xref: /openbsd-src/lib/libc/gen/getpwent.c (revision 850e275390052b330d93020bf619a739a3c277ac)
1 /*	$OpenBSD: getpwent.c,v 1.38 2008/07/23 19:36:47 deraadt Exp $ */
2 /*
3  * Copyright (c) 2008 Theo de Raadt
4  * Copyright (c) 1988, 1993
5  *	The Regents of the University of California.  All rights reserved.
6  * Portions Copyright (c) 1994, 1995, 1996, Jason Downs.  All rights reserved.
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted provided that the following conditions
10  * are met:
11  * 1. Redistributions of source code must retain the above copyright
12  *    notice, this list of conditions and the following disclaimer.
13  * 2. Redistributions in binary form must reproduce the above copyright
14  *    notice, this list of conditions and the following disclaimer in the
15  *    documentation and/or other materials provided with the distribution.
16  * 3. Neither the name of the University nor the names of its contributors
17  *    may be used to endorse or promote products derived from this software
18  *    without specific prior written permission.
19  *
20  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
21  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
24  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30  * SUCH DAMAGE.
31  */
32 
33 #include <sys/param.h>
34 #include <fcntl.h>
35 #include <db.h>
36 #include <syslog.h>
37 #include <pwd.h>
38 #include <errno.h>
39 #include <unistd.h>
40 #include <stdlib.h>
41 #include <string.h>
42 #include <limits.h>
43 #include <netgroup.h>
44 #ifdef YP
45 #include <machine/param.h>
46 #include <stdio.h>
47 #include <rpc/rpc.h>
48 #include <rpcsvc/yp.h>
49 #include <rpcsvc/ypclnt.h>
50 #include "ypinternal.h"
51 #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;
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 	if (lookup == LOOKUP_BYNAME) {
588 		map = PASSWD_BYNAME;
589 		name = strdup(name);
590 	} else {
591 		map = PASSWD_BYUID;
592 		asprintf(&name, "%u", uid);
593 	}
594 
595 	for (pw_keynum = 1; pw_keynum; pw_keynum++) {
596 		bf[0] = _PW_KEYBYNUM;
597 		bcopy((char *)&pw_keynum, &bf[1], sizeof(pw_keynum));
598 		key.data = (u_char *)bf;
599 		key.size = 1 + sizeof(pw_keynum);
600 		if (__hashpw(&key, buf, buflen, pw, flagsp) == 0)
601 			break;
602 		switch (pw->pw_name[0]) {
603 		case '+':
604 			if (!__ypdomain) {
605 				if (_yp_check(&__ypdomain) == 0)
606 					continue;
607 			}
608 			__ypproto_set(pw, yppbuf, *flagsp, &yp_pw_flags);
609 
610 			switch (pw->pw_name[1]) {
611 			case '\0':
612 				if (ypcurrent) {
613 					free(ypcurrent);
614 					ypcurrent = NULL;
615 				}
616 				r = yp_match(__ypdomain, map,
617 				    name, strlen(name),
618 				    &ypcurrent, &ypcurrentlen);
619 				if (r != 0 || ypcurrentlen > buflen) {
620 					if (ypcurrent)
621 						free(ypcurrent);
622 					ypcurrent = NULL;
623 					continue;
624 				}
625 				break;
626 			case '@':
627 pwnam_netgrp:
628 				if (ypcurrent) {
629 					free(ypcurrent);
630 					ypcurrent = NULL;
631 				}
632 				if (s == -1)	/* first time */
633 					setnetgrent(pw->pw_name + 2);
634 				s = getnetgrent(&host, &user, &dom);
635 				if (s == 0) {	/* end of group */
636 					endnetgrent();
637 					s = -1;
638 					continue;
639 				} else {
640 					if (user && *user) {
641 						r = yp_match(__ypdomain, map,
642 						    user, strlen(user),
643 						    &ypcurrent, &ypcurrentlen);
644 					} else
645 						goto pwnam_netgrp;
646 					if (r != 0 || ypcurrentlen > buflen) {
647 						if (ypcurrent)
648 							free(ypcurrent);
649 						ypcurrent = NULL;
650 						/*
651 						 * just because this
652 						 * user is bad, doesn't
653 						 * mean they all are.
654 						 */
655 						goto pwnam_netgrp;
656 					}
657 				}
658 				break;
659 			default:
660 				if (ypcurrent) {
661 					free(ypcurrent);
662 					ypcurrent = NULL;
663 				}
664 				user = pw->pw_name + 1;
665 				r = yp_match(__ypdomain, map,
666 				    user, strlen(user),
667 				    &ypcurrent, &ypcurrentlen);
668 				if (r != 0 || ypcurrentlen > buflen) {
669 					if (ypcurrent)
670 						free(ypcurrent);
671 					ypcurrent = NULL;
672 					continue;
673 				}
674 				break;
675 			}
676 			bcopy(ypcurrent, buf, ypcurrentlen);
677 			buf[ypcurrentlen] = '\0';
678 			if (__ypparse(pw, buf, yp_pw_flags) ||
679 			    __ypexclude_is(&ypexhead, pw->pw_name)) {
680 				if (s == 1)	/* inside netgrp */
681 					goto pwnam_netgrp;
682 				continue;
683 			}
684 			break;
685 		case '-':
686 			/* attempted exclusion */
687 			switch (pw->pw_name[1]) {
688 			case '\0':
689 				break;
690 			case '@':
691 				setnetgrent(pw->pw_name + 2);
692 				while (getnetgrent(&host, &user, &dom)) {
693 					if (user && *user)
694 						__ypexclude_add(&ypexhead, user);
695 				}
696 				endnetgrent();
697 				break;
698 			default:
699 				__ypexclude_add(&ypexhead, pw->pw_name + 1);
700 				break;
701 			}
702 			break;
703 		}
704 		if ((lookup == LOOKUP_BYUID && pw->pw_uid == uid) ||
705 		    (lookup == LOOKUP_BYNAME && strcmp(pw->pw_name, name) == 0))
706 			goto done;
707 		if (s == 1)	/* inside netgrp */
708 			goto pwnam_netgrp;
709 		continue;
710 	}
711 	pw = NULL;
712 done:
713 	__ypexclude_free(&ypexhead);
714 	__ypproto = NULL;
715 	if (ypcurrent)
716 		free(ypcurrent);
717 	ypcurrent = NULL;
718 	free(name);
719 	return (pw);
720 }
721 #endif /* YP */
722 
723 static struct passwd *
724 _pwhashbyname(const char *name, char *buf, size_t buflen, struct passwd *pw,
725     int *flagsp)
726 {
727 	char bf[1 + _PW_NAME_LEN];
728 	int len, r;
729 	DBT key;
730 
731 	len = strlen(name);
732 	if (len > _PW_NAME_LEN)
733 		return (NULL);
734 	bf[0] = _PW_KEYBYNAME;
735 	bcopy(name, &bf[1], MIN(len, _PW_NAME_LEN));
736 	key.data = (u_char *)bf;
737 	key.size = 1 + MIN(len, _PW_NAME_LEN);
738 	r = __hashpw(&key, buf, buflen, pw, flagsp);
739 	if (r)
740 		return (pw);
741 	return (NULL);
742 }
743 
744 static struct passwd *
745 _pwhashbyuid(uid_t uid, char *buf, size_t buflen, struct passwd *pw,
746     int *flagsp)
747 {
748 	char bf[1 + sizeof(int)];
749 	DBT key;
750 	int r;
751 
752 	bf[0] = _PW_KEYBYUID;
753 	bcopy(&uid, &bf[1], sizeof(uid));
754 	key.data = (u_char *)bf;
755 	key.size = 1 + sizeof(uid);
756 	r = __hashpw(&key, buf, buflen, pw, flagsp);
757 	if (r)
758 		return (pw);
759 	return (NULL);
760 }
761 
762 int
763 getpwnam_r(const char *name, struct passwd *pw, char *buf, size_t buflen,
764     struct passwd **pwretp)
765 {
766 	struct passwd *pwret = NULL;
767 	int flags = 0, *flagsp;
768 	DB *savedb;
769 
770 	_THREAD_PRIVATE_MUTEX_LOCK(pw);
771 	savedb = _pw_db;
772 	if (!_pw_db && !__initdb())
773 		goto fail;
774 
775 	if (pw == &_pw_passwd)
776 		flagsp = &_pw_flags;
777 	else
778 		flagsp = &flags;
779 
780 #ifdef YP
781 	if (__has_yppw())
782 		pwret = __yppwlookup(LOOKUP_BYNAME, (char *)name, 0, pw,
783 		    buf, buflen, flagsp);
784 #endif /* YP */
785 	if (!pwret)
786 		pwret = _pwhashbyname(name, buf, buflen, pw, flagsp);
787 
788 	if (savedb != _pw_db || !_pw_stayopen) {
789 		(void)(_pw_db->close)(_pw_db);
790 		_pw_db = NULL;
791 	}
792 fail:
793 	if (pwretp)
794 		*pwretp = pwret;
795 	_THREAD_PRIVATE_MUTEX_UNLOCK(pw);
796 	return (pwret ? 0 : 1);
797 }
798 
799 struct passwd *
800 getpwnam(const char *name)
801 {
802 	struct passwd *pw = NULL;
803 
804 	if (getpwnam_r(name, &_pw_passwd, _pw_string, sizeof _pw_string, &pw))
805 		pw = NULL;
806 	return (pw);
807 }
808 
809 int
810 getpwuid_r(uid_t uid, struct passwd *pw, char *buf, size_t buflen,
811     struct passwd **pwretp)
812 {
813 	struct passwd *pwret = NULL;
814 	int flags = 0, *flagsp;
815 	DB *savedb;
816 
817 	_THREAD_PRIVATE_MUTEX_LOCK(pw);
818 	savedb = _pw_db;
819 	if (!_pw_db && !__initdb())
820 		goto fail;
821 
822 	if (pw == &_pw_passwd)
823 		flagsp = &_pw_flags;
824 	else
825 		flagsp = &flags;
826 
827 #ifdef YP
828 	if (__has_yppw())
829 		pwret = __yppwlookup(LOOKUP_BYUID, NULL, uid, pw,
830 		    buf, buflen, flagsp);
831 #endif /* YP */
832 	if (!pwret)
833 		pwret = _pwhashbyuid(uid, buf, buflen, pw, flagsp);
834 
835 	if (savedb != _pw_db || !_pw_stayopen) {
836 		(void)(_pw_db->close)(_pw_db);
837 		_pw_db = NULL;
838 	}
839 fail:
840 	if (pwretp)
841 		*pwretp = pwret;
842 	_THREAD_PRIVATE_MUTEX_UNLOCK(pw);
843 	return (pwret ? 0 : 1);
844 }
845 
846 struct passwd *
847 getpwuid(uid_t uid)
848 {
849 	struct passwd *pw = NULL;
850 
851 	if (getpwuid_r(uid, &_pw_passwd, _pw_string, sizeof _pw_string, &pw))
852 		pw = NULL;
853 	return (pw);
854 }
855 
856 int
857 setpassent(int stayopen)
858 {
859 	_THREAD_PRIVATE_MUTEX_LOCK(pw);
860 	_pw_keynum = 0;
861 	_pw_stayopen = stayopen;
862 #ifdef YP
863 	__ypmode = YPMODE_NONE;
864 	if (__ypcurrent)
865 		free(__ypcurrent);
866 	__ypcurrent = NULL;
867 	__ypexclude_free(&__ypexhead);
868 	__ypproto = NULL;
869 #endif
870 	_THREAD_PRIVATE_MUTEX_UNLOCK(pw);
871 	return (1);
872 }
873 
874 void
875 setpwent(void)
876 {
877 	(void) setpassent(0);
878 }
879 
880 void
881 endpwent(void)
882 {
883 	_THREAD_PRIVATE_MUTEX_LOCK(pw);
884 	_pw_keynum = 0;
885 	if (_pw_db) {
886 		(void)(_pw_db->close)(_pw_db);
887 		_pw_db = NULL;
888 	}
889 #ifdef YP
890 	__ypmode = YPMODE_NONE;
891 	if (__ypcurrent)
892 		free(__ypcurrent);
893 	__ypcurrent = NULL;
894 	__ypexclude_free(&__ypexhead);
895 	__ypproto = NULL;
896 #endif
897 	_THREAD_PRIVATE_MUTEX_UNLOCK(pw);
898 }
899 
900 static int
901 __initdb(void)
902 {
903 	static int warned;
904 
905 #ifdef YP
906 	__ypmode = YPMODE_NONE;
907 	__getpwent_has_yppw = -1;
908 #endif
909 	if ((_pw_db = dbopen(_PATH_SMP_DB, O_RDONLY, 0, DB_HASH, NULL)) ||
910 	    (_pw_db = dbopen(_PATH_MP_DB, O_RDONLY, 0, DB_HASH, NULL)))
911 		return (1);
912 	if (!warned)
913 		syslog(LOG_ERR, "%s: %m", _PATH_MP_DB);
914 	warned = 1;
915 	return (0);
916 }
917 
918 static int
919 __hashpw(DBT *key, char *buf, size_t buflen, struct passwd *pw,
920     int *flagsp)
921 {
922 	char *p, *t;
923 	DBT data;
924 
925 	if ((_pw_db->get)(_pw_db, key, &data, 0))
926 		return (0);
927 	p = (char *)data.data;
928 	if (data.size > buflen)
929 		return (0);
930 
931 	t = buf;
932 #define	EXPAND(e)	e = t; while ((*t++ = *p++));
933 	EXPAND(pw->pw_name);
934 	EXPAND(pw->pw_passwd);
935 	bcopy(p, (char *)&pw->pw_uid, sizeof(int));
936 	p += sizeof(int);
937 	bcopy(p, (char *)&pw->pw_gid, sizeof(int));
938 	p += sizeof(int);
939 	bcopy(p, (char *)&pw->pw_change, sizeof(time_t));
940 	p += sizeof(time_t);
941 	EXPAND(pw->pw_class);
942 	EXPAND(pw->pw_gecos);
943 	EXPAND(pw->pw_dir);
944 	EXPAND(pw->pw_shell);
945 	bcopy(p, (char *)&pw->pw_expire, sizeof(time_t));
946 	p += sizeof(time_t);
947 
948 	/* See if there's any data left.  If so, read in flags. */
949 	if (data.size > (p - (char *)data.data)) {
950 		bcopy(p, (char *)flagsp, sizeof(int));
951 		p += sizeof(int);
952 	} else
953 		*flagsp = _PASSWORD_NOUID|_PASSWORD_NOGID;	/* default */
954 	return (1);
955 }
956