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 samdb_msg_add_dom_sid(state
->sam_ldb
, mem_ctx
,
165 msg
, "objectSid", state
->dom_sid
[database
]);
167 /* Well, we will have to use the one from the database */
168 state
->dom_sid
[database
] = samdb_search_dom_sid(state
->sam_ldb
, state
,
169 state
->base_dn
[database
],
173 if (state
->samsync_state
->domain_guid
) {
176 status
= GUID_to_ndr_blob(state
->samsync_state
->domain_guid
, msg
, &v
);
177 if (!NT_STATUS_IS_OK(status
)) {
178 *error_string
= talloc_asprintf(mem_ctx
, "ndr_push of domain GUID failed!");
182 ldb_msg_add_value(msg
, "objectGUID", &v
, NULL
);
184 } else if (database
== SAM_DATABASE_BUILTIN
) {
185 /* work out the builtin_dn - useful for so many calls its worth
187 const char *dnstring
= samdb_search_string(state
->sam_ldb
, mem_ctx
, NULL
,
188 "distinguishedName", "objectClass=builtinDomain");
189 state
->base_dn
[database
] = ldb_dn_new(state
, state
->sam_ldb
, dnstring
);
190 if ( ! ldb_dn_validate(state
->base_dn
[database
])) {
191 return NT_STATUS_INTERNAL_ERROR
;
195 return NT_STATUS_INVALID_PARAMETER
;
198 msg
->dn
= talloc_reference(mem_ctx
, state
->base_dn
[database
]);
200 return NT_STATUS_NO_MEMORY
;
203 ldb_msg_add_string(msg
, "oEMInformation",
204 domain
->oem_information
.string
);
206 samdb_msg_add_int64(state
->sam_ldb
, mem_ctx
,
207 msg
, "forceLogoff", domain
->force_logoff_time
);
209 samdb_msg_add_uint(state
->sam_ldb
, mem_ctx
,
210 msg
, "minPwdLen", domain
->min_password_length
);
212 samdb_msg_add_int64(state
->sam_ldb
, mem_ctx
,
213 msg
, "maxPwdAge", domain
->max_password_age
);
215 samdb_msg_add_int64(state
->sam_ldb
, mem_ctx
,
216 msg
, "minPwdAge", domain
->min_password_age
);
218 samdb_msg_add_uint(state
->sam_ldb
, mem_ctx
,
219 msg
, "pwdHistoryLength", domain
->password_history_length
);
221 samdb_msg_add_uint64(state
->sam_ldb
, mem_ctx
,
222 msg
, "modifiedCount",
223 domain
->sequence_num
);
225 samdb_msg_add_uint64(state
->sam_ldb
, mem_ctx
,
226 msg
, "creationTime", domain
->domain_create_time
);
228 /* TODO: Account lockout, password properties */
230 ret
= dsdb_replace(state
->sam_ldb
, msg
, 0);
231 if (ret
!= LDB_SUCCESS
) {
232 *error_string
= talloc_asprintf(mem_ctx
,
233 "Failed to modify domain record %s: %s",
234 ldb_dn_get_linearized(msg
->dn
),
235 ldb_errstring(state
->sam_ldb
));
236 return NT_STATUS_INTERNAL_DB_CORRUPTION
;
241 static NTSTATUS
samsync_ldb_handle_user(TALLOC_CTX
*mem_ctx
,
242 struct samsync_ldb_state
*state
,
243 enum netr_SamDatabaseID database
,
244 struct netr_DELTA_ENUM
*delta
,
247 uint32_t rid
= delta
->delta_id_union
.rid
;
248 struct netr_DELTA_USER
*user
= delta
->delta_union
.user
;
249 const char *container
, *obj_class
;
252 const struct dom_sid
*user_sid
;
253 struct ldb_message
*msg
;
254 struct ldb_message
**msgs
;
255 struct ldb_message
**remote_msgs
= NULL
;
260 const char *attrs
[] = { NULL
};
261 /* we may change this to a global search, then fill in only the things not in ldap later */
262 const char *remote_attrs
[] = { "userPrincipalName", "servicePrincipalName",
263 "msDS-KeyVersionNumber", "objectGUID", NULL
};
265 user_sid
= dom_sid_add_rid(mem_ctx
, state
->dom_sid
[database
], rid
);
267 return NT_STATUS_NO_MEMORY
;
270 msg
= ldb_msg_new(mem_ctx
);
272 return NT_STATUS_NO_MEMORY
;
276 /* search for the user, by rid */
277 ret
= gendb_search(state
->sam_ldb
, mem_ctx
, state
->base_dn
[database
],
278 &msgs
, attrs
, "(&(objectClass=user)(objectSid=%s))",
279 ldap_encode_ndr_dom_sid(mem_ctx
, user_sid
));
282 *error_string
= talloc_asprintf(mem_ctx
, "LDB for user %s failed: %s",
283 dom_sid_string(mem_ctx
, user_sid
),
284 ldb_errstring(state
->sam_ldb
));
285 return NT_STATUS_INTERNAL_DB_CORRUPTION
;
286 } else if (ret
== 0) {
288 } else if (ret
> 1) {
289 *error_string
= talloc_asprintf(mem_ctx
, "More than one user with SID: %s in local LDB",
290 dom_sid_string(mem_ctx
, user_sid
));
291 return NT_STATUS_INTERNAL_DB_CORRUPTION
;
293 msg
->dn
= msgs
[0]->dn
;
294 talloc_steal(msg
, msgs
[0]->dn
);
297 /* and do the same on the remote database */
298 if (state
->remote_ldb
) {
299 ret
= gendb_search(state
->remote_ldb
, mem_ctx
, state
->base_dn
[database
],
300 &remote_msgs
, remote_attrs
, "(&(objectClass=user)(objectSid=%s))",
301 ldap_encode_ndr_dom_sid(mem_ctx
, user_sid
));
304 *error_string
= talloc_asprintf(mem_ctx
, "remote LDAP for user %s failed: %s",
305 dom_sid_string(mem_ctx
, user_sid
),
306 ldb_errstring(state
->remote_ldb
));
307 return NT_STATUS_INTERNAL_DB_CORRUPTION
;
308 } else if (ret
== 0) {
309 *error_string
= talloc_asprintf(mem_ctx
, "User exists in samsync but not in remote LDAP domain! (base: %s, SID: %s)",
310 ldb_dn_get_linearized(state
->base_dn
[database
]),
311 dom_sid_string(mem_ctx
, user_sid
));
312 return NT_STATUS_NO_SUCH_USER
;
313 } else if (ret
> 1) {
314 *error_string
= talloc_asprintf(mem_ctx
, "More than one user in remote LDAP domain with SID: %s",
315 dom_sid_string(mem_ctx
, user_sid
));
316 return NT_STATUS_INTERNAL_DB_CORRUPTION
;
318 /* Try to put things in the same location as the remote server */
320 msg
->dn
= talloc_steal(msg
, remote_msgs
[0]->dn
);
324 cn_name
= talloc_strdup(mem_ctx
, user
->account_name
.string
);
325 NT_STATUS_HAVE_NO_MEMORY(cn_name
);
326 cn_name_len
= strlen(cn_name
);
328 #define ADD_OR_DEL(type, attrib, field) do { \
330 samdb_msg_add_ ## type(state->sam_ldb, mem_ctx, msg, \
331 attrib, user->field); \
333 samdb_msg_add_delete(state->sam_ldb, mem_ctx, msg, \
338 ADD_OR_DEL(string
, "samAccountName", account_name
.string
);
339 ADD_OR_DEL(string
, "displayName", full_name
.string
);
341 if (samdb_msg_add_dom_sid(state
->sam_ldb
, mem_ctx
, msg
,
342 "objectSid", dom_sid_add_rid(mem_ctx
, state
->dom_sid
[database
], rid
))) {
343 return NT_STATUS_NO_MEMORY
;
346 ADD_OR_DEL(uint
, "primaryGroupID", primary_gid
);
347 ADD_OR_DEL(string
, "homeDirectory", home_directory
.string
);
348 ADD_OR_DEL(string
, "homeDrive", home_drive
.string
);
349 ADD_OR_DEL(string
, "scriptPath", logon_script
.string
);
350 ADD_OR_DEL(string
, "description", description
.string
);
351 ADD_OR_DEL(string
, "userWorkstations", workstations
.string
);
353 ADD_OR_DEL(uint64
, "lastLogon", last_logon
);
354 ADD_OR_DEL(uint64
, "lastLogoff", last_logoff
);
356 if (samdb_msg_add_logon_hours(state
->sam_ldb
, mem_ctx
, msg
, "logonHours", &user
->logon_hours
) != 0) {
357 return NT_STATUS_NO_MEMORY
;
360 ADD_OR_DEL(uint
, "badPwdCount", bad_password_count
);
361 ADD_OR_DEL(uint
, "logonCount", logon_count
);
363 ADD_OR_DEL(uint64
, "pwdLastSet", last_password_change
);
364 ADD_OR_DEL(uint64
, "accountExpires", acct_expiry
);
366 if (samdb_msg_add_acct_flags(state
->sam_ldb
, mem_ctx
, msg
,
367 "userAccountControl", user
->acct_flags
) != 0) {
368 return NT_STATUS_NO_MEMORY
;
371 if (user
->lm_password_present
) {
372 samdb_msg_add_hash(state
->sam_ldb
, mem_ctx
, msg
,
373 "dBCSPwd", &user
->lmpassword
);
375 samdb_msg_add_delete(state
->sam_ldb
, mem_ctx
, msg
,
378 if (user
->nt_password_present
) {
379 samdb_msg_add_hash(state
->sam_ldb
, mem_ctx
, msg
,
380 "unicodePwd", &user
->ntpassword
);
382 samdb_msg_add_delete(state
->sam_ldb
, mem_ctx
, msg
,
386 ADD_OR_DEL(string
, "comment", comment
.string
);
388 if (samdb_msg_add_parameters(state
->sam_ldb
, mem_ctx
, msg
, "userParameters", &user
->parameters
) != 0) {
389 return NT_STATUS_NO_MEMORY
;
392 ADD_OR_DEL(uint
, "countryCode", country_code
);
393 ADD_OR_DEL(uint
, "codePage", code_page
);
395 ADD_OR_DEL(string
, "profilePath", profile_path
.string
);
399 for (i
=0; remote_attrs
[i
]; i
++) {
400 struct ldb_message_element
*el
= ldb_msg_find_element(remote_msgs
[0], remote_attrs
[i
]);
402 samdb_msg_add_delete(state
->sam_ldb
, mem_ctx
, msg
,
405 ldb_msg_add(msg
, el
, LDB_FLAG_MOD_REPLACE
);
409 acb
= user
->acct_flags
;
410 if (acb
& (ACB_WSTRUST
)) {
411 cn_name
[cn_name_len
- 1] = '\0';
412 container
= "Computers";
413 obj_class
= "computer";
415 } else if (acb
& ACB_SVRTRUST
) {
416 if (cn_name
[cn_name_len
- 1] != '$') {
417 return NT_STATUS_FOOBAR
;
419 cn_name
[cn_name_len
- 1] = '\0';
420 container
= "Domain Controllers";
421 obj_class
= "computer";
427 ldb_msg_add_string(msg
, "objectClass", obj_class
);
429 msg
->dn
= ldb_dn_copy(mem_ctx
, state
->base_dn
[database
]);
430 ldb_dn_add_child_fmt(msg
->dn
, "CN=%s,CN=%s", cn_name
, container
);
432 return NT_STATUS_NO_MEMORY
;
436 ret
= ldb_add(state
->sam_ldb
, msg
);
437 if (ret
!= LDB_SUCCESS
) {
438 struct ldb_dn
*first_try_dn
= msg
->dn
;
439 /* Try again with the default DN */
441 *error_string
= talloc_asprintf(mem_ctx
, "Failed to create user record. Tried %s: %s",
442 ldb_dn_get_linearized(first_try_dn
),
443 ldb_errstring(state
->sam_ldb
));
444 return NT_STATUS_INTERNAL_DB_CORRUPTION
;
446 msg
->dn
= talloc_steal(msg
, remote_msgs
[0]->dn
);
447 ret
= ldb_add(state
->sam_ldb
, msg
);
448 if (ret
!= LDB_SUCCESS
) {
449 *error_string
= talloc_asprintf(mem_ctx
, "Failed to create user record. Tried both %s and %s: %s",
450 ldb_dn_get_linearized(first_try_dn
),
451 ldb_dn_get_linearized(msg
->dn
),
452 ldb_errstring(state
->sam_ldb
));
453 return NT_STATUS_INTERNAL_DB_CORRUPTION
;
458 ret
= dsdb_replace(state
->sam_ldb
, msg
, 0);
459 if (ret
!= LDB_SUCCESS
) {
460 *error_string
= talloc_asprintf(mem_ctx
, "Failed to modify user record %s: %s",
461 ldb_dn_get_linearized(msg
->dn
),
462 ldb_errstring(state
->sam_ldb
));
463 return NT_STATUS_INTERNAL_DB_CORRUPTION
;
470 static NTSTATUS
samsync_ldb_delete_user(TALLOC_CTX
*mem_ctx
,
471 struct samsync_ldb_state
*state
,
472 enum netr_SamDatabaseID database
,
473 struct netr_DELTA_ENUM
*delta
,
476 uint32_t rid
= delta
->delta_id_union
.rid
;
477 struct ldb_message
**msgs
;
479 const char *attrs
[] = { NULL
};
481 /* search for the user, by rid */
482 ret
= gendb_search(state
->sam_ldb
, mem_ctx
, state
->base_dn
[database
],
483 &msgs
, attrs
, "(&(objectClass=user)(objectSid=%s))",
484 ldap_encode_ndr_dom_sid(mem_ctx
, dom_sid_add_rid(mem_ctx
, state
->dom_sid
[database
], rid
)));
487 *error_string
= talloc_asprintf(mem_ctx
, "gendb_search failed: %s", ldb_errstring(state
->sam_ldb
));
488 return NT_STATUS_INTERNAL_DB_CORRUPTION
;
489 } else if (ret
== 0) {
490 return NT_STATUS_NO_SUCH_USER
;
491 } else if (ret
> 1) {
492 *error_string
= talloc_asprintf(mem_ctx
, "More than one user with SID: %s",
493 dom_sid_string(mem_ctx
,
494 dom_sid_add_rid(mem_ctx
,
495 state
->dom_sid
[database
],
497 return NT_STATUS_INTERNAL_DB_CORRUPTION
;
500 ret
= ldb_delete(state
->sam_ldb
, msgs
[0]->dn
);
501 if (ret
!= LDB_SUCCESS
) {
502 *error_string
= talloc_asprintf(mem_ctx
, "Failed to delete user record %s: %s",
503 ldb_dn_get_linearized(msgs
[0]->dn
),
504 ldb_errstring(state
->sam_ldb
));
505 return NT_STATUS_INTERNAL_DB_CORRUPTION
;
511 static NTSTATUS
samsync_ldb_handle_group(TALLOC_CTX
*mem_ctx
,
512 struct samsync_ldb_state
*state
,
513 enum netr_SamDatabaseID database
,
514 struct netr_DELTA_ENUM
*delta
,
517 uint32_t rid
= delta
->delta_id_union
.rid
;
518 struct netr_DELTA_GROUP
*group
= delta
->delta_union
.group
;
519 const char *container
, *obj_class
;
522 struct ldb_message
*msg
;
523 struct ldb_message
**msgs
;
526 const char *attrs
[] = { NULL
};
528 msg
= ldb_msg_new(mem_ctx
);
530 return NT_STATUS_NO_MEMORY
;
533 /* search for the group, by rid */
534 ret
= gendb_search(state
->sam_ldb
, mem_ctx
, state
->base_dn
[database
], &msgs
, attrs
,
535 "(&(objectClass=group)(objectSid=%s))",
536 ldap_encode_ndr_dom_sid(mem_ctx
, dom_sid_add_rid(mem_ctx
, state
->dom_sid
[database
], rid
)));
539 *error_string
= talloc_asprintf(mem_ctx
, "gendb_search failed: %s", ldb_errstring(state
->sam_ldb
));
540 return NT_STATUS_INTERNAL_DB_CORRUPTION
;
541 } else if (ret
== 0) {
543 } else if (ret
> 1) {
544 *error_string
= talloc_asprintf(mem_ctx
, "More than one group/alias with SID: %s",
545 dom_sid_string(mem_ctx
,
546 dom_sid_add_rid(mem_ctx
,
547 state
->dom_sid
[database
],
549 return NT_STATUS_INTERNAL_DB_CORRUPTION
;
551 msg
->dn
= talloc_steal(msg
, msgs
[0]->dn
);
554 cn_name
= group
->group_name
.string
;
556 #define ADD_OR_DEL(type, attrib, field) do { \
557 if (group->field) { \
558 samdb_msg_add_ ## type(state->sam_ldb, mem_ctx, msg, \
559 attrib, group->field); \
561 samdb_msg_add_delete(state->sam_ldb, mem_ctx, msg, \
566 ADD_OR_DEL(string
, "samAccountName", group_name
.string
);
568 if (samdb_msg_add_dom_sid(state
->sam_ldb
, mem_ctx
, msg
,
569 "objectSid", dom_sid_add_rid(mem_ctx
, state
->dom_sid
[database
], rid
))) {
570 return NT_STATUS_NO_MEMORY
;
573 ADD_OR_DEL(string
, "description", description
.string
);
581 ldb_msg_add_string(msg
, "objectClass", obj_class
);
582 msg
->dn
= ldb_dn_copy(mem_ctx
, state
->base_dn
[database
]);
583 ldb_dn_add_child_fmt(msg
->dn
, "CN=%s,CN=%s", cn_name
, container
);
585 return NT_STATUS_NO_MEMORY
;
588 ret
= ldb_add(state
->sam_ldb
, msg
);
589 if (ret
!= LDB_SUCCESS
) {
590 *error_string
= talloc_asprintf(mem_ctx
, "Failed to create group record %s: %s",
591 ldb_dn_get_linearized(msg
->dn
),
592 ldb_errstring(state
->sam_ldb
));
593 return NT_STATUS_INTERNAL_DB_CORRUPTION
;
596 ret
= dsdb_replace(state
->sam_ldb
, msg
, 0);
597 if (ret
!= LDB_SUCCESS
) {
598 *error_string
= talloc_asprintf(mem_ctx
, "Failed to modify group record %s: %s",
599 ldb_dn_get_linearized(msg
->dn
),
600 ldb_errstring(state
->sam_ldb
));
601 return NT_STATUS_INTERNAL_DB_CORRUPTION
;
608 static NTSTATUS
samsync_ldb_delete_group(TALLOC_CTX
*mem_ctx
,
609 struct samsync_ldb_state
*state
,
610 enum netr_SamDatabaseID database
,
611 struct netr_DELTA_ENUM
*delta
,
614 uint32_t rid
= delta
->delta_id_union
.rid
;
615 struct ldb_message
**msgs
;
617 const char *attrs
[] = { NULL
};
619 /* search for the group, by rid */
620 ret
= gendb_search(state
->sam_ldb
, mem_ctx
, state
->base_dn
[database
], &msgs
, attrs
,
621 "(&(objectClass=group)(objectSid=%s))",
622 ldap_encode_ndr_dom_sid(mem_ctx
, dom_sid_add_rid(mem_ctx
, state
->dom_sid
[database
], rid
)));
625 *error_string
= talloc_asprintf(mem_ctx
, "gendb_search failed: %s", ldb_errstring(state
->sam_ldb
));
626 return NT_STATUS_INTERNAL_DB_CORRUPTION
;
627 } else if (ret
== 0) {
628 return NT_STATUS_NO_SUCH_GROUP
;
629 } else if (ret
> 1) {
630 *error_string
= talloc_asprintf(mem_ctx
, "More than one group/alias with SID: %s",
631 dom_sid_string(mem_ctx
,
632 dom_sid_add_rid(mem_ctx
,
633 state
->dom_sid
[database
],
635 return NT_STATUS_INTERNAL_DB_CORRUPTION
;
638 ret
= ldb_delete(state
->sam_ldb
, msgs
[0]->dn
);
639 if (ret
!= LDB_SUCCESS
) {
640 *error_string
= talloc_asprintf(mem_ctx
, "Failed to delete group record %s: %s",
641 ldb_dn_get_linearized(msgs
[0]->dn
),
642 ldb_errstring(state
->sam_ldb
));
643 return NT_STATUS_INTERNAL_DB_CORRUPTION
;
649 static NTSTATUS
samsync_ldb_handle_group_member(TALLOC_CTX
*mem_ctx
,
650 struct samsync_ldb_state
*state
,
651 enum netr_SamDatabaseID database
,
652 struct netr_DELTA_ENUM
*delta
,
655 uint32_t rid
= delta
->delta_id_union
.rid
;
656 struct netr_DELTA_GROUP_MEMBER
*group_member
= delta
->delta_union
.group_member
;
657 struct ldb_message
*msg
;
658 struct ldb_message
**msgs
;
660 const char *attrs
[] = { NULL
};
664 msg
= ldb_msg_new(mem_ctx
);
666 return NT_STATUS_NO_MEMORY
;
669 /* search for the group, by rid */
670 ret
= gendb_search(state
->sam_ldb
, mem_ctx
, state
->base_dn
[database
], &msgs
, attrs
,
671 "(&(objectClass=group)(objectSid=%s))",
672 ldap_encode_ndr_dom_sid(mem_ctx
, dom_sid_add_rid(mem_ctx
, state
->dom_sid
[database
], rid
)));
675 *error_string
= talloc_asprintf(mem_ctx
, "gendb_search failed: %s", ldb_errstring(state
->sam_ldb
));
676 return NT_STATUS_INTERNAL_DB_CORRUPTION
;
677 } else if (ret
== 0) {
678 return NT_STATUS_NO_SUCH_GROUP
;
679 } else if (ret
> 1) {
680 *error_string
= talloc_asprintf(mem_ctx
, "More than one group/alias with SID: %s",
681 dom_sid_string(mem_ctx
,
682 dom_sid_add_rid(mem_ctx
,
683 state
->dom_sid
[database
],
685 return NT_STATUS_INTERNAL_DB_CORRUPTION
;
687 msg
->dn
= talloc_steal(msg
, msgs
[0]->dn
);
692 for (i
=0; i
<group_member
->num_rids
; i
++) {
693 /* search for the group, by rid */
694 ret
= gendb_search(state
->sam_ldb
, mem_ctx
, state
->base_dn
[database
], &msgs
, attrs
,
695 "(&(objectClass=user)(objectSid=%s))",
696 ldap_encode_ndr_dom_sid(mem_ctx
, dom_sid_add_rid(mem_ctx
, state
->dom_sid
[database
], group_member
->rids
[i
])));
699 *error_string
= talloc_asprintf(mem_ctx
, "gendb_search failed: %s", ldb_errstring(state
->sam_ldb
));
700 return NT_STATUS_INTERNAL_DB_CORRUPTION
;
701 } else if (ret
== 0) {
702 return NT_STATUS_NO_SUCH_USER
;
703 } else if (ret
> 1) {
704 return NT_STATUS_INTERNAL_DB_CORRUPTION
;
706 str_dn
= ldb_dn_alloc_linearized(msg
, msgs
[0]->dn
);
707 NT_STATUS_HAVE_NO_MEMORY(str_dn
);
708 ret
= ldb_msg_add_string(msg
, "member", str_dn
);
709 if (ret
!= LDB_SUCCESS
) return NT_STATUS_NO_MEMORY
;
715 ret
= dsdb_replace(state
->sam_ldb
, msg
, 0);
716 if (ret
!= LDB_SUCCESS
) {
717 *error_string
= talloc_asprintf(mem_ctx
, "Failed to modify group record %s: %s",
718 ldb_dn_get_linearized(msg
->dn
),
719 ldb_errstring(state
->sam_ldb
));
720 return NT_STATUS_INTERNAL_DB_CORRUPTION
;
726 static NTSTATUS
samsync_ldb_handle_alias(TALLOC_CTX
*mem_ctx
,
727 struct samsync_ldb_state
*state
,
728 enum netr_SamDatabaseID database
,
729 struct netr_DELTA_ENUM
*delta
,
732 uint32_t rid
= delta
->delta_id_union
.rid
;
733 struct netr_DELTA_ALIAS
*alias
= delta
->delta_union
.alias
;
734 const char *container
, *obj_class
;
737 struct ldb_message
*msg
;
738 struct ldb_message
**msgs
;
741 const char *attrs
[] = { NULL
};
743 msg
= ldb_msg_new(mem_ctx
);
745 return NT_STATUS_NO_MEMORY
;
748 /* search for the alias, by rid */
749 ret
= gendb_search(state
->sam_ldb
, mem_ctx
, state
->base_dn
[database
], &msgs
, attrs
,
750 "(&(objectClass=group)(objectSid=%s))",
751 ldap_encode_ndr_dom_sid(mem_ctx
, dom_sid_add_rid(mem_ctx
, state
->dom_sid
[database
], rid
)));
754 *error_string
= talloc_asprintf(mem_ctx
, "gendb_search failed: %s", ldb_errstring(state
->sam_ldb
));
755 return NT_STATUS_INTERNAL_DB_CORRUPTION
;
756 } else if (ret
== 0) {
758 } else if (ret
> 1) {
759 *error_string
= talloc_asprintf(mem_ctx
, "More than one group/alias with SID: %s",
760 dom_sid_string(mem_ctx
,
761 dom_sid_add_rid(mem_ctx
,
762 state
->dom_sid
[database
],
764 return NT_STATUS_INTERNAL_DB_CORRUPTION
;
766 msg
->dn
= talloc_steal(mem_ctx
, msgs
[0]->dn
);
769 cn_name
= alias
->alias_name
.string
;
771 #define ADD_OR_DEL(type, attrib, field) do { \
772 if (alias->field) { \
773 samdb_msg_add_ ## type(state->sam_ldb, mem_ctx, msg, \
774 attrib, alias->field); \
776 samdb_msg_add_delete(state->sam_ldb, mem_ctx, msg, \
781 ADD_OR_DEL(string
, "samAccountName", alias_name
.string
);
783 if (samdb_msg_add_dom_sid(state
->sam_ldb
, mem_ctx
, msg
,
784 "objectSid", dom_sid_add_rid(mem_ctx
, state
->dom_sid
[database
], rid
))) {
785 return NT_STATUS_NO_MEMORY
;
788 ADD_OR_DEL(string
, "description", description
.string
);
792 samdb_msg_add_uint(state
->sam_ldb
, mem_ctx
, msg
, "groupType", 0x80000004);
798 ldb_msg_add_string(msg
, "objectClass", obj_class
);
799 msg
->dn
= ldb_dn_copy(mem_ctx
, state
->base_dn
[database
]);
800 ldb_dn_add_child_fmt(msg
->dn
, "CN=%s,CN=%s", cn_name
, container
);
802 return NT_STATUS_NO_MEMORY
;
805 ret
= ldb_add(state
->sam_ldb
, msg
);
806 if (ret
!= LDB_SUCCESS
) {
807 *error_string
= talloc_asprintf(mem_ctx
, "Failed to create alias record %s: %s",
808 ldb_dn_get_linearized(msg
->dn
),
809 ldb_errstring(state
->sam_ldb
));
810 return NT_STATUS_INTERNAL_DB_CORRUPTION
;
813 ret
= dsdb_replace(state
->sam_ldb
, msg
, 0);
814 if (ret
!= LDB_SUCCESS
) {
815 *error_string
= talloc_asprintf(mem_ctx
, "Failed to modify alias record %s: %s",
816 ldb_dn_get_linearized(msg
->dn
),
817 ldb_errstring(state
->sam_ldb
));
818 return NT_STATUS_INTERNAL_DB_CORRUPTION
;
825 static NTSTATUS
samsync_ldb_delete_alias(TALLOC_CTX
*mem_ctx
,
826 struct samsync_ldb_state
*state
,
827 enum netr_SamDatabaseID database
,
828 struct netr_DELTA_ENUM
*delta
,
831 uint32_t rid
= delta
->delta_id_union
.rid
;
832 struct ldb_message
**msgs
;
834 const char *attrs
[] = { NULL
};
836 /* search for the alias, by rid */
837 ret
= gendb_search(state
->sam_ldb
, mem_ctx
, state
->base_dn
[database
], &msgs
, attrs
,
838 "(&(objectClass=group)(objectSid=%s))",
839 ldap_encode_ndr_dom_sid(mem_ctx
, dom_sid_add_rid(mem_ctx
, state
->dom_sid
[database
], rid
)));
842 *error_string
= talloc_asprintf(mem_ctx
, "gendb_search failed: %s", ldb_errstring(state
->sam_ldb
));
843 return NT_STATUS_INTERNAL_DB_CORRUPTION
;
844 } else if (ret
== 0) {
845 return NT_STATUS_NO_SUCH_ALIAS
;
846 } else if (ret
> 1) {
847 return NT_STATUS_INTERNAL_DB_CORRUPTION
;
850 ret
= ldb_delete(state
->sam_ldb
, msgs
[0]->dn
);
851 if (ret
!= LDB_SUCCESS
) {
852 *error_string
= talloc_asprintf(mem_ctx
, "Failed to delete alias record %s: %s",
853 ldb_dn_get_linearized(msgs
[0]->dn
),
854 ldb_errstring(state
->sam_ldb
));
855 return NT_STATUS_INTERNAL_DB_CORRUPTION
;
861 static NTSTATUS
samsync_ldb_handle_alias_member(TALLOC_CTX
*mem_ctx
,
862 struct samsync_ldb_state
*state
,
863 enum netr_SamDatabaseID database
,
864 struct netr_DELTA_ENUM
*delta
,
867 uint32_t rid
= delta
->delta_id_union
.rid
;
868 struct netr_DELTA_ALIAS_MEMBER
*alias_member
= delta
->delta_union
.alias_member
;
869 struct ldb_message
*msg
;
870 struct ldb_message
**msgs
;
872 const char *attrs
[] = { NULL
};
875 msg
= ldb_msg_new(mem_ctx
);
877 return NT_STATUS_NO_MEMORY
;
880 /* search for the alias, by rid */
881 ret
= gendb_search(state
->sam_ldb
, mem_ctx
, state
->base_dn
[database
], &msgs
, attrs
,
882 "(&(objectClass=group)(objectSid=%s))",
883 ldap_encode_ndr_dom_sid(mem_ctx
, dom_sid_add_rid(mem_ctx
, state
->dom_sid
[database
], rid
)));
886 *error_string
= talloc_asprintf(mem_ctx
, "gendb_search failed: %s", ldb_errstring(state
->sam_ldb
));
887 return NT_STATUS_INTERNAL_DB_CORRUPTION
;
888 } else if (ret
== 0) {
889 return NT_STATUS_NO_SUCH_GROUP
;
890 } else if (ret
> 1) {
891 *error_string
= talloc_asprintf(mem_ctx
, "More than one group/alias with SID: %s",
892 dom_sid_string(mem_ctx
,
893 dom_sid_add_rid(mem_ctx
,
894 state
->dom_sid
[database
],
896 return NT_STATUS_INTERNAL_DB_CORRUPTION
;
898 msg
->dn
= talloc_steal(msg
, msgs
[0]->dn
);
903 for (i
=0; i
<alias_member
->sids
.num_sids
; i
++) {
904 struct ldb_dn
*alias_member_dn
;
906 /* search for members, in the top basedn (normal users are builtin aliases) */
907 ret
= gendb_search(state
->sam_ldb
, mem_ctx
, state
->base_dn
[SAM_DATABASE_DOMAIN
], &msgs
, attrs
,
909 ldap_encode_ndr_dom_sid(mem_ctx
, alias_member
->sids
.sids
[i
].sid
));
912 *error_string
= talloc_asprintf(mem_ctx
, "gendb_search failed: %s", ldb_errstring(state
->sam_ldb
));
913 return NT_STATUS_INTERNAL_DB_CORRUPTION
;
914 } else if (ret
== 0) {
916 nt_status
= samsync_ldb_add_foreignSecurityPrincipal(mem_ctx
, state
,
917 alias_member
->sids
.sids
[i
].sid
,
920 if (!NT_STATUS_IS_OK(nt_status
)) {
923 } else if (ret
> 1) {
924 return NT_STATUS_INTERNAL_DB_CORRUPTION
;
926 alias_member_dn
= msgs
[0]->dn
;
928 str_dn
= ldb_dn_alloc_linearized(msg
, alias_member_dn
);
929 NT_STATUS_HAVE_NO_MEMORY(str_dn
);
930 ret
= ldb_msg_add_string(msg
, "member", str_dn
);
931 if (ret
!= LDB_SUCCESS
) return NT_STATUS_NO_MEMORY
;
936 ret
= dsdb_replace(state
->sam_ldb
, msg
, 0);
937 if (ret
!= LDB_SUCCESS
) {
938 *error_string
= talloc_asprintf(mem_ctx
, "Failed to modify group record %s: %s",
939 ldb_dn_get_linearized(msg
->dn
),
940 ldb_errstring(state
->sam_ldb
));
941 return NT_STATUS_INTERNAL_DB_CORRUPTION
;
947 static NTSTATUS
samsync_ldb_handle_account(TALLOC_CTX
*mem_ctx
,
948 struct samsync_ldb_state
*state
,
949 enum netr_SamDatabaseID database
,
950 struct netr_DELTA_ENUM
*delta
,
953 struct dom_sid
*sid
= delta
->delta_id_union
.sid
;
954 struct netr_DELTA_ACCOUNT
*account
= delta
->delta_union
.account
;
956 struct ldb_message
*msg
;
959 char *dnstr
, *sidstr
;
961 msg
= ldb_msg_new(mem_ctx
);
963 return NT_STATUS_NO_MEMORY
;
966 sidstr
= dom_sid_string(msg
, sid
);
967 NT_STATUS_HAVE_NO_MEMORY_AND_FREE(sidstr
, msg
);
969 dnstr
= talloc_asprintf(msg
, "sid=%s", sidstr
);
970 NT_STATUS_HAVE_NO_MEMORY_AND_FREE(dnstr
, msg
);
972 msg
->dn
= ldb_dn_new(msg
, state
->pdb
, dnstr
);
973 NT_STATUS_HAVE_NO_MEMORY_AND_FREE(msg
->dn
, msg
);
975 for (i
=0; i
< account
->privilege_entries
; i
++) {
976 ldb_msg_add_string(msg
, "privilege", account
->privilege_name
[i
].string
);
979 ret
= dsdb_replace(state
->pdb
, msg
, 0);
980 if (ret
== LDB_ERR_NO_SUCH_OBJECT
) {
981 if (samdb_msg_add_dom_sid(state
->pdb
, msg
, msg
, "objectSid", sid
) != LDB_SUCCESS
) {
983 return NT_STATUS_NO_MEMORY
;
985 ldb_msg_add_string(msg
, "comment", "added via samsync");
986 ret
= ldb_add(state
->pdb
, msg
);
989 if (ret
!= LDB_SUCCESS
) {
990 *error_string
= talloc_asprintf(mem_ctx
, "Failed to modify privilege record %s",
991 ldb_dn_get_linearized(msg
->dn
));
992 return NT_STATUS_INTERNAL_DB_CORRUPTION
;
998 static NTSTATUS
samsync_ldb_delete_account(TALLOC_CTX
*mem_ctx
,
999 struct samsync_ldb_state
*state
,
1000 enum netr_SamDatabaseID database
,
1001 struct netr_DELTA_ENUM
*delta
,
1002 char **error_string
)
1004 struct dom_sid
*sid
= delta
->delta_id_union
.sid
;
1006 struct ldb_message
*msg
;
1007 struct ldb_message
**msgs
;
1009 const char *attrs
[] = { NULL
};
1011 msg
= ldb_msg_new(mem_ctx
);
1013 return NT_STATUS_NO_MEMORY
;
1016 /* search for the account, by sid, in the top basedn */
1017 ret
= gendb_search(state
->sam_ldb
, mem_ctx
, state
->base_dn
[SAM_DATABASE_DOMAIN
], &msgs
, attrs
,
1019 ldap_encode_ndr_dom_sid(mem_ctx
, sid
));
1022 *error_string
= talloc_asprintf(mem_ctx
, "gendb_search failed: %s", ldb_errstring(state
->sam_ldb
));
1023 return NT_STATUS_INTERNAL_DB_CORRUPTION
;
1024 } else if (ret
== 0) {
1025 return NT_STATUS_NO_SUCH_USER
;
1026 } else if (ret
> 1) {
1027 *error_string
= talloc_asprintf(mem_ctx
, "More than one account with SID: %s",
1028 dom_sid_string(mem_ctx
, sid
));
1029 return NT_STATUS_INTERNAL_DB_CORRUPTION
;
1031 msg
->dn
= talloc_steal(msg
, msgs
[0]->dn
);
1034 samdb_msg_add_delete(state
->sam_ldb
, mem_ctx
, msg
,
1037 ret
= dsdb_replace(state
->sam_ldb
, msg
, 0);
1038 if (ret
!= LDB_SUCCESS
) {
1039 *error_string
= talloc_asprintf(mem_ctx
, "Failed to modify privilege record %s",
1040 ldb_dn_get_linearized(msg
->dn
));
1041 return NT_STATUS_INTERNAL_DB_CORRUPTION
;
1044 return NT_STATUS_OK
;
1047 static NTSTATUS
libnet_samsync_ldb_fn(TALLOC_CTX
*mem_ctx
,
1049 enum netr_SamDatabaseID database
,
1050 struct netr_DELTA_ENUM
*delta
,
1051 char **error_string
)
1053 NTSTATUS nt_status
= NT_STATUS_OK
;
1054 struct samsync_ldb_state
*state
= talloc_get_type(private_data
, struct samsync_ldb_state
);
1056 *error_string
= NULL
;
1057 switch (delta
->delta_type
) {
1058 case NETR_DELTA_DOMAIN
:
1060 nt_status
= samsync_ldb_handle_domain(mem_ctx
,
1067 case NETR_DELTA_USER
:
1069 nt_status
= samsync_ldb_handle_user(mem_ctx
,
1076 case NETR_DELTA_DELETE_USER
:
1078 nt_status
= samsync_ldb_delete_user(mem_ctx
,
1085 case NETR_DELTA_GROUP
:
1087 nt_status
= samsync_ldb_handle_group(mem_ctx
,
1094 case NETR_DELTA_DELETE_GROUP
:
1096 nt_status
= samsync_ldb_delete_group(mem_ctx
,
1103 case NETR_DELTA_GROUP_MEMBER
:
1105 nt_status
= samsync_ldb_handle_group_member(mem_ctx
,
1112 case NETR_DELTA_ALIAS
:
1114 nt_status
= samsync_ldb_handle_alias(mem_ctx
,
1121 case NETR_DELTA_DELETE_ALIAS
:
1123 nt_status
= samsync_ldb_delete_alias(mem_ctx
,
1130 case NETR_DELTA_ALIAS_MEMBER
:
1132 nt_status
= samsync_ldb_handle_alias_member(mem_ctx
,
1139 case NETR_DELTA_ACCOUNT
:
1141 nt_status
= samsync_ldb_handle_account(mem_ctx
,
1148 case NETR_DELTA_DELETE_ACCOUNT
:
1150 nt_status
= samsync_ldb_delete_account(mem_ctx
,
1158 /* Can't dump them all right now */
1161 if (!NT_STATUS_IS_OK(nt_status
) && !*error_string
) {
1162 *error_string
= talloc_asprintf(mem_ctx
, "Failed to handle samsync delta: %s", nt_errstr(nt_status
));
1167 static NTSTATUS
libnet_samsync_ldb_init(TALLOC_CTX
*mem_ctx
,
1169 struct libnet_SamSync_state
*samsync_state
,
1170 char **error_string
)
1172 struct samsync_ldb_state
*state
= talloc_get_type(private_data
, struct samsync_ldb_state
);
1173 const char *server
= dcerpc_server_name(samsync_state
->netlogon_pipe
);
1176 state
->samsync_state
= samsync_state
;
1178 ZERO_STRUCT(state
->dom_sid
);
1179 if (state
->samsync_state
->domain_sid
) {
1180 state
->dom_sid
[SAM_DATABASE_DOMAIN
] = dom_sid_dup(state
, state
->samsync_state
->domain_sid
);
1183 state
->dom_sid
[SAM_DATABASE_BUILTIN
] = dom_sid_parse_talloc(state
, SID_BUILTIN
);
1185 if (state
->samsync_state
->realm
) {
1186 if (!server
|| !*server
) {
1187 /* huh? how do we not have a server name? */
1188 *error_string
= talloc_strdup(mem_ctx
, "No DCE/RPC server name available. How did we connect?");
1189 return NT_STATUS_INVALID_PARAMETER
;
1191 ldap_url
= talloc_asprintf(state
, "ldap://%s", server
);
1193 state
->remote_ldb
= ldb_wrap_connect(mem_ctx
,
1195 state
->samsync_state
->machine_net_ctx
->lp_ctx
,
1197 NULL
, state
->samsync_state
->machine_net_ctx
->cred
,
1199 if (!state
->remote_ldb
) {
1200 *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
);
1201 return NT_STATUS_NO_LOGON_SERVERS
;
1204 state
->remote_ldb
= NULL
;
1206 return NT_STATUS_OK
;
1209 NTSTATUS
libnet_samsync_ldb(struct libnet_context
*ctx
, TALLOC_CTX
*mem_ctx
, struct libnet_samsync_ldb
*r
)
1212 struct libnet_SamSync r2
;
1213 struct samsync_ldb_state
*state
= talloc(mem_ctx
, struct samsync_ldb_state
);
1216 return NT_STATUS_NO_MEMORY
;
1219 state
->secrets
= NULL
;
1220 state
->trusted_domains
= NULL
;
1222 state
->sam_ldb
= samdb_connect(mem_ctx
,
1227 if (!state
->sam_ldb
) {
1228 return NT_STATUS_INTERNAL_DB_ERROR
;
1231 state
->pdb
= privilege_connect(mem_ctx
,
1234 return NT_STATUS_INTERNAL_DB_ERROR
;
1237 r2
.out
.error_string
= NULL
;
1238 r2
.in
.binding_string
= r
->in
.binding_string
;
1239 r2
.in
.init_fn
= libnet_samsync_ldb_init
;
1240 r2
.in
.delta_fn
= libnet_samsync_ldb_fn
;
1241 r2
.in
.fn_ctx
= state
;
1242 r2
.in
.machine_account
= NULL
; /* TODO: Create a machine account, fill this in, and the delete it */
1243 nt_status
= libnet_SamSync_netlogon(ctx
, state
, &r2
);
1244 r
->out
.error_string
= r2
.out
.error_string
;
1245 talloc_steal(mem_ctx
, r
->out
.error_string
);
1247 if (!NT_STATUS_IS_OK(nt_status
)) {