Merge branch 'v3-2-test' of ssh://jra@git.samba.org/data/git/samba into v3-2-test
[ira/wip.git] / source3 / 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         if (!add_sid_to_array(mem_ctx, primary_group, user_sids, &num_groups)) {
600                 status = NT_STATUS_NO_MEMORY;
601                 goto done;
602         }
603
604         if (count > 0) {
605                 for (msg = ads_first_entry(ads, res); msg;
606                      msg = ads_next_entry(ads, msg)) {
607                         DOM_SID group_sid;
608                 
609                         if (!ads_pull_sid(ads, msg, "objectSid", &group_sid)) {
610                                 DEBUG(1,("No sid for this group ?!?\n"));
611                                 continue;
612                         }
613         
614                         /* ignore Builtin groups from ADS - Guenther */
615                         if (sid_check_is_in_builtin(&group_sid)) {
616                                 continue;
617                         }
618                                
619                         if (!add_sid_to_array(mem_ctx, &group_sid, user_sids,
620                                          &num_groups)) {
621                                 status = NT_STATUS_NO_MEMORY;
622                                 goto done;
623                         }
624                 }
625
626         }
627
628         *p_num_groups = num_groups;
629         status = (user_sids != NULL) ? NT_STATUS_OK : NT_STATUS_NO_MEMORY;
630
631         DEBUG(3,("ads lookup_usergroups (member) succeeded for dn=%s\n", user_dn));
632 done:
633         if (res) 
634                 ads_msgfree(ads, res);
635
636         return status;
637 }
638
639 /* Lookup groups a user is a member of - alternate method, for when
640    tokenGroups are not available. */
641 static NTSTATUS lookup_usergroups_memberof(struct winbindd_domain *domain,
642                                            TALLOC_CTX *mem_ctx,
643                                            const char *user_dn, 
644                                            DOM_SID *primary_group,
645                                            size_t *p_num_groups, DOM_SID **user_sids)
646 {
647         ADS_STATUS rc;
648         NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
649         ADS_STRUCT *ads;
650         const char *attrs[] = {"memberOf", NULL};
651         size_t num_groups = 0;
652         DOM_SID *group_sids = NULL;
653         int i;
654         char **strings;
655         size_t num_strings = 0;
656
657
658         DEBUG(3,("ads: lookup_usergroups_memberof\n"));
659
660         if ( !winbindd_can_contact_domain( domain ) ) {
661                 DEBUG(10,("lookup_usergroups_memberof: No incoming trust for domain %s\n",
662                           domain->name));               
663                 return NT_STATUS_OK;
664         }
665
666         ads = ads_cached_connection(domain);
667
668         if (!ads) {
669                 domain->last_status = NT_STATUS_SERVER_DISABLED;
670                 goto done;
671         }
672
673         rc = ads_search_retry_extended_dn_ranged(ads, mem_ctx, user_dn, attrs, 
674                                                  ADS_EXTENDED_DN_HEX_STRING, 
675                                                  &strings, &num_strings);
676
677         if (!ADS_ERR_OK(rc)) {
678                 DEBUG(1,("lookup_usergroups_memberof ads_search member=%s: %s\n", 
679                         user_dn, ads_errstr(rc)));
680                 return ads_ntstatus(rc);
681         }
682         
683         *user_sids = NULL;
684         num_groups = 0;
685
686         /* always add the primary group to the sid array */
687         if (!add_sid_to_array(mem_ctx, primary_group, user_sids, &num_groups)) {
688                 status = NT_STATUS_NO_MEMORY;
689                 goto done;
690         }
691
692         group_sids = TALLOC_ZERO_ARRAY(mem_ctx, DOM_SID, num_strings + 1);
693         if (!group_sids) {
694                 TALLOC_FREE(strings);
695                 status = NT_STATUS_NO_MEMORY;
696                 goto done;
697         }
698
699         for (i=0; i<num_strings; i++) {
700
701                 if (!ads_get_sid_from_extended_dn(mem_ctx, strings[i], 
702                                                   ADS_EXTENDED_DN_HEX_STRING, 
703                                                   &(group_sids)[i])) {
704                         TALLOC_FREE(group_sids);
705                         TALLOC_FREE(strings);
706                         status = NT_STATUS_NO_MEMORY;
707                         goto done;
708                 }
709         }
710
711         if (i == 0) {
712                 DEBUG(1,("No memberOf for this user?!?\n"));
713                 status = NT_STATUS_NO_MEMORY;
714                 goto done;
715         }
716
717         for (i=0; i<num_strings; i++) {
718
719                 /* ignore Builtin groups from ADS - Guenther */
720                 if (sid_check_is_in_builtin(&group_sids[i])) {
721                         continue;
722                 }
723                        
724                 if (!add_sid_to_array(mem_ctx, &group_sids[i], user_sids,
725                                  &num_groups)) {
726                         status = NT_STATUS_NO_MEMORY;
727                         goto done;
728                 }
729         
730         }
731
732         *p_num_groups = num_groups;
733         status = (*user_sids != NULL) ? NT_STATUS_OK : NT_STATUS_NO_MEMORY;
734
735         DEBUG(3,("ads lookup_usergroups (memberof) succeeded for dn=%s\n", user_dn));
736 done:
737         TALLOC_FREE(group_sids);
738
739         return status;
740 }
741
742
743 /* Lookup groups a user is a member of. */
744 static NTSTATUS lookup_usergroups(struct winbindd_domain *domain,
745                                   TALLOC_CTX *mem_ctx,
746                                   const DOM_SID *sid, 
747                                   uint32 *p_num_groups, DOM_SID **user_sids)
748 {
749         ADS_STRUCT *ads = NULL;
750         const char *attrs[] = {"tokenGroups", "primaryGroupID", NULL};
751         ADS_STATUS rc;
752         int count;
753         LDAPMessage *msg = NULL;
754         char *user_dn = NULL;
755         DOM_SID *sids;
756         int i;
757         DOM_SID primary_group;
758         uint32 primary_group_rid;
759         NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
760         size_t num_groups = 0;
761
762         DEBUG(3,("ads: lookup_usergroups\n"));
763         *p_num_groups = 0;
764
765         status = lookup_usergroups_cached(domain, mem_ctx, sid, 
766                                           p_num_groups, user_sids);
767         if (NT_STATUS_IS_OK(status)) {
768                 return NT_STATUS_OK;
769         }
770
771         if ( !winbindd_can_contact_domain( domain ) ) {
772                 DEBUG(10,("lookup_usergroups: No incoming trust for domain %s\n",
773                           domain->name));
774
775                 /* Tell the cache manager not to remember this one */
776
777                 return NT_STATUS_SYNCHRONIZATION_REQUIRED;
778         }
779
780         ads = ads_cached_connection(domain);
781         
782         if (!ads) {
783                 domain->last_status = NT_STATUS_SERVER_DISABLED;
784                 status = NT_STATUS_SERVER_DISABLED;
785                 goto done;
786         }
787
788         rc = ads_search_retry_sid(ads, &msg, sid, attrs);
789
790         if (!ADS_ERR_OK(rc)) {
791                 status = ads_ntstatus(rc);
792                 DEBUG(1, ("lookup_usergroups(sid=%s) ads_search tokenGroups: "
793                           "%s\n", sid_string_dbg(sid), ads_errstr(rc)));
794                 goto done;
795         }
796         
797         count = ads_count_replies(ads, msg);
798         if (count != 1) {
799                 status = NT_STATUS_UNSUCCESSFUL;
800                 DEBUG(1,("lookup_usergroups(sid=%s) ads_search tokenGroups: "
801                          "invalid number of results (count=%d)\n", 
802                          sid_string_dbg(sid), count));
803                 goto done;
804         }
805
806         if (!msg) {
807                 DEBUG(1,("lookup_usergroups(sid=%s) ads_search tokenGroups: NULL msg\n", 
808                          sid_string_dbg(sid)));
809                 status = NT_STATUS_UNSUCCESSFUL;
810                 goto done;
811         }
812
813         user_dn = ads_get_dn(ads, msg);
814         if (user_dn == NULL) {
815                 status = NT_STATUS_NO_MEMORY;
816                 goto done;
817         }
818
819         if (!ads_pull_uint32(ads, msg, "primaryGroupID", &primary_group_rid)) {
820                 DEBUG(1,("%s: No primary group for sid=%s !?\n", 
821                          domain->name, sid_string_dbg(sid)));
822                 goto done;
823         }
824
825         sid_copy(&primary_group, &domain->sid);
826         sid_append_rid(&primary_group, primary_group_rid);
827
828         count = ads_pull_sids(ads, mem_ctx, msg, "tokenGroups", &sids);
829
830         /* there must always be at least one group in the token, 
831            unless we are talking to a buggy Win2k server */
832
833         /* actually this only happens when the machine account has no read
834          * permissions on the tokenGroup attribute - gd */
835
836         if (count == 0) {
837
838                 /* no tokenGroups */
839                 
840                 /* lookup what groups this user is a member of by DN search on
841                  * "memberOf" */
842
843                 status = lookup_usergroups_memberof(domain, mem_ctx, user_dn,
844                                                     &primary_group,
845                                                     &num_groups, user_sids);
846                 *p_num_groups = (uint32)num_groups;
847                 if (NT_STATUS_IS_OK(status)) {
848                         goto done;
849                 }
850
851                 /* lookup what groups this user is a member of by DN search on
852                  * "member" */
853
854                 status = lookup_usergroups_member(domain, mem_ctx, user_dn, 
855                                                   &primary_group,
856                                                   &num_groups, user_sids);
857                 *p_num_groups = (uint32)num_groups;
858                 goto done;
859         }
860
861         *user_sids = NULL;
862         num_groups = 0;
863
864         if (!add_sid_to_array(mem_ctx, &primary_group, user_sids, &num_groups)) {
865                 status = NT_STATUS_NO_MEMORY;
866                 goto done;
867         }
868         
869         for (i=0;i<count;i++) {
870
871                 /* ignore Builtin groups from ADS - Guenther */
872                 if (sid_check_is_in_builtin(&sids[i])) {
873                         continue;
874                 }
875                                
876                 if (!add_sid_to_array_unique(mem_ctx, &sids[i],
877                                         user_sids, &num_groups)) {
878                         status = NT_STATUS_NO_MEMORY;
879                         goto done;
880                 }
881         }
882
883         *p_num_groups = (uint32)num_groups;
884         status = (*user_sids != NULL) ? NT_STATUS_OK : NT_STATUS_NO_MEMORY;
885
886         DEBUG(3,("ads lookup_usergroups (tokenGroups) succeeded for sid=%s\n",
887                  sid_string_dbg(sid)));
888 done:
889         ads_memfree(ads, user_dn);
890         ads_msgfree(ads, msg);
891         return status;
892 }
893
894 /*
895   find the members of a group, given a group rid and domain
896  */
897 static NTSTATUS lookup_groupmem(struct winbindd_domain *domain,
898                                 TALLOC_CTX *mem_ctx,
899                                 const DOM_SID *group_sid, uint32 *num_names, 
900                                 DOM_SID **sid_mem, char ***names, 
901                                 uint32 **name_types)
902 {
903         ADS_STATUS rc;
904         ADS_STRUCT *ads = NULL;
905         char *ldap_exp;
906         NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
907         char *sidbinstr;
908         char **members = NULL;
909         int i;
910         size_t num_members = 0;
911         ads_control args;
912         struct rpc_pipe_client *cli;
913         POLICY_HND lsa_policy;
914         DOM_SID *sid_mem_nocache = NULL;
915         char **names_nocache = NULL;
916         enum lsa_SidType *name_types_nocache = NULL;
917         char **domains_nocache = NULL;     /* only needed for rpccli_lsa_lookup_sids */
918         uint32 num_nocache = 0;
919         TALLOC_CTX *tmp_ctx = NULL;
920
921         DEBUG(10,("ads: lookup_groupmem %s sid=%s\n", domain->name, 
922                   sid_string_dbg(group_sid)));
923
924         *num_names = 0;
925
926         tmp_ctx = talloc_new(mem_ctx);
927         if (!tmp_ctx) {
928                 DEBUG(1, ("ads: lookup_groupmem: talloc failed\n"));
929                 status = NT_STATUS_NO_MEMORY;
930                 goto done;
931         }
932
933         if ( !winbindd_can_contact_domain( domain ) ) {
934                 DEBUG(10,("lookup_groupmem: No incoming trust for domain %s\n",
935                           domain->name));               
936                 return NT_STATUS_OK;
937         }
938
939         ads = ads_cached_connection(domain);
940         
941         if (!ads) {
942                 domain->last_status = NT_STATUS_SERVER_DISABLED;
943                 goto done;
944         }
945
946         if ((sidbinstr = sid_binstring(group_sid)) == NULL) {
947                 status = NT_STATUS_NO_MEMORY;
948                 goto done;
949         }
950
951         /* search for all members of the group */
952         if (!(ldap_exp = talloc_asprintf(tmp_ctx, "(objectSid=%s)", 
953                                          sidbinstr))) 
954         {
955                 SAFE_FREE(sidbinstr);
956                 DEBUG(1, ("ads: lookup_groupmem: talloc_asprintf for ldap_exp failed!\n"));
957                 status = NT_STATUS_NO_MEMORY;
958                 goto done;
959         }
960         SAFE_FREE(sidbinstr);
961
962         args.control = ADS_EXTENDED_DN_OID;
963         args.val = ADS_EXTENDED_DN_HEX_STRING;
964         args.critical = True;
965
966         rc = ads_ranged_search(ads, tmp_ctx, LDAP_SCOPE_SUBTREE, ads->config.bind_path, 
967                                ldap_exp, &args, "member", &members, &num_members);
968
969         if (!ADS_ERR_OK(rc)) {
970                 DEBUG(0,("ads_ranged_search failed with: %s\n", ads_errstr(rc)));
971                 status = NT_STATUS_UNSUCCESSFUL;
972                 goto done;
973         } 
974         
975         DEBUG(10, ("ads lookup_groupmem: got %d sids via extended dn call\n", (int)num_members));
976         
977         /* Now that we have a list of sids, we need to get the
978          * lists of names and name_types belonging to these sids.
979          * even though conceptually not quite clean,  we use the 
980          * RPC call lsa_lookup_sids for this since it can handle a 
981          * list of sids. ldap calls can just resolve one sid at a time.
982          *
983          * At this stage, the sids are still hidden in the exetended dn
984          * member output format. We actually do a little better than
985          * stated above: In extracting the sids from the member strings,
986          * we try to resolve as many sids as possible from the
987          * cache. Only the rest is passed to the lsa_lookup_sids call. */
988         
989         if (num_members) {
990                 (*sid_mem) = TALLOC_ZERO_ARRAY(mem_ctx, DOM_SID, num_members);
991                 (*names) = TALLOC_ZERO_ARRAY(mem_ctx, char *, num_members);
992                 (*name_types) = TALLOC_ZERO_ARRAY(mem_ctx, uint32, num_members);
993                 (sid_mem_nocache) = TALLOC_ZERO_ARRAY(tmp_ctx, DOM_SID, num_members);
994
995                 if ((members == NULL) || (*sid_mem == NULL) ||
996                     (*names == NULL) || (*name_types == NULL) ||
997                     (sid_mem_nocache == NULL))
998                 {
999                         DEBUG(1, ("ads: lookup_groupmem: talloc failed\n"));
1000                         status = NT_STATUS_NO_MEMORY;
1001                         goto done;
1002                 }
1003         }
1004         else {
1005                 (*sid_mem) = NULL;
1006                 (*names) = NULL;
1007                 (*name_types) = NULL;
1008         }
1009
1010         for (i=0; i<num_members; i++) {
1011                 enum lsa_SidType name_type;
1012                 char *name, *domain_name;
1013                 DOM_SID sid;
1014
1015                 if (!ads_get_sid_from_extended_dn(tmp_ctx, members[i], args.val, &sid)) {
1016                         status = NT_STATUS_INVALID_PARAMETER;
1017                         goto done;
1018                 }
1019                 if (lookup_cached_sid(mem_ctx, &sid, &domain_name, &name, &name_type)) {
1020                         DEBUG(10,("ads: lookup_groupmem: got sid %s from "
1021                                   "cache\n", sid_string_dbg(&sid)));
1022                         sid_copy(&(*sid_mem)[*num_names], &sid);
1023                         (*names)[*num_names] = talloc_asprintf(*names, "%s%c%s",
1024                                                                domain_name,
1025                                                                *lp_winbind_separator(),
1026                                                                name );
1027
1028                         (*name_types)[*num_names] = name_type;
1029                         (*num_names)++;
1030                 }
1031                 else {
1032                         DEBUG(10, ("ads: lookup_groupmem: sid %s not found in "
1033                                    "cache\n", sid_string_dbg(&sid)));
1034                         sid_copy(&(sid_mem_nocache)[num_nocache], &sid);
1035                         num_nocache++;
1036                 }
1037         }
1038
1039         DEBUG(10, ("ads: lookup_groupmem: %d sids found in cache, "
1040                   "%d left for lsa_lookupsids\n", *num_names, num_nocache));
1041
1042         /* handle sids not resolved from cache by lsa_lookup_sids */
1043         if (num_nocache > 0) {
1044
1045                 status = cm_connect_lsa(domain, tmp_ctx, &cli, &lsa_policy);
1046
1047                 if (!NT_STATUS_IS_OK(status)) {
1048                         goto done;
1049                 }
1050
1051                 status = rpccli_lsa_lookup_sids(cli, tmp_ctx, 
1052                                                 &lsa_policy,
1053                                                 num_nocache, 
1054                                                 sid_mem_nocache, 
1055                                                 &domains_nocache, 
1056                                                 &names_nocache, 
1057                                                 &name_types_nocache);
1058
1059                 if (NT_STATUS_IS_OK(status) ||
1060                     NT_STATUS_EQUAL(status, STATUS_SOME_UNMAPPED)) 
1061                 {
1062                         /* Copy the entries over from the "_nocache" arrays 
1063                          * to the result arrays, skipping the gaps the 
1064                          * lookup_sids call left. */
1065                         for (i=0; i < num_nocache; i++) {
1066                                 if (((names_nocache)[i] != NULL) && 
1067                                     ((name_types_nocache)[i] != SID_NAME_UNKNOWN)) 
1068                                 {
1069                                         sid_copy(&(*sid_mem)[*num_names],
1070                                                  &sid_mem_nocache[i]);
1071                                         (*names)[*num_names] = talloc_asprintf( *names, 
1072                                                                                 "%s%c%s",
1073                                                                                 domains_nocache[i],
1074                                                                                 *lp_winbind_separator(),
1075                                                                                 names_nocache[i] );
1076                                         (*name_types)[*num_names] = name_types_nocache[i];
1077                                         (*num_names)++;
1078                                 }
1079                         }
1080                 }
1081                 else if (NT_STATUS_EQUAL(status, NT_STATUS_NONE_MAPPED)) {
1082                         DEBUG(10, ("lookup_groupmem: lsa_lookup_sids could "
1083                                    "not map any SIDs at all.\n"));
1084                         /* Don't handle this as an error here.
1085                          * There is nothing left to do with respect to the 
1086                          * overall result... */
1087                 }
1088                 else if (!NT_STATUS_IS_OK(status)) {
1089                         DEBUG(10, ("lookup_groupmem: Error looking up %d "
1090                                    "sids via rpc_lsa_lookup_sids: %s\n",
1091                                    (int)num_members, nt_errstr(status)));
1092                         goto done;
1093                 }
1094         }
1095
1096         status = NT_STATUS_OK;
1097         DEBUG(3,("ads lookup_groupmem for sid=%s succeeded\n",
1098                  sid_string_dbg(group_sid)));
1099
1100 done:
1101
1102         TALLOC_FREE(tmp_ctx);
1103
1104         return status;
1105 }
1106
1107 /* find the sequence number for a domain */
1108 static NTSTATUS sequence_number(struct winbindd_domain *domain, uint32 *seq)
1109 {
1110         ADS_STRUCT *ads = NULL;
1111         ADS_STATUS rc;
1112
1113         DEBUG(3,("ads: fetch sequence_number for %s\n", domain->name));
1114
1115         if ( !winbindd_can_contact_domain( domain ) ) {
1116                 DEBUG(10,("sequence: No incoming trust for domain %s\n",
1117                           domain->name));
1118                 *seq = time(NULL);              
1119                 return NT_STATUS_OK;
1120         }
1121
1122         *seq = DOM_SEQUENCE_NONE;
1123
1124         ads = ads_cached_connection(domain);
1125         
1126         if (!ads) {
1127                 domain->last_status = NT_STATUS_SERVER_DISABLED;
1128                 return NT_STATUS_UNSUCCESSFUL;
1129         }
1130
1131         rc = ads_USN(ads, seq);
1132         
1133         if (!ADS_ERR_OK(rc)) {
1134         
1135                 /* its a dead connection, destroy it */
1136
1137                 if (domain->private_data) {
1138                         ads = (ADS_STRUCT *)domain->private_data;
1139                         ads->is_mine = True;
1140                         ads_destroy(&ads);
1141                         ads_kdestroy("MEMORY:winbind_ccache");
1142                         domain->private_data = NULL;
1143                 }
1144         }
1145         return ads_ntstatus(rc);
1146 }
1147
1148 /* get a list of trusted domains */
1149 static NTSTATUS trusted_domains(struct winbindd_domain *domain,
1150                                 TALLOC_CTX *mem_ctx,
1151                                 uint32 *num_domains,
1152                                 char ***names,
1153                                 char ***alt_names,
1154                                 DOM_SID **dom_sids)
1155 {
1156         NTSTATUS                result = NT_STATUS_UNSUCCESSFUL;
1157         struct ds_domain_trust  *domains = NULL;
1158         int                     count = 0;
1159         int                     i;
1160         uint32                  flags;  
1161         struct rpc_pipe_client *cli;
1162         uint32                 fr_flags = (DS_DOMAIN_IN_FOREST | DS_DOMAIN_TREE_ROOT);  
1163         int ret_count;
1164         
1165         DEBUG(3,("ads: trusted_domains\n"));
1166
1167         *num_domains = 0;
1168         *alt_names   = NULL;
1169         *names       = NULL;
1170         *dom_sids    = NULL;
1171
1172         /* If this is our primary domain or a root in our forest,
1173            query for all trusts.  If not, then just look for domain
1174            trusts in the target forest */
1175
1176         if ( domain->primary ||
1177                 ((domain->domain_flags&fr_flags) == fr_flags) ) 
1178         {
1179                 flags = DS_DOMAIN_DIRECT_OUTBOUND | 
1180                         DS_DOMAIN_DIRECT_INBOUND | 
1181                         DS_DOMAIN_IN_FOREST;
1182         } else {
1183                 flags = DS_DOMAIN_IN_FOREST;
1184         }       
1185
1186         result = cm_connect_netlogon(domain, &cli);
1187
1188         if (!NT_STATUS_IS_OK(result)) {
1189                 DEBUG(5, ("trusted_domains: Could not open a connection to %s "
1190                           "for PIPE_NETLOGON (%s)\n", 
1191                           domain->name, nt_errstr(result)));
1192                 return NT_STATUS_UNSUCCESSFUL;
1193         }
1194         
1195         if ( NT_STATUS_IS_OK(result) ) {
1196                 result = rpccli_ds_enum_domain_trusts(cli, mem_ctx,
1197                                                       cli->cli->desthost, 
1198                                                       flags, &domains,
1199                                                       (unsigned int *)&count);
1200         }
1201         
1202         if ( NT_STATUS_IS_OK(result) && count) {
1203
1204                 /* Allocate memory for trusted domain names and sids */
1205
1206                 if ( !(*names = TALLOC_ARRAY(mem_ctx, char *, 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 *, 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, 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 < 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 ( (domains[i].trust_attributes == DS_DOMAIN_TRUST_ATTRIB_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                                           domains[i].netbios_domain));                          
1239                                 continue;                               
1240                         }
1241                         
1242                         (*names)[ret_count] = domains[i].netbios_domain;
1243                         (*alt_names)[ret_count] = domains[i].dns_domain;
1244                         sid_copy(&(*dom_sids)[ret_count], &domains[i].sid);
1245
1246                         /* add to the trusted domain cache */
1247
1248                         fstrcpy( d.name,  domains[i].netbios_domain );
1249                         fstrcpy( d.alt_name, domains[i].dns_domain );                   
1250                         sid_copy( &d.sid, &domains[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 = domains[i].flags;
1270                                 d.domain_type = domains[i].trust_type;
1271                                 d.domain_trust_attribs = domains[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