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 2 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, write to the Free Software
22 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
29 #define DBGC_CLASS DBGC_WINBIND
31 static BOOL fillup_pw_field(const char *lp_template,
44 /* The substitution of %U and %D in the 'template
45 homedir' is done by talloc_sub_specified() below.
46 If we have an in string (which means the value has already
47 been set in the nss_info backend), then use that.
48 Otherwise use the template value passed in. */
50 if ( in && !strequal(in,"") && lp_security() == SEC_ADS ) {
51 templ = talloc_sub_specified(NULL, in,
55 templ = talloc_sub_specified(NULL, lp_template,
63 safe_strcpy(out, templ, sizeof(fstring) - 1);
69 /* Fill a pwent structure with information we have obtained */
71 static BOOL winbindd_fill_pwent(char *dom_name, char *user_name,
72 DOM_SID *user_sid, DOM_SID *group_sid,
73 char *full_name, char *homedir, char *shell,
74 struct winbindd_pw *pw)
76 fstring output_username;
79 if (!pw || !dom_name || !user_name)
82 /* Resolve the uid number */
84 if (!NT_STATUS_IS_OK(idmap_sid_to_uid(user_sid, &pw->pw_uid))) {
85 DEBUG(1, ("error getting user id for sid %s\n", sid_to_string(sid_string, user_sid)));
89 /* Resolve the gid number */
91 if (!NT_STATUS_IS_OK(idmap_sid_to_gid(group_sid, &pw->pw_gid))) {
92 DEBUG(1, ("error getting group id for sid %s\n", sid_to_string(sid_string, group_sid)));
96 strlower_m(user_name);
100 fill_domain_username(output_username, dom_name, user_name, True);
102 safe_strcpy(pw->pw_name, output_username, sizeof(pw->pw_name) - 1);
104 /* Full name (gecos) */
106 safe_strcpy(pw->pw_gecos, full_name, sizeof(pw->pw_gecos) - 1);
108 /* Home directory and shell - use template config parameters. The
109 defaults are /tmp for the home directory and /bin/false for
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_static(&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_static(&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_static(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", s->domain->name,
237 sid_string_static(&s->user_sid)));
238 request_error(s->state);
242 fstrcpy( username, acct_name );
243 strlower_m( username );
244 s->username = talloc_strdup(s->state->mem_ctx, username);
246 ws_name_replace( s->username, '_' );
248 s->fullname = talloc_strdup(s->state->mem_ctx, full_name);
249 s->homedir = talloc_strdup(s->state->mem_ctx, homedir);
250 s->shell = talloc_strdup(s->state->mem_ctx, shell);
252 sid_copy(&s->group_sid, &s->domain->sid);
253 sid_append_rid(&s->group_sid, group_rid);
255 winbindd_sid2uid_async(s->state->mem_ctx, &s->user_sid,
256 getpwsid_sid2uid_recv, s);
259 static void getpwsid_sid2uid_recv(void *private_data, BOOL success, uid_t uid)
261 struct getpwsid_state *s =
262 talloc_get_type_abort(private_data, struct getpwsid_state);
265 DEBUG(5, ("Could not query user's %s\\%s uid\n",
266 s->domain->name, s->username));
267 request_error(s->state);
272 winbindd_sid2gid_async(s->state->mem_ctx, &s->group_sid,
273 getpwsid_sid2gid_recv, s);
276 static void getpwsid_sid2gid_recv(void *private_data, BOOL success, gid_t gid)
278 struct getpwsid_state *s =
279 talloc_get_type_abort(private_data, struct getpwsid_state);
280 struct winbindd_pw *pw;
281 fstring output_username;
283 /* allow the nss backend to override the primary group ID.
284 If the gid has already been set, then keep it.
285 This makes me feel dirty. If the nss backend already
286 gave us a gid, we don't really care whether the sid2gid()
287 call worked or not. --jerry */
289 if ( s->gid == (gid_t)-1 ) {
292 DEBUG(5, ("Could not query user's %s\\%s\n gid",
293 s->domain->name, s->username));
297 /* take what the sid2gid() call gave us */
301 pw = &s->state->response.data.pw;
304 fill_domain_username(output_username, s->domain->name, s->username, True);
305 safe_strcpy(pw->pw_name, output_username, sizeof(pw->pw_name) - 1);
306 safe_strcpy(pw->pw_gecos, s->fullname, sizeof(pw->pw_gecos) - 1);
308 if (!fillup_pw_field(lp_template_homedir(), s->username, s->domain->name,
309 pw->pw_uid, pw->pw_gid, s->homedir, pw->pw_dir)) {
310 DEBUG(5, ("Could not compose homedir\n"));
314 if (!fillup_pw_field(lp_template_shell(), s->username, s->domain->name,
315 pw->pw_uid, pw->pw_gid, s->shell, pw->pw_shell)) {
316 DEBUG(5, ("Could not compose shell\n"));
320 /* Password - set to "*" as we can't generate anything useful here.
321 Authentication can be done using the pam_winbind module. */
323 safe_strcpy(pw->pw_passwd, "*", sizeof(pw->pw_passwd) - 1);
325 request_ok(s->state);
329 request_error(s->state);
332 /* Return a password structure from a username. */
334 static void getpwnam_name2sid_recv(void *private_data, BOOL success,
335 const DOM_SID *sid, enum lsa_SidType type);
337 void winbindd_getpwnam(struct winbindd_cli_state *state)
339 struct winbindd_domain *domain;
340 fstring domname, username;
342 /* Ensure null termination */
343 state->request.data.username[sizeof(state->request.data.username)-1]='\0';
345 DEBUG(3, ("[%5lu]: getpwnam %s\n", (unsigned long)state->pid,
346 state->request.data.username));
348 if (!parse_domain_user(state->request.data.username, domname,
350 DEBUG(5, ("Could not parse domain user: %s\n",
351 state->request.data.username));
352 request_error(state);
356 /* Get info for the domain */
358 domain = find_domain_from_name(domname);
360 if (domain == NULL) {
361 DEBUG(7, ("could not find domain entry for domain %s\n",
363 request_error(state);
367 if ( strequal(domname, lp_workgroup()) && lp_winbind_trusted_domains_only() ) {
368 DEBUG(7,("winbindd_getpwnam: My domain -- rejecting getpwnam() for %s\\%s.\n",
370 request_error(state);
374 /* Get rid and name type from name. The following costs 1 packet */
376 winbindd_lookupname_async(state->mem_ctx, domname, username,
377 getpwnam_name2sid_recv, state);
380 static void getpwnam_name2sid_recv(void *private_data, BOOL success,
381 const DOM_SID *sid, enum lsa_SidType type)
383 struct winbindd_cli_state *state =
384 (struct winbindd_cli_state *)private_data;
387 DEBUG(5, ("Could not lookup name for user %s\n",
388 state->request.data.username));
389 request_error(state);
393 if ((type != SID_NAME_USER) && (type != SID_NAME_COMPUTER)) {
394 DEBUG(5, ("%s is not a user\n", state->request.data.username));
395 request_error(state);
399 winbindd_getpwsid(state, sid);
402 static void getpwuid_recv(void *private_data, BOOL success, const char *sid)
404 struct winbindd_cli_state *state =
405 (struct winbindd_cli_state *)private_data;
409 DEBUG(10,("uid2sid_recv: uid [%lu] to sid mapping failed\n.",
410 (unsigned long)(state->request.data.uid)));
411 request_error(state);
415 DEBUG(10,("uid2sid_recv: uid %lu has sid %s\n",
416 (unsigned long)(state->request.data.uid), sid));
418 string_to_sid(&user_sid, sid);
419 winbindd_getpwsid(state, &user_sid);
422 /* Return a password structure given a uid number */
423 void winbindd_getpwuid(struct winbindd_cli_state *state)
425 DEBUG(3, ("[%5lu]: getpwuid %lu\n", (unsigned long)state->pid,
426 (unsigned long)state->request.data.uid));
428 /* always query idmap via the async interface */
429 /* if this turns to be too slow we will add here a direct query to the cache */
430 winbindd_uid2sid_async(state->mem_ctx, state->request.data.uid, getpwuid_recv, state);
434 * set/get/endpwent functions
437 /* Rewind file pointer for ntdom passwd database */
439 static BOOL winbindd_setpwent_internal(struct winbindd_cli_state *state)
441 struct winbindd_domain *domain;
443 DEBUG(3, ("[%5lu]: setpwent\n", (unsigned long)state->pid));
445 /* Check user has enabled this */
447 if (!lp_winbind_enum_users()) {
451 /* Free old static data if it exists */
453 if (state->getpwent_state != NULL) {
454 free_getent_state(state->getpwent_state);
455 state->getpwent_state = NULL;
459 /* add any local users we have */
461 if ( (domain_state = (struct getent_state *)malloc(sizeof(struct getent_state))) == NULL )
464 ZERO_STRUCTP(domain_state);
466 /* Add to list of open domains */
468 DLIST_ADD(state->getpwent_state, domain_state);
471 /* Create sam pipes for each domain we know about */
473 for(domain = domain_list(); domain != NULL; domain = domain->next) {
474 struct getent_state *domain_state;
477 /* don't add our domaina if we are a PDC or if we
478 are a member of a Samba domain */
480 if ( (IS_DC || lp_winbind_trusted_domains_only())
481 && strequal(domain->name, lp_workgroup()) )
486 /* Create a state record for this domain */
488 if ((domain_state = SMB_MALLOC_P(struct getent_state)) == NULL) {
489 DEBUG(0, ("malloc failed\n"));
493 ZERO_STRUCTP(domain_state);
495 fstrcpy(domain_state->domain_name, domain->name);
497 /* Add to list of open domains */
499 DLIST_ADD(state->getpwent_state, domain_state);
502 state->getpwent_initialized = True;
506 void winbindd_setpwent(struct winbindd_cli_state *state)
508 if (winbindd_setpwent_internal(state)) {
511 request_error(state);
515 /* Close file pointer to ntdom passwd database */
517 void winbindd_endpwent(struct winbindd_cli_state *state)
519 DEBUG(3, ("[%5lu]: endpwent\n", (unsigned long)state->pid));
521 free_getent_state(state->getpwent_state);
522 state->getpwent_initialized = False;
523 state->getpwent_state = NULL;
527 /* Get partial list of domain users for a domain. We fill in the sam_entries,
528 and num_sam_entries fields with domain user information. The dispinfo_ndx
529 field is incremented to the index of the next user to fetch. Return True if
530 some users were returned, False otherwise. */
532 static BOOL get_sam_user_entries(struct getent_state *ent, TALLOC_CTX *mem_ctx)
536 WINBIND_USERINFO *info;
537 struct getpwent_user *name_list = NULL;
538 struct winbindd_domain *domain;
539 struct winbindd_methods *methods;
542 if (ent->num_sam_entries)
545 if (!(domain = find_domain_from_name(ent->domain_name))) {
546 DEBUG(3, ("no such domain %s in get_sam_user_entries\n",
551 methods = domain->methods;
553 /* Free any existing user info */
555 SAFE_FREE(ent->sam_entries);
556 ent->num_sam_entries = 0;
558 /* Call query_user_list to get a list of usernames and user rids */
562 status = methods->query_user_list(domain, mem_ctx, &num_entries,
565 if (!NT_STATUS_IS_OK(status)) {
566 DEBUG(10,("get_sam_user_entries: query_user_list failed with %s\n",
567 nt_errstr(status) ));
572 name_list = SMB_REALLOC_ARRAY(name_list, struct getpwent_user, ent->num_sam_entries + num_entries);
575 DEBUG(0,("get_sam_user_entries realloc failed.\n"));
580 for (i = 0; i < num_entries; i++) {
581 /* Store account name and gecos */
582 if (!info[i].acct_name) {
583 fstrcpy(name_list[ent->num_sam_entries + i].name, "");
585 fstrcpy(name_list[ent->num_sam_entries + i].name,
588 if (!info[i].full_name) {
589 fstrcpy(name_list[ent->num_sam_entries + i].gecos, "");
591 fstrcpy(name_list[ent->num_sam_entries + i].gecos,
594 if (!info[i].homedir) {
595 fstrcpy(name_list[ent->num_sam_entries + i].homedir, "");
597 fstrcpy(name_list[ent->num_sam_entries + i].homedir,
600 if (!info[i].shell) {
601 fstrcpy(name_list[ent->num_sam_entries + i].shell, "");
603 fstrcpy(name_list[ent->num_sam_entries + i].shell,
608 /* User and group ids */
609 sid_copy(&name_list[ent->num_sam_entries+i].user_sid,
611 sid_copy(&name_list[ent->num_sam_entries+i].group_sid,
615 ent->num_sam_entries += num_entries;
617 /* Fill in remaining fields */
619 ent->sam_entries = name_list;
620 ent->sam_entry_index = 0;
621 return ent->num_sam_entries > 0;
624 /* Fetch next passwd entry from ntdom database */
626 #define MAX_GETPWENT_USERS 500
628 void winbindd_getpwent(struct winbindd_cli_state *state)
630 struct getent_state *ent;
631 struct winbindd_pw *user_list;
632 int num_users, user_list_ndx = 0, i;
634 DEBUG(3, ("[%5lu]: getpwent\n", (unsigned long)state->pid));
636 /* Check user has enabled this */
638 if (!lp_winbind_enum_users()) {
639 request_error(state);
643 /* Allocate space for returning a chunk of users */
645 num_users = MIN(MAX_GETPWENT_USERS, state->request.data.num_entries);
647 if ((state->response.extra_data.data = SMB_MALLOC_ARRAY(struct winbindd_pw, num_users)) == NULL) {
648 request_error(state);
652 memset(state->response.extra_data.data, 0, num_users *
653 sizeof(struct winbindd_pw));
655 user_list = (struct winbindd_pw *)state->response.extra_data.data;
657 if (!state->getpwent_initialized)
658 winbindd_setpwent_internal(state);
660 if (!(ent = state->getpwent_state)) {
661 request_error(state);
665 /* Start sending back users */
667 for (i = 0; i < num_users; i++) {
668 struct getpwent_user *name_list = NULL;
671 /* Do we need to fetch another chunk of users? */
673 if (ent->num_sam_entries == ent->sam_entry_index) {
676 !get_sam_user_entries(ent, state->mem_ctx)) {
677 struct getent_state *next_ent;
679 /* Free state information for this domain */
681 SAFE_FREE(ent->sam_entries);
683 next_ent = ent->next;
684 DLIST_REMOVE(state->getpwent_state, ent);
690 /* No more domains */
696 name_list = (struct getpwent_user *)ent->sam_entries;
698 /* Lookup user info */
700 result = winbindd_fill_pwent(
702 name_list[ent->sam_entry_index].name,
703 &name_list[ent->sam_entry_index].user_sid,
704 &name_list[ent->sam_entry_index].group_sid,
705 name_list[ent->sam_entry_index].gecos,
706 name_list[ent->sam_entry_index].homedir,
707 name_list[ent->sam_entry_index].shell,
708 &user_list[user_list_ndx]);
710 ent->sam_entry_index++;
712 /* Add user to return list */
717 state->response.data.num_entries++;
718 state->response.length +=
719 sizeof(struct winbindd_pw);
722 DEBUG(1, ("could not lookup domain user %s\n",
723 name_list[ent->sam_entry_index].name));
728 if (user_list_ndx > 0)
731 request_error(state);
734 /* List domain users without mapping to unix ids */
736 void winbindd_list_users(struct winbindd_cli_state *state)
738 struct winbindd_domain *domain;
739 WINBIND_USERINFO *info;
740 const char *which_domain;
741 uint32 num_entries = 0, total_entries = 0;
742 char *extra_data = NULL;
743 int extra_data_len = 0;
744 enum winbindd_result rv = WINBINDD_ERROR;
746 DEBUG(3, ("[%5lu]: list users\n", (unsigned long)state->pid));
748 /* Ensure null termination */
749 state->request.domain_name[sizeof(state->request.domain_name)-1]='\0';
750 which_domain = state->request.domain_name;
752 /* Enumerate over trusted domains */
754 for (domain = domain_list(); domain; domain = domain->next) {
756 struct winbindd_methods *methods;
759 /* if we have a domain name restricting the request and this
760 one in the list doesn't match, then just bypass the remainder
763 if ( *which_domain && !strequal(which_domain, domain->name) )
766 methods = domain->methods;
768 /* Query display info */
769 status = methods->query_user_list(domain, state->mem_ctx,
770 &num_entries, &info);
772 if (!NT_STATUS_IS_OK(status)) {
776 if (num_entries == 0)
779 /* Allocate some memory for extra data */
780 total_entries += num_entries;
782 extra_data = (char *)SMB_REALLOC(
783 extra_data, sizeof(fstring) * total_entries);
786 DEBUG(0,("failed to enlarge buffer!\n"));
790 /* Pack user list into extra data fields */
792 for (i = 0; i < num_entries; i++) {
793 fstring acct_name, name;
795 if (!info[i].acct_name) {
796 fstrcpy(acct_name, "");
798 fstrcpy(acct_name, info[i].acct_name);
801 fill_domain_username(name, domain->name, acct_name, True);
803 /* Append to extra data */
804 memcpy(&extra_data[extra_data_len], name,
806 extra_data_len += strlen(name);
807 extra_data[extra_data_len++] = ',';
811 /* Assign extra_data fields in response structure */
814 extra_data[extra_data_len - 1] = '\0';
815 state->response.extra_data.data = extra_data;
816 state->response.length += extra_data_len;
819 /* No domains responded but that's still OK so don't return an
826 if (rv == WINBINDD_OK)
829 request_error(state);