root/usr/src/lib/pkcs11/pkcs11_kernel/common/kernelUtil.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 (c) 2003, 2010, Oracle and/or its affiliates. All rights reserved.
 * Copyright 2018, Joyent, Inc.
 * Copyright 2023 RackTop Systems, Inc.
 */

#include <stdlib.h>
#include <string.h>
#include <strings.h>
#include <stdio.h>
#include <cryptoutil.h>
#include <errno.h>
#include <security/cryptoki.h>
#include <sys/crypto/common.h>
#include <sys/crypto/ioctl.h>
#include "kernelGlobal.h"
#include "kernelObject.h"
#include "kernelSlot.h"

#define ENCODE_ATTR(type, value, len) {         \
        cur_attr->oa_type = type;               \
        (void) memcpy(ptr, value, len);         \
        cur_attr->oa_value = ptr;               \
        cur_attr->oa_value_len = len;           \
        cur_attr++;                             \
}

#define MECH_HASH(type) (((uintptr_t)type) % KMECH_HASHTABLE_SIZE)
/*
 * Serialize writes to the hash table. We don't need a per bucket lock as
 * there are only a few writes and we don't need the lock for reads.
 */
pthread_mutex_t mechhash_mutex = PTHREAD_MUTEX_INITIALIZER;

static CK_RV
kmech_hash_insert(CK_MECHANISM_TYPE type, crypto_mech_type_t kmech)
{
        uint_t h;
        kmh_elem_t *elem, *cur;

        elem = malloc(sizeof (kmh_elem_t));
        if (elem == NULL)
                return (CKR_HOST_MEMORY);

        h = MECH_HASH(type);
        elem->type = type;
        elem->kmech = kmech;

        (void) pthread_mutex_lock(&mechhash_mutex);
        for (cur = kernel_mechhash[h]; cur != NULL; cur = cur->knext) {
                if (type == cur->type) {
                        /* Some other thread beat us to it. */
                        (void) pthread_mutex_unlock(&mechhash_mutex);
                        free(elem);
                        return (CKR_OK);
                }
        }
        elem->knext = kernel_mechhash[h];
        kernel_mechhash[h] = elem;
        (void) pthread_mutex_unlock(&mechhash_mutex);

        return (CKR_OK);
}

CK_RV
kernel_mech(CK_MECHANISM_TYPE type, crypto_mech_type_t *k_number)
{
        crypto_get_mechanism_number_t get_number;
        const char *string;
        CK_RV rv;
        int r;
        kmh_elem_t *elem;
        uint_t h;
        char buf[11];   /* Num chars for representing ulong in ASCII */

        /*
         * Search for an existing entry. No need to lock since we are
         * just a reader and we never free the entries in the hash table.
         */
        h = MECH_HASH(type);
        for (elem = kernel_mechhash[h]; elem != NULL; elem = elem->knext) {
                if (type == elem->type) {
                        *k_number = elem->kmech;
                        return (CKR_OK);
                }
        }

        if (type >= CKM_VENDOR_DEFINED) {
                (void) snprintf(buf, sizeof (buf), "%#lx", type);
                string = buf;
        } else {
                string = pkcs11_mech2str(type);
        }

        if (string == NULL)
                return (CKR_MECHANISM_INVALID);

        get_number.pn_mechanism_string = (char *)string;
        get_number.pn_mechanism_len = strlen(string) + 1;

        while ((r = ioctl(kernel_fd, CRYPTO_GET_MECHANISM_NUMBER,
            &get_number)) < 0) {
                if (errno != EINTR)
                        break;
        }
        if (r < 0) {
                rv = CKR_MECHANISM_INVALID;
        } else {
                if (get_number.pn_return_value != CRYPTO_SUCCESS) {
                        rv = crypto2pkcs11_error_number(
                            get_number.pn_return_value);
                } else {
                        rv = CKR_OK;
                }
        }

        if (rv == CKR_OK) {
                *k_number = get_number.pn_internal_number;
                /* Add this to the hash table */
                (void) kmech_hash_insert(type, *k_number);
        }

        return (rv);
}


/*
 * Return the value of a secret key object.
 * This routine allocates memory for the value.
 * A null pointer is returned on error.
 */
unsigned char *
get_symmetric_key_value(kernel_object_t *key_p)
{
        uint8_t *cipherKey;

        switch (key_p->class) {

        case CKO_SECRET_KEY:

                cipherKey = malloc(OBJ_SEC(key_p)->sk_value_len);
                if (cipherKey == NULL)
                        return (NULL);

                (void) memcpy(cipherKey, OBJ_SEC(key_p)->sk_value,
                    OBJ_SEC(key_p)->sk_value_len);

                return (cipherKey);

        default:
                return (NULL);
        }
}

/*
 * Convert a RSA private key object into a crypto_key structure.
 * Memory is allocated for each attribute stored in the crypto_key
 * structure.  Memory for the crypto_key structure is not
 * allocated.  Attributes can be freed by free_key_attributes().
 */
