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.\n",
87 nt_status
= NT_STATUS_NO_SUCH_DOMAIN
;
91 /* Resolve the uid number */
93 if (!NT_STATUS_IS_OK(idmap_sid_to_uid(domain
->have_idmap_config
?
94 dom_name
: "", user_sid
,
96 DEBUG(1, ("error getting user id for sid %s\n",
97 sid_string_dbg(user_sid
)));
101 /* Resolve the gid number */
103 if (!NT_STATUS_IS_OK(idmap_sid_to_gid(domain
->have_idmap_config
?
104 dom_name
: "", group_sid
,
106 DEBUG(1, ("error getting group id for sid %s\n",
107 sid_string_dbg(group_sid
)));
113 strlower_m(user_name
);
114 nt_status
= normalize_name_map(ctx
, domain
, user_name
, &mapped_name
);
116 /* Basic removal of whitespace */
117 if (NT_STATUS_IS_OK(nt_status
)) {
118 fill_domain_username(output_username
, dom_name
, mapped_name
, True
);
120 /* Complete name replacement */
121 else if (NT_STATUS_EQUAL(nt_status
, NT_STATUS_FILE_RENAMED
)) {
122 fstrcpy(output_username
, mapped_name
);
124 /* No change at all */
126 fill_domain_username(output_username
, dom_name
, user_name
, True
);
129 safe_strcpy(pw
->pw_name
, output_username
, sizeof(pw
->pw_name
) - 1);
131 /* Full name (gecos) */
133 safe_strcpy(pw
->pw_gecos
, full_name
, sizeof(pw
->pw_gecos
) - 1);
135 /* Home directory and shell */
137 if (!fillup_pw_field(lp_template_homedir(), user_name
, dom_name
,
138 pw
->pw_uid
, pw
->pw_gid
, homedir
, pw
->pw_dir
))
141 if (!fillup_pw_field(lp_template_shell(), user_name
, dom_name
,
142 pw
->pw_uid
, pw
->pw_gid
, shell
, pw
->pw_shell
))
145 /* Password - set to "*" as we can't generate anything useful here.
146 Authentication can be done using the pam_winbind module. */
148 safe_strcpy(pw
->pw_passwd
, "*", sizeof(pw
->pw_passwd
) - 1);
153 /* Wrapper for domain->methods->query_user, only on the parent->child pipe */
155 enum winbindd_result
winbindd_dual_userinfo(struct winbindd_domain
*domain
,
156 struct winbindd_cli_state
*state
)
159 WINBIND_USERINFO user_info
;
162 /* Ensure null termination */
163 state
->request
.data
.sid
[sizeof(state
->request
.data
.sid
)-1]='\0';
165 DEBUG(3, ("[%5lu]: lookupsid %s\n", (unsigned long)state
->pid
,
166 state
->request
.data
.sid
));
168 if (!string_to_sid(&sid
, state
->request
.data
.sid
)) {
169 DEBUG(5, ("%s not a SID\n", state
->request
.data
.sid
));
170 return WINBINDD_ERROR
;
173 status
= domain
->methods
->query_user(domain
, state
->mem_ctx
,
175 if (!NT_STATUS_IS_OK(status
)) {
176 DEBUG(1, ("error getting user info for sid %s\n",
177 sid_string_dbg(&sid
)));
178 return WINBINDD_ERROR
;
181 fstrcpy(state
->response
.data
.user_info
.acct_name
, user_info
.acct_name
);
182 fstrcpy(state
->response
.data
.user_info
.full_name
, user_info
.full_name
);
183 fstrcpy(state
->response
.data
.user_info
.homedir
, user_info
.homedir
);
184 fstrcpy(state
->response
.data
.user_info
.shell
, user_info
.shell
);
185 state
->response
.data
.user_info
.primary_gid
= user_info
.primary_gid
;
186 if (!sid_peek_check_rid(&domain
->sid
, &user_info
.group_sid
,
187 &state
->response
.data
.user_info
.group_rid
)) {
188 DEBUG(1, ("Could not extract group rid out of %s\n",
189 sid_string_dbg(&sid
)));
190 return WINBINDD_ERROR
;
196 struct getpwsid_state
{
197 struct winbindd_cli_state
*state
;
198 struct winbindd_domain
*domain
;
207 bool username_mapped
;
210 static void getpwsid_queryuser_recv(void *private_data
, bool success
,
211 const char *acct_name
,
212 const char *full_name
,
217 static void getpwsid_sid2uid_recv(void *private_data
, bool success
, uid_t uid
);
218 static void getpwsid_sid2gid_recv(void *private_data
, bool success
, gid_t gid
);
220 static void winbindd_getpwsid(struct winbindd_cli_state
*state
,
223 struct getpwsid_state
*s
;
225 s
= TALLOC_ZERO_P(state
->mem_ctx
, struct getpwsid_state
);
227 DEBUG(0, ("talloc failed\n"));
232 s
->domain
= find_domain_from_sid_noinit(sid
);
233 if (s
->domain
== NULL
) {
234 DEBUG(3, ("Could not find domain for sid %s\n",
235 sid_string_dbg(sid
)));
239 sid_copy(&s
->user_sid
, sid
);
241 query_user_async(s
->state
->mem_ctx
, s
->domain
, sid
,
242 getpwsid_queryuser_recv
, s
);
246 request_error(state
);
249 static void getpwsid_queryuser_recv(void *private_data
, bool success
,
250 const char *acct_name
,
251 const char *full_name
,
258 struct getpwsid_state
*s
=
259 talloc_get_type_abort(private_data
, struct getpwsid_state
);
261 NTSTATUS nt_status
= NT_STATUS_UNSUCCESSFUL
;
264 DEBUG(5, ("Could not query domain %s SID %s\n",
265 s
->domain
->name
, sid_string_dbg(&s
->user_sid
)));
266 request_error(s
->state
);
270 if ( acct_name
&& *acct_name
) {
271 fstrcpy( username
, acct_name
);
273 char *domain_name
= NULL
;
274 enum lsa_SidType type
;
275 char *user_name
= NULL
;
276 struct winbindd_domain
*domain
= NULL
;
278 domain
= find_lookup_domain_from_sid(&s
->user_sid
);
279 if (domain
== NULL
) {
280 DEBUG(5, ("find_lookup_domain_from_sid(%s) failed\n",
281 sid_string_dbg(&s
->user_sid
)));
282 request_error(s
->state
);
285 winbindd_lookup_name_by_sid(s
->state
->mem_ctx
, domain
,
286 &s
->user_sid
, &domain_name
,
289 /* If this still fails we ar4e done. Just error out */
291 DEBUG(5,("Could not obtain a name for SID %s\n",
292 sid_string_dbg(&s
->user_sid
)));
293 request_error(s
->state
);
297 fstrcpy( username
, user_name
);
300 strlower_m( username
);
301 s
->username
= talloc_strdup(s
->state
->mem_ctx
, username
);
303 nt_status
= normalize_name_map(s
->state
->mem_ctx
, s
->domain
,
304 s
->username
, &mapped_name
);
306 /* Basic removal of whitespace */
307 if (NT_STATUS_IS_OK(nt_status
)) {
308 s
->username
= mapped_name
;
309 s
->username_mapped
= false;
311 /* Complete name replacement */
312 else if (NT_STATUS_EQUAL(nt_status
, NT_STATUS_FILE_RENAMED
)) {
313 s
->username
= mapped_name
;
314 s
->username_mapped
= true;
316 /* No change at all */
318 s
->username_mapped
= false;
321 s
->fullname
= talloc_strdup(s
->state
->mem_ctx
, full_name
);
322 s
->homedir
= talloc_strdup(s
->state
->mem_ctx
, homedir
);
323 s
->shell
= talloc_strdup(s
->state
->mem_ctx
, shell
);
325 sid_copy(&s
->group_sid
, &s
->domain
->sid
);
326 sid_append_rid(&s
->group_sid
, group_rid
);
328 winbindd_sid2uid_async(s
->state
->mem_ctx
, &s
->user_sid
,
329 getpwsid_sid2uid_recv
, s
);
332 static void getpwsid_sid2uid_recv(void *private_data
, bool success
, uid_t uid
)
334 struct getpwsid_state
*s
=
335 talloc_get_type_abort(private_data
, struct getpwsid_state
);
338 DEBUG(5, ("Could not query uid for user %s\\%s\n",
339 s
->domain
->name
, s
->username
));
340 request_error(s
->state
);
345 winbindd_sid2gid_async(s
->state
->mem_ctx
, &s
->group_sid
,
346 getpwsid_sid2gid_recv
, s
);
349 static void getpwsid_sid2gid_recv(void *private_data
, bool success
, gid_t gid
)
351 struct getpwsid_state
*s
=
352 talloc_get_type_abort(private_data
, struct getpwsid_state
);
353 struct winbindd_pw
*pw
;
354 fstring output_username
;
356 /* allow the nss backend to override the primary group ID.
357 If the gid has already been set, then keep it.
358 This makes me feel dirty. If the nss backend already
359 gave us a gid, we don't really care whether the sid2gid()
360 call worked or not. --jerry */
362 if ( s
->gid
== (gid_t
)-1 ) {
365 DEBUG(5, ("Could not query gid for user %s\\%s\n",
366 s
->domain
->name
, s
->username
));
370 /* take what the sid2gid() call gave us */
374 pw
= &s
->state
->response
.data
.pw
;
378 /* allow username to be overridden by the alias mapping */
380 if ( s
->username_mapped
) {
381 fstrcpy( output_username
, s
->username
);
383 fill_domain_username(output_username
, s
->domain
->name
,
387 safe_strcpy(pw
->pw_name
, output_username
, sizeof(pw
->pw_name
) - 1);
388 safe_strcpy(pw
->pw_gecos
, s
->fullname
, sizeof(pw
->pw_gecos
) - 1);
390 if (!fillup_pw_field(lp_template_homedir(), s
->username
,
391 s
->domain
->name
, pw
->pw_uid
, pw
->pw_gid
,
392 s
->homedir
, pw
->pw_dir
)) {
393 DEBUG(5, ("Could not compose homedir\n"));
397 if (!fillup_pw_field(lp_template_shell(), s
->username
,
398 s
->domain
->name
, pw
->pw_uid
, pw
->pw_gid
,
399 s
->shell
, pw
->pw_shell
)) {
400 DEBUG(5, ("Could not compose shell\n"));
404 /* Password - set to "*" as we can't generate anything useful here.
405 Authentication can be done using the pam_winbind module. */
407 safe_strcpy(pw
->pw_passwd
, "*", sizeof(pw
->pw_passwd
) - 1);
409 request_ok(s
->state
);
413 request_error(s
->state
);
416 /* Return a password structure from a username. */
418 static void getpwnam_name2sid_recv(void *private_data
, bool success
,
419 const DOM_SID
*sid
, enum lsa_SidType type
);
421 void winbindd_getpwnam(struct winbindd_cli_state
*state
)
423 struct winbindd_domain
*domain
;
424 fstring domname
, username
;
425 char *mapped_user
= NULL
;
428 NTSTATUS nt_status
= NT_STATUS_UNSUCCESSFUL
;
430 domuser
= state
->request
.data
.username
;
431 dusize
= sizeof(state
->request
.data
.username
);
433 /* Ensure null termination (it's an fstring) */
434 domuser
[dusize
-1] = '\0';
436 DEBUG(3, ("[%5lu]: getpwnam %s\n",
437 (unsigned long)state
->pid
,
440 nt_status
= normalize_name_unmap(state
->mem_ctx
, domuser
,
443 /* If we could not convert from an aliased name or a
444 normalized name, then just use the original name */
446 if (!NT_STATUS_IS_OK(nt_status
) &&
447 !NT_STATUS_EQUAL(nt_status
, NT_STATUS_FILE_RENAMED
))
449 mapped_user
= domuser
;
452 if (!parse_domain_user(mapped_user
, domname
, username
)) {
453 DEBUG(5, ("Could not parse domain user: %s\n", domuser
));
454 request_error(state
);
458 /* Get info for the domain */
460 domain
= find_domain_from_name_noinit(domname
);
462 if (domain
== NULL
) {
463 DEBUG(7, ("could not find domain entry for domain %s. "
464 "Using primary domain\n", domname
));
465 if ( (domain
= find_our_domain()) == NULL
) {
466 DEBUG(0,("Cannot find my primary domain structure!\n"));
467 request_error(state
);
472 if (strequal(domname
, lp_workgroup()) &&
473 lp_winbind_trusted_domains_only() ) {
474 DEBUG(7,("winbindd_getpwnam: My domain -- "
475 "rejecting getpwnam() for %s\\%s.\n",
477 request_error(state
);
481 /* Get rid and name type from name. The following costs 1 packet */
483 winbindd_lookupname_async(state
->mem_ctx
, domname
, username
,
484 getpwnam_name2sid_recv
, WINBINDD_GETPWNAM
,
488 static void getpwnam_name2sid_recv(void *private_data
, bool success
,
489 const DOM_SID
*sid
, enum lsa_SidType type
)
491 struct winbindd_cli_state
*state
=
492 (struct winbindd_cli_state
*)private_data
;
493 fstring domname
, username
;
494 char *domuser
= state
->request
.data
.username
;
497 DEBUG(5, ("Could not lookup name for user %s\n", domuser
));
498 request_error(state
);
502 if ((type
!= SID_NAME_USER
) && (type
!= SID_NAME_COMPUTER
)) {
503 DEBUG(5, ("%s is not a user\n", domuser
));
504 request_error(state
);
508 if (parse_domain_user(domuser
, domname
, username
)) {
509 check_domain_trusted(domname
, sid
);
512 winbindd_getpwsid(state
, sid
);
515 static void getpwuid_recv(void *private_data
, bool success
, const char *sid
)
517 struct winbindd_cli_state
*state
=
518 (struct winbindd_cli_state
*)private_data
;
522 DEBUG(10,("uid2sid_recv: uid [%lu] to sid mapping failed\n.",
523 (unsigned long)(state
->request
.data
.uid
)));
524 request_error(state
);
528 DEBUG(10,("uid2sid_recv: uid %lu has sid %s\n",
529 (unsigned long)(state
->request
.data
.uid
), sid
));
531 if (!string_to_sid(&user_sid
, sid
)) {
532 DEBUG(1,("uid2sid_recv: Could not convert sid %s "
533 "from string\n,", sid
));
534 request_error(state
);
538 winbindd_getpwsid(state
, &user_sid
);
541 /* Return a password structure given a uid number */
542 void winbindd_getpwuid(struct winbindd_cli_state
*state
)
544 uid_t uid
= state
->request
.data
.uid
;
546 DEBUG(3, ("[%5lu]: getpwuid %lu\n",
547 (unsigned long)state
->pid
,
548 (unsigned long)uid
));
550 /* always query idmap via the async interface */
551 /* if this turns to be too slow we will add here
552 * a direct query to the cache */
553 winbindd_uid2sid_async(state
->mem_ctx
, uid
, getpwuid_recv
, state
);
557 * set/get/endpwent functions
560 /* Rewind file pointer for ntdom passwd database */
562 static bool winbindd_setpwent_internal(struct winbindd_cli_state
*state
)
564 struct winbindd_domain
*domain
;
566 DEBUG(3, ("[%5lu]: setpwent\n", (unsigned long)state
->pid
));
568 /* Check user has enabled this */
570 if (!lp_winbind_enum_users()) {
574 /* Free old static data if it exists */
576 if (state
->getpwent_state
!= NULL
) {
577 free_getent_state(state
->getpwent_state
);
578 state
->getpwent_state
= NULL
;
581 /* Create sam pipes for each domain we know about */
583 for(domain
= domain_list(); domain
!= NULL
; domain
= domain
->next
) {
584 struct getent_state
*domain_state
;
587 /* don't add our domaina if we are a PDC or if we
588 are a member of a Samba domain */
590 if ((IS_DC
|| lp_winbind_trusted_domains_only())
591 && strequal(domain
->name
, lp_workgroup())) {
595 /* Create a state record for this domain */
597 domain_state
= SMB_MALLOC_P(struct getent_state
);
599 DEBUG(0, ("malloc failed\n"));
603 ZERO_STRUCTP(domain_state
);
605 fstrcpy(domain_state
->domain_name
, domain
->name
);
607 /* Add to list of open domains */
609 DLIST_ADD(state
->getpwent_state
, domain_state
);
612 state
->getpwent_initialized
= True
;
616 void winbindd_setpwent(struct winbindd_cli_state
*state
)
618 if (winbindd_setpwent_internal(state
)) {
621 request_error(state
);
625 /* Close file pointer to ntdom passwd database */
627 void winbindd_endpwent(struct winbindd_cli_state
*state
)
629 DEBUG(3, ("[%5lu]: endpwent\n", (unsigned long)state
->pid
));
631 free_getent_state(state
->getpwent_state
);
632 state
->getpwent_initialized
= False
;
633 state
->getpwent_state
= NULL
;
637 /* Get partial list of domain users for a domain. We fill in the sam_entries,
638 and num_sam_entries fields with domain user information. The dispinfo_ndx
639 field is incremented to the index of the next user to fetch. Return True if
640 some users were returned, False otherwise. */
642 static bool get_sam_user_entries(struct getent_state
*ent
, TALLOC_CTX
*mem_ctx
)
646 WINBIND_USERINFO
*info
;
647 struct getpwent_user
*name_list
= NULL
;
648 struct winbindd_domain
*domain
;
649 struct winbindd_methods
*methods
;
652 if (ent
->num_sam_entries
)
655 if (!(domain
= find_domain_from_name(ent
->domain_name
))) {
656 DEBUG(3, ("no such domain %s in get_sam_user_entries\n",
661 methods
= domain
->methods
;
663 /* Free any existing user info */
665 SAFE_FREE(ent
->sam_entries
);
666 ent
->num_sam_entries
= 0;
668 /* Call query_user_list to get a list of usernames and user rids */
672 status
= methods
->query_user_list(domain
, mem_ctx
, &num_entries
, &info
);
674 if (!NT_STATUS_IS_OK(status
)) {
675 DEBUG(10,("get_sam_user_entries: "
676 "query_user_list failed with %s\n",
682 name_list
= SMB_REALLOC_ARRAY(name_list
, struct getpwent_user
,
683 ent
->num_sam_entries
+ num_entries
);
685 DEBUG(0,("get_sam_user_entries realloc failed.\n"));
690 for (i
= 0; i
< num_entries
; i
++) {
691 /* Store account name and gecos */
692 if (!info
[i
].acct_name
) {
693 fstrcpy(name_list
[ent
->num_sam_entries
+ i
].name
, "");
695 fstrcpy(name_list
[ent
->num_sam_entries
+ i
].name
,
698 if (!info
[i
].full_name
) {
699 fstrcpy(name_list
[ent
->num_sam_entries
+ i
].gecos
, "");
701 fstrcpy(name_list
[ent
->num_sam_entries
+ i
].gecos
,
704 if (!info
[i
].homedir
) {
705 fstrcpy(name_list
[ent
->num_sam_entries
+ i
].homedir
,"");
707 fstrcpy(name_list
[ent
->num_sam_entries
+ i
].homedir
,
710 if (!info
[i
].shell
) {
711 fstrcpy(name_list
[ent
->num_sam_entries
+ i
].shell
, "");
713 fstrcpy(name_list
[ent
->num_sam_entries
+ i
].shell
,
718 /* User and group ids */
719 sid_copy(&name_list
[ent
->num_sam_entries
+i
].user_sid
,
721 sid_copy(&name_list
[ent
->num_sam_entries
+i
].group_sid
,
725 ent
->num_sam_entries
+= num_entries
;
727 /* Fill in remaining fields */
729 ent
->sam_entries
= name_list
;
730 ent
->sam_entry_index
= 0;
731 return ent
->num_sam_entries
> 0;
734 /* Fetch next passwd entry from ntdom database */
736 #define MAX_GETPWENT_USERS 500
738 void winbindd_getpwent(struct winbindd_cli_state
*state
)
740 struct getent_state
*ent
;
741 struct winbindd_pw
*user_list
;
742 int num_users
, user_list_ndx
;
744 DEBUG(3, ("[%5lu]: getpwent\n", (unsigned long)state
->pid
));
746 /* Check user has enabled this */
748 if (!lp_winbind_enum_users()) {
749 request_error(state
);
753 /* Allocate space for returning a chunk of users */
755 num_users
= MIN(MAX_GETPWENT_USERS
, state
->request
.data
.num_entries
);
757 if (num_users
== 0) {
758 request_error(state
);
762 user_list
= SMB_MALLOC_ARRAY(struct winbindd_pw
, num_users
);
764 request_error(state
);
767 /* will be freed by process_request() */
768 state
->response
.extra_data
.data
= user_list
;
770 memset(user_list
, 0, num_users
* sizeof(struct winbindd_pw
));
772 if (!state
->getpwent_initialized
)
773 winbindd_setpwent_internal(state
);
775 if (!(ent
= state
->getpwent_state
)) {
776 request_error(state
);
780 /* Start sending back users */
782 for (user_list_ndx
= 0; user_list_ndx
< num_users
; ) {
783 struct getpwent_user
*name_list
= NULL
;
786 /* Do we need to fetch another chunk of users? */
788 if (ent
->num_sam_entries
== ent
->sam_entry_index
) {
791 !get_sam_user_entries(ent
, state
->mem_ctx
)) {
792 struct getent_state
*next_ent
;
794 /* Free state information for this domain */
796 SAFE_FREE(ent
->sam_entries
);
798 next_ent
= ent
->next
;
799 DLIST_REMOVE(state
->getpwent_state
, ent
);
805 /* No more domains */
811 name_list
= (struct getpwent_user
*)ent
->sam_entries
;
813 /* Lookup user info */
815 result
= winbindd_fill_pwent(
818 name_list
[ent
->sam_entry_index
].name
,
819 &name_list
[ent
->sam_entry_index
].user_sid
,
820 &name_list
[ent
->sam_entry_index
].group_sid
,
821 name_list
[ent
->sam_entry_index
].gecos
,
822 name_list
[ent
->sam_entry_index
].homedir
,
823 name_list
[ent
->sam_entry_index
].shell
,
824 &user_list
[user_list_ndx
]);
826 /* Add user to return list */
831 state
->response
.data
.num_entries
++;
832 state
->response
.length
+= sizeof(struct winbindd_pw
);
835 DEBUG(1, ("could not lookup domain user %s\n",
836 name_list
[ent
->sam_entry_index
].name
));
838 ent
->sam_entry_index
++;
844 if (user_list_ndx
> 0)
847 request_error(state
);
850 /* List domain users without mapping to unix ids */
851 void winbindd_list_users(struct winbindd_cli_state
*state
)
853 winbindd_list_ent(state
, LIST_USERS
);