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;
103 DEBUG(3, ("[%5d]: getpwnam %s\n", state->pid,
104 state->request.data.username));
106 /* Parse domain and username */
108 parse_domain_user(state->request.data.username, name_domain,
111 /* Reject names that don't have a domain - i.e name_domain contains
114 if (strequal(name_domain, ""))
115 return WINBINDD_ERROR;
117 if ((domain = find_domain_from_name(name_domain)) == NULL) {
118 DEBUG(5, ("No such domain: %s\n", name_domain));
119 return WINBINDD_ERROR;
122 /* Check for cached user entry */
124 if (winbindd_fetch_user_cache_entry(domain, name_user,
125 &state->response.data.pw))
128 slprintf(name, sizeof(name) - 1, "%s\\%s", name_domain, name_user);
130 /* Get rid and name type from name */
132 if (!winbindd_lookup_sid_by_name(name, &user_sid, &name_type)) {
133 DEBUG(1, ("user '%s' does not exist\n", name_user));
134 return WINBINDD_ERROR;
137 if (name_type != SID_NAME_USER) {
138 DEBUG(1, ("name '%s' is not a user name: %d\n", name_user,
140 return WINBINDD_ERROR;
143 /* Get some user info. Split the user rid from the sid obtained
144 from the winbind_lookup_by_name() call and use it in a
145 winbind_lookup_userinfo() */
147 sid_split_rid(&user_sid, &user_rid);
149 if (!winbindd_lookup_userinfo(domain, user_rid, &user_info)) {
150 DEBUG(1, ("pwnam_from_user(): error getting user info for "
151 "user '%s'\n", name_user));
152 return WINBINDD_ERROR;
155 group_rid = user_info->info.id21->group_rid;
156 unistr2_to_ascii(gecos_name, &user_info->info.id21->uni_full_name,
157 sizeof(gecos_name) - 1);
159 /* Now take all this information and fill in a passwd structure */
161 if (!winbindd_fill_pwent(name_domain, state->request.data.username,
162 user_rid, group_rid, gecos_name,
163 &state->response.data.pw)) {
164 return WINBINDD_ERROR;
167 winbindd_store_user_cache_entry(domain, name_user,
168 &state->response.data.pw);
173 /* Return a password structure given a uid number */
175 enum winbindd_result winbindd_getpwnam_from_uid(struct winbindd_cli_state
179 struct winbindd_domain *domain;
180 uint32 user_rid, group_rid;
181 fstring user_name, gecos_name;
182 enum SID_NAME_USE name_type;
183 SAM_USERINFO_CTR *user_info;
186 /* Bug out if the uid isn't in the winbind range */
188 if ((state->request.data.uid < server_state.uid_low ) ||
189 (state->request.data.uid > server_state.uid_high))
190 return WINBINDD_ERROR;
192 DEBUG(3, ("[%5d]: getpwuid %d\n", state->pid,
193 state->request.data.uid));
195 /* Get rid from uid */
197 if (!winbindd_idmap_get_rid_from_uid(state->request.data.uid,
198 &user_rid, &domain)) {
199 DEBUG(1, ("Could not convert uid %d to rid\n",
200 state->request.data.uid));
201 return WINBINDD_ERROR;
204 /* Check for cached uid entry */
206 if (winbindd_fetch_uid_cache_entry(domain,
207 state->request.data.uid,
208 &state->response.data.pw))
211 /* Get name and name type from rid */
213 sid_copy(&user_sid, &domain->sid);
214 sid_append_rid(&user_sid, user_rid);
216 if (!winbindd_lookup_name_by_sid(&user_sid, user_name, &name_type)) {
219 sid_to_string(temp, &user_sid);
220 DEBUG(1, ("Could not lookup sid %s\n", temp));
222 return WINBINDD_ERROR;
225 if (strcmp("\\", lp_winbind_separator()))
226 string_sub(user_name, "\\", lp_winbind_separator(),
229 /* Get some user info */
231 if (!winbindd_lookup_userinfo(domain, user_rid, &user_info)) {
232 DEBUG(1, ("pwnam_from_uid(): error getting user info for "
233 "user '%s'\n", user_name));
234 return WINBINDD_ERROR;
237 group_rid = user_info->info.id21->group_rid;
238 unistr2_to_ascii(gecos_name, &user_info->info.id21->uni_full_name,
239 sizeof(gecos_name) - 1);
241 /* Resolve gid number */
243 if (!winbindd_idmap_get_gid_from_rid(domain->name, group_rid, &gid)) {
244 DEBUG(1, ("error getting group id for user %s\n", user_name));
245 return WINBINDD_ERROR;
248 /* Fill in password structure */
250 if (!winbindd_fill_pwent(domain->name, user_name, user_rid, group_rid,
251 gecos_name, &state->response.data.pw))
252 return WINBINDD_ERROR;
254 winbindd_store_uid_cache_entry(domain, state->request.data.uid,
255 &state->response.data.pw);
261 * set/get/endpwent functions
264 /* Rewind file pointer for ntdom passwd database */
266 enum winbindd_result winbindd_setpwent(struct winbindd_cli_state *state)
268 struct winbindd_domain *tmp;
270 DEBUG(3, ("[%5d]: setpwent\n", state->pid));
272 /* Check user has enabled this */
274 if (!lp_winbind_enum_users())
275 return WINBINDD_ERROR;
277 /* Free old static data if it exists */
279 if (state->getpwent_state != NULL) {
280 free_getent_state(state->getpwent_state);
281 state->getpwent_state = NULL;
284 /* Create sam pipes for each domain we know about */
286 for(tmp = domain_list; tmp != NULL; tmp = tmp->next) {
287 struct getent_state *domain_state;
289 /* Skip domains other than WINBINDD_DOMAIN environment
292 if ((strcmp(state->request.domain, "") != 0) &&
293 !check_domain_env(state->request.domain, tmp->name))
296 /* Create a state record for this domain */
298 if ((domain_state = (struct getent_state *)
299 malloc(sizeof(struct getent_state))) == NULL)
300 return WINBINDD_ERROR;
302 ZERO_STRUCTP(domain_state);
304 domain_state->domain = tmp;
305 domain_state->mem_ctx = talloc_init();
307 /* Add to list of open domains */
309 DLIST_ADD(state->getpwent_state, domain_state);
315 /* Close file pointer to ntdom passwd database */
317 enum winbindd_result winbindd_endpwent(struct winbindd_cli_state *state)
319 DEBUG(3, ("[%5d]: endpwent\n", state->pid));
321 free_getent_state(state->getpwent_state);
322 state->getpwent_state = NULL;
327 /* Get partial list of domain users for a domain. We fill in the sam_entries,
328 and num_sam_entries fields with domain user information. The dispinfo_ndx
329 field is incremented to the index of the next user to fetch. Return True if
330 some users were returned, False otherwise. */
332 #define MAX_FETCH_SAM_ENTRIES 100
334 static BOOL get_sam_user_entries(struct getent_state *ent)
338 SAM_DISPINFO_1 info1;
339 SAM_DISPINFO_CTR ctr;
340 struct getpwent_user *name_list = NULL;
343 if (ent->got_all_sam_entries)
349 ctr.sam.info1 = &info1;
352 /* Look in cache for entries, else get them direct */
354 if (winbindd_fetch_user_cache(ent->domain,
355 (struct getpwent_user **)
357 &ent->num_sam_entries)) {
362 /* For the moment we set the primary group for every user to be the
363 Domain Users group. There are serious problems with determining
364 the actual primary group for large domains. This should really
365 be made into a 'winbind force group' smb.conf parameter or
366 something like that. */
368 group_rid = DOMAIN_GROUP_RID_USERS;
370 /* Free any existing user info */
372 SAFE_FREE(ent->sam_entries);
373 ent->num_sam_entries = 0;
375 /* Call query_dispinfo to get a list of usernames and user rids */
382 status = winbindd_query_dispinfo(ent->domain, ent->mem_ctx,
383 &ent->dispinfo_ndx, 1,
387 struct getpwent_user *tnl;
389 tnl = (struct getpwent_user *)Realloc(name_list,
390 sizeof(struct getpwent_user) *
391 (ent->num_sam_entries +
395 DEBUG(0,("get_sam_user_entries: Realloc failed.\n"));
396 SAFE_FREE(name_list);
397 return WINBINDD_ERROR;
402 for (i = 0; i < num_entries; i++) {
404 /* Store account name and gecos */
407 name_list[ent->num_sam_entries + i].name,
408 &info1.str[i].uni_acct_name,
412 name_list[ent->num_sam_entries + i].gecos,
413 &info1.str[i].uni_full_name,
416 /* User and group ids */
418 name_list[ent->num_sam_entries + i].user_rid =
419 info1.sam[i].rid_user;
421 name_list[ent->num_sam_entries + i].
422 group_rid = group_rid;
425 ent->num_sam_entries += num_entries;
427 if (NT_STATUS_V(status) != NT_STATUS_V(STATUS_MORE_ENTRIES)) {
431 } while (ent->num_sam_entries < MAX_FETCH_SAM_ENTRIES);
434 /* Fill cache with received entries */
436 winbindd_store_user_cache(ent->domain, ent->sam_entries,
437 ent->num_sam_entries);
440 /* Fill in remaining fields */
442 ent->sam_entries = name_list;
443 ent->sam_entry_index = 0;
444 ent->got_all_sam_entries = (NT_STATUS_V(status) != NT_STATUS_V(STATUS_MORE_ENTRIES));
446 return ent->num_sam_entries > 0;
449 /* Fetch next passwd entry from ntdom database */
451 #define MAX_GETPWENT_USERS 500
453 enum winbindd_result winbindd_getpwent(struct winbindd_cli_state *state)
455 struct getent_state *ent;
456 struct winbindd_pw *user_list;
457 int num_users, user_list_ndx = 0, i;
460 DEBUG(3, ("[%5d]: getpwent\n", state->pid));
462 /* Check user has enabled this */
464 if (!lp_winbind_enum_users())
465 return WINBINDD_ERROR;
467 /* Allocate space for returning a chunk of users */
469 num_users = MIN(MAX_GETPWENT_USERS, state->request.data.num_entries);
471 if ((state->response.extra_data =
472 malloc(num_users * sizeof(struct winbindd_pw))) == NULL)
473 return WINBINDD_ERROR;
475 memset(state->response.extra_data, 0, num_users *
476 sizeof(struct winbindd_pw));
478 user_list = (struct winbindd_pw *)state->response.extra_data;
479 sep = lp_winbind_separator();
481 if (!(ent = state->getpwent_state))
482 return WINBINDD_ERROR;
484 /* Start sending back users */
486 for (i = 0; i < num_users; i++) {
487 struct getpwent_user *name_list = NULL;
488 fstring domain_user_name;
491 /* Do we need to fetch another chunk of users? */
493 if (ent->num_sam_entries == ent->sam_entry_index) {
495 while(ent && !get_sam_user_entries(ent)) {
496 struct getent_state *next_ent;
498 /* Free state information for this domain */
500 SAFE_FREE(ent->sam_entries);
502 next_ent = ent->next;
503 DLIST_REMOVE(state->getpwent_state, ent);
509 /* No more domains */
515 name_list = ent->sam_entries;
517 /* Skip machine accounts */
519 if (name_list[ent->sam_entry_index].
520 name[strlen(name_list[ent->sam_entry_index].name) - 1]
522 ent->sam_entry_index++;
526 /* Lookup user info */
528 slprintf(domain_user_name, sizeof(domain_user_name) - 1,
529 "%s%s%s", ent->domain->name, sep,
530 name_list[ent->sam_entry_index].name);
532 result = winbindd_fill_pwent(
535 name_list[ent->sam_entry_index].user_rid,
536 name_list[ent->sam_entry_index].group_rid,
537 name_list[ent->sam_entry_index].gecos,
538 &user_list[user_list_ndx]);
540 ent->sam_entry_index++;
542 /* Add user to return list */
547 state->response.data.num_entries++;
548 state->response.length +=
549 sizeof(struct winbindd_pw);
552 DEBUG(1, ("could not lookup domain user %s\n",
558 return (user_list_ndx > 0) ? WINBINDD_OK : WINBINDD_ERROR;
561 /* List domain users without mapping to unix ids */
563 enum winbindd_result winbindd_list_users(struct winbindd_cli_state *state)
565 struct winbindd_domain *domain;
566 SAM_DISPINFO_CTR ctr;
567 SAM_DISPINFO_1 info1;
568 uint32 num_entries = 0, total_entries = 0;
569 char *ted, *extra_data = NULL;
570 int extra_data_len = 0;
572 enum winbindd_result rv = WINBINDD_ERROR;
574 DEBUG(3, ("[%5d]: list users\n", state->pid));
576 if (!(mem_ctx = talloc_init()))
577 return WINBINDD_ERROR;
579 /* Enumerate over trusted domains */
581 ctr.sam.info1 = &info1;
583 for (domain = domain_list; domain; domain = domain->next) {
585 uint32 start_ndx = 0;
587 /* Skip domains other than WINBINDD_DOMAIN environment
590 if ((strcmp(state->request.domain, "") != 0) &&
591 !check_domain_env(state->request.domain, domain->name))
594 /* Query display info */
599 status = winbindd_query_dispinfo(
600 domain, mem_ctx, &start_ndx,
601 1, &num_entries, &ctr);
603 if (num_entries == 0)
606 /* Allocate some memory for extra data */
608 total_entries += num_entries;
610 ted = Realloc(extra_data, sizeof(fstring) *
614 DEBUG(0,("winbindd_list_users: failed to enlarge buffer!\n"));
615 SAFE_FREE(extra_data);
620 /* Pack user list into extra data fields */
622 for (i = 0; i < num_entries; i++) {
623 UNISTR2 *uni_acct_name;
624 fstring acct_name, name;
626 /* Convert unistring to ascii */
628 uni_acct_name = &ctr.sam.info1->str[i].
630 unistr2_to_ascii(acct_name, uni_acct_name,
631 sizeof(acct_name) - 1);
633 slprintf(name, sizeof(name) - 1, "%s%s%s",
634 domain->name, lp_winbind_separator(),
637 /* Append to extra data */
639 memcpy(&extra_data[extra_data_len], name,
641 extra_data_len += strlen(name);
643 extra_data[extra_data_len++] = ',';
645 } while (NT_STATUS_V(status) == NT_STATUS_V(STATUS_MORE_ENTRIES));
648 /* Assign extra_data fields in response structure */
651 extra_data[extra_data_len - 1] = '\0';
652 state->response.extra_data = extra_data;
653 state->response.length += extra_data_len;
656 /* No domains responded but that's still OK so don't return an
662 talloc_destroy(mem_ctx);