xref: /illumos-gate/usr/src/lib/libc/port/gen/getpwnam_r.c (revision 5bbb4db2c3f208d12bf0fd11769728f9e5ba66a2)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License (the "License").
6  * You may not use this file except in compliance with the License.
7  *
8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9  * or http://www.opensolaris.org/os/licensing.
10  * See the License for the specific language governing permissions
11  * and limitations under the License.
12  *
13  * When distributing Covered Code, include this CDDL HEADER in each
14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15  * If applicable, add the following below this CDDL HEADER, with the
16  * fields enclosed by brackets "[]" replaced with your own identifying
17  * information: Portions Copyright [yyyy] [name of copyright owner]
18  *
19  * CDDL HEADER END
20  */
21 
22 /*
23  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
26 
27 #include "lint.h"
28 #include <sys/types.h>
29 #include <pwd.h>
30 #include <nss_dbdefs.h>
31 #include <stdio.h>
32 #include <synch.h>
33 #include <sys/param.h>
34 #include <string.h>
35 #include <stdlib.h>
36 #include <sys/mman.h>
37 #include <errno.h>
38 
39 int str2passwd(const char *, int, void *,
40 	char *, int);
41 
42 static DEFINE_NSS_DB_ROOT(db_root);
43 static DEFINE_NSS_GETENT(context);
44 
45 void
46 _nss_initf_passwd(nss_db_params_t *p)
47 {
48 	p->name	= NSS_DBNAM_PASSWD;
49 	p->default_config = NSS_DEFCONF_PASSWD;
50 }
51 
52 #include <getxby_door.h>
53 
54 struct passwd *
55 _uncached_getpwuid_r(uid_t uid, struct passwd *result, char *buffer,
56 	int buflen);
57 
58 struct passwd *
59 _uncached_getpwnam_r(const char *name, struct passwd *result, char *buffer,
60     int buflen);
61 
62 /*
63  * POSIX.1c Draft-6 version of the function getpwnam_r.
64  * It was implemented by Solaris 2.3.
65  */
66 struct passwd *
67 getpwnam_r(const char *name, struct passwd *result, char *buffer, int buflen)
68 {
69 	nss_XbyY_args_t arg;
70 
71 	if (name == (const char *)NULL) {
72 		errno = ERANGE;
73 		return (NULL);
74 	}
75 	NSS_XbyY_INIT(&arg, result, buffer, buflen, str2passwd);
76 	arg.key.name = name;
77 	(void) nss_search(&db_root, _nss_initf_passwd, NSS_DBOP_PASSWD_BYNAME,
78 	    &arg);
79 	return ((struct passwd *)NSS_XbyY_FINI(&arg));
80 }
81 
82 /*
83  * POSIX.1c Draft-6 version of the function getpwuid_r.
84  * It was implemented by Solaris 2.3.
85  */
86 struct passwd *
87 getpwuid_r(uid_t uid, struct passwd *result, char *buffer, int buflen)
88 {
89 	nss_XbyY_args_t arg;
90 
91 	NSS_XbyY_INIT(&arg, result, buffer, buflen, str2passwd);
92 	arg.key.uid = uid;
93 	(void) nss_search(&db_root, _nss_initf_passwd, NSS_DBOP_PASSWD_BYUID,
94 	    &arg);
95 	return ((struct passwd *)NSS_XbyY_FINI(&arg));
96 }
97 
98 
99 struct passwd *
100 _uncached_getpwuid_r(uid_t uid, struct passwd *result, char *buffer,
101 	int buflen)
102 {
103 	nss_XbyY_args_t arg;
104 
105 	NSS_XbyY_INIT(&arg, result, buffer, buflen, str2passwd);
106 	arg.key.uid = uid;
107 	(void) nss_search(&db_root, _nss_initf_passwd, NSS_DBOP_PASSWD_BYUID,
108 	    &arg);
109 	return ((struct passwd *)NSS_XbyY_FINI(&arg));
110 }
111 
112 
113 /*
114  * POSIX.1c standard version of the function getpwuid_r.
115  * User gets it via static getpwuid_r from the header file.
116  */
117 int
118 __posix_getpwuid_r(uid_t uid, struct passwd *pwd, char *buffer,
119     size_t bufsize, struct passwd **result)
120 {
121 	int nerrno = 0;
122 	int oerrno = errno;
123 
124 	errno = 0;
125 	if ((*result = getpwuid_r(uid, pwd, buffer, (uintptr_t)bufsize))
126 	    == NULL) {
127 			nerrno = errno;
128 	}
129 	errno = oerrno;
130 	return (nerrno);
131 }
132 
133 struct passwd *
134 _uncached_getpwnam_r(const char *name, struct passwd *result, char *buffer,
135 	int buflen)
136 {
137 	nss_XbyY_args_t arg;
138 
139 	NSS_XbyY_INIT(&arg, result, buffer, buflen, str2passwd);
140 	arg.key.name = name;
141 	(void) nss_search(&db_root, _nss_initf_passwd, NSS_DBOP_PASSWD_BYNAME,
142 	    &arg);
143 	return ((struct passwd *)NSS_XbyY_FINI(&arg));
144 }
145 
146 /*
147  * POSIX.1c standard version of the function getpwnam_r.
148  * User gets it via static getpwnam_r from the header file.
149  */
150 int
151 __posix_getpwnam_r(const char *name, struct passwd *pwd, char *buffer,
152     size_t bufsize, struct passwd **result)
153 {
154 	int nerrno = 0;
155 	int oerrno = errno;
156 
157 	errno = 0;
158 	if ((*result = getpwnam_r(name, pwd, buffer, (uintptr_t)bufsize))
159 	    == NULL) {
160 			nerrno = errno;
161 	}
162 	errno = oerrno;
163 	return (nerrno);
164 }
165 
166 void
167 setpwent(void)
168 {
169 	nss_setent(&db_root, _nss_initf_passwd, &context);
170 }
171 
172 void
173 endpwent(void)
174 {
175 	nss_endent(&db_root, _nss_initf_passwd, &context);
176 	nss_delete(&db_root);
177 }
178 
179 struct passwd *
180 getpwent_r(struct passwd *result, char *buffer, int buflen)
181 {
182 	nss_XbyY_args_t arg;
183 	char		*nam;
184 
185 	/* In getXXent_r(), protect the unsuspecting caller from +/- entries */
186 
187 	do {
188 		NSS_XbyY_INIT(&arg, result, buffer, buflen, str2passwd);
189 		/* No key to fill in */
190 		(void) nss_getent(&db_root, _nss_initf_passwd, &context, &arg);
191 	} while (arg.returnval != 0 &&
192 	    (nam = ((struct passwd *)arg.returnval)->pw_name) != 0 &&
193 	    (*nam == '+' || *nam == '-'));
194 
195 	return ((struct passwd *)NSS_XbyY_FINI(&arg));
196 }
197 
198 struct passwd *
199 fgetpwent_r(FILE *f, struct passwd *result, char *buffer, int buflen)
200 {
201 	extern void	_nss_XbyY_fgets(FILE *, nss_XbyY_args_t *);
202 	nss_XbyY_args_t	arg;
203 
204 	/* ... but in fgetXXent_r, the caller deserves any +/- entry he gets */
205 
206 	/* No key to fill in */
207 	NSS_XbyY_INIT(&arg, result, buffer, buflen, str2passwd);
208 	_nss_XbyY_fgets(f, &arg);
209 	return ((struct passwd *)NSS_XbyY_FINI(&arg));
210 }
211 
212 static char *
213 gettok(char **nextpp)
214 {
215 	char	*p = *nextpp;
216 	char	*q = p;
217 	char	c;
218 
219 	if (p == 0)
220 		return (0);
221 
222 	while ((c = *q) != '\0' && c != ':')
223 		q++;
224 
225 	if (c == '\0')
226 		*nextpp = 0;
227 	else {
228 		*q++ = '\0';
229 		*nextpp = q;
230 	}
231 	return (p);
232 }
233 
234 /*
235  * Return values: 0 = success, 1 = parse error, 2 = erange ...
236  * The structure pointer passed in is a structure in the caller's space
237  * wherein the field pointers would be set to areas in the buffer if
238  * need be. instring and buffer should be separate areas.
239  */
240 int
241 str2passwd(const char *instr, int lenstr, void *ent, char *buffer, int buflen)
242 {
243 	struct passwd	*passwd	= (struct passwd *)ent;
244 	char		*p, *next;
245 	int		black_magic;	/* "+" or "-" entry */
246 	ulong_t		tmp;
247 
248 	if (lenstr + 1 > buflen)
249 		return (NSS_STR_PARSE_ERANGE);
250 
251 	/*
252 	 * We copy the input string into the output buffer and
253 	 * operate on it in place.
254 	 */
255 	if (instr != buffer) {
256 		/* Overlapping buffer copies are OK */
257 		(void) memmove(buffer, instr, lenstr);
258 		buffer[lenstr] = '\0';
259 	}
260 
261 	/* quick exit do not entry fill if not needed */
262 	if (ent == (void *)NULL)
263 		return (NSS_STR_PARSE_SUCCESS);
264 
265 	next = buffer;
266 
267 	passwd->pw_name = p = gettok(&next);		/* username */
268 	if (*p == '\0') {
269 		/* Empty username;  not allowed */
270 		return (NSS_STR_PARSE_PARSE);
271 	}
272 	black_magic = (*p == '+' || *p == '-');
273 	if (black_magic) {
274 		passwd->pw_uid = UID_NOBODY;
275 		passwd->pw_gid = GID_NOBODY;
276 		/*
277 		 *  pwconv tests pw_passwd and pw_age == NULL
278 		 */
279 		passwd->pw_passwd  = "";
280 		passwd->pw_age	= "";
281 		/*
282 		 * the rest of the passwd entry is "optional"
283 		 */
284 		passwd->pw_comment = "";
285 		passwd->pw_gecos = "";
286 		passwd->pw_dir	= "";
287 		passwd->pw_shell = "";
288 	}
289 
290 	passwd->pw_passwd = p = gettok(&next);		/* password */
291 	if (p == 0) {
292 		if (black_magic)
293 			return (NSS_STR_PARSE_SUCCESS);
294 		else
295 			return (NSS_STR_PARSE_PARSE);
296 	}
297 	for (; *p != '\0';  p++) {			/* age */
298 		if (*p == ',') {
299 			*p++ = '\0';
300 			break;
301 		}
302 	}
303 	passwd->pw_age = p;
304 
305 	p = next;					/* uid */
306 	if (p == 0 || *p == '\0') {
307 		if (black_magic)
308 			return (NSS_STR_PARSE_SUCCESS);
309 		else
310 			return (NSS_STR_PARSE_PARSE);
311 	}
312 	if (!black_magic) {
313 		/*
314 		 * strtoul returns unsigned long which is
315 		 * 8 bytes on a 64-bit system. We don't want
316 		 * to assign it directly to passwd->pw_uid
317 		 * which is 4 bytes or else we will end up
318 		 * truncating the value.
319 		 */
320 		errno = 0;
321 		tmp = strtoul(p, &next, 10);
322 		if (next == p || errno != 0) {
323 			/* uid field should be nonempty */
324 			/* also check errno from strtoul */
325 			return (NSS_STR_PARSE_PARSE);
326 		}
327 		/*
328 		 * The old code (in 2.0 through 2.5) would check
329 		 * for the uid being negative, or being greater
330 		 * than 60001 (the rfs limit).  If it met either of
331 		 * these conditions, the uid was translated to 60001.
332 		 *
333 		 * Now we just check for -1 (UINT32_MAX); anything else
334 		 * is administrative policy
335 		 */
336 		if (tmp >= UINT32_MAX)
337 			passwd->pw_uid = UID_NOBODY;
338 		else
339 			passwd->pw_uid = (uid_t)tmp;
340 	}
341 	if (*next++ != ':') {
342 		if (black_magic)
343 			(void) gettok(&next);
344 		else
345 			return (NSS_STR_PARSE_PARSE);
346 	}
347 	p = next;					/* gid */
348 	if (p == 0 || *p == '\0') {
349 		if (black_magic)
350 			return (NSS_STR_PARSE_SUCCESS);
351 		else
352 			return (NSS_STR_PARSE_PARSE);
353 	}
354 	if (!black_magic) {
355 		errno = 0;
356 		tmp = strtoul(p, &next, 10);
357 		if (next == p || errno != 0) {
358 			/* gid field should be nonempty */
359 			/* also check errno from strtoul */
360 			return (NSS_STR_PARSE_PARSE);
361 		}
362 		/*
363 		 * gid should not be -1; anything else
364 		 * is administrative policy.
365 		 */
366 		if (tmp >= UINT32_MAX)
367 			passwd->pw_gid = GID_NOBODY;
368 		else
369 			passwd->pw_gid = (gid_t)tmp;
370 	}
371 	if (*next++ != ':') {
372 		if (black_magic)
373 			(void) gettok(&next);
374 		else
375 			return (NSS_STR_PARSE_PARSE);
376 	}
377 
378 	passwd->pw_gecos = passwd->pw_comment = p = gettok(&next);
379 	if (p == 0) {
380 		if (black_magic)
381 			return (NSS_STR_PARSE_SUCCESS);
382 		else
383 			return (NSS_STR_PARSE_PARSE);
384 	}
385 
386 	passwd->pw_dir = p = gettok(&next);
387 	if (p == 0) {
388 		if (black_magic)
389 			return (NSS_STR_PARSE_SUCCESS);
390 		else
391 			return (NSS_STR_PARSE_PARSE);
392 	}
393 
394 	passwd->pw_shell = p = gettok(&next);
395 	if (p == 0) {
396 		if (black_magic)
397 			return (NSS_STR_PARSE_SUCCESS);
398 		else
399 			return (NSS_STR_PARSE_PARSE);
400 	}
401 
402 	/* Better not be any more fields... */
403 	if (next == 0) {
404 		/* Successfully parsed and stored */
405 		return (NSS_STR_PARSE_SUCCESS);
406 	}
407 	return (NSS_STR_PARSE_PARSE);
408 }
409