1/*	$NetBSD: getpwent.c,v 1.84 2024/01/20 14:52:47 christos Exp $	*/
2
3/*-
4 * Copyright (c) 1997-2000, 2004-2005 The NetBSD Foundation, Inc.
5 * All rights reserved.
6 *
7 * This code is derived from software contributed to The NetBSD Foundation
8 * by Luke Mewburn.
9 *
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
12 * are met:
13 * 1. Redistributions of source code must retain the above copyright
14 *    notice, this list of conditions and the following disclaimer.
15 * 2. Redistributions in binary form must reproduce the above copyright
16 *    notice, this list of conditions and the following disclaimer in the
17 *    documentation and/or other materials provided with the distribution.
18 *
19 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
20 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
21 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22 * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
23 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29 * POSSIBILITY OF SUCH DAMAGE.
30 */
31
32/*
33 * Copyright (c) 1988, 1993
34 *	The Regents of the University of California.  All rights reserved.
35 *
36 * Redistribution and use in source and binary forms, with or without
37 * modification, are permitted provided that the following conditions
38 * are met:
39 * 1. Redistributions of source code must retain the above copyright
40 *    notice, this list of conditions and the following disclaimer.
41 * 2. Redistributions in binary form must reproduce the above copyright
42 *    notice, this list of conditions and the following disclaimer in the
43 *    documentation and/or other materials provided with the distribution.
44 * 3. Neither the name of the University nor the names of its contributors
45 *    may be used to endorse or promote products derived from this software
46 *    without specific prior written permission.
47 *
48 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
49 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
50 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
51 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
52 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
53 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
54 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
55 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
56 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
57 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
58 * SUCH DAMAGE.
59 */
60
61/*
62 * Portions Copyright (c) 1994, 1995, Jason Downs.  All rights reserved.
63 *
64 * Redistribution and use in source and binary forms, with or without
65 * modification, are permitted provided that the following conditions
66 * are met:
67 * 1. Redistributions of source code must retain the above copyright
68 *    notice, this list of conditions and the following disclaimer.
69 * 2. Redistributions in binary form must reproduce the above copyright
70 *    notice, this list of conditions and the following disclaimer in the
71 *    documentation and/or other materials provided with the distribution.
72 *
73 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS
74 * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
75 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
76 * DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT,
77 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
78 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
79 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
80 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
81 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
82 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
83 * SUCH DAMAGE.
84 */
85
86#include <sys/cdefs.h>
87#if defined(LIBC_SCCS) && !defined(lint)
88#if 0
89static char sccsid[] = "@(#)getpwent.c	8.2 (Berkeley) 4/27/95";
90#else
91__RCSID("$NetBSD: getpwent.c,v 1.84 2024/01/20 14:52:47 christos Exp $");
92#endif
93#endif /* LIBC_SCCS and not lint */
94
95#include "namespace.h"
96#include "reentrant.h"
97
98#include <sys/param.h>
99
100#include <assert.h>
101#include <db.h>
102#include <errno.h>
103#include <fcntl.h>
104#include <limits.h>
105#include <netgroup.h>
106#include <nsswitch.h>
107#include <pwd.h>
108#include <stdarg.h>
109#include <stdio.h>
110#include <stdlib.h>
111#include <string.h>
112#include <syslog.h>
113#include <unistd.h>
114#include "pw_private.h"
115
116#ifdef HESIOD
117#include <hesiod.h>
118#endif
119
120#ifdef YP
121#include <rpc/rpc.h>
122#include <rpcsvc/yp_prot.h>
123#include <rpcsvc/ypclnt.h>
124#endif
125
126#include "pw_private.h"
127
128#define	_PASSWD_COMPAT	/* "passwd" defaults to compat, so always provide it */
129
130#ifdef __weak_alias
131__weak_alias(endpwent,_endpwent)
132__weak_alias(setpassent,_setpassent)
133__weak_alias(setpwent,_setpwent)
134#endif
135
136#ifdef _REENTRANT
137static 	mutex_t			_pwmutex = MUTEX_INITIALIZER;
138#endif
139
140const char __yp_token[] = "__YP!";	/* Let pwd_mkdb pull this in. */
141
142
143/*
144 * The pwd.db lookup techniques and data extraction code here must be kept
145 * in sync with that in `pwd_mkdb'.
146 */
147
148#if defined(YP) || defined(HESIOD)
149/*
150 * _pw_parse
151 *	Parses entry using pw_scan(3) (without the trailing \n)
152 *	after copying to buf, and fills in pw with corresponding values.
153 *	If old is non-zero, entry is in _PASSWORD_OLDFMT.
154 *	Returns 1 if parsed successfully, 0 on parse failure.
155 */
156static int
157_pw_parse(const char *entry, struct passwd *pw, char *buf, size_t buflen,
158	int old)
159{
160	int	flags;
161
162	_DIAGASSERT(entry != NULL);
163	_DIAGASSERT(pw != NULL);
164	_DIAGASSERT(buf != NULL);
165
166	if (strlcpy(buf, entry, buflen) >= buflen)
167		return 0;
168	flags = _PASSWORD_NOWARN;
169	if (old)
170		flags |= _PASSWORD_OLDFMT;
171	return __pw_scan(buf, pw, &flags);
172}
173#endif /* YP || HESIOD */
174
175/*
176 * _pw_opendb
177 *	if *db is NULL, dbopen(3) /etc/spwd.db or /etc/pwd.db (depending
178 *	upon permissions, etc)
179 */
180static int
181_pw_opendb(DB **db, int *version)
182{
183	static int	warned;
184	DBT		key;
185	DBT		value;
186
187	const char	*dbfile = NULL;
188
189	_DIAGASSERT(db != NULL);
190	_DIAGASSERT(version != NULL);
191	if (*db != NULL)					/* open *db */
192		return NS_SUCCESS;
193
194	if (geteuid() == 0) {
195		dbfile = _PATH_SMP_DB;
196		*db = dbopen(dbfile, O_RDONLY, 0, DB_HASH, NULL);
197	}
198	if (*db == NULL) {
199		dbfile = _PATH_MP_DB;
200		*db = dbopen(dbfile, O_RDONLY, 0, DB_HASH, NULL);
201	}
202	if (*db == NULL) {
203		if (!warned) {
204			int	serrno = errno;
205			syslog(LOG_ERR, "%s: %m", dbfile);
206			errno = serrno;
207		}
208		warned = 1;
209		return NS_UNAVAIL;
210	}
211	key.data = __UNCONST("VERSION");
212	key.size = strlen((char *)key.data) + 1;
213	switch ((*(*db)->get)(*db, &key, &value, 0)) {
214	case 0:
215		if (sizeof(*version) != value.size)
216			return NS_UNAVAIL;
217		(void)memcpy(version, value.data, value.size);
218		break;			/* found */
219	case 1:
220		*version = 0;		/* not found */
221		break;
222	case -1:
223		return NS_UNAVAIL;	/* error in db routines */
224	default:
225		abort();
226	}
227	return NS_SUCCESS;
228}
229
230/*
231 * _pw_getkey
232 *	Lookup key in *db, filling in pw
233 *	with the result, allocating memory from buffer (size buflen).
234 *	(The caller may point key.data to buffer on entry; the contents
235 *	of key.data will be invalid on exit.)
236 */
237static int
238_pw_getkey(DB *db, DBT *key,
239	struct passwd *pw, char *buffer, size_t buflen, int *pwflags,
240	int version)
241{
242	char		*p, *t;
243	DBT		data;
244
245	_DIAGASSERT(db != NULL);
246	_DIAGASSERT(key != NULL);
247	_DIAGASSERT(pw != NULL);
248	_DIAGASSERT(buffer != NULL);
249	/* pwflags may be NULL (if we don't care about them */
250
251	if (db == NULL)			/* this shouldn't happen */
252		return NS_UNAVAIL;
253
254	switch ((db->get)(db, key, &data, 0)) {
255	case 0:
256		break;			/* found */
257	case 1:
258		return NS_NOTFOUND;	/* not found */
259	case -1:
260		return NS_UNAVAIL;	/* error in db routines */
261	default:
262		abort();
263	}
264
265	p = (char *)data.data;
266	if (data.size > buflen) {
267		errno = ERANGE;
268		return NS_UNAVAIL;
269	}
270
271			/*
272			 * THE DECODING BELOW MUST MATCH THAT IN pwd_mkdb.
273			 */
274	t = buffer;
275#define MACRO(a)	do { a } while (0)
276#define	EXPAND(e)	MACRO(e = t; while ((*t++ = *p++));)
277#define	SCALAR(v)	MACRO(memmove(&(v), p, sizeof v); p += sizeof v;)
278	EXPAND(pw->pw_name);
279	EXPAND(pw->pw_passwd);
280	SCALAR(pw->pw_uid);
281	SCALAR(pw->pw_gid);
282	if (version == 0) {
283		int32_t tmp;
284		SCALAR(tmp);
285		pw->pw_change = tmp;
286	} else
287		SCALAR(pw->pw_change);
288	EXPAND(pw->pw_class);
289	EXPAND(pw->pw_gecos);
290	EXPAND(pw->pw_dir);
291	EXPAND(pw->pw_shell);
292	if (version == 0) {
293		int32_t tmp;
294		SCALAR(tmp);
295		pw->pw_expire = tmp;
296	} else
297		SCALAR(pw->pw_expire);
298	if (pwflags) {
299		/* See if there's any data left.  If so, read in flags. */
300		if (data.size > (size_t) (p - (char *)data.data)) {
301			SCALAR(*pwflags);
302		} else {				/* default */
303			*pwflags = _PASSWORD_NOUID|_PASSWORD_NOGID;
304		}
305	}
306
307	return NS_SUCCESS;
308}
309
310/*
311 * _pw_memfrombuf
312 *	Obtain want bytes from buffer (of size buflen) and return a pointer
313 *	to the available memory after adjusting buffer/buflen.
314 *	Returns NULL if there is insufficient space.
315 */
316static char *
317_pw_memfrombuf(size_t want, char **buffer, size_t *buflen)
318{
319	char	*rv;
320
321	if (want > *buflen) {
322		errno = ERANGE;
323		return NULL;
324	}
325	rv = *buffer;
326	*buffer += want;
327	*buflen -= want;
328	return rv;
329}
330
331/*
332 * _pw_copy
333 *	Copy the contents of frompw to pw; memory for strings
334 *	and arrays will be allocated from buf (of size buflen).
335 *	If proto != NULL, use various fields in proto in preference to frompw.
336 *	Returns 1 if copied successfully, 0 on copy failure.
337 *	NOTE: frompw must not use buf for its own pointers.
338 */
339static int
340_pw_copy(const struct passwd *frompw, struct passwd *pw,
341	char *buf, size_t buflen, const struct passwd *protopw, int protoflags)
342{
343	size_t	count;
344	int	useproto;
345
346	_DIAGASSERT(frompw != NULL);
347	_DIAGASSERT(pw != NULL);
348	_DIAGASSERT(buf != NULL);
349	/* protopw may be NULL */
350
351	useproto = protopw && protopw->pw_name;
352
353#define	COPYSTR(to, from) \
354	do { \
355		count = strlen((from)); \
356		(to) = _pw_memfrombuf(count+1, &buf, &buflen); \
357		if ((to) == NULL) \
358			return 0; \
359		memmove((to), (from), count); \
360		to[count] = '\0'; \
361	} while (0)	/* LINTED */
362
363#define	COPYFIELD(field)	COPYSTR(pw->field, frompw->field)
364
365#define	COPYPROTOFIELD(field)	COPYSTR(pw->field, \
366		(useproto && *protopw->field ? protopw->field : frompw->field))
367
368	COPYFIELD(pw_name);
369
370#ifdef PW_OVERRIDE_PASSWD
371	COPYPROTOFIELD(pw_passwd);
372#else
373	COPYFIELD(pw_passwd);
374#endif
375
376	if (useproto && !(protoflags & _PASSWORD_NOUID))
377		pw->pw_uid = protopw->pw_uid;
378	else
379		pw->pw_uid = frompw->pw_uid;
380
381	if (useproto && !(protoflags & _PASSWORD_NOGID))
382		pw->pw_gid = protopw->pw_gid;
383	else
384		pw->pw_gid = frompw->pw_gid;
385
386	pw->pw_change = frompw->pw_change;
387	COPYFIELD(pw_class);
388	COPYPROTOFIELD(pw_gecos);
389	COPYPROTOFIELD(pw_dir);
390	COPYPROTOFIELD(pw_shell);
391
392#undef COPYSTR
393#undef COPYFIELD
394#undef COPYPROTOFIELD
395
396	return 1;
397}
398
399
400		/*
401		 *	files methods
402		 */
403
404	/* state shared between files methods */
405struct files_state {
406	int	 stayopen;		/* see getpassent(3) */
407	DB	*db;			/* passwd file handle */
408	int	 keynum;		/* key counter, -1 if no more */
409	int	 version;
410};
411
412static struct files_state	_files_state;
413					/* storage for non _r functions */
414static struct passwd		_files_passwd;
415static char			_files_passwdbuf[_GETPW_R_SIZE_MAX];
416
417static int
418_files_start(struct files_state *state)
419{
420	int	rv;
421
422	_DIAGASSERT(state != NULL);
423
424	state->keynum = 0;
425	rv = _pw_opendb(&state->db, &state->version);
426	if (rv != NS_SUCCESS)
427		return rv;
428	return NS_SUCCESS;
429}
430
431static int
432_files_end(struct files_state *state)
433{
434
435	_DIAGASSERT(state != NULL);
436
437	state->keynum = 0;
438	if (state->db) {
439		(void)(state->db->close)(state->db);
440		state->db = NULL;
441	}
442	return NS_SUCCESS;
443}
444
445/*
446 * _files_pwscan
447 *	Search state->db for the next desired entry.
448 *	If search is _PW_KEYBYNUM, look for state->keynum.
449 *	If search is _PW_KEYBYNAME, look for name.
450 *	If search is _PW_KEYBYUID, look for uid.
451 *	Sets *retval to the errno if the result is not NS_SUCCESS
452 *	or NS_NOTFOUND.
453 */
454static int
455_files_pwscan(int *retval, struct passwd *pw, char *buffer, size_t buflen,
456	struct files_state *state, int search, const char *name, uid_t uid)
457{
458	const void	*from;
459	size_t		 fromlen;
460	DBT		 key;
461	int		 rv;
462
463	_DIAGASSERT(retval != NULL);
464	_DIAGASSERT(pw != NULL);
465	_DIAGASSERT(buffer != NULL);
466	_DIAGASSERT(state != NULL);
467	/* name is NULL to indicate searching for uid */
468
469	*retval = 0;
470
471	if (state->db == NULL) {	/* only start if file not open yet */
472		rv = _files_start(state);
473		if (rv != NS_SUCCESS)
474			goto filespwscan_out;
475	}
476
477	for (;;) {				/* search for a match */
478		switch (search) {
479		case _PW_KEYBYNUM:
480			if (state->keynum == -1)
481				return NS_NOTFOUND;	/* no more records */
482			state->keynum++;
483			from = &state->keynum;
484			fromlen = sizeof(state->keynum);
485			break;
486		case _PW_KEYBYNAME:
487			from = name;
488			fromlen = strlen(name);
489			break;
490		case _PW_KEYBYUID:
491			from = &uid;
492			fromlen = sizeof(uid);
493			break;
494		default:
495			abort();
496		}
497
498		if (buflen <= fromlen) {		/* buffer too small */
499			*retval = ERANGE;
500			return NS_UNAVAIL;
501		}
502		buffer[0] = search;			/* setup key */
503		memmove(buffer + 1, from, fromlen);
504		key.size = fromlen + 1;
505		key.data = (u_char *)buffer;
506
507							/* search for key */
508		rv = _pw_getkey(state->db, &key, pw, buffer, buflen, NULL,
509		    state->version);
510		if (rv != NS_SUCCESS)			/* no match */
511			break;
512		if (pw->pw_name[0] == '+' || pw->pw_name[0] == '-') {
513						/* if a compat line */
514			if (search == _PW_KEYBYNUM)
515				continue;	/* read next if pwent */
516			rv = NS_NOTFOUND;	/* don't match if pw{nam,uid} */
517			break;
518		}
519		break;
520	}
521
522	if (rv == NS_NOTFOUND && search == _PW_KEYBYNUM)
523		state->keynum = -1;		/* flag `no more records' */
524
525	if (rv == NS_SUCCESS) {
526		if ((search == _PW_KEYBYUID && pw->pw_uid != uid) ||
527		    (search == _PW_KEYBYNAME && strcmp(pw->pw_name, name) != 0))
528			rv = NS_NOTFOUND;
529	}
530
531 filespwscan_out:
532	if (rv != NS_SUCCESS && rv != NS_NOTFOUND)
533		*retval = errno;
534	return rv;
535}
536
537/*ARGSUSED*/
538static int
539_files_setpwent(void *nsrv, void *nscb, va_list ap)
540{
541
542	_files_state.stayopen = 0;
543	return _files_start(&_files_state);
544}
545
546/*ARGSUSED*/
547static int
548_files_setpassent(void *nsrv, void *nscb, va_list ap)
549{
550	int	*retval		= va_arg(ap, int *);
551	int	 stayopen	= va_arg(ap, int);
552
553	int	rv;
554
555	_files_state.stayopen = stayopen;
556	rv = _files_start(&_files_state);
557	*retval = (rv == NS_SUCCESS);
558	return rv;
559}
560
561/*ARGSUSED*/
562static int
563_files_endpwent(void *nsrv, void *nscb, va_list ap)
564{
565
566	_files_state.stayopen = 0;
567	return _files_end(&_files_state);
568}
569
570/*ARGSUSED*/
571static int
572_files_getpwent(void *nsrv, void *nscb, va_list ap)
573{
574	struct passwd	**retval = va_arg(ap, struct passwd **);
575
576	int	rv, rerror;
577
578	_DIAGASSERT(retval != NULL);
579
580	*retval = NULL;
581	rv = _files_pwscan(&rerror, &_files_passwd,
582	    _files_passwdbuf, sizeof(_files_passwdbuf),
583	    &_files_state, _PW_KEYBYNUM, NULL, 0);
584	if (rv == NS_SUCCESS)
585		*retval = &_files_passwd;
586	return rv;
587}
588
589/*ARGSUSED*/
590static int
591_files_getpwent_r(void *nsrv, void *nscb, va_list ap)
592{
593	int		*retval	= va_arg(ap, int *);
594	struct passwd	*pw	= va_arg(ap, struct passwd *);
595	char		*buffer	= va_arg(ap, char *);
596	size_t		 buflen	= va_arg(ap, size_t);
597	struct passwd  **result	= va_arg(ap, struct passwd **);
598
599	int	rv;
600
601	_DIAGASSERT(retval != NULL);
602	_DIAGASSERT(pw != NULL);
603	_DIAGASSERT(buffer != NULL);
604	_DIAGASSERT(result != NULL);
605
606	rv = _files_pwscan(retval, pw, buffer, buflen, &_files_state,
607	    _PW_KEYBYNUM, NULL, 0);
608	if (rv == NS_SUCCESS)
609		*result = pw;
610	else
611		*result = NULL;
612	return rv;
613}
614
615/*ARGSUSED*/
616static int
617_files_getpwnam(void *nsrv, void *nscb, va_list ap)
618{
619	struct passwd	**retval = va_arg(ap, struct passwd **);
620	const char	*name	= va_arg(ap, const char *);
621
622	int	rv, rerror;
623
624	_DIAGASSERT(retval != NULL);
625
626	*retval = NULL;
627	rv = _files_start(&_files_state);
628	if (rv != NS_SUCCESS)
629		return rv;
630	rv = _files_pwscan(&rerror, &_files_passwd,
631	    _files_passwdbuf, sizeof(_files_passwdbuf),
632	    &_files_state, _PW_KEYBYNAME, name, 0);
633	if (!_files_state.stayopen)
634		_files_end(&_files_state);
635	if (rv == NS_SUCCESS)
636		*retval = &_files_passwd;
637	return rv;
638}
639
640/*ARGSUSED*/
641static int
642_files_getpwnam_r(void *nsrv, void *nscb, va_list ap)
643{
644	int		*retval	= va_arg(ap, int *);
645	const char	*name	= va_arg(ap, const char *);
646	struct passwd	*pw	= va_arg(ap, struct passwd *);
647	char		*buffer	= va_arg(ap, char *);
648	size_t		 buflen	= va_arg(ap, size_t);
649	struct passwd  **result	= va_arg(ap, struct passwd **);
650
651	struct files_state state;
652	int	rv;
653
654	_DIAGASSERT(retval != NULL);
655	_DIAGASSERT(pw != NULL);
656	_DIAGASSERT(buffer != NULL);
657	_DIAGASSERT(result != NULL);
658
659	*result = NULL;
660	memset(&state, 0, sizeof(state));
661	rv = _files_pwscan(retval, pw, buffer, buflen, &state,
662	    _PW_KEYBYNAME, name, 0);
663	_files_end(&state);
664	if (rv == NS_SUCCESS)
665		*result = pw;
666	return rv;
667}
668
669/*ARGSUSED*/
670static int
671_files_getpwuid(void *nsrv, void *nscb, va_list ap)
672{
673	struct passwd	**retval = va_arg(ap, struct passwd **);
674	uid_t		 uid	= va_arg(ap, uid_t);
675
676	int	rv, rerror;
677
678	_DIAGASSERT(retval != NULL);
679
680	*retval = NULL;
681	rv = _files_start(&_files_state);
682	if (rv != NS_SUCCESS)
683		return rv;
684	rv = _files_pwscan(&rerror, &_files_passwd,
685	    _files_passwdbuf, sizeof(_files_passwdbuf),
686	    &_files_state, _PW_KEYBYUID, NULL, uid);
687	if (!_files_state.stayopen)
688		_files_end(&_files_state);
689	if (rv == NS_SUCCESS)
690		*retval = &_files_passwd;
691	return rv;
692}
693
694/*ARGSUSED*/
695static int
696_files_getpwuid_r(void *nsrv, void *nscb, va_list ap)
697{
698	int		*retval	= va_arg(ap, int *);
699	uid_t		 uid	= va_arg(ap, uid_t);
700	struct passwd	*pw	= va_arg(ap, struct passwd *);
701	char		*buffer	= va_arg(ap, char *);
702	size_t		 buflen	= va_arg(ap, size_t);
703	struct passwd  **result	= va_arg(ap, struct passwd **);
704
705	struct files_state state;
706	int	rv;
707
708	_DIAGASSERT(retval != NULL);
709	_DIAGASSERT(pw != NULL);
710	_DIAGASSERT(buffer != NULL);
711	_DIAGASSERT(result != NULL);
712
713	*result = NULL;
714	memset(&state, 0, sizeof(state));
715	rv = _files_pwscan(retval, pw, buffer, buflen, &state,
716	    _PW_KEYBYUID, NULL, uid);
717	_files_end(&state);
718	if (rv == NS_SUCCESS)
719		*result = pw;
720	return rv;
721}
722
723
724#ifdef HESIOD
725		/*
726		 *	dns methods
727		 */
728
729	/* state shared between dns methods */
730struct dns_state {
731	int	 stayopen;		/* see getpassent(3) */
732	void	*context;		/* Hesiod context */
733	int	 num;			/* passwd index, -1 if no more */
734};
735
736static struct dns_state		_dns_state;
737					/* storage for non _r functions */
738static struct passwd		_dns_passwd;
739static char			_dns_passwdbuf[_GETPW_R_SIZE_MAX];
740
741static int
742_dns_start(struct dns_state *state)
743{
744
745	_DIAGASSERT(state != NULL);
746
747	state->num = 0;
748	if (state->context == NULL) {			/* setup Hesiod */
749		if (hesiod_init(&state->context) == -1)
750			return NS_UNAVAIL;
751	}
752
753	return NS_SUCCESS;
754}
755
756static int
757_dns_end(struct dns_state *state)
758{
759
760	_DIAGASSERT(state != NULL);
761
762	state->num = 0;
763	if (state->context) {
764		hesiod_end(state->context);
765		state->context = NULL;
766	}
767	return NS_SUCCESS;
768}
769
770/*
771 * _dns_pwscan
772 *	Look for the Hesiod name provided in buffer in the NULL-terminated
773 *	list of zones,
774 *	and decode into pw/buffer/buflen.
775 */
776static int
777_dns_pwscan(int *retval, struct passwd *pw, char *buffer, size_t buflen,
778	struct dns_state *state, const char **zones)
779{
780	const char	**curzone;
781	char		**hp, *ep;
782	int		rv;
783
784	_DIAGASSERT(retval != NULL);
785	_DIAGASSERT(pw != NULL);
786	_DIAGASSERT(buffer != NULL);
787	_DIAGASSERT(state != NULL);
788	_DIAGASSERT(zones != NULL);
789
790	*retval = 0;
791
792	if (state->context == NULL) {	/* only start if Hesiod not setup */
793		rv = _dns_start(state);
794		if (rv != NS_SUCCESS)
795			return rv;
796	}
797
798	hp = NULL;
799	rv = NS_NOTFOUND;
800
801	for (curzone = zones; *curzone; curzone++) {	/* search zones */
802		hp = hesiod_resolve(state->context, buffer, *curzone);
803		if (hp != NULL)
804			break;
805		if (errno != ENOENT) {
806			rv = NS_UNAVAIL;
807			goto dnspwscan_out;
808		}
809	}
810	if (*curzone == NULL)
811		goto dnspwscan_out;
812
813	if ((ep = strchr(hp[0], '\n')) != NULL)
814		*ep = '\0';				/* clear trailing \n */
815	if (_pw_parse(hp[0], pw, buffer, buflen, 1))	/* validate line */
816		rv = NS_SUCCESS;
817	else
818		rv = NS_UNAVAIL;
819
820 dnspwscan_out:
821	if (rv != NS_SUCCESS && rv != NS_NOTFOUND)
822		*retval = errno;
823	if (hp)
824		hesiod_free_list(state->context, hp);
825	return rv;
826}
827
828/*ARGSUSED*/
829static int
830_dns_setpwent(void *nsrv, void *nscb, va_list ap)
831{
832
833	_dns_state.stayopen = 0;
834	return _dns_start(&_dns_state);
835}
836
837/*ARGSUSED*/
838static int
839_dns_setpassent(void *nsrv, void *nscb, va_list ap)
840{
841	int	*retval		= va_arg(ap, int *);
842	int	 stayopen	= va_arg(ap, int);
843
844	int	rv;
845
846	_dns_state.stayopen = stayopen;
847	rv = _dns_start(&_dns_state);
848	*retval = (rv == NS_SUCCESS);
849	return rv;
850}
851
852/*ARGSUSED*/
853static int
854_dns_endpwent(void *nsrv, void *nscb, va_list ap)
855{
856
857	_dns_state.stayopen = 0;
858	return _dns_end(&_dns_state);
859}
860
861/*ARGSUSED*/
862static int
863_dns_getpwent(void *nsrv, void *nscb, va_list ap)
864{
865	struct passwd	**retval = va_arg(ap, struct passwd **);
866
867	char	**hp, *ep;
868	int	  rv;
869
870	_DIAGASSERT(retval != NULL);
871
872	*retval = NULL;
873
874	if (_dns_state.num == -1)			/* exhausted search */
875		return NS_NOTFOUND;
876
877	if (_dns_state.context == NULL) {
878			/* only start if Hesiod not setup */
879		rv = _dns_start(&_dns_state);
880		if (rv != NS_SUCCESS)
881			return rv;
882	}
883
884 next_dns_entry:
885	hp = NULL;
886	rv = NS_NOTFOUND;
887
888							/* find passwd-NNN */
889	snprintf(_dns_passwdbuf, sizeof(_dns_passwdbuf),
890	    "passwd-%u", _dns_state.num);
891	_dns_state.num++;
892
893	hp = hesiod_resolve(_dns_state.context, _dns_passwdbuf, "passwd");
894	if (hp == NULL) {
895		if (errno == ENOENT)
896			_dns_state.num = -1;
897		else
898			rv = NS_UNAVAIL;
899	} else {
900		if ((ep = strchr(hp[0], '\n')) != NULL)
901			*ep = '\0';			/* clear trailing \n */
902							/* validate line */
903		if (_pw_parse(hp[0], &_dns_passwd,
904		    _dns_passwdbuf, sizeof(_dns_passwdbuf), 1))
905			rv = NS_SUCCESS;
906		else {				/* dodgy entry, try again */
907			hesiod_free_list(_dns_state.context, hp);
908			goto next_dns_entry;
909		}
910	}
911
912	if (hp)
913		hesiod_free_list(_dns_state.context, hp);
914	if (rv == NS_SUCCESS)
915		*retval = &_dns_passwd;
916	return rv;
917}
918
919/*ARGSUSED*/
920static int
921_dns_getpwent_r(void *nsrv, void *nscb, va_list ap)
922{
923	int		*retval	= va_arg(ap, int *);
924	struct passwd	*pw	= va_arg(ap, struct passwd *);
925	char		*buffer	= va_arg(ap, char *);
926	size_t		 buflen	= va_arg(ap, size_t);
927	struct passwd  **result	= va_arg(ap, struct passwd **);
928
929	char	**hp, *ep;
930	int	  rv;
931
932	_DIAGASSERT(retval != NULL);
933	_DIAGASSERT(pw != NULL);
934	_DIAGASSERT(buffer != NULL);
935	_DIAGASSERT(result != NULL);
936
937	*retval = 0;
938
939	if (_dns_state.num == -1)			/* exhausted search */
940		return NS_NOTFOUND;
941
942	if (_dns_state.context == NULL) {
943			/* only start if Hesiod not setup */
944		rv = _dns_start(&_dns_state);
945		if (rv != NS_SUCCESS)
946			return rv;
947	}
948
949 next_dns_entry:
950	hp = NULL;
951	rv = NS_NOTFOUND;
952
953							/* find passwd-NNN */
954	snprintf(buffer, buflen, "passwd-%u", _dns_state.num);
955	_dns_state.num++;
956
957	hp = hesiod_resolve(_dns_state.context, buffer, "passwd");
958	if (hp == NULL) {
959		if (errno == ENOENT)
960			_dns_state.num = -1;
961		else
962			rv = NS_UNAVAIL;
963	} else {
964		if ((ep = strchr(hp[0], '\n')) != NULL)
965			*ep = '\0';			/* clear trailing \n */
966							/* validate line */
967		if (_pw_parse(hp[0], pw, buffer, buflen, 1))
968			rv = NS_SUCCESS;
969		else {				/* dodgy entry, try again */
970			hesiod_free_list(_dns_state.context, hp);
971			goto next_dns_entry;
972		}
973	}
974
975	if (hp)
976		hesiod_free_list(_dns_state.context, hp);
977	if (rv == NS_SUCCESS)
978		*result = pw;
979	else
980		*result = NULL;
981	return rv;
982}
983
984static const char *_dns_uid_zones[] = {
985	"uid",
986	"passwd",
987	NULL
988};
989
990/*ARGSUSED*/
991static int
992_dns_getpwuid(void *nsrv, void *nscb, va_list ap)
993{
994	struct passwd	**retval = va_arg(ap, struct passwd **);
995	uid_t		 uid	= va_arg(ap, uid_t);
996
997	int	rv, rerror;
998
999	_DIAGASSERT(retval != NULL);
1000
1001	*retval = NULL;
1002	rv = _dns_start(&_dns_state);
1003	if (rv != NS_SUCCESS)
1004		return rv;
1005	snprintf(_dns_passwdbuf, sizeof(_dns_passwdbuf),
1006	    "%u", (unsigned int)uid);
1007	rv = _dns_pwscan(&rerror, &_dns_passwd,
1008	    _dns_passwdbuf, sizeof(_dns_passwdbuf),
1009	    &_dns_state, _dns_uid_zones);
1010	if (!_dns_state.stayopen)
1011		_dns_end(&_dns_state);
1012	if (rv == NS_SUCCESS && uid == _dns_passwd.pw_uid)
1013		*retval = &_dns_passwd;
1014	return rv;
1015}
1016
1017/*ARGSUSED*/
1018static int
1019_dns_getpwuid_r(void *nsrv, void *nscb, va_list ap)
1020{
1021	int		*retval	= va_arg(ap, int *);
1022	uid_t		 uid	= va_arg(ap, uid_t);
1023	struct passwd	*pw	= va_arg(ap, struct passwd *);
1024	char		*buffer	= va_arg(ap, char *);
1025	size_t		 buflen	= va_arg(ap, size_t);
1026	struct passwd  **result	= va_arg(ap, struct passwd **);
1027
1028	struct dns_state state;
1029	int	rv;
1030
1031	_DIAGASSERT(retval != NULL);
1032	_DIAGASSERT(pw != NULL);
1033	_DIAGASSERT(buffer != NULL);
1034	_DIAGASSERT(result != NULL);
1035
1036	*result = NULL;
1037	memset(&state, 0, sizeof(state));
1038	snprintf(buffer, buflen, "%u", (unsigned int)uid);
1039	rv = _dns_pwscan(retval, pw, buffer, buflen, &state, _dns_uid_zones);
1040	_dns_end(&state);
1041	if (rv != NS_SUCCESS)
1042		return rv;
1043	if (uid == pw->pw_uid) {
1044		*result = pw;
1045		return NS_SUCCESS;
1046	} else
1047		return NS_NOTFOUND;
1048}
1049
1050static const char *_dns_nam_zones[] = {
1051	"passwd",
1052	NULL
1053};
1054
1055/*ARGSUSED*/
1056static int
1057_dns_getpwnam(void *nsrv, void *nscb, va_list ap)
1058{
1059	struct passwd	**retval = va_arg(ap, struct passwd **);
1060	const char	*name	= va_arg(ap, const char *);
1061
1062	int	rv, rerror;
1063
1064	_DIAGASSERT(retval != NULL);
1065
1066	*retval = NULL;
1067	rv = _dns_start(&_dns_state);
1068	if (rv != NS_SUCCESS)
1069		return rv;
1070	snprintf(_dns_passwdbuf, sizeof(_dns_passwdbuf), "%s", name);
1071	rv = _dns_pwscan(&rerror, &_dns_passwd,
1072	    _dns_passwdbuf, sizeof(_dns_passwdbuf),
1073	    &_dns_state, _dns_nam_zones);
1074	if (!_dns_state.stayopen)
1075		_dns_end(&_dns_state);
1076	if (rv == NS_SUCCESS && strcmp(name, _dns_passwd.pw_name) == 0)
1077		*retval = &_dns_passwd;
1078	return rv;
1079}
1080
1081/*ARGSUSED*/
1082static int
1083_dns_getpwnam_r(void *nsrv, void *nscb, va_list ap)
1084{
1085	int		*retval	= va_arg(ap, int *);
1086	const char	*name	= va_arg(ap, const char *);
1087	struct passwd	*pw	= va_arg(ap, struct passwd *);
1088	char		*buffer	= va_arg(ap, char *);
1089	size_t		 buflen	= va_arg(ap, size_t);
1090	struct passwd  **result	= va_arg(ap, struct passwd **);
1091
1092	struct dns_state state;
1093	int	rv;
1094
1095	_DIAGASSERT(retval != NULL);
1096	_DIAGASSERT(pw != NULL);
1097	_DIAGASSERT(buffer != NULL);
1098	_DIAGASSERT(result != NULL);
1099
1100	*result = NULL;
1101	memset(&state, 0, sizeof(state));
1102	snprintf(buffer, buflen, "%s", name);
1103	rv = _dns_pwscan(retval, pw, buffer, buflen, &state, _dns_nam_zones);
1104	_dns_end(&state);
1105	if (rv != NS_SUCCESS)
1106		return rv;
1107	if (strcmp(name, pw->pw_name) == 0) {
1108		*result = pw;
1109		return NS_SUCCESS;
1110	} else
1111		return NS_NOTFOUND;
1112}
1113
1114#endif /* HESIOD */
1115
1116
1117#ifdef YP
1118		/*
1119		 *	nis methods
1120		 */
1121	/* state shared between nis methods */
1122struct nis_state {
1123	int		 stayopen;	/* see getpassent(3) */
1124	char		*domain;	/* NIS domain */
1125	int		 done;		/* non-zero if search exhausted */
1126	char		*current;	/* current first/next match */
1127	int		 currentlen;	/* length of _nis_current */
1128	enum {				/* shadow map type */
1129		NISMAP_UNKNOWN = 0,	/*  unknown ... */
1130		NISMAP_NONE,		/*  none: use "passwd.by*" */
1131		NISMAP_ADJUNCT,		/*  pw_passwd from "passwd.adjunct.*" */
1132		NISMAP_MASTER		/*  all from "master.passwd.by*" */
1133	}		 maptype;
1134};
1135
1136static struct nis_state		_nis_state;
1137					/* storage for non _r functions */
1138static struct passwd		_nis_passwd;
1139static char			_nis_passwdbuf[_GETPW_R_SIZE_MAX];
1140
1141static const char __nis_pw_n_1[] = "master.passwd.byname";
1142static const char __nis_pw_n_2[] = "passwd.byname";
1143static const char __nis_pw_u_1[] = "master.passwd.byuid";
1144static const char __nis_pw_u_2[] = "passwd.byuid";
1145
1146static const char * const __nis_pw_n_map[4] = { __nis_pw_n_2, __nis_pw_n_2, __nis_pw_n_2, __nis_pw_n_1 };
1147static const char * const __nis_pw_u_map[4] = { __nis_pw_u_2, __nis_pw_u_2, __nis_pw_u_2, __nis_pw_u_1 };
1148
1149	/* macros for deciding which NIS maps to use. */
1150#define	PASSWD_BYNAME(x)	((x)->maptype == NISMAP_MASTER ? __nis_pw_n_1 : __nis_pw_n_2)
1151#define	PASSWD_BYUID(x)		((x)->maptype == NISMAP_MASTER ? __nis_pw_u_1 : __nis_pw_u_2)
1152
1153static int
1154_nis_start(struct nis_state *state)
1155{
1156
1157	_DIAGASSERT(state != NULL);
1158
1159	state->done = 0;
1160	if (state->current) {
1161		free(state->current);
1162		state->current = NULL;
1163	}
1164	if (state->domain == NULL) {			/* setup NIS */
1165		switch (yp_get_default_domain(&state->domain)) {
1166		case 0:
1167			break;
1168		case YPERR_RESRC:
1169			return NS_TRYAGAIN;
1170		default:
1171			return NS_UNAVAIL;
1172		}
1173	}
1174
1175				/* determine where to get pw_passwd from */
1176	if (state->maptype == NISMAP_UNKNOWN) {
1177		int	r, order;
1178
1179		state->maptype = NISMAP_NONE;	/* default to no adjunct */
1180		if (geteuid() != 0)		/* non-root can't use adjunct */
1181			return NS_SUCCESS;
1182
1183						/* look for "master.passwd.*" */
1184		r = yp_order(state->domain, "master.passwd.byname", &order);
1185		if (r == 0) {
1186			state->maptype = NISMAP_MASTER;
1187			return NS_SUCCESS;
1188		}
1189
1190			/* master.passwd doesn't exist, try passwd.adjunct */
1191		if (r == YPERR_MAP) {
1192			r = yp_order(state->domain, "passwd.adjunct.byname",
1193			    &order);
1194			if (r == 0)
1195				state->maptype = NISMAP_ADJUNCT;
1196		}
1197	}
1198	return NS_SUCCESS;
1199}
1200
1201static int
1202_nis_end(struct nis_state *state)
1203{
1204
1205	_DIAGASSERT(state != NULL);
1206
1207	if (state->domain)
1208		state->domain = NULL;
1209	state->done = 0;
1210	if (state->current)
1211		free(state->current);
1212	state->current = NULL;
1213	state->maptype = NISMAP_UNKNOWN;
1214	return NS_SUCCESS;
1215}
1216
1217/*
1218 * nis_parse
1219 *	wrapper to _pw_parse that obtains the real password from the
1220 *	"passwd.adjunct.byname" NIS map if the maptype is NISMAP_ADJUNCT.
1221 */
1222static int
1223_nis_parse(const char *entry, struct passwd *pw, char *buf, size_t buflen,
1224	struct nis_state *state)
1225{
1226	size_t	elen;
1227
1228	_DIAGASSERT(entry != NULL);
1229	_DIAGASSERT(pw != NULL);
1230	_DIAGASSERT(buf != NULL);
1231	_DIAGASSERT(state != NULL);
1232
1233	elen = strlen(entry) + 1;
1234	if (elen >= buflen)
1235		return 0;
1236	if (! _pw_parse(entry, pw, buf, buflen,
1237	    !(state->maptype == NISMAP_MASTER)))
1238		return 0;
1239
1240	if ((state->maptype == NISMAP_ADJUNCT) &&
1241	    (strstr(pw->pw_passwd, "##") != NULL)) {
1242		char	*data;
1243		int	datalen;
1244
1245		if (yp_match(state->domain, "passwd.adjunct.byname",
1246		    pw->pw_name, (int)strlen(pw->pw_name),
1247		    &data, &datalen) == 0) {
1248			char	*bp, *ep;
1249						/* skip name to get password */
1250			ep = data;
1251			if (strsep(&ep, ":") != NULL &&
1252			    (bp = strsep(&ep, ":")) != NULL) {
1253					/* store new pw_passwd after entry */
1254				if (strlcpy(buf + elen, bp, buflen - elen) >=
1255				    buflen - elen) {
1256					free(data);
1257					return 0;
1258				}
1259				pw->pw_passwd = &buf[elen];
1260			}
1261			free(data);
1262		}
1263	}
1264
1265	return 1;
1266}
1267
1268
1269/*
1270 * _nis_pwscan
1271 *	Look for the yp key provided in buffer from map,
1272 *	and decode into pw/buffer/buflen.
1273 */
1274static int
1275_nis_pwscan(int *retval, struct passwd *pw, char *buffer, size_t buflen,
1276	struct nis_state *state, const char * const *map_arr, size_t nmaps)
1277{
1278	char	*data;
1279	int	nisr, rv, datalen;
1280
1281	_DIAGASSERT(retval != NULL);
1282	_DIAGASSERT(pw != NULL);
1283	_DIAGASSERT(buffer != NULL);
1284	_DIAGASSERT(state != NULL);
1285	_DIAGASSERT(map_arr != NULL);
1286
1287	*retval = 0;
1288
1289	if (state->domain == NULL) {	/* only start if NIS not setup */
1290		rv = _nis_start(state);
1291		if (rv != NS_SUCCESS)
1292			return rv;
1293	}
1294
1295	data = NULL;
1296	rv = NS_NOTFOUND;
1297	_DIAGASSERT(state->maptype != NISMAP_UNKNOWN &&
1298		    (unsigned)state->maptype < nmaps);
1299
1300							/* search map */
1301	nisr = yp_match(state->domain, map_arr[state->maptype], buffer, (int)strlen(buffer),
1302	    &data, &datalen);
1303	switch (nisr) {
1304	case 0:
1305		data[datalen] = '\0';			/* clear trailing \n */
1306		if (_nis_parse(data, pw, buffer, buflen, state))
1307			rv = NS_SUCCESS;		/* validate line */
1308		else
1309			rv = NS_UNAVAIL;
1310		break;
1311	case YPERR_KEY:
1312		break;
1313	default:
1314		rv = NS_UNAVAIL;
1315		break;
1316	}
1317
1318	if (rv != NS_SUCCESS && rv != NS_NOTFOUND)
1319		*retval = errno;
1320	if (data)
1321		free(data);
1322	return rv;
1323}
1324
1325/*ARGSUSED*/
1326static int
1327_nis_setpwent(void *nsrv, void *nscb, va_list ap)
1328{
1329
1330	_nis_state.stayopen = 0;
1331	return _nis_start(&_nis_state);
1332}
1333
1334/*ARGSUSED*/
1335static int
1336_nis_setpassent(void *nsrv, void *nscb, va_list ap)
1337{
1338	int	*retval		= va_arg(ap, int *);
1339	int	 stayopen	= va_arg(ap, int);
1340
1341	int	rv;
1342
1343	_nis_state.stayopen = stayopen;
1344	rv = _nis_start(&_nis_state);
1345	*retval = (rv == NS_SUCCESS);
1346	return rv;
1347}
1348
1349/*ARGSUSED*/
1350static int
1351_nis_endpwent(void *nsrv, void *nscb, va_list ap)
1352{
1353
1354	return _nis_end(&_nis_state);
1355}
1356
1357
1358/*ARGSUSED*/
1359static int
1360_nis_getpwent(void *nsrv, void *nscb, va_list ap)
1361{
1362	struct passwd	**retval = va_arg(ap, struct passwd **);
1363
1364	char	*key, *data;
1365	int	keylen, datalen, rv, nisr;
1366
1367	_DIAGASSERT(retval != NULL);
1368
1369	*retval = NULL;
1370
1371	if (_nis_state.done)				/* exhausted search */
1372		return NS_NOTFOUND;
1373	if (_nis_state.domain == NULL) {
1374					/* only start if NIS not setup */
1375		rv = _nis_start(&_nis_state);
1376		if (rv != NS_SUCCESS)
1377			return rv;
1378	}
1379
1380 next_nis_entry:
1381	key = NULL;
1382	data = NULL;
1383	rv = NS_NOTFOUND;
1384
1385	if (_nis_state.current) {			/* already searching */
1386		nisr = yp_next(_nis_state.domain, PASSWD_BYNAME(&_nis_state),
1387		    _nis_state.current, _nis_state.currentlen,
1388		    &key, &keylen, &data, &datalen);
1389		free(_nis_state.current);
1390		_nis_state.current = NULL;
1391		switch (nisr) {
1392		case 0:
1393			_nis_state.current = key;
1394			_nis_state.currentlen = keylen;
1395			key = NULL;
1396			break;
1397		case YPERR_NOMORE:
1398			_nis_state.done = 1;
1399			goto nisent_out;
1400		default:
1401			rv = NS_UNAVAIL;
1402			goto nisent_out;
1403		}
1404	} else {					/* new search */
1405		if (yp_first(_nis_state.domain, PASSWD_BYNAME(&_nis_state),
1406		    &_nis_state.current, &_nis_state.currentlen,
1407		    &data, &datalen)) {
1408			rv = NS_UNAVAIL;
1409			goto nisent_out;
1410		}
1411	}
1412
1413	data[datalen] = '\0';				/* clear trailing \n */
1414							/* validate line */
1415	if (_nis_parse(data, &_nis_passwd,
1416	    _nis_passwdbuf, sizeof(_nis_passwdbuf), &_nis_state))
1417		rv = NS_SUCCESS;
1418	else {					/* dodgy entry, try again */
1419		free(data);
1420		goto next_nis_entry;
1421	}
1422
1423 nisent_out:
1424	if (key)
1425		free(key);
1426	if (data)
1427		free(data);
1428	if (rv == NS_SUCCESS)
1429		*retval = &_nis_passwd;
1430	return rv;
1431}
1432
1433/*ARGSUSED*/
1434static int
1435_nis_getpwent_r(void *nsrv, void *nscb, va_list ap)
1436{
1437	int		*retval	= va_arg(ap, int *);
1438	struct passwd	*pw	= va_arg(ap, struct passwd *);
1439	char		*buffer	= va_arg(ap, char *);
1440	size_t		 buflen	= va_arg(ap, size_t);
1441	struct passwd  **result	= va_arg(ap, struct passwd **);
1442
1443	char	*key, *data;
1444	int	keylen, datalen, rv, nisr;
1445
1446	_DIAGASSERT(retval != NULL);
1447	_DIAGASSERT(pw != NULL);
1448	_DIAGASSERT(buffer != NULL);
1449	_DIAGASSERT(result != NULL);
1450
1451	*retval = 0;
1452
1453	if (_nis_state.done)				/* exhausted search */
1454		return NS_NOTFOUND;
1455	if (_nis_state.domain == NULL) {
1456					/* only start if NIS not setup */
1457		rv = _nis_start(&_nis_state);
1458		if (rv != NS_SUCCESS)
1459			return rv;
1460	}
1461
1462 next_nis_entry:
1463	key = NULL;
1464	data = NULL;
1465	rv = NS_NOTFOUND;
1466
1467	if (_nis_state.current) {			/* already searching */
1468		nisr = yp_next(_nis_state.domain, PASSWD_BYNAME(&_nis_state),
1469		    _nis_state.current, _nis_state.currentlen,
1470		    &key, &keylen, &data, &datalen);
1471		free(_nis_state.current);
1472		_nis_state.current = NULL;
1473		switch (nisr) {
1474		case 0:
1475			_nis_state.current = key;
1476			_nis_state.currentlen = keylen;
1477			key = NULL;
1478			break;
1479		case YPERR_NOMORE:
1480			_nis_state.done = 1;
1481			goto nisent_out;
1482		default:
1483			rv = NS_UNAVAIL;
1484			goto nisent_out;
1485		}
1486	} else {					/* new search */
1487		if (yp_first(_nis_state.domain, PASSWD_BYNAME(&_nis_state),
1488		    &_nis_state.current, &_nis_state.currentlen,
1489		    &data, &datalen)) {
1490			rv = NS_UNAVAIL;
1491			goto nisent_out;
1492		}
1493	}
1494
1495	data[datalen] = '\0';				/* clear trailing \n */
1496							/* validate line */
1497	if (_nis_parse(data, pw, buffer, buflen, &_nis_state))
1498		rv = NS_SUCCESS;
1499	else {					/* dodgy entry, try again */
1500		if (key)
1501			free(key);
1502		free(data);
1503		goto next_nis_entry;
1504	}
1505
1506 nisent_out:
1507	if (key)
1508		free(key);
1509	if (data)
1510		free(data);
1511	if (rv == NS_SUCCESS)
1512		*result = pw;
1513	else
1514		*result = NULL;
1515	return rv;
1516}
1517
1518/*ARGSUSED*/
1519static int
1520_nis_getpwuid(void *nsrv, void *nscb, va_list ap)
1521{
1522	struct passwd	**retval = va_arg(ap, struct passwd **);
1523	uid_t		 uid	= va_arg(ap, uid_t);
1524
1525	int	rv, rerror;
1526
1527	_DIAGASSERT(retval != NULL);
1528
1529	*retval = NULL;
1530	rv = _nis_start(&_nis_state);
1531	if (rv != NS_SUCCESS)
1532		return rv;
1533	snprintf(_nis_passwdbuf, sizeof(_nis_passwdbuf), "%u", (unsigned int)uid);
1534	rv = _nis_pwscan(&rerror, &_nis_passwd,
1535	    _nis_passwdbuf, sizeof(_nis_passwdbuf),
1536	    &_nis_state, __nis_pw_u_map, __arraycount(__nis_pw_u_map));
1537	if (!_nis_state.stayopen)
1538		_nis_end(&_nis_state);
1539	if (rv == NS_SUCCESS && uid == _nis_passwd.pw_uid)
1540		*retval = &_nis_passwd;
1541	return rv;
1542}
1543
1544/*ARGSUSED*/
1545static int
1546_nis_getpwuid_r(void *nsrv, void *nscb, va_list ap)
1547{
1548	int		*retval	= va_arg(ap, int *);
1549	uid_t		 uid	= va_arg(ap, uid_t);
1550	struct passwd	*pw	= va_arg(ap, struct passwd *);
1551	char		*buffer	= va_arg(ap, char *);
1552	size_t		 buflen	= va_arg(ap, size_t);
1553	struct passwd  **result	= va_arg(ap, struct passwd **);
1554
1555	struct nis_state state;
1556	int	rv;
1557
1558	_DIAGASSERT(retval != NULL);
1559	_DIAGASSERT(pw != NULL);
1560	_DIAGASSERT(buffer != NULL);
1561	_DIAGASSERT(result != NULL);
1562
1563	*result = NULL;
1564	snprintf(buffer, buflen, "%u", (unsigned int)uid);
1565/* remark: we run under a global mutex inside of this module ... */
1566	if (_nis_state.stayopen)
1567	  { /* use global state only if stayopen is set - otherwise we would blow up getpwent_r() ... */
1568	    rv = _nis_pwscan(retval, pw, buffer, buflen,
1569		&_nis_state, __nis_pw_u_map, __arraycount(__nis_pw_u_map));
1570	  }
1571	else
1572	  { /* keep old semantic if no stayopen set - no need to call _nis_start() here - _nis_pwscan() will do it for us ... */
1573	    /* use same way as in getgrent.c ... */
1574	    memset(&state, 0, sizeof(state));
1575	    rv = _nis_pwscan(retval, pw, buffer, buflen,
1576		&state, __nis_pw_u_map, __arraycount(__nis_pw_u_map));
1577	    _nis_end(&state);
1578	  }
1579	if (rv != NS_SUCCESS)
1580		return rv;
1581	if (uid == pw->pw_uid) {
1582		*result = pw;
1583		return NS_SUCCESS;
1584	} else
1585		return NS_NOTFOUND;
1586}
1587
1588/*ARGSUSED*/
1589static int
1590_nis_getpwnam(void *nsrv, void *nscb, va_list ap)
1591{
1592	struct passwd	**retval = va_arg(ap, struct passwd **);
1593	const char	*name	= va_arg(ap, const char *);
1594
1595	int	rv, rerror;
1596
1597	_DIAGASSERT(retval != NULL);
1598
1599	*retval = NULL;
1600	rv = _nis_start(&_nis_state);
1601	if (rv != NS_SUCCESS)
1602		return rv;
1603	snprintf(_nis_passwdbuf, sizeof(_nis_passwdbuf), "%s", name);
1604	rv = _nis_pwscan(&rerror, &_nis_passwd,
1605	    _nis_passwdbuf, sizeof(_nis_passwdbuf),
1606	    &_nis_state, __nis_pw_n_map, __arraycount(__nis_pw_n_map));
1607	if (!_nis_state.stayopen)
1608		_nis_end(&_nis_state);
1609	if (rv == NS_SUCCESS && strcmp(name, _nis_passwd.pw_name) == 0)
1610		*retval = &_nis_passwd;
1611	return rv;
1612}
1613
1614/*ARGSUSED*/
1615static int
1616_nis_getpwnam_r(void *nsrv, void *nscb, va_list ap)
1617{
1618	int		*retval	= va_arg(ap, int *);
1619	const char	*name	= va_arg(ap, const char *);
1620	struct passwd	*pw	= va_arg(ap, struct passwd *);
1621	char		*buffer	= va_arg(ap, char *);
1622	size_t		 buflen	= va_arg(ap, size_t);
1623	struct passwd  **result	= va_arg(ap, struct passwd **);
1624
1625	struct nis_state state;
1626	int	rv;
1627
1628	_DIAGASSERT(retval != NULL);
1629	_DIAGASSERT(pw != NULL);
1630	_DIAGASSERT(buffer != NULL);
1631	_DIAGASSERT(result != NULL);
1632
1633	*result = NULL;
1634	snprintf(buffer, buflen, "%s", name);
1635/* remark: we run under a global mutex inside of this module ... */
1636	if (_nis_state.stayopen)
1637	  { /* use global state only if stayopen is set - otherwise we would blow up getpwent_r() ... */
1638	    rv = _nis_pwscan(retval, pw, buffer, buflen,
1639		&_nis_state, __nis_pw_n_map, __arraycount(__nis_pw_n_map));
1640	  }
1641	else
1642	  { /* keep old semantic if no stayopen set - no need to call _nis_start() here - _nis_pwscan() will do it for us ... */
1643	    /* use same way as in getgrent.c ... */
1644	    memset(&state, 0, sizeof(state));
1645	    rv = _nis_pwscan(retval, pw, buffer, buflen,
1646		&state, __nis_pw_n_map, __arraycount(__nis_pw_n_map));
1647	    _nis_end(&state);
1648	  }
1649	if (rv != NS_SUCCESS)
1650		return rv;
1651	if (strcmp(name, pw->pw_name) == 0) {
1652		*result = pw;
1653		return NS_SUCCESS;
1654	} else
1655		return NS_NOTFOUND;
1656}
1657
1658#endif /* YP */
1659
1660
1661#ifdef _PASSWD_COMPAT
1662		/*
1663		 *	compat methods
1664		 */
1665
1666	/* state shared between compat methods */
1667
1668struct compat_state {
1669	int		 stayopen;	/* see getpassent(3) */
1670	DB		*db;		/* passwd DB */
1671	int		 keynum;	/* key counter, -1 if no more */
1672	enum {				/* current compat mode */
1673		COMPAT_NOTOKEN = 0,	/*  no compat token present */
1674		COMPAT_NONE,		/*  parsing normal pwd.db line */
1675		COMPAT_FULL,		/*  parsing `+' entries */
1676		COMPAT_USER,		/*  parsing `+name' entries */
1677		COMPAT_NETGROUP		/*  parsing `+@netgroup' entries */
1678	}		 mode;
1679	char		*user;		/* COMPAT_USER "+name" */
1680	DB		*exclude;	/* compat exclude DB */
1681	struct passwd	 proto;		/* proto passwd entry */
1682	char		 protobuf[_GETPW_R_SIZE_MAX];
1683					/* buffer for proto ptrs */
1684	int		 protoflags;	/* proto passwd flags */
1685	int		 version;
1686};
1687
1688static struct compat_state	_compat_state;
1689					/* storage for non _r functions */
1690static struct passwd		_compat_passwd;
1691static char			_compat_passwdbuf[_GETPW_R_SIZE_MAX];
1692
1693static int
1694_compat_start(struct compat_state *state)
1695{
1696	int	rv;
1697
1698	_DIAGASSERT(state != NULL);
1699
1700	state->keynum = 0;
1701	if (state->db == NULL) {		/* not open yet */
1702		DBT	key, data;
1703		DBT	pkey, pdata;
1704		char	bf[MAXLOGNAME];
1705
1706		rv = _pw_opendb(&state->db, &state->version);
1707		if (rv != NS_SUCCESS)
1708			return rv;
1709
1710		state->mode = COMPAT_NOTOKEN;
1711
1712		/*
1713		 *	Determine if the "compat" token is present in pwd.db;
1714		 *	either "__YP!" or PW_KEYBYNAME+"+".
1715		 *	Only works if pwd_mkdb installs the token.
1716		 */
1717		key.data = (u_char *)__UNCONST(__yp_token);
1718		key.size = strlen(__yp_token);
1719
1720		bf[0] = _PW_KEYBYNAME;	 /* Pre-token database support. */
1721		bf[1] = '+';
1722		pkey.data = (u_char *)bf;
1723		pkey.size = 2;
1724
1725		if ((state->db->get)(state->db, &key, &data, 0) == 0
1726		    || (state->db->get)(state->db, &pkey, &pdata, 0) == 0)
1727			state->mode = COMPAT_NONE;
1728	}
1729	return NS_SUCCESS;
1730}
1731
1732static int
1733_compat_end(struct compat_state *state)
1734{
1735
1736	_DIAGASSERT(state != NULL);
1737
1738	state->keynum = 0;
1739	if (state->db) {
1740		(void)(state->db->close)(state->db);
1741		state->db = NULL;
1742	}
1743	state->mode = COMPAT_NOTOKEN;
1744	if (state->user)
1745		free(state->user);
1746	state->user = NULL;
1747	if (state->exclude != NULL)
1748		(void)(state->exclude->close)(state->exclude);
1749	state->exclude = NULL;
1750	state->proto.pw_name = NULL;
1751	state->protoflags = 0;
1752	return NS_SUCCESS;
1753}
1754
1755/*
1756 * _compat_add_exclude
1757 *	add the name to the exclude list in state->exclude.
1758 */
1759static int
1760_compat_add_exclude(struct compat_state *state, const char *name)
1761{
1762	DBT	key, data;
1763
1764	_DIAGASSERT(state != NULL);
1765	_DIAGASSERT(name != NULL);
1766
1767				/* initialize the exclusion table if needed */
1768	if (state->exclude == NULL) {
1769		state->exclude = dbopen(NULL, O_RDWR, 600, DB_HASH, NULL);
1770		if (state->exclude == NULL)
1771			return 0;
1772	}
1773
1774	key.size = strlen(name);			/* set up the key */
1775	key.data = (u_char *)__UNCONST(name);
1776
1777	data.data = NULL;				/* data is nothing */
1778	data.size = 0;
1779
1780							/* store it */
1781	if ((state->exclude->put)(state->exclude, &key, &data, 0) == -1)
1782		return 0;
1783
1784	return 1;
1785}
1786
1787/*
1788 * _compat_is_excluded
1789 *	test if a name is on the compat mode exclude list
1790 */
1791static int
1792_compat_is_excluded(struct compat_state *state, const char *name)
1793{
1794	DBT	key, data;
1795
1796	_DIAGASSERT(state != NULL);
1797	_DIAGASSERT(name != NULL);
1798
1799	if (state->exclude == NULL)
1800		return 0;	/* nothing excluded */
1801
1802	key.size = strlen(name);			/* set up the key */
1803	key.data = (u_char *)__UNCONST(name);
1804
1805	if ((state->exclude->get)(state->exclude, &key, &data, 0) == 0)
1806		return 1;				/* is excluded */
1807
1808	return 0;
1809}
1810
1811
1812/*
1813 * _passwdcompat_bad
1814 *	log an error if "files" or "compat" is specified in
1815 *	passwd_compat database
1816 */
1817/*ARGSUSED*/
1818static int
1819_passwdcompat_bad(void *nsrv, void *nscb, va_list ap)
1820{
1821	static int warned;
1822
1823	_DIAGASSERT(nsrv != NULL);
1824	_DIAGASSERT(nscb != NULL);
1825
1826	if (!warned) {
1827		syslog(LOG_ERR,
1828			"nsswitch.conf passwd_compat database can't use '%s'",
1829			(char *)nscb);
1830	}
1831	warned = 1;
1832	return NS_UNAVAIL;
1833}
1834
1835/*
1836 * _passwdcompat_setpassent
1837 *	Call setpassent for all passwd_compat sources.
1838 */
1839static int
1840_passwdcompat_setpassent(int stayopen)
1841{
1842	static const ns_dtab dtab[] = {
1843		NS_FILES_CB(_passwdcompat_bad, "files")
1844		NS_DNS_CB(_dns_setpassent, NULL)
1845		NS_NIS_CB(_nis_setpassent, NULL)
1846		NS_COMPAT_CB(_passwdcompat_bad, "compat")
1847		NS_NULL_CB
1848	};
1849
1850	int	rv, result;
1851
1852	rv = nsdispatch(NULL, dtab, NSDB_PASSWD_COMPAT, "setpassent",
1853	    __nsdefaultnis_forceall, &result, stayopen);
1854	return rv;
1855}
1856
1857/*
1858 * _passwdcompat_endpwent
1859 *	Call endpwent for all passwd_compat sources.
1860 */
1861static int
1862_passwdcompat_endpwent(void)
1863{
1864	static const ns_dtab dtab[] = {
1865		NS_FILES_CB(_passwdcompat_bad, "files")
1866		NS_DNS_CB(_dns_endpwent, NULL)
1867		NS_NIS_CB(_nis_endpwent, NULL)
1868		NS_COMPAT_CB(_passwdcompat_bad, "compat")
1869		NS_NULL_CB
1870	};
1871
1872	return nsdispatch(NULL, dtab, NSDB_PASSWD_COMPAT, "endpwent",
1873	    __nsdefaultnis_forceall);
1874}
1875
1876/*
1877 * _passwdcompat_pwscan
1878 *	When a name lookup in compat mode is required (e.g., `+name', or a
1879 *	name in `+@netgroup'), look it up in the 'passwd_compat' nsswitch
1880 *	database.
1881 *	Fail if passwd_compat contains files or compat.
1882 */
1883static int
1884_passwdcompat_pwscan(struct passwd *pw, char *buffer, size_t buflen,
1885	int search, const char *name, uid_t uid)
1886{
1887	static const ns_dtab compatentdtab[] = {
1888		NS_FILES_CB(_passwdcompat_bad, "files")
1889		NS_DNS_CB(_dns_getpwent_r, NULL)
1890		NS_NIS_CB(_nis_getpwent_r, NULL)
1891		NS_COMPAT_CB(_passwdcompat_bad, "compat")
1892		NS_NULL_CB
1893	};
1894	static const ns_dtab compatuiddtab[] = {
1895		NS_FILES_CB(_passwdcompat_bad, "files")
1896		NS_DNS_CB(_dns_getpwuid_r, NULL)
1897		NS_NIS_CB(_nis_getpwuid_r, NULL)
1898		NS_COMPAT_CB(_passwdcompat_bad, "compat")
1899		NS_NULL_CB
1900	};
1901	static const ns_dtab compatnamdtab[] = {
1902		NS_FILES_CB(_passwdcompat_bad, "files")
1903		NS_DNS_CB(_dns_getpwnam_r, NULL)
1904		NS_NIS_CB(_nis_getpwnam_r, NULL)
1905		NS_COMPAT_CB(_passwdcompat_bad, "compat")
1906		NS_NULL_CB
1907	};
1908
1909	int		rv, crv;
1910	struct passwd	*cpw;
1911
1912	switch (search) {
1913	case _PW_KEYBYNUM:
1914		rv = nsdispatch(NULL, compatentdtab,
1915		    NSDB_PASSWD_COMPAT, "getpwent_r", __nsdefaultnis,
1916		    &crv, pw, buffer, buflen, &cpw);
1917		break;
1918	case _PW_KEYBYNAME:
1919		_DIAGASSERT(name != NULL);
1920		rv = nsdispatch(NULL, compatnamdtab,
1921		    NSDB_PASSWD_COMPAT, "getpwnam_r", __nsdefaultnis,
1922		    &crv, name, pw, buffer, buflen, &cpw);
1923		break;
1924	case _PW_KEYBYUID:
1925		rv = nsdispatch(NULL, compatuiddtab,
1926		    NSDB_PASSWD_COMPAT, "getpwuid_r", __nsdefaultnis,
1927		    &crv, uid, pw, buffer, buflen, &cpw);
1928		break;
1929	default:
1930		abort();
1931		/*NOTREACHED*/
1932	}
1933	return rv;
1934}
1935
1936/*
1937 * _compat_pwscan
1938 *	Search state->db for the next desired entry.
1939 *	If search is _PW_KEYBYNUM, look for state->keynum.
1940 *	If search is _PW_KEYBYNAME, look for name.
1941 *	If search is _PW_KEYBYUID, look for uid.
1942 *	Sets *retval to the errno if the result is not NS_SUCCESS
1943 *	or NS_NOTFOUND.
1944 */
1945static int
1946_compat_pwscan(int *retval, struct passwd *pw, char *buffer, size_t buflen,
1947	struct compat_state *state, int search, const char *name, uid_t uid)
1948{
1949	DBT		 key;
1950	int		 rv, r, pwflags;
1951	const char	*user, *host, *dom;
1952	const void	*from;
1953	size_t		 fromlen;
1954
1955	_DIAGASSERT(retval != NULL);
1956	_DIAGASSERT(pw != NULL);
1957	_DIAGASSERT(buffer != NULL);
1958	_DIAGASSERT(state != NULL);
1959	/* name may be NULL */
1960
1961	*retval = 0;
1962
1963	if (state->db == NULL) {
1964		rv = _compat_start(state);
1965		if (rv != NS_SUCCESS)
1966			return rv;
1967	}
1968	if (buflen <= 1) {			/* buffer too small */
1969		*retval = ERANGE;
1970		return NS_UNAVAIL;
1971	}
1972
1973	for (;;) {				/* loop over pwd.db */
1974		rv = NS_NOTFOUND;
1975		if (state->mode != COMPAT_NOTOKEN &&
1976		    state->mode != COMPAT_NONE) {
1977						/* doing a compat lookup */
1978			struct passwd	cpw;
1979			char		cbuf[_GETPW_R_SIZE_MAX];
1980
1981			switch (state->mode) {
1982
1983			case COMPAT_FULL:
1984					/* get next user or lookup by key */
1985				rv = _passwdcompat_pwscan(&cpw,
1986				    cbuf, sizeof(cbuf), search, name, uid);
1987				if (rv != NS_SUCCESS)
1988					state->mode = COMPAT_NONE;
1989				break;
1990
1991			case COMPAT_NETGROUP:
1992/* XXXREENTRANT: getnetgrent is not thread safe */
1993					/* get next user from netgroup */
1994				r = getnetgrent(&host, &user, &dom);
1995				if (r == 0) {	/* end of group */
1996					endnetgrent();
1997					state->mode = COMPAT_NONE;
1998					break;
1999				}
2000				if (!user || !*user)
2001					break;
2002				rv = _passwdcompat_pwscan(&cpw,
2003				    cbuf, sizeof(cbuf),
2004				    _PW_KEYBYNAME, user, 0);
2005				break;
2006
2007			case COMPAT_USER:
2008					/* get specific user */
2009				if (state->user == NULL) {
2010					state->mode = COMPAT_NONE;
2011					break;
2012				}
2013				rv = _passwdcompat_pwscan(&cpw,
2014				    cbuf, sizeof(cbuf),
2015				    _PW_KEYBYNAME, state->user, 0);
2016				free(state->user);
2017				state->user = NULL;
2018				state->mode = COMPAT_NONE;
2019				break;
2020
2021			case COMPAT_NOTOKEN:
2022			case COMPAT_NONE:
2023				abort();
2024
2025			}
2026			if (rv != NS_SUCCESS)	/* if not matched, next loop */
2027				continue;
2028
2029				/* copy cpw to pw, applying prototype */
2030			if (! _pw_copy(&cpw, pw, buffer, buflen,
2031			    &state->proto, state->protoflags)) {
2032				rv = NS_UNAVAIL;
2033				break;
2034			}
2035
2036			if (_compat_is_excluded(state, pw->pw_name))
2037				continue;	/* excluded; next loop */
2038
2039			if ((search == _PW_KEYBYNAME
2040					&& strcmp(pw->pw_name, name) != 0)
2041			    || (search == _PW_KEYBYUID && pw->pw_uid != uid)) {
2042				continue;	/* not specific; next loop */
2043			}
2044
2045			break;			/* exit loop if found */
2046		} else {			/* not a compat line */
2047			state->proto.pw_name = NULL;
2048						/* clear prototype */
2049		}
2050
2051		if (state->mode == COMPAT_NOTOKEN) {
2052				/* no compat token; do direct lookup */
2053			switch (search) {
2054			case _PW_KEYBYNUM:
2055				if (state->keynum == -1)  /* no more records */
2056					return NS_NOTFOUND;
2057				state->keynum++;
2058				from = &state->keynum;
2059				fromlen = sizeof(state->keynum);
2060				break;
2061			case _PW_KEYBYNAME:
2062				from = name;
2063				fromlen = strlen(name);
2064				break;
2065			case _PW_KEYBYUID:
2066				from = &uid;
2067				fromlen = sizeof(uid);
2068				break;
2069			default:
2070				abort();
2071			}
2072			buffer[0] = search;
2073		} else {
2074				/* compat token; do line by line */
2075			if (state->keynum == -1)  /* no more records */
2076				return NS_NOTFOUND;
2077			state->keynum++;
2078			from = &state->keynum;
2079			fromlen = sizeof(state->keynum);
2080			buffer[0] = _PW_KEYBYNUM;
2081		}
2082
2083		if (buflen <= fromlen) {		/* buffer too small */
2084			*retval = ERANGE;
2085			return NS_UNAVAIL;
2086		}
2087		memmove(buffer + 1, from, fromlen);	/* setup key */
2088		key.size = fromlen + 1;
2089		key.data = (u_char *)buffer;
2090
2091		rv = _pw_getkey(state->db, &key, pw, buffer, buflen, &pwflags,
2092		    state->version);
2093		if (rv != NS_SUCCESS)		/* stop on error */
2094			break;
2095
2096		if (state->mode == COMPAT_NOTOKEN)
2097			break;			/* stop if no compat token */
2098
2099		if (pw->pw_name[0] == '+') {
2100						/* compat inclusion */
2101			switch(pw->pw_name[1]) {
2102			case '\0':		/* `+' */
2103				state->mode = COMPAT_FULL;
2104						/* reset passwd_compat search */
2105/* XXXREENTRANT: setpassent is not thread safe ? */
2106				(void) _passwdcompat_setpassent(_compat_state.stayopen);
2107				break;
2108			case '@':		/* `+@netgroup' */
2109				state->mode = COMPAT_NETGROUP;
2110						/* reset netgroup search */
2111/* XXXREENTRANT: setnetgrent is not thread safe */
2112				setnetgrent(pw->pw_name + 2);
2113				break;
2114			default:		/* `+name' */
2115				state->mode = COMPAT_USER;
2116				if (state->user)
2117					free(state->user);
2118				state->user = strdup(pw->pw_name + 1);
2119				break;
2120			}
2121						/* save the prototype */
2122			state->protoflags = pwflags;
2123			if (! _pw_copy(pw, &state->proto, state->protobuf,
2124			    sizeof(state->protobuf), NULL, 0)) {
2125				rv = NS_UNAVAIL;
2126				break;
2127			}
2128			continue;		/* loop again after inclusion */
2129		} else if (pw->pw_name[0] == '-') {
2130						/* compat exclusion */
2131			rv = NS_SUCCESS;
2132			switch(pw->pw_name[1]) {
2133			case '\0':		/* `-' */
2134				break;
2135			case '@':		/* `-@netgroup' */
2136/* XXXREENTRANT: {set,get,end}netgrent is not thread safe */
2137				setnetgrent(pw->pw_name + 2);
2138				while (getnetgrent(&host, &user, &dom)) {
2139					if (!user || !*user)
2140						continue;
2141					if (! _compat_add_exclude(state,user)) {
2142						rv = NS_UNAVAIL;
2143						break;
2144					}
2145				}
2146				endnetgrent();
2147				break;
2148			default:		/* `-name' */
2149				if (! _compat_add_exclude(state,
2150				    pw->pw_name + 1)) {
2151					rv = NS_UNAVAIL;
2152				}
2153				break;
2154			}
2155			if (rv != NS_SUCCESS)	/* exclusion failure */
2156				break;
2157			continue;		/* loop again after exclusion */
2158		}
2159		if (search == _PW_KEYBYNUM ||
2160		    (search == _PW_KEYBYUID && pw->pw_uid == uid) ||
2161		    (search == _PW_KEYBYNAME && strcmp(pw->pw_name, name) == 0))
2162			break;			/* token mode match found */
2163	}
2164
2165	if (rv == NS_NOTFOUND &&
2166	    (search == _PW_KEYBYNUM || state->mode != COMPAT_NOTOKEN))
2167		state->keynum = -1;		/* flag `no more records' */
2168
2169	if (rv == NS_SUCCESS) {
2170		if ((search == _PW_KEYBYNAME && strcmp(pw->pw_name, name) != 0)
2171		    || (search == _PW_KEYBYUID && pw->pw_uid != uid))
2172			rv = NS_NOTFOUND;
2173	}
2174
2175	if (rv != NS_SUCCESS && rv != NS_NOTFOUND)
2176		*retval = errno;
2177	return rv;
2178}
2179
2180/*ARGSUSED*/
2181static int
2182_compat_setpwent(void *nsrv, void *nscb, va_list ap)
2183{
2184
2185					/* force passwd_compat setpwent() */
2186	(void) _passwdcompat_setpassent(0);
2187
2188					/* reset state, keep db open */
2189	_compat_state.stayopen = 0;
2190	return _compat_start(&_compat_state);
2191}
2192
2193/*ARGSUSED*/
2194static int
2195_compat_setpassent(void *nsrv, void *nscb, va_list ap)
2196{
2197	int	*retval		= va_arg(ap, int *);
2198	int	 stayopen	= va_arg(ap, int);
2199
2200	int	rv;
2201
2202					/* force passwd_compat setpassent() */
2203	(void) _passwdcompat_setpassent(stayopen);
2204
2205	_compat_state.stayopen = stayopen;
2206	rv = _compat_start(&_compat_state);
2207	*retval = (rv == NS_SUCCESS);
2208	return rv;
2209}
2210
2211/*ARGSUSED*/
2212static int
2213_compat_endpwent(void *nsrv, void *nscb, va_list ap)
2214{
2215
2216					/* force passwd_compat endpwent() */
2217	(void) _passwdcompat_endpwent();
2218
2219					/* reset state, close db */
2220	_compat_state.stayopen = 0;
2221	return _compat_end(&_compat_state);
2222}
2223
2224
2225/*ARGSUSED*/
2226static int
2227_compat_getpwent(void *nsrv, void *nscb, va_list ap)
2228{
2229	struct passwd	**retval = va_arg(ap, struct passwd **);
2230
2231	int	rv, rerror;
2232
2233	_DIAGASSERT(retval != NULL);
2234
2235	*retval = NULL;
2236	rv = _compat_pwscan(&rerror, &_compat_passwd,
2237	    _compat_passwdbuf, sizeof(_compat_passwdbuf),
2238	    &_compat_state, _PW_KEYBYNUM, NULL, 0);
2239	if (rv == NS_SUCCESS)
2240		*retval = &_compat_passwd;
2241	return rv;
2242}
2243
2244/*ARGSUSED*/
2245static int
2246_compat_getpwent_r(void *nsrv, void *nscb, va_list ap)
2247{
2248	int		*retval	= va_arg(ap, int *);
2249	struct passwd	*pw	= va_arg(ap, struct passwd *);
2250	char		*buffer	= va_arg(ap, char *);
2251	size_t		 buflen	= va_arg(ap, size_t);
2252	struct passwd  **result	= va_arg(ap, struct passwd **);
2253
2254	int		rv;
2255
2256	_DIAGASSERT(retval != NULL);
2257	_DIAGASSERT(pw != NULL);
2258	_DIAGASSERT(buffer != NULL);
2259	_DIAGASSERT(result != NULL);
2260
2261	rv = _compat_pwscan(retval, pw, buffer, buflen, &_compat_state,
2262	    _PW_KEYBYNUM, NULL, 0);
2263	if (rv == NS_SUCCESS)
2264		*result = pw;
2265	else
2266		*result = NULL;
2267	return rv;
2268}
2269
2270
2271/*ARGSUSED*/
2272static int
2273_compat_getpwnam(void *nsrv, void *nscb, va_list ap)
2274{
2275	struct passwd	**retval = va_arg(ap, struct passwd **);
2276	const char	*name	= va_arg(ap, const char *);
2277
2278	int	rv, rerror;
2279
2280	_DIAGASSERT(retval != NULL);
2281
2282	*retval = NULL;
2283	rv = _compat_start(&_compat_state);
2284	if (rv != NS_SUCCESS)
2285		return rv;
2286	rv = _compat_pwscan(&rerror, &_compat_passwd,
2287	    _compat_passwdbuf, sizeof(_compat_passwdbuf),
2288	    &_compat_state, _PW_KEYBYNAME, name, 0);
2289	if (!_compat_state.stayopen)
2290		_compat_end(&_compat_state);
2291	if (rv == NS_SUCCESS)
2292		*retval = &_compat_passwd;
2293	return rv;
2294}
2295
2296/*ARGSUSED*/
2297static int
2298_compat_getpwnam_r(void *nsrv, void *nscb, va_list ap)
2299{
2300	int		*retval	= va_arg(ap, int *);
2301	const char	*name	= va_arg(ap, const char *);
2302	struct passwd	*pw	= va_arg(ap, struct passwd *);
2303	char		*buffer	= va_arg(ap, char *);
2304	size_t		 buflen	= va_arg(ap, size_t);
2305	struct passwd  **result	= va_arg(ap, struct passwd **);
2306
2307	struct compat_state	state;
2308	int		rv;
2309
2310	_DIAGASSERT(retval != NULL);
2311	_DIAGASSERT(pw != NULL);
2312	_DIAGASSERT(buffer != NULL);
2313	_DIAGASSERT(result != NULL);
2314
2315	*result = NULL;
2316	memset(&state, 0, sizeof(state));
2317	rv = _compat_pwscan(retval, pw, buffer, buflen, &state,
2318	    _PW_KEYBYNAME, name, 0);
2319	_compat_end(&state);
2320	if (rv == NS_SUCCESS)
2321		*result = pw;
2322	return rv;
2323}
2324
2325/*ARGSUSED*/
2326static int
2327_compat_getpwuid(void *nsrv, void *nscb, va_list ap)
2328{
2329	struct passwd	**retval = va_arg(ap, struct passwd **);
2330	uid_t		 uid	= va_arg(ap, uid_t);
2331
2332	int	rv, rerror;
2333
2334	_DIAGASSERT(retval != NULL);
2335
2336	*retval = NULL;
2337	rv = _compat_start(&_compat_state);
2338	if (rv != NS_SUCCESS)
2339		return rv;
2340	rv = _compat_pwscan(&rerror, &_compat_passwd,
2341	    _compat_passwdbuf, sizeof(_compat_passwdbuf),
2342	    &_compat_state, _PW_KEYBYUID, NULL, uid);
2343	if (!_compat_state.stayopen)
2344		_compat_end(&_compat_state);
2345	if (rv == NS_SUCCESS)
2346		*retval = &_compat_passwd;
2347	return rv;
2348}
2349
2350/*ARGSUSED*/
2351static int
2352_compat_getpwuid_r(void *nsrv, void *nscb, va_list ap)
2353{
2354	int		*retval	= va_arg(ap, int *);
2355	uid_t		 uid	= va_arg(ap, uid_t);
2356	struct passwd	*pw	= va_arg(ap, struct passwd *);
2357	char		*buffer	= va_arg(ap, char *);
2358	size_t		 buflen	= va_arg(ap, size_t);
2359	struct passwd  **result	= va_arg(ap, struct passwd **);
2360
2361	struct compat_state	state;
2362	int		rv;
2363
2364	_DIAGASSERT(retval != NULL);
2365	_DIAGASSERT(pw != NULL);
2366	_DIAGASSERT(buffer != NULL);
2367	_DIAGASSERT(result != NULL);
2368
2369	*result = NULL;
2370	memset(&state, 0, sizeof(state));
2371	rv = _compat_pwscan(retval, pw, buffer, buflen, &state,
2372	    _PW_KEYBYUID, NULL, uid);
2373	_compat_end(&state);
2374	if (rv == NS_SUCCESS)
2375		*result = pw;
2376	return rv;
2377}
2378
2379#endif /* _PASSWD_COMPAT */
2380
2381
2382		/*
2383		 *	public functions
2384		 */
2385
2386struct passwd *
2387getpwent(void)
2388{
2389	int		r;
2390	struct passwd	*retval;
2391
2392	static const ns_dtab dtab[] = {
2393		NS_FILES_CB(_files_getpwent, NULL)
2394		NS_DNS_CB(_dns_getpwent, NULL)
2395		NS_NIS_CB(_nis_getpwent, NULL)
2396		NS_COMPAT_CB(_compat_getpwent, NULL)
2397		NS_NULL_CB
2398	};
2399
2400	mutex_lock(&_pwmutex);
2401	r = nsdispatch(NULL, dtab, NSDB_PASSWD, "getpwent", __nsdefaultcompat,
2402	    &retval);
2403	mutex_unlock(&_pwmutex);
2404	return (r == NS_SUCCESS) ? retval : NULL;
2405}
2406
2407int
2408getpwent_r(struct passwd *pwd, char *buffer, size_t buflen,
2409    struct passwd **result)
2410{
2411	int	r, retval;
2412
2413	static const ns_dtab dtab[] = {
2414		NS_FILES_CB(_files_getpwent_r, NULL)
2415		NS_DNS_CB(_dns_getpwent_r, NULL)
2416		NS_NIS_CB(_nis_getpwent_r, NULL)
2417		NS_COMPAT_CB(_compat_getpwent_r, NULL)
2418		NS_NULL_CB
2419	};
2420
2421	_DIAGASSERT(pwd != NULL);
2422	_DIAGASSERT(buffer != NULL);
2423	_DIAGASSERT(result != NULL);
2424
2425	*result = NULL;
2426	retval = 0;
2427	mutex_lock(&_pwmutex);
2428	r = nsdispatch(NULL, dtab, NSDB_PASSWD, "getpwent_r", __nsdefaultcompat,
2429	    &retval, pwd, buffer, buflen, result);
2430	mutex_unlock(&_pwmutex);
2431	switch (r) {
2432	case NS_SUCCESS:
2433	case NS_NOTFOUND:
2434		return 0;
2435	default:
2436		return retval;
2437	}
2438}
2439
2440
2441struct passwd *
2442getpwnam(const char *name)
2443{
2444	int		rv;
2445	struct passwd	*retval;
2446
2447	static const ns_dtab dtab[] = {
2448		NS_FILES_CB(_files_getpwnam, NULL)
2449		NS_DNS_CB(_dns_getpwnam, NULL)
2450		NS_NIS_CB(_nis_getpwnam, NULL)
2451		NS_COMPAT_CB(_compat_getpwnam, NULL)
2452		NS_NULL_CB
2453	};
2454
2455	mutex_lock(&_pwmutex);
2456	rv = nsdispatch(NULL, dtab, NSDB_PASSWD, "getpwnam", __nsdefaultcompat,
2457	    &retval, name);
2458	mutex_unlock(&_pwmutex);
2459	return (rv == NS_SUCCESS) ? retval : NULL;
2460}
2461
2462int
2463getpwnam_r(const char *name, struct passwd *pwd, char *buffer, size_t buflen,
2464	struct passwd **result)
2465{
2466	int	r, retval;
2467
2468	static const ns_dtab dtab[] = {
2469		NS_FILES_CB(_files_getpwnam_r, NULL)
2470		NS_DNS_CB(_dns_getpwnam_r, NULL)
2471		NS_NIS_CB(_nis_getpwnam_r, NULL)
2472		NS_COMPAT_CB(_compat_getpwnam_r, NULL)
2473		NS_NULL_CB
2474	};
2475
2476	_DIAGASSERT(name != NULL);
2477	_DIAGASSERT(pwd != NULL);
2478	_DIAGASSERT(buffer != NULL);
2479	_DIAGASSERT(result != NULL);
2480
2481	*result = NULL;
2482	retval = 0;
2483	mutex_lock(&_pwmutex);
2484	r = nsdispatch(NULL, dtab, NSDB_PASSWD, "getpwnam_r", __nsdefaultcompat,
2485	    &retval, name, pwd, buffer, buflen, result);
2486	mutex_unlock(&_pwmutex);
2487	switch (r) {
2488	case NS_SUCCESS:
2489	case NS_NOTFOUND:
2490		return 0;
2491	default:
2492		return retval;
2493	}
2494}
2495
2496struct passwd *
2497getpwuid(uid_t uid)
2498{
2499	int		rv;
2500	struct passwd	*retval;
2501
2502	static const ns_dtab dtab[] = {
2503		NS_FILES_CB(_files_getpwuid, NULL)
2504		NS_DNS_CB(_dns_getpwuid, NULL)
2505		NS_NIS_CB(_nis_getpwuid, NULL)
2506		NS_COMPAT_CB(_compat_getpwuid, NULL)
2507		NS_NULL_CB
2508	};
2509
2510	mutex_lock(&_pwmutex);
2511	rv = nsdispatch(NULL, dtab, NSDB_PASSWD, "getpwuid", __nsdefaultcompat,
2512	    &retval, uid);
2513	mutex_unlock(&_pwmutex);
2514	return (rv == NS_SUCCESS) ? retval : NULL;
2515}
2516
2517int
2518getpwuid_r(uid_t uid, struct passwd *pwd, char *buffer, size_t buflen,
2519	struct passwd **result)
2520{
2521	int	r, retval;
2522
2523	static const ns_dtab dtab[] = {
2524		NS_FILES_CB(_files_getpwuid_r, NULL)
2525		NS_DNS_CB(_dns_getpwuid_r, NULL)
2526		NS_NIS_CB(_nis_getpwuid_r, NULL)
2527		NS_COMPAT_CB(_compat_getpwuid_r, NULL)
2528		NS_NULL_CB
2529	};
2530
2531	_DIAGASSERT(pwd != NULL);
2532	_DIAGASSERT(buffer != NULL);
2533	_DIAGASSERT(result != NULL);
2534
2535	*result = NULL;
2536	retval = 0;
2537	mutex_lock(&_pwmutex);
2538	r = nsdispatch(NULL, dtab, NSDB_PASSWD, "getpwuid_r", __nsdefaultcompat,
2539	    &retval, uid, pwd, buffer, buflen, result);
2540	mutex_unlock(&_pwmutex);
2541	switch (r) {
2542	case NS_SUCCESS:
2543	case NS_NOTFOUND:
2544		return 0;
2545	default:
2546		return retval;
2547	}
2548}
2549
2550void
2551endpwent(void)
2552{
2553	static const ns_dtab dtab[] = {
2554		NS_FILES_CB(_files_endpwent, NULL)
2555		NS_DNS_CB(_dns_endpwent, NULL)
2556		NS_NIS_CB(_nis_endpwent, NULL)
2557		NS_COMPAT_CB(_compat_endpwent, NULL)
2558		NS_NULL_CB
2559	};
2560
2561	mutex_lock(&_pwmutex);
2562					/* force all endpwent() methods */
2563	(void) nsdispatch(NULL, dtab, NSDB_PASSWD, "endpwent",
2564	    __nsdefaultcompat_forceall);
2565	mutex_unlock(&_pwmutex);
2566}
2567
2568/*ARGSUSED*/
2569int
2570setpassent(int stayopen)
2571{
2572	static const ns_dtab dtab[] = {
2573		NS_FILES_CB(_files_setpassent, NULL)
2574		NS_DNS_CB(_dns_setpassent, NULL)
2575		NS_NIS_CB(_nis_setpassent, NULL)
2576		NS_COMPAT_CB(_compat_setpassent, NULL)
2577		NS_NULL_CB
2578	};
2579	int	rv, retval;
2580
2581	mutex_lock(&_pwmutex);
2582					/* force all setpassent() methods */
2583	rv = nsdispatch(NULL, dtab, NSDB_PASSWD, "setpassent",
2584	    __nsdefaultcompat_forceall, &retval, stayopen);
2585	mutex_unlock(&_pwmutex);
2586	return (rv == NS_SUCCESS) ? retval : 0;
2587}
2588
2589void
2590setpwent(void)
2591{
2592	static const ns_dtab dtab[] = {
2593		NS_FILES_CB(_files_setpwent, NULL)
2594		NS_DNS_CB(_dns_setpwent, NULL)
2595		NS_NIS_CB(_nis_setpwent, NULL)
2596		NS_COMPAT_CB(_compat_setpwent, NULL)
2597		NS_NULL_CB
2598	};
2599
2600	mutex_lock(&_pwmutex);
2601					/* force all setpwent() methods */
2602	(void) nsdispatch(NULL, dtab, NSDB_PASSWD, "setpwent",
2603	    __nsdefaultcompat_forceall);
2604	mutex_unlock(&_pwmutex);
2605}
2606