/*
* 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 2009 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
/*
* Object Management Functions
* (as defined in PKCS#11 spec section 11.7)
*/
#include <strings.h>
#include "metaGlobal.h"
#include <stdio.h>
#define FIND_OBJ_BUF_SIZE 512 /* size of buf used for C_FindObjects */
/*
* Argument related return codes. Will return to the caller immediately,
* and not try the operation on another slot.
*/
static CK_RV stop_rv[] = {
CKR_ARGUMENTS_BAD,
CKR_ATTRIBUTE_TYPE_INVALID,
CKR_DOMAIN_PARAMS_INVALID,
CKR_TEMPLATE_INCOMPLETE
};
static int num_stop_rv = sizeof (stop_rv) / sizeof (CK_RV);
/*
* Return codes that are related to a specific slot.
* Will try to perform the operation in the next available slot.
* If all attempts failed, will return the error code from the first slot.
*
* This list is here for reference only, it is commented out because
* it doesn't need to be used by the code at this point.
*
* static CK_RV try_again_rv[] = {
* CKR_DEVICE_ERROR,
* CKR_DEVICE_MEMORY,
* CKR_DEVICE_REMOVED,
* CKR_FUNCTION_FAILED,
* CKR_GENERAL_ERROR,
* CKR_HOST_MEMORY,
* CKR_TEMPLATE_INCONSISTENT,
* CKR_ATTRIBUTE_READ_ONLY,
* CKR_ATTRIBUTE_VALUE_INVALID
* };
* static int num_try_again_rv = sizeof (try_again_rv) / sizeof (CK_RV);
*/
/*
* We should never get these return codes because
* MetaSlot is the one that actually created the
* sessions. When we get these errors in C_CreateObject,
* will try to create the object in the next available slot.
* If all attempts failed, will return CKR_FUNCTION_FAILED
* to the caller.
*/
static CK_RV other_rv[] = {
CKR_CRYPTOKI_NOT_INITIALIZED,
CKR_SESSION_CLOSED,
CKR_SESSION_HANDLE_INVALID,
CKR_SESSION_READ_ONLY
};
static int num_other_rv = sizeof (other_rv) / sizeof (CK_RV);
/*
* This function is only used by the C_CreateObject and C_CopyObject.
*
* It is used to determine if the operation should be tried on another slot
* based on the return code
*/
static boolean_t
try_again(CK_RV rv)
{
int i;
for (i = 0; i < num_stop_rv; i++) {
if (rv == stop_rv[i]) {
return (B_FALSE);
}
}
return (B_TRUE);
}
/*
* meta_CreateObject
*
*/
CK_RV
meta_CreateObject(CK_SESSION_HANDLE hSession, CK_ATTRIBUTE_PTR pTemplate,
CK_ULONG ulCount, CK_OBJECT_HANDLE_PTR phObject)
{
CK_RV rv;
meta_session_t *session;
slot_session_t *slot_session = NULL;
meta_object_t *object = NULL;
slot_object_t *slot_object = NULL;
CK_OBJECT_HANDLE hNewObject;
CK_ULONG slot_num, keystore_slotnum;
CK_RV first_rv;
if (pTemplate == NULL || ulCount < 1 || phObject == NULL)
return (CKR_ARGUMENTS_BAD);
rv = meta_handle2session(hSession, &session);
if (rv != CKR_OK)
return (rv);
rv = meta_object_alloc(session, &object);
if (rv != CKR_OK)
goto cleanup;
/*
* Create a clone of the object
*/
rv = meta_slot_object_alloc(&slot_object);
if (rv != CKR_OK)
goto cleanup;
/*
* Set to true (token object) if template has CKA_TOKEN=true;
* otherwise, it is false (session object).
*/
(void) get_template_boolean(CKA_TOKEN, pTemplate, ulCount,
&(object->isToken));
/* Can't create token objects in a read-only session. */
if ((IS_READ_ONLY_SESSION(session->session_flags)) && object->isToken) {
rv = CKR_SESSION_READ_ONLY;
goto cleanup;
}
/*
* Set to true (private object) if template has CKA_PRIVATE=true;
* otherwise, it is false (public object).
*/
(void) get_template_boolean(CKA_PRIVATE, pTemplate, ulCount,
&(object->isPrivate));
/* Assume object is extractable unless template has otherwise */
object->isExtractable = B_TRUE;
(void) get_template_boolean(CKA_EXTRACTABLE, pTemplate, ulCount,
&(object->isExtractable));
/*
* Set to true (sensitive object) if template has CKA_SENSITIVE=true;
* otherwise, it is false.
*/
(void) get_template_boolean(CKA_SENSITIVE, pTemplate, ulCount,
&(object->isSensitive));
/*
* Check if this can be a FreeObject.
*
* For creating objects, this check is mostly for preventing
* non-keystore hardware from creating CKA_PRIVATE objects without
* logging in.
*/
if (meta_freeobject_check(session, object, NULL, pTemplate, ulCount,
NULL)) {
/*
* Make sure we are logged into the keystore if this is a
* private freetoken object.
*/
if (object->isPrivate && !metaslot_logged_in())
return (CKR_USER_NOT_LOGGED_IN);
if (!meta_freeobject_set(object, pTemplate, ulCount, B_TRUE))
goto cleanup;
}
keystore_slotnum = get_keystore_slotnum();
if (object->isToken || object->isFreeToken == FREE_ENABLED) {
/*
* If this is a token object or a FreeToken then create it
* on the keystore slot.
*/
slot_num = keystore_slotnum;
rv = meta_get_slot_session(slot_num, &slot_session,
session->session_flags);
if (rv != CKR_OK)
goto cleanup;
object->tried_create_clone[slot_num] = B_TRUE;
rv = FUNCLIST(slot_session->fw_st_id)->C_CreateObject(
slot_session->hSession, pTemplate, ulCount, &hNewObject);
if (rv != CKR_OK)
goto cleanup;
} else {
/*
* Create a clone of the object in the first available slot.
*
* If creating a clone in a specific slot failed, it will
* either stop and return the error to the user, or try
* again in the next available slot until it succeeds. The
* decision to stop or continue is made based on the return
* code.
*/
CK_ULONG num_slots = meta_slotManager_get_slotcount();
for (slot_num = 0; slot_num < num_slots; slot_num++) {
/*
* If this is a free token and we are on the keystore
* slot, bypass this because it was already created
*/
rv = meta_get_slot_session(slot_num, &slot_session,
session->session_flags);
if (rv != CKR_OK)
goto cleanup;
object->tried_create_clone[slot_num] = B_TRUE;
rv = FUNCLIST(slot_session->fw_st_id)->C_CreateObject(
slot_session->hSession, pTemplate, ulCount,
&hNewObject);
if (rv == CKR_OK)
break;
if (!try_again(rv))
goto cleanup;
/* save first rv for other errors */
if (slot_num == 0)
first_rv = rv;
meta_release_slot_session(slot_session);
slot_session = NULL;
}
}
if (rv == CKR_OK) {
slot_object->hObject = hNewObject;
object->clones[slot_num] = slot_object;
object->master_clone_slotnum = slot_num;
/* Allow FreeToken to activate onto token obj list */
if (object->isFreeToken == FREE_ENABLED)
object->isToken = B_TRUE;
meta_slot_object_activate(slot_object, slot_session,
object->isToken);
slot_object = NULL;
meta_release_slot_session(slot_session);
slot_session = NULL;
} else {
/*
* return either first error code or
* CKR_FUNCTION_FAILED depending on the failure
*/
int i;
for (i = 0; i < num_other_rv; i++) {
if (rv == other_rv[i]) {
rv = CKR_FUNCTION_FAILED;
goto cleanup;
}
}
/* need to return first rv */
rv = first_rv;
goto cleanup;
}
/*
* always keep a copy of the template for C_CreateObject,
* so clones can be created on other slots if necessary.
* This is done even when the CKA_EXTRACTABLE=FALSE flag
* is set for the object. The supplied template is
* "owned" by metaslot. The application should not be
* penalized just because metaslot choose to try creating
* the object in a slot that's not capable of performing
* any future operation.
*/
rv = get_master_attributes_by_template(pTemplate, ulCount,
&object->attributes, &object->num_attributes);
if (rv == CKR_OK) {
CK_ULONG i;
for (i = 0; i < ulCount; i++) {
rv = attribute_set_value(&(pTemplate[i]),
object->attributes, object->num_attributes);
}
}
meta_object_activate(object);
*phObject = (CK_OBJECT_HANDLE) object;
REFRELEASE(session);
return (CKR_OK);
cleanup:
if (slot_object)
meta_slot_object_dealloc(slot_object);
if (slot_session)
meta_release_slot_session(slot_session);
if (object)
(void) meta_object_dealloc(session, object, B_TRUE);
REFRELEASE(session);
return (rv);
}
/*
* meta_CopyObject
*
*/
CK_RV
meta_CopyObject(CK_SESSION_HANDLE hSession, CK_OBJECT_HANDLE hObject,
CK_ATTRIBUTE_PTR pTemplate, CK_ULONG ulCount,
CK_OBJECT_HANDLE_PTR phNewObject)
{
CK_RV rv, first_rv;
meta_session_t *session;
meta_object_t *src_object, *dst_object = NULL;
slot_session_t *slot_session = NULL;
slot_object_t *dst_slot_object = NULL;
CK_ULONG i;
slot_object_t *src_slot_object;
CK_ULONG slotnum, num_slots;
boolean_t found;
if (pTemplate == NULL && ulCount != 0)
return (CKR_ARGUMENTS_BAD);
if (phNewObject == NULL)
return (CKR_ARGUMENTS_BAD);
rv = meta_handle2session(hSession, &session);
if (rv != CKR_OK)
return (rv);
rv = meta_handle2object(hObject, &src_object);
if (rv != CKR_OK) {
REFRELEASE(session);
return (rv);
}
rv = meta_object_alloc(session, &dst_object);
if (rv != CKR_OK)
goto finish;
found = get_template_boolean(CKA_TOKEN,
pTemplate, ulCount, &(dst_object->isToken));
if (!found) {
dst_object->isToken = src_object->isToken;
if (src_object->isFreeToken == FREE_ENABLED)
dst_object->isToken = TRUE;
else
dst_object->isToken = src_object->isToken;
}
/* Can't create token objects in a read-only session. */
if ((IS_READ_ONLY_SESSION(session->session_flags)) &&
(dst_object->isToken)) {
rv = CKR_SESSION_READ_ONLY;
goto finish;
}
if (dst_object->isToken) {
/*
* if the dst object is a token object, and the source
* object is not, the source object needs to be extractable.
* Otherwise, the source object needs to reside in the
* token object slot
*/
if ((!src_object->isExtractable) &&
(src_object->master_clone_slotnum
!= get_keystore_slotnum())) {
rv = CKR_FUNCTION_FAILED;
goto finish;
}
/* determine if dst is going to be private object or not */
found = get_template_boolean(CKA_PRIVATE,
pTemplate, ulCount, &(dst_object->isPrivate));
if (!found) {
/* will be the same as the source object */
dst_object->isPrivate = src_object->isPrivate;
}
slotnum = get_keystore_slotnum();
} else {
/* try create the obj in the same slot as the source obj */
slotnum = src_object->master_clone_slotnum;
}
rv = meta_slot_object_alloc(&dst_slot_object);
if (rv != CKR_OK)
goto finish;
rv = meta_get_slot_session(slotnum, &slot_session,
session->session_flags);
if (rv != CKR_OK)
goto finish;
rv = meta_object_get_clone(src_object, slotnum,
slot_session, &src_slot_object);
if (rv != CKR_OK)
goto finish;
dst_object->tried_create_clone[slotnum] = B_TRUE;
rv = FUNCLIST(slot_session->fw_st_id)->C_CopyObject(
slot_session->hSession, src_slot_object->hObject, pTemplate,
ulCount, &(dst_slot_object->hObject));
if (rv != CKR_OK) {
if (dst_object->isToken) {
/*
* token obj can only be created in the
* token slot. No need to try anywhere else
*/
goto finish;
}
if ((!src_object->isExtractable) ||
((src_object->isSensitive) && (src_object->isToken) &&
(!metaslot_auto_key_migrate))) {
/* source object isn't clonable in another slot */
goto finish;
}
if (!try_again(rv)) {
goto finish;
}
first_rv = rv;
meta_release_slot_session(slot_session);
slot_session = NULL;
num_slots = meta_slotManager_get_slotcount();
/* Try operation on other slots if the object is clonable */
for (slotnum = 0; slotnum < num_slots; slotnum++) {
if (slotnum == src_object->master_clone_slotnum) {
/* already tried, don't need to try again */
continue;
}
rv = meta_get_slot_session(slotnum, &slot_session,
session->session_flags);
if (rv != CKR_OK) {
goto finish;
}
rv = meta_object_get_clone(src_object, slotnum,
slot_session, &src_slot_object);
if (rv != CKR_OK)
goto finish;
dst_object->tried_create_clone[slotnum] = B_TRUE;
rv = FUNCLIST(slot_session->fw_st_id)->C_CopyObject(
slot_session->hSession, src_slot_object->hObject,
pTemplate, ulCount, &dst_slot_object->hObject);
if (rv == CKR_OK) {
break;
}
if (!try_again(rv)) {
goto finish;
}
meta_release_slot_session(slot_session);
slot_session = NULL;
}
}
if (rv == CKR_OK) {
rv = meta_object_get_attr(slot_session,
dst_slot_object->hObject, dst_object);
if (rv != CKR_OK) {
goto finish;
}
if (src_object->attributes != NULL) {
/* Keep a copy of the template for the future */
/*
* Don't allow attributes to change while
* we look at them.
*/
(void) pthread_rwlock_rdlock(
&src_object->attribute_lock);
rv = get_master_attributes_by_duplication(
src_object->attributes,
src_object->num_attributes,
&dst_object->attributes,
&dst_object->num_attributes);
(void) pthread_rwlock_unlock(
&src_object->attribute_lock);
if (rv != CKR_OK)
goto finish;
for (i = 0; i < ulCount; i++) {
rv = attribute_set_value(pTemplate + i,
dst_object->attributes,
dst_object->num_attributes);
if (rv != CKR_OK)
goto finish;
}
}
/* Allow FreeToken to activate onto token obj list */
if (dst_object->isFreeToken == FREE_ENABLED)
dst_object->isToken = TRUE;
meta_slot_object_activate(dst_slot_object,
slot_session, dst_object->isToken);
dst_object->clones[slotnum] = dst_slot_object;
dst_object->master_clone_slotnum = slotnum;
dst_slot_object = NULL; /* for error cleanup */
meta_release_slot_session(slot_session);
slot_session = NULL; /* for error cleanup */
} else {
/*
* return either first error code or
* CKR_FUNCTION_FAILED depending on the failure
*/
int j;
for (j = 0; j < num_other_rv; j++) {
if (rv == other_rv[j]) {
rv = CKR_FUNCTION_FAILED;
goto finish;
}
}
/* need to return first rv */
rv = first_rv;
goto finish;
}
meta_object_activate(dst_object);
*phNewObject = (CK_OBJECT_HANDLE) dst_object;
finish:
if (rv != CKR_OK) {
if (dst_slot_object)
meta_slot_object_dealloc(dst_slot_object);
if (dst_object)
(void) meta_object_dealloc(session, dst_object,
B_TRUE);
if (slot_session)
meta_release_slot_session(slot_session);
}
OBJRELEASE(src_object);
REFRELEASE(session);
return (rv);
}
/*
* meta_DestroyObject
*
* This function destroys an object by first removing it from the
* list of valid objects for a given session (if session object) or
* the global token object list. And then, calling C_DestroyObject
* on all the slots on which we have created a clone of this object.
*/
CK_RV
meta_DestroyObject(CK_SESSION_HANDLE hSession, CK_OBJECT_HANDLE hObject)
{
CK_RV rv;
meta_session_t *session;
meta_object_t *object;
rv = meta_handle2session(hSession, &session);
if (rv != CKR_OK)
return (rv);
rv = meta_handle2object(hObject, &object);
if (rv != CKR_OK) {
REFRELEASE(session);
return (rv);
}
/* Can't delete token objects from a read-only session. */
if ((IS_READ_ONLY_SESSION(session->session_flags)) &&
(object->isToken || object->isFreeToken == FREE_ENABLED)) {
OBJRELEASE(object);
REFRELEASE(session);
return (CKR_SESSION_READ_ONLY);
}
/* Remove object from list of valid meta_objects */
rv = meta_object_deactivate(object, B_FALSE, B_TRUE);
/*
* Actually call C_DestroyObject on all the slots on which we have
* created a clone of this object.
*/
if (rv == CKR_OK)
rv = meta_object_dealloc(session, object, B_TRUE);
REFRELEASE(session);
return (rv);
}
/*
* meta_GetObjectSize
*
* NOTES:
* 1) Because the "size" is so poorly defined in the spec, we have deemed
* it useless and won't support it. This is especially true for the
* metaslot, because the mulitple providers it uses may each interpret
* the size differently.
*/
/* ARGSUSED */
CK_RV
meta_GetObjectSize(CK_SESSION_HANDLE hSession, CK_OBJECT_HANDLE hObject,
CK_ULONG_PTR pulSize)
{
return (CKR_FUNCTION_NOT_SUPPORTED);
}
/*
* meta_GetAttributeValue
*
*/
CK_RV
meta_GetAttributeValue(CK_SESSION_HANDLE hSession, CK_OBJECT_HANDLE hObject,
CK_ATTRIBUTE_PTR pTemplate, CK_ULONG ulCount)
{
CK_RV rv;
meta_session_t *session;
meta_object_t *object;
CK_ULONG slotnum;
slot_session_t *slot_session;
if (pTemplate == NULL || ulCount < 1)
return (CKR_ARGUMENTS_BAD);
rv = meta_handle2session(hSession, &session);
if (rv != CKR_OK)
return (rv);
rv = meta_handle2object(hObject, &object);
if (rv != CKR_OK) {
REFRELEASE(session);
return (rv);
}
slotnum = object->master_clone_slotnum;
rv = meta_get_slot_session(slotnum, &slot_session,
session->session_flags);
if (rv == CKR_OK) {
rv = FUNCLIST(slot_session->fw_st_id)->C_GetAttributeValue(
slot_session->hSession, object->clones[slotnum]->hObject,
pTemplate, ulCount);
meta_release_slot_session(slot_session);
}
OBJRELEASE(object);
REFRELEASE(session);
return (rv);
}
/*
* meta_SetAttributeValue
*
* Call C_SetAttributeValue on all the clones. If the operation fails on
* all clones, return the failure.
*
* If the operation fails on some clones and not the others, delete all the
* clones that have failed the operation. If any of the deleted clone is the
* master clone, use one of the remaining clone as the master clone.
*
* If the operation is successful and the master template already exists,
* update the master template with new values.
*/
CK_RV
meta_SetAttributeValue(CK_SESSION_HANDLE hSession, CK_OBJECT_HANDLE hObject,
CK_ATTRIBUTE_PTR pTemplate, CK_ULONG ulCount)
{
CK_RV rv = CKR_OK, save_rv = CKR_OK;
meta_session_t *session;
meta_object_t *object;
CK_ULONG slotnum, num_slots;
/* Keep track of which slot's SetAttributeValue failed */
boolean_t *clone_failed_op = NULL;
int num_clones = 0, num_clones_failed = 0;
slot_session_t *slot_session;
slot_object_t *slot_object;
boolean_t need_update_master_clone = B_FALSE;
if (pTemplate == NULL || ulCount < 1)
return (CKR_ARGUMENTS_BAD);
rv = meta_handle2session(hSession, &session);
if (rv != CKR_OK)
return (rv);
rv = meta_handle2object(hObject, &object);
if (rv != CKR_OK) {
REFRELEASE(session);
return (rv);
}
if ((IS_READ_ONLY_SESSION(session->session_flags)) &&
(object->isToken || object->isFreeToken == FREE_ENABLED)) {
rv = CKR_SESSION_READ_ONLY;
goto finish;
}
if ((!object->isExtractable) && (object->attributes == NULL)) {
/*
* object has no clone, just need to do the operation
* in the master clone slot
*/
slot_session_t *slot_session;
slotnum = object->master_clone_slotnum;
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, pTemplate,
ulCount);
meta_release_slot_session(slot_session);
}
goto finish;
}
num_slots = meta_slotManager_get_slotcount();
/*
* object might have clones, need to do operation in all clones
*
* If the C_SetAttributeValue() call fails in a clone, the
* clone that failed the operation can not be deleted right
* away. The clone with the failed operation is recorded, and
* the deletion will happen in a separate loop.
*
* This is necessary because if ALL the clones failed
* C_SetAttributeVAlue(), then, the app's call to C_SetAttributeValue()
* is considered failed, and there shouldn't be any changes to the
* object, none of the clones should be deleted.
* On the other hand, if C_SetAttributeValue() fails in some clones
* and succeeds in other clones, the C_SetAttributeValue() operation
* is considered successful, and those clones that failed the
* operation is deleted.
*/
clone_failed_op = calloc(num_slots, sizeof (boolean_t));
if (clone_failed_op == NULL) {
rv = CKR_HOST_MEMORY;
goto finish;
}
for (slotnum = 0; slotnum < num_slots; slotnum++) {
if (object->clones[slotnum] != NULL) {
num_clones++;
rv = meta_get_slot_session(slotnum, &slot_session,
session->session_flags);
if (rv != CKR_OK) {
goto finish;
}
rv = FUNCLIST(slot_session->fw_st_id)->\
C_SetAttributeValue(slot_session->hSession,
object->clones[slotnum]->hObject, pTemplate,
ulCount);
if (rv != CKR_OK) {
num_clones_failed++;
clone_failed_op[slotnum] = B_TRUE;
if (save_rv == CKR_OK) {
save_rv = rv;
}
}
meta_release_slot_session(slot_session);
}
}
if (num_clones_failed == num_clones) {
/* all operations failed */
rv = save_rv;
goto finish;
}
if (num_clones_failed > 0) {
/*
* C_SetAttributeValue in some of the clones failed.
* Find out which ones failed, and delete the clones
* in those failed slots
*/
for (slotnum = 0; slotnum < num_slots; slotnum++) {
if (clone_failed_op[slotnum]) {
slot_object_t *clone = object->clones[slotnum];
rv = meta_get_slot_session(slotnum,
&slot_session, session->session_flags);
if (rv == CKR_OK) {
(void) FUNCLIST(
slot_session->fw_st_id)->
C_DestroyObject(
slot_session->hSession,
clone->hObject);
meta_release_slot_session(slot_session);
}
meta_slot_object_deactivate(clone);
meta_slot_object_dealloc(clone);
object->clones[slotnum] = NULL;
if (slotnum == object->master_clone_slotnum) {
need_update_master_clone = B_TRUE;
}
}
}
if (need_update_master_clone) {
/* make first available clone the master */
for (slotnum = 0; slotnum < num_slots; slotnum++) {
if (object->clones[slotnum]) {
object->master_clone_slotnum = slotnum;
need_update_master_clone = B_FALSE;
break;
}
}
}
if (need_update_master_clone) {
/*
* something is very wrong, can't continue
* it should never be this case.
*/
rv = CKR_FUNCTION_FAILED;
goto finish;
}
rv = CKR_OK;
}
/*
* Update the attribute information we keep in our metaslot object
*/
slot_object = object->clones[object->master_clone_slotnum];
rv = meta_get_slot_session(object->master_clone_slotnum,
&slot_session, session->session_flags);
if (rv == CKR_OK) {
(void) meta_object_get_attr(slot_session,
slot_object->hObject, object);
meta_release_slot_session(slot_session);
}
/* if there's a copy of the attributes, keep it up to date */
if (object->attributes != NULL) {
CK_ULONG i;
/* Make sure no one else is looking at attributes. */
(void) pthread_rwlock_wrlock(&object->attribute_lock);
for (i = 0; i < ulCount; i++) {
(void) attribute_set_value(pTemplate + i,
object->attributes, object->num_attributes);
}
(void) pthread_rwlock_unlock(&object->attribute_lock);
}
finish:
if (clone_failed_op) {
free(clone_failed_op);
}
OBJRELEASE(object);
REFRELEASE(session);
return (rv);
}
static boolean_t
meta_object_in_list(meta_object_t *obj, meta_object_t **objs_list, int num_objs)
{
int i;
for (i = 0; i < num_objs; i++) {
if (objs_list[i] == obj) {
return (B_TRUE);
}
}
return (B_FALSE);
}
static CK_RV
add_to_search_result(meta_object_t *object, find_objs_info_t *info,
int *num_results_alloc)
{
/*
* allocate space for storing results if the currently
* allocated space is not enough
*/
if (*num_results_alloc <= info->num_matched_objs) {
*num_results_alloc += FIND_OBJ_BUF_SIZE;
info->matched_objs = realloc(info->matched_objs,
sizeof (meta_object_t *) * (*num_results_alloc));
if (info->matched_objs == NULL) {
return (CKR_HOST_MEMORY);
}
}
(info->matched_objs)[(info->num_matched_objs)++] = object;
return (CKR_OK);
}
static CK_RV
process_find_results(CK_OBJECT_HANDLE *results, CK_ULONG num_results,
int *num_results_allocated, find_objs_info_t *info, CK_ULONG slotnum,
boolean_t token_only, slot_session_t *slot_session,
meta_session_t *session)
{
CK_ULONG i;
meta_object_t *object;
CK_RV rv;
for (i = 0; i < num_results; i++) {
object = meta_object_find_by_handle(results[i], slotnum,
token_only);
/*
* a token object is found from the keystore,
* need to create a meta object for it
*/
if (object == NULL) {
slot_object_t *slot_object;
rv = meta_object_alloc(session, &object);
if (rv != CKR_OK) {
return (rv);
}
rv = meta_slot_object_alloc(&slot_object);
if (rv != CKR_OK) {
(void) meta_object_dealloc(session, object,
B_TRUE);
return (rv);
}
slot_object->hObject = results[i];
object->master_clone_slotnum = slotnum;
object->clones[slotnum] = slot_object;
/* get in the attributes we keep in meta_object */
rv = meta_object_get_attr(slot_session,
slot_object->hObject, object);
if (rv != CKR_OK) {
(void) meta_object_dealloc(session, object,
B_TRUE);
return (rv);
}
meta_slot_object_activate(slot_object, slot_session,
B_TRUE);
meta_object_activate(object);
slot_object = NULL;
}
if (!meta_object_in_list(object, info->matched_objs,
info->num_matched_objs)) {
rv = add_to_search_result(object, info,
num_results_allocated);
if (rv != CKR_OK) {
return (rv);
}
}
}
return (CKR_OK);
}
static CK_RV
meta_search_for_objects(meta_session_t *session, find_objs_info_t *info,
slot_session_t *slot_session, CK_ATTRIBUTE_PTR pTemplate,
CK_ULONG ulCount, CK_ULONG slotnum, boolean_t token_only,
int *num_results_alloc)
{
CK_ULONG tmp_num_results;
CK_OBJECT_HANDLE tmp_results[FIND_OBJ_BUF_SIZE];
CK_SESSION_HANDLE hSession = slot_session->hSession;
CK_RV rv;
CK_SLOT_ID fw_st_id = slot_session->fw_st_id;
rv = FUNCLIST(fw_st_id)->C_FindObjectsInit(hSession,
pTemplate, ulCount);
if (rv != CKR_OK) {
return (rv);
}
tmp_num_results = 0;
rv = FUNCLIST(fw_st_id)->C_FindObjects(hSession, tmp_results,
FIND_OBJ_BUF_SIZE, &tmp_num_results);
if (rv != CKR_OK) {
return (rv);
}
rv = process_find_results(tmp_results, tmp_num_results,
num_results_alloc, info, slotnum, token_only,
slot_session, session);
if (rv != CKR_OK) {
return (rv);
}
while (tmp_num_results == FIND_OBJ_BUF_SIZE) {
/* might be more results, need to call C_FindObjects again */
rv = FUNCLIST(fw_st_id)->C_FindObjects(hSession, tmp_results,
FIND_OBJ_BUF_SIZE, &tmp_num_results);
if (rv != CKR_OK) {
return (rv);
}
rv = process_find_results(tmp_results, tmp_num_results,
num_results_alloc, info, slotnum, token_only,
slot_session, session);
if (rv != CKR_OK) {
return (rv);
}
}
rv = FUNCLIST(fw_st_id)->C_FindObjectsFinal(hSession);
return (rv);
}
/*
* meta_FindObjectsInit
*
* This function actually will do ALL the work of searching for objects
* that match all requirements specified in the template.
*
* Objects that matched the template will be stored in the
* session's data structure. When the subsequent C_FindObjects()
* calls are made, results saved will be returned.
*
*/
CK_RV
meta_FindObjectsInit(CK_SESSION_HANDLE hSession, CK_ATTRIBUTE_PTR pTemplate,
CK_ULONG ulCount)
{
CK_RV rv;
meta_session_t *session;
CK_ULONG slot_num = 0;
boolean_t have_token_attr, tokenTrue = B_FALSE;
slot_session_t *slot_find_session = NULL;
int num_results_allocated = 0;
CK_ULONG keystore_slotnum;
rv = meta_handle2session(hSession, &session);
if (rv != CKR_OK)
return (rv);
if ((session->find_objs_info).op_active) {
REFRELEASE(session);
return (CKR_OPERATION_ACTIVE);
}
(session->find_objs_info).op_active = B_TRUE;
REFRELEASE(session);
/* see if the template indicates token object only or not */
have_token_attr = get_template_boolean(CKA_TOKEN, pTemplate, ulCount,
&tokenTrue);
keystore_slotnum = get_keystore_slotnum();
if (have_token_attr && tokenTrue) {
/*
* only interested in token objects, just need to search
* token object slot
*/
rv = meta_get_slot_session(keystore_slotnum,
&slot_find_session, session->session_flags);
if (rv != CKR_OK) {
goto finish;
}
rv = meta_search_for_objects(session,
&(session->find_objs_info), slot_find_session, pTemplate,
ulCount, keystore_slotnum, B_TRUE, &num_results_allocated);
if (rv != CKR_OK) {
goto finish;
}
} else {
CK_ULONG num_slots = meta_slotManager_get_slotcount();
for (slot_num = 0; slot_num < num_slots; slot_num++) {
rv = meta_get_slot_session(slot_num,
&slot_find_session, session->session_flags);
if (rv != CKR_OK) {
goto finish;
}
/*
* if the slot is NOT the token object slot, and
* CKA_TOKEN is not specified, need to specified
* it to be false explicitly. This will prevent
* us from using token objects that doesn't
* belong to the token slot in the case that
* more than one slot supports token objects.
*/
if ((slot_num != keystore_slotnum) &&
(!have_token_attr)) {
CK_BBOOL false = FALSE;
CK_ATTRIBUTE_PTR newTemplate;
newTemplate = malloc((ulCount + 1) *
sizeof (CK_ATTRIBUTE));
if (newTemplate == NULL) {
rv = CKR_HOST_MEMORY;
goto finish;
}
(void) memcpy(newTemplate + 1, pTemplate,
ulCount * sizeof (CK_ATTRIBUTE));
newTemplate[0].type = CKA_TOKEN;
newTemplate[0].pValue = &false;
newTemplate[0].ulValueLen = sizeof (false);
rv = meta_search_for_objects(session,
&(session->find_objs_info),
slot_find_session, newTemplate,
ulCount+1, slot_num, B_FALSE,
&num_results_allocated);
free(newTemplate);
} else {
rv = meta_search_for_objects(session,
&(session->find_objs_info),
slot_find_session, pTemplate, ulCount,
slot_num, B_FALSE,
&num_results_allocated);
}
if (rv != CKR_OK) {
goto finish;
}
meta_release_slot_session(slot_find_session);
slot_find_session = NULL;
}
}
finish:
if (slot_find_session != NULL) {
meta_release_slot_session(slot_find_session);
}
if (rv != CKR_OK) {
(void) pthread_rwlock_wrlock(&session->session_lock);
if (((session->find_objs_info).matched_objs) != NULL) {
free((session->find_objs_info).matched_objs);
}
bzero(&(session->find_objs_info), sizeof (find_objs_info_t));
(void) pthread_rwlock_unlock(&(session->session_lock));
}
return (rv);
}
/*
* meta_FindObjects
*
* This function actually doesn't do any real work in search for the
* matching object. All the work is done in FindObjectsInit(). This
* function will only return the matching objects store in the session's
* "find_objs_info" variable.
*
*/
CK_RV
meta_FindObjects(CK_SESSION_HANDLE hSession, CK_OBJECT_HANDLE_PTR phObject,
CK_ULONG ulMaxObjectCount, CK_ULONG_PTR pulObjectCount)
{
CK_RV rv;
find_objs_info_t *info;
CK_ULONG num_objs_found = 0;
meta_object_t *obj;
meta_session_t *session;
int i;
rv = meta_handle2session(hSession, &session);
if (rv != CKR_OK)
return (rv);
info = &(session->find_objs_info);
if (!(info->op_active)) {
REFRELEASE(session);
return (CKR_OPERATION_NOT_INITIALIZED);
}
for (i = info->next_result_index;
((num_objs_found < ulMaxObjectCount) &&
(i < info->num_matched_objs));
i++) {
obj = info->matched_objs[i];
if (obj != NULL) {
/* sanity check to see if object is still valid */
(void) pthread_rwlock_rdlock(&obj->object_lock);
if (obj->magic_marker == METASLOT_OBJECT_MAGIC) {
phObject[num_objs_found++] =
(CK_OBJECT_HANDLE)obj;
}
(void) pthread_rwlock_unlock(&obj->object_lock);
}
}
info->next_result_index = i;
*pulObjectCount = num_objs_found;
REFRELEASE(session);
return (rv);
}
/*
* meta_FindObjectsFinal
*
*/
CK_RV
meta_FindObjectsFinal(CK_SESSION_HANDLE hSession)
{
CK_RV rv;
find_objs_info_t *info;
meta_session_t *session;
rv = meta_handle2session(hSession, &session);
if (rv != CKR_OK)
return (rv);
info = &(session->find_objs_info);
if (!info->op_active) {
REFRELEASE(session);
return (CKR_OPERATION_NOT_INITIALIZED);
}
if (info->matched_objs) {
free(info->matched_objs);
}
bzero(info, sizeof (find_objs_info_t));
REFRELEASE(session);
return (rv);
}