r23471: Here's a rough patch for expanding domain group membership
[sfrench/samba-autobuild/.git] / source / nsswitch / winbindd_ads.c
1 /*
2    Unix SMB/CIFS implementation.
3
4    Winbind ADS backend functions
5
6    Copyright (C) Andrew Tridgell 2001
7    Copyright (C) Andrew Bartlett <abartlet@samba.org> 2003
8    Copyright (C) Gerald (Jerry) Carter 2004
9    
10    This program is free software; you can redistribute it and/or modify
11    it under the terms of the GNU General Public License as published by
12    the Free Software Foundation; either version 2 of the License, or
13    (at your option) any later version.
14    
15    This program is distributed in the hope that it will be useful,
16    but WITHOUT ANY WARRANTY; without even the implied warranty of
17    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18    GNU General Public License for more details.
19    
20    You should have received a copy of the GNU General Public License
21    along with this program; if not, write to the Free Software
22    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
23 */
24
25 #include "includes.h"
26 #include "winbindd.h"
27
28 #ifdef HAVE_ADS
29
30 #undef DBGC_CLASS
31 #define DBGC_CLASS DBGC_WINBIND
32
33 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         ADS_STRUCT *ads = NULL;
902         char *ldap_exp;
903         NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
904         char *sidbinstr;
905         char **members = NULL;
906         int i;
907         size_t num_members = 0;
908         ads_control args;
909         struct rpc_pipe_client *cli;
910         POLICY_HND lsa_policy;
911         DOM_SID *sid_mem_nocache = NULL;
912         char **names_nocache = NULL;
913         uint32 *name_types_nocache = NULL;
914         char **domains_nocache = NULL;     /* only needed for rpccli_lsa_lookup_sids */
915         uint32 num_nocache = 0;
916         TALLOC_CTX *tmp_ctx = NULL;
917
918         DEBUG(10,("ads: lookup_groupmem %s sid=%s\n", domain->name, 
919                   sid_string_static(group_sid)));
920
921         *num_names = 0;
922
923         tmp_ctx = talloc_new(mem_ctx);
924         if (!tmp_ctx) {
925                 DEBUG(1, ("ads: lookup_groupmem: talloc failed\n"));
926                 status = NT_STATUS_NO_MEMORY;
927                 goto done;
928         }
929
930         if ( !winbindd_can_contact_domain( domain ) ) {
931                 DEBUG(10,("lookup_groupmem: No incoming trust for domain %s\n",
932                           domain->name));               
933                 return NT_STATUS_OK;
934         }
935
936         ads = ads_cached_connection(domain);
937         
938         if (!ads) {
939                 domain->last_status = NT_STATUS_SERVER_DISABLED;
940                 goto done;
941         }
942
943         if ((sidbinstr = sid_binstring(group_sid)) == NULL) {
944                 status = NT_STATUS_NO_MEMORY;
945                 goto done;
946         }
947
948         /* search for all members of the group */
949         if (!(ldap_exp = talloc_asprintf(tmp_ctx, "(objectSid=%s)", 
950                                          sidbinstr))) 
951         {
952                 SAFE_FREE(sidbinstr);
953                 DEBUG(1, ("ads: lookup_groupmem: talloc_asprintf for ldap_exp failed!\n"));
954                 status = NT_STATUS_NO_MEMORY;
955                 goto done;
956         }
957         SAFE_FREE(sidbinstr);
958
959         args.control = ADS_EXTENDED_DN_OID;
960         args.val = ADS_EXTENDED_DN_HEX_STRING;
961         args.critical = True;
962
963         rc = ads_ranged_search(ads, tmp_ctx, LDAP_SCOPE_SUBTREE, ads->config.bind_path, 
964                                ldap_exp, &args, "member", &members, &num_members);
965
966         if (!ADS_ERR_OK(rc)) {
967                 DEBUG(0,("ads_ranged_search failed with: %s\n", ads_errstr(rc)));
968                 status = NT_STATUS_UNSUCCESSFUL;
969                 goto done;
970         } 
971         
972         DEBUG(10, ("ads lookup_groupmem: got %d sids via extended dn call\n", (int)num_members));
973         
974         /* Now that we have a list of sids, we need to get the
975          * lists of names and name_types belonging to these sids.
976          * even though conceptually not quite clean,  we use the 
977          * RPC call lsa_lookup_sids for this since it can handle a 
978          * list of sids. ldap calls can just resolve one sid at a time.
979          *
980          * At this stage, the sids are still hidden in the exetended dn
981          * member output format. We actually do a little better than
982          * stated above: In extracting the sids from the member strings,
983          * we try to resolve as many sids as possible from the
984          * cache. Only the rest is passed to the lsa_lookup_sids call. */
985         
986         if (num_members) {
987                 (*sid_mem) = TALLOC_ZERO_ARRAY(mem_ctx, DOM_SID, num_members);
988                 (*names) = TALLOC_ZERO_ARRAY(mem_ctx, char *, num_members);
989                 (*name_types) = TALLOC_ZERO_ARRAY(mem_ctx, uint32, num_members);
990                 (sid_mem_nocache) = TALLOC_ZERO_ARRAY(tmp_ctx, DOM_SID, num_members);
991
992                 if ((members == NULL) || (*sid_mem == NULL) ||
993                     (*names == NULL) || (*name_types == NULL) ||
994                     (sid_mem_nocache == NULL))
995                 {
996                         DEBUG(1, ("ads: lookup_groupmem: talloc failed\n"));
997                         status = NT_STATUS_NO_MEMORY;
998                         goto done;
999                 }
1000         }
1001         else {
1002                 (*sid_mem) = NULL;
1003                 (*names) = NULL;
1004                 (*name_types) = NULL;
1005         }
1006
1007         for (i=0; i<num_members; i++) {
1008                 uint32 name_type;
1009                 char *name, *domain_name;
1010                 DOM_SID sid;
1011
1012                 if (!ads_get_sid_from_extended_dn(tmp_ctx, members[i], args.val, &sid)) {
1013                         status = NT_STATUS_INVALID_PARAMETER;
1014                         goto done;
1015                 }
1016                 if (lookup_cached_sid(mem_ctx, &sid, &domain_name, &name, &name_type)) {
1017                         DEBUG(10,("ads: lookup_groupmem: got sid %s from cache\n",
1018                                  sid_string_static(&sid)));
1019                         sid_copy(&(*sid_mem)[*num_names], &sid);
1020                         (*names)[*num_names] = CONST_DISCARD(char *,name);
1021                         (*name_types)[*num_names] = name_type;
1022                         (*num_names)++;
1023                 }
1024                 else {
1025                         DEBUG(10, ("ads: lookup_groupmem: sid %s not found in cache\n",
1026                                   sid_string_static(&sid)));
1027                         sid_copy(&(sid_mem_nocache)[num_nocache], &sid);
1028                         num_nocache++;
1029                 }
1030         }
1031
1032         DEBUG(10, ("ads: lookup_groupmem: %d sids found in cache, "
1033                   "%d left for lsa_lookupsids\n", *num_names, num_nocache));
1034
1035         /* handle sids not resolved from cache by lsa_lookup_sids */
1036         if (num_nocache > 0) {
1037
1038                 status = cm_connect_lsa(domain, tmp_ctx, &cli, &lsa_policy);
1039
1040                 if (!NT_STATUS_IS_OK(status)) {
1041                         goto done;
1042                 }
1043
1044                 status = rpccli_lsa_lookup_sids_all(cli, tmp_ctx, 
1045                                                     &lsa_policy,
1046                                                     num_nocache, 
1047                                                     sid_mem_nocache, 
1048                                                     &domains_nocache, 
1049                                                     &names_nocache, 
1050                                                     &name_types_nocache);
1051
1052                 if (NT_STATUS_IS_OK(status) ||
1053                     NT_STATUS_EQUAL(status, STATUS_SOME_UNMAPPED)) 
1054                 {
1055                         /* Copy the entries over from the "_nocache" arrays 
1056                          * to the result arrays, skipping the gaps the 
1057                          * lookup_sids call left. */
1058                         *num_names = 0;
1059                         for (i=0; i < num_nocache; i++) {
1060                                 if (((names_nocache)[i] != NULL) && 
1061                                     ((name_types_nocache)[i] != SID_NAME_UNKNOWN)) 
1062                                 {
1063                                         sid_copy(&(*sid_mem)[*num_names],
1064                                                  &sid_mem_nocache[i]);
1065                                         (*names)[*num_names] = talloc_asprintf( *names, 
1066                                                                                 "%s%c%s",
1067                                                                                 domains_nocache[i],
1068                                                                                 *lp_winbind_separator(),
1069                                                                                 names_nocache[i] );
1070                                         (*name_types)[*num_names] = name_types_nocache[i];
1071                                         (*num_names)++;
1072                                 }
1073                         }
1074                 }
1075                 else if (NT_STATUS_EQUAL(status, NT_STATUS_NONE_MAPPED)) {
1076                         DEBUG(10, ("lookup_groupmem: lsa_lookup_sids could "
1077                                    "not map any SIDs at all.\n"));
1078                         /* Don't handle this as an error here.
1079                          * There is nothing left to do with respect to the 
1080                          * overall result... */
1081                 }
1082                 else if (!NT_STATUS_IS_OK(status)) {
1083                         DEBUG(10, ("lookup_groupmem: Error looking up %d "
1084                                    "sids via rpc_lsa_lookup_sids: %s\n",
1085                                    (int)num_members, nt_errstr(status)));
1086                         goto done;
1087                 }
1088         }
1089
1090         status = NT_STATUS_OK;
1091         DEBUG(3,("ads lookup_groupmem for sid=%s succeeded\n",
1092                  sid_string_static(group_sid)));
1093
1094 done:
1095
1096         TALLOC_FREE(tmp_ctx);
1097
1098         return status;
1099 }
1100
1101 /* find the sequence number for a domain */
1102 static NTSTATUS sequence_number(struct winbindd_domain *domain, uint32 *seq)
1103 {
1104         ADS_STRUCT *ads = NULL;
1105         ADS_STATUS rc;
1106
1107         DEBUG(3,("ads: fetch sequence_number for %s\n", domain->name));
1108
1109         if ( !winbindd_can_contact_domain( domain ) ) {
1110                 DEBUG(10,("sequence: No incoming trust for domain %s\n",
1111                           domain->name));
1112                 *seq = time(NULL);              
1113                 return NT_STATUS_OK;
1114         }
1115
1116         *seq = DOM_SEQUENCE_NONE;
1117
1118         ads = ads_cached_connection(domain);
1119         
1120         if (!ads) {
1121                 domain->last_status = NT_STATUS_SERVER_DISABLED;
1122                 return NT_STATUS_UNSUCCESSFUL;
1123         }
1124
1125         rc = ads_USN(ads, seq);
1126         
1127         if (!ADS_ERR_OK(rc)) {
1128         
1129                 /* its a dead connection, destroy it */
1130
1131                 if (domain->private_data) {
1132                         ads = (ADS_STRUCT *)domain->private_data;
1133                         ads->is_mine = True;
1134                         ads_destroy(&ads);
1135                         ads_kdestroy("MEMORY:winbind_ccache");
1136                         domain->private_data = NULL;
1137                 }
1138         }
1139         return ads_ntstatus(rc);
1140 }
1141
1142 /* get a list of trusted domains */
1143 static NTSTATUS trusted_domains(struct winbindd_domain *domain,
1144                                 TALLOC_CTX *mem_ctx,
1145                                 uint32 *num_domains,
1146                                 char ***names,
1147                                 char ***alt_names,
1148                                 DOM_SID **dom_sids)
1149 {
1150         NTSTATUS                result = NT_STATUS_UNSUCCESSFUL;
1151         struct ds_domain_trust  *domains = NULL;
1152         int                     count = 0;
1153         int                     i;
1154         uint32                  flags;  
1155         struct rpc_pipe_client *cli;
1156         uint32                 fr_flags = (DS_DOMAIN_IN_FOREST | DS_DOMAIN_TREE_ROOT);  
1157         int ret_count;
1158         
1159         DEBUG(3,("ads: trusted_domains\n"));
1160
1161         *num_domains = 0;
1162         *alt_names   = NULL;
1163         *names       = NULL;
1164         *dom_sids    = NULL;
1165
1166         /* If this is our primary domain or a root in our forest,
1167            query for all trusts.  If not, then just look for domain
1168            trusts in the target forest */
1169
1170         if ( domain->primary ||
1171                 ((domain->domain_flags&fr_flags) == fr_flags) ) 
1172         {
1173                 flags = DS_DOMAIN_DIRECT_OUTBOUND | 
1174                         DS_DOMAIN_DIRECT_INBOUND | 
1175                         DS_DOMAIN_IN_FOREST;
1176         } else {
1177                 flags = DS_DOMAIN_IN_FOREST;
1178         }       
1179
1180         result = cm_connect_netlogon(domain, &cli);
1181
1182         if (!NT_STATUS_IS_OK(result)) {
1183                 DEBUG(5, ("trusted_domains: Could not open a connection to %s "
1184                           "for PIPE_NETLOGON (%s)\n", 
1185                           domain->name, nt_errstr(result)));
1186                 return NT_STATUS_UNSUCCESSFUL;
1187         }
1188         
1189         if ( NT_STATUS_IS_OK(result) ) {
1190                 result = rpccli_ds_enum_domain_trusts(cli, mem_ctx,
1191                                                       cli->cli->desthost, 
1192                                                       flags, &domains,
1193                                                       (unsigned int *)&count);
1194         }
1195         
1196         if ( NT_STATUS_IS_OK(result) && count) {
1197
1198                 /* Allocate memory for trusted domain names and sids */
1199
1200                 if ( !(*names = TALLOC_ARRAY(mem_ctx, char *, count)) ) {
1201                         DEBUG(0, ("trusted_domains: out of memory\n"));
1202                         return NT_STATUS_NO_MEMORY;
1203                 }
1204
1205                 if ( !(*alt_names = TALLOC_ARRAY(mem_ctx, char *, count)) ) {
1206                         DEBUG(0, ("trusted_domains: out of memory\n"));
1207                         return NT_STATUS_NO_MEMORY;
1208                 }
1209
1210                 if ( !(*dom_sids = TALLOC_ARRAY(mem_ctx, DOM_SID, count)) ) {
1211                         DEBUG(0, ("trusted_domains: out of memory\n"));
1212                         return NT_STATUS_NO_MEMORY;
1213                 }
1214
1215                 /* Copy across names and sids */
1216
1217
1218                 ret_count = 0;          
1219                 for (i = 0; i < count; i++) {
1220                         struct winbindd_domain d;
1221                         
1222                         /* drop external trusts if this is not our primary 
1223                            domain.  This means that the returned number of 
1224                            domains may be less that the ones actually trusted
1225                            by the DC. */
1226
1227                         if ( (domains[i].trust_attributes == DS_DOMAIN_TRUST_ATTRIB_QUARANTINED_DOMAIN) && 
1228                              !domain->primary ) 
1229                         {
1230                                 DEBUG(10,("trusted_domains: Skipping external trusted domain "
1231                                           "%s because it is outside of our primary domain\n",
1232                                           domains[i].netbios_domain));                          
1233                                 continue;                               
1234                         }
1235                         
1236                         (*names)[ret_count] = domains[i].netbios_domain;
1237                         (*alt_names)[ret_count] = domains[i].dns_domain;
1238                         sid_copy(&(*dom_sids)[ret_count], &domains[i].sid);
1239
1240                         /* add to the trusted domain cache */
1241
1242                         fstrcpy( d.name,  domains[i].netbios_domain );
1243                         fstrcpy( d.alt_name, domains[i].dns_domain );                   
1244                         sid_copy( &d.sid, &domains[i].sid );
1245
1246                         /* This gets a little tricky.  If we are
1247                            following a transitive forest trust, then
1248                            innerit the flags, type, and attrins from
1249                            the domain we queried to make sure we don't
1250                            record the view of the trust from the wrong
1251                            side.  Always view it from the side of our
1252                            primary domain.   --jerry */
1253                         if ( domain->primary ||
1254                              ((domain->domain_flags&fr_flags) == fr_flags) ) 
1255                         {
1256                                 DEBUG(10,("trusted_domains(ads):  Storing trust "
1257                                           "flags for domain %s\n", d.alt_name));
1258
1259                                 /* Look this up in cache to make sure
1260                                    we have the current trust flags and
1261                                    attributes */
1262
1263                                 d.domain_flags = domains[i].flags;
1264                                 d.domain_type = domains[i].trust_type;
1265                                 d.domain_trust_attribs = domains[i].trust_attributes;
1266                         } else {
1267                                 DEBUG(10,("trusted_domains(ads):  Inheriting trust "
1268                                           "flags for domain %s\n", d.alt_name));                                
1269                                 d.domain_flags = domain->domain_flags;                          
1270                                 d.domain_type  = domain->domain_type;
1271                                 d.domain_trust_attribs = domain->domain_trust_attribs;
1272                         }
1273
1274                         wcache_tdc_add_domain( &d );
1275
1276                         ret_count++;
1277
1278                 }
1279
1280                 *num_domains = ret_count;       
1281         }
1282
1283         return result;
1284 }
1285
1286 /* the ADS backend methods are exposed via this structure */
1287 struct winbindd_methods ads_methods = {
1288         True,
1289         query_user_list,
1290         enum_dom_groups,
1291         enum_local_groups,
1292         msrpc_name_to_sid,
1293         msrpc_sid_to_name,
1294         msrpc_rids_to_names,
1295         query_user,
1296         lookup_usergroups,
1297         msrpc_lookup_useraliases,
1298         lookup_groupmem,
1299         sequence_number,
1300         msrpc_lockout_policy,
1301         msrpc_password_policy,
1302         trusted_domains,
1303 };
1304
1305 #endif