CK_RV
get_rsa_private_key(kernel_object_t *object_p, crypto_key_t *key)
{
        biginteger_t *big;
        crypto_object_attribute_t *attrs, *cur_attr;
        char *ptr;
        CK_RV rv;

        (void) pthread_mutex_lock(&object_p->object_mutex);
        if (object_p->key_type != CKK_RSA ||
            object_p->class != CKO_PRIVATE_KEY) {
                (void) pthread_mutex_unlock(&object_p->object_mutex);
                return (CKR_ATTRIBUTE_TYPE_INVALID);
        }

        attrs = calloc(1,
            RSA_PRI_ATTR_COUNT * sizeof (crypto_object_attribute_t));
        if (attrs == NULL) {
                (void) pthread_mutex_unlock(&object_p->object_mutex);
                return (CKR_HOST_MEMORY);
        }

        key->ck_format = CRYPTO_KEY_ATTR_LIST;
        key->ck_attrs = attrs;
        cur_attr = attrs;

        /*
         * Allocate memory for each key attribute and set up the value
         * value length.
         */
        key->ck_count = 0;

        /* CKA_MODULUS is required. */
        big = OBJ_PRI_RSA_MOD(object_p);
        if (big->big_value == NULL) {
                rv = CKR_ATTRIBUTE_TYPE_INVALID;
                goto fail_cleanup;
        } else {
                if ((ptr = malloc(big->big_value_len)) == NULL) {
                        rv = CKR_HOST_MEMORY;
                        goto fail_cleanup;
                }
                ENCODE_ATTR(CKA_MODULUS, big->big_value, big->big_value_len);
                key->ck_count++;
        }

        /* CKA_PRIVATE_EXPONENT is required. */
        big = OBJ_PRI_RSA_PRIEXPO(object_p);
        if (big->big_value == NULL) {
                rv = CKR_ATTRIBUTE_TYPE_INVALID;
                goto fail_cleanup;
        } else {
                if ((ptr = malloc(big->big_value_len)) == NULL) {
                        rv = CKR_HOST_MEMORY;
                        goto fail_cleanup;
                }
                ENCODE_ATTR(CKA_PRIVATE_EXPONENT, big->big_value,
                    big->big_value_len);
                key->ck_count++;
        }

        /* CKA_PRIME_1 is optional. */
        big = OBJ_PRI_RSA_PRIME1(object_p);
        if (big->big_value != NULL) {
                if ((ptr = malloc(big->big_value_len)) == NULL) {
                        rv = CKR_HOST_MEMORY;
                        goto fail_cleanup;
                }
                ENCODE_ATTR(CKA_PRIME_1, big->big_value, big->big_value_len);
                key->ck_count++;
        }

        /* CKA_PRIME_2 is optional. */
        big = OBJ_PRI_RSA_PRIME2(object_p);
        if (big->big_value != NULL) {
                if ((ptr = malloc(big->big_value_len)) == NULL) {
                        rv = CKR_HOST_MEMORY;
                        goto fail_cleanup;
                }
                ENCODE_ATTR(CKA_PRIME_2, big->big_value, big->big_value_len);
                key->ck_count++;
        }

        /* CKA_EXPONENT_1 is optional. */
        big = OBJ_PRI_RSA_EXPO1(object_p);
        if (big->big_value != NULL) {
                if ((ptr = malloc(big->big_value_len)) == NULL) {
                        rv = CKR_HOST_MEMORY;
                        goto fail_cleanup;
                }
                ENCODE_ATTR(CKA_EXPONENT_1, big->big_value,
                    big->big_value_len);
                key->ck_count++;
        }

        /* CKA_EXPONENT_2 is optional. */
        big = OBJ_PRI_RSA_EXPO2(object_p);
        if (big->big_value != NULL) {
                if ((ptr = malloc(big->big_value_len)) == NULL) {
                        rv = CKR_HOST_MEMORY;
                        goto fail_cleanup;
                }
                ENCODE_ATTR(CKA_EXPONENT_2, big->big_value,
                    big->big_value_len);
                key->ck_count++;
        }

        /* CKA_COEFFICIENT is optional. */
        big = OBJ_PRI_RSA_COEF(object_p);
        if (big->big_value != NULL) {
                if ((ptr = malloc(big->big_value_len)) == NULL) {
                        rv = CKR_HOST_MEMORY;
                        goto fail_cleanup;
                }
                ENCODE_ATTR(CKA_COEFFICIENT, big->big_value,
                    big->big_value_len);
                key->ck_count++;
        }

        (void) pthread_mutex_unlock(&object_p->object_mutex);
        return (CKR_OK);

fail_cleanup:
        (void) pthread_mutex_unlock(&object_p->object_mutex);
        free_key_attributes(key);
        return (rv);
}

/*
 * Convert a RSA public key object into a crypto_key structure.
 * Memory is allocated for each attribute stored in the crypto_key
 * structure.  Memory for the crypto_key structure is not
 * allocated.  Attributes can be freed by free_key_attributes().
 */
