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, 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 */
110 if (!fillup_pw_field(lp_template_homedir(), user_name, dom_name,
111 pw->pw_uid, pw->pw_gid, homedir, pw->pw_dir))
114 if (!fillup_pw_field(lp_template_shell(), user_name, dom_name,
115 pw->pw_uid, pw->pw_gid, shell, pw->pw_shell))
118 /* Password - set to "*" as we can't generate anything useful here.
119 Authentication can be done using the pam_winbind module. */
121 safe_strcpy(pw->pw_passwd, "*", sizeof(pw->pw_passwd) - 1);
126 /* Wrapper for domain->methods->query_user, only on the parent->child pipe */
128 enum winbindd_result winbindd_dual_userinfo(struct winbindd_domain *domain,
129 struct winbindd_cli_state *state)
132 WINBIND_USERINFO user_info;
135 /* Ensure null termination */
136 state->request.data.sid[sizeof(state->request.data.sid)-1]='\0';
138 DEBUG(3, ("[%5lu]: lookupsid %s\n", (unsigned long)state->pid,
139 state->request.data.sid));
141 if (!string_to_sid(&sid, state->request.data.sid)) {
142 DEBUG(5, ("%s not a SID\n", state->request.data.sid));
143 return WINBINDD_ERROR;
146 status = domain->methods->query_user(domain, state->mem_ctx,
148 if (!NT_STATUS_IS_OK(status)) {
149 DEBUG(1, ("error getting user info for sid %s\n",
150 sid_string_static(&sid)));
151 return WINBINDD_ERROR;
154 fstrcpy(state->response.data.user_info.acct_name, user_info.acct_name);
155 fstrcpy(state->response.data.user_info.full_name, user_info.full_name);
156 fstrcpy(state->response.data.user_info.homedir, user_info.homedir);
157 fstrcpy(state->response.data.user_info.shell, user_info.shell);
158 state->response.data.user_info.primary_gid = user_info.primary_gid;
159 if (!sid_peek_check_rid(&domain->sid, &user_info.group_sid,
160 &state->response.data.user_info.group_rid)) {
161 DEBUG(1, ("Could not extract group rid out of %s\n",
162 sid_string_static(&sid)));
163 return WINBINDD_ERROR;
169 struct getpwsid_state {
170 struct winbindd_cli_state *state;
171 struct winbindd_domain *domain;
182 static void getpwsid_queryuser_recv(void *private_data, BOOL success,
183 const char *acct_name,
184 const char *full_name,
189 static void getpwsid_sid2uid_recv(void *private_data, BOOL success, uid_t uid);
190 static void getpwsid_sid2gid_recv(void *private_data, BOOL success, gid_t gid);
192 static void winbindd_getpwsid(struct winbindd_cli_state *state,
195 struct getpwsid_state *s;
197 s = TALLOC_ZERO_P(state->mem_ctx, struct getpwsid_state);
199 DEBUG(0, ("talloc failed\n"));
204 s->domain = find_domain_from_sid_noinit(sid);
205 if (s->domain == NULL) {
206 DEBUG(3, ("Could not find domain for sid %s\n",
207 sid_string_static(sid)));
211 sid_copy(&s->user_sid, sid);
213 query_user_async(s->state->mem_ctx, s->domain, sid,
214 getpwsid_queryuser_recv, s);
218 request_error(state);
221 static void getpwsid_queryuser_recv(void *private_data, BOOL success,
222 const char *acct_name,
223 const char *full_name,
230 struct getpwsid_state *s =
231 talloc_get_type_abort(private_data, struct getpwsid_state);
234 DEBUG(5, ("Could not query domain %s SID %s\n", s->domain->name,
235 sid_string_static(&s->user_sid)));
236 request_error(s->state);
240 if ( acct_name && *acct_name ) {
241 fstrcpy( username, acct_name );
243 char *domain_name = NULL;
244 enum lsa_SidType type;
245 char *user_name = NULL;
246 struct winbindd_domain *domain = NULL;
248 domain = find_lookup_domain_from_sid(&s->user_sid);
249 winbindd_lookup_name_by_sid(s->state->mem_ctx, domain,
250 &s->user_sid, &domain_name,
253 /* If this still fails we ar4e done. Just error out */
255 DEBUG(5,("Could not obtain a name for SID %s\n",
256 sid_string_static(&s->user_sid)));
257 request_error(s->state);
261 fstrcpy( username, user_name );
264 strlower_m( username );
265 s->username = talloc_strdup(s->state->mem_ctx, username);
267 ws_name_replace( s->username, WB_REPLACE_CHAR );
269 s->fullname = talloc_strdup(s->state->mem_ctx, full_name);
270 s->homedir = talloc_strdup(s->state->mem_ctx, homedir);
271 s->shell = talloc_strdup(s->state->mem_ctx, shell);
273 sid_copy(&s->group_sid, &s->domain->sid);
274 sid_append_rid(&s->group_sid, group_rid);
276 winbindd_sid2uid_async(s->state->mem_ctx, &s->user_sid,
277 getpwsid_sid2uid_recv, s);
280 static void getpwsid_sid2uid_recv(void *private_data, BOOL success, uid_t uid)
282 struct getpwsid_state *s =
283 talloc_get_type_abort(private_data, struct getpwsid_state);
286 DEBUG(5, ("Could not query uid for user %s\\%s\n",
287 s->domain->name, s->username));
288 request_error(s->state);
293 winbindd_sid2gid_async(s->state->mem_ctx, &s->group_sid,
294 getpwsid_sid2gid_recv, s);
297 static void getpwsid_sid2gid_recv(void *private_data, BOOL success, gid_t gid)
299 struct getpwsid_state *s =
300 talloc_get_type_abort(private_data, struct getpwsid_state);
301 struct winbindd_pw *pw;
302 fstring output_username;
304 /* allow the nss backend to override the primary group ID.
305 If the gid has already been set, then keep it.
306 This makes me feel dirty. If the nss backend already
307 gave us a gid, we don't really care whether the sid2gid()
308 call worked or not. --jerry */
310 if ( s->gid == (gid_t)-1 ) {
313 DEBUG(5, ("Could not query gid for user %s\\%s\n",
314 s->domain->name, s->username));
318 /* take what the sid2gid() call gave us */
322 pw = &s->state->response.data.pw;
325 fill_domain_username(output_username, s->domain->name, s->username, True);
326 safe_strcpy(pw->pw_name, output_username, sizeof(pw->pw_name) - 1);
327 safe_strcpy(pw->pw_gecos, s->fullname, sizeof(pw->pw_gecos) - 1);
329 if (!fillup_pw_field(lp_template_homedir(), s->username, s->domain->name,
330 pw->pw_uid, pw->pw_gid, s->homedir, pw->pw_dir)) {
331 DEBUG(5, ("Could not compose homedir\n"));
335 if (!fillup_pw_field(lp_template_shell(), s->username, s->domain->name,
336 pw->pw_uid, pw->pw_gid, s->shell, pw->pw_shell)) {
337 DEBUG(5, ("Could not compose shell\n"));
341 /* Password - set to "*" as we can't generate anything useful here.
342 Authentication can be done using the pam_winbind module. */
344 safe_strcpy(pw->pw_passwd, "*", sizeof(pw->pw_passwd) - 1);
346 request_ok(s->state);
350 request_error(s->state);
353 /* Return a password structure from a username. */
355 static void getpwnam_name2sid_recv(void *private_data, BOOL success,
356 const DOM_SID *sid, enum lsa_SidType type);
358 void winbindd_getpwnam(struct winbindd_cli_state *state)
360 struct winbindd_domain *domain;
361 fstring domname, username;
363 /* Ensure null termination */
364 state->request.data.username[sizeof(state->request.data.username)-1]='\0';
366 DEBUG(3, ("[%5lu]: getpwnam %s\n", (unsigned long)state->pid,
367 state->request.data.username));
369 ws_name_return( state->request.data.username, WB_REPLACE_CHAR );
371 if (!parse_domain_user(state->request.data.username, domname,
373 DEBUG(5, ("Could not parse domain user: %s\n",
374 state->request.data.username));
375 request_error(state);
379 /* Get info for the domain */
381 domain = find_domain_from_name(domname);
383 if (domain == NULL) {
384 DEBUG(7, ("could not find domain entry for domain %s. "
385 "Using primary domain\n", domname));
386 if ( (domain = find_our_domain()) == NULL ) {
387 DEBUG(0,("Cannot find my primary domain structure!\n"));
388 request_error(state);
393 if ( strequal(domname, lp_workgroup()) && lp_winbind_trusted_domains_only() ) {
394 DEBUG(7,("winbindd_getpwnam: My domain -- rejecting getpwnam() for %s\\%s.\n",
396 request_error(state);
400 /* Get rid and name type from name. The following costs 1 packet */
402 winbindd_lookupname_async(state->mem_ctx, domname, username,
403 getpwnam_name2sid_recv, WINBINDD_GETPWNAM,
407 static void getpwnam_name2sid_recv(void *private_data, BOOL success,
408 const DOM_SID *sid, enum lsa_SidType type)
410 struct winbindd_cli_state *state =
411 (struct winbindd_cli_state *)private_data;
412 fstring domname, username;
415 DEBUG(5, ("Could not lookup name for user %s\n",
416 state->request.data.username));
417 request_error(state);
421 if ((type != SID_NAME_USER) && (type != SID_NAME_COMPUTER)) {
422 DEBUG(5, ("%s is not a user\n", state->request.data.username));
423 request_error(state);
427 if ( parse_domain_user(state->request.data.username, domname, username) ) {
428 check_domain_trusted( domname, sid );
433 winbindd_getpwsid(state, sid);
436 static void getpwuid_recv(void *private_data, BOOL success, const char *sid)
438 struct winbindd_cli_state *state =
439 (struct winbindd_cli_state *)private_data;
443 DEBUG(10,("uid2sid_recv: uid [%lu] to sid mapping failed\n.",
444 (unsigned long)(state->request.data.uid)));
445 request_error(state);
449 DEBUG(10,("uid2sid_recv: uid %lu has sid %s\n",
450 (unsigned long)(state->request.data.uid), sid));
452 string_to_sid(&user_sid, sid);
453 winbindd_getpwsid(state, &user_sid);
456 /* Return a password structure given a uid number */
457 void winbindd_getpwuid(struct winbindd_cli_state *state)
459 DEBUG(3, ("[%5lu]: getpwuid %lu\n", (unsigned long)state->pid,
460 (unsigned long)state->request.data.uid));
462 /* always query idmap via the async interface */
463 /* if this turns to be too slow we will add here a direct query to the cache */
464 winbindd_uid2sid_async(state->mem_ctx, state->request.data.uid, getpwuid_recv, state);
468 * set/get/endpwent functions
471 /* Rewind file pointer for ntdom passwd database */
473 static BOOL winbindd_setpwent_internal(struct winbindd_cli_state *state)
475 struct winbindd_domain *domain;
477 DEBUG(3, ("[%5lu]: setpwent\n", (unsigned long)state->pid));
479 /* Check user has enabled this */
481 if (!lp_winbind_enum_users()) {
485 /* Free old static data if it exists */
487 if (state->getpwent_state != NULL) {
488 free_getent_state(state->getpwent_state);
489 state->getpwent_state = NULL;
493 /* add any local users we have */
495 if ( (domain_state = (struct getent_state *)malloc(sizeof(struct getent_state))) == NULL )
498 ZERO_STRUCTP(domain_state);
500 /* Add to list of open domains */
502 DLIST_ADD(state->getpwent_state, domain_state);
505 /* Create sam pipes for each domain we know about */
507 for(domain = domain_list(); domain != NULL; domain = domain->next) {
508 struct getent_state *domain_state;
511 /* don't add our domaina if we are a PDC or if we
512 are a member of a Samba domain */
514 if ( (IS_DC || lp_winbind_trusted_domains_only())
515 && strequal(domain->name, lp_workgroup()) )
520 /* Create a state record for this domain */
522 if ((domain_state = SMB_MALLOC_P(struct getent_state)) == NULL) {
523 DEBUG(0, ("malloc failed\n"));
527 ZERO_STRUCTP(domain_state);
529 fstrcpy(domain_state->domain_name, domain->name);
531 /* Add to list of open domains */
533 DLIST_ADD(state->getpwent_state, domain_state);
536 state->getpwent_initialized = True;
540 void winbindd_setpwent(struct winbindd_cli_state *state)
542 if (winbindd_setpwent_internal(state)) {
545 request_error(state);
549 /* Close file pointer to ntdom passwd database */
551 void winbindd_endpwent(struct winbindd_cli_state *state)
553 DEBUG(3, ("[%5lu]: endpwent\n", (unsigned long)state->pid));
555 free_getent_state(state->getpwent_state);
556 state->getpwent_initialized = False;
557 state->getpwent_state = NULL;
561 /* Get partial list of domain users for a domain. We fill in the sam_entries,
562 and num_sam_entries fields with domain user information. The dispinfo_ndx
563 field is incremented to the index of the next user to fetch. Return True if
564 some users were returned, False otherwise. */
566 static BOOL get_sam_user_entries(struct getent_state *ent, TALLOC_CTX *mem_ctx)
570 WINBIND_USERINFO *info;
571 struct getpwent_user *name_list = NULL;
572 struct winbindd_domain *domain;
573 struct winbindd_methods *methods;
576 if (ent->num_sam_entries)
579 if (!(domain = find_domain_from_name(ent->domain_name))) {
580 DEBUG(3, ("no such domain %s in get_sam_user_entries\n",
585 methods = domain->methods;
587 /* Free any existing user info */
589 SAFE_FREE(ent->sam_entries);
590 ent->num_sam_entries = 0;
592 /* Call query_user_list to get a list of usernames and user rids */
596 status = methods->query_user_list(domain, mem_ctx, &num_entries,
599 if (!NT_STATUS_IS_OK(status)) {
600 DEBUG(10,("get_sam_user_entries: query_user_list failed with %s\n",
601 nt_errstr(status) ));
606 name_list = SMB_REALLOC_ARRAY(name_list, struct getpwent_user, ent->num_sam_entries + num_entries);
609 DEBUG(0,("get_sam_user_entries realloc failed.\n"));
614 for (i = 0; i < num_entries; i++) {
615 /* Store account name and gecos */
616 if (!info[i].acct_name) {
617 fstrcpy(name_list[ent->num_sam_entries + i].name, "");
619 fstrcpy(name_list[ent->num_sam_entries + i].name,
622 if (!info[i].full_name) {
623 fstrcpy(name_list[ent->num_sam_entries + i].gecos, "");
625 fstrcpy(name_list[ent->num_sam_entries + i].gecos,
628 if (!info[i].homedir) {
629 fstrcpy(name_list[ent->num_sam_entries + i].homedir, "");
631 fstrcpy(name_list[ent->num_sam_entries + i].homedir,
634 if (!info[i].shell) {
635 fstrcpy(name_list[ent->num_sam_entries + i].shell, "");
637 fstrcpy(name_list[ent->num_sam_entries + i].shell,
642 /* User and group ids */
643 sid_copy(&name_list[ent->num_sam_entries+i].user_sid,
645 sid_copy(&name_list[ent->num_sam_entries+i].group_sid,
649 ent->num_sam_entries += num_entries;
651 /* Fill in remaining fields */
653 ent->sam_entries = name_list;
654 ent->sam_entry_index = 0;
655 return ent->num_sam_entries > 0;
658 /* Fetch next passwd entry from ntdom database */
660 #define MAX_GETPWENT_USERS 500
662 void winbindd_getpwent(struct winbindd_cli_state *state)
664 struct getent_state *ent;
665 struct winbindd_pw *user_list;
666 int num_users, user_list_ndx;
668 DEBUG(3, ("[%5lu]: getpwent\n", (unsigned long)state->pid));
670 /* Check user has enabled this */
672 if (!lp_winbind_enum_users()) {
673 request_error(state);
677 /* Allocate space for returning a chunk of users */
679 num_users = MIN(MAX_GETPWENT_USERS, state->request.data.num_entries);
681 if (num_users == 0) {
682 request_error(state);
686 if ((state->response.extra_data.data = SMB_MALLOC_ARRAY(struct winbindd_pw, num_users)) == NULL) {
687 request_error(state);
691 memset(state->response.extra_data.data, 0, num_users *
692 sizeof(struct winbindd_pw));
694 user_list = (struct winbindd_pw *)state->response.extra_data.data;
696 if (!state->getpwent_initialized)
697 winbindd_setpwent_internal(state);
699 if (!(ent = state->getpwent_state)) {
700 request_error(state);
704 /* Start sending back users */
706 for (user_list_ndx = 0; user_list_ndx < num_users; ) {
707 struct getpwent_user *name_list = NULL;
710 /* Do we need to fetch another chunk of users? */
712 if (ent->num_sam_entries == ent->sam_entry_index) {
715 !get_sam_user_entries(ent, state->mem_ctx)) {
716 struct getent_state *next_ent;
718 /* Free state information for this domain */
720 SAFE_FREE(ent->sam_entries);
722 next_ent = ent->next;
723 DLIST_REMOVE(state->getpwent_state, ent);
729 /* No more domains */
735 name_list = (struct getpwent_user *)ent->sam_entries;
737 /* Lookup user info */
739 result = winbindd_fill_pwent(
741 name_list[ent->sam_entry_index].name,
742 &name_list[ent->sam_entry_index].user_sid,
743 &name_list[ent->sam_entry_index].group_sid,
744 name_list[ent->sam_entry_index].gecos,
745 name_list[ent->sam_entry_index].homedir,
746 name_list[ent->sam_entry_index].shell,
747 &user_list[user_list_ndx]);
749 /* Add user to return list */
754 state->response.data.num_entries++;
755 state->response.length +=
756 sizeof(struct winbindd_pw);
759 DEBUG(1, ("could not lookup domain user %s\n",
760 name_list[ent->sam_entry_index].name));
762 ent->sam_entry_index++;
768 if (user_list_ndx > 0)
771 request_error(state);
774 /* List domain users without mapping to unix ids */
776 void winbindd_list_users(struct winbindd_cli_state *state)
778 struct winbindd_domain *domain;
779 WINBIND_USERINFO *info;
780 const char *which_domain;
781 uint32 num_entries = 0, total_entries = 0;
782 char *extra_data = NULL;
783 int extra_data_len = 0;
784 enum winbindd_result rv = WINBINDD_ERROR;
786 DEBUG(3, ("[%5lu]: list users\n", (unsigned long)state->pid));
788 /* Ensure null termination */
789 state->request.domain_name[sizeof(state->request.domain_name)-1]='\0';
790 which_domain = state->request.domain_name;
792 /* Enumerate over trusted domains */
794 for (domain = domain_list(); domain; domain = domain->next) {
796 struct winbindd_methods *methods;
799 /* if we have a domain name restricting the request and this
800 one in the list doesn't match, then just bypass the remainder
803 if ( *which_domain && !strequal(which_domain, domain->name) )
806 methods = domain->methods;
808 /* Query display info */
809 status = methods->query_user_list(domain, state->mem_ctx,
810 &num_entries, &info);
812 if (!NT_STATUS_IS_OK(status)) {
816 if (num_entries == 0)
819 /* Allocate some memory for extra data */
820 total_entries += num_entries;
822 extra_data = (char *)SMB_REALLOC(
823 extra_data, sizeof(fstring) * total_entries);
826 DEBUG(0,("failed to enlarge buffer!\n"));
830 /* Pack user list into extra data fields */
832 for (i = 0; i < num_entries; i++) {
833 fstring acct_name, name;
835 if (!info[i].acct_name) {
836 fstrcpy(acct_name, "");
838 fstrcpy(acct_name, info[i].acct_name);
841 fill_domain_username(name, domain->name, acct_name, True);
843 /* Append to extra data */
844 memcpy(&extra_data[extra_data_len], name,
846 extra_data_len += strlen(name);
847 extra_data[extra_data_len++] = ',';
851 /* Assign extra_data fields in response structure */
854 extra_data[extra_data_len - 1] = '\0';
855 state->response.extra_data.data = extra_data;
856 state->response.length += extra_data_len;
859 /* No domains responded but that's still OK so don't return an
866 if (rv == WINBINDD_OK)
869 request_error(state);