/* * 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) 2004, 2010, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2018, Joyent, Inc. */ #include #include #include #include #include #include #include #include "metaGlobal.h" /* Size of the template for creating key used for wrap/unwrap */ #define WRAP_KEY_TEMPLATE_SIZE 7 /* * Information necessary to create keys for C_WrapKey/C_UnwrapKey */ typedef struct _wrap_info { CK_OBJECT_CLASS class; /* class of the key for wrap/unwrap */ CK_KEY_TYPE key_type; /* key type of key for wrap/unwrap */ CK_ULONG key_length; /* length of key */ CK_MECHANISM_TYPE mech_type; /* mech used for wrap/unwrap */ CK_ULONG iv_length; /* length of iv for mech */ boolean_t src_supports; boolean_t dst_supports; } wrap_info_t; extern pthread_rwlock_t meta_sessionlist_lock; extern meta_session_t *meta_sessionlist_head; static wrap_info_t common_wrap_info[] = { {CKO_SECRET_KEY, CKK_AES, 16, CKM_AES_CBC_PAD, 16, B_FALSE, B_FALSE}, {CKO_SECRET_KEY, CKK_DES3, 24, CKM_DES3_CBC_PAD, 8, B_FALSE, B_FALSE}, {CKO_SECRET_KEY, CKK_DES, 8, CKM_DES_CBC_PAD, 8, B_FALSE, B_FALSE}, }; static unsigned int num_common_wrap_info = sizeof (common_wrap_info) / sizeof (wrap_info_t); static wrap_info_t special_wrap_info[] = { {CKO_SECRET_KEY, CKK_SKIPJACK, 12, CKM_SKIPJACK_WRAP, 0, B_FALSE, B_FALSE}, {CKO_SECRET_KEY, CKK_BATON, 40, CKM_BATON_WRAP, 0, B_FALSE, B_FALSE}, {CKO_SECRET_KEY, CKK_JUNIPER, 40, CKM_JUNIPER_WRAP, 0, B_FALSE, B_FALSE}, }; static unsigned int num_special_wrap_info = sizeof (special_wrap_info) / sizeof (wrap_info_t); static wrap_info_t rsa_wrap_info[] = { {CKO_PUBLIC_KEY, CKK_RSA, 0, CKM_RSA_PKCS, 0, B_FALSE, B_FALSE}, {CKO_PUBLIC_KEY, CKK_RSA, 0, CKM_RSA_X_509, 0, B_FALSE, B_FALSE}, }; static unsigned int num_rsa_wrap_info = sizeof (rsa_wrap_info) / sizeof (wrap_info_t); static pthread_rwlock_t meta_objectclose_lock; static pthread_rwlock_t tokenobject_list_lock; static meta_object_t *tokenobject_list_head; CK_BBOOL falsevalue = FALSE; CK_BBOOL truevalue = TRUE; /* * Public and private exponent, and Module value for * creating the RSA public/private key. * */ static CK_BYTE PubExpo[3] = {0x01, 0x00, 0x01}; CK_BYTE PriExpo[128] = { 0x8e, 0xc9, 0x70, 0x57, 0x6b, 0xcd, 0xfb, 0xa9, 0x19, 0xad, 0xcd, 0x91, 0x69, 0xd5, 0x52, 0xec, 0x72, 0x1e, 0x45, 0x15, 0x06, 0xdc, 0x65, 0x2d, 0x98, 0xc4, 0xce, 0x33, 0x54, 0x15, 0x70, 0x8d, 0xfa, 0x65, 0xea, 0x53, 0x44, 0xf3, 0x3e, 0x3f, 0xb4, 0x4c, 0x60, 0xd5, 0x01, 0x2d, 0xa4, 0x12, 0x99, 0xbf, 0x3f, 0x0b, 0xcd, 0xbb, 0x24, 0x10, 0x60, 0x30, 0x5e, 0x58, 0xf8, 0x59, 0xaa, 0xd1, 0x63, 0x3b, 0xbc, 0xcb, 0x94, 0x58, 0x38, 0x24, 0xfc, 0x65, 0x25, 0xc5, 0xa6, 0x51, 0xa2, 0x2e, 0xf1, 0x5e, 0xf5, 0xc1, 0xf5, 0x46, 0xf7, 0xbd, 0xc7, 0x62, 0xa8, 0xe2, 0x27, 0xd6, 0x94, 0x5b, 0xd3, 0xa2, 0xb5, 0x76, 0x42, 0x67, 0x6b, 0x86, 0x91, 0x97, 0x4d, 0x07, 0x92, 0x00, 0x4a, 0xdf, 0x0b, 0x65, 0x64, 0x05, 0x03, 0x48, 0x27, 0xeb, 0xce, 0x9a, 0x49, 0x7f, 0x3e, 0x10, 0xe0, 0x01}; static CK_BYTE Modulus[128] = { 0x94, 0x32, 0xb9, 0x12, 0x1d, 0x68, 0x2c, 0xda, 0x2b, 0xe0, 0xe4, 0x97, 0x1b, 0x4d, 0xdc, 0x43, 0xdf, 0x38, 0x6e, 0x7b, 0x9f, 0x07, 0x58, 0xae, 0x9d, 0x82, 0x1e, 0xc7, 0xbc, 0x92, 0xbf, 0xd3, 0xce, 0x00, 0xbb, 0x91, 0xc9, 0x79, 0x06, 0x03, 0x1f, 0xbc, 0x9f, 0x94, 0x75, 0x29, 0x5f, 0xd7, 0xc5, 0xf3, 0x73, 0x8a, 0xa4, 0x35, 0x43, 0x7a, 0x00, 0x32, 0x97, 0x3e, 0x86, 0xef, 0x70, 0x6f, 0x18, 0x56, 0x15, 0xaa, 0x6a, 0x87, 0xe7, 0x8d, 0x7d, 0xdd, 0x1f, 0xa4, 0xe4, 0x31, 0xd4, 0x7a, 0x8c, 0x0e, 0x20, 0xd2, 0x23, 0xf5, 0x57, 0x3c, 0x1b, 0xa8, 0x44, 0xa4, 0x57, 0x8f, 0x33, 0x52, 0xad, 0x83, 0xae, 0x4a, 0x97, 0xa6, 0x1e, 0xa6, 0x2b, 0xfa, 0xea, 0xeb, 0x6e, 0x71, 0xb8, 0xb6, 0x0a, 0x36, 0xed, 0x83, 0xce, 0xb0, 0xdf, 0xc1, 0xd4, 0x3a, 0xe9, 0x99, 0x6f, 0xf3, 0x96, 0xb7}; static CK_RV meta_clone_template_setup(meta_object_t *object, const generic_attr_t *attributes, size_t num_attributes); /* * meta_objectManager_initialize * * Called from meta_Initialize. Initializes all the variables used * by the object manager. */ CK_RV meta_objectManager_initialize() { if (pthread_rwlock_init(&meta_objectclose_lock, NULL) != 0) { return (CKR_FUNCTION_FAILED); } if (pthread_rwlock_init(&tokenobject_list_lock, NULL) != 0) { (void) pthread_rwlock_destroy(&meta_objectclose_lock); return (CKR_FUNCTION_FAILED); } tokenobject_list_head = NULL; return (CKR_OK); } void meta_objectManager_finalize() { /* * If there are still any token object in the list, need to * deactivate all of them. */ (void) meta_token_object_deactivate(ALL_TOKEN); (void) pthread_rwlock_destroy(&meta_objectclose_lock); (void) pthread_rwlock_destroy(&tokenobject_list_lock); } /* * meta_handle2object * * Convert a CK_OBJECT_HANDLE to the corresponding metaobject. If * successful, a reader-lock on the object will be held to indicate * that it's in use. Call OBJRELEASE() when finished. * */ CK_RV meta_handle2object(CK_OBJECT_HANDLE hObject, meta_object_t **object) { meta_object_t *tmp_object = (meta_object_t *)(hObject); /* Check for bad args (eg CK_INVALID_HANDLE, which is 0/NULL). */ if (tmp_object == NULL) { *object = NULL; return (CKR_OBJECT_HANDLE_INVALID); } /* Lock to ensure the magic-check + read-lock is atomic. */ (void) pthread_rwlock_rdlock(&meta_objectclose_lock); if (tmp_object->magic_marker != METASLOT_OBJECT_MAGIC) { (void) pthread_rwlock_unlock(&meta_objectclose_lock); *object = NULL; return (CKR_OBJECT_HANDLE_INVALID); } (void) pthread_rwlock_rdlock(&tmp_object->object_lock); (void) pthread_rwlock_unlock(&meta_objectclose_lock); *object = tmp_object; return (CKR_OK); } /* * meta_object_alloc * * Creates a new metaobject, but does not yet add it to the object list. * Once the caller has finished initializing the object (by setting * object attributes), meta_object_add should be called. This two-step * process prevents others from seeing the object until fully intitialized. * */ CK_RV meta_object_alloc(meta_session_t *session, meta_object_t **object) { meta_object_t *new_object; CK_ULONG num_slots; /* Allocate memory for the object. */ new_object = calloc(1, sizeof (meta_object_t)); if (new_object == NULL) return (CKR_HOST_MEMORY); num_slots = meta_slotManager_get_slotcount(); new_object->clones = calloc(num_slots, sizeof (slot_object_t *)); if (new_object->clones == NULL) { free(new_object); return (CKR_HOST_MEMORY); } new_object->tried_create_clone = calloc(num_slots, sizeof (boolean_t)); if (new_object->tried_create_clone == NULL) { free(new_object->clones); free(new_object); return (CKR_HOST_MEMORY); } /* Initialize the object fields. */ new_object->magic_marker = METASLOT_OBJECT_MAGIC; (void) pthread_rwlock_init(&new_object->object_lock, NULL); (void) pthread_rwlock_init(&new_object->attribute_lock, NULL); (void) pthread_mutex_init(&new_object->clone_create_lock, NULL); (void) pthread_mutex_init(&new_object->isClosingObject_lock, NULL); new_object->creator_session = session; *object = new_object; return (CKR_OK); } /* * meta_object_get_attr * * Get attribute values to fill in attribute values * being kept in the metaslot object. The following 4 attributes * in the meta_object_t structure will be filled in: * isToken, isPrivate, isSensitive, isExtractable * * It's basically an easy way to do a C_GetAttributeValue. * So, the hSession argument is assumed * to be valid, and the pointer to meta_object_t is also assumed * to be valid. */ CK_RV meta_object_get_attr(slot_session_t *slot_session, CK_OBJECT_HANDLE hObject, meta_object_t *object) { CK_BBOOL is_sensitive = object->isSensitive; CK_BBOOL is_extractable = object->isExtractable; CK_BBOOL is_token = B_FALSE, is_private = B_FALSE; CK_KEY_TYPE keytype; CK_OBJECT_CLASS class; CK_ATTRIBUTE attrs[3]; CK_RV rv; CK_SESSION_HANDLE hSession = slot_session->hSession; CK_SLOT_ID fw_st_id = slot_session->fw_st_id; int count = 1; attrs[0].type = CKA_CLASS; attrs[0].pValue = &class; attrs[0].ulValueLen = sizeof (class); if (object->isFreeObject != FREE_ENABLED) { attrs[1].type = CKA_TOKEN; attrs[1].pValue = &is_token; attrs[1].ulValueLen = sizeof (is_token); count++; } /* * If this is a freeobject, we already know the Private value * and we don't want to overwrite it with the wrong value */ if (object->isFreeObject <= FREE_DISABLED) { attrs[count].type = CKA_PRIVATE; attrs[count].pValue = &is_private; attrs[count].ulValueLen = sizeof (is_private); count++; } else is_private = object->isPrivate; rv = FUNCLIST(fw_st_id)->C_GetAttributeValue(hSession, hObject, attrs, count); if (rv != CKR_OK) { return (rv); } count = 0; switch (class) { case CKO_PRIVATE_KEY: case CKO_SECRET_KEY: /* Only need to check these for private & secret keys */ attrs[0].type = CKA_EXTRACTABLE; attrs[0].pValue = &is_extractable; attrs[0].ulValueLen = sizeof (is_extractable); count = 1; /* * If this is a freeobject, we already know the Sensitive * value and we don't want to overwrite it with the wrong * value. */ if (object->isFreeObject <= FREE_DISABLED) { attrs[1].type = CKA_SENSITIVE; attrs[1].pValue = &is_sensitive; attrs[1].ulValueLen = sizeof (is_sensitive); count = 2; /* * We only need the key type if this is the first * time we've looked at the object */ if (object->isFreeObject == FREE_UNCHECKED) { attrs[2].type = CKA_KEY_TYPE; attrs[2].pValue = &keytype; attrs[2].ulValueLen = sizeof (keytype); count = 3; } } break; case CKO_PUBLIC_KEY: if (object->isFreeObject == FREE_UNCHECKED) { attrs[count].type = CKA_KEY_TYPE; attrs[count].pValue = &keytype; attrs[count].ulValueLen = sizeof (keytype); count++; } is_sensitive = CK_FALSE; is_extractable = CK_TRUE; break; default: object->isFreeObject = FREE_DISABLED; is_sensitive = CK_FALSE; is_extractable = CK_TRUE; }; if (count > 0) { rv = FUNCLIST(fw_st_id)->C_GetAttributeValue(hSession, hObject, attrs, count); if (rv != CKR_OK) { return (rv); } if (object->isFreeObject == FREE_UNCHECKED) { if (keytype == CKK_EC || keytype == CKK_RSA || keytype == CKK_DH) { if (metaslot_config.auto_key_migrate) { object->isFreeObject = FREE_DISABLED; object->isFreeToken = FREE_DISABLED; } object->isFreeObject = FREE_ENABLED; if (is_token) object->isFreeToken = FREE_ENABLED; } else object->isFreeObject = FREE_DISABLED; } } object->isToken = is_token; object->isPrivate = is_private; object->isSensitive = is_sensitive; object->isExtractable = is_extractable; return (CKR_OK); } /* * meta_object_activate * * Add a new metaobject to the list of objects. See also meta_object_create, * which would be called to create an object before it is added. */ void meta_object_activate(meta_object_t *new_object) { pthread_rwlock_t *list_lock; meta_object_t **list_head; /* * For session objects, we keep the list in the session that created * this object, because this object will be destroyed when that session * is closed. * * For token objects, the list is global (ie, not associated with any * particular session). */ if (new_object->isToken) { list_lock = &tokenobject_list_lock; list_head = &tokenobject_list_head; } else { list_lock = &new_object->creator_session->object_list_lock; list_head = &new_object->creator_session->object_list_head; } /* Add object to the list of objects. */ (void) pthread_rwlock_wrlock(list_lock); INSERT_INTO_LIST(*list_head, new_object); (void) pthread_rwlock_unlock(list_lock); } /* * meta_object_deactivate * * Removes the object from the list of valid meta objects. Note * that this function does not clean up any allocated * resources (memory, object clones, etc). Cleaning up of * allocated resources is done by calling the meta_object_dealloc() * */ CK_RV meta_object_deactivate(meta_object_t *object, boolean_t have_list_lock, boolean_t have_object_lock) { pthread_rwlock_t *list_lock; meta_object_t **list_head; if (!have_object_lock) { (void) pthread_rwlock_rdlock(&object->object_lock); } (void) pthread_mutex_lock(&object->isClosingObject_lock); if (object->isClosingObject) { /* Lost a delete race. */ (void) pthread_mutex_unlock(&object->isClosingObject_lock); OBJRELEASE(object); return (CKR_OBJECT_HANDLE_INVALID); } object->isClosingObject = B_TRUE; (void) pthread_mutex_unlock(&object->isClosingObject_lock); if (object->isToken || (object->isFreeToken == FREE_ENABLED)) { list_lock = &tokenobject_list_lock; list_head = &tokenobject_list_head; } else { list_lock = &object->creator_session->object_list_lock; list_head = &object->creator_session->object_list_head; } /* * Remove object from the object list. Once removed, it will not * be possible for another thread to begin using the object. */ (void) pthread_rwlock_wrlock(&meta_objectclose_lock); if (!have_list_lock) { (void) pthread_rwlock_wrlock(list_lock); } object->magic_marker = METASLOT_OBJECT_BADMAGIC; /* * Can't use the regular REMOVE_FROM_LIST() function because * that will miss the "error cleanup" situation where object is not yet * in the list (object->next == NULL && object->prev == NULL) */ if (*list_head == object) { /* Object is the first one in the list */ if (object->next) { *list_head = object->next; object->next->prev = NULL; } else { /* Object is the only one in the list */ *list_head = NULL; } } else if (object->next != NULL || object->prev != NULL) { if (object->next) { object->prev->next = object->next; object->next->prev = object->prev; } else { /* Object is the last one in the list */ object->prev->next = NULL; } } if (!have_list_lock) { (void) pthread_rwlock_unlock(list_lock); } (void) pthread_rwlock_unlock(&meta_objectclose_lock); /* * Wait for anyone already using object to finish, by obtaining * a writer-lock (need to release our reader-lock first). Once we * get the write lock, we can just release it and finish cleaning * up the object. */ (void) pthread_rwlock_unlock(&object->object_lock); /* rdlock */ (void) pthread_rwlock_wrlock(&object->object_lock); (void) pthread_rwlock_unlock(&object->object_lock); /* wrlock */ return (CKR_OK); } /* * meta_object_dealloc * * Performs final object cleanup, releasing any allocated memory and * destroying any clones on other slots. Caller is assumed to have * called meta_object_deactivate() before this function. * * Caller is assumed to have only reference to object, but should have * released any lock. * * If "nukeSourceObj" argument is true, we will actually delete the * object from the underlying slot. */ CK_RV meta_object_dealloc(meta_session_t *session, meta_object_t *object, boolean_t nukeSourceObj) { CK_RV rv, save_rv = CKR_OK; CK_ULONG slotnum, num_slots; CK_ULONG i; /* First, delete all the clones of this object on other slots. */ num_slots = meta_slotManager_get_slotcount(); for (slotnum = 0; slotnum < num_slots; slotnum++) { slot_session_t *obj_session; slot_object_t *clone; clone = object->clones[slotnum]; if (clone == NULL) continue; if (nukeSourceObj || (!object->isToken && !(object->isFreeToken == FREE_ENABLED && get_keystore_slotnum() == slotnum))) { rv = meta_get_slot_session(slotnum, &obj_session, (session == NULL) ? object->creator_session->session_flags : session->session_flags); if (rv == CKR_OK) { rv = FUNCLIST(obj_session->fw_st_id)->\ C_DestroyObject(obj_session->hSession, clone->hObject); meta_release_slot_session(obj_session); if ((rv != CKR_OK) && (save_rv == CKR_OK)) { save_rv = rv; } } } meta_slot_object_deactivate(clone); meta_slot_object_dealloc(clone); object->clones[slotnum] = NULL; } /* Now erase and delete any attributes in the metaobject. */ dealloc_attributes(object->attributes, object->num_attributes); free(object->clones); free(object->tried_create_clone); if (object->clone_template) { for (i = 0; i < object->clone_template_size; i++) { freezero((object->clone_template)[i].pValue, (object->clone_template)[i].ulValueLen); } free(object->clone_template); } /* Cleanup remaining object fields. */ (void) pthread_rwlock_destroy(&object->object_lock); (void) pthread_rwlock_destroy(&object->attribute_lock); (void) pthread_mutex_destroy(&object->isClosingObject_lock); (void) pthread_mutex_destroy(&object->clone_create_lock); meta_object_delay_free(object); return (save_rv); } /* * meta_slot_object_alloc */ CK_RV meta_slot_object_alloc(slot_object_t **object) { slot_object_t *new_object; new_object = calloc(1, sizeof (slot_object_t)); if (new_object == NULL) return (CKR_HOST_MEMORY); *object = new_object; return (CKR_OK); } /* * meta_slot_object_activate */ void meta_slot_object_activate(slot_object_t *object, slot_session_t *creator_session, boolean_t isToken) { object->creator_session = creator_session; if (isToken) { extern slot_data_t *slots; slot_data_t *slot; slot = &(slots[object->creator_session->slotnum]); (void) pthread_rwlock_wrlock(&slot->tokenobject_list_lock); INSERT_INTO_LIST(slot->tokenobject_list_head, object); (void) pthread_rwlock_unlock(&slot->tokenobject_list_lock); } else { slot_session_t *session = object->creator_session; /* Add to session's list of session objects. */ (void) pthread_rwlock_wrlock(&session->object_list_lock); INSERT_INTO_LIST(session->object_list_head, object); (void) pthread_rwlock_unlock(&session->object_list_lock); } /* * This set tells the slot object that we are in the token list, * but does not cause harm with the metaobject knowing the object * isn't a token, but a freetoken */ object->isToken = isToken; } /* * meta_slot_object_deactivate * * Remove the specified slot object from the appropriate object list. */ void meta_slot_object_deactivate(slot_object_t *object) { slot_object_t **list_head; pthread_rwlock_t *list_lock; if (object->isToken) { extern slot_data_t *slots; slot_data_t *slot; slot = &(slots[object->creator_session->slotnum]); list_head = &slot->tokenobject_list_head; list_lock = &slot->tokenobject_list_lock; } else { list_head = &object->creator_session->object_list_head; list_lock = &object->creator_session->object_list_lock; } (void) pthread_rwlock_wrlock(list_lock); REMOVE_FROM_LIST(*list_head, object); (void) pthread_rwlock_unlock(list_lock); } /* * meta_slot_object_dealloc */ void meta_slot_object_dealloc(slot_object_t *object) { /* Not much cleanup for slot objects, unlike meta objects... */ free(object); } /* * meta_object_copyin * * When a key is generated/derived/unwrapped, the attribute values * created by the token are not immediately read into our copy of the * attributes. We defer this work until we actually need to know. */ CK_RV meta_object_copyin(meta_object_t *object) { CK_RV rv = CKR_OK; slot_session_t *session = NULL; CK_ATTRIBUTE *attrs = NULL, *attrs_with_val = NULL; slot_object_t *slot_object = NULL; CK_ULONG num_attrs = 0, i, num_attrs_with_val; CK_SESSION_HANDLE hSession; CK_SLOT_ID fw_st_id; /* Make sure no one else is looking at attributes. */ (void) pthread_rwlock_wrlock(&object->attribute_lock); /* Did we just lose a copyin race with another thread */ if (object->attributes != NULL) { goto finish; } slot_object = object->clones[object->master_clone_slotnum]; rv = meta_get_slot_session(object->master_clone_slotnum, &session, object->creator_session->session_flags); if (rv != CKR_OK) { goto finish; } /* * first, get the master template of all the attributes * for this object */ rv = get_master_attributes_by_object(session, slot_object, &(object->attributes), &(object->num_attributes)); if (rv != CKR_OK) { goto finish; } /* * Get value for each attribute items. * * Some attributes are required by the given object type. * Some are optional. Get all the values first, and then * make sure we have value for all required values, */ attrs = calloc(object->num_attributes, sizeof (CK_ATTRIBUTE)); if (attrs == NULL) { rv = CKR_HOST_MEMORY; goto finish; } for (i = 0; i < object->num_attributes; i++) { attrs[i].type = ((object->attributes[i]).attribute).type; } num_attrs = object->num_attributes; hSession = session->hSession; fw_st_id = session->fw_st_id; /* first, call C_GetAttributeValue() to get size for each attribute */ rv = FUNCLIST(fw_st_id)->C_GetAttributeValue(hSession, slot_object->hObject, attrs, num_attrs); /* * If the return value is not CKR_OK, allow it to be * CKR_ATTRIBUTE_TYPE_INVALID for now. * Some attributes defined in PKCS#11 version 2.11 * might not be defined in earlier versions. We will * TRY to work with those providers if the attribute * is optional. */ if ((rv != CKR_OK) && (rv != CKR_ATTRIBUTE_TYPE_INVALID)) { rv = CKR_FUNCTION_FAILED; /* make sure rv is appropriate */ goto finish; } /* * allocate space. * Since we don't know how many attributes have * values at this time, just assume all of them * have values so we save one loop to count the number * of attributes that have value. */ attrs_with_val = calloc(num_attrs, sizeof (CK_ATTRIBUTE)); if (attrs_with_val == NULL) { rv = CKR_HOST_MEMORY; goto finish; } num_attrs_with_val = 0; for (i = 0; i < num_attrs; i++) { if (!(((CK_LONG)(attrs[i].ulValueLen)) > 0)) { /* if it isn't an optional attr, len should be > 0 */ if (!object->attributes[i].canBeEmptyValue) { rv = CKR_FUNCTION_FAILED; goto finish; } } else { attrs_with_val[num_attrs_with_val].type = attrs[i].type; attrs_with_val[num_attrs_with_val].ulValueLen = attrs[i].ulValueLen; attrs_with_val[num_attrs_with_val].pValue = malloc(attrs[i].ulValueLen); if (attrs_with_val[num_attrs_with_val].pValue == NULL) { rv = CKR_HOST_MEMORY; goto finish; } num_attrs_with_val++; } } rv = FUNCLIST(fw_st_id)->C_GetAttributeValue(hSession, slot_object->hObject, attrs_with_val, num_attrs_with_val); if (rv != CKR_OK) { goto finish; } /* store these values into the meta object */ for (i = 0; i < num_attrs_with_val; i++) { rv = attribute_set_value(&(attrs_with_val[i]), object->attributes, object->num_attributes); if (rv != CKR_OK) { goto finish; } } finish: (void) pthread_rwlock_unlock(&object->attribute_lock); if (session) meta_release_slot_session(session); if (attrs) { for (i = 0; i < num_attrs; i++) { if (attrs[i].pValue != NULL) { free(attrs[i].pValue); } } free(attrs); } if (attrs_with_val) { for (i = 0; i < num_attrs; i++) { if (attrs_with_val[i].pValue != NULL) { freezero(attrs_with_val[i].pValue, attrs_with_val[i].ulValueLen); } } free(attrs_with_val); } return (rv); } /* * Create an object to be used for wrapping and unwrapping. * The same template will be used for all wrapping/unwrapping keys all * the time */ static CK_RV create_wrap_unwrap_key(slot_session_t *slot_session, CK_OBJECT_HANDLE *hObject, wrap_info_t *wrap_info, char *key_data, CK_ULONG key_len) { CK_OBJECT_CLASS objclass; CK_KEY_TYPE keytype; CK_RV rv = CKR_OK; int i; CK_ATTRIBUTE template[WRAP_KEY_TEMPLATE_SIZE]; i = 0; objclass = wrap_info->class; template[i].type = CKA_CLASS; template[i].pValue = &objclass; template[i].ulValueLen = sizeof (objclass); i++; keytype = wrap_info->key_type; template[i].type = CKA_KEY_TYPE; template[i].pValue = &keytype; template[i].ulValueLen = sizeof (keytype); i++; template[i].type = CKA_TOKEN; template[i].pValue = &falsevalue; template[i].ulValueLen = sizeof (falsevalue); if (objclass == CKO_SECRET_KEY) { i++; template[i].type = CKA_VALUE; template[i].pValue = key_data; template[i].ulValueLen = key_len; i++; template[i].type = CKA_WRAP; template[i].pValue = &truevalue; template[i].ulValueLen = sizeof (truevalue); i++; template[i].type = CKA_UNWRAP; template[i].pValue = &truevalue; template[i].ulValueLen = sizeof (truevalue); } else { /* Modulus is the same for rsa public and private key */ i++; template[i].type = CKA_MODULUS; template[i].pValue = Modulus; template[i].ulValueLen = sizeof (Modulus); if (objclass == CKO_PUBLIC_KEY) { /* RSA public key */ i++; template[i].type = CKA_PUBLIC_EXPONENT; template[i].pValue = PubExpo; template[i].ulValueLen = sizeof (PubExpo); i++; template[i].type = CKA_WRAP; template[i].pValue = &truevalue; template[i].ulValueLen = sizeof (truevalue); } else { /* RSA private key */ i++; template[i].type = CKA_PRIVATE_EXPONENT; template[i].pValue = PriExpo; template[i].ulValueLen = sizeof (PriExpo); i++; template[i].type = CKA_UNWRAP; template[i].pValue = &truevalue; template[i].ulValueLen = sizeof (truevalue); } } rv = FUNCLIST(slot_session->fw_st_id)->C_CreateObject( slot_session->hSession, template, i + 1, hObject); return (rv); } /* * Create a clone of a non-sensitive and extractable object. * If the template required for creating the clone doesn't exist, * it will be retrieved from the master clone. */ static CK_RV clone_by_create(meta_object_t *object, slot_object_t *new_clone, slot_session_t *dst_slot_session) { CK_RV rv; int free_token_index = -1; if (object->attributes == NULL) { rv = meta_object_copyin(object); if (rv != CKR_OK) { return (rv); } } if (object->clone_template == NULL) { rv = meta_clone_template_setup(object, object->attributes, object->num_attributes); if (rv != CKR_OK) { return (rv); } } if (object->isFreeToken == FREE_ENABLED) { if (dst_slot_session->slotnum == get_keystore_slotnum()) free_token_index = set_template_boolean(CKA_TOKEN, object->clone_template, object->clone_template_size, B_FALSE, &truevalue); else free_token_index = set_template_boolean(CKA_TOKEN, object->clone_template, object->clone_template_size, B_FALSE, &falsevalue); } /* Create the clone... */ rv = FUNCLIST(dst_slot_session->fw_st_id)->C_CreateObject( dst_slot_session->hSession, object->clone_template, object->clone_template_size, &(new_clone->hObject)); if (free_token_index != -1) { free_token_index = set_template_boolean(CKA_TOKEN, object->clone_template, object->clone_template_size, B_FALSE, &falsevalue); } if (rv != CKR_OK) { return (rv); } return (CKR_OK); } /* * Goes through the list of wraping mechanisms, and returns the first * one that is supported by both the source and the destination slot. * If none of the mechanisms are supported by both slot, return the * first mechanism that's supported by the source slot */ static CK_RV find_best_match_wrap_mech(wrap_info_t *wrap_info, int num_info, CK_ULONG src_slotnum, CK_ULONG dst_slotnum, int *first_both_mech, int *first_src_mech) { int i; boolean_t src_supports, dst_supports; CK_RV rv; CK_MECHANISM_INFO mech_info; mech_info.flags = CKF_WRAP; for (i = 0; i < num_info; i++) { src_supports = B_FALSE; dst_supports = B_FALSE; rv = meta_mechManager_slot_supports_mech( (wrap_info[i]).mech_type, src_slotnum, &src_supports, NULL, B_FALSE, &mech_info); if (rv != CKR_OK) { return (rv); } rv = meta_mechManager_slot_supports_mech( (wrap_info[i]).mech_type, dst_slotnum, &dst_supports, NULL, B_FALSE, &mech_info); if (rv != CKR_OK) { return (rv); } /* both source and destination supports the mech */ if ((src_supports) && (dst_supports)) { *first_both_mech = i; return (CKR_OK); } if ((src_supports) && (*first_src_mech == -1)) { *first_src_mech = i; } } return (CKR_OK); } /* * Determine the wrapping/unwrapping mechanism to be used * * If possible, select a mechanism that's supported by both source * and destination slot. If none of the mechanisms are supported * by both slot, then, select the first one supported by * the source slot. */ static CK_RV get_wrap_mechanism(CK_OBJECT_CLASS obj_class, CK_KEY_TYPE key_type, CK_ULONG src_slotnum, CK_ULONG dst_slotnum, wrap_info_t *wrap_info) { wrap_info_t *wrap_info_to_search = NULL; unsigned int num_wrap_info; CK_RV rv; int i; boolean_t src_supports = B_FALSE, dst_supports = B_FALSE; int first_src_mech, rsa_first_src_mech, first_both_mech; CK_MECHANISM_INFO mech_info; mech_info.flags = CKF_WRAP; if ((obj_class == CKO_PRIVATE_KEY) && (key_type == CKK_KEA)) { /* * only SKIPJACK keys can be used for wrapping * KEA private keys */ for (i = 0; i < num_special_wrap_info; i++) { if ((special_wrap_info[i]).mech_type != CKM_SKIPJACK_WRAP) { continue; } src_supports = B_FALSE; dst_supports = B_FALSE; rv = meta_mechManager_slot_supports_mech( (special_wrap_info[i]).mech_type, src_slotnum, &src_supports, NULL, B_FALSE, &mech_info); if (rv != CKR_OK) { goto finish; } rv = meta_mechManager_slot_supports_mech( (special_wrap_info[i]).mech_type, dst_slotnum, &dst_supports, NULL, B_FALSE, &mech_info); if (rv != CKR_OK) { goto finish; } if (src_supports) { /* * both src and dst supports the mech or * only the src supports the mech */ (void) memcpy(wrap_info, &(special_wrap_info[i]), sizeof (wrap_info_t)); wrap_info->src_supports = src_supports; wrap_info->dst_supports = dst_supports; rv = CKR_OK; goto finish; } } /* * if we are here, that means neither the source slot * nor the destination slots supports CKM_SKIPJACK_WRAP. */ rv = CKR_FUNCTION_FAILED; goto finish; } if ((key_type == CKK_SKIPJACK) || (key_type == CKK_BATON) || (key_type == CKK_JUNIPER)) { /* special key types */ wrap_info_to_search = special_wrap_info; num_wrap_info = num_special_wrap_info; } else { /* use the regular wrapping mechanisms */ wrap_info_to_search = common_wrap_info; num_wrap_info = num_common_wrap_info; } first_both_mech = -1; first_src_mech = -1; rv = find_best_match_wrap_mech(wrap_info_to_search, num_wrap_info, src_slotnum, dst_slotnum, &first_both_mech, &first_src_mech); if (rv != CKR_OK) { goto finish; } if (first_both_mech != -1) { (void) memcpy(wrap_info, &(wrap_info_to_search[first_both_mech]), sizeof (wrap_info_t)); wrap_info->src_supports = B_TRUE; wrap_info->dst_supports = B_TRUE; rv = CKR_OK; goto finish; } /* * If we are here, we did not find a mechanism that's supported * by both source and destination slot. * * If it is a secret key, can also try to wrap it with * a RSA public key */ if (obj_class == CKO_SECRET_KEY) { first_both_mech = -1; rsa_first_src_mech = -1; rv = find_best_match_wrap_mech(rsa_wrap_info, num_rsa_wrap_info, src_slotnum, dst_slotnum, &first_both_mech, &rsa_first_src_mech); if (rv != CKR_OK) { goto finish; } if (first_both_mech > -1) { (void) memcpy(wrap_info, &(rsa_wrap_info[first_both_mech]), sizeof (wrap_info_t)); wrap_info->src_supports = B_TRUE; wrap_info->dst_supports = B_TRUE; rv = CKR_OK; goto finish; } } /* * if we are here, that means none of the mechanisms are supported * by both the source and the destination */ if (first_src_mech > -1) { /* source slot support one of the secret key mechs */ (void) memcpy(wrap_info, &(wrap_info_to_search[first_src_mech]), sizeof (wrap_info_t)); wrap_info->src_supports = B_TRUE; wrap_info->dst_supports = B_FALSE; rv = CKR_OK; } else if (rsa_first_src_mech > -1) { /* source slot support one of the RSA mechs */ (void) memcpy(wrap_info, &(rsa_wrap_info[rsa_first_src_mech]), sizeof (wrap_info_t)); wrap_info->src_supports = B_TRUE; wrap_info->dst_supports = B_FALSE; rv = CKR_OK; } else { /* neither source nor destination support any wrap mechs */ rv = CKR_FUNCTION_FAILED; } finish: return (rv); } /* * This is called if the object to be cloned is a sensitive object */ static CK_RV clone_by_wrap(meta_object_t *object, slot_object_t *new_clone, slot_session_t *dst_slot_session) { slot_session_t *src_slot_session = NULL; CK_OBJECT_HANDLE wrappingKey = 0, unwrappingKey = 0; CK_MECHANISM wrappingMech; CK_BYTE *wrappedKey = NULL; CK_ULONG wrappedKeyLen = 0; slot_object_t *slot_object = NULL; CK_RV rv = CKR_OK; CK_OBJECT_HANDLE unwrapped_obj; meta_object_t *tmp_meta_obj = NULL; slot_object_t *tmp_slot_obj = NULL; CK_OBJECT_CLASS obj_class; CK_KEY_TYPE key_type; meta_session_t *tmp_meta_session = NULL; CK_ATTRIBUTE unwrap_template[4]; char key_data[1024]; /* should be big enough for any key size */ char ivbuf[1024]; /* should be big enough for any mech */ wrap_info_t wrap_info; CK_ULONG key_len, unwrap_template_size; slot_object = object->clones[object->master_clone_slotnum]; rv = meta_get_slot_session(object->master_clone_slotnum, &src_slot_session, object->creator_session->session_flags); if (rv != CKR_OK) { return (rv); } /* * get the object class and key type for unwrap template * This information will also be used for determining * which wrap mechanism and which key to use for * doing the wrapping */ unwrap_template[0].type = CKA_CLASS; unwrap_template[0].pValue = &obj_class; unwrap_template[0].ulValueLen = sizeof (obj_class); unwrap_template[1].type = CKA_KEY_TYPE; unwrap_template[1].pValue = &key_type; unwrap_template[1].ulValueLen = sizeof (key_type); rv = FUNCLIST(src_slot_session->fw_st_id)->C_GetAttributeValue( src_slot_session->hSession, slot_object->hObject, unwrap_template, 2); if (rv != CKR_OK) { goto finish; } rv = get_wrap_mechanism(obj_class, key_type, src_slot_session->slotnum, dst_slot_session->slotnum, &wrap_info); if (rv != CKR_OK) { goto finish; } /* * read number of bytes required from random device for * creating a secret key for wrapping and unwrapping */ if (wrap_info.class == CKO_SECRET_KEY) { /* * /dev/urandom will be used for generating the key used * for doing the wrap/unwrap. It's should be ok to * use /dev/urandom because this key is used for this * one time operation only. It doesn't need to be stored. */ key_len = wrap_info.key_length; if (pkcs11_get_urandom(key_data, key_len) < 0) { rv = CKR_FUNCTION_FAILED; goto finish; } if (wrap_info.iv_length > 0) { if (pkcs11_get_urandom( ivbuf, wrap_info.iv_length) < 0) { rv = CKR_FUNCTION_FAILED; goto finish; } } } /* create the wrapping key */ rv = create_wrap_unwrap_key(src_slot_session, &wrappingKey, &wrap_info, key_data, key_len); if (rv != CKR_OK) { goto finish; } wrappingMech.mechanism = wrap_info.mech_type; wrappingMech.pParameter = ((wrap_info.iv_length > 0) ? ivbuf : NULL); wrappingMech.ulParameterLen = wrap_info.iv_length; /* get the size of the wrapped key */ rv = FUNCLIST(src_slot_session->fw_st_id)->C_WrapKey( src_slot_session->hSession, &wrappingMech, wrappingKey, slot_object->hObject, NULL, &wrappedKeyLen); if (rv != CKR_OK) { goto finish; } wrappedKey = malloc(wrappedKeyLen * sizeof (CK_BYTE)); if (wrappedKey == NULL) { rv = CKR_HOST_MEMORY; goto finish; } /* do the actual key wrapping */ rv = FUNCLIST(src_slot_session->fw_st_id)->C_WrapKey( src_slot_session->hSession, &wrappingMech, wrappingKey, slot_object->hObject, wrappedKey, &wrappedKeyLen); if (rv != CKR_OK) { goto finish; } /* explicitly force the unwrapped object to be not sensitive */ unwrap_template[2].type = CKA_SENSITIVE; unwrap_template[2].pValue = &falsevalue; unwrap_template[2].ulValueLen = sizeof (falsevalue); unwrap_template[3].type = CKA_TOKEN; unwrap_template[3].pValue = &falsevalue; unwrap_template[3].ulValueLen = sizeof (falsevalue); unwrap_template_size = sizeof (unwrap_template) / sizeof (CK_ATTRIBUTE); if (!wrap_info.dst_supports) { /* * if we know for sure that the destination slot doesn't * support the wrapping mechanism, no point in trying. * go directly to unwrap in source slot, and create key * in destination */ goto unwrap_in_source; } /* create the unwrapping key in destination slot */ if (wrap_info.key_type == CKK_RSA) { /* for RSA key, the unwrapping key need to be private key */ wrap_info.class = CKO_PRIVATE_KEY; } rv = create_wrap_unwrap_key(dst_slot_session, &unwrappingKey, &wrap_info, key_data, key_len); if (rv != CKR_OK) { goto finish; } rv = FUNCLIST(dst_slot_session->fw_st_id)->C_UnwrapKey( dst_slot_session->hSession, &wrappingMech, unwrappingKey, wrappedKey, wrappedKeyLen, unwrap_template, unwrap_template_size, &(new_clone->hObject)); if (rv != CKR_OK) { unwrap_in_source: /* * There seemed to be a problem with unwrapping in the * destination slot. * Try to do the unwrap in the src slot so it becomes * a non-sensitive object, then, get all the attributes * and create the object in the destination slot */ if (wrap_info.class == CKO_SECRET_KEY) { /* unwrap with same key used for wrapping */ rv = FUNCLIST(src_slot_session->fw_st_id)->C_UnwrapKey( src_slot_session->hSession, &wrappingMech, wrappingKey, wrappedKey, wrappedKeyLen, unwrap_template, unwrap_template_size, &(unwrapped_obj)); } else { /* * If the object is wrapping with RSA public key, need * need to create RSA private key for unwrapping */ wrap_info.class = CKO_PRIVATE_KEY; rv = create_wrap_unwrap_key(src_slot_session, &unwrappingKey, &wrap_info, key_data, key_len); if (rv != CKR_OK) { goto finish; } rv = FUNCLIST(src_slot_session->fw_st_id)->C_UnwrapKey( src_slot_session->hSession, &wrappingMech, unwrappingKey, wrappedKey, wrappedKeyLen, unwrap_template, unwrap_template_size, &(unwrapped_obj)); } if (rv != CKR_OK) { goto finish; } rv = meta_session_alloc(&tmp_meta_session); if (rv != CKR_OK) { goto finish; } tmp_meta_session->session_flags = CKF_SERIAL_SESSION; rv = meta_object_alloc(tmp_meta_session, &tmp_meta_obj); if (rv != CKR_OK) { goto finish; } rv = meta_slot_object_alloc(&tmp_slot_obj); if (rv != CKR_OK) { goto finish; } tmp_meta_obj->master_clone_slotnum = src_slot_session->slotnum; tmp_slot_obj->hObject = unwrapped_obj; tmp_meta_obj->clones[tmp_meta_obj->master_clone_slotnum] = tmp_slot_obj; meta_slot_object_activate(tmp_slot_obj, src_slot_session, B_FALSE); tmp_slot_obj = NULL; rv = clone_by_create(tmp_meta_obj, new_clone, dst_slot_session); if (rv != CKR_OK) { goto finish; } } finish: if (unwrappingKey) { (void) FUNCLIST(dst_slot_session->fw_st_id)->C_DestroyObject( dst_slot_session->hSession, unwrappingKey); } if (wrappingKey) { (void) FUNCLIST(src_slot_session->fw_st_id)->C_DestroyObject( src_slot_session->hSession, wrappingKey); } if (tmp_slot_obj) { (void) meta_slot_object_dealloc(tmp_slot_obj); } if (tmp_meta_obj) { (void) meta_object_dealloc(tmp_meta_session, tmp_meta_obj, B_TRUE); } if (tmp_meta_session) { (void) meta_session_dealloc(tmp_meta_session); } if (wrappedKey) { freezero(wrappedKey, wrappedKeyLen); } if (src_slot_session) { meta_release_slot_session(src_slot_session); } return (rv); } /* * meta_object_get_clone * * Creates a "clone" of a metaobject on the specified slot. A clone is a * copy of the object. * * Clones are cached, so that they can be reused with subsequent operations. */ CK_RV meta_object_get_clone(meta_object_t *object, CK_ULONG slot_num, slot_session_t *slot_session, slot_object_t **clone) { CK_RV rv = CKR_OK; slot_object_t *newclone = NULL; /* Does a clone already exist? */ if (object->clones[slot_num] != NULL) { *clone = object->clones[slot_num]; return (CKR_OK); } if ((object->isSensitive) && (object->isToken) && (!metaslot_auto_key_migrate)) { /* * if the object is a sensitive token object, and auto * key migrate is not allowed, will not create the clone * in another slot */ return (CKR_FUNCTION_FAILED); } /* object attributes can't be extracted and attributes are not known */ if ((!object->isExtractable) && (object->attributes == NULL)) { return (CKR_FUNCTION_FAILED); } (void) pthread_mutex_lock(&object->clone_create_lock); /* Maybe someone just created one? */ if (object->clones[slot_num] != NULL) { *clone = object->clones[slot_num]; goto finish; } /* * has an attempt already been made to create this object in * slot? If yes, and there's no clone, as indicated above, * that means this object can't be created in this slot. */ if (object->tried_create_clone[slot_num]) { (void) pthread_mutex_unlock(&object->clone_create_lock); return (CKR_FUNCTION_FAILED); } rv = meta_slot_object_alloc(&newclone); if (rv != CKR_OK) goto finish; object->tried_create_clone[slot_num] = B_TRUE; /* * If this object is sensitive and we do not have not copied in the * attributes via FreeObject functionality, then we need to wrap it off * the provider. If we do have attributes, we can just create the * clone */ if (object->isSensitive && object->attributes == NULL) { rv = clone_by_wrap(object, newclone, slot_session); } else { rv = clone_by_create(object, newclone, slot_session); } if (rv != CKR_OK) { goto finish; } object->clones[slot_num] = newclone; meta_slot_object_activate(newclone, slot_session, object->isToken); *clone = newclone; newclone = NULL; finish: (void) pthread_mutex_unlock(&object->clone_create_lock); if (newclone) meta_slot_object_dealloc(newclone); return (rv); } /* * meta_setup_clone_template * * Create a clone template for the specified object. */ static CK_RV meta_clone_template_setup(meta_object_t *object, const generic_attr_t *attributes, size_t num_attributes) { CK_RV rv = CKR_OK; CK_ATTRIBUTE *clone_template; size_t i, c = 0; clone_template = malloc(num_attributes * sizeof (CK_ATTRIBUTE)); if (clone_template == NULL) { rv = CKR_HOST_MEMORY; goto finish; } /* Don't allow attributes to change while we look at them. */ (void) pthread_rwlock_rdlock(&object->attribute_lock); for (i = 0; i < num_attributes; i++) { if (!attributes[i].isCloneAttr || (attributes[i].attribute.type == CKA_TOKEN && object->isFreeToken == FREE_DISABLED)) { continue; } if ((!(attributes[i].hasValueForClone)) && (attributes[i].canBeEmptyValue)) { continue; } clone_template[c].type = attributes[i].attribute.type; clone_template[c].ulValueLen = attributes[i].attribute.ulValueLen; /* Allocate space to store the attribute value. */ clone_template[c].pValue = malloc(clone_template[c].ulValueLen); if (clone_template[c].pValue == NULL) { free(clone_template); rv = CKR_HOST_MEMORY; (void) pthread_rwlock_unlock(&object->attribute_lock); goto finish; } (void) memcpy(clone_template[c].pValue, object->attributes[i].attribute.pValue, clone_template[c].ulValueLen); c++; } (void) pthread_rwlock_unlock(&object->attribute_lock); object->clone_template = clone_template; object->clone_template_size = c; finish: return (rv); } /* * meta_object_find_by_handle * * Search for an existing metaobject, using the object handle of a clone * on a particular slot. * * Returns a matching metaobject, or NULL if no match was found. */ meta_object_t * meta_object_find_by_handle(CK_OBJECT_HANDLE hObject, CK_ULONG slotnum, boolean_t token_only) { meta_object_t *object = NULL, *tmp_obj; meta_session_t *session; if (!token_only) { (void) pthread_rwlock_rdlock(&meta_sessionlist_lock); session = meta_sessionlist_head; while (session != NULL) { /* lock the objects list while we look at it */ (void) pthread_rwlock_rdlock( &(session->object_list_lock)); tmp_obj = session->object_list_head; while (tmp_obj != NULL) { slot_object_t *slot_object; (void) pthread_rwlock_rdlock( &(tmp_obj->object_lock)); slot_object = tmp_obj->clones[slotnum]; if (slot_object != NULL) { if (slot_object->hObject == hObject) { object = tmp_obj; } } (void) pthread_rwlock_unlock( &(tmp_obj->object_lock)); if (object != NULL) { break; } tmp_obj = tmp_obj->next; } (void) pthread_rwlock_unlock( &(session->object_list_lock)); if (object != NULL) { break; } session = session->next; } (void) pthread_rwlock_unlock(&meta_sessionlist_lock); } if (object != NULL) { /* found the object, no need to look further */ return (object); } /* * Look at list of token objects */ (void) pthread_rwlock_rdlock(&tokenobject_list_lock); tmp_obj = tokenobject_list_head; while (tmp_obj != NULL) { slot_object_t *slot_object; (void) pthread_rwlock_rdlock(&(tmp_obj->object_lock)); slot_object = tmp_obj->clones[slotnum]; if (slot_object != NULL) { if (slot_object->hObject == hObject) object = tmp_obj; } (void) pthread_rwlock_unlock(&(tmp_obj->object_lock)); if (object != NULL) { break; } tmp_obj = tmp_obj->next; } (void) pthread_rwlock_unlock(&tokenobject_list_lock); return (object); } CK_RV meta_token_object_deactivate(token_obj_type_t token_type) { meta_object_t *object, *tmp_object; CK_RV save_rv = CKR_OK, rv; /* get a write lock on the token object list */ (void) pthread_rwlock_wrlock(&tokenobject_list_lock); object = tokenobject_list_head; /* go through each object and delete the one with matching type */ while (object != NULL) { tmp_object = object->next; if ((token_type == ALL_TOKEN) || ((object->isPrivate) && (token_type == PRIVATE_TOKEN)) || ((!object->isPrivate) && (token_type == PUBLIC_TOKEN))) { rv = meta_object_deactivate(object, B_TRUE, B_FALSE); if ((rv != CKR_OK) && (save_rv == CKR_OK)) { save_rv = rv; goto finish; } rv = meta_object_dealloc(NULL, object, B_FALSE); if ((rv != CKR_OK) && (save_rv == CKR_OK)) { save_rv = rv; goto finish; } } object = tmp_object; } finish: (void) pthread_rwlock_unlock(&tokenobject_list_lock); return (save_rv); } /* * This function adds the to-be-freed meta object to a linked list. * When the number of objects queued in the linked list reaches the * maximum threshold MAX_OBJ_TO_BE_FREED, it will free the first * object (FIFO) in the list. */ void meta_object_delay_free(meta_object_t *objp) { meta_object_t *tmp; (void) pthread_mutex_lock(&obj_delay_freed.obj_to_be_free_mutex); /* Add the newly deleted object at the end of the list */ objp->next = NULL; if (obj_delay_freed.first == NULL) { obj_delay_freed.last = objp; obj_delay_freed.first = objp; } else { obj_delay_freed.last->next = objp; obj_delay_freed.last = objp; } if (++obj_delay_freed.count >= MAX_OBJ_TO_BE_FREED) { /* * Free the first object in the list only if * the total count reaches maximum threshold. */ obj_delay_freed.count--; tmp = obj_delay_freed.first->next; free(obj_delay_freed.first); obj_delay_freed.first = tmp; } (void) pthread_mutex_unlock(&obj_delay_freed.obj_to_be_free_mutex); } /* * This function checks if the object passed can be a freeobject. * * If there is more than one provider that supports the supported freeobject * mechanisms then allow freeobjects to be an option. */ boolean_t meta_freeobject_check(meta_session_t *session, meta_object_t *object, CK_MECHANISM *pMech, CK_ATTRIBUTE *tmpl, CK_ULONG tmpl_len, CK_KEY_TYPE keytype) { mech_support_info_t *info = &(session->mech_support_info); /* * If key migration is turned off, or the object does not has any of * the required flags and there is only one slot, then we don't need * FreeObjects. */ if (!metaslot_auto_key_migrate || (!object->isToken && !object->isSensitive && meta_slotManager_get_slotcount() < 2)) goto failure; /* * If this call is for key generation, check pMech for supported * FreeObject mechs */ if (pMech != NULL) { if (pMech->mechanism == CKM_RSA_PKCS_KEY_PAIR_GEN || pMech->mechanism == CKM_EC_KEY_PAIR_GEN || pMech->mechanism == CKM_DH_PKCS_KEY_PAIR_GEN || pMech->mechanism == CKM_DH_PKCS_DERIVE) info->mech = pMech->mechanism; else goto failure; /* * If this call is for an object creation, look inside the template * for supported FreeObject mechs */ } else if (tmpl_len > 0) { if (!get_template_ulong(CKA_KEY_TYPE, tmpl, tmpl_len, &keytype)) goto failure; switch (keytype) { case CKK_RSA: info->mech = CKM_RSA_PKCS_KEY_PAIR_GEN; break; case CKK_EC: info->mech = CKM_EC_KEY_PAIR_GEN; break; case CKK_DH: info->mech = CKM_DH_PKCS_KEY_PAIR_GEN; break; default: goto failure; } } else goto failure; /* Get the slot that support this mech... */ if (meta_mechManager_get_slots(info, B_FALSE, NULL) != CKR_OK) goto failure; /* * If there is only one slot with the mech or the first slot in * the list is the keystore slot, we should bail. */ if (info->num_supporting_slots < 2 && info->supporting_slots[0]->slotnum == get_keystore_slotnum()) goto failure; if (object->isToken) object->isFreeToken = FREE_ALLOWED_KEY; else object->isFreeToken = FREE_DISABLED; object->isFreeObject = FREE_ALLOWED_KEY; return (B_TRUE); failure: object->isFreeToken = FREE_DISABLED; object->isFreeObject = FREE_DISABLED; return (B_FALSE); } /* * This function assumes meta_freeobject_check() has just been called and set * the isFreeObject and/or isFreeToken vars to FREE_ALLOWED_KEY. * * If the template value for CKA_PRIVATE, CKA_SENSITIVE and/or CKA_TOKEN are * true, then isFreeObject is fully enabled. In addition isFreeToken is * enabled if is CKA_TOKEN true. * * If create is true, we are doing a C_CreateObject operation and don't * handle CKA_PRIVATE & CKA_SENSITIVE flags, we only care about CKA_TOKEN. */ boolean_t meta_freeobject_set(meta_object_t *object, CK_ATTRIBUTE *tmpl, CK_ULONG tmpl_len, boolean_t create) { /* This check should never be true, if it is, it's a bug */ if (object->isFreeObject < FREE_ALLOWED_KEY) return (B_FALSE); if (!create) { /* Turn off the Sensitive flag */ if (object->isSensitive) { if (set_template_boolean(CKA_SENSITIVE, tmpl, tmpl_len, B_TRUE, &falsevalue) == -1) goto failure; object->isFreeObject = FREE_ENABLED; } /* Turn off the Private flag */ if (object->isPrivate) { if (set_template_boolean(CKA_PRIVATE, tmpl, tmpl_len, B_TRUE, &falsevalue) == -1) goto failure; object->isFreeObject = FREE_ENABLED; } } if (object->isToken) { object->isToken = B_FALSE; object->isFreeToken = FREE_ENABLED; object->isFreeObject = FREE_ENABLED; } else object->isFreeToken = FREE_DISABLED; /* * If isFreeObject is not in the FREE_ENABLED state yet, it can be * turned off because the object doesn't not need to be a FreeObject. */ if (object->isFreeObject == FREE_ALLOWED_KEY) object->isFreeObject = FREE_DISABLED; return (B_TRUE); failure: object->isFreeToken = FREE_DISABLED; object->isFreeObject = FREE_DISABLED; return (B_FALSE); } /* * This function sets the CKA_TOKEN flag on a given object template depending * if the slot being used is a keystore. * * If the object is a token, but the slot is not the system keystore or has * no keystore, then set the template to token = false; otherwise it's true. * In addition we know ahead of time what the value is, so if the value is * already correct, bypass the setting function */ CK_RV meta_freetoken_set(CK_ULONG slot_num, CK_BBOOL *current_value, CK_ATTRIBUTE *tmpl, CK_ULONG tmpl_len) { if (slot_num == get_keystore_slotnum()) { if (*current_value == TRUE) return (CKR_OK); if (set_template_boolean(CKA_TOKEN, tmpl, tmpl_len, B_TRUE, &truevalue) == -1) return (CKR_FUNCTION_FAILED); } else { if (*current_value == FALSE) return (CKR_OK); if (set_template_boolean(CKA_TOKEN, tmpl, tmpl_len, B_TRUE, &falsevalue) == -1) return (CKR_FUNCTION_FAILED); *current_value = FALSE; } return (CKR_OK); } /* * Cloning function for meta_freeobject_clone() to use. This function * is streamlined because we know what the object is and this should * not be called as a generic cloner. */ static CK_RV meta_freeobject_clone_maker(meta_session_t *session, meta_object_t *object, CK_ULONG slotnum) { slot_object_t *slot_object = NULL; slot_session_t *slot_session = NULL; CK_RV rv; rv = meta_slot_object_alloc(&slot_object); if (rv != CKR_OK) goto cleanup; rv = meta_get_slot_session(slotnum, &slot_session, session->session_flags); if (rv != CKR_OK) goto cleanup; rv = clone_by_create(object, slot_object, slot_session); if (rv == CKR_OK) { object->clones[slotnum] = slot_object; meta_slot_object_activate(slot_object, slot_session, B_TRUE); } cleanup: meta_release_slot_session(slot_session); return (rv); } /* * This function is called when a object is a FreeObject. * * What we are given is an object that has been generated on a provider * that is not its final usage place. That maybe because: * 1) it's a token and needs to be stored in keystore. * 2) it was to be a private/sensitive object that we modified so we could know * the important attributes for cloning before we make it private/sensitive. */ boolean_t meta_freeobject_clone(meta_session_t *session, meta_object_t *object) { CK_RV rv; CK_ULONG keystore_slotnum; CK_ATTRIBUTE attr[2]; boolean_t failover = B_FALSE; if (object->attributes == NULL) { rv = meta_object_copyin(object); if (rv != CKR_OK) return (rv); } if (object->isPrivate) { CK_OBJECT_HANDLE new_clone; CK_ULONG slotnum = object->master_clone_slotnum; slot_session_t *slot_session; attr[0].type = CKA_PRIVATE; attr[0].pValue = &truevalue; attr[0].ulValueLen = sizeof (truevalue); /* Set the master attribute list */ rv = attribute_set_value(attr, object->attributes, object->num_attributes); if (rv > 0) return (CKR_FUNCTION_FAILED); /* Get a slot session */ rv = meta_get_slot_session(slotnum, &slot_session, session->session_flags); if (rv > 0) return (rv); /* Create the new CKA_PRIVATE one */ rv = FUNCLIST(slot_session->fw_st_id)->\ C_CopyObject(slot_session->hSession, object->clones[slotnum]->hObject, attr, 1, &new_clone); if (rv == CKR_USER_NOT_LOGGED_IN) { /* * If the CopyObject fails, we may be using a provider * that has a keystore that is not the default * keystore set in metaslot or has object management * abilities. In which case we should write this * object to metaslot's keystore and let the failover. * rest of the function know we've changed providers. */ failover = B_TRUE; keystore_slotnum = get_keystore_slotnum(); if (object->clones[keystore_slotnum] == NULL) { rv = meta_freeobject_clone_maker(session, object, keystore_slotnum); if (rv != CKR_OK) { goto failure; } } object->master_clone_slotnum = keystore_slotnum; } else if (rv != CKR_OK) { meta_release_slot_session(slot_session); goto failure; } /* Remove the old object */ rv = FUNCLIST(slot_session->fw_st_id)-> \ C_DestroyObject(slot_session->hSession, object->clones[slotnum]->hObject); if (rv != CKR_OK) { meta_release_slot_session(slot_session); goto failure; } if (!failover) object->clones[slotnum]->hObject = new_clone; else object->clones[slotnum] = NULL; meta_release_slot_session(slot_session); } if (object->isSensitive) { slot_session_t *slot_session; CK_ULONG slotnum = object->master_clone_slotnum; attr[0].type = CKA_SENSITIVE; attr[0].pValue = &truevalue; attr[0].ulValueLen = sizeof (truevalue); rv = attribute_set_value(attr, object->attributes, object->num_attributes); if (rv != CKR_OK) goto failure; rv = meta_get_slot_session(slotnum, &slot_session, session->session_flags); if (rv == CKR_OK) { rv = FUNCLIST(slot_session->fw_st_id)-> \ C_SetAttributeValue(slot_session->hSession, object->clones[slotnum]->hObject, attr, 1); meta_release_slot_session(slot_session); } } if (object->isFreeToken == FREE_ENABLED || failover) { keystore_slotnum = get_keystore_slotnum(); if (object->clones[keystore_slotnum] == NULL) { rv = meta_freeobject_clone_maker(session, object, keystore_slotnum); if (rv != CKR_OK) goto failure; object->master_clone_slotnum = keystore_slotnum; } object->isFreeToken = FREE_ENABLED; } object->isFreeObject = FREE_ENABLED; return (CKR_OK); failure: object->isFreeToken = FREE_DISABLED; object->isFreeObject = FREE_DISABLED; return (rv); }