#include <string.h>
#include <cryptoutil.h>
#include "metaGlobal.h"
#include "pkcs11Session.h"
#include "pkcs11Global.h"
#define MAX_IDLE_SESSIONS 100
slot_data_t *slots;
CK_SLOT_ID metaslot_keystore_slotid;
static CK_ULONG num_slots;
static CK_ULONG objtok_slotnum;
static CK_ULONG softtoken_slotnum;
static boolean_t write_protected;
static pthread_mutex_t metaslotLoggedIn_mutex = PTHREAD_MUTEX_INITIALIZER;
static boolean_t metaslotLoggedIn;
CK_RV
meta_slotManager_initialize() {
CK_ULONG slot_count = 0;
CK_RV rv;
CK_SLOT_ID i;
write_protected = B_FALSE;
metaslotLoggedIn = B_FALSE;
for (i = (slottable->st_first) + 1; i <= slottable->st_last; i++) {
slot_count++;
}
if (slot_count < 1) {
rv = CKR_FUNCTION_FAILED;
goto clean_exit;
}
slots = calloc(slot_count, sizeof (slot_data_t));
if (slots == NULL) {
rv = CKR_HOST_MEMORY;
goto clean_exit;
}
for (i = 0; i < slot_count; i++) {
slots[i].fw_st_id = i + 1;
(void) pthread_rwlock_init(
&(slots[i].tokenobject_list_lock), NULL);
}
num_slots = slot_count;
return (CKR_OK);
clean_exit:
if (slots) {
free(slots);
slots = NULL;
}
num_slots = 0;
return (rv);
}
void
meta_slotManager_finalize() {
CK_ULONG slot;
if (slots == NULL)
return;
for (slot = 0; slot < num_slots; slot++) {
slot_session_t *session, *next_session;
session = slots[slot].session_pool.idle_list_head;
while (session) {
next_session = session->next;
(void) FUNCLIST(session->fw_st_id)->C_CloseSession(
session->hSession);
(void) pthread_rwlock_destroy(
&session->object_list_lock);
free(session);
session = next_session;
}
session = slots[slot].session_pool.persist_list_head;
while (session) {
next_session = session->next;
(void) FUNCLIST(session->fw_st_id)->C_CloseSession(
session->hSession);
(void) pthread_rwlock_destroy(
&session->object_list_lock);
free(session);
session = next_session;
}
(void) pthread_rwlock_destroy(
&slots[slot].tokenobject_list_lock);
}
free(slots);
slots = NULL;
num_slots = 0;
}
void
meta_slotManager_find_object_token() {
CK_ULONG slot;
boolean_t found = B_FALSE;
CK_RV rv;
unsigned int num_match_needed = 0;
CK_SLOT_INFO slotinfo;
CK_TOKEN_INFO tokeninfo;
if (metaslot_config.keystore_token_specified) {
num_match_needed++;
}
if (metaslot_config.keystore_slot_specified) {
num_match_needed++;
}
if (num_match_needed == 0) {
goto skip_search;
}
for (slot = 0; slot < num_slots; slot++) {
unsigned int num_matched = 0;
boolean_t have_tokeninfo = B_FALSE;
CK_SLOT_ID true_id, fw_st_id;
fw_st_id = slots[slot].fw_st_id;
true_id = TRUEID(fw_st_id);
(void) memset(&slotinfo, 0, sizeof (CK_SLOT_INFO));
rv = FUNCLIST(fw_st_id)->C_GetSlotInfo(true_id,
&slotinfo);
if (rv != CKR_OK)
continue;
if (strncmp((char *)SOFT_SLOT_DESCRIPTION,
(char *)slotinfo.slotDescription,
SLOT_DESCRIPTION_SIZE) == 0) {
softtoken_slotnum = slot;
}
if (metaslot_config.keystore_slot_specified) {
unsigned char *slot;
size_t slot_str_len;
rv = FUNCLIST(fw_st_id)->C_GetSlotInfo(true_id,
&slotinfo);
if (rv != CKR_OK)
continue;
slot = metaslot_config.keystore_slot;
slot_str_len = strlen((char *)slot);
(void) memset(slot + slot_str_len, ' ',
SLOT_DESCRIPTION_SIZE - slot_str_len);
if (strncmp((char *)slot,
(char *)slotinfo.slotDescription,
SLOT_DESCRIPTION_SIZE) == 0) {
num_matched++;
}
}
if (metaslot_config.keystore_token_specified) {
unsigned char *token;
size_t token_str_len;
rv = FUNCLIST(fw_st_id)->C_GetTokenInfo(true_id,
&tokeninfo);
if (rv != CKR_OK) {
continue;
}
have_tokeninfo = B_TRUE;
token = metaslot_config.keystore_token;
token_str_len = strlen((char *)token);
(void) memset(token + token_str_len, ' ',
TOKEN_LABEL_SIZE - token_str_len);
if (strncmp((char *)token, (char *)tokeninfo.label,
TOKEN_LABEL_SIZE) == 0) {
num_matched++;
}
}
if (num_match_needed == num_matched) {
if (!have_tokeninfo) {
rv = FUNCLIST(fw_st_id)->C_GetTokenInfo(true_id,
&tokeninfo);
if (rv != CKR_OK) {
continue;
}
}
if (tokeninfo.flags & CKF_WRITE_PROTECTED) {
write_protected = B_TRUE;
}
found = B_TRUE;
break;
}
}
skip_search:
if (found) {
objtok_slotnum = slot;
} else {
objtok_slotnum = 0;
}
slots[objtok_slotnum].session_pool.keep_one_alive = B_TRUE;
metaslot_keystore_slotid = slots[objtok_slotnum].fw_st_id;
}
CK_ULONG
get_keystore_slotnum()
{
return (objtok_slotnum);
}
CK_ULONG
get_softtoken_slotnum()
{
return (softtoken_slotnum);
}
CK_SLOT_ID
meta_slotManager_get_framework_table_id(CK_ULONG slotnum)
{
return (slots[slotnum].fw_st_id);
}
CK_ULONG
meta_slotManager_get_slotcount()
{
return (num_slots);
}
boolean_t
meta_slotManager_token_write_protected()
{
return (write_protected);
}
static slot_session_t *
get_session(slot_session_t **session_list, CK_FLAGS flags)
{
slot_session_t *tmp_session;
tmp_session = *session_list;
while (tmp_session != NULL) {
if (tmp_session->session_flags == flags) {
break;
} else {
tmp_session = tmp_session->next;
}
}
if (tmp_session == NULL) {
return (NULL);
}
REMOVE_FROM_LIST(*session_list, tmp_session);
return (tmp_session);
}
CK_RV
meta_get_slot_session(CK_ULONG slotnum, slot_session_t **session,
CK_FLAGS flags) {
session_pool_t *pool;
slot_session_t *new_session, *tmp_session;
CK_RV rv;
CK_SLOT_ID fw_st_id, true_id;
if (slotnum >= num_slots) {
return (CKR_SLOT_ID_INVALID);
}
pool = &slots[slotnum].session_pool;
(void) pthread_mutex_lock(&pool->list_lock);
if (pool->idle_list_head != NULL) {
tmp_session = get_session(&(pool->idle_list_head), flags);
if (tmp_session != NULL) {
INSERT_INTO_LIST(pool->active_list_head, tmp_session);
*session = tmp_session;
pool->num_idle_sessions--;
(void) pthread_mutex_unlock(&pool->list_lock);
return (CKR_OK);
}
}
if (pool->persist_list_head != NULL) {
tmp_session = get_session(&(pool->persist_list_head), flags);
if (tmp_session != NULL) {
INSERT_INTO_LIST(pool->active_list_head, tmp_session);
*session = tmp_session;
(void) pthread_mutex_unlock(&pool->list_lock);
return (CKR_OK);
}
}
(void) pthread_mutex_unlock(&pool->list_lock);
fw_st_id = slots[slotnum].fw_st_id;
true_id = TRUEID(fw_st_id);
new_session = calloc(1, sizeof (slot_session_t));
if (new_session == NULL) {
return (CKR_HOST_MEMORY);
}
new_session->slotnum = slotnum;
new_session->fw_st_id = fw_st_id;
new_session->object_list_head = NULL;
new_session->session_flags = flags;
(void) pthread_rwlock_init(&new_session->object_list_lock, NULL);
rv = FUNCLIST(fw_st_id)->C_OpenSession(true_id, flags, NULL, NULL,
&new_session->hSession);
if (rv == CKR_TOKEN_WRITE_PROTECTED) {
new_session->session_flags &= ~CKF_SERIAL_SESSION;
rv = FUNCLIST(fw_st_id)->C_OpenSession(true_id,
new_session->session_flags, NULL, NULL,
&new_session->hSession);
}
if (rv != CKR_OK) {
free(new_session);
return (CKR_FUNCTION_FAILED);
}
(void) pthread_mutex_lock(&pool->list_lock);
INSERT_INTO_LIST(pool->active_list_head, new_session);
(void) pthread_mutex_unlock(&pool->list_lock);
*session = new_session;
return (CKR_OK);
}
void
meta_release_slot_session(slot_session_t *session) {
session_pool_t *pool;
boolean_t must_retain, can_close = B_FALSE;
boolean_t this_is_last_session = B_FALSE;
pool = &slots[session->slotnum].session_pool;
if (pool->persist_list_head == NULL &&
pool->idle_list_head == NULL &&
pool->active_list_head->next == NULL)
this_is_last_session = B_TRUE;
must_retain = session->object_list_head != NULL ||
(pool->keep_one_alive && this_is_last_session);
if ((!must_retain) && (pool->num_idle_sessions > MAX_IDLE_SESSIONS)) {
can_close = B_TRUE;
}
(void) pthread_mutex_lock(&pool->list_lock);
REMOVE_FROM_LIST(pool->active_list_head, session);
if (must_retain) {
INSERT_INTO_LIST(pool->persist_list_head, session);
(void) pthread_mutex_unlock(&pool->list_lock);
return;
} else if (!can_close) {
INSERT_INTO_LIST(pool->idle_list_head, session);
pool->num_idle_sessions++;
(void) pthread_mutex_unlock(&pool->list_lock);
return;
}
(void) pthread_mutex_unlock(&pool->list_lock);
(void) FUNCLIST(session->fw_st_id)->C_CloseSession(session->hSession);
(void) pthread_rwlock_destroy(&session->object_list_lock);
free(session);
}
boolean_t
metaslot_logged_in()
{
return (metaslotLoggedIn);
}
void
metaslot_set_logged_in_flag(boolean_t value)
{
(void) pthread_mutex_lock(&metaslotLoggedIn_mutex);
metaslotLoggedIn = value;
(void) pthread_mutex_unlock(&metaslotLoggedIn_mutex);
}