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