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 2 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, write to the Free Software
22 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
27 #include "libnet/libnet.h"
28 #include "libcli/ldap/ldap.h"
29 #include "dsdb/samdb/samdb.h"
30 #include "auth/auth.h"
31 #include "librpc/gen_ndr/ndr_misc.h"
33 struct samsync_ldb_secret {
34 struct samsync_ldb_secret *prev, *next;
40 struct samsync_ldb_trusted_domain {
41 struct samsync_ldb_trusted_domain *prev, *next;
46 struct samsync_ldb_state {
47 /* Values from the LSA lookup */
48 const struct libnet_SamSync_state *samsync_state;
50 struct dom_sid *dom_sid[3];
51 struct ldb_context *sam_ldb, *remote_ldb;
52 struct ldb_dn *base_dn[3];
53 struct samsync_ldb_secret *secrets;
54 struct samsync_ldb_trusted_domain *trusted_domains;
57 static NTSTATUS samsync_ldb_add_foreignSecurityPrincipal(TALLOC_CTX *mem_ctx,
58 struct samsync_ldb_state *state,
60 struct ldb_dn **fsp_dn,
63 const char *sidstr = dom_sid_string(mem_ctx, sid);
64 /* We assume that ForeignSecurityPrincipals are under the BASEDN of the main domain */
65 struct ldb_dn *basedn = samdb_search_dn(state->sam_ldb, mem_ctx,
66 state->base_dn[SAM_DATABASE_DOMAIN],
67 "(&(objectClass=container)(cn=ForeignSecurityPrincipals))");
68 struct ldb_message *msg;
72 return NT_STATUS_NO_MEMORY;
76 *error_string = talloc_asprintf(mem_ctx,
77 "Failed to find DN for "
78 "ForeignSecurityPrincipal container under %s",
79 ldb_dn_linearize(mem_ctx, state->base_dn[SAM_DATABASE_DOMAIN]));
80 return NT_STATUS_INTERNAL_DB_CORRUPTION;
83 msg = ldb_msg_new(mem_ctx);
85 return NT_STATUS_NO_MEMORY;
88 /* add core elements to the ldb_message for the alias */
89 msg->dn = ldb_dn_build_child(mem_ctx, "CN", sidstr, basedn);
91 return NT_STATUS_NO_MEMORY;
93 samdb_msg_add_string(state->sam_ldb, mem_ctx, msg,
95 "foreignSecurityPrincipal");
99 /* create the alias */
100 ret = samdb_add(state->sam_ldb, mem_ctx, msg);
102 *error_string = talloc_asprintf(mem_ctx, "Failed to create foreignSecurityPrincipal "
104 ldb_dn_linearize(mem_ctx, msg->dn),
105 ldb_errstring(state->sam_ldb));
106 return NT_STATUS_INTERNAL_DB_CORRUPTION;
111 static NTSTATUS samsync_ldb_handle_domain(TALLOC_CTX *mem_ctx,
112 struct samsync_ldb_state *state,
113 enum netr_SamDatabaseID database,
114 struct netr_DELTA_ENUM *delta,
117 struct netr_DELTA_DOMAIN *domain = delta->delta_union.domain;
118 const char *domain_name = domain->domain_name.string;
119 struct ldb_message *msg;
122 msg = ldb_msg_new(mem_ctx);
124 return NT_STATUS_NO_MEMORY;
127 if (database == SAM_DATABASE_DOMAIN) {
128 const char *domain_attrs[] = {"nETBIOSName", "nCName", NULL};
129 struct ldb_message **msgs_domain;
132 ret_domain = gendb_search(state->sam_ldb, mem_ctx, NULL, &msgs_domain, domain_attrs,
133 "(&(&(nETBIOSName=%s)(objectclass=crossRef))(ncName=*))",
135 if (ret_domain == -1) {
136 *error_string = talloc_asprintf(mem_ctx, "gendb_search for domain failed: %s", ldb_errstring(state->sam_ldb));
137 return NT_STATUS_INTERNAL_DB_CORRUPTION;
140 if (ret_domain != 1) {
141 *error_string = talloc_asprintf(mem_ctx, "Failed to find existing domain record for %s: %d results", domain_name,
143 return NT_STATUS_NO_SUCH_DOMAIN;
146 state->base_dn[database] = samdb_result_dn(state, msgs_domain[0], "nCName", NULL);
148 if (state->dom_sid[database]) {
149 /* Update the domain sid with the incoming
150 * domain (found on LSA pipe, database sid may
152 samdb_msg_add_dom_sid(state->sam_ldb, mem_ctx,
153 msg, "objectSid", state->dom_sid[database]);
155 /* Well, we will have to use the one from the database */
156 state->dom_sid[database] = samdb_search_dom_sid(state->sam_ldb, state,
157 state->base_dn[database],
161 if (state->samsync_state->domain_guid) {
164 nt_status = ndr_push_struct_blob(&v, msg, state->samsync_state->domain_guid,
165 (ndr_push_flags_fn_t)ndr_push_GUID);
166 if (!NT_STATUS_IS_OK(nt_status)) {
167 *error_string = talloc_asprintf(mem_ctx, "ndr_push of domain GUID failed!");
171 ldb_msg_add_value(msg, "objectGUID", &v);
173 } else if (database == SAM_DATABASE_BUILTIN) {
174 /* work out the builtin_dn - useful for so many calls its worth
176 const char *dnstring = samdb_search_string(state->sam_ldb, mem_ctx, NULL,
177 "distinguishedName", "objectClass=builtinDomain");
178 state->base_dn[database] = ldb_dn_explode(state, dnstring);
181 return NT_STATUS_INVALID_PARAMETER;
184 msg->dn = talloc_reference(mem_ctx, state->base_dn[database]);
186 return NT_STATUS_NO_MEMORY;
189 samdb_msg_add_string(state->sam_ldb, mem_ctx,
190 msg, "oEMInformation", domain->comment.string);
192 samdb_msg_add_int64(state->sam_ldb, mem_ctx,
193 msg, "forceLogoff", domain->force_logoff_time);
195 samdb_msg_add_uint(state->sam_ldb, mem_ctx,
196 msg, "minPwdLen", domain->min_password_length);
198 samdb_msg_add_int64(state->sam_ldb, mem_ctx,
199 msg, "maxPwdAge", domain->max_password_age);
201 samdb_msg_add_int64(state->sam_ldb, mem_ctx,
202 msg, "minPwdAge", domain->min_password_age);
204 samdb_msg_add_uint(state->sam_ldb, mem_ctx,
205 msg, "pwdHistoryLength", domain->password_history_length);
207 samdb_msg_add_uint64(state->sam_ldb, mem_ctx,
208 msg, "modifiedCount",
209 domain->sequence_num);
211 samdb_msg_add_uint64(state->sam_ldb, mem_ctx,
212 msg, "creationTime", domain->domain_create_time);
214 /* TODO: Account lockout, password properties */
216 ret = samdb_replace(state->sam_ldb, mem_ctx, msg);
219 return NT_STATUS_INTERNAL_ERROR;
224 static NTSTATUS samsync_ldb_handle_user(TALLOC_CTX *mem_ctx,
225 struct samsync_ldb_state *state,
226 enum netr_SamDatabaseID database,
227 struct netr_DELTA_ENUM *delta,
230 uint32_t rid = delta->delta_id_union.rid;
231 struct netr_DELTA_USER *user = delta->delta_union.user;
232 const char *container, *obj_class;
235 const struct dom_sid *user_sid;
236 struct ldb_message *msg;
237 struct ldb_message **msgs;
238 struct ldb_message **remote_msgs;
242 const char *attrs[] = { NULL };
243 /* we may change this to a global search, then fill in only the things not in ldap later */
244 const char *remote_attrs[] = { "userPrincipalName", "servicePrincipalName",
245 "msDS-KeyVersionNumber", "objectGUID", NULL};
247 user_sid = dom_sid_add_rid(mem_ctx, state->dom_sid[database], rid);
249 return NT_STATUS_NO_MEMORY;
252 msg = ldb_msg_new(mem_ctx);
254 return NT_STATUS_NO_MEMORY;
258 /* search for the user, by rid */
259 ret = gendb_search(state->sam_ldb, mem_ctx, state->base_dn[database],
260 &msgs, attrs, "(&(objectClass=user)(objectSid=%s))",
261 ldap_encode_ndr_dom_sid(mem_ctx, user_sid));
264 *error_string = talloc_asprintf(mem_ctx, "LDB for user %s failed: %s",
265 dom_sid_string(mem_ctx, user_sid),
266 ldb_errstring(state->sam_ldb));
267 return NT_STATUS_INTERNAL_DB_CORRUPTION;
268 } else if (ret == 0) {
270 } else if (ret > 1) {
271 *error_string = talloc_asprintf(mem_ctx, "More than one user with SID: %s in local LDB",
272 dom_sid_string(mem_ctx, user_sid));
273 return NT_STATUS_INTERNAL_DB_CORRUPTION;
275 msg->dn = msgs[0]->dn;
276 talloc_steal(msg, msgs[0]->dn);
279 /* and do the same on the remote database */
280 if (state->remote_ldb) {
281 ret = gendb_search(state->remote_ldb, mem_ctx, state->base_dn[database],
282 &remote_msgs, remote_attrs, "(&(objectClass=user)(objectSid=%s))",
283 ldap_encode_ndr_dom_sid(mem_ctx, user_sid));
286 *error_string = talloc_asprintf(mem_ctx, "remote LDAP for user %s failed: %s",
287 dom_sid_string(mem_ctx, user_sid),
288 ldb_errstring(state->remote_ldb));
289 return NT_STATUS_INTERNAL_DB_CORRUPTION;
290 } else if (ret == 0) {
291 *error_string = talloc_asprintf(mem_ctx, "User exists in samsync but not in remote LDAP domain! (base: %s, SID: %s)",
292 ldb_dn_linearize(mem_ctx, state->base_dn[database]),
293 dom_sid_string(mem_ctx, user_sid));
294 return NT_STATUS_NO_SUCH_USER;
295 } else if (ret > 1) {
296 *error_string = talloc_asprintf(mem_ctx, "More than one user in remote LDAP domain with SID: %s",
297 dom_sid_string(mem_ctx, user_sid));
298 return NT_STATUS_INTERNAL_DB_CORRUPTION;
300 /* Try to put things in the same location as the remote server */
302 msg->dn = remote_msgs[0]->dn;
303 talloc_steal(msg, remote_msgs[0]->dn);
307 cn_name = talloc_strdup(mem_ctx, user->account_name.string);
308 NT_STATUS_HAVE_NO_MEMORY(cn_name);
309 cn_name_len = strlen(cn_name);
311 #define ADD_OR_DEL(type, attrib, field) do { \
313 samdb_msg_add_ ## type(state->sam_ldb, mem_ctx, msg, \
314 attrib, user->field); \
316 samdb_msg_add_delete(state->sam_ldb, mem_ctx, msg, \
321 ADD_OR_DEL(string, "samAccountName", account_name.string);
322 ADD_OR_DEL(string, "displayName", full_name.string);
324 if (samdb_msg_add_dom_sid(state->sam_ldb, mem_ctx, msg,
325 "objectSid", dom_sid_add_rid(mem_ctx, state->dom_sid[database], rid))) {
326 return NT_STATUS_NO_MEMORY;
329 ADD_OR_DEL(uint, "primaryGroupID", primary_gid);
330 ADD_OR_DEL(string, "homeDirectory", home_directory.string);
331 ADD_OR_DEL(string, "homeDrive", home_drive.string);
332 ADD_OR_DEL(string, "scriptPath", logon_script.string);
333 ADD_OR_DEL(string, "description", description.string);
334 ADD_OR_DEL(string, "userWorkstations", workstations.string);
336 ADD_OR_DEL(uint64, "lastLogon", last_logon);
337 ADD_OR_DEL(uint64, "lastLogoff", last_logoff);
339 if (samdb_msg_add_logon_hours(state->sam_ldb, mem_ctx, msg, "logonHours", &user->logon_hours) != 0) {
340 return NT_STATUS_NO_MEMORY;
343 ADD_OR_DEL(uint, "badPwdCount", bad_password_count);
344 ADD_OR_DEL(uint, "logonCount", logon_count);
346 ADD_OR_DEL(uint64, "pwdLastSet", last_password_change);
347 ADD_OR_DEL(uint64, "accountExpires", acct_expiry);
349 if (samdb_msg_add_acct_flags(state->sam_ldb, mem_ctx, msg,
350 "userAccountControl", user->acct_flags) != 0) {
351 return NT_STATUS_NO_MEMORY;
354 /* Passwords. Ensure there is no plaintext stored against
355 * this entry, as we only have hashes */
356 samdb_msg_add_delete(state->sam_ldb, mem_ctx, msg,
358 if (user->lm_password_present) {
359 samdb_msg_add_hash(state->sam_ldb, mem_ctx, msg,
360 "lmPwdHash", &user->lmpassword);
362 samdb_msg_add_delete(state->sam_ldb, mem_ctx, msg,
365 if (user->nt_password_present) {
366 samdb_msg_add_hash(state->sam_ldb, mem_ctx, msg,
367 "ntPwdHash", &user->ntpassword);
369 samdb_msg_add_delete(state->sam_ldb, mem_ctx, msg,
373 ADD_OR_DEL(string, "comment", comment.string);
374 ADD_OR_DEL(string, "userParameters", parameters.string);
375 ADD_OR_DEL(uint, "countryCode", country_code);
376 ADD_OR_DEL(uint, "codePage", code_page);
378 ADD_OR_DEL(string, "profilePath", profile_path.string);
382 for (i=0; remote_attrs[i]; i++) {
383 struct ldb_message_element *el = ldb_msg_find_element(remote_msgs[0], remote_attrs[i]);
385 samdb_msg_add_delete(state->sam_ldb, mem_ctx, msg,
388 ldb_msg_add(msg, el, LDB_FLAG_MOD_REPLACE);
392 acb = user->acct_flags;
393 if (acb & (ACB_WSTRUST)) {
394 cn_name[cn_name_len - 1] = '\0';
395 container = "Computers";
396 obj_class = "computer";
398 } else if (acb & ACB_SVRTRUST) {
399 if (cn_name[cn_name_len - 1] != '$') {
400 return NT_STATUS_FOOBAR;
402 cn_name[cn_name_len - 1] = '\0';
403 container = "Domain Controllers";
404 obj_class = "computer";
410 samdb_msg_add_string(state->sam_ldb, mem_ctx, msg,
411 "objectClass", obj_class);
413 msg->dn = ldb_dn_string_compose(mem_ctx, state->base_dn[database],
414 "CN=%s, CN=%s", cn_name, container);
416 return NT_STATUS_NO_MEMORY;
420 ret = samdb_add(state->sam_ldb, mem_ctx, msg);
422 struct ldb_dn *first_try_dn = msg->dn;
423 /* Try again with the default DN */
424 msg->dn = talloc_steal(msg, msgs[0]->dn);
425 ret = samdb_add(state->sam_ldb, mem_ctx, msg);
427 *error_string = talloc_asprintf(mem_ctx, "Failed to create user record. Tried both %s and %s: %s",
428 ldb_dn_linearize(mem_ctx, first_try_dn),
429 ldb_dn_linearize(mem_ctx, msg->dn),
430 ldb_errstring(state->sam_ldb));
431 return NT_STATUS_INTERNAL_DB_CORRUPTION;
435 ret = samdb_replace(state->sam_ldb, mem_ctx, msg);
437 *error_string = talloc_asprintf(mem_ctx, "Failed to modify user record %s: %s",
438 ldb_dn_linearize(mem_ctx, msg->dn),
439 ldb_errstring(state->sam_ldb));
440 return NT_STATUS_INTERNAL_DB_CORRUPTION;
447 static NTSTATUS samsync_ldb_delete_user(TALLOC_CTX *mem_ctx,
448 struct samsync_ldb_state *state,
449 enum netr_SamDatabaseID database,
450 struct netr_DELTA_ENUM *delta,
453 uint32_t rid = delta->delta_id_union.rid;
454 struct ldb_message **msgs;
456 const char *attrs[] = { NULL };
458 /* search for the user, by rid */
459 ret = gendb_search(state->sam_ldb, mem_ctx, state->base_dn[database],
460 &msgs, attrs, "(&(objectClass=user)(objectSid=%s))",
461 ldap_encode_ndr_dom_sid(mem_ctx, dom_sid_add_rid(mem_ctx, state->dom_sid[database], rid)));
464 *error_string = talloc_asprintf(mem_ctx, "gendb_search failed: %s", ldb_errstring(state->sam_ldb));
465 return NT_STATUS_INTERNAL_DB_CORRUPTION;
466 } else if (ret == 0) {
467 return NT_STATUS_NO_SUCH_USER;
468 } else if (ret > 1) {
469 *error_string = talloc_asprintf(mem_ctx, "More than one user with SID: %s",
470 dom_sid_string(mem_ctx,
471 dom_sid_add_rid(mem_ctx,
472 state->dom_sid[database],
474 return NT_STATUS_INTERNAL_DB_CORRUPTION;
477 ret = samdb_delete(state->sam_ldb, mem_ctx, msgs[0]->dn);
479 *error_string = talloc_asprintf(mem_ctx, "Failed to delete user record %s: %s",
480 ldb_dn_linearize(mem_ctx, msgs[0]->dn),
481 ldb_errstring(state->sam_ldb));
482 return NT_STATUS_INTERNAL_DB_CORRUPTION;
488 static NTSTATUS samsync_ldb_handle_group(TALLOC_CTX *mem_ctx,
489 struct samsync_ldb_state *state,
490 enum netr_SamDatabaseID database,
491 struct netr_DELTA_ENUM *delta,
494 uint32_t rid = delta->delta_id_union.rid;
495 struct netr_DELTA_GROUP *group = delta->delta_union.group;
496 const char *container, *obj_class;
499 struct ldb_message *msg;
500 struct ldb_message **msgs;
503 const char *attrs[] = { NULL };
505 msg = ldb_msg_new(mem_ctx);
507 return NT_STATUS_NO_MEMORY;
510 /* search for the group, by rid */
511 ret = gendb_search(state->sam_ldb, mem_ctx, state->base_dn[database], &msgs, attrs,
512 "(&(objectClass=group)(objectSid=%s))",
513 ldap_encode_ndr_dom_sid(mem_ctx, dom_sid_add_rid(mem_ctx, state->dom_sid[database], rid)));
516 *error_string = talloc_asprintf(mem_ctx, "gendb_search failed: %s", ldb_errstring(state->sam_ldb));
517 return NT_STATUS_INTERNAL_DB_CORRUPTION;
518 } else if (ret == 0) {
520 } else if (ret > 1) {
521 *error_string = talloc_asprintf(mem_ctx, "More than one group/alias with SID: %s",
522 dom_sid_string(mem_ctx,
523 dom_sid_add_rid(mem_ctx,
524 state->dom_sid[database],
526 return NT_STATUS_INTERNAL_DB_CORRUPTION;
528 msg->dn = talloc_steal(msg, msgs[0]->dn);
531 cn_name = group->group_name.string;
533 #define ADD_OR_DEL(type, attrib, field) do { \
534 if (group->field) { \
535 samdb_msg_add_ ## type(state->sam_ldb, mem_ctx, msg, \
536 attrib, group->field); \
538 samdb_msg_add_delete(state->sam_ldb, mem_ctx, msg, \
543 ADD_OR_DEL(string, "samAccountName", group_name.string);
545 if (samdb_msg_add_dom_sid(state->sam_ldb, mem_ctx, msg,
546 "objectSid", dom_sid_add_rid(mem_ctx, state->dom_sid[database], rid))) {
547 return NT_STATUS_NO_MEMORY;
550 ADD_OR_DEL(string, "description", description.string);
558 samdb_msg_add_string(state->sam_ldb, mem_ctx, msg,
559 "objectClass", obj_class);
560 msg->dn = ldb_dn_string_compose(mem_ctx, state->base_dn[database],
561 "CN=%s, CN=%s", cn_name, container);
563 return NT_STATUS_NO_MEMORY;
566 ret = samdb_add(state->sam_ldb, mem_ctx, msg);
568 *error_string = talloc_asprintf(mem_ctx, "Failed to create group record %s: %s",
569 ldb_dn_linearize(mem_ctx, msg->dn),
570 ldb_errstring(state->sam_ldb));
571 return NT_STATUS_INTERNAL_DB_CORRUPTION;
574 ret = samdb_replace(state->sam_ldb, mem_ctx, msg);
576 *error_string = talloc_asprintf(mem_ctx, "Failed to modify group record %s: %s",
577 ldb_dn_linearize(mem_ctx, msg->dn),
578 ldb_errstring(state->sam_ldb));
579 return NT_STATUS_INTERNAL_DB_CORRUPTION;
586 static NTSTATUS samsync_ldb_delete_group(TALLOC_CTX *mem_ctx,
587 struct samsync_ldb_state *state,
588 enum netr_SamDatabaseID database,
589 struct netr_DELTA_ENUM *delta,
592 uint32_t rid = delta->delta_id_union.rid;
593 struct ldb_message **msgs;
595 const char *attrs[] = { NULL };
597 /* search for the group, by rid */
598 ret = gendb_search(state->sam_ldb, mem_ctx, state->base_dn[database], &msgs, attrs,
599 "(&(objectClass=group)(objectSid=%s))",
600 ldap_encode_ndr_dom_sid(mem_ctx, dom_sid_add_rid(mem_ctx, state->dom_sid[database], rid)));
603 *error_string = talloc_asprintf(mem_ctx, "gendb_search failed: %s", ldb_errstring(state->sam_ldb));
604 return NT_STATUS_INTERNAL_DB_CORRUPTION;
605 } else if (ret == 0) {
606 return NT_STATUS_NO_SUCH_GROUP;
607 } else if (ret > 1) {
608 *error_string = talloc_asprintf(mem_ctx, "More than one group/alias with SID: %s",
609 dom_sid_string(mem_ctx,
610 dom_sid_add_rid(mem_ctx,
611 state->dom_sid[database],
613 return NT_STATUS_INTERNAL_DB_CORRUPTION;
616 ret = samdb_delete(state->sam_ldb, mem_ctx, msgs[0]->dn);
618 *error_string = talloc_asprintf(mem_ctx, "Failed to delete group record %s: %s",
619 ldb_dn_linearize(mem_ctx, msgs[0]->dn),
620 ldb_errstring(state->sam_ldb));
621 return NT_STATUS_INTERNAL_DB_CORRUPTION;
627 static NTSTATUS samsync_ldb_handle_group_member(TALLOC_CTX *mem_ctx,
628 struct samsync_ldb_state *state,
629 enum netr_SamDatabaseID database,
630 struct netr_DELTA_ENUM *delta,
633 uint32_t rid = delta->delta_id_union.rid;
634 struct netr_DELTA_GROUP_MEMBER *group_member = delta->delta_union.group_member;
635 struct ldb_message *msg;
636 struct ldb_message **msgs;
638 const char *attrs[] = { NULL };
641 msg = ldb_msg_new(mem_ctx);
643 return NT_STATUS_NO_MEMORY;
646 /* search for the group, by rid */
647 ret = gendb_search(state->sam_ldb, mem_ctx, state->base_dn[database], &msgs, attrs,
648 "(&(objectClass=group)(objectSid=%s))",
649 ldap_encode_ndr_dom_sid(mem_ctx, dom_sid_add_rid(mem_ctx, state->dom_sid[database], rid)));
652 *error_string = talloc_asprintf(mem_ctx, "gendb_search failed: %s", ldb_errstring(state->sam_ldb));
653 return NT_STATUS_INTERNAL_DB_CORRUPTION;
654 } else if (ret == 0) {
655 return NT_STATUS_NO_SUCH_GROUP;
656 } else if (ret > 1) {
657 *error_string = talloc_asprintf(mem_ctx, "More than one group/alias with SID: %s",
658 dom_sid_string(mem_ctx,
659 dom_sid_add_rid(mem_ctx,
660 state->dom_sid[database],
662 return NT_STATUS_INTERNAL_DB_CORRUPTION;
664 msg->dn = talloc_steal(msg, msgs[0]->dn);
669 for (i=0; i<group_member->num_rids; i++) {
670 /* search for the group, by rid */
671 ret = gendb_search(state->sam_ldb, mem_ctx, state->base_dn[database], &msgs, attrs,
672 "(&(objectClass=user)(objectSid=%s))",
673 ldap_encode_ndr_dom_sid(mem_ctx, dom_sid_add_rid(mem_ctx, state->dom_sid[database], group_member->rids[i])));
676 *error_string = talloc_asprintf(mem_ctx, "gendb_search failed: %s", ldb_errstring(state->sam_ldb));
677 return NT_STATUS_INTERNAL_DB_CORRUPTION;
678 } else if (ret == 0) {
679 return NT_STATUS_NO_SUCH_USER;
680 } else if (ret > 1) {
681 return NT_STATUS_INTERNAL_DB_CORRUPTION;
683 samdb_msg_add_string(state->sam_ldb, mem_ctx, msg, "member", ldb_dn_linearize(mem_ctx, msgs[0]->dn));
689 ret = samdb_replace(state->sam_ldb, mem_ctx, msg);
691 *error_string = talloc_asprintf(mem_ctx, "Failed to modify group record %s: %s",
692 ldb_dn_linearize(mem_ctx, msg->dn),
693 ldb_errstring(state->sam_ldb));
694 return NT_STATUS_INTERNAL_DB_CORRUPTION;
700 static NTSTATUS samsync_ldb_handle_alias(TALLOC_CTX *mem_ctx,
701 struct samsync_ldb_state *state,
702 enum netr_SamDatabaseID database,
703 struct netr_DELTA_ENUM *delta,
706 uint32_t rid = delta->delta_id_union.rid;
707 struct netr_DELTA_ALIAS *alias = delta->delta_union.alias;
708 const char *container, *obj_class;
711 struct ldb_message *msg;
712 struct ldb_message **msgs;
715 const char *attrs[] = { NULL };
717 msg = ldb_msg_new(mem_ctx);
719 return NT_STATUS_NO_MEMORY;
722 /* search for the alias, by rid */
723 ret = gendb_search(state->sam_ldb, mem_ctx, state->base_dn[database], &msgs, attrs,
724 "(&(objectClass=group)(objectSid=%s))",
725 ldap_encode_ndr_dom_sid(mem_ctx, dom_sid_add_rid(mem_ctx, state->dom_sid[database], rid)));
728 *error_string = talloc_asprintf(mem_ctx, "gendb_search failed: %s", ldb_errstring(state->sam_ldb));
729 return NT_STATUS_INTERNAL_DB_CORRUPTION;
730 } else if (ret == 0) {
732 } else if (ret > 1) {
733 *error_string = talloc_asprintf(mem_ctx, "More than one group/alias with SID: %s",
734 dom_sid_string(mem_ctx,
735 dom_sid_add_rid(mem_ctx,
736 state->dom_sid[database],
738 return NT_STATUS_INTERNAL_DB_CORRUPTION;
740 msg->dn = talloc_steal(mem_ctx, msgs[0]->dn);
743 cn_name = alias->alias_name.string;
745 #define ADD_OR_DEL(type, attrib, field) do { \
746 if (alias->field) { \
747 samdb_msg_add_ ## type(state->sam_ldb, mem_ctx, msg, \
748 attrib, alias->field); \
750 samdb_msg_add_delete(state->sam_ldb, mem_ctx, msg, \
755 ADD_OR_DEL(string, "samAccountName", alias_name.string);
757 if (samdb_msg_add_dom_sid(state->sam_ldb, mem_ctx, msg,
758 "objectSid", dom_sid_add_rid(mem_ctx, state->dom_sid[database], rid))) {
759 return NT_STATUS_NO_MEMORY;
762 ADD_OR_DEL(string, "description", description.string);
766 samdb_msg_add_uint(state->sam_ldb, mem_ctx, msg, "groupType", 0x80000004);
772 samdb_msg_add_string(state->sam_ldb, mem_ctx, msg,
773 "objectClass", obj_class);
774 msg->dn = ldb_dn_string_compose(mem_ctx, state->base_dn[database],
775 "CN=%s, CN=%s", cn_name, container);
777 return NT_STATUS_NO_MEMORY;
780 ret = samdb_add(state->sam_ldb, mem_ctx, msg);
782 *error_string = talloc_asprintf(mem_ctx, "Failed to create alias record %s: %s",
783 ldb_dn_linearize(mem_ctx, msg->dn),
784 ldb_errstring(state->sam_ldb));
785 return NT_STATUS_INTERNAL_DB_CORRUPTION;
788 ret = samdb_replace(state->sam_ldb, mem_ctx, msg);
790 *error_string = talloc_asprintf(mem_ctx, "Failed to modify alias record %s: %s",
791 ldb_dn_linearize(mem_ctx, msg->dn),
792 ldb_errstring(state->sam_ldb));
793 return NT_STATUS_INTERNAL_DB_CORRUPTION;
800 static NTSTATUS samsync_ldb_delete_alias(TALLOC_CTX *mem_ctx,
801 struct samsync_ldb_state *state,
802 enum netr_SamDatabaseID database,
803 struct netr_DELTA_ENUM *delta,
806 uint32_t rid = delta->delta_id_union.rid;
807 struct ldb_message **msgs;
809 const char *attrs[] = { NULL };
811 /* search for the alias, by rid */
812 ret = gendb_search(state->sam_ldb, mem_ctx, state->base_dn[database], &msgs, attrs,
813 "(&(objectClass=group)(objectSid=%s))",
814 ldap_encode_ndr_dom_sid(mem_ctx, dom_sid_add_rid(mem_ctx, state->dom_sid[database], rid)));
817 *error_string = talloc_asprintf(mem_ctx, "gendb_search failed: %s", ldb_errstring(state->sam_ldb));
818 return NT_STATUS_INTERNAL_DB_CORRUPTION;
819 } else if (ret == 0) {
820 return NT_STATUS_NO_SUCH_ALIAS;
821 } else if (ret > 1) {
822 return NT_STATUS_INTERNAL_DB_CORRUPTION;
825 ret = samdb_delete(state->sam_ldb, mem_ctx, msgs[0]->dn);
827 *error_string = talloc_asprintf(mem_ctx, "Failed to delete alias record %s: %s",
828 ldb_dn_linearize(mem_ctx, msgs[0]->dn),
829 ldb_errstring(state->sam_ldb));
830 return NT_STATUS_INTERNAL_DB_CORRUPTION;
836 static NTSTATUS samsync_ldb_handle_alias_member(TALLOC_CTX *mem_ctx,
837 struct samsync_ldb_state *state,
838 enum netr_SamDatabaseID database,
839 struct netr_DELTA_ENUM *delta,
842 uint32_t rid = delta->delta_id_union.rid;
843 struct netr_DELTA_ALIAS_MEMBER *alias_member = delta->delta_union.alias_member;
844 struct ldb_message *msg;
845 struct ldb_message **msgs;
847 const char *attrs[] = { NULL };
850 msg = ldb_msg_new(mem_ctx);
852 return NT_STATUS_NO_MEMORY;
855 /* search for the alias, by rid */
856 ret = gendb_search(state->sam_ldb, mem_ctx, state->base_dn[database], &msgs, attrs,
857 "(&(objectClass=group)(objectSid=%s))",
858 ldap_encode_ndr_dom_sid(mem_ctx, dom_sid_add_rid(mem_ctx, state->dom_sid[database], rid)));
861 *error_string = talloc_asprintf(mem_ctx, "gendb_search failed: %s", ldb_errstring(state->sam_ldb));
862 return NT_STATUS_INTERNAL_DB_CORRUPTION;
863 } else if (ret == 0) {
864 return NT_STATUS_NO_SUCH_GROUP;
865 } else if (ret > 1) {
866 *error_string = talloc_asprintf(mem_ctx, "More than one group/alias with SID: %s",
867 dom_sid_string(mem_ctx,
868 dom_sid_add_rid(mem_ctx,
869 state->dom_sid[database],
871 return NT_STATUS_INTERNAL_DB_CORRUPTION;
873 msg->dn = talloc_steal(msg, msgs[0]->dn);
878 for (i=0; i<alias_member->sids.num_sids; i++) {
879 struct ldb_dn *alias_member_dn;
880 /* search for members, in the top basedn (normal users are builtin aliases) */
881 ret = gendb_search(state->sam_ldb, mem_ctx, state->base_dn[SAM_DATABASE_DOMAIN], &msgs, attrs,
883 ldap_encode_ndr_dom_sid(mem_ctx, alias_member->sids.sids[i].sid));
886 *error_string = talloc_asprintf(mem_ctx, "gendb_search failed: %s", ldb_errstring(state->sam_ldb));
887 return NT_STATUS_INTERNAL_DB_CORRUPTION;
888 } else if (ret == 0) {
890 nt_status = samsync_ldb_add_foreignSecurityPrincipal(mem_ctx, state,
891 alias_member->sids.sids[i].sid,
894 if (!NT_STATUS_IS_OK(nt_status)) {
897 } else if (ret > 1) {
898 return NT_STATUS_INTERNAL_DB_CORRUPTION;
900 alias_member_dn = msgs[0]->dn;
902 samdb_msg_add_string(state->sam_ldb, mem_ctx, msg, "member", ldb_dn_linearize(mem_ctx, alias_member_dn));
907 ret = samdb_replace(state->sam_ldb, mem_ctx, msg);
909 *error_string = talloc_asprintf(mem_ctx, "Failed to modify group record %s: %s",
910 ldb_dn_linearize(mem_ctx, msg->dn),
911 ldb_errstring(state->sam_ldb));
912 return NT_STATUS_INTERNAL_DB_CORRUPTION;
918 static NTSTATUS samsync_ldb_handle_account(TALLOC_CTX *mem_ctx,
919 struct samsync_ldb_state *state,
920 enum netr_SamDatabaseID database,
921 struct netr_DELTA_ENUM *delta,
924 struct dom_sid *sid = delta->delta_id_union.sid;
925 struct netr_DELTA_ACCOUNT *account = delta->delta_union.account;
927 struct ldb_message *msg;
928 struct ldb_message **msgs;
929 struct ldb_dn *privilege_dn;
931 const char *attrs[] = { NULL };
934 msg = ldb_msg_new(mem_ctx);
936 return NT_STATUS_NO_MEMORY;
939 /* search for the account, by sid, in the top basedn */
940 ret = gendb_search(state->sam_ldb, mem_ctx, state->base_dn[SAM_DATABASE_DOMAIN], &msgs, attrs,
941 "(objectSid=%s)", ldap_encode_ndr_dom_sid(mem_ctx, sid));
944 *error_string = talloc_asprintf(mem_ctx, "gendb_search failed: %s", ldb_errstring(state->sam_ldb));
945 return NT_STATUS_INTERNAL_DB_CORRUPTION;
946 } else if (ret == 0) {
948 nt_status = samsync_ldb_add_foreignSecurityPrincipal(mem_ctx, state,
952 privilege_dn = talloc_steal(msg, privilege_dn);
953 if (!NT_STATUS_IS_OK(nt_status)) {
956 } else if (ret > 1) {
957 *error_string = talloc_asprintf(mem_ctx, "More than one account with SID: %s",
958 dom_sid_string(mem_ctx, sid));
959 return NT_STATUS_INTERNAL_DB_CORRUPTION;
961 privilege_dn = talloc_steal(msg, msgs[0]->dn);
964 msg->dn = privilege_dn;
966 for (i=0; i< account->privilege_entries; i++) {
967 samdb_msg_add_string(state->sam_ldb, mem_ctx, msg, "privilege",
968 account->privilege_name[i].string);
971 ret = samdb_replace(state->sam_ldb, mem_ctx, msg);
973 *error_string = talloc_asprintf(mem_ctx, "Failed to modify privilege record %s",
974 ldb_dn_linearize(mem_ctx, msg->dn));
975 return NT_STATUS_INTERNAL_DB_CORRUPTION;
981 static NTSTATUS samsync_ldb_delete_account(TALLOC_CTX *mem_ctx,
982 struct samsync_ldb_state *state,
983 enum netr_SamDatabaseID database,
984 struct netr_DELTA_ENUM *delta,
987 struct dom_sid *sid = delta->delta_id_union.sid;
989 struct ldb_message *msg;
990 struct ldb_message **msgs;
992 const char *attrs[] = { NULL };
994 msg = ldb_msg_new(mem_ctx);
996 return NT_STATUS_NO_MEMORY;
999 /* search for the account, by sid, in the top basedn */
1000 ret = gendb_search(state->sam_ldb, mem_ctx, state->base_dn[SAM_DATABASE_DOMAIN], &msgs, attrs,
1002 ldap_encode_ndr_dom_sid(mem_ctx, sid));
1005 *error_string = talloc_asprintf(mem_ctx, "gendb_search failed: %s", ldb_errstring(state->sam_ldb));
1006 return NT_STATUS_INTERNAL_DB_CORRUPTION;
1007 } else if (ret == 0) {
1008 return NT_STATUS_NO_SUCH_USER;
1009 } else if (ret > 1) {
1010 *error_string = talloc_asprintf(mem_ctx, "More than one account with SID: %s",
1011 dom_sid_string(mem_ctx, sid));
1012 return NT_STATUS_INTERNAL_DB_CORRUPTION;
1014 msg->dn = talloc_steal(msg, msgs[0]->dn);
1017 samdb_msg_add_delete(state->sam_ldb, mem_ctx, msg,
1020 ret = samdb_replace(state->sam_ldb, mem_ctx, msg);
1022 *error_string = talloc_asprintf(mem_ctx, "Failed to modify privilege record %s",
1023 ldb_dn_linearize(mem_ctx, msg->dn));
1024 return NT_STATUS_INTERNAL_DB_CORRUPTION;
1027 return NT_STATUS_OK;
1030 static NTSTATUS libnet_samsync_ldb_fn(TALLOC_CTX *mem_ctx,
1032 enum netr_SamDatabaseID database,
1033 struct netr_DELTA_ENUM *delta,
1034 char **error_string)
1036 NTSTATUS nt_status = NT_STATUS_OK;
1037 struct samsync_ldb_state *state = talloc_get_type(private, struct samsync_ldb_state);
1039 *error_string = NULL;
1040 switch (delta->delta_type) {
1041 case NETR_DELTA_DOMAIN:
1043 nt_status = samsync_ldb_handle_domain(mem_ctx,
1050 case NETR_DELTA_USER:
1052 nt_status = samsync_ldb_handle_user(mem_ctx,
1059 case NETR_DELTA_DELETE_USER:
1061 nt_status = samsync_ldb_delete_user(mem_ctx,
1068 case NETR_DELTA_GROUP:
1070 nt_status = samsync_ldb_handle_group(mem_ctx,
1077 case NETR_DELTA_DELETE_GROUP:
1079 nt_status = samsync_ldb_delete_group(mem_ctx,
1086 case NETR_DELTA_GROUP_MEMBER:
1088 nt_status = samsync_ldb_handle_group_member(mem_ctx,
1095 case NETR_DELTA_ALIAS:
1097 nt_status = samsync_ldb_handle_alias(mem_ctx,
1104 case NETR_DELTA_DELETE_ALIAS:
1106 nt_status = samsync_ldb_delete_alias(mem_ctx,
1113 case NETR_DELTA_ALIAS_MEMBER:
1115 nt_status = samsync_ldb_handle_alias_member(mem_ctx,
1122 case NETR_DELTA_ACCOUNT:
1124 nt_status = samsync_ldb_handle_account(mem_ctx,
1131 case NETR_DELTA_DELETE_ACCOUNT:
1133 nt_status = samsync_ldb_delete_account(mem_ctx,
1141 /* Can't dump them all right now */
1144 if (!NT_STATUS_IS_OK(nt_status) && !*error_string) {
1145 *error_string = talloc_asprintf(mem_ctx, "Failed to handle samsync delta: %s", nt_errstr(nt_status));
1150 static NTSTATUS libnet_samsync_ldb_init(TALLOC_CTX *mem_ctx,
1152 struct libnet_SamSync_state *samsync_state,
1153 char **error_string)
1155 struct samsync_ldb_state *state = talloc_get_type(private, struct samsync_ldb_state);
1156 const char *server = dcerpc_server_name(samsync_state->netlogon_pipe);
1159 state->samsync_state = samsync_state;
1161 ZERO_STRUCT(state->dom_sid);
1162 if (state->samsync_state->domain_sid) {
1163 state->dom_sid[SAM_DATABASE_DOMAIN] = dom_sid_dup(state, state->samsync_state->domain_sid);
1166 state->dom_sid[SAM_DATABASE_BUILTIN] = dom_sid_parse_talloc(state, SID_BUILTIN);
1168 if (state->samsync_state->realm) {
1169 if (!server || !*server) {
1170 /* huh? how do we not have a server name? */
1171 *error_string = talloc_strdup(mem_ctx, "No DCE/RPC server name available. How did we connect?");
1172 return NT_STATUS_INVALID_PARAMETER;
1174 ldap_url = talloc_asprintf(state, "ldap://%s", server);
1176 state->remote_ldb = ldb_wrap_connect(mem_ctx, ldap_url,
1177 NULL, state->samsync_state->machine_net_ctx->cred,
1179 if (!state->remote_ldb) {
1180 *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);
1181 return NT_STATUS_NO_LOGON_SERVERS;
1184 state->remote_ldb = NULL;
1186 return NT_STATUS_OK;
1189 NTSTATUS libnet_samsync_ldb(struct libnet_context *ctx, TALLOC_CTX *mem_ctx, struct libnet_samsync_ldb *r)
1192 struct libnet_SamSync r2;
1193 struct samsync_ldb_state *state = talloc(mem_ctx, struct samsync_ldb_state);
1196 return NT_STATUS_NO_MEMORY;
1199 state->secrets = NULL;
1200 state->trusted_domains = NULL;
1202 state->sam_ldb = ldb_wrap_connect(mem_ctx, lp_sam_url(), r->in.session_info,
1203 ctx->cred, 0, NULL);
1205 r2.out.error_string = NULL;
1206 r2.in.binding_string = r->in.binding_string;
1207 r2.in.init_fn = libnet_samsync_ldb_init;
1208 r2.in.delta_fn = libnet_samsync_ldb_fn;
1209 r2.in.fn_ctx = state;
1210 r2.in.machine_account = NULL; /* TODO: Create a machine account, fill this in, and the delete it */
1211 nt_status = libnet_SamSync_netlogon(ctx, state, &r2);
1212 r->out.error_string = r2.out.error_string;
1213 talloc_steal(mem_ctx, r->out.error_string);
1215 if (!NT_STATUS_IS_OK(nt_status)) {