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