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/proto.h"
35 struct samsync_ldb_secret {
36 struct samsync_ldb_secret *prev, *next;
42 struct samsync_ldb_trusted_domain {
43 struct samsync_ldb_trusted_domain *prev, *next;
48 struct samsync_ldb_state {
49 /* Values from the LSA lookup */
50 const struct libnet_SamSync_state *samsync_state;
52 struct dom_sid *dom_sid[3];
53 struct ldb_context *sam_ldb, *remote_ldb;
54 struct ldb_dn *base_dn[3];
55 struct samsync_ldb_secret *secrets;
56 struct samsync_ldb_trusted_domain *trusted_domains;
59 static NTSTATUS samsync_ldb_add_foreignSecurityPrincipal(TALLOC_CTX *mem_ctx,
60 struct samsync_ldb_state *state,
62 struct ldb_dn **fsp_dn,
65 const char *sidstr = dom_sid_string(mem_ctx, sid);
66 /* We assume that ForeignSecurityPrincipals are under the BASEDN of the main domain */
67 struct ldb_dn *basedn = samdb_search_dn(state->sam_ldb, mem_ctx,
68 state->base_dn[SAM_DATABASE_DOMAIN],
69 "(&(objectClass=container)(cn=ForeignSecurityPrincipals))");
70 struct ldb_message *msg;
74 return NT_STATUS_NO_MEMORY;
78 *error_string = talloc_asprintf(mem_ctx,
79 "Failed to find DN for "
80 "ForeignSecurityPrincipal container under %s",
81 ldb_dn_linearize(mem_ctx, state->base_dn[SAM_DATABASE_DOMAIN]));
82 return NT_STATUS_INTERNAL_DB_CORRUPTION;
85 msg = ldb_msg_new(mem_ctx);
87 return NT_STATUS_NO_MEMORY;
90 /* add core elements to the ldb_message for the alias */
91 msg->dn = ldb_dn_build_child(mem_ctx, "CN", sidstr, basedn);
93 return NT_STATUS_NO_MEMORY;
95 samdb_msg_add_string(state->sam_ldb, mem_ctx, msg,
97 "foreignSecurityPrincipal");
101 /* create the alias */
102 ret = samdb_add(state->sam_ldb, mem_ctx, msg);
104 *error_string = talloc_asprintf(mem_ctx, "Failed to create foreignSecurityPrincipal "
106 ldb_dn_linearize(mem_ctx, msg->dn),
107 ldb_errstring(state->sam_ldb));
108 return NT_STATUS_INTERNAL_DB_CORRUPTION;
113 static NTSTATUS samsync_ldb_handle_domain(TALLOC_CTX *mem_ctx,
114 struct samsync_ldb_state *state,
115 enum netr_SamDatabaseID database,
116 struct netr_DELTA_ENUM *delta,
119 struct netr_DELTA_DOMAIN *domain = delta->delta_union.domain;
120 const char *domain_name = domain->domain_name.string;
121 struct ldb_message *msg;
124 msg = ldb_msg_new(mem_ctx);
126 return NT_STATUS_NO_MEMORY;
129 if (database == SAM_DATABASE_DOMAIN) {
130 const char *domain_attrs[] = {"nETBIOSName", "nCName", NULL};
131 struct ldb_message **msgs_domain;
134 ret_domain = gendb_search(state->sam_ldb, mem_ctx, NULL, &msgs_domain, domain_attrs,
135 "(&(&(nETBIOSName=%s)(objectclass=crossRef))(ncName=*))",
137 if (ret_domain == -1) {
138 *error_string = talloc_asprintf(mem_ctx, "gendb_search for domain failed: %s", ldb_errstring(state->sam_ldb));
139 return NT_STATUS_INTERNAL_DB_CORRUPTION;
142 if (ret_domain != 1) {
143 *error_string = talloc_asprintf(mem_ctx, "Failed to find existing domain record for %s: %d results", domain_name,
145 return NT_STATUS_NO_SUCH_DOMAIN;
148 state->base_dn[database] = samdb_result_dn(state, msgs_domain[0], "nCName", NULL);
150 if (state->dom_sid[database]) {
151 /* Update the domain sid with the incoming
152 * domain (found on LSA pipe, database sid may
154 samdb_msg_add_dom_sid(state->sam_ldb, mem_ctx,
155 msg, "objectSid", state->dom_sid[database]);
157 /* Well, we will have to use the one from the database */
158 state->dom_sid[database] = samdb_search_dom_sid(state->sam_ldb, state,
159 state->base_dn[database],
163 if (state->samsync_state->domain_guid) {
166 nt_status = ndr_push_struct_blob(&v, msg, state->samsync_state->domain_guid,
167 (ndr_push_flags_fn_t)ndr_push_GUID);
168 if (!NT_STATUS_IS_OK(nt_status)) {
169 *error_string = talloc_asprintf(mem_ctx, "ndr_push of domain GUID failed!");
173 ldb_msg_add_value(msg, "objectGUID", &v);
175 } else if (database == SAM_DATABASE_BUILTIN) {
176 /* work out the builtin_dn - useful for so many calls its worth
178 const char *dnstring = samdb_search_string(state->sam_ldb, mem_ctx, NULL,
179 "distinguishedName", "objectClass=builtinDomain");
180 state->base_dn[database] = ldb_dn_explode(state, dnstring);
183 return NT_STATUS_INVALID_PARAMETER;
186 msg->dn = talloc_reference(mem_ctx, state->base_dn[database]);
188 return NT_STATUS_NO_MEMORY;
191 samdb_msg_add_string(state->sam_ldb, mem_ctx,
192 msg, "oEMInformation", domain->comment.string);
194 samdb_msg_add_int64(state->sam_ldb, mem_ctx,
195 msg, "forceLogoff", domain->force_logoff_time);
197 samdb_msg_add_uint(state->sam_ldb, mem_ctx,
198 msg, "minPwdLen", domain->min_password_length);
200 samdb_msg_add_int64(state->sam_ldb, mem_ctx,
201 msg, "maxPwdAge", domain->max_password_age);
203 samdb_msg_add_int64(state->sam_ldb, mem_ctx,
204 msg, "minPwdAge", domain->min_password_age);
206 samdb_msg_add_uint(state->sam_ldb, mem_ctx,
207 msg, "pwdHistoryLength", domain->password_history_length);
209 samdb_msg_add_uint64(state->sam_ldb, mem_ctx,
210 msg, "modifiedCount",
211 domain->sequence_num);
213 samdb_msg_add_uint64(state->sam_ldb, mem_ctx,
214 msg, "creationTime", domain->domain_create_time);
216 /* TODO: Account lockout, password properties */
218 ret = samdb_replace(state->sam_ldb, mem_ctx, msg);
221 return NT_STATUS_INTERNAL_ERROR;
226 static NTSTATUS samsync_ldb_handle_user(TALLOC_CTX *mem_ctx,
227 struct samsync_ldb_state *state,
228 enum netr_SamDatabaseID database,
229 struct netr_DELTA_ENUM *delta,
232 uint32_t rid = delta->delta_id_union.rid;
233 struct netr_DELTA_USER *user = delta->delta_union.user;
234 const char *container, *obj_class;
237 const struct dom_sid *user_sid;
238 struct ldb_message *msg;
239 struct ldb_message **msgs;
240 struct ldb_message **remote_msgs;
244 const char *attrs[] = { NULL };
245 /* we may change this to a global search, then fill in only the things not in ldap later */
246 const char *remote_attrs[] = { "userPrincipalName", "servicePrincipalName",
247 "msDS-KeyVersionNumber", "objectGUID", NULL};
249 user_sid = dom_sid_add_rid(mem_ctx, state->dom_sid[database], rid);
251 return NT_STATUS_NO_MEMORY;
254 msg = ldb_msg_new(mem_ctx);
256 return NT_STATUS_NO_MEMORY;
260 /* search for the user, by rid */
261 ret = gendb_search(state->sam_ldb, mem_ctx, state->base_dn[database],
262 &msgs, attrs, "(&(objectClass=user)(objectSid=%s))",
263 ldap_encode_ndr_dom_sid(mem_ctx, user_sid));
266 *error_string = talloc_asprintf(mem_ctx, "LDB for user %s failed: %s",
267 dom_sid_string(mem_ctx, user_sid),
268 ldb_errstring(state->sam_ldb));
269 return NT_STATUS_INTERNAL_DB_CORRUPTION;
270 } else if (ret == 0) {
272 } else if (ret > 1) {
273 *error_string = talloc_asprintf(mem_ctx, "More than one user with SID: %s in local LDB",
274 dom_sid_string(mem_ctx, user_sid));
275 return NT_STATUS_INTERNAL_DB_CORRUPTION;
277 msg->dn = msgs[0]->dn;
278 talloc_steal(msg, msgs[0]->dn);
281 /* and do the same on the remote database */
282 if (state->remote_ldb) {
283 ret = gendb_search(state->remote_ldb, mem_ctx, state->base_dn[database],
284 &remote_msgs, remote_attrs, "(&(objectClass=user)(objectSid=%s))",
285 ldap_encode_ndr_dom_sid(mem_ctx, user_sid));
288 *error_string = talloc_asprintf(mem_ctx, "remote LDAP for user %s failed: %s",
289 dom_sid_string(mem_ctx, user_sid),
290 ldb_errstring(state->remote_ldb));
291 return NT_STATUS_INTERNAL_DB_CORRUPTION;
292 } else if (ret == 0) {
293 *error_string = talloc_asprintf(mem_ctx, "User exists in samsync but not in remote LDAP domain! (base: %s, SID: %s)",
294 ldb_dn_linearize(mem_ctx, state->base_dn[database]),
295 dom_sid_string(mem_ctx, user_sid));
296 return NT_STATUS_NO_SUCH_USER;
297 } else if (ret > 1) {
298 *error_string = talloc_asprintf(mem_ctx, "More than one user in remote LDAP domain with SID: %s",
299 dom_sid_string(mem_ctx, user_sid));
300 return NT_STATUS_INTERNAL_DB_CORRUPTION;
302 /* Try to put things in the same location as the remote server */
304 msg->dn = remote_msgs[0]->dn;
305 talloc_steal(msg, remote_msgs[0]->dn);
309 cn_name = talloc_strdup(mem_ctx, user->account_name.string);
310 NT_STATUS_HAVE_NO_MEMORY(cn_name);
311 cn_name_len = strlen(cn_name);
313 #define ADD_OR_DEL(type, attrib, field) do { \
315 samdb_msg_add_ ## type(state->sam_ldb, mem_ctx, msg, \
316 attrib, user->field); \
318 samdb_msg_add_delete(state->sam_ldb, mem_ctx, msg, \
323 ADD_OR_DEL(string, "samAccountName", account_name.string);
324 ADD_OR_DEL(string, "displayName", full_name.string);
326 if (samdb_msg_add_dom_sid(state->sam_ldb, mem_ctx, msg,
327 "objectSid", dom_sid_add_rid(mem_ctx, state->dom_sid[database], rid))) {
328 return NT_STATUS_NO_MEMORY;
331 ADD_OR_DEL(uint, "primaryGroupID", primary_gid);
332 ADD_OR_DEL(string, "homeDirectory", home_directory.string);
333 ADD_OR_DEL(string, "homeDrive", home_drive.string);
334 ADD_OR_DEL(string, "scriptPath", logon_script.string);
335 ADD_OR_DEL(string, "description", description.string);
336 ADD_OR_DEL(string, "userWorkstations", workstations.string);
338 ADD_OR_DEL(uint64, "lastLogon", last_logon);
339 ADD_OR_DEL(uint64, "lastLogoff", last_logoff);
341 if (samdb_msg_add_logon_hours(state->sam_ldb, mem_ctx, msg, "logonHours", &user->logon_hours) != 0) {
342 return NT_STATUS_NO_MEMORY;
345 ADD_OR_DEL(uint, "badPwdCount", bad_password_count);
346 ADD_OR_DEL(uint, "logonCount", logon_count);
348 ADD_OR_DEL(uint64, "pwdLastSet", last_password_change);
349 ADD_OR_DEL(uint64, "accountExpires", acct_expiry);
351 if (samdb_msg_add_acct_flags(state->sam_ldb, mem_ctx, msg,
352 "userAccountControl", user->acct_flags) != 0) {
353 return NT_STATUS_NO_MEMORY;
356 /* Passwords. Ensure there is no plaintext stored against
357 * this entry, as we only have hashes */
358 samdb_msg_add_delete(state->sam_ldb, mem_ctx, msg,
360 if (user->lm_password_present) {
361 samdb_msg_add_hash(state->sam_ldb, mem_ctx, msg,
362 "lmPwdHash", &user->lmpassword);
364 samdb_msg_add_delete(state->sam_ldb, mem_ctx, msg,
367 if (user->nt_password_present) {
368 samdb_msg_add_hash(state->sam_ldb, mem_ctx, msg,
369 "ntPwdHash", &user->ntpassword);
371 samdb_msg_add_delete(state->sam_ldb, mem_ctx, msg,
375 ADD_OR_DEL(string, "comment", comment.string);
376 ADD_OR_DEL(string, "userParameters", parameters.string);
377 ADD_OR_DEL(uint, "countryCode", country_code);
378 ADD_OR_DEL(uint, "codePage", code_page);
380 ADD_OR_DEL(string, "profilePath", profile_path.string);
384 for (i=0; remote_attrs[i]; i++) {
385 struct ldb_message_element *el = ldb_msg_find_element(remote_msgs[0], remote_attrs[i]);
387 samdb_msg_add_delete(state->sam_ldb, mem_ctx, msg,
390 ldb_msg_add(msg, el, LDB_FLAG_MOD_REPLACE);
394 acb = user->acct_flags;
395 if (acb & (ACB_WSTRUST)) {
396 cn_name[cn_name_len - 1] = '\0';
397 container = "Computers";
398 obj_class = "computer";
400 } else if (acb & ACB_SVRTRUST) {
401 if (cn_name[cn_name_len - 1] != '$') {
402 return NT_STATUS_FOOBAR;
404 cn_name[cn_name_len - 1] = '\0';
405 container = "Domain Controllers";
406 obj_class = "computer";
412 samdb_msg_add_string(state->sam_ldb, mem_ctx, msg,
413 "objectClass", obj_class);
415 msg->dn = ldb_dn_string_compose(mem_ctx, state->base_dn[database],
416 "CN=%s, CN=%s", cn_name, container);
418 return NT_STATUS_NO_MEMORY;
422 ret = samdb_add(state->sam_ldb, mem_ctx, msg);
424 struct ldb_dn *first_try_dn = msg->dn;
425 /* Try again with the default DN */
426 msg->dn = talloc_steal(msg, msgs[0]->dn);
427 ret = samdb_add(state->sam_ldb, mem_ctx, msg);
429 *error_string = talloc_asprintf(mem_ctx, "Failed to create user record. Tried both %s and %s: %s",
430 ldb_dn_linearize(mem_ctx, first_try_dn),
431 ldb_dn_linearize(mem_ctx, msg->dn),
432 ldb_errstring(state->sam_ldb));
433 return NT_STATUS_INTERNAL_DB_CORRUPTION;
437 ret = samdb_replace(state->sam_ldb, mem_ctx, msg);
439 *error_string = talloc_asprintf(mem_ctx, "Failed to modify user record %s: %s",
440 ldb_dn_linearize(mem_ctx, msg->dn),
441 ldb_errstring(state->sam_ldb));
442 return NT_STATUS_INTERNAL_DB_CORRUPTION;
449 static NTSTATUS samsync_ldb_delete_user(TALLOC_CTX *mem_ctx,
450 struct samsync_ldb_state *state,
451 enum netr_SamDatabaseID database,
452 struct netr_DELTA_ENUM *delta,
455 uint32_t rid = delta->delta_id_union.rid;
456 struct ldb_message **msgs;
458 const char *attrs[] = { NULL };
460 /* search for the user, by rid */
461 ret = gendb_search(state->sam_ldb, mem_ctx, state->base_dn[database],
462 &msgs, attrs, "(&(objectClass=user)(objectSid=%s))",
463 ldap_encode_ndr_dom_sid(mem_ctx, dom_sid_add_rid(mem_ctx, state->dom_sid[database], rid)));
466 *error_string = talloc_asprintf(mem_ctx, "gendb_search failed: %s", ldb_errstring(state->sam_ldb));
467 return NT_STATUS_INTERNAL_DB_CORRUPTION;
468 } else if (ret == 0) {
469 return NT_STATUS_NO_SUCH_USER;
470 } else if (ret > 1) {
471 *error_string = talloc_asprintf(mem_ctx, "More than one user with SID: %s",
472 dom_sid_string(mem_ctx,
473 dom_sid_add_rid(mem_ctx,
474 state->dom_sid[database],
476 return NT_STATUS_INTERNAL_DB_CORRUPTION;
479 ret = samdb_delete(state->sam_ldb, mem_ctx, msgs[0]->dn);
481 *error_string = talloc_asprintf(mem_ctx, "Failed to delete user record %s: %s",
482 ldb_dn_linearize(mem_ctx, msgs[0]->dn),
483 ldb_errstring(state->sam_ldb));
484 return NT_STATUS_INTERNAL_DB_CORRUPTION;
490 static NTSTATUS samsync_ldb_handle_group(TALLOC_CTX *mem_ctx,
491 struct samsync_ldb_state *state,
492 enum netr_SamDatabaseID database,
493 struct netr_DELTA_ENUM *delta,
496 uint32_t rid = delta->delta_id_union.rid;
497 struct netr_DELTA_GROUP *group = delta->delta_union.group;
498 const char *container, *obj_class;
501 struct ldb_message *msg;
502 struct ldb_message **msgs;
505 const char *attrs[] = { NULL };
507 msg = ldb_msg_new(mem_ctx);
509 return NT_STATUS_NO_MEMORY;
512 /* search for the group, by rid */
513 ret = gendb_search(state->sam_ldb, mem_ctx, state->base_dn[database], &msgs, attrs,
514 "(&(objectClass=group)(objectSid=%s))",
515 ldap_encode_ndr_dom_sid(mem_ctx, dom_sid_add_rid(mem_ctx, state->dom_sid[database], rid)));
518 *error_string = talloc_asprintf(mem_ctx, "gendb_search failed: %s", ldb_errstring(state->sam_ldb));
519 return NT_STATUS_INTERNAL_DB_CORRUPTION;
520 } else if (ret == 0) {
522 } else if (ret > 1) {
523 *error_string = talloc_asprintf(mem_ctx, "More than one group/alias with SID: %s",
524 dom_sid_string(mem_ctx,
525 dom_sid_add_rid(mem_ctx,
526 state->dom_sid[database],
528 return NT_STATUS_INTERNAL_DB_CORRUPTION;
530 msg->dn = talloc_steal(msg, msgs[0]->dn);
533 cn_name = group->group_name.string;
535 #define ADD_OR_DEL(type, attrib, field) do { \
536 if (group->field) { \
537 samdb_msg_add_ ## type(state->sam_ldb, mem_ctx, msg, \
538 attrib, group->field); \
540 samdb_msg_add_delete(state->sam_ldb, mem_ctx, msg, \
545 ADD_OR_DEL(string, "samAccountName", group_name.string);
547 if (samdb_msg_add_dom_sid(state->sam_ldb, mem_ctx, msg,
548 "objectSid", dom_sid_add_rid(mem_ctx, state->dom_sid[database], rid))) {
549 return NT_STATUS_NO_MEMORY;
552 ADD_OR_DEL(string, "description", description.string);
560 samdb_msg_add_string(state->sam_ldb, mem_ctx, msg,
561 "objectClass", obj_class);
562 msg->dn = ldb_dn_string_compose(mem_ctx, state->base_dn[database],
563 "CN=%s, CN=%s", cn_name, container);
565 return NT_STATUS_NO_MEMORY;
568 ret = samdb_add(state->sam_ldb, mem_ctx, msg);
570 *error_string = talloc_asprintf(mem_ctx, "Failed to create group record %s: %s",
571 ldb_dn_linearize(mem_ctx, msg->dn),
572 ldb_errstring(state->sam_ldb));
573 return NT_STATUS_INTERNAL_DB_CORRUPTION;
576 ret = samdb_replace(state->sam_ldb, mem_ctx, msg);
578 *error_string = talloc_asprintf(mem_ctx, "Failed to modify group record %s: %s",
579 ldb_dn_linearize(mem_ctx, msg->dn),
580 ldb_errstring(state->sam_ldb));
581 return NT_STATUS_INTERNAL_DB_CORRUPTION;
588 static NTSTATUS samsync_ldb_delete_group(TALLOC_CTX *mem_ctx,
589 struct samsync_ldb_state *state,
590 enum netr_SamDatabaseID database,
591 struct netr_DELTA_ENUM *delta,
594 uint32_t rid = delta->delta_id_union.rid;
595 struct ldb_message **msgs;
597 const char *attrs[] = { NULL };
599 /* search for the group, by rid */
600 ret = gendb_search(state->sam_ldb, mem_ctx, state->base_dn[database], &msgs, attrs,
601 "(&(objectClass=group)(objectSid=%s))",
602 ldap_encode_ndr_dom_sid(mem_ctx, dom_sid_add_rid(mem_ctx, state->dom_sid[database], rid)));
605 *error_string = talloc_asprintf(mem_ctx, "gendb_search failed: %s", ldb_errstring(state->sam_ldb));
606 return NT_STATUS_INTERNAL_DB_CORRUPTION;
607 } else if (ret == 0) {
608 return NT_STATUS_NO_SUCH_GROUP;
609 } else if (ret > 1) {
610 *error_string = talloc_asprintf(mem_ctx, "More than one group/alias with SID: %s",
611 dom_sid_string(mem_ctx,
612 dom_sid_add_rid(mem_ctx,
613 state->dom_sid[database],
615 return NT_STATUS_INTERNAL_DB_CORRUPTION;
618 ret = samdb_delete(state->sam_ldb, mem_ctx, msgs[0]->dn);
620 *error_string = talloc_asprintf(mem_ctx, "Failed to delete group record %s: %s",
621 ldb_dn_linearize(mem_ctx, msgs[0]->dn),
622 ldb_errstring(state->sam_ldb));
623 return NT_STATUS_INTERNAL_DB_CORRUPTION;
629 static NTSTATUS samsync_ldb_handle_group_member(TALLOC_CTX *mem_ctx,
630 struct samsync_ldb_state *state,
631 enum netr_SamDatabaseID database,
632 struct netr_DELTA_ENUM *delta,
635 uint32_t rid = delta->delta_id_union.rid;
636 struct netr_DELTA_GROUP_MEMBER *group_member = delta->delta_union.group_member;
637 struct ldb_message *msg;
638 struct ldb_message **msgs;
640 const char *attrs[] = { NULL };
643 msg = ldb_msg_new(mem_ctx);
645 return NT_STATUS_NO_MEMORY;
648 /* search for the group, by rid */
649 ret = gendb_search(state->sam_ldb, mem_ctx, state->base_dn[database], &msgs, attrs,
650 "(&(objectClass=group)(objectSid=%s))",
651 ldap_encode_ndr_dom_sid(mem_ctx, dom_sid_add_rid(mem_ctx, state->dom_sid[database], rid)));
654 *error_string = talloc_asprintf(mem_ctx, "gendb_search failed: %s", ldb_errstring(state->sam_ldb));
655 return NT_STATUS_INTERNAL_DB_CORRUPTION;
656 } else if (ret == 0) {
657 return NT_STATUS_NO_SUCH_GROUP;
658 } else if (ret > 1) {
659 *error_string = talloc_asprintf(mem_ctx, "More than one group/alias with SID: %s",
660 dom_sid_string(mem_ctx,
661 dom_sid_add_rid(mem_ctx,
662 state->dom_sid[database],
664 return NT_STATUS_INTERNAL_DB_CORRUPTION;
666 msg->dn = talloc_steal(msg, msgs[0]->dn);
671 for (i=0; i<group_member->num_rids; i++) {
672 /* search for the group, by rid */
673 ret = gendb_search(state->sam_ldb, mem_ctx, state->base_dn[database], &msgs, attrs,
674 "(&(objectClass=user)(objectSid=%s))",
675 ldap_encode_ndr_dom_sid(mem_ctx, dom_sid_add_rid(mem_ctx, state->dom_sid[database], group_member->rids[i])));
678 *error_string = talloc_asprintf(mem_ctx, "gendb_search failed: %s", ldb_errstring(state->sam_ldb));
679 return NT_STATUS_INTERNAL_DB_CORRUPTION;
680 } else if (ret == 0) {
681 return NT_STATUS_NO_SUCH_USER;
682 } else if (ret > 1) {
683 return NT_STATUS_INTERNAL_DB_CORRUPTION;
685 samdb_msg_add_string(state->sam_ldb, mem_ctx, msg, "member", ldb_dn_linearize(mem_ctx, msgs[0]->dn));
691 ret = samdb_replace(state->sam_ldb, mem_ctx, msg);
693 *error_string = talloc_asprintf(mem_ctx, "Failed to modify group record %s: %s",
694 ldb_dn_linearize(mem_ctx, msg->dn),
695 ldb_errstring(state->sam_ldb));
696 return NT_STATUS_INTERNAL_DB_CORRUPTION;
702 static NTSTATUS samsync_ldb_handle_alias(TALLOC_CTX *mem_ctx,
703 struct samsync_ldb_state *state,
704 enum netr_SamDatabaseID database,
705 struct netr_DELTA_ENUM *delta,
708 uint32_t rid = delta->delta_id_union.rid;
709 struct netr_DELTA_ALIAS *alias = delta->delta_union.alias;
710 const char *container, *obj_class;
713 struct ldb_message *msg;
714 struct ldb_message **msgs;
717 const char *attrs[] = { NULL };
719 msg = ldb_msg_new(mem_ctx);
721 return NT_STATUS_NO_MEMORY;
724 /* search for the alias, by rid */
725 ret = gendb_search(state->sam_ldb, mem_ctx, state->base_dn[database], &msgs, attrs,
726 "(&(objectClass=group)(objectSid=%s))",
727 ldap_encode_ndr_dom_sid(mem_ctx, dom_sid_add_rid(mem_ctx, state->dom_sid[database], rid)));
730 *error_string = talloc_asprintf(mem_ctx, "gendb_search failed: %s", ldb_errstring(state->sam_ldb));
731 return NT_STATUS_INTERNAL_DB_CORRUPTION;
732 } else if (ret == 0) {
734 } else if (ret > 1) {
735 *error_string = talloc_asprintf(mem_ctx, "More than one group/alias with SID: %s",
736 dom_sid_string(mem_ctx,
737 dom_sid_add_rid(mem_ctx,
738 state->dom_sid[database],
740 return NT_STATUS_INTERNAL_DB_CORRUPTION;
742 msg->dn = talloc_steal(mem_ctx, msgs[0]->dn);
745 cn_name = alias->alias_name.string;
747 #define ADD_OR_DEL(type, attrib, field) do { \
748 if (alias->field) { \
749 samdb_msg_add_ ## type(state->sam_ldb, mem_ctx, msg, \
750 attrib, alias->field); \
752 samdb_msg_add_delete(state->sam_ldb, mem_ctx, msg, \
757 ADD_OR_DEL(string, "samAccountName", alias_name.string);
759 if (samdb_msg_add_dom_sid(state->sam_ldb, mem_ctx, msg,
760 "objectSid", dom_sid_add_rid(mem_ctx, state->dom_sid[database], rid))) {
761 return NT_STATUS_NO_MEMORY;
764 ADD_OR_DEL(string, "description", description.string);
768 samdb_msg_add_uint(state->sam_ldb, mem_ctx, msg, "groupType", 0x80000004);
774 samdb_msg_add_string(state->sam_ldb, mem_ctx, msg,
775 "objectClass", obj_class);
776 msg->dn = ldb_dn_string_compose(mem_ctx, state->base_dn[database],
777 "CN=%s, CN=%s", cn_name, container);
779 return NT_STATUS_NO_MEMORY;
782 ret = samdb_add(state->sam_ldb, mem_ctx, msg);
784 *error_string = talloc_asprintf(mem_ctx, "Failed to create alias record %s: %s",
785 ldb_dn_linearize(mem_ctx, msg->dn),
786 ldb_errstring(state->sam_ldb));
787 return NT_STATUS_INTERNAL_DB_CORRUPTION;
790 ret = samdb_replace(state->sam_ldb, mem_ctx, msg);
792 *error_string = talloc_asprintf(mem_ctx, "Failed to modify alias record %s: %s",
793 ldb_dn_linearize(mem_ctx, msg->dn),
794 ldb_errstring(state->sam_ldb));
795 return NT_STATUS_INTERNAL_DB_CORRUPTION;
802 static NTSTATUS samsync_ldb_delete_alias(TALLOC_CTX *mem_ctx,
803 struct samsync_ldb_state *state,
804 enum netr_SamDatabaseID database,
805 struct netr_DELTA_ENUM *delta,
808 uint32_t rid = delta->delta_id_union.rid;
809 struct ldb_message **msgs;
811 const char *attrs[] = { NULL };
813 /* search for the alias, by rid */
814 ret = gendb_search(state->sam_ldb, mem_ctx, state->base_dn[database], &msgs, attrs,
815 "(&(objectClass=group)(objectSid=%s))",
816 ldap_encode_ndr_dom_sid(mem_ctx, dom_sid_add_rid(mem_ctx, state->dom_sid[database], rid)));
819 *error_string = talloc_asprintf(mem_ctx, "gendb_search failed: %s", ldb_errstring(state->sam_ldb));
820 return NT_STATUS_INTERNAL_DB_CORRUPTION;
821 } else if (ret == 0) {
822 return NT_STATUS_NO_SUCH_ALIAS;
823 } else if (ret > 1) {
824 return NT_STATUS_INTERNAL_DB_CORRUPTION;
827 ret = samdb_delete(state->sam_ldb, mem_ctx, msgs[0]->dn);
829 *error_string = talloc_asprintf(mem_ctx, "Failed to delete alias record %s: %s",
830 ldb_dn_linearize(mem_ctx, msgs[0]->dn),
831 ldb_errstring(state->sam_ldb));
832 return NT_STATUS_INTERNAL_DB_CORRUPTION;
838 static NTSTATUS samsync_ldb_handle_alias_member(TALLOC_CTX *mem_ctx,
839 struct samsync_ldb_state *state,
840 enum netr_SamDatabaseID database,
841 struct netr_DELTA_ENUM *delta,
844 uint32_t rid = delta->delta_id_union.rid;
845 struct netr_DELTA_ALIAS_MEMBER *alias_member = delta->delta_union.alias_member;
846 struct ldb_message *msg;
847 struct ldb_message **msgs;
849 const char *attrs[] = { NULL };
852 msg = ldb_msg_new(mem_ctx);
854 return NT_STATUS_NO_MEMORY;
857 /* search for the alias, by rid */
858 ret = gendb_search(state->sam_ldb, mem_ctx, state->base_dn[database], &msgs, attrs,
859 "(&(objectClass=group)(objectSid=%s))",
860 ldap_encode_ndr_dom_sid(mem_ctx, dom_sid_add_rid(mem_ctx, state->dom_sid[database], rid)));
863 *error_string = talloc_asprintf(mem_ctx, "gendb_search failed: %s", ldb_errstring(state->sam_ldb));
864 return NT_STATUS_INTERNAL_DB_CORRUPTION;
865 } else if (ret == 0) {
866 return NT_STATUS_NO_SUCH_GROUP;
867 } else if (ret > 1) {
868 *error_string = talloc_asprintf(mem_ctx, "More than one group/alias with SID: %s",
869 dom_sid_string(mem_ctx,
870 dom_sid_add_rid(mem_ctx,
871 state->dom_sid[database],
873 return NT_STATUS_INTERNAL_DB_CORRUPTION;
875 msg->dn = talloc_steal(msg, msgs[0]->dn);
880 for (i=0; i<alias_member->sids.num_sids; i++) {
881 struct ldb_dn *alias_member_dn;
882 /* search for members, in the top basedn (normal users are builtin aliases) */
883 ret = gendb_search(state->sam_ldb, mem_ctx, state->base_dn[SAM_DATABASE_DOMAIN], &msgs, attrs,
885 ldap_encode_ndr_dom_sid(mem_ctx, alias_member->sids.sids[i].sid));
888 *error_string = talloc_asprintf(mem_ctx, "gendb_search failed: %s", ldb_errstring(state->sam_ldb));
889 return NT_STATUS_INTERNAL_DB_CORRUPTION;
890 } else if (ret == 0) {
892 nt_status = samsync_ldb_add_foreignSecurityPrincipal(mem_ctx, state,
893 alias_member->sids.sids[i].sid,
896 if (!NT_STATUS_IS_OK(nt_status)) {
899 } else if (ret > 1) {
900 return NT_STATUS_INTERNAL_DB_CORRUPTION;
902 alias_member_dn = msgs[0]->dn;
904 samdb_msg_add_string(state->sam_ldb, mem_ctx, msg, "member", ldb_dn_linearize(mem_ctx, alias_member_dn));
909 ret = samdb_replace(state->sam_ldb, mem_ctx, msg);
911 *error_string = talloc_asprintf(mem_ctx, "Failed to modify group record %s: %s",
912 ldb_dn_linearize(mem_ctx, msg->dn),
913 ldb_errstring(state->sam_ldb));
914 return NT_STATUS_INTERNAL_DB_CORRUPTION;
920 static NTSTATUS samsync_ldb_handle_account(TALLOC_CTX *mem_ctx,
921 struct samsync_ldb_state *state,
922 enum netr_SamDatabaseID database,
923 struct netr_DELTA_ENUM *delta,
926 struct dom_sid *sid = delta->delta_id_union.sid;
927 struct netr_DELTA_ACCOUNT *account = delta->delta_union.account;
929 struct ldb_message *msg;
930 struct ldb_message **msgs;
931 struct ldb_dn *privilege_dn;
933 const char *attrs[] = { NULL };
936 msg = ldb_msg_new(mem_ctx);
938 return NT_STATUS_NO_MEMORY;
941 /* search for the account, by sid, in the top basedn */
942 ret = gendb_search(state->sam_ldb, mem_ctx, state->base_dn[SAM_DATABASE_DOMAIN], &msgs, attrs,
943 "(objectSid=%s)", ldap_encode_ndr_dom_sid(mem_ctx, sid));
946 *error_string = talloc_asprintf(mem_ctx, "gendb_search failed: %s", ldb_errstring(state->sam_ldb));
947 return NT_STATUS_INTERNAL_DB_CORRUPTION;
948 } else if (ret == 0) {
950 nt_status = samsync_ldb_add_foreignSecurityPrincipal(mem_ctx, state,
954 privilege_dn = talloc_steal(msg, privilege_dn);
955 if (!NT_STATUS_IS_OK(nt_status)) {
958 } else if (ret > 1) {
959 *error_string = talloc_asprintf(mem_ctx, "More than one account with SID: %s",
960 dom_sid_string(mem_ctx, sid));
961 return NT_STATUS_INTERNAL_DB_CORRUPTION;
963 privilege_dn = talloc_steal(msg, msgs[0]->dn);
966 msg->dn = privilege_dn;
968 for (i=0; i< account->privilege_entries; i++) {
969 samdb_msg_add_string(state->sam_ldb, mem_ctx, msg, "privilege",
970 account->privilege_name[i].string);
973 ret = samdb_replace(state->sam_ldb, mem_ctx, msg);
975 *error_string = talloc_asprintf(mem_ctx, "Failed to modify privilege record %s",
976 ldb_dn_linearize(mem_ctx, msg->dn));
977 return NT_STATUS_INTERNAL_DB_CORRUPTION;
983 static NTSTATUS samsync_ldb_delete_account(TALLOC_CTX *mem_ctx,
984 struct samsync_ldb_state *state,
985 enum netr_SamDatabaseID database,
986 struct netr_DELTA_ENUM *delta,
989 struct dom_sid *sid = delta->delta_id_union.sid;
991 struct ldb_message *msg;
992 struct ldb_message **msgs;
994 const char *attrs[] = { NULL };
996 msg = ldb_msg_new(mem_ctx);
998 return NT_STATUS_NO_MEMORY;
1001 /* search for the account, by sid, in the top basedn */
1002 ret = gendb_search(state->sam_ldb, mem_ctx, state->base_dn[SAM_DATABASE_DOMAIN], &msgs, attrs,
1004 ldap_encode_ndr_dom_sid(mem_ctx, sid));
1007 *error_string = talloc_asprintf(mem_ctx, "gendb_search failed: %s", ldb_errstring(state->sam_ldb));
1008 return NT_STATUS_INTERNAL_DB_CORRUPTION;
1009 } else if (ret == 0) {
1010 return NT_STATUS_NO_SUCH_USER;
1011 } else if (ret > 1) {
1012 *error_string = talloc_asprintf(mem_ctx, "More than one account with SID: %s",
1013 dom_sid_string(mem_ctx, sid));
1014 return NT_STATUS_INTERNAL_DB_CORRUPTION;
1016 msg->dn = talloc_steal(msg, msgs[0]->dn);
1019 samdb_msg_add_delete(state->sam_ldb, mem_ctx, msg,
1022 ret = samdb_replace(state->sam_ldb, mem_ctx, msg);
1024 *error_string = talloc_asprintf(mem_ctx, "Failed to modify privilege record %s",
1025 ldb_dn_linearize(mem_ctx, msg->dn));
1026 return NT_STATUS_INTERNAL_DB_CORRUPTION;
1029 return NT_STATUS_OK;
1032 static NTSTATUS libnet_samsync_ldb_fn(TALLOC_CTX *mem_ctx,
1034 enum netr_SamDatabaseID database,
1035 struct netr_DELTA_ENUM *delta,
1036 char **error_string)
1038 NTSTATUS nt_status = NT_STATUS_OK;
1039 struct samsync_ldb_state *state = talloc_get_type(private, struct samsync_ldb_state);
1041 *error_string = NULL;
1042 switch (delta->delta_type) {
1043 case NETR_DELTA_DOMAIN:
1045 nt_status = samsync_ldb_handle_domain(mem_ctx,
1052 case NETR_DELTA_USER:
1054 nt_status = samsync_ldb_handle_user(mem_ctx,
1061 case NETR_DELTA_DELETE_USER:
1063 nt_status = samsync_ldb_delete_user(mem_ctx,
1070 case NETR_DELTA_GROUP:
1072 nt_status = samsync_ldb_handle_group(mem_ctx,
1079 case NETR_DELTA_DELETE_GROUP:
1081 nt_status = samsync_ldb_delete_group(mem_ctx,
1088 case NETR_DELTA_GROUP_MEMBER:
1090 nt_status = samsync_ldb_handle_group_member(mem_ctx,
1097 case NETR_DELTA_ALIAS:
1099 nt_status = samsync_ldb_handle_alias(mem_ctx,
1106 case NETR_DELTA_DELETE_ALIAS:
1108 nt_status = samsync_ldb_delete_alias(mem_ctx,
1115 case NETR_DELTA_ALIAS_MEMBER:
1117 nt_status = samsync_ldb_handle_alias_member(mem_ctx,
1124 case NETR_DELTA_ACCOUNT:
1126 nt_status = samsync_ldb_handle_account(mem_ctx,
1133 case NETR_DELTA_DELETE_ACCOUNT:
1135 nt_status = samsync_ldb_delete_account(mem_ctx,
1143 /* Can't dump them all right now */
1146 if (!NT_STATUS_IS_OK(nt_status) && !*error_string) {
1147 *error_string = talloc_asprintf(mem_ctx, "Failed to handle samsync delta: %s", nt_errstr(nt_status));
1152 static NTSTATUS libnet_samsync_ldb_init(TALLOC_CTX *mem_ctx,
1154 struct libnet_SamSync_state *samsync_state,
1155 char **error_string)
1157 struct samsync_ldb_state *state = talloc_get_type(private, struct samsync_ldb_state);
1158 const char *server = dcerpc_server_name(samsync_state->netlogon_pipe);
1161 state->samsync_state = samsync_state;
1163 ZERO_STRUCT(state->dom_sid);
1164 if (state->samsync_state->domain_sid) {
1165 state->dom_sid[SAM_DATABASE_DOMAIN] = dom_sid_dup(state, state->samsync_state->domain_sid);
1168 state->dom_sid[SAM_DATABASE_BUILTIN] = dom_sid_parse_talloc(state, SID_BUILTIN);
1170 if (state->samsync_state->realm) {
1171 if (!server || !*server) {
1172 /* huh? how do we not have a server name? */
1173 *error_string = talloc_strdup(mem_ctx, "No DCE/RPC server name available. How did we connect?");
1174 return NT_STATUS_INVALID_PARAMETER;
1176 ldap_url = talloc_asprintf(state, "ldap://%s", server);
1178 state->remote_ldb = ldb_wrap_connect(mem_ctx, ldap_url,
1179 NULL, state->samsync_state->machine_net_ctx->cred,
1181 if (!state->remote_ldb) {
1182 *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);
1183 return NT_STATUS_NO_LOGON_SERVERS;
1186 state->remote_ldb = NULL;
1188 return NT_STATUS_OK;
1191 NTSTATUS libnet_samsync_ldb(struct libnet_context *ctx, TALLOC_CTX *mem_ctx, struct libnet_samsync_ldb *r)
1194 struct libnet_SamSync r2;
1195 struct samsync_ldb_state *state = talloc(mem_ctx, struct samsync_ldb_state);
1198 return NT_STATUS_NO_MEMORY;
1201 state->secrets = NULL;
1202 state->trusted_domains = NULL;
1204 state->sam_ldb = ldb_wrap_connect(mem_ctx, lp_sam_url(), r->in.session_info,
1205 ctx->cred, 0, NULL);
1207 r2.out.error_string = NULL;
1208 r2.in.binding_string = r->in.binding_string;
1209 r2.in.init_fn = libnet_samsync_ldb_init;
1210 r2.in.delta_fn = libnet_samsync_ldb_fn;
1211 r2.in.fn_ctx = state;
1212 r2.in.machine_account = NULL; /* TODO: Create a machine account, fill this in, and the delete it */
1213 nt_status = libnet_SamSync_netlogon(ctx, state, &r2);
1214 r->out.error_string = r2.out.error_string;
1215 talloc_steal(mem_ctx, r->out.error_string);
1217 if (!NT_STATUS_IS_OK(nt_status)) {