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 <stdlib.h>
     27 #include <string.h>
     28 #include "metaGlobal.h"
     29 
     30 /*
     31  * The list and the list lock are global for two external uses:
     32  * 1) C_CloseAllSessions need to close the head (repeatedly,
     33  *    until no more sessions exist).
     34  * 2) meta_object_find_by_handle needs to walk all sessions,
     35  *    searching each session object list for matching objects.
     36  */
     37 pthread_rwlock_t meta_sessionlist_lock;
     38 meta_session_t *meta_sessionlist_head;
     39 
     40 /*
     41  * The following 2 variables are used for tracking the number of
     42  * sessions and number of rw sessios that are currently open
     43  *
     44  * They are being manipulated in the metaSession.c file, and being
     45  * referenced in the metaSlotToken.c file
     46  */
     47 CK_ULONG num_meta_sessions;
     48 CK_ULONG num_rw_meta_sessions;
     49 
     50 
     51 
     52 static pthread_rwlock_t meta_sessionclose_lock;
     53 
     54 
     55 /*
     56  * meta_sessionManager_initialize
     57  *
     58  * Called from meta_Initialize.  Initializes all the variables used
     59  * by the session manager.
     60  */
     61 CK_RV
     62 meta_sessionManager_initialize()
     63 {
     64 
     65 	if (pthread_rwlock_init(&meta_sessionlist_lock, NULL) != 0) {
     66 		return (CKR_FUNCTION_FAILED);
     67 	}
     68 
     69 	if (pthread_rwlock_init(&meta_sessionclose_lock, NULL) != 0) {
     70 		(void) pthread_rwlock_destroy(&meta_sessionlist_lock);
     71 		return (CKR_FUNCTION_FAILED);
     72 	}
     73 
     74 	meta_sessionlist_head = NULL;
     75 	num_meta_sessions = 0;
     76 	num_rw_meta_sessions = 0;
     77 
     78 	return (CKR_OK);
     79 }
     80 
     81 /*
     82  * meta_sessionManager_finalize
     83  *
     84  * Close all sessions, and destroy all the locks
     85  */
     86 void
     87 meta_sessionManager_finalize()
     88 {
     89 	/*
     90 	 * Close any remaining metasessions, can just simply call
     91 	 * meta_CloseAllSessions.  The METASLOT_SLOTID argument is
     92 	 * not used, but need to be passed in.
     93 	 */
     94 	(void) meta_CloseAllSessions(METASLOT_SLOTID);
     95 
     96 	(void) pthread_rwlock_destroy(&meta_sessionclose_lock);
     97 
     98 	(void) pthread_rwlock_destroy(&meta_sessionlist_lock);
     99 }
    100 
    101 /*
    102  * meta_handle2session
    103  *
    104  * Convert a CK_SESSION_HANDLE to the corresponding metasession. If
    105  * successful, a write-lock on the session will be held to indicate
    106  * that it's in use. Call REFRELEASE() when finished.
    107  *
    108  */
    109 CK_RV
    110 meta_handle2session(CK_SESSION_HANDLE hSession, meta_session_t **session)
    111 {
    112 	meta_session_t *tmp_session = (meta_session_t *)(hSession);
    113 
    114 	/* Check for bad args (eg CK_INVALID_HANDLE, which is 0/NULL). */
    115 	if (tmp_session == NULL ||
    116 	    tmp_session->magic_marker != METASLOT_SESSION_MAGIC) {
    117 		return (CKR_SESSION_HANDLE_INVALID);
    118 	}
    119 
    120 	/*
    121 	 * sessions can only be used by a single thread at a time.
    122 	 * So, we need to get a write-lock.
    123 	 */
    124 	(void) pthread_rwlock_wrlock(&tmp_session->session_lock);
    125 
    126 	/* Make sure this session is not in the process of being deleted */
    127 	(void) pthread_mutex_lock(&tmp_session->isClosingSession_lock);
    128 	if (tmp_session->isClosingSession) {
    129 		(void) pthread_mutex_unlock(
    130 		    &tmp_session->isClosingSession_lock);
    131 		(void) pthread_rwlock_unlock(&tmp_session->session_lock);
    132 		return (CKR_SESSION_HANDLE_INVALID);
    133 	}
    134 	(void) pthread_mutex_unlock(&tmp_session->isClosingSession_lock);
    135 
    136 	*session = tmp_session;
    137 	return (CKR_OK);
    138 }
    139 
    140 
    141 /*
    142  * meta_session_alloc
    143  */
    144 CK_RV
    145 meta_session_alloc(meta_session_t **session)
    146 {
    147 	meta_session_t *new_session;
    148 
    149 	/* Allocate memory for the session. */
    150 	new_session = calloc(1, sizeof (meta_session_t));
    151 	if (new_session == NULL)
    152 		return (CKR_HOST_MEMORY);
    153 
    154 	(new_session->mech_support_info).supporting_slots
    155 	    = malloc(meta_slotManager_get_slotcount() * sizeof (mechinfo_t *));
    156 	if ((new_session->mech_support_info).supporting_slots == NULL) {
    157 		free(new_session);
    158 		return (CKR_HOST_MEMORY);
    159 	}
    160 	(new_session->mech_support_info).num_supporting_slots = 0;
    161 
    162 	new_session->magic_marker = METASLOT_SESSION_MAGIC;
    163 	(void) pthread_rwlock_init(&new_session->session_lock, NULL);
    164 	(void) pthread_mutex_init(&new_session->isClosingSession_lock, NULL);
    165 	(void) pthread_rwlock_init(&new_session->object_list_lock, NULL);
    166 
    167 	*session = new_session;
    168 	return (CKR_OK);
    169 }
    170 
    171 
    172 /*
    173  * meta_session_activate
    174  *
    175  * Create and add a session to the list of active meta sessions.
    176  */
    177 CK_RV
    178 meta_session_activate(meta_session_t *session)
    179 {
    180 	CK_RV rv = CKR_OK;
    181 
    182 	/* Add session to the list of sessions. */
    183 	(void) pthread_rwlock_wrlock(&meta_sessionlist_lock);
    184 	INSERT_INTO_LIST(meta_sessionlist_head, session);
    185 	(void) pthread_rwlock_unlock(&meta_sessionlist_lock);
    186 
    187 	return (rv);
    188 }
    189 
    190 /*
    191  * meta_session_deactivate
    192  *
    193  *
    194  */
    195 CK_RV
    196 meta_session_deactivate(meta_session_t *session,
    197     boolean_t have_sessionlist_lock)
    198 {
    199 	boolean_t isLastSession = B_FALSE;
    200 	meta_object_t *object;
    201 
    202 	/* Safely resolve attempts of concurrent-close */
    203 	(void) pthread_mutex_lock(&session->isClosingSession_lock);
    204 	if (session->isClosingSession) {
    205 		/* Lost a delete race. */
    206 		(void) pthread_mutex_unlock(&session->isClosingSession_lock);
    207 		REFRELEASE(session);
    208 		return (CKR_SESSION_HANDLE_INVALID);
    209 	}
    210 	session->isClosingSession = B_TRUE;
    211 	session->magic_marker = METASLOT_SESSION_BADMAGIC;
    212 	(void) pthread_mutex_unlock(&session->isClosingSession_lock);
    213 
    214 	/*
    215 	 * Remove session from the session list. Once removed, it will not
    216 	 * be possible for another thread to begin using the session.
    217 	 */
    218 	(void) pthread_rwlock_wrlock(&meta_sessionclose_lock);
    219 	if (!have_sessionlist_lock) {
    220 		(void) pthread_rwlock_wrlock(&meta_sessionlist_lock);
    221 	}
    222 
    223 	REMOVE_FROM_LIST(meta_sessionlist_head, session);
    224 	if (meta_sessionlist_head == NULL) {
    225 		isLastSession = B_TRUE;
    226 	}
    227 	if (!have_sessionlist_lock) {
    228 		(void) pthread_rwlock_unlock(&meta_sessionlist_lock);
    229 	}
    230 	(void) pthread_rwlock_unlock(&meta_sessionclose_lock);
    231 
    232 	(void) pthread_rwlock_unlock(&session->session_lock);
    233 
    234 	/* Cleanup any in-progress operations. */
    235 	if (session->op1.type != 0) {
    236 		meta_operation_cleanup(session, session->op1.type, FALSE);
    237 	}
    238 
    239 	if (session->op1.session != NULL) {
    240 		meta_release_slot_session(session->op1.session);
    241 		session->op1.session = NULL;
    242 	}
    243 
    244 	/* Remove all the session metaobjects created in this session. */
    245 	/* Basically, emulate C_DestroyObject, including safety h2s */
    246 	while ((object = session->object_list_head) != NULL) {
    247 		CK_RV rv;
    248 
    249 		rv = meta_handle2object((CK_OBJECT_HANDLE)object, &object);
    250 		if (rv != CKR_OK) {
    251 			/* Can only happen if someone else just closed it. */
    252 			continue;
    253 		}
    254 
    255 		rv = meta_object_deactivate(object, B_FALSE, B_TRUE);
    256 		if (rv != CKR_OK) {
    257 			continue;
    258 		}
    259 
    260 		rv = meta_object_dealloc(NULL, object, B_FALSE);
    261 		if (rv != CKR_OK) {
    262 			continue;
    263 		}
    264 
    265 	}
    266 
    267 	if ((isLastSession) && (metaslot_logged_in())) {
    268 		slot_session_t *slotsessp;
    269 		CK_RV rv;
    270 
    271 		rv = meta_get_slot_session(get_keystore_slotnum(), &slotsessp,
    272 		    session->session_flags);
    273 		if (rv != CKR_OK)
    274 			return (rv);
    275 		rv = FUNCLIST(slotsessp->fw_st_id)->C_Logout(
    276 		    slotsessp->hSession);
    277 
    278 		meta_release_slot_session(slotsessp);
    279 
    280 		/* if C_Logout fails, just ignore the error */
    281 		metaslot_set_logged_in_flag(B_FALSE);
    282 
    283 		if (rv != CKR_OK)
    284 			return (rv);
    285 
    286 		/* need to deactivate all the PRIVATE token objects */
    287 		rv = meta_token_object_deactivate(PRIVATE_TOKEN);
    288 		if (rv != CKR_OK) {
    289 			return (rv);
    290 		}
    291 	}
    292 
    293 	return (CKR_OK);
    294 }
    295 
    296 
    297 /*
    298  * meta_session_dealloc
    299  *
    300  * Release the resources held by a metasession. If the session has been
    301  * activated, it must be deactivated first.
    302  */
    303 void
    304 meta_session_dealloc(meta_session_t *session)
    305 {
    306 	if ((session->find_objs_info).matched_objs) {
    307 		free((session->find_objs_info).matched_objs);
    308 	}
    309 
    310 	free((session->mech_support_info).supporting_slots);
    311 
    312 	/*
    313 	 * If there were active operations, cleanup the slot session so that
    314 	 * it can be reused (otherwise provider might complain that an
    315 	 * operation is active).
    316 	 */
    317 	if (session->op1.type != 0)
    318 		meta_operation_cleanup(session, session->op1.type, FALSE);
    319 
    320 	/* Final object cleanup. */
    321 	(void) pthread_rwlock_destroy(&session->session_lock);
    322 	(void) pthread_mutex_destroy(&session->isClosingSession_lock);
    323 	(void) pthread_rwlock_destroy(&session->object_list_lock);
    324 
    325 	meta_session_delay_free(session);
    326 }
    327 
    328 /*
    329  * This function adds the to-be-freed meta session to a linked list.
    330  * When the number of sessions queued in the linked list reaches the
    331  * maximum threshold MAX_SESSION_TO_BE_FREED, it will free the first
    332  * session (FIFO) in the list.
    333  */
    334 void
    335 meta_session_delay_free(meta_session_t *sp)
    336 {
    337 	meta_session_t *tmp;
    338 
    339 	(void) pthread_mutex_lock(&ses_delay_freed.ses_to_be_free_mutex);
    340 
    341 	/* Add the newly deleted session at the end of the list */
    342 	sp->next = NULL;
    343 	if (ses_delay_freed.first == NULL) {
    344 		ses_delay_freed.last = sp;
    345 		ses_delay_freed.first = sp;
    346 	} else {
    347 		ses_delay_freed.last->next = sp;
    348 		ses_delay_freed.last = sp;
    349 	}
    350 
    351 	if (++ses_delay_freed.count >= MAX_SESSION_TO_BE_FREED) {
    352 		/*
    353 		 * Free the first session in the list only if
    354 		 * the total count reaches maximum threshold.
    355 		 */
    356 		ses_delay_freed.count--;
    357 		tmp = ses_delay_freed.first->next;
    358 		free(ses_delay_freed.first);
    359 		ses_delay_freed.first = tmp;
    360 	}
    361 	(void) pthread_mutex_unlock(&ses_delay_freed.ses_to_be_free_mutex);
    362 }
    363