/*
* COPYRIGHT (c) International Business Machines Corp. 2001-2017
*
* This program is provided under the terms of the Common Public License,
* version 1.0 (CPL-1.0). Any use, reproduction or distribution for this
* software constitutes recipient's acceptance of CPL-1.0 terms which can be
* found in the file LICENSE file or at
* https://opensource.org/licenses/cpl1.0.php
*/
// File: session.c
//
// Session manager related functions
//
#include <stdlib.h>
#include <string.h> // for memcmp() et al
#include <pthread.h>
#include "pkcs11types.h"
#include "local_types.h"
#include "defs.h"
#include "host_defs.h"
#include "h_extern.h"
#include "tok_spec_struct.h"
#include "trace.h"
// session_mgr_find()
//
// search for the specified session. returning a pointer to the session
// might be dangerous, but performs well.
//
// The returned session must be put back (using bt_put_node_value()) by the
// caller to decrease the reference count!
//
// Returns: SESSION * or NULL
//
SESSION *session_mgr_find(STDLL_TokData_t *tokdata, CK_SESSION_HANDLE handle)
{
SESSION *result = NULL;
if (!handle) {
return NULL;
}
result = bt_get_node_value(&tokdata->sess_btree, handle);
return result;
}
void session_mgr_put(STDLL_TokData_t *tokdata, SESSION *session)
{
bt_put_node_value(&tokdata->sess_btree, session);
}
// session_mgr_new()
//
// creates a new session structure and adds it to the process's list
// of sessions
//
// Args: CK_ULONG flags : session flags (INPUT)
// SESSION ** sess : new session pointer (OUTPUT)
//
// Returns: CK_RV
//
CK_RV session_mgr_new(STDLL_TokData_t *tokdata, CK_ULONG flags,
CK_SLOT_ID slot_id, CK_SESSION_HANDLE_PTR phSession)
{
SESSION *new_session = NULL;
CK_BBOOL user_session = FALSE;
CK_BBOOL so_session = FALSE;
CK_RV rc = CKR_OK;
new_session = (SESSION *) malloc(sizeof(SESSION));
if (!new_session) {
TRACE_ERROR("%s\n", ock_err(ERR_HOST_MEMORY));
rc = CKR_HOST_MEMORY;
goto done;
}
memset(new_session, 0x0, sizeof(SESSION));
// find an unused session handle. session handles will wrap automatically...
//
new_session->session_info.slotID = slot_id;
new_session->session_info.flags = flags;
new_session->session_info.ulDeviceError = 0;
// determine the login/logout status of the new session. PKCS 11 requires
// that all sessions belonging to a process have the same login/logout
// status
//
so_session = session_mgr_so_session_exists(tokdata);
user_session = session_mgr_user_session_exists(tokdata);
if (pthread_rwlock_wrlock(&tokdata->sess_list_rwlock)) {
TRACE_ERROR("Write Lock failed.\n");
rc = CKR_CANT_LOCK;
goto done;
}
// we don't have to worry about having a user and SO session at the same
// time. that is prevented in the login routine
//
if (user_session) {
if (new_session->session_info.flags & CKF_RW_SESSION) {
new_session->session_info.state = CKS_RW_USER_FUNCTIONS;
} else {
new_session->session_info.state = CKS_RO_USER_FUNCTIONS;
tokdata->ro_session_count++;
}
} else if (so_session) {
new_session->session_info.state = CKS_RW_SO_FUNCTIONS;
} else {
if (new_session->session_info.flags & CKF_RW_SESSION) {
new_session->session_info.state = CKS_RW_PUBLIC_SESSION;
} else {
new_session->session_info.state = CKS_RO_PUBLIC_SESSION;
tokdata->ro_session_count++;
}
}
pthread_rwlock_unlock(&tokdata->sess_list_rwlock);
*phSession = bt_node_add(&tokdata->sess_btree, new_session);
if (*phSession == 0) {
rc = CKR_HOST_MEMORY;
/* new_session will be free'd below */
}
done:
if (rc != CKR_OK && new_session != NULL) {
TRACE_ERROR("Failed to add session to the btree.\n");
free(new_session);
}
return rc;
}
// session_mgr_so_session_exists()
//
// determines whether a RW_SO session exists for the specified process
//
// Returns: TRUE or FALSE
//
CK_BBOOL session_mgr_so_session_exists(STDLL_TokData_t *tokdata)
{
CK_BBOOL result;
/* we must acquire sess_list_rwlock in order to inspect
* global_login_state */
if (pthread_rwlock_rdlock(&tokdata->sess_list_rwlock)) {
TRACE_ERROR("Read Lock failed.\n");
return FALSE;
}
result = (tokdata->global_login_state == CKS_RW_SO_FUNCTIONS);
pthread_rwlock_unlock(&tokdata->sess_list_rwlock);
return result;
}
// session_mgr_user_session_exists()
//
// determines whether a USER session exists for the specified process
//
// Returns: TRUE or FALSE
//
CK_BBOOL session_mgr_user_session_exists(STDLL_TokData_t *tokdata)
{
CK_BBOOL result;
/* we must acquire sess_list_rwlock in order to inspect
* glogal_login_state */
if (pthread_rwlock_rdlock(&tokdata->sess_list_rwlock)) {
TRACE_ERROR("Read Lock failed.\n");
return FALSE;
}
result = ((tokdata->global_login_state == CKS_RO_USER_FUNCTIONS) ||
(tokdata->global_login_state == CKS_RW_USER_FUNCTIONS));
pthread_rwlock_unlock(&tokdata->sess_list_rwlock);
return result;
}
// session_mgr_public_session_exists()
//
// determines whether a PUBLIC session exists for the specified process
//
// Returns: TRUE or FALSE
//
CK_BBOOL session_mgr_public_session_exists(STDLL_TokData_t *tokdata)
{
CK_BBOOL result;
/* we must acquire sess_list_rwlock in order to inspect
* global_login_state */
if (pthread_rwlock_rdlock(&tokdata->sess_list_rwlock)) {
TRACE_ERROR("Read Lock failed.\n");
return FALSE;
}
result = ((tokdata->global_login_state == CKS_RO_PUBLIC_SESSION) ||
(tokdata->global_login_state == CKS_RW_PUBLIC_SESSION));
pthread_rwlock_unlock(&tokdata->sess_list_rwlock);
return result;
}
// session_mgr_readonly_exists()
//
// determines whether the specified process owns any read-only sessions. this is
// useful because the SO cannot log in if a read-only session exists.
//
CK_BBOOL session_mgr_readonly_session_exists(STDLL_TokData_t *tokdata)
{
CK_BBOOL result;
/* we must acquire sess_list_rwlock in order to inspect ro_session_count */
if (pthread_rwlock_rdlock(&tokdata->sess_list_rwlock)) {
TRACE_ERROR("Read Lock failed.\n");
return FALSE;
}
result = (tokdata->ro_session_count > 0);
pthread_rwlock_unlock(&tokdata->sess_list_rwlock);
return result;
}
// session_mgr_close_session()
//
// removes the specified session from the process' session list
//
// Args: PROCESS * proc : parent process
// SESSION * session : session to remove
//
// Returns: TRUE on success else FALSE
//
CK_RV session_mgr_close_session(STDLL_TokData_t *tokdata,
CK_SESSION_HANDLE handle)
{
SESSION *sess;
CK_RV rc = CKR_OK;
sess = bt_get_node_value(&tokdata->sess_btree, handle);
if (!sess) {
TRACE_ERROR("%s\n", ock_err(ERR_SESSION_HANDLE_INVALID));
return CKR_SESSION_HANDLE_INVALID;
}
if (pthread_rwlock_wrlock(&tokdata->sess_list_rwlock)) {
TRACE_ERROR("Write Lock failed.\n");
bt_put_node_value(&tokdata->sess_btree, sess);
sess = NULL;
return CKR_CANT_LOCK;
}
object_mgr_purge_session_objects(tokdata, sess, ALL);
if ((sess->session_info.state == CKS_RO_PUBLIC_SESSION) ||
(sess->session_info.state == CKS_RO_USER_FUNCTIONS)) {
tokdata->ro_session_count--;
}
// Make sure this address is now invalid
sess->handle = CK_INVALID_HANDLE;
if (sess->find_list)
free(sess->find_list);
if (sess->encr_ctx.context)
free(sess->encr_ctx.context);
if (sess->encr_ctx.mech.pParameter)
free(sess->encr_ctx.mech.pParameter);
if (sess->decr_ctx.context)
free(sess->decr_ctx.context);
if (sess->decr_ctx.mech.pParameter)
free(sess->decr_ctx.mech.pParameter);
if (sess->digest_ctx.context)
free(sess->digest_ctx.context);
if (sess->digest_ctx.mech.pParameter)
free(sess->digest_ctx.mech.pParameter);
if (sess->sign_ctx.context)
free(sess->sign_ctx.context);
if (sess->sign_ctx.mech.pParameter)
free(sess->sign_ctx.mech.pParameter);
if (sess->verify_ctx.context)
free(sess->verify_ctx.context);
if (sess->verify_ctx.mech.pParameter)
free(sess->verify_ctx.mech.pParameter);
bt_put_node_value(&tokdata->sess_btree, sess);
sess = NULL;
bt_node_free(&tokdata->sess_btree, handle, TRUE);
// XXX XXX Not having this is a problem
// for IHS. The spec states that there is an implicit logout
// when the last session is closed. Cannonicaly this is what other
// implementaitons do. however on linux for some reason IHS can't seem
// to keep the session open, which means that they go through the login
// path EVERY time, which of course causes a reload of the private
// objects EVERY time. If we are logged out, we MUST purge the private
// objects from this process..
//
if (bt_is_empty(&tokdata->sess_btree)) {
// SAB XXX if all sessions are closed. Is this effectivly logging out
if (token_specific.t_logout) {
rc = token_specific.t_logout(tokdata);
}
object_mgr_purge_private_token_objects(tokdata);
tokdata->global_login_state = CKS_RO_PUBLIC_SESSION;
// The objects really need to be purged .. but this impacts the
// performance under linux. So we need to make sure that the
// login state is valid. I don't really like this.
object_mgr_purge_map(tokdata, (SESSION *) 0xFFFF, PRIVATE);
}
pthread_rwlock_unlock(&tokdata->sess_list_rwlock);
return rc;
}
/* session_free
*
* Callback used to free an individual SESSION object
*/
void session_free(STDLL_TokData_t *tokdata, void *node_value,
unsigned long node_idx, void *p3)
{
SESSION *sess = (SESSION *) node_value;
UNUSED(p3);
object_mgr_purge_session_objects(tokdata, sess, ALL);
sess->handle = CK_INVALID_HANDLE;
if (sess->find_list)
free(sess->find_list);
if (sess->encr_ctx.context)
free(sess->encr_ctx.context);
if (sess->encr_ctx.mech.pParameter)
free(sess->encr_ctx.mech.pParameter);
if (sess->decr_ctx.context)
free(sess->decr_ctx.context);
if (sess->decr_ctx.mech.pParameter)
free(sess->decr_ctx.mech.pParameter);
if (sess->digest_ctx.context)
free(sess->digest_ctx.context);
if (sess->digest_ctx.mech.pParameter)
free(sess->digest_ctx.mech.pParameter);
if (sess->sign_ctx.context)
free(sess->sign_ctx.context);
if (sess->sign_ctx.mech.pParameter)
free(sess->sign_ctx.mech.pParameter);
if (sess->verify_ctx.context)
free(sess->verify_ctx.context);
if (sess->verify_ctx.mech.pParameter)
free(sess->verify_ctx.mech.pParameter);
/* NB: any access to sess or @node_value after this returns will segfault */
bt_node_free(&tokdata->sess_btree, node_idx, TRUE);
}
// session_mgr_close_all_sessions()
//
// removes all sessions from the specified process.
// If tokdata is not NULL, then only sessions for that token instance are
// removed.
//
CK_RV session_mgr_close_all_sessions(STDLL_TokData_t *tokdata)
{
bt_for_each_node(tokdata, &tokdata->sess_btree, session_free, NULL);
if (pthread_rwlock_wrlock(&tokdata->sess_list_rwlock)) {
TRACE_ERROR("Write Lock failed.\n");
return CKR_CANT_LOCK;
}
tokdata->global_login_state = CKS_RO_PUBLIC_SESSION;
tokdata->ro_session_count = 0;
pthread_rwlock_unlock(&tokdata->sess_list_rwlock);
return CKR_OK;
}
/* session_login
*
* Callback used to update a SESSION object's login state to logged in based on
* user type
*/
void session_login(STDLL_TokData_t *tokdata, void *node_value,
unsigned long node_idx, void *p3)
{
SESSION *s = (SESSION *) node_value;
CK_USER_TYPE user_type = *(CK_USER_TYPE *) p3;
UNUSED(tokdata);
UNUSED(node_idx);
if (s->session_info.flags & CKF_RW_SESSION) {
if (user_type == CKU_USER)
s->session_info.state = CKS_RW_USER_FUNCTIONS;
else
s->session_info.state = CKS_RW_SO_FUNCTIONS;
} else {
if (user_type == CKU_USER)
s->session_info.state = CKS_RO_USER_FUNCTIONS;
}
tokdata->global_login_state = s->session_info.state; // SAB
}
// session_mgr_login_all()
//
// changes the login status of all sessions in the token
//
// Arg: CK_USER_TYPE user_type : USER or SO
//
CK_RV session_mgr_login_all(STDLL_TokData_t *tokdata, CK_USER_TYPE user_type)
{
if (pthread_rwlock_wrlock(&tokdata->sess_list_rwlock)) {
TRACE_ERROR("Write Lock failed.\n");
return CKR_CANT_LOCK;
}
bt_for_each_node(tokdata, &tokdata->sess_btree, session_login,
(void *)&user_type);
pthread_rwlock_unlock(&tokdata->sess_list_rwlock);
return CKR_OK;
}
/* session_logout
*
* Callback used to update a SESSION object's login state to be logged out
*/
void session_logout(STDLL_TokData_t *tokdata, void *node_value,
unsigned long node_idx, void *p3)
{
SESSION *s = (SESSION *) node_value;
UNUSED(node_idx);
UNUSED(p3);
// all sessions get logged out so destroy any private objects
// public objects are left alone
//
object_mgr_purge_session_objects(tokdata, s, PRIVATE);
if (s->session_info.flags & CKF_RW_SESSION)
s->session_info.state = CKS_RW_PUBLIC_SESSION;
else
s->session_info.state = CKS_RO_PUBLIC_SESSION;
tokdata->global_login_state = s->session_info.state; // SAB
}
// session_mgr_logout_all()
//
// changes the login status of all sessions in the token
//
CK_RV session_mgr_logout_all(STDLL_TokData_t *tokdata)
{
if (pthread_rwlock_wrlock(&tokdata->sess_list_rwlock)) {
TRACE_ERROR("Write Lock failed.\n");
return CKR_CANT_LOCK;
}
bt_for_each_node(tokdata, &tokdata->sess_btree, session_logout, NULL);
pthread_rwlock_unlock(&tokdata->sess_list_rwlock);
return CKR_OK;
}
//
//
CK_RV session_mgr_get_op_state(SESSION *sess,
CK_BBOOL length_only,
CK_BYTE *data, CK_ULONG *data_len)
{
OP_STATE_DATA *op_data = NULL;
CK_ULONG op_data_len = 0;
CK_ULONG offset, active_ops;
if (!sess) {
TRACE_ERROR("Invalid function arguments.\n");
return CKR_FUNCTION_FAILED;
}
if (sess->find_active == TRUE) {
TRACE_ERROR("%s\n", ock_err(ERR_STATE_UNSAVEABLE));
return CKR_STATE_UNSAVEABLE;
}
// ensure that at least one operation is active
//
active_ops = 0;
if (sess->encr_ctx.active == TRUE) {
active_ops++;
if (op_data != NULL) {
TRACE_ERROR("%s\n", ock_err(ERR_STATE_UNSAVEABLE));
return CKR_STATE_UNSAVEABLE;
}
op_data_len = sizeof(OP_STATE_DATA) +
sizeof(ENCR_DECR_CONTEXT) +
sess->encr_ctx.context_len + sess->encr_ctx.mech.ulParameterLen;
if (length_only == FALSE) {
op_data = (OP_STATE_DATA *) data;
op_data->data_len = op_data_len - sizeof(OP_STATE_DATA);
op_data->session_state = sess->session_info.state;
op_data->active_operation = STATE_ENCR;
offset = sizeof(OP_STATE_DATA);
memcpy((CK_BYTE *) op_data + offset,
&sess->encr_ctx, sizeof(ENCR_DECR_CONTEXT));
offset += sizeof(ENCR_DECR_CONTEXT);
if (sess->encr_ctx.context_len != 0) {
memcpy((CK_BYTE *) op_data + offset,
sess->encr_ctx.context, sess->encr_ctx.context_len);
offset += sess->encr_ctx.context_len;
}
if (sess->encr_ctx.mech.ulParameterLen != 0) {
memcpy((CK_BYTE *) op_data + offset,
sess->encr_ctx.mech.pParameter,
sess->encr_ctx.mech.ulParameterLen);
}
}
}
if (sess->decr_ctx.active == TRUE) {
active_ops++;
if (op_data != NULL) {
TRACE_ERROR("%s\n", ock_err(ERR_STATE_UNSAVEABLE));
return CKR_STATE_UNSAVEABLE;
}
op_data_len = sizeof(OP_STATE_DATA) +
sizeof(ENCR_DECR_CONTEXT) +
sess->decr_ctx.context_len + sess->decr_ctx.mech.ulParameterLen;
if (length_only == FALSE) {
op_data = (OP_STATE_DATA *) data;
op_data->data_len = op_data_len - sizeof(OP_STATE_DATA);
op_data->session_state = sess->session_info.state;
op_data->active_operation = STATE_DECR;
offset = sizeof(OP_STATE_DATA);
memcpy((CK_BYTE *) op_data + offset,
&sess->decr_ctx, sizeof(ENCR_DECR_CONTEXT));
offset += sizeof(ENCR_DECR_CONTEXT);
if (sess->decr_ctx.context_len != 0) {
memcpy((CK_BYTE *) op_data + offset,
sess->decr_ctx.context, sess->decr_ctx.context_len);
offset += sess->decr_ctx.context_len;
}
if (sess->decr_ctx.mech.ulParameterLen != 0) {
memcpy((CK_BYTE *) op_data + offset,
sess->decr_ctx.mech.pParameter,
sess->decr_ctx.mech.ulParameterLen);
}
}
}
if (sess->digest_ctx.active == TRUE) {
active_ops++;
if (op_data != NULL) {
TRACE_ERROR("%s\n", ock_err(ERR_STATE_UNSAVEABLE));
return CKR_STATE_UNSAVEABLE;
}
op_data_len = sizeof(OP_STATE_DATA) +
sizeof(DIGEST_CONTEXT) +
sess->digest_ctx.context_len + sess->digest_ctx.mech.ulParameterLen;
if (length_only == FALSE) {
op_data = (OP_STATE_DATA *) data;
op_data->data_len = op_data_len - sizeof(OP_STATE_DATA);
op_data->session_state = sess->session_info.state;
op_data->active_operation = STATE_DIGEST;
offset = sizeof(OP_STATE_DATA);
memcpy((CK_BYTE *) op_data + offset,
&sess->digest_ctx, sizeof(DIGEST_CONTEXT));
offset += sizeof(DIGEST_CONTEXT);
if (sess->digest_ctx.context_len != 0) {
memcpy((CK_BYTE *) op_data + offset,
sess->digest_ctx.context, sess->digest_ctx.context_len);
offset += sess->digest_ctx.context_len;
}
if (sess->digest_ctx.mech.ulParameterLen != 0) {
memcpy((CK_BYTE *) op_data + offset,
sess->digest_ctx.mech.pParameter,
sess->digest_ctx.mech.ulParameterLen);
}
}
}
if (sess->sign_ctx.active == TRUE) {
active_ops++;
if (op_data != NULL) {
TRACE_ERROR("%s\n", ock_err(ERR_STATE_UNSAVEABLE));
return CKR_STATE_UNSAVEABLE;
}
op_data_len = sizeof(OP_STATE_DATA) +
sizeof(SIGN_VERIFY_CONTEXT) +
sess->sign_ctx.context_len + sess->sign_ctx.mech.ulParameterLen;
if (length_only == FALSE) {
op_data = (OP_STATE_DATA *) data;
op_data->data_len = op_data_len - sizeof(OP_STATE_DATA);
op_data->session_state = sess->session_info.state;
op_data->active_operation = STATE_SIGN;
offset = sizeof(OP_STATE_DATA);
memcpy((CK_BYTE *) op_data + offset,
&sess->sign_ctx, sizeof(SIGN_VERIFY_CONTEXT));
offset += sizeof(SIGN_VERIFY_CONTEXT);
if (sess->sign_ctx.context_len != 0) {
memcpy((CK_BYTE *) op_data + offset,
sess->sign_ctx.context, sess->sign_ctx.context_len);
offset += sess->sign_ctx.context_len;
}
if (sess->sign_ctx.mech.ulParameterLen != 0) {
memcpy((CK_BYTE *) op_data + offset,
sess->sign_ctx.mech.pParameter,
sess->sign_ctx.mech.ulParameterLen);
}
}
}
if (sess->verify_ctx.active == TRUE) {
active_ops++;
if (op_data != NULL) {
TRACE_ERROR("%s\n", ock_err(ERR_STATE_UNSAVEABLE));
return CKR_STATE_UNSAVEABLE;
}
op_data_len = sizeof(OP_STATE_DATA) +
sizeof(SIGN_VERIFY_CONTEXT) +
sess->verify_ctx.context_len + sess->verify_ctx.mech.ulParameterLen;
if (length_only == FALSE) {
op_data = (OP_STATE_DATA *) data;
op_data->data_len = op_data_len - sizeof(OP_STATE_DATA);
op_data->session_state = sess->session_info.state;
op_data->active_operation = STATE_SIGN;
offset = sizeof(OP_STATE_DATA);
memcpy((CK_BYTE *) op_data + offset,
&sess->verify_ctx, sizeof(SIGN_VERIFY_CONTEXT));
offset += sizeof(SIGN_VERIFY_CONTEXT);
if (sess->verify_ctx.context_len != 0) {
memcpy((CK_BYTE *) op_data + offset,
sess->verify_ctx.context, sess->verify_ctx.context_len);
offset += sess->verify_ctx.context_len;
}
if (sess->verify_ctx.mech.ulParameterLen != 0) {
memcpy((CK_BYTE *) op_data + offset,
sess->verify_ctx.mech.pParameter,
sess->verify_ctx.mech.ulParameterLen);
}
}
}
if (!active_ops) {
TRACE_ERROR("%s\n", ock_err(ERR_STATE_UNSAVEABLE));
return CKR_OPERATION_NOT_INITIALIZED;
}
*data_len = op_data_len;
return CKR_OK;
}
//
//
CK_RV session_mgr_set_op_state(SESSION *sess,
CK_OBJECT_HANDLE encr_key,
CK_OBJECT_HANDLE auth_key,
CK_BYTE *data, CK_ULONG data_len)
{
OP_STATE_DATA *op_data = NULL;
CK_BYTE *mech_param = NULL;
CK_BYTE *context = NULL;
CK_BYTE *ptr1 = NULL;
CK_BYTE *ptr2 = NULL;
CK_BYTE *ptr3 = NULL;
CK_ULONG len;
UNUSED(data_len);
if (!sess || !data) {
TRACE_ERROR("%s received bad argument(s)\n", __func__);
return CKR_FUNCTION_FAILED;
}
op_data = (OP_STATE_DATA *) data;
// make sure the session states are compatible
//
if (sess->session_info.state != op_data->session_state) {
TRACE_ERROR("%s\n", ock_err(ERR_SAVED_STATE_INVALID));
return CKR_SAVED_STATE_INVALID;
}
// validate the new state information. don't touch the session
// until the new state is valid.
//
switch (op_data->active_operation) {
case STATE_ENCR:
case STATE_DECR:
{
ENCR_DECR_CONTEXT *ctx =
(ENCR_DECR_CONTEXT *) (data + sizeof(OP_STATE_DATA));
len =
sizeof(ENCR_DECR_CONTEXT) + ctx->context_len +
ctx->mech.ulParameterLen;
if (len != op_data->data_len) {
TRACE_ERROR("%s\n", ock_err(ERR_SAVED_STATE_INVALID));
return CKR_SAVED_STATE_INVALID;
}
if (auth_key != 0) {
TRACE_ERROR("%s\n", ock_err(ERR_KEY_NOT_NEEDED));
return CKR_KEY_NOT_NEEDED;
}
if (encr_key == 0) {
TRACE_ERROR("%s\n", ock_err(ERR_KEY_NEEDED));
return CKR_KEY_NEEDED;
}
ptr1 = (CK_BYTE *) ctx;
ptr2 = ptr1 + sizeof(ENCR_DECR_CONTEXT);
ptr3 = ptr2 + ctx->context_len;
if (ctx->context_len) {
context = (CK_BYTE *) malloc(ctx->context_len);
if (!context) {
TRACE_ERROR("%s\n", ock_err(ERR_HOST_MEMORY));
return CKR_HOST_MEMORY;
}
memcpy(context, ptr2, ctx->context_len);
}
if (ctx->mech.ulParameterLen) {
mech_param = (CK_BYTE *) malloc(ctx->mech.ulParameterLen);
if (!mech_param) {
if (context)
free(context);
TRACE_ERROR("%s\n", ock_err(ERR_HOST_MEMORY));
return CKR_HOST_MEMORY;
}
memcpy(mech_param, ptr3, ctx->mech.ulParameterLen);
}
}
break;
case STATE_SIGN:
case STATE_VERIFY:
{
SIGN_VERIFY_CONTEXT *ctx =
(SIGN_VERIFY_CONTEXT *) (data + sizeof(OP_STATE_DATA));
len =
sizeof(SIGN_VERIFY_CONTEXT) + ctx->context_len +
ctx->mech.ulParameterLen;
if (len != op_data->data_len) {
TRACE_ERROR("%s\n", ock_err(ERR_SAVED_STATE_INVALID));
return CKR_SAVED_STATE_INVALID;
}
if (auth_key == 0) {
TRACE_ERROR("%s\n", ock_err(ERR_KEY_NEEDED));
return CKR_KEY_NEEDED;
}
if (encr_key != 0) {
TRACE_ERROR("%s\n", ock_err(ERR_KEY_NOT_NEEDED));
return CKR_KEY_NOT_NEEDED;
}
ptr1 = (CK_BYTE *) ctx;
ptr2 = ptr1 + sizeof(SIGN_VERIFY_CONTEXT);
ptr3 = ptr2 + ctx->context_len;
if (ctx->context_len) {
context = (CK_BYTE *) malloc(ctx->context_len);
if (!context) {
TRACE_ERROR("%s\n", ock_err(ERR_HOST_MEMORY));
return CKR_HOST_MEMORY;
}
memcpy(context, ptr2, ctx->context_len);
}
if (ctx->mech.ulParameterLen) {
mech_param = (CK_BYTE *) malloc(ctx->mech.ulParameterLen);
if (!mech_param) {
if (context)
free(context);
TRACE_ERROR("%s\n", ock_err(ERR_HOST_MEMORY));
return CKR_HOST_MEMORY;
}
memcpy(mech_param, ptr3, ctx->mech.ulParameterLen);
}
}
break;
case STATE_DIGEST:
{
DIGEST_CONTEXT *ctx =
(DIGEST_CONTEXT *) (data + sizeof(OP_STATE_DATA));
len =
sizeof(DIGEST_CONTEXT) + ctx->context_len +
ctx->mech.ulParameterLen;
if (len != op_data->data_len) {
TRACE_ERROR("%s\n", ock_err(ERR_SAVED_STATE_INVALID));
return CKR_SAVED_STATE_INVALID;
}
if (auth_key != 0) {
TRACE_ERROR("%s\n", ock_err(ERR_KEY_NOT_NEEDED));
return CKR_KEY_NOT_NEEDED;
}
if (encr_key != 0) {
TRACE_ERROR("%s\n", ock_err(ERR_KEY_NOT_NEEDED));
return CKR_KEY_NOT_NEEDED;
}
ptr1 = (CK_BYTE *) ctx;
ptr2 = ptr1 + sizeof(DIGEST_CONTEXT);
ptr3 = ptr2 + ctx->context_len;
if (ctx->context_len) {
context = (CK_BYTE *) malloc(ctx->context_len);
if (!context) {
TRACE_ERROR("%s\n", ock_err(ERR_HOST_MEMORY));
return CKR_HOST_MEMORY;
}
memcpy(context, ptr2, ctx->context_len);
}
if (ctx->mech.ulParameterLen) {
mech_param = (CK_BYTE *) malloc(ctx->mech.ulParameterLen);
if (!mech_param) {
if (context)
free(context);
TRACE_ERROR("%s\n", ock_err(ERR_HOST_MEMORY));
return CKR_HOST_MEMORY;
}
memcpy(mech_param, ptr3, ctx->mech.ulParameterLen);
}
}
break;
default:
TRACE_ERROR("%s\n", ock_err(ERR_SAVED_STATE_INVALID));
return CKR_SAVED_STATE_INVALID;
}
// state information looks okay. cleanup the current session state, first
//
if (sess->encr_ctx.active)
encr_mgr_cleanup(&sess->encr_ctx);
if (sess->decr_ctx.active)
decr_mgr_cleanup(&sess->decr_ctx);
if (sess->digest_ctx.active)
digest_mgr_cleanup(&sess->digest_ctx);
if (sess->sign_ctx.active)
sign_mgr_cleanup(&sess->sign_ctx);
if (sess->verify_ctx.active)
verify_mgr_cleanup(&sess->verify_ctx);
// copy the new state information
//
switch (op_data->active_operation) {
case STATE_ENCR:
memcpy(&sess->encr_ctx, ptr1, sizeof(ENCR_DECR_CONTEXT));
sess->encr_ctx.key = encr_key;
sess->encr_ctx.context = context;
sess->encr_ctx.mech.pParameter = mech_param;
break;
case STATE_DECR:
memcpy(&sess->decr_ctx, ptr1, sizeof(ENCR_DECR_CONTEXT));
sess->decr_ctx.key = encr_key;
sess->decr_ctx.context = context;
sess->decr_ctx.mech.pParameter = mech_param;
break;
case STATE_SIGN:
memcpy(&sess->sign_ctx, ptr1, sizeof(SIGN_VERIFY_CONTEXT));
sess->sign_ctx.key = auth_key;
sess->sign_ctx.context = context;
sess->sign_ctx.mech.pParameter = mech_param;
break;
case STATE_VERIFY:
memcpy(&sess->verify_ctx, ptr1, sizeof(SIGN_VERIFY_CONTEXT));
sess->verify_ctx.key = auth_key;
sess->verify_ctx.context = context;
sess->verify_ctx.mech.pParameter = mech_param;
break;
case STATE_DIGEST:
memcpy(&sess->digest_ctx, ptr1, sizeof(DIGEST_CONTEXT));
sess->digest_ctx.context = context;
sess->digest_ctx.mech.pParameter = mech_param;
break;
}
return CKR_OK;
}
// Return TRUE if the session we're in has its PIN expired.
CK_BBOOL pin_expired(CK_SESSION_INFO *si, CK_FLAGS flags)
{
// If this is an SO session
if ((flags & CKF_SO_PIN_TO_BE_CHANGED) &&
(si->state == CKS_RW_SO_FUNCTIONS))
return TRUE;
// Else we're a User session
return ((flags & CKF_USER_PIN_TO_BE_CHANGED) &&
((si->state == CKS_RO_USER_FUNCTIONS) ||
(si->state == CKS_RW_USER_FUNCTIONS)));
}
// Return TRUE if the session we're in has its PIN locked.
CK_BBOOL pin_locked(CK_SESSION_INFO *si, CK_FLAGS flags)
{
// If this is an SO session
if ((flags & CKF_SO_PIN_LOCKED) && (si->state == CKS_RW_SO_FUNCTIONS))
return TRUE;
// Else we're a User session
return ((flags & CKF_USER_PIN_LOCKED) &&
((si->state == CKS_RO_USER_FUNCTIONS) ||
(si->state == CKS_RW_USER_FUNCTIONS)));
}
// Increment the login flags after an incorrect password
// has been passed to C_Login. New for v2.11. - KEY
void set_login_flags(CK_USER_TYPE userType, CK_FLAGS_32 *flags)
{
if (userType == CKU_USER) {
if (*flags & CKF_USER_PIN_FINAL_TRY) {
*flags |= CKF_USER_PIN_LOCKED;
*flags &= ~(CKF_USER_PIN_FINAL_TRY);
} else if (*flags & CKF_USER_PIN_COUNT_LOW) {
*flags |= CKF_USER_PIN_FINAL_TRY;
*flags &= ~(CKF_USER_PIN_COUNT_LOW);
} else {
*flags |= CKF_USER_PIN_COUNT_LOW;
}
} else {
if (*flags & CKF_SO_PIN_FINAL_TRY) {
*flags |= CKF_SO_PIN_LOCKED;
*flags &= ~(CKF_SO_PIN_FINAL_TRY);
} else if (*flags & CKF_SO_PIN_COUNT_LOW) {
*flags |= CKF_SO_PIN_FINAL_TRY;
*flags &= ~(CKF_SO_PIN_COUNT_LOW);
} else {
*flags |= CKF_SO_PIN_COUNT_LOW;
}
}
}