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