xref: /openbsd-src/lib/libc/gen/getpwent.c (revision b2ea75c1b17e1a9a339660e7ed45cd24946b230e)
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. All advertising materials mentioning features or use of this software
15  *    must display the following acknowledgement:
16  *	This product includes software developed by the University of
17  *	California, Berkeley and its contributors.
18  * 4. Neither the name of the University nor the names of its contributors
19  *    may be used to endorse or promote products derived from this software
20  *    without specific prior written permission.
21  *
22  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
23  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
24  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
25  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
26  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
27  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
28  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
29  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
30  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
31  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
32  * SUCH DAMAGE.
33  */
34 
35 #if defined(LIBC_SCCS) && !defined(lint)
36 static char rcsid[] = "$OpenBSD: getpwent.c,v 1.22 2001/07/10 16:46:25 deraadt Exp $";
37 #endif /* LIBC_SCCS and not lint */
38 
39 #include <sys/param.h>
40 #include <fcntl.h>
41 #include <db.h>
42 #include <syslog.h>
43 #include <pwd.h>
44 #include <errno.h>
45 #include <unistd.h>
46 #include <stdlib.h>
47 #include <string.h>
48 #include <limits.h>
49 #include <netgroup.h>
50 #ifdef YP
51 #include <machine/param.h>
52 #include <stdio.h>
53 #include <rpc/rpc.h>
54 #include <rpcsvc/yp.h>
55 #include <rpcsvc/ypclnt.h>
56 #include "ypinternal.h"
57 #endif
58 
59 static struct passwd _pw_passwd;	/* password structure */
60 static DB *_pw_db;			/* password database */
61 static int _pw_keynum;			/* key counter */
62 static int _pw_stayopen;		/* keep fd's open */
63 static int _pw_flags;			/* password flags */
64 static int __hashpw __P((DBT *));
65 static int __initdb __P((void));
66 
67 #ifdef YP
68 enum _ypmode { YPMODE_NONE, YPMODE_FULL, YPMODE_USER, YPMODE_NETGRP };
69 static enum _ypmode __ypmode;
70 
71 static char     *__ypcurrent, *__ypdomain;
72 static int      __ypcurrentlen;
73 static struct passwd *__ypproto = (struct passwd *)NULL;
74 static int	__ypflags;
75 static char	__ypline[1024];
76 static long	__yppbuf[1024 / sizeof(long)];
77 static int	__yp_override_passwd = 0;
78 
79 static int __has_yppw __P((void));
80 static int __has_ypmaster __P((void));
81 
82 static int __ypexclude_add __P((const char *));
83 static int __ypexclude_is __P((const char *));
84 static void __ypexclude_free __P((void));
85 static void __ypproto_set __P((void));
86 
87 /* macro for deciding which YP maps to use. */
88 #define PASSWD_BYNAME \
89 	__has_ypmaster() ? "master.passwd.byname" : "passwd.byname"
90 #define PASSWD_BYUID \
91 	__has_ypmaster() ? "master.passwd.byuid" : "passwd.byuid"
92 
93 struct _ypexclude {
94 	const char *name;
95 	struct _ypexclude *next;
96 };
97 static struct _ypexclude *__ypexclude = (struct _ypexclude *)NULL;
98 
99 /*
100  * Using DB for this just wastes too damn much memory.
101  */
102 static int
103 __ypexclude_add(name)
104 	const char *name;
105 {
106 	struct _ypexclude *new;
107 
108 	if (name[0] == '\0')	/* skip */
109 		return (0);
110 
111 	new = (struct _ypexclude *)malloc(sizeof(struct _ypexclude));
112 	if (new == (struct _ypexclude *)NULL)
113 		return (1);
114 	new->name = strdup(name);
115 	if (new->name == (char *)NULL) {
116 		free(new);
117 		return (1);
118 	}
119 
120 	new->next = __ypexclude;
121 	__ypexclude = new;
122 
123 	return (0);
124 }
125 
126 static int
127 __ypexclude_is(name)
128 	const char *name;
129 {
130 	struct _ypexclude *curr;
131 
132 	for (curr = __ypexclude; curr != (struct _ypexclude *)NULL;
133 	     curr = curr->next) {
134 		if (strcmp(curr->name, name) == 0)
135 			return (1);	/* excluded */
136 	}
137 	return (0);
138 }
139 
140 static void
141 __ypexclude_free()
142 {
143 	struct _ypexclude *curr, *next;
144 
145 	for (curr = __ypexclude; curr != (struct _ypexclude *)NULL;
146 	     curr = next) {
147 		next = curr->next;
148 
149 		free((void *)curr->name);
150 		free(curr);
151 	}
152 	__ypexclude = (struct _ypexclude *)NULL;
153 }
154 
155 static void
156 __ypproto_set()
157 {
158 	register char *ptr;
159 	register struct passwd *pw = &_pw_passwd;
160 
161 	/* make this the new prototype */
162 	ptr = (char *)__yppbuf;
163 
164 	/* first allocate the struct. */
165 	__ypproto = (struct passwd *)ptr;
166 	ptr += sizeof(struct passwd);
167 
168 	/* name */
169 	if (pw->pw_name && (pw->pw_name)[0]) {
170 		ptr = (char *)ALIGN(ptr);
171 		bcopy(pw->pw_name, ptr, strlen(pw->pw_name) + 1);
172 		__ypproto->pw_name = ptr;
173 		ptr += (strlen(pw->pw_name) + 1);
174 	} else
175 		__ypproto->pw_name = (char *)NULL;
176 
177 	/* password */
178 	if (pw->pw_passwd && (pw->pw_passwd)[0]) {
179 		ptr = (char *)ALIGN(ptr);
180 		bcopy(pw->pw_passwd, ptr, strlen(pw->pw_passwd) + 1);
181 		__ypproto->pw_passwd = ptr;
182 		ptr += (strlen(pw->pw_passwd) + 1);
183 	} else
184 		__ypproto->pw_passwd = (char *)NULL;
185 
186 	/* uid */
187 	__ypproto->pw_uid = pw->pw_uid;
188 
189 	/* gid */
190 	__ypproto->pw_gid = pw->pw_gid;
191 
192 	/* change (ignored anyway) */
193 	__ypproto->pw_change = pw->pw_change;
194 
195 	/* class (ignored anyway) */
196 	__ypproto->pw_class = "";
197 
198 	/* gecos */
199 	if (pw->pw_gecos && (pw->pw_gecos)[0]) {
200 		ptr = (char *)ALIGN(ptr);
201 		bcopy(pw->pw_gecos, ptr, strlen(pw->pw_gecos) + 1);
202 		__ypproto->pw_gecos = ptr;
203 		ptr += (strlen(pw->pw_gecos) + 1);
204 	} else
205 		__ypproto->pw_gecos = (char *)NULL;
206 
207 	/* dir */
208 	if (pw->pw_dir && (pw->pw_dir)[0]) {
209 		ptr = (char *)ALIGN(ptr);
210 		bcopy(pw->pw_dir, ptr, strlen(pw->pw_dir) + 1);
211 		__ypproto->pw_dir = ptr;
212 		ptr += (strlen(pw->pw_dir) + 1);
213 	} else
214 		__ypproto->pw_dir = (char *)NULL;
215 
216 	/* shell */
217 	if (pw->pw_shell && (pw->pw_shell)[0]) {
218 		ptr = (char *)ALIGN(ptr);
219 		bcopy(pw->pw_shell, ptr, strlen(pw->pw_shell) + 1);
220 		__ypproto->pw_shell = ptr;
221 		ptr += (strlen(pw->pw_shell) + 1);
222 	} else
223 		__ypproto->pw_shell = (char *)NULL;
224 
225 	/* expire (ignored anyway) */
226 	__ypproto->pw_expire = pw->pw_expire;
227 
228 	/* flags */
229 	__ypflags = _pw_flags;
230 }
231 
232 static int
233 __ypparse(pw, s)
234 struct passwd *pw;
235 char *s;
236 {
237 	char *bp, *cp, *endp;
238 	u_long ul;
239 	int count = 0;
240 
241 	/* count the colons. */
242 	bp = s;
243 	while (*bp != '\0') {
244 		if (*bp++ == ':')
245 			count++;
246 	}
247 
248 	/* since this is currently using strsep(), parse it first */
249 	bp = s;
250 	pw->pw_name = strsep(&bp, ":\n");
251 	pw->pw_passwd = strsep(&bp, ":\n");
252 	if (!(cp = strsep(&bp, ":\n")))
253 		return 1;
254 	ul = strtoul(cp, &endp, 10);
255 	if (endp == cp || *endp != '\0' || ul >= UID_MAX)
256 		return 1;
257 	pw->pw_uid = (uid_t)ul;
258 	if (!(cp = strsep(&bp, ":\n")))
259 		return 1;
260 	ul = strtoul(cp, &endp, 10);
261 	if (endp == cp || *endp != '\0' || ul >= GID_MAX)
262 		return 1;
263 	pw->pw_gid = (gid_t)ul;
264 	if (count == 9) {
265 		long l;
266 
267 		/* If the ypserv gave us all the fields, use them. */
268 		pw->pw_class = strsep(&bp, ":\n");
269 		if (!(cp = strsep(&bp, ":\n")))
270 			return 1;
271 		l = strtol(cp, &endp, 10);
272 		if (endp == cp || *endp != '\0' || l >= INT_MAX || l <= INT_MIN)
273 			return 1;
274 		pw->pw_change = (time_t)l;
275 		if (!(cp = strsep(&bp, ":\n")))
276 			return 1;
277 		l = strtol(cp, &endp, 10);
278 		if (endp == cp || *endp != '\0' || l >= INT_MAX || l <= INT_MIN)
279 			return 1;
280 		pw->pw_expire = (time_t)l;
281 	} else {
282 		/* ..else it is a normal ypserv. */
283 		pw->pw_class = "";
284 		pw->pw_change = 0;
285 		pw->pw_expire = 0;
286 	}
287 	pw->pw_gecos = strsep(&bp, ":\n");
288 	pw->pw_dir = strsep(&bp, ":\n");
289 	pw->pw_shell = strsep(&bp, ":\n");
290 
291 	/* now let the prototype override, if set. */
292 	if (__ypproto != (struct passwd *)NULL) {
293 		if (__yp_override_passwd && __ypproto->pw_passwd != (char *)NULL)
294 			pw->pw_passwd = __ypproto->pw_passwd;
295 		if (!(__ypflags & _PASSWORD_NOUID))
296 			pw->pw_uid = __ypproto->pw_uid;
297 		if (!(__ypflags & _PASSWORD_NOGID))
298 			pw->pw_gid = __ypproto->pw_gid;
299 		if (__ypproto->pw_gecos != (char *)NULL)
300 			pw->pw_gecos = __ypproto->pw_gecos;
301 		if (__ypproto->pw_dir != (char *)NULL)
302 			pw->pw_dir = __ypproto->pw_dir;
303 		if (__ypproto->pw_shell != (char *)NULL)
304 			pw->pw_shell = __ypproto->pw_shell;
305 	}
306 	return 0;
307 }
308 #endif
309 
310 #ifdef YP
311 static int __getpwent_has_yppw = -1;
312 #endif
313 
314 struct passwd *
315 getpwent()
316 {
317 	DBT key;
318 	char bf[sizeof(_pw_keynum) + 1];
319 #ifdef YP
320 	static char *name = (char *)NULL;
321 	const char *user, *host, *dom;
322 #endif
323 
324 	if (!_pw_db && !__initdb())
325 		return ((struct passwd *)NULL);
326 
327 #ifdef YP
328 	if (__getpwent_has_yppw == -1)
329 		__getpwent_has_yppw = __has_yppw();
330 
331 again:
332 	if (__getpwent_has_yppw && (__ypmode != YPMODE_NONE)) {
333 		char *key, *data;
334 		int keylen, datalen;
335 		int r, s;
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, (PASSWD_BYNAME),
347 					__ypcurrent, __ypcurrentlen,
348 					&key, &keylen, &data, &datalen);
349 				free(__ypcurrent);
350 				if (r != 0) {
351 					__ypcurrent = NULL;
352 					__ypmode = YPMODE_NONE;
353 					if (data)
354 						free(data);
355 					data = NULL;
356 					goto again;
357 				}
358 				__ypcurrent = key;
359 				__ypcurrentlen = keylen;
360 				bcopy(data, __ypline, datalen);
361 				free(data);
362 				data = NULL;
363 			} else {
364 				r = yp_first(__ypdomain, (PASSWD_BYNAME),
365 					&__ypcurrent, &__ypcurrentlen,
366 					&data, &datalen);
367 				if (r != 0) {
368 					__ypmode = YPMODE_NONE;
369 					if (data)
370 						free(data);
371 					goto again;
372 				}
373 				bcopy(data, __ypline, datalen);
374 				free(data);
375 				data = NULL;
376 			}
377 			break;
378 		case YPMODE_NETGRP:
379 			s = getnetgrent(&host, &user, &dom);
380 			if (s == 0) {	/* end of group */
381 				endnetgrent();
382 				__ypmode = YPMODE_NONE;
383 				goto again;
384 			}
385 			if (user && *user) {
386 				r = yp_match(__ypdomain, (PASSWD_BYNAME),
387 					user, strlen(user),
388 					&data, &datalen);
389 			} else
390 				goto again;
391 			if (r != 0) {
392 				/*
393 				 * if the netgroup is invalid, keep looking
394 				 * as there may be valid users later on.
395 				 */
396 				if (data)
397 					free(data);
398 				goto again;
399 			}
400 			bcopy(data, __ypline, datalen);
401 			free(data);
402 			data = (char *)NULL;
403 			break;
404 		case YPMODE_USER:
405 			if (name != (char *)NULL) {
406 				r = yp_match(__ypdomain, (PASSWD_BYNAME),
407 					name, strlen(name),
408 					&data, &datalen);
409 				__ypmode = YPMODE_NONE;
410 				free(name);
411 				name = (char *)NULL;
412 				if (r != 0) {
413 					if (data)
414 						free(data);
415 					goto again;
416 				}
417 				bcopy(data, __ypline, datalen);
418 				free(data);
419 				data = (char *)NULL;
420 			} else {		/* XXX */
421 				__ypmode = YPMODE_NONE;
422 				goto again;
423 			}
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 #ifdef __STDC__
750 getpwuid(uid_t uid)
751 #else
752 getpwuid(uid)
753 	uid_t uid;
754 #endif
755 {
756 	DBT key;
757 	char bf[sizeof(_pw_keynum) + 1];
758 	uid_t keyuid;
759 	int rval;
760 
761 	if (!_pw_db && !__initdb())
762 		return ((struct passwd *)NULL);
763 
764 #ifdef YP
765 	/*
766 	 * If YP is active, we must sequence through the passwd file
767 	 * in sequence.
768 	 */
769 	if (__has_yppw()) {
770 		char uidbuf[20];
771 		int r;
772 		int s = -1;
773 		const char *host, *user, *dom;
774 
775 		sprintf(uidbuf, "%u", uid);
776 		for (_pw_keynum=1; _pw_keynum; _pw_keynum++) {
777 			bf[0] = _PW_KEYBYNUM;
778 			bcopy((char *)&_pw_keynum, bf + 1, sizeof(_pw_keynum));
779 			key.data = (u_char *)bf;
780 			key.size = sizeof(_pw_keynum) + 1;
781 			if (__hashpw(&key) == 0)
782 				break;
783 			switch (_pw_passwd.pw_name[0]) {
784 			case '+':
785 				if (!__ypdomain) {
786 					if (_yp_check(&__ypdomain) == 0) {
787 						continue;
788 					}
789 				}
790 				/* save the prototype */
791 				__ypproto_set();
792 
793 				switch (_pw_passwd.pw_name[1]) {
794 				case '\0':
795 					if (__ypcurrent) {
796 						free(__ypcurrent);
797 						__ypcurrent = NULL;
798 					}
799 					r = yp_match(__ypdomain, (PASSWD_BYUID),
800 						uidbuf, strlen(uidbuf),
801 						&__ypcurrent, &__ypcurrentlen);
802 					if (r != 0) {
803 						if (__ypcurrent)
804 							free(__ypcurrent);
805 						__ypcurrent = NULL;
806 						continue;
807 					}
808 					break;
809 				case '@':
810 pwuid_netgrp:
811 					if (__ypcurrent) {
812 						free(__ypcurrent);
813 						__ypcurrent = NULL;
814 					}
815 					if (s == -1)	/* first time */
816 						setnetgrent(_pw_passwd.pw_name + 2);
817 					s = getnetgrent(&host, &user, &dom);
818 					if (s == 0) {	/* end of group */
819 						endnetgrent();
820 						s = -1;
821 						continue;
822 					} else {
823 						if (user && *user) {
824 							r = yp_match(__ypdomain,
825 							    (PASSWD_BYNAME),
826 							    user, strlen(user),
827 							    &__ypcurrent,
828 							    &__ypcurrentlen);
829 						} else
830 							goto pwuid_netgrp;
831 						if (r != 0) {
832 							if (__ypcurrent)
833 							    free(__ypcurrent);
834 							__ypcurrent = NULL;
835 							/*
836 							 * just because this
837 							 * user is bad, doesn't
838 							 * mean they all are.
839 							 */
840 							goto pwuid_netgrp;
841 						}
842 					}
843 					break;
844 				default:
845 					if (__ypcurrent) {
846 						free(__ypcurrent);
847 						__ypcurrent = NULL;
848 					}
849 					user = _pw_passwd.pw_name + 1;
850 					r = yp_match(__ypdomain,
851 						(PASSWD_BYNAME),
852 						user, strlen(user),
853 						&__ypcurrent,
854 						&__ypcurrentlen);
855 					if (r != 0) {
856 						if (__ypcurrent)
857 							free(__ypcurrent);
858 						__ypcurrent = NULL;
859 						continue;
860 					}
861 					break;
862 				}
863 				bcopy(__ypcurrent, __ypline, __ypcurrentlen);
864 				__ypline[__ypcurrentlen] = '\0';
865 				if (__ypparse(&_pw_passwd, __ypline)
866 				   || __ypexclude_is(_pw_passwd.pw_name)) {
867 					if (s == 1)	/* inside netgroup */
868 						goto pwuid_netgrp;
869 					continue;
870 				}
871 				break;
872 			case '-':
873 				/* attempted exclusion */
874 				switch (_pw_passwd.pw_name[1]) {
875 				case '\0':
876 					break;
877 				case '@':
878 					setnetgrent(_pw_passwd.pw_name + 2);
879 					while (getnetgrent(&host, &user, &dom)) {
880 						if (user && *user)
881 							__ypexclude_add(user);
882 					}
883 					endnetgrent();
884 					break;
885 				default:
886 					__ypexclude_add(_pw_passwd.pw_name + 1);
887 					break;
888 				}
889 				break;
890 			}
891 			if (_pw_passwd.pw_uid == uid) {
892 				if (!_pw_stayopen) {
893 					(void)(_pw_db->close)(_pw_db);
894 					_pw_db = (DB *)NULL;
895 				}
896 				__ypexclude_free();
897 				__ypproto = NULL;
898 				return &_pw_passwd;
899 			}
900 			if (s == 1)	/* inside netgroup */
901 				goto pwuid_netgrp;
902 			continue;
903 		}
904 		if (!_pw_stayopen) {
905 			(void)(_pw_db->close)(_pw_db);
906 			_pw_db = (DB *)NULL;
907 		}
908 		__ypexclude_free();
909 		__ypproto = (struct passwd *)NULL;
910 		return (struct passwd *)NULL;
911 	}
912 #endif /* YP */
913 
914 	bf[0] = _PW_KEYBYUID;
915 	keyuid = uid;
916 	bcopy(&keyuid, bf + 1, sizeof(keyuid));
917 	key.data = (u_char *)bf;
918 	key.size = sizeof(keyuid) + 1;
919 	rval = __hashpw(&key);
920 
921 	if (!_pw_stayopen) {
922 		(void)(_pw_db->close)(_pw_db);
923 		_pw_db = (DB *)NULL;
924 	}
925 	return (rval ? &_pw_passwd : (struct passwd *)NULL);
926 }
927 
928 int
929 setpassent(stayopen)
930 	int stayopen;
931 {
932 	_pw_keynum = 0;
933 	_pw_stayopen = stayopen;
934 #ifdef YP
935 	__ypmode = YPMODE_NONE;
936 	if (__ypcurrent)
937 		free(__ypcurrent);
938 	__ypcurrent = NULL;
939 	__ypexclude_free();
940 	__ypproto = (struct passwd *)NULL;
941 #endif
942 	return (1);
943 }
944 
945 void
946 setpwent()
947 {
948 	(void) setpassent(0);
949 }
950 
951 void
952 endpwent()
953 {
954 	_pw_keynum = 0;
955 	if (_pw_db) {
956 		(void)(_pw_db->close)(_pw_db);
957 		_pw_db = (DB *)NULL;
958 	}
959 #ifdef YP
960 	__ypmode = YPMODE_NONE;
961 	if (__ypcurrent)
962 		free(__ypcurrent);
963 	__ypcurrent = NULL;
964 	__ypexclude_free();
965 	__ypproto = (struct passwd *)NULL;
966 #endif
967 }
968 
969 static int
970 __initdb()
971 {
972 	static int warned;
973 	char *p;
974 
975 #ifdef YP
976 	__ypmode = YPMODE_NONE;
977 	__getpwent_has_yppw = -1;
978 #endif
979 	p = (geteuid()) ? _PATH_MP_DB : _PATH_SMP_DB;
980 	_pw_db = dbopen(p, O_RDONLY, 0, DB_HASH, NULL);
981 	if (_pw_db)
982 		return (1);
983 	if (!warned)
984 		syslog(LOG_ERR, "%s: %m", p);
985 	warned = 1;
986 	return (0);
987 }
988 
989 static int
990 __hashpw(key)
991 	DBT *key;
992 {
993 	register char *p, *t;
994 	static u_int max;
995 	static char *line;
996 	DBT data;
997 
998 	if ((_pw_db->get)(_pw_db, key, &data, 0))
999 		return (0);
1000 	p = (char *)data.data;
1001 	if (data.size > max) {
1002 		char *nline;
1003 
1004 		max = data.size + 256;
1005 		nline = realloc(line, max);
1006 		if (nline == NULL) {
1007 			if (line)
1008 				free(line);
1009 			line = NULL;
1010 			return 0;
1011 		}
1012 		line = nline;
1013 	}
1014 
1015 	t = line;
1016 #define	EXPAND(e)	e = t; while ((*t++ = *p++));
1017 	EXPAND(_pw_passwd.pw_name);
1018 	EXPAND(_pw_passwd.pw_passwd);
1019 	bcopy(p, (char *)&_pw_passwd.pw_uid, sizeof(int));
1020 	p += sizeof(int);
1021 	bcopy(p, (char *)&_pw_passwd.pw_gid, sizeof(int));
1022 	p += sizeof(int);
1023 	bcopy(p, (char *)&_pw_passwd.pw_change, sizeof(time_t));
1024 	p += sizeof(time_t);
1025 	EXPAND(_pw_passwd.pw_class);
1026 	EXPAND(_pw_passwd.pw_gecos);
1027 	EXPAND(_pw_passwd.pw_dir);
1028 	EXPAND(_pw_passwd.pw_shell);
1029 	bcopy(p, (char *)&_pw_passwd.pw_expire, sizeof(time_t));
1030 	p += sizeof(time_t);
1031 
1032 	/* See if there's any data left.  If so, read in flags. */
1033 	if (data.size > (p - (char *)data.data)) {
1034 		bcopy(p, (char *)&_pw_flags, sizeof(int));
1035 		p += sizeof(int);
1036 	} else
1037 		_pw_flags = _PASSWORD_NOUID|_PASSWORD_NOGID;	/* default */
1038 
1039 	return (1);
1040 }
1041