2 Unix SMB/Netbios implementation.
5 Winbind daemon - user related functions
7 Copyright (C) Tim Potter 2000
9 This program is free software; you can redistribute it and/or modify
10 it under the terms of the GNU General Public License as published by
11 the Free Software Foundation; either version 2 of the License, or
12 (at your option) any later version.
14 This program is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 GNU General Public License for more details.
19 You should have received a copy of the GNU General Public License
20 along with this program; if not, write to the Free Software
21 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
26 /* Fill a pwent structure with information we have obtained */
28 static BOOL winbindd_fill_pwent(char *domain_name, char *name,
29 uint32 user_rid, uint32 group_rid,
30 char *full_name, struct winbindd_pw *pw)
32 extern userdom_struct current_user_info;
33 fstring name_domain, name_user;
39 /* Resolve the uid number */
41 if (!winbindd_idmap_get_uid_from_rid(domain_name, user_rid,
43 DEBUG(1, ("error getting user id for rid %d\n", user_rid));
47 /* Resolve the gid number */
49 if (!winbindd_idmap_get_gid_from_rid(domain_name, group_rid,
51 DEBUG(1, ("error getting group id for rid %d\n", group_rid));
57 safe_strcpy(pw->pw_name, name, sizeof(pw->pw_name) - 1);
59 /* Full name (gecos) */
61 safe_strcpy(pw->pw_gecos, full_name, sizeof(pw->pw_gecos) - 1);
63 /* Home directory and shell - use template config parameters. The
64 defaults are /tmp for the home directory and /bin/false for
67 parse_domain_user(name, name_domain, name_user);
69 /* The substitution of %U and %D in the 'template homedir' is done
70 by lp_string() calling standard_sub_basic(). */
72 fstrcpy(current_user_info.smb_name, name_user);
73 fstrcpy(current_user_info.domain, name_domain);
75 pstrcpy(homedir, lp_template_homedir());
77 safe_strcpy(pw->pw_dir, homedir, sizeof(pw->pw_dir) - 1);
79 safe_strcpy(pw->pw_shell, lp_template_shell(),
80 sizeof(pw->pw_shell) - 1);
82 /* Password - set to "x" as we can't generate anything useful here.
83 Authentication can be done using the pam_ntdom module. */
85 safe_strcpy(pw->pw_passwd, "x", sizeof(pw->pw_passwd) - 1);
90 /* Return a password structure from a username. Specify whether cached data
93 enum winbindd_result winbindd_getpwnam_from_user(struct winbindd_cli_state
96 uint32 user_rid, group_rid;
97 SAM_USERINFO_CTR *user_info;
99 fstring name_domain, name_user, name, gecos_name;
100 enum SID_NAME_USE name_type;
101 struct winbindd_domain *domain;
104 DEBUG(3, ("[%5d]: getpwnam %s\n", state->pid,
105 state->request.data.username));
107 /* Parse domain and username */
109 parse_domain_user(state->request.data.username, name_domain,
112 /* Reject names that don't have a domain - i.e name_domain contains
115 if (strequal(name_domain, ""))
116 return WINBINDD_ERROR;
118 if ((domain = find_domain_from_name(name_domain)) == NULL) {
119 DEBUG(5, ("No such domain: %s\n", name_domain));
120 return WINBINDD_ERROR;
123 /* Check for cached user entry */
125 if (winbindd_fetch_user_cache_entry(domain, name_user,
126 &state->response.data.pw))
129 slprintf(name, sizeof(name) - 1, "%s\\%s", name_domain, name_user);
131 /* Get rid and name type from name */
133 if (!winbindd_lookup_sid_by_name(name, &user_sid, &name_type)) {
134 DEBUG(1, ("user '%s' does not exist\n", name_user));
135 return WINBINDD_ERROR;
138 if (name_type != SID_NAME_USER) {
139 DEBUG(1, ("name '%s' is not a user name: %d\n", name_user,
141 return WINBINDD_ERROR;
144 /* Get some user info. Split the user rid from the sid obtained
145 from the winbind_lookup_by_name() call and use it in a
146 winbind_lookup_userinfo() */
148 if (!(mem_ctx = talloc_init())) {
149 DEBUG(1, ("out of memory\n"));
150 return WINBINDD_ERROR;
153 sid_split_rid(&user_sid, &user_rid);
155 if (!winbindd_lookup_userinfo(domain, mem_ctx, user_rid, &user_info)) {
156 DEBUG(1, ("pwnam_from_user(): error getting user info for "
157 "user '%s'\n", name_user));
158 return WINBINDD_ERROR;
161 group_rid = user_info->info.id21->group_rid;
163 unistr2_to_ascii(gecos_name, &user_info->info.id21->uni_full_name,
164 sizeof(gecos_name) - 1);
166 talloc_destroy(mem_ctx);
169 /* Now take all this information and fill in a passwd structure */
171 if (!winbindd_fill_pwent(name_domain, state->request.data.username,
172 user_rid, group_rid, gecos_name,
173 &state->response.data.pw)) {
174 return WINBINDD_ERROR;
177 winbindd_store_user_cache_entry(domain, name_user,
178 &state->response.data.pw);
183 /* Return a password structure given a uid number */
185 enum winbindd_result winbindd_getpwnam_from_uid(struct winbindd_cli_state
189 struct winbindd_domain *domain;
190 uint32 user_rid, group_rid;
191 fstring user_name, gecos_name;
192 enum SID_NAME_USE name_type;
193 SAM_USERINFO_CTR *user_info;
197 /* Bug out if the uid isn't in the winbind range */
199 if ((state->request.data.uid < server_state.uid_low ) ||
200 (state->request.data.uid > server_state.uid_high))
201 return WINBINDD_ERROR;
203 DEBUG(3, ("[%5d]: getpwuid %d\n", state->pid,
204 state->request.data.uid));
206 /* Get rid from uid */
208 if (!winbindd_idmap_get_rid_from_uid(state->request.data.uid,
209 &user_rid, &domain)) {
210 DEBUG(1, ("Could not convert uid %d to rid\n",
211 state->request.data.uid));
212 return WINBINDD_ERROR;
215 /* Check for cached uid entry */
217 if (winbindd_fetch_uid_cache_entry(domain,
218 state->request.data.uid,
219 &state->response.data.pw))
222 /* Get name and name type from rid */
224 sid_copy(&user_sid, &domain->sid);
225 sid_append_rid(&user_sid, user_rid);
227 if (!winbindd_lookup_name_by_sid(&user_sid, user_name, &name_type)) {
230 sid_to_string(temp, &user_sid);
231 DEBUG(1, ("Could not lookup sid %s\n", temp));
233 return WINBINDD_ERROR;
236 if (strcmp("\\", lp_winbind_separator()))
237 string_sub(user_name, "\\", lp_winbind_separator(),
240 /* Get some user info */
242 if (!(mem_ctx = talloc_init())) {
243 DEBUG(1, ("out of memory\n"));
244 return WINBINDD_ERROR;
247 if (!winbindd_lookup_userinfo(domain, mem_ctx, user_rid, &user_info)) {
248 DEBUG(1, ("pwnam_from_uid(): error getting user info for "
249 "user '%s'\n", user_name));
250 return WINBINDD_ERROR;
253 group_rid = user_info->info.id21->group_rid;
254 unistr2_to_ascii(gecos_name, &user_info->info.id21->uni_full_name,
255 sizeof(gecos_name) - 1);
257 talloc_destroy(mem_ctx);
260 /* Resolve gid number */
262 if (!winbindd_idmap_get_gid_from_rid(domain->name, group_rid, &gid)) {
263 DEBUG(1, ("error getting group id for user %s\n", user_name));
264 return WINBINDD_ERROR;
267 /* Fill in password structure */
269 if (!winbindd_fill_pwent(domain->name, user_name, user_rid, group_rid,
270 gecos_name, &state->response.data.pw))
271 return WINBINDD_ERROR;
273 winbindd_store_uid_cache_entry(domain, state->request.data.uid,
274 &state->response.data.pw);
280 * set/get/endpwent functions
283 /* Rewind file pointer for ntdom passwd database */
285 enum winbindd_result winbindd_setpwent(struct winbindd_cli_state *state)
287 struct winbindd_domain *tmp;
289 DEBUG(3, ("[%5d]: setpwent\n", state->pid));
291 /* Check user has enabled this */
293 if (!lp_winbind_enum_users())
294 return WINBINDD_ERROR;
296 /* Free old static data if it exists */
298 if (state->getpwent_state != NULL) {
299 free_getent_state(state->getpwent_state);
300 state->getpwent_state = NULL;
303 /* Create sam pipes for each domain we know about */
305 if (domain_list == NULL)
308 for(tmp = domain_list; tmp != NULL; tmp = tmp->next) {
309 struct getent_state *domain_state;
311 /* Skip domains other than WINBINDD_DOMAIN environment
314 if ((strcmp(state->request.domain, "") != 0) &&
315 !check_domain_env(state->request.domain, tmp->name))
318 /* Create a state record for this domain */
320 if ((domain_state = (struct getent_state *)
321 malloc(sizeof(struct getent_state))) == NULL)
322 return WINBINDD_ERROR;
324 ZERO_STRUCTP(domain_state);
326 domain_state->domain = tmp;
328 /* Add to list of open domains */
330 DLIST_ADD(state->getpwent_state, domain_state);
336 /* Close file pointer to ntdom passwd database */
338 enum winbindd_result winbindd_endpwent(struct winbindd_cli_state *state)
340 DEBUG(3, ("[%5d]: endpwent\n", state->pid));
342 free_getent_state(state->getpwent_state);
343 state->getpwent_state = NULL;
348 /* Get partial list of domain users for a domain. We fill in the sam_entries,
349 and num_sam_entries fields with domain user information. The dispinfo_ndx
350 field is incremented to the index of the next user to fetch. Return True if
351 some users were returned, False otherwise. */
353 #define MAX_FETCH_SAM_ENTRIES 100
355 static BOOL get_sam_user_entries(struct getent_state *ent)
359 SAM_DISPINFO_1 info1;
360 SAM_DISPINFO_CTR ctr;
361 struct getpwent_user *name_list = NULL;
366 if (ent->got_all_sam_entries)
369 if (!(mem_ctx = talloc_init()))
375 ctr.sam.info1 = &info1;
378 /* Look in cache for entries, else get them direct */
380 if (winbindd_fetch_user_cache(ent->domain,
381 (struct getpwent_user **)
383 &ent->num_sam_entries)) {
388 /* For the moment we set the primary group for every user to be the
389 Domain Users group. There are serious problems with determining
390 the actual primary group for large domains. This should really
391 be made into a 'winbind force group' smb.conf parameter or
392 something like that. */
394 group_rid = DOMAIN_GROUP_RID_USERS;
396 /* Free any existing user info */
398 SAFE_FREE(ent->sam_entries);
399 ent->num_sam_entries = 0;
401 /* Call query_dispinfo to get a list of usernames and user rids */
408 status = winbindd_query_dispinfo(ent->domain, mem_ctx,
409 &ent->dispinfo_ndx, 1,
413 struct getpwent_user *tnl;
415 tnl = (struct getpwent_user *)Realloc(name_list,
416 sizeof(struct getpwent_user) *
417 (ent->num_sam_entries +
421 DEBUG(0,("get_sam_user_entries: Realloc failed.\n"));
422 SAFE_FREE(name_list);
428 for (i = 0; i < num_entries; i++) {
430 /* Store account name and gecos */
433 name_list[ent->num_sam_entries + i].name,
434 &info1.str[i].uni_acct_name,
438 name_list[ent->num_sam_entries + i].gecos,
439 &info1.str[i].uni_full_name,
442 /* User and group ids */
444 name_list[ent->num_sam_entries + i].user_rid =
445 info1.sam[i].rid_user;
447 name_list[ent->num_sam_entries + i].
448 group_rid = group_rid;
451 ent->num_sam_entries += num_entries;
453 if (NT_STATUS_V(status) != NT_STATUS_V(STATUS_MORE_ENTRIES))
456 } while (ent->num_sam_entries < MAX_FETCH_SAM_ENTRIES);
459 /* Fill cache with received entries */
461 winbindd_store_user_cache(ent->domain, ent->sam_entries,
462 ent->num_sam_entries);
465 /* Fill in remaining fields */
467 ent->sam_entries = name_list;
468 ent->sam_entry_index = 0;
469 ent->got_all_sam_entries = (NT_STATUS_V(status) !=
470 NT_STATUS_V(STATUS_MORE_ENTRIES));
472 result = ent->num_sam_entries > 0;
475 talloc_destroy(mem_ctx);
480 /* Fetch next passwd entry from ntdom database */
482 #define MAX_GETPWENT_USERS 500
484 enum winbindd_result winbindd_getpwent(struct winbindd_cli_state *state)
486 struct getent_state *ent;
487 struct winbindd_pw *user_list;
488 int num_users, user_list_ndx = 0, i;
491 DEBUG(3, ("[%5d]: getpwent\n", state->pid));
493 /* Check user has enabled this */
495 if (!lp_winbind_enum_users())
496 return WINBINDD_ERROR;
498 /* Allocate space for returning a chunk of users */
500 num_users = MIN(MAX_GETPWENT_USERS, state->request.data.num_entries);
502 if ((state->response.extra_data =
503 malloc(num_users * sizeof(struct winbindd_pw))) == NULL)
504 return WINBINDD_ERROR;
506 memset(state->response.extra_data, 0, num_users *
507 sizeof(struct winbindd_pw));
509 user_list = (struct winbindd_pw *)state->response.extra_data;
510 sep = lp_winbind_separator();
512 if (!(ent = state->getpwent_state))
513 return WINBINDD_ERROR;
515 /* Start sending back users */
517 for (i = 0; i < num_users; i++) {
518 struct getpwent_user *name_list = NULL;
519 fstring domain_user_name;
522 /* Do we need to fetch another chunk of users? */
524 if (ent->num_sam_entries == ent->sam_entry_index) {
526 while(ent && !get_sam_user_entries(ent)) {
527 struct getent_state *next_ent;
529 /* Free state information for this domain */
531 SAFE_FREE(ent->sam_entries);
533 next_ent = ent->next;
534 DLIST_REMOVE(state->getpwent_state, ent);
540 /* No more domains */
546 name_list = ent->sam_entries;
548 /* Skip machine accounts */
550 if (name_list[ent->sam_entry_index].
551 name[strlen(name_list[ent->sam_entry_index].name) - 1]
553 ent->sam_entry_index++;
557 /* Lookup user info */
559 slprintf(domain_user_name, sizeof(domain_user_name) - 1,
560 "%s%s%s", ent->domain->name, sep,
561 name_list[ent->sam_entry_index].name);
563 result = winbindd_fill_pwent(
566 name_list[ent->sam_entry_index].user_rid,
567 name_list[ent->sam_entry_index].group_rid,
568 name_list[ent->sam_entry_index].gecos,
569 &user_list[user_list_ndx]);
571 ent->sam_entry_index++;
573 /* Add user to return list */
578 state->response.data.num_entries++;
579 state->response.length +=
580 sizeof(struct winbindd_pw);
583 DEBUG(1, ("could not lookup domain user %s\n",
589 return (user_list_ndx > 0) ? WINBINDD_OK : WINBINDD_ERROR;
592 /* List domain users without mapping to unix ids */
594 enum winbindd_result winbindd_list_users(struct winbindd_cli_state *state)
596 struct winbindd_domain *domain;
597 SAM_DISPINFO_CTR ctr;
598 SAM_DISPINFO_1 info1;
599 uint32 num_entries = 0, total_entries = 0;
600 char *ted, *extra_data = NULL;
601 int extra_data_len = 0;
603 enum winbindd_result rv = WINBINDD_ERROR;
605 DEBUG(3, ("[%5d]: list users\n", state->pid));
607 if (!(mem_ctx = talloc_init()))
608 return WINBINDD_ERROR;
610 /* Enumerate over trusted domains */
612 ctr.sam.info1 = &info1;
614 if (domain_list == NULL)
617 for (domain = domain_list; domain; domain = domain->next) {
619 uint32 start_ndx = 0;
621 /* Skip domains other than WINBINDD_DOMAIN environment
624 if ((strcmp(state->request.domain, "") != 0) &&
625 !check_domain_env(state->request.domain, domain->name))
628 /* Query display info */
633 status = winbindd_query_dispinfo(
634 domain, mem_ctx, &start_ndx,
635 1, &num_entries, &ctr);
637 if (num_entries == 0)
640 /* Allocate some memory for extra data */
642 total_entries += num_entries;
644 ted = Realloc(extra_data, sizeof(fstring) *
648 DEBUG(0,("winbindd_list_users: failed to enlarge buffer!\n"));
649 SAFE_FREE(extra_data);
654 /* Pack user list into extra data fields */
656 for (i = 0; i < num_entries; i++) {
657 UNISTR2 *uni_acct_name;
658 fstring acct_name, name;
660 /* Convert unistring to ascii */
662 uni_acct_name = &ctr.sam.info1->str[i].
664 unistr2_to_ascii(acct_name, uni_acct_name,
665 sizeof(acct_name) - 1);
667 slprintf(name, sizeof(name) - 1, "%s%s%s",
668 domain->name, lp_winbind_separator(),
671 /* Append to extra data */
673 memcpy(&extra_data[extra_data_len], name,
675 extra_data_len += strlen(name);
677 extra_data[extra_data_len++] = ',';
679 } while (NT_STATUS_V(status) == NT_STATUS_V(STATUS_MORE_ENTRIES));
682 /* Assign extra_data fields in response structure */
685 extra_data[extra_data_len - 1] = '\0';
686 state->response.extra_data = extra_data;
687 state->response.length += extra_data_len;
690 /* No domains responded but that's still OK so don't return an
697 talloc_destroy(mem_ctx);