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