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 /* This wrapper is needed for the "ADD_OR_DEL" macros */
61 static int samdb_msg_add_string(struct ldb_context
*sam_ldb
,
62 TALLOC_CTX
*mem_ctx
, struct ldb_message
*msg
,
63 const char *attr_name
, const char *str
)
65 return ldb_msg_add_string(msg
, attr_name
, str
);
68 static NTSTATUS
samsync_ldb_add_foreignSecurityPrincipal(TALLOC_CTX
*mem_ctx
,
69 struct samsync_ldb_state
*state
,
71 struct ldb_dn
**fsp_dn
,
74 const char *sidstr
= dom_sid_string(mem_ctx
, sid
);
75 /* We assume that ForeignSecurityPrincipals are under the BASEDN of the main domain */
76 struct ldb_dn
*basedn
= samdb_search_dn(state
->sam_ldb
, mem_ctx
,
77 state
->base_dn
[SAM_DATABASE_DOMAIN
],
78 "(&(objectClass=container)(cn=ForeignSecurityPrincipals))");
79 struct ldb_message
*msg
;
83 return NT_STATUS_NO_MEMORY
;
87 *error_string
= talloc_asprintf(mem_ctx
,
88 "Failed to find DN for "
89 "ForeignSecurityPrincipal container under %s",
90 ldb_dn_get_linearized(state
->base_dn
[SAM_DATABASE_DOMAIN
]));
91 return NT_STATUS_INTERNAL_DB_CORRUPTION
;
94 msg
= ldb_msg_new(mem_ctx
);
96 return NT_STATUS_NO_MEMORY
;
99 /* add core elements to the ldb_message for the alias */
101 if ( ! ldb_dn_add_child_fmt(msg
->dn
, "CN=%s", sidstr
))
102 return NT_STATUS_UNSUCCESSFUL
;
104 ldb_msg_add_string(msg
, "objectClass", "foreignSecurityPrincipal");
108 /* create the alias */
109 ret
= ldb_add(state
->sam_ldb
, msg
);
110 if (ret
!= LDB_SUCCESS
) {
111 *error_string
= talloc_asprintf(mem_ctx
, "Failed to create foreignSecurityPrincipal "
113 ldb_dn_get_linearized(msg
->dn
),
114 ldb_errstring(state
->sam_ldb
));
115 return NT_STATUS_INTERNAL_DB_CORRUPTION
;
120 static NTSTATUS
samsync_ldb_handle_domain(TALLOC_CTX
*mem_ctx
,
121 struct samsync_ldb_state
*state
,
122 enum netr_SamDatabaseID database
,
123 struct netr_DELTA_ENUM
*delta
,
126 struct netr_DELTA_DOMAIN
*domain
= delta
->delta_union
.domain
;
127 const char *domain_name
= domain
->domain_name
.string
;
128 struct ldb_message
*msg
;
131 msg
= ldb_msg_new(mem_ctx
);
133 return NT_STATUS_NO_MEMORY
;
136 if (database
== SAM_DATABASE_DOMAIN
) {
137 struct ldb_dn
*partitions_basedn
;
138 const char *domain_attrs
[] = {"nETBIOSName", "nCName", NULL
};
139 struct ldb_message
**msgs_domain
;
142 partitions_basedn
= samdb_partitions_dn(state
->sam_ldb
, mem_ctx
);
144 ret_domain
= gendb_search(state
->sam_ldb
, mem_ctx
, partitions_basedn
, &msgs_domain
, domain_attrs
,
145 "(&(&(nETBIOSName=%s)(objectclass=crossRef))(ncName=*))",
147 if (ret_domain
== -1) {
148 *error_string
= talloc_asprintf(mem_ctx
, "gendb_search for domain failed: %s", ldb_errstring(state
->sam_ldb
));
149 return NT_STATUS_INTERNAL_DB_CORRUPTION
;
152 if (ret_domain
!= 1) {
153 *error_string
= talloc_asprintf(mem_ctx
, "Failed to find existing domain record for %s: %d results", domain_name
,
155 return NT_STATUS_NO_SUCH_DOMAIN
;
158 state
->base_dn
[database
] = samdb_result_dn(state
->sam_ldb
, state
, msgs_domain
[0], "nCName", NULL
);
160 if (state
->dom_sid
[database
]) {
161 /* Update the domain sid with the incoming
162 * domain (found on LSA pipe, database sid may
164 ret
= samdb_msg_add_dom_sid(state
->sam_ldb
,
168 state
->dom_sid
[database
]);
169 if (ret
!= LDB_SUCCESS
) {
170 return NT_STATUS_INTERNAL_ERROR
;
173 /* Well, we will have to use the one from the database */
174 state
->dom_sid
[database
] = samdb_search_dom_sid(state
->sam_ldb
, state
,
175 state
->base_dn
[database
],
177 if (state
->dom_sid
[database
] == NULL
) {
178 return NT_STATUS_INTERNAL_ERROR
;
182 if (state
->samsync_state
->domain_guid
) {
185 status
= GUID_to_ndr_blob(state
->samsync_state
->domain_guid
, msg
, &v
);
186 if (!NT_STATUS_IS_OK(status
)) {
187 *error_string
= talloc_asprintf(mem_ctx
, "ndr_push of domain GUID failed!");
191 ret
= ldb_msg_add_value(msg
, "objectGUID", &v
, NULL
);
192 if (ret
!= LDB_SUCCESS
) {
193 return NT_STATUS_INTERNAL_ERROR
;
196 } else if (database
== SAM_DATABASE_BUILTIN
) {
197 /* work out the builtin_dn - useful for so many calls its worth
199 const char *dnstring
= samdb_search_string(state
->sam_ldb
, mem_ctx
, NULL
,
200 "distinguishedName", "objectClass=builtinDomain");
201 state
->base_dn
[database
] = ldb_dn_new(state
, state
->sam_ldb
, dnstring
);
202 if ( ! ldb_dn_validate(state
->base_dn
[database
])) {
203 return NT_STATUS_INTERNAL_ERROR
;
207 return NT_STATUS_INVALID_PARAMETER
;
210 msg
->dn
= talloc_reference(mem_ctx
, state
->base_dn
[database
]);
212 return NT_STATUS_NO_MEMORY
;
215 ldb_msg_add_string(msg
, "oEMInformation",
216 domain
->oem_information
.string
);
218 samdb_msg_add_int64(state
->sam_ldb
, mem_ctx
,
219 msg
, "forceLogoff", domain
->force_logoff_time
);
221 samdb_msg_add_uint(state
->sam_ldb
, mem_ctx
,
222 msg
, "minPwdLen", domain
->min_password_length
);
224 samdb_msg_add_int64(state
->sam_ldb
, mem_ctx
,
225 msg
, "maxPwdAge", domain
->max_password_age
);
227 samdb_msg_add_int64(state
->sam_ldb
, mem_ctx
,
228 msg
, "minPwdAge", domain
->min_password_age
);
230 samdb_msg_add_uint(state
->sam_ldb
, mem_ctx
,
231 msg
, "pwdHistoryLength", domain
->password_history_length
);
233 samdb_msg_add_uint64(state
->sam_ldb
, mem_ctx
,
234 msg
, "modifiedCount",
235 domain
->sequence_num
);
237 samdb_msg_add_uint64(state
->sam_ldb
, mem_ctx
,
238 msg
, "creationTime", domain
->domain_create_time
);
240 /* TODO: Account lockout, password properties */
242 ret
= dsdb_replace(state
->sam_ldb
, msg
, 0);
243 if (ret
!= LDB_SUCCESS
) {
244 *error_string
= talloc_asprintf(mem_ctx
,
245 "Failed to modify domain record %s: %s",
246 ldb_dn_get_linearized(msg
->dn
),
247 ldb_errstring(state
->sam_ldb
));
248 return NT_STATUS_INTERNAL_DB_CORRUPTION
;
253 static NTSTATUS
samsync_ldb_handle_user(TALLOC_CTX
*mem_ctx
,
254 struct samsync_ldb_state
*state
,
255 enum netr_SamDatabaseID database
,
256 struct netr_DELTA_ENUM
*delta
,
259 uint32_t rid
= delta
->delta_id_union
.rid
;
260 struct netr_DELTA_USER
*user
= delta
->delta_union
.user
;
261 const char *container
, *obj_class
;
264 const struct dom_sid
*user_sid
;
265 struct ldb_message
*msg
;
266 struct ldb_message
**msgs
;
267 struct ldb_message
**remote_msgs
= NULL
;
272 const char *attrs
[] = { NULL
};
273 /* we may change this to a global search, then fill in only the things not in ldap later */
274 const char *remote_attrs
[] = { "userPrincipalName", "servicePrincipalName",
275 "msDS-KeyVersionNumber", "objectGUID", NULL
};
277 user_sid
= dom_sid_add_rid(mem_ctx
, state
->dom_sid
[database
], rid
);
279 return NT_STATUS_NO_MEMORY
;
282 msg
= ldb_msg_new(mem_ctx
);
284 return NT_STATUS_NO_MEMORY
;
288 /* search for the user, by rid */
289 ret
= gendb_search(state
->sam_ldb
, mem_ctx
, state
->base_dn
[database
],
290 &msgs
, attrs
, "(&(objectClass=user)(objectSid=%s))",
291 ldap_encode_ndr_dom_sid(mem_ctx
, user_sid
));
294 *error_string
= talloc_asprintf(mem_ctx
, "LDB for user %s failed: %s",
295 dom_sid_string(mem_ctx
, user_sid
),
296 ldb_errstring(state
->sam_ldb
));
297 return NT_STATUS_INTERNAL_DB_CORRUPTION
;
298 } else if (ret
== 0) {
300 } else if (ret
> 1) {
301 *error_string
= talloc_asprintf(mem_ctx
, "More than one user with SID: %s in local LDB",
302 dom_sid_string(mem_ctx
, user_sid
));
303 return NT_STATUS_INTERNAL_DB_CORRUPTION
;
305 msg
->dn
= msgs
[0]->dn
;
306 talloc_steal(msg
, msgs
[0]->dn
);
309 /* and do the same on the remote database */
310 if (state
->remote_ldb
) {
311 ret
= gendb_search(state
->remote_ldb
, mem_ctx
, state
->base_dn
[database
],
312 &remote_msgs
, remote_attrs
, "(&(objectClass=user)(objectSid=%s))",
313 ldap_encode_ndr_dom_sid(mem_ctx
, user_sid
));
316 *error_string
= talloc_asprintf(mem_ctx
, "remote LDAP for user %s failed: %s",
317 dom_sid_string(mem_ctx
, user_sid
),
318 ldb_errstring(state
->remote_ldb
));
319 return NT_STATUS_INTERNAL_DB_CORRUPTION
;
320 } else if (ret
== 0) {
321 *error_string
= talloc_asprintf(mem_ctx
, "User exists in samsync but not in remote LDAP domain! (base: %s, SID: %s)",
322 ldb_dn_get_linearized(state
->base_dn
[database
]),
323 dom_sid_string(mem_ctx
, user_sid
));
324 return NT_STATUS_NO_SUCH_USER
;
325 } else if (ret
> 1) {
326 *error_string
= talloc_asprintf(mem_ctx
, "More than one user in remote LDAP domain with SID: %s",
327 dom_sid_string(mem_ctx
, user_sid
));
328 return NT_STATUS_INTERNAL_DB_CORRUPTION
;
330 /* Try to put things in the same location as the remote server */
332 msg
->dn
= talloc_steal(msg
, remote_msgs
[0]->dn
);
336 cn_name
= talloc_strdup(mem_ctx
, user
->account_name
.string
);
337 NT_STATUS_HAVE_NO_MEMORY(cn_name
);
338 cn_name_len
= strlen(cn_name
);
340 #define ADD_OR_DEL(type, attrib, field) do { \
342 samdb_msg_add_ ## type(state->sam_ldb, mem_ctx, msg, \
343 attrib, user->field); \
345 samdb_msg_add_delete(state->sam_ldb, mem_ctx, msg, \
350 ADD_OR_DEL(string
, "samAccountName", account_name
.string
);
351 ADD_OR_DEL(string
, "displayName", full_name
.string
);
353 if (samdb_msg_add_dom_sid(state
->sam_ldb
, mem_ctx
, msg
,
354 "objectSid", dom_sid_add_rid(mem_ctx
, state
->dom_sid
[database
], rid
))) {
355 return NT_STATUS_NO_MEMORY
;
358 ADD_OR_DEL(uint
, "primaryGroupID", primary_gid
);
359 ADD_OR_DEL(string
, "homeDirectory", home_directory
.string
);
360 ADD_OR_DEL(string
, "homeDrive", home_drive
.string
);
361 ADD_OR_DEL(string
, "scriptPath", logon_script
.string
);
362 ADD_OR_DEL(string
, "description", description
.string
);
363 ADD_OR_DEL(string
, "userWorkstations", workstations
.string
);
365 ADD_OR_DEL(uint64
, "lastLogon", last_logon
);
366 ADD_OR_DEL(uint64
, "lastLogoff", last_logoff
);
368 if (samdb_msg_add_logon_hours(state
->sam_ldb
, mem_ctx
, msg
, "logonHours", &user
->logon_hours
) != 0) {
369 return NT_STATUS_NO_MEMORY
;
372 ADD_OR_DEL(uint
, "badPwdCount", bad_password_count
);
373 ADD_OR_DEL(uint
, "logonCount", logon_count
);
375 ADD_OR_DEL(uint64
, "pwdLastSet", last_password_change
);
376 ADD_OR_DEL(uint64
, "accountExpires", acct_expiry
);
378 if (samdb_msg_add_acct_flags(state
->sam_ldb
, mem_ctx
, msg
,
379 "userAccountControl", user
->acct_flags
) != 0) {
380 return NT_STATUS_NO_MEMORY
;
383 if (user
->lm_password_present
) {
384 samdb_msg_add_hash(state
->sam_ldb
, mem_ctx
, msg
,
385 "dBCSPwd", &user
->lmpassword
);
387 samdb_msg_add_delete(state
->sam_ldb
, mem_ctx
, msg
,
390 if (user
->nt_password_present
) {
391 samdb_msg_add_hash(state
->sam_ldb
, mem_ctx
, msg
,
392 "unicodePwd", &user
->ntpassword
);
394 samdb_msg_add_delete(state
->sam_ldb
, mem_ctx
, msg
,
398 ADD_OR_DEL(string
, "comment", comment
.string
);
400 if (samdb_msg_add_parameters(state
->sam_ldb
, mem_ctx
, msg
, "userParameters", &user
->parameters
) != 0) {
401 return NT_STATUS_NO_MEMORY
;
404 ADD_OR_DEL(uint
, "countryCode", country_code
);
405 ADD_OR_DEL(uint
, "codePage", code_page
);
407 ADD_OR_DEL(string
, "profilePath", profile_path
.string
);
411 for (i
=0; remote_attrs
[i
]; i
++) {
412 struct ldb_message_element
*el
= ldb_msg_find_element(remote_msgs
[0], remote_attrs
[i
]);
414 samdb_msg_add_delete(state
->sam_ldb
, mem_ctx
, msg
,
417 ret
= ldb_msg_add(msg
, el
, LDB_FLAG_MOD_REPLACE
);
418 if (ret
!= LDB_SUCCESS
) {
419 *error_string
= talloc_strdup(
420 mem_ctx
, "ldb_msg_add failed");
421 return NT_STATUS_NO_MEMORY
;
426 acb
= user
->acct_flags
;
427 if (acb
& (ACB_WSTRUST
)) {
428 cn_name
[cn_name_len
- 1] = '\0';
429 container
= "Computers";
430 obj_class
= "computer";
432 } else if (acb
& ACB_SVRTRUST
) {
433 if (cn_name
[cn_name_len
- 1] != '$') {
434 return NT_STATUS_FOOBAR
;
436 cn_name
[cn_name_len
- 1] = '\0';
437 container
= "Domain Controllers";
438 obj_class
= "computer";
444 ldb_msg_add_string(msg
, "objectClass", obj_class
);
446 msg
->dn
= ldb_dn_copy(mem_ctx
, state
->base_dn
[database
]);
447 ldb_dn_add_child_fmt(msg
->dn
, "CN=%s,CN=%s", cn_name
, container
);
449 return NT_STATUS_NO_MEMORY
;
453 ret
= ldb_add(state
->sam_ldb
, msg
);
454 if (ret
!= LDB_SUCCESS
) {
455 struct ldb_dn
*first_try_dn
= msg
->dn
;
456 /* Try again with the default DN */
458 *error_string
= talloc_asprintf(mem_ctx
, "Failed to create user record. Tried %s: %s",
459 ldb_dn_get_linearized(first_try_dn
),
460 ldb_errstring(state
->sam_ldb
));
461 return NT_STATUS_INTERNAL_DB_CORRUPTION
;
463 msg
->dn
= talloc_steal(msg
, remote_msgs
[0]->dn
);
464 ret
= ldb_add(state
->sam_ldb
, msg
);
465 if (ret
!= LDB_SUCCESS
) {
466 *error_string
= talloc_asprintf(mem_ctx
, "Failed to create user record. Tried both %s and %s: %s",
467 ldb_dn_get_linearized(first_try_dn
),
468 ldb_dn_get_linearized(msg
->dn
),
469 ldb_errstring(state
->sam_ldb
));
470 return NT_STATUS_INTERNAL_DB_CORRUPTION
;
475 ret
= dsdb_replace(state
->sam_ldb
, msg
, 0);
476 if (ret
!= LDB_SUCCESS
) {
477 *error_string
= talloc_asprintf(mem_ctx
, "Failed to modify user record %s: %s",
478 ldb_dn_get_linearized(msg
->dn
),
479 ldb_errstring(state
->sam_ldb
));
480 return NT_STATUS_INTERNAL_DB_CORRUPTION
;
487 static NTSTATUS
samsync_ldb_delete_user(TALLOC_CTX
*mem_ctx
,
488 struct samsync_ldb_state
*state
,
489 enum netr_SamDatabaseID database
,
490 struct netr_DELTA_ENUM
*delta
,
493 uint32_t rid
= delta
->delta_id_union
.rid
;
494 struct ldb_message
**msgs
;
496 const char *attrs
[] = { NULL
};
498 /* search for the user, by rid */
499 ret
= gendb_search(state
->sam_ldb
, mem_ctx
, state
->base_dn
[database
],
500 &msgs
, attrs
, "(&(objectClass=user)(objectSid=%s))",
501 ldap_encode_ndr_dom_sid(mem_ctx
, dom_sid_add_rid(mem_ctx
, state
->dom_sid
[database
], rid
)));
504 *error_string
= talloc_asprintf(mem_ctx
, "gendb_search failed: %s", ldb_errstring(state
->sam_ldb
));
505 return NT_STATUS_INTERNAL_DB_CORRUPTION
;
506 } else if (ret
== 0) {
507 return NT_STATUS_NO_SUCH_USER
;
508 } else if (ret
> 1) {
509 *error_string
= talloc_asprintf(mem_ctx
, "More than one user with SID: %s",
510 dom_sid_string(mem_ctx
,
511 dom_sid_add_rid(mem_ctx
,
512 state
->dom_sid
[database
],
514 return NT_STATUS_INTERNAL_DB_CORRUPTION
;
517 ret
= ldb_delete(state
->sam_ldb
, msgs
[0]->dn
);
518 if (ret
!= LDB_SUCCESS
) {
519 *error_string
= talloc_asprintf(mem_ctx
, "Failed to delete user record %s: %s",
520 ldb_dn_get_linearized(msgs
[0]->dn
),
521 ldb_errstring(state
->sam_ldb
));
522 return NT_STATUS_INTERNAL_DB_CORRUPTION
;
528 static NTSTATUS
samsync_ldb_handle_group(TALLOC_CTX
*mem_ctx
,
529 struct samsync_ldb_state
*state
,
530 enum netr_SamDatabaseID database
,
531 struct netr_DELTA_ENUM
*delta
,
534 uint32_t rid
= delta
->delta_id_union
.rid
;
535 struct netr_DELTA_GROUP
*group
= delta
->delta_union
.group
;
536 const char *container
, *obj_class
;
539 struct ldb_message
*msg
;
540 struct ldb_message
**msgs
;
543 const char *attrs
[] = { NULL
};
545 msg
= ldb_msg_new(mem_ctx
);
547 return NT_STATUS_NO_MEMORY
;
550 /* search for the group, by rid */
551 ret
= gendb_search(state
->sam_ldb
, mem_ctx
, state
->base_dn
[database
], &msgs
, attrs
,
552 "(&(objectClass=group)(objectSid=%s))",
553 ldap_encode_ndr_dom_sid(mem_ctx
, dom_sid_add_rid(mem_ctx
, state
->dom_sid
[database
], rid
)));
556 *error_string
= talloc_asprintf(mem_ctx
, "gendb_search failed: %s", ldb_errstring(state
->sam_ldb
));
557 return NT_STATUS_INTERNAL_DB_CORRUPTION
;
558 } else if (ret
== 0) {
560 } else if (ret
> 1) {
561 *error_string
= talloc_asprintf(mem_ctx
, "More than one group/alias with SID: %s",
562 dom_sid_string(mem_ctx
,
563 dom_sid_add_rid(mem_ctx
,
564 state
->dom_sid
[database
],
566 return NT_STATUS_INTERNAL_DB_CORRUPTION
;
568 msg
->dn
= talloc_steal(msg
, msgs
[0]->dn
);
571 cn_name
= group
->group_name
.string
;
573 #define ADD_OR_DEL(type, attrib, field) do { \
574 if (group->field) { \
575 samdb_msg_add_ ## type(state->sam_ldb, mem_ctx, msg, \
576 attrib, group->field); \
578 samdb_msg_add_delete(state->sam_ldb, mem_ctx, msg, \
583 ADD_OR_DEL(string
, "samAccountName", group_name
.string
);
585 if (samdb_msg_add_dom_sid(state
->sam_ldb
, mem_ctx
, msg
,
586 "objectSid", dom_sid_add_rid(mem_ctx
, state
->dom_sid
[database
], rid
))) {
587 return NT_STATUS_NO_MEMORY
;
590 ADD_OR_DEL(string
, "description", description
.string
);
598 ldb_msg_add_string(msg
, "objectClass", obj_class
);
599 msg
->dn
= ldb_dn_copy(mem_ctx
, state
->base_dn
[database
]);
600 ldb_dn_add_child_fmt(msg
->dn
, "CN=%s,CN=%s", cn_name
, container
);
602 return NT_STATUS_NO_MEMORY
;
605 ret
= ldb_add(state
->sam_ldb
, msg
);
606 if (ret
!= LDB_SUCCESS
) {
607 *error_string
= talloc_asprintf(mem_ctx
, "Failed to create group record %s: %s",
608 ldb_dn_get_linearized(msg
->dn
),
609 ldb_errstring(state
->sam_ldb
));
610 return NT_STATUS_INTERNAL_DB_CORRUPTION
;
613 ret
= dsdb_replace(state
->sam_ldb
, msg
, 0);
614 if (ret
!= LDB_SUCCESS
) {
615 *error_string
= talloc_asprintf(mem_ctx
, "Failed to modify group record %s: %s",
616 ldb_dn_get_linearized(msg
->dn
),
617 ldb_errstring(state
->sam_ldb
));
618 return NT_STATUS_INTERNAL_DB_CORRUPTION
;
625 static NTSTATUS
samsync_ldb_delete_group(TALLOC_CTX
*mem_ctx
,
626 struct samsync_ldb_state
*state
,
627 enum netr_SamDatabaseID database
,
628 struct netr_DELTA_ENUM
*delta
,
631 uint32_t rid
= delta
->delta_id_union
.rid
;
632 struct ldb_message
**msgs
;
634 const char *attrs
[] = { NULL
};
636 /* search for the group, by rid */
637 ret
= gendb_search(state
->sam_ldb
, mem_ctx
, state
->base_dn
[database
], &msgs
, attrs
,
638 "(&(objectClass=group)(objectSid=%s))",
639 ldap_encode_ndr_dom_sid(mem_ctx
, dom_sid_add_rid(mem_ctx
, state
->dom_sid
[database
], rid
)));
642 *error_string
= talloc_asprintf(mem_ctx
, "gendb_search failed: %s", ldb_errstring(state
->sam_ldb
));
643 return NT_STATUS_INTERNAL_DB_CORRUPTION
;
644 } else if (ret
== 0) {
645 return NT_STATUS_NO_SUCH_GROUP
;
646 } else if (ret
> 1) {
647 *error_string
= talloc_asprintf(mem_ctx
, "More than one group/alias with SID: %s",
648 dom_sid_string(mem_ctx
,
649 dom_sid_add_rid(mem_ctx
,
650 state
->dom_sid
[database
],
652 return NT_STATUS_INTERNAL_DB_CORRUPTION
;
655 ret
= ldb_delete(state
->sam_ldb
, msgs
[0]->dn
);
656 if (ret
!= LDB_SUCCESS
) {
657 *error_string
= talloc_asprintf(mem_ctx
, "Failed to delete group record %s: %s",
658 ldb_dn_get_linearized(msgs
[0]->dn
),
659 ldb_errstring(state
->sam_ldb
));
660 return NT_STATUS_INTERNAL_DB_CORRUPTION
;
666 static NTSTATUS
samsync_ldb_handle_group_member(TALLOC_CTX
*mem_ctx
,
667 struct samsync_ldb_state
*state
,
668 enum netr_SamDatabaseID database
,
669 struct netr_DELTA_ENUM
*delta
,
672 uint32_t rid
= delta
->delta_id_union
.rid
;
673 struct netr_DELTA_GROUP_MEMBER
*delta_group_member
= delta
->delta_union
.group_member
;
674 struct ldb_message
*msg
;
675 struct ldb_message
**msgs
;
677 const char *attrs
[] = { NULL
};
681 msg
= ldb_msg_new(mem_ctx
);
683 return NT_STATUS_NO_MEMORY
;
686 /* search for the group, by rid */
687 ret
= gendb_search(state
->sam_ldb
, mem_ctx
, state
->base_dn
[database
], &msgs
, attrs
,
688 "(&(objectClass=group)(objectSid=%s))",
689 ldap_encode_ndr_dom_sid(mem_ctx
, dom_sid_add_rid(mem_ctx
, state
->dom_sid
[database
], rid
)));
692 *error_string
= talloc_asprintf(mem_ctx
, "gendb_search failed: %s", ldb_errstring(state
->sam_ldb
));
693 return NT_STATUS_INTERNAL_DB_CORRUPTION
;
694 } else if (ret
== 0) {
695 return NT_STATUS_NO_SUCH_GROUP
;
696 } else if (ret
> 1) {
697 *error_string
= talloc_asprintf(mem_ctx
, "More than one group/alias with SID: %s",
698 dom_sid_string(mem_ctx
,
699 dom_sid_add_rid(mem_ctx
,
700 state
->dom_sid
[database
],
702 return NT_STATUS_INTERNAL_DB_CORRUPTION
;
704 msg
->dn
= talloc_steal(msg
, msgs
[0]->dn
);
709 for (i
=0; i
<delta_group_member
->num_rids
; i
++) {
710 /* search for the group, by rid */
711 ret
= gendb_search(state
->sam_ldb
, mem_ctx
, state
->base_dn
[database
], &msgs
, attrs
,
712 "(&(objectClass=user)(objectSid=%s))",
713 ldap_encode_ndr_dom_sid(mem_ctx
, dom_sid_add_rid(mem_ctx
, state
->dom_sid
[database
], delta_group_member
->rids
[i
])));
716 *error_string
= talloc_asprintf(mem_ctx
, "gendb_search failed: %s", ldb_errstring(state
->sam_ldb
));
717 return NT_STATUS_INTERNAL_DB_CORRUPTION
;
718 } else if (ret
== 0) {
719 return NT_STATUS_NO_SUCH_USER
;
720 } else if (ret
> 1) {
721 return NT_STATUS_INTERNAL_DB_CORRUPTION
;
723 str_dn
= ldb_dn_alloc_linearized(msg
, msgs
[0]->dn
);
724 NT_STATUS_HAVE_NO_MEMORY(str_dn
);
725 ret
= ldb_msg_add_string(msg
, "member", str_dn
);
726 if (ret
!= LDB_SUCCESS
) return NT_STATUS_NO_MEMORY
;
732 ret
= dsdb_replace(state
->sam_ldb
, msg
, 0);
733 if (ret
!= LDB_SUCCESS
) {
734 *error_string
= talloc_asprintf(mem_ctx
, "Failed to modify group record %s: %s",
735 ldb_dn_get_linearized(msg
->dn
),
736 ldb_errstring(state
->sam_ldb
));
737 return NT_STATUS_INTERNAL_DB_CORRUPTION
;
743 static NTSTATUS
samsync_ldb_handle_alias(TALLOC_CTX
*mem_ctx
,
744 struct samsync_ldb_state
*state
,
745 enum netr_SamDatabaseID database
,
746 struct netr_DELTA_ENUM
*delta
,
749 uint32_t rid
= delta
->delta_id_union
.rid
;
750 struct netr_DELTA_ALIAS
*alias
= delta
->delta_union
.alias
;
751 const char *container
, *obj_class
;
754 struct ldb_message
*msg
;
755 struct ldb_message
**msgs
;
758 const char *attrs
[] = { NULL
};
760 msg
= ldb_msg_new(mem_ctx
);
762 return NT_STATUS_NO_MEMORY
;
765 /* search for the alias, by rid */
766 ret
= gendb_search(state
->sam_ldb
, mem_ctx
, state
->base_dn
[database
], &msgs
, attrs
,
767 "(&(objectClass=group)(objectSid=%s))",
768 ldap_encode_ndr_dom_sid(mem_ctx
, dom_sid_add_rid(mem_ctx
, state
->dom_sid
[database
], rid
)));
771 *error_string
= talloc_asprintf(mem_ctx
, "gendb_search failed: %s", ldb_errstring(state
->sam_ldb
));
772 return NT_STATUS_INTERNAL_DB_CORRUPTION
;
773 } else if (ret
== 0) {
775 } else if (ret
> 1) {
776 *error_string
= talloc_asprintf(mem_ctx
, "More than one group/alias with SID: %s",
777 dom_sid_string(mem_ctx
,
778 dom_sid_add_rid(mem_ctx
,
779 state
->dom_sid
[database
],
781 return NT_STATUS_INTERNAL_DB_CORRUPTION
;
783 msg
->dn
= talloc_steal(mem_ctx
, msgs
[0]->dn
);
786 cn_name
= alias
->alias_name
.string
;
788 #define ADD_OR_DEL(type, attrib, field) do { \
789 if (alias->field) { \
790 samdb_msg_add_ ## type(state->sam_ldb, mem_ctx, msg, \
791 attrib, alias->field); \
793 samdb_msg_add_delete(state->sam_ldb, mem_ctx, msg, \
798 ADD_OR_DEL(string
, "samAccountName", alias_name
.string
);
800 if (samdb_msg_add_dom_sid(state
->sam_ldb
, mem_ctx
, msg
,
801 "objectSid", dom_sid_add_rid(mem_ctx
, state
->dom_sid
[database
], rid
))) {
802 return NT_STATUS_NO_MEMORY
;
805 ADD_OR_DEL(string
, "description", description
.string
);
809 samdb_msg_add_uint(state
->sam_ldb
, mem_ctx
, msg
, "groupType", 0x80000004);
815 ldb_msg_add_string(msg
, "objectClass", obj_class
);
816 msg
->dn
= ldb_dn_copy(mem_ctx
, state
->base_dn
[database
]);
817 ldb_dn_add_child_fmt(msg
->dn
, "CN=%s,CN=%s", cn_name
, container
);
819 return NT_STATUS_NO_MEMORY
;
822 ret
= ldb_add(state
->sam_ldb
, msg
);
823 if (ret
!= LDB_SUCCESS
) {
824 *error_string
= talloc_asprintf(mem_ctx
, "Failed to create alias record %s: %s",
825 ldb_dn_get_linearized(msg
->dn
),
826 ldb_errstring(state
->sam_ldb
));
827 return NT_STATUS_INTERNAL_DB_CORRUPTION
;
830 ret
= dsdb_replace(state
->sam_ldb
, msg
, 0);
831 if (ret
!= LDB_SUCCESS
) {
832 *error_string
= talloc_asprintf(mem_ctx
, "Failed to modify alias record %s: %s",
833 ldb_dn_get_linearized(msg
->dn
),
834 ldb_errstring(state
->sam_ldb
));
835 return NT_STATUS_INTERNAL_DB_CORRUPTION
;
842 static NTSTATUS
samsync_ldb_delete_alias(TALLOC_CTX
*mem_ctx
,
843 struct samsync_ldb_state
*state
,
844 enum netr_SamDatabaseID database
,
845 struct netr_DELTA_ENUM
*delta
,
848 uint32_t rid
= delta
->delta_id_union
.rid
;
849 struct ldb_message
**msgs
;
851 const char *attrs
[] = { NULL
};
853 /* search for the alias, by rid */
854 ret
= gendb_search(state
->sam_ldb
, mem_ctx
, state
->base_dn
[database
], &msgs
, attrs
,
855 "(&(objectClass=group)(objectSid=%s))",
856 ldap_encode_ndr_dom_sid(mem_ctx
, dom_sid_add_rid(mem_ctx
, state
->dom_sid
[database
], rid
)));
859 *error_string
= talloc_asprintf(mem_ctx
, "gendb_search failed: %s", ldb_errstring(state
->sam_ldb
));
860 return NT_STATUS_INTERNAL_DB_CORRUPTION
;
861 } else if (ret
== 0) {
862 return NT_STATUS_NO_SUCH_ALIAS
;
863 } else if (ret
> 1) {
864 return NT_STATUS_INTERNAL_DB_CORRUPTION
;
867 ret
= ldb_delete(state
->sam_ldb
, msgs
[0]->dn
);
868 if (ret
!= LDB_SUCCESS
) {
869 *error_string
= talloc_asprintf(mem_ctx
, "Failed to delete alias record %s: %s",
870 ldb_dn_get_linearized(msgs
[0]->dn
),
871 ldb_errstring(state
->sam_ldb
));
872 return NT_STATUS_INTERNAL_DB_CORRUPTION
;
878 static NTSTATUS
samsync_ldb_handle_alias_member(TALLOC_CTX
*mem_ctx
,
879 struct samsync_ldb_state
*state
,
880 enum netr_SamDatabaseID database
,
881 struct netr_DELTA_ENUM
*delta
,
884 uint32_t rid
= delta
->delta_id_union
.rid
;
885 struct netr_DELTA_ALIAS_MEMBER
*alias_member
= delta
->delta_union
.alias_member
;
886 struct ldb_message
*msg
;
887 struct ldb_message
**msgs
;
889 const char *attrs
[] = { NULL
};
892 msg
= ldb_msg_new(mem_ctx
);
894 return NT_STATUS_NO_MEMORY
;
897 /* search for the alias, by rid */
898 ret
= gendb_search(state
->sam_ldb
, mem_ctx
, state
->base_dn
[database
], &msgs
, attrs
,
899 "(&(objectClass=group)(objectSid=%s))",
900 ldap_encode_ndr_dom_sid(mem_ctx
, dom_sid_add_rid(mem_ctx
, state
->dom_sid
[database
], rid
)));
903 *error_string
= talloc_asprintf(mem_ctx
, "gendb_search failed: %s", ldb_errstring(state
->sam_ldb
));
904 return NT_STATUS_INTERNAL_DB_CORRUPTION
;
905 } else if (ret
== 0) {
906 return NT_STATUS_NO_SUCH_GROUP
;
907 } else if (ret
> 1) {
908 *error_string
= talloc_asprintf(mem_ctx
, "More than one group/alias with SID: %s",
909 dom_sid_string(mem_ctx
,
910 dom_sid_add_rid(mem_ctx
,
911 state
->dom_sid
[database
],
913 return NT_STATUS_INTERNAL_DB_CORRUPTION
;
915 msg
->dn
= talloc_steal(msg
, msgs
[0]->dn
);
920 for (i
=0; i
<alias_member
->sids
.num_sids
; i
++) {
921 struct ldb_dn
*alias_member_dn
;
923 /* search for members, in the top basedn (normal users are builtin aliases) */
924 ret
= gendb_search(state
->sam_ldb
, mem_ctx
, state
->base_dn
[SAM_DATABASE_DOMAIN
], &msgs
, attrs
,
926 ldap_encode_ndr_dom_sid(mem_ctx
, alias_member
->sids
.sids
[i
].sid
));
929 *error_string
= talloc_asprintf(mem_ctx
, "gendb_search failed: %s", ldb_errstring(state
->sam_ldb
));
930 return NT_STATUS_INTERNAL_DB_CORRUPTION
;
931 } else if (ret
== 0) {
933 nt_status
= samsync_ldb_add_foreignSecurityPrincipal(mem_ctx
, state
,
934 alias_member
->sids
.sids
[i
].sid
,
937 if (!NT_STATUS_IS_OK(nt_status
)) {
940 } else if (ret
> 1) {
941 return NT_STATUS_INTERNAL_DB_CORRUPTION
;
943 alias_member_dn
= msgs
[0]->dn
;
945 str_dn
= ldb_dn_alloc_linearized(msg
, alias_member_dn
);
946 NT_STATUS_HAVE_NO_MEMORY(str_dn
);
947 ret
= ldb_msg_add_string(msg
, "member", str_dn
);
948 if (ret
!= LDB_SUCCESS
) return NT_STATUS_NO_MEMORY
;
953 ret
= dsdb_replace(state
->sam_ldb
, msg
, 0);
954 if (ret
!= LDB_SUCCESS
) {
955 *error_string
= talloc_asprintf(mem_ctx
, "Failed to modify group record %s: %s",
956 ldb_dn_get_linearized(msg
->dn
),
957 ldb_errstring(state
->sam_ldb
));
958 return NT_STATUS_INTERNAL_DB_CORRUPTION
;
964 static NTSTATUS
samsync_ldb_handle_account(TALLOC_CTX
*mem_ctx
,
965 struct samsync_ldb_state
*state
,
966 enum netr_SamDatabaseID database
,
967 struct netr_DELTA_ENUM
*delta
,
970 struct dom_sid
*sid
= delta
->delta_id_union
.sid
;
971 struct netr_DELTA_ACCOUNT
*account
= delta
->delta_union
.account
;
973 struct ldb_message
*msg
;
976 char *dnstr
, *sidstr
;
978 msg
= ldb_msg_new(mem_ctx
);
980 return NT_STATUS_NO_MEMORY
;
983 sidstr
= dom_sid_string(msg
, sid
);
984 if (sidstr
== NULL
) {
986 return NT_STATUS_NO_MEMORY
;
989 dnstr
= talloc_asprintf(msg
, "sid=%s", sidstr
);
992 return NT_STATUS_NO_MEMORY
;
995 msg
->dn
= ldb_dn_new(msg
, state
->pdb
, dnstr
);
996 if (msg
->dn
== NULL
) {
998 return NT_STATUS_NO_MEMORY
;
1001 for (i
=0; i
< account
->privilege_entries
; i
++) {
1002 ldb_msg_add_string(msg
, "privilege", account
->privilege_name
[i
].string
);
1005 ret
= dsdb_replace(state
->pdb
, msg
, 0);
1006 if (ret
== LDB_ERR_NO_SUCH_OBJECT
) {
1007 if (samdb_msg_add_dom_sid(state
->pdb
, msg
, msg
, "objectSid", sid
) != LDB_SUCCESS
) {
1009 return NT_STATUS_NO_MEMORY
;
1011 ldb_msg_add_string(msg
, "comment", "added via samsync");
1012 ret
= ldb_add(state
->pdb
, msg
);
1015 if (ret
!= LDB_SUCCESS
) {
1016 *error_string
= talloc_asprintf(mem_ctx
, "Failed to modify privilege record %s",
1017 ldb_dn_get_linearized(msg
->dn
));
1018 return NT_STATUS_INTERNAL_DB_CORRUPTION
;
1021 return NT_STATUS_OK
;
1024 static NTSTATUS
samsync_ldb_delete_account(TALLOC_CTX
*mem_ctx
,
1025 struct samsync_ldb_state
*state
,
1026 enum netr_SamDatabaseID database
,
1027 struct netr_DELTA_ENUM
*delta
,
1028 char **error_string
)
1030 struct dom_sid
*sid
= delta
->delta_id_union
.sid
;
1032 struct ldb_message
*msg
;
1033 struct ldb_message
**msgs
;
1035 const char *attrs
[] = { NULL
};
1037 msg
= ldb_msg_new(mem_ctx
);
1039 return NT_STATUS_NO_MEMORY
;
1042 /* search for the account, by sid, in the top basedn */
1043 ret
= gendb_search(state
->sam_ldb
, mem_ctx
, state
->base_dn
[SAM_DATABASE_DOMAIN
], &msgs
, attrs
,
1045 ldap_encode_ndr_dom_sid(mem_ctx
, sid
));
1048 *error_string
= talloc_asprintf(mem_ctx
, "gendb_search failed: %s", ldb_errstring(state
->sam_ldb
));
1049 return NT_STATUS_INTERNAL_DB_CORRUPTION
;
1050 } else if (ret
== 0) {
1051 return NT_STATUS_NO_SUCH_USER
;
1052 } else if (ret
> 1) {
1053 *error_string
= talloc_asprintf(mem_ctx
, "More than one account with SID: %s",
1054 dom_sid_string(mem_ctx
, sid
));
1055 return NT_STATUS_INTERNAL_DB_CORRUPTION
;
1057 msg
->dn
= talloc_steal(msg
, msgs
[0]->dn
);
1060 samdb_msg_add_delete(state
->sam_ldb
, mem_ctx
, msg
,
1063 ret
= dsdb_replace(state
->sam_ldb
, msg
, 0);
1064 if (ret
!= LDB_SUCCESS
) {
1065 *error_string
= talloc_asprintf(mem_ctx
, "Failed to modify privilege record %s",
1066 ldb_dn_get_linearized(msg
->dn
));
1067 return NT_STATUS_INTERNAL_DB_CORRUPTION
;
1070 return NT_STATUS_OK
;
1073 static NTSTATUS
libnet_samsync_ldb_fn(TALLOC_CTX
*mem_ctx
,
1075 enum netr_SamDatabaseID database
,
1076 struct netr_DELTA_ENUM
*delta
,
1077 char **error_string
)
1079 NTSTATUS nt_status
= NT_STATUS_OK
;
1080 struct samsync_ldb_state
*state
= talloc_get_type(private_data
, struct samsync_ldb_state
);
1082 *error_string
= NULL
;
1083 switch (delta
->delta_type
) {
1084 case NETR_DELTA_DOMAIN
:
1086 nt_status
= samsync_ldb_handle_domain(mem_ctx
,
1093 case NETR_DELTA_USER
:
1095 nt_status
= samsync_ldb_handle_user(mem_ctx
,
1102 case NETR_DELTA_DELETE_USER
:
1104 nt_status
= samsync_ldb_delete_user(mem_ctx
,
1111 case NETR_DELTA_GROUP
:
1113 nt_status
= samsync_ldb_handle_group(mem_ctx
,
1120 case NETR_DELTA_DELETE_GROUP
:
1122 nt_status
= samsync_ldb_delete_group(mem_ctx
,
1129 case NETR_DELTA_GROUP_MEMBER
:
1131 nt_status
= samsync_ldb_handle_group_member(mem_ctx
,
1138 case NETR_DELTA_ALIAS
:
1140 nt_status
= samsync_ldb_handle_alias(mem_ctx
,
1147 case NETR_DELTA_DELETE_ALIAS
:
1149 nt_status
= samsync_ldb_delete_alias(mem_ctx
,
1156 case NETR_DELTA_ALIAS_MEMBER
:
1158 nt_status
= samsync_ldb_handle_alias_member(mem_ctx
,
1165 case NETR_DELTA_ACCOUNT
:
1167 nt_status
= samsync_ldb_handle_account(mem_ctx
,
1174 case NETR_DELTA_DELETE_ACCOUNT
:
1176 nt_status
= samsync_ldb_delete_account(mem_ctx
,
1184 /* Can't dump them all right now */
1187 if (!NT_STATUS_IS_OK(nt_status
) && !*error_string
) {
1188 *error_string
= talloc_asprintf(mem_ctx
, "Failed to handle samsync delta: %s", nt_errstr(nt_status
));
1193 static NTSTATUS
libnet_samsync_ldb_init(TALLOC_CTX
*mem_ctx
,
1195 struct libnet_SamSync_state
*samsync_state
,
1196 char **error_string
)
1198 struct samsync_ldb_state
*state
= talloc_get_type(private_data
, struct samsync_ldb_state
);
1199 const char *server
= dcerpc_server_name(samsync_state
->netlogon_pipe
);
1202 state
->samsync_state
= samsync_state
;
1204 ZERO_STRUCT(state
->dom_sid
);
1205 if (state
->samsync_state
->domain_sid
) {
1206 state
->dom_sid
[SAM_DATABASE_DOMAIN
] = dom_sid_dup(state
, state
->samsync_state
->domain_sid
);
1209 state
->dom_sid
[SAM_DATABASE_BUILTIN
] = dom_sid_parse_talloc(state
, SID_BUILTIN
);
1211 if (state
->samsync_state
->realm
) {
1212 if (!server
|| !*server
) {
1213 /* huh? how do we not have a server name? */
1214 *error_string
= talloc_strdup(mem_ctx
, "No DCE/RPC server name available. How did we connect?");
1215 return NT_STATUS_INVALID_PARAMETER
;
1217 ldap_url
= talloc_asprintf(state
, "ldap://%s", server
);
1219 state
->remote_ldb
= ldb_wrap_connect(mem_ctx
,
1221 state
->samsync_state
->machine_net_ctx
->lp_ctx
,
1223 NULL
, state
->samsync_state
->machine_net_ctx
->cred
,
1225 if (!state
->remote_ldb
) {
1226 *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
);
1227 return NT_STATUS_NO_LOGON_SERVERS
;
1230 state
->remote_ldb
= NULL
;
1232 return NT_STATUS_OK
;
1235 NTSTATUS
libnet_samsync_ldb(struct libnet_context
*ctx
, TALLOC_CTX
*mem_ctx
, struct libnet_samsync_ldb
*r
)
1238 struct libnet_SamSync r2
;
1239 struct samsync_ldb_state
*state
= talloc(mem_ctx
, struct samsync_ldb_state
);
1242 return NT_STATUS_NO_MEMORY
;
1245 state
->secrets
= NULL
;
1246 state
->trusted_domains
= NULL
;
1248 state
->sam_ldb
= samdb_connect(mem_ctx
,
1253 if (!state
->sam_ldb
) {
1254 return NT_STATUS_INTERNAL_DB_ERROR
;
1257 state
->pdb
= privilege_connect(mem_ctx
,
1260 return NT_STATUS_INTERNAL_DB_ERROR
;
1263 r2
.out
.error_string
= NULL
;
1264 r2
.in
.binding_string
= r
->in
.binding_string
;
1265 r2
.in
.init_fn
= libnet_samsync_ldb_init
;
1266 r2
.in
.delta_fn
= libnet_samsync_ldb_fn
;
1267 r2
.in
.fn_ctx
= state
;
1268 r2
.in
.machine_account
= NULL
; /* TODO: Create a machine account, fill this in, and the delete it */
1269 nt_status
= libnet_SamSync_netlogon(ctx
, state
, &r2
);
1270 r
->out
.error_string
= r2
.out
.error_string
;
1271 talloc_steal(mem_ctx
, r
->out
.error_string
);
1273 if (!NT_STATUS_IS_OK(nt_status
)) {