xref: /illumos-gate/usr/src/lib/libc/port/gen/getpwnam_r.c (revision a5f69788de7ac07553de47f7fec8c05a9a94c105)
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, Version 1.0 only
6  * (the "License").  You may not use this file except in compliance
7  * with the License.
8  *
9  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10  * or http://www.opensolaris.org/os/licensing.
11  * See the License for the specific language governing permissions
12  * and limitations under the License.
13  *
14  * When distributing Covered Code, include this CDDL HEADER in each
15  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16  * If applicable, add the following below this CDDL HEADER, with the
17  * fields enclosed by brackets "[]" replaced with your own identifying
18  * information: Portions Copyright [yyyy] [name of copyright owner]
19  *
20  * CDDL HEADER END
21  */
22 /*
23  * Copyright 2004 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
26 
27 #pragma ident	"%Z%%M%	%I%	%E% SMI"
28 
29 #pragma weak endpwent = _endpwent
30 #pragma weak setpwent = _setpwent
31 #pragma weak getpwnam_r = _getpwnam_r
32 #pragma weak getpwuid_r = _getpwuid_r
33 #pragma weak getpwent_r = _getpwent_r
34 #pragma weak fgetpwent_r = _fgetpwent_r
35 
36 #include "synonyms.h"
37 #include <sys/types.h>
38 #include <pwd.h>
39 #include <nss_dbdefs.h>
40 #include <stdio.h>
41 #include <synch.h>
42 #include <sys/param.h>
43 #include <string.h>
44 #include <stdlib.h>
45 #include <sys/mman.h>
46 
47 int str2passwd(const char *, int, void *,
48 	char *, int);
49 
50 static DEFINE_NSS_DB_ROOT(db_root);
51 static DEFINE_NSS_GETENT(context);
52 
53 void
54 _nss_initf_passwd(nss_db_params_t *p)
55 {
56 	p->name	= NSS_DBNAM_PASSWD;
57 	p->default_config = NSS_DEFCONF_PASSWD;
58 }
59 
60 #include <getxby_door.h>
61 
62 struct passwd *
63 _uncached_getpwuid_r(uid_t uid, struct passwd *result, char *buffer,
64 	int buflen);
65 
66 struct passwd *
67 _uncached_getpwnam_r(const char *name, struct passwd *result, char *buffer,
68     int buflen);
69 
70 static struct passwd *
71 process_getpw(struct passwd *result, char *buffer, int buflen,
72 	nsc_data_t *sptr, int ndata);
73 
74 /* ARGSUSED4 */
75 static struct passwd *
76 process_getpw(struct passwd *result, char *buffer, int buflen,
77 	nsc_data_t *sptr, int ndata)
78 {
79 
80 	char *fixed;
81 #ifdef	_LP64
82 	struct passwd	pass64;
83 #endif
84 
85 #ifdef	_LP64
86 	fixed = (char *)(((uintptr_t)buffer + 7) & ~7);
87 #else
88 	fixed = (char *)(((uintptr_t)buffer + 3) & ~3);
89 #endif
90 	buflen -= fixed - buffer;
91 	buffer = fixed;
92 
93 	if (sptr->nsc_ret.nsc_return_code != SUCCESS)
94 		return (NULL);
95 
96 #ifdef	_LP64
97 	if (sptr->nsc_ret.nsc_bufferbytesused - (int)sizeof (passwd32_t)
98 	    > buflen) {
99 #else
100 	if (sptr->nsc_ret.nsc_bufferbytesused - (int)sizeof (struct passwd)
101 	    > buflen) {
102 #endif
103 		errno = ERANGE;
104 		return (NULL);
105 	}
106 
107 #ifdef	_LP64
108 
109 	(void) memcpy(buffer,
110 	    (sptr->nsc_ret.nsc_u.buff + sizeof (passwd32_t)),
111 	    (sptr->nsc_ret.nsc_bufferbytesused - sizeof (passwd32_t)));
112 
113 	pass64.pw_name = (char *)(sptr->nsc_ret.nsc_u.pwd.pw_name +
114 				(uintptr_t)buffer);
115 	pass64.pw_passwd = (char *)(sptr->nsc_ret.nsc_u.pwd.pw_passwd +
116 				(uintptr_t)buffer);
117 	pass64.pw_uid = sptr->nsc_ret.nsc_u.pwd.pw_uid;
118 	pass64.pw_gid = sptr->nsc_ret.nsc_u.pwd.pw_gid;
119 	pass64.pw_age = (char *)(sptr->nsc_ret.nsc_u.pwd.pw_age +
120 				(uintptr_t)buffer);
121 	pass64.pw_comment = (char *)(sptr->nsc_ret.nsc_u.pwd.pw_comment +
122 				(uintptr_t)buffer);
123 	pass64.pw_gecos = (char *)(sptr->nsc_ret.nsc_u.pwd.pw_gecos +
124 				(uintptr_t)buffer);
125 	pass64.pw_dir = (char *)(sptr->nsc_ret.nsc_u.pwd.pw_dir +
126 				(uintptr_t)buffer);
127 	pass64.pw_shell = (char *)(sptr->nsc_ret.nsc_u.pwd.pw_shell +
128 				(uintptr_t)buffer);
129 
130 	*result = pass64;
131 #else
132 	sptr->nsc_ret.nsc_u.pwd.pw_name += (uintptr_t)buffer;
133 	sptr->nsc_ret.nsc_u.pwd.pw_passwd += (uintptr_t)buffer;
134 	sptr->nsc_ret.nsc_u.pwd.pw_age += (uintptr_t)buffer;
135 	sptr->nsc_ret.nsc_u.pwd.pw_comment += (uintptr_t)buffer;
136 	sptr->nsc_ret.nsc_u.pwd.pw_gecos += (uintptr_t)buffer;
137 	sptr->nsc_ret.nsc_u.pwd.pw_dir += (uintptr_t)buffer;
138 	sptr->nsc_ret.nsc_u.pwd.pw_shell += (uintptr_t)buffer;
139 
140 	*result = sptr->nsc_ret.nsc_u.pwd;
141 
142 	(void) memcpy(buffer,
143 	    (sptr->nsc_ret.nsc_u.buff + sizeof (struct passwd)),
144 	    (sptr->nsc_ret.nsc_bufferbytesused - sizeof (struct passwd)));
145 #endif
146 
147 	return (result);
148 }
149 
150 /*
151  * POSIX.1c Draft-6 version of the function getpwnam_r.
152  * It was implemented by Solaris 2.3.
153  */
154 struct passwd *
155 _getpwnam_r(const char *name, struct passwd *result, char *buffer, int buflen)
156 {
157 	/*
158 	 * allocate data on the stack for passwd information
159 	 */
160 	union {
161 		nsc_data_t 	s_d;
162 		char		s_b[1024];
163 	} space;
164 	nsc_data_t	*sptr;
165 	int		ndata;
166 	int		adata;
167 	struct passwd	*resptr = NULL;
168 
169 	if ((name == (const char *)NULL) ||
170 	    (strlen(name) >= (sizeof (space) - sizeof (nsc_data_t)))) {
171 		errno = ERANGE;
172 		return ((struct passwd *)NULL);
173 	}
174 	ndata = sizeof (space);
175 	adata = strlen(name) + sizeof (nsc_call_t) + 1;
176 	space.s_d.nsc_call.nsc_callnumber = GETPWNAM;
177 	(void) strcpy(space.s_d.nsc_call.nsc_u.name, name);
178 	sptr = &space.s_d;
179 
180 	switch (_nsc_trydoorcall(&sptr, &ndata, &adata)) {
181 	case SUCCESS:	/* positive cache hit */
182 		break;
183 	case NOTFOUND:	/* negative cache hit */
184 		return (NULL);
185 	default:
186 		return ((struct passwd *)_uncached_getpwnam_r(name, result,
187 		    buffer, buflen));
188 	}
189 	resptr = process_getpw(result, buffer, buflen, sptr, ndata);
190 
191 	/*
192 	 * check if doors reallocated the memory underneath us
193 	 * if they did munmap it or suffer a memory leak
194 	 */
195 	if (sptr != &space.s_d)
196 		munmap((void *)sptr, ndata);
197 
198 	return (resptr);
199 }
200 
201 /*
202  * POSIX.1c Draft-6 version of the function getpwuid_r.
203  * It was implemented by Solaris 2.3.
204  */
205 struct passwd *
206 _getpwuid_r(uid_t uid, struct passwd *result, char *buffer, int buflen)
207 {
208 	union {
209 		nsc_data_t	s_d;
210 		char		s_b[1024];
211 	} space;
212 	nsc_data_t	*sptr;
213 	int		ndata;
214 	int		adata;
215 	struct passwd	*resptr = NULL;
216 
217 	ndata = sizeof (space);
218 	adata = sizeof (nsc_call_t) + 1;
219 	space.s_d.nsc_call.nsc_callnumber = GETPWUID;
220 	space.s_d.nsc_call.nsc_u.uid = uid;
221 	sptr = &space.s_d;
222 
223 	switch (_nsc_trydoorcall(&sptr, &ndata, &adata)) {
224 	case SUCCESS:	/* positive cache hit */
225 		break;
226 	case NOTFOUND:	/* negative cache hit */
227 		return (NULL);
228 	default:
229 		return ((struct passwd *)_uncached_getpwuid_r(uid, result,
230 		    buffer, buflen));
231 	}
232 	resptr = process_getpw(result, buffer, buflen, sptr, ndata);
233 
234 	/*
235 	 * check if doors reallocated the memory underneath us
236 	 * if they did munmap it or suffer a memory leak
237 	 */
238 	if (sptr != &space.s_d)
239 		munmap((void *)sptr, ndata);
240 
241 	return (resptr);
242 }
243 
244 
245 struct passwd *
246 _uncached_getpwuid_r(uid_t uid, struct passwd *result, char *buffer,
247 	int buflen)
248 {
249 	nss_XbyY_args_t arg;
250 
251 	NSS_XbyY_INIT(&arg, result, buffer, buflen, str2passwd);
252 	arg.key.uid = uid;
253 	(void) nss_search(&db_root, _nss_initf_passwd, NSS_DBOP_PASSWD_BYUID,
254 				&arg);
255 	return ((struct passwd *)NSS_XbyY_FINI(&arg));
256 }
257 
258 
259 /*
260  * POSIX.1c standard version of the function getpwuid_r.
261  * User gets it via static getpwuid_r from the header file.
262  */
263 int
264 __posix_getpwuid_r(uid_t uid, struct passwd *pwd, char *buffer,
265     size_t bufsize, struct passwd **result)
266 {
267 	int nerrno = 0;
268 	int oerrno = errno;
269 
270 	errno = 0;
271 	if ((*result = _getpwuid_r(uid, pwd, buffer, (uintptr_t)bufsize))
272 		== NULL) {
273 			nerrno = errno;
274 	}
275 	errno = oerrno;
276 	return (nerrno);
277 }
278 
279 struct passwd *
280 _uncached_getpwnam_r(const char *name, struct passwd *result, char *buffer,
281 	int buflen)
282 {
283 	nss_XbyY_args_t arg;
284 
285 	NSS_XbyY_INIT(&arg, result, buffer, buflen, str2passwd);
286 	arg.key.name = name;
287 	(void) nss_search(&db_root, _nss_initf_passwd, NSS_DBOP_PASSWD_BYNAME,
288 				&arg);
289 	return ((struct passwd *)NSS_XbyY_FINI(&arg));
290 }
291 
292 /*
293  * POSIX.1c standard version of the function getpwnam_r.
294  * User gets it via static getpwnam_r from the header file.
295  */
296 int
297 __posix_getpwnam_r(const char *name, struct passwd *pwd, char *buffer,
298     size_t bufsize, struct passwd **result)
299 {
300 	int nerrno = 0;
301 	int oerrno = errno;
302 
303 	errno = 0;
304 	if ((*result = _getpwnam_r(name, pwd, buffer, (uintptr_t)bufsize))
305 		== NULL) {
306 			nerrno = errno;
307 	}
308 	errno = oerrno;
309 	return (nerrno);
310 }
311 
312 void
313 setpwent(void)
314 {
315 	nss_setent(&db_root, _nss_initf_passwd, &context);
316 }
317 
318 void
319 endpwent(void)
320 {
321 	nss_endent(&db_root, _nss_initf_passwd, &context);
322 	nss_delete(&db_root);
323 }
324 
325 struct passwd *
326 getpwent_r(struct passwd *result, char *buffer, int buflen)
327 {
328 	nss_XbyY_args_t arg;
329 	char		*nam;
330 
331 	/* In getXXent_r(), protect the unsuspecting caller from +/- entries */
332 
333 	do {
334 		NSS_XbyY_INIT(&arg, result, buffer, buflen, str2passwd);
335 		/* No key to fill in */
336 		(void) nss_getent(&db_root, _nss_initf_passwd, &context, &arg);
337 	} while (arg.returnval != 0 &&
338 	    (nam = ((struct passwd *)arg.returnval)->pw_name) != 0 &&
339 		(*nam == '+' || *nam == '-'));
340 
341 	return ((struct passwd *)NSS_XbyY_FINI(&arg));
342 }
343 
344 struct passwd *
345 fgetpwent_r(FILE *f, struct passwd *result, char *buffer, int buflen)
346 {
347 	extern void	_nss_XbyY_fgets(FILE *, nss_XbyY_args_t *);
348 	nss_XbyY_args_t	arg;
349 
350 	/* ... but in fgetXXent_r, the caller deserves any +/- entry he gets */
351 
352 	/* No key to fill in */
353 	NSS_XbyY_INIT(&arg, result, buffer, buflen, str2passwd);
354 	_nss_XbyY_fgets(f, &arg);
355 	return ((struct passwd *)NSS_XbyY_FINI(&arg));
356 }
357 
358 static char *
359 gettok(char **nextpp)
360 {
361 	char	*p = *nextpp;
362 	char	*q = p;
363 	char	c;
364 
365 	if (p == 0)
366 		return (0);
367 
368 	while ((c = *q) != '\0' && c != ':')
369 		q++;
370 
371 	if (c == '\0')
372 		*nextpp = 0;
373 	else {
374 		*q++ = '\0';
375 		*nextpp = q;
376 	}
377 	return (p);
378 }
379 
380 /*
381  * Return values: 0 = success, 1 = parse error, 2 = erange ...
382  * The structure pointer passed in is a structure in the caller's space
383  * wherein the field pointers would be set to areas in the buffer if
384  * need be. instring and buffer should be separate areas.
385  */
386 int
387 str2passwd(const char *instr, int lenstr, void *ent, char *buffer, int buflen)
388 {
389 	struct passwd	*passwd	= (struct passwd *)ent;
390 	char		*p, *next;
391 	int		black_magic;	/* "+" or "-" entry */
392 
393 	if (lenstr + 1 > buflen)
394 		return (NSS_STR_PARSE_ERANGE);
395 
396 	/*
397 	 * We copy the input string into the output buffer and
398 	 * operate on it in place.
399 	 */
400 	(void) memcpy(buffer, instr, lenstr);
401 	buffer[lenstr] = '\0';
402 
403 	next = buffer;
404 
405 	passwd->pw_name = p = gettok(&next);		/* username */
406 	if (*p == '\0') {
407 		/* Empty username;  not allowed */
408 		return (NSS_STR_PARSE_PARSE);
409 	}
410 	black_magic = (*p == '+' || *p == '-');
411 	if (black_magic) {
412 		passwd->pw_uid = UID_NOBODY;
413 		passwd->pw_gid = GID_NOBODY;
414 		/*
415 		 *  pwconv tests pw_passwd and pw_age == NULL
416 		 */
417 		passwd->pw_passwd  = "";
418 		passwd->pw_age	= "";
419 		/*
420 		 * the rest of the passwd entry is "optional"
421 		 */
422 		passwd->pw_comment = "";
423 		passwd->pw_gecos = "";
424 		passwd->pw_dir	= "";
425 		passwd->pw_shell = "";
426 	}
427 
428 	passwd->pw_passwd = p = gettok(&next);		/* password */
429 	if (p == 0) {
430 		if (black_magic)
431 			return (NSS_STR_PARSE_SUCCESS);
432 		else
433 			return (NSS_STR_PARSE_PARSE);
434 	}
435 	for (; *p != '\0';  p++) {			/* age */
436 		if (*p == ',') {
437 			*p++ = '\0';
438 			break;
439 		}
440 	}
441 	passwd->pw_age = p;
442 
443 	p = next;					/* uid */
444 	if (p == 0 || *p == '\0') {
445 		if (black_magic)
446 			return (NSS_STR_PARSE_SUCCESS);
447 		else
448 			return (NSS_STR_PARSE_PARSE);
449 	}
450 	if (!black_magic) {
451 		passwd->pw_uid = (uid_t)strtol(p, &next, 10);
452 		if (next == p) {
453 			/* uid field should be nonempty */
454 			return (NSS_STR_PARSE_PARSE);
455 		}
456 		/*
457 		 * The old code (in 2.0 through 2.5) would check
458 		 * for the uid being negative, or being greater
459 		 * than 60001 (the rfs limit).  If it met either of
460 		 * these conditions, the uid was translated to 60001.
461 		 *
462 		 * Now we just check for negative uids; anything else
463 		 * is administrative policy
464 		 */
465 		if (passwd->pw_uid < 0)
466 			passwd->pw_uid = UID_NOBODY;
467 	}
468 	if (*next++ != ':') {
469 		if (black_magic)
470 			(void) gettok(&next);
471 		else
472 			return (NSS_STR_PARSE_PARSE);
473 	}
474 	p = next;					/* gid */
475 	if (p == 0 || *p == '\0') {
476 		if (black_magic)
477 			return (NSS_STR_PARSE_SUCCESS);
478 		else
479 			return (NSS_STR_PARSE_PARSE);
480 	}
481 	if (!black_magic) {
482 		passwd->pw_gid = (gid_t)strtol(p, &next, 10);
483 		if (next == p) {
484 			/* gid field should be nonempty */
485 			return (NSS_STR_PARSE_PARSE);
486 		}
487 		/*
488 		 * gid should be non-negative; anything else
489 		 * is administrative policy.
490 		 */
491 		if (passwd->pw_gid < 0)
492 			passwd->pw_gid = GID_NOBODY;
493 	}
494 	if (*next++ != ':') {
495 		if (black_magic)
496 			(void) gettok(&next);
497 		else
498 			return (NSS_STR_PARSE_PARSE);
499 	}
500 
501 	passwd->pw_gecos = passwd->pw_comment = p = gettok(&next);
502 	if (p == 0) {
503 		if (black_magic)
504 			return (NSS_STR_PARSE_SUCCESS);
505 		else
506 			return (NSS_STR_PARSE_PARSE);
507 	}
508 
509 	passwd->pw_dir = p = gettok(&next);
510 	if (p == 0) {
511 		if (black_magic)
512 			return (NSS_STR_PARSE_SUCCESS);
513 		else
514 			return (NSS_STR_PARSE_PARSE);
515 	}
516 
517 	passwd->pw_shell = p = gettok(&next);
518 	if (p == 0) {
519 		if (black_magic)
520 			return (NSS_STR_PARSE_SUCCESS);
521 		else
522 			return (NSS_STR_PARSE_PARSE);
523 	}
524 
525 	/* Better not be any more fields... */
526 	if (next == 0) {
527 		/* Successfully parsed and stored */
528 		return (NSS_STR_PARSE_SUCCESS);
529 	}
530 	return (NSS_STR_PARSE_PARSE);
531 }
532