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 2007 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 /* 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 (void) memset(&slotinfo, 0, sizeof (CK_SLOT_INFO)); 238 rv = FUNCLIST(fw_st_id)->C_GetSlotInfo(true_id, 239 &slotinfo); 240 if (rv != CKR_OK) 241 continue; 242 243 if (strncmp((char *)SOFT_SLOT_DESCRIPTION, 244 (char *)slotinfo.slotDescription, 245 SLOT_DESCRIPTION_SIZE) == 0) { 246 softtoken_slotnum = slot; 247 } 248 249 if (metaslot_config.keystore_slot_specified) { 250 251 unsigned char *slot; 252 size_t slot_str_len; 253 254 rv = FUNCLIST(fw_st_id)->C_GetSlotInfo(true_id, 255 &slotinfo); 256 if (rv != CKR_OK) 257 continue; 258 259 /* 260 * pad slot description from user/system configuration 261 * with spaces 262 */ 263 slot = metaslot_config.keystore_slot; 264 slot_str_len = strlen((char *)slot); 265 (void) memset(slot + slot_str_len, ' ', 266 SLOT_DESCRIPTION_SIZE - slot_str_len); 267 268 /* 269 * The PKCS#11 strings are not null-terminated, so, 270 * we just compare SLOT_DESCRIPTION_SIZE bytes 271 */ 272 if (strncmp((char *)slot, 273 (char *)slotinfo.slotDescription, 274 SLOT_DESCRIPTION_SIZE) == 0) { 275 num_matched++; 276 } 277 } 278 279 if (metaslot_config.keystore_token_specified) { 280 unsigned char *token; 281 size_t token_str_len; 282 283 rv = FUNCLIST(fw_st_id)->C_GetTokenInfo(true_id, 284 &tokeninfo); 285 286 if (rv != CKR_OK) { 287 continue; 288 } 289 290 have_tokeninfo = B_TRUE; 291 292 /* 293 * pad slot description from user/system configuration 294 * with spaces 295 */ 296 token = metaslot_config.keystore_token; 297 token_str_len = strlen((char *)token); 298 (void) memset(token + token_str_len, ' ', 299 TOKEN_LABEL_SIZE - token_str_len); 300 301 /* 302 * The PKCS#11 strings are not null-terminated. 303 * So, just compare TOKEN_LABEL_SIZE bytes 304 */ 305 if (strncmp((char *)token, (char *)tokeninfo.label, 306 TOKEN_LABEL_SIZE) == 0) { 307 num_matched++; 308 } 309 } 310 311 if (num_match_needed == num_matched) { 312 /* match is found */ 313 314 if (!have_tokeninfo) { 315 rv = FUNCLIST(fw_st_id)->C_GetTokenInfo(true_id, 316 &tokeninfo); 317 if (rv != CKR_OK) { 318 continue; 319 } 320 } 321 322 323 if (tokeninfo.flags & CKF_WRITE_PROTECTED) { 324 /* 325 * Currently this is the only time that 326 * the write_protected state is set, and 327 * it is never cleared. The token could 328 * clear (or set!) this flag later on. 329 * We might want to adjust the state 330 * of metaslot, but there's know way to know 331 * when a token changes this flag. 332 */ 333 write_protected = B_TRUE; 334 } 335 336 found = B_TRUE; 337 break; 338 } 339 } 340 341 skip_search: 342 if (found) { 343 objtok_slotnum = slot; 344 } else { 345 /* 346 * if slot and/or token is not defined for the keystore, 347 * just use the first available slot as keystore 348 */ 349 objtok_slotnum = 0; 350 } 351 slots[objtok_slotnum].session_pool.keep_one_alive = B_TRUE; 352 metaslot_keystore_slotid = slots[objtok_slotnum].fw_st_id; 353 } 354 355 356 CK_ULONG 357 get_keystore_slotnum() 358 { 359 return (objtok_slotnum); 360 } 361 362 CK_ULONG 363 get_softtoken_slotnum() 364 { 365 return (softtoken_slotnum); 366 } 367 368 CK_SLOT_ID 369 meta_slotManager_get_framework_table_id(CK_ULONG slotnum) 370 { 371 /* 372 * This is only used internally, and so the slotnum should always 373 * be valid. 374 */ 375 return (slots[slotnum].fw_st_id); 376 } 377 378 CK_ULONG 379 meta_slotManager_get_slotcount() 380 { 381 return (num_slots); 382 } 383 384 boolean_t 385 meta_slotManager_token_write_protected() 386 { 387 return (write_protected); 388 } 389 390 /* 391 * Find a session in the given list that matches the specified flags. 392 * If such a session is found, it will be removed from the list, and 393 * returned to the caller. If such a session is not found, will 394 * return NULL 395 */ 396 static slot_session_t * 397 get_session(slot_session_t **session_list, CK_FLAGS flags) 398 { 399 400 slot_session_t *tmp_session; 401 402 tmp_session = *session_list; 403 404 while (tmp_session != NULL) { 405 if (tmp_session->session_flags == flags) { 406 break; 407 } else { 408 tmp_session = tmp_session->next; 409 } 410 411 } 412 413 if (tmp_session == NULL) { 414 /* no match */ 415 return (NULL); 416 } 417 418 /* Remove from list */ 419 REMOVE_FROM_LIST(*session_list, tmp_session); 420 return (tmp_session); 421 } 422 423 /* 424 * meta_get_slot_session 425 * 426 * Call to get a session with a specific slot/token. 427 * 428 * NOTE - We assume the slot allows an unlimited number of sessions. We 429 * could look at what's reported in the token info, but that information is 430 * not always set. It's also unclear when we should (A) wait for one to become 431 * available, (B) skip the slot for now or (C) return a fatal error. The 432 * extra complexity is not worth it. 433 * 434 */ 435 CK_RV 436 meta_get_slot_session(CK_ULONG slotnum, slot_session_t **session, 437 CK_FLAGS flags) { 438 session_pool_t *pool; 439 slot_session_t *new_session, *tmp_session; 440 CK_RV rv; 441 CK_SLOT_ID fw_st_id, true_id; 442 443 if (slotnum >= num_slots) { 444 return (CKR_SLOT_ID_INVALID); 445 } 446 447 pool = &slots[slotnum].session_pool; 448 449 /* 450 * Try to reuse an existing session. 451 */ 452 453 (void) pthread_mutex_lock(&pool->list_lock); 454 455 if (pool->idle_list_head != NULL) { 456 tmp_session = get_session(&(pool->idle_list_head), flags); 457 if (tmp_session != NULL) { 458 /* Add to active list */ 459 INSERT_INTO_LIST(pool->active_list_head, tmp_session); 460 *session = tmp_session; 461 pool->num_idle_sessions--; 462 (void) pthread_mutex_unlock(&pool->list_lock); 463 return (CKR_OK); 464 } 465 } 466 467 if (pool->persist_list_head != NULL) { 468 tmp_session = get_session(&(pool->persist_list_head), flags); 469 if (tmp_session != NULL) { 470 /* Add to active list */ 471 INSERT_INTO_LIST(pool->active_list_head, tmp_session); 472 *session = tmp_session; 473 (void) pthread_mutex_unlock(&pool->list_lock); 474 return (CKR_OK); 475 } 476 } 477 (void) pthread_mutex_unlock(&pool->list_lock); 478 479 fw_st_id = slots[slotnum].fw_st_id; 480 true_id = TRUEID(fw_st_id); 481 482 new_session = calloc(1, sizeof (slot_session_t)); 483 if (new_session == NULL) { 484 return (CKR_HOST_MEMORY); 485 } 486 487 /* initialize slotsession */ 488 new_session->slotnum = slotnum; 489 new_session->fw_st_id = fw_st_id; 490 new_session->object_list_head = NULL; 491 new_session->session_flags = flags; 492 (void) pthread_rwlock_init(&new_session->object_list_lock, NULL); 493 494 rv = FUNCLIST(fw_st_id)->C_OpenSession(true_id, flags, NULL, NULL, 495 &new_session->hSession); 496 497 if (rv == CKR_TOKEN_WRITE_PROTECTED) { 498 /* Retry with a RO session. */ 499 new_session->session_flags &= ~CKF_SERIAL_SESSION; 500 rv = FUNCLIST(fw_st_id)->C_OpenSession(true_id, 501 new_session->session_flags, NULL, NULL, 502 &new_session->hSession); 503 } 504 505 if (rv != CKR_OK) { 506 free(new_session); 507 return (CKR_FUNCTION_FAILED); 508 } 509 510 /* Insert session into active list */ 511 (void) pthread_mutex_lock(&pool->list_lock); 512 INSERT_INTO_LIST(pool->active_list_head, new_session); 513 (void) pthread_mutex_unlock(&pool->list_lock); 514 *session = new_session; 515 return (CKR_OK); 516 } 517 518 519 /* 520 * meta_release_slot_session 521 * 522 * Call to release a session obtained via meta_get_slot_session() 523 */ 524 void 525 meta_release_slot_session(slot_session_t *session) { 526 session_pool_t *pool; 527 boolean_t must_retain, can_close = B_FALSE; 528 boolean_t this_is_last_session = B_FALSE; 529 530 pool = &slots[session->slotnum].session_pool; 531 532 /* Note that the active_list must have >= 1 entry (this session) */ 533 if (pool->persist_list_head == NULL && 534 pool->idle_list_head == NULL && 535 pool->active_list_head->next == NULL) 536 this_is_last_session = B_TRUE; 537 538 /* 539 * If the session has session objects, we need to retain it. Also 540 * retain it if it's the only session holding login state (or handles 541 * to public token objects) 542 */ 543 must_retain = session->object_list_head != NULL || 544 (pool->keep_one_alive && this_is_last_session); 545 546 if ((!must_retain) && (pool->num_idle_sessions > MAX_IDLE_SESSIONS)) { 547 can_close = B_TRUE; 548 } 549 550 (void) pthread_mutex_lock(&pool->list_lock); 551 /* remove from active list */ 552 REMOVE_FROM_LIST(pool->active_list_head, session); 553 554 if (must_retain) { 555 /* insert into persist list */ 556 INSERT_INTO_LIST(pool->persist_list_head, session); 557 (void) pthread_mutex_unlock(&pool->list_lock); 558 return; 559 } else if (!can_close) { 560 /* insert into idle list */ 561 INSERT_INTO_LIST(pool->idle_list_head, session); 562 pool->num_idle_sessions++; 563 (void) pthread_mutex_unlock(&pool->list_lock); 564 return; 565 } 566 567 (void) pthread_mutex_unlock(&pool->list_lock); 568 569 (void) FUNCLIST(session->fw_st_id)->C_CloseSession(session->hSession); 570 571 (void) pthread_rwlock_destroy(&session->object_list_lock); 572 free(session); 573 } 574 575 /* 576 * Returns whether metaslot has directly logged in 577 */ 578 boolean_t 579 metaslot_logged_in() 580 { 581 return (metaslotLoggedIn); 582 } 583 584 void 585 metaslot_set_logged_in_flag(boolean_t value) 586 { 587 (void) pthread_mutex_lock(&metaslotLoggedIn_mutex); 588 metaslotLoggedIn = value; 589 (void) pthread_mutex_unlock(&metaslotLoggedIn_mutex); 590 } 591