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