 2fe8fb192f
			
		
	
	
		2fe8fb192f
		
	
	
	
	
		
			
			There is important information about booting non-ack images in docs/UPDATING. ack/aout-format images can't be built any more, and booting clang/ELF-format ones is a little different. Updating to the new boot monitor is recommended. Changes in this commit: . drop boot monitor -> allowing dropping ack support . facility to copy ELF boot files to /boot so that old boot monitor can still boot fairly easily, see UPDATING . no more ack-format libraries -> single-case libraries . some cleanup of OBJECT_FMT, COMPILER_TYPE, etc cases . drop several ack toolchain commands, but not all support commands (e.g. aal is gone but acksize is not yet). . a few libc files moved to netbsd libc dir . new /bin/date as minix date used code in libc/ . test compile fix . harmonize includes . /usr/lib is no longer special: without ack, /usr/lib plays no kind of special bootstrapping role any more and bootstrapping is done exclusively through packages, so releases depend even less on the state of the machine making them now. . rename nbsd_lib* to lib* . reduce mtree
		
			
				
	
	
		
			2581 lines
		
	
	
		
			59 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			2581 lines
		
	
	
		
			59 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| /*	$NetBSD: getpwent.c,v 1.77 2010/03/23 20:28:59 drochner Exp $	*/
 | |
| 
 | |
| /*-
 | |
|  * Copyright (c) 1997-2000, 2004-2005 The NetBSD Foundation, Inc.
 | |
|  * All rights reserved.
 | |
|  *
 | |
|  * This code is derived from software contributed to The NetBSD Foundation
 | |
|  * by Luke Mewburn.
 | |
|  *
 | |
|  * Redistribution and use in source and binary forms, with or without
 | |
|  * modification, are permitted provided that the following conditions
 | |
|  * are met:
 | |
|  * 1. Redistributions of source code must retain the above copyright
 | |
|  *    notice, this list of conditions and the following disclaimer.
 | |
|  * 2. Redistributions in binary form must reproduce the above copyright
 | |
|  *    notice, this list of conditions and the following disclaimer in the
 | |
|  *    documentation and/or other materials provided with the distribution.
 | |
|  *
 | |
|  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
 | |
|  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
 | |
|  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 | |
|  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
 | |
|  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 | |
|  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 | |
|  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 | |
|  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 | |
|  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 | |
|  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 | |
|  * POSSIBILITY OF SUCH DAMAGE.
 | |
|  */
 | |
| 
 | |
| /*
 | |
|  * Copyright (c) 1988, 1993
 | |
|  *	The Regents of the University of California.  All rights reserved.
 | |
|  *
 | |
|  * Redistribution and use in source and binary forms, with or without
 | |
|  * modification, are permitted provided that the following conditions
 | |
|  * are met:
 | |
|  * 1. Redistributions of source code must retain the above copyright
 | |
|  *    notice, this list of conditions and the following disclaimer.
 | |
|  * 2. Redistributions in binary form must reproduce the above copyright
 | |
|  *    notice, this list of conditions and the following disclaimer in the
 | |
|  *    documentation and/or other materials provided with the distribution.
 | |
|  * 3. Neither the name of the University nor the names of its contributors
 | |
|  *    may be used to endorse or promote products derived from this software
 | |
|  *    without specific prior written permission.
 | |
|  *
 | |
|  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
 | |
|  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 | |
|  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 | |
|  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
 | |
|  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 | |
|  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 | |
|  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 | |
|  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 | |
|  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 | |
|  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 | |
|  * SUCH DAMAGE.
 | |
|  */
 | |
| 
 | |
| /*
 | |
|  * Portions Copyright (c) 1994, 1995, Jason Downs.  All rights reserved.
 | |
|  *
 | |
|  * Redistribution and use in source and binary forms, with or without
 | |
|  * modification, are permitted provided that the following conditions
 | |
|  * are met:
 | |
|  * 1. Redistributions of source code must retain the above copyright
 | |
|  *    notice, this list of conditions and the following disclaimer.
 | |
|  * 2. Redistributions in binary form must reproduce the above copyright
 | |
|  *    notice, this list of conditions and the following disclaimer in the
 | |
|  *    documentation and/or other materials provided with the distribution.
 | |
|  *
 | |
|  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS
 | |
|  * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 | |
|  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 | |
|  * DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT,
 | |
|  * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 | |
|  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
 | |
|  * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
 | |
|  * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 | |
|  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 | |
|  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 | |
|  * SUCH DAMAGE.
 | |
|  */
 | |
| 
 | |
| #include <sys/cdefs.h>
 | |
| #if defined(LIBC_SCCS) && !defined(lint)
 | |
| #if 0
 | |
| static char sccsid[] = "@(#)getpwent.c	8.2 (Berkeley) 4/27/95";
 | |
| #else
 | |
| __RCSID("$NetBSD: getpwent.c,v 1.77 2010/03/23 20:28:59 drochner Exp $");
 | |
| #endif
 | |
| #endif /* LIBC_SCCS and not lint */
 | |
| 
 | |
| #include "namespace.h"
 | |
| #include "reentrant.h"
 | |
| 
 | |
| #include <sys/param.h>
 | |
| 
 | |
| #include <assert.h>
 | |
| #include <db.h>
 | |
| #include <errno.h>
 | |
| #include <fcntl.h>
 | |
| #include <limits.h>
 | |
| #include <netgroup.h>
 | |
| #include <nsswitch.h>
 | |
| #include <pwd.h>
 | |
| #include <stdarg.h>
 | |
| #include <stdio.h>
 | |
| #include <stdlib.h>
 | |
| #include <string.h>
 | |
| #include <syslog.h>
 | |
| #include <unistd.h>
 | |
| 
 | |
| #ifdef HESIOD
 | |
| #include <hesiod.h>
 | |
| #endif
 | |
| 
 | |
| #ifdef YP
 | |
| #include <machine/param.h>
 | |
| #include <rpc/rpc.h>
 | |
| #include <rpcsvc/yp_prot.h>
 | |
| #include <rpcsvc/ypclnt.h>
 | |
| #endif
 | |
| 
 | |
| #include "pw_private.h"
 | |
| 
 | |
| #define	_PASSWD_COMPAT	/* "passwd" defaults to compat, so always provide it */
 | |
| 
 | |
| #ifdef __weak_alias
 | |
| __weak_alias(endpwent,_endpwent)
 | |
| __weak_alias(setpassent,_setpassent)
 | |
| __weak_alias(setpwent,_setpwent)
 | |
| #endif
 | |
| 
 | |
| #ifdef _REENTRANT
 | |
| static 	mutex_t			_pwmutex = MUTEX_INITIALIZER;
 | |
| #endif
 | |
| 
 | |
| const char __yp_token[] = "__YP!";	/* Let pwd_mkdb pull this in. */
 | |
| 
 | |
| 
 | |
| /*
 | |
|  * The pwd.db lookup techniques and data extraction code here must be kept
 | |
|  * in sync with that in `pwd_mkdb'.
 | |
|  */
 | |
| 
 | |
| #if defined(YP) || defined(HESIOD)
 | |
| /*
 | |
|  * _pw_parse
 | |
|  *	Parses entry using pw_scan(3) (without the trailing \n)
 | |
|  *	after copying to buf, and fills in pw with corresponding values.
 | |
|  *	If old is non-zero, entry is in _PASSWORD_OLDFMT.
 | |
|  *	Returns 1 if parsed successfully, 0 on parse failure.
 | |
|  */
 | |
| static int
 | |
| _pw_parse(const char *entry, struct passwd *pw, char *buf, size_t buflen,
 | |
| 	int old)
 | |
| {
 | |
| 	int	flags;
 | |
| 
 | |
| 	_DIAGASSERT(entry != NULL);
 | |
| 	_DIAGASSERT(pw != NULL);
 | |
| 	_DIAGASSERT(buf != NULL);
 | |
| 
 | |
| 	if (strlcpy(buf, entry, buflen) >= buflen)
 | |
| 		return 0;
 | |
| 	flags = _PASSWORD_NOWARN;
 | |
| 	if (old)
 | |
| 		flags |= _PASSWORD_OLDFMT;
 | |
| 	return __pw_scan(buf, pw, &flags);
 | |
| }
 | |
| #endif /* YP || HESIOD */
 | |
| 
 | |
| /*
 | |
|  * _pw_opendb
 | |
|  *	if *db is NULL, dbopen(3) /etc/spwd.db or /etc/pwd.db (depending
 | |
|  *	upon permissions, etc)
 | |
|  */
 | |
| static int
 | |
| _pw_opendb(DB **db, int *version)
 | |
| {
 | |
| 	static int	warned;
 | |
| 	DBT		key;
 | |
| 	DBT		value;
 | |
| 
 | |
| 	const char	*dbfile = NULL;
 | |
| 
 | |
| 	_DIAGASSERT(db != NULL);
 | |
| 	_DIAGASSERT(version != NULL);
 | |
| 	if (*db != NULL)					/* open *db */
 | |
| 		return NS_SUCCESS;
 | |
| 
 | |
| 	if (geteuid() == 0) {
 | |
| 		dbfile = _PATH_SMP_DB;
 | |
| 		*db = dbopen(dbfile, O_RDONLY, 0, DB_HASH, NULL);
 | |
| 	}
 | |
| 	if (*db == NULL) {
 | |
| 		dbfile = _PATH_MP_DB;
 | |
| 		*db = dbopen(dbfile, O_RDONLY, 0, DB_HASH, NULL);
 | |
| 	}
 | |
| 	if (*db == NULL) {
 | |
| 		if (!warned) {
 | |
| 			int	serrno = errno;
 | |
| 			syslog(LOG_ERR, "%s: %m", dbfile);
 | |
| 			errno = serrno;
 | |
| 		}
 | |
| 		warned = 1;
 | |
| 		return NS_UNAVAIL;
 | |
| 	}
 | |
| 	key.data = __UNCONST("VERSION");
 | |
| 	key.size = strlen((char *)key.data) + 1;
 | |
| 	switch ((*(*db)->get)(*db, &key, &value, 0)) {
 | |
| 	case 0:
 | |
| 		if (sizeof(*version) != value.size)
 | |
| 			return NS_UNAVAIL;
 | |
| 		(void)memcpy(version, value.data, value.size);
 | |
| 		break;			/* found */
 | |
| 	case 1:
 | |
| 		*version = 0;		/* not found */
 | |
| 		break;
 | |
| 	case -1:
 | |
| 		return NS_UNAVAIL;	/* error in db routines */
 | |
| 	default:
 | |
| 		abort();
 | |
| 	}
 | |
| 	return NS_SUCCESS;
 | |
| }
 | |
| 
 | |
| /*
 | |
|  * _pw_getkey
 | |
|  *	Lookup key in *db, filling in pw
 | |
|  *	with the result, allocating memory from buffer (size buflen).
 | |
|  *	(The caller may point key.data to buffer on entry; the contents
 | |
|  *	of key.data will be invalid on exit.)
 | |
|  */
 | |
| static int
 | |
| _pw_getkey(DB *db, DBT *key,
 | |
| 	struct passwd *pw, char *buffer, size_t buflen, int *pwflags,
 | |
| 	int version)
 | |
