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