2 Unix SMB/Netbios implementation.
4 Winbind daemon for ntdom nss module
6 Copyright (C) Tim Potter 2000-2001
7 Copyright (C) 2001 by Martin Pool <mbp@samba.org>
9 This program is free software; you can redistribute it and/or modify
10 it under the terms of the GNU General Public License as published by
11 the Free Software Foundation; either version 2 of the License, or
12 (at your option) any later version.
14 This program is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 GNU General Public License for more details.
19 You should have received a copy of the GNU General Public License
20 along with this program; if not, write to the Free Software
21 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
28 * @file winbindd_util.c
30 * Winbind daemon for NT domain authentication nss module.
35 * Used to clobber name fields that have an undefined value.
37 * Correct code should never look at a field that has this value.
39 static const fstring name_deadbeef
= "<deadbeef>";
42 /* Globals for domain list stuff */
44 struct winbindd_domain
*domain_list
= NULL
;
46 /* Given a domain name, return the struct winbindd domain info for it
47 if it is actually working. */
49 struct winbindd_domain
*find_domain_from_name(char *domain_name
)
51 struct winbindd_domain
*tmp
;
53 if (domain_list
== NULL
)
56 /* Search through list */
58 for (tmp
= domain_list
; tmp
!= NULL
; tmp
= tmp
->next
) {
59 if (strcmp(domain_name
, tmp
->name
) == 0)
68 /* Given a domain name, return the struct winbindd domain info for it */
70 struct winbindd_domain
*find_domain_from_sid(DOM_SID
*sid
)
72 struct winbindd_domain
*tmp
;
74 if (domain_list
== NULL
)
77 /* Search through list */
79 for (tmp
= domain_list
; tmp
!= NULL
; tmp
= tmp
->next
) {
80 if (sid_equal(sid
, &tmp
->sid
))
89 /* Add a trusted domain to our list of domains */
91 static struct winbindd_domain
*add_trusted_domain(char *domain_name
,
94 struct winbindd_domain
*domain
, *tmp
;
96 for (tmp
= domain_list
; tmp
!= NULL
; tmp
= tmp
->next
) {
97 if (strcmp(domain_name
, tmp
->name
) == 0) {
98 DEBUG(3, ("domain %s already in domain list\n", domain_name
));
103 DEBUG(1, ("adding domain %s\n", domain_name
));
105 /* Create new domain entry */
107 if ((domain
= (struct winbindd_domain
*)malloc(sizeof(*domain
))) == NULL
)
112 ZERO_STRUCTP(domain
);
113 fstrcpy(domain
->name
, domain_name
);
114 sid_copy(&domain
->sid
, domain_sid
);
116 /* Link to domain list */
118 DLIST_ADD(domain_list
, domain
);
123 /* Look up global info for the winbind daemon */
125 BOOL
get_domain_info(void)
127 uint32 enum_ctx
= 0, num_doms
= 0;
128 char **domains
= NULL
;
129 DOM_SID
*sids
= NULL
, domain_sid
;
137 DEBUG(1, ("getting trusted domain list\n"));
139 if (!(mem_ctx
= talloc_init()))
142 /* Add our workgroup - keep handle to look up trusted domains */
144 if (!(hnd
= cm_get_lsa_handle(lp_workgroup())))
147 result
= cli_lsa_query_info_policy(hnd
->cli
, mem_ctx
,
148 &hnd
->pol
, 0x05, level5_dom
, &domain_sid
);
150 if (!NT_STATUS_IS_OK(result
))
153 add_trusted_domain(lp_workgroup(), &domain_sid
);
155 /* Enumerate list of trusted domains */
157 if (!(hnd
= cm_get_lsa_handle(lp_workgroup())))
160 result
= cli_lsa_enum_trust_dom(hnd
->cli
, mem_ctx
,
161 &hnd
->pol
, &enum_ctx
, &num_doms
, &domains
, &sids
);
163 if (!NT_STATUS_IS_OK(result
))
166 /* Add each domain to the trusted domain list */
168 for(i
= 0; i
< num_doms
; i
++)
169 add_trusted_domain(domains
[i
], &sids
[i
]);
175 talloc_destroy(mem_ctx
);
180 /* Free global domain info */
182 void free_domain_info(void)
184 struct winbindd_domain
*domain
;
186 /* Free list of domains */
189 struct winbindd_domain
*next_domain
;
191 domain
= domain_list
;
194 next_domain
= domain
->next
;
196 domain
= next_domain
;
201 /* Connect to a domain controller using get_any_dc_name() to discover
202 the domain name and sid */
204 BOOL
lookup_domain_sid(char *domain_name
, struct winbindd_domain
*domain
)
207 uint32 enum_ctx
= 0, num_doms
= 0;
208 char **domains
= NULL
;
209 DOM_SID
*sids
= NULL
;
215 DEBUG(1, ("looking up sid for domain %s\n", domain_name
));
217 if (!(mem_ctx
= talloc_init()))
220 if (!(hnd
= cm_get_lsa_handle(domain_name
)))
223 /* Do a level 5 query info policy if we are looking up the SID for
226 if (strequal(domain_name
, lp_workgroup())) {
228 result
= cli_lsa_query_info_policy(hnd
->cli
, mem_ctx
,
229 &hnd
->pol
, 0x05, level5_dom
,
232 rv
= NT_STATUS_IS_OK(result
);
236 /* Use lsaenumdomains to get sid for this domain */
238 result
= cli_lsa_enum_trust_dom(hnd
->cli
, mem_ctx
, &hnd
->pol
,
239 &enum_ctx
, &num_doms
, &domains
, &sids
);
241 /* Look for domain name */
243 if (NT_STATUS_IS_OK(result
) && domains
&& sids
) {
247 for(i
= 0; i
< num_doms
; i
++) {
248 if (strequal(domain_name
, domains
[i
])) {
249 sid_copy(&domain
->sid
, &sids
[i
]);
259 rv
= False
; /* An error occured with a trusted domain */
263 talloc_destroy(mem_ctx
);
268 /* Store a SID in a domain indexed by name in the cache. */
270 static void store_sid_by_name_in_cache(fstring name
, DOM_SID
*sid
, enum SID_NAME_USE type
)
274 struct winbindd_sid sid_val
;
275 struct winbindd_domain
*domain
;
277 /* Get name from domain. */
278 fstrcpy( domain_str
, name
);
279 p
= strchr(domain_str
, '\\');
283 if ((domain
= find_domain_from_name(domain_str
)) == NULL
)
286 sid_to_string(sid_val
.sid
, sid
);
287 sid_val
.type
= (int)type
;
289 DEBUG(10,("store_sid_by_name_in_cache: storing cache entry %s -> SID %s\n",
290 name
, sid_val
.sid
));
292 winbindd_store_sid_cache_entry(domain
, name
, &sid_val
);
295 /* Lookup a SID in a domain indexed by name in the cache. */
297 static BOOL
winbindd_lookup_sid_by_name_in_cache(fstring name
, DOM_SID
*sid
, enum SID_NAME_USE
*type
)
301 struct winbindd_sid sid_ret
;
302 struct winbindd_domain
*domain
;
304 /* Get name from domain. */
305 fstrcpy( domain_str
, name
);
306 p
= strchr(domain_str
, '\\');
310 if ((domain
= find_domain_from_name(domain_str
)) == NULL
)
313 if (!winbindd_fetch_sid_cache_entry(domain
, name
, &sid_ret
))
316 string_to_sid( sid
, sid_ret
.sid
);
317 *type
= (enum SID_NAME_USE
)sid_ret
.type
;
319 DEBUG(10,("winbindd_lookup_sid_by_name_in_cache: Cache hit for name %s. SID = %s\n",
320 name
, sid_ret
.sid
));
325 /* Store a name in a domain indexed by SID in the cache. */
327 static void store_name_by_sid_in_cache(DOM_SID
*sid
, fstring name
, enum SID_NAME_USE type
)
332 struct winbindd_name name_val
;
333 struct winbindd_domain
*domain
;
335 /* Split sid into domain sid and user rid */
336 sid_copy(&domain_sid
, sid
);
337 sid_split_rid(&domain_sid
, &rid
);
339 if ((domain
= find_domain_from_sid(&domain_sid
)) == NULL
)
342 sid_to_string(sid_str
, sid
);
343 fstrcpy( name_val
.name
, name
);
344 name_val
.type
= (int)type
;
346 DEBUG(10,("store_name_by_sid_in_cache: storing cache entry SID %s -> %s\n",
347 sid_str
, name_val
.name
));
349 winbindd_store_name_cache_entry(domain
, sid_str
, &name_val
);
352 /* Lookup a name in a domain indexed by SID in the cache. */
354 static BOOL
winbindd_lookup_name_by_sid_in_cache(DOM_SID
*sid
, fstring name
, enum SID_NAME_USE
*type
)
359 struct winbindd_name name_ret
;
360 struct winbindd_domain
*domain
;
362 /* Split sid into domain sid and user rid */
363 sid_copy(&domain_sid
, sid
);
364 sid_split_rid(&domain_sid
, &rid
);
366 if ((domain
= find_domain_from_sid(&domain_sid
)) == NULL
)
369 sid_to_string(sid_str
, sid
);
371 if (!winbindd_fetch_name_cache_entry(domain
, sid_str
, &name_ret
))
374 fstrcpy( name
, name_ret
.name
);
375 *type
= (enum SID_NAME_USE
)name_ret
.type
;
377 DEBUG(10,("winbindd_lookup_name_by_sid_in_cache: Cache hit for SID = %s, name %s\n",
383 /* Lookup a sid in a domain from a name */
385 BOOL
winbindd_lookup_sid_by_name(char *name
, DOM_SID
*sid
, enum SID_NAME_USE
*type
)
387 int num_sids
= 0, num_names
= 1;
388 DOM_SID
*sids
= NULL
;
389 uint32
*types
= NULL
;
395 /* Don't bother with machine accounts */
397 if (name
[strlen(name
) - 1] == '$')
400 /* First check cache. */
401 if (winbindd_lookup_sid_by_name_in_cache(name
, sid
, type
)) {
402 if (*type
== SID_NAME_USE_NONE
)
403 return False
; /* Negative cache hit. */
409 if (!(mem_ctx
= talloc_init()))
412 if (!(hnd
= cm_get_lsa_handle(lp_workgroup())))
415 result
= cli_lsa_lookup_names(hnd
->cli
, mem_ctx
, &hnd
->pol
,
416 num_names
, (char **)&name
, &sids
,
419 /* Return rid and type if lookup successful */
421 if (NT_STATUS_IS_OK(result
)) {
425 if ((sid
!= NULL
) && (sids
!= NULL
))
426 sid_copy(sid
, &sids
[0]);
428 /* Return name type */
430 if ((type
!= NULL
) && (types
!= NULL
))
433 /* Store the forward and reverse map of this lookup in the cache. */
434 store_sid_by_name_in_cache(name
, &sids
[0], types
[0]);
435 store_name_by_sid_in_cache(&sids
[0], name
, types
[0]);
437 /* JRA. Here's where we add the -ve cache store with a name type of SID_NAME_USE_NONE. */
440 ZERO_STRUCT(nullsid
);
441 store_sid_by_name_in_cache(name
, &nullsid
, SID_NAME_USE_NONE
);
442 *type
= SID_NAME_UNKNOWN
;
445 rv
= NT_STATUS_IS_OK(result
);
448 talloc_destroy(mem_ctx
);
454 * @brief Lookup a name in a domain from a sid.
456 * @param sid Security ID you want to look up.
458 * @param name On success, set to the name corresponding to @p sid.
460 * @param type On success, contains the type of name: alias, group or
463 * @retval True if the name exists, in which case @p name and @p type
464 * are set, otherwise False.
466 BOOL
winbindd_lookup_name_by_sid(DOM_SID
*sid
,
468 enum SID_NAME_USE
*type
)
470 int num_sids
= 1, num_names
= 0;
471 uint32
*types
= NULL
;
478 /* First check cache. */
479 if (winbindd_lookup_name_by_sid_in_cache(sid
, name
, type
)) {
480 if (*type
== SID_NAME_USE_NONE
) {
481 fstrcpy(name
, name_deadbeef
);
482 *type
= SID_NAME_UNKNOWN
;
483 return False
; /* Negative cache hit. */
490 if (!(mem_ctx
= talloc_init()))
493 if (!(hnd
= cm_get_lsa_handle(lp_workgroup())))
496 result
= cli_lsa_lookup_sids(hnd
->cli
, mem_ctx
, &hnd
->pol
,
497 num_sids
, sid
, &names
, &types
,
500 /* Return name and type if successful */
502 if ((rv
= NT_STATUS_IS_OK(result
))) {
506 if ((names
!= NULL
) && (name
!= NULL
))
507 fstrcpy(name
, names
[0]);
509 /* Return name type */
511 if ((type
!= NULL
) && (types
!= NULL
))
514 store_sid_by_name_in_cache(names
[0], sid
, types
[0]);
515 store_name_by_sid_in_cache(sid
, names
[0], types
[0]);
517 /* OK, so we tried to look up a name in this sid, and
518 * didn't find it. Therefore add a negative cache
520 store_name_by_sid_in_cache(sid
, "", SID_NAME_USE_NONE
);
521 *type
= SID_NAME_UNKNOWN
;
522 fstrcpy(name
, name_deadbeef
);
527 talloc_destroy(mem_ctx
);
532 /* Lookup user information from a rid */
534 BOOL
winbindd_lookup_userinfo(struct winbindd_domain
*domain
,
535 TALLOC_CTX
*mem_ctx
, uint32 user_rid
,
536 SAM_USERINFO_CTR
**user_info
)
539 uint16 info_level
= 0x15;
540 NTSTATUS result
= NT_STATUS_UNSUCCESSFUL
;
541 uint32 des_access
= SEC_RIGHTS_MAXIMUM_ALLOWED
;
542 POLICY_HND dom_pol
, user_pol
;
543 BOOL got_dom_pol
= False
, got_user_pol
= False
;
547 if (!(hnd
= cm_get_sam_handle(domain
->name
)))
550 /* Get domain handle */
552 result
= cli_samr_open_domain(hnd
->cli
, mem_ctx
, &hnd
->pol
,
553 des_access
, &domain
->sid
, &dom_pol
);
555 if (!NT_STATUS_IS_OK(result
))
560 /* Get user handle */
562 result
= cli_samr_open_user(hnd
->cli
, mem_ctx
, &dom_pol
,
563 des_access
, user_rid
, &user_pol
);
565 if (!NT_STATUS_IS_OK(result
))
570 result
= cli_samr_query_userinfo(hnd
->cli
, mem_ctx
, &user_pol
,
571 info_level
, user_info
);
573 cli_samr_close(hnd
->cli
, mem_ctx
, &user_pol
);
576 /* Clean up policy handles */
579 cli_samr_close(hnd
->cli
, mem_ctx
, &user_pol
);
582 cli_samr_close(hnd
->cli
, mem_ctx
, &dom_pol
);
584 return NT_STATUS_IS_OK(result
);
587 /* Lookup groups a user is a member of. I wish Unix had a call like this! */
589 BOOL
winbindd_lookup_usergroups(struct winbindd_domain
*domain
,
591 uint32 user_rid
, uint32
*num_groups
,
592 DOM_GID
**user_groups
)
595 NTSTATUS result
= NT_STATUS_UNSUCCESSFUL
;
596 POLICY_HND dom_pol
, user_pol
;
597 uint32 des_access
= SEC_RIGHTS_MAXIMUM_ALLOWED
;
598 BOOL got_dom_pol
= False
, got_user_pol
= False
;
602 if (!(hnd
= cm_get_sam_handle(domain
->name
)))
605 /* Get domain handle */
607 result
= cli_samr_open_domain(hnd
->cli
, mem_ctx
, &hnd
->pol
,
608 des_access
, &domain
->sid
, &dom_pol
);
610 if (!NT_STATUS_IS_OK(result
))
615 /* Get user handle */
617 result
= cli_samr_open_user(hnd
->cli
, mem_ctx
, &dom_pol
,
618 des_access
, user_rid
, &user_pol
);
620 if (!NT_STATUS_IS_OK(result
))
625 /* Query user rids */
627 result
= cli_samr_query_usergroups(hnd
->cli
, mem_ctx
, &user_pol
,
628 num_groups
, user_groups
);
632 /* Clean up policy handles */
635 cli_samr_close(hnd
->cli
, mem_ctx
, &user_pol
);
638 cli_samr_close(hnd
->cli
, mem_ctx
, &dom_pol
);
640 return NT_STATUS_IS_OK(result
);
643 /* Lookup group membership given a rid. */
645 BOOL
winbindd_lookup_groupmem(struct winbindd_domain
*domain
,
647 uint32 group_rid
, uint32
*num_names
,
648 uint32
**rid_mem
, char ***names
,
652 NTSTATUS result
= NT_STATUS_UNSUCCESSFUL
;
653 uint32 i
, total_names
= 0;
654 POLICY_HND dom_pol
, group_pol
;
655 uint32 des_access
= SEC_RIGHTS_MAXIMUM_ALLOWED
;
656 BOOL got_dom_pol
= False
, got_group_pol
= False
;
660 if (!(hnd
= cm_get_sam_handle(domain
->name
)))
663 /* Get domain handle */
665 result
= cli_samr_open_domain(hnd
->cli
, mem_ctx
, &hnd
->pol
,
666 des_access
, &domain
->sid
, &dom_pol
);
668 if (!NT_STATUS_IS_OK(result
))
673 /* Get group handle */
675 result
= cli_samr_open_group(hnd
->cli
, mem_ctx
, &dom_pol
,
676 des_access
, group_rid
, &group_pol
);
678 if (!NT_STATUS_IS_OK(result
))
681 got_group_pol
= True
;
683 /* Step #1: Get a list of user rids that are the members of the
686 result
= cli_samr_query_groupmem(hnd
->cli
, mem_ctx
,
687 &group_pol
, num_names
, rid_mem
,
690 if (!NT_STATUS_IS_OK(result
))
693 /* Step #2: Convert list of rids into list of usernames. Do this
694 in bunches of ~1000 to avoid crashing NT4. It looks like there
695 is a buffer overflow or something like that lurking around
698 #define MAX_LOOKUP_RIDS 900
700 *names
= talloc(mem_ctx
, *num_names
* sizeof(char *));
701 *name_types
= talloc(mem_ctx
, *num_names
* sizeof(uint32
));
703 for (i
= 0; i
< *num_names
; i
+= MAX_LOOKUP_RIDS
) {
704 int num_lookup_rids
= MIN(*num_names
- i
, MAX_LOOKUP_RIDS
);
705 uint32 tmp_num_names
= 0;
706 char **tmp_names
= NULL
;
707 uint32
*tmp_types
= NULL
;
709 /* Lookup a chunk of rids */
711 result
= cli_samr_lookup_rids(hnd
->cli
, mem_ctx
,
712 &dom_pol
, 1000, /* flags */
716 &tmp_names
, &tmp_types
);
718 if (!NT_STATUS_IS_OK(result
))
721 /* Copy result into array. The talloc system will take
722 care of freeing the temporary arrays later on. */
724 memcpy(&(*names
)[i
], tmp_names
, sizeof(char *) *
727 memcpy(&(*name_types
)[i
], tmp_types
, sizeof(uint32
) *
730 total_names
+= tmp_num_names
;
733 *num_names
= total_names
;
737 cli_samr_close(hnd
->cli
, mem_ctx
, &group_pol
);
740 cli_samr_close(hnd
->cli
, mem_ctx
, &dom_pol
);
742 return NT_STATUS_IS_OK(result
);
745 BOOL
create_samr_domain_handle(struct winbindd_domain
*domain
, POLICY_HND
*pdom_pol
)
748 NTSTATUS result
= NT_STATUS_UNSUCCESSFUL
;
749 uint32 des_access
= SEC_RIGHTS_MAXIMUM_ALLOWED
;
750 TALLOC_CTX
*mem_ctx
= talloc_init();
752 ZERO_STRUCTP(pdom_pol
);
759 if (!(hnd
= cm_get_sam_handle(domain
->name
))) {
760 talloc_destroy(mem_ctx
);
763 /* Get domain handle */
764 result
= cli_samr_open_domain(hnd
->cli
, mem_ctx
, &hnd
->pol
,
765 des_access
, &domain
->sid
, pdom_pol
);
767 talloc_destroy(mem_ctx
);
768 if (!NT_STATUS_IS_OK(result
))
774 void close_samr_domain_handle(struct winbindd_domain
*domain
, POLICY_HND
*pdom_pol
)
776 static POLICY_HND zero_pol
;
778 TALLOC_CTX
*mem_ctx
= talloc_init();
783 if (memcmp(pdom_pol
, &zero_pol
, sizeof(zero_pol
)) == 0)
786 if (!(hnd
= cm_get_sam_handle(domain
->name
))) {
787 talloc_destroy(mem_ctx
);
791 cli_samr_close(hnd
->cli
, mem_ctx
, pdom_pol
);
792 ZERO_STRUCTP(pdom_pol
);
794 talloc_destroy(mem_ctx
);
797 /* Free state information held for {set,get,end}{pw,gr}ent() functions */
799 void free_getent_state(struct getent_state
*state
)
801 struct getent_state
*temp
;
803 /* Iterate over state list */
807 while(temp
!= NULL
) {
808 struct getent_state
*next
;
810 /* Close SAMR cache handle. */
811 close_samr_domain_handle(temp
->domain
, &temp
->dom_pol
);
813 /* Free sam entries then list entry */
815 SAFE_FREE(state
->sam_entries
);
816 DLIST_REMOVE(state
, state
);
824 struct getent_state
*create_getent_state(struct winbindd_domain
*domain
)
826 struct getent_state
*state
= (struct getent_state
*)malloc(sizeof(struct getent_state
));
832 state
->domain
= domain
;
834 /* Create and cache a SAMR domain handle. */
835 if (!create_samr_domain_handle(state
->domain
, &state
->dom_pol
)) {
836 free_getent_state(state
);
843 /* Initialise trusted domain info */
845 BOOL
winbindd_param_init(void)
847 /* Parse winbind uid and winbind_gid parameters */
849 if (!lp_winbind_uid(&server_state
.uid_low
, &server_state
.uid_high
)) {
850 DEBUG(0, ("winbind uid range missing or invalid\n"));
854 if (!lp_winbind_gid(&server_state
.gid_low
, &server_state
.gid_high
)) {
855 DEBUG(0, ("winbind gid range missing or invalid\n"));
862 /* Query display info for a domain. This returns enough information plus a
863 bit extra to give an overview of domain users for the User Manager
866 NTSTATUS
winbindd_query_dispinfo(struct winbindd_domain
*domain
,
867 TALLOC_CTX
*mem_ctx
, POLICY_HND
*pdom_pol
,
868 uint32
*start_ndx
, uint16 info_level
,
869 uint32
*num_entries
, SAM_DISPINFO_CTR
*ctr
)
872 NTSTATUS result
= NT_STATUS_UNSUCCESSFUL
;
876 if (!(hnd
= cm_get_sam_handle(domain
->name
)))
877 return NT_STATUS_UNSUCCESSFUL
;
879 /* Query display info */
881 result
= cli_samr_query_dispinfo(hnd
->cli
, mem_ctx
,
882 pdom_pol
, start_ndx
, info_level
,
883 num_entries
, 0xffff, ctr
);
888 /* Check if a domain is present in a comma-separated list of domains */
890 BOOL
check_domain_env(char *domain_env
, char *domain
)
893 char *tmp
= domain_env
;
895 while(next_token(&tmp
, name
, ",", sizeof(fstring
))) {
896 if (strequal(name
, domain
))
903 /* Parse a string of the form DOMAIN/user into a domain and a user */
905 BOOL
parse_domain_user(char *domuser
, fstring domain
, fstring user
)
907 char *p
= strchr(domuser
,*lp_winbind_separator());
913 fstrcpy(domain
, domuser
);
914 domain
[PTR_DIFF(p
, domuser
)] = 0;