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 /* Now take all this information and fill in a passwd structure */
172 if (!winbindd_fill_pwent(domain->name, state->request.data.username,
173 user_rid, group_rid, gecos_name,
174 &state->response.data.pw)) {
175 return WINBINDD_ERROR;
178 winbindd_store_user_cache_entry(name_domain, name_user,
179 &state->response.data.pw);
184 /* Return a password structure given a uid number */
186 enum winbindd_result winbindd_getpwnam_from_uid(struct winbindd_cli_state
190 struct winbindd_domain *domain;
191 uint32 user_rid, group_rid;
192 fstring user_name, gecos_name;
193 enum SID_NAME_USE name_type;
194 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;
204 DEBUG(3, ("[%5d]: getpwuid %d\n", state->pid,
205 state->request.data.uid));
207 /* Get rid from uid */
209 if (!winbindd_idmap_get_rid_from_uid(state->request.data.uid,
210 &user_rid, &domain)) {
211 DEBUG(1, ("Could not convert uid %d to rid\n",
212 state->request.data.uid));
213 return WINBINDD_ERROR;
216 if (!domain_handles_open(domain)) {
217 return WINBINDD_ERROR;
220 /* Check for cached uid entry */
222 if (winbindd_fetch_uid_cache_entry(domain->name,
223 state->request.data.uid,
224 &state->response.data.pw)) {
228 /* Get name and name type from rid */
230 sid_copy(&user_sid, &domain->sid);
231 sid_append_rid(&user_sid, user_rid);
233 if (!winbindd_lookup_name_by_sid(&user_sid, user_name, &name_type)) {
236 sid_to_string(temp, &user_sid);
237 DEBUG(1, ("Could not lookup sid %s\n", temp));
238 return WINBINDD_ERROR;
241 if (strcmp("\\", lp_winbind_separator())) {
242 string_sub(user_name, "\\", lp_winbind_separator(),
246 /* Get some user info */
248 if (!winbindd_lookup_userinfo(domain, user_rid, &user_info)) {
249 DEBUG(1, ("pwnam_from_uid(): error getting user info for "
250 "user '%s'\n", user_name));
251 return WINBINDD_ERROR;
254 group_rid = user_info->info.id21->group_rid;
255 unistr2_to_ascii(gecos_name, &user_info->info.id21->uni_full_name,
256 sizeof(gecos_name) - 1);
258 /* Resolve gid number */
260 if (!winbindd_idmap_get_gid_from_rid(domain->name, group_rid, &gid)) {
261 DEBUG(1, ("error getting group id for user %s\n", user_name));
262 return WINBINDD_ERROR;
265 /* Fill in password structure */
267 if (!winbindd_fill_pwent(domain->name, user_name, user_rid, group_rid,
268 gecos_name, &state->response.data.pw)) {
269 return WINBINDD_ERROR;
272 winbindd_store_uid_cache_entry(domain->name, state->request.data.uid,
273 &state->response.data.pw);
279 * set/get/endpwent functions
282 /* Rewind file pointer for ntdom passwd database */
284 enum winbindd_result winbindd_setpwent(struct winbindd_cli_state *state)
286 struct winbindd_domain *tmp;
288 DEBUG(3, ("[%5d]: setpwent\n", state->pid));
290 if (state == NULL) return WINBINDD_ERROR;
292 /* Check user has enabled this */
294 if (!lp_winbind_enum_users()) {
295 return WINBINDD_ERROR;
298 /* Free old static data if it exists */
300 if (state->getpwent_state != NULL) {
301 free_getent_state(state->getpwent_state);
302 state->getpwent_state = NULL;
305 /* Create sam pipes for each domain we know about */
307 for(tmp = domain_list; tmp != NULL; tmp = tmp->next) {
308 struct getent_state *domain_state;
310 /* Skip domains other than WINBINDD_DOMAIN environment variable */
312 if ((strcmp(state->request.domain, "") != 0) &&
313 !check_domain_env(state->request.domain, tmp->name)) {
317 /* Create a state record for this domain */
319 if ((domain_state = (struct getent_state *)
320 malloc(sizeof(struct getent_state))) == NULL) {
322 return WINBINDD_ERROR;
325 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 if (state == NULL) return WINBINDD_ERROR;
344 free_getent_state(state->getpwent_state);
345 state->getpwent_state = NULL;
350 /* Get partial list of domain users for a domain. We fill in the sam_entries,
351 and num_sam_entries fields with domain user information. The dispinfo_ndx
352 field is incremented to the index of the next user to fetch. Return True if
353 some users were returned, False otherwise. */
355 #define MAX_FETCH_SAM_ENTRIES 100
357 static BOOL get_sam_user_entries(struct getent_state *ent)
359 uint32 status, num_entries;
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 if (ent->sam_entries) {
400 free(ent->sam_entries);
401 ent->sam_entries = NULL;
402 ent->num_sam_entries = 0;
405 /* Call query_dispinfo to get a list of usernames and user rids */
412 status = winbindd_query_dispinfo(ent->domain,
413 &ent->dispinfo_ndx, 1,
417 struct getpwent_user *tnl;
419 tnl = (struct getpwent_user *)Realloc(name_list,
420 sizeof(struct getpwent_user) *
421 (ent->num_sam_entries +
425 DEBUG(0,("get_sam_user_entries: Realloc failed.\n"));
428 return WINBINDD_ERROR;
433 for (i = 0; i < num_entries; i++) {
435 /* Store account name and gecos */
438 name_list[ent->num_sam_entries + i].name,
439 &info1.str[i].uni_acct_name,
443 name_list[ent->num_sam_entries + i].gecos,
444 &info1.str[i].uni_full_name,
447 /* User and group ids */
449 name_list[ent->num_sam_entries + i].user_rid =
450 info1.sam[i].rid_user;
452 name_list[ent->num_sam_entries + i].
453 group_rid = group_rid;
456 ent->num_sam_entries += num_entries;
458 if (status != STATUS_MORE_ENTRIES) {
462 } while (ent->num_sam_entries < MAX_FETCH_SAM_ENTRIES);
465 /* Fill cache with received entries */
467 winbindd_store_user_cache(ent->domain->name, ent->sam_entries,
468 ent->num_sam_entries);
471 /* Fill in remaining fields */
473 ent->sam_entries = name_list;
474 ent->sam_entry_index = 0;
475 ent->got_all_sam_entries = (status != STATUS_MORE_ENTRIES);
477 return ent->num_sam_entries > 0;
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 if (state == NULL) return WINBINDD_ERROR;
495 /* Check user has enabled this */
497 if (!lp_winbind_enum_users()) {
498 return WINBINDD_ERROR;
501 /* Allocate space for returning a chunk of users */
503 num_users = MIN(MAX_GETPWENT_USERS, state->request.data.num_entries);
505 if ((state->response.extra_data =
506 malloc(num_users * sizeof(struct winbindd_pw))) == NULL) {
507 return WINBINDD_ERROR;
510 memset(state->response.extra_data, 0, num_users *
511 sizeof(struct winbindd_pw));
513 user_list = (struct winbindd_pw *)state->response.extra_data;
514 sep = lp_winbind_separator();
516 if (!(ent = state->getpwent_state)) {
517 return WINBINDD_ERROR;
520 /* Start sending back users */
522 for (i = 0; i < num_users; i++) {
523 struct getpwent_user *name_list = NULL;
524 fstring domain_user_name;
527 /* Do we need to fetch another chunk of users? */
529 if (ent->num_sam_entries == ent->sam_entry_index) {
531 while(ent && !get_sam_user_entries(ent)) {
532 struct getent_state *next_ent;
534 /* Free state information for this domain */
536 safe_free(ent->sam_entries);
537 ent->sam_entries = NULL;
539 next_ent = ent->next;
540 DLIST_REMOVE(state->getpwent_state, ent);
546 /* No more domains */
551 name_list = ent->sam_entries;
553 /* Skip machine accounts */
555 if (name_list[ent->sam_entry_index].
556 name[strlen(name_list[ent->sam_entry_index].name) - 1]
558 ent->sam_entry_index++;
562 /* Lookup user info */
564 slprintf(domain_user_name, sizeof(domain_user_name) - 1,
565 "%s%s%s", ent->domain->name, sep,
566 name_list[ent->sam_entry_index].name);
568 result = winbindd_fill_pwent(
571 name_list[ent->sam_entry_index].user_rid,
572 name_list[ent->sam_entry_index].group_rid,
573 name_list[ent->sam_entry_index].gecos,
574 &user_list[user_list_ndx]);
576 ent->sam_entry_index++;
578 /* Add user to return list */
583 state->response.data.num_entries++;
584 state->response.length +=
585 sizeof(struct winbindd_pw);
588 DEBUG(1, ("could not lookup domain user %s\n",
596 return (user_list_ndx > 0) ? WINBINDD_OK : WINBINDD_ERROR;
599 /* List domain users without mapping to unix ids */
601 enum winbindd_result winbindd_list_users(struct winbindd_cli_state *state)
603 struct winbindd_domain *domain;
604 SAM_DISPINFO_CTR ctr;
605 SAM_DISPINFO_1 info1;
606 uint32 num_entries = 0, total_entries = 0;
607 char *ted, *extra_data = NULL;
608 int extra_data_len = 0;
610 DEBUG(3, ("[%5d]: list users\n", state->pid));
612 /* Enumerate over trusted domains */
614 ctr.sam.info1 = &info1;
616 for (domain = domain_list; domain; domain = domain->next) {
617 uint32 status, start_ndx = 0;
619 /* Skip domains other than WINBINDD_DOMAIN environment
622 if ((strcmp(state->request.domain, "") != 0) &&
623 !check_domain_env(state->request.domain, domain->name)) {
627 if (!domain_handles_open(domain)) {
631 /* Query display info */
636 status = winbindd_query_dispinfo(domain, &start_ndx,
640 if (num_entries == 0) {
644 /* Allocate some memory for extra data */
646 total_entries += num_entries;
648 ted = Realloc(extra_data, sizeof(fstring) *
652 DEBUG(0,("winbindd_list_users: failed to enlarge buffer!\n"));
653 if (extra_data) free(extra_data);
654 return WINBINDD_ERROR;
656 else extra_data = ted;
658 /* Pack user list into extra data fields */
660 for (i = 0; i < num_entries; i++) {
661 UNISTR2 *uni_acct_name;
662 fstring acct_name, name;
664 /* Convert unistring to ascii */
666 uni_acct_name = &ctr.sam.info1->str[i].
668 unistr2_to_ascii(acct_name, uni_acct_name,
669 sizeof(acct_name) - 1);
671 slprintf(name, sizeof(name) - 1, "%s%s%s",
672 domain->name, lp_winbind_separator(),
675 /* Append to extra data */
677 memcpy(&extra_data[extra_data_len], name,
679 extra_data_len += strlen(name);
681 extra_data[extra_data_len++] = ',';
683 } while (status == STATUS_MORE_ENTRIES);
686 /* Assign extra_data fields in response structure */
689 extra_data[extra_data_len - 1] = '\0';
690 state->response.extra_data = extra_data;
691 state->response.length += extra_data_len;
694 /* No domains responded but that's still OK so don't return an