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