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