xref: /openbsd/lib/libc/gen/getpwent.c (revision c06670c5)
1 /*	$OpenBSD: getpwent.c,v 1.68 2024/01/22 21:07:09 deraadt Exp $ */
2 /*
3  * Copyright (c) 2008 Theo de Raadt
4  * Copyright (c) 1988, 1993
5  *	The Regents of the University of California.  All rights reserved.
6  * Portions Copyright (c) 1994, 1995, 1996, Jason Downs.  All rights reserved.
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted provided that the following conditions
10  * are met:
11  * 1. Redistributions of source code must retain the above copyright
12  *    notice, this list of conditions and the following disclaimer.
13  * 2. Redistributions in binary form must reproduce the above copyright
14  *    notice, this list of conditions and the following disclaimer in the
15  *    documentation and/or other materials provided with the distribution.
16  * 3. Neither the name of the University nor the names of its contributors
17  *    may be used to endorse or promote products derived from this software
18  *    without specific prior written permission.
19  *
20  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
21  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
24  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30  * SUCH DAMAGE.
31  */
32 
33 #include <sys/types.h>
34 #include <sys/mman.h>
35 #include <fcntl.h>
36 #include <db.h>
37 #include <syslog.h>
38 #include <pwd.h>
39 #include <errno.h>
40 #include <unistd.h>
41 #include <stdbool.h>
42 #include <stdlib.h>
43 #include <string.h>
44 #include <limits.h>
45 #include <netgroup.h>
46 #ifdef YP
47 #include <stdio.h>
48 #include <rpc/rpc.h>
49 #include <rpcsvc/yp.h>
50 #include <rpcsvc/ypclnt.h>
51 #include "ypinternal.h"
52 #include "ypexclude.h"
53 #endif
54 #include "thread_private.h"
55 
56 #define MINIMUM(a, b)	(((a) < (b)) ? (a) : (b))
57 
58 struct pw_storage {
59 	struct passwd pw;
60 	uid_t uid;
61 	char name[_PW_NAME_LEN + 1];
62 	char pwbuf[_PW_BUF_LEN];
63 };
64 
65 _THREAD_PRIVATE_KEY(pw);
66 
67 static DB *_pw_db;			/* password database */
68 
69 /* mmap'd password storage */
70 static struct pw_storage *_pw_storage = MAP_FAILED;
71 
72 /* Following are used only by setpwent(), getpwent(), and endpwent() */
73 static int _pw_keynum;			/* key counter */
74 static int _pw_stayopen;		/* keep fd's open */
75 static int _pw_flags;			/* password flags */
76 
77 static int __hashpw(DBT *, char *buf, size_t buflen, struct passwd *, int *);
78 static int __initdb(int);
79 static struct passwd *_pwhashbyname(const char *name, char *buf,
80 	size_t buflen, struct passwd *pw, int *);
81 static struct passwd *_pwhashbyuid(uid_t uid, char *buf,
82 	size_t buflen, struct passwd *pw, int *);
83 
84 #ifdef YP
85 static char	*__ypdomain;
86 
87 /* Following are used only by setpwent(), getpwent(), and endpwent() */
88 enum _ypmode { YPMODE_NONE, YPMODE_FULL, YPMODE_USER, YPMODE_NETGRP };
89 static enum	_ypmode __ypmode;
90 static char	*__ypcurrent;
91 static int	__ypcurrentlen;
92 static int	__yp_pw_flags;
93 static int	__getpwent_has_yppw = -1;
94 static struct _ypexclude *__ypexhead;
95 
96 static int __has_yppw(void);
97 static int __has_ypmaster(void);
98 static int __ypparse(struct passwd *pw, char *s, int);
99 
100 #define LOOKUP_BYNAME 0
101 #define LOOKUP_BYUID 1
102 static struct passwd *__yppwlookup(int, char *, uid_t, struct passwd *,
103     char *, size_t, int *);
104 
105 /* macro for deciding which YP maps to use. */
106 #define PASSWD_BYNAME \
107 	(__has_ypmaster() ? "master.passwd.byname" : "passwd.byname")
108 #define PASSWD_BYUID \
109 	(__has_ypmaster() ? "master.passwd.byuid" : "passwd.byuid")
110 
111 static struct passwd *__ypproto;
112 
113 static void __ypproto_set(struct passwd *, struct pw_storage *, int, int *);
114 
115 static void
__ypproto_set(struct passwd * pw,struct pw_storage * buf,int flags,int * yp_pw_flagsp)116 __ypproto_set(struct passwd *pw, struct pw_storage *buf, int flags,
117     int *yp_pw_flagsp)
118 {
119 	char *ptr = buf->pwbuf;
120 	__ypproto = &buf->pw;
121 
122 	/* name */
123 	if (pw->pw_name && (pw->pw_name)[0]) {
124 		bcopy(pw->pw_name, ptr, strlen(pw->pw_name) + 1);
125 		__ypproto->pw_name = ptr;
126 		ptr += (strlen(pw->pw_name) + 1);
127 	} else
128 		__ypproto->pw_name = NULL;
129 
130 	/* password */
131 	if (pw->pw_passwd && (pw->pw_passwd)[0]) {
132 		bcopy(pw->pw_passwd, ptr, strlen(pw->pw_passwd) + 1);
133 		__ypproto->pw_passwd = ptr;
134 		ptr += (strlen(pw->pw_passwd) + 1);
135 	} else
136 		__ypproto->pw_passwd = NULL;
137 
138 	/* uid */
139 	__ypproto->pw_uid = pw->pw_uid;
140 
141 	/* gid */
142 	__ypproto->pw_gid = pw->pw_gid;
143 
144 	/* change (ignored anyway) */
145 	__ypproto->pw_change = pw->pw_change;
146 
147 	/* class (ignored anyway) */
148 	__ypproto->pw_class = "";
149 
150 	/* gecos */
151 	if (pw->pw_gecos && (pw->pw_gecos)[0]) {
152 		bcopy(pw->pw_gecos, ptr, strlen(pw->pw_gecos) + 1);
153 		__ypproto->pw_gecos = ptr;
154 		ptr += (strlen(pw->pw_gecos) + 1);
155 	} else
156 		__ypproto->pw_gecos = NULL;
157 
158 	/* dir */
159 	if (pw->pw_dir && (pw->pw_dir)[0]) {
160 		bcopy(pw->pw_dir, ptr, strlen(pw->pw_dir) + 1);
161 		__ypproto->pw_dir = ptr;
162 		ptr += (strlen(pw->pw_dir) + 1);
163 	} else
164 		__ypproto->pw_dir = NULL;
165 
166 	/* shell */
167 	if (pw->pw_shell && (pw->pw_shell)[0]) {
168 		bcopy(pw->pw_shell, ptr, strlen(pw->pw_shell) + 1);
169 		__ypproto->pw_shell = ptr;
170 		ptr += (strlen(pw->pw_shell) + 1);
171 	} else
172 		__ypproto->pw_shell = NULL;
173 
174 	/* expire (ignored anyway) */
175 	__ypproto->pw_expire = pw->pw_expire;
176 
177 	/* flags */
178 	*yp_pw_flagsp = flags;
179 }
180 
181 static int
__ypparse(struct passwd * pw,char * s,int yp_pw_flags)182 __ypparse(struct passwd *pw, char *s, int yp_pw_flags)
183 {
184 	char *bp, *cp, *endp;
185 	u_long ul;
186 	int count = 0;
187 
188 	/* count the colons. */
189 	bp = s;
190 	while (*bp != '\0') {
191 		if (*bp++ == ':')
192 			count++;
193 	}
194 
195 	/* since this is currently using strsep(), parse it first */
196 	bp = s;
197 	pw->pw_name = strsep(&bp, ":\n");
198 	pw->pw_passwd = strsep(&bp, ":\n");
199 	if (!(cp = strsep(&bp, ":\n")))
200 		return (1);
201 	ul = strtoul(cp, &endp, 10);
202 	if (endp == cp || *endp != '\0' || ul >= UID_MAX)
203 		return (1);
204 	pw->pw_uid = (uid_t)ul;
205 	if (!(cp = strsep(&bp, ":\n")))
206 		return (1);
207 	ul = strtoul(cp, &endp, 10);
208 	if (endp == cp || *endp != '\0' || ul >= GID_MAX)
209 		return (1);
210 	pw->pw_gid = (gid_t)ul;
211 	if (count == 9) {
212 		long l;
213 
214 		/* If the ypserv gave us all the fields, use them. */
215 		pw->pw_class = strsep(&bp, ":\n");
216 		if (!(cp = strsep(&bp, ":\n")))
217 			return (1);
218 		l = strtol(cp, &endp, 10);
219 		if (endp == cp || *endp != '\0' || l >= INT_MAX || l <= INT_MIN)
220 			return (1);
221 		pw->pw_change = (time_t)l;
222 		if (!(cp = strsep(&bp, ":\n")))
223 			return (1);
224 		l = strtol(cp, &endp, 10);
225 		if (endp == cp || *endp != '\0' || l >= INT_MAX || l <= INT_MIN)
226 			return (1);
227 		pw->pw_expire = (time_t)l;
228 	} else {
229 		/* ..else it is a normal ypserv. */
230 		pw->pw_class = "";
231 		pw->pw_change = 0;
232 		pw->pw_expire = 0;
233 	}
234 	pw->pw_gecos = strsep(&bp, ":\n");
235 	pw->pw_dir = strsep(&bp, ":\n");
236 	pw->pw_shell = strsep(&bp, ":\n");
237 
238 	/* now let the prototype override, if set. */
239 	if (__ypproto) {
240 		if (!(yp_pw_flags & _PASSWORD_NOUID))
241 			pw->pw_uid = __ypproto->pw_uid;
242 		if (!(yp_pw_flags & _PASSWORD_NOGID))
243 			pw->pw_gid = __ypproto->pw_gid;
244 		if (__ypproto->pw_gecos)
245 			pw->pw_gecos = __ypproto->pw_gecos;
246 		if (__ypproto->pw_dir)
247 			pw->pw_dir = __ypproto->pw_dir;
248 		if (__ypproto->pw_shell)
249 			pw->pw_shell = __ypproto->pw_shell;
250 	}
251 	return (0);
252 }
253 #endif
254 
255 static struct passwd *
__get_pw_buf(char ** bufp,size_t * buflenp,uid_t uid,const char * name)256 __get_pw_buf(char **bufp, size_t *buflenp, uid_t uid, const char *name)
257 {
258 	bool remap = true;
259 
260 	/* Unmap the old buffer unless we are looking up the same uid/name */
261 	if (_pw_storage != MAP_FAILED) {
262 		if (name != NULL) {
263 			if (strcmp(_pw_storage->name, name) == 0) {
264 #ifdef PWDEBUG
265 				struct syslog_data sdata = SYSLOG_DATA_INIT;
266 				syslog_r(LOG_CRIT | LOG_CONS, &sdata,
267 				    "repeated passwd lookup of user \"%s\"",
268 				    name);
269 #endif
270 				remap = false;
271 			}
272 		} else if (uid != (uid_t)-1) {
273 			if (_pw_storage->uid == uid) {
274 #ifdef PWDEBUG
275 				struct syslog_data sdata = SYSLOG_DATA_INIT;
276 				syslog_r(LOG_CRIT | LOG_CONS, &sdata,
277 				    "repeated passwd lookup of uid %u",
278 				    uid);
279 #endif
280 				remap = false;
281 			}
282 		}
283 		if (remap)
284 			munmap(_pw_storage, sizeof(*_pw_storage));
285 	}
286 
287 	if (remap) {
288 		_pw_storage = mmap(NULL, sizeof(*_pw_storage),
289 		    PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANON, -1, 0);
290 		if (_pw_storage == MAP_FAILED)
291 			return NULL;
292 		if (name != NULL)
293 			strlcpy(_pw_storage->name, name, sizeof(_pw_storage->name));
294 		_pw_storage->uid = uid;
295 	}
296 
297 	*bufp = _pw_storage->pwbuf;
298 	*buflenp = sizeof(_pw_storage->pwbuf);
299 	return &_pw_storage->pw;
300 }
301 
302 struct passwd *
getpwent(void)303 getpwent(void)
304 {
305 #ifdef YP
306 	static char *name = NULL;
307 	char *map;
308 #endif
309 	char bf[1 + sizeof(_pw_keynum)];
310 	struct passwd *pw, *ret = NULL;
311 	char *pwbuf;
312 	size_t buflen;
313 	DBT key;
314 
315 	_THREAD_PRIVATE_MUTEX_LOCK(pw);
316 	if (!_pw_db && !__initdb(0))
317 		goto done;
318 
319 	/* Allocate space for struct and strings, unmapping the old. */
320 	if ((pw = __get_pw_buf(&pwbuf, &buflen, -1, NULL)) == NULL)
321 		goto done;
322 
323 #ifdef YP
324 	map = PASSWD_BYNAME;
325 
326 	if (__getpwent_has_yppw == -1)
327 		__getpwent_has_yppw = __has_yppw();
328 
329 again:
330 	if (__getpwent_has_yppw && (__ypmode != YPMODE_NONE)) {
331 		const char *user, *host, *dom;
332 		int keylen, datalen, r, s;
333 		char *key, *data = NULL;
334 
335 		if (!__ypdomain)
336 			yp_get_default_domain(&__ypdomain);
337 		switch (__ypmode) {
338 		case YPMODE_FULL:
339 			if (__ypcurrent) {
340 				r = yp_next(__ypdomain, map,
341 				    __ypcurrent, __ypcurrentlen,
342 				    &key, &keylen, &data, &datalen);
343 				free(__ypcurrent);
344 				__ypcurrent = NULL;
345 				if (r != 0) {
346 					__ypmode = YPMODE_NONE;
347 					free(data);
348 					goto again;
349 				}
350 				__ypcurrent = key;
351 				__ypcurrentlen = keylen;
352 			} else {
353 				r = yp_first(__ypdomain, map,
354 				    &__ypcurrent, &__ypcurrentlen,
355 				    &data, &datalen);
356 				if (r != 0 ||
357 				    __ypcurrentlen > buflen) {
358 					__ypmode = YPMODE_NONE;
359 					free(data);
360 					goto again;
361 				}
362 			}
363 			bcopy(data, pwbuf, datalen);
364 			free(data);
365 			break;
366 		case YPMODE_NETGRP:
367 			s = getnetgrent(&host, &user, &dom);
368 			if (s == 0) {	/* end of group */
369 				endnetgrent();
370 				__ypmode = YPMODE_NONE;
371 				goto again;
372 			}
373 			if (user && *user) {
374 				r = yp_match(__ypdomain, map,
375 				    user, strlen(user), &data, &datalen);
376 			} else
377 				goto again;
378 			if (r != 0 ||
379 			    __ypcurrentlen > buflen) {
380 				/*
381 				 * if the netgroup is invalid, keep looking
382 				 * as there may be valid users later on.
383 				 */
384 				free(data);
385 				goto again;
386 			}
387 			bcopy(data, pwbuf, datalen);
388 			free(data);
389 			break;
390 		case YPMODE_USER:
391 			if (name) {
392 				r = yp_match(__ypdomain, map,
393 				    name, strlen(name), &data, &datalen);
394 				__ypmode = YPMODE_NONE;
395 				free(name);
396 				name = NULL;
397 				if (r != 0 ||
398 				    __ypcurrentlen > buflen) {
399 					free(data);
400 					goto again;
401 				}
402 				bcopy(data, pwbuf, datalen);
403 				free(data);
404 			} else {		/* XXX */
405 				__ypmode = YPMODE_NONE;
406 				goto again;
407 			}
408 			break;
409 		case YPMODE_NONE:
410 			/* NOTREACHED */
411 			break;
412 		}
413 
414 		pwbuf[datalen] = '\0';
415 		if (__ypparse(pw, pwbuf, __yp_pw_flags))
416 			goto again;
417 		ret = pw;
418 		goto done;
419 	}
420 #endif
421 
422 	++_pw_keynum;
423 	bf[0] = _PW_KEYBYNUM;
424 	bcopy((char *)&_pw_keynum, &bf[1], sizeof(_pw_keynum));
425 	key.data = (u_char *)bf;
426 	key.size = 1 + sizeof(_pw_keynum);
427 	if (__hashpw(&key, pwbuf, buflen, pw, &_pw_flags)) {
428 #ifdef YP
429 		static struct pw_storage __yppbuf;
430 		const char *user, *host, *dom;
431 
432 		/* if we don't have YP at all, don't bother. */
433 		if (__getpwent_has_yppw) {
434 			if (pw->pw_name[0] == '+') {
435 				/* set the mode */
436 				switch (pw->pw_name[1]) {
437 				case '\0':
438 					__ypmode = YPMODE_FULL;
439 					break;
440 				case '@':
441 					__ypmode = YPMODE_NETGRP;
442 					setnetgrent(pw->pw_name + 2);
443 					break;
444 				default:
445 					__ypmode = YPMODE_USER;
446 					name = strdup(pw->pw_name + 1);
447 					break;
448 				}
449 
450 				__ypproto_set(pw, &__yppbuf, _pw_flags,
451 				    &__yp_pw_flags);
452 				goto again;
453 			} else if (pw->pw_name[0] == '-') {
454 				/* an attempted exclusion */
455 				switch (pw->pw_name[1]) {
456 				case '\0':
457 					break;
458 				case '@':
459 					setnetgrent(pw->pw_name + 2);
460 					while (getnetgrent(&host, &user, &dom)) {
461 						if (user && *user)
462 							__ypexclude_add(&__ypexhead,
463 							    user);
464 					}
465 					endnetgrent();
466 					break;
467 				default:
468 					__ypexclude_add(&__ypexhead,
469 					    pw->pw_name + 1);
470 					break;
471 				}
472 				goto again;
473 			}
474 		}
475 #endif
476 		ret = pw;
477 		goto done;
478 	}
479 
480 done:
481 	_THREAD_PRIVATE_MUTEX_UNLOCK(pw);
482 	return (ret);
483 }
484 
485 #ifdef YP
486 /*
487  * See if the YP token is in the database.  Only works if pwd_mkdb knows
488  * about the token.
489  */
490 static int
__has_yppw(void)491 __has_yppw(void)
492 {
493 	DBT key, data, pkey, pdata;
494 	char bf[2];
495 
496 	key.data = (u_char *)_PW_YPTOKEN;
497 	key.size = strlen(_PW_YPTOKEN);
498 
499 	/* Pre-token database support. */
500 	bf[0] = _PW_KEYBYNAME;
501 	bf[1] = '+';
502 	pkey.data = (u_char *)bf;
503 	pkey.size = sizeof(bf);
504 
505 	if ((_pw_db->get)(_pw_db, &key, &data, 0) &&
506 	    (_pw_db->get)(_pw_db, &pkey, &pdata, 0))
507 		return (0);	/* No YP. */
508 	return (1);
509 }
510 
511 /*
512  * See if there's a master.passwd map.
513  */
514 static int
__has_ypmaster(void)515 __has_ypmaster(void)
516 {
517 	int keylen, resultlen;
518 	char *key, *result;
519 	static int checked = -1;
520 	static uid_t saved_uid, saved_euid;
521 	uid_t uid = getuid(), euid = geteuid();
522 
523 	/*
524 	 * Do not recheck IFF the saved UID and the saved
525 	 * EUID are the same. In all other cases, recheck.
526 	 */
527 	if (checked != -1 && saved_uid == uid && saved_euid == euid)
528 		return (checked);
529 
530 	if (euid != 0) {
531 		saved_uid = uid;
532 		saved_euid = euid;
533 		checked = 0;
534 		return (checked);
535 	}
536 
537 	if (!__ypdomain)
538 		yp_get_default_domain(&__ypdomain);
539 
540 	if (yp_first(__ypdomain, "master.passwd.byname",
541 	    &key, &keylen, &result, &resultlen)) {
542 		saved_uid = uid;
543 		saved_euid = euid;
544 		checked = 0;
545 		return (checked);
546 	}
547 	free(result);
548 	free(key);
549 
550 	saved_uid = uid;
551 	saved_euid = euid;
552 	checked = 1;
553 	return (checked);
554 }
555 
556 static struct passwd *
__yppwlookup(int lookup,char * name,uid_t uid,struct passwd * pw,char * buf,size_t buflen,int * flagsp)557 __yppwlookup(int lookup, char *name, uid_t uid, struct passwd *pw,
558     char *buf, size_t buflen, int *flagsp)
559 {
560 	char bf[1 + _PW_NAME_LEN], *ypcurrent = NULL, *map = NULL;
561 	int yp_pw_flags = 0, ypcurrentlen, r, s = -1, pw_keynum;
562 	static struct pw_storage __yppbuf;
563 	struct _ypexclude *ypexhead = NULL;
564 	const char *host, *user, *dom;
565 	DBT key;
566 
567 	for (pw_keynum = 1; pw_keynum; pw_keynum++) {
568 		bf[0] = _PW_KEYBYNUM;
569 		bcopy((char *)&pw_keynum, &bf[1], sizeof(pw_keynum));
570 		key.data = (u_char *)bf;
571 		key.size = 1 + sizeof(pw_keynum);
572 		if (__hashpw(&key, buf, buflen, pw, flagsp) == 0)
573 			break;
574 		switch (pw->pw_name[0]) {
575 		case '+':
576 			if (!__ypdomain)
577 				yp_get_default_domain(&__ypdomain);
578 			__ypproto_set(pw, &__yppbuf, *flagsp, &yp_pw_flags);
579 			if (!map) {
580 				if (lookup == LOOKUP_BYNAME) {
581 					if ((name = strdup(name)) == NULL) {
582 						pw = NULL;
583 						goto done;
584 					}
585 					map = PASSWD_BYNAME;
586 				} else {
587 					if (asprintf(&name, "%u", uid) == -1) {
588 						pw = NULL;
589 						goto done;
590 					}
591 					map = PASSWD_BYUID;
592 				}
593 			}
594 
595 			switch (pw->pw_name[1]) {
596 			case '\0':
597 				free(ypcurrent);
598 				ypcurrent = NULL;
599 				r = yp_match(__ypdomain, map,
600 				    name, strlen(name),
601 				    &ypcurrent, &ypcurrentlen);
602 				if (r != 0 || ypcurrentlen > buflen) {
603 					free(ypcurrent);
604 					ypcurrent = NULL;
605 					continue;
606 				}
607 				break;
608 			case '@':
609 pwnam_netgrp:
610 				free(ypcurrent);
611 				ypcurrent = NULL;
612 				if (s == -1)	/* first time */
613 					setnetgrent(pw->pw_name + 2);
614 				s = getnetgrent(&host, &user, &dom);
615 				if (s == 0) {	/* end of group */
616 					endnetgrent();
617 					s = -1;
618 					continue;
619 				} else {
620 					if (user && *user) {
621 						r = yp_match(__ypdomain, map,
622 						    user, strlen(user),
623 						    &ypcurrent, &ypcurrentlen);
624 					} else
625 						goto pwnam_netgrp;
626 					if (r != 0 || ypcurrentlen > buflen) {
627 						free(ypcurrent);
628 						ypcurrent = NULL;
629 						/*
630 						 * just because this
631 						 * user is bad, doesn't
632 						 * mean they all are.
633 						 */
634 						goto pwnam_netgrp;
635 					}
636 				}
637 				break;
638 			default:
639 				free(ypcurrent);
640 				ypcurrent = NULL;
641 				user = pw->pw_name + 1;
642 				r = yp_match(__ypdomain, map,
643 				    user, strlen(user),
644 				    &ypcurrent, &ypcurrentlen);
645 				if (r != 0 || ypcurrentlen > buflen) {
646 					free(ypcurrent);
647 					ypcurrent = NULL;
648 					continue;
649 				}
650 				break;
651 			}
652 			bcopy(ypcurrent, buf, ypcurrentlen);
653 			buf[ypcurrentlen] = '\0';
654 			if (__ypparse(pw, buf, yp_pw_flags) ||
655 			    __ypexclude_is(&ypexhead, pw->pw_name)) {
656 				if (s == 1)	/* inside netgrp */
657 					goto pwnam_netgrp;
658 				continue;
659 			}
660 			break;
661 		case '-':
662 			/* attempted exclusion */
663 			switch (pw->pw_name[1]) {
664 			case '\0':
665 				break;
666 			case '@':
667 				setnetgrent(pw->pw_name + 2);
668 				while (getnetgrent(&host, &user, &dom)) {
669 					if (user && *user)
670 						__ypexclude_add(&ypexhead, user);
671 				}
672 				endnetgrent();
673 				break;
674 			default:
675 				__ypexclude_add(&ypexhead, pw->pw_name + 1);
676 				break;
677 			}
678 			break;
679 		}
680 		if ((lookup == LOOKUP_BYUID && pw->pw_uid == uid) ||
681 		    (lookup == LOOKUP_BYNAME && strcmp(pw->pw_name, name) == 0))
682 			goto done;
683 		if (s == 1)	/* inside netgrp */
684 			goto pwnam_netgrp;
685 		continue;
686 	}
687 	pw = NULL;
688 done:
689 	__ypexclude_free(&ypexhead);
690 	__ypproto = NULL;
691 	free(ypcurrent);
692 	ypcurrent = NULL;
693 	if (map)
694 		free(name);
695 	return (pw);
696 }
697 #endif /* YP */
698 
699 static struct passwd *
_pwhashbyname(const char * name,char * buf,size_t buflen,struct passwd * pw,int * flagsp)700 _pwhashbyname(const char *name, char *buf, size_t buflen, struct passwd *pw,
701     int *flagsp)
702 {
703 	char bf[1 + _PW_NAME_LEN];
704 	size_t len;
705 	DBT key;
706 	int r;
707 
708 	len = strlen(name);
709 	if (len > _PW_NAME_LEN)
710 		return (NULL);
711 	bf[0] = _PW_KEYBYNAME;
712 	bcopy(name, &bf[1], MINIMUM(len, _PW_NAME_LEN));
713 	key.data = (u_char *)bf;
714 	key.size = 1 + MINIMUM(len, _PW_NAME_LEN);
715 	r = __hashpw(&key, buf, buflen, pw, flagsp);
716 	if (r)
717 		return (pw);
718 	return (NULL);
719 }
720 
721 static struct passwd *
_pwhashbyuid(uid_t uid,char * buf,size_t buflen,struct passwd * pw,int * flagsp)722 _pwhashbyuid(uid_t uid, char *buf, size_t buflen, struct passwd *pw,
723     int *flagsp)
724 {
725 	char bf[1 + sizeof(int)];
726 	DBT key;
727 	int r;
728 
729 	bf[0] = _PW_KEYBYUID;
730 	bcopy(&uid, &bf[1], sizeof(uid));
731 	key.data = (u_char *)bf;
732 	key.size = 1 + sizeof(uid);
733 	r = __hashpw(&key, buf, buflen, pw, flagsp);
734 	if (r)
735 		return (pw);
736 	return (NULL);
737 }
738 
739 static int
getpwnam_internal(const char * name,struct passwd * pw,char * buf,size_t buflen,struct passwd ** pwretp,bool shadow,bool reentrant)740 getpwnam_internal(const char *name, struct passwd *pw, char *buf, size_t buflen,
741     struct passwd **pwretp, bool shadow, bool reentrant)
742 {
743 	struct passwd *pwret = NULL;
744 	int flags = 0, *flagsp = &flags;
745 	int my_errno = 0;
746 	int saved_errno, tmp_errno;
747 
748 	_THREAD_PRIVATE_MUTEX_LOCK(pw);
749 	saved_errno = errno;
750 	errno = 0;
751 	if (!_pw_db && !__initdb(shadow))
752 		goto fail;
753 
754 	if (!reentrant) {
755 		/* Allocate space for struct and strings, unmapping the old. */
756 		if ((pw = __get_pw_buf(&buf, &buflen, -1, name)) == NULL)
757 			goto fail;
758 		flagsp = &_pw_flags;
759 	}
760 
761 #ifdef YP
762 	if (__has_yppw())
763 		pwret = __yppwlookup(LOOKUP_BYNAME, (char *)name, 0, pw,
764 		    buf, buflen, flagsp);
765 #endif /* YP */
766 	if (!pwret)
767 		pwret = _pwhashbyname(name, buf, buflen, pw, flagsp);
768 
769 	if (!_pw_stayopen) {
770 		tmp_errno = errno;
771 		(void)(_pw_db->close)(_pw_db);
772 		_pw_db = NULL;
773 		errno = tmp_errno;
774 	}
775 fail:
776 	if (pwretp)
777 		*pwretp = pwret;
778 	if (pwret == NULL)
779 		my_errno = errno;
780 	errno = saved_errno;
781 	_THREAD_PRIVATE_MUTEX_UNLOCK(pw);
782 	return (my_errno);
783 }
784 
785 int
getpwnam_r(const char * name,struct passwd * pw,char * buf,size_t buflen,struct passwd ** pwretp)786 getpwnam_r(const char *name, struct passwd *pw, char *buf, size_t buflen,
787     struct passwd **pwretp)
788 {
789 	return getpwnam_internal(name, pw, buf, buflen, pwretp, false, true);
790 }
791 DEF_WEAK(getpwnam_r);
792 
793 struct passwd *
getpwnam(const char * name)794 getpwnam(const char *name)
795 {
796 	struct passwd *pw = NULL;
797 	int my_errno;
798 
799 	my_errno = getpwnam_internal(name, NULL, NULL, 0, &pw, false, false);
800 	if (my_errno) {
801 		pw = NULL;
802 		errno = my_errno;
803 	}
804 	return (pw);
805 }
806 
807 struct passwd *
getpwnam_shadow(const char * name)808 getpwnam_shadow(const char *name)
809 {
810 	struct passwd *pw = NULL;
811 	int my_errno;
812 
813 	my_errno = getpwnam_internal(name, NULL, NULL, 0, &pw, true, false);
814 	if (my_errno) {
815 		pw = NULL;
816 		errno = my_errno;
817 	}
818 	return (pw);
819 }
820 DEF_WEAK(getpwnam_shadow);
821 
822 static int
getpwuid_internal(uid_t uid,struct passwd * pw,char * buf,size_t buflen,struct passwd ** pwretp,bool shadow,bool reentrant)823 getpwuid_internal(uid_t uid, struct passwd *pw, char *buf, size_t buflen,
824     struct passwd **pwretp, bool shadow, bool reentrant)
825 {
826 	struct passwd *pwret = NULL;
827 	int flags = 0, *flagsp = &flags;
828 	int my_errno = 0;
829 	int saved_errno, tmp_errno;
830 
831 	_THREAD_PRIVATE_MUTEX_LOCK(pw);
832 	saved_errno = errno;
833 	errno = 0;
834 	if (!_pw_db && !__initdb(shadow))
835 		goto fail;
836 
837 	if (!reentrant) {
838 		/* Allocate space for struct and strings, unmapping the old. */
839 		if ((pw = __get_pw_buf(&buf, &buflen, uid, NULL)) == NULL)
840 			goto fail;
841 		flagsp = &_pw_flags;
842 	}
843 
844 #ifdef YP
845 	if (__has_yppw())
846 		pwret = __yppwlookup(LOOKUP_BYUID, NULL, uid, pw,
847 		    buf, buflen, flagsp);
848 #endif /* YP */
849 	if (!pwret)
850 		pwret = _pwhashbyuid(uid, buf, buflen, pw, flagsp);
851 
852 	if (!_pw_stayopen) {
853 		tmp_errno = errno;
854 		(void)(_pw_db->close)(_pw_db);
855 		_pw_db = NULL;
856 		errno = tmp_errno;
857 	}
858 fail:
859 	if (pwretp)
860 		*pwretp = pwret;
861 	if (pwret == NULL)
862 		my_errno = errno;
863 	errno = saved_errno;
864 	_THREAD_PRIVATE_MUTEX_UNLOCK(pw);
865 	return (my_errno);
866 }
867 
868 
869 int
getpwuid_r(uid_t uid,struct passwd * pw,char * buf,size_t buflen,struct passwd ** pwretp)870 getpwuid_r(uid_t uid, struct passwd *pw, char *buf, size_t buflen,
871     struct passwd **pwretp)
872 {
873 	return getpwuid_internal(uid, pw, buf, buflen, pwretp, false, true);
874 }
875 DEF_WEAK(getpwuid_r);
876 
877 struct passwd *
getpwuid(uid_t uid)878 getpwuid(uid_t uid)
879 {
880 	struct passwd *pw = NULL;
881 	int my_errno;
882 
883 	my_errno = getpwuid_internal(uid, NULL, NULL, 0, &pw, false, false);
884 	if (my_errno) {
885 		pw = NULL;
886 		errno = my_errno;
887 	}
888 	return (pw);
889 }
890 
891 struct passwd *
getpwuid_shadow(uid_t uid)892 getpwuid_shadow(uid_t uid)
893 {
894 	struct passwd *pw = NULL;
895 	int my_errno;
896 
897 	my_errno = getpwuid_internal(uid, NULL, NULL, 0, &pw, true, false);
898 	if (my_errno) {
899 		pw = NULL;
900 		errno = my_errno;
901 	}
902 	return (pw);
903 }
904 DEF_WEAK(getpwuid_shadow);
905 
906 int
setpassent(int stayopen)907 setpassent(int stayopen)
908 {
909 	_THREAD_PRIVATE_MUTEX_LOCK(pw);
910 	_pw_keynum = 0;
911 	_pw_stayopen = stayopen;
912 #ifdef YP
913 	__ypmode = YPMODE_NONE;
914 	free(__ypcurrent);
915 	__ypcurrent = NULL;
916 	__ypexclude_free(&__ypexhead);
917 	__ypproto = NULL;
918 #endif
919 	_THREAD_PRIVATE_MUTEX_UNLOCK(pw);
920 	return (1);
921 }
922 DEF_WEAK(setpassent);
923 
924 void
setpwent(void)925 setpwent(void)
926 {
927 	(void) setpassent(0);
928 }
929 
930 void
endpwent(void)931 endpwent(void)
932 {
933 	int saved_errno;
934 
935 	_THREAD_PRIVATE_MUTEX_LOCK(pw);
936 	saved_errno = errno;
937 	_pw_keynum = 0;
938 	if (_pw_db) {
939 		(void)(_pw_db->close)(_pw_db);
940 		_pw_db = NULL;
941 	}
942 #ifdef YP
943 	__ypmode = YPMODE_NONE;
944 	free(__ypcurrent);
945 	__ypcurrent = NULL;
946 	__ypexclude_free(&__ypexhead);
947 	__ypproto = NULL;
948 #endif
949 	errno = saved_errno;
950 	_THREAD_PRIVATE_MUTEX_UNLOCK(pw);
951 }
952 
953 static int
__initdb(int shadow)954 __initdb(int shadow)
955 {
956 	static int warned;
957 	int saved_errno = errno;
958 
959 #ifdef YP
960 	__ypmode = YPMODE_NONE;
961 	__getpwent_has_yppw = -1;
962 #endif
963 	if (shadow) {
964 #ifdef FORCE_DBOPEN
965 		_pw_db = dbopen(_PATH_SMP_DB, O_RDONLY, 0, DB_HASH, NULL);
966 #else
967 		_pw_db = __hash_open(_PATH_SMP_DB, O_RDONLY, 0, NULL, 0);
968 #endif
969 	}
970 	if (!_pw_db) {
971 #ifdef FORCE_DBOPEN
972 	    _pw_db = dbopen(_PATH_MP_DB, O_RDONLY, 0, DB_HASH, NULL);
973 #else
974 	    _pw_db = __hash_open(_PATH_MP_DB, O_RDONLY, 0, NULL, 0);
975 #endif
976 	}
977 	if (_pw_db) {
978 		errno = saved_errno;
979 		return (1);
980 	}
981 	if (!warned) {
982 		saved_errno = errno;
983 		errno = saved_errno;
984 		warned = 1;
985 	}
986 	return (0);
987 }
988 
989 static int
__hashpw(DBT * key,char * buf,size_t buflen,struct passwd * pw,int * flagsp)990 __hashpw(DBT *key, char *buf, size_t buflen, struct passwd *pw,
991     int *flagsp)
992 {
993 	char *p, *t;
994 	DBT data;
995 
996 	if ((_pw_db->get)(_pw_db, key, &data, 0))
997 		return (0);
998 	p = (char *)data.data;
999 	if (data.size > buflen) {
1000 		errno = ERANGE;
1001 		return (0);
1002 	}
1003 
1004 	t = buf;
1005 #define	EXPAND(e)	e = t; while ((*t++ = *p++));
1006 	EXPAND(pw->pw_name);
1007 	EXPAND(pw->pw_passwd);
1008 	bcopy(p, (char *)&pw->pw_uid, sizeof(int));
1009 	p += sizeof(int);
1010 	bcopy(p, (char *)&pw->pw_gid, sizeof(int));
1011 	p += sizeof(int);
1012 	bcopy(p, (char *)&pw->pw_change, sizeof(time_t));
1013 	p += sizeof(time_t);
1014 	EXPAND(pw->pw_class);
1015 	EXPAND(pw->pw_gecos);
1016 	EXPAND(pw->pw_dir);
1017 	EXPAND(pw->pw_shell);
1018 	bcopy(p, (char *)&pw->pw_expire, sizeof(time_t));
1019 	p += sizeof(time_t);
1020 
1021 	/* See if there's any data left.  If so, read in flags. */
1022 	if (data.size > (p - (char *)data.data)) {
1023 		bcopy(p, (char *)flagsp, sizeof(int));
1024 		p += sizeof(int);
1025 	} else
1026 		*flagsp = _PASSWORD_NOUID|_PASSWORD_NOGID;	/* default */
1027 	return (1);
1028 }
1029