sync'ing up for 3.0alpha20 release
[kai/samba.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    
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 #undef DBGC_CLASS
28 #define DBGC_CLASS DBGC_WINBIND
29
30 /* the realm of our primary LDAP server */
31 static char *primary_realm;
32
33
34 /*
35   a wrapper around ldap_search_s that retries depending on the error code
36   this is supposed to catch dropped connections and auto-reconnect
37 */
38 ADS_STATUS ads_do_search_retry(ADS_STRUCT *ads, const char *bind_path, int scope, 
39                                const char *exp,
40                                const char **attrs, void **res)
41 {
42         ADS_STATUS status;
43         int count = 3;
44         char *bp;
45
46         if (!ads->ld &&
47             time(NULL) - ads->last_attempt < ADS_RECONNECT_TIME) {
48                 return ADS_ERROR(LDAP_SERVER_DOWN);
49         }
50
51         bp = strdup(bind_path);
52
53         while (count--) {
54                 status = ads_do_search_all(ads, bp, scope, exp, attrs, res);
55                 if (ADS_ERR_OK(status)) {
56                         DEBUG(5,("Search for %s gave %d replies\n",
57                                  exp, ads_count_replies(ads, *res)));
58                         free(bp);
59                         return status;
60                 }
61
62                 if (*res) ads_msgfree(ads, *res);
63                 *res = NULL;
64                 DEBUG(3,("Reopening ads connection to realm '%s' after error %s\n", 
65                          ads->config.realm, ads_errstr(status)));
66                 if (ads->ld) {
67                         ldap_unbind(ads->ld); 
68                 }
69                 ads->ld = NULL;
70                 status = ads_connect(ads);
71                 if (!ADS_ERR_OK(status)) {
72                         DEBUG(1,("ads_search_retry: failed to reconnect (%s)\n",
73                                  ads_errstr(status)));
74                         ads_destroy(&ads);
75                         free(bp);
76                         return status;
77                 }
78         }
79         free(bp);
80
81         DEBUG(1,("ads reopen failed after error %s\n", ads_errstr(status)));
82         return status;
83 }
84
85
86 ADS_STATUS ads_search_retry(ADS_STRUCT *ads, void **res, 
87                             const char *exp, 
88                             const char **attrs)
89 {
90         return ads_do_search_retry(ads, ads->config.bind_path, LDAP_SCOPE_SUBTREE,
91                                    exp, attrs, res);
92 }
93
94 ADS_STATUS ads_search_retry_dn(ADS_STRUCT *ads, void **res, 
95                                const char *dn, 
96                                const char **attrs)
97 {
98         return ads_do_search_retry(ads, dn, LDAP_SCOPE_BASE,
99                                    "(objectclass=*)", attrs, res);
100 }
101
102 /*
103   return our ads connections structure for a domain. We keep the connection
104   open to make things faster
105 */
106 static ADS_STRUCT *ads_cached_connection(struct winbindd_domain *domain)
107 {
108         ADS_STRUCT *ads;
109         ADS_STATUS status;
110         char *ccache;
111
112         if (domain->private) {
113                 return (ADS_STRUCT *)domain->private;
114         }
115
116         /* we don't want this to affect the users ccache */
117         ccache = lock_path("winbindd_ccache");
118         SETENV("KRB5CCNAME", ccache, 1);
119         unlink(ccache);
120
121         ads = ads_init(domain->alt_name, domain->name, NULL);
122         if (!ads) {
123                 DEBUG(1,("ads_init for domain %s failed\n", domain->name));
124                 return NULL;
125         }
126
127         /* the machine acct password might have change - fetch it every time */
128         SAFE_FREE(ads->auth.password);
129         ads->auth.password = secrets_fetch_machine_password();
130
131         if (primary_realm) {
132                 SAFE_FREE(ads->auth.realm);
133                 ads->auth.realm = strdup(primary_realm);
134         }
135
136         status = ads_connect(ads);
137         if (!ADS_ERR_OK(status) || !ads->config.realm) {
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.err.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->config.realm);
156         }
157
158         domain->private = (void *)ads;
159         return ads;
160 }
161
162 /* useful utility */
163 static void sid_from_rid(struct winbindd_domain *domain, uint32 rid, DOM_SID *sid)
164 {
165         sid_copy(sid, &domain->sid);
166         sid_append_rid(sid, rid);
167 }
168
169 /* turn a sAMAccountType into a SID_NAME_USE */
170 static enum SID_NAME_USE ads_atype_map(uint32 atype)
171 {
172         switch (atype & 0xF0000000) {
173         case ATYPE_GLOBAL_GROUP:
174                 return SID_NAME_DOM_GRP;
175         case ATYPE_ACCOUNT:
176                 return SID_NAME_USER;
177         default:
178                 DEBUG(1,("hmm, need to map account type 0x%x\n", atype));
179         }
180         return SID_NAME_UNKNOWN;
181 }
182
183 /* 
184    in order to support usernames longer than 21 characters we need to 
185    use both the sAMAccountName and the userPrincipalName attributes 
186    It seems that not all users have the userPrincipalName attribute set
187 */
188 static char *pull_username(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, void *msg)
189 {
190         char *ret, *p;
191
192         ret = ads_pull_string(ads, mem_ctx, msg, "userPrincipalName");
193         if (ret && (p = strchr(ret, '@'))) {
194                 *p = 0;
195                 return ret;
196         }
197         return ads_pull_string(ads, mem_ctx, msg, "sAMAccountName");
198 }
199
200
201 /* Query display info for a realm. This is the basic user list fn */
202 static NTSTATUS query_user_list(struct winbindd_domain *domain,
203                                TALLOC_CTX *mem_ctx,
204                                uint32 *num_entries, 
205                                WINBIND_USERINFO **info)
206 {
207         ADS_STRUCT *ads = NULL;
208         const char *attrs[] = {"userPrincipalName",
209                                "sAMAccountName",
210                                "name", "objectSid", "primaryGroupID", 
211                                "sAMAccountType", NULL};
212         int i, count;
213         ADS_STATUS rc;
214         void *res = NULL;
215         void *msg = NULL;
216         NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
217
218         *num_entries = 0;
219
220         DEBUG(3,("ads: query_user_list\n"));
221
222         ads = ads_cached_connection(domain);
223         if (!ads) goto done;
224
225         rc = ads_search_retry(ads, &res, "(objectCategory=user)", attrs);
226         if (!ADS_ERR_OK(rc)) {
227                 DEBUG(1,("query_user_list ads_search: %s\n", ads_errstr(rc)));
228                 goto done;
229         }
230
231         count = ads_count_replies(ads, res);
232         if (count == 0) {
233                 DEBUG(1,("query_user_list: No users found\n"));
234                 goto done;
235         }
236
237         (*info) = talloc_zero(mem_ctx, count * sizeof(**info));
238         if (!*info) {
239                 status = NT_STATUS_NO_MEMORY;
240                 goto done;
241         }
242
243         i = 0;
244
245         for (msg = ads_first_entry(ads, res); msg; msg = ads_next_entry(ads, msg)) {
246                 char *name, *gecos;
247                 DOM_SID sid;
248                 uint32 rid, group;
249                 uint32 atype;
250
251                 if (!ads_pull_uint32(ads, msg, "sAMAccountType", &atype) ||
252                     ads_atype_map(atype) != SID_NAME_USER) {
253                         DEBUG(1,("Not a user account? atype=0x%x\n", atype));
254                         continue;
255                 }
256
257                 name = pull_username(ads, mem_ctx, msg);
258                 gecos = ads_pull_string(ads, mem_ctx, msg, "name");
259                 if (!ads_pull_sid(ads, msg, "objectSid", &sid)) {
260                         DEBUG(1,("No sid for %s !?\n", name));
261                         continue;
262                 }
263                 if (!ads_pull_uint32(ads, msg, "primaryGroupID", &group)) {
264                         DEBUG(1,("No primary group for %s !?\n", name));
265                         continue;
266                 }
267
268                 if (!sid_peek_check_rid(&domain->sid, &sid, &rid)) {
269                         DEBUG(1,("No rid for %s !?\n", name));
270                         continue;
271                 }
272
273                 (*info)[i].acct_name = name;
274                 (*info)[i].full_name = gecos;
275                 (*info)[i].user_rid = rid;
276                 (*info)[i].group_rid = group;
277                 i++;
278         }
279
280         (*num_entries) = i;
281         status = NT_STATUS_OK;
282
283         DEBUG(3,("ads query_user_list gave %d entries\n", (*num_entries)));
284
285 done:
286         if (res) ads_msgfree(ads, res);
287
288         return status;
289 }
290
291 /* list all domain groups */
292 static NTSTATUS enum_dom_groups(struct winbindd_domain *domain,
293                                 TALLOC_CTX *mem_ctx,
294                                 uint32 *num_entries, 
295                                 struct acct_info **info)
296 {
297         ADS_STRUCT *ads = NULL;
298         const char *attrs[] = {"userPrincipalName", "sAMAccountName",
299                                "name", "objectSid", 
300                                "sAMAccountType", NULL};
301         int i, count;
302         ADS_STATUS rc;
303         void *res = NULL;
304         void *msg = NULL;
305         NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
306
307         *num_entries = 0;
308
309         DEBUG(3,("ads: enum_dom_groups\n"));
310
311         ads = ads_cached_connection(domain);
312         if (!ads) goto done;
313
314         rc = ads_search_retry(ads, &res, "(objectCategory=group)", attrs);
315         if (!ADS_ERR_OK(rc)) {
316                 DEBUG(1,("enum_dom_groups ads_search: %s\n", ads_errstr(rc)));
317                 goto done;
318         }
319
320         count = ads_count_replies(ads, res);
321         if (count == 0) {
322                 DEBUG(1,("enum_dom_groups: No groups found\n"));
323                 goto done;
324         }
325
326         (*info) = talloc_zero(mem_ctx, count * sizeof(**info));
327         if (!*info) {
328                 status = NT_STATUS_NO_MEMORY;
329                 goto done;
330         }
331
332         i = 0;
333
334         for (msg = ads_first_entry(ads, res); msg; msg = ads_next_entry(ads, msg)) {
335                 char *name, *gecos;
336                 DOM_SID sid;
337                 uint32 rid;
338                 uint32 account_type;
339
340                 if (!ads_pull_uint32(ads, msg, "sAMAccountType", 
341                                      &account_type) ||
342                     !(account_type & ATYPE_GLOBAL_GROUP)) continue;
343
344                 name = pull_username(ads, mem_ctx, msg);
345                 gecos = ads_pull_string(ads, mem_ctx, msg, "name");
346                 if (!ads_pull_sid(ads, msg, "objectSid", &sid)) {
347                         DEBUG(1,("No sid for %s !?\n", name));
348                         continue;
349                 }
350
351                 if (!sid_peek_check_rid(&domain->sid, &sid, &rid)) {
352                         DEBUG(1,("No rid for %s !?\n", name));
353                         continue;
354                 }
355
356                 fstrcpy((*info)[i].acct_name, name);
357                 fstrcpy((*info)[i].acct_desc, gecos);
358                 (*info)[i].rid = rid;
359                 i++;
360         }
361
362         (*num_entries) = i;
363
364         status = NT_STATUS_OK;
365
366         DEBUG(3,("ads enum_dom_groups gave %d entries\n", (*num_entries)));
367
368 done:
369         if (res) ads_msgfree(ads, res);
370
371         return status;
372 }
373
374
375 /* convert a single name to a sid in a domain */
376 static NTSTATUS name_to_sid(struct winbindd_domain *domain,
377                             const char *name,
378                             DOM_SID *sid,
379                             enum SID_NAME_USE *type)
380 {
381         ADS_STRUCT *ads = NULL;
382         const char *attrs[] = {"objectSid", "sAMAccountType", NULL};
383         int count;
384         ADS_STATUS rc;
385         void *res = NULL;
386         char *exp;
387         uint32 t;
388         NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
389
390         DEBUG(3,("ads: name_to_sid\n"));
391
392         ads = ads_cached_connection(domain);
393         if (!ads) goto done;
394
395         /* accept either the win2000 or the pre-win2000 username */
396         asprintf(&exp, "(|(sAMAccountName=%s)(userPrincipalName=%s@%s))", 
397                  name, name, ads->config.realm);
398         rc = ads_search_retry(ads, &res, exp, attrs);
399         free(exp);
400         if (!ADS_ERR_OK(rc)) {
401                 DEBUG(1,("name_to_sid ads_search: %s\n", ads_errstr(rc)));
402                 goto done;
403         }
404
405         count = ads_count_replies(ads, res);
406         if (count != 1) {
407                 DEBUG(1,("name_to_sid: %s not found\n", name));
408                 goto done;
409         }
410
411         if (!ads_pull_sid(ads, res, "objectSid", sid)) {
412                 DEBUG(1,("No sid for %s !?\n", name));
413                 goto done;
414         }
415
416         if (!ads_pull_uint32(ads, res, "sAMAccountType", &t)) {
417                 DEBUG(1,("No sAMAccountType for %s !?\n", name));
418                 goto done;
419         }
420
421         *type = ads_atype_map(t);
422
423         status = NT_STATUS_OK;
424
425         DEBUG(3,("ads name_to_sid mapped %s\n", name));
426
427 done:
428         if (res) ads_msgfree(ads, res);
429
430         return status;
431 }
432
433 /* convert a sid to a user or group name */
434 static NTSTATUS sid_to_name(struct winbindd_domain *domain,
435                             TALLOC_CTX *mem_ctx,
436                             DOM_SID *sid,
437                             char **name,
438                             enum SID_NAME_USE *type)
439 {
440         ADS_STRUCT *ads = NULL;
441         const char *attrs[] = {"userPrincipalName", 
442                                "sAMAccountName",
443                                "sAMAccountType", NULL};
444         ADS_STATUS rc;
445         void *msg = NULL;
446         char *exp;
447         char *sidstr;
448         uint32 atype;
449         NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
450
451         DEBUG(3,("ads: sid_to_name\n"));
452
453         ads = ads_cached_connection(domain);
454         if (!ads) goto done;
455
456         sidstr = sid_binstring(sid);
457         asprintf(&exp, "(objectSid=%s)", sidstr);
458         rc = ads_search_retry(ads, &msg, exp, attrs);
459         free(exp);
460         free(sidstr);
461         if (!ADS_ERR_OK(rc)) {
462                 DEBUG(1,("sid_to_name ads_search: %s\n", ads_errstr(rc)));
463                 goto done;
464         }
465
466         if (!ads_pull_uint32(ads, msg, "sAMAccountType", &atype)) {
467                 goto done;
468         }
469
470         *name = pull_username(ads, mem_ctx, msg);
471         *type = ads_atype_map(atype);
472
473         status = NT_STATUS_OK;
474
475         DEBUG(3,("ads sid_to_name mapped %s\n", *name));
476
477 done:
478         if (msg) ads_msgfree(ads, msg);
479
480         return status;
481 }
482
483
484 /* convert a DN to a name, rid and name type 
485    this might become a major speed bottleneck if groups have
486    lots of users, in which case we could cache the results
487 */
488 static BOOL dn_lookup(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
489                       const char *dn,
490                       char **name, uint32 *name_type, uint32 *rid)
491 {
492         char *exp;
493         void *res = NULL;
494         const char *attrs[] = {"userPrincipalName", "sAMAccountName",
495                                "objectSid", "sAMAccountType", NULL};
496         ADS_STATUS rc;
497         uint32 atype;
498         DOM_SID sid;
499
500         asprintf(&exp, "(distinguishedName=%s)", dn);
501         rc = ads_search_retry(ads, &res, exp, attrs);
502         free(exp);
503         if (!ADS_ERR_OK(rc)) {
504                 goto failed;
505         }
506
507         (*name) = pull_username(ads, mem_ctx, res);
508
509         if (!ads_pull_uint32(ads, res, "sAMAccountType", &atype)) {
510                 goto failed;
511         }
512         (*name_type) = ads_atype_map(atype);
513
514         if (!ads_pull_sid(ads, res, "objectSid", &sid) || 
515             !sid_peek_rid(&sid, rid)) {
516                 goto failed;
517         }
518
519         if (res) ads_msgfree(ads, res);
520         return True;
521
522 failed:
523         if (res) ads_msgfree(ads, res);
524         return False;
525 }
526
527 /* Lookup user information from a rid */
528 static NTSTATUS query_user(struct winbindd_domain *domain, 
529                            TALLOC_CTX *mem_ctx, 
530                            uint32 user_rid, 
531                            WINBIND_USERINFO *info)
532 {
533         ADS_STRUCT *ads = NULL;
534         const char *attrs[] = {"userPrincipalName", 
535                                "sAMAccountName",
536                                "name", "objectSid", 
537                                "primaryGroupID", NULL};
538         ADS_STATUS rc;
539         int count;
540         void *msg = NULL;
541         char *exp;
542         DOM_SID sid;
543         char *sidstr;
544         NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
545
546         DEBUG(3,("ads: query_user\n"));
547
548         sid_from_rid(domain, user_rid, &sid);
549
550         ads = ads_cached_connection(domain);
551         if (!ads) goto done;
552
553         sidstr = sid_binstring(&sid);
554         asprintf(&exp, "(objectSid=%s)", sidstr);
555         rc = ads_search_retry(ads, &msg, exp, attrs);
556         free(exp);
557         free(sidstr);
558         if (!ADS_ERR_OK(rc)) {
559                 DEBUG(1,("query_user(rid=%d) ads_search: %s\n", user_rid, ads_errstr(rc)));
560                 goto done;
561         }
562
563         count = ads_count_replies(ads, msg);
564         if (count != 1) {
565                 DEBUG(1,("query_user(rid=%d): Not found\n", user_rid));
566                 goto done;
567         }
568
569         info->acct_name = pull_username(ads, mem_ctx, msg);
570         info->full_name = ads_pull_string(ads, mem_ctx, msg, "name");
571         if (!ads_pull_sid(ads, msg, "objectSid", &sid)) {
572                 DEBUG(1,("No sid for %d !?\n", user_rid));
573                 goto done;
574         }
575         if (!ads_pull_uint32(ads, msg, "primaryGroupID", &info->group_rid)) {
576                 DEBUG(1,("No primary group for %d !?\n", user_rid));
577                 goto done;
578         }
579         
580         if (!sid_peek_check_rid(&domain->sid,&sid, &info->user_rid)) {
581                 DEBUG(1,("No rid for %d !?\n", user_rid));
582                 goto done;
583         }
584
585         status = NT_STATUS_OK;
586
587         DEBUG(3,("ads query_user gave %s\n", info->acct_name));
588 done:
589         if (msg) ads_msgfree(ads, msg);
590
591         return status;
592 }
593
594
595 /* Lookup groups a user is a member of. */
596 static NTSTATUS lookup_usergroups(struct winbindd_domain *domain,
597                                   TALLOC_CTX *mem_ctx,
598                                   uint32 user_rid, 
599                                   uint32 *num_groups, uint32 **user_gids)
600 {
601         ADS_STRUCT *ads = NULL;
602         const char *attrs[] = {"distinguishedName", NULL};
603         const char *attrs2[] = {"tokenGroups", "primaryGroupID", NULL};
604         ADS_STATUS rc;
605         int count;
606         void *msg = NULL;
607         char *exp;
608         char *user_dn;
609         DOM_SID *sids;
610         int i;
611         uint32 primary_group;
612         DOM_SID sid;
613         char *sidstr;
614         NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
615
616         *num_groups = 0;
617
618         DEBUG(3,("ads: lookup_usergroups\n"));
619
620         (*num_groups) = 0;
621
622         sid_from_rid(domain, user_rid, &sid);
623
624         ads = ads_cached_connection(domain);
625         if (!ads) goto done;
626
627         sidstr = sid_binstring(&sid);
628         asprintf(&exp, "(objectSid=%s)", sidstr);
629         rc = ads_search_retry(ads, &msg, exp, attrs);
630         free(exp);
631         free(sidstr);
632         if (!ADS_ERR_OK(rc)) {
633                 DEBUG(1,("lookup_usergroups(rid=%d) ads_search: %s\n", user_rid, ads_errstr(rc)));
634                 goto done;
635         }
636
637         user_dn = ads_pull_string(ads, mem_ctx, msg, "distinguishedName");
638
639         if (msg) ads_msgfree(ads, msg);
640
641         rc = ads_search_retry_dn(ads, &msg, user_dn, attrs2);
642         if (!ADS_ERR_OK(rc)) {
643                 DEBUG(1,("lookup_usergroups(rid=%d) ads_search tokenGroups: %s\n", user_rid, ads_errstr(rc)));
644                 goto done;
645         }
646
647         if (!ads_pull_uint32(ads, msg, "primaryGroupID", &primary_group)) {
648                 DEBUG(1,("%s: No primary group for rid=%d !?\n", domain->name, user_rid));
649                 goto done;
650         }
651
652         count = ads_pull_sids(ads, mem_ctx, msg, "tokenGroups", &sids) + 1;
653         (*user_gids) = (uint32 *)talloc_zero(mem_ctx, sizeof(uint32) * count);
654         (*user_gids)[(*num_groups)++] = primary_group;
655
656         for (i=1;i<count;i++) {
657                 uint32 rid;
658                 if (!sid_peek_check_rid(&domain->sid, &sids[i-1], &rid)) continue;
659                 (*user_gids)[*num_groups] = rid;
660                 (*num_groups)++;
661         }
662
663         status = NT_STATUS_OK;
664         DEBUG(3,("ads lookup_usergroups for rid=%d\n", user_rid));
665 done:
666         if (msg) ads_msgfree(ads, msg);
667
668         return status;
669 }
670
671 /*
672   find the members of a group, given a group rid and domain
673  */
674 static NTSTATUS lookup_groupmem(struct winbindd_domain *domain,
675                                 TALLOC_CTX *mem_ctx,
676                                 uint32 group_rid, uint32 *num_names, 
677                                 uint32 **rid_mem, char ***names, 
678                                 uint32 **name_types)
679 {
680         DOM_SID group_sid;
681         ADS_STATUS rc;
682         int count;
683         void *res=NULL;
684         ADS_STRUCT *ads = NULL;
685         char *exp;
686         NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
687         char *sidstr;
688         const char *attrs[] = {"member", NULL};
689         char **members;
690         int i, num_members;
691
692         *num_names = 0;
693
694         ads = ads_cached_connection(domain);
695         if (!ads) goto done;
696
697         sid_from_rid(domain, group_rid, &group_sid);
698         sidstr = sid_binstring(&group_sid);
699
700         /* search for all members of the group */
701         asprintf(&exp, "(objectSid=%s)",sidstr);
702         rc = ads_search_retry(ads, &res, exp, attrs);
703         free(exp);
704         free(sidstr);
705
706         if (!ADS_ERR_OK(rc)) {
707                 DEBUG(1,("query_user_list ads_search: %s\n", ads_errstr(rc)));
708                 goto done;
709         }
710
711         count = ads_count_replies(ads, res);
712         if (count == 0) {
713                 status = NT_STATUS_OK;
714                 goto done;
715         }
716
717         members = ads_pull_strings(ads, mem_ctx, res, "member");
718         if (!members) {
719                 /* no members? ok ... */
720                 status = NT_STATUS_OK;
721                 goto done;
722         }
723
724         /* now we need to turn a list of members into rids, names and name types 
725            the problem is that the members are in the form of distinguised names
726         */
727         for (i=0;members[i];i++) /* noop */ ;
728         num_members = i;
729
730         (*rid_mem) = talloc_zero(mem_ctx, sizeof(uint32) * num_members);
731         (*name_types) = talloc_zero(mem_ctx, sizeof(uint32) * num_members);
732         (*names) = talloc_zero(mem_ctx, sizeof(char *) * num_members);
733
734         for (i=0;i<num_members;i++) {
735                 uint32 name_type, rid;
736                 char *name;
737
738                 if (dn_lookup(ads, mem_ctx, members[i], &name, &name_type, &rid)) {
739                     (*names)[*num_names] = name;
740                     (*name_types)[*num_names] = name_type;
741                     (*rid_mem)[*num_names] = rid;
742                     (*num_names)++;
743                 }
744         }       
745
746         status = NT_STATUS_OK;
747         DEBUG(3,("ads lookup_groupmem for rid=%d\n", group_rid));
748 done:
749         if (res) ads_msgfree(ads, res);
750
751         return status;
752 }
753
754
755 /* find the sequence number for a domain */
756 static NTSTATUS sequence_number(struct winbindd_domain *domain, uint32 *seq)
757 {
758         ADS_STRUCT *ads = NULL;
759         ADS_STATUS rc;
760
761         *seq = DOM_SEQUENCE_NONE;
762
763         ads = ads_cached_connection(domain);
764         if (!ads) return NT_STATUS_UNSUCCESSFUL;
765
766         rc = ads_USN(ads, seq);
767         if (!ADS_ERR_OK(rc)) {
768                 /* its a dead connection */
769                 ads_destroy(&ads);
770                 domain->private = NULL;
771         }
772         return ads_ntstatus(rc);
773 }
774
775 /* get a list of trusted domains */
776 static NTSTATUS trusted_domains(struct winbindd_domain *domain,
777                                 TALLOC_CTX *mem_ctx,
778                                 uint32 *num_domains,
779                                 char ***names,
780                                 char ***alt_names,
781                                 DOM_SID **dom_sids)
782 {
783         ADS_STRUCT *ads;
784         ADS_STATUS rc;
785
786         *num_domains = 0;
787         *names = NULL;
788
789         ads = ads_cached_connection(domain);
790         if (!ads) return NT_STATUS_UNSUCCESSFUL;
791
792         rc = ads_trusted_domains(ads, mem_ctx, num_domains, names, alt_names, dom_sids);
793
794         return ads_ntstatus(rc);
795 }
796
797 /* find the domain sid for a domain */
798 static NTSTATUS domain_sid(struct winbindd_domain *domain, DOM_SID *sid)
799 {
800         ADS_STRUCT *ads;
801         ADS_STATUS rc;
802
803         ads = ads_cached_connection(domain);
804         if (!ads) return NT_STATUS_UNSUCCESSFUL;
805
806         rc = ads_domain_sid(ads, sid);
807
808         if (!ADS_ERR_OK(rc)) {
809                 /* its a dead connection */
810                 ads_destroy(&ads);
811                 domain->private = NULL;
812         }
813
814         return ads_ntstatus(rc);
815 }
816
817
818 /* find alternate names list for the domain - for ADS this is the
819    netbios name */
820 static NTSTATUS alternate_name(struct winbindd_domain *domain)
821 {
822         ADS_STRUCT *ads;
823         ADS_STATUS rc;
824         TALLOC_CTX *ctx;
825         char *workgroup;
826
827         ads = ads_cached_connection(domain);
828         if (!ads) return NT_STATUS_UNSUCCESSFUL;
829
830         if (!(ctx = talloc_init_named("alternate_name"))) {
831                 return NT_STATUS_NO_MEMORY;
832         }
833
834         rc = ads_workgroup_name(ads, ctx, &workgroup);
835
836         if (ADS_ERR_OK(rc)) {
837                 fstrcpy(domain->name, workgroup);
838                 fstrcpy(domain->alt_name, ads->config.realm);
839                 strupper(domain->alt_name);
840                 strupper(domain->name);
841         }
842
843         talloc_destroy(ctx);
844
845         return ads_ntstatus(rc);        
846 }
847
848 /* the ADS backend methods are exposed via this structure */
849 struct winbindd_methods ads_methods = {
850         True,
851         query_user_list,
852         enum_dom_groups,
853         name_to_sid,
854         sid_to_name,
855         query_user,
856         lookup_usergroups,
857         lookup_groupmem,
858         sequence_number,
859         trusted_domains,
860         domain_sid,
861         alternate_name
862 };
863
864 #endif