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 2 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, write to the Free Software
22 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
27 #include "libnet/libnet.h"
28 #include "libcli/ldap/ldap.h"
29 #include "dsdb/samdb/samdb.h"
30 #include "auth/auth.h"
32 struct samsync_ldb_secret {
33 struct samsync_ldb_secret *prev, *next;
39 struct samsync_ldb_trusted_domain {
40 struct samsync_ldb_trusted_domain *prev, *next;
45 struct samsync_ldb_state {
46 /* Values from the LSA lookup */
47 const char *domain_name;
48 const struct dom_sid *domain_sid;
51 struct dom_sid *dom_sid[3];
52 struct ldb_context *sam_ldb, *remote_ldb;
53 struct ldb_dn *base_dn[3];
54 struct samsync_ldb_secret *secrets;
55 struct samsync_ldb_trusted_domain *trusted_domains;
58 static NTSTATUS samsync_ldb_add_foreignSecurityPrincipal(TALLOC_CTX *mem_ctx,
59 struct samsync_ldb_state *state,
61 struct ldb_dn **fsp_dn,
64 const char *sidstr = dom_sid_string(mem_ctx, sid);
65 /* We assume that ForeignSecurityPrincipals are under the BASEDN of the main domain */
66 struct ldb_dn *basedn = samdb_search_dn(state->sam_ldb, mem_ctx,
67 state->base_dn[SAM_DATABASE_DOMAIN],
68 "(&(objectClass=container)(cn=ForeignSecurityPrincipals))");
69 struct ldb_message *msg;
73 return NT_STATUS_NO_MEMORY;
77 *error_string = talloc_asprintf(mem_ctx,
78 "Failed to find DN for "
79 "ForeignSecurityPrincipal container under %s",
80 ldb_dn_linearize(mem_ctx, state->base_dn[SAM_DATABASE_DOMAIN]));
81 return NT_STATUS_INTERNAL_DB_CORRUPTION;
84 msg = ldb_msg_new(mem_ctx);
86 return NT_STATUS_NO_MEMORY;
89 /* add core elements to the ldb_message for the alias */
90 msg->dn = ldb_dn_build_child(mem_ctx, "CN", sidstr, basedn);
92 return NT_STATUS_NO_MEMORY;
94 samdb_msg_add_string(state->sam_ldb, mem_ctx, msg,
96 "foreignSecurityPrincipal");
100 /* create the alias */
101 ret = samdb_add(state->sam_ldb, mem_ctx, msg);
103 *error_string = talloc_asprintf(mem_ctx, "Failed to create foreignSecurityPrincipal "
105 ldb_dn_linearize(mem_ctx, msg->dn),
106 ldb_errstring(state->sam_ldb));
107 return NT_STATUS_INTERNAL_DB_CORRUPTION;
112 static NTSTATUS samsync_ldb_handle_domain(TALLOC_CTX *mem_ctx,
113 struct samsync_ldb_state *state,
114 enum netr_SamDatabaseID database,
115 struct netr_DELTA_ENUM *delta,
118 struct netr_DELTA_DOMAIN *domain = delta->delta_union.domain;
119 const char *domain_name = domain->domain_name.string;
120 struct ldb_message *msg;
123 if (database == SAM_DATABASE_DOMAIN) {
124 const char *domain_attrs[] = {"nETBIOSName", "nCName", NULL};
125 struct ldb_message **msgs_domain;
128 ret_domain = gendb_search(state->sam_ldb, mem_ctx, NULL, &msgs_domain, domain_attrs,
129 "(&(&(nETBIOSName=%s)(objectclass=crossRef))(ncName=*))",
131 if (ret_domain == -1) {
132 *error_string = talloc_asprintf(mem_ctx, "gendb_search for domain failed: %s", ldb_errstring(state->sam_ldb));
133 return NT_STATUS_INTERNAL_DB_CORRUPTION;
136 if (ret_domain != 1) {
137 *error_string = talloc_asprintf(mem_ctx, "Failed to find existing domain record for %s: %d results", domain_name,
139 return NT_STATUS_NO_SUCH_DOMAIN;
142 state->base_dn[database] = samdb_result_dn(state, msgs_domain[0], "nCName", NULL);
144 if (state->domain_sid) {
145 state->dom_sid[database] = dom_sid_dup(state, state->domain_sid);
147 state->dom_sid[database] = samdb_search_dom_sid(state->sam_ldb, state,
148 state->base_dn[database],
151 } else if (database == SAM_DATABASE_BUILTIN) {
152 /* work out the builtin_dn - useful for so many calls its worth
154 const char *dnstring = samdb_search_string(state->sam_ldb, mem_ctx, NULL,
155 "distinguishedName", "objectClass=builtinDomain");
156 state->base_dn[database] = ldb_dn_explode(state, dnstring);
157 state->dom_sid[database] = dom_sid_parse_talloc(state, SID_BUILTIN);
160 return NT_STATUS_INVALID_PARAMETER;
163 msg = ldb_msg_new(mem_ctx);
165 return NT_STATUS_NO_MEMORY;
168 msg->dn = talloc_reference(mem_ctx, state->base_dn[database]);
170 return NT_STATUS_NO_MEMORY;
173 samdb_msg_add_string(state->sam_ldb, mem_ctx,
174 msg, "oEMInformation", domain->comment.string);
176 samdb_msg_add_int64(state->sam_ldb, mem_ctx,
177 msg, "forceLogoff", domain->force_logoff_time);
179 samdb_msg_add_uint(state->sam_ldb, mem_ctx,
180 msg, "minPwdLen", domain->min_password_length);
182 samdb_msg_add_int64(state->sam_ldb, mem_ctx,
183 msg, "maxPwdAge", domain->max_password_age);
185 samdb_msg_add_int64(state->sam_ldb, mem_ctx,
186 msg, "minPwdAge", domain->min_password_age);
188 samdb_msg_add_uint(state->sam_ldb, mem_ctx,
189 msg, "pwdHistoryLength", domain->password_history_length);
191 samdb_msg_add_uint64(state->sam_ldb, mem_ctx,
192 msg, "modifiedCount",
193 domain->sequence_num);
195 samdb_msg_add_uint64(state->sam_ldb, mem_ctx,
196 msg, "creationTime", domain->domain_create_time);
198 /* Update the domain sid with the incoming domain */
199 samdb_msg_add_dom_sid(state->sam_ldb, mem_ctx,
200 msg, "objectSid", state->dom_sid[database]);
202 /* TODO: Account lockout, password properties */
204 ret = samdb_replace(state->sam_ldb, mem_ctx, msg);
207 return NT_STATUS_INTERNAL_ERROR;
212 static NTSTATUS samsync_ldb_handle_user(TALLOC_CTX *mem_ctx,
213 struct samsync_ldb_state *state,
214 enum netr_SamDatabaseID database,
215 struct netr_DELTA_ENUM *delta,
218 uint32_t rid = delta->delta_id_union.rid;
219 struct netr_DELTA_USER *user = delta->delta_union.user;
220 const char *container, *obj_class;
223 const struct dom_sid *user_sid;
224 struct ldb_message *msg;
225 struct ldb_message **msgs;
226 struct ldb_message **remote_msgs;
230 const char *attrs[] = { NULL };
231 /* we may change this to a global search, then fill in only the things not in ldap later */
232 const char *remote_attrs[] = { "userPrincipalName", "servicePrincipalName",
233 "msDS-KeyVersionNumber", NULL};
235 user_sid = dom_sid_add_rid(mem_ctx, state->dom_sid[database], rid);
237 return NT_STATUS_NO_MEMORY;
240 msg = ldb_msg_new(mem_ctx);
242 return NT_STATUS_NO_MEMORY;
245 /* search for the user, by rid */
246 ret = gendb_search(state->sam_ldb, mem_ctx, state->base_dn[database],
247 &msgs, attrs, "(&(objectClass=user)(objectSid=%s))",
248 ldap_encode_ndr_dom_sid(mem_ctx, user_sid));
251 *error_string = talloc_asprintf(mem_ctx, "LDB for user %s failed: %s",
252 dom_sid_string(mem_ctx, user_sid),
253 ldb_errstring(state->sam_ldb));
254 return NT_STATUS_INTERNAL_DB_CORRUPTION;
255 } else if (ret == 0) {
257 } else if (ret > 1) {
258 *error_string = talloc_asprintf(mem_ctx, "More than one user with SID: %s in local LDB",
259 dom_sid_string(mem_ctx, user_sid));
260 return NT_STATUS_INTERNAL_DB_CORRUPTION;
262 msg->dn = msgs[0]->dn;
263 talloc_steal(msg, msgs[0]->dn);
266 /* and do the same on the remote database */
267 ret = gendb_search(state->remote_ldb, mem_ctx, state->base_dn[database],
268 &remote_msgs, remote_attrs, "(&(objectClass=user)(objectSid=%s))",
269 ldap_encode_ndr_dom_sid(mem_ctx, user_sid));
272 *error_string = talloc_asprintf(mem_ctx, "remote LDAP for user %s failed: %s",
273 dom_sid_string(mem_ctx, user_sid),
274 ldb_errstring(state->remote_ldb));
275 return NT_STATUS_INTERNAL_DB_CORRUPTION;
276 } else if (ret == 0) {
277 *error_string = talloc_asprintf(mem_ctx, "User exists in samsync but not in remote LDAP domain! (base: %s, SID: %s)",
278 ldb_dn_linearize(mem_ctx, state->base_dn[database]),
279 dom_sid_string(mem_ctx, user_sid));
280 return NT_STATUS_NO_SUCH_USER;
281 } else if (ret > 1) {
282 *error_string = talloc_asprintf(mem_ctx, "More than one user in remote LDAP domain with SID: %s",
283 dom_sid_string(mem_ctx, user_sid));
284 return NT_STATUS_INTERNAL_DB_CORRUPTION;
286 /* Try to put things in the same location as the remote server */
288 msg->dn = remote_msgs[0]->dn;
289 talloc_steal(msg, remote_msgs[0]->dn);
292 cn_name = talloc_strdup(mem_ctx, user->account_name.string);
293 NT_STATUS_HAVE_NO_MEMORY(cn_name);
294 cn_name_len = strlen(cn_name);
296 #define ADD_OR_DEL(type, attrib, field) do { \
298 samdb_msg_add_ ## type(state->sam_ldb, mem_ctx, msg, \
299 attrib, user->field); \
301 samdb_msg_add_delete(state->sam_ldb, mem_ctx, msg, \
306 ADD_OR_DEL(string, "samAccountName", account_name.string);
307 ADD_OR_DEL(string, "displayName", full_name.string);
309 if (samdb_msg_add_dom_sid(state->sam_ldb, mem_ctx, msg,
310 "objectSid", dom_sid_add_rid(mem_ctx, state->dom_sid[database], rid))) {
311 return NT_STATUS_NO_MEMORY;
314 ADD_OR_DEL(uint, "primaryGroupID", primary_gid);
315 ADD_OR_DEL(string, "homeDirectory", home_directory.string);
316 ADD_OR_DEL(string, "homeDrive", home_drive.string);
317 ADD_OR_DEL(string, "scriptPath", logon_script.string);
318 ADD_OR_DEL(string, "description", description.string);
319 ADD_OR_DEL(string, "userWorkstations", workstations.string);
321 ADD_OR_DEL(uint64, "lastLogon", last_logon);
322 ADD_OR_DEL(uint64, "lastLogoff", last_logoff);
324 if (samdb_msg_add_logon_hours(state->sam_ldb, mem_ctx, msg, "logonHours", &user->logon_hours) != 0) {
325 return NT_STATUS_NO_MEMORY;
328 ADD_OR_DEL(uint, "badPwdCount", bad_password_count);
329 ADD_OR_DEL(uint, "logonCount", logon_count);
331 ADD_OR_DEL(uint64, "pwdLastSet", last_password_change);
332 ADD_OR_DEL(uint64, "accountExpires", acct_expiry);
334 if (samdb_msg_add_acct_flags(state->sam_ldb, mem_ctx, msg,
335 "userAccountControl", user->acct_flags) != 0) {
336 return NT_STATUS_NO_MEMORY;
339 /* Passwords. Ensure there is no plaintext stored against
340 * this entry, as we only have hashes */
341 samdb_msg_add_delete(state->sam_ldb, mem_ctx, msg,
343 if (user->lm_password_present) {
344 samdb_msg_add_hash(state->sam_ldb, mem_ctx, msg,
345 "lmPwdHash", &user->lmpassword);
347 samdb_msg_add_delete(state->sam_ldb, mem_ctx, msg,
350 if (user->nt_password_present) {
351 samdb_msg_add_hash(state->sam_ldb, mem_ctx, msg,
352 "ntPwdHash", &user->ntpassword);
354 samdb_msg_add_delete(state->sam_ldb, mem_ctx, msg,
358 ADD_OR_DEL(string, "comment", comment.string);
359 ADD_OR_DEL(string, "userParameters", parameters.string);
360 ADD_OR_DEL(uint, "countryCode", country_code);
361 ADD_OR_DEL(uint, "codePage", code_page);
363 ADD_OR_DEL(string, "profilePath", profile_path.string);
367 for (i=0; remote_attrs[i]; i++) {
368 struct ldb_message_element *el = ldb_msg_find_element(remote_msgs[0], remote_attrs[i]);
370 samdb_msg_add_delete(state->sam_ldb, mem_ctx, msg,
373 ldb_msg_add(msg, el, LDB_FLAG_MOD_REPLACE);
377 acb = user->acct_flags;
378 if (acb & (ACB_WSTRUST)) {
379 cn_name[cn_name_len - 1] = '\0';
380 container = "Computers";
381 obj_class = "computer";
383 } else if (acb & ACB_SVRTRUST) {
384 if (cn_name[cn_name_len - 1] != '$') {
385 return NT_STATUS_FOOBAR;
387 cn_name[cn_name_len - 1] = '\0';
388 container = "Domain Controllers";
389 obj_class = "computer";
395 samdb_msg_add_string(state->sam_ldb, mem_ctx, msg,
396 "objectClass", obj_class);
397 msg->dn = ldb_dn_string_compose(mem_ctx, state->base_dn[database],
398 "CN=%s, CN=%s", cn_name, container);
400 return NT_STATUS_NO_MEMORY;
403 ret = samdb_add(state->sam_ldb, mem_ctx, msg);
405 struct ldb_dn *first_try_dn = msg->dn;
406 /* Try again with the default DN */
407 msg->dn = talloc_steal(msg, msgs[0]->dn);
408 ret = samdb_add(state->sam_ldb, mem_ctx, msg);
410 *error_string = talloc_asprintf(mem_ctx, "Failed to create user record. Tried both %s and %s: %s",
411 ldb_dn_linearize(mem_ctx, first_try_dn),
412 ldb_dn_linearize(mem_ctx, msg->dn),
413 ldb_errstring(state->sam_ldb));
414 return NT_STATUS_INTERNAL_DB_CORRUPTION;
418 ret = samdb_replace(state->sam_ldb, mem_ctx, msg);
420 *error_string = talloc_asprintf(mem_ctx, "Failed to modify user record %s: %s",
421 ldb_dn_linearize(mem_ctx, msg->dn),
422 ldb_errstring(state->sam_ldb));
423 return NT_STATUS_INTERNAL_DB_CORRUPTION;
430 static NTSTATUS samsync_ldb_delete_user(TALLOC_CTX *mem_ctx,
431 struct samsync_ldb_state *state,
432 enum netr_SamDatabaseID database,
433 struct netr_DELTA_ENUM *delta,
436 uint32_t rid = delta->delta_id_union.rid;
437 struct ldb_message **msgs;
439 const char *attrs[] = { NULL };
441 /* search for the user, by rid */
442 ret = gendb_search(state->sam_ldb, mem_ctx, state->base_dn[database],
443 &msgs, attrs, "(&(objectClass=user)(objectSid=%s))",
444 ldap_encode_ndr_dom_sid(mem_ctx, dom_sid_add_rid(mem_ctx, state->dom_sid[database], rid)));
447 *error_string = talloc_asprintf(mem_ctx, "gendb_search failed: %s", ldb_errstring(state->sam_ldb));
448 return NT_STATUS_INTERNAL_DB_CORRUPTION;
449 } else if (ret == 0) {
450 return NT_STATUS_NO_SUCH_USER;
451 } else if (ret > 1) {
452 *error_string = talloc_asprintf(mem_ctx, "More than one user with SID: %s",
453 dom_sid_string(mem_ctx,
454 dom_sid_add_rid(mem_ctx,
455 state->dom_sid[database],
457 return NT_STATUS_INTERNAL_DB_CORRUPTION;
460 ret = samdb_delete(state->sam_ldb, mem_ctx, msgs[0]->dn);
462 *error_string = talloc_asprintf(mem_ctx, "Failed to delete user record %s: %s",
463 ldb_dn_linearize(mem_ctx, msgs[0]->dn),
464 ldb_errstring(state->sam_ldb));
465 return NT_STATUS_INTERNAL_DB_CORRUPTION;
471 static NTSTATUS samsync_ldb_handle_group(TALLOC_CTX *mem_ctx,
472 struct samsync_ldb_state *state,
473 enum netr_SamDatabaseID database,
474 struct netr_DELTA_ENUM *delta,
477 uint32_t rid = delta->delta_id_union.rid;
478 struct netr_DELTA_GROUP *group = delta->delta_union.group;
479 const char *container, *obj_class;
482 struct ldb_message *msg;
483 struct ldb_message **msgs;
486 const char *attrs[] = { NULL };
488 msg = ldb_msg_new(mem_ctx);
490 return NT_STATUS_NO_MEMORY;
493 /* search for the group, by rid */
494 ret = gendb_search(state->sam_ldb, mem_ctx, state->base_dn[database], &msgs, attrs,
495 "(&(objectClass=group)(objectSid=%s))",
496 ldap_encode_ndr_dom_sid(mem_ctx, dom_sid_add_rid(mem_ctx, state->dom_sid[database], rid)));
499 *error_string = talloc_asprintf(mem_ctx, "gendb_search failed: %s", ldb_errstring(state->sam_ldb));
500 return NT_STATUS_INTERNAL_DB_CORRUPTION;
501 } else if (ret == 0) {
503 } else if (ret > 1) {
504 *error_string = talloc_asprintf(mem_ctx, "More than one group/alias with SID: %s",
505 dom_sid_string(mem_ctx,
506 dom_sid_add_rid(mem_ctx,
507 state->dom_sid[database],
509 return NT_STATUS_INTERNAL_DB_CORRUPTION;
511 msg->dn = talloc_steal(msg, msgs[0]->dn);
514 cn_name = group->group_name.string;
516 #define ADD_OR_DEL(type, attrib, field) do { \
517 if (group->field) { \
518 samdb_msg_add_ ## type(state->sam_ldb, mem_ctx, msg, \
519 attrib, group->field); \
521 samdb_msg_add_delete(state->sam_ldb, mem_ctx, msg, \
526 ADD_OR_DEL(string, "samAccountName", group_name.string);
528 if (samdb_msg_add_dom_sid(state->sam_ldb, mem_ctx, msg,
529 "objectSid", dom_sid_add_rid(mem_ctx, state->dom_sid[database], rid))) {
530 return NT_STATUS_NO_MEMORY;
533 ADD_OR_DEL(string, "description", description.string);
541 samdb_msg_add_string(state->sam_ldb, mem_ctx, msg,
542 "objectClass", obj_class);
543 msg->dn = ldb_dn_string_compose(mem_ctx, state->base_dn[database],
544 "CN=%s, CN=%s", cn_name, container);
546 return NT_STATUS_NO_MEMORY;
549 ret = samdb_add(state->sam_ldb, mem_ctx, msg);
551 *error_string = talloc_asprintf(mem_ctx, "Failed to create group record %s: %s",
552 ldb_dn_linearize(mem_ctx, msg->dn),
553 ldb_errstring(state->sam_ldb));
554 return NT_STATUS_INTERNAL_DB_CORRUPTION;
557 ret = samdb_replace(state->sam_ldb, mem_ctx, msg);
559 *error_string = talloc_asprintf(mem_ctx, "Failed to modify group record %s: %s",
560 ldb_dn_linearize(mem_ctx, msg->dn),
561 ldb_errstring(state->sam_ldb));
562 return NT_STATUS_INTERNAL_DB_CORRUPTION;
569 static NTSTATUS samsync_ldb_delete_group(TALLOC_CTX *mem_ctx,
570 struct samsync_ldb_state *state,
571 enum netr_SamDatabaseID database,
572 struct netr_DELTA_ENUM *delta,
575 uint32_t rid = delta->delta_id_union.rid;
576 struct ldb_message **msgs;
578 const char *attrs[] = { NULL };
580 /* search for the group, by rid */
581 ret = gendb_search(state->sam_ldb, mem_ctx, state->base_dn[database], &msgs, attrs,
582 "(&(objectClass=group)(objectSid=%s))",
583 ldap_encode_ndr_dom_sid(mem_ctx, dom_sid_add_rid(mem_ctx, state->dom_sid[database], rid)));
586 *error_string = talloc_asprintf(mem_ctx, "gendb_search failed: %s", ldb_errstring(state->sam_ldb));
587 return NT_STATUS_INTERNAL_DB_CORRUPTION;
588 } else if (ret == 0) {
589 return NT_STATUS_NO_SUCH_GROUP;
590 } else if (ret > 1) {
591 *error_string = talloc_asprintf(mem_ctx, "More than one group/alias with SID: %s",
592 dom_sid_string(mem_ctx,
593 dom_sid_add_rid(mem_ctx,
594 state->dom_sid[database],
596 return NT_STATUS_INTERNAL_DB_CORRUPTION;
599 ret = samdb_delete(state->sam_ldb, mem_ctx, msgs[0]->dn);
601 *error_string = talloc_asprintf(mem_ctx, "Failed to delete group record %s: %s",
602 ldb_dn_linearize(mem_ctx, msgs[0]->dn),
603 ldb_errstring(state->sam_ldb));
604 return NT_STATUS_INTERNAL_DB_CORRUPTION;
610 static NTSTATUS samsync_ldb_handle_group_member(TALLOC_CTX *mem_ctx,
611 struct samsync_ldb_state *state,
612 enum netr_SamDatabaseID database,
613 struct netr_DELTA_ENUM *delta,
616 uint32_t rid = delta->delta_id_union.rid;
617 struct netr_DELTA_GROUP_MEMBER *group_member = delta->delta_union.group_member;
618 struct ldb_message *msg;
619 struct ldb_message **msgs;
621 const char *attrs[] = { NULL };
624 msg = ldb_msg_new(mem_ctx);
626 return NT_STATUS_NO_MEMORY;
629 /* search for the group, by rid */
630 ret = gendb_search(state->sam_ldb, mem_ctx, state->base_dn[database], &msgs, attrs,
631 "(&(objectClass=group)(objectSid=%s))",
632 ldap_encode_ndr_dom_sid(mem_ctx, dom_sid_add_rid(mem_ctx, state->dom_sid[database], rid)));
635 *error_string = talloc_asprintf(mem_ctx, "gendb_search failed: %s", ldb_errstring(state->sam_ldb));
636 return NT_STATUS_INTERNAL_DB_CORRUPTION;
637 } else if (ret == 0) {
638 return NT_STATUS_NO_SUCH_GROUP;
639 } else if (ret > 1) {
640 *error_string = talloc_asprintf(mem_ctx, "More than one group/alias with SID: %s",
641 dom_sid_string(mem_ctx,
642 dom_sid_add_rid(mem_ctx,
643 state->dom_sid[database],
645 return NT_STATUS_INTERNAL_DB_CORRUPTION;
647 msg->dn = talloc_steal(msg, msgs[0]->dn);
652 for (i=0; i<group_member->num_rids; i++) {
653 /* search for the group, by rid */
654 ret = gendb_search(state->sam_ldb, mem_ctx, state->base_dn[database], &msgs, attrs,
655 "(&(objectClass=user)(objectSid=%s))",
656 ldap_encode_ndr_dom_sid(mem_ctx, dom_sid_add_rid(mem_ctx, state->dom_sid[database], group_member->rids[i])));
659 *error_string = talloc_asprintf(mem_ctx, "gendb_search failed: %s", ldb_errstring(state->sam_ldb));
660 return NT_STATUS_INTERNAL_DB_CORRUPTION;
661 } else if (ret == 0) {
662 return NT_STATUS_NO_SUCH_USER;
663 } else if (ret > 1) {
664 return NT_STATUS_INTERNAL_DB_CORRUPTION;
666 samdb_msg_add_string(state->sam_ldb, mem_ctx, msg, "member", ldb_dn_linearize(mem_ctx, msgs[0]->dn));
672 ret = samdb_replace(state->sam_ldb, mem_ctx, msg);
674 *error_string = talloc_asprintf(mem_ctx, "Failed to modify group record %s: %s",
675 ldb_dn_linearize(mem_ctx, msg->dn),
676 ldb_errstring(state->sam_ldb));
677 return NT_STATUS_INTERNAL_DB_CORRUPTION;
683 static NTSTATUS samsync_ldb_handle_alias(TALLOC_CTX *mem_ctx,
684 struct samsync_ldb_state *state,
685 enum netr_SamDatabaseID database,
686 struct netr_DELTA_ENUM *delta,
689 uint32_t rid = delta->delta_id_union.rid;
690 struct netr_DELTA_ALIAS *alias = delta->delta_union.alias;
691 const char *container, *obj_class;
694 struct ldb_message *msg;
695 struct ldb_message **msgs;
698 const char *attrs[] = { NULL };
700 msg = ldb_msg_new(mem_ctx);
702 return NT_STATUS_NO_MEMORY;
705 /* search for the alias, by rid */
706 ret = gendb_search(state->sam_ldb, mem_ctx, state->base_dn[database], &msgs, attrs,
707 "(&(objectClass=group)(objectSid=%s))",
708 ldap_encode_ndr_dom_sid(mem_ctx, dom_sid_add_rid(mem_ctx, state->dom_sid[database], rid)));
711 *error_string = talloc_asprintf(mem_ctx, "gendb_search failed: %s", ldb_errstring(state->sam_ldb));
712 return NT_STATUS_INTERNAL_DB_CORRUPTION;
713 } else if (ret == 0) {
715 } else if (ret > 1) {
716 *error_string = talloc_asprintf(mem_ctx, "More than one group/alias with SID: %s",
717 dom_sid_string(mem_ctx,
718 dom_sid_add_rid(mem_ctx,
719 state->dom_sid[database],
721 return NT_STATUS_INTERNAL_DB_CORRUPTION;
723 msg->dn = talloc_steal(mem_ctx, msgs[0]->dn);
726 cn_name = alias->alias_name.string;
728 #define ADD_OR_DEL(type, attrib, field) do { \
729 if (alias->field) { \
730 samdb_msg_add_ ## type(state->sam_ldb, mem_ctx, msg, \
731 attrib, alias->field); \
733 samdb_msg_add_delete(state->sam_ldb, mem_ctx, msg, \
738 ADD_OR_DEL(string, "samAccountName", alias_name.string);
740 if (samdb_msg_add_dom_sid(state->sam_ldb, mem_ctx, msg,
741 "objectSid", dom_sid_add_rid(mem_ctx, state->dom_sid[database], rid))) {
742 return NT_STATUS_NO_MEMORY;
745 ADD_OR_DEL(string, "description", description.string);
749 samdb_msg_add_uint(state->sam_ldb, mem_ctx, msg, "groupType", 0x80000004);
755 samdb_msg_add_string(state->sam_ldb, mem_ctx, msg,
756 "objectClass", obj_class);
757 msg->dn = ldb_dn_string_compose(mem_ctx, state->base_dn[database],
758 "CN=%s, CN=%s", cn_name, container);
760 return NT_STATUS_NO_MEMORY;
763 ret = samdb_add(state->sam_ldb, mem_ctx, msg);
765 *error_string = talloc_asprintf(mem_ctx, "Failed to create alias record %s: %s",
766 ldb_dn_linearize(mem_ctx, msg->dn),
767 ldb_errstring(state->sam_ldb));
768 return NT_STATUS_INTERNAL_DB_CORRUPTION;
771 ret = samdb_replace(state->sam_ldb, mem_ctx, msg);
773 *error_string = talloc_asprintf(mem_ctx, "Failed to modify alias record %s: %s",
774 ldb_dn_linearize(mem_ctx, msg->dn),
775 ldb_errstring(state->sam_ldb));
776 return NT_STATUS_INTERNAL_DB_CORRUPTION;
783 static NTSTATUS samsync_ldb_delete_alias(TALLOC_CTX *mem_ctx,
784 struct samsync_ldb_state *state,
785 enum netr_SamDatabaseID database,
786 struct netr_DELTA_ENUM *delta,
789 uint32_t rid = delta->delta_id_union.rid;
790 struct ldb_message **msgs;
792 const char *attrs[] = { NULL };
794 /* search for the alias, by rid */
795 ret = gendb_search(state->sam_ldb, mem_ctx, state->base_dn[database], &msgs, attrs,
796 "(&(objectClass=group)(objectSid=%s))",
797 ldap_encode_ndr_dom_sid(mem_ctx, dom_sid_add_rid(mem_ctx, state->dom_sid[database], rid)));
800 *error_string = talloc_asprintf(mem_ctx, "gendb_search failed: %s", ldb_errstring(state->sam_ldb));
801 return NT_STATUS_INTERNAL_DB_CORRUPTION;
802 } else if (ret == 0) {
803 return NT_STATUS_NO_SUCH_ALIAS;
804 } else if (ret > 1) {
805 return NT_STATUS_INTERNAL_DB_CORRUPTION;
808 ret = samdb_delete(state->sam_ldb, mem_ctx, msgs[0]->dn);
810 *error_string = talloc_asprintf(mem_ctx, "Failed to delete alias record %s: %s",
811 ldb_dn_linearize(mem_ctx, msgs[0]->dn),
812 ldb_errstring(state->sam_ldb));
813 return NT_STATUS_INTERNAL_DB_CORRUPTION;
819 static NTSTATUS samsync_ldb_handle_alias_member(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 netr_DELTA_ALIAS_MEMBER *alias_member = delta->delta_union.alias_member;
827 struct ldb_message *msg;
828 struct ldb_message **msgs;
830 const char *attrs[] = { NULL };
833 msg = ldb_msg_new(mem_ctx);
835 return NT_STATUS_NO_MEMORY;
838 /* search for the alias, by rid */
839 ret = gendb_search(state->sam_ldb, mem_ctx, state->base_dn[database], &msgs, attrs,
840 "(&(objectClass=group)(objectSid=%s))",
841 ldap_encode_ndr_dom_sid(mem_ctx, dom_sid_add_rid(mem_ctx, state->dom_sid[database], rid)));
844 *error_string = talloc_asprintf(mem_ctx, "gendb_search failed: %s", ldb_errstring(state->sam_ldb));
845 return NT_STATUS_INTERNAL_DB_CORRUPTION;
846 } else if (ret == 0) {
847 return NT_STATUS_NO_SUCH_GROUP;
848 } else if (ret > 1) {
849 *error_string = talloc_asprintf(mem_ctx, "More than one group/alias with SID: %s",
850 dom_sid_string(mem_ctx,
851 dom_sid_add_rid(mem_ctx,
852 state->dom_sid[database],
854 return NT_STATUS_INTERNAL_DB_CORRUPTION;
856 msg->dn = talloc_steal(msg, msgs[0]->dn);
861 for (i=0; i<alias_member->sids.num_sids; i++) {
862 struct ldb_dn *alias_member_dn;
863 /* search for members, in the top basedn (normal users are builtin aliases) */
864 ret = gendb_search(state->sam_ldb, mem_ctx, state->base_dn[SAM_DATABASE_DOMAIN], &msgs, attrs,
866 ldap_encode_ndr_dom_sid(mem_ctx, alias_member->sids.sids[i].sid));
869 *error_string = talloc_asprintf(mem_ctx, "gendb_search failed: %s", ldb_errstring(state->sam_ldb));
870 return NT_STATUS_INTERNAL_DB_CORRUPTION;
871 } else if (ret == 0) {
873 nt_status = samsync_ldb_add_foreignSecurityPrincipal(mem_ctx, state,
874 alias_member->sids.sids[i].sid,
877 if (!NT_STATUS_IS_OK(nt_status)) {
880 } else if (ret > 1) {
881 return NT_STATUS_INTERNAL_DB_CORRUPTION;
883 alias_member_dn = msgs[0]->dn;
885 samdb_msg_add_string(state->sam_ldb, mem_ctx, msg, "member", ldb_dn_linearize(mem_ctx, alias_member_dn));
890 ret = samdb_replace(state->sam_ldb, mem_ctx, msg);
892 *error_string = talloc_asprintf(mem_ctx, "Failed to modify group record %s: %s",
893 ldb_dn_linearize(mem_ctx, msg->dn),
894 ldb_errstring(state->sam_ldb));
895 return NT_STATUS_INTERNAL_DB_CORRUPTION;
901 static NTSTATUS samsync_ldb_handle_account(TALLOC_CTX *mem_ctx,
902 struct samsync_ldb_state *state,
903 enum netr_SamDatabaseID database,
904 struct netr_DELTA_ENUM *delta,
907 struct dom_sid *sid = delta->delta_id_union.sid;
908 struct netr_DELTA_ACCOUNT *account = delta->delta_union.account;
910 struct ldb_message *msg;
911 struct ldb_message **msgs;
912 struct ldb_dn *privilege_dn;
914 const char *attrs[] = { NULL };
917 msg = ldb_msg_new(mem_ctx);
919 return NT_STATUS_NO_MEMORY;
922 /* search for the account, by sid, in the top basedn */
923 ret = gendb_search(state->sam_ldb, mem_ctx, state->base_dn[SAM_DATABASE_DOMAIN], &msgs, attrs,
924 "(objectSid=%s)", ldap_encode_ndr_dom_sid(mem_ctx, sid));
927 *error_string = talloc_asprintf(mem_ctx, "gendb_search failed: %s", ldb_errstring(state->sam_ldb));
928 return NT_STATUS_INTERNAL_DB_CORRUPTION;
929 } else if (ret == 0) {
931 nt_status = samsync_ldb_add_foreignSecurityPrincipal(mem_ctx, state,
935 privilege_dn = talloc_steal(msg, privilege_dn);
936 if (!NT_STATUS_IS_OK(nt_status)) {
939 } else if (ret > 1) {
940 *error_string = talloc_asprintf(mem_ctx, "More than one account with SID: %s",
941 dom_sid_string(mem_ctx, sid));
942 return NT_STATUS_INTERNAL_DB_CORRUPTION;
944 privilege_dn = talloc_steal(msg, msgs[0]->dn);
947 msg->dn = privilege_dn;
949 for (i=0; i< account->privilege_entries; i++) {
950 samdb_msg_add_string(state->sam_ldb, mem_ctx, msg, "privilege",
951 account->privilege_name[i].string);
954 ret = samdb_replace(state->sam_ldb, mem_ctx, msg);
956 *error_string = talloc_asprintf(mem_ctx, "Failed to modify privilege record %s",
957 ldb_dn_linearize(mem_ctx, msg->dn));
958 return NT_STATUS_INTERNAL_DB_CORRUPTION;
964 static NTSTATUS samsync_ldb_delete_account(TALLOC_CTX *mem_ctx,
965 struct samsync_ldb_state *state,
966 enum netr_SamDatabaseID database,
967 struct netr_DELTA_ENUM *delta,
970 struct dom_sid *sid = delta->delta_id_union.sid;
972 struct ldb_message *msg;
973 struct ldb_message **msgs;
975 const char *attrs[] = { NULL };
977 msg = ldb_msg_new(mem_ctx);
979 return NT_STATUS_NO_MEMORY;
982 /* search for the account, by sid, in the top basedn */
983 ret = gendb_search(state->sam_ldb, mem_ctx, state->base_dn[SAM_DATABASE_DOMAIN], &msgs, attrs,
985 ldap_encode_ndr_dom_sid(mem_ctx, sid));
988 *error_string = talloc_asprintf(mem_ctx, "gendb_search failed: %s", ldb_errstring(state->sam_ldb));
989 return NT_STATUS_INTERNAL_DB_CORRUPTION;
990 } else if (ret == 0) {
991 return NT_STATUS_NO_SUCH_USER;
992 } else if (ret > 1) {
993 *error_string = talloc_asprintf(mem_ctx, "More than one account with SID: %s",
994 dom_sid_string(mem_ctx, sid));
995 return NT_STATUS_INTERNAL_DB_CORRUPTION;
997 msg->dn = talloc_steal(msg, msgs[0]->dn);
1000 samdb_msg_add_delete(state->sam_ldb, mem_ctx, msg,
1003 ret = samdb_replace(state->sam_ldb, mem_ctx, msg);
1005 *error_string = talloc_asprintf(mem_ctx, "Failed to modify privilege record %s",
1006 ldb_dn_linearize(mem_ctx, msg->dn));
1007 return NT_STATUS_INTERNAL_DB_CORRUPTION;
1010 return NT_STATUS_OK;
1013 static NTSTATUS libnet_samsync_ldb_fn(TALLOC_CTX *mem_ctx,
1015 enum netr_SamDatabaseID database,
1016 struct netr_DELTA_ENUM *delta,
1017 char **error_string)
1019 NTSTATUS nt_status = NT_STATUS_OK;
1020 struct samsync_ldb_state *state = talloc_get_type(private, struct samsync_ldb_state);
1022 *error_string = NULL;
1023 switch (delta->delta_type) {
1024 case NETR_DELTA_DOMAIN:
1026 nt_status = samsync_ldb_handle_domain(mem_ctx,
1033 case NETR_DELTA_USER:
1035 nt_status = samsync_ldb_handle_user(mem_ctx,
1042 case NETR_DELTA_DELETE_USER:
1044 nt_status = samsync_ldb_delete_user(mem_ctx,
1051 case NETR_DELTA_GROUP:
1053 nt_status = samsync_ldb_handle_group(mem_ctx,
1060 case NETR_DELTA_DELETE_GROUP:
1062 nt_status = samsync_ldb_delete_group(mem_ctx,
1069 case NETR_DELTA_GROUP_MEMBER:
1071 nt_status = samsync_ldb_handle_group_member(mem_ctx,
1078 case NETR_DELTA_ALIAS:
1080 nt_status = samsync_ldb_handle_alias(mem_ctx,
1087 case NETR_DELTA_DELETE_ALIAS:
1089 nt_status = samsync_ldb_delete_alias(mem_ctx,
1096 case NETR_DELTA_ALIAS_MEMBER:
1098 nt_status = samsync_ldb_handle_alias_member(mem_ctx,
1105 case NETR_DELTA_ACCOUNT:
1107 nt_status = samsync_ldb_handle_account(mem_ctx,
1114 case NETR_DELTA_DELETE_ACCOUNT:
1116 nt_status = samsync_ldb_delete_account(mem_ctx,
1124 /* Can't dump them all right now */
1127 if (!NT_STATUS_IS_OK(nt_status) && !*error_string) {
1128 *error_string = talloc_asprintf(mem_ctx, "Failed to handle samsync delta: %s", nt_errstr(nt_status));
1133 static NTSTATUS libnet_samsync_ldb_init(TALLOC_CTX *mem_ctx,
1135 struct libnet_context *machine_net_ctx,
1136 struct dcerpc_pipe *p,
1137 const char *domain_name,
1138 const struct dom_sid *domain_sid,
1140 char **error_string)
1142 struct samsync_ldb_state *state = talloc_get_type(private, struct samsync_ldb_state);
1143 const char *server = dcerpc_server_name(p);
1146 state->domain_name = domain_name;
1147 state->domain_sid = domain_sid;
1148 state->realm = realm;
1151 if (!server || !*server) {
1152 /* huh? how do we not have a server name? */
1153 *error_string = talloc_strdup(mem_ctx, "No DCE/RPC server name available. How did we connect?");
1154 return NT_STATUS_INVALID_PARAMETER;
1156 ldap_url = talloc_asprintf(state, "ldap://%s", dcerpc_server_name(p));
1158 state->remote_ldb = ldb_wrap_connect(mem_ctx, ldap_url,
1159 NULL, machine_net_ctx->cred,
1161 /* TODO: Make inquires to see if this is AD, then decide that
1162 * the ldap connection is critical */
1164 return NT_STATUS_OK;
1167 NTSTATUS libnet_samsync_ldb(struct libnet_context *ctx, TALLOC_CTX *mem_ctx, struct libnet_samsync_ldb *r)
1170 struct libnet_SamSync r2;
1171 struct samsync_ldb_state *state = talloc(mem_ctx, struct samsync_ldb_state);
1174 return NT_STATUS_NO_MEMORY;
1177 state->secrets = NULL;
1178 state->trusted_domains = NULL;
1180 state->sam_ldb = samdb_connect(state, system_session(state));
1182 r2.out.error_string = NULL;
1183 r2.in.binding_string = r->in.binding_string;
1184 r2.in.init_fn = libnet_samsync_ldb_init;
1185 r2.in.delta_fn = libnet_samsync_ldb_fn;
1186 r2.in.fn_ctx = state;
1187 r2.in.machine_account = NULL; /* TODO: Create a machine account, fill this in, and the delete it */
1188 nt_status = libnet_SamSync_netlogon(ctx, state, &r2);
1189 r->out.error_string = r2.out.error_string;
1190 talloc_steal(mem_ctx, r->out.error_string);
1192 if (!NT_STATUS_IS_OK(nt_status)) {