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