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