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