#include <md5.h>
#include <pthread.h>
#include <syslog.h>
#include <stdlib.h>
#include <string.h>
#include <strings.h>
#include <sys/sha1.h>
#include <security/cryptoki.h>
#include "softGlobal.h"
#include "softSession.h"
#include "softObject.h"
#include "softOps.h"
#include "softKeystore.h"
#include "softKeystoreUtil.h"
CK_ULONG soft_session_cnt = 0;
CK_ULONG soft_session_rw_cnt = 0;
#define DIGEST_MECH_OK(_m_) ((_m_) == CKM_MD5 || (_m_) == CKM_SHA_1)
CK_RV
soft_delete_all_sessions(boolean_t force)
{
CK_RV rv = CKR_OK;
CK_RV rv1;
soft_session_t *session_p;
soft_session_t *session_p1;
(void) pthread_mutex_lock(&soft_sessionlist_mutex);
session_p = soft_session_list;
while (session_p) {
session_p1 = session_p->next;
rv1 = soft_delete_session(session_p, force, B_TRUE);
if (rv == CKR_OK) {
rv = rv1;
}
session_p = session_p1;
}
soft_session_list = NULL;
(void) pthread_mutex_unlock(&soft_sessionlist_mutex);
return (rv);
}
CK_RV
soft_add_session(CK_FLAGS flags, CK_VOID_PTR pApplication,
CK_NOTIFY notify, CK_ULONG *sessionhandle_p)
{
soft_session_t *new_sp = NULL;
new_sp = calloc(1, sizeof (soft_session_t));
if (new_sp == NULL) {
return (CKR_HOST_MEMORY);
}
new_sp->magic_marker = SOFTTOKEN_SESSION_MAGIC;
new_sp->pApplication = pApplication;
new_sp->Notify = notify;
new_sp->flags = flags;
new_sp->state = CKS_RO_PUBLIC_SESSION;
new_sp->object_list = NULL;
new_sp->ses_refcnt = 0;
new_sp->ses_close_sync = 0;
(void) pthread_mutex_lock(&soft_giant_mutex);
if (soft_slot.authenticated) {
(void) pthread_mutex_unlock(&soft_giant_mutex);
if (flags & CKF_RW_SESSION) {
new_sp->state = CKS_RW_USER_FUNCTIONS;
} else {
new_sp->state = CKS_RO_USER_FUNCTIONS;
}
} else {
(void) pthread_mutex_unlock(&soft_giant_mutex);
if (flags & CKF_RW_SESSION) {
new_sp->state = CKS_RW_PUBLIC_SESSION;
} else {
new_sp->state = CKS_RO_PUBLIC_SESSION;
}
}
if (pthread_mutex_init(&new_sp->session_mutex, NULL) != 0) {
free(new_sp);
return (CKR_CANT_LOCK);
}
(void) pthread_cond_init(&new_sp->ses_free_cond, NULL);
(void) pthread_mutex_lock(&soft_sessionlist_mutex);
do {
arc4random_buf(&new_sp->handle, sizeof (new_sp->handle));
if (new_sp->handle == CK_INVALID_HANDLE)
continue;
} while (avl_find(&soft_session_tree, new_sp, NULL) != NULL);
avl_add(&soft_session_tree, new_sp);
*sessionhandle_p = new_sp->handle;
if (soft_session_list == NULL) {
soft_session_list = new_sp;
new_sp->next = NULL;
new_sp->prev = NULL;
} else {
soft_session_list->prev = new_sp;
new_sp->next = soft_session_list;
new_sp->prev = NULL;
soft_session_list = new_sp;
}
++soft_session_cnt;
if (flags & CKF_RW_SESSION)
++soft_session_rw_cnt;
if (soft_session_cnt == 1)
soft_validate_token_objects(B_TRUE);
(void) pthread_mutex_unlock(&soft_sessionlist_mutex);
return (CKR_OK);
}
void
session_delay_free(soft_session_t *sp)
{
soft_session_t *tmp;
(void) pthread_mutex_lock(&ses_delay_freed.ses_to_be_free_mutex);
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_SES_TO_BE_FREED) {
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);
}
CK_RV
soft_delete_session(soft_session_t *session_p,
boolean_t force, boolean_t lock_held)
{
if (!lock_held) {
(void) pthread_mutex_lock(&soft_sessionlist_mutex);
}
if (soft_session_list == session_p) {
if (session_p->next) {
soft_session_list = session_p->next;
session_p->next->prev = NULL;
} else {
soft_session_list = NULL;
}
} else {
if (session_p->next) {
session_p->prev->next = session_p->next;
session_p->next->prev = session_p->prev;
} else {
session_p->prev->next = NULL;
}
}
avl_remove(&soft_session_tree, session_p);
--soft_session_cnt;
if (session_p->flags & CKF_RW_SESSION)
--soft_session_rw_cnt;
if (!lock_held) {
(void) pthread_mutex_unlock(&soft_sessionlist_mutex);
}
(void) pthread_mutex_lock(&session_p->session_mutex);
if (session_p->magic_marker != SOFTTOKEN_SESSION_MAGIC) {
(void) pthread_mutex_unlock(&session_p->session_mutex);
return (CKR_OK);
}
if (force)
session_p->ses_refcnt = 0;
while (session_p->ses_refcnt != 0) {
session_p->ses_close_sync |= SESSION_REFCNT_WAITING;
(void) pthread_cond_wait(&session_p->ses_free_cond,
&session_p->session_mutex);
}
session_p->ses_close_sync &= ~SESSION_REFCNT_WAITING;
soft_delete_all_objects_in_session(session_p, force);
session_p->magic_marker = 0;
(void) pthread_cond_destroy(&session_p->ses_free_cond);
if (session_p->digest.context != NULL)
free(session_p->digest.context);
if (session_p->encrypt.context != NULL)
soft_crypt_cleanup(session_p, B_TRUE, B_TRUE);
if (session_p->decrypt.context != NULL)
soft_crypt_cleanup(session_p, B_FALSE, B_TRUE);
if (session_p->sign.context != NULL)
free(session_p->sign.context);
if (session_p->verify.context != NULL)
free(session_p->verify.context);
if (session_p->find_objects.context != NULL) {
find_context_t *fcontext;
fcontext = (find_context_t *)session_p->find_objects.context;
free(fcontext->objs_found);
free(fcontext);
}
session_p->ses_close_sync &= ~SESSION_IS_CLOSING;
(void) pthread_mutex_unlock(&session_p->session_mutex);
(void) pthread_mutex_destroy(&session_p->session_mutex);
session_delay_free(session_p);
return (CKR_OK);
}
CK_RV
handle2session(CK_SESSION_HANDLE hSession, soft_session_t **session_p)
{
soft_session_t *sp;
soft_session_t node;
if (all_sessions_closing) {
return (CKR_SESSION_CLOSED);
}
(void) memset(&node, 0, sizeof (node));
node.handle = hSession;
(void) pthread_mutex_lock(&soft_sessionlist_mutex);
sp = avl_find(&soft_session_tree, &node, NULL);
if ((sp == NULL) ||
(sp->magic_marker != SOFTTOKEN_SESSION_MAGIC)) {
(void) pthread_mutex_unlock(&soft_sessionlist_mutex);
return (CKR_SESSION_HANDLE_INVALID);
}
(void) pthread_mutex_lock(&sp->session_mutex);
(void) pthread_mutex_unlock(&soft_sessionlist_mutex);
if (sp->ses_close_sync & SESSION_IS_CLOSING) {
(void) pthread_mutex_unlock(&sp->session_mutex);
return (CKR_SESSION_CLOSED);
}
sp->ses_refcnt++;
(void) pthread_mutex_unlock(&sp->session_mutex);
*session_p = sp;
return (CKR_OK);
}
CK_RV
soft_get_operationstate(soft_session_t *session_p, CK_BYTE_PTR pOperationState,
CK_ULONG_PTR pulOperationStateLen)
{
internal_op_state_t *p_op_state;
CK_ULONG op_data_len = 0;
CK_RV rv = CKR_OK;
if (pulOperationStateLen == NULL)
return (CKR_ARGUMENTS_BAD);
(void) pthread_mutex_lock(&session_p->session_mutex);
if (session_p->encrypt.flags & CRYPTO_OPERATION_ACTIVE) {
rv = CKR_STATE_UNSAVEABLE;
goto unlock_session;
}
if (session_p->decrypt.flags & CRYPTO_OPERATION_ACTIVE) {
rv = CKR_STATE_UNSAVEABLE;
goto unlock_session;
}
if (session_p->sign.flags & CRYPTO_OPERATION_ACTIVE) {
rv = CKR_STATE_UNSAVEABLE;
goto unlock_session;
}
if (session_p->verify.flags & CRYPTO_OPERATION_ACTIVE) {
rv = CKR_STATE_UNSAVEABLE;
goto unlock_session;
}
if (session_p->digest.flags & CRYPTO_OPERATION_ACTIVE) {
op_data_len = sizeof (internal_op_state_t) +
sizeof (crypto_active_op_t);
switch (session_p->digest.mech.mechanism) {
case CKM_MD5:
op_data_len += sizeof (MD5_CTX);
break;
case CKM_SHA_1:
op_data_len += sizeof (SHA1_CTX);
break;
default:
rv = CKR_STATE_UNSAVEABLE;
goto unlock_session;
}
if (pOperationState == NULL_PTR) {
*pulOperationStateLen = op_data_len;
goto unlock_session;
} else {
if (*pulOperationStateLen < op_data_len) {
*pulOperationStateLen = op_data_len;
rv = CKR_BUFFER_TOO_SMALL;
goto unlock_session;
}
}
p_op_state = (internal_op_state_t *)pOperationState;
p_op_state->op_len = op_data_len;
p_op_state->op_active = DIGEST_OP;
p_op_state->op_session_state = session_p->state;
(void) memcpy((CK_BYTE *)pOperationState +
sizeof (internal_op_state_t),
&session_p->digest,
sizeof (crypto_active_op_t));
switch (session_p->digest.mech.mechanism) {
case CKM_MD5:
(void) memcpy((CK_BYTE *)pOperationState +
sizeof (internal_op_state_t) +
sizeof (crypto_active_op_t),
session_p->digest.context,
sizeof (MD5_CTX));
break;
case CKM_SHA_1:
(void) memcpy((CK_BYTE *)pOperationState +
sizeof (internal_op_state_t) +
sizeof (crypto_active_op_t),
session_p->digest.context,
sizeof (SHA1_CTX));
break;
default:
rv = CKR_STATE_UNSAVEABLE;
}
} else {
rv = CKR_OPERATION_NOT_INITIALIZED;
goto unlock_session;
}
*pulOperationStateLen = op_data_len;
unlock_session:
(void) pthread_mutex_unlock(&session_p->session_mutex);
return (rv);
}
static CK_BYTE_PTR alloc_digest(CK_ULONG mech)
{
CK_BYTE_PTR ret_val;
switch (mech) {
case CKM_MD5:
ret_val = (CK_BYTE_PTR) malloc(sizeof (MD5_CTX));
break;
case CKM_SHA_1:
ret_val = (CK_BYTE_PTR) malloc(sizeof (SHA1_CTX));
break;
default: ret_val = NULL;
}
return (ret_val);
}
CK_RV
soft_set_operationstate(soft_session_t *session_p, CK_BYTE_PTR pOperationState,
CK_ULONG ulOperationStateLen, CK_OBJECT_HANDLE hEncryptionKey,
CK_OBJECT_HANDLE hAuthenticationKey)
{
CK_RV rv = CKR_OK;
internal_op_state_t *p_op_state;
crypto_active_op_t *p_active_op;
CK_ULONG offset = 0;
CK_ULONG mech;
void *free_it = NULL;
p_op_state = (internal_op_state_t *)pOperationState;
if (p_op_state->op_len != ulOperationStateLen) {
return (CKR_SAVED_STATE_INVALID);
}
if (p_op_state->op_active != DIGEST_OP)
return (CKR_SAVED_STATE_INVALID);
if ((hAuthenticationKey != 0) || (hEncryptionKey != 0)) {
return (CKR_KEY_NOT_NEEDED);
}
offset = sizeof (internal_op_state_t);
p_active_op = (crypto_active_op_t *)(pOperationState + offset);
offset += sizeof (crypto_active_op_t);
mech = p_active_op->mech.mechanism;
if (!DIGEST_MECH_OK(mech)) {
return (CKR_SAVED_STATE_INVALID);
}
(void) pthread_mutex_lock(&session_p->session_mutex);
if (session_p->state != p_op_state->op_session_state) {
rv = CKR_SAVED_STATE_INVALID;
goto unlock_session;
}
if (session_p->digest.context &&
(session_p->digest.mech.mechanism != mech)) {
free_it = session_p->digest.context;
session_p->digest.context = NULL;
}
if (session_p->digest.context == NULL) {
session_p->digest.context = alloc_digest(mech);
if (session_p->digest.context == NULL) {
session_p->digest.context = free_it;
free_it = NULL;
rv = CKR_HOST_MEMORY;
goto unlock_session;
}
}
session_p->digest.mech.mechanism = mech;
session_p->digest.flags = p_active_op->flags;
switch (mech) {
case CKM_MD5:
(void) memcpy((CK_BYTE *)session_p->digest.context,
(CK_BYTE *)pOperationState + offset,
sizeof (MD5_CTX));
break;
case CKM_SHA_1:
(void) memcpy((CK_BYTE *)session_p->digest.context,
(CK_BYTE *)pOperationState + offset,
sizeof (SHA1_CTX));
break;
default:
rv = CKR_SAVED_STATE_INVALID;
}
unlock_session:
(void) pthread_mutex_unlock(&session_p->session_mutex);
if (free_it != NULL)
free(free_it);
return (rv);
}
CK_RV
soft_login(CK_UTF8CHAR_PTR pPin, CK_ULONG ulPinLen)
{
return (soft_verify_pin(pPin, ulPinLen));
}
void
soft_logout(void)
{
soft_delete_all_in_core_token_objects(PRIVATE_TOKEN);
return;
}
void
soft_acquire_all_session_mutexes(soft_session_t *session_p)
{
while (session_p) {
soft_object_t *object_p;
(void) pthread_mutex_lock(&session_p->session_mutex);
object_p = session_p->object_list;
while (object_p) {
(void) pthread_mutex_lock(&object_p->object_mutex);
object_p = object_p->next;
}
session_p = session_p->next;
}
}
void
soft_release_all_session_mutexes(soft_session_t *session_p)
{
while (session_p) {
soft_object_t *object_p = session_p->object_list;
while (object_p) {
(void) pthread_mutex_unlock(&object_p->object_mutex);
object_p = object_p->next;
}
(void) pthread_mutex_unlock(&session_p->session_mutex);
session_p = session_p->next;
}
}