s3-smbclient: Fix cli_errstr() usage (part of bug #7864)
[Samba.git] / source4 / libnet / libnet_samsync_ldb.c
blobbdeced8eef96b3b5137b717231a0e69d9f8ced33
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 != LDB_SUCCESS) {
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 (user->lm_password_present) {
364 samdb_msg_add_hash(state->sam_ldb, mem_ctx, msg,
365 "dBCSPwd", &user->lmpassword);
366 } else if (!add) {
367 samdb_msg_add_delete(state->sam_ldb, mem_ctx, msg,
368 "dBCSPwd");
370 if (user->nt_password_present) {
371 samdb_msg_add_hash(state->sam_ldb, mem_ctx, msg,
372 "unicodePwd", &user->ntpassword);
373 } else if (!add) {
374 samdb_msg_add_delete(state->sam_ldb, mem_ctx, msg,
375 "unicodePwd");
378 ADD_OR_DEL(string, "comment", comment.string);
380 if (samdb_msg_add_parameters(state->sam_ldb, mem_ctx, msg, "userParameters", &user->parameters) != 0) {
381 return NT_STATUS_NO_MEMORY;
384 ADD_OR_DEL(uint, "countryCode", country_code);
385 ADD_OR_DEL(uint, "codePage", code_page);
387 ADD_OR_DEL(string, "profilePath", profile_path.string);
389 #undef ADD_OR_DEL
391 for (i=0; remote_attrs[i]; i++) {
392 struct ldb_message_element *el = ldb_msg_find_element(remote_msgs[0], remote_attrs[i]);
393 if (!el) {
394 samdb_msg_add_delete(state->sam_ldb, mem_ctx, msg,
395 remote_attrs[i]);
396 } else {
397 ldb_msg_add(msg, el, LDB_FLAG_MOD_REPLACE);
401 acb = user->acct_flags;
402 if (acb & (ACB_WSTRUST)) {
403 cn_name[cn_name_len - 1] = '\0';
404 container = "Computers";
405 obj_class = "computer";
407 } else if (acb & ACB_SVRTRUST) {
408 if (cn_name[cn_name_len - 1] != '$') {
409 return NT_STATUS_FOOBAR;
411 cn_name[cn_name_len - 1] = '\0';
412 container = "Domain Controllers";
413 obj_class = "computer";
414 } else {
415 container = "Users";
416 obj_class = "user";
418 if (add) {
419 samdb_msg_add_string(state->sam_ldb, mem_ctx, msg,
420 "objectClass", obj_class);
421 if (!msg->dn) {
422 msg->dn = ldb_dn_copy(mem_ctx, state->base_dn[database]);
423 ldb_dn_add_child_fmt(msg->dn, "CN=%s,CN=%s", cn_name, container);
424 if (!msg->dn) {
425 return NT_STATUS_NO_MEMORY;
429 ret = ldb_add(state->sam_ldb, msg);
430 if (ret != LDB_SUCCESS) {
431 struct ldb_dn *first_try_dn = msg->dn;
432 /* Try again with the default DN */
433 if (!remote_msgs) {
434 *error_string = talloc_asprintf(mem_ctx, "Failed to create user record. Tried %s: %s",
435 ldb_dn_get_linearized(first_try_dn),
436 ldb_errstring(state->sam_ldb));
437 return NT_STATUS_INTERNAL_DB_CORRUPTION;
438 } else {
439 msg->dn = talloc_steal(msg, remote_msgs[0]->dn);
440 ret = ldb_add(state->sam_ldb, msg);
441 if (ret != LDB_SUCCESS) {
442 *error_string = talloc_asprintf(mem_ctx, "Failed to create user record. Tried both %s and %s: %s",
443 ldb_dn_get_linearized(first_try_dn),
444 ldb_dn_get_linearized(msg->dn),
445 ldb_errstring(state->sam_ldb));
446 return NT_STATUS_INTERNAL_DB_CORRUPTION;
450 } else {
451 ret = dsdb_replace(state->sam_ldb, msg, 0);
452 if (ret != LDB_SUCCESS) {
453 *error_string = talloc_asprintf(mem_ctx, "Failed to modify user record %s: %s",
454 ldb_dn_get_linearized(msg->dn),
455 ldb_errstring(state->sam_ldb));
456 return NT_STATUS_INTERNAL_DB_CORRUPTION;
460 return NT_STATUS_OK;
463 static NTSTATUS samsync_ldb_delete_user(TALLOC_CTX *mem_ctx,
464 struct samsync_ldb_state *state,
465 enum netr_SamDatabaseID database,
466 struct netr_DELTA_ENUM *delta,
467 char **error_string)
469 uint32_t rid = delta->delta_id_union.rid;
470 struct ldb_message **msgs;
471 int ret;
472 const char *attrs[] = { NULL };
474 /* search for the user, by rid */
475 ret = gendb_search(state->sam_ldb, mem_ctx, state->base_dn[database],
476 &msgs, attrs, "(&(objectClass=user)(objectSid=%s))",
477 ldap_encode_ndr_dom_sid(mem_ctx, dom_sid_add_rid(mem_ctx, state->dom_sid[database], rid)));
479 if (ret == -1) {
480 *error_string = talloc_asprintf(mem_ctx, "gendb_search failed: %s", ldb_errstring(state->sam_ldb));
481 return NT_STATUS_INTERNAL_DB_CORRUPTION;
482 } else if (ret == 0) {
483 return NT_STATUS_NO_SUCH_USER;
484 } else if (ret > 1) {
485 *error_string = talloc_asprintf(mem_ctx, "More than one user with SID: %s",
486 dom_sid_string(mem_ctx,
487 dom_sid_add_rid(mem_ctx,
488 state->dom_sid[database],
489 rid)));
490 return NT_STATUS_INTERNAL_DB_CORRUPTION;
493 ret = ldb_delete(state->sam_ldb, msgs[0]->dn);
494 if (ret != LDB_SUCCESS) {
495 *error_string = talloc_asprintf(mem_ctx, "Failed to delete user record %s: %s",
496 ldb_dn_get_linearized(msgs[0]->dn),
497 ldb_errstring(state->sam_ldb));
498 return NT_STATUS_INTERNAL_DB_CORRUPTION;
501 return NT_STATUS_OK;
504 static NTSTATUS samsync_ldb_handle_group(TALLOC_CTX *mem_ctx,
505 struct samsync_ldb_state *state,
506 enum netr_SamDatabaseID database,
507 struct netr_DELTA_ENUM *delta,
508 char **error_string)
510 uint32_t rid = delta->delta_id_union.rid;
511 struct netr_DELTA_GROUP *group = delta->delta_union.group;
512 const char *container, *obj_class;
513 const char *cn_name;
515 struct ldb_message *msg;
516 struct ldb_message **msgs;
517 int ret;
518 bool add = false;
519 const char *attrs[] = { NULL };
521 msg = ldb_msg_new(mem_ctx);
522 if (msg == NULL) {
523 return NT_STATUS_NO_MEMORY;
526 /* search for the group, by rid */
527 ret = gendb_search(state->sam_ldb, mem_ctx, state->base_dn[database], &msgs, attrs,
528 "(&(objectClass=group)(objectSid=%s))",
529 ldap_encode_ndr_dom_sid(mem_ctx, dom_sid_add_rid(mem_ctx, state->dom_sid[database], rid)));
531 if (ret == -1) {
532 *error_string = talloc_asprintf(mem_ctx, "gendb_search failed: %s", ldb_errstring(state->sam_ldb));
533 return NT_STATUS_INTERNAL_DB_CORRUPTION;
534 } else if (ret == 0) {
535 add = true;
536 } else if (ret > 1) {
537 *error_string = talloc_asprintf(mem_ctx, "More than one group/alias with SID: %s",
538 dom_sid_string(mem_ctx,
539 dom_sid_add_rid(mem_ctx,
540 state->dom_sid[database],
541 rid)));
542 return NT_STATUS_INTERNAL_DB_CORRUPTION;
543 } else {
544 msg->dn = talloc_steal(msg, msgs[0]->dn);
547 cn_name = group->group_name.string;
549 #define ADD_OR_DEL(type, attrib, field) do { \
550 if (group->field) { \
551 samdb_msg_add_ ## type(state->sam_ldb, mem_ctx, msg, \
552 attrib, group->field); \
553 } else if (!add) { \
554 samdb_msg_add_delete(state->sam_ldb, mem_ctx, msg, \
555 attrib); \
557 } while (0);
559 ADD_OR_DEL(string, "samAccountName", group_name.string);
561 if (samdb_msg_add_dom_sid(state->sam_ldb, mem_ctx, msg,
562 "objectSid", dom_sid_add_rid(mem_ctx, state->dom_sid[database], rid))) {
563 return NT_STATUS_NO_MEMORY;
566 ADD_OR_DEL(string, "description", description.string);
568 #undef ADD_OR_DEL
570 container = "Users";
571 obj_class = "group";
573 if (add) {
574 samdb_msg_add_string(state->sam_ldb, mem_ctx, msg,
575 "objectClass", obj_class);
576 msg->dn = ldb_dn_copy(mem_ctx, state->base_dn[database]);
577 ldb_dn_add_child_fmt(msg->dn, "CN=%s,CN=%s", cn_name, container);
578 if (!msg->dn) {
579 return NT_STATUS_NO_MEMORY;
582 ret = ldb_add(state->sam_ldb, msg);
583 if (ret != LDB_SUCCESS) {
584 *error_string = talloc_asprintf(mem_ctx, "Failed to create group record %s: %s",
585 ldb_dn_get_linearized(msg->dn),
586 ldb_errstring(state->sam_ldb));
587 return NT_STATUS_INTERNAL_DB_CORRUPTION;
589 } else {
590 ret = dsdb_replace(state->sam_ldb, msg, 0);
591 if (ret != LDB_SUCCESS) {
592 *error_string = talloc_asprintf(mem_ctx, "Failed to modify group record %s: %s",
593 ldb_dn_get_linearized(msg->dn),
594 ldb_errstring(state->sam_ldb));
595 return NT_STATUS_INTERNAL_DB_CORRUPTION;
599 return NT_STATUS_OK;
602 static NTSTATUS samsync_ldb_delete_group(TALLOC_CTX *mem_ctx,
603 struct samsync_ldb_state *state,
604 enum netr_SamDatabaseID database,
605 struct netr_DELTA_ENUM *delta,
606 char **error_string)
608 uint32_t rid = delta->delta_id_union.rid;
609 struct ldb_message **msgs;
610 int ret;
611 const char *attrs[] = { NULL };
613 /* search for the group, by rid */
614 ret = gendb_search(state->sam_ldb, mem_ctx, state->base_dn[database], &msgs, attrs,
615 "(&(objectClass=group)(objectSid=%s))",
616 ldap_encode_ndr_dom_sid(mem_ctx, dom_sid_add_rid(mem_ctx, state->dom_sid[database], rid)));
618 if (ret == -1) {
619 *error_string = talloc_asprintf(mem_ctx, "gendb_search failed: %s", ldb_errstring(state->sam_ldb));
620 return NT_STATUS_INTERNAL_DB_CORRUPTION;
621 } else if (ret == 0) {
622 return NT_STATUS_NO_SUCH_GROUP;
623 } else if (ret > 1) {
624 *error_string = talloc_asprintf(mem_ctx, "More than one group/alias with SID: %s",
625 dom_sid_string(mem_ctx,
626 dom_sid_add_rid(mem_ctx,
627 state->dom_sid[database],
628 rid)));
629 return NT_STATUS_INTERNAL_DB_CORRUPTION;
632 ret = ldb_delete(state->sam_ldb, msgs[0]->dn);
633 if (ret != LDB_SUCCESS) {
634 *error_string = talloc_asprintf(mem_ctx, "Failed to delete group record %s: %s",
635 ldb_dn_get_linearized(msgs[0]->dn),
636 ldb_errstring(state->sam_ldb));
637 return NT_STATUS_INTERNAL_DB_CORRUPTION;
640 return NT_STATUS_OK;
643 static NTSTATUS samsync_ldb_handle_group_member(TALLOC_CTX *mem_ctx,
644 struct samsync_ldb_state *state,
645 enum netr_SamDatabaseID database,
646 struct netr_DELTA_ENUM *delta,
647 char **error_string)
649 uint32_t rid = delta->delta_id_union.rid;
650 struct netr_DELTA_GROUP_MEMBER *group_member = delta->delta_union.group_member;
651 struct ldb_message *msg;
652 struct ldb_message **msgs;
653 int ret;
654 const char *attrs[] = { NULL };
655 uint32_t i;
657 msg = ldb_msg_new(mem_ctx);
658 if (msg == NULL) {
659 return NT_STATUS_NO_MEMORY;
662 /* search for the group, by rid */
663 ret = gendb_search(state->sam_ldb, mem_ctx, state->base_dn[database], &msgs, attrs,
664 "(&(objectClass=group)(objectSid=%s))",
665 ldap_encode_ndr_dom_sid(mem_ctx, dom_sid_add_rid(mem_ctx, state->dom_sid[database], rid)));
667 if (ret == -1) {
668 *error_string = talloc_asprintf(mem_ctx, "gendb_search failed: %s", ldb_errstring(state->sam_ldb));
669 return NT_STATUS_INTERNAL_DB_CORRUPTION;
670 } else if (ret == 0) {
671 return NT_STATUS_NO_SUCH_GROUP;
672 } else if (ret > 1) {
673 *error_string = talloc_asprintf(mem_ctx, "More than one group/alias with SID: %s",
674 dom_sid_string(mem_ctx,
675 dom_sid_add_rid(mem_ctx,
676 state->dom_sid[database],
677 rid)));
678 return NT_STATUS_INTERNAL_DB_CORRUPTION;
679 } else {
680 msg->dn = talloc_steal(msg, msgs[0]->dn);
683 talloc_free(msgs);
685 for (i=0; i<group_member->num_rids; i++) {
686 /* search for the group, by rid */
687 ret = gendb_search(state->sam_ldb, mem_ctx, state->base_dn[database], &msgs, attrs,
688 "(&(objectClass=user)(objectSid=%s))",
689 ldap_encode_ndr_dom_sid(mem_ctx, dom_sid_add_rid(mem_ctx, state->dom_sid[database], group_member->rids[i])));
691 if (ret == -1) {
692 *error_string = talloc_asprintf(mem_ctx, "gendb_search failed: %s", ldb_errstring(state->sam_ldb));
693 return NT_STATUS_INTERNAL_DB_CORRUPTION;
694 } else if (ret == 0) {
695 return NT_STATUS_NO_SUCH_USER;
696 } else if (ret > 1) {
697 return NT_STATUS_INTERNAL_DB_CORRUPTION;
698 } else {
699 samdb_msg_add_string(state->sam_ldb, mem_ctx, msg, "member", ldb_dn_alloc_linearized(mem_ctx, msgs[0]->dn));
702 talloc_free(msgs);
705 ret = dsdb_replace(state->sam_ldb, msg, 0);
706 if (ret != LDB_SUCCESS) {
707 *error_string = talloc_asprintf(mem_ctx, "Failed to modify group record %s: %s",
708 ldb_dn_get_linearized(msg->dn),
709 ldb_errstring(state->sam_ldb));
710 return NT_STATUS_INTERNAL_DB_CORRUPTION;
713 return NT_STATUS_OK;
716 static NTSTATUS samsync_ldb_handle_alias(TALLOC_CTX *mem_ctx,
717 struct samsync_ldb_state *state,
718 enum netr_SamDatabaseID database,
719 struct netr_DELTA_ENUM *delta,
720 char **error_string)
722 uint32_t rid = delta->delta_id_union.rid;
723 struct netr_DELTA_ALIAS *alias = delta->delta_union.alias;
724 const char *container, *obj_class;
725 const char *cn_name;
727 struct ldb_message *msg;
728 struct ldb_message **msgs;
729 int ret;
730 bool add = false;
731 const char *attrs[] = { NULL };
733 msg = ldb_msg_new(mem_ctx);
734 if (msg == NULL) {
735 return NT_STATUS_NO_MEMORY;
738 /* search for the alias, by rid */
739 ret = gendb_search(state->sam_ldb, mem_ctx, state->base_dn[database], &msgs, attrs,
740 "(&(objectClass=group)(objectSid=%s))",
741 ldap_encode_ndr_dom_sid(mem_ctx, dom_sid_add_rid(mem_ctx, state->dom_sid[database], rid)));
743 if (ret == -1) {
744 *error_string = talloc_asprintf(mem_ctx, "gendb_search failed: %s", ldb_errstring(state->sam_ldb));
745 return NT_STATUS_INTERNAL_DB_CORRUPTION;
746 } else if (ret == 0) {
747 add = true;
748 } else if (ret > 1) {
749 *error_string = talloc_asprintf(mem_ctx, "More than one group/alias with SID: %s",
750 dom_sid_string(mem_ctx,
751 dom_sid_add_rid(mem_ctx,
752 state->dom_sid[database],
753 rid)));
754 return NT_STATUS_INTERNAL_DB_CORRUPTION;
755 } else {
756 msg->dn = talloc_steal(mem_ctx, msgs[0]->dn);
759 cn_name = alias->alias_name.string;
761 #define ADD_OR_DEL(type, attrib, field) do { \
762 if (alias->field) { \
763 samdb_msg_add_ ## type(state->sam_ldb, mem_ctx, msg, \
764 attrib, alias->field); \
765 } else if (!add) { \
766 samdb_msg_add_delete(state->sam_ldb, mem_ctx, msg, \
767 attrib); \
769 } while (0);
771 ADD_OR_DEL(string, "samAccountName", alias_name.string);
773 if (samdb_msg_add_dom_sid(state->sam_ldb, mem_ctx, msg,
774 "objectSid", dom_sid_add_rid(mem_ctx, state->dom_sid[database], rid))) {
775 return NT_STATUS_NO_MEMORY;
778 ADD_OR_DEL(string, "description", description.string);
780 #undef ADD_OR_DEL
782 samdb_msg_add_uint(state->sam_ldb, mem_ctx, msg, "groupType", 0x80000004);
784 container = "Users";
785 obj_class = "group";
787 if (add) {
788 samdb_msg_add_string(state->sam_ldb, mem_ctx, msg,
789 "objectClass", obj_class);
790 msg->dn = ldb_dn_copy(mem_ctx, state->base_dn[database]);
791 ldb_dn_add_child_fmt(msg->dn, "CN=%s,CN=%s", cn_name, container);
792 if (!msg->dn) {
793 return NT_STATUS_NO_MEMORY;
796 ret = ldb_add(state->sam_ldb, msg);
797 if (ret != LDB_SUCCESS) {
798 *error_string = talloc_asprintf(mem_ctx, "Failed to create alias record %s: %s",
799 ldb_dn_get_linearized(msg->dn),
800 ldb_errstring(state->sam_ldb));
801 return NT_STATUS_INTERNAL_DB_CORRUPTION;
803 } else {
804 ret = dsdb_replace(state->sam_ldb, msg, 0);
805 if (ret != LDB_SUCCESS) {
806 *error_string = talloc_asprintf(mem_ctx, "Failed to modify alias record %s: %s",
807 ldb_dn_get_linearized(msg->dn),
808 ldb_errstring(state->sam_ldb));
809 return NT_STATUS_INTERNAL_DB_CORRUPTION;
813 return NT_STATUS_OK;
816 static NTSTATUS samsync_ldb_delete_alias(TALLOC_CTX *mem_ctx,
817 struct samsync_ldb_state *state,
818 enum netr_SamDatabaseID database,
819 struct netr_DELTA_ENUM *delta,
820 char **error_string)
822 uint32_t rid = delta->delta_id_union.rid;
823 struct ldb_message **msgs;
824 int ret;
825 const char *attrs[] = { NULL };
827 /* search for the alias, by rid */
828 ret = gendb_search(state->sam_ldb, mem_ctx, state->base_dn[database], &msgs, attrs,
829 "(&(objectClass=group)(objectSid=%s))",
830 ldap_encode_ndr_dom_sid(mem_ctx, dom_sid_add_rid(mem_ctx, state->dom_sid[database], rid)));
832 if (ret == -1) {
833 *error_string = talloc_asprintf(mem_ctx, "gendb_search failed: %s", ldb_errstring(state->sam_ldb));
834 return NT_STATUS_INTERNAL_DB_CORRUPTION;
835 } else if (ret == 0) {
836 return NT_STATUS_NO_SUCH_ALIAS;
837 } else if (ret > 1) {
838 return NT_STATUS_INTERNAL_DB_CORRUPTION;
841 ret = ldb_delete(state->sam_ldb, msgs[0]->dn);
842 if (ret != LDB_SUCCESS) {
843 *error_string = talloc_asprintf(mem_ctx, "Failed to delete alias record %s: %s",
844 ldb_dn_get_linearized(msgs[0]->dn),
845 ldb_errstring(state->sam_ldb));
846 return NT_STATUS_INTERNAL_DB_CORRUPTION;
849 return NT_STATUS_OK;
852 static NTSTATUS samsync_ldb_handle_alias_member(TALLOC_CTX *mem_ctx,
853 struct samsync_ldb_state *state,
854 enum netr_SamDatabaseID database,
855 struct netr_DELTA_ENUM *delta,
856 char **error_string)
858 uint32_t rid = delta->delta_id_union.rid;
859 struct netr_DELTA_ALIAS_MEMBER *alias_member = delta->delta_union.alias_member;
860 struct ldb_message *msg;
861 struct ldb_message **msgs;
862 int ret;
863 const char *attrs[] = { NULL };
864 uint32_t i;
866 msg = ldb_msg_new(mem_ctx);
867 if (msg == NULL) {
868 return NT_STATUS_NO_MEMORY;
871 /* search for the alias, by rid */
872 ret = gendb_search(state->sam_ldb, mem_ctx, state->base_dn[database], &msgs, attrs,
873 "(&(objectClass=group)(objectSid=%s))",
874 ldap_encode_ndr_dom_sid(mem_ctx, dom_sid_add_rid(mem_ctx, state->dom_sid[database], rid)));
876 if (ret == -1) {
877 *error_string = talloc_asprintf(mem_ctx, "gendb_search failed: %s", ldb_errstring(state->sam_ldb));
878 return NT_STATUS_INTERNAL_DB_CORRUPTION;
879 } else if (ret == 0) {
880 return NT_STATUS_NO_SUCH_GROUP;
881 } else if (ret > 1) {
882 *error_string = talloc_asprintf(mem_ctx, "More than one group/alias with SID: %s",
883 dom_sid_string(mem_ctx,
884 dom_sid_add_rid(mem_ctx,
885 state->dom_sid[database],
886 rid)));
887 return NT_STATUS_INTERNAL_DB_CORRUPTION;
888 } else {
889 msg->dn = talloc_steal(msg, msgs[0]->dn);
892 talloc_free(msgs);
894 for (i=0; i<alias_member->sids.num_sids; i++) {
895 struct ldb_dn *alias_member_dn;
896 /* search for members, in the top basedn (normal users are builtin aliases) */
897 ret = gendb_search(state->sam_ldb, mem_ctx, state->base_dn[SAM_DATABASE_DOMAIN], &msgs, attrs,
898 "(objectSid=%s)",
899 ldap_encode_ndr_dom_sid(mem_ctx, alias_member->sids.sids[i].sid));
901 if (ret == -1) {
902 *error_string = talloc_asprintf(mem_ctx, "gendb_search failed: %s", ldb_errstring(state->sam_ldb));
903 return NT_STATUS_INTERNAL_DB_CORRUPTION;
904 } else if (ret == 0) {
905 NTSTATUS nt_status;
906 nt_status = samsync_ldb_add_foreignSecurityPrincipal(mem_ctx, state,
907 alias_member->sids.sids[i].sid,
908 &alias_member_dn,
909 error_string);
910 if (!NT_STATUS_IS_OK(nt_status)) {
911 return nt_status;
913 } else if (ret > 1) {
914 return NT_STATUS_INTERNAL_DB_CORRUPTION;
915 } else {
916 alias_member_dn = msgs[0]->dn;
918 samdb_msg_add_string(state->sam_ldb, mem_ctx, msg, "member", ldb_dn_alloc_linearized(mem_ctx, alias_member_dn));
920 talloc_free(msgs);
923 ret = dsdb_replace(state->sam_ldb, msg, 0);
924 if (ret != LDB_SUCCESS) {
925 *error_string = talloc_asprintf(mem_ctx, "Failed to modify group record %s: %s",
926 ldb_dn_get_linearized(msg->dn),
927 ldb_errstring(state->sam_ldb));
928 return NT_STATUS_INTERNAL_DB_CORRUPTION;
931 return NT_STATUS_OK;
934 static NTSTATUS samsync_ldb_handle_account(TALLOC_CTX *mem_ctx,
935 struct samsync_ldb_state *state,
936 enum netr_SamDatabaseID database,
937 struct netr_DELTA_ENUM *delta,
938 char **error_string)
940 struct dom_sid *sid = delta->delta_id_union.sid;
941 struct netr_DELTA_ACCOUNT *account = delta->delta_union.account;
943 struct ldb_message *msg;
944 int ret;
945 uint32_t i;
946 char *dnstr, *sidstr;
948 msg = ldb_msg_new(mem_ctx);
949 if (msg == NULL) {
950 return NT_STATUS_NO_MEMORY;
953 sidstr = dom_sid_string(msg, sid);
954 NT_STATUS_HAVE_NO_MEMORY_AND_FREE(sidstr, msg);
956 dnstr = talloc_asprintf(msg, "sid=%s", sidstr);
957 NT_STATUS_HAVE_NO_MEMORY_AND_FREE(dnstr, msg);
959 msg->dn = ldb_dn_new(msg, state->pdb, dnstr);
960 NT_STATUS_HAVE_NO_MEMORY_AND_FREE(msg->dn, msg);
962 for (i=0; i< account->privilege_entries; i++) {
963 samdb_msg_add_string(state->pdb, mem_ctx, msg, "privilege",
964 account->privilege_name[i].string);
967 ret = dsdb_replace(state->pdb, msg, 0);
968 if (ret == LDB_ERR_NO_SUCH_OBJECT) {
969 if (samdb_msg_add_dom_sid(state->pdb, msg, msg, "objectSid", sid) != LDB_SUCCESS) {
970 talloc_free(msg);
971 return NT_STATUS_NO_MEMORY;
973 samdb_msg_add_string(state->pdb, msg, msg, "comment", "added via samsync");
974 ret = ldb_add(state->pdb, msg);
977 if (ret != LDB_SUCCESS) {
978 *error_string = talloc_asprintf(mem_ctx, "Failed to modify privilege record %s",
979 ldb_dn_get_linearized(msg->dn));
980 return NT_STATUS_INTERNAL_DB_CORRUPTION;
983 return NT_STATUS_OK;
986 static NTSTATUS samsync_ldb_delete_account(TALLOC_CTX *mem_ctx,
987 struct samsync_ldb_state *state,
988 enum netr_SamDatabaseID database,
989 struct netr_DELTA_ENUM *delta,
990 char **error_string)
992 struct dom_sid *sid = delta->delta_id_union.sid;
994 struct ldb_message *msg;
995 struct ldb_message **msgs;
996 int ret;
997 const char *attrs[] = { NULL };
999 msg = ldb_msg_new(mem_ctx);
1000 if (msg == NULL) {
1001 return NT_STATUS_NO_MEMORY;
1004 /* search for the account, by sid, in the top basedn */
1005 ret = gendb_search(state->sam_ldb, mem_ctx, state->base_dn[SAM_DATABASE_DOMAIN], &msgs, attrs,
1006 "(objectSid=%s)",
1007 ldap_encode_ndr_dom_sid(mem_ctx, sid));
1009 if (ret == -1) {
1010 *error_string = talloc_asprintf(mem_ctx, "gendb_search failed: %s", ldb_errstring(state->sam_ldb));
1011 return NT_STATUS_INTERNAL_DB_CORRUPTION;
1012 } else if (ret == 0) {
1013 return NT_STATUS_NO_SUCH_USER;
1014 } else if (ret > 1) {
1015 *error_string = talloc_asprintf(mem_ctx, "More than one account with SID: %s",
1016 dom_sid_string(mem_ctx, sid));
1017 return NT_STATUS_INTERNAL_DB_CORRUPTION;
1018 } else {
1019 msg->dn = talloc_steal(msg, msgs[0]->dn);
1022 samdb_msg_add_delete(state->sam_ldb, mem_ctx, msg,
1023 "privilege");
1025 ret = dsdb_replace(state->sam_ldb, msg, 0);
1026 if (ret != LDB_SUCCESS) {
1027 *error_string = talloc_asprintf(mem_ctx, "Failed to modify privilege record %s",
1028 ldb_dn_get_linearized(msg->dn));
1029 return NT_STATUS_INTERNAL_DB_CORRUPTION;
1032 return NT_STATUS_OK;
1035 static NTSTATUS libnet_samsync_ldb_fn(TALLOC_CTX *mem_ctx,
1036 void *private_data,
1037 enum netr_SamDatabaseID database,
1038 struct netr_DELTA_ENUM *delta,
1039 char **error_string)
1041 NTSTATUS nt_status = NT_STATUS_OK;
1042 struct samsync_ldb_state *state = talloc_get_type(private_data, struct samsync_ldb_state);
1044 *error_string = NULL;
1045 switch (delta->delta_type) {
1046 case NETR_DELTA_DOMAIN:
1048 nt_status = samsync_ldb_handle_domain(mem_ctx,
1049 state,
1050 database,
1051 delta,
1052 error_string);
1053 break;
1055 case NETR_DELTA_USER:
1057 nt_status = samsync_ldb_handle_user(mem_ctx,
1058 state,
1059 database,
1060 delta,
1061 error_string);
1062 break;
1064 case NETR_DELTA_DELETE_USER:
1066 nt_status = samsync_ldb_delete_user(mem_ctx,
1067 state,
1068 database,
1069 delta,
1070 error_string);
1071 break;
1073 case NETR_DELTA_GROUP:
1075 nt_status = samsync_ldb_handle_group(mem_ctx,
1076 state,
1077 database,
1078 delta,
1079 error_string);
1080 break;
1082 case NETR_DELTA_DELETE_GROUP:
1084 nt_status = samsync_ldb_delete_group(mem_ctx,
1085 state,
1086 database,
1087 delta,
1088 error_string);
1089 break;
1091 case NETR_DELTA_GROUP_MEMBER:
1093 nt_status = samsync_ldb_handle_group_member(mem_ctx,
1094 state,
1095 database,
1096 delta,
1097 error_string);
1098 break;
1100 case NETR_DELTA_ALIAS:
1102 nt_status = samsync_ldb_handle_alias(mem_ctx,
1103 state,
1104 database,
1105 delta,
1106 error_string);
1107 break;
1109 case NETR_DELTA_DELETE_ALIAS:
1111 nt_status = samsync_ldb_delete_alias(mem_ctx,
1112 state,
1113 database,
1114 delta,
1115 error_string);
1116 break;
1118 case NETR_DELTA_ALIAS_MEMBER:
1120 nt_status = samsync_ldb_handle_alias_member(mem_ctx,
1121 state,
1122 database,
1123 delta,
1124 error_string);
1125 break;
1127 case NETR_DELTA_ACCOUNT:
1129 nt_status = samsync_ldb_handle_account(mem_ctx,
1130 state,
1131 database,
1132 delta,
1133 error_string);
1134 break;
1136 case NETR_DELTA_DELETE_ACCOUNT:
1138 nt_status = samsync_ldb_delete_account(mem_ctx,
1139 state,
1140 database,
1141 delta,
1142 error_string);
1143 break;
1145 default:
1146 /* Can't dump them all right now */
1147 break;
1149 if (!NT_STATUS_IS_OK(nt_status) && !*error_string) {
1150 *error_string = talloc_asprintf(mem_ctx, "Failed to handle samsync delta: %s", nt_errstr(nt_status));
1152 return nt_status;
1155 static NTSTATUS libnet_samsync_ldb_init(TALLOC_CTX *mem_ctx,
1156 void *private_data,
1157 struct libnet_SamSync_state *samsync_state,
1158 char **error_string)
1160 struct samsync_ldb_state *state = talloc_get_type(private_data, struct samsync_ldb_state);
1161 const char *server = dcerpc_server_name(samsync_state->netlogon_pipe);
1162 char *ldap_url;
1164 state->samsync_state = samsync_state;
1166 ZERO_STRUCT(state->dom_sid);
1167 if (state->samsync_state->domain_sid) {
1168 state->dom_sid[SAM_DATABASE_DOMAIN] = dom_sid_dup(state, state->samsync_state->domain_sid);
1171 state->dom_sid[SAM_DATABASE_BUILTIN] = dom_sid_parse_talloc(state, SID_BUILTIN);
1173 if (state->samsync_state->realm) {
1174 if (!server || !*server) {
1175 /* huh? how do we not have a server name? */
1176 *error_string = talloc_strdup(mem_ctx, "No DCE/RPC server name available. How did we connect?");
1177 return NT_STATUS_INVALID_PARAMETER;
1179 ldap_url = talloc_asprintf(state, "ldap://%s", server);
1181 state->remote_ldb = ldb_wrap_connect(mem_ctx,
1182 state->samsync_state->machine_net_ctx->event_ctx,
1183 state->samsync_state->machine_net_ctx->lp_ctx,
1184 ldap_url,
1185 NULL, state->samsync_state->machine_net_ctx->cred,
1187 if (!state->remote_ldb) {
1188 *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);
1189 return NT_STATUS_NO_LOGON_SERVERS;
1191 } else {
1192 state->remote_ldb = NULL;
1194 return NT_STATUS_OK;
1197 NTSTATUS libnet_samsync_ldb(struct libnet_context *ctx, TALLOC_CTX *mem_ctx, struct libnet_samsync_ldb *r)
1199 NTSTATUS nt_status;
1200 struct libnet_SamSync r2;
1201 struct samsync_ldb_state *state = talloc(mem_ctx, struct samsync_ldb_state);
1203 if (!state) {
1204 return NT_STATUS_NO_MEMORY;
1207 state->secrets = NULL;
1208 state->trusted_domains = NULL;
1210 state->sam_ldb = samdb_connect(mem_ctx,
1211 ctx->event_ctx,
1212 ctx->lp_ctx,
1213 r->in.session_info,
1215 if (!state->sam_ldb) {
1216 return NT_STATUS_INTERNAL_DB_ERROR;
1219 state->pdb = privilege_connect(mem_ctx,
1220 ctx->event_ctx,
1221 ctx->lp_ctx);
1222 if (!state->pdb) {
1223 return NT_STATUS_INTERNAL_DB_ERROR;
1226 r2.out.error_string = NULL;
1227 r2.in.binding_string = r->in.binding_string;
1228 r2.in.init_fn = libnet_samsync_ldb_init;
1229 r2.in.delta_fn = libnet_samsync_ldb_fn;
1230 r2.in.fn_ctx = state;
1231 r2.in.machine_account = NULL; /* TODO: Create a machine account, fill this in, and the delete it */
1232 nt_status = libnet_SamSync_netlogon(ctx, state, &r2);
1233 r->out.error_string = r2.out.error_string;
1234 talloc_steal(mem_ctx, r->out.error_string);
1236 if (!NT_STATUS_IS_OK(nt_status)) {
1237 talloc_free(state);
1238 return nt_status;
1240 talloc_free(state);
1241 return nt_status;