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 "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, state->samsync_state->domain_guid,
172 (ndr_push_flags_fn_t)ndr_push_GUID);
173 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
174 *error_string = talloc_asprintf(mem_ctx, "ndr_push of domain GUID failed!");
175 return ndr_map_error2ntstatus(ndr_err);
178 ldb_msg_add_value(msg, "objectGUID", &v, NULL);
180 } else if (database == SAM_DATABASE_BUILTIN) {
181 /* work out the builtin_dn - useful for so many calls its worth
183 const char *dnstring = samdb_search_string(state->sam_ldb, mem_ctx, NULL,
184 "distinguishedName", "objectClass=builtinDomain");
185 state->base_dn[database] = ldb_dn_new(state, state->sam_ldb, dnstring);
186 if ( ! ldb_dn_validate(state->base_dn[database])) {
187 return NT_STATUS_INTERNAL_ERROR;
191 return NT_STATUS_INVALID_PARAMETER;
194 msg->dn = talloc_reference(mem_ctx, state->base_dn[database]);
196 return NT_STATUS_NO_MEMORY;
199 samdb_msg_add_string(state->sam_ldb, mem_ctx,
200 msg, "oEMInformation", domain->comment.string);
202 samdb_msg_add_int64(state->sam_ldb, mem_ctx,
203 msg, "forceLogoff", domain->force_logoff_time);
205 samdb_msg_add_uint(state->sam_ldb, mem_ctx,
206 msg, "minPwdLen", domain->min_password_length);
208 samdb_msg_add_int64(state->sam_ldb, mem_ctx,
209 msg, "maxPwdAge", domain->max_password_age);
211 samdb_msg_add_int64(state->sam_ldb, mem_ctx,
212 msg, "minPwdAge", domain->min_password_age);
214 samdb_msg_add_uint(state->sam_ldb, mem_ctx,
215 msg, "pwdHistoryLength", domain->password_history_length);
217 samdb_msg_add_uint64(state->sam_ldb, mem_ctx,
218 msg, "modifiedCount",
219 domain->sequence_num);
221 samdb_msg_add_uint64(state->sam_ldb, mem_ctx,
222 msg, "creationTime", domain->domain_create_time);
224 /* TODO: Account lockout, password properties */
226 ret = samdb_replace(state->sam_ldb, mem_ctx, msg);
229 return NT_STATUS_INTERNAL_ERROR;
234 static NTSTATUS samsync_ldb_handle_user(TALLOC_CTX *mem_ctx,
235 struct samsync_ldb_state *state,
236 enum netr_SamDatabaseID database,
237 struct netr_DELTA_ENUM *delta,
240 uint32_t rid = delta->delta_id_union.rid;
241 struct netr_DELTA_USER *user = delta->delta_union.user;
242 const char *container, *obj_class;
245 const struct dom_sid *user_sid;
246 struct ldb_message *msg;
247 struct ldb_message **msgs;
248 struct ldb_message **remote_msgs = NULL;
252 const char *attrs[] = { NULL };
253 /* we may change this to a global search, then fill in only the things not in ldap later */
254 const char *remote_attrs[] = { "userPrincipalName", "servicePrincipalName",
255 "msDS-KeyVersionNumber", "objectGUID", NULL};
257 user_sid = dom_sid_add_rid(mem_ctx, state->dom_sid[database], rid);
259 return NT_STATUS_NO_MEMORY;
262 msg = ldb_msg_new(mem_ctx);
264 return NT_STATUS_NO_MEMORY;
268 /* search for the user, by rid */
269 ret = gendb_search(state->sam_ldb, mem_ctx, state->base_dn[database],
270 &msgs, attrs, "(&(objectClass=user)(objectSid=%s))",
271 ldap_encode_ndr_dom_sid(mem_ctx, user_sid));
274 *error_string = talloc_asprintf(mem_ctx, "LDB for user %s failed: %s",
275 dom_sid_string(mem_ctx, user_sid),
276 ldb_errstring(state->sam_ldb));
277 return NT_STATUS_INTERNAL_DB_CORRUPTION;
278 } else if (ret == 0) {
280 } else if (ret > 1) {
281 *error_string = talloc_asprintf(mem_ctx, "More than one user with SID: %s in local LDB",
282 dom_sid_string(mem_ctx, user_sid));
283 return NT_STATUS_INTERNAL_DB_CORRUPTION;
285 msg->dn = msgs[0]->dn;
286 talloc_steal(msg, msgs[0]->dn);
289 /* and do the same on the remote database */
290 if (state->remote_ldb) {
291 ret = gendb_search(state->remote_ldb, mem_ctx, state->base_dn[database],
292 &remote_msgs, remote_attrs, "(&(objectClass=user)(objectSid=%s))",
293 ldap_encode_ndr_dom_sid(mem_ctx, user_sid));
296 *error_string = talloc_asprintf(mem_ctx, "remote LDAP for user %s failed: %s",
297 dom_sid_string(mem_ctx, user_sid),
298 ldb_errstring(state->remote_ldb));
299 return NT_STATUS_INTERNAL_DB_CORRUPTION;
300 } else if (ret == 0) {
301 *error_string = talloc_asprintf(mem_ctx, "User exists in samsync but not in remote LDAP domain! (base: %s, SID: %s)",
302 ldb_dn_get_linearized(state->base_dn[database]),
303 dom_sid_string(mem_ctx, user_sid));
304 return NT_STATUS_NO_SUCH_USER;
305 } else if (ret > 1) {
306 *error_string = talloc_asprintf(mem_ctx, "More than one user in remote LDAP domain with SID: %s",
307 dom_sid_string(mem_ctx, user_sid));
308 return NT_STATUS_INTERNAL_DB_CORRUPTION;
310 /* Try to put things in the same location as the remote server */
312 msg->dn = remote_msgs[0]->dn;
313 talloc_steal(msg, remote_msgs[0]->dn);
317 cn_name = talloc_strdup(mem_ctx, user->account_name.string);
318 NT_STATUS_HAVE_NO_MEMORY(cn_name);
319 cn_name_len = strlen(cn_name);
321 #define ADD_OR_DEL(type, attrib, field) do { \
323 samdb_msg_add_ ## type(state->sam_ldb, mem_ctx, msg, \
324 attrib, user->field); \
326 samdb_msg_add_delete(state->sam_ldb, mem_ctx, msg, \
331 ADD_OR_DEL(string, "samAccountName", account_name.string);
332 ADD_OR_DEL(string, "displayName", full_name.string);
334 if (samdb_msg_add_dom_sid(state->sam_ldb, mem_ctx, msg,
335 "objectSid", dom_sid_add_rid(mem_ctx, state->dom_sid[database], rid))) {
336 return NT_STATUS_NO_MEMORY;
339 ADD_OR_DEL(uint, "primaryGroupID", primary_gid);
340 ADD_OR_DEL(string, "homeDirectory", home_directory.string);
341 ADD_OR_DEL(string, "homeDrive", home_drive.string);
342 ADD_OR_DEL(string, "scriptPath", logon_script.string);
343 ADD_OR_DEL(string, "description", description.string);
344 ADD_OR_DEL(string, "userWorkstations", workstations.string);
346 ADD_OR_DEL(uint64, "lastLogon", last_logon);
347 ADD_OR_DEL(uint64, "lastLogoff", last_logoff);
349 if (samdb_msg_add_logon_hours(state->sam_ldb, mem_ctx, msg, "logonHours", &user->logon_hours) != 0) {
350 return NT_STATUS_NO_MEMORY;
353 ADD_OR_DEL(uint, "badPwdCount", bad_password_count);
354 ADD_OR_DEL(uint, "logonCount", logon_count);
356 ADD_OR_DEL(uint64, "pwdLastSet", last_password_change);
357 ADD_OR_DEL(uint64, "accountExpires", acct_expiry);
359 if (samdb_msg_add_acct_flags(state->sam_ldb, mem_ctx, msg,
360 "userAccountControl", user->acct_flags) != 0) {
361 return NT_STATUS_NO_MEMORY;
365 /* Passwords. Ensure there is no plaintext stored against
366 * this entry, as we only have hashes */
367 samdb_msg_add_delete(state->sam_ldb, mem_ctx, msg,
370 if (user->lm_password_present) {
371 samdb_msg_add_hash(state->sam_ldb, mem_ctx, msg,
372 "dBCSPwd", &user->lmpassword);
374 samdb_msg_add_delete(state->sam_ldb, mem_ctx, msg,
377 if (user->nt_password_present) {
378 samdb_msg_add_hash(state->sam_ldb, mem_ctx, msg,
379 "unicodePwd", &user->ntpassword);
381 samdb_msg_add_delete(state->sam_ldb, mem_ctx, msg,
385 ADD_OR_DEL(string, "comment", comment.string);
386 ADD_OR_DEL(string, "userParameters", parameters.string);
387 ADD_OR_DEL(uint, "countryCode", country_code);
388 ADD_OR_DEL(uint, "codePage", code_page);
390 ADD_OR_DEL(string, "profilePath", profile_path.string);
394 for (i=0; remote_attrs[i]; i++) {
395 struct ldb_message_element *el = ldb_msg_find_element(remote_msgs[0], remote_attrs[i]);
397 samdb_msg_add_delete(state->sam_ldb, mem_ctx, msg,
400 ldb_msg_add(msg, el, LDB_FLAG_MOD_REPLACE);
404 acb = user->acct_flags;
405 if (acb & (ACB_WSTRUST)) {
406 cn_name[cn_name_len - 1] = '\0';
407 container = "Computers";
408 obj_class = "computer";
410 } else if (acb & ACB_SVRTRUST) {
411 if (cn_name[cn_name_len - 1] != '$') {
412 return NT_STATUS_FOOBAR;
414 cn_name[cn_name_len - 1] = '\0';
415 container = "Domain Controllers";
416 obj_class = "computer";
422 samdb_msg_add_string(state->sam_ldb, mem_ctx, msg,
423 "objectClass", obj_class);
425 msg->dn = ldb_dn_copy(mem_ctx, state->base_dn[database]);
426 ldb_dn_add_child_fmt(msg->dn, "CN=%s,CN=%s", cn_name, container);
428 return NT_STATUS_NO_MEMORY;
432 ret = ldb_add(state->sam_ldb, msg);
434 struct ldb_dn *first_try_dn = msg->dn;
435 /* Try again with the default DN */
437 *error_string = talloc_asprintf(mem_ctx, "Failed to create user record. Tried %s: %s",
438 ldb_dn_get_linearized(first_try_dn),
439 ldb_errstring(state->sam_ldb));
440 return NT_STATUS_INTERNAL_DB_CORRUPTION;
442 msg->dn = talloc_steal(msg, remote_msgs[0]->dn);
443 ret = ldb_add(state->sam_ldb, msg);
445 *error_string = talloc_asprintf(mem_ctx, "Failed to create user record. Tried both %s and %s: %s",
446 ldb_dn_get_linearized(first_try_dn),
447 ldb_dn_get_linearized(msg->dn),
448 ldb_errstring(state->sam_ldb));
449 return NT_STATUS_INTERNAL_DB_CORRUPTION;
454 ret = samdb_replace(state->sam_ldb, mem_ctx, msg);
456 *error_string = talloc_asprintf(mem_ctx, "Failed to modify user record %s: %s",
457 ldb_dn_get_linearized(msg->dn),
458 ldb_errstring(state->sam_ldb));
459 return NT_STATUS_INTERNAL_DB_CORRUPTION;
466 static NTSTATUS samsync_ldb_delete_user(TALLOC_CTX *mem_ctx,
467 struct samsync_ldb_state *state,
468 enum netr_SamDatabaseID database,
469 struct netr_DELTA_ENUM *delta,
472 uint32_t rid = delta->delta_id_union.rid;
473 struct ldb_message **msgs;
475 const char *attrs[] = { NULL };
477 /* search for the user, by rid */
478 ret = gendb_search(state->sam_ldb, mem_ctx, state->base_dn[database],
479 &msgs, attrs, "(&(objectClass=user)(objectSid=%s))",
480 ldap_encode_ndr_dom_sid(mem_ctx, dom_sid_add_rid(mem_ctx, state->dom_sid[database], rid)));
483 *error_string = talloc_asprintf(mem_ctx, "gendb_search failed: %s", ldb_errstring(state->sam_ldb));
484 return NT_STATUS_INTERNAL_DB_CORRUPTION;
485 } else if (ret == 0) {
486 return NT_STATUS_NO_SUCH_USER;
487 } else if (ret > 1) {
488 *error_string = talloc_asprintf(mem_ctx, "More than one user with SID: %s",
489 dom_sid_string(mem_ctx,
490 dom_sid_add_rid(mem_ctx,
491 state->dom_sid[database],
493 return NT_STATUS_INTERNAL_DB_CORRUPTION;
496 ret = ldb_delete(state->sam_ldb, msgs[0]->dn);
498 *error_string = talloc_asprintf(mem_ctx, "Failed to delete user record %s: %s",
499 ldb_dn_get_linearized(msgs[0]->dn),
500 ldb_errstring(state->sam_ldb));
501 return NT_STATUS_INTERNAL_DB_CORRUPTION;
507 static NTSTATUS samsync_ldb_handle_group(TALLOC_CTX *mem_ctx,
508 struct samsync_ldb_state *state,
509 enum netr_SamDatabaseID database,
510 struct netr_DELTA_ENUM *delta,
513 uint32_t rid = delta->delta_id_union.rid;
514 struct netr_DELTA_GROUP *group = delta->delta_union.group;
515 const char *container, *obj_class;
518 struct ldb_message *msg;
519 struct ldb_message **msgs;
522 const char *attrs[] = { NULL };
524 msg = ldb_msg_new(mem_ctx);
526 return NT_STATUS_NO_MEMORY;
529 /* search for the group, by rid */
530 ret = gendb_search(state->sam_ldb, mem_ctx, state->base_dn[database], &msgs, attrs,
531 "(&(objectClass=group)(objectSid=%s))",
532 ldap_encode_ndr_dom_sid(mem_ctx, dom_sid_add_rid(mem_ctx, state->dom_sid[database], rid)));
535 *error_string = talloc_asprintf(mem_ctx, "gendb_search failed: %s", ldb_errstring(state->sam_ldb));
536 return NT_STATUS_INTERNAL_DB_CORRUPTION;
537 } else if (ret == 0) {
539 } else if (ret > 1) {
540 *error_string = talloc_asprintf(mem_ctx, "More than one group/alias with SID: %s",
541 dom_sid_string(mem_ctx,
542 dom_sid_add_rid(mem_ctx,
543 state->dom_sid[database],
545 return NT_STATUS_INTERNAL_DB_CORRUPTION;
547 msg->dn = talloc_steal(msg, msgs[0]->dn);
550 cn_name = group->group_name.string;
552 #define ADD_OR_DEL(type, attrib, field) do { \
553 if (group->field) { \
554 samdb_msg_add_ ## type(state->sam_ldb, mem_ctx, msg, \
555 attrib, group->field); \
557 samdb_msg_add_delete(state->sam_ldb, mem_ctx, msg, \
562 ADD_OR_DEL(string, "samAccountName", group_name.string);
564 if (samdb_msg_add_dom_sid(state->sam_ldb, mem_ctx, msg,
565 "objectSid", dom_sid_add_rid(mem_ctx, state->dom_sid[database], rid))) {
566 return NT_STATUS_NO_MEMORY;
569 ADD_OR_DEL(string, "description", description.string);
577 samdb_msg_add_string(state->sam_ldb, mem_ctx, msg,
578 "objectClass", obj_class);
579 msg->dn = ldb_dn_copy(mem_ctx, state->base_dn[database]);
580 ldb_dn_add_child_fmt(msg->dn, "CN=%s,CN=%s", cn_name, container);
582 return NT_STATUS_NO_MEMORY;
585 ret = ldb_add(state->sam_ldb, msg);
587 *error_string = talloc_asprintf(mem_ctx, "Failed to create group record %s: %s",
588 ldb_dn_get_linearized(msg->dn),
589 ldb_errstring(state->sam_ldb));
590 return NT_STATUS_INTERNAL_DB_CORRUPTION;
593 ret = samdb_replace(state->sam_ldb, mem_ctx, msg);
595 *error_string = talloc_asprintf(mem_ctx, "Failed to modify group record %s: %s",
596 ldb_dn_get_linearized(msg->dn),
597 ldb_errstring(state->sam_ldb));
598 return NT_STATUS_INTERNAL_DB_CORRUPTION;
605 static NTSTATUS samsync_ldb_delete_group(TALLOC_CTX *mem_ctx,
606 struct samsync_ldb_state *state,
607 enum netr_SamDatabaseID database,
608 struct netr_DELTA_ENUM *delta,
611 uint32_t rid = delta->delta_id_union.rid;
612 struct ldb_message **msgs;
614 const char *attrs[] = { NULL };
616 /* search for the group, by rid */
617 ret = gendb_search(state->sam_ldb, mem_ctx, state->base_dn[database], &msgs, attrs,
618 "(&(objectClass=group)(objectSid=%s))",
619 ldap_encode_ndr_dom_sid(mem_ctx, dom_sid_add_rid(mem_ctx, state->dom_sid[database], rid)));
622 *error_string = talloc_asprintf(mem_ctx, "gendb_search failed: %s", ldb_errstring(state->sam_ldb));
623 return NT_STATUS_INTERNAL_DB_CORRUPTION;
624 } else if (ret == 0) {
625 return NT_STATUS_NO_SUCH_GROUP;
626 } else if (ret > 1) {
627 *error_string = talloc_asprintf(mem_ctx, "More than one group/alias with SID: %s",
628 dom_sid_string(mem_ctx,
629 dom_sid_add_rid(mem_ctx,
630 state->dom_sid[database],
632 return NT_STATUS_INTERNAL_DB_CORRUPTION;
635 ret = ldb_delete(state->sam_ldb, msgs[0]->dn);
637 *error_string = talloc_asprintf(mem_ctx, "Failed to delete group record %s: %s",
638 ldb_dn_get_linearized(msgs[0]->dn),
639 ldb_errstring(state->sam_ldb));
640 return NT_STATUS_INTERNAL_DB_CORRUPTION;
646 static NTSTATUS samsync_ldb_handle_group_member(TALLOC_CTX *mem_ctx,
647 struct samsync_ldb_state *state,
648 enum netr_SamDatabaseID database,
649 struct netr_DELTA_ENUM *delta,
652 uint32_t rid = delta->delta_id_union.rid;
653 struct netr_DELTA_GROUP_MEMBER *group_member = delta->delta_union.group_member;
654 struct ldb_message *msg;
655 struct ldb_message **msgs;
657 const char *attrs[] = { NULL };
660 msg = ldb_msg_new(mem_ctx);
662 return NT_STATUS_NO_MEMORY;
665 /* search for the group, by rid */
666 ret = gendb_search(state->sam_ldb, mem_ctx, state->base_dn[database], &msgs, attrs,
667 "(&(objectClass=group)(objectSid=%s))",
668 ldap_encode_ndr_dom_sid(mem_ctx, dom_sid_add_rid(mem_ctx, state->dom_sid[database], rid)));
671 *error_string = talloc_asprintf(mem_ctx, "gendb_search failed: %s", ldb_errstring(state->sam_ldb));
672 return NT_STATUS_INTERNAL_DB_CORRUPTION;
673 } else if (ret == 0) {
674 return NT_STATUS_NO_SUCH_GROUP;
675 } else if (ret > 1) {
676 *error_string = talloc_asprintf(mem_ctx, "More than one group/alias with SID: %s",
677 dom_sid_string(mem_ctx,
678 dom_sid_add_rid(mem_ctx,
679 state->dom_sid[database],
681 return NT_STATUS_INTERNAL_DB_CORRUPTION;
683 msg->dn = talloc_steal(msg, msgs[0]->dn);
688 for (i=0; i<group_member->num_rids; i++) {
689 /* search for the group, by rid */
690 ret = gendb_search(state->sam_ldb, mem_ctx, state->base_dn[database], &msgs, attrs,
691 "(&(objectClass=user)(objectSid=%s))",
692 ldap_encode_ndr_dom_sid(mem_ctx, dom_sid_add_rid(mem_ctx, state->dom_sid[database], group_member->rids[i])));
695 *error_string = talloc_asprintf(mem_ctx, "gendb_search failed: %s", ldb_errstring(state->sam_ldb));
696 return NT_STATUS_INTERNAL_DB_CORRUPTION;
697 } else if (ret == 0) {
698 return NT_STATUS_NO_SUCH_USER;
699 } else if (ret > 1) {
700 return NT_STATUS_INTERNAL_DB_CORRUPTION;
702 samdb_msg_add_string(state->sam_ldb, mem_ctx, msg, "member", ldb_dn_alloc_linearized(mem_ctx, msgs[0]->dn));
708 ret = samdb_replace(state->sam_ldb, mem_ctx, msg);
710 *error_string = talloc_asprintf(mem_ctx, "Failed to modify group record %s: %s",
711 ldb_dn_get_linearized(msg->dn),
712 ldb_errstring(state->sam_ldb));
713 return NT_STATUS_INTERNAL_DB_CORRUPTION;
719 static NTSTATUS samsync_ldb_handle_alias(TALLOC_CTX *mem_ctx,
720 struct samsync_ldb_state *state,
721 enum netr_SamDatabaseID database,
722 struct netr_DELTA_ENUM *delta,
725 uint32_t rid = delta->delta_id_union.rid;
726 struct netr_DELTA_ALIAS *alias = delta->delta_union.alias;
727 const char *container, *obj_class;
730 struct ldb_message *msg;
731 struct ldb_message **msgs;
734 const char *attrs[] = { NULL };
736 msg = ldb_msg_new(mem_ctx);
738 return NT_STATUS_NO_MEMORY;
741 /* search for the alias, by rid */
742 ret = gendb_search(state->sam_ldb, mem_ctx, state->base_dn[database], &msgs, attrs,
743 "(&(objectClass=group)(objectSid=%s))",
744 ldap_encode_ndr_dom_sid(mem_ctx, dom_sid_add_rid(mem_ctx, state->dom_sid[database], rid)));
747 *error_string = talloc_asprintf(mem_ctx, "gendb_search failed: %s", ldb_errstring(state->sam_ldb));
748 return NT_STATUS_INTERNAL_DB_CORRUPTION;
749 } else if (ret == 0) {
751 } else if (ret > 1) {
752 *error_string = talloc_asprintf(mem_ctx, "More than one group/alias with SID: %s",
753 dom_sid_string(mem_ctx,
754 dom_sid_add_rid(mem_ctx,
755 state->dom_sid[database],
757 return NT_STATUS_INTERNAL_DB_CORRUPTION;
759 msg->dn = talloc_steal(mem_ctx, msgs[0]->dn);
762 cn_name = alias->alias_name.string;
764 #define ADD_OR_DEL(type, attrib, field) do { \
765 if (alias->field) { \
766 samdb_msg_add_ ## type(state->sam_ldb, mem_ctx, msg, \
767 attrib, alias->field); \
769 samdb_msg_add_delete(state->sam_ldb, mem_ctx, msg, \
774 ADD_OR_DEL(string, "samAccountName", alias_name.string);
776 if (samdb_msg_add_dom_sid(state->sam_ldb, mem_ctx, msg,
777 "objectSid", dom_sid_add_rid(mem_ctx, state->dom_sid[database], rid))) {
778 return NT_STATUS_NO_MEMORY;
781 ADD_OR_DEL(string, "description", description.string);
785 samdb_msg_add_uint(state->sam_ldb, mem_ctx, msg, "groupType", 0x80000004);
791 samdb_msg_add_string(state->sam_ldb, mem_ctx, msg,
792 "objectClass", obj_class);
793 msg->dn = ldb_dn_copy(mem_ctx, state->base_dn[database]);
794 ldb_dn_add_child_fmt(msg->dn, "CN=%s,CN=%s", cn_name, container);
796 return NT_STATUS_NO_MEMORY;
799 ret = ldb_add(state->sam_ldb, msg);
801 *error_string = talloc_asprintf(mem_ctx, "Failed to create alias record %s: %s",
802 ldb_dn_get_linearized(msg->dn),
803 ldb_errstring(state->sam_ldb));
804 return NT_STATUS_INTERNAL_DB_CORRUPTION;
807 ret = samdb_replace(state->sam_ldb, mem_ctx, msg);
809 *error_string = talloc_asprintf(mem_ctx, "Failed to modify alias record %s: %s",
810 ldb_dn_get_linearized(msg->dn),
811 ldb_errstring(state->sam_ldb));
812 return NT_STATUS_INTERNAL_DB_CORRUPTION;
819 static NTSTATUS samsync_ldb_delete_alias(TALLOC_CTX *mem_ctx,
820 struct samsync_ldb_state *state,
821 enum netr_SamDatabaseID database,
822 struct netr_DELTA_ENUM *delta,
825 uint32_t rid = delta->delta_id_union.rid;
826 struct ldb_message **msgs;
828 const char *attrs[] = { NULL };
830 /* search for the alias, by rid */
831 ret = gendb_search(state->sam_ldb, mem_ctx, state->base_dn[database], &msgs, attrs,
832 "(&(objectClass=group)(objectSid=%s))",
833 ldap_encode_ndr_dom_sid(mem_ctx, dom_sid_add_rid(mem_ctx, state->dom_sid[database], rid)));
836 *error_string = talloc_asprintf(mem_ctx, "gendb_search failed: %s", ldb_errstring(state->sam_ldb));
837 return NT_STATUS_INTERNAL_DB_CORRUPTION;
838 } else if (ret == 0) {
839 return NT_STATUS_NO_SUCH_ALIAS;
840 } else if (ret > 1) {
841 return NT_STATUS_INTERNAL_DB_CORRUPTION;
844 ret = ldb_delete(state->sam_ldb, msgs[0]->dn);
846 *error_string = talloc_asprintf(mem_ctx, "Failed to delete alias record %s: %s",
847 ldb_dn_get_linearized(msgs[0]->dn),
848 ldb_errstring(state->sam_ldb));
849 return NT_STATUS_INTERNAL_DB_CORRUPTION;
855 static NTSTATUS samsync_ldb_handle_alias_member(TALLOC_CTX *mem_ctx,
856 struct samsync_ldb_state *state,
857 enum netr_SamDatabaseID database,
858 struct netr_DELTA_ENUM *delta,
861 uint32_t rid = delta->delta_id_union.rid;
862 struct netr_DELTA_ALIAS_MEMBER *alias_member = delta->delta_union.alias_member;
863 struct ldb_message *msg;
864 struct ldb_message **msgs;
866 const char *attrs[] = { NULL };
869 msg = ldb_msg_new(mem_ctx);
871 return NT_STATUS_NO_MEMORY;
874 /* search for the alias, by rid */
875 ret = gendb_search(state->sam_ldb, mem_ctx, state->base_dn[database], &msgs, attrs,
876 "(&(objectClass=group)(objectSid=%s))",
877 ldap_encode_ndr_dom_sid(mem_ctx, dom_sid_add_rid(mem_ctx, state->dom_sid[database], rid)));
880 *error_string = talloc_asprintf(mem_ctx, "gendb_search failed: %s", ldb_errstring(state->sam_ldb));
881 return NT_STATUS_INTERNAL_DB_CORRUPTION;
882 } else if (ret == 0) {
883 return NT_STATUS_NO_SUCH_GROUP;
884 } else if (ret > 1) {
885 *error_string = talloc_asprintf(mem_ctx, "More than one group/alias with SID: %s",
886 dom_sid_string(mem_ctx,
887 dom_sid_add_rid(mem_ctx,
888 state->dom_sid[database],
890 return NT_STATUS_INTERNAL_DB_CORRUPTION;
892 msg->dn = talloc_steal(msg, msgs[0]->dn);
897 for (i=0; i<alias_member->sids.num_sids; i++) {
898 struct ldb_dn *alias_member_dn;
899 /* search for members, in the top basedn (normal users are builtin aliases) */
900 ret = gendb_search(state->sam_ldb, mem_ctx, state->base_dn[SAM_DATABASE_DOMAIN], &msgs, attrs,
902 ldap_encode_ndr_dom_sid(mem_ctx, alias_member->sids.sids[i].sid));
905 *error_string = talloc_asprintf(mem_ctx, "gendb_search failed: %s", ldb_errstring(state->sam_ldb));
906 return NT_STATUS_INTERNAL_DB_CORRUPTION;
907 } else if (ret == 0) {
909 nt_status = samsync_ldb_add_foreignSecurityPrincipal(mem_ctx, state,
910 alias_member->sids.sids[i].sid,
913 if (!NT_STATUS_IS_OK(nt_status)) {
916 } else if (ret > 1) {
917 return NT_STATUS_INTERNAL_DB_CORRUPTION;
919 alias_member_dn = msgs[0]->dn;
921 samdb_msg_add_string(state->sam_ldb, mem_ctx, msg, "member", ldb_dn_alloc_linearized(mem_ctx, alias_member_dn));
926 ret = samdb_replace(state->sam_ldb, mem_ctx, msg);
928 *error_string = talloc_asprintf(mem_ctx, "Failed to modify group record %s: %s",
929 ldb_dn_get_linearized(msg->dn),
930 ldb_errstring(state->sam_ldb));
931 return NT_STATUS_INTERNAL_DB_CORRUPTION;
937 static NTSTATUS samsync_ldb_handle_account(TALLOC_CTX *mem_ctx,
938 struct samsync_ldb_state *state,
939 enum netr_SamDatabaseID database,
940 struct netr_DELTA_ENUM *delta,
943 struct dom_sid *sid = delta->delta_id_union.sid;
944 struct netr_DELTA_ACCOUNT *account = delta->delta_union.account;
946 struct ldb_message *msg;
947 struct ldb_message **msgs;
948 struct ldb_dn *privilege_dn;
950 const char *attrs[] = { NULL };
953 msg = ldb_msg_new(mem_ctx);
955 return NT_STATUS_NO_MEMORY;
958 /* search for the account, by sid, in the top basedn */
959 ret = gendb_search(state->sam_ldb, mem_ctx, state->base_dn[SAM_DATABASE_DOMAIN], &msgs, attrs,
960 "(objectSid=%s)", ldap_encode_ndr_dom_sid(mem_ctx, sid));
963 *error_string = talloc_asprintf(mem_ctx, "gendb_search failed: %s", ldb_errstring(state->sam_ldb));
964 return NT_STATUS_INTERNAL_DB_CORRUPTION;
965 } else if (ret == 0) {
967 nt_status = samsync_ldb_add_foreignSecurityPrincipal(mem_ctx, state,
971 privilege_dn = talloc_steal(msg, privilege_dn);
972 if (!NT_STATUS_IS_OK(nt_status)) {
975 } else if (ret > 1) {
976 *error_string = talloc_asprintf(mem_ctx, "More than one account with SID: %s",
977 dom_sid_string(mem_ctx, sid));
978 return NT_STATUS_INTERNAL_DB_CORRUPTION;
980 privilege_dn = talloc_steal(msg, msgs[0]->dn);
983 msg->dn = privilege_dn;
985 for (i=0; i< account->privilege_entries; i++) {
986 samdb_msg_add_string(state->sam_ldb, mem_ctx, msg, "privilege",
987 account->privilege_name[i].string);
990 ret = samdb_replace(state->sam_ldb, mem_ctx, msg);
992 *error_string = talloc_asprintf(mem_ctx, "Failed to modify privilege record %s",
993 ldb_dn_get_linearized(msg->dn));
994 return NT_STATUS_INTERNAL_DB_CORRUPTION;
1000 static NTSTATUS samsync_ldb_delete_account(TALLOC_CTX *mem_ctx,
1001 struct samsync_ldb_state *state,
1002 enum netr_SamDatabaseID database,
1003 struct netr_DELTA_ENUM *delta,
1004 char **error_string)
1006 struct dom_sid *sid = delta->delta_id_union.sid;
1008 struct ldb_message *msg;
1009 struct ldb_message **msgs;
1011 const char *attrs[] = { NULL };
1013 msg = ldb_msg_new(mem_ctx);
1015 return NT_STATUS_NO_MEMORY;
1018 /* search for the account, by sid, in the top basedn */
1019 ret = gendb_search(state->sam_ldb, mem_ctx, state->base_dn[SAM_DATABASE_DOMAIN], &msgs, attrs,
1021 ldap_encode_ndr_dom_sid(mem_ctx, sid));
1024 *error_string = talloc_asprintf(mem_ctx, "gendb_search failed: %s", ldb_errstring(state->sam_ldb));
1025 return NT_STATUS_INTERNAL_DB_CORRUPTION;
1026 } else if (ret == 0) {
1027 return NT_STATUS_NO_SUCH_USER;
1028 } else if (ret > 1) {
1029 *error_string = talloc_asprintf(mem_ctx, "More than one account with SID: %s",
1030 dom_sid_string(mem_ctx, sid));
1031 return NT_STATUS_INTERNAL_DB_CORRUPTION;
1033 msg->dn = talloc_steal(msg, msgs[0]->dn);
1036 samdb_msg_add_delete(state->sam_ldb, mem_ctx, msg,
1039 ret = samdb_replace(state->sam_ldb, mem_ctx, msg);
1041 *error_string = talloc_asprintf(mem_ctx, "Failed to modify privilege record %s",
1042 ldb_dn_get_linearized(msg->dn));
1043 return NT_STATUS_INTERNAL_DB_CORRUPTION;
1046 return NT_STATUS_OK;
1049 static NTSTATUS libnet_samsync_ldb_fn(TALLOC_CTX *mem_ctx,
1051 enum netr_SamDatabaseID database,
1052 struct netr_DELTA_ENUM *delta,
1053 char **error_string)
1055 NTSTATUS nt_status = NT_STATUS_OK;
1056 struct samsync_ldb_state *state = talloc_get_type(private, struct samsync_ldb_state);
1058 *error_string = NULL;
1059 switch (delta->delta_type) {
1060 case NETR_DELTA_DOMAIN:
1062 nt_status = samsync_ldb_handle_domain(mem_ctx,
1069 case NETR_DELTA_USER:
1071 nt_status = samsync_ldb_handle_user(mem_ctx,
1078 case NETR_DELTA_DELETE_USER:
1080 nt_status = samsync_ldb_delete_user(mem_ctx,
1087 case NETR_DELTA_GROUP:
1089 nt_status = samsync_ldb_handle_group(mem_ctx,
1096 case NETR_DELTA_DELETE_GROUP:
1098 nt_status = samsync_ldb_delete_group(mem_ctx,
1105 case NETR_DELTA_GROUP_MEMBER:
1107 nt_status = samsync_ldb_handle_group_member(mem_ctx,
1114 case NETR_DELTA_ALIAS:
1116 nt_status = samsync_ldb_handle_alias(mem_ctx,
1123 case NETR_DELTA_DELETE_ALIAS:
1125 nt_status = samsync_ldb_delete_alias(mem_ctx,
1132 case NETR_DELTA_ALIAS_MEMBER:
1134 nt_status = samsync_ldb_handle_alias_member(mem_ctx,
1141 case NETR_DELTA_ACCOUNT:
1143 nt_status = samsync_ldb_handle_account(mem_ctx,
1150 case NETR_DELTA_DELETE_ACCOUNT:
1152 nt_status = samsync_ldb_delete_account(mem_ctx,
1160 /* Can't dump them all right now */
1163 if (!NT_STATUS_IS_OK(nt_status) && !*error_string) {
1164 *error_string = talloc_asprintf(mem_ctx, "Failed to handle samsync delta: %s", nt_errstr(nt_status));
1169 static NTSTATUS libnet_samsync_ldb_init(TALLOC_CTX *mem_ctx,
1171 struct libnet_SamSync_state *samsync_state,
1172 char **error_string)
1174 struct samsync_ldb_state *state = talloc_get_type(private, struct samsync_ldb_state);
1175 const char *server = dcerpc_server_name(samsync_state->netlogon_pipe);
1178 state->samsync_state = samsync_state;
1180 ZERO_STRUCT(state->dom_sid);
1181 if (state->samsync_state->domain_sid) {
1182 state->dom_sid[SAM_DATABASE_DOMAIN] = dom_sid_dup(state, state->samsync_state->domain_sid);
1185 state->dom_sid[SAM_DATABASE_BUILTIN] = dom_sid_parse_talloc(state, SID_BUILTIN);
1187 if (state->samsync_state->realm) {
1188 if (!server || !*server) {
1189 /* huh? how do we not have a server name? */
1190 *error_string = talloc_strdup(mem_ctx, "No DCE/RPC server name available. How did we connect?");
1191 return NT_STATUS_INVALID_PARAMETER;
1193 ldap_url = talloc_asprintf(state, "ldap://%s", server);
1195 state->remote_ldb = ldb_wrap_connect(mem_ctx,
1196 state->samsync_state->machine_net_ctx->lp_ctx,
1198 NULL, state->samsync_state->machine_net_ctx->cred,
1200 if (!state->remote_ldb) {
1201 *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);
1202 return NT_STATUS_NO_LOGON_SERVERS;
1205 state->remote_ldb = NULL;
1207 return NT_STATUS_OK;
1210 NTSTATUS libnet_samsync_ldb(struct libnet_context *ctx, TALLOC_CTX *mem_ctx, struct libnet_samsync_ldb *r)
1213 struct libnet_SamSync r2;
1214 struct samsync_ldb_state *state = talloc(mem_ctx, struct samsync_ldb_state);
1217 return NT_STATUS_NO_MEMORY;
1220 state->secrets = NULL;
1221 state->trusted_domains = NULL;
1223 state->sam_ldb = ldb_wrap_connect(mem_ctx,
1225 lp_sam_url(ctx->lp_ctx),
1227 ctx->cred, 0, NULL);
1229 r2.out.error_string = NULL;
1230 r2.in.binding_string = r->in.binding_string;
1231 r2.in.rid_crypt = true;
1232 r2.in.init_fn = libnet_samsync_ldb_init;
1233 r2.in.delta_fn = libnet_samsync_ldb_fn;
1234 r2.in.fn_ctx = state;
1235 r2.in.machine_account = NULL; /* TODO: Create a machine account, fill this in, and the delete it */
1236 nt_status = libnet_SamSync_netlogon(ctx, state, &r2);
1237 r->out.error_string = r2.out.error_string;
1238 talloc_steal(mem_ctx, r->out.error_string);
1240 if (!NT_STATUS_IS_OK(nt_status)) {