/*
* Copyright (C) 2008 Stefan Walter
* Copyright (C) 2012 Red Hat Inc.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* * Redistributions of source code must retain the above
* copyright notice, this list of conditions and the
* following disclaimer.
* * Redistributions in binary form must reproduce the
* above copyright notice, this list of conditions and
* the following disclaimer in the documentation and/or
* other materials provided with the distribution.
* * The names of contributors to this software may not be
* used to endorse or promote products derived from this
* software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
* COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
* OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
* THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
* DAMAGE.
*
* Author: Stef Walter <stefw@gnome.org>
*/
#include "config.h"
#define P11_DEBUG_FLAG P11_DEBUG_RPC
#include "debug.h"
#include "library.h"
#include "message.h"
#include "private.h"
#include "rpc-message.h"
#include <assert.h>
#include <errno.h>
#include <string.h>
#define ELEMS(x) (sizeof (x) / sizeof (x[0]))
void
p11_rpc_message_init (p11_rpc_message *msg,
p11_buffer *input,
p11_buffer *output)
{
assert (input != NULL);
assert (output != NULL);
assert (output->ffree != NULL);
assert (output->frealloc != NULL);
memset (msg, 0, sizeof (*msg));
msg->output = output;
msg->input = input;
}
void
p11_rpc_message_clear (p11_rpc_message *msg)
{
void *allocated;
void **data;
assert (msg != NULL);
/* Free up the extra allocated memory */
allocated = msg->extra;
while (allocated != NULL) {
data = (void **)allocated;
/* Pointer to the next allocation */
allocated = *data;
assert (msg->output->ffree);
(msg->output->ffree) (data);
}
msg->output = NULL;
msg->input = NULL;
msg->extra = NULL;
}
void *
p11_rpc_message_alloc_extra (p11_rpc_message *msg,
size_t length)
{
void **data;
assert (msg != NULL);
if (length > 0x7fffffff)
return NULL;
assert (msg->output->frealloc != NULL);
data = (msg->output->frealloc) (NULL, sizeof (void *) + length);
if (data == NULL)
return NULL;
/* Munch up the memory to help catch bugs */
memset (data, 0xff, sizeof (void *) + length);
/* Store pointer to next allocated block at beginning */
*data = msg->extra;
msg->extra = data;
/* Data starts after first pointer */
return (void *)(data + 1);
}
void *
p11_rpc_message_alloc_extra_array (p11_rpc_message *msg,
size_t nmemb,
size_t size)
{
if (nmemb != 0 && (SIZE_MAX - sizeof (void *)) / nmemb < size) {
errno = ENOMEM;
return NULL;
}
return p11_rpc_message_alloc_extra (msg, nmemb * size);
}
bool
p11_rpc_message_prep (p11_rpc_message *msg,
int call_id,
p11_rpc_message_type type)
{
int len;
assert (type != 0);
assert (call_id >= P11_RPC_CALL_ERROR);
assert (call_id < P11_RPC_CALL_MAX);
p11_buffer_reset (msg->output, 0);
msg->signature = NULL;
/* The call id and signature */
if (type == P11_RPC_REQUEST)
msg->signature = p11_rpc_calls[call_id].request;
else if (type == P11_RPC_RESPONSE)
msg->signature = p11_rpc_calls[call_id].response;
else
assert_not_reached ();
assert (msg->signature != NULL);
msg->sigverify = msg->signature;
msg->call_id = call_id;
msg->call_type = type;
/* Encode the two of them */
p11_rpc_buffer_add_uint32 (msg->output, call_id);
if (msg->signature) {
len = strlen (msg->signature);
p11_rpc_buffer_add_byte_array (msg->output, (unsigned char*)msg->signature, len);
}
msg->parsed = 0;
return !p11_buffer_failed (msg->output);
}
bool
p11_rpc_message_parse (p11_rpc_message *msg,
p11_rpc_message_type type)
{
const unsigned char *val;
size_t len;
uint32_t call_id;
assert (msg != NULL);
assert (msg->input != NULL);
msg->parsed = 0;
/* Pull out the call identifier */
if (!p11_rpc_buffer_get_uint32 (msg->input, &msg->parsed, &call_id)) {
p11_message ("invalid message: couldn't read call identifier");
return false;
}
msg->signature = msg->sigverify = NULL;
/* The call id and signature */
if (call_id >= P11_RPC_CALL_MAX ||
(type == P11_RPC_REQUEST && call_id == P11_RPC_CALL_ERROR)) {
p11_message ("invalid message: bad call id: %d", call_id);
return false;
}
if (type == P11_RPC_REQUEST)
msg->signature = p11_rpc_calls[call_id].request;
else if (type == P11_RPC_RESPONSE)
msg->signature = p11_rpc_calls[call_id].response;
else
assert_not_reached ();
assert (msg->signature != NULL);
msg->call_id = call_id;
msg->call_type = type;
msg->sigverify = msg->signature;
/* Verify the incoming signature */
if (!p11_rpc_buffer_get_byte_array (msg->input, &msg->parsed, &val, &len) ||
/* This can happen if the length header == 0xffffffff */
val == NULL) {
p11_message ("invalid message: couldn't read signature");
return false;
}
if ((strlen (msg->signature) != len) || (memcmp (val, msg->signature, len) != 0)) {
p11_message ("invalid message: signature doesn't match");
return false;
}
return true;
}
bool
p11_rpc_message_verify_part (p11_rpc_message *msg,
const char* part)
{
int len;
bool ok;
if (!msg->sigverify)
return true;
len = strlen (part);
ok = (strncmp (msg->sigverify, part, len) == 0);
if (ok)
msg->sigverify += len;
return ok;
}
bool
p11_rpc_message_write_attribute_buffer (p11_rpc_message *msg,
CK_ATTRIBUTE_PTR arr,
CK_ULONG num)
{
CK_ATTRIBUTE_PTR attr;
CK_ULONG i;
assert (num == 0 || arr != NULL);
assert (msg != NULL);
assert (msg->output != NULL);
/* Make sure this is in the right order */
assert (!msg->signature || p11_rpc_message_verify_part (msg, "fA"));
/* Write the number of items */
p11_rpc_buffer_add_uint32 (msg->output, num);
for (i = 0; i < num; ++i) {
attr = &(arr[i]);
/* The attribute type */
p11_rpc_buffer_add_uint32 (msg->output, attr->type);
/* And the attribute buffer length */
p11_rpc_buffer_add_uint32 (msg->output, attr->pValue ? attr->ulValueLen : 0);
}
return !p11_buffer_failed (msg->output);
}
bool
p11_rpc_message_write_attribute_array (p11_rpc_message *msg,
CK_ATTRIBUTE_PTR arr,
CK_ULONG num)
{
CK_ULONG i;
assert (num == 0 || arr != NULL);
assert (msg != NULL);
assert (msg->output != NULL);
/* Make sure this is in the right order */
assert (!msg->signature || p11_rpc_message_verify_part (msg, "aA"));
/* Write the number of items */
p11_rpc_buffer_add_uint32 (msg->output, num);
for (i = 0; i < num; ++i)
p11_rpc_buffer_add_attribute (msg->output, &(arr[i]));
return !p11_buffer_failed (msg->output);
}
bool
p11_rpc_message_read_byte (p11_rpc_message *msg,
CK_BYTE *val)
{
assert (msg != NULL);
assert (msg->input != NULL);
/* Make sure this is in the right order */
assert (!msg->signature || p11_rpc_message_verify_part (msg, "y"));
return p11_rpc_buffer_get_byte (msg->input, &msg->parsed, val);
}
bool
p11_rpc_message_write_byte (p11_rpc_message *msg,
CK_BYTE val)
{
assert (msg != NULL);
assert (msg->output != NULL);
/* Make sure this is in the right order */
assert (!msg->signature || p11_rpc_message_verify_part (msg, "y"));
p11_rpc_buffer_add_byte (msg->output, val);
return !p11_buffer_failed (msg->output);
}
bool
p11_rpc_message_read_ulong (p11_rpc_message *msg,
CK_ULONG *val)
{
uint64_t v;
assert (msg != NULL);
assert (msg->input != NULL);
/* Make sure this is in the right order */
assert (!msg->signature || p11_rpc_message_verify_part (msg, "u"));
if (!p11_rpc_buffer_get_uint64 (msg->input, &msg->parsed, &v))
return false;
if (val)
*val = (CK_ULONG)v;
return true;
}
bool
p11_rpc_message_write_ulong (p11_rpc_message *msg,
CK_ULONG val)
{
assert (msg != NULL);
assert (msg->output != NULL);
/* Make sure this is in the right order */
assert (!msg->signature || p11_rpc_message_verify_part (msg, "u"));
p11_rpc_buffer_add_uint64 (msg->output, val);
return !p11_buffer_failed (msg->output);
}
bool
p11_rpc_message_write_byte_buffer (p11_rpc_message *msg,
CK_ULONG count)
{
assert (msg != NULL);
assert (msg->output != NULL);
/* Make sure this is in the right order */
assert (!msg->signature || p11_rpc_message_verify_part (msg, "fy"));
p11_rpc_buffer_add_uint32 (msg->output, count);
return !p11_buffer_failed (msg->output);
}
bool
p11_rpc_message_write_byte_array (p11_rpc_message *msg,
CK_BYTE_PTR arr,
CK_ULONG num)
{
assert (msg != NULL);
assert (msg->output != NULL);
/* Make sure this is in the right order */
assert (!msg->signature || p11_rpc_message_verify_part (msg, "ay"));
/* No array, no data, just length */
if (!arr) {
p11_rpc_buffer_add_byte (msg->output, 0);
p11_rpc_buffer_add_uint32 (msg->output, num);
} else {
p11_rpc_buffer_add_byte (msg->output, 1);
p11_rpc_buffer_add_byte_array (msg->output, arr, num);
}
return !p11_buffer_failed (msg->output);
}
bool
p11_rpc_message_write_ulong_buffer (p11_rpc_message *msg,
CK_ULONG count)
{
assert (msg != NULL);
assert (msg->output != NULL);
/* Make sure this is in the right order */
assert (!msg->signature || p11_rpc_message_verify_part (msg, "fu"));
p11_rpc_buffer_add_uint32 (msg->output, count);
return !p11_buffer_failed (msg->output);
}
bool
p11_rpc_message_write_ulong_array (p11_rpc_message *msg,
CK_ULONG_PTR array,
CK_ULONG n_array)
{
CK_ULONG i;
assert (msg != NULL);
assert (msg->output != NULL);
/* Check that we're supposed to have this at this point */
assert (!msg->signature || p11_rpc_message_verify_part (msg, "au"));
/* We send a byte which determines whether there's actual data present or not */
p11_rpc_buffer_add_byte (msg->output, array ? 1 : 0);
p11_rpc_buffer_add_uint32 (msg->output, n_array);
/* Now send the data if valid */
if (array) {
for (i = 0; i < n_array; ++i)
p11_rpc_buffer_add_uint64 (msg->output, array[i]);
}
return !p11_buffer_failed (msg->output);
}
bool
p11_rpc_message_read_version (p11_rpc_message *msg,
CK_VERSION *version)
{
assert (msg != NULL);
assert (msg->input != NULL);
assert (version != NULL);
/* Check that we're supposed to have this at this point */
assert (!msg->signature || p11_rpc_message_verify_part (msg, "v"));
return p11_rpc_buffer_get_byte (msg->input, &msg->parsed, &version->major) &&
p11_rpc_buffer_get_byte (msg->input, &msg->parsed, &version->minor);
}
bool
p11_rpc_message_write_version (p11_rpc_message *msg,
CK_VERSION *version)
{
assert (msg != NULL);
assert (msg->output != NULL);
assert (version != NULL);
/* Check that we're supposed to have this at this point */
assert (!msg->signature || p11_rpc_message_verify_part (msg, "v"));
p11_rpc_buffer_add_byte (msg->output, version->major);
p11_rpc_buffer_add_byte (msg->output, version->minor);
return !p11_buffer_failed (msg->output);
}
bool
p11_rpc_message_read_space_string (p11_rpc_message *msg,
CK_UTF8CHAR *buffer,
CK_ULONG length)
{
const unsigned char *data;
size_t n_data;
assert (msg != NULL);
assert (msg->input != NULL);
assert (buffer != NULL);
assert (length != 0);
assert (!msg->signature || p11_rpc_message_verify_part (msg, "s"));
if (!p11_rpc_buffer_get_byte_array (msg->input, &msg->parsed, &data, &n_data))
return false;
if (n_data != length) {
p11_message ("invalid length space padded string received: %d != %d",
(int)length, (int)n_data);
return false;
}
memcpy (buffer, data, length);
return true;
}
bool
p11_rpc_message_write_space_string (p11_rpc_message *msg,
CK_UTF8CHAR *data,
CK_ULONG length)
{
assert (msg != NULL);
assert (msg->output != NULL);
assert (data != NULL);
assert (length != 0);
assert (!msg->signature || p11_rpc_message_verify_part (msg, "s"));
p11_rpc_buffer_add_byte_array (msg->output, data, length);
return !p11_buffer_failed (msg->output);
}
bool
p11_rpc_message_write_zero_string (p11_rpc_message *msg,
CK_UTF8CHAR *string)
{
assert (msg != NULL);
assert (msg->output != NULL);
assert (string != NULL);
assert (!msg->signature || p11_rpc_message_verify_part (msg, "z"));
p11_rpc_buffer_add_byte_array (msg->output, string,
string ? strlen ((char *)string) : 0);
return !p11_buffer_failed (msg->output);
}
static void *
log_allocator (void *pointer,
size_t size)
{
void *result = realloc (pointer, (size_t)size);
return_val_if_fail (!size || result != NULL, NULL);
return result;
}
p11_buffer *
p11_rpc_buffer_new (size_t reserve)
{
return p11_rpc_buffer_new_full (reserve, log_allocator, free);
}
p11_buffer *
p11_rpc_buffer_new_full (size_t reserve,
void * (* frealloc) (void *data, size_t size),
void (* ffree) (void *data))
{
p11_buffer *buffer;
buffer = calloc (1, sizeof (p11_buffer));
return_val_if_fail (buffer != NULL, NULL);
p11_buffer_init_full (buffer, NULL, 0, 0, frealloc, ffree);
if (!p11_buffer_reset (buffer, reserve))
return_val_if_reached (NULL);
return buffer;
}
void
p11_rpc_buffer_free (p11_buffer *buf)
{
if (buf == NULL)
return;
p11_buffer_uninit (buf);
free (buf);
}
void
p11_rpc_buffer_add_byte (p11_buffer *buf,
unsigned char value)
{
p11_buffer_add (buf, &value, 1);
}
int
p11_rpc_buffer_get_byte (p11_buffer *buf,
size_t *offset,
unsigned char *val)
{
unsigned char *ptr;
if (buf->len < 1 || *offset > buf->len - 1) {
p11_buffer_fail (buf);
return 0;
}
ptr = (unsigned char *)buf->data + *offset;
if (val != NULL)
*val = *ptr;
*offset = *offset + 1;
return 1;
}
void
p11_rpc_buffer_encode_uint16 (unsigned char* data,
uint16_t value)
{
data[0] = (value >> 8) & 0xff;
data[1] = (value >> 0) & 0xff;
}
uint16_t
p11_rpc_buffer_decode_uint16 (unsigned char* data)
{
uint16_t value = data[0] << 8 | data[1];
return value;
}
void
p11_rpc_buffer_add_uint16 (p11_buffer *buffer,
uint16_t value)
{
size_t offset = buffer->len;
if (!p11_buffer_append (buffer, 2))
return_if_reached ();
p11_rpc_buffer_set_uint16 (buffer, offset, value);
}
bool
p11_rpc_buffer_set_uint16 (p11_buffer *buffer,
size_t offset,
uint16_t value)
{
unsigned char *ptr;
if (buffer->len < 2 || offset > buffer->len - 2) {
p11_buffer_fail (buffer);
return false;
}
ptr = (unsigned char *)buffer->data + offset;
p11_rpc_buffer_encode_uint16 (ptr, value);
return true;
}
bool
p11_rpc_buffer_get_uint16 (p11_buffer *buf,
size_t *offset,
uint16_t *value)
{
unsigned char *ptr;
if (buf->len < 2 || *offset > buf->len - 2) {
p11_buffer_fail (buf);
return false;
}
ptr = (unsigned char*)buf->data + *offset;
if (value != NULL)
*value = p11_rpc_buffer_decode_uint16 (ptr);
*offset = *offset + 2;
return true;
}
void
p11_rpc_buffer_encode_uint32 (unsigned char* data,
uint32_t value)
{
data[0] = (value >> 24) & 0xff;
data[1] = (value >> 16) & 0xff;
data[2] = (value >> 8) & 0xff;
data[3] = (value >> 0) & 0xff;
}
uint32_t
p11_rpc_buffer_decode_uint32 (unsigned char* ptr)
{
uint32_t val = (uint32_t) ptr[0] << 24 | ptr[1] << 16 | ptr[2] << 8 | ptr[3];
return val;
}
void
p11_rpc_buffer_add_uint32 (p11_buffer *buffer,
uint32_t value)
{
size_t offset = buffer->len;
if (!p11_buffer_append (buffer, 4))
return_val_if_reached ();
p11_rpc_buffer_set_uint32 (buffer, offset, value);
}
bool
p11_rpc_buffer_set_uint32 (p11_buffer *buffer,
size_t offset,
uint32_t value)
{
unsigned char *ptr;
if (buffer->len < 4 || offset > buffer->len - 4) {
p11_buffer_fail (buffer);
return false;
}
ptr = (unsigned char*)buffer->data + offset;
p11_rpc_buffer_encode_uint32 (ptr, value);
return true;
}
bool
p11_rpc_buffer_get_uint32 (p11_buffer *buf,
size_t *offset,
uint32_t *value)
{
unsigned char *ptr;
if (buf->len < 4 || *offset > buf->len - 4) {
p11_buffer_fail (buf);
return false;
}
ptr = (unsigned char*)buf->data + *offset;
if (value != NULL)
*value = p11_rpc_buffer_decode_uint32 (ptr);
*offset = *offset + 4;
return true;
}
void
p11_rpc_buffer_add_uint64 (p11_buffer *buffer,
uint64_t value)
{
p11_rpc_buffer_add_uint32 (buffer, ((value >> 32) & 0xffffffff));
p11_rpc_buffer_add_uint32 (buffer, (value & 0xffffffff));
}
bool
p11_rpc_buffer_get_uint64 (p11_buffer *buf,
size_t *offset,
uint64_t *value)
{
size_t off = *offset;
uint32_t a, b;
if (!p11_rpc_buffer_get_uint32 (buf, &off, &a) ||
!p11_rpc_buffer_get_uint32 (buf, &off, &b))
return false;
if (value != NULL)
*value = ((uint64_t)a) << 32 | b;
*offset = off;
return true;
}
void
p11_rpc_buffer_add_byte_array (p11_buffer *buffer,
const unsigned char *data,
size_t length)
{
if (data == NULL) {
p11_rpc_buffer_add_uint32 (buffer, 0xffffffff);
return;
} else if (length >= 0x7fffffff) {
p11_buffer_fail (buffer);
return;
}
p11_rpc_buffer_add_uint32 (buffer, length);
p11_buffer_add (buffer, data, length);
}
bool
p11_rpc_buffer_get_byte_array (p11_buffer *buf,
size_t *offset,
const unsigned char **data,
size_t *length)
{
size_t off = *offset;
uint32_t len;
if (!p11_rpc_buffer_get_uint32 (buf, &off, &len))
return false;
if (len == 0xffffffff) {
*offset = off;
if (data)
*data = NULL;
if (length)
*length = 0;
return true;
} else if (len >= 0x7fffffff) {
p11_buffer_fail (buf);
return false;
}
if (buf->len < len || off > buf->len - len) {
p11_buffer_fail (buf);
return false;
}
if (data)
*data = (unsigned char *)buf->data + off;
if (length)
*length = len;
*offset = off + len;
return true;
}
static p11_rpc_value_type
map_attribute_to_value_type (CK_ATTRIBUTE_TYPE type)
{
switch (type) {
case CKA_TOKEN:
case CKA_PRIVATE:
case CKA_TRUSTED:
case CKA_SENSITIVE:
case CKA_ENCRYPT:
case CKA_DECRYPT:
case CKA_WRAP:
case CKA_UNWRAP:
case CKA_SIGN:
case CKA_SIGN_RECOVER:
case CKA_VERIFY:
case CKA_VERIFY_RECOVER:
case CKA_DERIVE:
case CKA_EXTRACTABLE:
case CKA_LOCAL:
case CKA_NEVER_EXTRACTABLE:
case CKA_ALWAYS_SENSITIVE:
case CKA_MODIFIABLE:
case CKA_COPYABLE:
case CKA_SECONDARY_AUTH: /* Deprecated */
case CKA_ALWAYS_AUTHENTICATE:
case CKA_WRAP_WITH_TRUSTED:
case CKA_RESET_ON_INIT:
case CKA_HAS_RESET:
case CKA_COLOR:
return P11_RPC_VALUE_BYTE;
case CKA_CLASS:
case CKA_CERTIFICATE_TYPE:
case CKA_CERTIFICATE_CATEGORY:
case CKA_JAVA_MIDP_SECURITY_DOMAIN:
case CKA_KEY_TYPE:
case CKA_MODULUS_BITS:
case CKA_PRIME_BITS:
case CKA_SUB_PRIME_BITS:
case CKA_VALUE_BITS:
case CKA_VALUE_LEN:
case CKA_KEY_GEN_MECHANISM:
case CKA_AUTH_PIN_FLAGS: /* Deprecated */
case CKA_HW_FEATURE_TYPE:
case CKA_PIXEL_X:
case CKA_PIXEL_Y:
case CKA_RESOLUTION:
case CKA_CHAR_ROWS:
case CKA_CHAR_COLUMNS:
case CKA_BITS_PER_PIXEL:
case CKA_MECHANISM_TYPE:
return P11_RPC_VALUE_ULONG;
case CKA_WRAP_TEMPLATE:
case CKA_UNWRAP_TEMPLATE:
return P11_RPC_VALUE_ATTRIBUTE_ARRAY;
case CKA_ALLOWED_MECHANISMS:
return P11_RPC_VALUE_MECHANISM_TYPE_ARRAY;
case CKA_START_DATE:
case CKA_END_DATE:
return P11_RPC_VALUE_DATE;
default:
p11_debug ("cannot determine the type of attribute value for %lu; assuming byte array",
type);
/* fallthrough */
case CKA_LABEL:
case CKA_APPLICATION:
case CKA_VALUE:
case CKA_OBJECT_ID:
case CKA_ISSUER:
case CKA_SERIAL_NUMBER:
case CKA_AC_ISSUER:
case CKA_OWNER:
case CKA_ATTR_TYPES:
case CKA_URL:
case CKA_HASH_OF_SUBJECT_PUBLIC_KEY:
case CKA_HASH_OF_ISSUER_PUBLIC_KEY:
case CKA_CHECK_VALUE:
case CKA_SUBJECT:
case CKA_ID:
case CKA_MODULUS:
case CKA_PUBLIC_EXPONENT:
case CKA_PRIVATE_EXPONENT:
case CKA_PRIME_1:
case CKA_PRIME_2:
case CKA_EXPONENT_1:
case CKA_EXPONENT_2:
case CKA_COEFFICIENT:
case CKA_PRIME:
case CKA_SUBPRIME:
case CKA_BASE:
case CKA_EC_PARAMS:
/* same as CKA_ECDSA_PARAMS */
case CKA_EC_POINT:
case CKA_CHAR_SETS:
case CKA_ENCODING_METHODS:
case CKA_MIME_TYPES:
case CKA_REQUIRED_CMS_ATTRIBUTES:
case CKA_DEFAULT_CMS_ATTRIBUTES:
case CKA_SUPPORTED_CMS_ATTRIBUTES:
return P11_RPC_VALUE_BYTE_ARRAY;
}
}
typedef struct {
p11_rpc_value_type type;
p11_rpc_value_encoder encode;
p11_rpc_value_decoder decode;
} p11_rpc_attribute_serializer;
static p11_rpc_attribute_serializer p11_rpc_attribute_serializers[] = {
{ P11_RPC_VALUE_BYTE, p11_rpc_buffer_add_byte_value, p11_rpc_buffer_get_byte_value },
{ P11_RPC_VALUE_ULONG, p11_rpc_buffer_add_ulong_value, p11_rpc_buffer_get_ulong_value },
{ P11_RPC_VALUE_ATTRIBUTE_ARRAY, p11_rpc_buffer_add_attribute_array_value, p11_rpc_buffer_get_attribute_array_value },
{ P11_RPC_VALUE_MECHANISM_TYPE_ARRAY, p11_rpc_buffer_add_mechanism_type_array_value, p11_rpc_buffer_get_mechanism_type_array_value },
{ P11_RPC_VALUE_DATE, p11_rpc_buffer_add_date_value, p11_rpc_buffer_get_date_value },
{ P11_RPC_VALUE_BYTE_ARRAY, p11_rpc_buffer_add_byte_array_value, p11_rpc_buffer_get_byte_array_value }
};
P11_STATIC_ASSERT(sizeof(CK_BYTE) <= sizeof(uint8_t));
void
p11_rpc_buffer_add_byte_value (p11_buffer *buffer,
const void *value,
CK_ULONG value_length)
{
CK_BYTE byte_value = 0;
/* Check if value can be converted to CK_BYTE. */
if (value_length > sizeof (CK_BYTE)) {
p11_buffer_fail (buffer);
return;
}
if (value)
memcpy (&byte_value, value, value_length);
p11_rpc_buffer_add_byte (buffer, byte_value);
}
void
p11_rpc_buffer_add_ulong_value (p11_buffer *buffer,
const void *value,
CK_ULONG value_length)
{
CK_ULONG ulong_value = 0;
/* Check if value can be converted to CK_ULONG. */
if (value_length > sizeof (CK_ULONG)) {
p11_buffer_fail (buffer);
return;
}
if (value)
memcpy (&ulong_value, value, value_length);
/* Check if ulong_value can be converted to uint64_t. */
if (ulong_value > UINT64_MAX) {
p11_buffer_fail (buffer);
return;
}
p11_rpc_buffer_add_uint64 (buffer, ulong_value);
}
void
p11_rpc_buffer_add_attribute_array_value (p11_buffer *buffer,
const void *value,
CK_ULONG value_length)
{
const CK_ATTRIBUTE *attrs = value;
size_t count = value_length / sizeof (CK_ATTRIBUTE);
size_t i;
/* Check if count can be converted to uint32_t. */
if (count > UINT32_MAX) {
p11_buffer_fail (buffer);
return;
}
/* Write the number of items */
p11_rpc_buffer_add_uint32 (buffer, count);
/* Actually write the attributes. */
for (i = 0; i < count; i++) {
const CK_ATTRIBUTE *attr = &(attrs[i]);
p11_rpc_buffer_add_attribute (buffer, attr);
}
}
void
p11_rpc_buffer_add_mechanism_type_array_value (p11_buffer *buffer,
const void *value,
CK_ULONG value_length)
{
size_t count = value_length / sizeof (CK_MECHANISM_TYPE);
/* Check if count can be converted to uint32_t. */
if (count > UINT32_MAX) {
p11_buffer_fail (buffer);
return;
}
/* Write the number of items */
p11_rpc_buffer_add_uint32 (buffer, count);
if (value) {
const CK_MECHANISM_TYPE *mechs = value;
size_t i;
for (i = 0; i < count; i++) {
if (mechs[i] > UINT64_MAX) {
p11_buffer_fail (buffer);
return;
}
p11_rpc_buffer_add_uint64 (buffer, mechs[i]);
}
}
}
void
p11_rpc_buffer_add_date_value (p11_buffer *buffer,
const void *value,
CK_ULONG value_length)
{
CK_DATE date_value;
unsigned char array[8];
unsigned char *ptr = NULL;
/* Check if value is empty or can be converted to CK_DATE. */
if (value_length != 0 && value_length != sizeof (CK_DATE)) {
p11_buffer_fail (buffer);
return;
}
if (value && value_length == sizeof (CK_DATE)) {
memcpy (&date_value, value, value_length);
memcpy (array, date_value.year, 4);
memcpy (array + 4, date_value.month, 2);
memcpy (array + 6, date_value.day, 2);
ptr = array;
}
p11_rpc_buffer_add_byte_array (buffer, ptr, value_length);
}
void
p11_rpc_buffer_add_byte_array_value (p11_buffer *buffer,
const void *value,
CK_ULONG value_length)
{
/* Check if value length can be converted to uint32_t, as
* p11_rpc_buffer_add_byte_array expects. */
if (value_length > UINT32_MAX) {
p11_buffer_fail (buffer);
return;
}
p11_rpc_buffer_add_byte_array (buffer, value, value_length);
}
void
p11_rpc_buffer_add_attribute (p11_buffer *buffer, const CK_ATTRIBUTE *attr)
{
unsigned char validity;
p11_rpc_attribute_serializer *serializer;
p11_rpc_value_type value_type;
/* The attribute type */
if (attr->type > UINT32_MAX) {
p11_buffer_fail (buffer);
return;
}
p11_rpc_buffer_add_uint32 (buffer, attr->type);
/* Write out the attribute validity */
validity = (((CK_LONG)attr->ulValueLen) == -1) ? 0 : 1;
p11_rpc_buffer_add_byte (buffer, validity);
if (!validity)
return;
/* The attribute length */
if (attr->ulValueLen > UINT32_MAX) {
p11_buffer_fail (buffer);
return;
}
p11_rpc_buffer_add_uint32 (buffer, attr->ulValueLen);
/* The attribute value */
value_type = map_attribute_to_value_type (attr->type);
assert (value_type < ELEMS (p11_rpc_attribute_serializers));
serializer = &p11_rpc_attribute_serializers[value_type];
assert (serializer != NULL);
serializer->encode (buffer, attr->pValue, attr->ulValueLen);
}
bool
p11_rpc_buffer_get_byte_value (p11_buffer *buffer,
size_t *offset,
void *value,
CK_ULONG *value_length)
{
unsigned char val;
if (!p11_rpc_buffer_get_byte (buffer, offset, &val))
return false;
if (value) {
CK_BYTE byte_value = val;
memcpy (value, &byte_value, sizeof (CK_BYTE));
}
if (value_length)
*value_length = sizeof (CK_BYTE);
return true;
}
bool
p11_rpc_buffer_get_ulong_value (p11_buffer *buffer,
size_t *offset,
void *value,
CK_ULONG *value_length)
{
uint64_t val;
if (!p11_rpc_buffer_get_uint64 (buffer, offset, &val))
return false;
if (value) {
CK_ULONG ulong_value = val;
memcpy (value, &ulong_value, sizeof (CK_ULONG));
}
if (value_length)
*value_length = sizeof (CK_ULONG);
return true;
}
bool
p11_rpc_buffer_get_attribute_array_value (p11_buffer *buffer,
size_t *offset,
void *value,
CK_ULONG *value_length)
{
uint32_t count, i;
CK_ATTRIBUTE *attr, temp;
if (!p11_rpc_buffer_get_uint32 (buffer, offset, &count))
return false;
if (!value) {
memset (&temp, 0, sizeof (CK_ATTRIBUTE));
attr = &temp;
} else
attr = value;
for (i = 0; i < count; i++) {
if (!p11_rpc_buffer_get_attribute (buffer, offset, attr))
return false;
if (value)
attr++;
}
if (value_length)
*value_length = count * sizeof (CK_ATTRIBUTE);
return true;
}
bool
p11_rpc_buffer_get_mechanism_type_array_value (p11_buffer *buffer,
size_t *offset,
void *value,
CK_ULONG *value_length)
{
uint32_t count, i;
CK_MECHANISM_TYPE *mech, temp;
if (!p11_rpc_buffer_get_uint32 (buffer, offset, &count))
return false;
if (!value) {
memset (&temp, 0, sizeof (CK_MECHANISM_TYPE));
mech = &temp;
} else
mech = value;
for (i = 0; i < count; i++) {
CK_ULONG len;
if (!p11_rpc_buffer_get_ulong_value (buffer, offset, mech, &len))
return false;
if (value)
mech++;
}
if (value_length)
*value_length = count * sizeof (CK_MECHANISM_TYPE);
return true;
}
bool
p11_rpc_buffer_get_date_value (p11_buffer *buffer,
size_t *offset,
void *value,
CK_ULONG *value_length)
{
CK_DATE date_value;
const unsigned char *array;
size_t array_length;
/* The encoded date may be empty. */
if (!p11_rpc_buffer_get_byte_array (buffer, offset,
&array, &array_length) ||
(array_length != 0 && array_length != sizeof (CK_DATE)))
return false;
if (value && array_length == sizeof (CK_DATE)) {
memcpy (date_value.year, array, 4);
memcpy (date_value.month, array + 4, 2);
memcpy (date_value.day, array + 6, 2);
memcpy (value, &date_value, sizeof (CK_DATE));
}
if (value_length)
*value_length = array_length;
return true;
}
bool
p11_rpc_buffer_get_byte_array_value (p11_buffer *buffer,
size_t *offset,
void *value,
CK_ULONG *value_length)
{
const unsigned char *val;
size_t len;
if (!p11_rpc_buffer_get_byte_array (buffer, offset, &val, &len))
return false;
if (val && value)
memcpy (value, val, len);
if (value_length)
*value_length = len;
return true;
}
bool
p11_rpc_buffer_get_attribute (p11_buffer *buffer,
size_t *offset,
CK_ATTRIBUTE *attr)
{
uint32_t type, length, decode_length;
unsigned char validity;
p11_rpc_attribute_serializer *serializer;
p11_rpc_value_type value_type;
/* The attribute type */
if (!p11_rpc_buffer_get_uint32 (buffer, offset, &type))
return false;
/* Attribute validity */
if (!p11_rpc_buffer_get_byte (buffer, offset, &validity))
return false;
/* Not a valid attribute */
if (!validity) {
attr->ulValueLen = ((CK_ULONG)-1);
attr->type = type;
return true;
}
if (!p11_rpc_buffer_get_uint32 (buffer, offset, &length))
return false;
/* Decode the attribute value */
value_type = map_attribute_to_value_type (type);
assert (value_type < ELEMS (p11_rpc_attribute_serializers));
serializer = &p11_rpc_attribute_serializers[value_type];
assert (serializer != NULL);
if (!serializer->decode (buffer, offset, attr->pValue, &attr->ulValueLen))
return false;
if (!attr->pValue) {
decode_length = attr->ulValueLen;
attr->ulValueLen = length;
if (decode_length > length) {
return false;
}
}
attr->type = type;
return true;
}
/* Used to override the supported mechanisms in tests */
CK_MECHANISM_TYPE *p11_rpc_mechanisms_override_supported = NULL;
typedef struct {
CK_MECHANISM_TYPE type;
p11_rpc_value_encoder encode;
p11_rpc_value_decoder decode;
} p11_rpc_mechanism_serializer;
void
p11_rpc_buffer_add_rsa_pkcs_pss_mechanism_value (p11_buffer *buffer,
const void *value,
CK_ULONG value_length)
{
CK_RSA_PKCS_PSS_PARAMS params;
/* Check if value can be converted to CK_RSA_PKCS_PSS_PARAMS. */
if (value_length != sizeof (CK_RSA_PKCS_PSS_PARAMS)) {
p11_buffer_fail (buffer);
return;
}
memcpy (¶ms, value, value_length);
/* Check if params.hashAlg, params.mgf, and params.sLen can be
* converted to uint64_t. */
if (params.hashAlg > UINT64_MAX || params.mgf > UINT64_MAX ||
params.sLen > UINT64_MAX) {
p11_buffer_fail (buffer);
return;
}
p11_rpc_buffer_add_uint64 (buffer, params.hashAlg);
p11_rpc_buffer_add_uint64 (buffer, params.mgf);
p11_rpc_buffer_add_uint64 (buffer, params.sLen);
}
bool
p11_rpc_buffer_get_rsa_pkcs_pss_mechanism_value (p11_buffer *buffer,
size_t *offset,
void *value,
CK_ULONG *value_length)
{
uint64_t val[3];
if (!p11_rpc_buffer_get_uint64 (buffer, offset, &val[0]))
return false;
if (!p11_rpc_buffer_get_uint64 (buffer, offset, &val[1]))
return false;
if (!p11_rpc_buffer_get_uint64 (buffer, offset, &val[2]))
return false;
if (value) {
CK_RSA_PKCS_PSS_PARAMS params;
params.hashAlg = val[0];
params.mgf = val[1];
params.sLen = val[2];
memcpy (value, ¶ms, sizeof (CK_RSA_PKCS_PSS_PARAMS));
}
if (value_length)
*value_length = sizeof (CK_RSA_PKCS_PSS_PARAMS);
return true;
}
void
p11_rpc_buffer_add_rsa_pkcs_oaep_mechanism_value (p11_buffer *buffer,
const void *value,
CK_ULONG value_length)
{
CK_RSA_PKCS_OAEP_PARAMS params;
/* Check if value can be converted to CK_RSA_PKCS_OAEP_PARAMS. */
if (value_length != sizeof (CK_RSA_PKCS_OAEP_PARAMS)) {
p11_buffer_fail (buffer);
return;
}
memcpy (¶ms, value, value_length);
/* Check if params.hashAlg, params.mgf, and params.source can be
* converted to uint64_t. */
if (params.hashAlg > UINT64_MAX || params.mgf > UINT64_MAX ||
params.source > UINT64_MAX) {
p11_buffer_fail (buffer);
return;
}
p11_rpc_buffer_add_uint64 (buffer, params.hashAlg);
p11_rpc_buffer_add_uint64 (buffer, params.mgf);
p11_rpc_buffer_add_uint64 (buffer, params.source);
/* parmas.pSourceData can only be an array of CK_BYTE or
* NULL */
p11_rpc_buffer_add_byte_array (buffer,
(unsigned char *)params.pSourceData,
params.ulSourceDataLen);
}
bool
p11_rpc_buffer_get_rsa_pkcs_oaep_mechanism_value (p11_buffer *buffer,
size_t *offset,
void *value,
CK_ULONG *value_length)
{
uint64_t val[3];
const unsigned char *data;
size_t len;
if (!p11_rpc_buffer_get_uint64 (buffer, offset, &val[0]))
return false;
if (!p11_rpc_buffer_get_uint64 (buffer, offset, &val[1]))
return false;
if (!p11_rpc_buffer_get_uint64 (buffer, offset, &val[2]))
return false;
if (!p11_rpc_buffer_get_byte_array (buffer, offset, &data, &len))
return false;
if (value) {
CK_RSA_PKCS_OAEP_PARAMS params;
params.hashAlg = val[0];
params.mgf = val[1];
params.source = val[2];
params.pSourceData = (void *) data;
params.ulSourceDataLen = len;
memcpy (value, ¶ms, sizeof (CK_RSA_PKCS_OAEP_PARAMS));
}
if (value_length)
*value_length = sizeof (CK_RSA_PKCS_OAEP_PARAMS);
return true;
}
static p11_rpc_mechanism_serializer p11_rpc_mechanism_serializers[] = {
{ CKM_RSA_PKCS_PSS, p11_rpc_buffer_add_rsa_pkcs_pss_mechanism_value, p11_rpc_buffer_get_rsa_pkcs_pss_mechanism_value },
{ CKM_RSA_PKCS_OAEP, p11_rpc_buffer_add_rsa_pkcs_oaep_mechanism_value, p11_rpc_buffer_get_rsa_pkcs_oaep_mechanism_value }
};
static p11_rpc_mechanism_serializer p11_rpc_byte_array_mechanism_serializer = {
0, p11_rpc_buffer_add_byte_array_value, p11_rpc_buffer_get_byte_array_value
};
static bool
mechanism_has_sane_parameters (CK_MECHANISM_TYPE type)
{
int i;
/* This can be set from tests, to override default set of supported */
if (p11_rpc_mechanisms_override_supported) {
for (i = 0; p11_rpc_mechanisms_override_supported[i] != 0; i++) {
if (p11_rpc_mechanisms_override_supported[i] == type)
return true;
}
return false;
}
for (i = 0; i < ELEMS(p11_rpc_mechanism_serializers); i++) {
if (p11_rpc_mechanism_serializers[i].type == type)
return true;
}
return false;
}
static bool
mechanism_has_no_parameters (CK_MECHANISM_TYPE mech)
{
/* This list is incomplete */
switch (mech) {
case CKM_RSA_PKCS_KEY_PAIR_GEN:
case CKM_RSA_X9_31_KEY_PAIR_GEN:
case CKM_RSA_PKCS:
case CKM_RSA_9796:
case CKM_RSA_X_509:
case CKM_RSA_X9_31:
case CKM_MD2_RSA_PKCS:
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_RIPEMD128_RSA_PKCS:
case CKM_RIPEMD160_RSA_PKCS:
case CKM_SHA1_RSA_X9_31:
case CKM_DSA_KEY_PAIR_GEN:
case CKM_DSA_PARAMETER_GEN:
case CKM_DSA:
case CKM_DSA_SHA1:
case CKM_FORTEZZA_TIMESTAMP:
case CKM_EC_KEY_PAIR_GEN:
case CKM_ECDSA:
case CKM_ECDSA_SHA1:
case CKM_DH_PKCS_KEY_PAIR_GEN:
case CKM_DH_PKCS_PARAMETER_GEN:
case CKM_X9_42_DH_KEY_PAIR_GEN:
case CKM_X9_42_DH_PARAMETER_GEN:
case CKM_KEA_KEY_PAIR_GEN:
case CKM_GENERIC_SECRET_KEY_GEN:
case CKM_RC2_KEY_GEN:
case CKM_RC4_KEY_GEN:
case CKM_RC4:
case CKM_RC5_KEY_GEN:
case CKM_AES_KEY_GEN:
case CKM_AES_ECB:
case CKM_AES_MAC:
case CKM_DES_KEY_GEN:
case CKM_DES2_KEY_GEN:
case CKM_DES3_KEY_GEN:
case CKM_CDMF_KEY_GEN:
case CKM_CAST_KEY_GEN:
case CKM_CAST3_KEY_GEN:
case CKM_CAST128_KEY_GEN:
case CKM_IDEA_KEY_GEN:
case CKM_SSL3_PRE_MASTER_KEY_GEN:
case CKM_TLS_PRE_MASTER_KEY_GEN:
case CKM_SKIPJACK_KEY_GEN:
case CKM_BATON_KEY_GEN:
case CKM_JUNIPER_KEY_GEN:
case CKM_RC2_ECB:
case CKM_DES_ECB:
case CKM_DES3_ECB:
case CKM_CDMF_ECB:
case CKM_CAST_ECB:
case CKM_CAST3_ECB:
case CKM_CAST128_ECB:
case CKM_RC5_ECB:
case CKM_IDEA_ECB:
case CKM_RC2_MAC:
case CKM_DES_MAC:
case CKM_DES3_MAC:
case CKM_CDMF_MAC:
case CKM_CAST_MAC:
case CKM_CAST3_MAC:
case CKM_RC5_MAC:
case CKM_IDEA_MAC:
case CKM_SSL3_MD5_MAC:
case CKM_SSL3_SHA1_MAC:
case CKM_SKIPJACK_WRAP:
case CKM_BATON_WRAP:
case CKM_JUNIPER_WRAP:
case CKM_MD2:
case CKM_MD2_HMAC:
case CKM_MD5:
case CKM_MD5_HMAC:
case CKM_SHA_1:
case CKM_SHA_1_HMAC:
case CKM_SHA256:
case CKM_SHA256_HMAC:
case CKM_SHA384:
case CKM_SHA384_HMAC:
case CKM_SHA512:
case CKM_SHA512_HMAC:
case CKM_FASTHASH:
case CKM_RIPEMD128:
case CKM_RIPEMD128_HMAC:
case CKM_RIPEMD160:
case CKM_RIPEMD160_HMAC:
case CKM_KEY_WRAP_LYNKS:
return true;
default:
return false;
};
}
bool
p11_rpc_mechanism_is_supported (CK_MECHANISM_TYPE mech)
{
if (mechanism_has_no_parameters (mech) ||
mechanism_has_sane_parameters (mech))
return true;
return false;
}
void
p11_rpc_buffer_add_mechanism (p11_buffer *buffer, const CK_MECHANISM *mech)
{
p11_rpc_mechanism_serializer *serializer = NULL;
size_t i;
/* The mechanism type */
p11_rpc_buffer_add_uint32 (buffer, mech->mechanism);
if (mechanism_has_no_parameters (mech->mechanism)) {
p11_rpc_buffer_add_byte_array (buffer, NULL, 0);
return;
}
assert (mechanism_has_sane_parameters (mech->mechanism));
for (i = 0; i < ELEMS (p11_rpc_mechanism_serializers); i++) {
if (p11_rpc_mechanism_serializers[i].type == mech->mechanism) {
serializer = &p11_rpc_mechanism_serializers[i];
break;
}
}
if (serializer == NULL)
serializer = &p11_rpc_byte_array_mechanism_serializer;
serializer->encode (buffer, mech->pParameter, mech->ulParameterLen);
}
bool
p11_rpc_buffer_get_mechanism (p11_buffer *buffer,
size_t *offset,
CK_MECHANISM *mech)
{
uint32_t mechanism;
p11_rpc_mechanism_serializer *serializer = NULL;
size_t i;
/* The mechanism type */
if (!p11_rpc_buffer_get_uint32 (buffer, offset, &mechanism))
return false;
mech->mechanism = mechanism;
for (i = 0; i < ELEMS (p11_rpc_mechanism_serializers); i++) {
if (p11_rpc_mechanism_serializers[i].type == mech->mechanism) {
serializer = &p11_rpc_mechanism_serializers[i];
break;
}
}
if (serializer == NULL)
serializer = &p11_rpc_byte_array_mechanism_serializer;
if (!serializer->decode (buffer, offset,
mech->pParameter, &mech->ulParameterLen))
return false;
return true;
}