getpwent.c revision 85572
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)
39static const char *rcsid[] =
40  "$FreeBSD: head/lib/libc/gen/getpwent.c 85572 2001-10-27 02:13:41Z peter $";
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
68extern void setnetgrent __P((char *));
69extern int getnetgrent __P((char **, char **, char **));
70extern 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
83static struct passwd _pw_passwd = { "", "", 0, 0, 0, "", "", "", "", 0, 0 };
84static DB *_pw_db;			/* password database */
85static int _pw_keynum;			/* key counter. no more records if -1 */
86static int _pw_stayopen;		/* keep fd's open */
87
88static int __hashpw __P((DBT *));
89static int __initdb __P((void));
90
91static const ns_src compatsrc[] = {
92	{ NSSRC_COMPAT, NS_SUCCESS },
93	{ 0 }
94};
95
96#ifdef YP
97static char     *__ypcurrent, *__ypdomain;
98static int      __ypcurrentlen;
99static int	_pw_ypdone;		/* non-zero if no more yp records */
100#endif
101
102#ifdef HESIOD
103static int	_pw_hesnum;		/* hes counter. no more records if -1 */
104#endif
105
106#ifdef _PASSWD_COMPAT
107enum _pwmode { PWMODE_NONE, PWMODE_FULL, PWMODE_USER, PWMODE_NETGRP };
108static enum _pwmode __pwmode;
109
110enum _ypmap { YPMAP_NONE, YPMAP_ADJUNCT, YPMAP_MASTER };
111
112static struct passwd	*__pwproto = (struct passwd *)NULL;
113static int		 __pwproto_flags;
114static char		 line[1024];
115static long		 prbuf[1024 / sizeof(long)];
116static DB		*__pwexclude = (DB *)NULL;
117
118static int	__pwexclude_add __P((const char *));
119static int	__pwexclude_is __P((const char *));
120static void	__pwproto_set __P((void));
121static int	__ypmaptype __P((void));
122static 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 */
133static 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 */
166static 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 */
192static 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
255static 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 */
301static 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 */
358static int	_local_getpw __P((void *, void *, va_list));
359
360/*ARGSUSED*/
361static 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 */
420static int	_dns_getpw __P((void *, void *, va_list));
421
422/*ARGSUSED*/
423static 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 */
498static int	_nis_getpw __P((void *, void *, va_list));
499
500/*ARGSUSED*/
501static 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 */
605static int	__has_compatpw __P((void));
606
607static 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 */
634static int	_bad_getpw __P((void *, void *, va_list));
635
636/*ARGSUSED*/
637static 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 */
659static int	__getpwcompat __P((int, uid_t, const char *));
660
661static 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 */
701static int	_compat_getpwent __P((void *, void *, va_list));
702
703/*ARGSUSED*/
704static 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
725again:
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 */
832static int	_compat_getpw __P((void *, void *, va_list));
833
834static 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 '@':
894pwnam_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
983struct passwd *
984getpwent()
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
1002struct passwd *
1003getpwnam(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
1023struct passwd *
1024getpwuid(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
1041int
1042setpassent(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
1067void
1068setpwent()
1069{
1070	(void) setpassent(0);
1071}
1072
1073void
1074endpwent()
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
1102static 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
1125static int
1126__hashpw(key)
1127	DBT *key;
1128{
1129	char *p, *t;
1130	static u_int max;
1131	static char *buf;
1132	int32_t pw_change, pw_expire;
1133	DBT data;
1134
1135	switch ((_pw_db->get)(_pw_db, key, &data, 0)) {
1136	case 0:
1137		break;			/* found */
1138	case 1:
1139		return NS_NOTFOUND;
1140	case -1:
1141		return NS_UNAVAIL;	/* error in db routines */
1142	default:
1143		abort();
1144	}
1145
1146	p = (char *)data.data;
1147	if (data.size > max && !(buf = realloc(buf, (max += 1024))))
1148		return NS_UNAVAIL;
1149
1150	/* THIS CODE MUST MATCH THAT IN pwd_mkdb. */
1151	t = buf;
1152#define	EXPAND(e)	e = t; while ((*t++ = *p++));
1153#define	SCALAR(v)	memmove(&(v), p, sizeof v); p += sizeof v
1154	EXPAND(_pw_passwd.pw_name);
1155	EXPAND(_pw_passwd.pw_passwd);
1156	SCALAR(_pw_passwd.pw_uid);
1157	SCALAR(_pw_passwd.pw_gid);
1158	SCALAR(pw_change);
1159	EXPAND(_pw_passwd.pw_class);
1160	EXPAND(_pw_passwd.pw_gecos);
1161	EXPAND(_pw_passwd.pw_dir);
1162	EXPAND(_pw_passwd.pw_shell);
1163	SCALAR(pw_expire);
1164	SCALAR(_pw_passwd.pw_fields);
1165	_pw_passwd.pw_change = pw_change;
1166	_pw_passwd.pw_expire = pw_expire;
1167
1168	return NS_SUCCESS;
1169}
1170