added trusted realm support to ADS authentication
[jra/samba/.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         fstrcpy(domain->full_name, ads->server_realm);
151
152         domain->private = (void *)ads;
153         return ads;
154 }
155
156 /* useful utility */
157 static void sid_from_rid(struct winbindd_domain *domain, uint32 rid, DOM_SID *sid)
158 {
159         sid_copy(sid, &domain->sid);
160         sid_append_rid(sid, rid);
161 }
162
163 /* turn a sAMAccountType into a SID_NAME_USE */
164 static enum SID_NAME_USE ads_atype_map(uint32 atype)
165 {
166         switch (atype & 0xF0000000) {
167         case ATYPE_GROUP:
168                 return SID_NAME_DOM_GRP;
169         case ATYPE_USER:
170                 return SID_NAME_USER;
171         default:
172                 DEBUG(1,("hmm, need to map account type 0x%x\n", atype));
173         }
174         return SID_NAME_UNKNOWN;
175 }
176
177 /* Query display info for a realm. This is the basic user list fn */
178 static NTSTATUS query_user_list(struct winbindd_domain *domain,
179                                TALLOC_CTX *mem_ctx,
180                                uint32 *num_entries, 
181                                WINBIND_USERINFO **info)
182 {
183         ADS_STRUCT *ads = NULL;
184         const char *attrs[] = {"sAMAccountName", "name", "objectSid", "primaryGroupID", 
185                                "sAMAccountType", NULL};
186         int rc, i, count;
187         void *res = NULL;
188         void *msg = NULL;
189         NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
190
191         *num_entries = 0;
192
193         DEBUG(3,("ads: query_user_list\n"));
194
195         ads = ads_cached_connection(domain);
196         if (!ads) goto done;
197
198         rc = ads_search_retry(ads, &res, "(objectCategory=user)", attrs);
199         if (rc) {
200                 DEBUG(1,("query_user_list ads_search: %s\n", ads_errstr(rc)));
201                 goto done;
202         }
203
204         count = ads_count_replies(ads, res);
205         if (count == 0) {
206                 DEBUG(1,("query_user_list: No users found\n"));
207                 goto done;
208         }
209
210         (*info) = talloc(mem_ctx, count * sizeof(**info));
211         if (!*info) {
212                 status = NT_STATUS_NO_MEMORY;
213                 goto done;
214         }
215
216         i = 0;
217
218         for (msg = ads_first_entry(ads, res); msg; msg = ads_next_entry(ads, msg)) {
219                 char *name, *gecos;
220                 DOM_SID sid;
221                 uint32 rid, group;
222                 uint32 atype;
223
224                 if (!ads_pull_uint32(ads, msg, "sAMAccountType", &atype) ||
225                     ads_atype_map(atype) != SID_NAME_USER) {
226                         DEBUG(1,("Not a user account? atype=0x%x\n", atype));
227                         continue;
228                 }
229
230                 name = ads_pull_string(ads, mem_ctx, msg, "sAMAccountName");
231                 gecos = ads_pull_string(ads, mem_ctx, msg, "name");
232                 if (!ads_pull_sid(ads, msg, "objectSid", &sid)) {
233                         DEBUG(1,("No sid for %s !?\n", name));
234                         continue;
235                 }
236                 if (!ads_pull_uint32(ads, msg, "primaryGroupID", &group)) {
237                         DEBUG(1,("No primary group for %s !?\n", name));
238                         continue;
239                 }
240
241                 if (!sid_peek_rid(&sid, &rid)) {
242                         DEBUG(1,("No rid for %s !?\n", name));
243                         continue;
244                 }
245
246                 (*info)[i].acct_name = name;
247                 (*info)[i].full_name = gecos;
248                 (*info)[i].user_rid = rid;
249                 (*info)[i].group_rid = group;
250                 i++;
251         }
252
253         (*num_entries) = i;
254         status = NT_STATUS_OK;
255
256         DEBUG(3,("ads query_user_list gave %d entries\n", (*num_entries)));
257
258 done:
259         if (res) ads_msgfree(ads, res);
260
261         return status;
262 }
263
264 /* list all domain groups */
265 static NTSTATUS enum_dom_groups(struct winbindd_domain *domain,
266                                 TALLOC_CTX *mem_ctx,
267                                 uint32 *num_entries, 
268                                 struct acct_info **info)
269 {
270         ADS_STRUCT *ads = NULL;
271         const char *attrs[] = {"sAMAccountName", "name", "objectSid", 
272                                "sAMAccountType", NULL};
273         int rc, i, count;
274         void *res = NULL;
275         void *msg = NULL;
276         NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
277
278         *num_entries = 0;
279
280         DEBUG(3,("ads: enum_dom_groups\n"));
281
282         ads = ads_cached_connection(domain);
283         if (!ads) goto done;
284
285         rc = ads_search_retry(ads, &res, "(objectCategory=group)", attrs);
286         if (rc) {
287                 DEBUG(1,("query_user_list ads_search: %s\n", ads_errstr(rc)));
288                 goto done;
289         }
290
291         count = ads_count_replies(ads, res);
292         if (count == 0) {
293                 DEBUG(1,("query_user_list: No users found\n"));
294                 goto done;
295         }
296
297         (*info) = talloc(mem_ctx, count * sizeof(**info));
298         if (!*info) {
299                 status = NT_STATUS_NO_MEMORY;
300                 goto done;
301         }
302
303         i = 0;
304
305         for (msg = ads_first_entry(ads, res); msg; msg = ads_next_entry(ads, msg)) {
306                 char *name, *gecos;
307                 DOM_SID sid;
308                 uint32 rid;
309                 uint32 account_type;
310
311                 if (!ads_pull_uint32(ads, msg, "sAMAccountType", 
312                                      &account_type) ||
313                     !(account_type & ATYPE_GROUP)) continue;
314
315                 name = ads_pull_string(ads, mem_ctx, msg, "sAMAccountName");
316                 gecos = ads_pull_string(ads, mem_ctx, msg, "name");
317                 if (!ads_pull_sid(ads, msg, "objectSid", &sid)) {
318                         DEBUG(1,("No sid for %s !?\n", name));
319                         continue;
320                 }
321
322                 if (!sid_peek_rid(&sid, &rid)) {
323                         DEBUG(1,("No rid for %s !?\n", name));
324                         continue;
325                 }
326
327                 fstrcpy((*info)[i].acct_name, name);
328                 fstrcpy((*info)[i].acct_desc, gecos);
329                 (*info)[i].rid = rid;
330                 i++;
331         }
332
333         (*num_entries) = i;
334
335         status = NT_STATUS_OK;
336
337         DEBUG(3,("ads enum_dom_groups gave %d entries\n", (*num_entries)));
338
339 done:
340         if (res) ads_msgfree(ads, res);
341
342         return status;
343 }
344
345
346 /* convert a single name to a sid in a domain */
347 static NTSTATUS name_to_sid(struct winbindd_domain *domain,
348                             const char *name,
349                             DOM_SID *sid,
350                             enum SID_NAME_USE *type)
351 {
352         ADS_STRUCT *ads = NULL;
353         const char *attrs[] = {"objectSid", "sAMAccountType", NULL};
354         int rc, count;
355         void *res = NULL;
356         char *exp;
357         uint32 t;
358         fstring name2, dom2, fullname2;
359         NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
360
361         /* sigh. Need to fix interface to give us a raw name */
362         fstrcpy(fullname2, name);
363         fstring_sub(fullname2, "\\", lp_winbind_separator());
364         if (!parse_domain_user(fullname2, dom2, name2)) {
365                 goto done;
366         }
367
368         DEBUG(3,("ads: name_to_sid\n"));
369
370         ads = ads_cached_connection(domain);
371         if (!ads) goto done;
372
373         asprintf(&exp, "(sAMAccountName=%s)", name2);
374         rc = ads_search_retry(ads, &res, exp, attrs);
375         free(exp);
376         if (rc) {
377                 DEBUG(1,("name_to_sid ads_search: %s\n", ads_errstr(rc)));
378                 goto done;
379         }
380
381         count = ads_count_replies(ads, res);
382         if (count != 1) {
383                 DEBUG(1,("name_to_sid: %s not found\n", name));
384                 goto done;
385         }
386
387         if (!ads_pull_sid(ads, res, "objectSid", sid)) {
388                 DEBUG(1,("No sid for %s !?\n", name));
389                 goto done;
390         }
391
392         if (!ads_pull_uint32(ads, res, "sAMAccountType", &t)) {
393                 DEBUG(1,("No sAMAccountType for %s !?\n", name));
394                 goto done;
395         }
396
397         *type = ads_atype_map(t);
398
399         status = NT_STATUS_OK;
400
401         DEBUG(3,("ads name_to_sid mapped %s\n", name));
402
403 done:
404         if (res) ads_msgfree(ads, res);
405
406         return status;
407 }
408
409 /* convert a sid to a user or group name */
410 static NTSTATUS sid_to_name(struct winbindd_domain *domain,
411                             TALLOC_CTX *mem_ctx,
412                             DOM_SID *sid,
413                             char **name,
414                             enum SID_NAME_USE *type)
415 {
416         ADS_STRUCT *ads = NULL;
417         const char *attrs[] = {"sAMAccountName", "sAMAccountType", NULL};
418         int rc;
419         void *msg = NULL;
420         char *exp;
421         char *sidstr;
422         uint32 atype;
423         char *s;
424         NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
425
426         DEBUG(3,("ads: sid_to_name\n"));
427
428         ads = ads_cached_connection(domain);
429         if (!ads) goto done;
430
431         sidstr = sid_binstring(sid);
432         asprintf(&exp, "(objectSid=%s)", sidstr);
433         rc = ads_search_retry(ads, &msg, exp, attrs);
434         free(exp);
435         free(sidstr);
436         if (rc) {
437                 DEBUG(1,("sid_to_name ads_search: %s\n", ads_errstr(rc)));
438                 goto done;
439         }
440
441         if (!ads_pull_uint32(ads, msg, "sAMAccountType", &atype)) {
442                 goto done;
443         }
444
445         s = ads_pull_string(ads, mem_ctx, msg, "sAMAccountName");
446         *name = talloc_asprintf(mem_ctx, "%s%s%s", domain->name, lp_winbind_separator(), s);
447         *type = ads_atype_map(atype);
448
449         status = NT_STATUS_OK;
450
451         DEBUG(3,("ads sid_to_name mapped %s\n", *name));
452
453 done:
454         if (msg) ads_msgfree(ads, msg);
455
456         return status;
457 }
458
459
460 /* Lookup user information from a rid */
461 static NTSTATUS query_user(struct winbindd_domain *domain, 
462                            TALLOC_CTX *mem_ctx, 
463                            uint32 user_rid, 
464                            WINBIND_USERINFO *info)
465 {
466         ADS_STRUCT *ads = NULL;
467         const char *attrs[] = {"sAMAccountName", "name", "objectSid", 
468                                "primaryGroupID", NULL};
469         int rc, count;
470         void *msg = NULL;
471         char *exp;
472         DOM_SID sid;
473         char *sidstr;
474         NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
475
476         DEBUG(3,("ads: query_user\n"));
477
478         sid_from_rid(domain, user_rid, &sid);
479
480         ads = ads_cached_connection(domain);
481         if (!ads) goto done;
482
483         sidstr = sid_binstring(&sid);
484         asprintf(&exp, "(objectSid=%s)", sidstr);
485         rc = ads_search_retry(ads, &msg, exp, attrs);
486         free(exp);
487         free(sidstr);
488         if (rc) {
489                 DEBUG(1,("query_user(rid=%d) ads_search: %s\n", user_rid, ads_errstr(rc)));
490                 goto done;
491         }
492
493         count = ads_count_replies(ads, msg);
494         if (count != 1) {
495                 DEBUG(1,("query_user(rid=%d): Not found\n", user_rid));
496                 goto done;
497         }
498
499         info->acct_name = ads_pull_string(ads, mem_ctx, msg, "sAMAccountName");
500         info->full_name = ads_pull_string(ads, mem_ctx, msg, "name");
501         if (!ads_pull_sid(ads, msg, "objectSid", &sid)) {
502                 DEBUG(1,("No sid for %d !?\n", user_rid));
503                 goto done;
504         }
505         if (!ads_pull_uint32(ads, msg, "primaryGroupID", &info->group_rid)) {
506                 DEBUG(1,("No primary group for %d !?\n", user_rid));
507                 goto done;
508         }
509         
510         if (!sid_peek_rid(&sid, &info->user_rid)) {
511                 DEBUG(1,("No rid for %d !?\n", user_rid));
512                 goto done;
513         }
514
515         status = NT_STATUS_OK;
516
517         DEBUG(3,("ads query_user gave %s\n", info->acct_name));
518 done:
519         if (msg) ads_msgfree(ads, msg);
520
521         return status;
522 }
523
524
525 /* Lookup groups a user is a member of. */
526 static NTSTATUS lookup_usergroups(struct winbindd_domain *domain,
527                                   TALLOC_CTX *mem_ctx,
528                                   uint32 user_rid, 
529                                   uint32 *num_groups, uint32 **user_gids)
530 {
531         ADS_STRUCT *ads = NULL;
532         const char *attrs[] = {"distinguishedName", NULL};
533         const char *attrs2[] = {"tokenGroups", "primaryGroupID", NULL};
534         int rc, count;
535         void *msg = NULL;
536         char *exp;
537         char *user_dn;
538         DOM_SID *sids;
539         int i;
540         uint32 primary_group;
541         DOM_SID sid;
542         char *sidstr;
543         NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
544
545         *num_groups = 0;
546
547         DEBUG(3,("ads: lookup_usergroups\n"));
548
549         (*num_groups) = 0;
550
551         sid_from_rid(domain, user_rid, &sid);
552
553         ads = ads_cached_connection(domain);
554         if (!ads) goto done;
555
556         sidstr = sid_binstring(&sid);
557         asprintf(&exp, "(objectSid=%s)", sidstr);
558         rc = ads_search_retry(ads, &msg, exp, attrs);
559         free(exp);
560         free(sidstr);
561         if (rc) {
562                 DEBUG(1,("lookup_usergroups(rid=%d) ads_search: %s\n", user_rid, ads_errstr(rc)));
563                 goto done;
564         }
565
566         user_dn = ads_pull_string(ads, mem_ctx, msg, "distinguishedName");
567
568         if (msg) ads_msgfree(ads, msg);
569
570         rc = ads_search_retry_dn(ads, &msg, user_dn, attrs2);
571         if (rc) {
572                 DEBUG(1,("lookup_usergroups(rid=%d) ads_search tokenGroups: %s\n", user_rid, ads_errstr(rc)));
573                 goto done;
574         }
575
576         if (!ads_pull_uint32(ads, msg, "primaryGroupID", &primary_group)) {
577                 DEBUG(1,("%s: No primary group for rid=%d !?\n", domain->name, user_rid));
578                 goto done;
579         }
580
581         count = ads_pull_sids(ads, mem_ctx, msg, "tokenGroups", &sids) + 1;
582         (*user_gids) = (uint32 *)talloc(mem_ctx, sizeof(uint32) * count);
583         (*user_gids)[(*num_groups)++] = primary_group;
584
585         for (i=1;i<count;i++) {
586                 uint32 rid;
587                 if (!sid_peek_rid(&sids[i-1], &rid)) continue;
588                 (*user_gids)[*num_groups] = rid;
589                 (*num_groups)++;
590         }
591
592         status = NT_STATUS_OK;
593         DEBUG(3,("ads lookup_usergroups for rid=%d\n", user_rid));
594 done:
595         if (msg) ads_msgfree(ads, msg);
596
597         return status;
598 }
599
600
601 static NTSTATUS lookup_groupmem(struct winbindd_domain *domain,
602                                 TALLOC_CTX *mem_ctx,
603                                 uint32 group_rid, uint32 *num_names, 
604                                 uint32 **rid_mem, char ***names, 
605                                 uint32 **name_types)
606 {
607         DOM_SID group_sid;
608         char *sidstr;
609         const char *attrs[] = {"sAMAccountName", "objectSid", "sAMAccountType", NULL};
610         int rc, count;
611         void *res=NULL, *msg=NULL;
612         ADS_STRUCT *ads = NULL;
613         char *exp;
614         NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
615
616         *num_names = 0;
617
618         ads = ads_cached_connection(domain);
619         if (!ads) goto done;
620
621         sid_from_rid(domain, group_rid, &group_sid);
622         sidstr = sid_binstring(&group_sid);
623         /* search for all users who have that group sid as primary group or as member */
624         asprintf(&exp, "(&(objectCategory=user)(|(primaryGroupID=%d)(memberOf=%s)))",
625                  group_rid, sidstr);
626         rc = ads_search_retry(ads, &res, exp, attrs);
627         free(exp);
628         free(sidstr);
629         if (rc) {
630                 DEBUG(1,("query_user_list ads_search: %s\n", ads_errstr(rc)));
631                 goto done;
632         }
633
634         count = ads_count_replies(ads, res);
635         if (count == 0) {
636                 status = NT_STATUS_OK;
637                 goto done;
638         }
639
640         (*rid_mem) = talloc(mem_ctx, sizeof(uint32) * count);
641         (*name_types) = talloc(mem_ctx, sizeof(uint32) * count);
642         (*names) = talloc(mem_ctx, sizeof(char *) * count);
643
644         for (msg = ads_first_entry(ads, res); msg; msg = ads_next_entry(ads, msg)) {
645                 uint32 atype, rid;
646                 DOM_SID sid;
647
648                 (*names)[*num_names] = ads_pull_string(ads, mem_ctx, msg, "sAMAccountName");
649                 if (!ads_pull_uint32(ads, msg, "sAMAccountType", &atype)) {
650                         continue;
651                 }
652                 (*name_types)[*num_names] = ads_atype_map(atype);
653                 if (!ads_pull_sid(ads, msg, "objectSid", &sid)) {
654                         DEBUG(1,("No sid for %s !?\n", (*names)[*num_names]));
655                         continue;
656                 }
657                 if (!sid_peek_rid(&sid, &rid)) {
658                         DEBUG(1,("No rid for %s !?\n", (*names)[*num_names]));
659                         continue;
660                 }
661                 (*rid_mem)[*num_names] = rid;
662                 (*num_names)++;
663         }       
664
665         status = NT_STATUS_OK;
666         DEBUG(3,("ads lookup_groupmem for rid=%d\n", group_rid));
667 done:
668         if (res) ads_msgfree(ads, res);
669
670         return status;
671 }
672
673 /* find the sequence number for a domain */
674 static NTSTATUS sequence_number(struct winbindd_domain *domain, uint32 *seq)
675 {
676         ADS_STRUCT *ads = NULL;
677
678         *seq = DOM_SEQUENCE_NONE;
679
680         ads = ads_cached_connection(domain);
681         if (!ads) return NT_STATUS_UNSUCCESSFUL;
682
683         if (!ads_USN(ads, seq)) {
684                 return NT_STATUS_UNSUCCESSFUL;
685         }
686
687         return NT_STATUS_OK;
688 }
689
690 /* get a list of trusted domains */
691 static NTSTATUS trusted_domains(struct winbindd_domain *domain,
692                                 TALLOC_CTX *mem_ctx,
693                                 uint32 *num_domains,
694                                 char ***names,
695                                 DOM_SID **dom_sids)
696 {
697         ADS_STRUCT *ads = NULL;
698
699         *num_domains = 0;
700         *names = NULL;
701
702         ads = ads_cached_connection(domain);
703         if (!ads) return NT_STATUS_UNSUCCESSFUL;
704
705         if (!ads_trusted_domains(ads, mem_ctx, num_domains, names, dom_sids)) {
706                 return NT_STATUS_UNSUCCESSFUL;
707         }
708
709         return NT_STATUS_OK;
710 }
711
712 /* find the domain sid for a domain */
713 static NTSTATUS domain_sid(struct winbindd_domain *domain, DOM_SID *sid)
714 {
715         NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
716         const char *attrs[] = {"objectSid", NULL};
717         ADS_STRUCT *ads = NULL;
718         void *res;
719         int rc;
720
721         ads = ads_cached_connection(domain);
722         if (!ads) goto done;
723
724         rc = ads_do_search(ads, ads->bind_path, LDAP_SCOPE_BASE, "(objectclass=*)", 
725                            attrs, &res);
726         if (rc) goto done;
727         if (ads_pull_sid(ads, res, "objectSid", sid)) {
728                 status = NT_STATUS_OK;
729         }
730         ads_msgfree(ads, res);
731
732 done:
733         return status;
734 }
735
736 /* the ADS backend methods are exposed via this structure */
737 struct winbindd_methods ads_methods = {
738         True,
739         query_user_list,
740         enum_dom_groups,
741         name_to_sid,
742         sid_to_name,
743         query_user,
744         lookup_usergroups,
745         lookup_groupmem,
746         sequence_number,
747         trusted_domains,
748         domain_sid
749 };
750
751 #endif