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 #include <pthread.h> 30 #include <syslog.h> 31 #include <stdlib.h> 32 #include <string.h> 33 #include <errno.h> 34 #include <sys/crypto/ioctl.h> 35 #include <security/cryptoki.h> 36 #include "kernelGlobal.h" 37 #include "kernelSession.h" 38 #include "kernelSlot.h" 39 #include "kernelEmulate.h" 40 41 static pthread_mutex_t delete_sessions_mutex = PTHREAD_MUTEX_INITIALIZER; 42 43 /* 44 * Delete all the sessions. First, obtain the slot lock. 45 * Then start to delete one session at a time. The boolean wrapper_only 46 * argument indicates that whether the caller only wants to clean up the 47 * session wrappers and the object wrappers in the library. 48 * - When this function is called by C_CloseAllSessions or indirectly by 49 * C_Finalize, wrapper_only is FALSE. 50 * - When this function is called by cleanup_child, wrapper_only is TRUE. 51 */ 52 void 53 kernel_delete_all_sessions(CK_SLOT_ID slotID, boolean_t wrapper_only) 54 { 55 kernel_session_t *session_p; 56 kernel_slot_t *pslot; 57 58 (void) pthread_mutex_lock(&delete_sessions_mutex); 59 60 pslot = slot_table[slotID]; 61 62 /* 63 * Delete all the sessions in the slot's session list. 64 * The routine kernel_delete_session() updates the linked list. 65 * So, we do not need to maintain the list here. 66 */ 67 for (;;) { 68 (void) pthread_mutex_lock(&pslot->sl_mutex); 69 if (pslot->sl_sess_list == NULL) 70 break; 71 72 session_p = pslot->sl_sess_list; 73 /* 74 * Set SESSION_IS_CLOSING flag so any access to this 75 * session will be rejected. 76 */ 77 (void) pthread_mutex_lock(&session_p->session_mutex); 78 if (session_p->ses_close_sync & SESSION_IS_CLOSING) { 79 (void) pthread_mutex_unlock(&session_p->session_mutex); 80 continue; 81 } 82 session_p->ses_close_sync |= SESSION_IS_CLOSING; 83 (void) pthread_mutex_unlock(&session_p->session_mutex); 84 85 (void) pthread_mutex_unlock(&pslot->sl_mutex); 86 kernel_delete_session(slotID, session_p, B_FALSE, wrapper_only); 87 } 88 (void) pthread_mutex_unlock(&pslot->sl_mutex); 89 (void) pthread_mutex_unlock(&delete_sessions_mutex); 90 } 91 92 /* 93 * Create a new session struct, and add it to the slot's session list. 94 * 95 * This function is called by C_OpenSession(), which hold the slot lock. 96 */ 97 CK_RV 98 kernel_add_session(CK_SLOT_ID slotID, CK_FLAGS flags, CK_VOID_PTR pApplication, 99 CK_NOTIFY notify, CK_ULONG *sessionhandle_p) 100 { 101 CK_RV rv = CKR_OK; 102 kernel_session_t *new_sp = NULL; 103 crypto_open_session_t open_session; 104 kernel_slot_t *pslot; 105 int r; 106 107 /* Allocate a new session struct */ 108 new_sp = calloc(1, sizeof (kernel_session_t)); 109 if (new_sp == NULL) { 110 return (CKR_HOST_MEMORY); 111 } 112 113 new_sp->magic_marker = KERNELTOKEN_SESSION_MAGIC; 114 new_sp->pApplication = pApplication; 115 new_sp->Notify = notify; 116 new_sp->flags = flags; 117 new_sp->ses_RO = (flags & CKF_RW_SESSION) ? B_FALSE : B_TRUE; 118 new_sp->ses_slotid = slotID; 119 new_sp->object_list = NULL; 120 new_sp->ses_refcnt = 0; 121 new_sp->ses_close_sync = 0; 122 123 /* Initialize the lock for the newly created session */ 124 if (pthread_mutex_init(&new_sp->session_mutex, NULL) != 0) { 125 free(new_sp); 126 return (CKR_CANT_LOCK); 127 } 128 129 pslot = slot_table[slotID]; 130 open_session.os_provider_id = pslot->sl_provider_id; 131 open_session.os_flags = flags; 132 while ((r = ioctl(kernel_fd, CRYPTO_OPEN_SESSION, &open_session)) < 0) { 133 if (errno != EINTR) 134 break; 135 } 136 if (r < 0) { 137 rv = CKR_FUNCTION_FAILED; 138 } else { 139 rv = crypto2pkcs11_error_number(open_session.os_return_value); 140 } 141 142 if (rv != CKR_OK) { 143 (void) pthread_mutex_destroy(&new_sp->session_mutex); 144 free(new_sp); 145 return (rv); 146 } 147 148 new_sp->k_session = open_session.os_session; 149 150 (void) pthread_mutex_init(&new_sp->ses_free_mutex, NULL); 151 (void) pthread_cond_init(&new_sp->ses_free_cond, NULL); 152 153 /* Insert the new session in front of the slot's session list */ 154 if (pslot->sl_sess_list == NULL) { 155 pslot->sl_sess_list = new_sp; 156 new_sp->prev = NULL; 157 new_sp->next = NULL; 158 } else { 159 pslot->sl_sess_list->prev = new_sp; 160 new_sp->next = pslot->sl_sess_list; 161 new_sp->prev = NULL; 162 pslot->sl_sess_list = new_sp; 163 } 164 165 /* Type casting the address of a session struct to a session handle */ 166 *sessionhandle_p = (CK_ULONG)new_sp; 167 168 return (CKR_OK); 169 } 170 171 /* 172 * Delete a session: 173 * - Remove the session from the slot's session list. 174 * - Release all the objects created by the session. 175 * 176 * The boolean argument slot_lock_held is used to indicate that whether 177 * the caller of this function holds the slot lock or not. 178 * - When called by kernel_delete_all_sessions(), which is called by 179 * C_Finalize() or C_CloseAllSessions() -- slot_lock_held = TRUE. 180 * - When called by C_CloseSession() -- slot_lock_held = FALSE. 181 */ 182 void 183 kernel_delete_session(CK_SLOT_ID slotID, kernel_session_t *session_p, 184 boolean_t slot_lock_held, boolean_t wrapper_only) 185 { 186 crypto_session_id_t k_session; 187 crypto_close_session_t close_session; 188 kernel_slot_t *pslot; 189 kernel_object_t *objp; 190 kernel_object_t *objp1; 191 192 /* 193 * Check to see if the caller holds the lock on the global 194 * session list. If not, we need to acquire that lock in 195 * order to proceed. 196 */ 197 pslot = slot_table[slotID]; 198 if (!slot_lock_held) { 199 /* Acquire the slot lock */ 200 (void) pthread_mutex_lock(&pslot->sl_mutex); 201 } 202 203 /* 204 * Remove the session from the slot's session list first. 205 */ 206 if (pslot->sl_sess_list == session_p) { 207 /* Session is the first one in the list */ 208 if (session_p->next) { 209 pslot->sl_sess_list = session_p->next; 210 session_p->next->prev = NULL; 211 } else { 212 /* Session is the only one in the list */ 213 pslot->sl_sess_list = NULL; 214 } 215 } else { 216 /* Session is not the first one in the list */ 217 if (session_p->next) { 218 /* Session is in the middle of the list */ 219 session_p->prev->next = session_p->next; 220 session_p->next->prev = session_p->prev; 221 } else { 222 /* Session is the last one in the list */ 223 session_p->prev->next = NULL; 224 } 225 } 226 227 if (!slot_lock_held) { 228 /* 229 * If the slot lock is obtained by 230 * this function, then release that lock after 231 * removing the session from session linked list. 232 * We want the releasing of the objects of the 233 * session, and freeing of the session itself to 234 * be done without holding the slot's session list 235 * lock. 236 */ 237 (void) pthread_mutex_unlock(&pslot->sl_mutex); 238 } 239 240 /* Acquire the individual session lock */ 241 (void) pthread_mutex_lock(&session_p->session_mutex); 242 243 /* 244 * Make sure another thread hasn't freed the session. 245 */ 246 if (session_p->magic_marker != KERNELTOKEN_SESSION_MAGIC) { 247 (void) pthread_mutex_unlock(&session_p->session_mutex); 248 return; 249 } 250 251 /* 252 * The deletion of a session must be blocked when the session reference 253 * count is not zero. This means that if the thread that is attempting 254 * to close the session must wait until the prior operations on this 255 * session are finished. 256 */ 257 (void) pthread_mutex_lock(&session_p->ses_free_mutex); 258 259 while (session_p->ses_refcnt != 0) { 260 /* 261 * We set the SESSION_REFCNT_WAITING flag before we put 262 * this closing thread in a wait state, so other non-closing 263 * operation thread will wake it up only when 264 * the session reference count becomes zero and this flag 265 * is set. 266 */ 267 session_p->ses_close_sync |= SESSION_REFCNT_WAITING; 268 (void) pthread_mutex_unlock(&session_p->session_mutex); 269 (void) pthread_cond_wait(&session_p->ses_free_cond, 270 &session_p->ses_free_mutex); 271 (void) pthread_mutex_lock(&session_p->session_mutex); 272 } 273 274 session_p->ses_close_sync &= ~SESSION_REFCNT_WAITING; 275 276 /* Mark session as no longer valid. */ 277 session_p->magic_marker = 0; 278 279 (void) pthread_mutex_unlock(&session_p->ses_free_mutex); 280 (void) pthread_mutex_destroy(&session_p->ses_free_mutex); 281 (void) pthread_cond_destroy(&session_p->ses_free_cond); 282 283 /* 284 * Remove all the objects created in this session, waiting 285 * until each object's refcnt is 0. 286 */ 287 kernel_delete_all_objects_in_session(session_p, wrapper_only); 288 289 /* In case application did not call Final */ 290 if (session_p->digest.context != NULL) { 291 digest_buf_t *bufp = session_p->digest.context; 292 293 if (bufp->buf != NULL) { 294 free_soft_ctx(get_sp(&session_p->digest), OP_DIGEST); 295 bzero(bufp->buf, bufp->indata_len); 296 free(bufp->buf); 297 } 298 free(bufp); 299 } 300 301 if (session_p->encrypt.context != NULL) 302 free(session_p->encrypt.context); 303 304 if (session_p->decrypt.context != NULL) 305 free(session_p->decrypt.context); 306 307 if (session_p->sign.context != NULL) { 308 digest_buf_t *bufp = session_p->sign.context; 309 310 if (bufp->buf != NULL) { 311 free_soft_ctx(get_sp(&session_p->sign), OP_SIGN); 312 bzero(bufp->buf, bufp->indata_len); 313 free(bufp->buf); 314 } 315 free(bufp); 316 } 317 318 if (session_p->verify.context != NULL) { 319 digest_buf_t *bufp = session_p->verify.context; 320 321 if (bufp->buf != NULL) { 322 free_soft_ctx(get_sp(&session_p->verify), OP_VERIFY); 323 bzero(bufp->buf, bufp->indata_len); 324 free(bufp->buf); 325 } 326 free(bufp); 327 } 328 329 k_session = session_p->k_session; 330 331 /* Reset SESSION_IS_CLOSING flag. */ 332 session_p->ses_close_sync &= ~SESSION_IS_CLOSING; 333 334 (void) pthread_mutex_unlock(&session_p->session_mutex); 335 /* Destroy the individual session lock */ 336 (void) pthread_mutex_destroy(&session_p->session_mutex); 337 338 if (!wrapper_only) { 339 close_session.cs_session = k_session; 340 while (ioctl(kernel_fd, CRYPTO_CLOSE_SESSION, 341 &close_session) < 0) { 342 if (errno != EINTR) 343 break; 344 } 345 /* 346 * Ignore ioctl return codes. If the library tells the kernel 347 * to close a session and the kernel says "I don't know what 348 * session you're talking about", there's not much that can be 349 * done. All sessions in the kernel will be closed when the 350 * application exits and closes /dev/crypto. 351 */ 352 } 353 kernel_session_delay_free(session_p); 354 355 /* 356 * If there is no more session remained in this slot, reset the slot's 357 * session state to CKU_PUBLIC. Also, clean up all the token object 358 * wrappers in the library for this slot. 359 */ 360 /* Acquire the slot lock if lock is not held */ 361 if (!slot_lock_held) { 362 (void) pthread_mutex_lock(&pslot->sl_mutex); 363 } 364 365 if (pslot->sl_sess_list == NULL) { 366 /* Reset the session auth state. */ 367 pslot->sl_state = CKU_PUBLIC; 368 369 /* Clean up token object wrappers. */ 370 objp = pslot->sl_tobj_list; 371 while (objp) { 372 objp1 = objp->next; 373 (void) pthread_mutex_destroy(&objp->object_mutex); 374 (void) kernel_object_delay_free(objp); 375 objp = objp1; 376 } 377 pslot->sl_tobj_list = NULL; 378 } 379 380 /* Release the slot lock if lock is not held */ 381 if (!slot_lock_held) { 382 (void) pthread_mutex_unlock(&pslot->sl_mutex); 383 } 384 } 385 386 /* 387 * This function is used to type cast a session handle to a pointer to 388 * the session struct. Also, it does the following things: 389 * 1) Check to see if the session struct is tagged with a session 390 * magic number. This is to detect when an application passes 391 * a bogus session pointer. 392 * 2) Acquire the locks on the designated session and the slot which owns 393 * this session. 394 * 3) Check to see if the session is in the closing state that another 395 * thread is performing. 396 * 4) Increment the session reference count by one. This is to prevent 397 * this session from being closed by other thread. 398 * 5) Release the locks on the designated session and on the slot. 399 */ 400 CK_RV 401 handle2session(CK_SESSION_HANDLE hSession, kernel_session_t **session_p) 402 { 403 kernel_session_t *sp = (kernel_session_t *)(hSession); 404 CK_RV rv; 405 kernel_slot_t *pslot; 406 407 if ((sp == NULL) || 408 (sp->magic_marker != KERNELTOKEN_SESSION_MAGIC)) { 409 return (CKR_SESSION_HANDLE_INVALID); 410 } else { 411 pslot = slot_table[sp->ses_slotid]; 412 (void) pthread_mutex_lock(&pslot->sl_mutex); 413 (void) pthread_mutex_lock(&sp->session_mutex); 414 if (sp->ses_close_sync & SESSION_IS_CLOSING) { 415 rv = CKR_SESSION_CLOSED; 416 } else { 417 /* Increment session ref count. */ 418 sp->ses_refcnt++; 419 rv = CKR_OK; 420 } 421 (void) pthread_mutex_unlock(&sp->session_mutex); 422 (void) pthread_mutex_unlock(&pslot->sl_mutex); 423 } 424 425 if (rv == CKR_OK) 426 *session_p = sp; 427 428 return (rv); 429 } 430 431 /* 432 * This function adds the to-be-freed session to a linked list. 433 * When the number of sessions queued in the linked list reaches the 434 * maximum threshold MAX_SES_TO_BE_FREED, it will free the first 435 * session (FIFO) in the list. 436 */ 437 void 438 kernel_session_delay_free(kernel_session_t *sp) 439 { 440 kernel_session_t *tmp; 441 442 (void) pthread_mutex_lock(&ses_delay_freed.ses_to_be_free_mutex); 443 444 /* Add the newly deleted session at the end of the list */ 445 sp->next = NULL; 446 if (ses_delay_freed.first == NULL) { 447 ses_delay_freed.last = sp; 448 ses_delay_freed.first = sp; 449 } else { 450 ses_delay_freed.last->next = sp; 451 ses_delay_freed.last = sp; 452 } 453 454 if (++ses_delay_freed.count >= MAX_SES_TO_BE_FREED) { 455 /* 456 * Free the first session in the list only if 457 * the total count reaches maximum threshold. 458 */ 459 ses_delay_freed.count--; 460 tmp = ses_delay_freed.first->next; 461 free(ses_delay_freed.first); 462 ses_delay_freed.first = tmp; 463 } 464 (void) pthread_mutex_unlock(&ses_delay_freed.ses_to_be_free_mutex); 465 } 466