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