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 name_type, user_rid, group_rid;
98 SAM_USERINFO_CTR user_info;
100 fstring name_domain, name_user, name, gecos_name;
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;
118 /* Get info for the domain */
120 if ((domain = find_domain_from_name(name_domain)) == NULL) {
121 DEBUG(0, ("could not find domain entry for domain %s\n",
123 return WINBINDD_ERROR;
126 if (!domain_handles_open(domain)) {
127 return WINBINDD_ERROR;
130 /* Check for cached user entry */
132 if (winbindd_fetch_user_cache_entry(name_domain, name_user,
133 &state->response.data.pw)) {
137 slprintf(name, sizeof(name) - 1, "%s\\%s", name_domain, name_user);
139 /* Get rid and name type from name. The following costs 1 packet */
141 if (!winbindd_lookup_sid_by_name(name, &user_sid, &name_type)) {
142 DEBUG(1, ("user '%s' does not exist\n", name_user));
143 return WINBINDD_ERROR;
146 if (name_type != SID_NAME_USER) {
147 DEBUG(1, ("name '%s' is not a user name: %d\n", name_user,
149 return WINBINDD_ERROR;
152 /* Get some user info. Split the user rid from the sid obtained
153 from the winbind_lookup_by_name() call and use it in a
154 winbind_lookup_userinfo() */
156 sid_split_rid(&user_sid, &user_rid);
158 /* The following costs 3 packets */
160 if (!winbindd_lookup_userinfo(domain, user_rid, &user_info)) {
161 DEBUG(1, ("pwnam_from_user(): error getting user info for "
162 "user '%s'\n", name_user));
163 return WINBINDD_ERROR;
166 group_rid = user_info.info.id21->group_rid;
167 unistr2_to_ascii(gecos_name, &user_info.info.id21->uni_full_name,
168 sizeof(gecos_name) - 1);
170 wb_free_samr_userinfo_ctr(&user_info);
172 /* Now take all this information and fill in a passwd structure */
174 if (!winbindd_fill_pwent(domain->name, state->request.data.username,
175 user_rid, group_rid, gecos_name,
176 &state->response.data.pw)) {
177 return WINBINDD_ERROR;
180 winbindd_store_user_cache_entry(name_domain, name_user,
181 &state->response.data.pw);
186 /* Return a password structure given a uid number */
188 enum winbindd_result winbindd_getpwnam_from_uid(struct winbindd_cli_state
192 struct winbindd_domain *domain;
193 uint32 user_rid, group_rid;
194 fstring user_name, gecos_name;
195 enum SID_NAME_USE name_type;
196 SAM_USERINFO_CTR user_info;
199 /* Bug out if the uid isn't in the winbind range */
201 if ((state->request.data.uid < server_state.uid_low ) ||
202 (state->request.data.uid > server_state.uid_high)) {
203 return WINBINDD_ERROR;
206 DEBUG(3, ("[%5d]: getpwuid %d\n", state->pid,
207 state->request.data.uid));
209 /* Get rid from uid */
211 if (!winbindd_idmap_get_rid_from_uid(state->request.data.uid,
212 &user_rid, &domain)) {
213 DEBUG(1, ("Could not convert uid %d to rid\n",
214 state->request.data.uid));
215 return WINBINDD_ERROR;
218 if (!domain_handles_open(domain)) {
219 return WINBINDD_ERROR;
222 /* Check for cached uid entry */
224 if (winbindd_fetch_uid_cache_entry(domain->name,
225 state->request.data.uid,
226 &state->response.data.pw)) {
230 /* Get name and name type from rid */
232 sid_copy(&user_sid, &domain->sid);
233 sid_append_rid(&user_sid, user_rid);
235 if (!winbindd_lookup_name_by_sid(&user_sid, user_name, &name_type)) {
238 sid_to_string(temp, &user_sid);
239 DEBUG(1, ("Could not lookup sid %s\n", temp));
240 return WINBINDD_ERROR;
243 if (strcmp("\\", lp_winbind_separator())) {
244 string_sub(user_name, "\\", lp_winbind_separator(),
248 /* Get some user info */
250 if (!winbindd_lookup_userinfo(domain, user_rid, &user_info)) {
251 DEBUG(1, ("pwnam_from_uid(): error getting user info for "
252 "user '%s'\n", user_name));
253 return WINBINDD_ERROR;
256 group_rid = user_info.info.id21->group_rid;
257 unistr2_to_ascii(gecos_name, &user_info.info.id21->uni_full_name,
258 sizeof(gecos_name) - 1);
260 wb_free_samr_userinfo_ctr(&user_info);
262 /* Resolve gid number */
264 if (!winbindd_idmap_get_gid_from_rid(domain->name, group_rid, &gid)) {
265 DEBUG(1, ("error getting group id for user %s\n", user_name));
266 return WINBINDD_ERROR;
269 /* Fill in password structure */
271 if (!winbindd_fill_pwent(domain->name, user_name, user_rid, group_rid,
272 gecos_name, &state->response.data.pw)) {
273 return WINBINDD_ERROR;
276 winbindd_store_uid_cache_entry(domain->name, state->request.data.uid,
277 &state->response.data.pw);
283 * set/get/endpwent functions
286 /* Rewind file pointer for ntdom passwd database */
288 enum winbindd_result winbindd_setpwent(struct winbindd_cli_state *state)
290 struct winbindd_domain *tmp;
292 DEBUG(3, ("[%5d]: setpwent\n", state->pid));
294 if (state == NULL) return WINBINDD_ERROR;
296 /* Check user has enabled this */
298 if (!lp_winbind_enum_users()) {
299 return WINBINDD_ERROR;
302 /* Free old static data if it exists */
304 if (state->getpwent_state != NULL) {
305 free_getent_state(state->getpwent_state);
306 state->getpwent_state = NULL;
309 /* Create sam pipes for each domain we know about */
311 for(tmp = domain_list; tmp != NULL; tmp = tmp->next) {
312 struct getent_state *domain_state;
314 /* Skip domains other than WINBINDD_DOMAIN environment variable */
316 if ((strcmp(state->request.domain, "") != 0) &&
317 !check_domain_env(state->request.domain, tmp->name)) {
321 /* Create a state record for this domain */
323 if ((domain_state = (struct getent_state *)
324 malloc(sizeof(struct getent_state))) == NULL) {
326 return WINBINDD_ERROR;
329 ZERO_STRUCTP(domain_state);
330 domain_state->domain = tmp;
332 /* Add to list of open domains */
334 DLIST_ADD(state->getpwent_state, domain_state)
340 /* Close file pointer to ntdom passwd database */
342 enum winbindd_result winbindd_endpwent(struct winbindd_cli_state *state)
344 DEBUG(3, ("[%5d]: endpwent\n", state->pid));
346 if (state == NULL) return WINBINDD_ERROR;
348 free_getent_state(state->getpwent_state);
349 state->getpwent_state = NULL;
354 /* Get partial list of domain users for a domain. We fill in the sam_entries,
355 and num_sam_entries fields with domain user information. The dispinfo_ndx
356 field is incremented to the index of the next user to fetch. Return True if
357 some users were returned, False otherwise. */
359 #define MAX_FETCH_SAM_ENTRIES 100
361 static BOOL get_sam_user_entries(struct getent_state *ent)
363 uint32 status, num_entries;
364 SAM_DISPINFO_1 info1;
365 SAM_DISPINFO_CTR ctr;
366 struct getpwent_user *name_list = NULL;
369 if (ent->got_all_sam_entries) {
376 ctr.sam.info1 = &info1;
379 /* Look in cache for entries, else get them direct */
381 if (winbindd_fetch_user_cache(ent->domain->name,
382 (struct getpwent_user **)
384 &ent->num_sam_entries)) {
389 /* For the moment we set the primary group for every user to be the
390 Domain Users group. There are serious problems with determining
391 the actual primary group for large domains. This should really
392 be made into a 'winbind force group' smb.conf parameter or
393 something like that. */
395 group_rid = DOMAIN_GROUP_RID_USERS;
397 if (!domain_handles_open(ent->domain)) {
398 return WINBINDD_ERROR;
401 /* Free any existing user info */
403 if (ent->sam_entries) {
404 free(ent->sam_entries);
405 ent->sam_entries = NULL;
406 ent->num_sam_entries = 0;
409 /* Call query_dispinfo to get a list of usernames and user rids */
416 status = winbindd_query_dispinfo(ent->domain,
417 &ent->dispinfo_ndx, 1,
421 name_list = Realloc(name_list,
422 sizeof(struct getpwent_user) *
423 (ent->num_sam_entries +
427 for (i = 0; i < num_entries; i++) {
429 /* Store account name and gecos */
432 name_list[ent->num_sam_entries + i].name,
433 &info1.str[i].uni_acct_name,
437 name_list[ent->num_sam_entries + i].gecos,
438 &info1.str[i].uni_full_name,
441 /* User and group ids */
443 name_list[ent->num_sam_entries + i].user_rid =
444 info1.sam[i].rid_user;
446 name_list[ent->num_sam_entries + i].
447 group_rid = group_rid;
450 ent->num_sam_entries += num_entries;
452 if (status != 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->name, 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 = (status != STATUS_MORE_ENTRIES);
471 return ent->num_sam_entries > 0;
474 /* Fetch next passwd entry from ntdom database */
476 #define MAX_GETPWENT_USERS 500
478 enum winbindd_result winbindd_getpwent(struct winbindd_cli_state *state)
480 struct getent_state *ent;
481 struct winbindd_pw *user_list;
482 int num_users, user_list_ndx = 0, i;
485 DEBUG(3, ("[%5d]: getpwent\n", state->pid));
487 if (state == NULL) return WINBINDD_ERROR;
489 /* Check user has enabled this */
491 if (!lp_winbind_enum_users()) {
492 return WINBINDD_ERROR;
495 /* Allocate space for returning a chunk of users */
497 num_users = MIN(MAX_GETPWENT_USERS, state->request.data.num_entries);
499 if ((state->response.extra_data =
500 malloc(num_users * sizeof(struct winbindd_pw))) == NULL) {
501 return WINBINDD_ERROR;
504 memset(state->response.extra_data, 0, num_users *
505 sizeof(struct winbindd_pw));
507 user_list = (struct winbindd_pw *)state->response.extra_data;
508 sep = lp_winbind_separator();
510 if (!(ent = state->getpwent_state)) {
511 return WINBINDD_ERROR;
514 /* Start sending back users */
516 for (i = 0; i < num_users; i++) {
517 struct getpwent_user *name_list = NULL;
518 fstring domain_user_name;
521 /* Do we need to fetch another chunk of users? */
523 if (ent->num_sam_entries == ent->sam_entry_index) {
525 while(ent && !get_sam_user_entries(ent)) {
526 struct getent_state *next_ent;
528 /* Free state information for this domain */
530 safe_free(ent->sam_entries);
531 ent->sam_entries = NULL;
533 next_ent = ent->next;
534 DLIST_REMOVE(state->getpwent_state, ent);
540 /* No more domains */
545 name_list = ent->sam_entries;
547 /* Skip machine accounts */
549 if (name_list[ent->sam_entry_index].
550 name[strlen(name_list[ent->sam_entry_index].name) - 1]
552 ent->sam_entry_index++;
556 /* Lookup user info */
558 slprintf(domain_user_name, sizeof(domain_user_name) - 1,
559 "%s%s%s", ent->domain->name, sep,
560 name_list[ent->sam_entry_index].name);
562 result = winbindd_fill_pwent(
565 name_list[ent->sam_entry_index].user_rid,
566 name_list[ent->sam_entry_index].group_rid,
567 name_list[ent->sam_entry_index].gecos,
568 &user_list[user_list_ndx]);
570 ent->sam_entry_index++;
572 /* Add user to return list */
577 state->response.data.num_entries++;
578 state->response.length +=
579 sizeof(struct winbindd_pw);
582 DEBUG(1, ("could not lookup domain user %s\n",
590 return (user_list_ndx > 0) ? WINBINDD_OK : WINBINDD_ERROR;
593 /* List domain users without mapping to unix ids */
595 enum winbindd_result winbindd_list_users(struct winbindd_cli_state *state)
597 struct winbindd_domain *domain;
598 SAM_DISPINFO_CTR ctr;
599 SAM_DISPINFO_1 info1;
600 uint32 num_entries = 0, total_entries = 0;
601 char *extra_data = NULL;
602 int extra_data_len = 0;
604 DEBUG(3, ("[%5d]: list users\n", state->pid));
606 /* Enumerate over trusted domains */
608 ctr.sam.info1 = &info1;
610 for (domain = domain_list; domain; domain = domain->next) {
611 uint32 status, start_ndx = 0;
613 /* Skip domains other than WINBINDD_DOMAIN environment
616 if ((strcmp(state->request.domain, "") != 0) &&
617 !check_domain_env(state->request.domain, domain->name)) {
621 if (!domain_handles_open(domain)) {
625 /* Query display info */
630 status = winbindd_query_dispinfo(domain, &start_ndx,
634 if (num_entries == 0) {
638 /* Allocate some memory for extra data */
640 total_entries += num_entries;
642 extra_data = Realloc(extra_data, sizeof(fstring) *
646 return WINBINDD_ERROR;
649 /* Pack user list into extra data fields */
651 for (i = 0; i < num_entries; i++) {
652 UNISTR2 *uni_acct_name;
653 fstring acct_name, name;
655 /* Convert unistring to ascii */
657 uni_acct_name = &ctr.sam.info1->str[i].
659 unistr2_to_ascii(acct_name, uni_acct_name,
660 sizeof(acct_name) - 1);
662 slprintf(name, sizeof(name) - 1, "%s%s%s",
663 domain->name, lp_winbind_separator(),
666 /* Append to extra data */
668 memcpy(&extra_data[extra_data_len], name,
670 extra_data_len += strlen(name);
672 extra_data[extra_data_len++] = ',';
674 } while (status == STATUS_MORE_ENTRIES);
677 /* Assign extra_data fields in response structure */
680 extra_data[extra_data_len - 1] = '\0';
681 state->response.extra_data = extra_data;
682 state->response.length += extra_data_len;
685 /* No domains responded but that's still OK so don't return an