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 "librpc/rpc/dcerpc.h"
35 #include "param/param.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_get_linearized(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 */
94 if ( ! ldb_dn_add_child_fmt(msg->dn, "CN=%s", sidstr))
95 return NT_STATUS_UNSUCCESSFUL;
97 samdb_msg_add_string(state->sam_ldb, mem_ctx, msg,
99 "foreignSecurityPrincipal");
103 /* create the alias */
104 ret = ldb_add(state->sam_ldb, msg);
106 *error_string = talloc_asprintf(mem_ctx, "Failed to create foreignSecurityPrincipal "
108 ldb_dn_get_linearized(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 struct ldb_dn *partitions_basedn;
133 const char *domain_attrs[] = {"nETBIOSName", "nCName", NULL};
134 struct ldb_message **msgs_domain;
137 partitions_basedn = samdb_partitions_dn(state->sam_ldb, mem_ctx);
139 ret_domain = gendb_search(state->sam_ldb, mem_ctx, partitions_basedn, &msgs_domain, domain_attrs,
140 "(&(&(nETBIOSName=%s)(objectclass=crossRef))(ncName=*))",
142 if (ret_domain == -1) {
143 *error_string = talloc_asprintf(mem_ctx, "gendb_search for domain failed: %s", ldb_errstring(state->sam_ldb));
144 return NT_STATUS_INTERNAL_DB_CORRUPTION;
147 if (ret_domain != 1) {
148 *error_string = talloc_asprintf(mem_ctx, "Failed to find existing domain record for %s: %d results", domain_name,
150 return NT_STATUS_NO_SUCH_DOMAIN;
153 state->base_dn[database] = samdb_result_dn(state->sam_ldb, state, msgs_domain[0], "nCName", NULL);
155 if (state->dom_sid[database]) {
156 /* Update the domain sid with the incoming
157 * domain (found on LSA pipe, database sid may
159 samdb_msg_add_dom_sid(state->sam_ldb, mem_ctx,
160 msg, "objectSid", state->dom_sid[database]);
162 /* Well, we will have to use the one from the database */
163 state->dom_sid[database] = samdb_search_dom_sid(state->sam_ldb, state,
164 state->base_dn[database],
168 if (state->samsync_state->domain_guid) {
169 enum ndr_err_code ndr_err;
171 ndr_err = ndr_push_struct_blob(&v, msg, NULL,
172 state->samsync_state->domain_guid,
173 (ndr_push_flags_fn_t)ndr_push_GUID);
174 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
175 *error_string = talloc_asprintf(mem_ctx, "ndr_push of domain GUID failed!");
176 return ndr_map_error2ntstatus(ndr_err);
179 ldb_msg_add_value(msg, "objectGUID", &v, NULL);
181 } else if (database == SAM_DATABASE_BUILTIN) {
182 /* work out the builtin_dn - useful for so many calls its worth
184 const char *dnstring = samdb_search_string(state->sam_ldb, mem_ctx, NULL,
185 "distinguishedName", "objectClass=builtinDomain");
186 state->base_dn[database] = ldb_dn_new(state, state->sam_ldb, dnstring);
187 if ( ! ldb_dn_validate(state->base_dn[database])) {
188 return NT_STATUS_INTERNAL_ERROR;
192 return NT_STATUS_INVALID_PARAMETER;
195 msg->dn = talloc_reference(mem_ctx, state->base_dn[database]);
197 return NT_STATUS_NO_MEMORY;
200 samdb_msg_add_string(state->sam_ldb, mem_ctx,
201 msg, "oEMInformation", domain->oem_information.string);
203 samdb_msg_add_int64(state->sam_ldb, mem_ctx,
204 msg, "forceLogoff", domain->force_logoff_time);
206 samdb_msg_add_uint(state->sam_ldb, mem_ctx,
207 msg, "minPwdLen", domain->min_password_length);
209 samdb_msg_add_int64(state->sam_ldb, mem_ctx,
210 msg, "maxPwdAge", domain->max_password_age);
212 samdb_msg_add_int64(state->sam_ldb, mem_ctx,
213 msg, "minPwdAge", domain->min_password_age);
215 samdb_msg_add_uint(state->sam_ldb, mem_ctx,
216 msg, "pwdHistoryLength", domain->password_history_length);
218 samdb_msg_add_uint64(state->sam_ldb, mem_ctx,
219 msg, "modifiedCount",
220 domain->sequence_num);
222 samdb_msg_add_uint64(state->sam_ldb, mem_ctx,
223 msg, "creationTime", domain->domain_create_time);
225 /* TODO: Account lockout, password properties */
227 ret = samdb_replace(state->sam_ldb, mem_ctx, msg);
230 return NT_STATUS_INTERNAL_ERROR;
235 static NTSTATUS samsync_ldb_handle_user(TALLOC_CTX *mem_ctx,
236 struct samsync_ldb_state *state,
237 enum netr_SamDatabaseID database,
238 struct netr_DELTA_ENUM *delta,
241 uint32_t rid = delta->delta_id_union.rid;
242 struct netr_DELTA_USER *user = delta->delta_union.user;
243 const char *container, *obj_class;
246 const struct dom_sid *user_sid;
247 struct ldb_message *msg;
248 struct ldb_message **msgs;
249 struct ldb_message **remote_msgs = NULL;
253 const char *attrs[] = { NULL };
254 /* we may change this to a global search, then fill in only the things not in ldap later */
255 const char *remote_attrs[] = { "userPrincipalName", "servicePrincipalName",
256 "msDS-KeyVersionNumber", "objectGUID", NULL};
258 user_sid = dom_sid_add_rid(mem_ctx, state->dom_sid[database], rid);
260 return NT_STATUS_NO_MEMORY;
263 msg = ldb_msg_new(mem_ctx);
265 return NT_STATUS_NO_MEMORY;
269 /* search for the user, by rid */
270 ret = gendb_search(state->sam_ldb, mem_ctx, state->base_dn[database],
271 &msgs, attrs, "(&(objectClass=user)(objectSid=%s))",
272 ldap_encode_ndr_dom_sid(mem_ctx, user_sid));
275 *error_string = talloc_asprintf(mem_ctx, "LDB for user %s failed: %s",
276 dom_sid_string(mem_ctx, user_sid),
277 ldb_errstring(state->sam_ldb));
278 return NT_STATUS_INTERNAL_DB_CORRUPTION;
279 } else if (ret == 0) {
281 } else if (ret > 1) {
282 *error_string = talloc_asprintf(mem_ctx, "More than one user with SID: %s in local LDB",
283 dom_sid_string(mem_ctx, user_sid));
284 return NT_STATUS_INTERNAL_DB_CORRUPTION;
286 msg->dn = msgs[0]->dn;
287 talloc_steal(msg, msgs[0]->dn);
290 /* and do the same on the remote database */
291 if (state->remote_ldb) {
292 ret = gendb_search(state->remote_ldb, mem_ctx, state->base_dn[database],
293 &remote_msgs, remote_attrs, "(&(objectClass=user)(objectSid=%s))",
294 ldap_encode_ndr_dom_sid(mem_ctx, user_sid));
297 *error_string = talloc_asprintf(mem_ctx, "remote LDAP for user %s failed: %s",
298 dom_sid_string(mem_ctx, user_sid),
299 ldb_errstring(state->remote_ldb));
300 return NT_STATUS_INTERNAL_DB_CORRUPTION;
301 } else if (ret == 0) {
302 *error_string = talloc_asprintf(mem_ctx, "User exists in samsync but not in remote LDAP domain! (base: %s, SID: %s)",
303 ldb_dn_get_linearized(state->base_dn[database]),
304 dom_sid_string(mem_ctx, user_sid));
305 return NT_STATUS_NO_SUCH_USER;
306 } else if (ret > 1) {
307 *error_string = talloc_asprintf(mem_ctx, "More than one user in remote LDAP domain with SID: %s",
308 dom_sid_string(mem_ctx, user_sid));
309 return NT_STATUS_INTERNAL_DB_CORRUPTION;
311 /* Try to put things in the same location as the remote server */
313 msg->dn = remote_msgs[0]->dn;
314 talloc_steal(msg, remote_msgs[0]->dn);
318 cn_name = talloc_strdup(mem_ctx, user->account_name.string);
319 NT_STATUS_HAVE_NO_MEMORY(cn_name);
320 cn_name_len = strlen(cn_name);
322 #define ADD_OR_DEL(type, attrib, field) do { \
324 samdb_msg_add_ ## type(state->sam_ldb, mem_ctx, msg, \
325 attrib, user->field); \
327 samdb_msg_add_delete(state->sam_ldb, mem_ctx, msg, \
332 ADD_OR_DEL(string, "samAccountName", account_name.string);
333 ADD_OR_DEL(string, "displayName", full_name.string);
335 if (samdb_msg_add_dom_sid(state->sam_ldb, mem_ctx, msg,
336 "objectSid", dom_sid_add_rid(mem_ctx, state->dom_sid[database], rid))) {
337 return NT_STATUS_NO_MEMORY;
340 ADD_OR_DEL(uint, "primaryGroupID", primary_gid);
341 ADD_OR_DEL(string, "homeDirectory", home_directory.string);
342 ADD_OR_DEL(string, "homeDrive", home_drive.string);
343 ADD_OR_DEL(string, "scriptPath", logon_script.string);
344 ADD_OR_DEL(string, "description", description.string);
345 ADD_OR_DEL(string, "userWorkstations", workstations.string);
347 ADD_OR_DEL(uint64, "lastLogon", last_logon);
348 ADD_OR_DEL(uint64, "lastLogoff", last_logoff);
350 if (samdb_msg_add_logon_hours(state->sam_ldb, mem_ctx, msg, "logonHours", &user->logon_hours) != 0) {
351 return NT_STATUS_NO_MEMORY;
354 ADD_OR_DEL(uint, "badPwdCount", bad_password_count);
355 ADD_OR_DEL(uint, "logonCount", logon_count);
357 ADD_OR_DEL(uint64, "pwdLastSet", last_password_change);
358 ADD_OR_DEL(uint64, "accountExpires", acct_expiry);
360 if (samdb_msg_add_acct_flags(state->sam_ldb, mem_ctx, msg,
361 "userAccountControl", user->acct_flags) != 0) {
362 return NT_STATUS_NO_MEMORY;
366 /* Passwords. Ensure there is no plaintext stored against
367 * this entry, as we only have hashes */
368 samdb_msg_add_delete(state->sam_ldb, mem_ctx, msg,
371 if (user->lm_password_present) {
372 samdb_msg_add_hash(state->sam_ldb, mem_ctx, msg,
373 "dBCSPwd", &user->lmpassword);
375 samdb_msg_add_delete(state->sam_ldb, mem_ctx, msg,
378 if (user->nt_password_present) {
379 samdb_msg_add_hash(state->sam_ldb, mem_ctx, msg,
380 "unicodePwd", &user->ntpassword);
382 samdb_msg_add_delete(state->sam_ldb, mem_ctx, msg,
386 ADD_OR_DEL(string, "comment", comment.string);
387 ADD_OR_DEL(string, "userParameters", parameters.string);
388 ADD_OR_DEL(uint, "countryCode", country_code);
389 ADD_OR_DEL(uint, "codePage", code_page);
391 ADD_OR_DEL(string, "profilePath", profile_path.string);
395 for (i=0; remote_attrs[i]; i++) {
396 struct ldb_message_element *el = ldb_msg_find_element(remote_msgs[0], remote_attrs[i]);
398 samdb_msg_add_delete(state->sam_ldb, mem_ctx, msg,
401 ldb_msg_add(msg, el, LDB_FLAG_MOD_REPLACE);
405 acb = user->acct_flags;
406 if (acb & (ACB_WSTRUST)) {
407 cn_name[cn_name_len - 1] = '\0';
408 container = "Computers";
409 obj_class = "computer";
411 } else if (acb & ACB_SVRTRUST) {
412 if (cn_name[cn_name_len - 1] != '$') {
413 return NT_STATUS_FOOBAR;
415 cn_name[cn_name_len - 1] = '\0';
416 container = "Domain Controllers";
417 obj_class = "computer";
423 samdb_msg_add_string(state->sam_ldb, mem_ctx, msg,
424 "objectClass", obj_class);
426 msg->dn = ldb_dn_copy(mem_ctx, state->base_dn[database]);
427 ldb_dn_add_child_fmt(msg->dn, "CN=%s,CN=%s", cn_name, container);
429 return NT_STATUS_NO_MEMORY;
433 ret = ldb_add(state->sam_ldb, msg);
435 struct ldb_dn *first_try_dn = msg->dn;
436 /* Try again with the default DN */
438 *error_string = talloc_asprintf(mem_ctx, "Failed to create user record. Tried %s: %s",
439 ldb_dn_get_linearized(first_try_dn),
440 ldb_errstring(state->sam_ldb));
441 return NT_STATUS_INTERNAL_DB_CORRUPTION;
443 msg->dn = talloc_steal(msg, remote_msgs[0]->dn);
444 ret = ldb_add(state->sam_ldb, msg);
446 *error_string = talloc_asprintf(mem_ctx, "Failed to create user record. Tried both %s and %s: %s",
447 ldb_dn_get_linearized(first_try_dn),
448 ldb_dn_get_linearized(msg->dn),
449 ldb_errstring(state->sam_ldb));
450 return NT_STATUS_INTERNAL_DB_CORRUPTION;
455 ret = samdb_replace(state->sam_ldb, mem_ctx, msg);
457 *error_string = talloc_asprintf(mem_ctx, "Failed to modify user record %s: %s",
458 ldb_dn_get_linearized(msg->dn),
459 ldb_errstring(state->sam_ldb));
460 return NT_STATUS_INTERNAL_DB_CORRUPTION;
467 static NTSTATUS samsync_ldb_delete_user(TALLOC_CTX *mem_ctx,
468 struct samsync_ldb_state *state,
469 enum netr_SamDatabaseID database,
470 struct netr_DELTA_ENUM *delta,
473 uint32_t rid = delta->delta_id_union.rid;
474 struct ldb_message **msgs;
476 const char *attrs[] = { NULL };
478 /* search for the user, by rid */
479 ret = gendb_search(state->sam_ldb, mem_ctx, state->base_dn[database],
480 &msgs, attrs, "(&(objectClass=user)(objectSid=%s))",
481 ldap_encode_ndr_dom_sid(mem_ctx, dom_sid_add_rid(mem_ctx, state->dom_sid[database], rid)));
484 *error_string = talloc_asprintf(mem_ctx, "gendb_search failed: %s", ldb_errstring(state->sam_ldb));
485 return NT_STATUS_INTERNAL_DB_CORRUPTION;
486 } else if (ret == 0) {
487 return NT_STATUS_NO_SUCH_USER;
488 } else if (ret > 1) {
489 *error_string = talloc_asprintf(mem_ctx, "More than one user with SID: %s",
490 dom_sid_string(mem_ctx,
491 dom_sid_add_rid(mem_ctx,
492 state->dom_sid[database],
494 return NT_STATUS_INTERNAL_DB_CORRUPTION;
497 ret = ldb_delete(state->sam_ldb, msgs[0]->dn);
499 *error_string = talloc_asprintf(mem_ctx, "Failed to delete user record %s: %s",
500 ldb_dn_get_linearized(msgs[0]->dn),
501 ldb_errstring(state->sam_ldb));
502 return NT_STATUS_INTERNAL_DB_CORRUPTION;
508 static NTSTATUS samsync_ldb_handle_group(TALLOC_CTX *mem_ctx,
509 struct samsync_ldb_state *state,
510 enum netr_SamDatabaseID database,
511 struct netr_DELTA_ENUM *delta,
514 uint32_t rid = delta->delta_id_union.rid;
515 struct netr_DELTA_GROUP *group = delta->delta_union.group;
516 const char *container, *obj_class;
519 struct ldb_message *msg;
520 struct ldb_message **msgs;
523 const char *attrs[] = { NULL };
525 msg = ldb_msg_new(mem_ctx);
527 return NT_STATUS_NO_MEMORY;
530 /* search for the group, by rid */
531 ret = gendb_search(state->sam_ldb, mem_ctx, state->base_dn[database], &msgs, attrs,
532 "(&(objectClass=group)(objectSid=%s))",
533 ldap_encode_ndr_dom_sid(mem_ctx, dom_sid_add_rid(mem_ctx, state->dom_sid[database], rid)));
536 *error_string = talloc_asprintf(mem_ctx, "gendb_search failed: %s", ldb_errstring(state->sam_ldb));
537 return NT_STATUS_INTERNAL_DB_CORRUPTION;
538 } else if (ret == 0) {
540 } else if (ret > 1) {
541 *error_string = talloc_asprintf(mem_ctx, "More than one group/alias with SID: %s",
542 dom_sid_string(mem_ctx,
543 dom_sid_add_rid(mem_ctx,
544 state->dom_sid[database],
546 return NT_STATUS_INTERNAL_DB_CORRUPTION;
548 msg->dn = talloc_steal(msg, msgs[0]->dn);
551 cn_name = group->group_name.string;
553 #define ADD_OR_DEL(type, attrib, field) do { \
554 if (group->field) { \
555 samdb_msg_add_ ## type(state->sam_ldb, mem_ctx, msg, \
556 attrib, group->field); \
558 samdb_msg_add_delete(state->sam_ldb, mem_ctx, msg, \
563 ADD_OR_DEL(string, "samAccountName", group_name.string);
565 if (samdb_msg_add_dom_sid(state->sam_ldb, mem_ctx, msg,
566 "objectSid", dom_sid_add_rid(mem_ctx, state->dom_sid[database], rid))) {
567 return NT_STATUS_NO_MEMORY;
570 ADD_OR_DEL(string, "description", description.string);
578 samdb_msg_add_string(state->sam_ldb, mem_ctx, msg,
579 "objectClass", obj_class);
580 msg->dn = ldb_dn_copy(mem_ctx, state->base_dn[database]);
581 ldb_dn_add_child_fmt(msg->dn, "CN=%s,CN=%s", cn_name, container);
583 return NT_STATUS_NO_MEMORY;
586 ret = ldb_add(state->sam_ldb, msg);
588 *error_string = talloc_asprintf(mem_ctx, "Failed to create group record %s: %s",
589 ldb_dn_get_linearized(msg->dn),
590 ldb_errstring(state->sam_ldb));
591 return NT_STATUS_INTERNAL_DB_CORRUPTION;
594 ret = samdb_replace(state->sam_ldb, mem_ctx, msg);
596 *error_string = talloc_asprintf(mem_ctx, "Failed to modify group record %s: %s",
597 ldb_dn_get_linearized(msg->dn),
598 ldb_errstring(state->sam_ldb));
599 return NT_STATUS_INTERNAL_DB_CORRUPTION;
606 static NTSTATUS samsync_ldb_delete_group(TALLOC_CTX *mem_ctx,
607 struct samsync_ldb_state *state,
608 enum netr_SamDatabaseID database,
609 struct netr_DELTA_ENUM *delta,
612 uint32_t rid = delta->delta_id_union.rid;
613 struct ldb_message **msgs;
615 const char *attrs[] = { NULL };
617 /* search for the group, by rid */
618 ret = gendb_search(state->sam_ldb, mem_ctx, state->base_dn[database], &msgs, attrs,
619 "(&(objectClass=group)(objectSid=%s))",
620 ldap_encode_ndr_dom_sid(mem_ctx, dom_sid_add_rid(mem_ctx, state->dom_sid[database], rid)));
623 *error_string = talloc_asprintf(mem_ctx, "gendb_search failed: %s", ldb_errstring(state->sam_ldb));
624 return NT_STATUS_INTERNAL_DB_CORRUPTION;
625 } else if (ret == 0) {
626 return NT_STATUS_NO_SUCH_GROUP;
627 } else if (ret > 1) {
628 *error_string = talloc_asprintf(mem_ctx, "More than one group/alias with SID: %s",
629 dom_sid_string(mem_ctx,
630 dom_sid_add_rid(mem_ctx,
631 state->dom_sid[database],
633 return NT_STATUS_INTERNAL_DB_CORRUPTION;
636 ret = ldb_delete(state->sam_ldb, msgs[0]->dn);
638 *error_string = talloc_asprintf(mem_ctx, "Failed to delete group record %s: %s",
639 ldb_dn_get_linearized(msgs[0]->dn),
640 ldb_errstring(state->sam_ldb));
641 return NT_STATUS_INTERNAL_DB_CORRUPTION;
647 static NTSTATUS samsync_ldb_handle_group_member(TALLOC_CTX *mem_ctx,
648 struct samsync_ldb_state *state,
649 enum netr_SamDatabaseID database,
650 struct netr_DELTA_ENUM *delta,
653 uint32_t rid = delta->delta_id_union.rid;
654 struct netr_DELTA_GROUP_MEMBER *group_member = delta->delta_union.group_member;
655 struct ldb_message *msg;
656 struct ldb_message **msgs;
658 const char *attrs[] = { NULL };
661 msg = ldb_msg_new(mem_ctx);
663 return NT_STATUS_NO_MEMORY;
666 /* search for the group, by rid */
667 ret = gendb_search(state->sam_ldb, mem_ctx, state->base_dn[database], &msgs, attrs,
668 "(&(objectClass=group)(objectSid=%s))",
669 ldap_encode_ndr_dom_sid(mem_ctx, dom_sid_add_rid(mem_ctx, state->dom_sid[database], rid)));
672 *error_string = talloc_asprintf(mem_ctx, "gendb_search failed: %s", ldb_errstring(state->sam_ldb));
673 return NT_STATUS_INTERNAL_DB_CORRUPTION;
674 } else if (ret == 0) {
675 return NT_STATUS_NO_SUCH_GROUP;
676 } else if (ret > 1) {
677 *error_string = talloc_asprintf(mem_ctx, "More than one group/alias with SID: %s",
678 dom_sid_string(mem_ctx,
679 dom_sid_add_rid(mem_ctx,
680 state->dom_sid[database],
682 return NT_STATUS_INTERNAL_DB_CORRUPTION;
684 msg->dn = talloc_steal(msg, msgs[0]->dn);
689 for (i=0; i<group_member->num_rids; i++) {
690 /* search for the group, by rid */
691 ret = gendb_search(state->sam_ldb, mem_ctx, state->base_dn[database], &msgs, attrs,
692 "(&(objectClass=user)(objectSid=%s))",
693 ldap_encode_ndr_dom_sid(mem_ctx, dom_sid_add_rid(mem_ctx, state->dom_sid[database], group_member->rids[i])));
696 *error_string = talloc_asprintf(mem_ctx, "gendb_search failed: %s", ldb_errstring(state->sam_ldb));
697 return NT_STATUS_INTERNAL_DB_CORRUPTION;
698 } else if (ret == 0) {
699 return NT_STATUS_NO_SUCH_USER;
700 } else if (ret > 1) {
701 return NT_STATUS_INTERNAL_DB_CORRUPTION;
703 samdb_msg_add_string(state->sam_ldb, mem_ctx, msg, "member", ldb_dn_alloc_linearized(mem_ctx, msgs[0]->dn));
709 ret = samdb_replace(state->sam_ldb, mem_ctx, msg);
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);
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 = samdb_replace(state->sam_ldb, mem_ctx, msg);
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);
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;
900 /* search for members, in the top basedn (normal users are builtin aliases) */
901 ret = gendb_search(state->sam_ldb, mem_ctx, state->base_dn[SAM_DATABASE_DOMAIN], &msgs, attrs,
903 ldap_encode_ndr_dom_sid(mem_ctx, alias_member->sids.sids[i].sid));
906 *error_string = talloc_asprintf(mem_ctx, "gendb_search failed: %s", ldb_errstring(state->sam_ldb));
907 return NT_STATUS_INTERNAL_DB_CORRUPTION;
908 } else if (ret == 0) {
910 nt_status = samsync_ldb_add_foreignSecurityPrincipal(mem_ctx, state,
911 alias_member->sids.sids[i].sid,
914 if (!NT_STATUS_IS_OK(nt_status)) {
917 } else if (ret > 1) {
918 return NT_STATUS_INTERNAL_DB_CORRUPTION;
920 alias_member_dn = msgs[0]->dn;
922 samdb_msg_add_string(state->sam_ldb, mem_ctx, msg, "member", ldb_dn_alloc_linearized(mem_ctx, alias_member_dn));
927 ret = samdb_replace(state->sam_ldb, mem_ctx, msg);
929 *error_string = talloc_asprintf(mem_ctx, "Failed to modify group record %s: %s",
930 ldb_dn_get_linearized(msg->dn),
931 ldb_errstring(state->sam_ldb));
932 return NT_STATUS_INTERNAL_DB_CORRUPTION;
938 static NTSTATUS samsync_ldb_handle_account(TALLOC_CTX *mem_ctx,
939 struct samsync_ldb_state *state,
940 enum netr_SamDatabaseID database,
941 struct netr_DELTA_ENUM *delta,
944 struct dom_sid *sid = delta->delta_id_union.sid;
945 struct netr_DELTA_ACCOUNT *account = delta->delta_union.account;
947 struct ldb_message *msg;
948 struct ldb_message **msgs;
949 struct ldb_dn *privilege_dn;
951 const char *attrs[] = { NULL };
954 msg = ldb_msg_new(mem_ctx);
956 return NT_STATUS_NO_MEMORY;
959 /* search for the account, by sid, in the top basedn */
960 ret = gendb_search(state->sam_ldb, mem_ctx, state->base_dn[SAM_DATABASE_DOMAIN], &msgs, attrs,
961 "(objectSid=%s)", ldap_encode_ndr_dom_sid(mem_ctx, sid));
964 *error_string = talloc_asprintf(mem_ctx, "gendb_search failed: %s", ldb_errstring(state->sam_ldb));
965 return NT_STATUS_INTERNAL_DB_CORRUPTION;
966 } else if (ret == 0) {
968 nt_status = samsync_ldb_add_foreignSecurityPrincipal(mem_ctx, state,
972 privilege_dn = talloc_steal(msg, privilege_dn);
973 if (!NT_STATUS_IS_OK(nt_status)) {
976 } else if (ret > 1) {
977 *error_string = talloc_asprintf(mem_ctx, "More than one account with SID: %s",
978 dom_sid_string(mem_ctx, sid));
979 return NT_STATUS_INTERNAL_DB_CORRUPTION;
981 privilege_dn = talloc_steal(msg, msgs[0]->dn);
984 msg->dn = privilege_dn;
986 for (i=0; i< account->privilege_entries; i++) {
987 samdb_msg_add_string(state->sam_ldb, mem_ctx, msg, "privilege",
988 account->privilege_name[i].string);
991 ret = samdb_replace(state->sam_ldb, mem_ctx, msg);
993 *error_string = talloc_asprintf(mem_ctx, "Failed to modify privilege record %s",
994 ldb_dn_get_linearized(msg->dn));
995 return NT_STATUS_INTERNAL_DB_CORRUPTION;
1001 static NTSTATUS samsync_ldb_delete_account(TALLOC_CTX *mem_ctx,
1002 struct samsync_ldb_state *state,
1003 enum netr_SamDatabaseID database,
1004 struct netr_DELTA_ENUM *delta,
1005 char **error_string)
1007 struct dom_sid *sid = delta->delta_id_union.sid;
1009 struct ldb_message *msg;
1010 struct ldb_message **msgs;
1012 const char *attrs[] = { NULL };
1014 msg = ldb_msg_new(mem_ctx);
1016 return NT_STATUS_NO_MEMORY;
1019 /* search for the account, by sid, in the top basedn */
1020 ret = gendb_search(state->sam_ldb, mem_ctx, state->base_dn[SAM_DATABASE_DOMAIN], &msgs, attrs,
1022 ldap_encode_ndr_dom_sid(mem_ctx, sid));
1025 *error_string = talloc_asprintf(mem_ctx, "gendb_search failed: %s", ldb_errstring(state->sam_ldb));
1026 return NT_STATUS_INTERNAL_DB_CORRUPTION;
1027 } else if (ret == 0) {
1028 return NT_STATUS_NO_SUCH_USER;
1029 } else if (ret > 1) {
1030 *error_string = talloc_asprintf(mem_ctx, "More than one account with SID: %s",
1031 dom_sid_string(mem_ctx, sid));
1032 return NT_STATUS_INTERNAL_DB_CORRUPTION;
1034 msg->dn = talloc_steal(msg, msgs[0]->dn);
1037 samdb_msg_add_delete(state->sam_ldb, mem_ctx, msg,
1040 ret = samdb_replace(state->sam_ldb, mem_ctx, msg);
1042 *error_string = talloc_asprintf(mem_ctx, "Failed to modify privilege record %s",
1043 ldb_dn_get_linearized(msg->dn));
1044 return NT_STATUS_INTERNAL_DB_CORRUPTION;
1047 return NT_STATUS_OK;
1050 static NTSTATUS libnet_samsync_ldb_fn(TALLOC_CTX *mem_ctx,
1052 enum netr_SamDatabaseID database,
1053 struct netr_DELTA_ENUM *delta,
1054 char **error_string)
1056 NTSTATUS nt_status = NT_STATUS_OK;
1057 struct samsync_ldb_state *state = talloc_get_type(private, struct samsync_ldb_state);
1059 *error_string = NULL;
1060 switch (delta->delta_type) {
1061 case NETR_DELTA_DOMAIN:
1063 nt_status = samsync_ldb_handle_domain(mem_ctx,
1070 case NETR_DELTA_USER:
1072 nt_status = samsync_ldb_handle_user(mem_ctx,
1079 case NETR_DELTA_DELETE_USER:
1081 nt_status = samsync_ldb_delete_user(mem_ctx,
1088 case NETR_DELTA_GROUP:
1090 nt_status = samsync_ldb_handle_group(mem_ctx,
1097 case NETR_DELTA_DELETE_GROUP:
1099 nt_status = samsync_ldb_delete_group(mem_ctx,
1106 case NETR_DELTA_GROUP_MEMBER:
1108 nt_status = samsync_ldb_handle_group_member(mem_ctx,
1115 case NETR_DELTA_ALIAS:
1117 nt_status = samsync_ldb_handle_alias(mem_ctx,
1124 case NETR_DELTA_DELETE_ALIAS:
1126 nt_status = samsync_ldb_delete_alias(mem_ctx,
1133 case NETR_DELTA_ALIAS_MEMBER:
1135 nt_status = samsync_ldb_handle_alias_member(mem_ctx,
1142 case NETR_DELTA_ACCOUNT:
1144 nt_status = samsync_ldb_handle_account(mem_ctx,
1151 case NETR_DELTA_DELETE_ACCOUNT:
1153 nt_status = samsync_ldb_delete_account(mem_ctx,
1161 /* Can't dump them all right now */
1164 if (!NT_STATUS_IS_OK(nt_status) && !*error_string) {
1165 *error_string = talloc_asprintf(mem_ctx, "Failed to handle samsync delta: %s", nt_errstr(nt_status));
1170 static NTSTATUS libnet_samsync_ldb_init(TALLOC_CTX *mem_ctx,
1172 struct libnet_SamSync_state *samsync_state,
1173 char **error_string)
1175 struct samsync_ldb_state *state = talloc_get_type(private, struct samsync_ldb_state);
1176 const char *server = dcerpc_server_name(samsync_state->netlogon_pipe);
1179 state->samsync_state = samsync_state;
1181 ZERO_STRUCT(state->dom_sid);
1182 if (state->samsync_state->domain_sid) {
1183 state->dom_sid[SAM_DATABASE_DOMAIN] = dom_sid_dup(state, state->samsync_state->domain_sid);
1186 state->dom_sid[SAM_DATABASE_BUILTIN] = dom_sid_parse_talloc(state, SID_BUILTIN);
1188 if (state->samsync_state->realm) {
1189 if (!server || !*server) {
1190 /* huh? how do we not have a server name? */
1191 *error_string = talloc_strdup(mem_ctx, "No DCE/RPC server name available. How did we connect?");
1192 return NT_STATUS_INVALID_PARAMETER;
1194 ldap_url = talloc_asprintf(state, "ldap://%s", server);
1196 state->remote_ldb = ldb_wrap_connect(mem_ctx,
1197 state->samsync_state->machine_net_ctx->event_ctx,
1198 state->samsync_state->machine_net_ctx->lp_ctx,
1200 NULL, state->samsync_state->machine_net_ctx->cred,
1202 if (!state->remote_ldb) {
1203 *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);
1204 return NT_STATUS_NO_LOGON_SERVERS;
1207 state->remote_ldb = NULL;
1209 return NT_STATUS_OK;
1212 NTSTATUS libnet_samsync_ldb(struct libnet_context *ctx, TALLOC_CTX *mem_ctx, struct libnet_samsync_ldb *r)
1215 struct libnet_SamSync r2;
1216 struct samsync_ldb_state *state = talloc(mem_ctx, struct samsync_ldb_state);
1219 return NT_STATUS_NO_MEMORY;
1222 state->secrets = NULL;
1223 state->trusted_domains = NULL;
1225 state->sam_ldb = samdb_connect(mem_ctx,
1228 r->in.session_info);
1230 r2.out.error_string = NULL;
1231 r2.in.binding_string = r->in.binding_string;
1232 r2.in.rid_crypt = true;
1233 r2.in.init_fn = libnet_samsync_ldb_init;
1234 r2.in.delta_fn = libnet_samsync_ldb_fn;
1235 r2.in.fn_ctx = state;
1236 r2.in.machine_account = NULL; /* TODO: Create a machine account, fill this in, and the delete it */
1237 nt_status = libnet_SamSync_netlogon(ctx, state, &r2);
1238 r->out.error_string = r2.out.error_string;
1239 talloc_steal(mem_ctx, r->out.error_string);
1241 if (!NT_STATUS_IS_OK(nt_status)) {