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 "../lib/util/util_ldb.h"
31
#include "librpc/gen_ndr/ndr_misc.h"
33
#include "libcli/security/security.h"
34
#include "librpc/rpc/dcerpc.h"
35
#include "param/param.h"
37
struct samsync_ldb_secret {
38
struct samsync_ldb_secret *prev, *next;
44
struct samsync_ldb_trusted_domain {
45
struct samsync_ldb_trusted_domain *prev, *next;
50
struct samsync_ldb_state {
51
/* Values from the LSA lookup */
52
const struct libnet_SamSync_state *samsync_state;
54
struct dom_sid *dom_sid[3];
55
struct ldb_context *sam_ldb, *remote_ldb;
56
struct ldb_dn *base_dn[3];
57
struct samsync_ldb_secret *secrets;
58
struct samsync_ldb_trusted_domain *trusted_domains;
61
static NTSTATUS samsync_ldb_add_foreignSecurityPrincipal(TALLOC_CTX *mem_ctx,
62
struct samsync_ldb_state *state,
64
struct ldb_dn **fsp_dn,
67
const char *sidstr = dom_sid_string(mem_ctx, sid);
68
/* We assume that ForeignSecurityPrincipals are under the BASEDN of the main domain */
69
struct ldb_dn *basedn = samdb_search_dn(state->sam_ldb, mem_ctx,
70
state->base_dn[SAM_DATABASE_DOMAIN],
71
"(&(objectClass=container)(cn=ForeignSecurityPrincipals))");
72
struct ldb_message *msg;
76
return NT_STATUS_NO_MEMORY;
80
*error_string = talloc_asprintf(mem_ctx,
81
"Failed to find DN for "
82
"ForeignSecurityPrincipal container under %s",
83
ldb_dn_get_linearized(state->base_dn[SAM_DATABASE_DOMAIN]));
84
return NT_STATUS_INTERNAL_DB_CORRUPTION;
87
msg = ldb_msg_new(mem_ctx);
89
return NT_STATUS_NO_MEMORY;
92
/* add core elements to the ldb_message for the alias */
94
if ( ! ldb_dn_add_child_fmt(msg->dn, "CN=%s", sidstr))
95
return NT_STATUS_UNSUCCESSFUL;
97
samdb_msg_add_string(state->sam_ldb, mem_ctx, msg,
99
"foreignSecurityPrincipal");
103
/* create the alias */
104
ret = ldb_add(state->sam_ldb, msg);
106
*error_string = talloc_asprintf(mem_ctx, "Failed to create foreignSecurityPrincipal "
108
ldb_dn_get_linearized(msg->dn),
109
ldb_errstring(state->sam_ldb));
110
return NT_STATUS_INTERNAL_DB_CORRUPTION;
115
static NTSTATUS samsync_ldb_handle_domain(TALLOC_CTX *mem_ctx,
116
struct samsync_ldb_state *state,
117
enum netr_SamDatabaseID database,
118
struct netr_DELTA_ENUM *delta,
121
struct netr_DELTA_DOMAIN *domain = delta->delta_union.domain;
122
const char *domain_name = domain->domain_name.string;
123
struct ldb_message *msg;
126
msg = ldb_msg_new(mem_ctx);
128
return NT_STATUS_NO_MEMORY;
131
if (database == SAM_DATABASE_DOMAIN) {
132
struct ldb_dn *partitions_basedn;
133
const char *domain_attrs[] = {"nETBIOSName", "nCName", NULL};
134
struct ldb_message **msgs_domain;
137
partitions_basedn = samdb_partitions_dn(state->sam_ldb, mem_ctx);
139
ret_domain = gendb_search(state->sam_ldb, mem_ctx, partitions_basedn, &msgs_domain, domain_attrs,
140
"(&(&(nETBIOSName=%s)(objectclass=crossRef))(ncName=*))",
142
if (ret_domain == -1) {
143
*error_string = talloc_asprintf(mem_ctx, "gendb_search for domain failed: %s", ldb_errstring(state->sam_ldb));
144
return NT_STATUS_INTERNAL_DB_CORRUPTION;
147
if (ret_domain != 1) {
148
*error_string = talloc_asprintf(mem_ctx, "Failed to find existing domain record for %s: %d results", domain_name,
150
return NT_STATUS_NO_SUCH_DOMAIN;
153
state->base_dn[database] = samdb_result_dn(state->sam_ldb, state, msgs_domain[0], "nCName", NULL);
155
if (state->dom_sid[database]) {
156
/* Update the domain sid with the incoming
157
* domain (found on LSA pipe, database sid may
159
samdb_msg_add_dom_sid(state->sam_ldb, mem_ctx,
160
msg, "objectSid", state->dom_sid[database]);
162
/* Well, we will have to use the one from the database */
163
state->dom_sid[database] = samdb_search_dom_sid(state->sam_ldb, state,
164
state->base_dn[database],
168
if (state->samsync_state->domain_guid) {
169
enum ndr_err_code ndr_err;
171
ndr_err = ndr_push_struct_blob(&v, msg, NULL,
172
state->samsync_state->domain_guid,
173
(ndr_push_flags_fn_t)ndr_push_GUID);
174
if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
175
*error_string = talloc_asprintf(mem_ctx, "ndr_push of domain GUID failed!");
176
return ndr_map_error2ntstatus(ndr_err);
179
ldb_msg_add_value(msg, "objectGUID", &v, NULL);
181
} else if (database == SAM_DATABASE_BUILTIN) {
182
/* work out the builtin_dn - useful for so many calls its worth
184
const char *dnstring = samdb_search_string(state->sam_ldb, mem_ctx, NULL,
185
"distinguishedName", "objectClass=builtinDomain");
186
state->base_dn[database] = ldb_dn_new(state, state->sam_ldb, dnstring);
187
if ( ! ldb_dn_validate(state->base_dn[database])) {
188
return NT_STATUS_INTERNAL_ERROR;
192
return NT_STATUS_INVALID_PARAMETER;
195
msg->dn = talloc_reference(mem_ctx, state->base_dn[database]);
197
return NT_STATUS_NO_MEMORY;
200
samdb_msg_add_string(state->sam_ldb, mem_ctx,
201
msg, "oEMInformation", domain->oem_information.string);
203
samdb_msg_add_int64(state->sam_ldb, mem_ctx,
204
msg, "forceLogoff", domain->force_logoff_time);
206
samdb_msg_add_uint(state->sam_ldb, mem_ctx,
207
msg, "minPwdLen", domain->min_password_length);
209
samdb_msg_add_int64(state->sam_ldb, mem_ctx,
210
msg, "maxPwdAge", domain->max_password_age);
212
samdb_msg_add_int64(state->sam_ldb, mem_ctx,
213
msg, "minPwdAge", domain->min_password_age);
215
samdb_msg_add_uint(state->sam_ldb, mem_ctx,
216
msg, "pwdHistoryLength", domain->password_history_length);
218
samdb_msg_add_uint64(state->sam_ldb, mem_ctx,
219
msg, "modifiedCount",
220
domain->sequence_num);
222
samdb_msg_add_uint64(state->sam_ldb, mem_ctx,
223
msg, "creationTime", domain->domain_create_time);
225
/* TODO: Account lockout, password properties */
227
ret = samdb_replace(state->sam_ldb, mem_ctx, msg);
230
return NT_STATUS_INTERNAL_ERROR;
235
static NTSTATUS samsync_ldb_handle_user(TALLOC_CTX *mem_ctx,
236
struct samsync_ldb_state *state,
237
enum netr_SamDatabaseID database,
238
struct netr_DELTA_ENUM *delta,
241
uint32_t rid = delta->delta_id_union.rid;
242
struct netr_DELTA_USER *user = delta->delta_union.user;
243
const char *container, *obj_class;
246
const struct dom_sid *user_sid;
247
struct ldb_message *msg;
248
struct ldb_message **msgs;
249
struct ldb_message **remote_msgs = NULL;
253
const char *attrs[] = { NULL };
254
/* we may change this to a global search, then fill in only the things not in ldap later */
255
const char *remote_attrs[] = { "userPrincipalName", "servicePrincipalName",
256
"msDS-KeyVersionNumber", "objectGUID", NULL};
258
user_sid = dom_sid_add_rid(mem_ctx, state->dom_sid[database], rid);
260
return NT_STATUS_NO_MEMORY;
263
msg = ldb_msg_new(mem_ctx);
265
return NT_STATUS_NO_MEMORY;
269
/* search for the user, by rid */
270
ret = gendb_search(state->sam_ldb, mem_ctx, state->base_dn[database],
271
&msgs, attrs, "(&(objectClass=user)(objectSid=%s))",
272
ldap_encode_ndr_dom_sid(mem_ctx, user_sid));
275
*error_string = talloc_asprintf(mem_ctx, "LDB for user %s failed: %s",
276
dom_sid_string(mem_ctx, user_sid),
277
ldb_errstring(state->sam_ldb));
278
return NT_STATUS_INTERNAL_DB_CORRUPTION;
279
} else if (ret == 0) {
281
} else if (ret > 1) {
282
*error_string = talloc_asprintf(mem_ctx, "More than one user with SID: %s in local LDB",
283
dom_sid_string(mem_ctx, user_sid));
284
return NT_STATUS_INTERNAL_DB_CORRUPTION;
286
msg->dn = msgs[0]->dn;
287
talloc_steal(msg, msgs[0]->dn);
290
/* and do the same on the remote database */
291
if (state->remote_ldb) {
292
ret = gendb_search(state->remote_ldb, mem_ctx, state->base_dn[database],
293
&remote_msgs, remote_attrs, "(&(objectClass=user)(objectSid=%s))",
294
ldap_encode_ndr_dom_sid(mem_ctx, user_sid));
297
*error_string = talloc_asprintf(mem_ctx, "remote LDAP for user %s failed: %s",
298
dom_sid_string(mem_ctx, user_sid),
299
ldb_errstring(state->remote_ldb));
300
return NT_STATUS_INTERNAL_DB_CORRUPTION;
301
} else if (ret == 0) {
302
*error_string = talloc_asprintf(mem_ctx, "User exists in samsync but not in remote LDAP domain! (base: %s, SID: %s)",
303
ldb_dn_get_linearized(state->base_dn[database]),
304
dom_sid_string(mem_ctx, user_sid));
305
return NT_STATUS_NO_SUCH_USER;
306
} else if (ret > 1) {
307
*error_string = talloc_asprintf(mem_ctx, "More than one user in remote LDAP domain with SID: %s",
308
dom_sid_string(mem_ctx, user_sid));
309
return NT_STATUS_INTERNAL_DB_CORRUPTION;
311
/* Try to put things in the same location as the remote server */
313
msg->dn = remote_msgs[0]->dn;
314
talloc_steal(msg, remote_msgs[0]->dn);
318
cn_name = talloc_strdup(mem_ctx, user->account_name.string);
319
NT_STATUS_HAVE_NO_MEMORY(cn_name);
320
cn_name_len = strlen(cn_name);
322
#define ADD_OR_DEL(type, attrib, field) do { \
324
samdb_msg_add_ ## type(state->sam_ldb, mem_ctx, msg, \
325
attrib, user->field); \
327
samdb_msg_add_delete(state->sam_ldb, mem_ctx, msg, \
332
ADD_OR_DEL(string, "samAccountName", account_name.string);
333
ADD_OR_DEL(string, "displayName", full_name.string);
335
if (samdb_msg_add_dom_sid(state->sam_ldb, mem_ctx, msg,
336
"objectSid", dom_sid_add_rid(mem_ctx, state->dom_sid[database], rid))) {
337
return NT_STATUS_NO_MEMORY;
340
ADD_OR_DEL(uint, "primaryGroupID", primary_gid);
341
ADD_OR_DEL(string, "homeDirectory", home_directory.string);
342
ADD_OR_DEL(string, "homeDrive", home_drive.string);
343
ADD_OR_DEL(string, "scriptPath", logon_script.string);
344
ADD_OR_DEL(string, "description", description.string);
345
ADD_OR_DEL(string, "userWorkstations", workstations.string);
347
ADD_OR_DEL(uint64, "lastLogon", last_logon);
348
ADD_OR_DEL(uint64, "lastLogoff", last_logoff);
350
if (samdb_msg_add_logon_hours(state->sam_ldb, mem_ctx, msg, "logonHours", &user->logon_hours) != 0) {
351
return NT_STATUS_NO_MEMORY;
354
ADD_OR_DEL(uint, "badPwdCount", bad_password_count);
355
ADD_OR_DEL(uint, "logonCount", logon_count);
357
ADD_OR_DEL(uint64, "pwdLastSet", last_password_change);
358
ADD_OR_DEL(uint64, "accountExpires", acct_expiry);
360
if (samdb_msg_add_acct_flags(state->sam_ldb, mem_ctx, msg,
361
"userAccountControl", user->acct_flags) != 0) {
362
return NT_STATUS_NO_MEMORY;
366
/* Passwords. Ensure there is no plaintext stored against
367
* this entry, as we only have hashes */
368
samdb_msg_add_delete(state->sam_ldb, mem_ctx, msg,
371
if (user->lm_password_present) {
372
samdb_msg_add_hash(state->sam_ldb, mem_ctx, msg,
373
"dBCSPwd", &user->lmpassword);
375
samdb_msg_add_delete(state->sam_ldb, mem_ctx, msg,
378
if (user->nt_password_present) {
379
samdb_msg_add_hash(state->sam_ldb, mem_ctx, msg,
380
"unicodePwd", &user->ntpassword);
382
samdb_msg_add_delete(state->sam_ldb, mem_ctx, msg,
386
ADD_OR_DEL(string, "comment", comment.string);
388
if (samdb_msg_add_parameters(state->sam_ldb, mem_ctx, msg, "userParameters", &user->parameters) != 0) {
389
return NT_STATUS_NO_MEMORY;
392
ADD_OR_DEL(uint, "countryCode", country_code);
393
ADD_OR_DEL(uint, "codePage", code_page);
395
ADD_OR_DEL(string, "profilePath", profile_path.string);
399
for (i=0; remote_attrs[i]; i++) {
400
struct ldb_message_element *el = ldb_msg_find_element(remote_msgs[0], remote_attrs[i]);
402
samdb_msg_add_delete(state->sam_ldb, mem_ctx, msg,
405
ldb_msg_add(msg, el, LDB_FLAG_MOD_REPLACE);
409
acb = user->acct_flags;
410
if (acb & (ACB_WSTRUST)) {
411
cn_name[cn_name_len - 1] = '\0';
412
container = "Computers";
413
obj_class = "computer";
415
} else if (acb & ACB_SVRTRUST) {
416
if (cn_name[cn_name_len - 1] != '$') {
417
return NT_STATUS_FOOBAR;
419
cn_name[cn_name_len - 1] = '\0';
420
container = "Domain Controllers";
421
obj_class = "computer";
427
samdb_msg_add_string(state->sam_ldb, mem_ctx, msg,
428
"objectClass", obj_class);
430
msg->dn = ldb_dn_copy(mem_ctx, state->base_dn[database]);
431
ldb_dn_add_child_fmt(msg->dn, "CN=%s,CN=%s", cn_name, container);
433
return NT_STATUS_NO_MEMORY;
437
ret = ldb_add(state->sam_ldb, msg);
439
struct ldb_dn *first_try_dn = msg->dn;
440
/* Try again with the default DN */
442
*error_string = talloc_asprintf(mem_ctx, "Failed to create user record. Tried %s: %s",
443
ldb_dn_get_linearized(first_try_dn),
444
ldb_errstring(state->sam_ldb));
445
return NT_STATUS_INTERNAL_DB_CORRUPTION;
447
msg->dn = talloc_steal(msg, remote_msgs[0]->dn);
448
ret = ldb_add(state->sam_ldb, msg);
450
*error_string = talloc_asprintf(mem_ctx, "Failed to create user record. Tried both %s and %s: %s",
451
ldb_dn_get_linearized(first_try_dn),
452
ldb_dn_get_linearized(msg->dn),
453
ldb_errstring(state->sam_ldb));
454
return NT_STATUS_INTERNAL_DB_CORRUPTION;
459
ret = samdb_replace(state->sam_ldb, mem_ctx, msg);
461
*error_string = talloc_asprintf(mem_ctx, "Failed to modify user record %s: %s",
462
ldb_dn_get_linearized(msg->dn),
463
ldb_errstring(state->sam_ldb));
464
return NT_STATUS_INTERNAL_DB_CORRUPTION;
471
static NTSTATUS samsync_ldb_delete_user(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 ldb_message **msgs;
480
const char *attrs[] = { NULL };
482
/* search for the user, by rid */
483
ret = gendb_search(state->sam_ldb, mem_ctx, state->base_dn[database],
484
&msgs, attrs, "(&(objectClass=user)(objectSid=%s))",
485
ldap_encode_ndr_dom_sid(mem_ctx, dom_sid_add_rid(mem_ctx, state->dom_sid[database], rid)));
488
*error_string = talloc_asprintf(mem_ctx, "gendb_search failed: %s", ldb_errstring(state->sam_ldb));
489
return NT_STATUS_INTERNAL_DB_CORRUPTION;
490
} else if (ret == 0) {
491
return NT_STATUS_NO_SUCH_USER;
492
} else if (ret > 1) {
493
*error_string = talloc_asprintf(mem_ctx, "More than one user with SID: %s",
494
dom_sid_string(mem_ctx,
495
dom_sid_add_rid(mem_ctx,
496
state->dom_sid[database],
498
return NT_STATUS_INTERNAL_DB_CORRUPTION;
501
ret = ldb_delete(state->sam_ldb, msgs[0]->dn);
503
*error_string = talloc_asprintf(mem_ctx, "Failed to delete user record %s: %s",
504
ldb_dn_get_linearized(msgs[0]->dn),
505
ldb_errstring(state->sam_ldb));
506
return NT_STATUS_INTERNAL_DB_CORRUPTION;
512
static NTSTATUS samsync_ldb_handle_group(TALLOC_CTX *mem_ctx,
513
struct samsync_ldb_state *state,
514
enum netr_SamDatabaseID database,
515
struct netr_DELTA_ENUM *delta,
518
uint32_t rid = delta->delta_id_union.rid;
519
struct netr_DELTA_GROUP *group = delta->delta_union.group;
520
const char *container, *obj_class;
523
struct ldb_message *msg;
524
struct ldb_message **msgs;
527
const char *attrs[] = { NULL };
529
msg = ldb_msg_new(mem_ctx);
531
return NT_STATUS_NO_MEMORY;
534
/* search for the group, by rid */
535
ret = gendb_search(state->sam_ldb, mem_ctx, state->base_dn[database], &msgs, attrs,
536
"(&(objectClass=group)(objectSid=%s))",
537
ldap_encode_ndr_dom_sid(mem_ctx, dom_sid_add_rid(mem_ctx, state->dom_sid[database], rid)));
540
*error_string = talloc_asprintf(mem_ctx, "gendb_search failed: %s", ldb_errstring(state->sam_ldb));
541
return NT_STATUS_INTERNAL_DB_CORRUPTION;
542
} else if (ret == 0) {
544
} else if (ret > 1) {
545
*error_string = talloc_asprintf(mem_ctx, "More than one group/alias with SID: %s",
546
dom_sid_string(mem_ctx,
547
dom_sid_add_rid(mem_ctx,
548
state->dom_sid[database],
550
return NT_STATUS_INTERNAL_DB_CORRUPTION;
552
msg->dn = talloc_steal(msg, msgs[0]->dn);
555
cn_name = group->group_name.string;
557
#define ADD_OR_DEL(type, attrib, field) do { \
558
if (group->field) { \
559
samdb_msg_add_ ## type(state->sam_ldb, mem_ctx, msg, \
560
attrib, group->field); \
562
samdb_msg_add_delete(state->sam_ldb, mem_ctx, msg, \
567
ADD_OR_DEL(string, "samAccountName", group_name.string);
569
if (samdb_msg_add_dom_sid(state->sam_ldb, mem_ctx, msg,
570
"objectSid", dom_sid_add_rid(mem_ctx, state->dom_sid[database], rid))) {
571
return NT_STATUS_NO_MEMORY;
574
ADD_OR_DEL(string, "description", description.string);
582
samdb_msg_add_string(state->sam_ldb, mem_ctx, msg,
583
"objectClass", obj_class);
584
msg->dn = ldb_dn_copy(mem_ctx, state->base_dn[database]);
585
ldb_dn_add_child_fmt(msg->dn, "CN=%s,CN=%s", cn_name, container);
587
return NT_STATUS_NO_MEMORY;
590
ret = ldb_add(state->sam_ldb, msg);
592
*error_string = talloc_asprintf(mem_ctx, "Failed to create group record %s: %s",
593
ldb_dn_get_linearized(msg->dn),
594
ldb_errstring(state->sam_ldb));
595
return NT_STATUS_INTERNAL_DB_CORRUPTION;
598
ret = samdb_replace(state->sam_ldb, mem_ctx, msg);
600
*error_string = talloc_asprintf(mem_ctx, "Failed to modify group record %s: %s",
601
ldb_dn_get_linearized(msg->dn),
602
ldb_errstring(state->sam_ldb));
603
return NT_STATUS_INTERNAL_DB_CORRUPTION;
610
static NTSTATUS samsync_ldb_delete_group(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 ldb_message **msgs;
619
const char *attrs[] = { NULL };
621
/* search for the group, by rid */
622
ret = gendb_search(state->sam_ldb, mem_ctx, state->base_dn[database], &msgs, attrs,
623
"(&(objectClass=group)(objectSid=%s))",
624
ldap_encode_ndr_dom_sid(mem_ctx, dom_sid_add_rid(mem_ctx, state->dom_sid[database], rid)));
627
*error_string = talloc_asprintf(mem_ctx, "gendb_search failed: %s", ldb_errstring(state->sam_ldb));
628
return NT_STATUS_INTERNAL_DB_CORRUPTION;
629
} else if (ret == 0) {
630
return NT_STATUS_NO_SUCH_GROUP;
631
} else if (ret > 1) {
632
*error_string = talloc_asprintf(mem_ctx, "More than one group/alias with SID: %s",
633
dom_sid_string(mem_ctx,
634
dom_sid_add_rid(mem_ctx,
635
state->dom_sid[database],
637
return NT_STATUS_INTERNAL_DB_CORRUPTION;
640
ret = ldb_delete(state->sam_ldb, msgs[0]->dn);
642
*error_string = talloc_asprintf(mem_ctx, "Failed to delete group record %s: %s",
643
ldb_dn_get_linearized(msgs[0]->dn),
644
ldb_errstring(state->sam_ldb));
645
return NT_STATUS_INTERNAL_DB_CORRUPTION;
651
static NTSTATUS samsync_ldb_handle_group_member(TALLOC_CTX *mem_ctx,
652
struct samsync_ldb_state *state,
653
enum netr_SamDatabaseID database,
654
struct netr_DELTA_ENUM *delta,
657
uint32_t rid = delta->delta_id_union.rid;
658
struct netr_DELTA_GROUP_MEMBER *group_member = delta->delta_union.group_member;
659
struct ldb_message *msg;
660
struct ldb_message **msgs;
662
const char *attrs[] = { NULL };
665
msg = ldb_msg_new(mem_ctx);
667
return NT_STATUS_NO_MEMORY;
670
/* search for the group, by rid */
671
ret = gendb_search(state->sam_ldb, mem_ctx, state->base_dn[database], &msgs, attrs,
672
"(&(objectClass=group)(objectSid=%s))",
673
ldap_encode_ndr_dom_sid(mem_ctx, dom_sid_add_rid(mem_ctx, state->dom_sid[database], rid)));
676
*error_string = talloc_asprintf(mem_ctx, "gendb_search failed: %s", ldb_errstring(state->sam_ldb));
677
return NT_STATUS_INTERNAL_DB_CORRUPTION;
678
} else if (ret == 0) {
679
return NT_STATUS_NO_SUCH_GROUP;
680
} else if (ret > 1) {
681
*error_string = talloc_asprintf(mem_ctx, "More than one group/alias with SID: %s",
682
dom_sid_string(mem_ctx,
683
dom_sid_add_rid(mem_ctx,
684
state->dom_sid[database],
686
return NT_STATUS_INTERNAL_DB_CORRUPTION;
688
msg->dn = talloc_steal(msg, msgs[0]->dn);
693
for (i=0; i<group_member->num_rids; i++) {
694
/* search for the group, by rid */
695
ret = gendb_search(state->sam_ldb, mem_ctx, state->base_dn[database], &msgs, attrs,
696
"(&(objectClass=user)(objectSid=%s))",
697
ldap_encode_ndr_dom_sid(mem_ctx, dom_sid_add_rid(mem_ctx, state->dom_sid[database], group_member->rids[i])));
700
*error_string = talloc_asprintf(mem_ctx, "gendb_search failed: %s", ldb_errstring(state->sam_ldb));
701
return NT_STATUS_INTERNAL_DB_CORRUPTION;
702
} else if (ret == 0) {
703
return NT_STATUS_NO_SUCH_USER;
704
} else if (ret > 1) {
705
return NT_STATUS_INTERNAL_DB_CORRUPTION;
707
samdb_msg_add_string(state->sam_ldb, mem_ctx, msg, "member", ldb_dn_alloc_linearized(mem_ctx, msgs[0]->dn));
713
ret = samdb_replace(state->sam_ldb, mem_ctx, msg);
715
*error_string = talloc_asprintf(mem_ctx, "Failed to modify group record %s: %s",
716
ldb_dn_get_linearized(msg->dn),
717
ldb_errstring(state->sam_ldb));
718
return NT_STATUS_INTERNAL_DB_CORRUPTION;
724
static NTSTATUS samsync_ldb_handle_alias(TALLOC_CTX *mem_ctx,
725
struct samsync_ldb_state *state,
726
enum netr_SamDatabaseID database,
727
struct netr_DELTA_ENUM *delta,
730
uint32_t rid = delta->delta_id_union.rid;
731
struct netr_DELTA_ALIAS *alias = delta->delta_union.alias;
732
const char *container, *obj_class;
735
struct ldb_message *msg;
736
struct ldb_message **msgs;
739
const char *attrs[] = { NULL };
741
msg = ldb_msg_new(mem_ctx);
743
return NT_STATUS_NO_MEMORY;
746
/* search for the alias, by rid */
747
ret = gendb_search(state->sam_ldb, mem_ctx, state->base_dn[database], &msgs, attrs,
748
"(&(objectClass=group)(objectSid=%s))",
749
ldap_encode_ndr_dom_sid(mem_ctx, dom_sid_add_rid(mem_ctx, state->dom_sid[database], rid)));
752
*error_string = talloc_asprintf(mem_ctx, "gendb_search failed: %s", ldb_errstring(state->sam_ldb));
753
return NT_STATUS_INTERNAL_DB_CORRUPTION;
754
} else if (ret == 0) {
756
} else if (ret > 1) {
757
*error_string = talloc_asprintf(mem_ctx, "More than one group/alias with SID: %s",
758
dom_sid_string(mem_ctx,
759
dom_sid_add_rid(mem_ctx,
760
state->dom_sid[database],
762
return NT_STATUS_INTERNAL_DB_CORRUPTION;
764
msg->dn = talloc_steal(mem_ctx, msgs[0]->dn);
767
cn_name = alias->alias_name.string;
769
#define ADD_OR_DEL(type, attrib, field) do { \
770
if (alias->field) { \
771
samdb_msg_add_ ## type(state->sam_ldb, mem_ctx, msg, \
772
attrib, alias->field); \
774
samdb_msg_add_delete(state->sam_ldb, mem_ctx, msg, \
779
ADD_OR_DEL(string, "samAccountName", alias_name.string);
781
if (samdb_msg_add_dom_sid(state->sam_ldb, mem_ctx, msg,
782
"objectSid", dom_sid_add_rid(mem_ctx, state->dom_sid[database], rid))) {
783
return NT_STATUS_NO_MEMORY;
786
ADD_OR_DEL(string, "description", description.string);
790
samdb_msg_add_uint(state->sam_ldb, mem_ctx, msg, "groupType", 0x80000004);
796
samdb_msg_add_string(state->sam_ldb, mem_ctx, msg,
797
"objectClass", obj_class);
798
msg->dn = ldb_dn_copy(mem_ctx, state->base_dn[database]);
799
ldb_dn_add_child_fmt(msg->dn, "CN=%s,CN=%s", cn_name, container);
801
return NT_STATUS_NO_MEMORY;
804
ret = ldb_add(state->sam_ldb, msg);
806
*error_string = talloc_asprintf(mem_ctx, "Failed to create alias record %s: %s",
807
ldb_dn_get_linearized(msg->dn),
808
ldb_errstring(state->sam_ldb));
809
return NT_STATUS_INTERNAL_DB_CORRUPTION;
812
ret = samdb_replace(state->sam_ldb, mem_ctx, msg);
814
*error_string = talloc_asprintf(mem_ctx, "Failed to modify alias record %s: %s",
815
ldb_dn_get_linearized(msg->dn),
816
ldb_errstring(state->sam_ldb));
817
return NT_STATUS_INTERNAL_DB_CORRUPTION;
824
static NTSTATUS samsync_ldb_delete_alias(TALLOC_CTX *mem_ctx,
825
struct samsync_ldb_state *state,
826
enum netr_SamDatabaseID database,
827
struct netr_DELTA_ENUM *delta,
830
uint32_t rid = delta->delta_id_union.rid;
831
struct ldb_message **msgs;
833
const char *attrs[] = { NULL };
835
/* search for the alias, by rid */
836
ret = gendb_search(state->sam_ldb, mem_ctx, state->base_dn[database], &msgs, attrs,
837
"(&(objectClass=group)(objectSid=%s))",
838
ldap_encode_ndr_dom_sid(mem_ctx, dom_sid_add_rid(mem_ctx, state->dom_sid[database], rid)));
841
*error_string = talloc_asprintf(mem_ctx, "gendb_search failed: %s", ldb_errstring(state->sam_ldb));
842
return NT_STATUS_INTERNAL_DB_CORRUPTION;
843
} else if (ret == 0) {
844
return NT_STATUS_NO_SUCH_ALIAS;
845
} else if (ret > 1) {
846
return NT_STATUS_INTERNAL_DB_CORRUPTION;
849
ret = ldb_delete(state->sam_ldb, msgs[0]->dn);
851
*error_string = talloc_asprintf(mem_ctx, "Failed to delete alias record %s: %s",
852
ldb_dn_get_linearized(msgs[0]->dn),
853
ldb_errstring(state->sam_ldb));
854
return NT_STATUS_INTERNAL_DB_CORRUPTION;
860
static NTSTATUS samsync_ldb_handle_alias_member(TALLOC_CTX *mem_ctx,
861
struct samsync_ldb_state *state,
862
enum netr_SamDatabaseID database,
863
struct netr_DELTA_ENUM *delta,
866
uint32_t rid = delta->delta_id_union.rid;
867
struct netr_DELTA_ALIAS_MEMBER *alias_member = delta->delta_union.alias_member;
868
struct ldb_message *msg;
869
struct ldb_message **msgs;
871
const char *attrs[] = { NULL };
874
msg = ldb_msg_new(mem_ctx);
876
return NT_STATUS_NO_MEMORY;
879
/* search for the alias, by rid */
880
ret = gendb_search(state->sam_ldb, mem_ctx, state->base_dn[database], &msgs, attrs,
881
"(&(objectClass=group)(objectSid=%s))",
882
ldap_encode_ndr_dom_sid(mem_ctx, dom_sid_add_rid(mem_ctx, state->dom_sid[database], rid)));
885
*error_string = talloc_asprintf(mem_ctx, "gendb_search failed: %s", ldb_errstring(state->sam_ldb));
886
return NT_STATUS_INTERNAL_DB_CORRUPTION;
887
} else if (ret == 0) {
888
return NT_STATUS_NO_SUCH_GROUP;
889
} else if (ret > 1) {
890
*error_string = talloc_asprintf(mem_ctx, "More than one group/alias with SID: %s",
891
dom_sid_string(mem_ctx,
892
dom_sid_add_rid(mem_ctx,
893
state->dom_sid[database],
895
return NT_STATUS_INTERNAL_DB_CORRUPTION;
897
msg->dn = talloc_steal(msg, msgs[0]->dn);
902
for (i=0; i<alias_member->sids.num_sids; i++) {
903
struct ldb_dn *alias_member_dn;
904
/* search for members, in the top basedn (normal users are builtin aliases) */
905
ret = gendb_search(state->sam_ldb, mem_ctx, state->base_dn[SAM_DATABASE_DOMAIN], &msgs, attrs,
907
ldap_encode_ndr_dom_sid(mem_ctx, alias_member->sids.sids[i].sid));
910
*error_string = talloc_asprintf(mem_ctx, "gendb_search failed: %s", ldb_errstring(state->sam_ldb));
911
return NT_STATUS_INTERNAL_DB_CORRUPTION;
912
} else if (ret == 0) {
914
nt_status = samsync_ldb_add_foreignSecurityPrincipal(mem_ctx, state,
915
alias_member->sids.sids[i].sid,
918
if (!NT_STATUS_IS_OK(nt_status)) {
921
} else if (ret > 1) {
922
return NT_STATUS_INTERNAL_DB_CORRUPTION;
924
alias_member_dn = msgs[0]->dn;
926
samdb_msg_add_string(state->sam_ldb, mem_ctx, msg, "member", ldb_dn_alloc_linearized(mem_ctx, alias_member_dn));
931
ret = samdb_replace(state->sam_ldb, mem_ctx, msg);
933
*error_string = talloc_asprintf(mem_ctx, "Failed to modify group record %s: %s",
934
ldb_dn_get_linearized(msg->dn),
935
ldb_errstring(state->sam_ldb));
936
return NT_STATUS_INTERNAL_DB_CORRUPTION;
942
static NTSTATUS samsync_ldb_handle_account(TALLOC_CTX *mem_ctx,
943
struct samsync_ldb_state *state,
944
enum netr_SamDatabaseID database,
945
struct netr_DELTA_ENUM *delta,
948
struct dom_sid *sid = delta->delta_id_union.sid;
949
struct netr_DELTA_ACCOUNT *account = delta->delta_union.account;
951
struct ldb_message *msg;
952
struct ldb_message **msgs;
953
struct ldb_dn *privilege_dn;
955
const char *attrs[] = { NULL };
958
msg = ldb_msg_new(mem_ctx);
960
return NT_STATUS_NO_MEMORY;
963
/* search for the account, by sid, in the top basedn */
964
ret = gendb_search(state->sam_ldb, mem_ctx, state->base_dn[SAM_DATABASE_DOMAIN], &msgs, attrs,
965
"(objectSid=%s)", ldap_encode_ndr_dom_sid(mem_ctx, sid));
968
*error_string = talloc_asprintf(mem_ctx, "gendb_search failed: %s", ldb_errstring(state->sam_ldb));
969
return NT_STATUS_INTERNAL_DB_CORRUPTION;
970
} else if (ret == 0) {
972
nt_status = samsync_ldb_add_foreignSecurityPrincipal(mem_ctx, state,
976
privilege_dn = talloc_steal(msg, privilege_dn);
977
if (!NT_STATUS_IS_OK(nt_status)) {
980
} else if (ret > 1) {
981
*error_string = talloc_asprintf(mem_ctx, "More than one account with SID: %s",
982
dom_sid_string(mem_ctx, sid));
983
return NT_STATUS_INTERNAL_DB_CORRUPTION;
985
privilege_dn = talloc_steal(msg, msgs[0]->dn);
988
msg->dn = privilege_dn;
990
for (i=0; i< account->privilege_entries; i++) {
991
samdb_msg_add_string(state->sam_ldb, mem_ctx, msg, "privilege",
992
account->privilege_name[i].string);
995
ret = samdb_replace(state->sam_ldb, mem_ctx, msg);
997
*error_string = talloc_asprintf(mem_ctx, "Failed to modify privilege record %s",
998
ldb_dn_get_linearized(msg->dn));
999
return NT_STATUS_INTERNAL_DB_CORRUPTION;
1002
return NT_STATUS_OK;
1005
static NTSTATUS samsync_ldb_delete_account(TALLOC_CTX *mem_ctx,
1006
struct samsync_ldb_state *state,
1007
enum netr_SamDatabaseID database,
1008
struct netr_DELTA_ENUM *delta,
1009
char **error_string)
1011
struct dom_sid *sid = delta->delta_id_union.sid;
1013
struct ldb_message *msg;
1014
struct ldb_message **msgs;
1016
const char *attrs[] = { NULL };
1018
msg = ldb_msg_new(mem_ctx);
1020
return NT_STATUS_NO_MEMORY;
1023
/* search for the account, by sid, in the top basedn */
1024
ret = gendb_search(state->sam_ldb, mem_ctx, state->base_dn[SAM_DATABASE_DOMAIN], &msgs, attrs,
1026
ldap_encode_ndr_dom_sid(mem_ctx, sid));
1029
*error_string = talloc_asprintf(mem_ctx, "gendb_search failed: %s", ldb_errstring(state->sam_ldb));
1030
return NT_STATUS_INTERNAL_DB_CORRUPTION;
1031
} else if (ret == 0) {
1032
return NT_STATUS_NO_SUCH_USER;
1033
} else if (ret > 1) {
1034
*error_string = talloc_asprintf(mem_ctx, "More than one account with SID: %s",
1035
dom_sid_string(mem_ctx, sid));
1036
return NT_STATUS_INTERNAL_DB_CORRUPTION;
1038
msg->dn = talloc_steal(msg, msgs[0]->dn);
1041
samdb_msg_add_delete(state->sam_ldb, mem_ctx, msg,
1044
ret = samdb_replace(state->sam_ldb, mem_ctx, msg);
1046
*error_string = talloc_asprintf(mem_ctx, "Failed to modify privilege record %s",
1047
ldb_dn_get_linearized(msg->dn));
1048
return NT_STATUS_INTERNAL_DB_CORRUPTION;
1051
return NT_STATUS_OK;
1054
static NTSTATUS libnet_samsync_ldb_fn(TALLOC_CTX *mem_ctx,
1056
enum netr_SamDatabaseID database,
1057
struct netr_DELTA_ENUM *delta,
1058
char **error_string)
1060
NTSTATUS nt_status = NT_STATUS_OK;
1061
struct samsync_ldb_state *state = talloc_get_type(private_data, struct samsync_ldb_state);
1063
*error_string = NULL;
1064
switch (delta->delta_type) {
1065
case NETR_DELTA_DOMAIN:
1067
nt_status = samsync_ldb_handle_domain(mem_ctx,
1074
case NETR_DELTA_USER:
1076
nt_status = samsync_ldb_handle_user(mem_ctx,
1083
case NETR_DELTA_DELETE_USER:
1085
nt_status = samsync_ldb_delete_user(mem_ctx,
1092
case NETR_DELTA_GROUP:
1094
nt_status = samsync_ldb_handle_group(mem_ctx,
1101
case NETR_DELTA_DELETE_GROUP:
1103
nt_status = samsync_ldb_delete_group(mem_ctx,
1110
case NETR_DELTA_GROUP_MEMBER:
1112
nt_status = samsync_ldb_handle_group_member(mem_ctx,
1119
case NETR_DELTA_ALIAS:
1121
nt_status = samsync_ldb_handle_alias(mem_ctx,
1128
case NETR_DELTA_DELETE_ALIAS:
1130
nt_status = samsync_ldb_delete_alias(mem_ctx,
1137
case NETR_DELTA_ALIAS_MEMBER:
1139
nt_status = samsync_ldb_handle_alias_member(mem_ctx,
1146
case NETR_DELTA_ACCOUNT:
1148
nt_status = samsync_ldb_handle_account(mem_ctx,
1155
case NETR_DELTA_DELETE_ACCOUNT:
1157
nt_status = samsync_ldb_delete_account(mem_ctx,
1165
/* Can't dump them all right now */
1168
if (!NT_STATUS_IS_OK(nt_status) && !*error_string) {
1169
*error_string = talloc_asprintf(mem_ctx, "Failed to handle samsync delta: %s", nt_errstr(nt_status));
1174
static NTSTATUS libnet_samsync_ldb_init(TALLOC_CTX *mem_ctx,
1176
struct libnet_SamSync_state *samsync_state,
1177
char **error_string)
1179
struct samsync_ldb_state *state = talloc_get_type(private_data, struct samsync_ldb_state);
1180
const char *server = dcerpc_server_name(samsync_state->netlogon_pipe);
1183
state->samsync_state = samsync_state;
1185
ZERO_STRUCT(state->dom_sid);
1186
if (state->samsync_state->domain_sid) {
1187
state->dom_sid[SAM_DATABASE_DOMAIN] = dom_sid_dup(state, state->samsync_state->domain_sid);
1190
state->dom_sid[SAM_DATABASE_BUILTIN] = dom_sid_parse_talloc(state, SID_BUILTIN);
1192
if (state->samsync_state->realm) {
1193
if (!server || !*server) {
1194
/* huh? how do we not have a server name? */
1195
*error_string = talloc_strdup(mem_ctx, "No DCE/RPC server name available. How did we connect?");
1196
return NT_STATUS_INVALID_PARAMETER;
1198
ldap_url = talloc_asprintf(state, "ldap://%s", server);
1200
state->remote_ldb = ldb_wrap_connect(mem_ctx,
1201
state->samsync_state->machine_net_ctx->event_ctx,
1202
state->samsync_state->machine_net_ctx->lp_ctx,
1204
NULL, state->samsync_state->machine_net_ctx->cred,
1206
if (!state->remote_ldb) {
1207
*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);
1208
return NT_STATUS_NO_LOGON_SERVERS;
1211
state->remote_ldb = NULL;
1213
return NT_STATUS_OK;
1216
NTSTATUS libnet_samsync_ldb(struct libnet_context *ctx, TALLOC_CTX *mem_ctx, struct libnet_samsync_ldb *r)
1219
struct libnet_SamSync r2;
1220
struct samsync_ldb_state *state = talloc(mem_ctx, struct samsync_ldb_state);
1223
return NT_STATUS_NO_MEMORY;
1226
state->secrets = NULL;
1227
state->trusted_domains = NULL;
1229
state->sam_ldb = samdb_connect(mem_ctx,
1232
r->in.session_info);
1234
r2.out.error_string = NULL;
1235
r2.in.binding_string = r->in.binding_string;
1236
r2.in.rid_crypt = true;
1237
r2.in.init_fn = libnet_samsync_ldb_init;
1238
r2.in.delta_fn = libnet_samsync_ldb_fn;
1239
r2.in.fn_ctx = state;
1240
r2.in.machine_account = NULL; /* TODO: Create a machine account, fill this in, and the delete it */
1241
nt_status = libnet_SamSync_netlogon(ctx, state, &r2);
1242
r->out.error_string = r2.out.error_string;
1243
talloc_steal(mem_ctx, r->out.error_string);
1245
if (!NT_STATUS_IS_OK(nt_status)) {