- added initial support for trusted domains in winbindd_ads
[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 int 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         int rc = -1;
40         ADS_RETURN_CODE rc2;
41         int count = 3;
42
43         if (!ads->ld &&
44             time(NULL) - ads->last_attempt < ADS_RECONNECT_TIME) {
45                 return LDAP_SERVER_DOWN;
46         }
47
48         while (count--) {
49                 rc = ads_do_search(ads, bind_path, scope, exp, attrs, res);
50                 if (rc == 0) {
51                         DEBUG(5,("Search for %s gave %d replies\n",
52                                  exp, ads_count_replies(ads, *res)));
53                         return rc;
54                 }
55
56                 if (*res) ads_msgfree(ads, *res);
57                 *res = NULL;
58                 DEBUG(1,("Reopening ads connection after error %s\n", ads_errstr(rc)));
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                 rc2 = ads_connect(ads);
66                 if (rc2.rc) {
67                     DEBUG(1,("ads_search_retry: failed to reconnect:\n"));
68                     if(rc2.error_type) 
69                         ads_display_status("", rc2.rc, rc2.minor_status);
70                     else 
71                         DEBUG(1,("LDAP error: %s\n", ads_errstr(rc2.rc)));
72                     
73                     ads_destroy(&ads);
74                     return rc2.rc;
75                 }
76         }
77         DEBUG(1,("ads reopen failed after error %s\n", ads_errstr(rc)));
78         return rc;
79 }
80
81
82 int ads_search_retry(ADS_STRUCT *ads, void **res, 
83                      const char *exp, 
84                      const char **attrs)
85 {
86         return ads_do_search_retry(ads, ads->bind_path, LDAP_SCOPE_SUBTREE,
87                                    exp, attrs, res);
88 }
89
90 int ads_search_retry_dn(ADS_STRUCT *ads, void **res, 
91                         const char *dn, 
92                         const char **attrs)
93 {
94         return ads_do_search_retry(ads, dn, LDAP_SCOPE_BASE,
95                                    "(objectclass=*)", attrs, res);
96 }
97
98 /*
99   return our ads connections structure for a domain. We keep the connection
100   open to make things faster
101 */
102 static ADS_STRUCT *ads_cached_connection(struct winbindd_domain *domain)
103 {
104         ADS_STRUCT *ads;
105         ADS_RETURN_CODE rc;
106         char *ccache;
107         struct in_addr server_ip;
108
109         if (domain->private) {
110                 return (ADS_STRUCT *)domain->private;
111         }
112
113         /* we don't want this to affect the users ccache */
114         ccache = lock_path("winbindd_ccache");
115         SETENV("KRB5CCNAME", ccache, 1);
116         unlink(ccache);
117
118         if (!resolve_name(domain->name, &server_ip, 0x1b)) {
119                 DEBUG(1,("Can't find PDC for domain %s\n", domain->name));
120                 return NULL;
121         }
122
123         ads = ads_init(primary_realm, inet_ntoa(server_ip), NULL, NULL);
124         if (!ads) {
125                 DEBUG(1,("ads_init for domain %s failed\n", domain->name));
126                 return NULL;
127         }
128
129         /* the machine acct password might have change - fetch it every time */
130         SAFE_FREE(ads->password);
131         ads->password = secrets_fetch_machine_password();
132
133         rc = ads_connect(ads);
134         if (rc.rc) {
135                 DEBUG(1,("ads_connect for domain %s failed:\n", domain->name));
136                 if(rc.error_type) 
137                     ads_display_status("", rc.rc, rc.minor_status);
138                 else 
139                     DEBUG(1,("LDAP error: %s\n", ads_errstr(rc.rc)));
140                     
141                 ads_destroy(&ads);
142                 return NULL;
143         }
144
145         /* remember our primary realm for trusted domain support */
146         if (!primary_realm) {
147                 primary_realm = strdup(ads->realm);
148         }
149
150         domain->private = (void *)ads;
151         return ads;
152 }
153
154 /* useful utility */
155 static void sid_from_rid(struct winbindd_domain *domain, uint32 rid, DOM_SID *sid)
156 {
157         sid_copy(sid, &domain->sid);
158         sid_append_rid(sid, rid);
159 }
160
161 /* turn a sAMAccountType into a SID_NAME_USE */
162 static enum SID_NAME_USE ads_atype_map(uint32 atype)
163 {
164         switch (atype & 0xF0000000) {
165         case ATYPE_GROUP:
166                 return SID_NAME_DOM_GRP;
167         case ATYPE_USER:
168                 return SID_NAME_USER;
169         default:
170                 DEBUG(1,("hmm, need to map account type 0x%x\n", atype));
171         }
172         return SID_NAME_UNKNOWN;
173 }
174
175 /* Query display info for a realm. This is the basic user list fn */
176 static NTSTATUS query_user_list(struct winbindd_domain *domain,
177                                TALLOC_CTX *mem_ctx,
178                                uint32 *num_entries, 
179                                WINBIND_USERINFO **info)
180 {
181         ADS_STRUCT *ads = NULL;
182         const char *attrs[] = {"sAMAccountName", "name", "objectSid", "primaryGroupID", 
183                                "sAMAccountType", NULL};
184         int rc, i, count;
185         void *res = NULL;
186         void *msg = NULL;
187         NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
188
189         *num_entries = 0;
190
191         DEBUG(3,("ads: query_user_list\n"));
192
193         ads = ads_cached_connection(domain);
194         if (!ads) goto done;
195
196         rc = ads_search_retry(ads, &res, "(objectCategory=user)", attrs);
197         if (rc) {
198                 DEBUG(1,("query_user_list ads_search: %s\n", ads_errstr(rc)));
199                 goto done;
200         }
201
202         count = ads_count_replies(ads, res);
203         if (count == 0) {
204                 DEBUG(1,("query_user_list: No users found\n"));
205                 goto done;
206         }
207
208         (*info) = talloc(mem_ctx, count * sizeof(**info));
209         if (!*info) {
210                 status = NT_STATUS_NO_MEMORY;
211                 goto done;
212         }
213
214         i = 0;
215
216         for (msg = ads_first_entry(ads, res); msg; msg = ads_next_entry(ads, msg)) {
217                 char *name, *gecos;
218                 DOM_SID sid;
219                 uint32 rid, group;
220                 uint32 atype;
221
222                 if (!ads_pull_uint32(ads, msg, "sAMAccountType", &atype) ||
223                     ads_atype_map(atype) != SID_NAME_USER) {
224                         DEBUG(1,("Not a user account? atype=0x%x\n", atype));
225                         continue;
226                 }
227
228                 name = ads_pull_string(ads, mem_ctx, msg, "sAMAccountName");
229                 gecos = ads_pull_string(ads, mem_ctx, msg, "name");
230                 if (!ads_pull_sid(ads, msg, "objectSid", &sid)) {
231                         DEBUG(1,("No sid for %s !?\n", name));
232                         continue;
233                 }
234                 if (!ads_pull_uint32(ads, msg, "primaryGroupID", &group)) {
235                         DEBUG(1,("No primary group for %s !?\n", name));
236                         continue;
237                 }
238
239                 if (!sid_peek_rid(&sid, &rid)) {
240                         DEBUG(1,("No rid for %s !?\n", name));
241                         continue;
242                 }
243
244                 (*info)[i].acct_name = name;
245                 (*info)[i].full_name = gecos;
246                 (*info)[i].user_rid = rid;
247                 (*info)[i].group_rid = group;
248                 i++;
249         }
250
251         (*num_entries) = i;
252         status = NT_STATUS_OK;
253
254         DEBUG(3,("ads query_user_list gave %d entries\n", (*num_entries)));
255
256 done:
257         if (res) ads_msgfree(ads, res);
258
259         return status;
260 }
261
262 /* list all domain groups */
263 static NTSTATUS enum_dom_groups(struct winbindd_domain *domain,
264                                 TALLOC_CTX *mem_ctx,
265                                 uint32 *num_entries, 
266                                 struct acct_info **info)
267 {
268         ADS_STRUCT *ads = NULL;
269         const char *attrs[] = {"sAMAccountName", "name", "objectSid", 
270                                "sAMAccountType", NULL};
271         int rc, i, count;
272         void *res = NULL;
273         void *msg = NULL;
274         NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
275
276         *num_entries = 0;
277
278         DEBUG(3,("ads: enum_dom_groups\n"));
279
280         ads = ads_cached_connection(domain);
281         if (!ads) goto done;
282
283         rc = ads_search_retry(ads, &res, "(objectCategory=group)", attrs);
284         if (rc) {
285                 DEBUG(1,("query_user_list ads_search: %s\n", ads_errstr(rc)));
286                 goto done;
287         }
288
289         count = ads_count_replies(ads, res);
290         if (count == 0) {
291                 DEBUG(1,("query_user_list: No users found\n"));
292                 goto done;
293         }
294
295         (*info) = talloc(mem_ctx, count * sizeof(**info));
296         if (!*info) {
297                 status = NT_STATUS_NO_MEMORY;
298                 goto done;
299         }
300
301         i = 0;
302
303         for (msg = ads_first_entry(ads, res); msg; msg = ads_next_entry(ads, msg)) {
304                 char *name, *gecos;
305                 DOM_SID sid;
306                 uint32 rid;
307                 uint32 account_type;
308
309                 if (!ads_pull_uint32(ads, msg, "sAMAccountType", 
310                                      &account_type) ||
311                     !(account_type & ATYPE_GROUP)) continue;
312
313                 name = ads_pull_string(ads, mem_ctx, msg, "sAMAccountName");
314                 gecos = ads_pull_string(ads, mem_ctx, msg, "name");
315                 if (!ads_pull_sid(ads, msg, "objectSid", &sid)) {
316                         DEBUG(1,("No sid for %s !?\n", name));
317                         continue;
318                 }
319
320                 if (!sid_peek_rid(&sid, &rid)) {
321                         DEBUG(1,("No rid for %s !?\n", name));
322                         continue;
323                 }
324
325                 fstrcpy((*info)[i].acct_name, name);
326                 fstrcpy((*info)[i].acct_desc, gecos);
327                 (*info)[i].rid = rid;
328                 i++;
329         }
330
331         (*num_entries) = i;
332
333         status = NT_STATUS_OK;
334
335         DEBUG(3,("ads enum_dom_groups gave %d entries\n", (*num_entries)));
336
337 done:
338         if (res) ads_msgfree(ads, res);
339
340         return status;
341 }
342
343
344 /* convert a single name to a sid in a domain */
345 static NTSTATUS name_to_sid(struct winbindd_domain *domain,
346                             const char *name,
347                             DOM_SID *sid,
348                             enum SID_NAME_USE *type)
349 {
350         ADS_STRUCT *ads = NULL;
351         const char *attrs[] = {"objectSid", "sAMAccountType", NULL};
352         int rc, count;
353         void *res = NULL;
354         char *exp;
355         uint32 t;
356         fstring name2, dom2, fullname2;
357         NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
358
359         /* sigh. Need to fix interface to give us a raw name */
360         fstrcpy(fullname2, name);
361         fstring_sub(fullname2, "\\", lp_winbind_separator());
362         if (!parse_domain_user(fullname2, dom2, name2)) {
363                 goto done;
364         }
365
366         DEBUG(3,("ads: name_to_sid\n"));
367
368         ads = ads_cached_connection(domain);
369         if (!ads) goto done;
370
371         asprintf(&exp, "(sAMAccountName=%s)", name2);
372         rc = ads_search_retry(ads, &res, exp, attrs);
373         free(exp);
374         if (rc) {
375                 DEBUG(1,("name_to_sid ads_search: %s\n", ads_errstr(rc)));
376                 goto done;
377         }
378
379         count = ads_count_replies(ads, res);
380         if (count != 1) {
381                 DEBUG(1,("name_to_sid: %s not found\n", name));
382                 goto done;
383         }
384
385         if (!ads_pull_sid(ads, res, "objectSid", sid)) {
386                 DEBUG(1,("No sid for %s !?\n", name));
387                 goto done;
388         }
389
390         if (!ads_pull_uint32(ads, res, "sAMAccountType", &t)) {
391                 DEBUG(1,("No sAMAccountType for %s !?\n", name));
392                 goto done;
393         }
394
395         *type = ads_atype_map(t);
396
397         status = NT_STATUS_OK;
398
399         DEBUG(3,("ads name_to_sid mapped %s\n", name));
400
401 done:
402         if (res) ads_msgfree(ads, res);
403
404         return status;
405 }
406
407 /* convert a sid to a user or group name */
408 static NTSTATUS sid_to_name(struct winbindd_domain *domain,
409                             TALLOC_CTX *mem_ctx,
410                             DOM_SID *sid,
411                             char **name,
412                             enum SID_NAME_USE *type)
413 {
414         ADS_STRUCT *ads = NULL;
415         const char *attrs[] = {"sAMAccountName", "sAMAccountType", NULL};
416         int rc;
417         void *msg = NULL;
418         char *exp;
419         char *sidstr;
420         uint32 atype;
421         char *s;
422         NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
423
424         DEBUG(3,("ads: sid_to_name\n"));
425
426         ads = ads_cached_connection(domain);
427         if (!ads) goto done;
428
429         sidstr = sid_binstring(sid);
430         asprintf(&exp, "(objectSid=%s)", sidstr);
431         rc = ads_search_retry(ads, &msg, exp, attrs);
432         free(exp);
433         free(sidstr);
434         if (rc) {
435                 DEBUG(1,("sid_to_name ads_search: %s\n", ads_errstr(rc)));
436                 goto done;
437         }
438
439         if (!ads_pull_uint32(ads, msg, "sAMAccountType", &atype)) {
440                 goto done;
441         }
442
443         s = ads_pull_string(ads, mem_ctx, msg, "sAMAccountName");
444         *name = talloc_asprintf(mem_ctx, "%s%s%s", domain->name, lp_winbind_separator(), s);
445         *type = ads_atype_map(atype);
446
447         status = NT_STATUS_OK;
448
449         DEBUG(3,("ads sid_to_name mapped %s\n", *name));
450
451 done:
452         if (msg) ads_msgfree(ads, msg);
453
454         return status;
455 }
456
457
458 /* Lookup user information from a rid */
459 static NTSTATUS query_user(struct winbindd_domain *domain, 
460                            TALLOC_CTX *mem_ctx, 
461                            uint32 user_rid, 
462                            WINBIND_USERINFO *info)
463 {
464         ADS_STRUCT *ads = NULL;
465         const char *attrs[] = {"sAMAccountName", "name", "objectSid", 
466                                "primaryGroupID", NULL};
467         int rc, count;
468         void *msg = NULL;
469         char *exp;
470         DOM_SID sid;
471         char *sidstr;
472         NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
473
474         DEBUG(3,("ads: query_user\n"));
475
476         sid_from_rid(domain, user_rid, &sid);
477
478         ads = ads_cached_connection(domain);
479         if (!ads) goto done;
480
481         sidstr = sid_binstring(&sid);
482         asprintf(&exp, "(objectSid=%s)", sidstr);
483         rc = ads_search_retry(ads, &msg, exp, attrs);
484         free(exp);
485         free(sidstr);
486         if (rc) {
487                 DEBUG(1,("query_user(rid=%d) ads_search: %s\n", user_rid, ads_errstr(rc)));
488                 goto done;
489         }
490
491         count = ads_count_replies(ads, msg);
492         if (count != 1) {
493                 DEBUG(1,("query_user(rid=%d): Not found\n", user_rid));
494                 goto done;
495         }
496
497         info->acct_name = ads_pull_string(ads, mem_ctx, msg, "sAMAccountName");
498         info->full_name = ads_pull_string(ads, mem_ctx, msg, "name");
499         if (!ads_pull_sid(ads, msg, "objectSid", &sid)) {
500                 DEBUG(1,("No sid for %d !?\n", user_rid));
501                 goto done;
502         }
503         if (!ads_pull_uint32(ads, msg, "primaryGroupID", &info->group_rid)) {
504                 DEBUG(1,("No primary group for %d !?\n", user_rid));
505                 goto done;
506         }
507         
508         if (!sid_peek_rid(&sid, &info->user_rid)) {
509                 DEBUG(1,("No rid for %d !?\n", user_rid));
510                 goto done;
511         }
512
513         status = NT_STATUS_OK;
514
515         DEBUG(3,("ads query_user gave %s\n", info->acct_name));
516 done:
517         if (msg) ads_msgfree(ads, msg);
518
519         return status;
520 }
521
522
523 /* Lookup groups a user is a member of. */
524 static NTSTATUS lookup_usergroups(struct winbindd_domain *domain,
525                                   TALLOC_CTX *mem_ctx,
526                                   uint32 user_rid, 
527                                   uint32 *num_groups, uint32 **user_gids)
528 {
529         ADS_STRUCT *ads = NULL;
530         const char *attrs[] = {"distinguishedName", NULL};
531         const char *attrs2[] = {"tokenGroups", "primaryGroupID", NULL};
532         int rc, 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 (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 (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         int rc, count;
609         void *res=NULL, *msg=NULL;
610         ADS_STRUCT *ads = NULL;
611         char *exp;
612         NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
613
614         *num_names = 0;
615
616         ads = ads_cached_connection(domain);
617         if (!ads) goto done;
618
619         sid_from_rid(domain, group_rid, &group_sid);
620         sidstr = sid_binstring(&group_sid);
621         /* search for all users who have that group sid as primary group or as member */
622         asprintf(&exp, "(&(objectCategory=user)(|(primaryGroupID=%d)(memberOf=%s)))",
623                  group_rid, sidstr);
624         rc = ads_search_retry(ads, &res, exp, attrs);
625         free(exp);
626         free(sidstr);
627         if (rc) {
628                 DEBUG(1,("query_user_list ads_search: %s\n", ads_errstr(rc)));
629                 goto done;
630         }
631
632         count = ads_count_replies(ads, res);
633         if (count == 0) {
634                 status = NT_STATUS_OK;
635                 goto done;
636         }
637
638         (*rid_mem) = talloc(mem_ctx, sizeof(uint32) * count);
639         (*name_types) = talloc(mem_ctx, sizeof(uint32) * count);
640         (*names) = talloc(mem_ctx, sizeof(char *) * count);
641
642         for (msg = ads_first_entry(ads, res); msg; msg = ads_next_entry(ads, msg)) {
643                 uint32 atype, rid;
644                 DOM_SID sid;
645
646                 (*names)[*num_names] = ads_pull_string(ads, mem_ctx, msg, "sAMAccountName");
647                 if (!ads_pull_uint32(ads, msg, "sAMAccountType", &atype)) {
648                         continue;
649                 }
650                 (*name_types)[*num_names] = ads_atype_map(atype);
651                 if (!ads_pull_sid(ads, msg, "objectSid", &sid)) {
652                         DEBUG(1,("No sid for %s !?\n", (*names)[*num_names]));
653                         continue;
654                 }
655                 if (!sid_peek_rid(&sid, &rid)) {
656                         DEBUG(1,("No rid for %s !?\n", (*names)[*num_names]));
657                         continue;
658                 }
659                 (*rid_mem)[*num_names] = rid;
660                 (*num_names)++;
661         }       
662
663         status = NT_STATUS_OK;
664         DEBUG(3,("ads lookup_groupmem for rid=%d\n", group_rid));
665 done:
666         if (res) ads_msgfree(ads, res);
667
668         return status;
669 }
670
671 /* find the sequence number for a domain */
672 static NTSTATUS sequence_number(struct winbindd_domain *domain, uint32 *seq)
673 {
674         ADS_STRUCT *ads = NULL;
675
676         *seq = DOM_SEQUENCE_NONE;
677
678         ads = ads_cached_connection(domain);
679         if (!ads) return NT_STATUS_UNSUCCESSFUL;
680
681         if (!ads_USN(ads, seq)) {
682                 return NT_STATUS_UNSUCCESSFUL;
683         }
684
685         return NT_STATUS_OK;
686 }
687
688 /* get a list of trusted domains */
689 static NTSTATUS trusted_domains(struct winbindd_domain *domain,
690                                 TALLOC_CTX *mem_ctx,
691                                 uint32 *num_domains,
692                                 char ***names,
693                                 DOM_SID **dom_sids)
694 {
695         ADS_STRUCT *ads = NULL;
696
697         *num_domains = 0;
698         *names = NULL;
699
700         ads = ads_cached_connection(domain);
701         if (!ads) return NT_STATUS_UNSUCCESSFUL;
702
703         if (!ads_trusted_domains(ads, mem_ctx, num_domains, names, dom_sids)) {
704                 return NT_STATUS_UNSUCCESSFUL;
705         }
706
707         return NT_STATUS_OK;
708 }
709
710 /* find the domain sid for a domain */
711 static NTSTATUS domain_sid(struct winbindd_domain *domain, DOM_SID *sid)
712 {
713         NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
714         const char *attrs[] = {"objectSid", NULL};
715         ADS_STRUCT *ads = NULL;
716         void *res;
717         int rc;
718
719         ads = ads_cached_connection(domain);
720         if (!ads) goto done;
721
722         rc = ads_do_search(ads, ads->bind_path, LDAP_SCOPE_BASE, "(objectclass=*)", 
723                            attrs, &res);
724         if (rc) goto done;
725         if (ads_pull_sid(ads, res, "objectSid", sid)) {
726                 status = NT_STATUS_OK;
727         }
728         ads_msgfree(ads, res);
729
730 done:
731         return status;
732 }
733
734 /* the ADS backend methods are exposed via this structure */
735 struct winbindd_methods ads_methods = {
736         True,
737         query_user_list,
738         enum_dom_groups,
739         name_to_sid,
740         sid_to_name,
741         query_user,
742         lookup_usergroups,
743         lookup_groupmem,
744         sequence_number,
745         trusted_domains,
746         domain_sid
747 };
748
749 #endif