CK_RV
get_rsa_public_key(kernel_object_t *object_p, crypto_key_t *key)
{
        biginteger_t *big;
        crypto_object_attribute_t *attrs, *cur_attr;
        char *ptr;

        (void) pthread_mutex_lock(&object_p->object_mutex);
        if (object_p->key_type != CKK_RSA ||
            object_p->class != CKO_PUBLIC_KEY) {
                (void) pthread_mutex_unlock(&object_p->object_mutex);
                return (CKR_ATTRIBUTE_TYPE_INVALID);
        }

        attrs = calloc(1,
            RSA_PUB_ATTR_COUNT * sizeof (crypto_object_attribute_t));
        if (attrs == NULL) {
                (void) pthread_mutex_unlock(&object_p->object_mutex);
                return (CKR_HOST_MEMORY);
        }

        key->ck_format = CRYPTO_KEY_ATTR_LIST;
        key->ck_count = RSA_PUB_ATTR_COUNT;
        key->ck_attrs = attrs;

        cur_attr = attrs;
        big = OBJ_PUB_RSA_PUBEXPO(object_p);
        if ((ptr = malloc(big->big_value_len)) == NULL)
                goto mem_failure;
        ENCODE_ATTR(CKA_PUBLIC_EXPONENT, big->big_value, big->big_value_len);

        big = OBJ_PUB_RSA_MOD(object_p);
        if ((ptr = malloc(big->big_value_len)) == NULL)
                goto mem_failure;
        ENCODE_ATTR(CKA_MODULUS, big->big_value, big->big_value_len);

        if ((ptr = malloc(sizeof (CK_ULONG))) == NULL)
                goto mem_failure;
        ENCODE_ATTR(CKA_MODULUS_BITS, &OBJ_PUB_RSA_MOD_BITS(object_p),
            sizeof (CK_ULONG));

        (void) pthread_mutex_unlock(&object_p->object_mutex);
        return (CKR_OK);

mem_failure:
        (void) pthread_mutex_unlock(&object_p->object_mutex);
        free_key_attributes(key);
        return (CKR_HOST_MEMORY);
}

/*
 * Free attribute storage in a crypto_key structure.
 */
void
free_key_attributes(crypto_key_t *key)
{
        int i;

        if (key->ck_format == CRYPTO_KEY_ATTR_LIST &&
            (key->ck_count > 0) && key->ck_attrs != NULL) {
                for (i = 0; i < key->ck_count; i++) {
                        freezero(key->ck_attrs[i].oa_value,
                            key->ck_attrs[i].oa_value_len);
                }
                free(key->ck_attrs);
        }
}


/*
 * Convert a DSA private key object into a crypto_key structure.
 * Memory is allocated for each attribute stored in the crypto_key
 * structure.  Memory for the crypto_key structure is not
 * allocated.  Attributes can be freed by free_dsa_key_attributes().
 */
CK_RV
get_dsa_private_key(kernel_object_t *object_p, crypto_key_t *key)
{
        biginteger_t *big;
        crypto_object_attribute_t *attrs, *cur_attr;
        char *ptr;

        (void) pthread_mutex_lock(&object_p->object_mutex);
        if (object_p->key_type != CKK_DSA ||
            object_p->class != CKO_PRIVATE_KEY) {
                (void) pthread_mutex_unlock(&object_p->object_mutex);
                return (CKR_ATTRIBUTE_TYPE_INVALID);
        }

        attrs = calloc(1,
            DSA_ATTR_COUNT * sizeof (crypto_object_attribute_t));
        if (attrs == NULL) {
                (void) pthread_mutex_unlock(&object_p->object_mutex);
                return (CKR_HOST_MEMORY);
        }

        key->ck_format = CRYPTO_KEY_ATTR_LIST;
        key->ck_count = DSA_ATTR_COUNT;
        key->ck_attrs = attrs;

        cur_attr = attrs;
        big = OBJ_PRI_DSA_PRIME(object_p);
        if ((ptr = malloc(big->big_value_len)) == NULL)
                goto mem_failure;
        ENCODE_ATTR(CKA_PRIME, big->big_value, big->big_value_len);

        big = OBJ_PRI_DSA_SUBPRIME(object_p);
        if ((ptr = malloc(big->big_value_len)) == NULL)
                goto mem_failure;
        ENCODE_ATTR(CKA_SUBPRIME, big->big_value, big->big_value_len);

        big = OBJ_PRI_DSA_BASE(object_p);
        if ((ptr = malloc(big->big_value_len)) == NULL)
                goto mem_failure;
        ENCODE_ATTR(CKA_BASE, big->big_value, big->big_value_len);

        big = OBJ_PRI_DSA_VALUE(object_p);
        if ((ptr = malloc(big->big_value_len)) == NULL)
                goto mem_failure;
        ENCODE_ATTR(CKA_VALUE, big->big_value, big->big_value_len);

        (void) pthread_mutex_unlock(&object_p->object_mutex);
        return (CKR_OK);

mem_failure:
        (void) pthread_mutex_unlock(&object_p->object_mutex);
        free_key_attributes(key);
        return (CKR_HOST_MEMORY);
}


/*
 * Convert a DSA public key object into a crypto_key structure.
 * Memory is allocated for each attribute stored in the crypto_key
 * structure.  Memory for the crypto_key structure is not
 * allocated.  Attributes can be freed by free_dsa_key_attributes().
 */
