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