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.h"
28 #include "dsdb/samdb/samdb.h"
29 #include "auth/auth.h"
30 #include "librpc/gen_ndr/ndr_misc.h"
32 #include "libcli/security/security.h"
33 #include "librpc/rpc/dcerpc.h"
35 struct samsync_ldb_secret {
36 struct samsync_ldb_secret *prev, *next;
42 struct samsync_ldb_trusted_domain {
43 struct samsync_ldb_trusted_domain *prev, *next;
48 struct samsync_ldb_state {
49 /* Values from the LSA lookup */
50 const struct libnet_SamSync_state *samsync_state;
52 struct dom_sid *dom_sid[3];
53 struct ldb_context *sam_ldb, *remote_ldb;
54 struct ldb_dn *base_dn[3];
55 struct samsync_ldb_secret *secrets;
56 struct samsync_ldb_trusted_domain *trusted_domains;
59 static NTSTATUS samsync_ldb_add_foreignSecurityPrincipal(TALLOC_CTX *mem_ctx,
60 struct samsync_ldb_state *state,
62 struct ldb_dn **fsp_dn,
65 const char *sidstr = dom_sid_string(mem_ctx, sid);
66 /* We assume that ForeignSecurityPrincipals are under the BASEDN of the main domain */
67 struct ldb_dn *basedn = samdb_search_dn(state->sam_ldb, mem_ctx,
68 state->base_dn[SAM_DATABASE_DOMAIN],
69 "(&(objectClass=container)(cn=ForeignSecurityPrincipals))");
70 struct ldb_message *msg;
74 return NT_STATUS_NO_MEMORY;
78 *error_string = talloc_asprintf(mem_ctx,
79 "Failed to find DN for "
80 "ForeignSecurityPrincipal container under %s",
81 ldb_dn_get_linearized(state->base_dn[SAM_DATABASE_DOMAIN]));
82 return NT_STATUS_INTERNAL_DB_CORRUPTION;
85 msg = ldb_msg_new(mem_ctx);
87 return NT_STATUS_NO_MEMORY;
90 /* add core elements to the ldb_message for the alias */
92 if ( ! ldb_dn_add_child_fmt(msg->dn, "CN=%s", sidstr))
93 return NT_STATUS_UNSUCCESSFUL;
95 samdb_msg_add_string(state->sam_ldb, mem_ctx, msg,
97 "foreignSecurityPrincipal");
101 /* create the alias */
102 ret = samdb_add(state->sam_ldb, mem_ctx, msg);
104 *error_string = talloc_asprintf(mem_ctx, "Failed to create foreignSecurityPrincipal "
106 ldb_dn_get_linearized(msg->dn),
107 ldb_errstring(state->sam_ldb));
108 return NT_STATUS_INTERNAL_DB_CORRUPTION;
113 static NTSTATUS samsync_ldb_handle_domain(TALLOC_CTX *mem_ctx,
114 struct samsync_ldb_state *state,
115 enum netr_SamDatabaseID database,
116 struct netr_DELTA_ENUM *delta,
119 struct netr_DELTA_DOMAIN *domain = delta->delta_union.domain;
120 const char *domain_name = domain->domain_name.string;
121 struct ldb_message *msg;
124 msg = ldb_msg_new(mem_ctx);
126 return NT_STATUS_NO_MEMORY;
129 if (database == SAM_DATABASE_DOMAIN) {
130 struct ldb_dn *partitions_basedn;
131 const char *domain_attrs[] = {"nETBIOSName", "nCName", NULL};
132 struct ldb_message **msgs_domain;
135 partitions_basedn = samdb_partitions_dn(state->sam_ldb, mem_ctx);
137 ret_domain = gendb_search(state->sam_ldb, mem_ctx, partitions_basedn, &msgs_domain, domain_attrs,
138 "(&(&(nETBIOSName=%s)(objectclass=crossRef))(ncName=*))",
140 if (ret_domain == -1) {
141 *error_string = talloc_asprintf(mem_ctx, "gendb_search for domain failed: %s", ldb_errstring(state->sam_ldb));
142 return NT_STATUS_INTERNAL_DB_CORRUPTION;
145 if (ret_domain != 1) {
146 *error_string = talloc_asprintf(mem_ctx, "Failed to find existing domain record for %s: %d results", domain_name,
148 return NT_STATUS_NO_SUCH_DOMAIN;
151 state->base_dn[database] = samdb_result_dn(state->sam_ldb, state, msgs_domain[0], "nCName", NULL);
153 if (state->dom_sid[database]) {
154 /* Update the domain sid with the incoming
155 * domain (found on LSA pipe, database sid may
157 samdb_msg_add_dom_sid(state->sam_ldb, mem_ctx,
158 msg, "objectSid", state->dom_sid[database]);
160 /* Well, we will have to use the one from the database */
161 state->dom_sid[database] = samdb_search_dom_sid(state->sam_ldb, state,
162 state->base_dn[database],
166 if (state->samsync_state->domain_guid) {
169 nt_status = ndr_push_struct_blob(&v, msg, state->samsync_state->domain_guid,
170 (ndr_push_flags_fn_t)ndr_push_GUID);
171 if (!NT_STATUS_IS_OK(nt_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->comment.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 = samdb_replace(state->sam_ldb, mem_ctx, msg);
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;
250 const char *attrs[] = { NULL };
251 /* we may change this to a global search, then fill in only the things not in ldap later */
252 const char *remote_attrs[] = { "userPrincipalName", "servicePrincipalName",
253 "msDS-KeyVersionNumber", "objectGUID", NULL};
255 user_sid = dom_sid_add_rid(mem_ctx, state->dom_sid[database], rid);
257 return NT_STATUS_NO_MEMORY;
260 msg = ldb_msg_new(mem_ctx);
262 return NT_STATUS_NO_MEMORY;
266 /* search for the user, by rid */
267 ret = gendb_search(state->sam_ldb, mem_ctx, state->base_dn[database],
268 &msgs, attrs, "(&(objectClass=user)(objectSid=%s))",
269 ldap_encode_ndr_dom_sid(mem_ctx, user_sid));
272 *error_string = talloc_asprintf(mem_ctx, "LDB for user %s failed: %s",
273 dom_sid_string(mem_ctx, user_sid),
274 ldb_errstring(state->sam_ldb));
275 return NT_STATUS_INTERNAL_DB_CORRUPTION;
276 } else if (ret == 0) {
278 } else if (ret > 1) {
279 *error_string = talloc_asprintf(mem_ctx, "More than one user with SID: %s in local LDB",
280 dom_sid_string(mem_ctx, user_sid));
281 return NT_STATUS_INTERNAL_DB_CORRUPTION;
283 msg->dn = msgs[0]->dn;
284 talloc_steal(msg, msgs[0]->dn);
287 /* and do the same on the remote database */
288 if (state->remote_ldb) {
289 ret = gendb_search(state->remote_ldb, mem_ctx, state->base_dn[database],
290 &remote_msgs, remote_attrs, "(&(objectClass=user)(objectSid=%s))",
291 ldap_encode_ndr_dom_sid(mem_ctx, user_sid));
294 *error_string = talloc_asprintf(mem_ctx, "remote LDAP for user %s failed: %s",
295 dom_sid_string(mem_ctx, user_sid),
296 ldb_errstring(state->remote_ldb));
297 return NT_STATUS_INTERNAL_DB_CORRUPTION;
298 } else if (ret == 0) {
299 *error_string = talloc_asprintf(mem_ctx, "User exists in samsync but not in remote LDAP domain! (base: %s, SID: %s)",
300 ldb_dn_get_linearized(state->base_dn[database]),
301 dom_sid_string(mem_ctx, user_sid));
302 return NT_STATUS_NO_SUCH_USER;
303 } else if (ret > 1) {
304 *error_string = talloc_asprintf(mem_ctx, "More than one user in remote LDAP domain with SID: %s",
305 dom_sid_string(mem_ctx, user_sid));
306 return NT_STATUS_INTERNAL_DB_CORRUPTION;
308 /* Try to put things in the same location as the remote server */
310 msg->dn = remote_msgs[0]->dn;
311 talloc_steal(msg, remote_msgs[0]->dn);
315 cn_name = talloc_strdup(mem_ctx, user->account_name.string);
316 NT_STATUS_HAVE_NO_MEMORY(cn_name);
317 cn_name_len = strlen(cn_name);
319 #define ADD_OR_DEL(type, attrib, field) do { \
321 samdb_msg_add_ ## type(state->sam_ldb, mem_ctx, msg, \
322 attrib, user->field); \
324 samdb_msg_add_delete(state->sam_ldb, mem_ctx, msg, \
329 ADD_OR_DEL(string, "samAccountName", account_name.string);
330 ADD_OR_DEL(string, "displayName", full_name.string);
332 if (samdb_msg_add_dom_sid(state->sam_ldb, mem_ctx, msg,
333 "objectSid", dom_sid_add_rid(mem_ctx, state->dom_sid[database], rid))) {
334 return NT_STATUS_NO_MEMORY;
337 ADD_OR_DEL(uint, "primaryGroupID", primary_gid);
338 ADD_OR_DEL(string, "homeDirectory", home_directory.string);
339 ADD_OR_DEL(string, "homeDrive", home_drive.string);
340 ADD_OR_DEL(string, "scriptPath", logon_script.string);
341 ADD_OR_DEL(string, "description", description.string);
342 ADD_OR_DEL(string, "userWorkstations", workstations.string);
344 ADD_OR_DEL(uint64, "lastLogon", last_logon);
345 ADD_OR_DEL(uint64, "lastLogoff", last_logoff);
347 if (samdb_msg_add_logon_hours(state->sam_ldb, mem_ctx, msg, "logonHours", &user->logon_hours) != 0) {
348 return NT_STATUS_NO_MEMORY;
351 ADD_OR_DEL(uint, "badPwdCount", bad_password_count);
352 ADD_OR_DEL(uint, "logonCount", logon_count);
354 ADD_OR_DEL(uint64, "pwdLastSet", last_password_change);
355 ADD_OR_DEL(uint64, "accountExpires", acct_expiry);
357 if (samdb_msg_add_acct_flags(state->sam_ldb, mem_ctx, msg,
358 "userAccountControl", user->acct_flags) != 0) {
359 return NT_STATUS_NO_MEMORY;
363 /* Passwords. Ensure there is no plaintext stored against
364 * this entry, as we only have hashes */
365 samdb_msg_add_delete(state->sam_ldb, mem_ctx, msg,
368 if (user->lm_password_present) {
369 samdb_msg_add_hash(state->sam_ldb, mem_ctx, msg,
370 "dBCSPwd", &user->lmpassword);
372 samdb_msg_add_delete(state->sam_ldb, mem_ctx, msg,
375 if (user->nt_password_present) {
376 samdb_msg_add_hash(state->sam_ldb, mem_ctx, msg,
377 "unicodePwd", &user->ntpassword);
379 samdb_msg_add_delete(state->sam_ldb, mem_ctx, msg,
383 ADD_OR_DEL(string, "comment", comment.string);
384 ADD_OR_DEL(string, "userParameters", parameters.string);
385 ADD_OR_DEL(uint, "countryCode", country_code);
386 ADD_OR_DEL(uint, "codePage", code_page);
388 ADD_OR_DEL(string, "profilePath", profile_path.string);
392 for (i=0; remote_attrs[i]; i++) {
393 struct ldb_message_element *el = ldb_msg_find_element(remote_msgs[0], remote_attrs[i]);
395 samdb_msg_add_delete(state->sam_ldb, mem_ctx, msg,
398 ldb_msg_add(msg, el, LDB_FLAG_MOD_REPLACE);
402 acb = user->acct_flags;
403 if (acb & (ACB_WSTRUST)) {
404 cn_name[cn_name_len - 1] = '\0';
405 container = "Computers";
406 obj_class = "computer";
408 } else if (acb & ACB_SVRTRUST) {
409 if (cn_name[cn_name_len - 1] != '$') {
410 return NT_STATUS_FOOBAR;
412 cn_name[cn_name_len - 1] = '\0';
413 container = "Domain Controllers";
414 obj_class = "computer";
420 samdb_msg_add_string(state->sam_ldb, mem_ctx, msg,
421 "objectClass", obj_class);
423 msg->dn = ldb_dn_copy(mem_ctx, state->base_dn[database]);
424 ldb_dn_add_child_fmt(msg->dn, "CN=%s,CN=%s", cn_name, container);
426 return NT_STATUS_NO_MEMORY;
430 ret = samdb_add(state->sam_ldb, mem_ctx, msg);
432 struct ldb_dn *first_try_dn = msg->dn;
433 /* Try again with the default DN */
434 msg->dn = talloc_steal(msg, msgs[0]->dn);
435 ret = samdb_add(state->sam_ldb, mem_ctx, msg);
437 *error_string = talloc_asprintf(mem_ctx, "Failed to create user record. Tried both %s and %s: %s",
438 ldb_dn_get_linearized(first_try_dn),
439 ldb_dn_get_linearized(msg->dn),
440 ldb_errstring(state->sam_ldb));
441 return NT_STATUS_INTERNAL_DB_CORRUPTION;
445 ret = samdb_replace(state->sam_ldb, mem_ctx, msg);
447 *error_string = talloc_asprintf(mem_ctx, "Failed to modify user record %s: %s",
448 ldb_dn_get_linearized(msg->dn),
449 ldb_errstring(state->sam_ldb));
450 return NT_STATUS_INTERNAL_DB_CORRUPTION;
457 static NTSTATUS samsync_ldb_delete_user(TALLOC_CTX *mem_ctx,
458 struct samsync_ldb_state *state,
459 enum netr_SamDatabaseID database,
460 struct netr_DELTA_ENUM *delta,
463 uint32_t rid = delta->delta_id_union.rid;
464 struct ldb_message **msgs;
466 const char *attrs[] = { NULL };
468 /* search for the user, by rid */
469 ret = gendb_search(state->sam_ldb, mem_ctx, state->base_dn[database],
470 &msgs, attrs, "(&(objectClass=user)(objectSid=%s))",
471 ldap_encode_ndr_dom_sid(mem_ctx, dom_sid_add_rid(mem_ctx, state->dom_sid[database], rid)));
474 *error_string = talloc_asprintf(mem_ctx, "gendb_search failed: %s", ldb_errstring(state->sam_ldb));
475 return NT_STATUS_INTERNAL_DB_CORRUPTION;
476 } else if (ret == 0) {
477 return NT_STATUS_NO_SUCH_USER;
478 } else if (ret > 1) {
479 *error_string = talloc_asprintf(mem_ctx, "More than one user with SID: %s",
480 dom_sid_string(mem_ctx,
481 dom_sid_add_rid(mem_ctx,
482 state->dom_sid[database],
484 return NT_STATUS_INTERNAL_DB_CORRUPTION;
487 ret = samdb_delete(state->sam_ldb, mem_ctx, msgs[0]->dn);
489 *error_string = talloc_asprintf(mem_ctx, "Failed to delete user record %s: %s",
490 ldb_dn_get_linearized(msgs[0]->dn),
491 ldb_errstring(state->sam_ldb));
492 return NT_STATUS_INTERNAL_DB_CORRUPTION;
498 static NTSTATUS samsync_ldb_handle_group(TALLOC_CTX *mem_ctx,
499 struct samsync_ldb_state *state,
500 enum netr_SamDatabaseID database,
501 struct netr_DELTA_ENUM *delta,
504 uint32_t rid = delta->delta_id_union.rid;
505 struct netr_DELTA_GROUP *group = delta->delta_union.group;
506 const char *container, *obj_class;
509 struct ldb_message *msg;
510 struct ldb_message **msgs;
513 const char *attrs[] = { NULL };
515 msg = ldb_msg_new(mem_ctx);
517 return NT_STATUS_NO_MEMORY;
520 /* search for the group, by rid */
521 ret = gendb_search(state->sam_ldb, mem_ctx, state->base_dn[database], &msgs, attrs,
522 "(&(objectClass=group)(objectSid=%s))",
523 ldap_encode_ndr_dom_sid(mem_ctx, dom_sid_add_rid(mem_ctx, state->dom_sid[database], rid)));
526 *error_string = talloc_asprintf(mem_ctx, "gendb_search failed: %s", ldb_errstring(state->sam_ldb));
527 return NT_STATUS_INTERNAL_DB_CORRUPTION;
528 } else if (ret == 0) {
530 } else if (ret > 1) {
531 *error_string = talloc_asprintf(mem_ctx, "More than one group/alias with SID: %s",
532 dom_sid_string(mem_ctx,
533 dom_sid_add_rid(mem_ctx,
534 state->dom_sid[database],
536 return NT_STATUS_INTERNAL_DB_CORRUPTION;
538 msg->dn = talloc_steal(msg, msgs[0]->dn);
541 cn_name = group->group_name.string;
543 #define ADD_OR_DEL(type, attrib, field) do { \
544 if (group->field) { \
545 samdb_msg_add_ ## type(state->sam_ldb, mem_ctx, msg, \
546 attrib, group->field); \
548 samdb_msg_add_delete(state->sam_ldb, mem_ctx, msg, \
553 ADD_OR_DEL(string, "samAccountName", group_name.string);
555 if (samdb_msg_add_dom_sid(state->sam_ldb, mem_ctx, msg,
556 "objectSid", dom_sid_add_rid(mem_ctx, state->dom_sid[database], rid))) {
557 return NT_STATUS_NO_MEMORY;
560 ADD_OR_DEL(string, "description", description.string);
568 samdb_msg_add_string(state->sam_ldb, mem_ctx, msg,
569 "objectClass", obj_class);
570 msg->dn = ldb_dn_copy(mem_ctx, state->base_dn[database]);
571 ldb_dn_add_child_fmt(msg->dn, "CN=%s,CN=%s", cn_name, container);
573 return NT_STATUS_NO_MEMORY;
576 ret = samdb_add(state->sam_ldb, mem_ctx, msg);
578 *error_string = talloc_asprintf(mem_ctx, "Failed to create group record %s: %s",
579 ldb_dn_get_linearized(msg->dn),
580 ldb_errstring(state->sam_ldb));
581 return NT_STATUS_INTERNAL_DB_CORRUPTION;
584 ret = samdb_replace(state->sam_ldb, mem_ctx, msg);
586 *error_string = talloc_asprintf(mem_ctx, "Failed to modify group record %s: %s",
587 ldb_dn_get_linearized(msg->dn),
588 ldb_errstring(state->sam_ldb));
589 return NT_STATUS_INTERNAL_DB_CORRUPTION;
596 static NTSTATUS samsync_ldb_delete_group(TALLOC_CTX *mem_ctx,
597 struct samsync_ldb_state *state,
598 enum netr_SamDatabaseID database,
599 struct netr_DELTA_ENUM *delta,
602 uint32_t rid = delta->delta_id_union.rid;
603 struct ldb_message **msgs;
605 const char *attrs[] = { NULL };
607 /* search for the group, by rid */
608 ret = gendb_search(state->sam_ldb, mem_ctx, state->base_dn[database], &msgs, attrs,
609 "(&(objectClass=group)(objectSid=%s))",
610 ldap_encode_ndr_dom_sid(mem_ctx, dom_sid_add_rid(mem_ctx, state->dom_sid[database], rid)));
613 *error_string = talloc_asprintf(mem_ctx, "gendb_search failed: %s", ldb_errstring(state->sam_ldb));
614 return NT_STATUS_INTERNAL_DB_CORRUPTION;
615 } else if (ret == 0) {
616 return NT_STATUS_NO_SUCH_GROUP;
617 } else if (ret > 1) {
618 *error_string = talloc_asprintf(mem_ctx, "More than one group/alias with SID: %s",
619 dom_sid_string(mem_ctx,
620 dom_sid_add_rid(mem_ctx,
621 state->dom_sid[database],
623 return NT_STATUS_INTERNAL_DB_CORRUPTION;
626 ret = samdb_delete(state->sam_ldb, mem_ctx, msgs[0]->dn);
628 *error_string = talloc_asprintf(mem_ctx, "Failed to delete group record %s: %s",
629 ldb_dn_get_linearized(msgs[0]->dn),
630 ldb_errstring(state->sam_ldb));
631 return NT_STATUS_INTERNAL_DB_CORRUPTION;
637 static NTSTATUS samsync_ldb_handle_group_member(TALLOC_CTX *mem_ctx,
638 struct samsync_ldb_state *state,
639 enum netr_SamDatabaseID database,
640 struct netr_DELTA_ENUM *delta,
643 uint32_t rid = delta->delta_id_union.rid;
644 struct netr_DELTA_GROUP_MEMBER *group_member = delta->delta_union.group_member;
645 struct ldb_message *msg;
646 struct ldb_message **msgs;
648 const char *attrs[] = { NULL };
651 msg = ldb_msg_new(mem_ctx);
653 return NT_STATUS_NO_MEMORY;
656 /* search for the group, by rid */
657 ret = gendb_search(state->sam_ldb, mem_ctx, state->base_dn[database], &msgs, attrs,
658 "(&(objectClass=group)(objectSid=%s))",
659 ldap_encode_ndr_dom_sid(mem_ctx, dom_sid_add_rid(mem_ctx, state->dom_sid[database], rid)));
662 *error_string = talloc_asprintf(mem_ctx, "gendb_search failed: %s", ldb_errstring(state->sam_ldb));
663 return NT_STATUS_INTERNAL_DB_CORRUPTION;
664 } else if (ret == 0) {
665 return NT_STATUS_NO_SUCH_GROUP;
666 } else if (ret > 1) {
667 *error_string = talloc_asprintf(mem_ctx, "More than one group/alias with SID: %s",
668 dom_sid_string(mem_ctx,
669 dom_sid_add_rid(mem_ctx,
670 state->dom_sid[database],
672 return NT_STATUS_INTERNAL_DB_CORRUPTION;
674 msg->dn = talloc_steal(msg, msgs[0]->dn);
679 for (i=0; i<group_member->num_rids; i++) {
680 /* search for the group, by rid */
681 ret = gendb_search(state->sam_ldb, mem_ctx, state->base_dn[database], &msgs, attrs,
682 "(&(objectClass=user)(objectSid=%s))",
683 ldap_encode_ndr_dom_sid(mem_ctx, dom_sid_add_rid(mem_ctx, state->dom_sid[database], group_member->rids[i])));
686 *error_string = talloc_asprintf(mem_ctx, "gendb_search failed: %s", ldb_errstring(state->sam_ldb));
687 return NT_STATUS_INTERNAL_DB_CORRUPTION;
688 } else if (ret == 0) {
689 return NT_STATUS_NO_SUCH_USER;
690 } else if (ret > 1) {
691 return NT_STATUS_INTERNAL_DB_CORRUPTION;
693 samdb_msg_add_string(state->sam_ldb, mem_ctx, msg, "member", ldb_dn_alloc_linearized(mem_ctx, msgs[0]->dn));
699 ret = samdb_replace(state->sam_ldb, mem_ctx, msg);
701 *error_string = talloc_asprintf(mem_ctx, "Failed to modify group record %s: %s",
702 ldb_dn_get_linearized(msg->dn),
703 ldb_errstring(state->sam_ldb));
704 return NT_STATUS_INTERNAL_DB_CORRUPTION;
710 static NTSTATUS samsync_ldb_handle_alias(TALLOC_CTX *mem_ctx,
711 struct samsync_ldb_state *state,
712 enum netr_SamDatabaseID database,
713 struct netr_DELTA_ENUM *delta,
716 uint32_t rid = delta->delta_id_union.rid;
717 struct netr_DELTA_ALIAS *alias = delta->delta_union.alias;
718 const char *container, *obj_class;
721 struct ldb_message *msg;
722 struct ldb_message **msgs;
725 const char *attrs[] = { NULL };
727 msg = ldb_msg_new(mem_ctx);
729 return NT_STATUS_NO_MEMORY;
732 /* search for the alias, by rid */
733 ret = gendb_search(state->sam_ldb, mem_ctx, state->base_dn[database], &msgs, attrs,
734 "(&(objectClass=group)(objectSid=%s))",
735 ldap_encode_ndr_dom_sid(mem_ctx, dom_sid_add_rid(mem_ctx, state->dom_sid[database], rid)));
738 *error_string = talloc_asprintf(mem_ctx, "gendb_search failed: %s", ldb_errstring(state->sam_ldb));
739 return NT_STATUS_INTERNAL_DB_CORRUPTION;
740 } else if (ret == 0) {
742 } else if (ret > 1) {
743 *error_string = talloc_asprintf(mem_ctx, "More than one group/alias with SID: %s",
744 dom_sid_string(mem_ctx,
745 dom_sid_add_rid(mem_ctx,
746 state->dom_sid[database],
748 return NT_STATUS_INTERNAL_DB_CORRUPTION;
750 msg->dn = talloc_steal(mem_ctx, msgs[0]->dn);
753 cn_name = alias->alias_name.string;
755 #define ADD_OR_DEL(type, attrib, field) do { \
756 if (alias->field) { \
757 samdb_msg_add_ ## type(state->sam_ldb, mem_ctx, msg, \
758 attrib, alias->field); \
760 samdb_msg_add_delete(state->sam_ldb, mem_ctx, msg, \
765 ADD_OR_DEL(string, "samAccountName", alias_name.string);
767 if (samdb_msg_add_dom_sid(state->sam_ldb, mem_ctx, msg,
768 "objectSid", dom_sid_add_rid(mem_ctx, state->dom_sid[database], rid))) {
769 return NT_STATUS_NO_MEMORY;
772 ADD_OR_DEL(string, "description", description.string);
776 samdb_msg_add_uint(state->sam_ldb, mem_ctx, msg, "groupType", 0x80000004);
782 samdb_msg_add_string(state->sam_ldb, mem_ctx, msg,
783 "objectClass", obj_class);
784 msg->dn = ldb_dn_copy(mem_ctx, state->base_dn[database]);
785 ldb_dn_add_child_fmt(msg->dn, "CN=%s,CN=%s", cn_name, container);
787 return NT_STATUS_NO_MEMORY;
790 ret = samdb_add(state->sam_ldb, mem_ctx, msg);
792 *error_string = talloc_asprintf(mem_ctx, "Failed to create alias record %s: %s",
793 ldb_dn_get_linearized(msg->dn),
794 ldb_errstring(state->sam_ldb));
795 return NT_STATUS_INTERNAL_DB_CORRUPTION;
798 ret = samdb_replace(state->sam_ldb, mem_ctx, msg);
800 *error_string = talloc_asprintf(mem_ctx, "Failed to modify alias record %s: %s",
801 ldb_dn_get_linearized(msg->dn),
802 ldb_errstring(state->sam_ldb));
803 return NT_STATUS_INTERNAL_DB_CORRUPTION;
810 static NTSTATUS samsync_ldb_delete_alias(TALLOC_CTX *mem_ctx,
811 struct samsync_ldb_state *state,
812 enum netr_SamDatabaseID database,
813 struct netr_DELTA_ENUM *delta,
816 uint32_t rid = delta->delta_id_union.rid;
817 struct ldb_message **msgs;
819 const char *attrs[] = { NULL };
821 /* search for the alias, by rid */
822 ret = gendb_search(state->sam_ldb, mem_ctx, state->base_dn[database], &msgs, attrs,
823 "(&(objectClass=group)(objectSid=%s))",
824 ldap_encode_ndr_dom_sid(mem_ctx, dom_sid_add_rid(mem_ctx, state->dom_sid[database], rid)));
827 *error_string = talloc_asprintf(mem_ctx, "gendb_search failed: %s", ldb_errstring(state->sam_ldb));
828 return NT_STATUS_INTERNAL_DB_CORRUPTION;
829 } else if (ret == 0) {
830 return NT_STATUS_NO_SUCH_ALIAS;
831 } else if (ret > 1) {
832 return NT_STATUS_INTERNAL_DB_CORRUPTION;
835 ret = samdb_delete(state->sam_ldb, mem_ctx, msgs[0]->dn);
837 *error_string = talloc_asprintf(mem_ctx, "Failed to delete alias record %s: %s",
838 ldb_dn_get_linearized(msgs[0]->dn),
839 ldb_errstring(state->sam_ldb));
840 return NT_STATUS_INTERNAL_DB_CORRUPTION;
846 static NTSTATUS samsync_ldb_handle_alias_member(TALLOC_CTX *mem_ctx,
847 struct samsync_ldb_state *state,
848 enum netr_SamDatabaseID database,
849 struct netr_DELTA_ENUM *delta,
852 uint32_t rid = delta->delta_id_union.rid;
853 struct netr_DELTA_ALIAS_MEMBER *alias_member = delta->delta_union.alias_member;
854 struct ldb_message *msg;
855 struct ldb_message **msgs;
857 const char *attrs[] = { NULL };
860 msg = ldb_msg_new(mem_ctx);
862 return NT_STATUS_NO_MEMORY;
865 /* search for the alias, by rid */
866 ret = gendb_search(state->sam_ldb, mem_ctx, state->base_dn[database], &msgs, attrs,
867 "(&(objectClass=group)(objectSid=%s))",
868 ldap_encode_ndr_dom_sid(mem_ctx, dom_sid_add_rid(mem_ctx, state->dom_sid[database], rid)));
871 *error_string = talloc_asprintf(mem_ctx, "gendb_search failed: %s", ldb_errstring(state->sam_ldb));
872 return NT_STATUS_INTERNAL_DB_CORRUPTION;
873 } else if (ret == 0) {
874 return NT_STATUS_NO_SUCH_GROUP;
875 } else if (ret > 1) {
876 *error_string = talloc_asprintf(mem_ctx, "More than one group/alias with SID: %s",
877 dom_sid_string(mem_ctx,
878 dom_sid_add_rid(mem_ctx,
879 state->dom_sid[database],
881 return NT_STATUS_INTERNAL_DB_CORRUPTION;
883 msg->dn = talloc_steal(msg, msgs[0]->dn);
888 for (i=0; i<alias_member->sids.num_sids; i++) {
889 struct ldb_dn *alias_member_dn;
890 /* search for members, in the top basedn (normal users are builtin aliases) */
891 ret = gendb_search(state->sam_ldb, mem_ctx, state->base_dn[SAM_DATABASE_DOMAIN], &msgs, attrs,
893 ldap_encode_ndr_dom_sid(mem_ctx, alias_member->sids.sids[i].sid));
896 *error_string = talloc_asprintf(mem_ctx, "gendb_search failed: %s", ldb_errstring(state->sam_ldb));
897 return NT_STATUS_INTERNAL_DB_CORRUPTION;
898 } else if (ret == 0) {
900 nt_status = samsync_ldb_add_foreignSecurityPrincipal(mem_ctx, state,
901 alias_member->sids.sids[i].sid,
904 if (!NT_STATUS_IS_OK(nt_status)) {
907 } else if (ret > 1) {
908 return NT_STATUS_INTERNAL_DB_CORRUPTION;
910 alias_member_dn = msgs[0]->dn;
912 samdb_msg_add_string(state->sam_ldb, mem_ctx, msg, "member", ldb_dn_alloc_linearized(mem_ctx, alias_member_dn));
917 ret = samdb_replace(state->sam_ldb, mem_ctx, msg);
919 *error_string = talloc_asprintf(mem_ctx, "Failed to modify group record %s: %s",
920 ldb_dn_get_linearized(msg->dn),
921 ldb_errstring(state->sam_ldb));
922 return NT_STATUS_INTERNAL_DB_CORRUPTION;
928 static NTSTATUS samsync_ldb_handle_account(TALLOC_CTX *mem_ctx,
929 struct samsync_ldb_state *state,
930 enum netr_SamDatabaseID database,
931 struct netr_DELTA_ENUM *delta,
934 struct dom_sid *sid = delta->delta_id_union.sid;
935 struct netr_DELTA_ACCOUNT *account = delta->delta_union.account;
937 struct ldb_message *msg;
938 struct ldb_message **msgs;
939 struct ldb_dn *privilege_dn;
941 const char *attrs[] = { NULL };
944 msg = ldb_msg_new(mem_ctx);
946 return NT_STATUS_NO_MEMORY;
949 /* search for the account, by sid, in the top basedn */
950 ret = gendb_search(state->sam_ldb, mem_ctx, state->base_dn[SAM_DATABASE_DOMAIN], &msgs, attrs,
951 "(objectSid=%s)", ldap_encode_ndr_dom_sid(mem_ctx, sid));
954 *error_string = talloc_asprintf(mem_ctx, "gendb_search failed: %s", ldb_errstring(state->sam_ldb));
955 return NT_STATUS_INTERNAL_DB_CORRUPTION;
956 } else if (ret == 0) {
958 nt_status = samsync_ldb_add_foreignSecurityPrincipal(mem_ctx, state,
962 privilege_dn = talloc_steal(msg, privilege_dn);
963 if (!NT_STATUS_IS_OK(nt_status)) {
966 } else if (ret > 1) {
967 *error_string = talloc_asprintf(mem_ctx, "More than one account with SID: %s",
968 dom_sid_string(mem_ctx, sid));
969 return NT_STATUS_INTERNAL_DB_CORRUPTION;
971 privilege_dn = talloc_steal(msg, msgs[0]->dn);
974 msg->dn = privilege_dn;
976 for (i=0; i< account->privilege_entries; i++) {
977 samdb_msg_add_string(state->sam_ldb, mem_ctx, msg, "privilege",
978 account->privilege_name[i].string);
981 ret = samdb_replace(state->sam_ldb, mem_ctx, msg);
983 *error_string = talloc_asprintf(mem_ctx, "Failed to modify privilege record %s",
984 ldb_dn_get_linearized(msg->dn));
985 return NT_STATUS_INTERNAL_DB_CORRUPTION;
991 static NTSTATUS samsync_ldb_delete_account(TALLOC_CTX *mem_ctx,
992 struct samsync_ldb_state *state,
993 enum netr_SamDatabaseID database,
994 struct netr_DELTA_ENUM *delta,
997 struct dom_sid *sid = delta->delta_id_union.sid;
999 struct ldb_message *msg;
1000 struct ldb_message **msgs;
1002 const char *attrs[] = { NULL };
1004 msg = ldb_msg_new(mem_ctx);
1006 return NT_STATUS_NO_MEMORY;
1009 /* search for the account, by sid, in the top basedn */
1010 ret = gendb_search(state->sam_ldb, mem_ctx, state->base_dn[SAM_DATABASE_DOMAIN], &msgs, attrs,
1012 ldap_encode_ndr_dom_sid(mem_ctx, sid));
1015 *error_string = talloc_asprintf(mem_ctx, "gendb_search failed: %s", ldb_errstring(state->sam_ldb));
1016 return NT_STATUS_INTERNAL_DB_CORRUPTION;
1017 } else if (ret == 0) {
1018 return NT_STATUS_NO_SUCH_USER;
1019 } else if (ret > 1) {
1020 *error_string = talloc_asprintf(mem_ctx, "More than one account with SID: %s",
1021 dom_sid_string(mem_ctx, sid));
1022 return NT_STATUS_INTERNAL_DB_CORRUPTION;
1024 msg->dn = talloc_steal(msg, msgs[0]->dn);
1027 samdb_msg_add_delete(state->sam_ldb, mem_ctx, msg,
1030 ret = samdb_replace(state->sam_ldb, mem_ctx, msg);
1032 *error_string = talloc_asprintf(mem_ctx, "Failed to modify privilege record %s",
1033 ldb_dn_get_linearized(msg->dn));
1034 return NT_STATUS_INTERNAL_DB_CORRUPTION;
1037 return NT_STATUS_OK;
1040 static NTSTATUS libnet_samsync_ldb_fn(TALLOC_CTX *mem_ctx,
1042 enum netr_SamDatabaseID database,
1043 struct netr_DELTA_ENUM *delta,
1044 char **error_string)
1046 NTSTATUS nt_status = NT_STATUS_OK;
1047 struct samsync_ldb_state *state = talloc_get_type(private, struct samsync_ldb_state);
1049 *error_string = NULL;
1050 switch (delta->delta_type) {
1051 case NETR_DELTA_DOMAIN:
1053 nt_status = samsync_ldb_handle_domain(mem_ctx,
1060 case NETR_DELTA_USER:
1062 nt_status = samsync_ldb_handle_user(mem_ctx,
1069 case NETR_DELTA_DELETE_USER:
1071 nt_status = samsync_ldb_delete_user(mem_ctx,
1078 case NETR_DELTA_GROUP:
1080 nt_status = samsync_ldb_handle_group(mem_ctx,
1087 case NETR_DELTA_DELETE_GROUP:
1089 nt_status = samsync_ldb_delete_group(mem_ctx,
1096 case NETR_DELTA_GROUP_MEMBER:
1098 nt_status = samsync_ldb_handle_group_member(mem_ctx,
1105 case NETR_DELTA_ALIAS:
1107 nt_status = samsync_ldb_handle_alias(mem_ctx,
1114 case NETR_DELTA_DELETE_ALIAS:
1116 nt_status = samsync_ldb_delete_alias(mem_ctx,
1123 case NETR_DELTA_ALIAS_MEMBER:
1125 nt_status = samsync_ldb_handle_alias_member(mem_ctx,
1132 case NETR_DELTA_ACCOUNT:
1134 nt_status = samsync_ldb_handle_account(mem_ctx,
1141 case NETR_DELTA_DELETE_ACCOUNT:
1143 nt_status = samsync_ldb_delete_account(mem_ctx,
1151 /* Can't dump them all right now */
1154 if (!NT_STATUS_IS_OK(nt_status) && !*error_string) {
1155 *error_string = talloc_asprintf(mem_ctx, "Failed to handle samsync delta: %s", nt_errstr(nt_status));
1160 static NTSTATUS libnet_samsync_ldb_init(TALLOC_CTX *mem_ctx,
1162 struct libnet_SamSync_state *samsync_state,
1163 char **error_string)
1165 struct samsync_ldb_state *state = talloc_get_type(private, struct samsync_ldb_state);
1166 const char *server = dcerpc_server_name(samsync_state->netlogon_pipe);
1169 state->samsync_state = samsync_state;
1171 ZERO_STRUCT(state->dom_sid);
1172 if (state->samsync_state->domain_sid) {
1173 state->dom_sid[SAM_DATABASE_DOMAIN] = dom_sid_dup(state, state->samsync_state->domain_sid);
1176 state->dom_sid[SAM_DATABASE_BUILTIN] = dom_sid_parse_talloc(state, SID_BUILTIN);
1178 if (state->samsync_state->realm) {
1179 if (!server || !*server) {
1180 /* huh? how do we not have a server name? */
1181 *error_string = talloc_strdup(mem_ctx, "No DCE/RPC server name available. How did we connect?");
1182 return NT_STATUS_INVALID_PARAMETER;
1184 ldap_url = talloc_asprintf(state, "ldap://%s", server);
1186 state->remote_ldb = ldb_wrap_connect(mem_ctx, ldap_url,
1187 NULL, state->samsync_state->machine_net_ctx->cred,
1189 if (!state->remote_ldb) {
1190 *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);
1191 return NT_STATUS_NO_LOGON_SERVERS;
1194 state->remote_ldb = NULL;
1196 return NT_STATUS_OK;
1199 NTSTATUS libnet_samsync_ldb(struct libnet_context *ctx, TALLOC_CTX *mem_ctx, struct libnet_samsync_ldb *r)
1202 struct libnet_SamSync r2;
1203 struct samsync_ldb_state *state = talloc(mem_ctx, struct samsync_ldb_state);
1206 return NT_STATUS_NO_MEMORY;
1209 state->secrets = NULL;
1210 state->trusted_domains = NULL;
1212 state->sam_ldb = ldb_wrap_connect(mem_ctx, lp_sam_url(),
1214 ctx->cred, 0, NULL);
1216 r2.out.error_string = NULL;
1217 r2.in.binding_string = r->in.binding_string;
1218 r2.in.rid_crypt = true;
1219 r2.in.init_fn = libnet_samsync_ldb_init;
1220 r2.in.delta_fn = libnet_samsync_ldb_fn;
1221 r2.in.fn_ctx = state;
1222 r2.in.machine_account = NULL; /* TODO: Create a machine account, fill this in, and the delete it */
1223 nt_status = libnet_SamSync_netlogon(ctx, state, &r2);
1224 r->out.error_string = r2.out.error_string;
1225 talloc_steal(mem_ctx, r->out.error_string);
1227 if (!NT_STATUS_IS_OK(nt_status)) {