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