4b6bca78344477485021f382a1344606839d8777
[metze/old/v3-2-winbind-ndr.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         ads = ads_cached_connection(domain);
172         
173         if (!ads) {
174                 domain->last_status = NT_STATUS_SERVER_DISABLED;
175                 goto done;
176         }
177
178         rc = ads_search_retry(ads, &res, "(objectCategory=user)", attrs);
179         if (!ADS_ERR_OK(rc) || !res) {
180                 DEBUG(1,("query_user_list ads_search: %s\n", ads_errstr(rc)));
181                 goto done;
182         }
183
184         count = ads_count_replies(ads, res);
185         if (count == 0) {
186                 DEBUG(1,("query_user_list: No users found\n"));
187                 goto done;
188         }
189
190         (*info) = TALLOC_ZERO_ARRAY(mem_ctx, WINBIND_USERINFO, count);
191         if (!*info) {
192                 status = NT_STATUS_NO_MEMORY;
193                 goto done;
194         }
195
196         i = 0;
197
198         for (msg = ads_first_entry(ads, res); msg; msg = ads_next_entry(ads, msg)) {
199                 char *name, *gecos = NULL;
200                 char *homedir = NULL;
201                 char *shell = NULL;
202                 uint32 group;
203                 uint32 atype;
204                 DOM_SID user_sid;
205                 gid_t primary_gid = (gid_t)-1;
206
207                 if (!ads_pull_uint32(ads, msg, "sAMAccountType", &atype) ||
208                     ads_atype_map(atype) != SID_NAME_USER) {
209                         DEBUG(1,("Not a user account? atype=0x%x\n", atype));
210                         continue;
211                 }
212
213                 name = ads_pull_username(ads, mem_ctx, msg);
214
215                 if ( ads_pull_sid( ads, msg, "objectSid", &user_sid ) ) {
216                         status = nss_get_info( domain->name, &user_sid, mem_ctx, 
217                                                ads, msg, &homedir, &shell, &gecos,
218                                                &primary_gid );
219                 }
220
221                 if (gecos == NULL) {
222                         gecos = ads_pull_string(ads, mem_ctx, msg, "name");
223                 }
224         
225                 if (!ads_pull_sid(ads, msg, "objectSid",
226                                   &(*info)[i].user_sid)) {
227                         DEBUG(1,("No sid for %s !?\n", name));
228                         continue;
229                 }
230                 if (!ads_pull_uint32(ads, msg, "primaryGroupID", &group)) {
231                         DEBUG(1,("No primary group for %s !?\n", name));
232                         continue;
233                 }
234
235                 (*info)[i].acct_name = name;
236                 (*info)[i].full_name = gecos;
237                 (*info)[i].homedir = homedir;
238                 (*info)[i].shell = shell;
239                 (*info)[i].primary_gid = primary_gid;
240                 sid_compose(&(*info)[i].group_sid, &domain->sid, group);
241                 i++;
242         }
243
244         (*num_entries) = i;
245         status = NT_STATUS_OK;
246
247         DEBUG(3,("ads query_user_list gave %d entries\n", (*num_entries)));
248
249 done:
250         if (res) 
251                 ads_msgfree(ads, res);
252
253         return status;
254 }
255
256 /* list all domain groups */
257 static NTSTATUS enum_dom_groups(struct winbindd_domain *domain,
258                                 TALLOC_CTX *mem_ctx,
259                                 uint32 *num_entries, 
260                                 struct acct_info **info)
261 {
262         ADS_STRUCT *ads = NULL;
263         const char *attrs[] = {"userPrincipalName", "sAMAccountName",
264                                "name", "objectSid", NULL};
265         int i, count;
266         ADS_STATUS rc;
267         LDAPMessage *res = NULL;
268         LDAPMessage *msg = NULL;
269         NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
270         const char *filter;
271         BOOL enum_dom_local_groups = False;
272
273         *num_entries = 0;
274
275         DEBUG(3,("ads: enum_dom_groups\n"));
276
277         /* only grab domain local groups for our domain */
278         if ( domain->active_directory && strequal(lp_realm(), domain->alt_name)  ) {
279                 enum_dom_local_groups = True;
280         }
281
282         /* Workaround ADS LDAP bug present in MS W2K3 SP0 and W2K SP4 w/o
283          * rollup-fixes:
284          *
285          * According to Section 5.1(4) of RFC 2251 if a value of a type is it's
286          * default value, it MUST be absent. In case of extensible matching the
287          * "dnattr" boolean defaults to FALSE and so it must be only be present
288          * when set to TRUE. 
289          *
290          * When it is set to FALSE and the OpenLDAP lib (correctly) encodes a
291          * filter using bitwise matching rule then the buggy AD fails to decode
292          * the extensible match. As a workaround set it to TRUE and thereby add
293          * the dnAttributes "dn" field to cope with those older AD versions.
294          * It should not harm and won't put any additional load on the AD since
295          * none of the dn components have a bitmask-attribute.
296          *
297          * Thanks to Ralf Haferkamp for input and testing - Guenther */
298
299         filter = talloc_asprintf(mem_ctx, "(&(objectCategory=group)(&(groupType:dn:%s:=%d)(!(groupType:dn:%s:=%d))))", 
300                                  ADS_LDAP_MATCHING_RULE_BIT_AND, GROUP_TYPE_SECURITY_ENABLED,
301                                  ADS_LDAP_MATCHING_RULE_BIT_AND, 
302                                  enum_dom_local_groups ? GROUP_TYPE_BUILTIN_LOCAL_GROUP : GROUP_TYPE_RESOURCE_GROUP);
303
304         if (filter == NULL) {
305                 status = NT_STATUS_NO_MEMORY;
306                 goto done;
307         }
308
309         ads = ads_cached_connection(domain);
310
311         if (!ads) {
312                 domain->last_status = NT_STATUS_SERVER_DISABLED;
313                 goto done;
314         }
315
316         rc = ads_search_retry(ads, &res, filter, attrs);
317         if (!ADS_ERR_OK(rc) || !res) {
318                 DEBUG(1,("enum_dom_groups ads_search: %s\n", ads_errstr(rc)));
319                 goto done;
320         }
321
322         count = ads_count_replies(ads, res);
323         if (count == 0) {
324                 DEBUG(1,("enum_dom_groups: No groups found\n"));
325                 goto done;
326         }
327
328         (*info) = TALLOC_ZERO_ARRAY(mem_ctx, struct acct_info, count);
329         if (!*info) {
330                 status = NT_STATUS_NO_MEMORY;
331                 goto done;
332         }
333
334         i = 0;
335         
336         for (msg = ads_first_entry(ads, res); msg; msg = ads_next_entry(ads, msg)) {
337                 char *name, *gecos;
338                 DOM_SID sid;
339                 uint32 rid;
340
341                 name = ads_pull_username(ads, mem_ctx, msg);
342                 gecos = ads_pull_string(ads, mem_ctx, msg, "name");
343                 if (!ads_pull_sid(ads, msg, "objectSid", &sid)) {
344                         DEBUG(1,("No sid for %s !?\n", name));
345                         continue;
346                 }
347
348                 if (!sid_peek_check_rid(&domain->sid, &sid, &rid)) {
349                         DEBUG(1,("No rid for %s !?\n", name));
350                         continue;
351                 }
352
353                 fstrcpy((*info)[i].acct_name, name);
354                 fstrcpy((*info)[i].acct_desc, gecos);
355                 (*info)[i].rid = rid;
356                 i++;
357         }
358
359         (*num_entries) = i;
360
361         status = NT_STATUS_OK;
362
363         DEBUG(3,("ads enum_dom_groups gave %d entries\n", (*num_entries)));
364
365 done:
366         if (res) 
367                 ads_msgfree(ads, res);
368
369         return status;
370 }
371
372 /* list all domain local groups */
373 static NTSTATUS enum_local_groups(struct winbindd_domain *domain,
374                                 TALLOC_CTX *mem_ctx,
375                                 uint32 *num_entries, 
376                                 struct acct_info **info)
377 {
378         /*
379          * This is a stub function only as we returned the domain 
380          * local groups in enum_dom_groups() if the domain->native field
381          * was true.  This is a simple performance optimization when
382          * using LDAP.
383          *
384          * if we ever need to enumerate domain local groups separately, 
385          * then this the optimization in enum_dom_groups() will need 
386          * to be split out
387          */
388         *num_entries = 0;
389         
390         return NT_STATUS_OK;
391 }
392
393 /* convert a DN to a name, SID and name type 
394    this might become a major speed bottleneck if groups have
395    lots of users, in which case we could cache the results
396 */
397 static BOOL dn_lookup(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
398                       const char *dn,
399                       char **name, uint32 *name_type, DOM_SID *sid)
400 {
401         LDAPMessage *res = NULL;
402         const char *attrs[] = {"userPrincipalName", "sAMAccountName",
403                                "objectSid", "sAMAccountType", NULL};
404         ADS_STATUS rc;
405         uint32 atype;
406         DEBUG(3,("ads: dn_lookup\n"));
407
408         rc = ads_search_retry_dn(ads, &res, dn, attrs);
409
410         if (!ADS_ERR_OK(rc) || !res) {
411                 goto failed;
412         }
413
414         (*name) = ads_pull_username(ads, mem_ctx, res);
415
416         if (!ads_pull_uint32(ads, res, "sAMAccountType", &atype)) {
417                 goto failed;
418         }
419         (*name_type) = ads_atype_map(atype);
420
421         if (!ads_pull_sid(ads, res, "objectSid", sid)) {
422                 goto failed;
423         }
424
425         if (res) 
426                 ads_msgfree(ads, res);
427
428         return True;
429
430 failed:
431         if (res) 
432                 ads_msgfree(ads, res);
433
434         return False;
435 }
436
437 /* Lookup user information from a rid */
438 static NTSTATUS query_user(struct winbindd_domain *domain, 
439                            TALLOC_CTX *mem_ctx, 
440                            const DOM_SID *sid, 
441                            WINBIND_USERINFO *info)
442 {
443         ADS_STRUCT *ads = NULL;
444         const char *attrs[] = { "*", NULL };
445         ADS_STATUS rc;
446         int count;
447         LDAPMessage *msg = NULL;
448         char *ldap_exp;
449         char *sidstr;
450         uint32 group_rid;
451         NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
452
453         DEBUG(3,("ads: query_user\n"));
454
455         if ( (ads = ads_cached_connection(domain)) == NULL ) {
456                 domain->last_status = NT_STATUS_SERVER_DISABLED;
457                 goto done;
458         }
459
460         sidstr = sid_binstring(sid);
461         asprintf(&ldap_exp, "(objectSid=%s)", sidstr);
462         rc = ads_search_retry(ads, &msg, ldap_exp, attrs);
463         free(ldap_exp);
464         free(sidstr);
465         if (!ADS_ERR_OK(rc) || !msg) {
466                 DEBUG(1,("query_user(sid=%s) ads_search: %s\n",
467                          sid_string_static(sid), ads_errstr(rc)));
468                 goto done;
469         }
470
471         count = ads_count_replies(ads, msg);
472         if (count != 1) {
473                 DEBUG(1,("query_user(sid=%s): Not found\n",
474                          sid_string_static(sid)));
475                 goto done;
476         }
477
478         info->acct_name = ads_pull_username(ads, mem_ctx, msg);
479
480         info->primary_gid = (gid_t)-1;  
481         nss_get_info( domain->name, sid, mem_ctx, ads, msg, 
482                       &info->homedir, &info->shell, &info->full_name, &info->primary_gid );     
483
484         if (info->full_name == NULL) {
485                 info->full_name = ads_pull_string(ads, mem_ctx, msg, "name");
486         }
487
488         if (!ads_pull_uint32(ads, msg, "primaryGroupID", &group_rid)) {
489                 DEBUG(1,("No primary group for %s !?\n",
490                          sid_string_static(sid)));
491                 goto done;
492         }
493
494         sid_copy(&info->user_sid, sid);
495         sid_compose(&info->group_sid, &domain->sid, group_rid);
496
497         status = NT_STATUS_OK;
498
499         DEBUG(3,("ads query_user gave %s\n", info->acct_name));
500 done:
501         if (msg) 
502                 ads_msgfree(ads, msg);
503
504         return status;
505 }
506
507 /* Lookup groups a user is a member of - alternate method, for when
508    tokenGroups are not available. */
509 static NTSTATUS lookup_usergroups_member(struct winbindd_domain *domain,
510                                          TALLOC_CTX *mem_ctx,
511                                          const char *user_dn, 
512                                          DOM_SID *primary_group,
513                                          size_t *p_num_groups, DOM_SID **user_sids)
514 {
515         ADS_STATUS rc;
516         NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
517         int count;
518         LDAPMessage *res = NULL;
519         LDAPMessage *msg = NULL;
520         char *ldap_exp;
521         ADS_STRUCT *ads;
522         const char *group_attrs[] = {"objectSid", NULL};
523         char *escaped_dn;
524         size_t num_groups = 0;
525
526         DEBUG(3,("ads: lookup_usergroups_member\n"));
527
528         ads = ads_cached_connection(domain);
529
530         if (!ads) {
531                 domain->last_status = NT_STATUS_SERVER_DISABLED;
532                 goto done;
533         }
534
535         if (!(escaped_dn = escape_ldap_string_alloc(user_dn))) {
536                 status = NT_STATUS_NO_MEMORY;
537                 goto done;
538         }
539
540         if (!(ldap_exp = talloc_asprintf(mem_ctx, "(&(member=%s)(objectCategory=group))", escaped_dn))) {
541                 DEBUG(1,("lookup_usergroups(dn=%s) asprintf failed!\n", user_dn));
542                 SAFE_FREE(escaped_dn);
543                 status = NT_STATUS_NO_MEMORY;
544                 goto done;
545         }
546
547         SAFE_FREE(escaped_dn);
548
549         rc = ads_search_retry(ads, &res, ldap_exp, group_attrs);
550         
551         if (!ADS_ERR_OK(rc) || !res) {
552                 DEBUG(1,("lookup_usergroups ads_search member=%s: %s\n", user_dn, ads_errstr(rc)));
553                 return ads_ntstatus(rc);
554         }
555         
556         count = ads_count_replies(ads, res);
557         
558         *user_sids = NULL;
559         num_groups = 0;
560
561         /* always add the primary group to the sid array */
562         if (!add_sid_to_array(mem_ctx, primary_group, user_sids, &num_groups)) {
563                 status = NT_STATUS_NO_MEMORY;
564                 goto done;
565         }
566
567         if (count > 0) {
568                 for (msg = ads_first_entry(ads, res); msg;
569                      msg = ads_next_entry(ads, msg)) {
570                         DOM_SID group_sid;
571                 
572                         if (!ads_pull_sid(ads, msg, "objectSid", &group_sid)) {
573                                 DEBUG(1,("No sid for this group ?!?\n"));
574                                 continue;
575                         }
576         
577                         /* ignore Builtin groups from ADS - Guenther */
578                         if (sid_check_is_in_builtin(&group_sid)) {
579                                 continue;
580                         }
581                                
582                         if (!add_sid_to_array(mem_ctx, &group_sid, user_sids,
583                                          &num_groups)) {
584                                 status = NT_STATUS_NO_MEMORY;
585                                 goto done;
586                         }
587                 }
588
589         }
590
591         *p_num_groups = num_groups;
592         status = (user_sids != NULL) ? NT_STATUS_OK : NT_STATUS_NO_MEMORY;
593
594         DEBUG(3,("ads lookup_usergroups (member) succeeded for dn=%s\n", user_dn));
595 done:
596         if (res) 
597                 ads_msgfree(ads, res);
598
599         return status;
600 }
601
602 /* Lookup groups a user is a member of - alternate method, for when
603    tokenGroups are not available. */
604 static NTSTATUS lookup_usergroups_memberof(struct winbindd_domain *domain,
605                                            TALLOC_CTX *mem_ctx,
606                                            const char *user_dn, 
607                                            DOM_SID *primary_group,
608                                            size_t *p_num_groups, DOM_SID **user_sids)
609 {
610         ADS_STATUS rc;
611         NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
612         ADS_STRUCT *ads;
613         const char *attrs[] = {"memberOf", NULL};
614         size_t num_groups = 0;
615         DOM_SID *group_sids = NULL;
616         int i;
617         char **strings;
618         size_t num_strings = 0;
619
620
621         DEBUG(3,("ads: lookup_usergroups_memberof\n"));
622
623         ads = ads_cached_connection(domain);
624
625         if (!ads) {
626                 domain->last_status = NT_STATUS_SERVER_DISABLED;
627                 goto done;
628         }
629
630         rc = ads_search_retry_extended_dn_ranged(ads, mem_ctx, user_dn, attrs, 
631                                                  ADS_EXTENDED_DN_HEX_STRING, 
632                                                  &strings, &num_strings);
633
634         if (!ADS_ERR_OK(rc)) {
635                 DEBUG(1,("lookup_usergroups_memberof ads_search member=%s: %s\n", 
636                         user_dn, ads_errstr(rc)));
637                 return ads_ntstatus(rc);
638         }
639         
640         *user_sids = NULL;
641         num_groups = 0;
642
643         /* always add the primary group to the sid array */
644         if (!add_sid_to_array(mem_ctx, primary_group, user_sids, &num_groups)) {
645                 status = NT_STATUS_NO_MEMORY;
646                 goto done;
647         }
648
649         group_sids = TALLOC_ZERO_ARRAY(mem_ctx, DOM_SID, num_strings + 1);
650         if (!group_sids) {
651                 TALLOC_FREE(strings);
652                 status = NT_STATUS_NO_MEMORY;
653                 goto done;
654         }
655
656         for (i=0; i<num_strings; i++) {
657
658                 if (!ads_get_sid_from_extended_dn(mem_ctx, strings[i], 
659                                                   ADS_EXTENDED_DN_HEX_STRING, 
660                                                   &(group_sids)[i])) {
661                         TALLOC_FREE(group_sids);
662                         TALLOC_FREE(strings);
663                         status = NT_STATUS_NO_MEMORY;
664                         goto done;
665                 }
666         }
667
668         if (i == 0) {
669                 DEBUG(1,("No memberOf for this user?!?\n"));
670                 status = NT_STATUS_NO_MEMORY;
671                 goto done;
672         }
673
674         for (i=0; i<num_strings; i++) {
675
676                 /* ignore Builtin groups from ADS - Guenther */
677                 if (sid_check_is_in_builtin(&group_sids[i])) {
678                         continue;
679                 }
680                        
681                 if (!add_sid_to_array(mem_ctx, &group_sids[i], user_sids,
682                                  &num_groups)) {
683                         status = NT_STATUS_NO_MEMORY;
684                         goto done;
685                 }
686         
687         }
688
689         *p_num_groups = num_groups;
690         status = (*user_sids != NULL) ? NT_STATUS_OK : NT_STATUS_NO_MEMORY;
691
692         DEBUG(3,("ads lookup_usergroups (memberof) succeeded for dn=%s\n", user_dn));
693 done:
694         TALLOC_FREE(group_sids);
695
696         return status;
697 }
698
699
700 /* Lookup groups a user is a member of. */
701 static NTSTATUS lookup_usergroups(struct winbindd_domain *domain,
702                                   TALLOC_CTX *mem_ctx,
703                                   const DOM_SID *sid, 
704                                   uint32 *p_num_groups, DOM_SID **user_sids)
705 {
706         ADS_STRUCT *ads = NULL;
707         const char *attrs[] = {"tokenGroups", "primaryGroupID", NULL};
708         ADS_STATUS rc;
709         int count;
710         LDAPMessage *msg = NULL;
711         char *user_dn = NULL;
712         DOM_SID *sids;
713         int i;
714         DOM_SID primary_group;
715         uint32 primary_group_rid;
716         fstring sid_string;
717         NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
718         size_t num_groups = 0;
719
720         DEBUG(3,("ads: lookup_usergroups\n"));
721         *p_num_groups = 0;
722
723         status = lookup_usergroups_cached(domain, mem_ctx, sid, 
724                                           p_num_groups, user_sids);
725         if (NT_STATUS_IS_OK(status)) {
726                 return NT_STATUS_OK;
727         }
728
729         ads = ads_cached_connection(domain);
730         
731         if (!ads) {
732                 domain->last_status = NT_STATUS_SERVER_DISABLED;
733                 status = NT_STATUS_SERVER_DISABLED;
734                 goto done;
735         }
736
737         rc = ads_search_retry_sid(ads, &msg, sid, attrs);
738
739         if (!ADS_ERR_OK(rc)) {
740                 status = ads_ntstatus(rc);
741                 DEBUG(1,("lookup_usergroups(sid=%s) ads_search tokenGroups: %s\n", 
742                          sid_to_string(sid_string, sid), ads_errstr(rc)));
743                 goto done;
744         }
745         
746         count = ads_count_replies(ads, msg);
747         if (count != 1) {
748                 status = NT_STATUS_UNSUCCESSFUL;
749                 DEBUG(1,("lookup_usergroups(sid=%s) ads_search tokenGroups: "
750                          "invalid number of results (count=%d)\n", 
751                          sid_to_string(sid_string, sid), count));
752                 goto done;
753         }
754
755         if (!msg) {
756                 DEBUG(1,("lookup_usergroups(sid=%s) ads_search tokenGroups: NULL msg\n", 
757                          sid_to_string(sid_string, sid)));
758                 status = NT_STATUS_UNSUCCESSFUL;
759                 goto done;
760         }
761
762         user_dn = ads_get_dn(ads, msg);
763         if (user_dn == NULL) {
764                 status = NT_STATUS_NO_MEMORY;
765                 goto done;
766         }
767
768         if (!ads_pull_uint32(ads, msg, "primaryGroupID", &primary_group_rid)) {
769                 DEBUG(1,("%s: No primary group for sid=%s !?\n", 
770                          domain->name, sid_to_string(sid_string, sid)));
771                 goto done;
772         }
773
774         sid_copy(&primary_group, &domain->sid);
775         sid_append_rid(&primary_group, primary_group_rid);
776
777         count = ads_pull_sids(ads, mem_ctx, msg, "tokenGroups", &sids);
778
779         /* there must always be at least one group in the token, 
780            unless we are talking to a buggy Win2k server */
781
782         /* actually this only happens when the machine account has no read
783          * permissions on the tokenGroup attribute - gd */
784
785         if (count == 0) {
786
787                 /* no tokenGroups */
788                 
789                 /* lookup what groups this user is a member of by DN search on
790                  * "memberOf" */
791
792                 status = lookup_usergroups_memberof(domain, mem_ctx, user_dn,
793                                                     &primary_group,
794                                                     &num_groups, user_sids);
795                 *p_num_groups = (uint32)num_groups;
796                 if (NT_STATUS_IS_OK(status)) {
797                         goto done;
798                 }
799
800                 /* lookup what groups this user is a member of by DN search on
801                  * "member" */
802
803                 status = lookup_usergroups_member(domain, mem_ctx, user_dn, 
804                                                   &primary_group,
805                                                   &num_groups, user_sids);
806                 *p_num_groups = (uint32)num_groups;
807                 goto done;
808         }
809
810         *user_sids = NULL;
811         num_groups = 0;
812
813         if (!add_sid_to_array(mem_ctx, &primary_group, user_sids, &num_groups)) {
814                 status = NT_STATUS_NO_MEMORY;
815                 goto done;
816         }
817         
818         for (i=0;i<count;i++) {
819
820                 /* ignore Builtin groups from ADS - Guenther */
821                 if (sid_check_is_in_builtin(&sids[i])) {
822                         continue;
823                 }
824                                
825                 if (!add_sid_to_array_unique(mem_ctx, &sids[i],
826                                         user_sids, &num_groups)) {
827                         status = NT_STATUS_NO_MEMORY;
828                         goto done;
829                 }
830         }
831
832         *p_num_groups = (uint32)num_groups;
833         status = (*user_sids != NULL) ? NT_STATUS_OK : NT_STATUS_NO_MEMORY;
834
835         DEBUG(3,("ads lookup_usergroups (tokenGroups) succeeded for sid=%s\n",
836                  sid_to_string(sid_string, sid)));
837 done:
838         ads_memfree(ads, user_dn);
839         ads_msgfree(ads, msg);
840         return status;
841 }
842
843 /*
844   find the members of a group, given a group rid and domain
845  */
846 static NTSTATUS lookup_groupmem(struct winbindd_domain *domain,
847                                 TALLOC_CTX *mem_ctx,
848                                 const DOM_SID *group_sid, uint32 *num_names, 
849                                 DOM_SID **sid_mem, char ***names, 
850                                 uint32 **name_types)
851 {
852         ADS_STATUS rc;
853         LDAPMessage *res=NULL;
854         ADS_STRUCT *ads = NULL;
855         char *ldap_exp;
856         NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
857         char *sidstr;
858         char **members;
859         int i;
860         size_t num_members;
861         fstring sid_string;
862         ads_control args;
863
864         DEBUG(10,("ads: lookup_groupmem %s sid=%s\n", domain->name, 
865                   sid_string_static(group_sid)));
866
867         *num_names = 0;
868
869         ads = ads_cached_connection(domain);
870         
871         if (!ads) {
872                 domain->last_status = NT_STATUS_SERVER_DISABLED;
873                 goto done;
874         }
875
876         if ((sidstr = sid_binstring(group_sid)) == NULL) {
877                 status = NT_STATUS_NO_MEMORY;
878                 goto done;
879         }
880
881         /* search for all members of the group */
882         if (!(ldap_exp = talloc_asprintf(mem_ctx, "(objectSid=%s)",sidstr))) {
883                 SAFE_FREE(sidstr);
884                 DEBUG(1, ("ads: lookup_groupmem: tallloc_asprintf for ldap_exp failed!\n"));
885                 status = NT_STATUS_NO_MEMORY;
886                 goto done;
887         }
888         SAFE_FREE(sidstr);
889
890         members = NULL;
891         num_members = 0;
892
893         args.control = ADS_EXTENDED_DN_OID;
894         args.val = ADS_EXTENDED_DN_HEX_STRING;
895         args.critical = True;
896
897         rc = ads_ranged_search(ads, mem_ctx, LDAP_SCOPE_SUBTREE, ads->config.bind_path, 
898                                ldap_exp, &args, "member", &members, &num_members);
899
900         if (!ADS_ERR_OK(rc)) {
901                 DEBUG(0,("ads_ranged_search failed with: %s\n", ads_errstr(rc)));
902                 status = NT_STATUS_UNSUCCESSFUL;
903                 goto done;
904         } 
905         
906         /* now we need to turn a list of members into rids, names and name types 
907            the problem is that the members are in the form of distinguised names
908         */
909
910         if (num_members) {
911                 (*sid_mem) = TALLOC_ZERO_ARRAY(mem_ctx, DOM_SID, num_members);
912                 (*name_types) = TALLOC_ZERO_ARRAY(mem_ctx, uint32, num_members);
913                 (*names) = TALLOC_ZERO_ARRAY(mem_ctx, char *, num_members);
914
915                 if ((members == NULL) || (*sid_mem == NULL) ||
916                      (*name_types == NULL) || (*names == NULL)) {
917                         DEBUG(1, ("talloc failed\n"));
918                         status = NT_STATUS_NO_MEMORY;
919                         goto done;
920                 }
921         } else {
922                 (*sid_mem) = NULL;
923                 (*name_types) = NULL;
924                 (*names) = NULL;
925         }
926  
927         for (i=0;i<num_members;i++) {
928                 uint32 name_type;
929                 char *name, *domain_name, *dn;
930                 DOM_SID sid;
931
932                 if ((!ads_get_sid_from_extended_dn(mem_ctx, members[i], ADS_EXTENDED_DN_HEX_STRING, &sid)) ||
933                     (!ads_get_dn_from_extended_dn(mem_ctx, members[i], &dn)))
934                 {
935                         status = NT_STATUS_INVALID_PARAMETER;
936                         goto done;
937                 }
938
939                 if (lookup_cached_sid(mem_ctx, &sid, &domain_name, &name, &name_type)) {
940
941                         DEBUG(10,("ads: lookup_groupmem: got sid %s from cache\n", 
942                                 sid_string_static(&sid)));
943
944                         (*names)[*num_names] = CONST_DISCARD(char *,name);
945                         (*name_types)[*num_names] = name_type;
946                         sid_copy(&(*sid_mem)[*num_names], &sid);
947
948                         (*num_names)++;
949
950                         continue;
951                 }
952
953                 if (dn_lookup(ads, mem_ctx, dn, &name, &name_type, &sid)) {
954
955                         DEBUG(10,("ads: lookup_groupmem: got sid %s from dn_lookup\n", 
956                                 sid_string_static(&sid)));
957                         
958                         (*names)[*num_names] = name;
959                         (*name_types)[*num_names] = name_type;
960                         sid_copy(&(*sid_mem)[*num_names], &sid);
961                         
962                         (*num_names)++;
963
964                 }
965         }       
966
967         status = NT_STATUS_OK;
968         DEBUG(3,("ads lookup_groupmem for sid=%s succeeded\n", sid_to_string(sid_string, group_sid)));
969 done:
970
971         if (res) 
972                 ads_msgfree(ads, res);
973
974         return status;
975 }
976
977 /* find the sequence number for a domain */
978 static NTSTATUS sequence_number(struct winbindd_domain *domain, uint32 *seq)
979 {
980         ADS_STRUCT *ads = NULL;
981         ADS_STATUS rc;
982
983         DEBUG(3,("ads: fetch sequence_number for %s\n", domain->name));
984
985         *seq = DOM_SEQUENCE_NONE;
986
987         ads = ads_cached_connection(domain);
988         
989         if (!ads) {
990                 domain->last_status = NT_STATUS_SERVER_DISABLED;
991                 return NT_STATUS_UNSUCCESSFUL;
992         }
993
994         rc = ads_USN(ads, seq);
995         
996         if (!ADS_ERR_OK(rc)) {
997         
998                 /* its a dead connection, destroy it */
999
1000                 if (domain->private_data) {
1001                         ads = (ADS_STRUCT *)domain->private_data;
1002                         ads->is_mine = True;
1003                         ads_destroy(&ads);
1004                         ads_kdestroy("MEMORY:winbind_ccache");
1005                         domain->private_data = NULL;
1006                 }
1007         }
1008         return ads_ntstatus(rc);
1009 }
1010
1011 /* get a list of trusted domains */
1012 static NTSTATUS trusted_domains(struct winbindd_domain *domain,
1013                                 TALLOC_CTX *mem_ctx,
1014                                 uint32 *num_domains,
1015                                 char ***names,
1016                                 char ***alt_names,
1017                                 DOM_SID **dom_sids)
1018 {
1019         NTSTATUS                result = NT_STATUS_UNSUCCESSFUL;
1020         struct ds_domain_trust  *domains = NULL;
1021         int                     count = 0;
1022         int                     i;
1023         uint32                  flags;  
1024         struct rpc_pipe_client *cli;
1025         uint32                 fr_flags = (DS_DOMAIN_IN_FOREST | DS_DOMAIN_TREE_ROOT);  
1026         int ret_count;
1027
1028         DEBUG(3,("ads: trusted_domains\n"));
1029
1030         *num_domains = 0;
1031         *alt_names   = NULL;
1032         *names       = NULL;
1033         *dom_sids    = NULL;
1034
1035         /* If this is our primary domain or a root in our forest,
1036            query for all trusts.  If not, then just look for domain
1037            trusts in the target forest */
1038
1039         if ( domain->primary ||
1040                 ((domain->domain_flags&fr_flags) == fr_flags) ) 
1041         {
1042                 flags = DS_DOMAIN_DIRECT_OUTBOUND | 
1043                         DS_DOMAIN_DIRECT_INBOUND | 
1044                         DS_DOMAIN_IN_FOREST;
1045         } else {
1046                 flags = DS_DOMAIN_IN_FOREST;
1047         }       
1048
1049         result = cm_connect_netlogon(domain, &cli);
1050
1051         if (!NT_STATUS_IS_OK(result)) {
1052                 DEBUG(5, ("trusted_domains: Could not open a connection to %s "
1053                           "for PIPE_NETLOGON (%s)\n", 
1054                           domain->name, nt_errstr(result)));
1055                 return NT_STATUS_UNSUCCESSFUL;
1056         }
1057         
1058         if ( NT_STATUS_IS_OK(result) ) {
1059                 result = rpccli_ds_enum_domain_trusts(cli, mem_ctx,
1060                                                       cli->cli->desthost, 
1061                                                       flags, &domains,
1062                                                       (unsigned int *)&count);
1063         }
1064         
1065         if ( NT_STATUS_IS_OK(result) && count) {
1066         
1067                 /* Allocate memory for trusted domain names and sids */
1068
1069                 if ( !(*names = TALLOC_ARRAY(mem_ctx, char *, count)) ) {
1070                         DEBUG(0, ("trusted_domains: out of memory\n"));
1071                         return NT_STATUS_NO_MEMORY;
1072                 }
1073
1074                 if ( !(*alt_names = TALLOC_ARRAY(mem_ctx, char *, count)) ) {
1075                         DEBUG(0, ("trusted_domains: out of memory\n"));
1076                         return NT_STATUS_NO_MEMORY;
1077                 }
1078
1079                 if ( !(*dom_sids = TALLOC_ARRAY(mem_ctx, DOM_SID, count)) ) {
1080                         DEBUG(0, ("trusted_domains: out of memory\n"));
1081                         return NT_STATUS_NO_MEMORY;
1082                 }
1083
1084                 /* Copy across names and sids */
1085
1086
1087                 ret_count = 0;          
1088                 for (i = 0; i < count; i++) {
1089                         struct winbindd_domain d;
1090                         
1091                         /* drop external trusts if this is not our primary 
1092                            domain.  This means that the returned number of 
1093                            domains may be less that the ones actually trusted
1094                            by the DC. */
1095
1096                         if ( (domains[i].trust_attributes == DS_DOMAIN_TRUST_ATTRIB_QUARANTINED_DOMAIN) && 
1097                              !domain->primary ) 
1098                         {
1099                                 DEBUG(10,("trusted_domains: Skipping external trusted domain "
1100                                           "%s because it is outside of our primary domain\n",
1101                                           domains[i].netbios_domain));                          
1102                                 continue;                               
1103                         }
1104                         
1105                         (*names)[ret_count] = domains[i].netbios_domain;
1106                         (*alt_names)[ret_count] = domains[i].dns_domain;
1107                         sid_copy(&(*dom_sids)[ret_count], &domains[i].sid);
1108
1109                         /* add to the trusted domain cache */
1110
1111                         fstrcpy( d.name,  domains[i].netbios_domain );
1112                         fstrcpy( d.alt_name, domains[i].dns_domain );                   
1113                         sid_copy( &d.sid, &domains[i].sid );
1114
1115                         /* This gets a little tricky.  If we are
1116                            following a transitive forest trust, then
1117                            innerit the flags, type, and attrins from
1118                            the domain we queried to make sure we don't
1119                            record the view of the trust from the wrong
1120                            side.  Always view it from the side of our
1121                            primary domain.   --jerry */
1122                         if ( domain->primary ||
1123                              ((domain->domain_flags&fr_flags) == fr_flags) ) 
1124                         {
1125                                 DEBUG(10,("trusted_domains(ads):  Storing trust "
1126                                           "flags for domain %s\n", d.alt_name));
1127
1128                                 /* Look this up in cache to make sure
1129                                    we have the current trust flags and
1130                                    attributes */
1131
1132                                 d.domain_flags = domains[i].flags;
1133                                 d.domain_type = domains[i].trust_type;
1134                                 d.domain_trust_attribs = domains[i].trust_attributes;
1135                         } else {
1136                                 DEBUG(10,("trusted_domains(ads):  Inheriting trust "
1137                                           "flags for domain %s\n", d.alt_name));                                
1138                                 d.domain_flags = domain->domain_flags;                          
1139                                 d.domain_type  = domain->domain_type;
1140                                 d.domain_trust_attribs = domain->domain_trust_attribs;
1141                         }
1142
1143                         wcache_tdc_add_domain( &d );
1144
1145                         ret_count++;
1146
1147                 }
1148
1149                 *num_domains = ret_count;       
1150         }
1151
1152         return result;
1153 }
1154
1155 /* the ADS backend methods are exposed via this structure */
1156 struct winbindd_methods ads_methods = {
1157         True,
1158         query_user_list,
1159         enum_dom_groups,
1160         enum_local_groups,
1161         msrpc_name_to_sid,
1162         msrpc_sid_to_name,
1163         msrpc_rids_to_names,
1164         query_user,
1165         lookup_usergroups,
1166         msrpc_lookup_useraliases,
1167         lookup_groupmem,
1168         sequence_number,
1169         msrpc_lockout_policy,
1170         msrpc_password_policy,
1171         trusted_domains,
1172 };
1173
1174 #endif