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