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