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/gen_ndr/ndr_security.h"
35 #include "librpc/rpc/dcerpc.h"
37 struct samsync_ldb_secret {
38 struct samsync_ldb_secret *prev, *next;
44 struct samsync_ldb_trusted_domain {
45 struct samsync_ldb_trusted_domain *prev, *next;
50 struct samsync_ldb_state {
51 /* Values from the LSA lookup */
52 const struct libnet_SamSync_state *samsync_state;
54 struct dom_sid *dom_sid[3];
55 struct ldb_context *sam_ldb, *remote_ldb;
56 struct ldb_dn *base_dn[3];
57 struct samsync_ldb_secret *secrets;
58 struct samsync_ldb_trusted_domain *trusted_domains;
61 static NTSTATUS samsync_ldb_add_foreignSecurityPrincipal(TALLOC_CTX *mem_ctx,
62 struct samsync_ldb_state *state,
64 struct ldb_dn **fsp_dn,
67 const char *sidstr = dom_sid_string(mem_ctx, sid);
68 /* We assume that ForeignSecurityPrincipals are under the BASEDN of the main domain */
69 struct ldb_dn *basedn = samdb_search_dn(state->sam_ldb, mem_ctx,
70 state->base_dn[SAM_DATABASE_DOMAIN],
71 "(&(objectClass=container)(cn=ForeignSecurityPrincipals))");
72 struct ldb_message *msg;
76 return NT_STATUS_NO_MEMORY;
80 *error_string = talloc_asprintf(mem_ctx,
81 "Failed to find DN for "
82 "ForeignSecurityPrincipal container under %s",
83 ldb_dn_linearize(mem_ctx, state->base_dn[SAM_DATABASE_DOMAIN]));
84 return NT_STATUS_INTERNAL_DB_CORRUPTION;
87 msg = ldb_msg_new(mem_ctx);
89 return NT_STATUS_NO_MEMORY;
92 /* add core elements to the ldb_message for the alias */
93 msg->dn = ldb_dn_build_child(mem_ctx, "CN", sidstr, basedn);
95 return NT_STATUS_NO_MEMORY;
97 samdb_msg_add_string(state->sam_ldb, mem_ctx, msg,
99 "foreignSecurityPrincipal");
103 /* create the alias */
104 ret = samdb_add(state->sam_ldb, mem_ctx, msg);
106 *error_string = talloc_asprintf(mem_ctx, "Failed to create foreignSecurityPrincipal "
108 ldb_dn_linearize(mem_ctx, msg->dn),
109 ldb_errstring(state->sam_ldb));
110 return NT_STATUS_INTERNAL_DB_CORRUPTION;
115 static NTSTATUS samsync_ldb_handle_domain(TALLOC_CTX *mem_ctx,
116 struct samsync_ldb_state *state,
117 enum netr_SamDatabaseID database,
118 struct netr_DELTA_ENUM *delta,
121 struct netr_DELTA_DOMAIN *domain = delta->delta_union.domain;
122 const char *domain_name = domain->domain_name.string;
123 struct ldb_message *msg;
126 msg = ldb_msg_new(mem_ctx);
128 return NT_STATUS_NO_MEMORY;
131 if (database == SAM_DATABASE_DOMAIN) {
132 const char *domain_attrs[] = {"nETBIOSName", "nCName", NULL};
133 struct ldb_message **msgs_domain;
136 ret_domain = gendb_search(state->sam_ldb, mem_ctx, NULL, &msgs_domain, domain_attrs,
137 "(&(&(nETBIOSName=%s)(objectclass=crossRef))(ncName=*))",
139 if (ret_domain == -1) {
140 *error_string = talloc_asprintf(mem_ctx, "gendb_search for domain failed: %s", ldb_errstring(state->sam_ldb));
141 return NT_STATUS_INTERNAL_DB_CORRUPTION;
144 if (ret_domain != 1) {
145 *error_string = talloc_asprintf(mem_ctx, "Failed to find existing domain record for %s: %d results", domain_name,
147 return NT_STATUS_NO_SUCH_DOMAIN;
150 state->base_dn[database] = samdb_result_dn(state, msgs_domain[0], "nCName", NULL);
152 if (state->dom_sid[database]) {
153 /* Update the domain sid with the incoming
154 * domain (found on LSA pipe, database sid may
156 samdb_msg_add_dom_sid(state->sam_ldb, mem_ctx,
157 msg, "objectSid", state->dom_sid[database]);
159 /* Well, we will have to use the one from the database */
160 state->dom_sid[database] = samdb_search_dom_sid(state->sam_ldb, state,
161 state->base_dn[database],
165 if (state->samsync_state->domain_guid) {
168 nt_status = ndr_push_struct_blob(&v, msg, state->samsync_state->domain_guid,
169 (ndr_push_flags_fn_t)ndr_push_GUID);
170 if (!NT_STATUS_IS_OK(nt_status)) {
171 *error_string = talloc_asprintf(mem_ctx, "ndr_push of domain GUID failed!");
175 ldb_msg_add_value(msg, "objectGUID", &v);
177 } else if (database == SAM_DATABASE_BUILTIN) {
178 /* work out the builtin_dn - useful for so many calls its worth
180 const char *dnstring = samdb_search_string(state->sam_ldb, mem_ctx, NULL,
181 "distinguishedName", "objectClass=builtinDomain");
182 state->base_dn[database] = ldb_dn_explode(state, dnstring);
185 return NT_STATUS_INVALID_PARAMETER;
188 msg->dn = talloc_reference(mem_ctx, state->base_dn[database]);
190 return NT_STATUS_NO_MEMORY;
193 samdb_msg_add_string(state->sam_ldb, mem_ctx,
194 msg, "oEMInformation", domain->comment.string);
196 samdb_msg_add_int64(state->sam_ldb, mem_ctx,
197 msg, "forceLogoff", domain->force_logoff_time);
199 samdb_msg_add_uint(state->sam_ldb, mem_ctx,
200 msg, "minPwdLen", domain->min_password_length);
202 samdb_msg_add_int64(state->sam_ldb, mem_ctx,
203 msg, "maxPwdAge", domain->max_password_age);
205 samdb_msg_add_int64(state->sam_ldb, mem_ctx,
206 msg, "minPwdAge", domain->min_password_age);
208 samdb_msg_add_uint(state->sam_ldb, mem_ctx,
209 msg, "pwdHistoryLength", domain->password_history_length);
211 samdb_msg_add_uint64(state->sam_ldb, mem_ctx,
212 msg, "modifiedCount",
213 domain->sequence_num);
215 samdb_msg_add_uint64(state->sam_ldb, mem_ctx,
216 msg, "creationTime", domain->domain_create_time);
218 /* TODO: Account lockout, password properties */
220 ret = samdb_replace(state->sam_ldb, mem_ctx, msg);
223 return NT_STATUS_INTERNAL_ERROR;
228 static NTSTATUS samsync_ldb_handle_user(TALLOC_CTX *mem_ctx,
229 struct samsync_ldb_state *state,
230 enum netr_SamDatabaseID database,
231 struct netr_DELTA_ENUM *delta,
234 uint32_t rid = delta->delta_id_union.rid;
235 struct netr_DELTA_USER *user = delta->delta_union.user;
236 const char *container, *obj_class;
239 const struct dom_sid *user_sid;
240 struct ldb_message *msg;
241 struct ldb_message **msgs;
242 struct ldb_message **remote_msgs;
246 const char *attrs[] = { NULL };
247 /* we may change this to a global search, then fill in only the things not in ldap later */
248 const char *remote_attrs[] = { "userPrincipalName", "servicePrincipalName",
249 "msDS-KeyVersionNumber", "objectGUID", NULL};
251 user_sid = dom_sid_add_rid(mem_ctx, state->dom_sid[database], rid);
253 return NT_STATUS_NO_MEMORY;
256 msg = ldb_msg_new(mem_ctx);
258 return NT_STATUS_NO_MEMORY;
262 /* search for the user, by rid */
263 ret = gendb_search(state->sam_ldb, mem_ctx, state->base_dn[database],
264 &msgs, attrs, "(&(objectClass=user)(objectSid=%s))",
265 ldap_encode_ndr_dom_sid(mem_ctx, user_sid));
268 *error_string = talloc_asprintf(mem_ctx, "LDB for user %s failed: %s",
269 dom_sid_string(mem_ctx, user_sid),
270 ldb_errstring(state->sam_ldb));
271 return NT_STATUS_INTERNAL_DB_CORRUPTION;
272 } else if (ret == 0) {
274 } else if (ret > 1) {
275 *error_string = talloc_asprintf(mem_ctx, "More than one user with SID: %s in local LDB",
276 dom_sid_string(mem_ctx, user_sid));
277 return NT_STATUS_INTERNAL_DB_CORRUPTION;
279 msg->dn = msgs[0]->dn;
280 talloc_steal(msg, msgs[0]->dn);
283 /* and do the same on the remote database */
284 if (state->remote_ldb) {
285 ret = gendb_search(state->remote_ldb, mem_ctx, state->base_dn[database],
286 &remote_msgs, remote_attrs, "(&(objectClass=user)(objectSid=%s))",
287 ldap_encode_ndr_dom_sid(mem_ctx, user_sid));
290 *error_string = talloc_asprintf(mem_ctx, "remote LDAP for user %s failed: %s",
291 dom_sid_string(mem_ctx, user_sid),
292 ldb_errstring(state->remote_ldb));
293 return NT_STATUS_INTERNAL_DB_CORRUPTION;
294 } else if (ret == 0) {
295 *error_string = talloc_asprintf(mem_ctx, "User exists in samsync but not in remote LDAP domain! (base: %s, SID: %s)",
296 ldb_dn_linearize(mem_ctx, state->base_dn[database]),
297 dom_sid_string(mem_ctx, user_sid));
298 return NT_STATUS_NO_SUCH_USER;
299 } else if (ret > 1) {
300 *error_string = talloc_asprintf(mem_ctx, "More than one user in remote LDAP domain with SID: %s",
301 dom_sid_string(mem_ctx, user_sid));
302 return NT_STATUS_INTERNAL_DB_CORRUPTION;
304 /* Try to put things in the same location as the remote server */
306 msg->dn = remote_msgs[0]->dn;
307 talloc_steal(msg, remote_msgs[0]->dn);
311 cn_name = talloc_strdup(mem_ctx, user->account_name.string);
312 NT_STATUS_HAVE_NO_MEMORY(cn_name);
313 cn_name_len = strlen(cn_name);
315 #define ADD_OR_DEL(type, attrib, field) do { \
317 samdb_msg_add_ ## type(state->sam_ldb, mem_ctx, msg, \
318 attrib, user->field); \
320 samdb_msg_add_delete(state->sam_ldb, mem_ctx, msg, \
325 ADD_OR_DEL(string, "samAccountName", account_name.string);
326 ADD_OR_DEL(string, "displayName", full_name.string);
328 if (samdb_msg_add_dom_sid(state->sam_ldb, mem_ctx, msg,
329 "objectSid", dom_sid_add_rid(mem_ctx, state->dom_sid[database], rid))) {
330 return NT_STATUS_NO_MEMORY;
333 ADD_OR_DEL(uint, "primaryGroupID", primary_gid);
334 ADD_OR_DEL(string, "homeDirectory", home_directory.string);
335 ADD_OR_DEL(string, "homeDrive", home_drive.string);
336 ADD_OR_DEL(string, "scriptPath", logon_script.string);
337 ADD_OR_DEL(string, "description", description.string);
338 ADD_OR_DEL(string, "userWorkstations", workstations.string);
340 ADD_OR_DEL(uint64, "lastLogon", last_logon);
341 ADD_OR_DEL(uint64, "lastLogoff", last_logoff);
343 if (samdb_msg_add_logon_hours(state->sam_ldb, mem_ctx, msg, "logonHours", &user->logon_hours) != 0) {
344 return NT_STATUS_NO_MEMORY;
347 ADD_OR_DEL(uint, "badPwdCount", bad_password_count);
348 ADD_OR_DEL(uint, "logonCount", logon_count);
350 ADD_OR_DEL(uint64, "pwdLastSet", last_password_change);
351 ADD_OR_DEL(uint64, "accountExpires", acct_expiry);
353 if (samdb_msg_add_acct_flags(state->sam_ldb, mem_ctx, msg,
354 "userAccountControl", user->acct_flags) != 0) {
355 return NT_STATUS_NO_MEMORY;
358 /* Passwords. Ensure there is no plaintext stored against
359 * this entry, as we only have hashes */
360 samdb_msg_add_delete(state->sam_ldb, mem_ctx, msg,
362 if (user->lm_password_present) {
363 samdb_msg_add_hash(state->sam_ldb, mem_ctx, msg,
364 "lmPwdHash", &user->lmpassword);
366 samdb_msg_add_delete(state->sam_ldb, mem_ctx, msg,
369 if (user->nt_password_present) {
370 samdb_msg_add_hash(state->sam_ldb, mem_ctx, msg,
371 "ntPwdHash", &user->ntpassword);
373 samdb_msg_add_delete(state->sam_ldb, mem_ctx, msg,
377 ADD_OR_DEL(string, "comment", comment.string);
378 ADD_OR_DEL(string, "userParameters", parameters.string);
379 ADD_OR_DEL(uint, "countryCode", country_code);
380 ADD_OR_DEL(uint, "codePage", code_page);
382 ADD_OR_DEL(string, "profilePath", profile_path.string);
386 for (i=0; remote_attrs[i]; i++) {
387 struct ldb_message_element *el = ldb_msg_find_element(remote_msgs[0], remote_attrs[i]);
389 samdb_msg_add_delete(state->sam_ldb, mem_ctx, msg,
392 ldb_msg_add(msg, el, LDB_FLAG_MOD_REPLACE);
396 acb = user->acct_flags;
397 if (acb & (ACB_WSTRUST)) {
398 cn_name[cn_name_len - 1] = '\0';
399 container = "Computers";
400 obj_class = "computer";
402 } else if (acb & ACB_SVRTRUST) {
403 if (cn_name[cn_name_len - 1] != '$') {
404 return NT_STATUS_FOOBAR;
406 cn_name[cn_name_len - 1] = '\0';
407 container = "Domain Controllers";
408 obj_class = "computer";
414 samdb_msg_add_string(state->sam_ldb, mem_ctx, msg,
415 "objectClass", obj_class);
417 msg->dn = ldb_dn_string_compose(mem_ctx, state->base_dn[database],
418 "CN=%s, CN=%s", cn_name, container);
420 return NT_STATUS_NO_MEMORY;
424 ret = samdb_add(state->sam_ldb, mem_ctx, msg);
426 struct ldb_dn *first_try_dn = msg->dn;
427 /* Try again with the default DN */
428 msg->dn = talloc_steal(msg, msgs[0]->dn);
429 ret = samdb_add(state->sam_ldb, mem_ctx, msg);
431 *error_string = talloc_asprintf(mem_ctx, "Failed to create user record. Tried both %s and %s: %s",
432 ldb_dn_linearize(mem_ctx, first_try_dn),
433 ldb_dn_linearize(mem_ctx, msg->dn),
434 ldb_errstring(state->sam_ldb));
435 return NT_STATUS_INTERNAL_DB_CORRUPTION;
439 ret = samdb_replace(state->sam_ldb, mem_ctx, msg);
441 *error_string = talloc_asprintf(mem_ctx, "Failed to modify user record %s: %s",
442 ldb_dn_linearize(mem_ctx, msg->dn),
443 ldb_errstring(state->sam_ldb));
444 return NT_STATUS_INTERNAL_DB_CORRUPTION;
451 static NTSTATUS samsync_ldb_delete_user(TALLOC_CTX *mem_ctx,
452 struct samsync_ldb_state *state,
453 enum netr_SamDatabaseID database,
454 struct netr_DELTA_ENUM *delta,
457 uint32_t rid = delta->delta_id_union.rid;
458 struct ldb_message **msgs;
460 const char *attrs[] = { NULL };
462 /* search for the user, by rid */
463 ret = gendb_search(state->sam_ldb, mem_ctx, state->base_dn[database],
464 &msgs, attrs, "(&(objectClass=user)(objectSid=%s))",
465 ldap_encode_ndr_dom_sid(mem_ctx, dom_sid_add_rid(mem_ctx, state->dom_sid[database], rid)));
468 *error_string = talloc_asprintf(mem_ctx, "gendb_search failed: %s", ldb_errstring(state->sam_ldb));
469 return NT_STATUS_INTERNAL_DB_CORRUPTION;
470 } else if (ret == 0) {
471 return NT_STATUS_NO_SUCH_USER;
472 } else if (ret > 1) {
473 *error_string = talloc_asprintf(mem_ctx, "More than one user with SID: %s",
474 dom_sid_string(mem_ctx,
475 dom_sid_add_rid(mem_ctx,
476 state->dom_sid[database],
478 return NT_STATUS_INTERNAL_DB_CORRUPTION;
481 ret = samdb_delete(state->sam_ldb, mem_ctx, msgs[0]->dn);
483 *error_string = talloc_asprintf(mem_ctx, "Failed to delete user record %s: %s",
484 ldb_dn_linearize(mem_ctx, msgs[0]->dn),
485 ldb_errstring(state->sam_ldb));
486 return NT_STATUS_INTERNAL_DB_CORRUPTION;
492 static NTSTATUS samsync_ldb_handle_group(TALLOC_CTX *mem_ctx,
493 struct samsync_ldb_state *state,
494 enum netr_SamDatabaseID database,
495 struct netr_DELTA_ENUM *delta,
498 uint32_t rid = delta->delta_id_union.rid;
499 struct netr_DELTA_GROUP *group = delta->delta_union.group;
500 const char *container, *obj_class;
503 struct ldb_message *msg;
504 struct ldb_message **msgs;
507 const char *attrs[] = { NULL };
509 msg = ldb_msg_new(mem_ctx);
511 return NT_STATUS_NO_MEMORY;
514 /* search for the group, by rid */
515 ret = gendb_search(state->sam_ldb, mem_ctx, state->base_dn[database], &msgs, attrs,
516 "(&(objectClass=group)(objectSid=%s))",
517 ldap_encode_ndr_dom_sid(mem_ctx, dom_sid_add_rid(mem_ctx, state->dom_sid[database], rid)));
520 *error_string = talloc_asprintf(mem_ctx, "gendb_search failed: %s", ldb_errstring(state->sam_ldb));
521 return NT_STATUS_INTERNAL_DB_CORRUPTION;
522 } else if (ret == 0) {
524 } else if (ret > 1) {
525 *error_string = talloc_asprintf(mem_ctx, "More than one group/alias with SID: %s",
526 dom_sid_string(mem_ctx,
527 dom_sid_add_rid(mem_ctx,
528 state->dom_sid[database],
530 return NT_STATUS_INTERNAL_DB_CORRUPTION;
532 msg->dn = talloc_steal(msg, msgs[0]->dn);
535 cn_name = group->group_name.string;
537 #define ADD_OR_DEL(type, attrib, field) do { \
538 if (group->field) { \
539 samdb_msg_add_ ## type(state->sam_ldb, mem_ctx, msg, \
540 attrib, group->field); \
542 samdb_msg_add_delete(state->sam_ldb, mem_ctx, msg, \
547 ADD_OR_DEL(string, "samAccountName", group_name.string);
549 if (samdb_msg_add_dom_sid(state->sam_ldb, mem_ctx, msg,
550 "objectSid", dom_sid_add_rid(mem_ctx, state->dom_sid[database], rid))) {
551 return NT_STATUS_NO_MEMORY;
554 ADD_OR_DEL(string, "description", description.string);
562 samdb_msg_add_string(state->sam_ldb, mem_ctx, msg,
563 "objectClass", obj_class);
564 msg->dn = ldb_dn_string_compose(mem_ctx, state->base_dn[database],
565 "CN=%s, CN=%s", cn_name, container);
567 return NT_STATUS_NO_MEMORY;
570 ret = samdb_add(state->sam_ldb, mem_ctx, msg);
572 *error_string = talloc_asprintf(mem_ctx, "Failed to create group record %s: %s",
573 ldb_dn_linearize(mem_ctx, msg->dn),
574 ldb_errstring(state->sam_ldb));
575 return NT_STATUS_INTERNAL_DB_CORRUPTION;
578 ret = samdb_replace(state->sam_ldb, mem_ctx, msg);
580 *error_string = talloc_asprintf(mem_ctx, "Failed to modify group record %s: %s",
581 ldb_dn_linearize(mem_ctx, msg->dn),
582 ldb_errstring(state->sam_ldb));
583 return NT_STATUS_INTERNAL_DB_CORRUPTION;
590 static NTSTATUS samsync_ldb_delete_group(TALLOC_CTX *mem_ctx,
591 struct samsync_ldb_state *state,
592 enum netr_SamDatabaseID database,
593 struct netr_DELTA_ENUM *delta,
596 uint32_t rid = delta->delta_id_union.rid;
597 struct ldb_message **msgs;
599 const char *attrs[] = { NULL };
601 /* search for the group, by rid */
602 ret = gendb_search(state->sam_ldb, mem_ctx, state->base_dn[database], &msgs, attrs,
603 "(&(objectClass=group)(objectSid=%s))",
604 ldap_encode_ndr_dom_sid(mem_ctx, dom_sid_add_rid(mem_ctx, state->dom_sid[database], rid)));
607 *error_string = talloc_asprintf(mem_ctx, "gendb_search failed: %s", ldb_errstring(state->sam_ldb));
608 return NT_STATUS_INTERNAL_DB_CORRUPTION;
609 } else if (ret == 0) {
610 return NT_STATUS_NO_SUCH_GROUP;
611 } else if (ret > 1) {
612 *error_string = talloc_asprintf(mem_ctx, "More than one group/alias with SID: %s",
613 dom_sid_string(mem_ctx,
614 dom_sid_add_rid(mem_ctx,
615 state->dom_sid[database],
617 return NT_STATUS_INTERNAL_DB_CORRUPTION;
620 ret = samdb_delete(state->sam_ldb, mem_ctx, msgs[0]->dn);
622 *error_string = talloc_asprintf(mem_ctx, "Failed to delete group record %s: %s",
623 ldb_dn_linearize(mem_ctx, msgs[0]->dn),
624 ldb_errstring(state->sam_ldb));
625 return NT_STATUS_INTERNAL_DB_CORRUPTION;
631 static NTSTATUS samsync_ldb_handle_group_member(TALLOC_CTX *mem_ctx,
632 struct samsync_ldb_state *state,
633 enum netr_SamDatabaseID database,
634 struct netr_DELTA_ENUM *delta,
637 uint32_t rid = delta->delta_id_union.rid;
638 struct netr_DELTA_GROUP_MEMBER *group_member = delta->delta_union.group_member;
639 struct ldb_message *msg;
640 struct ldb_message **msgs;
642 const char *attrs[] = { NULL };
645 msg = ldb_msg_new(mem_ctx);
647 return NT_STATUS_NO_MEMORY;
650 /* search for the group, by rid */
651 ret = gendb_search(state->sam_ldb, mem_ctx, state->base_dn[database], &msgs, attrs,
652 "(&(objectClass=group)(objectSid=%s))",
653 ldap_encode_ndr_dom_sid(mem_ctx, dom_sid_add_rid(mem_ctx, state->dom_sid[database], rid)));
656 *error_string = talloc_asprintf(mem_ctx, "gendb_search failed: %s", ldb_errstring(state->sam_ldb));
657 return NT_STATUS_INTERNAL_DB_CORRUPTION;
658 } else if (ret == 0) {
659 return NT_STATUS_NO_SUCH_GROUP;
660 } else if (ret > 1) {
661 *error_string = talloc_asprintf(mem_ctx, "More than one group/alias with SID: %s",
662 dom_sid_string(mem_ctx,
663 dom_sid_add_rid(mem_ctx,
664 state->dom_sid[database],
666 return NT_STATUS_INTERNAL_DB_CORRUPTION;
668 msg->dn = talloc_steal(msg, msgs[0]->dn);
673 for (i=0; i<group_member->num_rids; i++) {
674 /* search for the group, by rid */
675 ret = gendb_search(state->sam_ldb, mem_ctx, state->base_dn[database], &msgs, attrs,
676 "(&(objectClass=user)(objectSid=%s))",
677 ldap_encode_ndr_dom_sid(mem_ctx, dom_sid_add_rid(mem_ctx, state->dom_sid[database], group_member->rids[i])));
680 *error_string = talloc_asprintf(mem_ctx, "gendb_search failed: %s", ldb_errstring(state->sam_ldb));
681 return NT_STATUS_INTERNAL_DB_CORRUPTION;
682 } else if (ret == 0) {
683 return NT_STATUS_NO_SUCH_USER;
684 } else if (ret > 1) {
685 return NT_STATUS_INTERNAL_DB_CORRUPTION;
687 samdb_msg_add_string(state->sam_ldb, mem_ctx, msg, "member", ldb_dn_linearize(mem_ctx, msgs[0]->dn));
693 ret = samdb_replace(state->sam_ldb, mem_ctx, msg);
695 *error_string = talloc_asprintf(mem_ctx, "Failed to modify group record %s: %s",
696 ldb_dn_linearize(mem_ctx, msg->dn),
697 ldb_errstring(state->sam_ldb));
698 return NT_STATUS_INTERNAL_DB_CORRUPTION;
704 static NTSTATUS samsync_ldb_handle_alias(TALLOC_CTX *mem_ctx,
705 struct samsync_ldb_state *state,
706 enum netr_SamDatabaseID database,
707 struct netr_DELTA_ENUM *delta,
710 uint32_t rid = delta->delta_id_union.rid;
711 struct netr_DELTA_ALIAS *alias = delta->delta_union.alias;
712 const char *container, *obj_class;
715 struct ldb_message *msg;
716 struct ldb_message **msgs;
719 const char *attrs[] = { NULL };
721 msg = ldb_msg_new(mem_ctx);
723 return NT_STATUS_NO_MEMORY;
726 /* search for the alias, by rid */
727 ret = gendb_search(state->sam_ldb, mem_ctx, state->base_dn[database], &msgs, attrs,
728 "(&(objectClass=group)(objectSid=%s))",
729 ldap_encode_ndr_dom_sid(mem_ctx, dom_sid_add_rid(mem_ctx, state->dom_sid[database], rid)));
732 *error_string = talloc_asprintf(mem_ctx, "gendb_search failed: %s", ldb_errstring(state->sam_ldb));
733 return NT_STATUS_INTERNAL_DB_CORRUPTION;
734 } else if (ret == 0) {
736 } else if (ret > 1) {
737 *error_string = talloc_asprintf(mem_ctx, "More than one group/alias with SID: %s",
738 dom_sid_string(mem_ctx,
739 dom_sid_add_rid(mem_ctx,
740 state->dom_sid[database],
742 return NT_STATUS_INTERNAL_DB_CORRUPTION;
744 msg->dn = talloc_steal(mem_ctx, msgs[0]->dn);
747 cn_name = alias->alias_name.string;
749 #define ADD_OR_DEL(type, attrib, field) do { \
750 if (alias->field) { \
751 samdb_msg_add_ ## type(state->sam_ldb, mem_ctx, msg, \
752 attrib, alias->field); \
754 samdb_msg_add_delete(state->sam_ldb, mem_ctx, msg, \
759 ADD_OR_DEL(string, "samAccountName", alias_name.string);
761 if (samdb_msg_add_dom_sid(state->sam_ldb, mem_ctx, msg,
762 "objectSid", dom_sid_add_rid(mem_ctx, state->dom_sid[database], rid))) {
763 return NT_STATUS_NO_MEMORY;
766 ADD_OR_DEL(string, "description", description.string);
770 samdb_msg_add_uint(state->sam_ldb, mem_ctx, msg, "groupType", 0x80000004);
776 samdb_msg_add_string(state->sam_ldb, mem_ctx, msg,
777 "objectClass", obj_class);
778 msg->dn = ldb_dn_string_compose(mem_ctx, state->base_dn[database],
779 "CN=%s, CN=%s", cn_name, container);
781 return NT_STATUS_NO_MEMORY;
784 ret = samdb_add(state->sam_ldb, mem_ctx, msg);
786 *error_string = talloc_asprintf(mem_ctx, "Failed to create alias record %s: %s",
787 ldb_dn_linearize(mem_ctx, msg->dn),
788 ldb_errstring(state->sam_ldb));
789 return NT_STATUS_INTERNAL_DB_CORRUPTION;
792 ret = samdb_replace(state->sam_ldb, mem_ctx, msg);
794 *error_string = talloc_asprintf(mem_ctx, "Failed to modify alias record %s: %s",
795 ldb_dn_linearize(mem_ctx, msg->dn),
796 ldb_errstring(state->sam_ldb));
797 return NT_STATUS_INTERNAL_DB_CORRUPTION;
804 static NTSTATUS samsync_ldb_delete_alias(TALLOC_CTX *mem_ctx,
805 struct samsync_ldb_state *state,
806 enum netr_SamDatabaseID database,
807 struct netr_DELTA_ENUM *delta,
810 uint32_t rid = delta->delta_id_union.rid;
811 struct ldb_message **msgs;
813 const char *attrs[] = { NULL };
815 /* search for the alias, by rid */
816 ret = gendb_search(state->sam_ldb, mem_ctx, state->base_dn[database], &msgs, attrs,
817 "(&(objectClass=group)(objectSid=%s))",
818 ldap_encode_ndr_dom_sid(mem_ctx, dom_sid_add_rid(mem_ctx, state->dom_sid[database], rid)));
821 *error_string = talloc_asprintf(mem_ctx, "gendb_search failed: %s", ldb_errstring(state->sam_ldb));
822 return NT_STATUS_INTERNAL_DB_CORRUPTION;
823 } else if (ret == 0) {
824 return NT_STATUS_NO_SUCH_ALIAS;
825 } else if (ret > 1) {
826 return NT_STATUS_INTERNAL_DB_CORRUPTION;
829 ret = samdb_delete(state->sam_ldb, mem_ctx, msgs[0]->dn);
831 *error_string = talloc_asprintf(mem_ctx, "Failed to delete alias record %s: %s",
832 ldb_dn_linearize(mem_ctx, msgs[0]->dn),
833 ldb_errstring(state->sam_ldb));
834 return NT_STATUS_INTERNAL_DB_CORRUPTION;
840 static NTSTATUS samsync_ldb_handle_alias_member(TALLOC_CTX *mem_ctx,
841 struct samsync_ldb_state *state,
842 enum netr_SamDatabaseID database,
843 struct netr_DELTA_ENUM *delta,
846 uint32_t rid = delta->delta_id_union.rid;
847 struct netr_DELTA_ALIAS_MEMBER *alias_member = delta->delta_union.alias_member;
848 struct ldb_message *msg;
849 struct ldb_message **msgs;
851 const char *attrs[] = { NULL };
854 msg = ldb_msg_new(mem_ctx);
856 return NT_STATUS_NO_MEMORY;
859 /* search for the alias, by rid */
860 ret = gendb_search(state->sam_ldb, mem_ctx, state->base_dn[database], &msgs, attrs,
861 "(&(objectClass=group)(objectSid=%s))",
862 ldap_encode_ndr_dom_sid(mem_ctx, dom_sid_add_rid(mem_ctx, state->dom_sid[database], rid)));
865 *error_string = talloc_asprintf(mem_ctx, "gendb_search failed: %s", ldb_errstring(state->sam_ldb));
866 return NT_STATUS_INTERNAL_DB_CORRUPTION;
867 } else if (ret == 0) {
868 return NT_STATUS_NO_SUCH_GROUP;
869 } else if (ret > 1) {
870 *error_string = talloc_asprintf(mem_ctx, "More than one group/alias with SID: %s",
871 dom_sid_string(mem_ctx,
872 dom_sid_add_rid(mem_ctx,
873 state->dom_sid[database],
875 return NT_STATUS_INTERNAL_DB_CORRUPTION;
877 msg->dn = talloc_steal(msg, msgs[0]->dn);
882 for (i=0; i<alias_member->sids.num_sids; i++) {
883 struct ldb_dn *alias_member_dn;
884 /* search for members, in the top basedn (normal users are builtin aliases) */
885 ret = gendb_search(state->sam_ldb, mem_ctx, state->base_dn[SAM_DATABASE_DOMAIN], &msgs, attrs,
887 ldap_encode_ndr_dom_sid(mem_ctx, alias_member->sids.sids[i].sid));
890 *error_string = talloc_asprintf(mem_ctx, "gendb_search failed: %s", ldb_errstring(state->sam_ldb));
891 return NT_STATUS_INTERNAL_DB_CORRUPTION;
892 } else if (ret == 0) {
894 nt_status = samsync_ldb_add_foreignSecurityPrincipal(mem_ctx, state,
895 alias_member->sids.sids[i].sid,
898 if (!NT_STATUS_IS_OK(nt_status)) {
901 } else if (ret > 1) {
902 return NT_STATUS_INTERNAL_DB_CORRUPTION;
904 alias_member_dn = msgs[0]->dn;
906 samdb_msg_add_string(state->sam_ldb, mem_ctx, msg, "member", ldb_dn_linearize(mem_ctx, alias_member_dn));
911 ret = samdb_replace(state->sam_ldb, mem_ctx, msg);
913 *error_string = talloc_asprintf(mem_ctx, "Failed to modify group record %s: %s",
914 ldb_dn_linearize(mem_ctx, msg->dn),
915 ldb_errstring(state->sam_ldb));
916 return NT_STATUS_INTERNAL_DB_CORRUPTION;
922 static NTSTATUS samsync_ldb_handle_account(TALLOC_CTX *mem_ctx,
923 struct samsync_ldb_state *state,
924 enum netr_SamDatabaseID database,
925 struct netr_DELTA_ENUM *delta,
928 struct dom_sid *sid = delta->delta_id_union.sid;
929 struct netr_DELTA_ACCOUNT *account = delta->delta_union.account;
931 struct ldb_message *msg;
932 struct ldb_message **msgs;
933 struct ldb_dn *privilege_dn;
935 const char *attrs[] = { NULL };
938 msg = ldb_msg_new(mem_ctx);
940 return NT_STATUS_NO_MEMORY;
943 /* search for the account, by sid, in the top basedn */
944 ret = gendb_search(state->sam_ldb, mem_ctx, state->base_dn[SAM_DATABASE_DOMAIN], &msgs, attrs,
945 "(objectSid=%s)", ldap_encode_ndr_dom_sid(mem_ctx, sid));
948 *error_string = talloc_asprintf(mem_ctx, "gendb_search failed: %s", ldb_errstring(state->sam_ldb));
949 return NT_STATUS_INTERNAL_DB_CORRUPTION;
950 } else if (ret == 0) {
952 nt_status = samsync_ldb_add_foreignSecurityPrincipal(mem_ctx, state,
956 privilege_dn = talloc_steal(msg, privilege_dn);
957 if (!NT_STATUS_IS_OK(nt_status)) {
960 } else if (ret > 1) {
961 *error_string = talloc_asprintf(mem_ctx, "More than one account with SID: %s",
962 dom_sid_string(mem_ctx, sid));
963 return NT_STATUS_INTERNAL_DB_CORRUPTION;
965 privilege_dn = talloc_steal(msg, msgs[0]->dn);
968 msg->dn = privilege_dn;
970 for (i=0; i< account->privilege_entries; i++) {
971 samdb_msg_add_string(state->sam_ldb, mem_ctx, msg, "privilege",
972 account->privilege_name[i].string);
975 ret = samdb_replace(state->sam_ldb, mem_ctx, msg);
977 *error_string = talloc_asprintf(mem_ctx, "Failed to modify privilege record %s",
978 ldb_dn_linearize(mem_ctx, msg->dn));
979 return NT_STATUS_INTERNAL_DB_CORRUPTION;
985 static NTSTATUS samsync_ldb_delete_account(TALLOC_CTX *mem_ctx,
986 struct samsync_ldb_state *state,
987 enum netr_SamDatabaseID database,
988 struct netr_DELTA_ENUM *delta,
991 struct dom_sid *sid = delta->delta_id_union.sid;
993 struct ldb_message *msg;
994 struct ldb_message **msgs;
996 const char *attrs[] = { NULL };
998 msg = ldb_msg_new(mem_ctx);
1000 return NT_STATUS_NO_MEMORY;
1003 /* search for the account, by sid, in the top basedn */
1004 ret = gendb_search(state->sam_ldb, mem_ctx, state->base_dn[SAM_DATABASE_DOMAIN], &msgs, attrs,
1006 ldap_encode_ndr_dom_sid(mem_ctx, sid));
1009 *error_string = talloc_asprintf(mem_ctx, "gendb_search failed: %s", ldb_errstring(state->sam_ldb));
1010 return NT_STATUS_INTERNAL_DB_CORRUPTION;
1011 } else if (ret == 0) {
1012 return NT_STATUS_NO_SUCH_USER;
1013 } else if (ret > 1) {
1014 *error_string = talloc_asprintf(mem_ctx, "More than one account with SID: %s",
1015 dom_sid_string(mem_ctx, sid));
1016 return NT_STATUS_INTERNAL_DB_CORRUPTION;
1018 msg->dn = talloc_steal(msg, msgs[0]->dn);
1021 samdb_msg_add_delete(state->sam_ldb, mem_ctx, msg,
1024 ret = samdb_replace(state->sam_ldb, mem_ctx, msg);
1026 *error_string = talloc_asprintf(mem_ctx, "Failed to modify privilege record %s",
1027 ldb_dn_linearize(mem_ctx, msg->dn));
1028 return NT_STATUS_INTERNAL_DB_CORRUPTION;
1031 return NT_STATUS_OK;
1034 static NTSTATUS libnet_samsync_ldb_fn(TALLOC_CTX *mem_ctx,
1036 enum netr_SamDatabaseID database,
1037 struct netr_DELTA_ENUM *delta,
1038 char **error_string)
1040 NTSTATUS nt_status = NT_STATUS_OK;
1041 struct samsync_ldb_state *state = talloc_get_type(private, struct samsync_ldb_state);
1043 *error_string = NULL;
1044 switch (delta->delta_type) {
1045 case NETR_DELTA_DOMAIN:
1047 nt_status = samsync_ldb_handle_domain(mem_ctx,
1054 case NETR_DELTA_USER:
1056 nt_status = samsync_ldb_handle_user(mem_ctx,
1063 case NETR_DELTA_DELETE_USER:
1065 nt_status = samsync_ldb_delete_user(mem_ctx,
1072 case NETR_DELTA_GROUP:
1074 nt_status = samsync_ldb_handle_group(mem_ctx,
1081 case NETR_DELTA_DELETE_GROUP:
1083 nt_status = samsync_ldb_delete_group(mem_ctx,
1090 case NETR_DELTA_GROUP_MEMBER:
1092 nt_status = samsync_ldb_handle_group_member(mem_ctx,
1099 case NETR_DELTA_ALIAS:
1101 nt_status = samsync_ldb_handle_alias(mem_ctx,
1108 case NETR_DELTA_DELETE_ALIAS:
1110 nt_status = samsync_ldb_delete_alias(mem_ctx,
1117 case NETR_DELTA_ALIAS_MEMBER:
1119 nt_status = samsync_ldb_handle_alias_member(mem_ctx,
1126 case NETR_DELTA_ACCOUNT:
1128 nt_status = samsync_ldb_handle_account(mem_ctx,
1135 case NETR_DELTA_DELETE_ACCOUNT:
1137 nt_status = samsync_ldb_delete_account(mem_ctx,
1145 /* Can't dump them all right now */
1148 if (!NT_STATUS_IS_OK(nt_status) && !*error_string) {
1149 *error_string = talloc_asprintf(mem_ctx, "Failed to handle samsync delta: %s", nt_errstr(nt_status));
1154 static NTSTATUS libnet_samsync_ldb_init(TALLOC_CTX *mem_ctx,
1156 struct libnet_SamSync_state *samsync_state,
1157 char **error_string)
1159 struct samsync_ldb_state *state = talloc_get_type(private, struct samsync_ldb_state);
1160 const char *server = dcerpc_server_name(samsync_state->netlogon_pipe);
1163 state->samsync_state = samsync_state;
1165 ZERO_STRUCT(state->dom_sid);
1166 if (state->samsync_state->domain_sid) {
1167 state->dom_sid[SAM_DATABASE_DOMAIN] = dom_sid_dup(state, state->samsync_state->domain_sid);
1170 state->dom_sid[SAM_DATABASE_BUILTIN] = dom_sid_parse_talloc(state, SID_BUILTIN);
1172 if (state->samsync_state->realm) {
1173 if (!server || !*server) {
1174 /* huh? how do we not have a server name? */
1175 *error_string = talloc_strdup(mem_ctx, "No DCE/RPC server name available. How did we connect?");
1176 return NT_STATUS_INVALID_PARAMETER;
1178 ldap_url = talloc_asprintf(state, "ldap://%s", server);
1180 state->remote_ldb = ldb_wrap_connect(mem_ctx, ldap_url,
1181 NULL, state->samsync_state->machine_net_ctx->cred,
1183 if (!state->remote_ldb) {
1184 *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);
1185 return NT_STATUS_NO_LOGON_SERVERS;
1188 state->remote_ldb = NULL;
1190 return NT_STATUS_OK;
1193 NTSTATUS libnet_samsync_ldb(struct libnet_context *ctx, TALLOC_CTX *mem_ctx, struct libnet_samsync_ldb *r)
1196 struct libnet_SamSync r2;
1197 struct samsync_ldb_state *state = talloc(mem_ctx, struct samsync_ldb_state);
1200 return NT_STATUS_NO_MEMORY;
1203 state->secrets = NULL;
1204 state->trusted_domains = NULL;
1206 state->sam_ldb = ldb_wrap_connect(mem_ctx, lp_sam_url(), r->in.session_info,
1207 ctx->cred, 0, NULL);
1209 r2.out.error_string = NULL;
1210 r2.in.binding_string = r->in.binding_string;
1211 r2.in.init_fn = libnet_samsync_ldb_init;
1212 r2.in.delta_fn = libnet_samsync_ldb_fn;
1213 r2.in.fn_ctx = state;
1214 r2.in.machine_account = NULL; /* TODO: Create a machine account, fill this in, and the delete it */
1215 nt_status = libnet_SamSync_netlogon(ctx, state, &r2);
1216 r->out.error_string = r2.out.error_string;
1217 talloc_steal(mem_ctx, r->out.error_string);
1219 if (!NT_STATUS_IS_OK(nt_status)) {