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.
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
)
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);
53 ads
= ads_init(domain
->alt_name
, domain
->name
, NULL
);
55 DEBUG(1,("ads_init for domain %s failed\n", domain
->name
));
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();
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
)));
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
;
85 /* remember our primary realm for trusted domain support */
87 primary_realm
= strdup(ads
->config
.realm
);
90 domain
->private = (void *)ads
;
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
,
106 WINBIND_USERINFO
**info
)
108 ADS_STRUCT
*ads
= NULL
;
109 const char *attrs
[] = {"userPrincipalName",
111 "name", "objectSid", "primaryGroupID",
112 "sAMAccountType", NULL
};
117 NTSTATUS status
= NT_STATUS_UNSUCCESSFUL
;
121 DEBUG(3,("ads: query_user_list\n"));
123 ads
= ads_cached_connection(domain
);
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
)));
132 count
= ads_count_replies(ads
, res
);
134 DEBUG(1,("query_user_list: No users found\n"));
138 (*info
) = talloc_zero(mem_ctx
, count
* sizeof(**info
));
140 status
= NT_STATUS_NO_MEMORY
;
146 for (msg
= ads_first_entry(ads
, res
); msg
; msg
= ads_next_entry(ads
, msg
)) {
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
));
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
));
164 if (!ads_pull_uint32(ads
, msg
, "primaryGroupID", &group
)) {
165 DEBUG(1,("No primary group for %s !?\n", name
));
169 if (!sid_peek_check_rid(&domain
->sid
, &sid
, &rid
)) {
170 DEBUG(1,("No rid for %s !?\n", name
));
174 (*info
)[i
].acct_name
= name
;
175 (*info
)[i
].full_name
= gecos
;
176 (*info
)[i
].user_rid
= rid
;
177 (*info
)[i
].group_rid
= group
;
182 status
= NT_STATUS_OK
;
184 DEBUG(3,("ads query_user_list gave %d entries\n", (*num_entries
)));
187 if (res
) ads_msgfree(ads
, res
);
192 /* list all domain groups */
193 static NTSTATUS
enum_dom_groups(struct winbindd_domain
*domain
,
196 struct acct_info
**info
)
198 ADS_STRUCT
*ads
= NULL
;
199 const char *attrs
[] = {"userPrincipalName", "sAMAccountName",
201 "sAMAccountType", NULL
};
206 NTSTATUS status
= NT_STATUS_UNSUCCESSFUL
;
211 DEBUG(3,("ads: enum_dom_groups\n"));
213 ads
= ads_cached_connection(domain
);
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
)));
222 count
= ads_count_replies(ads
, res
);
224 DEBUG(1,("enum_dom_groups: No groups found\n"));
228 (*info
) = talloc_zero(mem_ctx
, count
* sizeof(**info
));
230 status
= NT_STATUS_NO_MEMORY
;
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
)) {
246 if (!ads_pull_uint32(ads
, msg
, "sAMAccountType", &account_type
) || !(account_type
& group_flags
) )
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
));
256 if (!sid_peek_check_rid(&domain
->sid
, &sid
, &rid
)) {
257 DEBUG(1,("No rid for %s !?\n", name
));
261 fstrcpy((*info
)[i
].acct_name
, name
);
262 fstrcpy((*info
)[i
].acct_desc
, gecos
);
263 (*info
)[i
].rid
= rid
;
269 status
= NT_STATUS_OK
;
271 DEBUG(3,("ads enum_dom_groups gave %d entries\n", (*num_entries
)));
274 if (res
) ads_msgfree(ads
, res
);
279 /* list all domain local groups */
280 static NTSTATUS
enum_local_groups(struct winbindd_domain
*domain
,
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
291 * if we ever need to enumerate domain local groups separately,
292 * then this the optimization in enum_dom_groups() will need
300 /* convert a single name to a sid in a domain */
301 static NTSTATUS
name_to_sid(struct winbindd_domain
*domain
,
304 enum SID_NAME_USE
*type
)
308 DEBUG(3,("ads: name_to_sid\n"));
310 ads
= ads_cached_connection(domain
);
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
,
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
);
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
,
340 char **name
, uint32
*name_type
, uint32
*rid
)
344 const char *attrs
[] = {"userPrincipalName", "sAMAccountName",
345 "objectSid", "sAMAccountType", NULL
};
350 asprintf(&exp
, "(distinguishedName=%s)", dn
);
351 rc
= ads_search_retry(ads
, &res
, exp
, attrs
);
353 if (!ADS_ERR_OK(rc
)) {
357 (*name
) = ads_pull_username(ads
, mem_ctx
, res
);
359 if (!ads_pull_uint32(ads
, res
, "sAMAccountType", &atype
)) {
362 (*name_type
) = ads_atype_map(atype
);
364 if (!ads_pull_sid(ads
, res
, "objectSid", &sid
) ||
365 !sid_peek_rid(&sid
, rid
)) {
369 if (res
) ads_msgfree(ads
, res
);
373 if (res
) ads_msgfree(ads
, res
);
377 /* Lookup user information from a rid */
378 static NTSTATUS
query_user(struct winbindd_domain
*domain
,
381 WINBIND_USERINFO
*info
)
383 ADS_STRUCT
*ads
= NULL
;
384 const char *attrs
[] = {"userPrincipalName",
387 "primaryGroupID", NULL
};
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
);
403 sidstr
= sid_binstring(&sid
);
404 asprintf(&exp
, "(objectSid=%s)", sidstr
);
405 rc
= ads_search_retry(ads
, &msg
, exp
, attrs
);
408 if (!ADS_ERR_OK(rc
)) {
409 DEBUG(1,("query_user(rid=%d) ads_search: %s\n", user_rid
, ads_errstr(rc
)));
413 count
= ads_count_replies(ads
, msg
);
415 DEBUG(1,("query_user(rid=%d): Not found\n", user_rid
));
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
));
425 if (!ads_pull_uint32(ads
, msg
, "primaryGroupID", &info
->group_rid
)) {
426 DEBUG(1,("No primary group for %d !?\n", user_rid
));
430 if (!sid_peek_check_rid(&domain
->sid
,&sid
, &info
->user_rid
)) {
431 DEBUG(1,("No rid for %d !?\n", user_rid
));
435 status
= NT_STATUS_OK
;
437 DEBUG(3,("ads query_user gave %s\n", info
->acct_name
));
439 if (msg
) ads_msgfree(ads
, msg
);
445 /* Lookup groups a user is a member of. */
446 static NTSTATUS
lookup_usergroups(struct winbindd_domain
*domain
,
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
};
461 uint32 primary_group
;
464 NTSTATUS status
= NT_STATUS_UNSUCCESSFUL
;
468 DEBUG(3,("ads: lookup_usergroups\n"));
472 sid_from_rid(domain
, user_rid
, &sid
);
474 ads
= ads_cached_connection(domain
);
477 sidstr
= sid_binstring(&sid
);
478 asprintf(&exp
, "(objectSid=%s)", sidstr
);
479 rc
= ads_search_retry(ads
, &msg
, exp
, attrs
);
482 if (!ADS_ERR_OK(rc
)) {
483 DEBUG(1,("lookup_usergroups(rid=%d) ads_search: %s\n", user_rid
, ads_errstr(rc
)));
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
)));
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
));
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
++) {
508 if (!sid_peek_check_rid(&domain
->sid
, &sids
[i
-1], &rid
)) continue;
509 (*user_gids
)[*num_groups
] = rid
;
513 status
= NT_STATUS_OK
;
514 DEBUG(3,("ads lookup_usergroups for rid=%d\n", user_rid
));
516 if (msg
) ads_msgfree(ads
, msg
);
522 find the members of a group, given a group rid and domain
524 static NTSTATUS
lookup_groupmem(struct winbindd_domain
*domain
,
526 uint32 group_rid
, uint32
*num_names
,
527 uint32
**rid_mem
, char ***names
,
534 ADS_STRUCT
*ads
= NULL
;
536 NTSTATUS status
= NT_STATUS_UNSUCCESSFUL
;
538 const char *attrs
[] = {"member", NULL
};
544 ads
= ads_cached_connection(domain
);
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
);
556 if (!ADS_ERR_OK(rc
)) {
557 DEBUG(1,("query_user_list ads_search: %s\n", ads_errstr(rc
)));
561 count
= ads_count_replies(ads
, res
);
563 status
= NT_STATUS_OK
;
567 members
= ads_pull_strings(ads
, mem_ctx
, res
, "member");
569 /* no members? ok ... */
570 status
= NT_STATUS_OK
;
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 */ ;
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
;
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
;
596 status
= NT_STATUS_OK
;
597 DEBUG(3,("ads lookup_groupmem for rid=%d\n", group_rid
));
599 if (res
) ads_msgfree(ads
, res
);
605 /* find the sequence number for a domain */
606 static NTSTATUS
sequence_number(struct winbindd_domain
*domain
, uint32
*seq
)
608 ADS_STRUCT
*ads
= NULL
;
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 */
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
,
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
)
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 */
661 domain
->private = NULL
;
664 return ads_ntstatus(rc
);
668 /* find alternate names list for the domain - for ADS this is the
670 static NTSTATUS
alternate_name(struct winbindd_domain
*domain
)
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
);
695 return ads_ntstatus(rc
);
698 /* the ADS backend methods are exposed via this structure */
699 struct winbindd_methods ads_methods
= {