CUPS-PRINTER_CLASS patch from Michael Sweet
[Samba.git] / source / nsswitch / winbindd_ads.c
blob261c2f2237ae0c92cdc3ba0430a5beaa4c929e9d
1 /*
2 Unix SMB/CIFS implementation.
4 Winbind ADS backend functions
6 Copyright (C) Andrew Tridgell 2001
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.
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.
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.
23 #include "winbindd.h"
25 #ifdef HAVE_ADS
27 #undef DBGC_CLASS
28 #define DBGC_CLASS DBGC_WINBIND
30 /* the realm of our primary LDAP server */
31 static char *primary_realm;
35 return our ads connections structure for a domain. We keep the connection
36 open to make things faster
38 static ADS_STRUCT *ads_cached_connection(struct winbindd_domain *domain)
40 ADS_STRUCT *ads;
41 ADS_STATUS status;
42 char *ccache;
44 if (domain->private) {
45 return (ADS_STRUCT *)domain->private;
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);
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;
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();
63 if (primary_realm) {
64 SAFE_FREE(ads->auth.realm);
65 ads->auth.realm = strdup(primary_realm);
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);
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;
82 return NULL;
85 /* remember our primary realm for trusted domain support */
86 if (!primary_realm) {
87 primary_realm = strdup(ads->config.realm);
90 domain->private = (void *)ads;
91 return ads;
94 /* useful utility */
95 static void sid_from_rid(struct winbindd_domain *domain, uint32 rid, DOM_SID *sid)
97 sid_copy(sid, &domain->sid);
98 sid_append_rid(sid, rid);
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)
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;
119 *num_entries = 0;
121 DEBUG(3,("ads: query_user_list\n"));
123 ads = ads_cached_connection(domain);
124 if (!ads) goto done;
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;
132 count = ads_count_replies(ads, res);
133 if (count == 0) {
134 DEBUG(1,("query_user_list: No users found\n"));
135 goto done;
138 (*info) = talloc_zero(mem_ctx, count * sizeof(**info));
139 if (!*info) {
140 status = NT_STATUS_NO_MEMORY;
141 goto done;
144 i = 0;
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;
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;
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;
164 if (!ads_pull_uint32(ads, msg, "primaryGroupID", &group)) {
165 DEBUG(1,("No primary group for %s !?\n", name));
166 continue;
169 if (!sid_peek_check_rid(&domain->sid, &sid, &rid)) {
170 DEBUG(1,("No rid for %s !?\n", name));
171 continue;
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++;
181 (*num_entries) = i;
182 status = NT_STATUS_OK;
184 DEBUG(3,("ads query_user_list gave %d entries\n", (*num_entries)));
186 done:
187 if (res) ads_msgfree(ads, res);
189 return status;
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)
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;
209 *num_entries = 0;
211 DEBUG(3,("ads: enum_dom_groups\n"));
213 ads = ads_cached_connection(domain);
214 if (!ads) goto done;
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;
222 count = ads_count_replies(ads, res);
223 if (count == 0) {
224 DEBUG(1,("enum_dom_groups: No groups found\n"));
225 goto done;
228 (*info) = talloc_zero(mem_ctx, count * sizeof(**info));
229 if (!*info) {
230 status = NT_STATUS_NO_MEMORY;
231 goto done;
234 i = 0;
236 group_flags = ATYPE_GLOBAL_GROUP;
237 if ( domain->native_mode )
238 group_flags |= ATYPE_LOCAL_GROUP;
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;
246 if (!ads_pull_uint32(ads, msg, "sAMAccountType", &account_type) || !(account_type & group_flags) )
247 continue;
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;
256 if (!sid_peek_check_rid(&domain->sid, &sid, &rid)) {
257 DEBUG(1,("No rid for %s !?\n", name));
258 continue;
261 fstrcpy((*info)[i].acct_name, name);
262 fstrcpy((*info)[i].acct_desc, gecos);
263 (*info)[i].rid = rid;
264 i++;
267 (*num_entries) = i;
269 status = NT_STATUS_OK;
271 DEBUG(3,("ads enum_dom_groups gave %d entries\n", (*num_entries)));
273 done:
274 if (res) ads_msgfree(ads, res);
276 return status;
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)
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.
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
295 *num_entries = 0;
297 return NT_STATUS_OK;
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)
306 ADS_STRUCT *ads;
308 DEBUG(3,("ads: name_to_sid\n"));
310 ads = ads_cached_connection(domain);
311 if (!ads)
312 return NT_STATUS_UNSUCCESSFUL;
314 return ads_name_to_sid(ads, name, sid, type);
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)
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;
330 return ads_sid_to_name(ads, mem_ctx, sid, name, type);
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
338 static BOOL dn_lookup(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
339 const char *dn,
340 char **name, uint32 *name_type, uint32 *rid)
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;
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;
357 (*name) = ads_pull_username(ads, mem_ctx, res);
359 if (!ads_pull_uint32(ads, res, "sAMAccountType", &atype)) {
360 goto failed;
362 (*name_type) = ads_atype_map(atype);
364 if (!ads_pull_sid(ads, res, "objectSid", &sid) ||
365 !sid_peek_rid(&sid, rid)) {
366 goto failed;
369 if (res) ads_msgfree(ads, res);
370 return True;
372 failed:
373 if (res) ads_msgfree(ads, res);
374 return False;
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)
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;
396 DEBUG(3,("ads: query_user\n"));
398 sid_from_rid(domain, user_rid, &sid);
400 ads = ads_cached_connection(domain);
401 if (!ads) goto done;
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;
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;
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;
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;
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;
435 status = NT_STATUS_OK;
437 DEBUG(3,("ads query_user gave %s\n", info->acct_name));
438 done:
439 if (msg) ads_msgfree(ads, msg);
441 return status;
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)
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;
466 *num_groups = 0;
468 DEBUG(3,("ads: lookup_usergroups\n"));
470 (*num_groups) = 0;
472 sid_from_rid(domain, user_rid, &sid);
474 ads = ads_cached_connection(domain);
475 if (!ads) goto done;
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;
487 user_dn = ads_pull_string(ads, mem_ctx, msg, "distinguishedName");
489 if (msg) ads_msgfree(ads, msg);
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;
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;
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;
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)++;
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);
518 return status;
522 find the members of a group, given a group rid and domain
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)
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;
542 *num_names = 0;
544 ads = ads_cached_connection(domain);
545 if (!ads) goto done;
547 sid_from_rid(domain, group_rid, &group_sid);
548 sidstr = sid_binstring(&group_sid);
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);
556 if (!ADS_ERR_OK(rc)) {
557 DEBUG(1,("query_user_list ads_search: %s\n", ads_errstr(rc)));
558 goto done;
561 count = ads_count_replies(ads, res);
562 if (count == 0) {
563 status = NT_STATUS_OK;
564 goto done;
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;
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
577 for (i=0;members[i];i++) /* noop */ ;
578 num_members = i;
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);
584 for (i=0;i<num_members;i++) {
585 uint32 name_type, rid;
586 char *name;
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)++;
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);
601 return status;
605 /* find the sequence number for a domain */
606 static NTSTATUS sequence_number(struct winbindd_domain *domain, uint32 *seq)
608 ADS_STRUCT *ads = NULL;
609 ADS_STATUS rc;
611 *seq = DOM_SEQUENCE_NONE;
613 ads = ads_cached_connection(domain);
614 if (!ads) return NT_STATUS_UNSUCCESSFUL;
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;
622 return ads_ntstatus(rc);
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)
633 ADS_STRUCT *ads;
634 ADS_STATUS rc;
636 *num_domains = 0;
637 *names = NULL;
639 ads = ads_cached_connection(domain);
640 if (!ads) return NT_STATUS_UNSUCCESSFUL;
642 rc = ads_trusted_domains(ads, mem_ctx, num_domains, names, alt_names, dom_sids);
644 return ads_ntstatus(rc);
647 /* find the domain sid for a domain */
648 static NTSTATUS domain_sid(struct winbindd_domain *domain, DOM_SID *sid)
650 ADS_STRUCT *ads;
651 ADS_STATUS rc;
653 ads = ads_cached_connection(domain);
654 if (!ads) return NT_STATUS_UNSUCCESSFUL;
656 rc = ads_domain_sid(ads, sid);
658 if (!ADS_ERR_OK(rc)) {
659 /* its a dead connection */
660 ads_destroy(&ads);
661 domain->private = NULL;
664 return ads_ntstatus(rc);
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)
672 ADS_STRUCT *ads;
673 ADS_STATUS rc;
674 TALLOC_CTX *ctx;
675 char *workgroup;
677 ads = ads_cached_connection(domain);
678 if (!ads) return NT_STATUS_UNSUCCESSFUL;
680 if (!(ctx = talloc_init("alternate_name"))) {
681 return NT_STATUS_NO_MEMORY;
684 rc = ads_workgroup_name(ads, ctx, &workgroup);
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);
693 talloc_destroy(ctx);
695 return ads_ntstatus(rc);
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
715 #endif