1 /* $OpenBSD: pwcache.c,v 1.15 2018/09/22 02:47:23 millert 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 #include <sys/types.h> 37 38 #include <assert.h> 39 #include <grp.h> 40 #include <pwd.h> 41 #include <stdio.h> 42 #include <stdlib.h> 43 #include <string.h> 44 #include <unistd.h> 45 46 /* 47 * Constants and data structures used to implement group and password file 48 * caches. Name lengths have been chosen to be as large as those supported 49 * by the passwd and group files as well as the standard archive formats. 50 * CACHE SIZES MUST BE PRIME 51 */ 52 #define UNMLEN 32 /* >= user name found in any protocol */ 53 #define GNMLEN 32 /* >= group name found in any protocol */ 54 #define UID_SZ 317 /* size of uid to user_name cache */ 55 #define UNM_SZ 317 /* size of user_name to uid cache */ 56 #define GID_SZ 251 /* size of gid to group_name cache */ 57 #define GNM_SZ 251 /* size of group_name to gid cache */ 58 #define VALID 1 /* entry and name are valid */ 59 #define INVALID 2 /* entry valid, name NOT valid */ 60 61 /* 62 * Node structures used in the user, group, uid, and gid caches. 63 */ 64 65 typedef struct uidc { 66 int valid; /* is this a valid or a miss entry */ 67 char name[UNMLEN]; /* uid name */ 68 uid_t uid; /* cached uid */ 69 } UIDC; 70 71 typedef struct gidc { 72 int valid; /* is this a valid or a miss entry */ 73 char name[GNMLEN]; /* gid name */ 74 gid_t gid; /* cached gid */ 75 } GIDC; 76 77 /* 78 * Routines that control user, group, uid and gid caches. 79 * Traditional passwd/group cache routines perform quite poorly with 80 * archives. The chances of hitting a valid lookup with an archive is quite a 81 * bit worse than with files already resident on the file system. These misses 82 * create a MAJOR performance cost. To adress this problem, these routines 83 * cache both hits and misses. 84 */ 85 86 static UIDC **uidtb; /* uid to name cache */ 87 static GIDC **gidtb; /* gid to name cache */ 88 static UIDC **usrtb; /* user name to uid cache */ 89 static GIDC **grptb; /* group name to gid cache */ 90 91 static u_int 92 st_hash(const char *name, size_t len, int tabsz) 93 { 94 u_int key = 0; 95 96 assert(name != NULL); 97 98 while (len--) { 99 key += *name++; 100 key = (key << 8) | (key >> 24); 101 } 102 103 return key % tabsz; 104 } 105 106 /* 107 * uidtb_start 108 * creates an an empty uidtb 109 * Return: 110 * 0 if ok, -1 otherwise 111 */ 112 static int 113 uidtb_start(void) 114 { 115 static int fail = 0; 116 117 if (uidtb != NULL) 118 return 0; 119 if (fail) 120 return -1; 121 if ((uidtb = calloc(UID_SZ, sizeof(UIDC *))) == NULL) { 122 ++fail; 123 return -1; 124 } 125 return 0; 126 } 127 128 /* 129 * gidtb_start 130 * creates an an empty gidtb 131 * Return: 132 * 0 if ok, -1 otherwise 133 */ 134 static 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 = 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 static int 157 usrtb_start(void) 158 { 159 static int fail = 0; 160 161 if (usrtb != NULL) 162 return 0; 163 if (fail) 164 return -1; 165 if ((usrtb = calloc(UNM_SZ, sizeof(UIDC *))) == NULL) { 166 ++fail; 167 return -1; 168 } 169 return 0; 170 } 171 172 /* 173 * grptb_start 174 * creates an an empty grptb 175 * Return: 176 * 0 if ok, -1 otherwise 177 */ 178 static int 179 grptb_start(void) 180 { 181 static int fail = 0; 182 183 if (grptb != NULL) 184 return 0; 185 if (fail) 186 return -1; 187 if ((grptb = calloc(GNM_SZ, sizeof(GIDC *))) == NULL) { 188 ++fail; 189 return -1; 190 } 191 return 0; 192 } 193 194 /* 195 * user_from_uid() 196 * caches the name (if any) for the uid. If noname clear, we always 197 * return the stored name (if valid or invalid match). 198 * We use a simple hash table. 199 * Return: 200 * Pointer to stored name (or a empty string) 201 */ 202 const char * 203 user_from_uid(uid_t uid, int noname) 204 { 205 struct passwd pwstore, *pw = NULL; 206 char pwbuf[_PW_BUF_LEN]; 207 UIDC **pptr, *ptr = NULL; 208 209 if ((uidtb != NULL) || (uidtb_start() == 0)) { 210 /* 211 * see if we have this uid cached 212 */ 213 pptr = uidtb + (uid % UID_SZ); 214 ptr = *pptr; 215 216 if ((ptr != NULL) && (ptr->valid > 0) && (ptr->uid == uid)) { 217 /* 218 * have an entry for this uid 219 */ 220 if (!noname || (ptr->valid == VALID)) 221 return ptr->name; 222 return NULL; 223 } 224 225 if (ptr == NULL) 226 *pptr = ptr = malloc(sizeof(UIDC)); 227 } 228 229 getpwuid_r(uid, &pwstore, pwbuf, sizeof(pwbuf), &pw); 230 if (pw == NULL) { 231 /* 232 * no match for this uid in the local password file 233 * a string that is the uid in numeric format 234 */ 235 if (ptr == NULL) 236 return NULL; 237 ptr->uid = uid; 238 (void)snprintf(ptr->name, UNMLEN, "%u", uid); 239 ptr->valid = INVALID; 240 if (noname) 241 return NULL; 242 } else { 243 /* 244 * there is an entry for this uid in the password file 245 */ 246 if (ptr == NULL) 247 return pw->pw_name; 248 ptr->uid = uid; 249 (void)strlcpy(ptr->name, pw->pw_name, sizeof(ptr->name)); 250 ptr->valid = VALID; 251 } 252 return ptr->name; 253 } 254 255 /* 256 * group_from_gid() 257 * caches the name (if any) for the gid. If noname clear, we always 258 * return the stored name (if valid or invalid match). 259 * We use a simple hash table. 260 * Return: 261 * Pointer to stored name (or a empty string) 262 */ 263 const char * 264 group_from_gid(gid_t gid, int noname) 265 { 266 struct group grstore, *gr = NULL; 267 char grbuf[_GR_BUF_LEN]; 268 GIDC **pptr, *ptr = NULL; 269 270 if ((gidtb != NULL) || (gidtb_start() == 0)) { 271 /* 272 * see if we have this gid cached 273 */ 274 pptr = gidtb + (gid % GID_SZ); 275 ptr = *pptr; 276 277 if ((ptr != NULL) && (ptr->valid > 0) && (ptr->gid == gid)) { 278 /* 279 * have an entry for this gid 280 */ 281 if (!noname || (ptr->valid == VALID)) 282 return ptr->name; 283 return NULL; 284 } 285 286 if (ptr == NULL) 287 *pptr = ptr = malloc(sizeof(GIDC)); 288 } 289 290 getgrgid_r(gid, &grstore, grbuf, sizeof(grbuf), &gr); 291 if (gr == NULL) { 292 /* 293 * no match for this gid in the local group file, put in 294 * a string that is the gid in numeric format 295 */ 296 if (ptr == NULL) 297 return NULL; 298 ptr->gid = gid; 299 (void)snprintf(ptr->name, GNMLEN, "%u", gid); 300 ptr->valid = INVALID; 301 if (noname) 302 return NULL; 303 } else { 304 /* 305 * there is an entry for this group in the group file 306 */ 307 if (ptr == NULL) 308 return gr->gr_name; 309 ptr->gid = gid; 310 (void)strlcpy(ptr->name, gr->gr_name, sizeof(ptr->name)); 311 ptr->valid = VALID; 312 } 313 return ptr->name; 314 } 315 316 /* 317 * uid_from_user() 318 * caches the uid for a given user name. We use a simple hash table. 319 * Return: 320 * 0 if the user name is found (filling in uid), -1 otherwise 321 */ 322 int 323 uid_from_user(const char *name, uid_t *uid) 324 { 325 struct passwd pwstore, *pw = NULL; 326 char pwbuf[_PW_BUF_LEN]; 327 UIDC **pptr, *ptr = NULL; 328 size_t namelen; 329 330 /* 331 * return -1 for mangled names 332 */ 333 if (name == NULL || ((namelen = strlen(name)) == 0)) 334 return -1; 335 336 if ((usrtb != NULL) || (usrtb_start() == 0)) { 337 /* 338 * look up in hash table, if found and valid return the uid, 339 * if found and invalid, return a -1 340 */ 341 pptr = usrtb + st_hash(name, namelen, UNM_SZ); 342 ptr = *pptr; 343 344 if ((ptr != NULL) && (ptr->valid > 0) && 345 strcmp(name, ptr->name) == 0) { 346 if (ptr->valid == INVALID) 347 return -1; 348 *uid = ptr->uid; 349 return 0; 350 } 351 352 if (ptr == NULL) 353 *pptr = ptr = malloc(sizeof(UIDC)); 354 } 355 356 /* 357 * no match, look it up, if no match store it as an invalid entry, 358 * or store the matching uid 359 */ 360 getpwnam_r(name, &pwstore, pwbuf, sizeof(pwbuf), &pw); 361 if (ptr == NULL) { 362 if (pw == NULL) 363 return -1; 364 *uid = pw->pw_uid; 365 return 0; 366 } 367 (void)strlcpy(ptr->name, name, sizeof(ptr->name)); 368 if (pw == NULL) { 369 ptr->valid = INVALID; 370 return -1; 371 } 372 ptr->valid = VALID; 373 *uid = ptr->uid = pw->pw_uid; 374 return 0; 375 } 376 377 /* 378 * gid_from_group() 379 * caches the gid for a given group name. We use a simple hash table. 380 * Return: 381 * 0 if the group name is found (filling in gid), -1 otherwise 382 */ 383 int 384 gid_from_group(const char *name, gid_t *gid) 385 { 386 struct group grstore, *gr = NULL; 387 char grbuf[_GR_BUF_LEN]; 388 GIDC **pptr, *ptr = NULL; 389 size_t namelen; 390 391 /* 392 * return -1 for mangled names 393 */ 394 if (name == NULL || ((namelen = strlen(name)) == 0)) 395 return -1; 396 397 if ((grptb != NULL) || (grptb_start() == 0)) { 398 /* 399 * look up in hash table, if found and valid return the uid, 400 * if found and invalid, return a -1 401 */ 402 pptr = grptb + st_hash(name, namelen, GID_SZ); 403 ptr = *pptr; 404 405 if ((ptr != NULL) && (ptr->valid > 0) && 406 strcmp(name, ptr->name) == 0) { 407 if (ptr->valid == INVALID) 408 return -1; 409 *gid = ptr->gid; 410 return 0; 411 } 412 413 if (ptr == NULL) 414 *pptr = ptr = malloc(sizeof(GIDC)); 415 } 416 417 /* 418 * no match, look it up, if no match store it as an invalid entry, 419 * or store the matching gid 420 */ 421 getgrnam_r(name, &grstore, grbuf, sizeof(grbuf), &gr); 422 if (ptr == NULL) { 423 if (gr == NULL) 424 return -1; 425 *gid = gr->gr_gid; 426 return 0; 427 } 428 429 (void)strlcpy(ptr->name, name, sizeof(ptr->name)); 430 if (gr == NULL) { 431 ptr->valid = INVALID; 432 return -1; 433 } 434 ptr->valid = VALID; 435 *gid = ptr->gid = gr->gr_gid; 436 return 0; 437 } 438