1/*- 2 * Copyright (c) 2006, Maxime Henrion <mux@FreeBSD.org> 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * 1. Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * 2. Redistributions in binary form must reproduce the above copyright 11 * notice, this list of conditions and the following disclaimer in the 12 * documentation and/or other materials provided with the distribution. 13 * 14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24 * SUCH DAMAGE. 25 * 26 * $FreeBSD$ 27 */ 28#include <sys/types.h> 29 30#include <assert.h> 31#include <grp.h> 32#include <pthread.h> 33#include <pwd.h> 34#include <stdlib.h> 35#include <string.h> 36 37#include "idcache.h" 38#include "misc.h" 39 40/* 41 * Constants and data structures used to implement the thread-safe 42 * group and password file caches. Cache sizes must be prime. 43 */ 44#define UIDTONAME_SZ 317 /* Size of uid -> user name cache */ 45#define NAMETOUID_SZ 317 /* Size of user name -> uid cache */ 46#define GIDTONAME_SZ 317 /* Size of gid -> group name cache */ 47#define NAMETOGID_SZ 317 /* Size of group name -> gid cache */ 48 49/* Node structures used to cache lookups. */ 50struct uidc { 51 char *name; /* user name */ 52 uid_t uid; /* cached uid */ 53 int valid; /* is this a valid or a miss entry */ 54 struct uidc *next; /* for collisions */ 55}; 56 57struct gidc { 58 char *name; /* group name */ 59 gid_t gid; /* cached gid */ 60 int valid; /* is this a valid or a miss entry */ 61 struct gidc *next; /* for collisions */ 62}; 63 64static struct uidc **uidtoname; /* uid to user name cache */ 65static struct gidc **gidtoname; /* gid to group name cache */ 66static struct uidc **nametouid; /* user name to uid cache */ 67static struct gidc **nametogid; /* group name to gid cache */ 68 69static pthread_mutex_t uid_mtx; 70static pthread_mutex_t gid_mtx; 71 72static void uid_lock(void); 73static void uid_unlock(void); 74static void gid_lock(void); 75static void gid_unlock(void); 76 77static uint32_t hash(const char *); 78 79/* A 32-bit version of Peter Weinberger's (PJW) hash algorithm, 80 as used by ELF for hashing function names. */ 81static uint32_t 82hash(const char *name) 83{ 84 uint32_t g, h; 85 86 h = 0; 87 while(*name != '\0') { 88 h = (h << 4) + *name++; 89 if ((g = h & 0xF0000000)) { 90 h ^= g >> 24; 91 h &= 0x0FFFFFFF; 92 } 93 } 94 return (h); 95} 96 97static void 98uid_lock(void) 99{ 100 int error; 101 102 error = pthread_mutex_lock(&uid_mtx); 103 assert(!error); 104} 105 106static void 107uid_unlock(void) 108{ 109 int error; 110 111 error = pthread_mutex_unlock(&uid_mtx); 112 assert(!error); 113} 114 115static void 116gid_lock(void) 117{ 118 int error; 119 120 error = pthread_mutex_lock(&gid_mtx); 121 assert(!error); 122} 123 124static void 125gid_unlock(void) 126{ 127 int error; 128 129 error = pthread_mutex_unlock(&gid_mtx); 130 assert(!error); 131} 132 133static void 134uidc_insert(struct uidc **tbl, struct uidc *uidc, uint32_t key) 135{ 136 137 uidc->next = tbl[key]; 138 tbl[key] = uidc; 139} 140 141static void 142gidc_insert(struct gidc **tbl, struct gidc *gidc, uint32_t key) 143{ 144 145 gidc->next = tbl[key]; 146 tbl[key] = gidc; 147} 148 149/* Return the user name for this uid, or NULL if it's not found. */ 150char * 151getuserbyid(uid_t uid) 152{ 153 struct passwd *pw; 154 struct uidc *uidc, *uidc2; 155 uint32_t key, key2; 156 157 key = uid % UIDTONAME_SZ; 158 uid_lock(); 159 uidc = uidtoname[key]; 160 while (uidc != NULL) { 161 if (uidc->uid == uid) 162 break; 163 uidc = uidc->next; 164 } 165 166 if (uidc == NULL) { 167 /* We didn't find this uid, look it up and add it. */ 168 uidc = xmalloc(sizeof(struct uidc)); 169 uidc->uid = uid; 170 pw = getpwuid(uid); 171 if (pw != NULL) { 172 /* This uid is in the password file. */ 173 uidc->name = xstrdup(pw->pw_name); 174 uidc->valid = 1; 175 /* Also add it to the name -> gid table. */ 176 uidc2 = xmalloc(sizeof(struct uidc)); 177 uidc2->uid = uid; 178 uidc2->name = uidc->name; /* We reuse the pointer. */ 179 uidc2->valid = 1; 180 key2 = hash(uidc->name) % NAMETOUID_SZ; 181 uidc_insert(nametouid, uidc2, key2); 182 } else { 183 /* Add a miss entry for this uid. */ 184 uidc->name = NULL; 185 uidc->valid = 0; 186 } 187 uidc_insert(uidtoname, uidc, key); 188 } 189 /* It is safe to unlock here since the cache structure 190 is not going to get freed or changed. */ 191 uid_unlock(); 192 return (uidc->name); 193} 194 195/* Return the group name for this gid, or NULL if it's not found. */ 196char * 197getgroupbyid(gid_t gid) 198{ 199 struct group *gr; 200 struct gidc *gidc, *gidc2; 201 uint32_t key, key2; 202 203 key = gid % GIDTONAME_SZ; 204 gid_lock(); 205 gidc = gidtoname[key]; 206 while (gidc != NULL) { 207 if (gidc->gid == gid) 208 break; 209 gidc = gidc->next; 210 } 211 212 if (gidc == NULL) { 213 /* We didn't find this gid, look it up and add it. */ 214 gidc = xmalloc(sizeof(struct gidc)); 215 gidc->gid = gid; 216 gr = getgrgid(gid); 217 if (gr != NULL) { 218 /* This gid is in the group file. */ 219 gidc->name = xstrdup(gr->gr_name); 220 gidc->valid = 1; 221 /* Also add it to the name -> gid table. */ 222 gidc2 = xmalloc(sizeof(struct gidc)); 223 gidc2->gid = gid; 224 gidc2->name = gidc->name; /* We reuse the pointer. */ 225 gidc2->valid = 1; 226 key2 = hash(gidc->name) % NAMETOGID_SZ; 227 gidc_insert(nametogid, gidc2, key2); 228 } else { 229 /* Add a miss entry for this gid. */ 230 gidc->name = NULL; 231 gidc->valid = 0; 232 } 233 gidc_insert(gidtoname, gidc, key); 234 } 235 /* It is safe to unlock here since the cache structure 236 is not going to get freed or changed. */ 237 gid_unlock(); 238 return (gidc->name); 239} 240 241/* Finds the uid for this user name. If it's found, the gid is stored 242 in *uid and 0 is returned. Otherwise, -1 is returned. */ 243int 244getuidbyname(const char *name, uid_t *uid) 245{ 246 struct passwd *pw; 247 struct uidc *uidc, *uidc2; 248 uint32_t key, key2; 249 250 uid_lock(); 251 key = hash(name) % NAMETOUID_SZ; 252 uidc = nametouid[key]; 253 while (uidc != NULL) { 254 if (strcmp(uidc->name, name) == 0) 255 break; 256 uidc = uidc->next; 257 } 258 259 if (uidc == NULL) { 260 uidc = xmalloc(sizeof(struct uidc)); 261 uidc->name = xstrdup(name); 262 pw = getpwnam(name); 263 if (pw != NULL) { 264 /* This user name is in the password file. */ 265 uidc->valid = 1; 266 uidc->uid = pw->pw_uid; 267 /* Also add it to the uid -> name table. */ 268 uidc2 = xmalloc(sizeof(struct uidc)); 269 uidc2->name = uidc->name; /* We reuse the pointer. */ 270 uidc2->uid = uidc->uid; 271 uidc2->valid = 1; 272 key2 = uidc2->uid % UIDTONAME_SZ; 273 uidc_insert(uidtoname, uidc2, key2); 274 } else { 275 /* Add a miss entry for this user name. */ 276 uidc->valid = 0; 277 uidc->uid = (uid_t)-1; /* Should not be accessed. */ 278 } 279 uidc_insert(nametouid, uidc, key); 280 } 281 /* It is safe to unlock here since the cache structure 282 is not going to get freed or changed. */ 283 uid_unlock(); 284 if (!uidc->valid) 285 return (-1); 286 *uid = uidc->uid; 287 return (0); 288} 289 290/* Finds the gid for this group name. If it's found, the gid is stored 291 in *gid and 0 is returned. Otherwise, -1 is returned. */ 292int 293getgidbyname(const char *name, gid_t *gid) 294{ 295 struct group *gr; 296 struct gidc *gidc, *gidc2; 297 uint32_t key, key2; 298 299 gid_lock(); 300 key = hash(name) % NAMETOGID_SZ; 301 gidc = nametogid[key]; 302 while (gidc != NULL) { 303 if (strcmp(gidc->name, name) == 0) 304 break; 305 gidc = gidc->next; 306 } 307 308 if (gidc == NULL) { 309 gidc = xmalloc(sizeof(struct gidc)); 310 gidc->name = xstrdup(name); 311 gr = getgrnam(name); 312 if (gr != NULL) { 313 /* This group name is in the group file. */ 314 gidc->gid = gr->gr_gid; 315 gidc->valid = 1; 316 /* Also add it to the gid -> name table. */ 317 gidc2 = xmalloc(sizeof(struct gidc)); 318 gidc2->name = gidc->name; /* We reuse the pointer. */ 319 gidc2->gid = gidc->gid; 320 gidc2->valid = 1; 321 key2 = gidc2->gid % GIDTONAME_SZ; 322 gidc_insert(gidtoname, gidc2, key2); 323 } else { 324 /* Add a miss entry for this group name. */ 325 gidc->gid = (gid_t)-1; /* Should not be accessed. */ 326 gidc->valid = 0; 327 } 328 gidc_insert(nametogid, gidc, key); 329 } 330 /* It is safe to unlock here since the cache structure 331 is not going to get freed or changed. */ 332 gid_unlock(); 333 if (!gidc->valid) 334 return (-1); 335 *gid = gidc->gid; 336 return (0); 337} 338 339/* Initialize the cache structures. */ 340void 341idcache_init(void) 342{ 343 344 pthread_mutex_init(&uid_mtx, NULL); 345 pthread_mutex_init(&gid_mtx, NULL); 346 uidtoname = xmalloc(UIDTONAME_SZ * sizeof(struct uidc *)); 347 gidtoname = xmalloc(GIDTONAME_SZ * sizeof(struct gidc *)); 348 nametouid = xmalloc(NAMETOUID_SZ * sizeof(struct uidc *)); 349 nametogid = xmalloc(NAMETOGID_SZ * sizeof(struct gidc *)); 350 memset(uidtoname, 0, UIDTONAME_SZ * sizeof(struct uidc *)); 351 memset(gidtoname, 0, GIDTONAME_SZ * sizeof(struct gidc *)); 352 memset(nametouid, 0, NAMETOUID_SZ * sizeof(struct uidc *)); 353 memset(nametogid, 0, NAMETOGID_SZ * sizeof(struct gidc *)); 354} 355 356/* Cleanup the cache structures. */ 357void 358idcache_fini(void) 359{ 360 struct uidc *uidc, *uidc2; 361 struct gidc *gidc, *gidc2; 362 size_t i; 363 364 for (i = 0; i < UIDTONAME_SZ; i++) { 365 uidc = uidtoname[i]; 366 while (uidc != NULL) { 367 if (uidc->name != NULL) { 368 assert(uidc->valid); 369 free(uidc->name); 370 } 371 uidc2 = uidc->next; 372 free(uidc); 373 uidc = uidc2; 374 } 375 } 376 free(uidtoname); 377 for (i = 0; i < NAMETOUID_SZ; i++) { 378 uidc = nametouid[i]; 379 while (uidc != NULL) { 380 assert(uidc->name != NULL); 381 /* If it's a valid entry, it has been added to both the 382 uidtoname and nametouid tables, and the name pointer 383 has been reused for both entries. Thus, the name 384 pointer has already been freed in the loop above. */ 385 if (!uidc->valid) 386 free(uidc->name); 387 uidc2 = uidc->next; 388 free(uidc); 389 uidc = uidc2; 390 } 391 } 392 free(nametouid); 393 for (i = 0; i < GIDTONAME_SZ; i++) { 394 gidc = gidtoname[i]; 395 while (gidc != NULL) { 396 if (gidc->name != NULL) { 397 assert(gidc->valid); 398 free(gidc->name); 399 } 400 gidc2 = gidc->next; 401 free(gidc); 402 gidc = gidc2; 403 } 404 } 405 free(gidtoname); 406 for (i = 0; i < NAMETOGID_SZ; i++) { 407 gidc = nametogid[i]; 408 while (gidc != NULL) { 409 assert(gidc->name != NULL); 410 /* See above comment. */ 411 if (!gidc->valid) 412 free(gidc->name); 413 gidc2 = gidc->next; 414 free(gidc); 415 gidc = gidc2; 416 } 417 } 418 free(nametogid); 419 pthread_mutex_destroy(&uid_mtx); 420 pthread_mutex_destroy(&gid_mtx); 421} 422