OpenSolaris_b135/lib/pkcs11/libpkcs11/common/metaObject.c

/*
 * 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);
}