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