/*
* 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
*/
/*
* openCryptoki ICSF token
*
* Author: Marcelo Cerri (mhcerri@br.ibm.com)
*
* Based on CCC token.
*
*/
#define _GNU_SOURCE
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <limits.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <pthread.h>
#include <errno.h>
#include "pkcs11types.h"
#include "defs.h"
#include "host_defs.h"
#include "pbkdf.h"
#include "h_extern.h"
#include "tok_specific.h"
#include "tok_struct.h"
#include "icsf_config.h"
#include "icsf_specific.h"
#include "pbkdf.h"
#include "list.h"
#include "attributes.h"
#include "../api/apiproto.h"
#include "trace.h"
#include "shared_memory.h"
#include "slotmgr.h"
/* Default token attributes */
const char manuf[] = "IBM";
const char model[] = "ICSF";
const char descr[] = "IBM ICSF token";
const char label[] = "icsftok";
/* mechanisms provided by this token */
static const MECH_LIST_ELEMENT icsf_mech_list[] = {
{CKM_DES_KEY_GEN, {8, 8, CKF_HW | CKF_GENERATE}},
{CKM_DES_ECB, {0, 0, CKF_HW | CKF_ENCRYPT | CKF_DECRYPT}},
{CKM_DES_CBC, {0, 0, CKF_HW | CKF_ENCRYPT | CKF_DECRYPT}},
{CKM_DES_CBC_PAD,
{0, 0, CKF_HW | CKF_ENCRYPT | CKF_DECRYPT | CKF_WRAP | CKF_UNWRAP}},
{CKM_DES3_ECB, {0, 0, CKF_HW | CKF_ENCRYPT | CKF_DECRYPT}},
{CKM_DES3_CBC, {0, 0, CKF_HW | CKF_ENCRYPT | CKF_DECRYPT}},
{CKM_DES3_CBC_PAD,
{0, 0, CKF_HW | CKF_ENCRYPT | CKF_DECRYPT | CKF_WRAP | CKF_UNWRAP}},
{CKM_DES3_KEY_GEN, {24, 24, CKF_HW | CKF_GENERATE}},
{CKM_DES2_KEY_GEN, {24, 24, CKF_HW | CKF_GENERATE}},
{CKM_RSA_PKCS_KEY_PAIR_GEN, {512, 4096, CKF_HW | CKF_GENERATE_KEY_PAIR}},
{CKM_RSA_PKCS,
{512, 4096, CKF_HW | CKF_ENCRYPT | CKF_DECRYPT | CKF_WRAP | CKF_UNWRAP |
CKF_SIGN | CKF_VERIFY }},
{CKM_RSA_X_509,
{512, 4096, CKF_HW | CKF_ENCRYPT | CKF_DECRYPT | CKF_SIGN | CKF_VERIFY }},
{CKM_MD5_RSA_PKCS, {512, 4096, CKF_HW | CKF_SIGN | CKF_VERIFY}},
{CKM_SHA1_RSA_PKCS, {512, 4096, CKF_HW | CKF_SIGN | CKF_VERIFY}},
{CKM_SHA256_RSA_PKCS, {512, 4096, CKF_HW | CKF_SIGN | CKF_VERIFY}},
{CKM_SHA384_RSA_PKCS, {512, 4096, CKF_HW | CKF_SIGN | CKF_VERIFY}},
{CKM_SHA512_RSA_PKCS, {512, 4096, CKF_HW | CKF_SIGN | CKF_VERIFY}},
{CKM_SHA_1, {0, 0, CKF_HW | CKF_DIGEST}},
{CKM_SHA_1_HMAC, {0, 0, CKF_HW | CKF_SIGN | CKF_VERIFY}},
{CKM_SHA256_HMAC, {0, 0, CKF_HW | CKF_SIGN | CKF_VERIFY}},
{CKM_SHA384_HMAC, {0, 0, CKF_HW | CKF_SIGN | CKF_VERIFY}},
{CKM_SHA512_HMAC, {0, 0, CKF_HW | CKF_SIGN | CKF_VERIFY}},
{CKM_MD5, {0, 0, CKF_DIGEST}},
{CKM_MD5_HMAC, {0, 0, CKF_SIGN | CKF_VERIFY}},
{CKM_AES_KEY_GEN, {16, 32, CKF_HW | CKF_GENERATE}},
{CKM_AES_ECB, {16, 32, CKF_HW | CKF_ENCRYPT | CKF_DECRYPT}},
{CKM_AES_CBC, {16, 32, CKF_HW | CKF_ENCRYPT | CKF_DECRYPT}},
{CKM_AES_CBC_PAD,
{16, 32, CKF_HW | CKF_ENCRYPT | CKF_DECRYPT | CKF_WRAP | CKF_UNWRAP}},
{CKM_DH_PKCS_KEY_PAIR_GEN, {512, 2048, CKF_GENERATE_KEY_PAIR}},
{CKM_DH_PKCS_DERIVE, {512, 2048, CKF_DERIVE}},
{CKM_DSA_KEY_PAIR_GEN, {512, 2048, CKF_HW | CKF_GENERATE_KEY_PAIR}},
{CKM_DSA_SHA1, {512, 4096, CKF_HW | CKF_SIGN | CKF_VERIFY}},
{CKM_DSA, {512, 2048, CKF_HW | CKF_SIGN | CKF_VERIFY}},
{CKM_ECDSA_SHA1,
{512, 4096, CKF_HW | CKF_SIGN | CKF_VERIFY | CKF_EC_F_P |
CKF_EC_NAMEDCURVE | CKF_EC_UNCOMPRESS}},
{CKM_ECDSA,
{160, 521, CKF_HW | CKF_SIGN | CKF_VERIFY | CKF_EC_F_P |
CKF_EC_NAMEDCURVE | CKF_EC_UNCOMPRESS}},
{CKM_EC_KEY_PAIR_GEN,
{160, 521, CKF_HW | CKF_GENERATE_KEY_PAIR | CKF_EC_F_P |
CKF_EC_NAMEDCURVE | CKF_EC_UNCOMPRESS}},
{CKM_SSL3_PRE_MASTER_KEY_GEN, {48, 48, CKF_HW | CKF_GENERATE}},
{CKM_SSL3_MD5_MAC, {384, 384, CKF_SIGN | CKF_VERIFY}},
{CKM_SSL3_SHA1_MAC, {384, 384, CKF_SIGN | CKF_VERIFY}},
{CKM_SSL3_MASTER_KEY_DERIVE, {48, 48, CKF_DERIVE}},
{CKM_SSL3_KEY_AND_MAC_DERIVE, {48, 48, CKF_DERIVE}},
{CKM_TLS_KEY_AND_MAC_DERIVE, {48, 48, CKF_DERIVE}},
{CKM_GENERIC_SECRET_KEY_GEN, {80, 2048, CKF_HW | CKF_GENERATE}},
};
static const CK_ULONG icsf_mech_list_len =
(sizeof(icsf_mech_list) / sizeof(MECH_LIST_ELEMENT));
/* Each element of the list sessions should have this type: */
struct session_state {
CK_SESSION_HANDLE session_id;
LDAP *ld;
/* List element */
list_entry_t sessions;
};
/* Each element of the btree objects should have this type: */
struct icsf_object_mapping {
struct bt_ref_hdr hdr;
CK_SESSION_HANDLE session_id;
struct icsf_object_record icsf_object;
};
/*
* Structure used to keep track of data used in multi-part operations.
*/
struct icsf_multi_part_context {
int initiated;
char chain_data[ICSF_CHAINING_DATA_LEN];
char *data;
size_t data_len;
size_t used_data_len;
};
/*
* Get the session specific structure.
*/
static struct session_state *get_session_state(STDLL_TokData_t * tokdata,
CK_SESSION_HANDLE session_id)
{
icsf_private_data_t *icsf_data = tokdata->private_data;
struct session_state *found = NULL;
struct session_state *s;
/* Lock sessions list */
if (pthread_mutex_lock(&icsf_data->sess_list_mutex)) {
TRACE_ERROR("Failed to lock mutex.\n");
return NULL;
}
for_each_list_entry(&icsf_data->sessions, struct session_state, s, sessions) {
if (s->session_id == session_id) {
found = s;
goto done;
}
}
done:
/* Unlock */
if (pthread_mutex_unlock(&icsf_data->sess_list_mutex)) {
TRACE_ERROR("Mutex Unlock failed.\n");
return NULL;
}
return found;
}
static void purge_object_mapping_cb(STDLL_TokData_t * tokdata, void *value,
unsigned long node_num, void *p3)
{
icsf_private_data_t *icsf_data = tokdata->private_data;
UNUSED(value);
UNUSED(p3);
/* Remove the object */
bt_node_free(&icsf_data->objects, node_num, TRUE);
}
/*
* Remove all mapped objects.
*/
static CK_RV purge_object_mapping(STDLL_TokData_t * tokdata)
{
icsf_private_data_t *icsf_data = tokdata->private_data;
bt_for_each_node(tokdata, &icsf_data->objects, purge_object_mapping_cb,
NULL);
return CKR_OK;
}
/* Store ICSF specific data for each slot*/
struct slot_data {
int initialized;
char conf_name[PATH_MAX + 1];
char uri[PATH_MAX + 1];
char dn[NAME_MAX + 1];
char ca_file[PATH_MAX + 1];
char cert_file[PATH_MAX + 1];
char key_file[PATH_MAX + 1];
int mech;
};
struct slot_data *slot_data[NUMBER_SLOTS_MANAGED];
/*
* Converts an ICSF reason code to an ock error code
*/
int icsf_to_ock_err(int icsf_return_code, int icsf_reason_code)
{
switch (icsf_return_code) {
case 0:
return CKR_OK;
case 4:
switch (icsf_reason_code) {
case 8000:
case 11000:
return CKR_SIGNATURE_INVALID;
}
break;
case 8:
switch (icsf_reason_code) {
case 2154:
return CKR_KEY_TYPE_INCONSISTENT;
case 2028:
return CKR_WRAPPED_KEY_INVALID;
case 3003:
return CKR_BUFFER_TOO_SMALL;
case 3019:
return CKR_SESSION_HANDLE_INVALID;
case 3027:
return CKR_SESSION_HANDLE_INVALID;
case 3029:
return CKR_ATTRIBUTE_TYPE_INVALID;
case 3030:
return CKR_ATTRIBUTE_VALUE_INVALID;
case 3033:
return CKR_TEMPLATE_INCOMPLETE;
case 3034:
case 3035:
return CKR_ATTRIBUTE_READ_ONLY;
case 3038:
return CKR_KEY_FUNCTION_NOT_PERMITTED;
case 3039:
return CKR_KEY_TYPE_INCONSISTENT;
case 3041:
return CKR_KEY_NOT_WRAPPABLE;
case 3043:
return CKR_KEY_HANDLE_INVALID;
case 3045:
return CKR_KEY_UNEXTRACTABLE;
case 72:
case 11000:
return CKR_DATA_LEN_RANGE;
case 11028:
return CKR_SIGNATURE_INVALID;
}
break;
}
return CKR_FUNCTION_FAILED;
}
/*
* Called during C_Initialize.
*/
CK_RV icsftok_init(STDLL_TokData_t * tokdata, CK_SLOT_ID slot_id,
char *conf_name)
{
CK_RV rc;
struct slot_data *data;
icsf_private_data_t *icsf_data;
TRACE_INFO("icsf %s slot=%lu running\n", __func__, slot_id);
/* Check Slot ID */
if (slot_id >= NUMBER_SLOTS_MANAGED) {
TRACE_ERROR("Invalid slot ID: %lu\n", slot_id);
return CKR_FUNCTION_FAILED;
}
tokdata->mech_list = (MECH_LIST_ELEMENT *)icsf_mech_list;
tokdata->mech_list_len = icsf_mech_list_len;
icsf_data = calloc(1, sizeof(icsf_private_data_t));
if (icsf_data == NULL)
return CKR_HOST_MEMORY;
list_init(&icsf_data->sessions);
pthread_mutex_init(&icsf_data->sess_list_mutex, NULL);
bt_init(&icsf_data->objects, free);
tokdata->private_data = icsf_data;
rc = XProcLock(tokdata);
if (rc != CKR_OK)
return CKR_FUNCTION_FAILED;
if (slot_data[slot_id] == NULL) {
TRACE_ERROR("ICSF slot data not initialized.\n");
rc = CKR_FUNCTION_FAILED;
goto done;
}
data = slot_data[slot_id];
data->initialized = 0;
strncpy(data->conf_name, conf_name, sizeof(data->conf_name) - 1);
data->conf_name[sizeof(data->conf_name) - 1] = '\0';
done:
if (rc == CKR_OK)
rc = XProcUnLock(tokdata);
else
XProcUnLock(tokdata);
return rc;
}
CK_RV token_specific_init_token_data(STDLL_TokData_t * tokdata,
CK_SLOT_ID slot_id)
{
CK_RV rc = CKR_OK;
const char *conf_name = NULL;
struct icsf_config config;
/* Check Slot ID */
if (slot_id >= NUMBER_SLOTS_MANAGED) {
TRACE_ERROR("Invalid slot ID: %lu\n", slot_id);
return CKR_FUNCTION_FAILED;
}
rc = XProcLock(tokdata);
if (rc != CKR_OK)
return CKR_FUNCTION_FAILED;
if (slot_data[slot_id] == NULL) {
TRACE_ERROR("ICSF slot data not initialized.\n");
rc = CKR_FUNCTION_FAILED;
goto done;
}
/* Check if data needs to be retrieved for this slot */
if (slot_data[slot_id]->initialized) {
TRACE_DEVEL("Slot data already initialized for slot %lu. "
"Skipping it\n", slot_id);
goto done;
}
/* Check config file */
conf_name = slot_data[slot_id]->conf_name;
if (!conf_name || !conf_name[0]) {
TRACE_ERROR("Missing config for slot %lu.\n", slot_id);
rc = CKR_FUNCTION_FAILED;
goto done;
}
TRACE_DEVEL("DEBUG: conf_name=\"%s\".\n", conf_name);
if (parse_config_file(conf_name, slot_id, &config)) {
TRACE_ERROR("Failed to parse file \"%s\" for slot %lu.\n",
conf_name, slot_id);
rc = CKR_FUNCTION_FAILED;
goto done;
}
/* Copy general info */
memcpy(tokdata->nv_token_data->token_info.label, config.name,
strlen(config.name));
memcpy(tokdata->nv_token_data->token_info.manufacturerID, config.manuf,
strlen(config.manuf));
memcpy(tokdata->nv_token_data->token_info.model, config.model,
strlen(config.model));
memcpy(tokdata->nv_token_data->token_info.serialNumber, config.serial,
strlen(config.serial));
/* Copy ICSF specific info */
strcpy(slot_data[slot_id]->uri, config.uri);
strcpy(slot_data[slot_id]->dn, config.dn);
strcpy(slot_data[slot_id]->ca_file, config.ca_file);
strcpy(slot_data[slot_id]->cert_file, config.cert_file);
strcpy(slot_data[slot_id]->key_file, config.key_file);
slot_data[slot_id]->initialized = 1;
slot_data[slot_id]->mech = config.mech;
done:
if (rc == CKR_OK)
rc = XProcUnLock(tokdata);
else
XProcUnLock(tokdata);
return rc;
}
CK_RV token_specific_load_token_data(STDLL_TokData_t * tokdata,
CK_SLOT_ID slot_id, FILE * fh)
{
CK_RV rc;
struct slot_data data;
/* Check Slot ID */
if (slot_id >= NUMBER_SLOTS_MANAGED) {
TRACE_ERROR("Invalid slot ID: %lu\n", slot_id);
return CKR_FUNCTION_FAILED;
}
if (!fread(&data, sizeof(data), 1, fh)) {
TRACE_ERROR("Failed to read ICSF slot data.\n");
return CKR_FUNCTION_FAILED;
}
rc = XProcLock(tokdata);
if (rc != CKR_OK)
return CKR_FUNCTION_FAILED;
if (slot_data[slot_id] == NULL) {
TRACE_ERROR("ICSF slot data not initialized.\n");
rc = CKR_FUNCTION_FAILED;
goto done;
}
memcpy(slot_data[slot_id], &data, sizeof(data));
done:
if (rc == CKR_OK)
rc = XProcUnLock(tokdata);
else
XProcUnLock(tokdata);
return rc;
}
CK_RV token_specific_save_token_data(STDLL_TokData_t * tokdata,
CK_SLOT_ID slot_id, FILE * fh)
{
CK_RV rc;
/* Check Slot ID */
if (slot_id >= NUMBER_SLOTS_MANAGED) {
TRACE_ERROR("Invalid slot ID: %lu\n", slot_id);
return CKR_FUNCTION_FAILED;
}
rc = XProcLock(tokdata);
if (rc != CKR_OK)
return CKR_FUNCTION_FAILED;
if (slot_data[slot_id] == NULL) {
TRACE_ERROR("ICSF slot data not initialized.\n");
rc = CKR_FUNCTION_FAILED;
goto done;
}
if (!fwrite(slot_data[slot_id], sizeof(**slot_data), 1, fh)) {
TRACE_ERROR("Failed to write ICSF slot data.\n");
rc = CKR_FUNCTION_FAILED;
goto done;
}
done:
if (rc == CKR_OK)
rc = XProcUnLock(tokdata);
else
XProcUnLock(tokdata);
return rc;
}
/*
* Initialize the shared memory region. ICSF has to use a custom method for
* this because it uses additional data in the shared memory and in the future
* multiple slots should be supported for ICSF.
*/
CK_RV token_specific_attach_shm(STDLL_TokData_t * tokdata, CK_SLOT_ID slot_id)
{
CK_RV rc;
int ret;
void *ptr;
LW_SHM_TYPE **shm = &tokdata->global_shm;
size_t len = sizeof(**shm) + sizeof(**slot_data);
char *shm_id = NULL;
if (slot_id >= NUMBER_SLOTS_MANAGED) {
TRACE_ERROR("Invalid slot ID: %lu\n", slot_id);
return CKR_FUNCTION_FAILED;
}
if (asprintf(&shm_id, "/icsf-%lu", slot_id) < 0) {
TRACE_ERROR("Failed to allocate shared memory id "
"for slot %lu.\n", slot_id);
return CKR_HOST_MEMORY;
}
TRACE_DEVEL("Attaching to shared memory \"%s\".\n", shm_id);
rc = XProcLock(tokdata);
if (rc != CKR_OK)
return CKR_FUNCTION_FAILED;
/*
* Attach to an existing shared memory region or create it if it doesn't
* exists. When the it's created (ret=0) the region is initialized with
* zeroes.
*/
ret = sm_open(shm_id, 0666, (void **) &ptr, len, 1);
if (ret < 0) {
TRACE_ERROR("Failed to open shared memory \"%s\".\n", shm_id);
rc = CKR_FUNCTION_FAILED;
goto done;
}
*shm = ptr;
slot_data[slot_id] = (struct slot_data *)((unsigned char *)ptr
+ sizeof(**shm));
done:
if (rc == CKR_OK)
rc = XProcUnLock(tokdata);
else
XProcUnLock(tokdata);
if (shm_id)
free(shm_id);
return rc;
}
CK_RV login(STDLL_TokData_t * tokdata, LDAP ** ld, CK_SLOT_ID slot_id,
CK_BYTE * pin, CK_ULONG pin_len, const char *pass_file_type)
{
CK_RV rc;
struct slot_data data;
LDAP *ldapd = NULL;
int ret;
UNUSED(pass_file_type);
/* Check Slot ID */
if (slot_id >= NUMBER_SLOTS_MANAGED) {
TRACE_ERROR("Invalid slot ID: %lu\n", slot_id);
return CKR_FUNCTION_FAILED;
}
rc = XProcLock(tokdata);
if (rc != CKR_OK) {
TRACE_ERROR("Failed to get process lock.\n");
goto done;
}
/* Check slot data */
if (slot_data[slot_id] == NULL || !slot_data[slot_id]->initialized) {
TRACE_ERROR("ICSF slot data not initialized.\n");
rc = CKR_FUNCTION_FAILED;
goto done;
}
memcpy(&data, slot_data[slot_id], sizeof(data));
rc = XProcUnLock(tokdata);
if (rc != CKR_OK) {
TRACE_ERROR("Failed to release process lock.\n");
goto done;
}
if (data.mech == ICSF_CFG_MECH_SIMPLE) {
CK_BYTE mk[MAX_KEY_SIZE];
CK_BYTE racf_pass[PIN_SIZE];
int mk_len = sizeof(mk);
int racf_pass_len = sizeof(racf_pass);
char pk_dir_buf[PATH_MAX];
char fname[PATH_MAX];
/* Load master key */
sprintf(fname, "%s/MK_SO", get_pk_dir(tokdata, pk_dir_buf));
if (get_masterkey(pin, pin_len, fname, mk, &mk_len)) {
TRACE_DEVEL("Failed to get masterkey \"%s\".\n", fname);
return CKR_FUNCTION_FAILED;
}
/* Load RACF password */
if (get_racf(mk, mk_len, racf_pass, &racf_pass_len)) {
TRACE_DEVEL("Failed to get RACF password.\n");
return CKR_FUNCTION_FAILED;
}
/* Simple bind */
ret = icsf_login(&ldapd, data.uri, data.dn, (char *)racf_pass);
} else {
/* SASL bind */
ret = icsf_sasl_login(&ldapd, data.uri, data.cert_file,
data.key_file, data.ca_file, NULL);
}
if (ret) {
TRACE_DEVEL("Failed to bind to %s\n", data.uri);
rc = CKR_FUNCTION_FAILED;
goto done;
}
if (icsf_check_pkcs_extension(ldapd)) {
TRACE_ERROR("ICSF LDAP externsion not supported.\n");
rc = CKR_FUNCTION_FAILED;
goto done;
}
done:
if (rc == CKR_OK && ld)
*ld = ldapd;
return rc;
}
CK_RV reset_token_data(STDLL_TokData_t * tokdata, CK_SLOT_ID slot_id,
CK_CHAR_PTR pin, CK_ULONG pin_len)
{
CK_BYTE mk[MAX_KEY_SIZE];
CK_BYTE racf_pass[PIN_SIZE];
int mk_len = sizeof(mk);
int racf_pass_len = sizeof(racf_pass);
char pk_dir_buf[PATH_MAX];
char fname[PATH_MAX];
/* Remove user's masterkey */
if (slot_data[slot_id]->mech == ICSF_CFG_MECH_SIMPLE) {
sprintf(fname, "%s/MK_USER", get_pk_dir(tokdata, pk_dir_buf));
if (unlink(fname) && errno == ENOENT)
TRACE_WARNING("Failed to remove \"%s\".\n", fname);
/* Load master key */
sprintf(fname, "%s/MK_SO", get_pk_dir(tokdata, pk_dir_buf));
if (get_masterkey(pin, pin_len, fname, mk, &mk_len)) {
TRACE_DEVEL("Failed to load masterkey \"%s\".\n", fname);
return CKR_FUNCTION_FAILED;
}
/* Load RACF password */
if (get_racf(mk, mk_len, racf_pass, &racf_pass_len)) {
TRACE_DEVEL("Failed to get RACF password.\n");
return CKR_FUNCTION_FAILED;
}
/* Generate new key */
if (get_randombytes(mk, mk_len)) {
TRACE_DEVEL("Failed to generate new master key.\n");
return CKR_FUNCTION_FAILED;
}
/* Save racf password using the new master key */
if (secure_racf(racf_pass, racf_pass_len, mk, mk_len)) {
TRACE_DEVEL("Failed to save racf password.\n");
return CKR_FUNCTION_FAILED;
}
}
/* Reset token data and keep token name */
slot_data[slot_id]->initialized = 0;
load_token_data(tokdata, slot_id);
init_slotInfo(&tokdata->slot_info);
tokdata->nv_token_data->token_info.flags |= CKF_TOKEN_INITIALIZED;
if (slot_data[slot_id]->mech == ICSF_CFG_MECH_SIMPLE) {
/* Save master key */
if (secure_masterkey(mk, mk_len, pin, pin_len, fname)) {
TRACE_DEVEL("Failed to save the new master key.\n");
return CKR_FUNCTION_FAILED;
}
}
if (save_token_data(tokdata, slot_id)) {
TRACE_DEVEL("Failed to save token data.\n");
return CKR_FUNCTION_FAILED;
}
return CKR_OK;
}
CK_RV destroy_objects(STDLL_TokData_t * tokdata, CK_SLOT_ID slot_id,
CK_CHAR_PTR token_name, CK_CHAR_PTR pin, CK_ULONG pin_len)
{
CK_RV rv = CKR_OK;
LDAP *ld = NULL;
struct icsf_object_record records[16];
struct icsf_object_record *previous = NULL;
size_t i, records_len;
int reason = 0;
int rc;
if (login(tokdata, &ld, slot_id, pin, pin_len, RACFFILE))
return CKR_FUNCTION_FAILED;
TRACE_DEVEL("Destroying objects in slot %lu.\n", slot_id);
do {
records_len = sizeof(records) / sizeof(records[0]);
rc = icsf_list_objects(ld, NULL, (char *)token_name, 0, NULL,
previous, records, &records_len, 0);
if (ICSF_RC_IS_ERROR(rc)) {
TRACE_DEVEL("Failed to list objects for slot %lu.\n", slot_id);
rv = CKR_FUNCTION_FAILED;
goto done;
}
for (i = 0; i < records_len; i++) {
if ((rc = icsf_destroy_object(ld, &reason, &records[i]))) {
TRACE_DEVEL("Failed to destroy object "
"%s/%lu/%c in slot %lu.\n",
records[i].token_name,
records[i].sequence, records[i].id, slot_id);
rv = icsf_to_ock_err(rc, reason);
goto done;
}
}
if (records_len)
previous = &records[records_len - 1];
} while (records_len);
done:
if (icsf_logout(ld) && rv == CKR_OK)
rv = CKR_FUNCTION_FAILED;
return rv;
}
/*
* Initialize token.
*/
CK_RV icsftok_init_token(STDLL_TokData_t * tokdata, CK_SLOT_ID slot_id,
CK_CHAR_PTR pin, CK_ULONG pin_len, CK_CHAR_PTR label)
{
CK_RV rc = CKR_OK;
CK_BYTE hash_sha[SHA1_HASH_SIZE];
CK_CHAR token_name[sizeof(tokdata->nv_token_data->token_info.label) + 1];
UNUSED(label);
/* Check pin */
rc = compute_sha1(tokdata, pin, pin_len, hash_sha);
if (memcmp(tokdata->nv_token_data->so_pin_sha, hash_sha,
SHA1_HASH_SIZE) != 0) {
TRACE_ERROR("%s\n", ock_err(ERR_PIN_INCORRECT));
rc = CKR_PIN_INCORRECT;
goto done;
}
if ((rc = reset_token_data(tokdata, slot_id, pin, pin_len)))
goto done;
strunpad((char *)token_name,
(const char *)tokdata->nv_token_data->token_info.label,
sizeof(tokdata->nv_token_data->token_info.label), ' ');
if ((rc = destroy_objects(tokdata, slot_id, token_name, pin, pin_len)))
goto done;
/* purge the object btree */
if (purge_object_mapping(tokdata)) {
TRACE_DEVEL("Failed to purge objects.\n");
rc = CKR_FUNCTION_FAILED;
}
done:
return rc;
}
CK_RV icsftok_init_pin(STDLL_TokData_t * tokdata, SESSION * sess,
CK_CHAR_PTR pPin, CK_ULONG ulPinLen)
{
CK_RV rc = CKR_OK;
CK_BYTE hash_sha[SHA1_HASH_SIZE];
CK_SLOT_ID sid;
char fname[PATH_MAX];
char pk_dir_buf[PATH_MAX];
/* get slot id */
sid = sess->session_info.slotID;
/* compute the SHA of the user pin */
rc = compute_sha1(tokdata, pPin, ulPinLen, hash_sha);
if (rc != CKR_OK) {
TRACE_ERROR("Hash Computation Failed.\n");
return rc;
}
/* encrypt the masterkey and store in MK_USER if using SIMPLE AUTH
* to authenticate to ldao server. The masterkey protects the
* racf passwd.
*/
if (slot_data[sid]->mech == ICSF_CFG_MECH_SIMPLE) {
sprintf(fname, "%s/MK_USER", get_pk_dir(tokdata, pk_dir_buf));
rc = secure_masterkey(tokdata->master_key,
AES_KEY_SIZE_256, pPin, ulPinLen, fname);
if (rc != CKR_OK) {
TRACE_DEVEL("Could not create MK_USER.\n");
return rc;
}
}
rc = XProcLock(tokdata);
if (rc != CKR_OK) {
TRACE_ERROR("Failed to get process lock.\n");
return rc;
}
memcpy(tokdata->nv_token_data->user_pin_sha, hash_sha, SHA1_HASH_SIZE);
tokdata->nv_token_data->token_info.flags |= CKF_USER_PIN_INITIALIZED;
tokdata->nv_token_data->token_info.flags &= ~(CKF_USER_PIN_TO_BE_CHANGED);
tokdata->nv_token_data->token_info.flags &= ~(CKF_USER_PIN_LOCKED);
rc = XProcUnLock(tokdata);
if (rc != CKR_OK) {
TRACE_ERROR("Process Lock Failed.\n");
return rc;
}
return rc;
}
CK_RV icsftok_set_pin(STDLL_TokData_t * tokdata, SESSION * sess,
CK_CHAR_PTR pOldPin, CK_ULONG ulOldLen,
CK_CHAR_PTR pNewPin, CK_ULONG ulNewLen)
{
CK_RV rc = CKR_OK;
CK_BYTE new_hash_sha[SHA1_HASH_SIZE];
CK_BYTE old_hash_sha[SHA1_HASH_SIZE];
CK_SLOT_ID sid;
char fname[PATH_MAX];
char pk_dir_buf[PATH_MAX];
/* get slot id */
sid = sess->session_info.slotID;
rc = compute_sha1(tokdata, pNewPin, ulNewLen, new_hash_sha);
rc |= compute_sha1(tokdata, pOldPin, ulOldLen, old_hash_sha);
if (rc != CKR_OK) {
TRACE_ERROR("Hash Computation Failed.\n");
return rc;
}
/* check that the old pin and new pin are not the same. */
if (memcmp(old_hash_sha, new_hash_sha, SHA1_HASH_SIZE) == 0) {
TRACE_ERROR("%s\n", ock_err(ERR_PIN_INVALID));
return CKR_PIN_INVALID;
}
/* check the length requirements */
if ((ulNewLen < MIN_PIN_LEN) || (ulNewLen > MAX_PIN_LEN)) {
TRACE_ERROR("%s\n", ock_err(ERR_PIN_LEN_RANGE));
return CKR_PIN_LEN_RANGE;
}
if ((sess->session_info.state == CKS_RW_USER_FUNCTIONS) ||
(sess->session_info.state == CKS_RW_PUBLIC_SESSION)) {
/* check that old pin matches what is in NVTOK.DAT */
if (memcmp
(tokdata->nv_token_data->user_pin_sha, old_hash_sha,
SHA1_HASH_SIZE) != 0) {
TRACE_ERROR("%s\n", ock_err(ERR_PIN_INCORRECT));
return CKR_PIN_INCORRECT;
}
/* if using simple auth, encrypt masterkey with new pin */
if (slot_data[sid]->mech == ICSF_CFG_MECH_SIMPLE) {
sprintf(fname, "%s/MK_USER", get_pk_dir(tokdata, pk_dir_buf));
rc = secure_masterkey(tokdata->master_key,
AES_KEY_SIZE_256, pNewPin, ulNewLen, fname);
if (rc != CKR_OK) {
TRACE_ERROR("Save Master Key Failed.\n");
return rc;
}
}
/* grab lock and change shared memory */
rc = XProcLock(tokdata);
if (rc != CKR_OK) {
TRACE_ERROR("Process Lock Failed.\n");
return rc;
}
memcpy(tokdata->nv_token_data->user_pin_sha, new_hash_sha,
SHA1_HASH_SIZE);
tokdata->nv_token_data->token_info.flags &=
~(CKF_USER_PIN_TO_BE_CHANGED);
rc = XProcUnLock(tokdata);
if (rc != CKR_OK) {
TRACE_ERROR("Process Lock Failed.\n");
return rc;
}
} else if (sess->session_info.state == CKS_RW_SO_FUNCTIONS) {
/* check that old pin matches what is in NVTOK.DAT */
if (memcmp
(tokdata->nv_token_data->so_pin_sha, old_hash_sha,
SHA1_HASH_SIZE) != 0) {
TRACE_ERROR("%s\n", ock_err(ERR_PIN_INCORRECT));
return CKR_PIN_INCORRECT;
}
/* check that new pin is not the default */
if (memcmp(new_hash_sha, default_so_pin_sha, SHA1_HASH_SIZE) == 0) {
TRACE_ERROR("%s\n", ock_err(ERR_PIN_INVALID));
return CKR_PIN_INVALID;
}
if (slot_data[sid]->mech == ICSF_CFG_MECH_SIMPLE) {
/*
* if using simle auth, encrypt masterkey with new pin
*/
sprintf(fname, "%s/MK_SO", get_pk_dir(tokdata, pk_dir_buf));
rc = secure_masterkey(tokdata->master_key,
AES_KEY_SIZE_256, pNewPin, ulNewLen, fname);
if (rc != CKR_OK) {
TRACE_ERROR("Save Master Key Failed.\n");
return rc;
}
}
/* grab lock and change shared memory */
rc = XProcLock(tokdata);
if (rc != CKR_OK) {
TRACE_ERROR("Process Lock Failed.\n");
return rc;
}
memcpy(tokdata->nv_token_data->so_pin_sha, new_hash_sha,
SHA1_HASH_SIZE);
tokdata->nv_token_data->token_info.flags &= ~(CKF_SO_PIN_TO_BE_CHANGED);
XProcUnLock(tokdata);
if (rc != CKR_OK) {
TRACE_ERROR("Process Lock Failed.\n");
return rc;
}
} else {
TRACE_ERROR("%s\n", ock_err(ERR_SESSION_READ_ONLY));
return CKR_SESSION_READ_ONLY;
}
rc = save_token_data(tokdata, sid);
if (rc != CKR_OK) {
TRACE_ERROR("Save Token Failed.\n");
return rc;
}
return rc;
}
LDAP *getLDAPhandle(STDLL_TokData_t * tokdata, CK_SLOT_ID slot_id)
{
CK_BYTE racfpwd[PIN_SIZE];
int racflen;
char *ca_dir = NULL;
LDAP *new_ld = NULL;
CK_RV rc = CKR_OK;
if (slot_data[slot_id] == NULL) {
TRACE_ERROR("ICSF slot data not initialized.\n");
return NULL;
}
/* Check if using sasl or simple auth */
if (slot_data[slot_id]->mech == ICSF_CFG_MECH_SIMPLE) {
TRACE_INFO("Using SIMPLE auth with slot ID: %lu\n", slot_id);
/* get racf passwd */
rc = get_racf(tokdata->master_key, AES_KEY_SIZE_256, racfpwd, &racflen);
if (rc != CKR_OK) {
TRACE_DEVEL("Failed to get racf passwd.\n");
return NULL;
}
/* ok got the passwd, perform simple ldap bind call */
rc = icsf_login(&new_ld, slot_data[slot_id]->uri,
slot_data[slot_id]->dn, (char *)racfpwd);
if (rc != CKR_OK) {
TRACE_DEVEL("Failed to bind to ldap server.\n");
return NULL;
}
} else {
TRACE_INFO("Using SASL auth with slot ID: %lu\n", slot_id);
rc = icsf_sasl_login(&new_ld, slot_data[slot_id]->uri,
slot_data[slot_id]->cert_file,
slot_data[slot_id]->key_file,
slot_data[slot_id]->ca_file, ca_dir);
if (rc != CKR_OK) {
TRACE_DEVEL("Failed to bind to ldap server.\n");
return NULL;
}
}
return new_ld;
}
CK_RV icsf_get_handles(STDLL_TokData_t * tokdata, CK_SLOT_ID slot_id)
{
icsf_private_data_t *icsf_data = tokdata->private_data;
struct session_state *s;
/* Any prior sessions without an ldap descriptor, can now get one. */
/* Lock sessions list */
if (pthread_mutex_lock(&icsf_data->sess_list_mutex)) {
TRACE_ERROR("Failed to lock mutex.\n");
return CKR_FUNCTION_FAILED;
}
for_each_list_entry(&icsf_data->sessions, struct session_state, s,
sessions) {
if (s->ld == NULL)
s->ld = getLDAPhandle(tokdata, slot_id);
}
if (pthread_mutex_unlock(&icsf_data->sess_list_mutex)) {
TRACE_ERROR("Mutex Unlock failed.\n");
return CKR_FUNCTION_FAILED;
}
return CKR_OK;
}
CK_RV icsftok_open_session(STDLL_TokData_t * tokdata, SESSION * sess)
{
icsf_private_data_t *icsf_data = tokdata->private_data;
CK_RV rc = CKR_OK;
LDAP *ld;
struct session_state *session_state;
/* Sanity */
if (sess == NULL) {
TRACE_ERROR("%s\n", ock_err(ERR_ARGUMENTS_BAD));
return CKR_FUNCTION_FAILED;
}
/* Add session to list */
session_state = malloc(sizeof(struct session_state));
if (!session_state) {
TRACE_ERROR("%s\n", ock_err(ERR_HOST_MEMORY));
return CKR_FUNCTION_FAILED;
}
session_state->session_id = sess->handle;
session_state->ld = NULL;
if (pthread_mutex_lock(&icsf_data->sess_list_mutex)) {
TRACE_ERROR("Failed to lock mutex.\n");
free(session_state);
return CKR_FUNCTION_FAILED;
}
/* see if user has logged in to acquire ldap handle for session.
* pkcs#11v2.2 states that all sessions within a process have
* same login state.
*/
if (session_mgr_user_session_exists(tokdata)) {
ld = getLDAPhandle(tokdata, sess->session_info.slotID);
if (ld == NULL) {
TRACE_DEVEL("Failed to get LDAP handle for session.\n");
rc = CKR_FUNCTION_FAILED;
goto done;
}
/* put the new ldap handle into the session state. */
session_state->ld = ld;
}
/* put new session_state into the list */
list_insert_head(&icsf_data->sessions, &session_state->sessions);
done:
/* Unlock */
if (pthread_mutex_unlock(&icsf_data->sess_list_mutex)) {
TRACE_ERROR("Mutex Unlock Failed.\n");
rc = CKR_FUNCTION_FAILED;
}
if (rc != CKR_OK)
free(session_state);
return rc;
}
/*
* Close a session.
*
* Must be called with sess_list_mutex locked.
*/
static CK_RV close_session(STDLL_TokData_t * tokdata,
struct session_state *session_state,
CK_BBOOL in_fork_initializer)
{
icsf_private_data_t *icsf_data = tokdata->private_data;
CK_RV rc = CKR_OK;
unsigned long i;
int reason = 0;
/* Remove each session object */
for (i = 1; i <= icsf_data->objects.size; i++) {
struct icsf_object_mapping *mapping;
/* Skip missing ids */
if (!(mapping = bt_get_node_value(&icsf_data->objects, i)))
continue;
/* Skip object from other sessions */
if (mapping->session_id != session_state->session_id) {
bt_put_node_value(&icsf_data->objects, mapping);
mapping = NULL;
continue;
}
/* Skip token objects */
if (mapping->icsf_object.id != ICSF_SESSION_OBJECT) {
bt_put_node_value(&icsf_data->objects, mapping);
mapping = NULL;
continue;
}
if ((rc = icsf_destroy_object(session_state->ld, &reason,
&mapping->icsf_object))) {
/* Log error */
TRACE_DEBUG("Failed to remove icsf object: %s/%lu/%c",
mapping->icsf_object.token_name,
mapping->icsf_object.sequence, mapping->icsf_object.id);
rc = icsf_to_ock_err(rc, reason);
bt_put_node_value(&icsf_data->objects, mapping);
mapping = NULL;
break;
}
bt_put_node_value(&icsf_data->objects, mapping);
mapping = NULL;
/* Remove object from object list */
bt_node_free(&icsf_data->objects, i, TRUE);
}
if (rc)
return rc;
/* Log off from LDAP server */
if (session_state->ld) {
if (!in_fork_initializer && icsf_logout(session_state->ld)) {
TRACE_DEVEL("Failed to disconnect from LDAP server.\n");
return CKR_FUNCTION_FAILED;
}
session_state->ld = NULL;
}
/* Remove session */
list_remove(&session_state->sessions);
if (list_is_empty(&icsf_data->sessions)) {
if (purge_object_mapping(tokdata)) {
TRACE_DEVEL("Failed to purge objects.\n");
rc = CKR_FUNCTION_FAILED;
}
}
free(session_state);
return rc;
}
/*
* Called during C_CloseSession.
*/
CK_RV icsftok_close_session(STDLL_TokData_t * tokdata, SESSION * session,
CK_BBOOL in_fork_initializer)
{
CK_RV rc;
struct session_state *session_state;
/* Get the related session_state */
if (session == NULL
|| !(session_state = get_session_state(tokdata, session->handle))) {
TRACE_ERROR("%s\n", ock_err(ERR_SESSION_HANDLE_INVALID));
return CKR_SESSION_HANDLE_INVALID;
}
if ((rc = close_session(tokdata, session_state, in_fork_initializer)))
TRACE_ERROR("close_session failed\n");
return rc;
}
/*
* Called during C_Finalize and C_CloseAllSessions
*/
CK_RV icsftok_final(STDLL_TokData_t * tokdata, CK_BBOOL finalize,
CK_BBOOL in_fork_initializer)
{
icsf_private_data_t *icsf_data = tokdata->private_data;
CK_RV rc = CKR_OK;
struct session_state *session_state;
list_entry_t *e;
/* Lock to add a new session in the list */
if (pthread_mutex_lock(&icsf_data->sess_list_mutex)) {
TRACE_ERROR("Failed to lock mutex.\n");
return CKR_FUNCTION_FAILED;
}
for_each_list_entry_safe(&icsf_data->sessions, struct session_state,
session_state, sessions, e) {
if ((rc = close_session(tokdata, session_state, in_fork_initializer)))
break;
}
/* Unlock */
if (pthread_mutex_unlock(&icsf_data->sess_list_mutex)) {
TRACE_ERROR("Mutex Unlock Failed.\n");
return CKR_FUNCTION_FAILED;
}
if (finalize) {
bt_destroy(&icsf_data->objects);
pthread_mutex_destroy(&icsf_data->sess_list_mutex);
free(icsf_data);
tokdata->private_data = NULL;
}
return rc;
}
CK_RV icsftok_login(STDLL_TokData_t * tokdata, SESSION * sess,
CK_USER_TYPE userType, CK_CHAR_PTR pPin, CK_ULONG ulPinLen)
{
CK_RV rc = CKR_OK;
char fname[PATH_MAX];
CK_BYTE hash_sha[SHA1_HASH_SIZE];
int mklen;
char pk_dir_buf[PATH_MAX];
CK_SLOT_ID slot_id = sess->session_info.slotID;
/* Check Slot ID */
if (slot_id >= NUMBER_SLOTS_MANAGED) {
TRACE_ERROR("Invalid slot ID: %lu\n", slot_id);
return CKR_FUNCTION_FAILED;
}
/* compute the sha of the pin. */
rc = compute_sha1(tokdata, pPin, ulPinLen, hash_sha);
if (rc != CKR_OK) {
TRACE_ERROR("Hash Computation Failed.\n");
return rc;
}
rc = XProcLock(tokdata);
if (rc != CKR_OK) {
TRACE_ERROR("Process Lock Failed.\n");
return rc;
}
if (userType == CKU_USER) {
/* check if pin initialized */
if (memcmp(tokdata->nv_token_data->user_pin_sha,
"00000000000000000000", SHA1_HASH_SIZE) == 0) {
TRACE_ERROR("%s\n", ock_err(ERR_USER_PIN_NOT_INITIALIZED));
rc = CKR_USER_PIN_NOT_INITIALIZED;
goto done;
}
/* check that pin is the same as the one in NVTOK.DAT */
if (memcmp(tokdata->nv_token_data->user_pin_sha, hash_sha,
SHA1_HASH_SIZE) != 0) {
TRACE_ERROR("%s\n", ock_err(ERR_PIN_INCORRECT));
rc = CKR_PIN_INCORRECT;
goto done;
}
/* now load the master key */
if (slot_data[slot_id]->mech == ICSF_CFG_MECH_SIMPLE) {
sprintf(fname, "%s/MK_USER", get_pk_dir(tokdata, pk_dir_buf));
rc = get_masterkey(pPin, ulPinLen, fname,
tokdata->master_key, &mklen);
if (rc != CKR_OK) {
TRACE_DEVEL("Failed to load master key.\n");
goto done;
}
}
} else {
/* if SO ... */
/* check that pin is the same as the one in NVTOK.DAT */
if (memcmp(tokdata->nv_token_data->so_pin_sha, hash_sha,
SHA1_HASH_SIZE) != 0) {
TRACE_ERROR("%s\n", ock_err(ERR_PIN_INCORRECT));
rc = CKR_PIN_INCORRECT;
goto done;
}
if (slot_data[slot_id]->mech == ICSF_CFG_MECH_SIMPLE) {
/* now load the master key */
sprintf(fname, "%s/MK_SO", get_pk_dir(tokdata, pk_dir_buf));
rc = get_masterkey(pPin, ulPinLen, fname,
tokdata->master_key, &mklen);
if (rc != CKR_OK) {
TRACE_DEVEL("Failed to load master key.\n");
goto done;
}
}
}
/* Now that user is authenticated, can get racf passwd and
* establish ldap handle for session. This will get done
* when we call icsf_get_handles() in SC_Login().
*/
done:
if (rc == CKR_OK)
rc = XProcUnLock(tokdata);
else
XProcUnLock(tokdata);
return rc;
}
static CK_RV check_session_permissions(SESSION * sess, CK_ATTRIBUTE * attrs,
CK_ULONG attrs_len)
{
CK_RV rc = CKR_OK;
/* PKCS#11 default value for CKA_TOKEN is FALSE */
CK_BBOOL is_token_obj = FALSE;
/* ICSF default value for CKA_PRIVATE is TRUE */
CK_BBOOL is_priv_obj = TRUE;
/* Get attributes values */
find_bbool_attribute(attrs, attrs_len, CKA_TOKEN, &is_token_obj);
find_bbool_attribute(attrs, attrs_len, CKA_PRIVATE, &is_priv_obj);
/*
* Check whether session has permissions to create the object, etc
*
* Object R/O R/W R/O R/W R/W
* Type Public Public User User SO
* -------------------------------------------------------------
* Public session R/W R/W R/W R/W R/W
* Private session R/W R/W
* Public token R/O R/W R/O R/W R/W
* Private token R/O R/W
*/
if (sess->session_info.state == CKS_RO_PUBLIC_SESSION) {
if (is_priv_obj) {
TRACE_ERROR("%s\n", ock_err(ERR_USER_NOT_LOGGED_IN));
rc = CKR_USER_NOT_LOGGED_IN;
goto done;
}
if (is_token_obj) {
TRACE_ERROR("%s\n", ock_err(ERR_SESSION_READ_ONLY));
rc = CKR_SESSION_READ_ONLY;
goto done;
}
}
if (sess->session_info.state == CKS_RO_USER_FUNCTIONS) {
if (is_token_obj) {
TRACE_ERROR("%s\n", ock_err(ERR_SESSION_READ_ONLY));
rc = CKR_SESSION_READ_ONLY;
goto done;
}
}
if (sess->session_info.state == CKS_RW_PUBLIC_SESSION) {
if (is_priv_obj) {
TRACE_ERROR("%s\n", ock_err(ERR_USER_NOT_LOGGED_IN));
rc = CKR_USER_NOT_LOGGED_IN;
goto done;
}
}
if (sess->session_info.state == CKS_RW_SO_FUNCTIONS) {
if (is_priv_obj) {
TRACE_ERROR("%s\n", ock_err(ERR_USER_NOT_LOGGED_IN));
rc = CKR_USER_NOT_LOGGED_IN;
goto done;
}
}
done:
return rc;
}
/*
* Copy an existing object.
*/
CK_RV icsftok_copy_object(STDLL_TokData_t * tokdata,
SESSION * session, CK_ATTRIBUTE_PTR attrs,
CK_ULONG attrs_len, CK_OBJECT_HANDLE src,
CK_OBJECT_HANDLE_PTR dst)
{
icsf_private_data_t *icsf_data = tokdata->private_data;
CK_RV rc = CKR_OK;
struct session_state *session_state;
struct icsf_object_mapping *mapping_dst = NULL;
struct icsf_object_mapping *mapping_src = NULL;
CK_ULONG node_number;
int reason = 0;
CK_BBOOL is_priv;
CK_BBOOL is_token;
CK_RV rc_permission = CKR_OK;
CK_ATTRIBUTE priv_attrs[] = {
{CKA_PRIVATE, &is_priv, sizeof(is_priv)}
,
{CKA_TOKEN, &is_token, sizeof(is_token)}
,
};
CK_ATTRIBUTE_PTR temp_attrs;
/* Get session state */
if (!(session_state = get_session_state(tokdata, session->handle))) {
TRACE_ERROR("%s\n", ock_err(ERR_SESSION_HANDLE_INVALID));
rc = CKR_SESSION_HANDLE_INVALID;
goto done;
}
/* check ldap handle */
if (session_state->ld == NULL) {
TRACE_ERROR("No LDAP handle.\n");
rc = CKR_FUNCTION_FAILED;
goto done;
}
/* Allocate structure for new object */
if (!(mapping_dst = malloc(sizeof(*mapping_dst)))) {
TRACE_ERROR("%s\n", ock_err(ERR_HOST_MEMORY));
rc = CKR_HOST_MEMORY;
goto done;
}
mapping_src = bt_get_node_value(&icsf_data->objects, src);
if (!mapping_src) {
TRACE_ERROR("%s\n", ock_err(ERR_HOST_MEMORY));
rc = CKR_OBJECT_HANDLE_INVALID;
goto done;
}
rc = icsf_get_attribute(session_state->ld, &reason,
&mapping_src->icsf_object, priv_attrs, 2);
if (rc != CKR_OK) {
TRACE_ERROR("icsf_get_attribute failed\n");
goto done;
}
if (attrs_len != 0) {
/* looking for CKA_PRIVATE */
temp_attrs = get_attribute_by_type(attrs, attrs_len, CKA_PRIVATE);
if (temp_attrs != NULL) {
priv_attrs[0].pValue = temp_attrs->pValue;
priv_attrs[0].ulValueLen = temp_attrs->ulValueLen;
}
/* looking for CKA_TOKEN */
temp_attrs = get_attribute_by_type(attrs, attrs_len, CKA_TOKEN);
if (temp_attrs != NULL) {
priv_attrs[1].pValue = temp_attrs->pValue;
priv_attrs[1].ulValueLen = attrs->ulValueLen;
}
}
/* Check permissions based on attributes and session */
rc = check_session_permissions(session, priv_attrs, 2);
if (rc_permission != CKR_OK) {
TRACE_DEVEL("check_session_permissions failed\n");
goto done;
}
/* Call ICSF service */
rc = icsf_copy_object(session_state->ld, &reason, attrs, attrs_len,
&mapping_src->icsf_object, &mapping_dst->icsf_object);
if (rc != 0) {
TRACE_DEVEL("Failed to Copy object.\n");
rc = icsf_to_ock_err(rc, reason);
goto done;
}
/* Add info about object into session */
if (!(node_number = bt_node_add(&icsf_data->objects, mapping_dst))) {
TRACE_ERROR("Failed to add object to binary tree.\n");
rc = CKR_FUNCTION_FAILED;
goto done;
}
/* Use node number as handle */
*dst = node_number;
done:
if (mapping_src) {
bt_put_node_value(&icsf_data->objects, mapping_src);
mapping_src = NULL;
}
/* If allocated, object must be freed in case of failure */
if (rc && mapping_dst)
free(mapping_dst);
return rc;
}
/*
* Create a new object.
*/
CK_RV icsftok_create_object(STDLL_TokData_t * tokdata, SESSION * session,
CK_ATTRIBUTE_PTR attrs, CK_ULONG attrs_len,
CK_OBJECT_HANDLE_PTR handle)
{
icsf_private_data_t *icsf_data = tokdata->private_data;
CK_RV rc = CKR_OK;
struct session_state *session_state;
struct icsf_object_mapping *mapping;
CK_ULONG node_number;
char token_name[sizeof(tokdata->nv_token_data->token_info.label) + 1];
int reason = 0;
/* Check permissions based on attributes and session */
rc = check_session_permissions(session, attrs, attrs_len);
if (rc != CKR_OK)
return rc;
/* Copy token name from shared memory */
rc = XProcLock(tokdata);
if (rc != CKR_OK) {
TRACE_ERROR("Failed to get process lock.\n");
return rc;
}
strunpad(token_name, (const char *)tokdata->nv_token_data->token_info.label,
sizeof(tokdata->nv_token_data->token_info.label), ' ');
rc = XProcUnLock(tokdata);
if (rc != CKR_OK) {
TRACE_ERROR("Failed to release process lock.\n");
return rc;
}
/* Allocate structure to keep ICSF object information */
if (!(mapping = malloc(sizeof(*mapping)))) {
TRACE_ERROR("%s\n", ock_err(ERR_HOST_MEMORY));
return CKR_HOST_MEMORY;
}
memset(mapping, 0, sizeof(struct icsf_object_mapping));
mapping->session_id = session->handle;
/* Get session state */
if (!(session_state = get_session_state(tokdata, session->handle))) {
TRACE_ERROR("%s\n", ock_err(ERR_SESSION_HANDLE_INVALID));
rc = CKR_SESSION_HANDLE_INVALID;
goto done;
}
/* check ldap handle */
if (session_state->ld == NULL) {
TRACE_ERROR("No LDAP handle.\n");
rc = CKR_FUNCTION_FAILED;
goto done;
}
/* Call ICSF service */
if ((rc = icsf_create_object(session_state->ld, &reason, token_name,
attrs, attrs_len, &mapping->icsf_object))) {
TRACE_DEVEL("icsf_create_object failed\n");
rc = icsf_to_ock_err(rc, reason);
goto done;
}
/* Add info about object into session */
if (!(node_number = bt_node_add(&icsf_data->objects, mapping))) {
TRACE_ERROR("Failed to add object to binary tree.\n");
rc = CKR_FUNCTION_FAILED;
goto done;
}
/* Use node number as handle */
*handle = node_number;
done:
/* If allocated, object must be freed in case of failure */
if (rc && mapping)
free(mapping);
return rc;
}
/*
* Check if attribute values are valid and add default values for missing ones.
*
* It returns a new allocated array that must be freed with
* free_attribute_array().
*/
static CK_RV check_key_attributes(CK_ULONG class, CK_ULONG key_type,
CK_ATTRIBUTE_PTR attrs, CK_ULONG attrs_len,
CK_ATTRIBUTE_PTR * p_attrs,
CK_ULONG * p_attrs_len)
{
CK_RV rc;
CK_ULONG i;
CK_ULONG check_types[] = { CKA_CLASS, CKA_KEY_TYPE };
CK_ULONG *check_values[] = { &class, &key_type };
if ((rc = dup_attribute_array(attrs, attrs_len, p_attrs, p_attrs_len)))
return rc;
for (i = 0; i < sizeof(check_types) / sizeof(*check_types); i++) {
/* Search for the attribute */
CK_ATTRIBUTE_PTR attr = get_attribute_by_type(*p_attrs,
*p_attrs_len,
check_types[i]);
if (attr) {
/* Check the expected value */
if (*((CK_ULONG *) attr->pValue) != *check_values[i]) {
TRACE_ERROR("%s\n", ock_err(ERR_ATTRIBUTE_VALUE_INVALID));
rc = CKR_ATTRIBUTE_VALUE_INVALID;
goto cleanup;
}
} else {
/* Add default value */
rc = add_to_attribute_array(p_attrs, p_attrs_len,
check_types[i],
(CK_BYTE *) check_values[i],
sizeof(*check_values[i]));
if (rc)
goto cleanup;
}
}
rc = CKR_OK;
cleanup:
if (rc) {
free_attribute_array(*p_attrs, *p_attrs_len);
*p_attrs = NULL;
*p_attrs_len = 0;
}
return rc;
}
/*
* Get the type of the key that must be generated based on given mechanism.
*
* This functions is used by both symmetric and asymmetric key generation
* functions.
*/
static CK_ULONG get_generate_key_type(CK_MECHANISM_PTR mech)
{
switch (mech->mechanism) {
/* Symmetric keys */
case CKM_AES_KEY_GEN:
return CKK_AES;
case CKM_DES_KEY_GEN:
return CKK_DES;
case CKM_DES2_KEY_GEN:
return CKK_DES2;
case CKM_DES3_KEY_GEN:
return CKK_DES3;
case CKM_SSL3_PRE_MASTER_KEY_GEN:
return CKK_GENERIC_SECRET;
/* Asymmetric keys */
case CKM_RSA_PKCS_KEY_PAIR_GEN:
return CKK_RSA;
case CKM_DSA_KEY_PAIR_GEN:
return CKK_DSA;
case CKM_DH_PKCS_KEY_PAIR_GEN:
case CKM_DH_PKCS_DERIVE:
return CKK_DH;
case CKM_EC_KEY_PAIR_GEN:
return CKK_EC;
case CKM_SSL3_MASTER_KEY_DERIVE:
case CKM_SSL3_KEY_AND_MAC_DERIVE:
case CKM_TLS_KEY_AND_MAC_DERIVE:
case CKM_GENERIC_SECRET_KEY_GEN:
return CKK_GENERIC_SECRET;
}
return -1;
}
/*
* Generate a key pair.
*/
CK_RV icsftok_generate_key_pair(STDLL_TokData_t * tokdata, SESSION * session,
CK_MECHANISM_PTR mech,
CK_ATTRIBUTE_PTR pub_attrs,
CK_ULONG pub_attrs_len,
CK_ATTRIBUTE_PTR priv_attrs,
CK_ULONG priv_attrs_len,
CK_OBJECT_HANDLE_PTR p_pub_key,
CK_OBJECT_HANDLE_PTR p_priv_key)
{
icsf_private_data_t *icsf_data = tokdata->private_data;
CK_RV rc;
char token_name[sizeof(tokdata->nv_token_data->token_info.label) + 1];
struct session_state *session_state;
struct icsf_object_mapping *pub_key_mapping = NULL;
struct icsf_object_mapping *priv_key_mapping = NULL;
int reason = 0;
int pub_node_number, priv_node_number;
CK_ATTRIBUTE_PTR new_pub_attrs = NULL;
CK_ULONG new_pub_attrs_len = 0;
CK_ATTRIBUTE_PTR new_priv_attrs = NULL;
CK_ULONG new_priv_attrs_len = 0;
CK_ULONG key_type;
/* Check and set default attributes based on mech */
if ((key_type = get_generate_key_type(mech)) == (CK_ULONG)-1) {
TRACE_ERROR("%s\n", ock_err(ERR_MECHANISM_INVALID));
rc = CKR_MECHANISM_INVALID;
goto done;
}
rc = check_key_attributes(CKO_PUBLIC_KEY, key_type, pub_attrs,
pub_attrs_len, &new_pub_attrs,
&new_pub_attrs_len);
if (rc != CKR_OK)
goto done;
rc = check_key_attributes(CKO_PRIVATE_KEY, key_type, priv_attrs,
priv_attrs_len, &new_priv_attrs,
&new_priv_attrs_len);
if (rc != CKR_OK)
goto done;
/* Check permissions based on attributes and session */
rc = check_session_permissions(session, new_pub_attrs, new_pub_attrs_len);
if (rc != CKR_OK)
goto done;
rc = check_session_permissions(session, new_priv_attrs, new_priv_attrs_len);
if (rc != CKR_OK)
goto done;
/* Get session state */
if (!(session_state = get_session_state(tokdata, session->handle))) {
TRACE_ERROR("%s\n", ock_err(ERR_SESSION_HANDLE_INVALID));
rc = CKR_SESSION_HANDLE_INVALID;
goto done;
}
/* check ldap handle */
if (session_state->ld == NULL) {
TRACE_ERROR("No LDAP handle.\n");
rc = CKR_FUNCTION_FAILED;
goto done;
}
/* Copy token name from shared memory */
rc = XProcLock(tokdata);
if (rc != CKR_OK) {
TRACE_ERROR("Failed to get process lock.\n");
goto done;
}
strunpad(token_name, (const char *)tokdata->nv_token_data->token_info.label,
sizeof(tokdata->nv_token_data->token_info.label), ' ');
rc = XProcUnLock(tokdata);
if (rc != CKR_OK) {
TRACE_ERROR("Failed to release process lock.\n");
goto done;
}
/* Allocate structure to keep ICSF objects information */
if (!(pub_key_mapping = malloc(sizeof(*pub_key_mapping))) ||
!(priv_key_mapping = malloc(sizeof(*priv_key_mapping)))) {
TRACE_ERROR("%s\n", ock_err(ERR_HOST_MEMORY));
rc = CKR_HOST_MEMORY;
goto done;
}
/* Call ICSF service */
if ((rc = icsf_generate_key_pair(session_state->ld, &reason, token_name,
new_pub_attrs, new_pub_attrs_len,
new_priv_attrs, new_priv_attrs_len,
&pub_key_mapping->icsf_object,
&priv_key_mapping->icsf_object))) {
TRACE_DEVEL("icsf_generate_key_pair failed\n");
rc = icsf_to_ock_err(rc, reason);
goto done;
}
/* Add info about objects into session */
if (!(pub_node_number = bt_node_add(&icsf_data->objects, pub_key_mapping)) ||
!(priv_node_number = bt_node_add(&icsf_data->objects, priv_key_mapping))) {
TRACE_ERROR("Failed to add object to binary tree.\n");
rc = CKR_FUNCTION_FAILED;
goto done;
}
/* Use node numbers as handles */
*p_pub_key = pub_node_number;
*p_priv_key = priv_node_number;
done:
free_attribute_array(new_pub_attrs, new_pub_attrs_len);
free_attribute_array(new_priv_attrs, new_priv_attrs_len);
/* Object mappings must be freed in case of failure */
if (rc && pub_key_mapping)
free(pub_key_mapping);
if (rc && priv_key_mapping)
free(priv_key_mapping);
return rc;
}
/*
* Generate a symmetric key.
*/
CK_RV icsftok_generate_key(STDLL_TokData_t * tokdata, SESSION * session,
CK_MECHANISM_PTR mech, CK_ATTRIBUTE_PTR attrs,
CK_ULONG attrs_len, CK_OBJECT_HANDLE_PTR handle)
{
icsf_private_data_t *icsf_data = tokdata->private_data;
CK_RV rc = CKR_OK;
struct session_state *session_state;
struct icsf_object_mapping *mapping = NULL;
CK_ULONG node_number;
char token_name[sizeof(tokdata->nv_token_data->token_info.label) + 1];
CK_ATTRIBUTE_PTR new_attrs = NULL;
CK_ULONG new_attrs_len = 0;
CK_ULONG class = CKO_SECRET_KEY;
CK_ULONG key_type = 0;
int reason = 0;
/* Check attributes */
if ((key_type = get_generate_key_type(mech)) == (CK_ULONG)-1) {
TRACE_ERROR("%s\n", ock_err(ERR_MECHANISM_INVALID));
rc = CKR_MECHANISM_INVALID;
goto done;
}
rc = check_key_attributes(class, key_type, attrs, attrs_len, &new_attrs,
&new_attrs_len);
if (rc != CKR_OK)
goto done;
/* Check permissions based on attributes and session */
rc = check_session_permissions(session, new_attrs, new_attrs_len);
if (rc != CKR_OK)
goto done;
/* Copy token name from shared memory */
rc = XProcLock(tokdata);
if (rc != CKR_OK) {
TRACE_ERROR("Failed to get process lock.\n");
goto done;
}
strunpad(token_name, (const char *)tokdata->nv_token_data->token_info.label,
sizeof(tokdata->nv_token_data->token_info.label), ' ');
rc = XProcUnLock(tokdata);
if (rc != CKR_OK) {
TRACE_ERROR("Failed to release process lock.\n");
goto done;
}
/* Allocate structure to keep ICSF object information */
if (!(mapping = malloc(sizeof(*mapping)))) {
TRACE_ERROR("%s\n", ock_err(ERR_HOST_MEMORY));
goto done;
}
memset(mapping, 0, sizeof(struct icsf_object_mapping));
mapping->session_id = session->handle;
/* Get session state */
if (!(session_state = get_session_state(tokdata, session->handle))) {
TRACE_ERROR("%s\n", ock_err(ERR_SESSION_HANDLE_INVALID));
rc = CKR_SESSION_HANDLE_INVALID;
goto done;
}
/* check ldap handle */
if (session_state->ld == NULL) {
TRACE_ERROR("No LDAP handle.\n");
rc = CKR_FUNCTION_FAILED;
goto done;
}
/* Call ICSF service */
if ((rc = icsf_generate_secret_key(session_state->ld, &reason, token_name,
mech, new_attrs, new_attrs_len,
&mapping->icsf_object))) {
TRACE_DEVEL("icsf_generate_secret_key failed\n");
rc = icsf_to_ock_err(rc, reason);
goto done;
}
/* Add info about object into session */
if (!(node_number = bt_node_add(&icsf_data->objects, mapping))) {
TRACE_ERROR("Failed to add object to binary tree.\n");
rc = CKR_FUNCTION_FAILED;
goto done;
}
/* Use node number as handle */
*handle = node_number;
done:
if (new_attrs)
free_attribute_array(new_attrs, new_attrs_len);
/* If allocated, object must be freed in case of failure */
if (rc && mapping)
free(mapping);
return rc;
}
/*
* Free all data pointed by an encryption context and set everything to zero.
*/
static void free_encr_ctx(ENCR_DECR_CONTEXT * encr_ctx)
{
struct icsf_multi_part_context *multi_part_ctx;
if (!encr_ctx)
return;
/* Initialize encryption context */
multi_part_ctx = (struct icsf_multi_part_context *) encr_ctx->context;
if (multi_part_ctx) {
if (multi_part_ctx->data)
free(multi_part_ctx->data);
free(multi_part_ctx);
}
if (encr_ctx->mech.pParameter)
free(encr_ctx->mech.pParameter);
memset(encr_ctx, 0, sizeof(*encr_ctx));
}
/*
* Return if the algorithm used by a mechanism is asymmetric or symmetric.
*/
static CK_RV get_crypt_type(CK_MECHANISM_PTR mech, int *p_symmetric)
{
switch (mech->mechanism) {
case CKM_AES_ECB:
case CKM_AES_CBC:
case CKM_AES_CBC_PAD:
case CKM_DES_ECB:
case CKM_DES_CBC:
case CKM_DES_CBC_PAD:
case CKM_DES3_ECB:
case CKM_DES3_CBC:
case CKM_DES3_CBC_PAD:
*p_symmetric = 1;
break;
case CKM_RSA_PKCS:
case CKM_RSA_X_509:
*p_symmetric = 0;
break;
default:
return CKR_MECHANISM_INVALID;
}
return CKR_OK;
}
/**
* Validate mechanism parameter length here for the applicable
* encryption/decryption mechanisms supported by icsf token
*/
static CK_RV validate_mech_parameters(CK_MECHANISM_PTR mech)
{
CK_RV rc = CKR_OK;
size_t expected_block_size = 0;
/* Verify the mechanisms that has a parameter length
* specification per pkcs11#v2.2 spec
* */
switch (mech->mechanism) {
case CKM_DES_CBC:
case CKM_DES_CBC_PAD:
case CKM_DES3_CBC:
case CKM_DES3_CBC_PAD:
case CKM_AES_CBC:
case CKM_AES_CBC_PAD:
/* Get the expected block size. This check needs to be here as
* CKM_RSA_X_509 and CKM_RSA_PKCS does not have a block size */
if ((rc = icsf_block_size(mech->mechanism, &expected_block_size)))
return rc;
if (mech->ulParameterLen != expected_block_size) {
TRACE_ERROR("Invalid mechanism parameter length: %lu "
"(expected %lu)\n",
(unsigned long) mech->ulParameterLen,
(unsigned long) expected_block_size);
return CKR_MECHANISM_PARAM_INVALID;
}
break;
case CKM_DES_ECB:
case CKM_DES3_ECB:
case CKM_RSA_X_509:
case CKM_RSA_PKCS:
case CKM_AES_ECB:
if (mech->ulParameterLen != 0) {
TRACE_ERROR("%s\n", ock_err(ERR_MECHANISM_PARAM_INVALID));
return CKR_MECHANISM_PARAM_INVALID;
}
break;
default:
/** Encryption/decryption mechanism not supported by icsf token */
TRACE_ERROR("icsf invalid mechanism %lu\n", mech->mechanism);
return CKR_MECHANISM_INVALID;
}
return rc;
}
/*
* Initialize an encryption operation.
*/
CK_RV icsftok_encrypt_init(STDLL_TokData_t * tokdata,
SESSION * session, CK_MECHANISM_PTR mech,
CK_OBJECT_HANDLE key)
{
icsf_private_data_t *icsf_data = tokdata->private_data;
CK_RV rc = CKR_OK;
ENCR_DECR_CONTEXT *encr_ctx = &session->encr_ctx;
struct icsf_multi_part_context *multi_part_ctx = NULL;
size_t block_size = 0;
int symmetric = 0;
struct icsf_object_mapping *mapping = NULL;
/* Check session */
if (!get_session_state(tokdata, session->handle)) {
rc = CKR_SESSION_HANDLE_INVALID;
TRACE_ERROR("%s\n", ock_err(ERR_SESSION_HANDLE_INVALID));
goto done;
}
/* Get algorithm type */
if ((rc = get_crypt_type(mech, &symmetric)))
goto done;
/* Check if key exists */
if (!(mapping = bt_get_node_value(&icsf_data->objects, key))) {
rc = CKR_KEY_HANDLE_INVALID;
TRACE_ERROR("%s\n", ock_err(ERR_KEY_HANDLE_INVALID));
goto done;
}
bt_put_node_value(&icsf_data->objects, mapping);
mapping = NULL;
/** validate the mechanism parameter length here */
if ((rc = validate_mech_parameters(mech)))
goto done;
/* Initialize encryption context */
free_encr_ctx(encr_ctx);
encr_ctx->key = key;
encr_ctx->active = TRUE;
encr_ctx->multi = FALSE;
/* Copy mechanism */
if (mech->pParameter == NULL || mech->ulParameterLen == 0) {
encr_ctx->mech.ulParameterLen = 0;
encr_ctx->mech.pParameter = NULL;
} else {
encr_ctx->mech.pParameter = malloc(mech->ulParameterLen);
if (!encr_ctx->mech.pParameter) {
TRACE_ERROR("%s\n", ock_err(ERR_HOST_MEMORY));
rc = CKR_HOST_MEMORY;
goto done;
}
encr_ctx->mech.ulParameterLen = mech->ulParameterLen;
memcpy(encr_ctx->mech.pParameter, mech->pParameter,
mech->ulParameterLen);
}
encr_ctx->mech.mechanism = mech->mechanism;
/*
* Asymmetric algorithms don't support multi-part and then there's no
* need to allocate context.
*/
if (!symmetric)
goto done;
/* Allocate context for multi-part operations */
if (!(multi_part_ctx = malloc(sizeof(*multi_part_ctx)))) {
TRACE_ERROR("%s\n", ock_err(ERR_HOST_MEMORY));
rc = CKR_HOST_MEMORY;
goto done;
}
encr_ctx->context = (void *) multi_part_ctx;
/* Chained data has always a fixed length */
memset(multi_part_ctx, 0, sizeof(*multi_part_ctx));
/* Check mechanism and get block size */
rc = icsf_block_size(mech->mechanism, &block_size);
if (rc != CKR_OK)
goto done;
/*
* data is used to retain data until at least the block size is reached.
*/
multi_part_ctx->data_len = block_size;
multi_part_ctx->data = malloc(multi_part_ctx->data_len);
if (!multi_part_ctx->data) {
TRACE_ERROR("%s\n", ock_err(ERR_HOST_MEMORY));
rc = CKR_HOST_MEMORY;
goto done;
}
done:
if (rc != CKR_OK)
free_encr_ctx(encr_ctx);
return rc;
}
/*
* Encrypt data and finalize an encryption operation.
*/
CK_RV icsftok_encrypt(STDLL_TokData_t * tokdata,
SESSION * session, CK_BYTE_PTR input_data,
CK_ULONG input_data_len, CK_BYTE_PTR output_data,
CK_ULONG_PTR p_output_data_len)
{
icsf_private_data_t *icsf_data = tokdata->private_data;
CK_RV rc = CKR_OK;
CK_BBOOL is_length_only = (output_data == NULL);
ENCR_DECR_CONTEXT *encr_ctx = &session->encr_ctx;
struct session_state *session_state;
struct icsf_object_mapping *mapping = NULL;
char chain_data[ICSF_CHAINING_DATA_LEN] = { 0, };
size_t chain_data_len = sizeof(chain_data);
int reason = 0;
int symmetric = 0;
/* Get algorithm type */
if ((rc = get_crypt_type(&encr_ctx->mech, &symmetric)))
goto done;
/* Check if there's a multi-part encryption in progress */
if (encr_ctx->multi) {
TRACE_ERROR("%s\n", ock_err(ERR_OPERATION_ACTIVE));
rc = CKR_OPERATION_ACTIVE;
goto done;
}
/* Check session */
if (!(session_state = get_session_state(tokdata, session->handle))) {
TRACE_ERROR("%s\n", ock_err(ERR_SESSION_HANDLE_INVALID));
rc = CKR_SESSION_HANDLE_INVALID;
goto done;
}
/* check ldap handle */
if (session_state->ld == NULL) {
TRACE_ERROR("No LDAP handle.\n");
rc = CKR_FUNCTION_FAILED;
goto done;
}
/* Check if key exists */
if (!(mapping = bt_get_node_value(&icsf_data->objects, encr_ctx->key))) {
TRACE_ERROR("%s\n", ock_err(ERR_KEY_HANDLE_INVALID));
rc = CKR_KEY_HANDLE_INVALID;
goto done;
}
/* Encrypt data using remote token. */
if (symmetric) {
rc = icsf_secret_key_encrypt(session_state->ld, &reason,
&mapping->icsf_object,
&encr_ctx->mech,
ICSF_CHAINING_ONLY, (char *)input_data,
input_data_len, (char *)output_data,
p_output_data_len, chain_data,
&chain_data_len);
} else {
rc = icsf_public_key_verify(session_state->ld, &reason, TRUE,
&mapping->icsf_object,
&encr_ctx->mech, (char *)input_data,
input_data_len, (char *)output_data,
p_output_data_len);
}
if (rc) {
if (reason == ICSF_REASON_OUTPUT_PARAMETER_TOO_SHORT) {
if (is_length_only) {
/*
* Parameter too short is not a problem when
* querying the expect output size.
*/
rc = CKR_OK;
} else {
TRACE_ERROR("%s\n", ock_err(ERR_BUFFER_TOO_SMALL));
rc = CKR_BUFFER_TOO_SMALL;
}
} else {
TRACE_ERROR("Failed to encrypt data. reason = %d\n", reason);
rc = icsf_to_ock_err(rc, reason);
}
goto done;
}
done:
if (mapping) {
bt_put_node_value(&icsf_data->objects, mapping);
mapping = NULL;
}
if (rc != CKR_BUFFER_TOO_SMALL && !(rc == CKR_OK && is_length_only))
free_encr_ctx(encr_ctx);
return rc;
}
/*
* Multi-part encryption.
*/
CK_RV icsftok_encrypt_update(STDLL_TokData_t * tokdata,
SESSION * session, CK_BYTE_PTR input_part,
CK_ULONG input_part_len, CK_BYTE_PTR output_part,
CK_ULONG_PTR p_output_part_len)
{
icsf_private_data_t *icsf_data = tokdata->private_data;
CK_RV rc = CKR_OK;
CK_BBOOL is_length_only = (output_part == NULL);
ENCR_DECR_CONTEXT *encr_ctx = &session->encr_ctx;
struct icsf_multi_part_context *multi_part_ctx;
struct session_state *session_state;
struct icsf_object_mapping *mapping = NULL;
char chain_data[ICSF_CHAINING_DATA_LEN] = { 0, };
size_t chain_data_len = sizeof(chain_data);
CK_ULONG total, remaining;
char *buffer = NULL;
int chaining;
int reason = 0;
int symmetric = 0;
/* Multi-part is not supported for asymmetric algorithms. */
if ((rc = get_crypt_type(&encr_ctx->mech, &symmetric)))
goto done;
if (!symmetric) {
TRACE_ERROR("%s\n", ock_err(ERR_MECHANISM_INVALID));
rc = CKR_MECHANISM_INVALID;
goto done;
}
/* Check session */
if (!(session_state = get_session_state(tokdata, session->handle))) {
TRACE_ERROR("%s\n", ock_err(ERR_SESSION_HANDLE_INVALID));
rc = CKR_SESSION_HANDLE_INVALID;
goto done;
}
/* check ldap handle */
if (session_state->ld == NULL) {
TRACE_ERROR("No LDAP handle.\n");
rc = CKR_FUNCTION_FAILED;
goto done;
}
/* Check if key exists */
if (!(mapping = bt_get_node_value(&icsf_data->objects, encr_ctx->key))) {
TRACE_ERROR("%s\n", ock_err(ERR_KEY_HANDLE_INVALID));
rc = CKR_KEY_HANDLE_INVALID;
goto done;
}
multi_part_ctx = (struct icsf_multi_part_context *) encr_ctx->context;
/* Define the type of the call */
switch (encr_ctx->mech.mechanism) {
case CKM_DES_ECB:
case CKM_DES3_ECB:
case CKM_AES_ECB:
/* ICSF just support the chaining mode ONLY for ECB. */
chaining = ICSF_CHAINING_ONLY;
break;
default:
if (multi_part_ctx->initiated) {
chaining = ICSF_CHAINING_CONTINUE;
memcpy(chain_data, multi_part_ctx->chain_data, chain_data_len);
} else {
chaining = ICSF_CHAINING_INITIAL;
}
}
/*
* Data needs to be sent to ICSF in chucks with size that is multiple of
* block size. Any remaining data is kept in the multi-part context and
* can be sent in a further call of the update function or when the
* finalize function is called.
*/
total = multi_part_ctx->used_data_len + input_part_len;
remaining = total % multi_part_ctx->data_len;
/*
* If there's no enough data to make a call, skip it.
*/
if (total < multi_part_ctx->data_len) {
*p_output_part_len = 0;
goto keep_remaining_data;
}
/*
* The data to be encrypted should have length that is multiple of the
* block size. It is composed by data kept in the multi-part context
* concatenated with part of the data given.
*/
if (!(buffer = malloc(total - remaining))) {
TRACE_ERROR("%s\n", ock_err(ERR_HOST_MEMORY));
rc = CKR_HOST_MEMORY;
goto done;
}
memcpy(buffer, multi_part_ctx->data, multi_part_ctx->used_data_len);
memcpy(buffer + multi_part_ctx->used_data_len, input_part,
input_part_len - remaining);
/* Encrypt data using remote token. */
rc = icsf_secret_key_encrypt(session_state->ld, &reason,
&mapping->icsf_object,
&encr_ctx->mech, chaining,
buffer, total - remaining,
(char *)output_part, p_output_part_len,
chain_data, &chain_data_len);
if (rc) {
if (reason == ICSF_REASON_OUTPUT_PARAMETER_TOO_SHORT) {
if (is_length_only) {
/*
* Parameter too short is not a problem when
* querying the expect output size.
*/
rc = CKR_OK;
} else {
TRACE_ERROR("%s\n", ock_err(ERR_BUFFER_TOO_SMALL));
rc = CKR_BUFFER_TOO_SMALL;
}
} else {
TRACE_DEVEL("Failed to encrypt data. reason = %d\n", reason);
rc = icsf_to_ock_err(rc, reason);
}
goto done;
}
/** If this is the first block for multi-part operation, also set
* the encr_ctx->context_len here. This is needed for
* C_GetOperationState to work correctly */
if (!multi_part_ctx->initiated)
encr_ctx->context_len = sizeof(*multi_part_ctx);
/*
* When blocks are sent it's necessary to keep the chain data returned
* to be used in a subsequent call.
*/
if (!is_length_only) {
/* Copy chain data into context */
memcpy(multi_part_ctx->chain_data, chain_data, chain_data_len);
/* Mark multi-part operation as initiated */
multi_part_ctx->initiated = TRUE;
/* Mark the multi-part operation in encr_ctx */
encr_ctx->multi = TRUE;
/* Data stored in cache was used */
multi_part_ctx->used_data_len = 0;
}
keep_remaining_data:
/* Keep the remaining data to a next call */
if (!is_length_only) {
/* Copy remaining part of input_part into context */
if (total < multi_part_ctx->data_len) {
memcpy(multi_part_ctx->data +
multi_part_ctx->used_data_len, input_part, input_part_len);
} else {
memcpy(multi_part_ctx->data,
input_part + input_part_len - remaining, remaining);
}
multi_part_ctx->used_data_len = remaining;
}
done:
if (mapping) {
bt_put_node_value(&icsf_data->objects, mapping);
mapping = NULL;
}
/* Free resources */
if (buffer)
free(buffer);
if (rc != CKR_OK && rc != CKR_BUFFER_TOO_SMALL)
free_encr_ctx(encr_ctx);
return rc;
}
/*
* Finalize a multi-part encryption.
*/
CK_RV icsftok_encrypt_final(STDLL_TokData_t * tokdata,
SESSION * session, CK_BYTE_PTR output_part,
CK_ULONG_PTR p_output_part_len)
{
icsf_private_data_t *icsf_data = tokdata->private_data;
CK_RV rc = CKR_OK;
CK_BBOOL is_length_only = (output_part == NULL);
ENCR_DECR_CONTEXT *encr_ctx = &session->encr_ctx;
struct icsf_multi_part_context *multi_part_ctx;
struct session_state *session_state;
struct icsf_object_mapping *mapping = NULL;
char chain_data[ICSF_CHAINING_DATA_LEN] = { 0, };
size_t chain_data_len = sizeof(chain_data);
int chaining;
int reason = 0;
int symmetric = 0;
/* Multi-part is not supported for asymmetric algorithms. */
if ((rc = get_crypt_type(&encr_ctx->mech, &symmetric)))
goto done;
if (!symmetric) {
TRACE_ERROR("%s\n", ock_err(ERR_MECHANISM_INVALID));
rc = CKR_MECHANISM_INVALID;
goto done;
}
/* Check session */
if (!(session_state = get_session_state(tokdata, session->handle))) {
TRACE_ERROR("%s\n", ock_err(ERR_SESSION_HANDLE_INVALID));
rc = CKR_SESSION_HANDLE_INVALID;
goto done;
}
/* check ldap handle */
if (session_state->ld == NULL) {
TRACE_ERROR("No LDAP handle.\n");
rc = CKR_FUNCTION_FAILED;
goto done;
}
/* Check if key exists */
if (!(mapping = bt_get_node_value(&icsf_data->objects, encr_ctx->key))) {
TRACE_ERROR("%s\n", ock_err(ERR_KEY_HANDLE_INVALID));
rc = CKR_KEY_HANDLE_INVALID;
goto done;
}
/* Define the type of the call */
multi_part_ctx = (struct icsf_multi_part_context *) encr_ctx->context;
switch (encr_ctx->mech.mechanism) {
case CKM_DES_ECB:
case CKM_DES3_ECB:
case CKM_AES_ECB:
/*
* When not using a chained algorithm and there's no remaining
* data, don't call ICSF.
*/
*p_output_part_len = 0;
if (!multi_part_ctx->used_data_len)
goto done;
/* ICSF just support the chaining mode ONLY for ECB. */
chaining = ICSF_CHAINING_ONLY;
break;
default:
if (multi_part_ctx->initiated) {
chaining = ICSF_CHAINING_FINAL;
memcpy(chain_data, multi_part_ctx->chain_data, chain_data_len);
} else {
chaining = ICSF_CHAINING_ONLY;
}
}
/*
* Encrypt data using remote token.
*
* All the data in multi-part context should be sent.
*/
rc = icsf_secret_key_encrypt(session_state->ld, &reason,
&mapping->icsf_object,
&encr_ctx->mech, chaining,
multi_part_ctx->data,
multi_part_ctx->used_data_len,
(char *)output_part, p_output_part_len,
chain_data, &chain_data_len);
if (rc) {
if (reason == ICSF_REASON_OUTPUT_PARAMETER_TOO_SHORT) {
if (is_length_only) {
/*
* Parameter too short is not a problem when
* querying the expect output size.
*/
rc = CKR_OK;
} else {
TRACE_ERROR("%s\n", ock_err(ERR_BUFFER_TOO_SMALL));
rc = CKR_BUFFER_TOO_SMALL;
}
} else {
TRACE_DEVEL("Failed to encrypt data. reason = %d\n", reason);
rc = icsf_to_ock_err(rc, reason);
}
goto done;
}
done:
if (mapping) {
bt_put_node_value(&icsf_data->objects, mapping);
mapping = NULL;
}
if ((is_length_only && rc != CKR_OK) ||
(!is_length_only && rc != CKR_BUFFER_TOO_SMALL))
free_encr_ctx(encr_ctx);
return rc;
}
/*
* Initialize a decryption operation.
*/
CK_RV icsftok_decrypt_init(STDLL_TokData_t * tokdata,
SESSION * session, CK_MECHANISM_PTR mech,
CK_OBJECT_HANDLE key)
{
icsf_private_data_t *icsf_data = tokdata->private_data;
CK_RV rc = CKR_OK;
ENCR_DECR_CONTEXT *decr_ctx = &session->decr_ctx;
struct icsf_multi_part_context *multi_part_ctx = NULL;
size_t block_size = 0;
int symmetric = 0;
struct icsf_object_mapping *mapping = NULL;
/* Check session */
if (!get_session_state(tokdata, session->handle)) {
rc = CKR_SESSION_HANDLE_INVALID;
TRACE_ERROR("%s\n", ock_err(ERR_SESSION_HANDLE_INVALID));
goto done;
}
/* Get algorithm type */
if ((rc = get_crypt_type(mech, &symmetric)))
goto done;
/* Check if key exists */
if (!(mapping = bt_get_node_value(&icsf_data->objects, key))) {
rc = CKR_KEY_HANDLE_INVALID;
TRACE_ERROR("%s\n", ock_err(ERR_KEY_HANDLE_INVALID));
goto done;
}
bt_put_node_value(&icsf_data->objects, mapping);
mapping = NULL;
/** validate the mechanism parameter length here */
if ((rc = validate_mech_parameters(mech)))
goto done;
/* Initialize decryption context */
free_encr_ctx(decr_ctx);
decr_ctx->key = key;
decr_ctx->active = TRUE;
decr_ctx->multi = FALSE;
/* Copy mechanism */
if (mech->pParameter == NULL || mech->ulParameterLen == 0) {
decr_ctx->mech.ulParameterLen = 0;
decr_ctx->mech.pParameter = NULL;
} else {
decr_ctx->mech.pParameter = malloc(mech->ulParameterLen);
if (!decr_ctx->mech.pParameter) {
TRACE_ERROR("%s\n", ock_err(ERR_HOST_MEMORY));
rc = CKR_HOST_MEMORY;
goto done;
}
decr_ctx->mech.ulParameterLen = mech->ulParameterLen;
memcpy(decr_ctx->mech.pParameter, mech->pParameter,
mech->ulParameterLen);
}
decr_ctx->mech.mechanism = mech->mechanism;
/*
* Asymmetric algorithms don't support multi-part and then there's no
* need to allocate context.
*/
if (!symmetric)
goto done;
/* Allocate context for multi-part operations */
if (!(multi_part_ctx = malloc(sizeof(*multi_part_ctx)))) {
TRACE_ERROR("%s\n", ock_err(ERR_HOST_MEMORY));
rc = CKR_HOST_MEMORY;
goto done;
}
decr_ctx->context = (void *) multi_part_ctx;
/* Chained data has always a fixed length */
memset(multi_part_ctx, 0, sizeof(*multi_part_ctx));
/* Check mechanism and get block size */
rc = icsf_block_size(mech->mechanism, &block_size);
if (rc != CKR_OK)
goto done;
/*
* data is used to retain data until at least the block size is reached.
*/
multi_part_ctx->data_len = block_size;
multi_part_ctx->data = malloc(multi_part_ctx->data_len);
if (!multi_part_ctx->data) {
TRACE_ERROR("%s\n", ock_err(ERR_HOST_MEMORY));
rc = CKR_HOST_MEMORY;
goto done;
}
done:
if (rc != CKR_OK)
free_encr_ctx(decr_ctx);
return rc;
}
/*
* Decrypt data and finalize a decryption operation.
*/
CK_RV icsftok_decrypt(STDLL_TokData_t * tokdata,
SESSION * session, CK_BYTE_PTR input_data,
CK_ULONG input_data_len, CK_BYTE_PTR output_data,
CK_ULONG_PTR p_output_data_len)
{
icsf_private_data_t *icsf_data = tokdata->private_data;
CK_RV rc = CKR_OK;
CK_BBOOL is_length_only = (output_data == NULL);
ENCR_DECR_CONTEXT *decr_ctx = &session->decr_ctx;
struct session_state *session_state;
struct icsf_object_mapping *mapping = NULL;
char chain_data[ICSF_CHAINING_DATA_LEN] = { 0, };
size_t chain_data_len = sizeof(chain_data);
int reason = 0;
int symmetric = 0;
/* Get algorithm type */
if ((rc = get_crypt_type(&decr_ctx->mech, &symmetric)))
goto done;
/* Check if there's a multi-part decryption in progress */
if (decr_ctx->multi) {
TRACE_ERROR("%s\n", ock_err(ERR_OPERATION_ACTIVE));
rc = CKR_OPERATION_ACTIVE;
goto done;
}
/* Check session */
if (!(session_state = get_session_state(tokdata, session->handle))) {
TRACE_ERROR("%s\n", ock_err(ERR_SESSION_HANDLE_INVALID));
rc = CKR_SESSION_HANDLE_INVALID;
goto done;
}
/* check ldap handle */
if (session_state->ld == NULL) {
TRACE_ERROR("No LDAP handle.\n");
rc = CKR_FUNCTION_FAILED;
goto done;
}
/* Check if key exists */
if (!(mapping = bt_get_node_value(&icsf_data->objects, decr_ctx->key))) {
TRACE_ERROR("%s\n", ock_err(ERR_KEY_HANDLE_INVALID));
rc = CKR_KEY_HANDLE_INVALID;
goto done;
}
/* Decrypt data using remote token. */
if (symmetric) {
rc = icsf_secret_key_decrypt(session_state->ld, &reason,
&mapping->icsf_object,
&decr_ctx->mech,
ICSF_CHAINING_ONLY, (char *)input_data,
input_data_len, (char *)output_data,
p_output_data_len, chain_data,
&chain_data_len);
} else {
rc = icsf_private_key_sign(session_state->ld, &reason, TRUE,
&mapping->icsf_object,
&decr_ctx->mech, (char *)input_data,
input_data_len, (char *)output_data,
p_output_data_len);
}
if (rc) {
if (reason == ICSF_REASON_OUTPUT_PARAMETER_TOO_SHORT) {
if (is_length_only) {
/*
* Parameter too short is not a problem when
* querying the expect output size.
*/
rc = CKR_OK;
} else {
TRACE_ERROR("%s\n", ock_err(ERR_BUFFER_TOO_SMALL));
rc = CKR_BUFFER_TOO_SMALL;
}
} else {
TRACE_DEVEL("Failed to decrypt data. reason = %d\n", reason);
rc = icsf_to_ock_err(rc, reason);
}
goto done;
}
done:
if (mapping) {
bt_put_node_value(&icsf_data->objects, mapping);
mapping = NULL;
}
if (rc != CKR_BUFFER_TOO_SMALL && !(rc == CKR_OK && is_length_only))
free_encr_ctx(decr_ctx);
return rc;
}
/*
* Multi-part decryption.
*/
CK_RV icsftok_decrypt_update(STDLL_TokData_t * tokdata,
SESSION * session, CK_BYTE_PTR input_part,
CK_ULONG input_part_len, CK_BYTE_PTR output_part,
CK_ULONG_PTR p_output_part_len)
{
icsf_private_data_t *icsf_data = tokdata->private_data;
CK_RV rc = CKR_OK;
CK_BBOOL is_length_only = (output_part == NULL);
ENCR_DECR_CONTEXT *decr_ctx = &session->decr_ctx;
struct icsf_multi_part_context *multi_part_ctx;
struct session_state *session_state;
struct icsf_object_mapping *mapping = NULL;
char chain_data[ICSF_CHAINING_DATA_LEN] = { 0, };
size_t chain_data_len = sizeof(chain_data);
CK_ULONG total, remaining;
char *buffer = NULL;
int chaining;
int reason = 0;
int padding = 0;
int symmetric = 0;
/* Multi-part is not supported for asymmetric algorithms. */
if ((rc = get_crypt_type(&decr_ctx->mech, &symmetric)))
goto done;
if (!symmetric) {
TRACE_ERROR("%s\n", ock_err(ERR_MECHANISM_INVALID));
rc = CKR_MECHANISM_INVALID;
goto done;
}
/* Check session */
if (!(session_state = get_session_state(tokdata, session->handle))) {
TRACE_ERROR("%s\n", ock_err(ERR_SESSION_HANDLE_INVALID));
rc = CKR_SESSION_HANDLE_INVALID;
goto done;
}
/* check ldap handle */
if (session_state->ld == NULL) {
TRACE_ERROR("No LDAP handle.\n");
rc = CKR_FUNCTION_FAILED;
goto done;
}
/* Check if key exists */
if (!(mapping = bt_get_node_value(&icsf_data->objects, decr_ctx->key))) {
TRACE_ERROR("%s\n", ock_err(ERR_KEY_HANDLE_INVALID));
rc = CKR_KEY_HANDLE_INVALID;
goto done;
}
multi_part_ctx = (struct icsf_multi_part_context *) decr_ctx->context;
/* Define the type of the call */
switch (decr_ctx->mech.mechanism) {
case CKM_AES_ECB:
case CKM_DES_ECB:
case CKM_DES3_ECB:
/* ICSF just support the chaining mode ONLY for ECB. */
chaining = ICSF_CHAINING_ONLY;
break;
case CKM_AES_CBC_PAD:
case CKM_DES_CBC_PAD:
case CKM_DES3_CBC_PAD:
padding = 1;
/* fallthrough */
default:
if (multi_part_ctx->initiated) {
chaining = ICSF_CHAINING_CONTINUE;
memcpy(chain_data, multi_part_ctx->chain_data, chain_data_len);
} else {
chaining = ICSF_CHAINING_INITIAL;
}
}
/*
* Data needs to be sent to ICSF in chucks with size that is multiple of
* block size. Any remaining data is kept in the multi-part context and
* can be sent in a further call of the update function or when the
* finalize function is called.
*
* When padding is used, there's no way to know if the current block of
* data is the one that contains the padding, So a block is kept in
* multi-part context when the data available is exactly multiple of the
* block size.
*/
total = multi_part_ctx->used_data_len + input_part_len;
if (!padding) {
remaining = total % multi_part_ctx->data_len;
} else {
remaining = MIN(((total - 1) % multi_part_ctx->data_len) + 1, total);
}
/*
* If there's no enough data to make a call, skip it.
*/
if (total < multi_part_ctx->data_len ||
(padding && total == multi_part_ctx->data_len)) {
*p_output_part_len = 0;
goto keep_remaining_data;
}
/*
* The data to be decrypted should have length that is multiple of the
* block size. It is composed by data kept in the multi-part context
* concatenated with part of the data given.
*/
if (!(buffer = malloc(total - remaining))) {
TRACE_ERROR("%s\n", ock_err(ERR_HOST_MEMORY));
rc = CKR_HOST_MEMORY;
goto done;
}
memcpy(buffer, multi_part_ctx->data, multi_part_ctx->used_data_len);
memcpy(buffer + multi_part_ctx->used_data_len, input_part,
input_part_len - remaining);
/* Decrypt data using remote token. */
rc = icsf_secret_key_decrypt(session_state->ld, &reason,
&mapping->icsf_object,
&decr_ctx->mech, chaining,
buffer, total - remaining,
(char *)output_part, p_output_part_len,
chain_data, &chain_data_len);
if (rc) {
if (reason == ICSF_REASON_OUTPUT_PARAMETER_TOO_SHORT) {
if (is_length_only) {
/*
* Parameter too short is not a problem when
* querying the expect output size.
*/
rc = CKR_OK;
} else {
TRACE_ERROR("%s\n", ock_err(ERR_BUFFER_TOO_SMALL));
rc = CKR_BUFFER_TOO_SMALL;
}
} else {
TRACE_DEVEL("Failed to decrypt data. reason = %d\n", reason);
rc = icsf_to_ock_err(rc, reason);
}
goto done;
}
/* If this is the first block sent for multi-part set the context_len */
if (!multi_part_ctx->initiated)
decr_ctx->context_len = sizeof(*multi_part_ctx);
/*
* When blocks are sent it's necessary to keep the chain data returned
* to be used in a subsequent call.
*/
if (!is_length_only) {
/* Copy chain data into context */
memcpy(multi_part_ctx->chain_data, chain_data, chain_data_len);
/* Mark multi-part operation as initiated */
multi_part_ctx->initiated = TRUE;
/* Mark multi-part operation in decr_ctx in session */
decr_ctx->multi = TRUE;
/* Data stored in cache was used */
multi_part_ctx->used_data_len = 0;
}
keep_remaining_data:
/* Keep the remaining data to a next call */
if (!is_length_only) {
/* Copy remaining part of input_part into context */
if (total < multi_part_ctx->data_len ||
(padding && total == multi_part_ctx->data_len)) {
memcpy(multi_part_ctx->data +
multi_part_ctx->used_data_len, input_part, input_part_len);
} else {
memcpy(multi_part_ctx->data,
input_part + input_part_len - remaining, remaining);
}
multi_part_ctx->used_data_len = remaining;
}
done:
if (mapping) {
bt_put_node_value(&icsf_data->objects, mapping);
mapping = NULL;
}
/* Free resources */
if (buffer)
free(buffer);
if (rc != CKR_OK && rc != CKR_BUFFER_TOO_SMALL)
free_encr_ctx(decr_ctx);
return rc;
}
/*
* Finalize a multi-part decryption.
*/
CK_RV icsftok_decrypt_final(STDLL_TokData_t * tokdata,
SESSION * session, CK_BYTE_PTR output_part,
CK_ULONG_PTR p_output_part_len)
{
icsf_private_data_t *icsf_data = tokdata->private_data;
CK_RV rc = CKR_OK;
CK_BBOOL is_length_only = (output_part == NULL);
ENCR_DECR_CONTEXT *decr_ctx = &session->decr_ctx;
struct icsf_multi_part_context *multi_part_ctx;
struct session_state *session_state;
struct icsf_object_mapping *mapping = NULL;
char chain_data[ICSF_CHAINING_DATA_LEN] = { 0, };
size_t chain_data_len = sizeof(chain_data);
int chaining;
int reason = 0;
int symmetric = 0;
/* Multi-part is not supported for asymmetric algorithms. */
if ((rc = get_crypt_type(&decr_ctx->mech, &symmetric)))
goto done;
if (!symmetric) {
TRACE_ERROR("%s\n", ock_err(ERR_MECHANISM_INVALID));
rc = CKR_MECHANISM_INVALID;
goto done;
}
/* Check session */
if (!(session_state = get_session_state(tokdata, session->handle))) {
TRACE_ERROR("%s\n", ock_err(ERR_SESSION_HANDLE_INVALID));
rc = CKR_SESSION_HANDLE_INVALID;
goto done;
}
/* check ldap handle */
if (session_state->ld == NULL) {
TRACE_ERROR("No LDAP handle.\n");
rc = CKR_FUNCTION_FAILED;
goto done;
}
/* Check if key exists */
if (!(mapping = bt_get_node_value(&icsf_data->objects, decr_ctx->key))) {
TRACE_ERROR("%s\n", ock_err(ERR_KEY_HANDLE_INVALID));
rc = CKR_KEY_HANDLE_INVALID;
goto done;
}
/* Define the type of the call */
multi_part_ctx = (struct icsf_multi_part_context *) decr_ctx->context;
switch (decr_ctx->mech.mechanism) {
case CKM_AES_ECB:
case CKM_DES_ECB:
case CKM_DES3_ECB:
/*
* When not using a chained algorithm and there's no remaining
* data, don't call ICSF.
*/
*p_output_part_len = 0;
if (!multi_part_ctx->used_data_len)
goto done;
/* ICSF just support the chaining mode ONLY for ECB. */
chaining = ICSF_CHAINING_ONLY;
break;
default:
if (multi_part_ctx->initiated) {
chaining = ICSF_CHAINING_FINAL;
memcpy(chain_data, multi_part_ctx->chain_data, chain_data_len);
} else {
chaining = ICSF_CHAINING_ONLY;
}
}
/*
* Decrypt data using remote token.
*
* All the data in multi-part context should be sent.
*/
rc = icsf_secret_key_decrypt(session_state->ld, &reason,
&mapping->icsf_object,
&decr_ctx->mech, chaining,
multi_part_ctx->data,
multi_part_ctx->used_data_len,
(char *)output_part, p_output_part_len,
chain_data, &chain_data_len);
if (rc) {
if (reason == ICSF_REASON_OUTPUT_PARAMETER_TOO_SHORT) {
if (is_length_only) {
/*
* Parameter too short is not a problem when
* querying the expect output size.
*/
rc = CKR_OK;
} else {
TRACE_ERROR("%s\n", ock_err(ERR_BUFFER_TOO_SMALL));
rc = CKR_BUFFER_TOO_SMALL;
}
} else {
TRACE_DEVEL("Failed to decrypt data. reason = %d\n", reason);
rc = icsf_to_ock_err(rc, reason);
}
goto done;
}
done:
if (mapping) {
bt_put_node_value(&icsf_data->objects, mapping);
mapping = NULL;
}
if ((is_length_only && rc != CKR_OK) ||
(!is_length_only && rc != CKR_BUFFER_TOO_SMALL))
free_encr_ctx(decr_ctx);
return rc;
}
/*
* Get the attribute values for a list of attributes.
*/
CK_RV icsftok_get_attribute_value(STDLL_TokData_t * tokdata,
SESSION * sess, CK_OBJECT_HANDLE handle,
CK_ATTRIBUTE * pTemplate, CK_ULONG ulCount,
CK_ULONG * obj_size)
{
icsf_private_data_t *icsf_data = tokdata->private_data;
CK_RV rc = CKR_OK;
CK_BBOOL priv_obj;
struct session_state *session_state;
struct icsf_object_mapping *mapping = NULL;
int reason = 0;
CK_ATTRIBUTE priv_attr[] = {
{CKA_PRIVATE, &priv_obj, sizeof(priv_obj)}
,
};
/* Get session state */
if (!(session_state = get_session_state(tokdata, sess->handle))) {
TRACE_ERROR("%s\n", ock_err(ERR_SESSION_HANDLE_INVALID));
return CKR_SESSION_HANDLE_INVALID;
}
/* check ldap handle */
if (session_state->ld == NULL) {
TRACE_ERROR("No LDAP handle.\n");
return CKR_FUNCTION_FAILED;
}
/* get the object handle */
mapping = bt_get_node_value(&icsf_data->objects, handle);
if (!mapping) {
TRACE_ERROR("%s\n", ock_err(ERR_OBJECT_HANDLE_INVALID));
rc = CKR_OBJECT_HANDLE_INVALID;
goto done;
}
/* get the private attribute so we can check the permissions */
rc = icsf_get_attribute(session_state->ld, &reason,
&mapping->icsf_object, priv_attr, 1);
if (rc != CKR_OK) {
TRACE_DEVEL("icsf_get_attribute failed\n");
rc = icsf_to_ock_err(rc, reason);
goto done;
}
if (priv_obj == TRUE) {
if (sess->session_info.state == CKS_RO_PUBLIC_SESSION ||
sess->session_info.state == CKS_RW_PUBLIC_SESSION) {
TRACE_ERROR("%s\n", ock_err(ERR_USER_NOT_LOGGED_IN));
rc = CKR_USER_NOT_LOGGED_IN;
goto done;
}
}
// get requested attributes and values if the obj_size ptr is not set
if (!obj_size) {
/* Now call icsf to get the attribute values */
rc = icsf_get_attribute(session_state->ld, &reason,
&mapping->icsf_object, pTemplate, ulCount);
if (rc != CKR_OK) {
TRACE_DEVEL("icsf_get_attribute failed\n");
rc = icsf_to_ock_err(rc, reason);
}
} else {
/* if size is specified get the object size from remote end */
rc = icsf_get_object_size(session_state->ld, &reason,
&mapping->icsf_object, ulCount, obj_size);
if (rc != CKR_OK) {
TRACE_DEVEL("icsf_get_object_size failed\n");
rc = icsf_to_ock_err(rc, reason);
}
}
done:
if (mapping) {
bt_put_node_value(&icsf_data->objects, mapping);
mapping = NULL;
}
return rc;
}
/*
* Set attribute values for a list of attributes.
*/
CK_RV icsftok_set_attribute_value(STDLL_TokData_t * tokdata,
SESSION * sess, CK_OBJECT_HANDLE handle,
CK_ATTRIBUTE * pTemplate, CK_ULONG ulCount)
{
icsf_private_data_t *icsf_data = tokdata->private_data;
struct session_state *session_state;
struct icsf_object_mapping *mapping = NULL;
CK_BBOOL is_priv;
CK_BBOOL is_token;
CK_RV rc = CKR_OK;
int reason = 0;
CK_ATTRIBUTE priv_attrs[] = {
{CKA_PRIVATE, &is_priv, sizeof(is_priv)}
,
{CKA_TOKEN, &is_token, sizeof(is_token)}
,
};
/* Get session state */
if (!(session_state = get_session_state(tokdata, sess->handle))) {
TRACE_ERROR("%s\n", ock_err(ERR_SESSION_HANDLE_INVALID));
return CKR_SESSION_HANDLE_INVALID;
}
/* check ldap handle */
if (session_state->ld == NULL) {
TRACE_ERROR("No LDAP handle.\n");
return CKR_FUNCTION_FAILED;
}
/* get the object handle */
mapping = bt_get_node_value(&icsf_data->objects, handle);
if (!mapping) {
TRACE_ERROR("%s\n", ock_err(ERR_OBJECT_HANDLE_INVALID));
rc = CKR_OBJECT_HANDLE_INVALID;
goto done;
}
/* check permissions :
* first get CKA_PRIVATE since we need to check againse session
* icsf will check if the attributes are modifiable
*/
rc = icsf_get_attribute(session_state->ld, &reason,
&mapping->icsf_object, priv_attrs, 2);
if (rc != CKR_OK) {
TRACE_DEVEL("icsf_get_attribute failed\n");
rc = icsf_to_ock_err(rc, reason);
goto done;
}
/* Check permissions based on attributes and session */
rc = check_session_permissions(sess, priv_attrs, 2);
if (rc != CKR_OK) {
TRACE_DEVEL("check_session_permissions failed\n");
goto done;
}
/* Now call into icsf to set the attribute values */
rc = icsf_set_attribute(session_state->ld, &reason,
&mapping->icsf_object, pTemplate, ulCount);
if (rc != CKR_OK) {
TRACE_ERROR("icsf_set_attribute failed\n");
rc = icsf_to_ock_err(rc, reason);
goto done;
}
done:
if (mapping) {
bt_put_node_value(&icsf_data->objects, mapping);
mapping = NULL;
}
return rc;
}
/*
* Initialize a search for token and session objects that match a template.
*/
CK_RV icsftok_find_objects_init(STDLL_TokData_t * tokdata, SESSION * sess,
CK_ATTRIBUTE * pTemplate, CK_ULONG ulCount)
{
icsf_private_data_t *icsf_data = tokdata->private_data;
char token_name[sizeof(tokdata->nv_token_data->token_info.label) + 1];
struct session_state *session_state;
struct icsf_object_record records[MAX_RECORDS];
struct icsf_object_record *previous = NULL;
size_t records_len;
unsigned int i, j;
int node_number, rc;
int reason = 0;
CK_RV rv = CKR_OK;
/* Whether we retrieve public or private objects is determined by
* the caller's SAF authority on the token, something ock doesn't
* control.
* Since an app MUST have authenticated to ICSF token to use it,
* we can always assume it is an authenticated session and anything else
* is an error.
*/
if (sess->session_info.state == CKS_RO_PUBLIC_SESSION ||
sess->session_info.state == CKS_RW_PUBLIC_SESSION ||
sess->session_info.state == CKS_RW_SO_FUNCTIONS) {
TRACE_ERROR("You must authenticate to access ICSF token.\n");
return CKR_FUNCTION_FAILED;
}
/* Initialize the found object list. In keeping with other tokens,
* if the list does not exist, allocate list big enough for MAX_RECORD
* handles. reallocate later if more needed.
*/
if (sess->find_list == NULL) {
sess->find_list =
(CK_OBJECT_HANDLE *) malloc(10 * sizeof(CK_OBJECT_HANDLE));
if (!sess->find_list) {
TRACE_ERROR("%s\n", ock_err(ERR_HOST_MEMORY));
return CKR_HOST_MEMORY;
}
sess->find_len = 10;
}
memset(sess->find_list, 0x0, sess->find_len * sizeof(CK_OBJECT_HANDLE));
sess->find_count = 0;
sess->find_idx = 0;
/* Prepare to query ICSF for list objects
* Copy token name from shared memory
*/
rc = XProcLock(tokdata);
if (rc != CKR_OK) {
TRACE_ERROR("Failed to get process lock.\n");
return rc;
}
strunpad(token_name, (const char *)tokdata->nv_token_data->token_info.label,
sizeof(tokdata->nv_token_data->token_info.label), ' ');
rc = XProcUnLock(tokdata);
if (rc != CKR_OK) {
TRACE_ERROR("Failed to release process lock.\n");
return rc;
}
/* Get session state */
if (!(session_state = get_session_state(tokdata, sess->handle))) {
TRACE_ERROR("%s\n", ock_err(ERR_SESSION_HANDLE_INVALID));
return CKR_SESSION_HANDLE_INVALID;
}
/* check ldap handle */
if (session_state->ld == NULL) {
TRACE_ERROR("No LDAP handle.\n");
return CKR_FUNCTION_FAILED;
}
/* clear out records */
memset(records, 0, MAX_RECORDS * (sizeof(struct icsf_object_record)));
do {
records_len = sizeof(records) / sizeof(struct icsf_object_record);
rc = icsf_list_objects(session_state->ld, &reason, token_name,
ulCount, pTemplate, previous, records,
&records_len, 0);
if (ICSF_RC_IS_ERROR(rc)) {
TRACE_DEVEL("Failed to list objects.\n");
rv = icsf_to_ock_err(rc, reason);
goto done;
}
/* Now step thru the object btree so we can find the node
* value for any matching objects we retrieved from ICSF.
* If we cannot find a matching object in the btree,
* then add it so we can get a node value.
* And also because ICSF object database is authoritative.
*/
for (i = 0; i < records_len; i++) {
/* mark not found */
node_number = 0;
for (j = 1; j <= icsf_data->objects.size; j++) {
struct icsf_object_mapping *mapping = NULL;
/* skip missing ids */
mapping = bt_get_node_value(&icsf_data->objects, j);
if (mapping) {
if (memcmp(&records[i],
&mapping->icsf_object,
sizeof(struct icsf_object_record)) == 0) {
node_number = j;
bt_put_node_value(&icsf_data->objects, mapping);
mapping = NULL;
break;
}
bt_put_node_value(&icsf_data->objects, mapping);
mapping = NULL;
} else {
continue;
}
}
/* if could not find in our object tree, then add it
* since ICSF object database is authoritative.
*/
if (!node_number) {
struct icsf_object_mapping *new_mapping;
if (!(new_mapping = malloc(sizeof(*new_mapping)))) {
TRACE_ERROR("%s\n", ock_err(ERR_HOST_MEMORY));
rv = CKR_HOST_MEMORY;
goto done;
}
new_mapping->session_id = sess->handle;
new_mapping->icsf_object = records[i];
if (!(node_number = bt_node_add(&icsf_data->objects,
new_mapping))) {
TRACE_ERROR("Failed to add object to " "binary tree.\n");
rv = CKR_FUNCTION_FAILED;
goto done;
}
}
/* Add to our findobject list */
if (node_number) {
sess->find_list[sess->find_count] = node_number;
sess->find_count++;
if (sess->find_count >= sess->find_len) {
void *find_list;
size_t find_len = sess->find_len + MAX_RECORDS;
find_list = realloc(sess->find_list,
find_len * sizeof(CK_OBJECT_HANDLE));
if (!find_list) {
TRACE_ERROR("%s\n", ock_err(ERR_HOST_MEMORY));
rv = CKR_HOST_MEMORY;
goto done;
}
sess->find_list = find_list;
sess->find_len = find_len;
}
}
}
if (records_len)
previous = &records[records_len - 1];
} while (records_len);
sess->find_active = TRUE;
done:
return rv;
}
/*
* Destroy an object.
*/
CK_RV icsftok_destroy_object(STDLL_TokData_t * tokdata, SESSION * sess,
CK_OBJECT_HANDLE handle)
{
icsf_private_data_t *icsf_data = tokdata->private_data;
struct session_state *session_state;
struct icsf_object_mapping *mapping = NULL;
int reason;
CK_RV rc = CKR_OK;
/* Get session state */
if (!(session_state = get_session_state(tokdata, sess->handle))) {
TRACE_ERROR("%s\n", ock_err(ERR_SESSION_HANDLE_INVALID));
return CKR_SESSION_HANDLE_INVALID;;
}
/* check ldap handle */
if (session_state->ld == NULL) {
TRACE_ERROR("No LDAP handle.\n");
return CKR_FUNCTION_FAILED;
}
/* get the object handle */
mapping = bt_get_node_value(&icsf_data->objects, handle);
if (!mapping) {
TRACE_ERROR("%s\n", ock_err(ERR_OBJECT_HANDLE_INVALID));
rc = CKR_OBJECT_HANDLE_INVALID;
goto done;
}
/* Now remove the object from ICSF */
rc = icsf_destroy_object(session_state->ld, &reason, &mapping->icsf_object);
if (rc != 0) {
TRACE_DEVEL("icsf_destroy_object failed\n");
rc = CKR_FUNCTION_FAILED;
goto done;
}
bt_put_node_value(&icsf_data->objects, mapping);
mapping = NULL;
/* Now remove the object from the object btree */
bt_node_free(&icsf_data->objects, handle, TRUE);
done:
if (mapping) {
bt_put_node_value(&icsf_data->objects, mapping);
mapping = NULL;
}
return rc;
}
/*
* Free all data pointed by SIGN_VERIFY_CONTEXT and set everything to zero.
*/
static void free_sv_ctx(SIGN_VERIFY_CONTEXT * ctx)
{
struct icsf_multi_part_context *multi_part_ctx;
if (!ctx)
return;
/* Initialize encryption context */
multi_part_ctx = (struct icsf_multi_part_context *) ctx->context;
if (multi_part_ctx) {
if (multi_part_ctx->data)
free(multi_part_ctx->data);
free(multi_part_ctx);
}
if (ctx->mech.pParameter)
free(ctx->mech.pParameter);
memset(ctx, 0, sizeof(*ctx));
}
/*
* get the hash size for hmacs.
*/
int get_signverify_len(CK_MECHANISM mech)
{
switch (mech.mechanism) {
case CKM_MD5_HMAC:
case CKM_SSL3_MD5_MAC:
return MD5_HASH_SIZE;
case CKM_SHA_1_HMAC:
case CKM_SSL3_SHA1_MAC:
return SHA1_HASH_SIZE;
case CKM_SHA256_HMAC:
return SHA256_HASH_SIZE;
case CKM_SHA384_HMAC:
return SHA384_HASH_SIZE;
case CKM_SHA512_HMAC:
return SHA512_HASH_SIZE;
}
return -1;
}
CK_RV icsftok_sign_init(STDLL_TokData_t * tokdata,
SESSION * session, CK_MECHANISM * mech,
CK_OBJECT_HANDLE key)
{
icsf_private_data_t *icsf_data = tokdata->private_data;
struct session_state *session_state;
SIGN_VERIFY_CONTEXT *ctx = &session->sign_ctx;
struct icsf_multi_part_context *multi_part_ctx = NULL;
struct icsf_object_mapping *mapping = NULL;
CK_RV rc = CKR_OK;
CK_BBOOL multi = FALSE;
CK_BBOOL datacaching = FALSE;
CK_MAC_GENERAL_PARAMS *param;
/* Check session */
if (!(session_state = get_session_state(tokdata, session->handle))) {
TRACE_ERROR("%s\n", ock_err(ERR_SESSION_HANDLE_INVALID));
return CKR_SESSION_HANDLE_INVALID;
}
/* Check if key exists */
if (!(mapping = bt_get_node_value(&icsf_data->objects, key))) {
TRACE_ERROR("%s\n", ock_err(ERR_KEY_HANDLE_INVALID));
rc = CKR_KEY_HANDLE_INVALID;
return rc;
}
/* Check the mechanism info */
switch (mech->mechanism) {
case CKM_RSA_X_509:
case CKM_RSA_PKCS:
case CKM_DSA:
case CKM_ECDSA:
/* these do not do multipart and do not require
* a mechanism parameter.
*/
if (mech->ulParameterLen != 0) {
TRACE_ERROR("%s\n", ock_err(ERR_MECHANISM_PARAM_INVALID));
return CKR_MECHANISM_PARAM_INVALID;
}
multi = FALSE;
break;
case CKM_MD5_HMAC:
case CKM_SHA_1_HMAC:
case CKM_SHA256_HMAC:
case CKM_SHA384_HMAC:
case CKM_SHA512_HMAC:
/* hmacs can do mulitpart and do not require a
* mechanism parameter.
*/
if (mech->ulParameterLen != 0) {
TRACE_ERROR("%s\n", ock_err(ERR_MECHANISM_PARAM_INVALID));
return CKR_MECHANISM_PARAM_INVALID;
}
multi = TRUE;
break;
case CKM_SSL3_MD5_MAC:
case CKM_SSL3_SHA1_MAC:
/* can do mulitpart and take a mech parameter */
param = (CK_MAC_GENERAL_PARAMS *) mech->pParameter;
if (mech->ulParameterLen != sizeof(CK_MAC_GENERAL_PARAMS)) {
TRACE_ERROR("%s\n", ock_err(ERR_MECHANISM_PARAM_INVALID));
return CKR_MECHANISM_PARAM_INVALID;
}
if (((mech->mechanism == CKM_SSL3_MD5_MAC) && (*param != 16)) ||
((mech->mechanism == CKM_SSL3_SHA1_MAC) && (*param != 20))) {
TRACE_ERROR("%s\n", ock_err(ERR_MECHANISM_PARAM_INVALID));
return CKR_MECHANISM_PARAM_INVALID;
}
multi = TRUE;
break;
case CKM_MD5_RSA_PKCS:
case CKM_SHA1_RSA_PKCS:
case CKM_SHA256_RSA_PKCS:
case CKM_SHA384_RSA_PKCS:
case CKM_SHA512_RSA_PKCS:
case CKM_DSA_SHA1:
case CKM_ECDSA_SHA1:
/* these can do mulitpart and require data caching
* and do not require a mechanism parameter.
*/
if (mech->ulParameterLen != 0) {
TRACE_ERROR("%s\n", ock_err(ERR_MECHANISM_PARAM_INVALID));
return CKR_MECHANISM_PARAM_INVALID;
}
multi = TRUE;
datacaching = TRUE;
break;
default:
TRACE_ERROR("%s\n", ock_err(ERR_MECHANISM_INVALID));
return CKR_MECHANISM_INVALID;
}
/* Initialize sign context */
free_sv_ctx(ctx);
/* Copy mechanism */
if (mech->pParameter == NULL || mech->ulParameterLen == 0) {
ctx->mech.ulParameterLen = 0;
ctx->mech.pParameter = NULL;
} else {
ctx->mech.pParameter = malloc(mech->ulParameterLen);
if (!ctx->mech.pParameter) {
TRACE_ERROR("%s\n", ock_err(ERR_HOST_MEMORY));
rc = CKR_HOST_MEMORY;
goto done;
}
ctx->mech.ulParameterLen = mech->ulParameterLen;
memcpy(ctx->mech.pParameter, mech->pParameter, mech->ulParameterLen);
}
ctx->mech.mechanism = mech->mechanism;
/* If the mechanism supports multipart, prepare ctx */
if (multi) {
/* Allocate context for multi-part operations */
if (!(multi_part_ctx = malloc(sizeof(*multi_part_ctx)))) {
TRACE_ERROR("%s\n", ock_err(ERR_HOST_MEMORY));
rc = CKR_HOST_MEMORY;
goto done;
}
ctx->context_len = sizeof(*multi_part_ctx);
ctx->context = (void *) multi_part_ctx;
memset(multi_part_ctx, 0, sizeof(*multi_part_ctx));
/* keep a cache to ensure multiple of blocksize
* is sent to ICSF.
*/
if (datacaching) {
size_t blocksize;
rc = icsf_block_size(mech->mechanism, &blocksize);
if (rc != CKR_OK)
goto done;
multi_part_ctx->data_len = blocksize;
multi_part_ctx->data = malloc(multi_part_ctx->data_len);
if (!multi_part_ctx->data) {
TRACE_ERROR("%s\n", ock_err(ERR_HOST_MEMORY));
rc = CKR_HOST_MEMORY;
goto done;
}
memset(multi_part_ctx->data, 0, blocksize);
}
} else {
ctx->context_len = 0;
ctx->context = NULL;
}
ctx->key = key;
ctx->multi = FALSE;
ctx->active = TRUE;
done:
if (mapping) {
bt_put_node_value(&icsf_data->objects, mapping);
mapping = NULL;
}
if (rc != CKR_OK)
free_sv_ctx(ctx);
return rc;
}
CK_RV icsftok_sign(STDLL_TokData_t * tokdata,
SESSION * session, CK_BYTE * in_data, CK_ULONG in_data_len,
CK_BYTE * signature, CK_ULONG * sig_len)
{
icsf_private_data_t *icsf_data = tokdata->private_data;
struct session_state *session_state;
SIGN_VERIFY_CONTEXT *ctx = &session->sign_ctx;
struct icsf_object_mapping *mapping = NULL;
char chain_data[ICSF_CHAINING_DATA_LEN] = { 0, };
size_t chain_data_len = sizeof(chain_data);
CK_RV rc = CKR_OK;
int hlen, reason;
CK_BBOOL length_only = (signature == NULL);
if (ctx->multi == TRUE) {
TRACE_ERROR("%s\n", ock_err(ERR_OPERATION_ACTIVE));
rc = CKR_OPERATION_ACTIVE;
goto done;
}
/* Check session */
if (!(session_state = get_session_state(tokdata, session->handle))) {
TRACE_ERROR("%s\n", ock_err(ERR_SESSION_HANDLE_INVALID));
rc = CKR_SESSION_HANDLE_INVALID;
goto done;
}
/* check ldap handle */
if (session_state->ld == NULL) {
TRACE_ERROR("No LDAP handle.\n");
rc = CKR_FUNCTION_FAILED;
goto done;
}
/* Check if key exists */
if (!(mapping = bt_get_node_value(&icsf_data->objects, ctx->key))) {
TRACE_ERROR("%s\n", ock_err(ERR_KEY_HANDLE_INVALID));
rc = CKR_KEY_HANDLE_INVALID;
goto done;
}
switch (ctx->mech.mechanism) {
case CKM_MD5_HMAC:
case CKM_SHA_1_HMAC:
case CKM_SHA256_HMAC:
case CKM_SHA384_HMAC:
case CKM_SHA512_HMAC:
case CKM_SSL3_MD5_MAC:
case CKM_SSL3_SHA1_MAC:
if (length_only) {
hlen = get_signverify_len(ctx->mech);
if (hlen < 0) {
TRACE_ERROR("%s\n", ock_err(ERR_MECHANISM_INVALID));
rc = CKR_MECHANISM_INVALID;
goto done;
}
*sig_len = hlen;
rc = CKR_OK;
goto done;
}
rc = icsf_hmac_sign(session_state->ld, &reason,
&mapping->icsf_object, &ctx->mech, "ONLY",
(char *)in_data, in_data_len,
(char *)signature, sig_len,
chain_data, &chain_data_len);
if (rc != 0)
rc = icsf_to_ock_err(rc, reason);
break;
case CKM_RSA_X_509:
case CKM_RSA_PKCS:
case CKM_DSA:
case CKM_ECDSA:
rc = icsf_private_key_sign(session_state->ld, &reason, FALSE,
&mapping->icsf_object, &ctx->mech,
(char *)in_data, in_data_len,
(char *)signature, sig_len);
if (rc != 0) {
if (reason == ICSF_REASON_OUTPUT_PARAMETER_TOO_SHORT &&
length_only) {
rc = CKR_OK;
} else {
TRACE_DEVEL("icsf_private_key_sign failed\n");
rc = icsf_to_ock_err(rc, reason);
}
}
break;
case CKM_MD5_RSA_PKCS:
case CKM_SHA1_RSA_PKCS:
case CKM_SHA256_RSA_PKCS:
case CKM_SHA384_RSA_PKCS:
case CKM_SHA512_RSA_PKCS:
case CKM_DSA_SHA1:
case CKM_ECDSA_SHA1:
if (length_only) {
rc = CKR_OK;
goto done;
}
rc = icsf_hash_signverify(session_state->ld, &reason,
&mapping->icsf_object, &ctx->mech,
"ONLY", (char *)in_data, in_data_len,
(char *)signature, sig_len,
chain_data, &chain_data_len, 0);
if (rc != 0) {
if (reason == ICSF_REASON_OUTPUT_PARAMETER_TOO_SHORT &&
length_only) {
rc = CKR_OK;
} else {
TRACE_DEVEL("icsf_hash_signverify failed\n");
rc = icsf_to_ock_err(rc, reason);
}
}
break;
default:
TRACE_ERROR("%s\n", ock_err(ERR_MECHANISM_INVALID));
rc = CKR_MECHANISM_INVALID;
}
done:
if (mapping) {
bt_put_node_value(&icsf_data->objects, mapping);
mapping = NULL;
}
if (rc != CKR_BUFFER_TOO_SMALL && !(rc == CKR_OK && length_only))
free_sv_ctx(ctx);
return rc;
}
CK_RV icsftok_sign_update(STDLL_TokData_t * tokdata,
SESSION * session, CK_BYTE * in_data,
CK_ULONG in_data_len)
{
icsf_private_data_t *icsf_data = tokdata->private_data;
struct session_state *session_state;
SIGN_VERIFY_CONTEXT *ctx = &session->sign_ctx;
struct icsf_object_mapping *mapping = NULL;
struct icsf_multi_part_context *multi_part_ctx = NULL;
char chain_data[ICSF_CHAINING_DATA_LEN] = { 0, };
size_t chain_data_len = sizeof(chain_data);
CK_RV rc = CKR_OK;
int reason;
size_t siglen = 0;
CK_ULONG total, remain, out_len = 0;
char *buffer = NULL;
/* Check session */
if (!(session_state = get_session_state(tokdata, session->handle))) {
TRACE_ERROR("%s\n", ock_err(ERR_SESSION_HANDLE_INVALID));
rc = CKR_SESSION_HANDLE_INVALID;
goto done;
}
/* check ldap handle */
if (session_state->ld == NULL) {
TRACE_ERROR("No LDAP handle.\n");
rc = CKR_FUNCTION_FAILED;
goto done;
}
/* Check if key exists */
if (!(mapping = bt_get_node_value(&icsf_data->objects, ctx->key))) {
TRACE_ERROR("%s\n", ock_err(ERR_KEY_HANDLE_INVALID));
rc = CKR_KEY_HANDLE_INVALID;
goto done;
}
/* indicate this is multipart operation and get chain info from ctx.
* if any mechanisms that cannot do multipart sign come here, they
* will not have had ctx->context allocated and will
* get an error in switch below.
*/
ctx->multi = TRUE;
if (ctx->context) {
multi_part_ctx = (struct icsf_multi_part_context *) ctx->context;
if (multi_part_ctx->initiated)
memcpy(chain_data, multi_part_ctx->chain_data, chain_data_len);
} else {
TRACE_ERROR("%s\n", ock_err(ERR_ARGUMENTS_BAD));
rc = ERR_ARGUMENTS_BAD;
goto done;
}
switch (ctx->mech.mechanism) {
case CKM_MD5_HMAC:
case CKM_SHA_1_HMAC:
case CKM_SHA256_HMAC:
case CKM_SHA384_HMAC:
case CKM_SHA512_HMAC:
case CKM_SSL3_MD5_MAC:
case CKM_SSL3_SHA1_MAC:
rc = icsf_hmac_sign(session_state->ld, &reason,
&mapping->icsf_object, &ctx->mech,
(multi_part_ctx->initiated) ? "MIDDLE" : "FIRST",
(char *)in_data, in_data_len, NULL, &siglen,
chain_data, &chain_data_len);
if (rc != 0) {
TRACE_DEVEL("icsf_hmac_sign failed\n");
rc = icsf_to_ock_err(rc, reason);
} else {
multi_part_ctx->initiated = TRUE;
memcpy(multi_part_ctx->chain_data, chain_data, chain_data_len);
}
break;
case CKM_MD5_RSA_PKCS:
case CKM_SHA1_RSA_PKCS:
case CKM_SHA256_RSA_PKCS:
case CKM_SHA384_RSA_PKCS:
case CKM_SHA512_RSA_PKCS:
case CKM_DSA_SHA1:
case CKM_ECDSA_SHA1:
/* caching data since ICSF wants in multiple of blocksize */
if (multi_part_ctx && multi_part_ctx->data) {
total = multi_part_ctx->used_data_len + in_data_len;
remain = total % multi_part_ctx->data_len;;
/* if not enough to meet blocksize, cache and exit. */
if (total < multi_part_ctx->data_len) {
memcpy(multi_part_ctx->data + multi_part_ctx->used_data_len,
(char *)in_data, in_data_len);
multi_part_ctx->used_data_len += in_data_len;
rc = CKR_OK;
goto done;
} else {
/* there is at least 1 block */
out_len = total - remain;
/* prepare a buffer to send data in */
if (!(buffer = malloc(out_len))) {
TRACE_ERROR("%s\n", ock_err(ERR_HOST_MEMORY));
rc = CKR_HOST_MEMORY;
goto done;
}
memcpy(buffer, multi_part_ctx->data,
multi_part_ctx->used_data_len);
memcpy(buffer + multi_part_ctx->used_data_len,
(char *)in_data,
out_len - multi_part_ctx->used_data_len);
/* copy remainder of data to ctx
* for next time. caching.
*/
if (remain != 0)
memcpy(multi_part_ctx->data,
in_data + (in_data_len - remain), remain);
multi_part_ctx->used_data_len = remain;
}
}
rc = icsf_hash_signverify(session_state->ld, &reason,
&mapping->icsf_object, &ctx->mech,
(multi_part_ctx->
initiated) ? "MIDDLE" : "FIRST", buffer,
out_len, NULL, NULL, chain_data,
&chain_data_len, 0);
if (rc != 0) {
TRACE_DEVEL("icsf_hash_signverify failed\n");
rc = icsf_to_ock_err(rc, reason);
} else {
multi_part_ctx->initiated = TRUE;
memcpy(multi_part_ctx->chain_data, chain_data, chain_data_len);
}
if (buffer)
free(buffer);
break;
default:
TRACE_ERROR("%s\n", ock_err(ERR_MECHANISM_INVALID));
rc = CKR_MECHANISM_INVALID;
}
done:
if (mapping) {
bt_put_node_value(&icsf_data->objects, mapping);
mapping = NULL;
}
if (rc != CKR_OK)
free_sv_ctx(ctx);
return rc;
}
CK_RV icsftok_sign_final(STDLL_TokData_t * tokdata,
SESSION * session, CK_BYTE * signature,
CK_ULONG * sig_len)
{
icsf_private_data_t *icsf_data = tokdata->private_data;
struct session_state *session_state;
SIGN_VERIFY_CONTEXT *ctx = &session->sign_ctx;
struct icsf_object_mapping *mapping = NULL;
struct icsf_multi_part_context *multi_part_ctx = NULL;
char chain_data[ICSF_CHAINING_DATA_LEN] = { 0, };
size_t chain_data_len = sizeof(chain_data);
char *buffer = NULL;
CK_RV rc = CKR_OK;
int hlen, reason;
CK_BBOOL length_only = (signature == NULL);
/* Check session */
if (!(session_state = get_session_state(tokdata, session->handle))) {
TRACE_ERROR("%s\n", ock_err(ERR_SESSION_HANDLE_INVALID));
rc = CKR_SESSION_HANDLE_INVALID;
goto done;
}
/* check ldap handle */
if (session_state->ld == NULL) {
TRACE_ERROR("No LDAP handle.\n");
rc = CKR_FUNCTION_FAILED;
goto done;
}
/* Check if key exists */
if (!(mapping = bt_get_node_value(&icsf_data->objects, ctx->key))) {
TRACE_ERROR("%s\n", ock_err(ERR_KEY_HANDLE_INVALID));
rc = CKR_KEY_HANDLE_INVALID;
goto done;
}
/* get the chain data from ctx */
if (ctx->context) {
multi_part_ctx = (struct icsf_multi_part_context *) ctx->context;
memcpy(chain_data, multi_part_ctx->chain_data, chain_data_len);
} else {
TRACE_ERROR("%s\n", ock_err(ERR_ARGUMENTS_BAD));
rc = ERR_ARGUMENTS_BAD;
goto done;
}
switch (ctx->mech.mechanism) {
case CKM_MD5_HMAC:
case CKM_SHA_1_HMAC:
case CKM_SHA256_HMAC:
case CKM_SHA384_HMAC:
case CKM_SHA512_HMAC:
case CKM_SSL3_MD5_MAC:
case CKM_SSL3_SHA1_MAC:
if (length_only) {
hlen = get_signverify_len(ctx->mech);
if (hlen < 0) {
TRACE_ERROR("%s\n", ock_err(ERR_MECHANISM_INVALID));
return CKR_MECHANISM_INVALID;
}
*sig_len = hlen;
return CKR_OK;
}
rc = icsf_hmac_sign(session_state->ld, &reason,
&mapping->icsf_object, &ctx->mech,
multi_part_ctx->initiated ? "LAST" : "ONLY", "",
0, (char *)signature, sig_len,
chain_data, &chain_data_len);
if (rc != 0)
rc = icsf_to_ock_err(rc, reason);
break;
case CKM_MD5_RSA_PKCS:
case CKM_SHA1_RSA_PKCS:
case CKM_SHA256_RSA_PKCS:
case CKM_SHA384_RSA_PKCS:
case CKM_SHA512_RSA_PKCS:
case CKM_DSA_SHA1:
case CKM_ECDSA_SHA1:
if (length_only) {
rc = CKR_OK;
goto done;
}
/* see if any data left in the cache */
if (multi_part_ctx && multi_part_ctx->used_data_len) {
if (!(buffer = malloc(multi_part_ctx->used_data_len))) {
TRACE_ERROR("%s\n", ock_err(ERR_HOST_MEMORY));
rc = CKR_HOST_MEMORY;
goto done;
}
memcpy(buffer, multi_part_ctx->data, multi_part_ctx->used_data_len);
}
rc = icsf_hash_signverify(session_state->ld, &reason,
&mapping->icsf_object, &ctx->mech,
multi_part_ctx->initiated ? "LAST" : "ONLY",
(buffer) ? buffer : NULL,
multi_part_ctx->used_data_len,
(char *)signature, sig_len,
chain_data, &chain_data_len, 0);
if (rc != 0) {
if (length_only && reason == 3003) {
rc = CKR_OK;
} else {
TRACE_DEVEL("icsf_hash_signverify failed\n");
rc = icsf_to_ock_err(rc, reason);
}
}
if (buffer)
free(buffer);
break;
default:
TRACE_ERROR("%s\n", ock_err(ERR_MECHANISM_INVALID));
rc = CKR_MECHANISM_INVALID;
}
done:
if (mapping) {
bt_put_node_value(&icsf_data->objects, mapping);
mapping = NULL;
}
if (rc != CKR_BUFFER_TOO_SMALL && !(rc == CKR_OK && length_only))
free_sv_ctx(ctx);
return rc;
}
CK_RV icsftok_verify_init(STDLL_TokData_t * tokdata,
SESSION * session, CK_MECHANISM * mech,
CK_OBJECT_HANDLE key)
{
icsf_private_data_t *icsf_data = tokdata->private_data;
struct session_state *session_state;
SIGN_VERIFY_CONTEXT *ctx = &session->verify_ctx;
struct icsf_multi_part_context *multi_part_ctx = NULL;
struct icsf_object_mapping *mapping = NULL;
CK_RV rc = CKR_OK;
CK_BBOOL multi = FALSE;
CK_BBOOL datacaching = FALSE;
CK_MAC_GENERAL_PARAMS *param;
/* Check session */
if (!(session_state = get_session_state(tokdata, session->handle))) {
TRACE_ERROR("%s\n", ock_err(ERR_SESSION_HANDLE_INVALID));
return CKR_SESSION_HANDLE_INVALID;
}
/* Check if key exists */
if (!(mapping = bt_get_node_value(&icsf_data->objects, key))) {
TRACE_ERROR("%s\n", ock_err(ERR_KEY_HANDLE_INVALID));
rc = CKR_KEY_HANDLE_INVALID;
return rc;
}
/* Check the mechanism info */
switch (mech->mechanism) {
case CKM_RSA_X_509:
case CKM_RSA_PKCS:
case CKM_DSA:
case CKM_ECDSA:
/* these do not do multipart and do not require
* a mechanism parameter.
*/
if (mech->ulParameterLen != 0) {
TRACE_ERROR("%s\n", ock_err(ERR_MECHANISM_PARAM_INVALID));
rc = CKR_MECHANISM_PARAM_INVALID;
goto done;
}
multi = FALSE;
break;
case CKM_MD5_HMAC:
case CKM_SHA_1_HMAC:
case CKM_SHA256_HMAC:
case CKM_SHA384_HMAC:
case CKM_SHA512_HMAC:
/* hmacs can do mulitpart and do not require a
* mechanism parameter.
*/
if (mech->ulParameterLen != 0) {
TRACE_ERROR("%s\n", ock_err(ERR_MECHANISM_PARAM_INVALID));
rc = CKR_MECHANISM_PARAM_INVALID;
goto done;
}
multi = TRUE;
break;
case CKM_SSL3_MD5_MAC:
case CKM_SSL3_SHA1_MAC:
/* can do mulitpart and take a mech parameter */
param = (CK_MAC_GENERAL_PARAMS *) mech->pParameter;
if (mech->ulParameterLen != sizeof(CK_MAC_GENERAL_PARAMS)) {
TRACE_ERROR("%s\n", ock_err(ERR_MECHANISM_PARAM_INVALID));
rc = CKR_MECHANISM_PARAM_INVALID;
goto done;
}
if (((mech->mechanism == CKM_SSL3_MD5_MAC) && (*param != 16)) ||
((mech->mechanism == CKM_SSL3_SHA1_MAC) && (*param != 20))) {
TRACE_ERROR("%s\n", ock_err(ERR_MECHANISM_PARAM_INVALID));
rc = CKR_MECHANISM_PARAM_INVALID;
goto done;
}
multi = TRUE;
break;
case CKM_MD5_RSA_PKCS:
case CKM_SHA1_RSA_PKCS:
case CKM_SHA256_RSA_PKCS:
case CKM_SHA384_RSA_PKCS:
case CKM_SHA512_RSA_PKCS:
case CKM_DSA_SHA1:
case CKM_ECDSA_SHA1:
/* these can do mulitpart and require data caching
* but do not require a mechanism parameter
*/
if (mech->ulParameterLen != 0) {
TRACE_ERROR("%s\n", ock_err(ERR_MECHANISM_PARAM_INVALID));
rc = CKR_MECHANISM_PARAM_INVALID;
goto done;
}
multi = TRUE;
datacaching = TRUE;
break;
default:
TRACE_ERROR("%s\n", ock_err(ERR_MECHANISM_INVALID));
rc = CKR_MECHANISM_INVALID;
goto done;
}
/* Initialize ctx */
free_sv_ctx(ctx);
/* Copy mechanism */
if (mech->pParameter == NULL || mech->ulParameterLen == 0) {
ctx->mech.ulParameterLen = 0;
ctx->mech.pParameter = NULL;
} else {
ctx->mech.pParameter = malloc(mech->ulParameterLen);
if (!ctx->mech.pParameter) {
TRACE_ERROR("%s\n", ock_err(ERR_HOST_MEMORY));
rc = CKR_HOST_MEMORY;
goto done;
}
ctx->mech.ulParameterLen = mech->ulParameterLen;
memcpy(ctx->mech.pParameter, mech->pParameter, mech->ulParameterLen);
}
ctx->mech.mechanism = mech->mechanism;
/* If the mechanism supports multipart, prepare ctx */
if (multi) {
/* Allocate context for multi-part operations */
if (!(multi_part_ctx = malloc(sizeof(*multi_part_ctx)))) {
TRACE_ERROR("%s\n", ock_err(ERR_HOST_MEMORY));
rc = CKR_HOST_MEMORY;
goto done;
}
ctx->context_len = sizeof(*multi_part_ctx);
ctx->context = (void *) multi_part_ctx;
memset(multi_part_ctx, 0, sizeof(*multi_part_ctx));
/* keep a cache to ensure multiple of blocksize
* is sent to ICSF.
*/
if (datacaching) {
size_t blocksize;
rc = icsf_block_size(mech->mechanism, &blocksize);
if (rc != CKR_OK)
goto done;
multi_part_ctx->data_len = blocksize;
multi_part_ctx->data = malloc(multi_part_ctx->data_len);
if (!multi_part_ctx->data) {
TRACE_ERROR("%s\n", ock_err(ERR_HOST_MEMORY));
rc = CKR_HOST_MEMORY;
goto done;
}
memset(multi_part_ctx->data, 0, blocksize);
}
} else {
ctx->context_len = 0;
ctx->context = NULL;
}
ctx->key = key;
ctx->multi = FALSE;
ctx->active = TRUE;
done:
if (mapping) {
bt_put_node_value(&icsf_data->objects, mapping);
mapping = NULL;
}
if (rc != CKR_OK)
free_sv_ctx(ctx);
return rc;
}
CK_RV icsftok_verify(STDLL_TokData_t * tokdata,
SESSION * session, CK_BYTE * in_data, CK_ULONG in_data_len,
CK_BYTE * signature, CK_ULONG sig_len)
{
icsf_private_data_t *icsf_data = tokdata->private_data;
struct session_state *session_state;
SIGN_VERIFY_CONTEXT *ctx = &session->verify_ctx;
struct icsf_object_mapping *mapping = NULL;
char chain_data[ICSF_CHAINING_DATA_LEN] = { 0, };
size_t chain_data_len = sizeof(chain_data);
CK_RV rc = CKR_OK;
int reason;
if (ctx->multi == TRUE) {
TRACE_ERROR("%s\n", ock_err(ERR_OPERATION_ACTIVE));
rc = CKR_OPERATION_ACTIVE;
goto done;
}
/* Check session */
if (!(session_state = get_session_state(tokdata, session->handle))) {
TRACE_ERROR("%s\n", ock_err(ERR_SESSION_HANDLE_INVALID));
rc = CKR_SESSION_HANDLE_INVALID;
goto done;
}
/* check ldap handle */
if (session_state->ld == NULL) {
TRACE_ERROR("No LDAP handle.\n");
rc = CKR_FUNCTION_FAILED;
goto done;
}
/* Check if key exists */
if (!(mapping = bt_get_node_value(&icsf_data->objects, ctx->key))) {
TRACE_ERROR("%s\n", ock_err(ERR_KEY_HANDLE_INVALID));
rc = CKR_KEY_HANDLE_INVALID;
goto done;
}
switch (ctx->mech.mechanism) {
case CKM_MD5_HMAC:
case CKM_SHA_1_HMAC:
case CKM_SHA256_HMAC:
case CKM_SHA384_HMAC:
case CKM_SHA512_HMAC:
case CKM_SSL3_MD5_MAC:
case CKM_SSL3_SHA1_MAC:
rc = icsf_hmac_verify(session_state->ld, &reason,
&mapping->icsf_object, &ctx->mech, "ONLY",
(char *)in_data, in_data_len,
(char *)signature, sig_len,
chain_data, &chain_data_len);
if (rc != 0)
rc = icsf_to_ock_err(rc, reason);
break;
case CKM_RSA_X_509:
case CKM_RSA_PKCS:
case CKM_DSA:
case CKM_ECDSA:
rc = icsf_public_key_verify(session_state->ld, &reason, FALSE,
&mapping->icsf_object, &ctx->mech,
(char *)in_data, in_data_len,
(char *)signature, &sig_len);
if (rc != 0)
rc = icsf_to_ock_err(rc, reason);
break;
case CKM_MD5_RSA_PKCS:
case CKM_SHA1_RSA_PKCS:
case CKM_SHA256_RSA_PKCS:
case CKM_SHA384_RSA_PKCS:
case CKM_SHA512_RSA_PKCS:
case CKM_DSA_SHA1:
case CKM_ECDSA_SHA1:
rc = icsf_hash_signverify(session_state->ld, &reason,
&mapping->icsf_object, &ctx->mech,
"ONLY", (char *)in_data, in_data_len,
(char *)signature, &sig_len,
chain_data, &chain_data_len, 1);
if (rc != 0)
rc = icsf_to_ock_err(rc, reason);
break;
default:
TRACE_ERROR("%s\n", ock_err(ERR_MECHANISM_INVALID));
rc = CKR_MECHANISM_INVALID;
}
done:
if (mapping) {
bt_put_node_value(&icsf_data->objects, mapping);
mapping = NULL;
}
free_sv_ctx(ctx);
return rc;
}
CK_RV icsftok_verify_update(STDLL_TokData_t * tokdata,
SESSION * session, CK_BYTE * in_data,
CK_ULONG in_data_len)
{
icsf_private_data_t *icsf_data = tokdata->private_data;
struct session_state *session_state;
SIGN_VERIFY_CONTEXT *ctx = &session->verify_ctx;
struct icsf_object_mapping *mapping = NULL;
struct icsf_multi_part_context *multi_part_ctx = NULL;
char chain_data[ICSF_CHAINING_DATA_LEN] = { 0, };
size_t chain_data_len = sizeof(chain_data);
CK_RV rc = CKR_OK;
int reason;
CK_ULONG total, remain, out_len = 0;
char *buffer = NULL;
/* Check session */
if (!(session_state = get_session_state(tokdata, session->handle))) {
TRACE_ERROR("%s\n", ock_err(ERR_SESSION_HANDLE_INVALID));
rc = CKR_SESSION_HANDLE_INVALID;
goto done;
}
/* check ldap handle */
if (session_state->ld == NULL) {
TRACE_ERROR("No LDAP handle.\n");
rc = CKR_FUNCTION_FAILED;
goto done;
}
/* Check if key exists */
if (!(mapping = bt_get_node_value(&icsf_data->objects, ctx->key))) {
TRACE_ERROR("%s\n", ock_err(ERR_KEY_HANDLE_INVALID));
rc = CKR_KEY_HANDLE_INVALID;
goto done;
}
/* indicate this is multipart operation and get chain info from ctx.
* if any mechanisms that cannot do multipart verify come here, they
* will get an error in switch below.
*/
ctx->multi = TRUE;
if (ctx->context) {
multi_part_ctx = (struct icsf_multi_part_context *) ctx->context;
if (multi_part_ctx->initiated)
memcpy(chain_data, multi_part_ctx->chain_data, chain_data_len);
} else {
TRACE_ERROR("%s\n", ock_err(ERR_ARGUMENTS_BAD));
rc = ERR_ARGUMENTS_BAD;
goto done;
}
switch (ctx->mech.mechanism) {
case CKM_MD5_HMAC:
case CKM_SHA_1_HMAC:
case CKM_SHA256_HMAC:
case CKM_SHA384_HMAC:
case CKM_SHA512_HMAC:
case CKM_SSL3_MD5_MAC:
case CKM_SSL3_SHA1_MAC:
rc = icsf_hmac_verify(session_state->ld, &reason,
&mapping->icsf_object, &ctx->mech,
(multi_part_ctx->initiated) ? "MIDDLE" : "FIRST",
(char *)in_data, in_data_len, "", 0,
chain_data, &chain_data_len);
if (rc != 0) {
TRACE_DEVEL("icsf_hmac_verify failed\n");
rc = icsf_to_ock_err(rc, reason);
} else {
multi_part_ctx->initiated = TRUE;
memcpy(multi_part_ctx->chain_data, chain_data, chain_data_len);
}
break;
case CKM_MD5_RSA_PKCS:
case CKM_SHA1_RSA_PKCS:
case CKM_SHA256_RSA_PKCS:
case CKM_SHA384_RSA_PKCS:
case CKM_SHA512_RSA_PKCS:
case CKM_DSA_SHA1:
case CKM_ECDSA_SHA1:
/* caching data since ICSF wants in multiple of blocksize */
if (multi_part_ctx && multi_part_ctx->data) {
total = multi_part_ctx->used_data_len + in_data_len;
remain = total % multi_part_ctx->data_len;;
/* if not enough to meet blocksize, cache and exit. */
if (total < multi_part_ctx->data_len) {
memcpy(multi_part_ctx->data + multi_part_ctx->used_data_len,
(char *)in_data, in_data_len);
multi_part_ctx->used_data_len += in_data_len;
rc = CKR_OK;
goto done;
} else {
/* there is at least 1 block */
out_len = total - remain;
/* prepare a buffer to send data in */
if (!(buffer = malloc(out_len))) {
TRACE_ERROR("%s\n", ock_err(ERR_HOST_MEMORY));
rc = CKR_HOST_MEMORY;
goto done;
}
memcpy(buffer, multi_part_ctx->data,
multi_part_ctx->used_data_len);
memcpy(buffer + multi_part_ctx->used_data_len,
(char *)in_data,
out_len - multi_part_ctx->used_data_len);
/* copy remainder of data to ctx
* for next time. caching.
*/
if (remain != 0)
memcpy(multi_part_ctx->data,
(char *)in_data + (in_data_len - remain), remain);
multi_part_ctx->used_data_len = remain;
}
}
rc = icsf_hash_signverify(session_state->ld, &reason,
&mapping->icsf_object, &ctx->mech,
(multi_part_ctx->
initiated) ? "MIDDLE" : "FIRST", buffer,
out_len, NULL, NULL, chain_data,
&chain_data_len, 1);
if (rc != 0) {
TRACE_DEVEL("icsf_hash_signverify failed\n");
rc = icsf_to_ock_err(rc, reason);
} else {
multi_part_ctx->initiated = TRUE;
memcpy(multi_part_ctx->chain_data, chain_data, chain_data_len);
}
if (buffer)
free(buffer);
break;
default:
TRACE_ERROR("%s\n", ock_err(ERR_MECHANISM_INVALID));
rc = CKR_MECHANISM_INVALID;
}
done:
if (mapping) {
bt_put_node_value(&icsf_data->objects, mapping);
mapping = NULL;
}
if (rc != CKR_OK)
free_sv_ctx(ctx);
return rc;
}
CK_RV icsftok_verify_final(STDLL_TokData_t * tokdata,
SESSION * session, CK_BYTE * signature,
CK_ULONG sig_len)
{
icsf_private_data_t *icsf_data = tokdata->private_data;
struct session_state *session_state;
SIGN_VERIFY_CONTEXT *ctx = &session->verify_ctx;
struct icsf_object_mapping *mapping = NULL;
struct icsf_multi_part_context *multi_part_ctx = NULL;
char chain_data[ICSF_CHAINING_DATA_LEN] = { 0, };
size_t chain_data_len = sizeof(chain_data);
CK_RV rc = CKR_OK;
int reason;
char *buffer = NULL;
if (!sig_len) {
TRACE_ERROR("%s\n", ock_err(ERR_ARGUMENTS_BAD));
rc = CKR_ARGUMENTS_BAD;
goto done;
}
/* Check session */
if (!(session_state = get_session_state(tokdata, session->handle))) {
TRACE_ERROR("%s\n", ock_err(ERR_SESSION_HANDLE_INVALID));
rc = CKR_SESSION_HANDLE_INVALID;
goto done;
}
/* check ldap handle */
if (session_state->ld == NULL) {
TRACE_ERROR("No LDAP handle.\n");
rc = CKR_FUNCTION_FAILED;
goto done;
}
/* Check if key exists */
if (!(mapping = bt_get_node_value(&icsf_data->objects, ctx->key))) {
TRACE_ERROR("%s\n", ock_err(ERR_KEY_HANDLE_INVALID));
rc = CKR_KEY_HANDLE_INVALID;
goto done;
}
/* get the chain data from ctx */
if (ctx->context) {
multi_part_ctx = (struct icsf_multi_part_context *) ctx->context;
memcpy(chain_data, multi_part_ctx->chain_data, chain_data_len);
} else {
TRACE_ERROR("%s\n", ock_err(ERR_ARGUMENTS_BAD));
rc = ERR_ARGUMENTS_BAD;
goto done;
}
switch (ctx->mech.mechanism) {
case CKM_MD5_HMAC:
case CKM_SHA_1_HMAC:
case CKM_SHA256_HMAC:
case CKM_SHA384_HMAC:
case CKM_SHA512_HMAC:
case CKM_SSL3_MD5_MAC:
case CKM_SSL3_SHA1_MAC:
/* get the chain data */
rc = icsf_hmac_verify(session_state->ld, &reason,
&mapping->icsf_object, &ctx->mech,
multi_part_ctx->initiated ? "LAST" : "ONLY", "",
0, (char *)signature, sig_len,
chain_data, &chain_data_len);
if (rc != 0)
rc = icsf_to_ock_err(rc, reason);
break;
case CKM_MD5_RSA_PKCS:
case CKM_SHA1_RSA_PKCS:
case CKM_SHA256_RSA_PKCS:
case CKM_SHA384_RSA_PKCS:
case CKM_SHA512_RSA_PKCS:
case CKM_DSA_SHA1:
case CKM_ECDSA_SHA1:
/* see if any data left in the cache */
if (multi_part_ctx && multi_part_ctx->used_data_len) {
if (!(buffer = malloc(multi_part_ctx->used_data_len))) {
TRACE_ERROR("%s\n", ock_err(ERR_HOST_MEMORY));
rc = CKR_HOST_MEMORY;
goto done;
}
memcpy(buffer, multi_part_ctx->data, multi_part_ctx->used_data_len);
}
rc = icsf_hash_signverify(session_state->ld, &reason,
&mapping->icsf_object, &ctx->mech,
multi_part_ctx->initiated ? "LAST" : "ONLY",
(buffer) ? buffer : NULL,
multi_part_ctx->used_data_len,
(char *)signature, &sig_len,
chain_data, &chain_data_len, 1);
if (rc != 0)
rc = icsf_to_ock_err(rc, reason);
if (buffer)
free(buffer);
break;
default:
TRACE_ERROR("%s\n", ock_err(ERR_MECHANISM_INVALID));
rc = CKR_MECHANISM_INVALID;
}
done:
if (mapping) {
bt_put_node_value(&icsf_data->objects, mapping);
mapping = NULL;
}
free_sv_ctx(ctx);
return rc;
}
/*
* Wrap a key and return it as binary data.
*/
CK_RV icsftok_wrap_key(STDLL_TokData_t * tokdata,
SESSION * session, CK_MECHANISM_PTR mech,
CK_OBJECT_HANDLE wrapping_key, CK_OBJECT_HANDLE key,
CK_BYTE_PTR wrapped_key, CK_ULONG_PTR p_wrapped_key_len)
{
icsf_private_data_t *icsf_data = tokdata->private_data;
int rc = CKR_OK;
int reason = 0;
struct session_state *session_state;
struct icsf_object_mapping *wrapping_key_mapping = NULL;
struct icsf_object_mapping *key_mapping = NULL;
size_t expected_block_size = 0;
/* Check session */
if (!(session_state = get_session_state(tokdata, session->handle))) {
TRACE_ERROR("%s\n", ock_err(ERR_SESSION_HANDLE_INVALID));
return CKR_SESSION_HANDLE_INVALID;
}
/* check ldap handle */
if (session_state->ld == NULL) {
TRACE_ERROR("No LDAP handle.\n");
return CKR_FUNCTION_FAILED;
}
/* Check if keys exist */
wrapping_key_mapping = bt_get_node_value(&icsf_data->objects, wrapping_key);
key_mapping = bt_get_node_value(&icsf_data->objects, key);
if (!wrapping_key_mapping || !key_mapping) {
TRACE_ERROR("%s\n", ock_err(ERR_KEY_HANDLE_INVALID));
rc = CKR_KEY_HANDLE_INVALID;
goto done;
}
/* validate mechanism parameters. Only 4 mechanisms support
* key wrapping in icsf token */
switch (mech->mechanism) {
case CKM_DES_CBC_PAD:
case CKM_DES3_CBC_PAD:
case CKM_AES_CBC_PAD:
if ((rc = icsf_block_size(mech->mechanism, &expected_block_size)))
goto done;
if (mech->ulParameterLen != expected_block_size) {
TRACE_ERROR("Invalid mechanism parameter length: %lu "
"(expected %lu)\n",
(unsigned long) mech->ulParameterLen,
(unsigned long) expected_block_size);
rc = CKR_MECHANISM_PARAM_INVALID;
goto done;
}
break;
case CKM_RSA_PKCS:
if (mech->ulParameterLen != 0) {
TRACE_ERROR("%s\n", ock_err(ERR_MECHANISM_PARAM_INVALID));
rc = CKR_MECHANISM_PARAM_INVALID;
goto done;
}
break;
default:
TRACE_ERROR("icsf invalid %lu mechanism for key wrapping\n",
mech->mechanism);
rc = CKR_MECHANISM_INVALID;
goto done;
}
/* Call ICSF service */
rc = icsf_wrap_key(session_state->ld, &reason, mech,
&wrapping_key_mapping->icsf_object,
&key_mapping->icsf_object, wrapped_key,
p_wrapped_key_len);
if (rc) {
TRACE_DEVEL("icsf_wrap_key failed\n");
rc = icsf_to_ock_err(rc, reason);
goto done;
}
done:
if (wrapping_key_mapping) {
bt_put_node_value(&icsf_data->objects, wrapping_key_mapping);
wrapping_key_mapping = NULL;
}
if (key_mapping) {
bt_put_node_value(&icsf_data->objects, key_mapping);
key_mapping = NULL;
}
return rc;
}
/*
* Unwrap a key from binary data and create a new key object.
*/
CK_RV icsftok_unwrap_key(STDLL_TokData_t * tokdata,
SESSION * session, CK_MECHANISM_PTR mech,
CK_ATTRIBUTE_PTR attrs, CK_ULONG attrs_len,
CK_BYTE_PTR wrapped_key, CK_ULONG wrapped_key_len,
CK_OBJECT_HANDLE wrapping_key,
CK_OBJECT_HANDLE_PTR p_key)
{
icsf_private_data_t *icsf_data = tokdata->private_data;
int rc;
int reason = 0;
struct session_state *session_state;
struct icsf_object_mapping *wrapping_key_mapping = NULL;
struct icsf_object_mapping *key_mapping = NULL;
CK_ULONG node_number;
size_t expected_block_size = 0;
/* Check session */
if (!(session_state = get_session_state(tokdata, session->handle))) {
TRACE_ERROR("%s\n", ock_err(ERR_SESSION_HANDLE_INVALID));
return CKR_SESSION_HANDLE_INVALID;
}
/* check ldap handle */
if (session_state->ld == NULL) {
TRACE_ERROR("No LDAP handle.\n");
return CKR_FUNCTION_FAILED;
}
/* Check if key exists */
wrapping_key_mapping = bt_get_node_value(&icsf_data->objects, wrapping_key);
if (!wrapping_key_mapping) {
TRACE_ERROR("%s\n", ock_err(ERR_KEY_HANDLE_INVALID));
return CKR_KEY_HANDLE_INVALID;
}
/* Allocate structure to keep ICSF object information */
if (!(key_mapping = malloc(sizeof(*key_mapping)))) {
TRACE_ERROR("%s\n", ock_err(ERR_HOST_MEMORY));
rc = CKR_HOST_MEMORY;
goto done;
}
memset(key_mapping, 0, sizeof(*key_mapping));
key_mapping->session_id = session->handle;
/* validate mechanism parameters. Only 4 mechanisms support
* key wrapping in icsf token */
switch (mech->mechanism) {
case CKM_DES_CBC_PAD:
case CKM_DES3_CBC_PAD:
case CKM_AES_CBC_PAD:
if ((rc = icsf_block_size(mech->mechanism, &expected_block_size)))
goto done;
if (mech->ulParameterLen != expected_block_size) {
TRACE_ERROR("Invalid mechanism parameter length: %lu "
"(expected %lu)\n",
(unsigned long) mech->ulParameterLen,
(unsigned long) expected_block_size);
rc = CKR_MECHANISM_PARAM_INVALID;
goto done;
}
break;
case CKM_RSA_PKCS:
if (mech->ulParameterLen != 0) {
TRACE_ERROR("%s\n", ock_err(ERR_MECHANISM_PARAM_INVALID));
rc = CKR_MECHANISM_PARAM_INVALID;
goto done;
}
break;
default:
TRACE_ERROR("icsf invalid %lu mechanism for key wrapping\n",
mech->mechanism);
rc = CKR_MECHANISM_INVALID;
goto done;
}
/* Call ICSF service */
rc = icsf_unwrap_key(session_state->ld, &reason, mech,
&wrapping_key_mapping->icsf_object,
wrapped_key, wrapped_key_len,
attrs, attrs_len, &key_mapping->icsf_object);
if (rc) {
TRACE_DEVEL("icsf_unwrap_key failed\n");
rc = icsf_to_ock_err(rc, reason);
goto done;
}
/* Add info about object into session */
if (!(node_number = bt_node_add(&icsf_data->objects, key_mapping))) {
TRACE_ERROR("Failed to add object to binary tree.\n");
rc = CKR_FUNCTION_FAILED;
goto done;
}
/* Use node number as handle */
*p_key = node_number;
done:
if (wrapping_key_mapping) {
bt_put_node_value(&icsf_data->objects, wrapping_key_mapping);
wrapping_key_mapping = NULL;
}
/* If allocated, object must be freed in case of failure */
if (rc && key_mapping)
free(key_mapping);
return rc;
}
/*
* Derive a key from a base key, creating a new key object.
*/
CK_RV icsftok_derive_key(STDLL_TokData_t * tokdata, SESSION * session,
CK_MECHANISM_PTR mech, CK_OBJECT_HANDLE hBaseKey,
CK_OBJECT_HANDLE_PTR handle, CK_ATTRIBUTE_PTR attrs,
CK_ULONG attrs_len)
{
icsf_private_data_t *icsf_data = tokdata->private_data;
CK_RV rc = CKR_OK;
struct session_state *session_state;
struct icsf_object_mapping *base_key_mapping = NULL;
CK_ULONG node_number;
char token_name[sizeof(tokdata->nv_token_data->token_info.label) + 1];
CK_SSL3_KEY_MAT_PARAMS *params = { 0 };
unsigned int i;
int reason = 0;
/* Variable for multiple keys derivation */
int multiple = 0;
struct icsf_object_mapping *mappings[4] = { NULL, };
CK_OBJECT_HANDLE *keys[4] = { NULL, };
/* Check type of derivation */
if (mech->mechanism == CKM_SSL3_KEY_AND_MAC_DERIVE ||
mech->mechanism == CKM_TLS_KEY_AND_MAC_DERIVE) {
multiple = 1;
params = (CK_SSL3_KEY_MAT_PARAMS *) mech->pParameter;
keys[0] = ¶ms->pReturnedKeyMaterial->hClientMacSecret;
keys[1] = ¶ms->pReturnedKeyMaterial->hServerMacSecret;
keys[2] = ¶ms->pReturnedKeyMaterial->hClientKey;
keys[3] = ¶ms->pReturnedKeyMaterial->hServerKey;
} else {
keys[0] = handle;
}
/* Check permissions based on attributes and session */
rc = check_session_permissions(session, attrs, attrs_len);
if (rc != CKR_OK)
return rc;
/* Copy token name from shared memory */
rc = XProcLock(tokdata);
if (rc != CKR_OK) {
TRACE_ERROR("Failed to get process lock.\n");
return rc;
}
strunpad(token_name, (const char *)tokdata->nv_token_data->token_info.label,
sizeof(tokdata->nv_token_data->token_info.label), ' ');
rc = XProcUnLock(tokdata);
if (rc != CKR_OK) {
TRACE_ERROR("Failed to release process lock.\n");
return rc;
}
/* Allocate structure to keep ICSF object information */
for (i = 0; i < sizeof(mappings) / sizeof(*mappings); i++) {
if (!(mappings[i] = malloc(sizeof(*mappings[i])))) {
TRACE_ERROR("%s\n", ock_err(ERR_HOST_MEMORY));
rc = CKR_HOST_MEMORY;
goto done;
}
memset(mappings[i], 0, sizeof(*mappings[i]));
mappings[i]->session_id = session->handle;
/* If not deriving multiple keys, just one key is needed */
if (!multiple)
break;
}
/* Get session state */
if (!(session_state = get_session_state(tokdata, session->handle))) {
TRACE_ERROR("%s\n", ock_err(ERR_SESSION_HANDLE_INVALID));
rc = CKR_SESSION_HANDLE_INVALID;
goto done;
}
/* check ldap handle */
if (session_state->ld == NULL) {
TRACE_ERROR("No LDAP handle.\n");
rc = CKR_FUNCTION_FAILED;
goto done;
}
/* Convert the OCK_CK_OBJECT_HANDLE_PTR to ICSF */
base_key_mapping = bt_get_node_value(&icsf_data->objects, hBaseKey);
if (!base_key_mapping) {
TRACE_ERROR("%s\n", ock_err(ERR_KEY_HANDLE_INVALID));
rc = CKR_KEY_HANDLE_INVALID;
goto done;
}
/* Call ICSF service */
if (!multiple)
rc = icsf_derive_key(session_state->ld, &reason, mech,
&base_key_mapping->icsf_object,
&mappings[0]->icsf_object, attrs, attrs_len);
else
rc = icsf_derive_multiple_keys(session_state->ld, &reason,
mech, &base_key_mapping->icsf_object,
attrs, attrs_len,
&mappings[0]->icsf_object,
&mappings[1]->icsf_object,
&mappings[2]->icsf_object,
&mappings[3]->icsf_object,
params->pReturnedKeyMaterial->pIVClient,
params->pReturnedKeyMaterial->pIVServer);
if (rc) {
rc = icsf_to_ock_err(rc, reason);
goto done;
}
for (i = 0; i < sizeof(mappings) / sizeof(*mappings); i++) {
/* Add info about object into session */
if (!(node_number = bt_node_add(&icsf_data->objects, mappings[i]))) {
TRACE_ERROR("Failed to add object to binary tree.\n");
rc = CKR_FUNCTION_FAILED;
goto done;
}
/* Use node number as handle */
*keys[i] = node_number;
/* If not deriving multiple keys, just one key is returned */
if (!multiple)
break;
}
done:
if (base_key_mapping) {
bt_put_node_value(&icsf_data->objects, base_key_mapping);
base_key_mapping = NULL;
}
/* If allocated, object must be freed in case of failure */
if (rc) {
for (i = 0; i < sizeof(mappings) / sizeof(*mappings); i++)
if (mappings[i])
free(mappings[i]);
}
return rc;
}