1113596Snectar/*-
2113596Snectar * Copyright (c) 2003 Networks Associates Technology, Inc.
3113596Snectar * All rights reserved.
41573Srgrimes *
5113596Snectar * This software was developed for the FreeBSD Project by
6113596Snectar * Jacques A. Vidrine, Safeport Network Services, and Network
7113596Snectar * Associates Laboratories, the Security Research Division of Network
8113596Snectar * Associates, Inc. under DARPA/SPAWAR contract N66001-01-C-8035
9113596Snectar * ("CBOSS"), as part of the DARPA CHATS research program.
10113596Snectar *
111573Srgrimes * Redistribution and use in source and binary forms, with or without
121573Srgrimes * modification, are permitted provided that the following conditions
131573Srgrimes * are met:
141573Srgrimes * 1. Redistributions of source code must retain the above copyright
151573Srgrimes *    notice, this list of conditions and the following disclaimer.
161573Srgrimes * 2. Redistributions in binary form must reproduce the above copyright
171573Srgrimes *    notice, this list of conditions and the following disclaimer in the
181573Srgrimes *    documentation and/or other materials provided with the distribution.
191573Srgrimes *
20113596Snectar * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
211573Srgrimes * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
221573Srgrimes * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23113596Snectar * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
241573Srgrimes * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
251573Srgrimes * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
261573Srgrimes * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
271573Srgrimes * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
281573Srgrimes * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
291573Srgrimes * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
301573Srgrimes * SUCH DAMAGE.
31113596Snectar *
321573Srgrimes */
3390016Sbde#include <sys/cdefs.h>
3489999Sobrien__FBSDID("$FreeBSD$");
351573Srgrimes
36113596Snectar#include "namespace.h"
371573Srgrimes#include <sys/param.h>
38113596Snectar#ifdef YP
39113596Snectar#include <rpc/rpc.h>
40113596Snectar#include <rpcsvc/yp_prot.h>
41113596Snectar#include <rpcsvc/ypclnt.h>
42113596Snectar#endif
43113596Snectar#include <arpa/inet.h>
44113596Snectar#include <errno.h>
451573Srgrimes#include <fcntl.h>
4665532Snectar#ifdef HESIOD
4765532Snectar#include <hesiod.h>
4865532Snectar#endif
49113596Snectar#include <netdb.h>
50113596Snectar#include <nsswitch.h>
51113596Snectar#include <pthread.h>
52113596Snectar#include <pthread_np.h>
53113596Snectar#include <pwd.h>
54113596Snectar#include <stdlib.h>
5565532Snectar#include <stdio.h>
56113596Snectar#include <string.h>
57113596Snectar#include <syslog.h>
58113596Snectar#include <unistd.h>
5971579Sdeischen#include "un-namespace.h"
60113596Snectar#include <db.h>
61111618Snectar#include "libc_private.h"
6265532Snectar#include "pw_scan.h"
63113596Snectar#include "nss_tls.h"
64158115Sume#ifdef NS_CACHING
65158115Sume#include "nscache.h"
66158115Sume#endif
6765532Snectar
68113596Snectar#ifndef CTASSERT
69113596Snectar#define CTASSERT(x)		_CTASSERT(x, __LINE__)
70113596Snectar#define _CTASSERT(x, y)		__CTASSERT(x, y)
71113596Snectar#define __CTASSERT(x, y)	typedef char __assert_ ## y [(x) ? 1 : -1]
7265532Snectar#endif
7365532Snectar
74113596Snectar/* Counter as stored in /etc/pwd.db */
75113596Snectartypedef	int		pwkeynum;
7623668Speter
77113596SnectarCTASSERT(MAXLOGNAME > sizeof(uid_t));
78113596SnectarCTASSERT(MAXLOGNAME > sizeof(pwkeynum));
7965532Snectar
80113596Snectarenum constants {
81113596Snectar	PWD_STORAGE_INITIAL	= 1 << 10, /* 1 KByte */
82113596Snectar	PWD_STORAGE_MAX		= 1 << 20, /* 1 MByte */
83113596Snectar	SETPWENT		= 1,
84113596Snectar	ENDPWENT		= 2,
85113596Snectar	HESIOD_NAME_MAX		= 256
86113596Snectar};
8765532Snectar
88113596Snectarstatic const ns_src defaultsrc[] = {
89113643Snectar	{ NSSRC_COMPAT, NS_SUCCESS },
90113596Snectar	{ NULL, 0 }
9165532Snectar};
9265532Snectar
93113596Snectarint	__pw_match_entry(const char *, size_t, enum nss_lookup_type,
94113596Snectar	    const char *, uid_t);
95113596Snectarint	__pw_parse_entry(char *, size_t, struct passwd *, int, int *errnop);
9615267Swpaul
97113694Snectarstatic	void	 pwd_init(struct passwd *);
98113672Snectar
99113596Snectarunion key {
100113596Snectar	const char	*name;
101113596Snectar	uid_t		 uid;
102113596Snectar};
103113596Snectar
104113596Snectarstatic	struct passwd *getpw(int (*fn)(union key, struct passwd *, char *,
105113596Snectar		    size_t, struct passwd **), union key);
106113596Snectarstatic	int	 wrap_getpwnam_r(union key, struct passwd *, char *,
107113596Snectar		    size_t, struct passwd **);
108113596Snectarstatic	int	 wrap_getpwuid_r(union key, struct passwd *, char *, size_t,
109113596Snectar		    struct passwd **);
110113596Snectarstatic	int	 wrap_getpwent_r(union key, struct passwd *, char *, size_t,
111113596Snectar		    struct passwd **);
112113596Snectar
113113596Snectarstatic	int	 pwdb_match_entry_v3(char *, size_t, enum nss_lookup_type,
114113596Snectar		    const char *, uid_t);
115113596Snectarstatic	int	 pwdb_parse_entry_v3(char *, size_t, struct passwd *, int *);
116113596Snectarstatic	int	 pwdb_match_entry_v4(char *, size_t, enum nss_lookup_type,
117113596Snectar		    const char *, uid_t);
118113596Snectarstatic	int	 pwdb_parse_entry_v4(char *, size_t, struct passwd *, int *);
119113596Snectar
120113596Snectar
121113596Snectarstruct {
122113596Snectar	int	(*match)(char *, size_t, enum nss_lookup_type, const char *,
123113596Snectar		    uid_t);
124113596Snectar	int	(*parse)(char *, size_t, struct passwd *, int *);
125113596Snectar} pwdb_versions[] = {
126113596Snectar	{ NULL, NULL },					/* version 0 */
127113596Snectar	{ NULL, NULL },					/* version 1 */
128113596Snectar	{ NULL, NULL },					/* version 2 */
129113596Snectar	{ pwdb_match_entry_v3, pwdb_parse_entry_v3 },	/* version 3 */
130113596Snectar	{ pwdb_match_entry_v4, pwdb_parse_entry_v4 },	/* version 4 */
131113596Snectar};
132113596Snectar
133113596Snectar
134113596Snectarstruct files_state {
135113596Snectar	DB		*db;
136113596Snectar	pwkeynum	 keynum;
137113596Snectar	int		 stayopen;
138113596Snectar	int		 version;
139113596Snectar};
140113596Snectarstatic	void	files_endstate(void *);
141113596SnectarNSS_TLS_HANDLING(files);
142113596Snectarstatic	DB	*pwdbopen(int *);
143113596Snectarstatic	void	 files_endstate(void *);
144113596Snectarstatic	int	 files_setpwent(void *, void *, va_list);
145113596Snectarstatic	int	 files_passwd(void *, void *, va_list);
146113596Snectar
147113596Snectar
14865532Snectar#ifdef HESIOD
149113596Snectarstruct dns_state {
150113596Snectar	long	counter;
151113596Snectar};
152113596Snectarstatic	void	dns_endstate(void *);
153113596SnectarNSS_TLS_HANDLING(dns);
154113596Snectarstatic	int	 dns_setpwent(void *, void *, va_list);
155113596Snectarstatic	int	 dns_passwd(void *, void *, va_list);
15611436Swpaul#endif
1572917Swollman
15865532Snectar
159113596Snectar#ifdef YP
160113596Snectarstruct nis_state {
161113596Snectar	char	 domain[MAXHOSTNAMELEN];
162113596Snectar	int	 done;
163113596Snectar	char	*key;
164113596Snectar	int	 keylen;
165113596Snectar};
166113596Snectarstatic	void	 nis_endstate(void *);
167113596SnectarNSS_TLS_HANDLING(nis);
168113596Snectarstatic	int	 nis_setpwent(void *, void *, va_list);
169113596Snectarstatic	int	 nis_passwd(void *, void *, va_list);
170113596Snectarstatic	int	 nis_map(char *, enum nss_lookup_type, char *, size_t, int *);
171113596Snectarstatic	int	 nis_adjunct(char *, const char *, char *, size_t);
172113596Snectar#endif
17365532Snectar
17465532Snectar
175113596Snectarstruct compat_state {
176113596Snectar	DB		*db;
177113596Snectar	pwkeynum	 keynum;
178113596Snectar	int		 stayopen;
179113596Snectar	int		 version;
180113596Snectar	DB		*exclude;
181113596Snectar	struct passwd	 template;
182113596Snectar	char		*name;
183113596Snectar	enum _compat {
184113596Snectar		COMPAT_MODE_OFF = 0,
185113596Snectar		COMPAT_MODE_ALL,
186113596Snectar		COMPAT_MODE_NAME,
187113596Snectar		COMPAT_MODE_NETGROUP
188113596Snectar	}		 compat;
189113596Snectar};
190113596Snectarstatic	void	 compat_endstate(void *);
191113596SnectarNSS_TLS_HANDLING(compat);
192113596Snectarstatic	int	 compat_setpwent(void *, void *, va_list);
193113596Snectarstatic	int	 compat_passwd(void *, void *, va_list);
194113596Snectarstatic	void	 compat_clear_template(struct passwd *);
195113596Snectarstatic	int	 compat_set_template(struct passwd *, struct passwd *);
196113596Snectarstatic	int	 compat_use_template(struct passwd *, struct passwd *, char *,
197113596Snectar		    size_t);
198113596Snectarstatic	int	 compat_redispatch(struct compat_state *, enum nss_lookup_type,
199113596Snectar		    enum nss_lookup_type, const char *, const char *, uid_t,
200113596Snectar		    struct passwd *, char *, size_t, int *);
201158115Sume
202158115Sume#ifdef NS_CACHING
203158115Sumestatic	int	 pwd_id_func(char *, size_t *, va_list ap, void *);
204158115Sumestatic	int	 pwd_marshal_func(char *, size_t *, void *, va_list, void *);
205158115Sumestatic	int	 pwd_unmarshal_func(char *, size_t, void *, va_list, void *);
206158115Sume
207158115Sumestatic int
208158115Sumepwd_id_func(char *buffer, size_t *buffer_size, va_list ap, void *cache_mdata)
209158115Sume{
210158115Sume	char	*name;
211158115Sume	uid_t	uid;
212158115Sume	size_t	size, desired_size;
213158115Sume	int	res = NS_UNAVAIL;
214158115Sume	enum nss_lookup_type lookup_type;
215158115Sume
216158115Sume	lookup_type = (enum nss_lookup_type)cache_mdata;
217158115Sume	switch (lookup_type) {
218158115Sume	case nss_lt_name:
219158115Sume		name = va_arg(ap, char *);
220158115Sume		size = strlen(name);
221158115Sume		desired_size = sizeof(enum nss_lookup_type) + size + 1;
222158115Sume		if (desired_size > *buffer_size) {
223158115Sume			res = NS_RETURN;
224158115Sume			goto fin;
225158115Sume		}
226158115Sume
227158115Sume		memcpy(buffer, &lookup_type, sizeof(enum nss_lookup_type));
228158115Sume		memcpy(buffer + sizeof(enum nss_lookup_type), name, size + 1);
229158115Sume
230158115Sume		res = NS_SUCCESS;
231158115Sume		break;
232158115Sume	case nss_lt_id:
233158115Sume		uid = va_arg(ap, uid_t);
234158115Sume		desired_size = sizeof(enum nss_lookup_type) + sizeof(uid_t);
235158115Sume		if (desired_size > *buffer_size) {
236158115Sume			res = NS_RETURN;
237158115Sume			goto fin;
238158115Sume		}
239158115Sume
240158115Sume		memcpy(buffer, &lookup_type, sizeof(enum nss_lookup_type));
241158115Sume		memcpy(buffer + sizeof(enum nss_lookup_type), &uid,
242158115Sume		    sizeof(uid_t));
243158115Sume
244158115Sume		res = NS_SUCCESS;
245158115Sume		break;
246158115Sume	default:
247158115Sume		/* should be unreachable */
248158115Sume		return (NS_UNAVAIL);
249158115Sume	}
250158115Sume
251158115Sumefin:
252158115Sume	*buffer_size = desired_size;
253158115Sume	return (res);
254158115Sume}
255158115Sume
256158115Sumestatic int
257158115Sumepwd_marshal_func(char *buffer, size_t *buffer_size, void *retval, va_list ap,
258158115Sume    void *cache_mdata)
259158115Sume{
260158115Sume	char *name;
261158115Sume	uid_t uid;
262158115Sume	struct passwd *pwd;
263158115Sume	char *orig_buf;
264158115Sume	size_t orig_buf_size;
265158115Sume
266158115Sume	struct passwd new_pwd;
267158115Sume	size_t desired_size, size;
268158115Sume	char *p;
269158115Sume
270158115Sume	switch ((enum nss_lookup_type)cache_mdata) {
271158115Sume	case nss_lt_name:
272158115Sume		name = va_arg(ap, char *);
273158115Sume		break;
274158115Sume	case nss_lt_id:
275158115Sume		uid = va_arg(ap, uid_t);
276158115Sume		break;
277158115Sume	case nss_lt_all:
278158115Sume		break;
279158115Sume	default:
280158115Sume		/* should be unreachable */
281158115Sume		return (NS_UNAVAIL);
282158115Sume	}
283158115Sume
284158115Sume	pwd = va_arg(ap, struct passwd *);
285158115Sume	orig_buf = va_arg(ap, char *);
286158115Sume	orig_buf_size = va_arg(ap, size_t);
287158115Sume
288158115Sume	desired_size = sizeof(struct passwd) + sizeof(char *) +
289158115Sume	    strlen(pwd->pw_name) + 1;
290158115Sume	if (pwd->pw_passwd != NULL)
291158115Sume		desired_size += strlen(pwd->pw_passwd) + 1;
292158115Sume	if (pwd->pw_class != NULL)
293158115Sume		desired_size += strlen(pwd->pw_class) + 1;
294158115Sume	if (pwd->pw_gecos != NULL)
295158115Sume		desired_size += strlen(pwd->pw_gecos) + 1;
296158115Sume	if (pwd->pw_dir != NULL)
297158115Sume		desired_size += strlen(pwd->pw_dir) + 1;
298158115Sume	if (pwd->pw_shell != NULL)
299158115Sume		desired_size += strlen(pwd->pw_shell) + 1;
300158115Sume
301158115Sume	if (*buffer_size < desired_size) {
302158115Sume		/* this assignment is here for future use */
303158115Sume		*buffer_size = desired_size;
304158115Sume		return (NS_RETURN);
305158115Sume	}
306158115Sume
307158115Sume	memcpy(&new_pwd, pwd, sizeof(struct passwd));
308158115Sume	memset(buffer, 0, desired_size);
309158115Sume
310158115Sume	*buffer_size = desired_size;
311158115Sume	p = buffer + sizeof(struct passwd) + sizeof(char *);
312158115Sume	memcpy(buffer + sizeof(struct passwd), &p, sizeof(char *));
313158115Sume
314158115Sume	if (new_pwd.pw_name != NULL) {
315158115Sume		size = strlen(new_pwd.pw_name);
316158115Sume		memcpy(p, new_pwd.pw_name, size);
317158115Sume		new_pwd.pw_name = p;
318158115Sume		p += size + 1;
319158115Sume	}
320158115Sume
321158115Sume	if (new_pwd.pw_passwd != NULL) {
322158115Sume		size = strlen(new_pwd.pw_passwd);
323158115Sume		memcpy(p, new_pwd.pw_passwd, size);
324158115Sume		new_pwd.pw_passwd = p;
325158115Sume		p += size + 1;
326158115Sume	}
327158115Sume
328158115Sume	if (new_pwd.pw_class != NULL) {
329158115Sume		size = strlen(new_pwd.pw_class);
330158115Sume		memcpy(p, new_pwd.pw_class, size);
331158115Sume		new_pwd.pw_class = p;
332158115Sume		p += size + 1;
333158115Sume	}
334158115Sume
335158115Sume	if (new_pwd.pw_gecos != NULL) {
336158115Sume		size = strlen(new_pwd.pw_gecos);
337158115Sume		memcpy(p, new_pwd.pw_gecos, size);
338158115Sume		new_pwd.pw_gecos = p;
339158115Sume		p += size + 1;
340158115Sume	}
341158115Sume
342158115Sume	if (new_pwd.pw_dir != NULL) {
343158115Sume		size = strlen(new_pwd.pw_dir);
344158115Sume		memcpy(p, new_pwd.pw_dir, size);
345158115Sume		new_pwd.pw_dir = p;
346158115Sume		p += size + 1;
347158115Sume	}
348158115Sume
349158115Sume	if (new_pwd.pw_shell != NULL) {
350158115Sume		size = strlen(new_pwd.pw_shell);
351158115Sume		memcpy(p, new_pwd.pw_shell, size);
352158115Sume		new_pwd.pw_shell = p;
353158115Sume		p += size + 1;
354158115Sume	}
355158115Sume
356158115Sume	memcpy(buffer, &new_pwd, sizeof(struct passwd));
357158115Sume	return (NS_SUCCESS);
358158115Sume}
359158115Sume
360158115Sumestatic int
361158115Sumepwd_unmarshal_func(char *buffer, size_t buffer_size, void *retval, va_list ap,
362158115Sume    void *cache_mdata)
363158115Sume{
364158115Sume	char *name;
365158115Sume	uid_t uid;
366158115Sume	struct passwd *pwd;
367158115Sume	char *orig_buf;
368158115Sume	size_t orig_buf_size;
369158115Sume	int *ret_errno;
370158115Sume
371158115Sume	char *p;
372158115Sume
373158115Sume	switch ((enum nss_lookup_type)cache_mdata) {
374158115Sume	case nss_lt_name:
375158115Sume		name = va_arg(ap, char *);
376158115Sume		break;
377158115Sume	case nss_lt_id:
378158115Sume		uid = va_arg(ap, uid_t);
379158115Sume		break;
380158115Sume	case nss_lt_all:
381158115Sume		break;
382158115Sume	default:
383158115Sume		/* should be unreachable */
384158115Sume		return (NS_UNAVAIL);
385158115Sume	}
386158115Sume
387158115Sume	pwd = va_arg(ap, struct passwd *);
388158115Sume	orig_buf = va_arg(ap, char *);
389158115Sume	orig_buf_size = va_arg(ap, size_t);
390158115Sume	ret_errno = va_arg(ap, int *);
391158115Sume
392158115Sume	if (orig_buf_size <
393158115Sume	    buffer_size - sizeof(struct passwd) - sizeof(char *)) {
394158115Sume		*ret_errno = ERANGE;
395158115Sume		return (NS_RETURN);
396158115Sume	}
397158115Sume
398158115Sume	memcpy(pwd, buffer, sizeof(struct passwd));
399158115Sume	memcpy(&p, buffer + sizeof(struct passwd), sizeof(char *));
400158115Sume	memcpy(orig_buf, buffer + sizeof(struct passwd) + sizeof(char *),
401158115Sume	    buffer_size - sizeof(struct passwd) - sizeof(char *));
402158115Sume
403158115Sume	NS_APPLY_OFFSET(pwd->pw_name, orig_buf, p, char *);
404158115Sume	NS_APPLY_OFFSET(pwd->pw_passwd, orig_buf, p, char *);
405158115Sume	NS_APPLY_OFFSET(pwd->pw_class, orig_buf, p, char *);
406158115Sume	NS_APPLY_OFFSET(pwd->pw_gecos, orig_buf, p, char *);
407158115Sume	NS_APPLY_OFFSET(pwd->pw_dir, orig_buf, p, char *);
408158115Sume	NS_APPLY_OFFSET(pwd->pw_shell, orig_buf, p, char *);
409158115Sume
410158115Sume	if (retval != NULL)
411158115Sume		*((struct passwd **)retval) = pwd;
412158115Sume
413158115Sume	return (NS_SUCCESS);
414158115Sume}
415158115Sume
416158115SumeNSS_MP_CACHE_HANDLING(passwd);
417158115Sume#endif /* NS_CACHING */
418158115Sume
419113596Snectarvoid
420113596Snectarsetpwent(void)
4211573Srgrimes{
422158115Sume#ifdef NS_CACHING
423158115Sume	static const nss_cache_info cache_info = NS_MP_CACHE_INFO_INITIALIZER(
424158115Sume		passwd, (void *)nss_lt_all,
425158115Sume		NULL, NULL);
426158115Sume#endif
427158115Sume
428113596Snectar	static const ns_dtab dtab[] = {
429113596Snectar		{ NSSRC_FILES, files_setpwent, (void *)SETPWENT },
430113596Snectar#ifdef HESIOD
431113596Snectar		{ NSSRC_DNS, dns_setpwent, (void *)SETPWENT },
432113596Snectar#endif
433113596Snectar#ifdef YP
434113596Snectar		{ NSSRC_NIS, nis_setpwent, (void *)SETPWENT },
435113596Snectar#endif
436113596Snectar		{ NSSRC_COMPAT, compat_setpwent, (void *)SETPWENT },
437158115Sume#ifdef NS_CACHING
438158115Sume		NS_CACHE_CB(&cache_info)
439158115Sume#endif
440113596Snectar		{ NULL, NULL, NULL }
441113596Snectar	};
442113596Snectar	(void)_nsdispatch(NULL, dtab, NSDB_PASSWD, "setpwent", defaultsrc, 0);
443113596Snectar}
4441573Srgrimes
44510521Swpaul
446113596Snectarint
447113596Snectarsetpassent(int stayopen)
448113596Snectar{
449158115Sume#ifdef NS_CACHING
450158115Sume	static const nss_cache_info cache_info = NS_MP_CACHE_INFO_INITIALIZER(
451158115Sume		passwd, (void *)nss_lt_all,
452158115Sume		NULL, NULL);
453158115Sume#endif
454158115Sume
455113596Snectar	static const ns_dtab dtab[] = {
456113596Snectar		{ NSSRC_FILES, files_setpwent, (void *)SETPWENT },
457113596Snectar#ifdef HESIOD
458113596Snectar		{ NSSRC_DNS, dns_setpwent, (void *)SETPWENT },
459113596Snectar#endif
460113596Snectar#ifdef YP
461113596Snectar		{ NSSRC_NIS, nis_setpwent, (void *)SETPWENT },
462113596Snectar#endif
463113596Snectar		{ NSSRC_COMPAT, compat_setpwent, (void *)SETPWENT },
464158115Sume#ifdef NS_CACHING
465158115Sume		NS_CACHE_CB(&cache_info)
466158115Sume#endif
467113596Snectar		{ NULL, NULL, NULL }
468113596Snectar	};
469113596Snectar	(void)_nsdispatch(NULL, dtab, NSDB_PASSWD, "setpwent", defaultsrc,
470113596Snectar	    stayopen);
471113596Snectar	return (1);
472113596Snectar}
47365532Snectar
47465532Snectar
475113596Snectarvoid
476113596Snectarendpwent(void)
477113596Snectar{
478158115Sume#ifdef NS_CACHING
479158115Sume	static const nss_cache_info cache_info = NS_MP_CACHE_INFO_INITIALIZER(
480158115Sume		passwd, (void *)nss_lt_all,
481158115Sume		NULL, NULL);
482158115Sume#endif
483158115Sume
484113596Snectar	static const ns_dtab dtab[] = {
485113596Snectar		{ NSSRC_FILES, files_setpwent, (void *)ENDPWENT },
486113596Snectar#ifdef HESIOD
487113596Snectar		{ NSSRC_DNS, dns_setpwent, (void *)ENDPWENT },
488113596Snectar#endif
489113596Snectar#ifdef YP
490113596Snectar		{ NSSRC_NIS, nis_setpwent, (void *)ENDPWENT },
491113596Snectar#endif
492113596Snectar		{ NSSRC_COMPAT, compat_setpwent, (void *)ENDPWENT },
493158115Sume#ifdef NS_CACHING
494158115Sume		NS_CACHE_CB(&cache_info)
495158115Sume#endif
496113596Snectar		{ NULL, NULL, NULL }
497113596Snectar	};
498113596Snectar	(void)_nsdispatch(NULL, dtab, NSDB_PASSWD, "endpwent", defaultsrc);
4991573Srgrimes}
5001573Srgrimes
501113596Snectar
502113596Snectarint
503113596Snectargetpwent_r(struct passwd *pwd, char *buffer, size_t bufsize,
504113596Snectar    struct passwd **result)
5051573Srgrimes{
506158115Sume#ifdef NS_CACHING
507158115Sume	static const nss_cache_info cache_info = NS_MP_CACHE_INFO_INITIALIZER(
508158115Sume		passwd, (void *)nss_lt_all,
509158115Sume		pwd_marshal_func, pwd_unmarshal_func);
510158115Sume#endif
511158115Sume
512113596Snectar	static const ns_dtab dtab[] = {
513113596Snectar		{ NSSRC_FILES, files_passwd, (void *)nss_lt_all },
514113596Snectar#ifdef HESIOD
515113596Snectar		{ NSSRC_DNS, dns_passwd, (void *)nss_lt_all },
516113596Snectar#endif
517113596Snectar#ifdef YP
518113596Snectar		{ NSSRC_NIS, nis_passwd, (void *)nss_lt_all },
519113596Snectar#endif
520113596Snectar		{ NSSRC_COMPAT, compat_passwd, (void *)nss_lt_all },
521158115Sume#ifdef NS_CACHING
522158115Sume		NS_CACHE_CB(&cache_info)
523158115Sume#endif
524113596Snectar		{ NULL, NULL, NULL }
525113596Snectar	};
526113596Snectar	int	rv, ret_errno;
5271573Srgrimes
528113694Snectar	pwd_init(pwd);
529113694Snectar	ret_errno = 0;
530113596Snectar	*result = NULL;
531113596Snectar	rv = _nsdispatch(result, dtab, NSDB_PASSWD, "getpwent_r", defaultsrc,
532113596Snectar	    pwd, buffer, bufsize, &ret_errno);
533113596Snectar	if (rv == NS_SUCCESS)
534113596Snectar		return (0);
535113596Snectar	else
536113596Snectar		return (ret_errno);
537113596Snectar}
5381573Srgrimes
5391573Srgrimes
540113596Snectarint
541113596Snectargetpwnam_r(const char *name, struct passwd *pwd, char *buffer, size_t bufsize,
542113596Snectar    struct passwd **result)
543113596Snectar{
544158115Sume#ifdef NS_CACHING
545158115Sume	static const nss_cache_info cache_info =
546158115Sume    		NS_COMMON_CACHE_INFO_INITIALIZER(
547158115Sume		passwd, (void *)nss_lt_name,
548158115Sume		pwd_id_func, pwd_marshal_func, pwd_unmarshal_func);
549158115Sume#endif
550158115Sume
551113596Snectar	static const ns_dtab dtab[] = {
552113596Snectar		{ NSSRC_FILES, files_passwd, (void *)nss_lt_name },
553113596Snectar#ifdef HESIOD
554113596Snectar		{ NSSRC_DNS, dns_passwd, (void *)nss_lt_name },
555113596Snectar#endif
556113596Snectar#ifdef YP
557113596Snectar		{ NSSRC_NIS, nis_passwd, (void *)nss_lt_name },
558113596Snectar#endif
559113596Snectar		{ NSSRC_COMPAT, compat_passwd, (void *)nss_lt_name },
560158115Sume#ifdef NS_CACHING
561158115Sume		NS_CACHE_CB(&cache_info)
562158115Sume#endif
563113596Snectar		{ NULL, NULL, NULL }
564113596Snectar	};
565113596Snectar	int	rv, ret_errno;
566113596Snectar
567113694Snectar	pwd_init(pwd);
568113694Snectar	ret_errno = 0;
569113596Snectar	*result = NULL;
570113596Snectar	rv = _nsdispatch(result, dtab, NSDB_PASSWD, "getpwnam_r", defaultsrc,
571113596Snectar	    name, pwd, buffer, bufsize, &ret_errno);
572113596Snectar	if (rv == NS_SUCCESS)
573113596Snectar		return (0);
574113596Snectar	else
575113596Snectar		return (ret_errno);
57665532Snectar}
5775714Swollman
578113596Snectar
579113596Snectarint
580113596Snectargetpwuid_r(uid_t uid, struct passwd *pwd, char *buffer, size_t bufsize,
581113596Snectar    struct passwd **result)
58265532Snectar{
583158115Sume#ifdef NS_CACHING
584158115Sume	static const nss_cache_info cache_info =
585158115Sume    		NS_COMMON_CACHE_INFO_INITIALIZER(
586158115Sume		passwd, (void *)nss_lt_id,
587158115Sume		pwd_id_func, pwd_marshal_func, pwd_unmarshal_func);
588158115Sume#endif
589158115Sume
590113596Snectar	static const ns_dtab dtab[] = {
591113596Snectar		{ NSSRC_FILES, files_passwd, (void *)nss_lt_id },
592113596Snectar#ifdef HESIOD
593113596Snectar		{ NSSRC_DNS, dns_passwd, (void *)nss_lt_id },
594113596Snectar#endif
595113596Snectar#ifdef YP
596113596Snectar		{ NSSRC_NIS, nis_passwd, (void *)nss_lt_id },
597113596Snectar#endif
598113596Snectar		{ NSSRC_COMPAT, compat_passwd, (void *)nss_lt_id },
599158115Sume#ifdef NS_CACHING
600158115Sume		NS_CACHE_CB(&cache_info)
601158115Sume#endif
602113596Snectar		{ NULL, NULL, NULL }
603113596Snectar	};
604113596Snectar	int	rv, ret_errno;
60565532Snectar
606113694Snectar	pwd_init(pwd);
607113694Snectar	ret_errno = 0;
608113596Snectar	*result = NULL;
609113596Snectar	rv = _nsdispatch(result, dtab, NSDB_PASSWD, "getpwuid_r", defaultsrc,
610113596Snectar	    uid, pwd, buffer, bufsize, &ret_errno);
611113596Snectar	if (rv == NS_SUCCESS)
612113596Snectar		return (0);
613113596Snectar	else
614113596Snectar		return (ret_errno);
615113596Snectar}
61665532Snectar
61765532Snectar
618113694Snectarstatic void
619113694Snectarpwd_init(struct passwd *pwd)
620113672Snectar{
621113694Snectar	static char nul[] = "";
622113672Snectar
623113672Snectar	memset(pwd, 0, sizeof(*pwd));
624113672Snectar	pwd->pw_uid = (uid_t)-1;  /* Considered least likely to lead to */
625113672Snectar	pwd->pw_gid = (gid_t)-1;  /* a security issue.                  */
626113694Snectar	pwd->pw_name = nul;
627113694Snectar	pwd->pw_passwd = nul;
628113694Snectar	pwd->pw_class = nul;
629113694Snectar	pwd->pw_gecos = nul;
630113694Snectar	pwd->pw_dir = nul;
631113694Snectar	pwd->pw_shell = nul;
632113672Snectar}
633113672Snectar
634113672Snectar
635113596Snectarstatic struct passwd	 pwd;
636113596Snectarstatic char		*pwd_storage;
637113596Snectarstatic size_t		 pwd_storage_size;
63868577Snectar
63965532Snectar
640113596Snectarstatic struct passwd *
641113596Snectargetpw(int (*fn)(union key, struct passwd *, char *, size_t, struct passwd **),
642113596Snectar    union key key)
643113596Snectar{
644113596Snectar	int		 rv;
645113596Snectar	struct passwd	*res;
64665532Snectar
647113596Snectar	if (pwd_storage == NULL) {
648113596Snectar		pwd_storage = malloc(PWD_STORAGE_INITIAL);
649113596Snectar		if (pwd_storage == NULL)
650113596Snectar			return (NULL);
651113596Snectar		pwd_storage_size = PWD_STORAGE_INITIAL;
65268577Snectar	}
653113596Snectar	do {
654113596Snectar		rv = fn(key, &pwd, pwd_storage, pwd_storage_size, &res);
655113596Snectar		if (res == NULL && rv == ERANGE) {
656113596Snectar			free(pwd_storage);
657113596Snectar			if ((pwd_storage_size << 1) > PWD_STORAGE_MAX) {
658113596Snectar				pwd_storage = NULL;
659129319Skientzle				errno = ERANGE;
660113596Snectar				return (NULL);
661113596Snectar			}
662113596Snectar			pwd_storage_size <<= 1;
663113596Snectar			pwd_storage = malloc(pwd_storage_size);
664113596Snectar			if (pwd_storage == NULL)
665113596Snectar				return (NULL);
666113596Snectar		}
667113596Snectar	} while (res == NULL && rv == ERANGE);
668129349Skientzle	if (rv != 0)
669129319Skientzle		errno = rv;
670113596Snectar	return (res);
671113596Snectar}
67265532Snectar
673113596Snectar
674113596Snectarstatic int
675113596Snectarwrap_getpwnam_r(union key key, struct passwd *pwd, char *buffer,
676113596Snectar    size_t bufsize, struct passwd **res)
677113596Snectar{
678113596Snectar	return (getpwnam_r(key.name, pwd, buffer, bufsize, res));
6791573Srgrimes}
6801573Srgrimes
681113596Snectar
68265532Snectarstatic int
683113596Snectarwrap_getpwuid_r(union key key, struct passwd *pwd, char *buffer,
684113596Snectar    size_t bufsize, struct passwd **res)
6851573Srgrimes{
686113596Snectar	return (getpwuid_r(key.uid, pwd, buffer, bufsize, res));
687113596Snectar}
6881573Srgrimes
6891573Srgrimes
690113596Snectarstatic int
691113596Snectarwrap_getpwent_r(union key key __unused, struct passwd *pwd, char *buffer,
692113596Snectar    size_t bufsize, struct passwd **res)
693113596Snectar{
694113596Snectar	return (getpwent_r(pwd, buffer, bufsize, res));
695113596Snectar}
6961573Srgrimes
69765532Snectar
698113596Snectarstruct passwd *
699113596Snectargetpwnam(const char *name)
700113596Snectar{
701113596Snectar	union key key;
70265532Snectar
703113596Snectar	key.name = name;
704113596Snectar	return (getpw(wrap_getpwnam_r, key));
705113596Snectar}
7067258Swpaul
7071573Srgrimes
708113596Snectarstruct passwd *
709113596Snectargetpwuid(uid_t uid)
710113596Snectar{
711113596Snectar	union key key;
712113596Snectar
713113596Snectar	key.uid = uid;
714113596Snectar	return (getpw(wrap_getpwuid_r, key));
7151573Srgrimes}
7161573Srgrimes
717113596Snectar
718113596Snectarstruct passwd *
719113596Snectargetpwent(void)
720113596Snectar{
721113596Snectar	union key key;
722113596Snectar
723113596Snectar	key.uid = 0; /* not used */
724113596Snectar	return (getpw(wrap_getpwent_r, key));
725113596Snectar}
726113596Snectar
727113596Snectar
72865532Snectar/*
729113596Snectar * files backend
73065532Snectar */
731113596Snectarstatic DB *
732113596Snectarpwdbopen(int *version)
7331573Srgrimes{
734113596Snectar	DB	*res;
735113596Snectar	DBT	 key, entry;
736113596Snectar	int	 rv;
7371573Srgrimes
738113596Snectar	if (geteuid() != 0 ||
739113596Snectar	    (res = dbopen(_PATH_SMP_DB, O_RDONLY, 0, DB_HASH, NULL)) == NULL)
740113596Snectar		res = dbopen(_PATH_MP_DB, O_RDONLY, 0, DB_HASH, NULL);
741113596Snectar	if (res == NULL)
742113596Snectar		return (NULL);
743113596Snectar	key.data = _PWD_VERSION_KEY;
744113596Snectar	key.size = strlen(_PWD_VERSION_KEY);
745113596Snectar	rv = res->get(res, &key, &entry, 0);
746113596Snectar	if (rv == 0)
747113596Snectar		*version = *(unsigned char *)entry.data;
748113596Snectar	else
749113596Snectar		*version = 3;
750113596Snectar	if (*version < 3 ||
751113596Snectar	    *version >= sizeof(pwdb_versions)/sizeof(pwdb_versions[0])) {
752113596Snectar		syslog(LOG_CRIT, "Unsupported password database version %d",
753113596Snectar		    *version);
754113596Snectar		res->close(res);
755113596Snectar		res = NULL;
75615267Swpaul	}
757113596Snectar	return (res);
758113596Snectar}
75965532Snectar
76065532Snectar
761113596Snectarstatic void
762113596Snectarfiles_endstate(void *p)
763113596Snectar{
764113596Snectar	DB	*db;
765113596Snectar
766113596Snectar	if (p == NULL)
767113596Snectar		return;
768113596Snectar	db = ((struct files_state *)p)->db;
769113596Snectar	if (db != NULL)
770113596Snectar		db->close(db);
771113596Snectar	free(p);
772113596Snectar}
773113596Snectar
774113596Snectar
775113596Snectarstatic int
776113596Snectarfiles_setpwent(void *retval, void *mdata, va_list ap)
777113596Snectar{
778113596Snectar	struct files_state	*st;
779113596Snectar	int			 rv, stayopen;
780113596Snectar
781113596Snectar	rv = files_getstate(&st);
782113596Snectar	if (rv != 0)
783113596Snectar		return (NS_UNAVAIL);
784113596Snectar	switch ((enum constants)mdata) {
785113596Snectar	case SETPWENT:
786113596Snectar		stayopen = va_arg(ap, int);
787113596Snectar		st->keynum = 0;
788113596Snectar		if (stayopen)
789113596Snectar			st->db = pwdbopen(&st->version);
790113596Snectar		st->stayopen = stayopen;
791113596Snectar		break;
792113596Snectar	case ENDPWENT:
793113596Snectar		if (st->db != NULL) {
794113596Snectar			(void)st->db->close(st->db);
795113596Snectar			st->db = NULL;
79665532Snectar		}
797113596Snectar		break;
798113596Snectar	default:
799113596Snectar		break;
80015267Swpaul	}
801113596Snectar	return (NS_UNAVAIL);
8021573Srgrimes}
8031573Srgrimes
80465532Snectar
80517141Sjkhstatic int
806113596Snectarfiles_passwd(void *retval, void *mdata, va_list ap)
8071573Srgrimes{
808113596Snectar	char			 keybuf[MAXLOGNAME + 1];
809113596Snectar	DBT			 key, entry;
810113596Snectar	struct files_state	*st;
811113596Snectar	enum nss_lookup_type	 how;
812113596Snectar	const char		*name;
813113596Snectar	struct passwd		*pwd;
814113596Snectar	char			*buffer;
815113596Snectar	size_t			 bufsize, namesize;
816113596Snectar	uid_t			 uid;
817113596Snectar	uint32_t		 store;
818113596Snectar	int			 rv, stayopen, *errnop;
8191573Srgrimes
820113596Snectar	name = NULL;
821113596Snectar	uid = (uid_t)-1;
822113596Snectar	how = (enum nss_lookup_type)mdata;
823113596Snectar	switch (how) {
824113596Snectar	case nss_lt_name:
82565532Snectar		name = va_arg(ap, const char *);
826113596Snectar		keybuf[0] = _PW_KEYBYNAME;
82765532Snectar		break;
828113596Snectar	case nss_lt_id:
82965532Snectar		uid = va_arg(ap, uid_t);
830113596Snectar		keybuf[0] = _PW_KEYBYUID;
83165532Snectar		break;
832113596Snectar	case nss_lt_all:
833113596Snectar		keybuf[0] = _PW_KEYBYNUM;
834113596Snectar		break;
83565532Snectar	default:
836113596Snectar		rv = NS_NOTFOUND;
837113596Snectar		goto fin;
83865532Snectar	}
839113596Snectar	pwd = va_arg(ap, struct passwd *);
840113596Snectar	buffer = va_arg(ap, char *);
841113596Snectar	bufsize = va_arg(ap, size_t);
842113596Snectar	errnop = va_arg(ap, int *);
843113596Snectar	*errnop = files_getstate(&st);
844113596Snectar	if (*errnop != 0)
845113596Snectar		return (NS_UNAVAIL);
846113596Snectar	if (how == nss_lt_all && st->keynum < 0) {
847113596Snectar		rv = NS_NOTFOUND;
848113596Snectar		goto fin;
84965532Snectar	}
850113596Snectar	if (st->db == NULL &&
851113596Snectar	    (st->db = pwdbopen(&st->version)) == NULL) {
852113596Snectar		*errnop = errno;
853113596Snectar		rv = NS_UNAVAIL;
854113596Snectar		goto fin;
85594688Sdes	}
856113596Snectar	if (how == nss_lt_all)
857113596Snectar		stayopen = 1;
858113596Snectar	else
859113596Snectar		stayopen = st->stayopen;
860113596Snectar	key.data = keybuf;
861113596Snectar	do {
862113596Snectar		switch (how) {
863113596Snectar		case nss_lt_name:
864113596Snectar			/* MAXLOGNAME includes NUL byte, but we do not
865113596Snectar			 * include the NUL byte in the key.
866113596Snectar			 */
867114443Snectar			namesize = strlcpy(&keybuf[1], name, sizeof(keybuf)-1);
868113596Snectar			if (namesize >= sizeof(keybuf)-1) {
869113596Snectar				*errnop = EINVAL;
870113596Snectar				rv = NS_NOTFOUND;
871113596Snectar				goto fin;
872113596Snectar			}
873113596Snectar			key.size = namesize + 1;
874113596Snectar			break;
875113596Snectar		case nss_lt_id:
876113596Snectar			if (st->version < _PWD_CURRENT_VERSION) {
877113596Snectar				memcpy(&keybuf[1], &uid, sizeof(uid));
878113596Snectar				key.size = sizeof(uid) + 1;
879113596Snectar			} else {
880113596Snectar				store = htonl(uid);
881113596Snectar				memcpy(&keybuf[1], &store, sizeof(store));
882113596Snectar				key.size = sizeof(store) + 1;
883113596Snectar			}
884113596Snectar			break;
885113596Snectar		case nss_lt_all:
886113596Snectar			st->keynum++;
887113596Snectar			if (st->version < _PWD_CURRENT_VERSION) {
888113596Snectar				memcpy(&keybuf[1], &st->keynum,
889113596Snectar				    sizeof(st->keynum));
890113596Snectar				key.size = sizeof(st->keynum) + 1;
891113596Snectar			} else {
892113596Snectar				store = htonl(st->keynum);
893113596Snectar				memcpy(&keybuf[1], &store, sizeof(store));
894113596Snectar				key.size = sizeof(store) + 1;
895113596Snectar			}
896113596Snectar			break;
897113596Snectar		}
898113666Snectar		keybuf[0] = _PW_VERSIONED(keybuf[0], st->version);
899113596Snectar		rv = st->db->get(st->db, &key, &entry, 0);
900113596Snectar		if (rv < 0 || rv > 1) { /* should never return > 1 */
901113596Snectar			*errnop = errno;
902113596Snectar			rv = NS_UNAVAIL;
903113596Snectar			goto fin;
904113596Snectar		} else if (rv == 1) {
905113596Snectar			if (how == nss_lt_all)
906113596Snectar				st->keynum = -1;
907113596Snectar			rv = NS_NOTFOUND;
908113596Snectar			goto fin;
909113596Snectar		}
910113596Snectar		rv = pwdb_versions[st->version].match(entry.data, entry.size,
911113596Snectar		    how, name, uid);
912113596Snectar		if (rv != NS_SUCCESS)
913113596Snectar			continue;
914113596Snectar		if (entry.size > bufsize) {
915113596Snectar			*errnop = ERANGE;
916113596Snectar			rv = NS_RETURN;
917113596Snectar			break;
918113596Snectar		}
919113596Snectar		memcpy(buffer, entry.data, entry.size);
920113596Snectar		rv = pwdb_versions[st->version].parse(buffer, entry.size, pwd,
921113596Snectar		    errnop);
922113596Snectar	} while (how == nss_lt_all && !(rv & NS_TERMINATE));
923113596Snectarfin:
924113596Snectar	if (!stayopen && st->db != NULL) {
925113596Snectar		(void)st->db->close(st->db);
926113596Snectar		st->db = NULL;
927113596Snectar	}
928113670Snectar	if (rv == NS_SUCCESS) {
929113670Snectar		pwd->pw_fields &= ~_PWF_SOURCE;
930113670Snectar		pwd->pw_fields |= _PWF_FILES;
931113670Snectar		if (retval != NULL)
932113670Snectar			*(struct passwd **)retval = pwd;
933113670Snectar	}
934113596Snectar	return (rv);
9351573Srgrimes}
9361573Srgrimes
937113596Snectar
938113596Snectarstatic int
939124432Snectarpwdb_match_entry_v3(char *entry, size_t entrysize, enum nss_lookup_type how,
940113596Snectar    const char *name, uid_t uid)
941113596Snectar{
942113596Snectar	const char	*p, *eom;
943113596Snectar	uid_t		 uid2;
944113596Snectar
945113596Snectar	eom = &entry[entrysize];
946113596Snectar	for (p = entry; p < eom; p++)
947113596Snectar		if (*p == '\0')
948113596Snectar			break;
949113596Snectar	if (*p != '\0')
950113596Snectar		return (NS_NOTFOUND);
951113596Snectar	if (how == nss_lt_all)
952113596Snectar		return (NS_SUCCESS);
953124432Snectar	if (how == nss_lt_name)
954113596Snectar		return (strcmp(name, entry) == 0 ? NS_SUCCESS : NS_NOTFOUND);
955113596Snectar	for (p++; p < eom; p++)
956113596Snectar		if (*p == '\0')
957113596Snectar			break;
958113596Snectar	if (*p != '\0' || (++p) + sizeof(uid) >= eom)
959113596Snectar		return (NS_NOTFOUND);
960113596Snectar	memcpy(&uid2, p, sizeof(uid2));
961113596Snectar	return (uid == uid2 ? NS_SUCCESS : NS_NOTFOUND);
962113596Snectar}
963113596Snectar
964113596Snectar
965113596Snectarstatic int
966113596Snectarpwdb_parse_entry_v3(char *buffer, size_t bufsize, struct passwd *pwd,
967113596Snectar    int *errnop)
968113596Snectar{
969113596Snectar	char		*p, *eom;
970113596Snectar	int32_t		 pw_change, pw_expire;
971113596Snectar
972113596Snectar	/* THIS CODE MUST MATCH THAT IN pwd_mkdb. */
973113596Snectar	p = buffer;
974113596Snectar	eom = &buffer[bufsize];
975113596Snectar#define STRING(field)	do {			\
976113596Snectar		(field) = p;			\
977113596Snectar		while (p < eom && *p != '\0')	\
978113596Snectar			p++;			\
979113596Snectar		if (p >= eom)			\
980113596Snectar			return (NS_NOTFOUND);	\
981113596Snectar		p++;				\
982113596Snectar	} while (0)
983113596Snectar#define SCALAR(field)	do {				\
984113596Snectar		if (p + sizeof(field) > eom)		\
985113596Snectar			return (NS_NOTFOUND);		\
986113596Snectar		memcpy(&(field), p, sizeof(field));	\
987113596Snectar		p += sizeof(field);			\
988113596Snectar	} while (0)
989113596Snectar	STRING(pwd->pw_name);
990113596Snectar	STRING(pwd->pw_passwd);
991113596Snectar	SCALAR(pwd->pw_uid);
992113596Snectar	SCALAR(pwd->pw_gid);
993113596Snectar	SCALAR(pw_change);
994113596Snectar	STRING(pwd->pw_class);
995113596Snectar	STRING(pwd->pw_gecos);
996113596Snectar	STRING(pwd->pw_dir);
997113596Snectar	STRING(pwd->pw_shell);
998113596Snectar	SCALAR(pw_expire);
999113596Snectar	SCALAR(pwd->pw_fields);
1000113596Snectar#undef STRING
1001113596Snectar#undef SCALAR
1002113596Snectar	pwd->pw_change = pw_change;
1003113596Snectar	pwd->pw_expire = pw_expire;
1004113596Snectar	return (NS_SUCCESS);
1005113596Snectar}
1006113596Snectar
1007113596Snectar
1008113596Snectarstatic int
1009124432Snectarpwdb_match_entry_v4(char *entry, size_t entrysize, enum nss_lookup_type how,
1010113596Snectar    const char *name, uid_t uid)
1011113596Snectar{
1012113596Snectar	const char	*p, *eom;
1013113596Snectar	uint32_t	 uid2;
1014113596Snectar
1015113596Snectar	eom = &entry[entrysize];
1016113596Snectar	for (p = entry; p < eom; p++)
1017113596Snectar		if (*p == '\0')
1018113596Snectar			break;
1019113596Snectar	if (*p != '\0')
1020113596Snectar		return (NS_NOTFOUND);
1021113596Snectar	if (how == nss_lt_all)
1022113596Snectar		return (NS_SUCCESS);
1023124432Snectar	if (how == nss_lt_name)
1024113596Snectar		return (strcmp(name, entry) == 0 ? NS_SUCCESS : NS_NOTFOUND);
1025113596Snectar	for (p++; p < eom; p++)
1026113596Snectar		if (*p == '\0')
1027113596Snectar			break;
1028113596Snectar	if (*p != '\0' || (++p) + sizeof(uid) >= eom)
1029113596Snectar		return (NS_NOTFOUND);
1030113596Snectar	memcpy(&uid2, p, sizeof(uid2));
1031113596Snectar	uid2 = ntohl(uid2);
1032113596Snectar	return (uid == (uid_t)uid2 ? NS_SUCCESS : NS_NOTFOUND);
1033113596Snectar}
1034113596Snectar
1035113596Snectar
1036113596Snectarstatic int
1037113596Snectarpwdb_parse_entry_v4(char *buffer, size_t bufsize, struct passwd *pwd,
1038113596Snectar    int *errnop)
1039113596Snectar{
1040113596Snectar	char		*p, *eom;
1041113596Snectar	uint32_t	 n;
1042113596Snectar
1043113596Snectar	/* THIS CODE MUST MATCH THAT IN pwd_mkdb. */
1044113596Snectar	p = buffer;
1045113596Snectar	eom = &buffer[bufsize];
1046113596Snectar#define STRING(field)	do {			\
1047113596Snectar		(field) = p;			\
1048113596Snectar		while (p < eom && *p != '\0')	\
1049113596Snectar			p++;			\
1050113596Snectar		if (p >= eom)			\
1051113596Snectar			return (NS_NOTFOUND);	\
1052113596Snectar		p++;				\
1053113596Snectar	} while (0)
1054113596Snectar#define SCALAR(field)	do {				\
1055113596Snectar		if (p + sizeof(n) > eom)		\
1056113596Snectar			return (NS_NOTFOUND);		\
1057113596Snectar		memcpy(&n, p, sizeof(n));		\
1058113596Snectar		(field) = ntohl(n);			\
1059113596Snectar		p += sizeof(n);				\
1060113596Snectar	} while (0)
1061113596Snectar	STRING(pwd->pw_name);
1062113596Snectar	STRING(pwd->pw_passwd);
1063113596Snectar	SCALAR(pwd->pw_uid);
1064113596Snectar	SCALAR(pwd->pw_gid);
1065113596Snectar	SCALAR(pwd->pw_change);
1066113596Snectar	STRING(pwd->pw_class);
1067113596Snectar	STRING(pwd->pw_gecos);
1068113596Snectar	STRING(pwd->pw_dir);
1069113596Snectar	STRING(pwd->pw_shell);
1070113596Snectar	SCALAR(pwd->pw_expire);
1071113596Snectar	SCALAR(pwd->pw_fields);
1072113596Snectar#undef STRING
1073113596Snectar#undef SCALAR
1074113596Snectar	return (NS_SUCCESS);
1075113596Snectar}
1076113596Snectar
1077113596Snectar
107865532Snectar#ifdef HESIOD
107965532Snectar/*
1080113596Snectar * dns backend
108165532Snectar */
1082113596Snectarstatic void
1083113596Snectardns_endstate(void *p)
1084113596Snectar{
1085113596Snectar	free(p);
1086113596Snectar}
108765532Snectar
1088113596Snectar
108917141Sjkhstatic int
1090113596Snectardns_setpwent(void *retval, void *mdata, va_list ap)
10911573Srgrimes{
1092113596Snectar	struct dns_state	*st;
1093113596Snectar	int			 rv;
10941573Srgrimes
1095113596Snectar	rv = dns_getstate(&st);
1096113596Snectar	if (rv != 0)
1097113596Snectar		return (NS_UNAVAIL);
1098113596Snectar	st->counter = 0;
1099113596Snectar	return (NS_UNAVAIL);
1100113596Snectar}
11011573Srgrimes
1102113596Snectar
1103113596Snectarstatic int
1104113596Snectardns_passwd(void *retval, void *mdata, va_list ap)
1105113596Snectar{
1106113596Snectar	char			 buf[HESIOD_NAME_MAX];
1107113596Snectar	struct dns_state	*st;
1108113596Snectar	struct passwd		*pwd;
1109113596Snectar	const char		*name, *label;
1110113596Snectar	void			*ctx;
1111113596Snectar	char			*buffer, **hes;
1112113596Snectar	size_t			 bufsize, linesize;
1113113596Snectar	uid_t			 uid;
1114113596Snectar	enum nss_lookup_type	 how;
1115113596Snectar	int			 rv, *errnop;
1116113596Snectar
1117113596Snectar	ctx = NULL;
1118113596Snectar	hes = NULL;
1119113596Snectar	name = NULL;
1120113596Snectar	uid = (uid_t)-1;
1121113596Snectar	how = (enum nss_lookup_type)mdata;
1122113596Snectar	switch (how) {
1123113596Snectar	case nss_lt_name:
112465532Snectar		name = va_arg(ap, const char *);
112565532Snectar		break;
1126113596Snectar	case nss_lt_id:
112765532Snectar		uid = va_arg(ap, uid_t);
112865532Snectar		break;
1129113596Snectar	case nss_lt_all:
1130113596Snectar		break;
113129479Swosch	}
1132113596Snectar	pwd     = va_arg(ap, struct passwd *);
1133113596Snectar	buffer  = va_arg(ap, char *);
1134113596Snectar	bufsize = va_arg(ap, size_t);
1135113596Snectar	errnop  = va_arg(ap, int *);
1136113596Snectar	*errnop = dns_getstate(&st);
1137113596Snectar	if (*errnop != 0)
1138113596Snectar		return (NS_UNAVAIL);
1139113596Snectar	if (hesiod_init(&ctx) != 0) {
1140113596Snectar		*errnop = errno;
1141113596Snectar		rv = NS_UNAVAIL;
1142113596Snectar		goto fin;
1143113596Snectar	}
1144113596Snectar	do {
1145113596Snectar		rv = NS_NOTFOUND;
1146113596Snectar		switch (how) {
1147113596Snectar		case nss_lt_name:
1148113596Snectar			label = name;
1149113596Snectar			break;
1150113596Snectar		case nss_lt_id:
1151113596Snectar			if (snprintf(buf, sizeof(buf), "%lu",
1152113596Snectar			    (unsigned long)uid) >= sizeof(buf))
1153113596Snectar				goto fin;
1154113596Snectar			label = buf;
1155113596Snectar			break;
1156113596Snectar		case nss_lt_all:
1157113596Snectar			if (st->counter < 0)
1158113596Snectar				goto fin;
1159113596Snectar			if (snprintf(buf, sizeof(buf), "passwd-%ld",
1160113596Snectar			    st->counter++) >= sizeof(buf))
1161113596Snectar				goto fin;
1162113596Snectar			label = buf;
1163113596Snectar			break;
116465532Snectar		}
1165113596Snectar		hes = hesiod_resolve(ctx, label,
1166113596Snectar		    how == nss_lt_id ? "uid" : "passwd");
1167113596Snectar		if (hes == NULL) {
1168113596Snectar			if (how == nss_lt_all)
1169113596Snectar				st->counter = -1;
1170113596Snectar			if (errno != ENOENT)
1171113596Snectar				*errnop = errno;
1172113596Snectar			goto fin;
1173113596Snectar		}
1174113596Snectar		rv = __pw_match_entry(hes[0], strlen(hes[0]), how, name, uid);
1175113596Snectar		if (rv != NS_SUCCESS) {
1176113596Snectar			hesiod_free_list(ctx, hes);
1177113596Snectar			hes = NULL;
1178113596Snectar			continue;
1179113596Snectar		}
1180114443Snectar		linesize = strlcpy(buffer, hes[0], bufsize);
1181113596Snectar		if (linesize >= bufsize) {
1182113596Snectar			*errnop = ERANGE;
1183113596Snectar			rv = NS_RETURN;
1184113596Snectar			continue;
1185113596Snectar		}
1186113596Snectar		hesiod_free_list(ctx, hes);
1187113596Snectar		hes = NULL;
1188113596Snectar		rv = __pw_parse_entry(buffer, bufsize, pwd, 0, errnop);
1189113596Snectar	} while (how == nss_lt_all && !(rv & NS_TERMINATE));
1190113596Snectarfin:
1191113596Snectar	if (hes != NULL)
1192113596Snectar		hesiod_free_list(ctx, hes);
1193113596Snectar	if (ctx != NULL)
1194113596Snectar		hesiod_end(ctx);
1195113596Snectar	if (rv == NS_SUCCESS) {
1196113596Snectar		pwd->pw_fields &= ~_PWF_SOURCE;
1197113596Snectar		pwd->pw_fields |= _PWF_HESIOD;
1198113596Snectar		if (retval != NULL)
1199113596Snectar			*(struct passwd **)retval = pwd;
120065532Snectar	}
1201113596Snectar	return (rv);
120215267Swpaul}
1203113596Snectar#endif /* HESIOD */
12047258Swpaul
1205113596Snectar
120665532Snectar#ifdef YP
120715267Swpaul/*
1208113596Snectar * nis backend
120915267Swpaul */
1210113596Snectarstatic void
1211113596Snectarnis_endstate(void *p)
1212113596Snectar{
1213113596Snectar	free(((struct nis_state *)p)->key);
1214113596Snectar	free(p);
1215113596Snectar}
121665532Snectar
1217117750Swpaul/*
1218117750Swpaul * Test for the presence of special FreeBSD-specific master.passwd.by*
1219117750Swpaul * maps. We do this using yp_order(). If it fails, then either the server
1220117750Swpaul * doesn't have the map, or the YPPROC_ORDER procedure isn't supported by
1221117750Swpaul * the server (Sun NIS+ servers in YP compat mode behave this way). If
1222117750Swpaul * the master.passwd.by* maps don't exist, then let the lookup routine try
1223117750Swpaul * the regular passwd.by* maps instead. If the lookup routine fails, it
1224117750Swpaul * can return an error as needed.
1225117750Swpaul */
122665532Snectarstatic int
1227113596Snectarnis_map(char *domain, enum nss_lookup_type how, char *buffer, size_t bufsize,
1228113596Snectar    int *master)
122915267Swpaul{
1230117750Swpaul	int	rv, order;
12317258Swpaul
1232113596Snectar	*master = 0;
1233113794Snectar	if (geteuid() == 0) {
1234113794Snectar		if (snprintf(buffer, bufsize, "master.passwd.by%s",
1235113794Snectar		    (how == nss_lt_id) ? "uid" : "name") >= bufsize)
1236113794Snectar			return (NS_UNAVAIL);
1237117750Swpaul		rv = yp_order(domain, buffer, &order);
1238113794Snectar		if (rv == 0) {
1239113794Snectar			*master = 1;
1240113794Snectar			return (NS_SUCCESS);
1241113794Snectar		}
124265532Snectar	}
1243117750Swpaul
1244113596Snectar	if (snprintf(buffer, bufsize, "passwd.by%s",
1245113596Snectar	    (how == nss_lt_id) ? "uid" : "name") >= bufsize)
1246113596Snectar		return (NS_UNAVAIL);
1247117750Swpaul
1248117750Swpaul	return (NS_SUCCESS);
1249113596Snectar}
125015267Swpaul
1251113596Snectar
1252113596Snectarstatic int
1253113596Snectarnis_adjunct(char *domain, const char *name, char *buffer, size_t bufsize)
1254113596Snectar{
1255113596Snectar	int	 rv;
1256113596Snectar	char	*result, *p, *q, *eor;
1257113596Snectar	int	 resultlen;
1258113596Snectar
1259113596Snectar	result = NULL;
1260113596Snectar	rv = yp_match(domain, "passwd.adjunct.byname", name, strlen(name),
1261113596Snectar	    &result, &resultlen);
1262113596Snectar	if (rv != 0)
1263113596Snectar		rv = 1;
1264113596Snectar	else {
1265113596Snectar		eor = &result[resultlen];
1266113596Snectar		p = memchr(result, ':', eor - result);
1267113596Snectar		if (p != NULL && ++p < eor &&
1268113596Snectar		    (q = memchr(p, ':', eor - p)) != NULL) {
1269113596Snectar			if (q - p >= bufsize)
1270113596Snectar				rv = -1;
1271113596Snectar			else {
1272113596Snectar				memcpy(buffer, p, q - p);
1273113596Snectar				buffer[q - p] ='\0';
1274113596Snectar			}
1275113596Snectar		} else
1276113596Snectar			rv = 1;
1277124432Snectar	}
1278113596Snectar	free(result);
1279113596Snectar	return (rv);
1280113596Snectar}
1281113596Snectar
1282113596Snectar
1283113596Snectarstatic int
1284113596Snectarnis_setpwent(void *retval, void *mdata, va_list ap)
1285113596Snectar{
1286113596Snectar	struct nis_state	*st;
1287113596Snectar	int			 rv;
1288113596Snectar
1289113596Snectar	rv = nis_getstate(&st);
1290113596Snectar	if (rv != 0)
1291113596Snectar		return (NS_UNAVAIL);
1292113596Snectar	st->done = 0;
1293113596Snectar	free(st->key);
1294113596Snectar	st->key = NULL;
1295113596Snectar	return (NS_UNAVAIL);
1296113596Snectar}
1297113596Snectar
1298113596Snectar
1299113596Snectarstatic int
1300113596Snectarnis_passwd(void *retval, void *mdata, va_list ap)
1301113596Snectar{
1302113596Snectar	char		 map[YPMAXMAP];
1303113596Snectar	struct nis_state *st;
1304113596Snectar	struct passwd	*pwd;
1305113596Snectar	const char	*name;
1306113596Snectar	char		*buffer, *key, *result;
1307113596Snectar	size_t		 bufsize;
1308113596Snectar	uid_t		 uid;
1309113596Snectar	enum nss_lookup_type how;
1310113596Snectar	int		*errnop, keylen, resultlen, rv, master;
1311124432Snectar
1312113596Snectar	name = NULL;
1313113596Snectar	uid = (uid_t)-1;
1314113596Snectar	how = (enum nss_lookup_type)mdata;
1315113596Snectar	switch (how) {
1316113596Snectar	case nss_lt_name:
131765532Snectar		name = va_arg(ap, const char *);
131865532Snectar		break;
1319113596Snectar	case nss_lt_id:
132065532Snectar		uid = va_arg(ap, uid_t);
132165532Snectar		break;
1322113596Snectar	case nss_lt_all:
1323113596Snectar		break;
132465532Snectar	}
1325113596Snectar	pwd     = va_arg(ap, struct passwd *);
1326113596Snectar	buffer  = va_arg(ap, char *);
1327113596Snectar	bufsize = va_arg(ap, size_t);
1328113596Snectar	errnop  = va_arg(ap, int *);
1329113596Snectar	*errnop = nis_getstate(&st);
1330113596Snectar	if (*errnop != 0)
1331113596Snectar		return (NS_UNAVAIL);
1332113596Snectar	if (st->domain[0] == '\0') {
1333113596Snectar		if (getdomainname(st->domain, sizeof(st->domain)) != 0) {
1334113596Snectar			*errnop = errno;
1335113596Snectar			return (NS_UNAVAIL);
133665532Snectar		}
133765532Snectar	}
1338113596Snectar	rv = nis_map(st->domain, how, map, sizeof(map), &master);
1339113596Snectar	if (rv != NS_SUCCESS)
1340113596Snectar		return (rv);
1341113596Snectar	result = NULL;
1342113596Snectar	do {
1343113596Snectar		rv = NS_NOTFOUND;
1344113596Snectar		switch (how) {
1345113596Snectar		case nss_lt_name:
1346114443Snectar			if (strlcpy(buffer, name, bufsize) >= bufsize)
1347113596Snectar				goto erange;
1348113596Snectar			break;
1349113596Snectar		case nss_lt_id:
1350113596Snectar			if (snprintf(buffer, bufsize, "%lu",
1351113596Snectar			    (unsigned long)uid) >= bufsize)
1352113596Snectar				goto erange;
1353113596Snectar			break;
1354113596Snectar		case nss_lt_all:
1355113596Snectar			if (st->done)
1356113596Snectar				goto fin;
1357113596Snectar			break;
1358113596Snectar		}
1359113596Snectar		result = NULL;
1360113596Snectar		if (how == nss_lt_all) {
1361113596Snectar			if (st->key == NULL)
1362113596Snectar				rv = yp_first(st->domain, map, &st->key,
1363113596Snectar				    &st->keylen, &result, &resultlen);
1364113596Snectar			else {
1365113596Snectar				key = st->key;
1366113596Snectar				keylen = st->keylen;
1367113596Snectar				st->key = NULL;
1368113596Snectar				rv = yp_next(st->domain, map, key, keylen,
1369113596Snectar				    &st->key, &st->keylen, &result,
1370113596Snectar				    &resultlen);
1371113596Snectar				free(key);
137265532Snectar			}
1373113596Snectar			if (rv != 0) {
1374113596Snectar				free(result);
1375113596Snectar				free(st->key);
1376113596Snectar				st->key = NULL;
1377113596Snectar				if (rv == YPERR_NOMORE)
1378113596Snectar					st->done = 1;
1379113596Snectar				else
1380113596Snectar					rv = NS_UNAVAIL;
1381113596Snectar				goto fin;
1382113596Snectar			}
138365532Snectar		} else {
1384113596Snectar			rv = yp_match(st->domain, map, buffer, strlen(buffer),
1385113596Snectar			    &result, &resultlen);
1386113596Snectar			if (rv == YPERR_KEY) {
1387113596Snectar				rv = NS_NOTFOUND;
1388113596Snectar				continue;
1389113596Snectar			} else if (rv != 0) {
1390113596Snectar				free(result);
1391113596Snectar				rv = NS_UNAVAIL;
1392113596Snectar				continue;
1393113596Snectar			}
139465532Snectar		}
1395113596Snectar		if (resultlen >= bufsize)
1396113596Snectar			goto erange;
1397113596Snectar		memcpy(buffer, result, resultlen);
1398113596Snectar		buffer[resultlen] = '\0';
1399113596Snectar		free(result);
1400113596Snectar		rv = __pw_match_entry(buffer, resultlen, how, name, uid);
1401113596Snectar		if (rv == NS_SUCCESS)
1402113596Snectar			rv = __pw_parse_entry(buffer, resultlen, pwd, master,
1403113596Snectar			    errnop);
1404113596Snectar	} while (how == nss_lt_all && !(rv & NS_TERMINATE));
1405124432Snectarfin:
1406113596Snectar	if (rv == NS_SUCCESS) {
1407113596Snectar		if (strstr(pwd->pw_passwd, "##") != NULL) {
1408124431Snectar			rv = nis_adjunct(st->domain, pwd->pw_name,
1409113596Snectar			    &buffer[resultlen+1], bufsize-resultlen-1);
1410113596Snectar			if (rv < 0)
1411113596Snectar				goto erange;
1412113596Snectar			else if (rv == 0)
1413113596Snectar				pwd->pw_passwd = &buffer[resultlen+1];
141465532Snectar		}
1415113596Snectar		pwd->pw_fields &= ~_PWF_SOURCE;
1416113596Snectar		pwd->pw_fields |= _PWF_NIS;
1417113596Snectar		if (retval != NULL)
1418113596Snectar			*(struct passwd **)retval = pwd;
1419128537Sjon		rv = NS_SUCCESS;
142015267Swpaul	}
1421124432Snectar	return (rv);
1422113596Snectarerange:
1423113596Snectar	*errnop = ERANGE;
1424113596Snectar	return (NS_RETURN);
1425113596Snectar}
1426113596Snectar#endif /* YP */
142715267Swpaul
1428113596Snectar
142915267Swpaul/*
1430113596Snectar * compat backend
143115267Swpaul */
1432113596Snectarstatic void
1433113596Snectarcompat_clear_template(struct passwd *template)
1434113596Snectar{
143565532Snectar
1436113596Snectar	free(template->pw_passwd);
1437113596Snectar	free(template->pw_gecos);
1438113596Snectar	free(template->pw_dir);
1439113596Snectar	free(template->pw_shell);
1440113596Snectar	memset(template, 0, sizeof(*template));
1441113596Snectar}
1442113596Snectar
1443113596Snectar
144465532Snectarstatic int
1445113596Snectarcompat_set_template(struct passwd *src, struct passwd *template)
144615267Swpaul{
144715267Swpaul
1448113596Snectar	compat_clear_template(template);
1449113596Snectar#ifdef PW_OVERRIDE_PASSWD
1450113596Snectar	if ((src->pw_fields & _PWF_PASSWD) &&
1451113596Snectar	    (template->pw_passwd = strdup(src->pw_passwd)) == NULL)
1452113596Snectar		goto enomem;
1453113596Snectar#endif
1454113596Snectar	if (src->pw_fields & _PWF_UID)
1455113596Snectar		template->pw_uid = src->pw_uid;
1456113596Snectar	if (src->pw_fields & _PWF_GID)
1457113596Snectar		template->pw_gid = src->pw_gid;
1458113596Snectar	if ((src->pw_fields & _PWF_GECOS) &&
1459113596Snectar	    (template->pw_gecos = strdup(src->pw_gecos)) == NULL)
1460113596Snectar		goto enomem;
1461113596Snectar	if ((src->pw_fields & _PWF_DIR) &&
1462113596Snectar	    (template->pw_dir = strdup(src->pw_dir)) == NULL)
1463113596Snectar		goto enomem;
1464113596Snectar	if ((src->pw_fields & _PWF_SHELL) &&
1465113596Snectar	    (template->pw_shell = strdup(src->pw_shell)) == NULL)
1466113596Snectar		goto enomem;
1467113596Snectar	template->pw_fields = src->pw_fields;
1468113596Snectar	return (0);
1469113596Snectarenomem:
1470113596Snectar	syslog(LOG_ERR, "getpwent memory allocation failure");
1471113596Snectar	return (-1);
1472113596Snectar}
147315267Swpaul
147415267Swpaul
1475113596Snectarstatic int
1476113596Snectarcompat_use_template(struct passwd *pwd, struct passwd *template, char *buffer,
1477113596Snectar    size_t bufsize)
1478113596Snectar{
1479113596Snectar	struct passwd hold;
1480113596Snectar	char	*copy, *p, *q, *eob;
1481113596Snectar	size_t	 n;
1482113596Snectar
1483113596Snectar	/* We cannot know the layout of the password fields in `buffer',
1484113596Snectar	 * so we have to copy everything.
1485113596Snectar	 */
1486113596Snectar	if (template->pw_fields == 0) /* nothing to fill-in */
1487113596Snectar		return (0);
1488113596Snectar	n = 0;
1489113596Snectar	n += pwd->pw_name != NULL ? strlen(pwd->pw_name) + 1 : 0;
1490113596Snectar	n += pwd->pw_passwd != NULL ? strlen(pwd->pw_passwd) + 1 : 0;
1491113596Snectar	n += pwd->pw_class != NULL ? strlen(pwd->pw_class) + 1 : 0;
1492113596Snectar	n += pwd->pw_gecos != NULL ? strlen(pwd->pw_gecos) + 1 : 0;
1493113596Snectar	n += pwd->pw_dir != NULL ? strlen(pwd->pw_dir) + 1 : 0;
1494113596Snectar	n += pwd->pw_shell != NULL ? strlen(pwd->pw_shell) + 1 : 0;
1495113596Snectar	copy = malloc(n);
1496113596Snectar	if (copy == NULL) {
1497113596Snectar		syslog(LOG_ERR, "getpwent memory allocation failure");
1498113596Snectar		return (ENOMEM);
1499113596Snectar	}
1500113596Snectar	p = copy;
1501113596Snectar	eob = &copy[n];
1502113596Snectar#define COPY(field) do {				\
1503113596Snectar	if (pwd->field == NULL)				\
1504113596Snectar		hold.field = NULL;			\
1505113596Snectar	else {						\
1506113596Snectar		hold.field = p;				\
1507114443Snectar		p += strlcpy(p, pwd->field, eob-p) + 1;	\
1508113596Snectar	}						\
1509113596Snectar} while (0)
1510113596Snectar	COPY(pw_name);
1511113596Snectar	COPY(pw_passwd);
1512113596Snectar	COPY(pw_class);
1513113596Snectar	COPY(pw_gecos);
1514113596Snectar	COPY(pw_dir);
1515113596Snectar	COPY(pw_shell);
1516113596Snectar#undef COPY
1517113596Snectar	p = buffer;
1518113596Snectar	eob = &buffer[bufsize];
1519113596Snectar#define COPY(field, flag) do {						 \
1520113596Snectar	q = (template->pw_fields & flag) ? template->field : hold.field; \
1521113596Snectar	if (q == NULL)							 \
1522113596Snectar		pwd->field = NULL;					 \
1523113596Snectar	else {								 \
1524113596Snectar		pwd->field = p;						 \
1525114443Snectar		if ((n = strlcpy(p, q, eob-p)) >= eob-p) {		 \
1526113596Snectar			free(copy);					 \
1527113596Snectar			return (ERANGE);				 \
1528113596Snectar		}							 \
1529113596Snectar		p += n + 1;						 \
1530113596Snectar	}								 \
1531113596Snectar} while (0)
1532113596Snectar	COPY(pw_name, 0);
1533113596Snectar#ifdef PW_OVERRIDE_PASSWD
1534113596Snectar	COPY(pw_passwd, _PWF_PASSWD);
1535113596Snectar#else
1536113596Snectar	COPY(pw_passwd, 0);
1537113596Snectar#endif
1538113596Snectar	COPY(pw_class, 0);
1539113596Snectar	COPY(pw_gecos, _PWF_GECOS);
1540113596Snectar	COPY(pw_dir, _PWF_DIR);
1541113596Snectar	COPY(pw_shell, _PWF_SHELL);
1542113596Snectar#undef COPY
1543113596Snectar#define COPY(field, flag) do {			\
1544113596Snectar	if (template->pw_fields & flag)		\
1545113596Snectar		pwd->field = template->field;	\
1546113596Snectar} while (0)
1547113596Snectar	COPY(pw_uid, _PWF_UID);
1548113596Snectar	COPY(pw_gid, _PWF_GID);
1549113596Snectar#undef COPY
1550113596Snectar	free(copy);
1551113596Snectar	return (0);
155215267Swpaul}
155315267Swpaul
155465532Snectar
155565532Snectarstatic int
1556113596Snectarcompat_exclude(const char *name, DB **db)
155715267Swpaul{
1558113596Snectar	DBT	key, data;
1559113596Snectar
1560113596Snectar	if (*db == NULL &&
1561113596Snectar	    (*db = dbopen(NULL, O_RDWR, 600, DB_HASH, 0)) == NULL)
1562113596Snectar		return (errno);
1563113596Snectar	key.size = strlen(name);
1564113596Snectar	key.data = (char *)name;
1565113596Snectar	data.size = 0;
1566113596Snectar	data.data = NULL;
1567113596Snectar
1568113596Snectar	if ((*db)->put(*db, &key, &data, 0) == -1)
1569113596Snectar		return (errno);
1570113596Snectar	return (0);
15717258Swpaul}
15727258Swpaul
157365532Snectar
157465532Snectarstatic int
1575113596Snectarcompat_is_excluded(const char *name, DB *db)
15767258Swpaul{
1577113596Snectar	DBT	key, data;
15787258Swpaul
1579113596Snectar	if (db == NULL)
1580113596Snectar		return (0);
1581113596Snectar	key.size = strlen(name);
1582113596Snectar	key.data = (char *)name;
1583113596Snectar	return (db->get(db, &key, &data, 0) == 0);
15847258Swpaul}
15857258Swpaul
158665532Snectar
158765532Snectarstatic int
1588113596Snectarcompat_redispatch(struct compat_state *st, enum nss_lookup_type how,
1589113596Snectar    enum nss_lookup_type lookup_how, const char *name, const char *lookup_name,
1590113596Snectar    uid_t uid, struct passwd *pwd, char *buffer, size_t bufsize, int *errnop)
159110521Swpaul{
1592113596Snectar	static const ns_src compatsrc[] = {
1593113596Snectar#ifdef YP
1594113596Snectar		{ NSSRC_NIS, NS_SUCCESS },
159565532Snectar#endif
1596113596Snectar		{ NULL, 0 }
1597113596Snectar	};
1598113596Snectar	ns_dtab dtab[] = {
1599113596Snectar#ifdef YP
1600113596Snectar		{ NSSRC_NIS, nis_passwd, NULL },
1601113596Snectar#endif
1602113596Snectar#ifdef HESIOD
1603113596Snectar		{ NSSRC_DNS, dns_passwd, NULL },
1604113596Snectar#endif
1605113596Snectar		{ NULL, NULL, NULL }
1606113596Snectar	};
1607113596Snectar	void		*discard;
1608113596Snectar	int		 rv, e, i;
160910521Swpaul
1610113596Snectar	for (i = 0; i < sizeof(dtab)/sizeof(dtab[0]) - 1; i++)
1611113596Snectar		dtab[i].mdata = (void *)lookup_how;
1612113596Snectarmore:
1613113992Snectar	pwd_init(pwd);
1614113596Snectar	switch (lookup_how) {
1615113596Snectar	case nss_lt_all:
1616113596Snectar		rv = _nsdispatch(&discard, dtab, NSDB_PASSWD_COMPAT,
1617113596Snectar		    "getpwent_r", compatsrc, pwd, buffer, bufsize,
1618113596Snectar		    errnop);
1619113596Snectar		break;
1620113596Snectar	case nss_lt_id:
1621113596Snectar		rv = _nsdispatch(&discard, dtab, NSDB_PASSWD_COMPAT,
1622113596Snectar		    "getpwuid_r", compatsrc, uid, pwd, buffer,
1623113596Snectar		    bufsize, errnop);
1624113596Snectar		break;
1625113596Snectar	case nss_lt_name:
1626113596Snectar		rv = _nsdispatch(&discard, dtab, NSDB_PASSWD_COMPAT,
1627113596Snectar		    "getpwnam_r", compatsrc, lookup_name, pwd, buffer,
1628113596Snectar		    bufsize, errnop);
1629113596Snectar		break;
1630113596Snectar	default:
1631113596Snectar		return (NS_UNAVAIL);
1632113596Snectar	}
1633113596Snectar	if (rv != NS_SUCCESS)
1634113596Snectar		return (rv);
1635113596Snectar	if (compat_is_excluded(pwd->pw_name, st->exclude)) {
1636113596Snectar		if (how == nss_lt_all)
1637113596Snectar			goto more;
1638113596Snectar		return (NS_NOTFOUND);
1639113596Snectar	}
1640113596Snectar	e = compat_use_template(pwd, &st->template, buffer, bufsize);
1641113596Snectar	if (e != 0) {
1642113596Snectar		*errnop = e;
1643113596Snectar		if (e == ERANGE)
1644113596Snectar			return (NS_RETURN);
1645113596Snectar		else
1646113596Snectar			return (NS_UNAVAIL);
1647113596Snectar	}
1648113596Snectar	switch (how) {
1649113596Snectar	case nss_lt_name:
1650113596Snectar		if (strcmp(name, pwd->pw_name) != 0)
1651113596Snectar			return (NS_NOTFOUND);
1652113596Snectar		break;
1653113596Snectar	case nss_lt_id:
1654113596Snectar		if (uid != pwd->pw_uid)
1655113596Snectar			return (NS_NOTFOUND);
1656113596Snectar		break;
1657113596Snectar	default:
1658113596Snectar		break;
1659113596Snectar	}
1660113596Snectar	return (NS_SUCCESS);
1661113596Snectar}
166265532Snectar
166365532Snectar
1664113596Snectarstatic void
1665113596Snectarcompat_endstate(void *p)
1666113596Snectar{
1667113596Snectar	struct compat_state *st;
166865532Snectar
1669113596Snectar	if (p == NULL)
1670113596Snectar		return;
1671113596Snectar	st = (struct compat_state *)p;
1672113596Snectar	if (st->db != NULL)
1673113596Snectar		st->db->close(st->db);
1674113596Snectar	if (st->exclude != NULL)
1675113596Snectar		st->exclude->close(st->exclude);
1676113596Snectar	compat_clear_template(&st->template);
1677113596Snectar	free(p);
1678113596Snectar}
167965532Snectar
168065532Snectar
1681113596Snectarstatic int
1682113596Snectarcompat_setpwent(void *retval, void *mdata, va_list ap)
1683113596Snectar{
1684114021Snectar	static const ns_src compatsrc[] = {
1685114021Snectar#ifdef YP
1686114021Snectar		{ NSSRC_NIS, NS_SUCCESS },
1687114021Snectar#endif
1688114021Snectar		{ NULL, 0 }
1689114021Snectar	};
1690114021Snectar	ns_dtab dtab[] = {
1691114021Snectar#ifdef YP
1692114021Snectar		{ NSSRC_NIS, nis_setpwent, NULL },
1693114021Snectar#endif
1694114021Snectar#ifdef HESIOD
1695114021Snectar		{ NSSRC_DNS, dns_setpwent, NULL },
1696114021Snectar#endif
1697114021Snectar		{ NULL, NULL, NULL }
1698114021Snectar	};
1699113596Snectar	struct compat_state	*st;
1700113596Snectar	int			 rv, stayopen;
170165532Snectar
1702114021Snectar#define set_setent(x, y) do {	 				\
1703114021Snectar	int i;							\
1704114021Snectar								\
1705114021Snectar	for (i = 0; i < (sizeof(x)/sizeof(x[0])) - 1; i++)	\
1706114021Snectar		x[i].mdata = (void *)y;				\
1707114021Snectar} while (0)
1708114021Snectar
1709113596Snectar	rv = compat_getstate(&st);
1710113596Snectar	if (rv != 0)
1711113596Snectar		return (NS_UNAVAIL);
1712113596Snectar	switch ((enum constants)mdata) {
1713113596Snectar	case SETPWENT:
1714113596Snectar		stayopen = va_arg(ap, int);
1715113596Snectar		st->keynum = 0;
1716113596Snectar		if (stayopen)
1717113596Snectar			st->db = pwdbopen(&st->version);
1718113596Snectar		st->stayopen = stayopen;
1719114021Snectar		set_setent(dtab, mdata);
1720114021Snectar		(void)_nsdispatch(NULL, dtab, NSDB_PASSWD_COMPAT, "setpwent",
1721114021Snectar		    compatsrc, 0);
1722113596Snectar		break;
1723113596Snectar	case ENDPWENT:
1724113596Snectar		if (st->db != NULL) {
1725113596Snectar			(void)st->db->close(st->db);
1726113596Snectar			st->db = NULL;
172765532Snectar		}
1728114021Snectar		set_setent(dtab, mdata);
1729114021Snectar		(void)_nsdispatch(NULL, dtab, NSDB_PASSWD_COMPAT, "endpwent",
1730114021Snectar		    compatsrc, 0);
1731113596Snectar		break;
1732113596Snectar	default:
1733113596Snectar		break;
173410521Swpaul	}
1735113596Snectar	return (NS_UNAVAIL);
1736114021Snectar#undef set_setent
173710521Swpaul}
173820119Swpaul
173920119Swpaul
17409332Swpaulstatic int
1741113596Snectarcompat_passwd(void *retval, void *mdata, va_list ap)
17422917Swollman{
1743113596Snectar	char			 keybuf[MAXLOGNAME + 1];
1744113596Snectar	DBT			 key, entry;
1745113596Snectar	struct compat_state	*st;
1746113596Snectar	enum nss_lookup_type	 how;
1747113596Snectar	const char		*name;
1748113596Snectar	struct passwd		*pwd;
1749113596Snectar	char			*buffer, *pw_name;
1750113596Snectar	char			*host, *user, *domain;
1751113596Snectar	size_t			 bufsize;
1752113596Snectar	uid_t			 uid;
1753113596Snectar	uint32_t		 store;
1754113691Snectar	int			 rv, from_compat, stayopen, *errnop;
17552917Swollman
1756113691Snectar	from_compat = 0;
175765532Snectar	name = NULL;
1758113596Snectar	uid = (uid_t)-1;
1759113596Snectar	how = (enum nss_lookup_type)mdata;
1760113596Snectar	switch (how) {
1761113596Snectar	case nss_lt_name:
1762113596Snectar		name = va_arg(ap, const char *);
176365532Snectar		break;
1764113596Snectar	case nss_lt_id:
176565532Snectar		uid = va_arg(ap, uid_t);
176665532Snectar		break;
1767113596Snectar	case nss_lt_all:
1768113596Snectar		break;
176965532Snectar	default:
1770113596Snectar		rv = NS_NOTFOUND;
1771113596Snectar		goto fin;
17722917Swollman	}
1773113596Snectar	pwd = va_arg(ap, struct passwd *);
1774113596Snectar	buffer = va_arg(ap, char *);
1775113596Snectar	bufsize = va_arg(ap, size_t);
1776113596Snectar	errnop = va_arg(ap, int *);
1777113596Snectar	*errnop = compat_getstate(&st);
1778113596Snectar	if (*errnop != 0)
1779113596Snectar		return (NS_UNAVAIL);
1780113596Snectar	if (how == nss_lt_all && st->keynum < 0) {
1781113596Snectar		rv = NS_NOTFOUND;
1782113596Snectar		goto fin;
1783113596Snectar	}
1784113596Snectar	if (st->db == NULL &&
1785113596Snectar	    (st->db = pwdbopen(&st->version)) == NULL) {
1786113596Snectar		*errnop = errno;
1787113596Snectar		rv = NS_UNAVAIL;
1788113596Snectar		goto fin;
1789113596Snectar	}
1790113596Snectar	if (how == nss_lt_all) {
1791113596Snectar		if (st->keynum < 0) {
1792113596Snectar			rv = NS_NOTFOUND;
1793113596Snectar			goto fin;
1794113596Snectar		}
1795113596Snectar		stayopen = 1;
1796113596Snectar	} else {
1797113596Snectar		st->keynum = 0;
1798113596Snectar		stayopen = st->stayopen;
1799113596Snectar	}
1800113596Snectardocompat:
1801113596Snectar	rv = NS_NOTFOUND;
1802113596Snectar	switch (st->compat) {
1803113596Snectar	case COMPAT_MODE_ALL:
1804113596Snectar		rv = compat_redispatch(st, how, how, name, name, uid, pwd,
1805113596Snectar		    buffer, bufsize, errnop);
1806113596Snectar		if (rv != NS_SUCCESS)
1807113596Snectar			st->compat = COMPAT_MODE_OFF;
1808113596Snectar		break;
1809113596Snectar	case COMPAT_MODE_NETGROUP:
1810113596Snectar		/* XXX getnetgrent is not thread-safe. */
1811113596Snectar		do {
1812113596Snectar			rv = getnetgrent(&host, &user, &domain);
1813113596Snectar			if (rv == 0) {
1814113596Snectar				endnetgrent();
1815113596Snectar				st->compat = COMPAT_MODE_OFF;
1816113596Snectar				rv = NS_NOTFOUND;
1817113596Snectar				continue;
1818113596Snectar			} else if (user == NULL || user[0] == '\0')
1819113596Snectar				continue;
1820113596Snectar			rv = compat_redispatch(st, how, nss_lt_name, name,
1821113596Snectar			    user, uid, pwd, buffer, bufsize, errnop);
1822113596Snectar		} while (st->compat == COMPAT_MODE_NETGROUP &&
1823113596Snectar		    !(rv & NS_TERMINATE));
1824113596Snectar		break;
1825113596Snectar	case COMPAT_MODE_NAME:
1826113596Snectar		rv = compat_redispatch(st, how, nss_lt_name, name, st->name,
1827113596Snectar		    uid, pwd, buffer, bufsize, errnop);
1828113596Snectar		free(st->name);
1829113596Snectar		st->name = NULL;
1830113596Snectar		st->compat = COMPAT_MODE_OFF;
1831113596Snectar		break;
1832113596Snectar	default:
1833113596Snectar		break;
1834113596Snectar	}
1835113691Snectar	if (rv & NS_TERMINATE) {
1836113691Snectar		from_compat = 1;
1837113596Snectar		goto fin;
1838113691Snectar	}
1839113596Snectar	key.data = keybuf;
1840113596Snectar	rv = NS_NOTFOUND;
1841113596Snectar	while (st->keynum >= 0) {
1842113596Snectar		st->keynum++;
1843113596Snectar		if (st->version < _PWD_CURRENT_VERSION) {
1844113596Snectar			memcpy(&keybuf[1], &st->keynum, sizeof(st->keynum));
1845113596Snectar			key.size = sizeof(st->keynum) + 1;
1846113596Snectar		} else {
1847113596Snectar			store = htonl(st->keynum);
1848113596Snectar			memcpy(&keybuf[1], &store, sizeof(store));
1849113596Snectar			key.size = sizeof(store) + 1;
1850113596Snectar		}
1851113666Snectar		keybuf[0] = _PW_VERSIONED(_PW_KEYBYNUM, st->version);
1852113596Snectar		rv = st->db->get(st->db, &key, &entry, 0);
1853113596Snectar		if (rv < 0 || rv > 1) { /* should never return > 1 */
1854113596Snectar			*errnop = errno;
1855113596Snectar			rv = NS_UNAVAIL;
1856113596Snectar			goto fin;
1857113596Snectar		} else if (rv == 1) {
1858113596Snectar			st->keynum = -1;
1859113596Snectar			rv = NS_NOTFOUND;
1860113596Snectar			goto fin;
1861113596Snectar		}
1862113596Snectar		pw_name = (char *)entry.data;
1863113596Snectar		switch (pw_name[0]) {
186465532Snectar		case '+':
1865113596Snectar			switch (pw_name[1]) {
186665532Snectar			case '\0':
1867113596Snectar				st->compat = COMPAT_MODE_ALL;
186865532Snectar				break;
186965532Snectar			case '@':
1870113596Snectar				setnetgrent(&pw_name[2]);
1871113596Snectar				st->compat = COMPAT_MODE_NETGROUP;
187265532Snectar				break;
187365532Snectar			default:
1874113596Snectar				st->name = strdup(&pw_name[1]);
1875113596Snectar				if (st->name == NULL) {
1876113596Snectar					syslog(LOG_ERR,
1877113596Snectar					 "getpwent memory allocation failure");
1878113596Snectar					*errnop = ENOMEM;
1879113596Snectar					rv = NS_UNAVAIL;
1880113596Snectar					break;
1881113596Snectar				}
1882113596Snectar				st->compat = COMPAT_MODE_NAME;
188365532Snectar			}
1884113596Snectar			if (entry.size > bufsize) {
1885113596Snectar				*errnop = ERANGE;
1886113596Snectar				rv = NS_RETURN;
1887113596Snectar				goto fin;
188865532Snectar			}
1889113596Snectar			memcpy(buffer, entry.data, entry.size);
1890113596Snectar			rv = pwdb_versions[st->version].parse(buffer,
1891113596Snectar			    entry.size, pwd, errnop);
1892113596Snectar			if (rv != NS_SUCCESS)
1893113596Snectar				;
1894113596Snectar			else if (compat_set_template(pwd, &st->template) < 0) {
1895113596Snectar				*errnop = ENOMEM;
1896113596Snectar				rv = NS_UNAVAIL;
1897113596Snectar				goto fin;
1898113596Snectar			}
1899113596Snectar			goto docompat;
190065532Snectar		case '-':
1901113596Snectar			switch (pw_name[1]) {
190265532Snectar			case '\0':
1903113596Snectar				/* XXX Maybe syslog warning */
1904113596Snectar				continue;
190565532Snectar			case '@':
1906113596Snectar				setnetgrent(&pw_name[2]);
1907113596Snectar				while (getnetgrent(&host, &user, &domain) !=
1908126643Smarkm				    0) {
1909113596Snectar					if (user != NULL && user[0] != '\0')
1910113596Snectar						compat_exclude(user,
1911113596Snectar						    &st->exclude);
191265532Snectar				}
191365532Snectar				endnetgrent();
1914113596Snectar				continue;
191565532Snectar			default:
1916113596Snectar				compat_exclude(&pw_name[1], &st->exclude);
1917113596Snectar				continue;
191865532Snectar			}
191965532Snectar			break;
192096186Sdes		default:
1921113596Snectar			break;
19226190Swpaul		}
1923113596Snectar		if (compat_is_excluded((char *)entry.data, st->exclude))
1924113596Snectar			continue;
1925113596Snectar		rv = pwdb_versions[st->version].match(entry.data, entry.size,
1926113596Snectar		    how, name, uid);
1927113596Snectar		if (rv == NS_RETURN)
192865532Snectar			break;
1929113596Snectar		else if (rv != NS_SUCCESS)
1930113596Snectar			continue;
1931113596Snectar		if (entry.size > bufsize) {
1932113596Snectar			*errnop = ERANGE;
1933113596Snectar			rv = NS_RETURN;
1934113596Snectar			break;
193565532Snectar		}
1936113596Snectar		memcpy(buffer, entry.data, entry.size);
1937113596Snectar		rv = pwdb_versions[st->version].parse(buffer, entry.size, pwd,
1938113596Snectar		    errnop);
1939113596Snectar		if (rv & NS_TERMINATE)
1940113596Snectar			break;
19412917Swollman	}
1942113596Snectarfin:
1943113596Snectar	if (!stayopen && st->db != NULL) {
1944113596Snectar		(void)st->db->close(st->db);
1945113596Snectar		st->db = NULL;
19466076Swpaul	}
1947124432Snectar	if (rv == NS_SUCCESS) {
1948113691Snectar		if (!from_compat) {
1949113691Snectar			pwd->pw_fields &= ~_PWF_SOURCE;
1950113691Snectar			pwd->pw_fields |= _PWF_FILES;
1951113691Snectar		}
1952113691Snectar		if (retval != NULL)
1953113691Snectar			*(struct passwd **)retval = pwd;
1954113691Snectar	}
1955113596Snectar	return (rv);
195665532Snectar}
19576076Swpaul
19589332Swpaul
1959113596Snectar/*
1960113596Snectar * common passwd line matching and parsing
1961113596Snectar */
196265532Snectarint
1963113596Snectar__pw_match_entry(const char *entry, size_t entrysize, enum nss_lookup_type how,
1964113596Snectar    const char *name, uid_t uid)
19652917Swollman{
1966113596Snectar	const char	*p, *eom;
1967113596Snectar	char		*q;
1968113596Snectar	size_t		 len;
1969113596Snectar	unsigned long	 m;
19702917Swollman
1971113596Snectar	eom = entry + entrysize;
1972113596Snectar	for (p = entry; p < eom; p++)
1973113596Snectar		if (*p == ':')
1974113596Snectar			break;
1975113596Snectar	if (*p != ':')
1976113596Snectar		return (NS_NOTFOUND);
1977113596Snectar	if (how == nss_lt_all)
1978113596Snectar		return (NS_SUCCESS);
1979113596Snectar	if (how == nss_lt_name) {
1980113596Snectar		len = strlen(name);
1981113596Snectar		if (len == (p - entry) && memcmp(name, entry, len) == 0)
1982113596Snectar			return (NS_SUCCESS);
1983113596Snectar		else
1984113596Snectar			return (NS_NOTFOUND);
198545066Sdes	}
1986113596Snectar	for (p++; p < eom; p++)
1987113596Snectar		if (*p == ':')
1988113596Snectar			break;
1989113596Snectar	if (*p != ':')
1990113596Snectar		return (NS_NOTFOUND);
1991113596Snectar	m = strtoul(++p, &q, 10);
1992113596Snectar	if (q[0] != ':' || (uid_t)m != uid)
1993113596Snectar		return (NS_NOTFOUND);
1994113596Snectar	else
1995113596Snectar		return (NS_SUCCESS);
199665532Snectar}
19972917Swollman
199815267Swpaul
1999113596Snectar/* XXX buffer must be NUL-terminated.  errnop is not set correctly. */
2000113596Snectarint
2001113596Snectar__pw_parse_entry(char *buffer, size_t bufsize __unused, struct passwd *pwd,
2002113596Snectar    int master, int *errnop __unused)
20032917Swollman{
20042917Swollman
2005113596Snectar	if (__pw_scan(buffer, pwd, master ? _PWSCAN_MASTER : 0) == 0)
2006113596Snectar		return (NS_NOTFOUND);
2007113596Snectar	else
2008113596Snectar		return (NS_SUCCESS);
20092917Swollman}
2010