| /* |
| * CDDL HEADER START |
| * |
| * The contents of this file are subject to the terms of the |
| * Common Development and Distribution License (the "License"). |
| * You may not use this file except in compliance with the License. |
| * |
| * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE |
| * or http://www.opensolaris.org/os/licensing. |
| * See the License for the specific language governing permissions |
| * and limitations under the License. |
| * |
| * When distributing Covered Code, include this CDDL HEADER in each |
| * file and include the License file at usr/src/OPENSOLARIS.LICENSE. |
| * If applicable, add the following below this CDDL HEADER, with the |
| * fields enclosed by brackets "[]" replaced with your own identifying |
| * information: Portions Copyright [yyyy] [name of copyright owner] |
| * |
| * CDDL HEADER END |
| */ |
| /* |
| * Copyright (c) 2003, 2010, Oracle and/or its affiliates. All rights reserved. |
| * Copyright 2018, Joyent, Inc. |
| */ |
| |
| #include <stdlib.h> |
| #include <string.h> |
| #include <strings.h> |
| #include <stdio.h> |
| #include <cryptoutil.h> |
| #include <errno.h> |
| #include <security/cryptoki.h> |
| #include <sys/crypto/common.h> |
| #include <sys/crypto/ioctl.h> |
| #include "kernelGlobal.h" |
| #include "kernelObject.h" |
| #include "kernelSlot.h" |
| |
| #define ENCODE_ATTR(type, value, len) { \ |
| cur_attr->oa_type = type; \ |
| (void) memcpy(ptr, value, len); \ |
| cur_attr->oa_value = ptr; \ |
| cur_attr->oa_value_len = len; \ |
| cur_attr++; \ |
| } |
| |
| #define MECH_HASH(type) (((uintptr_t)type) % KMECH_HASHTABLE_SIZE) |
| /* |
| * Serialize writes to the hash table. We don't need a per bucket lock as |
| * there are only a few writes and we don't need the lock for reads. |
| */ |
| pthread_mutex_t mechhash_mutex = PTHREAD_MUTEX_INITIALIZER; |
| |
| static CK_RV |
| kmech_hash_insert(CK_MECHANISM_TYPE type, crypto_mech_type_t kmech) |
| { |
| uint_t h; |
| kmh_elem_t *elem, *cur; |
| |
| elem = malloc(sizeof (kmh_elem_t)); |
| if (elem == NULL) |
| return (CKR_HOST_MEMORY); |
| |
| h = MECH_HASH(type); |
| elem->type = type; |
| elem->kmech = kmech; |
| |
| (void) pthread_mutex_lock(&mechhash_mutex); |
| for (cur = kernel_mechhash[h]; cur != NULL; cur = cur->knext) { |
| if (type == cur->type) { |
| /* Some other thread beat us to it. */ |
| (void) pthread_mutex_unlock(&mechhash_mutex); |
| free(elem); |
| return (CKR_OK); |
| } |
| } |
| elem->knext = kernel_mechhash[h]; |
| kernel_mechhash[h] = elem; |
| (void) pthread_mutex_unlock(&mechhash_mutex); |
| |
| return (CKR_OK); |
| } |
| |
| CK_RV |
| kernel_mech(CK_MECHANISM_TYPE type, crypto_mech_type_t *k_number) |
| { |
| crypto_get_mechanism_number_t get_number; |
| const char *string; |
| CK_RV rv; |
| int r; |
| kmh_elem_t *elem; |
| uint_t h; |
| char buf[11]; /* Num chars for representing ulong in ASCII */ |
| |
| /* |
| * Search for an existing entry. No need to lock since we are |
| * just a reader and we never free the entries in the hash table. |
| */ |
| h = MECH_HASH(type); |
| for (elem = kernel_mechhash[h]; elem != NULL; elem = elem->knext) { |
| if (type == elem->type) { |
| *k_number = elem->kmech; |
| return (CKR_OK); |
| } |
| } |
| |
| if (type >= CKM_VENDOR_DEFINED) { |
| (void) snprintf(buf, sizeof (buf), "%#lx", type); |
| string = buf; |
| } else { |
| string = pkcs11_mech2str(type); |
| } |
| |
| if (string == NULL) |
| return (CKR_MECHANISM_INVALID); |
| |
| get_number.pn_mechanism_string = (char *)string; |
| get_number.pn_mechanism_len = strlen(string) + 1; |
| |
| while ((r = ioctl(kernel_fd, CRYPTO_GET_MECHANISM_NUMBER, |
| &get_number)) < 0) { |
| if (errno != EINTR) |
| break; |
| } |
| if (r < 0) { |
| rv = CKR_MECHANISM_INVALID; |
| } else { |
| if (get_number.pn_return_value != CRYPTO_SUCCESS) { |
| rv = crypto2pkcs11_error_number( |
| get_number.pn_return_value); |
| } else { |
| rv = CKR_OK; |
| } |
| } |
| |
| if (rv == CKR_OK) { |
| *k_number = get_number.pn_internal_number; |
| /* Add this to the hash table */ |
| (void) kmech_hash_insert(type, *k_number); |
| } |
| |
| return (rv); |
| } |
| |
| |
| /* |
| * Return the value of a secret key object. |
| * This routine allocates memory for the value. |
| * A null pointer is returned on error. |
| */ |
| unsigned char * |
| get_symmetric_key_value(kernel_object_t *key_p) |
| { |
| uint8_t *cipherKey; |
| |
| switch (key_p->class) { |
| |
| case CKO_SECRET_KEY: |
| |
| cipherKey = malloc(OBJ_SEC(key_p)->sk_value_len); |
| if (cipherKey == NULL) |
| return (NULL); |
| |
| (void) memcpy(cipherKey, OBJ_SEC(key_p)->sk_value, |
| OBJ_SEC(key_p)->sk_value_len); |
| |
| return (cipherKey); |
| |
| default: |
| return (NULL); |
| } |
| } |
| |
| /* |
| * Convert a RSA private key object into a crypto_key structure. |
| * Memory is allocated for each attribute stored in the crypto_key |
| * structure. Memory for the crypto_key structure is not |
| * allocated. Attributes can be freed by free_key_attributes(). |
| */ |
| CK_RV |
| get_rsa_private_key(kernel_object_t *object_p, crypto_key_t *key) |
| { |
| biginteger_t *big; |
| crypto_object_attribute_t *attrs, *cur_attr; |
| char *ptr; |
| CK_RV rv; |
| |
| (void) pthread_mutex_lock(&object_p->object_mutex); |
| if (object_p->key_type != CKK_RSA || |
| object_p->class != CKO_PRIVATE_KEY) { |
| (void) pthread_mutex_unlock(&object_p->object_mutex); |
| return (CKR_ATTRIBUTE_TYPE_INVALID); |
| } |
| |
| attrs = calloc(1, |
| RSA_PRI_ATTR_COUNT * sizeof (crypto_object_attribute_t)); |
| if (attrs == NULL) { |
| (void) pthread_mutex_unlock(&object_p->object_mutex); |
| return (CKR_HOST_MEMORY); |
| } |
| |
| key->ck_format = CRYPTO_KEY_ATTR_LIST; |
| key->ck_attrs = attrs; |
| cur_attr = attrs; |
| |
| /* |
| * Allocate memory for each key attribute and set up the value |
| * value length. |
| */ |
| key->ck_count = 0; |
| |
| /* CKA_MODULUS is required. */ |
| big = OBJ_PRI_RSA_MOD(object_p); |
| if (big->big_value == NULL) { |
| rv = CKR_ATTRIBUTE_TYPE_INVALID; |
| goto fail_cleanup; |
| } else { |
| if ((ptr = malloc(big->big_value_len)) == NULL) { |
| rv = CKR_HOST_MEMORY; |
| goto fail_cleanup; |
| } |
| ENCODE_ATTR(CKA_MODULUS, big->big_value, big->big_value_len); |
| key->ck_count++; |
| } |
| |
| /* CKA_PRIVATE_EXPONENT is required. */ |
| big = OBJ_PRI_RSA_PRIEXPO(object_p); |
| if (big->big_value == NULL) { |
| rv = CKR_ATTRIBUTE_TYPE_INVALID; |
| goto fail_cleanup; |
| } else { |
| if ((ptr = malloc(big->big_value_len)) == NULL) { |
| rv = CKR_HOST_MEMORY; |
| goto fail_cleanup; |
| } |
| ENCODE_ATTR(CKA_PRIVATE_EXPONENT, big->big_value, |
| big->big_value_len); |
| key->ck_count++; |
| } |
| |
| /* CKA_PRIME_1 is optional. */ |
| big = OBJ_PRI_RSA_PRIME1(object_p); |
| if (big->big_value != NULL) { |
| if ((ptr = malloc(big->big_value_len)) == NULL) { |
| rv = CKR_HOST_MEMORY; |
| goto fail_cleanup; |
| } |
| ENCODE_ATTR(CKA_PRIME_1, big->big_value, big->big_value_len); |
| key->ck_count++; |
| } |
| |
| /* CKA_PRIME_2 is optional. */ |
| big = OBJ_PRI_RSA_PRIME2(object_p); |
| if (big->big_value != NULL) { |
| if ((ptr = malloc(big->big_value_len)) == NULL) { |
| rv = CKR_HOST_MEMORY; |
| goto fail_cleanup; |
| } |
| ENCODE_ATTR(CKA_PRIME_2, big->big_value, big->big_value_len); |
| key->ck_count++; |
| } |
| |
| /* CKA_EXPONENT_1 is optional. */ |
| big = OBJ_PRI_RSA_EXPO1(object_p); |
| if (big->big_value != NULL) { |
| if ((ptr = malloc(big->big_value_len)) == NULL) { |
| rv = CKR_HOST_MEMORY; |
| goto fail_cleanup; |
| } |
| ENCODE_ATTR(CKA_EXPONENT_1, big->big_value, |
| big->big_value_len); |
| key->ck_count++; |
| } |
| |
| /* CKA_EXPONENT_2 is optional. */ |
| big = OBJ_PRI_RSA_EXPO2(object_p); |
| if (big->big_value != NULL) { |
| if ((ptr = malloc(big->big_value_len)) == NULL) { |
| rv = CKR_HOST_MEMORY; |
| goto fail_cleanup; |
| } |
| ENCODE_ATTR(CKA_EXPONENT_2, big->big_value, |
| big->big_value_len); |
| key->ck_count++; |
| } |
| |
| /* CKA_COEFFICIENT is optional. */ |
| big = OBJ_PRI_RSA_COEF(object_p); |
| if (big->big_value != NULL) { |
| if ((ptr = malloc(big->big_value_len)) == NULL) { |
| rv = CKR_HOST_MEMORY; |
| goto fail_cleanup; |
| } |
| ENCODE_ATTR(CKA_COEFFICIENT, big->big_value, |
| big->big_value_len); |
| key->ck_count++; |
| } |
| |
| (void) pthread_mutex_unlock(&object_p->object_mutex); |
| return (CKR_OK); |
| |
| fail_cleanup: |
| (void) pthread_mutex_unlock(&object_p->object_mutex); |
| free_key_attributes(key); |
| return (rv); |
| } |
| |
| /* |
| * Convert a RSA public key object into a crypto_key structure. |
| * Memory is allocated for each attribute stored in the crypto_key |
| * structure. Memory for the crypto_key structure is not |
| * allocated. Attributes can be freed by free_key_attributes(). |
| */ |
| CK_RV |
| get_rsa_public_key(kernel_object_t *object_p, crypto_key_t *key) |
| { |
| biginteger_t *big; |
| crypto_object_attribute_t *attrs, *cur_attr; |
| char *ptr; |
| |
| (void) pthread_mutex_lock(&object_p->object_mutex); |
| if (object_p->key_type != CKK_RSA || |
| object_p->class != CKO_PUBLIC_KEY) { |
| (void) pthread_mutex_unlock(&object_p->object_mutex); |
| return (CKR_ATTRIBUTE_TYPE_INVALID); |
| } |
| |
| attrs = calloc(1, |
| RSA_PUB_ATTR_COUNT * sizeof (crypto_object_attribute_t)); |
| if (attrs == NULL) { |
| (void) pthread_mutex_unlock(&object_p->object_mutex); |
| return (CKR_HOST_MEMORY); |
| } |
| |
| key->ck_format = CRYPTO_KEY_ATTR_LIST; |
| key->ck_count = RSA_PUB_ATTR_COUNT; |
| key->ck_attrs = attrs; |
| |
| cur_attr = attrs; |
| big = OBJ_PUB_RSA_PUBEXPO(object_p); |
| if ((ptr = malloc(big->big_value_len)) == NULL) |
| goto mem_failure; |
| ENCODE_ATTR(CKA_PUBLIC_EXPONENT, big->big_value, big->big_value_len); |
| |
| big = OBJ_PUB_RSA_MOD(object_p); |
| if ((ptr = malloc(big->big_value_len)) == NULL) |
| goto mem_failure; |
| ENCODE_ATTR(CKA_MODULUS, big->big_value, big->big_value_len); |
| |
| if ((ptr = malloc(sizeof (CK_ULONG))) == NULL) |
| goto mem_failure; |
| ENCODE_ATTR(CKA_MODULUS_BITS, &OBJ_PUB_RSA_MOD_BITS(object_p), |
| sizeof (CK_ULONG)); |
| |
| (void) pthread_mutex_unlock(&object_p->object_mutex); |
| return (CKR_OK); |
| |
| mem_failure: |
| (void) pthread_mutex_unlock(&object_p->object_mutex); |
| free_key_attributes(key); |
| return (CKR_HOST_MEMORY); |
| } |
| |
| /* |
| * Free attribute storage in a crypto_key structure. |
| */ |
| void |
| free_key_attributes(crypto_key_t *key) |
| { |
| int i; |
| |
| if (key->ck_format == CRYPTO_KEY_ATTR_LIST && |
| (key->ck_count > 0) && key->ck_attrs != NULL) { |
| for (i = 0; i < key->ck_count; i++) { |
| freezero(key->ck_attrs[i].oa_value, |
| key->ck_attrs[i].oa_value_len); |
| } |
| free(key->ck_attrs); |
| } |
| } |
| |
| |
| /* |
| * Convert a DSA private key object into a crypto_key structure. |
| * Memory is allocated for each attribute stored in the crypto_key |
| * structure. Memory for the crypto_key structure is not |
| * allocated. Attributes can be freed by free_dsa_key_attributes(). |
| */ |
| CK_RV |
| get_dsa_private_key(kernel_object_t *object_p, crypto_key_t *key) |
| { |
| biginteger_t *big; |
| crypto_object_attribute_t *attrs, *cur_attr; |
| char *ptr; |
| |
| (void) pthread_mutex_lock(&object_p->object_mutex); |
| if (object_p->key_type != CKK_DSA || |
| object_p->class != CKO_PRIVATE_KEY) { |
| (void) pthread_mutex_unlock(&object_p->object_mutex); |
| return (CKR_ATTRIBUTE_TYPE_INVALID); |
| } |
| |
| attrs = calloc(1, |
| DSA_ATTR_COUNT * sizeof (crypto_object_attribute_t)); |
| if (attrs == NULL) { |
| (void) pthread_mutex_unlock(&object_p->object_mutex); |
| return (CKR_HOST_MEMORY); |
| } |
| |
| key->ck_format = CRYPTO_KEY_ATTR_LIST; |
| key->ck_count = DSA_ATTR_COUNT; |
| key->ck_attrs = attrs; |
| |
| cur_attr = attrs; |
| big = OBJ_PRI_DSA_PRIME(object_p); |
| if ((ptr = malloc(big->big_value_len)) == NULL) |
| goto mem_failure; |
| ENCODE_ATTR(CKA_PRIME, big->big_value, big->big_value_len); |
| |
| big = OBJ_PRI_DSA_SUBPRIME(object_p); |
| if ((ptr = malloc(big->big_value_len)) == NULL) |
| goto mem_failure; |
| ENCODE_ATTR(CKA_SUBPRIME, big->big_value, big->big_value_len); |
| |
| big = OBJ_PRI_DSA_BASE(object_p); |
| if ((ptr = malloc(big->big_value_len)) == NULL) |
| goto mem_failure; |
| ENCODE_ATTR(CKA_BASE, big->big_value, big->big_value_len); |
| |
| big = OBJ_PRI_DSA_VALUE(object_p); |
| if ((ptr = malloc(big->big_value_len)) == NULL) |
| goto mem_failure; |
| ENCODE_ATTR(CKA_VALUE, big->big_value, big->big_value_len); |
| |
| (void) pthread_mutex_unlock(&object_p->object_mutex); |
| return (CKR_OK); |
| |
| mem_failure: |
| (void) pthread_mutex_unlock(&object_p->object_mutex); |
| free_key_attributes(key); |
| return (CKR_HOST_MEMORY); |
| } |
| |
| |
| /* |
| * Convert a DSA public key object into a crypto_key structure. |
| * Memory is allocated for each attribute stored in the crypto_key |
| * structure. Memory for the crypto_key structure is not |
| * allocated. Attributes can be freed by free_dsa_key_attributes(). |
| */ |
| CK_RV |
| get_dsa_public_key(kernel_object_t *object_p, crypto_key_t *key) |
| { |
| biginteger_t *big; |
| crypto_object_attribute_t *attrs, *cur_attr; |
| char *ptr; |
| |
| (void) pthread_mutex_lock(&object_p->object_mutex); |
| if (object_p->key_type != CKK_DSA || |
| object_p->class != CKO_PUBLIC_KEY) { |
| (void) pthread_mutex_unlock(&object_p->object_mutex); |
| return (CKR_ATTRIBUTE_TYPE_INVALID); |
| } |
| |
| attrs = calloc(1, |
| DSA_ATTR_COUNT * sizeof (crypto_object_attribute_t)); |
| if (attrs == NULL) { |
| (void) pthread_mutex_unlock(&object_p->object_mutex); |
| return (CKR_HOST_MEMORY); |
| } |
| |
| key->ck_format = CRYPTO_KEY_ATTR_LIST; |
| key->ck_count = DSA_ATTR_COUNT; |
| key->ck_attrs = attrs; |
| |
| cur_attr = attrs; |
| big = OBJ_PUB_DSA_PRIME(object_p); |
| if ((ptr = malloc(big->big_value_len)) == NULL) |
| goto mem_failure; |
| ENCODE_ATTR(CKA_PRIME, big->big_value, big->big_value_len); |
| |
| big = OBJ_PUB_DSA_SUBPRIME(object_p); |
| if ((ptr = malloc(big->big_value_len)) == NULL) |
| goto mem_failure; |
| ENCODE_ATTR(CKA_SUBPRIME, big->big_value, big->big_value_len); |
| |
| big = OBJ_PUB_DSA_BASE(object_p); |
| if ((ptr = malloc(big->big_value_len)) == NULL) |
| goto mem_failure; |
| ENCODE_ATTR(CKA_BASE, big->big_value, big->big_value_len); |
| |
| big = OBJ_PUB_DSA_VALUE(object_p); |
| if ((ptr = malloc(big->big_value_len)) == NULL) |
| goto mem_failure; |
| ENCODE_ATTR(CKA_VALUE, big->big_value, big->big_value_len); |
| |
| (void) pthread_mutex_unlock(&object_p->object_mutex); |
| return (CKR_OK); |
| |
| mem_failure: |
| (void) pthread_mutex_unlock(&object_p->object_mutex); |
| free_key_attributes(key); |
| return (CKR_HOST_MEMORY); |
| } |
| |
| |
| /* |
| * Convert a EC private key object into a crypto_key structure. |
| * Memory is allocated for each attribute stored in the crypto_key |
| * structure. Memory for the crypto_key structure is not |
| * allocated. Attributes can be freed by free_ec_key_attributes(). |
| */ |
| CK_RV |
| get_ec_private_key(kernel_object_t *object_p, crypto_key_t *key) |
| { |
| biginteger_t *big; |
| crypto_object_attribute_t *attrs, *cur_attr; |
| CK_ATTRIBUTE tmp; |
| char *ptr; |
| int rv; |
| |
| (void) pthread_mutex_lock(&object_p->object_mutex); |
| if (object_p->key_type != CKK_EC || |
| object_p->class != CKO_PRIVATE_KEY) { |
| (void) pthread_mutex_unlock(&object_p->object_mutex); |
| return (CKR_ATTRIBUTE_TYPE_INVALID); |
| } |
| |
| attrs = calloc(EC_ATTR_COUNT, sizeof (crypto_object_attribute_t)); |
| if (attrs == NULL) { |
| (void) pthread_mutex_unlock(&object_p->object_mutex); |
| return (CKR_HOST_MEMORY); |
| } |
| |
| key->ck_format = CRYPTO_KEY_ATTR_LIST; |
| key->ck_count = EC_ATTR_COUNT; |
| key->ck_attrs = attrs; |
| |
| cur_attr = attrs; |
| big = OBJ_PRI_EC_VALUE(object_p); |
| if ((ptr = malloc(big->big_value_len)) == NULL) { |
| rv = CKR_HOST_MEMORY; |
| goto fail; |
| } |
| ENCODE_ATTR(CKA_VALUE, big->big_value, big->big_value_len); |
| |
| tmp.type = CKA_EC_PARAMS; |
| tmp.pValue = NULL; |
| rv = kernel_get_attribute(object_p, &tmp); |
| if (rv != CKR_OK) { |
| goto fail; |
| } |
| |
| tmp.pValue = malloc(tmp.ulValueLen); |
| if (tmp.pValue == NULL) { |
| rv = CKR_HOST_MEMORY; |
| goto fail; |
| } |
| |
| rv = kernel_get_attribute(object_p, &tmp); |
| if (rv != CKR_OK) { |
| free(tmp.pValue); |
| goto fail; |
| } |
| |
| cur_attr->oa_type = tmp.type; |
| cur_attr->oa_value = tmp.pValue; |
| cur_attr->oa_value_len = tmp.ulValueLen; |
| |
| (void) pthread_mutex_unlock(&object_p->object_mutex); |
| return (CKR_OK); |
| |
| fail: |
| (void) pthread_mutex_unlock(&object_p->object_mutex); |
| free_key_attributes(key); |
| return (rv); |
| } |
| |
| /* |
| * Convert an EC public key object into a crypto_key structure. |
| * Memory is allocated for each attribute stored in the crypto_key |
| * structure. Memory for the crypto_key structure is not |
| * allocated. Attributes can be freed by free_ec_key_attributes(). |
| */ |
| CK_RV |
| get_ec_public_key(kernel_object_t *object_p, crypto_key_t *key) |
| { |
| biginteger_t *big; |
| crypto_object_attribute_t *attrs, *cur_attr; |
| CK_ATTRIBUTE tmp; |
| char *ptr; |
| int rv; |
| |
| (void) pthread_mutex_lock(&object_p->object_mutex); |
| if (object_p->key_type != CKK_EC || |
| object_p->class != CKO_PUBLIC_KEY) { |
| (void) pthread_mutex_unlock(&object_p->object_mutex); |
| return (CKR_ATTRIBUTE_TYPE_INVALID); |
| } |
| |
| attrs = calloc(EC_ATTR_COUNT, sizeof (crypto_object_attribute_t)); |
| if (attrs == NULL) { |
| (void) pthread_mutex_unlock(&object_p->object_mutex); |
| return (CKR_HOST_MEMORY); |
| } |
| |
| key->ck_format = CRYPTO_KEY_ATTR_LIST; |
| key->ck_count = EC_ATTR_COUNT; |
| key->ck_attrs = attrs; |
| |
| cur_attr = attrs; |
| big = OBJ_PUB_EC_POINT(object_p); |
| if ((ptr = malloc(big->big_value_len)) == NULL) { |
| rv = CKR_HOST_MEMORY; |
| goto fail; |
| } |
| ENCODE_ATTR(CKA_EC_POINT, big->big_value, big->big_value_len); |
| |
| tmp.type = CKA_EC_PARAMS; |
| tmp.pValue = NULL; |
| rv = kernel_get_attribute(object_p, &tmp); |
| if (rv != CKR_OK) { |
| goto fail; |
| } |
| |
| tmp.pValue = malloc(tmp.ulValueLen); |
| if (tmp.pValue == NULL) { |
| rv = CKR_HOST_MEMORY; |
| goto fail; |
| } |
| |
| rv = kernel_get_attribute(object_p, &tmp); |
| if (rv != CKR_OK) { |
| free(tmp.pValue); |
| goto fail; |
| } |
| |
| cur_attr->oa_type = tmp.type; |
| cur_attr->oa_value = tmp.pValue; |
| cur_attr->oa_value_len = tmp.ulValueLen; |
| |
| (void) pthread_mutex_unlock(&object_p->object_mutex); |
| return (CKR_OK); |
| |
| fail: |
| (void) pthread_mutex_unlock(&object_p->object_mutex); |
| free_key_attributes(key); |
| return (rv); |
| } |
| |
| /* |
| * Convert an attribute template into an obj_attrs array. |
| * Memory is allocated for each attribute stored in the obj_attrs. |
| * The memory can be freed by free_object_attributes(). |
| * |
| * If the boolean pointer is_token_obj is not NULL, the caller wants to |
| * retrieve the value of the CKA_TOKEN attribute if it is specified in the |
| * template. |
| * - When this routine is called thru C_CreateObject(), C_CopyObject(), or |
| * any key management function, is_token_obj should NOT be NULL. |
| * - When this routine is called thru C_GetAttributeValue() or |
| * C_SetAttributeValue(), "is_token_obj" should be NULL. |
| */ |
| CK_RV |
| process_object_attributes(CK_ATTRIBUTE_PTR pTemplate, CK_ULONG ulCount, |
| caddr_t *obj_attrs, CK_BBOOL *is_token_obj) |
| { |
| crypto_object_attribute_t *attrs, *cur_attr; |
| int i, cur_i; |
| char *ptr; |
| CK_RV rv; |
| ssize_t value_len; |
| |
| if (ulCount == 0) { |
| obj_attrs = NULL; |
| return (CKR_OK); |
| } |
| |
| attrs = calloc(1, ulCount * sizeof (crypto_object_attribute_t)); |
| if (attrs == NULL) { |
| return (CKR_HOST_MEMORY); |
| } |
| |
| cur_attr = attrs; |
| for (i = 0; i < ulCount; i++) { |
| /* |
| * The length of long attributes must be set correctly |
| * so providers can determine whether they came from 32 |
| * or 64-bit applications. |
| */ |
| switch (pTemplate[i].type) { |
| case CKA_CLASS: |
| case CKA_CERTIFICATE_TYPE: |
| case CKA_KEY_TYPE: |
| case CKA_MODULUS_BITS: |
| case CKA_HW_FEATURE_TYPE: |
| value_len = sizeof (ulong_t); |
| if (pTemplate[i].pValue != NULL && |
| (pTemplate[i].ulValueLen < value_len)) { |
| rv = CKR_ATTRIBUTE_VALUE_INVALID; |
| cur_i = i; |
| goto fail_cleanup; |
| } |
| break; |
| default: |
| value_len = pTemplate[i].ulValueLen; |
| } |
| |
| cur_attr->oa_type = pTemplate[i].type; |
| cur_attr->oa_value_len = value_len; |
| cur_attr->oa_value = NULL; |
| |
| if ((pTemplate[i].pValue != NULL) && |
| (pTemplate[i].ulValueLen > 0)) { |
| ptr = malloc(pTemplate[i].ulValueLen); |
| if (ptr == NULL) { |
| rv = CKR_HOST_MEMORY; |
| cur_i = i; |
| goto fail_cleanup; |
| } else { |
| (void) memcpy(ptr, pTemplate[i].pValue, |
| pTemplate[i].ulValueLen); |
| cur_attr->oa_value = ptr; |
| } |
| } |
| |
| if ((is_token_obj != NULL) && |
| (pTemplate[i].type == CKA_TOKEN)) { |
| /* Get the CKA_TOKEN attribute value. */ |
| if (pTemplate[i].pValue == NULL) { |
| rv = CKR_ATTRIBUTE_VALUE_INVALID; |
| cur_i = i; |
| goto fail_cleanup; |
| } else { |
| *is_token_obj = |
| *(CK_BBOOL *)pTemplate[i].pValue; |
| } |
| } |
| |
| cur_attr++; |
| } |
| |
| *obj_attrs = (char *)attrs; |
| return (CKR_OK); |
| |
| fail_cleanup: |
| cur_attr = attrs; |
| for (i = 0; i < cur_i; i++) { |
| if (cur_attr->oa_value != NULL) { |
| (void) free(cur_attr->oa_value); |
| } |
| cur_attr++; |
| } |
| |
| (void) free(attrs); |
| return (rv); |
| } |
| |
| |
| /* |
| * Copy the attribute values from obj_attrs to pTemplate. |
| * The obj_attrs is an image of the Template and is expected to have the |
| * same attributes in the same order and each one of the attribute pValue |
| * in obj_attr has enough space allocated for the corresponding valueLen |
| * in pTemplate. |
| */ |
| CK_RV |
| get_object_attributes(CK_ATTRIBUTE_PTR pTemplate, CK_ULONG ulCount, |
| caddr_t obj_attrs) |
| { |
| crypto_object_attribute_t *cur_attr; |
| CK_RV rv = CKR_OK; |
| int i; |
| |
| /* LINTED */ |
| cur_attr = (crypto_object_attribute_t *)obj_attrs; |
| for (i = 0; i < ulCount; i++) { |
| if (pTemplate[i].type != cur_attr->oa_type) { |
| /* The attribute type doesn't match, this is bad. */ |
| rv = CKR_FUNCTION_FAILED; |
| return (rv); |
| } |
| |
| pTemplate[i].ulValueLen = cur_attr->oa_value_len; |
| |
| if ((pTemplate[i].pValue != NULL) && |
| ((CK_LONG)pTemplate[i].ulValueLen != -1)) { |
| (void) memcpy(pTemplate[i].pValue, cur_attr->oa_value, |
| pTemplate[i].ulValueLen); |
| } |
| cur_attr++; |
| } |
| |
| return (rv); |
| } |
| |
| /* |
| * Free the attribute storage in a crypto_object_attribute_t structure. |
| */ |
| void |
| free_object_attributes(caddr_t obj_attrs, CK_ULONG ulCount) |
| { |
| crypto_object_attribute_t *cur_attr; |
| int i; |
| |
| if ((ulCount == 0) || (obj_attrs == NULL)) { |
| return; |
| } |
| |
| /* LINTED */ |
| cur_attr = (crypto_object_attribute_t *)obj_attrs; |
| for (i = 0; i < ulCount; i++) { |
| /* XXX check that oa_value > 0 */ |
| if (cur_attr->oa_value != NULL) { |
| free(cur_attr->oa_value); |
| } |
| cur_attr++; |
| } |
| |
| free(obj_attrs); |
| } |
| |
| /* |
| * This function is called by process_found_objects(). It will check the |
| * CKA_PRIVATE and CKA_TOKEN attributes for the kernel object "oid", then |
| * initialize all the necessary fields in the object wrapper "objp". |
| */ |
| static CK_RV |
| create_new_tobj_in_lib(kernel_slot_t *pslot, kernel_session_t *sp, |
| kernel_object_t *objp, crypto_object_id_t oid) |
| { |
| CK_RV rv = CKR_OK; |
| crypto_object_get_attribute_value_t obj_ga; |
| boolean_t is_pri_obj; |
| boolean_t is_token_obj; |
| CK_BBOOL pri_value, token_value; |
| CK_ATTRIBUTE pTemplate[2]; |
| int r; |
| |
| /* |
| * Make a CRYPTO_OBJECT_GET_ATTRIBUTE_VALUE ioctl call to get this |
| * kernel object's attribute values for CKA_PRIVATE and CKA_TOKEN. |
| */ |
| obj_ga.og_session = sp->k_session; |
| obj_ga.og_handle = oid; |
| obj_ga.og_count = 2; |
| |
| pTemplate[0].type = CKA_PRIVATE; |
| pTemplate[0].pValue = &pri_value; |
| pTemplate[0].ulValueLen = sizeof (pri_value); |
| pTemplate[1].type = CKA_TOKEN; |
| pTemplate[1].pValue = &token_value; |
| pTemplate[1].ulValueLen = sizeof (token_value); |
| rv = process_object_attributes(pTemplate, 2, &obj_ga.og_attributes, |
| NULL); |
| if (rv != CKR_OK) { |
| return (rv); |
| } |
| |
| while ((r = ioctl(kernel_fd, CRYPTO_OBJECT_GET_ATTRIBUTE_VALUE, |
| &obj_ga)) < 0) { |
| if (errno != EINTR) |
| break; |
| } |
| if (r < 0) { |
| rv = CKR_FUNCTION_FAILED; |
| } else { |
| rv = crypto2pkcs11_error_number(obj_ga.og_return_value); |
| } |
| |
| if (rv == CKR_OK) { |
| rv = get_object_attributes(pTemplate, 2, obj_ga.og_attributes); |
| if (rv == CKR_OK) { |
| is_pri_obj = *(CK_BBOOL *)pTemplate[0].pValue; |
| is_token_obj = *(CK_BBOOL *)pTemplate[1].pValue; |
| } |
| } |
| |
| free_object_attributes(obj_ga.og_attributes, 2); |
| if (rv != CKR_OK) { |
| return (rv); |
| } |
| |
| /* Make sure it is a token object. */ |
| if (!is_token_obj) { |
| rv = CKR_ATTRIBUTE_VALUE_INVALID; |
| return (rv); |
| } |
| |
| /* If it is a private object, make sure the user has logged in. */ |
| if (is_pri_obj && (pslot->sl_state != CKU_USER)) { |
| rv = CKR_ATTRIBUTE_VALUE_INVALID; |
| return (rv); |
| } |
| |
| objp->is_lib_obj = B_FALSE; |
| objp->k_handle = oid; |
| objp->bool_attr_mask |= TOKEN_BOOL_ON; |
| if (is_pri_obj) { |
| objp->bool_attr_mask |= PRIVATE_BOOL_ON; |
| } else { |
| objp->bool_attr_mask &= ~PRIVATE_BOOL_ON; |
| } |
| |
| (void) pthread_mutex_init(&objp->object_mutex, NULL); |
| objp->magic_marker = KERNELTOKEN_OBJECT_MAGIC; |
| objp->session_handle = (CK_SESSION_HANDLE) sp; |
| |
| return (CKR_OK); |
| } |
| |
| /* |
| * This function processes the kernel object handles returned from the |
| * CRYPTO_OBJECT_FIND_UPDATE ioctl and returns an object handle list |
| * and the number of object handles to the caller - C_FindObjects(). |
| * The caller acquires the slot lock and the session lock. |
| */ |
| CK_RV |
| process_found_objects(kernel_session_t *cur_sp, CK_OBJECT_HANDLE *obj_found, |
| CK_ULONG *found_obj_count, crypto_object_find_update_t obj_fu) |
| { |
| CK_RV rv = CKR_OK; |
| crypto_object_id_t *oid_p; |
| kernel_slot_t *pslot; |
| kernel_object_t *objp; |
| kernel_object_t *objp1; |
| kernel_object_t *new_tobj_list = NULL; |
| kernel_session_t *sp; |
| CK_ULONG num_obj_found = 0; |
| boolean_t is_in_lib; |
| int i; |
| |
| if (obj_fu.fu_count == 0) { |
| *found_obj_count = 0; |
| return (CKR_OK); |
| } |
| |
| pslot = slot_table[cur_sp->ses_slotid]; |
| |
| /* LINTED */ |
| oid_p = (crypto_object_id_t *)obj_fu.fu_handles; |
| for (i = 0; i < obj_fu.fu_count; i++) { |
| is_in_lib = B_FALSE; |
| /* |
| * Check if this oid has an object wrapper in the library |
| * already. First, search the slot's token object list. |
| */ |
| objp = pslot->sl_tobj_list; |
| while (!is_in_lib && objp) { |
| if (objp->k_handle == *oid_p) { |
| is_in_lib = B_TRUE; |
| } else { |
| objp = objp->next; |
| } |
| } |
| |
| /* |
| * If it is not in the slot's token object list, |
| * search it in all the sessions. |
| */ |
| if (!is_in_lib) { |
| sp = pslot->sl_sess_list; |
| while (!is_in_lib && sp) { |
| objp = sp->object_list; |
| while (!is_in_lib && objp) { |
| if (objp->k_handle == *oid_p) { |
| is_in_lib = B_TRUE; |
| } else { |
| objp = objp->next; |
| } |
| } |
| sp = sp->next; |
| } |
| } |
| |
| /* |
| * If this object is in the library already, add its object |
| * wrapper to the returned find object list. |
| */ |
| if (is_in_lib) { |
| obj_found[num_obj_found++] = (CK_OBJECT_HANDLE)objp; |
| } |
| |
| /* |
| * If we still do not find it in the library. This object |
| * must be a token object pre-existed in the HW provider. |
| * We need to create an object wrapper for it in the library. |
| */ |
| if (!is_in_lib) { |
| objp1 = calloc(1, sizeof (kernel_object_t)); |
| if (objp1 == NULL) { |
| rv = CKR_HOST_MEMORY; |
| goto failed_exit; |
| } |
| rv = create_new_tobj_in_lib(pslot, cur_sp, objp1, |
| *oid_p); |
| |
| if (rv == CKR_OK) { |
| /* Save the new object to the new_tobj_list. */ |
| if (new_tobj_list == NULL) { |
| new_tobj_list = objp1; |
| objp1->next = NULL; |
| objp1->prev = NULL; |
| } else { |
| new_tobj_list->prev = objp1; |
| objp1->next = new_tobj_list; |
| objp1->prev = NULL; |
| new_tobj_list = objp1; |
| } |
| } else { |
| /* |
| * If create_new_tobj_in_lib() doesn't fail |
| * with CKR_HOST_MEMORY, the failure should be |
| * caused by the attributes' checking. We will |
| * just ignore this object and continue on. |
| */ |
| free(objp1); |
| if (rv == CKR_HOST_MEMORY) { |
| goto failed_exit; |
| } |
| } |
| } |
| |
| /* Process next one */ |
| oid_p++; |
| } |
| |
| /* |
| * Add the newly created token object wrappers to the found object |
| * list and to the slot's token object list. |
| */ |
| if (new_tobj_list != NULL) { |
| /* Add to the obj_found array. */ |
| objp = new_tobj_list; |
| while (objp) { |
| obj_found[num_obj_found++] = (CK_OBJECT_HANDLE)objp; |
| if (objp->next == NULL) { |
| break; |
| } |
| objp = objp->next; |
| } |
| |
| /* Add to the beginning of the slot's token object list. */ |
| if (pslot->sl_tobj_list != NULL) { |
| objp->next = pslot->sl_tobj_list; |
| pslot->sl_tobj_list->prev = objp; |
| } |
| pslot->sl_tobj_list = new_tobj_list; |
| } |
| |
| *found_obj_count = num_obj_found; |
| return (CKR_OK); |
| |
| failed_exit: |
| |
| /* Free the newly created token object wrappers. */ |
| objp = new_tobj_list; |
| while (objp) { |
| objp1 = objp->next; |
| (void) pthread_mutex_destroy(&objp->object_mutex); |
| free(objp); |
| objp = objp1; |
| } |
| |
| return (rv); |
| } |
| |
| |
| /* |
| * Get the value of the CKA_PRIVATE attribute for the object just returned |
| * from the HW provider. This function will be called by any function |
| * that creates a new object, because the CKA_PRIVATE value of an object is |
| * token specific. The CKA_PRIVATE attribute value of the new object will be |
| * stored in the object structure in the library, which will be used later at |
| * C_Logout to clean up all private objects. |
| */ |
| CK_RV |
| get_cka_private_value(kernel_session_t *sp, crypto_object_id_t oid, |
| CK_BBOOL *is_pri_obj) |
| { |
| CK_RV rv = CKR_OK; |
| crypto_object_get_attribute_value_t obj_ga; |
| crypto_object_attribute_t obj_attr; |
| CK_BBOOL pri_value; |
| int r; |
| |
| obj_ga.og_session = sp->k_session; |
| obj_ga.og_handle = oid; |
| obj_ga.og_count = 1; |
| |
| obj_attr.oa_type = CKA_PRIVATE; |
| obj_attr.oa_value = (char *)&pri_value; |
| obj_attr.oa_value_len = sizeof (CK_BBOOL); |
| obj_ga.og_attributes = (char *)&obj_attr; |
| |
| while ((r = ioctl(kernel_fd, CRYPTO_OBJECT_GET_ATTRIBUTE_VALUE, |
| &obj_ga)) < 0) { |
| if (errno != EINTR) |
| break; |
| } |
| if (r < 0) { |
| rv = CKR_FUNCTION_FAILED; |
| } else { |
| rv = crypto2pkcs11_error_number(obj_ga.og_return_value); |
| } |
| |
| if (rv == CKR_OK) { |
| *is_pri_obj = *(CK_BBOOL *)obj_attr.oa_value; |
| } |
| |
| return (rv); |
| } |
| |
| |
| CK_RV |
| get_mechanism_info(kernel_slot_t *pslot, CK_MECHANISM_TYPE type, |
| CK_MECHANISM_INFO_PTR pInfo, uint32_t *k_mi_flags) |
| { |
| crypto_get_provider_mechanism_info_t mechanism_info; |
| const char *string; |
| CK_FLAGS flags, mi_flags; |
| CK_RV rv; |
| int r; |
| char buf[11]; /* Num chars for representing ulong in ASCII */ |
| |
| if (type >= CKM_VENDOR_DEFINED) { |
| /* allocate/build a string containing the mechanism number */ |
| (void) snprintf(buf, sizeof (buf), "%#lx", type); |
| string = buf; |
| } else { |
| string = pkcs11_mech2str(type); |
| } |
| |
| if (string == NULL) |
| return (CKR_MECHANISM_INVALID); |
| |
| (void) strcpy(mechanism_info.mi_mechanism_name, string); |
| mechanism_info.mi_provider_id = pslot->sl_provider_id; |
| |
| while ((r = ioctl(kernel_fd, CRYPTO_GET_PROVIDER_MECHANISM_INFO, |
| &mechanism_info)) < 0) { |
| if (errno != EINTR) |
| break; |
| } |
| if (r < 0) { |
| rv = CKR_FUNCTION_FAILED; |
| } else { |
| rv = crypto2pkcs11_error_number( |
| mechanism_info.mi_return_value); |
| } |
| |
| if (rv != CKR_OK) { |
| return (rv); |
| } |
| |
| /* |
| * Atomic flags are not part of PKCS#11 so we filter |
| * them out here. |
| */ |
| mi_flags = mechanism_info.mi_flags; |
| mi_flags &= ~(CRYPTO_FG_DIGEST_ATOMIC | CRYPTO_FG_ENCRYPT_ATOMIC | |
| CRYPTO_FG_DECRYPT_ATOMIC | CRYPTO_FG_MAC_ATOMIC | |
| CRYPTO_FG_SIGN_ATOMIC | CRYPTO_FG_VERIFY_ATOMIC | |
| CRYPTO_FG_SIGN_RECOVER_ATOMIC | |
| CRYPTO_FG_VERIFY_RECOVER_ATOMIC | |
| CRYPTO_FG_ENCRYPT_MAC_ATOMIC | |
| CRYPTO_FG_MAC_DECRYPT_ATOMIC); |
| |
| if (mi_flags == 0) { |
| return (CKR_MECHANISM_INVALID); |
| } |
| |
| if (rv == CKR_OK) { |
| /* set the value of k_mi_flags first */ |
| *k_mi_flags = mi_flags; |
| |
| /* convert KEF flags into pkcs11 flags */ |
| flags = CKF_HW; |
| if (mi_flags & CRYPTO_FG_ENCRYPT) |
| flags |= CKF_ENCRYPT; |
| if (mi_flags & CRYPTO_FG_DECRYPT) { |
| flags |= CKF_DECRYPT; |
| /* |
| * Since we'll be emulating C_UnwrapKey() for some |
| * cases, we can go ahead and claim CKF_UNWRAP |
| */ |
| flags |= CKF_UNWRAP; |
| } |
| if (mi_flags & CRYPTO_FG_DIGEST) |
| flags |= CKF_DIGEST; |
| if (mi_flags & CRYPTO_FG_SIGN) |
| flags |= CKF_SIGN; |
| if (mi_flags & CRYPTO_FG_SIGN_RECOVER) |
| flags |= CKF_SIGN_RECOVER; |
| if (mi_flags & CRYPTO_FG_VERIFY) |
| flags |= CKF_VERIFY; |
| if (mi_flags & CRYPTO_FG_VERIFY_RECOVER) |
| flags |= CKF_VERIFY_RECOVER; |
| if (mi_flags & CRYPTO_FG_GENERATE) |
| flags |= CKF_GENERATE; |
| if (mi_flags & CRYPTO_FG_GENERATE_KEY_PAIR) |
| flags |= CKF_GENERATE_KEY_PAIR; |
| if (mi_flags & CRYPTO_FG_WRAP) |
| flags |= CKF_WRAP; |
| if (mi_flags & CRYPTO_FG_UNWRAP) |
| flags |= CKF_UNWRAP; |
| if (mi_flags & CRYPTO_FG_DERIVE) |
| flags |= CKF_DERIVE; |
| |
| pInfo->ulMinKeySize = mechanism_info.mi_min_key_size; |
| pInfo->ulMaxKeySize = mechanism_info.mi_max_key_size; |
| pInfo->flags = flags; |
| |
| } |
| |
| return (rv); |
| } |