CK_RV
get_dsa_public_key(kernel_object_t *object_p, crypto_key_t *key)
{
        biginteger_t *big;
        crypto_object_attribute_t *attrs, *cur_attr;
        char *ptr;

        (void) pthread_mutex_lock(&object_p->object_mutex);
        if (object_p->key_type != CKK_DSA ||
            object_p->class != CKO_PUBLIC_KEY) {
                (void) pthread_mutex_unlock(&object_p->object_mutex);
                return (CKR_ATTRIBUTE_TYPE_INVALID);
        }

        attrs = calloc(1,
            DSA_ATTR_COUNT * sizeof (crypto_object_attribute_t));
        if (attrs == NULL) {
                (void) pthread_mutex_unlock(&object_p->object_mutex);
                return (CKR_HOST_MEMORY);
        }

        key->ck_format = CRYPTO_KEY_ATTR_LIST;
        key->ck_count = DSA_ATTR_COUNT;
        key->ck_attrs = attrs;

        cur_attr = attrs;
        big = OBJ_PUB_DSA_PRIME(object_p);
        if ((ptr = malloc(big->big_value_len)) == NULL)
                goto mem_failure;
        ENCODE_ATTR(CKA_PRIME, big->big_value, big->big_value_len);

        big = OBJ_PUB_DSA_SUBPRIME(object_p);
        if ((ptr = malloc(big->big_value_len)) == NULL)
                goto mem_failure;
        ENCODE_ATTR(CKA_SUBPRIME, big->big_value, big->big_value_len);

        big = OBJ_PUB_DSA_BASE(object_p);
        if ((ptr = malloc(big->big_value_len)) == NULL)
                goto mem_failure;
        ENCODE_ATTR(CKA_BASE, big->big_value, big->big_value_len);

        big = OBJ_PUB_DSA_VALUE(object_p);
        if ((ptr = malloc(big->big_value_len)) == NULL)
                goto mem_failure;
        ENCODE_ATTR(CKA_VALUE, big->big_value, big->big_value_len);

        (void) pthread_mutex_unlock(&object_p->object_mutex);
        return (CKR_OK);

mem_failure:
        (void) pthread_mutex_unlock(&object_p->object_mutex);
        free_key_attributes(key);
        return (CKR_HOST_MEMORY);
}


/*
 * Convert a EC private key object into a crypto_key structure.
 * Memory is allocated for each attribute stored in the crypto_key
 * structure.  Memory for the crypto_key structure is not
 * allocated.  Attributes can be freed by free_ec_key_attributes().
 */
CK_RV
get_ec_private_key(kernel_object_t *object_p, crypto_key_t *key)
{
        biginteger_t *big;
        crypto_object_attribute_t *attrs, *cur_attr;
        CK_ATTRIBUTE tmp;
        char *ptr;
        int rv;

        (void) pthread_mutex_lock(&object_p->object_mutex);
        if (object_p->key_type != CKK_EC ||
            object_p->class != CKO_PRIVATE_KEY) {
                (void) pthread_mutex_unlock(&object_p->object_mutex);
                return (CKR_ATTRIBUTE_TYPE_INVALID);
        }

        attrs = calloc(EC_ATTR_COUNT, sizeof (crypto_object_attribute_t));
        if (attrs == NULL) {
                (void) pthread_mutex_unlock(&object_p->object_mutex);
                return (CKR_HOST_MEMORY);
        }

        key->ck_format = CRYPTO_KEY_ATTR_LIST;
        key->ck_count = EC_ATTR_COUNT;
        key->ck_attrs = attrs;

        cur_attr = attrs;
        big = OBJ_PRI_EC_VALUE(object_p);
        if ((ptr = malloc(big->big_value_len)) == NULL) {
                rv = CKR_HOST_MEMORY;
                goto fail;
        }
        ENCODE_ATTR(CKA_VALUE, big->big_value, big->big_value_len);

        tmp.type = CKA_EC_PARAMS;
        tmp.pValue = NULL;
        rv = kernel_get_attribute(object_p, &tmp);
        if (rv != CKR_OK) {
                goto fail;
        }

        tmp.pValue = malloc(tmp.ulValueLen);
        if (tmp.pValue == NULL) {
                rv = CKR_HOST_MEMORY;
                goto fail;
        }

        rv = kernel_get_attribute(object_p, &tmp);
        if (rv != CKR_OK) {
                free(tmp.pValue);
                goto fail;
        }

        cur_attr->oa_type = tmp.type;
        cur_attr->oa_value = tmp.pValue;
        cur_attr->oa_value_len = tmp.ulValueLen;

        (void) pthread_mutex_unlock(&object_p->object_mutex);
        return (CKR_OK);

fail:
        (void) pthread_mutex_unlock(&object_p->object_mutex);
        free_key_attributes(key);
        return (rv);
}

/*
 * Convert an EC public key object into a crypto_key structure.
 * Memory is allocated for each attribute stored in the crypto_key
 * structure.  Memory for the crypto_key structure is not
 * allocated.  Attributes can be freed by free_ec_key_attributes().
 */
CK_RV
get_ec_public_key(kernel_object_t *object_p, crypto_key_t *key)
{
        biginteger_t *big;
        crypto_object_attribute_t *attrs, *cur_attr;
        CK_ATTRIBUTE tmp;
        char *ptr;
        int rv;

        (void) pthread_mutex_lock(&object_p->object_mutex);
        if (object_p->key_type != CKK_EC ||
            object_p->class != CKO_PUBLIC_KEY) {
                (void) pthread_mutex_unlock(&object_p->object_mutex);
                return (CKR_ATTRIBUTE_TYPE_INVALID);
        }

        attrs = calloc(EC_ATTR_COUNT, sizeof (crypto_object_attribute_t));
        if (attrs == NULL) {
                (void) pthread_mutex_unlock(&object_p->object_mutex);
                return (CKR_HOST_MEMORY);
        }

        key->ck_format = CRYPTO_KEY_ATTR_LIST;
        key->ck_count = EC_ATTR_COUNT;
        key->ck_attrs = attrs;

        cur_attr = attrs;
        big = OBJ_PUB_EC_POINT(object_p);
        if ((ptr = malloc(big->big_value_len)) == NULL) {
                rv = CKR_HOST_MEMORY;
                goto fail;
        }
        ENCODE_ATTR(CKA_EC_POINT, big->big_value, big->big_value_len);

        tmp.type = CKA_EC_PARAMS;
        tmp.pValue = NULL;
        rv = kernel_get_attribute(object_p, &tmp);
        if (rv != CKR_OK) {
                goto fail;
        }

        tmp.pValue = malloc(tmp.ulValueLen);
        if (tmp.pValue == NULL) {
                rv = CKR_HOST_MEMORY;
                goto fail;
        }

        rv = kernel_get_attribute(object_p, &tmp);
        if (rv != CKR_OK) {
                free(tmp.pValue);
                goto fail;
        }

        cur_attr->oa_type = tmp.type;
        cur_attr->oa_value = tmp.pValue;
        cur_attr->oa_value_len = tmp.ulValueLen;

        (void) pthread_mutex_unlock(&object_p->object_mutex);
        return (CKR_OK);

fail:
        (void) pthread_mutex_unlock(&object_p->object_mutex);
        free_key_attributes(key);
        return (rv);
}

