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