Merge branch 'v3-2-test' of ssh://git.samba.org/data/git/samba into v3-2-test
[tprouty/samba.git] / source / winbindd / 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 3 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, see <http://www.gnu.org/licenses/>.
22 */
23
24 #include "includes.h"
25 #include "winbindd.h"
26
27 #ifdef HAVE_ADS
28
29 #undef DBGC_CLASS
30 #define DBGC_CLASS DBGC_WINBIND
31
32 extern struct winbindd_methods reconnect_methods;
33
34 /*
35   return our ads connections structure for a domain. We keep the connection
36   open to make things faster
37 */
38 static ADS_STRUCT *ads_cached_connection(struct winbindd_domain *domain)
39 {
40         ADS_STRUCT *ads;
41         ADS_STATUS status;
42         fstring dc_name;
43         struct sockaddr_storage dc_ss;
44
45         DEBUG(10,("ads_cached_connection\n"));
46
47         if (domain->private_data) {
48
49                 time_t expire;
50                 time_t now = time(NULL);
51
52                 /* check for a valid structure */
53                 ads = (ADS_STRUCT *)domain->private_data;
54
55                 expire = MIN(ads->auth.tgt_expire, ads->auth.tgs_expire);
56
57                 DEBUG(7, ("Current tickets expire in %d seconds (at %d, time is now %d)\n",
58                           (uint32)expire-(uint32)now, (uint32) expire, (uint32) now));
59
60                 if ( ads->config.realm && (expire > now)) {
61                         return ads;
62                 } else {
63                         /* we own this ADS_STRUCT so make sure it goes away */
64                         DEBUG(7,("Deleting expired krb5 credential cache\n"));
65                         ads->is_mine = True;
66                         ads_destroy( &ads );
67                         ads_kdestroy("MEMORY:winbind_ccache");
68                         domain->private_data = NULL;
69                 }
70         }
71
72         /* we don't want this to affect the users ccache */
73         setenv("KRB5CCNAME", "MEMORY:winbind_ccache", 1);
74
75         ads = ads_init(domain->alt_name, domain->name, NULL);
76         if (!ads) {
77                 DEBUG(1,("ads_init for domain %s failed\n", domain->name));
78                 return NULL;
79         }
80
81         /* the machine acct password might have change - fetch it every time */
82
83         SAFE_FREE(ads->auth.password);
84         SAFE_FREE(ads->auth.realm);
85
86         if ( IS_DC ) {
87                 DOM_SID sid;
88                 time_t last_set_time;
89
90                 if ( !pdb_get_trusteddom_pw( domain->name, &ads->auth.password, &sid, &last_set_time ) ) {
91                         ads_destroy( &ads );
92                         return NULL;
93                 }
94                 ads->auth.realm = SMB_STRDUP( ads->server.realm );
95                 strupper_m( ads->auth.realm );
96         }
97         else {
98                 struct winbindd_domain *our_domain = domain;
99
100                 ads->auth.password = secrets_fetch_machine_password(lp_workgroup(), NULL, NULL);
101
102                 /* always give preference to the alt_name in our
103                    primary domain if possible */
104
105                 if ( !domain->primary )
106                         our_domain = find_our_domain();
107
108                 if ( our_domain->alt_name[0] != '\0' ) {
109                         ads->auth.realm = SMB_STRDUP( our_domain->alt_name );
110                         strupper_m( ads->auth.realm );
111                 }
112                 else
113                         ads->auth.realm = SMB_STRDUP( lp_realm() );
114         }
115
116         ads->auth.renewable = WINBINDD_PAM_AUTH_KRB5_RENEW_TIME;
117
118         /* Setup the server affinity cache.  We don't reaally care
119            about the name.  Just setup affinity and the KRB5_CONFIG
120            file. */
121
122         get_dc_name( ads->server.workgroup, ads->server.realm, dc_name, &dc_ss );
123
124         status = ads_connect(ads);
125         if (!ADS_ERR_OK(status) || !ads->config.realm) {
126                 DEBUG(1,("ads_connect for domain %s failed: %s\n",
127                          domain->name, ads_errstr(status)));
128                 ads_destroy(&ads);
129
130                 /* if we get ECONNREFUSED then it might be a NT4
131                    server, fall back to MSRPC */
132                 if (status.error_type == ENUM_ADS_ERROR_SYSTEM &&
133                     status.err.rc == ECONNREFUSED) {
134                         /* 'reconnect_methods' is the MS-RPC backend. */
135                         DEBUG(1,("Trying MSRPC methods\n"));
136                         domain->backend = &reconnect_methods;
137                 }
138                 return NULL;
139         }
140
141         /* set the flag that says we don't own the memory even
142            though we do so that ads_destroy() won't destroy the
143            structure we pass back by reference */
144
145         ads->is_mine = False;
146
147         domain->private_data = (void *)ads;
148         return ads;
149 }
150
151
152 /* Query display info for a realm. This is the basic user list fn */
153 static NTSTATUS query_user_list(struct winbindd_domain *domain,
154                                TALLOC_CTX *mem_ctx,
155                                uint32 *num_entries, 
156                                WINBIND_USERINFO **info)
157 {
158         ADS_STRUCT *ads = NULL;
159         const char *attrs[] = { "*", NULL };
160         int i, count;
161         ADS_STATUS rc;
162         LDAPMessage *res = NULL;
163         LDAPMessage *msg = NULL;
164         NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
165
166         *num_entries = 0;
167
168         DEBUG(3,("ads: query_user_list\n"));
169
170         if ( !winbindd_can_contact_domain( domain ) ) {
171                 DEBUG(10,("query_user_list: No incoming trust for domain %s\n",
172                           domain->name));               
173                 return NT_STATUS_OK;
174         }
175
176         ads = ads_cached_connection(domain);
177         
178         if (!ads) {
179                 domain->last_status = NT_STATUS_SERVER_DISABLED;
180                 goto done;
181         }
182
183         rc = ads_search_retry(ads, &res, "(objectCategory=user)", attrs);
184         if (!ADS_ERR_OK(rc) || !res) {
185                 DEBUG(1,("query_user_list ads_search: %s\n", ads_errstr(rc)));
186                 goto done;
187         }
188
189         count = ads_count_replies(ads, res);
190         if (count == 0) {
191                 DEBUG(1,("query_user_list: No users found\n"));
192                 goto done;
193         }
194
195         (*info) = TALLOC_ZERO_ARRAY(mem_ctx, WINBIND_USERINFO, count);
196         if (!*info) {
197                 status = NT_STATUS_NO_MEMORY;
198                 goto done;
199         }
200
201         i = 0;
202
203         for (msg = ads_first_entry(ads, res); msg; msg = ads_next_entry(ads, msg)) {
204                 char *name, *gecos = NULL;
205                 char *homedir = NULL;
206                 char *shell = NULL;
207                 uint32 group;
208                 uint32 atype;
209                 DOM_SID user_sid;
210                 gid_t primary_gid = (gid_t)-1;
211
212                 if (!ads_pull_uint32(ads, msg, "sAMAccountType", &atype) ||
213                     ads_atype_map(atype) != SID_NAME_USER) {
214                         DEBUG(1,("Not a user account? atype=0x%x\n", atype));
215                         continue;
216                 }
217
218                 name = ads_pull_username(ads, mem_ctx, msg);
219
220                 if ( ads_pull_sid( ads, msg, "objectSid", &user_sid ) ) {
221                         status = nss_get_info_cached( domain, &user_sid, mem_ctx, 
222                                                ads, msg, &homedir, &shell, &gecos,
223                                                &primary_gid );
224                 }
225
226                 if (gecos == NULL) {
227                         gecos = ads_pull_string(ads, mem_ctx, msg, "name");
228                 }
229         
230                 if (!ads_pull_sid(ads, msg, "objectSid",
231                                   &(*info)[i].user_sid)) {
232                         DEBUG(1,("No sid for %s !?\n", name));
233                         continue;
234                 }
235                 if (!ads_pull_uint32(ads, msg, "primaryGroupID", &group)) {
236                         DEBUG(1,("No primary group for %s !?\n", name));
237                         continue;
238                 }
239
240                 (*info)[i].acct_name = name;
241                 (*info)[i].full_name = gecos;
242                 (*info)[i].homedir = homedir;
243                 (*info)[i].shell = shell;
244                 (*info)[i].primary_gid = primary_gid;
245                 sid_compose(&(*info)[i].group_sid, &domain->sid, group);
246                 i++;
247         }
248
249         (*num_entries) = i;
250         status = NT_STATUS_OK;
251
252         DEBUG(3,("ads query_user_list gave %d entries\n", (*num_entries)));
253
254 done:
255         if (res) 
256                 ads_msgfree(ads, res);
257
258         return status;
259 }
260
261 /* list all domain groups */
262 static NTSTATUS enum_dom_groups(struct winbindd_domain *domain,
263                                 TALLOC_CTX *mem_ctx,
264                                 uint32 *num_entries, 
265                                 struct acct_info **info)
266 {
267         ADS_STRUCT *ads = NULL;
268         const char *attrs[] = {"userPrincipalName", "sAMAccountName",
269                                "name", "objectSid", NULL};
270         int i, count;
271         ADS_STATUS rc;
272         LDAPMessage *res = NULL;
273         LDAPMessage *msg = NULL;
274         NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
275         const char *filter;
276         bool enum_dom_local_groups = False;
277
278         *num_entries = 0;
279
280         DEBUG(3,("ads: enum_dom_groups\n"));
281
282         if ( !winbindd_can_contact_domain( domain ) ) {
283                 DEBUG(10,("enum_dom_groups: No incoming trust for domain %s\n",
284                           domain->name));               
285                 return NT_STATUS_OK;
286         }
287
288         /* only grab domain local groups for our domain */
289         if ( domain->active_directory && strequal(lp_realm(), domain->alt_name)  ) {
290                 enum_dom_local_groups = True;
291         }
292
293         /* Workaround ADS LDAP bug present in MS W2K3 SP0 and W2K SP4 w/o
294          * rollup-fixes:
295          *
296          * According to Section 5.1(4) of RFC 2251 if a value of a type is it's
297          * default value, it MUST be absent. In case of extensible matching the
298          * "dnattr" boolean defaults to FALSE and so it must be only be present
299          * when set to TRUE. 
300          *
301          * When it is set to FALSE and the OpenLDAP lib (correctly) encodes a
302          * filter using bitwise matching rule then the buggy AD fails to decode
303          * the extensible match. As a workaround set it to TRUE and thereby add
304          * the dnAttributes "dn" field to cope with those older AD versions.
305          * It should not harm and won't put any additional load on the AD since
306          * none of the dn components have a bitmask-attribute.
307          *
308          * Thanks to Ralf Haferkamp for input and testing - Guenther */
309
310         filter = talloc_asprintf(mem_ctx, "(&(objectCategory=group)(&(groupType:dn:%s:=%d)(!(groupType:dn:%s:=%d))))", 
311                                  ADS_LDAP_MATCHING_RULE_BIT_AND, GROUP_TYPE_SECURITY_ENABLED,
312                                  ADS_LDAP_MATCHING_RULE_BIT_AND, 
313                                  enum_dom_local_groups ? GROUP_TYPE_BUILTIN_LOCAL_GROUP : GROUP_TYPE_RESOURCE_GROUP);
314
315         if (filter == NULL) {
316                 status = NT_STATUS_NO_MEMORY;
317                 goto done;
318         }
319
320         ads = ads_cached_connection(domain);
321
322         if (!ads) {
323                 domain->last_status = NT_STATUS_SERVER_DISABLED;
324                 goto done;
325         }
326
327         rc = ads_search_retry(ads, &res, filter, attrs);
328         if (!ADS_ERR_OK(rc) || !res) {
329                 DEBUG(1,("enum_dom_groups ads_search: %s\n", ads_errstr(rc)));
330                 goto done;
331         }
332
333         count = ads_count_replies(ads, res);
334         if (count == 0) {
335                 DEBUG(1,("enum_dom_groups: No groups found\n"));
336                 goto done;
337         }
338
339         (*info) = TALLOC_ZERO_ARRAY(mem_ctx, struct acct_info, count);
340         if (!*info) {
341                 status = NT_STATUS_NO_MEMORY;
342                 goto done;
343         }
344
345         i = 0;
346         
347         for (msg = ads_first_entry(ads, res); msg; msg = ads_next_entry(ads, msg)) {
348                 char *name, *gecos;
349                 DOM_SID sid;
350                 uint32 rid;
351
352                 name = ads_pull_username(ads, mem_ctx, msg);
353                 gecos = ads_pull_string(ads, mem_ctx, msg, "name");
354                 if (!ads_pull_sid(ads, msg, "objectSid", &sid)) {
355                         DEBUG(1,("No sid for %s !?\n", name));
356                         continue;
357                 }
358
359                 if (!sid_peek_check_rid(&domain->sid, &sid, &rid)) {
360                         DEBUG(1,("No rid for %s !?\n", name));
361                         continue;
362                 }
363
364                 fstrcpy((*info)[i].acct_name, name);
365                 fstrcpy((*info)[i].acct_desc, gecos);
366                 (*info)[i].rid = rid;
367                 i++;
368         }
369
370         (*num_entries) = i;
371
372         status = NT_STATUS_OK;
373
374         DEBUG(3,("ads enum_dom_groups gave %d entries\n", (*num_entries)));
375
376 done:
377         if (res) 
378                 ads_msgfree(ads, res);
379
380         return status;
381 }
382
383 /* list all domain local groups */
384 static NTSTATUS enum_local_groups(struct winbindd_domain *domain,
385                                 TALLOC_CTX *mem_ctx,
386                                 uint32 *num_entries, 
387                                 struct acct_info **info)
388 {
389         /*
390          * This is a stub function only as we returned the domain 
391          * local groups in enum_dom_groups() if the domain->native field
392          * was true.  This is a simple performance optimization when
393          * using LDAP.
394          *
395          * if we ever need to enumerate domain local groups separately, 
396          * then this the optimization in enum_dom_groups() will need 
397          * to be split out
398          */
399         *num_entries = 0;
400         
401         return NT_STATUS_OK;
402 }
403
404 /* If you are looking for "dn_lookup": Yes, it used to be here!
405  * It has gone now since it was a major speed bottleneck in
406  * lookup_groupmem (its only use). It has been replaced by
407  * an rpc lookup sids call... R.I.P. */
408
409 /* Lookup user information from a rid */
410 static NTSTATUS query_user(struct winbindd_domain *domain, 
411                            TALLOC_CTX *mem_ctx, 
412                            const DOM_SID *sid, 
413                            WINBIND_USERINFO *info)
414 {
415         ADS_STRUCT *ads = NULL;
416         const char *attrs[] = { "*", NULL };
417         ADS_STATUS rc;
418         int count;
419         LDAPMessage *msg = NULL;
420         char *ldap_exp;
421         char *sidstr;
422         uint32 group_rid;
423         NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
424         NET_USER_INFO_3 *user;
425
426         DEBUG(3,("ads: query_user\n"));
427
428         info->homedir = NULL;
429         info->shell = NULL;
430         info->primary_gid = (gid_t)-1;
431
432         /* try netsamlogon cache first */
433                         
434         if ( (user = netsamlogon_cache_get( mem_ctx, sid )) != NULL ) 
435         {
436                                 
437                 DEBUG(5,("query_user: Cache lookup succeeded for %s\n", 
438                          sid_string_dbg(sid)));
439
440                 sid_compose(&info->user_sid, &domain->sid, user->user_rid);
441                 sid_compose(&info->group_sid, &domain->sid, user->group_rid);
442                                 
443                 info->acct_name = unistr2_to_ascii_talloc(mem_ctx, &user->uni_user_name);
444                 info->full_name = unistr2_to_ascii_talloc(mem_ctx, &user->uni_full_name);
445                 
446                 nss_get_info_cached( domain, sid, mem_ctx, NULL, NULL, 
447                               &info->homedir, &info->shell, &info->full_name, 
448                               &info->primary_gid );     
449
450                 TALLOC_FREE(user);
451                                 
452                 return NT_STATUS_OK;
453         }
454
455         if ( !winbindd_can_contact_domain(domain)) {
456                 DEBUG(8,("query_user: No incoming trust from domain %s\n",
457                          domain->name));
458
459                 /* We still need to generate some basic information
460                    about the user even if we cannot contact the 
461                    domain.  Most of this stuff we can deduce. */
462
463                 sid_copy( &info->user_sid, sid );
464
465                 /* Assume "Domain Users" for the primary group */
466
467                 sid_compose(&info->group_sid, &domain->sid, DOMAIN_GROUP_RID_USERS );
468
469                 /* Try to fill in what the nss_info backend can do */
470
471                 nss_get_info_cached( domain, sid, mem_ctx, NULL, NULL, 
472                               &info->homedir, &info->shell, &info->full_name, 
473                               &info->primary_gid );
474
475                 status = NT_STATUS_OK;
476                 goto done;
477         }
478
479         /* no cache...do the query */
480
481         if ( (ads = ads_cached_connection(domain)) == NULL ) {
482                 domain->last_status = NT_STATUS_SERVER_DISABLED;
483                 goto done;
484         }
485
486         sidstr = sid_binstring(sid);
487         asprintf(&ldap_exp, "(objectSid=%s)", sidstr);
488         rc = ads_search_retry(ads, &msg, ldap_exp, attrs);
489         free(ldap_exp);
490         free(sidstr);
491         if (!ADS_ERR_OK(rc) || !msg) {
492                 DEBUG(1,("query_user(sid=%s) ads_search: %s\n",
493                          sid_string_dbg(sid), ads_errstr(rc)));
494                 goto done;
495         }
496
497         count = ads_count_replies(ads, msg);
498         if (count != 1) {
499                 DEBUG(1,("query_user(sid=%s): Not found\n",
500                          sid_string_dbg(sid)));
501                 goto done;
502         }
503
504         info->acct_name = ads_pull_username(ads, mem_ctx, msg);
505
506         nss_get_info_cached( domain, sid, mem_ctx, ads, msg, 
507                       &info->homedir, &info->shell, &info->full_name, 
508                       &info->primary_gid );     
509
510         if (info->full_name == NULL) {
511                 info->full_name = ads_pull_string(ads, mem_ctx, msg, "name");
512         }
513
514         if (!ads_pull_uint32(ads, msg, "primaryGroupID", &group_rid)) {
515                 DEBUG(1,("No primary group for %s !?\n",
516                          sid_string_dbg(sid)));
517                 goto done;
518         }
519
520         sid_copy(&info->user_sid, sid);
521         sid_compose(&info->group_sid, &domain->sid, group_rid);
522
523         status = NT_STATUS_OK;
524
525         DEBUG(3,("ads query_user gave %s\n", info->acct_name));
526 done:
527         if (msg) 
528                 ads_msgfree(ads, msg);
529
530         return status;
531 }
532
533 /* Lookup groups a user is a member of - alternate method, for when
534    tokenGroups are not available. */
535 static NTSTATUS lookup_usergroups_member(struct winbindd_domain *domain,
536                                          TALLOC_CTX *mem_ctx,
537                                          const char *user_dn, 
538                                          DOM_SID *primary_group,
539                                          size_t *p_num_groups, DOM_SID **user_sids)
540 {
541         ADS_STATUS rc;
542         NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
543         int count;
544         LDAPMessage *res = NULL;
545         LDAPMessage *msg = NULL;
546         char *ldap_exp;
547         ADS_STRUCT *ads;
548         const char *group_attrs[] = {"objectSid", NULL};
549         char *escaped_dn;
550         size_t num_groups = 0;
551
552         DEBUG(3,("ads: lookup_usergroups_member\n"));
553
554         if ( !winbindd_can_contact_domain( domain ) ) {
555                 DEBUG(10,("lookup_usergroups_members: No incoming trust for domain %s\n",
556                           domain->name));               
557                 return NT_STATUS_OK;
558         }
559
560         ads = ads_cached_connection(domain);
561
562         if (!ads) {
563                 domain->last_status = NT_STATUS_SERVER_DISABLED;
564                 goto done;
565         }
566
567         if (!(escaped_dn = escape_ldap_string_alloc(user_dn))) {
568                 status = NT_STATUS_NO_MEMORY;
569                 goto done;
570         }
571
572         ldap_exp = talloc_asprintf(mem_ctx,
573                 "(&(member=%s)(objectCategory=group)(groupType:dn:%s:=%d))",
574                 escaped_dn,
575                 ADS_LDAP_MATCHING_RULE_BIT_AND,
576                 GROUP_TYPE_SECURITY_ENABLED);
577         if (!ldap_exp) {
578                 DEBUG(1,("lookup_usergroups(dn=%s) asprintf failed!\n", user_dn));
579                 SAFE_FREE(escaped_dn);
580                 status = NT_STATUS_NO_MEMORY;
581                 goto done;
582         }
583
584         SAFE_FREE(escaped_dn);
585
586         rc = ads_search_retry(ads, &res, ldap_exp, group_attrs);
587         
588         if (!ADS_ERR_OK(rc) || !res) {
589                 DEBUG(1,("lookup_usergroups ads_search member=%s: %s\n", user_dn, ads_errstr(rc)));
590                 return ads_ntstatus(rc);
591         }
592         
593         count = ads_count_replies(ads, res);
594         
595         *user_sids = NULL;
596         num_groups = 0;
597
598         /* always add the primary group to the sid array */
599         status = add_sid_to_array(mem_ctx, primary_group, user_sids,
600                                   &num_groups);
601         if (!NT_STATUS_IS_OK(status)) {
602                 goto done;
603         }
604
605         if (count > 0) {
606                 for (msg = ads_first_entry(ads, res); msg;
607                      msg = ads_next_entry(ads, msg)) {
608                         DOM_SID group_sid;
609                 
610                         if (!ads_pull_sid(ads, msg, "objectSid", &group_sid)) {
611                                 DEBUG(1,("No sid for this group ?!?\n"));
612                                 continue;
613                         }
614         
615                         /* ignore Builtin groups from ADS - Guenther */
616                         if (sid_check_is_in_builtin(&group_sid)) {
617                                 continue;
618                         }
619
620                         status = add_sid_to_array(mem_ctx, &group_sid,
621                                                   user_sids, &num_groups);
622                         if (!NT_STATUS_IS_OK(status)) {
623                                 goto done;
624                         }
625                 }
626
627         }
628
629         *p_num_groups = num_groups;
630         status = (user_sids != NULL) ? NT_STATUS_OK : NT_STATUS_NO_MEMORY;
631
632         DEBUG(3,("ads lookup_usergroups (member) succeeded for dn=%s\n", user_dn));
633 done:
634         if (res) 
635                 ads_msgfree(ads, res);
636
637         return status;
638 }
639
640 /* Lookup groups a user is a member of - alternate method, for when
641    tokenGroups are not available. */
642 static NTSTATUS lookup_usergroups_memberof(struct winbindd_domain *domain,
643                                            TALLOC_CTX *mem_ctx,
644                                            const char *user_dn, 
645                                            DOM_SID *primary_group,
646                                            size_t *p_num_groups, DOM_SID **user_sids)
647 {
648         ADS_STATUS rc;
649         NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
650         ADS_STRUCT *ads;
651         const char *attrs[] = {"memberOf", NULL};
652         size_t num_groups = 0;
653         DOM_SID *group_sids = NULL;
654         int i;
655         char **strings;
656         size_t num_strings = 0;
657
658
659         DEBUG(3,("ads: lookup_usergroups_memberof\n"));
660
661         if ( !winbindd_can_contact_domain( domain ) ) {
662                 DEBUG(10,("lookup_usergroups_memberof: No incoming trust for domain %s\n",
663                           domain->name));               
664                 return NT_STATUS_OK;
665         }
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         rc = ads_search_retry_extended_dn_ranged(ads, mem_ctx, user_dn, attrs, 
675                                                  ADS_EXTENDED_DN_HEX_STRING, 
676                                                  &strings, &num_strings);
677
678         if (!ADS_ERR_OK(rc)) {
679                 DEBUG(1,("lookup_usergroups_memberof ads_search member=%s: %s\n", 
680                         user_dn, ads_errstr(rc)));
681                 return ads_ntstatus(rc);
682         }
683         
684         *user_sids = NULL;
685         num_groups = 0;
686
687         /* always add the primary group to the sid array */
688         status = add_sid_to_array(mem_ctx, primary_group, user_sids,
689                                   &num_groups);
690         if (!NT_STATUS_IS_OK(status)) {
691                 goto done;
692         }
693
694         group_sids = TALLOC_ZERO_ARRAY(mem_ctx, DOM_SID, num_strings + 1);
695         if (!group_sids) {
696                 TALLOC_FREE(strings);
697                 status = NT_STATUS_NO_MEMORY;
698                 goto done;
699         }
700
701         for (i=0; i<num_strings; i++) {
702
703                 if (!ads_get_sid_from_extended_dn(mem_ctx, strings[i], 
704                                                   ADS_EXTENDED_DN_HEX_STRING, 
705                                                   &(group_sids)[i])) {
706                         TALLOC_FREE(group_sids);
707                         TALLOC_FREE(strings);
708                         status = NT_STATUS_NO_MEMORY;
709                         goto done;
710                 }
711         }
712
713         if (i == 0) {
714                 DEBUG(1,("No memberOf for this user?!?\n"));
715                 status = NT_STATUS_NO_MEMORY;
716                 goto done;
717         }
718
719         for (i=0; i<num_strings; i++) {
720
721                 /* ignore Builtin groups from ADS - Guenther */
722                 if (sid_check_is_in_builtin(&group_sids[i])) {
723                         continue;
724                 }
725
726                 status = add_sid_to_array(mem_ctx, &group_sids[i], user_sids,
727                                           &num_groups);
728                 if (!NT_STATUS_IS_OK(status)) {
729                         goto done;
730                 }
731         
732         }
733
734         *p_num_groups = num_groups;
735         status = (*user_sids != NULL) ? NT_STATUS_OK : NT_STATUS_NO_MEMORY;
736
737         DEBUG(3,("ads lookup_usergroups (memberof) succeeded for dn=%s\n", user_dn));
738 done:
739         TALLOC_FREE(group_sids);
740
741         return status;
742 }
743
744
745 /* Lookup groups a user is a member of. */
746 static NTSTATUS lookup_usergroups(struct winbindd_domain *domain,
747                                   TALLOC_CTX *mem_ctx,
748                                   const DOM_SID *sid, 
749                                   uint32 *p_num_groups, DOM_SID **user_sids)
750 {
751         ADS_STRUCT *ads = NULL;
752         const char *attrs[] = {"tokenGroups", "primaryGroupID", NULL};
753         ADS_STATUS rc;
754         int count;
755         LDAPMessage *msg = NULL;
756         char *user_dn = NULL;
757         DOM_SID *sids;
758         int i;
759         DOM_SID primary_group;
760         uint32 primary_group_rid;
761         NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
762         size_t num_groups = 0;
763
764         DEBUG(3,("ads: lookup_usergroups\n"));
765         *p_num_groups = 0;
766
767         status = lookup_usergroups_cached(domain, mem_ctx, sid, 
768                                           p_num_groups, user_sids);
769         if (NT_STATUS_IS_OK(status)) {
770                 return NT_STATUS_OK;
771         }
772
773         if ( !winbindd_can_contact_domain( domain ) ) {
774                 DEBUG(10,("lookup_usergroups: No incoming trust for domain %s\n",
775                           domain->name));
776
777                 /* Tell the cache manager not to remember this one */
778
779                 return NT_STATUS_SYNCHRONIZATION_REQUIRED;
780         }
781
782         ads = ads_cached_connection(domain);
783         
784         if (!ads) {
785                 domain->last_status = NT_STATUS_SERVER_DISABLED;
786                 status = NT_STATUS_SERVER_DISABLED;
787                 goto done;
788         }
789
790         rc = ads_search_retry_sid(ads, &msg, sid, attrs);
791
792         if (!ADS_ERR_OK(rc)) {
793                 status = ads_ntstatus(rc);
794                 DEBUG(1, ("lookup_usergroups(sid=%s) ads_search tokenGroups: "
795                           "%s\n", sid_string_dbg(sid), ads_errstr(rc)));
796                 goto done;
797         }
798         
799         count = ads_count_replies(ads, msg);
800         if (count != 1) {
801                 status = NT_STATUS_UNSUCCESSFUL;
802                 DEBUG(1,("lookup_usergroups(sid=%s) ads_search tokenGroups: "
803                          "invalid number of results (count=%d)\n", 
804                          sid_string_dbg(sid), count));
805                 goto done;
806         }
807
808         if (!msg) {
809                 DEBUG(1,("lookup_usergroups(sid=%s) ads_search tokenGroups: NULL msg\n", 
810                          sid_string_dbg(sid)));
811                 status = NT_STATUS_UNSUCCESSFUL;
812                 goto done;
813         }
814
815         user_dn = ads_get_dn(ads, msg);
816         if (user_dn == NULL) {
817                 status = NT_STATUS_NO_MEMORY;
818                 goto done;
819         }
820
821         if (!ads_pull_uint32(ads, msg, "primaryGroupID", &primary_group_rid)) {
822                 DEBUG(1,("%s: No primary group for sid=%s !?\n", 
823                          domain->name, sid_string_dbg(sid)));
824                 goto done;
825         }
826
827         sid_copy(&primary_group, &domain->sid);
828         sid_append_rid(&primary_group, primary_group_rid);
829
830         count = ads_pull_sids(ads, mem_ctx, msg, "tokenGroups", &sids);
831
832         /* there must always be at least one group in the token, 
833            unless we are talking to a buggy Win2k server */
834
835         /* actually this only happens when the machine account has no read
836          * permissions on the tokenGroup attribute - gd */
837
838         if (count == 0) {
839
840                 /* no tokenGroups */
841                 
842                 /* lookup what groups this user is a member of by DN search on
843                  * "memberOf" */
844
845                 status = lookup_usergroups_memberof(domain, mem_ctx, user_dn,
846                                                     &primary_group,
847                                                     &num_groups, user_sids);
848                 *p_num_groups = (uint32)num_groups;
849                 if (NT_STATUS_IS_OK(status)) {
850                         goto done;
851                 }
852
853                 /* lookup what groups this user is a member of by DN search on
854                  * "member" */
855
856                 status = lookup_usergroups_member(domain, mem_ctx, user_dn, 
857                                                   &primary_group,
858                                                   &num_groups, user_sids);
859                 *p_num_groups = (uint32)num_groups;
860                 goto done;
861         }
862
863         *user_sids = NULL;
864         num_groups = 0;
865
866         status = add_sid_to_array(mem_ctx, &primary_group, user_sids,
867                                   &num_groups);
868         if (!NT_STATUS_IS_OK(status)) {
869                 goto done;
870         }
871         
872         for (i=0;i<count;i++) {
873
874                 /* ignore Builtin groups from ADS - Guenther */
875                 if (sid_check_is_in_builtin(&sids[i])) {
876                         continue;
877                 }
878
879                 status = add_sid_to_array_unique(mem_ctx, &sids[i],
880                                                  user_sids, &num_groups);
881                 if (!NT_STATUS_IS_OK(status)) {
882                         goto done;
883                 }
884         }
885
886         *p_num_groups = (uint32)num_groups;
887         status = (*user_sids != NULL) ? NT_STATUS_OK : NT_STATUS_NO_MEMORY;
888
889         DEBUG(3,("ads lookup_usergroups (tokenGroups) succeeded for sid=%s\n",
890                  sid_string_dbg(sid)));
891 done:
892         ads_memfree(ads, user_dn);
893         ads_msgfree(ads, msg);
894         return status;
895 }
896
897 /*
898   find the members of a group, given a group rid and domain
899  */
900 static NTSTATUS lookup_groupmem(struct winbindd_domain *domain,
901                                 TALLOC_CTX *mem_ctx,
902                                 const DOM_SID *group_sid, uint32 *num_names, 
903                                 DOM_SID **sid_mem, char ***names, 
904                                 uint32 **name_types)
905 {
906         ADS_STATUS rc;
907         ADS_STRUCT *ads = NULL;
908         char *ldap_exp;
909         NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
910         char *sidbinstr;
911         char **members = NULL;
912         int i;
913         size_t num_members = 0;
914         ads_control args;
915         struct rpc_pipe_client *cli;
916         POLICY_HND lsa_policy;
917         DOM_SID *sid_mem_nocache = NULL;
918         char **names_nocache = NULL;
919         enum lsa_SidType *name_types_nocache = NULL;
920         char **domains_nocache = NULL;     /* only needed for rpccli_lsa_lookup_sids */
921         uint32 num_nocache = 0;
922         TALLOC_CTX *tmp_ctx = NULL;
923
924         DEBUG(10,("ads: lookup_groupmem %s sid=%s\n", domain->name, 
925                   sid_string_dbg(group_sid)));
926
927         *num_names = 0;
928
929         tmp_ctx = talloc_new(mem_ctx);
930         if (!tmp_ctx) {
931                 DEBUG(1, ("ads: lookup_groupmem: talloc failed\n"));
932                 status = NT_STATUS_NO_MEMORY;
933                 goto done;
934         }
935
936         if ( !winbindd_can_contact_domain( domain ) ) {
937                 DEBUG(10,("lookup_groupmem: No incoming trust for domain %s\n",
938                           domain->name));               
939                 return NT_STATUS_OK;
940         }
941
942         ads = ads_cached_connection(domain);
943         
944         if (!ads) {
945                 domain->last_status = NT_STATUS_SERVER_DISABLED;
946                 goto done;
947         }
948
949         if ((sidbinstr = sid_binstring(group_sid)) == NULL) {
950                 status = NT_STATUS_NO_MEMORY;
951                 goto done;
952         }
953
954         /* search for all members of the group */
955         if (!(ldap_exp = talloc_asprintf(tmp_ctx, "(objectSid=%s)", 
956                                          sidbinstr))) 
957         {
958                 SAFE_FREE(sidbinstr);
959                 DEBUG(1, ("ads: lookup_groupmem: talloc_asprintf for ldap_exp failed!\n"));
960                 status = NT_STATUS_NO_MEMORY;
961                 goto done;
962         }
963         SAFE_FREE(sidbinstr);
964
965         args.control = ADS_EXTENDED_DN_OID;
966         args.val = ADS_EXTENDED_DN_HEX_STRING;
967         args.critical = True;
968
969         rc = ads_ranged_search(ads, tmp_ctx, LDAP_SCOPE_SUBTREE, ads->config.bind_path, 
970                                ldap_exp, &args, "member", &members, &num_members);
971
972         if (!ADS_ERR_OK(rc)) {
973                 DEBUG(0,("ads_ranged_search failed with: %s\n", ads_errstr(rc)));
974                 status = NT_STATUS_UNSUCCESSFUL;
975                 goto done;
976         } 
977         
978         DEBUG(10, ("ads lookup_groupmem: got %d sids via extended dn call\n", (int)num_members));
979         
980         /* Now that we have a list of sids, we need to get the
981          * lists of names and name_types belonging to these sids.
982          * even though conceptually not quite clean,  we use the 
983          * RPC call lsa_lookup_sids for this since it can handle a 
984          * list of sids. ldap calls can just resolve one sid at a time.
985          *
986          * At this stage, the sids are still hidden in the exetended dn
987          * member output format. We actually do a little better than
988          * stated above: In extracting the sids from the member strings,
989          * we try to resolve as many sids as possible from the
990          * cache. Only the rest is passed to the lsa_lookup_sids call. */
991         
992         if (num_members) {
993                 (*sid_mem) = TALLOC_ZERO_ARRAY(mem_ctx, DOM_SID, num_members);
994                 (*names) = TALLOC_ZERO_ARRAY(mem_ctx, char *, num_members);
995                 (*name_types) = TALLOC_ZERO_ARRAY(mem_ctx, uint32, num_members);
996                 (sid_mem_nocache) = TALLOC_ZERO_ARRAY(tmp_ctx, DOM_SID, num_members);
997
998                 if ((members == NULL) || (*sid_mem == NULL) ||
999                     (*names == NULL) || (*name_types == NULL) ||
1000                     (sid_mem_nocache == NULL))
1001                 {
1002                         DEBUG(1, ("ads: lookup_groupmem: talloc failed\n"));
1003                         status = NT_STATUS_NO_MEMORY;
1004                         goto done;
1005                 }
1006         }
1007         else {
1008                 (*sid_mem) = NULL;
1009                 (*names) = NULL;
1010                 (*name_types) = NULL;
1011         }
1012
1013         for (i=0; i<num_members; i++) {
1014                 enum lsa_SidType name_type;
1015                 char *name, *domain_name;
1016                 DOM_SID sid;
1017
1018                 if (!ads_get_sid_from_extended_dn(tmp_ctx, members[i], args.val, &sid)) {
1019                         status = NT_STATUS_INVALID_PARAMETER;
1020                         goto done;
1021                 }
1022                 if (lookup_cached_sid(mem_ctx, &sid, &domain_name, &name, &name_type)) {
1023                         DEBUG(10,("ads: lookup_groupmem: got sid %s from "
1024                                   "cache\n", sid_string_dbg(&sid)));
1025                         sid_copy(&(*sid_mem)[*num_names], &sid);
1026                         (*names)[*num_names] = talloc_asprintf(*names, "%s%c%s",
1027                                                                domain_name,
1028                                                                *lp_winbind_separator(),
1029                                                                name );
1030
1031                         (*name_types)[*num_names] = name_type;
1032                         (*num_names)++;
1033                 }
1034                 else {
1035                         DEBUG(10, ("ads: lookup_groupmem: sid %s not found in "
1036                                    "cache\n", sid_string_dbg(&sid)));
1037                         sid_copy(&(sid_mem_nocache)[num_nocache], &sid);
1038                         num_nocache++;
1039                 }
1040         }
1041
1042         DEBUG(10, ("ads: lookup_groupmem: %d sids found in cache, "
1043                   "%d left for lsa_lookupsids\n", *num_names, num_nocache));
1044
1045         /* handle sids not resolved from cache by lsa_lookup_sids */
1046         if (num_nocache > 0) {
1047
1048                 status = cm_connect_lsa(domain, tmp_ctx, &cli, &lsa_policy);
1049
1050                 if (!NT_STATUS_IS_OK(status)) {
1051                         goto done;
1052                 }
1053
1054                 status = rpccli_lsa_lookup_sids(cli, tmp_ctx, 
1055                                                 &lsa_policy,
1056                                                 num_nocache, 
1057                                                 sid_mem_nocache, 
1058                                                 &domains_nocache, 
1059                                                 &names_nocache, 
1060                                                 &name_types_nocache);
1061
1062                 if (NT_STATUS_IS_OK(status) ||
1063                     NT_STATUS_EQUAL(status, STATUS_SOME_UNMAPPED)) 
1064                 {
1065                         /* Copy the entries over from the "_nocache" arrays 
1066                          * to the result arrays, skipping the gaps the 
1067                          * lookup_sids call left. */
1068                         for (i=0; i < num_nocache; i++) {
1069                                 if (((names_nocache)[i] != NULL) && 
1070                                     ((name_types_nocache)[i] != SID_NAME_UNKNOWN)) 
1071                                 {
1072                                         sid_copy(&(*sid_mem)[*num_names],
1073                                                  &sid_mem_nocache[i]);
1074                                         (*names)[*num_names] = talloc_asprintf( *names, 
1075                                                                                 "%s%c%s",
1076                                                                                 domains_nocache[i],
1077                                                                                 *lp_winbind_separator(),
1078                                                                                 names_nocache[i] );
1079                                         (*name_types)[*num_names] = name_types_nocache[i];
1080                                         (*num_names)++;
1081                                 }
1082                         }
1083                 }
1084                 else if (NT_STATUS_EQUAL(status, NT_STATUS_NONE_MAPPED)) {
1085                         DEBUG(10, ("lookup_groupmem: lsa_lookup_sids could "
1086                                    "not map any SIDs at all.\n"));
1087                         /* Don't handle this as an error here.
1088                          * There is nothing left to do with respect to the 
1089                          * overall result... */
1090                 }
1091                 else if (!NT_STATUS_IS_OK(status)) {
1092                         DEBUG(10, ("lookup_groupmem: Error looking up %d "
1093                                    "sids via rpc_lsa_lookup_sids: %s\n",
1094                                    (int)num_members, nt_errstr(status)));
1095                         goto done;
1096                 }
1097         }
1098
1099         status = NT_STATUS_OK;
1100         DEBUG(3,("ads lookup_groupmem for sid=%s succeeded\n",
1101                  sid_string_dbg(group_sid)));
1102
1103 done:
1104
1105         TALLOC_FREE(tmp_ctx);
1106
1107         return status;
1108 }
1109
1110 /* find the sequence number for a domain */
1111 static NTSTATUS sequence_number(struct winbindd_domain *domain, uint32 *seq)
1112 {
1113         ADS_STRUCT *ads = NULL;
1114         ADS_STATUS rc;
1115
1116         DEBUG(3,("ads: fetch sequence_number for %s\n", domain->name));
1117
1118         if ( !winbindd_can_contact_domain( domain ) ) {
1119                 DEBUG(10,("sequence: No incoming trust for domain %s\n",
1120                           domain->name));
1121                 *seq = time(NULL);              
1122                 return NT_STATUS_OK;
1123         }
1124
1125         *seq = DOM_SEQUENCE_NONE;
1126
1127         ads = ads_cached_connection(domain);
1128         
1129         if (!ads) {
1130                 domain->last_status = NT_STATUS_SERVER_DISABLED;
1131                 return NT_STATUS_UNSUCCESSFUL;
1132         }
1133
1134         rc = ads_USN(ads, seq);
1135         
1136         if (!ADS_ERR_OK(rc)) {
1137         
1138                 /* its a dead connection, destroy it */
1139
1140                 if (domain->private_data) {
1141                         ads = (ADS_STRUCT *)domain->private_data;
1142                         ads->is_mine = True;
1143                         ads_destroy(&ads);
1144                         ads_kdestroy("MEMORY:winbind_ccache");
1145                         domain->private_data = NULL;
1146                 }
1147         }
1148         return ads_ntstatus(rc);
1149 }
1150
1151 /* get a list of trusted domains */
1152 static NTSTATUS trusted_domains(struct winbindd_domain *domain,
1153                                 TALLOC_CTX *mem_ctx,
1154                                 uint32 *num_domains,
1155                                 char ***names,
1156                                 char ***alt_names,
1157                                 DOM_SID **dom_sids)
1158 {
1159         NTSTATUS                result = NT_STATUS_UNSUCCESSFUL;
1160         struct netr_DomainTrustList trusts;
1161         int                     i;
1162         uint32                  flags;  
1163         struct rpc_pipe_client *cli;
1164         uint32                 fr_flags = (NETR_TRUST_FLAG_IN_FOREST | NETR_TRUST_FLAG_TREEROOT);
1165         int ret_count;
1166         
1167         DEBUG(3,("ads: trusted_domains\n"));
1168
1169         *num_domains = 0;
1170         *alt_names   = NULL;
1171         *names       = NULL;
1172         *dom_sids    = NULL;
1173
1174         /* If this is our primary domain or a root in our forest,
1175            query for all trusts.  If not, then just look for domain
1176            trusts in the target forest */
1177
1178         if ( domain->primary ||
1179                 ((domain->domain_flags&fr_flags) == fr_flags) ) 
1180         {
1181                 flags = NETR_TRUST_FLAG_OUTBOUND |
1182                         NETR_TRUST_FLAG_INBOUND |
1183                         NETR_TRUST_FLAG_IN_FOREST;
1184         } else {
1185                 flags = NETR_TRUST_FLAG_IN_FOREST;
1186         }       
1187
1188         result = cm_connect_netlogon(domain, &cli);
1189
1190         if (!NT_STATUS_IS_OK(result)) {
1191                 DEBUG(5, ("trusted_domains: Could not open a connection to %s "
1192                           "for PIPE_NETLOGON (%s)\n", 
1193                           domain->name, nt_errstr(result)));
1194                 return NT_STATUS_UNSUCCESSFUL;
1195         }
1196
1197         result = rpccli_netr_DsrEnumerateDomainTrusts(cli, mem_ctx,
1198                                                       cli->cli->desthost,
1199                                                       flags,
1200                                                       &trusts,
1201                                                       NULL);
1202         if ( NT_STATUS_IS_OK(result) && trusts.count) {
1203
1204                 /* Allocate memory for trusted domain names and sids */
1205
1206                 if ( !(*names = TALLOC_ARRAY(mem_ctx, char *, trusts.count)) ) {
1207                         DEBUG(0, ("trusted_domains: out of memory\n"));
1208                         return NT_STATUS_NO_MEMORY;
1209                 }
1210
1211                 if ( !(*alt_names = TALLOC_ARRAY(mem_ctx, char *, trusts.count)) ) {
1212                         DEBUG(0, ("trusted_domains: out of memory\n"));
1213                         return NT_STATUS_NO_MEMORY;
1214                 }
1215
1216                 if ( !(*dom_sids = TALLOC_ARRAY(mem_ctx, DOM_SID, trusts.count)) ) {
1217                         DEBUG(0, ("trusted_domains: out of memory\n"));
1218                         return NT_STATUS_NO_MEMORY;
1219                 }
1220
1221                 /* Copy across names and sids */
1222
1223
1224                 ret_count = 0;          
1225                 for (i = 0; i < trusts.count; i++) {
1226                         struct winbindd_domain d;
1227                         
1228                         /* drop external trusts if this is not our primary 
1229                            domain.  This means that the returned number of 
1230                            domains may be less that the ones actually trusted
1231                            by the DC. */
1232
1233                         if ( (trusts.array[i].trust_attributes == NETR_TRUST_ATTRIBUTE_QUARANTINED_DOMAIN) &&
1234                              !domain->primary ) 
1235                         {
1236                                 DEBUG(10,("trusted_domains: Skipping external trusted domain "
1237                                           "%s because it is outside of our primary domain\n",
1238                                           trusts.array[i].netbios_name));
1239                                 continue;                               
1240                         }
1241                         
1242                         (*names)[ret_count] = CONST_DISCARD(char *, trusts.array[i].netbios_name);
1243                         (*alt_names)[ret_count] = CONST_DISCARD(char *, trusts.array[i].dns_name);
1244                         sid_copy(&(*dom_sids)[ret_count], trusts.array[i].sid);
1245
1246                         /* add to the trusted domain cache */
1247
1248                         fstrcpy( d.name,  trusts.array[i].netbios_name);
1249                         fstrcpy( d.alt_name, trusts.array[i].dns_name);
1250                         sid_copy( &d.sid, trusts.array[i].sid);
1251
1252                         /* This gets a little tricky.  If we are
1253                            following a transitive forest trust, then
1254                            innerit the flags, type, and attrins from
1255                            the domain we queried to make sure we don't
1256                            record the view of the trust from the wrong
1257                            side.  Always view it from the side of our
1258                            primary domain.   --jerry */
1259                         if ( domain->primary ||
1260                              ((domain->domain_flags&fr_flags) == fr_flags) ) 
1261                         {
1262                                 DEBUG(10,("trusted_domains(ads):  Storing trust "
1263                                           "flags for domain %s\n", d.alt_name));
1264
1265                                 /* Look this up in cache to make sure
1266                                    we have the current trust flags and
1267                                    attributes */
1268
1269                                 d.domain_flags = trusts.array[i].trust_flags;
1270                                 d.domain_type = trusts.array[i].trust_type;
1271                                 d.domain_trust_attribs = trusts.array[i].trust_attributes;
1272                         } else {
1273                                 /* Look up the record in the cache */
1274                                 struct winbindd_tdc_domain *parent;
1275
1276                                 DEBUG(10,("trusted_domains(ads):  Inheriting trust "
1277                                           "flags for domain %s\n", d.alt_name));                                
1278
1279                                 parent = wcache_tdc_fetch_domain(NULL, domain->name);
1280                                 if (parent) {
1281                                         d.domain_flags = parent->trust_flags;
1282                                         d.domain_type  = parent->trust_type;
1283                                         d.domain_trust_attribs = parent->trust_attribs;
1284                                 } else {
1285                                 d.domain_flags = domain->domain_flags;                          
1286                                 d.domain_type  = domain->domain_type;
1287                                 d.domain_trust_attribs = domain->domain_trust_attribs;
1288                         }
1289                                 TALLOC_FREE(parent);
1290                         }
1291
1292                         wcache_tdc_add_domain( &d );
1293
1294                         ret_count++;
1295
1296                 }
1297
1298                 *num_domains = ret_count;       
1299         }
1300
1301         return result;
1302 }
1303
1304 /* the ADS backend methods are exposed via this structure */
1305 struct winbindd_methods ads_methods = {
1306         True,
1307         query_user_list,
1308         enum_dom_groups,
1309         enum_local_groups,
1310         msrpc_name_to_sid,
1311         msrpc_sid_to_name,
1312         msrpc_rids_to_names,
1313         query_user,
1314         lookup_usergroups,
1315         msrpc_lookup_useraliases,
1316         lookup_groupmem,
1317         sequence_number,
1318         msrpc_lockout_policy,
1319         msrpc_password_policy,
1320         trusted_domains,
1321 };
1322
1323 #endif