/* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (the "License"). * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright 2009 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ #include #include #include "metaGlobal.h" /* * The list and the list lock are global for two external uses: * 1) C_CloseAllSessions need to close the head (repeatedly, * until no more sessions exist). * 2) meta_object_find_by_handle needs to walk all sessions, * searching each session object list for matching objects. */ pthread_rwlock_t meta_sessionlist_lock; meta_session_t *meta_sessionlist_head; /* * The following 2 variables are used for tracking the number of * sessions and number of rw sessios that are currently open * * They are being manipulated in the metaSession.c file, and being * referenced in the metaSlotToken.c file */ CK_ULONG num_meta_sessions; CK_ULONG num_rw_meta_sessions; static pthread_rwlock_t meta_sessionclose_lock; /* * meta_sessionManager_initialize * * Called from meta_Initialize. Initializes all the variables used * by the session manager. */ CK_RV meta_sessionManager_initialize() { if (pthread_rwlock_init(&meta_sessionlist_lock, NULL) != 0) { return (CKR_FUNCTION_FAILED); } if (pthread_rwlock_init(&meta_sessionclose_lock, NULL) != 0) { (void) pthread_rwlock_destroy(&meta_sessionlist_lock); return (CKR_FUNCTION_FAILED); } meta_sessionlist_head = NULL; num_meta_sessions = 0; num_rw_meta_sessions = 0; return (CKR_OK); } /* * meta_sessionManager_finalize * * Close all sessions, and destroy all the locks */ void meta_sessionManager_finalize() { /* * Close any remaining metasessions, can just simply call * meta_CloseAllSessions. The METASLOT_SLOTID argument is * not used, but need to be passed in. */ (void) meta_CloseAllSessions(METASLOT_SLOTID); (void) pthread_rwlock_destroy(&meta_sessionclose_lock); (void) pthread_rwlock_destroy(&meta_sessionlist_lock); } /* * meta_handle2session * * Convert a CK_SESSION_HANDLE to the corresponding metasession. If * successful, a write-lock on the session will be held to indicate * that it's in use. Call REFRELEASE() when finished. * */ CK_RV meta_handle2session(CK_SESSION_HANDLE hSession, meta_session_t **session) { meta_session_t *tmp_session = (meta_session_t *)(hSession); /* Check for bad args (eg CK_INVALID_HANDLE, which is 0/NULL). */ if (tmp_session == NULL || tmp_session->magic_marker != METASLOT_SESSION_MAGIC) { return (CKR_SESSION_HANDLE_INVALID); } /* * sessions can only be used by a single thread at a time. * So, we need to get a write-lock. */ (void) pthread_rwlock_wrlock(&tmp_session->session_lock); /* Make sure this session is not in the process of being deleted */ (void) pthread_mutex_lock(&tmp_session->isClosingSession_lock); if (tmp_session->isClosingSession) { (void) pthread_mutex_unlock( &tmp_session->isClosingSession_lock); (void) pthread_rwlock_unlock(&tmp_session->session_lock); return (CKR_SESSION_HANDLE_INVALID); } (void) pthread_mutex_unlock(&tmp_session->isClosingSession_lock); *session = tmp_session; return (CKR_OK); } /* * meta_session_alloc */ CK_RV meta_session_alloc(meta_session_t **session) { meta_session_t *new_session; /* Allocate memory for the session. */ new_session = calloc(1, sizeof (meta_session_t)); if (new_session == NULL) return (CKR_HOST_MEMORY); (new_session->mech_support_info).supporting_slots = malloc(meta_slotManager_get_slotcount() * sizeof (mechinfo_t *)); if ((new_session->mech_support_info).supporting_slots == NULL) { free(new_session); return (CKR_HOST_MEMORY); } (new_session->mech_support_info).num_supporting_slots = 0; new_session->magic_marker = METASLOT_SESSION_MAGIC; (void) pthread_rwlock_init(&new_session->session_lock, NULL); (void) pthread_mutex_init(&new_session->isClosingSession_lock, NULL); (void) pthread_rwlock_init(&new_session->object_list_lock, NULL); *session = new_session; return (CKR_OK); } /* * meta_session_activate * * Create and add a session to the list of active meta sessions. */ CK_RV meta_session_activate(meta_session_t *session) { CK_RV rv = CKR_OK; /* Add session to the list of sessions. */ (void) pthread_rwlock_wrlock(&meta_sessionlist_lock); INSERT_INTO_LIST(meta_sessionlist_head, session); (void) pthread_rwlock_unlock(&meta_sessionlist_lock); return (rv); } /* * meta_session_deactivate * * */ CK_RV meta_session_deactivate(meta_session_t *session, boolean_t have_sessionlist_lock) { boolean_t isLastSession = B_FALSE; meta_object_t *object; /* Safely resolve attempts of concurrent-close */ (void) pthread_mutex_lock(&session->isClosingSession_lock); if (session->isClosingSession) { /* Lost a delete race. */ (void) pthread_mutex_unlock(&session->isClosingSession_lock); REFRELEASE(session); return (CKR_SESSION_HANDLE_INVALID); } session->isClosingSession = B_TRUE; session->magic_marker = METASLOT_SESSION_BADMAGIC; (void) pthread_mutex_unlock(&session->isClosingSession_lock); /* * Remove session from the session list. Once removed, it will not * be possible for another thread to begin using the session. */ (void) pthread_rwlock_wrlock(&meta_sessionclose_lock); if (!have_sessionlist_lock) { (void) pthread_rwlock_wrlock(&meta_sessionlist_lock); } REMOVE_FROM_LIST(meta_sessionlist_head, session); if (meta_sessionlist_head == NULL) { isLastSession = B_TRUE; } if (!have_sessionlist_lock) { (void) pthread_rwlock_unlock(&meta_sessionlist_lock); } (void) pthread_rwlock_unlock(&meta_sessionclose_lock); (void) pthread_rwlock_unlock(&session->session_lock); /* Cleanup any in-progress operations. */ if (session->op1.type != 0) { meta_operation_cleanup(session, session->op1.type, FALSE); } if (session->op1.session != NULL) { meta_release_slot_session(session->op1.session); session->op1.session = NULL; } /* Remove all the session metaobjects created in this session. */ /* Basically, emulate C_DestroyObject, including safety h2s */ while ((object = session->object_list_head) != NULL) { CK_RV rv; rv = meta_handle2object((CK_OBJECT_HANDLE)object, &object); if (rv != CKR_OK) { /* Can only happen if someone else just closed it. */ continue; } rv = meta_object_deactivate(object, B_FALSE, B_TRUE); if (rv != CKR_OK) { continue; } rv = meta_object_dealloc(NULL, object, B_FALSE); if (rv != CKR_OK) { continue; } } if ((isLastSession) && (metaslot_logged_in())) { slot_session_t *slotsessp; CK_RV rv; rv = meta_get_slot_session(get_keystore_slotnum(), &slotsessp, session->session_flags); if (rv != CKR_OK) return (rv); rv = FUNCLIST(slotsessp->fw_st_id)->C_Logout( slotsessp->hSession); meta_release_slot_session(slotsessp); /* if C_Logout fails, just ignore the error */ metaslot_set_logged_in_flag(B_FALSE); if (rv != CKR_OK) return (rv); /* need to deactivate all the PRIVATE token objects */ rv = meta_token_object_deactivate(PRIVATE_TOKEN); if (rv != CKR_OK) { return (rv); } } return (CKR_OK); } /* * meta_session_dealloc * * Release the resources held by a metasession. If the session has been * activated, it must be deactivated first. */ void meta_session_dealloc(meta_session_t *session) { if ((session->find_objs_info).matched_objs) { free((session->find_objs_info).matched_objs); } free((session->mech_support_info).supporting_slots); /* * If there were active operations, cleanup the slot session so that * it can be reused (otherwise provider might complain that an * operation is active). */ if (session->op1.type != 0) meta_operation_cleanup(session, session->op1.type, FALSE); /* Final object cleanup. */ (void) pthread_rwlock_destroy(&session->session_lock); (void) pthread_mutex_destroy(&session->isClosingSession_lock); (void) pthread_rwlock_destroy(&session->object_list_lock); meta_session_delay_free(session); } /* * This function adds the to-be-freed meta session to a linked list. * When the number of sessions queued in the linked list reaches the * maximum threshold MAX_SESSION_TO_BE_FREED, it will free the first * session (FIFO) in the list. */ void meta_session_delay_free(meta_session_t *sp) { meta_session_t *tmp; (void) pthread_mutex_lock(&ses_delay_freed.ses_to_be_free_mutex); /* Add the newly deleted session at the end of the list */ sp->next = NULL; if (ses_delay_freed.first == NULL) { ses_delay_freed.last = sp; ses_delay_freed.first = sp; } else { ses_delay_freed.last->next = sp; ses_delay_freed.last = sp; } if (++ses_delay_freed.count >= MAX_SESSION_TO_BE_FREED) { /* * Free the first session in the list only if * the total count reaches maximum threshold. */ ses_delay_freed.count--; tmp = ses_delay_freed.first->next; free(ses_delay_freed.first); ses_delay_freed.first = tmp; } (void) pthread_mutex_unlock(&ses_delay_freed.ses_to_be_free_mutex); }