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