2 Unix SMB/Netbios implementation.
5 Winbind daemon - user related function
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 void winbindd_fill_pwent(struct winbindd_pw
*pw
, char *name
,
29 uid_t unix_uid
, gid_t unix_gid
,
33 fstring name_domain
, name_user
;
41 pw
->pw_uid
= unix_uid
;
42 pw
->pw_gid
= unix_gid
;
46 safe_strcpy(pw
->pw_name
, name
, sizeof(pw
->pw_name
) - 1);
48 /* Full name (gecos) */
50 safe_strcpy(pw
->pw_gecos
, full_name
, sizeof(pw
->pw_gecos
) - 1);
52 /* Home directory and shell - use template config parameters. The
53 defaults are /tmp for the home directory and /bin/false for shell. */
55 parse_domain_user(name
, name_domain
, name_user
);
57 pstrcpy(homedir
, lp_template_homedir());
59 pstring_sub(homedir
, "%U", name_user
);
60 pstring_sub(homedir
, "%D", name_domain
);
62 safe_strcpy(pw
->pw_dir
, homedir
, sizeof(pw
->pw_dir
) - 1);
64 safe_strcpy(pw
->pw_shell
, lp_template_shell(), sizeof(pw
->pw_shell
) - 1);
66 /* Password - set to "x" as we can't generate anything useful here.
67 Authentication can be done using the pam_ntdom module. */
69 safe_strcpy(pw
->pw_passwd
, "x", sizeof(pw
->pw_passwd
) - 1);
72 /* Return a password structure from a username. Specify whether cached data
75 enum winbindd_result
winbindd_getpwnam_from_user(struct winbindd_cli_state
*state
)
77 uint32 name_type
, user_rid
, group_rid
;
78 SAM_USERINFO_CTR user_info
;
80 fstring name_domain
, name_user
, name
, gecos_name
;
81 struct winbindd_domain
*domain
;
85 /* Parse domain and username */
86 parse_domain_user(state
->request
.data
.username
, name_domain
, name_user
);
88 /* Reject names that don't have a domain - i.e name_domain contains the
91 if (strequal(name_domain
, "")) {
92 return WINBINDD_ERROR
;
95 /* Get info for the domain */
97 if ((domain
= find_domain_from_name(name_domain
)) == NULL
) {
98 DEBUG(0, ("could not find domain entry for domain %s\n", name_domain
));
99 return WINBINDD_ERROR
;
102 /* Check for cached user entry */
104 if (winbindd_fetch_user_cache_entry(name_domain
, name_user
,
105 &state
->response
.data
.pw
)) {
109 slprintf(name
,sizeof(name
),"%s\\%s", name_domain
, name_user
);
111 /* Get rid and name type from name */
112 /* the following costs 1 packet */
113 if (!winbindd_lookup_sid_by_name(domain
, name
, &user_sid
, &name_type
)) {
114 DEBUG(1, ("user '%s' does not exist\n", name_user
));
115 return WINBINDD_ERROR
;
118 if (name_type
!= SID_NAME_USER
) {
119 DEBUG(1, ("name '%s' is not a user name: %d\n", name_user
, name_type
));
120 return WINBINDD_ERROR
;
123 /* Get some user info. Split the user rid from the sid obtained from
124 the winbind_lookup_by_name() call and use it in a
125 winbind_lookup_userinfo() */
127 sid_split_rid(&user_sid
, &user_rid
);
129 /* the following costs 3 packets */
130 if (!winbindd_lookup_userinfo(domain
, user_rid
, &user_info
)) {
131 DEBUG(1, ("pwnam_from_user(): error getting user info for user '%s'\n",
133 return WINBINDD_ERROR
;
136 group_rid
= user_info
.info
.id21
->group_rid
;
137 unistr2_to_ascii(gecos_name
, &user_info
.info
.id21
->uni_full_name
,
138 sizeof(gecos_name
) - 1);
140 free_samr_userinfo_ctr(&user_info
);
142 /* Resolve the uid number */
144 if (!winbindd_idmap_get_uid_from_rid(domain
->name
, user_rid
, &uid
)) {
145 DEBUG(1, ("error getting user id for user %s\n", name_user
));
146 return WINBINDD_ERROR
;
149 /* Resolve the gid number */
151 if (!winbindd_idmap_get_gid_from_rid(domain
->name
, group_rid
, &gid
)) {
152 DEBUG(1, ("error getting group id for user %s\n", name_user
));
153 return WINBINDD_ERROR
;
156 /* Now take all this information and fill in a passwd structure */
158 winbindd_fill_pwent(&state
->response
.data
.pw
,
159 state
->request
.data
.username
, uid
, gid
,
162 winbindd_fill_user_cache_entry(name_domain
, name_user
,
163 &state
->response
.data
.pw
);
168 /* Return a password structure given a uid number */
170 enum winbindd_result
winbindd_getpwnam_from_uid(struct winbindd_cli_state
174 struct winbindd_domain
*domain
;
175 uint32 user_rid
, group_rid
;
176 fstring user_name
, gecos_name
;
177 enum SID_NAME_USE name_type
;
178 SAM_USERINFO_CTR user_info
;
181 /* Get rid from uid */
182 if (!winbindd_idmap_get_rid_from_uid(state
->request
.data
.uid
, &user_rid
,
184 DEBUG(1, ("Could not convert uid %d to rid\n",
185 state
->request
.data
.uid
));
186 return WINBINDD_ERROR
;
189 /* Check for cached uid entry */
190 if (winbindd_fetch_uid_cache_entry(domain
->name
, state
->request
.data
.uid
,
191 &state
->response
.data
.pw
)) {
196 /* Get name and name type from rid */
198 sid_copy(&user_sid
, &domain
->sid
);
199 sid_append_rid(&user_sid
, user_rid
);
201 if (!winbindd_lookup_name_by_sid(domain
, &user_sid
, user_name
,
205 sid_to_string(temp
, &user_sid
);
206 DEBUG(1, ("Could not lookup sid %s\n", temp
));
207 return WINBINDD_ERROR
;
210 if (strcmp("\\", lp_winbind_separator())) {
211 string_sub(user_name
, "\\", lp_winbind_separator(), sizeof(fstring
));
214 /* Get some user info */
216 if (!winbindd_lookup_userinfo(domain
, user_rid
, &user_info
)) {
217 DEBUG(1, ("pwnam_from_uid(): error getting user info for user '%s'\n",
219 return WINBINDD_ERROR
;
222 group_rid
= user_info
.info
.id21
->group_rid
;
223 unistr2_to_ascii(gecos_name
, &user_info
.info
.id21
->uni_full_name
,
224 sizeof(gecos_name
) - 1);
226 free_samr_userinfo_ctr(&user_info
);
228 /* Resolve gid number */
230 if (!winbindd_idmap_get_gid_from_rid(domain
->name
, group_rid
, &gid
)) {
231 DEBUG(1, ("error getting group id for user %s\n", user_name
));
232 return WINBINDD_ERROR
;
235 /* Fill in password structure */
237 winbindd_fill_pwent(&state
->response
.data
.pw
, user_name
,
238 state
->request
.data
.uid
, gid
, gecos_name
);
240 winbindd_fill_uid_cache_entry(domain
->name
, state
->request
.data
.uid
,
241 &state
->response
.data
.pw
);
247 * set/get/endpwent functions
250 /* Rewind file pointer for ntdom passwd database */
252 enum winbindd_result
winbindd_setpwent(struct winbindd_cli_state
*state
)
254 struct winbindd_domain
*tmp
;
256 if (state
== NULL
) return WINBINDD_ERROR
;
258 /* Free old static data if it exists */
260 if (state
->getpwent_state
!= NULL
) {
261 free_getent_state(state
->getpwent_state
);
262 state
->getpwent_state
= NULL
;
265 /* Create sam pipes for each domain we know about */
267 for(tmp
= domain_list
; tmp
!= NULL
; tmp
= tmp
->next
) {
268 struct getent_state
*domain_state
;
270 /* Skip domains other than WINBINDD_DOMAIN environment variable */
272 if ((strcmp(state
->request
.domain
, "") != 0) &&
273 (strcmp(state
->request
.domain
, tmp
->name
) != 0)) {
277 /* Create a state record for this domain */
279 if ((domain_state
= (struct getent_state
*)
280 malloc(sizeof(struct getent_state
))) == NULL
) {
282 return WINBINDD_ERROR
;
285 ZERO_STRUCTP(domain_state
);
286 domain_state
->domain
= tmp
;
288 /* Add to list of open domains */
290 DLIST_ADD(state
->getpwent_state
, domain_state
)
296 /* Close file pointer to ntdom passwd database */
298 enum winbindd_result
winbindd_endpwent(struct winbindd_cli_state
*state
)
300 if (state
== NULL
) return WINBINDD_ERROR
;
302 free_getent_state(state
->getpwent_state
);
303 state
->getpwent_state
= NULL
;
308 /* Fetch next passwd entry from ntdom database */
310 enum winbindd_result
winbindd_getpwent(struct winbindd_cli_state
*state
)
312 if (state
== NULL
) return WINBINDD_ERROR
;
314 /* Process the current head of the getent_state list */
316 while(state
->getpwent_state
!= NULL
) {
317 struct getent_state
*ent
= state
->getpwent_state
;
319 /* Get list of user entries for this pipe */
321 if (!ent
->got_sam_entries
) {
322 uint32 status
, start_ndx
= 0;
324 /* Look in cache for entries, else get them direct */
326 if (!winbindd_fetch_user_cache(ent
->domain
->name
,
328 &ent
->num_sam_entries
)) {
330 /* Fetch the user entries */
332 if (!domain_handles_open(ent
->domain
)) goto cleanup
;
337 &ent
->domain
->sam_dom_handle
, &start_ndx
, 0, 0,
338 0x10000, &ent
->sam_entries
, &ent
->num_sam_entries
);
339 } while (status
== STATUS_MORE_ENTRIES
);
341 /* Fill cache with received entries */
343 winbindd_fill_user_cache(ent
->domain
->name
, ent
->sam_entries
,
344 ent
->num_sam_entries
);
347 ent
->got_sam_entries
= True
;
350 /* Send back a user */
352 while (ent
->sam_entry_index
< ent
->num_sam_entries
) {
353 enum winbindd_result result
;
354 fstring domain_user_name
;
355 char *user_name
= (ent
->sam_entries
)
356 [ent
->sam_entry_index
].acct_name
;
358 /* Don't bother with machine accounts */
360 if (user_name
[strlen(user_name
) - 1] == '$') {
361 ent
->sam_entry_index
++;
365 /* Prepend domain to name */
367 slprintf(domain_user_name
, sizeof(domain_user_name
),
368 "%s%s%s", ent
->domain
->name
, lp_winbind_separator(), user_name
);
370 /* Get passwd entry from user name */
372 fstrcpy(state
->request
.data
.username
, domain_user_name
);
373 result
= winbindd_getpwnam_from_user(state
);
375 ent
->sam_entry_index
++;
377 /* Return if user lookup worked */
379 if (result
== WINBINDD_OK
) {
385 DEBUG(1, ("could not getpwnam_from_user for username %s\n",
389 /* We've exhausted all users for this pipe - close it down and
390 start on the next one. */
394 /* Free mallocated memory for sam entries. The data stored here
395 may have been allocated from the cache. */
397 if (ent
->sam_entries
!= NULL
) free(ent
->sam_entries
);
398 ent
->sam_entries
= NULL
;
400 /* Free state information for this domain */
403 struct getent_state
*old_ent
;
405 old_ent
= state
->getpwent_state
;
406 DLIST_REMOVE(state
->getpwent_state
, state
->getpwent_state
);
411 /* Out of pipes so we're done */
413 return WINBINDD_ERROR
;