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