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