/*
 * Convert an attribute template into an obj_attrs array.
 * Memory is allocated for each attribute stored in the obj_attrs.
 * The memory can be freed by free_object_attributes().
 *
 * If the boolean pointer is_token_obj is not NULL, the caller wants to
 * retrieve the value of the CKA_TOKEN attribute if it is specified in the
 * template.
 * - When this routine is called thru C_CreateObject(), C_CopyObject(), or
 *   any key management function, is_token_obj should NOT be NULL.
 * - When this routine is called thru C_GetAttributeValue() or
 *   C_SetAttributeValue(), "is_token_obj" should be NULL.
 */
CK_RV
process_object_attributes(CK_ATTRIBUTE_PTR pTemplate, CK_ULONG ulCount,
    caddr_t *obj_attrs, CK_BBOOL *is_token_obj)
{
        crypto_object_attribute_t *attrs, *cur_attr;
        int i, cur_i;
        char *ptr;
        CK_RV rv;
        ssize_t value_len;

        if (ulCount == 0) {
                obj_attrs = NULL;
                return (CKR_OK);
        }

        attrs = calloc(1, ulCount * sizeof (crypto_object_attribute_t));
        if (attrs == NULL) {
                return (CKR_HOST_MEMORY);
        }

        cur_attr = attrs;
        for (i = 0; i < ulCount; i++) {
                /*
                 * The length of long attributes must be set correctly
                 * so providers can determine whether they came from 32
                 * or 64-bit applications.
                 */
                switch (pTemplate[i].type) {
                case CKA_CLASS:
                case CKA_CERTIFICATE_TYPE:
                case CKA_KEY_TYPE:
                case CKA_MODULUS_BITS:
                case CKA_HW_FEATURE_TYPE:
                        value_len = sizeof (ulong_t);
                        if (pTemplate[i].pValue != NULL &&
                            (pTemplate[i].ulValueLen < value_len)) {
                                rv = CKR_ATTRIBUTE_VALUE_INVALID;
                                cur_i = i;
                                goto fail_cleanup;
                        }
                        break;
                default:
                        value_len = pTemplate[i].ulValueLen;
                }

                cur_attr->oa_type = pTemplate[i].type;
                cur_attr->oa_value_len = value_len;
                cur_attr->oa_value = NULL;

                if ((pTemplate[i].pValue != NULL) &&
                    (pTemplate[i].ulValueLen > 0)) {
                        ptr = malloc(pTemplate[i].ulValueLen);
                        if (ptr == NULL) {
                                rv = CKR_HOST_MEMORY;
                                cur_i = i;
                                goto fail_cleanup;
                        } else {
                                (void) memcpy(ptr, pTemplate[i].pValue,
                                    pTemplate[i].ulValueLen);
                                cur_attr->oa_value = ptr;
                        }
                }

                if ((is_token_obj != NULL) &&
                    (pTemplate[i].type == CKA_TOKEN)) {
                        /* Get the CKA_TOKEN attribute value. */
                        if (pTemplate[i].pValue == NULL) {
                                rv = CKR_ATTRIBUTE_VALUE_INVALID;
                                cur_i = i;
                                goto fail_cleanup;
                        } else {
                                *is_token_obj =
                                    *(CK_BBOOL *)pTemplate[i].pValue;
                        }
                }

                cur_attr++;
        }

        *obj_attrs = (char *)attrs;
        return (CKR_OK);

fail_cleanup:
        cur_attr = attrs;
        for (i = 0; i < cur_i; i++) {
                if (cur_attr->oa_value != NULL) {
                        (void) free(cur_attr->oa_value);
                }
                cur_attr++;
        }

        (void) free(attrs);
        return (rv);
}


/*
 * Copy the attribute values from obj_attrs to pTemplate.
 * The obj_attrs is an image of the Template and is expected to have the
 * same attributes in the same order and each one of the attribute pValue
 * in obj_attr has enough space allocated for the corresponding valueLen
 * in pTemplate.
 */
