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