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 #include "libcli/security/security.h"
34 #include "librpc/rpc/dcerpc.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_linearize(mem_ctx, 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 */
92 msg->dn = ldb_dn_build_child(mem_ctx, "CN", sidstr, basedn);
94 return NT_STATUS_NO_MEMORY;
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_linearize(mem_ctx, 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 const 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, 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_explode(state, dnstring);
187 return NT_STATUS_INVALID_PARAMETER;
190 msg->dn = talloc_reference(mem_ctx, state->base_dn[database]);
192 return NT_STATUS_NO_MEMORY;
195 samdb_msg_add_string(state->sam_ldb, mem_ctx,
196 msg, "oEMInformation", domain->comment.string);
198 samdb_msg_add_int64(state->sam_ldb, mem_ctx,
199 msg, "forceLogoff", domain->force_logoff_time);
201 samdb_msg_add_uint(state->sam_ldb, mem_ctx,
202 msg, "minPwdLen", domain->min_password_length);
204 samdb_msg_add_int64(state->sam_ldb, mem_ctx,
205 msg, "maxPwdAge", domain->max_password_age);
207 samdb_msg_add_int64(state->sam_ldb, mem_ctx,
208 msg, "minPwdAge", domain->min_password_age);
210 samdb_msg_add_uint(state->sam_ldb, mem_ctx,
211 msg, "pwdHistoryLength", domain->password_history_length);
213 samdb_msg_add_uint64(state->sam_ldb, mem_ctx,
214 msg, "modifiedCount",
215 domain->sequence_num);
217 samdb_msg_add_uint64(state->sam_ldb, mem_ctx,
218 msg, "creationTime", domain->domain_create_time);
220 /* TODO: Account lockout, password properties */
222 ret = samdb_replace(state->sam_ldb, mem_ctx, msg);
225 return NT_STATUS_INTERNAL_ERROR;
230 static NTSTATUS samsync_ldb_handle_user(TALLOC_CTX *mem_ctx,
231 struct samsync_ldb_state *state,
232 enum netr_SamDatabaseID database,
233 struct netr_DELTA_ENUM *delta,
236 uint32_t rid = delta->delta_id_union.rid;
237 struct netr_DELTA_USER *user = delta->delta_union.user;
238 const char *container, *obj_class;
241 const struct dom_sid *user_sid;
242 struct ldb_message *msg;
243 struct ldb_message **msgs;
244 struct ldb_message **remote_msgs = NULL;
248 const char *attrs[] = { NULL };
249 /* we may change this to a global search, then fill in only the things not in ldap later */
250 const char *remote_attrs[] = { "userPrincipalName", "servicePrincipalName",
251 "msDS-KeyVersionNumber", "objectGUID", NULL};
253 user_sid = dom_sid_add_rid(mem_ctx, state->dom_sid[database], rid);
255 return NT_STATUS_NO_MEMORY;
258 msg = ldb_msg_new(mem_ctx);
260 return NT_STATUS_NO_MEMORY;
264 /* search for the user, by rid */
265 ret = gendb_search(state->sam_ldb, mem_ctx, state->base_dn[database],
266 &msgs, attrs, "(&(objectClass=user)(objectSid=%s))",
267 ldap_encode_ndr_dom_sid(mem_ctx, user_sid));
270 *error_string = talloc_asprintf(mem_ctx, "LDB for user %s failed: %s",
271 dom_sid_string(mem_ctx, user_sid),
272 ldb_errstring(state->sam_ldb));
273 return NT_STATUS_INTERNAL_DB_CORRUPTION;
274 } else if (ret == 0) {
276 } else if (ret > 1) {
277 *error_string = talloc_asprintf(mem_ctx, "More than one user with SID: %s in local LDB",
278 dom_sid_string(mem_ctx, user_sid));
279 return NT_STATUS_INTERNAL_DB_CORRUPTION;
281 msg->dn = msgs[0]->dn;
282 talloc_steal(msg, msgs[0]->dn);
285 /* and do the same on the remote database */
286 if (state->remote_ldb) {
287 ret = gendb_search(state->remote_ldb, mem_ctx, state->base_dn[database],
288 &remote_msgs, remote_attrs, "(&(objectClass=user)(objectSid=%s))",
289 ldap_encode_ndr_dom_sid(mem_ctx, user_sid));
292 *error_string = talloc_asprintf(mem_ctx, "remote LDAP for user %s failed: %s",
293 dom_sid_string(mem_ctx, user_sid),
294 ldb_errstring(state->remote_ldb));
295 return NT_STATUS_INTERNAL_DB_CORRUPTION;
296 } else if (ret == 0) {
297 *error_string = talloc_asprintf(mem_ctx, "User exists in samsync but not in remote LDAP domain! (base: %s, SID: %s)",
298 ldb_dn_linearize(mem_ctx, state->base_dn[database]),
299 dom_sid_string(mem_ctx, user_sid));
300 return NT_STATUS_NO_SUCH_USER;
301 } else if (ret > 1) {
302 *error_string = talloc_asprintf(mem_ctx, "More than one user in remote LDAP domain with SID: %s",
303 dom_sid_string(mem_ctx, user_sid));
304 return NT_STATUS_INTERNAL_DB_CORRUPTION;
306 /* Try to put things in the same location as the remote server */
308 msg->dn = remote_msgs[0]->dn;
309 talloc_steal(msg, remote_msgs[0]->dn);
313 cn_name = talloc_strdup(mem_ctx, user->account_name.string);
314 NT_STATUS_HAVE_NO_MEMORY(cn_name);
315 cn_name_len = strlen(cn_name);
317 #define ADD_OR_DEL(type, attrib, field) do { \
319 samdb_msg_add_ ## type(state->sam_ldb, mem_ctx, msg, \
320 attrib, user->field); \
322 samdb_msg_add_delete(state->sam_ldb, mem_ctx, msg, \
327 ADD_OR_DEL(string, "samAccountName", account_name.string);
328 ADD_OR_DEL(string, "displayName", full_name.string);
330 if (samdb_msg_add_dom_sid(state->sam_ldb, mem_ctx, msg,
331 "objectSid", dom_sid_add_rid(mem_ctx, state->dom_sid[database], rid))) {
332 return NT_STATUS_NO_MEMORY;
335 ADD_OR_DEL(uint, "primaryGroupID", primary_gid);
336 ADD_OR_DEL(string, "homeDirectory", home_directory.string);
337 ADD_OR_DEL(string, "homeDrive", home_drive.string);
338 ADD_OR_DEL(string, "scriptPath", logon_script.string);
339 ADD_OR_DEL(string, "description", description.string);
340 ADD_OR_DEL(string, "userWorkstations", workstations.string);
342 ADD_OR_DEL(uint64, "lastLogon", last_logon);
343 ADD_OR_DEL(uint64, "lastLogoff", last_logoff);
345 if (samdb_msg_add_logon_hours(state->sam_ldb, mem_ctx, msg, "logonHours", &user->logon_hours) != 0) {
346 return NT_STATUS_NO_MEMORY;
349 ADD_OR_DEL(uint, "badPwdCount", bad_password_count);
350 ADD_OR_DEL(uint, "logonCount", logon_count);
352 ADD_OR_DEL(uint64, "pwdLastSet", last_password_change);
353 ADD_OR_DEL(uint64, "accountExpires", acct_expiry);
355 if (samdb_msg_add_acct_flags(state->sam_ldb, mem_ctx, msg,
356 "userAccountControl", user->acct_flags) != 0) {
357 return NT_STATUS_NO_MEMORY;
361 /* Passwords. Ensure there is no plaintext stored against
362 * this entry, as we only have hashes */
363 samdb_msg_add_delete(state->sam_ldb, mem_ctx, msg,
366 if (user->lm_password_present) {
367 samdb_msg_add_hash(state->sam_ldb, mem_ctx, msg,
368 "lmPwdHash", &user->lmpassword);
370 samdb_msg_add_delete(state->sam_ldb, mem_ctx, msg,
373 if (user->nt_password_present) {
374 samdb_msg_add_hash(state->sam_ldb, mem_ctx, msg,
375 "ntPwdHash", &user->ntpassword);
377 samdb_msg_add_delete(state->sam_ldb, mem_ctx, msg,
381 ADD_OR_DEL(string, "comment", comment.string);
382 ADD_OR_DEL(string, "userParameters", parameters.string);
383 ADD_OR_DEL(uint, "countryCode", country_code);
384 ADD_OR_DEL(uint, "codePage", code_page);
386 ADD_OR_DEL(string, "profilePath", profile_path.string);
390 for (i=0; remote_attrs[i]; i++) {
391 struct ldb_message_element *el = ldb_msg_find_element(remote_msgs[0], remote_attrs[i]);
393 samdb_msg_add_delete(state->sam_ldb, mem_ctx, msg,
396 ldb_msg_add(msg, el, LDB_FLAG_MOD_REPLACE);
400 acb = user->acct_flags;
401 if (acb & (ACB_WSTRUST)) {
402 cn_name[cn_name_len - 1] = '\0';
403 container = "Computers";
404 obj_class = "computer";
406 } else if (acb & ACB_SVRTRUST) {
407 if (cn_name[cn_name_len - 1] != '$') {
408 return NT_STATUS_FOOBAR;
410 cn_name[cn_name_len - 1] = '\0';
411 container = "Domain Controllers";
412 obj_class = "computer";
418 samdb_msg_add_string(state->sam_ldb, mem_ctx, msg,
419 "objectClass", obj_class);
421 msg->dn = ldb_dn_string_compose(mem_ctx, state->base_dn[database],
422 "CN=%s, CN=%s", cn_name, container);
424 return NT_STATUS_NO_MEMORY;
428 ret = samdb_add(state->sam_ldb, mem_ctx, msg);
430 struct ldb_dn *first_try_dn = msg->dn;
431 /* Try again with the default DN */
432 msg->dn = talloc_steal(msg, msgs[0]->dn);
433 ret = samdb_add(state->sam_ldb, mem_ctx, msg);
435 *error_string = talloc_asprintf(mem_ctx, "Failed to create user record. Tried both %s and %s: %s",
436 ldb_dn_linearize(mem_ctx, first_try_dn),
437 ldb_dn_linearize(mem_ctx, msg->dn),
438 ldb_errstring(state->sam_ldb));
439 return NT_STATUS_INTERNAL_DB_CORRUPTION;
443 ret = samdb_replace(state->sam_ldb, mem_ctx, msg);
445 *error_string = talloc_asprintf(mem_ctx, "Failed to modify user record %s: %s",
446 ldb_dn_linearize(mem_ctx, msg->dn),
447 ldb_errstring(state->sam_ldb));
448 return NT_STATUS_INTERNAL_DB_CORRUPTION;
455 static NTSTATUS samsync_ldb_delete_user(TALLOC_CTX *mem_ctx,
456 struct samsync_ldb_state *state,
457 enum netr_SamDatabaseID database,
458 struct netr_DELTA_ENUM *delta,
461 uint32_t rid = delta->delta_id_union.rid;
462 struct ldb_message **msgs;
464 const char *attrs[] = { NULL };
466 /* search for the user, by rid */
467 ret = gendb_search(state->sam_ldb, mem_ctx, state->base_dn[database],
468 &msgs, attrs, "(&(objectClass=user)(objectSid=%s))",
469 ldap_encode_ndr_dom_sid(mem_ctx, dom_sid_add_rid(mem_ctx, state->dom_sid[database], rid)));
472 *error_string = talloc_asprintf(mem_ctx, "gendb_search failed: %s", ldb_errstring(state->sam_ldb));
473 return NT_STATUS_INTERNAL_DB_CORRUPTION;
474 } else if (ret == 0) {
475 return NT_STATUS_NO_SUCH_USER;
476 } else if (ret > 1) {
477 *error_string = talloc_asprintf(mem_ctx, "More than one user with SID: %s",
478 dom_sid_string(mem_ctx,
479 dom_sid_add_rid(mem_ctx,
480 state->dom_sid[database],
482 return NT_STATUS_INTERNAL_DB_CORRUPTION;
485 ret = samdb_delete(state->sam_ldb, mem_ctx, msgs[0]->dn);
487 *error_string = talloc_asprintf(mem_ctx, "Failed to delete user record %s: %s",
488 ldb_dn_linearize(mem_ctx, msgs[0]->dn),
489 ldb_errstring(state->sam_ldb));
490 return NT_STATUS_INTERNAL_DB_CORRUPTION;
496 static NTSTATUS samsync_ldb_handle_group(TALLOC_CTX *mem_ctx,
497 struct samsync_ldb_state *state,
498 enum netr_SamDatabaseID database,
499 struct netr_DELTA_ENUM *delta,
502 uint32_t rid = delta->delta_id_union.rid;
503 struct netr_DELTA_GROUP *group = delta->delta_union.group;
504 const char *container, *obj_class;
507 struct ldb_message *msg;
508 struct ldb_message **msgs;
511 const char *attrs[] = { NULL };
513 msg = ldb_msg_new(mem_ctx);
515 return NT_STATUS_NO_MEMORY;
518 /* search for the group, by rid */
519 ret = gendb_search(state->sam_ldb, mem_ctx, state->base_dn[database], &msgs, attrs,
520 "(&(objectClass=group)(objectSid=%s))",
521 ldap_encode_ndr_dom_sid(mem_ctx, dom_sid_add_rid(mem_ctx, state->dom_sid[database], rid)));
524 *error_string = talloc_asprintf(mem_ctx, "gendb_search failed: %s", ldb_errstring(state->sam_ldb));
525 return NT_STATUS_INTERNAL_DB_CORRUPTION;
526 } else if (ret == 0) {
528 } else if (ret > 1) {
529 *error_string = talloc_asprintf(mem_ctx, "More than one group/alias with SID: %s",
530 dom_sid_string(mem_ctx,
531 dom_sid_add_rid(mem_ctx,
532 state->dom_sid[database],
534 return NT_STATUS_INTERNAL_DB_CORRUPTION;
536 msg->dn = talloc_steal(msg, msgs[0]->dn);
539 cn_name = group->group_name.string;
541 #define ADD_OR_DEL(type, attrib, field) do { \
542 if (group->field) { \
543 samdb_msg_add_ ## type(state->sam_ldb, mem_ctx, msg, \
544 attrib, group->field); \
546 samdb_msg_add_delete(state->sam_ldb, mem_ctx, msg, \
551 ADD_OR_DEL(string, "samAccountName", group_name.string);
553 if (samdb_msg_add_dom_sid(state->sam_ldb, mem_ctx, msg,
554 "objectSid", dom_sid_add_rid(mem_ctx, state->dom_sid[database], rid))) {
555 return NT_STATUS_NO_MEMORY;
558 ADD_OR_DEL(string, "description", description.string);
566 samdb_msg_add_string(state->sam_ldb, mem_ctx, msg,
567 "objectClass", obj_class);
568 msg->dn = ldb_dn_string_compose(mem_ctx, state->base_dn[database],
569 "CN=%s, CN=%s", cn_name, container);
571 return NT_STATUS_NO_MEMORY;
574 ret = samdb_add(state->sam_ldb, mem_ctx, msg);
576 *error_string = talloc_asprintf(mem_ctx, "Failed to create 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;
582 ret = samdb_replace(state->sam_ldb, mem_ctx, msg);
584 *error_string = talloc_asprintf(mem_ctx, "Failed to modify group record %s: %s",
585 ldb_dn_linearize(mem_ctx, msg->dn),
586 ldb_errstring(state->sam_ldb));
587 return NT_STATUS_INTERNAL_DB_CORRUPTION;
594 static NTSTATUS samsync_ldb_delete_group(TALLOC_CTX *mem_ctx,
595 struct samsync_ldb_state *state,
596 enum netr_SamDatabaseID database,
597 struct netr_DELTA_ENUM *delta,
600 uint32_t rid = delta->delta_id_union.rid;
601 struct ldb_message **msgs;
603 const char *attrs[] = { NULL };
605 /* search for the group, by rid */
606 ret = gendb_search(state->sam_ldb, mem_ctx, state->base_dn[database], &msgs, attrs,
607 "(&(objectClass=group)(objectSid=%s))",
608 ldap_encode_ndr_dom_sid(mem_ctx, dom_sid_add_rid(mem_ctx, state->dom_sid[database], rid)));
611 *error_string = talloc_asprintf(mem_ctx, "gendb_search failed: %s", ldb_errstring(state->sam_ldb));
612 return NT_STATUS_INTERNAL_DB_CORRUPTION;
613 } else if (ret == 0) {
614 return NT_STATUS_NO_SUCH_GROUP;
615 } else if (ret > 1) {
616 *error_string = talloc_asprintf(mem_ctx, "More than one group/alias with SID: %s",
617 dom_sid_string(mem_ctx,
618 dom_sid_add_rid(mem_ctx,
619 state->dom_sid[database],
621 return NT_STATUS_INTERNAL_DB_CORRUPTION;
624 ret = samdb_delete(state->sam_ldb, mem_ctx, msgs[0]->dn);
626 *error_string = talloc_asprintf(mem_ctx, "Failed to delete group record %s: %s",
627 ldb_dn_linearize(mem_ctx, msgs[0]->dn),
628 ldb_errstring(state->sam_ldb));
629 return NT_STATUS_INTERNAL_DB_CORRUPTION;
635 static NTSTATUS samsync_ldb_handle_group_member(TALLOC_CTX *mem_ctx,
636 struct samsync_ldb_state *state,
637 enum netr_SamDatabaseID database,
638 struct netr_DELTA_ENUM *delta,
641 uint32_t rid = delta->delta_id_union.rid;
642 struct netr_DELTA_GROUP_MEMBER *group_member = delta->delta_union.group_member;
643 struct ldb_message *msg;
644 struct ldb_message **msgs;
646 const char *attrs[] = { NULL };
649 msg = ldb_msg_new(mem_ctx);
651 return NT_STATUS_NO_MEMORY;
654 /* search for the group, by rid */
655 ret = gendb_search(state->sam_ldb, mem_ctx, state->base_dn[database], &msgs, attrs,
656 "(&(objectClass=group)(objectSid=%s))",
657 ldap_encode_ndr_dom_sid(mem_ctx, dom_sid_add_rid(mem_ctx, state->dom_sid[database], rid)));
660 *error_string = talloc_asprintf(mem_ctx, "gendb_search failed: %s", ldb_errstring(state->sam_ldb));
661 return NT_STATUS_INTERNAL_DB_CORRUPTION;
662 } else if (ret == 0) {
663 return NT_STATUS_NO_SUCH_GROUP;
664 } else if (ret > 1) {
665 *error_string = talloc_asprintf(mem_ctx, "More than one group/alias with SID: %s",
666 dom_sid_string(mem_ctx,
667 dom_sid_add_rid(mem_ctx,
668 state->dom_sid[database],
670 return NT_STATUS_INTERNAL_DB_CORRUPTION;
672 msg->dn = talloc_steal(msg, msgs[0]->dn);
677 for (i=0; i<group_member->num_rids; i++) {
678 /* search for the group, by rid */
679 ret = gendb_search(state->sam_ldb, mem_ctx, state->base_dn[database], &msgs, attrs,
680 "(&(objectClass=user)(objectSid=%s))",
681 ldap_encode_ndr_dom_sid(mem_ctx, dom_sid_add_rid(mem_ctx, state->dom_sid[database], group_member->rids[i])));
684 *error_string = talloc_asprintf(mem_ctx, "gendb_search failed: %s", ldb_errstring(state->sam_ldb));
685 return NT_STATUS_INTERNAL_DB_CORRUPTION;
686 } else if (ret == 0) {
687 return NT_STATUS_NO_SUCH_USER;
688 } else if (ret > 1) {
689 return NT_STATUS_INTERNAL_DB_CORRUPTION;
691 samdb_msg_add_string(state->sam_ldb, mem_ctx, msg, "member", ldb_dn_linearize(mem_ctx, msgs[0]->dn));
697 ret = samdb_replace(state->sam_ldb, mem_ctx, msg);
699 *error_string = talloc_asprintf(mem_ctx, "Failed to modify group record %s: %s",
700 ldb_dn_linearize(mem_ctx, msg->dn),
701 ldb_errstring(state->sam_ldb));
702 return NT_STATUS_INTERNAL_DB_CORRUPTION;
708 static NTSTATUS samsync_ldb_handle_alias(TALLOC_CTX *mem_ctx,
709 struct samsync_ldb_state *state,
710 enum netr_SamDatabaseID database,
711 struct netr_DELTA_ENUM *delta,
714 uint32_t rid = delta->delta_id_union.rid;
715 struct netr_DELTA_ALIAS *alias = delta->delta_union.alias;
716 const char *container, *obj_class;
719 struct ldb_message *msg;
720 struct ldb_message **msgs;
723 const char *attrs[] = { NULL };
725 msg = ldb_msg_new(mem_ctx);
727 return NT_STATUS_NO_MEMORY;
730 /* search for the alias, by rid */
731 ret = gendb_search(state->sam_ldb, mem_ctx, state->base_dn[database], &msgs, attrs,
732 "(&(objectClass=group)(objectSid=%s))",
733 ldap_encode_ndr_dom_sid(mem_ctx, dom_sid_add_rid(mem_ctx, state->dom_sid[database], rid)));
736 *error_string = talloc_asprintf(mem_ctx, "gendb_search failed: %s", ldb_errstring(state->sam_ldb));
737 return NT_STATUS_INTERNAL_DB_CORRUPTION;
738 } else if (ret == 0) {
740 } else if (ret > 1) {
741 *error_string = talloc_asprintf(mem_ctx, "More than one group/alias with SID: %s",
742 dom_sid_string(mem_ctx,
743 dom_sid_add_rid(mem_ctx,
744 state->dom_sid[database],
746 return NT_STATUS_INTERNAL_DB_CORRUPTION;
748 msg->dn = talloc_steal(mem_ctx, msgs[0]->dn);
751 cn_name = alias->alias_name.string;
753 #define ADD_OR_DEL(type, attrib, field) do { \
754 if (alias->field) { \
755 samdb_msg_add_ ## type(state->sam_ldb, mem_ctx, msg, \
756 attrib, alias->field); \
758 samdb_msg_add_delete(state->sam_ldb, mem_ctx, msg, \
763 ADD_OR_DEL(string, "samAccountName", alias_name.string);
765 if (samdb_msg_add_dom_sid(state->sam_ldb, mem_ctx, msg,
766 "objectSid", dom_sid_add_rid(mem_ctx, state->dom_sid[database], rid))) {
767 return NT_STATUS_NO_MEMORY;
770 ADD_OR_DEL(string, "description", description.string);
774 samdb_msg_add_uint(state->sam_ldb, mem_ctx, msg, "groupType", 0x80000004);
780 samdb_msg_add_string(state->sam_ldb, mem_ctx, msg,
781 "objectClass", obj_class);
782 msg->dn = ldb_dn_string_compose(mem_ctx, state->base_dn[database],
783 "CN=%s, CN=%s", cn_name, container);
785 return NT_STATUS_NO_MEMORY;
788 ret = samdb_add(state->sam_ldb, mem_ctx, msg);
790 *error_string = talloc_asprintf(mem_ctx, "Failed to create 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;
796 ret = samdb_replace(state->sam_ldb, mem_ctx, msg);
798 *error_string = talloc_asprintf(mem_ctx, "Failed to modify alias record %s: %s",
799 ldb_dn_linearize(mem_ctx, msg->dn),
800 ldb_errstring(state->sam_ldb));
801 return NT_STATUS_INTERNAL_DB_CORRUPTION;
808 static NTSTATUS samsync_ldb_delete_alias(TALLOC_CTX *mem_ctx,
809 struct samsync_ldb_state *state,
810 enum netr_SamDatabaseID database,
811 struct netr_DELTA_ENUM *delta,
814 uint32_t rid = delta->delta_id_union.rid;
815 struct ldb_message **msgs;
817 const char *attrs[] = { NULL };
819 /* search for the alias, by rid */
820 ret = gendb_search(state->sam_ldb, mem_ctx, state->base_dn[database], &msgs, attrs,
821 "(&(objectClass=group)(objectSid=%s))",
822 ldap_encode_ndr_dom_sid(mem_ctx, dom_sid_add_rid(mem_ctx, state->dom_sid[database], rid)));
825 *error_string = talloc_asprintf(mem_ctx, "gendb_search failed: %s", ldb_errstring(state->sam_ldb));
826 return NT_STATUS_INTERNAL_DB_CORRUPTION;
827 } else if (ret == 0) {
828 return NT_STATUS_NO_SUCH_ALIAS;
829 } else if (ret > 1) {
830 return NT_STATUS_INTERNAL_DB_CORRUPTION;
833 ret = samdb_delete(state->sam_ldb, mem_ctx, msgs[0]->dn);
835 *error_string = talloc_asprintf(mem_ctx, "Failed to delete alias record %s: %s",
836 ldb_dn_linearize(mem_ctx, msgs[0]->dn),
837 ldb_errstring(state->sam_ldb));
838 return NT_STATUS_INTERNAL_DB_CORRUPTION;
844 static NTSTATUS samsync_ldb_handle_alias_member(TALLOC_CTX *mem_ctx,
845 struct samsync_ldb_state *state,
846 enum netr_SamDatabaseID database,
847 struct netr_DELTA_ENUM *delta,
850 uint32_t rid = delta->delta_id_union.rid;
851 struct netr_DELTA_ALIAS_MEMBER *alias_member = delta->delta_union.alias_member;
852 struct ldb_message *msg;
853 struct ldb_message **msgs;
855 const char *attrs[] = { NULL };
858 msg = ldb_msg_new(mem_ctx);
860 return NT_STATUS_NO_MEMORY;
863 /* search for the alias, by rid */
864 ret = gendb_search(state->sam_ldb, mem_ctx, state->base_dn[database], &msgs, attrs,
865 "(&(objectClass=group)(objectSid=%s))",
866 ldap_encode_ndr_dom_sid(mem_ctx, dom_sid_add_rid(mem_ctx, state->dom_sid[database], rid)));
869 *error_string = talloc_asprintf(mem_ctx, "gendb_search failed: %s", ldb_errstring(state->sam_ldb));
870 return NT_STATUS_INTERNAL_DB_CORRUPTION;
871 } else if (ret == 0) {
872 return NT_STATUS_NO_SUCH_GROUP;
873 } else if (ret > 1) {
874 *error_string = talloc_asprintf(mem_ctx, "More than one group/alias with SID: %s",
875 dom_sid_string(mem_ctx,
876 dom_sid_add_rid(mem_ctx,
877 state->dom_sid[database],
879 return NT_STATUS_INTERNAL_DB_CORRUPTION;
881 msg->dn = talloc_steal(msg, msgs[0]->dn);
886 for (i=0; i<alias_member->sids.num_sids; i++) {
887 struct ldb_dn *alias_member_dn;
888 /* search for members, in the top basedn (normal users are builtin aliases) */
889 ret = gendb_search(state->sam_ldb, mem_ctx, state->base_dn[SAM_DATABASE_DOMAIN], &msgs, attrs,
891 ldap_encode_ndr_dom_sid(mem_ctx, alias_member->sids.sids[i].sid));
894 *error_string = talloc_asprintf(mem_ctx, "gendb_search failed: %s", ldb_errstring(state->sam_ldb));
895 return NT_STATUS_INTERNAL_DB_CORRUPTION;
896 } else if (ret == 0) {
898 nt_status = samsync_ldb_add_foreignSecurityPrincipal(mem_ctx, state,
899 alias_member->sids.sids[i].sid,
902 if (!NT_STATUS_IS_OK(nt_status)) {
905 } else if (ret > 1) {
906 return NT_STATUS_INTERNAL_DB_CORRUPTION;
908 alias_member_dn = msgs[0]->dn;
910 samdb_msg_add_string(state->sam_ldb, mem_ctx, msg, "member", ldb_dn_linearize(mem_ctx, alias_member_dn));
915 ret = samdb_replace(state->sam_ldb, mem_ctx, msg);
917 *error_string = talloc_asprintf(mem_ctx, "Failed to modify group record %s: %s",
918 ldb_dn_linearize(mem_ctx, msg->dn),
919 ldb_errstring(state->sam_ldb));
920 return NT_STATUS_INTERNAL_DB_CORRUPTION;
926 static NTSTATUS samsync_ldb_handle_account(TALLOC_CTX *mem_ctx,
927 struct samsync_ldb_state *state,
928 enum netr_SamDatabaseID database,
929 struct netr_DELTA_ENUM *delta,
932 struct dom_sid *sid = delta->delta_id_union.sid;
933 struct netr_DELTA_ACCOUNT *account = delta->delta_union.account;
935 struct ldb_message *msg;
936 struct ldb_message **msgs;
937 struct ldb_dn *privilege_dn;
939 const char *attrs[] = { NULL };
942 msg = ldb_msg_new(mem_ctx);
944 return NT_STATUS_NO_MEMORY;
947 /* search for the account, by sid, in the top basedn */
948 ret = gendb_search(state->sam_ldb, mem_ctx, state->base_dn[SAM_DATABASE_DOMAIN], &msgs, attrs,
949 "(objectSid=%s)", ldap_encode_ndr_dom_sid(mem_ctx, sid));
952 *error_string = talloc_asprintf(mem_ctx, "gendb_search failed: %s", ldb_errstring(state->sam_ldb));
953 return NT_STATUS_INTERNAL_DB_CORRUPTION;
954 } else if (ret == 0) {
956 nt_status = samsync_ldb_add_foreignSecurityPrincipal(mem_ctx, state,
960 privilege_dn = talloc_steal(msg, privilege_dn);
961 if (!NT_STATUS_IS_OK(nt_status)) {
964 } else if (ret > 1) {
965 *error_string = talloc_asprintf(mem_ctx, "More than one account with SID: %s",
966 dom_sid_string(mem_ctx, sid));
967 return NT_STATUS_INTERNAL_DB_CORRUPTION;
969 privilege_dn = talloc_steal(msg, msgs[0]->dn);
972 msg->dn = privilege_dn;
974 for (i=0; i< account->privilege_entries; i++) {
975 samdb_msg_add_string(state->sam_ldb, mem_ctx, msg, "privilege",
976 account->privilege_name[i].string);
979 ret = samdb_replace(state->sam_ldb, mem_ctx, msg);
981 *error_string = talloc_asprintf(mem_ctx, "Failed to modify privilege record %s",
982 ldb_dn_linearize(mem_ctx, msg->dn));
983 return NT_STATUS_INTERNAL_DB_CORRUPTION;
989 static NTSTATUS samsync_ldb_delete_account(TALLOC_CTX *mem_ctx,
990 struct samsync_ldb_state *state,
991 enum netr_SamDatabaseID database,
992 struct netr_DELTA_ENUM *delta,
995 struct dom_sid *sid = delta->delta_id_union.sid;
997 struct ldb_message *msg;
998 struct ldb_message **msgs;
1000 const char *attrs[] = { NULL };
1002 msg = ldb_msg_new(mem_ctx);
1004 return NT_STATUS_NO_MEMORY;
1007 /* search for the account, by sid, in the top basedn */
1008 ret = gendb_search(state->sam_ldb, mem_ctx, state->base_dn[SAM_DATABASE_DOMAIN], &msgs, attrs,
1010 ldap_encode_ndr_dom_sid(mem_ctx, sid));
1013 *error_string = talloc_asprintf(mem_ctx, "gendb_search failed: %s", ldb_errstring(state->sam_ldb));
1014 return NT_STATUS_INTERNAL_DB_CORRUPTION;
1015 } else if (ret == 0) {
1016 return NT_STATUS_NO_SUCH_USER;
1017 } else if (ret > 1) {
1018 *error_string = talloc_asprintf(mem_ctx, "More than one account with SID: %s",
1019 dom_sid_string(mem_ctx, sid));
1020 return NT_STATUS_INTERNAL_DB_CORRUPTION;
1022 msg->dn = talloc_steal(msg, msgs[0]->dn);
1025 samdb_msg_add_delete(state->sam_ldb, mem_ctx, msg,
1028 ret = samdb_replace(state->sam_ldb, mem_ctx, msg);
1030 *error_string = talloc_asprintf(mem_ctx, "Failed to modify privilege record %s",
1031 ldb_dn_linearize(mem_ctx, msg->dn));
1032 return NT_STATUS_INTERNAL_DB_CORRUPTION;
1035 return NT_STATUS_OK;
1038 static NTSTATUS libnet_samsync_ldb_fn(TALLOC_CTX *mem_ctx,
1040 enum netr_SamDatabaseID database,
1041 struct netr_DELTA_ENUM *delta,
1042 char **error_string)
1044 NTSTATUS nt_status = NT_STATUS_OK;
1045 struct samsync_ldb_state *state = talloc_get_type(private, struct samsync_ldb_state);
1047 *error_string = NULL;
1048 switch (delta->delta_type) {
1049 case NETR_DELTA_DOMAIN:
1051 nt_status = samsync_ldb_handle_domain(mem_ctx,
1058 case NETR_DELTA_USER:
1060 nt_status = samsync_ldb_handle_user(mem_ctx,
1067 case NETR_DELTA_DELETE_USER:
1069 nt_status = samsync_ldb_delete_user(mem_ctx,
1076 case NETR_DELTA_GROUP:
1078 nt_status = samsync_ldb_handle_group(mem_ctx,
1085 case NETR_DELTA_DELETE_GROUP:
1087 nt_status = samsync_ldb_delete_group(mem_ctx,
1094 case NETR_DELTA_GROUP_MEMBER:
1096 nt_status = samsync_ldb_handle_group_member(mem_ctx,
1103 case NETR_DELTA_ALIAS:
1105 nt_status = samsync_ldb_handle_alias(mem_ctx,
1112 case NETR_DELTA_DELETE_ALIAS:
1114 nt_status = samsync_ldb_delete_alias(mem_ctx,
1121 case NETR_DELTA_ALIAS_MEMBER:
1123 nt_status = samsync_ldb_handle_alias_member(mem_ctx,
1130 case NETR_DELTA_ACCOUNT:
1132 nt_status = samsync_ldb_handle_account(mem_ctx,
1139 case NETR_DELTA_DELETE_ACCOUNT:
1141 nt_status = samsync_ldb_delete_account(mem_ctx,
1149 /* Can't dump them all right now */
1152 if (!NT_STATUS_IS_OK(nt_status) && !*error_string) {
1153 *error_string = talloc_asprintf(mem_ctx, "Failed to handle samsync delta: %s", nt_errstr(nt_status));
1158 static NTSTATUS libnet_samsync_ldb_init(TALLOC_CTX *mem_ctx,
1160 struct libnet_SamSync_state *samsync_state,
1161 char **error_string)
1163 struct samsync_ldb_state *state = talloc_get_type(private, struct samsync_ldb_state);
1164 const char *server = dcerpc_server_name(samsync_state->netlogon_pipe);
1167 state->samsync_state = samsync_state;
1169 ZERO_STRUCT(state->dom_sid);
1170 if (state->samsync_state->domain_sid) {
1171 state->dom_sid[SAM_DATABASE_DOMAIN] = dom_sid_dup(state, state->samsync_state->domain_sid);
1174 state->dom_sid[SAM_DATABASE_BUILTIN] = dom_sid_parse_talloc(state, SID_BUILTIN);
1176 if (state->samsync_state->realm) {
1177 if (!server || !*server) {
1178 /* huh? how do we not have a server name? */
1179 *error_string = talloc_strdup(mem_ctx, "No DCE/RPC server name available. How did we connect?");
1180 return NT_STATUS_INVALID_PARAMETER;
1182 ldap_url = talloc_asprintf(state, "ldap://%s", server);
1184 state->remote_ldb = ldb_wrap_connect(mem_ctx, ldap_url,
1185 NULL, state->samsync_state->machine_net_ctx->cred,
1187 if (!state->remote_ldb) {
1188 *error_string = talloc_asprintf(mem_ctx, "Failed to connect to remote LDAP server at %s (used to extract additional data in SamSync replication)", ldap_url);
1189 return NT_STATUS_NO_LOGON_SERVERS;
1192 state->remote_ldb = NULL;
1194 return NT_STATUS_OK;
1197 NTSTATUS libnet_samsync_ldb(struct libnet_context *ctx, TALLOC_CTX *mem_ctx, struct libnet_samsync_ldb *r)
1200 struct libnet_SamSync r2;
1201 struct samsync_ldb_state *state = talloc(mem_ctx, struct samsync_ldb_state);
1204 return NT_STATUS_NO_MEMORY;
1207 state->secrets = NULL;
1208 state->trusted_domains = NULL;
1210 state->sam_ldb = ldb_wrap_connect(mem_ctx, lp_sam_url(), r->in.session_info,
1211 ctx->cred, 0, NULL);
1213 r2.out.error_string = NULL;
1214 r2.in.binding_string = r->in.binding_string;
1215 r2.in.init_fn = libnet_samsync_ldb_init;
1216 r2.in.delta_fn = libnet_samsync_ldb_fn;
1217 r2.in.fn_ctx = state;
1218 r2.in.machine_account = NULL; /* TODO: Create a machine account, fill this in, and the delete it */
1219 nt_status = libnet_SamSync_netlogon(ctx, state, &r2);
1220 r->out.error_string = r2.out.error_string;
1221 talloc_steal(mem_ctx, r->out.error_string);
1223 if (!NT_STATUS_IS_OK(nt_status)) {