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, Version 1.0 only 6 * (the "License"). You may not use this file except in compliance 7 * with the License. 8 * 9 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 10 * or http://www.opensolaris.org/os/licensing. 11 * See the License for the specific language governing permissions 12 * and limitations under the License. 13 * 14 * When distributing Covered Code, include this CDDL HEADER in each 15 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 16 * If applicable, add the following below this CDDL HEADER, with the 17 * fields enclosed by brackets "[]" replaced with your own identifying 18 * information: Portions Copyright [yyyy] [name of copyright owner] 19 * 20 * CDDL HEADER END 21 */ 22 /* 23 * Copyright 2005 Sun Microsystems, Inc. All rights reserved. 24 * Use is subject to license terms. 25 */ 26 27 #pragma ident "%Z%%M% %I% %E% SMI" 28 29 /* 30 * Functions for dealing with provider sessions 31 */ 32 33 #include <string.h> 34 #include <cryptoutil.h> 35 #include "metaGlobal.h" 36 #include "pkcs11Session.h" 37 #include "pkcs11Global.h" 38 39 40 /* 41 * This is just a **WILD** guess for the maximum idle sessions to 42 * keep for each slot. This number should probably be adjusted 43 * when there's more data from actual application use 44 */ 45 #define MAX_IDLE_SESSIONS 100 46 47 /* 48 * The following 5 variables are initialized at the time metaslot 49 * is initialized. They are not modified after they are initialized 50 * 51 * During initialization time, they are protected by the "initmutex" 52 * defined in metaGeneral.c 53 */ 54 slot_data_t *slots; 55 CK_SLOT_ID metaslot_keystore_slotid; 56 static CK_ULONG num_slots; 57 static CK_ULONG objtok_slotnum; 58 static boolean_t write_protected; 59 60 /* protects the "metaslotLoggedIn" variable */ 61 static pthread_mutex_t metaslotLoggedIn_mutex = PTHREAD_MUTEX_INITIALIZER; 62 static boolean_t metaslotLoggedIn; 63 64 /* 65 * meta_slotManager_initialize 66 * 67 * Called from C_Initialize. Allocates and initializes the storage needed 68 * by the slot manager. 69 */ 70 CK_RV 71 meta_slotManager_initialize() { 72 CK_ULONG slot_count = 0; 73 CK_RV rv; 74 CK_SLOT_ID i; 75 76 /* Initialize the static variables */ 77 write_protected = B_FALSE; 78 metaslotLoggedIn = B_FALSE; 79 80 /* 81 * Count the number of slots in the framework. 82 * We start at ((slottable->st_first) + 1) instead of 83 * slottable->st_first because when we are here, metaslot is 84 * enabled, and st_first is always metaslot, which doesn't 85 * need to be counted. 86 */ 87 for (i = (slottable->st_first) + 1; i <= slottable->st_last; i++) { 88 slot_count++; 89 } 90 91 /* 92 * This shouldn't happen, because there should at least 93 * be 1 other slot besides metaslot. 94 */ 95 if (slot_count < 1) { 96 rv = CKR_FUNCTION_FAILED; 97 goto clean_exit; 98 } 99 100 slots = calloc(slot_count, sizeof (slot_data_t)); 101 if (slots == NULL) { 102 rv = CKR_HOST_MEMORY; 103 goto clean_exit; 104 } 105 106 /* 107 * Store the slot IDs. Adjust for the fact that the first slot is 108 * actually us (metaslot). 109 */ 110 for (i = 0; i < slot_count; i++) { 111 slots[i].fw_st_id = i + 1; 112 (void) pthread_rwlock_init( 113 &(slots[i].tokenobject_list_lock), NULL); 114 } 115 num_slots = slot_count; 116 117 return (CKR_OK); 118 119 clean_exit: 120 if (slots) { 121 free(slots); 122 slots = NULL; 123 } 124 125 num_slots = 0; 126 127 return (rv); 128 } 129 130 131 /* 132 * meta_slotManager_finalize 133 * 134 * Called from C_Finalize. Deallocates any storage held by the slot manager. 135 */ 136 void 137 meta_slotManager_finalize() { 138 CK_ULONG slot; 139 140 /* 141 * No need to lock pool, we assume all meta sessions are closed. 142 * 143 * Close all sessions in the idle and persist list. 144 * The active list is empty. It doesn't need to be checked. 145 */ 146 147 for (slot = 0; slot < num_slots; slot++) { 148 slot_session_t *session, *next_session; 149 150 /* 151 * The slotobjects associated with the session should have 152 * been closed when the metaobjects were closed. Thus, no 153 * need to do anything here. 154 */ 155 156 session = slots[slot].session_pool.idle_list_head; 157 while (session) { 158 next_session = session->next; 159 (void) FUNCLIST(session->fw_st_id)->C_CloseSession( 160 session->hSession); 161 (void) pthread_rwlock_destroy( 162 &session->object_list_lock); 163 free(session); 164 session = next_session; 165 } 166 167 session = slots[slot].session_pool.persist_list_head; 168 while (session) { 169 next_session = session->next; 170 (void) FUNCLIST(session->fw_st_id)->C_CloseSession( 171 session->hSession); 172 (void) pthread_rwlock_destroy( 173 &session->object_list_lock); 174 free(session); 175 session = next_session; 176 } 177 178 (void) pthread_rwlock_destroy( 179 &slots[slot].tokenobject_list_lock); 180 } 181 182 free(slots); 183 slots = NULL; 184 } 185 186 187 /* 188 * meta_slotManager_find_object_token() 189 * 190 * Called from meta_Initialize. Searches for the "object token," which is used 191 * for storing token objects and loging into. 192 * 193 * We do the search using the following algorithm. 194 * 195 * If either ${METASLOT_OBJECTSTORE_SLOT} or ${METASLOT_OBJECTSTORE_TOKEN} 196 * environment variable is defined, the value of the defined variable(s) 197 * will be used for the match. All token and slot values defined system-wide 198 * will be ignored. 199 * 200 * If neither variables above are defined, the system-wide values defined 201 * in pkcs11.conf are used. 202 * 203 * If neither environment variables or system-wide values are defined, 204 * or if none of the existing slots/tokens match the defined 205 * values, the first slot after metaslot will be used as the default. 206 * 207 */ 208 void 209 meta_slotManager_find_object_token() { 210 CK_ULONG slot; 211 boolean_t found = B_FALSE; 212 CK_RV rv; 213 unsigned int num_match_needed = 0; 214 CK_SLOT_INFO slotinfo; 215 CK_TOKEN_INFO tokeninfo; 216 217 if (metaslot_config.keystore_token_specified) { 218 num_match_needed++; 219 } 220 221 if (metaslot_config.keystore_slot_specified) { 222 num_match_needed++; 223 } 224 225 if (num_match_needed == 0) { 226 goto skip_search; 227 } 228 229 for (slot = 0; slot < num_slots; slot++) { 230 unsigned int num_matched = 0; 231 boolean_t have_tokeninfo = B_FALSE; 232 CK_SLOT_ID true_id, fw_st_id; 233 234 fw_st_id = slots[slot].fw_st_id; 235 true_id = TRUEID(fw_st_id); 236 237 if (metaslot_config.keystore_slot_specified) { 238 239 unsigned char *slot; 240 size_t slot_str_len; 241 242 rv = FUNCLIST(fw_st_id)->C_GetSlotInfo(true_id, 243 &slotinfo); 244 if (rv != CKR_OK) 245 continue; 246 247 /* 248 * pad slot description from user/system configuration 249 * with spaces 250 */ 251 slot = metaslot_config.keystore_slot; 252 slot_str_len = strlen((char *)slot); 253 (void) memset(slot + slot_str_len, ' ', 254 SLOT_DESCRIPTION_SIZE - slot_str_len); 255 256 /* 257 * The PKCS#11 strings are not null-terminated, so, 258 * we just compare SLOT_DESCRIPTION_SIZE bytes 259 */ 260 if (strncmp((char *)slot, 261 (char *)slotinfo.slotDescription, 262 SLOT_DESCRIPTION_SIZE) == 0) { 263 num_matched++; 264 } 265 } 266 267 if (metaslot_config.keystore_token_specified) { 268 unsigned char *token; 269 size_t token_str_len; 270 271 rv = FUNCLIST(fw_st_id)->C_GetTokenInfo(true_id, 272 &tokeninfo); 273 274 if (rv != CKR_OK) { 275 continue; 276 } 277 278 have_tokeninfo = B_TRUE; 279 280 /* 281 * pad slot description from user/system configuration 282 * with spaces 283 */ 284 token = metaslot_config.keystore_token; 285 token_str_len = strlen((char *)token); 286 (void) memset(token + token_str_len, ' ', 287 TOKEN_LABEL_SIZE - token_str_len); 288 289 /* 290 * The PKCS#11 strings are not null-terminated. 291 * So, just compare TOKEN_LABEL_SIZE bytes 292 */ 293 if (strncmp((char *)token, (char *)tokeninfo.label, 294 TOKEN_LABEL_SIZE) == 0) { 295 num_matched++; 296 } 297 } 298 299 if (num_match_needed == num_matched) { 300 /* match is found */ 301 302 if (!have_tokeninfo) { 303 rv = FUNCLIST(fw_st_id)->C_GetTokenInfo(true_id, 304 &tokeninfo); 305 if (rv != CKR_OK) { 306 continue; 307 } 308 } 309 310 311 if (tokeninfo.flags & CKF_WRITE_PROTECTED) { 312 /* 313 * Currently this is the only time that 314 * the write_protected state is set, and 315 * it is never cleared. The token could 316 * clear (or set!) this flag later on. 317 * We might want to adjust the state 318 * of metaslot, but there's know way to know 319 * when a token changes this flag. 320 */ 321 write_protected = B_TRUE; 322 } 323 324 found = B_TRUE; 325 break; 326 } 327 } 328 329 skip_search: 330 if (found) { 331 objtok_slotnum = slot; 332 } else { 333 /* 334 * if slot and/or token is not defined for the keystore, 335 * just use the first available slot as keystore 336 */ 337 objtok_slotnum = 0; 338 } 339 slots[objtok_slotnum].session_pool.keep_one_alive = B_TRUE; 340 metaslot_keystore_slotid = slots[objtok_slotnum].fw_st_id; 341 } 342 343 344 CK_ULONG 345 get_keystore_slotnum() 346 { 347 return (objtok_slotnum); 348 } 349 350 351 CK_SLOT_ID 352 meta_slotManager_get_framework_table_id(CK_ULONG slotnum) 353 { 354 /* 355 * This is only used internally, and so the slotnum should always 356 * be valid. 357 */ 358 return (slots[slotnum].fw_st_id); 359 } 360 361 CK_ULONG 362 meta_slotManager_get_slotcount() 363 { 364 return (num_slots); 365 } 366 367 boolean_t 368 meta_slotManager_token_write_protected() 369 { 370 return (write_protected); 371 } 372 373 /* 374 * Find a session in the given list that matches the specified flags. 375 * If such a session is found, it will be removed from the list, and 376 * returned to the caller. If such a session is not found, will 377 * return NULL 378 */ 379 static slot_session_t * 380 get_session(slot_session_t **session_list, CK_FLAGS flags) 381 { 382 383 slot_session_t *tmp_session; 384 385 tmp_session = *session_list; 386 387 while (tmp_session != NULL) { 388 if (tmp_session->session_flags == flags) { 389 break; 390 } else { 391 tmp_session = tmp_session->next; 392 } 393 394 } 395 396 if (tmp_session == NULL) { 397 /* no match */ 398 return (NULL); 399 } 400 401 /* Remove from list */ 402 REMOVE_FROM_LIST(*session_list, tmp_session); 403 return (tmp_session); 404 } 405 406 /* 407 * meta_get_slot_session 408 * 409 * Call to get a session with a specific slot/token. 410 * 411 * NOTE - We assume the slot allows an unlimited number of sessions. We 412 * could look at what's reported in the token info, but that information is 413 * not always set. It's also unclear when we should (A) wait for one to become 414 * available, (B) skip the slot for now or (C) return a fatal error. The 415 * extra complexity is not worth it. 416 * 417 */ 418 CK_RV 419 meta_get_slot_session(CK_ULONG slotnum, slot_session_t **session, 420 CK_FLAGS flags) { 421 session_pool_t *pool; 422 slot_session_t *new_session, *tmp_session; 423 CK_RV rv; 424 CK_SLOT_ID fw_st_id, true_id; 425 426 if (slotnum >= num_slots) { 427 return (CKR_SLOT_ID_INVALID); 428 } 429 430 pool = &slots[slotnum].session_pool; 431 432 /* 433 * Try to reuse an existing session. 434 */ 435 436 (void) pthread_mutex_lock(&pool->list_lock); 437 438 if (pool->idle_list_head != NULL) { 439 tmp_session = get_session(&(pool->idle_list_head), flags); 440 if (tmp_session != NULL) { 441 /* Add to active list */ 442 INSERT_INTO_LIST(pool->active_list_head, tmp_session); 443 *session = tmp_session; 444 pool->num_idle_sessions--; 445 (void) pthread_mutex_unlock(&pool->list_lock); 446 return (CKR_OK); 447 } 448 } 449 450 if (pool->persist_list_head != NULL) { 451 tmp_session = get_session(&(pool->persist_list_head), flags); 452 if (tmp_session != NULL) { 453 /* Add to active list */ 454 INSERT_INTO_LIST(pool->active_list_head, tmp_session); 455 *session = tmp_session; 456 (void) pthread_mutex_unlock(&pool->list_lock); 457 return (CKR_OK); 458 } 459 } 460 (void) pthread_mutex_unlock(&pool->list_lock); 461 462 fw_st_id = slots[slotnum].fw_st_id; 463 true_id = TRUEID(fw_st_id); 464 465 new_session = calloc(1, sizeof (slot_session_t)); 466 if (new_session == NULL) { 467 return (CKR_HOST_MEMORY); 468 } 469 470 /* initialize slotsession */ 471 new_session->slotnum = slotnum; 472 new_session->fw_st_id = fw_st_id; 473 new_session->object_list_head = NULL; 474 new_session->session_flags = flags; 475 (void) pthread_rwlock_init(&new_session->object_list_lock, NULL); 476 477 rv = FUNCLIST(fw_st_id)->C_OpenSession(true_id, flags, NULL, NULL, 478 &new_session->hSession); 479 480 if (rv == CKR_TOKEN_WRITE_PROTECTED) { 481 /* Retry with a RO session. */ 482 new_session->session_flags &= ~CKF_SERIAL_SESSION; 483 rv = FUNCLIST(fw_st_id)->C_OpenSession(true_id, 484 new_session->session_flags, NULL, NULL, 485 &new_session->hSession); 486 } 487 488 if (rv != CKR_OK) { 489 free(new_session); 490 return (CKR_FUNCTION_FAILED); 491 } 492 493 /* Insert session into active list */ 494 (void) pthread_mutex_lock(&pool->list_lock); 495 INSERT_INTO_LIST(pool->active_list_head, new_session); 496 (void) pthread_mutex_unlock(&pool->list_lock); 497 *session = new_session; 498 return (CKR_OK); 499 } 500 501 502 /* 503 * meta_release_slot_session 504 * 505 * Call to release a session obtained via meta_get_slot_session() 506 */ 507 void 508 meta_release_slot_session(slot_session_t *session) { 509 session_pool_t *pool; 510 boolean_t must_retain, can_close = B_FALSE; 511 boolean_t this_is_last_session = B_FALSE; 512 513 pool = &slots[session->slotnum].session_pool; 514 515 /* Note that the active_list must have >= 1 entry (this session) */ 516 if (pool->persist_list_head == NULL && 517 pool->idle_list_head == NULL && 518 pool->active_list_head->next == NULL) 519 this_is_last_session = B_TRUE; 520 521 /* 522 * If the session has session objects, we need to retain it. Also 523 * retain it if it's the only session holding login state (or handles 524 * to public token objects) 525 */ 526 must_retain = session->object_list_head != NULL || 527 (pool->keep_one_alive && this_is_last_session); 528 529 if ((!must_retain) && (pool->num_idle_sessions > MAX_IDLE_SESSIONS)) { 530 can_close = B_TRUE; 531 } 532 533 (void) pthread_mutex_lock(&pool->list_lock); 534 /* remove from active list */ 535 REMOVE_FROM_LIST(pool->active_list_head, session); 536 537 if (must_retain) { 538 /* insert into persist list */ 539 INSERT_INTO_LIST(pool->persist_list_head, session); 540 (void) pthread_mutex_unlock(&pool->list_lock); 541 return; 542 } else if (!can_close) { 543 /* insert into idle list */ 544 INSERT_INTO_LIST(pool->idle_list_head, session); 545 pool->num_idle_sessions++; 546 (void) pthread_mutex_unlock(&pool->list_lock); 547 return; 548 } 549 550 (void) pthread_mutex_unlock(&pool->list_lock); 551 552 (void) FUNCLIST(session->fw_st_id)->C_CloseSession(session->hSession); 553 554 (void) pthread_rwlock_destroy(&session->object_list_lock); 555 free(session); 556 } 557 558 /* 559 * Returns whether metaslot has directly logged in 560 */ 561 boolean_t 562 metaslot_logged_in() 563 { 564 return (metaslotLoggedIn); 565 } 566 567 void 568 metaslot_set_logged_in_flag(boolean_t value) 569 { 570 (void) pthread_mutex_lock(&metaslotLoggedIn_mutex); 571 metaslotLoggedIn = value; 572 (void) pthread_mutex_unlock(&metaslotLoggedIn_mutex); 573 } 574