s3: use monotonic clock for time deltas in smbget
[Samba/gbeck.git] / source4 / libnet / libnet_samsync_ldb.c
blobb9e93cf7c8c05e5c8eadbf633fd0a2dbdaba6bec
1 /*
2 Unix SMB/CIFS implementation.
4 Extract the user/system database from a remote SamSync server
6 Copyright (C) Andrew Bartlett <abartlet@samba.org> 2004-2005
7 Copyright (C) Andrew Tridgell 2004
8 Copyright (C) Volker Lendecke 2004
10 This program is free software; you can redistribute it and/or modify
11 it under the terms of the GNU General Public License as published by
12 the Free Software Foundation; either version 3 of the License, or
13 (at your option) any later version.
15 This program is distributed in the hope that it will be useful,
16 but WITHOUT ANY WARRANTY; without even the implied warranty of
17 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 GNU General Public License for more details.
20 You should have received a copy of the GNU General Public License
21 along with this program. If not, see <http://www.gnu.org/licenses/>.
25 #include "includes.h"
26 #include "libnet/libnet.h"
27 #include "libcli/ldap/ldap_ndr.h"
28 #include "dsdb/samdb/samdb.h"
29 #include "auth/auth.h"
30 #include "../lib/util/util_ldb.h"
31 #include "librpc/gen_ndr/ndr_misc.h"
32 #include "ldb_wrap.h"
33 #include "libcli/security/security.h"
34 #include "param/param.h"
36 struct samsync_ldb_secret {
37 struct samsync_ldb_secret *prev, *next;
38 DATA_BLOB secret;
39 char *name;
40 NTTIME mtime;
43 struct samsync_ldb_trusted_domain {
44 struct samsync_ldb_trusted_domain *prev, *next;
45 struct dom_sid *sid;
46 char *name;
49 struct samsync_ldb_state {
50 /* Values from the LSA lookup */
51 const struct libnet_SamSync_state *samsync_state;
53 struct dom_sid *dom_sid[3];
54 struct ldb_context *sam_ldb, *remote_ldb, *pdb;
55 struct ldb_dn *base_dn[3];
56 struct samsync_ldb_secret *secrets;
57 struct samsync_ldb_trusted_domain *trusted_domains;
60 static NTSTATUS samsync_ldb_add_foreignSecurityPrincipal(TALLOC_CTX *mem_ctx,
61 struct samsync_ldb_state *state,
62 struct dom_sid *sid,
63 struct ldb_dn **fsp_dn,
64 char **error_string)
66 const char *sidstr = dom_sid_string(mem_ctx, sid);
67 /* We assume that ForeignSecurityPrincipals are under the BASEDN of the main domain */
68 struct ldb_dn *basedn = samdb_search_dn(state->sam_ldb, mem_ctx,
69 state->base_dn[SAM_DATABASE_DOMAIN],
70 "(&(objectClass=container)(cn=ForeignSecurityPrincipals))");
71 struct ldb_message *msg;
72 int ret;
74 if (!sidstr) {
75 return NT_STATUS_NO_MEMORY;
78 if (basedn == NULL) {
79 *error_string = talloc_asprintf(mem_ctx,
80 "Failed to find DN for "
81 "ForeignSecurityPrincipal container under %s",
82 ldb_dn_get_linearized(state->base_dn[SAM_DATABASE_DOMAIN]));
83 return NT_STATUS_INTERNAL_DB_CORRUPTION;
86 msg = ldb_msg_new(mem_ctx);
87 if (msg == NULL) {
88 return NT_STATUS_NO_MEMORY;
91 /* add core elements to the ldb_message for the alias */
92 msg->dn = basedn;
93 if ( ! ldb_dn_add_child_fmt(msg->dn, "CN=%s", sidstr))
94 return NT_STATUS_UNSUCCESSFUL;
96 samdb_msg_add_string(state->sam_ldb, mem_ctx, msg,
97 "objectClass",
98 "foreignSecurityPrincipal");
100 *fsp_dn = msg->dn;
102 /* create the alias */
103 ret = ldb_add(state->sam_ldb, msg);
104 if (ret != 0) {
105 *error_string = talloc_asprintf(mem_ctx, "Failed to create foreignSecurityPrincipal "
106 "record %s: %s",
107 ldb_dn_get_linearized(msg->dn),
108 ldb_errstring(state->sam_ldb));
109 return NT_STATUS_INTERNAL_DB_CORRUPTION;
111 return NT_STATUS_OK;
114 static NTSTATUS samsync_ldb_handle_domain(TALLOC_CTX *mem_ctx,
115 struct samsync_ldb_state *state,
116 enum netr_SamDatabaseID database,
117 struct netr_DELTA_ENUM *delta,
118 char **error_string)
120 struct netr_DELTA_DOMAIN *domain = delta->delta_union.domain;
121 const char *domain_name = domain->domain_name.string;
122 struct ldb_message *msg;
123 int ret;
125 msg = ldb_msg_new(mem_ctx);
126 if (msg == NULL) {
127 return NT_STATUS_NO_MEMORY;
130 if (database == SAM_DATABASE_DOMAIN) {
131 struct ldb_dn *partitions_basedn;
132 const char *domain_attrs[] = {"nETBIOSName", "nCName", NULL};
133 struct ldb_message **msgs_domain;
134 int ret_domain;
136 partitions_basedn = samdb_partitions_dn(state->sam_ldb, mem_ctx);
138 ret_domain = gendb_search(state->sam_ldb, mem_ctx, partitions_basedn, &msgs_domain, domain_attrs,
139 "(&(&(nETBIOSName=%s)(objectclass=crossRef))(ncName=*))",
140 domain_name);
141 if (ret_domain == -1) {
142 *error_string = talloc_asprintf(mem_ctx, "gendb_search for domain failed: %s", ldb_errstring(state->sam_ldb));
143 return NT_STATUS_INTERNAL_DB_CORRUPTION;
146 if (ret_domain != 1) {
147 *error_string = talloc_asprintf(mem_ctx, "Failed to find existing domain record for %s: %d results", domain_name,
148 ret_domain);
149 return NT_STATUS_NO_SUCH_DOMAIN;
152 state->base_dn[database] = samdb_result_dn(state->sam_ldb, state, msgs_domain[0], "nCName", NULL);
154 if (state->dom_sid[database]) {
155 /* Update the domain sid with the incoming
156 * domain (found on LSA pipe, database sid may
157 * be random) */
158 samdb_msg_add_dom_sid(state->sam_ldb, mem_ctx,
159 msg, "objectSid", state->dom_sid[database]);
160 } else {
161 /* Well, we will have to use the one from the database */
162 state->dom_sid[database] = samdb_search_dom_sid(state->sam_ldb, state,
163 state->base_dn[database],
164 "objectSid", NULL);
167 if (state->samsync_state->domain_guid) {
168 struct ldb_val v;
169 NTSTATUS status;
170 status = GUID_to_ndr_blob(state->samsync_state->domain_guid, msg, &v);
171 if (!NT_STATUS_IS_OK(status)) {
172 *error_string = talloc_asprintf(mem_ctx, "ndr_push of domain GUID failed!");
173 return status;
176 ldb_msg_add_value(msg, "objectGUID", &v, NULL);
178 } else if (database == SAM_DATABASE_BUILTIN) {
179 /* work out the builtin_dn - useful for so many calls its worth
180 fetching here */
181 const char *dnstring = samdb_search_string(state->sam_ldb, mem_ctx, NULL,
182 "distinguishedName", "objectClass=builtinDomain");
183 state->base_dn[database] = ldb_dn_new(state, state->sam_ldb, dnstring);
184 if ( ! ldb_dn_validate(state->base_dn[database])) {
185 return NT_STATUS_INTERNAL_ERROR;
187 } else {
188 /* PRIVs DB */
189 return NT_STATUS_INVALID_PARAMETER;
192 msg->dn = talloc_reference(mem_ctx, state->base_dn[database]);
193 if (!msg->dn) {
194 return NT_STATUS_NO_MEMORY;
197 samdb_msg_add_string(state->sam_ldb, mem_ctx,
198 msg, "oEMInformation", domain->oem_information.string);
200 samdb_msg_add_int64(state->sam_ldb, mem_ctx,
201 msg, "forceLogoff", domain->force_logoff_time);
203 samdb_msg_add_uint(state->sam_ldb, mem_ctx,
204 msg, "minPwdLen", domain->min_password_length);
206 samdb_msg_add_int64(state->sam_ldb, mem_ctx,
207 msg, "maxPwdAge", domain->max_password_age);
209 samdb_msg_add_int64(state->sam_ldb, mem_ctx,
210 msg, "minPwdAge", domain->min_password_age);
212 samdb_msg_add_uint(state->sam_ldb, mem_ctx,
213 msg, "pwdHistoryLength", domain->password_history_length);
215 samdb_msg_add_uint64(state->sam_ldb, mem_ctx,
216 msg, "modifiedCount",
217 domain->sequence_num);
219 samdb_msg_add_uint64(state->sam_ldb, mem_ctx,
220 msg, "creationTime", domain->domain_create_time);
222 /* TODO: Account lockout, password properties */
224 ret = dsdb_replace(state->sam_ldb, msg, 0);
226 if (ret) {
227 return NT_STATUS_INTERNAL_ERROR;
229 return NT_STATUS_OK;
232 static NTSTATUS samsync_ldb_handle_user(TALLOC_CTX *mem_ctx,
233 struct samsync_ldb_state *state,
234 enum netr_SamDatabaseID database,
235 struct netr_DELTA_ENUM *delta,
236 char **error_string)
238 uint32_t rid = delta->delta_id_union.rid;
239 struct netr_DELTA_USER *user = delta->delta_union.user;
240 const char *container, *obj_class;
241 char *cn_name;
242 int cn_name_len;
243 const struct dom_sid *user_sid;
244 struct ldb_message *msg;
245 struct ldb_message **msgs;
246 struct ldb_message **remote_msgs = NULL;
247 unsigned int i;
248 int ret;
249 uint32_t acb;
250 bool add = false;
251 const char *attrs[] = { NULL };
252 /* we may change this to a global search, then fill in only the things not in ldap later */
253 const char *remote_attrs[] = { "userPrincipalName", "servicePrincipalName",
254 "msDS-KeyVersionNumber", "objectGUID", NULL};
256 user_sid = dom_sid_add_rid(mem_ctx, state->dom_sid[database], rid);
257 if (!user_sid) {
258 return NT_STATUS_NO_MEMORY;
261 msg = ldb_msg_new(mem_ctx);
262 if (msg == NULL) {
263 return NT_STATUS_NO_MEMORY;
266 msg->dn = NULL;
267 /* search for the user, by rid */
268 ret = gendb_search(state->sam_ldb, mem_ctx, state->base_dn[database],
269 &msgs, attrs, "(&(objectClass=user)(objectSid=%s))",
270 ldap_encode_ndr_dom_sid(mem_ctx, user_sid));
272 if (ret == -1) {
273 *error_string = talloc_asprintf(mem_ctx, "LDB for user %s failed: %s",
274 dom_sid_string(mem_ctx, user_sid),
275 ldb_errstring(state->sam_ldb));
276 return NT_STATUS_INTERNAL_DB_CORRUPTION;
277 } else if (ret == 0) {
278 add = true;
279 } else if (ret > 1) {
280 *error_string = talloc_asprintf(mem_ctx, "More than one user with SID: %s in local LDB",
281 dom_sid_string(mem_ctx, user_sid));
282 return NT_STATUS_INTERNAL_DB_CORRUPTION;
283 } else {
284 msg->dn = msgs[0]->dn;
285 talloc_steal(msg, msgs[0]->dn);
288 /* and do the same on the remote database */
289 if (state->remote_ldb) {
290 ret = gendb_search(state->remote_ldb, mem_ctx, state->base_dn[database],
291 &remote_msgs, remote_attrs, "(&(objectClass=user)(objectSid=%s))",
292 ldap_encode_ndr_dom_sid(mem_ctx, user_sid));
294 if (ret == -1) {
295 *error_string = talloc_asprintf(mem_ctx, "remote LDAP for user %s failed: %s",
296 dom_sid_string(mem_ctx, user_sid),
297 ldb_errstring(state->remote_ldb));
298 return NT_STATUS_INTERNAL_DB_CORRUPTION;
299 } else if (ret == 0) {
300 *error_string = talloc_asprintf(mem_ctx, "User exists in samsync but not in remote LDAP domain! (base: %s, SID: %s)",
301 ldb_dn_get_linearized(state->base_dn[database]),
302 dom_sid_string(mem_ctx, user_sid));
303 return NT_STATUS_NO_SUCH_USER;
304 } else if (ret > 1) {
305 *error_string = talloc_asprintf(mem_ctx, "More than one user in remote LDAP domain with SID: %s",
306 dom_sid_string(mem_ctx, user_sid));
307 return NT_STATUS_INTERNAL_DB_CORRUPTION;
309 /* Try to put things in the same location as the remote server */
310 } else if (add) {
311 msg->dn = remote_msgs[0]->dn;
312 talloc_steal(msg, remote_msgs[0]->dn);
316 cn_name = talloc_strdup(mem_ctx, user->account_name.string);
317 NT_STATUS_HAVE_NO_MEMORY(cn_name);
318 cn_name_len = strlen(cn_name);
320 #define ADD_OR_DEL(type, attrib, field) do { \
321 if (user->field) { \
322 samdb_msg_add_ ## type(state->sam_ldb, mem_ctx, msg, \
323 attrib, user->field); \
324 } else if (!add) { \
325 samdb_msg_add_delete(state->sam_ldb, mem_ctx, msg, \
326 attrib); \
328 } while (0);
330 ADD_OR_DEL(string, "samAccountName", account_name.string);
331 ADD_OR_DEL(string, "displayName", full_name.string);
333 if (samdb_msg_add_dom_sid(state->sam_ldb, mem_ctx, msg,
334 "objectSid", dom_sid_add_rid(mem_ctx, state->dom_sid[database], rid))) {
335 return NT_STATUS_NO_MEMORY;
338 ADD_OR_DEL(uint, "primaryGroupID", primary_gid);
339 ADD_OR_DEL(string, "homeDirectory", home_directory.string);
340 ADD_OR_DEL(string, "homeDrive", home_drive.string);
341 ADD_OR_DEL(string, "scriptPath", logon_script.string);
342 ADD_OR_DEL(string, "description", description.string);
343 ADD_OR_DEL(string, "userWorkstations", workstations.string);
345 ADD_OR_DEL(uint64, "lastLogon", last_logon);
346 ADD_OR_DEL(uint64, "lastLogoff", last_logoff);
348 if (samdb_msg_add_logon_hours(state->sam_ldb, mem_ctx, msg, "logonHours", &user->logon_hours) != 0) {
349 return NT_STATUS_NO_MEMORY;
352 ADD_OR_DEL(uint, "badPwdCount", bad_password_count);
353 ADD_OR_DEL(uint, "logonCount", logon_count);
355 ADD_OR_DEL(uint64, "pwdLastSet", last_password_change);
356 ADD_OR_DEL(uint64, "accountExpires", acct_expiry);
358 if (samdb_msg_add_acct_flags(state->sam_ldb, mem_ctx, msg,
359 "userAccountControl", user->acct_flags) != 0) {
360 return NT_STATUS_NO_MEMORY;
363 if (!add) {
364 /* Passwords. Ensure there is no plaintext stored against
365 * this entry, as we only have hashes */
366 samdb_msg_add_delete(state->sam_ldb, mem_ctx, msg,
367 "userPassword");
369 if (user->lm_password_present) {
370 samdb_msg_add_hash(state->sam_ldb, mem_ctx, msg,
371 "dBCSPwd", &user->lmpassword);
372 } else if (!add) {
373 samdb_msg_add_delete(state->sam_ldb, mem_ctx, msg,
374 "dBCSPwd");
376 if (user->nt_password_present) {
377 samdb_msg_add_hash(state->sam_ldb, mem_ctx, msg,
378 "unicodePwd", &user->ntpassword);
379 } else if (!add) {
380 samdb_msg_add_delete(state->sam_ldb, mem_ctx, msg,
381 "unicodePwd");
384 ADD_OR_DEL(string, "comment", comment.string);
386 if (samdb_msg_add_parameters(state->sam_ldb, mem_ctx, msg, "userParameters", &user->parameters) != 0) {
387 return NT_STATUS_NO_MEMORY;
390 ADD_OR_DEL(uint, "countryCode", country_code);
391 ADD_OR_DEL(uint, "codePage", code_page);
393 ADD_OR_DEL(string, "profilePath", profile_path.string);
395 #undef ADD_OR_DEL
397 for (i=0; remote_attrs[i]; i++) {
398 struct ldb_message_element *el = ldb_msg_find_element(remote_msgs[0], remote_attrs[i]);
399 if (!el) {
400 samdb_msg_add_delete(state->sam_ldb, mem_ctx, msg,
401 remote_attrs[i]);
402 } else {
403 ldb_msg_add(msg, el, LDB_FLAG_MOD_REPLACE);
407 acb = user->acct_flags;
408 if (acb & (ACB_WSTRUST)) {
409 cn_name[cn_name_len - 1] = '\0';
410 container = "Computers";
411 obj_class = "computer";
413 } else if (acb & ACB_SVRTRUST) {
414 if (cn_name[cn_name_len - 1] != '$') {
415 return NT_STATUS_FOOBAR;
417 cn_name[cn_name_len - 1] = '\0';
418 container = "Domain Controllers";
419 obj_class = "computer";
420 } else {
421 container = "Users";
422 obj_class = "user";
424 if (add) {
425 samdb_msg_add_string(state->sam_ldb, mem_ctx, msg,
426 "objectClass", obj_class);
427 if (!msg->dn) {
428 msg->dn = ldb_dn_copy(mem_ctx, state->base_dn[database]);
429 ldb_dn_add_child_fmt(msg->dn, "CN=%s,CN=%s", cn_name, container);
430 if (!msg->dn) {
431 return NT_STATUS_NO_MEMORY;
435 ret = ldb_add(state->sam_ldb, msg);
436 if (ret != 0) {
437 struct ldb_dn *first_try_dn = msg->dn;
438 /* Try again with the default DN */
439 if (!remote_msgs) {
440 *error_string = talloc_asprintf(mem_ctx, "Failed to create user record. Tried %s: %s",
441 ldb_dn_get_linearized(first_try_dn),
442 ldb_errstring(state->sam_ldb));
443 return NT_STATUS_INTERNAL_DB_CORRUPTION;
444 } else {
445 msg->dn = talloc_steal(msg, remote_msgs[0]->dn);
446 ret = ldb_add(state->sam_ldb, msg);
447 if (ret != 0) {
448 *error_string = talloc_asprintf(mem_ctx, "Failed to create user record. Tried both %s and %s: %s",
449 ldb_dn_get_linearized(first_try_dn),
450 ldb_dn_get_linearized(msg->dn),
451 ldb_errstring(state->sam_ldb));
452 return NT_STATUS_INTERNAL_DB_CORRUPTION;
456 } else {
457 ret = dsdb_replace(state->sam_ldb, msg, 0);
458 if (ret != 0) {
459 *error_string = talloc_asprintf(mem_ctx, "Failed to modify user record %s: %s",
460 ldb_dn_get_linearized(msg->dn),
461 ldb_errstring(state->sam_ldb));
462 return NT_STATUS_INTERNAL_DB_CORRUPTION;
466 return NT_STATUS_OK;
469 static NTSTATUS samsync_ldb_delete_user(TALLOC_CTX *mem_ctx,
470 struct samsync_ldb_state *state,
471 enum netr_SamDatabaseID database,
472 struct netr_DELTA_ENUM *delta,
473 char **error_string)
475 uint32_t rid = delta->delta_id_union.rid;
476 struct ldb_message **msgs;
477 int ret;
478 const char *attrs[] = { NULL };
480 /* search for the user, by rid */
481 ret = gendb_search(state->sam_ldb, mem_ctx, state->base_dn[database],
482 &msgs, attrs, "(&(objectClass=user)(objectSid=%s))",
483 ldap_encode_ndr_dom_sid(mem_ctx, dom_sid_add_rid(mem_ctx, state->dom_sid[database], rid)));
485 if (ret == -1) {
486 *error_string = talloc_asprintf(mem_ctx, "gendb_search failed: %s", ldb_errstring(state->sam_ldb));
487 return NT_STATUS_INTERNAL_DB_CORRUPTION;
488 } else if (ret == 0) {
489 return NT_STATUS_NO_SUCH_USER;
490 } else if (ret > 1) {
491 *error_string = talloc_asprintf(mem_ctx, "More than one user with SID: %s",
492 dom_sid_string(mem_ctx,
493 dom_sid_add_rid(mem_ctx,
494 state->dom_sid[database],
495 rid)));
496 return NT_STATUS_INTERNAL_DB_CORRUPTION;
499 ret = ldb_delete(state->sam_ldb, msgs[0]->dn);
500 if (ret != 0) {
501 *error_string = talloc_asprintf(mem_ctx, "Failed to delete user record %s: %s",
502 ldb_dn_get_linearized(msgs[0]->dn),
503 ldb_errstring(state->sam_ldb));
504 return NT_STATUS_INTERNAL_DB_CORRUPTION;
507 return NT_STATUS_OK;
510 static NTSTATUS samsync_ldb_handle_group(TALLOC_CTX *mem_ctx,
511 struct samsync_ldb_state *state,
512 enum netr_SamDatabaseID database,
513 struct netr_DELTA_ENUM *delta,
514 char **error_string)
516 uint32_t rid = delta->delta_id_union.rid;
517 struct netr_DELTA_GROUP *group = delta->delta_union.group;
518 const char *container, *obj_class;
519 const char *cn_name;
521 struct ldb_message *msg;
522 struct ldb_message **msgs;
523 int ret;
524 bool add = false;
525 const char *attrs[] = { NULL };
527 msg = ldb_msg_new(mem_ctx);
528 if (msg == NULL) {
529 return NT_STATUS_NO_MEMORY;
532 /* search for the group, by rid */
533 ret = gendb_search(state->sam_ldb, mem_ctx, state->base_dn[database], &msgs, attrs,
534 "(&(objectClass=group)(objectSid=%s))",
535 ldap_encode_ndr_dom_sid(mem_ctx, dom_sid_add_rid(mem_ctx, state->dom_sid[database], rid)));
537 if (ret == -1) {
538 *error_string = talloc_asprintf(mem_ctx, "gendb_search failed: %s", ldb_errstring(state->sam_ldb));
539 return NT_STATUS_INTERNAL_DB_CORRUPTION;
540 } else if (ret == 0) {
541 add = true;
542 } else if (ret > 1) {
543 *error_string = talloc_asprintf(mem_ctx, "More than one group/alias with SID: %s",
544 dom_sid_string(mem_ctx,
545 dom_sid_add_rid(mem_ctx,
546 state->dom_sid[database],
547 rid)));
548 return NT_STATUS_INTERNAL_DB_CORRUPTION;
549 } else {
550 msg->dn = talloc_steal(msg, msgs[0]->dn);
553 cn_name = group->group_name.string;
555 #define ADD_OR_DEL(type, attrib, field) do { \
556 if (group->field) { \
557 samdb_msg_add_ ## type(state->sam_ldb, mem_ctx, msg, \
558 attrib, group->field); \
559 } else if (!add) { \
560 samdb_msg_add_delete(state->sam_ldb, mem_ctx, msg, \
561 attrib); \
563 } while (0);
565 ADD_OR_DEL(string, "samAccountName", group_name.string);
567 if (samdb_msg_add_dom_sid(state->sam_ldb, mem_ctx, msg,
568 "objectSid", dom_sid_add_rid(mem_ctx, state->dom_sid[database], rid))) {
569 return NT_STATUS_NO_MEMORY;
572 ADD_OR_DEL(string, "description", description.string);
574 #undef ADD_OR_DEL
576 container = "Users";
577 obj_class = "group";
579 if (add) {
580 samdb_msg_add_string(state->sam_ldb, mem_ctx, msg,
581 "objectClass", obj_class);
582 msg->dn = ldb_dn_copy(mem_ctx, state->base_dn[database]);
583 ldb_dn_add_child_fmt(msg->dn, "CN=%s,CN=%s", cn_name, container);
584 if (!msg->dn) {
585 return NT_STATUS_NO_MEMORY;
588 ret = ldb_add(state->sam_ldb, msg);
589 if (ret != 0) {
590 *error_string = talloc_asprintf(mem_ctx, "Failed to create group record %s: %s",
591 ldb_dn_get_linearized(msg->dn),
592 ldb_errstring(state->sam_ldb));
593 return NT_STATUS_INTERNAL_DB_CORRUPTION;
595 } else {
596 ret = dsdb_replace(state->sam_ldb, msg, 0);
597 if (ret != 0) {
598 *error_string = talloc_asprintf(mem_ctx, "Failed to modify group record %s: %s",
599 ldb_dn_get_linearized(msg->dn),
600 ldb_errstring(state->sam_ldb));
601 return NT_STATUS_INTERNAL_DB_CORRUPTION;
605 return NT_STATUS_OK;
608 static NTSTATUS samsync_ldb_delete_group(TALLOC_CTX *mem_ctx,
609 struct samsync_ldb_state *state,
610 enum netr_SamDatabaseID database,
611 struct netr_DELTA_ENUM *delta,
612 char **error_string)
614 uint32_t rid = delta->delta_id_union.rid;
615 struct ldb_message **msgs;
616 int ret;
617 const char *attrs[] = { NULL };
619 /* search for the group, by rid */
620 ret = gendb_search(state->sam_ldb, mem_ctx, state->base_dn[database], &msgs, attrs,
621 "(&(objectClass=group)(objectSid=%s))",
622 ldap_encode_ndr_dom_sid(mem_ctx, dom_sid_add_rid(mem_ctx, state->dom_sid[database], rid)));
624 if (ret == -1) {
625 *error_string = talloc_asprintf(mem_ctx, "gendb_search failed: %s", ldb_errstring(state->sam_ldb));
626 return NT_STATUS_INTERNAL_DB_CORRUPTION;
627 } else if (ret == 0) {
628 return NT_STATUS_NO_SUCH_GROUP;
629 } else if (ret > 1) {
630 *error_string = talloc_asprintf(mem_ctx, "More than one group/alias with SID: %s",
631 dom_sid_string(mem_ctx,
632 dom_sid_add_rid(mem_ctx,
633 state->dom_sid[database],
634 rid)));
635 return NT_STATUS_INTERNAL_DB_CORRUPTION;
638 ret = ldb_delete(state->sam_ldb, msgs[0]->dn);
639 if (ret != 0) {
640 *error_string = talloc_asprintf(mem_ctx, "Failed to delete group record %s: %s",
641 ldb_dn_get_linearized(msgs[0]->dn),
642 ldb_errstring(state->sam_ldb));
643 return NT_STATUS_INTERNAL_DB_CORRUPTION;
646 return NT_STATUS_OK;
649 static NTSTATUS samsync_ldb_handle_group_member(TALLOC_CTX *mem_ctx,
650 struct samsync_ldb_state *state,
651 enum netr_SamDatabaseID database,
652 struct netr_DELTA_ENUM *delta,
653 char **error_string)
655 uint32_t rid = delta->delta_id_union.rid;
656 struct netr_DELTA_GROUP_MEMBER *group_member = delta->delta_union.group_member;
657 struct ldb_message *msg;
658 struct ldb_message **msgs;
659 int ret;
660 const char *attrs[] = { NULL };
661 uint32_t i;
663 msg = ldb_msg_new(mem_ctx);
664 if (msg == NULL) {
665 return NT_STATUS_NO_MEMORY;
668 /* search for the group, by rid */
669 ret = gendb_search(state->sam_ldb, mem_ctx, state->base_dn[database], &msgs, attrs,
670 "(&(objectClass=group)(objectSid=%s))",
671 ldap_encode_ndr_dom_sid(mem_ctx, dom_sid_add_rid(mem_ctx, state->dom_sid[database], rid)));
673 if (ret == -1) {
674 *error_string = talloc_asprintf(mem_ctx, "gendb_search failed: %s", ldb_errstring(state->sam_ldb));
675 return NT_STATUS_INTERNAL_DB_CORRUPTION;
676 } else if (ret == 0) {
677 return NT_STATUS_NO_SUCH_GROUP;
678 } else if (ret > 1) {
679 *error_string = talloc_asprintf(mem_ctx, "More than one group/alias with SID: %s",
680 dom_sid_string(mem_ctx,
681 dom_sid_add_rid(mem_ctx,
682 state->dom_sid[database],
683 rid)));
684 return NT_STATUS_INTERNAL_DB_CORRUPTION;
685 } else {
686 msg->dn = talloc_steal(msg, msgs[0]->dn);
689 talloc_free(msgs);
691 for (i=0; i<group_member->num_rids; i++) {
692 /* search for the group, by rid */
693 ret = gendb_search(state->sam_ldb, mem_ctx, state->base_dn[database], &msgs, attrs,
694 "(&(objectClass=user)(objectSid=%s))",
695 ldap_encode_ndr_dom_sid(mem_ctx, dom_sid_add_rid(mem_ctx, state->dom_sid[database], group_member->rids[i])));
697 if (ret == -1) {
698 *error_string = talloc_asprintf(mem_ctx, "gendb_search failed: %s", ldb_errstring(state->sam_ldb));
699 return NT_STATUS_INTERNAL_DB_CORRUPTION;
700 } else if (ret == 0) {
701 return NT_STATUS_NO_SUCH_USER;
702 } else if (ret > 1) {
703 return NT_STATUS_INTERNAL_DB_CORRUPTION;
704 } else {
705 samdb_msg_add_string(state->sam_ldb, mem_ctx, msg, "member", ldb_dn_alloc_linearized(mem_ctx, msgs[0]->dn));
708 talloc_free(msgs);
711 ret = dsdb_replace(state->sam_ldb, msg, 0);
712 if (ret != 0) {
713 *error_string = talloc_asprintf(mem_ctx, "Failed to modify group record %s: %s",
714 ldb_dn_get_linearized(msg->dn),
715 ldb_errstring(state->sam_ldb));
716 return NT_STATUS_INTERNAL_DB_CORRUPTION;
719 return NT_STATUS_OK;
722 static NTSTATUS samsync_ldb_handle_alias(TALLOC_CTX *mem_ctx,
723 struct samsync_ldb_state *state,
724 enum netr_SamDatabaseID database,
725 struct netr_DELTA_ENUM *delta,
726 char **error_string)
728 uint32_t rid = delta->delta_id_union.rid;
729 struct netr_DELTA_ALIAS *alias = delta->delta_union.alias;
730 const char *container, *obj_class;
731 const char *cn_name;
733 struct ldb_message *msg;
734 struct ldb_message **msgs;
735 int ret;
736 bool add = false;
737 const char *attrs[] = { NULL };
739 msg = ldb_msg_new(mem_ctx);
740 if (msg == NULL) {
741 return NT_STATUS_NO_MEMORY;
744 /* search for the alias, by rid */
745 ret = gendb_search(state->sam_ldb, mem_ctx, state->base_dn[database], &msgs, attrs,
746 "(&(objectClass=group)(objectSid=%s))",
747 ldap_encode_ndr_dom_sid(mem_ctx, dom_sid_add_rid(mem_ctx, state->dom_sid[database], rid)));
749 if (ret == -1) {
750 *error_string = talloc_asprintf(mem_ctx, "gendb_search failed: %s", ldb_errstring(state->sam_ldb));
751 return NT_STATUS_INTERNAL_DB_CORRUPTION;
752 } else if (ret == 0) {
753 add = true;
754 } else if (ret > 1) {
755 *error_string = talloc_asprintf(mem_ctx, "More than one group/alias with SID: %s",
756 dom_sid_string(mem_ctx,
757 dom_sid_add_rid(mem_ctx,
758 state->dom_sid[database],
759 rid)));
760 return NT_STATUS_INTERNAL_DB_CORRUPTION;
761 } else {
762 msg->dn = talloc_steal(mem_ctx, msgs[0]->dn);
765 cn_name = alias->alias_name.string;
767 #define ADD_OR_DEL(type, attrib, field) do { \
768 if (alias->field) { \
769 samdb_msg_add_ ## type(state->sam_ldb, mem_ctx, msg, \
770 attrib, alias->field); \
771 } else if (!add) { \
772 samdb_msg_add_delete(state->sam_ldb, mem_ctx, msg, \
773 attrib); \
775 } while (0);
777 ADD_OR_DEL(string, "samAccountName", alias_name.string);
779 if (samdb_msg_add_dom_sid(state->sam_ldb, mem_ctx, msg,
780 "objectSid", dom_sid_add_rid(mem_ctx, state->dom_sid[database], rid))) {
781 return NT_STATUS_NO_MEMORY;
784 ADD_OR_DEL(string, "description", description.string);
786 #undef ADD_OR_DEL
788 samdb_msg_add_uint(state->sam_ldb, mem_ctx, msg, "groupType", 0x80000004);
790 container = "Users";
791 obj_class = "group";
793 if (add) {
794 samdb_msg_add_string(state->sam_ldb, mem_ctx, msg,
795 "objectClass", obj_class);
796 msg->dn = ldb_dn_copy(mem_ctx, state->base_dn[database]);
797 ldb_dn_add_child_fmt(msg->dn, "CN=%s,CN=%s", cn_name, container);
798 if (!msg->dn) {
799 return NT_STATUS_NO_MEMORY;
802 ret = ldb_add(state->sam_ldb, msg);
803 if (ret != 0) {
804 *error_string = talloc_asprintf(mem_ctx, "Failed to create alias record %s: %s",
805 ldb_dn_get_linearized(msg->dn),
806 ldb_errstring(state->sam_ldb));
807 return NT_STATUS_INTERNAL_DB_CORRUPTION;
809 } else {
810 ret = dsdb_replace(state->sam_ldb, msg, 0);
811 if (ret != 0) {
812 *error_string = talloc_asprintf(mem_ctx, "Failed to modify alias record %s: %s",
813 ldb_dn_get_linearized(msg->dn),
814 ldb_errstring(state->sam_ldb));
815 return NT_STATUS_INTERNAL_DB_CORRUPTION;
819 return NT_STATUS_OK;
822 static NTSTATUS samsync_ldb_delete_alias(TALLOC_CTX *mem_ctx,
823 struct samsync_ldb_state *state,
824 enum netr_SamDatabaseID database,
825 struct netr_DELTA_ENUM *delta,
826 char **error_string)
828 uint32_t rid = delta->delta_id_union.rid;
829 struct ldb_message **msgs;
830 int ret;
831 const char *attrs[] = { NULL };
833 /* search for the alias, by rid */
834 ret = gendb_search(state->sam_ldb, mem_ctx, state->base_dn[database], &msgs, attrs,
835 "(&(objectClass=group)(objectSid=%s))",
836 ldap_encode_ndr_dom_sid(mem_ctx, dom_sid_add_rid(mem_ctx, state->dom_sid[database], rid)));
838 if (ret == -1) {
839 *error_string = talloc_asprintf(mem_ctx, "gendb_search failed: %s", ldb_errstring(state->sam_ldb));
840 return NT_STATUS_INTERNAL_DB_CORRUPTION;
841 } else if (ret == 0) {
842 return NT_STATUS_NO_SUCH_ALIAS;
843 } else if (ret > 1) {
844 return NT_STATUS_INTERNAL_DB_CORRUPTION;
847 ret = ldb_delete(state->sam_ldb, msgs[0]->dn);
848 if (ret != 0) {
849 *error_string = talloc_asprintf(mem_ctx, "Failed to delete alias record %s: %s",
850 ldb_dn_get_linearized(msgs[0]->dn),
851 ldb_errstring(state->sam_ldb));
852 return NT_STATUS_INTERNAL_DB_CORRUPTION;
855 return NT_STATUS_OK;
858 static NTSTATUS samsync_ldb_handle_alias_member(TALLOC_CTX *mem_ctx,
859 struct samsync_ldb_state *state,
860 enum netr_SamDatabaseID database,
861 struct netr_DELTA_ENUM *delta,
862 char **error_string)
864 uint32_t rid = delta->delta_id_union.rid;
865 struct netr_DELTA_ALIAS_MEMBER *alias_member = delta->delta_union.alias_member;
866 struct ldb_message *msg;
867 struct ldb_message **msgs;
868 int ret;
869 const char *attrs[] = { NULL };
870 uint32_t i;
872 msg = ldb_msg_new(mem_ctx);
873 if (msg == NULL) {
874 return NT_STATUS_NO_MEMORY;
877 /* search for the alias, by rid */
878 ret = gendb_search(state->sam_ldb, mem_ctx, state->base_dn[database], &msgs, attrs,
879 "(&(objectClass=group)(objectSid=%s))",
880 ldap_encode_ndr_dom_sid(mem_ctx, dom_sid_add_rid(mem_ctx, state->dom_sid[database], rid)));
882 if (ret == -1) {
883 *error_string = talloc_asprintf(mem_ctx, "gendb_search failed: %s", ldb_errstring(state->sam_ldb));
884 return NT_STATUS_INTERNAL_DB_CORRUPTION;
885 } else if (ret == 0) {
886 return NT_STATUS_NO_SUCH_GROUP;
887 } else if (ret > 1) {
888 *error_string = talloc_asprintf(mem_ctx, "More than one group/alias with SID: %s",
889 dom_sid_string(mem_ctx,
890 dom_sid_add_rid(mem_ctx,
891 state->dom_sid[database],
892 rid)));
893 return NT_STATUS_INTERNAL_DB_CORRUPTION;
894 } else {
895 msg->dn = talloc_steal(msg, msgs[0]->dn);
898 talloc_free(msgs);
900 for (i=0; i<alias_member->sids.num_sids; i++) {
901 struct ldb_dn *alias_member_dn;
902 /* search for members, in the top basedn (normal users are builtin aliases) */
903 ret = gendb_search(state->sam_ldb, mem_ctx, state->base_dn[SAM_DATABASE_DOMAIN], &msgs, attrs,
904 "(objectSid=%s)",
905 ldap_encode_ndr_dom_sid(mem_ctx, alias_member->sids.sids[i].sid));
907 if (ret == -1) {
908 *error_string = talloc_asprintf(mem_ctx, "gendb_search failed: %s", ldb_errstring(state->sam_ldb));
909 return NT_STATUS_INTERNAL_DB_CORRUPTION;
910 } else if (ret == 0) {
911 NTSTATUS nt_status;
912 nt_status = samsync_ldb_add_foreignSecurityPrincipal(mem_ctx, state,
913 alias_member->sids.sids[i].sid,
914 &alias_member_dn,
915 error_string);
916 if (!NT_STATUS_IS_OK(nt_status)) {
917 return nt_status;
919 } else if (ret > 1) {
920 return NT_STATUS_INTERNAL_DB_CORRUPTION;
921 } else {
922 alias_member_dn = msgs[0]->dn;
924 samdb_msg_add_string(state->sam_ldb, mem_ctx, msg, "member", ldb_dn_alloc_linearized(mem_ctx, alias_member_dn));
926 talloc_free(msgs);
929 ret = dsdb_replace(state->sam_ldb, msg, 0);
930 if (ret != 0) {
931 *error_string = talloc_asprintf(mem_ctx, "Failed to modify group record %s: %s",
932 ldb_dn_get_linearized(msg->dn),
933 ldb_errstring(state->sam_ldb));
934 return NT_STATUS_INTERNAL_DB_CORRUPTION;
937 return NT_STATUS_OK;
940 static NTSTATUS samsync_ldb_handle_account(TALLOC_CTX *mem_ctx,
941 struct samsync_ldb_state *state,
942 enum netr_SamDatabaseID database,
943 struct netr_DELTA_ENUM *delta,
944 char **error_string)
946 struct dom_sid *sid = delta->delta_id_union.sid;
947 struct netr_DELTA_ACCOUNT *account = delta->delta_union.account;
949 struct ldb_message *msg;
950 int ret;
951 uint32_t i;
952 char *dnstr, *sidstr;
954 msg = ldb_msg_new(mem_ctx);
955 if (msg == NULL) {
956 return NT_STATUS_NO_MEMORY;
959 sidstr = dom_sid_string(msg, sid);
960 NT_STATUS_HAVE_NO_MEMORY_AND_FREE(sidstr, msg);
962 dnstr = talloc_asprintf(msg, "sid=%s", sidstr);
963 NT_STATUS_HAVE_NO_MEMORY_AND_FREE(dnstr, msg);
965 msg->dn = ldb_dn_new(msg, state->pdb, dnstr);
966 NT_STATUS_HAVE_NO_MEMORY_AND_FREE(msg->dn, msg);
968 for (i=0; i< account->privilege_entries; i++) {
969 samdb_msg_add_string(state->pdb, mem_ctx, msg, "privilege",
970 account->privilege_name[i].string);
973 ret = dsdb_replace(state->pdb, msg, 0);
974 if (ret == LDB_ERR_NO_SUCH_OBJECT) {
975 if (samdb_msg_add_dom_sid(state->pdb, msg, msg, "objectSid", sid) != LDB_SUCCESS) {
976 talloc_free(msg);
977 return NT_STATUS_NO_MEMORY;
979 samdb_msg_add_string(state->pdb, msg, msg, "comment", "added via samsync");
980 ret = ldb_add(state->pdb, msg);
983 if (ret != 0) {
984 *error_string = talloc_asprintf(mem_ctx, "Failed to modify privilege record %s",
985 ldb_dn_get_linearized(msg->dn));
986 return NT_STATUS_INTERNAL_DB_CORRUPTION;
989 return NT_STATUS_OK;
992 static NTSTATUS samsync_ldb_delete_account(TALLOC_CTX *mem_ctx,
993 struct samsync_ldb_state *state,
994 enum netr_SamDatabaseID database,
995 struct netr_DELTA_ENUM *delta,
996 char **error_string)
998 struct dom_sid *sid = delta->delta_id_union.sid;
1000 struct ldb_message *msg;
1001 struct ldb_message **msgs;
1002 int ret;
1003 const char *attrs[] = { NULL };
1005 msg = ldb_msg_new(mem_ctx);
1006 if (msg == NULL) {
1007 return NT_STATUS_NO_MEMORY;
1010 /* search for the account, by sid, in the top basedn */
1011 ret = gendb_search(state->sam_ldb, mem_ctx, state->base_dn[SAM_DATABASE_DOMAIN], &msgs, attrs,
1012 "(objectSid=%s)",
1013 ldap_encode_ndr_dom_sid(mem_ctx, sid));
1015 if (ret == -1) {
1016 *error_string = talloc_asprintf(mem_ctx, "gendb_search failed: %s", ldb_errstring(state->sam_ldb));
1017 return NT_STATUS_INTERNAL_DB_CORRUPTION;
1018 } else if (ret == 0) {
1019 return NT_STATUS_NO_SUCH_USER;
1020 } else if (ret > 1) {
1021 *error_string = talloc_asprintf(mem_ctx, "More than one account with SID: %s",
1022 dom_sid_string(mem_ctx, sid));
1023 return NT_STATUS_INTERNAL_DB_CORRUPTION;
1024 } else {
1025 msg->dn = talloc_steal(msg, msgs[0]->dn);
1028 samdb_msg_add_delete(state->sam_ldb, mem_ctx, msg,
1029 "privilege");
1031 ret = dsdb_replace(state->sam_ldb, msg, 0);
1032 if (ret != 0) {
1033 *error_string = talloc_asprintf(mem_ctx, "Failed to modify privilege record %s",
1034 ldb_dn_get_linearized(msg->dn));
1035 return NT_STATUS_INTERNAL_DB_CORRUPTION;
1038 return NT_STATUS_OK;
1041 static NTSTATUS libnet_samsync_ldb_fn(TALLOC_CTX *mem_ctx,
1042 void *private_data,
1043 enum netr_SamDatabaseID database,
1044 struct netr_DELTA_ENUM *delta,
1045 char **error_string)
1047 NTSTATUS nt_status = NT_STATUS_OK;
1048 struct samsync_ldb_state *state = talloc_get_type(private_data, struct samsync_ldb_state);
1050 *error_string = NULL;
1051 switch (delta->delta_type) {
1052 case NETR_DELTA_DOMAIN:
1054 nt_status = samsync_ldb_handle_domain(mem_ctx,
1055 state,
1056 database,
1057 delta,
1058 error_string);
1059 break;
1061 case NETR_DELTA_USER:
1063 nt_status = samsync_ldb_handle_user(mem_ctx,
1064 state,
1065 database,
1066 delta,
1067 error_string);
1068 break;
1070 case NETR_DELTA_DELETE_USER:
1072 nt_status = samsync_ldb_delete_user(mem_ctx,
1073 state,
1074 database,
1075 delta,
1076 error_string);
1077 break;
1079 case NETR_DELTA_GROUP:
1081 nt_status = samsync_ldb_handle_group(mem_ctx,
1082 state,
1083 database,
1084 delta,
1085 error_string);
1086 break;
1088 case NETR_DELTA_DELETE_GROUP:
1090 nt_status = samsync_ldb_delete_group(mem_ctx,
1091 state,
1092 database,
1093 delta,
1094 error_string);
1095 break;
1097 case NETR_DELTA_GROUP_MEMBER:
1099 nt_status = samsync_ldb_handle_group_member(mem_ctx,
1100 state,
1101 database,
1102 delta,
1103 error_string);
1104 break;
1106 case NETR_DELTA_ALIAS:
1108 nt_status = samsync_ldb_handle_alias(mem_ctx,
1109 state,
1110 database,
1111 delta,
1112 error_string);
1113 break;
1115 case NETR_DELTA_DELETE_ALIAS:
1117 nt_status = samsync_ldb_delete_alias(mem_ctx,
1118 state,
1119 database,
1120 delta,
1121 error_string);
1122 break;
1124 case NETR_DELTA_ALIAS_MEMBER:
1126 nt_status = samsync_ldb_handle_alias_member(mem_ctx,
1127 state,
1128 database,
1129 delta,
1130 error_string);
1131 break;
1133 case NETR_DELTA_ACCOUNT:
1135 nt_status = samsync_ldb_handle_account(mem_ctx,
1136 state,
1137 database,
1138 delta,
1139 error_string);
1140 break;
1142 case NETR_DELTA_DELETE_ACCOUNT:
1144 nt_status = samsync_ldb_delete_account(mem_ctx,
1145 state,
1146 database,
1147 delta,
1148 error_string);
1149 break;
1151 default:
1152 /* Can't dump them all right now */
1153 break;
1155 if (!NT_STATUS_IS_OK(nt_status) && !*error_string) {
1156 *error_string = talloc_asprintf(mem_ctx, "Failed to handle samsync delta: %s", nt_errstr(nt_status));
1158 return nt_status;
1161 static NTSTATUS libnet_samsync_ldb_init(TALLOC_CTX *mem_ctx,
1162 void *private_data,
1163 struct libnet_SamSync_state *samsync_state,
1164 char **error_string)
1166 struct samsync_ldb_state *state = talloc_get_type(private_data, struct samsync_ldb_state);
1167 const char *server = dcerpc_server_name(samsync_state->netlogon_pipe);
1168 char *ldap_url;
1170 state->samsync_state = samsync_state;
1172 ZERO_STRUCT(state->dom_sid);
1173 if (state->samsync_state->domain_sid) {
1174 state->dom_sid[SAM_DATABASE_DOMAIN] = dom_sid_dup(state, state->samsync_state->domain_sid);
1177 state->dom_sid[SAM_DATABASE_BUILTIN] = dom_sid_parse_talloc(state, SID_BUILTIN);
1179 if (state->samsync_state->realm) {
1180 if (!server || !*server) {
1181 /* huh? how do we not have a server name? */
1182 *error_string = talloc_strdup(mem_ctx, "No DCE/RPC server name available. How did we connect?");
1183 return NT_STATUS_INVALID_PARAMETER;
1185 ldap_url = talloc_asprintf(state, "ldap://%s", server);
1187 state->remote_ldb = ldb_wrap_connect(mem_ctx,
1188 state->samsync_state->machine_net_ctx->event_ctx,
1189 state->samsync_state->machine_net_ctx->lp_ctx,
1190 ldap_url,
1191 NULL, state->samsync_state->machine_net_ctx->cred,
1193 if (!state->remote_ldb) {
1194 *error_string = talloc_asprintf(mem_ctx, "Failed to connect to remote LDAP server at %s (used to extract additional data in SamSync replication)", ldap_url);
1195 return NT_STATUS_NO_LOGON_SERVERS;
1197 } else {
1198 state->remote_ldb = NULL;
1200 return NT_STATUS_OK;
1203 NTSTATUS libnet_samsync_ldb(struct libnet_context *ctx, TALLOC_CTX *mem_ctx, struct libnet_samsync_ldb *r)
1205 NTSTATUS nt_status;
1206 struct libnet_SamSync r2;
1207 struct samsync_ldb_state *state = talloc(mem_ctx, struct samsync_ldb_state);
1209 if (!state) {
1210 return NT_STATUS_NO_MEMORY;
1213 state->secrets = NULL;
1214 state->trusted_domains = NULL;
1216 state->sam_ldb = samdb_connect(mem_ctx,
1217 ctx->event_ctx,
1218 ctx->lp_ctx,
1219 r->in.session_info);
1220 if (!state->sam_ldb) {
1221 return NT_STATUS_INTERNAL_DB_ERROR;
1224 state->pdb = privilege_connect(mem_ctx,
1225 ctx->event_ctx,
1226 ctx->lp_ctx);
1227 if (!state->pdb) {
1228 return NT_STATUS_INTERNAL_DB_ERROR;
1231 r2.out.error_string = NULL;
1232 r2.in.binding_string = r->in.binding_string;
1233 r2.in.init_fn = libnet_samsync_ldb_init;
1234 r2.in.delta_fn = libnet_samsync_ldb_fn;
1235 r2.in.fn_ctx = state;
1236 r2.in.machine_account = NULL; /* TODO: Create a machine account, fill this in, and the delete it */
1237 nt_status = libnet_SamSync_netlogon(ctx, state, &r2);
1238 r->out.error_string = r2.out.error_string;
1239 talloc_steal(mem_ctx, r->out.error_string);
1241 if (!NT_STATUS_IS_OK(nt_status)) {
1242 talloc_free(state);
1243 return nt_status;
1245 talloc_free(state);
1246 return nt_status;