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;
40 /* Resolve the uid number */
42 if (!winbindd_idmap_get_uid_from_rid(domain_name, user_rid,
44 DEBUG(1, ("error getting user id for rid %d\n", user_rid));
48 /* Resolve the gid number */
50 if (!winbindd_idmap_get_gid_from_rid(domain_name, group_rid,
52 DEBUG(1, ("error getting group id for rid %d\n", group_rid));
58 safe_strcpy(pw->pw_name, name, sizeof(pw->pw_name) - 1);
60 /* Full name (gecos) */
62 safe_strcpy(pw->pw_gecos, full_name, sizeof(pw->pw_gecos) - 1);
64 /* Home directory and shell - use template config parameters. The
65 defaults are /tmp for the home directory and /bin/false for
68 parse_domain_user(name, name_domain, name_user);
70 /* The substitution of %U and %D in the 'template homedir' is done
71 by lp_string() calling standard_sub_basic(). */
73 fstrcpy(current_user_info.smb_name, name_user);
74 fstrcpy(current_user_info.domain, name_domain);
76 pstrcpy(homedir, lp_template_homedir());
78 safe_strcpy(pw->pw_dir, homedir, sizeof(pw->pw_dir) - 1);
80 safe_strcpy(pw->pw_shell, lp_template_shell(),
81 sizeof(pw->pw_shell) - 1);
83 /* Password - set to "x" as we can't generate anything useful here.
84 Authentication can be done using the pam_ntdom module. */
86 safe_strcpy(pw->pw_passwd, "x", sizeof(pw->pw_passwd) - 1);
91 /* Return a password structure from a username. Specify whether cached data
94 enum winbindd_result winbindd_getpwnam_from_user(struct winbindd_cli_state
97 uint32 user_rid, group_rid;
98 SAM_USERINFO_CTR *user_info;
100 fstring name_domain, name_user, name, gecos_name;
101 enum SID_NAME_USE name_type;
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;
118 /* Check for cached user entry */
120 if (winbindd_fetch_user_cache_entry(name_domain, name_user,
121 &state->response.data.pw)) {
125 slprintf(name, sizeof(name) - 1, "%s\\%s", name_domain, name_user);
127 /* Get rid and name type from name. The following costs 1 packet */
129 if (!winbindd_lookup_sid_by_name(name, &user_sid, &name_type)) {
130 DEBUG(1, ("user '%s' does not exist\n", name_user));
131 return WINBINDD_ERROR;
134 if (name_type != SID_NAME_USER) {
135 DEBUG(1, ("name '%s' is not a user name: %d\n", name_user,
137 return WINBINDD_ERROR;
140 /* Get some user info. Split the user rid from the sid obtained
141 from the winbind_lookup_by_name() call and use it in a
142 winbind_lookup_userinfo() */
144 sid_split_rid(&user_sid, &user_rid);
146 /* The following costs 3 packets */
150 if (!winbindd_lookup_userinfo(domain, user_rid, &user_info)) {
151 DEBUG(1, ("pwnam_from_user(): error getting user info for "
152 "user '%s'\n", name_user));
153 return WINBINDD_ERROR;
156 group_rid = user_info->info.id21->group_rid;
157 unistr2_to_ascii(gecos_name, &user_info->info.id21->uni_full_name,
158 sizeof(gecos_name) - 1);
162 group_rid = DOMAIN_GROUP_RID_GUESTS;
163 fstrcpy(gecos_name, "foo");
165 /* Now take all this information and fill in a passwd structure */
167 if (!winbindd_fill_pwent(name_domain, state->request.data.username,
168 user_rid, group_rid, gecos_name,
169 &state->response.data.pw)) {
170 return WINBINDD_ERROR;
173 winbindd_store_user_cache_entry(name_domain, name_user,
174 &state->response.data.pw);
179 /* Return a password structure given a uid number */
181 enum winbindd_result winbindd_getpwnam_from_uid(struct winbindd_cli_state
185 struct winbindd_domain *domain;
186 uint32 user_rid, group_rid;
187 fstring user_name, gecos_name;
188 enum SID_NAME_USE name_type;
189 SAM_USERINFO_CTR *user_info;
192 /* Bug out if the uid isn't in the winbind range */
194 if ((state->request.data.uid < server_state.uid_low ) ||
195 (state->request.data.uid > server_state.uid_high)) {
196 return WINBINDD_ERROR;
199 DEBUG(3, ("[%5d]: getpwuid %d\n", state->pid,
200 state->request.data.uid));
202 /* Get rid from uid */
204 if (!winbindd_idmap_get_rid_from_uid(state->request.data.uid,
205 &user_rid, &domain)) {
206 DEBUG(1, ("Could not convert uid %d to rid\n",
207 state->request.data.uid));
208 return WINBINDD_ERROR;
211 /* Check for cached uid entry */
213 if (winbindd_fetch_uid_cache_entry(domain->name,
214 state->request.data.uid,
215 &state->response.data.pw)) {
219 /* Get name and name type from rid */
221 sid_copy(&user_sid, &domain->sid);
222 sid_append_rid(&user_sid, user_rid);
224 if (!winbindd_lookup_name_by_sid(&user_sid, user_name, &name_type)) {
227 sid_to_string(temp, &user_sid);
228 DEBUG(1, ("Could not lookup sid %s\n", temp));
229 return WINBINDD_ERROR;
232 if (strcmp("\\", lp_winbind_separator())) {
233 string_sub(user_name, "\\", lp_winbind_separator(),
237 /* Get some user info */
241 if (!winbindd_lookup_userinfo(domain, user_rid, &user_info)) {
242 DEBUG(1, ("pwnam_from_uid(): error getting user info for "
243 "user '%s'\n", user_name));
244 return WINBINDD_ERROR;
247 group_rid = user_info->info.id21->group_rid;
248 unistr2_to_ascii(gecos_name, &user_info->info.id21->uni_full_name,
249 sizeof(gecos_name) - 1);
252 group_rid = DOMAIN_GROUP_RID_GUESTS;
253 fstrcpy(gecos_name, "foo");
255 /* Resolve gid number */
257 if (!winbindd_idmap_get_gid_from_rid(domain->name, group_rid, &gid)) {
258 DEBUG(1, ("error getting group id for user %s\n", user_name));
259 return WINBINDD_ERROR;
262 /* Fill in password structure */
264 if (!winbindd_fill_pwent(domain->name, user_name, user_rid, group_rid,
265 gecos_name, &state->response.data.pw)) {
266 return WINBINDD_ERROR;
269 winbindd_store_uid_cache_entry(domain->name, state->request.data.uid,
270 &state->response.data.pw);
278 * set/get/endpwent functions
281 /* Rewind file pointer for ntdom passwd database */
283 enum winbindd_result winbindd_setpwent(struct winbindd_cli_state *state)
285 struct winbindd_domain *tmp;
287 DEBUG(3, ("[%5d]: setpwent\n", state->pid));
289 if (state == NULL) return WINBINDD_ERROR;
291 /* Check user has enabled this */
293 if (!lp_winbind_enum_users()) {
294 return WINBINDD_ERROR;
297 /* Free old static data if it exists */
299 if (state->getpwent_state != NULL) {
300 free_getent_state(state->getpwent_state);
301 state->getpwent_state = NULL;
304 /* Create sam pipes for each domain we know about */
306 for(tmp = domain_list; tmp != NULL; tmp = tmp->next) {
307 struct getent_state *domain_state;
309 /* Skip domains other than WINBINDD_DOMAIN environment variable */
311 if ((strcmp(state->request.domain, "") != 0) &&
312 !check_domain_env(state->request.domain, tmp->name)) {
316 /* Create a state record for this domain */
318 if ((domain_state = (struct getent_state *)
319 malloc(sizeof(struct getent_state))) == NULL) {
321 return WINBINDD_ERROR;
324 ZERO_STRUCTP(domain_state);
325 domain_state->domain = tmp;
327 /* Add to list of open domains */
329 DLIST_ADD(state->getpwent_state, domain_state)
335 /* Close file pointer to ntdom passwd database */
337 enum winbindd_result winbindd_endpwent(struct winbindd_cli_state *state)
339 DEBUG(3, ("[%5d]: endpwent\n", state->pid));
341 if (state == NULL) return WINBINDD_ERROR;
343 free_getent_state(state->getpwent_state);
344 state->getpwent_state = NULL;
349 /* Get partial list of domain users for a domain. We fill in the sam_entries,
350 and num_sam_entries fields with domain user information. The dispinfo_ndx
351 field is incremented to the index of the next user to fetch. Return True if
352 some users were returned, False otherwise. */
354 #define MAX_FETCH_SAM_ENTRIES 100
356 static BOOL get_sam_user_entries(struct getent_state *ent)
360 SAM_DISPINFO_1 info1;
361 SAM_DISPINFO_CTR ctr;
362 struct getpwent_user *name_list = NULL;
365 if (ent->got_all_sam_entries) {
372 ctr.sam.info1 = &info1;
375 /* Look in cache for entries, else get them direct */
377 if (winbindd_fetch_user_cache(ent->domain->name,
378 (struct getpwent_user **)
380 &ent->num_sam_entries)) {
385 /* For the moment we set the primary group for every user to be the
386 Domain Users group. There are serious problems with determining
387 the actual primary group for large domains. This should really
388 be made into a 'winbind force group' smb.conf parameter or
389 something like that. */
391 group_rid = DOMAIN_GROUP_RID_USERS;
393 if (!domain_handles_open(ent->domain)) {
394 return WINBINDD_ERROR;
397 /* Free any existing user info */
399 SAFE_FREE(ent->sam_entries);
400 ent->num_sam_entries = 0;
402 /* Call query_dispinfo to get a list of usernames and user rids */
409 status = winbindd_query_dispinfo(ent->domain,
410 &ent->dispinfo_ndx, 1,
414 struct getpwent_user *tnl;
416 tnl = (struct getpwent_user *)Realloc(name_list,
417 sizeof(struct getpwent_user) *
418 (ent->num_sam_entries +
422 DEBUG(0,("get_sam_user_entries: Realloc failed.\n"));
423 SAFE_FREE(name_list);
424 return WINBINDD_ERROR;
429 for (i = 0; i < num_entries; i++) {
431 /* Store account name and gecos */
434 name_list[ent->num_sam_entries + i].name,
435 &info1.str[i].uni_acct_name,
439 name_list[ent->num_sam_entries + i].gecos,
440 &info1.str[i].uni_full_name,
443 /* User and group ids */
445 name_list[ent->num_sam_entries + i].user_rid =
446 info1.sam[i].rid_user;
448 name_list[ent->num_sam_entries + i].
449 group_rid = group_rid;
452 ent->num_sam_entries += num_entries;
454 if (NT_STATUS_V(status) != NT_STATUS_V(STATUS_MORE_ENTRIES)) {
458 } while (ent->num_sam_entries < MAX_FETCH_SAM_ENTRIES);
461 /* Fill cache with received entries */
463 winbindd_store_user_cache(ent->domain->name, ent->sam_entries,
464 ent->num_sam_entries);
467 /* Fill in remaining fields */
469 ent->sam_entries = name_list;
470 ent->sam_entry_index = 0;
471 ent->got_all_sam_entries = (NT_STATUS_V(status) != NT_STATUS_V(STATUS_MORE_ENTRIES));
473 return ent->num_sam_entries > 0;
476 /* Fetch next passwd entry from ntdom database */
478 #define MAX_GETPWENT_USERS 500
480 enum winbindd_result winbindd_getpwent(struct winbindd_cli_state *state)
482 struct getent_state *ent;
483 struct winbindd_pw *user_list;
484 int num_users, user_list_ndx = 0, i;
487 DEBUG(3, ("[%5d]: getpwent\n", state->pid));
489 if (state == NULL) return WINBINDD_ERROR;
491 /* Check user has enabled this */
493 if (!lp_winbind_enum_users()) {
494 return WINBINDD_ERROR;
497 /* Allocate space for returning a chunk of users */
499 num_users = MIN(MAX_GETPWENT_USERS, state->request.data.num_entries);
501 if ((state->response.extra_data =
502 malloc(num_users * sizeof(struct winbindd_pw))) == NULL) {
503 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;
516 /* Start sending back users */
518 for (i = 0; i < num_users; i++) {
519 struct getpwent_user *name_list = NULL;
520 fstring domain_user_name;
523 /* Do we need to fetch another chunk of users? */
525 if (ent->num_sam_entries == ent->sam_entry_index) {
527 while(ent && !get_sam_user_entries(ent)) {
528 struct getent_state *next_ent;
530 /* Free state information for this domain */
532 SAFE_FREE(ent->sam_entries);
534 next_ent = ent->next;
535 DLIST_REMOVE(state->getpwent_state, ent);
541 /* 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",
591 return (user_list_ndx > 0) ? WINBINDD_OK : WINBINDD_ERROR;
594 /* List domain users without mapping to unix ids */
596 enum winbindd_result winbindd_list_users(struct winbindd_cli_state *state)
598 struct winbindd_domain *domain;
599 SAM_DISPINFO_CTR ctr;
600 SAM_DISPINFO_1 info1;
601 uint32 num_entries = 0, total_entries = 0;
602 char *ted, *extra_data = NULL;
603 int extra_data_len = 0;
605 DEBUG(3, ("[%5d]: list users\n", state->pid));
607 /* Enumerate over trusted domains */
609 ctr.sam.info1 = &info1;
611 for (domain = domain_list; domain; domain = domain->next) {
613 uint32 start_ndx = 0;
615 /* Skip domains other than WINBINDD_DOMAIN environment
618 if ((strcmp(state->request.domain, "") != 0) &&
619 !check_domain_env(state->request.domain, domain->name)) {
623 if (!domain_handles_open(domain)) {
627 /* Query display info */
632 status = winbindd_query_dispinfo(domain, &start_ndx,
636 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);
650 return WINBINDD_ERROR;
652 else extra_data = ted;
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