r16168: Make the example match the actual function.
[Samba/aatanasov.git] / source / libnet / libnet_samsync_ldb.c
blob89123932319aaae2a5c36c4369227dcd9dc2d9d4
1 /*
2 Unix SMB/CIFS implementation.
4 Extract the user/system database from a remote SamSync server
6 Copyright (C) Andrew Bartlett <abartlet@samba.org> 2004-2005
7 Copyright (C) Andrew Tridgell 2004
8 Copyright (C) Volker Lendecke 2004
10 This program is free software; you can redistribute it and/or modify
11 it under the terms of the GNU General Public License as published by
12 the Free Software Foundation; either version 2 of the License, or
13 (at your option) any later version.
15 This program is distributed in the hope that it will be useful,
16 but WITHOUT ANY WARRANTY; without even the implied warranty of
17 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 GNU General Public License for more details.
20 You should have received a copy of the GNU General Public License
21 along with this program; if not, write to the Free Software
22 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
26 #include "includes.h"
27 #include "libnet/libnet.h"
28 #include "libcli/ldap/ldap.h"
29 #include "dsdb/samdb/samdb.h"
30 #include "auth/auth.h"
31 #include "librpc/gen_ndr/ndr_misc.h"
32 #include "db_wrap.h"
33 #include "libcli/security/security.h"
34 #include "librpc/rpc/dcerpc.h"
36 struct samsync_ldb_secret {
37 struct samsync_ldb_secret *prev, *next;
38 DATA_BLOB secret;
39 char *name;
40 NTTIME mtime;
43 struct samsync_ldb_trusted_domain {
44 struct samsync_ldb_trusted_domain *prev, *next;
45 struct dom_sid *sid;
46 char *name;
49 struct samsync_ldb_state {
50 /* Values from the LSA lookup */
51 const struct libnet_SamSync_state *samsync_state;
53 struct dom_sid *dom_sid[3];
54 struct ldb_context *sam_ldb, *remote_ldb;
55 struct ldb_dn *base_dn[3];
56 struct samsync_ldb_secret *secrets;
57 struct samsync_ldb_trusted_domain *trusted_domains;
60 static NTSTATUS samsync_ldb_add_foreignSecurityPrincipal(TALLOC_CTX *mem_ctx,
61 struct samsync_ldb_state *state,
62 struct dom_sid *sid,
63 struct ldb_dn **fsp_dn,
64 char **error_string)
66 const char *sidstr = dom_sid_string(mem_ctx, sid);
67 /* We assume that ForeignSecurityPrincipals are under the BASEDN of the main domain */
68 struct ldb_dn *basedn = samdb_search_dn(state->sam_ldb, mem_ctx,
69 state->base_dn[SAM_DATABASE_DOMAIN],
70 "(&(objectClass=container)(cn=ForeignSecurityPrincipals))");
71 struct ldb_message *msg;
72 int ret;
74 if (!sidstr) {
75 return NT_STATUS_NO_MEMORY;
78 if (basedn == NULL) {
79 *error_string = talloc_asprintf(mem_ctx,
80 "Failed to find DN for "
81 "ForeignSecurityPrincipal container under %s",
82 ldb_dn_linearize(mem_ctx, state->base_dn[SAM_DATABASE_DOMAIN]));
83 return NT_STATUS_INTERNAL_DB_CORRUPTION;
86 msg = ldb_msg_new(mem_ctx);
87 if (msg == NULL) {
88 return NT_STATUS_NO_MEMORY;
91 /* add core elements to the ldb_message for the alias */
92 msg->dn = ldb_dn_build_child(mem_ctx, "CN", sidstr, basedn);
93 if (msg->dn == NULL)
94 return NT_STATUS_NO_MEMORY;
96 samdb_msg_add_string(state->sam_ldb, mem_ctx, msg,
97 "objectClass",
98 "foreignSecurityPrincipal");
100 *fsp_dn = msg->dn;
102 /* create the alias */
103 ret = samdb_add(state->sam_ldb, mem_ctx, msg);
104 if (ret != 0) {
105 *error_string = talloc_asprintf(mem_ctx, "Failed to create foreignSecurityPrincipal "
106 "record %s: %s",
107 ldb_dn_linearize(mem_ctx, msg->dn),
108 ldb_errstring(state->sam_ldb));
109 return NT_STATUS_INTERNAL_DB_CORRUPTION;
111 return NT_STATUS_OK;
114 static NTSTATUS samsync_ldb_handle_domain(TALLOC_CTX *mem_ctx,
115 struct samsync_ldb_state *state,
116 enum netr_SamDatabaseID database,
117 struct netr_DELTA_ENUM *delta,
118 char **error_string)
120 struct netr_DELTA_DOMAIN *domain = delta->delta_union.domain;
121 const char *domain_name = domain->domain_name.string;
122 struct ldb_message *msg;
123 int ret;
125 msg = ldb_msg_new(mem_ctx);
126 if (msg == NULL) {
127 return NT_STATUS_NO_MEMORY;
130 if (database == SAM_DATABASE_DOMAIN) {
131 const char *domain_attrs[] = {"nETBIOSName", "nCName", NULL};
132 struct ldb_message **msgs_domain;
133 int ret_domain;
135 ret_domain = gendb_search(state->sam_ldb, mem_ctx, NULL, &msgs_domain, domain_attrs,
136 "(&(&(nETBIOSName=%s)(objectclass=crossRef))(ncName=*))",
137 domain_name);
138 if (ret_domain == -1) {
139 *error_string = talloc_asprintf(mem_ctx, "gendb_search for domain failed: %s", ldb_errstring(state->sam_ldb));
140 return NT_STATUS_INTERNAL_DB_CORRUPTION;
143 if (ret_domain != 1) {
144 *error_string = talloc_asprintf(mem_ctx, "Failed to find existing domain record for %s: %d results", domain_name,
145 ret_domain);
146 return NT_STATUS_NO_SUCH_DOMAIN;
149 state->base_dn[database] = samdb_result_dn(state, msgs_domain[0], "nCName", NULL);
151 if (state->dom_sid[database]) {
152 /* Update the domain sid with the incoming
153 * domain (found on LSA pipe, database sid may
154 * be random) */
155 samdb_msg_add_dom_sid(state->sam_ldb, mem_ctx,
156 msg, "objectSid", state->dom_sid[database]);
157 } else {
158 /* Well, we will have to use the one from the database */
159 state->dom_sid[database] = samdb_search_dom_sid(state->sam_ldb, state,
160 state->base_dn[database],
161 "objectSid", NULL);
164 if (state->samsync_state->domain_guid) {
165 NTSTATUS nt_status;
166 struct ldb_val v;
167 nt_status = ndr_push_struct_blob(&v, msg, state->samsync_state->domain_guid,
168 (ndr_push_flags_fn_t)ndr_push_GUID);
169 if (!NT_STATUS_IS_OK(nt_status)) {
170 *error_string = talloc_asprintf(mem_ctx, "ndr_push of domain GUID failed!");
171 return nt_status;
174 ldb_msg_add_value(msg, "objectGUID", &v);
176 } else if (database == SAM_DATABASE_BUILTIN) {
177 /* work out the builtin_dn - useful for so many calls its worth
178 fetching here */
179 const char *dnstring = samdb_search_string(state->sam_ldb, mem_ctx, NULL,
180 "distinguishedName", "objectClass=builtinDomain");
181 state->base_dn[database] = ldb_dn_explode(state, dnstring);
182 } else {
183 /* PRIVs DB */
184 return NT_STATUS_INVALID_PARAMETER;
187 msg->dn = talloc_reference(mem_ctx, state->base_dn[database]);
188 if (!msg->dn) {
189 return NT_STATUS_NO_MEMORY;
192 samdb_msg_add_string(state->sam_ldb, mem_ctx,
193 msg, "oEMInformation", domain->comment.string);
195 samdb_msg_add_int64(state->sam_ldb, mem_ctx,
196 msg, "forceLogoff", domain->force_logoff_time);
198 samdb_msg_add_uint(state->sam_ldb, mem_ctx,
199 msg, "minPwdLen", domain->min_password_length);
201 samdb_msg_add_int64(state->sam_ldb, mem_ctx,
202 msg, "maxPwdAge", domain->max_password_age);
204 samdb_msg_add_int64(state->sam_ldb, mem_ctx,
205 msg, "minPwdAge", domain->min_password_age);
207 samdb_msg_add_uint(state->sam_ldb, mem_ctx,
208 msg, "pwdHistoryLength", domain->password_history_length);
210 samdb_msg_add_uint64(state->sam_ldb, mem_ctx,
211 msg, "modifiedCount",
212 domain->sequence_num);
214 samdb_msg_add_uint64(state->sam_ldb, mem_ctx,
215 msg, "creationTime", domain->domain_create_time);
217 /* TODO: Account lockout, password properties */
219 ret = samdb_replace(state->sam_ldb, mem_ctx, msg);
221 if (ret) {
222 return NT_STATUS_INTERNAL_ERROR;
224 return NT_STATUS_OK;
227 static NTSTATUS samsync_ldb_handle_user(TALLOC_CTX *mem_ctx,
228 struct samsync_ldb_state *state,
229 enum netr_SamDatabaseID database,
230 struct netr_DELTA_ENUM *delta,
231 char **error_string)
233 uint32_t rid = delta->delta_id_union.rid;
234 struct netr_DELTA_USER *user = delta->delta_union.user;
235 const char *container, *obj_class;
236 char *cn_name;
237 int cn_name_len;
238 const struct dom_sid *user_sid;
239 struct ldb_message *msg;
240 struct ldb_message **msgs;
241 struct ldb_message **remote_msgs = NULL;
242 int ret, i;
243 uint32_t acb;
244 BOOL add = False;
245 const char *attrs[] = { NULL };
246 /* we may change this to a global search, then fill in only the things not in ldap later */
247 const char *remote_attrs[] = { "userPrincipalName", "servicePrincipalName",
248 "msDS-KeyVersionNumber", "objectGUID", NULL};
250 user_sid = dom_sid_add_rid(mem_ctx, state->dom_sid[database], rid);
251 if (!user_sid) {
252 return NT_STATUS_NO_MEMORY;
255 msg = ldb_msg_new(mem_ctx);
256 if (msg == NULL) {
257 return NT_STATUS_NO_MEMORY;
260 msg->dn = NULL;
261 /* search for the user, by rid */
262 ret = gendb_search(state->sam_ldb, mem_ctx, state->base_dn[database],
263 &msgs, attrs, "(&(objectClass=user)(objectSid=%s))",
264 ldap_encode_ndr_dom_sid(mem_ctx, user_sid));
266 if (ret == -1) {
267 *error_string = talloc_asprintf(mem_ctx, "LDB for user %s failed: %s",
268 dom_sid_string(mem_ctx, user_sid),
269 ldb_errstring(state->sam_ldb));
270 return NT_STATUS_INTERNAL_DB_CORRUPTION;
271 } else if (ret == 0) {
272 add = True;
273 } else if (ret > 1) {
274 *error_string = talloc_asprintf(mem_ctx, "More than one user with SID: %s in local LDB",
275 dom_sid_string(mem_ctx, user_sid));
276 return NT_STATUS_INTERNAL_DB_CORRUPTION;
277 } else {
278 msg->dn = msgs[0]->dn;
279 talloc_steal(msg, msgs[0]->dn);
282 /* and do the same on the remote database */
283 if (state->remote_ldb) {
284 ret = gendb_search(state->remote_ldb, mem_ctx, state->base_dn[database],
285 &remote_msgs, remote_attrs, "(&(objectClass=user)(objectSid=%s))",
286 ldap_encode_ndr_dom_sid(mem_ctx, user_sid));
288 if (ret == -1) {
289 *error_string = talloc_asprintf(mem_ctx, "remote LDAP for user %s failed: %s",
290 dom_sid_string(mem_ctx, user_sid),
291 ldb_errstring(state->remote_ldb));
292 return NT_STATUS_INTERNAL_DB_CORRUPTION;
293 } else if (ret == 0) {
294 *error_string = talloc_asprintf(mem_ctx, "User exists in samsync but not in remote LDAP domain! (base: %s, SID: %s)",
295 ldb_dn_linearize(mem_ctx, state->base_dn[database]),
296 dom_sid_string(mem_ctx, user_sid));
297 return NT_STATUS_NO_SUCH_USER;
298 } else if (ret > 1) {
299 *error_string = talloc_asprintf(mem_ctx, "More than one user in remote LDAP domain with SID: %s",
300 dom_sid_string(mem_ctx, user_sid));
301 return NT_STATUS_INTERNAL_DB_CORRUPTION;
303 /* Try to put things in the same location as the remote server */
304 } else if (add) {
305 msg->dn = remote_msgs[0]->dn;
306 talloc_steal(msg, remote_msgs[0]->dn);
310 cn_name = talloc_strdup(mem_ctx, user->account_name.string);
311 NT_STATUS_HAVE_NO_MEMORY(cn_name);
312 cn_name_len = strlen(cn_name);
314 #define ADD_OR_DEL(type, attrib, field) do { \
315 if (user->field) { \
316 samdb_msg_add_ ## type(state->sam_ldb, mem_ctx, msg, \
317 attrib, user->field); \
318 } else if (!add) { \
319 samdb_msg_add_delete(state->sam_ldb, mem_ctx, msg, \
320 attrib); \
322 } while (0);
324 ADD_OR_DEL(string, "samAccountName", account_name.string);
325 ADD_OR_DEL(string, "displayName", full_name.string);
327 if (samdb_msg_add_dom_sid(state->sam_ldb, mem_ctx, msg,
328 "objectSid", dom_sid_add_rid(mem_ctx, state->dom_sid[database], rid))) {
329 return NT_STATUS_NO_MEMORY;
332 ADD_OR_DEL(uint, "primaryGroupID", primary_gid);
333 ADD_OR_DEL(string, "homeDirectory", home_directory.string);
334 ADD_OR_DEL(string, "homeDrive", home_drive.string);
335 ADD_OR_DEL(string, "scriptPath", logon_script.string);
336 ADD_OR_DEL(string, "description", description.string);
337 ADD_OR_DEL(string, "userWorkstations", workstations.string);
339 ADD_OR_DEL(uint64, "lastLogon", last_logon);
340 ADD_OR_DEL(uint64, "lastLogoff", last_logoff);
342 if (samdb_msg_add_logon_hours(state->sam_ldb, mem_ctx, msg, "logonHours", &user->logon_hours) != 0) {
343 return NT_STATUS_NO_MEMORY;
346 ADD_OR_DEL(uint, "badPwdCount", bad_password_count);
347 ADD_OR_DEL(uint, "logonCount", logon_count);
349 ADD_OR_DEL(uint64, "pwdLastSet", last_password_change);
350 ADD_OR_DEL(uint64, "accountExpires", acct_expiry);
352 if (samdb_msg_add_acct_flags(state->sam_ldb, mem_ctx, msg,
353 "userAccountControl", user->acct_flags) != 0) {
354 return NT_STATUS_NO_MEMORY;
357 /* Passwords. Ensure there is no plaintext stored against
358 * this entry, as we only have hashes */
359 samdb_msg_add_delete(state->sam_ldb, mem_ctx, msg,
360 "sambaPassword");
361 if (user->lm_password_present) {
362 samdb_msg_add_hash(state->sam_ldb, mem_ctx, msg,
363 "lmPwdHash", &user->lmpassword);
364 } else {
365 samdb_msg_add_delete(state->sam_ldb, mem_ctx, msg,
366 "lmPwdHash");
368 if (user->nt_password_present) {
369 samdb_msg_add_hash(state->sam_ldb, mem_ctx, msg,
370 "ntPwdHash", &user->ntpassword);
371 } else {
372 samdb_msg_add_delete(state->sam_ldb, mem_ctx, msg,
373 "ntPwdHash");
376 ADD_OR_DEL(string, "comment", comment.string);
377 ADD_OR_DEL(string, "userParameters", parameters.string);
378 ADD_OR_DEL(uint, "countryCode", country_code);
379 ADD_OR_DEL(uint, "codePage", code_page);
381 ADD_OR_DEL(string, "profilePath", profile_path.string);
383 #undef ADD_OR_DEL
385 for (i=0; remote_attrs[i]; i++) {
386 struct ldb_message_element *el = ldb_msg_find_element(remote_msgs[0], remote_attrs[i]);
387 if (!el) {
388 samdb_msg_add_delete(state->sam_ldb, mem_ctx, msg,
389 remote_attrs[i]);
390 } else {
391 ldb_msg_add(msg, el, LDB_FLAG_MOD_REPLACE);
395 acb = user->acct_flags;
396 if (acb & (ACB_WSTRUST)) {
397 cn_name[cn_name_len - 1] = '\0';
398 container = "Computers";
399 obj_class = "computer";
401 } else if (acb & ACB_SVRTRUST) {
402 if (cn_name[cn_name_len - 1] != '$') {
403 return NT_STATUS_FOOBAR;
405 cn_name[cn_name_len - 1] = '\0';
406 container = "Domain Controllers";
407 obj_class = "computer";
408 } else {
409 container = "Users";
410 obj_class = "user";
412 if (add) {
413 samdb_msg_add_string(state->sam_ldb, mem_ctx, msg,
414 "objectClass", obj_class);
415 if (!msg->dn) {
416 msg->dn = ldb_dn_string_compose(mem_ctx, state->base_dn[database],
417 "CN=%s, CN=%s", cn_name, container);
418 if (!msg->dn) {
419 return NT_STATUS_NO_MEMORY;
423 ret = samdb_add(state->sam_ldb, mem_ctx, msg);
424 if (ret != 0) {
425 struct ldb_dn *first_try_dn = msg->dn;
426 /* Try again with the default DN */
427 msg->dn = talloc_steal(msg, msgs[0]->dn);
428 ret = samdb_add(state->sam_ldb, mem_ctx, msg);
429 if (ret != 0) {
430 *error_string = talloc_asprintf(mem_ctx, "Failed to create user record. Tried both %s and %s: %s",
431 ldb_dn_linearize(mem_ctx, first_try_dn),
432 ldb_dn_linearize(mem_ctx, msg->dn),
433 ldb_errstring(state->sam_ldb));
434 return NT_STATUS_INTERNAL_DB_CORRUPTION;
437 } else {
438 ret = samdb_replace(state->sam_ldb, mem_ctx, msg);
439 if (ret != 0) {
440 *error_string = talloc_asprintf(mem_ctx, "Failed to modify user record %s: %s",
441 ldb_dn_linearize(mem_ctx, msg->dn),
442 ldb_errstring(state->sam_ldb));
443 return NT_STATUS_INTERNAL_DB_CORRUPTION;
447 return NT_STATUS_OK;
450 static NTSTATUS samsync_ldb_delete_user(TALLOC_CTX *mem_ctx,
451 struct samsync_ldb_state *state,
452 enum netr_SamDatabaseID database,
453 struct netr_DELTA_ENUM *delta,
454 char **error_string)
456 uint32_t rid = delta->delta_id_union.rid;
457 struct ldb_message **msgs;
458 int ret;
459 const char *attrs[] = { NULL };
461 /* search for the user, by rid */
462 ret = gendb_search(state->sam_ldb, mem_ctx, state->base_dn[database],
463 &msgs, attrs, "(&(objectClass=user)(objectSid=%s))",
464 ldap_encode_ndr_dom_sid(mem_ctx, dom_sid_add_rid(mem_ctx, state->dom_sid[database], rid)));
466 if (ret == -1) {
467 *error_string = talloc_asprintf(mem_ctx, "gendb_search failed: %s", ldb_errstring(state->sam_ldb));
468 return NT_STATUS_INTERNAL_DB_CORRUPTION;
469 } else if (ret == 0) {
470 return NT_STATUS_NO_SUCH_USER;
471 } else if (ret > 1) {
472 *error_string = talloc_asprintf(mem_ctx, "More than one user with SID: %s",
473 dom_sid_string(mem_ctx,
474 dom_sid_add_rid(mem_ctx,
475 state->dom_sid[database],
476 rid)));
477 return NT_STATUS_INTERNAL_DB_CORRUPTION;
480 ret = samdb_delete(state->sam_ldb, mem_ctx, msgs[0]->dn);
481 if (ret != 0) {
482 *error_string = talloc_asprintf(mem_ctx, "Failed to delete user record %s: %s",
483 ldb_dn_linearize(mem_ctx, msgs[0]->dn),
484 ldb_errstring(state->sam_ldb));
485 return NT_STATUS_INTERNAL_DB_CORRUPTION;
488 return NT_STATUS_OK;
491 static NTSTATUS samsync_ldb_handle_group(TALLOC_CTX *mem_ctx,
492 struct samsync_ldb_state *state,
493 enum netr_SamDatabaseID database,
494 struct netr_DELTA_ENUM *delta,
495 char **error_string)
497 uint32_t rid = delta->delta_id_union.rid;
498 struct netr_DELTA_GROUP *group = delta->delta_union.group;
499 const char *container, *obj_class;
500 const char *cn_name;
502 struct ldb_message *msg;
503 struct ldb_message **msgs;
504 int ret;
505 BOOL add = False;
506 const char *attrs[] = { NULL };
508 msg = ldb_msg_new(mem_ctx);
509 if (msg == NULL) {
510 return NT_STATUS_NO_MEMORY;
513 /* search for the group, by rid */
514 ret = gendb_search(state->sam_ldb, mem_ctx, state->base_dn[database], &msgs, attrs,
515 "(&(objectClass=group)(objectSid=%s))",
516 ldap_encode_ndr_dom_sid(mem_ctx, dom_sid_add_rid(mem_ctx, state->dom_sid[database], rid)));
518 if (ret == -1) {
519 *error_string = talloc_asprintf(mem_ctx, "gendb_search failed: %s", ldb_errstring(state->sam_ldb));
520 return NT_STATUS_INTERNAL_DB_CORRUPTION;
521 } else if (ret == 0) {
522 add = True;
523 } else if (ret > 1) {
524 *error_string = talloc_asprintf(mem_ctx, "More than one group/alias with SID: %s",
525 dom_sid_string(mem_ctx,
526 dom_sid_add_rid(mem_ctx,
527 state->dom_sid[database],
528 rid)));
529 return NT_STATUS_INTERNAL_DB_CORRUPTION;
530 } else {
531 msg->dn = talloc_steal(msg, msgs[0]->dn);
534 cn_name = group->group_name.string;
536 #define ADD_OR_DEL(type, attrib, field) do { \
537 if (group->field) { \
538 samdb_msg_add_ ## type(state->sam_ldb, mem_ctx, msg, \
539 attrib, group->field); \
540 } else if (!add) { \
541 samdb_msg_add_delete(state->sam_ldb, mem_ctx, msg, \
542 attrib); \
544 } while (0);
546 ADD_OR_DEL(string, "samAccountName", group_name.string);
548 if (samdb_msg_add_dom_sid(state->sam_ldb, mem_ctx, msg,
549 "objectSid", dom_sid_add_rid(mem_ctx, state->dom_sid[database], rid))) {
550 return NT_STATUS_NO_MEMORY;
553 ADD_OR_DEL(string, "description", description.string);
555 #undef ADD_OR_DEL
557 container = "Users";
558 obj_class = "group";
560 if (add) {
561 samdb_msg_add_string(state->sam_ldb, mem_ctx, msg,
562 "objectClass", obj_class);
563 msg->dn = ldb_dn_string_compose(mem_ctx, state->base_dn[database],
564 "CN=%s, CN=%s", cn_name, container);
565 if (!msg->dn) {
566 return NT_STATUS_NO_MEMORY;
569 ret = samdb_add(state->sam_ldb, mem_ctx, msg);
570 if (ret != 0) {
571 *error_string = talloc_asprintf(mem_ctx, "Failed to create group record %s: %s",
572 ldb_dn_linearize(mem_ctx, msg->dn),
573 ldb_errstring(state->sam_ldb));
574 return NT_STATUS_INTERNAL_DB_CORRUPTION;
576 } else {
577 ret = samdb_replace(state->sam_ldb, mem_ctx, msg);
578 if (ret != 0) {
579 *error_string = talloc_asprintf(mem_ctx, "Failed to modify group record %s: %s",
580 ldb_dn_linearize(mem_ctx, msg->dn),
581 ldb_errstring(state->sam_ldb));
582 return NT_STATUS_INTERNAL_DB_CORRUPTION;
586 return NT_STATUS_OK;
589 static NTSTATUS samsync_ldb_delete_group(TALLOC_CTX *mem_ctx,
590 struct samsync_ldb_state *state,
591 enum netr_SamDatabaseID database,
592 struct netr_DELTA_ENUM *delta,
593 char **error_string)
595 uint32_t rid = delta->delta_id_union.rid;
596 struct ldb_message **msgs;
597 int ret;
598 const char *attrs[] = { NULL };
600 /* search for the group, by rid */
601 ret = gendb_search(state->sam_ldb, mem_ctx, state->base_dn[database], &msgs, attrs,
602 "(&(objectClass=group)(objectSid=%s))",
603 ldap_encode_ndr_dom_sid(mem_ctx, dom_sid_add_rid(mem_ctx, state->dom_sid[database], rid)));
605 if (ret == -1) {
606 *error_string = talloc_asprintf(mem_ctx, "gendb_search failed: %s", ldb_errstring(state->sam_ldb));
607 return NT_STATUS_INTERNAL_DB_CORRUPTION;
608 } else if (ret == 0) {
609 return NT_STATUS_NO_SUCH_GROUP;
610 } else if (ret > 1) {
611 *error_string = talloc_asprintf(mem_ctx, "More than one group/alias with SID: %s",
612 dom_sid_string(mem_ctx,
613 dom_sid_add_rid(mem_ctx,
614 state->dom_sid[database],
615 rid)));
616 return NT_STATUS_INTERNAL_DB_CORRUPTION;
619 ret = samdb_delete(state->sam_ldb, mem_ctx, msgs[0]->dn);
620 if (ret != 0) {
621 *error_string = talloc_asprintf(mem_ctx, "Failed to delete group record %s: %s",
622 ldb_dn_linearize(mem_ctx, msgs[0]->dn),
623 ldb_errstring(state->sam_ldb));
624 return NT_STATUS_INTERNAL_DB_CORRUPTION;
627 return NT_STATUS_OK;
630 static NTSTATUS samsync_ldb_handle_group_member(TALLOC_CTX *mem_ctx,
631 struct samsync_ldb_state *state,
632 enum netr_SamDatabaseID database,
633 struct netr_DELTA_ENUM *delta,
634 char **error_string)
636 uint32_t rid = delta->delta_id_union.rid;
637 struct netr_DELTA_GROUP_MEMBER *group_member = delta->delta_union.group_member;
638 struct ldb_message *msg;
639 struct ldb_message **msgs;
640 int ret;
641 const char *attrs[] = { NULL };
642 int i;
644 msg = ldb_msg_new(mem_ctx);
645 if (msg == NULL) {
646 return NT_STATUS_NO_MEMORY;
649 /* search for the group, by rid */
650 ret = gendb_search(state->sam_ldb, mem_ctx, state->base_dn[database], &msgs, attrs,
651 "(&(objectClass=group)(objectSid=%s))",
652 ldap_encode_ndr_dom_sid(mem_ctx, dom_sid_add_rid(mem_ctx, state->dom_sid[database], rid)));
654 if (ret == -1) {
655 *error_string = talloc_asprintf(mem_ctx, "gendb_search failed: %s", ldb_errstring(state->sam_ldb));
656 return NT_STATUS_INTERNAL_DB_CORRUPTION;
657 } else if (ret == 0) {
658 return NT_STATUS_NO_SUCH_GROUP;
659 } else if (ret > 1) {
660 *error_string = talloc_asprintf(mem_ctx, "More than one group/alias with SID: %s",
661 dom_sid_string(mem_ctx,
662 dom_sid_add_rid(mem_ctx,
663 state->dom_sid[database],
664 rid)));
665 return NT_STATUS_INTERNAL_DB_CORRUPTION;
666 } else {
667 msg->dn = talloc_steal(msg, msgs[0]->dn);
670 talloc_free(msgs);
672 for (i=0; i<group_member->num_rids; i++) {
673 /* search for the group, by rid */
674 ret = gendb_search(state->sam_ldb, mem_ctx, state->base_dn[database], &msgs, attrs,
675 "(&(objectClass=user)(objectSid=%s))",
676 ldap_encode_ndr_dom_sid(mem_ctx, dom_sid_add_rid(mem_ctx, state->dom_sid[database], group_member->rids[i])));
678 if (ret == -1) {
679 *error_string = talloc_asprintf(mem_ctx, "gendb_search failed: %s", ldb_errstring(state->sam_ldb));
680 return NT_STATUS_INTERNAL_DB_CORRUPTION;
681 } else if (ret == 0) {
682 return NT_STATUS_NO_SUCH_USER;
683 } else if (ret > 1) {
684 return NT_STATUS_INTERNAL_DB_CORRUPTION;
685 } else {
686 samdb_msg_add_string(state->sam_ldb, mem_ctx, msg, "member", ldb_dn_linearize(mem_ctx, msgs[0]->dn));
689 talloc_free(msgs);
692 ret = samdb_replace(state->sam_ldb, mem_ctx, msg);
693 if (ret != 0) {
694 *error_string = talloc_asprintf(mem_ctx, "Failed to modify group record %s: %s",
695 ldb_dn_linearize(mem_ctx, msg->dn),
696 ldb_errstring(state->sam_ldb));
697 return NT_STATUS_INTERNAL_DB_CORRUPTION;
700 return NT_STATUS_OK;
703 static NTSTATUS samsync_ldb_handle_alias(TALLOC_CTX *mem_ctx,
704 struct samsync_ldb_state *state,
705 enum netr_SamDatabaseID database,
706 struct netr_DELTA_ENUM *delta,
707 char **error_string)
709 uint32_t rid = delta->delta_id_union.rid;
710 struct netr_DELTA_ALIAS *alias = delta->delta_union.alias;
711 const char *container, *obj_class;
712 const char *cn_name;
714 struct ldb_message *msg;
715 struct ldb_message **msgs;
716 int ret;
717 BOOL add = False;
718 const char *attrs[] = { NULL };
720 msg = ldb_msg_new(mem_ctx);
721 if (msg == NULL) {
722 return NT_STATUS_NO_MEMORY;
725 /* search for the alias, by rid */
726 ret = gendb_search(state->sam_ldb, mem_ctx, state->base_dn[database], &msgs, attrs,
727 "(&(objectClass=group)(objectSid=%s))",
728 ldap_encode_ndr_dom_sid(mem_ctx, dom_sid_add_rid(mem_ctx, state->dom_sid[database], rid)));
730 if (ret == -1) {
731 *error_string = talloc_asprintf(mem_ctx, "gendb_search failed: %s", ldb_errstring(state->sam_ldb));
732 return NT_STATUS_INTERNAL_DB_CORRUPTION;
733 } else if (ret == 0) {
734 add = True;
735 } else if (ret > 1) {
736 *error_string = talloc_asprintf(mem_ctx, "More than one group/alias with SID: %s",
737 dom_sid_string(mem_ctx,
738 dom_sid_add_rid(mem_ctx,
739 state->dom_sid[database],
740 rid)));
741 return NT_STATUS_INTERNAL_DB_CORRUPTION;
742 } else {
743 msg->dn = talloc_steal(mem_ctx, msgs[0]->dn);
746 cn_name = alias->alias_name.string;
748 #define ADD_OR_DEL(type, attrib, field) do { \
749 if (alias->field) { \
750 samdb_msg_add_ ## type(state->sam_ldb, mem_ctx, msg, \
751 attrib, alias->field); \
752 } else if (!add) { \
753 samdb_msg_add_delete(state->sam_ldb, mem_ctx, msg, \
754 attrib); \
756 } while (0);
758 ADD_OR_DEL(string, "samAccountName", alias_name.string);
760 if (samdb_msg_add_dom_sid(state->sam_ldb, mem_ctx, msg,
761 "objectSid", dom_sid_add_rid(mem_ctx, state->dom_sid[database], rid))) {
762 return NT_STATUS_NO_MEMORY;
765 ADD_OR_DEL(string, "description", description.string);
767 #undef ADD_OR_DEL
769 samdb_msg_add_uint(state->sam_ldb, mem_ctx, msg, "groupType", 0x80000004);
771 container = "Users";
772 obj_class = "group";
774 if (add) {
775 samdb_msg_add_string(state->sam_ldb, mem_ctx, msg,
776 "objectClass", obj_class);
777 msg->dn = ldb_dn_string_compose(mem_ctx, state->base_dn[database],
778 "CN=%s, CN=%s", cn_name, container);
779 if (!msg->dn) {
780 return NT_STATUS_NO_MEMORY;
783 ret = samdb_add(state->sam_ldb, mem_ctx, msg);
784 if (ret != 0) {
785 *error_string = talloc_asprintf(mem_ctx, "Failed to create alias record %s: %s",
786 ldb_dn_linearize(mem_ctx, msg->dn),
787 ldb_errstring(state->sam_ldb));
788 return NT_STATUS_INTERNAL_DB_CORRUPTION;
790 } else {
791 ret = samdb_replace(state->sam_ldb, mem_ctx, msg);
792 if (ret != 0) {
793 *error_string = talloc_asprintf(mem_ctx, "Failed to modify alias record %s: %s",
794 ldb_dn_linearize(mem_ctx, msg->dn),
795 ldb_errstring(state->sam_ldb));
796 return NT_STATUS_INTERNAL_DB_CORRUPTION;
800 return NT_STATUS_OK;
803 static NTSTATUS samsync_ldb_delete_alias(TALLOC_CTX *mem_ctx,
804 struct samsync_ldb_state *state,
805 enum netr_SamDatabaseID database,
806 struct netr_DELTA_ENUM *delta,
807 char **error_string)
809 uint32_t rid = delta->delta_id_union.rid;
810 struct ldb_message **msgs;
811 int ret;
812 const char *attrs[] = { NULL };
814 /* search for the alias, by rid */
815 ret = gendb_search(state->sam_ldb, mem_ctx, state->base_dn[database], &msgs, attrs,
816 "(&(objectClass=group)(objectSid=%s))",
817 ldap_encode_ndr_dom_sid(mem_ctx, dom_sid_add_rid(mem_ctx, state->dom_sid[database], rid)));
819 if (ret == -1) {
820 *error_string = talloc_asprintf(mem_ctx, "gendb_search failed: %s", ldb_errstring(state->sam_ldb));
821 return NT_STATUS_INTERNAL_DB_CORRUPTION;
822 } else if (ret == 0) {
823 return NT_STATUS_NO_SUCH_ALIAS;
824 } else if (ret > 1) {
825 return NT_STATUS_INTERNAL_DB_CORRUPTION;
828 ret = samdb_delete(state->sam_ldb, mem_ctx, msgs[0]->dn);
829 if (ret != 0) {
830 *error_string = talloc_asprintf(mem_ctx, "Failed to delete alias record %s: %s",
831 ldb_dn_linearize(mem_ctx, msgs[0]->dn),
832 ldb_errstring(state->sam_ldb));
833 return NT_STATUS_INTERNAL_DB_CORRUPTION;
836 return NT_STATUS_OK;
839 static NTSTATUS samsync_ldb_handle_alias_member(TALLOC_CTX *mem_ctx,
840 struct samsync_ldb_state *state,
841 enum netr_SamDatabaseID database,
842 struct netr_DELTA_ENUM *delta,
843 char **error_string)
845 uint32_t rid = delta->delta_id_union.rid;
846 struct netr_DELTA_ALIAS_MEMBER *alias_member = delta->delta_union.alias_member;
847 struct ldb_message *msg;
848 struct ldb_message **msgs;
849 int ret;
850 const char *attrs[] = { NULL };
851 int i;
853 msg = ldb_msg_new(mem_ctx);
854 if (msg == NULL) {
855 return NT_STATUS_NO_MEMORY;
858 /* search for the alias, by rid */
859 ret = gendb_search(state->sam_ldb, mem_ctx, state->base_dn[database], &msgs, attrs,
860 "(&(objectClass=group)(objectSid=%s))",
861 ldap_encode_ndr_dom_sid(mem_ctx, dom_sid_add_rid(mem_ctx, state->dom_sid[database], rid)));
863 if (ret == -1) {
864 *error_string = talloc_asprintf(mem_ctx, "gendb_search failed: %s", ldb_errstring(state->sam_ldb));
865 return NT_STATUS_INTERNAL_DB_CORRUPTION;
866 } else if (ret == 0) {
867 return NT_STATUS_NO_SUCH_GROUP;
868 } else if (ret > 1) {
869 *error_string = talloc_asprintf(mem_ctx, "More than one group/alias with SID: %s",
870 dom_sid_string(mem_ctx,
871 dom_sid_add_rid(mem_ctx,
872 state->dom_sid[database],
873 rid)));
874 return NT_STATUS_INTERNAL_DB_CORRUPTION;
875 } else {
876 msg->dn = talloc_steal(msg, msgs[0]->dn);
879 talloc_free(msgs);
881 for (i=0; i<alias_member->sids.num_sids; i++) {
882 struct ldb_dn *alias_member_dn;
883 /* search for members, in the top basedn (normal users are builtin aliases) */
884 ret = gendb_search(state->sam_ldb, mem_ctx, state->base_dn[SAM_DATABASE_DOMAIN], &msgs, attrs,
885 "(objectSid=%s)",
886 ldap_encode_ndr_dom_sid(mem_ctx, alias_member->sids.sids[i].sid));
888 if (ret == -1) {
889 *error_string = talloc_asprintf(mem_ctx, "gendb_search failed: %s", ldb_errstring(state->sam_ldb));
890 return NT_STATUS_INTERNAL_DB_CORRUPTION;
891 } else if (ret == 0) {
892 NTSTATUS nt_status;
893 nt_status = samsync_ldb_add_foreignSecurityPrincipal(mem_ctx, state,
894 alias_member->sids.sids[i].sid,
895 &alias_member_dn,
896 error_string);
897 if (!NT_STATUS_IS_OK(nt_status)) {
898 return nt_status;
900 } else if (ret > 1) {
901 return NT_STATUS_INTERNAL_DB_CORRUPTION;
902 } else {
903 alias_member_dn = msgs[0]->dn;
905 samdb_msg_add_string(state->sam_ldb, mem_ctx, msg, "member", ldb_dn_linearize(mem_ctx, alias_member_dn));
907 talloc_free(msgs);
910 ret = samdb_replace(state->sam_ldb, mem_ctx, msg);
911 if (ret != 0) {
912 *error_string = talloc_asprintf(mem_ctx, "Failed to modify group record %s: %s",
913 ldb_dn_linearize(mem_ctx, msg->dn),
914 ldb_errstring(state->sam_ldb));
915 return NT_STATUS_INTERNAL_DB_CORRUPTION;
918 return NT_STATUS_OK;
921 static NTSTATUS samsync_ldb_handle_account(TALLOC_CTX *mem_ctx,
922 struct samsync_ldb_state *state,
923 enum netr_SamDatabaseID database,
924 struct netr_DELTA_ENUM *delta,
925 char **error_string)
927 struct dom_sid *sid = delta->delta_id_union.sid;
928 struct netr_DELTA_ACCOUNT *account = delta->delta_union.account;
930 struct ldb_message *msg;
931 struct ldb_message **msgs;
932 struct ldb_dn *privilege_dn;
933 int ret;
934 const char *attrs[] = { NULL };
935 int i;
937 msg = ldb_msg_new(mem_ctx);
938 if (msg == NULL) {
939 return NT_STATUS_NO_MEMORY;
942 /* search for the account, by sid, in the top basedn */
943 ret = gendb_search(state->sam_ldb, mem_ctx, state->base_dn[SAM_DATABASE_DOMAIN], &msgs, attrs,
944 "(objectSid=%s)", ldap_encode_ndr_dom_sid(mem_ctx, sid));
946 if (ret == -1) {
947 *error_string = talloc_asprintf(mem_ctx, "gendb_search failed: %s", ldb_errstring(state->sam_ldb));
948 return NT_STATUS_INTERNAL_DB_CORRUPTION;
949 } else if (ret == 0) {
950 NTSTATUS nt_status;
951 nt_status = samsync_ldb_add_foreignSecurityPrincipal(mem_ctx, state,
952 sid,
953 &privilege_dn,
954 error_string);
955 privilege_dn = talloc_steal(msg, privilege_dn);
956 if (!NT_STATUS_IS_OK(nt_status)) {
957 return nt_status;
959 } else if (ret > 1) {
960 *error_string = talloc_asprintf(mem_ctx, "More than one account with SID: %s",
961 dom_sid_string(mem_ctx, sid));
962 return NT_STATUS_INTERNAL_DB_CORRUPTION;
963 } else {
964 privilege_dn = talloc_steal(msg, msgs[0]->dn);
967 msg->dn = privilege_dn;
969 for (i=0; i< account->privilege_entries; i++) {
970 samdb_msg_add_string(state->sam_ldb, mem_ctx, msg, "privilege",
971 account->privilege_name[i].string);
974 ret = samdb_replace(state->sam_ldb, mem_ctx, msg);
975 if (ret != 0) {
976 *error_string = talloc_asprintf(mem_ctx, "Failed to modify privilege record %s",
977 ldb_dn_linearize(mem_ctx, msg->dn));
978 return NT_STATUS_INTERNAL_DB_CORRUPTION;
981 return NT_STATUS_OK;
984 static NTSTATUS samsync_ldb_delete_account(TALLOC_CTX *mem_ctx,
985 struct samsync_ldb_state *state,
986 enum netr_SamDatabaseID database,
987 struct netr_DELTA_ENUM *delta,
988 char **error_string)
990 struct dom_sid *sid = delta->delta_id_union.sid;
992 struct ldb_message *msg;
993 struct ldb_message **msgs;
994 int ret;
995 const char *attrs[] = { NULL };
997 msg = ldb_msg_new(mem_ctx);
998 if (msg == NULL) {
999 return NT_STATUS_NO_MEMORY;
1002 /* search for the account, by sid, in the top basedn */
1003 ret = gendb_search(state->sam_ldb, mem_ctx, state->base_dn[SAM_DATABASE_DOMAIN], &msgs, attrs,
1004 "(objectSid=%s)",
1005 ldap_encode_ndr_dom_sid(mem_ctx, sid));
1007 if (ret == -1) {
1008 *error_string = talloc_asprintf(mem_ctx, "gendb_search failed: %s", ldb_errstring(state->sam_ldb));
1009 return NT_STATUS_INTERNAL_DB_CORRUPTION;
1010 } else if (ret == 0) {
1011 return NT_STATUS_NO_SUCH_USER;
1012 } else if (ret > 1) {
1013 *error_string = talloc_asprintf(mem_ctx, "More than one account with SID: %s",
1014 dom_sid_string(mem_ctx, sid));
1015 return NT_STATUS_INTERNAL_DB_CORRUPTION;
1016 } else {
1017 msg->dn = talloc_steal(msg, msgs[0]->dn);
1020 samdb_msg_add_delete(state->sam_ldb, mem_ctx, msg,
1021 "privilage");
1023 ret = samdb_replace(state->sam_ldb, mem_ctx, msg);
1024 if (ret != 0) {
1025 *error_string = talloc_asprintf(mem_ctx, "Failed to modify privilege record %s",
1026 ldb_dn_linearize(mem_ctx, msg->dn));
1027 return NT_STATUS_INTERNAL_DB_CORRUPTION;
1030 return NT_STATUS_OK;
1033 static NTSTATUS libnet_samsync_ldb_fn(TALLOC_CTX *mem_ctx,
1034 void *private,
1035 enum netr_SamDatabaseID database,
1036 struct netr_DELTA_ENUM *delta,
1037 char **error_string)
1039 NTSTATUS nt_status = NT_STATUS_OK;
1040 struct samsync_ldb_state *state = talloc_get_type(private, struct samsync_ldb_state);
1042 *error_string = NULL;
1043 switch (delta->delta_type) {
1044 case NETR_DELTA_DOMAIN:
1046 nt_status = samsync_ldb_handle_domain(mem_ctx,
1047 state,
1048 database,
1049 delta,
1050 error_string);
1051 break;
1053 case NETR_DELTA_USER:
1055 nt_status = samsync_ldb_handle_user(mem_ctx,
1056 state,
1057 database,
1058 delta,
1059 error_string);
1060 break;
1062 case NETR_DELTA_DELETE_USER:
1064 nt_status = samsync_ldb_delete_user(mem_ctx,
1065 state,
1066 database,
1067 delta,
1068 error_string);
1069 break;
1071 case NETR_DELTA_GROUP:
1073 nt_status = samsync_ldb_handle_group(mem_ctx,
1074 state,
1075 database,
1076 delta,
1077 error_string);
1078 break;
1080 case NETR_DELTA_DELETE_GROUP:
1082 nt_status = samsync_ldb_delete_group(mem_ctx,
1083 state,
1084 database,
1085 delta,
1086 error_string);
1087 break;
1089 case NETR_DELTA_GROUP_MEMBER:
1091 nt_status = samsync_ldb_handle_group_member(mem_ctx,
1092 state,
1093 database,
1094 delta,
1095 error_string);
1096 break;
1098 case NETR_DELTA_ALIAS:
1100 nt_status = samsync_ldb_handle_alias(mem_ctx,
1101 state,
1102 database,
1103 delta,
1104 error_string);
1105 break;
1107 case NETR_DELTA_DELETE_ALIAS:
1109 nt_status = samsync_ldb_delete_alias(mem_ctx,
1110 state,
1111 database,
1112 delta,
1113 error_string);
1114 break;
1116 case NETR_DELTA_ALIAS_MEMBER:
1118 nt_status = samsync_ldb_handle_alias_member(mem_ctx,
1119 state,
1120 database,
1121 delta,
1122 error_string);
1123 break;
1125 case NETR_DELTA_ACCOUNT:
1127 nt_status = samsync_ldb_handle_account(mem_ctx,
1128 state,
1129 database,
1130 delta,
1131 error_string);
1132 break;
1134 case NETR_DELTA_DELETE_ACCOUNT:
1136 nt_status = samsync_ldb_delete_account(mem_ctx,
1137 state,
1138 database,
1139 delta,
1140 error_string);
1141 break;
1143 default:
1144 /* Can't dump them all right now */
1145 break;
1147 if (!NT_STATUS_IS_OK(nt_status) && !*error_string) {
1148 *error_string = talloc_asprintf(mem_ctx, "Failed to handle samsync delta: %s", nt_errstr(nt_status));
1150 return nt_status;
1153 static NTSTATUS libnet_samsync_ldb_init(TALLOC_CTX *mem_ctx,
1154 void *private,
1155 struct libnet_SamSync_state *samsync_state,
1156 char **error_string)
1158 struct samsync_ldb_state *state = talloc_get_type(private, struct samsync_ldb_state);
1159 const char *server = dcerpc_server_name(samsync_state->netlogon_pipe);
1160 char *ldap_url;
1162 state->samsync_state = samsync_state;
1164 ZERO_STRUCT(state->dom_sid);
1165 if (state->samsync_state->domain_sid) {
1166 state->dom_sid[SAM_DATABASE_DOMAIN] = dom_sid_dup(state, state->samsync_state->domain_sid);
1169 state->dom_sid[SAM_DATABASE_BUILTIN] = dom_sid_parse_talloc(state, SID_BUILTIN);
1171 if (state->samsync_state->realm) {
1172 if (!server || !*server) {
1173 /* huh? how do we not have a server name? */
1174 *error_string = talloc_strdup(mem_ctx, "No DCE/RPC server name available. How did we connect?");
1175 return NT_STATUS_INVALID_PARAMETER;
1177 ldap_url = talloc_asprintf(state, "ldap://%s", server);
1179 state->remote_ldb = ldb_wrap_connect(mem_ctx, ldap_url,
1180 NULL, state->samsync_state->machine_net_ctx->cred,
1181 0, NULL);
1182 if (!state->remote_ldb) {
1183 *error_string = talloc_asprintf(mem_ctx, "Failed to connect to remote LDAP server at %s (used to extract additional data in SamSync replication)", ldap_url);
1184 return NT_STATUS_NO_LOGON_SERVERS;
1186 } else {
1187 state->remote_ldb = NULL;
1189 return NT_STATUS_OK;
1192 NTSTATUS libnet_samsync_ldb(struct libnet_context *ctx, TALLOC_CTX *mem_ctx, struct libnet_samsync_ldb *r)
1194 NTSTATUS nt_status;
1195 struct libnet_SamSync r2;
1196 struct samsync_ldb_state *state = talloc(mem_ctx, struct samsync_ldb_state);
1198 if (!state) {
1199 return NT_STATUS_NO_MEMORY;
1202 state->secrets = NULL;
1203 state->trusted_domains = NULL;
1205 state->sam_ldb = ldb_wrap_connect(mem_ctx, lp_sam_url(), r->in.session_info,
1206 ctx->cred, 0, NULL);
1208 r2.out.error_string = NULL;
1209 r2.in.binding_string = r->in.binding_string;
1210 r2.in.init_fn = libnet_samsync_ldb_init;
1211 r2.in.delta_fn = libnet_samsync_ldb_fn;
1212 r2.in.fn_ctx = state;
1213 r2.in.machine_account = NULL; /* TODO: Create a machine account, fill this in, and the delete it */
1214 nt_status = libnet_SamSync_netlogon(ctx, state, &r2);
1215 r->out.error_string = r2.out.error_string;
1216 talloc_steal(mem_ctx, r->out.error_string);
1218 if (!NT_STATUS_IS_OK(nt_status)) {
1219 talloc_free(state);
1220 return nt_status;
1222 talloc_free(state);
1223 return nt_status;