1 /* $NetBSD: pwcache.c,v 1.25 2003/10/13 07:41:22 agc 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. Neither the name of the University nor the names of its contributors 20 * may be used to endorse or promote products derived from this software 21 * without specific prior written permission. 22 * 23 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 24 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 25 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 26 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 27 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 28 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 29 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 30 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 31 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 32 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 33 * SUCH DAMAGE. 34 */ 35 36 /*- 37 * Copyright (c) 2002 The NetBSD Foundation, Inc. 38 * All rights reserved. 39 * 40 * Redistribution and use in source and binary forms, with or without 41 * modification, are permitted provided that the following conditions 42 * are met: 43 * 1. Redistributions of source code must retain the above copyright 44 * notice, this list of conditions and the following disclaimer. 45 * 2. Redistributions in binary form must reproduce the above copyright 46 * notice, this list of conditions and the following disclaimer in the 47 * documentation and/or other materials provided with the distribution. 48 * 3. All advertising materials mentioning features or use of this software 49 * must display the following acknowledgement: 50 * This product includes software developed by the NetBSD 51 * Foundation, Inc. and its contributors. 52 * 4. Neither the name of The NetBSD Foundation nor the names of its 53 * contributors may be used to endorse or promote products derived 54 * from this software without specific prior written permission. 55 * 56 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 57 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 58 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 59 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 60 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 61 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 62 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 63 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 64 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 65 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 66 * POSSIBILITY OF SUCH DAMAGE. 67 */ 68 69 #include <sys/cdefs.h> 70 #if defined(LIBC_SCCS) && !defined(lint) 71 #if 0 72 static char sccsid[] = "@(#)cache.c 8.1 (Berkeley) 5/31/93"; 73 #else 74 __RCSID("$NetBSD: pwcache.c,v 1.25 2003/10/13 07:41:22 agc Exp $"); 75 #endif 76 #endif /* LIBC_SCCS and not lint */ 77 78 #include "namespace.h" 79 80 #include <sys/types.h> 81 #include <sys/param.h> 82 83 #include <assert.h> 84 #include <grp.h> 85 #include <pwd.h> 86 #include <stdio.h> 87 #include <stdlib.h> 88 #include <string.h> 89 #include <unistd.h> 90 91 #ifdef __weak_alias 92 __weak_alias(user_from_uid,_user_from_uid) 93 __weak_alias(group_from_gid,_group_from_gid) 94 __weak_alias(pwcache_userdb,_pwcache_userdb) 95 __weak_alias(pwcache_groupdb,_pwcache_groupdb) 96 #endif 97 98 #if !HAVE_PWCACHE_USERDB 99 #include "pwcache.h" 100 101 /* 102 * routines that control user, group, uid and gid caches (for the archive 103 * member print routine). 104 * IMPORTANT: 105 * these routines cache BOTH hits and misses, a major performance improvement 106 */ 107 108 /* 109 * function pointers to various name lookup routines. 110 * these may be changed as necessary. 111 */ 112 static int (*_pwcache_setgroupent)(int) = setgroupent; 113 static void (*_pwcache_endgrent)(void) = endgrent; 114 static struct group * (*_pwcache_getgrnam)(const char *) = getgrnam; 115 static struct group * (*_pwcache_getgrgid)(gid_t) = getgrgid; 116 static int (*_pwcache_setpassent)(int) = setpassent; 117 static void (*_pwcache_endpwent)(void) = endpwent; 118 static struct passwd * (*_pwcache_getpwnam)(const char *) = getpwnam; 119 static struct passwd * (*_pwcache_getpwuid)(uid_t) = getpwuid; 120 121 /* 122 * internal state 123 */ 124 static int pwopn; /* is password file open */ 125 static int gropn; /* is group file open */ 126 static UIDC **uidtb; /* uid to name cache */ 127 static GIDC **gidtb; /* gid to name cache */ 128 static UIDC **usrtb; /* user name to uid cache */ 129 static GIDC **grptb; /* group name to gid cache */ 130 131 static int uidtb_fail; /* uidtb_start() failed ? */ 132 static int gidtb_fail; /* gidtb_start() failed ? */ 133 static int usrtb_fail; /* usrtb_start() failed ? */ 134 static int grptb_fail; /* grptb_start() failed ? */ 135 136 137 static u_int st_hash(const char *, size_t, int); 138 static int uidtb_start(void); 139 static int gidtb_start(void); 140 static int usrtb_start(void); 141 static int grptb_start(void); 142 143 144 static u_int 145 st_hash(const char *name, size_t len, int tabsz) 146 { 147 u_int key = 0; 148 149 _DIAGASSERT(name != NULL); 150 151 while (len--) { 152 key += *name++; 153 key = (key << 8) | (key >> 24); 154 } 155 156 return (key % tabsz); 157 } 158 159 /* 160 * uidtb_start 161 * creates an an empty uidtb 162 * Return: 163 * 0 if ok, -1 otherwise 164 */ 165 static int 166 uidtb_start(void) 167 { 168 169 if (uidtb != NULL) 170 return (0); 171 if (uidtb_fail) 172 return (-1); 173 if ((uidtb = (UIDC **)calloc(UID_SZ, sizeof(UIDC *))) == NULL) { 174 ++uidtb_fail; 175 return (-1); 176 } 177 return (0); 178 } 179 180 /* 181 * gidtb_start 182 * creates an an empty gidtb 183 * Return: 184 * 0 if ok, -1 otherwise 185 */ 186 static int 187 gidtb_start(void) 188 { 189 190 if (gidtb != NULL) 191 return (0); 192 if (gidtb_fail) 193 return (-1); 194 if ((gidtb = (GIDC **)calloc(GID_SZ, sizeof(GIDC *))) == NULL) { 195 ++gidtb_fail; 196 return (-1); 197 } 198 return (0); 199 } 200 201 /* 202 * usrtb_start 203 * creates an an empty usrtb 204 * Return: 205 * 0 if ok, -1 otherwise 206 */ 207 static int 208 usrtb_start(void) 209 { 210 211 if (usrtb != NULL) 212 return (0); 213 if (usrtb_fail) 214 return (-1); 215 if ((usrtb = (UIDC **)calloc(UNM_SZ, sizeof(UIDC *))) == NULL) { 216 ++usrtb_fail; 217 return (-1); 218 } 219 return (0); 220 } 221 222 /* 223 * grptb_start 224 * creates an an empty grptb 225 * Return: 226 * 0 if ok, -1 otherwise 227 */ 228 static int 229 grptb_start(void) 230 { 231 232 if (grptb != NULL) 233 return (0); 234 if (grptb_fail) 235 return (-1); 236 if ((grptb = (GIDC **)calloc(GNM_SZ, sizeof(GIDC *))) == NULL) { 237 ++grptb_fail; 238 return (-1); 239 } 240 return (0); 241 } 242 243 #if !HAVE_USER_FROM_UID 244 /* 245 * user_from_uid() 246 * caches the name (if any) for the uid. If noname clear, we always 247 * return the the stored name (if valid or invalid match). 248 * We use a simple hash table. 249 * Return 250 * Pointer to stored name (or a empty string) 251 */ 252 253 const char * 254 user_from_uid(uid_t uid, int noname) 255 { 256 struct passwd *pw; 257 UIDC *ptr, **pptr; 258 259 if ((uidtb == NULL) && (uidtb_start() < 0)) 260 return (NULL); 261 262 /* 263 * see if we have this uid cached 264 */ 265 pptr = uidtb + (uid % UID_SZ); 266 ptr = *pptr; 267 268 if ((ptr != NULL) && (ptr->valid > 0) && (ptr->uid == uid)) { 269 /* 270 * have an entry for this uid 271 */ 272 if (!noname || (ptr->valid == VALID)) 273 return (ptr->name); 274 return (NULL); 275 } 276 277 /* 278 * No entry for this uid, we will add it 279 */ 280 if (!pwopn) { 281 if (_pwcache_setpassent != NULL) 282 (*_pwcache_setpassent)(1); 283 ++pwopn; 284 } 285 286 if (ptr == NULL) 287 *pptr = ptr = (UIDC *)malloc(sizeof(UIDC)); 288 289 if ((pw = (*_pwcache_getpwuid)(uid)) == NULL) { 290 /* 291 * no match for this uid in the local password file 292 * a string that is the uid in numeric format 293 */ 294 if (ptr == NULL) 295 return (NULL); 296 ptr->uid = uid; 297 (void)snprintf(ptr->name, UNMLEN, "%lu", (long) uid); 298 ptr->valid = INVALID; 299 if (noname) 300 return (NULL); 301 } else { 302 /* 303 * there is an entry for this uid in the password file 304 */ 305 if (ptr == NULL) 306 return (pw->pw_name); 307 ptr->uid = uid; 308 (void)strlcpy(ptr->name, pw->pw_name, UNMLEN); 309 ptr->valid = VALID; 310 } 311 return (ptr->name); 312 } 313 314 /* 315 * group_from_gid() 316 * caches the name (if any) for the gid. If noname clear, we always 317 * return the the stored name (if valid or invalid match). 318 * We use a simple hash table. 319 * Return 320 * Pointer to stored name (or a empty string) 321 */ 322 323 const char * 324 group_from_gid(gid_t gid, int noname) 325 { 326 struct group *gr; 327 GIDC *ptr, **pptr; 328 329 if ((gidtb == NULL) && (gidtb_start() < 0)) 330 return (NULL); 331 332 /* 333 * see if we have this gid cached 334 */ 335 pptr = gidtb + (gid % GID_SZ); 336 ptr = *pptr; 337 338 if ((ptr != NULL) && (ptr->valid > 0) && (ptr->gid == gid)) { 339 /* 340 * have an entry for this gid 341 */ 342 if (!noname || (ptr->valid == VALID)) 343 return (ptr->name); 344 return (NULL); 345 } 346 347 /* 348 * No entry for this gid, we will add it 349 */ 350 if (!gropn) { 351 if (_pwcache_setgroupent != NULL) 352 (*_pwcache_setgroupent)(1); 353 ++gropn; 354 } 355 356 if (ptr == NULL) 357 *pptr = ptr = (GIDC *)malloc(sizeof(GIDC)); 358 359 if ((gr = (*_pwcache_getgrgid)(gid)) == NULL) { 360 /* 361 * no match for this gid in the local group file, put in 362 * a string that is the gid in numberic format 363 */ 364 if (ptr == NULL) 365 return (NULL); 366 ptr->gid = gid; 367 (void)snprintf(ptr->name, GNMLEN, "%lu", (long) gid); 368 ptr->valid = INVALID; 369 if (noname) 370 return (NULL); 371 } else { 372 /* 373 * there is an entry for this group in the group file 374 */ 375 if (ptr == NULL) 376 return (gr->gr_name); 377 ptr->gid = gid; 378 (void)strlcpy(ptr->name, gr->gr_name, GNMLEN); 379 ptr->valid = VALID; 380 } 381 return (ptr->name); 382 } 383 #endif /* HAVE_USER_FROM_UID */ 384 385 /* 386 * uid_from_user() 387 * caches the uid for a given user name. We use a simple hash table. 388 * Return 389 * the uid (if any) for a user name, or a -1 if no match can be found 390 */ 391 392 int 393 uid_from_user(const char *name, uid_t *uid) 394 { 395 struct passwd *pw; 396 UIDC *ptr, **pptr; 397 size_t namelen; 398 399 /* 400 * return -1 for mangled names 401 */ 402 if (name == NULL || ((namelen = strlen(name)) == 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 if (_pwcache_setpassent != NULL) 423 (*_pwcache_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 = (*_pwcache_getpwnam)(name)) == NULL) 436 return (-1); 437 *uid = pw->pw_uid; 438 return (0); 439 } 440 (void)strlcpy(ptr->name, name, UNMLEN); 441 if ((pw = (*_pwcache_getpwnam)(name)) == NULL) { 442 ptr->valid = INVALID; 443 return (-1); 444 } 445 ptr->valid = VALID; 446 *uid = ptr->uid = pw->pw_uid; 447 return (0); 448 } 449 450 /* 451 * gid_from_group() 452 * caches the gid for a given group name. We use a simple hash table. 453 * Return 454 * the gid (if any) for a group name, or a -1 if no match can be found 455 */ 456 457 int 458 gid_from_group(const char *name, gid_t *gid) 459 { 460 struct group *gr; 461 GIDC *ptr, **pptr; 462 size_t namelen; 463 464 /* 465 * return -1 for mangled names 466 */ 467 if (name == NULL || ((namelen = strlen(name)) == 0)) 468 return (-1); 469 if ((grptb == NULL) && (grptb_start() < 0)) 470 return (-1); 471 472 /* 473 * look up in hash table, if found and valid return the uid, 474 * if found and invalid, return a -1 475 */ 476 pptr = grptb + st_hash(name, namelen, GID_SZ); 477 ptr = *pptr; 478 479 if ((ptr != NULL) && (ptr->valid > 0) && !strcmp(name, ptr->name)) { 480 if (ptr->valid == INVALID) 481 return (-1); 482 *gid = ptr->gid; 483 return (0); 484 } 485 486 if (!gropn) { 487 if (_pwcache_setgroupent != NULL) 488 (*_pwcache_setgroupent)(1); 489 ++gropn; 490 } 491 492 if (ptr == NULL) 493 *pptr = ptr = (GIDC *)malloc(sizeof(GIDC)); 494 495 /* 496 * no match, look it up, if no match store it as an invalid entry, 497 * or store the matching gid 498 */ 499 if (ptr == NULL) { 500 if ((gr = (*_pwcache_getgrnam)(name)) == NULL) 501 return (-1); 502 *gid = gr->gr_gid; 503 return (0); 504 } 505 506 (void)strlcpy(ptr->name, name, GNMLEN); 507 if ((gr = (*_pwcache_getgrnam)(name)) == NULL) { 508 ptr->valid = INVALID; 509 return (-1); 510 } 511 ptr->valid = VALID; 512 *gid = ptr->gid = gr->gr_gid; 513 return (0); 514 } 515 516 #define FLUSHTB(arr, len, fail) \ 517 do { \ 518 if (arr != NULL) { \ 519 for (i = 0; i < len; i++) \ 520 if (arr[i] != NULL) \ 521 free(arr[i]); \ 522 arr = NULL; \ 523 } \ 524 fail = 0; \ 525 } while (/* CONSTCOND */0); 526 527 int 528 pwcache_userdb( 529 int (*a_setpassent)(int), 530 void (*a_endpwent)(void), 531 struct passwd * (*a_getpwnam)(const char *), 532 struct passwd * (*a_getpwuid)(uid_t)) 533 { 534 int i; 535 536 /* a_setpassent and a_endpwent may be NULL */ 537 if (a_getpwnam == NULL || a_getpwuid == NULL) 538 return (-1); 539 540 if (_pwcache_endpwent != NULL) 541 (*_pwcache_endpwent)(); 542 FLUSHTB(uidtb, UID_SZ, uidtb_fail); 543 FLUSHTB(usrtb, UNM_SZ, usrtb_fail); 544 pwopn = 0; 545 _pwcache_setpassent = a_setpassent; 546 _pwcache_endpwent = a_endpwent; 547 _pwcache_getpwnam = a_getpwnam; 548 _pwcache_getpwuid = a_getpwuid; 549 550 return (0); 551 } 552 553 int 554 pwcache_groupdb( 555 int (*a_setgroupent)(int), 556 void (*a_endgrent)(void), 557 struct group * (*a_getgrnam)(const char *), 558 struct group * (*a_getgrgid)(gid_t)) 559 { 560 int i; 561 562 /* a_setgroupent and a_endgrent may be NULL */ 563 if (a_getgrnam == NULL || a_getgrgid == NULL) 564 return (-1); 565 566 if (_pwcache_endgrent != NULL) 567 (*_pwcache_endgrent)(); 568 FLUSHTB(gidtb, GID_SZ, gidtb_fail); 569 FLUSHTB(grptb, GNM_SZ, grptb_fail); 570 gropn = 0; 571 _pwcache_setgroupent = a_setgroupent; 572 _pwcache_endgrent = a_endgrent; 573 _pwcache_getgrnam = a_getgrnam; 574 _pwcache_getgrgid = a_getgrgid; 575 576 return (0); 577 } 578 579 580 #ifdef TEST_PWCACHE 581 582 struct passwd * 583 test_getpwnam(const char *name) 584 { 585 static struct passwd foo; 586 587 memset(&foo, 0, sizeof(foo)); 588 if (strcmp(name, "toor") == 0) { 589 foo.pw_uid = 666; 590 return &foo; 591 } 592 return (getpwnam(name)); 593 } 594 595 int 596 main(int argc, char *argv[]) 597 { 598 uid_t u; 599 int r, i; 600 601 printf("pass 1 (default userdb)\n"); 602 for (i = 1; i < argc; i++) { 603 printf("i: %d, pwopn %d usrtb_fail %d usrtb %p\n", 604 i, pwopn, usrtb_fail, usrtb); 605 r = uid_from_user(argv[i], &u); 606 if (r == -1) 607 printf(" uid_from_user %s: failed\n", argv[i]); 608 else 609 printf(" uid_from_user %s: %d\n", argv[i], u); 610 } 611 printf("pass 1 finish: pwopn %d usrtb_fail %d usrtb %p\n", 612 pwopn, usrtb_fail, usrtb); 613 614 puts(""); 615 printf("pass 2 (replacement userdb)\n"); 616 printf("pwcache_userdb returned %d\n", 617 pwcache_userdb(setpassent, test_getpwnam, getpwuid)); 618 printf("pwopn %d usrtb_fail %d usrtb %p\n", pwopn, usrtb_fail, usrtb); 619 620 for (i = 1; i < argc; i++) { 621 printf("i: %d, pwopn %d usrtb_fail %d usrtb %p\n", 622 i, pwopn, usrtb_fail, usrtb); 623 u = -1; 624 r = uid_from_user(argv[i], &u); 625 if (r == -1) 626 printf(" uid_from_user %s: failed\n", argv[i]); 627 else 628 printf(" uid_from_user %s: %d\n", argv[i], u); 629 } 630 printf("pass 2 finish: pwopn %d usrtb_fail %d usrtb %p\n", 631 pwopn, usrtb_fail, usrtb); 632 633 puts(""); 634 printf("pass 3 (null pointers)\n"); 635 printf("pwcache_userdb returned %d\n", 636 pwcache_userdb(NULL, NULL, NULL)); 637 638 return (0); 639 } 640 #endif /* TEST_PWCACHE */ 641 #endif /* !HAVE_PWCACHE_USERDB */ 642