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