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