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(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
;
77 if (!pw
|| !dom_name
|| !user_name
)
80 /* Resolve the uid number */
82 if (!NT_STATUS_IS_OK(idmap_sid_to_uid(dom_name
, user_sid
,
84 DEBUG(1, ("error getting user id for sid %s\n",
85 sid_string_dbg(user_sid
)));
89 /* Resolve the gid number */
91 if (!NT_STATUS_IS_OK(idmap_sid_to_gid(dom_name
, group_sid
,
93 DEBUG(1, ("error getting group id for sid %s\n",
94 sid_string_dbg(group_sid
)));
98 strlower_m(user_name
);
102 fill_domain_username(output_username
, dom_name
, user_name
, True
);
104 safe_strcpy(pw
->pw_name
, output_username
, sizeof(pw
->pw_name
) - 1);
106 /* Full name (gecos) */
108 safe_strcpy(pw
->pw_gecos
, full_name
, sizeof(pw
->pw_gecos
) - 1);
110 /* Home directory and shell */
112 if (!fillup_pw_field(lp_template_homedir(), user_name
, dom_name
,
113 pw
->pw_uid
, pw
->pw_gid
, homedir
, pw
->pw_dir
))
116 if (!fillup_pw_field(lp_template_shell(), user_name
, dom_name
,
117 pw
->pw_uid
, pw
->pw_gid
, shell
, pw
->pw_shell
))
120 /* Password - set to "*" as we can't generate anything useful here.
121 Authentication can be done using the pam_winbind module. */
123 safe_strcpy(pw
->pw_passwd
, "*", sizeof(pw
->pw_passwd
) - 1);
128 /* Wrapper for domain->methods->query_user, only on the parent->child pipe */
130 enum winbindd_result
winbindd_dual_userinfo(struct winbindd_domain
*domain
,
131 struct winbindd_cli_state
*state
)
134 WINBIND_USERINFO user_info
;
137 /* Ensure null termination */
138 state
->request
.data
.sid
[sizeof(state
->request
.data
.sid
)-1]='\0';
140 DEBUG(3, ("[%5lu]: lookupsid %s\n", (unsigned long)state
->pid
,
141 state
->request
.data
.sid
));
143 if (!string_to_sid(&sid
, state
->request
.data
.sid
)) {
144 DEBUG(5, ("%s not a SID\n", state
->request
.data
.sid
));
145 return WINBINDD_ERROR
;
148 status
= domain
->methods
->query_user(domain
, state
->mem_ctx
,
150 if (!NT_STATUS_IS_OK(status
)) {
151 DEBUG(1, ("error getting user info for sid %s\n",
152 sid_string_dbg(&sid
)));
153 return WINBINDD_ERROR
;
156 fstrcpy(state
->response
.data
.user_info
.acct_name
, user_info
.acct_name
);
157 fstrcpy(state
->response
.data
.user_info
.full_name
, user_info
.full_name
);
158 fstrcpy(state
->response
.data
.user_info
.homedir
, user_info
.homedir
);
159 fstrcpy(state
->response
.data
.user_info
.shell
, user_info
.shell
);
160 state
->response
.data
.user_info
.primary_gid
= user_info
.primary_gid
;
161 if (!sid_peek_check_rid(&domain
->sid
, &user_info
.group_sid
,
162 &state
->response
.data
.user_info
.group_rid
)) {
163 DEBUG(1, ("Could not extract group rid out of %s\n",
164 sid_string_dbg(&sid
)));
165 return WINBINDD_ERROR
;
171 struct getpwsid_state
{
172 struct winbindd_cli_state
*state
;
173 struct winbindd_domain
*domain
;
184 static void getpwsid_queryuser_recv(void *private_data
, bool success
,
185 const char *acct_name
,
186 const char *full_name
,
191 static void getpwsid_sid2uid_recv(void *private_data
, bool success
, uid_t uid
);
192 static void getpwsid_sid2gid_recv(void *private_data
, bool success
, gid_t gid
);
194 static void winbindd_getpwsid(struct winbindd_cli_state
*state
,
197 struct getpwsid_state
*s
;
199 s
= TALLOC_ZERO_P(state
->mem_ctx
, struct getpwsid_state
);
201 DEBUG(0, ("talloc failed\n"));
206 s
->domain
= find_domain_from_sid_noinit(sid
);
207 if (s
->domain
== NULL
) {
208 DEBUG(3, ("Could not find domain for sid %s\n",
209 sid_string_dbg(sid
)));
213 sid_copy(&s
->user_sid
, sid
);
215 query_user_async(s
->state
->mem_ctx
, s
->domain
, sid
,
216 getpwsid_queryuser_recv
, s
);
220 request_error(state
);
223 static void getpwsid_queryuser_recv(void *private_data
, bool success
,
224 const char *acct_name
,
225 const char *full_name
,
232 struct getpwsid_state
*s
=
233 talloc_get_type_abort(private_data
, struct getpwsid_state
);
236 DEBUG(5, ("Could not query domain %s SID %s\n",
237 s
->domain
->name
, sid_string_dbg(&s
->user_sid
)));
238 request_error(s
->state
);
242 if ( acct_name
&& *acct_name
) {
243 fstrcpy( username
, acct_name
);
245 char *domain_name
= NULL
;
246 enum lsa_SidType type
;
247 char *user_name
= NULL
;
248 struct winbindd_domain
*domain
= NULL
;
250 domain
= find_lookup_domain_from_sid(&s
->user_sid
);
251 if (domain
== NULL
) {
252 DEBUG(5, ("find_lookup_domain_from_sid(%s) failed\n",
253 sid_string_dbg(&s
->user_sid
)));
254 request_error(s
->state
);
257 winbindd_lookup_name_by_sid(s
->state
->mem_ctx
, domain
,
258 &s
->user_sid
, &domain_name
,
261 /* If this still fails we ar4e done. Just error out */
263 DEBUG(5,("Could not obtain a name for SID %s\n",
264 sid_string_dbg(&s
->user_sid
)));
265 request_error(s
->state
);
269 fstrcpy( username
, user_name
);
272 strlower_m( username
);
273 s
->username
= talloc_strdup(s
->state
->mem_ctx
, username
);
275 ws_name_replace( s
->username
, WB_REPLACE_CHAR
);
277 s
->fullname
= talloc_strdup(s
->state
->mem_ctx
, full_name
);
278 s
->homedir
= talloc_strdup(s
->state
->mem_ctx
, homedir
);
279 s
->shell
= talloc_strdup(s
->state
->mem_ctx
, shell
);
281 sid_copy(&s
->group_sid
, &s
->domain
->sid
);
282 sid_append_rid(&s
->group_sid
, group_rid
);
284 winbindd_sid2uid_async(s
->state
->mem_ctx
, &s
->user_sid
,
285 getpwsid_sid2uid_recv
, s
);
288 static void getpwsid_sid2uid_recv(void *private_data
, bool success
, uid_t uid
)
290 struct getpwsid_state
*s
=
291 talloc_get_type_abort(private_data
, struct getpwsid_state
);
294 DEBUG(5, ("Could not query uid for user %s\\%s\n",
295 s
->domain
->name
, s
->username
));
296 request_error(s
->state
);
301 winbindd_sid2gid_async(s
->state
->mem_ctx
, &s
->group_sid
,
302 getpwsid_sid2gid_recv
, s
);
305 static void getpwsid_sid2gid_recv(void *private_data
, bool success
, gid_t gid
)
307 struct getpwsid_state
*s
=
308 talloc_get_type_abort(private_data
, struct getpwsid_state
);
309 struct winbindd_pw
*pw
;
310 fstring output_username
;
312 /* allow the nss backend to override the primary group ID.
313 If the gid has already been set, then keep it.
314 This makes me feel dirty. If the nss backend already
315 gave us a gid, we don't really care whether the sid2gid()
316 call worked or not. --jerry */
318 if ( s
->gid
== (gid_t
)-1 ) {
321 DEBUG(5, ("Could not query gid for user %s\\%s\n",
322 s
->domain
->name
, s
->username
));
326 /* take what the sid2gid() call gave us */
330 pw
= &s
->state
->response
.data
.pw
;
333 fill_domain_username(output_username
, s
->domain
->name
,
335 safe_strcpy(pw
->pw_name
, output_username
, sizeof(pw
->pw_name
) - 1);
336 safe_strcpy(pw
->pw_gecos
, s
->fullname
, sizeof(pw
->pw_gecos
) - 1);
338 if (!fillup_pw_field(lp_template_homedir(), s
->username
,
339 s
->domain
->name
, pw
->pw_uid
, pw
->pw_gid
,
340 s
->homedir
, pw
->pw_dir
)) {
341 DEBUG(5, ("Could not compose homedir\n"));
345 if (!fillup_pw_field(lp_template_shell(), s
->username
,
346 s
->domain
->name
, pw
->pw_uid
, pw
->pw_gid
,
347 s
->shell
, pw
->pw_shell
)) {
348 DEBUG(5, ("Could not compose shell\n"));
352 /* Password - set to "*" as we can't generate anything useful here.
353 Authentication can be done using the pam_winbind module. */
355 safe_strcpy(pw
->pw_passwd
, "*", sizeof(pw
->pw_passwd
) - 1);
357 request_ok(s
->state
);
361 request_error(s
->state
);
364 /* Return a password structure from a username. */
366 static void getpwnam_name2sid_recv(void *private_data
, bool success
,
367 const DOM_SID
*sid
, enum lsa_SidType type
);
369 void winbindd_getpwnam(struct winbindd_cli_state
*state
)
371 struct winbindd_domain
*domain
;
372 fstring domname
, username
;
376 domuser
= state
->request
.data
.username
;
377 dusize
= sizeof(state
->request
.data
.username
);
379 /* Ensure null termination (it's an fstring) */
380 domuser
[dusize
-1] = '\0';
382 DEBUG(3, ("[%5lu]: getpwnam %s\n",
383 (unsigned long)state
->pid
,
386 ws_name_return(domuser
, WB_REPLACE_CHAR
);
388 if (!parse_domain_user(domuser
, domname
, username
)) {
389 DEBUG(5, ("Could not parse domain user: %s\n", domuser
));
390 request_error(state
);
394 /* Get info for the domain */
396 domain
= find_domain_from_name(domname
);
398 if (domain
== NULL
) {
399 DEBUG(7, ("could not find domain entry for domain %s. "
400 "Using primary domain\n", domname
));
401 if ( (domain
= find_our_domain()) == NULL
) {
402 DEBUG(0,("Cannot find my primary domain structure!\n"));
403 request_error(state
);
408 if (strequal(domname
, lp_workgroup()) &&
409 lp_winbind_trusted_domains_only() ) {
410 DEBUG(7,("winbindd_getpwnam: My domain -- "
411 "rejecting getpwnam() for %s\\%s.\n",
413 request_error(state
);
417 /* Get rid and name type from name. The following costs 1 packet */
419 winbindd_lookupname_async(state
->mem_ctx
, domname
, username
,
420 getpwnam_name2sid_recv
, WINBINDD_GETPWNAM
,
424 static void getpwnam_name2sid_recv(void *private_data
, bool success
,
425 const DOM_SID
*sid
, enum lsa_SidType type
)
427 struct winbindd_cli_state
*state
=
428 (struct winbindd_cli_state
*)private_data
;
429 fstring domname
, username
;
430 char *domuser
= state
->request
.data
.username
;
433 DEBUG(5, ("Could not lookup name for user %s\n", domuser
));
434 request_error(state
);
438 if ((type
!= SID_NAME_USER
) && (type
!= SID_NAME_COMPUTER
)) {
439 DEBUG(5, ("%s is not a user\n", domuser
));
440 request_error(state
);
444 if (parse_domain_user(domuser
, domname
, username
)) {
445 check_domain_trusted(domname
, sid
);
448 winbindd_getpwsid(state
, sid
);
451 static void getpwuid_recv(void *private_data
, bool success
, const char *sid
)
453 struct winbindd_cli_state
*state
=
454 (struct winbindd_cli_state
*)private_data
;
458 DEBUG(10,("uid2sid_recv: uid [%lu] to sid mapping failed\n.",
459 (unsigned long)(state
->request
.data
.uid
)));
460 request_error(state
);
464 DEBUG(10,("uid2sid_recv: uid %lu has sid %s\n",
465 (unsigned long)(state
->request
.data
.uid
), sid
));
467 string_to_sid(&user_sid
, sid
);
468 winbindd_getpwsid(state
, &user_sid
);
471 /* Return a password structure given a uid number */
472 void winbindd_getpwuid(struct winbindd_cli_state
*state
)
474 uid_t uid
= state
->request
.data
.uid
;
476 DEBUG(3, ("[%5lu]: getpwuid %lu\n",
477 (unsigned long)state
->pid
,
478 (unsigned long)uid
));
480 /* always query idmap via the async interface */
481 /* if this turns to be too slow we will add here
482 * a direct query to the cache */
483 winbindd_uid2sid_async(state
->mem_ctx
, uid
, getpwuid_recv
, state
);
487 * set/get/endpwent functions
490 /* Rewind file pointer for ntdom passwd database */
492 static bool winbindd_setpwent_internal(struct winbindd_cli_state
*state
)
494 struct winbindd_domain
*domain
;
496 DEBUG(3, ("[%5lu]: setpwent\n", (unsigned long)state
->pid
));
498 /* Check user has enabled this */
500 if (!lp_winbind_enum_users()) {
504 /* Free old static data if it exists */
506 if (state
->getpwent_state
!= NULL
) {
507 free_getent_state(state
->getpwent_state
);
508 state
->getpwent_state
= NULL
;
511 /* Create sam pipes for each domain we know about */
513 for(domain
= domain_list(); domain
!= NULL
; domain
= domain
->next
) {
514 struct getent_state
*domain_state
;
517 /* don't add our domaina if we are a PDC or if we
518 are a member of a Samba domain */
520 if ((IS_DC
|| lp_winbind_trusted_domains_only())
521 && strequal(domain
->name
, lp_workgroup())) {
525 /* Create a state record for this domain */
527 domain_state
= SMB_MALLOC_P(struct getent_state
);
529 DEBUG(0, ("malloc failed\n"));
533 ZERO_STRUCTP(domain_state
);
535 fstrcpy(domain_state
->domain_name
, domain
->name
);
537 /* Add to list of open domains */
539 DLIST_ADD(state
->getpwent_state
, domain_state
);
542 state
->getpwent_initialized
= True
;
546 void winbindd_setpwent(struct winbindd_cli_state
*state
)
548 if (winbindd_setpwent_internal(state
)) {
551 request_error(state
);
555 /* Close file pointer to ntdom passwd database */
557 void winbindd_endpwent(struct winbindd_cli_state
*state
)
559 DEBUG(3, ("[%5lu]: endpwent\n", (unsigned long)state
->pid
));
561 free_getent_state(state
->getpwent_state
);
562 state
->getpwent_initialized
= False
;
563 state
->getpwent_state
= NULL
;
567 /* Get partial list of domain users for a domain. We fill in the sam_entries,
568 and num_sam_entries fields with domain user information. The dispinfo_ndx
569 field is incremented to the index of the next user to fetch. Return True if
570 some users were returned, False otherwise. */
572 static bool get_sam_user_entries(struct getent_state
*ent
, TALLOC_CTX
*mem_ctx
)
576 WINBIND_USERINFO
*info
;
577 struct getpwent_user
*name_list
= NULL
;
578 struct winbindd_domain
*domain
;
579 struct winbindd_methods
*methods
;
582 if (ent
->num_sam_entries
)
585 if (!(domain
= find_domain_from_name(ent
->domain_name
))) {
586 DEBUG(3, ("no such domain %s in get_sam_user_entries\n",
591 methods
= domain
->methods
;
593 /* Free any existing user info */
595 SAFE_FREE(ent
->sam_entries
);
596 ent
->num_sam_entries
= 0;
598 /* Call query_user_list to get a list of usernames and user rids */
602 status
= methods
->query_user_list(domain
, mem_ctx
, &num_entries
, &info
);
604 if (!NT_STATUS_IS_OK(status
)) {
605 DEBUG(10,("get_sam_user_entries: "
606 "query_user_list failed with %s\n",
612 name_list
= SMB_REALLOC_ARRAY(name_list
, struct getpwent_user
,
613 ent
->num_sam_entries
+ num_entries
);
615 DEBUG(0,("get_sam_user_entries realloc failed.\n"));
620 for (i
= 0; i
< num_entries
; i
++) {
621 /* Store account name and gecos */
622 if (!info
[i
].acct_name
) {
623 fstrcpy(name_list
[ent
->num_sam_entries
+ i
].name
, "");
625 fstrcpy(name_list
[ent
->num_sam_entries
+ i
].name
,
628 if (!info
[i
].full_name
) {
629 fstrcpy(name_list
[ent
->num_sam_entries
+ i
].gecos
, "");
631 fstrcpy(name_list
[ent
->num_sam_entries
+ i
].gecos
,
634 if (!info
[i
].homedir
) {
635 fstrcpy(name_list
[ent
->num_sam_entries
+ i
].homedir
,"");
637 fstrcpy(name_list
[ent
->num_sam_entries
+ i
].homedir
,
640 if (!info
[i
].shell
) {
641 fstrcpy(name_list
[ent
->num_sam_entries
+ i
].shell
, "");
643 fstrcpy(name_list
[ent
->num_sam_entries
+ i
].shell
,
648 /* User and group ids */
649 sid_copy(&name_list
[ent
->num_sam_entries
+i
].user_sid
,
651 sid_copy(&name_list
[ent
->num_sam_entries
+i
].group_sid
,
655 ent
->num_sam_entries
+= num_entries
;
657 /* Fill in remaining fields */
659 ent
->sam_entries
= name_list
;
660 ent
->sam_entry_index
= 0;
661 return ent
->num_sam_entries
> 0;
664 /* Fetch next passwd entry from ntdom database */
666 #define MAX_GETPWENT_USERS 500
668 void winbindd_getpwent(struct winbindd_cli_state
*state
)
670 struct getent_state
*ent
;
671 struct winbindd_pw
*user_list
;
672 int num_users
, user_list_ndx
;
674 DEBUG(3, ("[%5lu]: getpwent\n", (unsigned long)state
->pid
));
676 /* Check user has enabled this */
678 if (!lp_winbind_enum_users()) {
679 request_error(state
);
683 /* Allocate space for returning a chunk of users */
685 num_users
= MIN(MAX_GETPWENT_USERS
, state
->request
.data
.num_entries
);
687 if (num_users
== 0) {
688 request_error(state
);
692 user_list
= SMB_MALLOC_ARRAY(struct winbindd_pw
, num_users
);
694 request_error(state
);
697 /* will be freed by process_request() */
698 state
->response
.extra_data
.data
= user_list
;
700 memset(user_list
, 0, num_users
* sizeof(struct winbindd_pw
));
702 if (!state
->getpwent_initialized
)
703 winbindd_setpwent_internal(state
);
705 if (!(ent
= state
->getpwent_state
)) {
706 request_error(state
);
710 /* Start sending back users */
712 for (user_list_ndx
= 0; user_list_ndx
< num_users
; ) {
713 struct getpwent_user
*name_list
= NULL
;
716 /* Do we need to fetch another chunk of users? */
718 if (ent
->num_sam_entries
== ent
->sam_entry_index
) {
721 !get_sam_user_entries(ent
, state
->mem_ctx
)) {
722 struct getent_state
*next_ent
;
724 /* Free state information for this domain */
726 SAFE_FREE(ent
->sam_entries
);
728 next_ent
= ent
->next
;
729 DLIST_REMOVE(state
->getpwent_state
, ent
);
735 /* No more domains */
741 name_list
= (struct getpwent_user
*)ent
->sam_entries
;
743 /* Lookup user info */
745 result
= winbindd_fill_pwent(
747 name_list
[ent
->sam_entry_index
].name
,
748 &name_list
[ent
->sam_entry_index
].user_sid
,
749 &name_list
[ent
->sam_entry_index
].group_sid
,
750 name_list
[ent
->sam_entry_index
].gecos
,
751 name_list
[ent
->sam_entry_index
].homedir
,
752 name_list
[ent
->sam_entry_index
].shell
,
753 &user_list
[user_list_ndx
]);
755 /* Add user to return list */
760 state
->response
.data
.num_entries
++;
761 state
->response
.length
+= sizeof(struct winbindd_pw
);
764 DEBUG(1, ("could not lookup domain user %s\n",
765 name_list
[ent
->sam_entry_index
].name
));
767 ent
->sam_entry_index
++;
773 if (user_list_ndx
> 0)
776 request_error(state
);
779 /* List domain users without mapping to unix ids */
780 void winbindd_list_users(struct winbindd_cli_state
*state
)
782 winbindd_list_ent(state
, LIST_USERS
);