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