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