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"
31 #include "librpc/gen_ndr/ndr_misc.h"
33 #include "libcli/security/security.h"
34 #include "librpc/rpc/dcerpc.h"
36 struct samsync_ldb_secret
{
37 struct samsync_ldb_secret
*prev
, *next
;
43 struct samsync_ldb_trusted_domain
{
44 struct samsync_ldb_trusted_domain
*prev
, *next
;
49 struct samsync_ldb_state
{
50 /* Values from the LSA lookup */
51 const struct libnet_SamSync_state
*samsync_state
;
53 struct dom_sid
*dom_sid
[3];
54 struct ldb_context
*sam_ldb
, *remote_ldb
;
55 struct ldb_dn
*base_dn
[3];
56 struct samsync_ldb_secret
*secrets
;
57 struct samsync_ldb_trusted_domain
*trusted_domains
;
60 static NTSTATUS
samsync_ldb_add_foreignSecurityPrincipal(TALLOC_CTX
*mem_ctx
,
61 struct samsync_ldb_state
*state
,
63 struct ldb_dn
**fsp_dn
,
66 const char *sidstr
= dom_sid_string(mem_ctx
, sid
);
67 /* We assume that ForeignSecurityPrincipals are under the BASEDN of the main domain */
68 struct ldb_dn
*basedn
= samdb_search_dn(state
->sam_ldb
, mem_ctx
,
69 state
->base_dn
[SAM_DATABASE_DOMAIN
],
70 "(&(objectClass=container)(cn=ForeignSecurityPrincipals))");
71 struct ldb_message
*msg
;
75 return NT_STATUS_NO_MEMORY
;
79 *error_string
= talloc_asprintf(mem_ctx
,
80 "Failed to find DN for "
81 "ForeignSecurityPrincipal container under %s",
82 ldb_dn_linearize(mem_ctx
, state
->base_dn
[SAM_DATABASE_DOMAIN
]));
83 return NT_STATUS_INTERNAL_DB_CORRUPTION
;
86 msg
= ldb_msg_new(mem_ctx
);
88 return NT_STATUS_NO_MEMORY
;
91 /* add core elements to the ldb_message for the alias */
92 msg
->dn
= ldb_dn_build_child(mem_ctx
, "CN", sidstr
, basedn
);
94 return NT_STATUS_NO_MEMORY
;
96 samdb_msg_add_string(state
->sam_ldb
, mem_ctx
, msg
,
98 "foreignSecurityPrincipal");
102 /* create the alias */
103 ret
= samdb_add(state
->sam_ldb
, mem_ctx
, msg
);
105 *error_string
= talloc_asprintf(mem_ctx
, "Failed to create foreignSecurityPrincipal "
107 ldb_dn_linearize(mem_ctx
, msg
->dn
),
108 ldb_errstring(state
->sam_ldb
));
109 return NT_STATUS_INTERNAL_DB_CORRUPTION
;
114 static NTSTATUS
samsync_ldb_handle_domain(TALLOC_CTX
*mem_ctx
,
115 struct samsync_ldb_state
*state
,
116 enum netr_SamDatabaseID database
,
117 struct netr_DELTA_ENUM
*delta
,
120 struct netr_DELTA_DOMAIN
*domain
= delta
->delta_union
.domain
;
121 const char *domain_name
= domain
->domain_name
.string
;
122 struct ldb_message
*msg
;
125 msg
= ldb_msg_new(mem_ctx
);
127 return NT_STATUS_NO_MEMORY
;
130 if (database
== SAM_DATABASE_DOMAIN
) {
131 const char *domain_attrs
[] = {"nETBIOSName", "nCName", NULL
};
132 struct ldb_message
**msgs_domain
;
135 ret_domain
= gendb_search(state
->sam_ldb
, mem_ctx
, NULL
, &msgs_domain
, domain_attrs
,
136 "(&(&(nETBIOSName=%s)(objectclass=crossRef))(ncName=*))",
138 if (ret_domain
== -1) {
139 *error_string
= talloc_asprintf(mem_ctx
, "gendb_search for domain failed: %s", ldb_errstring(state
->sam_ldb
));
140 return NT_STATUS_INTERNAL_DB_CORRUPTION
;
143 if (ret_domain
!= 1) {
144 *error_string
= talloc_asprintf(mem_ctx
, "Failed to find existing domain record for %s: %d results", domain_name
,
146 return NT_STATUS_NO_SUCH_DOMAIN
;
149 state
->base_dn
[database
] = samdb_result_dn(state
, msgs_domain
[0], "nCName", NULL
);
151 if (state
->dom_sid
[database
]) {
152 /* Update the domain sid with the incoming
153 * domain (found on LSA pipe, database sid may
155 samdb_msg_add_dom_sid(state
->sam_ldb
, mem_ctx
,
156 msg
, "objectSid", state
->dom_sid
[database
]);
158 /* Well, we will have to use the one from the database */
159 state
->dom_sid
[database
] = samdb_search_dom_sid(state
->sam_ldb
, state
,
160 state
->base_dn
[database
],
164 if (state
->samsync_state
->domain_guid
) {
167 nt_status
= ndr_push_struct_blob(&v
, msg
, state
->samsync_state
->domain_guid
,
168 (ndr_push_flags_fn_t
)ndr_push_GUID
);
169 if (!NT_STATUS_IS_OK(nt_status
)) {
170 *error_string
= talloc_asprintf(mem_ctx
, "ndr_push of domain GUID failed!");
174 ldb_msg_add_value(msg
, "objectGUID", &v
);
176 } else if (database
== SAM_DATABASE_BUILTIN
) {
177 /* work out the builtin_dn - useful for so many calls its worth
179 const char *dnstring
= samdb_search_string(state
->sam_ldb
, mem_ctx
, NULL
,
180 "distinguishedName", "objectClass=builtinDomain");
181 state
->base_dn
[database
] = ldb_dn_explode(state
, dnstring
);
184 return NT_STATUS_INVALID_PARAMETER
;
187 msg
->dn
= talloc_reference(mem_ctx
, state
->base_dn
[database
]);
189 return NT_STATUS_NO_MEMORY
;
192 samdb_msg_add_string(state
->sam_ldb
, mem_ctx
,
193 msg
, "oEMInformation", domain
->comment
.string
);
195 samdb_msg_add_int64(state
->sam_ldb
, mem_ctx
,
196 msg
, "forceLogoff", domain
->force_logoff_time
);
198 samdb_msg_add_uint(state
->sam_ldb
, mem_ctx
,
199 msg
, "minPwdLen", domain
->min_password_length
);
201 samdb_msg_add_int64(state
->sam_ldb
, mem_ctx
,
202 msg
, "maxPwdAge", domain
->max_password_age
);
204 samdb_msg_add_int64(state
->sam_ldb
, mem_ctx
,
205 msg
, "minPwdAge", domain
->min_password_age
);
207 samdb_msg_add_uint(state
->sam_ldb
, mem_ctx
,
208 msg
, "pwdHistoryLength", domain
->password_history_length
);
210 samdb_msg_add_uint64(state
->sam_ldb
, mem_ctx
,
211 msg
, "modifiedCount",
212 domain
->sequence_num
);
214 samdb_msg_add_uint64(state
->sam_ldb
, mem_ctx
,
215 msg
, "creationTime", domain
->domain_create_time
);
217 /* TODO: Account lockout, password properties */
219 ret
= samdb_replace(state
->sam_ldb
, mem_ctx
, msg
);
222 return NT_STATUS_INTERNAL_ERROR
;
227 static NTSTATUS
samsync_ldb_handle_user(TALLOC_CTX
*mem_ctx
,
228 struct samsync_ldb_state
*state
,
229 enum netr_SamDatabaseID database
,
230 struct netr_DELTA_ENUM
*delta
,
233 uint32_t rid
= delta
->delta_id_union
.rid
;
234 struct netr_DELTA_USER
*user
= delta
->delta_union
.user
;
235 const char *container
, *obj_class
;
238 const struct dom_sid
*user_sid
;
239 struct ldb_message
*msg
;
240 struct ldb_message
**msgs
;
241 struct ldb_message
**remote_msgs
= NULL
;
245 const char *attrs
[] = { NULL
};
246 /* we may change this to a global search, then fill in only the things not in ldap later */
247 const char *remote_attrs
[] = { "userPrincipalName", "servicePrincipalName",
248 "msDS-KeyVersionNumber", "objectGUID", NULL
};
250 user_sid
= dom_sid_add_rid(mem_ctx
, state
->dom_sid
[database
], rid
);
252 return NT_STATUS_NO_MEMORY
;
255 msg
= ldb_msg_new(mem_ctx
);
257 return NT_STATUS_NO_MEMORY
;
261 /* search for the user, by rid */
262 ret
= gendb_search(state
->sam_ldb
, mem_ctx
, state
->base_dn
[database
],
263 &msgs
, attrs
, "(&(objectClass=user)(objectSid=%s))",
264 ldap_encode_ndr_dom_sid(mem_ctx
, user_sid
));
267 *error_string
= talloc_asprintf(mem_ctx
, "LDB for user %s failed: %s",
268 dom_sid_string(mem_ctx
, user_sid
),
269 ldb_errstring(state
->sam_ldb
));
270 return NT_STATUS_INTERNAL_DB_CORRUPTION
;
271 } else if (ret
== 0) {
273 } else if (ret
> 1) {
274 *error_string
= talloc_asprintf(mem_ctx
, "More than one user with SID: %s in local LDB",
275 dom_sid_string(mem_ctx
, user_sid
));
276 return NT_STATUS_INTERNAL_DB_CORRUPTION
;
278 msg
->dn
= msgs
[0]->dn
;
279 talloc_steal(msg
, msgs
[0]->dn
);
282 /* and do the same on the remote database */
283 if (state
->remote_ldb
) {
284 ret
= gendb_search(state
->remote_ldb
, mem_ctx
, state
->base_dn
[database
],
285 &remote_msgs
, remote_attrs
, "(&(objectClass=user)(objectSid=%s))",
286 ldap_encode_ndr_dom_sid(mem_ctx
, user_sid
));
289 *error_string
= talloc_asprintf(mem_ctx
, "remote LDAP for user %s failed: %s",
290 dom_sid_string(mem_ctx
, user_sid
),
291 ldb_errstring(state
->remote_ldb
));
292 return NT_STATUS_INTERNAL_DB_CORRUPTION
;
293 } else if (ret
== 0) {
294 *error_string
= talloc_asprintf(mem_ctx
, "User exists in samsync but not in remote LDAP domain! (base: %s, SID: %s)",
295 ldb_dn_linearize(mem_ctx
, state
->base_dn
[database
]),
296 dom_sid_string(mem_ctx
, user_sid
));
297 return NT_STATUS_NO_SUCH_USER
;
298 } else if (ret
> 1) {
299 *error_string
= talloc_asprintf(mem_ctx
, "More than one user in remote LDAP domain with SID: %s",
300 dom_sid_string(mem_ctx
, user_sid
));
301 return NT_STATUS_INTERNAL_DB_CORRUPTION
;
303 /* Try to put things in the same location as the remote server */
305 msg
->dn
= remote_msgs
[0]->dn
;
306 talloc_steal(msg
, remote_msgs
[0]->dn
);
310 cn_name
= talloc_strdup(mem_ctx
, user
->account_name
.string
);
311 NT_STATUS_HAVE_NO_MEMORY(cn_name
);
312 cn_name_len
= strlen(cn_name
);
314 #define ADD_OR_DEL(type, attrib, field) do { \
316 samdb_msg_add_ ## type(state->sam_ldb, mem_ctx, msg, \
317 attrib, user->field); \
319 samdb_msg_add_delete(state->sam_ldb, mem_ctx, msg, \
324 ADD_OR_DEL(string
, "samAccountName", account_name
.string
);
325 ADD_OR_DEL(string
, "displayName", full_name
.string
);
327 if (samdb_msg_add_dom_sid(state
->sam_ldb
, mem_ctx
, msg
,
328 "objectSid", dom_sid_add_rid(mem_ctx
, state
->dom_sid
[database
], rid
))) {
329 return NT_STATUS_NO_MEMORY
;
332 ADD_OR_DEL(uint
, "primaryGroupID", primary_gid
);
333 ADD_OR_DEL(string
, "homeDirectory", home_directory
.string
);
334 ADD_OR_DEL(string
, "homeDrive", home_drive
.string
);
335 ADD_OR_DEL(string
, "scriptPath", logon_script
.string
);
336 ADD_OR_DEL(string
, "description", description
.string
);
337 ADD_OR_DEL(string
, "userWorkstations", workstations
.string
);
339 ADD_OR_DEL(uint64
, "lastLogon", last_logon
);
340 ADD_OR_DEL(uint64
, "lastLogoff", last_logoff
);
342 if (samdb_msg_add_logon_hours(state
->sam_ldb
, mem_ctx
, msg
, "logonHours", &user
->logon_hours
) != 0) {
343 return NT_STATUS_NO_MEMORY
;
346 ADD_OR_DEL(uint
, "badPwdCount", bad_password_count
);
347 ADD_OR_DEL(uint
, "logonCount", logon_count
);
349 ADD_OR_DEL(uint64
, "pwdLastSet", last_password_change
);
350 ADD_OR_DEL(uint64
, "accountExpires", acct_expiry
);
352 if (samdb_msg_add_acct_flags(state
->sam_ldb
, mem_ctx
, msg
,
353 "userAccountControl", user
->acct_flags
) != 0) {
354 return NT_STATUS_NO_MEMORY
;
357 /* Passwords. Ensure there is no plaintext stored against
358 * this entry, as we only have hashes */
359 samdb_msg_add_delete(state
->sam_ldb
, mem_ctx
, msg
,
361 if (user
->lm_password_present
) {
362 samdb_msg_add_hash(state
->sam_ldb
, mem_ctx
, msg
,
363 "lmPwdHash", &user
->lmpassword
);
365 samdb_msg_add_delete(state
->sam_ldb
, mem_ctx
, msg
,
368 if (user
->nt_password_present
) {
369 samdb_msg_add_hash(state
->sam_ldb
, mem_ctx
, msg
,
370 "ntPwdHash", &user
->ntpassword
);
372 samdb_msg_add_delete(state
->sam_ldb
, mem_ctx
, msg
,
376 ADD_OR_DEL(string
, "comment", comment
.string
);
377 ADD_OR_DEL(string
, "userParameters", parameters
.string
);
378 ADD_OR_DEL(uint
, "countryCode", country_code
);
379 ADD_OR_DEL(uint
, "codePage", code_page
);
381 ADD_OR_DEL(string
, "profilePath", profile_path
.string
);
385 for (i
=0; remote_attrs
[i
]; i
++) {
386 struct ldb_message_element
*el
= ldb_msg_find_element(remote_msgs
[0], remote_attrs
[i
]);
388 samdb_msg_add_delete(state
->sam_ldb
, mem_ctx
, msg
,
391 ldb_msg_add(msg
, el
, LDB_FLAG_MOD_REPLACE
);
395 acb
= user
->acct_flags
;
396 if (acb
& (ACB_WSTRUST
)) {
397 cn_name
[cn_name_len
- 1] = '\0';
398 container
= "Computers";
399 obj_class
= "computer";
401 } else if (acb
& ACB_SVRTRUST
) {
402 if (cn_name
[cn_name_len
- 1] != '$') {
403 return NT_STATUS_FOOBAR
;
405 cn_name
[cn_name_len
- 1] = '\0';
406 container
= "Domain Controllers";
407 obj_class
= "computer";
413 samdb_msg_add_string(state
->sam_ldb
, mem_ctx
, msg
,
414 "objectClass", obj_class
);
416 msg
->dn
= ldb_dn_string_compose(mem_ctx
, state
->base_dn
[database
],
417 "CN=%s, CN=%s", cn_name
, container
);
419 return NT_STATUS_NO_MEMORY
;
423 ret
= samdb_add(state
->sam_ldb
, mem_ctx
, msg
);
425 struct ldb_dn
*first_try_dn
= msg
->dn
;
426 /* Try again with the default DN */
427 msg
->dn
= talloc_steal(msg
, msgs
[0]->dn
);
428 ret
= samdb_add(state
->sam_ldb
, mem_ctx
, msg
);
430 *error_string
= talloc_asprintf(mem_ctx
, "Failed to create user record. Tried both %s and %s: %s",
431 ldb_dn_linearize(mem_ctx
, first_try_dn
),
432 ldb_dn_linearize(mem_ctx
, msg
->dn
),
433 ldb_errstring(state
->sam_ldb
));
434 return NT_STATUS_INTERNAL_DB_CORRUPTION
;
438 ret
= samdb_replace(state
->sam_ldb
, mem_ctx
, msg
);
440 *error_string
= talloc_asprintf(mem_ctx
, "Failed to modify user record %s: %s",
441 ldb_dn_linearize(mem_ctx
, msg
->dn
),
442 ldb_errstring(state
->sam_ldb
));
443 return NT_STATUS_INTERNAL_DB_CORRUPTION
;
450 static NTSTATUS
samsync_ldb_delete_user(TALLOC_CTX
*mem_ctx
,
451 struct samsync_ldb_state
*state
,
452 enum netr_SamDatabaseID database
,
453 struct netr_DELTA_ENUM
*delta
,
456 uint32_t rid
= delta
->delta_id_union
.rid
;
457 struct ldb_message
**msgs
;
459 const char *attrs
[] = { NULL
};
461 /* search for the user, by rid */
462 ret
= gendb_search(state
->sam_ldb
, mem_ctx
, state
->base_dn
[database
],
463 &msgs
, attrs
, "(&(objectClass=user)(objectSid=%s))",
464 ldap_encode_ndr_dom_sid(mem_ctx
, dom_sid_add_rid(mem_ctx
, state
->dom_sid
[database
], rid
)));
467 *error_string
= talloc_asprintf(mem_ctx
, "gendb_search failed: %s", ldb_errstring(state
->sam_ldb
));
468 return NT_STATUS_INTERNAL_DB_CORRUPTION
;
469 } else if (ret
== 0) {
470 return NT_STATUS_NO_SUCH_USER
;
471 } else if (ret
> 1) {
472 *error_string
= talloc_asprintf(mem_ctx
, "More than one user with SID: %s",
473 dom_sid_string(mem_ctx
,
474 dom_sid_add_rid(mem_ctx
,
475 state
->dom_sid
[database
],
477 return NT_STATUS_INTERNAL_DB_CORRUPTION
;
480 ret
= samdb_delete(state
->sam_ldb
, mem_ctx
, msgs
[0]->dn
);
482 *error_string
= talloc_asprintf(mem_ctx
, "Failed to delete user record %s: %s",
483 ldb_dn_linearize(mem_ctx
, msgs
[0]->dn
),
484 ldb_errstring(state
->sam_ldb
));
485 return NT_STATUS_INTERNAL_DB_CORRUPTION
;
491 static NTSTATUS
samsync_ldb_handle_group(TALLOC_CTX
*mem_ctx
,
492 struct samsync_ldb_state
*state
,
493 enum netr_SamDatabaseID database
,
494 struct netr_DELTA_ENUM
*delta
,
497 uint32_t rid
= delta
->delta_id_union
.rid
;
498 struct netr_DELTA_GROUP
*group
= delta
->delta_union
.group
;
499 const char *container
, *obj_class
;
502 struct ldb_message
*msg
;
503 struct ldb_message
**msgs
;
506 const char *attrs
[] = { NULL
};
508 msg
= ldb_msg_new(mem_ctx
);
510 return NT_STATUS_NO_MEMORY
;
513 /* search for the group, by rid */
514 ret
= gendb_search(state
->sam_ldb
, mem_ctx
, state
->base_dn
[database
], &msgs
, attrs
,
515 "(&(objectClass=group)(objectSid=%s))",
516 ldap_encode_ndr_dom_sid(mem_ctx
, dom_sid_add_rid(mem_ctx
, state
->dom_sid
[database
], rid
)));
519 *error_string
= talloc_asprintf(mem_ctx
, "gendb_search failed: %s", ldb_errstring(state
->sam_ldb
));
520 return NT_STATUS_INTERNAL_DB_CORRUPTION
;
521 } else if (ret
== 0) {
523 } else if (ret
> 1) {
524 *error_string
= talloc_asprintf(mem_ctx
, "More than one group/alias with SID: %s",
525 dom_sid_string(mem_ctx
,
526 dom_sid_add_rid(mem_ctx
,
527 state
->dom_sid
[database
],
529 return NT_STATUS_INTERNAL_DB_CORRUPTION
;
531 msg
->dn
= talloc_steal(msg
, msgs
[0]->dn
);
534 cn_name
= group
->group_name
.string
;
536 #define ADD_OR_DEL(type, attrib, field) do { \
537 if (group->field) { \
538 samdb_msg_add_ ## type(state->sam_ldb, mem_ctx, msg, \
539 attrib, group->field); \
541 samdb_msg_add_delete(state->sam_ldb, mem_ctx, msg, \
546 ADD_OR_DEL(string
, "samAccountName", group_name
.string
);
548 if (samdb_msg_add_dom_sid(state
->sam_ldb
, mem_ctx
, msg
,
549 "objectSid", dom_sid_add_rid(mem_ctx
, state
->dom_sid
[database
], rid
))) {
550 return NT_STATUS_NO_MEMORY
;
553 ADD_OR_DEL(string
, "description", description
.string
);
561 samdb_msg_add_string(state
->sam_ldb
, mem_ctx
, msg
,
562 "objectClass", obj_class
);
563 msg
->dn
= ldb_dn_string_compose(mem_ctx
, state
->base_dn
[database
],
564 "CN=%s, CN=%s", cn_name
, container
);
566 return NT_STATUS_NO_MEMORY
;
569 ret
= samdb_add(state
->sam_ldb
, mem_ctx
, msg
);
571 *error_string
= talloc_asprintf(mem_ctx
, "Failed to create group record %s: %s",
572 ldb_dn_linearize(mem_ctx
, msg
->dn
),
573 ldb_errstring(state
->sam_ldb
));
574 return NT_STATUS_INTERNAL_DB_CORRUPTION
;
577 ret
= samdb_replace(state
->sam_ldb
, mem_ctx
, msg
);
579 *error_string
= talloc_asprintf(mem_ctx
, "Failed to modify group record %s: %s",
580 ldb_dn_linearize(mem_ctx
, msg
->dn
),
581 ldb_errstring(state
->sam_ldb
));
582 return NT_STATUS_INTERNAL_DB_CORRUPTION
;
589 static NTSTATUS
samsync_ldb_delete_group(TALLOC_CTX
*mem_ctx
,
590 struct samsync_ldb_state
*state
,
591 enum netr_SamDatabaseID database
,
592 struct netr_DELTA_ENUM
*delta
,
595 uint32_t rid
= delta
->delta_id_union
.rid
;
596 struct ldb_message
**msgs
;
598 const char *attrs
[] = { NULL
};
600 /* search for the group, by rid */
601 ret
= gendb_search(state
->sam_ldb
, mem_ctx
, state
->base_dn
[database
], &msgs
, attrs
,
602 "(&(objectClass=group)(objectSid=%s))",
603 ldap_encode_ndr_dom_sid(mem_ctx
, dom_sid_add_rid(mem_ctx
, state
->dom_sid
[database
], rid
)));
606 *error_string
= talloc_asprintf(mem_ctx
, "gendb_search failed: %s", ldb_errstring(state
->sam_ldb
));
607 return NT_STATUS_INTERNAL_DB_CORRUPTION
;
608 } else if (ret
== 0) {
609 return NT_STATUS_NO_SUCH_GROUP
;
610 } else if (ret
> 1) {
611 *error_string
= talloc_asprintf(mem_ctx
, "More than one group/alias with SID: %s",
612 dom_sid_string(mem_ctx
,
613 dom_sid_add_rid(mem_ctx
,
614 state
->dom_sid
[database
],
616 return NT_STATUS_INTERNAL_DB_CORRUPTION
;
619 ret
= samdb_delete(state
->sam_ldb
, mem_ctx
, msgs
[0]->dn
);
621 *error_string
= talloc_asprintf(mem_ctx
, "Failed to delete group record %s: %s",
622 ldb_dn_linearize(mem_ctx
, msgs
[0]->dn
),
623 ldb_errstring(state
->sam_ldb
));
624 return NT_STATUS_INTERNAL_DB_CORRUPTION
;
630 static NTSTATUS
samsync_ldb_handle_group_member(TALLOC_CTX
*mem_ctx
,
631 struct samsync_ldb_state
*state
,
632 enum netr_SamDatabaseID database
,
633 struct netr_DELTA_ENUM
*delta
,
636 uint32_t rid
= delta
->delta_id_union
.rid
;
637 struct netr_DELTA_GROUP_MEMBER
*group_member
= delta
->delta_union
.group_member
;
638 struct ldb_message
*msg
;
639 struct ldb_message
**msgs
;
641 const char *attrs
[] = { NULL
};
644 msg
= ldb_msg_new(mem_ctx
);
646 return NT_STATUS_NO_MEMORY
;
649 /* search for the group, by rid */
650 ret
= gendb_search(state
->sam_ldb
, mem_ctx
, state
->base_dn
[database
], &msgs
, attrs
,
651 "(&(objectClass=group)(objectSid=%s))",
652 ldap_encode_ndr_dom_sid(mem_ctx
, dom_sid_add_rid(mem_ctx
, state
->dom_sid
[database
], rid
)));
655 *error_string
= talloc_asprintf(mem_ctx
, "gendb_search failed: %s", ldb_errstring(state
->sam_ldb
));
656 return NT_STATUS_INTERNAL_DB_CORRUPTION
;
657 } else if (ret
== 0) {
658 return NT_STATUS_NO_SUCH_GROUP
;
659 } else if (ret
> 1) {
660 *error_string
= talloc_asprintf(mem_ctx
, "More than one group/alias with SID: %s",
661 dom_sid_string(mem_ctx
,
662 dom_sid_add_rid(mem_ctx
,
663 state
->dom_sid
[database
],
665 return NT_STATUS_INTERNAL_DB_CORRUPTION
;
667 msg
->dn
= talloc_steal(msg
, msgs
[0]->dn
);
672 for (i
=0; i
<group_member
->num_rids
; i
++) {
673 /* search for the group, by rid */
674 ret
= gendb_search(state
->sam_ldb
, mem_ctx
, state
->base_dn
[database
], &msgs
, attrs
,
675 "(&(objectClass=user)(objectSid=%s))",
676 ldap_encode_ndr_dom_sid(mem_ctx
, dom_sid_add_rid(mem_ctx
, state
->dom_sid
[database
], group_member
->rids
[i
])));
679 *error_string
= talloc_asprintf(mem_ctx
, "gendb_search failed: %s", ldb_errstring(state
->sam_ldb
));
680 return NT_STATUS_INTERNAL_DB_CORRUPTION
;
681 } else if (ret
== 0) {
682 return NT_STATUS_NO_SUCH_USER
;
683 } else if (ret
> 1) {
684 return NT_STATUS_INTERNAL_DB_CORRUPTION
;
686 samdb_msg_add_string(state
->sam_ldb
, mem_ctx
, msg
, "member", ldb_dn_linearize(mem_ctx
, msgs
[0]->dn
));
692 ret
= samdb_replace(state
->sam_ldb
, mem_ctx
, msg
);
694 *error_string
= talloc_asprintf(mem_ctx
, "Failed to modify group record %s: %s",
695 ldb_dn_linearize(mem_ctx
, msg
->dn
),
696 ldb_errstring(state
->sam_ldb
));
697 return NT_STATUS_INTERNAL_DB_CORRUPTION
;
703 static NTSTATUS
samsync_ldb_handle_alias(TALLOC_CTX
*mem_ctx
,
704 struct samsync_ldb_state
*state
,
705 enum netr_SamDatabaseID database
,
706 struct netr_DELTA_ENUM
*delta
,
709 uint32_t rid
= delta
->delta_id_union
.rid
;
710 struct netr_DELTA_ALIAS
*alias
= delta
->delta_union
.alias
;
711 const char *container
, *obj_class
;
714 struct ldb_message
*msg
;
715 struct ldb_message
**msgs
;
718 const char *attrs
[] = { NULL
};
720 msg
= ldb_msg_new(mem_ctx
);
722 return NT_STATUS_NO_MEMORY
;
725 /* search for the alias, by rid */
726 ret
= gendb_search(state
->sam_ldb
, mem_ctx
, state
->base_dn
[database
], &msgs
, attrs
,
727 "(&(objectClass=group)(objectSid=%s))",
728 ldap_encode_ndr_dom_sid(mem_ctx
, dom_sid_add_rid(mem_ctx
, state
->dom_sid
[database
], rid
)));
731 *error_string
= talloc_asprintf(mem_ctx
, "gendb_search failed: %s", ldb_errstring(state
->sam_ldb
));
732 return NT_STATUS_INTERNAL_DB_CORRUPTION
;
733 } else if (ret
== 0) {
735 } else if (ret
> 1) {
736 *error_string
= talloc_asprintf(mem_ctx
, "More than one group/alias with SID: %s",
737 dom_sid_string(mem_ctx
,
738 dom_sid_add_rid(mem_ctx
,
739 state
->dom_sid
[database
],
741 return NT_STATUS_INTERNAL_DB_CORRUPTION
;
743 msg
->dn
= talloc_steal(mem_ctx
, msgs
[0]->dn
);
746 cn_name
= alias
->alias_name
.string
;
748 #define ADD_OR_DEL(type, attrib, field) do { \
749 if (alias->field) { \
750 samdb_msg_add_ ## type(state->sam_ldb, mem_ctx, msg, \
751 attrib, alias->field); \
753 samdb_msg_add_delete(state->sam_ldb, mem_ctx, msg, \
758 ADD_OR_DEL(string
, "samAccountName", alias_name
.string
);
760 if (samdb_msg_add_dom_sid(state
->sam_ldb
, mem_ctx
, msg
,
761 "objectSid", dom_sid_add_rid(mem_ctx
, state
->dom_sid
[database
], rid
))) {
762 return NT_STATUS_NO_MEMORY
;
765 ADD_OR_DEL(string
, "description", description
.string
);
769 samdb_msg_add_uint(state
->sam_ldb
, mem_ctx
, msg
, "groupType", 0x80000004);
775 samdb_msg_add_string(state
->sam_ldb
, mem_ctx
, msg
,
776 "objectClass", obj_class
);
777 msg
->dn
= ldb_dn_string_compose(mem_ctx
, state
->base_dn
[database
],
778 "CN=%s, CN=%s", cn_name
, container
);
780 return NT_STATUS_NO_MEMORY
;
783 ret
= samdb_add(state
->sam_ldb
, mem_ctx
, msg
);
785 *error_string
= talloc_asprintf(mem_ctx
, "Failed to create alias record %s: %s",
786 ldb_dn_linearize(mem_ctx
, msg
->dn
),
787 ldb_errstring(state
->sam_ldb
));
788 return NT_STATUS_INTERNAL_DB_CORRUPTION
;
791 ret
= samdb_replace(state
->sam_ldb
, mem_ctx
, msg
);
793 *error_string
= talloc_asprintf(mem_ctx
, "Failed to modify alias record %s: %s",
794 ldb_dn_linearize(mem_ctx
, msg
->dn
),
795 ldb_errstring(state
->sam_ldb
));
796 return NT_STATUS_INTERNAL_DB_CORRUPTION
;
803 static NTSTATUS
samsync_ldb_delete_alias(TALLOC_CTX
*mem_ctx
,
804 struct samsync_ldb_state
*state
,
805 enum netr_SamDatabaseID database
,
806 struct netr_DELTA_ENUM
*delta
,
809 uint32_t rid
= delta
->delta_id_union
.rid
;
810 struct ldb_message
**msgs
;
812 const char *attrs
[] = { NULL
};
814 /* search for the alias, by rid */
815 ret
= gendb_search(state
->sam_ldb
, mem_ctx
, state
->base_dn
[database
], &msgs
, attrs
,
816 "(&(objectClass=group)(objectSid=%s))",
817 ldap_encode_ndr_dom_sid(mem_ctx
, dom_sid_add_rid(mem_ctx
, state
->dom_sid
[database
], rid
)));
820 *error_string
= talloc_asprintf(mem_ctx
, "gendb_search failed: %s", ldb_errstring(state
->sam_ldb
));
821 return NT_STATUS_INTERNAL_DB_CORRUPTION
;
822 } else if (ret
== 0) {
823 return NT_STATUS_NO_SUCH_ALIAS
;
824 } else if (ret
> 1) {
825 return NT_STATUS_INTERNAL_DB_CORRUPTION
;
828 ret
= samdb_delete(state
->sam_ldb
, mem_ctx
, msgs
[0]->dn
);
830 *error_string
= talloc_asprintf(mem_ctx
, "Failed to delete alias record %s: %s",
831 ldb_dn_linearize(mem_ctx
, msgs
[0]->dn
),
832 ldb_errstring(state
->sam_ldb
));
833 return NT_STATUS_INTERNAL_DB_CORRUPTION
;
839 static NTSTATUS
samsync_ldb_handle_alias_member(TALLOC_CTX
*mem_ctx
,
840 struct samsync_ldb_state
*state
,
841 enum netr_SamDatabaseID database
,
842 struct netr_DELTA_ENUM
*delta
,
845 uint32_t rid
= delta
->delta_id_union
.rid
;
846 struct netr_DELTA_ALIAS_MEMBER
*alias_member
= delta
->delta_union
.alias_member
;
847 struct ldb_message
*msg
;
848 struct ldb_message
**msgs
;
850 const char *attrs
[] = { NULL
};
853 msg
= ldb_msg_new(mem_ctx
);
855 return NT_STATUS_NO_MEMORY
;
858 /* search for the alias, by rid */
859 ret
= gendb_search(state
->sam_ldb
, mem_ctx
, state
->base_dn
[database
], &msgs
, attrs
,
860 "(&(objectClass=group)(objectSid=%s))",
861 ldap_encode_ndr_dom_sid(mem_ctx
, dom_sid_add_rid(mem_ctx
, state
->dom_sid
[database
], rid
)));
864 *error_string
= talloc_asprintf(mem_ctx
, "gendb_search failed: %s", ldb_errstring(state
->sam_ldb
));
865 return NT_STATUS_INTERNAL_DB_CORRUPTION
;
866 } else if (ret
== 0) {
867 return NT_STATUS_NO_SUCH_GROUP
;
868 } else if (ret
> 1) {
869 *error_string
= talloc_asprintf(mem_ctx
, "More than one group/alias with SID: %s",
870 dom_sid_string(mem_ctx
,
871 dom_sid_add_rid(mem_ctx
,
872 state
->dom_sid
[database
],
874 return NT_STATUS_INTERNAL_DB_CORRUPTION
;
876 msg
->dn
= talloc_steal(msg
, msgs
[0]->dn
);
881 for (i
=0; i
<alias_member
->sids
.num_sids
; i
++) {
882 struct ldb_dn
*alias_member_dn
;
883 /* search for members, in the top basedn (normal users are builtin aliases) */
884 ret
= gendb_search(state
->sam_ldb
, mem_ctx
, state
->base_dn
[SAM_DATABASE_DOMAIN
], &msgs
, attrs
,
886 ldap_encode_ndr_dom_sid(mem_ctx
, alias_member
->sids
.sids
[i
].sid
));
889 *error_string
= talloc_asprintf(mem_ctx
, "gendb_search failed: %s", ldb_errstring(state
->sam_ldb
));
890 return NT_STATUS_INTERNAL_DB_CORRUPTION
;
891 } else if (ret
== 0) {
893 nt_status
= samsync_ldb_add_foreignSecurityPrincipal(mem_ctx
, state
,
894 alias_member
->sids
.sids
[i
].sid
,
897 if (!NT_STATUS_IS_OK(nt_status
)) {
900 } else if (ret
> 1) {
901 return NT_STATUS_INTERNAL_DB_CORRUPTION
;
903 alias_member_dn
= msgs
[0]->dn
;
905 samdb_msg_add_string(state
->sam_ldb
, mem_ctx
, msg
, "member", ldb_dn_linearize(mem_ctx
, alias_member_dn
));
910 ret
= samdb_replace(state
->sam_ldb
, mem_ctx
, msg
);
912 *error_string
= talloc_asprintf(mem_ctx
, "Failed to modify group record %s: %s",
913 ldb_dn_linearize(mem_ctx
, msg
->dn
),
914 ldb_errstring(state
->sam_ldb
));
915 return NT_STATUS_INTERNAL_DB_CORRUPTION
;
921 static NTSTATUS
samsync_ldb_handle_account(TALLOC_CTX
*mem_ctx
,
922 struct samsync_ldb_state
*state
,
923 enum netr_SamDatabaseID database
,
924 struct netr_DELTA_ENUM
*delta
,
927 struct dom_sid
*sid
= delta
->delta_id_union
.sid
;
928 struct netr_DELTA_ACCOUNT
*account
= delta
->delta_union
.account
;
930 struct ldb_message
*msg
;
931 struct ldb_message
**msgs
;
932 struct ldb_dn
*privilege_dn
;
934 const char *attrs
[] = { NULL
};
937 msg
= ldb_msg_new(mem_ctx
);
939 return NT_STATUS_NO_MEMORY
;
942 /* search for the account, by sid, in the top basedn */
943 ret
= gendb_search(state
->sam_ldb
, mem_ctx
, state
->base_dn
[SAM_DATABASE_DOMAIN
], &msgs
, attrs
,
944 "(objectSid=%s)", ldap_encode_ndr_dom_sid(mem_ctx
, sid
));
947 *error_string
= talloc_asprintf(mem_ctx
, "gendb_search failed: %s", ldb_errstring(state
->sam_ldb
));
948 return NT_STATUS_INTERNAL_DB_CORRUPTION
;
949 } else if (ret
== 0) {
951 nt_status
= samsync_ldb_add_foreignSecurityPrincipal(mem_ctx
, state
,
955 privilege_dn
= talloc_steal(msg
, privilege_dn
);
956 if (!NT_STATUS_IS_OK(nt_status
)) {
959 } else if (ret
> 1) {
960 *error_string
= talloc_asprintf(mem_ctx
, "More than one account with SID: %s",
961 dom_sid_string(mem_ctx
, sid
));
962 return NT_STATUS_INTERNAL_DB_CORRUPTION
;
964 privilege_dn
= talloc_steal(msg
, msgs
[0]->dn
);
967 msg
->dn
= privilege_dn
;
969 for (i
=0; i
< account
->privilege_entries
; i
++) {
970 samdb_msg_add_string(state
->sam_ldb
, mem_ctx
, msg
, "privilege",
971 account
->privilege_name
[i
].string
);
974 ret
= samdb_replace(state
->sam_ldb
, mem_ctx
, msg
);
976 *error_string
= talloc_asprintf(mem_ctx
, "Failed to modify privilege record %s",
977 ldb_dn_linearize(mem_ctx
, msg
->dn
));
978 return NT_STATUS_INTERNAL_DB_CORRUPTION
;
984 static NTSTATUS
samsync_ldb_delete_account(TALLOC_CTX
*mem_ctx
,
985 struct samsync_ldb_state
*state
,
986 enum netr_SamDatabaseID database
,
987 struct netr_DELTA_ENUM
*delta
,
990 struct dom_sid
*sid
= delta
->delta_id_union
.sid
;
992 struct ldb_message
*msg
;
993 struct ldb_message
**msgs
;
995 const char *attrs
[] = { NULL
};
997 msg
= ldb_msg_new(mem_ctx
);
999 return NT_STATUS_NO_MEMORY
;
1002 /* search for the account, by sid, in the top basedn */
1003 ret
= gendb_search(state
->sam_ldb
, mem_ctx
, state
->base_dn
[SAM_DATABASE_DOMAIN
], &msgs
, attrs
,
1005 ldap_encode_ndr_dom_sid(mem_ctx
, sid
));
1008 *error_string
= talloc_asprintf(mem_ctx
, "gendb_search failed: %s", ldb_errstring(state
->sam_ldb
));
1009 return NT_STATUS_INTERNAL_DB_CORRUPTION
;
1010 } else if (ret
== 0) {
1011 return NT_STATUS_NO_SUCH_USER
;
1012 } else if (ret
> 1) {
1013 *error_string
= talloc_asprintf(mem_ctx
, "More than one account with SID: %s",
1014 dom_sid_string(mem_ctx
, sid
));
1015 return NT_STATUS_INTERNAL_DB_CORRUPTION
;
1017 msg
->dn
= talloc_steal(msg
, msgs
[0]->dn
);
1020 samdb_msg_add_delete(state
->sam_ldb
, mem_ctx
, msg
,
1023 ret
= samdb_replace(state
->sam_ldb
, mem_ctx
, msg
);
1025 *error_string
= talloc_asprintf(mem_ctx
, "Failed to modify privilege record %s",
1026 ldb_dn_linearize(mem_ctx
, msg
->dn
));
1027 return NT_STATUS_INTERNAL_DB_CORRUPTION
;
1030 return NT_STATUS_OK
;
1033 static NTSTATUS
libnet_samsync_ldb_fn(TALLOC_CTX
*mem_ctx
,
1035 enum netr_SamDatabaseID database
,
1036 struct netr_DELTA_ENUM
*delta
,
1037 char **error_string
)
1039 NTSTATUS nt_status
= NT_STATUS_OK
;
1040 struct samsync_ldb_state
*state
= talloc_get_type(private, struct samsync_ldb_state
);
1042 *error_string
= NULL
;
1043 switch (delta
->delta_type
) {
1044 case NETR_DELTA_DOMAIN
:
1046 nt_status
= samsync_ldb_handle_domain(mem_ctx
,
1053 case NETR_DELTA_USER
:
1055 nt_status
= samsync_ldb_handle_user(mem_ctx
,
1062 case NETR_DELTA_DELETE_USER
:
1064 nt_status
= samsync_ldb_delete_user(mem_ctx
,
1071 case NETR_DELTA_GROUP
:
1073 nt_status
= samsync_ldb_handle_group(mem_ctx
,
1080 case NETR_DELTA_DELETE_GROUP
:
1082 nt_status
= samsync_ldb_delete_group(mem_ctx
,
1089 case NETR_DELTA_GROUP_MEMBER
:
1091 nt_status
= samsync_ldb_handle_group_member(mem_ctx
,
1098 case NETR_DELTA_ALIAS
:
1100 nt_status
= samsync_ldb_handle_alias(mem_ctx
,
1107 case NETR_DELTA_DELETE_ALIAS
:
1109 nt_status
= samsync_ldb_delete_alias(mem_ctx
,
1116 case NETR_DELTA_ALIAS_MEMBER
:
1118 nt_status
= samsync_ldb_handle_alias_member(mem_ctx
,
1125 case NETR_DELTA_ACCOUNT
:
1127 nt_status
= samsync_ldb_handle_account(mem_ctx
,
1134 case NETR_DELTA_DELETE_ACCOUNT
:
1136 nt_status
= samsync_ldb_delete_account(mem_ctx
,
1144 /* Can't dump them all right now */
1147 if (!NT_STATUS_IS_OK(nt_status
) && !*error_string
) {
1148 *error_string
= talloc_asprintf(mem_ctx
, "Failed to handle samsync delta: %s", nt_errstr(nt_status
));
1153 static NTSTATUS
libnet_samsync_ldb_init(TALLOC_CTX
*mem_ctx
,
1155 struct libnet_SamSync_state
*samsync_state
,
1156 char **error_string
)
1158 struct samsync_ldb_state
*state
= talloc_get_type(private, struct samsync_ldb_state
);
1159 const char *server
= dcerpc_server_name(samsync_state
->netlogon_pipe
);
1162 state
->samsync_state
= samsync_state
;
1164 ZERO_STRUCT(state
->dom_sid
);
1165 if (state
->samsync_state
->domain_sid
) {
1166 state
->dom_sid
[SAM_DATABASE_DOMAIN
] = dom_sid_dup(state
, state
->samsync_state
->domain_sid
);
1169 state
->dom_sid
[SAM_DATABASE_BUILTIN
] = dom_sid_parse_talloc(state
, SID_BUILTIN
);
1171 if (state
->samsync_state
->realm
) {
1172 if (!server
|| !*server
) {
1173 /* huh? how do we not have a server name? */
1174 *error_string
= talloc_strdup(mem_ctx
, "No DCE/RPC server name available. How did we connect?");
1175 return NT_STATUS_INVALID_PARAMETER
;
1177 ldap_url
= talloc_asprintf(state
, "ldap://%s", server
);
1179 state
->remote_ldb
= ldb_wrap_connect(mem_ctx
, ldap_url
,
1180 NULL
, state
->samsync_state
->machine_net_ctx
->cred
,
1182 if (!state
->remote_ldb
) {
1183 *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
);
1184 return NT_STATUS_NO_LOGON_SERVERS
;
1187 state
->remote_ldb
= NULL
;
1189 return NT_STATUS_OK
;
1192 NTSTATUS
libnet_samsync_ldb(struct libnet_context
*ctx
, TALLOC_CTX
*mem_ctx
, struct libnet_samsync_ldb
*r
)
1195 struct libnet_SamSync r2
;
1196 struct samsync_ldb_state
*state
= talloc(mem_ctx
, struct samsync_ldb_state
);
1199 return NT_STATUS_NO_MEMORY
;
1202 state
->secrets
= NULL
;
1203 state
->trusted_domains
= NULL
;
1205 state
->sam_ldb
= ldb_wrap_connect(mem_ctx
, lp_sam_url(), r
->in
.session_info
,
1206 ctx
->cred
, 0, NULL
);
1208 r2
.out
.error_string
= NULL
;
1209 r2
.in
.binding_string
= r
->in
.binding_string
;
1210 r2
.in
.init_fn
= libnet_samsync_ldb_init
;
1211 r2
.in
.delta_fn
= libnet_samsync_ldb_fn
;
1212 r2
.in
.fn_ctx
= state
;
1213 r2
.in
.machine_account
= NULL
; /* TODO: Create a machine account, fill this in, and the delete it */
1214 nt_status
= libnet_SamSync_netlogon(ctx
, state
, &r2
);
1215 r
->out
.error_string
= r2
.out
.error_string
;
1216 talloc_steal(mem_ctx
, r
->out
.error_string
);
1218 if (!NT_STATUS_IS_OK(nt_status
)) {