r20117: 1st Error in the merge
[nivanova/samba-autobuild/.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 /*
34   return our ads connections structure for a domain. We keep the connection
35   open to make things faster
36 */
37 static ADS_STRUCT *ads_cached_connection(struct winbindd_domain *domain)
38 {
39         ADS_STRUCT *ads;
40         ADS_STATUS status;
41         enum wb_posix_mapping map_type;
42
43         DEBUG(10,("ads_cached_connection\n"));
44
45         if (domain->private_data) {
46                 ads = (ADS_STRUCT *)domain->private_data;
47
48                 /* check for a valid structure */
49
50                 DEBUG(7, ("Current tickets expire at %d, time is now %d\n",
51                           (uint32) ads->auth.expire, (uint32) time(NULL)));
52                 if ( ads->config.realm && (ads->auth.expire > time(NULL))) {
53                         return ads;
54                 }
55                 else {
56                         /* we own this ADS_STRUCT so make sure it goes away */
57                         ads->is_mine = True;
58                         ads_destroy( &ads );
59                         ads_kdestroy("MEMORY:winbind_ccache");
60                         domain->private_data = NULL;
61                 }       
62         }
63
64         /* we don't want this to affect the users ccache */
65         setenv("KRB5CCNAME", "MEMORY:winbind_ccache", 1);
66
67         ads = ads_init(domain->alt_name, domain->name, NULL);
68         if (!ads) {
69                 DEBUG(1,("ads_init for domain %s failed\n", domain->name));
70                 return NULL;
71         }
72
73         /* the machine acct password might have change - fetch it every time */
74
75         SAFE_FREE(ads->auth.password);
76         SAFE_FREE(ads->auth.realm);
77
78         if ( IS_DC ) {
79                 DOM_SID sid;
80                 time_t last_set_time;
81
82                 if ( !secrets_fetch_trusted_domain_password( domain->name, &ads->auth.password, &sid, &last_set_time ) ) {
83                         ads_destroy( &ads );
84                         return NULL;
85                 }
86                 ads->auth.realm = SMB_STRDUP( ads->server.realm );
87                 strupper_m( ads->auth.realm );
88         }
89         else {
90                 struct winbindd_domain *our_domain = domain;
91
92                 ads->auth.password = secrets_fetch_machine_password(lp_workgroup(), NULL, NULL);
93
94                 /* always give preference to the alt_name in our 
95                    primary domain if possible */
96
97                 if ( !domain->primary )
98                         our_domain = find_our_domain();
99
100                 if ( our_domain->alt_name[0] != '\0' ) {
101                         ads->auth.realm = SMB_STRDUP( our_domain->alt_name );
102                         strupper_m( ads->auth.realm );
103                 }
104                 else
105                         ads->auth.realm = SMB_STRDUP( lp_realm() );
106         }
107
108         ads->auth.renewable = WINBINDD_PAM_AUTH_KRB5_RENEW_TIME;
109
110         status = ads_connect(ads);
111         if (!ADS_ERR_OK(status) || !ads->config.realm) {
112                 DEBUG(1,("ads_connect for domain %s failed: %s\n", 
113                          domain->name, ads_errstr(status)));
114                 ads_destroy(&ads);
115
116                 /* if we get ECONNREFUSED then it might be a NT4
117                    server, fall back to MSRPC */
118                 if (status.error_type == ENUM_ADS_ERROR_SYSTEM &&
119                     status.err.rc == ECONNREFUSED) {
120                         extern struct winbindd_methods reconnect_methods;
121                         /* 'reconnect_methods' is the MS-RPC backend. */
122                         DEBUG(1,("Trying MSRPC methods\n"));
123                         domain->backend = &reconnect_methods;
124                 }
125                 return NULL;
126         }
127
128         map_type = get_nss_info(domain->name);
129
130         if ((map_type == WB_POSIX_MAP_RFC2307)||
131             (map_type == WB_POSIX_MAP_SFU)) {
132         
133                 status = ads_check_posix_schema_mapping(ads, map_type);
134                 if (!ADS_ERR_OK(status)) {
135                         DEBUG(10,("ads_check_posix_schema_mapping failed "
136                                   "with: %s\n", ads_errstr(status)));
137                 } 
138         }
139
140         /* set the flag that says we don't own the memory even 
141            though we do so that ads_destroy() won't destroy the 
142            structure we pass back by reference */
143
144         ads->is_mine = False;
145
146         domain->private_data = (void *)ads;
147         return ads;
148 }
149
150
151 /* Query display info for a realm. This is the basic user list fn */
152 static NTSTATUS query_user_list(struct winbindd_domain *domain,
153                                TALLOC_CTX *mem_ctx,
154                                uint32 *num_entries, 
155                                WINBIND_USERINFO **info)
156 {
157         ADS_STRUCT *ads = NULL;
158         const char *attrs[] = {"userPrincipalName",
159                                "sAMAccountName",
160                                "name", "objectSid", "primaryGroupID", 
161                                "sAMAccountType", 
162                                ADS_ATTR_SFU_HOMEDIR_OID, 
163                                ADS_ATTR_SFU_SHELL_OID,
164                                ADS_ATTR_SFU_GECOS_OID,
165                                ADS_ATTR_RFC2307_HOMEDIR_OID,
166                                ADS_ATTR_RFC2307_SHELL_OID,
167                                ADS_ATTR_RFC2307_GECOS_OID,
168                                NULL};
169         int i, count;
170         ADS_STATUS rc;
171         LDAPMessage *res = NULL;
172         LDAPMessage *msg = NULL;
173         NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
174
175         *num_entries = 0;
176
177         DEBUG(3,("ads: query_user_list\n"));
178
179         ads = ads_cached_connection(domain);
180         
181         if (!ads) {
182                 domain->last_status = NT_STATUS_SERVER_DISABLED;
183                 goto done;
184         }
185
186         rc = ads_search_retry(ads, &res, "(objectCategory=user)", attrs);
187         if (!ADS_ERR_OK(rc) || !res) {
188                 DEBUG(1,("query_user_list ads_search: %s\n", ads_errstr(rc)));
189                 goto done;
190         }
191
192         count = ads_count_replies(ads, res);
193         if (count == 0) {
194                 DEBUG(1,("query_user_list: No users found\n"));
195                 goto done;
196         }
197
198         (*info) = TALLOC_ZERO_ARRAY(mem_ctx, WINBIND_USERINFO, count);
199         if (!*info) {
200                 status = NT_STATUS_NO_MEMORY;
201                 goto done;
202         }
203
204         i = 0;
205
206         for (msg = ads_first_entry(ads, res); msg; msg = ads_next_entry(ads, msg)) {
207                 char *name, *gecos = NULL;
208                 char *homedir = NULL;
209                 char *shell = NULL;
210                 uint32 group;
211                 uint32 atype;
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 (get_nss_info(domain->name) && ads->schema.map_type) {
222
223                         DEBUG(10,("pulling posix attributes (%s schema)\n", 
224                                 wb_posix_map_str(ads->schema.map_type)));
225
226                         homedir = ads_pull_string(ads, mem_ctx, msg, 
227                                                   ads->schema.posix_homedir_attr);
228                         shell   = ads_pull_string(ads, mem_ctx, msg, 
229                                                   ads->schema.posix_shell_attr);
230                         gecos   = ads_pull_string(ads, mem_ctx, msg, 
231                                                   ads->schema.posix_gecos_attr);
232                 }
233
234                 if (gecos == NULL) {
235                         gecos = ads_pull_string(ads, mem_ctx, msg, "name");
236                 }
237         
238                 if (!ads_pull_sid(ads, msg, "objectSid",
239                                   &(*info)[i].user_sid)) {
240                         DEBUG(1,("No sid for %s !?\n", name));
241                         continue;
242                 }
243                 if (!ads_pull_uint32(ads, msg, "primaryGroupID", &group)) {
244                         DEBUG(1,("No primary group for %s !?\n", name));
245                         continue;
246                 }
247
248                 (*info)[i].acct_name = name;
249                 (*info)[i].full_name = gecos;
250                 (*info)[i].homedir = homedir;
251                 (*info)[i].shell = shell;
252                 sid_compose(&(*info)[i].group_sid, &domain->sid, group);
253                 i++;
254         }
255
256         (*num_entries) = i;
257         status = NT_STATUS_OK;
258
259         DEBUG(3,("ads query_user_list gave %d entries\n", (*num_entries)));
260
261 done:
262         if (res) 
263                 ads_msgfree(ads, res);
264
265         return status;
266 }
267
268 /* list all domain groups */
269 static NTSTATUS enum_dom_groups(struct winbindd_domain *domain,
270                                 TALLOC_CTX *mem_ctx,
271                                 uint32 *num_entries, 
272                                 struct acct_info **info)
273 {
274         ADS_STRUCT *ads = NULL;
275         const char *attrs[] = {"userPrincipalName", "sAMAccountName",
276                                "name", "objectSid", NULL};
277         int i, count;
278         ADS_STATUS rc;
279         LDAPMessage *res = NULL;
280         LDAPMessage *msg = NULL;
281         NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
282         const char *filter;
283         BOOL enum_dom_local_groups = False;
284
285         *num_entries = 0;
286
287         DEBUG(3,("ads: enum_dom_groups\n"));
288
289         /* only grab domain local groups for our domain */
290         if ( domain->native_mode && 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 /* convert a DN to a name, SID and name type 
406    this might become a major speed bottleneck if groups have
407    lots of users, in which case we could cache the results
408 */
409 static BOOL dn_lookup(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
410                       const char *dn,
411                       char **name, uint32 *name_type, DOM_SID *sid)
412 {
413         LDAPMessage *res = NULL;
414         const char *attrs[] = {"userPrincipalName", "sAMAccountName",
415                                "objectSid", "sAMAccountType", NULL};
416         ADS_STATUS rc;
417         uint32 atype;
418         DEBUG(3,("ads: dn_lookup\n"));
419
420         rc = ads_search_retry_dn(ads, &res, dn, attrs);
421
422         if (!ADS_ERR_OK(rc) || !res) {
423                 goto failed;
424         }
425
426         (*name) = ads_pull_username(ads, mem_ctx, res);
427
428         if (!ads_pull_uint32(ads, res, "sAMAccountType", &atype)) {
429                 goto failed;
430         }
431         (*name_type) = ads_atype_map(atype);
432
433         if (!ads_pull_sid(ads, res, "objectSid", sid)) {
434                 goto failed;
435         }
436
437         if (res) 
438                 ads_msgfree(ads, res);
439
440         return True;
441
442 failed:
443         if (res) 
444                 ads_msgfree(ads, res);
445
446         return False;
447 }
448
449 /* Lookup user information from a rid */
450 static NTSTATUS query_user(struct winbindd_domain *domain, 
451                            TALLOC_CTX *mem_ctx, 
452                            const DOM_SID *sid, 
453                            WINBIND_USERINFO *info)
454 {
455         ADS_STRUCT *ads = NULL;
456         const char *attrs[] = {"userPrincipalName", 
457                                "sAMAccountName",
458                                "name", 
459                                "primaryGroupID", 
460                                ADS_ATTR_SFU_HOMEDIR_OID, 
461                                ADS_ATTR_SFU_SHELL_OID,
462                                ADS_ATTR_SFU_GECOS_OID,
463                                ADS_ATTR_RFC2307_HOMEDIR_OID,
464                                ADS_ATTR_RFC2307_SHELL_OID,
465                                ADS_ATTR_RFC2307_GECOS_OID,
466                                NULL};
467         ADS_STATUS rc;
468         int count;
469         LDAPMessage *msg = NULL;
470         char *ldap_exp;
471         char *sidstr;
472         uint32 group_rid;
473         NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
474
475         DEBUG(3,("ads: query_user\n"));
476
477         ads = ads_cached_connection(domain);
478         
479         if (!ads) {
480                 domain->last_status = NT_STATUS_SERVER_DISABLED;
481                 goto done;
482         }
483
484         sidstr = sid_binstring(sid);
485         asprintf(&ldap_exp, "(objectSid=%s)", sidstr);
486         rc = ads_search_retry(ads, &msg, ldap_exp, attrs);
487         free(ldap_exp);
488         free(sidstr);
489         if (!ADS_ERR_OK(rc) || !msg) {
490                 DEBUG(1,("query_user(sid=%s) ads_search: %s\n",
491                          sid_string_static(sid), ads_errstr(rc)));
492                 goto done;
493         }
494
495         count = ads_count_replies(ads, msg);
496         if (count != 1) {
497                 DEBUG(1,("query_user(sid=%s): Not found\n",
498                          sid_string_static(sid)));
499                 goto done;
500         }
501
502         info->acct_name = ads_pull_username(ads, mem_ctx, msg);
503
504         if (get_nss_info(domain->name) && ads->schema.map_type) {
505
506                 DEBUG(10,("pulling posix attributes (%s schema)\n", 
507                         wb_posix_map_str(ads->schema.map_type)));
508                 
509                 info->homedir   = ads_pull_string(ads, mem_ctx, msg, 
510                                                   ads->schema.posix_homedir_attr);
511                 info->shell     = ads_pull_string(ads, mem_ctx, msg, 
512                                                   ads->schema.posix_shell_attr);
513                 info->full_name = ads_pull_string(ads, mem_ctx, msg,
514                                                   ads->schema.posix_gecos_attr);
515         }
516
517         if (info->full_name == NULL) {
518                 info->full_name = ads_pull_string(ads, mem_ctx, msg, "name");
519         }
520
521         if (!ads_pull_uint32(ads, msg, "primaryGroupID", &group_rid)) {
522                 DEBUG(1,("No primary group for %s !?\n",
523                          sid_string_static(sid)));
524                 goto done;
525         }
526
527         sid_copy(&info->user_sid, sid);
528         sid_compose(&info->group_sid, &domain->sid, group_rid);
529
530         status = NT_STATUS_OK;
531
532         DEBUG(3,("ads query_user gave %s\n", info->acct_name));
533 done:
534         if (msg) 
535                 ads_msgfree(ads, msg);
536
537         return status;
538 }
539
540 /* Lookup groups a user is a member of - alternate method, for when
541    tokenGroups are not available. */
542 static NTSTATUS lookup_usergroups_member(struct winbindd_domain *domain,
543                                          TALLOC_CTX *mem_ctx,
544                                          const char *user_dn, 
545                                          DOM_SID *primary_group,
546                                          size_t *p_num_groups, DOM_SID **user_sids)
547 {
548         ADS_STATUS rc;
549         NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
550         int count;
551         LDAPMessage *res = NULL;
552         LDAPMessage *msg = NULL;
553         char *ldap_exp;
554         ADS_STRUCT *ads;
555         const char *group_attrs[] = {"objectSid", NULL};
556         char *escaped_dn;
557         size_t num_groups = 0;
558
559         DEBUG(3,("ads: lookup_usergroups_member\n"));
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         int count;
646         LDAPMessage *res = NULL;
647         ADS_STRUCT *ads;
648         const char *attrs[] = {"memberOf", NULL};
649         size_t num_groups = 0;
650         DOM_SID *group_sids = NULL;
651         int i;
652
653         DEBUG(3,("ads: lookup_usergroups_memberof\n"));
654
655         ads = ads_cached_connection(domain);
656
657         if (!ads) {
658                 domain->last_status = NT_STATUS_SERVER_DISABLED;
659                 goto done;
660         }
661
662         rc = ads_search_retry_extended_dn(ads, &res, user_dn, attrs, 
663                                           ADS_EXTENDED_DN_HEX_STRING);
664         
665         if (!ADS_ERR_OK(rc) || !res) {
666                 DEBUG(1,("lookup_usergroups_memberof ads_search member=%s: %s\n", 
667                         user_dn, ads_errstr(rc)));
668                 return ads_ntstatus(rc);
669         }
670         
671         count = ads_count_replies(ads, res);
672
673         if (count == 0) {
674                 status = NT_STATUS_NO_SUCH_USER;
675                 goto done;
676         }
677         
678         *user_sids = NULL;
679         num_groups = 0;
680
681         /* always add the primary group to the sid array */
682         if (!add_sid_to_array(mem_ctx, primary_group, user_sids, &num_groups)) {
683                 status = NT_STATUS_NO_MEMORY;
684                 goto done;
685         }
686
687         count = ads_pull_sids_from_extendeddn(ads, mem_ctx, res, "memberOf", 
688                                               ADS_EXTENDED_DN_HEX_STRING, 
689                                               &group_sids);
690         if (count == 0) {
691                 DEBUG(1,("No memberOf for this user?!?\n"));
692                 status = NT_STATUS_NO_MEMORY;
693                 goto done;
694         }
695
696         for (i=0; i<count; i++) {
697
698                 /* ignore Builtin groups from ADS - Guenther */
699                 if (sid_check_is_in_builtin(&group_sids[i])) {
700                         continue;
701                 }
702                        
703                 if (!add_sid_to_array(mem_ctx, &group_sids[i], user_sids,
704                                  &num_groups)) {
705                         status = NT_STATUS_NO_MEMORY;
706                         goto done;
707                 }
708         
709         }
710
711         *p_num_groups = num_groups;
712         status = (*user_sids != NULL) ? NT_STATUS_OK : NT_STATUS_NO_MEMORY;
713
714         DEBUG(3,("ads lookup_usergroups (memberof) succeeded for dn=%s\n", user_dn));
715 done:
716         TALLOC_FREE(group_sids);
717         if (res) 
718                 ads_msgfree(ads, res);
719
720         return status;
721 }
722
723
724 /* Lookup groups a user is a member of. */
725 static NTSTATUS lookup_usergroups(struct winbindd_domain *domain,
726                                   TALLOC_CTX *mem_ctx,
727                                   const DOM_SID *sid, 
728                                   uint32 *p_num_groups, DOM_SID **user_sids)
729 {
730         ADS_STRUCT *ads = NULL;
731         const char *attrs[] = {"tokenGroups", "primaryGroupID", NULL};
732         ADS_STATUS rc;
733         int count;
734         LDAPMessage *msg = NULL;
735         char *user_dn = NULL;
736         DOM_SID *sids;
737         int i;
738         DOM_SID primary_group;
739         uint32 primary_group_rid;
740         fstring sid_string;
741         NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
742         size_t num_groups = 0;
743
744         DEBUG(3,("ads: lookup_usergroups\n"));
745         *p_num_groups = 0;
746
747         status = lookup_usergroups_cached(domain, mem_ctx, sid, 
748                                           p_num_groups, user_sids);
749         if (NT_STATUS_IS_OK(status)) {
750                 return NT_STATUS_OK;
751         }
752
753         ads = ads_cached_connection(domain);
754         
755         if (!ads) {
756                 domain->last_status = NT_STATUS_SERVER_DISABLED;
757                 status = NT_STATUS_SERVER_DISABLED;
758                 goto done;
759         }
760
761         rc = ads_search_retry_sid(ads, &msg, sid, attrs);
762
763         if (!ADS_ERR_OK(rc)) {
764                 status = ads_ntstatus(rc);
765                 DEBUG(1,("lookup_usergroups(sid=%s) ads_search tokenGroups: %s\n", 
766                          sid_to_string(sid_string, sid), ads_errstr(rc)));
767                 goto done;
768         }
769         
770         count = ads_count_replies(ads, msg);
771         if (count != 1) {
772                 status = NT_STATUS_UNSUCCESSFUL;
773                 DEBUG(1,("lookup_usergroups(sid=%s) ads_search tokenGroups: "
774                          "invalid number of results (count=%d)\n", 
775                          sid_to_string(sid_string, sid), count));
776                 goto done;
777         }
778
779         if (!msg) {
780                 DEBUG(1,("lookup_usergroups(sid=%s) ads_search tokenGroups: NULL msg\n", 
781                          sid_to_string(sid_string, sid)));
782                 status = NT_STATUS_UNSUCCESSFUL;
783                 goto done;
784         }
785
786         user_dn = ads_get_dn(ads, msg);
787         if (user_dn == NULL) {
788                 status = NT_STATUS_NO_MEMORY;
789                 goto done;
790         }
791
792         if (!ads_pull_uint32(ads, msg, "primaryGroupID", &primary_group_rid)) {
793                 DEBUG(1,("%s: No primary group for sid=%s !?\n", 
794                          domain->name, sid_to_string(sid_string, sid)));
795                 goto done;
796         }
797
798         sid_copy(&primary_group, &domain->sid);
799         sid_append_rid(&primary_group, primary_group_rid);
800
801         count = ads_pull_sids(ads, mem_ctx, msg, "tokenGroups", &sids);
802
803         /* there must always be at least one group in the token, 
804            unless we are talking to a buggy Win2k server */
805
806         /* actually this only happens when the machine account has no read
807          * permissions on the tokenGroup attribute - gd */
808
809         if (count == 0) {
810
811                 /* no tokenGroups */
812                 
813                 /* lookup what groups this user is a member of by DN search on
814                  * "memberOf" */
815
816                 status = lookup_usergroups_memberof(domain, mem_ctx, user_dn,
817                                                     &primary_group,
818                                                     &num_groups, user_sids);
819                 *p_num_groups = (uint32)num_groups;
820                 if (NT_STATUS_IS_OK(status)) {
821                         goto done;
822                 }
823
824                 /* lookup what groups this user is a member of by DN search on
825                  * "member" */
826
827                 status = lookup_usergroups_member(domain, mem_ctx, user_dn, 
828                                                   &primary_group,
829                                                   &num_groups, user_sids);
830                 *p_num_groups = (uint32)num_groups;
831                 goto done;
832         }
833
834         *user_sids = NULL;
835         num_groups = 0;
836
837         if (!add_sid_to_array(mem_ctx, &primary_group, user_sids, &num_groups)) {
838                 status = NT_STATUS_NO_MEMORY;
839                 goto done;
840         }
841         
842         for (i=0;i<count;i++) {
843
844                 /* ignore Builtin groups from ADS - Guenther */
845                 if (sid_check_is_in_builtin(&sids[i])) {
846                         continue;
847                 }
848                                
849                 if (!add_sid_to_array_unique(mem_ctx, &sids[i],
850                                         user_sids, &num_groups)) {
851                         status = NT_STATUS_NO_MEMORY;
852                         goto done;
853                 }
854         }
855
856         *p_num_groups = (uint32)num_groups;
857         status = (*user_sids != NULL) ? NT_STATUS_OK : NT_STATUS_NO_MEMORY;
858
859         DEBUG(3,("ads lookup_usergroups (tokenGroups) succeeded for sid=%s\n",
860                  sid_to_string(sid_string, sid)));
861 done:
862         ads_memfree(ads, user_dn);
863         ads_msgfree(ads, msg);
864         return status;
865 }
866
867 /*
868   find the members of a group, given a group rid and domain
869  */
870 static NTSTATUS lookup_groupmem(struct winbindd_domain *domain,
871                                 TALLOC_CTX *mem_ctx,
872                                 const DOM_SID *group_sid, uint32 *num_names, 
873                                 DOM_SID **sid_mem, char ***names, 
874                                 uint32 **name_types)
875 {
876         ADS_STATUS rc;
877         int count;
878         LDAPMessage *res=NULL;
879         ADS_STRUCT *ads = NULL;
880         char *ldap_exp;
881         NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
882         char *sidstr;
883         char **members;
884         int i;
885         size_t num_members;
886         fstring sid_string;
887         BOOL more_values;
888         const char **attrs;
889         uint32 first_usn;
890         uint32 current_usn;
891         int num_retries = 0;
892
893         DEBUG(10,("ads: lookup_groupmem %s sid=%s\n", domain->name, 
894                   sid_string_static(group_sid)));
895
896         *num_names = 0;
897
898         ads = ads_cached_connection(domain);
899         
900         if (!ads) {
901                 domain->last_status = NT_STATUS_SERVER_DISABLED;
902                 goto done;
903         }
904
905         if ((sidstr = sid_binstring(group_sid)) == NULL) {
906                 status = NT_STATUS_NO_MEMORY;
907                 goto done;
908         }
909
910         /* search for all members of the group */
911         if (!(ldap_exp = talloc_asprintf(mem_ctx, "(objectSid=%s)",sidstr))) {
912                 SAFE_FREE(sidstr);
913                 DEBUG(1, ("ads: lookup_groupmem: tallloc_asprintf for ldap_exp failed!\n"));
914                 status = NT_STATUS_NO_MEMORY;
915                 goto done;
916         }
917         SAFE_FREE(sidstr);
918
919         members = NULL;
920         num_members = 0;
921
922         if ((attrs = TALLOC_ARRAY(mem_ctx, const char *, 3)) == NULL) {
923                 status = NT_STATUS_NO_MEMORY;
924                 goto done;
925         }
926                 
927         attrs[1] = talloc_strdup(mem_ctx, "usnChanged");
928         attrs[2] = NULL;
929                 
930         do {
931                 if (num_members == 0) 
932                         attrs[0] = talloc_strdup(mem_ctx, "member");
933
934                 DEBUG(10, ("Searching for attrs[0] = %s, attrs[1] = %s\n", attrs[0], attrs[1]));
935
936                 rc = ads_search_retry(ads, &res, ldap_exp, attrs);
937
938                 if (!ADS_ERR_OK(rc) || !res) {
939                         DEBUG(1,("ads: lookup_groupmem ads_search: %s\n",
940                                  ads_errstr(rc)));
941                         status = ads_ntstatus(rc);
942                         goto done;
943                 }
944
945                 count = ads_count_replies(ads, res);
946                 if (count == 0)
947                         break;
948
949                 if (num_members == 0) {
950                         if (!ads_pull_uint32(ads, res, "usnChanged", &first_usn)) {
951                                 DEBUG(1, ("ads: lookup_groupmem could not pull usnChanged!\n"));
952                                 goto done;
953                         }
954                 }
955
956                 if (!ads_pull_uint32(ads, res, "usnChanged", &current_usn)) {
957                         DEBUG(1, ("ads: lookup_groupmem could not pull usnChanged!\n"));
958                         goto done;
959                 }
960
961                 if (first_usn != current_usn) {
962                         DEBUG(5, ("ads: lookup_groupmem USN on this record changed"
963                                   " - restarting search\n"));
964                         if (num_retries < 5) {
965                                 num_retries++;
966                                 num_members = 0;
967                                 continue;
968                         } else {
969                                 DEBUG(5, ("ads: lookup_groupmem USN on this record changed"
970                                           " - restarted search too many times, aborting!\n"));
971                                 status = NT_STATUS_UNSUCCESSFUL;
972                                 goto done;
973                         }
974                 }
975
976                 members = ads_pull_strings_range(ads, mem_ctx, res,
977                                                  "member",
978                                                  members,
979                                                  &attrs[0],
980                                                  &num_members,
981                                                  &more_values);
982
983                 if ((members == NULL) || (num_members == 0))
984                         break;
985
986         } while (more_values);
987                 
988         /* now we need to turn a list of members into rids, names and name types 
989            the problem is that the members are in the form of distinguised names
990         */
991
992         (*sid_mem) = TALLOC_ZERO_ARRAY(mem_ctx, DOM_SID, num_members);
993         (*name_types) = TALLOC_ZERO_ARRAY(mem_ctx, uint32, num_members);
994         (*names) = TALLOC_ZERO_ARRAY(mem_ctx, char *, num_members);
995
996         if ((num_members != 0) &&
997             ((members == NULL) || (*sid_mem == NULL) ||
998              (*name_types == NULL) || (*names == NULL))) {
999                 DEBUG(1, ("talloc failed\n"));
1000                 status = NT_STATUS_NO_MEMORY;
1001                 goto done;
1002         }
1003  
1004         for (i=0;i<num_members;i++) {
1005                 uint32 name_type;
1006                 char *name;
1007                 DOM_SID sid;
1008
1009                 if (dn_lookup(ads, mem_ctx, members[i], &name, &name_type, &sid)) {
1010                     (*names)[*num_names] = name;
1011                     (*name_types)[*num_names] = name_type;
1012                     sid_copy(&(*sid_mem)[*num_names], &sid);
1013                     (*num_names)++;
1014                 }
1015         }       
1016
1017         status = NT_STATUS_OK;
1018         DEBUG(3,("ads lookup_groupmem for sid=%s\n", sid_to_string(sid_string, group_sid)));
1019 done:
1020
1021         if (res) 
1022                 ads_msgfree(ads, res);
1023
1024         return status;
1025 }
1026
1027 /* find the sequence number for a domain */
1028 static NTSTATUS sequence_number(struct winbindd_domain *domain, uint32 *seq)
1029 {
1030         ADS_STRUCT *ads = NULL;
1031         ADS_STATUS rc;
1032
1033         DEBUG(3,("ads: fetch sequence_number for %s\n", domain->name));
1034
1035         *seq = DOM_SEQUENCE_NONE;
1036
1037         ads = ads_cached_connection(domain);
1038         
1039         if (!ads) {
1040                 domain->last_status = NT_STATUS_SERVER_DISABLED;
1041                 return NT_STATUS_UNSUCCESSFUL;
1042         }
1043
1044         rc = ads_USN(ads, seq);
1045         
1046         if (!ADS_ERR_OK(rc)) {
1047         
1048                 /* its a dead connection ; don't destroy it 
1049                    through since ads_USN() has already done 
1050                    that indirectly */
1051                    
1052                 domain->private_data = NULL;
1053         }
1054         return ads_ntstatus(rc);
1055 }
1056
1057 /* get a list of trusted domains */
1058 static NTSTATUS trusted_domains(struct winbindd_domain *domain,
1059                                 TALLOC_CTX *mem_ctx,
1060                                 uint32 *num_domains,
1061                                 char ***names,
1062                                 char ***alt_names,
1063                                 DOM_SID **dom_sids)
1064 {
1065         NTSTATUS                result = NT_STATUS_UNSUCCESSFUL;
1066         struct ds_domain_trust  *domains = NULL;
1067         int                     count = 0;
1068         int                     i;
1069         uint32                  flags = DS_DOMAIN_IN_FOREST | DS_DOMAIN_DIRECT_OUTBOUND;
1070         struct rpc_pipe_client *cli;
1071
1072         DEBUG(3,("ads: trusted_domains\n"));
1073
1074         *num_domains = 0;
1075         *alt_names   = NULL;
1076         *names       = NULL;
1077         *dom_sids    = NULL;
1078
1079         result = cm_connect_netlogon(domain, &cli);
1080
1081         if (!NT_STATUS_IS_OK(result)) {
1082                 DEBUG(5, ("trusted_domains: Could not open a connection to %s "
1083                           "for PIPE_NETLOGON (%s)\n", 
1084                           domain->name, nt_errstr(result)));
1085                 return NT_STATUS_UNSUCCESSFUL;
1086         }
1087         
1088         if ( NT_STATUS_IS_OK(result) ) {
1089                 result = rpccli_ds_enum_domain_trusts(cli, mem_ctx,
1090                                                       cli->cli->desthost, 
1091                                                       flags, &domains,
1092                                                       (unsigned int *)&count);
1093         }
1094         
1095         if ( NT_STATUS_IS_OK(result) && count) {
1096         
1097                 /* Allocate memory for trusted domain names and sids */
1098
1099                 if ( !(*names = TALLOC_ARRAY(mem_ctx, char *, count)) ) {
1100                         DEBUG(0, ("trusted_domains: out of memory\n"));
1101                         return NT_STATUS_NO_MEMORY;
1102                 }
1103
1104                 if ( !(*alt_names = TALLOC_ARRAY(mem_ctx, char *, count)) ) {
1105                         DEBUG(0, ("trusted_domains: out of memory\n"));
1106                         return NT_STATUS_NO_MEMORY;
1107                 }
1108
1109                 if ( !(*dom_sids = TALLOC_ARRAY(mem_ctx, DOM_SID, count)) ) {
1110                         DEBUG(0, ("trusted_domains: out of memory\n"));
1111                         return NT_STATUS_NO_MEMORY;
1112                 }
1113
1114                 /* Copy across names and sids */
1115
1116                 for (i = 0; i < count; i++) {
1117                         (*names)[i] = domains[i].netbios_domain;
1118                         (*alt_names)[i] = domains[i].dns_domain;
1119
1120                         sid_copy(&(*dom_sids)[i], &domains[i].sid);
1121                 }
1122
1123                 *num_domains = count;   
1124         }
1125
1126         return result;
1127 }
1128
1129 /* the ADS backend methods are exposed via this structure */
1130 struct winbindd_methods ads_methods = {
1131         True,
1132         query_user_list,
1133         enum_dom_groups,
1134         enum_local_groups,
1135         msrpc_name_to_sid,
1136         msrpc_sid_to_name,
1137         msrpc_rids_to_names,
1138         query_user,
1139         lookup_usergroups,
1140         msrpc_lookup_useraliases,
1141         lookup_groupmem,
1142         sequence_number,
1143         msrpc_lockout_policy,
1144         msrpc_password_policy,
1145         trusted_domains,
1146 };
1147
1148 #endif