2 Unix SMB/CIFS implementation.
4 Winbind daemon - user related functions
6 Copyright (C) Tim Potter 2000
7 Copyright (C) Jeremy Allison 2001.
8 Copyright (C) Gerald (Jerry) Carter 2003.
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 3 of the License, or
13 (at your option) any later version.
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.
20 You should have received a copy of the GNU General Public License
21 along with this program. If not, see <http://www.gnu.org/licenses/>.
28 #define DBGC_CLASS DBGC_WINBIND
30 static bool fillup_pw_field(const char *lp_template
,
43 /* The substitution of %U and %D in the 'template
44 homedir' is done by talloc_sub_specified() below.
45 If we have an in string (which means the value has already
46 been set in the nss_info backend), then use that.
47 Otherwise use the template value passed in. */
49 if ( in
&& !strequal(in
,"") && lp_security() == SEC_ADS
) {
50 templ
= talloc_sub_specified(NULL
, in
,
54 templ
= talloc_sub_specified(NULL
, lp_template
,
62 safe_strcpy(out
, templ
, sizeof(fstring
) - 1);
68 /* Fill a pwent structure with information we have obtained */
70 static bool winbindd_fill_pwent(TALLOC_CTX
*ctx
, char *dom_name
, char *user_name
,
71 DOM_SID
*user_sid
, DOM_SID
*group_sid
,
72 char *full_name
, char *homedir
, char *shell
,
73 struct winbindd_pw
*pw
)
75 fstring output_username
;
76 char *mapped_name
= NULL
;
77 struct winbindd_domain
*domain
= NULL
;
78 NTSTATUS nt_status
= NT_STATUS_UNSUCCESSFUL
;
80 if (!pw
|| !dom_name
|| !user_name
)
83 domain
= find_domain_from_name_noinit(dom_name
);
85 DEBUG(5,("winbindd_fill_pwent: Failed to find domain for %s. "
86 "Disabling name alias support\n", dom_name
));
87 nt_status
= NT_STATUS_NO_SUCH_DOMAIN
;
90 /* Resolve the uid number */
92 if (!NT_STATUS_IS_OK(idmap_sid_to_uid(dom_name
, user_sid
,
94 DEBUG(1, ("error getting user id for sid %s\n",
95 sid_string_dbg(user_sid
)));
99 /* Resolve the gid number */
101 if (!NT_STATUS_IS_OK(idmap_sid_to_gid(dom_name
, group_sid
,
103 DEBUG(1, ("error getting group id for sid %s\n",
104 sid_string_dbg(group_sid
)));
110 strlower_m(user_name
);
111 nt_status
= normalize_name_map(ctx
, domain
, user_name
, &mapped_name
);
113 /* Basic removal of whitespace */
114 if (NT_STATUS_IS_OK(nt_status
)) {
115 fill_domain_username(output_username
, dom_name
, mapped_name
, True
);
117 /* Complete name replacement */
118 else if (NT_STATUS_EQUAL(nt_status
, NT_STATUS_FILE_RENAMED
)) {
119 fstrcpy(output_username
, mapped_name
);
121 /* No change at all */
123 fill_domain_username(output_username
, dom_name
, user_name
, True
);
126 safe_strcpy(pw
->pw_name
, output_username
, sizeof(pw
->pw_name
) - 1);
128 /* Full name (gecos) */
130 safe_strcpy(pw
->pw_gecos
, full_name
, sizeof(pw
->pw_gecos
) - 1);
132 /* Home directory and shell */
134 if (!fillup_pw_field(lp_template_homedir(), user_name
, dom_name
,
135 pw
->pw_uid
, pw
->pw_gid
, homedir
, pw
->pw_dir
))
138 if (!fillup_pw_field(lp_template_shell(), user_name
, dom_name
,
139 pw
->pw_uid
, pw
->pw_gid
, shell
, pw
->pw_shell
))
142 /* Password - set to "*" as we can't generate anything useful here.
143 Authentication can be done using the pam_winbind module. */
145 safe_strcpy(pw
->pw_passwd
, "*", sizeof(pw
->pw_passwd
) - 1);
150 /* Wrapper for domain->methods->query_user, only on the parent->child pipe */
152 enum winbindd_result
winbindd_dual_userinfo(struct winbindd_domain
*domain
,
153 struct winbindd_cli_state
*state
)
156 WINBIND_USERINFO user_info
;
159 /* Ensure null termination */
160 state
->request
.data
.sid
[sizeof(state
->request
.data
.sid
)-1]='\0';
162 DEBUG(3, ("[%5lu]: lookupsid %s\n", (unsigned long)state
->pid
,
163 state
->request
.data
.sid
));
165 if (!string_to_sid(&sid
, state
->request
.data
.sid
)) {
166 DEBUG(5, ("%s not a SID\n", state
->request
.data
.sid
));
167 return WINBINDD_ERROR
;
170 status
= domain
->methods
->query_user(domain
, state
->mem_ctx
,
172 if (!NT_STATUS_IS_OK(status
)) {
173 DEBUG(1, ("error getting user info for sid %s\n",
174 sid_string_dbg(&sid
)));
175 return WINBINDD_ERROR
;
178 fstrcpy(state
->response
.data
.user_info
.acct_name
, user_info
.acct_name
);
179 fstrcpy(state
->response
.data
.user_info
.full_name
, user_info
.full_name
);
180 fstrcpy(state
->response
.data
.user_info
.homedir
, user_info
.homedir
);
181 fstrcpy(state
->response
.data
.user_info
.shell
, user_info
.shell
);
182 state
->response
.data
.user_info
.primary_gid
= user_info
.primary_gid
;
183 if (!sid_peek_check_rid(&domain
->sid
, &user_info
.group_sid
,
184 &state
->response
.data
.user_info
.group_rid
)) {
185 DEBUG(1, ("Could not extract group rid out of %s\n",
186 sid_string_dbg(&sid
)));
187 return WINBINDD_ERROR
;
193 struct getpwsid_state
{
194 struct winbindd_cli_state
*state
;
195 struct winbindd_domain
*domain
;
204 bool username_mapped
;
207 static void getpwsid_queryuser_recv(void *private_data
, bool success
,
208 const char *acct_name
,
209 const char *full_name
,
214 static void getpwsid_sid2uid_recv(void *private_data
, bool success
, uid_t uid
);
215 static void getpwsid_sid2gid_recv(void *private_data
, bool success
, gid_t gid
);
217 static void winbindd_getpwsid(struct winbindd_cli_state
*state
,
220 struct getpwsid_state
*s
;
222 s
= TALLOC_ZERO_P(state
->mem_ctx
, struct getpwsid_state
);
224 DEBUG(0, ("talloc failed\n"));
229 s
->domain
= find_domain_from_sid_noinit(sid
);
230 if (s
->domain
== NULL
) {
231 DEBUG(3, ("Could not find domain for sid %s\n",
232 sid_string_dbg(sid
)));
236 sid_copy(&s
->user_sid
, sid
);
238 query_user_async(s
->state
->mem_ctx
, s
->domain
, sid
,
239 getpwsid_queryuser_recv
, s
);
243 request_error(state
);
246 static void getpwsid_queryuser_recv(void *private_data
, bool success
,
247 const char *acct_name
,
248 const char *full_name
,
255 struct getpwsid_state
*s
=
256 talloc_get_type_abort(private_data
, struct getpwsid_state
);
258 NTSTATUS nt_status
= NT_STATUS_UNSUCCESSFUL
;
261 DEBUG(5, ("Could not query domain %s SID %s\n",
262 s
->domain
->name
, sid_string_dbg(&s
->user_sid
)));
263 request_error(s
->state
);
267 if ( acct_name
&& *acct_name
) {
268 fstrcpy( username
, acct_name
);
270 char *domain_name
= NULL
;
271 enum lsa_SidType type
;
272 char *user_name
= NULL
;
273 struct winbindd_domain
*domain
= NULL
;
275 domain
= find_lookup_domain_from_sid(&s
->user_sid
);
276 if (domain
== NULL
) {
277 DEBUG(5, ("find_lookup_domain_from_sid(%s) failed\n",
278 sid_string_dbg(&s
->user_sid
)));
279 request_error(s
->state
);
282 winbindd_lookup_name_by_sid(s
->state
->mem_ctx
, domain
,
283 &s
->user_sid
, &domain_name
,
286 /* If this still fails we ar4e done. Just error out */
288 DEBUG(5,("Could not obtain a name for SID %s\n",
289 sid_string_dbg(&s
->user_sid
)));
290 request_error(s
->state
);
294 fstrcpy( username
, user_name
);
297 strlower_m( username
);
298 s
->username
= talloc_strdup(s
->state
->mem_ctx
, username
);
300 nt_status
= normalize_name_map(s
->state
->mem_ctx
, s
->domain
,
301 s
->username
, &mapped_name
);
303 /* Basic removal of whitespace */
304 if (NT_STATUS_IS_OK(nt_status
)) {
305 s
->username
= mapped_name
;
306 s
->username_mapped
= false;
308 /* Complete name replacement */
309 else if (NT_STATUS_EQUAL(nt_status
, NT_STATUS_FILE_RENAMED
)) {
310 s
->username
= mapped_name
;
311 s
->username_mapped
= true;
313 /* No change at all */
315 s
->username_mapped
= false;
318 s
->fullname
= talloc_strdup(s
->state
->mem_ctx
, full_name
);
319 s
->homedir
= talloc_strdup(s
->state
->mem_ctx
, homedir
);
320 s
->shell
= talloc_strdup(s
->state
->mem_ctx
, shell
);
322 sid_copy(&s
->group_sid
, &s
->domain
->sid
);
323 sid_append_rid(&s
->group_sid
, group_rid
);
325 winbindd_sid2uid_async(s
->state
->mem_ctx
, &s
->user_sid
,
326 getpwsid_sid2uid_recv
, s
);
329 static void getpwsid_sid2uid_recv(void *private_data
, bool success
, uid_t uid
)
331 struct getpwsid_state
*s
=
332 talloc_get_type_abort(private_data
, struct getpwsid_state
);
335 DEBUG(5, ("Could not query uid for user %s\\%s\n",
336 s
->domain
->name
, s
->username
));
337 request_error(s
->state
);
342 winbindd_sid2gid_async(s
->state
->mem_ctx
, &s
->group_sid
,
343 getpwsid_sid2gid_recv
, s
);
346 static void getpwsid_sid2gid_recv(void *private_data
, bool success
, gid_t gid
)
348 struct getpwsid_state
*s
=
349 talloc_get_type_abort(private_data
, struct getpwsid_state
);
350 struct winbindd_pw
*pw
;
351 fstring output_username
;
353 /* allow the nss backend to override the primary group ID.
354 If the gid has already been set, then keep it.
355 This makes me feel dirty. If the nss backend already
356 gave us a gid, we don't really care whether the sid2gid()
357 call worked or not. --jerry */
359 if ( s
->gid
== (gid_t
)-1 ) {
362 DEBUG(5, ("Could not query gid for user %s\\%s\n",
363 s
->domain
->name
, s
->username
));
367 /* take what the sid2gid() call gave us */
371 pw
= &s
->state
->response
.data
.pw
;
375 /* allow username to be overridden by the alias mapping */
377 if ( s
->username_mapped
) {
378 fstrcpy( output_username
, s
->username
);
380 fill_domain_username(output_username
, s
->domain
->name
,
384 safe_strcpy(pw
->pw_name
, output_username
, sizeof(pw
->pw_name
) - 1);
385 safe_strcpy(pw
->pw_gecos
, s
->fullname
, sizeof(pw
->pw_gecos
) - 1);
387 if (!fillup_pw_field(lp_template_homedir(), s
->username
,
388 s
->domain
->name
, pw
->pw_uid
, pw
->pw_gid
,
389 s
->homedir
, pw
->pw_dir
)) {
390 DEBUG(5, ("Could not compose homedir\n"));
394 if (!fillup_pw_field(lp_template_shell(), s
->username
,
395 s
->domain
->name
, pw
->pw_uid
, pw
->pw_gid
,
396 s
->shell
, pw
->pw_shell
)) {
397 DEBUG(5, ("Could not compose shell\n"));
401 /* Password - set to "*" as we can't generate anything useful here.
402 Authentication can be done using the pam_winbind module. */
404 safe_strcpy(pw
->pw_passwd
, "*", sizeof(pw
->pw_passwd
) - 1);
406 request_ok(s
->state
);
410 request_error(s
->state
);
413 /* Return a password structure from a username. */
415 static void getpwnam_name2sid_recv(void *private_data
, bool success
,
416 const DOM_SID
*sid
, enum lsa_SidType type
);
418 void winbindd_getpwnam(struct winbindd_cli_state
*state
)
420 struct winbindd_domain
*domain
;
421 fstring domname
, username
;
422 char *mapped_user
= NULL
;
425 NTSTATUS nt_status
= NT_STATUS_UNSUCCESSFUL
;
427 domuser
= state
->request
.data
.username
;
428 dusize
= sizeof(state
->request
.data
.username
);
430 /* Ensure null termination (it's an fstring) */
431 domuser
[dusize
-1] = '\0';
433 DEBUG(3, ("[%5lu]: getpwnam %s\n",
434 (unsigned long)state
->pid
,
437 nt_status
= normalize_name_unmap(state
->mem_ctx
, domuser
,
440 /* If we could not convert from an aliased name or a
441 normalized name, then just use the original name */
443 if (!NT_STATUS_IS_OK(nt_status
) &&
444 !NT_STATUS_EQUAL(nt_status
, NT_STATUS_FILE_RENAMED
))
446 mapped_user
= domuser
;
449 if (!parse_domain_user(mapped_user
, domname
, username
)) {
450 DEBUG(5, ("Could not parse domain user: %s\n", domuser
));
451 request_error(state
);
455 /* Get info for the domain */
457 domain
= find_domain_from_name_noinit(domname
);
459 if (domain
== NULL
) {
460 DEBUG(7, ("could not find domain entry for domain %s. "
461 "Using primary domain\n", domname
));
462 if ( (domain
= find_our_domain()) == NULL
) {
463 DEBUG(0,("Cannot find my primary domain structure!\n"));
464 request_error(state
);
469 if (strequal(domname
, lp_workgroup()) &&
470 lp_winbind_trusted_domains_only() ) {
471 DEBUG(7,("winbindd_getpwnam: My domain -- "
472 "rejecting getpwnam() for %s\\%s.\n",
474 request_error(state
);
478 /* Get rid and name type from name. The following costs 1 packet */
480 winbindd_lookupname_async(state
->mem_ctx
, domname
, username
,
481 getpwnam_name2sid_recv
, WINBINDD_GETPWNAM
,
485 static void getpwnam_name2sid_recv(void *private_data
, bool success
,
486 const DOM_SID
*sid
, enum lsa_SidType type
)
488 struct winbindd_cli_state
*state
=
489 (struct winbindd_cli_state
*)private_data
;
490 fstring domname
, username
;
491 char *domuser
= state
->request
.data
.username
;
494 DEBUG(5, ("Could not lookup name for user %s\n", domuser
));
495 request_error(state
);
499 if ((type
!= SID_NAME_USER
) && (type
!= SID_NAME_COMPUTER
)) {
500 DEBUG(5, ("%s is not a user\n", domuser
));
501 request_error(state
);
505 if (parse_domain_user(domuser
, domname
, username
)) {
506 check_domain_trusted(domname
, sid
);
509 winbindd_getpwsid(state
, sid
);
512 static void getpwuid_recv(void *private_data
, bool success
, const char *sid
)
514 struct winbindd_cli_state
*state
=
515 (struct winbindd_cli_state
*)private_data
;
519 DEBUG(10,("uid2sid_recv: uid [%lu] to sid mapping failed\n.",
520 (unsigned long)(state
->request
.data
.uid
)));
521 request_error(state
);
525 DEBUG(10,("uid2sid_recv: uid %lu has sid %s\n",
526 (unsigned long)(state
->request
.data
.uid
), sid
));
528 if (!string_to_sid(&user_sid
, sid
)) {
529 DEBUG(1,("uid2sid_recv: Could not convert sid %s "
530 "from string\n,", sid
));
531 request_error(state
);
535 winbindd_getpwsid(state
, &user_sid
);
538 /* Return a password structure given a uid number */
539 void winbindd_getpwuid(struct winbindd_cli_state
*state
)
541 uid_t uid
= state
->request
.data
.uid
;
543 DEBUG(3, ("[%5lu]: getpwuid %lu\n",
544 (unsigned long)state
->pid
,
545 (unsigned long)uid
));
547 /* always query idmap via the async interface */
548 /* if this turns to be too slow we will add here
549 * a direct query to the cache */
550 winbindd_uid2sid_async(state
->mem_ctx
, uid
, getpwuid_recv
, state
);
554 * set/get/endpwent functions
557 /* Rewind file pointer for ntdom passwd database */
559 static bool winbindd_setpwent_internal(struct winbindd_cli_state
*state
)
561 struct winbindd_domain
*domain
;
563 DEBUG(3, ("[%5lu]: setpwent\n", (unsigned long)state
->pid
));
565 /* Check user has enabled this */
567 if (!lp_winbind_enum_users()) {
571 /* Free old static data if it exists */
573 if (state
->getpwent_state
!= NULL
) {
574 free_getent_state(state
->getpwent_state
);
575 state
->getpwent_state
= NULL
;
578 /* Create sam pipes for each domain we know about */
580 for(domain
= domain_list(); domain
!= NULL
; domain
= domain
->next
) {
581 struct getent_state
*domain_state
;
584 /* don't add our domaina if we are a PDC or if we
585 are a member of a Samba domain */
587 if ((IS_DC
|| lp_winbind_trusted_domains_only())
588 && strequal(domain
->name
, lp_workgroup())) {
592 /* Create a state record for this domain */
594 domain_state
= SMB_MALLOC_P(struct getent_state
);
596 DEBUG(0, ("malloc failed\n"));
600 ZERO_STRUCTP(domain_state
);
602 fstrcpy(domain_state
->domain_name
, domain
->name
);
604 /* Add to list of open domains */
606 DLIST_ADD(state
->getpwent_state
, domain_state
);
609 state
->getpwent_initialized
= True
;
613 void winbindd_setpwent(struct winbindd_cli_state
*state
)
615 if (winbindd_setpwent_internal(state
)) {
618 request_error(state
);
622 /* Close file pointer to ntdom passwd database */
624 void winbindd_endpwent(struct winbindd_cli_state
*state
)
626 DEBUG(3, ("[%5lu]: endpwent\n", (unsigned long)state
->pid
));
628 free_getent_state(state
->getpwent_state
);
629 state
->getpwent_initialized
= False
;
630 state
->getpwent_state
= NULL
;
634 /* Get partial list of domain users for a domain. We fill in the sam_entries,
635 and num_sam_entries fields with domain user information. The dispinfo_ndx
636 field is incremented to the index of the next user to fetch. Return True if
637 some users were returned, False otherwise. */
639 static bool get_sam_user_entries(struct getent_state
*ent
, TALLOC_CTX
*mem_ctx
)
643 WINBIND_USERINFO
*info
;
644 struct getpwent_user
*name_list
= NULL
;
645 struct winbindd_domain
*domain
;
646 struct winbindd_methods
*methods
;
649 if (ent
->num_sam_entries
)
652 if (!(domain
= find_domain_from_name(ent
->domain_name
))) {
653 DEBUG(3, ("no such domain %s in get_sam_user_entries\n",
658 methods
= domain
->methods
;
660 /* Free any existing user info */
662 SAFE_FREE(ent
->sam_entries
);
663 ent
->num_sam_entries
= 0;
665 /* Call query_user_list to get a list of usernames and user rids */
669 status
= methods
->query_user_list(domain
, mem_ctx
, &num_entries
, &info
);
671 if (!NT_STATUS_IS_OK(status
)) {
672 DEBUG(10,("get_sam_user_entries: "
673 "query_user_list failed with %s\n",
679 name_list
= SMB_REALLOC_ARRAY(name_list
, struct getpwent_user
,
680 ent
->num_sam_entries
+ num_entries
);
682 DEBUG(0,("get_sam_user_entries realloc failed.\n"));
687 for (i
= 0; i
< num_entries
; i
++) {
688 /* Store account name and gecos */
689 if (!info
[i
].acct_name
) {
690 fstrcpy(name_list
[ent
->num_sam_entries
+ i
].name
, "");
692 fstrcpy(name_list
[ent
->num_sam_entries
+ i
].name
,
695 if (!info
[i
].full_name
) {
696 fstrcpy(name_list
[ent
->num_sam_entries
+ i
].gecos
, "");
698 fstrcpy(name_list
[ent
->num_sam_entries
+ i
].gecos
,
701 if (!info
[i
].homedir
) {
702 fstrcpy(name_list
[ent
->num_sam_entries
+ i
].homedir
,"");
704 fstrcpy(name_list
[ent
->num_sam_entries
+ i
].homedir
,
707 if (!info
[i
].shell
) {
708 fstrcpy(name_list
[ent
->num_sam_entries
+ i
].shell
, "");
710 fstrcpy(name_list
[ent
->num_sam_entries
+ i
].shell
,
715 /* User and group ids */
716 sid_copy(&name_list
[ent
->num_sam_entries
+i
].user_sid
,
718 sid_copy(&name_list
[ent
->num_sam_entries
+i
].group_sid
,
722 ent
->num_sam_entries
+= num_entries
;
724 /* Fill in remaining fields */
726 ent
->sam_entries
= name_list
;
727 ent
->sam_entry_index
= 0;
728 return ent
->num_sam_entries
> 0;
731 /* Fetch next passwd entry from ntdom database */
733 #define MAX_GETPWENT_USERS 500
735 void winbindd_getpwent(struct winbindd_cli_state
*state
)
737 struct getent_state
*ent
;
738 struct winbindd_pw
*user_list
;
739 int num_users
, user_list_ndx
;
741 DEBUG(3, ("[%5lu]: getpwent\n", (unsigned long)state
->pid
));
743 /* Check user has enabled this */
745 if (!lp_winbind_enum_users()) {
746 request_error(state
);
750 /* Allocate space for returning a chunk of users */
752 num_users
= MIN(MAX_GETPWENT_USERS
, state
->request
.data
.num_entries
);
754 if (num_users
== 0) {
755 request_error(state
);
759 user_list
= SMB_MALLOC_ARRAY(struct winbindd_pw
, num_users
);
761 request_error(state
);
764 /* will be freed by process_request() */
765 state
->response
.extra_data
.data
= user_list
;
767 memset(user_list
, 0, num_users
* sizeof(struct winbindd_pw
));
769 if (!state
->getpwent_initialized
)
770 winbindd_setpwent_internal(state
);
772 if (!(ent
= state
->getpwent_state
)) {
773 request_error(state
);
777 /* Start sending back users */
779 for (user_list_ndx
= 0; user_list_ndx
< num_users
; ) {
780 struct getpwent_user
*name_list
= NULL
;
783 /* Do we need to fetch another chunk of users? */
785 if (ent
->num_sam_entries
== ent
->sam_entry_index
) {
788 !get_sam_user_entries(ent
, state
->mem_ctx
)) {
789 struct getent_state
*next_ent
;
791 /* Free state information for this domain */
793 SAFE_FREE(ent
->sam_entries
);
795 next_ent
= ent
->next
;
796 DLIST_REMOVE(state
->getpwent_state
, ent
);
802 /* No more domains */
808 name_list
= (struct getpwent_user
*)ent
->sam_entries
;
810 /* Lookup user info */
812 result
= winbindd_fill_pwent(
815 name_list
[ent
->sam_entry_index
].name
,
816 &name_list
[ent
->sam_entry_index
].user_sid
,
817 &name_list
[ent
->sam_entry_index
].group_sid
,
818 name_list
[ent
->sam_entry_index
].gecos
,
819 name_list
[ent
->sam_entry_index
].homedir
,
820 name_list
[ent
->sam_entry_index
].shell
,
821 &user_list
[user_list_ndx
]);
823 /* Add user to return list */
828 state
->response
.data
.num_entries
++;
829 state
->response
.length
+= sizeof(struct winbindd_pw
);
832 DEBUG(1, ("could not lookup domain user %s\n",
833 name_list
[ent
->sam_entry_index
].name
));
835 ent
->sam_entry_index
++;
841 if (user_list_ndx
> 0)
844 request_error(state
);
847 /* List domain users without mapping to unix ids */
848 void winbindd_list_users(struct winbindd_cli_state
*state
)
850 winbindd_list_ent(state
, LIST_USERS
);