Merge from Subversion r50.
[ira/wip.git] / source3 / nsswitch / winbindd_ads.c
1 /* 
2    Unix SMB/CIFS implementation.
3
4    Winbind ADS backend functions
5
6    Copyright (C) Andrew Tridgell 2001
7    Copyright (C) Andrew Bartlett <abartlet@samba.org> 2003
8    
9    This program is free software; you can redistribute it and/or modify
10    it under the terms of the GNU General Public License as published by
11    the Free Software Foundation; either version 2 of the License, or
12    (at your option) any later version.
13    
14    This program is distributed in the hope that it will be useful,
15    but WITHOUT ANY WARRANTY; without even the implied warranty of
16    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17    GNU General Public License for more details.
18    
19    You should have received a copy of the GNU General Public License
20    along with this program; if not, write to the Free Software
21    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
22 */
23
24 #include "winbindd.h"
25
26 #ifdef HAVE_ADS
27
28 #undef DBGC_CLASS
29 #define DBGC_CLASS DBGC_WINBIND
30
31 /* the realm of our primary LDAP server */
32 static char *primary_realm;
33
34
35 /*
36   return our ads connections structure for a domain. We keep the connection
37   open to make things faster
38 */
39 static ADS_STRUCT *ads_cached_connection(struct winbindd_domain *domain)
40 {
41         ADS_STRUCT *ads;
42         ADS_STATUS status;
43
44         if (domain->private) {
45                 return (ADS_STRUCT *)domain->private;
46         }
47
48         /* we don't want this to affect the users ccache */
49         setenv("KRB5CCNAME", "MEMORY:winbind_ccache", 1);
50
51         ads = ads_init(domain->alt_name, domain->name, NULL);
52         if (!ads) {
53                 DEBUG(1,("ads_init for domain %s failed\n", domain->name));
54                 return NULL;
55         }
56
57         /* the machine acct password might have change - fetch it every time */
58         SAFE_FREE(ads->auth.password);
59         ads->auth.password = secrets_fetch_machine_password();
60
61         if (primary_realm) {
62                 SAFE_FREE(ads->auth.realm);
63                 ads->auth.realm = strdup(primary_realm);
64         }
65
66         status = ads_connect(ads);
67         if (!ADS_ERR_OK(status) || !ads->config.realm) {
68                 extern struct winbindd_methods msrpc_methods;
69                 DEBUG(1,("ads_connect for domain %s failed: %s\n", 
70                          domain->name, ads_errstr(status)));
71                 ads_destroy(&ads);
72
73                 /* if we get ECONNREFUSED then it might be a NT4
74                    server, fall back to MSRPC */
75                 if (status.error_type == ADS_ERROR_SYSTEM &&
76                     status.err.rc == ECONNREFUSED) {
77                         DEBUG(1,("Trying MSRPC methods\n"));
78                         domain->methods = &msrpc_methods;
79                 }
80                 return NULL;
81         }
82
83         /* remember our primary realm for trusted domain support */
84         if (!primary_realm) {
85                 primary_realm = strdup(ads->config.realm);
86         }
87
88         domain->private = (void *)ads;
89         return ads;
90 }
91
92
93 /* Query display info for a realm. This is the basic user list fn */
94 static NTSTATUS query_user_list(struct winbindd_domain *domain,
95                                TALLOC_CTX *mem_ctx,
96                                uint32 *num_entries, 
97                                WINBIND_USERINFO **info)
98 {
99         ADS_STRUCT *ads = NULL;
100         const char *attrs[] = {"userPrincipalName",
101                                "sAMAccountName",
102                                "name", "objectSid", "primaryGroupID", 
103                                "sAMAccountType", NULL};
104         int i, count;
105         ADS_STATUS rc;
106         void *res = NULL;
107         void *msg = NULL;
108         NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
109
110         *num_entries = 0;
111
112         DEBUG(3,("ads: query_user_list\n"));
113
114         ads = ads_cached_connection(domain);
115         if (!ads) goto done;
116
117         rc = ads_search_retry(ads, &res, "(objectCategory=user)", attrs);
118         if (!ADS_ERR_OK(rc)) {
119                 DEBUG(1,("query_user_list ads_search: %s\n", ads_errstr(rc)));
120                 goto done;
121         }
122
123         count = ads_count_replies(ads, res);
124         if (count == 0) {
125                 DEBUG(1,("query_user_list: No users found\n"));
126                 goto done;
127         }
128
129         (*info) = talloc_zero(mem_ctx, count * sizeof(**info));
130         if (!*info) {
131                 status = NT_STATUS_NO_MEMORY;
132                 goto done;
133         }
134
135         i = 0;
136
137         for (msg = ads_first_entry(ads, res); msg; msg = ads_next_entry(ads, msg)) {
138                 char *name, *gecos;
139                 DOM_SID sid;
140                 DOM_SID *sid2;
141                 DOM_SID *group_sid;
142                 uint32 group;
143                 uint32 atype;
144
145                 if (!ads_pull_uint32(ads, msg, "sAMAccountType", &atype) ||
146                     ads_atype_map(atype) != SID_NAME_USER) {
147                         DEBUG(1,("Not a user account? atype=0x%x\n", atype));
148                         continue;
149                 }
150
151                 name = ads_pull_username(ads, mem_ctx, msg);
152                 gecos = ads_pull_string(ads, mem_ctx, msg, "name");
153                 if (!ads_pull_sid(ads, msg, "objectSid", &sid)) {
154                         DEBUG(1,("No sid for %s !?\n", name));
155                         continue;
156                 }
157                 if (!ads_pull_uint32(ads, msg, "primaryGroupID", &group)) {
158                         DEBUG(1,("No primary group for %s !?\n", name));
159                         continue;
160                 }
161
162                 sid2 = talloc(mem_ctx, sizeof(*sid2));
163                 if (!sid2) {
164                         status = NT_STATUS_NO_MEMORY;
165                         goto done;
166                 }
167
168                 sid_copy(sid2, &sid);
169
170                 group_sid = rid_to_talloced_sid(domain, mem_ctx, group);
171
172                 (*info)[i].acct_name = name;
173                 (*info)[i].full_name = gecos;
174                 (*info)[i].user_sid = sid2;
175                 (*info)[i].group_sid = group_sid;
176                 i++;
177         }
178
179         (*num_entries) = i;
180         status = NT_STATUS_OK;
181
182         DEBUG(3,("ads query_user_list gave %d entries\n", (*num_entries)));
183
184 done:
185         if (res) ads_msgfree(ads, res);
186
187         return status;
188 }
189
190 /* list all domain groups */
191 static NTSTATUS enum_dom_groups(struct winbindd_domain *domain,
192                                 TALLOC_CTX *mem_ctx,
193                                 uint32 *num_entries, 
194                                 struct acct_info **info)
195 {
196         ADS_STRUCT *ads = NULL;
197         const char *attrs[] = {"userPrincipalName", "sAMAccountName",
198                                "name", "objectSid", 
199                                "sAMAccountType", NULL};
200         int i, count;
201         ADS_STATUS rc;
202         void *res = NULL;
203         void *msg = NULL;
204         NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
205         uint32 group_flags;
206
207         *num_entries = 0;
208
209         DEBUG(3,("ads: enum_dom_groups\n"));
210
211         ads = ads_cached_connection(domain);
212         if (!ads) goto done;
213
214         rc = ads_search_retry(ads, &res, "(objectCategory=group)", attrs);
215         if (!ADS_ERR_OK(rc)) {
216                 DEBUG(1,("enum_dom_groups ads_search: %s\n", ads_errstr(rc)));
217                 goto done;
218         }
219
220         count = ads_count_replies(ads, res);
221         if (count == 0) {
222                 DEBUG(1,("enum_dom_groups: No groups found\n"));
223                 goto done;
224         }
225
226         (*info) = talloc_zero(mem_ctx, count * sizeof(**info));
227         if (!*info) {
228                 status = NT_STATUS_NO_MEMORY;
229                 goto done;
230         }
231
232         i = 0;
233         
234         group_flags = ATYPE_GLOBAL_GROUP;
235         if ( domain->native_mode )
236                 group_flags |= ATYPE_LOCAL_GROUP;
237
238         for (msg = ads_first_entry(ads, res); msg; msg = ads_next_entry(ads, msg)) {
239                 char *name, *gecos;
240                 DOM_SID sid;
241                 uint32 rid;
242                 uint32 account_type;
243
244                 if (!ads_pull_uint32(ads, msg, "sAMAccountType", &account_type) || !(account_type & group_flags) ) 
245                         continue; 
246                         
247                 name = ads_pull_username(ads, mem_ctx, msg);
248                 gecos = ads_pull_string(ads, mem_ctx, msg, "name");
249                 if (!ads_pull_sid(ads, msg, "objectSid", &sid)) {
250                         DEBUG(1,("No sid for %s !?\n", name));
251                         continue;
252                 }
253
254                 if (!sid_peek_check_rid(&domain->sid, &sid, &rid)) {
255                         DEBUG(1,("No rid for %s !?\n", name));
256                         continue;
257                 }
258
259                 fstrcpy((*info)[i].acct_name, name);
260                 fstrcpy((*info)[i].acct_desc, gecos);
261                 (*info)[i].rid = rid;
262                 i++;
263         }
264
265         (*num_entries) = i;
266
267         status = NT_STATUS_OK;
268
269         DEBUG(3,("ads enum_dom_groups gave %d entries\n", (*num_entries)));
270
271 done:
272         if (res) ads_msgfree(ads, res);
273
274         return status;
275 }
276
277 /* list all domain local groups */
278 static NTSTATUS enum_local_groups(struct winbindd_domain *domain,
279                                 TALLOC_CTX *mem_ctx,
280                                 uint32 *num_entries, 
281                                 struct acct_info **info)
282 {
283         /*
284          * This is a stub function only as we returned the domain 
285          * ocal groups in enum_dom_groups() if the domain->native field
286          * was true.  This is a simple performance optimization when
287          * using LDAP.
288          *
289          * if we ever need to enumerate domain local groups separately, 
290          * then this the optimization in enum_dom_groups() will need 
291          * to be split out
292          */
293         *num_entries = 0;
294         
295         return NT_STATUS_OK;
296 }
297
298 /* convert a single name to a sid in a domain */
299 static NTSTATUS name_to_sid(struct winbindd_domain *domain,
300                             TALLOC_CTX *mem_ctx,
301                             const char *name,
302                             DOM_SID *sid,
303                             enum SID_NAME_USE *type)
304 {
305         ADS_STRUCT *ads;
306
307         DEBUG(3,("ads: name_to_sid\n"));
308
309         ads = ads_cached_connection(domain);
310         if (!ads) 
311                 return NT_STATUS_UNSUCCESSFUL;
312
313         return ads_name_to_sid(ads, name, sid, type);
314 }
315
316 /* convert a sid to a user or group name */
317 static NTSTATUS sid_to_name(struct winbindd_domain *domain,
318                             TALLOC_CTX *mem_ctx,
319                             DOM_SID *sid,
320                             char **name,
321                             enum SID_NAME_USE *type)
322 {
323         ADS_STRUCT *ads = NULL;
324         DEBUG(3,("ads: sid_to_name\n"));
325         ads = ads_cached_connection(domain);
326         if (!ads) 
327                 return NT_STATUS_UNSUCCESSFUL;
328
329         return ads_sid_to_name(ads, mem_ctx, sid, name, type);
330 }
331
332
333 /* convert a DN to a name, SID and name type 
334    this might become a major speed bottleneck if groups have
335    lots of users, in which case we could cache the results
336 */
337 static BOOL dn_lookup(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
338                       const char *dn,
339                       char **name, uint32 *name_type, DOM_SID *sid)
340 {
341         char *exp;
342         void *res = NULL;
343         const char *attrs[] = {"userPrincipalName", "sAMAccountName",
344                                "objectSid", "sAMAccountType", NULL};
345         ADS_STATUS rc;
346         uint32 atype;
347         char *escaped_dn = escape_ldap_string_alloc(dn);
348
349         if (!escaped_dn) {
350                 return False;
351         }
352
353         asprintf(&exp, "(distinguishedName=%s)", dn);
354         rc = ads_search_retry(ads, &res, exp, attrs);
355         SAFE_FREE(exp);
356         SAFE_FREE(escaped_dn);
357
358         if (!ADS_ERR_OK(rc)) {
359                 goto failed;
360         }
361
362         (*name) = ads_pull_username(ads, mem_ctx, res);
363
364         if (!ads_pull_uint32(ads, res, "sAMAccountType", &atype)) {
365                 goto failed;
366         }
367         (*name_type) = ads_atype_map(atype);
368
369         if (!ads_pull_sid(ads, res, "objectSid", sid)) {
370                 goto failed;
371         }
372
373         if (res) ads_msgfree(ads, res);
374         return True;
375
376 failed:
377         if (res) ads_msgfree(ads, res);
378         return False;
379 }
380
381 /* Lookup user information from a rid */
382 static NTSTATUS query_user(struct winbindd_domain *domain, 
383                            TALLOC_CTX *mem_ctx, 
384                            DOM_SID *sid, 
385                            WINBIND_USERINFO *info)
386 {
387         ADS_STRUCT *ads = NULL;
388         const char *attrs[] = {"userPrincipalName", 
389                                "sAMAccountName",
390                                "name", 
391                                "primaryGroupID", NULL};
392         ADS_STATUS rc;
393         int count;
394         void *msg = NULL;
395         char *exp;
396         char *sidstr;
397         uint32 group_rid;
398         NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
399         DOM_SID *sid2;
400         fstring sid_string;
401
402         DEBUG(3,("ads: query_user\n"));
403
404         ads = ads_cached_connection(domain);
405         if (!ads) goto done;
406
407         sidstr = sid_binstring(sid);
408         asprintf(&exp, "(objectSid=%s)", sidstr);
409         rc = ads_search_retry(ads, &msg, exp, attrs);
410         free(exp);
411         free(sidstr);
412         if (!ADS_ERR_OK(rc)) {
413                 DEBUG(1,("query_user(sid=%s) ads_search: %s\n", sid_to_string(sid_string, sid), ads_errstr(rc)));
414                 goto done;
415         }
416
417         count = ads_count_replies(ads, msg);
418         if (count != 1) {
419                 DEBUG(1,("query_user(sid=%s): Not found\n", sid_to_string(sid_string, sid)));
420                 goto done;
421         }
422
423         info->acct_name = ads_pull_username(ads, mem_ctx, msg);
424         info->full_name = ads_pull_string(ads, mem_ctx, msg, "name");
425
426         if (!ads_pull_uint32(ads, msg, "primaryGroupID", &group_rid)) {
427                 DEBUG(1,("No primary group for %s !?\n", sid_to_string(sid_string, sid)));
428                 goto done;
429         }
430         
431         sid2 = talloc(mem_ctx, sizeof(*sid2));
432         if (!sid2) {
433                 status = NT_STATUS_NO_MEMORY;
434                 goto done;
435         }
436         sid_copy(sid2, sid);
437         
438         info->user_sid = sid2;
439
440         info->group_sid = rid_to_talloced_sid(domain, mem_ctx, group_rid);
441
442         status = NT_STATUS_OK;
443
444         DEBUG(3,("ads query_user gave %s\n", info->acct_name));
445 done:
446         if (msg) ads_msgfree(ads, msg);
447
448         return status;
449 }
450
451 /* Lookup groups a user is a member of - alternate method, for when
452    tokenGroups are not available. */
453 static NTSTATUS lookup_usergroups_alt(struct winbindd_domain *domain,
454                                       TALLOC_CTX *mem_ctx,
455                                       const char *user_dn, 
456                                       DOM_SID *primary_group,
457                                       uint32 *num_groups, DOM_SID ***user_gids)
458 {
459         ADS_STATUS rc;
460         NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
461         int count;
462         void *res = NULL;
463         void *msg = NULL;
464         char *exp;
465         ADS_STRUCT *ads;
466         const char *group_attrs[] = {"objectSid", NULL};
467
468         ads = ads_cached_connection(domain);
469         if (!ads) goto done;
470
471         /* buggy server, no tokenGroups.  Instead lookup what groups this user
472            is a member of by DN search on member*/
473         if (asprintf(&exp, "(&(member=%s)(objectClass=group))", user_dn) == -1) {
474                 DEBUG(1,("lookup_usergroups(dn=%s) asprintf failed!\n", user_dn));
475                 return NT_STATUS_NO_MEMORY;
476         }
477         
478         rc = ads_search_retry(ads, &res, exp, group_attrs);
479         free(exp);
480         
481         if (!ADS_ERR_OK(rc)) {
482                 DEBUG(1,("lookup_usergroups ads_search member=%s: %s\n", user_dn, ads_errstr(rc)));
483                 return ads_ntstatus(rc);
484         }
485         
486         count = ads_count_replies(ads, res);
487         if (count == 0) {
488                 DEBUG(5,("lookup_usergroups: No supp groups found\n"));
489                 
490                 status = ads_ntstatus(rc);
491                 goto done;
492         }
493         
494         (*user_gids) = talloc_zero(mem_ctx, sizeof(**user_gids) * (count + 1));
495         (*user_gids)[0] = primary_group;
496         
497         *num_groups = 1;
498         
499         for (msg = ads_first_entry(ads, res); msg; msg = ads_next_entry(ads, msg)) {
500                 DOM_SID group_sid;
501                 
502                 if (!ads_pull_sid(ads, msg, "objectSid", &group_sid)) {
503                         DEBUG(1,("No sid for this group ?!?\n"));
504                         continue;
505                 }
506                 
507                 if (sid_equal(&group_sid, primary_group)) continue;
508                 
509                 (*user_gids)[*num_groups] = talloc(mem_ctx, sizeof(***user_gids));
510                 if (!(*user_gids)[*num_groups]) {
511                         status = NT_STATUS_NO_MEMORY;
512                         goto done;
513                 }
514
515                 sid_copy((*user_gids)[*num_groups], &group_sid);
516
517                 (*num_groups)++;
518                         
519         }
520
521         status = NT_STATUS_OK;
522
523         DEBUG(3,("ads lookup_usergroups (alt) for dn=%s\n", user_dn));
524 done:
525         if (res) ads_msgfree(ads, res);
526         if (msg) ads_msgfree(ads, msg);
527
528         return status;
529 }
530
531 /* Lookup groups a user is a member of. */
532 static NTSTATUS lookup_usergroups(struct winbindd_domain *domain,
533                                   TALLOC_CTX *mem_ctx,
534                                   DOM_SID *sid, 
535                                   uint32 *num_groups, DOM_SID ***user_gids)
536 {
537         ADS_STRUCT *ads = NULL;
538         const char *attrs[] = {"distinguishedName", NULL};
539         const char *attrs2[] = {"tokenGroups", "primaryGroupID", NULL};
540         ADS_STATUS rc;
541         int count;
542         void *msg = NULL;
543         char *exp;
544         char *user_dn;
545         DOM_SID *sids;
546         int i;
547         DOM_SID *primary_group;
548         uint32 primary_group_rid;
549         char *sidstr;
550         fstring sid_string;
551         NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
552
553         DEBUG(3,("ads: lookup_usergroups\n"));
554         *num_groups = 0;
555
556         ads = ads_cached_connection(domain);
557         if (!ads) goto done;
558
559         if (!(sidstr = sid_binstring(sid))) {
560                 DEBUG(1,("lookup_usergroups(sid=%s) sid_binstring returned NULL\n", sid_to_string(sid_string, sid)));
561                 status = NT_STATUS_NO_MEMORY;
562                 goto done;
563         }
564         if (asprintf(&exp, "(objectSid=%s)", sidstr) == -1) {
565                 free(sidstr);
566                 DEBUG(1,("lookup_usergroups(sid=%s) asprintf failed!\n", sid_to_string(sid_string, sid)));
567                 status = NT_STATUS_NO_MEMORY;
568                 goto done;
569         }
570
571         rc = ads_search_retry(ads, &msg, exp, attrs);
572         free(exp);
573         free(sidstr);
574
575         if (!ADS_ERR_OK(rc)) {
576                 DEBUG(1,("lookup_usergroups(sid=%s) ads_search: %s\n", sid_to_string(sid_string, sid), ads_errstr(rc)));
577                 goto done;
578         }
579
580         user_dn = ads_pull_string(ads, mem_ctx, msg, "distinguishedName");
581         if (!user_dn) {
582                 DEBUG(1,("lookup_usergroups(sid=%s) ads_search did not return a a distinguishedName!\n", sid_to_string(sid_string, sid)));
583                 if (msg) ads_msgfree(ads, msg);
584                 goto done;
585         }
586
587         if (msg) ads_msgfree(ads, msg);
588
589         rc = ads_search_retry_dn(ads, &msg, user_dn, attrs2);
590         if (!ADS_ERR_OK(rc)) {
591                 DEBUG(1,("lookup_usergroups(sid=%s) ads_search tokenGroups: %s\n", sid_to_string(sid_string, sid), ads_errstr(rc)));
592                 goto done;
593         }
594
595         if (!ads_pull_uint32(ads, msg, "primaryGroupID", &primary_group_rid)) {
596                 DEBUG(1,("%s: No primary group for sid=%s !?\n", domain->name, sid_to_string(sid_string, sid)));
597                 goto done;
598         }
599
600         primary_group = rid_to_talloced_sid(domain, mem_ctx, primary_group_rid);
601
602         count = ads_pull_sids(ads, mem_ctx, msg, "tokenGroups", &sids);
603
604         if (msg) ads_msgfree(ads, msg);
605
606         /* there must always be at least one group in the token, 
607            unless we are talking to a buggy Win2k server */
608         if (count == 0) {
609                 return lookup_usergroups_alt(domain, mem_ctx, user_dn, 
610                                              primary_group,
611                                              num_groups, user_gids);
612         }
613
614         (*user_gids) = talloc_zero(mem_ctx, sizeof(**user_gids) * (count + 1));
615         (*user_gids)[0] = primary_group;
616         
617         *num_groups = 1;
618         
619         for (i=0;i<count;i++) {
620                 if (sid_equal(&sids[i], primary_group)) continue;
621                 
622                 (*user_gids)[*num_groups] = talloc(mem_ctx, sizeof(***user_gids));
623                 if (!(*user_gids)[*num_groups]) {
624                         status = NT_STATUS_NO_MEMORY;
625                         goto done;
626                 }
627
628                 sid_copy((*user_gids)[*num_groups], &sids[i]);
629                 (*num_groups)++;
630         }
631
632         status = NT_STATUS_OK;
633         DEBUG(3,("ads lookup_usergroups for sid=%s\n", sid_to_string(sid_string, sid)));
634 done:
635         return status;
636 }
637
638 /*
639   find the members of a group, given a group rid and domain
640  */
641 static NTSTATUS lookup_groupmem(struct winbindd_domain *domain,
642                                 TALLOC_CTX *mem_ctx,
643                                 DOM_SID *group_sid, uint32 *num_names, 
644                                 DOM_SID ***sid_mem, char ***names, 
645                                 uint32 **name_types)
646 {
647         ADS_STATUS rc;
648         int count;
649         void *res=NULL;
650         ADS_STRUCT *ads = NULL;
651         char *exp;
652         NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
653         char *sidstr;
654         const char *attrs[] = {"member", NULL};
655         char **members;
656         int i, num_members;
657         fstring sid_string;
658
659         *num_names = 0;
660
661         ads = ads_cached_connection(domain);
662         if (!ads) goto done;
663
664         sidstr = sid_binstring(group_sid);
665
666         /* search for all members of the group */
667         asprintf(&exp, "(objectSid=%s)",sidstr);
668         rc = ads_search_retry(ads, &res, exp, attrs);
669         free(exp);
670         free(sidstr);
671
672         if (!ADS_ERR_OK(rc)) {
673                 DEBUG(1,("query_user_list ads_search: %s\n", ads_errstr(rc)));
674                 goto done;
675         }
676
677         count = ads_count_replies(ads, res);
678         if (count == 0) {
679                 status = NT_STATUS_OK;
680                 goto done;
681         }
682
683         members = ads_pull_strings(ads, mem_ctx, res, "member");
684         if (!members) {
685                 /* no members? ok ... */
686                 status = NT_STATUS_OK;
687                 goto done;
688         }
689
690         /* now we need to turn a list of members into rids, names and name types 
691            the problem is that the members are in the form of distinguised names
692         */
693         for (i=0;members[i];i++) /* noop */ ;
694         num_members = i;
695
696         (*sid_mem) = talloc_zero(mem_ctx, sizeof(**sid_mem) * num_members);
697         (*name_types) = talloc_zero(mem_ctx, sizeof(**name_types) * num_members);
698         (*names) = talloc_zero(mem_ctx, sizeof(**names) * num_members);
699
700         for (i=0;i<num_members;i++) {
701                 uint32 name_type;
702                 char *name;
703                 DOM_SID sid;
704
705                 if (dn_lookup(ads, mem_ctx, members[i], &name, &name_type, &sid)) {
706                     (*names)[*num_names] = name;
707                     (*name_types)[*num_names] = name_type;
708                     (*sid_mem)[*num_names] = talloc(mem_ctx, sizeof(***sid_mem));
709                     if (!(*sid_mem)[*num_names]) {
710                             status = NT_STATUS_NO_MEMORY;
711                             goto done;
712                     }
713                     sid_copy((*sid_mem)[*num_names], &sid);
714                     (*num_names)++;
715                 }
716         }       
717
718         status = NT_STATUS_OK;
719         DEBUG(3,("ads lookup_groupmem for sid=%s\n", sid_to_string(sid_string, group_sid)));
720 done:
721         if (res) ads_msgfree(ads, res);
722
723         return status;
724 }
725
726
727 /* find the sequence number for a domain */
728 static NTSTATUS sequence_number(struct winbindd_domain *domain, uint32 *seq)
729 {
730         ADS_STRUCT *ads = NULL;
731         ADS_STATUS rc;
732
733         *seq = DOM_SEQUENCE_NONE;
734
735         ads = ads_cached_connection(domain);
736         if (!ads) return NT_STATUS_UNSUCCESSFUL;
737
738         rc = ads_USN(ads, seq);
739         if (!ADS_ERR_OK(rc)) {
740                 /* its a dead connection */
741                 ads_destroy(&ads);
742                 domain->private = NULL;
743         }
744         return ads_ntstatus(rc);
745 }
746
747 /* get a list of trusted domains */
748 static NTSTATUS trusted_domains(struct winbindd_domain *domain,
749                                 TALLOC_CTX *mem_ctx,
750                                 uint32 *num_domains,
751                                 char ***names,
752                                 char ***alt_names,
753                                 DOM_SID **dom_sids)
754 {
755         ADS_STRUCT *ads;
756         ADS_STATUS rc;
757
758         *num_domains = 0;
759         *names = NULL;
760
761         ads = ads_cached_connection(domain);
762         if (!ads) return NT_STATUS_UNSUCCESSFUL;
763
764         rc = ads_trusted_domains(ads, mem_ctx, num_domains, names, alt_names, dom_sids);
765
766         return ads_ntstatus(rc);
767 }
768
769 /* find the domain sid for a domain */
770 static NTSTATUS domain_sid(struct winbindd_domain *domain, DOM_SID *sid)
771 {
772         ADS_STRUCT *ads;
773         ADS_STATUS rc;
774
775         ads = ads_cached_connection(domain);
776         if (!ads) return NT_STATUS_UNSUCCESSFUL;
777
778         rc = ads_domain_sid(ads, sid);
779
780         if (!ADS_ERR_OK(rc)) {
781                 /* its a dead connection */
782                 ads_destroy(&ads);
783                 domain->private = NULL;
784         }
785
786         return ads_ntstatus(rc);
787 }
788
789
790 /* find alternate names list for the domain - for ADS this is the
791    netbios name */
792 static NTSTATUS alternate_name(struct winbindd_domain *domain)
793 {
794         ADS_STRUCT *ads;
795         ADS_STATUS rc;
796         TALLOC_CTX *ctx;
797         char *workgroup;
798
799         ads = ads_cached_connection(domain);
800         if (!ads) return NT_STATUS_UNSUCCESSFUL;
801
802         if (!(ctx = talloc_init("alternate_name"))) {
803                 return NT_STATUS_NO_MEMORY;
804         }
805
806         rc = ads_workgroup_name(ads, ctx, &workgroup);
807
808         if (ADS_ERR_OK(rc)) {
809                 fstrcpy(domain->name, workgroup);
810                 fstrcpy(domain->alt_name, ads->config.realm);
811                 strupper(domain->alt_name);
812                 strupper(domain->name);
813         }
814
815         talloc_destroy(ctx);
816
817         return ads_ntstatus(rc);        
818 }
819
820 /* the ADS backend methods are exposed via this structure */
821 struct winbindd_methods ads_methods = {
822         True,
823         query_user_list,
824         enum_dom_groups,
825         enum_local_groups,
826         name_to_sid,
827         sid_to_name,
828         query_user,
829         lookup_usergroups,
830         lookup_groupmem,
831         sequence_number,
832         trusted_domains,
833         domain_sid,
834         alternate_name
835 };
836
837 #endif