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 "librpc/gen_ndr/ndr_misc.h"
32 #include "libcli/security/security.h"
33 #include "param/param.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, *pdb;
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 = ldb_add(state->sam_ldb, msg);
103 if (ret != LDB_SUCCESS) {
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 status = GUID_to_ndr_blob(state->samsync_state->domain_guid, msg, &v);
170 if (!NT_STATUS_IS_OK(status)) {
171 *error_string = talloc_asprintf(mem_ctx, "ndr_push of domain GUID failed!");
175 ldb_msg_add_value(msg, "objectGUID", &v, NULL);
177 } else if (database == SAM_DATABASE_BUILTIN) {
178 /* work out the builtin_dn - useful for so many calls its worth
180 const char *dnstring = samdb_search_string(state->sam_ldb, mem_ctx, NULL,
181 "distinguishedName", "objectClass=builtinDomain");
182 state->base_dn[database] = ldb_dn_new(state, state->sam_ldb, dnstring);
183 if ( ! ldb_dn_validate(state->base_dn[database])) {
184 return NT_STATUS_INTERNAL_ERROR;
188 return NT_STATUS_INVALID_PARAMETER;
191 msg->dn = talloc_reference(mem_ctx, state->base_dn[database]);
193 return NT_STATUS_NO_MEMORY;
196 samdb_msg_add_string(state->sam_ldb, mem_ctx,
197 msg, "oEMInformation", domain->oem_information.string);
199 samdb_msg_add_int64(state->sam_ldb, mem_ctx,
200 msg, "forceLogoff", domain->force_logoff_time);
202 samdb_msg_add_uint(state->sam_ldb, mem_ctx,
203 msg, "minPwdLen", domain->min_password_length);
205 samdb_msg_add_int64(state->sam_ldb, mem_ctx,
206 msg, "maxPwdAge", domain->max_password_age);
208 samdb_msg_add_int64(state->sam_ldb, mem_ctx,
209 msg, "minPwdAge", domain->min_password_age);
211 samdb_msg_add_uint(state->sam_ldb, mem_ctx,
212 msg, "pwdHistoryLength", domain->password_history_length);
214 samdb_msg_add_uint64(state->sam_ldb, mem_ctx,
215 msg, "modifiedCount",
216 domain->sequence_num);
218 samdb_msg_add_uint64(state->sam_ldb, mem_ctx,
219 msg, "creationTime", domain->domain_create_time);
221 /* TODO: Account lockout, password properties */
223 ret = dsdb_replace(state->sam_ldb, msg, 0);
226 return NT_STATUS_INTERNAL_ERROR;
231 static NTSTATUS samsync_ldb_handle_user(TALLOC_CTX *mem_ctx,
232 struct samsync_ldb_state *state,
233 enum netr_SamDatabaseID database,
234 struct netr_DELTA_ENUM *delta,
237 uint32_t rid = delta->delta_id_union.rid;
238 struct netr_DELTA_USER *user = delta->delta_union.user;
239 const char *container, *obj_class;
242 const struct dom_sid *user_sid;
243 struct ldb_message *msg;
244 struct ldb_message **msgs;
245 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);
385 if (samdb_msg_add_parameters(state->sam_ldb, mem_ctx, msg, "userParameters", &user->parameters) != 0) {
386 return NT_STATUS_NO_MEMORY;
389 ADD_OR_DEL(uint, "countryCode", country_code);
390 ADD_OR_DEL(uint, "codePage", code_page);
392 ADD_OR_DEL(string, "profilePath", profile_path.string);
396 for (i=0; remote_attrs[i]; i++) {
397 struct ldb_message_element *el = ldb_msg_find_element(remote_msgs[0], remote_attrs[i]);
399 samdb_msg_add_delete(state->sam_ldb, mem_ctx, msg,
402 ldb_msg_add(msg, el, LDB_FLAG_MOD_REPLACE);
406 acb = user->acct_flags;
407 if (acb & (ACB_WSTRUST)) {
408 cn_name[cn_name_len - 1] = '\0';
409 container = "Computers";
410 obj_class = "computer";
412 } else if (acb & ACB_SVRTRUST) {
413 if (cn_name[cn_name_len - 1] != '$') {
414 return NT_STATUS_FOOBAR;
416 cn_name[cn_name_len - 1] = '\0';
417 container = "Domain Controllers";
418 obj_class = "computer";
424 samdb_msg_add_string(state->sam_ldb, mem_ctx, msg,
425 "objectClass", obj_class);
427 msg->dn = ldb_dn_copy(mem_ctx, state->base_dn[database]);
428 ldb_dn_add_child_fmt(msg->dn, "CN=%s,CN=%s", cn_name, container);
430 return NT_STATUS_NO_MEMORY;
434 ret = ldb_add(state->sam_ldb, msg);
435 if (ret != LDB_SUCCESS) {
436 struct ldb_dn *first_try_dn = msg->dn;
437 /* Try again with the default DN */
439 *error_string = talloc_asprintf(mem_ctx, "Failed to create user record. Tried %s: %s",
440 ldb_dn_get_linearized(first_try_dn),
441 ldb_errstring(state->sam_ldb));
442 return NT_STATUS_INTERNAL_DB_CORRUPTION;
444 msg->dn = talloc_steal(msg, remote_msgs[0]->dn);
445 ret = ldb_add(state->sam_ldb, msg);
446 if (ret != LDB_SUCCESS) {
447 *error_string = talloc_asprintf(mem_ctx, "Failed to create user record. Tried both %s and %s: %s",
448 ldb_dn_get_linearized(first_try_dn),
449 ldb_dn_get_linearized(msg->dn),
450 ldb_errstring(state->sam_ldb));
451 return NT_STATUS_INTERNAL_DB_CORRUPTION;
456 ret = dsdb_replace(state->sam_ldb, msg, 0);
457 if (ret != LDB_SUCCESS) {
458 *error_string = talloc_asprintf(mem_ctx, "Failed to modify user record %s: %s",
459 ldb_dn_get_linearized(msg->dn),
460 ldb_errstring(state->sam_ldb));
461 return NT_STATUS_INTERNAL_DB_CORRUPTION;
468 static NTSTATUS samsync_ldb_delete_user(TALLOC_CTX *mem_ctx,
469 struct samsync_ldb_state *state,
470 enum netr_SamDatabaseID database,
471 struct netr_DELTA_ENUM *delta,
474 uint32_t rid = delta->delta_id_union.rid;
475 struct ldb_message **msgs;
477 const char *attrs[] = { NULL };
479 /* search for the user, by rid */
480 ret = gendb_search(state->sam_ldb, mem_ctx, state->base_dn[database],
481 &msgs, attrs, "(&(objectClass=user)(objectSid=%s))",
482 ldap_encode_ndr_dom_sid(mem_ctx, dom_sid_add_rid(mem_ctx, state->dom_sid[database], rid)));
485 *error_string = talloc_asprintf(mem_ctx, "gendb_search failed: %s", ldb_errstring(state->sam_ldb));
486 return NT_STATUS_INTERNAL_DB_CORRUPTION;
487 } else if (ret == 0) {
488 return NT_STATUS_NO_SUCH_USER;
489 } else if (ret > 1) {
490 *error_string = talloc_asprintf(mem_ctx, "More than one user with SID: %s",
491 dom_sid_string(mem_ctx,
492 dom_sid_add_rid(mem_ctx,
493 state->dom_sid[database],
495 return NT_STATUS_INTERNAL_DB_CORRUPTION;
498 ret = ldb_delete(state->sam_ldb, msgs[0]->dn);
499 if (ret != LDB_SUCCESS) {
500 *error_string = talloc_asprintf(mem_ctx, "Failed to delete user record %s: %s",
501 ldb_dn_get_linearized(msgs[0]->dn),
502 ldb_errstring(state->sam_ldb));
503 return NT_STATUS_INTERNAL_DB_CORRUPTION;
509 static NTSTATUS samsync_ldb_handle_group(TALLOC_CTX *mem_ctx,
510 struct samsync_ldb_state *state,
511 enum netr_SamDatabaseID database,
512 struct netr_DELTA_ENUM *delta,
515 uint32_t rid = delta->delta_id_union.rid;
516 struct netr_DELTA_GROUP *group = delta->delta_union.group;
517 const char *container, *obj_class;
520 struct ldb_message *msg;
521 struct ldb_message **msgs;
524 const char *attrs[] = { NULL };
526 msg = ldb_msg_new(mem_ctx);
528 return NT_STATUS_NO_MEMORY;
531 /* search for the group, by rid */
532 ret = gendb_search(state->sam_ldb, mem_ctx, state->base_dn[database], &msgs, attrs,
533 "(&(objectClass=group)(objectSid=%s))",
534 ldap_encode_ndr_dom_sid(mem_ctx, dom_sid_add_rid(mem_ctx, state->dom_sid[database], rid)));
537 *error_string = talloc_asprintf(mem_ctx, "gendb_search failed: %s", ldb_errstring(state->sam_ldb));
538 return NT_STATUS_INTERNAL_DB_CORRUPTION;
539 } else if (ret == 0) {
541 } else if (ret > 1) {
542 *error_string = talloc_asprintf(mem_ctx, "More than one group/alias with SID: %s",
543 dom_sid_string(mem_ctx,
544 dom_sid_add_rid(mem_ctx,
545 state->dom_sid[database],
547 return NT_STATUS_INTERNAL_DB_CORRUPTION;
549 msg->dn = talloc_steal(msg, msgs[0]->dn);
552 cn_name = group->group_name.string;
554 #define ADD_OR_DEL(type, attrib, field) do { \
555 if (group->field) { \
556 samdb_msg_add_ ## type(state->sam_ldb, mem_ctx, msg, \
557 attrib, group->field); \
559 samdb_msg_add_delete(state->sam_ldb, mem_ctx, msg, \
564 ADD_OR_DEL(string, "samAccountName", group_name.string);
566 if (samdb_msg_add_dom_sid(state->sam_ldb, mem_ctx, msg,
567 "objectSid", dom_sid_add_rid(mem_ctx, state->dom_sid[database], rid))) {
568 return NT_STATUS_NO_MEMORY;
571 ADD_OR_DEL(string, "description", description.string);
579 samdb_msg_add_string(state->sam_ldb, mem_ctx, msg,
580 "objectClass", obj_class);
581 msg->dn = ldb_dn_copy(mem_ctx, state->base_dn[database]);
582 ldb_dn_add_child_fmt(msg->dn, "CN=%s,CN=%s", cn_name, container);
584 return NT_STATUS_NO_MEMORY;
587 ret = ldb_add(state->sam_ldb, msg);
588 if (ret != LDB_SUCCESS) {
589 *error_string = talloc_asprintf(mem_ctx, "Failed to create group record %s: %s",
590 ldb_dn_get_linearized(msg->dn),
591 ldb_errstring(state->sam_ldb));
592 return NT_STATUS_INTERNAL_DB_CORRUPTION;
595 ret = dsdb_replace(state->sam_ldb, msg, 0);
596 if (ret != LDB_SUCCESS) {
597 *error_string = talloc_asprintf(mem_ctx, "Failed to modify group record %s: %s",
598 ldb_dn_get_linearized(msg->dn),
599 ldb_errstring(state->sam_ldb));
600 return NT_STATUS_INTERNAL_DB_CORRUPTION;
607 static NTSTATUS samsync_ldb_delete_group(TALLOC_CTX *mem_ctx,
608 struct samsync_ldb_state *state,
609 enum netr_SamDatabaseID database,
610 struct netr_DELTA_ENUM *delta,
613 uint32_t rid = delta->delta_id_union.rid;
614 struct ldb_message **msgs;
616 const char *attrs[] = { NULL };
618 /* search for the group, by rid */
619 ret = gendb_search(state->sam_ldb, mem_ctx, state->base_dn[database], &msgs, attrs,
620 "(&(objectClass=group)(objectSid=%s))",
621 ldap_encode_ndr_dom_sid(mem_ctx, dom_sid_add_rid(mem_ctx, state->dom_sid[database], rid)));
624 *error_string = talloc_asprintf(mem_ctx, "gendb_search failed: %s", ldb_errstring(state->sam_ldb));
625 return NT_STATUS_INTERNAL_DB_CORRUPTION;
626 } else if (ret == 0) {
627 return NT_STATUS_NO_SUCH_GROUP;
628 } else if (ret > 1) {
629 *error_string = talloc_asprintf(mem_ctx, "More than one group/alias with SID: %s",
630 dom_sid_string(mem_ctx,
631 dom_sid_add_rid(mem_ctx,
632 state->dom_sid[database],
634 return NT_STATUS_INTERNAL_DB_CORRUPTION;
637 ret = ldb_delete(state->sam_ldb, msgs[0]->dn);
638 if (ret != LDB_SUCCESS) {
639 *error_string = talloc_asprintf(mem_ctx, "Failed to delete group record %s: %s",
640 ldb_dn_get_linearized(msgs[0]->dn),
641 ldb_errstring(state->sam_ldb));
642 return NT_STATUS_INTERNAL_DB_CORRUPTION;
648 static NTSTATUS samsync_ldb_handle_group_member(TALLOC_CTX *mem_ctx,
649 struct samsync_ldb_state *state,
650 enum netr_SamDatabaseID database,
651 struct netr_DELTA_ENUM *delta,
654 uint32_t rid = delta->delta_id_union.rid;
655 struct netr_DELTA_GROUP_MEMBER *group_member = delta->delta_union.group_member;
656 struct ldb_message *msg;
657 struct ldb_message **msgs;
659 const char *attrs[] = { NULL };
662 msg = ldb_msg_new(mem_ctx);
664 return NT_STATUS_NO_MEMORY;
667 /* search for the group, by rid */
668 ret = gendb_search(state->sam_ldb, mem_ctx, state->base_dn[database], &msgs, attrs,
669 "(&(objectClass=group)(objectSid=%s))",
670 ldap_encode_ndr_dom_sid(mem_ctx, dom_sid_add_rid(mem_ctx, state->dom_sid[database], rid)));
673 *error_string = talloc_asprintf(mem_ctx, "gendb_search failed: %s", ldb_errstring(state->sam_ldb));
674 return NT_STATUS_INTERNAL_DB_CORRUPTION;
675 } else if (ret == 0) {
676 return NT_STATUS_NO_SUCH_GROUP;
677 } else if (ret > 1) {
678 *error_string = talloc_asprintf(mem_ctx, "More than one group/alias with SID: %s",
679 dom_sid_string(mem_ctx,
680 dom_sid_add_rid(mem_ctx,
681 state->dom_sid[database],
683 return NT_STATUS_INTERNAL_DB_CORRUPTION;
685 msg->dn = talloc_steal(msg, msgs[0]->dn);
690 for (i=0; i<group_member->num_rids; i++) {
691 /* search for the group, by rid */
692 ret = gendb_search(state->sam_ldb, mem_ctx, state->base_dn[database], &msgs, attrs,
693 "(&(objectClass=user)(objectSid=%s))",
694 ldap_encode_ndr_dom_sid(mem_ctx, dom_sid_add_rid(mem_ctx, state->dom_sid[database], group_member->rids[i])));
697 *error_string = talloc_asprintf(mem_ctx, "gendb_search failed: %s", ldb_errstring(state->sam_ldb));
698 return NT_STATUS_INTERNAL_DB_CORRUPTION;
699 } else if (ret == 0) {
700 return NT_STATUS_NO_SUCH_USER;
701 } else if (ret > 1) {
702 return NT_STATUS_INTERNAL_DB_CORRUPTION;
704 samdb_msg_add_string(state->sam_ldb, mem_ctx, msg, "member", ldb_dn_alloc_linearized(mem_ctx, msgs[0]->dn));
710 ret = dsdb_replace(state->sam_ldb, msg, 0);
711 if (ret != LDB_SUCCESS) {
712 *error_string = talloc_asprintf(mem_ctx, "Failed to modify group record %s: %s",
713 ldb_dn_get_linearized(msg->dn),
714 ldb_errstring(state->sam_ldb));
715 return NT_STATUS_INTERNAL_DB_CORRUPTION;
721 static NTSTATUS samsync_ldb_handle_alias(TALLOC_CTX *mem_ctx,
722 struct samsync_ldb_state *state,
723 enum netr_SamDatabaseID database,
724 struct netr_DELTA_ENUM *delta,
727 uint32_t rid = delta->delta_id_union.rid;
728 struct netr_DELTA_ALIAS *alias = delta->delta_union.alias;
729 const char *container, *obj_class;
732 struct ldb_message *msg;
733 struct ldb_message **msgs;
736 const char *attrs[] = { NULL };
738 msg = ldb_msg_new(mem_ctx);
740 return NT_STATUS_NO_MEMORY;
743 /* search for the alias, by rid */
744 ret = gendb_search(state->sam_ldb, mem_ctx, state->base_dn[database], &msgs, attrs,
745 "(&(objectClass=group)(objectSid=%s))",
746 ldap_encode_ndr_dom_sid(mem_ctx, dom_sid_add_rid(mem_ctx, state->dom_sid[database], rid)));
749 *error_string = talloc_asprintf(mem_ctx, "gendb_search failed: %s", ldb_errstring(state->sam_ldb));
750 return NT_STATUS_INTERNAL_DB_CORRUPTION;
751 } else if (ret == 0) {
753 } else if (ret > 1) {
754 *error_string = talloc_asprintf(mem_ctx, "More than one group/alias with SID: %s",
755 dom_sid_string(mem_ctx,
756 dom_sid_add_rid(mem_ctx,
757 state->dom_sid[database],
759 return NT_STATUS_INTERNAL_DB_CORRUPTION;
761 msg->dn = talloc_steal(mem_ctx, msgs[0]->dn);
764 cn_name = alias->alias_name.string;
766 #define ADD_OR_DEL(type, attrib, field) do { \
767 if (alias->field) { \
768 samdb_msg_add_ ## type(state->sam_ldb, mem_ctx, msg, \
769 attrib, alias->field); \
771 samdb_msg_add_delete(state->sam_ldb, mem_ctx, msg, \
776 ADD_OR_DEL(string, "samAccountName", alias_name.string);
778 if (samdb_msg_add_dom_sid(state->sam_ldb, mem_ctx, msg,
779 "objectSid", dom_sid_add_rid(mem_ctx, state->dom_sid[database], rid))) {
780 return NT_STATUS_NO_MEMORY;
783 ADD_OR_DEL(string, "description", description.string);
787 samdb_msg_add_uint(state->sam_ldb, mem_ctx, msg, "groupType", 0x80000004);
793 samdb_msg_add_string(state->sam_ldb, mem_ctx, msg,
794 "objectClass", obj_class);
795 msg->dn = ldb_dn_copy(mem_ctx, state->base_dn[database]);
796 ldb_dn_add_child_fmt(msg->dn, "CN=%s,CN=%s", cn_name, container);
798 return NT_STATUS_NO_MEMORY;
801 ret = ldb_add(state->sam_ldb, msg);
802 if (ret != LDB_SUCCESS) {
803 *error_string = talloc_asprintf(mem_ctx, "Failed to create alias record %s: %s",
804 ldb_dn_get_linearized(msg->dn),
805 ldb_errstring(state->sam_ldb));
806 return NT_STATUS_INTERNAL_DB_CORRUPTION;
809 ret = dsdb_replace(state->sam_ldb, msg, 0);
810 if (ret != LDB_SUCCESS) {
811 *error_string = talloc_asprintf(mem_ctx, "Failed to modify alias record %s: %s",
812 ldb_dn_get_linearized(msg->dn),
813 ldb_errstring(state->sam_ldb));
814 return NT_STATUS_INTERNAL_DB_CORRUPTION;
821 static NTSTATUS samsync_ldb_delete_alias(TALLOC_CTX *mem_ctx,
822 struct samsync_ldb_state *state,
823 enum netr_SamDatabaseID database,
824 struct netr_DELTA_ENUM *delta,
827 uint32_t rid = delta->delta_id_union.rid;
828 struct ldb_message **msgs;
830 const char *attrs[] = { NULL };
832 /* search for the alias, by rid */
833 ret = gendb_search(state->sam_ldb, mem_ctx, state->base_dn[database], &msgs, attrs,
834 "(&(objectClass=group)(objectSid=%s))",
835 ldap_encode_ndr_dom_sid(mem_ctx, dom_sid_add_rid(mem_ctx, state->dom_sid[database], rid)));
838 *error_string = talloc_asprintf(mem_ctx, "gendb_search failed: %s", ldb_errstring(state->sam_ldb));
839 return NT_STATUS_INTERNAL_DB_CORRUPTION;
840 } else if (ret == 0) {
841 return NT_STATUS_NO_SUCH_ALIAS;
842 } else if (ret > 1) {
843 return NT_STATUS_INTERNAL_DB_CORRUPTION;
846 ret = ldb_delete(state->sam_ldb, msgs[0]->dn);
847 if (ret != LDB_SUCCESS) {
848 *error_string = talloc_asprintf(mem_ctx, "Failed to delete alias record %s: %s",
849 ldb_dn_get_linearized(msgs[0]->dn),
850 ldb_errstring(state->sam_ldb));
851 return NT_STATUS_INTERNAL_DB_CORRUPTION;
857 static NTSTATUS samsync_ldb_handle_alias_member(TALLOC_CTX *mem_ctx,
858 struct samsync_ldb_state *state,
859 enum netr_SamDatabaseID database,
860 struct netr_DELTA_ENUM *delta,
863 uint32_t rid = delta->delta_id_union.rid;
864 struct netr_DELTA_ALIAS_MEMBER *alias_member = delta->delta_union.alias_member;
865 struct ldb_message *msg;
866 struct ldb_message **msgs;
868 const char *attrs[] = { NULL };
871 msg = ldb_msg_new(mem_ctx);
873 return NT_STATUS_NO_MEMORY;
876 /* search for the alias, by rid */
877 ret = gendb_search(state->sam_ldb, mem_ctx, state->base_dn[database], &msgs, attrs,
878 "(&(objectClass=group)(objectSid=%s))",
879 ldap_encode_ndr_dom_sid(mem_ctx, dom_sid_add_rid(mem_ctx, state->dom_sid[database], rid)));
882 *error_string = talloc_asprintf(mem_ctx, "gendb_search failed: %s", ldb_errstring(state->sam_ldb));
883 return NT_STATUS_INTERNAL_DB_CORRUPTION;
884 } else if (ret == 0) {
885 return NT_STATUS_NO_SUCH_GROUP;
886 } else if (ret > 1) {
887 *error_string = talloc_asprintf(mem_ctx, "More than one group/alias with SID: %s",
888 dom_sid_string(mem_ctx,
889 dom_sid_add_rid(mem_ctx,
890 state->dom_sid[database],
892 return NT_STATUS_INTERNAL_DB_CORRUPTION;
894 msg->dn = talloc_steal(msg, msgs[0]->dn);
899 for (i=0; i<alias_member->sids.num_sids; i++) {
900 struct ldb_dn *alias_member_dn;
901 /* search for members, in the top basedn (normal users are builtin aliases) */
902 ret = gendb_search(state->sam_ldb, mem_ctx, state->base_dn[SAM_DATABASE_DOMAIN], &msgs, attrs,
904 ldap_encode_ndr_dom_sid(mem_ctx, alias_member->sids.sids[i].sid));
907 *error_string = talloc_asprintf(mem_ctx, "gendb_search failed: %s", ldb_errstring(state->sam_ldb));
908 return NT_STATUS_INTERNAL_DB_CORRUPTION;
909 } else if (ret == 0) {
911 nt_status = samsync_ldb_add_foreignSecurityPrincipal(mem_ctx, state,
912 alias_member->sids.sids[i].sid,
915 if (!NT_STATUS_IS_OK(nt_status)) {
918 } else if (ret > 1) {
919 return NT_STATUS_INTERNAL_DB_CORRUPTION;
921 alias_member_dn = msgs[0]->dn;
923 samdb_msg_add_string(state->sam_ldb, mem_ctx, msg, "member", ldb_dn_alloc_linearized(mem_ctx, alias_member_dn));
928 ret = dsdb_replace(state->sam_ldb, msg, 0);
929 if (ret != LDB_SUCCESS) {
930 *error_string = talloc_asprintf(mem_ctx, "Failed to modify group record %s: %s",
931 ldb_dn_get_linearized(msg->dn),
932 ldb_errstring(state->sam_ldb));
933 return NT_STATUS_INTERNAL_DB_CORRUPTION;
939 static NTSTATUS samsync_ldb_handle_account(TALLOC_CTX *mem_ctx,
940 struct samsync_ldb_state *state,
941 enum netr_SamDatabaseID database,
942 struct netr_DELTA_ENUM *delta,
945 struct dom_sid *sid = delta->delta_id_union.sid;
946 struct netr_DELTA_ACCOUNT *account = delta->delta_union.account;
948 struct ldb_message *msg;
951 char *dnstr, *sidstr;
953 msg = ldb_msg_new(mem_ctx);
955 return NT_STATUS_NO_MEMORY;
958 sidstr = dom_sid_string(msg, sid);
959 NT_STATUS_HAVE_NO_MEMORY_AND_FREE(sidstr, msg);
961 dnstr = talloc_asprintf(msg, "sid=%s", sidstr);
962 NT_STATUS_HAVE_NO_MEMORY_AND_FREE(dnstr, msg);
964 msg->dn = ldb_dn_new(msg, state->pdb, dnstr);
965 NT_STATUS_HAVE_NO_MEMORY_AND_FREE(msg->dn, msg);
967 for (i=0; i< account->privilege_entries; i++) {
968 samdb_msg_add_string(state->pdb, mem_ctx, msg, "privilege",
969 account->privilege_name[i].string);
972 ret = dsdb_replace(state->pdb, msg, 0);
973 if (ret == LDB_ERR_NO_SUCH_OBJECT) {
974 if (samdb_msg_add_dom_sid(state->pdb, msg, msg, "objectSid", sid) != LDB_SUCCESS) {
976 return NT_STATUS_NO_MEMORY;
978 samdb_msg_add_string(state->pdb, msg, msg, "comment", "added via samsync");
979 ret = ldb_add(state->pdb, msg);
982 if (ret != LDB_SUCCESS) {
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 = dsdb_replace(state->sam_ldb, msg, 0);
1031 if (ret != LDB_SUCCESS) {
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_data, 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_data, 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,
1187 state->samsync_state->machine_net_ctx->event_ctx,
1188 state->samsync_state->machine_net_ctx->lp_ctx,
1190 NULL, state->samsync_state->machine_net_ctx->cred,
1192 if (!state->remote_ldb) {
1193 *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);
1194 return NT_STATUS_NO_LOGON_SERVERS;
1197 state->remote_ldb = NULL;
1199 return NT_STATUS_OK;
1202 NTSTATUS libnet_samsync_ldb(struct libnet_context *ctx, TALLOC_CTX *mem_ctx, struct libnet_samsync_ldb *r)
1205 struct libnet_SamSync r2;
1206 struct samsync_ldb_state *state = talloc(mem_ctx, struct samsync_ldb_state);
1209 return NT_STATUS_NO_MEMORY;
1212 state->secrets = NULL;
1213 state->trusted_domains = NULL;
1215 state->sam_ldb = samdb_connect(mem_ctx,
1220 if (!state->sam_ldb) {
1221 return NT_STATUS_INTERNAL_DB_ERROR;
1224 state->pdb = privilege_connect(mem_ctx,
1228 return NT_STATUS_INTERNAL_DB_ERROR;
1231 r2.out.error_string = NULL;
1232 r2.in.binding_string = r->in.binding_string;
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)) {