1 /* $NetBSD: pwcache.c,v 1.11 1998/07/28 18:13:53 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.11 1998/07/28 18:13:53 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 ptr = (UIDC *)malloc(sizeof(UIDC)); 265 *pptr = ptr; 266 267 if ((pw = getpwuid(uid)) == NULL) { 268 /* 269 * no match for this uid in the local password file 270 * a string that is the uid in numberic format 271 */ 272 if (ptr == NULL) 273 return (NULL); 274 ptr->uid = uid; 275 # ifdef NET2_STAT 276 (void)snprintf(ptr->name, UNMLEN, "%u", uid); 277 # else 278 (void)snprintf(ptr->name, UNMLEN, "%lu", (long) uid); 279 # endif 280 ptr->valid = INVALID; 281 if (noname) 282 return (NULL); 283 } else { 284 /* 285 * there is an entry for this uid in the password file 286 */ 287 if (ptr == NULL) 288 return (pw->pw_name); 289 ptr->uid = uid; 290 (void)strncpy(ptr->name, pw->pw_name, UNMLEN); 291 ptr->name[UNMLEN-1] = '\0'; 292 ptr->valid = VALID; 293 } 294 return (ptr->name); 295 } 296 297 /* 298 * group_from_gid() 299 * caches the name (if any) for the gid. If noname clear, we always return the 300 * the stored name (if valid or invalid match). We use a simple hash table. 301 * Return 302 * Pointer to stored name (or a empty string) 303 */ 304 305 #if __STDC__ 306 const char * 307 group_from_gid(gid_t gid, int noname) 308 #else 309 const char * 310 group_from_gid(gid, noname) 311 gid_t gid; 312 int noname; 313 #endif 314 { 315 struct group *gr; 316 GIDC *ptr, **pptr; 317 318 if ((gidtb == NULL) && (gidtb_start() < 0)) 319 return (NULL); 320 321 /* 322 * see if we have this gid cached 323 */ 324 pptr = gidtb + (gid % GID_SZ); 325 ptr = *pptr; 326 327 if ((ptr != NULL) && (ptr->valid > 0) && (ptr->gid == gid)) { 328 /* 329 * have an entry for this gid 330 */ 331 if (!noname || (ptr->valid == VALID)) 332 return (ptr->name); 333 return (NULL); 334 } 335 336 /* 337 * No entry for this gid, we will add it 338 */ 339 if (!gropn) { 340 setgroupent(1); 341 ++gropn; 342 } 343 344 if (ptr == NULL) 345 ptr = (GIDC *)malloc(sizeof(GIDC)); 346 *pptr = ptr; 347 348 if ((gr = getgrgid(gid)) == NULL) { 349 /* 350 * no match for this gid in the local group file, put in 351 * a string that is the gid in numberic format 352 */ 353 if (ptr == NULL) 354 return (NULL); 355 ptr->gid = gid; 356 # ifdef NET2_STAT 357 (void)snprintf(ptr->name, GNMLEN, "%u", gid); 358 # else 359 (void)snprintf(ptr->name, GNMLEN, "%lu", (long) gid); 360 # endif 361 ptr->valid = INVALID; 362 if (noname) 363 return (NULL); 364 } else { 365 /* 366 * there is an entry for this group in the group file 367 */ 368 if (ptr == NULL) 369 return (gr->gr_name); 370 ptr->gid = gid; 371 (void)strncpy(ptr->name, gr->gr_name, GNMLEN); 372 ptr->name[GNMLEN-1] = '\0'; 373 ptr->valid = VALID; 374 } 375 return (ptr->name); 376 } 377 378 /* 379 * uid_from_user() 380 * caches the uid for a given user name. We use a simple hash table. 381 * Return 382 * the uid (if any) for a user name, or a -1 if no match can be found 383 */ 384 385 #if __STDC__ 386 int 387 uid_from_user(const char *name, uid_t *uid) 388 #else 389 int 390 uid_from_user(name, uid) 391 const char *name; 392 uid_t *uid; 393 #endif 394 { 395 struct passwd *pw; 396 UIDC *ptr, **pptr; 397 size_t namelen; 398 399 /* 400 * return -1 for mangled names 401 */ 402 if (((namelen = strlen(name)) == 0) || (name[0] == '\0')) 403 return (-1); 404 if ((usrtb == NULL) && (usrtb_start() < 0)) 405 return (-1); 406 407 /* 408 * look up in hash table, if found and valid return the uid, 409 * if found and invalid, return a -1 410 */ 411 pptr = usrtb + st_hash(name, namelen, UNM_SZ); 412 ptr = *pptr; 413 414 if ((ptr != NULL) && (ptr->valid > 0) && !strcmp(name, ptr->name)) { 415 if (ptr->valid == INVALID) 416 return (-1); 417 *uid = ptr->uid; 418 return (0); 419 } 420 421 if (!pwopn) { 422 setpassent(1); 423 ++pwopn; 424 } 425 426 if (ptr == NULL) 427 ptr = (UIDC *)malloc(sizeof(UIDC)); 428 *pptr = ptr; 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 (((namelen = strlen(name)) == 0) || (name[0] == '\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 ptr = (GIDC *)malloc(sizeof(GIDC)); 501 *pptr = ptr; 502 503 /* 504 * no match, look it up, if no match store it as an invalid entry, 505 * or store the matching gid 506 */ 507 if (ptr == NULL) { 508 if ((gr = getgrnam(name)) == NULL) 509 return (-1); 510 *gid = gr->gr_gid; 511 return (0); 512 } 513 514 (void)strncpy(ptr->name, name, GNMLEN); 515 ptr->name[GNMLEN-1] = '\0'; 516 if ((gr = getgrnam(name)) == NULL) { 517 ptr->valid = INVALID; 518 return (-1); 519 } 520 ptr->valid = VALID; 521 *gid = ptr->gid = gr->gr_gid; 522 return (0); 523 } 524