Forward port the change to talloc_init() to make all talloc contexts
[jra/samba/.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    
8    This program is free software; you can redistribute it and/or modify
9    it under the terms of the GNU General Public License as published by
10    the Free Software Foundation; either version 2 of the License, or
11    (at your option) any later version.
12    
13    This program is distributed in the hope that it will be useful,
14    but WITHOUT ANY WARRANTY; without even the implied warranty of
15    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16    GNU General Public License for more details.
17    
18    You should have received a copy of the GNU General Public License
19    along with this program; if not, write to the Free Software
20    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
21 */
22
23 #include "winbindd.h"
24
25 #ifdef HAVE_ADS
26
27 #undef DBGC_CLASS
28 #define DBGC_CLASS DBGC_WINBIND
29
30 /* the realm of our primary LDAP server */
31 static char *primary_realm;
32
33
34 /*
35   return our ads connections structure for a domain. We keep the connection
36   open to make things faster
37 */
38 static ADS_STRUCT *ads_cached_connection(struct winbindd_domain *domain)
39 {
40         ADS_STRUCT *ads;
41         ADS_STATUS status;
42         char *ccache;
43
44         if (domain->private) {
45                 return (ADS_STRUCT *)domain->private;
46         }
47
48         /* we don't want this to affect the users ccache */
49         ccache = lock_path("winbindd_ccache");
50         SETENV("KRB5CCNAME", ccache, 1);
51         unlink(ccache);
52
53         ads = ads_init(domain->alt_name, domain->name, NULL);
54         if (!ads) {
55                 DEBUG(1,("ads_init for domain %s failed\n", domain->name));
56                 return NULL;
57         }
58
59         /* the machine acct password might have change - fetch it every time */
60         SAFE_FREE(ads->auth.password);
61         ads->auth.password = secrets_fetch_machine_password();
62
63         if (primary_realm) {
64                 SAFE_FREE(ads->auth.realm);
65                 ads->auth.realm = strdup(primary_realm);
66         }
67
68         status = ads_connect(ads);
69         if (!ADS_ERR_OK(status) || !ads->config.realm) {
70                 extern struct winbindd_methods msrpc_methods;
71                 DEBUG(1,("ads_connect for domain %s failed: %s\n", 
72                          domain->name, ads_errstr(status)));
73                 ads_destroy(&ads);
74
75                 /* if we get ECONNREFUSED then it might be a NT4
76                    server, fall back to MSRPC */
77                 if (status.error_type == ADS_ERROR_SYSTEM &&
78                     status.err.rc == ECONNREFUSED) {
79                         DEBUG(1,("Trying MSRPC methods\n"));
80                         domain->methods = &msrpc_methods;
81                 }
82                 return NULL;
83         }
84
85         /* remember our primary realm for trusted domain support */
86         if (!primary_realm) {
87                 primary_realm = strdup(ads->config.realm);
88         }
89
90         domain->private = (void *)ads;
91         return ads;
92 }
93
94 /* useful utility */
95 static void sid_from_rid(struct winbindd_domain *domain, uint32 rid, DOM_SID *sid)
96 {
97         sid_copy(sid, &domain->sid);
98         sid_append_rid(sid, rid);
99 }
100
101
102 /* Query display info for a realm. This is the basic user list fn */
103 static NTSTATUS query_user_list(struct winbindd_domain *domain,
104                                TALLOC_CTX *mem_ctx,
105                                uint32 *num_entries, 
106                                WINBIND_USERINFO **info)
107 {
108         ADS_STRUCT *ads = NULL;
109         const char *attrs[] = {"userPrincipalName",
110                                "sAMAccountName",
111                                "name", "objectSid", "primaryGroupID", 
112                                "sAMAccountType", NULL};
113         int i, count;
114         ADS_STATUS rc;
115         void *res = NULL;
116         void *msg = NULL;
117         NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
118
119         *num_entries = 0;
120
121         DEBUG(3,("ads: query_user_list\n"));
122
123         ads = ads_cached_connection(domain);
124         if (!ads) goto done;
125
126         rc = ads_search_retry(ads, &res, "(objectCategory=user)", attrs);
127         if (!ADS_ERR_OK(rc)) {
128                 DEBUG(1,("query_user_list ads_search: %s\n", ads_errstr(rc)));
129                 goto done;
130         }
131
132         count = ads_count_replies(ads, res);
133         if (count == 0) {
134                 DEBUG(1,("query_user_list: No users found\n"));
135                 goto done;
136         }
137
138         (*info) = talloc_zero(mem_ctx, count * sizeof(**info));
139         if (!*info) {
140                 status = NT_STATUS_NO_MEMORY;
141                 goto done;
142         }
143
144         i = 0;
145
146         for (msg = ads_first_entry(ads, res); msg; msg = ads_next_entry(ads, msg)) {
147                 char *name, *gecos;
148                 DOM_SID sid;
149                 uint32 rid, group;
150                 uint32 atype;
151
152                 if (!ads_pull_uint32(ads, msg, "sAMAccountType", &atype) ||
153                     ads_atype_map(atype) != SID_NAME_USER) {
154                         DEBUG(1,("Not a user account? atype=0x%x\n", atype));
155                         continue;
156                 }
157
158                 name = ads_pull_username(ads, mem_ctx, msg);
159                 gecos = ads_pull_string(ads, mem_ctx, msg, "name");
160                 if (!ads_pull_sid(ads, msg, "objectSid", &sid)) {
161                         DEBUG(1,("No sid for %s !?\n", name));
162                         continue;
163                 }
164                 if (!ads_pull_uint32(ads, msg, "primaryGroupID", &group)) {
165                         DEBUG(1,("No primary group for %s !?\n", name));
166                         continue;
167                 }
168
169                 if (!sid_peek_check_rid(&domain->sid, &sid, &rid)) {
170                         DEBUG(1,("No rid for %s !?\n", name));
171                         continue;
172                 }
173
174                 (*info)[i].acct_name = name;
175                 (*info)[i].full_name = gecos;
176                 (*info)[i].user_rid = rid;
177                 (*info)[i].group_rid = group;
178                 i++;
179         }
180
181         (*num_entries) = i;
182         status = NT_STATUS_OK;
183
184         DEBUG(3,("ads query_user_list gave %d entries\n", (*num_entries)));
185
186 done:
187         if (res) ads_msgfree(ads, res);
188
189         return status;
190 }
191
192 /* list all domain groups */
193 static NTSTATUS enum_dom_groups(struct winbindd_domain *domain,
194                                 TALLOC_CTX *mem_ctx,
195                                 uint32 *num_entries, 
196                                 struct acct_info **info)
197 {
198         ADS_STRUCT *ads = NULL;
199         const char *attrs[] = {"userPrincipalName", "sAMAccountName",
200                                "name", "objectSid", 
201                                "sAMAccountType", NULL};
202         int i, count;
203         ADS_STATUS rc;
204         void *res = NULL;
205         void *msg = NULL;
206         NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
207         uint32 group_flags;
208
209         *num_entries = 0;
210
211         DEBUG(3,("ads: enum_dom_groups\n"));
212
213         ads = ads_cached_connection(domain);
214         if (!ads) goto done;
215
216         rc = ads_search_retry(ads, &res, "(objectCategory=group)", attrs);
217         if (!ADS_ERR_OK(rc)) {
218                 DEBUG(1,("enum_dom_groups ads_search: %s\n", ads_errstr(rc)));
219                 goto done;
220         }
221
222         count = ads_count_replies(ads, res);
223         if (count == 0) {
224                 DEBUG(1,("enum_dom_groups: No groups found\n"));
225                 goto done;
226         }
227
228         (*info) = talloc_zero(mem_ctx, count * sizeof(**info));
229         if (!*info) {
230                 status = NT_STATUS_NO_MEMORY;
231                 goto done;
232         }
233
234         i = 0;
235         
236         group_flags = ATYPE_GLOBAL_GROUP;
237         if ( domain->native_mode )
238                 group_flags |= ATYPE_LOCAL_GROUP;
239
240         for (msg = ads_first_entry(ads, res); msg; msg = ads_next_entry(ads, msg)) {
241                 char *name, *gecos;
242                 DOM_SID sid;
243                 uint32 rid;
244                 uint32 account_type;
245
246                 if (!ads_pull_uint32(ads, msg, "sAMAccountType", &account_type) || !(account_type & group_flags) ) 
247                         continue; 
248                         
249                 name = ads_pull_username(ads, mem_ctx, msg);
250                 gecos = ads_pull_string(ads, mem_ctx, msg, "name");
251                 if (!ads_pull_sid(ads, msg, "objectSid", &sid)) {
252                         DEBUG(1,("No sid for %s !?\n", name));
253                         continue;
254                 }
255
256                 if (!sid_peek_check_rid(&domain->sid, &sid, &rid)) {
257                         DEBUG(1,("No rid for %s !?\n", name));
258                         continue;
259                 }
260
261                 fstrcpy((*info)[i].acct_name, name);
262                 fstrcpy((*info)[i].acct_desc, gecos);
263                 (*info)[i].rid = rid;
264                 i++;
265         }
266
267         (*num_entries) = i;
268
269         status = NT_STATUS_OK;
270
271         DEBUG(3,("ads enum_dom_groups gave %d entries\n", (*num_entries)));
272
273 done:
274         if (res) ads_msgfree(ads, res);
275
276         return status;
277 }
278
279 /* list all domain local groups */
280 static NTSTATUS enum_local_groups(struct winbindd_domain *domain,
281                                 TALLOC_CTX *mem_ctx,
282                                 uint32 *num_entries, 
283                                 struct acct_info **info)
284 {
285         /*
286          * This is a stub function only as we returned the domain 
287          * ocal groups in enum_dom_groups() if the domain->native field
288          * was true.  This is a simple performance optimization when
289          * using LDAP.
290          *
291          * if we ever need to enumerate domain local groups separately, 
292          * then this the optimization in enum_dom_groups() will need 
293          * to be split out
294          */
295         *num_entries = 0;
296         
297         return NT_STATUS_OK;
298 }
299
300 /* convert a single name to a sid in a domain */
301 static NTSTATUS name_to_sid(struct winbindd_domain *domain,
302                             const char *name,
303                             DOM_SID *sid,
304                             enum SID_NAME_USE *type)
305 {
306         ADS_STRUCT *ads;
307
308         DEBUG(3,("ads: name_to_sid\n"));
309
310         ads = ads_cached_connection(domain);
311         if (!ads) 
312                 return NT_STATUS_UNSUCCESSFUL;
313
314         return ads_name_to_sid(ads, name, sid, type);
315 }
316
317 /* convert a sid to a user or group name */
318 static NTSTATUS sid_to_name(struct winbindd_domain *domain,
319                             TALLOC_CTX *mem_ctx,
320                             DOM_SID *sid,
321                             char **name,
322                             enum SID_NAME_USE *type)
323 {
324         ADS_STRUCT *ads = NULL;
325         DEBUG(3,("ads: sid_to_name\n"));
326         ads = ads_cached_connection(domain);
327         if (!ads) 
328                 return NT_STATUS_UNSUCCESSFUL;
329
330         return ads_sid_to_name(ads, mem_ctx, sid, name, type);
331 }
332
333
334 /* convert a DN to a name, rid and name type 
335    this might become a major speed bottleneck if groups have
336    lots of users, in which case we could cache the results
337 */
338 static BOOL dn_lookup(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
339                       const char *dn,
340                       char **name, uint32 *name_type, uint32 *rid)
341 {
342         char *exp;
343         void *res = NULL;
344         const char *attrs[] = {"userPrincipalName", "sAMAccountName",
345                                "objectSid", "sAMAccountType", NULL};
346         ADS_STATUS rc;
347         uint32 atype;
348         DOM_SID sid;
349
350         asprintf(&exp, "(distinguishedName=%s)", dn);
351         rc = ads_search_retry(ads, &res, exp, attrs);
352         free(exp);
353         if (!ADS_ERR_OK(rc)) {
354                 goto failed;
355         }
356
357         (*name) = ads_pull_username(ads, mem_ctx, res);
358
359         if (!ads_pull_uint32(ads, res, "sAMAccountType", &atype)) {
360                 goto failed;
361         }
362         (*name_type) = ads_atype_map(atype);
363
364         if (!ads_pull_sid(ads, res, "objectSid", &sid) || 
365             !sid_peek_rid(&sid, rid)) {
366                 goto failed;
367         }
368
369         if (res) ads_msgfree(ads, res);
370         return True;
371
372 failed:
373         if (res) ads_msgfree(ads, res);
374         return False;
375 }
376
377 /* Lookup user information from a rid */
378 static NTSTATUS query_user(struct winbindd_domain *domain, 
379                            TALLOC_CTX *mem_ctx, 
380                            uint32 user_rid, 
381                            WINBIND_USERINFO *info)
382 {
383         ADS_STRUCT *ads = NULL;
384         const char *attrs[] = {"userPrincipalName", 
385                                "sAMAccountName",
386                                "name", "objectSid", 
387                                "primaryGroupID", NULL};
388         ADS_STATUS rc;
389         int count;
390         void *msg = NULL;
391         char *exp;
392         DOM_SID sid;
393         char *sidstr;
394         NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
395
396         DEBUG(3,("ads: query_user\n"));
397
398         sid_from_rid(domain, user_rid, &sid);
399
400         ads = ads_cached_connection(domain);
401         if (!ads) goto done;
402
403         sidstr = sid_binstring(&sid);
404         asprintf(&exp, "(objectSid=%s)", sidstr);
405         rc = ads_search_retry(ads, &msg, exp, attrs);
406         free(exp);
407         free(sidstr);
408         if (!ADS_ERR_OK(rc)) {
409                 DEBUG(1,("query_user(rid=%d) ads_search: %s\n", user_rid, ads_errstr(rc)));
410                 goto done;
411         }
412
413         count = ads_count_replies(ads, msg);
414         if (count != 1) {
415                 DEBUG(1,("query_user(rid=%d): Not found\n", user_rid));
416                 goto done;
417         }
418
419         info->acct_name = ads_pull_username(ads, mem_ctx, msg);
420         info->full_name = ads_pull_string(ads, mem_ctx, msg, "name");
421         if (!ads_pull_sid(ads, msg, "objectSid", &sid)) {
422                 DEBUG(1,("No sid for %d !?\n", user_rid));
423                 goto done;
424         }
425         if (!ads_pull_uint32(ads, msg, "primaryGroupID", &info->group_rid)) {
426                 DEBUG(1,("No primary group for %d !?\n", user_rid));
427                 goto done;
428         }
429         
430         if (!sid_peek_check_rid(&domain->sid,&sid, &info->user_rid)) {
431                 DEBUG(1,("No rid for %d !?\n", user_rid));
432                 goto done;
433         }
434
435         status = NT_STATUS_OK;
436
437         DEBUG(3,("ads query_user gave %s\n", info->acct_name));
438 done:
439         if (msg) ads_msgfree(ads, msg);
440
441         return status;
442 }
443
444
445 /* Lookup groups a user is a member of. */
446 static NTSTATUS lookup_usergroups(struct winbindd_domain *domain,
447                                   TALLOC_CTX *mem_ctx,
448                                   uint32 user_rid, 
449                                   uint32 *num_groups, uint32 **user_gids)
450 {
451         ADS_STRUCT *ads = NULL;
452         const char *attrs[] = {"distinguishedName", NULL};
453         const char *attrs2[] = {"tokenGroups", "primaryGroupID", NULL};
454         ADS_STATUS rc;
455         int count;
456         void *msg = NULL;
457         char *exp;
458         char *user_dn;
459         DOM_SID *sids;
460         int i;
461         uint32 primary_group;
462         DOM_SID sid;
463         char *sidstr;
464         NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
465
466         *num_groups = 0;
467
468         DEBUG(3,("ads: lookup_usergroups\n"));
469
470         (*num_groups) = 0;
471
472         sid_from_rid(domain, user_rid, &sid);
473
474         ads = ads_cached_connection(domain);
475         if (!ads) goto done;
476
477         sidstr = sid_binstring(&sid);
478         asprintf(&exp, "(objectSid=%s)", sidstr);
479         rc = ads_search_retry(ads, &msg, exp, attrs);
480         free(exp);
481         free(sidstr);
482         if (!ADS_ERR_OK(rc)) {
483                 DEBUG(1,("lookup_usergroups(rid=%d) ads_search: %s\n", user_rid, ads_errstr(rc)));
484                 goto done;
485         }
486
487         user_dn = ads_pull_string(ads, mem_ctx, msg, "distinguishedName");
488
489         if (msg) ads_msgfree(ads, msg);
490
491         rc = ads_search_retry_dn(ads, &msg, user_dn, attrs2);
492         if (!ADS_ERR_OK(rc)) {
493                 DEBUG(1,("lookup_usergroups(rid=%d) ads_search tokenGroups: %s\n", user_rid, ads_errstr(rc)));
494                 goto done;
495         }
496
497         if (!ads_pull_uint32(ads, msg, "primaryGroupID", &primary_group)) {
498                 DEBUG(1,("%s: No primary group for rid=%d !?\n", domain->name, user_rid));
499                 goto done;
500         }
501
502         count = ads_pull_sids(ads, mem_ctx, msg, "tokenGroups", &sids) + 1;
503         (*user_gids) = (uint32 *)talloc_zero(mem_ctx, sizeof(uint32) * count);
504         (*user_gids)[(*num_groups)++] = primary_group;
505
506         for (i=1;i<count;i++) {
507                 uint32 rid;
508                 if (!sid_peek_check_rid(&domain->sid, &sids[i-1], &rid)) continue;
509                 (*user_gids)[*num_groups] = rid;
510                 (*num_groups)++;
511         }
512
513         status = NT_STATUS_OK;
514         DEBUG(3,("ads lookup_usergroups for rid=%d\n", user_rid));
515 done:
516         if (msg) ads_msgfree(ads, msg);
517
518         return status;
519 }
520
521 /*
522   find the members of a group, given a group rid and domain
523  */
524 static NTSTATUS lookup_groupmem(struct winbindd_domain *domain,
525                                 TALLOC_CTX *mem_ctx,
526                                 uint32 group_rid, uint32 *num_names, 
527                                 uint32 **rid_mem, char ***names, 
528                                 uint32 **name_types)
529 {
530         DOM_SID group_sid;
531         ADS_STATUS rc;
532         int count;
533         void *res=NULL;
534         ADS_STRUCT *ads = NULL;
535         char *exp;
536         NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
537         char *sidstr;
538         const char *attrs[] = {"member", NULL};
539         char **members;
540         int i, num_members;
541
542         *num_names = 0;
543
544         ads = ads_cached_connection(domain);
545         if (!ads) goto done;
546
547         sid_from_rid(domain, group_rid, &group_sid);
548         sidstr = sid_binstring(&group_sid);
549
550         /* search for all members of the group */
551         asprintf(&exp, "(objectSid=%s)",sidstr);
552         rc = ads_search_retry(ads, &res, exp, attrs);
553         free(exp);
554         free(sidstr);
555
556         if (!ADS_ERR_OK(rc)) {
557                 DEBUG(1,("query_user_list ads_search: %s\n", ads_errstr(rc)));
558                 goto done;
559         }
560
561         count = ads_count_replies(ads, res);
562         if (count == 0) {
563                 status = NT_STATUS_OK;
564                 goto done;
565         }
566
567         members = ads_pull_strings(ads, mem_ctx, res, "member");
568         if (!members) {
569                 /* no members? ok ... */
570                 status = NT_STATUS_OK;
571                 goto done;
572         }
573
574         /* now we need to turn a list of members into rids, names and name types 
575            the problem is that the members are in the form of distinguised names
576         */
577         for (i=0;members[i];i++) /* noop */ ;
578         num_members = i;
579
580         (*rid_mem) = talloc_zero(mem_ctx, sizeof(uint32) * num_members);
581         (*name_types) = talloc_zero(mem_ctx, sizeof(uint32) * num_members);
582         (*names) = talloc_zero(mem_ctx, sizeof(char *) * num_members);
583
584         for (i=0;i<num_members;i++) {
585                 uint32 name_type, rid;
586                 char *name;
587
588                 if (dn_lookup(ads, mem_ctx, members[i], &name, &name_type, &rid)) {
589                     (*names)[*num_names] = name;
590                     (*name_types)[*num_names] = name_type;
591                     (*rid_mem)[*num_names] = rid;
592                     (*num_names)++;
593                 }
594         }       
595
596         status = NT_STATUS_OK;
597         DEBUG(3,("ads lookup_groupmem for rid=%d\n", group_rid));
598 done:
599         if (res) ads_msgfree(ads, res);
600
601         return status;
602 }
603
604
605 /* find the sequence number for a domain */
606 static NTSTATUS sequence_number(struct winbindd_domain *domain, uint32 *seq)
607 {
608         ADS_STRUCT *ads = NULL;
609         ADS_STATUS rc;
610
611         *seq = DOM_SEQUENCE_NONE;
612
613         ads = ads_cached_connection(domain);
614         if (!ads) return NT_STATUS_UNSUCCESSFUL;
615
616         rc = ads_USN(ads, seq);
617         if (!ADS_ERR_OK(rc)) {
618                 /* its a dead connection */
619                 ads_destroy(&ads);
620                 domain->private = NULL;
621         }
622         return ads_ntstatus(rc);
623 }
624
625 /* get a list of trusted domains */
626 static NTSTATUS trusted_domains(struct winbindd_domain *domain,
627                                 TALLOC_CTX *mem_ctx,
628                                 uint32 *num_domains,
629                                 char ***names,
630                                 char ***alt_names,
631                                 DOM_SID **dom_sids)
632 {
633         ADS_STRUCT *ads;
634         ADS_STATUS rc;
635
636         *num_domains = 0;
637         *names = NULL;
638
639         ads = ads_cached_connection(domain);
640         if (!ads) return NT_STATUS_UNSUCCESSFUL;
641
642         rc = ads_trusted_domains(ads, mem_ctx, num_domains, names, alt_names, dom_sids);
643
644         return ads_ntstatus(rc);
645 }
646
647 /* find the domain sid for a domain */
648 static NTSTATUS domain_sid(struct winbindd_domain *domain, DOM_SID *sid)
649 {
650         ADS_STRUCT *ads;
651         ADS_STATUS rc;
652
653         ads = ads_cached_connection(domain);
654         if (!ads) return NT_STATUS_UNSUCCESSFUL;
655
656         rc = ads_domain_sid(ads, sid);
657
658         if (!ADS_ERR_OK(rc)) {
659                 /* its a dead connection */
660                 ads_destroy(&ads);
661                 domain->private = NULL;
662         }
663
664         return ads_ntstatus(rc);
665 }
666
667
668 /* find alternate names list for the domain - for ADS this is the
669    netbios name */
670 static NTSTATUS alternate_name(struct winbindd_domain *domain)
671 {
672         ADS_STRUCT *ads;
673         ADS_STATUS rc;
674         TALLOC_CTX *ctx;
675         char *workgroup;
676
677         ads = ads_cached_connection(domain);
678         if (!ads) return NT_STATUS_UNSUCCESSFUL;
679
680         if (!(ctx = talloc_init("alternate_name"))) {
681                 return NT_STATUS_NO_MEMORY;
682         }
683
684         rc = ads_workgroup_name(ads, ctx, &workgroup);
685
686         if (ADS_ERR_OK(rc)) {
687                 fstrcpy(domain->name, workgroup);
688                 fstrcpy(domain->alt_name, ads->config.realm);
689                 strupper(domain->alt_name);
690                 strupper(domain->name);
691         }
692
693         talloc_destroy(ctx);
694
695         return ads_ntstatus(rc);        
696 }
697
698 /* the ADS backend methods are exposed via this structure */
699 struct winbindd_methods ads_methods = {
700         True,
701         query_user_list,
702         enum_dom_groups,
703         enum_local_groups,
704         name_to_sid,
705         sid_to_name,
706         query_user,
707         lookup_usergroups,
708         lookup_groupmem,
709         sequence_number,
710         trusted_domains,
711         domain_sid,
712         alternate_name
713 };
714
715 #endif