r22511: Remove unused LDAPMessage.
[sfrench/samba-autobuild/.git] / source / nsswitch / winbindd_ads.c
1 /*
2    Unix SMB/CIFS implementation.
3
4    Winbind ADS backend functions
5
6    Copyright (C) Andrew Tridgell 2001
7    Copyright (C) Andrew Bartlett <abartlet@samba.org> 2003
8    Copyright (C) Gerald (Jerry) Carter 2004
9    
10    This program is free software; you can redistribute it and/or modify
11    it under the terms of the GNU General Public License as published by
12    the Free Software Foundation; either version 2 of the License, or
13    (at your option) any later version.
14    
15    This program is distributed in the hope that it will be useful,
16    but WITHOUT ANY WARRANTY; without even the implied warranty of
17    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18    GNU General Public License for more details.
19    
20    You should have received a copy of the GNU General Public License
21    along with this program; if not, write to the Free Software
22    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
23 */
24
25 #include "includes.h"
26 #include "winbindd.h"
27
28 #ifdef HAVE_ADS
29
30 #undef DBGC_CLASS
31 #define DBGC_CLASS DBGC_WINBIND
32
33 extern struct winbindd_methods reconnect_methods;
34
35 /*
36   return our ads connections structure for a domain. We keep the connection
37   open to make things faster
38 */
39 static ADS_STRUCT *ads_cached_connection(struct winbindd_domain *domain)
40 {
41         ADS_STRUCT *ads;
42         ADS_STATUS status;
43         fstring dc_name;
44         struct in_addr dc_ip;   
45
46         DEBUG(10,("ads_cached_connection\n"));
47
48         if (domain->private_data) {
49
50                 time_t expire;
51                 time_t now = time(NULL);
52
53                 /* check for a valid structure */
54                 ads = (ADS_STRUCT *)domain->private_data;
55
56                 expire = MIN(ads->auth.tgt_expire, ads->auth.tgs_expire);
57
58                 DEBUG(7, ("Current tickets expire in %d seconds (at %d, time is now %d)\n",
59                           (uint32)expire-(uint32)now, (uint32) expire, (uint32) now));
60
61                 if ( ads->config.realm && (expire > now)) {
62                         return ads;
63                 } else {
64                         /* we own this ADS_STRUCT so make sure it goes away */
65                         DEBUG(7,("Deleting expired krb5 credential cache\n"));
66                         ads->is_mine = True;
67                         ads_destroy( &ads );
68                         ads_kdestroy("MEMORY:winbind_ccache");
69                         domain->private_data = NULL;
70                 }       
71         }
72
73         /* we don't want this to affect the users ccache */
74         setenv("KRB5CCNAME", "MEMORY:winbind_ccache", 1);
75
76         ads = ads_init(domain->alt_name, domain->name, NULL);
77         if (!ads) {
78                 DEBUG(1,("ads_init for domain %s failed\n", domain->name));
79                 return NULL;
80         }
81
82         /* the machine acct password might have change - fetch it every time */
83
84         SAFE_FREE(ads->auth.password);
85         SAFE_FREE(ads->auth.realm);
86
87         if ( IS_DC ) {
88                 DOM_SID sid;
89                 time_t last_set_time;
90
91                 if ( !pdb_get_trusteddom_pw( domain->name, &ads->auth.password, &sid, &last_set_time ) ) {
92                         ads_destroy( &ads );
93                         return NULL;
94                 }
95                 ads->auth.realm = SMB_STRDUP( ads->server.realm );
96                 strupper_m( ads->auth.realm );
97         }
98         else {
99                 struct winbindd_domain *our_domain = domain;
100
101                 ads->auth.password = secrets_fetch_machine_password(lp_workgroup(), NULL, NULL);
102
103                 /* always give preference to the alt_name in our 
104                    primary domain if possible */
105
106                 if ( !domain->primary )
107                         our_domain = find_our_domain();
108
109                 if ( our_domain->alt_name[0] != '\0' ) {
110                         ads->auth.realm = SMB_STRDUP( our_domain->alt_name );
111                         strupper_m( ads->auth.realm );
112                 }
113                 else
114                         ads->auth.realm = SMB_STRDUP( lp_realm() );
115         }
116
117         ads->auth.renewable = WINBINDD_PAM_AUTH_KRB5_RENEW_TIME;
118
119         /* Setup the server affinity cache.  We don't reaally care
120            about the name.  Just setup affinity and the KRB5_CONFIG 
121            file. */
122
123         get_dc_name( ads->server.workgroup, ads->server.realm, dc_name, &dc_ip );
124         
125         status = ads_connect(ads);
126         if (!ADS_ERR_OK(status) || !ads->config.realm) {
127                 DEBUG(1,("ads_connect for domain %s failed: %s\n", 
128                          domain->name, ads_errstr(status)));
129                 ads_destroy(&ads);
130
131                 /* if we get ECONNREFUSED then it might be a NT4
132                    server, fall back to MSRPC */
133                 if (status.error_type == ENUM_ADS_ERROR_SYSTEM &&
134                     status.err.rc == ECONNREFUSED) {
135                         /* 'reconnect_methods' is the MS-RPC backend. */
136                         DEBUG(1,("Trying MSRPC methods\n"));
137                         domain->backend = &reconnect_methods;
138                 }
139                 return NULL;
140         }
141
142         /* set the flag that says we don't own the memory even 
143            though we do so that ads_destroy() won't destroy the 
144            structure we pass back by reference */
145
146         ads->is_mine = False;
147
148         domain->private_data = (void *)ads;
149         return ads;
150 }
151
152
153 /* Query display info for a realm. This is the basic user list fn */
154 static NTSTATUS query_user_list(struct winbindd_domain *domain,
155                                TALLOC_CTX *mem_ctx,
156                                uint32 *num_entries, 
157                                WINBIND_USERINFO **info)
158 {
159         ADS_STRUCT *ads = NULL;
160         const char *attrs[] = { "*", NULL };
161         int i, count;
162         ADS_STATUS rc;
163         LDAPMessage *res = NULL;
164         LDAPMessage *msg = NULL;
165         NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
166
167         *num_entries = 0;
168
169         DEBUG(3,("ads: query_user_list\n"));
170
171         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->native_mode && 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         (*sid_mem) = TALLOC_ZERO_ARRAY(mem_ctx, DOM_SID, num_members);
911         (*name_types) = TALLOC_ZERO_ARRAY(mem_ctx, uint32, num_members);
912         (*names) = TALLOC_ZERO_ARRAY(mem_ctx, char *, num_members);
913
914         if ((num_members != 0) &&
915             ((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  
922         for (i=0;i<num_members;i++) {
923                 uint32 name_type;
924                 char *name, *domain_name, *dn;
925                 DOM_SID sid;
926
927                 if ((!ads_get_sid_from_extended_dn(mem_ctx, members[i], ADS_EXTENDED_DN_HEX_STRING, &sid)) ||
928                     (!ads_get_dn_from_extended_dn(mem_ctx, members[i], &dn)))
929                 {
930                         status = NT_STATUS_INVALID_PARAMETER;
931                         goto done;
932                 }
933
934                 if (lookup_cached_sid(mem_ctx, &sid, &domain_name, &name, &name_type)) {
935
936                         DEBUG(10,("ads: lookup_groupmem: got sid %s from cache\n", 
937                                 sid_string_static(&sid)));
938
939                         (*names)[*num_names] = CONST_DISCARD(char *,name);
940                         (*name_types)[*num_names] = name_type;
941                         sid_copy(&(*sid_mem)[*num_names], &sid);
942
943                         (*num_names)++;
944
945                         continue;
946                 }
947
948                 if (dn_lookup(ads, mem_ctx, dn, &name, &name_type, &sid)) {
949
950                         DEBUG(10,("ads: lookup_groupmem: got sid %s from dn_lookup\n", 
951                                 sid_string_static(&sid)));
952                         
953                         (*names)[*num_names] = name;
954                         (*name_types)[*num_names] = name_type;
955                         sid_copy(&(*sid_mem)[*num_names], &sid);
956                         
957                         (*num_names)++;
958
959                 }
960         }       
961
962         status = NT_STATUS_OK;
963         DEBUG(3,("ads lookup_groupmem for sid=%s succeeded\n", sid_to_string(sid_string, group_sid)));
964 done:
965
966         if (res) 
967                 ads_msgfree(ads, res);
968
969         return status;
970 }
971
972 /* find the sequence number for a domain */
973 static NTSTATUS sequence_number(struct winbindd_domain *domain, uint32 *seq)
974 {
975         ADS_STRUCT *ads = NULL;
976         ADS_STATUS rc;
977
978         DEBUG(3,("ads: fetch sequence_number for %s\n", domain->name));
979
980         *seq = DOM_SEQUENCE_NONE;
981
982         ads = ads_cached_connection(domain);
983         
984         if (!ads) {
985                 domain->last_status = NT_STATUS_SERVER_DISABLED;
986                 return NT_STATUS_UNSUCCESSFUL;
987         }
988
989         rc = ads_USN(ads, seq);
990         
991         if (!ADS_ERR_OK(rc)) {
992         
993                 /* its a dead connection, destroy it */
994
995                 if (domain->private_data) {
996                         ads = (ADS_STRUCT *)domain->private_data;
997                         ads->is_mine = True;
998                         ads_destroy(&ads);
999                         ads_kdestroy("MEMORY:winbind_ccache");
1000                         domain->private_data = NULL;
1001                 }
1002         }
1003         return ads_ntstatus(rc);
1004 }
1005
1006 /* get a list of trusted domains */
1007 static NTSTATUS trusted_domains(struct winbindd_domain *domain,
1008                                 TALLOC_CTX *mem_ctx,
1009                                 uint32 *num_domains,
1010                                 char ***names,
1011                                 char ***alt_names,
1012                                 DOM_SID **dom_sids)
1013 {
1014         NTSTATUS                result = NT_STATUS_UNSUCCESSFUL;
1015         struct ds_domain_trust  *domains = NULL;
1016         int                     count = 0;
1017         int                     i;
1018         uint32                  flags = DS_DOMAIN_IN_FOREST | DS_DOMAIN_DIRECT_OUTBOUND;
1019         struct rpc_pipe_client *cli;
1020
1021         DEBUG(3,("ads: trusted_domains\n"));
1022
1023         *num_domains = 0;
1024         *alt_names   = NULL;
1025         *names       = NULL;
1026         *dom_sids    = NULL;
1027
1028         result = cm_connect_netlogon(domain, &cli);
1029
1030         if (!NT_STATUS_IS_OK(result)) {
1031                 DEBUG(5, ("trusted_domains: Could not open a connection to %s "
1032                           "for PIPE_NETLOGON (%s)\n", 
1033                           domain->name, nt_errstr(result)));
1034                 return NT_STATUS_UNSUCCESSFUL;
1035         }
1036         
1037         if ( NT_STATUS_IS_OK(result) ) {
1038                 result = rpccli_ds_enum_domain_trusts(cli, mem_ctx,
1039                                                       cli->cli->desthost, 
1040                                                       flags, &domains,
1041                                                       (unsigned int *)&count);
1042         }
1043         
1044         if ( NT_STATUS_IS_OK(result) && count) {
1045         
1046                 /* Allocate memory for trusted domain names and sids */
1047
1048                 if ( !(*names = TALLOC_ARRAY(mem_ctx, char *, count)) ) {
1049                         DEBUG(0, ("trusted_domains: out of memory\n"));
1050                         return NT_STATUS_NO_MEMORY;
1051                 }
1052
1053                 if ( !(*alt_names = TALLOC_ARRAY(mem_ctx, char *, count)) ) {
1054                         DEBUG(0, ("trusted_domains: out of memory\n"));
1055                         return NT_STATUS_NO_MEMORY;
1056                 }
1057
1058                 if ( !(*dom_sids = TALLOC_ARRAY(mem_ctx, DOM_SID, count)) ) {
1059                         DEBUG(0, ("trusted_domains: out of memory\n"));
1060                         return NT_STATUS_NO_MEMORY;
1061                 }
1062
1063                 /* Copy across names and sids */
1064
1065                 for (i = 0; i < count; i++) {
1066                         (*names)[i] = domains[i].netbios_domain;
1067                         (*alt_names)[i] = domains[i].dns_domain;
1068
1069                         sid_copy(&(*dom_sids)[i], &domains[i].sid);
1070                 }
1071
1072                 *num_domains = count;   
1073         }
1074
1075         return result;
1076 }
1077
1078 /* the ADS backend methods are exposed via this structure */
1079 struct winbindd_methods ads_methods = {
1080         True,
1081         query_user_list,
1082         enum_dom_groups,
1083         enum_local_groups,
1084         msrpc_name_to_sid,
1085         msrpc_sid_to_name,
1086         msrpc_rids_to_names,
1087         query_user,
1088         lookup_usergroups,
1089         msrpc_lookup_useraliases,
1090         lookup_groupmem,
1091         sequence_number,
1092         msrpc_lockout_policy,
1093         msrpc_password_policy,
1094         trusted_domains,
1095 };
1096
1097 #endif