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