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 name_list
= Realloc(name_list
,
418 sizeof(struct getpwent_user
) *
419 (ent
->num_sam_entries
+
423 for (i
= 0; i
< num_entries
; i
++) {
425 /* Store account name and gecos */
428 name_list
[ent
->num_sam_entries
+ i
].name
,
429 &info1
.str
[i
].uni_acct_name
,
433 name_list
[ent
->num_sam_entries
+ i
].gecos
,
434 &info1
.str
[i
].uni_full_name
,
437 /* User and group ids */
439 name_list
[ent
->num_sam_entries
+ i
].user_rid
=
440 info1
.sam
[i
].rid_user
;
442 name_list
[ent
->num_sam_entries
+ i
].
443 group_rid
= group_rid
;
446 ent
->num_sam_entries
+= num_entries
;
448 if (status
!= STATUS_MORE_ENTRIES
) {
452 } while (ent
->num_sam_entries
< MAX_FETCH_SAM_ENTRIES
);
455 /* Fill cache with received entries */
457 winbindd_store_user_cache(ent
->domain
->name
, ent
->sam_entries
,
458 ent
->num_sam_entries
);
461 /* Fill in remaining fields */
463 ent
->sam_entries
= name_list
;
464 ent
->sam_entry_index
= 0;
465 ent
->got_all_sam_entries
= (status
!= STATUS_MORE_ENTRIES
);
467 return ent
->num_sam_entries
> 0;
470 /* Fetch next passwd entry from ntdom database */
472 #define MAX_GETPWENT_USERS 500
474 enum winbindd_result
winbindd_getpwent(struct winbindd_cli_state
*state
)
476 struct getent_state
*ent
;
477 struct winbindd_pw
*user_list
;
478 int num_users
, user_list_ndx
= 0, i
;
481 DEBUG(3, ("[%5d]: getpwent\n", state
->pid
));
483 if (state
== NULL
) return WINBINDD_ERROR
;
485 /* Check user has enabled this */
487 if (!lp_winbind_enum_users()) {
488 return WINBINDD_ERROR
;
491 /* Allocate space for returning a chunk of users */
493 num_users
= MIN(MAX_GETPWENT_USERS
, state
->request
.data
.num_entries
);
495 if ((state
->response
.extra_data
=
496 malloc(num_users
* sizeof(struct winbindd_pw
))) == NULL
) {
497 return WINBINDD_ERROR
;
500 memset(state
->response
.extra_data
, 0, num_users
*
501 sizeof(struct winbindd_pw
));
503 user_list
= (struct winbindd_pw
*)state
->response
.extra_data
;
504 sep
= lp_winbind_separator();
506 if (!(ent
= state
->getpwent_state
)) {
507 return WINBINDD_ERROR
;
510 /* Start sending back users */
512 for (i
= 0; i
< num_users
; i
++) {
513 struct getpwent_user
*name_list
= NULL
;
514 fstring domain_user_name
;
517 /* Do we need to fetch another chunk of users? */
519 if (ent
->num_sam_entries
== ent
->sam_entry_index
) {
521 while(ent
&& !get_sam_user_entries(ent
)) {
522 struct getent_state
*next_ent
;
524 /* Free state information for this domain */
526 safe_free(ent
->sam_entries
);
527 ent
->sam_entries
= NULL
;
529 next_ent
= ent
->next
;
530 DLIST_REMOVE(state
->getpwent_state
, ent
);
536 /* No more domains */
541 name_list
= ent
->sam_entries
;
543 /* Skip machine accounts */
545 if (name_list
[ent
->sam_entry_index
].
546 name
[strlen(name_list
[ent
->sam_entry_index
].name
) - 1]
548 ent
->sam_entry_index
++;
552 /* Lookup user info */
554 slprintf(domain_user_name
, sizeof(domain_user_name
) - 1,
555 "%s%s%s", ent
->domain
->name
, sep
,
556 name_list
[ent
->sam_entry_index
].name
);
558 result
= winbindd_fill_pwent(
561 name_list
[ent
->sam_entry_index
].user_rid
,
562 name_list
[ent
->sam_entry_index
].group_rid
,
563 name_list
[ent
->sam_entry_index
].gecos
,
564 &user_list
[user_list_ndx
]);
566 ent
->sam_entry_index
++;
568 /* Add user to return list */
573 state
->response
.data
.num_entries
++;
574 state
->response
.length
+=
575 sizeof(struct winbindd_pw
);
578 DEBUG(1, ("could not lookup domain user %s\n",
586 return (user_list_ndx
> 0) ? WINBINDD_OK
: WINBINDD_ERROR
;
589 /* List domain users without mapping to unix ids */
591 enum winbindd_result
winbindd_list_users(struct winbindd_cli_state
*state
)
593 struct winbindd_domain
*domain
;
594 SAM_DISPINFO_CTR ctr
;
595 SAM_DISPINFO_1 info1
;
596 uint32 num_entries
= 0, total_entries
= 0;
597 char *extra_data
= NULL
;
598 int extra_data_len
= 0;
600 DEBUG(3, ("[%5d]: list users\n", state
->pid
));
602 /* Enumerate over trusted domains */
604 ctr
.sam
.info1
= &info1
;
606 for (domain
= domain_list
; domain
; domain
= domain
->next
) {
607 uint32 status
, start_ndx
= 0;
609 /* Skip domains other than WINBINDD_DOMAIN environment
612 if ((strcmp(state
->request
.domain
, "") != 0) &&
613 !check_domain_env(state
->request
.domain
, domain
->name
)) {
617 if (!domain_handles_open(domain
)) {
621 /* Query display info */
626 status
= winbindd_query_dispinfo(domain
, &start_ndx
,
630 if (num_entries
== 0) {
634 /* Allocate some memory for extra data */
636 total_entries
+= num_entries
;
638 extra_data
= Realloc(extra_data
, sizeof(fstring
) *
642 return WINBINDD_ERROR
;
645 /* Pack user list into extra data fields */
647 for (i
= 0; i
< num_entries
; i
++) {
648 UNISTR2
*uni_acct_name
;
649 fstring acct_name
, name
;
651 /* Convert unistring to ascii */
653 uni_acct_name
= &ctr
.sam
.info1
->str
[i
].
655 unistr2_to_ascii(acct_name
, uni_acct_name
,
656 sizeof(acct_name
) - 1);
658 slprintf(name
, sizeof(name
) - 1, "%s%s%s",
659 domain
->name
, lp_winbind_separator(),
662 /* Append to extra data */
664 memcpy(&extra_data
[extra_data_len
], name
,
666 extra_data_len
+= strlen(name
);
668 extra_data
[extra_data_len
++] = ',';
670 } while (status
== STATUS_MORE_ENTRIES
);
673 /* Assign extra_data fields in response structure */
676 extra_data
[extra_data_len
- 1] = '\0';
677 state
->response
.extra_data
= extra_data
;
678 state
->response
.length
+= extra_data_len
;
681 /* No domains responded but that's still OK so don't return an