Home | History | Annotate | Line # | Download | only in gen
pwcache.c revision 1.18
      1 /*	$NetBSD: pwcache.c,v 1.18 2002/01/24 04:07:13 lukem Exp $	*/
      2 
      3 /*-
      4  * Copyright (c) 1992 Keith Muller.
      5  * Copyright (c) 1992, 1993
      6  *	The Regents of the University of California.  All rights reserved.
      7  *
      8  * This code is derived from software contributed to Berkeley by
      9  * Keith Muller of the University of California, San Diego.
     10  *
     11  * Redistribution and use in source and binary forms, with or without
     12  * modification, are permitted provided that the following conditions
     13  * are met:
     14  * 1. Redistributions of source code must retain the above copyright
     15  *    notice, this list of conditions and the following disclaimer.
     16  * 2. Redistributions in binary form must reproduce the above copyright
     17  *    notice, this list of conditions and the following disclaimer in the
     18  *    documentation and/or other materials provided with the distribution.
     19  * 3. All advertising materials mentioning features or use of this software
     20  *    must display the following acknowledgement:
     21  *	This product includes software developed by the University of
     22  *	California, Berkeley and its contributors.
     23  * 4. Neither the name of the University nor the names of its contributors
     24  *    may be used to endorse or promote products derived from this software
     25  *    without specific prior written permission.
     26  *
     27  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
     28  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
     29  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
     30  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
     31  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
     32  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
     33  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
     34  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
     35  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
     36  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
     37  * SUCH DAMAGE.
     38  */
     39 
     40 /*-
     41  * Copyright (c) 2002 The NetBSD Foundation, Inc.
     42  * All rights reserved.
     43  *
     44  * Redistribution and use in source and binary forms, with or without
     45  * modification, are permitted provided that the following conditions
     46  * are met:
     47  * 1. Redistributions of source code must retain the above copyright
     48  *    notice, this list of conditions and the following disclaimer.
     49  * 2. Redistributions in binary form must reproduce the above copyright
     50  *    notice, this list of conditions and the following disclaimer in the
     51  *    documentation and/or other materials provided with the distribution.
     52  * 3. All advertising materials mentioning features or use of this software
     53  *    must display the following acknowledgement:
     54  *        This product includes software developed by the NetBSD
     55  *        Foundation, Inc. and its contributors.
     56  * 4. Neither the name of The NetBSD Foundation nor the names of its
     57  *    contributors may be used to endorse or promote products derived
     58  *    from this software without specific prior written permission.
     59  *
     60  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
     61  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
     62  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
     63  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
     64  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
     65  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
     66  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
     67  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
     68  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
     69  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
     70  * POSSIBILITY OF SUCH DAMAGE.
     71  */
     72 
     73 #if HAVE_CONFIG_H
     74 #include "config.h"
     75 #else
     76 #include <sys/cdefs.h>
     77 #if defined(LIBC_SCCS) && !defined(lint)
     78 #if 0
     79 static char sccsid[] = "@(#)cache.c	8.1 (Berkeley) 5/31/93";
     80 #else
     81 __RCSID("$NetBSD: pwcache.c,v 1.18 2002/01/24 04:07:13 lukem Exp $");
     82 #endif
     83 #endif /* LIBC_SCCS and not lint */
     84 
     85 #include "namespace.h"
     86 
     87 #include <sys/types.h>
     88 #include <sys/param.h>
     89 
     90 #include <assert.h>
     91 #include <grp.h>
     92 #include <pwd.h>
     93 #include <stdio.h>
     94 #include <stdlib.h>
     95 #include <string.h>
     96 #include <unistd.h>
     97 
     98 #endif	/* HAVE_CONFIG_H */
     99 
    100 #if !HAVE_PWCACHE_USERDB
    101 #include "pwcache.h"
    102 
    103 #ifdef __weak_alias
    104 __weak_alias(user_from_uid,_user_from_uid)
    105 __weak_alias(group_from_gid,_group_from_gid)
    106 __weak_alias(pwcache_userdb,_pwcache_userdb)
    107 __weak_alias(pwcache_groupdb,_pwcache_groupdb)
    108 #endif
    109 
    110 /*
    111  * routines that control user, group, uid and gid caches (for the archive
    112  * member print routine).
    113  * IMPORTANT:
    114  * these routines cache BOTH hits and misses, a major performance improvement
    115  */
    116 
    117 /*
    118  * function pointers to various name lookup routines.
    119  * these may be changed as necessary.
    120  */
    121 static	int		(*_pwcache_setgroupent)(int)		= setgroupent;
    122 static	void		(*_pwcache_endgrent)(void)		= endgrent;
    123 static	struct group *	(*_pwcache_getgrnam)(const char *)	= getgrnam;
    124 static	struct group *	(*_pwcache_getgrgid)(gid_t)		= getgrgid;
    125 static	int		(*_pwcache_setpassent)(int)		= setpassent;
    126 static	void		(*_pwcache_endpwent)(void)		= endpwent;
    127 static	struct passwd *	(*_pwcache_getpwnam)(const char *)	= getpwnam;
    128 static	struct passwd *	(*_pwcache_getpwuid)(uid_t)		= getpwuid;
    129 
    130 /*
    131  * internal state
    132  */
    133 static	int	pwopn;		/* is password file open */
    134 static	int	gropn;		/* is group file open */
    135 static	UIDC	**uidtb;	/* uid to name cache */
    136 static	GIDC	**gidtb;	/* gid to name cache */
    137 static	UIDC	**usrtb;	/* user name to uid cache */
    138 static	GIDC	**grptb;	/* group name to gid cache */
    139 
    140 static	int	uidtb_fail;	/* uidtb_start() failed ? */
    141 static	int	gidtb_fail;	/* gidtb_start() failed ? */
    142 static	int	usrtb_fail;	/* usrtb_start() failed ? */
    143 static	int	grptb_fail;	/* grptb_start() failed ? */
    144 
    145 
    146 static	u_int	st_hash(const char *, size_t, int);
    147 static	int	uidtb_start(void);
    148 static	int	gidtb_start(void);
    149 static	int	usrtb_start(void);
    150 static	int	grptb_start(void);
    151 
    152 
    153 static u_int
    154 st_hash(const char *name, size_t len, int tabsz)
    155 {
    156 	u_int key = 0;
    157 
    158 	_DIAGASSERT(name != NULL);
    159 
    160 	while (len--) {
    161 		key += *name++;
    162 		key = (key << 8) | (key >> 24);
    163 	}
    164 
    165 	return (key % tabsz);
    166 }
    167 
    168 /*
    169  * uidtb_start
    170  *	creates an an empty uidtb
    171  * Return:
    172  *	0 if ok, -1 otherwise
    173  */
    174 static int
    175 uidtb_start(void)
    176 {
    177 
    178 	if (uidtb != NULL)
    179 		return (0);
    180 	if (uidtb_fail)
    181 		return (-1);
    182 	if ((uidtb = (UIDC **)calloc(UID_SZ, sizeof(UIDC *))) == NULL) {
    183 		++uidtb_fail;
    184 		return (-1);
    185 	}
    186 	return (0);
    187 }
    188 
    189 /*
    190  * gidtb_start
    191  *	creates an an empty gidtb
    192  * Return:
    193  *	0 if ok, -1 otherwise
    194  */
    195 static int
    196 gidtb_start(void)
    197 {
    198 
    199 	if (gidtb != NULL)
    200 		return (0);
    201 	if (gidtb_fail)
    202 		return (-1);
    203 	if ((gidtb = (GIDC **)calloc(GID_SZ, sizeof(GIDC *))) == NULL) {
    204 		++gidtb_fail;
    205 		return (-1);
    206 	}
    207 	return (0);
    208 }
    209 
    210 /*
    211  * usrtb_start
    212  *	creates an an empty usrtb
    213  * Return:
    214  *	0 if ok, -1 otherwise
    215  */
    216 static int
    217 usrtb_start(void)
    218 {
    219 
    220 	if (usrtb != NULL)
    221 		return (0);
    222 	if (usrtb_fail)
    223 		return (-1);
    224 	if ((usrtb = (UIDC **)calloc(UNM_SZ, sizeof(UIDC *))) == NULL) {
    225 		++usrtb_fail;
    226 		return (-1);
    227 	}
    228 	return (0);
    229 }
    230 
    231 /*
    232  * grptb_start
    233  *	creates an an empty grptb
    234  * Return:
    235  *	0 if ok, -1 otherwise
    236  */
    237 static int
    238 grptb_start(void)
    239 {
    240 
    241 	if (grptb != NULL)
    242 		return (0);
    243 	if (grptb_fail)
    244 		return (-1);
    245 	if ((grptb = (GIDC **)calloc(GNM_SZ, sizeof(GIDC *))) == NULL) {
    246 		++grptb_fail;
    247 		return (-1);
    248 	}
    249 	return (0);
    250 }
    251 
    252 /*
    253  * user_from_uid()
    254  *	caches the name (if any) for the uid. If noname clear, we always
    255  *	return the the stored name (if valid or invalid match).
    256  *	We use a simple hash table.
    257  * Return
    258  *	Pointer to stored name (or a empty string)
    259  */
    260 
    261 const char *
    262 user_from_uid(uid_t uid, int noname)
    263 {
    264 	struct passwd *pw;
    265 	UIDC *ptr, **pptr;
    266 
    267 	if ((uidtb == NULL) && (uidtb_start() < 0))
    268 		return (NULL);
    269 
    270 	/*
    271 	 * see if we have this uid cached
    272 	 */
    273 	pptr = uidtb + (uid % UID_SZ);
    274 	ptr = *pptr;
    275 
    276 	if ((ptr != NULL) && (ptr->valid > 0) && (ptr->uid == uid)) {
    277 		/*
    278 		 * have an entry for this uid
    279 		 */
    280 		if (!noname || (ptr->valid == VALID))
    281 			return (ptr->name);
    282 		return (NULL);
    283 	}
    284 
    285 	/*
    286 	 * No entry for this uid, we will add it
    287 	 */
    288 	if (!pwopn) {
    289 		if (_pwcache_setpassent != NULL)
    290 			(*_pwcache_setpassent)(1);
    291 		++pwopn;
    292 	}
    293 
    294 	if (ptr == NULL)
    295 		*pptr = ptr = (UIDC *)malloc(sizeof(UIDC));
    296 
    297 	if ((pw = (*_pwcache_getpwuid)(uid)) == NULL) {
    298 		/*
    299 		 * no match for this uid in the local password file
    300 		 * a string that is the uid in numberic format
    301 		 */
    302 		if (ptr == NULL)
    303 			return (NULL);
    304 		ptr->uid = uid;
    305 		(void)snprintf(ptr->name, UNMLEN, "%lu", (long) uid);
    306 		ptr->valid = INVALID;
    307 		if (noname)
    308 			return (NULL);
    309 	} else {
    310 		/*
    311 		 * there is an entry for this uid in the password file
    312 		 */
    313 		if (ptr == NULL)
    314 			return (pw->pw_name);
    315 		ptr->uid = uid;
    316 		(void)strlcpy(ptr->name, pw->pw_name, UNMLEN);
    317 		ptr->valid = VALID;
    318 	}
    319 	return (ptr->name);
    320 }
    321 
    322 /*
    323  * group_from_gid()
    324  *	caches the name (if any) for the gid. If noname clear, we always
    325  *	return the the stored name (if valid or invalid match).
    326  *	We use a simple hash table.
    327  * Return
    328  *	Pointer to stored name (or a empty string)
    329  */
    330 
    331 const char *
    332 group_from_gid(gid_t gid, int noname)
    333 {
    334 	struct group *gr;
    335 	GIDC *ptr, **pptr;
    336 
    337 	if ((gidtb == NULL) && (gidtb_start() < 0))
    338 		return (NULL);
    339 
    340 	/*
    341 	 * see if we have this gid cached
    342 	 */
    343 	pptr = gidtb + (gid % GID_SZ);
    344 	ptr = *pptr;
    345 
    346 	if ((ptr != NULL) && (ptr->valid > 0) && (ptr->gid == gid)) {
    347 		/*
    348 		 * have an entry for this gid
    349 		 */
    350 		if (!noname || (ptr->valid == VALID))
    351 			return (ptr->name);
    352 		return (NULL);
    353 	}
    354 
    355 	/*
    356 	 * No entry for this gid, we will add it
    357 	 */
    358 	if (!gropn) {
    359 		if (_pwcache_setgroupent != NULL)
    360 			(*_pwcache_setgroupent)(1);
    361 		++gropn;
    362 	}
    363 
    364 	if (ptr == NULL)
    365 		*pptr = ptr = (GIDC *)malloc(sizeof(GIDC));
    366 
    367 	if ((gr = (*_pwcache_getgrgid)(gid)) == NULL) {
    368 		/*
    369 		 * no match for this gid in the local group file, put in
    370 		 * a string that is the gid in numberic format
    371 		 */
    372 		if (ptr == NULL)
    373 			return (NULL);
    374 		ptr->gid = gid;
    375 		(void)snprintf(ptr->name, GNMLEN, "%lu", (long) gid);
    376 		ptr->valid = INVALID;
    377 		if (noname)
    378 			return (NULL);
    379 	} else {
    380 		/*
    381 		 * there is an entry for this group in the group file
    382 		 */
    383 		if (ptr == NULL)
    384 			return (gr->gr_name);
    385 		ptr->gid = gid;
    386 		(void)strlcpy(ptr->name, gr->gr_name, GNMLEN);
    387 		ptr->valid = VALID;
    388 	}
    389 	return (ptr->name);
    390 }
    391 
    392 /*
    393  * uid_from_user()
    394  *	caches the uid for a given user name. We use a simple hash table.
    395  * Return
    396  *	the uid (if any) for a user name, or a -1 if no match can be found
    397  */
    398 
    399 int
    400 uid_from_user(const char *name, uid_t *uid)
    401 {
    402 	struct passwd *pw;
    403 	UIDC *ptr, **pptr;
    404 	size_t namelen;
    405 
    406 	/*
    407 	 * return -1 for mangled names
    408 	 */
    409 	if (name == NULL || ((namelen = strlen(name)) == 0))
    410 		return (-1);
    411 	if ((usrtb == NULL) && (usrtb_start() < 0))
    412 		return (-1);
    413 
    414 	/*
    415 	 * look up in hash table, if found and valid return the uid,
    416 	 * if found and invalid, return a -1
    417 	 */
    418 	pptr = usrtb + st_hash(name, namelen, UNM_SZ);
    419 	ptr = *pptr;
    420 
    421 	if ((ptr != NULL) && (ptr->valid > 0) && !strcmp(name, ptr->name)) {
    422 		if (ptr->valid == INVALID)
    423 			return (-1);
    424 		*uid = ptr->uid;
    425 		return (0);
    426 	}
    427 
    428 	if (!pwopn) {
    429 		if (_pwcache_setpassent != NULL)
    430 			(*_pwcache_setpassent)(1);
    431 		++pwopn;
    432 	}
    433 
    434 	if (ptr == NULL)
    435 		*pptr = ptr = (UIDC *)malloc(sizeof(UIDC));
    436 
    437 	/*
    438 	 * no match, look it up, if no match store it as an invalid entry,
    439 	 * or store the matching uid
    440 	 */
    441 	if (ptr == NULL) {
    442 		if ((pw = (*_pwcache_getpwnam)(name)) == NULL)
    443 			return (-1);
    444 		*uid = pw->pw_uid;
    445 		return (0);
    446 	}
    447 	(void)strlcpy(ptr->name, name, UNMLEN);
    448 	if ((pw = (*_pwcache_getpwnam)(name)) == NULL) {
    449 		ptr->valid = INVALID;
    450 		return (-1);
    451 	}
    452 	ptr->valid = VALID;
    453 	*uid = ptr->uid = pw->pw_uid;
    454 	return (0);
    455 }
    456 
    457 /*
    458  * gid_from_group()
    459  *	caches the gid for a given group name. We use a simple hash table.
    460  * Return
    461  *	the gid (if any) for a group name, or a -1 if no match can be found
    462  */
    463 
    464 int
    465 gid_from_group(const char *name, gid_t *gid)
    466 {
    467 	struct group *gr;
    468 	GIDC *ptr, **pptr;
    469 	size_t namelen;
    470 
    471 	/*
    472 	 * return -1 for mangled names
    473 	 */
    474 	if (name == NULL || ((namelen = strlen(name)) == 0))
    475 		return (-1);
    476 	if ((grptb == NULL) && (grptb_start() < 0))
    477 		return (-1);
    478 
    479 	/*
    480 	 * look up in hash table, if found and valid return the uid,
    481 	 * if found and invalid, return a -1
    482 	 */
    483 	pptr = grptb + st_hash(name, namelen, GID_SZ);
    484 	ptr = *pptr;
    485 
    486 	if ((ptr != NULL) && (ptr->valid > 0) && !strcmp(name, ptr->name)) {
    487 		if (ptr->valid == INVALID)
    488 			return (-1);
    489 		*gid = ptr->gid;
    490 		return (0);
    491 	}
    492 
    493 	if (!gropn) {
    494 		if (_pwcache_setgroupent != NULL)
    495 			(*_pwcache_setgroupent)(1);
    496 		++gropn;
    497 	}
    498 
    499 	if (ptr == NULL)
    500 		*pptr = ptr = (GIDC *)malloc(sizeof(GIDC));
    501 
    502 	/*
    503 	 * no match, look it up, if no match store it as an invalid entry,
    504 	 * or store the matching gid
    505 	 */
    506 	if (ptr == NULL) {
    507 		if ((gr = (*_pwcache_getgrnam)(name)) == NULL)
    508 			return (-1);
    509 		*gid = gr->gr_gid;
    510 		return (0);
    511 	}
    512 
    513 	(void)strlcpy(ptr->name, name, GNMLEN);
    514 	if ((gr = (*_pwcache_getgrnam)(name)) == NULL) {
    515 		ptr->valid = INVALID;
    516 		return (-1);
    517 	}
    518 	ptr->valid = VALID;
    519 	*gid = ptr->gid = gr->gr_gid;
    520 	return (0);
    521 }
    522 
    523 #define FLUSHTB(arr, len, fail)				\
    524 	do {						\
    525 		if (arr != NULL) {			\
    526 			for (i = 0; i < len; i++)	\
    527 				if (arr[i] != NULL)	\
    528 					free(arr[i]);	\
    529 			arr = NULL;			\
    530 		}					\
    531 		fail = 0;				\
    532 	} while (/* CONSTCOND */0);
    533 
    534 int
    535 pwcache_userdb(
    536 	int		(*a_setpassent)(int),
    537 	void		(*a_endpwent)(void),
    538 	struct passwd *	(*a_getpwnam)(const char *),
    539 	struct passwd *	(*a_getpwuid)(uid_t))
    540 {
    541 	int i;
    542 
    543 		/* a_setpassent and a_endpwent may be NULL */
    544 	if (a_getpwnam == NULL || a_getpwuid == NULL)
    545 		return (-1);
    546 
    547 	if (_pwcache_endpwent != NULL)
    548 		(*_pwcache_endpwent)();
    549 	FLUSHTB(uidtb, UID_SZ, uidtb_fail);
    550 	FLUSHTB(usrtb, UNM_SZ, usrtb_fail);
    551 	pwopn = 0;
    552 	_pwcache_setpassent = a_setpassent;
    553 	_pwcache_endpwent = a_endpwent;
    554 	_pwcache_getpwnam = a_getpwnam;
    555 	_pwcache_getpwuid = a_getpwuid;
    556 
    557 	return (0);
    558 }
    559 
    560 int
    561 pwcache_groupdb(
    562 	int		(*a_setgroupent)(int),
    563 	void		(*a_endgrent)(void),
    564 	struct group *	(*a_getgrnam)(const char *),
    565 	struct group *	(*a_getgrgid)(gid_t))
    566 {
    567 	int i;
    568 
    569 		/* a_setgroupent and a_endgrent may be NULL */
    570 	if (a_getgrnam == NULL || a_getgrgid == NULL)
    571 		return (-1);
    572 
    573 	if (_pwcache_endgrent != NULL)
    574 		(*_pwcache_endgrent)();
    575 	FLUSHTB(gidtb, GID_SZ, gidtb_fail);
    576 	FLUSHTB(grptb, GNM_SZ, grptb_fail);
    577 	gropn = 0;
    578 	_pwcache_setgroupent = a_setgroupent;
    579 	_pwcache_endgrent = a_endgrent;
    580 	_pwcache_getgrnam = a_getgrnam;
    581 	_pwcache_getgrgid = a_getgrgid;
    582 
    583 	return (0);
    584 }
    585 
    586 
    587 #ifdef TEST_PWCACHE
    588 
    589 struct passwd *
    590 test_getpwnam(const char *name)
    591 {
    592 	static struct passwd foo;
    593 
    594 	memset(&foo, 0, sizeof(foo));
    595 	if (strcmp(name, "toor") == 0) {
    596 		foo.pw_uid = 666;
    597 		return &foo;
    598 	}
    599 	return (getpwnam(name));
    600 }
    601 
    602 int
    603 main(int argc, char *argv[])
    604 {
    605 	uid_t	u;
    606 	int	r, i;
    607 
    608 	printf("pass 1 (default userdb)\n");
    609 	for (i = 1; i < argc; i++) {
    610 		printf("i: %d, pwopn %d usrtb_fail %d usrtb %p\n",
    611 		    i, pwopn, usrtb_fail, usrtb);
    612 		r = uid_from_user(argv[i], &u);
    613 		if (r == -1)
    614 			printf("  uid_from_user %s: failed\n", argv[i]);
    615 		else
    616 			printf("  uid_from_user %s: %d\n", argv[i], u);
    617 	}
    618 	printf("pass 1 finish: pwopn %d usrtb_fail %d usrtb %p\n",
    619 		    pwopn, usrtb_fail, usrtb);
    620 
    621 	puts("");
    622 	printf("pass 2 (replacement userdb)\n");
    623 	printf("pwcache_userdb returned %d\n",
    624 	    pwcache_userdb(setpassent, test_getpwnam, getpwuid));
    625 	printf("pwopn %d usrtb_fail %d usrtb %p\n", pwopn, usrtb_fail, usrtb);
    626 
    627 	for (i = 1; i < argc; i++) {
    628 		printf("i: %d, pwopn %d usrtb_fail %d usrtb %p\n",
    629 		    i, pwopn, usrtb_fail, usrtb);
    630 		u = -1;
    631 		r = uid_from_user(argv[i], &u);
    632 		if (r == -1)
    633 			printf("  uid_from_user %s: failed\n", argv[i]);
    634 		else
    635 			printf("  uid_from_user %s: %d\n", argv[i], u);
    636 	}
    637 	printf("pass 2 finish: pwopn %d usrtb_fail %d usrtb %p\n",
    638 		    pwopn, usrtb_fail, usrtb);
    639 
    640 	puts("");
    641 	printf("pass 3 (null pointers)\n");
    642 	printf("pwcache_userdb returned %d\n",
    643 	    pwcache_userdb(NULL, NULL, NULL));
    644 
    645 	return (0);
    646 }
    647 #endif	/* TEST_PWCACHE */
    648 #endif	/* !HAVE_PWCACHE_USERDB */
    649