Additionally send the vfs function id with the protocol.
[Samba/ekacnet.git] / source4 / libnet / libnet_samsync_ldb.c
blobfa39375e1602b675c79810d285a94d74d063d90c
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 "librpc/rpc/dcerpc.h"
35 #include "param/param.h"
37 struct samsync_ldb_secret {
38 struct samsync_ldb_secret *prev, *next;
39 DATA_BLOB secret;
40 char *name;
41 NTTIME mtime;
44 struct samsync_ldb_trusted_domain {
45 struct samsync_ldb_trusted_domain *prev, *next;
46 struct dom_sid *sid;
47 char *name;
50 struct samsync_ldb_state {
51 /* Values from the LSA lookup */
52 const struct libnet_SamSync_state *samsync_state;
54 struct dom_sid *dom_sid[3];
55 struct ldb_context *sam_ldb, *remote_ldb, *pdb;
56 struct ldb_dn *base_dn[3];
57 struct samsync_ldb_secret *secrets;
58 struct samsync_ldb_trusted_domain *trusted_domains;
61 static NTSTATUS samsync_ldb_add_foreignSecurityPrincipal(TALLOC_CTX *mem_ctx,
62 struct samsync_ldb_state *state,
63 struct dom_sid *sid,
64 struct ldb_dn **fsp_dn,
65 char **error_string)
67 const char *sidstr = dom_sid_string(mem_ctx, sid);
68 /* We assume that ForeignSecurityPrincipals are under the BASEDN of the main domain */
69 struct ldb_dn *basedn = samdb_search_dn(state->sam_ldb, mem_ctx,
70 state->base_dn[SAM_DATABASE_DOMAIN],
71 "(&(objectClass=container)(cn=ForeignSecurityPrincipals))");
72 struct ldb_message *msg;
73 int ret;
75 if (!sidstr) {
76 return NT_STATUS_NO_MEMORY;
79 if (basedn == NULL) {
80 *error_string = talloc_asprintf(mem_ctx,
81 "Failed to find DN for "
82 "ForeignSecurityPrincipal container under %s",
83 ldb_dn_get_linearized(state->base_dn[SAM_DATABASE_DOMAIN]));
84 return NT_STATUS_INTERNAL_DB_CORRUPTION;
87 msg = ldb_msg_new(mem_ctx);
88 if (msg == NULL) {
89 return NT_STATUS_NO_MEMORY;
92 /* add core elements to the ldb_message for the alias */
93 msg->dn = basedn;
94 if ( ! ldb_dn_add_child_fmt(msg->dn, "CN=%s", sidstr))
95 return NT_STATUS_UNSUCCESSFUL;
97 samdb_msg_add_string(state->sam_ldb, mem_ctx, msg,
98 "objectClass",
99 "foreignSecurityPrincipal");
101 *fsp_dn = msg->dn;
103 /* create the alias */
104 ret = ldb_add(state->sam_ldb, msg);
105 if (ret != 0) {
106 *error_string = talloc_asprintf(mem_ctx, "Failed to create foreignSecurityPrincipal "
107 "record %s: %s",
108 ldb_dn_get_linearized(msg->dn),
109 ldb_errstring(state->sam_ldb));
110 return NT_STATUS_INTERNAL_DB_CORRUPTION;
112 return NT_STATUS_OK;
115 static NTSTATUS samsync_ldb_handle_domain(TALLOC_CTX *mem_ctx,
116 struct samsync_ldb_state *state,
117 enum netr_SamDatabaseID database,
118 struct netr_DELTA_ENUM *delta,
119 char **error_string)
121 struct netr_DELTA_DOMAIN *domain = delta->delta_union.domain;
122 const char *domain_name = domain->domain_name.string;
123 struct ldb_message *msg;
124 int ret;
126 msg = ldb_msg_new(mem_ctx);
127 if (msg == NULL) {
128 return NT_STATUS_NO_MEMORY;
131 if (database == SAM_DATABASE_DOMAIN) {
132 struct ldb_dn *partitions_basedn;
133 const char *domain_attrs[] = {"nETBIOSName", "nCName", NULL};
134 struct ldb_message **msgs_domain;
135 int ret_domain;
137 partitions_basedn = samdb_partitions_dn(state->sam_ldb, mem_ctx);
139 ret_domain = gendb_search(state->sam_ldb, mem_ctx, partitions_basedn, &msgs_domain, domain_attrs,
140 "(&(&(nETBIOSName=%s)(objectclass=crossRef))(ncName=*))",
141 domain_name);
142 if (ret_domain == -1) {
143 *error_string = talloc_asprintf(mem_ctx, "gendb_search for domain failed: %s", ldb_errstring(state->sam_ldb));
144 return NT_STATUS_INTERNAL_DB_CORRUPTION;
147 if (ret_domain != 1) {
148 *error_string = talloc_asprintf(mem_ctx, "Failed to find existing domain record for %s: %d results", domain_name,
149 ret_domain);
150 return NT_STATUS_NO_SUCH_DOMAIN;
153 state->base_dn[database] = samdb_result_dn(state->sam_ldb, state, msgs_domain[0], "nCName", NULL);
155 if (state->dom_sid[database]) {
156 /* Update the domain sid with the incoming
157 * domain (found on LSA pipe, database sid may
158 * be random) */
159 samdb_msg_add_dom_sid(state->sam_ldb, mem_ctx,
160 msg, "objectSid", state->dom_sid[database]);
161 } else {
162 /* Well, we will have to use the one from the database */
163 state->dom_sid[database] = samdb_search_dom_sid(state->sam_ldb, state,
164 state->base_dn[database],
165 "objectSid", NULL);
168 if (state->samsync_state->domain_guid) {
169 struct ldb_val v;
170 NTSTATUS status;
171 status = GUID_to_ndr_blob(state->samsync_state->domain_guid, msg, &v);
172 if (!NT_STATUS_IS_OK(status)) {
173 *error_string = talloc_asprintf(mem_ctx, "ndr_push of domain GUID failed!");
174 return status;
177 ldb_msg_add_value(msg, "objectGUID", &v, NULL);
179 } else if (database == SAM_DATABASE_BUILTIN) {
180 /* work out the builtin_dn - useful for so many calls its worth
181 fetching here */
182 const char *dnstring = samdb_search_string(state->sam_ldb, mem_ctx, NULL,
183 "distinguishedName", "objectClass=builtinDomain");
184 state->base_dn[database] = ldb_dn_new(state, state->sam_ldb, dnstring);
185 if ( ! ldb_dn_validate(state->base_dn[database])) {
186 return NT_STATUS_INTERNAL_ERROR;
188 } else {
189 /* PRIVs DB */
190 return NT_STATUS_INVALID_PARAMETER;
193 msg->dn = talloc_reference(mem_ctx, state->base_dn[database]);
194 if (!msg->dn) {
195 return NT_STATUS_NO_MEMORY;
198 samdb_msg_add_string(state->sam_ldb, mem_ctx,
199 msg, "oEMInformation", domain->oem_information.string);
201 samdb_msg_add_int64(state->sam_ldb, mem_ctx,
202 msg, "forceLogoff", domain->force_logoff_time);
204 samdb_msg_add_uint(state->sam_ldb, mem_ctx,
205 msg, "minPwdLen", domain->min_password_length);
207 samdb_msg_add_int64(state->sam_ldb, mem_ctx,
208 msg, "maxPwdAge", domain->max_password_age);
210 samdb_msg_add_int64(state->sam_ldb, mem_ctx,
211 msg, "minPwdAge", domain->min_password_age);
213 samdb_msg_add_uint(state->sam_ldb, mem_ctx,
214 msg, "pwdHistoryLength", domain->password_history_length);
216 samdb_msg_add_uint64(state->sam_ldb, mem_ctx,
217 msg, "modifiedCount",
218 domain->sequence_num);
220 samdb_msg_add_uint64(state->sam_ldb, mem_ctx,
221 msg, "creationTime", domain->domain_create_time);
223 /* TODO: Account lockout, password properties */
225 ret = dsdb_replace(state->sam_ldb, msg, 0);
227 if (ret) {
228 return NT_STATUS_INTERNAL_ERROR;
230 return NT_STATUS_OK;
233 static NTSTATUS samsync_ldb_handle_user(TALLOC_CTX *mem_ctx,
234 struct samsync_ldb_state *state,
235 enum netr_SamDatabaseID database,
236 struct netr_DELTA_ENUM *delta,
237 char **error_string)
239 uint32_t rid = delta->delta_id_union.rid;
240 struct netr_DELTA_USER *user = delta->delta_union.user;
241 const char *container, *obj_class;
242 char *cn_name;
243 int cn_name_len;
244 const struct dom_sid *user_sid;
245 struct ldb_message *msg;
246 struct ldb_message **msgs;
247 struct ldb_message **remote_msgs = NULL;
248 unsigned int i;
249 int ret;
250 uint32_t acb;
251 bool add = false;
252 const char *attrs[] = { NULL };
253 /* we may change this to a global search, then fill in only the things not in ldap later */
254 const char *remote_attrs[] = { "userPrincipalName", "servicePrincipalName",
255 "msDS-KeyVersionNumber", "objectGUID", NULL};
257 user_sid = dom_sid_add_rid(mem_ctx, state->dom_sid[database], rid);
258 if (!user_sid) {
259 return NT_STATUS_NO_MEMORY;
262 msg = ldb_msg_new(mem_ctx);
263 if (msg == NULL) {
264 return NT_STATUS_NO_MEMORY;
267 msg->dn = NULL;
268 /* search for the user, by rid */
269 ret = gendb_search(state->sam_ldb, mem_ctx, state->base_dn[database],
270 &msgs, attrs, "(&(objectClass=user)(objectSid=%s))",
271 ldap_encode_ndr_dom_sid(mem_ctx, user_sid));
273 if (ret == -1) {
274 *error_string = talloc_asprintf(mem_ctx, "LDB for user %s failed: %s",
275 dom_sid_string(mem_ctx, user_sid),
276 ldb_errstring(state->sam_ldb));
277 return NT_STATUS_INTERNAL_DB_CORRUPTION;
278 } else if (ret == 0) {
279 add = true;
280 } else if (ret > 1) {
281 *error_string = talloc_asprintf(mem_ctx, "More than one user with SID: %s in local LDB",
282 dom_sid_string(mem_ctx, user_sid));
283 return NT_STATUS_INTERNAL_DB_CORRUPTION;
284 } else {
285 msg->dn = msgs[0]->dn;
286 talloc_steal(msg, msgs[0]->dn);
289 /* and do the same on the remote database */
290 if (state->remote_ldb) {
291 ret = gendb_search(state->remote_ldb, mem_ctx, state->base_dn[database],
292 &remote_msgs, remote_attrs, "(&(objectClass=user)(objectSid=%s))",
293 ldap_encode_ndr_dom_sid(mem_ctx, user_sid));
295 if (ret == -1) {
296 *error_string = talloc_asprintf(mem_ctx, "remote LDAP for user %s failed: %s",
297 dom_sid_string(mem_ctx, user_sid),
298 ldb_errstring(state->remote_ldb));
299 return NT_STATUS_INTERNAL_DB_CORRUPTION;
300 } else if (ret == 0) {
301 *error_string = talloc_asprintf(mem_ctx, "User exists in samsync but not in remote LDAP domain! (base: %s, SID: %s)",
302 ldb_dn_get_linearized(state->base_dn[database]),
303 dom_sid_string(mem_ctx, user_sid));
304 return NT_STATUS_NO_SUCH_USER;
305 } else if (ret > 1) {
306 *error_string = talloc_asprintf(mem_ctx, "More than one user in remote LDAP domain with SID: %s",
307 dom_sid_string(mem_ctx, user_sid));
308 return NT_STATUS_INTERNAL_DB_CORRUPTION;
310 /* Try to put things in the same location as the remote server */
311 } else if (add) {
312 msg->dn = remote_msgs[0]->dn;
313 talloc_steal(msg, remote_msgs[0]->dn);
317 cn_name = talloc_strdup(mem_ctx, user->account_name.string);
318 NT_STATUS_HAVE_NO_MEMORY(cn_name);
319 cn_name_len = strlen(cn_name);
321 #define ADD_OR_DEL(type, attrib, field) do { \
322 if (user->field) { \
323 samdb_msg_add_ ## type(state->sam_ldb, mem_ctx, msg, \
324 attrib, user->field); \
325 } else if (!add) { \
326 samdb_msg_add_delete(state->sam_ldb, mem_ctx, msg, \
327 attrib); \
329 } while (0);
331 ADD_OR_DEL(string, "samAccountName", account_name.string);
332 ADD_OR_DEL(string, "displayName", full_name.string);
334 if (samdb_msg_add_dom_sid(state->sam_ldb, mem_ctx, msg,
335 "objectSid", dom_sid_add_rid(mem_ctx, state->dom_sid[database], rid))) {
336 return NT_STATUS_NO_MEMORY;
339 ADD_OR_DEL(uint, "primaryGroupID", primary_gid);
340 ADD_OR_DEL(string, "homeDirectory", home_directory.string);
341 ADD_OR_DEL(string, "homeDrive", home_drive.string);
342 ADD_OR_DEL(string, "scriptPath", logon_script.string);
343 ADD_OR_DEL(string, "description", description.string);
344 ADD_OR_DEL(string, "userWorkstations", workstations.string);
346 ADD_OR_DEL(uint64, "lastLogon", last_logon);
347 ADD_OR_DEL(uint64, "lastLogoff", last_logoff);
349 if (samdb_msg_add_logon_hours(state->sam_ldb, mem_ctx, msg, "logonHours", &user->logon_hours) != 0) {
350 return NT_STATUS_NO_MEMORY;
353 ADD_OR_DEL(uint, "badPwdCount", bad_password_count);
354 ADD_OR_DEL(uint, "logonCount", logon_count);
356 ADD_OR_DEL(uint64, "pwdLastSet", last_password_change);
357 ADD_OR_DEL(uint64, "accountExpires", acct_expiry);
359 if (samdb_msg_add_acct_flags(state->sam_ldb, mem_ctx, msg,
360 "userAccountControl", user->acct_flags) != 0) {
361 return NT_STATUS_NO_MEMORY;
364 if (!add) {
365 /* Passwords. Ensure there is no plaintext stored against
366 * this entry, as we only have hashes */
367 samdb_msg_add_delete(state->sam_ldb, mem_ctx, msg,
368 "userPassword");
370 if (user->lm_password_present) {
371 samdb_msg_add_hash(state->sam_ldb, mem_ctx, msg,
372 "dBCSPwd", &user->lmpassword);
373 } else if (!add) {
374 samdb_msg_add_delete(state->sam_ldb, mem_ctx, msg,
375 "dBCSPwd");
377 if (user->nt_password_present) {
378 samdb_msg_add_hash(state->sam_ldb, mem_ctx, msg,
379 "unicodePwd", &user->ntpassword);
380 } else if (!add) {
381 samdb_msg_add_delete(state->sam_ldb, mem_ctx, msg,
382 "unicodePwd");
385 ADD_OR_DEL(string, "comment", comment.string);
387 if (samdb_msg_add_parameters(state->sam_ldb, mem_ctx, msg, "userParameters", &user->parameters) != 0) {
388 return NT_STATUS_NO_MEMORY;
391 ADD_OR_DEL(uint, "countryCode", country_code);
392 ADD_OR_DEL(uint, "codePage", code_page);
394 ADD_OR_DEL(string, "profilePath", profile_path.string);
396 #undef ADD_OR_DEL
398 for (i=0; remote_attrs[i]; i++) {
399 struct ldb_message_element *el = ldb_msg_find_element(remote_msgs[0], remote_attrs[i]);
400 if (!el) {
401 samdb_msg_add_delete(state->sam_ldb, mem_ctx, msg,
402 remote_attrs[i]);
403 } else {
404 ldb_msg_add(msg, el, LDB_FLAG_MOD_REPLACE);
408 acb = user->acct_flags;
409 if (acb & (ACB_WSTRUST)) {
410 cn_name[cn_name_len - 1] = '\0';
411 container = "Computers";
412 obj_class = "computer";
414 } else if (acb & ACB_SVRTRUST) {
415 if (cn_name[cn_name_len - 1] != '$') {
416 return NT_STATUS_FOOBAR;
418 cn_name[cn_name_len - 1] = '\0';
419 container = "Domain Controllers";
420 obj_class = "computer";
421 } else {
422 container = "Users";
423 obj_class = "user";
425 if (add) {
426 samdb_msg_add_string(state->sam_ldb, mem_ctx, msg,
427 "objectClass", obj_class);
428 if (!msg->dn) {
429 msg->dn = ldb_dn_copy(mem_ctx, state->base_dn[database]);
430 ldb_dn_add_child_fmt(msg->dn, "CN=%s,CN=%s", cn_name, container);
431 if (!msg->dn) {
432 return NT_STATUS_NO_MEMORY;
436 ret = ldb_add(state->sam_ldb, msg);
437 if (ret != 0) {
438 struct ldb_dn *first_try_dn = msg->dn;
439 /* Try again with the default DN */
440 if (!remote_msgs) {
441 *error_string = talloc_asprintf(mem_ctx, "Failed to create user record. Tried %s: %s",
442 ldb_dn_get_linearized(first_try_dn),
443 ldb_errstring(state->sam_ldb));
444 return NT_STATUS_INTERNAL_DB_CORRUPTION;
445 } else {
446 msg->dn = talloc_steal(msg, remote_msgs[0]->dn);
447 ret = ldb_add(state->sam_ldb, msg);
448 if (ret != 0) {
449 *error_string = talloc_asprintf(mem_ctx, "Failed to create user record. Tried both %s and %s: %s",
450 ldb_dn_get_linearized(first_try_dn),
451 ldb_dn_get_linearized(msg->dn),
452 ldb_errstring(state->sam_ldb));
453 return NT_STATUS_INTERNAL_DB_CORRUPTION;
457 } else {
458 ret = dsdb_replace(state->sam_ldb, msg, 0);
459 if (ret != 0) {
460 *error_string = talloc_asprintf(mem_ctx, "Failed to modify user record %s: %s",
461 ldb_dn_get_linearized(msg->dn),
462 ldb_errstring(state->sam_ldb));
463 return NT_STATUS_INTERNAL_DB_CORRUPTION;
467 return NT_STATUS_OK;
470 static NTSTATUS samsync_ldb_delete_user(TALLOC_CTX *mem_ctx,
471 struct samsync_ldb_state *state,
472 enum netr_SamDatabaseID database,
473 struct netr_DELTA_ENUM *delta,
474 char **error_string)
476 uint32_t rid = delta->delta_id_union.rid;
477 struct ldb_message **msgs;
478 int ret;
479 const char *attrs[] = { NULL };
481 /* search for the user, by rid */
482 ret = gendb_search(state->sam_ldb, mem_ctx, state->base_dn[database],
483 &msgs, attrs, "(&(objectClass=user)(objectSid=%s))",
484 ldap_encode_ndr_dom_sid(mem_ctx, dom_sid_add_rid(mem_ctx, state->dom_sid[database], rid)));
486 if (ret == -1) {
487 *error_string = talloc_asprintf(mem_ctx, "gendb_search failed: %s", ldb_errstring(state->sam_ldb));
488 return NT_STATUS_INTERNAL_DB_CORRUPTION;
489 } else if (ret == 0) {
490 return NT_STATUS_NO_SUCH_USER;
491 } else if (ret > 1) {
492 *error_string = talloc_asprintf(mem_ctx, "More than one user with SID: %s",
493 dom_sid_string(mem_ctx,
494 dom_sid_add_rid(mem_ctx,
495 state->dom_sid[database],
496 rid)));
497 return NT_STATUS_INTERNAL_DB_CORRUPTION;
500 ret = ldb_delete(state->sam_ldb, msgs[0]->dn);
501 if (ret != 0) {
502 *error_string = talloc_asprintf(mem_ctx, "Failed to delete user record %s: %s",
503 ldb_dn_get_linearized(msgs[0]->dn),
504 ldb_errstring(state->sam_ldb));
505 return NT_STATUS_INTERNAL_DB_CORRUPTION;
508 return NT_STATUS_OK;
511 static NTSTATUS samsync_ldb_handle_group(TALLOC_CTX *mem_ctx,
512 struct samsync_ldb_state *state,
513 enum netr_SamDatabaseID database,
514 struct netr_DELTA_ENUM *delta,
515 char **error_string)
517 uint32_t rid = delta->delta_id_union.rid;
518 struct netr_DELTA_GROUP *group = delta->delta_union.group;
519 const char *container, *obj_class;
520 const char *cn_name;
522 struct ldb_message *msg;
523 struct ldb_message **msgs;
524 int ret;
525 bool add = false;
526 const char *attrs[] = { NULL };
528 msg = ldb_msg_new(mem_ctx);
529 if (msg == NULL) {
530 return NT_STATUS_NO_MEMORY;
533 /* search for the group, by rid */
534 ret = gendb_search(state->sam_ldb, mem_ctx, state->base_dn[database], &msgs, attrs,
535 "(&(objectClass=group)(objectSid=%s))",
536 ldap_encode_ndr_dom_sid(mem_ctx, dom_sid_add_rid(mem_ctx, state->dom_sid[database], rid)));
538 if (ret == -1) {
539 *error_string = talloc_asprintf(mem_ctx, "gendb_search failed: %s", ldb_errstring(state->sam_ldb));
540 return NT_STATUS_INTERNAL_DB_CORRUPTION;
541 } else if (ret == 0) {
542 add = true;
543 } else if (ret > 1) {
544 *error_string = talloc_asprintf(mem_ctx, "More than one group/alias with SID: %s",
545 dom_sid_string(mem_ctx,
546 dom_sid_add_rid(mem_ctx,
547 state->dom_sid[database],
548 rid)));
549 return NT_STATUS_INTERNAL_DB_CORRUPTION;
550 } else {
551 msg->dn = talloc_steal(msg, msgs[0]->dn);
554 cn_name = group->group_name.string;
556 #define ADD_OR_DEL(type, attrib, field) do { \
557 if (group->field) { \
558 samdb_msg_add_ ## type(state->sam_ldb, mem_ctx, msg, \
559 attrib, group->field); \
560 } else if (!add) { \
561 samdb_msg_add_delete(state->sam_ldb, mem_ctx, msg, \
562 attrib); \
564 } while (0);
566 ADD_OR_DEL(string, "samAccountName", group_name.string);
568 if (samdb_msg_add_dom_sid(state->sam_ldb, mem_ctx, msg,
569 "objectSid", dom_sid_add_rid(mem_ctx, state->dom_sid[database], rid))) {
570 return NT_STATUS_NO_MEMORY;
573 ADD_OR_DEL(string, "description", description.string);
575 #undef ADD_OR_DEL
577 container = "Users";
578 obj_class = "group";
580 if (add) {
581 samdb_msg_add_string(state->sam_ldb, mem_ctx, msg,
582 "objectClass", obj_class);
583 msg->dn = ldb_dn_copy(mem_ctx, state->base_dn[database]);
584 ldb_dn_add_child_fmt(msg->dn, "CN=%s,CN=%s", cn_name, container);
585 if (!msg->dn) {
586 return NT_STATUS_NO_MEMORY;
589 ret = ldb_add(state->sam_ldb, msg);
590 if (ret != 0) {
591 *error_string = talloc_asprintf(mem_ctx, "Failed to create group record %s: %s",
592 ldb_dn_get_linearized(msg->dn),
593 ldb_errstring(state->sam_ldb));
594 return NT_STATUS_INTERNAL_DB_CORRUPTION;
596 } else {
597 ret = dsdb_replace(state->sam_ldb, msg, 0);
598 if (ret != 0) {
599 *error_string = talloc_asprintf(mem_ctx, "Failed to modify group record %s: %s",
600 ldb_dn_get_linearized(msg->dn),
601 ldb_errstring(state->sam_ldb));
602 return NT_STATUS_INTERNAL_DB_CORRUPTION;
606 return NT_STATUS_OK;
609 static NTSTATUS samsync_ldb_delete_group(TALLOC_CTX *mem_ctx,
610 struct samsync_ldb_state *state,
611 enum netr_SamDatabaseID database,
612 struct netr_DELTA_ENUM *delta,
613 char **error_string)
615 uint32_t rid = delta->delta_id_union.rid;
616 struct ldb_message **msgs;
617 int ret;
618 const char *attrs[] = { NULL };
620 /* search for the group, by rid */
621 ret = gendb_search(state->sam_ldb, mem_ctx, state->base_dn[database], &msgs, attrs,
622 "(&(objectClass=group)(objectSid=%s))",
623 ldap_encode_ndr_dom_sid(mem_ctx, dom_sid_add_rid(mem_ctx, state->dom_sid[database], rid)));
625 if (ret == -1) {
626 *error_string = talloc_asprintf(mem_ctx, "gendb_search failed: %s", ldb_errstring(state->sam_ldb));
627 return NT_STATUS_INTERNAL_DB_CORRUPTION;
628 } else if (ret == 0) {
629 return NT_STATUS_NO_SUCH_GROUP;
630 } else if (ret > 1) {
631 *error_string = talloc_asprintf(mem_ctx, "More than one group/alias with SID: %s",
632 dom_sid_string(mem_ctx,
633 dom_sid_add_rid(mem_ctx,
634 state->dom_sid[database],
635 rid)));
636 return NT_STATUS_INTERNAL_DB_CORRUPTION;
639 ret = ldb_delete(state->sam_ldb, msgs[0]->dn);
640 if (ret != 0) {
641 *error_string = talloc_asprintf(mem_ctx, "Failed to delete group record %s: %s",
642 ldb_dn_get_linearized(msgs[0]->dn),
643 ldb_errstring(state->sam_ldb));
644 return NT_STATUS_INTERNAL_DB_CORRUPTION;
647 return NT_STATUS_OK;
650 static NTSTATUS samsync_ldb_handle_group_member(TALLOC_CTX *mem_ctx,
651 struct samsync_ldb_state *state,
652 enum netr_SamDatabaseID database,
653 struct netr_DELTA_ENUM *delta,
654 char **error_string)
656 uint32_t rid = delta->delta_id_union.rid;
657 struct netr_DELTA_GROUP_MEMBER *group_member = delta->delta_union.group_member;
658 struct ldb_message *msg;
659 struct ldb_message **msgs;
660 int ret;
661 const char *attrs[] = { NULL };
662 uint32_t i;
664 msg = ldb_msg_new(mem_ctx);
665 if (msg == NULL) {
666 return NT_STATUS_NO_MEMORY;
669 /* search for the group, by rid */
670 ret = gendb_search(state->sam_ldb, mem_ctx, state->base_dn[database], &msgs, attrs,
671 "(&(objectClass=group)(objectSid=%s))",
672 ldap_encode_ndr_dom_sid(mem_ctx, dom_sid_add_rid(mem_ctx, state->dom_sid[database], rid)));
674 if (ret == -1) {
675 *error_string = talloc_asprintf(mem_ctx, "gendb_search failed: %s", ldb_errstring(state->sam_ldb));
676 return NT_STATUS_INTERNAL_DB_CORRUPTION;
677 } else if (ret == 0) {
678 return NT_STATUS_NO_SUCH_GROUP;
679 } else if (ret > 1) {
680 *error_string = talloc_asprintf(mem_ctx, "More than one group/alias with SID: %s",
681 dom_sid_string(mem_ctx,
682 dom_sid_add_rid(mem_ctx,
683 state->dom_sid[database],
684 rid)));
685 return NT_STATUS_INTERNAL_DB_CORRUPTION;
686 } else {
687 msg->dn = talloc_steal(msg, msgs[0]->dn);
690 talloc_free(msgs);
692 for (i=0; i<group_member->num_rids; i++) {
693 /* search for the group, by rid */
694 ret = gendb_search(state->sam_ldb, mem_ctx, state->base_dn[database], &msgs, attrs,
695 "(&(objectClass=user)(objectSid=%s))",
696 ldap_encode_ndr_dom_sid(mem_ctx, dom_sid_add_rid(mem_ctx, state->dom_sid[database], group_member->rids[i])));
698 if (ret == -1) {
699 *error_string = talloc_asprintf(mem_ctx, "gendb_search failed: %s", ldb_errstring(state->sam_ldb));
700 return NT_STATUS_INTERNAL_DB_CORRUPTION;
701 } else if (ret == 0) {
702 return NT_STATUS_NO_SUCH_USER;
703 } else if (ret > 1) {
704 return NT_STATUS_INTERNAL_DB_CORRUPTION;
705 } else {
706 samdb_msg_add_string(state->sam_ldb, mem_ctx, msg, "member", ldb_dn_alloc_linearized(mem_ctx, msgs[0]->dn));
709 talloc_free(msgs);
712 ret = dsdb_replace(state->sam_ldb, msg, 0);
713 if (ret != 0) {
714 *error_string = talloc_asprintf(mem_ctx, "Failed to modify group record %s: %s",
715 ldb_dn_get_linearized(msg->dn),
716 ldb_errstring(state->sam_ldb));
717 return NT_STATUS_INTERNAL_DB_CORRUPTION;
720 return NT_STATUS_OK;
723 static NTSTATUS samsync_ldb_handle_alias(TALLOC_CTX *mem_ctx,
724 struct samsync_ldb_state *state,
725 enum netr_SamDatabaseID database,
726 struct netr_DELTA_ENUM *delta,
727 char **error_string)
729 uint32_t rid = delta->delta_id_union.rid;
730 struct netr_DELTA_ALIAS *alias = delta->delta_union.alias;
731 const char *container, *obj_class;
732 const char *cn_name;
734 struct ldb_message *msg;
735 struct ldb_message **msgs;
736 int ret;
737 bool add = false;
738 const char *attrs[] = { NULL };
740 msg = ldb_msg_new(mem_ctx);
741 if (msg == NULL) {
742 return NT_STATUS_NO_MEMORY;
745 /* search for the alias, by rid */
746 ret = gendb_search(state->sam_ldb, mem_ctx, state->base_dn[database], &msgs, attrs,
747 "(&(objectClass=group)(objectSid=%s))",
748 ldap_encode_ndr_dom_sid(mem_ctx, dom_sid_add_rid(mem_ctx, state->dom_sid[database], rid)));
750 if (ret == -1) {
751 *error_string = talloc_asprintf(mem_ctx, "gendb_search failed: %s", ldb_errstring(state->sam_ldb));
752 return NT_STATUS_INTERNAL_DB_CORRUPTION;
753 } else if (ret == 0) {
754 add = true;
755 } else if (ret > 1) {
756 *error_string = talloc_asprintf(mem_ctx, "More than one group/alias with SID: %s",
757 dom_sid_string(mem_ctx,
758 dom_sid_add_rid(mem_ctx,
759 state->dom_sid[database],
760 rid)));
761 return NT_STATUS_INTERNAL_DB_CORRUPTION;
762 } else {
763 msg->dn = talloc_steal(mem_ctx, msgs[0]->dn);
766 cn_name = alias->alias_name.string;
768 #define ADD_OR_DEL(type, attrib, field) do { \
769 if (alias->field) { \
770 samdb_msg_add_ ## type(state->sam_ldb, mem_ctx, msg, \
771 attrib, alias->field); \
772 } else if (!add) { \
773 samdb_msg_add_delete(state->sam_ldb, mem_ctx, msg, \
774 attrib); \
776 } while (0);
778 ADD_OR_DEL(string, "samAccountName", alias_name.string);
780 if (samdb_msg_add_dom_sid(state->sam_ldb, mem_ctx, msg,
781 "objectSid", dom_sid_add_rid(mem_ctx, state->dom_sid[database], rid))) {
782 return NT_STATUS_NO_MEMORY;
785 ADD_OR_DEL(string, "description", description.string);
787 #undef ADD_OR_DEL
789 samdb_msg_add_uint(state->sam_ldb, mem_ctx, msg, "groupType", 0x80000004);
791 container = "Users";
792 obj_class = "group";
794 if (add) {
795 samdb_msg_add_string(state->sam_ldb, mem_ctx, msg,
796 "objectClass", obj_class);
797 msg->dn = ldb_dn_copy(mem_ctx, state->base_dn[database]);
798 ldb_dn_add_child_fmt(msg->dn, "CN=%s,CN=%s", cn_name, container);
799 if (!msg->dn) {
800 return NT_STATUS_NO_MEMORY;
803 ret = ldb_add(state->sam_ldb, msg);
804 if (ret != 0) {
805 *error_string = talloc_asprintf(mem_ctx, "Failed to create alias record %s: %s",
806 ldb_dn_get_linearized(msg->dn),
807 ldb_errstring(state->sam_ldb));
808 return NT_STATUS_INTERNAL_DB_CORRUPTION;
810 } else {
811 ret = dsdb_replace(state->sam_ldb, msg, 0);
812 if (ret != 0) {
813 *error_string = talloc_asprintf(mem_ctx, "Failed to modify alias record %s: %s",
814 ldb_dn_get_linearized(msg->dn),
815 ldb_errstring(state->sam_ldb));
816 return NT_STATUS_INTERNAL_DB_CORRUPTION;
820 return NT_STATUS_OK;
823 static NTSTATUS samsync_ldb_delete_alias(TALLOC_CTX *mem_ctx,
824 struct samsync_ldb_state *state,
825 enum netr_SamDatabaseID database,
826 struct netr_DELTA_ENUM *delta,
827 char **error_string)
829 uint32_t rid = delta->delta_id_union.rid;
830 struct ldb_message **msgs;
831 int ret;
832 const char *attrs[] = { NULL };
834 /* search for the alias, by rid */
835 ret = gendb_search(state->sam_ldb, mem_ctx, state->base_dn[database], &msgs, attrs,
836 "(&(objectClass=group)(objectSid=%s))",
837 ldap_encode_ndr_dom_sid(mem_ctx, dom_sid_add_rid(mem_ctx, state->dom_sid[database], rid)));
839 if (ret == -1) {
840 *error_string = talloc_asprintf(mem_ctx, "gendb_search failed: %s", ldb_errstring(state->sam_ldb));
841 return NT_STATUS_INTERNAL_DB_CORRUPTION;
842 } else if (ret == 0) {
843 return NT_STATUS_NO_SUCH_ALIAS;
844 } else if (ret > 1) {
845 return NT_STATUS_INTERNAL_DB_CORRUPTION;
848 ret = ldb_delete(state->sam_ldb, msgs[0]->dn);
849 if (ret != 0) {
850 *error_string = talloc_asprintf(mem_ctx, "Failed to delete alias record %s: %s",
851 ldb_dn_get_linearized(msgs[0]->dn),
852 ldb_errstring(state->sam_ldb));
853 return NT_STATUS_INTERNAL_DB_CORRUPTION;
856 return NT_STATUS_OK;
859 static NTSTATUS samsync_ldb_handle_alias_member(TALLOC_CTX *mem_ctx,
860 struct samsync_ldb_state *state,
861 enum netr_SamDatabaseID database,
862 struct netr_DELTA_ENUM *delta,
863 char **error_string)
865 uint32_t rid = delta->delta_id_union.rid;
866 struct netr_DELTA_ALIAS_MEMBER *alias_member = delta->delta_union.alias_member;
867 struct ldb_message *msg;
868 struct ldb_message **msgs;
869 int ret;
870 const char *attrs[] = { NULL };
871 uint32_t i;
873 msg = ldb_msg_new(mem_ctx);
874 if (msg == NULL) {
875 return NT_STATUS_NO_MEMORY;
878 /* search for the alias, by rid */
879 ret = gendb_search(state->sam_ldb, mem_ctx, state->base_dn[database], &msgs, attrs,
880 "(&(objectClass=group)(objectSid=%s))",
881 ldap_encode_ndr_dom_sid(mem_ctx, dom_sid_add_rid(mem_ctx, state->dom_sid[database], rid)));
883 if (ret == -1) {
884 *error_string = talloc_asprintf(mem_ctx, "gendb_search failed: %s", ldb_errstring(state->sam_ldb));
885 return NT_STATUS_INTERNAL_DB_CORRUPTION;
886 } else if (ret == 0) {
887 return NT_STATUS_NO_SUCH_GROUP;
888 } else if (ret > 1) {
889 *error_string = talloc_asprintf(mem_ctx, "More than one group/alias with SID: %s",
890 dom_sid_string(mem_ctx,
891 dom_sid_add_rid(mem_ctx,
892 state->dom_sid[database],
893 rid)));
894 return NT_STATUS_INTERNAL_DB_CORRUPTION;
895 } else {
896 msg->dn = talloc_steal(msg, msgs[0]->dn);
899 talloc_free(msgs);
901 for (i=0; i<alias_member->sids.num_sids; i++) {
902 struct ldb_dn *alias_member_dn;
903 /* search for members, in the top basedn (normal users are builtin aliases) */
904 ret = gendb_search(state->sam_ldb, mem_ctx, state->base_dn[SAM_DATABASE_DOMAIN], &msgs, attrs,
905 "(objectSid=%s)",
906 ldap_encode_ndr_dom_sid(mem_ctx, alias_member->sids.sids[i].sid));
908 if (ret == -1) {
909 *error_string = talloc_asprintf(mem_ctx, "gendb_search failed: %s", ldb_errstring(state->sam_ldb));
910 return NT_STATUS_INTERNAL_DB_CORRUPTION;
911 } else if (ret == 0) {
912 NTSTATUS nt_status;
913 nt_status = samsync_ldb_add_foreignSecurityPrincipal(mem_ctx, state,
914 alias_member->sids.sids[i].sid,
915 &alias_member_dn,
916 error_string);
917 if (!NT_STATUS_IS_OK(nt_status)) {
918 return nt_status;
920 } else if (ret > 1) {
921 return NT_STATUS_INTERNAL_DB_CORRUPTION;
922 } else {
923 alias_member_dn = msgs[0]->dn;
925 samdb_msg_add_string(state->sam_ldb, mem_ctx, msg, "member", ldb_dn_alloc_linearized(mem_ctx, alias_member_dn));
927 talloc_free(msgs);
930 ret = dsdb_replace(state->sam_ldb, msg, 0);
931 if (ret != 0) {
932 *error_string = talloc_asprintf(mem_ctx, "Failed to modify group record %s: %s",
933 ldb_dn_get_linearized(msg->dn),
934 ldb_errstring(state->sam_ldb));
935 return NT_STATUS_INTERNAL_DB_CORRUPTION;
938 return NT_STATUS_OK;
941 static NTSTATUS samsync_ldb_handle_account(TALLOC_CTX *mem_ctx,
942 struct samsync_ldb_state *state,
943 enum netr_SamDatabaseID database,
944 struct netr_DELTA_ENUM *delta,
945 char **error_string)
947 struct dom_sid *sid = delta->delta_id_union.sid;
948 struct netr_DELTA_ACCOUNT *account = delta->delta_union.account;
950 struct ldb_message *msg;
951 int ret;
952 uint32_t i;
953 char *dnstr, *sidstr;
955 msg = ldb_msg_new(mem_ctx);
956 if (msg == NULL) {
957 return NT_STATUS_NO_MEMORY;
960 sidstr = dom_sid_string(msg, sid);
961 NT_STATUS_HAVE_NO_MEMORY_AND_FREE(sidstr, msg);
963 dnstr = talloc_asprintf(msg, "sid=%s", sidstr);
964 NT_STATUS_HAVE_NO_MEMORY_AND_FREE(dnstr, msg);
966 msg->dn = ldb_dn_new(msg, state->pdb, dnstr);
967 NT_STATUS_HAVE_NO_MEMORY_AND_FREE(msg->dn, msg);
969 for (i=0; i< account->privilege_entries; i++) {
970 samdb_msg_add_string(state->pdb, mem_ctx, msg, "privilege",
971 account->privilege_name[i].string);
974 ret = dsdb_replace(state->pdb, msg, 0);
975 if (ret == LDB_ERR_NO_SUCH_OBJECT) {
976 if (samdb_msg_add_dom_sid(state->pdb, msg, msg, "objectSid", sid) != LDB_SUCCESS) {
977 talloc_free(msg);
978 return NT_STATUS_NO_MEMORY;
980 samdb_msg_add_string(state->pdb, msg, msg, "comment", "added via samsync");
981 ret = ldb_add(state->pdb, msg);
984 if (ret != 0) {
985 *error_string = talloc_asprintf(mem_ctx, "Failed to modify privilege record %s",
986 ldb_dn_get_linearized(msg->dn));
987 return NT_STATUS_INTERNAL_DB_CORRUPTION;
990 return NT_STATUS_OK;
993 static NTSTATUS samsync_ldb_delete_account(TALLOC_CTX *mem_ctx,
994 struct samsync_ldb_state *state,
995 enum netr_SamDatabaseID database,
996 struct netr_DELTA_ENUM *delta,
997 char **error_string)
999 struct dom_sid *sid = delta->delta_id_union.sid;
1001 struct ldb_message *msg;
1002 struct ldb_message **msgs;
1003 int ret;
1004 const char *attrs[] = { NULL };
1006 msg = ldb_msg_new(mem_ctx);
1007 if (msg == NULL) {
1008 return NT_STATUS_NO_MEMORY;
1011 /* search for the account, by sid, in the top basedn */
1012 ret = gendb_search(state->sam_ldb, mem_ctx, state->base_dn[SAM_DATABASE_DOMAIN], &msgs, attrs,
1013 "(objectSid=%s)",
1014 ldap_encode_ndr_dom_sid(mem_ctx, sid));
1016 if (ret == -1) {
1017 *error_string = talloc_asprintf(mem_ctx, "gendb_search failed: %s", ldb_errstring(state->sam_ldb));
1018 return NT_STATUS_INTERNAL_DB_CORRUPTION;
1019 } else if (ret == 0) {
1020 return NT_STATUS_NO_SUCH_USER;
1021 } else if (ret > 1) {
1022 *error_string = talloc_asprintf(mem_ctx, "More than one account with SID: %s",
1023 dom_sid_string(mem_ctx, sid));
1024 return NT_STATUS_INTERNAL_DB_CORRUPTION;
1025 } else {
1026 msg->dn = talloc_steal(msg, msgs[0]->dn);
1029 samdb_msg_add_delete(state->sam_ldb, mem_ctx, msg,
1030 "privilege");
1032 ret = dsdb_replace(state->sam_ldb, msg, 0);
1033 if (ret != 0) {
1034 *error_string = talloc_asprintf(mem_ctx, "Failed to modify privilege record %s",
1035 ldb_dn_get_linearized(msg->dn));
1036 return NT_STATUS_INTERNAL_DB_CORRUPTION;
1039 return NT_STATUS_OK;
1042 static NTSTATUS libnet_samsync_ldb_fn(TALLOC_CTX *mem_ctx,
1043 void *private_data,
1044 enum netr_SamDatabaseID database,
1045 struct netr_DELTA_ENUM *delta,
1046 char **error_string)
1048 NTSTATUS nt_status = NT_STATUS_OK;
1049 struct samsync_ldb_state *state = talloc_get_type(private_data, struct samsync_ldb_state);
1051 *error_string = NULL;
1052 switch (delta->delta_type) {
1053 case NETR_DELTA_DOMAIN:
1055 nt_status = samsync_ldb_handle_domain(mem_ctx,
1056 state,
1057 database,
1058 delta,
1059 error_string);
1060 break;
1062 case NETR_DELTA_USER:
1064 nt_status = samsync_ldb_handle_user(mem_ctx,
1065 state,
1066 database,
1067 delta,
1068 error_string);
1069 break;
1071 case NETR_DELTA_DELETE_USER:
1073 nt_status = samsync_ldb_delete_user(mem_ctx,
1074 state,
1075 database,
1076 delta,
1077 error_string);
1078 break;
1080 case NETR_DELTA_GROUP:
1082 nt_status = samsync_ldb_handle_group(mem_ctx,
1083 state,
1084 database,
1085 delta,
1086 error_string);
1087 break;
1089 case NETR_DELTA_DELETE_GROUP:
1091 nt_status = samsync_ldb_delete_group(mem_ctx,
1092 state,
1093 database,
1094 delta,
1095 error_string);
1096 break;
1098 case NETR_DELTA_GROUP_MEMBER:
1100 nt_status = samsync_ldb_handle_group_member(mem_ctx,
1101 state,
1102 database,
1103 delta,
1104 error_string);
1105 break;
1107 case NETR_DELTA_ALIAS:
1109 nt_status = samsync_ldb_handle_alias(mem_ctx,
1110 state,
1111 database,
1112 delta,
1113 error_string);
1114 break;
1116 case NETR_DELTA_DELETE_ALIAS:
1118 nt_status = samsync_ldb_delete_alias(mem_ctx,
1119 state,
1120 database,
1121 delta,
1122 error_string);
1123 break;
1125 case NETR_DELTA_ALIAS_MEMBER:
1127 nt_status = samsync_ldb_handle_alias_member(mem_ctx,
1128 state,
1129 database,
1130 delta,
1131 error_string);
1132 break;
1134 case NETR_DELTA_ACCOUNT:
1136 nt_status = samsync_ldb_handle_account(mem_ctx,
1137 state,
1138 database,
1139 delta,
1140 error_string);
1141 break;
1143 case NETR_DELTA_DELETE_ACCOUNT:
1145 nt_status = samsync_ldb_delete_account(mem_ctx,
1146 state,
1147 database,
1148 delta,
1149 error_string);
1150 break;
1152 default:
1153 /* Can't dump them all right now */
1154 break;
1156 if (!NT_STATUS_IS_OK(nt_status) && !*error_string) {
1157 *error_string = talloc_asprintf(mem_ctx, "Failed to handle samsync delta: %s", nt_errstr(nt_status));
1159 return nt_status;
1162 static NTSTATUS libnet_samsync_ldb_init(TALLOC_CTX *mem_ctx,
1163 void *private_data,
1164 struct libnet_SamSync_state *samsync_state,
1165 char **error_string)
1167 struct samsync_ldb_state *state = talloc_get_type(private_data, struct samsync_ldb_state);
1168 const char *server = dcerpc_server_name(samsync_state->netlogon_pipe);
1169 char *ldap_url;
1171 state->samsync_state = samsync_state;
1173 ZERO_STRUCT(state->dom_sid);
1174 if (state->samsync_state->domain_sid) {
1175 state->dom_sid[SAM_DATABASE_DOMAIN] = dom_sid_dup(state, state->samsync_state->domain_sid);
1178 state->dom_sid[SAM_DATABASE_BUILTIN] = dom_sid_parse_talloc(state, SID_BUILTIN);
1180 if (state->samsync_state->realm) {
1181 if (!server || !*server) {
1182 /* huh? how do we not have a server name? */
1183 *error_string = talloc_strdup(mem_ctx, "No DCE/RPC server name available. How did we connect?");
1184 return NT_STATUS_INVALID_PARAMETER;
1186 ldap_url = talloc_asprintf(state, "ldap://%s", server);
1188 state->remote_ldb = ldb_wrap_connect(mem_ctx,
1189 state->samsync_state->machine_net_ctx->event_ctx,
1190 state->samsync_state->machine_net_ctx->lp_ctx,
1191 ldap_url,
1192 NULL, state->samsync_state->machine_net_ctx->cred,
1194 if (!state->remote_ldb) {
1195 *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);
1196 return NT_STATUS_NO_LOGON_SERVERS;
1198 } else {
1199 state->remote_ldb = NULL;
1201 return NT_STATUS_OK;
1204 NTSTATUS libnet_samsync_ldb(struct libnet_context *ctx, TALLOC_CTX *mem_ctx, struct libnet_samsync_ldb *r)
1206 NTSTATUS nt_status;
1207 struct libnet_SamSync r2;
1208 struct samsync_ldb_state *state = talloc(mem_ctx, struct samsync_ldb_state);
1210 if (!state) {
1211 return NT_STATUS_NO_MEMORY;
1214 state->secrets = NULL;
1215 state->trusted_domains = NULL;
1217 state->sam_ldb = samdb_connect(mem_ctx,
1218 ctx->event_ctx,
1219 ctx->lp_ctx,
1220 r->in.session_info);
1221 if (!state->sam_ldb) {
1222 return NT_STATUS_INTERNAL_DB_ERROR;
1225 state->pdb = privilege_connect(mem_ctx,
1226 ctx->event_ctx,
1227 ctx->lp_ctx);
1228 if (!state->pdb) {
1229 return NT_STATUS_INTERNAL_DB_ERROR;
1232 r2.out.error_string = NULL;
1233 r2.in.binding_string = r->in.binding_string;
1234 r2.in.init_fn = libnet_samsync_ldb_init;
1235 r2.in.delta_fn = libnet_samsync_ldb_fn;
1236 r2.in.fn_ctx = state;
1237 r2.in.machine_account = NULL; /* TODO: Create a machine account, fill this in, and the delete it */
1238 nt_status = libnet_SamSync_netlogon(ctx, state, &r2);
1239 r->out.error_string = r2.out.error_string;
1240 talloc_steal(mem_ctx, r->out.error_string);
1242 if (!NT_STATUS_IS_OK(nt_status)) {
1243 talloc_free(state);
1244 return nt_status;
1246 talloc_free(state);
1247 return nt_status;