CK_RV
get_object_attributes(CK_ATTRIBUTE_PTR pTemplate,  CK_ULONG ulCount,
    caddr_t obj_attrs)
{
        crypto_object_attribute_t *cur_attr;
        CK_RV rv = CKR_OK;
        int i;

        /* LINTED */
        cur_attr = (crypto_object_attribute_t *)obj_attrs;
        for (i = 0; i < ulCount; i++) {
                if (pTemplate[i].type != cur_attr->oa_type) {
                        /* The attribute type doesn't match, this is bad. */
                        rv = CKR_FUNCTION_FAILED;
                        return (rv);
                }

                pTemplate[i].ulValueLen = cur_attr->oa_value_len;

                if ((pTemplate[i].pValue != NULL) &&
                    ((CK_LONG)pTemplate[i].ulValueLen != -1)) {
                        (void) memcpy(pTemplate[i].pValue, cur_attr->oa_value,
                            pTemplate[i].ulValueLen);
                }
                cur_attr++;
        }

        return (rv);
}

/*
 * Free the attribute storage in a crypto_object_attribute_t structure.
 */
void
free_object_attributes(caddr_t obj_attrs, CK_ULONG ulCount)
{
        crypto_object_attribute_t *cur_attr;
        int i;

        if ((ulCount == 0) || (obj_attrs == NULL)) {
                return;
        }

        /* LINTED */
        cur_attr = (crypto_object_attribute_t *)obj_attrs;
        for (i = 0; i < ulCount; i++) {
                /* XXX check that oa_value > 0 */
                if (cur_attr->oa_value != NULL) {
                        free(cur_attr->oa_value);
                }
                cur_attr++;
        }

        free(obj_attrs);
}

/*
 * This function is called by process_found_objects().  It will check the
 * CKA_PRIVATE and CKA_TOKEN attributes for the kernel object "oid", then
 * initialize all the necessary fields in the object wrapper "objp".
 */
static CK_RV
create_new_tobj_in_lib(kernel_slot_t *pslot, kernel_session_t *sp,
    kernel_object_t *objp,  crypto_object_id_t oid)
{
        CK_RV  rv = CKR_OK;
        crypto_object_get_attribute_value_t obj_ga;
        boolean_t is_pri_obj;
        boolean_t is_token_obj;
        CK_BBOOL pri_value, token_value;
        CK_ATTRIBUTE  pTemplate[2];
        int r;

        /*
         * Make a CRYPTO_OBJECT_GET_ATTRIBUTE_VALUE ioctl call to get this
         * kernel object's attribute values for CKA_PRIVATE and CKA_TOKEN.
         */
        obj_ga.og_session = sp->k_session;
        obj_ga.og_handle = oid;
        obj_ga.og_count = 2;

        pTemplate[0].type = CKA_PRIVATE;
        pTemplate[0].pValue = &pri_value;
        pTemplate[0].ulValueLen = sizeof (pri_value);
        pTemplate[1].type = CKA_TOKEN;
        pTemplate[1].pValue = &token_value;
        pTemplate[1].ulValueLen = sizeof (token_value);
        rv = process_object_attributes(pTemplate, 2, &obj_ga.og_attributes,
            NULL);
        if (rv != CKR_OK) {
                return (rv);
        }

        while ((r = ioctl(kernel_fd, CRYPTO_OBJECT_GET_ATTRIBUTE_VALUE,
            &obj_ga)) < 0) {
                if (errno != EINTR)
                        break;
        }
        if (r < 0) {
                rv = CKR_FUNCTION_FAILED;
        } else {
                rv = crypto2pkcs11_error_number(obj_ga.og_return_value);
        }

        if (rv == CKR_OK) {
                rv = get_object_attributes(pTemplate, 2, obj_ga.og_attributes);
                if (rv == CKR_OK) {
                        is_pri_obj = *(CK_BBOOL *)pTemplate[0].pValue;
                        is_token_obj = *(CK_BBOOL *)pTemplate[1].pValue;
                }
        }

        free_object_attributes(obj_ga.og_attributes, 2);
        if (rv != CKR_OK) {
                return (rv);
        }

        /* Make sure it is a token object. */
        if (!is_token_obj) {
                rv = CKR_ATTRIBUTE_VALUE_INVALID;
                return (rv);
        }

        /* If it is a private object, make sure the user has logged in. */
        if (is_pri_obj && (pslot->sl_state != CKU_USER)) {
                rv = CKR_ATTRIBUTE_VALUE_INVALID;
                return (rv);
        }

        objp->is_lib_obj = B_FALSE;
        objp->k_handle = oid;
        objp->bool_attr_mask |= TOKEN_BOOL_ON;
        if (is_pri_obj) {
                objp->bool_attr_mask |= PRIVATE_BOOL_ON;
        } else {
                objp->bool_attr_mask &= ~PRIVATE_BOOL_ON;
        }

        (void) pthread_mutex_init(&objp->object_mutex, NULL);
        objp->magic_marker = KERNELTOKEN_OBJECT_MAGIC;
        objp->session_handle = (CK_SESSION_HANDLE) sp;

        return (CKR_OK);
}

/*
 * This function processes the kernel object handles returned from the
 * CRYPTO_OBJECT_FIND_UPDATE ioctl and returns an object handle list
 * and the number of object handles to the caller - C_FindObjects().
 * The caller acquires the slot lock and the session lock.
 */
