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/*
2N/A * Functions for dealing with provider sessions
2N/A */
2N/A
2N/A#include <string.h>
2N/A#include <cryptoutil.h>
2N/A#include "metaGlobal.h"
2N/A#include "pkcs11Session.h"
2N/A#include "pkcs11Global.h"
2N/A
2N/A
2N/A/*
2N/A * This is just a **WILD** guess for the maximum idle sessions to
2N/A * keep for each slot. This number should probably be adjusted
2N/A * when there's more data from actual application use
2N/A */
2N/A#define MAX_IDLE_SESSIONS 100
2N/A
2N/A/*
2N/A * The following 5 variables are initialized at the time metaslot
2N/A * is initialized. They are not modified after they are initialized
2N/A *
2N/A * During initialization time, they are protected by the "initmutex"
2N/A * defined in metaGeneral.c
2N/A */
2N/Aslot_data_t *slots;
2N/ACK_SLOT_ID metaslot_keystore_slotid;
2N/Astatic CK_ULONG num_slots;
2N/Astatic CK_ULONG objtok_slotnum;
2N/Astatic CK_ULONG softtoken_slotnum;
2N/Astatic boolean_t write_protected;
2N/A
2N/A/* protects the "metaslotLoggedIn" variable */
2N/Astatic pthread_mutex_t metaslotLoggedIn_mutex = PTHREAD_MUTEX_INITIALIZER;
2N/Astatic boolean_t metaslotLoggedIn;
2N/A
2N/A/*
2N/A * meta_slotManager_initialize
2N/A *
2N/A * Called from C_Initialize. Allocates and initializes the storage needed
2N/A * by the slot manager.
2N/A */
2N/ACK_RV
2N/Ameta_slotManager_initialize(void)
2N/A{
2N/A CK_ULONG slot_count = 0;
2N/A CK_RV rv;
2N/A CK_SLOT_ID i;
2N/A
2N/A if (slots != NULL) {
2N/A meta_slotManager_finalize();
2N/A }
2N/A
2N/A /* Initialize the static variables */
2N/A write_protected = B_FALSE;
2N/A metaslotLoggedIn = B_FALSE;
2N/A
2N/A /*
2N/A * Count the number of slots in the framework.
2N/A * We start at ((slottable->st_first) + 1) instead of
2N/A * slottable->st_first because when we are here, metaslot is
2N/A * enabled, and st_first is always metaslot, which doesn't
2N/A * need to be counted.
2N/A */
2N/A for (i = (slottable->st_first) + 1; i <= slottable->st_last; i++) {
2N/A slot_count++;
2N/A }
2N/A
2N/A /*
2N/A * This shouldn't happen, because there should at least
2N/A * be 1 other slot besides metaslot.
2N/A */
2N/A if (slot_count < 1) {
2N/A rv = CKR_FUNCTION_FAILED;
2N/A goto clean_exit;
2N/A }
2N/A
2N/A slots = calloc(slot_count, sizeof (slot_data_t));
2N/A if (slots == NULL) {
2N/A rv = CKR_HOST_MEMORY;
2N/A goto clean_exit;
2N/A }
2N/A
2N/A /*
2N/A * Store the slot IDs. Adjust for the fact that the first slot is
2N/A * actually us (metaslot).
2N/A */
2N/A for (i = 0; i < slot_count; i++) {
2N/A slots[i].fw_st_id = i + 1;
2N/A (void) pthread_rwlock_init(
2N/A &(slots[i].tokenobject_list_lock), NULL);
2N/A }
2N/A num_slots = slot_count;
2N/A
2N/A return (CKR_OK);
2N/A
2N/Aclean_exit:
2N/A if (slots != NULL) {
2N/A free(slots);
2N/A slots = NULL;
2N/A }
2N/A
2N/A num_slots = 0;
2N/A
2N/A return (rv);
2N/A}
2N/A
2N/A
2N/A/*
2N/A * meta_slotManager_finalize
2N/A *
2N/A * Called from C_Finalize. Deallocates any storage held by the slot manager.
2N/A */
2N/Avoid
2N/Ameta_slotManager_finalize(void)
2N/A{
2N/A CK_ULONG slot;
2N/A
2N/A /* If no slots to free, return */
2N/A if (slots == NULL)
2N/A return;
2N/A
2N/A /*
2N/A * No need to lock pool, we assume all meta sessions are closed.
2N/A *
2N/A * Close all sessions in the idle and persist list.
2N/A * The active list is empty. It doesn't need to be checked.
2N/A */
2N/A
2N/A for (slot = 0; slot < num_slots; slot++) {
2N/A slot_session_t *session, *next_session;
2N/A
2N/A /*
2N/A * The slotobjects associated with the session should have
2N/A * been closed when the metaobjects were closed. Thus, no
2N/A * need to do anything here.
2N/A */
2N/A
2N/A session = slots[slot].session_pool.idle_list_head;
2N/A while (session) {
2N/A next_session = session->next;
2N/A (void) FUNCLIST(session->fw_st_id)->C_CloseSession(
2N/A session->hSession);
2N/A (void) pthread_rwlock_destroy(
2N/A &session->object_list_lock);
2N/A free(session);
2N/A session = next_session;
2N/A }
2N/A
2N/A session = slots[slot].session_pool.persist_list_head;
2N/A while (session) {
2N/A next_session = session->next;
2N/A (void) FUNCLIST(session->fw_st_id)->C_CloseSession(
2N/A session->hSession);
2N/A (void) pthread_rwlock_destroy(
2N/A &session->object_list_lock);
2N/A free(session);
2N/A session = next_session;
2N/A }
2N/A
2N/A (void) pthread_rwlock_destroy(
2N/A &slots[slot].tokenobject_list_lock);
2N/A }
2N/A
2N/A free(slots);
2N/A slots = NULL;
2N/A num_slots = 0;
2N/A}
2N/A
2N/A
2N/A/*
2N/A * meta_slotManager_find_object_token()
2N/A *
2N/A * Called from meta_Initialize. Searches for the "object token," which is used
2N/A * for storing token objects and logging into.
2N/A *
2N/A * We do the search using the following algorithm.
2N/A *
2N/A * If either ${METASLOT_OBJECTSTORE_SLOT} or ${METASLOT_OBJECTSTORE_TOKEN}
2N/A * environment variable is defined, the value of the defined variable(s)
2N/A * will be used for the match. All token and slot values defined system-wide
2N/A * will be ignored.
2N/A *
2N/A * If neither variables above are defined, the system-wide values defined
2N/A * in pkcs11.conf are used.
2N/A *
2N/A * If neither environment variables or system-wide values are defined,
2N/A * or if none of the existing slots/tokens match the defined
2N/A * values, the first slot after metaslot will be used as the default.
2N/A *
2N/A */
2N/Avoid
2N/Ameta_slotManager_find_object_token(void)
2N/A{
2N/A CK_ULONG slot;
2N/A boolean_t found = B_FALSE;
2N/A CK_RV rv;
2N/A unsigned int num_match_needed = 0;
2N/A CK_SLOT_INFO slotinfo;
2N/A CK_TOKEN_INFO tokeninfo;
2N/A
2N/A if (metaslot_config.keystore_token_specified) {
2N/A num_match_needed++;
2N/A }
2N/A
2N/A if (metaslot_config.keystore_slot_specified) {
2N/A num_match_needed++;
2N/A }
2N/A
2N/A if (num_match_needed == 0) {
2N/A goto skip_search;
2N/A }
2N/A
2N/A for (slot = 0; slot < num_slots; slot++) {
2N/A unsigned int num_matched = 0;
2N/A boolean_t have_tokeninfo = B_FALSE;
2N/A CK_SLOT_ID true_id, fw_st_id;
2N/A
2N/A fw_st_id = slots[slot].fw_st_id;
2N/A true_id = TRUEID(fw_st_id);
2N/A
2N/A (void) memset(&slotinfo, 0, sizeof (CK_SLOT_INFO));
2N/A rv = FUNCLIST(fw_st_id)->C_GetSlotInfo(true_id,
2N/A &slotinfo);
2N/A if (rv != CKR_OK)
2N/A continue;
2N/A
2N/A if (strncmp((char *)SOFT_SLOT_DESCRIPTION,
2N/A (char *)slotinfo.slotDescription,
2N/A SLOT_DESCRIPTION_SIZE) == 0) {
2N/A softtoken_slotnum = slot;
2N/A }
2N/A
2N/A if (metaslot_config.keystore_slot_specified) {
2N/A
2N/A unsigned char *slot;
2N/A size_t slot_str_len;
2N/A
2N/A rv = FUNCLIST(fw_st_id)->C_GetSlotInfo(true_id,
2N/A &slotinfo);
2N/A if (rv != CKR_OK)
2N/A continue;
2N/A
2N/A /*
2N/A * pad slot description from user/system configuration
2N/A * with spaces
2N/A */
2N/A slot = metaslot_config.keystore_slot;
2N/A slot_str_len = strlen((char *)slot);
2N/A (void) memset(slot + slot_str_len, ' ',
2N/A SLOT_DESCRIPTION_SIZE - slot_str_len);
2N/A
2N/A /*
2N/A * The PKCS#11 strings are not null-terminated, so,
2N/A * we just compare SLOT_DESCRIPTION_SIZE bytes
2N/A */
2N/A if (strncmp((char *)slot,
2N/A (char *)slotinfo.slotDescription,
2N/A SLOT_DESCRIPTION_SIZE) == 0) {
2N/A num_matched++;
2N/A }
2N/A }
2N/A
2N/A if (metaslot_config.keystore_token_specified) {
2N/A unsigned char *token;
2N/A size_t token_str_len;
2N/A
2N/A rv = FUNCLIST(fw_st_id)->C_GetTokenInfo(true_id,
2N/A &tokeninfo);
2N/A
2N/A if (rv != CKR_OK) {
2N/A continue;
2N/A }
2N/A
2N/A have_tokeninfo = B_TRUE;
2N/A
2N/A /*
2N/A * pad slot description from user/system configuration
2N/A * with spaces
2N/A */
2N/A token = metaslot_config.keystore_token;
2N/A token_str_len = strlen((char *)token);
2N/A (void) memset(token + token_str_len, ' ',
2N/A TOKEN_LABEL_SIZE - token_str_len);
2N/A
2N/A /*
2N/A * The PKCS#11 strings are not null-terminated.
2N/A * So, just compare TOKEN_LABEL_SIZE bytes
2N/A */
2N/A if (strncmp((char *)token, (char *)tokeninfo.label,
2N/A TOKEN_LABEL_SIZE) == 0) {
2N/A num_matched++;
2N/A }
2N/A }
2N/A
2N/A if (num_match_needed == num_matched) {
2N/A /* match is found */
2N/A
2N/A if (!have_tokeninfo) {
2N/A rv = FUNCLIST(fw_st_id)->C_GetTokenInfo(true_id,
2N/A &tokeninfo);
2N/A if (rv != CKR_OK) {
2N/A continue;
2N/A }
2N/A }
2N/A
2N/A
2N/A if (tokeninfo.flags & CKF_WRITE_PROTECTED) {
2N/A /*
2N/A * Currently this is the only time that
2N/A * the write_protected state is set, and
2N/A * it is never cleared. The token could
2N/A * clear (or set!) this flag later on.
2N/A * We might want to adjust the state
2N/A * of metaslot, but there's know way to know
2N/A * when a token changes this flag.
2N/A */
2N/A write_protected = B_TRUE;
2N/A }
2N/A
2N/A found = B_TRUE;
2N/A break;
2N/A }
2N/A }
2N/A
2N/Askip_search:
2N/A if (found) {
2N/A objtok_slotnum = slot;
2N/A } else {
2N/A /*
2N/A * if slot and/or token is not defined for the keystore,
2N/A * just use the first available slot as keystore
2N/A */
2N/A objtok_slotnum = 0;
2N/A }
2N/A slots[objtok_slotnum].session_pool.keep_one_alive = B_TRUE;
2N/A metaslot_keystore_slotid = slots[objtok_slotnum].fw_st_id;
2N/A}
2N/A
2N/A
2N/ACK_ULONG
2N/Aget_keystore_slotnum(void)
2N/A{
2N/A return (objtok_slotnum);
2N/A}
2N/A
2N/ACK_ULONG
2N/Aget_softtoken_slotnum(void)
2N/A{
2N/A return (softtoken_slotnum);
2N/A}
2N/A
2N/ACK_SLOT_ID
2N/Ameta_slotManager_get_framework_table_id(CK_ULONG slotnum)
2N/A{
2N/A /*
2N/A * This is only used internally, and so the slotnum should always
2N/A * be valid.
2N/A */
2N/A return (slots[slotnum].fw_st_id);
2N/A}
2N/A
2N/ACK_ULONG
2N/Ameta_slotManager_get_slotcount(void)
2N/A{
2N/A return (num_slots);
2N/A}
2N/A
2N/Aboolean_t
2N/Ameta_slotManager_token_write_protected(void)
2N/A{
2N/A return (write_protected);
2N/A}
2N/A
2N/A/*
2N/A * Find a session in the given list that matches the specified flags.
2N/A * If such a session is found, it will be removed from the list, and
2N/A * returned to the caller. If such a session is not found, will
2N/A * return NULL
2N/A */
2N/Astatic slot_session_t *
2N/Aget_session(slot_session_t **session_list, CK_FLAGS flags)
2N/A{
2N/A
2N/A slot_session_t *tmp_session;
2N/A
2N/A tmp_session = *session_list;
2N/A
2N/A while (tmp_session != NULL) {
2N/A if (tmp_session->session_flags == flags) {
2N/A break;
2N/A } else {
2N/A tmp_session = tmp_session->next;
2N/A }
2N/A
2N/A }
2N/A
2N/A if (tmp_session == NULL) {
2N/A /* no match */
2N/A return (NULL);
2N/A }
2N/A
2N/A /* Remove from list */
2N/A REMOVE_FROM_LIST(*session_list, tmp_session);
2N/A return (tmp_session);
2N/A}
2N/A
2N/A/*
2N/A * meta_get_slot_session
2N/A *
2N/A * Call to get a session with a specific slot/token.
2N/A *
2N/A * NOTE - We assume the slot allows an unlimited number of sessions. We
2N/A * could look at what's reported in the token info, but that information is
2N/A * not always set. It's also unclear when we should (A) wait for one to become
2N/A * available, (B) skip the slot for now or (C) return a fatal error. The
2N/A * extra complexity is not worth it.
2N/A *
2N/A */
2N/ACK_RV
2N/Ameta_get_slot_session(CK_ULONG slotnum, slot_session_t **session,
2N/A CK_FLAGS flags) {
2N/A session_pool_t *pool;
2N/A slot_session_t *new_session, *tmp_session;
2N/A CK_RV rv;
2N/A CK_SLOT_ID fw_st_id, true_id;
2N/A
2N/A if (slotnum >= num_slots) {
2N/A return (CKR_SLOT_ID_INVALID);
2N/A }
2N/A
2N/A pool = &slots[slotnum].session_pool;
2N/A
2N/A /*
2N/A * Try to reuse an existing session.
2N/A */
2N/A
2N/A (void) pthread_mutex_lock(&pool->list_lock);
2N/A
2N/A if (pool->idle_list_head != NULL) {
2N/A tmp_session = get_session(&(pool->idle_list_head), flags);
2N/A if (tmp_session != NULL) {
2N/A /* Add to active list */
2N/A INSERT_INTO_LIST(pool->active_list_head, tmp_session);
2N/A *session = tmp_session;
2N/A pool->num_idle_sessions--;
2N/A (void) pthread_mutex_unlock(&pool->list_lock);
2N/A return (CKR_OK);
2N/A }
2N/A }
2N/A
2N/A if (pool->persist_list_head != NULL) {
2N/A tmp_session = get_session(&(pool->persist_list_head), flags);
2N/A if (tmp_session != NULL) {
2N/A /* Add to active list */
2N/A INSERT_INTO_LIST(pool->active_list_head, tmp_session);
2N/A *session = tmp_session;
2N/A (void) pthread_mutex_unlock(&pool->list_lock);
2N/A return (CKR_OK);
2N/A }
2N/A }
2N/A (void) pthread_mutex_unlock(&pool->list_lock);
2N/A
2N/A fw_st_id = slots[slotnum].fw_st_id;
2N/A true_id = TRUEID(fw_st_id);
2N/A
2N/A new_session = calloc(1, sizeof (slot_session_t));
2N/A if (new_session == NULL) {
2N/A return (CKR_HOST_MEMORY);
2N/A }
2N/A
2N/A /* initialize slotsession */
2N/A new_session->slotnum = slotnum;
2N/A new_session->fw_st_id = fw_st_id;
2N/A new_session->object_list_head = NULL;
2N/A new_session->session_flags = flags;
2N/A (void) pthread_rwlock_init(&new_session->object_list_lock, NULL);
2N/A
2N/A rv = FUNCLIST(fw_st_id)->C_OpenSession(true_id, flags, NULL, NULL,
2N/A &new_session->hSession);
2N/A
2N/A if (rv == CKR_TOKEN_WRITE_PROTECTED) {
2N/A /* Retry with a RO session. */
2N/A new_session->session_flags &= ~CKF_SERIAL_SESSION;
2N/A rv = FUNCLIST(fw_st_id)->C_OpenSession(true_id,
2N/A new_session->session_flags, NULL, NULL,
2N/A &new_session->hSession);
2N/A }
2N/A
2N/A if (rv != CKR_OK) {
2N/A free(new_session);
2N/A return (CKR_FUNCTION_FAILED);
2N/A }
2N/A
2N/A /* Insert session into active list */
2N/A (void) pthread_mutex_lock(&pool->list_lock);
2N/A INSERT_INTO_LIST(pool->active_list_head, new_session);
2N/A (void) pthread_mutex_unlock(&pool->list_lock);
2N/A *session = new_session;
2N/A return (CKR_OK);
2N/A}
2N/A
2N/A
2N/A/*
2N/A * meta_release_slot_session
2N/A *
2N/A * Call to release a session obtained via meta_get_slot_session()
2N/A */
2N/Avoid
2N/Ameta_release_slot_session(slot_session_t *session) {
2N/A session_pool_t *pool;
2N/A boolean_t must_retain, can_close = B_FALSE;
2N/A boolean_t this_is_last_session = B_FALSE;
2N/A
2N/A pool = &slots[session->slotnum].session_pool;
2N/A
2N/A /* Note that the active_list must have >= 1 entry (this session) */
2N/A if (pool->persist_list_head == NULL &&
2N/A pool->idle_list_head == NULL &&
2N/A pool->active_list_head->next == NULL)
2N/A this_is_last_session = B_TRUE;
2N/A
2N/A /*
2N/A * If the session has session objects, we need to retain it. Also
2N/A * retain it if it's the only session holding login state (or handles
2N/A * to public token objects)
2N/A */
2N/A must_retain = session->object_list_head != NULL ||
2N/A (pool->keep_one_alive && this_is_last_session);
2N/A
2N/A if ((!must_retain) && (pool->num_idle_sessions > MAX_IDLE_SESSIONS)) {
2N/A can_close = B_TRUE;
2N/A }
2N/A
2N/A (void) pthread_mutex_lock(&pool->list_lock);
2N/A /* remove from active list */
2N/A REMOVE_FROM_LIST(pool->active_list_head, session);
2N/A
2N/A if (must_retain) {
2N/A /* insert into persist list */
2N/A INSERT_INTO_LIST(pool->persist_list_head, session);
2N/A (void) pthread_mutex_unlock(&pool->list_lock);
2N/A return;
2N/A } else if (!can_close) {
2N/A /* insert into idle list */
2N/A INSERT_INTO_LIST(pool->idle_list_head, session);
2N/A pool->num_idle_sessions++;
2N/A (void) pthread_mutex_unlock(&pool->list_lock);
2N/A return;
2N/A }
2N/A
2N/A (void) pthread_mutex_unlock(&pool->list_lock);
2N/A
2N/A (void) FUNCLIST(session->fw_st_id)->C_CloseSession(session->hSession);
2N/A
2N/A (void) pthread_rwlock_destroy(&session->object_list_lock);
2N/A free(session);
2N/A}
2N/A
2N/A/*
2N/A * Returns whether metaslot has directly logged in
2N/A */
2N/Aboolean_t
2N/Ametaslot_logged_in(void)
2N/A{
2N/A return (metaslotLoggedIn);
2N/A}
2N/A
2N/A/*
2N/A * Set or clear the logged-in flag
2N/A */
2N/Avoid
2N/Ametaslot_set_logged_in_flag(boolean_t value)
2N/A{
2N/A (void) pthread_mutex_lock(&metaslotLoggedIn_mutex);
2N/A metaslotLoggedIn = value;
2N/A (void) pthread_mutex_unlock(&metaslotLoggedIn_mutex);
2N/A}