1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License (the "License"). 6 * You may not use this file except in compliance with the License. 7 * 8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9 * or http://www.opensolaris.org/os/licensing. 10 * See the License for the specific language governing permissions 11 * and limitations under the License. 12 * 13 * When distributing Covered Code, include this CDDL HEADER in each 14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15 * If applicable, add the following below this CDDL HEADER, with the 16 * fields enclosed by brackets "[]" replaced with your own identifying 17 * information: Portions Copyright [yyyy] [name of copyright owner] 18 * 19 * CDDL HEADER END 20 */ 21 22 /* 23 * Copyright 2007 Sun Microsystems, Inc. All rights reserved. 24 * Use is subject to license terms. 25 */ 26 27 /* 28 * Windows to Solaris Identity Mapping kernel API 29 * This module provides the kernel cache. 30 */ 31 32 #pragma ident "%Z%%M% %I% %E% SMI" 33 34 35 #include <sys/types.h> 36 #include <sys/avl.h> 37 #include <sys/systm.h> 38 #include <sys/sysmacros.h> 39 #include <sys/ksynch.h> 40 #include <sys/cmn_err.h> 41 #include <sys/kidmap.h> 42 #include "idmap_prot.h" 43 #include "kidmap_priv.h" 44 45 46 /* 47 * External functions 48 */ 49 extern uintptr_t space_fetch(char *key); 50 extern int space_store(char *key, uintptr_t ptr); 51 52 53 /* 54 * Internal definitions and functions 55 */ 56 57 #define CACHE_TRIGGER_SIZE 8192 58 #define CACHE_PURGE_INTERVAL (60 * 3) 59 60 typedef struct sid_prefix_node { 61 avl_node_t avl_link; 62 const char *sid_prefix; 63 } sid_prefix_node_t; 64 65 66 typedef struct entry { 67 avl_node_t avl_link; 68 const char *sid_prefix; 69 uint32_t rid; 70 uid_t pid; 71 int is_user; 72 time_t ttl; 73 } entry_t; 74 75 typedef int (*avl_comp_fn)(const void*, const void*); 76 77 78 struct sid_prefix_store { 79 struct avl_tree tree; 80 krwlock_t lock; 81 }; 82 83 struct sid_prefix_store *kidmap_sid_prefix_store = NULL; 84 85 86 87 static void 88 kidmap_cache_purge_avl(idmap_avl_cache_t *cache); 89 90 /* 91 * kidmap_strdup() copied from uts/common/fs/sockfs/nl7c.c 92 */ 93 static char * 94 kidmap_strdup(const char *s) 95 { 96 int len = strlen(s) + 1; 97 char *ret = kmem_alloc(len, KM_SLEEP); 98 99 bcopy(s, ret, len); 100 return (ret); 101 } 102 103 104 static int 105 kidmap_compare_sid(const entry_t *entry1, const entry_t *entry2) 106 { 107 int comp = entry2->rid - entry1->rid; 108 109 if (comp == 0) 110 comp = strcmp(entry2->sid_prefix, entry1->sid_prefix); 111 112 if (comp < 0) 113 comp = -1; 114 else if (comp > 0) 115 comp = 1; 116 117 return (comp); 118 } 119 120 121 static int 122 kidmap_compare_pid(const entry_t *entry1, const entry_t *entry2) 123 { 124 int comp = entry2->pid - entry1->pid; 125 126 if (comp == 0) 127 comp = entry2->is_user - entry1->is_user; 128 129 if (comp < 0) 130 comp = -1; 131 else if (comp > 0) 132 comp = 1; 133 134 return (comp); 135 } 136 137 138 static int 139 kidmap_compare_sid_prefix(const sid_prefix_node_t *entry1, 140 const sid_prefix_node_t *entry2) 141 { 142 int comp; 143 144 comp = strcmp(entry2->sid_prefix, entry1->sid_prefix); 145 146 if (comp < 0) 147 comp = -1; 148 else if (comp > 0) 149 comp = 1; 150 151 return (comp); 152 } 153 154 155 void 156 kidmap_cache_create(idmap_cache_t *cache) 157 { 158 typedef int (*comp)(const void*, const void*); 159 160 rw_init(&cache->sid.lock, NULL, RW_DRIVER, NULL); 161 avl_create(&cache->sid.tree, (avl_comp_fn)kidmap_compare_sid, 162 sizeof (entry_t), offsetof(entry_t, avl_link)); 163 mutex_init(&cache->sid.mutex, NULL, MUTEX_DEFAULT, NULL); 164 cache->sid.state = CACHE_CREATED; 165 cache->sid.purge_time = 0; 166 167 rw_init(&cache->pid.lock, NULL, RW_DRIVER, NULL); 168 avl_create(&cache->pid.tree, (avl_comp_fn)kidmap_compare_pid, 169 sizeof (entry_t), offsetof(entry_t, avl_link)); 170 mutex_init(&cache->pid.mutex, NULL, MUTEX_DEFAULT, NULL); 171 cache->pid.state = CACHE_CREATED; 172 cache->pid.purge_time = 0; 173 } 174 175 176 void 177 kidmap_cache_delete(idmap_cache_t *cache) 178 { 179 entry_t *entry; 180 void *cookie; 181 182 cookie = NULL; 183 while ((entry = avl_destroy_nodes(&cache->pid.tree, &cookie)) 184 != NULL) { 185 kmem_free(entry, sizeof (entry_t)); 186 } 187 avl_destroy(&cache->pid.tree); 188 rw_destroy(&cache->pid.lock); 189 mutex_destroy(&cache->pid.mutex); 190 191 cookie = NULL; 192 while ((entry = avl_destroy_nodes(&cache->sid.tree, &cookie)) 193 != NULL) { 194 kmem_free(entry, sizeof (entry_t)); 195 } 196 avl_destroy(&cache->sid.tree); 197 rw_destroy(&cache->sid.lock); 198 mutex_destroy(&cache->sid.mutex); 199 } 200 201 202 int 203 kidmap_cache_lookupbypid(idmap_cache_t *cache, const char **sid_prefix, 204 uint32_t *rid, uid_t pid, int is_user) 205 206 { 207 entry_t entry; 208 entry_t *result; 209 avl_index_t where; 210 int status; 211 time_t now = gethrestime_sec(); 212 213 entry.pid = pid; 214 entry.is_user = is_user; 215 216 rw_enter(&cache->pid.lock, RW_READER); 217 218 result = avl_find(&cache->pid.tree, &entry, &where); 219 220 if (result && result->ttl > now) { 221 *sid_prefix = result->sid_prefix; 222 *rid = result->rid; 223 status = IDMAP_SUCCESS; 224 } else 225 status = IDMAP_ERR_NOMAPPING; 226 227 rw_exit(&cache->pid.lock); 228 229 return (status); 230 } 231 232 233 int 234 kidmap_cache_lookupbysid(idmap_cache_t *cache, const char *sid_prefix, 235 uint32_t rid, uid_t *pid, int *is_user) 236 { 237 entry_t entry; 238 entry_t *result; 239 avl_index_t where; 240 int status; 241 time_t now = gethrestime_sec(); 242 243 entry.sid_prefix = sid_prefix; 244 entry.rid = rid; 245 246 rw_enter(&cache->sid.lock, RW_READER); 247 248 result = avl_find(&cache->sid.tree, &entry, &where); 249 250 if (result && result->ttl > now) { 251 *pid = result->pid; 252 *is_user = result->is_user; 253 status = IDMAP_SUCCESS; 254 } else 255 status = IDMAP_ERR_NOMAPPING; 256 257 rw_exit(&cache->sid.lock); 258 259 return (status); 260 } 261 262 263 void 264 kidmap_cache_addbypid(idmap_cache_t *cache, const char *sid_prefix, 265 uint32_t rid, uid_t pid, int is_user, time_t ttl) 266 { 267 entry_t find; 268 entry_t *result; 269 entry_t *new; 270 avl_index_t where; 271 int purge_required = FALSE; 272 273 find.pid = pid; 274 find.is_user = is_user; 275 276 rw_enter(&cache->pid.lock, RW_WRITER); 277 result = avl_find(&cache->pid.tree, &find, &where); 278 279 if (result) { 280 result->sid_prefix = sid_prefix; 281 result->rid = rid; 282 result->ttl = ttl; 283 } else { 284 new = kmem_alloc(sizeof (entry_t), KM_SLEEP); 285 new->pid = pid; 286 new->is_user = is_user; 287 new->sid_prefix = sid_prefix; 288 new->rid = rid; 289 new->ttl = ttl; 290 291 avl_insert(&cache->pid.tree, new, where); 292 if ((avl_numnodes(&cache->pid.tree) > CACHE_TRIGGER_SIZE) && 293 (cache->pid.purge_time + CACHE_PURGE_INTERVAL < 294 gethrestime_sec())) 295 purge_required = TRUE; 296 } 297 298 rw_exit(&cache->pid.lock); 299 300 if (purge_required) 301 kidmap_cache_purge_avl(&cache->pid); 302 } 303 304 305 void 306 kidmap_cache_addbysid(idmap_cache_t *cache, const char *sid_prefix, 307 uint32_t rid, uid_t pid, int is_user, time_t ttl) 308 309 { 310 entry_t find; 311 entry_t *result; 312 entry_t *new; 313 avl_index_t where; 314 int purge_required = FALSE; 315 316 find.sid_prefix = sid_prefix; 317 find.rid = rid; 318 319 rw_enter(&cache->sid.lock, RW_WRITER); 320 result = avl_find(&cache->sid.tree, &find, &where); 321 322 if (result) { 323 result->pid = pid; 324 result->is_user = is_user; 325 result->ttl = ttl; 326 } else { 327 new = kmem_alloc(sizeof (entry_t), KM_SLEEP); 328 new->pid = pid; 329 new->is_user = is_user; 330 new->sid_prefix = sid_prefix; 331 new->rid = rid; 332 new->ttl = ttl; 333 334 avl_insert(&cache->sid.tree, new, where); 335 336 if ((avl_numnodes(&cache->sid.tree) > CACHE_TRIGGER_SIZE) && 337 (cache->sid.purge_time + CACHE_PURGE_INTERVAL < 338 gethrestime_sec())) 339 purge_required = TRUE; 340 } 341 342 rw_exit(&cache->sid.lock); 343 344 if (purge_required) 345 kidmap_cache_purge_avl(&cache->sid); 346 } 347 348 349 static void 350 kidmap_cache_purge_avl(idmap_avl_cache_t *cache) 351 { 352 time_t now = gethrestime_sec(); 353 entry_t *curr; 354 entry_t *prev = NULL; 355 356 mutex_enter(&cache->mutex); 357 if (cache->state != CACHE_CREATED) { 358 mutex_exit(&cache->mutex); 359 return; 360 } 361 cache->state = CACHE_PURGING; 362 mutex_exit(&cache->mutex); 363 364 rw_enter(&cache->lock, RW_READER); 365 curr = avl_first(&cache->tree); 366 while (curr != NULL) { 367 if (curr->ttl < now) { 368 /* Old entry to remove - we need a write lock */ 369 if (rw_tryupgrade(&cache->lock) == 0) { 370 /* 371 * Could not upgrade lock so release lock 372 * and aquire the write lock. It is valid to 373 * release abd re-aquire the lock as there 374 * can only be one purge routine running on an 375 * avl tree and no other routine removes 376 * entries. 377 */ 378 rw_exit(&cache->lock); 379 rw_enter(&cache->lock, RW_WRITER); 380 } 381 /* Old entry to remove */ 382 avl_remove(&cache->tree, curr); 383 rw_downgrade(&cache->lock); 384 385 curr = prev; 386 if (curr == NULL) { 387 /* We removed the first entery */ 388 curr = avl_first(&cache->tree); 389 continue; 390 } 391 } 392 prev = curr; 393 curr = AVL_NEXT(&cache->tree, curr); 394 } 395 rw_exit(&cache->lock); 396 397 mutex_enter(&cache->mutex); 398 cache->state = CACHE_CREATED; 399 cache->purge_time = now; 400 mutex_exit(&cache->mutex); 401 } 402 403 void 404 kidmap_sid_prefix_store_init(void) 405 { 406 kidmap_sid_prefix_store = (struct sid_prefix_store *) 407 space_fetch("SUNW,idmap_sid_prefix"); 408 if (kidmap_sid_prefix_store == NULL) { 409 kidmap_sid_prefix_store = kmem_alloc( 410 sizeof (struct sid_prefix_store), KM_SLEEP); 411 rw_init(&kidmap_sid_prefix_store->lock, NULL, RW_DRIVER, NULL); 412 avl_create(&kidmap_sid_prefix_store->tree, 413 (avl_comp_fn)kidmap_compare_sid_prefix, 414 sizeof (sid_prefix_node_t), 415 offsetof(sid_prefix_node_t, avl_link)); 416 (void) space_store("SUNW,idmap_sid_prefix", 417 (uintptr_t)kidmap_sid_prefix_store); 418 } else { 419 /* 420 * The AVL comparison function must be re-initialised on 421 * re-load because may not be loaded into the same 422 * address space. 423 */ 424 kidmap_sid_prefix_store->tree.avl_compar = 425 (avl_comp_fn)kidmap_compare_sid_prefix; 426 } 427 } 428 429 430 const char * 431 kidmap_find_sid_prefix(const char *sid_prefix) { 432 sid_prefix_node_t find; 433 sid_prefix_node_t *result; 434 sid_prefix_node_t *new; 435 avl_index_t where; 436 437 if (sid_prefix == NULL || *sid_prefix == '\0') 438 return (NULL); 439 440 find.sid_prefix = sid_prefix; 441 442 443 rw_enter(&kidmap_sid_prefix_store->lock, RW_READER); 444 445 result = avl_find(&kidmap_sid_prefix_store->tree, &find, &where); 446 447 if (result) { 448 rw_exit(&kidmap_sid_prefix_store->lock); 449 return (result->sid_prefix); 450 } 451 452 if (rw_tryupgrade(&kidmap_sid_prefix_store->lock) == 0) { 453 /* 454 * Could not upgrade lock so release lock 455 * and aquire the write lock 456 */ 457 rw_exit(&kidmap_sid_prefix_store->lock); 458 rw_enter(&kidmap_sid_prefix_store->lock, RW_WRITER); 459 460 result = avl_find(&kidmap_sid_prefix_store->tree, 461 &find, &where); 462 if (result) { 463 rw_exit(&kidmap_sid_prefix_store->lock); 464 return (result->sid_prefix); 465 } 466 } 467 468 new = kmem_alloc(sizeof (sid_prefix_node_t), KM_SLEEP); 469 new->sid_prefix = kidmap_strdup(sid_prefix); 470 avl_insert(&kidmap_sid_prefix_store->tree, new, where); 471 rw_exit(&kidmap_sid_prefix_store->lock); 472 473 return (new->sid_prefix); 474 } 475