| {
 | |
| 	char		*p, *t;
 | |
| 	DBT		data;
 | |
| 
 | |
| 	_DIAGASSERT(db != NULL);
 | |
| 	_DIAGASSERT(key != NULL);
 | |
| 	_DIAGASSERT(pw != NULL);
 | |
| 	_DIAGASSERT(buffer != NULL);
 | |
| 	/* pwflags may be NULL (if we don't care about them */
 | |
| 
 | |
| 	if (db == NULL)			/* this shouldn't happen */
 | |
| 		return NS_UNAVAIL;
 | |
| 
 | |
| 	switch ((db->get)(db, key, &data, 0)) {
 | |
| 	case 0:
 | |
| 		break;			/* found */
 | |
| 	case 1:
 | |
| 		return NS_NOTFOUND;	/* not found */
 | |
| 	case -1:
 | |
| 		return NS_UNAVAIL;	/* error in db routines */
 | |
| 	default:
 | |
| 		abort();
 | |
| 	}
 | |
| 
 | |
| 	p = (char *)data.data;
 | |
| 	if (data.size > buflen) {
 | |
| 		errno = ERANGE;
 | |
| 		return NS_UNAVAIL;
 | |
| 	}
 | |
| 
 | |
| 			/*
 | |
| 			 * THE DECODING BELOW MUST MATCH THAT IN pwd_mkdb.
 | |
| 			 */
 | |
| 	t = buffer;
 | |
| #define MACRO(a)	do { a } while (/*CONSTCOND*/0)
 | |
| #define	EXPAND(e)	MACRO(e = t; while ((*t++ = *p++));)
 | |
| #define	SCALAR(v)	MACRO(memmove(&(v), p, sizeof v); p += sizeof v;)
 | |
| 	EXPAND(pw->pw_name);
 | |
| 	EXPAND(pw->pw_passwd);
 | |
| 	SCALAR(pw->pw_uid);
 | |
| 	SCALAR(pw->pw_gid);
 | |
| 	if (version == 0) {
 | |
| 		int32_t tmp;
 | |
| 		SCALAR(tmp);
 | |
| 		pw->pw_change = tmp;
 | |
| 	} else
 | |
| 		SCALAR(pw->pw_change);
 | |
| 	EXPAND(pw->pw_class);
 | |
| 	EXPAND(pw->pw_gecos);
 | |
| 	EXPAND(pw->pw_dir);
 | |
| 	EXPAND(pw->pw_shell);
 | |
| 	if (version == 0) {
 | |
| 		int32_t tmp;
 | |
| 		SCALAR(tmp);
 | |
| 		pw->pw_expire = tmp;
 | |
| 	} else
 | |
| 		SCALAR(pw->pw_expire);
 | |
| 	if (pwflags) {
 | |
| 		/* See if there's any data left.  If so, read in flags. */
 | |
| 		if (data.size > (size_t) (p - (char *)data.data)) {
 | |
| 			SCALAR(*pwflags);
 | |
| 		} else {				/* default */
 | |
| 			*pwflags = _PASSWORD_NOUID|_PASSWORD_NOGID;
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	return NS_SUCCESS;
 | |
| }
 | |
| 
 | |
| /*
 | |
|  * _pw_memfrombuf
 | |
|  *	Obtain want bytes from buffer (of size buflen) and return a pointer
 | |
|  *	to the available memory after adjusting buffer/buflen.
 | |
|  *	Returns NULL if there is insufficient space.
 | |
|  */
 | |
| static char *
 | |
| _pw_memfrombuf(size_t want, char **buffer, size_t *buflen)
 | |
| {
 | |
| 	char	*rv;
 | |
| 
 | |
| 	if (want > *buflen) {
 | |
| 		errno = ERANGE;
 | |
| 		return NULL;
 | |
| 	}
 | |
| 	rv = *buffer;
 | |
| 	*buffer += want;
 | |
| 	*buflen -= want;
 | |
| 	return rv;
 | |
| }
 | |
| 
 | |
| /*
 | |
|  * _pw_copy
 | |
|  *	Copy the contents of frompw to pw; memory for strings
 | |
|  *	and arrays will be allocated from buf (of size buflen).
 | |
|  *	If proto != NULL, use various fields in proto in preference to frompw.
 | |
|  *	Returns 1 if copied successfully, 0 on copy failure.
 | |
|  *	NOTE: frompw must not use buf for its own pointers.
 | |
|  */
 | |
| static int
 | |
| _pw_copy(const struct passwd *frompw, struct passwd *pw,
 | |
| 	char *buf, size_t buflen, const struct passwd *protopw, int protoflags)
 | |
| {
 | |
| 	size_t	count;
 | |
| 	int	useproto;
 | |
| 
 | |
| 	_DIAGASSERT(frompw != NULL);
 | |
| 	_DIAGASSERT(pw != NULL);
 | |
| 	_DIAGASSERT(buf != NULL);
 | |
| 	/* protopw may be NULL */
 | |
| 
 | |
| 	useproto = protopw && protopw->pw_name;
 | |
| 
 | |
| #define	COPYSTR(to, from) \
 | |
| 	do { \
 | |
| 		count = strlen((from)); \
 | |
| 		(to) = _pw_memfrombuf(count+1, &buf, &buflen); \
 | |
| 		if ((to) == NULL) \
 | |
| 			return 0; \
 | |
| 		memmove((to), (from), count); \
 | |
| 		to[count] = '\0'; \
 | |
| 	} while (0)	/* LINTED */
 | |
| 
 | |
| #define	COPYFIELD(field)	COPYSTR(pw->field, frompw->field)
 | |
| 
 | |
| #define	COPYPROTOFIELD(field)	COPYSTR(pw->field, \
 | |
| 		(useproto && *protopw->field ? protopw->field : frompw->field))
 | |
| 
 | |
| 	COPYFIELD(pw_name);
 | |
| 
 | |
| #ifdef PW_OVERRIDE_PASSWD
 | |
| 	COPYPROTOFIELD(pw_passwd);
 | |
| #else
 | |
| 	COPYFIELD(pw_passwd);
 | |
| #endif
 | |
| 
 | |
| 	if (useproto && !(protoflags & _PASSWORD_NOUID))
 | |
| 		pw->pw_uid = protopw->pw_uid;
 | |
| 	else
 | |
| 		pw->pw_uid = frompw->pw_uid;
 | |
| 
 | |
| 	if (useproto && !(protoflags & _PASSWORD_NOGID))
 | |
| 		pw->pw_gid = protopw->pw_gid;
 | |
| 	else
 | |
| 		pw->pw_gid = frompw->pw_gid;
 | |
| 
 | |
| 	pw->pw_change = frompw->pw_change;
 | |
| 	COPYFIELD(pw_class);
 | |
| 	COPYPROTOFIELD(pw_gecos);
 | |
| 	COPYPROTOFIELD(pw_dir);
 | |
| 	COPYPROTOFIELD(pw_shell);
 | |
| 
 | |
| #undef COPYSTR
 | |
| #undef COPYFIELD
 | |
| #undef COPYPROTOFIELD
 | |
| 
 | |
| 	return 1;
 | |
| }
 | |
| 
 | |
| 
 | |
| 		/*
 | |
| 		 *	files methods
 | |
| 		 */
 | |
| 
 | |
| 	/* state shared between files methods */
 | |
| struct files_state {
 | |
| 	int	 stayopen;		/* see getpassent(3) */
 | |
| 	DB	*db;			/* passwd file handle */
 | |
| 	int	 keynum;		/* key counter, -1 if no more */
 | |
| 	int	 version;
 | |
| };
 | |
| 
 | |
| static struct files_state	_files_state;
 | |
| 					/* storage for non _r functions */
 | |
| static struct passwd		_files_passwd;
 | |
| static char			_files_passwdbuf[_GETPW_R_SIZE_MAX];
 | |
| 
 | |
| static int
 | |
| _files_start(struct files_state *state)
 | |
| {
 | |
| 	int	rv;
 | |
| 
 | |
| 	_DIAGASSERT(state != NULL);
 | |
| 
 | |
| 	state->keynum = 0;
 | |
| 	rv = _pw_opendb(&state->db, &state->version);
 | |
| 	if (rv != NS_SUCCESS)
 | |
| 		return rv;
 | |
| 	return NS_SUCCESS;
 | |
| }
 | |
| 
 | |
| static int
 | |
| _files_end(struct files_state *state)
 | |
| {
 | |
| 
 | |
| 	_DIAGASSERT(state != NULL);
 | |
| 
 | |
| 	state->keynum = 0;
 | |
| 	if (state->db) {
 | |
| 		(void)(state->db->close)(state->db);
 | |
| 		state->db = NULL;
 | |
| 	}
 | |
| 	return NS_SUCCESS;
 | |
| }
 | |
| 
 | |
| /*
 | |
|  * _files_pwscan
 | |
|  *	Search state->db for the next desired entry.
 | |
|  *	If search is _PW_KEYBYNUM, look for state->keynum.
 | |
|  *	If search is _PW_KEYBYNAME, look for name.
 | |
|  *	If search is _PW_KEYBYUID, look for uid.
 | |
|  *	Sets *retval to the errno if the result is not NS_SUCCESS
 | |
|  *	or NS_NOTFOUND.
 | |
|  */
 | |
| static int
 | |
| _files_pwscan(int *retval, struct passwd *pw, char *buffer, size_t buflen,
 | |
| 	struct files_state *state, int search, const char *name, uid_t uid)
 | |
| {
 | |
| 	const void	*from;
 | |
| 	size_t		 fromlen;
 | |
| 	DBT		 key;
 | |
| 	int		 rv;
 | |
| 
 | |
| 	_DIAGASSERT(retval != NULL);
 | |
| 	_DIAGASSERT(pw != NULL);
 | |
| 	_DIAGASSERT(buffer != NULL);
 | |
| 	_DIAGASSERT(state != NULL);
 | |
| 	/* name is NULL to indicate searching for uid */
 | |
| 
 | |
| 	*retval = 0;
 | |
| 
 | |
| 	if (state->db == NULL) {	/* only start if file not open yet */
 | |
| 		rv = _files_start(state);
 | |
| 		if (rv != NS_SUCCESS)
 | |
| 			goto filespwscan_out;
 | |
| 	}
 | |
| 
 | |
| 	for (;;) {				/* search for a match */
 | |
| 		switch (search) {
 | |
| 		case _PW_KEYBYNUM:
 | |
| 			if (state->keynum == -1)
 | |
| 				return NS_NOTFOUND;	/* no more records */
 | |
| 			state->keynum++;
 | |
| 			from = &state->keynum;
 | |
| 			fromlen = sizeof(state->keynum);
 | |
| 			break;
 | |
| 		case _PW_KEYBYNAME:
 | |
| 			from = name;
 | |
| 			fromlen = strlen(name);
 | |
| 			break;
 | |
| 		case _PW_KEYBYUID:
 | |
| 			from = &uid;
 | |
| 			fromlen = sizeof(uid);
 | |
| 			break;
 | |
| 		default:
 | |
| 			abort();
 | |
| 		}
 | |
| 
 | |
| 		if (buflen <= fromlen) {		/* buffer too small */
 | |
| 			*retval = ERANGE;
 | |
| 			return NS_UNAVAIL;
 | |
| 		}
 | |
| 		buffer[0] = search;			/* setup key */
 | |
| 		memmove(buffer + 1, from, fromlen);
 | |
| 		key.size = fromlen + 1;
 | |
| 		key.data = (u_char *)buffer;
 | |
| 
 | |
| 							/* search for key */
 | |
| 		rv = _pw_getkey(state->db, &key, pw, buffer, buflen, NULL,
 | |
| 		    state->version);
 | |
| 		if (rv != NS_SUCCESS)			/* no match */
 | |
| 			break;
 | |
| 		if (pw->pw_name[0] == '+' || pw->pw_name[0] == '-') {
 | |
| 						/* if a compat line */
 | |
| 			if (search == _PW_KEYBYNUM)
 | |
| 				continue;	/* read next if pwent */
 | |
| 			rv = NS_NOTFOUND;	/* don't match if pw{nam,uid} */
 | |
| 			break;
 | |
| 		}
 | |
| 		break;
 | |
| 	}
 | |
| 
 | |
| 	if (rv == NS_NOTFOUND && search == _PW_KEYBYNUM)
 | |
| 		state->keynum = -1;		/* flag `no more records' */
 | |
| 
 | |
| 	if (rv == NS_SUCCESS) {
 | |
| 		if ((search == _PW_KEYBYUID && pw->pw_uid != uid) ||
 | |
| 		    (search == _PW_KEYBYNAME && strcmp(pw->pw_name, name) != 0))
 | |
| 			rv = NS_NOTFOUND;
 | |
| 	}
 | |
| 
 | |
|  filespwscan_out:
 | |
| 	if (rv != NS_SUCCESS && rv != NS_NOTFOUND)
 | |
| 		*retval = errno;
 | |
| 	return rv;
 | |
| }
 | |
| 
 | |
| /*ARGSUSED*/
 | |
| static int
 | |
| _files_setpwent(void *nsrv, void *nscb, va_list ap)
 | |
| {
 | |
| 
 | |
| 	_files_state.stayopen = 0;
 | |
| 	return _files_start(&_files_state);
 | |
| }
 | |
| 
 | |
| /*ARGSUSED*/
 | |
| static int
 | |
| _files_setpassent(void *nsrv, void *nscb, va_list ap)
 | |
| {
 | |
| 	int	*retval		= va_arg(ap, int *);
 | |
| 	int	 stayopen	= va_arg(ap, int);
 | |
| 
 | |
| 	int	rv;
 | |
| 
 | |
| 	_files_state.stayopen = stayopen;
 | |
| 	rv = _files_start(&_files_state);
 | |
| 	*retval = (rv == NS_SUCCESS);
 | |
| 	return rv;
 | |
| }
 | |
| 
 | |
| /*ARGSUSED*/
 | |
| static int
 | |
| _files_endpwent(void *nsrv, void *nscb, va_list ap)
 | |
| {
 | |
| 
 | |
| 	_files_state.stayopen = 0;
 | |
| 	return _files_end(&_files_state);
 | |
| }
 | |
| 
 | |
| /*ARGSUSED*/
 | |
| static int
 | |
| _files_getpwent(void *nsrv, void *nscb, va_list ap)
 | |
| {
 | |
| 	struct passwd	**retval = va_arg(ap, struct passwd **);
 | |
| 
 | |
| 	int	rv, rerror;
 | |
| 
 | |
| 	_DIAGASSERT(retval != NULL);
 | |
| 
 | |
| 	*retval = NULL;
 | |
| 	rv = _files_pwscan(&rerror, &_files_passwd,
 | |
| 	    _files_passwdbuf, sizeof(_files_passwdbuf),
 | |
| 	    &_files_state, _PW_KEYBYNUM, NULL, 0);
 | |
| 	if (rv == NS_SUCCESS)
 | |
| 		*retval = &_files_passwd;
 | |
| 	return rv;
 | |
| }
 | |
| 
 | |
| /*ARGSUSED*/
 | |
| static int
 | |
| _files_getpwent_r(void *nsrv, void *nscb, va_list ap)
 | |
| {
 | |
| 	int		*retval	= va_arg(ap, int *);
 | |
| 	struct passwd	*pw	= va_arg(ap, struct passwd *);
 | |
| 	char		*buffer	= va_arg(ap, char *);
 | |
| 	size_t		 buflen	= va_arg(ap, size_t);
 | |
| 	struct passwd  **result	= va_arg(ap, struct passwd **);
 | |
| 
 | |
| 	int	rv;
 | |
| 
 | |
| 	_DIAGASSERT(retval != NULL);
 | |
| 	_DIAGASSERT(pw != NULL);
 | |
| 	_DIAGASSERT(buffer != NULL);
 | |
| 	_DIAGASSERT(result != NULL);
 | |
| 
 | |
| 	rv = _files_pwscan(retval, pw, buffer, buflen, &_files_state,
 | |
| 	    _PW_KEYBYNUM, NULL, 0);
 | |
| 	if (rv == NS_SUCCESS)
 | |
| 		*result = pw;
 | |
| 	else
 | |
| 		*result = NULL;
 | |
| 	return rv;
 | |
| }
 | |
| 
 | |
| /*ARGSUSED*/
 | |
| static int
 | |
| _files_getpwnam(void *nsrv, void *nscb, va_list ap)
 | |
| {
 | |
| 	struct passwd	**retval = va_arg(ap, struct passwd **);
 | |
| 	const char	*name	= va_arg(ap, const char *);
 | |
| 
 | |
| 	int	rv, rerror;
 | |
| 
 | |
| 	_DIAGASSERT(retval != NULL);
 | |
| 
 | |
| 	*retval = NULL;
 | |
| 	rv = _files_start(&_files_state);
 | |
| 	if (rv != NS_SUCCESS)
 | |
| 		return rv;
 | |
| 	rv = _files_pwscan(&rerror, &_files_passwd,
 | |
| 	    _files_passwdbuf, sizeof(_files_passwdbuf),
 | |
| 	    &_files_state, _PW_KEYBYNAME, name, 0);
 | |
| 	if (!_files_state.stayopen)
 | |
| 		_files_end(&_files_state);
 | |
| 	if (rv == NS_SUCCESS)
 | |
| 		*retval = &_files_passwd;
 | |
| 	return rv;
 | |
| }
 | |
| 
 | |
| /*ARGSUSED*/
 | |
| static int
 | |
| _files_getpwnam_r(void *nsrv, void *nscb, va_list ap)
 | |
| {
 | |
| 	int		*retval	= va_arg(ap, int *);
 | |
| 	const char	*name	= va_arg(ap, const char *);
 | |
| 	struct passwd	*pw	= va_arg(ap, struct passwd *);
 | |
| 	char		*buffer	= va_arg(ap, char *);
 | |
| 	size_t		 buflen	= va_arg(ap, size_t);
 | |
| 	struct passwd  **result	= va_arg(ap, struct passwd **);
 | |
| 
 | |
| 	struct files_state state;
 | |
| 	int	rv;
 | |
| 
 | |
| 	_DIAGASSERT(retval != NULL);
 | |
| 	_DIAGASSERT(pw != NULL);
 | |
| 	_DIAGASSERT(buffer != NULL);
 | |
| 	_DIAGASSERT(result != NULL);
 | |
| 
 | |
| 	*result = NULL;
 | |
| 	memset(&state, 0, sizeof(state));
 | |
| 	rv = _files_pwscan(retval, pw, buffer, buflen, &state,
 | |
| 	    _PW_KEYBYNAME, name, 0);
 | |
| 	_files_end(&state);
 | |
| 	if (rv == NS_SUCCESS)
 | |
| 		*result = pw;
 | |
| 	return rv;
 | |
| }
 | |
| 
 | |
| /*ARGSUSED*/
 | |
| static int
 | |
| _files_getpwuid(void *nsrv, void *nscb, va_list ap)
 | |
| {
 | |
| 	struct passwd	**retval = va_arg(ap, struct passwd **);
 | |
| 	uid_t		 uid	= va_arg(ap, uid_t);
 | |
| 
 | |
| 	int	rv, rerror;
 | |
| 
 | |
| 	_DIAGASSERT(retval != NULL);
 | |
| 
 | |
| 	*retval = NULL;
 | |
| 	rv = _files_start(&_files_state);
 | |
| 	if (rv != NS_SUCCESS)
 | |
| 		return rv;
 | |
| 	rv = _files_pwscan(&rerror, &_files_passwd,
 | |
| 	    _files_passwdbuf, sizeof(_files_passwdbuf),
 | |
| 	    &_files_state, _PW_KEYBYUID, NULL, uid);
 | |
| 	if (!_files_state.stayopen)
 | |
| 		_files_end(&_files_state);
 | |
| 	if (rv == NS_SUCCESS)
 | |
| 		*retval = &_files_passwd;
 | |
| 	return rv;
 | |
| }
 | |
| 
 | |
| /*ARGSUSED*/
 | |
| static int
 | |
| _files_getpwuid_r(void *nsrv, void *nscb, va_list ap)
 | |
| {
 | |
| 	int		*retval	= va_arg(ap, int *);
 | |
| 	uid_t		 uid	= va_arg(ap, uid_t);
 | |
| 	struct passwd	*pw	= va_arg(ap, struct passwd *);
 | |
| 	char		*buffer	= va_arg(ap, char *);
 | |
| 	size_t		 buflen	= va_arg(ap, size_t);
 | |
| 	struct passwd  **result	= va_arg(ap, struct passwd **);
 | |
| 
 | |
| 	struct files_state state;
 | |
| 	int	rv;
 | |
| 
 | |
| 	_DIAGASSERT(retval != NULL);
 | |
| 	_DIAGASSERT(pw != NULL);
 | |
| 	_DIAGASSERT(buffer != NULL);
 | |
| 	_DIAGASSERT(result != NULL);
 | |
| 
 | |
| 	*result = NULL;
 | |
| 	memset(&state, 0, sizeof(state));
 | |
| 	rv = _files_pwscan(retval, pw, buffer, buflen, &state,
 | |
| 	    _PW_KEYBYUID, NULL, uid);
 | |
| 	_files_end(&state);
 | |
| 	if (rv == NS_SUCCESS)
 | |
| 		*result = pw;
 | |
| 	return rv;
 | |
| }
 | |
| 
 | |
| 
 | |
| #ifdef HESIOD
 | |
| 		/*
 | |
| 		 *	dns methods
 | |
| 		 */
 | |
| 
 | |
| 	/* state shared between dns methods */
 | |
| struct dns_state {
 | |
| 	int	 stayopen;		/* see getpassent(3) */
 | |
| 	void	*context;		/* Hesiod context */
 | |
| 	int	 num;			/* passwd index, -1 if no more */
 | |
| };
 | |
| 
 | |
| static struct dns_state		_dns_state;
 | |
| 					/* storage for non _r functions */
 | |
| static struct passwd		_dns_passwd;
 | |
| static char			_dns_passwdbuf[_GETPW_R_SIZE_MAX];
 | |
| 
 | |
| static int
 | |
| _dns_start(struct dns_state *state)
 | |
| {
 | |
| 
 | |
| 	_DIAGASSERT(state != NULL);
 | |
| 
 | |
| 	state->num = 0;
 | |
| 	if (state->context == NULL) {			/* setup Hesiod */
 | |
| 		if (hesiod_init(&state->context) == -1)
 | |
| 			return NS_UNAVAIL;
 | |
| 	}
 | |
| 
 | |
| 	return NS_SUCCESS;
 | |
| }
 | |
| 
 | |
| static int
 | |
| _dns_end(struct dns_state *state)
 | |
| {
 | |
| 
 | |
| 	_DIAGASSERT(state != NULL);
 | |
| 
 | |
| 	state->num = 0;
 | |
| 	if (state->context) {
 | |
| 		hesiod_end(state->context);
 | |
| 		state->context = NULL;
 | |
| 	}
 | |
| 	return NS_SUCCESS;
 | |
| }
 | |
| 
 | |
| /*
 | |
|  * _dns_pwscan
 | |
|  *	Look for the Hesiod name provided in buffer in the NULL-terminated
 | |
|  *	list of zones,
 | |
|  *	and decode into pw/buffer/buflen.
 | |
|  */
 | |
| static int
 | |
| _dns_pwscan(int *retval, struct passwd *pw, char *buffer, size_t buflen,
 | |
| 	struct dns_state *state, const char **zones)
 | |
| {
 | |
| 	const char	**curzone;
 | |
| 	char		**hp, *ep;
 | |
| 	int		rv;
 | |
| 
 | |
| 	_DIAGASSERT(retval != NULL);
 | |
| 	_DIAGASSERT(pw != NULL);
 | |
| 	_DIAGASSERT(buffer != NULL);
 | |
| 	_DIAGASSERT(state != NULL);
 | |
| 	_DIAGASSERT(zones != NULL);
 | |
| 
 | |
| 	*retval = 0;
 | |
| 
 | |
| 	if (state->context == NULL) {	/* only start if Hesiod not setup */
 | |
| 		rv = _dns_start(state);
 | |
| 		if (rv != NS_SUCCESS)
 | |
| 			return rv;
 | |
| 	}
 | |
| 
 | |
| 	hp = NULL;
 | |
| 	rv = NS_NOTFOUND;
 | |
| 
 | |
| 	for (curzone = zones; *curzone; curzone++) {	/* search zones */
 | |
| 		hp = hesiod_resolve(state->context, buffer, *curzone);
 | |
| 		if (hp != NULL)
 | |
| 			break;
 | |
| 		if (errno != ENOENT) {
 | |
| 			rv = NS_UNAVAIL;
 | |
| 			goto dnspwscan_out;
 | |
| 		}
 | |
| 	}
 | |
| 	if (*curzone == NULL)
 | |
| 		goto dnspwscan_out;
 | |
| 
 | |
| 	if ((ep = strchr(hp[0], '\n')) != NULL)
 | |
| 		*ep = '\0';				/* clear trailing \n */
 | |
| 	if (_pw_parse(hp[0], pw, buffer, buflen, 1))	/* validate line */
 | |
| 		rv = NS_SUCCESS;
 | |
| 	else
 | |
| 		rv = NS_UNAVAIL;
 | |
| 
 | |
|  dnspwscan_out:
 | |
| 	if (rv != NS_SUCCESS && rv != NS_NOTFOUND)
 | |
| 		*retval = errno;
 | |
| 	if (hp)
 | |
| 		hesiod_free_list(state->context, hp);
 | |
| 	return rv;
 | |
| }
 | |
| 
 | |
| /*ARGSUSED*/
 | |
| static int
 | |
| _dns_setpwent(void *nsrv, void *nscb, va_list ap)
 | |
| {
 | |
| 
 | |
| 	_dns_state.stayopen = 0;
 | |
| 	return _dns_start(&_dns_state);
 | |
| }
 | |
| 
 | |
| /*ARGSUSED*/
 | |
| static int
 | |
| _dns_setpassent(void *nsrv, void *nscb, va_list ap)
 | |
| {
 | |
| 	int	*retval		= va_arg(ap, int *);
 | |
| 	int	 stayopen	= va_arg(ap, int);
 | |
| 
 | |
| 	int	rv;
 | |
| 
 | |
| 	_dns_state.stayopen = stayopen;
 | |
| 	rv = _dns_start(&_dns_state);
 | |
| 	*retval = (rv == NS_SUCCESS);
 | |
| 	return rv;
 | |
| }
 | |
| 
 | |
| /*ARGSUSED*/
 | |
| static int
 | |
| _dns_endpwent(void *nsrv, void *nscb, va_list ap)
 | |
| {
 | |
| 
 | |
| 	_dns_state.stayopen = 0;
 | |
| 	return _dns_end(&_dns_state);
 | |
| }
 | |
| 
 | |
| /*ARGSUSED*/
 | |
| static int
 | |
| _dns_getpwent(void *nsrv, void *nscb, va_list ap)
 | |
| {
 | |
| 	struct passwd	**retval = va_arg(ap, struct passwd **);
 | |
| 
 | |
| 	char	**hp, *ep;
 | |
| 	int	  rv;
 | |
| 
 | |
| 	_DIAGASSERT(retval != NULL);
 | |
| 
 | |
| 	*retval = NULL;
 | |
| 
 | |
| 	if (_dns_state.num == -1)			/* exhausted search */
 | |
| 		return NS_NOTFOUND;
 | |
| 
 | |
| 	if (_dns_state.context == NULL) {
 | |
| 			/* only start if Hesiod not setup */
 | |
| 		rv = _dns_start(&_dns_state);
 | |
| 		if (rv != NS_SUCCESS)
 | |
| 			return rv;
 | |
| 	}
 | |
| 
 | |
|  next_dns_entry:
 | |
| 	hp = NULL;
 | |
| 	rv = NS_NOTFOUND;
 | |
| 
 | |
| 							/* find passwd-NNN */
 | |
| 	snprintf(_dns_passwdbuf, sizeof(_dns_passwdbuf),
 | |
| 	    "passwd-%u", _dns_state.num);
 | |
| 	_dns_state.num++;
 | |
| 
 | |
| 	hp = hesiod_resolve(_dns_state.context, _dns_passwdbuf, "passwd");
 | |
| 	if (hp == NULL) {
 | |
| 		if (errno == ENOENT)
 | |
| 			_dns_state.num = -1;
 | |
| 		else
 | |
| 			rv = NS_UNAVAIL;
 | |
| 	} else {
 | |
| 		if ((ep = strchr(hp[0], '\n')) != NULL)
 | |
| 			*ep = '\0';			/* clear trailing \n */
 | |
| 							/* validate line */
 | |
| 		if (_pw_parse(hp[0], &_dns_passwd,
 | |
| 		    _dns_passwdbuf, sizeof(_dns_passwdbuf), 1))
 | |
| 			rv = NS_SUCCESS;
 | |
| 		else {				/* dodgy entry, try again */
 | |
| 			hesiod_free_list(_dns_state.context, hp);
 | |
| 			goto next_dns_entry;
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	if (hp)
 | |
| 		hesiod_free_list(_dns_state.context, hp);
 | |
| 	if (rv == NS_SUCCESS)
 | |
| 		*retval = &_dns_passwd;
 | |
| 	return rv;
 | |
| }
 | |
| 
 | |
| /*ARGSUSED*/
 | |
| static int
 | |
| _dns_getpwent_r(void *nsrv, void *nscb, va_list ap)
 | |
| {
 | |
| 	int		*retval	= va_arg(ap, int *);
 | |
| 	struct passwd	*pw	= va_arg(ap, struct passwd *);
 | |
| 	char		*buffer	= va_arg(ap, char *);
 | |
| 	size_t		 buflen	= va_arg(ap, size_t);
 | |
| 	struct passwd  **result	= va_arg(ap, struct passwd **);
 | |
| 
 | |
| 	char	**hp, *ep;
 | |
| 	int	  rv;
 | |
| 
 | |
| 	_DIAGASSERT(retval != NULL);
 | |
| 	_DIAGASSERT(pw != NULL);
 | |
| 	_DIAGASSERT(buffer != NULL);
 | |
| 	_DIAGASSERT(result != NULL);
 | |
| 
 | |
| 	*retval = 0;
 | |
| 
 | |
| 	if (_dns_state.num == -1)			/* exhausted search */
 | |
| 		return NS_NOTFOUND;
 | |
| 
 | |
| 	if (_dns_state.context == NULL) {
 | |
| 			/* only start if Hesiod not setup */
 | |
| 		rv = _dns_start(&_dns_state);
 | |
| 		if (rv != NS_SUCCESS)
 | |
| 			return rv;
 | |
| 	}
 | |
| 
 | |
|  next_dns_entry:
 | |
| 	hp = NULL;
 | |
| 	rv = NS_NOTFOUND;
 | |
| 
 | |
| 							/* find passwd-NNN */
 | |
| 	snprintf(buffer, buflen, "passwd-%u", _dns_state.num);
 | |
| 	_dns_state.num++;
 | |
| 
 | |
| 	hp = hesiod_resolve(_dns_state.context, buffer, "passwd");
 | |
| 	if (hp == NULL) {
 | |
| 		if (errno == ENOENT)
 | |
| 			_dns_state.num = -1;
 | |
| 		else
 | |
| 			rv = NS_UNAVAIL;
 | |
| 	} else {
 | |
| 		if ((ep = strchr(hp[0], '\n')) != NULL)
 | |
| 			*ep = '\0';			/* clear trailing \n */
 | |
| 							/* validate line */
 | |
| 		if (_pw_parse(hp[0], pw, buffer, buflen, 1))
 | |
| 			rv = NS_SUCCESS;
 | |
| 		else {				/* dodgy entry, try again */
 | |
| 			hesiod_free_list(_dns_state.context, hp);
 | |
| 			goto next_dns_entry;
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	if (hp)
 | |
| 		hesiod_free_list(_dns_state.context, hp);
 | |
| 	if (rv == NS_SUCCESS)
 | |
| 		*result = pw;
 | |
| 	else
 | |
| 		*result = NULL;
 | |
| 	return rv;
 | |
| }
 | |
| 
 | |
| static const char *_dns_uid_zones[] = {
 | |
| 	"uid",
 | |
| 	"passwd",
 | |
| 	NULL
 | |
| };
 | |
| 
 | |
| /*ARGSUSED*/
 | |
| static int
 | |
| _dns_getpwuid(void *nsrv, void *nscb, va_list ap)
 | |
| {
 | |
| 	struct passwd	**retval = va_arg(ap, struct passwd **);
 | |
| 	uid_t		 uid	= va_arg(ap, uid_t);
 | |
| 
 | |
| 	int	rv, rerror;
 | |
| 
 | |
| 	_DIAGASSERT(retval != NULL);
 | |
| 
 | |
| 	*retval = NULL;
 | |
| 	rv = _dns_start(&_dns_state);
 | |
| 	if (rv != NS_SUCCESS)
 | |
| 		return rv;
 | |
| 	snprintf(_dns_passwdbuf, sizeof(_dns_passwdbuf),
 | |
| 	    "%u", (unsigned int)uid);
 | |
| 	rv = _dns_pwscan(&rerror, &_dns_passwd,
 | |
| 	    _dns_passwdbuf, sizeof(_dns_passwdbuf),
 | |
| 	    &_dns_state, _dns_uid_zones);
 | |
| 	if (!_dns_state.stayopen)
 | |
| 		_dns_end(&_dns_state);
 | |
| 	if (rv == NS_SUCCESS && uid == _dns_passwd.pw_uid)
 | |
| 		*retval = &_dns_passwd;
 | |
| 	return rv;
 | |
| }
 | |
| 
 | |
| /*ARGSUSED*/
 | |
| static int
 | |
| _dns_getpwuid_r(void *nsrv, void *nscb, va_list ap)
 | |
| {
 | |
| 	int		*retval	= va_arg(ap, int *);
 | |
| 	uid_t		 uid	= va_arg(ap, uid_t);
 | |
| 	struct passwd	*pw	= va_arg(ap, struct passwd *);
 | |
| 	char		*buffer	= va_arg(ap, char *);
 | |
| 	size_t		 buflen	= va_arg(ap, size_t);
 | |
| 	struct passwd  **result	= va_arg(ap, struct passwd **);
 | |
| 
 | |
| 	struct dns_state state;
 | |
| 	int	rv;
 | |
| 
 | |
| 	_DIAGASSERT(retval != NULL);
 | |
| 	_DIAGASSERT(pw != NULL);
 | |
| 	_DIAGASSERT(buffer != NULL);
 | |
| 	_DIAGASSERT(result != NULL);
 | |
| 
 | |
| 	*result = NULL;
 | |
| 	memset(&state, 0, sizeof(state));
 | |
| 	snprintf(buffer, buflen, "%u", (unsigned int)uid);
 | |
| 	rv = _dns_pwscan(retval, pw, buffer, buflen, &state, _dns_uid_zones);
 | |
| 	_dns_end(&state);
 | |
| 	if (rv != NS_SUCCESS)
 | |
| 		return rv;
 | |
| 	if (uid == pw->pw_uid) {
 | |
| 		*result = pw;
 | |
| 		return NS_SUCCESS;
 | |
| 	} else
 | |
| 		return NS_NOTFOUND;
 | |
| }
 | |
| 
 | |
| static const char *_dns_nam_zones[] = {
 | |
| 	"passwd",
 | |
| 	NULL
 | |
| };
 | |
| 
 | |
| /*ARGSUSED*/
 | |
| static int
 | |
| _dns_getpwnam(void *nsrv, void *nscb, va_list ap)
 | |
| {
 | |
| 	struct passwd	**retval = va_arg(ap, struct passwd **);
 | |
| 	const char	*name	= va_arg(ap, const char *);
 | |
| 
 | |
| 	int	rv, rerror;
 | |
| 
 | |
| 	_DIAGASSERT(retval != NULL);
 | |
| 
 | |
| 	*retval = NULL;
 | |
| 	rv = _dns_start(&_dns_state);
 | |
| 	if (rv != NS_SUCCESS)
 | |
| 		return rv;
 | |
| 	snprintf(_dns_passwdbuf, sizeof(_dns_passwdbuf), "%s", name);
 | |
| 	rv = _dns_pwscan(&rerror, &_dns_passwd,
 | |
| 	    _dns_passwdbuf, sizeof(_dns_passwdbuf),
 | |
| 	    &_dns_state, _dns_nam_zones);
 | |
| 	if (!_dns_state.stayopen)
 | |
| 		_dns_end(&_dns_state);
 | |
| 	if (rv == NS_SUCCESS && strcmp(name, _dns_passwd.pw_name) == 0)
 | |
| 		*retval = &_dns_passwd;
 | |
| 	return rv;
 | |
| }
 | |
| 
 | |
| /*ARGSUSED*/
 | |
| static int
 | |
| _dns_getpwnam_r(void *nsrv, void *nscb, va_list ap)
 | |
| {
 | |
| 	int		*retval	= va_arg(ap, int *);
 | |
| 	const char	*name	= va_arg(ap, const char *);
 | |
| 	struct passwd	*pw	= va_arg(ap, struct passwd *);
 | |
| 	char		*buffer	= va_arg(ap, char *);
 | |
| 	size_t		 buflen	= va_arg(ap, size_t);
 | |
| 	struct passwd  **result	= va_arg(ap, struct passwd **);
 | |
| 
 | |
| 	struct dns_state state;
 | |
| 	int	rv;
 | |
| 
 | |
| 	_DIAGASSERT(retval != NULL);
 | |
| 	_DIAGASSERT(pw != NULL);
 | |
| 	_DIAGASSERT(buffer != NULL);
 | |
| 	_DIAGASSERT(result != NULL);
 | |
| 
 | |
| 	*result = NULL;
 | |
| 	memset(&state, 0, sizeof(state));
 | |
| 	snprintf(buffer, buflen, "%s", name);
 | |
| 	rv = _dns_pwscan(retval, pw, buffer, buflen, &state, _dns_nam_zones);
 | |
| 	_dns_end(&state);
 | |
| 	if (rv != NS_SUCCESS)
 | |
| 		return rv;
 | |
| 	if (strcmp(name, pw->pw_name) == 0) {
 | |
| 		*result = pw;
 | |
| 		return NS_SUCCESS;
 | |
| 	} else
 | |
| 		return NS_NOTFOUND;
 | |
| }
 | |
| 
 | |
| #endif /* HESIOD */
 | |
| 
 | |
| 
 | |
| #ifdef YP
 | |
| 		/*
 | |
| 		 *	nis methods
 | |
| 		 */
 | |
| 	/* state shared between nis methods */
 | |
| struct nis_state {
 | |
| 	int		 stayopen;	/* see getpassent(3) */
 | |
| 	char		*domain;	/* NIS domain */
 | |
| 	int		 done;		/* non-zero if search exhausted */
 | |
| 	char		*current;	/* current first/next match */
 | |
| 	int		 currentlen;	/* length of _nis_current */
 | |
| 	enum {				/* shadow map type */
 | |
| 		NISMAP_UNKNOWN,		/*  unknown ... */
 | |
| 		NISMAP_NONE,		/*  none: use "passwd.by*" */
 | |
| 		NISMAP_ADJUNCT,		/*  pw_passwd from "passwd.adjunct.*" */
 | |
| 		NISMAP_MASTER		/*  all from "master.passwd.by*" */
 | |
| 	}		 maptype;
 | |
| };
 | |
| 
 | |
| static struct nis_state		_nis_state;
 | |
| 					/* storage for non _r functions */
 | |
| static struct passwd		_nis_passwd;
 | |
| static char			_nis_passwdbuf[_GETPW_R_SIZE_MAX];
 | |
| 
 | |
| 	/* macros for deciding which NIS maps to use. */
 | |
| #define	PASSWD_BYNAME(x)	((x)->maptype == NISMAP_MASTER \
 | |
| 				    ? "master.passwd.byname" : "passwd.byname")
 | |
| #define	PASSWD_BYUID(x)		((x)->maptype == NISMAP_MASTER \
 | |
| 				    ? "master.passwd.byuid" : "passwd.byuid")
 | |
| 
 | |
| static int
 | |
| _nis_start(struct nis_state *state)
 | |
| {
 | |
| 
 | |
| 	_DIAGASSERT(state != NULL);
 | |
| 
 | |
| 	state->done = 0;
 | |
| 	if (state->current) {
 | |
| 		free(state->current);
 | |
| 		state->current = NULL;
 | |
| 	}
 | |
| 	if (state->domain == NULL) {			/* setup NIS */
 | |
| 		switch (yp_get_default_domain(&state->domain)) {
 | |
| 		case 0:
 | |
| 			break;
 | |
| 		case YPERR_RESRC:
 | |
| 			return NS_TRYAGAIN;
 | |
| 		default:
 | |
| 			return NS_UNAVAIL;
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 				/* determine where to get pw_passwd from */
 | |
| 	if (state->maptype == NISMAP_UNKNOWN) {
 | |
| 		int	r, order;
 | |
| 
 | |
| 		state->maptype = NISMAP_NONE;	/* default to no adjunct */
 | |
| 		if (geteuid() != 0)		/* non-root can't use adjunct */
 | |
| 			return NS_SUCCESS;
 | |
| 
 | |
| 						/* look for "master.passwd.*" */
 | |
| 		r = yp_order(state->domain, "master.passwd.byname", &order);
 | |
| 		if (r == 0) {
 | |
| 			state->maptype = NISMAP_MASTER;
 | |
| 			return NS_SUCCESS;
 | |
| 		}
 | |
| 
 | |
| 			/* master.passwd doesn't exist, try passwd.adjunct */
 | |
| 		if (r == YPERR_MAP) {
 | |
| 			r = yp_order(state->domain, "passwd.adjunct.byname",
 | |
| 			    &order);
 | |
| 			if (r == 0)
 | |
| 				state->maptype = NISMAP_ADJUNCT;
 | |
| 		}
 | |
| 	}
 | |
| 	return NS_SUCCESS;
 | |
| }
 | |
| 
 | |
| static int
 | |
| _nis_end(struct nis_state *state)
 | |
| {
 | |
| 
 | |
| 	_DIAGASSERT(state != NULL);
 | |
| 
 | |
| 	if (state->domain)
 | |
| 		state->domain = NULL;
 | |
| 	state->done = 0;
 | |
| 	if (state->current)
 | |
| 		free(state->current);
 | |
| 	state->current = NULL;
 | |
| 	state->maptype = NISMAP_UNKNOWN;
 | |
| 	return NS_SUCCESS;
 | |
| }
 | |
| 
 | |
| /*
 | |
|  * nis_parse
 | |
|  *	wrapper to _pw_parse that obtains the real password from the
 | |
|  *	"passwd.adjunct.byname" NIS map if the maptype is NISMAP_ADJUNCT.
 | |
|  */
 | |
| static int
 | |
| _nis_parse(const char *entry, struct passwd *pw, char *buf, size_t buflen,
 | |
| 	struct nis_state *state)
 | |
| {
 | |
| 	size_t	elen;
 | |
| 
 | |
| 	_DIAGASSERT(entry != NULL);
 | |
| 	_DIAGASSERT(pw != NULL);
 | |
| 	_DIAGASSERT(buf != NULL);
 | |
| 	_DIAGASSERT(state != NULL);
 | |
| 
 | |
| 	elen = strlen(entry);
 | |
| 	if (elen >= buflen)
 | |
| 		return 0;
 | |
| 	if (! _pw_parse(entry, pw, buf, buflen,
 | |
| 	    !(state->maptype == NISMAP_MASTER)))
 | |
| 		return 0;
 | |
| 
 | |
| 	if ((state->maptype == NISMAP_ADJUNCT) &&
 | |
| 	    (strstr(pw->pw_passwd, "##") != NULL)) {
 | |
| 		char	*data;
 | |
| 		int	datalen;
 | |
| 
 | |
| 		if (yp_match(state->domain, "passwd.adjunct.byname",
 | |
| 		    pw->pw_name, (int)strlen(pw->pw_name),
 | |
| 		    &data, &datalen) == 0) {
 | |
| 			char	*bp, *ep;
 | |
| 						/* skip name to get password */
 | |
| 			ep = data;
 | |
| 			if ((bp = strsep(&ep, ":")) != NULL &&
 | |
| 			    (bp = strsep(&ep, ":")) != NULL) {
 | |
| 					/* store new pw_passwd after entry */
 | |
| 				strlcpy(buf + elen, bp, buflen - elen);
 | |
| 				pw->pw_passwd = &buf[elen];
 | |
| 			}
 | |
| 			free(data);
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	return 1;
 | |
| }
 | |
| 
 | |
| 
 | |
| /*
 | |
|  * _nis_pwscan
 | |
|  *	Look for the yp key provided in buffer from map,
 | |
|  *	and decode into pw/buffer/buflen.
 | |
|  */
 | |
| static int
 | |
| _nis_pwscan(int *retval, struct passwd *pw, char *buffer, size_t buflen,
 | |
| 	struct nis_state *state, const char *map)
 | |
| {
 | |
| 	char	*data;
 | |
| 	int	nisr, rv, datalen;
 | |
| 
 | |
| 	_DIAGASSERT(retval != NULL);
 | |
| 	_DIAGASSERT(pw != NULL);
 | |
| 	_DIAGASSERT(buffer != NULL);
 | |
| 	_DIAGASSERT(state != NULL);
 | |
| 	_DIAGASSERT(map != NULL);
 | |
| 
 | |
| 	*retval = 0;
 | |
| 
 | |
| 	if (state->domain == NULL) {	/* only start if NIS not setup */
 | |
| 		rv = _nis_start(state);
 | |
| 		if (rv != NS_SUCCESS)
 | |
| 			return rv;
 | |
| 	}
 | |
| 
 | |
| 	data = NULL;
 | |
| 	rv = NS_NOTFOUND;
 | |
| 
 | |
| 							/* search map */
 | |
| 	nisr = yp_match(state->domain, map, buffer, (int)strlen(buffer),
 | |
| 	    &data, &datalen);
 | |
| 	switch (nisr) {
 | |
| 	case 0:
 | |
| 		data[datalen] = '\0';			/* clear trailing \n */
 | |
| 		if (_nis_parse(data, pw, buffer, buflen, state))
 | |
| 			rv = NS_SUCCESS;		/* validate line */
 | |
| 		else
 | |
| 			rv = NS_UNAVAIL;
 | |
| 		break;
 | |
| 	case YPERR_KEY:
 | |
| 		break;
 | |
| 	default:
 | |
| 		rv = NS_UNAVAIL;
 | |
| 		break;
 | |
| 	}
 | |
| 
 | |
| 	if (rv != NS_SUCCESS && rv != NS_NOTFOUND)
 | |
| 		*retval = errno;
 | |
| 	if (data)
 | |
| 		free(data);
 | |
| 	return rv;
 | |
| }
 | |
| 
 | |
| /*ARGSUSED*/
 | |
| static int
 | |
| _nis_setpwent(void *nsrv, void *nscb, va_list ap)
 | |
| {
 | |
| 
 | |
| 	_nis_state.stayopen = 0;
 | |
| 	return _nis_start(&_nis_state);
 | |
| }
 | |
| 
 | |
| /*ARGSUSED*/
 | |
| static int
 | |
| _nis_setpassent(void *nsrv, void *nscb, va_list ap)
 | |
| {
 | |
| 	int	*retval		= va_arg(ap, int *);
 | |
| 	int	 stayopen	= va_arg(ap, int);
 | |
| 
 | |
| 	int	rv;
 | |
| 
 | |
| 	_nis_state.stayopen = stayopen;
 | |
| 	rv = _nis_start(&_nis_state);
 | |
| 	*retval = (rv == NS_SUCCESS);
 | |
| 	return rv;
 | |
| }
 | |
| 
 | |
| /*ARGSUSED*/
 | |
| static int
 | |
| _nis_endpwent(void *nsrv, void *nscb, va_list ap)
 | |
| {
 | |
| 
 | |
| 	return _nis_end(&_nis_state);
 | |
| }
 | |
| 
 | |
| 
 | |
| /*ARGSUSED*/
 | |
| static int
 | |
| _nis_getpwent(void *nsrv, void *nscb, va_list ap)
 | |
| {
 | |
| 	struct passwd	**retval = va_arg(ap, struct passwd **);
 | |
| 
 | |
| 	char	*key, *data;
 | |
| 	int	keylen, datalen, rv, nisr;
 | |
| 
 | |
| 	_DIAGASSERT(retval != NULL);
 | |
| 
 | |
| 	*retval = NULL;
 | |
| 
 | |
| 	if (_nis_state.done)				/* exhausted search */
 | |
| 		return NS_NOTFOUND;
 | |
| 	if (_nis_state.domain == NULL) {
 | |
| 					/* only start if NIS not setup */
 | |
| 		rv = _nis_start(&_nis_state);
 | |
| 		if (rv != NS_SUCCESS)
 | |
| 			return rv;
 | |
| 	}
 | |
| 
 | |
|  next_nis_entry:
 | |
| 	key = NULL;
 | |
| 	data = NULL;
 | |
| 	rv = NS_NOTFOUND;
 | |
| 
 | |
| 	if (_nis_state.current) {			/* already searching */
 | |
| 		nisr = yp_next(_nis_state.domain, PASSWD_BYNAME(&_nis_state),
 | |
| 		    _nis_state.current, _nis_state.currentlen,
 | |
| 		    &key, &keylen, &data, &datalen);
 | |
| 		free(_nis_state.current);
 | |
| 		_nis_state.current = NULL;
 | |
| 		switch (nisr) {
 | |
| 		case 0:
 | |
| 			_nis_state.current = key;
 | |
| 			_nis_state.currentlen = keylen;
 | |
| 			key = NULL;
 | |
| 			break;
 | |
| 		case YPERR_NOMORE:
 | |
| 			_nis_state.done = 1;
 | |
| 			goto nisent_out;
 | |
| 		default:
 | |
| 			rv = NS_UNAVAIL;
 | |
| 			goto nisent_out;
 | |
| 		}
 | |
| 	} else {					/* new search */
 | |
| 		if (yp_first(_nis_state.domain, PASSWD_BYNAME(&_nis_state),
 | |
| 		    &_nis_state.current, &_nis_state.currentlen,
 | |
| 		    &data, &datalen)) {
 | |
| 			rv = NS_UNAVAIL;
 | |
| 			goto nisent_out;
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	data[datalen] = '\0';				/* clear trailing \n */
 | |
| 							/* validate line */
 | |
| 	if (_nis_parse(data, &_nis_passwd,
 | |
| 	    _nis_passwdbuf, sizeof(_nis_passwdbuf), &_nis_state))
 | |
| 		rv = NS_SUCCESS;
 | |
| 	else {					/* dodgy entry, try again */
 | |
| 		free(data);
 | |
| 		goto next_nis_entry;
 | |
| 	}
 | |
| 
 | |
|  nisent_out:
 | |
| 	if (key)
 | |
| 		free(key);
 | |
| 	if (data)
 | |
| 		free(data);
 | |
| 	if (rv == NS_SUCCESS)
 | |
| 		*retval = &_nis_passwd;
 | |
| 	return rv;
 | |
| }
 | |
| 
 | |
| /*ARGSUSED*/
 | |
| static int
 | |
| _nis_getpwent_r(void *nsrv, void *nscb, va_list ap)
 | |
| {
 | |
| 	int		*retval	= va_arg(ap, int *);
 | |
| 	struct passwd	*pw	= va_arg(ap, struct passwd *);
 | |
| 	char		*buffer	= va_arg(ap, char *);
 | |
| 	size_t		 buflen	= va_arg(ap, size_t);
 | |
| 	struct passwd  **result	= va_arg(ap, struct passwd **);
 | |
| 
 | |
| 	char	*key, *data;
 | |
| 	int	keylen, datalen, rv, nisr;
 | |
| 
 | |
| 	_DIAGASSERT(retval != NULL);
 | |
| 	_DIAGASSERT(pw != NULL);
 | |
| 	_DIAGASSERT(buffer != NULL);
 | |
| 	_DIAGASSERT(result != NULL);
 | |
| 
 | |
| 	*retval = 0;
 | |
| 
 | |
| 	if (_nis_state.done)				/* exhausted search */
 | |
| 		return NS_NOTFOUND;
 | |
| 	if (_nis_state.domain == NULL) {
 | |
| 					/* only start if NIS not setup */
 | |
| 		rv = _nis_start(&_nis_state);
 | |
| 		if (rv != NS_SUCCESS)
 | |
| 			return rv;
 | |
| 	}
 | |
| 
 | |
|  next_nis_entry:
 | |
| 	key = NULL;
 | |
| 	data = NULL;
 | |
| 	rv = NS_NOTFOUND;
 | |
| 
 | |
| 	if (_nis_state.current) {			/* already searching */
 | |
| 		nisr = yp_next(_nis_state.domain, PASSWD_BYNAME(&_nis_state),
 | |
| 		    _nis_state.current, _nis_state.currentlen,
 | |
| 		    &key, &keylen, &data, &datalen);
 | |
| 		free(_nis_state.current);
 | |
| 		_nis_state.current = NULL;
 | |
| 		switch (nisr) {
 | |
| 		case 0:
 | |
| 			_nis_state.current = key;
 | |
| 			_nis_state.currentlen = keylen;
 | |
| 			key = NULL;
 | |
| 			break;
 | |
| 		case YPERR_NOMORE:
 | |
| 			_nis_state.done = 1;
 | |
| 			goto nisent_out;
 | |
| 		default:
 | |
| 			rv = NS_UNAVAIL;
 | |
| 			goto nisent_out;
 | |
| 		}
 | |
| 	} else {					/* new search */
 | |
| 		if (yp_first(_nis_state.domain, PASSWD_BYNAME(&_nis_state),
 | |
| 		    &_nis_state.current, &_nis_state.currentlen,
 | |
| 		    &data, &datalen)) {
 | |
| 			rv = NS_UNAVAIL;
 | |
| 			goto nisent_out;
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	data[datalen] = '\0';				/* clear trailing \n */
 | |
| 							/* validate line */
 | |
| 	if (_nis_parse(data, pw, buffer, buflen, &_nis_state))
 | |
| 		rv = NS_SUCCESS;
 | |
| 	else {					/* dodgy entry, try again */
 | |
| 		if (key)
 | |
| 			free(key);
 | |
| 		free(data);
 | |
| 		goto next_nis_entry;
 | |
| 	}
 | |
| 
 | |
|  nisent_out:
 | |
| 	if (key)
 | |
| 		free(key);
 | |
| 	if (data)
 | |
| 		free(data);
 | |
| 	if (rv == NS_SUCCESS)
 | |
| 		*result = pw;
 | |
| 	else
 | |
| 		*result = NULL;
 | |
| 	return rv;
 | |
| }
 | |
| 
 | |
| /*ARGSUSED*/
 | |
| static int
 | |
| _nis_getpwuid(void *nsrv, void *nscb, va_list ap)
 | |
| {
 | |
| 	struct passwd	**retval = va_arg(ap, struct passwd **);
 | |
| 	uid_t		 uid	= va_arg(ap, uid_t);
 | |
| 
 | |
| 	int	rv, rerror;
 | |
| 
 | |
| 	_DIAGASSERT(retval != NULL);
 | |
| 
 | |
| 	*retval = NULL;
 | |
| 	rv = _nis_start(&_nis_state);
 | |
| 	if (rv != NS_SUCCESS)
 | |
| 		return rv;
 | |
| 	snprintf(_nis_passwdbuf, sizeof(_nis_passwdbuf), "%u", (unsigned int)uid);
 | |
| 	rv = _nis_pwscan(&rerror, &_nis_passwd,
 | |
| 	    _nis_passwdbuf, sizeof(_nis_passwdbuf),
 | |
| 	    &_nis_state, PASSWD_BYUID(&_nis_state));
 | |
| 	if (!_nis_state.stayopen)
 | |
| 		_nis_end(&_nis_state);
 | |
| 	if (rv == NS_SUCCESS && uid == _nis_passwd.pw_uid)
 | |
| 		*retval = &_nis_passwd;
 | |
| 	return rv;
 | |
| }
 | |
| 
 | |
| /*ARGSUSED*/
 | |
| static int
 | |
| _nis_getpwuid_r(void *nsrv, void *nscb, va_list ap)
 | |
| {
 | |
| 	int		*retval	= va_arg(ap, int *);
 | |
| 	uid_t		 uid	= va_arg(ap, uid_t);
 | |
| 	struct passwd	*pw	= va_arg(ap, struct passwd *);
 | |
| 	char		*buffer	= va_arg(ap, char *);
 | |
| 	size_t		 buflen	= va_arg(ap, size_t);
 | |
| 	struct passwd  **result	= va_arg(ap, struct passwd **);
 | |
| 
 | |
| 	struct nis_state state;
 | |
| 	int	rv;
 | |
| 
 | |
| 	_DIAGASSERT(retval != NULL);
 | |
| 	_DIAGASSERT(pw != NULL);
 | |
| 	_DIAGASSERT(buffer != NULL);
 | |
| 	_DIAGASSERT(result != NULL);
 | |
| 
 | |
| 	*result = NULL;
 | |
| 	memset(&state, 0, sizeof(state));
 | |
| 	rv = _nis_start(&state);
 | |
| 	if (rv != NS_SUCCESS)
 | |
| 		return rv;
 | |
| 	snprintf(buffer, buflen, "%u", (unsigned int)uid);
 | |
| 	rv = _nis_pwscan(retval, pw, buffer, buflen,
 | |
| 	    &state, PASSWD_BYUID(&state));
 | |
| 	_nis_end(&state);
 | |
| 	if (rv != NS_SUCCESS)
 | |
| 		return rv;
 | |
| 	if (uid == pw->pw_uid) {
 | |
| 		*result = pw;
 | |
| 		return NS_SUCCESS;
 | |
| 	} else
 | |
| 		return NS_NOTFOUND;
 | |
| }
 | |
| 
 | |
| /*ARGSUSED*/
 | |
| static int
 | |
| _nis_getpwnam(void *nsrv, void *nscb, va_list ap)
 | |
| {
 | |
| 	struct passwd	**retval = va_arg(ap, struct passwd **);
 | |
| 	const char	*name	= va_arg(ap, const char *);
 | |
| 
 | |
| 	int	rv, rerror;
 | |
| 
 | |
| 	_DIAGASSERT(retval != NULL);
 | |
| 
 | |
| 	*retval = NULL;
 | |
| 	rv = _nis_start(&_nis_state);
 | |
| 	if (rv != NS_SUCCESS)
 | |
| 		return rv;
 | |
| 	snprintf(_nis_passwdbuf, sizeof(_nis_passwdbuf), "%s", name);
 | |
| 	rv = _nis_pwscan(&rerror, &_nis_passwd,
 | |
| 	    _nis_passwdbuf, sizeof(_nis_passwdbuf),
 | |
| 	    &_nis_state, PASSWD_BYNAME(&_nis_state));
 | |
| 	if (!_nis_state.stayopen)
 | |
| 		_nis_end(&_nis_state);
 | |
| 	if (rv == NS_SUCCESS && strcmp(name, _nis_passwd.pw_name) == 0)
 | |
| 		*retval = &_nis_passwd;
 | |
| 	return rv;
 | |
| }
 | |
| 
 | |
| /*ARGSUSED*/
 | |
| static int
 | |
| _nis_getpwnam_r(void *nsrv, void *nscb, va_list ap)
 | |
| {
 | |
| 	int		*retval	= va_arg(ap, int *);
 | |
| 	const char	*name	= va_arg(ap, const char *);
 | |
| 	struct passwd	*pw	= va_arg(ap, struct passwd *);
 | |
| 	char		*buffer	= va_arg(ap, char *);
 | |
| 	size_t		 buflen	= va_arg(ap, size_t);
 | |
| 	struct passwd  **result	= va_arg(ap, struct passwd **);
 | |
| 
 | |
| 	struct nis_state state;
 | |
| 	int	rv;
 | |
| 
 | |
| 	_DIAGASSERT(retval != NULL);
 | |
| 	_DIAGASSERT(pw != NULL);
 | |
| 	_DIAGASSERT(buffer != NULL);
 | |
| 	_DIAGASSERT(result != NULL);
 | |
| 
 | |
| 	*result = NULL;
 | |
| 	snprintf(buffer, buflen, "%s", name);
 | |
| 	memset(&state, 0, sizeof(state));
 | |
| 	rv = _nis_start(&state);
 | |
| 	if (rv != NS_SUCCESS)
 | |
| 		return rv;
 | |
| 	rv = _nis_pwscan(retval, pw, buffer, buflen,
 | |
| 	    &state, PASSWD_BYNAME(&state));
 | |
| 	_nis_end(&state);
 | |
| 	if (rv != NS_SUCCESS)
 | |
| 		return rv;
 | |
| 	if (strcmp(name, pw->pw_name) == 0) {
 | |
| 		*result = pw;
 | |
| 		return NS_SUCCESS;
 | |
| 	} else
 | |
| 		return NS_NOTFOUND;
 | |
| }
 | |
| 
 | |
| #endif /* YP */
 | |
| 
 | |
| 
 | |
| #ifdef _PASSWD_COMPAT
 | |
| 		/*
 | |
| 		 *	compat methods
 | |
| 		 */
 | |
| 
 | |
| 	/* state shared between compat methods */
 | |
| 
 | |
| struct compat_state {
 | |
| 	int		 stayopen;	/* see getpassent(3) */
 | |
| 	DB		*db;		/* passwd DB */
 | |
| 	int		 keynum;	/* key counter, -1 if no more */
 | |
| 	enum {				/* current compat mode */
 | |
| 		COMPAT_NOTOKEN = 0,	/*  no compat token present */
 | |
| 		COMPAT_NONE,		/*  parsing normal pwd.db line */
 | |
| 		COMPAT_FULL,		/*  parsing `+' entries */
 | |
| 		COMPAT_USER,		/*  parsing `+name' entries */
 | |
| 		COMPAT_NETGROUP		/*  parsing `+@netgroup' entries */
 | |
| 	}		 mode;
 | |
| 	char		*user;		/* COMPAT_USER "+name" */
 | |
| 	DB		*exclude;	/* compat exclude DB */
 | |
| 	struct passwd	 proto;		/* proto passwd entry */
 | |
| 	char		 protobuf[_GETPW_R_SIZE_MAX];
 | |
| 					/* buffer for proto ptrs */
 | |
| 	int		 protoflags;	/* proto passwd flags */
 | |
| 	int		 version;
 | |
| };
 | |
| 
 | |
| static struct compat_state	_compat_state;
 | |
| 					/* storage for non _r functions */
 | |
| static struct passwd		_compat_passwd;
 | |
| static char			_compat_passwdbuf[_GETPW_R_SIZE_MAX];
 | |
| 
 | |
| static int
 | |
| _compat_start(struct compat_state *state)
 | |
| {
 | |
| 	int	rv;
 | |
| 
 | |
| 	_DIAGASSERT(state != NULL);
 | |
| 
 | |
| 	state->keynum = 0;
 | |
| 	if (state->db == NULL) {		/* not open yet */
 | |
| 		DBT	key, data;
 | |
| 		DBT	pkey, pdata;
 | |
| 		char	bf[MAXLOGNAME];
 | |
| 
 | |
| 		rv = _pw_opendb(&state->db, &state->version);
 | |
| 		if (rv != NS_SUCCESS)
 | |
| 			return rv;
 | |
| 
 | |
| 		state->mode = COMPAT_NOTOKEN;
 | |
| 
 | |
| 		/*
 | |
| 		 *	Determine if the "compat" token is present in pwd.db;
 | |
| 		 *	either "__YP!" or PW_KEYBYNAME+"+".
 | |
| 		 *	Only works if pwd_mkdb installs the token.
 | |
| 		 */
 | |
| 		key.data = (u_char *)__UNCONST(__yp_token);
 | |
| 		key.size = strlen(__yp_token);
 | |
| 
 | |
| 		bf[0] = _PW_KEYBYNAME;	 /* Pre-token database support. */
 | |
| 		bf[1] = '+';
 | |
| 		pkey.data = (u_char *)bf;
 | |
| 		pkey.size = 2;
 | |
| 
 | |
| 		if ((state->db->get)(state->db, &key, &data, 0) == 0
 | |
| 		    || (state->db->get)(state->db, &pkey, &pdata, 0) == 0)
 | |
| 			state->mode = COMPAT_NONE;
 | |
| 	}
 | |
| 	return NS_SUCCESS;
 | |
| }
 | |
| 
 | |
| static int
 | |
| _compat_end(struct compat_state *state)
 | |
| {
 | |
| 
 | |
| 	_DIAGASSERT(state != NULL);
 | |
| 
 | |
| 	state->keynum = 0;
 | |
| 	if (state->db) {
 | |
| 		(void)(state->db->close)(state->db);
 | |
| 		state->db = NULL;
 | |
| 	}
 | |
| 	state->mode = COMPAT_NOTOKEN;
 | |
| 	if (state->user)
 | |
| 		free(state->user);
 | |
| 	state->user = NULL;
 | |
| 	if (state->exclude != NULL)
 | |
| 		(void)(state->exclude->close)(state->exclude);
 | |
| 	state->exclude = NULL;
 | |
| 	state->proto.pw_name = NULL;
 | |
| 	state->protoflags = 0;
 | |
| 	return NS_SUCCESS;
 | |
| }
 | |
| 
 | |
| /*
 | |
|  * _compat_add_exclude
 | |
|  *	add the name to the exclude list in state->exclude.
 | |
|  */
 | |
| static int
 | |
| _compat_add_exclude(struct compat_state *state, const char *name)
 | |
| {
 | |
| 	DBT	key, data;
 | |
| 
 | |
| 	_DIAGASSERT(state != NULL);
 | |
| 	_DIAGASSERT(name != NULL);
 | |
| 
 | |
| 				/* initialize the exclusion table if needed */
 | |
| 	if (state->exclude == NULL) {
 | |
| 		state->exclude = dbopen(NULL, O_RDWR, 600, DB_HASH, NULL);
 | |
| 		if (state->exclude == NULL)
 | |
| 			return 0;
 | |
| 	}
 | |
| 
 | |
| 	key.size = strlen(name);			/* set up the key */
 | |
| 	key.data = (u_char *)__UNCONST(name);
 | |
| 
 | |
| 	data.data = NULL;				/* data is nothing */
 | |
| 	data.size = 0;
 | |
| 
 | |
| 							/* store it */
 | |
| 	if ((state->exclude->put)(state->exclude, &key, &data, 0) == -1)
 | |
| 		return 0;
 | |
| 
 | |
| 	return 1;
 | |
| }
 | |
| 
 | |
| /*
 | |
|  * _compat_is_excluded
 | |
|  *	test if a name is on the compat mode exclude list
 | |
|  */
 | |
| static int
 | |
| _compat_is_excluded(struct compat_state *state, const char *name)
 | |
| {
 | |
| 	DBT	key, data;
 | |
| 
 | |
| 	_DIAGASSERT(state != NULL);
 | |
| 	_DIAGASSERT(name != NULL);
 | |
| 
 | |
| 	if (state->exclude == NULL)
 | |
| 		return 0;	/* nothing excluded */
 | |
| 
 | |
| 	key.size = strlen(name);			/* set up the key */
 | |
| 	key.data = (u_char *)__UNCONST(name);
 | |
| 
 | |
| 	if ((state->exclude->get)(state->exclude, &key, &data, 0) == 0)
 | |
| 		return 1;				/* is excluded */
 | |
| 
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| 
 | |
| /*
 | |
|  * _passwdcompat_bad
 | |
|  *	log an error if "files" or "compat" is specified in
 | |
|  *	passwd_compat database
 | |
|  */
 | |
| /*ARGSUSED*/
 | |
| static int
 | |
| _passwdcompat_bad(void *nsrv, void *nscb, va_list ap)
 | |
| {
 | |
| 	static int warned;
 | |
| 
 | |
| 	_DIAGASSERT(nsrv != NULL);
 | |
| 	_DIAGASSERT(nscb != NULL);
 | |
| 
 | |
| 	if (!warned) {
 | |
| 		syslog(LOG_ERR,
 | |
| 			"nsswitch.conf passwd_compat database can't use '%s'",
 | |
| 			(char *)nscb);
 | |
| 	}
 | |
| 	warned = 1;
 | |
| 	return NS_UNAVAIL;
 | |
| }
 | |
| 
 | |
| /*
 | |
|  * _passwdcompat_setpassent
 | |
|  *	Call setpassent for all passwd_compat sources.
 | |
|  */
 | |
| static int
 | |
| _passwdcompat_setpassent(int stayopen)
 | |
| {
 | |
| 	static const ns_dtab dtab[] = {
 | |
| 		NS_FILES_CB(_passwdcompat_bad, "files")
 | |
| 		NS_DNS_CB(_dns_setpassent, NULL)
 | |
| 		NS_NIS_CB(_nis_setpassent, NULL)
 | |
| 		NS_COMPAT_CB(_passwdcompat_bad, "compat")
 | |
| 		NS_NULL_CB
 | |
| 	};
 | |
| 
 | |
| 	int	rv, result;
 | |
| 
 | |
| 	rv = nsdispatch(NULL, dtab, NSDB_PASSWD_COMPAT, "setpassent",
 | |
| 	    __nsdefaultnis_forceall, &result, stayopen);
 | |
| 	return rv;
 | |
| }
 | |
| 
 | |
| /*
 | |
|  * _passwdcompat_endpwent
 | |
|  *	Call endpwent for all passwd_compat sources.
 | |
|  */
 | |
| static int
 | |
| _passwdcompat_endpwent(void)
 | |
| {
 | |
| 	static const ns_dtab dtab[] = {
 | |
| 		NS_FILES_CB(_passwdcompat_bad, "files")
 | |
| 		NS_DNS_CB(_dns_endpwent, NULL)
 | |
| 		NS_NIS_CB(_nis_endpwent, NULL)
 | |
| 		NS_COMPAT_CB(_passwdcompat_bad, "compat")
 | |
| 		NS_NULL_CB
 | |
| 	};
 | |
| 
 | |
| 	return nsdispatch(NULL, dtab, NSDB_PASSWD_COMPAT, "endpwent",
 | |
| 	    __nsdefaultnis_forceall);
 | |
| }
 | |
| 
 | |
| /*
 | |
|  * _passwdcompat_pwscan
 | |
|  *	When a name lookup in compat mode is required (e.g., `+name', or a
 | |
|  *	name in `+@netgroup'), look it up in the 'passwd_compat' nsswitch
 | |
|  *	database.
 | |
|  *	Fail if passwd_compat contains files or compat.
 | |
|  */
 | |
| static int
 | |
| _passwdcompat_pwscan(struct passwd *pw, char *buffer, size_t buflen,
 | |
| 	int search, const char *name, uid_t uid)
 | |
| {
 | |
| 	static const ns_dtab compatentdtab[] = {
 | |
| 		NS_FILES_CB(_passwdcompat_bad, "files")
 | |
| 		NS_DNS_CB(_dns_getpwent_r, NULL)
 | |
| 		NS_NIS_CB(_nis_getpwent_r, NULL)
 | |
| 		NS_COMPAT_CB(_passwdcompat_bad, "compat")
 | |
| 		NS_NULL_CB
 | |
| 	};
 | |
| 	static const ns_dtab compatuiddtab[] = {
 | |
| 		NS_FILES_CB(_passwdcompat_bad, "files")
 | |
| 		NS_DNS_CB(_dns_getpwuid_r, NULL)
 | |
| 		NS_NIS_CB(_nis_getpwuid_r, NULL)
 | |
| 		NS_COMPAT_CB(_passwdcompat_bad, "compat")
 | |
| 		NS_NULL_CB
 | |
| 	};
 | |
| 	static const ns_dtab compatnamdtab[] = {
 | |
| 		NS_FILES_CB(_passwdcompat_bad, "files")
 | |
| 		NS_DNS_CB(_dns_getpwnam_r, NULL)
 | |
| 		NS_NIS_CB(_nis_getpwnam_r, NULL)
 | |
| 		NS_COMPAT_CB(_passwdcompat_bad, "compat")
 | |
| 		NS_NULL_CB
 | |
| 	};
 | |
| 
 | |
| 	int		rv, crv;
 | |
| 	struct passwd	*cpw;
 | |
| 
 | |
| 	switch (search) {
 | |
| 	case _PW_KEYBYNUM:
 | |
| 		rv = nsdispatch(NULL, compatentdtab,
 | |
| 		    NSDB_PASSWD_COMPAT, "getpwent_r", __nsdefaultnis,
 | |
| 		    &crv, pw, buffer, buflen, &cpw);
 | |
| 		break;
 | |
| 	case _PW_KEYBYNAME:
 | |
| 		_DIAGASSERT(name != NULL);
 | |
| 		rv = nsdispatch(NULL, compatnamdtab,
 | |
| 		    NSDB_PASSWD_COMPAT, "getpwnam_r", __nsdefaultnis,
 | |
| 		    &crv, name, pw, buffer, buflen, &cpw);
 | |
| 		break;
 | |
| 	case _PW_KEYBYUID:
 | |
| 		rv = nsdispatch(NULL, compatuiddtab,
 | |
| 		    NSDB_PASSWD_COMPAT, "getpwuid_r", __nsdefaultnis,
 | |
| 		    &crv, uid, pw, buffer, buflen, &cpw);
 | |
| 		break;
 | |
| 	default:
 | |
| 		abort();
 | |
| 		/*NOTREACHED*/
 | |
| 	}
 | |
| 	return rv;
 | |
| }
 | |
| 
 | |
| /*
 | |
|  * _compat_pwscan
 | |
|  *	Search state->db for the next desired entry.
 | |
|  *	If search is _PW_KEYBYNUM, look for state->keynum.
 | |
|  *	If search is _PW_KEYBYNAME, look for name.
 | |
|  *	If search is _PW_KEYBYUID, look for uid.
 | |
|  *	Sets *retval to the errno if the result is not NS_SUCCESS
 | |
|  *	or NS_NOTFOUND.
 | |
|  */
 | |
| static int
 | |
| _compat_pwscan(int *retval, struct passwd *pw, char *buffer, size_t buflen,
 | |
| 	struct compat_state *state, int search, const char *name, uid_t uid)
 | |
| {
 | |
| 	DBT		 key;
 | |
| 	int		 rv, r, pwflags;
 | |
| 	const char	*user, *host, *dom;
 | |
| 	const void	*from;
 | |
| 	size_t		 fromlen;
 | |
| 
 | |
| 	_DIAGASSERT(retval != NULL);
 | |
| 	_DIAGASSERT(pw != NULL);
 | |
| 	_DIAGASSERT(buffer != NULL);
 | |
| 	_DIAGASSERT(state != NULL);
 | |
| 	/* name may be NULL */
 | |
| 
 | |
| 	*retval = 0;
 | |
| 
 | |
| 	if (state->db == NULL) {
 | |
| 		rv = _compat_start(state);
 | |
| 		if (rv != NS_SUCCESS)
 | |
| 			return rv;
 | |
| 	}
 | |
| 	if (buflen <= 1) {			/* buffer too small */
 | |
| 		*retval = ERANGE;
 | |
| 		return NS_UNAVAIL;
 | |
| 	}
 | |
| 
 | |
| 	for (;;) {				/* loop over pwd.db */
 | |
| 		rv = NS_NOTFOUND;
 | |
| 		if (state->mode != COMPAT_NOTOKEN &&
 | |
| 		    state->mode != COMPAT_NONE) {
 | |
| 						/* doing a compat lookup */
 | |
| 			struct passwd	cpw;
 | |
| 			char		cbuf[_GETPW_R_SIZE_MAX];
 | |
| 
 | |
| 			switch (state->mode) {
 | |
| 
 | |
| 			case COMPAT_FULL:
 | |
| 					/* get next user or lookup by key */
 | |
| 				rv = _passwdcompat_pwscan(&cpw,
 | |
| 				    cbuf, sizeof(cbuf), search, name, uid);
 | |
| 				if (rv != NS_SUCCESS)
 | |
| 					state->mode = COMPAT_NONE;
 | |
| 				break;
 | |
| 
 | |
| 			case COMPAT_NETGROUP:
 | |
| /* XXXREENTRANT: getnetgrent is not thread safe */
 | |
| 					/* get next user from netgroup */
 | |
| 				r = getnetgrent(&host, &user, &dom);
 | |
| 				if (r == 0) {	/* end of group */
 | |
| 					endnetgrent();
 | |
| 					state->mode = COMPAT_NONE;
 | |
| 					break;
 | |
| 				}
 | |
| 				if (!user || !*user)
 | |
| 					break;
 | |
| 				rv = _passwdcompat_pwscan(&cpw,
 | |
| 				    cbuf, sizeof(cbuf),
 | |
| 				    _PW_KEYBYNAME, user, 0);
 | |
| 				break;
 | |
| 
 | |
| 			case COMPAT_USER:
 | |
| 					/* get specific user */
 | |
| 				if (state->user == NULL) {
 | |
| 					state->mode = COMPAT_NONE;
 | |
| 					break;
 | |
| 				}
 | |
| 				rv = _passwdcompat_pwscan(&cpw,
 | |
| 				    cbuf, sizeof(cbuf),
 | |
| 				    _PW_KEYBYNAME, state->user, 0);
 | |
| 				free(state->user);
 | |
| 				state->user = NULL;
 | |
| 				state->mode = COMPAT_NONE;
 | |
| 				break;
 | |
| 
 | |
| 			case COMPAT_NOTOKEN:
 | |
| 			case COMPAT_NONE:
 | |
| 				abort();
 | |
| 
 | |
| 			}
 | |
| 			if (rv != NS_SUCCESS)	/* if not matched, next loop */
 | |
| 				continue;
 | |
| 
 | |
| 				/* copy cpw to pw, applying prototype */
 | |
| 			if (! _pw_copy(&cpw, pw, buffer, buflen,
 | |
| 			    &state->proto, state->protoflags)) {
 | |
| 				rv = NS_UNAVAIL;
 | |
| 				break;
 | |
| 			}
 | |
| 
 | |
| 			if (_compat_is_excluded(state, pw->pw_name))
 | |
| 				continue;	/* excluded; next loop */
 | |
| 
 | |
| 			if ((search == _PW_KEYBYNAME
 | |
| 					&& strcmp(pw->pw_name, name) != 0)
 | |
| 			    || (search == _PW_KEYBYUID && pw->pw_uid != uid)) {
 | |
| 				continue;	/* not specific; next loop */
 | |
| 			}
 | |
| 
 | |
| 			break;			/* exit loop if found */
 | |
| 		} else {			/* not a compat line */
 | |
| 			state->proto.pw_name = NULL;
 | |
| 						/* clear prototype */
 | |
| 		}
 | |
| 
 | |
| 		if (state->mode == COMPAT_NOTOKEN) {
 | |
| 				/* no compat token; do direct lookup */
 | |
| 			switch (search) {
 | |
| 			case _PW_KEYBYNUM:
 | |
| 				if (state->keynum == -1)  /* no more records */
 | |
| 					return NS_NOTFOUND;
 | |
| 				state->keynum++;
 | |
| 				from = &state->keynum;
 | |
| 				fromlen = sizeof(state->keynum);
 | |
| 				break;
 | |
| 			case _PW_KEYBYNAME:
 | |
| 				from = name;
 | |
| 				fromlen = strlen(name);
 | |
| 				break;
 | |
| 			case _PW_KEYBYUID:
 | |
| 				from = &uid;
 | |
| 				fromlen = sizeof(uid);
 | |
| 				break;
 | |
| 			default:
 | |
| 				abort();
 | |
| 			}
 | |
| 			buffer[0] = search;
 | |
| 		} else {
 | |
| 				/* compat token; do line by line */
 | |
| 			if (state->keynum == -1)  /* no more records */
 | |
| 				return NS_NOTFOUND;
 | |
| 			state->keynum++;
 | |
| 			from = &state->keynum;
 | |
| 			fromlen = sizeof(state->keynum);
 | |
| 			buffer[0] = _PW_KEYBYNUM;
 | |
| 		}
 | |
| 
 | |
| 		if (buflen <= fromlen) {		/* buffer too small */
 | |
| 			*retval = ERANGE;
 | |
| 			return NS_UNAVAIL;
 | |
| 		}
 | |
| 		memmove(buffer + 1, from, fromlen);	/* setup key */
 | |
| 		key.size = fromlen + 1;
 | |
| 		key.data = (u_char *)buffer;
 | |
| 
 | |
| 		rv = _pw_getkey(state->db, &key, pw, buffer, buflen, &pwflags,
 | |
| 		    state->version);
 | |
| 		if (rv != NS_SUCCESS)		/* stop on error */
 | |
| 			break;
 | |
| 
 | |
| 		if (state->mode == COMPAT_NOTOKEN)
 | |
| 			break;			/* stop if no compat token */
 | |
| 
 | |
| 		if (pw->pw_name[0] == '+') {
 | |
| 						/* compat inclusion */
 | |
| 			switch(pw->pw_name[1]) {
 | |
| 			case '\0':		/* `+' */
 | |
| 				state->mode = COMPAT_FULL;
 | |
| 						/* reset passwd_compat search */
 | |
| /* XXXREENTRANT: setpassent is not thread safe ? */
 | |
| 				(void) _passwdcompat_setpassent(0);
 | |
| 				break;
 | |
| 			case '@':		/* `+@netgroup' */
 | |
| 				state->mode = COMPAT_NETGROUP;
 | |
| 						/* reset netgroup search */
 | |
| /* XXXREENTRANT: setnetgrent is not thread safe */
 | |
| 				setnetgrent(pw->pw_name + 2);
 | |
| 				break;
 | |
| 			default:		/* `+name' */
 | |
| 				state->mode = COMPAT_USER;
 | |
| 				if (state->user)
 | |
| 					free(state->user);
 | |
| 				state->user = strdup(pw->pw_name + 1);
 | |
| 				break;
 | |
| 			}
 | |
| 						/* save the prototype */
 | |
| 			state->protoflags = pwflags;
 | |
| 			if (! _pw_copy(pw, &state->proto, state->protobuf,
 | |
| 			    sizeof(state->protobuf), NULL, 0)) {
 | |
| 				rv = NS_UNAVAIL;
 | |
| 				break;
 | |
| 			}
 | |
| 			continue;		/* loop again after inclusion */
 | |
| 		} else if (pw->pw_name[0] == '-') {
 | |
| 						/* compat exclusion */
 | |
| 			rv = NS_SUCCESS;
 | |
| 			switch(pw->pw_name[1]) {
 | |
| 			case '\0':		/* `-' */
 | |
| 				break;
 | |
| 			case '@':		/* `-@netgroup' */
 | |
| /* XXXREENTRANT: {set,get,end}netgrent is not thread safe */
 | |
| 				setnetgrent(pw->pw_name + 2);
 | |
| 				while (getnetgrent(&host, &user, &dom)) {
 | |
| 					if (!user || !*user)
 | |
| 						continue;
 | |
| 					if (! _compat_add_exclude(state,user)) {
 | |
| 						rv = NS_UNAVAIL;
 | |
| 						break;
 | |
| 					}
 | |
| 				}
 | |
| 				endnetgrent();
 | |
| 				break;
 | |
| 			default:		/* `-name' */
 | |
| 				if (! _compat_add_exclude(state,
 | |
| 				    pw->pw_name + 1)) {
 | |
| 					rv = NS_UNAVAIL;
 | |
| 				}
 | |
| 				break;
 | |
| 			}
 | |
| 			if (rv != NS_SUCCESS)	/* exclusion failure */
 | |
| 				break;
 | |
| 			continue;		/* loop again after exclusion */
 | |
| 		}
 | |
| 		if (search == _PW_KEYBYNUM ||
 | |
| 		    (search == _PW_KEYBYUID && pw->pw_uid == uid) ||
 | |
| 		    (search == _PW_KEYBYNAME && strcmp(pw->pw_name, name) == 0))
 | |
| 			break;			/* token mode match found */
 | |
| 	}
 | |
| 
 | |
| 	if (rv == NS_NOTFOUND &&
 | |
| 	    (search == _PW_KEYBYNUM || state->mode != COMPAT_NOTOKEN))
 | |
| 		state->keynum = -1;		/* flag `no more records' */
 | |
| 
 | |
| 	if (rv == NS_SUCCESS) {
 | |
| 		if ((search == _PW_KEYBYNAME && strcmp(pw->pw_name, name) != 0)
 | |
| 		    || (search == _PW_KEYBYUID && pw->pw_uid != uid))
 | |
| 			rv = NS_NOTFOUND;
 | |
| 	}
 | |
| 
 | |
| 	if (rv != NS_SUCCESS && rv != NS_NOTFOUND)
 | |
| 		*retval = errno;
 | |
| 	return rv;
 | |
| }
 | |
| 
 | |
| /*ARGSUSED*/
 | |
| static int
 | |
| _compat_setpwent(void *nsrv, void *nscb, va_list ap)
 | |
| {
 | |
| 
 | |
| 					/* force passwd_compat setpwent() */
 | |
| 	(void) _passwdcompat_setpassent(0);
 | |
| 
 | |
| 					/* reset state, keep db open */
 | |
| 	_compat_state.stayopen = 0;
 | |
| 	return _compat_start(&_compat_state);
 | |
| }
 | |
| 
 | |
| /*ARGSUSED*/
 | |
| static int
 | |
| _compat_setpassent(void *nsrv, void *nscb, va_list ap)
 | |
| {
 | |
| 	int	*retval		= va_arg(ap, int *);
 | |
| 	int	 stayopen	= va_arg(ap, int);
 | |
| 
 | |
| 	int	rv;
 | |
| 
 | |
| 					/* force passwd_compat setpassent() */
 | |
| 	(void) _passwdcompat_setpassent(stayopen);
 | |
| 
 | |
| 	_compat_state.stayopen = stayopen;
 | |
| 	rv = _compat_start(&_compat_state);
 | |
| 	*retval = (rv == NS_SUCCESS);
 | |
| 	return rv;
 | |
| }
 | |
| 
 | |
| /*ARGSUSED*/
 | |
| static int
 | |
| _compat_endpwent(void *nsrv, void *nscb, va_list ap)
 | |
| {
 | |
| 
 | |
| 					/* force passwd_compat endpwent() */
 | |
| 	(void) _passwdcompat_endpwent();
 | |
| 
 | |
| 					/* reset state, close db */
 | |
| 	_compat_state.stayopen = 0;
 | |
| 	return _compat_end(&_compat_state);
 | |
| }
 | |
| 
 | |
| 
 | |
| /*ARGSUSED*/
 | |
| static int
 | |
| _compat_getpwent(void *nsrv, void *nscb, va_list ap)
 | |
| {
 | |
| 	struct passwd	**retval = va_arg(ap, struct passwd **);
 | |
| 
 | |
| 	int	rv, rerror;
 | |
| 
 | |
| 	_DIAGASSERT(retval != NULL);
 | |
| 
 | |
| 	*retval = NULL;
 | |
| 	rv = _compat_pwscan(&rerror, &_compat_passwd,
 | |
| 	    _compat_passwdbuf, sizeof(_compat_passwdbuf),
 | |
| 	    &_compat_state, _PW_KEYBYNUM, NULL, 0);
 | |
| 	if (rv == NS_SUCCESS)
 | |
| 		*retval = &_compat_passwd;
 | |
| 	return rv;
 | |
| }
 | |
| 
 | |
| /*ARGSUSED*/
 | |
| static int
 | |
| _compat_getpwent_r(void *nsrv, void *nscb, va_list ap)
 | |
| {
 | |
| 	int		*retval	= va_arg(ap, int *);
 | |
| 	struct passwd	*pw	= va_arg(ap, struct passwd *);
 | |
| 	char		*buffer	= va_arg(ap, char *);
 | |
| 	size_t		 buflen	= va_arg(ap, size_t);
 | |
| 	struct passwd  **result	= va_arg(ap, struct passwd **);
 | |
| 
 | |
| 	int		rv;
 | |
| 
 | |
| 	_DIAGASSERT(retval != NULL);
 | |
| 	_DIAGASSERT(pw != NULL);
 | |
| 	_DIAGASSERT(buffer != NULL);
 | |
| 	_DIAGASSERT(result != NULL);
 | |
| 
 | |
| 	rv = _compat_pwscan(retval, pw, buffer, buflen, &_compat_state,
 | |
| 	    _PW_KEYBYNUM, NULL, 0);
 | |
| 	if (rv == NS_SUCCESS)
 | |
| 		*result = pw;
 | |
| 	else
 | |
| 		*result = NULL;
 | |
| 	return rv;
 | |
| }
 | |
| 
 | |
| 
 | |
| /*ARGSUSED*/
 | |
| static int
 | |
| _compat_getpwnam(void *nsrv, void *nscb, va_list ap)
 | |
| {
 | |
| 	struct passwd	**retval = va_arg(ap, struct passwd **);
 | |
| 	const char	*name	= va_arg(ap, const char *);
 | |
| 
 | |
| 	int	rv, rerror;
 | |
| 
 | |
| 	_DIAGASSERT(retval != NULL);
 | |
| 
 | |
| 	*retval = NULL;
 | |
| 	rv = _compat_start(&_compat_state);
 | |
| 	if (rv != NS_SUCCESS)
 | |
| 		return rv;
 | |
| 	rv = _compat_pwscan(&rerror, &_compat_passwd,
 | |
| 	    _compat_passwdbuf, sizeof(_compat_passwdbuf),
 | |
| 	    &_compat_state, _PW_KEYBYNAME, name, 0);
 | |
| 	if (!_compat_state.stayopen)
 | |
| 		_compat_end(&_compat_state);
 | |
| 	if (rv == NS_SUCCESS)
 | |
| 		*retval = &_compat_passwd;
 | |
| 	return rv;
 | |
| }
 | |
| 
 | |
| /*ARGSUSED*/
 | |
| static int
 | |
| _compat_getpwnam_r(void *nsrv, void *nscb, va_list ap)
 | |
| {
 | |
| 	int		*retval	= va_arg(ap, int *);
 | |
| 	const char	*name	= va_arg(ap, const char *);
 | |
| 	struct passwd	*pw	= va_arg(ap, struct passwd *);
 | |
| 	char		*buffer	= va_arg(ap, char *);
 | |
| 	size_t		 buflen	= va_arg(ap, size_t);
 | |
| 	struct passwd  **result	= va_arg(ap, struct passwd **);
 | |
| 
 | |
| 	struct compat_state	state;
 | |
| 	int		rv;
 | |
| 
 | |
| 	_DIAGASSERT(retval != NULL);
 | |
| 	_DIAGASSERT(pw != NULL);
 | |
| 	_DIAGASSERT(buffer != NULL);
 | |
| 	_DIAGASSERT(result != NULL);
 | |
| 
 | |
| 	*result = NULL;
 | |
| 	memset(&state, 0, sizeof(state));
 | |
| 	rv = _compat_pwscan(retval, pw, buffer, buflen, &state,
 | |
| 	    _PW_KEYBYNAME, name, 0);
 | |
| 	_compat_end(&state);
 | |
| 	if (rv == NS_SUCCESS)
 | |
| 		*result = pw;
 | |
| 	return rv;
 | |
| }
 | |
| 
 | |
| /*ARGSUSED*/
 | |
| static int
 | |
| _compat_getpwuid(void *nsrv, void *nscb, va_list ap)
 | |
| {
 | |
| 	struct passwd	**retval = va_arg(ap, struct passwd **);
 | |
| 	uid_t		 uid	= va_arg(ap, uid_t);
 | |
| 
 | |
| 	int	rv, rerror;
 | |
| 
 | |
| 	_DIAGASSERT(retval != NULL);
 | |
| 
 | |
| 	*retval = NULL;
 | |
| 	rv = _compat_start(&_compat_state);
 | |
| 	if (rv != NS_SUCCESS)
 | |
| 		return rv;
 | |
| 	rv = _compat_pwscan(&rerror, &_compat_passwd,
 | |
| 	    _compat_passwdbuf, sizeof(_compat_passwdbuf),
 | |
| 	    &_compat_state, _PW_KEYBYUID, NULL, uid);
 | |
| 	if (!_compat_state.stayopen)
 | |
| 		_compat_end(&_compat_state);
 | |
| 	if (rv == NS_SUCCESS)
 | |
| 		*retval = &_compat_passwd;
 | |
| 	return rv;
 | |
| }
 | |
| 
 | |
| /*ARGSUSED*/
 | |
| static int
 | |
| _compat_getpwuid_r(void *nsrv, void *nscb, va_list ap)
 | |
| {
 | |
| 	int		*retval	= va_arg(ap, int *);
 | |
| 	uid_t		 uid	= va_arg(ap, uid_t);
 | |
| 	struct passwd	*pw	= va_arg(ap, struct passwd *);
 | |
| 	char		*buffer	= va_arg(ap, char *);
 | |
| 	size_t		 buflen	= va_arg(ap, size_t);
 | |
| 	struct passwd  **result	= va_arg(ap, struct passwd **);
 | |
| 
 | |
| 	struct compat_state	state;
 | |
| 	int		rv;
 | |
| 
 | |
| 	_DIAGASSERT(retval != NULL);
 | |
| 	_DIAGASSERT(pw != NULL);
 | |
| 	_DIAGASSERT(buffer != NULL);
 | |
| 	_DIAGASSERT(result != NULL);
 | |
| 
 | |
| 	*result = NULL;
 | |
| 	memset(&state, 0, sizeof(state));
 | |
| 	rv = _compat_pwscan(retval, pw, buffer, buflen, &state,
 | |
| 	    _PW_KEYBYUID, NULL, uid);
 | |
| 	_compat_end(&state);
 | |
| 	if (rv == NS_SUCCESS)
 | |
| 		*result = pw;
 | |
| 	return rv;
 | |
| }
 | |
| 
 | |
| #endif /* _PASSWD_COMPAT */
 | |
| 
 | |
| 
 | |
| 		/*
 | |
| 		 *	public functions
 | |
| 		 */
 | |
| 
 | |
| struct passwd *
 | |
| getpwent(void)
 | |
| {
 | |
| 	int		r;
 | |
| 	struct passwd	*retval;
 | |
| 
 | |
| 	static const ns_dtab dtab[] = {
 | |
| 		NS_FILES_CB(_files_getpwent, NULL)
 | |
| 		NS_DNS_CB(_dns_getpwent, NULL)
 | |
| 		NS_NIS_CB(_nis_getpwent, NULL)
 | |
| 		NS_COMPAT_CB(_compat_getpwent, NULL)
 | |
| 		NS_NULL_CB
 | |
| 	};
 | |
| 
 | |
| 	mutex_lock(&_pwmutex);
 | |
| 	r = nsdispatch(NULL, dtab, NSDB_PASSWD, "getpwent", __nsdefaultcompat,
 | |
| 	    &retval);
 | |
| 	mutex_unlock(&_pwmutex);
 | |
| 	return (r == NS_SUCCESS) ? retval : NULL;
 | |
| }
 | |
| 
 | |
| int
 | |
| getpwent_r(struct passwd *pwd, char *buffer, size_t buflen,
 | |
|     struct passwd **result)
 | |
| {
 | |
| 	int	r, retval;
 | |
| 
 | |
| 	static const ns_dtab dtab[] = {
 | |
| 		NS_FILES_CB(_files_getpwent_r, NULL)
 | |
| 		NS_DNS_CB(_dns_getpwent_r, NULL)
 | |
| 		NS_NIS_CB(_nis_getpwent_r, NULL)
 | |
| 		NS_COMPAT_CB(_compat_getpwent_r, NULL)
 | |
| 		NS_NULL_CB
 | |
| 	};
 | |
| 
 | |
| 	_DIAGASSERT(pwd != NULL);
 | |
| 	_DIAGASSERT(buffer != NULL);
 | |
| 	_DIAGASSERT(result != NULL);
 | |
| 
 | |
| 	*result = NULL;
 | |
| 	retval = 0;
 | |
| 	mutex_lock(&_pwmutex);
 | |
| 	r = nsdispatch(NULL, dtab, NSDB_PASSWD, "getpwent_r", __nsdefaultcompat,
 | |
| 	    &retval, pwd, buffer, buflen, result);
 | |
| 	mutex_unlock(&_pwmutex);
 | |
| 	switch (r) {
 | |
| 	case NS_SUCCESS:
 | |
| 	case NS_NOTFOUND:
 | |
| 		return 0;
 | |
| 	default:
 | |
| 		return retval;
 | |
| 	}
 | |
| }
 | |
| 
 | |
| 
 | |
| struct passwd *
 | |
| getpwnam(const char *name)
 | |
| {
 | |
| 	int		rv;
 | |
| 	struct passwd	*retval;
 | |
| 
 | |
| 	static const ns_dtab dtab[] = {
 | |
| 		NS_FILES_CB(_files_getpwnam, NULL)
 | |
| 		NS_DNS_CB(_dns_getpwnam, NULL)
 | |
| 		NS_NIS_CB(_nis_getpwnam, NULL)
 | |
| 		NS_COMPAT_CB(_compat_getpwnam, NULL)
 | |
| 		NS_NULL_CB
 | |
| 	};
 | |
| 
 | |
| 	mutex_lock(&_pwmutex);
 | |
| 	rv = nsdispatch(NULL, dtab, NSDB_PASSWD, "getpwnam", __nsdefaultcompat,
 | |
| 	    &retval, name);
 | |
| 	mutex_unlock(&_pwmutex);
 | |
| 	return (rv == NS_SUCCESS) ? retval : NULL;
 | |
| }
 | |
| 
 | |
| int
 | |
| getpwnam_r(const char *name, struct passwd *pwd, char *buffer, size_t buflen,
 | |
| 	struct passwd **result)
 | |
| {
 | |
| 	int	r, retval;
 | |
| 
 | |
| 	static const ns_dtab dtab[] = {
 | |
| 		NS_FILES_CB(_files_getpwnam_r, NULL)
 | |
| 		NS_DNS_CB(_dns_getpwnam_r, NULL)
 | |
| 		NS_NIS_CB(_nis_getpwnam_r, NULL)
 | |
| 		NS_COMPAT_CB(_compat_getpwnam_r, NULL)
 | |
| 		NS_NULL_CB
 | |
| 	};
 | |
| 
 | |
| 	_DIAGASSERT(name != NULL);
 | |
| 	_DIAGASSERT(pwd != NULL);
 | |
| 	_DIAGASSERT(buffer != NULL);
 | |
| 	_DIAGASSERT(result != NULL);
 | |
| 
 | |
| 	*result = NULL;
 | |
| 	retval = 0;
 | |
| 	mutex_lock(&_pwmutex);
 | |
| 	r = nsdispatch(NULL, dtab, NSDB_PASSWD, "getpwnam_r", __nsdefaultcompat,
 | |
| 	    &retval, name, pwd, buffer, buflen, result);
 | |
| 	mutex_unlock(&_pwmutex);
 | |
| 	switch (r) {
 | |
| 	case NS_SUCCESS:
 | |
| 	case NS_NOTFOUND:
 | |
| 		return 0;
 | |
| 	default:
 | |
| 		return retval;
 | |
| 	}
 | |
| }
 | |
| 
 | |
| struct passwd *
 | |
| getpwuid(uid_t uid)
 | |
| {
 | |
| 	int		rv;
 | |
| 	struct passwd	*retval;
 | |
| 
 | |
| 	static const ns_dtab dtab[] = {
 | |
| 		NS_FILES_CB(_files_getpwuid, NULL)
 | |
| 		NS_DNS_CB(_dns_getpwuid, NULL)
 | |
| 		NS_NIS_CB(_nis_getpwuid, NULL)
 | |
| 		NS_COMPAT_CB(_compat_getpwuid, NULL)
 | |
| 		NS_NULL_CB
 | |
| 	};
 | |
| 
 | |
| 	mutex_lock(&_pwmutex);
 | |
| 	rv = nsdispatch(NULL, dtab, NSDB_PASSWD, "getpwuid", __nsdefaultcompat,
 | |
| 	    &retval, uid);
 | |
| 	mutex_unlock(&_pwmutex);
 | |
| 	return (rv == NS_SUCCESS) ? retval : NULL;
 | |
| }
 | |
| 
 | |
| int
 | |
| getpwuid_r(uid_t uid, struct passwd *pwd, char *buffer, size_t buflen,
 | |
| 	struct passwd **result)
 | |
| {
 | |
| 	int	r, retval;
 | |
| 
 | |
| 	static const ns_dtab dtab[] = {
 | |
| 		NS_FILES_CB(_files_getpwuid_r, NULL)
 | |
| 		NS_DNS_CB(_dns_getpwuid_r, NULL)
 | |
| 		NS_NIS_CB(_nis_getpwuid_r, NULL)
 | |
| 		NS_COMPAT_CB(_compat_getpwuid_r, NULL)
 | |
| 		NS_NULL_CB
 | |
| 	};
 | |
| 
 | |
| 	_DIAGASSERT(pwd != NULL);
 | |
| 	_DIAGASSERT(buffer != NULL);
 | |
| 	_DIAGASSERT(result != NULL);
 | |
| 
 | |
| 	*result = NULL;
 | |
| 	retval = 0;
 | |
| 	mutex_lock(&_pwmutex);
 | |
| 	r = nsdispatch(NULL, dtab, NSDB_PASSWD, "getpwuid_r", __nsdefaultcompat,
 | |
| 	    &retval, uid, pwd, buffer, buflen, result);
 | |
| 
 | |
| 	mutex_unlock(&_pwmutex);
 | |
| 	switch (r) {
 | |
| 	case NS_SUCCESS:
 | |
| 	case NS_NOTFOUND:
 | |
| 		return 0;
 | |
| 	default:
 | |
| 		return retval;
 | |
| 	}
 | |
| }
 | |
| 
 | |
| void
 | |
| endpwent(void)
 | |
| {
 | |
| 	static const ns_dtab dtab[] = {
 | |
| 		NS_FILES_CB(_files_endpwent, NULL)
 | |
| 		NS_DNS_CB(_dns_endpwent, NULL)
 | |
| 		NS_NIS_CB(_nis_endpwent, NULL)
 | |
| 		NS_COMPAT_CB(_compat_endpwent, NULL)
 | |
| 		NS_NULL_CB
 | |
| 	};
 | |
| 
 | |
| 	mutex_lock(&_pwmutex);
 | |
| 					/* force all endpwent() methods */
 | |
| 	(void) nsdispatch(NULL, dtab, NSDB_PASSWD, "endpwent",
 | |
| 	    __nsdefaultcompat_forceall);
 | |
| 	mutex_unlock(&_pwmutex);
 | |
| }
 | |
| 
 | |
| /*ARGSUSED*/
 | |
| int
 | |
| setpassent(int stayopen)
 | |
| {
 | |
| 	static const ns_dtab dtab[] = {
 | |
| 		NS_FILES_CB(_files_setpassent, NULL)
 | |
| 		NS_DNS_CB(_dns_setpassent, NULL)
 | |
| 		NS_NIS_CB(_nis_setpassent, NULL)
 | |
| 		NS_COMPAT_CB(_compat_setpassent, NULL)
 | |
| 		NS_NULL_CB
 | |
| 	};
 | |
| 	int	rv, retval;
 | |
| 
 | |
| 	mutex_lock(&_pwmutex);
 | |
| 					/* force all setpassent() methods */
 | |
| 	rv = nsdispatch(NULL, dtab, NSDB_PASSWD, "setpassent",
 | |
| 	    __nsdefaultcompat_forceall, &retval, stayopen);
 | |
| 	mutex_unlock(&_pwmutex);
 | |
| 	return (rv == NS_SUCCESS) ? retval : 0;
 | |
| }
 | |
| 
 | |
| void
 | |
| setpwent(void)
 | |
| {
 | |
| 	static const ns_dtab dtab[] = {
 | |
| 		NS_FILES_CB(_files_setpwent, NULL)
 | |
| 		NS_DNS_CB(_dns_setpwent, NULL)
 | |
| 		NS_NIS_CB(_nis_setpwent, NULL)
 | |
| 		NS_COMPAT_CB(_compat_setpwent, NULL)
 | |
| 		NS_NULL_CB
 | |
| 	};
 | |
| 
 | |
| 	mutex_lock(&_pwmutex);
 | |
| 					/* force all setpwent() methods */
 | |
| 	(void) nsdispatch(NULL, dtab, NSDB_PASSWD, "setpwent",
 | |
| 	    __nsdefaultcompat_forceall);
 | |
| 	mutex_unlock(&_pwmutex);
 | |
| }
 |