xref: /freebsd/lib/libc/gen/getpwent.c (revision ce4946daa5ce852d28008dac492029500ab2ee95)
1 /*	$NetBSD: getpwent.c,v 1.40.2.2 1999/04/27 22:09:45 perry Exp $	*/
2 
3 /*
4  * Copyright (c) 1988, 1993
5  *	The Regents of the University of California.  All rights reserved.
6  * Portions Copyright (c) 1994, 1995, 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. All advertising materials mentioning features or use of this software
17  *    must display the following acknowledgement:
18  *	This product includes software developed by the University of
19  *	California, Berkeley and its contributors.
20  * 4. Neither the name of the University nor the names of its contributors
21  *    may be used to endorse or promote products derived from this software
22  *    without specific prior written permission.
23  *
24  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
25  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
26  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
27  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
28  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
29  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
30  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
31  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
32  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
33  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
34  * SUCH DAMAGE.
35  */
36 
37 #include <sys/cdefs.h>
38 #if defined(LIBC_SCCS) && !defined(lint)
39 static const char *rcsid[] =
40   "$FreeBSD$";
41 #endif /* LIBC_SCCS and not lint */
42 
43 #include "un-namespace.h"
44 #include <sys/param.h>
45 #include <fcntl.h>
46 #include <db.h>
47 #include <syslog.h>
48 #include <pwd.h>
49 #include <utmp.h>
50 #include <errno.h>
51 #include <unistd.h>
52 #include <stdlib.h>
53 #include <string.h>
54 #include <limits.h>
55 #include <nsswitch.h>
56 #ifdef HESIOD
57 #include <hesiod.h>
58 #endif
59 #ifdef YP
60 #include <machine/param.h>
61 #include <stdio.h>
62 #include <rpc/rpc.h>
63 #include <rpcsvc/yp_prot.h>
64 #include <rpcsvc/ypclnt.h>
65 #endif
66 #include "un-namespace.h"
67 
68 extern void setnetgrent __P((char *));
69 extern int getnetgrent __P((char **, char **, char **));
70 extern int innetgr __P((const char *, const char *, const char *, const char *));
71 
72 #include "pw_scan.h"
73 
74 #if defined(YP) || defined(HESIOD)
75 #define _PASSWD_COMPAT
76 #endif
77 
78 /*
79  * The lookup techniques and data extraction code here must be kept
80  * in sync with that in `pwd_mkdb'.
81  */
82 
83 static struct passwd _pw_passwd = { "", "", 0, 0, 0, "", "", "", "", 0, 0 };
84 static DB *_pw_db;			/* password database */
85 static int _pw_keynum;			/* key counter. no more records if -1 */
86 static int _pw_stayopen;		/* keep fd's open */
87 
88 static int __hashpw __P((DBT *));
89 static int __initdb __P((void));
90 
91 static const ns_src compatsrc[] = {
92 	{ NSSRC_COMPAT, NS_SUCCESS },
93 	{ 0 }
94 };
95 
96 #ifdef YP
97 static char     *__ypcurrent, *__ypdomain;
98 static int      __ypcurrentlen;
99 static int	_pw_ypdone;		/* non-zero if no more yp records */
100 #endif
101 
102 #ifdef HESIOD
103 static int	_pw_hesnum;		/* hes counter. no more records if -1 */
104 #endif
105 
106 #ifdef _PASSWD_COMPAT
107 enum _pwmode { PWMODE_NONE, PWMODE_FULL, PWMODE_USER, PWMODE_NETGRP };
108 static enum _pwmode __pwmode;
109 
110 enum _ypmap { YPMAP_NONE, YPMAP_ADJUNCT, YPMAP_MASTER };
111 
112 static struct passwd	*__pwproto = (struct passwd *)NULL;
113 static int		 __pwproto_flags;
114 static char		 line[1024];
115 static long		 prbuf[1024 / sizeof(long)];
116 static DB		*__pwexclude = (DB *)NULL;
117 
118 static int	__pwexclude_add __P((const char *));
119 static int	__pwexclude_is __P((const char *));
120 static void	__pwproto_set __P((void));
121 static int	__ypmaptype __P((void));
122 static int	__pwparse __P((struct passwd *, char *));
123 
124 	/* macros for deciding which YP maps to use. */
125 #define PASSWD_BYNAME	(__ypmaptype() == YPMAP_MASTER \
126 			    ? "master.passwd.byname" : "passwd.byname")
127 #define PASSWD_BYUID	(__ypmaptype() == YPMAP_MASTER \
128 			    ? "master.passwd.byuid" : "passwd.byuid")
129 
130 /*
131  * add a name to the compat mode exclude list
132  */
133 static int
134 __pwexclude_add(name)
135 	const char *name;
136 {
137 	DBT key;
138 	DBT data;
139 
140 	/* initialize the exclusion table if needed. */
141 	if(__pwexclude == (DB *)NULL) {
142 		__pwexclude = dbopen(NULL, O_RDWR, 600, DB_HASH, NULL);
143 		if(__pwexclude == (DB *)NULL)
144 			return 1;
145 	}
146 
147 	/* set up the key */
148 	key.size = strlen(name);
149 	/* LINTED key does not get modified */
150 	key.data = (char *)name;
151 
152 	/* data is nothing. */
153 	data.data = NULL;
154 	data.size = 0;
155 
156 	/* store it */
157 	if((__pwexclude->put)(__pwexclude, &key, &data, 0) == -1)
158 		return 1;
159 
160 	return 0;
161 }
162 
163 /*
164  * test if a name is on the compat mode exclude list
165  */
166 static int
167 __pwexclude_is(name)
168 	const char *name;
169 {
170 	DBT key;
171 	DBT data;
172 
173 	if(__pwexclude == (DB *)NULL)
174 		return 0;	/* nothing excluded */
175 
176 	/* set up the key */
177 	key.size = strlen(name);
178 	/* LINTED key does not get modified */
179 	key.data = (char *)name;
180 
181 	if((__pwexclude->get)(__pwexclude, &key, &data, 0) == 0)
182 		return 1;	/* excluded */
183 
184 	return 0;
185 }
186 
187 /*
188  * Setup the compat mode prototype template that may be used in
189  * __pwparse.  Only pw_passwd, pw_uid, pw_gid, pw_gecos, pw_dir, and
190  * pw_shell are used.  The other fields are zero'd.
191  */
192 static void
193 __pwproto_set()
194 {
195 	char *ptr;
196 	struct passwd *pw = &_pw_passwd;
197 
198 	/* make this the new prototype */
199 	ptr = (char *)(void *)prbuf;
200 
201 	/* first allocate the struct. */
202 	__pwproto = (struct passwd *)(void *)ptr;
203 	ptr += sizeof(struct passwd);
204 	memset(__pwproto, 0, sizeof(*__pwproto));
205 
206 	__pwproto_flags = 0;
207 
208 	/* password */
209 	if(pw->pw_passwd && (pw->pw_passwd)[0]) {
210 		ptr = (char *)ALIGN((u_long)ptr);
211 		memmove(ptr, pw->pw_passwd, strlen(pw->pw_passwd) + 1);
212 		__pwproto->pw_passwd = ptr;
213 		ptr += (strlen(pw->pw_passwd) + 1);
214 		__pwproto_flags |= _PWF_PASSWD;
215 	}
216 
217 	/* uid, gid */
218 	if (pw->pw_fields & _PWF_UID) {
219 		__pwproto->pw_uid = pw->pw_uid;
220 		__pwproto_flags |= _PWF_UID;
221 	}
222 	if (pw->pw_fields & _PWF_GID) {
223 		__pwproto->pw_gid = pw->pw_gid;
224 		__pwproto_flags |= _PWF_GID;
225 	}
226 
227 	/* gecos */
228 	if(pw->pw_gecos && (pw->pw_gecos)[0]) {
229 		ptr = (char *)ALIGN((u_long)ptr);
230 		memmove(ptr, pw->pw_gecos, strlen(pw->pw_gecos) + 1);
231 		__pwproto->pw_gecos = ptr;
232 		ptr += (strlen(pw->pw_gecos) + 1);
233 		__pwproto_flags |= _PWF_GECOS;
234 	}
235 
236 	/* dir */
237 	if(pw->pw_dir && (pw->pw_dir)[0]) {
238 		ptr = (char *)ALIGN((u_long)ptr);
239 		memmove(ptr, pw->pw_dir, strlen(pw->pw_dir) + 1);
240 		__pwproto->pw_dir = ptr;
241 		ptr += (strlen(pw->pw_dir) + 1);
242 		__pwproto_flags |= _PWF_DIR;
243 	}
244 
245 	/* shell */
246 	if(pw->pw_shell && (pw->pw_shell)[0]) {
247 		ptr = (char *)ALIGN((u_long)ptr);
248 		memmove(ptr, pw->pw_shell, strlen(pw->pw_shell) + 1);
249 		__pwproto->pw_shell = ptr;
250 		ptr += (strlen(pw->pw_shell) + 1);
251 		__pwproto_flags |= _PWF_SHELL;
252 	}
253 }
254 
255 static int
256 __ypmaptype()
257 {
258 	static int maptype = -1;
259 	int order, r;
260 
261 	if (maptype != -1)
262 		return (maptype);
263 
264 	maptype = YPMAP_NONE;
265 	if (geteuid() != 0)
266 		return (maptype);
267 
268 	if (!__ypdomain) {
269 		if( _yp_check(&__ypdomain) == 0)
270 			return (maptype);
271 	}
272 
273 	r = yp_order(__ypdomain, "master.passwd.byname", &order);
274 	if (r == 0) {
275 		maptype = YPMAP_MASTER;
276 		return (maptype);
277 	}
278 
279 	/*
280 	 * NIS+ in YP compat mode doesn't support
281 	 * YPPROC_ORDER -- no point in continuing.
282 	 */
283 	if (r == YPERR_YPERR)
284 		return (maptype);
285 
286 	/* master.passwd doesn't exist -- try passwd.adjunct */
287 	if (r == YPERR_MAP) {
288 		r = yp_order(__ypdomain, "passwd.adjunct.byname", &order);
289 		if (r == 0)
290 			maptype = YPMAP_ADJUNCT;
291 		return (maptype);
292 	}
293 
294 	return (maptype);
295 }
296 
297 /*
298  * parse a passwd file line (from NIS or HESIOD).
299  * assumed to be `old-style' if maptype != YPMAP_MASTER.
300  */
301 static int
302 __pwparse(pw, s)
303 	struct passwd *pw;
304 	char *s;
305 {
306 	static char adjunctpw[YPMAXRECORD + 2];
307 	int flags, maptype;
308 
309 	maptype = __ypmaptype();
310 	flags = 0;
311 	if (maptype == YPMAP_MASTER)
312 		flags |= _PWSCAN_MASTER;
313 	if (! __pw_scan(s, pw, flags))
314 		return 1;
315 
316 	/* now let the prototype override, if set. */
317 	if(__pwproto != (struct passwd *)NULL) {
318 #ifdef PW_OVERRIDE_PASSWD
319 		if(__pwproto_flags & _PWF_PASSWD)
320 			pw->pw_passwd = __pwproto->pw_passwd;
321 #endif
322 		if(__pwproto_flags & _PWF_UID)
323 			pw->pw_uid = __pwproto->pw_uid;
324 		if(__pwproto_flags & _PWF_GID)
325 			pw->pw_gid = __pwproto->pw_gid;
326 		if(__pwproto_flags & _PWF_GECOS)
327 			pw->pw_gecos = __pwproto->pw_gecos;
328 		if(__pwproto_flags & _PWF_DIR)
329 			pw->pw_dir = __pwproto->pw_dir;
330 		if(__pwproto_flags & _PWF_SHELL)
331 			pw->pw_shell = __pwproto->pw_shell;
332 	}
333 	if ((maptype == YPMAP_ADJUNCT) &&
334 	    (strstr(pw->pw_passwd, "##") != NULL)) {
335 		char *data, *bp;
336 		int datalen;
337 
338 		if (yp_match(__ypdomain, "passwd.adjunct.byname", pw->pw_name,
339 		    (int)strlen(pw->pw_name), &data, &datalen) == 0) {
340 			if (datalen > sizeof(adjunctpw) - 1)
341 				datalen = sizeof(adjunctpw) - 1;
342 			strncpy(adjunctpw, data, (size_t)datalen);
343 
344 				/* skip name to get password */
345 			if ((bp = strsep(&data, ":")) != NULL &&
346 			    (bp = strsep(&data, ":")) != NULL)
347 				pw->pw_passwd = bp;
348 		}
349 	}
350 	return 0;
351 }
352 #endif /* _PASSWD_COMPAT */
353 
354 /*
355  * local files implementation of getpw*()
356  * varargs: type, [ uid (type == _PW_KEYBYUID) | name (type == _PW_KEYBYNAME) ]
357  */
358 static int	_local_getpw __P((void *, void *, va_list));
359 
360 /*ARGSUSED*/
361 static int
362 _local_getpw(rv, cb_data, ap)
363 	void	*rv;
364 	void	*cb_data;
365 	va_list	 ap;
366 {
367 	DBT		 key;
368 	char		 bf[/*CONSTCOND*/ MAX(MAXLOGNAME, sizeof(_pw_keynum)) + 1];
369 	uid_t		 uid;
370 	int		 search, len, rval;
371 	const char	*name;
372 
373 	if (!_pw_db && !__initdb())
374 		return NS_UNAVAIL;
375 
376 	search = va_arg(ap, int);
377 	bf[0] = search;
378 	switch (search) {
379 	case _PW_KEYBYNUM:
380 		if (_pw_keynum == -1)
381 			return NS_NOTFOUND;	/* no more local records */
382 		++_pw_keynum;
383 		memmove(bf + 1, &_pw_keynum, sizeof(_pw_keynum));
384 		key.size = sizeof(_pw_keynum) + 1;
385 		break;
386 	case _PW_KEYBYNAME:
387 		name = va_arg(ap, const char *);
388 		len = strlen(name);
389 		memmove(bf + 1, name, (size_t)MIN(len, MAXLOGNAME));
390 		key.size = len + 1;
391 		break;
392 	case _PW_KEYBYUID:
393 		uid = va_arg(ap, uid_t);
394 		memmove(bf + 1, &uid, sizeof(len));
395 		key.size = sizeof(uid) + 1;
396 		break;
397 	default:
398 		abort();
399 	}
400 
401 	key.data = (u_char *)bf;
402 	rval = __hashpw(&key);
403 	if (rval == NS_NOTFOUND && search == _PW_KEYBYNUM)
404 		_pw_keynum = -1;	/* flag `no more local records' */
405 
406 	if (!_pw_stayopen && (search != _PW_KEYBYNUM)) {
407 		(void)(_pw_db->close)(_pw_db);
408 		_pw_db = (DB *)NULL;
409 	}
410 	return (rval);
411 }
412 
413 #ifdef HESIOD
414 /*
415  * hesiod implementation of getpw*()
416  * varargs: type, [ uid (type == _PW_KEYBYUID) | name (type == _PW_KEYBYNAME) ]
417  */
418 static int	_dns_getpw __P((void *, void *, va_list));
419 
420 /*ARGSUSED*/
421 static int
422 _dns_getpw(rv, cb_data, ap)
423 	void	*rv;
424 	void	*cb_data;
425 	va_list	 ap;
426 {
427 	const char	 *name;
428 	uid_t		  uid;
429 	int		  search;
430 
431 	const char	 *map;
432 	char		**hp;
433 	void		 *context;
434 	int		  r;
435 
436 	search = va_arg(ap, int);
437  nextdnsbynum:
438 	switch (search) {
439 	case _PW_KEYBYNUM:
440 		if (_pw_hesnum == -1)
441 			return NS_NOTFOUND;	/* no more hesiod records */
442 		snprintf(line, sizeof(line) - 1, "passwd-%u", _pw_hesnum);
443 		_pw_hesnum++;
444 		map = "passwd";
445 		break;
446 	case _PW_KEYBYNAME:
447 		name = va_arg(ap, const char *);
448 		strncpy(line, name, sizeof(line));
449 		map = "passwd";
450 		break;
451 	case _PW_KEYBYUID:
452 		uid = va_arg(ap, uid_t);
453 		snprintf(line, sizeof(line), "%u", (unsigned int)uid);
454 		map = "uid";		/* XXX this is `passwd' on ultrix */
455 		break;
456 	default:
457 		abort();
458 	}
459 	line[sizeof(line) - 1] = '\0';
460 
461 	r = NS_UNAVAIL;
462 	if (hesiod_init(&context) == -1)
463 		return (r);
464 
465 	hp = hesiod_resolve(context, line, map);
466 	if (hp == NULL) {
467 		if (errno == ENOENT) {
468 					/* flag `no more hesiod records' */
469 			if (search == _PW_KEYBYNUM)
470 				_pw_hesnum = -1;
471 			r = NS_NOTFOUND;
472 		}
473 		goto cleanup_dns_getpw;
474 	}
475 
476 	strncpy(line, hp[0], sizeof(line));	/* only check first elem */
477 	line[sizeof(line) - 1] = '\0';
478 	hesiod_free_list(context, hp);
479 	if (__pwparse(&_pw_passwd, line)) {
480 		if (search == _PW_KEYBYNUM)
481 			goto nextdnsbynum;	/* skip dogdy entries */
482 		r = NS_UNAVAIL;
483 	} else
484 		r = NS_SUCCESS;
485  cleanup_dns_getpw:
486 	hesiod_end(context);
487 	return (r);
488 }
489 #endif
490 
491 #ifdef YP
492 /*
493  * nis implementation of getpw*()
494  * varargs: type, [ uid (type == _PW_KEYBYUID) | name (type == _PW_KEYBYNAME) ]
495  */
496 static int	_nis_getpw __P((void *, void *, va_list));
497 
498 /*ARGSUSED*/
499 static int
500 _nis_getpw(rv, cb_data, ap)
501 	void	*rv;
502 	void	*cb_data;
503 	va_list	 ap;
504 {
505 	const char	*name;
506 	uid_t		 uid;
507 	int		 search;
508 	char		*key, *data;
509 	char		*map;
510 	int		 keylen, datalen, r, rval;
511 
512 	if(__ypdomain == NULL) {
513 		if(_yp_check(&__ypdomain) == 0)
514 			return NS_UNAVAIL;
515 	}
516 
517 	map = PASSWD_BYNAME;
518 	search = va_arg(ap, int);
519 	switch (search) {
520 	case _PW_KEYBYNUM:
521 		break;
522 	case _PW_KEYBYNAME:
523 		name = va_arg(ap, const char *);
524 		strncpy(line, name, sizeof(line));
525 		break;
526 	case _PW_KEYBYUID:
527 		uid = va_arg(ap, uid_t);
528 		snprintf(line, sizeof(line), "%u", (unsigned int)uid);
529 		map = PASSWD_BYUID;
530 		break;
531 	default:
532 		abort();
533 	}
534 	line[sizeof(line) - 1] = '\0';
535 	rval = NS_UNAVAIL;
536 	if (search != _PW_KEYBYNUM) {
537 		data = NULL;
538 		r = yp_match(__ypdomain, map, line, (int)strlen(line),
539 				&data, &datalen);
540 		if (r == YPERR_KEY)
541 			rval = NS_NOTFOUND;
542 		if (r != 0) {
543 			if (data)
544 				free(data);
545 			return (rval);
546 		}
547 		data[datalen] = '\0';		/* clear trailing \n */
548 		strncpy(line, data, sizeof(line));
549 		line[sizeof(line) - 1] = '\0';
550 		free(data);
551 		if (__pwparse(&_pw_passwd, line))
552 			return NS_UNAVAIL;
553 		return NS_SUCCESS;
554 	}
555 
556 	if (_pw_ypdone)
557 		return NS_NOTFOUND;
558 	for (;;) {
559 		data = key = NULL;
560 		if (__ypcurrent) {
561 			r = yp_next(__ypdomain, map,
562 					__ypcurrent, __ypcurrentlen,
563 					&key, &keylen, &data, &datalen);
564 			free(__ypcurrent);
565 			switch (r) {
566 			case 0:
567 				__ypcurrent = key;
568 				__ypcurrentlen = keylen;
569 				break;
570 			case YPERR_NOMORE:
571 				__ypcurrent = NULL;
572 					/* flag `no more yp records' */
573 				_pw_ypdone = 1;
574 				rval = NS_NOTFOUND;
575 			}
576 		} else {
577 			r = yp_first(__ypdomain, map, &__ypcurrent,
578 					&__ypcurrentlen, &data, &datalen);
579 		}
580 		if (r != 0) {
581 			if (key)
582 				free(key);
583 			if (data)
584 				free(data);
585 			return (rval);
586 		}
587 		data[datalen] = '\0';		/* clear trailing \n */
588 		strncpy(line, data, sizeof(line));
589 		line[sizeof(line) - 1] = '\0';
590 				free(data);
591 		if (! __pwparse(&_pw_passwd, line))
592 			return NS_SUCCESS;
593 	}
594 	/* NOTREACHED */
595 } /* _nis_getpw */
596 #endif
597 
598 #ifdef _PASSWD_COMPAT
599 /*
600  * See if the compat token is in the database.  Only works if pwd_mkdb knows
601  * about the token.
602  */
603 static int	__has_compatpw __P((void));
604 
605 static int
606 __has_compatpw()
607 {
608 	DBT key, data;
609 	DBT pkey, pdata;
610 	char bf[MAXLOGNAME];
611 	u_char cyp[] = { _PW_KEYYPENABLED };
612 
613 	/*LINTED*/
614 	key.data = cyp;
615 	key.size = 1;
616 
617 	/* Pre-token database support. */
618 	bf[0] = _PW_KEYBYNAME;
619 	bf[1] = '+';
620 	pkey.data = (u_char *)bf;
621 	pkey.size = 2;
622 
623 	if ((_pw_db->get)(_pw_db, &key, &data, 0)
624 	    && (_pw_db->get)(_pw_db, &pkey, &pdata, 0))
625 		return 0;		/* No compat token */
626 	return 1;
627 }
628 
629 /*
630  * log an error if "files" or "compat" is specified in passwd_compat database
631  */
632 static int	_bad_getpw __P((void *, void *, va_list));
633 
634 /*ARGSUSED*/
635 static int
636 _bad_getpw(rv, cb_data, ap)
637 	void	*rv;
638 	void	*cb_data;
639 	va_list	 ap;
640 {
641 	static int warned;
642 	if (!warned) {
643 		syslog(LOG_ERR,
644 			"nsswitch.conf passwd_compat database can't use '%s'",
645 			(char *)cb_data);
646 	}
647 	warned = 1;
648 	return NS_UNAVAIL;
649 }
650 
651 /*
652  * when a name lookup in compat mode is required (e.g., '+name', or a name in
653  * '+@netgroup'), look it up in the 'passwd_compat' nsswitch database.
654  * only Hesiod and NIS is supported - it doesn't make sense to lookup
655  * compat names from 'files' or 'compat'.
656  */
657 static int	__getpwcompat __P((int, uid_t, const char *));
658 
659 static int
660 __getpwcompat(type, uid, name)
661 	int		 type;
662 	uid_t		 uid;
663 	const char	*name;
664 {
665 	static const ns_dtab dtab[] = {
666 		NS_FILES_CB(_bad_getpw, "files")
667 		NS_DNS_CB(_dns_getpw, NULL)
668 		NS_NIS_CB(_nis_getpw, NULL)
669 		NS_COMPAT_CB(_bad_getpw, "compat")
670 		{ 0 }
671 	};
672 	static const ns_src defaultnis[] = {
673 		{ NSSRC_NIS, 	NS_SUCCESS },
674 		{ 0 }
675 	};
676 
677 	switch (type) {
678 	case _PW_KEYBYNUM:
679 		return nsdispatch(NULL, dtab, NSDB_PASSWD_COMPAT, "getpwcompat",
680 		    defaultnis, type);
681 	case _PW_KEYBYNAME:
682 		return nsdispatch(NULL, dtab, NSDB_PASSWD_COMPAT, "getpwcompat",
683 		    defaultnis, type, name);
684 	case _PW_KEYBYUID:
685 		return nsdispatch(NULL, dtab, NSDB_PASSWD_COMPAT, "getpwcompat",
686 		    defaultnis, type, uid);
687 	default:
688 		abort();
689 		/*NOTREACHED*/
690 	}
691 }
692 #endif /* _PASSWD_COMPAT */
693 
694 /*
695  * compat implementation of getpwent()
696  * varargs (ignored):
697  *	type, [ uid (type == _PW_KEYBYUID) | name (type == _PW_KEYBYNAME) ]
698  */
699 static int	_compat_getpwent __P((void *, void *, va_list));
700 
701 /*ARGSUSED*/
702 static int
703 _compat_getpwent(rv, cb_data, ap)
704 	void	*rv;
705 	void	*cb_data;
706 	va_list	 ap;
707 {
708 	DBT		 key;
709 	int		 rval;
710 	char		 bf[sizeof(_pw_keynum) + 1];
711 #ifdef _PASSWD_COMPAT
712 	static char	*name = NULL;
713 	char		*user, *host, *dom;
714 	int		 has_compatpw;
715 #endif
716 
717 	if (!_pw_db && !__initdb())
718 		return NS_UNAVAIL;
719 
720 #ifdef _PASSWD_COMPAT
721 	has_compatpw = __has_compatpw();
722 
723 again:
724 	if (has_compatpw && (__pwmode != PWMODE_NONE)) {
725 		int r;
726 
727 		switch (__pwmode) {
728 		case PWMODE_FULL:
729 			r = __getpwcompat(_PW_KEYBYNUM, 0, NULL);
730 			if (r == NS_SUCCESS)
731 				return r;
732 			__pwmode = PWMODE_NONE;
733 			break;
734 
735 		case PWMODE_NETGRP:
736 			r = getnetgrent(&host, &user, &dom);
737 			if (r == 0) {	/* end of group */
738 				endnetgrent();
739 				__pwmode = PWMODE_NONE;
740 				break;
741 			}
742 			if (!user || !*user)
743 				break;
744 			r = __getpwcompat(_PW_KEYBYNAME, 0, user);
745 			if (r == NS_SUCCESS)
746 				return r;
747 			break;
748 
749 		case PWMODE_USER:
750 			if (name == NULL) {
751 				__pwmode = PWMODE_NONE;
752 				break;
753 			}
754 			r = __getpwcompat(_PW_KEYBYNAME, 0, name);
755 			free(name);
756 			name = NULL;
757 			if (r == NS_SUCCESS)
758 				return r;
759 			break;
760 
761 		case PWMODE_NONE:
762 			abort();
763 		}
764 		goto again;
765 	}
766 #endif
767 
768 	if (_pw_keynum == -1)
769 		return NS_NOTFOUND;	/* no more local records */
770 	++_pw_keynum;
771 	bf[0] = _PW_KEYBYNUM;
772 	memmove(bf + 1, &_pw_keynum, sizeof(_pw_keynum));
773 	key.data = (u_char *)bf;
774 	key.size = sizeof(_pw_keynum) + 1;
775 	rval = __hashpw(&key);
776 	if (rval == NS_NOTFOUND)
777 		_pw_keynum = -1;	/* flag `no more local records' */
778 	else if (rval == NS_SUCCESS) {
779 #ifdef _PASSWD_COMPAT
780 		/* if we don't have YP at all, don't bother. */
781 		if (has_compatpw) {
782 			if(_pw_passwd.pw_name[0] == '+') {
783 				/* set the mode */
784 				switch(_pw_passwd.pw_name[1]) {
785 				case '\0':
786 					__pwmode = PWMODE_FULL;
787 					break;
788 				case '@':
789 					__pwmode = PWMODE_NETGRP;
790 					setnetgrent(_pw_passwd.pw_name + 2);
791 					break;
792 				default:
793 					__pwmode = PWMODE_USER;
794 					name = strdup(_pw_passwd.pw_name + 1);
795 					break;
796 				}
797 
798 				/* save the prototype */
799 				__pwproto_set();
800 				goto again;
801 			} else if(_pw_passwd.pw_name[0] == '-') {
802 				/* an attempted exclusion */
803 				switch(_pw_passwd.pw_name[1]) {
804 				case '\0':
805 					break;
806 				case '@':
807 					setnetgrent(_pw_passwd.pw_name + 2);
808 					while(getnetgrent(&host, &user, &dom)) {
809 						if(user && *user)
810 							__pwexclude_add(user);
811 					}
812 					endnetgrent();
813 					break;
814 				default:
815 					__pwexclude_add(_pw_passwd.pw_name + 1);
816 					break;
817 				}
818 				goto again;
819 			}
820 		}
821 #endif
822 	}
823 	return (rval);
824 }
825 
826 /*
827  * compat implementation of getpwnam() and getpwuid()
828  * varargs: type, [ uid (type == _PW_KEYBYUID) | name (type == _PW_KEYBYNAME) ]
829  */
830 static int	_compat_getpw __P((void *, void *, va_list));
831 
832 static int
833 _compat_getpw(rv, cb_data, ap)
834 	void	*rv;
835 	void	*cb_data;
836 	va_list	 ap;
837 {
838 #ifdef _PASSWD_COMPAT
839 	DBT		key;
840 	int		search, rval, r, s, keynum;
841 	uid_t		uid;
842 	char		bf[sizeof(keynum) + 1];
843 	char		*name, *host, *user, *dom;
844 #endif
845 
846 	if (!_pw_db && !__initdb())
847 		return NS_UNAVAIL;
848 
849 		/*
850 		 * If there isn't a compat token in the database, use files.
851 		 */
852 #ifdef _PASSWD_COMPAT
853 	if (! __has_compatpw())
854 #endif
855 		return (_local_getpw(rv, cb_data, ap));
856 
857 #ifdef _PASSWD_COMPAT
858 	search = va_arg(ap, int);
859 	uid = 0;
860 	name = NULL;
861 	rval = NS_NOTFOUND;
862 	switch (search) {
863 	case _PW_KEYBYNAME:
864 		name = va_arg(ap, char *);
865 		break;
866 	case _PW_KEYBYUID:
867 		uid = va_arg(ap, uid_t);
868 		break;
869 	default:
870 		abort();
871 	}
872 
873 	for (s = -1, keynum = 1 ; ; keynum++) {
874 		bf[0] = _PW_KEYBYNUM;
875 		memmove(bf + 1, &keynum, sizeof(keynum));
876 		key.data = (u_char *)bf;
877 		key.size = sizeof(keynum) + 1;
878 		if(__hashpw(&key) != NS_SUCCESS)
879 			break;
880 		switch(_pw_passwd.pw_name[0]) {
881 		case '+':
882 			/* save the prototype */
883 			__pwproto_set();
884 
885 			switch(_pw_passwd.pw_name[1]) {
886 			case '\0':
887 				r = __getpwcompat(search, uid, name);
888 				if (r != NS_SUCCESS)
889 					continue;
890 				break;
891 			case '@':
892 pwnam_netgrp:
893 #if 0			/* XXX: is this a hangover from pre-nsswitch?  */
894 				if(__ypcurrent) {
895 					free(__ypcurrent);
896 					__ypcurrent = NULL;
897 				}
898 #endif
899 				if (s == -1)		/* first time */
900 					setnetgrent(_pw_passwd.pw_name + 2);
901 				s = getnetgrent(&host, &user, &dom);
902 				if (s == 0) {		/* end of group */
903 					endnetgrent();
904 					s = -1;
905 					continue;
906 				}
907 				if (!user || !*user)
908 					goto pwnam_netgrp;
909 
910 				r = __getpwcompat(_PW_KEYBYNAME, 0, user);
911 
912 				if (r == NS_UNAVAIL)
913 					return r;
914 				if (r == NS_NOTFOUND) {
915 					/*
916 					 * just because this user is bad
917 					 * it doesn't mean they all are.
918 					 */
919 					goto pwnam_netgrp;
920 				}
921 				break;
922 			default:
923 				user = _pw_passwd.pw_name + 1;
924 				r = __getpwcompat(_PW_KEYBYNAME, 0, user);
925 
926 				if (r == NS_UNAVAIL)
927 					return r;
928 				if (r == NS_NOTFOUND)
929 					continue;
930 				break;
931 			}
932 			if(__pwexclude_is(_pw_passwd.pw_name)) {
933 				if(s == 1)		/* inside netgroup */
934 					goto pwnam_netgrp;
935 				continue;
936 			}
937 			break;
938 		case '-':
939 			/* attempted exclusion */
940 			switch(_pw_passwd.pw_name[1]) {
941 			case '\0':
942 				break;
943 			case '@':
944 				setnetgrent(_pw_passwd.pw_name + 2);
945 				while(getnetgrent(&host, &user, &dom)) {
946 					if(user && *user)
947 						__pwexclude_add(user);
948 				}
949 				endnetgrent();
950 				break;
951 			default:
952 				__pwexclude_add(_pw_passwd.pw_name + 1);
953 				break;
954 			}
955 			break;
956 		}
957 		if ((search == _PW_KEYBYNAME &&
958 			    strcmp(_pw_passwd.pw_name, name) == 0)
959 		 || (search == _PW_KEYBYUID && _pw_passwd.pw_uid == uid)) {
960 			rval = NS_SUCCESS;
961 			break;
962 		}
963 		if(s == 1)				/* inside netgroup */
964 			goto pwnam_netgrp;
965 		continue;
966 	}
967 	__pwproto = (struct passwd *)NULL;
968 
969 	if (!_pw_stayopen) {
970 		(void)(_pw_db->close)(_pw_db);
971 		_pw_db = (DB *)NULL;
972 	}
973 	if(__pwexclude != (DB *)NULL) {
974 		(void)(__pwexclude->close)(__pwexclude);
975 			__pwexclude = (DB *)NULL;
976 	}
977 	return rval;
978 #endif /* _PASSWD_COMPAT */
979 }
980 
981 struct passwd *
982 getpwent()
983 {
984 	int		r;
985 	static const ns_dtab dtab[] = {
986 		NS_FILES_CB(_local_getpw, NULL)
987 		NS_DNS_CB(_dns_getpw, NULL)
988 		NS_NIS_CB(_nis_getpw, NULL)
989 		NS_COMPAT_CB(_compat_getpwent, NULL)
990 		{ 0 }
991 	};
992 
993 	r = nsdispatch(NULL, dtab, NSDB_PASSWD, "getpwent", compatsrc,
994 	    _PW_KEYBYNUM);
995 	if (r != NS_SUCCESS)
996 		return (struct passwd *)NULL;
997 	return &_pw_passwd;
998 }
999 
1000 struct passwd *
1001 getpwnam(name)
1002 	const char *name;
1003 {
1004 	int		r;
1005 	static const ns_dtab dtab[] = {
1006 		NS_FILES_CB(_local_getpw, NULL)
1007 		NS_DNS_CB(_dns_getpw, NULL)
1008 		NS_NIS_CB(_nis_getpw, NULL)
1009 		NS_COMPAT_CB(_compat_getpw, NULL)
1010 		{ 0 }
1011 	};
1012 
1013 	if (name == NULL || name[0] == '\0')
1014 		return (struct passwd *)NULL;
1015 
1016 	r = nsdispatch(NULL, dtab, NSDB_PASSWD, "getpwnam", compatsrc,
1017 	    _PW_KEYBYNAME, name);
1018 	return (r == NS_SUCCESS ? &_pw_passwd : (struct passwd *)NULL);
1019 }
1020 
1021 struct passwd *
1022 getpwuid(uid)
1023 	uid_t uid;
1024 {
1025 	int		r;
1026 	static const ns_dtab dtab[] = {
1027 		NS_FILES_CB(_local_getpw, NULL)
1028 		NS_DNS_CB(_dns_getpw, NULL)
1029 		NS_NIS_CB(_nis_getpw, NULL)
1030 		NS_COMPAT_CB(_compat_getpw, NULL)
1031 		{ 0 }
1032 	};
1033 
1034 	r = nsdispatch(NULL, dtab, NSDB_PASSWD, "getpwuid", compatsrc,
1035 	    _PW_KEYBYUID, uid);
1036 	return (r == NS_SUCCESS ? &_pw_passwd : (struct passwd *)NULL);
1037 }
1038 
1039 int
1040 setpassent(stayopen)
1041 	int stayopen;
1042 {
1043 	_pw_keynum = 0;
1044 	_pw_stayopen = stayopen;
1045 #ifdef YP
1046 	__pwmode = PWMODE_NONE;
1047 	if(__ypcurrent)
1048 		free(__ypcurrent);
1049 	__ypcurrent = NULL;
1050 	_pw_ypdone = 0;
1051 #endif
1052 #ifdef HESIOD
1053 	_pw_hesnum = 0;
1054 #endif
1055 #ifdef _PASSWD_COMPAT
1056 	if(__pwexclude != (DB *)NULL) {
1057 		(void)(__pwexclude->close)(__pwexclude);
1058 		__pwexclude = (DB *)NULL;
1059 	}
1060 	__pwproto = (struct passwd *)NULL;
1061 #endif
1062 	return 1;
1063 }
1064 
1065 void
1066 setpwent()
1067 {
1068 	(void) setpassent(0);
1069 }
1070 
1071 void
1072 endpwent()
1073 {
1074 	_pw_keynum = 0;
1075 	if (_pw_db) {
1076 		(void)(_pw_db->close)(_pw_db);
1077 		_pw_db = (DB *)NULL;
1078 	}
1079 #ifdef _PASSWD_COMPAT
1080 	__pwmode = PWMODE_NONE;
1081 #endif
1082 #ifdef YP
1083 	if(__ypcurrent)
1084 		free(__ypcurrent);
1085 	__ypcurrent = NULL;
1086 	_pw_ypdone = 0;
1087 #endif
1088 #ifdef HESIOD
1089 	_pw_hesnum = 0;
1090 #endif
1091 #ifdef _PASSWD_COMPAT
1092 	if(__pwexclude != (DB *)NULL) {
1093 		(void)(__pwexclude->close)(__pwexclude);
1094 		__pwexclude = (DB *)NULL;
1095 	}
1096 	__pwproto = (struct passwd *)NULL;
1097 #endif
1098 }
1099 
1100 static int
1101 __initdb()
1102 {
1103 	static int warned;
1104 	char *p;
1105 
1106 #ifdef _PASSWD_COMPAT
1107 	__pwmode = PWMODE_NONE;
1108 #endif
1109 	if (geteuid() == 0) {
1110 		_pw_db = dbopen((p = _PATH_SMP_DB), O_RDONLY, 0, DB_HASH, NULL);
1111 		if (_pw_db)
1112 			return(1);
1113 	}
1114 	_pw_db = dbopen((p = _PATH_MP_DB), O_RDONLY, 0, DB_HASH, NULL);
1115 	if (_pw_db)
1116 		return 1;
1117 	if (!warned)
1118 		syslog(LOG_ERR, "%s: %m", p);
1119 	warned = 1;
1120 	return 0;
1121 }
1122 
1123 static int
1124 __hashpw(key)
1125 	DBT *key;
1126 {
1127 	char *p, *t;
1128 	static u_int max;
1129 	static char *buf;
1130 	DBT data;
1131 
1132 	switch ((_pw_db->get)(_pw_db, key, &data, 0)) {
1133 	case 0:
1134 		break;			/* found */
1135 	case 1:
1136 		return NS_NOTFOUND;
1137 	case -1:
1138 		return NS_UNAVAIL;	/* error in db routines */
1139 	default:
1140 		abort();
1141 	}
1142 
1143 	p = (char *)data.data;
1144 	if (data.size > max && !(buf = realloc(buf, (max += 1024))))
1145 		return NS_UNAVAIL;
1146 
1147 	/* THIS CODE MUST MATCH THAT IN pwd_mkdb. */
1148 	t = buf;
1149 #define	EXPAND(e)	e = t; while ((*t++ = *p++));
1150 #define	SCALAR(v)	memmove(&(v), p, sizeof v); p += sizeof v
1151 	EXPAND(_pw_passwd.pw_name);
1152 	EXPAND(_pw_passwd.pw_passwd);
1153 	SCALAR(_pw_passwd.pw_uid);
1154 	SCALAR(_pw_passwd.pw_gid);
1155 	SCALAR(_pw_passwd.pw_change);
1156 	EXPAND(_pw_passwd.pw_class);
1157 	EXPAND(_pw_passwd.pw_gecos);
1158 	EXPAND(_pw_passwd.pw_dir);
1159 	EXPAND(_pw_passwd.pw_shell);
1160 	SCALAR(_pw_passwd.pw_expire);
1161 	SCALAR(_pw_passwd.pw_fields);
1162 
1163 	return NS_SUCCESS;
1164 }
1165