1241233Sbrooks/*	$NetBSD: pwcache.c,v 1.31 2010/03/23 20:28:59 drochner Exp $	*/
2241233Sbrooks
3241233Sbrooks/*-
4241233Sbrooks * Copyright (c) 1992 Keith Muller.
5241233Sbrooks * Copyright (c) 1992, 1993
6241233Sbrooks *	The Regents of the University of California.  All rights reserved.
7241233Sbrooks *
8241233Sbrooks * This code is derived from software contributed to Berkeley by
9241233Sbrooks * Keith Muller of the University of California, San Diego.
10241233Sbrooks *
11241233Sbrooks * Redistribution and use in source and binary forms, with or without
12241233Sbrooks * modification, are permitted provided that the following conditions
13241233Sbrooks * are met:
14241233Sbrooks * 1. Redistributions of source code must retain the above copyright
15241233Sbrooks *    notice, this list of conditions and the following disclaimer.
16241233Sbrooks * 2. Redistributions in binary form must reproduce the above copyright
17241233Sbrooks *    notice, this list of conditions and the following disclaimer in the
18241233Sbrooks *    documentation and/or other materials provided with the distribution.
19241233Sbrooks * 3. Neither the name of the University nor the names of its contributors
20241233Sbrooks *    may be used to endorse or promote products derived from this software
21241233Sbrooks *    without specific prior written permission.
22241233Sbrooks *
23241233Sbrooks * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
24241233Sbrooks * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
25241233Sbrooks * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
26241233Sbrooks * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
27241233Sbrooks * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
28241233Sbrooks * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
29241233Sbrooks * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
30241233Sbrooks * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
31241233Sbrooks * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
32241233Sbrooks * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
33241233Sbrooks * SUCH DAMAGE.
34241233Sbrooks */
35241233Sbrooks
36241233Sbrooks/*-
37241233Sbrooks * Copyright (c) 2002 The NetBSD Foundation, Inc.
38241233Sbrooks * All rights reserved.
39241233Sbrooks *
40241233Sbrooks * Redistribution and use in source and binary forms, with or without
41241233Sbrooks * modification, are permitted provided that the following conditions
42241233Sbrooks * are met:
43241233Sbrooks * 1. Redistributions of source code must retain the above copyright
44241233Sbrooks *    notice, this list of conditions and the following disclaimer.
45241233Sbrooks * 2. Redistributions in binary form must reproduce the above copyright
46241233Sbrooks *    notice, this list of conditions and the following disclaimer in the
47241233Sbrooks *    documentation and/or other materials provided with the distribution.
48241233Sbrooks *
49241233Sbrooks * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
50241233Sbrooks * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
51241233Sbrooks * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
52241233Sbrooks * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
53241233Sbrooks * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
54241233Sbrooks * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
55241233Sbrooks * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
56241233Sbrooks * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
57241233Sbrooks * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
58241233Sbrooks * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
59241233Sbrooks * POSSIBILITY OF SUCH DAMAGE.
60241233Sbrooks */
61241233Sbrooks
62241233Sbrooks#if HAVE_NBTOOL_CONFIG_H
63241233Sbrooks#include "nbtool_config.h"
64241233Sbrooks/*
65241233Sbrooks * XXX Undefine the renames of these functions so that we don't
66241233Sbrooks * XXX rename the versions found in the host's <pwd.h> by mistake!
67241233Sbrooks */
68241233Sbrooks#undef group_from_gid
69241233Sbrooks#undef user_from_uid
70241233Sbrooks#endif
71241233Sbrooks
72241233Sbrooks#include <sys/cdefs.h>
73241233Sbrooks#if defined(LIBC_SCCS) && !defined(lint)
74241233Sbrooks#if 0
75241233Sbrooksstatic char sccsid[] = "@(#)cache.c	8.1 (Berkeley) 5/31/93";
76241233Sbrooks#else
77241233Sbrooks__RCSID("$NetBSD: pwcache.c,v 1.31 2010/03/23 20:28:59 drochner Exp $");
78241233Sbrooks#endif
79241233Sbrooks#endif /* LIBC_SCCS and not lint */
80241731Sbrooks__FBSDID("$FreeBSD$");
81241233Sbrooks
82241233Sbrooks#include "namespace.h"
83241233Sbrooks
84241233Sbrooks#include <sys/types.h>
85241233Sbrooks#include <sys/param.h>
86241233Sbrooks
87241233Sbrooks#include <assert.h>
88241233Sbrooks#include <grp.h>
89241233Sbrooks#include <pwd.h>
90241233Sbrooks#include <stdio.h>
91241233Sbrooks#include <stdlib.h>
92241233Sbrooks#include <string.h>
93241233Sbrooks#include <unistd.h>
94241233Sbrooks
95241731Sbrooks#define	_DIAGASSERT(x)	assert((x))
96241731Sbrooks
97241233Sbrooks#if HAVE_NBTOOL_CONFIG_H
98241233Sbrooks/* XXX Now, re-apply the renaming that we undid above. */
99241233Sbrooks#define	group_from_gid	__nbcompat_group_from_gid
100241233Sbrooks#define	user_from_uid	__nbcompat_user_from_uid
101241233Sbrooks#endif
102241233Sbrooks
103241233Sbrooks#ifdef __weak_alias
104241233Sbrooks__weak_alias(user_from_uid,_user_from_uid)
105241233Sbrooks__weak_alias(group_from_gid,_group_from_gid)
106241233Sbrooks__weak_alias(pwcache_groupdb,_pwcache_groupdb)
107241233Sbrooks#endif
108241233Sbrooks
109241233Sbrooks#if !HAVE_PWCACHE_USERDB || HAVE_NBTOOL_CONFIG_H
110241233Sbrooks#include "pwcache.h"
111241233Sbrooks
112241233Sbrooks/*
113241233Sbrooks * routines that control user, group, uid and gid caches (for the archive
114241233Sbrooks * member print routine).
115241233Sbrooks * IMPORTANT:
116241233Sbrooks * these routines cache BOTH hits and misses, a major performance improvement
117241233Sbrooks */
118241233Sbrooks
119241233Sbrooks/*
120241233Sbrooks * function pointers to various name lookup routines.
121241233Sbrooks * these may be changed as necessary.
122241233Sbrooks */
123241233Sbrooksstatic	int		(*_pwcache_setgroupent)(int)		= setgroupent;
124241233Sbrooksstatic	void		(*_pwcache_endgrent)(void)		= endgrent;
125241233Sbrooksstatic	struct group *	(*_pwcache_getgrnam)(const char *)	= getgrnam;
126241233Sbrooksstatic	struct group *	(*_pwcache_getgrgid)(gid_t)		= getgrgid;
127241233Sbrooksstatic	int		(*_pwcache_setpassent)(int)		= setpassent;
128241233Sbrooksstatic	void		(*_pwcache_endpwent)(void)		= endpwent;
129241233Sbrooksstatic	struct passwd *	(*_pwcache_getpwnam)(const char *)	= getpwnam;
130241233Sbrooksstatic	struct passwd *	(*_pwcache_getpwuid)(uid_t)		= getpwuid;
131241233Sbrooks
132241233Sbrooks/*
133241233Sbrooks * internal state
134241233Sbrooks */
135241233Sbrooksstatic	int	pwopn;		/* is password file open */
136241233Sbrooksstatic	int	gropn;		/* is group file open */
137241233Sbrooksstatic	UIDC	**uidtb;	/* uid to name cache */
138241233Sbrooksstatic	GIDC	**gidtb;	/* gid to name cache */
139241233Sbrooksstatic	UIDC	**usrtb;	/* user name to uid cache */
140241233Sbrooksstatic	GIDC	**grptb;	/* group name to gid cache */
141241233Sbrooks
142241233Sbrooksstatic	int	uidtb_fail;	/* uidtb_start() failed ? */
143241233Sbrooksstatic	int	gidtb_fail;	/* gidtb_start() failed ? */
144241233Sbrooksstatic	int	usrtb_fail;	/* usrtb_start() failed ? */
145241233Sbrooksstatic	int	grptb_fail;	/* grptb_start() failed ? */
146241233Sbrooks
147241233Sbrooks
148241233Sbrooksstatic	u_int	st_hash(const char *, size_t, int);
149241233Sbrooksstatic	int	uidtb_start(void);
150241233Sbrooksstatic	int	gidtb_start(void);
151241233Sbrooksstatic	int	usrtb_start(void);
152241233Sbrooksstatic	int	grptb_start(void);
153241233Sbrooks
154241233Sbrooks
155241233Sbrooksstatic u_int
156241233Sbrooksst_hash(const char *name, size_t len, int tabsz)
157241233Sbrooks{
158241233Sbrooks	u_int key = 0;
159241233Sbrooks
160241233Sbrooks	_DIAGASSERT(name != NULL);
161241233Sbrooks
162241233Sbrooks	while (len--) {
163241233Sbrooks		key += *name++;
164241233Sbrooks		key = (key << 8) | (key >> 24);
165241233Sbrooks	}
166241233Sbrooks
167241233Sbrooks	return (key % tabsz);
168241233Sbrooks}
169241233Sbrooks
170241233Sbrooks/*
171241233Sbrooks * uidtb_start
172241233Sbrooks *	creates an an empty uidtb
173241233Sbrooks * Return:
174241233Sbrooks *	0 if ok, -1 otherwise
175241233Sbrooks */
176241233Sbrooksstatic int
177241233Sbrooksuidtb_start(void)
178241233Sbrooks{
179241233Sbrooks
180241233Sbrooks	if (uidtb != NULL)
181241233Sbrooks		return (0);
182241233Sbrooks	if (uidtb_fail)
183241233Sbrooks		return (-1);
184241233Sbrooks	if ((uidtb = (UIDC **)calloc(UID_SZ, sizeof(UIDC *))) == NULL) {
185241233Sbrooks		++uidtb_fail;
186241233Sbrooks		return (-1);
187241233Sbrooks	}
188241233Sbrooks	return (0);
189241233Sbrooks}
190241233Sbrooks
191241233Sbrooks/*
192241233Sbrooks * gidtb_start
193241233Sbrooks *	creates an an empty gidtb
194241233Sbrooks * Return:
195241233Sbrooks *	0 if ok, -1 otherwise
196241233Sbrooks */
197241233Sbrooksstatic int
198241233Sbrooksgidtb_start(void)
199241233Sbrooks{
200241233Sbrooks
201241233Sbrooks	if (gidtb != NULL)
202241233Sbrooks		return (0);
203241233Sbrooks	if (gidtb_fail)
204241233Sbrooks		return (-1);
205241233Sbrooks	if ((gidtb = (GIDC **)calloc(GID_SZ, sizeof(GIDC *))) == NULL) {
206241233Sbrooks		++gidtb_fail;
207241233Sbrooks		return (-1);
208241233Sbrooks	}
209241233Sbrooks	return (0);
210241233Sbrooks}
211241233Sbrooks
212241233Sbrooks/*
213241233Sbrooks * usrtb_start
214241233Sbrooks *	creates an an empty usrtb
215241233Sbrooks * Return:
216241233Sbrooks *	0 if ok, -1 otherwise
217241233Sbrooks */
218241233Sbrooksstatic int
219241233Sbrooksusrtb_start(void)
220241233Sbrooks{
221241233Sbrooks
222241233Sbrooks	if (usrtb != NULL)
223241233Sbrooks		return (0);
224241233Sbrooks	if (usrtb_fail)
225241233Sbrooks		return (-1);
226241233Sbrooks	if ((usrtb = (UIDC **)calloc(UNM_SZ, sizeof(UIDC *))) == NULL) {
227241233Sbrooks		++usrtb_fail;
228241233Sbrooks		return (-1);
229241233Sbrooks	}
230241233Sbrooks	return (0);
231241233Sbrooks}
232241233Sbrooks
233241233Sbrooks/*
234241233Sbrooks * grptb_start
235241233Sbrooks *	creates an an empty grptb
236241233Sbrooks * Return:
237241233Sbrooks *	0 if ok, -1 otherwise
238241233Sbrooks */
239241233Sbrooksstatic int
240241233Sbrooksgrptb_start(void)
241241233Sbrooks{
242241233Sbrooks
243241233Sbrooks	if (grptb != NULL)
244241233Sbrooks		return (0);
245241233Sbrooks	if (grptb_fail)
246241233Sbrooks		return (-1);
247241233Sbrooks	if ((grptb = (GIDC **)calloc(GNM_SZ, sizeof(GIDC *))) == NULL) {
248241233Sbrooks		++grptb_fail;
249241233Sbrooks		return (-1);
250241233Sbrooks	}
251241233Sbrooks	return (0);
252241233Sbrooks}
253241233Sbrooks
254241233Sbrooks/*
255241233Sbrooks * user_from_uid()
256241233Sbrooks *	caches the name (if any) for the uid. If noname clear, we always
257241233Sbrooks *	return the stored name (if valid or invalid match).
258241233Sbrooks *	We use a simple hash table.
259241233Sbrooks * Return
260241233Sbrooks *	Pointer to stored name (or a empty string)
261241233Sbrooks */
262241233Sbrooksconst char *
263241233Sbrooksuser_from_uid(uid_t uid, int noname)
264241233Sbrooks{
265241233Sbrooks	struct passwd *pw;
266241233Sbrooks	UIDC *ptr, **pptr;
267241233Sbrooks
268241233Sbrooks	if ((uidtb == NULL) && (uidtb_start() < 0))
269241233Sbrooks		return (NULL);
270241233Sbrooks
271241233Sbrooks	/*
272241233Sbrooks	 * see if we have this uid cached
273241233Sbrooks	 */
274241233Sbrooks	pptr = uidtb + (uid % UID_SZ);
275241233Sbrooks	ptr = *pptr;
276241233Sbrooks
277241233Sbrooks	if ((ptr != NULL) && (ptr->valid > 0) && (ptr->uid == uid)) {
278241233Sbrooks		/*
279241233Sbrooks		 * have an entry for this uid
280241233Sbrooks		 */
281241233Sbrooks		if (!noname || (ptr->valid == VALID))
282241233Sbrooks			return (ptr->name);
283241233Sbrooks		return (NULL);
284241233Sbrooks	}
285241233Sbrooks
286241233Sbrooks	/*
287241233Sbrooks	 * No entry for this uid, we will add it
288241233Sbrooks	 */
289241233Sbrooks	if (!pwopn) {
290241233Sbrooks		if (_pwcache_setpassent != NULL)
291241233Sbrooks			(*_pwcache_setpassent)(1);
292241233Sbrooks		++pwopn;
293241233Sbrooks	}
294241233Sbrooks
295241233Sbrooks	if (ptr == NULL)
296241233Sbrooks		*pptr = ptr = (UIDC *)malloc(sizeof(UIDC));
297241233Sbrooks
298241233Sbrooks	if ((pw = (*_pwcache_getpwuid)(uid)) == NULL) {
299241233Sbrooks		/*
300241233Sbrooks		 * no match for this uid in the local password file
301241233Sbrooks		 * a string that is the uid in numeric format
302241233Sbrooks		 */
303241233Sbrooks		if (ptr == NULL)
304241233Sbrooks			return (NULL);
305241233Sbrooks		ptr->uid = uid;
306241233Sbrooks		(void)snprintf(ptr->name, UNMLEN, "%lu", (long) uid);
307241233Sbrooks		ptr->valid = INVALID;
308241233Sbrooks		if (noname)
309241233Sbrooks			return (NULL);
310241233Sbrooks	} else {
311241233Sbrooks		/*
312241233Sbrooks		 * there is an entry for this uid in the password file
313241233Sbrooks		 */
314241233Sbrooks		if (ptr == NULL)
315241233Sbrooks			return (pw->pw_name);
316241233Sbrooks		ptr->uid = uid;
317241233Sbrooks		(void)strlcpy(ptr->name, pw->pw_name, UNMLEN);
318241233Sbrooks		ptr->valid = VALID;
319241233Sbrooks	}
320241233Sbrooks	return (ptr->name);
321241233Sbrooks}
322241233Sbrooks
323241233Sbrooks/*
324241233Sbrooks * group_from_gid()
325241233Sbrooks *	caches the name (if any) for the gid. If noname clear, we always
326241233Sbrooks *	return the stored name (if valid or invalid match).
327241233Sbrooks *	We use a simple hash table.
328241233Sbrooks * Return
329241233Sbrooks *	Pointer to stored name (or a empty string)
330241233Sbrooks */
331241233Sbrooksconst char *
332241233Sbrooksgroup_from_gid(gid_t gid, int noname)
333241233Sbrooks{
334241233Sbrooks	struct group *gr;
335241233Sbrooks	GIDC *ptr, **pptr;
336241233Sbrooks
337241233Sbrooks	if ((gidtb == NULL) && (gidtb_start() < 0))
338241233Sbrooks		return (NULL);
339241233Sbrooks
340241233Sbrooks	/*
341241233Sbrooks	 * see if we have this gid cached
342241233Sbrooks	 */
343241233Sbrooks	pptr = gidtb + (gid % GID_SZ);
344241233Sbrooks	ptr = *pptr;
345241233Sbrooks
346241233Sbrooks	if ((ptr != NULL) && (ptr->valid > 0) && (ptr->gid == gid)) {
347241233Sbrooks		/*
348241233Sbrooks		 * have an entry for this gid
349241233Sbrooks		 */
350241233Sbrooks		if (!noname || (ptr->valid == VALID))
351241233Sbrooks			return (ptr->name);
352241233Sbrooks		return (NULL);
353241233Sbrooks	}
354241233Sbrooks
355241233Sbrooks	/*
356241233Sbrooks	 * No entry for this gid, we will add it
357241233Sbrooks	 */
358241233Sbrooks	if (!gropn) {
359241233Sbrooks		if (_pwcache_setgroupent != NULL)
360241233Sbrooks			(*_pwcache_setgroupent)(1);
361241233Sbrooks		++gropn;
362241233Sbrooks	}
363241233Sbrooks
364241233Sbrooks	if (ptr == NULL)
365241233Sbrooks		*pptr = ptr = (GIDC *)malloc(sizeof(GIDC));
366241233Sbrooks
367241233Sbrooks	if ((gr = (*_pwcache_getgrgid)(gid)) == NULL) {
368241233Sbrooks		/*
369241233Sbrooks		 * no match for this gid in the local group file, put in
370241233Sbrooks		 * a string that is the gid in numberic format
371241233Sbrooks		 */
372241233Sbrooks		if (ptr == NULL)
373241233Sbrooks			return (NULL);
374241233Sbrooks		ptr->gid = gid;
375241233Sbrooks		(void)snprintf(ptr->name, GNMLEN, "%lu", (long) gid);
376241233Sbrooks		ptr->valid = INVALID;
377241233Sbrooks		if (noname)
378241233Sbrooks			return (NULL);
379241233Sbrooks	} else {
380241233Sbrooks		/*
381241233Sbrooks		 * there is an entry for this group in the group file
382241233Sbrooks		 */
383241233Sbrooks		if (ptr == NULL)
384241233Sbrooks			return (gr->gr_name);
385241233Sbrooks		ptr->gid = gid;
386241233Sbrooks		(void)strlcpy(ptr->name, gr->gr_name, GNMLEN);
387241233Sbrooks		ptr->valid = VALID;
388241233Sbrooks	}
389241233Sbrooks	return (ptr->name);
390241233Sbrooks}
391241233Sbrooks
392241233Sbrooks/*
393241233Sbrooks * uid_from_user()
394241233Sbrooks *	caches the uid for a given user name. We use a simple hash table.
395241233Sbrooks * Return
396241233Sbrooks *	the uid (if any) for a user name, or a -1 if no match can be found
397241233Sbrooks */
398241233Sbrooksint
399241233Sbrooksuid_from_user(const char *name, uid_t *uid)
400241233Sbrooks{
401241233Sbrooks	struct passwd *pw;
402241233Sbrooks	UIDC *ptr, **pptr;
403241233Sbrooks	size_t namelen;
404241233Sbrooks
405241233Sbrooks	/*
406241233Sbrooks	 * return -1 for mangled names
407241233Sbrooks	 */
408241233Sbrooks	if (name == NULL || ((namelen = strlen(name)) == 0))
409241233Sbrooks		return (-1);
410241233Sbrooks	if ((usrtb == NULL) && (usrtb_start() < 0))
411241233Sbrooks		return (-1);
412241233Sbrooks
413241233Sbrooks	/*
414241233Sbrooks	 * look up in hash table, if found and valid return the uid,
415241233Sbrooks	 * if found and invalid, return a -1
416241233Sbrooks	 */
417241233Sbrooks	pptr = usrtb + st_hash(name, namelen, UNM_SZ);
418241233Sbrooks	ptr = *pptr;
419241233Sbrooks
420241233Sbrooks	if ((ptr != NULL) && (ptr->valid > 0) && !strcmp(name, ptr->name)) {
421241233Sbrooks		if (ptr->valid == INVALID)
422241233Sbrooks			return (-1);
423241233Sbrooks		*uid = ptr->uid;
424241233Sbrooks		return (0);
425241233Sbrooks	}
426241233Sbrooks
427241233Sbrooks	if (!pwopn) {
428241233Sbrooks		if (_pwcache_setpassent != NULL)
429241233Sbrooks			(*_pwcache_setpassent)(1);
430241233Sbrooks		++pwopn;
431241233Sbrooks	}
432241233Sbrooks
433241233Sbrooks	if (ptr == NULL)
434241233Sbrooks		*pptr = ptr = (UIDC *)malloc(sizeof(UIDC));
435241233Sbrooks
436241233Sbrooks	/*
437241233Sbrooks	 * no match, look it up, if no match store it as an invalid entry,
438241233Sbrooks	 * or store the matching uid
439241233Sbrooks	 */
440241233Sbrooks	if (ptr == NULL) {
441241233Sbrooks		if ((pw = (*_pwcache_getpwnam)(name)) == NULL)
442241233Sbrooks			return (-1);
443241233Sbrooks		*uid = pw->pw_uid;
444241233Sbrooks		return (0);
445241233Sbrooks	}
446241233Sbrooks	(void)strlcpy(ptr->name, name, UNMLEN);
447241233Sbrooks	if ((pw = (*_pwcache_getpwnam)(name)) == NULL) {
448241233Sbrooks		ptr->valid = INVALID;
449241233Sbrooks		return (-1);
450241233Sbrooks	}
451241233Sbrooks	ptr->valid = VALID;
452241233Sbrooks	*uid = ptr->uid = pw->pw_uid;
453241233Sbrooks	return (0);
454241233Sbrooks}
455241233Sbrooks
456241233Sbrooks/*
457241233Sbrooks * gid_from_group()
458241233Sbrooks *	caches the gid for a given group name. We use a simple hash table.
459241233Sbrooks * Return
460241233Sbrooks *	the gid (if any) for a group name, or a -1 if no match can be found
461241233Sbrooks */
462241233Sbrooksint
463241233Sbrooksgid_from_group(const char *name, gid_t *gid)
464241233Sbrooks{
465241233Sbrooks	struct group *gr;
466241233Sbrooks	GIDC *ptr, **pptr;
467241233Sbrooks	size_t namelen;
468241233Sbrooks
469241233Sbrooks	/*
470241233Sbrooks	 * return -1 for mangled names
471241233Sbrooks	 */
472241233Sbrooks	if (name == NULL || ((namelen = strlen(name)) == 0))
473241233Sbrooks		return (-1);
474241233Sbrooks	if ((grptb == NULL) && (grptb_start() < 0))
475241233Sbrooks		return (-1);
476241233Sbrooks
477241233Sbrooks	/*
478241233Sbrooks	 * look up in hash table, if found and valid return the uid,
479241233Sbrooks	 * if found and invalid, return a -1
480241233Sbrooks	 */
481241233Sbrooks	pptr = grptb + st_hash(name, namelen, GID_SZ);
482241233Sbrooks	ptr = *pptr;
483241233Sbrooks
484241233Sbrooks	if ((ptr != NULL) && (ptr->valid > 0) && !strcmp(name, ptr->name)) {
485241233Sbrooks		if (ptr->valid == INVALID)
486241233Sbrooks			return (-1);
487241233Sbrooks		*gid = ptr->gid;
488241233Sbrooks		return (0);
489241233Sbrooks	}
490241233Sbrooks
491241233Sbrooks	if (!gropn) {
492241233Sbrooks		if (_pwcache_setgroupent != NULL)
493241233Sbrooks			(*_pwcache_setgroupent)(1);
494241233Sbrooks		++gropn;
495241233Sbrooks	}
496241233Sbrooks
497241233Sbrooks	if (ptr == NULL)
498241233Sbrooks		*pptr = ptr = (GIDC *)malloc(sizeof(GIDC));
499241233Sbrooks
500241233Sbrooks	/*
501241233Sbrooks	 * no match, look it up, if no match store it as an invalid entry,
502241233Sbrooks	 * or store the matching gid
503241233Sbrooks	 */
504241233Sbrooks	if (ptr == NULL) {
505241233Sbrooks		if ((gr = (*_pwcache_getgrnam)(name)) == NULL)
506241233Sbrooks			return (-1);
507241233Sbrooks		*gid = gr->gr_gid;
508241233Sbrooks		return (0);
509241233Sbrooks	}
510241233Sbrooks
511241233Sbrooks	(void)strlcpy(ptr->name, name, GNMLEN);
512241233Sbrooks	if ((gr = (*_pwcache_getgrnam)(name)) == NULL) {
513241233Sbrooks		ptr->valid = INVALID;
514241233Sbrooks		return (-1);
515241233Sbrooks	}
516241233Sbrooks	ptr->valid = VALID;
517241233Sbrooks	*gid = ptr->gid = gr->gr_gid;
518241233Sbrooks	return (0);
519241233Sbrooks}
520241233Sbrooks
521241233Sbrooks#define FLUSHTB(arr, len, fail)				\
522241233Sbrooks	do {						\
523241233Sbrooks		if (arr != NULL) {			\
524241233Sbrooks			for (i = 0; i < len; i++)	\
525241233Sbrooks				if (arr[i] != NULL)	\
526241233Sbrooks					free(arr[i]);	\
527241233Sbrooks			arr = NULL;			\
528241233Sbrooks		}					\
529241233Sbrooks		fail = 0;				\
530241233Sbrooks	} while (/* CONSTCOND */0);
531241233Sbrooks
532241233Sbrooksint
533241233Sbrookspwcache_userdb(
534241233Sbrooks	int		(*a_setpassent)(int),
535241233Sbrooks	void		(*a_endpwent)(void),
536241233Sbrooks	struct passwd *	(*a_getpwnam)(const char *),
537241233Sbrooks	struct passwd *	(*a_getpwuid)(uid_t))
538241233Sbrooks{
539241233Sbrooks	int i;
540241233Sbrooks
541241233Sbrooks		/* a_setpassent and a_endpwent may be NULL */
542241233Sbrooks	if (a_getpwnam == NULL || a_getpwuid == NULL)
543241233Sbrooks		return (-1);
544241233Sbrooks
545241233Sbrooks	if (_pwcache_endpwent != NULL)
546241233Sbrooks		(*_pwcache_endpwent)();
547241233Sbrooks	FLUSHTB(uidtb, UID_SZ, uidtb_fail);
548241233Sbrooks	FLUSHTB(usrtb, UNM_SZ, usrtb_fail);
549241233Sbrooks	pwopn = 0;
550241233Sbrooks	_pwcache_setpassent = a_setpassent;
551241233Sbrooks	_pwcache_endpwent = a_endpwent;
552241233Sbrooks	_pwcache_getpwnam = a_getpwnam;
553241233Sbrooks	_pwcache_getpwuid = a_getpwuid;
554241233Sbrooks
555241233Sbrooks	return (0);
556241233Sbrooks}
557241233Sbrooks
558241233Sbrooksint
559241233Sbrookspwcache_groupdb(
560241233Sbrooks	int		(*a_setgroupent)(int),
561241233Sbrooks	void		(*a_endgrent)(void),
562241233Sbrooks	struct group *	(*a_getgrnam)(const char *),
563241233Sbrooks	struct group *	(*a_getgrgid)(gid_t))
564241233Sbrooks{
565241233Sbrooks	int i;
566241233Sbrooks
567241233Sbrooks		/* a_setgroupent and a_endgrent may be NULL */
568241233Sbrooks	if (a_getgrnam == NULL || a_getgrgid == NULL)
569241233Sbrooks		return (-1);
570241233Sbrooks
571241233Sbrooks	if (_pwcache_endgrent != NULL)
572241233Sbrooks		(*_pwcache_endgrent)();
573241233Sbrooks	FLUSHTB(gidtb, GID_SZ, gidtb_fail);
574241233Sbrooks	FLUSHTB(grptb, GNM_SZ, grptb_fail);
575241233Sbrooks	gropn = 0;
576241233Sbrooks	_pwcache_setgroupent = a_setgroupent;
577241233Sbrooks	_pwcache_endgrent = a_endgrent;
578241233Sbrooks	_pwcache_getgrnam = a_getgrnam;
579241233Sbrooks	_pwcache_getgrgid = a_getgrgid;
580241233Sbrooks
581241233Sbrooks	return (0);
582241233Sbrooks}
583241233Sbrooks
584241233Sbrooks
585241233Sbrooks#ifdef TEST_PWCACHE
586241233Sbrooks
587241233Sbrooksstruct passwd *
588241233Sbrookstest_getpwnam(const char *name)
589241233Sbrooks{
590241233Sbrooks	static struct passwd foo;
591241233Sbrooks
592241233Sbrooks	memset(&foo, 0, sizeof(foo));
593241233Sbrooks	if (strcmp(name, "toor") == 0) {
594241233Sbrooks		foo.pw_uid = 666;
595241233Sbrooks		return &foo;
596241233Sbrooks	}
597241233Sbrooks	return (getpwnam(name));
598241233Sbrooks}
599241233Sbrooks
600241233Sbrooksint
601241233Sbrooksmain(int argc, char *argv[])
602241233Sbrooks{
603241233Sbrooks	uid_t	u;
604241233Sbrooks	int	r, i;
605241233Sbrooks
606241233Sbrooks	printf("pass 1 (default userdb)\n");
607241233Sbrooks	for (i = 1; i < argc; i++) {
608241233Sbrooks		printf("i: %d, pwopn %d usrtb_fail %d usrtb %p\n",
609241233Sbrooks		    i, pwopn, usrtb_fail, usrtb);
610241233Sbrooks		r = uid_from_user(argv[i], &u);
611241233Sbrooks		if (r == -1)
612241233Sbrooks			printf("  uid_from_user %s: failed\n", argv[i]);
613241233Sbrooks		else
614241233Sbrooks			printf("  uid_from_user %s: %d\n", argv[i], u);
615241233Sbrooks	}
616241233Sbrooks	printf("pass 1 finish: pwopn %d usrtb_fail %d usrtb %p\n",
617241233Sbrooks		    pwopn, usrtb_fail, usrtb);
618241233Sbrooks
619241233Sbrooks	puts("");
620241233Sbrooks	printf("pass 2 (replacement userdb)\n");
621241233Sbrooks	printf("pwcache_userdb returned %d\n",
622241233Sbrooks	    pwcache_userdb(setpassent, test_getpwnam, getpwuid));
623241233Sbrooks	printf("pwopn %d usrtb_fail %d usrtb %p\n", pwopn, usrtb_fail, usrtb);
624241233Sbrooks
625241233Sbrooks	for (i = 1; i < argc; i++) {
626241233Sbrooks		printf("i: %d, pwopn %d usrtb_fail %d usrtb %p\n",
627241233Sbrooks		    i, pwopn, usrtb_fail, usrtb);
628241233Sbrooks		u = -1;
629241233Sbrooks		r = uid_from_user(argv[i], &u);
630241233Sbrooks		if (r == -1)
631241233Sbrooks			printf("  uid_from_user %s: failed\n", argv[i]);
632241233Sbrooks		else
633241233Sbrooks			printf("  uid_from_user %s: %d\n", argv[i], u);
634241233Sbrooks	}
635241233Sbrooks	printf("pass 2 finish: pwopn %d usrtb_fail %d usrtb %p\n",
636241233Sbrooks		    pwopn, usrtb_fail, usrtb);
637241233Sbrooks
638241233Sbrooks	puts("");
639241233Sbrooks	printf("pass 3 (null pointers)\n");
640241233Sbrooks	printf("pwcache_userdb returned %d\n",
641241233Sbrooks	    pwcache_userdb(NULL, NULL, NULL));
642241233Sbrooks
643241233Sbrooks	return (0);
644241233Sbrooks}
645241233Sbrooks#endif	/* TEST_PWCACHE */
646241233Sbrooks#endif	/* !HAVE_PWCACHE_USERDB */
647