/* * 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 2008 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ #include #include #include #include #include #include #include #include #include #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 CRYPTO_LAST_ERROR (CRYPTO_WEAK_KEY + 1) /* * In order to fit everything on one line, the 'CRYPTO_' prefix * has been dropped from the KCF #defines, e.g. * CRYPTO_SUCCESS becomes SUCCESS. */ static CK_RV error_number_table[CRYPTO_LAST_ERROR] = { CKR_OK, /* SUCCESS */ CKR_CANCEL, /* CANCEL */ CKR_HOST_MEMORY, /* HOST_MEMORY */ CKR_GENERAL_ERROR, /* GENERAL_ERROR */ CKR_FUNCTION_FAILED, /* FAILED */ CKR_ARGUMENTS_BAD, /* ARGUMENTS_BAD */ CKR_ATTRIBUTE_READ_ONLY, /* ATTRIBUTE_READ_ONLY */ CKR_ATTRIBUTE_SENSITIVE, /* ATTRIBUTE_SENSITIVE */ CKR_ATTRIBUTE_TYPE_INVALID, /* ATTRIBUTE_TYPE_INVALID */ CKR_ATTRIBUTE_VALUE_INVALID, /* ATTRIBUTE_VALUE_INVALID */ CKR_FUNCTION_FAILED, /* CANCELED */ CKR_DATA_INVALID, /* DATA_INVALID */ CKR_DATA_LEN_RANGE, /* DATA_LEN_RANGE */ CKR_DEVICE_ERROR, /* DEVICE_ERROR */ CKR_DEVICE_MEMORY, /* DEVICE_MEMORY */ CKR_DEVICE_REMOVED, /* DEVICE_REMOVED */ CKR_ENCRYPTED_DATA_INVALID, /* ENCRYPTED_DATA_INVALID */ CKR_ENCRYPTED_DATA_LEN_RANGE, /* ENCRYPTED_DATA_LEN_RANGE */ CKR_KEY_HANDLE_INVALID, /* KEY_HANDLE_INVALID */ CKR_KEY_SIZE_RANGE, /* KEY_SIZE_RANGE */ CKR_KEY_TYPE_INCONSISTENT, /* KEY_TYPE_INCONSISTENT */ CKR_KEY_NOT_NEEDED, /* KEY_NOT_NEEDED */ CKR_KEY_CHANGED, /* KEY_CHANGED */ CKR_KEY_NEEDED, /* KEY_NEEDED */ CKR_KEY_INDIGESTIBLE, /* KEY_INDIGESTIBLE */ CKR_KEY_FUNCTION_NOT_PERMITTED, /* KEY_FUNCTION_NOT_PERMITTED */ CKR_KEY_NOT_WRAPPABLE, /* KEY_NOT_WRAPPABLE */ CKR_KEY_UNEXTRACTABLE, /* KEY_UNEXTRACTABLE */ CKR_MECHANISM_INVALID, /* MECHANISM_INVALID */ CKR_MECHANISM_PARAM_INVALID, /* MECHANISM_PARAM_INVALID */ CKR_OBJECT_HANDLE_INVALID, /* OBJECT_HANDLE_INVALID */ CKR_OPERATION_ACTIVE, /* OPERATION_ACTIVE */ CKR_OPERATION_NOT_INITIALIZED, /* OPERATION_NOT_INITIALIZED */ CKR_PIN_INCORRECT, /* PIN_INCORRECT */ CKR_PIN_INVALID, /* PIN_INVALID */ CKR_PIN_LEN_RANGE, /* PIN_LEN_RANGE */ CKR_PIN_EXPIRED, /* PIN_EXPIRED */ CKR_PIN_LOCKED, /* PIN_LOCKED */ CKR_SESSION_CLOSED, /* SESSION_CLOSED */ CKR_SESSION_COUNT, /* SESSION_COUNT */ CKR_SESSION_HANDLE_INVALID, /* SESSION_HANDLE_INVALID */ CKR_SESSION_READ_ONLY, /* SESSION_READ_ONLY */ CKR_SESSION_EXISTS, /* SESSION_EXISTS */ CKR_SESSION_READ_ONLY_EXISTS, /* SESSION_READ_ONLY_EXISTS */ CKR_SESSION_READ_WRITE_SO_EXISTS, /* SESSION_READ_WRITE_SO_EXISTS */ CKR_SIGNATURE_INVALID, /* SIGNATURE_INVALID */ CKR_SIGNATURE_LEN_RANGE, /* SIGNATURE_LEN_RANGE */ CKR_TEMPLATE_INCOMPLETE, /* TEMPLATE_INCOMPLETE */ CKR_TEMPLATE_INCONSISTENT, /* TEMPLATE_INCONSISTENT */ CKR_UNWRAPPING_KEY_HANDLE_INVALID, /* UNWRAPPING_KEY_HANDLE_INVALID */ CKR_UNWRAPPING_KEY_SIZE_RANGE, /* UNWRAPPING_KEY_SIZE_RANGE */ CKR_UNWRAPPING_KEY_TYPE_INCONSISTENT, /* UNWRAPPING_KEY_TYPE_INCONSISTENT */ CKR_USER_ALREADY_LOGGED_IN, /* USER_ALREADY_LOGGED_IN */ CKR_USER_NOT_LOGGED_IN, /* USER_NOT_LOGGED_IN */ CKR_USER_PIN_NOT_INITIALIZED, /* USER_PIN_NOT_INITIALIZED */ CKR_USER_TYPE_INVALID, /* USER_TYPE_INVALID */ CKR_USER_ANOTHER_ALREADY_LOGGED_IN, /* USER_ANOTHER_ALREADY_LOGGED_IN */ CKR_USER_TOO_MANY_TYPES, /* USER_TOO_MANY_TYPES */ CKR_WRAPPED_KEY_INVALID, /* WRAPPED_KEY_INVALID */ CKR_WRAPPED_KEY_LEN_RANGE, /* WRAPPED_KEY_LEN_RANGE */ CKR_WRAPPING_KEY_HANDLE_INVALID, /* WRAPPING_KEY_HANDLE_INVALID */ CKR_WRAPPING_KEY_SIZE_RANGE, /* WRAPPING_KEY_SIZE_RANGE */ CKR_WRAPPING_KEY_TYPE_INCONSISTENT, /* WRAPPING_KEY_TYPE_INCONSISTENT */ CKR_RANDOM_SEED_NOT_SUPPORTED, /* RANDOM_SEED_NOT_SUPPORTED */ CKR_RANDOM_NO_RNG, /* RANDOM_NO_RNG */ CKR_DOMAIN_PARAMS_INVALID, /* DOMAIN_PARAMS_INVALID */ CKR_BUFFER_TOO_SMALL, /* BUFFER_TOO_SMALL */ CKR_INFORMATION_SENSITIVE, /* INFORMATION_SENSITIVE */ CKR_FUNCTION_NOT_SUPPORTED, /* NOT_SUPPORTED */ CKR_GENERAL_ERROR, /* QUEUED */ CKR_GENERAL_ERROR, /* BUFFER_TOO_BIG */ CKR_OPERATION_NOT_INITIALIZED, /* INVALID_CONTEXT */ CKR_GENERAL_ERROR, /* INVALID_MAC */ CKR_GENERAL_ERROR, /* MECH_NOT_SUPPORTED */ CKR_GENERAL_ERROR, /* INCONSISTENT_ATTRIBUTE */ CKR_GENERAL_ERROR, /* NO_PERMISSION */ CKR_SLOT_ID_INVALID, /* INVALID_PROVIDER_ID */ CKR_GENERAL_ERROR, /* VERSION_MISMATCH */ CKR_GENERAL_ERROR, /* BUSY */ CKR_GENERAL_ERROR, /* UNKNOWN_PROVIDER */ CKR_GENERAL_ERROR, /* MODVERIFICATION_FAILED */ CKR_GENERAL_ERROR, /* OLD_CTX_TEMPLATE */ CKR_GENERAL_ERROR, /* WEAK_KEY */ }; /* * Map KCF error codes into PKCS11 error codes. */ CK_RV crypto2pkcs11_error_number(uint_t n) { if (n > CRYPTO_LAST_ERROR) return (CKR_GENERAL_ERROR); return (error_number_table[n]); } #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. */ static 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++) { if (key->ck_attrs[i].oa_value != NULL) { bzero(key->ck_attrs[i].oa_value, key->ck_attrs[i].oa_value_len); free(key->ck_attrs[i].oa_value); } } 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); }