749adafcb6f0d31dc058a9774d21779490b54a67
[sfrench/samba-autobuild/.git] / source3 / nsswitch / winbindd_ads.c
1 /* 
2    Unix SMB/Netbios implementation.
3
4    Winbind ADS backend functions
5
6    Copyright (C) Andrew Tridgell 2001
7    
8    This program is free software; you can redistribute it and/or modify
9    it under the terms of the GNU General Public License as published by
10    the Free Software Foundation; either version 2 of the License, or
11    (at your option) any later version.
12    
13    This program is distributed in the hope that it will be useful,
14    but WITHOUT ANY WARRANTY; without even the implied warranty of
15    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16    GNU General Public License for more details.
17    
18    You should have received a copy of the GNU General Public License
19    along with this program; if not, write to the Free Software
20    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
21 */
22
23 #include "winbindd.h"
24
25 #ifdef HAVE_ADS
26
27 /* the realm of our primary LDAP server */
28 static char *primary_realm;
29
30
31 /*
32   a wrapper around ldap_search_s that retries depending on the error code
33   this is supposed to catch dropped connections and auto-reconnect
34 */
35 ADS_STATUS ads_do_search_retry(ADS_STRUCT *ads, const char *bind_path, int scope, 
36                                const char *exp,
37                                const char **attrs, void **res)
38 {
39         ADS_STATUS status;
40         int count = 3;
41
42         if (!ads->ld &&
43             time(NULL) - ads->last_attempt < ADS_RECONNECT_TIME) {
44                 return ADS_ERROR(LDAP_SERVER_DOWN);
45         }
46
47         while (count--) {
48                 status = ads_do_search(ads, bind_path, scope, exp, attrs, res);
49                 if (ADS_ERR_OK(status)) {
50                         DEBUG(5,("Search for %s gave %d replies\n",
51                                  exp, ads_count_replies(ads, *res)));
52                         return status;
53                 }
54
55                 if (*res) ads_msgfree(ads, *res);
56                 *res = NULL;
57                 DEBUG(1,("Reopening ads connection after error %s\n", 
58                          ads_errstr(status)));
59                 if (ads->ld) {
60                         /* we should unbind here, but that seems to trigger openldap bugs :(
61                            ldap_unbind(ads->ld); 
62                         */
63                 }
64                 ads->ld = NULL;
65                 status = ads_connect(ads);
66                 if (!ADS_ERR_OK(status)) {
67                         DEBUG(1,("ads_search_retry: failed to reconnect (%s)\n",
68                                  ads_errstr(status)));
69                         ads_destroy(&ads);
70                         return status;
71                 }
72         }
73
74         DEBUG(1,("ads reopen failed after error %s\n", ads_errstr(status)));
75         return status;
76 }
77
78
79 ADS_STATUS ads_search_retry(ADS_STRUCT *ads, void **res, 
80                             const char *exp, 
81                             const char **attrs)
82 {
83         return ads_do_search_retry(ads, ads->bind_path, LDAP_SCOPE_SUBTREE,
84                                    exp, attrs, res);
85 }
86
87 ADS_STATUS ads_search_retry_dn(ADS_STRUCT *ads, void **res, 
88                                const char *dn, 
89                                const char **attrs)
90 {
91         return ads_do_search_retry(ads, dn, LDAP_SCOPE_BASE,
92                                    "(objectclass=*)", attrs, res);
93 }
94
95 /*
96   return our ads connections structure for a domain. We keep the connection
97   open to make things faster
98 */
99 static ADS_STRUCT *ads_cached_connection(struct winbindd_domain *domain)
100 {
101         ADS_STRUCT *ads;
102         ADS_STATUS status;
103         char *ccache;
104         struct in_addr server_ip;
105
106         if (domain->private) {
107                 return (ADS_STRUCT *)domain->private;
108         }
109
110         /* we don't want this to affect the users ccache */
111         ccache = lock_path("winbindd_ccache");
112         SETENV("KRB5CCNAME", ccache, 1);
113         unlink(ccache);
114
115         if (!resolve_name(domain->name, &server_ip, 0x1b)) {
116                 DEBUG(1,("Can't find PDC for domain %s\n", domain->name));
117                 return NULL;
118         }
119
120         ads = ads_init(primary_realm, inet_ntoa(server_ip), NULL, NULL);
121         if (!ads) {
122                 DEBUG(1,("ads_init for domain %s failed\n", domain->name));
123                 return NULL;
124         }
125
126         /* the machine acct password might have change - fetch it every time */
127         SAFE_FREE(ads->password);
128         ads->password = secrets_fetch_machine_password();
129
130         status = ads_connect(ads);
131         if (!ADS_ERR_OK(status)) {
132                 DEBUG(1,("ads_connect for domain %s failed: %s\n", 
133                          domain->name, ads_errstr(status)));
134                 ads_destroy(&ads);
135                 return NULL;
136         }
137
138         /* remember our primary realm for trusted domain support */
139         if (!primary_realm) {
140                 primary_realm = strdup(ads->realm);
141         }
142
143         fstrcpy(domain->full_name, ads->server_realm);
144
145         domain->private = (void *)ads;
146         return ads;
147 }
148
149 /* useful utility */
150 static void sid_from_rid(struct winbindd_domain *domain, uint32 rid, DOM_SID *sid)
151 {
152         sid_copy(sid, &domain->sid);
153         sid_append_rid(sid, rid);
154 }
155
156 /* turn a sAMAccountType into a SID_NAME_USE */
157 static enum SID_NAME_USE ads_atype_map(uint32 atype)
158 {
159         switch (atype & 0xF0000000) {
160         case ATYPE_GROUP:
161                 return SID_NAME_DOM_GRP;
162         case ATYPE_USER:
163                 return SID_NAME_USER;
164         default:
165                 DEBUG(1,("hmm, need to map account type 0x%x\n", atype));
166         }
167         return SID_NAME_UNKNOWN;
168 }
169
170 /* Query display info for a realm. This is the basic user list fn */
171 static NTSTATUS query_user_list(struct winbindd_domain *domain,
172                                TALLOC_CTX *mem_ctx,
173                                uint32 *num_entries, 
174                                WINBIND_USERINFO **info)
175 {
176         ADS_STRUCT *ads = NULL;
177         const char *attrs[] = {"sAMAccountName", "name", "objectSid", "primaryGroupID", 
178                                "sAMAccountType", NULL};
179         int i, count;
180         ADS_STATUS rc;
181         void *res = NULL;
182         void *msg = NULL;
183         NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
184
185         *num_entries = 0;
186
187         DEBUG(3,("ads: query_user_list\n"));
188
189         ads = ads_cached_connection(domain);
190         if (!ads) goto done;
191
192         rc = ads_search_retry(ads, &res, "(objectCategory=user)", attrs);
193         if (!ADS_ERR_OK(rc)) {
194                 DEBUG(1,("query_user_list ads_search: %s\n", ads_errstr(rc)));
195                 goto done;
196         }
197
198         count = ads_count_replies(ads, res);
199         if (count == 0) {
200                 DEBUG(1,("query_user_list: No users found\n"));
201                 goto done;
202         }
203
204         (*info) = talloc(mem_ctx, count * sizeof(**info));
205         if (!*info) {
206                 status = NT_STATUS_NO_MEMORY;
207                 goto done;
208         }
209
210         i = 0;
211
212         for (msg = ads_first_entry(ads, res); msg; msg = ads_next_entry(ads, msg)) {
213                 char *name, *gecos;
214                 DOM_SID sid;
215                 uint32 rid, group;
216                 uint32 atype;
217
218                 if (!ads_pull_uint32(ads, msg, "sAMAccountType", &atype) ||
219                     ads_atype_map(atype) != SID_NAME_USER) {
220                         DEBUG(1,("Not a user account? atype=0x%x\n", atype));
221                         continue;
222                 }
223
224                 name = ads_pull_string(ads, mem_ctx, msg, "sAMAccountName");
225                 gecos = ads_pull_string(ads, mem_ctx, msg, "name");
226                 if (!ads_pull_sid(ads, msg, "objectSid", &sid)) {
227                         DEBUG(1,("No sid for %s !?\n", name));
228                         continue;
229                 }
230                 if (!ads_pull_uint32(ads, msg, "primaryGroupID", &group)) {
231                         DEBUG(1,("No primary group for %s !?\n", name));
232                         continue;
233                 }
234
235                 if (!sid_peek_rid(&sid, &rid)) {
236                         DEBUG(1,("No rid for %s !?\n", name));
237                         continue;
238                 }
239
240                 (*info)[i].acct_name = name;
241                 (*info)[i].full_name = gecos;
242                 (*info)[i].user_rid = rid;
243                 (*info)[i].group_rid = group;
244                 i++;
245         }
246
247         (*num_entries) = i;
248         status = NT_STATUS_OK;
249
250         DEBUG(3,("ads query_user_list gave %d entries\n", (*num_entries)));
251
252 done:
253         if (res) ads_msgfree(ads, res);
254
255         return status;
256 }
257
258 /* list all domain groups */
259 static NTSTATUS enum_dom_groups(struct winbindd_domain *domain,
260                                 TALLOC_CTX *mem_ctx,
261                                 uint32 *num_entries, 
262                                 struct acct_info **info)
263 {
264         ADS_STRUCT *ads = NULL;
265         const char *attrs[] = {"sAMAccountName", "name", "objectSid", 
266                                "sAMAccountType", NULL};
267         int i, count;
268         ADS_STATUS rc;
269         void *res = NULL;
270         void *msg = NULL;
271         NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
272
273         *num_entries = 0;
274
275         DEBUG(3,("ads: enum_dom_groups\n"));
276
277         ads = ads_cached_connection(domain);
278         if (!ads) goto done;
279
280         rc = ads_search_retry(ads, &res, "(objectCategory=group)", attrs);
281         if (!ADS_ERR_OK(rc)) {
282                 DEBUG(1,("query_user_list ads_search: %s\n", ads_errstr(rc)));
283                 goto done;
284         }
285
286         count = ads_count_replies(ads, res);
287         if (count == 0) {
288                 DEBUG(1,("query_user_list: No users found\n"));
289                 goto done;
290         }
291
292         (*info) = talloc(mem_ctx, count * sizeof(**info));
293         if (!*info) {
294                 status = NT_STATUS_NO_MEMORY;
295                 goto done;
296         }
297
298         i = 0;
299
300         for (msg = ads_first_entry(ads, res); msg; msg = ads_next_entry(ads, msg)) {
301                 char *name, *gecos;
302                 DOM_SID sid;
303                 uint32 rid;
304                 uint32 account_type;
305
306                 if (!ads_pull_uint32(ads, msg, "sAMAccountType", 
307                                      &account_type) ||
308                     !(account_type & ATYPE_GROUP)) continue;
309
310                 name = ads_pull_string(ads, mem_ctx, msg, "sAMAccountName");
311                 gecos = ads_pull_string(ads, mem_ctx, msg, "name");
312                 if (!ads_pull_sid(ads, msg, "objectSid", &sid)) {
313                         DEBUG(1,("No sid for %s !?\n", name));
314                         continue;
315                 }
316
317                 if (!sid_peek_rid(&sid, &rid)) {
318                         DEBUG(1,("No rid for %s !?\n", name));
319                         continue;
320                 }
321
322                 fstrcpy((*info)[i].acct_name, name);
323                 fstrcpy((*info)[i].acct_desc, gecos);
324                 (*info)[i].rid = rid;
325                 i++;
326         }
327
328         (*num_entries) = i;
329
330         status = NT_STATUS_OK;
331
332         DEBUG(3,("ads enum_dom_groups gave %d entries\n", (*num_entries)));
333
334 done:
335         if (res) ads_msgfree(ads, res);
336
337         return status;
338 }
339
340
341 /* convert a single name to a sid in a domain */
342 static NTSTATUS name_to_sid(struct winbindd_domain *domain,
343                             const char *name,
344                             DOM_SID *sid,
345                             enum SID_NAME_USE *type)
346 {
347         ADS_STRUCT *ads = NULL;
348         const char *attrs[] = {"objectSid", "sAMAccountType", NULL};
349         int count;
350         ADS_STATUS rc;
351         void *res = NULL;
352         char *exp;
353         uint32 t;
354         fstring name2, dom2, fullname2;
355         NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
356
357         /* sigh. Need to fix interface to give us a raw name */
358         fstrcpy(fullname2, name);
359         fstring_sub(fullname2, "\\", lp_winbind_separator());
360         if (!parse_domain_user(fullname2, dom2, name2)) {
361                 goto done;
362         }
363
364         DEBUG(3,("ads: name_to_sid\n"));
365
366         ads = ads_cached_connection(domain);
367         if (!ads) goto done;
368
369         asprintf(&exp, "(sAMAccountName=%s)", name2);
370         rc = ads_search_retry(ads, &res, exp, attrs);
371         free(exp);
372         if (!ADS_ERR_OK(rc)) {
373                 DEBUG(1,("name_to_sid ads_search: %s\n", ads_errstr(rc)));
374                 goto done;
375         }
376
377         count = ads_count_replies(ads, res);
378         if (count != 1) {
379                 DEBUG(1,("name_to_sid: %s not found\n", name));
380                 goto done;
381         }
382
383         if (!ads_pull_sid(ads, res, "objectSid", sid)) {
384                 DEBUG(1,("No sid for %s !?\n", name));
385                 goto done;
386         }
387
388         if (!ads_pull_uint32(ads, res, "sAMAccountType", &t)) {
389                 DEBUG(1,("No sAMAccountType for %s !?\n", name));
390                 goto done;
391         }
392
393         *type = ads_atype_map(t);
394
395         status = NT_STATUS_OK;
396
397         DEBUG(3,("ads name_to_sid mapped %s\n", name));
398
399 done:
400         if (res) ads_msgfree(ads, res);
401
402         return status;
403 }
404
405 /* convert a sid to a user or group name */
406 static NTSTATUS sid_to_name(struct winbindd_domain *domain,
407                             TALLOC_CTX *mem_ctx,
408                             DOM_SID *sid,
409                             char **name,
410                             enum SID_NAME_USE *type)
411 {
412         ADS_STRUCT *ads = NULL;
413         const char *attrs[] = {"sAMAccountName", "sAMAccountType", NULL};
414         ADS_STATUS rc;
415         void *msg = NULL;
416         char *exp;
417         char *sidstr;
418         uint32 atype;
419         char *s;
420         NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
421
422         DEBUG(3,("ads: sid_to_name\n"));
423
424         ads = ads_cached_connection(domain);
425         if (!ads) goto done;
426
427         sidstr = sid_binstring(sid);
428         asprintf(&exp, "(objectSid=%s)", sidstr);
429         rc = ads_search_retry(ads, &msg, exp, attrs);
430         free(exp);
431         free(sidstr);
432         if (!ADS_ERR_OK(rc)) {
433                 DEBUG(1,("sid_to_name ads_search: %s\n", ads_errstr(rc)));
434                 goto done;
435         }
436
437         if (!ads_pull_uint32(ads, msg, "sAMAccountType", &atype)) {
438                 goto done;
439         }
440
441         s = ads_pull_string(ads, mem_ctx, msg, "sAMAccountName");
442         *name = talloc_asprintf(mem_ctx, "%s%s%s", domain->name, lp_winbind_separator(), s);
443         *type = ads_atype_map(atype);
444
445         status = NT_STATUS_OK;
446
447         DEBUG(3,("ads sid_to_name mapped %s\n", *name));
448
449 done:
450         if (msg) ads_msgfree(ads, msg);
451
452         return status;
453 }
454
455
456 /* Lookup user information from a rid */
457 static NTSTATUS query_user(struct winbindd_domain *domain, 
458                            TALLOC_CTX *mem_ctx, 
459                            uint32 user_rid, 
460                            WINBIND_USERINFO *info)
461 {
462         ADS_STRUCT *ads = NULL;
463         const char *attrs[] = {"sAMAccountName", "name", "objectSid", 
464                                "primaryGroupID", NULL};
465         ADS_STATUS rc;
466         int count;
467         void *msg = NULL;
468         char *exp;
469         DOM_SID sid;
470         char *sidstr;
471         NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
472
473         DEBUG(3,("ads: query_user\n"));
474
475         sid_from_rid(domain, user_rid, &sid);
476
477         ads = ads_cached_connection(domain);
478         if (!ads) goto done;
479
480         sidstr = sid_binstring(&sid);
481         asprintf(&exp, "(objectSid=%s)", sidstr);
482         rc = ads_search_retry(ads, &msg, exp, attrs);
483         free(exp);
484         free(sidstr);
485         if (!ADS_ERR_OK(rc)) {
486                 DEBUG(1,("query_user(rid=%d) ads_search: %s\n", user_rid, ads_errstr(rc)));
487                 goto done;
488         }
489
490         count = ads_count_replies(ads, msg);
491         if (count != 1) {
492                 DEBUG(1,("query_user(rid=%d): Not found\n", user_rid));
493                 goto done;
494         }
495
496         info->acct_name = ads_pull_string(ads, mem_ctx, msg, "sAMAccountName");
497         info->full_name = ads_pull_string(ads, mem_ctx, msg, "name");
498         if (!ads_pull_sid(ads, msg, "objectSid", &sid)) {
499                 DEBUG(1,("No sid for %d !?\n", user_rid));
500                 goto done;
501         }
502         if (!ads_pull_uint32(ads, msg, "primaryGroupID", &info->group_rid)) {
503                 DEBUG(1,("No primary group for %d !?\n", user_rid));
504                 goto done;
505         }
506         
507         if (!sid_peek_rid(&sid, &info->user_rid)) {
508                 DEBUG(1,("No rid for %d !?\n", user_rid));
509                 goto done;
510         }
511
512         status = NT_STATUS_OK;
513
514         DEBUG(3,("ads query_user gave %s\n", info->acct_name));
515 done:
516         if (msg) ads_msgfree(ads, msg);
517
518         return status;
519 }
520
521
522 /* Lookup groups a user is a member of. */
523 static NTSTATUS lookup_usergroups(struct winbindd_domain *domain,
524                                   TALLOC_CTX *mem_ctx,
525                                   uint32 user_rid, 
526                                   uint32 *num_groups, uint32 **user_gids)
527 {
528         ADS_STRUCT *ads = NULL;
529         const char *attrs[] = {"distinguishedName", NULL};
530         const char *attrs2[] = {"tokenGroups", "primaryGroupID", NULL};
531         ADS_STATUS rc;
532         int count;
533         void *msg = NULL;
534         char *exp;
535         char *user_dn;
536         DOM_SID *sids;
537         int i;
538         uint32 primary_group;
539         DOM_SID sid;
540         char *sidstr;
541         NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
542
543         *num_groups = 0;
544
545         DEBUG(3,("ads: lookup_usergroups\n"));
546
547         (*num_groups) = 0;
548
549         sid_from_rid(domain, user_rid, &sid);
550
551         ads = ads_cached_connection(domain);
552         if (!ads) goto done;
553
554         sidstr = sid_binstring(&sid);
555         asprintf(&exp, "(objectSid=%s)", sidstr);
556         rc = ads_search_retry(ads, &msg, exp, attrs);
557         free(exp);
558         free(sidstr);
559         if (!ADS_ERR_OK(rc)) {
560                 DEBUG(1,("lookup_usergroups(rid=%d) ads_search: %s\n", user_rid, ads_errstr(rc)));
561                 goto done;
562         }
563
564         user_dn = ads_pull_string(ads, mem_ctx, msg, "distinguishedName");
565
566         if (msg) ads_msgfree(ads, msg);
567
568         rc = ads_search_retry_dn(ads, &msg, user_dn, attrs2);
569         if (!ADS_ERR_OK(rc)) {
570                 DEBUG(1,("lookup_usergroups(rid=%d) ads_search tokenGroups: %s\n", user_rid, ads_errstr(rc)));
571                 goto done;
572         }
573
574         if (!ads_pull_uint32(ads, msg, "primaryGroupID", &primary_group)) {
575                 DEBUG(1,("%s: No primary group for rid=%d !?\n", domain->name, user_rid));
576                 goto done;
577         }
578
579         count = ads_pull_sids(ads, mem_ctx, msg, "tokenGroups", &sids) + 1;
580         (*user_gids) = (uint32 *)talloc(mem_ctx, sizeof(uint32) * count);
581         (*user_gids)[(*num_groups)++] = primary_group;
582
583         for (i=1;i<count;i++) {
584                 uint32 rid;
585                 if (!sid_peek_rid(&sids[i-1], &rid)) continue;
586                 (*user_gids)[*num_groups] = rid;
587                 (*num_groups)++;
588         }
589
590         status = NT_STATUS_OK;
591         DEBUG(3,("ads lookup_usergroups for rid=%d\n", user_rid));
592 done:
593         if (msg) ads_msgfree(ads, msg);
594
595         return status;
596 }
597
598
599 static NTSTATUS lookup_groupmem(struct winbindd_domain *domain,
600                                 TALLOC_CTX *mem_ctx,
601                                 uint32 group_rid, uint32 *num_names, 
602                                 uint32 **rid_mem, char ***names, 
603                                 uint32 **name_types)
604 {
605         DOM_SID group_sid;
606         char *sidstr;
607         const char *attrs[] = {"sAMAccountName", "objectSid", "sAMAccountType", NULL};
608         ADS_STATUS rc;
609         int count;
610         void *res=NULL, *msg=NULL;
611         ADS_STRUCT *ads = NULL;
612         char *exp;
613         NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
614
615         *num_names = 0;
616
617         ads = ads_cached_connection(domain);
618         if (!ads) goto done;
619
620         sid_from_rid(domain, group_rid, &group_sid);
621         sidstr = sid_binstring(&group_sid);
622         /* search for all users who have that group sid as primary group or as member */
623         asprintf(&exp, "(&(objectCategory=user)(|(primaryGroupID=%d)(memberOf=%s)))",
624                  group_rid, sidstr);
625         rc = ads_search_retry(ads, &res, exp, attrs);
626         free(exp);
627         free(sidstr);
628         if (!ADS_ERR_OK(rc)) {
629                 DEBUG(1,("query_user_list ads_search: %s\n", ads_errstr(rc)));
630                 goto done;
631         }
632
633         count = ads_count_replies(ads, res);
634         if (count == 0) {
635                 status = NT_STATUS_OK;
636                 goto done;
637         }
638
639         (*rid_mem) = talloc(mem_ctx, sizeof(uint32) * count);
640         (*name_types) = talloc(mem_ctx, sizeof(uint32) * count);
641         (*names) = talloc(mem_ctx, sizeof(char *) * count);
642
643         for (msg = ads_first_entry(ads, res); msg; msg = ads_next_entry(ads, msg)) {
644                 uint32 atype, rid;
645                 DOM_SID sid;
646
647                 (*names)[*num_names] = ads_pull_string(ads, mem_ctx, msg, "sAMAccountName");
648                 if (!ads_pull_uint32(ads, msg, "sAMAccountType", &atype)) {
649                         continue;
650                 }
651                 (*name_types)[*num_names] = ads_atype_map(atype);
652                 if (!ads_pull_sid(ads, msg, "objectSid", &sid)) {
653                         DEBUG(1,("No sid for %s !?\n", (*names)[*num_names]));
654                         continue;
655                 }
656                 if (!sid_peek_rid(&sid, &rid)) {
657                         DEBUG(1,("No rid for %s !?\n", (*names)[*num_names]));
658                         continue;
659                 }
660                 (*rid_mem)[*num_names] = rid;
661                 (*num_names)++;
662         }       
663
664         status = NT_STATUS_OK;
665         DEBUG(3,("ads lookup_groupmem for rid=%d\n", group_rid));
666 done:
667         if (res) ads_msgfree(ads, res);
668
669         return status;
670 }
671
672 /* find the sequence number for a domain */
673 static NTSTATUS sequence_number(struct winbindd_domain *domain, uint32 *seq)
674 {
675         ADS_STRUCT *ads = NULL;
676         ADS_STATUS rc;
677
678         *seq = DOM_SEQUENCE_NONE;
679
680         ads = ads_cached_connection(domain);
681         if (!ads) return NT_STATUS_UNSUCCESSFUL;
682
683         rc = ads_USN(ads, seq);
684         return ads_ntstatus(rc);
685 }
686
687 /* get a list of trusted domains */
688 static NTSTATUS trusted_domains(struct winbindd_domain *domain,
689                                 TALLOC_CTX *mem_ctx,
690                                 uint32 *num_domains,
691                                 char ***names,
692                                 DOM_SID **dom_sids)
693 {
694         ADS_STRUCT *ads = NULL;
695         ADS_STATUS rc;
696
697         *num_domains = 0;
698         *names = NULL;
699
700         ads = ads_cached_connection(domain);
701         if (!ads) return NT_STATUS_UNSUCCESSFUL;
702
703         rc = ads_trusted_domains(ads, mem_ctx, num_domains, names, dom_sids);
704
705         return ads_ntstatus(rc);
706 }
707
708 /* find the domain sid for a domain */
709 static NTSTATUS domain_sid(struct winbindd_domain *domain, DOM_SID *sid)
710 {
711         NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
712         const char *attrs[] = {"objectSid", NULL};
713         ADS_STRUCT *ads = NULL;
714         void *res;
715         ADS_STATUS rc;
716
717         ads = ads_cached_connection(domain);
718         if (!ads) goto done;
719
720         rc = ads_do_search(ads, ads->bind_path, LDAP_SCOPE_BASE, "(objectclass=*)", 
721                            attrs, &res);
722         if (!ADS_ERR_OK(rc)) goto done;
723         if (ads_pull_sid(ads, res, "objectSid", sid)) {
724                 status = NT_STATUS_OK;
725         }
726         ads_msgfree(ads, res);
727
728 done:
729         return status;
730 }
731
732 /* the ADS backend methods are exposed via this structure */
733 struct winbindd_methods ads_methods = {
734         True,
735         query_user_list,
736         enum_dom_groups,
737         name_to_sid,
738         sid_to_name,
739         query_user,
740         lookup_usergroups,
741         lookup_groupmem,
742         sequence_number,
743         trusted_domains,
744         domain_sid
745 };
746
747 #endif