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(user_sid, &pw->pw_uid))) {
83 DEBUG(1, ("error getting user id for sid %s\n",
84 sid_string_dbg(user_sid)));
88 /* Resolve the gid number */
90 if (!NT_STATUS_IS_OK(idmap_sid_to_gid(group_sid, &pw->pw_gid))) {
91 DEBUG(1, ("error getting group id for sid %s\n",
92 sid_string_dbg(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 struct getpwsid_state {
127 struct winbindd_cli_state *state;
128 struct winbindd_domain *domain;
139 static void getpwsid_queryuser_recv(void *private_data, bool success,
140 const char *acct_name,
141 const char *full_name,
146 static void getpwsid_sid2uid_recv(void *private_data, bool success, uid_t uid);
147 static void getpwsid_sid2gid_recv(void *private_data, bool success, gid_t gid);
149 static void winbindd_getpwsid(struct winbindd_cli_state *state,
152 struct getpwsid_state *s;
154 s = TALLOC_ZERO_P(state->mem_ctx, struct getpwsid_state);
156 DEBUG(0, ("talloc failed\n"));
161 s->domain = find_domain_from_sid_noinit(sid);
162 if (s->domain == NULL) {
163 DEBUG(3, ("Could not find domain for sid %s\n",
164 sid_string_dbg(sid)));
168 sid_copy(&s->user_sid, sid);
170 query_user_async(s->state->mem_ctx, s->domain, sid,
171 getpwsid_queryuser_recv, s);
175 request_error(state);
178 static void getpwsid_queryuser_recv(void *private_data, bool success,
179 const char *acct_name,
180 const char *full_name,
187 struct getpwsid_state *s =
188 talloc_get_type_abort(private_data, struct getpwsid_state);
191 DEBUG(5, ("Could not query domain %s SID %s\n",
192 s->domain->name, sid_string_dbg(&s->user_sid)));
193 request_error(s->state);
197 if ( acct_name && *acct_name ) {
198 fstrcpy( username, acct_name );
200 char *domain_name = NULL;
201 enum lsa_SidType type;
202 char *user_name = NULL;
203 struct winbindd_domain *domain = NULL;
205 domain = find_lookup_domain_from_sid(&s->user_sid);
206 if (domain == NULL) {
207 DEBUG(5, ("find_lookup_domain_from_sid(%s) failed\n",
208 sid_string_dbg(&s->user_sid)));
209 request_error(s->state);
212 winbindd_lookup_name_by_sid(s->state->mem_ctx, domain,
213 &s->user_sid, &domain_name,
216 /* If this still fails we ar4e done. Just error out */
218 DEBUG(5,("Could not obtain a name for SID %s\n",
219 sid_string_dbg(&s->user_sid)));
220 request_error(s->state);
224 fstrcpy( username, user_name );
227 strlower_m( username );
228 s->username = talloc_strdup(s->state->mem_ctx, username);
230 ws_name_replace( s->username, WB_REPLACE_CHAR );
232 s->fullname = talloc_strdup(s->state->mem_ctx, full_name);
233 s->homedir = talloc_strdup(s->state->mem_ctx, homedir);
234 s->shell = talloc_strdup(s->state->mem_ctx, shell);
236 sid_copy(&s->group_sid, &s->domain->sid);
237 sid_append_rid(&s->group_sid, group_rid);
239 winbindd_sid2uid_async(s->state->mem_ctx, &s->user_sid,
240 getpwsid_sid2uid_recv, s);
243 static void getpwsid_sid2uid_recv(void *private_data, bool success, uid_t uid)
245 struct getpwsid_state *s =
246 talloc_get_type_abort(private_data, struct getpwsid_state);
249 DEBUG(5, ("Could not query uid for user %s\\%s\n",
250 s->domain->name, s->username));
251 request_error(s->state);
256 winbindd_sid2gid_async(s->state->mem_ctx, &s->group_sid,
257 getpwsid_sid2gid_recv, s);
260 static void getpwsid_sid2gid_recv(void *private_data, bool success, gid_t gid)
262 struct getpwsid_state *s =
263 talloc_get_type_abort(private_data, struct getpwsid_state);
264 struct winbindd_pw *pw;
265 fstring output_username;
267 /* allow the nss backend to override the primary group ID.
268 If the gid has already been set, then keep it.
269 This makes me feel dirty. If the nss backend already
270 gave us a gid, we don't really care whether the sid2gid()
271 call worked or not. --jerry */
273 if ( s->gid == (gid_t)-1 ) {
276 DEBUG(5, ("Could not query gid for user %s\\%s\n",
277 s->domain->name, s->username));
281 /* take what the sid2gid() call gave us */
285 pw = &s->state->response.data.pw;
288 fill_domain_username(output_username, s->domain->name, s->username, True);
289 safe_strcpy(pw->pw_name, output_username, sizeof(pw->pw_name) - 1);
290 safe_strcpy(pw->pw_gecos, s->fullname, sizeof(pw->pw_gecos) - 1);
292 if (!fillup_pw_field(lp_template_homedir(), s->username, s->domain->name,
293 pw->pw_uid, pw->pw_gid, s->homedir, pw->pw_dir)) {
294 DEBUG(5, ("Could not compose homedir\n"));
298 if (!fillup_pw_field(lp_template_shell(), s->username, s->domain->name,
299 pw->pw_uid, pw->pw_gid, s->shell, pw->pw_shell)) {
300 DEBUG(5, ("Could not compose shell\n"));
304 /* Password - set to "*" as we can't generate anything useful here.
305 Authentication can be done using the pam_winbind module. */
307 safe_strcpy(pw->pw_passwd, "*", sizeof(pw->pw_passwd) - 1);
309 request_ok(s->state);
313 request_error(s->state);
316 /* Return a password structure from a username. */
318 static void getpwnam_name2sid_recv(void *private_data, bool success,
319 const DOM_SID *sid, enum lsa_SidType type);
321 void winbindd_getpwnam(struct winbindd_cli_state *state)
323 struct winbindd_domain *domain;
324 fstring domname, username;
326 /* Ensure null termination */
327 state->request.data.username[sizeof(state->request.data.username)-1]='\0';
329 DEBUG(3, ("[%5lu]: getpwnam %s\n", (unsigned long)state->pid,
330 state->request.data.username));
332 ws_name_return( state->request.data.username, WB_REPLACE_CHAR );
334 if (!parse_domain_user(state->request.data.username, domname,
336 DEBUG(5, ("Could not parse domain user: %s\n",
337 state->request.data.username));
338 request_error(state);
342 /* Get info for the domain */
344 domain = find_domain_from_name(domname);
346 if (domain == NULL) {
347 DEBUG(7, ("could not find domain entry for domain %s. "
348 "Using primary domain\n", domname));
349 if ( (domain = find_our_domain()) == NULL ) {
350 DEBUG(0,("Cannot find my primary domain structure!\n"));
351 request_error(state);
356 if ( strequal(domname, lp_workgroup()) && lp_winbind_trusted_domains_only() ) {
357 DEBUG(7,("winbindd_getpwnam: My domain -- rejecting getpwnam() for %s\\%s.\n",
359 request_error(state);
363 /* Get rid and name type from name. The following costs 1 packet */
365 winbindd_lookupname_async(state->mem_ctx, domname, username,
366 getpwnam_name2sid_recv, WINBINDD_GETPWNAM,
370 static void getpwnam_name2sid_recv(void *private_data, bool success,
371 const DOM_SID *sid, enum lsa_SidType type)
373 struct winbindd_cli_state *state =
374 (struct winbindd_cli_state *)private_data;
375 fstring domname, username;
378 DEBUG(5, ("Could not lookup name for user %s\n",
379 state->request.data.username));
380 request_error(state);
384 if ((type != SID_NAME_USER) && (type != SID_NAME_COMPUTER)) {
385 DEBUG(5, ("%s is not a user\n", state->request.data.username));
386 request_error(state);
390 if ( parse_domain_user(state->request.data.username, domname, username) ) {
391 check_domain_trusted( domname, sid );
396 winbindd_getpwsid(state, sid);
399 static void getpwuid_recv(void *private_data, bool success, const char *sid)
401 struct winbindd_cli_state *state =
402 (struct winbindd_cli_state *)private_data;
406 DEBUG(10,("uid2sid_recv: uid [%lu] to sid mapping failed\n.",
407 (unsigned long)(state->request.data.uid)));
408 request_error(state);
412 DEBUG(10,("uid2sid_recv: uid %lu has sid %s\n",
413 (unsigned long)(state->request.data.uid), sid));
415 string_to_sid(&user_sid, sid);
416 winbindd_getpwsid(state, &user_sid);
419 /* Return a password structure given a uid number */
420 void winbindd_getpwuid(struct winbindd_cli_state *state)
422 DEBUG(3, ("[%5lu]: getpwuid %lu\n", (unsigned long)state->pid,
423 (unsigned long)state->request.data.uid));
425 /* always query idmap via the async interface */
426 /* if this turns to be too slow we will add here a direct query to the cache */
427 winbindd_uid2sid_async(state->mem_ctx, state->request.data.uid, getpwuid_recv, state);
431 * set/get/endpwent functions
434 /* Rewind file pointer for ntdom passwd database */
436 static bool winbindd_setpwent_internal(struct winbindd_cli_state *state)
438 struct winbindd_domain *domain;
440 DEBUG(3, ("[%5lu]: setpwent\n", (unsigned long)state->pid));
442 /* Check user has enabled this */
444 if (!lp_winbind_enum_users()) {
448 /* Free old static data if it exists */
450 if (state->getpwent_state != NULL) {
451 free_getent_state(state->getpwent_state);
452 state->getpwent_state = NULL;
456 /* add any local users we have */
458 if ( (domain_state = (struct getent_state *)malloc(sizeof(struct getent_state))) == NULL )
461 ZERO_STRUCTP(domain_state);
463 /* Add to list of open domains */
465 DLIST_ADD(state->getpwent_state, domain_state);
468 /* Create sam pipes for each domain we know about */
470 for(domain = domain_list(); domain != NULL; domain = domain->next) {
471 struct getent_state *domain_state;
474 /* don't add our domaina if we are a PDC or if we
475 are a member of a Samba domain */
477 if ( (IS_DC || lp_winbind_trusted_domains_only())
478 && strequal(domain->name, lp_workgroup()) )
483 /* Create a state record for this domain */
485 if ((domain_state = SMB_MALLOC_P(struct getent_state)) == NULL) {
486 DEBUG(0, ("malloc failed\n"));
490 ZERO_STRUCTP(domain_state);
492 fstrcpy(domain_state->domain_name, domain->name);
494 /* Add to list of open domains */
496 DLIST_ADD(state->getpwent_state, domain_state);
499 state->getpwent_initialized = True;
503 void winbindd_setpwent(struct winbindd_cli_state *state)
505 if (winbindd_setpwent_internal(state)) {
508 request_error(state);
512 /* Close file pointer to ntdom passwd database */
514 void winbindd_endpwent(struct winbindd_cli_state *state)
516 DEBUG(3, ("[%5lu]: endpwent\n", (unsigned long)state->pid));
518 free_getent_state(state->getpwent_state);
519 state->getpwent_initialized = False;
520 state->getpwent_state = NULL;
524 /* Get partial list of domain users for a domain. We fill in the sam_entries,
525 and num_sam_entries fields with domain user information. The dispinfo_ndx
526 field is incremented to the index of the next user to fetch. Return True if
527 some users were returned, False otherwise. */
529 static bool get_sam_user_entries(struct getent_state *ent, TALLOC_CTX *mem_ctx)
533 WINBIND_USERINFO *info;
534 struct getpwent_user *name_list = NULL;
535 struct winbindd_domain *domain;
536 struct winbindd_methods *methods;
539 if (ent->num_sam_entries)
542 if (!(domain = find_domain_from_name(ent->domain_name))) {
543 DEBUG(3, ("no such domain %s in get_sam_user_entries\n",
548 methods = domain->methods;
550 /* Free any existing user info */
552 SAFE_FREE(ent->sam_entries);
553 ent->num_sam_entries = 0;
555 /* Call query_user_list to get a list of usernames and user rids */
559 status = methods->query_user_list(domain, mem_ctx, &num_entries,
562 if (!NT_STATUS_IS_OK(status)) {
563 DEBUG(10,("get_sam_user_entries: query_user_list failed with %s\n",
564 nt_errstr(status) ));
569 name_list = SMB_REALLOC_ARRAY(name_list, struct getpwent_user, ent->num_sam_entries + num_entries);
572 DEBUG(0,("get_sam_user_entries realloc failed.\n"));
577 for (i = 0; i < num_entries; i++) {
578 /* Store account name and gecos */
579 if (!info[i].acct_name) {
580 fstrcpy(name_list[ent->num_sam_entries + i].name, "");
582 fstrcpy(name_list[ent->num_sam_entries + i].name,
585 if (!info[i].full_name) {
586 fstrcpy(name_list[ent->num_sam_entries + i].gecos, "");
588 fstrcpy(name_list[ent->num_sam_entries + i].gecos,
591 if (!info[i].homedir) {
592 fstrcpy(name_list[ent->num_sam_entries + i].homedir, "");
594 fstrcpy(name_list[ent->num_sam_entries + i].homedir,
597 if (!info[i].shell) {
598 fstrcpy(name_list[ent->num_sam_entries + i].shell, "");
600 fstrcpy(name_list[ent->num_sam_entries + i].shell,
605 /* User and group ids */
606 sid_copy(&name_list[ent->num_sam_entries+i].user_sid,
608 sid_copy(&name_list[ent->num_sam_entries+i].group_sid,
612 ent->num_sam_entries += num_entries;
614 /* Fill in remaining fields */
616 ent->sam_entries = name_list;
617 ent->sam_entry_index = 0;
618 return ent->num_sam_entries > 0;
621 /* Fetch next passwd entry from ntdom database */
623 #define MAX_GETPWENT_USERS 500
625 void winbindd_getpwent(struct winbindd_cli_state *state)
627 struct getent_state *ent;
628 struct winbindd_pw *user_list;
629 int num_users, user_list_ndx;
631 DEBUG(3, ("[%5lu]: getpwent\n", (unsigned long)state->pid));
633 /* Check user has enabled this */
635 if (!lp_winbind_enum_users()) {
636 request_error(state);
640 /* Allocate space for returning a chunk of users */
642 num_users = MIN(MAX_GETPWENT_USERS, state->request.data.num_entries);
644 if (num_users == 0) {
645 request_error(state);
649 if ((state->response.extra_data.data = SMB_MALLOC_ARRAY(struct winbindd_pw, num_users)) == NULL) {
650 request_error(state);
654 memset(state->response.extra_data.data, 0, num_users *
655 sizeof(struct winbindd_pw));
657 user_list = (struct winbindd_pw *)state->response.extra_data.data;
659 if (!state->getpwent_initialized)
660 winbindd_setpwent_internal(state);
662 if (!(ent = state->getpwent_state)) {
663 request_error(state);
667 /* Start sending back users */
669 for (user_list_ndx = 0; user_list_ndx < num_users; ) {
670 struct getpwent_user *name_list = NULL;
673 /* Do we need to fetch another chunk of users? */
675 if (ent->num_sam_entries == ent->sam_entry_index) {
678 !get_sam_user_entries(ent, state->mem_ctx)) {
679 struct getent_state *next_ent;
681 /* Free state information for this domain */
683 SAFE_FREE(ent->sam_entries);
685 next_ent = ent->next;
686 DLIST_REMOVE(state->getpwent_state, ent);
692 /* No more domains */
698 name_list = (struct getpwent_user *)ent->sam_entries;
700 /* Lookup user info */
702 result = winbindd_fill_pwent(
704 name_list[ent->sam_entry_index].name,
705 &name_list[ent->sam_entry_index].user_sid,
706 &name_list[ent->sam_entry_index].group_sid,
707 name_list[ent->sam_entry_index].gecos,
708 name_list[ent->sam_entry_index].homedir,
709 name_list[ent->sam_entry_index].shell,
710 &user_list[user_list_ndx]);
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));
725 ent->sam_entry_index++;
731 if (user_list_ndx > 0)
734 request_error(state);
737 /* List domain users without mapping to unix ids */
739 void winbindd_list_users(struct winbindd_cli_state *state)
741 struct winbindd_domain *domain;
742 WINBIND_USERINFO *info;
743 const char *which_domain;
744 uint32 num_entries = 0, total_entries = 0;
745 char *extra_data = NULL;
746 int extra_data_len = 0;
747 enum winbindd_result rv = WINBINDD_ERROR;
749 DEBUG(3, ("[%5lu]: list users\n", (unsigned long)state->pid));
751 /* Ensure null termination */
752 state->request.domain_name[sizeof(state->request.domain_name)-1]='\0';
753 which_domain = state->request.domain_name;
755 /* Enumerate over trusted domains */
757 for (domain = domain_list(); domain; domain = domain->next) {
759 struct winbindd_methods *methods;
762 /* if we have a domain name restricting the request and this
763 one in the list doesn't match, then just bypass the remainder
766 if ( *which_domain && !strequal(which_domain, domain->name) )
769 methods = domain->methods;
771 /* Query display info */
772 status = methods->query_user_list(domain, state->mem_ctx,
773 &num_entries, &info);
775 if (!NT_STATUS_IS_OK(status)) {
779 if (num_entries == 0)
782 /* Allocate some memory for extra data */
783 total_entries += num_entries;
785 extra_data = (char *)SMB_REALLOC(
786 extra_data, sizeof(fstring) * total_entries);
789 DEBUG(0,("failed to enlarge buffer!\n"));
793 /* Pack user list into extra data fields */
795 for (i = 0; i < num_entries; i++) {
796 fstring acct_name, name;
798 if (!info[i].acct_name) {
799 fstrcpy(acct_name, "");
801 fstrcpy(acct_name, info[i].acct_name);
804 fill_domain_username(name, domain->name, acct_name, True);
806 /* Append to extra data */
807 memcpy(&extra_data[extra_data_len], name,
809 extra_data_len += strlen(name);
810 extra_data[extra_data_len++] = ',';
814 /* Assign extra_data fields in response structure */
817 extra_data[extra_data_len - 1] = '\0';
818 state->response.extra_data.data = extra_data;
819 state->response.length += extra_data_len;
822 /* No domains responded but that's still OK so don't return an
829 if (rv == WINBINDD_OK)
832 request_error(state);