Merge 2610c05b5b95cc7036b3d6dfb894c6cfbdb68483 as Samba-4.0alpha16
[amitay/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.h"
24 #include "idmap_adex.h"
25 #include "libads/cldap.h"
26 #include "../libcli/ldap/ldap_ndr.h"
27
28 #undef DBGC_CLASS
29 #define DBGC_CLASS DBGC_IDMAP
30
31 static struct gc_info *_gc_server_list = NULL;
32
33
34 /**********************************************************************
35  *********************************************************************/
36
37 static struct gc_info *gc_list_head(void)
38 {
39         return _gc_server_list;
40 }
41
42 /**********************************************************************
43  Checks if either of the domains is a subdomain of the other
44  *********************************************************************/
45
46 static bool is_subdomain(const char* a, const char *b)
47 {
48         char *s;
49         TALLOC_CTX *frame = talloc_stackframe();
50         char *x, *y;
51         bool ret = false;
52
53         /* Trivial cases */
54
55         if (!a && !b)
56                 return true;
57
58         if (!a || !b)
59                 return false;
60
61         /* Normalize the case */
62
63         x = talloc_strdup(frame, a);
64         y = talloc_strdup(frame, b);
65         if (!x || !y) {
66                 ret = false;
67                 goto done;
68         }
69
70         strupper_m(x);
71         strupper_m(y);
72
73         /* Exact match */
74
75         if (strcmp(x, y) == 0) {
76                 ret = true;
77                 goto done;
78         }
79
80         /* Check for trailing substrings */
81
82         s = strstr_m(x, y);
83         if (s && (strlen(s) == strlen(y))) {
84                 ret = true;
85                 goto done;
86         }
87
88         s = strstr_m(y, x);
89         if (s && (strlen(s) == strlen(x))) {
90                 ret = true;
91                 goto done;
92         }
93
94 done:
95         talloc_destroy(frame);
96
97         return ret;
98 }
99
100 /**********************************************************************
101  *********************************************************************/
102
103  NTSTATUS gc_find_forest_root(struct gc_info *gc, const char *domain)
104 {
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;
111
112         if (!gc || !domain) {
113                 return NT_STATUS_INVALID_PARAMETER;
114         }
115
116         ZERO_STRUCT(cldap_reply);
117
118         ads = ads_init(domain, NULL, NULL);
119         BAIL_ON_PTR_ERROR(ads, nt_status);
120
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)));
126         }
127         nt_status = ads_ntstatus(ads_status);
128         BAIL_ON_NTSTATUS_ERROR(nt_status);
129
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);
137         }
138
139         if (!ads_cldap_netlogon_5(frame,
140                                   &ss,
141                                   ads->config.realm,
142                                   &cldap_reply))
143         {
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);
148         }
149
150         gc->forest_name = talloc_strdup(gc, cldap_reply.forest);
151         BAIL_ON_PTR_ERROR(gc->forest_name, nt_status);
152
153 done:
154         if (ads) {
155                 ads_destroy(&ads);
156         }
157
158         return nt_status;
159 }
160
161 /**********************************************************************
162  *********************************************************************/
163
164 static NTSTATUS gc_add_forest(const char *domain)
165 {
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;
172
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);
177         }
178
179         /* Check for duplicates based on domain name first as this
180            requires no connection */
181
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;
187         }
188
189         if (find_gc) {
190                 DEBUG(10,("gc_add_forest: %s already in list\n", find_gc->forest_name));
191                 return NT_STATUS_OK;
192         }
193
194         if ((gc = talloc_zero(NULL, struct gc_info)) == NULL) {
195                 nt_status = NT_STATUS_NO_MEMORY;
196                 BAIL_ON_NTSTATUS_ERROR(nt_status);
197         }
198
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 */
202
203         nt_status = gc_find_forest_root(gc, domain);
204         BAIL_ON_NTSTATUS_ERROR(nt_status);
205
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;
211         }
212
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;
217         }
218
219         /* Not found, so add it here.  Make sure we connect to
220            a DC in _this_ domain and not the forest root. */
221
222         dn = ads_build_dn(gc->forest_name);
223         BAIL_ON_PTR_ERROR(dn, nt_status);
224
225         gc->search_base = talloc_strdup(gc, dn);
226         SAFE_FREE(dn);
227         BAIL_ON_PTR_ERROR(gc->search_base, nt_status);
228
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() */
232
233         nt_status = cell_connect_dn(&gc->forest_cell, gc->search_base);
234         BAIL_ON_NTSTATUS_ERROR(nt_status);
235 #else
236
237         gc->forest_cell = cell_new();
238         BAIL_ON_PTR_ERROR(gc->forest_cell, nt_status);
239
240         /* Set the DNS domain, dn, etc ... and add it to the list */
241
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
246
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. */
252
253         if (strequal(primary_cell->forest_name, gc->forest_name)
254             || is_subdomain(primary_cell->dns_domain, gc->forest_name))
255         {
256                 cell_set_flags(gc->forest_cell, cell_flags(primary_cell));
257         } else {
258                 /* outside of our domain */
259
260                 nt_status = cell_connect(gc->forest_cell);
261                 BAIL_ON_NTSTATUS_ERROR(nt_status);
262
263                 nt_status = cell_lookup_settings(gc->forest_cell);
264                 BAIL_ON_NTSTATUS_ERROR(nt_status);
265
266                 /* Drop the connection now that we have the settings */
267
268                 ads = cell_connection(gc->forest_cell);
269                 ads_destroy(&ads);
270                 cell_set_connection(gc->forest_cell, NULL);
271         }
272
273         DLIST_ADD_END(_gc_server_list, gc, struct gc_info*);
274
275         DEBUG(10,("gc_add_forest: Added %s to Global Catalog list of servers\n",
276                   gc->forest_name));
277
278         nt_status = NT_STATUS_OK;
279
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)));
285         }
286
287         return nt_status;
288 }
289
290 /**********************************************************************
291  *********************************************************************/
292
293 static void gc_server_list_destroy(void)
294 {
295         struct gc_info *gc = gc_list_head();
296
297         while (gc) {
298                 struct gc_info *p = gc->next;
299
300                 cell_destroy(gc->forest_cell);
301                 talloc_destroy(gc);
302
303                 gc = p;
304         }
305
306         _gc_server_list = NULL;
307
308         return;
309 }
310
311 /**********************************************************************
312  Setup the initial list of forests and initial the forest cell
313  settings for each.  FIXME!!!
314  *********************************************************************/
315
316  NTSTATUS gc_init_list(void)
317 {
318         NTSTATUS nt_status = NT_STATUS_UNSUCCESSFUL;
319         struct winbindd_tdc_domain *domains = NULL;
320         size_t num_domains = 0;
321         int i;
322
323         if (_gc_server_list != NULL) {
324                 gc_server_list_destroy();
325         }
326
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);
330         }
331
332         /* Find our forest first.  Have to try all domains here starting
333            with our own.  gc_add_forest() filters duplicates */
334
335         nt_status = gc_add_forest(lp_realm());
336         WARN_ON_NTSTATUS_ERROR(nt_status);
337
338         for (i=0; i<num_domains; i++) {
339                 uint32_t flags = (NETR_TRUST_FLAG_IN_FOREST);
340
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 */
347
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 */
353                 }
354         }
355
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.  */
359
360         for (i=0; i<num_domains; i++) {
361                 uint32_t flags = domains[i].trust_flags;
362                 uint32_t attribs = domains[i].trust_attribs;
363
364                 /* Skip non_AD domains */
365
366                 if (strlen(domains[i].dns_name) == 0) {
367                         continue;
368                 }
369
370                 /* Only add a GC for a forest outside of our own.
371                    Ignore QUARANTINED/EXTERNAL trusts */
372
373                 if ((flags & NETR_TRUST_FLAG_INBOUND)
374                     && !(flags & NETR_TRUST_FLAG_IN_FOREST)
375                     && (attribs & NETR_TRUST_ATTRIBUTE_FOREST_TRANSITIVE))
376                 {
377                         nt_status = gc_add_forest(domains[i].dns_name);
378                         WARN_ON_NTSTATUS_ERROR(nt_status);
379                 }
380         }
381
382         nt_status = NT_STATUS_OK;
383
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)));
388         }
389
390         TALLOC_FREE(domains);
391
392         return nt_status;
393 }
394
395
396 /**********************************************************************
397  *********************************************************************/
398
399  struct gc_info *gc_search_start(void)
400 {
401         NTSTATUS nt_status = NT_STATUS_OK;
402         struct gc_info *gc = gc_list_head();
403
404         if (!gc) {
405                 nt_status = gc_init_list();
406                 BAIL_ON_NTSTATUS_ERROR(nt_status);
407
408                 gc = gc_list_head();
409         }
410
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)));
415         }
416
417         return gc;
418 }
419
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  *********************************************************************/
426
427  NTSTATUS gc_search_forest(struct gc_info *gc,
428                            LDAPMessage **msg,
429                            const char *filter)
430 {
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;
435
436         if (!gc || !msg || !filter) {
437                 nt_status = NT_STATUS_INVALID_PARAMETER;
438                 BAIL_ON_NTSTATUS_ERROR(nt_status);
439         }
440
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.  */
445
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);
450
451         *msg = m;
452
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)));
457         }
458
459         return nt_status;
460 }
461
462 /**********************************************************************
463  Search all forests via GC and return the results in an array of
464  ADS_STRUCT/LDAPMessage pairs.
465  *********************************************************************/
466
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)
471 {
472         NTSTATUS nt_status = NT_STATUS_UNSUCCESSFUL;
473         struct gc_info *gc = NULL;
474         uint32_t test_flags = ADEX_GC_SEARCH_CHECK_UNIQUE;
475
476         *ads_list = NULL;
477         *msg_list = NULL;
478         *num_resp = 0;
479
480         if ((gc = gc_search_start()) == NULL) {
481                 nt_status = NT_STATUS_INVALID_DOMAIN_STATE;
482                 BAIL_ON_NTSTATUS_ERROR(nt_status);
483         }
484
485         while (gc) {
486                 LDAPMessage *m = NULL;
487
488                 nt_status = gc_search_forest(gc, &m, filter);
489                 if (!NT_STATUS_IS_OK(nt_status)) {
490                         gc = gc->next;
491                         continue;
492                 }
493
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);
498
499                 /* If there can only be one match, then we are done */
500
501                 if ((*num_resp > 0) && ((flags & test_flags) == test_flags)) {
502                         break;
503                 }
504
505                 gc = gc->next;
506         }
507
508         if (*num_resp == 0) {
509                 nt_status = NT_STATUS_OBJECT_NAME_NOT_FOUND;
510                 BAIL_ON_NTSTATUS_ERROR(nt_status);
511         }
512
513         nt_status = NT_STATUS_OK;
514
515 done:
516         return nt_status;
517 }
518
519 /**********************************************************************
520  Search all forests via GC and return the results in an array of
521  ADS_STRUCT/LDAPMessage pairs.
522  *********************************************************************/
523
524  NTSTATUS gc_search_all_forests_unique(const char *filter,
525                                        ADS_STRUCT **ads,
526                                        LDAPMessage **msg)
527 {
528         ADS_STRUCT **ads_list = NULL;
529         LDAPMessage **msg_list = NULL;
530         int num_resp;
531         NTSTATUS nt_status = NT_STATUS_UNSUCCESSFUL;
532
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);
537
538         nt_status = check_result_unique(ads_list[0], msg_list[0]);
539         BAIL_ON_NTSTATUS_ERROR(nt_status);
540
541         *ads = ads_list[0];
542         *msg = msg_list[0];
543
544 done:
545         /* Be care that we don't free the msg result being returned */
546
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);
552         }
553
554         return nt_status;
555 }
556
557 /*********************************************************************
558  ********************************************************************/
559
560  NTSTATUS gc_name_to_sid(const char *domain,
561                          const char *name,
562                          struct dom_sid *sid,
563                          enum lsa_SidType *sid_type)
564 {
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;
578
579         /* Strip the "DOMAIN\" prefix if necessary and search for
580            a matching sAMAccountName in the forest */
581
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);
587
588         name_filter = talloc_asprintf(frame, "(sAMAccountName=%s)", name_user);
589         BAIL_ON_PTR_ERROR(name_filter, nt_status);
590
591         nt_status = gc_search_all_forests(name_filter, &ads_list,
592                                           &msg_list, &num_resp, 0);
593         BAIL_ON_NTSTATUS_ERROR(nt_status);
594
595         /* Assume failure until we know otherwise*/
596
597         nt_status = NT_STATUS_OBJECT_NAME_NOT_FOUND;
598
599         /* Match the domain name from the DN */
600
601         for (i=0; i<num_resp; i++) {
602                 ads = ads_list[i];
603                 msg = msg_list[i];
604
605                 e = ads_first_entry(ads, msg);
606                 while (e) {
607                         struct winbindd_tdc_domain *domain_rec;
608
609                         dn = ads_get_dn(ads, frame, e);
610                         BAIL_ON_PTR_ERROR(dn, nt_status);
611
612                         dns_domain = cell_dn_to_dns(dn);
613                         TALLOC_FREE(dn);
614                         BAIL_ON_PTR_ERROR(dns_domain, nt_status);
615
616                         domain_rec = wcache_tdc_fetch_domain(frame, dns_domain);
617                         SAFE_FREE(dns_domain);
618
619                         /* Ignore failures and continue the search */
620
621                         if (!domain_rec) {
622                                 e = ads_next_entry(ads, e);
623                                 continue;
624                         }
625
626                         /* Check for a match on the domain name */
627
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);
632                                 }
633
634                                 talloc_destroy(domain_rec);
635
636                                 nt_status = get_sid_type(ads, msg, sid_type);
637                                 BAIL_ON_NTSTATUS_ERROR(nt_status);
638
639                                 /* We're done! */
640                                 nt_status = NT_STATUS_OK;
641                                 break;
642                         }
643
644                         /* once more around thew merry-go-round */
645
646                         talloc_destroy(domain_rec);
647                         e = ads_next_entry(ads, e);
648                 }
649         }
650
651 done:
652         free_result_array(ads_list, msg_list, num_resp);
653         talloc_destroy(frame);
654
655         return nt_status;
656 }
657
658 /********************************************************************
659  Pull an attribute string value
660  *******************************************************************/
661
662 static NTSTATUS get_object_account_name(ADS_STRUCT *ads,
663                                         LDAPMessage *msg,
664                                         char **name)
665 {
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;
673
674         /* Check parameters */
675
676         if (!ads || !msg || !name) {
677                 nt_status = NT_STATUS_INVALID_PARAMETER;
678                 BAIL_ON_NTSTATUS_ERROR(nt_status);
679         }
680
681         /* get the name and domain */
682
683         dn = ads_get_dn(ads, frame, msg);
684         BAIL_ON_PTR_ERROR(dn, nt_status);
685
686         DEBUG(10,("get_object_account_name: dn = \"%s\"\n", dn));
687
688         dns_domain = cell_dn_to_dns(dn);
689         TALLOC_FREE(dn);
690         BAIL_ON_PTR_ERROR(dns_domain, nt_status);
691
692         domain_rec = wcache_tdc_fetch_domain(frame, dns_domain);
693         SAFE_FREE(dns_domain);
694
695         if (!domain_rec) {
696                 nt_status = NT_STATUS_TRUSTED_DOMAIN_FAILURE;
697                 BAIL_ON_NTSTATUS_ERROR(nt_status);
698         }
699
700         sam_name = ads_pull_string(ads, frame, msg, "sAMAccountName");
701         BAIL_ON_PTR_ERROR(sam_name, nt_status);
702
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);
707         }
708
709         nt_status = NT_STATUS_OK;
710
711 done:
712         talloc_destroy(frame);
713
714         return nt_status;
715 }
716
717 /*********************************************************************
718  ********************************************************************/
719
720  NTSTATUS gc_sid_to_name(const struct dom_sid *sid,
721                          char **name,
722                          enum lsa_SidType *sid_type)
723 {
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;
730
731         *name = NULL;
732
733         sid_string = ldap_encode_ndr_dom_sid(frame, sid);
734         BAIL_ON_PTR_ERROR(sid_string, nt_status);
735
736         filter = talloc_asprintf(frame, "(objectSid=%s)", sid_string);
737         TALLOC_FREE(sid_string);
738         BAIL_ON_PTR_ERROR(filter, nt_status);
739
740         nt_status = gc_search_all_forests_unique(filter, &ads, &msg);
741         BAIL_ON_NTSTATUS_ERROR(nt_status);
742
743         nt_status = get_object_account_name(ads, msg, name);
744         BAIL_ON_NTSTATUS_ERROR(nt_status);
745
746         nt_status = get_sid_type(ads, msg, sid_type);
747         BAIL_ON_NTSTATUS_ERROR(nt_status);
748
749 done:
750         ads_msgfree(ads, msg);
751         talloc_destroy(frame);
752
753         return nt_status;
754 }
755
756 /**********************************************************************
757  *********************************************************************/
758
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)
764 {
765         NTSTATUS nt_status = NT_STATUS_UNSUCCESSFUL;
766         ADS_STRUCT **ads_tmp = NULL;
767         LDAPMessage **msg_tmp = NULL;
768         int count = *size;
769
770         if (!ads || !msg) {
771                 nt_status = NT_STATUS_INVALID_PARAMETER;
772                 BAIL_ON_NTSTATUS_ERROR(nt_status);
773         }
774
775 #if 0
776         /* Don't add a response with no entries */
777
778         if (ads_count_replies(ads, msg) == 0) {
779                 return NT_STATUS_OK;
780         }
781 #endif
782
783         if (count == 0) {
784                 ads_tmp = talloc_array(NULL, ADS_STRUCT*, 1);
785                 BAIL_ON_PTR_ERROR(ads_tmp, nt_status);
786
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);
793
794                 msg_tmp = talloc_realloc(*msg_list, *msg_list, LDAPMessage*,
795                                                count+1);
796                 BAIL_ON_PTR_ERROR(msg_tmp, nt_status);
797         }
798
799         ads_tmp[count] = ads;
800         msg_tmp[count] = msg;
801         count++;
802
803         *ads_list = ads_tmp;
804         *msg_list = msg_tmp;
805         *size = count;
806
807         nt_status = NT_STATUS_OK;
808
809 done:
810         if (!NT_STATUS_IS_OK(nt_status)) {
811                 talloc_destroy(ads_tmp);
812                 talloc_destroy(msg_tmp);
813         }
814
815         return nt_status;
816 }
817
818 /**********************************************************************
819  Frees search results.  Do not free the ads_list as these are
820  references back to the GC search structures.
821  *********************************************************************/
822
823  void free_result_array(ADS_STRUCT **ads_list,
824                         LDAPMessage **msg_list,
825                         int num_resp)
826 {
827         int i;
828
829         for (i=0; i<num_resp; i++) {
830                 ads_msgfree(ads_list[i], msg_list[i]);
831         }
832
833         talloc_destroy(ads_list);
834         talloc_destroy(msg_list);
835 }
836
837 /**********************************************************************
838  Check that we have exactly one entry from the search
839  *********************************************************************/
840
841  NTSTATUS check_result_unique(ADS_STRUCT *ads, LDAPMessage *msg)
842 {
843         NTSTATUS nt_status;
844         int count;
845
846         count = ads_count_replies(ads, msg);
847
848         if (count <= 0) {
849                 nt_status = NT_STATUS_OBJECT_NAME_NOT_FOUND;
850                 BAIL_ON_NTSTATUS_ERROR(nt_status);
851         }
852
853         if (count > 1) {
854                 nt_status = NT_STATUS_DUPLICATE_NAME;
855                 BAIL_ON_NTSTATUS_ERROR(nt_status);
856         }
857
858         nt_status = NT_STATUS_OK;
859
860 done:
861         return nt_status;
862 }