1/* $OpenBSD: cache.c,v 1.17 2004/03/16 03:28:34 tedu Exp $ */ 2/* $NetBSD: cache.c,v 1.4 1995/03/21 09:07:10 cgd Exp $ */ 3 4/*- 5 * Copyright (c) 1992 Keith Muller. 6 * Copyright (c) 1992, 1993 7 * The Regents of the University of California. All rights reserved. 8 * 9 * This code is derived from software contributed to Berkeley by 10 * Keith Muller of the University of California, San Diego. 11 * 12 * Redistribution and use in source and binary forms, with or without 13 * modification, are permitted provided that the following conditions 14 * are met: 15 * 1. Redistributions of source code must retain the above copyright 16 * notice, this list of conditions and the following disclaimer. 17 * 2. Redistributions in binary form must reproduce the above copyright 18 * notice, this list of conditions and the following disclaimer in the 19 * documentation and/or other materials provided with the distribution. 20 * 3. 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#ifndef lint 38#if 0 39static const char sccsid[] = "@(#)cache.c 8.1 (Berkeley) 5/31/93"; 40#else 41static const char rcsid[] = "$OpenBSD: cache.c,v 1.17 2004/03/16 03:28:34 tedu Exp $"; 42#endif 43#endif /* not lint */ 44 45#include <sys/types.h> 46#include <sys/time.h> 47#include <sys/stat.h> 48#include <sys/param.h> 49#include <string.h> 50#include <stdio.h> 51#include <pwd.h> 52#include <grp.h> 53#include <unistd.h> 54#include <stdlib.h> 55#include "pax.h" 56#include "cache.h" 57#include "extern.h" 58 59/* 60 * routines that control user, group, uid and gid caches (for the archive 61 * member print routine). 62 * IMPORTANT: 63 * these routines cache BOTH hits and misses, a major performance improvement 64 */ 65 66static int pwopn = 0; /* is password file open */ 67static int gropn = 0; /* is group file open */ 68static UIDC **uidtb = NULL; /* uid to name cache */ 69static GIDC **gidtb = NULL; /* gid to name cache */ 70static UIDC **usrtb = NULL; /* user name to uid cache */ 71static GIDC **grptb = NULL; /* group name to gid cache */ 72 73/* 74 * uidtb_start 75 * creates an empty uidtb 76 * Return: 77 * 0 if ok, -1 otherwise 78 */ 79 80int 81uidtb_start(void) 82{ 83 static int fail = 0; 84 85 if (uidtb != NULL) 86 return(0); 87 if (fail) 88 return(-1); 89 if ((uidtb = (UIDC **)calloc(UID_SZ, sizeof(UIDC *))) == NULL) { 90 ++fail; 91 paxwarn(1, "Unable to allocate memory for user id cache table"); 92 return(-1); 93 } 94 return(0); 95} 96 97/* 98 * gidtb_start 99 * creates an empty gidtb 100 * Return: 101 * 0 if ok, -1 otherwise 102 */ 103 104int 105gidtb_start(void) 106{ 107 static int fail = 0; 108 109 if (gidtb != NULL) 110 return(0); 111 if (fail) 112 return(-1); 113 if ((gidtb = (GIDC **)calloc(GID_SZ, sizeof(GIDC *))) == NULL) { 114 ++fail; 115 paxwarn(1, "Unable to allocate memory for group id cache table"); 116 return(-1); 117 } 118 return(0); 119} 120 121/* 122 * usrtb_start 123 * creates an empty usrtb 124 * Return: 125 * 0 if ok, -1 otherwise 126 */ 127 128int 129usrtb_start(void) 130{ 131 static int fail = 0; 132 133 if (usrtb != NULL) 134 return(0); 135 if (fail) 136 return(-1); 137 if ((usrtb = (UIDC **)calloc(UNM_SZ, sizeof(UIDC *))) == NULL) { 138 ++fail; 139 paxwarn(1, "Unable to allocate memory for user name cache table"); 140 return(-1); 141 } 142 return(0); 143} 144 145/* 146 * grptb_start 147 * creates an empty grptb 148 * Return: 149 * 0 if ok, -1 otherwise 150 */ 151 152int 153grptb_start(void) 154{ 155 static int fail = 0; 156 157 if (grptb != NULL) 158 return(0); 159 if (fail) 160 return(-1); 161 if ((grptb = (GIDC **)calloc(GNM_SZ, sizeof(GIDC *))) == NULL) { 162 ++fail; 163 paxwarn(1,"Unable to allocate memory for group name cache table"); 164 return(-1); 165 } 166 return(0); 167} 168 169/* 170 * name_uid() 171 * caches the name (if any) for the uid. If frc set, we always return the 172 * the stored name (if valid or invalid match). We use a simple hash table. 173 * Return 174 * Pointer to stored name (or a empty string) 175 */ 176 177char * 178name_uid(uid_t uid, int frc) 179{ 180 struct passwd *pw; 181 UIDC *ptr; 182 183 if ((uidtb == NULL) && (uidtb_start() < 0)) 184 return(""); 185 186 /* 187 * see if we have this uid cached 188 */ 189 ptr = uidtb[uid % UID_SZ]; 190 if ((ptr != NULL) && (ptr->valid > 0) && (ptr->uid == uid)) { 191 /* 192 * have an entry for this uid 193 */ 194 if (frc || (ptr->valid == VALID)) 195 return(ptr->name); 196 return(""); 197 } 198 199 /* 200 * No entry for this uid, we will add it 201 */ 202 if (!pwopn) { 203 setpassent(1); 204 ++pwopn; 205 } 206 if (ptr == NULL) 207 ptr = uidtb[uid % UID_SZ] = malloc(sizeof(UIDC)); 208 209 if ((pw = getpwuid(uid)) == NULL) { 210 /* 211 * no match for this uid in the local password file 212 * a string that is the uid in numeric format 213 */ 214 if (ptr == NULL) 215 return(""); 216 ptr->uid = uid; 217 ptr->valid = INVALID; 218 (void)snprintf(ptr->name, sizeof(ptr->name), "%lu", 219 (unsigned long)uid); 220 if (frc == 0) 221 return(""); 222 } else { 223 /* 224 * there is an entry for this uid in the password file 225 */ 226 if (ptr == NULL) 227 return(pw->pw_name); 228 ptr->uid = uid; 229 (void)strlcpy(ptr->name, pw->pw_name, sizeof(ptr->name)); 230 ptr->valid = VALID; 231 } 232 return(ptr->name); 233} 234 235/* 236 * name_gid() 237 * caches the name (if any) for the gid. If frc set, we always return the 238 * the stored name (if valid or invalid match). We use a simple hash table. 239 * Return 240 * Pointer to stored name (or a empty string) 241 */ 242 243char * 244name_gid(gid_t gid, int frc) 245{ 246 struct group *gr; 247 GIDC *ptr; 248 249 if ((gidtb == NULL) && (gidtb_start() < 0)) 250 return(""); 251 252 /* 253 * see if we have this gid cached 254 */ 255 ptr = gidtb[gid % GID_SZ]; 256 if ((ptr != NULL) && (ptr->valid > 0) && (ptr->gid == gid)) { 257 /* 258 * have an entry for this gid 259 */ 260 if (frc || (ptr->valid == VALID)) 261 return(ptr->name); 262 return(""); 263 } 264 265 /* 266 * No entry for this gid, we will add it 267 */ 268 if (!gropn) { 269 setgroupent(1); 270 ++gropn; 271 } 272 if (ptr == NULL) 273 ptr = gidtb[gid % GID_SZ] = malloc(sizeof(GIDC)); 274 275 if ((gr = getgrgid(gid)) == NULL) { 276 /* 277 * no match for this gid in the local group file, put in 278 * a string that is the gid in numberic format 279 */ 280 if (ptr == NULL) 281 return(""); 282 ptr->gid = gid; 283 ptr->valid = INVALID; 284 (void)snprintf(ptr->name, sizeof(ptr->name), "%lu", 285 (unsigned long)gid); 286 if (frc == 0) 287 return(""); 288 } else { 289 /* 290 * there is an entry for this group in the group file 291 */ 292 if (ptr == NULL) 293 return(gr->gr_name); 294 ptr->gid = gid; 295 (void)strlcpy(ptr->name, gr->gr_name, sizeof(ptr->name)); 296 ptr->valid = VALID; 297 } 298 return(ptr->name); 299} 300 301/* 302 * uid_name() 303 * caches the uid for a given user name. We use a simple hash table. 304 * Return 305 * the uid (if any) for a user name, or a -1 if no match can be found 306 */ 307 308int 309uid_name(char *name, uid_t *uid) 310{ 311 struct passwd *pw; 312 UIDC *ptr; 313 int namelen; 314 315 /* 316 * return -1 for mangled names 317 */ 318 if (((namelen = strlen(name)) == 0) || (name[0] == '\0')) 319 return(-1); 320 if ((usrtb == NULL) && (usrtb_start() < 0)) 321 return(-1); 322 323 /* 324 * look up in hash table, if found and valid return the uid, 325 * if found and invalid, return a -1 326 */ 327 ptr = usrtb[st_hash(name, namelen, UNM_SZ)]; 328 if ((ptr != NULL) && (ptr->valid > 0) && !strcmp(name, ptr->name)) { 329 if (ptr->valid == INVALID) 330 return(-1); 331 *uid = ptr->uid; 332 return(0); 333 } 334 335 if (!pwopn) { 336 setpassent(1); 337 ++pwopn; 338 } 339 340 if (ptr == NULL) 341 ptr = usrtb[st_hash(name, namelen, UNM_SZ)] = 342 (UIDC *)malloc(sizeof(UIDC)); 343 344 /* 345 * no match, look it up, if no match store it as an invalid entry, 346 * or store the matching uid 347 */ 348 if (ptr == NULL) { 349 if ((pw = getpwnam(name)) == NULL) 350 return(-1); 351 *uid = pw->pw_uid; 352 return(0); 353 } 354 (void)strlcpy(ptr->name, name, sizeof(ptr->name)); 355 if ((pw = getpwnam(name)) == NULL) { 356 ptr->valid = INVALID; 357 return(-1); 358 } 359 ptr->valid = VALID; 360 *uid = ptr->uid = pw->pw_uid; 361 return(0); 362} 363 364/* 365 * gid_name() 366 * caches the gid for a given group name. We use a simple hash table. 367 * Return 368 * the gid (if any) for a group name, or a -1 if no match can be found 369 */ 370 371int 372gid_name(char *name, gid_t *gid) 373{ 374 struct group *gr; 375 GIDC *ptr; 376 int namelen; 377 378 /* 379 * return -1 for mangled names 380 */ 381 if (((namelen = strlen(name)) == 0) || (name[0] == '\0')) 382 return(-1); 383 if ((grptb == NULL) && (grptb_start() < 0)) 384 return(-1); 385 386 /* 387 * look up in hash table, if found and valid return the uid, 388 * if found and invalid, return a -1 389 */ 390 ptr = grptb[st_hash(name, namelen, GID_SZ)]; 391 if ((ptr != NULL) && (ptr->valid > 0) && !strcmp(name, ptr->name)) { 392 if (ptr->valid == INVALID) 393 return(-1); 394 *gid = ptr->gid; 395 return(0); 396 } 397 398 if (!gropn) { 399 setgroupent(1); 400 ++gropn; 401 } 402 if (ptr == NULL) 403 ptr = grptb[st_hash(name, namelen, GID_SZ)] = 404 (GIDC *)malloc(sizeof(GIDC)); 405 406 /* 407 * no match, look it up, if no match store it as an invalid entry, 408 * or store the matching gid 409 */ 410 if (ptr == NULL) { 411 if ((gr = getgrnam(name)) == NULL) 412 return(-1); 413 *gid = gr->gr_gid; 414 return(0); 415 } 416 417 (void)strlcpy(ptr->name, name, sizeof(ptr->name)); 418 if ((gr = getgrnam(name)) == NULL) { 419 ptr->valid = INVALID; 420 return(-1); 421 } 422 ptr->valid = VALID; 423 *gid = ptr->gid = gr->gr_gid; 424 return(0); 425} 426