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