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