1156701Smux/*- 2156701Smux * Copyright (c) 2006, Maxime Henrion <mux@FreeBSD.org> 3156701Smux * All rights reserved. 4156701Smux * 5156701Smux * Redistribution and use in source and binary forms, with or without 6156701Smux * modification, are permitted provided that the following conditions 7156701Smux * are met: 8156701Smux * 1. Redistributions of source code must retain the above copyright 9156701Smux * notice, this list of conditions and the following disclaimer. 10156701Smux * 2. Redistributions in binary form must reproduce the above copyright 11156701Smux * notice, this list of conditions and the following disclaimer in the 12156701Smux * documentation and/or other materials provided with the distribution. 13156701Smux * 14156701Smux * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15156701Smux * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16156701Smux * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17156701Smux * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 18156701Smux * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19156701Smux * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20156701Smux * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21156701Smux * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22156701Smux * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23156701Smux * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24156701Smux * SUCH DAMAGE. 25156701Smux * 26156701Smux * $FreeBSD$ 27156701Smux */ 28156701Smux#include <sys/types.h> 29156701Smux 30156701Smux#include <assert.h> 31156701Smux#include <grp.h> 32156701Smux#include <pthread.h> 33156701Smux#include <pwd.h> 34156701Smux#include <stdlib.h> 35156701Smux#include <string.h> 36156701Smux 37156701Smux#include "idcache.h" 38156701Smux#include "misc.h" 39156701Smux 40156701Smux/* 41156701Smux * Constants and data structures used to implement the thread-safe 42156701Smux * group and password file caches. Cache sizes must be prime. 43156701Smux */ 44156701Smux#define UIDTONAME_SZ 317 /* Size of uid -> user name cache */ 45156701Smux#define NAMETOUID_SZ 317 /* Size of user name -> uid cache */ 46156701Smux#define GIDTONAME_SZ 317 /* Size of gid -> group name cache */ 47156701Smux#define NAMETOGID_SZ 317 /* Size of group name -> gid cache */ 48156701Smux 49156701Smux/* Node structures used to cache lookups. */ 50156701Smuxstruct uidc { 51156701Smux char *name; /* user name */ 52156701Smux uid_t uid; /* cached uid */ 53156701Smux int valid; /* is this a valid or a miss entry */ 54156701Smux struct uidc *next; /* for collisions */ 55156701Smux}; 56156701Smux 57156701Smuxstruct gidc { 58156701Smux char *name; /* group name */ 59156701Smux gid_t gid; /* cached gid */ 60156701Smux int valid; /* is this a valid or a miss entry */ 61156701Smux struct gidc *next; /* for collisions */ 62156701Smux}; 63156701Smux 64156701Smuxstatic struct uidc **uidtoname; /* uid to user name cache */ 65156701Smuxstatic struct gidc **gidtoname; /* gid to group name cache */ 66156701Smuxstatic struct uidc **nametouid; /* user name to uid cache */ 67156701Smuxstatic struct gidc **nametogid; /* group name to gid cache */ 68156701Smux 69156701Smuxstatic pthread_mutex_t uid_mtx; 70156701Smuxstatic pthread_mutex_t gid_mtx; 71156701Smux 72156701Smuxstatic void uid_lock(void); 73156701Smuxstatic void uid_unlock(void); 74156701Smuxstatic void gid_lock(void); 75156701Smuxstatic void gid_unlock(void); 76156701Smux 77156701Smuxstatic uint32_t hash(const char *); 78156701Smux 79156701Smux/* A 32-bit version of Peter Weinberger's (PJW) hash algorithm, 80156701Smux as used by ELF for hashing function names. */ 81156701Smuxstatic uint32_t 82156701Smuxhash(const char *name) 83156701Smux{ 84156701Smux uint32_t g, h; 85156701Smux 86156701Smux h = 0; 87156701Smux while(*name != '\0') { 88156701Smux h = (h << 4) + *name++; 89156701Smux if ((g = h & 0xF0000000)) { 90156701Smux h ^= g >> 24; 91156701Smux h &= 0x0FFFFFFF; 92156701Smux } 93156701Smux } 94156701Smux return (h); 95156701Smux} 96156701Smux 97156701Smuxstatic void 98156701Smuxuid_lock(void) 99156701Smux{ 100156701Smux int error; 101156701Smux 102156701Smux error = pthread_mutex_lock(&uid_mtx); 103156701Smux assert(!error); 104156701Smux} 105156701Smux 106156701Smuxstatic void 107156701Smuxuid_unlock(void) 108156701Smux{ 109156701Smux int error; 110156701Smux 111156701Smux error = pthread_mutex_unlock(&uid_mtx); 112156701Smux assert(!error); 113156701Smux} 114156701Smux 115156701Smuxstatic void 116156701Smuxgid_lock(void) 117156701Smux{ 118156701Smux int error; 119156701Smux 120156701Smux error = pthread_mutex_lock(&gid_mtx); 121156701Smux assert(!error); 122156701Smux} 123156701Smux 124156701Smuxstatic void 125156701Smuxgid_unlock(void) 126156701Smux{ 127156701Smux int error; 128156701Smux 129156701Smux error = pthread_mutex_unlock(&gid_mtx); 130156701Smux assert(!error); 131156701Smux} 132156701Smux 133156701Smuxstatic void 134156701Smuxuidc_insert(struct uidc **tbl, struct uidc *uidc, uint32_t key) 135156701Smux{ 136156701Smux 137156701Smux uidc->next = tbl[key]; 138156701Smux tbl[key] = uidc; 139156701Smux} 140156701Smux 141156701Smuxstatic void 142156701Smuxgidc_insert(struct gidc **tbl, struct gidc *gidc, uint32_t key) 143156701Smux{ 144156701Smux 145156701Smux gidc->next = tbl[key]; 146156701Smux tbl[key] = gidc; 147156701Smux} 148156701Smux 149156701Smux/* Return the user name for this uid, or NULL if it's not found. */ 150156701Smuxchar * 151156701Smuxgetuserbyid(uid_t uid) 152156701Smux{ 153156701Smux struct passwd *pw; 154156701Smux struct uidc *uidc, *uidc2; 155156701Smux uint32_t key, key2; 156156701Smux 157156701Smux key = uid % UIDTONAME_SZ; 158156701Smux uid_lock(); 159156701Smux uidc = uidtoname[key]; 160156701Smux while (uidc != NULL) { 161156701Smux if (uidc->uid == uid) 162156701Smux break; 163156701Smux uidc = uidc->next; 164156701Smux } 165156701Smux 166156701Smux if (uidc == NULL) { 167156701Smux /* We didn't find this uid, look it up and add it. */ 168156701Smux uidc = xmalloc(sizeof(struct uidc)); 169156701Smux uidc->uid = uid; 170156701Smux pw = getpwuid(uid); 171156701Smux if (pw != NULL) { 172156701Smux /* This uid is in the password file. */ 173156701Smux uidc->name = xstrdup(pw->pw_name); 174156701Smux uidc->valid = 1; 175156701Smux /* Also add it to the name -> gid table. */ 176156701Smux uidc2 = xmalloc(sizeof(struct uidc)); 177156701Smux uidc2->uid = uid; 178156701Smux uidc2->name = uidc->name; /* We reuse the pointer. */ 179156701Smux uidc2->valid = 1; 180156701Smux key2 = hash(uidc->name) % NAMETOUID_SZ; 181156701Smux uidc_insert(nametouid, uidc2, key2); 182156701Smux } else { 183156701Smux /* Add a miss entry for this uid. */ 184156701Smux uidc->name = NULL; 185156701Smux uidc->valid = 0; 186156701Smux } 187156701Smux uidc_insert(uidtoname, uidc, key); 188156701Smux } 189156701Smux /* It is safe to unlock here since the cache structure 190156701Smux is not going to get freed or changed. */ 191156701Smux uid_unlock(); 192156701Smux return (uidc->name); 193156701Smux} 194156701Smux 195156701Smux/* Return the group name for this gid, or NULL if it's not found. */ 196156701Smuxchar * 197156701Smuxgetgroupbyid(gid_t gid) 198156701Smux{ 199156701Smux struct group *gr; 200156701Smux struct gidc *gidc, *gidc2; 201156701Smux uint32_t key, key2; 202156701Smux 203156701Smux key = gid % GIDTONAME_SZ; 204156701Smux gid_lock(); 205156701Smux gidc = gidtoname[key]; 206156701Smux while (gidc != NULL) { 207156701Smux if (gidc->gid == gid) 208156701Smux break; 209156701Smux gidc = gidc->next; 210156701Smux } 211156701Smux 212156701Smux if (gidc == NULL) { 213156701Smux /* We didn't find this gid, look it up and add it. */ 214156701Smux gidc = xmalloc(sizeof(struct gidc)); 215156701Smux gidc->gid = gid; 216156701Smux gr = getgrgid(gid); 217156701Smux if (gr != NULL) { 218156701Smux /* This gid is in the group file. */ 219156701Smux gidc->name = xstrdup(gr->gr_name); 220156701Smux gidc->valid = 1; 221156701Smux /* Also add it to the name -> gid table. */ 222156701Smux gidc2 = xmalloc(sizeof(struct gidc)); 223156701Smux gidc2->gid = gid; 224156701Smux gidc2->name = gidc->name; /* We reuse the pointer. */ 225156701Smux gidc2->valid = 1; 226156701Smux key2 = hash(gidc->name) % NAMETOGID_SZ; 227156701Smux gidc_insert(nametogid, gidc2, key2); 228156701Smux } else { 229156701Smux /* Add a miss entry for this gid. */ 230156701Smux gidc->name = NULL; 231156701Smux gidc->valid = 0; 232156701Smux } 233156701Smux gidc_insert(gidtoname, gidc, key); 234156701Smux } 235156701Smux /* It is safe to unlock here since the cache structure 236156701Smux is not going to get freed or changed. */ 237156701Smux gid_unlock(); 238156701Smux return (gidc->name); 239156701Smux} 240156701Smux 241156701Smux/* Finds the uid for this user name. If it's found, the gid is stored 242156701Smux in *uid and 0 is returned. Otherwise, -1 is returned. */ 243156701Smuxint 244156701Smuxgetuidbyname(const char *name, uid_t *uid) 245156701Smux{ 246156701Smux struct passwd *pw; 247156701Smux struct uidc *uidc, *uidc2; 248156701Smux uint32_t key, key2; 249156701Smux 250156701Smux uid_lock(); 251156701Smux key = hash(name) % NAMETOUID_SZ; 252156701Smux uidc = nametouid[key]; 253156701Smux while (uidc != NULL) { 254156701Smux if (strcmp(uidc->name, name) == 0) 255156701Smux break; 256156701Smux uidc = uidc->next; 257156701Smux } 258156701Smux 259156701Smux if (uidc == NULL) { 260156701Smux uidc = xmalloc(sizeof(struct uidc)); 261156701Smux uidc->name = xstrdup(name); 262156701Smux pw = getpwnam(name); 263156701Smux if (pw != NULL) { 264156701Smux /* This user name is in the password file. */ 265156701Smux uidc->valid = 1; 266156701Smux uidc->uid = pw->pw_uid; 267156701Smux /* Also add it to the uid -> name table. */ 268156701Smux uidc2 = xmalloc(sizeof(struct uidc)); 269156701Smux uidc2->name = uidc->name; /* We reuse the pointer. */ 270156701Smux uidc2->uid = uidc->uid; 271156701Smux uidc2->valid = 1; 272156701Smux key2 = uidc2->uid % UIDTONAME_SZ; 273156701Smux uidc_insert(uidtoname, uidc2, key2); 274156701Smux } else { 275156701Smux /* Add a miss entry for this user name. */ 276156701Smux uidc->valid = 0; 277156701Smux uidc->uid = (uid_t)-1; /* Should not be accessed. */ 278156701Smux } 279156701Smux uidc_insert(nametouid, uidc, key); 280156701Smux } 281156701Smux /* It is safe to unlock here since the cache structure 282156701Smux is not going to get freed or changed. */ 283156701Smux uid_unlock(); 284156701Smux if (!uidc->valid) 285156701Smux return (-1); 286156701Smux *uid = uidc->uid; 287156701Smux return (0); 288156701Smux} 289156701Smux 290156701Smux/* Finds the gid for this group name. If it's found, the gid is stored 291156701Smux in *gid and 0 is returned. Otherwise, -1 is returned. */ 292156701Smuxint 293156701Smuxgetgidbyname(const char *name, gid_t *gid) 294156701Smux{ 295156701Smux struct group *gr; 296156701Smux struct gidc *gidc, *gidc2; 297156701Smux uint32_t key, key2; 298156701Smux 299156701Smux gid_lock(); 300156701Smux key = hash(name) % NAMETOGID_SZ; 301156701Smux gidc = nametogid[key]; 302156701Smux while (gidc != NULL) { 303156701Smux if (strcmp(gidc->name, name) == 0) 304156701Smux break; 305156701Smux gidc = gidc->next; 306156701Smux } 307156701Smux 308156701Smux if (gidc == NULL) { 309156701Smux gidc = xmalloc(sizeof(struct gidc)); 310156701Smux gidc->name = xstrdup(name); 311156701Smux gr = getgrnam(name); 312156701Smux if (gr != NULL) { 313156701Smux /* This group name is in the group file. */ 314156701Smux gidc->gid = gr->gr_gid; 315156701Smux gidc->valid = 1; 316156701Smux /* Also add it to the gid -> name table. */ 317156701Smux gidc2 = xmalloc(sizeof(struct gidc)); 318156701Smux gidc2->name = gidc->name; /* We reuse the pointer. */ 319156701Smux gidc2->gid = gidc->gid; 320156701Smux gidc2->valid = 1; 321156701Smux key2 = gidc2->gid % GIDTONAME_SZ; 322156701Smux gidc_insert(gidtoname, gidc2, key2); 323156701Smux } else { 324156701Smux /* Add a miss entry for this group name. */ 325156701Smux gidc->gid = (gid_t)-1; /* Should not be accessed. */ 326156701Smux gidc->valid = 0; 327156701Smux } 328156701Smux gidc_insert(nametogid, gidc, key); 329156701Smux } 330156701Smux /* It is safe to unlock here since the cache structure 331156701Smux is not going to get freed or changed. */ 332156701Smux gid_unlock(); 333156701Smux if (!gidc->valid) 334156701Smux return (-1); 335156701Smux *gid = gidc->gid; 336156701Smux return (0); 337156701Smux} 338156701Smux 339156701Smux/* Initialize the cache structures. */ 340156701Smuxvoid 341156701Smuxidcache_init(void) 342156701Smux{ 343156701Smux 344156701Smux pthread_mutex_init(&uid_mtx, NULL); 345156701Smux pthread_mutex_init(&gid_mtx, NULL); 346156701Smux uidtoname = xmalloc(UIDTONAME_SZ * sizeof(struct uidc *)); 347156701Smux gidtoname = xmalloc(GIDTONAME_SZ * sizeof(struct gidc *)); 348156701Smux nametouid = xmalloc(NAMETOUID_SZ * sizeof(struct uidc *)); 349156701Smux nametogid = xmalloc(NAMETOGID_SZ * sizeof(struct gidc *)); 350156701Smux memset(uidtoname, 0, UIDTONAME_SZ * sizeof(struct uidc *)); 351156701Smux memset(gidtoname, 0, GIDTONAME_SZ * sizeof(struct gidc *)); 352156701Smux memset(nametouid, 0, NAMETOUID_SZ * sizeof(struct uidc *)); 353156701Smux memset(nametogid, 0, NAMETOGID_SZ * sizeof(struct gidc *)); 354156701Smux} 355156701Smux 356156701Smux/* Cleanup the cache structures. */ 357156701Smuxvoid 358156701Smuxidcache_fini(void) 359156701Smux{ 360156701Smux struct uidc *uidc, *uidc2; 361156701Smux struct gidc *gidc, *gidc2; 362156701Smux size_t i; 363156701Smux 364156701Smux for (i = 0; i < UIDTONAME_SZ; i++) { 365156701Smux uidc = uidtoname[i]; 366156701Smux while (uidc != NULL) { 367156701Smux if (uidc->name != NULL) { 368156701Smux assert(uidc->valid); 369156701Smux free(uidc->name); 370156701Smux } 371156701Smux uidc2 = uidc->next; 372156701Smux free(uidc); 373156701Smux uidc = uidc2; 374156701Smux } 375156701Smux } 376156701Smux free(uidtoname); 377156701Smux for (i = 0; i < NAMETOUID_SZ; i++) { 378156701Smux uidc = nametouid[i]; 379156701Smux while (uidc != NULL) { 380156701Smux assert(uidc->name != NULL); 381156701Smux /* If it's a valid entry, it has been added to both the 382156701Smux uidtoname and nametouid tables, and the name pointer 383156701Smux has been reused for both entries. Thus, the name 384156701Smux pointer has already been freed in the loop above. */ 385156701Smux if (!uidc->valid) 386156701Smux free(uidc->name); 387156701Smux uidc2 = uidc->next; 388156701Smux free(uidc); 389156701Smux uidc = uidc2; 390156701Smux } 391156701Smux } 392156701Smux free(nametouid); 393156701Smux for (i = 0; i < GIDTONAME_SZ; i++) { 394156701Smux gidc = gidtoname[i]; 395156701Smux while (gidc != NULL) { 396156701Smux if (gidc->name != NULL) { 397156701Smux assert(gidc->valid); 398156701Smux free(gidc->name); 399156701Smux } 400156701Smux gidc2 = gidc->next; 401156701Smux free(gidc); 402156701Smux gidc = gidc2; 403156701Smux } 404156701Smux } 405156701Smux free(gidtoname); 406156701Smux for (i = 0; i < NAMETOGID_SZ; i++) { 407156701Smux gidc = nametogid[i]; 408156701Smux while (gidc != NULL) { 409156701Smux assert(gidc->name != NULL); 410156701Smux /* See above comment. */ 411156701Smux if (!gidc->valid) 412156701Smux free(gidc->name); 413156701Smux gidc2 = gidc->next; 414156701Smux free(gidc); 415156701Smux gidc = gidc2; 416156701Smux } 417156701Smux } 418156701Smux free(nametogid); 419156701Smux pthread_mutex_destroy(&uid_mtx); 420156701Smux pthread_mutex_destroy(&gid_mtx); 421156701Smux} 422