xref: /freebsd/lib/libc/gen/getpwent.c (revision 6990ffd8a95caaba6858ad44ff1b3157d1efba8f)
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 		if (len > sizeof(bf) - 1)
390 			return NS_NOTFOUND;
391 		memmove(bf + 1, name, len);
392 		key.size = len + 1;
393 		break;
394 	case _PW_KEYBYUID:
395 		uid = va_arg(ap, uid_t);
396 		memmove(bf + 1, &uid, sizeof(len));
397 		key.size = sizeof(uid) + 1;
398 		break;
399 	default:
400 		abort();
401 	}
402 
403 	key.data = (u_char *)bf;
404 	rval = __hashpw(&key);
405 	if (rval == NS_NOTFOUND && search == _PW_KEYBYNUM)
406 		_pw_keynum = -1;	/* flag `no more local records' */
407 
408 	if (!_pw_stayopen && (search != _PW_KEYBYNUM)) {
409 		(void)(_pw_db->close)(_pw_db);
410 		_pw_db = (DB *)NULL;
411 	}
412 	return (rval);
413 }
414 
415 #ifdef HESIOD
416 /*
417  * hesiod implementation of getpw*()
418  * varargs: type, [ uid (type == _PW_KEYBYUID) | name (type == _PW_KEYBYNAME) ]
419  */
420 static int	_dns_getpw __P((void *, void *, va_list));
421 
422 /*ARGSUSED*/
423 static int
424 _dns_getpw(rv, cb_data, ap)
425 	void	*rv;
426 	void	*cb_data;
427 	va_list	 ap;
428 {
429 	const char	 *name;
430 	uid_t		  uid;
431 	int		  search;
432 
433 	const char	 *map;
434 	char		**hp;
435 	void		 *context;
436 	int		  r;
437 
438 	search = va_arg(ap, int);
439  nextdnsbynum:
440 	switch (search) {
441 	case _PW_KEYBYNUM:
442 		if (_pw_hesnum == -1)
443 			return NS_NOTFOUND;	/* no more hesiod records */
444 		snprintf(line, sizeof(line) - 1, "passwd-%u", _pw_hesnum);
445 		_pw_hesnum++;
446 		map = "passwd";
447 		break;
448 	case _PW_KEYBYNAME:
449 		name = va_arg(ap, const char *);
450 		strncpy(line, name, sizeof(line));
451 		map = "passwd";
452 		break;
453 	case _PW_KEYBYUID:
454 		uid = va_arg(ap, uid_t);
455 		snprintf(line, sizeof(line), "%u", (unsigned int)uid);
456 		map = "uid";		/* XXX this is `passwd' on ultrix */
457 		break;
458 	default:
459 		abort();
460 	}
461 	line[sizeof(line) - 1] = '\0';
462 
463 	r = NS_UNAVAIL;
464 	if (hesiod_init(&context) == -1)
465 		return (r);
466 
467 	hp = hesiod_resolve(context, line, map);
468 	if (hp == NULL) {
469 		if (errno == ENOENT) {
470 					/* flag `no more hesiod records' */
471 			if (search == _PW_KEYBYNUM)
472 				_pw_hesnum = -1;
473 			r = NS_NOTFOUND;
474 		}
475 		goto cleanup_dns_getpw;
476 	}
477 
478 	strncpy(line, hp[0], sizeof(line));	/* only check first elem */
479 	line[sizeof(line) - 1] = '\0';
480 	hesiod_free_list(context, hp);
481 	if (__pwparse(&_pw_passwd, line)) {
482 		if (search == _PW_KEYBYNUM)
483 			goto nextdnsbynum;	/* skip dogdy entries */
484 		r = NS_UNAVAIL;
485 	} else
486 		r = NS_SUCCESS;
487  cleanup_dns_getpw:
488 	hesiod_end(context);
489 	return (r);
490 }
491 #endif
492 
493 #ifdef YP
494 /*
495  * nis implementation of getpw*()
496  * varargs: type, [ uid (type == _PW_KEYBYUID) | name (type == _PW_KEYBYNAME) ]
497  */
498 static int	_nis_getpw __P((void *, void *, va_list));
499 
500 /*ARGSUSED*/
501 static int
502 _nis_getpw(rv, cb_data, ap)
503 	void	*rv;
504 	void	*cb_data;
505 	va_list	 ap;
506 {
507 	const char	*name;
508 	uid_t		 uid;
509 	int		 search;
510 	char		*key, *data;
511 	char		*map;
512 	int		 keylen, datalen, r, rval;
513 
514 	if(__ypdomain == NULL) {
515 		if(_yp_check(&__ypdomain) == 0)
516 			return NS_UNAVAIL;
517 	}
518 
519 	map = PASSWD_BYNAME;
520 	search = va_arg(ap, int);
521 	switch (search) {
522 	case _PW_KEYBYNUM:
523 		break;
524 	case _PW_KEYBYNAME:
525 		name = va_arg(ap, const char *);
526 		strncpy(line, name, sizeof(line));
527 		break;
528 	case _PW_KEYBYUID:
529 		uid = va_arg(ap, uid_t);
530 		snprintf(line, sizeof(line), "%u", (unsigned int)uid);
531 		map = PASSWD_BYUID;
532 		break;
533 	default:
534 		abort();
535 	}
536 	line[sizeof(line) - 1] = '\0';
537 	rval = NS_UNAVAIL;
538 	if (search != _PW_KEYBYNUM) {
539 		data = NULL;
540 		r = yp_match(__ypdomain, map, line, (int)strlen(line),
541 				&data, &datalen);
542 		if (r == YPERR_KEY)
543 			rval = NS_NOTFOUND;
544 		if (r != 0) {
545 			if (data)
546 				free(data);
547 			return (rval);
548 		}
549 		data[datalen] = '\0';		/* clear trailing \n */
550 		strncpy(line, data, sizeof(line));
551 		line[sizeof(line) - 1] = '\0';
552 		free(data);
553 		if (__pwparse(&_pw_passwd, line))
554 			return NS_UNAVAIL;
555 		return NS_SUCCESS;
556 	}
557 
558 	if (_pw_ypdone)
559 		return NS_NOTFOUND;
560 	for (;;) {
561 		data = key = NULL;
562 		if (__ypcurrent) {
563 			r = yp_next(__ypdomain, map,
564 					__ypcurrent, __ypcurrentlen,
565 					&key, &keylen, &data, &datalen);
566 			free(__ypcurrent);
567 			switch (r) {
568 			case 0:
569 				__ypcurrent = key;
570 				__ypcurrentlen = keylen;
571 				break;
572 			case YPERR_NOMORE:
573 				__ypcurrent = NULL;
574 					/* flag `no more yp records' */
575 				_pw_ypdone = 1;
576 				rval = NS_NOTFOUND;
577 			}
578 		} else {
579 			r = yp_first(__ypdomain, map, &__ypcurrent,
580 					&__ypcurrentlen, &data, &datalen);
581 		}
582 		if (r != 0) {
583 			if (key)
584 				free(key);
585 			if (data)
586 				free(data);
587 			return (rval);
588 		}
589 		data[datalen] = '\0';		/* clear trailing \n */
590 		strncpy(line, data, sizeof(line));
591 		line[sizeof(line) - 1] = '\0';
592 				free(data);
593 		if (! __pwparse(&_pw_passwd, line))
594 			return NS_SUCCESS;
595 	}
596 	/* NOTREACHED */
597 } /* _nis_getpw */
598 #endif
599 
600 #ifdef _PASSWD_COMPAT
601 /*
602  * See if the compat token is in the database.  Only works if pwd_mkdb knows
603  * about the token.
604  */
605 static int	__has_compatpw __P((void));
606 
607 static int
608 __has_compatpw()
609 {
610 	DBT key, data;
611 	DBT pkey, pdata;
612 	char bf[MAXLOGNAME];
613 	u_char cyp[] = { _PW_KEYYPENABLED };
614 
615 	/*LINTED*/
616 	key.data = cyp;
617 	key.size = 1;
618 
619 	/* Pre-token database support. */
620 	bf[0] = _PW_KEYBYNAME;
621 	bf[1] = '+';
622 	pkey.data = (u_char *)bf;
623 	pkey.size = 2;
624 
625 	if ((_pw_db->get)(_pw_db, &key, &data, 0)
626 	    && (_pw_db->get)(_pw_db, &pkey, &pdata, 0))
627 		return 0;		/* No compat token */
628 	return 1;
629 }
630 
631 /*
632  * log an error if "files" or "compat" is specified in passwd_compat database
633  */
634 static int	_bad_getpw __P((void *, void *, va_list));
635 
636 /*ARGSUSED*/
637 static int
638 _bad_getpw(rv, cb_data, ap)
639 	void	*rv;
640 	void	*cb_data;
641 	va_list	 ap;
642 {
643 	static int warned;
644 	if (!warned) {
645 		syslog(LOG_ERR,
646 			"nsswitch.conf passwd_compat database can't use '%s'",
647 			(char *)cb_data);
648 	}
649 	warned = 1;
650 	return NS_UNAVAIL;
651 }
652 
653 /*
654  * when a name lookup in compat mode is required (e.g., '+name', or a name in
655  * '+@netgroup'), look it up in the 'passwd_compat' nsswitch database.
656  * only Hesiod and NIS is supported - it doesn't make sense to lookup
657  * compat names from 'files' or 'compat'.
658  */
659 static int	__getpwcompat __P((int, uid_t, const char *));
660 
661 static int
662 __getpwcompat(type, uid, name)
663 	int		 type;
664 	uid_t		 uid;
665 	const char	*name;
666 {
667 	static const ns_dtab dtab[] = {
668 		NS_FILES_CB(_bad_getpw, "files")
669 		NS_DNS_CB(_dns_getpw, NULL)
670 		NS_NIS_CB(_nis_getpw, NULL)
671 		NS_COMPAT_CB(_bad_getpw, "compat")
672 		{ 0 }
673 	};
674 	static const ns_src defaultnis[] = {
675 		{ NSSRC_NIS, 	NS_SUCCESS },
676 		{ 0 }
677 	};
678 
679 	switch (type) {
680 	case _PW_KEYBYNUM:
681 		return nsdispatch(NULL, dtab, NSDB_PASSWD_COMPAT, "getpwcompat",
682 		    defaultnis, type);
683 	case _PW_KEYBYNAME:
684 		return nsdispatch(NULL, dtab, NSDB_PASSWD_COMPAT, "getpwcompat",
685 		    defaultnis, type, name);
686 	case _PW_KEYBYUID:
687 		return nsdispatch(NULL, dtab, NSDB_PASSWD_COMPAT, "getpwcompat",
688 		    defaultnis, type, uid);
689 	default:
690 		abort();
691 		/*NOTREACHED*/
692 	}
693 }
694 #endif /* _PASSWD_COMPAT */
695 
696 /*
697  * compat implementation of getpwent()
698  * varargs (ignored):
699  *	type, [ uid (type == _PW_KEYBYUID) | name (type == _PW_KEYBYNAME) ]
700  */
701 static int	_compat_getpwent __P((void *, void *, va_list));
702 
703 /*ARGSUSED*/
704 static int
705 _compat_getpwent(rv, cb_data, ap)
706 	void	*rv;
707 	void	*cb_data;
708 	va_list	 ap;
709 {
710 	DBT		 key;
711 	int		 rval;
712 	char		 bf[sizeof(_pw_keynum) + 1];
713 #ifdef _PASSWD_COMPAT
714 	static char	*name = NULL;
715 	char		*user, *host, *dom;
716 	int		 has_compatpw;
717 #endif
718 
719 	if (!_pw_db && !__initdb())
720 		return NS_UNAVAIL;
721 
722 #ifdef _PASSWD_COMPAT
723 	has_compatpw = __has_compatpw();
724 
725 again:
726 	if (has_compatpw && (__pwmode != PWMODE_NONE)) {
727 		int r;
728 
729 		switch (__pwmode) {
730 		case PWMODE_FULL:
731 			r = __getpwcompat(_PW_KEYBYNUM, 0, NULL);
732 			if (r == NS_SUCCESS)
733 				return r;
734 			__pwmode = PWMODE_NONE;
735 			break;
736 
737 		case PWMODE_NETGRP:
738 			r = getnetgrent(&host, &user, &dom);
739 			if (r == 0) {	/* end of group */
740 				endnetgrent();
741 				__pwmode = PWMODE_NONE;
742 				break;
743 			}
744 			if (!user || !*user)
745 				break;
746 			r = __getpwcompat(_PW_KEYBYNAME, 0, user);
747 			if (r == NS_SUCCESS)
748 				return r;
749 			break;
750 
751 		case PWMODE_USER:
752 			if (name == NULL) {
753 				__pwmode = PWMODE_NONE;
754 				break;
755 			}
756 			r = __getpwcompat(_PW_KEYBYNAME, 0, name);
757 			free(name);
758 			name = NULL;
759 			if (r == NS_SUCCESS)
760 				return r;
761 			break;
762 
763 		case PWMODE_NONE:
764 			abort();
765 		}
766 		goto again;
767 	}
768 #endif
769 
770 	if (_pw_keynum == -1)
771 		return NS_NOTFOUND;	/* no more local records */
772 	++_pw_keynum;
773 	bf[0] = _PW_KEYBYNUM;
774 	memmove(bf + 1, &_pw_keynum, sizeof(_pw_keynum));
775 	key.data = (u_char *)bf;
776 	key.size = sizeof(_pw_keynum) + 1;
777 	rval = __hashpw(&key);
778 	if (rval == NS_NOTFOUND)
779 		_pw_keynum = -1;	/* flag `no more local records' */
780 	else if (rval == NS_SUCCESS) {
781 #ifdef _PASSWD_COMPAT
782 		/* if we don't have YP at all, don't bother. */
783 		if (has_compatpw) {
784 			if(_pw_passwd.pw_name[0] == '+') {
785 				/* set the mode */
786 				switch(_pw_passwd.pw_name[1]) {
787 				case '\0':
788 					__pwmode = PWMODE_FULL;
789 					break;
790 				case '@':
791 					__pwmode = PWMODE_NETGRP;
792 					setnetgrent(_pw_passwd.pw_name + 2);
793 					break;
794 				default:
795 					__pwmode = PWMODE_USER;
796 					name = strdup(_pw_passwd.pw_name + 1);
797 					break;
798 				}
799 
800 				/* save the prototype */
801 				__pwproto_set();
802 				goto again;
803 			} else if(_pw_passwd.pw_name[0] == '-') {
804 				/* an attempted exclusion */
805 				switch(_pw_passwd.pw_name[1]) {
806 				case '\0':
807 					break;
808 				case '@':
809 					setnetgrent(_pw_passwd.pw_name + 2);
810 					while(getnetgrent(&host, &user, &dom)) {
811 						if(user && *user)
812 							__pwexclude_add(user);
813 					}
814 					endnetgrent();
815 					break;
816 				default:
817 					__pwexclude_add(_pw_passwd.pw_name + 1);
818 					break;
819 				}
820 				goto again;
821 			}
822 		}
823 #endif
824 	}
825 	return (rval);
826 }
827 
828 /*
829  * compat implementation of getpwnam() and getpwuid()
830  * varargs: type, [ uid (type == _PW_KEYBYUID) | name (type == _PW_KEYBYNAME) ]
831  */
832 static int	_compat_getpw __P((void *, void *, va_list));
833 
834 static int
835 _compat_getpw(rv, cb_data, ap)
836 	void	*rv;
837 	void	*cb_data;
838 	va_list	 ap;
839 {
840 #ifdef _PASSWD_COMPAT
841 	DBT		key;
842 	int		search, rval, r, s, keynum;
843 	uid_t		uid;
844 	char		bf[sizeof(keynum) + 1];
845 	char		*name, *host, *user, *dom;
846 #endif
847 
848 	if (!_pw_db && !__initdb())
849 		return NS_UNAVAIL;
850 
851 		/*
852 		 * If there isn't a compat token in the database, use files.
853 		 */
854 #ifdef _PASSWD_COMPAT
855 	if (! __has_compatpw())
856 #endif
857 		return (_local_getpw(rv, cb_data, ap));
858 
859 #ifdef _PASSWD_COMPAT
860 	search = va_arg(ap, int);
861 	uid = 0;
862 	name = NULL;
863 	rval = NS_NOTFOUND;
864 	switch (search) {
865 	case _PW_KEYBYNAME:
866 		name = va_arg(ap, char *);
867 		break;
868 	case _PW_KEYBYUID:
869 		uid = va_arg(ap, uid_t);
870 		break;
871 	default:
872 		abort();
873 	}
874 
875 	for (s = -1, keynum = 1 ; ; keynum++) {
876 		bf[0] = _PW_KEYBYNUM;
877 		memmove(bf + 1, &keynum, sizeof(keynum));
878 		key.data = (u_char *)bf;
879 		key.size = sizeof(keynum) + 1;
880 		if(__hashpw(&key) != NS_SUCCESS)
881 			break;
882 		switch(_pw_passwd.pw_name[0]) {
883 		case '+':
884 			/* save the prototype */
885 			__pwproto_set();
886 
887 			switch(_pw_passwd.pw_name[1]) {
888 			case '\0':
889 				r = __getpwcompat(search, uid, name);
890 				if (r != NS_SUCCESS)
891 					continue;
892 				break;
893 			case '@':
894 pwnam_netgrp:
895 #if 0			/* XXX: is this a hangover from pre-nsswitch?  */
896 				if(__ypcurrent) {
897 					free(__ypcurrent);
898 					__ypcurrent = NULL;
899 				}
900 #endif
901 				if (s == -1)		/* first time */
902 					setnetgrent(_pw_passwd.pw_name + 2);
903 				s = getnetgrent(&host, &user, &dom);
904 				if (s == 0) {		/* end of group */
905 					endnetgrent();
906 					s = -1;
907 					continue;
908 				}
909 				if (!user || !*user)
910 					goto pwnam_netgrp;
911 
912 				r = __getpwcompat(_PW_KEYBYNAME, 0, user);
913 
914 				if (r == NS_UNAVAIL)
915 					return r;
916 				if (r == NS_NOTFOUND) {
917 					/*
918 					 * just because this user is bad
919 					 * it doesn't mean they all are.
920 					 */
921 					goto pwnam_netgrp;
922 				}
923 				break;
924 			default:
925 				user = _pw_passwd.pw_name + 1;
926 				r = __getpwcompat(_PW_KEYBYNAME, 0, user);
927 
928 				if (r == NS_UNAVAIL)
929 					return r;
930 				if (r == NS_NOTFOUND)
931 					continue;
932 				break;
933 			}
934 			if(__pwexclude_is(_pw_passwd.pw_name)) {
935 				if(s == 1)		/* inside netgroup */
936 					goto pwnam_netgrp;
937 				continue;
938 			}
939 			break;
940 		case '-':
941 			/* attempted exclusion */
942 			switch(_pw_passwd.pw_name[1]) {
943 			case '\0':
944 				break;
945 			case '@':
946 				setnetgrent(_pw_passwd.pw_name + 2);
947 				while(getnetgrent(&host, &user, &dom)) {
948 					if(user && *user)
949 						__pwexclude_add(user);
950 				}
951 				endnetgrent();
952 				break;
953 			default:
954 				__pwexclude_add(_pw_passwd.pw_name + 1);
955 				break;
956 			}
957 			break;
958 		}
959 		if ((search == _PW_KEYBYNAME &&
960 			    strcmp(_pw_passwd.pw_name, name) == 0)
961 		 || (search == _PW_KEYBYUID && _pw_passwd.pw_uid == uid)) {
962 			rval = NS_SUCCESS;
963 			break;
964 		}
965 		if(s == 1)				/* inside netgroup */
966 			goto pwnam_netgrp;
967 		continue;
968 	}
969 	__pwproto = (struct passwd *)NULL;
970 
971 	if (!_pw_stayopen) {
972 		(void)(_pw_db->close)(_pw_db);
973 		_pw_db = (DB *)NULL;
974 	}
975 	if(__pwexclude != (DB *)NULL) {
976 		(void)(__pwexclude->close)(__pwexclude);
977 			__pwexclude = (DB *)NULL;
978 	}
979 	return rval;
980 #endif /* _PASSWD_COMPAT */
981 }
982 
983 struct passwd *
984 getpwent()
985 {
986 	int		r;
987 	static const ns_dtab dtab[] = {
988 		NS_FILES_CB(_local_getpw, NULL)
989 		NS_DNS_CB(_dns_getpw, NULL)
990 		NS_NIS_CB(_nis_getpw, NULL)
991 		NS_COMPAT_CB(_compat_getpwent, NULL)
992 		{ 0 }
993 	};
994 
995 	r = nsdispatch(NULL, dtab, NSDB_PASSWD, "getpwent", compatsrc,
996 	    _PW_KEYBYNUM);
997 	if (r != NS_SUCCESS)
998 		return (struct passwd *)NULL;
999 	return &_pw_passwd;
1000 }
1001 
1002 struct passwd *
1003 getpwnam(name)
1004 	const char *name;
1005 {
1006 	int		r;
1007 	static const ns_dtab dtab[] = {
1008 		NS_FILES_CB(_local_getpw, NULL)
1009 		NS_DNS_CB(_dns_getpw, NULL)
1010 		NS_NIS_CB(_nis_getpw, NULL)
1011 		NS_COMPAT_CB(_compat_getpw, NULL)
1012 		{ 0 }
1013 	};
1014 
1015 	if (name == NULL || name[0] == '\0')
1016 		return (struct passwd *)NULL;
1017 
1018 	r = nsdispatch(NULL, dtab, NSDB_PASSWD, "getpwnam", compatsrc,
1019 	    _PW_KEYBYNAME, name);
1020 	return (r == NS_SUCCESS ? &_pw_passwd : (struct passwd *)NULL);
1021 }
1022 
1023 struct passwd *
1024 getpwuid(uid)
1025 	uid_t uid;
1026 {
1027 	int		r;
1028 	static const ns_dtab dtab[] = {
1029 		NS_FILES_CB(_local_getpw, NULL)
1030 		NS_DNS_CB(_dns_getpw, NULL)
1031 		NS_NIS_CB(_nis_getpw, NULL)
1032 		NS_COMPAT_CB(_compat_getpw, NULL)
1033 		{ 0 }
1034 	};
1035 
1036 	r = nsdispatch(NULL, dtab, NSDB_PASSWD, "getpwuid", compatsrc,
1037 	    _PW_KEYBYUID, uid);
1038 	return (r == NS_SUCCESS ? &_pw_passwd : (struct passwd *)NULL);
1039 }
1040 
1041 int
1042 setpassent(stayopen)
1043 	int stayopen;
1044 {
1045 	_pw_keynum = 0;
1046 	_pw_stayopen = stayopen;
1047 #ifdef YP
1048 	__pwmode = PWMODE_NONE;
1049 	if(__ypcurrent)
1050 		free(__ypcurrent);
1051 	__ypcurrent = NULL;
1052 	_pw_ypdone = 0;
1053 #endif
1054 #ifdef HESIOD
1055 	_pw_hesnum = 0;
1056 #endif
1057 #ifdef _PASSWD_COMPAT
1058 	if(__pwexclude != (DB *)NULL) {
1059 		(void)(__pwexclude->close)(__pwexclude);
1060 		__pwexclude = (DB *)NULL;
1061 	}
1062 	__pwproto = (struct passwd *)NULL;
1063 #endif
1064 	return 1;
1065 }
1066 
1067 void
1068 setpwent()
1069 {
1070 	(void) setpassent(0);
1071 }
1072 
1073 void
1074 endpwent()
1075 {
1076 	_pw_keynum = 0;
1077 	if (_pw_db) {
1078 		(void)(_pw_db->close)(_pw_db);
1079 		_pw_db = (DB *)NULL;
1080 	}
1081 #ifdef _PASSWD_COMPAT
1082 	__pwmode = PWMODE_NONE;
1083 #endif
1084 #ifdef YP
1085 	if(__ypcurrent)
1086 		free(__ypcurrent);
1087 	__ypcurrent = NULL;
1088 	_pw_ypdone = 0;
1089 #endif
1090 #ifdef HESIOD
1091 	_pw_hesnum = 0;
1092 #endif
1093 #ifdef _PASSWD_COMPAT
1094 	if(__pwexclude != (DB *)NULL) {
1095 		(void)(__pwexclude->close)(__pwexclude);
1096 		__pwexclude = (DB *)NULL;
1097 	}
1098 	__pwproto = (struct passwd *)NULL;
1099 #endif
1100 }
1101 
1102 static int
1103 __initdb()
1104 {
1105 	static int warned;
1106 	char *p;
1107 
1108 #ifdef _PASSWD_COMPAT
1109 	__pwmode = PWMODE_NONE;
1110 #endif
1111 	if (geteuid() == 0) {
1112 		_pw_db = dbopen((p = _PATH_SMP_DB), O_RDONLY, 0, DB_HASH, NULL);
1113 		if (_pw_db)
1114 			return(1);
1115 	}
1116 	_pw_db = dbopen((p = _PATH_MP_DB), O_RDONLY, 0, DB_HASH, NULL);
1117 	if (_pw_db)
1118 		return 1;
1119 	if (!warned)
1120 		syslog(LOG_ERR, "%s: %m", p);
1121 	warned = 1;
1122 	return 0;
1123 }
1124 
1125 static int
1126 __hashpw(key)
1127 	DBT *key;
1128 {
1129 	char *p, *t;
1130 	static u_int max;
1131 	static char *buf;
1132 	DBT data;
1133 
1134 	switch ((_pw_db->get)(_pw_db, key, &data, 0)) {
1135 	case 0:
1136 		break;			/* found */
1137 	case 1:
1138 		return NS_NOTFOUND;
1139 	case -1:
1140 		return NS_UNAVAIL;	/* error in db routines */
1141 	default:
1142 		abort();
1143 	}
1144 
1145 	p = (char *)data.data;
1146 	if (data.size > max && !(buf = realloc(buf, (max += 1024))))
1147 		return NS_UNAVAIL;
1148 
1149 	/* THIS CODE MUST MATCH THAT IN pwd_mkdb. */
1150 	t = buf;
1151 #define	EXPAND(e)	e = t; while ((*t++ = *p++));
1152 #define	SCALAR(v)	memmove(&(v), p, sizeof v); p += sizeof v
1153 	EXPAND(_pw_passwd.pw_name);
1154 	EXPAND(_pw_passwd.pw_passwd);
1155 	SCALAR(_pw_passwd.pw_uid);
1156 	SCALAR(_pw_passwd.pw_gid);
1157 	SCALAR(_pw_passwd.pw_change);
1158 	EXPAND(_pw_passwd.pw_class);
1159 	EXPAND(_pw_passwd.pw_gecos);
1160 	EXPAND(_pw_passwd.pw_dir);
1161 	EXPAND(_pw_passwd.pw_shell);
1162 	SCALAR(_pw_passwd.pw_expire);
1163 	SCALAR(_pw_passwd.pw_fields);
1164 
1165 	return NS_SUCCESS;
1166 }
1167