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 "param/param.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
, *pdb
;
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_get_linearized(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 */
93 if ( ! ldb_dn_add_child_fmt(msg
->dn
, "CN=%s", sidstr
))
94 return NT_STATUS_UNSUCCESSFUL
;
96 samdb_msg_add_string(state
->sam_ldb
, mem_ctx
, msg
,
98 "foreignSecurityPrincipal");
102 /* create the alias */
103 ret
= ldb_add(state
->sam_ldb
, msg
);
104 if (ret
!= LDB_SUCCESS
) {
105 *error_string
= talloc_asprintf(mem_ctx
, "Failed to create foreignSecurityPrincipal "
107 ldb_dn_get_linearized(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 struct ldb_dn
*partitions_basedn
;
132 const char *domain_attrs
[] = {"nETBIOSName", "nCName", NULL
};
133 struct ldb_message
**msgs_domain
;
136 partitions_basedn
= samdb_partitions_dn(state
->sam_ldb
, mem_ctx
);
138 ret_domain
= gendb_search(state
->sam_ldb
, mem_ctx
, partitions_basedn
, &msgs_domain
, domain_attrs
,
139 "(&(&(nETBIOSName=%s)(objectclass=crossRef))(ncName=*))",
141 if (ret_domain
== -1) {
142 *error_string
= talloc_asprintf(mem_ctx
, "gendb_search for domain failed: %s", ldb_errstring(state
->sam_ldb
));
143 return NT_STATUS_INTERNAL_DB_CORRUPTION
;
146 if (ret_domain
!= 1) {
147 *error_string
= talloc_asprintf(mem_ctx
, "Failed to find existing domain record for %s: %d results", domain_name
,
149 return NT_STATUS_NO_SUCH_DOMAIN
;
152 state
->base_dn
[database
] = samdb_result_dn(state
->sam_ldb
, state
, msgs_domain
[0], "nCName", NULL
);
154 if (state
->dom_sid
[database
]) {
155 /* Update the domain sid with the incoming
156 * domain (found on LSA pipe, database sid may
158 samdb_msg_add_dom_sid(state
->sam_ldb
, mem_ctx
,
159 msg
, "objectSid", state
->dom_sid
[database
]);
161 /* Well, we will have to use the one from the database */
162 state
->dom_sid
[database
] = samdb_search_dom_sid(state
->sam_ldb
, state
,
163 state
->base_dn
[database
],
167 if (state
->samsync_state
->domain_guid
) {
170 status
= GUID_to_ndr_blob(state
->samsync_state
->domain_guid
, msg
, &v
);
171 if (!NT_STATUS_IS_OK(status
)) {
172 *error_string
= talloc_asprintf(mem_ctx
, "ndr_push of domain GUID failed!");
176 ldb_msg_add_value(msg
, "objectGUID", &v
, NULL
);
178 } else if (database
== SAM_DATABASE_BUILTIN
) {
179 /* work out the builtin_dn - useful for so many calls its worth
181 const char *dnstring
= samdb_search_string(state
->sam_ldb
, mem_ctx
, NULL
,
182 "distinguishedName", "objectClass=builtinDomain");
183 state
->base_dn
[database
] = ldb_dn_new(state
, state
->sam_ldb
, dnstring
);
184 if ( ! ldb_dn_validate(state
->base_dn
[database
])) {
185 return NT_STATUS_INTERNAL_ERROR
;
189 return NT_STATUS_INVALID_PARAMETER
;
192 msg
->dn
= talloc_reference(mem_ctx
, state
->base_dn
[database
]);
194 return NT_STATUS_NO_MEMORY
;
197 samdb_msg_add_string(state
->sam_ldb
, mem_ctx
,
198 msg
, "oEMInformation", domain
->oem_information
.string
);
200 samdb_msg_add_int64(state
->sam_ldb
, mem_ctx
,
201 msg
, "forceLogoff", domain
->force_logoff_time
);
203 samdb_msg_add_uint(state
->sam_ldb
, mem_ctx
,
204 msg
, "minPwdLen", domain
->min_password_length
);
206 samdb_msg_add_int64(state
->sam_ldb
, mem_ctx
,
207 msg
, "maxPwdAge", domain
->max_password_age
);
209 samdb_msg_add_int64(state
->sam_ldb
, mem_ctx
,
210 msg
, "minPwdAge", domain
->min_password_age
);
212 samdb_msg_add_uint(state
->sam_ldb
, mem_ctx
,
213 msg
, "pwdHistoryLength", domain
->password_history_length
);
215 samdb_msg_add_uint64(state
->sam_ldb
, mem_ctx
,
216 msg
, "modifiedCount",
217 domain
->sequence_num
);
219 samdb_msg_add_uint64(state
->sam_ldb
, mem_ctx
,
220 msg
, "creationTime", domain
->domain_create_time
);
222 /* TODO: Account lockout, password properties */
224 ret
= dsdb_replace(state
->sam_ldb
, msg
, 0);
225 if (ret
!= LDB_SUCCESS
) {
226 *error_string
= talloc_asprintf(mem_ctx
,
227 "Failed to modify domain record %s: %s",
228 ldb_dn_get_linearized(msg
->dn
),
229 ldb_errstring(state
->sam_ldb
));
230 return NT_STATUS_INTERNAL_DB_CORRUPTION
;
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
;
254 const char *attrs
[] = { NULL
};
255 /* we may change this to a global search, then fill in only the things not in ldap later */
256 const char *remote_attrs
[] = { "userPrincipalName", "servicePrincipalName",
257 "msDS-KeyVersionNumber", "objectGUID", NULL
};
259 user_sid
= dom_sid_add_rid(mem_ctx
, state
->dom_sid
[database
], rid
);
261 return NT_STATUS_NO_MEMORY
;
264 msg
= ldb_msg_new(mem_ctx
);
266 return NT_STATUS_NO_MEMORY
;
270 /* search for the user, by rid */
271 ret
= gendb_search(state
->sam_ldb
, mem_ctx
, state
->base_dn
[database
],
272 &msgs
, attrs
, "(&(objectClass=user)(objectSid=%s))",
273 ldap_encode_ndr_dom_sid(mem_ctx
, user_sid
));
276 *error_string
= talloc_asprintf(mem_ctx
, "LDB for user %s failed: %s",
277 dom_sid_string(mem_ctx
, user_sid
),
278 ldb_errstring(state
->sam_ldb
));
279 return NT_STATUS_INTERNAL_DB_CORRUPTION
;
280 } else if (ret
== 0) {
282 } else if (ret
> 1) {
283 *error_string
= talloc_asprintf(mem_ctx
, "More than one user with SID: %s in local LDB",
284 dom_sid_string(mem_ctx
, user_sid
));
285 return NT_STATUS_INTERNAL_DB_CORRUPTION
;
287 msg
->dn
= msgs
[0]->dn
;
288 talloc_steal(msg
, msgs
[0]->dn
);
291 /* and do the same on the remote database */
292 if (state
->remote_ldb
) {
293 ret
= gendb_search(state
->remote_ldb
, mem_ctx
, state
->base_dn
[database
],
294 &remote_msgs
, remote_attrs
, "(&(objectClass=user)(objectSid=%s))",
295 ldap_encode_ndr_dom_sid(mem_ctx
, user_sid
));
298 *error_string
= talloc_asprintf(mem_ctx
, "remote LDAP for user %s failed: %s",
299 dom_sid_string(mem_ctx
, user_sid
),
300 ldb_errstring(state
->remote_ldb
));
301 return NT_STATUS_INTERNAL_DB_CORRUPTION
;
302 } else if (ret
== 0) {
303 *error_string
= talloc_asprintf(mem_ctx
, "User exists in samsync but not in remote LDAP domain! (base: %s, SID: %s)",
304 ldb_dn_get_linearized(state
->base_dn
[database
]),
305 dom_sid_string(mem_ctx
, user_sid
));
306 return NT_STATUS_NO_SUCH_USER
;
307 } else if (ret
> 1) {
308 *error_string
= talloc_asprintf(mem_ctx
, "More than one user in remote LDAP domain with SID: %s",
309 dom_sid_string(mem_ctx
, user_sid
));
310 return NT_STATUS_INTERNAL_DB_CORRUPTION
;
312 /* Try to put things in the same location as the remote server */
314 msg
->dn
= 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
;
365 if (user
->lm_password_present
) {
366 samdb_msg_add_hash(state
->sam_ldb
, mem_ctx
, msg
,
367 "dBCSPwd", &user
->lmpassword
);
369 samdb_msg_add_delete(state
->sam_ldb
, mem_ctx
, msg
,
372 if (user
->nt_password_present
) {
373 samdb_msg_add_hash(state
->sam_ldb
, mem_ctx
, msg
,
374 "unicodePwd", &user
->ntpassword
);
376 samdb_msg_add_delete(state
->sam_ldb
, mem_ctx
, msg
,
380 ADD_OR_DEL(string
, "comment", comment
.string
);
382 if (samdb_msg_add_parameters(state
->sam_ldb
, mem_ctx
, msg
, "userParameters", &user
->parameters
) != 0) {
383 return NT_STATUS_NO_MEMORY
;
386 ADD_OR_DEL(uint
, "countryCode", country_code
);
387 ADD_OR_DEL(uint
, "codePage", code_page
);
389 ADD_OR_DEL(string
, "profilePath", profile_path
.string
);
393 for (i
=0; remote_attrs
[i
]; i
++) {
394 struct ldb_message_element
*el
= ldb_msg_find_element(remote_msgs
[0], remote_attrs
[i
]);
396 samdb_msg_add_delete(state
->sam_ldb
, mem_ctx
, msg
,
399 ldb_msg_add(msg
, el
, LDB_FLAG_MOD_REPLACE
);
403 acb
= user
->acct_flags
;
404 if (acb
& (ACB_WSTRUST
)) {
405 cn_name
[cn_name_len
- 1] = '\0';
406 container
= "Computers";
407 obj_class
= "computer";
409 } else if (acb
& ACB_SVRTRUST
) {
410 if (cn_name
[cn_name_len
- 1] != '$') {
411 return NT_STATUS_FOOBAR
;
413 cn_name
[cn_name_len
- 1] = '\0';
414 container
= "Domain Controllers";
415 obj_class
= "computer";
421 samdb_msg_add_string(state
->sam_ldb
, mem_ctx
, msg
,
422 "objectClass", obj_class
);
424 msg
->dn
= ldb_dn_copy(mem_ctx
, state
->base_dn
[database
]);
425 ldb_dn_add_child_fmt(msg
->dn
, "CN=%s,CN=%s", cn_name
, container
);
427 return NT_STATUS_NO_MEMORY
;
431 ret
= ldb_add(state
->sam_ldb
, msg
);
432 if (ret
!= LDB_SUCCESS
) {
433 struct ldb_dn
*first_try_dn
= msg
->dn
;
434 /* Try again with the default DN */
436 *error_string
= talloc_asprintf(mem_ctx
, "Failed to create user record. Tried %s: %s",
437 ldb_dn_get_linearized(first_try_dn
),
438 ldb_errstring(state
->sam_ldb
));
439 return NT_STATUS_INTERNAL_DB_CORRUPTION
;
441 msg
->dn
= talloc_steal(msg
, remote_msgs
[0]->dn
);
442 ret
= ldb_add(state
->sam_ldb
, msg
);
443 if (ret
!= LDB_SUCCESS
) {
444 *error_string
= talloc_asprintf(mem_ctx
, "Failed to create user record. Tried both %s and %s: %s",
445 ldb_dn_get_linearized(first_try_dn
),
446 ldb_dn_get_linearized(msg
->dn
),
447 ldb_errstring(state
->sam_ldb
));
448 return NT_STATUS_INTERNAL_DB_CORRUPTION
;
453 ret
= dsdb_replace(state
->sam_ldb
, msg
, 0);
454 if (ret
!= LDB_SUCCESS
) {
455 *error_string
= talloc_asprintf(mem_ctx
, "Failed to modify user record %s: %s",
456 ldb_dn_get_linearized(msg
->dn
),
457 ldb_errstring(state
->sam_ldb
));
458 return NT_STATUS_INTERNAL_DB_CORRUPTION
;
465 static NTSTATUS
samsync_ldb_delete_user(TALLOC_CTX
*mem_ctx
,
466 struct samsync_ldb_state
*state
,
467 enum netr_SamDatabaseID database
,
468 struct netr_DELTA_ENUM
*delta
,
471 uint32_t rid
= delta
->delta_id_union
.rid
;
472 struct ldb_message
**msgs
;
474 const char *attrs
[] = { NULL
};
476 /* search for the user, by rid */
477 ret
= gendb_search(state
->sam_ldb
, mem_ctx
, state
->base_dn
[database
],
478 &msgs
, attrs
, "(&(objectClass=user)(objectSid=%s))",
479 ldap_encode_ndr_dom_sid(mem_ctx
, dom_sid_add_rid(mem_ctx
, state
->dom_sid
[database
], rid
)));
482 *error_string
= talloc_asprintf(mem_ctx
, "gendb_search failed: %s", ldb_errstring(state
->sam_ldb
));
483 return NT_STATUS_INTERNAL_DB_CORRUPTION
;
484 } else if (ret
== 0) {
485 return NT_STATUS_NO_SUCH_USER
;
486 } else if (ret
> 1) {
487 *error_string
= talloc_asprintf(mem_ctx
, "More than one user with SID: %s",
488 dom_sid_string(mem_ctx
,
489 dom_sid_add_rid(mem_ctx
,
490 state
->dom_sid
[database
],
492 return NT_STATUS_INTERNAL_DB_CORRUPTION
;
495 ret
= ldb_delete(state
->sam_ldb
, msgs
[0]->dn
);
496 if (ret
!= LDB_SUCCESS
) {
497 *error_string
= talloc_asprintf(mem_ctx
, "Failed to delete user record %s: %s",
498 ldb_dn_get_linearized(msgs
[0]->dn
),
499 ldb_errstring(state
->sam_ldb
));
500 return NT_STATUS_INTERNAL_DB_CORRUPTION
;
506 static NTSTATUS
samsync_ldb_handle_group(TALLOC_CTX
*mem_ctx
,
507 struct samsync_ldb_state
*state
,
508 enum netr_SamDatabaseID database
,
509 struct netr_DELTA_ENUM
*delta
,
512 uint32_t rid
= delta
->delta_id_union
.rid
;
513 struct netr_DELTA_GROUP
*group
= delta
->delta_union
.group
;
514 const char *container
, *obj_class
;
517 struct ldb_message
*msg
;
518 struct ldb_message
**msgs
;
521 const char *attrs
[] = { NULL
};
523 msg
= ldb_msg_new(mem_ctx
);
525 return NT_STATUS_NO_MEMORY
;
528 /* search for the group, by rid */
529 ret
= gendb_search(state
->sam_ldb
, mem_ctx
, state
->base_dn
[database
], &msgs
, attrs
,
530 "(&(objectClass=group)(objectSid=%s))",
531 ldap_encode_ndr_dom_sid(mem_ctx
, dom_sid_add_rid(mem_ctx
, state
->dom_sid
[database
], rid
)));
534 *error_string
= talloc_asprintf(mem_ctx
, "gendb_search failed: %s", ldb_errstring(state
->sam_ldb
));
535 return NT_STATUS_INTERNAL_DB_CORRUPTION
;
536 } else if (ret
== 0) {
538 } else if (ret
> 1) {
539 *error_string
= talloc_asprintf(mem_ctx
, "More than one group/alias with SID: %s",
540 dom_sid_string(mem_ctx
,
541 dom_sid_add_rid(mem_ctx
,
542 state
->dom_sid
[database
],
544 return NT_STATUS_INTERNAL_DB_CORRUPTION
;
546 msg
->dn
= talloc_steal(msg
, msgs
[0]->dn
);
549 cn_name
= group
->group_name
.string
;
551 #define ADD_OR_DEL(type, attrib, field) do { \
552 if (group->field) { \
553 samdb_msg_add_ ## type(state->sam_ldb, mem_ctx, msg, \
554 attrib, group->field); \
556 samdb_msg_add_delete(state->sam_ldb, mem_ctx, msg, \
561 ADD_OR_DEL(string
, "samAccountName", group_name
.string
);
563 if (samdb_msg_add_dom_sid(state
->sam_ldb
, mem_ctx
, msg
,
564 "objectSid", dom_sid_add_rid(mem_ctx
, state
->dom_sid
[database
], rid
))) {
565 return NT_STATUS_NO_MEMORY
;
568 ADD_OR_DEL(string
, "description", description
.string
);
576 samdb_msg_add_string(state
->sam_ldb
, mem_ctx
, msg
,
577 "objectClass", obj_class
);
578 msg
->dn
= ldb_dn_copy(mem_ctx
, state
->base_dn
[database
]);
579 ldb_dn_add_child_fmt(msg
->dn
, "CN=%s,CN=%s", cn_name
, container
);
581 return NT_STATUS_NO_MEMORY
;
584 ret
= ldb_add(state
->sam_ldb
, msg
);
585 if (ret
!= LDB_SUCCESS
) {
586 *error_string
= talloc_asprintf(mem_ctx
, "Failed to create group record %s: %s",
587 ldb_dn_get_linearized(msg
->dn
),
588 ldb_errstring(state
->sam_ldb
));
589 return NT_STATUS_INTERNAL_DB_CORRUPTION
;
592 ret
= dsdb_replace(state
->sam_ldb
, msg
, 0);
593 if (ret
!= LDB_SUCCESS
) {
594 *error_string
= talloc_asprintf(mem_ctx
, "Failed to modify group record %s: %s",
595 ldb_dn_get_linearized(msg
->dn
),
596 ldb_errstring(state
->sam_ldb
));
597 return NT_STATUS_INTERNAL_DB_CORRUPTION
;
604 static NTSTATUS
samsync_ldb_delete_group(TALLOC_CTX
*mem_ctx
,
605 struct samsync_ldb_state
*state
,
606 enum netr_SamDatabaseID database
,
607 struct netr_DELTA_ENUM
*delta
,
610 uint32_t rid
= delta
->delta_id_union
.rid
;
611 struct ldb_message
**msgs
;
613 const char *attrs
[] = { NULL
};
615 /* search for the group, by rid */
616 ret
= gendb_search(state
->sam_ldb
, mem_ctx
, state
->base_dn
[database
], &msgs
, attrs
,
617 "(&(objectClass=group)(objectSid=%s))",
618 ldap_encode_ndr_dom_sid(mem_ctx
, dom_sid_add_rid(mem_ctx
, state
->dom_sid
[database
], rid
)));
621 *error_string
= talloc_asprintf(mem_ctx
, "gendb_search failed: %s", ldb_errstring(state
->sam_ldb
));
622 return NT_STATUS_INTERNAL_DB_CORRUPTION
;
623 } else if (ret
== 0) {
624 return NT_STATUS_NO_SUCH_GROUP
;
625 } else if (ret
> 1) {
626 *error_string
= talloc_asprintf(mem_ctx
, "More than one group/alias with SID: %s",
627 dom_sid_string(mem_ctx
,
628 dom_sid_add_rid(mem_ctx
,
629 state
->dom_sid
[database
],
631 return NT_STATUS_INTERNAL_DB_CORRUPTION
;
634 ret
= ldb_delete(state
->sam_ldb
, msgs
[0]->dn
);
635 if (ret
!= LDB_SUCCESS
) {
636 *error_string
= talloc_asprintf(mem_ctx
, "Failed to delete group record %s: %s",
637 ldb_dn_get_linearized(msgs
[0]->dn
),
638 ldb_errstring(state
->sam_ldb
));
639 return NT_STATUS_INTERNAL_DB_CORRUPTION
;
645 static NTSTATUS
samsync_ldb_handle_group_member(TALLOC_CTX
*mem_ctx
,
646 struct samsync_ldb_state
*state
,
647 enum netr_SamDatabaseID database
,
648 struct netr_DELTA_ENUM
*delta
,
651 uint32_t rid
= delta
->delta_id_union
.rid
;
652 struct netr_DELTA_GROUP_MEMBER
*group_member
= delta
->delta_union
.group_member
;
653 struct ldb_message
*msg
;
654 struct ldb_message
**msgs
;
656 const char *attrs
[] = { NULL
};
660 msg
= ldb_msg_new(mem_ctx
);
662 return NT_STATUS_NO_MEMORY
;
665 /* search for the group, by rid */
666 ret
= gendb_search(state
->sam_ldb
, mem_ctx
, state
->base_dn
[database
], &msgs
, attrs
,
667 "(&(objectClass=group)(objectSid=%s))",
668 ldap_encode_ndr_dom_sid(mem_ctx
, dom_sid_add_rid(mem_ctx
, state
->dom_sid
[database
], rid
)));
671 *error_string
= talloc_asprintf(mem_ctx
, "gendb_search failed: %s", ldb_errstring(state
->sam_ldb
));
672 return NT_STATUS_INTERNAL_DB_CORRUPTION
;
673 } else if (ret
== 0) {
674 return NT_STATUS_NO_SUCH_GROUP
;
675 } else if (ret
> 1) {
676 *error_string
= talloc_asprintf(mem_ctx
, "More than one group/alias with SID: %s",
677 dom_sid_string(mem_ctx
,
678 dom_sid_add_rid(mem_ctx
,
679 state
->dom_sid
[database
],
681 return NT_STATUS_INTERNAL_DB_CORRUPTION
;
683 msg
->dn
= talloc_steal(msg
, msgs
[0]->dn
);
688 for (i
=0; i
<group_member
->num_rids
; i
++) {
689 /* search for the group, by rid */
690 ret
= gendb_search(state
->sam_ldb
, mem_ctx
, state
->base_dn
[database
], &msgs
, attrs
,
691 "(&(objectClass=user)(objectSid=%s))",
692 ldap_encode_ndr_dom_sid(mem_ctx
, dom_sid_add_rid(mem_ctx
, state
->dom_sid
[database
], group_member
->rids
[i
])));
695 *error_string
= talloc_asprintf(mem_ctx
, "gendb_search failed: %s", ldb_errstring(state
->sam_ldb
));
696 return NT_STATUS_INTERNAL_DB_CORRUPTION
;
697 } else if (ret
== 0) {
698 return NT_STATUS_NO_SUCH_USER
;
699 } else if (ret
> 1) {
700 return NT_STATUS_INTERNAL_DB_CORRUPTION
;
702 str_dn
= ldb_dn_alloc_linearized(msg
, msgs
[0]->dn
);
703 NT_STATUS_HAVE_NO_MEMORY(str_dn
);
704 ret
= ldb_msg_add_string(msg
, "member", str_dn
);
705 if (ret
!= LDB_SUCCESS
) return NT_STATUS_NO_MEMORY
;
711 ret
= dsdb_replace(state
->sam_ldb
, msg
, 0);
712 if (ret
!= LDB_SUCCESS
) {
713 *error_string
= talloc_asprintf(mem_ctx
, "Failed to modify group record %s: %s",
714 ldb_dn_get_linearized(msg
->dn
),
715 ldb_errstring(state
->sam_ldb
));
716 return NT_STATUS_INTERNAL_DB_CORRUPTION
;
722 static NTSTATUS
samsync_ldb_handle_alias(TALLOC_CTX
*mem_ctx
,
723 struct samsync_ldb_state
*state
,
724 enum netr_SamDatabaseID database
,
725 struct netr_DELTA_ENUM
*delta
,
728 uint32_t rid
= delta
->delta_id_union
.rid
;
729 struct netr_DELTA_ALIAS
*alias
= delta
->delta_union
.alias
;
730 const char *container
, *obj_class
;
733 struct ldb_message
*msg
;
734 struct ldb_message
**msgs
;
737 const char *attrs
[] = { NULL
};
739 msg
= ldb_msg_new(mem_ctx
);
741 return NT_STATUS_NO_MEMORY
;
744 /* search for the alias, by rid */
745 ret
= gendb_search(state
->sam_ldb
, mem_ctx
, state
->base_dn
[database
], &msgs
, attrs
,
746 "(&(objectClass=group)(objectSid=%s))",
747 ldap_encode_ndr_dom_sid(mem_ctx
, dom_sid_add_rid(mem_ctx
, state
->dom_sid
[database
], rid
)));
750 *error_string
= talloc_asprintf(mem_ctx
, "gendb_search failed: %s", ldb_errstring(state
->sam_ldb
));
751 return NT_STATUS_INTERNAL_DB_CORRUPTION
;
752 } else if (ret
== 0) {
754 } else if (ret
> 1) {
755 *error_string
= talloc_asprintf(mem_ctx
, "More than one group/alias with SID: %s",
756 dom_sid_string(mem_ctx
,
757 dom_sid_add_rid(mem_ctx
,
758 state
->dom_sid
[database
],
760 return NT_STATUS_INTERNAL_DB_CORRUPTION
;
762 msg
->dn
= talloc_steal(mem_ctx
, msgs
[0]->dn
);
765 cn_name
= alias
->alias_name
.string
;
767 #define ADD_OR_DEL(type, attrib, field) do { \
768 if (alias->field) { \
769 samdb_msg_add_ ## type(state->sam_ldb, mem_ctx, msg, \
770 attrib, alias->field); \
772 samdb_msg_add_delete(state->sam_ldb, mem_ctx, msg, \
777 ADD_OR_DEL(string
, "samAccountName", alias_name
.string
);
779 if (samdb_msg_add_dom_sid(state
->sam_ldb
, mem_ctx
, msg
,
780 "objectSid", dom_sid_add_rid(mem_ctx
, state
->dom_sid
[database
], rid
))) {
781 return NT_STATUS_NO_MEMORY
;
784 ADD_OR_DEL(string
, "description", description
.string
);
788 samdb_msg_add_uint(state
->sam_ldb
, mem_ctx
, msg
, "groupType", 0x80000004);
794 samdb_msg_add_string(state
->sam_ldb
, mem_ctx
, msg
,
795 "objectClass", obj_class
);
796 msg
->dn
= ldb_dn_copy(mem_ctx
, state
->base_dn
[database
]);
797 ldb_dn_add_child_fmt(msg
->dn
, "CN=%s,CN=%s", cn_name
, container
);
799 return NT_STATUS_NO_MEMORY
;
802 ret
= ldb_add(state
->sam_ldb
, msg
);
803 if (ret
!= LDB_SUCCESS
) {
804 *error_string
= talloc_asprintf(mem_ctx
, "Failed to create alias record %s: %s",
805 ldb_dn_get_linearized(msg
->dn
),
806 ldb_errstring(state
->sam_ldb
));
807 return NT_STATUS_INTERNAL_DB_CORRUPTION
;
810 ret
= dsdb_replace(state
->sam_ldb
, msg
, 0);
811 if (ret
!= LDB_SUCCESS
) {
812 *error_string
= talloc_asprintf(mem_ctx
, "Failed to modify alias record %s: %s",
813 ldb_dn_get_linearized(msg
->dn
),
814 ldb_errstring(state
->sam_ldb
));
815 return NT_STATUS_INTERNAL_DB_CORRUPTION
;
822 static NTSTATUS
samsync_ldb_delete_alias(TALLOC_CTX
*mem_ctx
,
823 struct samsync_ldb_state
*state
,
824 enum netr_SamDatabaseID database
,
825 struct netr_DELTA_ENUM
*delta
,
828 uint32_t rid
= delta
->delta_id_union
.rid
;
829 struct ldb_message
**msgs
;
831 const char *attrs
[] = { NULL
};
833 /* search for the alias, by rid */
834 ret
= gendb_search(state
->sam_ldb
, mem_ctx
, state
->base_dn
[database
], &msgs
, attrs
,
835 "(&(objectClass=group)(objectSid=%s))",
836 ldap_encode_ndr_dom_sid(mem_ctx
, dom_sid_add_rid(mem_ctx
, state
->dom_sid
[database
], rid
)));
839 *error_string
= talloc_asprintf(mem_ctx
, "gendb_search failed: %s", ldb_errstring(state
->sam_ldb
));
840 return NT_STATUS_INTERNAL_DB_CORRUPTION
;
841 } else if (ret
== 0) {
842 return NT_STATUS_NO_SUCH_ALIAS
;
843 } else if (ret
> 1) {
844 return NT_STATUS_INTERNAL_DB_CORRUPTION
;
847 ret
= ldb_delete(state
->sam_ldb
, msgs
[0]->dn
);
848 if (ret
!= LDB_SUCCESS
) {
849 *error_string
= talloc_asprintf(mem_ctx
, "Failed to delete alias record %s: %s",
850 ldb_dn_get_linearized(msgs
[0]->dn
),
851 ldb_errstring(state
->sam_ldb
));
852 return NT_STATUS_INTERNAL_DB_CORRUPTION
;
858 static NTSTATUS
samsync_ldb_handle_alias_member(TALLOC_CTX
*mem_ctx
,
859 struct samsync_ldb_state
*state
,
860 enum netr_SamDatabaseID database
,
861 struct netr_DELTA_ENUM
*delta
,
864 uint32_t rid
= delta
->delta_id_union
.rid
;
865 struct netr_DELTA_ALIAS_MEMBER
*alias_member
= delta
->delta_union
.alias_member
;
866 struct ldb_message
*msg
;
867 struct ldb_message
**msgs
;
869 const char *attrs
[] = { NULL
};
872 msg
= ldb_msg_new(mem_ctx
);
874 return NT_STATUS_NO_MEMORY
;
877 /* search for the alias, by rid */
878 ret
= gendb_search(state
->sam_ldb
, mem_ctx
, state
->base_dn
[database
], &msgs
, attrs
,
879 "(&(objectClass=group)(objectSid=%s))",
880 ldap_encode_ndr_dom_sid(mem_ctx
, dom_sid_add_rid(mem_ctx
, state
->dom_sid
[database
], rid
)));
883 *error_string
= talloc_asprintf(mem_ctx
, "gendb_search failed: %s", ldb_errstring(state
->sam_ldb
));
884 return NT_STATUS_INTERNAL_DB_CORRUPTION
;
885 } else if (ret
== 0) {
886 return NT_STATUS_NO_SUCH_GROUP
;
887 } else if (ret
> 1) {
888 *error_string
= talloc_asprintf(mem_ctx
, "More than one group/alias with SID: %s",
889 dom_sid_string(mem_ctx
,
890 dom_sid_add_rid(mem_ctx
,
891 state
->dom_sid
[database
],
893 return NT_STATUS_INTERNAL_DB_CORRUPTION
;
895 msg
->dn
= talloc_steal(msg
, msgs
[0]->dn
);
900 for (i
=0; i
<alias_member
->sids
.num_sids
; i
++) {
901 struct ldb_dn
*alias_member_dn
;
903 /* search for members, in the top basedn (normal users are builtin aliases) */
904 ret
= gendb_search(state
->sam_ldb
, mem_ctx
, state
->base_dn
[SAM_DATABASE_DOMAIN
], &msgs
, attrs
,
906 ldap_encode_ndr_dom_sid(mem_ctx
, alias_member
->sids
.sids
[i
].sid
));
909 *error_string
= talloc_asprintf(mem_ctx
, "gendb_search failed: %s", ldb_errstring(state
->sam_ldb
));
910 return NT_STATUS_INTERNAL_DB_CORRUPTION
;
911 } else if (ret
== 0) {
913 nt_status
= samsync_ldb_add_foreignSecurityPrincipal(mem_ctx
, state
,
914 alias_member
->sids
.sids
[i
].sid
,
917 if (!NT_STATUS_IS_OK(nt_status
)) {
920 } else if (ret
> 1) {
921 return NT_STATUS_INTERNAL_DB_CORRUPTION
;
923 alias_member_dn
= msgs
[0]->dn
;
925 str_dn
= ldb_dn_alloc_linearized(msg
, alias_member_dn
);
926 NT_STATUS_HAVE_NO_MEMORY(str_dn
);
927 ret
= ldb_msg_add_string(msg
, "member", str_dn
);
928 if (ret
!= LDB_SUCCESS
) return NT_STATUS_NO_MEMORY
;
933 ret
= dsdb_replace(state
->sam_ldb
, msg
, 0);
934 if (ret
!= LDB_SUCCESS
) {
935 *error_string
= talloc_asprintf(mem_ctx
, "Failed to modify group record %s: %s",
936 ldb_dn_get_linearized(msg
->dn
),
937 ldb_errstring(state
->sam_ldb
));
938 return NT_STATUS_INTERNAL_DB_CORRUPTION
;
944 static NTSTATUS
samsync_ldb_handle_account(TALLOC_CTX
*mem_ctx
,
945 struct samsync_ldb_state
*state
,
946 enum netr_SamDatabaseID database
,
947 struct netr_DELTA_ENUM
*delta
,
950 struct dom_sid
*sid
= delta
->delta_id_union
.sid
;
951 struct netr_DELTA_ACCOUNT
*account
= delta
->delta_union
.account
;
953 struct ldb_message
*msg
;
956 char *dnstr
, *sidstr
;
958 msg
= ldb_msg_new(mem_ctx
);
960 return NT_STATUS_NO_MEMORY
;
963 sidstr
= dom_sid_string(msg
, sid
);
964 NT_STATUS_HAVE_NO_MEMORY_AND_FREE(sidstr
, msg
);
966 dnstr
= talloc_asprintf(msg
, "sid=%s", sidstr
);
967 NT_STATUS_HAVE_NO_MEMORY_AND_FREE(dnstr
, msg
);
969 msg
->dn
= ldb_dn_new(msg
, state
->pdb
, dnstr
);
970 NT_STATUS_HAVE_NO_MEMORY_AND_FREE(msg
->dn
, msg
);
972 for (i
=0; i
< account
->privilege_entries
; i
++) {
973 samdb_msg_add_string(state
->pdb
, mem_ctx
, msg
, "privilege",
974 account
->privilege_name
[i
].string
);
977 ret
= dsdb_replace(state
->pdb
, msg
, 0);
978 if (ret
== LDB_ERR_NO_SUCH_OBJECT
) {
979 if (samdb_msg_add_dom_sid(state
->pdb
, msg
, msg
, "objectSid", sid
) != LDB_SUCCESS
) {
981 return NT_STATUS_NO_MEMORY
;
983 samdb_msg_add_string(state
->pdb
, msg
, msg
, "comment", "added via samsync");
984 ret
= ldb_add(state
->pdb
, msg
);
987 if (ret
!= LDB_SUCCESS
) {
988 *error_string
= talloc_asprintf(mem_ctx
, "Failed to modify privilege record %s",
989 ldb_dn_get_linearized(msg
->dn
));
990 return NT_STATUS_INTERNAL_DB_CORRUPTION
;
996 static NTSTATUS
samsync_ldb_delete_account(TALLOC_CTX
*mem_ctx
,
997 struct samsync_ldb_state
*state
,
998 enum netr_SamDatabaseID database
,
999 struct netr_DELTA_ENUM
*delta
,
1000 char **error_string
)
1002 struct dom_sid
*sid
= delta
->delta_id_union
.sid
;
1004 struct ldb_message
*msg
;
1005 struct ldb_message
**msgs
;
1007 const char *attrs
[] = { NULL
};
1009 msg
= ldb_msg_new(mem_ctx
);
1011 return NT_STATUS_NO_MEMORY
;
1014 /* search for the account, by sid, in the top basedn */
1015 ret
= gendb_search(state
->sam_ldb
, mem_ctx
, state
->base_dn
[SAM_DATABASE_DOMAIN
], &msgs
, attrs
,
1017 ldap_encode_ndr_dom_sid(mem_ctx
, sid
));
1020 *error_string
= talloc_asprintf(mem_ctx
, "gendb_search failed: %s", ldb_errstring(state
->sam_ldb
));
1021 return NT_STATUS_INTERNAL_DB_CORRUPTION
;
1022 } else if (ret
== 0) {
1023 return NT_STATUS_NO_SUCH_USER
;
1024 } else if (ret
> 1) {
1025 *error_string
= talloc_asprintf(mem_ctx
, "More than one account with SID: %s",
1026 dom_sid_string(mem_ctx
, sid
));
1027 return NT_STATUS_INTERNAL_DB_CORRUPTION
;
1029 msg
->dn
= talloc_steal(msg
, msgs
[0]->dn
);
1032 samdb_msg_add_delete(state
->sam_ldb
, mem_ctx
, msg
,
1035 ret
= dsdb_replace(state
->sam_ldb
, msg
, 0);
1036 if (ret
!= LDB_SUCCESS
) {
1037 *error_string
= talloc_asprintf(mem_ctx
, "Failed to modify privilege record %s",
1038 ldb_dn_get_linearized(msg
->dn
));
1039 return NT_STATUS_INTERNAL_DB_CORRUPTION
;
1042 return NT_STATUS_OK
;
1045 static NTSTATUS
libnet_samsync_ldb_fn(TALLOC_CTX
*mem_ctx
,
1047 enum netr_SamDatabaseID database
,
1048 struct netr_DELTA_ENUM
*delta
,
1049 char **error_string
)
1051 NTSTATUS nt_status
= NT_STATUS_OK
;
1052 struct samsync_ldb_state
*state
= talloc_get_type(private_data
, struct samsync_ldb_state
);
1054 *error_string
= NULL
;
1055 switch (delta
->delta_type
) {
1056 case NETR_DELTA_DOMAIN
:
1058 nt_status
= samsync_ldb_handle_domain(mem_ctx
,
1065 case NETR_DELTA_USER
:
1067 nt_status
= samsync_ldb_handle_user(mem_ctx
,
1074 case NETR_DELTA_DELETE_USER
:
1076 nt_status
= samsync_ldb_delete_user(mem_ctx
,
1083 case NETR_DELTA_GROUP
:
1085 nt_status
= samsync_ldb_handle_group(mem_ctx
,
1092 case NETR_DELTA_DELETE_GROUP
:
1094 nt_status
= samsync_ldb_delete_group(mem_ctx
,
1101 case NETR_DELTA_GROUP_MEMBER
:
1103 nt_status
= samsync_ldb_handle_group_member(mem_ctx
,
1110 case NETR_DELTA_ALIAS
:
1112 nt_status
= samsync_ldb_handle_alias(mem_ctx
,
1119 case NETR_DELTA_DELETE_ALIAS
:
1121 nt_status
= samsync_ldb_delete_alias(mem_ctx
,
1128 case NETR_DELTA_ALIAS_MEMBER
:
1130 nt_status
= samsync_ldb_handle_alias_member(mem_ctx
,
1137 case NETR_DELTA_ACCOUNT
:
1139 nt_status
= samsync_ldb_handle_account(mem_ctx
,
1146 case NETR_DELTA_DELETE_ACCOUNT
:
1148 nt_status
= samsync_ldb_delete_account(mem_ctx
,
1156 /* Can't dump them all right now */
1159 if (!NT_STATUS_IS_OK(nt_status
) && !*error_string
) {
1160 *error_string
= talloc_asprintf(mem_ctx
, "Failed to handle samsync delta: %s", nt_errstr(nt_status
));
1165 static NTSTATUS
libnet_samsync_ldb_init(TALLOC_CTX
*mem_ctx
,
1167 struct libnet_SamSync_state
*samsync_state
,
1168 char **error_string
)
1170 struct samsync_ldb_state
*state
= talloc_get_type(private_data
, struct samsync_ldb_state
);
1171 const char *server
= dcerpc_server_name(samsync_state
->netlogon_pipe
);
1174 state
->samsync_state
= samsync_state
;
1176 ZERO_STRUCT(state
->dom_sid
);
1177 if (state
->samsync_state
->domain_sid
) {
1178 state
->dom_sid
[SAM_DATABASE_DOMAIN
] = dom_sid_dup(state
, state
->samsync_state
->domain_sid
);
1181 state
->dom_sid
[SAM_DATABASE_BUILTIN
] = dom_sid_parse_talloc(state
, SID_BUILTIN
);
1183 if (state
->samsync_state
->realm
) {
1184 if (!server
|| !*server
) {
1185 /* huh? how do we not have a server name? */
1186 *error_string
= talloc_strdup(mem_ctx
, "No DCE/RPC server name available. How did we connect?");
1187 return NT_STATUS_INVALID_PARAMETER
;
1189 ldap_url
= talloc_asprintf(state
, "ldap://%s", server
);
1191 state
->remote_ldb
= ldb_wrap_connect(mem_ctx
,
1192 state
->samsync_state
->machine_net_ctx
->event_ctx
,
1193 state
->samsync_state
->machine_net_ctx
->lp_ctx
,
1195 NULL
, state
->samsync_state
->machine_net_ctx
->cred
,
1197 if (!state
->remote_ldb
) {
1198 *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
);
1199 return NT_STATUS_NO_LOGON_SERVERS
;
1202 state
->remote_ldb
= NULL
;
1204 return NT_STATUS_OK
;
1207 NTSTATUS
libnet_samsync_ldb(struct libnet_context
*ctx
, TALLOC_CTX
*mem_ctx
, struct libnet_samsync_ldb
*r
)
1210 struct libnet_SamSync r2
;
1211 struct samsync_ldb_state
*state
= talloc(mem_ctx
, struct samsync_ldb_state
);
1214 return NT_STATUS_NO_MEMORY
;
1217 state
->secrets
= NULL
;
1218 state
->trusted_domains
= NULL
;
1220 state
->sam_ldb
= samdb_connect(mem_ctx
,
1225 if (!state
->sam_ldb
) {
1226 return NT_STATUS_INTERNAL_DB_ERROR
;
1229 state
->pdb
= privilege_connect(mem_ctx
,
1233 return NT_STATUS_INTERNAL_DB_ERROR
;
1236 r2
.out
.error_string
= NULL
;
1237 r2
.in
.binding_string
= r
->in
.binding_string
;
1238 r2
.in
.init_fn
= libnet_samsync_ldb_init
;
1239 r2
.in
.delta_fn
= libnet_samsync_ldb_fn
;
1240 r2
.in
.fn_ctx
= state
;
1241 r2
.in
.machine_account
= NULL
; /* TODO: Create a machine account, fill this in, and the delete it */
1242 nt_status
= libnet_SamSync_netlogon(ctx
, state
, &r2
);
1243 r
->out
.error_string
= r2
.out
.error_string
;
1244 talloc_steal(mem_ctx
, r
->out
.error_string
);
1246 if (!NT_STATUS_IS_OK(nt_status
)) {