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
9 This program is free software; you can redistribute it and/or modify
10 it under the terms of the GNU General Public License as published by
11 the Free Software Foundation; either version 2 of the License, or
12 (at your option) any later version.
14 This program is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 GNU General Public License for more details.
19 You should have received a copy of the GNU General Public License
20 along with this program; if not, write to the Free Software
21 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
26 #include "libnet/libnet.h"
27 #include "librpc/gen_ndr/ndr_netlogon.h"
28 #include "librpc/gen_ndr/ndr_samr.h"
29 #include "dlinklist.h"
30 #include "lib/ldb/include/ldb.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 struct dom_sid *dom_sid[3];
47 struct ldb_context *sam_ldb;
49 struct samsync_ldb_secret *secrets;
50 struct samsync_ldb_trusted_domain *trusted_domains;
53 static NTSTATUS samsync_ldb_handle_domain(TALLOC_CTX *mem_ctx,
54 struct samsync_ldb_state *state,
55 struct creds_CredentialState *creds,
56 enum netr_SamDatabaseID database,
57 struct netr_DELTA_ENUM *delta)
59 struct netr_DELTA_DOMAIN *domain = delta->delta_union.domain;
60 const char *domain_name = domain->domain_name.string;
61 struct ldb_message *msg;
64 if (database == SAM_DATABASE_DOMAIN) {
65 const char *domain_attrs[] = {"nETBIOSName", "nCName", NULL};
66 struct ldb_message **msgs_domain;
68 ret_domain = gendb_search(state->sam_ldb, mem_ctx, NULL, &msgs_domain, domain_attrs,
69 "(&(&(nETBIOSName=%s)(objectclass=crossRef))(ncName=*))",
71 if (ret_domain == -1) {
72 return NT_STATUS_INTERNAL_DB_CORRUPTION;
75 if (ret_domain != 1) {
76 return NT_STATUS_NO_SUCH_DOMAIN;
79 state->base_dn[database]
80 = talloc_steal(state, samdb_result_string(msgs_domain[0],
83 state->dom_sid[database]
85 samdb_search_dom_sid(state->sam_ldb, state,
86 state->base_dn[database], "objectSid",
87 "dn=%s", state->base_dn[database]));
88 } else if (database == SAM_DATABASE_BUILTIN) {
89 /* work out the builtin_dn - useful for so many calls its worth
91 state->base_dn[database]
93 samdb_search_string(state->sam_ldb, mem_ctx, NULL,
94 "dn", "objectClass=builtinDomain"));
95 state->dom_sid[database]
96 = dom_sid_parse_talloc(state, SID_BUILTIN);
99 return NT_STATUS_INVALID_PARAMETER;
102 msg = ldb_msg_new(mem_ctx);
104 return NT_STATUS_NO_MEMORY;
107 msg->dn = talloc_reference(mem_ctx, state->base_dn[database]);
109 return NT_STATUS_NO_MEMORY;
112 samdb_msg_add_string(state->sam_ldb, mem_ctx,
113 msg, "oEMInformation", domain->comment.string);
115 samdb_msg_add_uint64(state->sam_ldb, mem_ctx,
116 msg, "forceLogff", domain->force_logoff_time);
118 samdb_msg_add_uint(state->sam_ldb, mem_ctx,
119 msg, "minPwdLen", domain->min_password_length);
121 samdb_msg_add_int64(state->sam_ldb, mem_ctx,
122 msg, "maxPwdAge", domain->max_password_age);
124 samdb_msg_add_int64(state->sam_ldb, mem_ctx,
125 msg, "minPwdAge", domain->min_password_age);
127 samdb_msg_add_uint(state->sam_ldb, mem_ctx,
128 msg, "pwdHistoryLength", domain->password_history_length);
130 samdb_msg_add_uint64(state->sam_ldb, mem_ctx,
131 msg, "modifiedCountAtLastProm",
132 domain->sequence_num);
134 samdb_msg_add_uint64(state->sam_ldb, mem_ctx,
135 msg, "creationTime", domain->domain_create_time);
137 /* TODO: Account lockout, password properties */
139 ret = samdb_replace(state->sam_ldb, mem_ctx, msg);
142 return NT_STATUS_INTERNAL_ERROR;
147 static NTSTATUS samsync_ldb_handle_user(TALLOC_CTX *mem_ctx,
148 struct samsync_ldb_state *state,
149 struct creds_CredentialState *creds,
150 enum netr_SamDatabaseID database,
151 struct netr_DELTA_ENUM *delta)
153 uint32_t rid = delta->delta_id_union.rid;
154 struct netr_DELTA_USER *user = delta->delta_union.user;
155 const char *container, *obj_class;
159 struct ldb_message *msg;
160 struct ldb_message **msgs;
164 const char *attrs[] = { NULL };
166 msg = ldb_msg_new(mem_ctx);
168 return NT_STATUS_NO_MEMORY;
171 /* search for the user, by rid */
172 ret = gendb_search(state->sam_ldb, mem_ctx, state->base_dn[database], &msgs, attrs,
173 "(&(objectClass=user)(objectSid=%s))",
174 ldap_encode_ndr_dom_sid(mem_ctx, dom_sid_add_rid(mem_ctx, state->dom_sid[database], rid)));
177 return NT_STATUS_INTERNAL_DB_CORRUPTION;
178 } else if (ret == 0) {
180 } else if (ret > 1) {
181 return NT_STATUS_INTERNAL_DB_CORRUPTION;
183 msg->dn = talloc_steal(mem_ctx, msgs[0]->dn);
187 cn_name = talloc_strdup(mem_ctx, user->account_name.string);
188 NT_STATUS_HAVE_NO_MEMORY(cn_name);
189 cn_name_len = strlen(cn_name);
191 #define ADD_OR_DEL(type, attrib, field) do {\
193 samdb_msg_add_ ## type(state->sam_ldb, mem_ctx, msg, \
194 attrib, user->field); \
196 samdb_msg_add_delete(state->sam_ldb, mem_ctx, msg, \
201 ADD_OR_DEL(string, "samAccountName", account_name.string);
202 ADD_OR_DEL(string, "displayName", full_name.string);
204 if (samdb_msg_add_dom_sid(state->sam_ldb, mem_ctx, msg,
205 "objectSid", dom_sid_add_rid(mem_ctx, state->dom_sid[database], rid))) {
206 return NT_STATUS_NO_MEMORY;
209 ADD_OR_DEL(uint, "primaryGroupID", primary_gid);
210 ADD_OR_DEL(string, "homeDirectory", home_directory.string);
211 ADD_OR_DEL(string, "homeDrive", home_drive.string);
212 ADD_OR_DEL(string, "scriptPath", logon_script.string);
213 ADD_OR_DEL(string, "description", description.string);
214 ADD_OR_DEL(string, "userWorkstations", workstations.string);
216 ADD_OR_DEL(uint64, "lastLogon", last_logon);
217 ADD_OR_DEL(uint64, "lastLogoff", last_logoff);
219 /* TODO: Logon hours */
221 ADD_OR_DEL(uint, "badPwdCount", bad_password_count);
222 ADD_OR_DEL(uint, "logonCount", logon_count);
224 ADD_OR_DEL(uint64, "pwdLastSet", last_password_change);
225 ADD_OR_DEL(uint64, "accountExpires", acct_expiry);
227 if (samdb_msg_add_acct_flags(state->sam_ldb, mem_ctx, msg,
228 "userAccountConrol", user->acct_flags) != 0) {
229 return NT_STATUS_NO_MEMORY;
233 samdb_msg_add_delete(state->sam_ldb, mem_ctx, msg,
235 if (user->lm_password_present) {
236 samdb_msg_add_hash(state->sam_ldb, mem_ctx, msg,
237 "lmPwdHash", &user->lmpassword);
239 samdb_msg_add_delete(state->sam_ldb, mem_ctx, msg,
242 if (user->nt_password_present) {
243 samdb_msg_add_hash(state->sam_ldb, mem_ctx, msg,
244 "ntPwdHash", &user->ntpassword);
246 samdb_msg_add_delete(state->sam_ldb, mem_ctx, msg,
250 ADD_OR_DEL(string, "comment", comment.string);
251 ADD_OR_DEL(string, "userParameters", parameters.string);
252 ADD_OR_DEL(uint, "countryCode", country_code);
253 ADD_OR_DEL(uint, "codePage", code_page);
255 ADD_OR_DEL(string, "profilePath", profile_path.string);
259 acb = user->acct_flags;
260 if (acb & (ACB_WSTRUST)) {
261 cn_name[cn_name_len - 1] = '\0';
262 container = "Computers";
263 obj_class = "computer";
265 } else if (acb & ACB_SVRTRUST) {
266 if (cn_name[cn_name_len - 1] != '$') {
267 return NT_STATUS_FOOBAR;
269 cn_name[cn_name_len - 1] = '\0';
270 container = "Domain Controllers";
271 obj_class = "computer";
277 samdb_msg_add_string(state->sam_ldb, mem_ctx, msg,
278 "objectClass", obj_class);
279 msg->dn = talloc_asprintf(mem_ctx, "CN=%s,CN=%s,%s",
280 cn_name, container, state->base_dn[database]);
282 return NT_STATUS_NO_MEMORY;
285 ret = samdb_add(state->sam_ldb, mem_ctx, msg);
287 DEBUG(0,("Failed to create user record %s\n", msg->dn));
288 return NT_STATUS_INTERNAL_DB_CORRUPTION;
291 ret = samdb_replace(state->sam_ldb, mem_ctx, msg);
293 DEBUG(0,("Failed to modify user record %s\n", msg->dn));
294 return NT_STATUS_INTERNAL_DB_CORRUPTION;
301 static NTSTATUS samsync_ldb_delete_user(TALLOC_CTX *mem_ctx,
302 struct samsync_ldb_state *state,
303 struct creds_CredentialState *creds,
304 enum netr_SamDatabaseID database,
305 struct netr_DELTA_ENUM *delta)
307 uint32_t rid = delta->delta_id_union.rid;
308 struct ldb_message **msgs;
310 const char *attrs[] = { NULL };
312 /* search for the user, by rid */
313 ret = gendb_search(state->sam_ldb, mem_ctx, state->base_dn[database], &msgs, attrs,
314 "(&(objectClass=user)(objectSid=%s))",
315 ldap_encode_ndr_dom_sid(mem_ctx, dom_sid_add_rid(mem_ctx, state->dom_sid[database], rid)));
318 return NT_STATUS_INTERNAL_DB_CORRUPTION;
319 } else if (ret == 0) {
320 return NT_STATUS_NO_SUCH_USER;
321 } else if (ret > 1) {
322 return NT_STATUS_INTERNAL_DB_CORRUPTION;
325 ret = samdb_delete(state->sam_ldb, mem_ctx, msgs[0]->dn);
327 DEBUG(0,("Failed to delete user record %s: %s\n", msgs[0]->dn, ldb_errstring(state->sam_ldb)));
328 return NT_STATUS_INTERNAL_DB_CORRUPTION;
334 static NTSTATUS samsync_ldb_handle_group(TALLOC_CTX *mem_ctx,
335 struct samsync_ldb_state *state,
336 struct creds_CredentialState *creds,
337 enum netr_SamDatabaseID database,
338 struct netr_DELTA_ENUM *delta)
340 uint32_t rid = delta->delta_id_union.rid;
341 struct netr_DELTA_GROUP *group = delta->delta_union.group;
342 const char *container, *obj_class;
345 struct ldb_message *msg;
346 struct ldb_message **msgs;
349 const char *attrs[] = { NULL };
351 msg = ldb_msg_new(mem_ctx);
353 return NT_STATUS_NO_MEMORY;
356 /* search for the group, by rid */
357 ret = gendb_search(state->sam_ldb, mem_ctx, state->base_dn[database], &msgs, attrs,
358 "(&(objectClass=group)(objectSid=%s))",
359 ldap_encode_ndr_dom_sid(mem_ctx, dom_sid_add_rid(mem_ctx, state->dom_sid[database], rid)));
362 return NT_STATUS_INTERNAL_DB_CORRUPTION;
363 } else if (ret == 0) {
365 } else if (ret > 1) {
366 return NT_STATUS_INTERNAL_DB_CORRUPTION;
368 msg->dn = talloc_steal(mem_ctx, msgs[0]->dn);
371 cn_name = group->group_name.string;
373 #define ADD_OR_DEL(type, attrib, field) do {\
374 if (group->field) { \
375 samdb_msg_add_ ## type(state->sam_ldb, mem_ctx, msg, \
376 attrib, group->field); \
378 samdb_msg_add_delete(state->sam_ldb, mem_ctx, msg, \
383 ADD_OR_DEL(string, "samAccountName", group_name.string);
385 if (samdb_msg_add_dom_sid(state->sam_ldb, mem_ctx, msg,
386 "objectSid", dom_sid_add_rid(mem_ctx, state->dom_sid[database], rid))) {
387 return NT_STATUS_NO_MEMORY;
390 ADD_OR_DEL(string, "description", description.string);
398 samdb_msg_add_string(state->sam_ldb, mem_ctx, msg,
399 "objectClass", obj_class);
400 msg->dn = talloc_asprintf(mem_ctx, "CN=%s,CN=%s,%s",
401 cn_name, container, state->base_dn[database]);
403 return NT_STATUS_NO_MEMORY;
406 ret = samdb_add(state->sam_ldb, mem_ctx, msg);
408 DEBUG(0,("Failed to create group record %s: %s\n", msg->dn, ldb_errstring(state->sam_ldb)));
409 return NT_STATUS_INTERNAL_DB_CORRUPTION;
412 ret = samdb_replace(state->sam_ldb, mem_ctx, msg);
414 DEBUG(0,("Failed to modify group record %s: %s\n", msg->dn, ldb_errstring(state->sam_ldb)));
415 return NT_STATUS_INTERNAL_DB_CORRUPTION;
422 static NTSTATUS samsync_ldb_delete_group(TALLOC_CTX *mem_ctx,
423 struct samsync_ldb_state *state,
424 struct creds_CredentialState *creds,
425 enum netr_SamDatabaseID database,
426 struct netr_DELTA_ENUM *delta)
428 uint32_t rid = delta->delta_id_union.rid;
429 struct ldb_message **msgs;
431 const char *attrs[] = { NULL };
433 /* search for the group, by rid */
434 ret = gendb_search(state->sam_ldb, mem_ctx, state->base_dn[database], &msgs, attrs,
435 "(&(objectClass=group)(objectSid=%s))",
436 ldap_encode_ndr_dom_sid(mem_ctx, dom_sid_add_rid(mem_ctx, state->dom_sid[database], rid)));
439 return NT_STATUS_INTERNAL_DB_CORRUPTION;
440 } else if (ret == 0) {
441 return NT_STATUS_NO_SUCH_GROUP;
442 } else if (ret > 1) {
443 return NT_STATUS_INTERNAL_DB_CORRUPTION;
446 ret = samdb_delete(state->sam_ldb, mem_ctx, msgs[0]->dn);
448 DEBUG(0,("Failed to delete group record %s: %s\n", msgs[0]->dn, ldb_errstring(state->sam_ldb)));
449 return NT_STATUS_INTERNAL_DB_CORRUPTION;
455 static NTSTATUS samsync_ldb_handle_alias(TALLOC_CTX *mem_ctx,
456 struct samsync_ldb_state *state,
457 struct creds_CredentialState *creds,
458 enum netr_SamDatabaseID database,
459 struct netr_DELTA_ENUM *delta)
461 uint32_t rid = delta->delta_id_union.rid;
462 struct netr_DELTA_ALIAS *alias = delta->delta_union.alias;
463 const char *container, *obj_class;
466 struct ldb_message *msg;
467 struct ldb_message **msgs;
470 const char *attrs[] = { NULL };
472 msg = ldb_msg_new(mem_ctx);
474 return NT_STATUS_NO_MEMORY;
477 /* search for the alias, by rid */
478 ret = gendb_search(state->sam_ldb, mem_ctx, state->base_dn[database], &msgs, attrs,
479 "(&(objectClass=group)(objectSid=%s))",
480 ldap_encode_ndr_dom_sid(mem_ctx, dom_sid_add_rid(mem_ctx, state->dom_sid[database], rid)));
483 return NT_STATUS_INTERNAL_DB_CORRUPTION;
484 } else if (ret == 0) {
486 } else if (ret > 1) {
487 return NT_STATUS_INTERNAL_DB_CORRUPTION;
489 msg->dn = talloc_steal(mem_ctx, msgs[0]->dn);
492 cn_name = alias->alias_name.string;
494 #define ADD_OR_DEL(type, attrib, field) do {\
495 if (alias->field) { \
496 samdb_msg_add_ ## type(state->sam_ldb, mem_ctx, msg, \
497 attrib, alias->field); \
499 samdb_msg_add_delete(state->sam_ldb, mem_ctx, msg, \
504 ADD_OR_DEL(string, "samAccountName", alias_name.string);
506 if (samdb_msg_add_dom_sid(state->sam_ldb, mem_ctx, msg,
507 "objectSid", dom_sid_add_rid(mem_ctx, state->dom_sid[database], rid))) {
508 return NT_STATUS_NO_MEMORY;
511 ADD_OR_DEL(string, "description", description.string);
515 samdb_msg_add_string(state->sam_ldb, mem_ctx, msg, "groupType", "0x80000004");
521 samdb_msg_add_string(state->sam_ldb, mem_ctx, msg,
522 "objectClass", obj_class);
523 msg->dn = talloc_asprintf(mem_ctx, "CN=%s,CN=%s,%s",
524 cn_name, container, state->base_dn[database]);
526 return NT_STATUS_NO_MEMORY;
529 ret = samdb_add(state->sam_ldb, mem_ctx, msg);
531 DEBUG(0,("Failed to create alias record %s: %s\n", msg->dn, ldb_errstring(state->sam_ldb)));
532 return NT_STATUS_INTERNAL_DB_CORRUPTION;
535 ret = samdb_replace(state->sam_ldb, mem_ctx, msg);
537 DEBUG(0,("Failed to modify alias record %s: %s\n", msg->dn, ldb_errstring(state->sam_ldb)));
538 return NT_STATUS_INTERNAL_DB_CORRUPTION;
545 static NTSTATUS samsync_ldb_delete_alias(TALLOC_CTX *mem_ctx,
546 struct samsync_ldb_state *state,
547 struct creds_CredentialState *creds,
548 enum netr_SamDatabaseID database,
549 struct netr_DELTA_ENUM *delta)
551 uint32_t rid = delta->delta_id_union.rid;
552 struct ldb_message **msgs;
554 const char *attrs[] = { NULL };
556 /* search for the alias, by rid */
557 ret = gendb_search(state->sam_ldb, mem_ctx, state->base_dn[database], &msgs, attrs,
558 "(&(objectClass=group)(objectSid=%s))",
559 ldap_encode_ndr_dom_sid(mem_ctx, dom_sid_add_rid(mem_ctx, state->dom_sid[database], rid)));
562 return NT_STATUS_INTERNAL_DB_CORRUPTION;
563 } else if (ret == 0) {
564 return NT_STATUS_NO_SUCH_ALIAS;
565 } else if (ret > 1) {
566 return NT_STATUS_INTERNAL_DB_CORRUPTION;
569 ret = samdb_delete(state->sam_ldb, mem_ctx, msgs[0]->dn);
571 DEBUG(0,("Failed to delete alias record %s: %s\n", msgs[0]->dn, ldb_errstring(state->sam_ldb)));
572 return NT_STATUS_INTERNAL_DB_CORRUPTION;
578 static NTSTATUS libnet_samsync_ldb_fn(TALLOC_CTX *mem_ctx,
580 struct creds_CredentialState *creds,
581 enum netr_SamDatabaseID database,
582 struct netr_DELTA_ENUM *delta,
585 NTSTATUS nt_status = NT_STATUS_OK;
586 struct samsync_ldb_state *state = private;
588 *error_string = NULL;
589 switch (delta->delta_type) {
590 case NETR_DELTA_DOMAIN:
592 nt_status = samsync_ldb_handle_domain(mem_ctx,
599 case NETR_DELTA_USER:
601 nt_status = samsync_ldb_handle_user(mem_ctx,
608 case NETR_DELTA_DELETE_USER:
610 nt_status = samsync_ldb_delete_user(mem_ctx,
617 case NETR_DELTA_GROUP:
619 nt_status = samsync_ldb_handle_group(mem_ctx,
626 case NETR_DELTA_DELETE_GROUP:
628 nt_status = samsync_ldb_delete_group(mem_ctx,
635 case NETR_DELTA_ALIAS:
637 nt_status = samsync_ldb_handle_alias(mem_ctx,
644 case NETR_DELTA_DELETE_ALIAS:
646 nt_status = samsync_ldb_delete_alias(mem_ctx,
654 /* Can't dump them all right now */
660 static NTSTATUS libnet_samsync_ldb_netlogon(struct libnet_context *ctx, TALLOC_CTX *mem_ctx, struct libnet_samsync_ldb *r)
663 struct libnet_SamSync r2;
664 struct samsync_ldb_state *state = talloc(mem_ctx, struct samsync_ldb_state);
667 return NT_STATUS_NO_MEMORY;
670 state->secrets = NULL;
671 state->trusted_domains = NULL;
673 state->sam_ldb = samdb_connect(state);
677 r2.error_string = NULL;
678 r2.delta_fn = libnet_samsync_ldb_fn;
680 r2.machine_account = NULL; /* TODO: Create a machine account, fill this in, and the delete it */
681 nt_status = libnet_SamSync_netlogon(ctx, state, &r2);
682 r->error_string = r2.error_string;
684 if (!NT_STATUS_IS_OK(nt_status)) {
694 static NTSTATUS libnet_samsync_ldb_generic(struct libnet_context *ctx, TALLOC_CTX *mem_ctx, struct libnet_samsync_ldb *r)
697 struct libnet_samsync_ldb r2;
698 r2.level = LIBNET_SAMSYNC_LDB_NETLOGON;
699 r2.error_string = NULL;
700 nt_status = libnet_samsync_ldb(ctx, mem_ctx, &r2);
701 r->error_string = r2.error_string;
706 NTSTATUS libnet_samsync_ldb(struct libnet_context *ctx, TALLOC_CTX *mem_ctx, struct libnet_samsync_ldb *r)
709 case LIBNET_SAMSYNC_LDB_GENERIC:
710 return libnet_samsync_ldb_generic(ctx, mem_ctx, r);
711 case LIBNET_SAMSYNC_LDB_NETLOGON:
712 return libnet_samsync_ldb_netlogon(ctx, mem_ctx, r);
715 return NT_STATUS_INVALID_LEVEL;