s3-passdb: fix uninitialized variable in local_password_change().
[Samba.git] / source3 / winbindd / idmap_adex / gc_util.c
blob58e641b630199b1d1337f03b07a6f378adabb1fc
1 /*
2 * idmap_adex: Global Catalog search interface
4 * Copyright (C) Gerald (Jerry) Carter 2007-2008
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
21 #include "includes.h"
22 #include "idmap_adex.h"
24 #undef DBGC_CLASS
25 #define DBGC_CLASS DBGC_IDMAP
27 static struct gc_info *_gc_server_list = NULL;
30 /**********************************************************************
31 *********************************************************************/
33 static struct gc_info *gc_list_head(void)
35 return _gc_server_list;
38 /**********************************************************************
39 Checks if either of the domains is a subdomain of the other
40 *********************************************************************/
42 static bool is_subdomain(const char* a, const char *b)
44 char *s;
45 TALLOC_CTX *frame = talloc_stackframe();
46 char *x, *y;
47 bool ret = false;
49 /* Trivial cases */
51 if (!a && !b)
52 return true;
54 if (!a || !b)
55 return false;
57 /* Normalize the case */
59 x = talloc_strdup(frame, a);
60 y = talloc_strdup(frame, b);
61 if (!x || !y) {
62 ret = false;
63 goto done;
66 strupper_m(x);
67 strupper_m(y);
69 /* Exact match */
71 if (strcmp(x, y) == 0) {
72 ret = true;
73 goto done;
76 /* Check for trailing substrings */
78 s = strstr_m(x, y);
79 if (s && (strlen(s) == strlen(y))) {
80 ret = true;
81 goto done;
84 s = strstr_m(y, x);
85 if (s && (strlen(s) == strlen(x))) {
86 ret = true;
87 goto done;
90 done:
91 talloc_destroy(frame);
93 return ret;
96 /**********************************************************************
97 *********************************************************************/
99 NTSTATUS gc_find_forest_root(struct gc_info *gc, const char *domain)
101 ADS_STRUCT *ads = NULL;
102 ADS_STATUS ads_status;
103 NTSTATUS nt_status = NT_STATUS_UNSUCCESSFUL;
104 struct NETLOGON_SAM_LOGON_RESPONSE_EX cldap_reply;
105 TALLOC_CTX *frame = talloc_stackframe();
107 if (!gc || !domain) {
108 return NT_STATUS_INVALID_PARAMETER;
111 ZERO_STRUCT(cldap_reply);
113 ads = ads_init(domain, NULL, NULL);
114 BAIL_ON_PTR_ERROR(ads, nt_status);
116 ads->auth.flags = ADS_AUTH_NO_BIND;
117 ads_status = ads_connect(ads);
118 if (!ADS_ERR_OK(ads_status)) {
119 DEBUG(4, ("find_forest_root: ads_connect(%s) failed! (%s)\n",
120 domain, ads_errstr(ads_status)));
122 nt_status = ads_ntstatus(ads_status);
123 BAIL_ON_NTSTATUS_ERROR(nt_status);
125 if (!ads_cldap_netlogon_5(frame,
126 ads->config.ldap_server_name,
127 ads->config.realm,
128 &cldap_reply))
130 DEBUG(4,("find_forest_root: Failed to get a CLDAP reply from %s!\n",
131 ads->server.ldap_server));
132 nt_status = NT_STATUS_IO_TIMEOUT;
133 BAIL_ON_NTSTATUS_ERROR(nt_status);
136 gc->forest_name = talloc_strdup(gc, cldap_reply.forest);
137 BAIL_ON_PTR_ERROR(gc->forest_name, nt_status);
139 done:
140 if (ads) {
141 ads_destroy(&ads);
144 return nt_status;
147 /**********************************************************************
148 *********************************************************************/
150 static NTSTATUS gc_add_forest(const char *domain)
152 NTSTATUS nt_status = NT_STATUS_UNSUCCESSFUL;
153 struct gc_info *gc = NULL;
154 struct gc_info *find_gc = NULL;
155 char *dn;
156 ADS_STRUCT *ads = NULL;
157 struct likewise_cell *primary_cell = NULL;
159 primary_cell = cell_list_head();
160 if (!primary_cell) {
161 nt_status = NT_STATUS_INVALID_SERVER_STATE;
162 BAIL_ON_NTSTATUS_ERROR(nt_status);
165 /* Check for duplicates based on domain name first as this
166 requires no connection */
168 find_gc = gc_list_head();
169 while (find_gc) {
170 if (strequal (find_gc->forest_name, domain))
171 break;
172 find_gc = find_gc->next;
175 if (find_gc) {
176 DEBUG(10,("gc_add_forest: %s already in list\n", find_gc->forest_name));
177 return NT_STATUS_OK;
180 if ((gc = TALLOC_ZERO_P(NULL, struct gc_info)) == NULL) {
181 nt_status = NT_STATUS_NO_MEMORY;
182 BAIL_ON_NTSTATUS_ERROR(nt_status);
185 /* Query the rootDSE for the forest root naming conect first.
186 Check that the a GC server for the forest has not already
187 been added */
189 nt_status = gc_find_forest_root(gc, domain);
190 BAIL_ON_NTSTATUS_ERROR(nt_status);
192 find_gc = gc_list_head();
193 while (find_gc) {
194 if (strequal (find_gc->forest_name, gc->forest_name))
195 break;
196 find_gc = find_gc->next;
199 if (find_gc) {
200 DEBUG(10,("gc_add_forest: Forest %s already in list\n",
201 find_gc->forest_name));
202 return NT_STATUS_OK;
205 /* Not found, so add it here. Make sure we connect to
206 a DC in _this_ domain and not the forest root. */
208 dn = ads_build_dn(gc->forest_name);
209 BAIL_ON_PTR_ERROR(dn, nt_status);
211 gc->search_base = talloc_strdup(gc, dn);
212 SAFE_FREE(dn);
213 BAIL_ON_PTR_ERROR(gc->search_base, nt_status);
215 #if 0
216 /* Can't use cell_connect_dn() here as there is no way to
217 specifiy the LWCELL_FLAG_GC_CELL flag setting for cell_connect() */
219 nt_status = cell_connect_dn(&gc->forest_cell, gc->search_base);
220 BAIL_ON_NTSTATUS_ERROR(nt_status);
221 #else
223 gc->forest_cell = cell_new();
224 BAIL_ON_PTR_ERROR(gc->forest_cell, nt_status);
226 /* Set the DNS domain, dn, etc ... and add it to the list */
228 cell_set_dns_domain(gc->forest_cell, gc->forest_name);
229 cell_set_dn(gc->forest_cell, gc->search_base);
230 cell_set_flags(gc->forest_cell, LWCELL_FLAG_GC_CELL);
231 #endif
233 /* It is possible to belong to a non-forest cell and a
234 non-provisioned forest (at our domain levele). In that
235 case, we should just inherit the flags from our primary
236 cell since the GC searches will match our own schema
237 model. */
239 if (strequal(primary_cell->forest_name, gc->forest_name)
240 || is_subdomain(primary_cell->dns_domain, gc->forest_name))
242 cell_set_flags(gc->forest_cell, cell_flags(primary_cell));
243 } else {
244 /* outside of our domain */
246 nt_status = cell_connect(gc->forest_cell);
247 BAIL_ON_NTSTATUS_ERROR(nt_status);
249 nt_status = cell_lookup_settings(gc->forest_cell);
250 BAIL_ON_NTSTATUS_ERROR(nt_status);
252 /* Drop the connection now that we have the settings */
254 ads = cell_connection(gc->forest_cell);
255 ads_destroy(&ads);
256 cell_set_connection(gc->forest_cell, NULL);
259 DLIST_ADD_END(_gc_server_list, gc, struct gc_info*);
261 DEBUG(10,("gc_add_forest: Added %s to Global Catalog list of servers\n",
262 gc->forest_name));
264 nt_status = NT_STATUS_OK;
266 done:
267 if (!NT_STATUS_IS_OK(nt_status)) {
268 talloc_destroy(gc);
269 DEBUG(3,("LWI: Failed to add new GC connection for %s (%s)\n",
270 domain, nt_errstr(nt_status)));
273 return nt_status;
276 /**********************************************************************
277 *********************************************************************/
279 static void gc_server_list_destroy(void)
281 struct gc_info *gc = gc_list_head();
283 while (gc) {
284 struct gc_info *p = gc->next;
286 cell_destroy(gc->forest_cell);
287 talloc_destroy(gc);
289 gc = p;
292 _gc_server_list = NULL;
294 return;
297 /**********************************************************************
298 Setup the initial list of forests and initial the forest cell
299 settings for each. FIXME!!!
300 *********************************************************************/
302 NTSTATUS gc_init_list(void)
304 NTSTATUS nt_status = NT_STATUS_UNSUCCESSFUL;
305 struct winbindd_tdc_domain *domains = NULL;
306 size_t num_domains = 0;
307 int i;
309 if (_gc_server_list != NULL) {
310 gc_server_list_destroy();
313 if (!wcache_tdc_fetch_list(&domains, &num_domains)) {
314 nt_status = NT_STATUS_CANT_ACCESS_DOMAIN_INFO;
315 BAIL_ON_NTSTATUS_ERROR(nt_status);
318 /* Find our forest first. Have to try all domains here starting
319 with our own. gc_add_forest() filters duplicates */
321 nt_status = gc_add_forest(lp_realm());
322 WARN_ON_NTSTATUS_ERROR(nt_status);
324 for (i=0; i<num_domains; i++) {
325 uint32_t flags = (NETR_TRUST_FLAG_IN_FOREST);
327 /* I think we should be able to break out of loop once
328 we add a GC for our forest and not have to test every one.
329 In fact, this entire loop is probably irrelevant since
330 the GC location code should always find a GC given lp_realm().
331 Will have to spend time testing before making the change.
332 --jerry */
334 if ((domains[i].trust_flags & flags) == flags) {
335 nt_status = gc_add_forest(domains[i].dns_name);
336 WARN_ON_NTSTATUS_ERROR(nt_status);
337 /* Don't BAIL here since not every domain may
338 have a GC server */
342 /* Now add trusted forests. gc_add_forest() will filter out
343 duplicates. Check everything with an incoming trust path
344 that is not in our own forest. */
346 for (i=0; i<num_domains; i++) {
347 uint32_t flags = domains[i].trust_flags;
348 uint32_t attribs = domains[i].trust_attribs;
350 /* Skip non_AD domains */
352 if (strlen(domains[i].dns_name) == 0) {
353 continue;
356 /* Only add a GC for a forest outside of our own.
357 Ignore QUARANTINED/EXTERNAL trusts */
359 if ((flags & NETR_TRUST_FLAG_INBOUND)
360 && !(flags & NETR_TRUST_FLAG_IN_FOREST)
361 && (attribs & NETR_TRUST_ATTRIBUTE_FOREST_TRANSITIVE))
363 nt_status = gc_add_forest(domains[i].dns_name);
364 WARN_ON_NTSTATUS_ERROR(nt_status);
368 nt_status = NT_STATUS_OK;
370 done:
371 if (!NT_STATUS_IS_OK(nt_status)) {
372 DEBUG(2,("LWI: Failed to initialized GC list (%s)\n",
373 nt_errstr(nt_status)));
376 TALLOC_FREE(domains);
378 return nt_status;
382 /**********************************************************************
383 *********************************************************************/
385 struct gc_info *gc_search_start(void)
387 NTSTATUS nt_status = NT_STATUS_OK;
388 struct gc_info *gc = gc_list_head();
390 if (!gc) {
391 nt_status = gc_init_list();
392 BAIL_ON_NTSTATUS_ERROR(nt_status);
394 gc = gc_list_head();
397 done:
398 if (!NT_STATUS_IS_OK(nt_status)) {
399 DEBUG(2,("LWI: Failed to initialize GC list (%s)\n",
400 nt_errstr(nt_status)));
403 return gc;
406 /**********************************************************************
407 Search Global Catalog. Always search our own forest. The flags set
408 controls whether or not we search cross forest. Assume that the
409 resulting set is always returned from one GC so that we don't have to
410 both combining the LDAPMessage * results
411 *********************************************************************/
413 NTSTATUS gc_search_forest(struct gc_info *gc,
414 LDAPMessage **msg,
415 const char *filter)
417 NTSTATUS nt_status = NT_STATUS_UNSUCCESSFUL;
418 ADS_STATUS ads_status = ADS_ERROR_NT(NT_STATUS_UNSUCCESSFUL);
419 const char *attrs[] = {"*", NULL};
420 LDAPMessage *m = NULL;
422 if (!gc || !msg || !filter) {
423 nt_status = NT_STATUS_INVALID_PARAMETER;
424 BAIL_ON_NTSTATUS_ERROR(nt_status);
427 /* When you have multiple domain trees in a forest, the
428 GC will search all naming contexts when you send it
429 and empty ("") base search suffix. Tested against
430 Windows 2003. */
432 ads_status = cell_do_search(gc->forest_cell, "",
433 LDAP_SCOPE_SUBTREE, filter, attrs, &m);
434 nt_status = ads_ntstatus(ads_status);
435 BAIL_ON_NTSTATUS_ERROR(nt_status);
437 *msg = m;
439 done:
440 if (!NT_STATUS_IS_OK(nt_status)) {
441 DEBUG(2,("LWI: Forest wide search %s failed (%s)\n",
442 filter, nt_errstr(nt_status)));
445 return nt_status;
448 /**********************************************************************
449 Search all forests via GC and return the results in an array of
450 ADS_STRUCT/LDAPMessage pairs.
451 *********************************************************************/
453 NTSTATUS gc_search_all_forests(const char *filter,
454 ADS_STRUCT ***ads_list,
455 LDAPMessage ***msg_list,
456 int *num_resp, uint32_t flags)
458 NTSTATUS nt_status = NT_STATUS_UNSUCCESSFUL;
459 struct gc_info *gc = NULL;
460 uint32_t test_flags = ADEX_GC_SEARCH_CHECK_UNIQUE;
462 *ads_list = NULL;
463 *msg_list = NULL;
464 *num_resp = 0;
466 if ((gc = gc_search_start()) == NULL) {
467 nt_status = NT_STATUS_INVALID_DOMAIN_STATE;
468 BAIL_ON_NTSTATUS_ERROR(nt_status);
471 while (gc) {
472 LDAPMessage *m = NULL;
474 nt_status = gc_search_forest(gc, &m, filter);
475 if (!NT_STATUS_IS_OK(nt_status)) {
476 gc = gc->next;
477 continue;
480 nt_status = add_ads_result_to_array(cell_connection(gc->forest_cell),
481 m, ads_list, msg_list,
482 num_resp);
483 BAIL_ON_NTSTATUS_ERROR(nt_status);
485 /* If there can only be one match, then we are done */
487 if ((*num_resp > 0) && ((flags & test_flags) == test_flags)) {
488 break;
491 gc = gc->next;
494 if (*num_resp == 0) {
495 nt_status = NT_STATUS_OBJECT_NAME_NOT_FOUND;
496 BAIL_ON_NTSTATUS_ERROR(nt_status);
499 nt_status = NT_STATUS_OK;
501 done:
502 return nt_status;
505 /**********************************************************************
506 Search all forests via GC and return the results in an array of
507 ADS_STRUCT/LDAPMessage pairs.
508 *********************************************************************/
510 NTSTATUS gc_search_all_forests_unique(const char *filter,
511 ADS_STRUCT **ads,
512 LDAPMessage **msg)
514 ADS_STRUCT **ads_list = NULL;
515 LDAPMessage **msg_list = NULL;
516 int num_resp;
517 NTSTATUS nt_status = NT_STATUS_UNSUCCESSFUL;
519 nt_status = gc_search_all_forests(filter, &ads_list,
520 &msg_list, &num_resp,
521 ADEX_GC_SEARCH_CHECK_UNIQUE);
522 BAIL_ON_NTSTATUS_ERROR(nt_status);
524 nt_status = check_result_unique(ads_list[0], msg_list[0]);
525 BAIL_ON_NTSTATUS_ERROR(nt_status);
527 *ads = ads_list[0];
528 *msg = msg_list[0];
530 done:
531 /* Be care that we don't free the msg result being returned */
533 if (!NT_STATUS_IS_OK(nt_status)) {
534 free_result_array(ads_list, msg_list, num_resp);
535 } else {
536 talloc_destroy(ads_list);
537 talloc_destroy(msg_list);
540 return nt_status;
543 /*********************************************************************
544 ********************************************************************/
546 NTSTATUS gc_name_to_sid(const char *domain,
547 const char *name,
548 DOM_SID *sid,
549 enum lsa_SidType *sid_type)
551 TALLOC_CTX *frame = talloc_stackframe();
552 char *p, *name_user;
553 NTSTATUS nt_status = NT_STATUS_UNSUCCESSFUL;
554 char *name_filter;
555 ADS_STRUCT *ads = NULL;
556 LDAPMessage *msg = NULL;
557 LDAPMessage *e = NULL;
558 char *dn = NULL;
559 char *dns_domain = NULL;
560 ADS_STRUCT **ads_list = NULL;
561 LDAPMessage **msg_list = NULL;
562 int num_resp = 0;
563 int i;
565 /* Strip the "DOMAIN\" prefix if necessary and search for
566 a matching sAMAccountName in the forest */
568 if ((p = strchr_m( name, '\\' )) == NULL)
569 name_user = talloc_strdup( frame, name );
570 else
571 name_user = talloc_strdup( frame, p+1 );
572 BAIL_ON_PTR_ERROR(name_user, nt_status);
574 name_filter = talloc_asprintf(frame, "(sAMAccountName=%s)", name_user);
575 BAIL_ON_PTR_ERROR(name_filter, nt_status);
577 nt_status = gc_search_all_forests(name_filter, &ads_list,
578 &msg_list, &num_resp, 0);
579 BAIL_ON_NTSTATUS_ERROR(nt_status);
581 /* Assume failure until we know otherwise*/
583 nt_status = NT_STATUS_OBJECT_NAME_NOT_FOUND;
585 /* Match the domain name from the DN */
587 for (i=0; i<num_resp; i++) {
588 ads = ads_list[i];
589 msg = msg_list[i];
591 e = ads_first_entry(ads, msg);
592 while (e) {
593 struct winbindd_tdc_domain *domain_rec;
595 dn = ads_get_dn(ads, frame, e);
596 BAIL_ON_PTR_ERROR(dn, nt_status);
598 dns_domain = cell_dn_to_dns(dn);
599 TALLOC_FREE(dn);
600 BAIL_ON_PTR_ERROR(dns_domain, nt_status);
602 domain_rec = wcache_tdc_fetch_domain(frame, dns_domain);
603 SAFE_FREE(dns_domain);
605 /* Ignore failures and continue the search */
607 if (!domain_rec) {
608 e = ads_next_entry(ads, e);
609 continue;
612 /* Check for a match on the domain name */
614 if (strequal(domain, domain_rec->domain_name)) {
615 if (!ads_pull_sid(ads, e, "objectSid", sid)) {
616 nt_status = NT_STATUS_INVALID_SID;
617 BAIL_ON_NTSTATUS_ERROR(nt_status);
620 talloc_destroy(domain_rec);
622 nt_status = get_sid_type(ads, msg, sid_type);
623 BAIL_ON_NTSTATUS_ERROR(nt_status);
625 /* We're done! */
626 nt_status = NT_STATUS_OK;
627 break;
630 /* once more around thew merry-go-round */
632 talloc_destroy(domain_rec);
633 e = ads_next_entry(ads, e);
637 done:
638 free_result_array(ads_list, msg_list, num_resp);
639 talloc_destroy(frame);
641 return nt_status;
644 /********************************************************************
645 Pull an attribute string value
646 *******************************************************************/
648 static NTSTATUS get_object_account_name(ADS_STRUCT *ads,
649 LDAPMessage *msg,
650 char **name)
652 NTSTATUS nt_status = NT_STATUS_UNSUCCESSFUL;
653 char *sam_name = NULL;
654 struct winbindd_tdc_domain *domain_rec = NULL;
655 char *dns_domain = NULL;
656 char *dn = NULL;
657 TALLOC_CTX *frame = talloc_stackframe();
658 int len;
660 /* Check parameters */
662 if (!ads || !msg || !name) {
663 nt_status = NT_STATUS_INVALID_PARAMETER;
664 BAIL_ON_NTSTATUS_ERROR(nt_status);
667 /* get the name and domain */
669 dn = ads_get_dn(ads, frame, msg);
670 BAIL_ON_PTR_ERROR(dn, nt_status);
672 DEBUG(10,("get_object_account_name: dn = \"%s\"\n", dn));
674 dns_domain = cell_dn_to_dns(dn);
675 TALLOC_FREE(dn);
676 BAIL_ON_PTR_ERROR(dns_domain, nt_status);
678 domain_rec = wcache_tdc_fetch_domain(frame, dns_domain);
679 SAFE_FREE(dns_domain);
681 if (!domain_rec) {
682 nt_status = NT_STATUS_TRUSTED_DOMAIN_FAILURE;
683 BAIL_ON_NTSTATUS_ERROR(nt_status);
686 sam_name = ads_pull_string(ads, frame, msg, "sAMAccountName");
687 BAIL_ON_PTR_ERROR(sam_name, nt_status);
689 len = asprintf(name, "%s\\%s", domain_rec->domain_name, sam_name);
690 if (len == -1) {
691 *name = NULL;
692 BAIL_ON_PTR_ERROR((*name), nt_status);
695 nt_status = NT_STATUS_OK;
697 done:
698 talloc_destroy(frame);
700 return nt_status;
703 /*********************************************************************
704 ********************************************************************/
706 NTSTATUS gc_sid_to_name(const DOM_SID *sid,
707 char **name,
708 enum lsa_SidType *sid_type)
710 TALLOC_CTX *frame = talloc_stackframe();
711 NTSTATUS nt_status = NT_STATUS_UNSUCCESSFUL;
712 char *filter;
713 ADS_STRUCT *ads = NULL;
714 LDAPMessage *msg = NULL;
715 char *sid_string;
717 *name = NULL;
719 sid_string = sid_binstring(sid);
720 BAIL_ON_PTR_ERROR(sid_string, nt_status);
722 filter = talloc_asprintf(frame, "(objectSid=%s)", sid_string);
723 SAFE_FREE(sid_string);
724 BAIL_ON_PTR_ERROR(filter, nt_status);
726 nt_status = gc_search_all_forests_unique(filter, &ads, &msg);
727 BAIL_ON_NTSTATUS_ERROR(nt_status);
729 nt_status = get_object_account_name(ads, msg, name);
730 BAIL_ON_NTSTATUS_ERROR(nt_status);
732 nt_status = get_sid_type(ads, msg, sid_type);
733 BAIL_ON_NTSTATUS_ERROR(nt_status);
735 done:
736 ads_msgfree(ads, msg);
737 talloc_destroy(frame);
739 return nt_status;
742 /**********************************************************************
743 *********************************************************************/
745 NTSTATUS add_ads_result_to_array(ADS_STRUCT *ads,
746 LDAPMessage *msg,
747 ADS_STRUCT ***ads_list,
748 LDAPMessage ***msg_list,
749 int *size)
751 NTSTATUS nt_status = NT_STATUS_UNSUCCESSFUL;
752 ADS_STRUCT **ads_tmp = NULL;
753 LDAPMessage **msg_tmp = NULL;
754 int count = *size;
756 if (!ads || !msg) {
757 nt_status = NT_STATUS_INVALID_PARAMETER;
758 BAIL_ON_NTSTATUS_ERROR(nt_status);
761 #if 0
762 /* Don't add a response with no entries */
764 if (ads_count_replies(ads, msg) == 0) {
765 return NT_STATUS_OK;
767 #endif
769 if (count == 0) {
770 ads_tmp = TALLOC_ARRAY(NULL, ADS_STRUCT*, 1);
771 BAIL_ON_PTR_ERROR(ads_tmp, nt_status);
773 msg_tmp = TALLOC_ARRAY(NULL, LDAPMessage*, 1);
774 BAIL_ON_PTR_ERROR(msg_tmp, nt_status);
775 } else {
776 ads_tmp = TALLOC_REALLOC_ARRAY(*ads_list, *ads_list, ADS_STRUCT*,
777 count+1);
778 BAIL_ON_PTR_ERROR(ads_tmp, nt_status);
780 msg_tmp = TALLOC_REALLOC_ARRAY(*msg_list, *msg_list, LDAPMessage*,
781 count+1);
782 BAIL_ON_PTR_ERROR(msg_tmp, nt_status);
785 ads_tmp[count] = ads;
786 msg_tmp[count] = msg;
787 count++;
789 *ads_list = ads_tmp;
790 *msg_list = msg_tmp;
791 *size = count;
793 nt_status = NT_STATUS_OK;
795 done:
796 if (!NT_STATUS_IS_OK(nt_status)) {
797 talloc_destroy(ads_tmp);
798 talloc_destroy(msg_tmp);
801 return nt_status;
804 /**********************************************************************
805 Frees search results. Do not free the ads_list as these are
806 references back to the GC search structures.
807 *********************************************************************/
809 void free_result_array(ADS_STRUCT **ads_list,
810 LDAPMessage **msg_list,
811 int num_resp)
813 int i;
815 for (i=0; i<num_resp; i++) {
816 ads_msgfree(ads_list[i], msg_list[i]);
819 talloc_destroy(ads_list);
820 talloc_destroy(msg_list);
823 /**********************************************************************
824 Check that we have exactly one entry from the search
825 *********************************************************************/
827 NTSTATUS check_result_unique(ADS_STRUCT *ads, LDAPMessage *msg)
829 NTSTATUS nt_status;
830 int count;
832 count = ads_count_replies(ads, msg);
834 if (count <= 0) {
835 nt_status = NT_STATUS_OBJECT_NAME_NOT_FOUND;
836 BAIL_ON_NTSTATUS_ERROR(nt_status);
839 if (count > 1) {
840 nt_status = NT_STATUS_DUPLICATE_NAME;
841 BAIL_ON_NTSTATUS_ERROR(nt_status);
844 nt_status = NT_STATUS_OK;
846 done:
847 return nt_status;