VERSION: Bump version up to 4.9.0rc1...
[Samba.git] / source4 / libnet / libnet_samsync_ldb.c
blob9ea7dce041c66eed5e94212e12be66c8d7cf2926
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 /* This wrapper is needed for the "ADD_OR_DEL" macros */
61 static int samdb_msg_add_string(struct ldb_context *sam_ldb,
62 TALLOC_CTX *mem_ctx, struct ldb_message *msg,
63 const char *attr_name, const char *str)
65 return ldb_msg_add_string(msg, attr_name, str);
68 static NTSTATUS samsync_ldb_add_foreignSecurityPrincipal(TALLOC_CTX *mem_ctx,
69 struct samsync_ldb_state *state,
70 struct dom_sid *sid,
71 struct ldb_dn **fsp_dn,
72 char **error_string)
74 const char *sidstr = dom_sid_string(mem_ctx, sid);
75 /* We assume that ForeignSecurityPrincipals are under the BASEDN of the main domain */
76 struct ldb_dn *basedn = samdb_search_dn(state->sam_ldb, mem_ctx,
77 state->base_dn[SAM_DATABASE_DOMAIN],
78 "(&(objectClass=container)(cn=ForeignSecurityPrincipals))");
79 struct ldb_message *msg;
80 int ret;
82 if (!sidstr) {
83 return NT_STATUS_NO_MEMORY;
86 if (basedn == NULL) {
87 *error_string = talloc_asprintf(mem_ctx,
88 "Failed to find DN for "
89 "ForeignSecurityPrincipal container under %s",
90 ldb_dn_get_linearized(state->base_dn[SAM_DATABASE_DOMAIN]));
91 return NT_STATUS_INTERNAL_DB_CORRUPTION;
94 msg = ldb_msg_new(mem_ctx);
95 if (msg == NULL) {
96 return NT_STATUS_NO_MEMORY;
99 /* add core elements to the ldb_message for the alias */
100 msg->dn = basedn;
101 if ( ! ldb_dn_add_child_fmt(msg->dn, "CN=%s", sidstr))
102 return NT_STATUS_UNSUCCESSFUL;
104 ldb_msg_add_string(msg, "objectClass", "foreignSecurityPrincipal");
106 *fsp_dn = msg->dn;
108 /* create the alias */
109 ret = ldb_add(state->sam_ldb, msg);
110 if (ret != LDB_SUCCESS) {
111 *error_string = talloc_asprintf(mem_ctx, "Failed to create foreignSecurityPrincipal "
112 "record %s: %s",
113 ldb_dn_get_linearized(msg->dn),
114 ldb_errstring(state->sam_ldb));
115 return NT_STATUS_INTERNAL_DB_CORRUPTION;
117 return NT_STATUS_OK;
120 static NTSTATUS samsync_ldb_handle_domain(TALLOC_CTX *mem_ctx,
121 struct samsync_ldb_state *state,
122 enum netr_SamDatabaseID database,
123 struct netr_DELTA_ENUM *delta,
124 char **error_string)
126 struct netr_DELTA_DOMAIN *domain = delta->delta_union.domain;
127 const char *domain_name = domain->domain_name.string;
128 struct ldb_message *msg;
129 int ret;
131 msg = ldb_msg_new(mem_ctx);
132 if (msg == NULL) {
133 return NT_STATUS_NO_MEMORY;
136 if (database == SAM_DATABASE_DOMAIN) {
137 struct ldb_dn *partitions_basedn;
138 const char *domain_attrs[] = {"nETBIOSName", "nCName", NULL};
139 struct ldb_message **msgs_domain;
140 int ret_domain;
142 partitions_basedn = samdb_partitions_dn(state->sam_ldb, mem_ctx);
144 ret_domain = gendb_search(state->sam_ldb, mem_ctx, partitions_basedn, &msgs_domain, domain_attrs,
145 "(&(&(nETBIOSName=%s)(objectclass=crossRef))(ncName=*))",
146 domain_name);
147 if (ret_domain == -1) {
148 *error_string = talloc_asprintf(mem_ctx, "gendb_search for domain failed: %s", ldb_errstring(state->sam_ldb));
149 return NT_STATUS_INTERNAL_DB_CORRUPTION;
152 if (ret_domain != 1) {
153 *error_string = talloc_asprintf(mem_ctx, "Failed to find existing domain record for %s: %d results", domain_name,
154 ret_domain);
155 return NT_STATUS_NO_SUCH_DOMAIN;
158 state->base_dn[database] = samdb_result_dn(state->sam_ldb, state, msgs_domain[0], "nCName", NULL);
160 if (state->dom_sid[database]) {
161 /* Update the domain sid with the incoming
162 * domain (found on LSA pipe, database sid may
163 * be random) */
164 ret = samdb_msg_add_dom_sid(state->sam_ldb,
165 mem_ctx,
166 msg,
167 "objectSid",
168 state->dom_sid[database]);
169 if (ret != LDB_SUCCESS) {
170 return NT_STATUS_INTERNAL_ERROR;
172 } else {
173 /* Well, we will have to use the one from the database */
174 state->dom_sid[database] = samdb_search_dom_sid(state->sam_ldb, state,
175 state->base_dn[database],
176 "objectSid", NULL);
177 if (state->dom_sid[database] == NULL) {
178 return NT_STATUS_INTERNAL_ERROR;
182 if (state->samsync_state->domain_guid) {
183 struct ldb_val v;
184 NTSTATUS status;
185 status = GUID_to_ndr_blob(state->samsync_state->domain_guid, msg, &v);
186 if (!NT_STATUS_IS_OK(status)) {
187 *error_string = talloc_asprintf(mem_ctx, "ndr_push of domain GUID failed!");
188 return status;
191 ret = ldb_msg_add_value(msg, "objectGUID", &v, NULL);
192 if (ret != LDB_SUCCESS) {
193 return NT_STATUS_INTERNAL_ERROR;
196 } else if (database == SAM_DATABASE_BUILTIN) {
197 /* work out the builtin_dn - useful for so many calls its worth
198 fetching here */
199 const char *dnstring = samdb_search_string(state->sam_ldb, mem_ctx, NULL,
200 "distinguishedName", "objectClass=builtinDomain");
201 state->base_dn[database] = ldb_dn_new(state, state->sam_ldb, dnstring);
202 if ( ! ldb_dn_validate(state->base_dn[database])) {
203 return NT_STATUS_INTERNAL_ERROR;
205 } else {
206 /* PRIVs DB */
207 return NT_STATUS_INVALID_PARAMETER;
210 msg->dn = talloc_reference(mem_ctx, state->base_dn[database]);
211 if (!msg->dn) {
212 return NT_STATUS_NO_MEMORY;
215 ldb_msg_add_string(msg, "oEMInformation",
216 domain->oem_information.string);
218 samdb_msg_add_int64(state->sam_ldb, mem_ctx,
219 msg, "forceLogoff", domain->force_logoff_time);
221 samdb_msg_add_uint(state->sam_ldb, mem_ctx,
222 msg, "minPwdLen", domain->min_password_length);
224 samdb_msg_add_int64(state->sam_ldb, mem_ctx,
225 msg, "maxPwdAge", domain->max_password_age);
227 samdb_msg_add_int64(state->sam_ldb, mem_ctx,
228 msg, "minPwdAge", domain->min_password_age);
230 samdb_msg_add_uint(state->sam_ldb, mem_ctx,
231 msg, "pwdHistoryLength", domain->password_history_length);
233 samdb_msg_add_uint64(state->sam_ldb, mem_ctx,
234 msg, "modifiedCount",
235 domain->sequence_num);
237 samdb_msg_add_uint64(state->sam_ldb, mem_ctx,
238 msg, "creationTime", domain->domain_create_time);
240 /* TODO: Account lockout, password properties */
242 ret = dsdb_replace(state->sam_ldb, msg, 0);
243 if (ret != LDB_SUCCESS) {
244 *error_string = talloc_asprintf(mem_ctx,
245 "Failed to modify domain record %s: %s",
246 ldb_dn_get_linearized(msg->dn),
247 ldb_errstring(state->sam_ldb));
248 return NT_STATUS_INTERNAL_DB_CORRUPTION;
250 return NT_STATUS_OK;
253 static NTSTATUS samsync_ldb_handle_user(TALLOC_CTX *mem_ctx,
254 struct samsync_ldb_state *state,
255 enum netr_SamDatabaseID database,
256 struct netr_DELTA_ENUM *delta,
257 char **error_string)
259 uint32_t rid = delta->delta_id_union.rid;
260 struct netr_DELTA_USER *user = delta->delta_union.user;
261 const char *container, *obj_class;
262 char *cn_name;
263 int cn_name_len;
264 const struct dom_sid *user_sid;
265 struct ldb_message *msg;
266 struct ldb_message **msgs;
267 struct ldb_message **remote_msgs = NULL;
268 unsigned int i;
269 int ret;
270 uint32_t acb;
271 bool add = false;
272 const char *attrs[] = { NULL };
273 /* we may change this to a global search, then fill in only the things not in ldap later */
274 const char *remote_attrs[] = { "userPrincipalName", "servicePrincipalName",
275 "msDS-KeyVersionNumber", "objectGUID", NULL};
277 user_sid = dom_sid_add_rid(mem_ctx, state->dom_sid[database], rid);
278 if (!user_sid) {
279 return NT_STATUS_NO_MEMORY;
282 msg = ldb_msg_new(mem_ctx);
283 if (msg == NULL) {
284 return NT_STATUS_NO_MEMORY;
287 msg->dn = NULL;
288 /* search for the user, by rid */
289 ret = gendb_search(state->sam_ldb, mem_ctx, state->base_dn[database],
290 &msgs, attrs, "(&(objectClass=user)(objectSid=%s))",
291 ldap_encode_ndr_dom_sid(mem_ctx, user_sid));
293 if (ret == -1) {
294 *error_string = talloc_asprintf(mem_ctx, "LDB for user %s failed: %s",
295 dom_sid_string(mem_ctx, user_sid),
296 ldb_errstring(state->sam_ldb));
297 return NT_STATUS_INTERNAL_DB_CORRUPTION;
298 } else if (ret == 0) {
299 add = true;
300 } else if (ret > 1) {
301 *error_string = talloc_asprintf(mem_ctx, "More than one user with SID: %s in local LDB",
302 dom_sid_string(mem_ctx, user_sid));
303 return NT_STATUS_INTERNAL_DB_CORRUPTION;
304 } else {
305 msg->dn = msgs[0]->dn;
306 talloc_steal(msg, msgs[0]->dn);
309 /* and do the same on the remote database */
310 if (state->remote_ldb) {
311 ret = gendb_search(state->remote_ldb, mem_ctx, state->base_dn[database],
312 &remote_msgs, remote_attrs, "(&(objectClass=user)(objectSid=%s))",
313 ldap_encode_ndr_dom_sid(mem_ctx, user_sid));
315 if (ret == -1) {
316 *error_string = talloc_asprintf(mem_ctx, "remote LDAP for user %s failed: %s",
317 dom_sid_string(mem_ctx, user_sid),
318 ldb_errstring(state->remote_ldb));
319 return NT_STATUS_INTERNAL_DB_CORRUPTION;
320 } else if (ret == 0) {
321 *error_string = talloc_asprintf(mem_ctx, "User exists in samsync but not in remote LDAP domain! (base: %s, SID: %s)",
322 ldb_dn_get_linearized(state->base_dn[database]),
323 dom_sid_string(mem_ctx, user_sid));
324 return NT_STATUS_NO_SUCH_USER;
325 } else if (ret > 1) {
326 *error_string = talloc_asprintf(mem_ctx, "More than one user in remote LDAP domain with SID: %s",
327 dom_sid_string(mem_ctx, user_sid));
328 return NT_STATUS_INTERNAL_DB_CORRUPTION;
330 /* Try to put things in the same location as the remote server */
331 } else if (add) {
332 msg->dn = talloc_steal(msg, remote_msgs[0]->dn);
336 cn_name = talloc_strdup(mem_ctx, user->account_name.string);
337 NT_STATUS_HAVE_NO_MEMORY(cn_name);
338 cn_name_len = strlen(cn_name);
340 #define ADD_OR_DEL(type, attrib, field) do { \
341 if (user->field) { \
342 samdb_msg_add_ ## type(state->sam_ldb, mem_ctx, msg, \
343 attrib, user->field); \
344 } else if (!add) { \
345 samdb_msg_add_delete(state->sam_ldb, mem_ctx, msg, \
346 attrib); \
348 } while (0);
350 ADD_OR_DEL(string, "samAccountName", account_name.string);
351 ADD_OR_DEL(string, "displayName", full_name.string);
353 if (samdb_msg_add_dom_sid(state->sam_ldb, mem_ctx, msg,
354 "objectSid", dom_sid_add_rid(mem_ctx, state->dom_sid[database], rid))) {
355 return NT_STATUS_NO_MEMORY;
358 ADD_OR_DEL(uint, "primaryGroupID", primary_gid);
359 ADD_OR_DEL(string, "homeDirectory", home_directory.string);
360 ADD_OR_DEL(string, "homeDrive", home_drive.string);
361 ADD_OR_DEL(string, "scriptPath", logon_script.string);
362 ADD_OR_DEL(string, "description", description.string);
363 ADD_OR_DEL(string, "userWorkstations", workstations.string);
365 ADD_OR_DEL(uint64, "lastLogon", last_logon);
366 ADD_OR_DEL(uint64, "lastLogoff", last_logoff);
368 if (samdb_msg_add_logon_hours(state->sam_ldb, mem_ctx, msg, "logonHours", &user->logon_hours) != 0) {
369 return NT_STATUS_NO_MEMORY;
372 ADD_OR_DEL(uint, "badPwdCount", bad_password_count);
373 ADD_OR_DEL(uint, "logonCount", logon_count);
375 ADD_OR_DEL(uint64, "pwdLastSet", last_password_change);
376 ADD_OR_DEL(uint64, "accountExpires", acct_expiry);
378 if (samdb_msg_add_acct_flags(state->sam_ldb, mem_ctx, msg,
379 "userAccountControl", user->acct_flags) != 0) {
380 return NT_STATUS_NO_MEMORY;
383 if (user->lm_password_present) {
384 samdb_msg_add_hash(state->sam_ldb, mem_ctx, msg,
385 "dBCSPwd", &user->lmpassword);
386 } else if (!add) {
387 samdb_msg_add_delete(state->sam_ldb, mem_ctx, msg,
388 "dBCSPwd");
390 if (user->nt_password_present) {
391 samdb_msg_add_hash(state->sam_ldb, mem_ctx, msg,
392 "unicodePwd", &user->ntpassword);
393 } else if (!add) {
394 samdb_msg_add_delete(state->sam_ldb, mem_ctx, msg,
395 "unicodePwd");
398 ADD_OR_DEL(string, "comment", comment.string);
400 if (samdb_msg_add_parameters(state->sam_ldb, mem_ctx, msg, "userParameters", &user->parameters) != 0) {
401 return NT_STATUS_NO_MEMORY;
404 ADD_OR_DEL(uint, "countryCode", country_code);
405 ADD_OR_DEL(uint, "codePage", code_page);
407 ADD_OR_DEL(string, "profilePath", profile_path.string);
409 #undef ADD_OR_DEL
411 for (i=0; remote_attrs[i]; i++) {
412 struct ldb_message_element *el = ldb_msg_find_element(remote_msgs[0], remote_attrs[i]);
413 if (!el) {
414 samdb_msg_add_delete(state->sam_ldb, mem_ctx, msg,
415 remote_attrs[i]);
416 } else {
417 ret = ldb_msg_add(msg, el, LDB_FLAG_MOD_REPLACE);
418 if (ret != LDB_SUCCESS) {
419 *error_string = talloc_strdup(
420 mem_ctx, "ldb_msg_add failed");
421 return NT_STATUS_NO_MEMORY;
426 acb = user->acct_flags;
427 if (acb & (ACB_WSTRUST)) {
428 cn_name[cn_name_len - 1] = '\0';
429 container = "Computers";
430 obj_class = "computer";
432 } else if (acb & ACB_SVRTRUST) {
433 if (cn_name[cn_name_len - 1] != '$') {
434 return NT_STATUS_FOOBAR;
436 cn_name[cn_name_len - 1] = '\0';
437 container = "Domain Controllers";
438 obj_class = "computer";
439 } else {
440 container = "Users";
441 obj_class = "user";
443 if (add) {
444 ldb_msg_add_string(msg, "objectClass", obj_class);
445 if (!msg->dn) {
446 msg->dn = ldb_dn_copy(mem_ctx, state->base_dn[database]);
447 ldb_dn_add_child_fmt(msg->dn, "CN=%s,CN=%s", cn_name, container);
448 if (!msg->dn) {
449 return NT_STATUS_NO_MEMORY;
453 ret = ldb_add(state->sam_ldb, msg);
454 if (ret != LDB_SUCCESS) {
455 struct ldb_dn *first_try_dn = msg->dn;
456 /* Try again with the default DN */
457 if (!remote_msgs) {
458 *error_string = talloc_asprintf(mem_ctx, "Failed to create user record. Tried %s: %s",
459 ldb_dn_get_linearized(first_try_dn),
460 ldb_errstring(state->sam_ldb));
461 return NT_STATUS_INTERNAL_DB_CORRUPTION;
462 } else {
463 msg->dn = talloc_steal(msg, remote_msgs[0]->dn);
464 ret = ldb_add(state->sam_ldb, msg);
465 if (ret != LDB_SUCCESS) {
466 *error_string = talloc_asprintf(mem_ctx, "Failed to create user record. Tried both %s and %s: %s",
467 ldb_dn_get_linearized(first_try_dn),
468 ldb_dn_get_linearized(msg->dn),
469 ldb_errstring(state->sam_ldb));
470 return NT_STATUS_INTERNAL_DB_CORRUPTION;
474 } else {
475 ret = dsdb_replace(state->sam_ldb, msg, 0);
476 if (ret != LDB_SUCCESS) {
477 *error_string = talloc_asprintf(mem_ctx, "Failed to modify user record %s: %s",
478 ldb_dn_get_linearized(msg->dn),
479 ldb_errstring(state->sam_ldb));
480 return NT_STATUS_INTERNAL_DB_CORRUPTION;
484 return NT_STATUS_OK;
487 static NTSTATUS samsync_ldb_delete_user(TALLOC_CTX *mem_ctx,
488 struct samsync_ldb_state *state,
489 enum netr_SamDatabaseID database,
490 struct netr_DELTA_ENUM *delta,
491 char **error_string)
493 uint32_t rid = delta->delta_id_union.rid;
494 struct ldb_message **msgs;
495 int ret;
496 const char *attrs[] = { NULL };
498 /* search for the user, by rid */
499 ret = gendb_search(state->sam_ldb, mem_ctx, state->base_dn[database],
500 &msgs, attrs, "(&(objectClass=user)(objectSid=%s))",
501 ldap_encode_ndr_dom_sid(mem_ctx, dom_sid_add_rid(mem_ctx, state->dom_sid[database], rid)));
503 if (ret == -1) {
504 *error_string = talloc_asprintf(mem_ctx, "gendb_search failed: %s", ldb_errstring(state->sam_ldb));
505 return NT_STATUS_INTERNAL_DB_CORRUPTION;
506 } else if (ret == 0) {
507 return NT_STATUS_NO_SUCH_USER;
508 } else if (ret > 1) {
509 *error_string = talloc_asprintf(mem_ctx, "More than one user with SID: %s",
510 dom_sid_string(mem_ctx,
511 dom_sid_add_rid(mem_ctx,
512 state->dom_sid[database],
513 rid)));
514 return NT_STATUS_INTERNAL_DB_CORRUPTION;
517 ret = ldb_delete(state->sam_ldb, msgs[0]->dn);
518 if (ret != LDB_SUCCESS) {
519 *error_string = talloc_asprintf(mem_ctx, "Failed to delete user record %s: %s",
520 ldb_dn_get_linearized(msgs[0]->dn),
521 ldb_errstring(state->sam_ldb));
522 return NT_STATUS_INTERNAL_DB_CORRUPTION;
525 return NT_STATUS_OK;
528 static NTSTATUS samsync_ldb_handle_group(TALLOC_CTX *mem_ctx,
529 struct samsync_ldb_state *state,
530 enum netr_SamDatabaseID database,
531 struct netr_DELTA_ENUM *delta,
532 char **error_string)
534 uint32_t rid = delta->delta_id_union.rid;
535 struct netr_DELTA_GROUP *group = delta->delta_union.group;
536 const char *container, *obj_class;
537 const char *cn_name;
539 struct ldb_message *msg;
540 struct ldb_message **msgs;
541 int ret;
542 bool add = false;
543 const char *attrs[] = { NULL };
545 msg = ldb_msg_new(mem_ctx);
546 if (msg == NULL) {
547 return NT_STATUS_NO_MEMORY;
550 /* search for the group, by rid */
551 ret = gendb_search(state->sam_ldb, mem_ctx, state->base_dn[database], &msgs, attrs,
552 "(&(objectClass=group)(objectSid=%s))",
553 ldap_encode_ndr_dom_sid(mem_ctx, dom_sid_add_rid(mem_ctx, state->dom_sid[database], rid)));
555 if (ret == -1) {
556 *error_string = talloc_asprintf(mem_ctx, "gendb_search failed: %s", ldb_errstring(state->sam_ldb));
557 return NT_STATUS_INTERNAL_DB_CORRUPTION;
558 } else if (ret == 0) {
559 add = true;
560 } else if (ret > 1) {
561 *error_string = talloc_asprintf(mem_ctx, "More than one group/alias with SID: %s",
562 dom_sid_string(mem_ctx,
563 dom_sid_add_rid(mem_ctx,
564 state->dom_sid[database],
565 rid)));
566 return NT_STATUS_INTERNAL_DB_CORRUPTION;
567 } else {
568 msg->dn = talloc_steal(msg, msgs[0]->dn);
571 cn_name = group->group_name.string;
573 #define ADD_OR_DEL(type, attrib, field) do { \
574 if (group->field) { \
575 samdb_msg_add_ ## type(state->sam_ldb, mem_ctx, msg, \
576 attrib, group->field); \
577 } else if (!add) { \
578 samdb_msg_add_delete(state->sam_ldb, mem_ctx, msg, \
579 attrib); \
581 } while (0);
583 ADD_OR_DEL(string, "samAccountName", group_name.string);
585 if (samdb_msg_add_dom_sid(state->sam_ldb, mem_ctx, msg,
586 "objectSid", dom_sid_add_rid(mem_ctx, state->dom_sid[database], rid))) {
587 return NT_STATUS_NO_MEMORY;
590 ADD_OR_DEL(string, "description", description.string);
592 #undef ADD_OR_DEL
594 container = "Users";
595 obj_class = "group";
597 if (add) {
598 ldb_msg_add_string(msg, "objectClass", obj_class);
599 msg->dn = ldb_dn_copy(mem_ctx, state->base_dn[database]);
600 ldb_dn_add_child_fmt(msg->dn, "CN=%s,CN=%s", cn_name, container);
601 if (!msg->dn) {
602 return NT_STATUS_NO_MEMORY;
605 ret = ldb_add(state->sam_ldb, msg);
606 if (ret != LDB_SUCCESS) {
607 *error_string = talloc_asprintf(mem_ctx, "Failed to create group record %s: %s",
608 ldb_dn_get_linearized(msg->dn),
609 ldb_errstring(state->sam_ldb));
610 return NT_STATUS_INTERNAL_DB_CORRUPTION;
612 } else {
613 ret = dsdb_replace(state->sam_ldb, msg, 0);
614 if (ret != LDB_SUCCESS) {
615 *error_string = talloc_asprintf(mem_ctx, "Failed to modify group record %s: %s",
616 ldb_dn_get_linearized(msg->dn),
617 ldb_errstring(state->sam_ldb));
618 return NT_STATUS_INTERNAL_DB_CORRUPTION;
622 return NT_STATUS_OK;
625 static NTSTATUS samsync_ldb_delete_group(TALLOC_CTX *mem_ctx,
626 struct samsync_ldb_state *state,
627 enum netr_SamDatabaseID database,
628 struct netr_DELTA_ENUM *delta,
629 char **error_string)
631 uint32_t rid = delta->delta_id_union.rid;
632 struct ldb_message **msgs;
633 int ret;
634 const char *attrs[] = { NULL };
636 /* search for the group, by rid */
637 ret = gendb_search(state->sam_ldb, mem_ctx, state->base_dn[database], &msgs, attrs,
638 "(&(objectClass=group)(objectSid=%s))",
639 ldap_encode_ndr_dom_sid(mem_ctx, dom_sid_add_rid(mem_ctx, state->dom_sid[database], rid)));
641 if (ret == -1) {
642 *error_string = talloc_asprintf(mem_ctx, "gendb_search failed: %s", ldb_errstring(state->sam_ldb));
643 return NT_STATUS_INTERNAL_DB_CORRUPTION;
644 } else if (ret == 0) {
645 return NT_STATUS_NO_SUCH_GROUP;
646 } else if (ret > 1) {
647 *error_string = talloc_asprintf(mem_ctx, "More than one group/alias with SID: %s",
648 dom_sid_string(mem_ctx,
649 dom_sid_add_rid(mem_ctx,
650 state->dom_sid[database],
651 rid)));
652 return NT_STATUS_INTERNAL_DB_CORRUPTION;
655 ret = ldb_delete(state->sam_ldb, msgs[0]->dn);
656 if (ret != LDB_SUCCESS) {
657 *error_string = talloc_asprintf(mem_ctx, "Failed to delete group record %s: %s",
658 ldb_dn_get_linearized(msgs[0]->dn),
659 ldb_errstring(state->sam_ldb));
660 return NT_STATUS_INTERNAL_DB_CORRUPTION;
663 return NT_STATUS_OK;
666 static NTSTATUS samsync_ldb_handle_group_member(TALLOC_CTX *mem_ctx,
667 struct samsync_ldb_state *state,
668 enum netr_SamDatabaseID database,
669 struct netr_DELTA_ENUM *delta,
670 char **error_string)
672 uint32_t rid = delta->delta_id_union.rid;
673 struct netr_DELTA_GROUP_MEMBER *delta_group_member = delta->delta_union.group_member;
674 struct ldb_message *msg;
675 struct ldb_message **msgs;
676 int ret;
677 const char *attrs[] = { NULL };
678 const char *str_dn;
679 uint32_t i;
681 msg = ldb_msg_new(mem_ctx);
682 if (msg == NULL) {
683 return NT_STATUS_NO_MEMORY;
686 /* search for the group, by rid */
687 ret = gendb_search(state->sam_ldb, mem_ctx, state->base_dn[database], &msgs, attrs,
688 "(&(objectClass=group)(objectSid=%s))",
689 ldap_encode_ndr_dom_sid(mem_ctx, dom_sid_add_rid(mem_ctx, state->dom_sid[database], rid)));
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_GROUP;
696 } else if (ret > 1) {
697 *error_string = talloc_asprintf(mem_ctx, "More than one group/alias with SID: %s",
698 dom_sid_string(mem_ctx,
699 dom_sid_add_rid(mem_ctx,
700 state->dom_sid[database],
701 rid)));
702 return NT_STATUS_INTERNAL_DB_CORRUPTION;
703 } else {
704 msg->dn = talloc_steal(msg, msgs[0]->dn);
707 talloc_free(msgs);
709 for (i=0; i<delta_group_member->num_rids; i++) {
710 /* search for the group, by rid */
711 ret = gendb_search(state->sam_ldb, mem_ctx, state->base_dn[database], &msgs, attrs,
712 "(&(objectClass=user)(objectSid=%s))",
713 ldap_encode_ndr_dom_sid(mem_ctx, dom_sid_add_rid(mem_ctx, state->dom_sid[database], delta_group_member->rids[i])));
715 if (ret == -1) {
716 *error_string = talloc_asprintf(mem_ctx, "gendb_search failed: %s", ldb_errstring(state->sam_ldb));
717 return NT_STATUS_INTERNAL_DB_CORRUPTION;
718 } else if (ret == 0) {
719 return NT_STATUS_NO_SUCH_USER;
720 } else if (ret > 1) {
721 return NT_STATUS_INTERNAL_DB_CORRUPTION;
722 } else {
723 str_dn = ldb_dn_alloc_linearized(msg, msgs[0]->dn);
724 NT_STATUS_HAVE_NO_MEMORY(str_dn);
725 ret = ldb_msg_add_string(msg, "member", str_dn);
726 if (ret != LDB_SUCCESS) return NT_STATUS_NO_MEMORY;
729 talloc_free(msgs);
732 ret = dsdb_replace(state->sam_ldb, msg, 0);
733 if (ret != LDB_SUCCESS) {
734 *error_string = talloc_asprintf(mem_ctx, "Failed to modify group record %s: %s",
735 ldb_dn_get_linearized(msg->dn),
736 ldb_errstring(state->sam_ldb));
737 return NT_STATUS_INTERNAL_DB_CORRUPTION;
740 return NT_STATUS_OK;
743 static NTSTATUS samsync_ldb_handle_alias(TALLOC_CTX *mem_ctx,
744 struct samsync_ldb_state *state,
745 enum netr_SamDatabaseID database,
746 struct netr_DELTA_ENUM *delta,
747 char **error_string)
749 uint32_t rid = delta->delta_id_union.rid;
750 struct netr_DELTA_ALIAS *alias = delta->delta_union.alias;
751 const char *container, *obj_class;
752 const char *cn_name;
754 struct ldb_message *msg;
755 struct ldb_message **msgs;
756 int ret;
757 bool add = false;
758 const char *attrs[] = { NULL };
760 msg = ldb_msg_new(mem_ctx);
761 if (msg == NULL) {
762 return NT_STATUS_NO_MEMORY;
765 /* search for the alias, by rid */
766 ret = gendb_search(state->sam_ldb, mem_ctx, state->base_dn[database], &msgs, attrs,
767 "(&(objectClass=group)(objectSid=%s))",
768 ldap_encode_ndr_dom_sid(mem_ctx, dom_sid_add_rid(mem_ctx, state->dom_sid[database], rid)));
770 if (ret == -1) {
771 *error_string = talloc_asprintf(mem_ctx, "gendb_search failed: %s", ldb_errstring(state->sam_ldb));
772 return NT_STATUS_INTERNAL_DB_CORRUPTION;
773 } else if (ret == 0) {
774 add = true;
775 } else if (ret > 1) {
776 *error_string = talloc_asprintf(mem_ctx, "More than one group/alias with SID: %s",
777 dom_sid_string(mem_ctx,
778 dom_sid_add_rid(mem_ctx,
779 state->dom_sid[database],
780 rid)));
781 return NT_STATUS_INTERNAL_DB_CORRUPTION;
782 } else {
783 msg->dn = talloc_steal(mem_ctx, msgs[0]->dn);
786 cn_name = alias->alias_name.string;
788 #define ADD_OR_DEL(type, attrib, field) do { \
789 if (alias->field) { \
790 samdb_msg_add_ ## type(state->sam_ldb, mem_ctx, msg, \
791 attrib, alias->field); \
792 } else if (!add) { \
793 samdb_msg_add_delete(state->sam_ldb, mem_ctx, msg, \
794 attrib); \
796 } while (0);
798 ADD_OR_DEL(string, "samAccountName", alias_name.string);
800 if (samdb_msg_add_dom_sid(state->sam_ldb, mem_ctx, msg,
801 "objectSid", dom_sid_add_rid(mem_ctx, state->dom_sid[database], rid))) {
802 return NT_STATUS_NO_MEMORY;
805 ADD_OR_DEL(string, "description", description.string);
807 #undef ADD_OR_DEL
809 samdb_msg_add_uint(state->sam_ldb, mem_ctx, msg, "groupType", 0x80000004);
811 container = "Users";
812 obj_class = "group";
814 if (add) {
815 ldb_msg_add_string(msg, "objectClass", obj_class);
816 msg->dn = ldb_dn_copy(mem_ctx, state->base_dn[database]);
817 ldb_dn_add_child_fmt(msg->dn, "CN=%s,CN=%s", cn_name, container);
818 if (!msg->dn) {
819 return NT_STATUS_NO_MEMORY;
822 ret = ldb_add(state->sam_ldb, msg);
823 if (ret != LDB_SUCCESS) {
824 *error_string = talloc_asprintf(mem_ctx, "Failed to create alias record %s: %s",
825 ldb_dn_get_linearized(msg->dn),
826 ldb_errstring(state->sam_ldb));
827 return NT_STATUS_INTERNAL_DB_CORRUPTION;
829 } else {
830 ret = dsdb_replace(state->sam_ldb, msg, 0);
831 if (ret != LDB_SUCCESS) {
832 *error_string = talloc_asprintf(mem_ctx, "Failed to modify alias record %s: %s",
833 ldb_dn_get_linearized(msg->dn),
834 ldb_errstring(state->sam_ldb));
835 return NT_STATUS_INTERNAL_DB_CORRUPTION;
839 return NT_STATUS_OK;
842 static NTSTATUS samsync_ldb_delete_alias(TALLOC_CTX *mem_ctx,
843 struct samsync_ldb_state *state,
844 enum netr_SamDatabaseID database,
845 struct netr_DELTA_ENUM *delta,
846 char **error_string)
848 uint32_t rid = delta->delta_id_union.rid;
849 struct ldb_message **msgs;
850 int ret;
851 const char *attrs[] = { NULL };
853 /* search for the alias, by rid */
854 ret = gendb_search(state->sam_ldb, mem_ctx, state->base_dn[database], &msgs, attrs,
855 "(&(objectClass=group)(objectSid=%s))",
856 ldap_encode_ndr_dom_sid(mem_ctx, dom_sid_add_rid(mem_ctx, state->dom_sid[database], rid)));
858 if (ret == -1) {
859 *error_string = talloc_asprintf(mem_ctx, "gendb_search failed: %s", ldb_errstring(state->sam_ldb));
860 return NT_STATUS_INTERNAL_DB_CORRUPTION;
861 } else if (ret == 0) {
862 return NT_STATUS_NO_SUCH_ALIAS;
863 } else if (ret > 1) {
864 return NT_STATUS_INTERNAL_DB_CORRUPTION;
867 ret = ldb_delete(state->sam_ldb, msgs[0]->dn);
868 if (ret != LDB_SUCCESS) {
869 *error_string = talloc_asprintf(mem_ctx, "Failed to delete alias record %s: %s",
870 ldb_dn_get_linearized(msgs[0]->dn),
871 ldb_errstring(state->sam_ldb));
872 return NT_STATUS_INTERNAL_DB_CORRUPTION;
875 return NT_STATUS_OK;
878 static NTSTATUS samsync_ldb_handle_alias_member(TALLOC_CTX *mem_ctx,
879 struct samsync_ldb_state *state,
880 enum netr_SamDatabaseID database,
881 struct netr_DELTA_ENUM *delta,
882 char **error_string)
884 uint32_t rid = delta->delta_id_union.rid;
885 struct netr_DELTA_ALIAS_MEMBER *alias_member = delta->delta_union.alias_member;
886 struct ldb_message *msg;
887 struct ldb_message **msgs;
888 int ret;
889 const char *attrs[] = { NULL };
890 uint32_t i;
892 msg = ldb_msg_new(mem_ctx);
893 if (msg == NULL) {
894 return NT_STATUS_NO_MEMORY;
897 /* search for the alias, by rid */
898 ret = gendb_search(state->sam_ldb, mem_ctx, state->base_dn[database], &msgs, attrs,
899 "(&(objectClass=group)(objectSid=%s))",
900 ldap_encode_ndr_dom_sid(mem_ctx, dom_sid_add_rid(mem_ctx, state->dom_sid[database], rid)));
902 if (ret == -1) {
903 *error_string = talloc_asprintf(mem_ctx, "gendb_search failed: %s", ldb_errstring(state->sam_ldb));
904 return NT_STATUS_INTERNAL_DB_CORRUPTION;
905 } else if (ret == 0) {
906 return NT_STATUS_NO_SUCH_GROUP;
907 } else if (ret > 1) {
908 *error_string = talloc_asprintf(mem_ctx, "More than one group/alias with SID: %s",
909 dom_sid_string(mem_ctx,
910 dom_sid_add_rid(mem_ctx,
911 state->dom_sid[database],
912 rid)));
913 return NT_STATUS_INTERNAL_DB_CORRUPTION;
914 } else {
915 msg->dn = talloc_steal(msg, msgs[0]->dn);
918 talloc_free(msgs);
920 for (i=0; i<alias_member->sids.num_sids; i++) {
921 struct ldb_dn *alias_member_dn;
922 const char *str_dn;
923 /* search for members, in the top basedn (normal users are builtin aliases) */
924 ret = gendb_search(state->sam_ldb, mem_ctx, state->base_dn[SAM_DATABASE_DOMAIN], &msgs, attrs,
925 "(objectSid=%s)",
926 ldap_encode_ndr_dom_sid(mem_ctx, alias_member->sids.sids[i].sid));
928 if (ret == -1) {
929 *error_string = talloc_asprintf(mem_ctx, "gendb_search failed: %s", ldb_errstring(state->sam_ldb));
930 return NT_STATUS_INTERNAL_DB_CORRUPTION;
931 } else if (ret == 0) {
932 NTSTATUS nt_status;
933 nt_status = samsync_ldb_add_foreignSecurityPrincipal(mem_ctx, state,
934 alias_member->sids.sids[i].sid,
935 &alias_member_dn,
936 error_string);
937 if (!NT_STATUS_IS_OK(nt_status)) {
938 return nt_status;
940 } else if (ret > 1) {
941 return NT_STATUS_INTERNAL_DB_CORRUPTION;
942 } else {
943 alias_member_dn = msgs[0]->dn;
945 str_dn = ldb_dn_alloc_linearized(msg, alias_member_dn);
946 NT_STATUS_HAVE_NO_MEMORY(str_dn);
947 ret = ldb_msg_add_string(msg, "member", str_dn);
948 if (ret != LDB_SUCCESS) return NT_STATUS_NO_MEMORY;
950 talloc_free(msgs);
953 ret = dsdb_replace(state->sam_ldb, msg, 0);
954 if (ret != LDB_SUCCESS) {
955 *error_string = talloc_asprintf(mem_ctx, "Failed to modify group record %s: %s",
956 ldb_dn_get_linearized(msg->dn),
957 ldb_errstring(state->sam_ldb));
958 return NT_STATUS_INTERNAL_DB_CORRUPTION;
961 return NT_STATUS_OK;
964 static NTSTATUS samsync_ldb_handle_account(TALLOC_CTX *mem_ctx,
965 struct samsync_ldb_state *state,
966 enum netr_SamDatabaseID database,
967 struct netr_DELTA_ENUM *delta,
968 char **error_string)
970 struct dom_sid *sid = delta->delta_id_union.sid;
971 struct netr_DELTA_ACCOUNT *account = delta->delta_union.account;
973 struct ldb_message *msg;
974 int ret;
975 uint32_t i;
976 char *dnstr, *sidstr;
978 msg = ldb_msg_new(mem_ctx);
979 if (msg == NULL) {
980 return NT_STATUS_NO_MEMORY;
983 sidstr = dom_sid_string(msg, sid);
984 if (sidstr == NULL) {
985 TALLOC_FREE(msg);
986 return NT_STATUS_NO_MEMORY;
989 dnstr = talloc_asprintf(msg, "sid=%s", sidstr);
990 if (dnstr == NULL) {
991 TALLOC_FREE(msg);
992 return NT_STATUS_NO_MEMORY;
995 msg->dn = ldb_dn_new(msg, state->pdb, dnstr);
996 if (msg->dn == NULL) {
997 TALLOC_FREE(msg);
998 return NT_STATUS_NO_MEMORY;
1001 for (i=0; i< account->privilege_entries; i++) {
1002 ldb_msg_add_string(msg, "privilege", account->privilege_name[i].string);
1005 ret = dsdb_replace(state->pdb, msg, 0);
1006 if (ret == LDB_ERR_NO_SUCH_OBJECT) {
1007 if (samdb_msg_add_dom_sid(state->pdb, msg, msg, "objectSid", sid) != LDB_SUCCESS) {
1008 talloc_free(msg);
1009 return NT_STATUS_NO_MEMORY;
1011 ldb_msg_add_string(msg, "comment", "added via samsync");
1012 ret = ldb_add(state->pdb, msg);
1015 if (ret != LDB_SUCCESS) {
1016 *error_string = talloc_asprintf(mem_ctx, "Failed to modify privilege record %s",
1017 ldb_dn_get_linearized(msg->dn));
1018 return NT_STATUS_INTERNAL_DB_CORRUPTION;
1021 return NT_STATUS_OK;
1024 static NTSTATUS samsync_ldb_delete_account(TALLOC_CTX *mem_ctx,
1025 struct samsync_ldb_state *state,
1026 enum netr_SamDatabaseID database,
1027 struct netr_DELTA_ENUM *delta,
1028 char **error_string)
1030 struct dom_sid *sid = delta->delta_id_union.sid;
1032 struct ldb_message *msg;
1033 struct ldb_message **msgs;
1034 int ret;
1035 const char *attrs[] = { NULL };
1037 msg = ldb_msg_new(mem_ctx);
1038 if (msg == NULL) {
1039 return NT_STATUS_NO_MEMORY;
1042 /* search for the account, by sid, in the top basedn */
1043 ret = gendb_search(state->sam_ldb, mem_ctx, state->base_dn[SAM_DATABASE_DOMAIN], &msgs, attrs,
1044 "(objectSid=%s)",
1045 ldap_encode_ndr_dom_sid(mem_ctx, sid));
1047 if (ret == -1) {
1048 *error_string = talloc_asprintf(mem_ctx, "gendb_search failed: %s", ldb_errstring(state->sam_ldb));
1049 return NT_STATUS_INTERNAL_DB_CORRUPTION;
1050 } else if (ret == 0) {
1051 return NT_STATUS_NO_SUCH_USER;
1052 } else if (ret > 1) {
1053 *error_string = talloc_asprintf(mem_ctx, "More than one account with SID: %s",
1054 dom_sid_string(mem_ctx, sid));
1055 return NT_STATUS_INTERNAL_DB_CORRUPTION;
1056 } else {
1057 msg->dn = talloc_steal(msg, msgs[0]->dn);
1060 samdb_msg_add_delete(state->sam_ldb, mem_ctx, msg,
1061 "privilege");
1063 ret = dsdb_replace(state->sam_ldb, msg, 0);
1064 if (ret != LDB_SUCCESS) {
1065 *error_string = talloc_asprintf(mem_ctx, "Failed to modify privilege record %s",
1066 ldb_dn_get_linearized(msg->dn));
1067 return NT_STATUS_INTERNAL_DB_CORRUPTION;
1070 return NT_STATUS_OK;
1073 static NTSTATUS libnet_samsync_ldb_fn(TALLOC_CTX *mem_ctx,
1074 void *private_data,
1075 enum netr_SamDatabaseID database,
1076 struct netr_DELTA_ENUM *delta,
1077 char **error_string)
1079 NTSTATUS nt_status = NT_STATUS_OK;
1080 struct samsync_ldb_state *state = talloc_get_type(private_data, struct samsync_ldb_state);
1082 *error_string = NULL;
1083 switch (delta->delta_type) {
1084 case NETR_DELTA_DOMAIN:
1086 nt_status = samsync_ldb_handle_domain(mem_ctx,
1087 state,
1088 database,
1089 delta,
1090 error_string);
1091 break;
1093 case NETR_DELTA_USER:
1095 nt_status = samsync_ldb_handle_user(mem_ctx,
1096 state,
1097 database,
1098 delta,
1099 error_string);
1100 break;
1102 case NETR_DELTA_DELETE_USER:
1104 nt_status = samsync_ldb_delete_user(mem_ctx,
1105 state,
1106 database,
1107 delta,
1108 error_string);
1109 break;
1111 case NETR_DELTA_GROUP:
1113 nt_status = samsync_ldb_handle_group(mem_ctx,
1114 state,
1115 database,
1116 delta,
1117 error_string);
1118 break;
1120 case NETR_DELTA_DELETE_GROUP:
1122 nt_status = samsync_ldb_delete_group(mem_ctx,
1123 state,
1124 database,
1125 delta,
1126 error_string);
1127 break;
1129 case NETR_DELTA_GROUP_MEMBER:
1131 nt_status = samsync_ldb_handle_group_member(mem_ctx,
1132 state,
1133 database,
1134 delta,
1135 error_string);
1136 break;
1138 case NETR_DELTA_ALIAS:
1140 nt_status = samsync_ldb_handle_alias(mem_ctx,
1141 state,
1142 database,
1143 delta,
1144 error_string);
1145 break;
1147 case NETR_DELTA_DELETE_ALIAS:
1149 nt_status = samsync_ldb_delete_alias(mem_ctx,
1150 state,
1151 database,
1152 delta,
1153 error_string);
1154 break;
1156 case NETR_DELTA_ALIAS_MEMBER:
1158 nt_status = samsync_ldb_handle_alias_member(mem_ctx,
1159 state,
1160 database,
1161 delta,
1162 error_string);
1163 break;
1165 case NETR_DELTA_ACCOUNT:
1167 nt_status = samsync_ldb_handle_account(mem_ctx,
1168 state,
1169 database,
1170 delta,
1171 error_string);
1172 break;
1174 case NETR_DELTA_DELETE_ACCOUNT:
1176 nt_status = samsync_ldb_delete_account(mem_ctx,
1177 state,
1178 database,
1179 delta,
1180 error_string);
1181 break;
1183 default:
1184 /* Can't dump them all right now */
1185 break;
1187 if (!NT_STATUS_IS_OK(nt_status) && !*error_string) {
1188 *error_string = talloc_asprintf(mem_ctx, "Failed to handle samsync delta: %s", nt_errstr(nt_status));
1190 return nt_status;
1193 static NTSTATUS libnet_samsync_ldb_init(TALLOC_CTX *mem_ctx,
1194 void *private_data,
1195 struct libnet_SamSync_state *samsync_state,
1196 char **error_string)
1198 struct samsync_ldb_state *state = talloc_get_type(private_data, struct samsync_ldb_state);
1199 const char *server = dcerpc_server_name(samsync_state->netlogon_pipe);
1200 char *ldap_url;
1202 state->samsync_state = samsync_state;
1204 ZERO_STRUCT(state->dom_sid);
1205 if (state->samsync_state->domain_sid) {
1206 state->dom_sid[SAM_DATABASE_DOMAIN] = dom_sid_dup(state, state->samsync_state->domain_sid);
1209 state->dom_sid[SAM_DATABASE_BUILTIN] = dom_sid_parse_talloc(state, SID_BUILTIN);
1211 if (state->samsync_state->realm) {
1212 if (!server || !*server) {
1213 /* huh? how do we not have a server name? */
1214 *error_string = talloc_strdup(mem_ctx, "No DCE/RPC server name available. How did we connect?");
1215 return NT_STATUS_INVALID_PARAMETER;
1217 ldap_url = talloc_asprintf(state, "ldap://%s", server);
1219 state->remote_ldb = ldb_wrap_connect(mem_ctx,
1220 NULL,
1221 state->samsync_state->machine_net_ctx->lp_ctx,
1222 ldap_url,
1223 NULL, state->samsync_state->machine_net_ctx->cred,
1225 if (!state->remote_ldb) {
1226 *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);
1227 return NT_STATUS_NO_LOGON_SERVERS;
1229 } else {
1230 state->remote_ldb = NULL;
1232 return NT_STATUS_OK;
1235 NTSTATUS libnet_samsync_ldb(struct libnet_context *ctx, TALLOC_CTX *mem_ctx, struct libnet_samsync_ldb *r)
1237 NTSTATUS nt_status;
1238 struct libnet_SamSync r2;
1239 struct samsync_ldb_state *state = talloc(mem_ctx, struct samsync_ldb_state);
1241 if (!state) {
1242 return NT_STATUS_NO_MEMORY;
1245 state->secrets = NULL;
1246 state->trusted_domains = NULL;
1248 state->sam_ldb = samdb_connect(mem_ctx,
1249 ctx->event_ctx,
1250 ctx->lp_ctx,
1251 r->in.session_info,
1252 NULL,
1254 if (!state->sam_ldb) {
1255 return NT_STATUS_INTERNAL_DB_ERROR;
1258 state->pdb = privilege_connect(mem_ctx,
1259 ctx->lp_ctx);
1260 if (!state->pdb) {
1261 return NT_STATUS_INTERNAL_DB_ERROR;
1264 r2.out.error_string = NULL;
1265 r2.in.binding_string = r->in.binding_string;
1266 r2.in.init_fn = libnet_samsync_ldb_init;
1267 r2.in.delta_fn = libnet_samsync_ldb_fn;
1268 r2.in.fn_ctx = state;
1269 r2.in.machine_account = NULL; /* TODO: Create a machine account, fill this in, and the delete it */
1270 nt_status = libnet_SamSync_netlogon(ctx, state, &r2);
1271 r->out.error_string = r2.out.error_string;
1272 talloc_steal(mem_ctx, r->out.error_string);
1274 if (!NT_STATUS_IS_OK(nt_status)) {
1275 talloc_free(state);
1276 return nt_status;
1278 talloc_free(state);
1279 return nt_status;