CK_RV
process_found_objects(kernel_session_t *cur_sp, CK_OBJECT_HANDLE *obj_found,
    CK_ULONG *found_obj_count, crypto_object_find_update_t obj_fu)
{
        CK_RV rv = CKR_OK;
        crypto_object_id_t  *oid_p;
        kernel_slot_t *pslot;
        kernel_object_t *objp;
        kernel_object_t *objp1;
        kernel_object_t *new_tobj_list = NULL;
        kernel_session_t  *sp;
        CK_ULONG num_obj_found = 0;
        boolean_t is_in_lib;
        int i;

        if (obj_fu.fu_count == 0) {
                *found_obj_count = 0;
                return (CKR_OK);
        }

        pslot = slot_table[cur_sp->ses_slotid];

        /* LINTED */
        oid_p = (crypto_object_id_t *)obj_fu.fu_handles;
        for (i = 0; i < obj_fu.fu_count; i++) {
                is_in_lib = B_FALSE;
                /*
                 * Check if this oid has an object wrapper in the library
                 * already.  First, search the slot's token object list.
                 */
                objp = pslot->sl_tobj_list;
                while (!is_in_lib && objp) {
                        if (objp->k_handle == *oid_p) {
                                is_in_lib = B_TRUE;
                        } else {
                                objp = objp->next;
                        }
                }

                /*
                 * If it is not in the slot's token object list,
                 * search it in all the sessions.
                 */
                if (!is_in_lib) {
                        sp = pslot->sl_sess_list;
                        while (!is_in_lib && sp) {
                                objp = sp->object_list;
                                while (!is_in_lib && objp) {
                                        if (objp->k_handle == *oid_p) {
                                                is_in_lib = B_TRUE;
                                        } else {
                                                objp = objp->next;
                                        }
                                }
                                sp = sp->next;
                        }
                }

                /*
                 * If this object is in the library already, add its object
                 * wrapper to the returned find object list.
                 */
                if (is_in_lib) {
                        obj_found[num_obj_found++] = (CK_OBJECT_HANDLE)objp;
                }

                /*
                 * If we still do not find it in the library.  This object
                 * must be a token object pre-existed in the HW provider.
                 * We need to create an object wrapper for it in the library.
                 */
                if (!is_in_lib) {
                        objp1 = calloc(1, sizeof (kernel_object_t));
                        if (objp1 == NULL) {
                                rv = CKR_HOST_MEMORY;
                                goto failed_exit;
                        }
                        rv = create_new_tobj_in_lib(pslot, cur_sp, objp1,
                            *oid_p);

                        if (rv == CKR_OK) {
                                /* Save the new object to the new_tobj_list. */
                                if (new_tobj_list == NULL) {
                                        new_tobj_list = objp1;
                                        objp1->next = NULL;
                                        objp1->prev = NULL;
                                } else {
                                        new_tobj_list->prev = objp1;
                                        objp1->next = new_tobj_list;
                                        objp1->prev = NULL;
                                        new_tobj_list = objp1;
                                }
                        } else {
                                /*
                                 * If create_new_tobj_in_lib() doesn't fail
                                 * with CKR_HOST_MEMORY, the failure should be
                                 * caused by the attributes' checking. We will
                                 * just ignore this object and continue on.
                                 */
                                free(objp1);
                                if (rv == CKR_HOST_MEMORY) {
                                        goto failed_exit;
                                }
                        }
                }

                /* Process next one */
                oid_p++;
        }

        /*
         * Add the newly created token object wrappers to the found object
         * list and to the slot's token object list.
         */
        if (new_tobj_list != NULL) {
                /* Add to the obj_found array. */
                objp = new_tobj_list;
                while (objp) {
                        obj_found[num_obj_found++] = (CK_OBJECT_HANDLE)objp;
                        if (objp->next == NULL) {
                                break;
                        }
                        objp = objp->next;
                }

                /* Add to the beginning of the slot's token object list. */
                if (pslot->sl_tobj_list != NULL) {
                        objp->next = pslot->sl_tobj_list;
                        pslot->sl_tobj_list->prev = objp;
                }
                pslot->sl_tobj_list = new_tobj_list;
        }

        *found_obj_count = num_obj_found;
        return (CKR_OK);

failed_exit:

        /* Free the newly created token object wrappers. */
        objp = new_tobj_list;
        while (objp) {
                objp1 = objp->next;
                (void) pthread_mutex_destroy(&objp->object_mutex);
                free(objp);
                objp = objp1;
        }

        return (rv);
}


/*
 * Get the value of the CKA_PRIVATE attribute for the object just returned
 * from the HW provider.  This function will be called by any function
 * that creates a new object, because the CKA_PRIVATE value of an object is
 * token specific.  The CKA_PRIVATE attribute value of the new object will be
 * stored in the object structure in the library, which will be used later at
 * C_Logout to clean up all private objects.
 */
CK_RV
get_cka_private_value(kernel_session_t *sp, crypto_object_id_t oid,
    CK_BBOOL *is_pri_obj)
{
        CK_RV  rv = CKR_OK;
        crypto_object_get_attribute_value_t obj_ga;
        crypto_object_attribute_t obj_attr;
        CK_BBOOL pri_value;
        int r;

        obj_ga.og_session = sp->k_session;
        obj_ga.og_handle = oid;
        obj_ga.og_count = 1;

        obj_attr.oa_type = CKA_PRIVATE;
        obj_attr.oa_value = (char *)&pri_value;
        obj_attr.oa_value_len = sizeof (CK_BBOOL);
        obj_ga.og_attributes = (char *)&obj_attr;

        while ((r = ioctl(kernel_fd, CRYPTO_OBJECT_GET_ATTRIBUTE_VALUE,
            &obj_ga)) < 0) {
                if (errno != EINTR)
                        break;
        }
        if (r < 0) {
                rv = CKR_FUNCTION_FAILED;
        } else {
                rv = crypto2pkcs11_error_number(obj_ga.og_return_value);
        }

        if (rv == CKR_OK) {
                *is_pri_obj = *(CK_BBOOL *)obj_attr.oa_value;
        }

        return (rv);
}


