Home | History | Annotate | Line # | Download | only in gen
pwcache.c revision 1.16
      1 /*	$NetBSD: pwcache.c,v 1.16 2002/01/04 14:50:29 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 #include <sys/cdefs.h>
     41 #if defined(LIBC_SCCS) && !defined(lint)
     42 #if 0
     43 static char sccsid[] = "@(#)cache.c	8.1 (Berkeley) 5/31/93";
     44 #else
     45 __RCSID("$NetBSD: pwcache.c,v 1.16 2002/01/04 14:50:29 lukem Exp $");
     46 #endif
     47 #endif /* LIBC_SCCS and not lint */
     48 
     49 #include "namespace.h"
     50 
     51 #include <sys/types.h>
     52 #include <sys/param.h>
     53 
     54 #include <assert.h>
     55 #include <grp.h>
     56 #include <pwd.h>
     57 #include <stdio.h>
     58 #include <stdlib.h>
     59 #include <string.h>
     60 #include <unistd.h>
     61 
     62 #include "pwcache.h"
     63 
     64 #ifdef __weak_alias
     65 __weak_alias(user_from_uid,_user_from_uid)
     66 __weak_alias(group_from_gid,_group_from_gid)
     67 #endif
     68 
     69 /*
     70  * routines that control user, group, uid and gid caches (for the archive
     71  * member print routine).
     72  * IMPORTANT:
     73  * these routines cache BOTH hits and misses, a major performance improvement
     74  */
     75 
     76 static	int pwopn = 0;		/* is password file open */
     77 static	int gropn = 0;		/* is group file open */
     78 static UIDC **uidtb = NULL;	/* uid to name cache */
     79 static GIDC **gidtb = NULL;	/* gid to name cache */
     80 static UIDC **usrtb = NULL;	/* user name to uid cache */
     81 static GIDC **grptb = NULL;	/* group name to gid cache */
     82 
     83 static u_int st_hash(const char *, size_t, int);
     84 static int uidtb_start(void);
     85 static int gidtb_start(void);
     86 static int usrtb_start(void);
     87 static int grptb_start(void);
     88 
     89 static u_int
     90 st_hash(const char *name, size_t len, int tabsz)
     91 {
     92 	u_int key = 0;
     93 
     94 	_DIAGASSERT(name != NULL);
     95 
     96 	while (len--) {
     97 		key += *name++;
     98 		key = (key << 8) | (key >> 24);
     99 	}
    100 
    101 	return (key % tabsz);
    102 }
    103 
    104 /*
    105  * uidtb_start
    106  *	creates an an empty uidtb
    107  * Return:
    108  *	0 if ok, -1 otherwise
    109  */
    110 
    111 static int
    112 uidtb_start(void)
    113 {
    114 	static int fail = 0;
    115 
    116 	if (uidtb != NULL)
    117 		return (0);
    118 	if (fail)
    119 		return (-1);
    120 	if ((uidtb = (UIDC **)calloc(UID_SZ, sizeof(UIDC *))) == NULL) {
    121 		++fail;
    122 		return (-1);
    123 	}
    124 	return (0);
    125 }
    126 
    127 /*
    128  * gidtb_start
    129  *	creates an an empty gidtb
    130  * Return:
    131  *	0 if ok, -1 otherwise
    132  */
    133 
    134 int
    135 gidtb_start(void)
    136 {
    137 	static int fail = 0;
    138 
    139 	if (gidtb != NULL)
    140 		return (0);
    141 	if (fail)
    142 		return (-1);
    143 	if ((gidtb = (GIDC **)calloc(GID_SZ, sizeof(GIDC *))) == NULL) {
    144 		++fail;
    145 		return (-1);
    146 	}
    147 	return (0);
    148 }
    149 
    150 /*
    151  * usrtb_start
    152  *	creates an an empty usrtb
    153  * Return:
    154  *	0 if ok, -1 otherwise
    155  */
    156 
    157 int
    158 usrtb_start(void)
    159 {
    160 	static int fail = 0;
    161 
    162 	if (usrtb != NULL)
    163 		return (0);
    164 	if (fail)
    165 		return (-1);
    166 	if ((usrtb = (UIDC **)calloc(UNM_SZ, sizeof(UIDC *))) == NULL) {
    167 		++fail;
    168 		return (-1);
    169 	}
    170 	return (0);
    171 }
    172 
    173 /*
    174  * grptb_start
    175  *	creates an an empty grptb
    176  * Return:
    177  *	0 if ok, -1 otherwise
    178  */
    179 
    180 int
    181 grptb_start(void)
    182 {
    183 	static int fail = 0;
    184 
    185 	if (grptb != NULL)
    186 		return (0);
    187 	if (fail)
    188 		return (-1);
    189 	if ((grptb = (GIDC **)calloc(GNM_SZ, sizeof(GIDC *))) == NULL) {
    190 		++fail;
    191 		return (-1);
    192 	}
    193 	return (0);
    194 }
    195 
    196 /*
    197  * user_from_uid()
    198  *	caches the name (if any) for the uid. If noname clear, we always
    199  *	return the the stored name (if valid or invalid match).
    200  *	We use a simple hash table.
    201  * Return
    202  *	Pointer to stored name (or a empty string)
    203  */
    204 
    205 const char *
    206 user_from_uid(uid_t uid, int noname)
    207 {
    208 	struct passwd *pw;
    209 	UIDC *ptr, **pptr;
    210 
    211 	if ((uidtb == NULL) && (uidtb_start() < 0))
    212 		return (NULL);
    213 
    214 	/*
    215 	 * see if we have this uid cached
    216 	 */
    217 	pptr = uidtb + (uid % UID_SZ);
    218 	ptr = *pptr;
    219 
    220 	if ((ptr != NULL) && (ptr->valid > 0) && (ptr->uid == uid)) {
    221 		/*
    222 		 * have an entry for this uid
    223 		 */
    224 		if (!noname || (ptr->valid == VALID))
    225 			return (ptr->name);
    226 		return (NULL);
    227 	}
    228 
    229 	/*
    230 	 * No entry for this uid, we will add it
    231 	 */
    232 	if (!pwopn) {
    233 		setpassent(1);
    234 		++pwopn;
    235 	}
    236 
    237 	if (ptr == NULL)
    238 		*pptr = ptr = (UIDC *)malloc(sizeof(UIDC));
    239 
    240 	if ((pw = getpwuid(uid)) == NULL) {
    241 		/*
    242 		 * no match for this uid in the local password file
    243 		 * a string that is the uid in numberic format
    244 		 */
    245 		if (ptr == NULL)
    246 			return (NULL);
    247 		ptr->uid = uid;
    248 		(void)snprintf(ptr->name, UNMLEN, "%lu", (long) uid);
    249 		ptr->valid = INVALID;
    250 		if (noname)
    251 			return (NULL);
    252 	} else {
    253 		/*
    254 		 * there is an entry for this uid in the password file
    255 		 */
    256 		if (ptr == NULL)
    257 			return (pw->pw_name);
    258 		ptr->uid = uid;
    259 		(void)strncpy(ptr->name, pw->pw_name, UNMLEN);
    260 		ptr->name[UNMLEN-1] = '\0';
    261 		ptr->valid = VALID;
    262 	}
    263 	return (ptr->name);
    264 }
    265 
    266 /*
    267  * group_from_gid()
    268  *	caches the name (if any) for the gid. If noname clear, we always
    269  *	return the the stored name (if valid or invalid match).
    270  *	We use a simple hash table.
    271  * Return
    272  *	Pointer to stored name (or a empty string)
    273  */
    274 
    275 const char *
    276 group_from_gid(gid_t gid, int noname)
    277 {
    278 	struct group *gr;
    279 	GIDC *ptr, **pptr;
    280 
    281 	if ((gidtb == NULL) && (gidtb_start() < 0))
    282 		return (NULL);
    283 
    284 	/*
    285 	 * see if we have this gid cached
    286 	 */
    287 	pptr = gidtb + (gid % GID_SZ);
    288 	ptr = *pptr;
    289 
    290 	if ((ptr != NULL) && (ptr->valid > 0) && (ptr->gid == gid)) {
    291 		/*
    292 		 * have an entry for this gid
    293 		 */
    294 		if (!noname || (ptr->valid == VALID))
    295 			return (ptr->name);
    296 		return (NULL);
    297 	}
    298 
    299 	/*
    300 	 * No entry for this gid, we will add it
    301 	 */
    302 	if (!gropn) {
    303 		setgroupent(1);
    304 		++gropn;
    305 	}
    306 
    307 	if (ptr == NULL)
    308 		*pptr = ptr = (GIDC *)malloc(sizeof(GIDC));
    309 
    310 	if ((gr = getgrgid(gid)) == NULL) {
    311 		/*
    312 		 * no match for this gid in the local group file, put in
    313 		 * a string that is the gid in numberic format
    314 		 */
    315 		if (ptr == NULL)
    316 			return (NULL);
    317 		ptr->gid = gid;
    318 		(void)snprintf(ptr->name, GNMLEN, "%lu", (long) gid);
    319 		ptr->valid = INVALID;
    320 		if (noname)
    321 			return (NULL);
    322 	} else {
    323 		/*
    324 		 * there is an entry for this group in the group file
    325 		 */
    326 		if (ptr == NULL)
    327 			return (gr->gr_name);
    328 		ptr->gid = gid;
    329 		(void)strncpy(ptr->name, gr->gr_name, GNMLEN);
    330 		ptr->name[GNMLEN-1] = '\0';
    331 		ptr->valid = VALID;
    332 	}
    333 	return (ptr->name);
    334 }
    335 
    336 /*
    337  * uid_from_user()
    338  *	caches the uid for a given user name. We use a simple hash table.
    339  * Return
    340  *	the uid (if any) for a user name, or a -1 if no match can be found
    341  */
    342 
    343 int
    344 uid_from_user(const char *name, uid_t *uid)
    345 {
    346 	struct passwd *pw;
    347 	UIDC *ptr, **pptr;
    348 	size_t namelen;
    349 
    350 	/*
    351 	 * return -1 for mangled names
    352 	 */
    353 	if (name == NULL || ((namelen = strlen(name)) == 0))
    354 		return (-1);
    355 	if ((usrtb == NULL) && (usrtb_start() < 0))
    356 		return (-1);
    357 
    358 	/*
    359 	 * look up in hash table, if found and valid return the uid,
    360 	 * if found and invalid, return a -1
    361 	 */
    362 	pptr = usrtb + st_hash(name, namelen, UNM_SZ);
    363 	ptr = *pptr;
    364 
    365 	if ((ptr != NULL) && (ptr->valid > 0) && !strcmp(name, ptr->name)) {
    366 		if (ptr->valid == INVALID)
    367 			return (-1);
    368 		*uid = ptr->uid;
    369 		return (0);
    370 	}
    371 
    372 	if (!pwopn) {
    373 		setpassent(1);
    374 		++pwopn;
    375 	}
    376 
    377 	if (ptr == NULL)
    378 		*pptr = ptr = (UIDC *)malloc(sizeof(UIDC));
    379 
    380 	/*
    381 	 * no match, look it up, if no match store it as an invalid entry,
    382 	 * or store the matching uid
    383 	 */
    384 	if (ptr == NULL) {
    385 		if ((pw = getpwnam(name)) == NULL)
    386 			return (-1);
    387 		*uid = pw->pw_uid;
    388 		return (0);
    389 	}
    390 	(void)strncpy(ptr->name, name, UNMLEN);
    391 	ptr->name[UNMLEN-1] = '\0';
    392 	if ((pw = getpwnam(name)) == NULL) {
    393 		ptr->valid = INVALID;
    394 		return (-1);
    395 	}
    396 	ptr->valid = VALID;
    397 	*uid = ptr->uid = pw->pw_uid;
    398 	return (0);
    399 }
    400 
    401 /*
    402  * gid_from_group()
    403  *	caches the gid for a given group name. We use a simple hash table.
    404  * Return
    405  *	the gid (if any) for a group name, or a -1 if no match can be found
    406  */
    407 
    408 int
    409 gid_from_group(const char *name, gid_t *gid)
    410 {
    411 	struct group *gr;
    412 	GIDC *ptr, **pptr;
    413 	size_t namelen;
    414 
    415 	/*
    416 	 * return -1 for mangled names
    417 	 */
    418 	if (name == NULL || ((namelen = strlen(name)) == 0))
    419 		return (-1);
    420 	if ((grptb == NULL) && (grptb_start() < 0))
    421 		return (-1);
    422 
    423 	/*
    424 	 * look up in hash table, if found and valid return the uid,
    425 	 * if found and invalid, return a -1
    426 	 */
    427 	pptr = grptb + st_hash(name, namelen, GID_SZ);
    428 	ptr = *pptr;
    429 
    430 	if ((ptr != NULL) && (ptr->valid > 0) && !strcmp(name, ptr->name)) {
    431 		if (ptr->valid == INVALID)
    432 			return (-1);
    433 		*gid = ptr->gid;
    434 		return (0);
    435 	}
    436 
    437 	if (!gropn) {
    438 		setgroupent(1);
    439 		++gropn;
    440 	}
    441 
    442 	if (ptr == NULL)
    443 		*pptr = ptr = (GIDC *)malloc(sizeof(GIDC));
    444 
    445 	/*
    446 	 * no match, look it up, if no match store it as an invalid entry,
    447 	 * or store the matching gid
    448 	 */
    449 	if (ptr == NULL) {
    450 		if ((gr = getgrnam(name)) == NULL)
    451 			return (-1);
    452 		*gid = gr->gr_gid;
    453 		return (0);
    454 	}
    455 
    456 	(void)strncpy(ptr->name, name, GNMLEN);
    457 	ptr->name[GNMLEN-1] = '\0';
    458 	if ((gr = getgrnam(name)) == NULL) {
    459 		ptr->valid = INVALID;
    460 		return (-1);
    461 	}
    462 	ptr->valid = VALID;
    463 	*gid = ptr->gid = gr->gr_gid;
    464 	return (0);
    465 }
    466