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