CK_RV
get_mechanism_info(kernel_slot_t *pslot, CK_MECHANISM_TYPE type,
    CK_MECHANISM_INFO_PTR pInfo, uint32_t *k_mi_flags)
{
        crypto_get_provider_mechanism_info_t mechanism_info;
        const char *string;
        CK_FLAGS flags, mi_flags;
        CK_RV rv;
        int r;
        char buf[11];   /* Num chars for representing ulong in ASCII */

        if (type >= CKM_VENDOR_DEFINED) {
                /* allocate/build a string containing the mechanism number */
                (void) snprintf(buf, sizeof (buf), "%#lx", type);
                string = buf;
        } else {
                string = pkcs11_mech2str(type);
        }

        if (string == NULL)
                return (CKR_MECHANISM_INVALID);

        (void) strcpy(mechanism_info.mi_mechanism_name, string);
        mechanism_info.mi_provider_id = pslot->sl_provider_id;

        while ((r = ioctl(kernel_fd, CRYPTO_GET_PROVIDER_MECHANISM_INFO,
            &mechanism_info)) < 0) {
                if (errno != EINTR)
                        break;
        }
        if (r < 0) {
                rv = CKR_FUNCTION_FAILED;
        } else {
                rv = crypto2pkcs11_error_number(
                    mechanism_info.mi_return_value);
        }

        if (rv != CKR_OK) {
                return (rv);
        }

        /*
         * Atomic flags are not part of PKCS#11 so we filter
         * them out here.
         * Neither is CRYPTO_FG_MAC.
         */
        mi_flags = mechanism_info.mi_flags;
        mi_flags &= ~(CRYPTO_FG_DIGEST_ATOMIC | CRYPTO_FG_ENCRYPT_ATOMIC |
            CRYPTO_FG_DECRYPT_ATOMIC | CRYPTO_FG_MAC_ATOMIC |
            CRYPTO_FG_SIGN_ATOMIC | CRYPTO_FG_VERIFY_ATOMIC |
            CRYPTO_FG_SIGN_RECOVER_ATOMIC |
            CRYPTO_FG_VERIFY_RECOVER_ATOMIC |
            CRYPTO_FG_ENCRYPT_MAC_ATOMIC |
            CRYPTO_FG_MAC_DECRYPT_ATOMIC |
            CRYPTO_FG_MAC);

        if (mi_flags == 0) {
                return (CKR_MECHANISM_INVALID);
        }

        if (rv == CKR_OK) {
                /* set the value of k_mi_flags first */
                *k_mi_flags = mi_flags;

                /* convert KEF flags into pkcs11 flags */
                flags = CKF_HW;
                if (mi_flags & CRYPTO_FG_ENCRYPT)
                        flags |= CKF_ENCRYPT;
                if (mi_flags & CRYPTO_FG_DECRYPT) {
                        flags |= CKF_DECRYPT;
                        /*
                         * Since we'll be emulating C_UnwrapKey() for some
                         * cases, we can go ahead and claim CKF_UNWRAP
                         */
                        flags |= CKF_UNWRAP;
                }
                if (mi_flags & CRYPTO_FG_DIGEST)
                        flags |= CKF_DIGEST;
                if (mi_flags & CRYPTO_FG_SIGN)
                        flags |= CKF_SIGN;
                if (mi_flags & CRYPTO_FG_SIGN_RECOVER)
                        flags |= CKF_SIGN_RECOVER;
                if (mi_flags & CRYPTO_FG_VERIFY)
                        flags |= CKF_VERIFY;
                if (mi_flags & CRYPTO_FG_VERIFY_RECOVER)
                        flags |= CKF_VERIFY_RECOVER;
                if (mi_flags & CRYPTO_FG_GENERATE)
                        flags |= CKF_GENERATE;
                if (mi_flags & CRYPTO_FG_GENERATE_KEY_PAIR)
                        flags |= CKF_GENERATE_KEY_PAIR;
                if (mi_flags & CRYPTO_FG_WRAP)
                        flags |= CKF_WRAP;
                if (mi_flags & CRYPTO_FG_UNWRAP)
                        flags |= CKF_UNWRAP;
                if (mi_flags & CRYPTO_FG_DERIVE)
                        flags |= CKF_DERIVE;

                pInfo->ulMinKeySize = mechanism_info.mi_min_key_size;
                pInfo->ulMaxKeySize = mechanism_info.mi_max_key_size;
                pInfo->flags = flags;

        }

        return (rv);
}

/*
 * Unfortunately the kernel and PKCS#11 use a slightly different struct to
 * specify CCM parameters.
 */
void
p11_to_kernel_ccm_params(const CK_CCM_PARAMS *in, CK_AES_CCM_PARAMS *out)
{
        out->ulMACSize = in->ulMACLen;
        out->ulNonceSize = in->ulNonceLen;
        out->ulAuthDataSize = in->ulAADLen;
        out->ulDataSize = in->ulDataLen;
        out->nonce = in->pNonce;
        out->authData = in->pAAD;
}