xref: /openbsd-src/lib/libc/gen/getpwent.c (revision d13be5d47e4149db2549a9828e244d59dbc43f15)
1 /*	$OpenBSD: getpwent.c,v 1.43 2011/09/05 03:52:24 guenther 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 
450 	key.data = (u_char *)_PW_YPTOKEN;
451 	key.size = strlen(_PW_YPTOKEN);
452 
453 	/* Pre-token database support. */
454 	bf[0] = _PW_KEYBYNAME;
455 	bf[1] = '+';
456 	pkey.data = (u_char *)bf;
457 	pkey.size = sizeof(bf);
458 
459 	if ((_pw_db->get)(_pw_db, &key, &data, 0) &&
460 	    (_pw_db->get)(_pw_db, &pkey, &pdata, 0))
461 		return (0);	/* No YP. */
462 	return (1);
463 }
464 
465 /*
466  * See if there's a master.passwd map.
467  */
468 static int
469 __has_ypmaster(void)
470 {
471 	int keylen, resultlen;
472 	char *key, *result;
473 	static int checked = -1;
474 	static uid_t saved_uid, saved_euid;
475 	uid_t uid = getuid(), euid = geteuid();
476 
477 	/*
478 	 * Do not recheck IFF the saved UID and the saved
479 	 * EUID are the same. In all other cases, recheck.
480 	 */
481 	if (checked != -1 && saved_uid == uid && saved_euid == euid)
482 		return (checked);
483 
484 	if (euid != 0) {
485 		saved_uid = uid;
486 		saved_euid = euid;
487 		checked = 0;
488 		return (checked);
489 	}
490 
491 	if (!__ypdomain) {
492 		if (_yp_check(&__ypdomain) == 0) {
493 			saved_uid = uid;
494 			saved_euid = euid;
495 			checked = 0;
496 			return (checked);	/* No domain. */
497 		}
498 	}
499 
500 	if (yp_first(__ypdomain, "master.passwd.byname",
501 	    &key, &keylen, &result, &resultlen)) {
502 		saved_uid = uid;
503 		saved_euid = euid;
504 		checked = 0;
505 		return (checked);
506 	}
507 	free(result);
508 	if (key)
509 		free(key);
510 
511 	saved_uid = uid;
512 	saved_euid = euid;
513 	checked = 1;
514 	return (checked);
515 }
516 
517 static struct passwd *
518 __yppwlookup(int lookup, char *name, uid_t uid, struct passwd *pw,
519     char *buf, size_t buflen, int *flagsp)
520 {
521 	char bf[1 + _PW_NAME_LEN], *ypcurrent = NULL, *map = NULL;
522 	int yp_pw_flags = 0, ypcurrentlen, r, s = -1, pw_keynum;
523 	static long yppbuf[_PW_BUF_LEN / sizeof(long)];
524 	struct _ypexclude *ypexhead = NULL;
525 	const char *host, *user, *dom;
526 	DBT key;
527 
528 	for (pw_keynum = 1; pw_keynum; pw_keynum++) {
529 		bf[0] = _PW_KEYBYNUM;
530 		bcopy((char *)&pw_keynum, &bf[1], sizeof(pw_keynum));
531 		key.data = (u_char *)bf;
532 		key.size = 1 + sizeof(pw_keynum);
533 		if (__hashpw(&key, buf, buflen, pw, flagsp) == 0)
534 			break;
535 		switch (pw->pw_name[0]) {
536 		case '+':
537 			if (!__ypdomain) {
538 				if (_yp_check(&__ypdomain) == 0)
539 					continue;
540 			}
541 			__ypproto_set(pw, yppbuf, *flagsp, &yp_pw_flags);
542 			if (!map) {
543 				if (lookup == LOOKUP_BYNAME) {
544 					map = PASSWD_BYNAME;
545 					name = strdup(name);
546 				} else {
547 					map = PASSWD_BYUID;
548 					asprintf(&name, "%u", uid);
549 				}
550 			}
551 
552 			switch (pw->pw_name[1]) {
553 			case '\0':
554 				if (ypcurrent) {
555 					free(ypcurrent);
556 					ypcurrent = NULL;
557 				}
558 				r = yp_match(__ypdomain, map,
559 				    name, strlen(name),
560 				    &ypcurrent, &ypcurrentlen);
561 				if (r != 0 || ypcurrentlen > buflen) {
562 					if (ypcurrent)
563 						free(ypcurrent);
564 					ypcurrent = NULL;
565 					continue;
566 				}
567 				break;
568 			case '@':
569 pwnam_netgrp:
570 				if (ypcurrent) {
571 					free(ypcurrent);
572 					ypcurrent = NULL;
573 				}
574 				if (s == -1)	/* first time */
575 					setnetgrent(pw->pw_name + 2);
576 				s = getnetgrent(&host, &user, &dom);
577 				if (s == 0) {	/* end of group */
578 					endnetgrent();
579 					s = -1;
580 					continue;
581 				} else {
582 					if (user && *user) {
583 						r = yp_match(__ypdomain, map,
584 						    user, strlen(user),
585 						    &ypcurrent, &ypcurrentlen);
586 					} else
587 						goto pwnam_netgrp;
588 					if (r != 0 || ypcurrentlen > buflen) {
589 						if (ypcurrent)
590 							free(ypcurrent);
591 						ypcurrent = NULL;
592 						/*
593 						 * just because this
594 						 * user is bad, doesn't
595 						 * mean they all are.
596 						 */
597 						goto pwnam_netgrp;
598 					}
599 				}
600 				break;
601 			default:
602 				if (ypcurrent) {
603 					free(ypcurrent);
604 					ypcurrent = NULL;
605 				}
606 				user = pw->pw_name + 1;
607 				r = yp_match(__ypdomain, map,
608 				    user, strlen(user),
609 				    &ypcurrent, &ypcurrentlen);
610 				if (r != 0 || ypcurrentlen > buflen) {
611 					if (ypcurrent)
612 						free(ypcurrent);
613 					ypcurrent = NULL;
614 					continue;
615 				}
616 				break;
617 			}
618 			bcopy(ypcurrent, buf, ypcurrentlen);
619 			buf[ypcurrentlen] = '\0';
620 			if (__ypparse(pw, buf, yp_pw_flags) ||
621 			    __ypexclude_is(&ypexhead, pw->pw_name)) {
622 				if (s == 1)	/* inside netgrp */
623 					goto pwnam_netgrp;
624 				continue;
625 			}
626 			break;
627 		case '-':
628 			/* attempted exclusion */
629 			switch (pw->pw_name[1]) {
630 			case '\0':
631 				break;
632 			case '@':
633 				setnetgrent(pw->pw_name + 2);
634 				while (getnetgrent(&host, &user, &dom)) {
635 					if (user && *user)
636 						__ypexclude_add(&ypexhead, user);
637 				}
638 				endnetgrent();
639 				break;
640 			default:
641 				__ypexclude_add(&ypexhead, pw->pw_name + 1);
642 				break;
643 			}
644 			break;
645 		}
646 		if ((lookup == LOOKUP_BYUID && pw->pw_uid == uid) ||
647 		    (lookup == LOOKUP_BYNAME && strcmp(pw->pw_name, name) == 0))
648 			goto done;
649 		if (s == 1)	/* inside netgrp */
650 			goto pwnam_netgrp;
651 		continue;
652 	}
653 	pw = NULL;
654 done:
655 	__ypexclude_free(&ypexhead);
656 	__ypproto = NULL;
657 	if (ypcurrent)
658 		free(ypcurrent);
659 	ypcurrent = NULL;
660 	if (map)
661 		free(name);
662 	return (pw);
663 }
664 #endif /* YP */
665 
666 static struct passwd *
667 _pwhashbyname(const char *name, char *buf, size_t buflen, struct passwd *pw,
668     int *flagsp)
669 {
670 	char bf[1 + _PW_NAME_LEN];
671 	int len, r;
672 	DBT key;
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 	DB *savedb;
712 
713 	_THREAD_PRIVATE_MUTEX_LOCK(pw);
714 	savedb = _pw_db;
715 	if (!_pw_db && !__initdb())
716 		goto fail;
717 
718 	if (pw == &_pw_passwd)
719 		flagsp = &_pw_flags;
720 	else
721 		flagsp = &flags;
722 
723 #ifdef YP
724 	if (__has_yppw())
725 		pwret = __yppwlookup(LOOKUP_BYNAME, (char *)name, 0, pw,
726 		    buf, buflen, flagsp);
727 #endif /* YP */
728 	if (!pwret)
729 		pwret = _pwhashbyname(name, buf, buflen, pw, flagsp);
730 
731 	if (savedb != _pw_db || !_pw_stayopen) {
732 		(void)(_pw_db->close)(_pw_db);
733 		_pw_db = NULL;
734 	}
735 fail:
736 	if (pwretp)
737 		*pwretp = pwret;
738 	_THREAD_PRIVATE_MUTEX_UNLOCK(pw);
739 	return (pwret ? 0 : 1);
740 }
741 
742 struct passwd *
743 getpwnam(const char *name)
744 {
745 	struct passwd *pw = NULL;
746 
747 	if (getpwnam_r(name, &_pw_passwd, _pw_string, sizeof _pw_string, &pw))
748 		pw = NULL;
749 	return (pw);
750 }
751 
752 int
753 getpwuid_r(uid_t uid, struct passwd *pw, char *buf, size_t buflen,
754     struct passwd **pwretp)
755 {
756 	struct passwd *pwret = NULL;
757 	int flags = 0, *flagsp;
758 	DB *savedb;
759 
760 	_THREAD_PRIVATE_MUTEX_LOCK(pw);
761 	savedb = _pw_db;
762 	if (!_pw_db && !__initdb())
763 		goto fail;
764 
765 	if (pw == &_pw_passwd)
766 		flagsp = &_pw_flags;
767 	else
768 		flagsp = &flags;
769 
770 #ifdef YP
771 	if (__has_yppw())
772 		pwret = __yppwlookup(LOOKUP_BYUID, NULL, uid, pw,
773 		    buf, buflen, flagsp);
774 #endif /* YP */
775 	if (!pwret)
776 		pwret = _pwhashbyuid(uid, buf, buflen, pw, flagsp);
777 
778 	if (savedb != _pw_db || !_pw_stayopen) {
779 		(void)(_pw_db->close)(_pw_db);
780 		_pw_db = NULL;
781 	}
782 fail:
783 	if (pwretp)
784 		*pwretp = pwret;
785 	_THREAD_PRIVATE_MUTEX_UNLOCK(pw);
786 	return (pwret ? 0 : 1);
787 }
788 
789 struct passwd *
790 getpwuid(uid_t uid)
791 {
792 	struct passwd *pw = NULL;
793 
794 	if (getpwuid_r(uid, &_pw_passwd, _pw_string, sizeof _pw_string, &pw))
795 		pw = NULL;
796 	return (pw);
797 }
798 
799 int
800 setpassent(int stayopen)
801 {
802 	_THREAD_PRIVATE_MUTEX_LOCK(pw);
803 	_pw_keynum = 0;
804 	_pw_stayopen = stayopen;
805 #ifdef YP
806 	__ypmode = YPMODE_NONE;
807 	if (__ypcurrent)
808 		free(__ypcurrent);
809 	__ypcurrent = NULL;
810 	__ypexclude_free(&__ypexhead);
811 	__ypproto = NULL;
812 #endif
813 	_THREAD_PRIVATE_MUTEX_UNLOCK(pw);
814 	return (1);
815 }
816 
817 void
818 setpwent(void)
819 {
820 	(void) setpassent(0);
821 }
822 
823 void
824 endpwent(void)
825 {
826 	_THREAD_PRIVATE_MUTEX_LOCK(pw);
827 	_pw_keynum = 0;
828 	if (_pw_db) {
829 		(void)(_pw_db->close)(_pw_db);
830 		_pw_db = NULL;
831 	}
832 #ifdef YP
833 	__ypmode = YPMODE_NONE;
834 	if (__ypcurrent)
835 		free(__ypcurrent);
836 	__ypcurrent = NULL;
837 	__ypexclude_free(&__ypexhead);
838 	__ypproto = NULL;
839 #endif
840 	_THREAD_PRIVATE_MUTEX_UNLOCK(pw);
841 }
842 
843 static int
844 __initdb(void)
845 {
846 	static int warned;
847 	int saved_errno = errno;
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 		errno = saved_errno;
856 		return (1);
857 	}
858 	if (!warned)
859 		syslog(LOG_ERR, "%s: %m", _PATH_MP_DB);
860 	warned = 1;
861 	return (0);
862 }
863 
864 static int
865 __hashpw(DBT *key, char *buf, size_t buflen, struct passwd *pw,
866     int *flagsp)
867 {
868 	char *p, *t;
869 	DBT data;
870 
871 	if ((_pw_db->get)(_pw_db, key, &data, 0))
872 		return (0);
873 	p = (char *)data.data;
874 	if (data.size > buflen)
875 		return (0);
876 
877 	t = buf;
878 #define	EXPAND(e)	e = t; while ((*t++ = *p++));
879 	EXPAND(pw->pw_name);
880 	EXPAND(pw->pw_passwd);
881 	bcopy(p, (char *)&pw->pw_uid, sizeof(int));
882 	p += sizeof(int);
883 	bcopy(p, (char *)&pw->pw_gid, sizeof(int));
884 	p += sizeof(int);
885 	bcopy(p, (char *)&pw->pw_change, sizeof(time_t));
886 	p += sizeof(time_t);
887 	EXPAND(pw->pw_class);
888 	EXPAND(pw->pw_gecos);
889 	EXPAND(pw->pw_dir);
890 	EXPAND(pw->pw_shell);
891 	bcopy(p, (char *)&pw->pw_expire, sizeof(time_t));
892 	p += sizeof(time_t);
893 
894 	/* See if there's any data left.  If so, read in flags. */
895 	if (data.size > (p - (char *)data.data)) {
896 		bcopy(p, (char *)flagsp, sizeof(int));
897 		p += sizeof(int);
898 	} else
899 		*flagsp = _PASSWORD_NOUID|_PASSWORD_NOGID;	/* default */
900 	return (1);
901 }
902