r15697: I take no comments as no objections :)
[abartlet/samba.git/.git] / source3 / nsswitch / winbindd_ads.c
1 /*
2    Unix SMB/CIFS implementation.
3
4    Winbind ADS backend functions
5
6    Copyright (C) Andrew Tridgell 2001
7    Copyright (C) Andrew Bartlett <abartlet@samba.org> 2003
8    Copyright (C) Gerald (Jerry) Carter 2004
9    
10    This program is free software; you can redistribute it and/or modify
11    it under the terms of the GNU General Public License as published by
12    the Free Software Foundation; either version 2 of the License, or
13    (at your option) any later version.
14    
15    This program is distributed in the hope that it will be useful,
16    but WITHOUT ANY WARRANTY; without even the implied warranty of
17    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18    GNU General Public License for more details.
19    
20    You should have received a copy of the GNU General Public License
21    along with this program; if not, write to the Free Software
22    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
23 */
24
25 #include "includes.h"
26 #include "winbindd.h"
27
28 #ifdef HAVE_ADS
29
30 #undef DBGC_CLASS
31 #define DBGC_CLASS DBGC_WINBIND
32
33 /*
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                 extern struct winbindd_methods msrpc_methods, cache_methods;
113                 DEBUG(1,("ads_connect for domain %s failed: %s\n", 
114                          domain->name, ads_errstr(status)));
115                 ads_destroy(&ads);
116
117                 /* if we get ECONNREFUSED then it might be a NT4
118                    server, fall back to MSRPC */
119                 if (status.error_type == ENUM_ADS_ERROR_SYSTEM &&
120                     status.err.rc == ECONNREFUSED) {
121                         DEBUG(1,("Trying MSRPC methods\n"));
122                         if (domain->methods == &cache_methods) {
123                                 domain->backend = &msrpc_methods;
124                         } else {
125                                 domain->methods = &msrpc_methods;
126                         }
127                 }
128                 return NULL;
129         }
130
131         map_type = get_nss_info(domain->name);
132
133         if ((map_type == WB_POSIX_MAP_RFC2307)||
134             (map_type == WB_POSIX_MAP_SFU)) {
135         
136                 status = ads_check_posix_schema_mapping(ads, map_type);
137                 if (!ADS_ERR_OK(status)) {
138                         DEBUG(10,("ads_check_posix_schema_mapping failed "
139                                   "with: %s\n", ads_errstr(status)));
140                 } 
141         }
142
143         /* set the flag that says we don't own the memory even 
144            though we do so that ads_destroy() won't destroy the 
145            structure we pass back by reference */
146
147         ads->is_mine = False;
148
149         domain->private_data = (void *)ads;
150         return ads;
151 }
152
153
154 /* Query display info for a realm. This is the basic user list fn */
155 static NTSTATUS query_user_list(struct winbindd_domain *domain,
156                                TALLOC_CTX *mem_ctx,
157                                uint32 *num_entries, 
158                                WINBIND_USERINFO **info)
159 {
160         ADS_STRUCT *ads = NULL;
161         const char *attrs[] = {"userPrincipalName",
162                                "sAMAccountName",
163                                "name", "objectSid", "primaryGroupID", 
164                                "sAMAccountType", 
165                                ADS_ATTR_SFU_HOMEDIR_OID, 
166                                ADS_ATTR_SFU_SHELL_OID,
167                                ADS_ATTR_SFU_GECOS_OID,
168                                ADS_ATTR_RFC2307_HOMEDIR_OID,
169                                ADS_ATTR_RFC2307_SHELL_OID,
170                                ADS_ATTR_RFC2307_GECOS_OID,
171                                NULL};
172         int i, count;
173         ADS_STATUS rc;
174         void *res = NULL;
175         void *msg = NULL;
176         NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
177
178         *num_entries = 0;
179
180         DEBUG(3,("ads: query_user_list\n"));
181
182         ads = ads_cached_connection(domain);
183         
184         if (!ads) {
185                 domain->last_status = NT_STATUS_SERVER_DISABLED;
186                 goto done;
187         }
188
189         rc = ads_search_retry(ads, &res, "(objectCategory=user)", attrs);
190         if (!ADS_ERR_OK(rc) || !res) {
191                 DEBUG(1,("query_user_list ads_search: %s\n", ads_errstr(rc)));
192                 goto done;
193         }
194
195         count = ads_count_replies(ads, res);
196         if (count == 0) {
197                 DEBUG(1,("query_user_list: No users found\n"));
198                 goto done;
199         }
200
201         (*info) = TALLOC_ZERO_ARRAY(mem_ctx, WINBIND_USERINFO, count);
202         if (!*info) {
203                 status = NT_STATUS_NO_MEMORY;
204                 goto done;
205         }
206
207         i = 0;
208
209         for (msg = ads_first_entry(ads, res); msg; msg = ads_next_entry(ads, msg)) {
210                 char *name, *gecos = NULL;
211                 char *homedir = NULL;
212                 char *shell = NULL;
213                 uint32 group;
214                 uint32 atype;
215
216                 if (!ads_pull_uint32(ads, msg, "sAMAccountType", &atype) ||
217                     ads_atype_map(atype) != SID_NAME_USER) {
218                         DEBUG(1,("Not a user account? atype=0x%x\n", atype));
219                         continue;
220                 }
221
222                 name = ads_pull_username(ads, mem_ctx, msg);
223
224                 if (get_nss_info(domain->name) && ads->schema.map_type) {
225
226                         DEBUG(10,("pulling posix attributes (%s schema)\n", 
227                                 wb_posix_map_str(ads->schema.map_type)));
228
229                         homedir = ads_pull_string(ads, mem_ctx, msg, 
230                                                   ads->schema.posix_homedir_attr);
231                         shell   = ads_pull_string(ads, mem_ctx, msg, 
232                                                   ads->schema.posix_shell_attr);
233                         gecos   = ads_pull_string(ads, mem_ctx, msg, 
234                                                   ads->schema.posix_gecos_attr);
235                 }
236
237                 if (gecos == NULL) {
238                         gecos = ads_pull_string(ads, mem_ctx, msg, "name");
239                 }
240         
241                 if (!ads_pull_sid(ads, msg, "objectSid",
242                                   &(*info)[i].user_sid)) {
243                         DEBUG(1,("No sid for %s !?\n", name));
244                         continue;
245                 }
246                 if (!ads_pull_uint32(ads, msg, "primaryGroupID", &group)) {
247                         DEBUG(1,("No primary group for %s !?\n", name));
248                         continue;
249                 }
250
251                 (*info)[i].acct_name = name;
252                 (*info)[i].full_name = gecos;
253                 (*info)[i].homedir = homedir;
254                 (*info)[i].shell = shell;
255                 sid_compose(&(*info)[i].group_sid, &domain->sid, group);
256                 i++;
257         }
258
259         (*num_entries) = i;
260         status = NT_STATUS_OK;
261
262         DEBUG(3,("ads query_user_list gave %d entries\n", (*num_entries)));
263
264 done:
265         if (res) 
266                 ads_msgfree(ads, res);
267
268         return status;
269 }
270
271 /* list all domain groups */
272 static NTSTATUS enum_dom_groups(struct winbindd_domain *domain,
273                                 TALLOC_CTX *mem_ctx,
274                                 uint32 *num_entries, 
275                                 struct acct_info **info)
276 {
277         ADS_STRUCT *ads = NULL;
278         const char *attrs[] = {"userPrincipalName", "sAMAccountName",
279                                "name", "objectSid", NULL};
280         int i, count;
281         ADS_STATUS rc;
282         void *res = NULL;
283         void *msg = NULL;
284         NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
285         const char *filter;
286         BOOL enum_dom_local_groups = False;
287
288         *num_entries = 0;
289
290         DEBUG(3,("ads: enum_dom_groups\n"));
291
292         /* only grab domain local groups for our domain */
293         if ( domain->native_mode && strequal(lp_realm(), domain->alt_name)  ) {
294                 enum_dom_local_groups = True;
295         }
296
297         /* Workaround ADS LDAP bug present in MS W2K3 SP0 and W2K SP4 w/o
298          * rollup-fixes:
299          *
300          * According to Section 5.1(4) of RFC 2251 if a value of a type is it's
301          * default value, it MUST be absent. In case of extensible matching the
302          * "dnattr" boolean defaults to FALSE and so it must be only be present
303          * when set to TRUE. 
304          *
305          * When it is set to FALSE and the OpenLDAP lib (correctly) encodes a
306          * filter using bitwise matching rule then the buggy AD fails to decode
307          * the extensible match. As a workaround set it to TRUE and thereby add
308          * the dnAttributes "dn" field to cope with those older AD versions.
309          * It should not harm and won't put any additional load on the AD since
310          * none of the dn components have a bitmask-attribute.
311          *
312          * Thanks to Ralf Haferkamp for input and testing - Guenther */
313
314         filter = talloc_asprintf(mem_ctx, "(&(objectCategory=group)(&(groupType:dn:%s:=%d)(!(groupType:dn:%s:=%d))))", 
315                                  ADS_LDAP_MATCHING_RULE_BIT_AND, GROUP_TYPE_SECURITY_ENABLED,
316                                  ADS_LDAP_MATCHING_RULE_BIT_AND, 
317                                  enum_dom_local_groups ? GROUP_TYPE_BUILTIN_LOCAL_GROUP : GROUP_TYPE_RESOURCE_GROUP);
318
319         if (filter == NULL) {
320                 status = NT_STATUS_NO_MEMORY;
321                 goto done;
322         }
323
324         ads = ads_cached_connection(domain);
325
326         if (!ads) {
327                 domain->last_status = NT_STATUS_SERVER_DISABLED;
328                 goto done;
329         }
330
331         rc = ads_search_retry(ads, &res, filter, attrs);
332         if (!ADS_ERR_OK(rc) || !res) {
333                 DEBUG(1,("enum_dom_groups ads_search: %s\n", ads_errstr(rc)));
334                 goto done;
335         }
336
337         count = ads_count_replies(ads, res);
338         if (count == 0) {
339                 DEBUG(1,("enum_dom_groups: No groups found\n"));
340                 goto done;
341         }
342
343         (*info) = TALLOC_ZERO_ARRAY(mem_ctx, struct acct_info, count);
344         if (!*info) {
345                 status = NT_STATUS_NO_MEMORY;
346                 goto done;
347         }
348
349         i = 0;
350         
351         for (msg = ads_first_entry(ads, res); msg; msg = ads_next_entry(ads, msg)) {
352                 char *name, *gecos;
353                 DOM_SID sid;
354                 uint32 rid;
355
356                 name = ads_pull_username(ads, mem_ctx, msg);
357                 gecos = ads_pull_string(ads, mem_ctx, msg, "name");
358                 if (!ads_pull_sid(ads, msg, "objectSid", &sid)) {
359                         DEBUG(1,("No sid for %s !?\n", name));
360                         continue;
361                 }
362
363                 if (!sid_peek_check_rid(&domain->sid, &sid, &rid)) {
364                         DEBUG(1,("No rid for %s !?\n", name));
365                         continue;
366                 }
367
368                 fstrcpy((*info)[i].acct_name, name);
369                 fstrcpy((*info)[i].acct_desc, gecos);
370                 (*info)[i].rid = rid;
371                 i++;
372         }
373
374         (*num_entries) = i;
375
376         status = NT_STATUS_OK;
377
378         DEBUG(3,("ads enum_dom_groups gave %d entries\n", (*num_entries)));
379
380 done:
381         if (res) 
382                 ads_msgfree(ads, res);
383
384         return status;
385 }
386
387 /* list all domain local groups */
388 static NTSTATUS enum_local_groups(struct winbindd_domain *domain,
389                                 TALLOC_CTX *mem_ctx,
390                                 uint32 *num_entries, 
391                                 struct acct_info **info)
392 {
393         /*
394          * This is a stub function only as we returned the domain 
395          * local groups in enum_dom_groups() if the domain->native field
396          * was true.  This is a simple performance optimization when
397          * using LDAP.
398          *
399          * if we ever need to enumerate domain local groups separately, 
400          * then this the optimization in enum_dom_groups() will need 
401          * to be split out
402          */
403         *num_entries = 0;
404         
405         return NT_STATUS_OK;
406 }
407
408 /* convert a DN to a name, SID and name type 
409    this might become a major speed bottleneck if groups have
410    lots of users, in which case we could cache the results
411 */
412 static BOOL dn_lookup(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
413                       const char *dn,
414                       char **name, uint32 *name_type, DOM_SID *sid)
415 {
416         void *res = NULL;
417         const char *attrs[] = {"userPrincipalName", "sAMAccountName",
418                                "objectSid", "sAMAccountType", NULL};
419         ADS_STATUS rc;
420         uint32 atype;
421         DEBUG(3,("ads: dn_lookup\n"));
422
423         rc = ads_search_retry_dn(ads, &res, dn, attrs);
424
425         if (!ADS_ERR_OK(rc) || !res) {
426                 goto failed;
427         }
428
429         (*name) = ads_pull_username(ads, mem_ctx, res);
430
431         if (!ads_pull_uint32(ads, res, "sAMAccountType", &atype)) {
432                 goto failed;
433         }
434         (*name_type) = ads_atype_map(atype);
435
436         if (!ads_pull_sid(ads, res, "objectSid", sid)) {
437                 goto failed;
438         }
439
440         if (res) 
441                 ads_msgfree(ads, res);
442
443         return True;
444
445 failed:
446         if (res) 
447                 ads_msgfree(ads, res);
448
449         return False;
450 }
451
452 /* Lookup user information from a rid */
453 static NTSTATUS query_user(struct winbindd_domain *domain, 
454                            TALLOC_CTX *mem_ctx, 
455                            const DOM_SID *sid, 
456                            WINBIND_USERINFO *info)
457 {
458         ADS_STRUCT *ads = NULL;
459         const char *attrs[] = {"userPrincipalName", 
460                                "sAMAccountName",
461                                "name", 
462                                "primaryGroupID", 
463                                ADS_ATTR_SFU_HOMEDIR_OID, 
464                                ADS_ATTR_SFU_SHELL_OID,
465                                ADS_ATTR_SFU_GECOS_OID,
466                                ADS_ATTR_RFC2307_HOMEDIR_OID,
467                                ADS_ATTR_RFC2307_SHELL_OID,
468                                ADS_ATTR_RFC2307_GECOS_OID,
469                                NULL};
470         ADS_STATUS rc;
471         int count;
472         void *msg = NULL;
473         char *ldap_exp;
474         char *sidstr;
475         uint32 group_rid;
476         NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
477
478         DEBUG(3,("ads: query_user\n"));
479
480         ads = ads_cached_connection(domain);
481         
482         if (!ads) {
483                 domain->last_status = NT_STATUS_SERVER_DISABLED;
484                 goto done;
485         }
486
487         sidstr = sid_binstring(sid);
488         asprintf(&ldap_exp, "(objectSid=%s)", sidstr);
489         rc = ads_search_retry(ads, &msg, ldap_exp, attrs);
490         free(ldap_exp);
491         free(sidstr);
492         if (!ADS_ERR_OK(rc) || !msg) {
493                 DEBUG(1,("query_user(sid=%s) ads_search: %s\n",
494                          sid_string_static(sid), ads_errstr(rc)));
495                 goto done;
496         }
497
498         count = ads_count_replies(ads, msg);
499         if (count != 1) {
500                 DEBUG(1,("query_user(sid=%s): Not found\n",
501                          sid_string_static(sid)));
502                 goto done;
503         }
504
505         info->acct_name = ads_pull_username(ads, mem_ctx, msg);
506
507         if (get_nss_info(domain->name) && ads->schema.map_type) {
508
509                 DEBUG(10,("pulling posix attributes (%s schema)\n", 
510                         wb_posix_map_str(ads->schema.map_type)));
511                 
512                 info->homedir   = ads_pull_string(ads, mem_ctx, msg, 
513                                                   ads->schema.posix_homedir_attr);
514                 info->shell     = ads_pull_string(ads, mem_ctx, msg, 
515                                                   ads->schema.posix_shell_attr);
516                 info->full_name = ads_pull_string(ads, mem_ctx, msg,
517                                                   ads->schema.posix_gecos_attr);
518         }
519
520         if (info->full_name == NULL) {
521                 info->full_name = ads_pull_string(ads, mem_ctx, msg, "name");
522         }
523
524         if (!ads_pull_uint32(ads, msg, "primaryGroupID", &group_rid)) {
525                 DEBUG(1,("No primary group for %s !?\n",
526                          sid_string_static(sid)));
527                 goto done;
528         }
529
530         sid_copy(&info->user_sid, sid);
531         sid_compose(&info->group_sid, &domain->sid, group_rid);
532
533         status = NT_STATUS_OK;
534
535         DEBUG(3,("ads query_user gave %s\n", info->acct_name));
536 done:
537         if (msg) 
538                 ads_msgfree(ads, msg);
539
540         return status;
541 }
542
543 /* Lookup groups a user is a member of - alternate method, for when
544    tokenGroups are not available. */
545 static NTSTATUS lookup_usergroups_alt(struct winbindd_domain *domain,
546                                       TALLOC_CTX *mem_ctx,
547                                       const char *user_dn, 
548                                       DOM_SID *primary_group,
549                                       size_t *p_num_groups, DOM_SID **user_sids)
550 {
551         ADS_STATUS rc;
552         NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
553         int count;
554         void *res = NULL;
555         void *msg = NULL;
556         char *ldap_exp;
557         ADS_STRUCT *ads;
558         const char *group_attrs[] = {"objectSid", NULL};
559         char *escaped_dn;
560         size_t num_groups = 0;
561
562         DEBUG(3,("ads: lookup_usergroups_alt\n"));
563
564         ads = ads_cached_connection(domain);
565
566         if (!ads) {
567                 domain->last_status = NT_STATUS_SERVER_DISABLED;
568                 goto done;
569         }
570
571         if (!(escaped_dn = escape_ldap_string_alloc(user_dn))) {
572                 status = NT_STATUS_NO_MEMORY;
573                 goto done;
574         }
575
576         /* buggy server, no tokenGroups.  Instead lookup what groups this user
577            is a member of by DN search on member*/
578
579         if (!(ldap_exp = talloc_asprintf(mem_ctx, "(&(member=%s)(objectCategory=group))", escaped_dn))) {
580                 DEBUG(1,("lookup_usergroups(dn=%s) asprintf failed!\n", user_dn));
581                 SAFE_FREE(escaped_dn);
582                 status = NT_STATUS_NO_MEMORY;
583                 goto done;
584         }
585
586         SAFE_FREE(escaped_dn);
587
588         rc = ads_search_retry(ads, &res, ldap_exp, group_attrs);
589         
590         if (!ADS_ERR_OK(rc) || !res) {
591                 DEBUG(1,("lookup_usergroups ads_search member=%s: %s\n", user_dn, ads_errstr(rc)));
592                 return ads_ntstatus(rc);
593         }
594         
595         count = ads_count_replies(ads, res);
596         
597         *user_sids = NULL;
598         num_groups = 0;
599
600         /* always add the primary group to the sid array */
601         add_sid_to_array(mem_ctx, primary_group, user_sids, &num_groups);
602
603         if (count > 0) {
604                 for (msg = ads_first_entry(ads, res); msg;
605                      msg = ads_next_entry(ads, msg)) {
606                         DOM_SID group_sid;
607                 
608                         if (!ads_pull_sid(ads, msg, "objectSid", &group_sid)) {
609                                 DEBUG(1,("No sid for this group ?!?\n"));
610                                 continue;
611                         }
612         
613                         /* ignore Builtin groups from ADS - Guenther */
614                         if (sid_check_is_in_builtin(&group_sid)) {
615                                 continue;
616                         }
617                                
618                         add_sid_to_array(mem_ctx, &group_sid, user_sids,
619                                          &num_groups);
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 (alt) 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. */
636 static NTSTATUS lookup_usergroups(struct winbindd_domain *domain,
637                                   TALLOC_CTX *mem_ctx,
638                                   const DOM_SID *sid, 
639                                   uint32 *p_num_groups, DOM_SID **user_sids)
640 {
641         ADS_STRUCT *ads = NULL;
642         const char *attrs[] = {"tokenGroups", "primaryGroupID", NULL};
643         ADS_STATUS rc;
644         int count;
645         LDAPMessage *msg = NULL;
646         char *user_dn;
647         DOM_SID *sids;
648         int i;
649         DOM_SID primary_group;
650         uint32 primary_group_rid;
651         fstring sid_string;
652         NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
653         size_t num_groups = 0;
654
655         DEBUG(3,("ads: lookup_usergroups\n"));
656         *p_num_groups = 0;
657
658         status = lookup_usergroups_cached(domain, mem_ctx, sid, 
659                                           p_num_groups, user_sids);
660         if (NT_STATUS_IS_OK(status)) {
661                 return NT_STATUS_OK;
662         }
663
664         ads = ads_cached_connection(domain);
665         
666         if (!ads) {
667                 domain->last_status = NT_STATUS_SERVER_DISABLED;
668                 status = NT_STATUS_SERVER_DISABLED;
669                 goto done;
670         }
671
672         rc = ads_search_retry_sid(ads, (void**)(void *)&msg, sid, attrs);
673
674         if (!ADS_ERR_OK(rc)) {
675                 status = ads_ntstatus(rc);
676                 DEBUG(1,("lookup_usergroups(sid=%s) ads_search tokenGroups: %s\n", 
677                          sid_to_string(sid_string, sid), ads_errstr(rc)));
678                 goto done;
679         }
680         
681         count = ads_count_replies(ads, msg);
682         if (count != 1) {
683                 status = NT_STATUS_UNSUCCESSFUL;
684                 DEBUG(1,("lookup_usergroups(sid=%s) ads_search tokenGroups: "
685                          "invalid number of results (count=%d)\n", 
686                          sid_to_string(sid_string, sid), count));
687                 goto done;
688         }
689
690         if (!msg) {
691                 DEBUG(1,("lookup_usergroups(sid=%s) ads_search tokenGroups: NULL msg\n", 
692                          sid_to_string(sid_string, sid)));
693                 status = NT_STATUS_UNSUCCESSFUL;
694                 goto done;
695         }
696
697         user_dn = ads_get_dn(ads, msg);
698         if (user_dn == NULL) {
699                 status = NT_STATUS_NO_MEMORY;
700                 goto done;
701         }
702
703         if (!ads_pull_uint32(ads, msg, "primaryGroupID", &primary_group_rid)) {
704                 DEBUG(1,("%s: No primary group for sid=%s !?\n", 
705                          domain->name, sid_to_string(sid_string, sid)));
706                 goto done;
707         }
708
709         sid_copy(&primary_group, &domain->sid);
710         sid_append_rid(&primary_group, primary_group_rid);
711
712         count = ads_pull_sids(ads, mem_ctx, msg, "tokenGroups", &sids);
713
714         if (msg) 
715                 ads_msgfree(ads, msg);
716
717         /* there must always be at least one group in the token, 
718            unless we are talking to a buggy Win2k server */
719
720         if (count == 0) {
721
722                 status = lookup_usergroups_alt(domain, mem_ctx, user_dn, 
723                                                &primary_group,
724                                                &num_groups, user_sids);
725                 *p_num_groups = (uint32)num_groups;
726                 return status;
727         }
728
729         *user_sids = NULL;
730         num_groups = 0;
731
732         add_sid_to_array(mem_ctx, &primary_group, user_sids, &num_groups);
733         
734         for (i=0;i<count;i++) {
735
736                 /* ignore Builtin groups from ADS - Guenther */
737                 if (sid_check_is_in_builtin(&sids[i])) {
738                         continue;
739                 }
740                                
741                 add_sid_to_array_unique(mem_ctx, &sids[i],
742                                         user_sids, &num_groups);
743         }
744
745         *p_num_groups = (uint32)num_groups;
746         status = (user_sids != NULL) ? NT_STATUS_OK : NT_STATUS_NO_MEMORY;
747
748         DEBUG(3,("ads lookup_usergroups for sid=%s\n",
749                  sid_to_string(sid_string, sid)));
750 done:
751         return status;
752 }
753
754 /*
755   find the members of a group, given a group rid and domain
756  */
757 static NTSTATUS lookup_groupmem(struct winbindd_domain *domain,
758                                 TALLOC_CTX *mem_ctx,
759                                 const DOM_SID *group_sid, uint32 *num_names, 
760                                 DOM_SID **sid_mem, char ***names, 
761                                 uint32 **name_types)
762 {
763         ADS_STATUS rc;
764         int count;
765         void *res=NULL;
766         ADS_STRUCT *ads = NULL;
767         char *ldap_exp;
768         NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
769         char *sidstr;
770         char **members;
771         int i;
772         size_t num_members;
773         fstring sid_string;
774         BOOL more_values;
775         const char **attrs;
776         uint32 first_usn;
777         uint32 current_usn;
778         int num_retries = 0;
779
780         DEBUG(10,("ads: lookup_groupmem %s sid=%s\n", domain->name, 
781                   sid_string_static(group_sid)));
782
783         *num_names = 0;
784
785         ads = ads_cached_connection(domain);
786         
787         if (!ads) {
788                 domain->last_status = NT_STATUS_SERVER_DISABLED;
789                 goto done;
790         }
791
792         sidstr = sid_binstring(group_sid);
793
794         /* search for all members of the group */
795         if (!(ldap_exp = talloc_asprintf(mem_ctx, "(objectSid=%s)",sidstr))) {
796                 SAFE_FREE(sidstr);
797                 DEBUG(1, ("ads: lookup_groupmem: tallloc_asprintf for ldap_exp failed!\n"));
798                 status = NT_STATUS_NO_MEMORY;
799                 goto done;
800         }
801         SAFE_FREE(sidstr);
802
803         members = NULL;
804         num_members = 0;
805
806         attrs = TALLOC_ARRAY(mem_ctx, const char *, 3);
807         attrs[1] = talloc_strdup(mem_ctx, "usnChanged");
808         attrs[2] = NULL;
809                 
810         do {
811                 if (num_members == 0) 
812                         attrs[0] = talloc_strdup(mem_ctx, "member");
813
814                 DEBUG(10, ("Searching for attrs[0] = %s, attrs[1] = %s\n", attrs[0], attrs[1]));
815
816                 rc = ads_search_retry(ads, &res, ldap_exp, attrs);
817
818                 if (!ADS_ERR_OK(rc) || !res) {
819                         DEBUG(1,("ads: lookup_groupmem ads_search: %s\n",
820                                  ads_errstr(rc)));
821                         status = ads_ntstatus(rc);
822                         goto done;
823                 }
824
825                 count = ads_count_replies(ads, res);
826                 if (count == 0)
827                         break;
828
829                 if (num_members == 0) {
830                         if (!ads_pull_uint32(ads, res, "usnChanged", &first_usn)) {
831                                 DEBUG(1, ("ads: lookup_groupmem could not pull usnChanged!\n"));
832                                 goto done;
833                         }
834                 }
835
836                 if (!ads_pull_uint32(ads, res, "usnChanged", &current_usn)) {
837                         DEBUG(1, ("ads: lookup_groupmem could not pull usnChanged!\n"));
838                         goto done;
839                 }
840
841                 if (first_usn != current_usn) {
842                         DEBUG(5, ("ads: lookup_groupmem USN on this record changed"
843                                   " - restarting search\n"));
844                         if (num_retries < 5) {
845                                 num_retries++;
846                                 num_members = 0;
847                                 continue;
848                         } else {
849                                 DEBUG(5, ("ads: lookup_groupmem USN on this record changed"
850                                           " - restarted search too many times, aborting!\n"));
851                                 status = NT_STATUS_UNSUCCESSFUL;
852                                 goto done;
853                         }
854                 }
855
856                 members = ads_pull_strings_range(ads, mem_ctx, res,
857                                                  "member",
858                                                  members,
859                                                  &attrs[0],
860                                                  &num_members,
861                                                  &more_values);
862
863                 if ((members == NULL) || (num_members == 0))
864                         break;
865
866         } while (more_values);
867                 
868         /* now we need to turn a list of members into rids, names and name types 
869            the problem is that the members are in the form of distinguised names
870         */
871
872         (*sid_mem) = TALLOC_ZERO_ARRAY(mem_ctx, DOM_SID, num_members);
873         (*name_types) = TALLOC_ZERO_ARRAY(mem_ctx, uint32, num_members);
874         (*names) = TALLOC_ZERO_ARRAY(mem_ctx, char *, num_members);
875
876         if ((num_members != 0) &&
877             ((members == NULL) || (*sid_mem == NULL) ||
878              (*name_types == NULL) || (*names == NULL))) {
879                 DEBUG(1, ("talloc failed\n"));
880                 status = NT_STATUS_NO_MEMORY;
881                 goto done;
882         }
883  
884         for (i=0;i<num_members;i++) {
885                 uint32 name_type;
886                 char *name;
887                 DOM_SID sid;
888
889                 if (dn_lookup(ads, mem_ctx, members[i], &name, &name_type, &sid)) {
890                     (*names)[*num_names] = name;
891                     (*name_types)[*num_names] = name_type;
892                     sid_copy(&(*sid_mem)[*num_names], &sid);
893                     (*num_names)++;
894                 }
895         }       
896
897         status = NT_STATUS_OK;
898         DEBUG(3,("ads lookup_groupmem for sid=%s\n", sid_to_string(sid_string, group_sid)));
899 done:
900
901         if (res) 
902                 ads_msgfree(ads, res);
903
904         return status;
905 }
906
907 /* find the sequence number for a domain */
908 static NTSTATUS sequence_number(struct winbindd_domain *domain, uint32 *seq)
909 {
910         ADS_STRUCT *ads = NULL;
911         ADS_STATUS rc;
912
913         DEBUG(3,("ads: fetch sequence_number for %s\n", domain->name));
914
915         *seq = DOM_SEQUENCE_NONE;
916
917         ads = ads_cached_connection(domain);
918         
919         if (!ads) {
920                 domain->last_status = NT_STATUS_SERVER_DISABLED;
921                 return NT_STATUS_UNSUCCESSFUL;
922         }
923
924         rc = ads_USN(ads, seq);
925         
926         if (!ADS_ERR_OK(rc)) {
927         
928                 /* its a dead connection ; don't destroy it 
929                    through since ads_USN() has already done 
930                    that indirectly */
931                    
932                 domain->private_data = NULL;
933         }
934         return ads_ntstatus(rc);
935 }
936
937 /* get a list of trusted domains */
938 static NTSTATUS trusted_domains(struct winbindd_domain *domain,
939                                 TALLOC_CTX *mem_ctx,
940                                 uint32 *num_domains,
941                                 char ***names,
942                                 char ***alt_names,
943                                 DOM_SID **dom_sids)
944 {
945         NTSTATUS                result = NT_STATUS_UNSUCCESSFUL;
946         struct ds_domain_trust  *domains = NULL;
947         int                     count = 0;
948         int                     i;
949         uint32                  flags = DS_DOMAIN_DIRECT_OUTBOUND;
950         struct rpc_pipe_client *cli;
951
952         DEBUG(3,("ads: trusted_domains\n"));
953
954         *num_domains = 0;
955         *alt_names   = NULL;
956         *names       = NULL;
957         *dom_sids    = NULL;
958
959         result = cm_connect_netlogon(domain, &cli);
960
961         if (!NT_STATUS_IS_OK(result)) {
962                 DEBUG(5, ("trusted_domains: Could not open a connection to %s "
963                           "for PIPE_NETLOGON (%s)\n", 
964                           domain->name, nt_errstr(result)));
965                 return NT_STATUS_UNSUCCESSFUL;
966         }
967         
968         if ( NT_STATUS_IS_OK(result) ) {
969                 result = rpccli_ds_enum_domain_trusts(cli, mem_ctx,
970                                                       cli->cli->desthost, 
971                                                       flags, &domains,
972                                                       (unsigned int *)&count);
973         }
974         
975         if ( NT_STATUS_IS_OK(result) && count) {
976         
977                 /* Allocate memory for trusted domain names and sids */
978
979                 if ( !(*names = TALLOC_ARRAY(mem_ctx, char *, count)) ) {
980                         DEBUG(0, ("trusted_domains: out of memory\n"));
981                         return NT_STATUS_NO_MEMORY;
982                 }
983
984                 if ( !(*alt_names = TALLOC_ARRAY(mem_ctx, char *, count)) ) {
985                         DEBUG(0, ("trusted_domains: out of memory\n"));
986                         return NT_STATUS_NO_MEMORY;
987                 }
988
989                 if ( !(*dom_sids = TALLOC_ARRAY(mem_ctx, DOM_SID, count)) ) {
990                         DEBUG(0, ("trusted_domains: out of memory\n"));
991                         return NT_STATUS_NO_MEMORY;
992                 }
993
994                 /* Copy across names and sids */
995
996                 for (i = 0; i < count; i++) {
997                         (*names)[i] = domains[i].netbios_domain;
998                         (*alt_names)[i] = domains[i].dns_domain;
999
1000                         sid_copy(&(*dom_sids)[i], &domains[i].sid);
1001                 }
1002
1003                 *num_domains = count;   
1004         }
1005
1006         return result;
1007 }
1008
1009 /* the ADS backend methods are exposed via this structure */
1010 struct winbindd_methods ads_methods = {
1011         True,
1012         query_user_list,
1013         enum_dom_groups,
1014         enum_local_groups,
1015         msrpc_name_to_sid,
1016         msrpc_sid_to_name,
1017         query_user,
1018         lookup_usergroups,
1019         msrpc_lookup_useraliases,
1020         lookup_groupmem,
1021         sequence_number,
1022         msrpc_lockout_policy,
1023         msrpc_password_policy,
1024         trusted_domains,
1025 };
1026
1027 #endif