s3-printing: rename queue->job sysjob
[Samba/id10ts.git] / source3 / winbindd / idmap_adex / gc_util.c
blob461a2e679b7a2aadba202d84a4aa965dbcfe6b3e
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();
110 struct sockaddr_storage ss;
112 if (!gc || !domain) {
113 return NT_STATUS_INVALID_PARAMETER;
116 ZERO_STRUCT(cldap_reply);
118 ads = ads_init(domain, NULL, NULL);
119 BAIL_ON_PTR_ERROR(ads, nt_status);
121 ads->auth.flags = ADS_AUTH_NO_BIND;
122 ads_status = ads_connect(ads);
123 if (!ADS_ERR_OK(ads_status)) {
124 DEBUG(4, ("find_forest_root: ads_connect(%s) failed! (%s)\n",
125 domain, ads_errstr(ads_status)));
127 nt_status = ads_ntstatus(ads_status);
128 BAIL_ON_NTSTATUS_ERROR(nt_status);
130 if (!resolve_name(ads->config.ldap_server_name, &ss, 0x20, true)) {
131 DEBUG(5,("gc_find_forest_root: unable to resolve name %s\n",
132 ads->config.ldap_server_name));
133 nt_status = NT_STATUS_IO_TIMEOUT;
134 /* This matches the old code which did the resolve in
135 * ads_cldap_netlogon_5 */
136 BAIL_ON_NTSTATUS_ERROR(nt_status);
139 if (!ads_cldap_netlogon_5(frame,
140 &ss,
141 ads->config.realm,
142 &cldap_reply))
144 DEBUG(4,("find_forest_root: Failed to get a CLDAP reply from %s!\n",
145 ads->server.ldap_server));
146 nt_status = NT_STATUS_IO_TIMEOUT;
147 BAIL_ON_NTSTATUS_ERROR(nt_status);
150 gc->forest_name = talloc_strdup(gc, cldap_reply.forest);
151 BAIL_ON_PTR_ERROR(gc->forest_name, nt_status);
153 done:
154 if (ads) {
155 ads_destroy(&ads);
158 return nt_status;
161 /**********************************************************************
162 *********************************************************************/
164 static NTSTATUS gc_add_forest(const char *domain)
166 NTSTATUS nt_status = NT_STATUS_UNSUCCESSFUL;
167 struct gc_info *gc = NULL;
168 struct gc_info *find_gc = NULL;
169 char *dn;
170 ADS_STRUCT *ads = NULL;
171 struct likewise_cell *primary_cell = NULL;
173 primary_cell = cell_list_head();
174 if (!primary_cell) {
175 nt_status = NT_STATUS_INVALID_SERVER_STATE;
176 BAIL_ON_NTSTATUS_ERROR(nt_status);
179 /* Check for duplicates based on domain name first as this
180 requires no connection */
182 find_gc = gc_list_head();
183 while (find_gc) {
184 if (strequal (find_gc->forest_name, domain))
185 break;
186 find_gc = find_gc->next;
189 if (find_gc) {
190 DEBUG(10,("gc_add_forest: %s already in list\n", find_gc->forest_name));
191 return NT_STATUS_OK;
194 if ((gc = talloc_zero(NULL, struct gc_info)) == NULL) {
195 nt_status = NT_STATUS_NO_MEMORY;
196 BAIL_ON_NTSTATUS_ERROR(nt_status);
199 /* Query the rootDSE for the forest root naming conect first.
200 Check that the a GC server for the forest has not already
201 been added */
203 nt_status = gc_find_forest_root(gc, domain);
204 BAIL_ON_NTSTATUS_ERROR(nt_status);
206 find_gc = gc_list_head();
207 while (find_gc) {
208 if (strequal (find_gc->forest_name, gc->forest_name))
209 break;
210 find_gc = find_gc->next;
213 if (find_gc) {
214 DEBUG(10,("gc_add_forest: Forest %s already in list\n",
215 find_gc->forest_name));
216 return NT_STATUS_OK;
219 /* Not found, so add it here. Make sure we connect to
220 a DC in _this_ domain and not the forest root. */
222 dn = ads_build_dn(gc->forest_name);
223 BAIL_ON_PTR_ERROR(dn, nt_status);
225 gc->search_base = talloc_strdup(gc, dn);
226 SAFE_FREE(dn);
227 BAIL_ON_PTR_ERROR(gc->search_base, nt_status);
229 #if 0
230 /* Can't use cell_connect_dn() here as there is no way to
231 specifiy the LWCELL_FLAG_GC_CELL flag setting for cell_connect() */
233 nt_status = cell_connect_dn(&gc->forest_cell, gc->search_base);
234 BAIL_ON_NTSTATUS_ERROR(nt_status);
235 #else
237 gc->forest_cell = cell_new();
238 BAIL_ON_PTR_ERROR(gc->forest_cell, nt_status);
240 /* Set the DNS domain, dn, etc ... and add it to the list */
242 cell_set_dns_domain(gc->forest_cell, gc->forest_name);
243 cell_set_dn(gc->forest_cell, gc->search_base);
244 cell_set_flags(gc->forest_cell, LWCELL_FLAG_GC_CELL);
245 #endif
247 /* It is possible to belong to a non-forest cell and a
248 non-provisioned forest (at our domain levele). In that
249 case, we should just inherit the flags from our primary
250 cell since the GC searches will match our own schema
251 model. */
253 if (strequal(primary_cell->forest_name, gc->forest_name)
254 || is_subdomain(primary_cell->dns_domain, gc->forest_name))
256 cell_set_flags(gc->forest_cell, cell_flags(primary_cell));
257 } else {
258 /* outside of our domain */
260 nt_status = cell_connect(gc->forest_cell);
261 BAIL_ON_NTSTATUS_ERROR(nt_status);
263 nt_status = cell_lookup_settings(gc->forest_cell);
264 BAIL_ON_NTSTATUS_ERROR(nt_status);
266 /* Drop the connection now that we have the settings */
268 ads = cell_connection(gc->forest_cell);
269 ads_destroy(&ads);
270 cell_set_connection(gc->forest_cell, NULL);
273 DLIST_ADD_END(_gc_server_list, gc, struct gc_info*);
275 DEBUG(10,("gc_add_forest: Added %s to Global Catalog list of servers\n",
276 gc->forest_name));
278 nt_status = NT_STATUS_OK;
280 done:
281 if (!NT_STATUS_IS_OK(nt_status)) {
282 talloc_destroy(gc);
283 DEBUG(3,("LWI: Failed to add new GC connection for %s (%s)\n",
284 domain, nt_errstr(nt_status)));
287 return nt_status;
290 /**********************************************************************
291 *********************************************************************/
293 static void gc_server_list_destroy(void)
295 struct gc_info *gc = gc_list_head();
297 while (gc) {
298 struct gc_info *p = gc->next;
300 cell_destroy(gc->forest_cell);
301 talloc_destroy(gc);
303 gc = p;
306 _gc_server_list = NULL;
308 return;
311 /**********************************************************************
312 Setup the initial list of forests and initial the forest cell
313 settings for each. FIXME!!!
314 *********************************************************************/
316 NTSTATUS gc_init_list(void)
318 NTSTATUS nt_status = NT_STATUS_UNSUCCESSFUL;
319 struct winbindd_tdc_domain *domains = NULL;
320 size_t num_domains = 0;
321 int i;
323 if (_gc_server_list != NULL) {
324 gc_server_list_destroy();
327 if (!wcache_tdc_fetch_list(&domains, &num_domains)) {
328 nt_status = NT_STATUS_CANT_ACCESS_DOMAIN_INFO;
329 BAIL_ON_NTSTATUS_ERROR(nt_status);
332 /* Find our forest first. Have to try all domains here starting
333 with our own. gc_add_forest() filters duplicates */
335 nt_status = gc_add_forest(lp_realm());
336 WARN_ON_NTSTATUS_ERROR(nt_status);
338 for (i=0; i<num_domains; i++) {
339 uint32_t flags = (NETR_TRUST_FLAG_IN_FOREST);
341 /* I think we should be able to break out of loop once
342 we add a GC for our forest and not have to test every one.
343 In fact, this entire loop is probably irrelevant since
344 the GC location code should always find a GC given lp_realm().
345 Will have to spend time testing before making the change.
346 --jerry */
348 if ((domains[i].trust_flags & flags) == flags) {
349 nt_status = gc_add_forest(domains[i].dns_name);
350 WARN_ON_NTSTATUS_ERROR(nt_status);
351 /* Don't BAIL here since not every domain may
352 have a GC server */
356 /* Now add trusted forests. gc_add_forest() will filter out
357 duplicates. Check everything with an incoming trust path
358 that is not in our own forest. */
360 for (i=0; i<num_domains; i++) {
361 uint32_t flags = domains[i].trust_flags;
362 uint32_t attribs = domains[i].trust_attribs;
364 /* Skip non_AD domains */
366 if (strlen(domains[i].dns_name) == 0) {
367 continue;
370 /* Only add a GC for a forest outside of our own.
371 Ignore QUARANTINED/EXTERNAL trusts */
373 if ((flags & NETR_TRUST_FLAG_INBOUND)
374 && !(flags & NETR_TRUST_FLAG_IN_FOREST)
375 && (attribs & NETR_TRUST_ATTRIBUTE_FOREST_TRANSITIVE))
377 nt_status = gc_add_forest(domains[i].dns_name);
378 WARN_ON_NTSTATUS_ERROR(nt_status);
382 nt_status = NT_STATUS_OK;
384 done:
385 if (!NT_STATUS_IS_OK(nt_status)) {
386 DEBUG(2,("LWI: Failed to initialized GC list (%s)\n",
387 nt_errstr(nt_status)));
390 TALLOC_FREE(domains);
392 return nt_status;
396 /**********************************************************************
397 *********************************************************************/
399 struct gc_info *gc_search_start(void)
401 NTSTATUS nt_status = NT_STATUS_OK;
402 struct gc_info *gc = gc_list_head();
404 if (!gc) {
405 nt_status = gc_init_list();
406 BAIL_ON_NTSTATUS_ERROR(nt_status);
408 gc = gc_list_head();
411 done:
412 if (!NT_STATUS_IS_OK(nt_status)) {
413 DEBUG(2,("LWI: Failed to initialize GC list (%s)\n",
414 nt_errstr(nt_status)));
417 return gc;
420 /**********************************************************************
421 Search Global Catalog. Always search our own forest. The flags set
422 controls whether or not we search cross forest. Assume that the
423 resulting set is always returned from one GC so that we don't have to
424 both combining the LDAPMessage * results
425 *********************************************************************/
427 NTSTATUS gc_search_forest(struct gc_info *gc,
428 LDAPMessage **msg,
429 const char *filter)
431 NTSTATUS nt_status = NT_STATUS_UNSUCCESSFUL;
432 ADS_STATUS ads_status = ADS_ERROR_NT(NT_STATUS_UNSUCCESSFUL);
433 const char *attrs[] = {"*", NULL};
434 LDAPMessage *m = NULL;
436 if (!gc || !msg || !filter) {
437 nt_status = NT_STATUS_INVALID_PARAMETER;
438 BAIL_ON_NTSTATUS_ERROR(nt_status);
441 /* When you have multiple domain trees in a forest, the
442 GC will search all naming contexts when you send it
443 and empty ("") base search suffix. Tested against
444 Windows 2003. */
446 ads_status = cell_do_search(gc->forest_cell, "",
447 LDAP_SCOPE_SUBTREE, filter, attrs, &m);
448 nt_status = ads_ntstatus(ads_status);
449 BAIL_ON_NTSTATUS_ERROR(nt_status);
451 *msg = m;
453 done:
454 if (!NT_STATUS_IS_OK(nt_status)) {
455 DEBUG(2,("LWI: Forest wide search %s failed (%s)\n",
456 filter, nt_errstr(nt_status)));
459 return nt_status;
462 /**********************************************************************
463 Search all forests via GC and return the results in an array of
464 ADS_STRUCT/LDAPMessage pairs.
465 *********************************************************************/
467 NTSTATUS gc_search_all_forests(const char *filter,
468 ADS_STRUCT ***ads_list,
469 LDAPMessage ***msg_list,
470 int *num_resp, uint32_t flags)
472 NTSTATUS nt_status = NT_STATUS_UNSUCCESSFUL;
473 struct gc_info *gc = NULL;
474 uint32_t test_flags = ADEX_GC_SEARCH_CHECK_UNIQUE;
476 *ads_list = NULL;
477 *msg_list = NULL;
478 *num_resp = 0;
480 if ((gc = gc_search_start()) == NULL) {
481 nt_status = NT_STATUS_INVALID_DOMAIN_STATE;
482 BAIL_ON_NTSTATUS_ERROR(nt_status);
485 while (gc) {
486 LDAPMessage *m = NULL;
488 nt_status = gc_search_forest(gc, &m, filter);
489 if (!NT_STATUS_IS_OK(nt_status)) {
490 gc = gc->next;
491 continue;
494 nt_status = add_ads_result_to_array(cell_connection(gc->forest_cell),
495 m, ads_list, msg_list,
496 num_resp);
497 BAIL_ON_NTSTATUS_ERROR(nt_status);
499 /* If there can only be one match, then we are done */
501 if ((*num_resp > 0) && ((flags & test_flags) == test_flags)) {
502 break;
505 gc = gc->next;
508 if (*num_resp == 0) {
509 nt_status = NT_STATUS_OBJECT_NAME_NOT_FOUND;
510 BAIL_ON_NTSTATUS_ERROR(nt_status);
513 nt_status = NT_STATUS_OK;
515 done:
516 return nt_status;
519 /**********************************************************************
520 Search all forests via GC and return the results in an array of
521 ADS_STRUCT/LDAPMessage pairs.
522 *********************************************************************/
524 NTSTATUS gc_search_all_forests_unique(const char *filter,
525 ADS_STRUCT **ads,
526 LDAPMessage **msg)
528 ADS_STRUCT **ads_list = NULL;
529 LDAPMessage **msg_list = NULL;
530 int num_resp;
531 NTSTATUS nt_status = NT_STATUS_UNSUCCESSFUL;
533 nt_status = gc_search_all_forests(filter, &ads_list,
534 &msg_list, &num_resp,
535 ADEX_GC_SEARCH_CHECK_UNIQUE);
536 BAIL_ON_NTSTATUS_ERROR(nt_status);
538 nt_status = check_result_unique(ads_list[0], msg_list[0]);
539 BAIL_ON_NTSTATUS_ERROR(nt_status);
541 *ads = ads_list[0];
542 *msg = msg_list[0];
544 done:
545 /* Be care that we don't free the msg result being returned */
547 if (!NT_STATUS_IS_OK(nt_status)) {
548 free_result_array(ads_list, msg_list, num_resp);
549 } else {
550 talloc_destroy(ads_list);
551 talloc_destroy(msg_list);
554 return nt_status;
557 /*********************************************************************
558 ********************************************************************/
560 NTSTATUS gc_name_to_sid(const char *domain,
561 const char *name,
562 struct dom_sid *sid,
563 enum lsa_SidType *sid_type)
565 TALLOC_CTX *frame = talloc_stackframe();
566 char *p, *name_user;
567 NTSTATUS nt_status = NT_STATUS_UNSUCCESSFUL;
568 char *name_filter;
569 ADS_STRUCT *ads = NULL;
570 LDAPMessage *msg = NULL;
571 LDAPMessage *e = NULL;
572 char *dn = NULL;
573 char *dns_domain = NULL;
574 ADS_STRUCT **ads_list = NULL;
575 LDAPMessage **msg_list = NULL;
576 int num_resp = 0;
577 int i;
579 /* Strip the "DOMAIN\" prefix if necessary and search for
580 a matching sAMAccountName in the forest */
582 if ((p = strchr_m( name, '\\' )) == NULL)
583 name_user = talloc_strdup( frame, name );
584 else
585 name_user = talloc_strdup( frame, p+1 );
586 BAIL_ON_PTR_ERROR(name_user, nt_status);
588 name_filter = talloc_asprintf(frame, "(sAMAccountName=%s)", name_user);
589 BAIL_ON_PTR_ERROR(name_filter, nt_status);
591 nt_status = gc_search_all_forests(name_filter, &ads_list,
592 &msg_list, &num_resp, 0);
593 BAIL_ON_NTSTATUS_ERROR(nt_status);
595 /* Assume failure until we know otherwise*/
597 nt_status = NT_STATUS_OBJECT_NAME_NOT_FOUND;
599 /* Match the domain name from the DN */
601 for (i=0; i<num_resp; i++) {
602 ads = ads_list[i];
603 msg = msg_list[i];
605 e = ads_first_entry(ads, msg);
606 while (e) {
607 struct winbindd_tdc_domain *domain_rec;
609 dn = ads_get_dn(ads, frame, e);
610 BAIL_ON_PTR_ERROR(dn, nt_status);
612 dns_domain = cell_dn_to_dns(dn);
613 TALLOC_FREE(dn);
614 BAIL_ON_PTR_ERROR(dns_domain, nt_status);
616 domain_rec = wcache_tdc_fetch_domain(frame, dns_domain);
617 SAFE_FREE(dns_domain);
619 /* Ignore failures and continue the search */
621 if (!domain_rec) {
622 e = ads_next_entry(ads, e);
623 continue;
626 /* Check for a match on the domain name */
628 if (strequal(domain, domain_rec->domain_name)) {
629 if (!ads_pull_sid(ads, e, "objectSid", sid)) {
630 nt_status = NT_STATUS_INVALID_SID;
631 BAIL_ON_NTSTATUS_ERROR(nt_status);
634 talloc_destroy(domain_rec);
636 nt_status = get_sid_type(ads, msg, sid_type);
637 BAIL_ON_NTSTATUS_ERROR(nt_status);
639 /* We're done! */
640 nt_status = NT_STATUS_OK;
641 break;
644 /* once more around thew merry-go-round */
646 talloc_destroy(domain_rec);
647 e = ads_next_entry(ads, e);
651 done:
652 free_result_array(ads_list, msg_list, num_resp);
653 talloc_destroy(frame);
655 return nt_status;
658 /********************************************************************
659 Pull an attribute string value
660 *******************************************************************/
662 static NTSTATUS get_object_account_name(ADS_STRUCT *ads,
663 LDAPMessage *msg,
664 char **name)
666 NTSTATUS nt_status = NT_STATUS_UNSUCCESSFUL;
667 char *sam_name = NULL;
668 struct winbindd_tdc_domain *domain_rec = NULL;
669 char *dns_domain = NULL;
670 char *dn = NULL;
671 TALLOC_CTX *frame = talloc_stackframe();
672 int len;
674 /* Check parameters */
676 if (!ads || !msg || !name) {
677 nt_status = NT_STATUS_INVALID_PARAMETER;
678 BAIL_ON_NTSTATUS_ERROR(nt_status);
681 /* get the name and domain */
683 dn = ads_get_dn(ads, frame, msg);
684 BAIL_ON_PTR_ERROR(dn, nt_status);
686 DEBUG(10,("get_object_account_name: dn = \"%s\"\n", dn));
688 dns_domain = cell_dn_to_dns(dn);
689 TALLOC_FREE(dn);
690 BAIL_ON_PTR_ERROR(dns_domain, nt_status);
692 domain_rec = wcache_tdc_fetch_domain(frame, dns_domain);
693 SAFE_FREE(dns_domain);
695 if (!domain_rec) {
696 nt_status = NT_STATUS_TRUSTED_DOMAIN_FAILURE;
697 BAIL_ON_NTSTATUS_ERROR(nt_status);
700 sam_name = ads_pull_string(ads, frame, msg, "sAMAccountName");
701 BAIL_ON_PTR_ERROR(sam_name, nt_status);
703 len = asprintf(name, "%s\\%s", domain_rec->domain_name, sam_name);
704 if (len == -1) {
705 *name = NULL;
706 BAIL_ON_PTR_ERROR((*name), nt_status);
709 nt_status = NT_STATUS_OK;
711 done:
712 talloc_destroy(frame);
714 return nt_status;
717 /*********************************************************************
718 ********************************************************************/
720 NTSTATUS gc_sid_to_name(const struct dom_sid *sid,
721 char **name,
722 enum lsa_SidType *sid_type)
724 TALLOC_CTX *frame = talloc_stackframe();
725 NTSTATUS nt_status = NT_STATUS_UNSUCCESSFUL;
726 char *filter;
727 ADS_STRUCT *ads = NULL;
728 LDAPMessage *msg = NULL;
729 char *sid_string;
731 *name = NULL;
733 sid_string = ldap_encode_ndr_dom_sid(frame, sid);
734 BAIL_ON_PTR_ERROR(sid_string, nt_status);
736 filter = talloc_asprintf(frame, "(objectSid=%s)", sid_string);
737 TALLOC_FREE(sid_string);
738 BAIL_ON_PTR_ERROR(filter, nt_status);
740 nt_status = gc_search_all_forests_unique(filter, &ads, &msg);
741 BAIL_ON_NTSTATUS_ERROR(nt_status);
743 nt_status = get_object_account_name(ads, msg, name);
744 BAIL_ON_NTSTATUS_ERROR(nt_status);
746 nt_status = get_sid_type(ads, msg, sid_type);
747 BAIL_ON_NTSTATUS_ERROR(nt_status);
749 done:
750 ads_msgfree(ads, msg);
751 talloc_destroy(frame);
753 return nt_status;
756 /**********************************************************************
757 *********************************************************************/
759 NTSTATUS add_ads_result_to_array(ADS_STRUCT *ads,
760 LDAPMessage *msg,
761 ADS_STRUCT ***ads_list,
762 LDAPMessage ***msg_list,
763 int *size)
765 NTSTATUS nt_status = NT_STATUS_UNSUCCESSFUL;
766 ADS_STRUCT **ads_tmp = NULL;
767 LDAPMessage **msg_tmp = NULL;
768 int count = *size;
770 if (!ads || !msg) {
771 nt_status = NT_STATUS_INVALID_PARAMETER;
772 BAIL_ON_NTSTATUS_ERROR(nt_status);
775 #if 0
776 /* Don't add a response with no entries */
778 if (ads_count_replies(ads, msg) == 0) {
779 return NT_STATUS_OK;
781 #endif
783 if (count == 0) {
784 ads_tmp = talloc_array(NULL, ADS_STRUCT*, 1);
785 BAIL_ON_PTR_ERROR(ads_tmp, nt_status);
787 msg_tmp = talloc_array(NULL, LDAPMessage*, 1);
788 BAIL_ON_PTR_ERROR(msg_tmp, nt_status);
789 } else {
790 ads_tmp = talloc_realloc(*ads_list, *ads_list, ADS_STRUCT*,
791 count+1);
792 BAIL_ON_PTR_ERROR(ads_tmp, nt_status);
794 msg_tmp = talloc_realloc(*msg_list, *msg_list, LDAPMessage*,
795 count+1);
796 BAIL_ON_PTR_ERROR(msg_tmp, nt_status);
799 ads_tmp[count] = ads;
800 msg_tmp[count] = msg;
801 count++;
803 *ads_list = ads_tmp;
804 *msg_list = msg_tmp;
805 *size = count;
807 nt_status = NT_STATUS_OK;
809 done:
810 if (!NT_STATUS_IS_OK(nt_status)) {
811 talloc_destroy(ads_tmp);
812 talloc_destroy(msg_tmp);
815 return nt_status;
818 /**********************************************************************
819 Frees search results. Do not free the ads_list as these are
820 references back to the GC search structures.
821 *********************************************************************/
823 void free_result_array(ADS_STRUCT **ads_list,
824 LDAPMessage **msg_list,
825 int num_resp)
827 int i;
829 for (i=0; i<num_resp; i++) {
830 ads_msgfree(ads_list[i], msg_list[i]);
833 talloc_destroy(ads_list);
834 talloc_destroy(msg_list);
837 /**********************************************************************
838 Check that we have exactly one entry from the search
839 *********************************************************************/
841 NTSTATUS check_result_unique(ADS_STRUCT *ads, LDAPMessage *msg)
843 NTSTATUS nt_status;
844 int count;
846 count = ads_count_replies(ads, msg);
848 if (count <= 0) {
849 nt_status = NT_STATUS_OBJECT_NAME_NOT_FOUND;
850 BAIL_ON_NTSTATUS_ERROR(nt_status);
853 if (count > 1) {
854 nt_status = NT_STATUS_DUPLICATE_NAME;
855 BAIL_ON_NTSTATUS_ERROR(nt_status);
858 nt_status = NT_STATUS_OK;
860 done:
861 return nt_status;