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/>.
26 #include "libnet/libnet.h"
27 #include "libcli/ldap/ldap.h"
28 #include "dsdb/samdb/samdb.h"
29 #include "auth/auth.h"
30 #include "librpc/gen_ndr/ndr_misc.h"
32 #include "libcli/security/security.h"
33 #include "librpc/rpc/dcerpc.h"
34 #include "param/param.h"
36 struct samsync_ldb_secret {
37 struct samsync_ldb_secret *prev, *next;
43 struct samsync_ldb_trusted_domain {
44 struct samsync_ldb_trusted_domain *prev, *next;
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;
55 struct ldb_dn *base_dn[3];
56 struct samsync_ldb_secret *secrets;
57 struct samsync_ldb_trusted_domain *trusted_domains;
60 static NTSTATUS samsync_ldb_add_foreignSecurityPrincipal(TALLOC_CTX *mem_ctx,
61 struct samsync_ldb_state *state,
63 struct ldb_dn **fsp_dn,
66 const char *sidstr = dom_sid_string(mem_ctx, sid);
67 /* We assume that ForeignSecurityPrincipals are under the BASEDN of the main domain */
68 struct ldb_dn *basedn = samdb_search_dn(state->sam_ldb, mem_ctx,
69 state->base_dn[SAM_DATABASE_DOMAIN],
70 "(&(objectClass=container)(cn=ForeignSecurityPrincipals))");
71 struct ldb_message *msg;
75 return NT_STATUS_NO_MEMORY;
79 *error_string = talloc_asprintf(mem_ctx,
80 "Failed to find DN for "
81 "ForeignSecurityPrincipal container under %s",
82 ldb_dn_get_linearized(state->base_dn[SAM_DATABASE_DOMAIN]));
83 return NT_STATUS_INTERNAL_DB_CORRUPTION;
86 msg = ldb_msg_new(mem_ctx);
88 return NT_STATUS_NO_MEMORY;
91 /* add core elements to the ldb_message for the alias */
93 if ( ! ldb_dn_add_child_fmt(msg->dn, "CN=%s", sidstr))
94 return NT_STATUS_UNSUCCESSFUL;
96 samdb_msg_add_string(state->sam_ldb, mem_ctx, msg,
98 "foreignSecurityPrincipal");
102 /* create the alias */
103 ret = samdb_add(state->sam_ldb, mem_ctx, msg);
105 *error_string = talloc_asprintf(mem_ctx, "Failed to create foreignSecurityPrincipal "
107 ldb_dn_get_linearized(msg->dn),
108 ldb_errstring(state->sam_ldb));
109 return NT_STATUS_INTERNAL_DB_CORRUPTION;
114 static NTSTATUS samsync_ldb_handle_domain(TALLOC_CTX *mem_ctx,
115 struct samsync_ldb_state *state,
116 enum netr_SamDatabaseID database,
117 struct netr_DELTA_ENUM *delta,
120 struct netr_DELTA_DOMAIN *domain = delta->delta_union.domain;
121 const char *domain_name = domain->domain_name.string;
122 struct ldb_message *msg;
125 msg = ldb_msg_new(mem_ctx);
127 return NT_STATUS_NO_MEMORY;
130 if (database == SAM_DATABASE_DOMAIN) {
131 struct ldb_dn *partitions_basedn;
132 const char *domain_attrs[] = {"nETBIOSName", "nCName", NULL};
133 struct ldb_message **msgs_domain;
136 partitions_basedn = samdb_partitions_dn(state->sam_ldb, mem_ctx);
138 ret_domain = gendb_search(state->sam_ldb, mem_ctx, partitions_basedn, &msgs_domain, domain_attrs,
139 "(&(&(nETBIOSName=%s)(objectclass=crossRef))(ncName=*))",
141 if (ret_domain == -1) {
142 *error_string = talloc_asprintf(mem_ctx, "gendb_search for domain failed: %s", ldb_errstring(state->sam_ldb));
143 return NT_STATUS_INTERNAL_DB_CORRUPTION;
146 if (ret_domain != 1) {
147 *error_string = talloc_asprintf(mem_ctx, "Failed to find existing domain record for %s: %d results", domain_name,
149 return NT_STATUS_NO_SUCH_DOMAIN;
152 state->base_dn[database] = samdb_result_dn(state->sam_ldb, state, msgs_domain[0], "nCName", NULL);
154 if (state->dom_sid[database]) {
155 /* Update the domain sid with the incoming
156 * domain (found on LSA pipe, database sid may
158 samdb_msg_add_dom_sid(state->sam_ldb, mem_ctx,
159 msg, "objectSid", state->dom_sid[database]);
161 /* Well, we will have to use the one from the database */
162 state->dom_sid[database] = samdb_search_dom_sid(state->sam_ldb, state,
163 state->base_dn[database],
167 if (state->samsync_state->domain_guid) {
170 nt_status = ndr_push_struct_blob(&v, msg, state->samsync_state->domain_guid,
171 (ndr_push_flags_fn_t)ndr_push_GUID);
172 if (!NT_STATUS_IS_OK(nt_status)) {
173 *error_string = talloc_asprintf(mem_ctx, "ndr_push of domain GUID failed!");
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
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;
190 return NT_STATUS_INVALID_PARAMETER;
193 msg->dn = talloc_reference(mem_ctx, state->base_dn[database]);
195 return NT_STATUS_NO_MEMORY;
198 samdb_msg_add_string(state->sam_ldb, mem_ctx,
199 msg, "oEMInformation", domain->comment.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 = samdb_replace(state->sam_ldb, mem_ctx, msg);
228 return NT_STATUS_INTERNAL_ERROR;
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,
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;
244 const struct dom_sid *user_sid;
245 struct ldb_message *msg;
246 struct ldb_message **msgs;
247 struct ldb_message **remote_msgs = NULL;
251 const char *attrs[] = { NULL };
252 /* we may change this to a global search, then fill in only the things not in ldap later */
253 const char *remote_attrs[] = { "userPrincipalName", "servicePrincipalName",
254 "msDS-KeyVersionNumber", "objectGUID", NULL};
256 user_sid = dom_sid_add_rid(mem_ctx, state->dom_sid[database], rid);
258 return NT_STATUS_NO_MEMORY;
261 msg = ldb_msg_new(mem_ctx);
263 return NT_STATUS_NO_MEMORY;
267 /* search for the user, by rid */
268 ret = gendb_search(state->sam_ldb, mem_ctx, state->base_dn[database],
269 &msgs, attrs, "(&(objectClass=user)(objectSid=%s))",
270 ldap_encode_ndr_dom_sid(mem_ctx, user_sid));
273 *error_string = talloc_asprintf(mem_ctx, "LDB for user %s failed: %s",
274 dom_sid_string(mem_ctx, user_sid),
275 ldb_errstring(state->sam_ldb));
276 return NT_STATUS_INTERNAL_DB_CORRUPTION;
277 } else if (ret == 0) {
279 } else if (ret > 1) {
280 *error_string = talloc_asprintf(mem_ctx, "More than one user with SID: %s in local LDB",
281 dom_sid_string(mem_ctx, user_sid));
282 return NT_STATUS_INTERNAL_DB_CORRUPTION;
284 msg->dn = msgs[0]->dn;
285 talloc_steal(msg, msgs[0]->dn);
288 /* and do the same on the remote database */
289 if (state->remote_ldb) {
290 ret = gendb_search(state->remote_ldb, mem_ctx, state->base_dn[database],
291 &remote_msgs, remote_attrs, "(&(objectClass=user)(objectSid=%s))",
292 ldap_encode_ndr_dom_sid(mem_ctx, user_sid));
295 *error_string = talloc_asprintf(mem_ctx, "remote LDAP for user %s failed: %s",
296 dom_sid_string(mem_ctx, user_sid),
297 ldb_errstring(state->remote_ldb));
298 return NT_STATUS_INTERNAL_DB_CORRUPTION;
299 } else if (ret == 0) {
300 *error_string = talloc_asprintf(mem_ctx, "User exists in samsync but not in remote LDAP domain! (base: %s, SID: %s)",
301 ldb_dn_get_linearized(state->base_dn[database]),
302 dom_sid_string(mem_ctx, user_sid));
303 return NT_STATUS_NO_SUCH_USER;
304 } else if (ret > 1) {
305 *error_string = talloc_asprintf(mem_ctx, "More than one user in remote LDAP domain with SID: %s",
306 dom_sid_string(mem_ctx, user_sid));
307 return NT_STATUS_INTERNAL_DB_CORRUPTION;
309 /* Try to put things in the same location as the remote server */
311 msg->dn = remote_msgs[0]->dn;
312 talloc_steal(msg, remote_msgs[0]->dn);
316 cn_name = talloc_strdup(mem_ctx, user->account_name.string);
317 NT_STATUS_HAVE_NO_MEMORY(cn_name);
318 cn_name_len = strlen(cn_name);
320 #define ADD_OR_DEL(type, attrib, field) do { \
322 samdb_msg_add_ ## type(state->sam_ldb, mem_ctx, msg, \
323 attrib, user->field); \
325 samdb_msg_add_delete(state->sam_ldb, mem_ctx, msg, \
330 ADD_OR_DEL(string, "samAccountName", account_name.string);
331 ADD_OR_DEL(string, "displayName", full_name.string);
333 if (samdb_msg_add_dom_sid(state->sam_ldb, mem_ctx, msg,
334 "objectSid", dom_sid_add_rid(mem_ctx, state->dom_sid[database], rid))) {
335 return NT_STATUS_NO_MEMORY;
338 ADD_OR_DEL(uint, "primaryGroupID", primary_gid);
339 ADD_OR_DEL(string, "homeDirectory", home_directory.string);
340 ADD_OR_DEL(string, "homeDrive", home_drive.string);
341 ADD_OR_DEL(string, "scriptPath", logon_script.string);
342 ADD_OR_DEL(string, "description", description.string);
343 ADD_OR_DEL(string, "userWorkstations", workstations.string);
345 ADD_OR_DEL(uint64, "lastLogon", last_logon);
346 ADD_OR_DEL(uint64, "lastLogoff", last_logoff);
348 if (samdb_msg_add_logon_hours(state->sam_ldb, mem_ctx, msg, "logonHours", &user->logon_hours) != 0) {
349 return NT_STATUS_NO_MEMORY;
352 ADD_OR_DEL(uint, "badPwdCount", bad_password_count);
353 ADD_OR_DEL(uint, "logonCount", logon_count);
355 ADD_OR_DEL(uint64, "pwdLastSet", last_password_change);
356 ADD_OR_DEL(uint64, "accountExpires", acct_expiry);
358 if (samdb_msg_add_acct_flags(state->sam_ldb, mem_ctx, msg,
359 "userAccountControl", user->acct_flags) != 0) {
360 return NT_STATUS_NO_MEMORY;
364 /* Passwords. Ensure there is no plaintext stored against
365 * this entry, as we only have hashes */
366 samdb_msg_add_delete(state->sam_ldb, mem_ctx, msg,
369 if (user->lm_password_present) {
370 samdb_msg_add_hash(state->sam_ldb, mem_ctx, msg,
371 "dBCSPwd", &user->lmpassword);
373 samdb_msg_add_delete(state->sam_ldb, mem_ctx, msg,
376 if (user->nt_password_present) {
377 samdb_msg_add_hash(state->sam_ldb, mem_ctx, msg,
378 "unicodePwd", &user->ntpassword);
380 samdb_msg_add_delete(state->sam_ldb, mem_ctx, msg,
384 ADD_OR_DEL(string, "comment", comment.string);
385 ADD_OR_DEL(string, "userParameters", parameters.string);
386 ADD_OR_DEL(uint, "countryCode", country_code);
387 ADD_OR_DEL(uint, "codePage", code_page);
389 ADD_OR_DEL(string, "profilePath", profile_path.string);
393 for (i=0; remote_attrs[i]; i++) {
394 struct ldb_message_element *el = ldb_msg_find_element(remote_msgs[0], remote_attrs[i]);
396 samdb_msg_add_delete(state->sam_ldb, mem_ctx, msg,
399 ldb_msg_add(msg, el, LDB_FLAG_MOD_REPLACE);
403 acb = user->acct_flags;
404 if (acb & (ACB_WSTRUST)) {
405 cn_name[cn_name_len - 1] = '\0';
406 container = "Computers";
407 obj_class = "computer";
409 } else if (acb & ACB_SVRTRUST) {
410 if (cn_name[cn_name_len - 1] != '$') {
411 return NT_STATUS_FOOBAR;
413 cn_name[cn_name_len - 1] = '\0';
414 container = "Domain Controllers";
415 obj_class = "computer";
421 samdb_msg_add_string(state->sam_ldb, mem_ctx, msg,
422 "objectClass", obj_class);
424 msg->dn = ldb_dn_copy(mem_ctx, state->base_dn[database]);
425 ldb_dn_add_child_fmt(msg->dn, "CN=%s,CN=%s", cn_name, container);
427 return NT_STATUS_NO_MEMORY;
431 ret = samdb_add(state->sam_ldb, mem_ctx, msg);
433 struct ldb_dn *first_try_dn = msg->dn;
434 /* Try again with the default DN */
436 *error_string = talloc_asprintf(mem_ctx, "Failed to create user record. Tried %s: %s",
437 ldb_dn_get_linearized(first_try_dn),
438 ldb_errstring(state->sam_ldb));
439 return NT_STATUS_INTERNAL_DB_CORRUPTION;
441 msg->dn = talloc_steal(msg, remote_msgs[0]->dn);
442 ret = samdb_add(state->sam_ldb, mem_ctx, msg);
444 *error_string = talloc_asprintf(mem_ctx, "Failed to create user record. Tried both %s and %s: %s",
445 ldb_dn_get_linearized(first_try_dn),
446 ldb_dn_get_linearized(msg->dn),
447 ldb_errstring(state->sam_ldb));
448 return NT_STATUS_INTERNAL_DB_CORRUPTION;
453 ret = samdb_replace(state->sam_ldb, mem_ctx, msg);
455 *error_string = talloc_asprintf(mem_ctx, "Failed to modify user record %s: %s",
456 ldb_dn_get_linearized(msg->dn),
457 ldb_errstring(state->sam_ldb));
458 return NT_STATUS_INTERNAL_DB_CORRUPTION;
465 static NTSTATUS samsync_ldb_delete_user(TALLOC_CTX *mem_ctx,
466 struct samsync_ldb_state *state,
467 enum netr_SamDatabaseID database,
468 struct netr_DELTA_ENUM *delta,
471 uint32_t rid = delta->delta_id_union.rid;
472 struct ldb_message **msgs;
474 const char *attrs[] = { NULL };
476 /* search for the user, by rid */
477 ret = gendb_search(state->sam_ldb, mem_ctx, state->base_dn[database],
478 &msgs, attrs, "(&(objectClass=user)(objectSid=%s))",
479 ldap_encode_ndr_dom_sid(mem_ctx, dom_sid_add_rid(mem_ctx, state->dom_sid[database], rid)));
482 *error_string = talloc_asprintf(mem_ctx, "gendb_search failed: %s", ldb_errstring(state->sam_ldb));
483 return NT_STATUS_INTERNAL_DB_CORRUPTION;
484 } else if (ret == 0) {
485 return NT_STATUS_NO_SUCH_USER;
486 } else if (ret > 1) {
487 *error_string = talloc_asprintf(mem_ctx, "More than one user with SID: %s",
488 dom_sid_string(mem_ctx,
489 dom_sid_add_rid(mem_ctx,
490 state->dom_sid[database],
492 return NT_STATUS_INTERNAL_DB_CORRUPTION;
495 ret = samdb_delete(state->sam_ldb, mem_ctx, msgs[0]->dn);
497 *error_string = talloc_asprintf(mem_ctx, "Failed to delete user record %s: %s",
498 ldb_dn_get_linearized(msgs[0]->dn),
499 ldb_errstring(state->sam_ldb));
500 return NT_STATUS_INTERNAL_DB_CORRUPTION;
506 static NTSTATUS samsync_ldb_handle_group(TALLOC_CTX *mem_ctx,
507 struct samsync_ldb_state *state,
508 enum netr_SamDatabaseID database,
509 struct netr_DELTA_ENUM *delta,
512 uint32_t rid = delta->delta_id_union.rid;
513 struct netr_DELTA_GROUP *group = delta->delta_union.group;
514 const char *container, *obj_class;
517 struct ldb_message *msg;
518 struct ldb_message **msgs;
521 const char *attrs[] = { NULL };
523 msg = ldb_msg_new(mem_ctx);
525 return NT_STATUS_NO_MEMORY;
528 /* search for the group, by rid */
529 ret = gendb_search(state->sam_ldb, mem_ctx, state->base_dn[database], &msgs, attrs,
530 "(&(objectClass=group)(objectSid=%s))",
531 ldap_encode_ndr_dom_sid(mem_ctx, dom_sid_add_rid(mem_ctx, state->dom_sid[database], rid)));
534 *error_string = talloc_asprintf(mem_ctx, "gendb_search failed: %s", ldb_errstring(state->sam_ldb));
535 return NT_STATUS_INTERNAL_DB_CORRUPTION;
536 } else if (ret == 0) {
538 } else if (ret > 1) {
539 *error_string = talloc_asprintf(mem_ctx, "More than one group/alias with SID: %s",
540 dom_sid_string(mem_ctx,
541 dom_sid_add_rid(mem_ctx,
542 state->dom_sid[database],
544 return NT_STATUS_INTERNAL_DB_CORRUPTION;
546 msg->dn = talloc_steal(msg, msgs[0]->dn);
549 cn_name = group->group_name.string;
551 #define ADD_OR_DEL(type, attrib, field) do { \
552 if (group->field) { \
553 samdb_msg_add_ ## type(state->sam_ldb, mem_ctx, msg, \
554 attrib, group->field); \
556 samdb_msg_add_delete(state->sam_ldb, mem_ctx, msg, \
561 ADD_OR_DEL(string, "samAccountName", group_name.string);
563 if (samdb_msg_add_dom_sid(state->sam_ldb, mem_ctx, msg,
564 "objectSid", dom_sid_add_rid(mem_ctx, state->dom_sid[database], rid))) {
565 return NT_STATUS_NO_MEMORY;
568 ADD_OR_DEL(string, "description", description.string);
576 samdb_msg_add_string(state->sam_ldb, mem_ctx, msg,
577 "objectClass", obj_class);
578 msg->dn = ldb_dn_copy(mem_ctx, state->base_dn[database]);
579 ldb_dn_add_child_fmt(msg->dn, "CN=%s,CN=%s", cn_name, container);
581 return NT_STATUS_NO_MEMORY;
584 ret = samdb_add(state->sam_ldb, mem_ctx, msg);
586 *error_string = talloc_asprintf(mem_ctx, "Failed to create group record %s: %s",
587 ldb_dn_get_linearized(msg->dn),
588 ldb_errstring(state->sam_ldb));
589 return NT_STATUS_INTERNAL_DB_CORRUPTION;
592 ret = samdb_replace(state->sam_ldb, mem_ctx, msg);
594 *error_string = talloc_asprintf(mem_ctx, "Failed to modify group record %s: %s",
595 ldb_dn_get_linearized(msg->dn),
596 ldb_errstring(state->sam_ldb));
597 return NT_STATUS_INTERNAL_DB_CORRUPTION;
604 static NTSTATUS samsync_ldb_delete_group(TALLOC_CTX *mem_ctx,
605 struct samsync_ldb_state *state,
606 enum netr_SamDatabaseID database,
607 struct netr_DELTA_ENUM *delta,
610 uint32_t rid = delta->delta_id_union.rid;
611 struct ldb_message **msgs;
613 const char *attrs[] = { NULL };
615 /* search for the group, by rid */
616 ret = gendb_search(state->sam_ldb, mem_ctx, state->base_dn[database], &msgs, attrs,
617 "(&(objectClass=group)(objectSid=%s))",
618 ldap_encode_ndr_dom_sid(mem_ctx, dom_sid_add_rid(mem_ctx, state->dom_sid[database], rid)));
621 *error_string = talloc_asprintf(mem_ctx, "gendb_search failed: %s", ldb_errstring(state->sam_ldb));
622 return NT_STATUS_INTERNAL_DB_CORRUPTION;
623 } else if (ret == 0) {
624 return NT_STATUS_NO_SUCH_GROUP;
625 } else if (ret > 1) {
626 *error_string = talloc_asprintf(mem_ctx, "More than one group/alias with SID: %s",
627 dom_sid_string(mem_ctx,
628 dom_sid_add_rid(mem_ctx,
629 state->dom_sid[database],
631 return NT_STATUS_INTERNAL_DB_CORRUPTION;
634 ret = samdb_delete(state->sam_ldb, mem_ctx, msgs[0]->dn);
636 *error_string = talloc_asprintf(mem_ctx, "Failed to delete group record %s: %s",
637 ldb_dn_get_linearized(msgs[0]->dn),
638 ldb_errstring(state->sam_ldb));
639 return NT_STATUS_INTERNAL_DB_CORRUPTION;
645 static NTSTATUS samsync_ldb_handle_group_member(TALLOC_CTX *mem_ctx,
646 struct samsync_ldb_state *state,
647 enum netr_SamDatabaseID database,
648 struct netr_DELTA_ENUM *delta,
651 uint32_t rid = delta->delta_id_union.rid;
652 struct netr_DELTA_GROUP_MEMBER *group_member = delta->delta_union.group_member;
653 struct ldb_message *msg;
654 struct ldb_message **msgs;
656 const char *attrs[] = { NULL };
659 msg = ldb_msg_new(mem_ctx);
661 return NT_STATUS_NO_MEMORY;
664 /* search for the group, by rid */
665 ret = gendb_search(state->sam_ldb, mem_ctx, state->base_dn[database], &msgs, attrs,
666 "(&(objectClass=group)(objectSid=%s))",
667 ldap_encode_ndr_dom_sid(mem_ctx, dom_sid_add_rid(mem_ctx, state->dom_sid[database], rid)));
670 *error_string = talloc_asprintf(mem_ctx, "gendb_search failed: %s", ldb_errstring(state->sam_ldb));
671 return NT_STATUS_INTERNAL_DB_CORRUPTION;
672 } else if (ret == 0) {
673 return NT_STATUS_NO_SUCH_GROUP;
674 } else if (ret > 1) {
675 *error_string = talloc_asprintf(mem_ctx, "More than one group/alias with SID: %s",
676 dom_sid_string(mem_ctx,
677 dom_sid_add_rid(mem_ctx,
678 state->dom_sid[database],
680 return NT_STATUS_INTERNAL_DB_CORRUPTION;
682 msg->dn = talloc_steal(msg, msgs[0]->dn);
687 for (i=0; i<group_member->num_rids; i++) {
688 /* search for the group, by rid */
689 ret = gendb_search(state->sam_ldb, mem_ctx, state->base_dn[database], &msgs, attrs,
690 "(&(objectClass=user)(objectSid=%s))",
691 ldap_encode_ndr_dom_sid(mem_ctx, dom_sid_add_rid(mem_ctx, state->dom_sid[database], group_member->rids[i])));
694 *error_string = talloc_asprintf(mem_ctx, "gendb_search failed: %s", ldb_errstring(state->sam_ldb));
695 return NT_STATUS_INTERNAL_DB_CORRUPTION;
696 } else if (ret == 0) {
697 return NT_STATUS_NO_SUCH_USER;
698 } else if (ret > 1) {
699 return NT_STATUS_INTERNAL_DB_CORRUPTION;
701 samdb_msg_add_string(state->sam_ldb, mem_ctx, msg, "member", ldb_dn_alloc_linearized(mem_ctx, msgs[0]->dn));
707 ret = samdb_replace(state->sam_ldb, mem_ctx, msg);
709 *error_string = talloc_asprintf(mem_ctx, "Failed to modify group record %s: %s",
710 ldb_dn_get_linearized(msg->dn),
711 ldb_errstring(state->sam_ldb));
712 return NT_STATUS_INTERNAL_DB_CORRUPTION;
718 static NTSTATUS samsync_ldb_handle_alias(TALLOC_CTX *mem_ctx,
719 struct samsync_ldb_state *state,
720 enum netr_SamDatabaseID database,
721 struct netr_DELTA_ENUM *delta,
724 uint32_t rid = delta->delta_id_union.rid;
725 struct netr_DELTA_ALIAS *alias = delta->delta_union.alias;
726 const char *container, *obj_class;
729 struct ldb_message *msg;
730 struct ldb_message **msgs;
733 const char *attrs[] = { NULL };
735 msg = ldb_msg_new(mem_ctx);
737 return NT_STATUS_NO_MEMORY;
740 /* search for the alias, by rid */
741 ret = gendb_search(state->sam_ldb, mem_ctx, state->base_dn[database], &msgs, attrs,
742 "(&(objectClass=group)(objectSid=%s))",
743 ldap_encode_ndr_dom_sid(mem_ctx, dom_sid_add_rid(mem_ctx, state->dom_sid[database], rid)));
746 *error_string = talloc_asprintf(mem_ctx, "gendb_search failed: %s", ldb_errstring(state->sam_ldb));
747 return NT_STATUS_INTERNAL_DB_CORRUPTION;
748 } else if (ret == 0) {
750 } else if (ret > 1) {
751 *error_string = talloc_asprintf(mem_ctx, "More than one group/alias with SID: %s",
752 dom_sid_string(mem_ctx,
753 dom_sid_add_rid(mem_ctx,
754 state->dom_sid[database],
756 return NT_STATUS_INTERNAL_DB_CORRUPTION;
758 msg->dn = talloc_steal(mem_ctx, msgs[0]->dn);
761 cn_name = alias->alias_name.string;
763 #define ADD_OR_DEL(type, attrib, field) do { \
764 if (alias->field) { \
765 samdb_msg_add_ ## type(state->sam_ldb, mem_ctx, msg, \
766 attrib, alias->field); \
768 samdb_msg_add_delete(state->sam_ldb, mem_ctx, msg, \
773 ADD_OR_DEL(string, "samAccountName", alias_name.string);
775 if (samdb_msg_add_dom_sid(state->sam_ldb, mem_ctx, msg,
776 "objectSid", dom_sid_add_rid(mem_ctx, state->dom_sid[database], rid))) {
777 return NT_STATUS_NO_MEMORY;
780 ADD_OR_DEL(string, "description", description.string);
784 samdb_msg_add_uint(state->sam_ldb, mem_ctx, msg, "groupType", 0x80000004);
790 samdb_msg_add_string(state->sam_ldb, mem_ctx, msg,
791 "objectClass", obj_class);
792 msg->dn = ldb_dn_copy(mem_ctx, state->base_dn[database]);
793 ldb_dn_add_child_fmt(msg->dn, "CN=%s,CN=%s", cn_name, container);
795 return NT_STATUS_NO_MEMORY;
798 ret = samdb_add(state->sam_ldb, mem_ctx, msg);
800 *error_string = talloc_asprintf(mem_ctx, "Failed to create alias record %s: %s",
801 ldb_dn_get_linearized(msg->dn),
802 ldb_errstring(state->sam_ldb));
803 return NT_STATUS_INTERNAL_DB_CORRUPTION;
806 ret = samdb_replace(state->sam_ldb, mem_ctx, msg);
808 *error_string = talloc_asprintf(mem_ctx, "Failed to modify alias record %s: %s",
809 ldb_dn_get_linearized(msg->dn),
810 ldb_errstring(state->sam_ldb));
811 return NT_STATUS_INTERNAL_DB_CORRUPTION;
818 static NTSTATUS samsync_ldb_delete_alias(TALLOC_CTX *mem_ctx,
819 struct samsync_ldb_state *state,
820 enum netr_SamDatabaseID database,
821 struct netr_DELTA_ENUM *delta,
824 uint32_t rid = delta->delta_id_union.rid;
825 struct ldb_message **msgs;
827 const char *attrs[] = { NULL };
829 /* search for the alias, by rid */
830 ret = gendb_search(state->sam_ldb, mem_ctx, state->base_dn[database], &msgs, attrs,
831 "(&(objectClass=group)(objectSid=%s))",
832 ldap_encode_ndr_dom_sid(mem_ctx, dom_sid_add_rid(mem_ctx, state->dom_sid[database], rid)));
835 *error_string = talloc_asprintf(mem_ctx, "gendb_search failed: %s", ldb_errstring(state->sam_ldb));
836 return NT_STATUS_INTERNAL_DB_CORRUPTION;
837 } else if (ret == 0) {
838 return NT_STATUS_NO_SUCH_ALIAS;
839 } else if (ret > 1) {
840 return NT_STATUS_INTERNAL_DB_CORRUPTION;
843 ret = samdb_delete(state->sam_ldb, mem_ctx, msgs[0]->dn);
845 *error_string = talloc_asprintf(mem_ctx, "Failed to delete alias record %s: %s",
846 ldb_dn_get_linearized(msgs[0]->dn),
847 ldb_errstring(state->sam_ldb));
848 return NT_STATUS_INTERNAL_DB_CORRUPTION;
854 static NTSTATUS samsync_ldb_handle_alias_member(TALLOC_CTX *mem_ctx,
855 struct samsync_ldb_state *state,
856 enum netr_SamDatabaseID database,
857 struct netr_DELTA_ENUM *delta,
860 uint32_t rid = delta->delta_id_union.rid;
861 struct netr_DELTA_ALIAS_MEMBER *alias_member = delta->delta_union.alias_member;
862 struct ldb_message *msg;
863 struct ldb_message **msgs;
865 const char *attrs[] = { NULL };
868 msg = ldb_msg_new(mem_ctx);
870 return NT_STATUS_NO_MEMORY;
873 /* search for the alias, by rid */
874 ret = gendb_search(state->sam_ldb, mem_ctx, state->base_dn[database], &msgs, attrs,
875 "(&(objectClass=group)(objectSid=%s))",
876 ldap_encode_ndr_dom_sid(mem_ctx, dom_sid_add_rid(mem_ctx, state->dom_sid[database], rid)));
879 *error_string = talloc_asprintf(mem_ctx, "gendb_search failed: %s", ldb_errstring(state->sam_ldb));
880 return NT_STATUS_INTERNAL_DB_CORRUPTION;
881 } else if (ret == 0) {
882 return NT_STATUS_NO_SUCH_GROUP;
883 } else if (ret > 1) {
884 *error_string = talloc_asprintf(mem_ctx, "More than one group/alias with SID: %s",
885 dom_sid_string(mem_ctx,
886 dom_sid_add_rid(mem_ctx,
887 state->dom_sid[database],
889 return NT_STATUS_INTERNAL_DB_CORRUPTION;
891 msg->dn = talloc_steal(msg, msgs[0]->dn);
896 for (i=0; i<alias_member->sids.num_sids; i++) {
897 struct ldb_dn *alias_member_dn;
898 /* search for members, in the top basedn (normal users are builtin aliases) */
899 ret = gendb_search(state->sam_ldb, mem_ctx, state->base_dn[SAM_DATABASE_DOMAIN], &msgs, attrs,
901 ldap_encode_ndr_dom_sid(mem_ctx, alias_member->sids.sids[i].sid));
904 *error_string = talloc_asprintf(mem_ctx, "gendb_search failed: %s", ldb_errstring(state->sam_ldb));
905 return NT_STATUS_INTERNAL_DB_CORRUPTION;
906 } else if (ret == 0) {
908 nt_status = samsync_ldb_add_foreignSecurityPrincipal(mem_ctx, state,
909 alias_member->sids.sids[i].sid,
912 if (!NT_STATUS_IS_OK(nt_status)) {
915 } else if (ret > 1) {
916 return NT_STATUS_INTERNAL_DB_CORRUPTION;
918 alias_member_dn = msgs[0]->dn;
920 samdb_msg_add_string(state->sam_ldb, mem_ctx, msg, "member", ldb_dn_alloc_linearized(mem_ctx, alias_member_dn));
925 ret = samdb_replace(state->sam_ldb, mem_ctx, msg);
927 *error_string = talloc_asprintf(mem_ctx, "Failed to modify group record %s: %s",
928 ldb_dn_get_linearized(msg->dn),
929 ldb_errstring(state->sam_ldb));
930 return NT_STATUS_INTERNAL_DB_CORRUPTION;
936 static NTSTATUS samsync_ldb_handle_account(TALLOC_CTX *mem_ctx,
937 struct samsync_ldb_state *state,
938 enum netr_SamDatabaseID database,
939 struct netr_DELTA_ENUM *delta,
942 struct dom_sid *sid = delta->delta_id_union.sid;
943 struct netr_DELTA_ACCOUNT *account = delta->delta_union.account;
945 struct ldb_message *msg;
946 struct ldb_message **msgs;
947 struct ldb_dn *privilege_dn;
949 const char *attrs[] = { NULL };
952 msg = ldb_msg_new(mem_ctx);
954 return NT_STATUS_NO_MEMORY;
957 /* search for the account, by sid, in the top basedn */
958 ret = gendb_search(state->sam_ldb, mem_ctx, state->base_dn[SAM_DATABASE_DOMAIN], &msgs, attrs,
959 "(objectSid=%s)", ldap_encode_ndr_dom_sid(mem_ctx, sid));
962 *error_string = talloc_asprintf(mem_ctx, "gendb_search failed: %s", ldb_errstring(state->sam_ldb));
963 return NT_STATUS_INTERNAL_DB_CORRUPTION;
964 } else if (ret == 0) {
966 nt_status = samsync_ldb_add_foreignSecurityPrincipal(mem_ctx, state,
970 privilege_dn = talloc_steal(msg, privilege_dn);
971 if (!NT_STATUS_IS_OK(nt_status)) {
974 } else if (ret > 1) {
975 *error_string = talloc_asprintf(mem_ctx, "More than one account with SID: %s",
976 dom_sid_string(mem_ctx, sid));
977 return NT_STATUS_INTERNAL_DB_CORRUPTION;
979 privilege_dn = talloc_steal(msg, msgs[0]->dn);
982 msg->dn = privilege_dn;
984 for (i=0; i< account->privilege_entries; i++) {
985 samdb_msg_add_string(state->sam_ldb, mem_ctx, msg, "privilege",
986 account->privilege_name[i].string);
989 ret = samdb_replace(state->sam_ldb, mem_ctx, msg);
991 *error_string = talloc_asprintf(mem_ctx, "Failed to modify privilege record %s",
992 ldb_dn_get_linearized(msg->dn));
993 return NT_STATUS_INTERNAL_DB_CORRUPTION;
999 static NTSTATUS samsync_ldb_delete_account(TALLOC_CTX *mem_ctx,
1000 struct samsync_ldb_state *state,
1001 enum netr_SamDatabaseID database,
1002 struct netr_DELTA_ENUM *delta,
1003 char **error_string)
1005 struct dom_sid *sid = delta->delta_id_union.sid;
1007 struct ldb_message *msg;
1008 struct ldb_message **msgs;
1010 const char *attrs[] = { NULL };
1012 msg = ldb_msg_new(mem_ctx);
1014 return NT_STATUS_NO_MEMORY;
1017 /* search for the account, by sid, in the top basedn */
1018 ret = gendb_search(state->sam_ldb, mem_ctx, state->base_dn[SAM_DATABASE_DOMAIN], &msgs, attrs,
1020 ldap_encode_ndr_dom_sid(mem_ctx, sid));
1023 *error_string = talloc_asprintf(mem_ctx, "gendb_search failed: %s", ldb_errstring(state->sam_ldb));
1024 return NT_STATUS_INTERNAL_DB_CORRUPTION;
1025 } else if (ret == 0) {
1026 return NT_STATUS_NO_SUCH_USER;
1027 } else if (ret > 1) {
1028 *error_string = talloc_asprintf(mem_ctx, "More than one account with SID: %s",
1029 dom_sid_string(mem_ctx, sid));
1030 return NT_STATUS_INTERNAL_DB_CORRUPTION;
1032 msg->dn = talloc_steal(msg, msgs[0]->dn);
1035 samdb_msg_add_delete(state->sam_ldb, mem_ctx, msg,
1038 ret = samdb_replace(state->sam_ldb, mem_ctx, msg);
1040 *error_string = talloc_asprintf(mem_ctx, "Failed to modify privilege record %s",
1041 ldb_dn_get_linearized(msg->dn));
1042 return NT_STATUS_INTERNAL_DB_CORRUPTION;
1045 return NT_STATUS_OK;
1048 static NTSTATUS libnet_samsync_ldb_fn(TALLOC_CTX *mem_ctx,
1050 enum netr_SamDatabaseID database,
1051 struct netr_DELTA_ENUM *delta,
1052 char **error_string)
1054 NTSTATUS nt_status = NT_STATUS_OK;
1055 struct samsync_ldb_state *state = talloc_get_type(private, struct samsync_ldb_state);
1057 *error_string = NULL;
1058 switch (delta->delta_type) {
1059 case NETR_DELTA_DOMAIN:
1061 nt_status = samsync_ldb_handle_domain(mem_ctx,
1068 case NETR_DELTA_USER:
1070 nt_status = samsync_ldb_handle_user(mem_ctx,
1077 case NETR_DELTA_DELETE_USER:
1079 nt_status = samsync_ldb_delete_user(mem_ctx,
1086 case NETR_DELTA_GROUP:
1088 nt_status = samsync_ldb_handle_group(mem_ctx,
1095 case NETR_DELTA_DELETE_GROUP:
1097 nt_status = samsync_ldb_delete_group(mem_ctx,
1104 case NETR_DELTA_GROUP_MEMBER:
1106 nt_status = samsync_ldb_handle_group_member(mem_ctx,
1113 case NETR_DELTA_ALIAS:
1115 nt_status = samsync_ldb_handle_alias(mem_ctx,
1122 case NETR_DELTA_DELETE_ALIAS:
1124 nt_status = samsync_ldb_delete_alias(mem_ctx,
1131 case NETR_DELTA_ALIAS_MEMBER:
1133 nt_status = samsync_ldb_handle_alias_member(mem_ctx,
1140 case NETR_DELTA_ACCOUNT:
1142 nt_status = samsync_ldb_handle_account(mem_ctx,
1149 case NETR_DELTA_DELETE_ACCOUNT:
1151 nt_status = samsync_ldb_delete_account(mem_ctx,
1159 /* Can't dump them all right now */
1162 if (!NT_STATUS_IS_OK(nt_status) && !*error_string) {
1163 *error_string = talloc_asprintf(mem_ctx, "Failed to handle samsync delta: %s", nt_errstr(nt_status));
1168 static NTSTATUS libnet_samsync_ldb_init(TALLOC_CTX *mem_ctx,
1170 struct libnet_SamSync_state *samsync_state,
1171 char **error_string)
1173 struct samsync_ldb_state *state = talloc_get_type(private, struct samsync_ldb_state);
1174 const char *server = dcerpc_server_name(samsync_state->netlogon_pipe);
1177 state->samsync_state = samsync_state;
1179 ZERO_STRUCT(state->dom_sid);
1180 if (state->samsync_state->domain_sid) {
1181 state->dom_sid[SAM_DATABASE_DOMAIN] = dom_sid_dup(state, state->samsync_state->domain_sid);
1184 state->dom_sid[SAM_DATABASE_BUILTIN] = dom_sid_parse_talloc(state, SID_BUILTIN);
1186 if (state->samsync_state->realm) {
1187 if (!server || !*server) {
1188 /* huh? how do we not have a server name? */
1189 *error_string = talloc_strdup(mem_ctx, "No DCE/RPC server name available. How did we connect?");
1190 return NT_STATUS_INVALID_PARAMETER;
1192 ldap_url = talloc_asprintf(state, "ldap://%s", server);
1194 state->remote_ldb = ldb_wrap_connect(mem_ctx, ldap_url,
1195 NULL, state->samsync_state->machine_net_ctx->cred,
1197 if (!state->remote_ldb) {
1198 *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);
1199 return NT_STATUS_NO_LOGON_SERVERS;
1202 state->remote_ldb = NULL;
1204 return NT_STATUS_OK;
1207 NTSTATUS libnet_samsync_ldb(struct libnet_context *ctx, TALLOC_CTX *mem_ctx, struct libnet_samsync_ldb *r)
1210 struct libnet_SamSync r2;
1211 struct samsync_ldb_state *state = talloc(mem_ctx, struct samsync_ldb_state);
1214 return NT_STATUS_NO_MEMORY;
1217 state->secrets = NULL;
1218 state->trusted_domains = NULL;
1220 state->sam_ldb = ldb_wrap_connect(mem_ctx, lp_sam_url(),
1222 ctx->cred, 0, NULL);
1224 r2.out.error_string = NULL;
1225 r2.in.binding_string = r->in.binding_string;
1226 r2.in.rid_crypt = true;
1227 r2.in.init_fn = libnet_samsync_ldb_init;
1228 r2.in.delta_fn = libnet_samsync_ldb_fn;
1229 r2.in.fn_ctx = state;
1230 r2.in.machine_account = NULL; /* TODO: Create a machine account, fill this in, and the delete it */
1231 nt_status = libnet_SamSync_netlogon(ctx, state, &r2);
1232 r->out.error_string = r2.out.error_string;
1233 talloc_steal(mem_ctx, r->out.error_string);
1235 if (!NT_STATUS_IS_OK(nt_status)) {