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