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