upgradeprovision: do not try to remove/change attribute before the RID Set object...
[Samba/vl.git] / source3 / winbindd / idmap_adex / gc_util.c
blob56bd9542f6682976d82a53969093bf319cdbcaa5
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"
27 #undef DBGC_CLASS
28 #define DBGC_CLASS DBGC_IDMAP
30 static struct gc_info *_gc_server_list = NULL;
33 /**********************************************************************
34 *********************************************************************/
36 static struct gc_info *gc_list_head(void)
38 return _gc_server_list;
41 /**********************************************************************
42 Checks if either of the domains is a subdomain of the other
43 *********************************************************************/
45 static bool is_subdomain(const char* a, const char *b)
47 char *s;
48 TALLOC_CTX *frame = talloc_stackframe();
49 char *x, *y;
50 bool ret = false;
52 /* Trivial cases */
54 if (!a && !b)
55 return true;
57 if (!a || !b)
58 return false;
60 /* Normalize the case */
62 x = talloc_strdup(frame, a);
63 y = talloc_strdup(frame, b);
64 if (!x || !y) {
65 ret = false;
66 goto done;
69 strupper_m(x);
70 strupper_m(y);
72 /* Exact match */
74 if (strcmp(x, y) == 0) {
75 ret = true;
76 goto done;
79 /* Check for trailing substrings */
81 s = strstr_m(x, y);
82 if (s && (strlen(s) == strlen(y))) {
83 ret = true;
84 goto done;
87 s = strstr_m(y, x);
88 if (s && (strlen(s) == strlen(x))) {
89 ret = true;
90 goto done;
93 done:
94 talloc_destroy(frame);
96 return ret;
99 /**********************************************************************
100 *********************************************************************/
102 NTSTATUS gc_find_forest_root(struct gc_info *gc, const char *domain)
104 ADS_STRUCT *ads = NULL;
105 ADS_STATUS ads_status;
106 NTSTATUS nt_status = NT_STATUS_UNSUCCESSFUL;
107 struct NETLOGON_SAM_LOGON_RESPONSE_EX cldap_reply;
108 TALLOC_CTX *frame = talloc_stackframe();
110 if (!gc || !domain) {
111 return NT_STATUS_INVALID_PARAMETER;
114 ZERO_STRUCT(cldap_reply);
116 ads = ads_init(domain, NULL, NULL);
117 BAIL_ON_PTR_ERROR(ads, nt_status);
119 ads->auth.flags = ADS_AUTH_NO_BIND;
120 ads_status = ads_connect(ads);
121 if (!ADS_ERR_OK(ads_status)) {
122 DEBUG(4, ("find_forest_root: ads_connect(%s) failed! (%s)\n",
123 domain, ads_errstr(ads_status)));
125 nt_status = ads_ntstatus(ads_status);
126 BAIL_ON_NTSTATUS_ERROR(nt_status);
128 if (!ads_cldap_netlogon_5(frame,
129 ads->config.ldap_server_name,
130 ads->config.realm,
131 &cldap_reply))
133 DEBUG(4,("find_forest_root: Failed to get a CLDAP reply from %s!\n",
134 ads->server.ldap_server));
135 nt_status = NT_STATUS_IO_TIMEOUT;
136 BAIL_ON_NTSTATUS_ERROR(nt_status);
139 gc->forest_name = talloc_strdup(gc, cldap_reply.forest);
140 BAIL_ON_PTR_ERROR(gc->forest_name, nt_status);
142 done:
143 if (ads) {
144 ads_destroy(&ads);
147 return nt_status;
150 /**********************************************************************
151 *********************************************************************/
153 static NTSTATUS gc_add_forest(const char *domain)
155 NTSTATUS nt_status = NT_STATUS_UNSUCCESSFUL;
156 struct gc_info *gc = NULL;
157 struct gc_info *find_gc = NULL;
158 char *dn;
159 ADS_STRUCT *ads = NULL;
160 struct likewise_cell *primary_cell = NULL;
162 primary_cell = cell_list_head();
163 if (!primary_cell) {
164 nt_status = NT_STATUS_INVALID_SERVER_STATE;
165 BAIL_ON_NTSTATUS_ERROR(nt_status);
168 /* Check for duplicates based on domain name first as this
169 requires no connection */
171 find_gc = gc_list_head();
172 while (find_gc) {
173 if (strequal (find_gc->forest_name, domain))
174 break;
175 find_gc = find_gc->next;
178 if (find_gc) {
179 DEBUG(10,("gc_add_forest: %s already in list\n", find_gc->forest_name));
180 return NT_STATUS_OK;
183 if ((gc = TALLOC_ZERO_P(NULL, struct gc_info)) == NULL) {
184 nt_status = NT_STATUS_NO_MEMORY;
185 BAIL_ON_NTSTATUS_ERROR(nt_status);
188 /* Query the rootDSE for the forest root naming conect first.
189 Check that the a GC server for the forest has not already
190 been added */
192 nt_status = gc_find_forest_root(gc, domain);
193 BAIL_ON_NTSTATUS_ERROR(nt_status);
195 find_gc = gc_list_head();
196 while (find_gc) {
197 if (strequal (find_gc->forest_name, gc->forest_name))
198 break;
199 find_gc = find_gc->next;
202 if (find_gc) {
203 DEBUG(10,("gc_add_forest: Forest %s already in list\n",
204 find_gc->forest_name));
205 return NT_STATUS_OK;
208 /* Not found, so add it here. Make sure we connect to
209 a DC in _this_ domain and not the forest root. */
211 dn = ads_build_dn(gc->forest_name);
212 BAIL_ON_PTR_ERROR(dn, nt_status);
214 gc->search_base = talloc_strdup(gc, dn);
215 SAFE_FREE(dn);
216 BAIL_ON_PTR_ERROR(gc->search_base, nt_status);
218 #if 0
219 /* Can't use cell_connect_dn() here as there is no way to
220 specifiy the LWCELL_FLAG_GC_CELL flag setting for cell_connect() */
222 nt_status = cell_connect_dn(&gc->forest_cell, gc->search_base);
223 BAIL_ON_NTSTATUS_ERROR(nt_status);
224 #else
226 gc->forest_cell = cell_new();
227 BAIL_ON_PTR_ERROR(gc->forest_cell, nt_status);
229 /* Set the DNS domain, dn, etc ... and add it to the list */
231 cell_set_dns_domain(gc->forest_cell, gc->forest_name);
232 cell_set_dn(gc->forest_cell, gc->search_base);
233 cell_set_flags(gc->forest_cell, LWCELL_FLAG_GC_CELL);
234 #endif
236 /* It is possible to belong to a non-forest cell and a
237 non-provisioned forest (at our domain levele). In that
238 case, we should just inherit the flags from our primary
239 cell since the GC searches will match our own schema
240 model. */
242 if (strequal(primary_cell->forest_name, gc->forest_name)
243 || is_subdomain(primary_cell->dns_domain, gc->forest_name))
245 cell_set_flags(gc->forest_cell, cell_flags(primary_cell));
246 } else {
247 /* outside of our domain */
249 nt_status = cell_connect(gc->forest_cell);
250 BAIL_ON_NTSTATUS_ERROR(nt_status);
252 nt_status = cell_lookup_settings(gc->forest_cell);
253 BAIL_ON_NTSTATUS_ERROR(nt_status);
255 /* Drop the connection now that we have the settings */
257 ads = cell_connection(gc->forest_cell);
258 ads_destroy(&ads);
259 cell_set_connection(gc->forest_cell, NULL);
262 DLIST_ADD_END(_gc_server_list, gc, struct gc_info*);
264 DEBUG(10,("gc_add_forest: Added %s to Global Catalog list of servers\n",
265 gc->forest_name));
267 nt_status = NT_STATUS_OK;
269 done:
270 if (!NT_STATUS_IS_OK(nt_status)) {
271 talloc_destroy(gc);
272 DEBUG(3,("LWI: Failed to add new GC connection for %s (%s)\n",
273 domain, nt_errstr(nt_status)));
276 return nt_status;
279 /**********************************************************************
280 *********************************************************************/
282 static void gc_server_list_destroy(void)
284 struct gc_info *gc = gc_list_head();
286 while (gc) {
287 struct gc_info *p = gc->next;
289 cell_destroy(gc->forest_cell);
290 talloc_destroy(gc);
292 gc = p;
295 _gc_server_list = NULL;
297 return;
300 /**********************************************************************
301 Setup the initial list of forests and initial the forest cell
302 settings for each. FIXME!!!
303 *********************************************************************/
305 NTSTATUS gc_init_list(void)
307 NTSTATUS nt_status = NT_STATUS_UNSUCCESSFUL;
308 struct winbindd_tdc_domain *domains = NULL;
309 size_t num_domains = 0;
310 int i;
312 if (_gc_server_list != NULL) {
313 gc_server_list_destroy();
316 if (!wcache_tdc_fetch_list(&domains, &num_domains)) {
317 nt_status = NT_STATUS_CANT_ACCESS_DOMAIN_INFO;
318 BAIL_ON_NTSTATUS_ERROR(nt_status);
321 /* Find our forest first. Have to try all domains here starting
322 with our own. gc_add_forest() filters duplicates */
324 nt_status = gc_add_forest(lp_realm());
325 WARN_ON_NTSTATUS_ERROR(nt_status);
327 for (i=0; i<num_domains; i++) {
328 uint32_t flags = (NETR_TRUST_FLAG_IN_FOREST);
330 /* I think we should be able to break out of loop once
331 we add a GC for our forest and not have to test every one.
332 In fact, this entire loop is probably irrelevant since
333 the GC location code should always find a GC given lp_realm().
334 Will have to spend time testing before making the change.
335 --jerry */
337 if ((domains[i].trust_flags & flags) == flags) {
338 nt_status = gc_add_forest(domains[i].dns_name);
339 WARN_ON_NTSTATUS_ERROR(nt_status);
340 /* Don't BAIL here since not every domain may
341 have a GC server */
345 /* Now add trusted forests. gc_add_forest() will filter out
346 duplicates. Check everything with an incoming trust path
347 that is not in our own forest. */
349 for (i=0; i<num_domains; i++) {
350 uint32_t flags = domains[i].trust_flags;
351 uint32_t attribs = domains[i].trust_attribs;
353 /* Skip non_AD domains */
355 if (strlen(domains[i].dns_name) == 0) {
356 continue;
359 /* Only add a GC for a forest outside of our own.
360 Ignore QUARANTINED/EXTERNAL trusts */
362 if ((flags & NETR_TRUST_FLAG_INBOUND)
363 && !(flags & NETR_TRUST_FLAG_IN_FOREST)
364 && (attribs & NETR_TRUST_ATTRIBUTE_FOREST_TRANSITIVE))
366 nt_status = gc_add_forest(domains[i].dns_name);
367 WARN_ON_NTSTATUS_ERROR(nt_status);
371 nt_status = NT_STATUS_OK;
373 done:
374 if (!NT_STATUS_IS_OK(nt_status)) {
375 DEBUG(2,("LWI: Failed to initialized GC list (%s)\n",
376 nt_errstr(nt_status)));
379 TALLOC_FREE(domains);
381 return nt_status;
385 /**********************************************************************
386 *********************************************************************/
388 struct gc_info *gc_search_start(void)
390 NTSTATUS nt_status = NT_STATUS_OK;
391 struct gc_info *gc = gc_list_head();
393 if (!gc) {
394 nt_status = gc_init_list();
395 BAIL_ON_NTSTATUS_ERROR(nt_status);
397 gc = gc_list_head();
400 done:
401 if (!NT_STATUS_IS_OK(nt_status)) {
402 DEBUG(2,("LWI: Failed to initialize GC list (%s)\n",
403 nt_errstr(nt_status)));
406 return gc;
409 /**********************************************************************
410 Search Global Catalog. Always search our own forest. The flags set
411 controls whether or not we search cross forest. Assume that the
412 resulting set is always returned from one GC so that we don't have to
413 both combining the LDAPMessage * results
414 *********************************************************************/
416 NTSTATUS gc_search_forest(struct gc_info *gc,
417 LDAPMessage **msg,
418 const char *filter)
420 NTSTATUS nt_status = NT_STATUS_UNSUCCESSFUL;
421 ADS_STATUS ads_status = ADS_ERROR_NT(NT_STATUS_UNSUCCESSFUL);
422 const char *attrs[] = {"*", NULL};
423 LDAPMessage *m = NULL;
425 if (!gc || !msg || !filter) {
426 nt_status = NT_STATUS_INVALID_PARAMETER;
427 BAIL_ON_NTSTATUS_ERROR(nt_status);
430 /* When you have multiple domain trees in a forest, the
431 GC will search all naming contexts when you send it
432 and empty ("") base search suffix. Tested against
433 Windows 2003. */
435 ads_status = cell_do_search(gc->forest_cell, "",
436 LDAP_SCOPE_SUBTREE, filter, attrs, &m);
437 nt_status = ads_ntstatus(ads_status);
438 BAIL_ON_NTSTATUS_ERROR(nt_status);
440 *msg = m;
442 done:
443 if (!NT_STATUS_IS_OK(nt_status)) {
444 DEBUG(2,("LWI: Forest wide search %s failed (%s)\n",
445 filter, nt_errstr(nt_status)));
448 return nt_status;
451 /**********************************************************************
452 Search all forests via GC and return the results in an array of
453 ADS_STRUCT/LDAPMessage pairs.
454 *********************************************************************/
456 NTSTATUS gc_search_all_forests(const char *filter,
457 ADS_STRUCT ***ads_list,
458 LDAPMessage ***msg_list,
459 int *num_resp, uint32_t flags)
461 NTSTATUS nt_status = NT_STATUS_UNSUCCESSFUL;
462 struct gc_info *gc = NULL;
463 uint32_t test_flags = ADEX_GC_SEARCH_CHECK_UNIQUE;
465 *ads_list = NULL;
466 *msg_list = NULL;
467 *num_resp = 0;
469 if ((gc = gc_search_start()) == NULL) {
470 nt_status = NT_STATUS_INVALID_DOMAIN_STATE;
471 BAIL_ON_NTSTATUS_ERROR(nt_status);
474 while (gc) {
475 LDAPMessage *m = NULL;
477 nt_status = gc_search_forest(gc, &m, filter);
478 if (!NT_STATUS_IS_OK(nt_status)) {
479 gc = gc->next;
480 continue;
483 nt_status = add_ads_result_to_array(cell_connection(gc->forest_cell),
484 m, ads_list, msg_list,
485 num_resp);
486 BAIL_ON_NTSTATUS_ERROR(nt_status);
488 /* If there can only be one match, then we are done */
490 if ((*num_resp > 0) && ((flags & test_flags) == test_flags)) {
491 break;
494 gc = gc->next;
497 if (*num_resp == 0) {
498 nt_status = NT_STATUS_OBJECT_NAME_NOT_FOUND;
499 BAIL_ON_NTSTATUS_ERROR(nt_status);
502 nt_status = NT_STATUS_OK;
504 done:
505 return nt_status;
508 /**********************************************************************
509 Search all forests via GC and return the results in an array of
510 ADS_STRUCT/LDAPMessage pairs.
511 *********************************************************************/
513 NTSTATUS gc_search_all_forests_unique(const char *filter,
514 ADS_STRUCT **ads,
515 LDAPMessage **msg)
517 ADS_STRUCT **ads_list = NULL;
518 LDAPMessage **msg_list = NULL;
519 int num_resp;
520 NTSTATUS nt_status = NT_STATUS_UNSUCCESSFUL;
522 nt_status = gc_search_all_forests(filter, &ads_list,
523 &msg_list, &num_resp,
524 ADEX_GC_SEARCH_CHECK_UNIQUE);
525 BAIL_ON_NTSTATUS_ERROR(nt_status);
527 nt_status = check_result_unique(ads_list[0], msg_list[0]);
528 BAIL_ON_NTSTATUS_ERROR(nt_status);
530 *ads = ads_list[0];
531 *msg = msg_list[0];
533 done:
534 /* Be care that we don't free the msg result being returned */
536 if (!NT_STATUS_IS_OK(nt_status)) {
537 free_result_array(ads_list, msg_list, num_resp);
538 } else {
539 talloc_destroy(ads_list);
540 talloc_destroy(msg_list);
543 return nt_status;
546 /*********************************************************************
547 ********************************************************************/
549 NTSTATUS gc_name_to_sid(const char *domain,
550 const char *name,
551 struct dom_sid *sid,
552 enum lsa_SidType *sid_type)
554 TALLOC_CTX *frame = talloc_stackframe();
555 char *p, *name_user;
556 NTSTATUS nt_status = NT_STATUS_UNSUCCESSFUL;
557 char *name_filter;
558 ADS_STRUCT *ads = NULL;
559 LDAPMessage *msg = NULL;
560 LDAPMessage *e = NULL;
561 char *dn = NULL;
562 char *dns_domain = NULL;
563 ADS_STRUCT **ads_list = NULL;
564 LDAPMessage **msg_list = NULL;
565 int num_resp = 0;
566 int i;
568 /* Strip the "DOMAIN\" prefix if necessary and search for
569 a matching sAMAccountName in the forest */
571 if ((p = strchr_m( name, '\\' )) == NULL)
572 name_user = talloc_strdup( frame, name );
573 else
574 name_user = talloc_strdup( frame, p+1 );
575 BAIL_ON_PTR_ERROR(name_user, nt_status);
577 name_filter = talloc_asprintf(frame, "(sAMAccountName=%s)", name_user);
578 BAIL_ON_PTR_ERROR(name_filter, nt_status);
580 nt_status = gc_search_all_forests(name_filter, &ads_list,
581 &msg_list, &num_resp, 0);
582 BAIL_ON_NTSTATUS_ERROR(nt_status);
584 /* Assume failure until we know otherwise*/
586 nt_status = NT_STATUS_OBJECT_NAME_NOT_FOUND;
588 /* Match the domain name from the DN */
590 for (i=0; i<num_resp; i++) {
591 ads = ads_list[i];
592 msg = msg_list[i];
594 e = ads_first_entry(ads, msg);
595 while (e) {
596 struct winbindd_tdc_domain *domain_rec;
598 dn = ads_get_dn(ads, frame, e);
599 BAIL_ON_PTR_ERROR(dn, nt_status);
601 dns_domain = cell_dn_to_dns(dn);
602 TALLOC_FREE(dn);
603 BAIL_ON_PTR_ERROR(dns_domain, nt_status);
605 domain_rec = wcache_tdc_fetch_domain(frame, dns_domain);
606 SAFE_FREE(dns_domain);
608 /* Ignore failures and continue the search */
610 if (!domain_rec) {
611 e = ads_next_entry(ads, e);
612 continue;
615 /* Check for a match on the domain name */
617 if (strequal(domain, domain_rec->domain_name)) {
618 if (!ads_pull_sid(ads, e, "objectSid", sid)) {
619 nt_status = NT_STATUS_INVALID_SID;
620 BAIL_ON_NTSTATUS_ERROR(nt_status);
623 talloc_destroy(domain_rec);
625 nt_status = get_sid_type(ads, msg, sid_type);
626 BAIL_ON_NTSTATUS_ERROR(nt_status);
628 /* We're done! */
629 nt_status = NT_STATUS_OK;
630 break;
633 /* once more around thew merry-go-round */
635 talloc_destroy(domain_rec);
636 e = ads_next_entry(ads, e);
640 done:
641 free_result_array(ads_list, msg_list, num_resp);
642 talloc_destroy(frame);
644 return nt_status;
647 /********************************************************************
648 Pull an attribute string value
649 *******************************************************************/
651 static NTSTATUS get_object_account_name(ADS_STRUCT *ads,
652 LDAPMessage *msg,
653 char **name)
655 NTSTATUS nt_status = NT_STATUS_UNSUCCESSFUL;
656 char *sam_name = NULL;
657 struct winbindd_tdc_domain *domain_rec = NULL;
658 char *dns_domain = NULL;
659 char *dn = NULL;
660 TALLOC_CTX *frame = talloc_stackframe();
661 int len;
663 /* Check parameters */
665 if (!ads || !msg || !name) {
666 nt_status = NT_STATUS_INVALID_PARAMETER;
667 BAIL_ON_NTSTATUS_ERROR(nt_status);
670 /* get the name and domain */
672 dn = ads_get_dn(ads, frame, msg);
673 BAIL_ON_PTR_ERROR(dn, nt_status);
675 DEBUG(10,("get_object_account_name: dn = \"%s\"\n", dn));
677 dns_domain = cell_dn_to_dns(dn);
678 TALLOC_FREE(dn);
679 BAIL_ON_PTR_ERROR(dns_domain, nt_status);
681 domain_rec = wcache_tdc_fetch_domain(frame, dns_domain);
682 SAFE_FREE(dns_domain);
684 if (!domain_rec) {
685 nt_status = NT_STATUS_TRUSTED_DOMAIN_FAILURE;
686 BAIL_ON_NTSTATUS_ERROR(nt_status);
689 sam_name = ads_pull_string(ads, frame, msg, "sAMAccountName");
690 BAIL_ON_PTR_ERROR(sam_name, nt_status);
692 len = asprintf(name, "%s\\%s", domain_rec->domain_name, sam_name);
693 if (len == -1) {
694 *name = NULL;
695 BAIL_ON_PTR_ERROR((*name), nt_status);
698 nt_status = NT_STATUS_OK;
700 done:
701 talloc_destroy(frame);
703 return nt_status;
706 /*********************************************************************
707 ********************************************************************/
709 NTSTATUS gc_sid_to_name(const struct dom_sid *sid,
710 char **name,
711 enum lsa_SidType *sid_type)
713 TALLOC_CTX *frame = talloc_stackframe();
714 NTSTATUS nt_status = NT_STATUS_UNSUCCESSFUL;
715 char *filter;
716 ADS_STRUCT *ads = NULL;
717 LDAPMessage *msg = NULL;
718 char *sid_string;
720 *name = NULL;
722 sid_string = sid_binstring(frame, sid);
723 BAIL_ON_PTR_ERROR(sid_string, nt_status);
725 filter = talloc_asprintf(frame, "(objectSid=%s)", sid_string);
726 TALLOC_FREE(sid_string);
727 BAIL_ON_PTR_ERROR(filter, nt_status);
729 nt_status = gc_search_all_forests_unique(filter, &ads, &msg);
730 BAIL_ON_NTSTATUS_ERROR(nt_status);
732 nt_status = get_object_account_name(ads, msg, name);
733 BAIL_ON_NTSTATUS_ERROR(nt_status);
735 nt_status = get_sid_type(ads, msg, sid_type);
736 BAIL_ON_NTSTATUS_ERROR(nt_status);
738 done:
739 ads_msgfree(ads, msg);
740 talloc_destroy(frame);
742 return nt_status;
745 /**********************************************************************
746 *********************************************************************/
748 NTSTATUS add_ads_result_to_array(ADS_STRUCT *ads,
749 LDAPMessage *msg,
750 ADS_STRUCT ***ads_list,
751 LDAPMessage ***msg_list,
752 int *size)
754 NTSTATUS nt_status = NT_STATUS_UNSUCCESSFUL;
755 ADS_STRUCT **ads_tmp = NULL;
756 LDAPMessage **msg_tmp = NULL;
757 int count = *size;
759 if (!ads || !msg) {
760 nt_status = NT_STATUS_INVALID_PARAMETER;
761 BAIL_ON_NTSTATUS_ERROR(nt_status);
764 #if 0
765 /* Don't add a response with no entries */
767 if (ads_count_replies(ads, msg) == 0) {
768 return NT_STATUS_OK;
770 #endif
772 if (count == 0) {
773 ads_tmp = TALLOC_ARRAY(NULL, ADS_STRUCT*, 1);
774 BAIL_ON_PTR_ERROR(ads_tmp, nt_status);
776 msg_tmp = TALLOC_ARRAY(NULL, LDAPMessage*, 1);
777 BAIL_ON_PTR_ERROR(msg_tmp, nt_status);
778 } else {
779 ads_tmp = TALLOC_REALLOC_ARRAY(*ads_list, *ads_list, ADS_STRUCT*,
780 count+1);
781 BAIL_ON_PTR_ERROR(ads_tmp, nt_status);
783 msg_tmp = TALLOC_REALLOC_ARRAY(*msg_list, *msg_list, LDAPMessage*,
784 count+1);
785 BAIL_ON_PTR_ERROR(msg_tmp, nt_status);
788 ads_tmp[count] = ads;
789 msg_tmp[count] = msg;
790 count++;
792 *ads_list = ads_tmp;
793 *msg_list = msg_tmp;
794 *size = count;
796 nt_status = NT_STATUS_OK;
798 done:
799 if (!NT_STATUS_IS_OK(nt_status)) {
800 talloc_destroy(ads_tmp);
801 talloc_destroy(msg_tmp);
804 return nt_status;
807 /**********************************************************************
808 Frees search results. Do not free the ads_list as these are
809 references back to the GC search structures.
810 *********************************************************************/
812 void free_result_array(ADS_STRUCT **ads_list,
813 LDAPMessage **msg_list,
814 int num_resp)
816 int i;
818 for (i=0; i<num_resp; i++) {
819 ads_msgfree(ads_list[i], msg_list[i]);
822 talloc_destroy(ads_list);
823 talloc_destroy(msg_list);
826 /**********************************************************************
827 Check that we have exactly one entry from the search
828 *********************************************************************/
830 NTSTATUS check_result_unique(ADS_STRUCT *ads, LDAPMessage *msg)
832 NTSTATUS nt_status;
833 int count;
835 count = ads_count_replies(ads, msg);
837 if (count <= 0) {
838 nt_status = NT_STATUS_OBJECT_NAME_NOT_FOUND;
839 BAIL_ON_NTSTATUS_ERROR(nt_status);
842 if (count > 1) {
843 nt_status = NT_STATUS_DUPLICATE_NAME;
844 BAIL_ON_NTSTATUS_ERROR(nt_status);
847 nt_status = NT_STATUS_OK;
849 done:
850 return nt_status;