s3: only use netlogon/nbt header when needed.
[metze/samba/wip.git] / source3 / winbindd / idmap_adex / gc_util.c
1 /*
2  * idmap_adex: Global Catalog search interface
3  *
4  * Copyright (C) Gerald (Jerry) Carter 2007-2008
5  *
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.
10  *
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.
15  *
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.
19  */
20
21 #include "includes.h"
22 #include "idmap_adex.h"
23 #include "libads/cldap.h"
24
25 #undef DBGC_CLASS
26 #define DBGC_CLASS DBGC_IDMAP
27
28 static struct gc_info *_gc_server_list = NULL;
29
30
31 /**********************************************************************
32  *********************************************************************/
33
34 static struct gc_info *gc_list_head(void)
35 {
36         return _gc_server_list;
37 }
38
39 /**********************************************************************
40  Checks if either of the domains is a subdomain of the other
41  *********************************************************************/
42
43 static bool is_subdomain(const char* a, const char *b)
44 {
45         char *s;
46         TALLOC_CTX *frame = talloc_stackframe();
47         char *x, *y;
48         bool ret = false;
49
50         /* Trivial cases */
51
52         if (!a && !b)
53                 return true;
54
55         if (!a || !b)
56                 return false;
57
58         /* Normalize the case */
59
60         x = talloc_strdup(frame, a);
61         y = talloc_strdup(frame, b);
62         if (!x || !y) {
63                 ret = false;
64                 goto done;
65         }
66
67         strupper_m(x);
68         strupper_m(y);
69
70         /* Exact match */
71
72         if (strcmp(x, y) == 0) {
73                 ret = true;
74                 goto done;
75         }
76
77         /* Check for trailing substrings */
78
79         s = strstr_m(x, y);
80         if (s && (strlen(s) == strlen(y))) {
81                 ret = true;
82                 goto done;
83         }
84
85         s = strstr_m(y, x);
86         if (s && (strlen(s) == strlen(x))) {
87                 ret = true;
88                 goto done;
89         }
90
91 done:
92         talloc_destroy(frame);
93
94         return ret;
95 }
96
97 /**********************************************************************
98  *********************************************************************/
99
100  NTSTATUS gc_find_forest_root(struct gc_info *gc, const char *domain)
101 {
102         ADS_STRUCT *ads = NULL;
103         ADS_STATUS ads_status;
104         NTSTATUS nt_status = NT_STATUS_UNSUCCESSFUL;
105         struct NETLOGON_SAM_LOGON_RESPONSE_EX cldap_reply;
106         TALLOC_CTX *frame = talloc_stackframe();
107
108         if (!gc || !domain) {
109                 return NT_STATUS_INVALID_PARAMETER;
110         }
111
112         ZERO_STRUCT(cldap_reply);
113
114         ads = ads_init(domain, NULL, NULL);
115         BAIL_ON_PTR_ERROR(ads, nt_status);
116
117         ads->auth.flags = ADS_AUTH_NO_BIND;
118         ads_status = ads_connect(ads);
119         if (!ADS_ERR_OK(ads_status)) {
120                 DEBUG(4, ("find_forest_root: ads_connect(%s) failed! (%s)\n",
121                           domain, ads_errstr(ads_status)));
122         }
123         nt_status = ads_ntstatus(ads_status);
124         BAIL_ON_NTSTATUS_ERROR(nt_status);
125
126         if (!ads_cldap_netlogon_5(frame,
127                                   ads->config.ldap_server_name,
128                                   ads->config.realm,
129                                   &cldap_reply))
130         {
131                 DEBUG(4,("find_forest_root: Failed to get a CLDAP reply from %s!\n",
132                          ads->server.ldap_server));
133                 nt_status = NT_STATUS_IO_TIMEOUT;
134                 BAIL_ON_NTSTATUS_ERROR(nt_status);
135         }
136
137         gc->forest_name = talloc_strdup(gc, cldap_reply.forest);
138         BAIL_ON_PTR_ERROR(gc->forest_name, nt_status);
139
140 done:
141         if (ads) {
142                 ads_destroy(&ads);
143         }
144
145         return nt_status;
146 }
147
148 /**********************************************************************
149  *********************************************************************/
150
151 static NTSTATUS gc_add_forest(const char *domain)
152 {
153         NTSTATUS nt_status = NT_STATUS_UNSUCCESSFUL;
154         struct gc_info *gc = NULL;
155         struct gc_info *find_gc = NULL;
156         char *dn;
157         ADS_STRUCT *ads = NULL;
158         struct likewise_cell *primary_cell = NULL;
159
160         primary_cell = cell_list_head();
161         if (!primary_cell) {
162                 nt_status = NT_STATUS_INVALID_SERVER_STATE;
163                 BAIL_ON_NTSTATUS_ERROR(nt_status);
164         }
165
166         /* Check for duplicates based on domain name first as this
167            requires no connection */
168
169         find_gc = gc_list_head();
170         while (find_gc) {
171                 if (strequal (find_gc->forest_name, domain))
172                         break;
173                 find_gc = find_gc->next;
174         }
175
176         if (find_gc) {
177                 DEBUG(10,("gc_add_forest: %s already in list\n", find_gc->forest_name));
178                 return NT_STATUS_OK;
179         }
180
181         if ((gc = TALLOC_ZERO_P(NULL, struct gc_info)) == NULL) {
182                 nt_status = NT_STATUS_NO_MEMORY;
183                 BAIL_ON_NTSTATUS_ERROR(nt_status);
184         }
185
186         /* Query the rootDSE for the forest root naming conect first.
187            Check that the a GC server for the forest has not already
188            been added */
189
190         nt_status = gc_find_forest_root(gc, domain);
191         BAIL_ON_NTSTATUS_ERROR(nt_status);
192
193         find_gc = gc_list_head();
194         while (find_gc) {
195                 if (strequal (find_gc->forest_name, gc->forest_name))
196                         break;
197                 find_gc = find_gc->next;
198         }
199
200         if (find_gc) {
201                 DEBUG(10,("gc_add_forest: Forest %s already in list\n",
202                           find_gc->forest_name));
203                 return NT_STATUS_OK;
204         }
205
206         /* Not found, so add it here.  Make sure we connect to
207            a DC in _this_ domain and not the forest root. */
208
209         dn = ads_build_dn(gc->forest_name);
210         BAIL_ON_PTR_ERROR(dn, nt_status);
211
212         gc->search_base = talloc_strdup(gc, dn);
213         SAFE_FREE(dn);
214         BAIL_ON_PTR_ERROR(gc->search_base, nt_status);
215
216 #if 0
217         /* Can't use cell_connect_dn() here as there is no way to
218            specifiy the LWCELL_FLAG_GC_CELL flag setting for cell_connect() */
219
220         nt_status = cell_connect_dn(&gc->forest_cell, gc->search_base);
221         BAIL_ON_NTSTATUS_ERROR(nt_status);
222 #else
223
224         gc->forest_cell = cell_new();
225         BAIL_ON_PTR_ERROR(gc->forest_cell, nt_status);
226
227         /* Set the DNS domain, dn, etc ... and add it to the list */
228
229         cell_set_dns_domain(gc->forest_cell, gc->forest_name);
230         cell_set_dn(gc->forest_cell, gc->search_base);
231         cell_set_flags(gc->forest_cell, LWCELL_FLAG_GC_CELL);
232 #endif
233
234         /* It is possible to belong to a non-forest cell and a
235            non-provisioned forest (at our domain levele). In that
236            case, we should just inherit the flags from our primary
237            cell since the GC searches will match our own schema
238            model. */
239
240         if (strequal(primary_cell->forest_name, gc->forest_name)
241             || is_subdomain(primary_cell->dns_domain, gc->forest_name))
242         {
243                 cell_set_flags(gc->forest_cell, cell_flags(primary_cell));
244         } else {
245                 /* outside of our domain */
246
247                 nt_status = cell_connect(gc->forest_cell);
248                 BAIL_ON_NTSTATUS_ERROR(nt_status);
249
250                 nt_status = cell_lookup_settings(gc->forest_cell);
251                 BAIL_ON_NTSTATUS_ERROR(nt_status);
252
253                 /* Drop the connection now that we have the settings */
254
255                 ads = cell_connection(gc->forest_cell);
256                 ads_destroy(&ads);
257                 cell_set_connection(gc->forest_cell, NULL);
258         }
259
260         DLIST_ADD_END(_gc_server_list, gc, struct gc_info*);
261
262         DEBUG(10,("gc_add_forest: Added %s to Global Catalog list of servers\n",
263                   gc->forest_name));
264
265         nt_status = NT_STATUS_OK;
266
267 done:
268         if (!NT_STATUS_IS_OK(nt_status)) {
269                 talloc_destroy(gc);
270                 DEBUG(3,("LWI: Failed to add new GC connection for %s (%s)\n",
271                          domain, nt_errstr(nt_status)));
272         }
273
274         return nt_status;
275 }
276
277 /**********************************************************************
278  *********************************************************************/
279
280 static void gc_server_list_destroy(void)
281 {
282         struct gc_info *gc = gc_list_head();
283
284         while (gc) {
285                 struct gc_info *p = gc->next;
286
287                 cell_destroy(gc->forest_cell);
288                 talloc_destroy(gc);
289
290                 gc = p;
291         }
292
293         _gc_server_list = NULL;
294
295         return;
296 }
297
298 /**********************************************************************
299  Setup the initial list of forests and initial the forest cell
300  settings for each.  FIXME!!!
301  *********************************************************************/
302
303  NTSTATUS gc_init_list(void)
304 {
305         NTSTATUS nt_status = NT_STATUS_UNSUCCESSFUL;
306         struct winbindd_tdc_domain *domains = NULL;
307         size_t num_domains = 0;
308         int i;
309
310         if (_gc_server_list != NULL) {
311                 gc_server_list_destroy();
312         }
313
314         if (!wcache_tdc_fetch_list(&domains, &num_domains)) {
315                 nt_status = NT_STATUS_CANT_ACCESS_DOMAIN_INFO;
316                 BAIL_ON_NTSTATUS_ERROR(nt_status);
317         }
318
319         /* Find our forest first.  Have to try all domains here starting
320            with our own.  gc_add_forest() filters duplicates */
321
322         nt_status = gc_add_forest(lp_realm());
323         WARN_ON_NTSTATUS_ERROR(nt_status);
324
325         for (i=0; i<num_domains; i++) {
326                 uint32_t flags = (NETR_TRUST_FLAG_IN_FOREST);
327
328                 /* I think we should be able to break out of loop once
329                    we add a GC for our forest and not have to test every one.
330                    In fact, this entire loop is probably irrelevant since
331                    the GC location code should always find a GC given lp_realm().
332                    Will have to spend time testing before making the change.
333                    --jerry */
334
335                 if ((domains[i].trust_flags & flags) == flags) {
336                         nt_status = gc_add_forest(domains[i].dns_name);
337                         WARN_ON_NTSTATUS_ERROR(nt_status);
338                         /* Don't BAIL here since not every domain may
339                            have a GC server */
340                 }
341         }
342
343         /* Now add trusted forests.  gc_add_forest() will filter out
344            duplicates. Check everything with an incoming trust path
345            that is not in our own forest.  */
346
347         for (i=0; i<num_domains; i++) {
348                 uint32_t flags = domains[i].trust_flags;
349                 uint32_t attribs = domains[i].trust_attribs;
350
351                 /* Skip non_AD domains */
352
353                 if (strlen(domains[i].dns_name) == 0) {
354                         continue;
355                 }
356
357                 /* Only add a GC for a forest outside of our own.
358                    Ignore QUARANTINED/EXTERNAL trusts */
359
360                 if ((flags & NETR_TRUST_FLAG_INBOUND)
361                     && !(flags & NETR_TRUST_FLAG_IN_FOREST)
362                     && (attribs & NETR_TRUST_ATTRIBUTE_FOREST_TRANSITIVE))
363                 {
364                         nt_status = gc_add_forest(domains[i].dns_name);
365                         WARN_ON_NTSTATUS_ERROR(nt_status);
366                 }
367         }
368
369         nt_status = NT_STATUS_OK;
370
371 done:
372         if (!NT_STATUS_IS_OK(nt_status)) {
373                 DEBUG(2,("LWI: Failed to initialized GC list (%s)\n",
374                          nt_errstr(nt_status)));
375         }
376
377         TALLOC_FREE(domains);
378
379         return nt_status;
380 }
381
382
383 /**********************************************************************
384  *********************************************************************/
385
386  struct gc_info *gc_search_start(void)
387 {
388         NTSTATUS nt_status = NT_STATUS_OK;
389         struct gc_info *gc = gc_list_head();
390
391         if (!gc) {
392                 nt_status = gc_init_list();
393                 BAIL_ON_NTSTATUS_ERROR(nt_status);
394
395                 gc = gc_list_head();
396         }
397
398 done:
399         if (!NT_STATUS_IS_OK(nt_status)) {
400                 DEBUG(2,("LWI: Failed to initialize GC list (%s)\n",
401                          nt_errstr(nt_status)));
402         }
403
404         return gc;
405 }
406
407 /**********************************************************************
408  Search Global Catalog.  Always search our own forest.  The flags set
409  controls whether or not we search cross forest.  Assume that the
410  resulting set is always returned from one GC so that we don't have to
411  both combining the LDAPMessage * results
412  *********************************************************************/
413
414  NTSTATUS gc_search_forest(struct gc_info *gc,
415                            LDAPMessage **msg,
416                            const char *filter)
417 {
418         NTSTATUS nt_status = NT_STATUS_UNSUCCESSFUL;
419         ADS_STATUS ads_status = ADS_ERROR_NT(NT_STATUS_UNSUCCESSFUL);
420         const char *attrs[] = {"*", NULL};
421         LDAPMessage *m = NULL;
422
423         if (!gc || !msg || !filter) {
424                 nt_status = NT_STATUS_INVALID_PARAMETER;
425                 BAIL_ON_NTSTATUS_ERROR(nt_status);
426         }
427
428         /* When you have multiple domain trees in a forest, the
429            GC will search all naming contexts when you send it
430            and empty ("") base search suffix.   Tested against
431            Windows 2003.  */
432
433         ads_status = cell_do_search(gc->forest_cell, "",
434                                    LDAP_SCOPE_SUBTREE, filter, attrs, &m);
435         nt_status = ads_ntstatus(ads_status);
436         BAIL_ON_NTSTATUS_ERROR(nt_status);
437
438         *msg = m;
439
440 done:
441         if (!NT_STATUS_IS_OK(nt_status)) {
442                 DEBUG(2,("LWI: Forest wide search %s failed (%s)\n",
443                          filter, nt_errstr(nt_status)));
444         }
445
446         return nt_status;
447 }
448
449 /**********************************************************************
450  Search all forests via GC and return the results in an array of
451  ADS_STRUCT/LDAPMessage pairs.
452  *********************************************************************/
453
454  NTSTATUS gc_search_all_forests(const char *filter,
455                                 ADS_STRUCT ***ads_list,
456                                 LDAPMessage ***msg_list,
457                                 int *num_resp, uint32_t flags)
458 {
459         NTSTATUS nt_status = NT_STATUS_UNSUCCESSFUL;
460         struct gc_info *gc = NULL;
461         uint32_t test_flags = ADEX_GC_SEARCH_CHECK_UNIQUE;
462
463         *ads_list = NULL;
464         *msg_list = NULL;
465         *num_resp = 0;
466
467         if ((gc = gc_search_start()) == NULL) {
468                 nt_status = NT_STATUS_INVALID_DOMAIN_STATE;
469                 BAIL_ON_NTSTATUS_ERROR(nt_status);
470         }
471
472         while (gc) {
473                 LDAPMessage *m = NULL;
474
475                 nt_status = gc_search_forest(gc, &m, filter);
476                 if (!NT_STATUS_IS_OK(nt_status)) {
477                         gc = gc->next;
478                         continue;
479                 }
480
481                 nt_status = add_ads_result_to_array(cell_connection(gc->forest_cell),
482                                                     m, ads_list, msg_list,
483                                                     num_resp);
484                 BAIL_ON_NTSTATUS_ERROR(nt_status);
485
486                 /* If there can only be one match, then we are done */
487
488                 if ((*num_resp > 0) && ((flags & test_flags) == test_flags)) {
489                         break;
490                 }
491
492                 gc = gc->next;
493         }
494
495         if (*num_resp == 0) {
496                 nt_status = NT_STATUS_OBJECT_NAME_NOT_FOUND;
497                 BAIL_ON_NTSTATUS_ERROR(nt_status);
498         }
499
500         nt_status = NT_STATUS_OK;
501
502 done:
503         return nt_status;
504 }
505
506 /**********************************************************************
507  Search all forests via GC and return the results in an array of
508  ADS_STRUCT/LDAPMessage pairs.
509  *********************************************************************/
510
511  NTSTATUS gc_search_all_forests_unique(const char *filter,
512                                        ADS_STRUCT **ads,
513                                        LDAPMessage **msg)
514 {
515         ADS_STRUCT **ads_list = NULL;
516         LDAPMessage **msg_list = NULL;
517         int num_resp;
518         NTSTATUS nt_status = NT_STATUS_UNSUCCESSFUL;
519
520         nt_status = gc_search_all_forests(filter, &ads_list,
521                                           &msg_list, &num_resp,
522                                           ADEX_GC_SEARCH_CHECK_UNIQUE);
523         BAIL_ON_NTSTATUS_ERROR(nt_status);
524
525         nt_status = check_result_unique(ads_list[0], msg_list[0]);
526         BAIL_ON_NTSTATUS_ERROR(nt_status);
527
528         *ads = ads_list[0];
529         *msg = msg_list[0];
530
531 done:
532         /* Be care that we don't free the msg result being returned */
533
534         if (!NT_STATUS_IS_OK(nt_status)) {
535                 free_result_array(ads_list, msg_list, num_resp);
536         } else {
537                 talloc_destroy(ads_list);
538                 talloc_destroy(msg_list);
539         }
540
541         return nt_status;
542 }
543
544 /*********************************************************************
545  ********************************************************************/
546
547  NTSTATUS gc_name_to_sid(const char *domain,
548                          const char *name,
549                          struct dom_sid *sid,
550                          enum lsa_SidType *sid_type)
551 {
552         TALLOC_CTX *frame = talloc_stackframe();
553         char *p, *name_user;
554         NTSTATUS nt_status = NT_STATUS_UNSUCCESSFUL;
555         char *name_filter;
556         ADS_STRUCT *ads = NULL;
557         LDAPMessage *msg = NULL;
558         LDAPMessage *e = NULL;
559         char *dn = NULL;
560         char *dns_domain = NULL;
561         ADS_STRUCT **ads_list = NULL;
562         LDAPMessage **msg_list = NULL;
563         int num_resp = 0;
564         int i;
565
566         /* Strip the "DOMAIN\" prefix if necessary and search for
567            a matching sAMAccountName in the forest */
568
569         if ((p = strchr_m( name, '\\' )) == NULL)
570                 name_user = talloc_strdup( frame, name );
571         else
572                 name_user = talloc_strdup( frame, p+1 );
573         BAIL_ON_PTR_ERROR(name_user, nt_status);
574
575         name_filter = talloc_asprintf(frame, "(sAMAccountName=%s)", name_user);
576         BAIL_ON_PTR_ERROR(name_filter, nt_status);
577
578         nt_status = gc_search_all_forests(name_filter, &ads_list,
579                                           &msg_list, &num_resp, 0);
580         BAIL_ON_NTSTATUS_ERROR(nt_status);
581
582         /* Assume failure until we know otherwise*/
583
584         nt_status = NT_STATUS_OBJECT_NAME_NOT_FOUND;
585
586         /* Match the domain name from the DN */
587
588         for (i=0; i<num_resp; i++) {
589                 ads = ads_list[i];
590                 msg = msg_list[i];
591
592                 e = ads_first_entry(ads, msg);
593                 while (e) {
594                         struct winbindd_tdc_domain *domain_rec;
595
596                         dn = ads_get_dn(ads, frame, e);
597                         BAIL_ON_PTR_ERROR(dn, nt_status);
598
599                         dns_domain = cell_dn_to_dns(dn);
600                         TALLOC_FREE(dn);
601                         BAIL_ON_PTR_ERROR(dns_domain, nt_status);
602
603                         domain_rec = wcache_tdc_fetch_domain(frame, dns_domain);
604                         SAFE_FREE(dns_domain);
605
606                         /* Ignore failures and continue the search */
607
608                         if (!domain_rec) {
609                                 e = ads_next_entry(ads, e);
610                                 continue;
611                         }
612
613                         /* Check for a match on the domain name */
614
615                         if (strequal(domain, domain_rec->domain_name)) {
616                                 if (!ads_pull_sid(ads, e, "objectSid", sid)) {
617                                         nt_status = NT_STATUS_INVALID_SID;
618                                         BAIL_ON_NTSTATUS_ERROR(nt_status);
619                                 }
620
621                                 talloc_destroy(domain_rec);
622
623                                 nt_status = get_sid_type(ads, msg, sid_type);
624                                 BAIL_ON_NTSTATUS_ERROR(nt_status);
625
626                                 /* We're done! */
627                                 nt_status = NT_STATUS_OK;
628                                 break;
629                         }
630
631                         /* once more around thew merry-go-round */
632
633                         talloc_destroy(domain_rec);
634                         e = ads_next_entry(ads, e);
635                 }
636         }
637
638 done:
639         free_result_array(ads_list, msg_list, num_resp);
640         talloc_destroy(frame);
641
642         return nt_status;
643 }
644
645 /********************************************************************
646  Pull an attribute string value
647  *******************************************************************/
648
649 static NTSTATUS get_object_account_name(ADS_STRUCT *ads,
650                                         LDAPMessage *msg,
651                                         char **name)
652 {
653         NTSTATUS nt_status = NT_STATUS_UNSUCCESSFUL;
654         char *sam_name = NULL;
655         struct winbindd_tdc_domain *domain_rec = NULL;
656         char *dns_domain = NULL;
657         char *dn = NULL;
658         TALLOC_CTX *frame = talloc_stackframe();
659         int len;
660
661         /* Check parameters */
662
663         if (!ads || !msg || !name) {
664                 nt_status = NT_STATUS_INVALID_PARAMETER;
665                 BAIL_ON_NTSTATUS_ERROR(nt_status);
666         }
667
668         /* get the name and domain */
669
670         dn = ads_get_dn(ads, frame, msg);
671         BAIL_ON_PTR_ERROR(dn, nt_status);
672
673         DEBUG(10,("get_object_account_name: dn = \"%s\"\n", dn));
674
675         dns_domain = cell_dn_to_dns(dn);
676         TALLOC_FREE(dn);
677         BAIL_ON_PTR_ERROR(dns_domain, nt_status);
678
679         domain_rec = wcache_tdc_fetch_domain(frame, dns_domain);
680         SAFE_FREE(dns_domain);
681
682         if (!domain_rec) {
683                 nt_status = NT_STATUS_TRUSTED_DOMAIN_FAILURE;
684                 BAIL_ON_NTSTATUS_ERROR(nt_status);
685         }
686
687         sam_name = ads_pull_string(ads, frame, msg, "sAMAccountName");
688         BAIL_ON_PTR_ERROR(sam_name, nt_status);
689
690         len = asprintf(name, "%s\\%s", domain_rec->domain_name, sam_name);
691         if (len == -1) {
692                 *name = NULL;
693                 BAIL_ON_PTR_ERROR((*name), nt_status);
694         }
695
696         nt_status = NT_STATUS_OK;
697
698 done:
699         talloc_destroy(frame);
700
701         return nt_status;
702 }
703
704 /*********************************************************************
705  ********************************************************************/
706
707  NTSTATUS gc_sid_to_name(const struct dom_sid *sid,
708                          char **name,
709                          enum lsa_SidType *sid_type)
710 {
711         TALLOC_CTX *frame = talloc_stackframe();
712         NTSTATUS nt_status = NT_STATUS_UNSUCCESSFUL;
713         char *filter;
714         ADS_STRUCT *ads = NULL;
715         LDAPMessage *msg = NULL;
716         char *sid_string;
717
718         *name = NULL;
719
720         sid_string = sid_binstring(frame, sid);
721         BAIL_ON_PTR_ERROR(sid_string, nt_status);
722
723         filter = talloc_asprintf(frame, "(objectSid=%s)", sid_string);
724         TALLOC_FREE(sid_string);
725         BAIL_ON_PTR_ERROR(filter, nt_status);
726
727         nt_status = gc_search_all_forests_unique(filter, &ads, &msg);
728         BAIL_ON_NTSTATUS_ERROR(nt_status);
729
730         nt_status = get_object_account_name(ads, msg, name);
731         BAIL_ON_NTSTATUS_ERROR(nt_status);
732
733         nt_status = get_sid_type(ads, msg, sid_type);
734         BAIL_ON_NTSTATUS_ERROR(nt_status);
735
736 done:
737         ads_msgfree(ads, msg);
738         talloc_destroy(frame);
739
740         return nt_status;
741 }
742
743 /**********************************************************************
744  *********************************************************************/
745
746  NTSTATUS add_ads_result_to_array(ADS_STRUCT *ads,
747                                   LDAPMessage *msg,
748                                   ADS_STRUCT ***ads_list,
749                                   LDAPMessage ***msg_list,
750                                   int *size)
751 {
752         NTSTATUS nt_status = NT_STATUS_UNSUCCESSFUL;
753         ADS_STRUCT **ads_tmp = NULL;
754         LDAPMessage **msg_tmp = NULL;
755         int count = *size;
756
757         if (!ads || !msg) {
758                 nt_status = NT_STATUS_INVALID_PARAMETER;
759                 BAIL_ON_NTSTATUS_ERROR(nt_status);
760         }
761
762 #if 0
763         /* Don't add a response with no entries */
764
765         if (ads_count_replies(ads, msg) == 0) {
766                 return NT_STATUS_OK;
767         }
768 #endif
769
770         if (count == 0) {
771                 ads_tmp = TALLOC_ARRAY(NULL, ADS_STRUCT*, 1);
772                 BAIL_ON_PTR_ERROR(ads_tmp, nt_status);
773
774                 msg_tmp = TALLOC_ARRAY(NULL, LDAPMessage*, 1);
775                 BAIL_ON_PTR_ERROR(msg_tmp, nt_status);
776         } else {
777                 ads_tmp = TALLOC_REALLOC_ARRAY(*ads_list, *ads_list, ADS_STRUCT*,
778                                                count+1);
779                 BAIL_ON_PTR_ERROR(ads_tmp, nt_status);
780
781                 msg_tmp = TALLOC_REALLOC_ARRAY(*msg_list, *msg_list, LDAPMessage*,
782                                                count+1);
783                 BAIL_ON_PTR_ERROR(msg_tmp, nt_status);
784         }
785
786         ads_tmp[count] = ads;
787         msg_tmp[count] = msg;
788         count++;
789
790         *ads_list = ads_tmp;
791         *msg_list = msg_tmp;
792         *size = count;
793
794         nt_status = NT_STATUS_OK;
795
796 done:
797         if (!NT_STATUS_IS_OK(nt_status)) {
798                 talloc_destroy(ads_tmp);
799                 talloc_destroy(msg_tmp);
800         }
801
802         return nt_status;
803 }
804
805 /**********************************************************************
806  Frees search results.  Do not free the ads_list as these are
807  references back to the GC search structures.
808  *********************************************************************/
809
810  void free_result_array(ADS_STRUCT **ads_list,
811                         LDAPMessage **msg_list,
812                         int num_resp)
813 {
814         int i;
815
816         for (i=0; i<num_resp; i++) {
817                 ads_msgfree(ads_list[i], msg_list[i]);
818         }
819
820         talloc_destroy(ads_list);
821         talloc_destroy(msg_list);
822 }
823
824 /**********************************************************************
825  Check that we have exactly one entry from the search
826  *********************************************************************/
827
828  NTSTATUS check_result_unique(ADS_STRUCT *ads, LDAPMessage *msg)
829 {
830         NTSTATUS nt_status;
831         int count;
832
833         count = ads_count_replies(ads, msg);
834
835         if (count <= 0) {
836                 nt_status = NT_STATUS_OBJECT_NAME_NOT_FOUND;
837                 BAIL_ON_NTSTATUS_ERROR(nt_status);
838         }
839
840         if (count > 1) {
841                 nt_status = NT_STATUS_DUPLICATE_NAME;
842                 BAIL_ON_NTSTATUS_ERROR(nt_status);
843         }
844
845         nt_status = NT_STATUS_OK;
846
847 done:
848         return nt_status;
849 }