/*
* COPYRIGHT (c) International Business Machines Corp. 2012-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 - LDAP functions
*
* Author: Marcelo Cerri (mhcerri@br.ibm.com)
* Eduardo Otubo (eotubo@br.ibm.com)
*
*/
#define _GNU_SOURCE
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <ctype.h>
#include <lber.h>
#include <limits.h>
#include "icsf.h"
/* For logging functions: */
#include "defs.h"
#include "host_defs.h"
#include "h_extern.h"
#include "trace.h"
/*
* Note about ICSF callable services:
*
* Any ICSF service uses a base data structure containing some common fields.
* This base structure is described by the following ASN.1 definition:
*
* requestValue ::= SEQUENCE {
* version INTEGER,
* exitData OCTET STRING,
* handle OCTET STRING,
* ruleArraySeq RuleArraySeq,
* requestData CSFPInput
* }
*
* RuleArraySeq ::= SEQUENCE {
* ruleArrayCount INTEGER,
* ruleArray OCTET STRING
* }
*
* CSFPInput ::= CHOICE {
* IQF [CSFIQF] IQFInput,
* DMK [CSFPDMK] DMKInput,
* DVK [CSFPDVK] DVKInput,
* GAV [CSFPGAV] GAVInput,
* GKP [CSFPGKP] GKPInput,
* GSK [CSFPGSK] GSKInput,
* HMG [CSFPHMG] HMGInput,
* HMV [CSFPHMV] HMVInput,
* OWH [CSFPOWH] OWHInput,
* PKS [CSFPPKS] PKSInput,
* PKV [CSFPPKV] PKVInput,
* SAV [CSFPSAV] SAVInput,
* SKD [CSFPSKD] SKDInput,
* SKE [CSFPSKE] SKEInput,
* TRC [CSFPTRC] TRCInput,
* TRD [CSFPTRD] TRDInput,
* TRL [CSFPTRL] TRLInput,
* UWK [CSFPUWK] UWKInput,
* WPK [CSFPWPK] WPKInput,
* GLDTRD [GLDTRD] GLDTRDInput,
* IQA [CSFIQA] IQAInput
* }
*
* CSFPInput defines which service is being called. A different tag number
* and data structure is defined for each service.
*
* In the same way, the output is also based on a common data structure.
*
* responseValue ::= SEQUENCE {
* version INTEGER,
* ICSFRc INTEGER (0 .. MaxCSFPInteger),
* ICSFRsnCode INTEGER (0 .. MaxCSFPInteger),
* exitData OCTET STRING,
* handle OCTET STRING,
* responseData CSFPOutput
* }
*
* CSFPOutput ::= CHOICE {
* IQF [CSFIQF] IQFOutput,
* DMK [CSFPDMK] DMKOutput,
* DVK [CSFPDVK] DVKOutput,
* GAV [CSFPGAV] GAVOutput,
* GKP [CSFPGKP] GKPOutput,
* GSK [CSFPGSK] GSKOutput,
* HMG [CSFPHMG] HMGOutput,
* HMV [CSFPHMV] HMVOutput,
* OWH [CSFPOWH] OWHOutput,
* PKS [CSFPPKS] PKSOutput,
* PKV [CSFPPKV] PKVOutput,
* SAV [CSFPSAV] SAVOutput,
* SKD [CSFPSKD] SKDOutput,
* SKE [CSFPSKE] SKEOutput,
* TRC [CSFPTRC] TRCOutput,
* TRD [CSFPTRD] TRDOutput,
* TRL [CSFPTRL] TRLOutput,
* UWK [CSFPUWK] UWKOutput,
* WPK [CSFPWPK] WPKOutput,
* GLDTRD [GLDTRD] GLDTRDOutput,
* IQA [CSFIQA] IQAOutput
* }
*
* ICSFRc is the return code: 0 indicates success, 4 partial success and
* values greater than 4 indicates an error. ICSFRsnCode is the reason code
* that provides further details about an error.
*/
/* Macros for argument checking */
#define CHECK_ARG_NON_NULL(_arg) \
if (_arg == NULL) { \
TRACE_ERROR("Null argument \"%s\".\n", #_arg); \
return -1; \
}
#define CHECK_ARG_MAX_LEN(_arg, _length) \
if (_arg && (strlen(_arg) > _length)) { \
TRACE_ERROR("String too long %s=\"%s\"\n", #_arg, _arg); \
return -1; \
}
#define CHECK_ARG_NON_NULL_AND_MAX_LEN(_arg, _length) \
CHECK_ARG_NON_NULL(_arg); \
CHECK_ARG_MAX_LEN(_arg, _length);
/*
* Copy a null terminated string from `orig` to the buffer `dest` of length
* `len` and fill the remaining bytes with `padding_char`. The result string is
* not null terminated.
*/
static void strpad(char *dest, const char *orig, size_t len, int padding_char)
{
size_t str_len = strlen(orig);
UNUSED(padding_char);
if (str_len > len)
str_len = len;
memcpy(dest, orig, str_len);
if ((len - str_len) > 0)
memset(dest + str_len, ' ', len - str_len);
}
/* Copy a string `orig` of length `len` and padded with `padding_char` to a null
* terminated string `dest`. `dest` should be at least `len` + 1 bytes long.
*/
void strunpad(char *dest, const char *orig, size_t len, int padding_char)
{
size_t i;
for (i = len - 1; i; i--)
if (orig[i - 1] != padding_char)
break;
memcpy(dest, orig, i);
dest[i] = '\0';
}
/*
* Build a token handle based on token name.
*
* `handle` must be at least ICSF_HANDLE_LEN long.
*/
static void token_name_to_handle(char *handle, const char *token_name)
{
/* The first 32 bytes of `handle` specifies the token's name, the
* remaining bytes should be blank.
*/
strpad(handle, token_name, ICSF_TOKEN_NAME_LEN, ' ');
memset(handle + ICSF_TOKEN_NAME_LEN, ' ',
ICSF_HANDLE_LEN - ICSF_TOKEN_NAME_LEN);
}
/*
* Parse a structure object record to a handle.
*
* `data` must be at least ICSF_HANDLE_LEN long.
*/
void object_record_to_handle(char *data,
const struct icsf_object_record *record)
{
/*
* Object handle is composed by token name, sequence number
* converted to hexadecimal and ID padded with blanks.
*/
size_t offset = 0;
char hex_seq[ICSF_SEQUENCE_LEN + 1];
strpad(data + offset, record->token_name, ICSF_TOKEN_NAME_LEN, ' ');
offset += ICSF_TOKEN_NAME_LEN;
snprintf(hex_seq, sizeof(hex_seq), "%0*lX", ICSF_SEQUENCE_LEN,
record->sequence);
memcpy(data + offset, hex_seq, ICSF_SEQUENCE_LEN);
offset += ICSF_SEQUENCE_LEN;
memset(data + offset, ' ', ICSF_HANDLE_LEN - offset);
data[offset] = record->id;
}
/*
* Parse a raw object handle into token name, sequence and object type.
*/
void handle_to_object_record(struct icsf_object_record *record,
const char *data)
{
size_t offset = 0;
char hex_seq[ICSF_SEQUENCE_LEN + 1];
strunpad(record->token_name, data + offset, ICSF_TOKEN_NAME_LEN + 1, ' ');
offset += ICSF_TOKEN_NAME_LEN;
memcpy(hex_seq, data + offset, ICSF_SEQUENCE_LEN);
hex_seq[ICSF_SEQUENCE_LEN] = '\0';
sscanf(hex_seq, "%lx", &record->sequence);
offset += ICSF_SEQUENCE_LEN;
record->id = data[offset];
}
/*
* Ensure that LDAPv3 is used. V3 is needed for extended operations.
*/
static int icsf_force_ldap_v3(LDAP * ld)
{
int rc;
int version = 0;
CHECK_ARG_NON_NULL(ld);
rc = ldap_get_option(ld, LDAP_OPT_PROTOCOL_VERSION, &version);
if (rc != LDAP_OPT_SUCCESS) {
TRACE_ERROR("Failed to get LDAP version: %s (%d)\n",
ldap_err2string(rc), rc);
return -1;
}
if (version < LDAP_VERSION3) {
TRACE_INFO("Changing version from %d to %d.\n", version, LDAP_VERSION3);
version = LDAP_VERSION3;
rc = ldap_set_option(ld, LDAP_OPT_PROTOCOL_VERSION, &version);
if (rc != LDAP_OPT_SUCCESS) {
TRACE_ERROR("Failed to set LDAP version: %s (%d)\n",
ldap_err2string(rc), rc);
return -1;
}
}
return 0;
}
/*
* Perform a simple bind to `uri` using `dn` and `password` as credentials.
*/
int icsf_login(LDAP ** ld, const char *uri, const char *dn,
const char *password)
{
int rc;
struct berval cred;
CHECK_ARG_NON_NULL(ld);
CHECK_ARG_NON_NULL(password);
/* Handle empty and null string in the same way */
uri = (uri && uri[0]) ? uri : NULL;
dn = (dn && dn[0]) ? dn : NULL;
/* Connect to LDAP server */
TRACE_DEVEL("Connecting to: %s\n", uri ? uri : "(null)");
rc = ldap_initialize(ld, uri);
if (rc != LDAP_SUCCESS) {
TRACE_ERROR("Failed to connect to \"%s\": %s (%d)\n",
uri ? uri : "(null)", ldap_err2string(rc), rc);
return -1;
}
if (icsf_force_ldap_v3(*ld))
return -1;
TRACE_DEVEL("Binding with DN: %s\n", dn ? dn : "(null)");
cred.bv_len = strlen(password);
cred.bv_val = (char *) password;
rc = ldap_sasl_bind_s(*ld, dn, LDAP_SASL_SIMPLE, &cred, NULL, NULL, NULL);
if (rc != LDAP_SUCCESS) {
TRACE_ERROR("LDAP bind failed: %s (%d)\n", ldap_err2string(rc), rc);
return -1;
}
return 0;
}
/*
* Set the paths for private key, certificate and CA, which are used for
* SASL authentication using external certificate.
*
* TODO: check why these options just work as globals (ld == NULL)
*/
static int icsf_set_sasl_params(LDAP * ld, const char *cert, const char *key,
const char *ca, const char *ca_dir)
{
int rc;
CHECK_ARG_NON_NULL(ld);
TRACE_DEVEL("Preparing environment for TLS\n");
if (cert && *cert) {
TRACE_DEVEL("Using certificate: %s\n", cert);
rc = ldap_set_option(NULL, LDAP_OPT_X_TLS_CERTFILE, cert);
if (rc != LDAP_SUCCESS) {
TRACE_ERROR("Failed to set certificate file for TLS: "
"%s (%d)\n", ldap_err2string(rc), rc);
return -1;
}
}
if (key && *key) {
TRACE_DEVEL("Using private key: %s\n", key);
rc = ldap_set_option(NULL, LDAP_OPT_X_TLS_KEYFILE, key);
if (rc != LDAP_SUCCESS) {
TRACE_ERROR("Failed to set key file for TLS: "
"%s (%d)\n", ldap_err2string(rc), rc);
return -1;
}
}
if (ca && *ca) {
TRACE_DEVEL("Using CA certificate file: %s\n", ca);
rc = ldap_set_option(NULL, LDAP_OPT_X_TLS_CACERTFILE, ca);
if (rc != LDAP_SUCCESS) {
TRACE_ERROR("Failed to set CA certificate file for TLS:"
" %s (%d)\n", ldap_err2string(rc), rc);
return -1;
}
}
if (ca_dir && *ca_dir) {
TRACE_DEVEL("Using CA certificate dir: %s\n", ca_dir);
rc = ldap_set_option(NULL, LDAP_OPT_X_TLS_CACERTDIR, ca_dir);
if (rc != LDAP_SUCCESS) {
TRACE_ERROR("Failed to set CA certificate dir for TLS: "
"%s (%d)\n", ldap_err2string(rc), rc);
return -1;
}
}
return 0;
}
/*
* Perform a SASL bind to `uri` using the given certificate, private key
* and CA paths.
*/
int icsf_sasl_login(LDAP ** ld, const char *uri, const char *cert,
const char *key, const char *ca, const char *ca_dir)
{
int rc;
CHECK_ARG_NON_NULL(ld);
/* Handle empty and null string in the same way */
uri = (uri && uri[0]) ? uri : NULL;
/* Connect to LDAP server */
TRACE_DEVEL("Connecting to: %s\n", uri ? uri : "(null)");
rc = ldap_initialize(ld, uri);
if (rc != LDAP_SUCCESS) {
TRACE_ERROR("Failed to connect to \"%s\": %s (%d)\n",
uri ? uri : "(null)", ldap_err2string(rc), rc);
return -1;
}
if (icsf_force_ldap_v3(*ld))
return -1;
/* Initialize TLS */
if (icsf_set_sasl_params(*ld, cert, key, ca, ca_dir))
return -1;
TRACE_DEVEL("Binding\n");
rc = ldap_sasl_bind_s(*ld, NULL, "EXTERNAL", NULL, NULL, NULL, NULL);
if (rc != LDAP_SUCCESS) {
char *ext_msg = NULL;
ldap_get_option(*ld, LDAP_OPT_DIAGNOSTIC_MESSAGE, &ext_msg);
TRACE_ERROR("LDAP bind failed: %s (%d)%s%s\n",
ldap_err2string(rc), rc,
ext_msg ? "\nDetailed message: " : "",
ext_msg ? ext_msg : "");
if (ext_msg)
ldap_memfree(ext_msg);
return -1;
}
return 0;
}
/*
* Disconnect from the server.
*/
int icsf_logout(LDAP * ld)
{
int rc;
CHECK_ARG_NON_NULL(ld);
rc = ldap_unbind_ext_s(ld, NULL, NULL);
if (rc != LDAP_SUCCESS) {
TRACE_ERROR("Failed to unbind: %s (%d)\n", ldap_err2string(rc), rc);
return -1;
}
return 0;
}
/*
* Check if the ICSF LDAP extension is supported by the server.
*/
int icsf_check_pkcs_extension(LDAP * ld)
{
int rc = -1;
int ret;
LDAPMessage *res = NULL;
LDAPMessage *entry = NULL;
BerElement *ber = NULL;
char *attr = NULL;
char expected_attr[] = "supportedextension";
char *attr_list[] = { expected_attr, NULL };
const char *expected_oid = ICSF_REQ_OID;
CHECK_ARG_NON_NULL(ld);
/* Search root DSE. */
ret = ldap_search_ext_s(ld, "", /* Base DN */
LDAP_SCOPE_BASE, /* Scope */
"(objectclass=*)", /* Filter */
attr_list, /* Attribute list */
0, /* Attributes only */
NULL, /* Server controls */
NULL, /* Client controls */
NULL, /* Timeout */
0, /* Size limit */
&res);
if (ret)
goto cleanup;
/* It should contain just one entry */
entry = ldap_first_entry(ld, res);
if (entry == NULL)
goto cleanup;
/* Loop through attributes */
attr = ldap_first_attribute(ld, entry, &ber);
while (attr) {
if (!strcmp(expected_attr, attr)) {
/* Get the value for each attribute */
struct berval **it;
struct berval **values = ldap_get_values_len(ld, entry, attr);
if (values == NULL)
goto cleanup;
/* Print each value */
for (it = values; *it; it++)
if (!strncmp(expected_oid,
(*it)->bv_val,
strlen(expected_oid))) {
/* It's supported */
rc = 0;
}
ldap_value_free_len(values);
if (rc == 0)
goto cleanup;
}
/* Get next attribute */
ldap_memfree(attr);
attr = ldap_next_attribute(ld, entry, ber);
}
/* Not supported. */
rc = 1;
cleanup:
if (attr)
ldap_memfree(attr);
if (ber)
ber_free(ber, 0);
if (res)
ldap_msgfree(res);
return rc;
}
/*
* `icsf_call` is a generic helper function for ICSF services.
*
* Every request message to an ICSF service has some common fields and a
* specific field that depends on the service that is called. The structure of
* this field differs for each service and it's also marked with a specific tag
* that identifies the service.
*
* `handle` identifies a token or object. It should be always 44 bytes long.
*
* `reason` returns the ICSF reason code. It's ignored when NULL.
*
* `rule_array` should be a sequence of 8 bytes strings padded with blanks. Each
* 8 bytes is an item and can change the behaviour of a call.
*
* `tag` identifies the ICSF service.
*
* `specific` is the service-specific field of the request message. A NULL value
* indicates an empty field.
*
* `result` points to the service-specific part of the response message. If a
* non-NULL value is given, the caller must free it.
*/
static int icsf_call(LDAP * ld, int *reason, char *handle, size_t handle_len,
const char *rule_array, size_t rule_array_len,
ber_tag_t tag, BerElement * specific, BerElement ** result)
{
int rc;
BerElement *ber_req = NULL;
BerElement *ber_res = NULL;
struct berval *raw_req = NULL;
struct berval *raw_res = NULL;
struct berval *raw_specific = NULL;
char *response_oid = NULL;
/* Variables used as input */
int version = 1;
char *exit_data = ""; /* Ignored */
int rule_array_count;
/* Variables used as output */
int return_code = 0;
int reason_code = 0;
struct berval *out_handle = NULL;
/* Check sizes */
if (handle_len != ICSF_HANDLE_LEN) {
TRACE_ERROR("Invalid handle length: %lu\n", handle_len);
return -1;
}
if ((rule_array_len % ICSF_RULE_ITEM_LEN)) {
TRACE_ERROR("Invalid rule array length: %lu\n", rule_array_len);
return -1;
}
rule_array_count = rule_array_len / ICSF_RULE_ITEM_LEN;
/* Allocate ber_req to encode message. */
ber_req = ber_alloc_t(LBER_USE_DER);
if (ber_req == NULL) {
TRACE_ERROR("%s\n", ock_err(ERR_HOST_MEMORY));
rc = -1;
goto cleanup;
}
if (specific) {
rc = ber_flatten(specific, &raw_specific);
if (rc) {
TRACE_ERROR("Failed to flatten specific data.\n");
rc = -1;
goto cleanup;
}
}
/* Encode message:
*
* requestValue ::= SEQUENCE {
* version INTEGER,
* exitData OCTET STRING,
* handle_len OCTET STRING,
* ruleArraySeq RuleArraySeq,
* requestData CSFPInput
* }
*
* RuleArraySeq ::= SEQUENCE {
* ruleArrayCount INTEGER,
* ruleArray OCTET STRING
* }
*
* CSFPInput ::= CHOICE {
* IQF [CSFIQF] IQFInput,
* DMK [CSFPDMK] DMKInput,
* DVK [CSFPDVK] DVKInput,
* GAV [CSFPGAV] GAVInput,
* GKP [CSFPGKP] GKPInput,
* GSK [CSFPGSK] GSKInput,
* HMG [CSFPHMG] HMGInput,
* HMV [CSFPHMV] HMVInput,
* OWH [CSFPOWH] OWHInput,
* PKS [CSFPPKS] PKSInput,
* PKV [CSFPPKV] PKVInput,
* SAV [CSFPSAV] SAVInput,
* SKD [CSFPSKD] SKDInput,
* SKE [CSFPSKE] SKEInput,
* TRC [CSFPTRC] TRCInput,
* TRD [CSFPTRD] TRDInput,
* TRL [CSFPTRL] TRLInput,
* UWK [CSFPUWK] UWKInput,
* WPK [CSFPWPK] WPKInput,
* GLDTRD [GLDTRD] GLDTRDInput,
* IQA [CSFIQA] IQAInput
* }
*/
tag |= LBER_CLASS_CONTEXT | LBER_CONSTRUCTED;
rc = ber_printf(ber_req, "{iso{io}to}", version, exit_data, handle,
handle_len, rule_array_count, rule_array,
rule_array_len, tag,
(raw_specific) ? raw_specific->bv_val : "",
(raw_specific) ? raw_specific->bv_len : 0);
if (rc < 0) {
TRACE_ERROR("Failed to encode message.\n");
rc = -1;
goto cleanup;
}
rc = ber_flatten(ber_req, &raw_req);
if (rc) {
TRACE_ERROR("Failed to flatten BER data.\n");
rc = -1;
goto cleanup;
}
/* Call ICSF service */
rc = ldap_extended_operation_s(ld, ICSF_REQ_OID, raw_req, NULL, NULL,
&response_oid, &raw_res);
if (rc != LDAP_SUCCESS) {
char *ext_msg = NULL;
ldap_get_option(ld, LDAP_OPT_DIAGNOSTIC_MESSAGE, &ext_msg);
TRACE_ERROR("ICSF call failed: %s (%d)%s%s\n",
ldap_err2string(rc), rc,
ext_msg ? "\nDetailed message: " : "",
ext_msg ? ext_msg : "");
if (ext_msg)
ldap_memfree(ext_msg);
rc = -1;
goto cleanup;
}
/* Decode result */
ber_res = ber_init(raw_res);
if (ber_res == NULL) {
TRACE_ERROR("Failed to create a response buffer\n");
rc = -1;
goto cleanup;
}
/* Decode common response fields: */
rc = ber_scanf(ber_res, "{iiixO", &version, &return_code,
&reason_code, &out_handle);
if (rc < 0) {
TRACE_ERROR("Failed to decode message.\n");
rc = -1;
goto cleanup;
}
/* Copy handle */
if (out_handle == NULL) {
memset(handle, 0, handle_len);
} else {
size_t len = (handle_len < out_handle->bv_len)
? handle_len : out_handle->bv_len;
memcpy(handle, out_handle->bv_val, len);
memset(handle + len, 0, handle_len - len);
}
TRACE_DEVEL("ICSF call result: %d (%d)\n", return_code, reason_code);
if (ICSF_RC_IS_ERROR(return_code)) {
TRACE_ERROR("ICSF call failed: %d (%d)\n", return_code, reason_code);
}
rc = return_code;
cleanup:
if (reason)
*reason = reason_code;
if (result)
*result = ber_res;
else if (ber_res)
ber_free(ber_res, 1);
if (ber_req)
ber_free(ber_req, 1);
if (raw_req)
ber_bvfree(raw_req);
if (raw_res)
ber_bvfree(raw_res);
if (response_oid)
ldap_memfree(response_oid);
if (out_handle)
ber_bvfree(out_handle);
if (raw_specific)
ber_bvfree(raw_specific);
return rc;
}
/*
* Create a new token. All parameters must be null terminated strings.
*/
int icsf_create_token(LDAP * ld, int *reason, const char *token_name,
const char *manufacturer, const char *model,
const char *serial)
{
int rc = -1;
char handle[ICSF_HANDLE_LEN];
char rule_array[2 * ICSF_RULE_ITEM_LEN];
char attribute_list[68] = { 0, };
BerElement *msg = NULL;
CHECK_ARG_NON_NULL(ld);
CHECK_ARG_NON_NULL_AND_MAX_LEN(token_name, ICSF_TOKEN_NAME_LEN);
CHECK_ARG_NON_NULL_AND_MAX_LEN(manufacturer, ICSF_MANUFACTURER_LEN);
CHECK_ARG_NON_NULL_AND_MAX_LEN(model, ICSF_MODEL_LEN);
CHECK_ARG_NON_NULL_AND_MAX_LEN(serial, ICSF_SERIAL_LEN);
token_name_to_handle(handle, token_name);
/* Should be 8 bytes padded. It's a token creation and if the token
* already exists it is recreated.
*/
strpad(rule_array, "TOKEN", ICSF_RULE_ITEM_LEN, ' ');
strpad(rule_array + ICSF_RULE_ITEM_LEN, "RECREATE", ICSF_RULE_ITEM_LEN,
' ');
/* For token creation, handle is composed by 32 bytes for manufacturer
* id, 16 bytes for model, 16 bytes for serial number, and 4 trailing
* bytes with zeros.
*/
strpad(attribute_list, manufacturer, ICSF_MANUFACTURER_LEN, ' ');
strpad(attribute_list + ICSF_MANUFACTURER_LEN, model, ICSF_MODEL_LEN, ' ');
strpad(attribute_list + ICSF_MANUFACTURER_LEN + ICSF_MODEL_LEN, serial,
ICSF_SERIAL_LEN, ' ');
/* Allocate ber_req to encode message. */
msg = ber_alloc_t(LBER_USE_DER);
if (msg == NULL) {
TRACE_ERROR("%s\n", ock_err(ERR_HOST_MEMORY));
goto cleanup;
}
/* Encode message:
*
* TRCInput ::= SEQUENCE {
* trcAttrs ::= CHOICE {
* tokenAttrString [0] OCTET STRING,
* }
* }
*/
rc = ber_printf(msg, "to", 0 | LBER_CLASS_CONTEXT, attribute_list,
sizeof(attribute_list));
if (rc < 0) {
TRACE_ERROR("Failed to encode message.\n");
goto cleanup;
}
rc = icsf_call(ld, reason, handle, sizeof(handle),
rule_array, sizeof(rule_array), ICSF_TAG_CSFPTRC, msg, NULL);
cleanup:
if (msg)
ber_free(msg, 1);
return rc;
}
/*
* Destroy a token.
*/
int icsf_destroy_token(LDAP * ld, int *reason, char *token_name)
{
/* Variables used as input */
char handle[ICSF_HANDLE_LEN];
char rule_array[1 * ICSF_RULE_ITEM_LEN];
CHECK_ARG_NON_NULL(ld);
CHECK_ARG_NON_NULL_AND_MAX_LEN(token_name, ICSF_TOKEN_NAME_LEN);
token_name_to_handle(handle, token_name);
/* Should be 8 bytes padded. */
strpad(rule_array, "TOKEN", ICSF_RULE_ITEM_LEN, ' ');
/*
* CSFPTRD service is used to destroy a token or an object. Handle
* indicates the token or object that must be destroyed and no
* additional data is needed.
*/
return icsf_call(ld, reason, handle, sizeof(handle), rule_array,
sizeof(rule_array), ICSF_TAG_CSFPTRD, NULL, NULL);
}
/*
* Parse a sequence of bytes `data` returned by a CSFPTRL call containing the
* attributes of a token and store the parsed value in the structure `record`.
*
* The data is formated as the following:
* - 32 bytes for token name;
* - 32 bytes for manufacturer name;
* - 16 bytes for model identification;
* - 16 bytes for serial number;
* - 8 bytes for date in UTC of the last change encoded as a string in the
* format "yyyymmdd".
* - 8 bytes for time in UTC of the last change encoded as a string in the
* format "hhmmssth".
* - 4 bytes of flags (the first bit of the first byte indicate that the
* token is write protected).
*/
static void parse_token_record(struct icsf_token_record *record,
const char *data)
{
size_t offset = 0;
strunpad(record->name, data + offset, ICSF_TOKEN_NAME_LEN + 1, ' ');
offset += ICSF_TOKEN_NAME_LEN;
strunpad(record->manufacturer, data + offset, ICSF_MANUFACTURER_LEN + 1,
' ');
offset += ICSF_MANUFACTURER_LEN;
strunpad(record->model, data + offset, ICSF_MODEL_LEN + 1, ' ');
offset += ICSF_MODEL_LEN;
strunpad(record->serial, data + offset, ICSF_SERIAL_LEN + 1, ' ');
offset += ICSF_SERIAL_LEN;
strunpad(record->date, data + offset, ICSF_DATE_LEN + 1, ' ');
offset += ICSF_DATE_LEN;
strunpad(record->time, data + offset, ICSF_TIME_LEN + 1, ' ');
offset += ICSF_TIME_LEN;
/* Flags are not a string, just a bunch of flags. So it doesn't need
* to be null terminated.
*/
memcpy(record->flags, data + offset, ICSF_FLAGS_LEN);
}
/* helper function to determine if a specific keyword is in the rule array */
int in_rulearray(const char *keyword, const char *rulearray, int count)
{
int i = 0;
while (count) {
if (memcmp(keyword, rulearray + i, 8) == 0)
return 1;
i += 8;
count--;
}
return 0;
}
/*
* This function indicates if an attribute should be BER encoded as a number or
* not, based on its type.
*/
static int is_numeric_attr(CK_ULONG type)
{
switch (type) {
case CKA_CLASS:
case CKA_KEY_TYPE:
case CKA_CERTIFICATE_TYPE:
case CKA_KEY_GEN_MECHANISM:
case CKA_VALUE_LEN:
case CKA_MODULUS_BITS:
return 1;
}
return 0;
}
/*
* This helper functions receives a list of attributes containing type, length
* and value and encode it in BER encoding. Numeric and non numeric attributes
* are encoded using different rules.
*
* The attributes are encoded following rules:
*
* Attributes ::= SEQUENCE OF SEQUENCE {
* attrName INTEGER,
* attrValue AttributeValue
* }
*
* AttributeValue ::= CHOICE {
* charValue [0] OCTET STRING,
* intValue [1] INTEGER
* }
*
*/
static int icsf_ber_put_attribute_list(BerElement * ber, CK_ATTRIBUTE * attrs,
CK_ULONG attrs_len)
{
size_t i;
for (i = 0; i < attrs_len; i++) {
if (!is_numeric_attr(attrs[i].type)) {
/* Non numeric attributes are encode as octet strings */
if (ber_printf(ber, "{ito}", attrs[i].type,
0 | LBER_CLASS_CONTEXT, attrs[i].pValue,
attrs[i].ulValueLen) < 0) {
goto encode_error;
}
} else {
long value;
unsigned long mask;
/* `long` is used here to support any size of integer,
* however if the value is shorter than a `long` then
* just the significant bits should be used.
*/
if (attrs[i].ulValueLen > sizeof(long)) {
TRACE_ERROR("Integer value too long for attribute\n");
goto encode_error;
}
/* Calculate a mask to get just the bits in the range of
* the given length.
*/
mask = (1UL << (8 * attrs[i].ulValueLen)) - 1;
if (mask == 0)
mask = (unsigned long) -1;
value = *((unsigned long *) attrs[i].pValue) & mask;
/* Encode integer attribute. */
if (ber_printf(ber, "{iti}", attrs[i].type,
1 | LBER_CLASS_CONTEXT, value) < 0) {
goto encode_error;
}
}
}
return 0;
encode_error:
TRACE_ERROR("Failed to encode message.\n");
return -1;
}
/*
*
* `icsf_list` is a helper function for CSFPTRL service,
* which is used for token and object listing.
*
* `handle` identifies the last token or object returned by a previous call of
* `icsf_list`. It should be always 44 bytes long and be in the following
* format:
*
* - For tokens:
* * 32 bytes containing the token name padded with blanks;
* * remaining bytes filled with blanks.
*
* - For objects:
* * 32 bytes containing the token name padded with blanks;
* * 8 bytes containing the object's sequence number encoded int
* hexadecimal.
* * 1 byte with the character 'T' for token objects or 'S' for session
* objects.
* * remaining bytes filled with blanks.
*
* `rule_array` should be a sequence of 8 bytes strings padded with blanks.
* It indicates if a list of tokens or a objects will be returned (please refer
* to `icsf_create_token` and `icsf_create_object` for details).
*
* `bv_list` is an output buffer for the raw data and should be freed by the
* caller.
*
* `list_len` is used as input to indicate the number of bytes of the buffer to
* be returned, and it's updated with the number of bytes returned.
*
* `list_count` indicates how many items should be returned.
*/
static int icsf_list(LDAP * ld, int *reason, char *handle, size_t handle_len,
CK_ULONG attrs_len, CK_ATTRIBUTE * attrs,
const char *rule_array, size_t rule_array_len,
struct berval **bv_list, size_t * list_len,
size_t list_count)
{
int rc = -1;
BerElement *msg = NULL;
BerElement *result = NULL;
int out_list_len = 0;
int objectInRuleArray = 0;
/* Allocate request message. */
msg = ber_alloc_t(LBER_USE_DER);
if (msg == NULL) {
TRACE_ERROR("%s\n", ock_err(ERR_HOST_MEMORY));
goto cleanup;
}
/* Encode message:
*
* TRLInput ::= SEQUENCE {
* inListLen INTEGER (0 .. MaxCSFPInteger),
* maxHandleCount INTEGER (0 .. MaxCSFPInteger),
* searchTemplate [0] Attributes OPTIONAL
* }
*
*/
if (ber_printf(msg, "ii", *list_len, list_count) < 0) {
TRACE_ERROR("Failed to encode message.\n");
goto cleanup;
}
objectInRuleArray = in_rulearray("OBJECT ", rule_array,
rule_array_len / ICSF_RULE_ITEM_LEN);
if ((objectInRuleArray) && (attrs != NULL)) {
if (ber_printf(msg, "t{", 0 | LBER_CLASS_CONTEXT | LBER_CONSTRUCTED) <
0) {
TRACE_ERROR("Failed to flatten attribute list\n");
goto cleanup;
}
if (icsf_ber_put_attribute_list(msg, attrs, attrs_len) < 0) {
TRACE_ERROR("Failed to flatten attribute list\n");
goto cleanup;
}
if (ber_printf(msg, "}") < 0) {
TRACE_ERROR("Failed to encode message.\n");
goto cleanup;
}
}
rc = icsf_call(ld, reason, handle, handle_len, rule_array,
rule_array_len, ICSF_TAG_CSFPTRL, msg, &result);
if (ICSF_RC_IS_ERROR(rc))
goto cleanup;
/* Decode result:
*
* TRLOutput ::= SEQUENCE {
* outList CHOICE {
* tokenList [0] OCTET STRING,
* handleList [1] OCTET STRING
* },
* outListLen INTEGER (0 .. MaxCSFPInteger)
* }
*/
if (ber_scanf(result, "{Oi}", bv_list, &out_list_len) == LBER_ERROR) {
TRACE_ERROR("Failed to decode message.\n");
rc = -1;
goto cleanup;
}
*list_len = out_list_len;
cleanup:
if (msg)
ber_free(msg, 1);
if (result)
ber_free(result, 1);
return rc;
}
/*
* List tokens on the server.
*
* `previous` must point to the last token returned by a previous call of
* `icsf_list_tokens` or should be NULL for the first call.
*
* `records` must point to a buffer of token records with `records_len`
* elements. `records_len` is updated with the number of tokens returned
* and it's zero when there's no more records left.
*/
int icsf_list_tokens(LDAP * ld, int *reason, struct icsf_token_record *previous,
struct icsf_token_record *records, size_t * records_len)
{
int rc = -1;
char handle[44];
char rule_array[ICSF_RULE_ITEM_LEN];
struct berval *bv_list = NULL;
size_t list_len;
size_t i;
CHECK_ARG_NON_NULL(ld);
CHECK_ARG_NON_NULL(records);
CHECK_ARG_NON_NULL(records_len);
/* The first record that must be returned in `records` is the next one
* after `previous`, and for that the `previous` handle must be
* provided. When `previous` is null a blank handle should be used
* instead.
*/
if (previous)
token_name_to_handle(handle, previous->name);
else
memset(handle, ' ', sizeof(handle));
/* Should be 8 bytes padded. */
strpad(rule_array, "TOKEN", ICSF_RULE_ITEM_LEN, ' ');
list_len = ICSF_TOKEN_RECORD_LEN * *records_len;
rc = icsf_list(ld, reason, handle, sizeof(handle), 0, NULL, rule_array,
sizeof(rule_array), &bv_list, &list_len, *records_len);
if (ICSF_RC_IS_ERROR(rc))
goto cleanup;
/* Parse result */
*records_len = list_len / ICSF_TOKEN_RECORD_LEN;
for (i = 0; i < *records_len; i++) {
size_t offset = i * ICSF_TOKEN_RECORD_LEN;
parse_token_record(&records[i], bv_list->bv_val + offset);
}
cleanup:
if (bv_list)
ber_bvfree(bv_list);
return rc;
}
int icsf_copy_object(LDAP * ld, int *reason,
CK_ATTRIBUTE * attrs, CK_ULONG attrs_len,
struct icsf_object_record *src,
struct icsf_object_record *dst)
{
int rc = -1;
char handle[ICSF_HANDLE_LEN];
char rule_array[2 * ICSF_RULE_ITEM_LEN];
BerElement *msg = NULL;
CHECK_ARG_NON_NULL(ld);
CHECK_ARG_NON_NULL(src);
CHECK_ARG_NON_NULL(attrs);
object_record_to_handle(handle, src);
/* Allocate ber_req to encode message. */
msg = ber_alloc_t(LBER_USE_DER);
if (msg == NULL) {
TRACE_ERROR("%s\n", ock_err(ERR_HOST_MEMORY));
goto cleanup;
}
if (attrs_len != 0) {
rc = ber_printf(msg, "t{", 1 | LBER_CLASS_CONTEXT | LBER_CONSTRUCTED);
if (rc < 0) {
TRACE_ERROR("%s\n", ock_err(ERR_FUNCTION_FAILED));
goto cleanup;
}
if (icsf_ber_put_attribute_list(msg, attrs, attrs_len) < 0) {
TRACE_DEVEL("icsf_ber_put_attribute_list failed\n");
goto cleanup;
}
if (ber_printf(msg, "}") < 0) {
TRACE_ERROR("%s\n", ock_err(ERR_FUNCTION_FAILED));
goto cleanup;
}
} else {
rc = ber_printf(msg, "tn", 1 | LBER_CLASS_CONTEXT | LBER_CONSTRUCTED);
if (rc < 0) {
TRACE_ERROR("%s\n", ock_err(ERR_FUNCTION_FAILED));
goto cleanup;
}
}
/* Should be 8 bytes padded. */
strpad(rule_array, "OBJECT", ICSF_RULE_ITEM_LEN, ' ');
strpad(rule_array + ICSF_RULE_ITEM_LEN, "COPY", ICSF_RULE_ITEM_LEN, ' ');
rc = icsf_call(ld, reason, handle, sizeof(handle),
rule_array, sizeof(rule_array), ICSF_TAG_CSFPTRC, msg, NULL);
if (!rc && dst)
handle_to_object_record(dst, handle);
cleanup:
if (msg)
ber_free(msg, 1);
return rc;
}
/*
* Create an object in the token defined by the given `token_name`.
*
* `attrs` is a list of attributes each one consisting in a type, a length and a
* value (a sequence of bytes). `attrs_len` indicates how many attributes the
* input list has.
*
* `obj_handle` is the buffer that will receive the handler for the new object.
* And it should be at least 44 bytes long (indicated by `obj_handle_len`).
*/
int icsf_create_object(LDAP * ld, int *reason, const char *token_name,
CK_ATTRIBUTE * attrs, CK_ULONG attrs_len,
struct icsf_object_record *object)
{
int rc = -1;
char handle[ICSF_HANDLE_LEN];
char rule_array[ICSF_RULE_ITEM_LEN];
BerElement *msg = NULL;
CHECK_ARG_NON_NULL(ld);
CHECK_ARG_NON_NULL_AND_MAX_LEN(token_name, ICSF_TOKEN_NAME_LEN);
CHECK_ARG_NON_NULL(attrs);
token_name_to_handle(handle, token_name);
/* Should be 8 bytes padded. */
strpad(rule_array, "OBJECT", sizeof(rule_array), ' ');
/* Allocate ber_req to encode message. */
msg = ber_alloc_t(LBER_USE_DER);
if (msg == NULL) {
TRACE_ERROR("%s\n", ock_err(ERR_HOST_MEMORY));
goto cleanup;
}
/* Encode message:
*
* TRCInput ::= SEQUENCE {
* trcAttrs ::= CHOICE {
* objectAttrList [1] Attributes
* }
* }
*
* Attributes ::= SEQUENCE OF SEQUENCE {
* attrName INTEGER,
* attrValue AttributeValue
* }
*
* AttributeValue ::= CHOICE {
* charValue [0] OCTET STRING,
* intValue [1] INTEGER
* }
*
*/
if (ber_printf(msg, "t{", 1 | LBER_CLASS_CONTEXT | LBER_CONSTRUCTED) < 0) {
TRACE_ERROR("Failed to encode message.\n");
goto cleanup;
}
if (icsf_ber_put_attribute_list(msg, attrs, attrs_len) < 0) {
TRACE_ERROR("Failed to flatten attribute list\n");
goto cleanup;
}
if (ber_printf(msg, "}") < 0) {
TRACE_ERROR("Failed to encode message.\n");
goto cleanup;
}
rc = icsf_call(ld, reason, handle, sizeof(handle),
rule_array, sizeof(rule_array), ICSF_TAG_CSFPTRC, msg, NULL);
cleanup:
if (msg)
ber_free(msg, 1);
if (!rc && object)
handle_to_object_record(object, handle);
return rc;
}
/*
* List objects for a token indicated by `token_name`.
*
* `previous` must point to the last object returned by a previous call of
* `icsf_list_objects` or should be NULL for the first call.
*
* `records` must point to a buffer of object records with `records_len`
* elements. `records_len` is updated with the number of objects returned
* and it's zero when there's no more records left.
*/
int icsf_list_objects(LDAP * ld, int *reason, const char *token_name,
CK_ULONG attrs_len, CK_ATTRIBUTE * attrs,
struct icsf_object_record *previous,
struct icsf_object_record *records, size_t * records_len,
int all)
{
int rc = -1;
char handle[ICSF_HANDLE_LEN];
char rule_array[2 * ICSF_RULE_ITEM_LEN];
size_t rule_array_count = 1;
struct berval *bv_list = NULL;
size_t list_len;
size_t i;
CHECK_ARG_NON_NULL(ld);
CHECK_ARG_NON_NULL_AND_MAX_LEN(token_name, ICSF_TOKEN_NAME_LEN);
CHECK_ARG_NON_NULL(records);
CHECK_ARG_NON_NULL(records_len);
/* The first record that must be returned in `records` is the next one
* after `previous`, and for that the `previous` handle must be
* provided. When `previous` is null, the token handle should be used
* instead.
*/
if (previous)
object_record_to_handle(handle, previous);
else
token_name_to_handle(handle, token_name);
/* Should be 8 bytes padded. */
strpad(rule_array, "OBJECT", ICSF_RULE_ITEM_LEN, ' ');
if (all) {
strpad(rule_array + ICSF_RULE_ITEM_LEN, "ALL", ICSF_RULE_ITEM_LEN, ' ');
rule_array_count += 1;
}
list_len = ICSF_HANDLE_LEN * *records_len;
rc = icsf_list(ld, reason, handle, sizeof(handle), attrs_len, attrs,
rule_array, rule_array_count * ICSF_RULE_ITEM_LEN, &bv_list,
&list_len, *records_len);
if (ICSF_RC_IS_ERROR(rc))
goto cleanup;
/* Parse result */
*records_len = list_len / ICSF_HANDLE_LEN;
for (i = 0; i < *records_len; i++) {
size_t offset = i * ICSF_HANDLE_LEN;
handle_to_object_record(&records[i], bv_list->bv_val + offset);
}
cleanup:
if (bv_list)
ber_bvfree(bv_list);
return rc;
}
/*
* Destroy an object.
*/
int icsf_destroy_object(LDAP * ld, int *reason, struct icsf_object_record *obj)
{
/* Variables used as input */
char handle[ICSF_HANDLE_LEN];
char rule_array[1 * ICSF_RULE_ITEM_LEN];
CHECK_ARG_NON_NULL(ld);
CHECK_ARG_NON_NULL(obj);
object_record_to_handle(handle, obj);
/* Should be 8 bytes padded. */
strpad(rule_array, "OBJECT", ICSF_RULE_ITEM_LEN, ' ');
/*
* CSFPTRD service is used to destroy a token or an object. Handle
* indicates the token or object that must be destroyed and no
* additional data is needed.
*/
return icsf_call(ld, reason, handle, sizeof(handle), rule_array,
sizeof(rule_array), ICSF_TAG_CSFPTRD, NULL, NULL);
}
/*
* Generate an asymmetric key pair.
*/
int icsf_generate_key_pair(LDAP * ld, int *reason, const char *token_name,
CK_ATTRIBUTE * pub_attrs, CK_ULONG pub_attrs_len,
CK_ATTRIBUTE * priv_attrs, CK_ULONG priv_attrs_len,
struct icsf_object_record *pub_key_object,
struct icsf_object_record *priv_key_object)
{
int rc = -1;
char handle[ICSF_HANDLE_LEN];
BerElement *msg = NULL;
BerElement *result = NULL;
struct berval bv_priv_handle = { 0, NULL };
CHECK_ARG_NON_NULL(ld);
CHECK_ARG_NON_NULL_AND_MAX_LEN(token_name, ICSF_TOKEN_NAME_LEN);
CHECK_ARG_NON_NULL(pub_attrs);
CHECK_ARG_NON_NULL(priv_attrs);
CHECK_ARG_NON_NULL(pub_key_object);
CHECK_ARG_NON_NULL(priv_key_object);
token_name_to_handle(handle, token_name);
if (!(msg = ber_alloc_t(LBER_USE_DER))) {
TRACE_ERROR("%s\n", ock_err(ERR_HOST_MEMORY));
return rc;
}
/* Encode message:
*
* GKPInput ::= SEQUENCE {
* publicKeyAttrList Attributes,
* privateKeyAttrList Attributes
* }
*
* Attribute lists are built by icsf_ber_put_attribute_list()
*/
if (ber_printf(msg, "{") < 0 ||
icsf_ber_put_attribute_list(msg, pub_attrs, pub_attrs_len) < 0 ||
ber_printf(msg, "}{") < 0 ||
icsf_ber_put_attribute_list(msg, priv_attrs, priv_attrs_len) < 0 ||
ber_printf(msg, "}") < 0) {
TRACE_ERROR("Failed to encode message.\n");
goto cleanup;
}
rc = icsf_call(ld, reason, handle, sizeof(handle), "", 0,
ICSF_TAG_CSFPGKP, msg, &result);
if (rc)
goto cleanup;
/* Get private key handle from GKP response */
if (ber_scanf(result, "m", &bv_priv_handle) == LBER_ERROR) {
TRACE_ERROR("Failed to decode the response.\n");
rc = -1;
goto cleanup;
}
if (bv_priv_handle.bv_len != ICSF_HANDLE_LEN) {
TRACE_ERROR("Invalid length for handle: %lu\n",
(unsigned long) bv_priv_handle.bv_len);
rc = -1;
goto cleanup;
}
handle_to_object_record(priv_key_object, bv_priv_handle.bv_val);
/* Get public key handle from common ICSF header */
handle_to_object_record(pub_key_object, handle);
cleanup:
if (result)
ber_free(result, 1);
if (msg)
ber_free(msg, 1);
return rc;
}
/*
* Generate a symmetric key.
*/
int icsf_generate_secret_key(LDAP * ld, int *reason, const char *token_name,
CK_MECHANISM_PTR mech,
CK_ATTRIBUTE * attrs, CK_ULONG attrs_len,
struct icsf_object_record *object)
{
int rc = -1;
char handle[ICSF_HANDLE_LEN];
char rule_array[1 * ICSF_RULE_ITEM_LEN];
char param[2];
size_t param_len;
CK_VERSION_PTR version;
BerElement *msg = NULL;
CHECK_ARG_NON_NULL(ld);
CHECK_ARG_NON_NULL_AND_MAX_LEN(token_name, ICSF_TOKEN_NAME_LEN);
CHECK_ARG_NON_NULL(mech);
CHECK_ARG_NON_NULL(attrs);
token_name_to_handle(handle, token_name);
/* Map mechanism into the rule array */
switch (mech->mechanism) {
case CKM_TLS_PRE_MASTER_KEY_GEN:
strpad(rule_array, "TLS", sizeof(rule_array), ' ');
break;
case CKM_SSL3_PRE_MASTER_KEY_GEN:
strpad(rule_array, "SSL", sizeof(rule_array), ' ');
break;
case CKM_DSA_PARAMETER_GEN:
case CKM_DH_PKCS_PARAMETER_GEN:
strpad(rule_array, "PARMS", sizeof(rule_array), ' ');
break;
default:
strpad(rule_array, "KEY", sizeof(rule_array), ' ');
}
/* Fill parameters if necessary */
switch (mech->mechanism) {
case CKM_TLS_PRE_MASTER_KEY_GEN:
case CKM_SSL3_PRE_MASTER_KEY_GEN:
/* Check expected length */
if (mech->ulParameterLen != sizeof(*version)) {
TRACE_ERROR("Invalid mechanism parameter length: "
"%lu\n", (unsigned long) mech->ulParameterLen);
return -1;
}
/* Fill parameter with version numbers */
version = (CK_VERSION_PTR) mech->pParameter;
param[0] = version->major;
param[1] = version->minor;
param_len = 2;
break;
default:
/* Parameter should be empty */
param_len = 0;
}
if (!(msg = ber_alloc_t(LBER_USE_DER))) {
TRACE_ERROR("%s\n", ock_err(ERR_HOST_MEMORY));
return rc;
}
/* Encode message:
*
* GSKInput ::= SEQUENCE {
* attrList Attributes,
* parmsList OCTET STRING
* }
*
* attrList is built by icsf_ber_put_attribute_list()
*/
if (ber_printf(msg, "{") < 0) {
TRACE_ERROR("Failed to encode message.\n");
goto cleanup;
}
if (icsf_ber_put_attribute_list(msg, attrs, attrs_len) < 0 ||
ber_printf(msg, "}o", param, param_len) < 0) {
TRACE_ERROR("Failed to encode message.\n");
goto cleanup;
}
rc = icsf_call(ld, reason, handle, sizeof(handle), rule_array,
sizeof(rule_array), ICSF_TAG_CSFPGSK, msg, NULL);
if (!rc)
handle_to_object_record(object, handle);
cleanup:
if (msg)
ber_free(msg, 1);
return rc;
}
/*
* Return the rule array element for the given mechanism.
*/
static const char *get_algorithm_rule(CK_MECHANISM_PTR mech, int arg)
{
switch (mech->mechanism) {
case CKM_DES_ECB:
case CKM_DES_CBC:
case CKM_DES_CBC_PAD:
return "DES";
case CKM_DES3_ECB:
case CKM_DES3_CBC:
case CKM_DES3_CBC_PAD:
return "DES3";
case CKM_AES_ECB:
case CKM_AES_CBC:
case CKM_AES_CBC_PAD:
case CKM_AES_CTR:
return "AES";
case CKM_DSA:
return "DSA";
case CKM_ECDSA:
return "ECDSA";
case CKM_RSA_X_509:
return "RSA-ZERO";
case CKM_RSA_PKCS:
return "RSA-PKCS";
case CKM_SHA_1_HMAC:
return "SHA-1";
case CKM_SHA256_HMAC:
return "SHA-256";
case CKM_SHA384_HMAC:
return "SHA-384";
case CKM_SHA512_HMAC:
return "SHA-512";
case CKM_MD5_HMAC:
return "MD5";
case CKM_SSL3_MD5_MAC:
return "SSL3-MD5";
case CKM_SSL3_SHA1_MAC:
return "SSL3-SHA";
case CKM_SHA1_RSA_PKCS:
if (arg)
return "SHA-1 VER-RSA";
else
return "SHA-1 SIGN-RSA";
case CKM_SHA256_RSA_PKCS:
if (arg)
return "SHA-256 VER-RSA";
else
return "SHA-256 SIGN-RSA";
case CKM_SHA384_RSA_PKCS:
if (arg)
return "SHA-384 VER-RSA";
else
return "SHA-384 SIGN-RSA";
case CKM_SHA512_RSA_PKCS:
if (arg)
return "SHA-512 VER-RSA";
else
return "SHA-512 SIGN-RSA";
case CKM_MD5_RSA_PKCS:
if (arg)
return "MD5 VER-RSA";
else
return "MD5 SIGN-RSA";
case CKM_DSA_SHA1:
if (arg)
return "SHA-1 VER-DSA";
else
return "SHA-1 SIGN-DSA";
case CKM_ECDSA_SHA1:
if (arg)
return "SHA-1 VER-EC";
else
return "SHA-1 SIGN-EC";
case CKM_SSL3_KEY_AND_MAC_DERIVE:
return "SSL-KM";
case CKM_TLS_KEY_AND_MAC_DERIVE:
return "TLS-KM";
}
return NULL;
}
/*
* Return the rule array element for the cipher mode based on the given
* mechanism.
*/
static const char *get_cipher_mode(CK_MECHANISM_PTR mech)
{
switch (mech->mechanism) {
case CKM_DES_ECB:
case CKM_DES3_ECB:
case CKM_AES_ECB:
return "ECB";
case CKM_DES_CBC:
case CKM_DES3_CBC:
case CKM_AES_CBC:
return "CBC";
case CKM_DES_CBC_PAD:
case CKM_DES3_CBC_PAD:
case CKM_AES_CBC_PAD:
return "CBC-PAD";
}
return NULL;
}
/*
* Get the block size of supported algorithms/mechanism.
*/
CK_RV icsf_block_size(CK_MECHANISM_TYPE mech_type, CK_ULONG_PTR p_block_size)
{
CK_ULONG block_size;
switch (mech_type) {
case CKM_DES_CBC:
case CKM_DES_CBC_PAD:
case CKM_DES3_CBC:
case CKM_DES3_CBC_PAD:
case CKM_DES_ECB:
case CKM_DES3_ECB:
block_size = DES_BLOCK_SIZE;
break;
case CKM_AES_CBC:
case CKM_AES_CBC_PAD:
case CKM_AES_ECB:
block_size = AES_BLOCK_SIZE;
break;
case CKM_MD5_RSA_PKCS:
block_size = MD5_BLOCK_SIZE;
break;
case CKM_SHA1_RSA_PKCS:
case CKM_SHA256_RSA_PKCS:
case CKM_DSA_SHA1:
case CKM_ECDSA_SHA1:
block_size = SHA1_BLOCK_SIZE;
break;
case CKM_SHA384_RSA_PKCS:
case CKM_SHA512_RSA_PKCS:
block_size = SHA384_BLOCK_SIZE;
break;
default:
return CKR_MECHANISM_INVALID;
}
if (p_block_size)
*p_block_size = block_size;
return CKR_OK;
}
/*
* Extract and check the initialization vector contained in the given mechanism.
*/
static CK_RV icsf_encrypt_initial_vector(CK_MECHANISM_PTR mech, char *iv,
size_t * iv_len)
{
CK_RV rc;
int use_iv = 0;
size_t expected_iv_len = 0;
if ((rc = icsf_block_size(mech->mechanism, &expected_iv_len)))
return rc;
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:
use_iv = 1;
}
if (iv_len && *iv_len < expected_iv_len) {
TRACE_ERROR("IV too small.\n");
return CKR_FUNCTION_FAILED;
}
/* Set the Initialization vector */
if (iv)
memset(iv, 0, expected_iv_len);
if (use_iv) {
/*
* Otherwise use the mechanism parameter as the IV.
*/
if (mech->ulParameterLen != expected_iv_len) {
TRACE_ERROR("Invalid mechanism parameter length: %lu "
"(expected %lu)\n",
(unsigned long) mech->ulParameterLen,
(unsigned long) expected_iv_len);
return CKR_MECHANISM_PARAM_INVALID;
}
if (iv)
memcpy(iv, mech->pParameter, expected_iv_len);
}
if (iv_len)
*iv_len = expected_iv_len;
return 0;
}
/*
* Symmetric key encrypt.
*/
int icsf_secret_key_encrypt(LDAP * ld, int *p_reason,
struct icsf_object_record *key,
CK_MECHANISM_PTR mech, int chaining,
const char *clear_text, size_t clear_text_len,
char *cipher_text, size_t * p_cipher_text_len,
char *chaining_data, size_t * p_chaining_data_len)
{
int rc = 0;
char handle[ICSF_HANDLE_LEN];
char rule_array[3 * ICSF_RULE_ITEM_LEN];
BerElement *msg = NULL;
BerElement *result = NULL;
char init_vector[32];
size_t init_vector_len = sizeof(init_vector);
struct berval bv_cipher_data = { 0UL, NULL };
struct berval bv_chaining_data = { 0UL, NULL };
const char *rule_alg, *rule_cipher;
int reason = 0, length = 0;
CHECK_ARG_NON_NULL(ld);
CHECK_ARG_NON_NULL(key);
CHECK_ARG_NON_NULL(mech);
CHECK_ARG_NON_NULL(clear_text);
CHECK_ARG_NON_NULL(p_cipher_text_len);
if (!ICSF_CHAINING_IS_VALID(chaining)) {
TRACE_ERROR("Invalid value for chaining: %d\n", chaining);
return -1;
}
object_record_to_handle(handle, key);
/*
* Add to rule array the algorithm, the cipher mode and the
* chaining mode.
*/
if (!(rule_alg = get_algorithm_rule(mech, 0))) {
TRACE_ERROR("Invalid algorithm: %lu\n",
(unsigned long) mech->mechanism);
return -1;
}
if (!(rule_cipher = get_cipher_mode(mech))) {
TRACE_ERROR("Invalid cipher mode: %lu\n",
(unsigned long) mech->mechanism);
return -1;
}
strpad(rule_array + 0 * ICSF_RULE_ITEM_LEN, rule_alg,
ICSF_RULE_ITEM_LEN, ' ');
strpad(rule_array + 1 * ICSF_RULE_ITEM_LEN, rule_cipher,
ICSF_RULE_ITEM_LEN, ' ');
strpad(rule_array + 2 * ICSF_RULE_ITEM_LEN, ICSF_CHAINING(chaining),
ICSF_RULE_ITEM_LEN, ' ');
/* Set the IV based on the given mechanism */
if (chaining != ICSF_CHAINING_INITIAL && chaining != ICSF_CHAINING_ONLY) {
rc = icsf_encrypt_initial_vector(mech, NULL, NULL);
memset(init_vector, 0, init_vector_len);
} else {
rc = icsf_encrypt_initial_vector(mech, init_vector, &init_vector_len);
}
if (rc)
return -1;
/* Build request */
if (!(msg = ber_alloc_t(LBER_USE_DER))) {
TRACE_ERROR("%s\n", ock_err(ERR_HOST_MEMORY));
return -1;
}
if (ber_printf(msg, "toooi",
0 | LBER_CLASS_CONTEXT, init_vector, init_vector_len,
(chaining_data) ? chaining_data : "",
(p_chaining_data_len) ? *p_chaining_data_len : 0UL,
clear_text, clear_text_len,
(cipher_text) ? *p_cipher_text_len : 0UL) < 0) {
rc = -1;
TRACE_ERROR("Failed to encode message: %d.\n", rc);
goto done;
}
/* Call service */
rc = icsf_call(ld, &reason, handle, sizeof(handle),
rule_array, sizeof(rule_array),
ICSF_TAG_CSFPSKE, msg, &result);
if (p_reason)
*p_reason = reason;
if (ICSF_RC_IS_ERROR(rc)
&& reason != ICSF_REASON_OUTPUT_PARAMETER_TOO_SHORT)
goto done;
/* Parse response */
if (ber_scanf(result, "{mmi", &bv_chaining_data, &bv_cipher_data,
&length) == LBER_ERROR) {
rc = -1;
TRACE_ERROR("Failed to decode the response.\n");
goto done;
}
*p_cipher_text_len = length;
/* Copy encrypted data */
if (bv_cipher_data.bv_len > *p_cipher_text_len) {
TRACE_ERROR("Cipher data longer than expected: %lu "
"(expected %lu)\n",
(unsigned long) bv_cipher_data.bv_len,
(unsigned long) *p_cipher_text_len);
rc = -1;
goto done;
}
if (cipher_text)
memcpy(cipher_text, bv_cipher_data.bv_val, bv_cipher_data.bv_len);
/* Copy chaining data */
if (p_chaining_data_len) {
if (bv_chaining_data.bv_len > *p_chaining_data_len) {
TRACE_ERROR("Chaining data longer than expected: %lu "
"(expected %lu)\n",
(unsigned long) bv_chaining_data.bv_len,
(unsigned long) *p_chaining_data_len);
rc = -1;
goto done;
}
*p_chaining_data_len = bv_chaining_data.bv_len;
if (chaining_data) {
memcpy(chaining_data, bv_chaining_data.bv_val,
*p_chaining_data_len);
}
}
done:
if (result)
ber_free(result, 1);
if (msg)
ber_free(msg, 1);
return rc;
}
/*
* Symmetric key decrypt.
*/
int icsf_secret_key_decrypt(LDAP * ld, int *p_reason,
struct icsf_object_record *key,
CK_MECHANISM_PTR mech, int chaining,
const char *cipher_text, size_t cipher_text_len,
char *clear_text, size_t * p_clear_text_len,
char *chaining_data, size_t * p_chaining_data_len)
{
int rc = 0;
char handle[ICSF_HANDLE_LEN];
char rule_array[3 * ICSF_RULE_ITEM_LEN];
BerElement *msg = NULL;
BerElement *result = NULL;
char init_vector[32];
size_t init_vector_len = sizeof(init_vector);
struct berval bv_clear_data = { 0UL, NULL };
struct berval bv_chaining_data = { 0UL, NULL };
const char *rule_alg, *rule_cipher;
int reason = 0, length = 0;
size_t clear_len;
CHECK_ARG_NON_NULL(ld);
CHECK_ARG_NON_NULL(key);
CHECK_ARG_NON_NULL(mech);
CHECK_ARG_NON_NULL(cipher_text);
CHECK_ARG_NON_NULL(p_clear_text_len);
if (!ICSF_CHAINING_IS_VALID(chaining)) {
TRACE_ERROR("Invalid value for chaining: %d\n", chaining);
return -1;
}
object_record_to_handle(handle, key);
/*
* Add to rule array the algorithm, the cipher mode and the
* chaining mode.
*/
if (!(rule_alg = get_algorithm_rule(mech, 0))) {
TRACE_ERROR("Invalid algorithm: %lu\n",
(unsigned long) mech->mechanism);
return -1;
}
if (!(rule_cipher = get_cipher_mode(mech))) {
TRACE_ERROR("Invalid cipher mode: %lu\n",
(unsigned long) mech->mechanism);
return -1;
}
strpad(rule_array + 0 * ICSF_RULE_ITEM_LEN, rule_alg,
ICSF_RULE_ITEM_LEN, ' ');
strpad(rule_array + 1 * ICSF_RULE_ITEM_LEN, rule_cipher,
ICSF_RULE_ITEM_LEN, ' ');
strpad(rule_array + 2 * ICSF_RULE_ITEM_LEN, ICSF_CHAINING(chaining),
ICSF_RULE_ITEM_LEN, ' ');
/* Set the IV based on the given mechanism */
if (chaining != ICSF_CHAINING_INITIAL && chaining != ICSF_CHAINING_ONLY) {
rc = icsf_encrypt_initial_vector(mech, NULL, NULL);
memset(init_vector, 0, init_vector_len);
} else {
rc = icsf_encrypt_initial_vector(mech, init_vector, &init_vector_len);
}
if (rc)
return -1;
/* Build request */
if (!(msg = ber_alloc_t(LBER_USE_DER))) {
TRACE_ERROR("%s\n", ock_err(ERR_HOST_MEMORY));
return -1;
}
/*
* For mechs with CBC padding (CKM_*_CBC_PAD), we need to specify the clear
* text length at least as large as the cipher text length. The padding is
* removed, but the call still wants the output size to be that large,
* otherwise it returns an ICSF_REASON_OUTPUT_PARAMETER_TOO_SHORT error,
* although the resulting (un-padded) clear text fits into the user
* supplied buffer.
*/
clear_len = *p_clear_text_len;
switch (mech->mechanism) {
case CKM_DES_CBC_PAD:
case CKM_DES3_CBC_PAD:
case CKM_AES_CBC_PAD:
if (clear_len < cipher_text_len)
clear_len = cipher_text_len;
break;
default:
break;
}
if (ber_printf(msg, "totototi",
0 | LBER_CLASS_CONTEXT | LBER_PRIMITIVE,
init_vector, init_vector_len,
2 | LBER_CLASS_CONTEXT | LBER_PRIMITIVE,
(chaining_data) ? chaining_data : "",
(p_chaining_data_len) ? *p_chaining_data_len : 0UL,
3 | LBER_CLASS_CONTEXT | LBER_PRIMITIVE,
cipher_text, cipher_text_len,
4 | LBER_CLASS_CONTEXT | LBER_PRIMITIVE,
(clear_text) ? clear_len : 0UL) < 0) {
rc = -1;
TRACE_ERROR("Failed to encode message: %d.\n", rc);
goto done;
}
/* Call service */
rc = icsf_call(ld, &reason, handle, sizeof(handle),
rule_array, sizeof(rule_array),
ICSF_TAG_CSFPSKD, msg, &result);
if (p_reason)
*p_reason = reason;
if (ICSF_RC_IS_ERROR(rc) &&
reason != ICSF_REASON_OUTPUT_PARAMETER_TOO_SHORT)
goto done;
/* Parse response */
if (ber_scanf(result, "{mmi", &bv_chaining_data, &bv_clear_data,
&length) == LBER_ERROR) {
rc = -1;
TRACE_ERROR("Failed to decode the response.\n");
goto done;
}
*p_clear_text_len = length;
/* Copy encrypted data */
if (bv_clear_data.bv_len > *p_clear_text_len) {
TRACE_ERROR("Clear data longer than expected: %lu "
"(expected %lu)\n",
(unsigned long) bv_clear_data.bv_len,
(unsigned long) *p_clear_text_len);
rc = -1;
goto done;
}
if (clear_text)
memcpy(clear_text, bv_clear_data.bv_val, bv_clear_data.bv_len);
/* Copy chaining data */
if (p_chaining_data_len) {
if (bv_chaining_data.bv_len > *p_chaining_data_len) {
TRACE_ERROR("Chaining data longer than expected: %lu "
"(expected %lu)\n",
(unsigned long) bv_chaining_data.bv_len,
(unsigned long) *p_chaining_data_len);
rc = -1;
goto done;
}
*p_chaining_data_len = bv_chaining_data.bv_len;
if (chaining_data) {
memcpy(chaining_data, bv_chaining_data.bv_val,
*p_chaining_data_len);
}
}
done:
if (result)
ber_free(result, 1);
if (msg)
ber_free(msg, 1);
return rc;
}
static int icsf_ber_decode_get_attribute_list(BerElement * berbuf,
CK_ATTRIBUTE * attrs,
CK_ULONG attrs_len)
{
int attrtype;
struct berval attrbval = { 0, NULL };
ber_int_t intval;
unsigned int i;
CK_ULONG found = 0;
ber_tag_t tag;
CK_RV rc = CKR_OK;
if (ber_scanf(berbuf, "{{") == LBER_ERROR)
goto decode_error;
while (1) {
/* get tag preceding sequence */
if (ber_scanf(berbuf, "t", &tag) == LBER_ERROR)
goto decode_error;
/* is it a sequence (thus attribute) */
if (tag != (LBER_CLASS_UNIVERSAL | LBER_CONSTRUCTED | LBER_SEQUENCE))
break;
/* sequence, so get attribute info */
if (ber_scanf(berbuf, "{it", &attrtype, &tag) == LBER_ERROR)
goto decode_error;
if ((tag & LBER_BIG_TAG_MASK) == 0) {
if (ber_scanf(berbuf, "o}", &attrbval) == LBER_ERROR)
goto decode_error;
} else {
if (ber_scanf(berbuf, "i}", &intval) == LBER_ERROR)
goto decode_error;
attrbval.bv_len = sizeof(CK_ULONG);
}
/* see if this type matches any that we need to
* get value for. if so, then get the value, otherwise
* continue until we have found all of them or there
* are no more attributes to search
*/
for (i = 0; i < attrs_len; i++) {
if (attrs[i].type != (CK_ATTRIBUTE_TYPE)attrtype)
continue;
/* we have decoded attribute, now add the values */
if (attrs[i].pValue == NULL) {
attrs[i].ulValueLen = attrbval.bv_len;
} else if (attrs[i].ulValueLen >= attrbval.bv_len) {
if ((tag & LBER_BIG_TAG_MASK) == 0) {
memcpy(attrs[i].pValue, attrbval.bv_val, attrbval.bv_len);
} else {
*((CK_ULONG *) attrs[i].pValue) = intval;
}
attrs[i].ulValueLen = attrbval.bv_len;
} else {
rc = CKR_BUFFER_TOO_SMALL;
attrs[i].ulValueLen = -1;
goto decode_error;
}
/* keep count of how many are found. */
found++;
}
/* if we have found all the values for our list, then
* we are done.
*/
if (found == attrs_len)
break;
}
/* if we have gone through the entire loop and could not find
* all of the attributes in our list, mark this as an error.
*/
if (found < attrs_len) {
TRACE_ERROR("%s\n", ock_err(ERR_ATTRIBUTE_TYPE_INVALID));
rc = CKR_ATTRIBUTE_TYPE_INVALID;
goto decode_error;
}
return rc;
decode_error:
TRACE_ERROR("Failed to decode message.\n");
if (!rc)
rc = CKR_FUNCTION_FAILED;
return rc;
}
int icsf_get_attribute(LDAP * ld, int *reason,
struct icsf_object_record *object, CK_ATTRIBUTE * attrs,
CK_ULONG attrs_len)
{
char handle[ICSF_HANDLE_LEN];
BerElement *msg = NULL;
BerElement *result = NULL;
unsigned int i;
int rc = 0;
CHECK_ARG_NON_NULL(ld);
CHECK_ARG_NON_NULL(attrs);
CHECK_ARG_NON_NULL(object);
object_record_to_handle(handle, object);
if (!(msg = ber_alloc_t(LBER_USE_DER))) {
TRACE_ERROR("%s\n", ock_err(ERR_HOST_MEMORY));
return CKR_HOST_MEMORY;
}
/* Encode message:
*
* GAVInput ::= attrListLen
*
* attrListLen ::= INTEGER (0 .. MaxCSFPInteger)
*
*/
rc = ber_printf(msg, "i", attrs_len);
if (rc < 0)
goto cleanup;
rc = icsf_call(ld, reason, handle, sizeof(handle), "", 0,
ICSF_TAG_CSFPGAV, msg, &result);
if (rc != 0) {
TRACE_DEVEL("icsf_call failed.\n");
goto cleanup;
}
/* Before decoding the result, initialize the attribute values length.
* This will help to indicate which attributes were not found
* or not enough storage was allocated for the value.
*/
for (i = 0; i < attrs_len; i++)
attrs[i].ulValueLen = (CK_ULONG) - 1;
/* Decode the result:
*
* GAVOutput ::= SEQUENCE {
* attrList Attributes,
* attrListLen INTEGER (0 .. MaxCSFPInteger)
* }
*
* asn.1 {{{ito|i} {ito|i} ...}i}
*/
rc = icsf_ber_decode_get_attribute_list(result, attrs, attrs_len);
if (rc < 0) {
TRACE_ERROR("Failed to decode message.\n");
goto cleanup;
}
cleanup:
if (msg)
ber_free(msg, 1);
if (result)
ber_free(result, 1);
return rc;
}
int icsf_set_attribute(LDAP * ld, int *reason,
struct icsf_object_record *object, CK_ATTRIBUTE * attrs,
CK_ULONG attrs_len)
{
int rc = -1;
char handle[ICSF_HANDLE_LEN];
BerElement *msg = NULL;
CHECK_ARG_NON_NULL(ld);
CHECK_ARG_NON_NULL(attrs);
object_record_to_handle(handle, object);
if (!(msg = ber_alloc_t(LBER_USE_DER))) {
return rc;
}
/* Encode message:
*
* SAVInput ::= Attributes
*
* attrList is built by icsf_ber_put_attribute_list()
*/
if (icsf_ber_put_attribute_list(msg, attrs, attrs_len) < 0) {
TRACE_ERROR("Failed to encode message.\n");
goto cleanup;
}
rc = icsf_call(ld, reason, handle, sizeof(handle), "", 0,
ICSF_TAG_CSFPSAV, msg, NULL);
if (rc < 0) {
TRACE_ERROR("icsf_call failed.\n");
goto cleanup;
}
/* Decode message:
*
* SAVOutput ::= NULL
*
*/
cleanup:
if (msg)
ber_free(msg, 1);
return rc;
}
/*
* Sign or decrypt data using a private key.
*/
int icsf_private_key_sign(LDAP * ld, int *p_reason, int decrypt,
struct icsf_object_record *key, CK_MECHANISM_PTR mech,
const char *cipher_text, size_t cipher_text_len,
char *clear_text, size_t * p_clear_text_len)
{
int rc;
int reason = 0;
char handle[ICSF_HANDLE_LEN];
char rule_array[2 * ICSF_RULE_ITEM_LEN];
size_t rule_array_count = 0;
const char *rule_alg;
BerElement *msg = NULL;
BerElement *result = NULL;
struct berval bv_clear_text = { 0, NULL };
int length = 0;
CHECK_ARG_NON_NULL(ld);
CHECK_ARG_NON_NULL(key);
CHECK_ARG_NON_NULL(mech);
CHECK_ARG_NON_NULL(cipher_text);
CHECK_ARG_NON_NULL(p_clear_text_len);
object_record_to_handle(handle, key);
/* Build rule array based on mechanism */
if (!(rule_alg = get_algorithm_rule(mech, 0))) {
TRACE_ERROR("Invalid algorithm: %lu\n",
(unsigned long) mech->mechanism);
return -1;
}
strpad(rule_array, rule_alg, ICSF_RULE_ITEM_LEN, ' ');
rule_array_count += 1;
if (decrypt) {
strpad(rule_array + (rule_array_count * ICSF_RULE_ITEM_LEN),
"DECRYPT", ICSF_RULE_ITEM_LEN, ' ');
rule_array_count += 1;
}
/* Build request */
if (!(msg = ber_alloc_t(LBER_USE_DER))) {
TRACE_ERROR("%s\n", ock_err(ERR_HOST_MEMORY));
return -1;
}
if (ber_printf(msg, "oi", cipher_text, (ber_int_t) cipher_text_len,
(!clear_text) ? 0 : ((ber_int_t) * p_clear_text_len)) < 0) {
rc = -1;
TRACE_ERROR("Failed to encode message: %d.\n", rc);
goto done;
}
/* Call service */
rc = icsf_call(ld, &reason, handle, sizeof(handle), rule_array,
rule_array_count * ICSF_RULE_ITEM_LEN, ICSF_TAG_CSFPPKS,
msg, &result);
if (p_reason)
*p_reason = reason;
if (ICSF_RC_IS_ERROR(rc)
&& reason != ICSF_REASON_OUTPUT_PARAMETER_TOO_SHORT)
goto done;
if (ber_scanf(result, "{mi}", &bv_clear_text, &length) == LBER_ERROR) {
rc = -1;
TRACE_ERROR("Failed to decode the response.\n");
goto done;
}
/* Copy clear data */
*p_clear_text_len = length;
if (bv_clear_text.bv_len > *p_clear_text_len) {
TRACE_ERROR("Clear data longer than expected: %lu "
"(expected %lu)\n",
(unsigned long) bv_clear_text.bv_len,
(unsigned long) *p_clear_text_len);
rc = -1;
goto done;
}
if (clear_text)
memcpy(clear_text, bv_clear_text.bv_val, *p_clear_text_len);
done:
if (result)
ber_free(result, 1);
if (msg)
ber_free(msg, 1);
return rc;
}
/*
* Verify or encrypt using a public. key.
*/
int icsf_public_key_verify(LDAP * ld, int *p_reason, int encrypt,
struct icsf_object_record *key,
CK_MECHANISM_PTR mech, const char *clear_text,
size_t clear_text_len, char *cipher_text,
size_t * p_cipher_text_len)
{
int rc;
int reason = 0;
char handle[ICSF_HANDLE_LEN];
char rule_array[2 * ICSF_RULE_ITEM_LEN];
size_t rule_array_count = 0;
const char *rule_alg;
BerElement *msg = NULL;
BerElement *result = NULL;
struct berval bv_cipher_text = { 0, NULL };
int length = 0;
CHECK_ARG_NON_NULL(ld);
CHECK_ARG_NON_NULL(key);
CHECK_ARG_NON_NULL(mech);
CHECK_ARG_NON_NULL(clear_text);
CHECK_ARG_NON_NULL(p_cipher_text_len);
object_record_to_handle(handle, key);
/* Build rule array based on mechanism */
if (!(rule_alg = get_algorithm_rule(mech, 0))) {
TRACE_ERROR("Invalid algorithm: %lu\n",
(unsigned long) mech->mechanism);
return -1;
}
strpad(rule_array, rule_alg, ICSF_RULE_ITEM_LEN, ' ');
rule_array_count += 1;
if (encrypt) {
strpad(rule_array + (rule_array_count * ICSF_RULE_ITEM_LEN),
"ENCRYPT", ICSF_RULE_ITEM_LEN, ' ');
rule_array_count += 1;
}
/* Build request */
if (!(msg = ber_alloc_t(LBER_USE_DER))) {
TRACE_ERROR("%s\n", ock_err(ERR_HOST_MEMORY));
return -1;
}
if (encrypt) {
rc = ber_printf(msg, "oti", clear_text, clear_text_len,
0 | LBER_CLASS_CONTEXT, *p_cipher_text_len);
} else {
rc = ber_printf(msg, "oto", cipher_text, *p_cipher_text_len,
1 | LBER_CLASS_CONTEXT, clear_text, clear_text_len);
}
if (rc < 0) {
rc = -1;
TRACE_ERROR("Failed to encode message: %d.\n", rc);
goto done;
}
/* Call request */
rc = icsf_call(ld, &reason, handle, sizeof(handle), rule_array,
rule_array_count * ICSF_RULE_ITEM_LEN, ICSF_TAG_CSFPPKV,
msg, &result);
if (p_reason)
*p_reason = reason;
if (ICSF_RC_IS_ERROR(rc)
&& reason != ICSF_REASON_OUTPUT_PARAMETER_TOO_SHORT)
goto done;
/* There's no output data when verifying */
if (!encrypt)
goto done;
if (ber_scanf(result, "{mi}", &bv_cipher_text, &length) == LBER_ERROR) {
rc = -1;
TRACE_ERROR("Failed to decode the response.\n");
goto done;
}
/* Copy clear data */
*p_cipher_text_len = length;
if (bv_cipher_text.bv_len != *p_cipher_text_len) {
TRACE_ERROR("Cipher data length different that expected: %lu "
"(expected %lu)\n",
(unsigned long) bv_cipher_text.bv_len,
(unsigned long) *p_cipher_text_len);
rc = -1;
goto done;
}
if (cipher_text)
memcpy(cipher_text, bv_cipher_text.bv_val, *p_cipher_text_len);
done:
if (result)
ber_free(result, 1);
if (msg)
ber_free(msg, 1);
return rc;
}
int icsf_hmac_sign(LDAP * ld, int *reason, struct icsf_object_record *key,
CK_MECHANISM_PTR mech, const char *chain_rule,
const char *clear_text, size_t clear_text_len, char *hmac,
size_t * hmac_len, char *chain_data, size_t * chain_data_len)
{
int rc = 0;
char handle[ICSF_HANDLE_LEN];
char rule_array[2 * ICSF_RULE_ITEM_LEN];
BerElement *msg = NULL;
BerElement *result = NULL;
struct berval bvHmac = { 0, NULL };
struct berval bvChain = { 0, NULL };
int hmac_length;
const char *rule_alg;
CHECK_ARG_NON_NULL(ld);
CHECK_ARG_NON_NULL(key);
CHECK_ARG_NON_NULL(mech);
object_record_to_handle(handle, key);
/* Add to rule array, the algorithm and chaining mode */
if (!(rule_alg = get_algorithm_rule(mech, 0))) {
TRACE_ERROR("Invalid algorithm: %lu\n",
(unsigned long) mech->mechanism);
return -1;
}
strpad(rule_array + 0 * ICSF_RULE_ITEM_LEN, rule_alg,
ICSF_RULE_ITEM_LEN, ' ');
strpad(rule_array + 1 * ICSF_RULE_ITEM_LEN, chain_rule,
ICSF_RULE_ITEM_LEN, ' ');
/* Build the request */
if (!(msg = ber_alloc_t(LBER_USE_DER))) {
TRACE_ERROR("%s\n", ock_err(ERR_HOST_MEMORY));
return -1;
}
/* Input ASN.1 for CSFPHMG.
* HMGInput ::= SEQUENCE {
* text OCTET STRING,
* chainData OCTET STRING,
* hmacLength INTEGER (0 .. MaxCSFPInteger)
* }
*/
if (ber_printf(msg, "ooi", (clear_text) ? clear_text : "",
clear_text_len, chain_data, *chain_data_len,
*hmac_len) < 0) {
rc = -1;
TRACE_ERROR("Failed to encode message: %d.\n", rc);
goto done;
}
/* Call service */
rc = icsf_call(ld, reason, handle, sizeof(handle), rule_array,
sizeof(rule_array), ICSF_TAG_CSFPHMG, msg, &result);
if (ICSF_RC_IS_ERROR(rc)) {
TRACE_DEVEL("icsf_call failed\n");
goto done;
}
/* Parse the response.
* HMGOutput ::= SEQUENCE {
* chainData OCTET STRING,
* hmac OCTET STRING,
* hmacLength INTEGER (0 .. MaxCSFPInteger)
* }
*
* Where,
* chainData - A string that specifies the chaining data returned
* during multi-part HMAC hashing in the CSFPHMG callable service.
* This chainData must be specified on subsequent calls to
* the CSFPHMG callable service.
* hmac - A string containing the HMAC value
* hmacLength - ignored by ICSF
*
* NOTE:
* - chainData is always blindly returned, whether it is pertinent
* or not.
* - For a FIRST or MIDDLE request, hmac is returned as a zero length
* string.
* - For a LAST or ONLY request, hmac is returned as a string of
* appropriate length based on the mechanism. The validity of the hmac
* contents are subject to ICSF behavior, based on the ICSF return
* code and ICSF reason code.
* - The hmacLength is ignored by ICSF and has no affect on how we
* encode the returned hmac. The hmacLength is passed along through
* the BER encoded messages and in and out of the ICSF call in case
* this changes in the future.
*/
if (ber_scanf(result, "{ooi}", &bvChain, &bvHmac, &hmac_length)
== LBER_ERROR) {
rc = -1;
TRACE_ERROR("Failed to decode the response.\n");
goto done;
}
/* copy the chained data even if not using it */
*chain_data_len = bvChain.bv_len;
memcpy(chain_data, bvChain.bv_val, bvChain.bv_len);
/* copy the hmac when needed */
if (*hmac_len) {
if (*hmac_len >= bvHmac.bv_len) {
memcpy(hmac, bvHmac.bv_val, bvHmac.bv_len);
*hmac_len = bvHmac.bv_len;
} else {
/* supplied buffer is too small */
*reason = 3003;
}
}
done:
if (result)
ber_free(result, 1);
if (msg)
ber_free(msg, 1);
if (bvHmac.bv_val)
ber_memfree(bvHmac.bv_val);
if (bvChain.bv_val)
ber_memfree(bvChain.bv_val);
return rc;
}
int icsf_hmac_verify(LDAP * ld, int *reason, struct icsf_object_record *key,
CK_MECHANISM_PTR mech, const char *chain_rule,
const char *clear_text, size_t clear_text_len,
char *hmac, size_t hmac_len, char *chain_data,
size_t * chain_data_len)
{
int rc = 0;
char handle[ICSF_HANDLE_LEN];
char rule_array[2 * ICSF_RULE_ITEM_LEN];
BerElement *msg = NULL;
BerElement *result = NULL;
struct berval bvChain = { 0UL, NULL };
const char *rule_alg;
CHECK_ARG_NON_NULL(ld);
CHECK_ARG_NON_NULL(key);
CHECK_ARG_NON_NULL(mech);
object_record_to_handle(handle, key);
/* Add to rule array, the algorithm and chaining mode */
if (!(rule_alg = get_algorithm_rule(mech, 0))) {
TRACE_ERROR("Invalid algorithm: %lu\n",
(unsigned long) mech->mechanism);
return -1;
}
strpad(rule_array + 0 * ICSF_RULE_ITEM_LEN, rule_alg,
ICSF_RULE_ITEM_LEN, ' ');
strpad(rule_array + 1 * ICSF_RULE_ITEM_LEN, chain_rule,
ICSF_RULE_ITEM_LEN, ' ');
/* Build the request */
if (!(msg = ber_alloc_t(LBER_USE_DER))) {
TRACE_ERROR("%s\n", ock_err(ERR_HOST_MEMORY));
return -1;
}
/*
* Input ASN.1 for CSFPHMV.
* HMVInput ::= SEQUENCE {
* text OCTET STRING,
* chainData OCTET STRING,
* hmac OCTET STRING
* }
*
* Where,
* text - A string that identifies the text to test an HMAC hash
* in the CSFPHMV callable service.
* chainData - A string that specifies the chaining data maintained
* during multi-part HMAC hashing in the CSFPHMV callable
* service.
* hmac - A string that identifies the HMAC hash to verify
* against the text in the CSFPHMV callable service.
* NOTE:
* - chainData is always required, even on a FIRST call (where it is
* not really an input) and even on an ONLY call (where there is no
* chaining). An HMV ONLY call fails with reason_code=11000 when
* chain_data_length is 0.
* - For an ONLY call or LAST call, hmac MUST be at least as
* many bytes in length as required based on the mechanism.
* - For a FIRST or MIDDLE call, hmac is ignored.
*/
if (ber_printf(msg, "ooo", (clear_text) ? clear_text : "",
clear_text_len, chain_data, *chain_data_len,
hmac, hmac_len) < 0) {
rc = -1;
TRACE_ERROR("Failed to encode message: %d.\n", rc);
goto done;
}
/* Call service */
rc = icsf_call(ld, reason, handle, sizeof(handle), rule_array,
sizeof(rule_array), ICSF_TAG_CSFPHMV, msg, &result);
if (ICSF_RC_IS_ERROR(rc)) {
TRACE_DEVEL("icsf_call failed\n");
goto done;
}
/* Parse the response.
* HMVOutput ::= chainData OCTET STRING
*
* Where,
* chainData - A string that specifies the chaining data returned
* during multi-part HMAC hashing in the CSFPHMV callable service.
* This chainData must be specified on subsequent calls to
* the CSFPHMV callable service.
*
* NOTE:
* - chainData is always blindly returned, whether it is pertinent
* or not.
*/
if (ber_scanf(result, "m", &bvChain) == LBER_ERROR) {
rc = -1;
TRACE_ERROR("Failed to decode the response.\n");
goto done;
}
/* if chaining, copy the chained data */
*chain_data_len = bvChain.bv_len;
memcpy(chain_data, bvChain.bv_val, bvChain.bv_len);
done:
if (result)
ber_free(result, 1);
if (msg)
ber_free(msg, 1);
return rc;
}
/*
* Wrap a key.
*/
int icsf_wrap_key(LDAP * ld, int *p_reason, CK_MECHANISM_PTR mech,
struct icsf_object_record *wrapping_key,
struct icsf_object_record *key, CK_BYTE_PTR wrapped_key,
CK_ULONG_PTR p_wrapped_key_len)
{
int rc = 0;
int reason = 0;
char handle[ICSF_HANDLE_LEN];
char wrapping_handle[ICSF_HANDLE_LEN];
char rule_array[2 * ICSF_RULE_ITEM_LEN];
size_t rule_array_count = 0;
const char *rule_fmt = NULL;
const char *rule_alg = NULL;
BerElement *msg = NULL;
BerElement *result = NULL;
struct berval bv_wrapped_key = { 0, NULL };
ber_int_t wrapped_key_len = 0;
CHECK_ARG_NON_NULL(ld);
CHECK_ARG_NON_NULL(mech);
CHECK_ARG_NON_NULL(wrapping_key);
CHECK_ARG_NON_NULL(key);
CHECK_ARG_NON_NULL(p_wrapped_key_len);
object_record_to_handle(handle, key);
object_record_to_handle(wrapping_handle, wrapping_key);
/* Build rule array based on mechanism */
switch (mech->mechanism) {
case CKM_RSA_PKCS:
rule_fmt = "PKCS-1.2";
break;
case CKM_DES_CBC_PAD:
case CKM_DES3_CBC_PAD:
case CKM_AES_CBC_PAD:
rule_fmt = "PKCS-8";
if (!(rule_alg = get_algorithm_rule(mech, 0))) {
TRACE_ERROR("Invalid algorithm: %lu\n",
(unsigned long) mech->mechanism);
return -1;
}
break;
default:
TRACE_ERROR("%s\n", ock_err(ERR_MECHANISM_INVALID));
return -1;
}
strpad(rule_array, rule_fmt, ICSF_RULE_ITEM_LEN, ' ');
rule_array_count += 1;
if (rule_alg) {
strpad(rule_array + (rule_array_count * ICSF_RULE_ITEM_LEN),
rule_alg, ICSF_RULE_ITEM_LEN, ' ');
rule_array_count += 1;
}
/* Build request */
if (!(msg = ber_alloc_t(LBER_USE_DER))) {
TRACE_ERROR("%s\n", ock_err(ERR_HOST_MEMORY));
return -1;
}
/* Encode message:
*
* WPKInput ::= SEQUENCE {
* wrappingHandle OCTET STRING,
* wrappedKeyMaxLen INTEGER (0 .. MaxCSFPInteger),
* initialValue OCTET STRING
* }
* For a size query (wrapped_key is NULL), we set wrappedKeyMaxLen to
* USHRT_MAX (65535), which is hopefully large enough.
*/
rc = ber_printf(msg, "ois", wrapping_handle, sizeof(wrapping_handle),
wrapped_key != NULL ? (ber_int_t)*p_wrapped_key_len : USHRT_MAX, "");
if (rc < 0) {
rc = -1;
TRACE_ERROR("Failed to encode message: %d.\n", rc);
goto done;
}
/* Call request */
rc = icsf_call(ld, &reason, handle, sizeof(handle), rule_array,
rule_array_count * ICSF_RULE_ITEM_LEN, ICSF_TAG_CSFPWPK,
msg, &result);
if (p_reason)
*p_reason = reason;
if (ICSF_RC_IS_ERROR(rc)
&& reason != ICSF_REASON_OUTPUT_PARAMETER_TOO_SHORT)
goto done;
/* Decode message:
*
* WPKOutput ::= SEQUENCE {
* wrappedKey OCTET STRING,
* wrappedKeyLen INTEGER
* }
*/
if (ber_scanf(result, "{mi}", &bv_wrapped_key, &wrapped_key_len)
== LBER_ERROR) {
rc = -1;
TRACE_ERROR("Failed to decode the response.\n");
goto done;
}
*p_wrapped_key_len = wrapped_key_len;
/* Copy wrapped key */
if (bv_wrapped_key.bv_len > *p_wrapped_key_len) {
TRACE_ERROR("Wrapped key length different that expected: %lu "
"(expected %lu)\n",
(unsigned long) bv_wrapped_key.bv_len,
(unsigned long) *p_wrapped_key_len);
rc = -1;
goto done;
}
if (wrapped_key)
memcpy(wrapped_key, bv_wrapped_key.bv_val, *p_wrapped_key_len);
done:
if (result)
ber_free(result, 1);
if (msg)
ber_free(msg, 1);
return rc;
}
/*
* Unwrap a key.
*/
int icsf_unwrap_key(LDAP * ld, int *p_reason, CK_MECHANISM_PTR mech,
struct icsf_object_record *unwrapping_key,
CK_BYTE_PTR wrapped_key, CK_ULONG wrapped_key_len,
CK_ATTRIBUTE_PTR attrs, CK_ULONG attrs_len,
struct icsf_object_record *key)
{
int rc = 0;
int reason = 0;
char handle[ICSF_HANDLE_LEN];
char rule_array[2 * ICSF_RULE_ITEM_LEN];
size_t rule_array_count = 0;
const char *rule_fmt = NULL;
const char *rule_alg = NULL;
BerElement *msg = NULL;
CHECK_ARG_NON_NULL(ld);
CHECK_ARG_NON_NULL(mech);
CHECK_ARG_NON_NULL(unwrapping_key);
CHECK_ARG_NON_NULL(wrapped_key);
CHECK_ARG_NON_NULL(key);
object_record_to_handle(handle, unwrapping_key);
/* Build rule array based on mechanism */
switch (mech->mechanism) {
case CKM_RSA_PKCS:
rule_fmt = "PKCS-1.2";
break;
case CKM_DES_CBC_PAD:
case CKM_DES3_CBC_PAD:
case CKM_AES_CBC_PAD:
rule_fmt = "PKCS-8";
if (!(rule_alg = get_algorithm_rule(mech, 0))) {
TRACE_ERROR("Invalid algorithm: %lu\n",
(unsigned long) mech->mechanism);
return -1;
}
break;
default:
TRACE_ERROR("%s\n", ock_err(ERR_MECHANISM_INVALID));
return -1;
}
strpad(rule_array, rule_fmt, ICSF_RULE_ITEM_LEN, ' ');
rule_array_count += 1;
if (rule_alg) {
strpad(rule_array + (rule_array_count * ICSF_RULE_ITEM_LEN),
rule_alg, ICSF_RULE_ITEM_LEN, ' ');
rule_array_count += 1;
}
/* Build request */
if (!(msg = ber_alloc_t(LBER_USE_DER))) {
TRACE_ERROR("%s\n", ock_err(ERR_HOST_MEMORY));
return -1;
}
/* Encode message:
*
* UWKInput ::= SEQUENCE {
* wrappedKey OCTET STRING,
* initialValue OCTET STRING,
* attrList Attributes
* }
*/
if (ber_printf(msg, "os", wrapped_key, wrapped_key_len, "") < 0 ||
ber_printf(msg, "{") < 0 ||
icsf_ber_put_attribute_list(msg, attrs, attrs_len) ||
ber_printf(msg, "}") < 0) {
rc = -1;
TRACE_ERROR("Failed to encode message: %d.\n", rc);
goto done;
}
/* Call request */
rc = icsf_call(ld, &reason, handle, sizeof(handle), rule_array,
rule_array_count * ICSF_RULE_ITEM_LEN, ICSF_TAG_CSFPUWK,
msg, NULL);
if (p_reason)
*p_reason = reason;
if (ICSF_RC_IS_ERROR(rc))
goto done;
handle_to_object_record(key, handle);
done:
if (msg)
ber_free(msg, 1);
return rc;
}
int icsf_hash_signverify(LDAP * ld, int *reason, struct icsf_object_record *key,
CK_MECHANISM_PTR mech, const char *chain_rule,
const char *clear_text, unsigned long clear_text_len,
char *sig, unsigned long *sig_len, char *chain_data,
size_t * chain_data_len, int verify)
{
int rc = 0;
char handle[ICSF_HANDLE_LEN];
char rule_array[3 * ICSF_RULE_ITEM_LEN];
BerElement *msg = NULL;
BerElement *result = NULL;
struct berval bvSig = { 0, NULL };
struct berval bvChain = { 0, NULL };
int length, reason_code;
const char *rule_alg;
CHECK_ARG_NON_NULL(ld);
CHECK_ARG_NON_NULL(key);
CHECK_ARG_NON_NULL(mech);
object_record_to_handle(handle, key);
/* Add to rule array, the algorithm and chaining mode */
if (!(rule_alg = get_algorithm_rule(mech, verify))) {
TRACE_ERROR("Invalid algorithm: %lu\n",
(unsigned long) mech->mechanism);
return -1;
}
strpad(rule_array + 0 * ICSF_RULE_ITEM_LEN, rule_alg,
2 * ICSF_RULE_ITEM_LEN, ' ');
strpad(rule_array + 2 * ICSF_RULE_ITEM_LEN, chain_rule,
ICSF_RULE_ITEM_LEN, ' ');
/* Build the request */
if (!(msg = ber_alloc_t(LBER_USE_DER))) {
TRACE_ERROR("%s\n", ock_err(ERR_HOST_MEMORY));
return -1;
}
if (ber_printf(msg, "ooo", (clear_text) ? clear_text : "",
clear_text_len, (chain_data) ? chain_data : "",
(chain_data_len) ? *chain_data_len : 0UL,
(sig) ? sig : "", (sig_len) ? *sig_len : 0) < 0) {
rc = -1;
TRACE_ERROR("Failed to encode message: %d.\n", rc);
goto done;
}
/* Call service */
rc = icsf_call(ld, &reason_code, handle, sizeof(handle), rule_array,
sizeof(rule_array), ICSF_TAG_CSFPOWH, msg, &result);
if (reason)
*reason = reason_code;
/* If there was an error related to buffer being too small,
* don't exit until you get the max required length from response.
*/
if (ICSF_RC_IS_ERROR(rc) && (reason_code != 3003))
goto done;
else if ((reason_code == 8000) || (reason_code == 11000))
goto done;
/* Parse the response. */
if (ber_scanf(result, "{ooi}", &bvChain, &bvSig, &length) == LBER_ERROR) {
rc = -1;
TRACE_ERROR("Failed to decode the response.\n");
goto done;
}
/* Only need to return the length for signing */
if (sig_len && !verify)
*sig_len = length;
/* leave if just returning the length. */
if (!verify && *reason == 3003)
goto done;
/* copy the chained data when required */
if (chain_data)
memcpy(chain_data, bvChain.bv_val, bvChain.bv_len);
/* copy signature when signing */
if (!verify)
memcpy(sig, bvSig.bv_val, bvSig.bv_len);
done:
if (result)
ber_free(result, 1);
if (msg)
ber_free(msg, 1);
if (bvSig.bv_val)
ber_memfree(bvSig.bv_val);
if (bvChain.bv_val)
ber_memfree(bvChain.bv_val);
return rc;
}
/*
* Derive a symmetric key.
*/
int icsf_derive_key(LDAP * ld, int *reason, CK_MECHANISM_PTR mech,
struct icsf_object_record *baseKey,
struct icsf_object_record *object,
CK_ATTRIBUTE * attrs, CK_ULONG attrs_len)
{
int rc = -1;
char handle[ICSF_HANDLE_LEN];
char rule_array[1 * ICSF_RULE_ITEM_LEN];
BerElement *msg = NULL;
BerElement *result = NULL;
CK_VERSION_PTR version = NULL;
CK_SSL3_MASTER_KEY_DERIVE_PARAMS *params = NULL;
CK_SSL3_RANDOM_DATA *random_data = NULL;
struct berval clientData = { 0, NULL }, serverData = {
0, NULL};
struct berval publicValue = { 0, NULL }, bvParam = {
0, NULL};
CHECK_ARG_NON_NULL(ld);
CHECK_ARG_NON_NULL(mech);
object_record_to_handle(handle, baseKey);
/* Map mechanism into the rule array */
switch (mech->mechanism) {
case CKM_SSL3_MASTER_KEY_DERIVE:
strpad(rule_array, "SSL-MS", ICSF_RULE_ITEM_LEN, ' ');
break;
case CKM_DH_PKCS_DERIVE:
strpad(rule_array, "PKCS-DH", ICSF_RULE_ITEM_LEN, ' ');
break;
default:
TRACE_ERROR("%s\n", ock_err(ERR_MECHANISM_INVALID));
return -1;
}
if (!(msg = ber_alloc_t(LBER_USE_DER))) {
TRACE_ERROR("%s\n", ock_err(ERR_HOST_MEMORY));
return rc;
}
/* Encode message:
*
* DVKInput ::= SEQUENCE {
* attrList Attributes,
* parmsListChoice DVKInputParmsList
* }
*
* DVKInputParmsList ::= CHOICE {
* PKCS-DH_publicValue [0] OCTET STRING,
* SSL-TLS [1] SSL-TLS_DVKInputParmsList,
* EC-DH [2] EC-DH_DVKInputParmsList
* }
*
* SSL-TLS_DVKInputParmsList ::= SEQUENCE {
* clientRandomData OCTET STRING,
* serverRandomData OCTET STRING
* }
*
* EC-DH is not supported
*
* attrList is built by icsf_ber_put_attribute_list()
*/
if (ber_printf(msg, "{") < 0) {
TRACE_ERROR("Failed to encode message.\n");
goto cleanup;
}
if (icsf_ber_put_attribute_list(msg, attrs, attrs_len) < 0) {
TRACE_DEVEL("Failed to encode message.\n");
goto cleanup;
}
if (ber_printf(msg, "}") < 0) {
TRACE_ERROR("Failed to encode message.\n");
goto cleanup;
}
/* Attribute list depends on type of mechanism */
switch (mech->mechanism) {
case CKM_DH_PKCS_DERIVE:
if ((!mech->pParameter) || ((mech->ulParameterLen < 64) ||
(mech->ulParameterLen > 256))) {
TRACE_ERROR("%s\n", ock_err(ERR_MECHANISM_PARAM_INVALID));
return CKR_MECHANISM_PARAM_INVALID;
}
publicValue.bv_val = mech->pParameter;
publicValue.bv_len = mech->ulParameterLen;
if (ber_printf
(msg, "tO", 0 | LBER_PRIMITIVE | LBER_CLASS_CONTEXT,
&publicValue) < 0) {
TRACE_ERROR("Failed to encode message.\n");
goto cleanup;
}
break;
case CKM_SSL3_MASTER_KEY_DERIVE:
params = (CK_SSL3_MASTER_KEY_DERIVE_PARAMS *) mech->pParameter;
random_data = (CK_SSL3_RANDOM_DATA *) (¶ms->RandomInfo);
clientData.bv_len = random_data->ulClientRandomLen;
clientData.bv_val = (char *)random_data->pClientRandom;
serverData.bv_len = random_data->ulServerRandomLen;
serverData.bv_val = (char *)random_data->pServerRandom;
if (ber_printf(msg, "t{OO}", 1 | LBER_CLASS_CONTEXT | LBER_CONSTRUCTED,
&clientData, &serverData) < 0) {
TRACE_ERROR("Failed to encode message.\n");
goto cleanup;
}
break;
default:
TRACE_ERROR("Mechanism not supported.\n");
return -1;
}
rc = icsf_call(ld, reason, handle, sizeof(handle), rule_array,
sizeof(rule_array), ICSF_TAG_CSFPDVK, msg, &result);
if (!rc) {
handle_to_object_record(object, handle);
/* Decode the result:
*
* DVKOutput ::= SEQUENCE {
* parmsListChoice DVKOutputParmsList
* }
*
* DVKOutputParmsList ::= CHOICE {
* PKCS-DH_Output [0] NULL,
* SSL-TLS_Output [1] OCTET STRING,
* EC-DH_Output [2] NULL (not supported)
* }
*/
if (mech->mechanism == CKM_SSL3_MASTER_KEY_DERIVE) {
if (ber_scanf(result, "o", &bvParam) == LBER_ERROR) {
TRACE_ERROR("Failed to Derive Key\n");
rc = -1;
goto cleanup;
}
params = (CK_SSL3_MASTER_KEY_DERIVE_PARAMS *) mech->pParameter;
version = (CK_VERSION_PTR) (¶ms->pVersion);
version->major = bvParam.bv_val[0];
version->minor = bvParam.bv_val[1];
}
}
rc = 0;
cleanup:
if (msg)
ber_free(msg, 1);
if (result)
ber_free(result, 1);
return rc;
}
/*
* Devive multiple keys at once.
*/
int icsf_derive_multiple_keys(LDAP * ld, int *p_reason, CK_MECHANISM_PTR mech,
struct icsf_object_record *key,
CK_ATTRIBUTE_PTR attrs, CK_ULONG attrs_len,
struct icsf_object_record *client_mac_handle,
struct icsf_object_record *server_mac_handle,
struct icsf_object_record *client_key_handle,
struct icsf_object_record *server_key_handle,
unsigned char *client_iv,
unsigned char *server_iv)
{
int rc = 0;
const char *rule_alg;
char handle[ICSF_HANDLE_LEN];
char rule_array[ICSF_RULE_ITEM_LEN];
BerElement *msg = NULL;
BerElement *result = NULL;
ber_tag_t tag;
CK_SSL3_KEY_MAT_PARAMS *params;
struct berval bv_client_random_data;
struct berval bv_server_random_data;
struct berval bv_client_mac_handle = { 0, NULL };
struct berval bv_server_mac_handle = { 0, NULL };
struct berval bv_client_key_handle = { 0, NULL };
struct berval bv_server_key_handle = { 0, NULL };
struct berval bv_client_iv = { 0, NULL };
struct berval bv_server_iv = { 0, NULL };
UNUSED(client_iv);
UNUSED(server_iv);
CHECK_ARG_NON_NULL(ld);
CHECK_ARG_NON_NULL(mech);
CHECK_ARG_NON_NULL(key);
object_record_to_handle(handle, key);
/* Build rule array based on mechanism */
if (!(rule_alg = get_algorithm_rule(mech, 0))) {
TRACE_ERROR("Invalid algorithm: %lu\n",
(unsigned long) mech->mechanism);
return -1;
}
strpad(rule_array, rule_alg, ICSF_RULE_ITEM_LEN, ' ');
/* Build request */
if (!(msg = ber_alloc_t(LBER_USE_DER))) {
TRACE_ERROR("%s\n", ock_err(ERR_HOST_MEMORY));
return -1;
}
/*
* DMKInput sequence.
*
* DMKInput ::= SEQUENCE {
* attrList Attributes,
* parmsListChoice DMKInputParmsList
* }
*
* DMKInputParmsList ::= CHOICE {
* SSL-KM_TLS-KM [0] SSL_TLS_DMKInputParmsList
* }
*
* SSL_TLS_DMKInputParmsList ::= SEQUENCE {
* export BOOLEAN,
* macSize INTEGER,
* keySize INTEGER,
* ivSize INTEGER,
* clientRandomData OCTET STRING,
* serverRandomData OCTET STRING
* }
*/
params = (CK_SSL3_KEY_MAT_PARAMS *) mech->pParameter;
if (!params) {
TRACE_ERROR("%s\n", ock_err(ERR_MECHANISM_PARAM_INVALID));
rc = CKR_MECHANISM_PARAM_INVALID;
goto done;
}
rc = ber_printf(msg, "{");
if (rc < 0) {
TRACE_ERROR("%s\n", ock_err(ERR_FUNCTION_FAILED));
goto done;
}
if (icsf_ber_put_attribute_list(msg, attrs, attrs_len) < 0) {
TRACE_ERROR("icsf_ber_put_attribute_list failed\n");
goto done;
}
if (ber_printf(msg, "}") < 0) {
TRACE_ERROR("%s\n", ock_err(ERR_FUNCTION_FAILED));
goto done;
}
tag = 0 | LBER_CLASS_CONTEXT | LBER_CONSTRUCTED;
bv_client_random_data.bv_len = params->RandomInfo.ulClientRandomLen;
bv_client_random_data.bv_val = (char *)params->RandomInfo.pClientRandom;
bv_server_random_data.bv_len = params->RandomInfo.ulServerRandomLen;
bv_server_random_data.bv_val = (char *)params->RandomInfo.pServerRandom;
rc = ber_printf(msg, "t{biiiOO}", tag,
(ber_int_t) params->bIsExport,
(ber_int_t) params->ulMacSizeInBits,
(ber_int_t) params->ulKeySizeInBits,
(ber_int_t) params->ulIVSizeInBits,
&bv_client_random_data, &bv_server_random_data);
if (rc < 0) {
TRACE_ERROR("%s\n", ock_err(ERR_FUNCTION_FAILED));
goto done;
}
/* Call request */
rc = icsf_call(ld, p_reason, handle, sizeof(handle), rule_array,
sizeof(rule_array), ICSF_TAG_CSFPDMK, msg, &result);
if (ICSF_RC_IS_ERROR(rc))
goto done;
/*
* DMKOutput ::= SEQUENCE {
* parmsListChoice DMKOutputParmsList
* }
*
* DMKOutputParmsList ::= CHOICE {
* SSL-KM_TLS-KM [0] SSL_TLS_DMKOutputParmsList
* }
*
* SSL_TLS_DMKOutputParmsList ::= SEQUENCE {
* clientMACHandle OCTET STRING,
* clientMACHandle OCTET STRING,
* clientKeyHandle OCTET STRING,
* serverKeyHandle OCTET STRING,
* clientIV OCTET STRING,
* serverIV OCTET STRING
* }
*/
/*
* Since we are copying the values after all, "m" has the advantage of
* not needing to free the returned values...
*/
if (ber_scanf(result, "{t{mmmmmm}}", &tag, &bv_client_mac_handle,
&bv_server_mac_handle, &bv_client_key_handle,
&bv_server_key_handle, &bv_client_iv, &bv_server_iv)
== LBER_ERROR) {
rc = -1;
TRACE_ERROR("Failed to decode the response.\n");
goto done;
}
/* Copy key handles */
if (bv_client_mac_handle.bv_len != ICSF_HANDLE_LEN ||
bv_server_mac_handle.bv_len != ICSF_HANDLE_LEN ||
bv_client_key_handle.bv_len != ICSF_HANDLE_LEN ||
bv_server_key_handle.bv_len != ICSF_HANDLE_LEN) {
TRACE_ERROR("Invalid key handle size: %lu/%lu/%lu/%lu\n",
(unsigned long) bv_client_mac_handle.bv_len,
(unsigned long) bv_server_mac_handle.bv_len,
(unsigned long) bv_client_key_handle.bv_len,
(unsigned long) bv_server_key_handle.bv_len);
rc = CKR_FUNCTION_FAILED;
goto done;
}
handle_to_object_record(client_mac_handle, bv_client_mac_handle.bv_val);
handle_to_object_record(server_mac_handle, bv_server_mac_handle.bv_val);
handle_to_object_record(client_key_handle, bv_client_key_handle.bv_val);
handle_to_object_record(server_key_handle, bv_server_key_handle.bv_val);
/* Copy IVs */
if (params->ulIVSizeInBits) {
if (8 * bv_client_iv.bv_len != params->ulIVSizeInBits) {
TRACE_ERROR("Invalid client IV size: %lu\n",
(unsigned long) bv_client_iv.bv_len);
rc = CKR_FUNCTION_FAILED;
goto done;
}
memcpy(params->pReturnedKeyMaterial->pIVClient,
bv_client_iv.bv_val, bv_client_iv.bv_len);
if (8 * bv_server_iv.bv_len != params->ulIVSizeInBits) {
TRACE_ERROR("Invalid server IV size: %lu\n",
(unsigned long) bv_server_iv.bv_len);
rc = CKR_FUNCTION_FAILED;
goto done;
}
memcpy(params->pReturnedKeyMaterial->pIVServer,
bv_server_iv.bv_val, bv_server_iv.bv_len);
}
done:
if (result)
ber_free(result, 1);
if (msg)
ber_free(msg, 1);
return rc;
}
/** get size of an icsf object */
int icsf_get_object_size(LDAP * ld, int *reason,
struct icsf_object_record *object, CK_ULONG attrs_len,
CK_ULONG * obj_size)
{
char handle[ICSF_HANDLE_LEN];
BerElement *msg = NULL;
BerElement *result = NULL;
int rc = 0;
int size = 0;
CHECK_ARG_NON_NULL(ld);
CHECK_ARG_NON_NULL(object);
object_record_to_handle(handle, object);
if (!(msg = ber_alloc_t(LBER_USE_DER))) {
TRACE_ERROR("%s\n", ock_err(ERR_HOST_MEMORY));
return CKR_HOST_MEMORY;
}
/* Encode message:
*
* GAVInput ::= attrListLen
*
* attrListLen ::= INTEGER (0 .. MaxCSFPInteger)
*
*/
rc = ber_printf(msg, "i", attrs_len);
if (rc < 0)
goto cleanup;
rc = icsf_call(ld, reason, handle, sizeof(handle), "", 0,
ICSF_TAG_CSFPGAV, msg, &result);
if (rc != 0) {
TRACE_DEVEL("icsf_call failed. rc=%d, reason=%d", rc, *reason);
goto cleanup;
}
/* Decode the result:
*
* GAVOutput ::= SEQUENCE {
* attrList Attributes,
* attrListLen INTEGER (0 .. MaxCSFPInteger)
* }
*
* asn.1 {{{ito|i} {ito|i} ...}i}
*/
if (ber_scanf(result, "{") == LBER_ERROR) {
TRACE_ERROR("Failed to decode message - icsf_get_object_size");
goto cleanup;
}
//interested only in the list length which will be the size of the object in
//bytes
if (ber_scanf(result, "xi}", &size) == LBER_ERROR) {
TRACE_ERROR("Failed to decode message - icsf_get_object_size");
goto cleanup;
}
TRACE_INFO("icsf_get_object_size - size = %d\n", size);
*obj_size = size;
cleanup:
if (msg)
ber_free(msg, 1);
if (result)
ber_free(result, 1);
return rc;
}