s3: We only need base_name in map_open_params_to_ntcreate
[Samba.git] / source3 / winbindd / idmap_adex / gc_util.c
blob77b318cb2d71f596700fb51ddcdb6ec0b7542e19
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 "ads.h"
23 #include "idmap.h"
24 #include "idmap_adex.h"
25 #include "libads/cldap.h"
26 #include "../libcli/ldap/ldap_ndr.h"
28 #undef DBGC_CLASS
29 #define DBGC_CLASS DBGC_IDMAP
31 static struct gc_info *_gc_server_list = NULL;
34 /**********************************************************************
35 *********************************************************************/
37 static struct gc_info *gc_list_head(void)
39 return _gc_server_list;
42 /**********************************************************************
43 Checks if either of the domains is a subdomain of the other
44 *********************************************************************/
46 static bool is_subdomain(const char* a, const char *b)
48 char *s;
49 TALLOC_CTX *frame = talloc_stackframe();
50 char *x, *y;
51 bool ret = false;
53 /* Trivial cases */
55 if (!a && !b)
56 return true;
58 if (!a || !b)
59 return false;
61 /* Normalize the case */
63 x = talloc_strdup(frame, a);
64 y = talloc_strdup(frame, b);
65 if (!x || !y) {
66 ret = false;
67 goto done;
70 strupper_m(x);
71 strupper_m(y);
73 /* Exact match */
75 if (strcmp(x, y) == 0) {
76 ret = true;
77 goto done;
80 /* Check for trailing substrings */
82 s = strstr_m(x, y);
83 if (s && (strlen(s) == strlen(y))) {
84 ret = true;
85 goto done;
88 s = strstr_m(y, x);
89 if (s && (strlen(s) == strlen(x))) {
90 ret = true;
91 goto done;
94 done:
95 talloc_destroy(frame);
97 return ret;
100 /**********************************************************************
101 *********************************************************************/
103 NTSTATUS gc_find_forest_root(struct gc_info *gc, const char *domain)
105 ADS_STRUCT *ads = NULL;
106 ADS_STATUS ads_status;
107 NTSTATUS nt_status = NT_STATUS_UNSUCCESSFUL;
108 struct NETLOGON_SAM_LOGON_RESPONSE_EX cldap_reply;
109 TALLOC_CTX *frame = talloc_stackframe();
111 if (!gc || !domain) {
112 return NT_STATUS_INVALID_PARAMETER;
115 ZERO_STRUCT(cldap_reply);
117 ads = ads_init(domain, NULL, NULL);
118 BAIL_ON_PTR_ERROR(ads, nt_status);
120 ads->auth.flags = ADS_AUTH_NO_BIND;
121 ads_status = ads_connect(ads);
122 if (!ADS_ERR_OK(ads_status)) {
123 DEBUG(4, ("find_forest_root: ads_connect(%s) failed! (%s)\n",
124 domain, ads_errstr(ads_status)));
126 nt_status = ads_ntstatus(ads_status);
127 BAIL_ON_NTSTATUS_ERROR(nt_status);
129 if (!ads_cldap_netlogon_5(frame,
130 ads->config.ldap_server_name,
131 ads->config.realm,
132 &cldap_reply))
134 DEBUG(4,("find_forest_root: Failed to get a CLDAP reply from %s!\n",
135 ads->server.ldap_server));
136 nt_status = NT_STATUS_IO_TIMEOUT;
137 BAIL_ON_NTSTATUS_ERROR(nt_status);
140 gc->forest_name = talloc_strdup(gc, cldap_reply.forest);
141 BAIL_ON_PTR_ERROR(gc->forest_name, nt_status);
143 done:
144 if (ads) {
145 ads_destroy(&ads);
148 return nt_status;
151 /**********************************************************************
152 *********************************************************************/
154 static NTSTATUS gc_add_forest(const char *domain)
156 NTSTATUS nt_status = NT_STATUS_UNSUCCESSFUL;
157 struct gc_info *gc = NULL;
158 struct gc_info *find_gc = NULL;
159 char *dn;
160 ADS_STRUCT *ads = NULL;
161 struct likewise_cell *primary_cell = NULL;
163 primary_cell = cell_list_head();
164 if (!primary_cell) {
165 nt_status = NT_STATUS_INVALID_SERVER_STATE;
166 BAIL_ON_NTSTATUS_ERROR(nt_status);
169 /* Check for duplicates based on domain name first as this
170 requires no connection */
172 find_gc = gc_list_head();
173 while (find_gc) {
174 if (strequal (find_gc->forest_name, domain))
175 break;
176 find_gc = find_gc->next;
179 if (find_gc) {
180 DEBUG(10,("gc_add_forest: %s already in list\n", find_gc->forest_name));
181 return NT_STATUS_OK;
184 if ((gc = TALLOC_ZERO_P(NULL, struct gc_info)) == NULL) {
185 nt_status = NT_STATUS_NO_MEMORY;
186 BAIL_ON_NTSTATUS_ERROR(nt_status);
189 /* Query the rootDSE for the forest root naming conect first.
190 Check that the a GC server for the forest has not already
191 been added */
193 nt_status = gc_find_forest_root(gc, domain);
194 BAIL_ON_NTSTATUS_ERROR(nt_status);
196 find_gc = gc_list_head();
197 while (find_gc) {
198 if (strequal (find_gc->forest_name, gc->forest_name))
199 break;
200 find_gc = find_gc->next;
203 if (find_gc) {
204 DEBUG(10,("gc_add_forest: Forest %s already in list\n",
205 find_gc->forest_name));
206 return NT_STATUS_OK;
209 /* Not found, so add it here. Make sure we connect to
210 a DC in _this_ domain and not the forest root. */
212 dn = ads_build_dn(gc->forest_name);
213 BAIL_ON_PTR_ERROR(dn, nt_status);
215 gc->search_base = talloc_strdup(gc, dn);
216 SAFE_FREE(dn);
217 BAIL_ON_PTR_ERROR(gc->search_base, nt_status);
219 #if 0
220 /* Can't use cell_connect_dn() here as there is no way to
221 specifiy the LWCELL_FLAG_GC_CELL flag setting for cell_connect() */
223 nt_status = cell_connect_dn(&gc->forest_cell, gc->search_base);
224 BAIL_ON_NTSTATUS_ERROR(nt_status);
225 #else
227 gc->forest_cell = cell_new();
228 BAIL_ON_PTR_ERROR(gc->forest_cell, nt_status);
230 /* Set the DNS domain, dn, etc ... and add it to the list */
232 cell_set_dns_domain(gc->forest_cell, gc->forest_name);
233 cell_set_dn(gc->forest_cell, gc->search_base);
234 cell_set_flags(gc->forest_cell, LWCELL_FLAG_GC_CELL);
235 #endif
237 /* It is possible to belong to a non-forest cell and a
238 non-provisioned forest (at our domain levele). In that
239 case, we should just inherit the flags from our primary
240 cell since the GC searches will match our own schema
241 model. */
243 if (strequal(primary_cell->forest_name, gc->forest_name)
244 || is_subdomain(primary_cell->dns_domain, gc->forest_name))
246 cell_set_flags(gc->forest_cell, cell_flags(primary_cell));
247 } else {
248 /* outside of our domain */
250 nt_status = cell_connect(gc->forest_cell);
251 BAIL_ON_NTSTATUS_ERROR(nt_status);
253 nt_status = cell_lookup_settings(gc->forest_cell);
254 BAIL_ON_NTSTATUS_ERROR(nt_status);
256 /* Drop the connection now that we have the settings */
258 ads = cell_connection(gc->forest_cell);
259 ads_destroy(&ads);
260 cell_set_connection(gc->forest_cell, NULL);
263 DLIST_ADD_END(_gc_server_list, gc, struct gc_info*);
265 DEBUG(10,("gc_add_forest: Added %s to Global Catalog list of servers\n",
266 gc->forest_name));
268 nt_status = NT_STATUS_OK;
270 done:
271 if (!NT_STATUS_IS_OK(nt_status)) {
272 talloc_destroy(gc);
273 DEBUG(3,("LWI: Failed to add new GC connection for %s (%s)\n",
274 domain, nt_errstr(nt_status)));
277 return nt_status;
280 /**********************************************************************
281 *********************************************************************/
283 static void gc_server_list_destroy(void)
285 struct gc_info *gc = gc_list_head();
287 while (gc) {
288 struct gc_info *p = gc->next;
290 cell_destroy(gc->forest_cell);
291 talloc_destroy(gc);
293 gc = p;
296 _gc_server_list = NULL;
298 return;
301 /**********************************************************************
302 Setup the initial list of forests and initial the forest cell
303 settings for each. FIXME!!!
304 *********************************************************************/
306 NTSTATUS gc_init_list(void)
308 NTSTATUS nt_status = NT_STATUS_UNSUCCESSFUL;
309 struct winbindd_tdc_domain *domains = NULL;
310 size_t num_domains = 0;
311 int i;
313 if (_gc_server_list != NULL) {
314 gc_server_list_destroy();
317 if (!wcache_tdc_fetch_list(&domains, &num_domains)) {
318 nt_status = NT_STATUS_CANT_ACCESS_DOMAIN_INFO;
319 BAIL_ON_NTSTATUS_ERROR(nt_status);
322 /* Find our forest first. Have to try all domains here starting
323 with our own. gc_add_forest() filters duplicates */
325 nt_status = gc_add_forest(lp_realm());
326 WARN_ON_NTSTATUS_ERROR(nt_status);
328 for (i=0; i<num_domains; i++) {
329 uint32_t flags = (NETR_TRUST_FLAG_IN_FOREST);
331 /* I think we should be able to break out of loop once
332 we add a GC for our forest and not have to test every one.
333 In fact, this entire loop is probably irrelevant since
334 the GC location code should always find a GC given lp_realm().
335 Will have to spend time testing before making the change.
336 --jerry */
338 if ((domains[i].trust_flags & flags) == flags) {
339 nt_status = gc_add_forest(domains[i].dns_name);
340 WARN_ON_NTSTATUS_ERROR(nt_status);
341 /* Don't BAIL here since not every domain may
342 have a GC server */
346 /* Now add trusted forests. gc_add_forest() will filter out
347 duplicates. Check everything with an incoming trust path
348 that is not in our own forest. */
350 for (i=0; i<num_domains; i++) {
351 uint32_t flags = domains[i].trust_flags;
352 uint32_t attribs = domains[i].trust_attribs;
354 /* Skip non_AD domains */
356 if (strlen(domains[i].dns_name) == 0) {
357 continue;
360 /* Only add a GC for a forest outside of our own.
361 Ignore QUARANTINED/EXTERNAL trusts */
363 if ((flags & NETR_TRUST_FLAG_INBOUND)
364 && !(flags & NETR_TRUST_FLAG_IN_FOREST)
365 && (attribs & NETR_TRUST_ATTRIBUTE_FOREST_TRANSITIVE))
367 nt_status = gc_add_forest(domains[i].dns_name);
368 WARN_ON_NTSTATUS_ERROR(nt_status);
372 nt_status = NT_STATUS_OK;
374 done:
375 if (!NT_STATUS_IS_OK(nt_status)) {
376 DEBUG(2,("LWI: Failed to initialized GC list (%s)\n",
377 nt_errstr(nt_status)));
380 TALLOC_FREE(domains);
382 return nt_status;
386 /**********************************************************************
387 *********************************************************************/
389 struct gc_info *gc_search_start(void)
391 NTSTATUS nt_status = NT_STATUS_OK;
392 struct gc_info *gc = gc_list_head();
394 if (!gc) {
395 nt_status = gc_init_list();
396 BAIL_ON_NTSTATUS_ERROR(nt_status);
398 gc = gc_list_head();
401 done:
402 if (!NT_STATUS_IS_OK(nt_status)) {
403 DEBUG(2,("LWI: Failed to initialize GC list (%s)\n",
404 nt_errstr(nt_status)));
407 return gc;
410 /**********************************************************************
411 Search Global Catalog. Always search our own forest. The flags set
412 controls whether or not we search cross forest. Assume that the
413 resulting set is always returned from one GC so that we don't have to
414 both combining the LDAPMessage * results
415 *********************************************************************/
417 NTSTATUS gc_search_forest(struct gc_info *gc,
418 LDAPMessage **msg,
419 const char *filter)
421 NTSTATUS nt_status = NT_STATUS_UNSUCCESSFUL;
422 ADS_STATUS ads_status = ADS_ERROR_NT(NT_STATUS_UNSUCCESSFUL);
423 const char *attrs[] = {"*", NULL};
424 LDAPMessage *m = NULL;
426 if (!gc || !msg || !filter) {
427 nt_status = NT_STATUS_INVALID_PARAMETER;
428 BAIL_ON_NTSTATUS_ERROR(nt_status);
431 /* When you have multiple domain trees in a forest, the
432 GC will search all naming contexts when you send it
433 and empty ("") base search suffix. Tested against
434 Windows 2003. */
436 ads_status = cell_do_search(gc->forest_cell, "",
437 LDAP_SCOPE_SUBTREE, filter, attrs, &m);
438 nt_status = ads_ntstatus(ads_status);
439 BAIL_ON_NTSTATUS_ERROR(nt_status);
441 *msg = m;
443 done:
444 if (!NT_STATUS_IS_OK(nt_status)) {
445 DEBUG(2,("LWI: Forest wide search %s failed (%s)\n",
446 filter, nt_errstr(nt_status)));
449 return nt_status;
452 /**********************************************************************
453 Search all forests via GC and return the results in an array of
454 ADS_STRUCT/LDAPMessage pairs.
455 *********************************************************************/
457 NTSTATUS gc_search_all_forests(const char *filter,
458 ADS_STRUCT ***ads_list,
459 LDAPMessage ***msg_list,
460 int *num_resp, uint32_t flags)
462 NTSTATUS nt_status = NT_STATUS_UNSUCCESSFUL;
463 struct gc_info *gc = NULL;
464 uint32_t test_flags = ADEX_GC_SEARCH_CHECK_UNIQUE;
466 *ads_list = NULL;
467 *msg_list = NULL;
468 *num_resp = 0;
470 if ((gc = gc_search_start()) == NULL) {
471 nt_status = NT_STATUS_INVALID_DOMAIN_STATE;
472 BAIL_ON_NTSTATUS_ERROR(nt_status);
475 while (gc) {
476 LDAPMessage *m = NULL;
478 nt_status = gc_search_forest(gc, &m, filter);
479 if (!NT_STATUS_IS_OK(nt_status)) {
480 gc = gc->next;
481 continue;
484 nt_status = add_ads_result_to_array(cell_connection(gc->forest_cell),
485 m, ads_list, msg_list,
486 num_resp);
487 BAIL_ON_NTSTATUS_ERROR(nt_status);
489 /* If there can only be one match, then we are done */
491 if ((*num_resp > 0) && ((flags & test_flags) == test_flags)) {
492 break;
495 gc = gc->next;
498 if (*num_resp == 0) {
499 nt_status = NT_STATUS_OBJECT_NAME_NOT_FOUND;
500 BAIL_ON_NTSTATUS_ERROR(nt_status);
503 nt_status = NT_STATUS_OK;
505 done:
506 return nt_status;
509 /**********************************************************************
510 Search all forests via GC and return the results in an array of
511 ADS_STRUCT/LDAPMessage pairs.
512 *********************************************************************/
514 NTSTATUS gc_search_all_forests_unique(const char *filter,
515 ADS_STRUCT **ads,
516 LDAPMessage **msg)
518 ADS_STRUCT **ads_list = NULL;
519 LDAPMessage **msg_list = NULL;
520 int num_resp;
521 NTSTATUS nt_status = NT_STATUS_UNSUCCESSFUL;
523 nt_status = gc_search_all_forests(filter, &ads_list,
524 &msg_list, &num_resp,
525 ADEX_GC_SEARCH_CHECK_UNIQUE);
526 BAIL_ON_NTSTATUS_ERROR(nt_status);
528 nt_status = check_result_unique(ads_list[0], msg_list[0]);
529 BAIL_ON_NTSTATUS_ERROR(nt_status);
531 *ads = ads_list[0];
532 *msg = msg_list[0];
534 done:
535 /* Be care that we don't free the msg result being returned */
537 if (!NT_STATUS_IS_OK(nt_status)) {
538 free_result_array(ads_list, msg_list, num_resp);
539 } else {
540 talloc_destroy(ads_list);
541 talloc_destroy(msg_list);
544 return nt_status;
547 /*********************************************************************
548 ********************************************************************/
550 NTSTATUS gc_name_to_sid(const char *domain,
551 const char *name,
552 struct dom_sid *sid,
553 enum lsa_SidType *sid_type)
555 TALLOC_CTX *frame = talloc_stackframe();
556 char *p, *name_user;
557 NTSTATUS nt_status = NT_STATUS_UNSUCCESSFUL;
558 char *name_filter;
559 ADS_STRUCT *ads = NULL;
560 LDAPMessage *msg = NULL;
561 LDAPMessage *e = NULL;
562 char *dn = NULL;
563 char *dns_domain = NULL;
564 ADS_STRUCT **ads_list = NULL;
565 LDAPMessage **msg_list = NULL;
566 int num_resp = 0;
567 int i;
569 /* Strip the "DOMAIN\" prefix if necessary and search for
570 a matching sAMAccountName in the forest */
572 if ((p = strchr_m( name, '\\' )) == NULL)
573 name_user = talloc_strdup( frame, name );
574 else
575 name_user = talloc_strdup( frame, p+1 );
576 BAIL_ON_PTR_ERROR(name_user, nt_status);
578 name_filter = talloc_asprintf(frame, "(sAMAccountName=%s)", name_user);
579 BAIL_ON_PTR_ERROR(name_filter, nt_status);
581 nt_status = gc_search_all_forests(name_filter, &ads_list,
582 &msg_list, &num_resp, 0);
583 BAIL_ON_NTSTATUS_ERROR(nt_status);
585 /* Assume failure until we know otherwise*/
587 nt_status = NT_STATUS_OBJECT_NAME_NOT_FOUND;
589 /* Match the domain name from the DN */
591 for (i=0; i<num_resp; i++) {
592 ads = ads_list[i];
593 msg = msg_list[i];
595 e = ads_first_entry(ads, msg);
596 while (e) {
597 struct winbindd_tdc_domain *domain_rec;
599 dn = ads_get_dn(ads, frame, e);
600 BAIL_ON_PTR_ERROR(dn, nt_status);
602 dns_domain = cell_dn_to_dns(dn);
603 TALLOC_FREE(dn);
604 BAIL_ON_PTR_ERROR(dns_domain, nt_status);
606 domain_rec = wcache_tdc_fetch_domain(frame, dns_domain);
607 SAFE_FREE(dns_domain);
609 /* Ignore failures and continue the search */
611 if (!domain_rec) {
612 e = ads_next_entry(ads, e);
613 continue;
616 /* Check for a match on the domain name */
618 if (strequal(domain, domain_rec->domain_name)) {
619 if (!ads_pull_sid(ads, e, "objectSid", sid)) {
620 nt_status = NT_STATUS_INVALID_SID;
621 BAIL_ON_NTSTATUS_ERROR(nt_status);
624 talloc_destroy(domain_rec);
626 nt_status = get_sid_type(ads, msg, sid_type);
627 BAIL_ON_NTSTATUS_ERROR(nt_status);
629 /* We're done! */
630 nt_status = NT_STATUS_OK;
631 break;
634 /* once more around thew merry-go-round */
636 talloc_destroy(domain_rec);
637 e = ads_next_entry(ads, e);
641 done:
642 free_result_array(ads_list, msg_list, num_resp);
643 talloc_destroy(frame);
645 return nt_status;
648 /********************************************************************
649 Pull an attribute string value
650 *******************************************************************/
652 static NTSTATUS get_object_account_name(ADS_STRUCT *ads,
653 LDAPMessage *msg,
654 char **name)
656 NTSTATUS nt_status = NT_STATUS_UNSUCCESSFUL;
657 char *sam_name = NULL;
658 struct winbindd_tdc_domain *domain_rec = NULL;
659 char *dns_domain = NULL;
660 char *dn = NULL;
661 TALLOC_CTX *frame = talloc_stackframe();
662 int len;
664 /* Check parameters */
666 if (!ads || !msg || !name) {
667 nt_status = NT_STATUS_INVALID_PARAMETER;
668 BAIL_ON_NTSTATUS_ERROR(nt_status);
671 /* get the name and domain */
673 dn = ads_get_dn(ads, frame, msg);
674 BAIL_ON_PTR_ERROR(dn, nt_status);
676 DEBUG(10,("get_object_account_name: dn = \"%s\"\n", dn));
678 dns_domain = cell_dn_to_dns(dn);
679 TALLOC_FREE(dn);
680 BAIL_ON_PTR_ERROR(dns_domain, nt_status);
682 domain_rec = wcache_tdc_fetch_domain(frame, dns_domain);
683 SAFE_FREE(dns_domain);
685 if (!domain_rec) {
686 nt_status = NT_STATUS_TRUSTED_DOMAIN_FAILURE;
687 BAIL_ON_NTSTATUS_ERROR(nt_status);
690 sam_name = ads_pull_string(ads, frame, msg, "sAMAccountName");
691 BAIL_ON_PTR_ERROR(sam_name, nt_status);
693 len = asprintf(name, "%s\\%s", domain_rec->domain_name, sam_name);
694 if (len == -1) {
695 *name = NULL;
696 BAIL_ON_PTR_ERROR((*name), nt_status);
699 nt_status = NT_STATUS_OK;
701 done:
702 talloc_destroy(frame);
704 return nt_status;
707 /*********************************************************************
708 ********************************************************************/
710 NTSTATUS gc_sid_to_name(const struct dom_sid *sid,
711 char **name,
712 enum lsa_SidType *sid_type)
714 TALLOC_CTX *frame = talloc_stackframe();
715 NTSTATUS nt_status = NT_STATUS_UNSUCCESSFUL;
716 char *filter;
717 ADS_STRUCT *ads = NULL;
718 LDAPMessage *msg = NULL;
719 char *sid_string;
721 *name = NULL;
723 sid_string = ldap_encode_ndr_dom_sid(frame, sid);
724 BAIL_ON_PTR_ERROR(sid_string, nt_status);
726 filter = talloc_asprintf(frame, "(objectSid=%s)", sid_string);
727 TALLOC_FREE(sid_string);
728 BAIL_ON_PTR_ERROR(filter, nt_status);
730 nt_status = gc_search_all_forests_unique(filter, &ads, &msg);
731 BAIL_ON_NTSTATUS_ERROR(nt_status);
733 nt_status = get_object_account_name(ads, msg, name);
734 BAIL_ON_NTSTATUS_ERROR(nt_status);
736 nt_status = get_sid_type(ads, msg, sid_type);
737 BAIL_ON_NTSTATUS_ERROR(nt_status);
739 done:
740 ads_msgfree(ads, msg);
741 talloc_destroy(frame);
743 return nt_status;
746 /**********************************************************************
747 *********************************************************************/
749 NTSTATUS add_ads_result_to_array(ADS_STRUCT *ads,
750 LDAPMessage *msg,
751 ADS_STRUCT ***ads_list,
752 LDAPMessage ***msg_list,
753 int *size)
755 NTSTATUS nt_status = NT_STATUS_UNSUCCESSFUL;
756 ADS_STRUCT **ads_tmp = NULL;
757 LDAPMessage **msg_tmp = NULL;
758 int count = *size;
760 if (!ads || !msg) {
761 nt_status = NT_STATUS_INVALID_PARAMETER;
762 BAIL_ON_NTSTATUS_ERROR(nt_status);
765 #if 0
766 /* Don't add a response with no entries */
768 if (ads_count_replies(ads, msg) == 0) {
769 return NT_STATUS_OK;
771 #endif
773 if (count == 0) {
774 ads_tmp = TALLOC_ARRAY(NULL, ADS_STRUCT*, 1);
775 BAIL_ON_PTR_ERROR(ads_tmp, nt_status);
777 msg_tmp = TALLOC_ARRAY(NULL, LDAPMessage*, 1);
778 BAIL_ON_PTR_ERROR(msg_tmp, nt_status);
779 } else {
780 ads_tmp = TALLOC_REALLOC_ARRAY(*ads_list, *ads_list, ADS_STRUCT*,
781 count+1);
782 BAIL_ON_PTR_ERROR(ads_tmp, nt_status);
784 msg_tmp = TALLOC_REALLOC_ARRAY(*msg_list, *msg_list, LDAPMessage*,
785 count+1);
786 BAIL_ON_PTR_ERROR(msg_tmp, nt_status);
789 ads_tmp[count] = ads;
790 msg_tmp[count] = msg;
791 count++;
793 *ads_list = ads_tmp;
794 *msg_list = msg_tmp;
795 *size = count;
797 nt_status = NT_STATUS_OK;
799 done:
800 if (!NT_STATUS_IS_OK(nt_status)) {
801 talloc_destroy(ads_tmp);
802 talloc_destroy(msg_tmp);
805 return nt_status;
808 /**********************************************************************
809 Frees search results. Do not free the ads_list as these are
810 references back to the GC search structures.
811 *********************************************************************/
813 void free_result_array(ADS_STRUCT **ads_list,
814 LDAPMessage **msg_list,
815 int num_resp)
817 int i;
819 for (i=0; i<num_resp; i++) {
820 ads_msgfree(ads_list[i], msg_list[i]);
823 talloc_destroy(ads_list);
824 talloc_destroy(msg_list);
827 /**********************************************************************
828 Check that we have exactly one entry from the search
829 *********************************************************************/
831 NTSTATUS check_result_unique(ADS_STRUCT *ads, LDAPMessage *msg)
833 NTSTATUS nt_status;
834 int count;
836 count = ads_count_replies(ads, msg);
838 if (count <= 0) {
839 nt_status = NT_STATUS_OBJECT_NAME_NOT_FOUND;
840 BAIL_ON_NTSTATUS_ERROR(nt_status);
843 if (count > 1) {
844 nt_status = NT_STATUS_DUPLICATE_NAME;
845 BAIL_ON_NTSTATUS_ERROR(nt_status);
848 nt_status = NT_STATUS_OK;
850 done:
851 return nt_status;