deal with allocation size of 0 in prs_unistr when UNMARSHALLING
[Samba.git] / source / nsswitch / winbindd_user.c
blob9a73b0107b0a48a74c0c0f8b0cac36b135653cdd
1 /*
2 Unix SMB/Netbios implementation.
3 Version 2.0
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.
24 #include "winbindd.h"
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,
30 char *full_name)
32 pstring homedir;
33 fstring name_domain, name_user;
35 if (!pw || !name) {
36 return;
39 /* Fill in uid/gid */
41 pw->pw_uid = unix_uid;
42 pw->pw_gid = unix_gid;
44 /* Username */
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
73 can be returned. */
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;
79 DOM_SID user_sid;
80 fstring name_domain, name_user, name, gecos_name;
81 struct winbindd_domain *domain;
82 uid_t uid;
83 gid_t gid;
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
89 entire name. */
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)) {
106 return WINBINDD_OK;
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",
132 name_user));
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,
160 gecos_name);
162 winbindd_fill_user_cache_entry(name_domain, name_user,
163 &state->response.data.pw);
165 return WINBINDD_OK;
168 /* Return a password structure given a uid number */
170 enum winbindd_result winbindd_getpwnam_from_uid(struct winbindd_cli_state
171 *state)
173 DOM_SID user_sid;
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;
179 gid_t gid;
181 /* Get rid from uid */
182 if (!winbindd_idmap_get_rid_from_uid(state->request.data.uid, &user_rid,
183 &domain)) {
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)) {
192 return WINBINDD_OK;
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,
202 &name_type)) {
203 fstring temp;
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",
218 user_name));
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);
243 return WINBINDD_OK;
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)) {
274 continue;
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)
293 return WINBINDD_OK;
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;
305 return WINBINDD_OK;
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,
327 &ent->sam_entries,
328 &ent->num_sam_entries)) {
330 /* Fetch the user entries */
332 if (!domain_handles_open(ent->domain)) goto cleanup;
334 do {
335 status =
336 samr_enum_dom_users(
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++;
362 continue;
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) {
380 return result;
383 /* Try next user */
385 DEBUG(1, ("could not getpwnam_from_user for username %s\n",
386 domain_user_name));
389 /* We've exhausted all users for this pipe - close it down and
390 start on the next one. */
392 cleanup:
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);
407 free(old_ent);
411 /* Out of pipes so we're done */
413 return WINBINDD_ERROR;