r7882: Looks like a large patch - but what it actually does is make Samba
[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_data) {
43                 ads = (ADS_STRUCT *)domain->private_data;
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_data = 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_data = (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                 uint32 group;
160                 uint32 atype;
161
162                 if (!ads_pull_uint32(ads, msg, "sAMAccountType", &atype) ||
163                     ads_atype_map(atype) != SID_NAME_USER) {
164                         DEBUG(1,("Not a user account? atype=0x%x\n", atype));
165                         continue;
166                 }
167
168                 name = ads_pull_username(ads, mem_ctx, msg);
169                 gecos = ads_pull_string(ads, mem_ctx, msg, "name");
170                 if (!ads_pull_sid(ads, msg, "objectSid",
171                                   &(*info)[i].user_sid)) {
172                         DEBUG(1,("No sid for %s !?\n", name));
173                         continue;
174                 }
175                 if (!ads_pull_uint32(ads, msg, "primaryGroupID", &group)) {
176                         DEBUG(1,("No primary group for %s !?\n", name));
177                         continue;
178                 }
179
180                 (*info)[i].acct_name = name;
181                 (*info)[i].full_name = gecos;
182                 sid_compose(&(*info)[i].group_sid, &domain->sid, group);
183                 i++;
184         }
185
186         (*num_entries) = i;
187         status = NT_STATUS_OK;
188
189         DEBUG(3,("ads query_user_list gave %d entries\n", (*num_entries)));
190
191 done:
192         if (res) 
193                 ads_msgfree(ads, res);
194
195         return status;
196 }
197
198 /* list all domain groups */
199 static NTSTATUS enum_dom_groups(struct winbindd_domain *domain,
200                                 TALLOC_CTX *mem_ctx,
201                                 uint32 *num_entries, 
202                                 struct acct_info **info)
203 {
204         ADS_STRUCT *ads = NULL;
205         const char *attrs[] = {"userPrincipalName", "sAMAccountName",
206                                "name", "objectSid", 
207                                "sAMAccountType", NULL};
208         int i, count;
209         ADS_STATUS rc;
210         void *res = NULL;
211         void *msg = NULL;
212         NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
213         uint32 group_flags;
214
215         *num_entries = 0;
216
217         DEBUG(3,("ads: enum_dom_groups\n"));
218
219         ads = ads_cached_connection(domain);
220
221         if (!ads) {
222                 domain->last_status = NT_STATUS_SERVER_DISABLED;
223                 goto done;
224         }
225
226         rc = ads_search_retry(ads, &res, "(objectCategory=group)", attrs);
227         if (!ADS_ERR_OK(rc) || !res) {
228                 DEBUG(1,("enum_dom_groups ads_search: %s\n", ads_errstr(rc)));
229                 goto done;
230         }
231
232         count = ads_count_replies(ads, res);
233         if (count == 0) {
234                 DEBUG(1,("enum_dom_groups: No groups found\n"));
235                 goto done;
236         }
237
238         (*info) = TALLOC_ZERO_ARRAY(mem_ctx, struct acct_info, count);
239         if (!*info) {
240                 status = NT_STATUS_NO_MEMORY;
241                 goto done;
242         }
243
244         i = 0;
245         
246         group_flags = ATYPE_GLOBAL_GROUP;
247
248         /* only grab domain local groups for our domain */
249         if ( domain->native_mode && strequal(lp_realm(), domain->alt_name)  )
250                 group_flags |= ATYPE_LOCAL_GROUP;
251
252         for (msg = ads_first_entry(ads, res); msg; msg = ads_next_entry(ads, msg)) {
253                 char *name, *gecos;
254                 DOM_SID sid;
255                 uint32 rid;
256                 uint32 account_type;
257
258                 if (!ads_pull_uint32(ads, msg, "sAMAccountType", &account_type) || !(account_type & group_flags) ) 
259                         continue; 
260                         
261                 name = ads_pull_username(ads, mem_ctx, msg);
262                 gecos = ads_pull_string(ads, mem_ctx, msg, "name");
263                 if (!ads_pull_sid(ads, msg, "objectSid", &sid)) {
264                         DEBUG(1,("No sid for %s !?\n", name));
265                         continue;
266                 }
267
268                 if (!sid_peek_check_rid(&domain->sid, &sid, &rid)) {
269                         DEBUG(1,("No rid for %s !?\n", name));
270                         continue;
271                 }
272
273                 fstrcpy((*info)[i].acct_name, name);
274                 fstrcpy((*info)[i].acct_desc, gecos);
275                 (*info)[i].rid = rid;
276                 i++;
277         }
278
279         (*num_entries) = i;
280
281         status = NT_STATUS_OK;
282
283         DEBUG(3,("ads enum_dom_groups gave %d entries\n", (*num_entries)));
284
285 done:
286         if (res) 
287                 ads_msgfree(ads, res);
288
289         return status;
290 }
291
292 /* list all domain local groups */
293 static NTSTATUS enum_local_groups(struct winbindd_domain *domain,
294                                 TALLOC_CTX *mem_ctx,
295                                 uint32 *num_entries, 
296                                 struct acct_info **info)
297 {
298         /*
299          * This is a stub function only as we returned the domain 
300          * local groups in enum_dom_groups() if the domain->native field
301          * was true.  This is a simple performance optimization when
302          * using LDAP.
303          *
304          * if we ever need to enumerate domain local groups separately, 
305          * then this the optimization in enum_dom_groups() will need 
306          * to be split out
307          */
308         *num_entries = 0;
309         
310         return NT_STATUS_OK;
311 }
312
313 /* convert a DN to a name, SID and name type 
314    this might become a major speed bottleneck if groups have
315    lots of users, in which case we could cache the results
316 */
317 static BOOL dn_lookup(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
318                       const char *dn,
319                       char **name, uint32 *name_type, DOM_SID *sid)
320 {
321         void *res = NULL;
322         const char *attrs[] = {"userPrincipalName", "sAMAccountName",
323                                "objectSid", "sAMAccountType", NULL};
324         ADS_STATUS rc;
325         uint32 atype;
326         DEBUG(3,("ads: dn_lookup\n"));
327
328         rc = ads_search_retry_dn(ads, &res, dn, attrs);
329
330         if (!ADS_ERR_OK(rc) || !res) {
331                 goto failed;
332         }
333
334         (*name) = ads_pull_username(ads, mem_ctx, res);
335
336         if (!ads_pull_uint32(ads, res, "sAMAccountType", &atype)) {
337                 goto failed;
338         }
339         (*name_type) = ads_atype_map(atype);
340
341         if (!ads_pull_sid(ads, res, "objectSid", sid)) {
342                 goto failed;
343         }
344
345         if (res) 
346                 ads_msgfree(ads, res);
347
348         return True;
349
350 failed:
351         if (res) 
352                 ads_msgfree(ads, res);
353
354         return False;
355 }
356
357 /* Lookup user information from a rid */
358 static NTSTATUS query_user(struct winbindd_domain *domain, 
359                            TALLOC_CTX *mem_ctx, 
360                            const DOM_SID *sid, 
361                            WINBIND_USERINFO *info)
362 {
363         ADS_STRUCT *ads = NULL;
364         const char *attrs[] = {"userPrincipalName", 
365                                "sAMAccountName",
366                                "name", 
367                                "primaryGroupID", NULL};
368         ADS_STATUS rc;
369         int count;
370         void *msg = NULL;
371         char *ldap_exp;
372         char *sidstr;
373         uint32 group_rid;
374         NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
375
376         DEBUG(3,("ads: query_user\n"));
377
378         ads = ads_cached_connection(domain);
379         
380         if (!ads) {
381                 domain->last_status = NT_STATUS_SERVER_DISABLED;
382                 goto done;
383         }
384
385         sidstr = sid_binstring(sid);
386         asprintf(&ldap_exp, "(objectSid=%s)", sidstr);
387         rc = ads_search_retry(ads, &msg, ldap_exp, attrs);
388         free(ldap_exp);
389         free(sidstr);
390         if (!ADS_ERR_OK(rc) || !msg) {
391                 DEBUG(1,("query_user(sid=%s) ads_search: %s\n",
392                          sid_string_static(sid), ads_errstr(rc)));
393                 goto done;
394         }
395
396         count = ads_count_replies(ads, msg);
397         if (count != 1) {
398                 DEBUG(1,("query_user(sid=%s): Not found\n",
399                          sid_string_static(sid)));
400                 goto done;
401         }
402
403         info->acct_name = ads_pull_username(ads, mem_ctx, msg);
404         info->full_name = ads_pull_string(ads, mem_ctx, msg, "name");
405
406         if (!ads_pull_uint32(ads, msg, "primaryGroupID", &group_rid)) {
407                 DEBUG(1,("No primary group for %s !?\n",
408                          sid_string_static(sid)));
409                 goto done;
410         }
411
412         sid_copy(&info->user_sid, sid);
413         sid_compose(&info->group_sid, &domain->sid, group_rid);
414
415         status = NT_STATUS_OK;
416
417         DEBUG(3,("ads query_user gave %s\n", info->acct_name));
418 done:
419         if (msg) 
420                 ads_msgfree(ads, msg);
421
422         return status;
423 }
424
425 /* Lookup groups a user is a member of - alternate method, for when
426    tokenGroups are not available. */
427 static NTSTATUS lookup_usergroups_alt(struct winbindd_domain *domain,
428                                       TALLOC_CTX *mem_ctx,
429                                       const char *user_dn, 
430                                       DOM_SID *primary_group,
431                                       uint32 *num_groups, DOM_SID **user_sids)
432 {
433         ADS_STATUS rc;
434         NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
435         int count;
436         void *res = NULL;
437         void *msg = NULL;
438         char *ldap_exp;
439         ADS_STRUCT *ads;
440         const char *group_attrs[] = {"objectSid", NULL};
441         char *escaped_dn;
442
443         DEBUG(3,("ads: lookup_usergroups_alt\n"));
444
445         ads = ads_cached_connection(domain);
446
447         if (!ads) {
448                 domain->last_status = NT_STATUS_SERVER_DISABLED;
449                 goto done;
450         }
451
452         if (!(escaped_dn = escape_ldap_string_alloc(user_dn))) {
453                 status = NT_STATUS_NO_MEMORY;
454                 goto done;
455         }
456
457         /* buggy server, no tokenGroups.  Instead lookup what groups this user
458            is a member of by DN search on member*/
459
460         if (!(ldap_exp = talloc_asprintf(mem_ctx, "(&(member=%s)(objectClass=group))", escaped_dn))) {
461                 DEBUG(1,("lookup_usergroups(dn=%s) asprintf failed!\n", user_dn));
462                 SAFE_FREE(escaped_dn);
463                 status = NT_STATUS_NO_MEMORY;
464                 goto done;
465         }
466
467         SAFE_FREE(escaped_dn);
468
469         rc = ads_search_retry(ads, &res, ldap_exp, group_attrs);
470         
471         if (!ADS_ERR_OK(rc) || !res) {
472                 DEBUG(1,("lookup_usergroups ads_search member=%s: %s\n", user_dn, ads_errstr(rc)));
473                 return ads_ntstatus(rc);
474         }
475         
476         count = ads_count_replies(ads, res);
477         if (count == 0) {
478                 DEBUG(5,("lookup_usergroups: No supp groups found\n"));
479                 
480                 status = ads_ntstatus(rc);
481                 goto done;
482         }
483         
484         *user_sids = NULL;
485         *num_groups = 0;
486
487         add_sid_to_array(mem_ctx, primary_group, user_sids, num_groups);
488
489         for (msg = ads_first_entry(ads, res); msg;
490              msg = ads_next_entry(ads, msg)) {
491                 DOM_SID group_sid;
492                 
493                 if (!ads_pull_sid(ads, msg, "objectSid", &group_sid)) {
494                         DEBUG(1,("No sid for this group ?!?\n"));
495                         continue;
496                 }
497
498                 add_sid_to_array(mem_ctx, &group_sid, user_sids, num_groups);
499         }
500
501         status = (user_sids != NULL) ? NT_STATUS_OK : NT_STATUS_NO_MEMORY;
502
503         DEBUG(3,("ads lookup_usergroups (alt) for dn=%s\n", user_dn));
504 done:
505         if (res) 
506                 ads_msgfree(ads, res);
507
508         return status;
509 }
510
511 /* Lookup groups a user is a member of. */
512 static NTSTATUS lookup_usergroups(struct winbindd_domain *domain,
513                                   TALLOC_CTX *mem_ctx,
514                                   const DOM_SID *sid, 
515                                   uint32 *num_groups, DOM_SID **user_sids)
516 {
517         ADS_STRUCT *ads = NULL;
518         const char *attrs[] = {"tokenGroups", "primaryGroupID", NULL};
519         ADS_STATUS rc;
520         int count;
521         LDAPMessage *msg = NULL;
522         char *user_dn;
523         DOM_SID *sids;
524         int i;
525         DOM_SID primary_group;
526         uint32 primary_group_rid;
527         fstring sid_string;
528         NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
529
530         DEBUG(3,("ads: lookup_usergroups\n"));
531         *num_groups = 0;
532
533         ads = ads_cached_connection(domain);
534         
535         if (!ads) {
536                 domain->last_status = NT_STATUS_SERVER_DISABLED;
537                 status = NT_STATUS_SERVER_DISABLED;
538                 goto done;
539         }
540
541         rc = ads_sid_to_dn(ads, mem_ctx, sid, &user_dn);
542         if (!ADS_ERR_OK(rc)) {
543                 status = ads_ntstatus(rc);
544                 goto done;
545         }
546
547         rc = ads_search_retry_dn(ads, (void**)&msg, user_dn, attrs);
548         if (!ADS_ERR_OK(rc)) {
549                 status = ads_ntstatus(rc);
550                 DEBUG(1,("lookup_usergroups(sid=%s) ads_search tokenGroups: %s\n", 
551                          sid_to_string(sid_string, sid), ads_errstr(rc)));
552                 goto done;
553         }
554         
555         if (!msg) {
556                 DEBUG(1,("lookup_usergroups(sid=%s) ads_search tokenGroups: NULL msg\n", 
557                          sid_to_string(sid_string, sid)));
558                 status = NT_STATUS_UNSUCCESSFUL;
559                 goto done;
560         }
561
562         if (!ads_pull_uint32(ads, msg, "primaryGroupID", &primary_group_rid)) {
563                 DEBUG(1,("%s: No primary group for sid=%s !?\n", 
564                          domain->name, sid_to_string(sid_string, sid)));
565                 goto done;
566         }
567
568         sid_copy(&primary_group, &domain->sid);
569         sid_append_rid(&primary_group, primary_group_rid);
570
571         count = ads_pull_sids(ads, mem_ctx, msg, "tokenGroups", &sids);
572
573         if (msg) 
574                 ads_msgfree(ads, msg);
575
576         /* there must always be at least one group in the token, 
577            unless we are talking to a buggy Win2k server */
578         if (count == 0) {
579                 return lookup_usergroups_alt(domain, mem_ctx, user_dn, 
580                                              &primary_group,
581                                              num_groups, user_sids);
582         }
583
584         *user_sids = NULL;
585         *num_groups = 0;
586
587         add_sid_to_array(mem_ctx, &primary_group, user_sids, num_groups);
588         
589         for (i=0;i<count;i++)
590                 add_sid_to_array_unique(mem_ctx, &sids[i],
591                                         user_sids, num_groups);
592
593         status = (user_sids != NULL) ? NT_STATUS_OK : NT_STATUS_NO_MEMORY;
594
595         DEBUG(3,("ads lookup_usergroups for sid=%s\n",
596                  sid_to_string(sid_string, sid)));
597 done:
598         return status;
599 }
600
601 /*
602   find the members of a group, given a group rid and domain
603  */
604 static NTSTATUS lookup_groupmem(struct winbindd_domain *domain,
605                                 TALLOC_CTX *mem_ctx,
606                                 const DOM_SID *group_sid, uint32 *num_names, 
607                                 DOM_SID **sid_mem, char ***names, 
608                                 uint32 **name_types)
609 {
610         ADS_STATUS rc;
611         int count;
612         void *res=NULL;
613         ADS_STRUCT *ads = NULL;
614         char *ldap_exp;
615         NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
616         char *sidstr;
617         char **members;
618         int i, num_members;
619         fstring sid_string;
620         BOOL more_values;
621         const char **attrs;
622         uint32 first_usn;
623         uint32 current_usn;
624         int num_retries = 0;
625
626         DEBUG(10,("ads: lookup_groupmem %s sid=%s\n", domain->name, 
627                   sid_string_static(group_sid)));
628
629         *num_names = 0;
630
631         ads = ads_cached_connection(domain);
632         
633         if (!ads) {
634                 domain->last_status = NT_STATUS_SERVER_DISABLED;
635                 goto done;
636         }
637
638         sidstr = sid_binstring(group_sid);
639
640         /* search for all members of the group */
641         if (!(ldap_exp = talloc_asprintf(mem_ctx, "(objectSid=%s)",sidstr))) {
642                 SAFE_FREE(sidstr);
643                 DEBUG(1, ("ads: lookup_groupmem: tallloc_asprintf for ldap_exp failed!\n"));
644                 status = NT_STATUS_NO_MEMORY;
645                 goto done;
646         }
647         SAFE_FREE(sidstr);
648
649         members = NULL;
650         num_members = 0;
651
652         attrs = TALLOC_ARRAY(mem_ctx, const char *, 3);
653         attrs[1] = talloc_strdup(mem_ctx, "usnChanged");
654         attrs[2] = NULL;
655                 
656         do {
657                 if (num_members == 0) 
658                         attrs[0] = talloc_strdup(mem_ctx, "member");
659
660                 DEBUG(10, ("Searching for attrs[0] = %s, attrs[1] = %s\n", attrs[0], attrs[1]));
661
662                 rc = ads_search_retry(ads, &res, ldap_exp, attrs);
663
664                 if (!ADS_ERR_OK(rc) || !res) {
665                         DEBUG(1,("ads: lookup_groupmem ads_search: %s\n",
666                                  ads_errstr(rc)));
667                         status = ads_ntstatus(rc);
668                         goto done;
669                 }
670
671                 count = ads_count_replies(ads, res);
672                 if (count == 0)
673                         break;
674
675                 if (num_members == 0) {
676                         if (!ads_pull_uint32(ads, res, "usnChanged", &first_usn)) {
677                                 DEBUG(1, ("ads: lookup_groupmem could not pull usnChanged!\n"));
678                                 goto done;
679                         }
680                 }
681
682                 if (!ads_pull_uint32(ads, res, "usnChanged", &current_usn)) {
683                         DEBUG(1, ("ads: lookup_groupmem could not pull usnChanged!\n"));
684                         goto done;
685                 }
686
687                 if (first_usn != current_usn) {
688                         DEBUG(5, ("ads: lookup_groupmem USN on this record changed"
689                                   " - restarting search\n"));
690                         if (num_retries < 5) {
691                                 num_retries++;
692                                 num_members = 0;
693                                 continue;
694                         } else {
695                                 DEBUG(5, ("ads: lookup_groupmem USN on this record changed"
696                                           " - restarted search too many times, aborting!\n"));
697                                 status = NT_STATUS_UNSUCCESSFUL;
698                                 goto done;
699                         }
700                 }
701
702                 members = ads_pull_strings_range(ads, mem_ctx, res,
703                                                  "member",
704                                                  members,
705                                                  &attrs[0],
706                                                  &num_members,
707                                                  &more_values);
708
709                 if ((members == NULL) || (num_members == 0))
710                         break;
711
712         } while (more_values);
713                 
714         /* now we need to turn a list of members into rids, names and name types 
715            the problem is that the members are in the form of distinguised names
716         */
717
718         (*sid_mem) = TALLOC_ZERO_ARRAY(mem_ctx, DOM_SID, num_members);
719         (*name_types) = TALLOC_ZERO_ARRAY(mem_ctx, uint32, num_members);
720         (*names) = TALLOC_ZERO_ARRAY(mem_ctx, char *, num_members);
721
722         for (i=0;i<num_members;i++) {
723                 uint32 name_type;
724                 char *name;
725                 DOM_SID sid;
726
727                 if (dn_lookup(ads, mem_ctx, members[i], &name, &name_type, &sid)) {
728                     (*names)[*num_names] = name;
729                     (*name_types)[*num_names] = name_type;
730                     sid_copy(&(*sid_mem)[*num_names], &sid);
731                     (*num_names)++;
732                 }
733         }       
734
735         status = NT_STATUS_OK;
736         DEBUG(3,("ads lookup_groupmem for sid=%s\n", sid_to_string(sid_string, group_sid)));
737 done:
738
739         if (res) 
740                 ads_msgfree(ads, res);
741
742         return status;
743 }
744
745 /* find the sequence number for a domain */
746 static NTSTATUS sequence_number(struct winbindd_domain *domain, uint32 *seq)
747 {
748         ADS_STRUCT *ads = NULL;
749         ADS_STATUS rc;
750
751         DEBUG(3,("ads: fetch sequence_number for %s\n", domain->name));
752
753         *seq = DOM_SEQUENCE_NONE;
754
755         ads = ads_cached_connection(domain);
756         
757         if (!ads) {
758                 domain->last_status = NT_STATUS_SERVER_DISABLED;
759                 return NT_STATUS_UNSUCCESSFUL;
760         }
761
762         rc = ads_USN(ads, seq);
763         
764         if (!ADS_ERR_OK(rc)) {
765         
766                 /* its a dead connection ; don't destroy it 
767                    through since ads_USN() has already done 
768                    that indirectly */
769                    
770                 domain->private_data = NULL;
771         }
772         return ads_ntstatus(rc);
773 }
774
775 /* get a list of trusted domains */
776 static NTSTATUS trusted_domains(struct winbindd_domain *domain,
777                                 TALLOC_CTX *mem_ctx,
778                                 uint32 *num_domains,
779                                 char ***names,
780                                 char ***alt_names,
781                                 DOM_SID **dom_sids)
782 {
783         NTSTATUS                result = NT_STATUS_UNSUCCESSFUL;
784         struct ds_domain_trust  *domains = NULL;
785         int                     count = 0;
786         int                     i;
787                                 /* i think we only need our forest and downlevel trusted domains */
788         uint32                  flags = DS_DOMAIN_IN_FOREST | DS_DOMAIN_DIRECT_OUTBOUND;
789         struct rpc_pipe_client *cli;
790
791         DEBUG(3,("ads: trusted_domains\n"));
792
793         *num_domains = 0;
794         *alt_names   = NULL;
795         *names       = NULL;
796         *dom_sids    = NULL;
797
798         {
799                 unsigned char *session_key;
800                 DOM_CRED *creds;
801
802                 result = cm_connect_netlogon(domain, mem_ctx, &cli,
803                                              &session_key, &creds);
804         }
805
806         if (!NT_STATUS_IS_OK(result)) {
807                 DEBUG(5, ("trusted_domains: Could not open a connection to %s "
808                           "for PIPE_NETLOGON (%s)\n", 
809                           domain->name, nt_errstr(result)));
810                 return NT_STATUS_UNSUCCESSFUL;
811         }
812         
813         if ( NT_STATUS_IS_OK(result) )
814                 result = rpccli_ds_enum_domain_trusts(cli, mem_ctx,
815                                                       cli->cli->desthost, 
816                                                       flags, &domains,
817                                                       (unsigned int *)&count);
818         
819         if ( NT_STATUS_IS_OK(result) && count) {
820         
821                 /* Allocate memory for trusted domain names and sids */
822
823                 if ( !(*names = TALLOC_ARRAY(mem_ctx, char *, count)) ) {
824                         DEBUG(0, ("trusted_domains: out of memory\n"));
825                         return NT_STATUS_NO_MEMORY;
826                 }
827
828                 if ( !(*alt_names = TALLOC_ARRAY(mem_ctx, char *, count)) ) {
829                         DEBUG(0, ("trusted_domains: out of memory\n"));
830                         return NT_STATUS_NO_MEMORY;
831                 }
832
833                 if ( !(*dom_sids = TALLOC_ARRAY(mem_ctx, DOM_SID, count)) ) {
834                         DEBUG(0, ("trusted_domains: out of memory\n"));
835                         return NT_STATUS_NO_MEMORY;
836                 }
837
838                 /* Copy across names and sids */
839
840                 for (i = 0; i < count; i++) {
841                         (*names)[i] = domains[i].netbios_domain;
842                         (*alt_names)[i] = domains[i].dns_domain;
843
844                         sid_copy(&(*dom_sids)[i], &domains[i].sid);
845                 }
846
847                 *num_domains = count;   
848         }
849
850         return result;
851 }
852
853 /* find alternate names list for the domain - for ADS this is the
854    netbios name */
855 static NTSTATUS alternate_name(struct winbindd_domain *domain)
856 {
857         ADS_STRUCT *ads;
858         ADS_STATUS rc;
859         TALLOC_CTX *ctx;
860         const char *workgroup;
861
862         DEBUG(3,("ads: alternate_name\n"));
863
864         ads = ads_cached_connection(domain);
865         
866         if (!ads) {
867                 domain->last_status = NT_STATUS_SERVER_DISABLED;
868                 return NT_STATUS_UNSUCCESSFUL;
869         }
870
871         if (!(ctx = talloc_init("alternate_name"))) {
872                 return NT_STATUS_NO_MEMORY;
873         }
874
875         rc = ads_workgroup_name(ads, ctx, &workgroup);
876
877         if (ADS_ERR_OK(rc)) {
878                 fstrcpy(domain->name, workgroup);
879                 fstrcpy(domain->alt_name, ads->config.realm);
880                 strupper_m(domain->alt_name);
881                 strupper_m(domain->name);
882         }
883
884         talloc_destroy(ctx);
885
886         return ads_ntstatus(rc);        
887 }
888
889 /* the ADS backend methods are exposed via this structure */
890 struct winbindd_methods ads_methods = {
891         True,
892         query_user_list,
893         enum_dom_groups,
894         enum_local_groups,
895         msrpc_name_to_sid,
896         msrpc_sid_to_name,
897         query_user,
898         lookup_usergroups,
899         msrpc_lookup_useraliases,
900         lookup_groupmem,
901         sequence_number,
902         trusted_domains,
903         alternate_name
904 };
905
906 #endif