2 Unix SMB/CIFS implementation.
4 Extract the user/system database from a remote SamSync server
6 Copyright (C) Andrew Bartlett <abartlet@samba.org> 2004-2005
7 Copyright (C) Andrew Tridgell 2004
8 Copyright (C) Volker Lendecke 2004
10 This program is free software; you can redistribute it and/or modify
11 it under the terms of the GNU General Public License as published by
12 the Free Software Foundation; either version 3 of the License, or
13 (at your option) any later version.
15 This program is distributed in the hope that it will be useful,
16 but WITHOUT ANY WARRANTY; without even the implied warranty of
17 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 GNU General Public License for more details.
20 You should have received a copy of the GNU General Public License
21 along with this program. If not, see <http://www.gnu.org/licenses/>.
26 #include "libnet/libnet.h"
27 #include "libcli/ldap/ldap_ndr.h"
28 #include "dsdb/samdb/samdb.h"
29 #include "auth/auth.h"
30 #include "../lib/util/util_ldb.h"
31 #include "librpc/gen_ndr/ndr_misc.h"
33 #include "libcli/security/security.h"
34 #include "param/param.h"
36 struct samsync_ldb_secret {
37 struct samsync_ldb_secret *prev, *next;
43 struct samsync_ldb_trusted_domain {
44 struct samsync_ldb_trusted_domain *prev, *next;
49 struct samsync_ldb_state {
50 /* Values from the LSA lookup */
51 const struct libnet_SamSync_state *samsync_state;
53 struct dom_sid *dom_sid[3];
54 struct ldb_context *sam_ldb, *remote_ldb, *pdb;
55 struct ldb_dn *base_dn[3];
56 struct samsync_ldb_secret *secrets;
57 struct samsync_ldb_trusted_domain *trusted_domains;
60 static NTSTATUS samsync_ldb_add_foreignSecurityPrincipal(TALLOC_CTX *mem_ctx,
61 struct samsync_ldb_state *state,
63 struct ldb_dn **fsp_dn,
66 const char *sidstr = dom_sid_string(mem_ctx, sid);
67 /* We assume that ForeignSecurityPrincipals are under the BASEDN of the main domain */
68 struct ldb_dn *basedn = samdb_search_dn(state->sam_ldb, mem_ctx,
69 state->base_dn[SAM_DATABASE_DOMAIN],
70 "(&(objectClass=container)(cn=ForeignSecurityPrincipals))");
71 struct ldb_message *msg;
75 return NT_STATUS_NO_MEMORY;
79 *error_string = talloc_asprintf(mem_ctx,
80 "Failed to find DN for "
81 "ForeignSecurityPrincipal container under %s",
82 ldb_dn_get_linearized(state->base_dn[SAM_DATABASE_DOMAIN]));
83 return NT_STATUS_INTERNAL_DB_CORRUPTION;
86 msg = ldb_msg_new(mem_ctx);
88 return NT_STATUS_NO_MEMORY;
91 /* add core elements to the ldb_message for the alias */
93 if ( ! ldb_dn_add_child_fmt(msg->dn, "CN=%s", sidstr))
94 return NT_STATUS_UNSUCCESSFUL;
96 samdb_msg_add_string(state->sam_ldb, mem_ctx, msg,
98 "foreignSecurityPrincipal");
102 /* create the alias */
103 ret = ldb_add(state->sam_ldb, msg);
104 if (ret != LDB_SUCCESS) {
105 *error_string = talloc_asprintf(mem_ctx, "Failed to create foreignSecurityPrincipal "
107 ldb_dn_get_linearized(msg->dn),
108 ldb_errstring(state->sam_ldb));
109 return NT_STATUS_INTERNAL_DB_CORRUPTION;
114 static NTSTATUS samsync_ldb_handle_domain(TALLOC_CTX *mem_ctx,
115 struct samsync_ldb_state *state,
116 enum netr_SamDatabaseID database,
117 struct netr_DELTA_ENUM *delta,
120 struct netr_DELTA_DOMAIN *domain = delta->delta_union.domain;
121 const char *domain_name = domain->domain_name.string;
122 struct ldb_message *msg;
125 msg = ldb_msg_new(mem_ctx);
127 return NT_STATUS_NO_MEMORY;
130 if (database == SAM_DATABASE_DOMAIN) {
131 struct ldb_dn *partitions_basedn;
132 const char *domain_attrs[] = {"nETBIOSName", "nCName", NULL};
133 struct ldb_message **msgs_domain;
136 partitions_basedn = samdb_partitions_dn(state->sam_ldb, mem_ctx);
138 ret_domain = gendb_search(state->sam_ldb, mem_ctx, partitions_basedn, &msgs_domain, domain_attrs,
139 "(&(&(nETBIOSName=%s)(objectclass=crossRef))(ncName=*))",
141 if (ret_domain == -1) {
142 *error_string = talloc_asprintf(mem_ctx, "gendb_search for domain failed: %s", ldb_errstring(state->sam_ldb));
143 return NT_STATUS_INTERNAL_DB_CORRUPTION;
146 if (ret_domain != 1) {
147 *error_string = talloc_asprintf(mem_ctx, "Failed to find existing domain record for %s: %d results", domain_name,
149 return NT_STATUS_NO_SUCH_DOMAIN;
152 state->base_dn[database] = samdb_result_dn(state->sam_ldb, state, msgs_domain[0], "nCName", NULL);
154 if (state->dom_sid[database]) {
155 /* Update the domain sid with the incoming
156 * domain (found on LSA pipe, database sid may
158 samdb_msg_add_dom_sid(state->sam_ldb, mem_ctx,
159 msg, "objectSid", state->dom_sid[database]);
161 /* Well, we will have to use the one from the database */
162 state->dom_sid[database] = samdb_search_dom_sid(state->sam_ldb, state,
163 state->base_dn[database],
167 if (state->samsync_state->domain_guid) {
170 status = GUID_to_ndr_blob(state->samsync_state->domain_guid, msg, &v);
171 if (!NT_STATUS_IS_OK(status)) {
172 *error_string = talloc_asprintf(mem_ctx, "ndr_push of domain GUID failed!");
176 ldb_msg_add_value(msg, "objectGUID", &v, NULL);
178 } else if (database == SAM_DATABASE_BUILTIN) {
179 /* work out the builtin_dn - useful for so many calls its worth
181 const char *dnstring = samdb_search_string(state->sam_ldb, mem_ctx, NULL,
182 "distinguishedName", "objectClass=builtinDomain");
183 state->base_dn[database] = ldb_dn_new(state, state->sam_ldb, dnstring);
184 if ( ! ldb_dn_validate(state->base_dn[database])) {
185 return NT_STATUS_INTERNAL_ERROR;
189 return NT_STATUS_INVALID_PARAMETER;
192 msg->dn = talloc_reference(mem_ctx, state->base_dn[database]);
194 return NT_STATUS_NO_MEMORY;
197 samdb_msg_add_string(state->sam_ldb, mem_ctx,
198 msg, "oEMInformation", domain->oem_information.string);
200 samdb_msg_add_int64(state->sam_ldb, mem_ctx,
201 msg, "forceLogoff", domain->force_logoff_time);
203 samdb_msg_add_uint(state->sam_ldb, mem_ctx,
204 msg, "minPwdLen", domain->min_password_length);
206 samdb_msg_add_int64(state->sam_ldb, mem_ctx,
207 msg, "maxPwdAge", domain->max_password_age);
209 samdb_msg_add_int64(state->sam_ldb, mem_ctx,
210 msg, "minPwdAge", domain->min_password_age);
212 samdb_msg_add_uint(state->sam_ldb, mem_ctx,
213 msg, "pwdHistoryLength", domain->password_history_length);
215 samdb_msg_add_uint64(state->sam_ldb, mem_ctx,
216 msg, "modifiedCount",
217 domain->sequence_num);
219 samdb_msg_add_uint64(state->sam_ldb, mem_ctx,
220 msg, "creationTime", domain->domain_create_time);
222 /* TODO: Account lockout, password properties */
224 ret = dsdb_replace(state->sam_ldb, msg, 0);
227 return NT_STATUS_INTERNAL_ERROR;
232 static NTSTATUS samsync_ldb_handle_user(TALLOC_CTX *mem_ctx,
233 struct samsync_ldb_state *state,
234 enum netr_SamDatabaseID database,
235 struct netr_DELTA_ENUM *delta,
238 uint32_t rid = delta->delta_id_union.rid;
239 struct netr_DELTA_USER *user = delta->delta_union.user;
240 const char *container, *obj_class;
243 const struct dom_sid *user_sid;
244 struct ldb_message *msg;
245 struct ldb_message **msgs;
246 struct ldb_message **remote_msgs = NULL;
251 const char *attrs[] = { NULL };
252 /* we may change this to a global search, then fill in only the things not in ldap later */
253 const char *remote_attrs[] = { "userPrincipalName", "servicePrincipalName",
254 "msDS-KeyVersionNumber", "objectGUID", NULL};
256 user_sid = dom_sid_add_rid(mem_ctx, state->dom_sid[database], rid);
258 return NT_STATUS_NO_MEMORY;
261 msg = ldb_msg_new(mem_ctx);
263 return NT_STATUS_NO_MEMORY;
267 /* search for the user, by rid */
268 ret = gendb_search(state->sam_ldb, mem_ctx, state->base_dn[database],
269 &msgs, attrs, "(&(objectClass=user)(objectSid=%s))",
270 ldap_encode_ndr_dom_sid(mem_ctx, user_sid));
273 *error_string = talloc_asprintf(mem_ctx, "LDB for user %s failed: %s",
274 dom_sid_string(mem_ctx, user_sid),
275 ldb_errstring(state->sam_ldb));
276 return NT_STATUS_INTERNAL_DB_CORRUPTION;
277 } else if (ret == 0) {
279 } else if (ret > 1) {
280 *error_string = talloc_asprintf(mem_ctx, "More than one user with SID: %s in local LDB",
281 dom_sid_string(mem_ctx, user_sid));
282 return NT_STATUS_INTERNAL_DB_CORRUPTION;
284 msg->dn = msgs[0]->dn;
285 talloc_steal(msg, msgs[0]->dn);
288 /* and do the same on the remote database */
289 if (state->remote_ldb) {
290 ret = gendb_search(state->remote_ldb, mem_ctx, state->base_dn[database],
291 &remote_msgs, remote_attrs, "(&(objectClass=user)(objectSid=%s))",
292 ldap_encode_ndr_dom_sid(mem_ctx, user_sid));
295 *error_string = talloc_asprintf(mem_ctx, "remote LDAP for user %s failed: %s",
296 dom_sid_string(mem_ctx, user_sid),
297 ldb_errstring(state->remote_ldb));
298 return NT_STATUS_INTERNAL_DB_CORRUPTION;
299 } else if (ret == 0) {
300 *error_string = talloc_asprintf(mem_ctx, "User exists in samsync but not in remote LDAP domain! (base: %s, SID: %s)",
301 ldb_dn_get_linearized(state->base_dn[database]),
302 dom_sid_string(mem_ctx, user_sid));
303 return NT_STATUS_NO_SUCH_USER;
304 } else if (ret > 1) {
305 *error_string = talloc_asprintf(mem_ctx, "More than one user in remote LDAP domain with SID: %s",
306 dom_sid_string(mem_ctx, user_sid));
307 return NT_STATUS_INTERNAL_DB_CORRUPTION;
309 /* Try to put things in the same location as the remote server */
311 msg->dn = remote_msgs[0]->dn;
312 talloc_steal(msg, remote_msgs[0]->dn);
316 cn_name = talloc_strdup(mem_ctx, user->account_name.string);
317 NT_STATUS_HAVE_NO_MEMORY(cn_name);
318 cn_name_len = strlen(cn_name);
320 #define ADD_OR_DEL(type, attrib, field) do { \
322 samdb_msg_add_ ## type(state->sam_ldb, mem_ctx, msg, \
323 attrib, user->field); \
325 samdb_msg_add_delete(state->sam_ldb, mem_ctx, msg, \
330 ADD_OR_DEL(string, "samAccountName", account_name.string);
331 ADD_OR_DEL(string, "displayName", full_name.string);
333 if (samdb_msg_add_dom_sid(state->sam_ldb, mem_ctx, msg,
334 "objectSid", dom_sid_add_rid(mem_ctx, state->dom_sid[database], rid))) {
335 return NT_STATUS_NO_MEMORY;
338 ADD_OR_DEL(uint, "primaryGroupID", primary_gid);
339 ADD_OR_DEL(string, "homeDirectory", home_directory.string);
340 ADD_OR_DEL(string, "homeDrive", home_drive.string);
341 ADD_OR_DEL(string, "scriptPath", logon_script.string);
342 ADD_OR_DEL(string, "description", description.string);
343 ADD_OR_DEL(string, "userWorkstations", workstations.string);
345 ADD_OR_DEL(uint64, "lastLogon", last_logon);
346 ADD_OR_DEL(uint64, "lastLogoff", last_logoff);
348 if (samdb_msg_add_logon_hours(state->sam_ldb, mem_ctx, msg, "logonHours", &user->logon_hours) != 0) {
349 return NT_STATUS_NO_MEMORY;
352 ADD_OR_DEL(uint, "badPwdCount", bad_password_count);
353 ADD_OR_DEL(uint, "logonCount", logon_count);
355 ADD_OR_DEL(uint64, "pwdLastSet", last_password_change);
356 ADD_OR_DEL(uint64, "accountExpires", acct_expiry);
358 if (samdb_msg_add_acct_flags(state->sam_ldb, mem_ctx, msg,
359 "userAccountControl", user->acct_flags) != 0) {
360 return NT_STATUS_NO_MEMORY;
363 if (user->lm_password_present) {
364 samdb_msg_add_hash(state->sam_ldb, mem_ctx, msg,
365 "dBCSPwd", &user->lmpassword);
367 samdb_msg_add_delete(state->sam_ldb, mem_ctx, msg,
370 if (user->nt_password_present) {
371 samdb_msg_add_hash(state->sam_ldb, mem_ctx, msg,
372 "unicodePwd", &user->ntpassword);
374 samdb_msg_add_delete(state->sam_ldb, mem_ctx, msg,
378 ADD_OR_DEL(string, "comment", comment.string);
380 if (samdb_msg_add_parameters(state->sam_ldb, mem_ctx, msg, "userParameters", &user->parameters) != 0) {
381 return NT_STATUS_NO_MEMORY;
384 ADD_OR_DEL(uint, "countryCode", country_code);
385 ADD_OR_DEL(uint, "codePage", code_page);
387 ADD_OR_DEL(string, "profilePath", profile_path.string);
391 for (i=0; remote_attrs[i]; i++) {
392 struct ldb_message_element *el = ldb_msg_find_element(remote_msgs[0], remote_attrs[i]);
394 samdb_msg_add_delete(state->sam_ldb, mem_ctx, msg,
397 ldb_msg_add(msg, el, LDB_FLAG_MOD_REPLACE);
401 acb = user->acct_flags;
402 if (acb & (ACB_WSTRUST)) {
403 cn_name[cn_name_len - 1] = '\0';
404 container = "Computers";
405 obj_class = "computer";
407 } else if (acb & ACB_SVRTRUST) {
408 if (cn_name[cn_name_len - 1] != '$') {
409 return NT_STATUS_FOOBAR;
411 cn_name[cn_name_len - 1] = '\0';
412 container = "Domain Controllers";
413 obj_class = "computer";
419 samdb_msg_add_string(state->sam_ldb, mem_ctx, msg,
420 "objectClass", obj_class);
422 msg->dn = ldb_dn_copy(mem_ctx, state->base_dn[database]);
423 ldb_dn_add_child_fmt(msg->dn, "CN=%s,CN=%s", cn_name, container);
425 return NT_STATUS_NO_MEMORY;
429 ret = ldb_add(state->sam_ldb, msg);
430 if (ret != LDB_SUCCESS) {
431 struct ldb_dn *first_try_dn = msg->dn;
432 /* Try again with the default DN */
434 *error_string = talloc_asprintf(mem_ctx, "Failed to create user record. Tried %s: %s",
435 ldb_dn_get_linearized(first_try_dn),
436 ldb_errstring(state->sam_ldb));
437 return NT_STATUS_INTERNAL_DB_CORRUPTION;
439 msg->dn = talloc_steal(msg, remote_msgs[0]->dn);
440 ret = ldb_add(state->sam_ldb, msg);
441 if (ret != LDB_SUCCESS) {
442 *error_string = talloc_asprintf(mem_ctx, "Failed to create user record. Tried both %s and %s: %s",
443 ldb_dn_get_linearized(first_try_dn),
444 ldb_dn_get_linearized(msg->dn),
445 ldb_errstring(state->sam_ldb));
446 return NT_STATUS_INTERNAL_DB_CORRUPTION;
451 ret = dsdb_replace(state->sam_ldb, msg, 0);
452 if (ret != LDB_SUCCESS) {
453 *error_string = talloc_asprintf(mem_ctx, "Failed to modify user record %s: %s",
454 ldb_dn_get_linearized(msg->dn),
455 ldb_errstring(state->sam_ldb));
456 return NT_STATUS_INTERNAL_DB_CORRUPTION;
463 static NTSTATUS samsync_ldb_delete_user(TALLOC_CTX *mem_ctx,
464 struct samsync_ldb_state *state,
465 enum netr_SamDatabaseID database,
466 struct netr_DELTA_ENUM *delta,
469 uint32_t rid = delta->delta_id_union.rid;
470 struct ldb_message **msgs;
472 const char *attrs[] = { NULL };
474 /* search for the user, by rid */
475 ret = gendb_search(state->sam_ldb, mem_ctx, state->base_dn[database],
476 &msgs, attrs, "(&(objectClass=user)(objectSid=%s))",
477 ldap_encode_ndr_dom_sid(mem_ctx, dom_sid_add_rid(mem_ctx, state->dom_sid[database], rid)));
480 *error_string = talloc_asprintf(mem_ctx, "gendb_search failed: %s", ldb_errstring(state->sam_ldb));
481 return NT_STATUS_INTERNAL_DB_CORRUPTION;
482 } else if (ret == 0) {
483 return NT_STATUS_NO_SUCH_USER;
484 } else if (ret > 1) {
485 *error_string = talloc_asprintf(mem_ctx, "More than one user with SID: %s",
486 dom_sid_string(mem_ctx,
487 dom_sid_add_rid(mem_ctx,
488 state->dom_sid[database],
490 return NT_STATUS_INTERNAL_DB_CORRUPTION;
493 ret = ldb_delete(state->sam_ldb, msgs[0]->dn);
494 if (ret != LDB_SUCCESS) {
495 *error_string = talloc_asprintf(mem_ctx, "Failed to delete user record %s: %s",
496 ldb_dn_get_linearized(msgs[0]->dn),
497 ldb_errstring(state->sam_ldb));
498 return NT_STATUS_INTERNAL_DB_CORRUPTION;
504 static NTSTATUS samsync_ldb_handle_group(TALLOC_CTX *mem_ctx,
505 struct samsync_ldb_state *state,
506 enum netr_SamDatabaseID database,
507 struct netr_DELTA_ENUM *delta,
510 uint32_t rid = delta->delta_id_union.rid;
511 struct netr_DELTA_GROUP *group = delta->delta_union.group;
512 const char *container, *obj_class;
515 struct ldb_message *msg;
516 struct ldb_message **msgs;
519 const char *attrs[] = { NULL };
521 msg = ldb_msg_new(mem_ctx);
523 return NT_STATUS_NO_MEMORY;
526 /* search for the group, by rid */
527 ret = gendb_search(state->sam_ldb, mem_ctx, state->base_dn[database], &msgs, attrs,
528 "(&(objectClass=group)(objectSid=%s))",
529 ldap_encode_ndr_dom_sid(mem_ctx, dom_sid_add_rid(mem_ctx, state->dom_sid[database], rid)));
532 *error_string = talloc_asprintf(mem_ctx, "gendb_search failed: %s", ldb_errstring(state->sam_ldb));
533 return NT_STATUS_INTERNAL_DB_CORRUPTION;
534 } else if (ret == 0) {
536 } else if (ret > 1) {
537 *error_string = talloc_asprintf(mem_ctx, "More than one group/alias with SID: %s",
538 dom_sid_string(mem_ctx,
539 dom_sid_add_rid(mem_ctx,
540 state->dom_sid[database],
542 return NT_STATUS_INTERNAL_DB_CORRUPTION;
544 msg->dn = talloc_steal(msg, msgs[0]->dn);
547 cn_name = group->group_name.string;
549 #define ADD_OR_DEL(type, attrib, field) do { \
550 if (group->field) { \
551 samdb_msg_add_ ## type(state->sam_ldb, mem_ctx, msg, \
552 attrib, group->field); \
554 samdb_msg_add_delete(state->sam_ldb, mem_ctx, msg, \
559 ADD_OR_DEL(string, "samAccountName", group_name.string);
561 if (samdb_msg_add_dom_sid(state->sam_ldb, mem_ctx, msg,
562 "objectSid", dom_sid_add_rid(mem_ctx, state->dom_sid[database], rid))) {
563 return NT_STATUS_NO_MEMORY;
566 ADD_OR_DEL(string, "description", description.string);
574 samdb_msg_add_string(state->sam_ldb, mem_ctx, msg,
575 "objectClass", obj_class);
576 msg->dn = ldb_dn_copy(mem_ctx, state->base_dn[database]);
577 ldb_dn_add_child_fmt(msg->dn, "CN=%s,CN=%s", cn_name, container);
579 return NT_STATUS_NO_MEMORY;
582 ret = ldb_add(state->sam_ldb, msg);
583 if (ret != LDB_SUCCESS) {
584 *error_string = talloc_asprintf(mem_ctx, "Failed to create group record %s: %s",
585 ldb_dn_get_linearized(msg->dn),
586 ldb_errstring(state->sam_ldb));
587 return NT_STATUS_INTERNAL_DB_CORRUPTION;
590 ret = dsdb_replace(state->sam_ldb, msg, 0);
591 if (ret != LDB_SUCCESS) {
592 *error_string = talloc_asprintf(mem_ctx, "Failed to modify group record %s: %s",
593 ldb_dn_get_linearized(msg->dn),
594 ldb_errstring(state->sam_ldb));
595 return NT_STATUS_INTERNAL_DB_CORRUPTION;
602 static NTSTATUS samsync_ldb_delete_group(TALLOC_CTX *mem_ctx,
603 struct samsync_ldb_state *state,
604 enum netr_SamDatabaseID database,
605 struct netr_DELTA_ENUM *delta,
608 uint32_t rid = delta->delta_id_union.rid;
609 struct ldb_message **msgs;
611 const char *attrs[] = { NULL };
613 /* search for the group, by rid */
614 ret = gendb_search(state->sam_ldb, mem_ctx, state->base_dn[database], &msgs, attrs,
615 "(&(objectClass=group)(objectSid=%s))",
616 ldap_encode_ndr_dom_sid(mem_ctx, dom_sid_add_rid(mem_ctx, state->dom_sid[database], rid)));
619 *error_string = talloc_asprintf(mem_ctx, "gendb_search failed: %s", ldb_errstring(state->sam_ldb));
620 return NT_STATUS_INTERNAL_DB_CORRUPTION;
621 } else if (ret == 0) {
622 return NT_STATUS_NO_SUCH_GROUP;
623 } else if (ret > 1) {
624 *error_string = talloc_asprintf(mem_ctx, "More than one group/alias with SID: %s",
625 dom_sid_string(mem_ctx,
626 dom_sid_add_rid(mem_ctx,
627 state->dom_sid[database],
629 return NT_STATUS_INTERNAL_DB_CORRUPTION;
632 ret = ldb_delete(state->sam_ldb, msgs[0]->dn);
633 if (ret != LDB_SUCCESS) {
634 *error_string = talloc_asprintf(mem_ctx, "Failed to delete group record %s: %s",
635 ldb_dn_get_linearized(msgs[0]->dn),
636 ldb_errstring(state->sam_ldb));
637 return NT_STATUS_INTERNAL_DB_CORRUPTION;
643 static NTSTATUS samsync_ldb_handle_group_member(TALLOC_CTX *mem_ctx,
644 struct samsync_ldb_state *state,
645 enum netr_SamDatabaseID database,
646 struct netr_DELTA_ENUM *delta,
649 uint32_t rid = delta->delta_id_union.rid;
650 struct netr_DELTA_GROUP_MEMBER *group_member = delta->delta_union.group_member;
651 struct ldb_message *msg;
652 struct ldb_message **msgs;
654 const char *attrs[] = { NULL };
658 msg = ldb_msg_new(mem_ctx);
660 return NT_STATUS_NO_MEMORY;
663 /* search for the group, by rid */
664 ret = gendb_search(state->sam_ldb, mem_ctx, state->base_dn[database], &msgs, attrs,
665 "(&(objectClass=group)(objectSid=%s))",
666 ldap_encode_ndr_dom_sid(mem_ctx, dom_sid_add_rid(mem_ctx, state->dom_sid[database], rid)));
669 *error_string = talloc_asprintf(mem_ctx, "gendb_search failed: %s", ldb_errstring(state->sam_ldb));
670 return NT_STATUS_INTERNAL_DB_CORRUPTION;
671 } else if (ret == 0) {
672 return NT_STATUS_NO_SUCH_GROUP;
673 } else if (ret > 1) {
674 *error_string = talloc_asprintf(mem_ctx, "More than one group/alias with SID: %s",
675 dom_sid_string(mem_ctx,
676 dom_sid_add_rid(mem_ctx,
677 state->dom_sid[database],
679 return NT_STATUS_INTERNAL_DB_CORRUPTION;
681 msg->dn = talloc_steal(msg, msgs[0]->dn);
686 for (i=0; i<group_member->num_rids; i++) {
687 /* search for the group, by rid */
688 ret = gendb_search(state->sam_ldb, mem_ctx, state->base_dn[database], &msgs, attrs,
689 "(&(objectClass=user)(objectSid=%s))",
690 ldap_encode_ndr_dom_sid(mem_ctx, dom_sid_add_rid(mem_ctx, state->dom_sid[database], group_member->rids[i])));
693 *error_string = talloc_asprintf(mem_ctx, "gendb_search failed: %s", ldb_errstring(state->sam_ldb));
694 return NT_STATUS_INTERNAL_DB_CORRUPTION;
695 } else if (ret == 0) {
696 return NT_STATUS_NO_SUCH_USER;
697 } else if (ret > 1) {
698 return NT_STATUS_INTERNAL_DB_CORRUPTION;
700 str_dn = ldb_dn_alloc_linearized(msg, msgs[0]->dn);
701 NT_STATUS_HAVE_NO_MEMORY(str_dn);
702 ret = ldb_msg_add_string(msg, "member", str_dn);
703 if (ret != LDB_SUCCESS) return NT_STATUS_NO_MEMORY;
709 ret = dsdb_replace(state->sam_ldb, msg, 0);
710 if (ret != LDB_SUCCESS) {
711 *error_string = talloc_asprintf(mem_ctx, "Failed to modify group record %s: %s",
712 ldb_dn_get_linearized(msg->dn),
713 ldb_errstring(state->sam_ldb));
714 return NT_STATUS_INTERNAL_DB_CORRUPTION;
720 static NTSTATUS samsync_ldb_handle_alias(TALLOC_CTX *mem_ctx,
721 struct samsync_ldb_state *state,
722 enum netr_SamDatabaseID database,
723 struct netr_DELTA_ENUM *delta,
726 uint32_t rid = delta->delta_id_union.rid;
727 struct netr_DELTA_ALIAS *alias = delta->delta_union.alias;
728 const char *container, *obj_class;
731 struct ldb_message *msg;
732 struct ldb_message **msgs;
735 const char *attrs[] = { NULL };
737 msg = ldb_msg_new(mem_ctx);
739 return NT_STATUS_NO_MEMORY;
742 /* search for the alias, by rid */
743 ret = gendb_search(state->sam_ldb, mem_ctx, state->base_dn[database], &msgs, attrs,
744 "(&(objectClass=group)(objectSid=%s))",
745 ldap_encode_ndr_dom_sid(mem_ctx, dom_sid_add_rid(mem_ctx, state->dom_sid[database], rid)));
748 *error_string = talloc_asprintf(mem_ctx, "gendb_search failed: %s", ldb_errstring(state->sam_ldb));
749 return NT_STATUS_INTERNAL_DB_CORRUPTION;
750 } else if (ret == 0) {
752 } else if (ret > 1) {
753 *error_string = talloc_asprintf(mem_ctx, "More than one group/alias with SID: %s",
754 dom_sid_string(mem_ctx,
755 dom_sid_add_rid(mem_ctx,
756 state->dom_sid[database],
758 return NT_STATUS_INTERNAL_DB_CORRUPTION;
760 msg->dn = talloc_steal(mem_ctx, msgs[0]->dn);
763 cn_name = alias->alias_name.string;
765 #define ADD_OR_DEL(type, attrib, field) do { \
766 if (alias->field) { \
767 samdb_msg_add_ ## type(state->sam_ldb, mem_ctx, msg, \
768 attrib, alias->field); \
770 samdb_msg_add_delete(state->sam_ldb, mem_ctx, msg, \
775 ADD_OR_DEL(string, "samAccountName", alias_name.string);
777 if (samdb_msg_add_dom_sid(state->sam_ldb, mem_ctx, msg,
778 "objectSid", dom_sid_add_rid(mem_ctx, state->dom_sid[database], rid))) {
779 return NT_STATUS_NO_MEMORY;
782 ADD_OR_DEL(string, "description", description.string);
786 samdb_msg_add_uint(state->sam_ldb, mem_ctx, msg, "groupType", 0x80000004);
792 samdb_msg_add_string(state->sam_ldb, mem_ctx, msg,
793 "objectClass", obj_class);
794 msg->dn = ldb_dn_copy(mem_ctx, state->base_dn[database]);
795 ldb_dn_add_child_fmt(msg->dn, "CN=%s,CN=%s", cn_name, container);
797 return NT_STATUS_NO_MEMORY;
800 ret = ldb_add(state->sam_ldb, msg);
801 if (ret != LDB_SUCCESS) {
802 *error_string = talloc_asprintf(mem_ctx, "Failed to create alias record %s: %s",
803 ldb_dn_get_linearized(msg->dn),
804 ldb_errstring(state->sam_ldb));
805 return NT_STATUS_INTERNAL_DB_CORRUPTION;
808 ret = dsdb_replace(state->sam_ldb, msg, 0);
809 if (ret != LDB_SUCCESS) {
810 *error_string = talloc_asprintf(mem_ctx, "Failed to modify alias record %s: %s",
811 ldb_dn_get_linearized(msg->dn),
812 ldb_errstring(state->sam_ldb));
813 return NT_STATUS_INTERNAL_DB_CORRUPTION;
820 static NTSTATUS samsync_ldb_delete_alias(TALLOC_CTX *mem_ctx,
821 struct samsync_ldb_state *state,
822 enum netr_SamDatabaseID database,
823 struct netr_DELTA_ENUM *delta,
826 uint32_t rid = delta->delta_id_union.rid;
827 struct ldb_message **msgs;
829 const char *attrs[] = { NULL };
831 /* search for the alias, by rid */
832 ret = gendb_search(state->sam_ldb, mem_ctx, state->base_dn[database], &msgs, attrs,
833 "(&(objectClass=group)(objectSid=%s))",
834 ldap_encode_ndr_dom_sid(mem_ctx, dom_sid_add_rid(mem_ctx, state->dom_sid[database], rid)));
837 *error_string = talloc_asprintf(mem_ctx, "gendb_search failed: %s", ldb_errstring(state->sam_ldb));
838 return NT_STATUS_INTERNAL_DB_CORRUPTION;
839 } else if (ret == 0) {
840 return NT_STATUS_NO_SUCH_ALIAS;
841 } else if (ret > 1) {
842 return NT_STATUS_INTERNAL_DB_CORRUPTION;
845 ret = ldb_delete(state->sam_ldb, msgs[0]->dn);
846 if (ret != LDB_SUCCESS) {
847 *error_string = talloc_asprintf(mem_ctx, "Failed to delete alias record %s: %s",
848 ldb_dn_get_linearized(msgs[0]->dn),
849 ldb_errstring(state->sam_ldb));
850 return NT_STATUS_INTERNAL_DB_CORRUPTION;
856 static NTSTATUS samsync_ldb_handle_alias_member(TALLOC_CTX *mem_ctx,
857 struct samsync_ldb_state *state,
858 enum netr_SamDatabaseID database,
859 struct netr_DELTA_ENUM *delta,
862 uint32_t rid = delta->delta_id_union.rid;
863 struct netr_DELTA_ALIAS_MEMBER *alias_member = delta->delta_union.alias_member;
864 struct ldb_message *msg;
865 struct ldb_message **msgs;
867 const char *attrs[] = { NULL };
870 msg = ldb_msg_new(mem_ctx);
872 return NT_STATUS_NO_MEMORY;
875 /* search for the alias, by rid */
876 ret = gendb_search(state->sam_ldb, mem_ctx, state->base_dn[database], &msgs, attrs,
877 "(&(objectClass=group)(objectSid=%s))",
878 ldap_encode_ndr_dom_sid(mem_ctx, dom_sid_add_rid(mem_ctx, state->dom_sid[database], rid)));
881 *error_string = talloc_asprintf(mem_ctx, "gendb_search failed: %s", ldb_errstring(state->sam_ldb));
882 return NT_STATUS_INTERNAL_DB_CORRUPTION;
883 } else if (ret == 0) {
884 return NT_STATUS_NO_SUCH_GROUP;
885 } else if (ret > 1) {
886 *error_string = talloc_asprintf(mem_ctx, "More than one group/alias with SID: %s",
887 dom_sid_string(mem_ctx,
888 dom_sid_add_rid(mem_ctx,
889 state->dom_sid[database],
891 return NT_STATUS_INTERNAL_DB_CORRUPTION;
893 msg->dn = talloc_steal(msg, msgs[0]->dn);
898 for (i=0; i<alias_member->sids.num_sids; i++) {
899 struct ldb_dn *alias_member_dn;
901 /* search for members, in the top basedn (normal users are builtin aliases) */
902 ret = gendb_search(state->sam_ldb, mem_ctx, state->base_dn[SAM_DATABASE_DOMAIN], &msgs, attrs,
904 ldap_encode_ndr_dom_sid(mem_ctx, alias_member->sids.sids[i].sid));
907 *error_string = talloc_asprintf(mem_ctx, "gendb_search failed: %s", ldb_errstring(state->sam_ldb));
908 return NT_STATUS_INTERNAL_DB_CORRUPTION;
909 } else if (ret == 0) {
911 nt_status = samsync_ldb_add_foreignSecurityPrincipal(mem_ctx, state,
912 alias_member->sids.sids[i].sid,
915 if (!NT_STATUS_IS_OK(nt_status)) {
918 } else if (ret > 1) {
919 return NT_STATUS_INTERNAL_DB_CORRUPTION;
921 alias_member_dn = msgs[0]->dn;
923 str_dn = ldb_dn_alloc_linearized(msg, alias_member_dn);
924 NT_STATUS_HAVE_NO_MEMORY(str_dn);
925 ret = ldb_msg_add_string(msg, "member", str_dn);
926 if (ret != LDB_SUCCESS) return NT_STATUS_NO_MEMORY;
931 ret = dsdb_replace(state->sam_ldb, msg, 0);
932 if (ret != LDB_SUCCESS) {
933 *error_string = talloc_asprintf(mem_ctx, "Failed to modify group record %s: %s",
934 ldb_dn_get_linearized(msg->dn),
935 ldb_errstring(state->sam_ldb));
936 return NT_STATUS_INTERNAL_DB_CORRUPTION;
942 static NTSTATUS samsync_ldb_handle_account(TALLOC_CTX *mem_ctx,
943 struct samsync_ldb_state *state,
944 enum netr_SamDatabaseID database,
945 struct netr_DELTA_ENUM *delta,
948 struct dom_sid *sid = delta->delta_id_union.sid;
949 struct netr_DELTA_ACCOUNT *account = delta->delta_union.account;
951 struct ldb_message *msg;
954 char *dnstr, *sidstr;
956 msg = ldb_msg_new(mem_ctx);
958 return NT_STATUS_NO_MEMORY;
961 sidstr = dom_sid_string(msg, sid);
962 NT_STATUS_HAVE_NO_MEMORY_AND_FREE(sidstr, msg);
964 dnstr = talloc_asprintf(msg, "sid=%s", sidstr);
965 NT_STATUS_HAVE_NO_MEMORY_AND_FREE(dnstr, msg);
967 msg->dn = ldb_dn_new(msg, state->pdb, dnstr);
968 NT_STATUS_HAVE_NO_MEMORY_AND_FREE(msg->dn, msg);
970 for (i=0; i< account->privilege_entries; i++) {
971 samdb_msg_add_string(state->pdb, mem_ctx, msg, "privilege",
972 account->privilege_name[i].string);
975 ret = dsdb_replace(state->pdb, msg, 0);
976 if (ret == LDB_ERR_NO_SUCH_OBJECT) {
977 if (samdb_msg_add_dom_sid(state->pdb, msg, msg, "objectSid", sid) != LDB_SUCCESS) {
979 return NT_STATUS_NO_MEMORY;
981 samdb_msg_add_string(state->pdb, msg, msg, "comment", "added via samsync");
982 ret = ldb_add(state->pdb, msg);
985 if (ret != LDB_SUCCESS) {
986 *error_string = talloc_asprintf(mem_ctx, "Failed to modify privilege record %s",
987 ldb_dn_get_linearized(msg->dn));
988 return NT_STATUS_INTERNAL_DB_CORRUPTION;
994 static NTSTATUS samsync_ldb_delete_account(TALLOC_CTX *mem_ctx,
995 struct samsync_ldb_state *state,
996 enum netr_SamDatabaseID database,
997 struct netr_DELTA_ENUM *delta,
1000 struct dom_sid *sid = delta->delta_id_union.sid;
1002 struct ldb_message *msg;
1003 struct ldb_message **msgs;
1005 const char *attrs[] = { NULL };
1007 msg = ldb_msg_new(mem_ctx);
1009 return NT_STATUS_NO_MEMORY;
1012 /* search for the account, by sid, in the top basedn */
1013 ret = gendb_search(state->sam_ldb, mem_ctx, state->base_dn[SAM_DATABASE_DOMAIN], &msgs, attrs,
1015 ldap_encode_ndr_dom_sid(mem_ctx, sid));
1018 *error_string = talloc_asprintf(mem_ctx, "gendb_search failed: %s", ldb_errstring(state->sam_ldb));
1019 return NT_STATUS_INTERNAL_DB_CORRUPTION;
1020 } else if (ret == 0) {
1021 return NT_STATUS_NO_SUCH_USER;
1022 } else if (ret > 1) {
1023 *error_string = talloc_asprintf(mem_ctx, "More than one account with SID: %s",
1024 dom_sid_string(mem_ctx, sid));
1025 return NT_STATUS_INTERNAL_DB_CORRUPTION;
1027 msg->dn = talloc_steal(msg, msgs[0]->dn);
1030 samdb_msg_add_delete(state->sam_ldb, mem_ctx, msg,
1033 ret = dsdb_replace(state->sam_ldb, msg, 0);
1034 if (ret != LDB_SUCCESS) {
1035 *error_string = talloc_asprintf(mem_ctx, "Failed to modify privilege record %s",
1036 ldb_dn_get_linearized(msg->dn));
1037 return NT_STATUS_INTERNAL_DB_CORRUPTION;
1040 return NT_STATUS_OK;
1043 static NTSTATUS libnet_samsync_ldb_fn(TALLOC_CTX *mem_ctx,
1045 enum netr_SamDatabaseID database,
1046 struct netr_DELTA_ENUM *delta,
1047 char **error_string)
1049 NTSTATUS nt_status = NT_STATUS_OK;
1050 struct samsync_ldb_state *state = talloc_get_type(private_data, struct samsync_ldb_state);
1052 *error_string = NULL;
1053 switch (delta->delta_type) {
1054 case NETR_DELTA_DOMAIN:
1056 nt_status = samsync_ldb_handle_domain(mem_ctx,
1063 case NETR_DELTA_USER:
1065 nt_status = samsync_ldb_handle_user(mem_ctx,
1072 case NETR_DELTA_DELETE_USER:
1074 nt_status = samsync_ldb_delete_user(mem_ctx,
1081 case NETR_DELTA_GROUP:
1083 nt_status = samsync_ldb_handle_group(mem_ctx,
1090 case NETR_DELTA_DELETE_GROUP:
1092 nt_status = samsync_ldb_delete_group(mem_ctx,
1099 case NETR_DELTA_GROUP_MEMBER:
1101 nt_status = samsync_ldb_handle_group_member(mem_ctx,
1108 case NETR_DELTA_ALIAS:
1110 nt_status = samsync_ldb_handle_alias(mem_ctx,
1117 case NETR_DELTA_DELETE_ALIAS:
1119 nt_status = samsync_ldb_delete_alias(mem_ctx,
1126 case NETR_DELTA_ALIAS_MEMBER:
1128 nt_status = samsync_ldb_handle_alias_member(mem_ctx,
1135 case NETR_DELTA_ACCOUNT:
1137 nt_status = samsync_ldb_handle_account(mem_ctx,
1144 case NETR_DELTA_DELETE_ACCOUNT:
1146 nt_status = samsync_ldb_delete_account(mem_ctx,
1154 /* Can't dump them all right now */
1157 if (!NT_STATUS_IS_OK(nt_status) && !*error_string) {
1158 *error_string = talloc_asprintf(mem_ctx, "Failed to handle samsync delta: %s", nt_errstr(nt_status));
1163 static NTSTATUS libnet_samsync_ldb_init(TALLOC_CTX *mem_ctx,
1165 struct libnet_SamSync_state *samsync_state,
1166 char **error_string)
1168 struct samsync_ldb_state *state = talloc_get_type(private_data, struct samsync_ldb_state);
1169 const char *server = dcerpc_server_name(samsync_state->netlogon_pipe);
1172 state->samsync_state = samsync_state;
1174 ZERO_STRUCT(state->dom_sid);
1175 if (state->samsync_state->domain_sid) {
1176 state->dom_sid[SAM_DATABASE_DOMAIN] = dom_sid_dup(state, state->samsync_state->domain_sid);
1179 state->dom_sid[SAM_DATABASE_BUILTIN] = dom_sid_parse_talloc(state, SID_BUILTIN);
1181 if (state->samsync_state->realm) {
1182 if (!server || !*server) {
1183 /* huh? how do we not have a server name? */
1184 *error_string = talloc_strdup(mem_ctx, "No DCE/RPC server name available. How did we connect?");
1185 return NT_STATUS_INVALID_PARAMETER;
1187 ldap_url = talloc_asprintf(state, "ldap://%s", server);
1189 state->remote_ldb = ldb_wrap_connect(mem_ctx,
1190 state->samsync_state->machine_net_ctx->event_ctx,
1191 state->samsync_state->machine_net_ctx->lp_ctx,
1193 NULL, state->samsync_state->machine_net_ctx->cred,
1195 if (!state->remote_ldb) {
1196 *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);
1197 return NT_STATUS_NO_LOGON_SERVERS;
1200 state->remote_ldb = NULL;
1202 return NT_STATUS_OK;
1205 NTSTATUS libnet_samsync_ldb(struct libnet_context *ctx, TALLOC_CTX *mem_ctx, struct libnet_samsync_ldb *r)
1208 struct libnet_SamSync r2;
1209 struct samsync_ldb_state *state = talloc(mem_ctx, struct samsync_ldb_state);
1212 return NT_STATUS_NO_MEMORY;
1215 state->secrets = NULL;
1216 state->trusted_domains = NULL;
1218 state->sam_ldb = samdb_connect(mem_ctx,
1223 if (!state->sam_ldb) {
1224 return NT_STATUS_INTERNAL_DB_ERROR;
1227 state->pdb = privilege_connect(mem_ctx,
1231 return NT_STATUS_INTERNAL_DB_ERROR;
1234 r2.out.error_string = NULL;
1235 r2.in.binding_string = r->in.binding_string;
1236 r2.in.init_fn = libnet_samsync_ldb_init;
1237 r2.in.delta_fn = libnet_samsync_ldb_fn;
1238 r2.in.fn_ctx = state;
1239 r2.in.machine_account = NULL; /* TODO: Create a machine account, fill this in, and the delete it */
1240 nt_status = libnet_SamSync_netlogon(ctx, state, &r2);
1241 r->out.error_string = r2.out.error_string;
1242 talloc_steal(mem_ctx, r->out.error_string);
1244 if (!NT_STATUS_IS_OK(nt_status)) {