# # 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 2006 Sun Microsystems, Inc. All rights reserved. # Use is subject to license terms. # include $(SRC)/lib/Makefile.lib # Hammerhead: amd64-only SUBDIRS= $(MACH64) all : TARGET= all check : TARGET= check clean : TARGET= clean clobber : TARGET= clobber install : TARGET= install .KEEP_STATE: all clean clobber install: $(SUBDIRS) $(SUBDIRS): FRC @cd $@; pwd; $(MAKE) $(TARGET) FRC: # # 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 2007 Sun Microsystems, Inc. All rights reserved. # Use is subject to license terms. # # Copyright (c) 2018, Joyent, Inc. LIBRARY= pkcs11_kernel.a VERS= .1 CORE_OBJECTS= \ kernelGeneral.o \ kernelSlottable.o \ kernelSlotToken.o \ kernelObject.o \ kernelDigest.o \ kernelSign.o \ kernelVerify.o \ kernelDualCrypt.o \ kernelKeys.o \ kernelRand.o \ kernelSession.o \ kernelSessionUtil.o \ kernelUtil.o \ kernelEncrypt.o \ kernelDecrypt.o \ kernelObjectUtil.o \ kernelAttributeUtil.o \ kernelEmulate.o OTHER_OBJECTS = kernelSoftCommon.o ST_OBJECTS = softDigestUtil.o softMAC.o OBJECTS= \ $(CORE_OBJECTS) \ $(OTHER_OBJECTS) \ $(ST_OBJECTS) AESDIR= $(SRC)/common/crypto/aes ARCFOURDIR= $(SRC)/common/crypto/arcfour BLOWFISHDIR= $(SRC)/common/crypto/blowfish DESDIR= $(SRC)/common/crypto/des ECCDIR= $(SRC)/common/crypto/ecc CRYPTDIR= $(SRC)/common/crypto ST_DIR= $(SRC)/lib/pkcs11/pkcs11_softtoken/common # Hammerhead: converted target-specific variables to GNU Make syntax lint pics/kernelAttributeUtil.o: CPPFLAGS += -I$(AESDIR) -I$(BLOWFISHDIR) -I$(ARCFOURDIR) -I$(DESDIR) -I$(ECCDIR) -I$(CRYPTDIR) pics/kernelKeys.o: CPPFLAGS += -I$(ECCDIR) pics/kernelSoftCommon.o: CPPFLAGS = -I$(ST_DIR) $(CPPFLAGS.master) include $(SRC)/lib/Makefile.lib # set signing mode POST_PROCESS_SO += ; $(ELFSIGN_CRYPTO) SRCDIR= ../common CORESRCS = \ $(CORE_OBJECTS:%.o=$(SRCDIR)/%.c) LIBS = $(DYNLIB) LDLIBS += -lc -lcryptoutil -lmd # Hammerhead: GNU ld needs -Bsymbolic so provider's C_* function table # binds to its own definitions, not libpkcs11's (which are already in # global scope when the provider is dlopen'd). Without this, the # CK_FUNCTION_LIST relocations resolve to libpkcs11's C_Initialize # causing infinite recursion and stack overflow. DYNFLAGS += -Wl,-Bsymbolic CFLAGS += $(CCVERBOSE) CERRWARN += $(CNOWARN_UNINIT) # not linted SMATCH=off ROOTLIBDIR= $(ROOT)/usr/lib/security # Hammerhead: 64-bit only - flatten security library path ROOTLIBDIR64= $(ROOT)/usr/lib/security .KEEP_STATE: all: $(LIBS) pics/%.o: $(ST_DIR)/%.c $(COMPILE.c) -o $@ $< -I$(ST_DIR) $(POST_PROCESS_O) include $(SRC)/lib/Makefile.targ # # CDDL HEADER START # # The contents of this file are subject to the terms of the # Common Development and Distribution License, Version 1.0 only # (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 2004 Sun Microsystems, Inc. All rights reserved. # Use is subject to license terms. # # ident "%Z%%M% %I% %E% SMI" # include ../Makefile.com include ../../../Makefile.lib.64 install: all $(ROOTLIBS64) $(ROOTLINKS64) /* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (the "License"). * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright 2008 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. * Copyright 2012 Milan Jurik. All rights reserved. */ #include #include #include #include #include #include #include #include #include "kernelGlobal.h" #include "kernelObject.h" #include "kernelSession.h" #include "kernelSlot.h" /* * This attribute table is used by the kernel_lookup_attr() * to validate the attributes. */ CK_ATTRIBUTE_TYPE attr_map[] = { CKA_PRIVATE, CKA_LABEL, CKA_APPLICATION, CKA_OBJECT_ID, CKA_CERTIFICATE_TYPE, CKA_ISSUER, CKA_SERIAL_NUMBER, CKA_AC_ISSUER, CKA_OWNER, CKA_ATTR_TYPES, CKA_SUBJECT, CKA_ID, CKA_SENSITIVE, CKA_START_DATE, CKA_END_DATE, CKA_MODULUS, CKA_MODULUS_BITS, CKA_PUBLIC_EXPONENT, CKA_PRIVATE_EXPONENT, CKA_PRIME_1, CKA_PRIME_2, CKA_EXPONENT_1, CKA_EXPONENT_2, CKA_COEFFICIENT, CKA_PRIME, CKA_SUBPRIME, CKA_BASE, CKA_EXTRACTABLE, CKA_LOCAL, CKA_NEVER_EXTRACTABLE, CKA_ALWAYS_SENSITIVE, CKA_MODIFIABLE, CKA_ECDSA_PARAMS, CKA_EC_POINT, CKA_SECONDARY_AUTH, CKA_AUTH_PIN_FLAGS, CKA_HW_FEATURE_TYPE, CKA_RESET_ON_INIT, CKA_HAS_RESET }; /* * attributes that exists only in public key objects * Note: some attributes may also exist in one or two * other object classes, but they are also listed * because not all object have them. */ CK_ATTRIBUTE_TYPE PUB_KEY_ATTRS[] = { CKA_SUBJECT, CKA_ENCRYPT, CKA_WRAP, CKA_VERIFY, CKA_VERIFY_RECOVER, CKA_MODULUS, CKA_MODULUS_BITS, CKA_PUBLIC_EXPONENT, CKA_PRIME, CKA_SUBPRIME, CKA_BASE, CKA_TRUSTED, CKA_ECDSA_PARAMS, CKA_EC_PARAMS, CKA_EC_POINT }; /* * attributes that exists only in private key objects * Note: some attributes may also exist in one or two * other object classes, but they are also listed * because not all object have them. */ CK_ATTRIBUTE_TYPE PRIV_KEY_ATTRS[] = { CKA_DECRYPT, CKA_UNWRAP, CKA_SIGN, CKA_SIGN_RECOVER, CKA_MODULUS, CKA_PUBLIC_EXPONENT, CKA_PRIVATE_EXPONENT, CKA_PRIME, CKA_SUBPRIME, CKA_BASE, CKA_PRIME_1, CKA_PRIME_2, CKA_EXPONENT_1, CKA_EXPONENT_2, CKA_COEFFICIENT, CKA_VALUE_BITS, CKA_SUBJECT, CKA_SENSITIVE, CKA_EXTRACTABLE, CKA_NEVER_EXTRACTABLE, CKA_ALWAYS_SENSITIVE, CKA_ECDSA_PARAMS, CKA_EC_PARAMS }; /* * attributes that exists only in secret key objects * Note: some attributes may also exist in one or two * other object classes, but they are also listed * because not all object have them. */ CK_ATTRIBUTE_TYPE SECRET_KEY_ATTRS[] = { CKA_VALUE_LEN, CKA_ENCRYPT, CKA_DECRYPT, CKA_WRAP, CKA_UNWRAP, CKA_SIGN, CKA_VERIFY, CKA_SENSITIVE, CKA_EXTRACTABLE, CKA_NEVER_EXTRACTABLE, CKA_ALWAYS_SENSITIVE }; /* * attributes that exists only in domain parameter objects * Note: some attributes may also exist in one or two * other object classes, but they are also listed * because not all object have them. */ CK_ATTRIBUTE_TYPE DOMAIN_ATTRS[] = { CKA_PRIME, CKA_SUBPRIME, CKA_BASE, CKA_PRIME_BITS, CKA_SUBPRIME_BITS, CKA_SUB_PRIME_BITS }; /* * attributes that exists only in hardware feature objects */ CK_ATTRIBUTE_TYPE HARDWARE_ATTRS[] = { CKA_HW_FEATURE_TYPE, CKA_RESET_ON_INIT, CKA_HAS_RESET }; /* * attributes that exists only in certificate objects */ CK_ATTRIBUTE_TYPE CERT_ATTRS[] = { CKA_CERTIFICATE_TYPE, CKA_SUBJECT, CKA_ID, CKA_ISSUER, CKA_AC_ISSUER, CKA_SERIAL_NUMBER, CKA_OWNER, CKA_ATTR_TYPES }; /* * Validate the attribute by using binary search algorithm. */ CK_RV kernel_lookup_attr(CK_ATTRIBUTE_TYPE type) { size_t lower, middle, upper; lower = 0; upper = (sizeof (attr_map) / sizeof (CK_ATTRIBUTE_TYPE)) - 1; while (lower <= upper) { /* Always starts from middle. */ middle = (lower + upper) / 2; if (type > attr_map[middle]) { /* Adjust the lower bound to upper half. */ lower = middle + 1; continue; } if (type == attr_map[middle]) { /* Found it. */ return (CKR_OK); } if (type < attr_map[middle]) { /* Adjust the upper bound to lower half. */ upper = middle - 1; continue; } } /* Failed to find the matching attribute from the attribute table. */ return (CKR_ATTRIBUTE_TYPE_INVALID); } /* * Validate the attribute by using the following search algorithm: * * 1) Search for the most frequently used attributes first. * 2) If not found, search for the usage-purpose attributes - these * attributes have dense set of values, therefore compiler will * optimize it with a branch table and branch to the appropriate * case. * 3) If still not found, use binary search for the rest of the * attributes in the attr_map[] table. */ CK_RV kernel_validate_attr(CK_ATTRIBUTE_PTR template, CK_ULONG ulAttrNum, CK_OBJECT_CLASS *class) { CK_ULONG i; CK_RV rv = CKR_OK; for (i = 0; i < ulAttrNum; i++) { /* First tier search */ switch (template[i].type) { case CKA_CLASS: *class = *((CK_OBJECT_CLASS*)template[i].pValue); break; case CKA_TOKEN: break; case CKA_KEY_TYPE: break; case CKA_VALUE: break; case CKA_VALUE_LEN: break; case CKA_VALUE_BITS: break; default: /* Second tier search */ switch (template[i].type) { case CKA_ENCRYPT: break; case CKA_DECRYPT: break; case CKA_WRAP: break; case CKA_UNWRAP: break; case CKA_SIGN: break; case CKA_SIGN_RECOVER: break; case CKA_VERIFY: break; case CKA_VERIFY_RECOVER: break; case CKA_DERIVE: break; default: /* Third tier search */ rv = kernel_lookup_attr(template[i].type); if (rv != CKR_OK) return (rv); break; } break; } } return (rv); } /* * Clean up and release all the storage in the extra attribute list * of an object. */ void kernel_cleanup_extra_attr(kernel_object_t *object_p) { CK_ATTRIBUTE_INFO_PTR extra_attr; CK_ATTRIBUTE_INFO_PTR tmp; extra_attr = object_p->extra_attrlistp; while (extra_attr) { tmp = extra_attr->next; if (extra_attr->attr.pValue) /* * All extra attributes in the extra attribute * list have pValue points to the value of the * attribute (with simple byte array type). * Free the storage for the value of the attribute. */ free(extra_attr->attr.pValue); /* Free the storage for the attribute_info struct. */ free(extra_attr); extra_attr = tmp; } object_p->extra_attrlistp = NULL; } /* * Create the attribute_info struct to hold the object's attribute, * and add it to the extra attribute list of an object. */ CK_RV kernel_add_extra_attr(CK_ATTRIBUTE_PTR template, kernel_object_t *object_p) { CK_ATTRIBUTE_INFO_PTR attrp; /* Allocate the storage for the attribute_info struct. */ attrp = calloc(1, sizeof (attribute_info_t)); if (attrp == NULL) { return (CKR_HOST_MEMORY); } /* Set up attribute_info struct. */ attrp->attr.type = template->type; attrp->attr.ulValueLen = template->ulValueLen; if ((template->pValue != NULL) && (template->ulValueLen > 0)) { /* Allocate storage for the value of the attribute. */ attrp->attr.pValue = malloc(template->ulValueLen); if (attrp->attr.pValue == NULL) { free(attrp); return (CKR_HOST_MEMORY); } (void) memcpy(attrp->attr.pValue, template->pValue, template->ulValueLen); } else { attrp->attr.pValue = NULL; } /* Insert the new attribute in front of extra attribute list. */ if (object_p->extra_attrlistp == NULL) { object_p->extra_attrlistp = attrp; attrp->next = NULL; } else { attrp->next = object_p->extra_attrlistp; object_p->extra_attrlistp = attrp; } return (CKR_OK); } /* * Copy the attribute_info struct from the old object to a new attribute_info * struct, and add that new struct to the extra attribute list of the new * object. */ CK_RV kernel_copy_extra_attr(CK_ATTRIBUTE_INFO_PTR old_attrp, kernel_object_t *object_p) { CK_ATTRIBUTE_INFO_PTR attrp; /* Allocate attribute_info struct. */ attrp = calloc(1, sizeof (attribute_info_t)); if (attrp == NULL) { return (CKR_HOST_MEMORY); } attrp->attr.type = old_attrp->attr.type; attrp->attr.ulValueLen = old_attrp->attr.ulValueLen; if ((old_attrp->attr.pValue != NULL) && (old_attrp->attr.ulValueLen > 0)) { attrp->attr.pValue = malloc(old_attrp->attr.ulValueLen); if (attrp->attr.pValue == NULL) { free(attrp); return (CKR_HOST_MEMORY); } (void) memcpy(attrp->attr.pValue, old_attrp->attr.pValue, old_attrp->attr.ulValueLen); } else { attrp->attr.pValue = NULL; } /* Insert the new attribute in front of extra attribute list */ if (object_p->extra_attrlistp == NULL) { object_p->extra_attrlistp = attrp; attrp->next = NULL; } else { attrp->next = object_p->extra_attrlistp; object_p->extra_attrlistp = attrp; } return (CKR_OK); } /* * Get the attribute triple from the extra attribute list in the object * (if the specified attribute type is found), and copy it to a template. * Note the type of the attribute to be copied is specified by the template, * and the storage is pre-allocated for the atrribute value in the template * for doing the copy. */ CK_RV get_extra_attr_from_object(kernel_object_t *object_p, CK_ATTRIBUTE_PTR template) { CK_ATTRIBUTE_INFO_PTR extra_attr; CK_ATTRIBUTE_TYPE type = template->type; extra_attr = object_p->extra_attrlistp; while (extra_attr) { if (type == extra_attr->attr.type) { /* Found it. */ break; } else { /* Does not match, try next one. */ extra_attr = extra_attr->next; } } if (extra_attr == NULL) { /* A valid but un-initialized attribute. */ template->ulValueLen = 0; return (CKR_OK); } /* * We found the attribute in the extra attribute list. */ if (template->pValue == NULL) { template->ulValueLen = extra_attr->attr.ulValueLen; return (CKR_OK); } if (template->ulValueLen >= extra_attr->attr.ulValueLen) { /* * The buffer provided by the application is large * enough to hold the value of the attribute. */ (void) memcpy(template->pValue, extra_attr->attr.pValue, extra_attr->attr.ulValueLen); template->ulValueLen = extra_attr->attr.ulValueLen; return (CKR_OK); } else { /* * The buffer provided by the application does * not have enough space to hold the value. */ template->ulValueLen = (CK_ULONG)-1; return (CKR_BUFFER_TOO_SMALL); } } /* * Modify the attribute triple in the extra attribute list of the object * if the specified attribute type is found. Otherwise, just add it to * list. */ CK_RV set_extra_attr_to_object(kernel_object_t *object_p, CK_ATTRIBUTE_TYPE type, CK_ATTRIBUTE_PTR template) { CK_ATTRIBUTE_INFO_PTR extra_attr; extra_attr = object_p->extra_attrlistp; while (extra_attr) { if (type == extra_attr->attr.type) { /* Found it. */ break; } else { /* Does not match, try next one. */ extra_attr = extra_attr->next; } } if (extra_attr == NULL) { /* * This attribute is a new one, go ahead adding it to * the extra attribute list. */ return (kernel_add_extra_attr(template, object_p)); } /* We found the attribute in the extra attribute list. */ if ((template->pValue != NULL) && (template->ulValueLen > 0)) { if (template->ulValueLen > extra_attr->attr.ulValueLen) { /* The old buffer is too small to hold the new value. */ if (extra_attr->attr.pValue != NULL) /* Free storage for the old attribute value. */ free(extra_attr->attr.pValue); /* Allocate storage for the new attribute value. */ extra_attr->attr.pValue = malloc(template->ulValueLen); if (extra_attr->attr.pValue == NULL) { return (CKR_HOST_MEMORY); } } /* Replace the attribute with new value. */ extra_attr->attr.ulValueLen = template->ulValueLen; (void) memcpy(extra_attr->attr.pValue, template->pValue, template->ulValueLen); } else { extra_attr->attr.pValue = NULL; } return (CKR_OK); } /* * Copy the big integer attribute value from template to a biginteger_t struct. */ CK_RV get_bigint_attr_from_template(biginteger_t *big, CK_ATTRIBUTE_PTR template) { if ((template->pValue != NULL) && (template->ulValueLen > 0)) { /* Allocate storage for the value of the attribute. */ big->big_value = malloc(template->ulValueLen); if (big->big_value == NULL) { return (CKR_HOST_MEMORY); } (void) memcpy(big->big_value, template->pValue, template->ulValueLen); big->big_value_len = template->ulValueLen; } else { big->big_value = NULL; big->big_value_len = 0; } return (CKR_OK); } /* * Copy the big integer attribute value from a biginteger_t struct in the * object to a template. */ CK_RV get_bigint_attr_from_object(biginteger_t *big, CK_ATTRIBUTE_PTR template) { if (template->pValue == NULL) { template->ulValueLen = big->big_value_len; return (CKR_OK); } if (big->big_value == NULL) { template->ulValueLen = 0; return (CKR_OK); } if (template->ulValueLen >= big->big_value_len) { /* * The buffer provided by the application is large * enough to hold the value of the attribute. */ (void) memcpy(template->pValue, big->big_value, big->big_value_len); template->ulValueLen = big->big_value_len; return (CKR_OK); } else { /* * The buffer provided by the application does * not have enough space to hold the value. */ template->ulValueLen = (CK_ULONG)-1; return (CKR_BUFFER_TOO_SMALL); } } /* * Copy the boolean data type attribute value from an object for the * specified attribute to the template. */ CK_RV get_bool_attr_from_object(kernel_object_t *object_p, CK_ULONG bool_flag, CK_ATTRIBUTE_PTR template) { if (template->pValue == NULL) { template->ulValueLen = sizeof (CK_BBOOL); return (CKR_OK); } if (template->ulValueLen >= sizeof (CK_BBOOL)) { /* * The buffer provided by the application is large * enough to hold the value of the attribute. */ if (object_p->bool_attr_mask & bool_flag) { *((CK_BBOOL *)template->pValue) = B_TRUE; } else { *((CK_BBOOL *)template->pValue) = B_FALSE; } template->ulValueLen = sizeof (CK_BBOOL); return (CKR_OK); } else { /* * The buffer provided by the application does * not have enough space to hold the value. */ template->ulValueLen = (CK_ULONG)-1; return (CKR_BUFFER_TOO_SMALL); } } /* * Set the boolean data type attribute value in the object. */ CK_RV set_bool_attr_to_object(kernel_object_t *object_p, CK_ULONG bool_flag, CK_ATTRIBUTE_PTR template) { if (*(CK_BBOOL *)template->pValue) object_p->bool_attr_mask |= bool_flag; else object_p->bool_attr_mask &= ~bool_flag; return (CKR_OK); } /* * Copy the CK_ULONG data type attribute value from an object to the * template. */ CK_RV get_ulong_attr_from_object(CK_ULONG value, CK_ATTRIBUTE_PTR template) { if (template->pValue == NULL) { template->ulValueLen = sizeof (CK_ULONG); return (CKR_OK); } if (template->ulValueLen >= sizeof (CK_ULONG)) { /* * The buffer provided by the application is large * enough to hold the value of the attribute. */ *(CK_ULONG_PTR)template->pValue = value; template->ulValueLen = sizeof (CK_ULONG); return (CKR_OK); } else { /* * The buffer provided by the application does * not have enough space to hold the value. */ template->ulValueLen = (CK_ULONG)-1; return (CKR_BUFFER_TOO_SMALL); } } /* * Copy the CK_ULONG data type attribute value from a template to the * object. */ void get_ulong_attr_from_template(CK_ULONG *value, CK_ATTRIBUTE_PTR template) { if (template->pValue != NULL) { *value = *(CK_ULONG_PTR)template->pValue; } else { *value = 0; } } /* * Copy the big integer attribute value from source's biginteger_t to * destination's biginteger_t. */ void copy_bigint_attr(biginteger_t *src, biginteger_t *dst) { if ((src->big_value != NULL) && (src->big_value_len > 0)) { /* * To do the copy, just have dst's big_value points * to src's. */ dst->big_value = src->big_value; dst->big_value_len = src->big_value_len; /* * After the copy, nullify the src's big_value pointer. * It prevents any double freeing the value. */ src->big_value = NULL; src->big_value_len = 0; } else { dst->big_value = NULL; dst->big_value_len = 0; } } CK_RV get_string_from_template(CK_ATTRIBUTE_PTR dest, CK_ATTRIBUTE_PTR src) { if ((src->pValue != NULL) && (src->ulValueLen > 0)) { /* Allocate storage for the value of the attribute. */ dest->pValue = malloc(src->ulValueLen); if (dest->pValue == NULL) { return (CKR_HOST_MEMORY); } (void) memcpy(dest->pValue, src->pValue, src->ulValueLen); dest->ulValueLen = src->ulValueLen; dest->type = src->type; } else { dest->pValue = NULL; dest->ulValueLen = 0; dest->type = src->type; } return (CKR_OK); } void string_attr_cleanup(CK_ATTRIBUTE_PTR template) { if (template->pValue) { free(template->pValue); template->pValue = NULL; template->ulValueLen = 0; } } /* * Release the storage allocated for object attribute with big integer * value. */ void bigint_attr_cleanup(biginteger_t *big) { if (big == NULL) return; if (big->big_value) { (void) memset(big->big_value, 0, big->big_value_len); free(big->big_value); big->big_value = NULL; big->big_value_len = 0; } } /* * Clean up and release all the storage allocated to hold the big integer * attributes associated with the type (i.e. class) of the object. Also, * release the storage allocated to the type of the object. */ void kernel_cleanup_object_bigint_attrs(kernel_object_t *object_p) { CK_OBJECT_CLASS class = object_p->class; CK_KEY_TYPE keytype = object_p->key_type; switch (class) { case CKO_PUBLIC_KEY: if (OBJ_PUB(object_p)) { switch (keytype) { case CKK_RSA: bigint_attr_cleanup(OBJ_PUB_RSA_MOD( object_p)); bigint_attr_cleanup(OBJ_PUB_RSA_PUBEXPO( object_p)); break; case CKK_DSA: bigint_attr_cleanup(OBJ_PUB_DSA_PRIME( object_p)); bigint_attr_cleanup(OBJ_PUB_DSA_SUBPRIME( object_p)); bigint_attr_cleanup(OBJ_PUB_DSA_BASE( object_p)); bigint_attr_cleanup(OBJ_PUB_DSA_VALUE( object_p)); break; case CKK_DH: bigint_attr_cleanup(OBJ_PUB_DH_PRIME(object_p)); bigint_attr_cleanup(OBJ_PUB_DH_BASE(object_p)); bigint_attr_cleanup(OBJ_PUB_DH_VALUE(object_p)); break; case CKK_EC: bigint_attr_cleanup(OBJ_PUB_EC_POINT(object_p)); break; } /* Release Public Key Object struct */ free(OBJ_PUB(object_p)); OBJ_PUB(object_p) = NULL; } break; case CKO_PRIVATE_KEY: if (OBJ_PRI(object_p)) { switch (keytype) { case CKK_RSA: bigint_attr_cleanup(OBJ_PRI_RSA_MOD( object_p)); bigint_attr_cleanup(OBJ_PRI_RSA_PUBEXPO( object_p)); bigint_attr_cleanup(OBJ_PRI_RSA_PRIEXPO( object_p)); bigint_attr_cleanup(OBJ_PRI_RSA_PRIME1( object_p)); bigint_attr_cleanup(OBJ_PRI_RSA_PRIME2( object_p)); bigint_attr_cleanup(OBJ_PRI_RSA_EXPO1( object_p)); bigint_attr_cleanup(OBJ_PRI_RSA_EXPO2( object_p)); bigint_attr_cleanup(OBJ_PRI_RSA_COEF( object_p)); break; case CKK_DSA: bigint_attr_cleanup(OBJ_PRI_DSA_PRIME( object_p)); bigint_attr_cleanup(OBJ_PRI_DSA_SUBPRIME( object_p)); bigint_attr_cleanup(OBJ_PRI_DSA_BASE( object_p)); bigint_attr_cleanup(OBJ_PRI_DSA_VALUE( object_p)); break; case CKK_DH: bigint_attr_cleanup(OBJ_PRI_DH_PRIME(object_p)); bigint_attr_cleanup(OBJ_PRI_DH_BASE(object_p)); bigint_attr_cleanup(OBJ_PRI_DH_VALUE(object_p)); break; case CKK_EC: bigint_attr_cleanup(OBJ_PRI_EC_VALUE(object_p)); break; } /* Release Private Key Object struct. */ free(OBJ_PRI(object_p)); OBJ_PRI(object_p) = NULL; } break; } } /* * Parse the common attributes. Return to caller with appropriate return * value to indicate if the supplied template specifies a valid attribute * with a valid value. */ CK_RV kernel_parse_common_attrs(CK_ATTRIBUTE_PTR template, kernel_session_t *sp, uint64_t *attr_mask_p) { CK_RV rv = CKR_OK; kernel_slot_t *pslot = slot_table[sp->ses_slotid]; switch (template->type) { case CKA_CLASS: break; /* default boolean attributes */ case CKA_TOKEN: if ((*(CK_BBOOL *)template->pValue) == TRUE) { rv = CKR_ATTRIBUTE_VALUE_INVALID; } break; case CKA_PRIVATE: if ((*(CK_BBOOL *)template->pValue) == TRUE) { /* * Cannot create a private object if the token * has a keystore and the user isn't logged in. */ if (pslot->sl_func_list.fl_object_create && pslot->sl_state != CKU_USER) { rv = CKR_ATTRIBUTE_VALUE_INVALID; } else { *attr_mask_p |= PRIVATE_BOOL_ON; } } break; case CKA_MODIFIABLE: if ((*(CK_BBOOL *)template->pValue) == FALSE) { *attr_mask_p &= ~MODIFIABLE_BOOL_ON; } break; case CKA_LABEL: break; default: rv = CKR_TEMPLATE_INCONSISTENT; } return (rv); } /* * Build a Public Key Object. * * - Parse the object's template, and when an error is detected such as * invalid attribute type, invalid attribute value, etc., return * with appropriate return value. * - Set up attribute mask field in the object for the supplied common * attributes that have boolean type. * - Build the attribute_info struct to hold the value of each supplied * attribute that has byte array type. Link attribute_info structs * together to form the extra attribute list of the object. * - Allocate storage for the Public Key object. * - Build the Public Key object according to the key type. Allocate * storage to hold the big integer value for the supplied attributes * that are required for a certain key type. * */ CK_RV kernel_build_public_key_object(CK_ATTRIBUTE_PTR template, CK_ULONG ulAttrNum, kernel_object_t *new_object, kernel_session_t *sp, uint_t mode) { int i; CK_KEY_TYPE keytype = (CK_KEY_TYPE)~0UL; uint64_t attr_mask = PUBLIC_KEY_DEFAULT; CK_RV rv = CKR_OK; int isLabel = 0; /* Must set flags */ int isModulus = 0; int isPubExpo = 0; int isPrime = 0; int isSubprime = 0; int isBase = 0; int isValue = 0; int isPoint = 0; int isParams = 0; /* Must not set flags */ int isModulusBits = 0; CK_ULONG modulus_bits = 0; biginteger_t modulus; biginteger_t pubexpo; biginteger_t prime; biginteger_t subprime; biginteger_t base; biginteger_t value; biginteger_t point; CK_ATTRIBUTE string_tmp; CK_ATTRIBUTE param_tmp; public_key_obj_t *pbk; /* prevent bigint_attr_cleanup from freeing invalid attr value */ (void) memset(&modulus, 0x0, sizeof (biginteger_t)); (void) memset(&pubexpo, 0x0, sizeof (biginteger_t)); (void) memset(&prime, 0x0, sizeof (biginteger_t)); (void) memset(&subprime, 0x0, sizeof (biginteger_t)); (void) memset(&base, 0x0, sizeof (biginteger_t)); (void) memset(&value, 0x0, sizeof (biginteger_t)); (void) memset(&point, 0x0, sizeof (biginteger_t)); string_tmp.pValue = NULL; param_tmp.pValue = NULL; for (i = 0; i < ulAttrNum; i++) { /* Public Key Object Attributes */ switch (template[i].type) { /* common key attributes */ case CKA_KEY_TYPE: keytype = *((CK_KEY_TYPE*)template[i].pValue); break; case CKA_ID: case CKA_START_DATE: case CKA_END_DATE: /* common public key attribute */ case CKA_SUBJECT: /* * Allocate storage to hold the attribute * value with byte array type, and add it to * the extra attribute list of the object. */ rv = kernel_add_extra_attr(&template[i], new_object); if (rv != CKR_OK) { goto fail_cleanup; } break; /* * The following key related attribute types must * not be specified by C_CreateObject. */ case CKA_LOCAL: case CKA_KEY_GEN_MECHANISM: rv = CKR_TEMPLATE_INCONSISTENT; goto fail_cleanup; /* Key related boolean attributes */ case CKA_DERIVE: if (*(CK_BBOOL *)template[i].pValue) attr_mask |= DERIVE_BOOL_ON; break; case CKA_ENCRYPT: if (*(CK_BBOOL *)template[i].pValue) attr_mask |= ENCRYPT_BOOL_ON; else attr_mask &= ~ENCRYPT_BOOL_ON; break; case CKA_VERIFY: if (*(CK_BBOOL *)template[i].pValue) attr_mask |= VERIFY_BOOL_ON; else attr_mask &= ~VERIFY_BOOL_ON; break; case CKA_VERIFY_RECOVER: if (*(CK_BBOOL *)template[i].pValue) attr_mask |= VERIFY_RECOVER_BOOL_ON; else attr_mask &= ~VERIFY_RECOVER_BOOL_ON; break; case CKA_WRAP: if (*(CK_BBOOL *)template[i].pValue) attr_mask |= WRAP_BOOL_ON; break; case CKA_TRUSTED: if (*(CK_BBOOL *)template[i].pValue) attr_mask |= TRUSTED_BOOL_ON; break; /* * The following key related attribute types must * be specified according to the key type by * C_CreateObject. */ case CKA_MODULUS: isModulus = 1; /* * Copyin big integer attribute from template * to a local variable. */ rv = get_bigint_attr_from_template(&modulus, &template[i]); if (rv != CKR_OK) goto fail_cleanup; break; case CKA_PUBLIC_EXPONENT: isPubExpo = 1; rv = get_bigint_attr_from_template(&pubexpo, &template[i]); if (rv != CKR_OK) goto fail_cleanup; break; case CKA_PRIME: isPrime = 1; rv = get_bigint_attr_from_template(&prime, &template[i]); if (rv != CKR_OK) goto fail_cleanup; break; case CKA_SUBPRIME: isSubprime = 1; rv = get_bigint_attr_from_template(&subprime, &template[i]); if (rv != CKR_OK) goto fail_cleanup; break; case CKA_BASE: isBase = 1; rv = get_bigint_attr_from_template(&base, &template[i]); if (rv != CKR_OK) goto fail_cleanup; break; case CKA_VALUE: isValue = 1; rv = get_bigint_attr_from_template(&value, &template[i]); if (rv != CKR_OK) goto fail_cleanup; break; case CKA_MODULUS_BITS: isModulusBits = 1; get_ulong_attr_from_template(&modulus_bits, &template[i]); break; case CKA_LABEL: isLabel = 1; rv = get_string_from_template(&string_tmp, &template[i]); if (rv != CKR_OK) goto fail_cleanup; break; case CKA_EC_POINT: isPoint = 1; rv = get_bigint_attr_from_template(&point, &template[i]); if (rv != CKR_OK) goto fail_cleanup; break; case CKA_EC_PARAMS: isParams = 1; rv = get_string_from_template(¶m_tmp, &template[i]); if (rv != CKR_OK) goto fail_cleanup; break; default: rv = kernel_parse_common_attrs(&template[i], sp, &attr_mask); if (rv != CKR_OK) goto fail_cleanup; break; } } /* For */ /* Allocate storage for Public Key Object. */ pbk = calloc(1, sizeof (public_key_obj_t)); if (pbk == NULL) { rv = CKR_HOST_MEMORY; goto fail_cleanup; } new_object->object_class_u.public_key = pbk; new_object->class = CKO_PUBLIC_KEY; if (keytype == (CK_KEY_TYPE)~0UL) { rv = CKR_TEMPLATE_INCOMPLETE; goto fail_cleanup; } new_object->key_type = keytype; /* Supported key types of the Public Key Object */ switch (keytype) { case CKK_RSA: if (mode == KERNEL_CREATE_OBJ) { if (isModulusBits || isPrime || isSubprime || isBase|| isValue) { rv = CKR_TEMPLATE_INCONSISTENT; goto fail_cleanup; } } if (isModulus && isPubExpo) { /* * Copy big integer attribute value to the * designated place in the public key object. */ copy_bigint_attr(&modulus, KEY_PUB_RSA_MOD(pbk)); copy_bigint_attr(&pubexpo, KEY_PUB_RSA_PUBEXPO(pbk)); } else { rv = CKR_TEMPLATE_INCOMPLETE; goto fail_cleanup; } /* must be generating a RSA key pair by value */ if (isModulusBits) { KEY_PUB_RSA_MOD_BITS(pbk) = modulus_bits; } break; case CKK_DSA: if (isModulusBits || isModulus || isPubExpo) { rv = CKR_TEMPLATE_INCONSISTENT; goto fail_cleanup; } if (!(isPrime && isSubprime && isBase && isValue)) { rv = CKR_TEMPLATE_INCOMPLETE; goto fail_cleanup; } copy_bigint_attr(&prime, KEY_PUB_DSA_PRIME(pbk)); copy_bigint_attr(&subprime, KEY_PUB_DSA_SUBPRIME(pbk)); copy_bigint_attr(&base, KEY_PUB_DSA_BASE(pbk)); copy_bigint_attr(&value, KEY_PUB_DSA_VALUE(pbk)); break; case CKK_DH: if (!(isPrime && isBase && isValue)) { rv = CKR_TEMPLATE_INCOMPLETE; goto fail_cleanup; } copy_bigint_attr(&prime, KEY_PUB_DH_PRIME(pbk)); copy_bigint_attr(&base, KEY_PUB_DH_BASE(pbk)); copy_bigint_attr(&value, KEY_PUB_DH_VALUE(pbk)); break; case CKK_EC: if (!isPoint || !isParams) { rv = CKR_TEMPLATE_INCOMPLETE; goto fail_cleanup; } copy_bigint_attr(&point, KEY_PUB_EC_POINT(pbk)); rv = kernel_add_extra_attr(¶m_tmp, new_object); if (rv != CKR_OK) goto fail_cleanup; string_attr_cleanup(¶m_tmp); break; default: rv = CKR_TEMPLATE_INCONSISTENT; goto fail_cleanup; } /* Set up object. */ new_object->bool_attr_mask = attr_mask; if (isLabel) { rv = kernel_add_extra_attr(&string_tmp, new_object); if (rv != CKR_OK) goto fail_cleanup; string_attr_cleanup(&string_tmp); } return (rv); fail_cleanup: /* * cleanup the storage allocated to the local variables. */ bigint_attr_cleanup(&modulus); bigint_attr_cleanup(&pubexpo); bigint_attr_cleanup(&prime); bigint_attr_cleanup(&subprime); bigint_attr_cleanup(&base); bigint_attr_cleanup(&value); bigint_attr_cleanup(&point); string_attr_cleanup(&string_tmp); string_attr_cleanup(¶m_tmp); /* * cleanup the storage allocated inside the object itself. */ kernel_cleanup_object(new_object); return (rv); } /* * Build a Private Key Object. * * - Parse the object's template, and when an error is detected such as * invalid attribute type, invalid attribute value, etc., return * with appropriate return value. * - Set up attribute mask field in the object for the supplied common * attributes that have boolean type. * - Build the attribute_info struct to hold the value of each supplied * attribute that has byte array type. Link attribute_info structs * together to form the extra attribute list of the object. * - Allocate storage for the Private Key object. * - Build the Private Key object according to the key type. Allocate * storage to hold the big integer value for the supplied attributes * that are required for a certain key type. * */ CK_RV kernel_build_private_key_object(CK_ATTRIBUTE_PTR template, CK_ULONG ulAttrNum, kernel_object_t *new_object, kernel_session_t *sp, uint_t mode) { ulong_t i; CK_KEY_TYPE keytype = (CK_KEY_TYPE)~0UL; uint64_t attr_mask = PRIVATE_KEY_DEFAULT; CK_RV rv = CKR_OK; int isLabel = 0; /* Must set flags */ int isModulus = 0; int isPriExpo = 0; int isPrime = 0; int isSubprime = 0; int isBase = 0; int isValue = 0; int isParams = 0; /* Must not set flags */ int isValueBits = 0; CK_ULONG value_bits = 0; /* Private Key RSA optional */ int isPubExpo = 0; int isPrime1 = 0; int isPrime2 = 0; int isExpo1 = 0; int isExpo2 = 0; int isCoef = 0; biginteger_t modulus; biginteger_t priexpo; biginteger_t prime; biginteger_t subprime; biginteger_t base; biginteger_t value; biginteger_t pubexpo; biginteger_t prime1; biginteger_t prime2; biginteger_t expo1; biginteger_t expo2; biginteger_t coef; CK_ATTRIBUTE string_tmp; CK_ATTRIBUTE param_tmp; private_key_obj_t *pvk; /* prevent bigint_attr_cleanup from freeing invalid attr value */ (void) memset(&modulus, 0x0, sizeof (biginteger_t)); (void) memset(&priexpo, 0x0, sizeof (biginteger_t)); (void) memset(&prime, 0x0, sizeof (biginteger_t)); (void) memset(&subprime, 0x0, sizeof (biginteger_t)); (void) memset(&base, 0x0, sizeof (biginteger_t)); (void) memset(&value, 0x0, sizeof (biginteger_t)); (void) memset(&pubexpo, 0x0, sizeof (biginteger_t)); (void) memset(&prime1, 0x0, sizeof (biginteger_t)); (void) memset(&prime2, 0x0, sizeof (biginteger_t)); (void) memset(&expo1, 0x0, sizeof (biginteger_t)); (void) memset(&expo2, 0x0, sizeof (biginteger_t)); (void) memset(&coef, 0x0, sizeof (biginteger_t)); string_tmp.pValue = NULL; param_tmp.pValue = NULL; for (i = 0; i < ulAttrNum; i++) { /* Private Key Object Attributes */ switch (template[i].type) { /* common key attributes */ case CKA_KEY_TYPE: keytype = *((CK_KEY_TYPE*)template[i].pValue); break; case CKA_ID: case CKA_START_DATE: case CKA_END_DATE: /* common private key attribute */ case CKA_SUBJECT: /* * Allocate storage to hold the attribute * value with byte array type, and add it to * the extra attribute list of the object. */ rv = kernel_add_extra_attr(&template[i], new_object); if (rv != CKR_OK) { goto fail_cleanup; } break; /* * The following key related attribute types must * not be specified by C_CreateObject. */ case CKA_LOCAL: case CKA_KEY_GEN_MECHANISM: case CKA_AUTH_PIN_FLAGS: case CKA_ALWAYS_SENSITIVE: case CKA_NEVER_EXTRACTABLE: rv = CKR_TEMPLATE_INCONSISTENT; goto fail_cleanup; /* Key related boolean attributes */ case CKA_DERIVE: if (*(CK_BBOOL *)template[i].pValue) attr_mask |= DERIVE_BOOL_ON; break; case CKA_SENSITIVE: if (*(CK_BBOOL *)template[i].pValue) attr_mask |= SENSITIVE_BOOL_ON; break; case CKA_SECONDARY_AUTH: if (*(CK_BBOOL *)template[i].pValue) { rv = CKR_ATTRIBUTE_VALUE_INVALID; goto fail_cleanup; } break; case CKA_DECRYPT: if (*(CK_BBOOL *)template[i].pValue) attr_mask |= DECRYPT_BOOL_ON; else attr_mask &= ~DECRYPT_BOOL_ON; break; case CKA_SIGN: if (*(CK_BBOOL *)template[i].pValue) attr_mask |= SIGN_BOOL_ON; else attr_mask &= ~SIGN_BOOL_ON; break; case CKA_SIGN_RECOVER: if (*(CK_BBOOL *)template[i].pValue) attr_mask |= SIGN_RECOVER_BOOL_ON; else attr_mask &= ~SIGN_RECOVER_BOOL_ON; break; case CKA_UNWRAP: if (*(CK_BBOOL *)template[i].pValue) attr_mask |= UNWRAP_BOOL_ON; break; case CKA_EXTRACTABLE: if (*(CK_BBOOL *)template[i].pValue) attr_mask |= EXTRACTABLE_BOOL_ON; else attr_mask &= ~EXTRACTABLE_BOOL_ON; break; /* * The following key related attribute types must * be specified according to the key type by * C_CreateObject. */ case CKA_MODULUS: isModulus = 1; /* * Copyin big integer attribute from template * to a local variable. */ rv = get_bigint_attr_from_template(&modulus, &template[i]); if (rv != CKR_OK) goto fail_cleanup; break; case CKA_PUBLIC_EXPONENT: isPubExpo = 1; rv = get_bigint_attr_from_template(&pubexpo, &template[i]); if (rv != CKR_OK) goto fail_cleanup; break; case CKA_PRIVATE_EXPONENT: isPriExpo = 1; rv = get_bigint_attr_from_template(&priexpo, &template[i]); if (rv != CKR_OK) goto fail_cleanup; break; case CKA_PRIME_1: isPrime1 = 1; rv = get_bigint_attr_from_template(&prime1, &template[i]); if (rv != CKR_OK) goto fail_cleanup; break; case CKA_PRIME_2: isPrime2 = 1; rv = get_bigint_attr_from_template(&prime2, &template[i]); if (rv != CKR_OK) goto fail_cleanup; break; case CKA_EXPONENT_1: isExpo1 = 1; rv = get_bigint_attr_from_template(&expo1, &template[i]); if (rv != CKR_OK) goto fail_cleanup; break; case CKA_EXPONENT_2: isExpo2 = 1; rv = get_bigint_attr_from_template(&expo2, &template[i]); if (rv != CKR_OK) goto fail_cleanup; break; case CKA_COEFFICIENT: isCoef = 1; rv = get_bigint_attr_from_template(&coef, &template[i]); if (rv != CKR_OK) goto fail_cleanup; break; case CKA_PRIME: isPrime = 1; rv = get_bigint_attr_from_template(&prime, &template[i]); if (rv != CKR_OK) goto fail_cleanup; break; case CKA_SUBPRIME: isSubprime = 1; rv = get_bigint_attr_from_template(&subprime, &template[i]); if (rv != CKR_OK) goto fail_cleanup; break; case CKA_BASE: isBase = 1; rv = get_bigint_attr_from_template(&base, &template[i]); if (rv != CKR_OK) goto fail_cleanup; break; case CKA_VALUE: isValue = 1; rv = get_bigint_attr_from_template(&value, &template[i]); if (rv != CKR_OK) goto fail_cleanup; break; case CKA_VALUE_BITS: isValueBits = 1; get_ulong_attr_from_template(&value_bits, &template[i]); break; case CKA_LABEL: isLabel = 1; rv = get_string_from_template(&string_tmp, &template[i]); if (rv != CKR_OK) goto fail_cleanup; break; case CKA_EC_PARAMS: isParams = 1; rv = get_string_from_template(¶m_tmp, &template[i]); if (rv != CKR_OK) goto fail_cleanup; break; default: rv = kernel_parse_common_attrs(&template[i], sp, &attr_mask); if (rv != CKR_OK) goto fail_cleanup; break; } } /* For */ /* Allocate storage for Private Key Object. */ pvk = calloc(1, sizeof (private_key_obj_t)); if (pvk == NULL) { rv = CKR_HOST_MEMORY; goto fail_cleanup; } new_object->object_class_u.private_key = pvk; new_object->class = CKO_PRIVATE_KEY; if (keytype == (CK_KEY_TYPE)~0UL) { rv = CKR_TEMPLATE_INCOMPLETE; goto fail_cleanup; } new_object->key_type = keytype; /* Supported key types of the Private Key Object */ switch (keytype) { case CKK_RSA: if (isPrime || isSubprime || isBase || isValue || isValueBits) { rv = CKR_TEMPLATE_INCONSISTENT; goto fail_cleanup; } if (isModulus && isPriExpo) { /* * Copy big integer attribute value to the * designated place in the Private Key object. */ copy_bigint_attr(&modulus, KEY_PRI_RSA_MOD(pvk)); copy_bigint_attr(&priexpo, KEY_PRI_RSA_PRIEXPO(pvk)); } else { rv = CKR_TEMPLATE_INCOMPLETE; goto fail_cleanup; } /* The following attributes are optional. */ if (isPubExpo) { copy_bigint_attr(&pubexpo, KEY_PRI_RSA_PUBEXPO(pvk)); } if (isPrime1) { copy_bigint_attr(&prime1, KEY_PRI_RSA_PRIME1(pvk)); } if (isPrime2) { copy_bigint_attr(&prime2, KEY_PRI_RSA_PRIME2(pvk)); } if (isExpo1) { copy_bigint_attr(&expo1, KEY_PRI_RSA_EXPO1(pvk)); } if (isExpo2) { copy_bigint_attr(&expo2, KEY_PRI_RSA_EXPO2(pvk)); } if (isCoef) { copy_bigint_attr(&coef, KEY_PRI_RSA_COEF(pvk)); } break; case CKK_DSA: if (isModulus || isPubExpo || isPriExpo || isPrime1 || isPrime2 || isExpo1 || isExpo2 || isCoef || isValueBits) { rv = CKR_TEMPLATE_INCONSISTENT; goto fail_cleanup; } if (!(isPrime && isSubprime && isBase && isValue)) { rv = CKR_TEMPLATE_INCOMPLETE; goto fail_cleanup; } copy_bigint_attr(&prime, KEY_PRI_DSA_PRIME(pvk)); copy_bigint_attr(&subprime, KEY_PRI_DSA_SUBPRIME(pvk)); copy_bigint_attr(&base, KEY_PRI_DSA_BASE(pvk)); copy_bigint_attr(&value, KEY_PRI_DSA_VALUE(pvk)); break; case CKK_DH: if (mode == KERNEL_CREATE_OBJ && isValueBits) { rv = CKR_TEMPLATE_INCONSISTENT; goto fail_cleanup; } if (!(isPrime && isBase && isValue)) { rv = CKR_TEMPLATE_INCOMPLETE; goto fail_cleanup; } copy_bigint_attr(&prime, KEY_PRI_DH_PRIME(pvk)); copy_bigint_attr(&base, KEY_PRI_DH_BASE(pvk)); copy_bigint_attr(&value, KEY_PRI_DH_VALUE(pvk)); KEY_PRI_DH_VAL_BITS(pvk) = (isValueBits) ? value_bits : 0; break; case CKK_EC: if (!isValue || !isParams) { rv = CKR_TEMPLATE_INCOMPLETE; goto fail_cleanup; } copy_bigint_attr(&value, KEY_PRI_EC_VALUE(pvk)); rv = kernel_add_extra_attr(¶m_tmp, new_object); if (rv != CKR_OK) goto fail_cleanup; string_attr_cleanup(¶m_tmp); break; default: rv = CKR_TEMPLATE_INCONSISTENT; goto fail_cleanup; } /* Set up object. */ new_object->bool_attr_mask = attr_mask; if (isLabel) { rv = kernel_add_extra_attr(&string_tmp, new_object); if (rv != CKR_OK) goto fail_cleanup; string_attr_cleanup(&string_tmp); } return (rv); fail_cleanup: /* * cleanup the storage allocated to the local variables. */ bigint_attr_cleanup(&modulus); bigint_attr_cleanup(&priexpo); bigint_attr_cleanup(&prime); bigint_attr_cleanup(&subprime); bigint_attr_cleanup(&base); bigint_attr_cleanup(&value); bigint_attr_cleanup(&pubexpo); bigint_attr_cleanup(&prime1); bigint_attr_cleanup(&prime2); bigint_attr_cleanup(&expo1); bigint_attr_cleanup(&expo2); bigint_attr_cleanup(&coef); string_attr_cleanup(&string_tmp); string_attr_cleanup(¶m_tmp); /* * cleanup the storage allocated inside the object itself. */ kernel_cleanup_object(new_object); return (rv); } /* * Build a Secret Key Object. * * - Parse the object's template, and when an error is detected such as * invalid attribute type, invalid attribute value, etc., return * with appropriate return value. * - Set up attribute mask field in the object for the supplied common * attributes that have boolean type. * - Build the attribute_info struct to hold the value of each supplied * attribute that has byte array type. Link attribute_info structs * together to form the extra attribute list of the object. * - Allocate storage for the Secret Key object. * - Build the Secret Key object. Allocate storage to hold the big integer * value for the attribute CKA_VALUE that is required for all the key * types supported by secret key object. * */ CK_RV kernel_build_secret_key_object(CK_ATTRIBUTE_PTR template, CK_ULONG ulAttrNum, kernel_object_t *new_object, kernel_session_t *sp) { int i; CK_KEY_TYPE keytype = (CK_KEY_TYPE)~0UL; uint64_t attr_mask = SECRET_KEY_DEFAULT; CK_RV rv = CKR_OK; int isLabel = 0; /* Must set flags */ int isValue = 0; /* Must not set flags */ int isValueLen = 0; CK_ATTRIBUTE string_tmp; secret_key_obj_t *sck; string_tmp.pValue = NULL; /* Allocate storage for Secret Key Object. */ sck = calloc(1, sizeof (secret_key_obj_t)); if (sck == NULL) { rv = CKR_HOST_MEMORY; goto fail_cleanup; } new_object->object_class_u.secret_key = sck; new_object->class = CKO_SECRET_KEY; for (i = 0; i < ulAttrNum; i++) { /* Secret Key Object Attributes */ switch (template[i].type) { /* common key attributes */ case CKA_KEY_TYPE: keytype = *((CK_KEY_TYPE*)template[i].pValue); break; case CKA_ID: case CKA_START_DATE: case CKA_END_DATE: /* * Allocate storage to hold the attribute * value with byte array type, and add it to * the extra attribute list of the object. */ rv = kernel_add_extra_attr(&template[i], new_object); if (rv != CKR_OK) { goto fail_cleanup; } break; /* * The following key related attribute types must * not be specified by C_CreateObject. */ case CKA_LOCAL: case CKA_KEY_GEN_MECHANISM: case CKA_ALWAYS_SENSITIVE: case CKA_NEVER_EXTRACTABLE: rv = CKR_TEMPLATE_INCONSISTENT; goto fail_cleanup; /* Key related boolean attributes */ case CKA_DERIVE: if (*(CK_BBOOL *)template[i].pValue) attr_mask |= DERIVE_BOOL_ON; break; case CKA_SENSITIVE: if (*(CK_BBOOL *)template[i].pValue) attr_mask |= SENSITIVE_BOOL_ON; break; case CKA_ENCRYPT: if (*(CK_BBOOL *)template[i].pValue) attr_mask |= ENCRYPT_BOOL_ON; else attr_mask &= ~ENCRYPT_BOOL_ON; break; case CKA_DECRYPT: if (*(CK_BBOOL *)template[i].pValue) attr_mask |= DECRYPT_BOOL_ON; else attr_mask &= ~DECRYPT_BOOL_ON; break; case CKA_SIGN: if (*(CK_BBOOL *)template[i].pValue) attr_mask |= SIGN_BOOL_ON; else attr_mask &= ~SIGN_BOOL_ON; break; case CKA_VERIFY: if (*(CK_BBOOL *)template[i].pValue) attr_mask |= VERIFY_BOOL_ON; else attr_mask &= ~VERIFY_BOOL_ON; break; case CKA_WRAP: if (*(CK_BBOOL *)template[i].pValue) attr_mask |= WRAP_BOOL_ON; break; case CKA_UNWRAP: if (*(CK_BBOOL *)template[i].pValue) attr_mask |= UNWRAP_BOOL_ON; break; case CKA_EXTRACTABLE: if (*(CK_BBOOL *)template[i].pValue) attr_mask |= EXTRACTABLE_BOOL_ON; else attr_mask &= ~EXTRACTABLE_BOOL_ON; break; case CKA_VALUE: isValue = 1; if ((template[i].ulValueLen == 0) || (template[i].pValue == NULL)) { rv = CKR_ATTRIBUTE_VALUE_INVALID; goto fail_cleanup; } /* * Copyin attribute from template * to a local variable. */ sck->sk_value = malloc(template[i].ulValueLen); if (sck->sk_value == NULL) { rv = CKR_HOST_MEMORY; goto fail_cleanup; } (void) memcpy(sck->sk_value, template[i].pValue, template[i].ulValueLen); sck->sk_value_len = template[i].ulValueLen; break; case CKA_VALUE_LEN: isValueLen = 1; break; case CKA_LABEL: isLabel = 1; rv = get_string_from_template(&string_tmp, &template[i]); if (rv != CKR_OK) goto fail_cleanup; break; default: rv = kernel_parse_common_attrs(&template[i], sp, &attr_mask); if (rv != CKR_OK) goto fail_cleanup; break; } } /* For */ if (keytype == (CK_KEY_TYPE)~0UL) { rv = CKR_TEMPLATE_INCOMPLETE; goto fail_cleanup; } new_object->key_type = keytype; /* Supported key types of the Secret Key Object */ switch (keytype) { case CKK_RC4: if (!isValue) { rv = CKR_TEMPLATE_INCOMPLETE; goto fail_cleanup; } if ((sck->sk_value_len < ARCFOUR_MIN_KEY_BYTES) || (sck->sk_value_len > ARCFOUR_MAX_KEY_BYTES)) { rv = CKR_ATTRIBUTE_VALUE_INVALID; goto fail_cleanup; } break; case CKK_GENERIC_SECRET: if (!isValue) { rv = CKR_TEMPLATE_INCOMPLETE; goto fail_cleanup; } break; case CKK_AES: if (!isValue) { rv = CKR_TEMPLATE_INCOMPLETE; goto fail_cleanup; } if (sck->sk_value_len < AES_MIN_KEY_BYTES) { rv = CKR_ATTRIBUTE_VALUE_INVALID; goto fail_cleanup; } break; case CKK_BLOWFISH: if (!isValue) { rv = CKR_TEMPLATE_INCOMPLETE; goto fail_cleanup; } if (sck->sk_value_len < BLOWFISH_MINBYTES) { rv = CKR_ATTRIBUTE_VALUE_INVALID; goto fail_cleanup; } break; case CKK_DES: if (!isValue) { rv = CKR_TEMPLATE_INCOMPLETE; goto fail_cleanup; } if (sck->sk_value_len != DES_KEYSIZE) { rv = CKR_ATTRIBUTE_VALUE_INVALID; goto fail_cleanup; } break; case CKK_DES2: if (!isValue) { rv = CKR_TEMPLATE_INCOMPLETE; goto fail_cleanup; } if (sck->sk_value_len != DES2_KEYSIZE) { rv = CKR_ATTRIBUTE_VALUE_INVALID; goto fail_cleanup; } break; case CKK_DES3: if (!isValue) { rv = CKR_TEMPLATE_INCOMPLETE; goto fail_cleanup; } if (sck->sk_value_len != DES3_KEYSIZE) { rv = CKR_ATTRIBUTE_VALUE_INVALID; goto fail_cleanup; } break; default: rv = CKR_TEMPLATE_INCONSISTENT; goto fail_cleanup; } if (isValueLen) { rv = CKR_TEMPLATE_INCONSISTENT; goto fail_cleanup; } /* Set up object. */ new_object->bool_attr_mask = attr_mask; if (isLabel) { rv = kernel_add_extra_attr(&string_tmp, new_object); if (rv != CKR_OK) goto fail_cleanup; string_attr_cleanup(&string_tmp); } return (rv); fail_cleanup: /* * cleanup the storage allocated to the local variables. */ string_attr_cleanup(&string_tmp); /* * cleanup the storage allocated inside the object itself. */ kernel_cleanup_object(new_object); return (rv); } /* * Validate the attribute types in the object's template. Then, * call the appropriate build function according to the class of * the object specified in the template. * * Note: The following classes of objects are supported: * - CKO_SECRET_KEY * - CKO_PUBLIC_KEY * - CKO_PRIVATE_KEY */ CK_RV kernel_build_object(CK_ATTRIBUTE_PTR template, CK_ULONG ulAttrNum, kernel_object_t *new_object, kernel_session_t *sp, uint_t mode) { CK_OBJECT_CLASS class = (CK_OBJECT_CLASS)~0UL; CK_RV rv = CKR_OK; if (template == NULL) { return (CKR_ARGUMENTS_BAD); } /* Validate the attribute type in the template. */ rv = kernel_validate_attr(template, ulAttrNum, &class); if (rv != CKR_OK) return (rv); if (class == (CK_OBJECT_CLASS)~0UL) return (CKR_TEMPLATE_INCOMPLETE); /* * Call the appropriate function based on the supported class * of the object. */ switch (class) { case CKO_PUBLIC_KEY: rv = kernel_build_public_key_object(template, ulAttrNum, new_object, sp, mode); break; case CKO_PRIVATE_KEY: rv = kernel_build_private_key_object(template, ulAttrNum, new_object, sp, mode); break; case CKO_SECRET_KEY: rv = kernel_build_secret_key_object(template, ulAttrNum, new_object, sp); break; case CKO_DOMAIN_PARAMETERS: case CKO_DATA: case CKO_CERTIFICATE: case CKO_HW_FEATURE: case CKO_VENDOR_DEFINED: default: return (CKR_ATTRIBUTE_VALUE_INVALID); } return (rv); } /* * Get the value of a requested attribute that is common to all supported * classes (i.e. public key, private key, secret key classes). */ CK_RV kernel_get_common_attrs(kernel_object_t *object_p, CK_ATTRIBUTE_PTR template) { CK_RV rv = CKR_OK; switch (template->type) { case CKA_CLASS: return (get_ulong_attr_from_object(object_p->class, template)); /* default boolean attributes */ case CKA_TOKEN: template->ulValueLen = sizeof (CK_BBOOL); if (template->pValue == NULL) { return (CKR_OK); } /* * A token object will not be created in the library, so we * return FALSE. */ *((CK_BBOOL *)template->pValue) = B_FALSE; break; case CKA_PRIVATE: template->ulValueLen = sizeof (CK_BBOOL); if (template->pValue == NULL) { return (CKR_OK); } if (object_p->bool_attr_mask & PRIVATE_BOOL_ON) { *((CK_BBOOL *)template->pValue) = B_TRUE; } else { *((CK_BBOOL *)template->pValue) = B_FALSE; } break; case CKA_MODIFIABLE: template->ulValueLen = sizeof (CK_BBOOL); if (template->pValue == NULL) { return (CKR_OK); } if ((object_p->bool_attr_mask) & MODIFIABLE_BOOL_ON) *((CK_BBOOL *)template->pValue) = B_TRUE; else *((CK_BBOOL *)template->pValue) = B_FALSE; break; case CKA_LABEL: return (get_extra_attr_from_object(object_p, template)); default: /* * The specified attribute for the object is invalid. * (the object does not possess such an attribute.) */ template->ulValueLen = (CK_ULONG)-1; return (CKR_ATTRIBUTE_TYPE_INVALID); } return (rv); } /* * Get the value of a requested attribute that is common to all key objects * (i.e. public key, private key and secret key). */ CK_RV kernel_get_common_key_attrs(kernel_object_t *object_p, CK_ATTRIBUTE_PTR template) { switch (template->type) { case CKA_KEY_TYPE: return (get_ulong_attr_from_object(object_p->key_type, template)); case CKA_ID: case CKA_START_DATE: case CKA_END_DATE: /* * The above extra attributes have byte array type. */ return (get_extra_attr_from_object(object_p, template)); /* Key related boolean attributes */ case CKA_LOCAL: return (get_bool_attr_from_object(object_p, LOCAL_BOOL_ON, template)); case CKA_DERIVE: return (get_bool_attr_from_object(object_p, DERIVE_BOOL_ON, template)); case CKA_KEY_GEN_MECHANISM: return (get_ulong_attr_from_object(object_p->mechanism, template)); default: return (CKR_ATTRIBUTE_TYPE_INVALID); } } /* * Get the value of a requested attribute of a Public Key Object. * * Rule: All the attributes in the public key object can be revealed. */ CK_RV kernel_get_public_key_attribute(kernel_object_t *object_p, CK_ATTRIBUTE_PTR template) { CK_RV rv = CKR_OK; CK_KEY_TYPE keytype = object_p->key_type; switch (template->type) { case CKA_SUBJECT: case CKA_EC_PARAMS: /* * The above extra attributes have byte array type. */ return (get_extra_attr_from_object(object_p, template)); /* Key related boolean attributes */ case CKA_ENCRYPT: return (get_bool_attr_from_object(object_p, ENCRYPT_BOOL_ON, template)); case CKA_VERIFY: return (get_bool_attr_from_object(object_p, VERIFY_BOOL_ON, template)); case CKA_VERIFY_RECOVER: return (get_bool_attr_from_object(object_p, VERIFY_RECOVER_BOOL_ON, template)); case CKA_WRAP: return (get_bool_attr_from_object(object_p, WRAP_BOOL_ON, template)); case CKA_TRUSTED: return (get_bool_attr_from_object(object_p, TRUSTED_BOOL_ON, template)); case CKA_MODULUS: /* * This attribute is valid only for RSA public key * object. */ if (keytype == CKK_RSA) { return (get_bigint_attr_from_object( OBJ_PUB_RSA_MOD(object_p), template)); } else { template->ulValueLen = (CK_ULONG)-1; return (CKR_ATTRIBUTE_TYPE_INVALID); } case CKA_PUBLIC_EXPONENT: if (keytype == CKK_RSA) { return (get_bigint_attr_from_object( OBJ_PUB_RSA_PUBEXPO(object_p), template)); } else { template->ulValueLen = (CK_ULONG)-1; return (CKR_ATTRIBUTE_TYPE_INVALID); } case CKA_MODULUS_BITS: if (keytype == CKK_RSA) { return (get_ulong_attr_from_object( OBJ_PUB_RSA_MOD_BITS(object_p), template)); } else { template->ulValueLen = (CK_ULONG)-1; return (CKR_ATTRIBUTE_TYPE_INVALID); } case CKA_PRIME: switch (keytype) { case CKK_DSA: return (get_bigint_attr_from_object( OBJ_PUB_DSA_PRIME(object_p), template)); case CKK_DH: return (get_bigint_attr_from_object( OBJ_PUB_DH_PRIME(object_p), template)); default: template->ulValueLen = (CK_ULONG)-1; return (CKR_ATTRIBUTE_TYPE_INVALID); } case CKA_SUBPRIME: switch (keytype) { case CKK_DSA: return (get_bigint_attr_from_object( OBJ_PUB_DSA_SUBPRIME(object_p), template)); default: template->ulValueLen = (CK_ULONG)-1; return (CKR_ATTRIBUTE_TYPE_INVALID); } case CKA_BASE: switch (keytype) { case CKK_DSA: return (get_bigint_attr_from_object( OBJ_PUB_DSA_BASE(object_p), template)); case CKK_DH: return (get_bigint_attr_from_object( OBJ_PUB_DH_BASE(object_p), template)); default: template->ulValueLen = (CK_ULONG)-1; return (CKR_ATTRIBUTE_TYPE_INVALID); } case CKA_VALUE: switch (keytype) { case CKK_DSA: return (get_bigint_attr_from_object( OBJ_PUB_DSA_VALUE(object_p), template)); case CKK_DH: return (get_bigint_attr_from_object( OBJ_PUB_DH_VALUE(object_p), template)); default: template->ulValueLen = (CK_ULONG)-1; return (CKR_ATTRIBUTE_TYPE_INVALID); } case CKA_EC_POINT: switch (keytype) { case CKK_EC: return (get_bigint_attr_from_object( OBJ_PUB_EC_POINT(object_p), template)); default: template->ulValueLen = (CK_ULONG)-1; return (CKR_ATTRIBUTE_TYPE_INVALID); } default: /* * First, get the value of the request attribute defined * in the list of common key attributes. If the request * attribute is not found in that list, then get the * attribute from the list of common attributes. */ rv = kernel_get_common_key_attrs(object_p, template); if (rv == CKR_ATTRIBUTE_TYPE_INVALID) { rv = kernel_get_common_attrs(object_p, template); } break; } return (rv); } /* * Get the value of a requested attribute of a Private Key Object. * * Rule: All the attributes in the private key object can be revealed * except those marked with footnote number "7" when the object * has its CKA_SENSITIVE attribute set to TRUE or its * CKA_EXTRACTABLE attribute set to FALSE (p.88 in PKCS11 spec.). */ CK_RV kernel_get_private_key_attribute(kernel_object_t *object_p, CK_ATTRIBUTE_PTR template) { CK_RV rv = CKR_OK; CK_KEY_TYPE keytype = object_p->key_type; /* * If the following specified attributes for the private key * object cannot be revealed because the object is sensitive * or unextractable, then the ulValueLen is set to -1. */ if ((object_p->bool_attr_mask & SENSITIVE_BOOL_ON) || !(object_p->bool_attr_mask & EXTRACTABLE_BOOL_ON)) { switch (template->type) { case CKA_PRIVATE_EXPONENT: case CKA_PRIME_1: case CKA_PRIME_2: case CKA_EXPONENT_1: case CKA_EXPONENT_2: case CKA_COEFFICIENT: case CKA_VALUE: template->ulValueLen = (CK_ULONG)-1; return (CKR_ATTRIBUTE_SENSITIVE); } } switch (template->type) { case CKA_SUBJECT: case CKA_EC_PARAMS: /* * The above extra attributes have byte array type. */ return (get_extra_attr_from_object(object_p, template)); /* Key related boolean attributes */ case CKA_SENSITIVE: return (get_bool_attr_from_object(object_p, SENSITIVE_BOOL_ON, template)); case CKA_SECONDARY_AUTH: return (get_bool_attr_from_object(object_p, SECONDARY_AUTH_BOOL_ON, template)); case CKA_DECRYPT: return (get_bool_attr_from_object(object_p, DECRYPT_BOOL_ON, template)); case CKA_SIGN: return (get_bool_attr_from_object(object_p, SIGN_BOOL_ON, template)); case CKA_SIGN_RECOVER: return (get_bool_attr_from_object(object_p, SIGN_RECOVER_BOOL_ON, template)); case CKA_UNWRAP: return (get_bool_attr_from_object(object_p, UNWRAP_BOOL_ON, template)); case CKA_EXTRACTABLE: return (get_bool_attr_from_object(object_p, EXTRACTABLE_BOOL_ON, template)); case CKA_ALWAYS_SENSITIVE: return (get_bool_attr_from_object(object_p, ALWAYS_SENSITIVE_BOOL_ON, template)); case CKA_NEVER_EXTRACTABLE: return (get_bool_attr_from_object(object_p, NEVER_EXTRACTABLE_BOOL_ON, template)); case CKA_MODULUS: if (keytype == CKK_RSA) { return (get_bigint_attr_from_object( OBJ_PRI_RSA_MOD(object_p), template)); } else { template->ulValueLen = (CK_ULONG)-1; rv = CKR_ATTRIBUTE_TYPE_INVALID; break; } case CKA_PUBLIC_EXPONENT: if (keytype == CKK_RSA) { return (get_bigint_attr_from_object( OBJ_PRI_RSA_PUBEXPO(object_p), template)); } else { template->ulValueLen = (CK_ULONG)-1; rv = CKR_ATTRIBUTE_TYPE_INVALID; break; } case CKA_PRIVATE_EXPONENT: if (keytype == CKK_RSA) { return (get_bigint_attr_from_object( OBJ_PRI_RSA_PRIEXPO(object_p), template)); } else { template->ulValueLen = (CK_ULONG)-1; rv = CKR_ATTRIBUTE_TYPE_INVALID; break; } case CKA_PRIME_1: if (keytype == CKK_RSA) { return (get_bigint_attr_from_object( OBJ_PRI_RSA_PRIME1(object_p), template)); } else { template->ulValueLen = (CK_ULONG)-1; rv = CKR_ATTRIBUTE_TYPE_INVALID; break; } case CKA_PRIME_2: if (keytype == CKK_RSA) { return (get_bigint_attr_from_object( OBJ_PRI_RSA_PRIME2(object_p), template)); } else { template->ulValueLen = (CK_ULONG)-1; rv = CKR_ATTRIBUTE_TYPE_INVALID; break; } case CKA_EXPONENT_1: if (keytype == CKK_RSA) { return (get_bigint_attr_from_object( OBJ_PRI_RSA_EXPO1(object_p), template)); } else { template->ulValueLen = (CK_ULONG)-1; rv = CKR_ATTRIBUTE_TYPE_INVALID; break; } case CKA_EXPONENT_2: if (keytype == CKK_RSA) { return (get_bigint_attr_from_object( OBJ_PRI_RSA_EXPO2(object_p), template)); } else { template->ulValueLen = (CK_ULONG)-1; rv = CKR_ATTRIBUTE_TYPE_INVALID; break; } case CKA_COEFFICIENT: if (keytype == CKK_RSA) { return (get_bigint_attr_from_object( OBJ_PRI_RSA_COEF(object_p), template)); } else { template->ulValueLen = (CK_ULONG)-1; rv = CKR_ATTRIBUTE_TYPE_INVALID; break; } case CKA_VALUE_BITS: if (keytype == CKK_DH) { return (get_ulong_attr_from_object( OBJ_PRI_DH_VAL_BITS(object_p), template)); } else { template->ulValueLen = (CK_ULONG)-1; rv = CKR_ATTRIBUTE_TYPE_INVALID; break; } case CKA_PRIME: switch (keytype) { case CKK_DSA: return (get_bigint_attr_from_object( OBJ_PRI_DSA_PRIME(object_p), template)); case CKK_DH: return (get_bigint_attr_from_object( OBJ_PRI_DH_PRIME(object_p), template)); default: template->ulValueLen = (CK_ULONG)-1; return (CKR_ATTRIBUTE_TYPE_INVALID); } case CKA_SUBPRIME: switch (keytype) { case CKK_DSA: return (get_bigint_attr_from_object( OBJ_PRI_DSA_SUBPRIME(object_p), template)); default: template->ulValueLen = (CK_ULONG)-1; return (CKR_ATTRIBUTE_TYPE_INVALID); } case CKA_BASE: switch (keytype) { case CKK_DSA: return (get_bigint_attr_from_object( OBJ_PRI_DSA_BASE(object_p), template)); case CKK_DH: return (get_bigint_attr_from_object( OBJ_PRI_DH_BASE(object_p), template)); default: template->ulValueLen = (CK_ULONG)-1; return (CKR_ATTRIBUTE_TYPE_INVALID); } case CKA_VALUE: switch (keytype) { case CKK_DSA: return (get_bigint_attr_from_object( OBJ_PRI_DSA_VALUE(object_p), template)); case CKK_DH: return (get_bigint_attr_from_object( OBJ_PRI_DH_VALUE(object_p), template)); case CKK_EC: return (get_bigint_attr_from_object( OBJ_PRI_EC_VALUE(object_p), template)); default: template->ulValueLen = (CK_ULONG)-1; return (CKR_ATTRIBUTE_TYPE_INVALID); } default: /* * First, get the value of the request attribute defined * in the list of common key attributes. If the request * attribute is not found in that list, then get the * attribute from the list of common attributes. */ rv = kernel_get_common_key_attrs(object_p, template); if (rv == CKR_ATTRIBUTE_TYPE_INVALID) { rv = kernel_get_common_attrs(object_p, template); } break; } return (rv); } /* * Get the value of a requested attribute of a Secret Key Object. * * Rule: All the attributes in the secret key object can be revealed * except those marked with footnote number "7" when the object * has its CKA_SENSITIVE attribute set to TRUE or its * CKA_EXTRACTABLE attribute set to FALSE (p.88 in PKCS11 spec.). */ CK_RV kernel_get_secret_key_attribute(kernel_object_t *object_p, CK_ATTRIBUTE_PTR template) { CK_RV rv = CKR_OK; CK_KEY_TYPE keytype = object_p->key_type; switch (template->type) { /* Key related boolean attributes */ case CKA_SENSITIVE: return (get_bool_attr_from_object(object_p, SENSITIVE_BOOL_ON, template)); case CKA_ENCRYPT: return (get_bool_attr_from_object(object_p, ENCRYPT_BOOL_ON, template)); case CKA_DECRYPT: return (get_bool_attr_from_object(object_p, DECRYPT_BOOL_ON, template)); case CKA_SIGN: return (get_bool_attr_from_object(object_p, SIGN_BOOL_ON, template)); case CKA_VERIFY: return (get_bool_attr_from_object(object_p, VERIFY_BOOL_ON, template)); case CKA_WRAP: return (get_bool_attr_from_object(object_p, WRAP_BOOL_ON, template)); case CKA_UNWRAP: return (get_bool_attr_from_object(object_p, UNWRAP_BOOL_ON, template)); case CKA_EXTRACTABLE: return (get_bool_attr_from_object(object_p, EXTRACTABLE_BOOL_ON, template)); case CKA_ALWAYS_SENSITIVE: return (get_bool_attr_from_object(object_p, ALWAYS_SENSITIVE_BOOL_ON, template)); case CKA_NEVER_EXTRACTABLE: return (get_bool_attr_from_object(object_p, NEVER_EXTRACTABLE_BOOL_ON, template)); case CKA_VALUE: /* * If the specified attribute for the secret key object * cannot be revealed because the object is sensitive * or unextractable, then the ulValueLen is set to -1. */ if ((object_p->bool_attr_mask & SENSITIVE_BOOL_ON) || !(object_p->bool_attr_mask & EXTRACTABLE_BOOL_ON)) { template->ulValueLen = (CK_ULONG)-1; return (CKR_ATTRIBUTE_SENSITIVE); } switch (keytype) { case CKK_RC4: case CKK_GENERIC_SECRET: case CKK_RC5: case CKK_DES: case CKK_DES2: case CKK_DES3: case CKK_CDMF: case CKK_AES: case CKK_BLOWFISH: /* * Copy secret key object attributes to template. */ if (template->pValue == NULL) { template->ulValueLen = OBJ_SEC_VALUE_LEN(object_p); return (CKR_OK); } if (OBJ_SEC_VALUE(object_p) == NULL) { template->ulValueLen = 0; return (CKR_OK); } if (template->ulValueLen >= OBJ_SEC_VALUE_LEN(object_p)) { (void) memcpy(template->pValue, OBJ_SEC_VALUE(object_p), OBJ_SEC_VALUE_LEN(object_p)); template->ulValueLen = OBJ_SEC_VALUE_LEN(object_p); return (CKR_OK); } else { template->ulValueLen = (CK_ULONG)-1; return (CKR_BUFFER_TOO_SMALL); } default: template->ulValueLen = (CK_ULONG)-1; rv = CKR_ATTRIBUTE_TYPE_INVALID; break; } break; case CKA_VALUE_LEN: return (get_ulong_attr_from_object(OBJ_SEC_VALUE_LEN(object_p), template)); default: /* * First, get the value of the request attribute defined * in the list of common key attributes. If the request * attribute is not found in that list, then get the * attribute from the list of common attributes. */ rv = kernel_get_common_key_attrs(object_p, template); if (rv == CKR_ATTRIBUTE_TYPE_INVALID) { rv = kernel_get_common_attrs(object_p, template); } break; } return (rv); } /* * Call the appropriate get attribute function according to the class * of object. * * The caller of this function holds the lock on the object. */ CK_RV kernel_get_attribute(kernel_object_t *object_p, CK_ATTRIBUTE_PTR template) { CK_RV rv = CKR_OK; CK_OBJECT_CLASS class = object_p->class; switch (class) { case CKO_PUBLIC_KEY: rv = kernel_get_public_key_attribute(object_p, template); break; case CKO_PRIVATE_KEY: rv = kernel_get_private_key_attribute(object_p, template); break; case CKO_SECRET_KEY: rv = kernel_get_secret_key_attribute(object_p, template); break; default: /* * If the specified attribute for the object is invalid * (the object does not possess such as attribute), then * the ulValueLen is modified to hold the value -1. */ template->ulValueLen = (CK_ULONG)-1; return (CKR_ATTRIBUTE_TYPE_INVALID); } return (rv); } /* * Set the value of an attribute that is common to all key objects * (i.e. public key, private key and secret key). */ CK_RV kernel_set_common_key_attribute(kernel_object_t *object_p, CK_ATTRIBUTE_PTR template, boolean_t copy, kernel_session_t *sp) { kernel_slot_t *pslot = slot_table[sp->ses_slotid]; CK_RV rv = CKR_OK; switch (template->type) { case CKA_LABEL: /* * Only the LABEL can be modified in the common storage * object attributes after the object is created. */ return (set_extra_attr_to_object(object_p, CKA_LABEL, template)); case CKA_ID: return (set_extra_attr_to_object(object_p, CKA_ID, template)); case CKA_START_DATE: return (set_extra_attr_to_object(object_p, CKA_START_DATE, template)); case CKA_END_DATE: return (set_extra_attr_to_object(object_p, CKA_END_DATE, template)); case CKA_DERIVE: return (set_bool_attr_to_object(object_p, DERIVE_BOOL_ON, template)); case CKA_CLASS: case CKA_KEY_TYPE: case CKA_LOCAL: return (CKR_ATTRIBUTE_READ_ONLY); case CKA_PRIVATE: if (!copy) { /* called from C_SetAttributeValue() */ return (CKR_ATTRIBUTE_READ_ONLY); } /* called from C_CopyObject() */ if ((*(CK_BBOOL *)template->pValue) != B_TRUE) { return (CKR_OK); } (void) pthread_mutex_lock(&pslot->sl_mutex); /* * Cannot create a private object if the token * has a keystore and the user isn't logged in. */ if (pslot->sl_func_list.fl_object_create && pslot->sl_state != CKU_USER) { rv = CKR_USER_NOT_LOGGED_IN; } else { rv = set_bool_attr_to_object(object_p, PRIVATE_BOOL_ON, template); } (void) pthread_mutex_unlock(&pslot->sl_mutex); return (rv); case CKA_MODIFIABLE: if (copy) { rv = set_bool_attr_to_object(object_p, MODIFIABLE_BOOL_ON, template); } else { rv = CKR_ATTRIBUTE_READ_ONLY; } return (rv); default: return (CKR_TEMPLATE_INCONSISTENT); } } /* * Set the value of an attribute of a Public Key Object. * * Rule: The attributes marked with footnote number "8" in the PKCS11 * spec may be modified (p.88 in PKCS11 spec.). */ CK_RV kernel_set_public_key_attribute(kernel_object_t *object_p, CK_ATTRIBUTE_PTR template, boolean_t copy, kernel_session_t *sp) { CK_KEY_TYPE keytype = object_p->key_type; switch (template->type) { case CKA_SUBJECT: return (set_extra_attr_to_object(object_p, CKA_SUBJECT, template)); case CKA_ENCRYPT: return (set_bool_attr_to_object(object_p, ENCRYPT_BOOL_ON, template)); case CKA_VERIFY: return (set_bool_attr_to_object(object_p, VERIFY_BOOL_ON, template)); case CKA_VERIFY_RECOVER: return (set_bool_attr_to_object(object_p, VERIFY_RECOVER_BOOL_ON, template)); case CKA_WRAP: return (set_bool_attr_to_object(object_p, WRAP_BOOL_ON, template)); case CKA_MODULUS: case CKA_MODULUS_BITS: case CKA_PUBLIC_EXPONENT: if (keytype == CKK_RSA) return (CKR_ATTRIBUTE_READ_ONLY); break; case CKA_SUBPRIME: case CKA_PRIME: case CKA_BASE: case CKA_VALUE: if (keytype == CKK_DSA) return (CKR_ATTRIBUTE_READ_ONLY); break; default: /* * Set the value of a common key attribute. */ return (kernel_set_common_key_attribute(object_p, template, copy, sp)); } /* * If we got this far, then the combination of key type * and requested attribute is invalid. */ return (CKR_ATTRIBUTE_TYPE_INVALID); } /* * Set the value of an attribute of a Private Key Object. * * Rule: The attributes marked with footnote number "8" in the PKCS11 * spec may be modified (p.88 in PKCS11 spec.). */ CK_RV kernel_set_private_key_attribute(kernel_object_t *object_p, CK_ATTRIBUTE_PTR template, boolean_t copy, kernel_session_t *sp) { CK_KEY_TYPE keytype = object_p->key_type; switch (template->type) { case CKA_SUBJECT: return (set_extra_attr_to_object(object_p, CKA_SUBJECT, template)); case CKA_SENSITIVE: /* * Cannot set SENSITIVE to FALSE if it is already ON. */ if (((*(CK_BBOOL *)template->pValue) == B_FALSE) && (object_p->bool_attr_mask & SENSITIVE_BOOL_ON)) { return (CKR_ATTRIBUTE_READ_ONLY); } if (*(CK_BBOOL *)template->pValue) object_p->bool_attr_mask |= SENSITIVE_BOOL_ON; return (CKR_OK); case CKA_DECRYPT: return (set_bool_attr_to_object(object_p, DECRYPT_BOOL_ON, template)); case CKA_SIGN: return (set_bool_attr_to_object(object_p, SIGN_BOOL_ON, template)); case CKA_SIGN_RECOVER: return (set_bool_attr_to_object(object_p, SIGN_RECOVER_BOOL_ON, template)); case CKA_UNWRAP: return (set_bool_attr_to_object(object_p, UNWRAP_BOOL_ON, template)); case CKA_EXTRACTABLE: /* * Cannot set EXTRACTABLE to TRUE if it is already OFF. */ if ((*(CK_BBOOL *)template->pValue) && !(object_p->bool_attr_mask & EXTRACTABLE_BOOL_ON)) { return (CKR_ATTRIBUTE_READ_ONLY); } if ((*(CK_BBOOL *)template->pValue) == B_FALSE) object_p->bool_attr_mask &= ~EXTRACTABLE_BOOL_ON; return (CKR_OK); case CKA_MODULUS: case CKA_PUBLIC_EXPONENT: case CKA_PRIVATE_EXPONENT: case CKA_PRIME_1: case CKA_PRIME_2: case CKA_EXPONENT_1: case CKA_EXPONENT_2: case CKA_COEFFICIENT: if (keytype == CKK_RSA) { return (CKR_ATTRIBUTE_READ_ONLY); } break; case CKA_SUBPRIME: case CKA_PRIME: case CKA_BASE: case CKA_VALUE: if (keytype == CKK_DSA) return (CKR_ATTRIBUTE_READ_ONLY); break; default: /* * Set the value of a common key attribute. */ return (kernel_set_common_key_attribute(object_p, template, copy, sp)); } /* * If we got this far, then the combination of key type * and requested attribute is invalid. */ return (CKR_ATTRIBUTE_TYPE_INVALID); } /* * Set the value of an attribute of a Secret Key Object. * * Rule: The attributes marked with footnote number "8" in the PKCS11 * spec may be modified (p.88 in PKCS11 spec.). */ CK_RV kernel_set_secret_key_attribute(kernel_object_t *object_p, CK_ATTRIBUTE_PTR template, boolean_t copy, kernel_session_t *sp) { CK_KEY_TYPE keytype = object_p->key_type; switch (template->type) { case CKA_SENSITIVE: /* * Cannot set SENSITIVE to FALSE if it is already ON. */ if (((*(CK_BBOOL *)template->pValue) == B_FALSE) && (object_p->bool_attr_mask & SENSITIVE_BOOL_ON)) { return (CKR_ATTRIBUTE_READ_ONLY); } if (*(CK_BBOOL *)template->pValue) object_p->bool_attr_mask |= SENSITIVE_BOOL_ON; return (CKR_OK); case CKA_ENCRYPT: return (set_bool_attr_to_object(object_p, ENCRYPT_BOOL_ON, template)); case CKA_DECRYPT: return (set_bool_attr_to_object(object_p, DECRYPT_BOOL_ON, template)); case CKA_SIGN: return (set_bool_attr_to_object(object_p, SIGN_BOOL_ON, template)); case CKA_VERIFY: return (set_bool_attr_to_object(object_p, VERIFY_BOOL_ON, template)); case CKA_WRAP: return (set_bool_attr_to_object(object_p, WRAP_BOOL_ON, template)); case CKA_UNWRAP: return (set_bool_attr_to_object(object_p, UNWRAP_BOOL_ON, template)); case CKA_EXTRACTABLE: /* * Cannot set EXTRACTABLE to TRUE if it is already OFF. */ if ((*(CK_BBOOL *)template->pValue) && !(object_p->bool_attr_mask & EXTRACTABLE_BOOL_ON)) { return (CKR_ATTRIBUTE_READ_ONLY); } if ((*(CK_BBOOL *)template->pValue) == B_FALSE) object_p->bool_attr_mask &= ~EXTRACTABLE_BOOL_ON; return (CKR_OK); case CKA_VALUE: return (CKR_ATTRIBUTE_READ_ONLY); case CKA_VALUE_LEN: if ((keytype == CKK_RC4) || (keytype == CKK_GENERIC_SECRET) || (keytype == CKK_AES) || (keytype == CKK_BLOWFISH)) return (CKR_ATTRIBUTE_READ_ONLY); break; default: /* * Set the value of a common key attribute. */ return (kernel_set_common_key_attribute(object_p, template, copy, sp)); } /* * If we got this far, then the combination of key type * and requested attribute is invalid. */ return (CKR_ATTRIBUTE_TYPE_INVALID); } /* * Call the appropriate set attribute function according to the class * of object. * * The caller of this function does not hold the lock on the original * object, since this function is setting the attribute on the new object * that is being modified. * */ CK_RV kernel_set_attribute(kernel_object_t *object_p, CK_ATTRIBUTE_PTR template, boolean_t copy, kernel_session_t *sp) { CK_RV rv = CKR_OK; CK_OBJECT_CLASS class = object_p->class; switch (class) { case CKO_PUBLIC_KEY: rv = kernel_set_public_key_attribute(object_p, template, copy, sp); break; case CKO_PRIVATE_KEY: rv = kernel_set_private_key_attribute(object_p, template, copy, sp); break; case CKO_SECRET_KEY: rv = kernel_set_secret_key_attribute(object_p, template, copy, sp); break; default: /* * If the template specifies a value of an attribute * which is incompatible with other existing attributes * of the object, then fails with return code * CKR_TEMPLATE_INCONSISTENT. */ rv = CKR_TEMPLATE_INCONSISTENT; break; } return (rv); } static CK_RV copy_bigint(biginteger_t *new_bigint, biginteger_t *old_bigint) { new_bigint->big_value = malloc((sizeof (CK_BYTE) * new_bigint->big_value_len)); if (new_bigint->big_value == NULL) { return (CKR_HOST_MEMORY); } (void) memcpy(new_bigint->big_value, old_bigint->big_value, (sizeof (CK_BYTE) * new_bigint->big_value_len)); return (CKR_OK); } static void free_public_key_attr(public_key_obj_t *pbk, CK_KEY_TYPE key_type) { if (pbk == NULL) { return; } switch (key_type) { case CKK_RSA: bigint_attr_cleanup(KEY_PUB_RSA_MOD(pbk)); bigint_attr_cleanup(KEY_PUB_RSA_PUBEXPO(pbk)); break; case CKK_DSA: bigint_attr_cleanup(KEY_PUB_DSA_PRIME(pbk)); bigint_attr_cleanup(KEY_PUB_DSA_SUBPRIME(pbk)); bigint_attr_cleanup(KEY_PUB_DSA_BASE(pbk)); bigint_attr_cleanup(KEY_PUB_DSA_VALUE(pbk)); break; default: break; } free(pbk); } CK_RV kernel_copy_public_key_attr(public_key_obj_t *old_pub_key_obj_p, public_key_obj_t **new_pub_key_obj_p, CK_KEY_TYPE key_type) { public_key_obj_t *pbk; CK_RV rv = CKR_OK; pbk = calloc(1, sizeof (public_key_obj_t)); if (pbk == NULL) { return (CKR_HOST_MEMORY); } switch (key_type) { case CKK_RSA: (void) memcpy(KEY_PUB_RSA(pbk), KEY_PUB_RSA(old_pub_key_obj_p), sizeof (rsa_pub_key_t)); /* copy modulus */ rv = copy_bigint(KEY_PUB_RSA_MOD(pbk), KEY_PUB_RSA_MOD(old_pub_key_obj_p)); if (rv != CKR_OK) { free_public_key_attr(pbk, key_type); return (rv); } /* copy public exponent */ rv = copy_bigint(KEY_PUB_RSA_PUBEXPO(pbk), KEY_PUB_RSA_PUBEXPO(old_pub_key_obj_p)); if (rv != CKR_OK) { free_public_key_attr(pbk, key_type); return (rv); } break; case CKK_DSA: (void) memcpy(KEY_PUB_DSA(pbk), KEY_PUB_DSA(old_pub_key_obj_p), sizeof (dsa_pub_key_t)); /* copy prime */ rv = copy_bigint(KEY_PUB_DSA_PRIME(pbk), KEY_PUB_DSA_PRIME(old_pub_key_obj_p)); if (rv != CKR_OK) { free_public_key_attr(pbk, key_type); return (rv); } /* copy subprime */ rv = copy_bigint(KEY_PUB_DSA_SUBPRIME(pbk), KEY_PUB_DSA_SUBPRIME(old_pub_key_obj_p)); if (rv != CKR_OK) { free_public_key_attr(pbk, key_type); return (rv); } /* copy base */ rv = copy_bigint(KEY_PUB_DSA_BASE(pbk), KEY_PUB_DSA_BASE(old_pub_key_obj_p)); if (rv != CKR_OK) { free_public_key_attr(pbk, key_type); return (rv); } /* copy value */ rv = copy_bigint(KEY_PUB_DSA_VALUE(pbk), KEY_PUB_DSA_VALUE(old_pub_key_obj_p)); if (rv != CKR_OK) { free_public_key_attr(pbk, key_type); return (rv); } break; default: break; } *new_pub_key_obj_p = pbk; return (rv); } static void free_private_key_attr(private_key_obj_t *pbk, CK_KEY_TYPE key_type) { if (pbk == NULL) { return; } switch (key_type) { case CKK_RSA: bigint_attr_cleanup(KEY_PRI_RSA_MOD(pbk)); bigint_attr_cleanup(KEY_PRI_RSA_PUBEXPO(pbk)); bigint_attr_cleanup(KEY_PRI_RSA_PRIEXPO(pbk)); bigint_attr_cleanup(KEY_PRI_RSA_PRIME1(pbk)); bigint_attr_cleanup(KEY_PRI_RSA_PRIME2(pbk)); bigint_attr_cleanup(KEY_PRI_RSA_EXPO1(pbk)); bigint_attr_cleanup(KEY_PRI_RSA_EXPO2(pbk)); bigint_attr_cleanup(KEY_PRI_RSA_COEF(pbk)); break; case CKK_DSA: bigint_attr_cleanup(KEY_PRI_DSA_PRIME(pbk)); bigint_attr_cleanup(KEY_PRI_DSA_SUBPRIME(pbk)); bigint_attr_cleanup(KEY_PRI_DSA_BASE(pbk)); bigint_attr_cleanup(KEY_PRI_DSA_VALUE(pbk)); break; default: break; } free(pbk); } CK_RV kernel_copy_private_key_attr(private_key_obj_t *old_pri_key_obj_p, private_key_obj_t **new_pri_key_obj_p, CK_KEY_TYPE key_type) { CK_RV rv = CKR_OK; private_key_obj_t *pbk; pbk = calloc(1, sizeof (private_key_obj_t)); if (pbk == NULL) { return (CKR_HOST_MEMORY); } switch (key_type) { case CKK_RSA: (void) memcpy(KEY_PRI_RSA(pbk), KEY_PRI_RSA(old_pri_key_obj_p), sizeof (rsa_pri_key_t)); /* copy modulus */ rv = copy_bigint(KEY_PRI_RSA_MOD(pbk), KEY_PRI_RSA_MOD(old_pri_key_obj_p)); if (rv != CKR_OK) { free_private_key_attr(pbk, key_type); return (rv); } /* copy public exponent */ rv = copy_bigint(KEY_PRI_RSA_PUBEXPO(pbk), KEY_PRI_RSA_PUBEXPO(old_pri_key_obj_p)); if (rv != CKR_OK) { free_private_key_attr(pbk, key_type); return (rv); } /* copy private exponent */ rv = copy_bigint(KEY_PRI_RSA_PRIEXPO(pbk), KEY_PRI_RSA_PRIEXPO(old_pri_key_obj_p)); if (rv != CKR_OK) { free_private_key_attr(pbk, key_type); return (rv); } /* copy prime_1 */ rv = copy_bigint(KEY_PRI_RSA_PRIME1(pbk), KEY_PRI_RSA_PRIME1(old_pri_key_obj_p)); if (rv != CKR_OK) { free_private_key_attr(pbk, key_type); return (rv); } /* copy prime_2 */ rv = copy_bigint(KEY_PRI_RSA_PRIME2(pbk), KEY_PRI_RSA_PRIME2(old_pri_key_obj_p)); if (rv != CKR_OK) { free_private_key_attr(pbk, key_type); return (rv); } /* copy exponent_1 */ rv = copy_bigint(KEY_PRI_RSA_EXPO1(pbk), KEY_PRI_RSA_EXPO1(old_pri_key_obj_p)); if (rv != CKR_OK) { free_private_key_attr(pbk, key_type); return (rv); } /* copy exponent_2 */ rv = copy_bigint(KEY_PRI_RSA_EXPO2(pbk), KEY_PRI_RSA_EXPO2(old_pri_key_obj_p)); if (rv != CKR_OK) { free_private_key_attr(pbk, key_type); return (rv); } /* copy coefficient */ rv = copy_bigint(KEY_PRI_RSA_COEF(pbk), KEY_PRI_RSA_COEF(old_pri_key_obj_p)); if (rv != CKR_OK) { free_private_key_attr(pbk, key_type); return (rv); } break; case CKK_DSA: (void) memcpy(KEY_PRI_DSA(pbk), KEY_PRI_DSA(old_pri_key_obj_p), sizeof (dsa_pri_key_t)); /* copy prime */ rv = copy_bigint(KEY_PRI_DSA_PRIME(pbk), KEY_PRI_DSA_PRIME(old_pri_key_obj_p)); if (rv != CKR_OK) { free_private_key_attr(pbk, key_type); return (rv); } /* copy subprime */ rv = copy_bigint(KEY_PRI_DSA_SUBPRIME(pbk), KEY_PRI_DSA_SUBPRIME(old_pri_key_obj_p)); if (rv != CKR_OK) { free_private_key_attr(pbk, key_type); return (rv); } /* copy base */ rv = copy_bigint(KEY_PRI_DSA_BASE(pbk), KEY_PRI_DSA_BASE(old_pri_key_obj_p)); if (rv != CKR_OK) { free_private_key_attr(pbk, key_type); return (rv); } /* copy value */ rv = copy_bigint(KEY_PRI_DSA_VALUE(pbk), KEY_PRI_DSA_VALUE(old_pri_key_obj_p)); if (rv != CKR_OK) { free_private_key_attr(pbk, key_type); return (rv); } break; default: break; } *new_pri_key_obj_p = pbk; return (rv); } CK_RV kernel_copy_secret_key_attr(secret_key_obj_t *old_secret_key_obj_p, secret_key_obj_t **new_secret_key_obj_p) { secret_key_obj_t *sk; sk = malloc(sizeof (secret_key_obj_t)); if (sk == NULL) { return (CKR_HOST_MEMORY); } (void) memcpy(sk, old_secret_key_obj_p, sizeof (secret_key_obj_t)); /* copy the secret key value */ sk->sk_value = malloc((sizeof (CK_BYTE) * sk->sk_value_len)); if (sk->sk_value == NULL) { free(sk); return (CKR_HOST_MEMORY); } (void) memcpy(sk->sk_value, old_secret_key_obj_p->sk_value, (sizeof (CK_BYTE) * sk->sk_value_len)); *new_secret_key_obj_p = sk; return (CKR_OK); } /* * If CKA_CLASS not given, guess CKA_CLASS using * attributes on template . * * Some attributes are specific to an object class. If one or more * of these attributes are in the template, make a list of classes * that can have these attributes. This would speed up the search later, * because we can immediately skip an object if the class of that * object can not possibly contain one of the attributes. * */ void kernel_process_find_attr(CK_OBJECT_CLASS *pclasses, CK_ULONG *num_result_pclasses, CK_ATTRIBUTE_PTR pTemplate, CK_ULONG ulCount) { ulong_t i; int j; boolean_t pub_found = B_FALSE, priv_found = B_FALSE, secret_found = B_FALSE, domain_found = B_FALSE, hardware_found = B_FALSE, cert_found = B_FALSE; int num_pub_key_attrs, num_priv_key_attrs, num_secret_key_attrs, num_domain_attrs, num_hardware_attrs, num_cert_attrs; int num_pclasses = 0; for (i = 0; i < ulCount; i++) { if (pTemplate[i].type == CKA_CLASS) { /* * don't need to guess the class, it is specified. * Just record the class, and return. */ pclasses[0] = (*((CK_OBJECT_CLASS *)pTemplate[i].pValue)); *num_result_pclasses = 1; return; } } num_pub_key_attrs = sizeof (PUB_KEY_ATTRS) / sizeof (CK_ATTRIBUTE_TYPE); num_priv_key_attrs = sizeof (PRIV_KEY_ATTRS) / sizeof (CK_ATTRIBUTE_TYPE); num_secret_key_attrs = sizeof (SECRET_KEY_ATTRS) / sizeof (CK_ATTRIBUTE_TYPE); num_domain_attrs = sizeof (DOMAIN_ATTRS) / sizeof (CK_ATTRIBUTE_TYPE); num_hardware_attrs = sizeof (HARDWARE_ATTRS) / sizeof (CK_ATTRIBUTE_TYPE); num_cert_attrs = sizeof (CERT_ATTRS) / sizeof (CK_ATTRIBUTE_TYPE); /* * Get the list of objects class that might contain * some attributes. */ for (i = 0; i < ulCount; i++) { /* * only check if this attribute can belong to public key object * class if public key object isn't already in the list */ if (!pub_found) { for (j = 0; j < num_pub_key_attrs; j++) { if (pTemplate[i].type == PUB_KEY_ATTRS[j]) { pub_found = B_TRUE; pclasses[num_pclasses++] = CKO_PUBLIC_KEY; break; } } } if (!priv_found) { for (j = 0; j < num_priv_key_attrs; j++) { if (pTemplate[i].type == PRIV_KEY_ATTRS[j]) { priv_found = B_TRUE; pclasses[num_pclasses++] = CKO_PRIVATE_KEY; break; } } } if (!secret_found) { for (j = 0; j < num_secret_key_attrs; j++) { if (pTemplate[i].type == SECRET_KEY_ATTRS[j]) { secret_found = B_TRUE; pclasses[num_pclasses++] = CKO_SECRET_KEY; break; } } } if (!domain_found) { for (j = 0; j < num_domain_attrs; j++) { if (pTemplate[i].type == DOMAIN_ATTRS[j]) { domain_found = B_TRUE; pclasses[num_pclasses++] = CKO_DOMAIN_PARAMETERS; break; } } } if (!hardware_found) { for (j = 0; j < num_hardware_attrs; j++) { if (pTemplate[i].type == HARDWARE_ATTRS[j]) { hardware_found = B_TRUE; pclasses[num_pclasses++] = CKO_HW_FEATURE; break; } } } if (!cert_found) { for (j = 0; j < num_cert_attrs; j++) { if (pTemplate[i].type == CERT_ATTRS[j]) { cert_found = B_TRUE; pclasses[num_pclasses++] = CKO_CERTIFICATE; break; } } } } *num_result_pclasses = num_pclasses; } boolean_t kernel_find_match_attrs(kernel_object_t *obj, CK_OBJECT_CLASS *pclasses, CK_ULONG num_pclasses, CK_ATTRIBUTE *template, CK_ULONG num_attr) { ulong_t i; CK_ATTRIBUTE *tmpl_attr, *obj_attr; uint64_t attr_mask; biginteger_t *bigint; boolean_t compare_attr, compare_bigint, compare_boolean; /* * Check if the class of this object match with any * of object classes that can possibly contain the * requested attributes. */ if (num_pclasses > 0) { for (i = 0; i < num_pclasses; i++) { if (obj->class == pclasses[i]) { break; } } if (i == num_pclasses) { /* * this object can't possibly contain one or * more attributes, don't need to check this object */ return (B_FALSE); } } /* need to examine everything */ for (i = 0; i < num_attr; i++) { tmpl_attr = &(template[i]); compare_attr = B_FALSE; compare_bigint = B_FALSE; compare_boolean = B_FALSE; switch (tmpl_attr->type) { /* First, check the most common attributes */ case CKA_CLASS: if (*((CK_OBJECT_CLASS *)tmpl_attr->pValue) != obj->class) { return (B_FALSE); } break; case CKA_KEY_TYPE: if (*((CK_KEY_TYPE *)tmpl_attr->pValue) != obj->key_type) { return (B_FALSE); } break; case CKA_ENCRYPT: attr_mask = (obj->bool_attr_mask) & ENCRYPT_BOOL_ON; compare_boolean = B_TRUE; break; case CKA_DECRYPT: attr_mask = (obj->bool_attr_mask) & DECRYPT_BOOL_ON; compare_boolean = B_TRUE; break; case CKA_WRAP: attr_mask = (obj->bool_attr_mask) & WRAP_BOOL_ON; compare_boolean = B_TRUE; break; case CKA_UNWRAP: attr_mask = (obj->bool_attr_mask) & UNWRAP_BOOL_ON; compare_boolean = B_TRUE; break; case CKA_SIGN: attr_mask = (obj->bool_attr_mask) & SIGN_BOOL_ON; compare_boolean = B_TRUE; break; case CKA_SIGN_RECOVER: attr_mask = (obj->bool_attr_mask) & SIGN_RECOVER_BOOL_ON; compare_boolean = B_TRUE; break; case CKA_VERIFY: attr_mask = (obj->bool_attr_mask) & VERIFY_BOOL_ON; compare_boolean = B_TRUE; break; case CKA_VERIFY_RECOVER: attr_mask = (obj->bool_attr_mask) & VERIFY_RECOVER_BOOL_ON; compare_boolean = B_TRUE; break; case CKA_DERIVE: attr_mask = (obj->bool_attr_mask) & DERIVE_BOOL_ON; compare_boolean = B_TRUE; break; case CKA_LOCAL: attr_mask = (obj->bool_attr_mask) & LOCAL_BOOL_ON; compare_boolean = B_TRUE; break; case CKA_SENSITIVE: attr_mask = (obj->bool_attr_mask) & SENSITIVE_BOOL_ON; compare_boolean = B_TRUE; break; case CKA_SECONDARY_AUTH: attr_mask = (obj->bool_attr_mask) & SECONDARY_AUTH_BOOL_ON; compare_boolean = B_TRUE; break; case CKA_TRUSTED: attr_mask = (obj->bool_attr_mask) & TRUSTED_BOOL_ON; compare_boolean = B_TRUE; break; case CKA_EXTRACTABLE: attr_mask = (obj->bool_attr_mask) & EXTRACTABLE_BOOL_ON; compare_boolean = B_TRUE; break; case CKA_ALWAYS_SENSITIVE: attr_mask = (obj->bool_attr_mask) & ALWAYS_SENSITIVE_BOOL_ON; compare_boolean = B_TRUE; break; case CKA_NEVER_EXTRACTABLE: attr_mask = (obj->bool_attr_mask) & NEVER_EXTRACTABLE_BOOL_ON; compare_boolean = B_TRUE; break; case CKA_TOKEN: /* * CKA_TOKEN value is not applicable to an object * created in the library, it should only contain * the default value FALSE */ attr_mask = 0; compare_boolean = B_TRUE; break; case CKA_PRIVATE: attr_mask = (obj->bool_attr_mask) & PRIVATE_BOOL_ON; compare_boolean = B_TRUE; break; case CKA_MODIFIABLE: attr_mask = (obj->bool_attr_mask) & MODIFIABLE_BOOL_ON; compare_boolean = B_TRUE; break; case CKA_SUBJECT: case CKA_ID: case CKA_START_DATE: case CKA_END_DATE: case CKA_KEY_GEN_MECHANISM: case CKA_LABEL: /* find these attributes from extra_attrlistp */ obj_attr = get_extra_attr(tmpl_attr->type, obj); compare_attr = B_TRUE; break; case CKA_VALUE_LEN: /* only secret key has this attribute */ if (obj->class == CKO_SECRET_KEY) { if (*((CK_ULONG *)tmpl_attr->pValue) != OBJ_SEC_VALUE_LEN(obj)) { return (B_FALSE); } } else { return (B_FALSE); } break; case CKA_VALUE: switch (obj->class) { case CKO_SECRET_KEY: /* * secret_key_obj_t is the same as * biginteger_t */ bigint = (biginteger_t *)OBJ_SEC(obj); break; case CKO_PRIVATE_KEY: if (obj->key_type == CKK_DSA) { bigint = OBJ_PRI_DSA_VALUE(obj); } else { return (B_FALSE); } break; case CKO_PUBLIC_KEY: if (obj->key_type == CKK_DSA) { bigint = OBJ_PUB_DSA_VALUE(obj); } else { return (B_FALSE); } break; default: return (B_FALSE); } compare_bigint = B_TRUE; break; case CKA_MODULUS: /* only RSA public and private key have this attr */ if (obj->key_type == CKK_RSA) { if (obj->class == CKO_PUBLIC_KEY) { bigint = OBJ_PUB_RSA_MOD(obj); } else if (obj->class == CKO_PRIVATE_KEY) { bigint = OBJ_PRI_RSA_MOD(obj); } else { return (B_FALSE); } compare_bigint = B_TRUE; } else { return (B_FALSE); } break; case CKA_MODULUS_BITS: /* only RSA public key has this attribute */ if ((obj->key_type == CKK_RSA) && (obj->class == CKO_PUBLIC_KEY)) { CK_ULONG mod_bits = OBJ_PUB_RSA_MOD_BITS(obj); if (mod_bits != *((CK_ULONG *)tmpl_attr->pValue)) { return (B_FALSE); } } else { return (B_FALSE); } break; case CKA_PUBLIC_EXPONENT: /* only RSA public and private key have this attr */ if (obj->key_type == CKK_RSA) { if (obj->class == CKO_PUBLIC_KEY) { bigint = OBJ_PUB_RSA_PUBEXPO(obj); } else if (obj->class == CKO_PRIVATE_KEY) { bigint = OBJ_PRI_RSA_PUBEXPO(obj); } else { return (B_FALSE); } compare_bigint = B_TRUE; } else { return (B_FALSE); } break; case CKA_PRIVATE_EXPONENT: /* only RSA private key has this attribute */ if ((obj->key_type == CKK_RSA) && (obj->class == CKO_PRIVATE_KEY)) { bigint = OBJ_PRI_RSA_PRIEXPO(obj); compare_bigint = B_TRUE; } else { return (B_FALSE); } break; case CKA_PRIME_1: /* only RSA private key has this attribute */ if ((obj->key_type == CKK_RSA) && (obj->class == CKO_PRIVATE_KEY)) { bigint = OBJ_PRI_RSA_PRIME1(obj); compare_bigint = B_TRUE; } else { return (B_FALSE); } break; case CKA_PRIME_2: /* only RSA private key has this attribute */ if ((obj->key_type == CKK_RSA) && (obj->class == CKO_PRIVATE_KEY)) { bigint = OBJ_PRI_RSA_PRIME2(obj); compare_bigint = B_TRUE; } else { return (B_FALSE); } break; case CKA_EXPONENT_1: /* only RSA private key has this attribute */ if ((obj->key_type == CKK_RSA) && (obj->class == CKO_PRIVATE_KEY)) { bigint = OBJ_PRI_RSA_EXPO1(obj); compare_bigint = B_TRUE; } else { return (B_FALSE); } break; case CKA_EXPONENT_2: /* only RSA private key has this attribute */ if ((obj->key_type == CKK_RSA) && (obj->class == CKO_PRIVATE_KEY)) { bigint = OBJ_PRI_RSA_EXPO2(obj); compare_bigint = B_TRUE; } else { return (B_FALSE); } break; case CKA_COEFFICIENT: /* only RSA private key has this attribute */ if ((obj->key_type == CKK_RSA) && (obj->class == CKO_PRIVATE_KEY)) { bigint = OBJ_PRI_RSA_COEF(obj); compare_bigint = B_TRUE; } else { return (B_FALSE); } break; case CKA_VALUE_BITS: return (B_FALSE); case CKA_PRIME: if (obj->class == CKO_PUBLIC_KEY) { switch (obj->key_type) { case CKK_DSA: bigint = OBJ_PUB_DSA_PRIME(obj); break; default: return (B_FALSE); } } else if (obj->class == CKO_PRIVATE_KEY) { switch (obj->key_type) { case CKK_DSA: bigint = OBJ_PRI_DSA_PRIME(obj); break; default: return (B_FALSE); } } else { return (B_FALSE); } compare_bigint = B_TRUE; break; case CKA_SUBPRIME: if (obj->class == CKO_PUBLIC_KEY) { switch (obj->key_type) { case CKK_DSA: bigint = OBJ_PUB_DSA_SUBPRIME(obj); break; default: return (B_FALSE); } } else if (obj->class == CKO_PRIVATE_KEY) { switch (obj->key_type) { case CKK_DSA: bigint = OBJ_PRI_DSA_SUBPRIME(obj); break; default: return (B_FALSE); } } else { return (B_FALSE); } compare_bigint = B_TRUE; break; case CKA_BASE: if (obj->class == CKO_PUBLIC_KEY) { switch (obj->key_type) { case CKK_DSA: bigint = OBJ_PUB_DSA_BASE(obj); break; default: return (B_FALSE); } } else if (obj->class == CKO_PRIVATE_KEY) { switch (obj->key_type) { case CKK_DSA: bigint = OBJ_PRI_DSA_BASE(obj); break; default: return (B_FALSE); } } else { return (B_FALSE); } compare_bigint = B_TRUE; break; case CKA_PRIME_BITS: return (B_FALSE); case CKA_SUBPRIME_BITS: return (B_FALSE); default: /* * any other attributes are currently not supported. * so, it's not possible for them to be in the * object */ return (B_FALSE); } if (compare_boolean) { CK_BBOOL bval; if (attr_mask) { bval = TRUE; } else { bval = FALSE; } if (bval != *((CK_BBOOL *)tmpl_attr->pValue)) { return (B_FALSE); } } else if (compare_bigint) { if (bigint == NULL) { return (B_FALSE); } if (tmpl_attr->ulValueLen != bigint->big_value_len) { return (B_FALSE); } if (memcmp(tmpl_attr->pValue, bigint->big_value, tmpl_attr->ulValueLen) != 0) { return (B_FALSE); } } else if (compare_attr) { if (obj_attr == NULL) { /* * The attribute type is valid, and its value * has not been initialized in the object. In * this case, it only matches the template's * attribute if the template's value length * is 0. */ if (tmpl_attr->ulValueLen != 0) return (B_FALSE); } else { if (tmpl_attr->ulValueLen != obj_attr->ulValueLen) { return (B_FALSE); } if (memcmp(tmpl_attr->pValue, obj_attr->pValue, tmpl_attr->ulValueLen) != 0) { return (B_FALSE); } } } } return (B_TRUE); } CK_ATTRIBUTE_PTR get_extra_attr(CK_ATTRIBUTE_TYPE type, kernel_object_t *obj) { CK_ATTRIBUTE_INFO_PTR tmp; tmp = obj->extra_attrlistp; while (tmp != NULL) { if (tmp->attr.type == type) { return (&(tmp->attr)); } tmp = tmp->next; } /* if get there, the specified attribute is not found */ return (NULL); } /* * 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. * Copyright 2018, Joyent, Inc. */ #include #include #include #include #include #include "kernelGlobal.h" #include "kernelSession.h" #include "kernelObject.h" /* * Real decryptInit work. The caller doesn't hold the session lock. */ CK_RV kernel_decrypt_init(kernel_session_t *session_p, kernel_object_t *key_p, CK_MECHANISM_PTR pMechanism) { CK_RV rv; crypto_decrypt_init_t decrypt_init; crypto_mech_type_t k_mech_type; boolean_t ses_lock_held = B_FALSE; int r; CK_AES_CCM_PARAMS ccm_params = { 0 }; /* Check to see if key object allows for decryption. */ if (key_p->is_lib_obj && !(key_p->bool_attr_mask & DECRYPT_BOOL_ON)) { return (CKR_KEY_TYPE_INCONSISTENT); } /* Get the kernel's internal mechanism number. */ rv = kernel_mech(pMechanism->mechanism, &k_mech_type); if (rv != CKR_OK) return (rv); (void) pthread_mutex_lock(&session_p->session_mutex); ses_lock_held = B_TRUE; /* * This active flag will remain ON until application calls either * C_Decrypt or C_DecryptFinal to actually obtain the final piece * of plaintext. */ session_p->decrypt.flags = CRYPTO_OPERATION_ACTIVE; /* set up key data */ if (!key_p->is_lib_obj) { decrypt_init.di_key.ck_format = CRYPTO_KEY_REFERENCE; decrypt_init.di_key.ck_obj_id = key_p->k_handle; } else { if (key_p->class == CKO_SECRET_KEY) { decrypt_init.di_key.ck_format = CRYPTO_KEY_RAW; decrypt_init.di_key.ck_data = get_symmetric_key_value(key_p); if (decrypt_init.di_key.ck_data == NULL) { rv = CKR_HOST_MEMORY; goto clean_exit; } /* KEF key lengths are expressed in bits */ decrypt_init.di_key.ck_length = OBJ_SEC(key_p)->sk_value_len << 3; } else if (key_p->key_type == CKK_RSA) { if (get_rsa_private_key(key_p, &decrypt_init.di_key) != CKR_OK) { rv = CKR_HOST_MEMORY; goto clean_exit; } } else { rv = CKR_KEY_TYPE_INCONSISTENT; goto clean_exit; } } decrypt_init.di_session = session_p->k_session; session_p->decrypt.mech = *pMechanism; /* Cache this capability value for efficiency */ if (INPLACE_MECHANISM(session_p->decrypt.mech.mechanism)) { session_p->decrypt.flags |= CRYPTO_OPERATION_INPLACE_OK; } (void) pthread_mutex_unlock(&session_p->session_mutex); ses_lock_held = B_FALSE; decrypt_init.di_mech.cm_type = k_mech_type; decrypt_init.di_mech.cm_param = pMechanism->pParameter; decrypt_init.di_mech.cm_param_len = pMechanism->ulParameterLen; /* * PKCS#11 uses CK_CCM_PARAMS as its mechanism parameter, while the * kernel uses CK_AES_CCM_PARAMS. Unlike * CK_GCM_PARAMS / CK_AES_GCM_PARAMS, the two definitions are not * equivalent -- the fields are defined in different orders, so * we must translate. */ if (session_p->decrypt.mech.mechanism == CKM_AES_CCM) { if (pMechanism->ulParameterLen != sizeof (CK_CCM_PARAMS)) { rv = CKR_MECHANISM_PARAM_INVALID; goto clean_exit; } p11_to_kernel_ccm_params(pMechanism->pParameter, &ccm_params); decrypt_init.di_mech.cm_param = (caddr_t)&ccm_params; decrypt_init.di_mech.cm_param_len = sizeof (ccm_params); } while ((r = ioctl(kernel_fd, CRYPTO_DECRYPT_INIT, &decrypt_init)) < 0) { if (errno != EINTR) break; } if (r < 0) { rv = CKR_FUNCTION_FAILED; } else { rv = crypto2pkcs11_error_number(decrypt_init.di_return_value); } /* Free memory allocated for decrypt_init.di_key */ if (key_p->is_lib_obj) { if (key_p->class == CKO_SECRET_KEY) { free(decrypt_init.di_key.ck_data); } else if (key_p->key_type == CKK_RSA) { free_key_attributes(&decrypt_init.di_key); } } clean_exit: /* * ccm_params does not contain any key material -- just lengths and * pointers, therefore it does not need to be zeroed on exit. */ if (!ses_lock_held) { (void) pthread_mutex_lock(&session_p->session_mutex); ses_lock_held = B_TRUE; } if (rv != CKR_OK) session_p->decrypt.flags &= ~CRYPTO_OPERATION_ACTIVE; if (ses_lock_held) { (void) pthread_mutex_unlock(&session_p->session_mutex); ses_lock_held = B_FALSE; } return (rv); } CK_RV C_DecryptInit(CK_SESSION_HANDLE hSession, CK_MECHANISM_PTR pMechanism, CK_OBJECT_HANDLE hKey) { CK_RV rv; kernel_session_t *session_p; kernel_object_t *key_p; boolean_t ses_lock_held = B_FALSE; if (!kernel_initialized) return (CKR_CRYPTOKI_NOT_INITIALIZED); if (pMechanism == NULL) { return (CKR_ARGUMENTS_BAD); } /* Obtain the session pointer. */ rv = handle2session(hSession, &session_p); if (rv != CKR_OK) return (rv); /* Obtain the object pointer. */ HANDLE2OBJECT(hKey, key_p, rv); if (rv == CKR_OK) { rv = kernel_decrypt_init(session_p, key_p, pMechanism); OBJ_REFRELE(key_p); } REFRELE(session_p, ses_lock_held); return (rv); } /* * Real decrypt work. The caller doesn't hold the session lock. */ CK_RV kernel_decrypt(kernel_session_t *session_p, CK_BYTE_PTR pEncryptedData, CK_ULONG ulEncryptedData, CK_BYTE_PTR pData, CK_ULONG_PTR pulDataLen) { crypto_decrypt_t decrypt; boolean_t ses_lock_held = B_FALSE; boolean_t inplace; CK_RV rv; int r; (void) pthread_mutex_lock(&session_p->session_mutex); ses_lock_held = B_TRUE; /* Application must call C_DecryptInit before calling C_Decrypt. */ if (!(session_p->decrypt.flags & CRYPTO_OPERATION_ACTIVE)) { rv = CKR_OPERATION_NOT_INITIALIZED; goto clean_exit; } /* * C_Decrypt must be called without intervening C_DecryptUpdate * calls. */ if (session_p->decrypt.flags & CRYPTO_OPERATION_UPDATE) { /* * C_Decrypt cannot be used to terminate a multiple-part * operation, so we'll leave the active decrypt operation * flag on and let the application continue with the * decrypt update operation. */ rv = CKR_FUNCTION_FAILED; goto clean_exit; } decrypt.cd_session = session_p->k_session; /* * Certain mechanisms, where the length of the plaintext is * same as the transformed ciphertext, can be optimized * by the kernel into an in-place operation. Unfortunately, * some applications use a plaintext buffer that is larger * than it needs to be. We fix that here. */ inplace = (session_p->decrypt.flags & CRYPTO_OPERATION_INPLACE_OK) != 0; if (ulEncryptedData < *pulDataLen && inplace) { decrypt.cd_datalen = ulEncryptedData; } else { decrypt.cd_datalen = *pulDataLen; } (void) pthread_mutex_unlock(&session_p->session_mutex); ses_lock_held = B_FALSE; decrypt.cd_databuf = (char *)pData; decrypt.cd_encrlen = ulEncryptedData; decrypt.cd_encrbuf = (char *)pEncryptedData; decrypt.cd_flags = ((inplace && (pData != NULL)) || (pData == pEncryptedData)) && (decrypt.cd_datalen == decrypt.cd_encrlen) ? CRYPTO_INPLACE_OPERATION : 0; while ((r = ioctl(kernel_fd, CRYPTO_DECRYPT, &decrypt)) < 0) { if (errno != EINTR) break; } if (r < 0) { rv = CKR_FUNCTION_FAILED; } else { rv = crypto2pkcs11_error_number(decrypt.cd_return_value); } if (rv == CKR_OK || rv == CKR_BUFFER_TOO_SMALL) *pulDataLen = decrypt.cd_datalen; clean_exit: if (ses_lock_held) (void) pthread_mutex_unlock(&session_p->session_mutex); return (rv); } CK_RV C_Decrypt(CK_SESSION_HANDLE hSession, CK_BYTE_PTR pEncryptedData, CK_ULONG ulEncryptedData, CK_BYTE_PTR pData, CK_ULONG_PTR pulDataLen) { CK_RV rv; kernel_session_t *session_p; boolean_t ses_lock_held = B_FALSE; if (!kernel_initialized) return (CKR_CRYPTOKI_NOT_INITIALIZED); /* Obtain the session pointer. */ rv = handle2session(hSession, &session_p); if (rv != CKR_OK) return (rv); /* * No need to check pData because application might * just want to know the length of decrypted data. */ if (pulDataLen == NULL) { rv = CKR_ARGUMENTS_BAD; goto clean_exit; } rv = kernel_decrypt(session_p, pEncryptedData, ulEncryptedData, pData, pulDataLen); if ((rv == CKR_BUFFER_TOO_SMALL) || (rv == CKR_OK && pData == NULL)) { /* * We will not terminate the active decrypt operation flag, * when the application-supplied buffer is too small, or * the application asks for the length of buffer to hold * the plaintext. */ REFRELE(session_p, ses_lock_held); return (rv); } clean_exit: /* * Terminates the active decrypt operation. * Application needs to call C_DecryptInit again for next * decrypt operation. */ (void) pthread_mutex_lock(&session_p->session_mutex); session_p->decrypt.flags = 0; ses_lock_held = B_TRUE; REFRELE(session_p, ses_lock_held); return (rv); } CK_RV C_DecryptUpdate(CK_SESSION_HANDLE hSession, CK_BYTE_PTR pEncryptedPart, CK_ULONG ulEncryptedPartLen, CK_BYTE_PTR pPart, CK_ULONG_PTR pulPartLen) { CK_RV rv; kernel_session_t *session_p; boolean_t ses_lock_held = B_FALSE; boolean_t inplace; crypto_decrypt_update_t decrypt_update; int r; if (!kernel_initialized) return (CKR_CRYPTOKI_NOT_INITIALIZED); /* Obtain the session pointer. */ rv = handle2session(hSession, &session_p); if (rv != CKR_OK) return (rv); if (pEncryptedPart == NULL) { rv = CKR_ARGUMENTS_BAD; goto clean_exit; } /* * Only check if pulPartLen is NULL. * No need to check if pPart is NULL because application * might just ask for the length of buffer to hold the * recovered data. */ if (pulPartLen == NULL) { rv = CKR_ARGUMENTS_BAD; goto clean_exit; } (void) pthread_mutex_lock(&session_p->session_mutex); ses_lock_held = B_TRUE; /* * Application must call C_DecryptInit before calling * C_DecryptUpdate. */ if (!(session_p->decrypt.flags & CRYPTO_OPERATION_ACTIVE)) { REFRELE(session_p, ses_lock_held); return (CKR_OPERATION_NOT_INITIALIZED); } session_p->decrypt.flags |= CRYPTO_OPERATION_UPDATE; decrypt_update.du_session = session_p->k_session; (void) pthread_mutex_unlock(&session_p->session_mutex); ses_lock_held = B_FALSE; decrypt_update.du_datalen = *pulPartLen; decrypt_update.du_databuf = (char *)pPart; decrypt_update.du_encrlen = ulEncryptedPartLen; decrypt_update.du_encrbuf = (char *)pEncryptedPart; inplace = (session_p->decrypt.flags & CRYPTO_OPERATION_INPLACE_OK) != 0; decrypt_update.du_flags = ((inplace && (pPart != NULL)) || (pPart == pEncryptedPart)) && (decrypt_update.du_datalen == decrypt_update.du_encrlen) ? CRYPTO_INPLACE_OPERATION : 0; while ((r = ioctl(kernel_fd, CRYPTO_DECRYPT_UPDATE, &decrypt_update)) < 0) { if (errno != EINTR) break; } if (r < 0) { rv = CKR_FUNCTION_FAILED; } else { rv = crypto2pkcs11_error_number( decrypt_update.du_return_value); } /* * If CKR_OK or CKR_BUFFER_TOO_SMALL, set the output length. * We don't terminate the current decryption operation. */ if (rv == CKR_OK || rv == CKR_BUFFER_TOO_SMALL) { *pulPartLen = decrypt_update.du_datalen; REFRELE(session_p, ses_lock_held); return (rv); } clean_exit: /* * After an error occurred, terminate the current decrypt * operation by resetting the active and update flags. */ (void) pthread_mutex_lock(&session_p->session_mutex); session_p->decrypt.flags = 0; ses_lock_held = B_TRUE; REFRELE(session_p, ses_lock_held); return (rv); } CK_RV C_DecryptFinal(CK_SESSION_HANDLE hSession, CK_BYTE_PTR pLastPart, CK_ULONG_PTR pulLastPartLen) { CK_RV rv; kernel_session_t *session_p; boolean_t ses_lock_held = B_FALSE; crypto_decrypt_final_t decrypt_final; int r; if (!kernel_initialized) return (CKR_CRYPTOKI_NOT_INITIALIZED); /* Obtain the session pointer. */ rv = handle2session(hSession, &session_p); if (rv != CKR_OK) return (rv); if (pulLastPartLen == NULL) { rv = CKR_ARGUMENTS_BAD; goto clean_exit; } (void) pthread_mutex_lock(&session_p->session_mutex); ses_lock_held = B_TRUE; /* * Application must call C_DecryptInit before calling * C_DecryptFinal. */ if (!(session_p->decrypt.flags & CRYPTO_OPERATION_ACTIVE)) { REFRELE(session_p, ses_lock_held); return (CKR_OPERATION_NOT_INITIALIZED); } decrypt_final.df_session = session_p->k_session; (void) pthread_mutex_unlock(&session_p->session_mutex); ses_lock_held = B_FALSE; decrypt_final.df_datalen = *pulLastPartLen; decrypt_final.df_databuf = (char *)pLastPart; while ((r = ioctl(kernel_fd, CRYPTO_DECRYPT_FINAL, &decrypt_final)) < 0) { if (errno != EINTR) break; } if (r < 0) { rv = CKR_FUNCTION_FAILED; } else { rv = crypto2pkcs11_error_number(decrypt_final.df_return_value); } if (rv == CKR_OK || rv == CKR_BUFFER_TOO_SMALL) *pulLastPartLen = decrypt_final.df_datalen; if (rv == CKR_BUFFER_TOO_SMALL || (rv == CKR_OK && pLastPart == NULL)) { /* * We will not terminate the active decrypt operation flag, * when the application-supplied buffer is too small, or * the application asks for the length of buffer to hold * the plaintext. */ REFRELE(session_p, ses_lock_held); return (rv); } clean_exit: /* Terminates the active decrypt operation */ (void) pthread_mutex_lock(&session_p->session_mutex); session_p->decrypt.flags = 0; ses_lock_held = B_TRUE; REFRELE(session_p, ses_lock_held); return (rv); } /* * 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. * Copyright 2018, Joyent, Inc. */ #include #include #include #include #include "kernelGlobal.h" #include "kernelSession.h" #include "kernelEmulate.h" static CK_RV common_digest_init(CK_SESSION_HANDLE hSession, CK_MECHANISM_PTR pMechanism, boolean_t is_external_caller) { CK_RV rv; kernel_session_t *session_p; boolean_t ses_lock_held = B_FALSE; crypto_digest_init_t digest_init; crypto_mech_type_t k_mech_type; int r; if (!kernel_initialized) return (CKR_CRYPTOKI_NOT_INITIALIZED); if (pMechanism == NULL) return (CKR_ARGUMENTS_BAD); /* * Get the kernel's internal mechanism number. */ rv = kernel_mech(pMechanism->mechanism, &k_mech_type); if (rv != CKR_OK) return (rv); /* * Obtain the session pointer. Also, increment the session * reference count. */ rv = handle2session(hSession, &session_p); if (rv != CKR_OK) return (rv); /* Acquire the session lock */ (void) pthread_mutex_lock(&session_p->session_mutex); ses_lock_held = B_TRUE; /* * This active flag will remain ON until application calls either * C_Digest or C_DigestFinal to actually obtain the value of * the message digest. */ session_p->digest.flags |= CRYPTO_OPERATION_ACTIVE; if (SLOT_HAS_LIMITED_HASH(session_p) && is_external_caller) { session_p->digest.mech.mechanism = pMechanism->mechanism; session_p->digest.mech.pParameter = NULL; session_p->digest.mech.ulParameterLen = 0; session_p->digest.flags |= CRYPTO_EMULATE; rv = emulate_buf_init(session_p, EDIGEST_LENGTH, OP_DIGEST); REFRELE(session_p, ses_lock_held); return (rv); } digest_init.di_session = session_p->k_session; (void) pthread_mutex_unlock(&session_p->session_mutex); ses_lock_held = B_FALSE; digest_init.di_mech.cm_type = k_mech_type; digest_init.di_mech.cm_param = pMechanism->pParameter; /* * If pParameter is NULL, set cm_param_len to be 0, so that ioctl call * will have a clean input data. */ if (pMechanism->pParameter != NULL) digest_init.di_mech.cm_param_len = pMechanism->ulParameterLen; else digest_init.di_mech.cm_param_len = 0; while ((r = ioctl(kernel_fd, CRYPTO_DIGEST_INIT, &digest_init)) < 0) { if (errno != EINTR) break; } if (r < 0) { rv = CKR_FUNCTION_FAILED; } else { rv = crypto2pkcs11_error_number(digest_init.di_return_value); } if (rv != CKR_OK) { (void) pthread_mutex_lock(&session_p->session_mutex); ses_lock_held = B_TRUE; session_p->digest.flags &= ~CRYPTO_OPERATION_ACTIVE; /* * Decrement the session reference count. * We hold the session lock, and REFRELE() * will release the session lock for us. */ REFRELE(session_p, ses_lock_held); return (rv); } /* * Decrement the session reference count. * We do not hold the session lock. */ REFRELE(session_p, ses_lock_held); return (rv); } CK_RV C_DigestInit(CK_SESSION_HANDLE hSession, CK_MECHANISM_PTR pMechanism) { return (common_digest_init(hSession, pMechanism, B_TRUE)); } CK_RV C_Digest(CK_SESSION_HANDLE hSession, CK_BYTE_PTR pData, CK_ULONG ulDataLen, CK_BYTE_PTR pDigest, CK_ULONG_PTR pulDigestLen) { CK_RV rv; kernel_session_t *session_p; boolean_t ses_lock_held = B_FALSE; crypto_digest_t digest; int r; if (!kernel_initialized) return (CKR_CRYPTOKI_NOT_INITIALIZED); /* * Obtain the session pointer. Also, increment the session * reference count. */ rv = handle2session(hSession, &session_p); if (rv != CKR_OK) return (rv); if ((pData == NULL && ulDataLen != 0) || pulDigestLen == NULL) { rv = CKR_ARGUMENTS_BAD; goto clean_exit; } /* Acquire the session lock */ (void) pthread_mutex_lock(&session_p->session_mutex); ses_lock_held = B_TRUE; /* Application must call C_DigestInit before calling C_Digest */ if (!(session_p->digest.flags & CRYPTO_OPERATION_ACTIVE)) { /* * Decrement the session reference count. * We hold the session lock, and REFRELE() * will release the session lock for us. */ REFRELE(session_p, ses_lock_held); return (CKR_OPERATION_NOT_INITIALIZED); } /* * C_Digest must be called without intervening C_DigestUpdate * calls. */ if (session_p->digest.flags & CRYPTO_OPERATION_UPDATE) { /* * C_Digest can not be used to terminate a multi-part * operation, so we'll leave the active digest operation * flag on and let the application continue with the * digest update operation. * * Decrement the session reference count. * We hold the session lock, and REFRELE() * will release the session lock for us. */ REFRELE(session_p, ses_lock_held); return (CKR_FUNCTION_FAILED); } if (session_p->digest.flags & CRYPTO_EMULATE) { crypto_active_op_t *opp; CK_MECHANISM_PTR pMechanism; opp = &(session_p->digest); if (opp->context == NULL) { REFRELE(session_p, ses_lock_held); return (CKR_ARGUMENTS_BAD); } pMechanism = &(opp->mech); if ((ulDataLen < SLOT_THRESHOLD(session_p)) || (ulDataLen > SLOT_HASH_MAX_INDATA_LEN(session_p))) { session_p->digest.flags |= CRYPTO_EMULATE_USING_SW; (void) pthread_mutex_unlock(&session_p->session_mutex); ses_lock_held = B_FALSE; rv = do_soft_digest(get_spp(opp), pMechanism, pData, ulDataLen, pDigest, pulDigestLen, OP_INIT | OP_SINGLE); goto done; } else if (!(session_p->digest.flags & CRYPTO_EMULATE_INIT_DONE)) { session_p->digest.flags |= CRYPTO_EMULATE_INIT_DONE; (void) pthread_mutex_unlock(&session_p->session_mutex); ses_lock_held = B_FALSE; rv = common_digest_init(hSession, pMechanism, B_FALSE); if (rv != CKR_OK) goto clean_exit; (void) pthread_mutex_lock(&session_p->session_mutex); ses_lock_held = B_TRUE; } } digest.cd_session = session_p->k_session; (void) pthread_mutex_unlock(&session_p->session_mutex); ses_lock_held = B_FALSE; digest.cd_datalen = ulDataLen; digest.cd_databuf = (char *)pData; digest.cd_digestbuf = (char *)pDigest; digest.cd_digestlen = *pulDigestLen; while ((r = ioctl(kernel_fd, CRYPTO_DIGEST, &digest)) < 0) { if (errno != EINTR) break; } if (r < 0) { rv = CKR_FUNCTION_FAILED; } else { rv = crypto2pkcs11_error_number(digest.cd_return_value); } if ((rv == CKR_OK) || (rv == CKR_BUFFER_TOO_SMALL)) *pulDigestLen = digest.cd_digestlen; done: if ((rv == CKR_BUFFER_TOO_SMALL) || (rv == CKR_OK && pDigest == NULL)) { /* * We will not terminate the active digest operation flag, * when the application-supplied buffer is too small, or * the application asks for the length of buffer to hold * the message digest. * * Decrement the session reference count. * We do not hold the session lock. */ REFRELE(session_p, ses_lock_held); return (rv); } clean_exit: /* * Terminates the active digest operation. * Application needs to call C_DigestInit again for next * digest operation. */ (void) pthread_mutex_lock(&session_p->session_mutex); ses_lock_held = B_TRUE; REINIT_OPBUF(&session_p->digest); session_p->digest.flags = 0; /* * Decrement the session reference count. * We hold the session lock, and REFRELE() * will release the session lock for us. */ REFRELE(session_p, ses_lock_held); return (rv); } CK_RV C_DigestUpdate(CK_SESSION_HANDLE hSession, CK_BYTE_PTR pPart, CK_ULONG ulPartLen) { CK_RV rv; kernel_session_t *session_p; boolean_t ses_lock_held = B_FALSE; crypto_digest_update_t digest_update; int r; if (!kernel_initialized) return (CKR_CRYPTOKI_NOT_INITIALIZED); /* * Obtain the session pointer. Also, increment the session * reference count. */ rv = handle2session(hSession, &session_p); if (rv != CKR_OK) return (rv); if (pPart == NULL) { rv = CKR_ARGUMENTS_BAD; goto clean_exit; } /* Acquire the session lock */ (void) pthread_mutex_lock(&session_p->session_mutex); ses_lock_held = B_TRUE; /* * Application must call C_DigestInit before calling * C_DigestUpdate. */ if (!(session_p->digest.flags & CRYPTO_OPERATION_ACTIVE)) { /* * Decrement the session reference count. * We hold the session lock, and REFRELE() * will release the session lock for us. */ REFRELE(session_p, ses_lock_held); return (CKR_OPERATION_NOT_INITIALIZED); } /* Set update flag to protect C_Digest */ session_p->digest.flags |= CRYPTO_OPERATION_UPDATE; if (session_p->digest.flags & CRYPTO_EMULATE) { (void) pthread_mutex_unlock(&session_p->session_mutex); ses_lock_held = B_FALSE; rv = emulate_update(session_p, pPart, ulPartLen, OP_DIGEST); goto done; } digest_update.du_session = session_p->k_session; (void) pthread_mutex_unlock(&session_p->session_mutex); ses_lock_held = B_FALSE; digest_update.du_datalen = ulPartLen; digest_update.du_databuf = (char *)pPart; while ((r = ioctl(kernel_fd, CRYPTO_DIGEST_UPDATE, &digest_update)) < 0) { if (errno != EINTR) break; } if (r < 0) { rv = CKR_FUNCTION_FAILED; } else { rv = crypto2pkcs11_error_number(digest_update.du_return_value); } done: if (rv == CKR_OK) { /* * Decrement the session reference count. * We do not hold the session lock. */ REFRELE(session_p, ses_lock_held); return (CKR_OK); } clean_exit: /* * After an error occurred, terminate the current digest * operation by resetting the active and update flags. */ (void) pthread_mutex_lock(&session_p->session_mutex); ses_lock_held = B_TRUE; REINIT_OPBUF(&session_p->digest); session_p->digest.flags = 0; /* * Decrement the session reference count. * We hold the session lock, and REFRELE() * will release the session lock for us. */ REFRELE(session_p, ses_lock_held); return (rv); } CK_RV C_DigestKey(CK_SESSION_HANDLE hSession, CK_OBJECT_HANDLE hKey) { CK_RV rv; kernel_session_t *session_p; kernel_object_t *key_p; boolean_t ses_lock_held = B_FALSE; CK_BYTE_PTR pPart; CK_ULONG ulPartLen; crypto_digest_key_t digest_key; crypto_digest_update_t digest_update; int r; if (!kernel_initialized) return (CKR_CRYPTOKI_NOT_INITIALIZED); /* * Obtain the session pointer. Also, increment the session * reference count. */ rv = handle2session(hSession, &session_p); if (rv != CKR_OK) return (rv); /* Obtain the object pointer. */ HANDLE2OBJECT(hKey, key_p, rv); if (rv != CKR_OK) { (void) pthread_mutex_lock(&session_p->session_mutex); ses_lock_held = B_TRUE; REINIT_OPBUF(&session_p->digest); session_p->digest.flags = 0; REFRELE(session_p, ses_lock_held); return (rv); } /* Check the key type */ if (key_p->is_lib_obj && (key_p->class != CKO_SECRET_KEY)) { rv = CKR_KEY_INDIGESTIBLE; goto clean_exit; } /* * Application must call C_DigestInit before calling * C_DigestKey. */ (void) pthread_mutex_lock(&session_p->session_mutex); ses_lock_held = B_TRUE; if (!(session_p->digest.flags & CRYPTO_OPERATION_ACTIVE)) { /* * Decrement the session reference count. * We hold the session lock, and REFRELE() * will release the session lock for us. */ OBJ_REFRELE(key_p); REFRELE(session_p, ses_lock_held); return (CKR_OPERATION_NOT_INITIALIZED); } session_p->digest.flags |= CRYPTO_OPERATION_UPDATE; /* * If the key object is from the HW provider, call CRYPTO_DIGEST_KEY * ioctl. Otherwise, call CRYPTO_DIGEST_UPDATE ioctl and pass the key * by value. */ if (key_p->is_lib_obj) { digest_update.du_session = session_p->k_session; } else { digest_key.dk_session = session_p->k_session; } (void) pthread_mutex_unlock(&session_p->session_mutex); ses_lock_held = B_FALSE; if (!key_p->is_lib_obj) { if (session_p->digest.flags & CRYPTO_EMULATE) { rv = CKR_FUNCTION_NOT_SUPPORTED; goto clean_exit; } digest_key.dk_key.ck_format = CRYPTO_KEY_REFERENCE; digest_key.dk_key.ck_obj_id = key_p->k_handle; while ((r = ioctl(kernel_fd, CRYPTO_DIGEST_KEY, &digest_key)) < 0) { if (errno != EINTR) break; } if (r < 0) { rv = CKR_FUNCTION_FAILED; } else { rv = crypto2pkcs11_error_number( digest_key.dk_return_value); } } else { ulPartLen = OBJ_SEC_VALUE_LEN(key_p); if (ulPartLen == 0) { rv = CKR_KEY_SIZE_RANGE; goto clean_exit; } pPart = (CK_BYTE_PTR) OBJ_SEC_VALUE(key_p); if (pPart == NULL) { rv = CKR_KEY_HANDLE_INVALID; goto clean_exit; } (void) pthread_mutex_lock(&session_p->session_mutex); ses_lock_held = B_TRUE; if (session_p->digest.flags & CRYPTO_EMULATE) { (void) pthread_mutex_unlock(&session_p->session_mutex); ses_lock_held = B_FALSE; rv = emulate_update(session_p, pPart, ulPartLen, OP_DIGEST); goto done; } (void) pthread_mutex_unlock(&session_p->session_mutex); ses_lock_held = B_FALSE; digest_update.du_datalen = ulPartLen; digest_update.du_databuf = (char *)pPart; while ((r = ioctl(kernel_fd, CRYPTO_DIGEST_UPDATE, &digest_update)) < 0) { if (errno != EINTR) break; } if (r < 0) { rv = CKR_FUNCTION_FAILED; } else { rv = crypto2pkcs11_error_number( digest_update.du_return_value); } } done: if (rv == CKR_OK) { /* * Decrement the session reference count. * We do not hold the session lock. */ OBJ_REFRELE(key_p); REFRELE(session_p, ses_lock_held); return (CKR_OK); } clean_exit: OBJ_REFRELE(key_p); /* * After an error occurred, terminate the current digest * operation by resetting the active and update flags. */ (void) pthread_mutex_lock(&session_p->session_mutex); ses_lock_held = B_TRUE; REINIT_OPBUF(&session_p->digest); session_p->digest.flags = 0; /* * Decrement the session reference count. * We hold the session lock, and REFRELE() * will release the session lock for us. */ REFRELE(session_p, ses_lock_held); return (rv); } CK_RV C_DigestFinal(CK_SESSION_HANDLE hSession, CK_BYTE_PTR pDigest, CK_ULONG_PTR pulDigestLen) { CK_RV rv; kernel_session_t *session_p; boolean_t ses_lock_held = B_FALSE; crypto_digest_final_t digest_final; int r; if (!kernel_initialized) return (CKR_CRYPTOKI_NOT_INITIALIZED); /* * Obtain the session pointer. Also, increment the session * reference count. */ rv = handle2session(hSession, &session_p); if (rv != CKR_OK) return (rv); if (pulDigestLen == NULL) { rv = CKR_ARGUMENTS_BAD; goto clean_exit; } /* Acquire the session lock */ (void) pthread_mutex_lock(&session_p->session_mutex); ses_lock_held = B_TRUE; /* * Application must call C_DigestInit before calling * C_DigestFinal. */ if (!(session_p->digest.flags & CRYPTO_OPERATION_ACTIVE)) { /* * Decrement the session reference count. * We hold the session lock, and REFRELE() * will release the session lock for us. */ REFRELE(session_p, ses_lock_held); return (CKR_OPERATION_NOT_INITIALIZED); } /* The order of checks is important here */ if (session_p->digest.flags & CRYPTO_EMULATE_USING_SW) { if (session_p->digest.flags & CRYPTO_EMULATE_UPDATE_DONE) { (void) pthread_mutex_unlock(&session_p->session_mutex); ses_lock_held = B_FALSE; rv = do_soft_digest(get_spp(&session_p->digest), NULL, NULL, 0, pDigest, pulDigestLen, OP_FINAL); } else { /* * We end up here if an earlier C_DigestFinal() call * took the C_Digest() path and it had returned * CKR_BUFFER_TOO_SMALL. */ digest_buf_t *bufp = session_p->digest.context; (void) pthread_mutex_unlock(&session_p->session_mutex); ses_lock_held = B_FALSE; if (bufp == NULL || bufp->buf == NULL) { rv = CKR_ARGUMENTS_BAD; goto clean_exit; } rv = do_soft_digest(get_spp(&session_p->digest), NULL, bufp->buf, bufp->indata_len, pDigest, pulDigestLen, OP_SINGLE); } goto done; } else if (session_p->digest.flags & CRYPTO_EMULATE) { digest_buf_t *bufp = session_p->digest.context; /* * We are emulating a single-part operation now. * So, clear the flag. */ session_p->digest.flags &= ~CRYPTO_OPERATION_UPDATE; if (bufp == NULL || bufp->buf == NULL) { rv = CKR_ARGUMENTS_BAD; goto clean_exit; } REFRELE(session_p, ses_lock_held); rv = C_Digest(hSession, bufp->buf, bufp->indata_len, pDigest, pulDigestLen); return (rv); } digest_final.df_session = session_p->k_session; (void) pthread_mutex_unlock(&session_p->session_mutex); ses_lock_held = B_FALSE; digest_final.df_digestlen = *pulDigestLen; digest_final.df_digestbuf = (char *)pDigest; while ((r = ioctl(kernel_fd, CRYPTO_DIGEST_FINAL, &digest_final)) < 0) { if (errno != EINTR) break; } if (r < 0) { rv = CKR_FUNCTION_FAILED; } else { rv = crypto2pkcs11_error_number(digest_final.df_return_value); } if ((rv == CKR_OK) || (rv == CKR_BUFFER_TOO_SMALL)) *pulDigestLen = digest_final.df_digestlen; done: if ((rv == CKR_BUFFER_TOO_SMALL) || (rv == CKR_OK && pDigest == NULL)) { /* * We will not terminate the active digest operation flag, * when the application-supplied buffer is too small, or * the application asks for the length of buffer to hold * the message digest. * * Decrement the session reference count. * We do not hold the session lock. */ REFRELE(session_p, ses_lock_held); return (rv); } clean_exit: /* Terminates the active digest operation */ (void) pthread_mutex_lock(&session_p->session_mutex); ses_lock_held = B_TRUE; REINIT_OPBUF(&session_p->digest); session_p->digest.flags = 0; /* * Decrement the session reference count. * We hold the session lock, and REFRELE() * will release the session lock for us. */ REFRELE(session_p, ses_lock_held); return (rv); } /* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License, Version 1.0 only * (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 2003 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ #include "kernelGlobal.h" #include /*ARGSUSED*/ CK_RV C_DigestEncryptUpdate(CK_SESSION_HANDLE hSession, CK_BYTE_PTR pPart, CK_ULONG ulPartLen, CK_BYTE_PTR pEncryptedPart, CK_ULONG_PTR pulEncryptedPartLen) { if (!kernel_initialized) return (CKR_CRYPTOKI_NOT_INITIALIZED); return (CKR_FUNCTION_NOT_SUPPORTED); } /*ARGSUSED*/ CK_RV C_DecryptDigestUpdate(CK_SESSION_HANDLE hSession, CK_BYTE_PTR pEncryptedPart, CK_ULONG ulEncryptedPartLen, CK_BYTE_PTR pPart, CK_ULONG_PTR pulPartLen) { if (!kernel_initialized) return (CKR_CRYPTOKI_NOT_INITIALIZED); return (CKR_FUNCTION_NOT_SUPPORTED); } /*ARGSUSED*/ CK_RV C_SignEncryptUpdate(CK_SESSION_HANDLE hSession, CK_BYTE_PTR pPart, CK_ULONG ulPartLen, CK_BYTE_PTR pEncryptedPart, CK_ULONG_PTR pulEncryptedPartLen) { if (!kernel_initialized) return (CKR_CRYPTOKI_NOT_INITIALIZED); return (CKR_FUNCTION_NOT_SUPPORTED); } /*ARGSUSED*/ CK_RV C_DecryptVerifyUpdate(CK_SESSION_HANDLE hSession, CK_BYTE_PTR pEncryptedPart, CK_ULONG ulEncryptedPartLen, CK_BYTE_PTR pPart, CK_ULONG_PTR pulPartLen) { if (!kernel_initialized) return (CKR_CRYPTOKI_NOT_INITIALIZED); return (CKR_FUNCTION_NOT_SUPPORTED); } /* * 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. */ #include #include #include #include #include #include "kernelGlobal.h" #include "kernelSession.h" #include "kernelEmulate.h" /* * Helper routine to know if this is a HMAC. We can't just check * the CKF_SIGN mech flag as it is set for non-HMAC mechs too. */ boolean_t is_hmac(CK_MECHANISM_TYPE mechanism) { switch (mechanism) { case CKM_SSL3_MD5_MAC: case CKM_SSL3_SHA1_MAC: case CKM_MD5_HMAC_GENERAL: case CKM_MD5_HMAC: case CKM_SHA_1_HMAC_GENERAL: case CKM_SHA_1_HMAC: case CKM_SHA256_HMAC_GENERAL: case CKM_SHA256_HMAC: case CKM_SHA384_HMAC_GENERAL: case CKM_SHA384_HMAC: case CKM_SHA512_HMAC_GENERAL: case CKM_SHA512_HMAC: return (B_TRUE); default: return (B_FALSE); } } /* * Helper routine to allocate an emulation structure for the session. * buflen indicates the size of the scratch buffer to be allocated. */ CK_RV emulate_buf_init(kernel_session_t *session_p, int buflen, int opflag) { digest_buf_t *bufp; crypto_active_op_t *opp; opp = (opflag & OP_DIGEST) ? &(session_p->digest) : \ ((opflag & OP_SIGN) ? &(session_p->sign) : &(session_p->verify)); bufp = opp->context; if (bufp != NULL) { bufp->indata_len = 0; /* * We can reuse the context structure, digest_buf_t. * See if we can reuse the scratch buffer in the context too. */ if (buflen > bufp->buf_len) { free(bufp->buf); bufp->buf = NULL; } } else { bufp = opp->context = calloc(1, sizeof (digest_buf_t)); if (bufp == NULL) { return (CKR_HOST_MEMORY); } } if (bufp->buf == NULL) { bufp->buf = malloc(buflen); if (bufp->buf == NULL) { free(bufp); opp->context = NULL; return (CKR_HOST_MEMORY); } bufp->buf_len = buflen; } return (CKR_OK); } /* * Setup the support necessary to do this operation in a * single part. We allocate a buffer to accumulate the * input data from later calls. We also get ready for * the case where we have to do it in software by initializing * a standby context. The opflag tells if this is a sign or verify. */ CK_RV emulate_init(kernel_session_t *session_p, CK_MECHANISM_PTR pMechanism, crypto_key_t *keyp, int opflag) { CK_RV rv; crypto_active_op_t *opp; if ((rv = emulate_buf_init(session_p, EDIGEST_LENGTH, opflag)) != CKR_OK) return (rv); opp = (opflag & OP_SIGN) ? &(session_p->sign) : &(session_p->verify); opflag |= OP_INIT; rv = do_soft_hmac_init(get_spp(opp), pMechanism, keyp->ck_data, keyp->ck_length >> 3, opflag); return (rv); } #define DO_SOFT_UPDATE(opp, pPart, ulPartLen, opflag) \ if ((opflag) & OP_DIGEST) { \ rv = do_soft_digest(get_spp(opp), NULL, pPart, \ ulPartLen, NULL, NULL, opflag); \ } else { \ rv = do_soft_hmac_update(get_spp(opp), pPart, \ ulPartLen, opflag); \ } /* * Accumulate the input data in the buffer, allocating a bigger * buffer if needed. If we reach the maximum input data size * that can be accumulated, start using the software from then on. * The opflag tells if this is a digest, sign or verify. */ CK_RV emulate_update(kernel_session_t *session_p, CK_BYTE_PTR pPart, CK_ULONG ulPartLen, int opflag) { CK_RV rv; int maxlen; digest_buf_t *bufp; boolean_t use_soft = B_FALSE; crypto_active_op_t *opp; if (opflag & OP_DIGEST) { opp = &(session_p->digest); if (!SLOT_HAS_LIMITED_HASH(session_p)) return (CKR_ARGUMENTS_BAD); maxlen = SLOT_HASH_MAX_INDATA_LEN(session_p); } else if (opflag & (OP_SIGN | OP_VERIFY)) { opp = (opflag & OP_SIGN) ? &(session_p->sign) : &(session_p->verify); if (!SLOT_HAS_LIMITED_HMAC(session_p)) return (CKR_ARGUMENTS_BAD); maxlen = SLOT_HMAC_MAX_INDATA_LEN(session_p); } else return (CKR_ARGUMENTS_BAD); if (opp->flags & CRYPTO_EMULATE_USING_SW) { opflag |= OP_UPDATE; DO_SOFT_UPDATE(opp, pPart, ulPartLen, opflag); opp->flags |= CRYPTO_EMULATE_UPDATE_DONE; return (rv); } bufp = opp->context; if (bufp == NULL) { return (CKR_FUNCTION_FAILED); } /* Did we exceed the maximum allowed? */ if (bufp->indata_len + ulPartLen > maxlen) { use_soft = B_TRUE; } else if (ulPartLen > (bufp->buf_len - bufp->indata_len)) { int siz = ulPartLen < bufp->buf_len ? bufp->buf_len * 2 : bufp->buf_len + ulPartLen; uint8_t *old = bufp->buf; bufp->buf = realloc(bufp->buf, siz); if (bufp->buf == NULL) { /* Try harder rather than failing */ bufp->buf = old; use_soft = B_TRUE; } else bufp->buf_len = siz; } if (use_soft) { opp->flags |= CRYPTO_EMULATE_USING_SW; if (opflag & OP_DIGEST) { CK_MECHANISM_PTR pMechanism; pMechanism = &(opp->mech); rv = do_soft_digest(get_spp(opp), pMechanism, NULL, 0, NULL, NULL, OP_INIT); if (rv != CKR_OK) return (rv); } opflag |= OP_UPDATE; DO_SOFT_UPDATE(opp, bufp->buf, bufp->indata_len, opflag); opp->flags |= CRYPTO_EMULATE_UPDATE_DONE; if (rv == CKR_OK) { DO_SOFT_UPDATE(opp, pPart, ulPartLen, opflag); } return (rv); } /* accumulate the update data */ bcopy(pPart, bufp->buf + bufp->indata_len, ulPartLen); bufp->indata_len += ulPartLen; return (CKR_OK); } /* * 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. */ #ifndef _KERNEL_EMULATE_H #define _KERNEL_EMULATE_H #ifdef __cplusplus extern "C" { #endif #include #include #include #include "kernelGlobal.h" #include "kernelSession.h" #include "kernelSoftCommon.h" #define SLOT_THRESHOLD(sp) (slot_table[sp->ses_slotid]->sl_threshold) #define SLOT_HASH_MAX_INDATA_LEN(sp) \ (slot_table[sp->ses_slotid]->sl_hash_max_inlen) #define SLOT_HMAC_MAX_INDATA_LEN(sp) \ (slot_table[sp->ses_slotid]->sl_hmac_max_inlen) #define SLOT_HAS_LIMITED_HASH(sp) (slot_table[sp->ses_slotid]->sl_flags & \ CRYPTO_LIMITED_HASH_SUPPORT) #define SLOT_HAS_LIMITED_HMAC(sp) (slot_table[sp->ses_slotid]->sl_flags & \ CRYPTO_LIMITED_HMAC_SUPPORT) #define get_sp(opp) (((digest_buf_t *)((opp)->context))->soft_sp) #define get_spp(opp) (&(((digest_buf_t *)((opp)->context))->soft_sp)) /* reinit buffer so that we can reuse it */ #define REINIT_OPBUF(opp) { \ if ((opp)->flags & CRYPTO_EMULATE) { \ digest_buf_t *bufp = (opp)->context; \ if (bufp != NULL) { \ bzero(bufp->buf, bufp->indata_len); \ bufp->indata_len = 0; \ } \ } \ } boolean_t is_hmac(CK_MECHANISM_TYPE mechanism); CK_RV emulate_buf_init(kernel_session_t *session_p, int buflen, int opflag); CK_RV emulate_init(kernel_session_t *session_p, CK_MECHANISM_PTR pMechanism, crypto_key_t *keyp, int opflag); CK_RV emulate_update(kernel_session_t *session_p, CK_BYTE_PTR pPart, CK_ULONG ulPartLen, int opflag); #ifdef __cplusplus } #endif #endif /* _KERNEL_EMULATE_H */ /* * 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. * Copyright 2018, Joyent, Inc. */ #include #include #include #include #include #include "kernelGlobal.h" #include "kernelSession.h" #include "kernelObject.h" CK_RV C_EncryptInit(CK_SESSION_HANDLE hSession, CK_MECHANISM_PTR pMechanism, CK_OBJECT_HANDLE hKey) { CK_RV rv; kernel_session_t *session_p; kernel_object_t *key_p; boolean_t ses_lock_held = B_FALSE; crypto_encrypt_init_t encrypt_init; crypto_mech_type_t k_mech_type; int r; CK_AES_CCM_PARAMS ccm_params = { 0 }; if (!kernel_initialized) return (CKR_CRYPTOKI_NOT_INITIALIZED); if (pMechanism == NULL) { return (CKR_ARGUMENTS_BAD); } /* Get the kernel's internal mechanism number. */ rv = kernel_mech(pMechanism->mechanism, &k_mech_type); if (rv != CKR_OK) return (rv); /* Obtain the session pointer. */ rv = handle2session(hSession, &session_p); if (rv != CKR_OK) return (rv); /* Obtain the object pointer. */ HANDLE2OBJECT(hKey, key_p, rv); if (rv != CKR_OK) { REFRELE(session_p, ses_lock_held); return (rv); } /* Check to see if key object allows for encryption. */ if (key_p->is_lib_obj && !(key_p->bool_attr_mask & ENCRYPT_BOOL_ON)) { rv = CKR_KEY_TYPE_INCONSISTENT; goto clean_exit; } (void) pthread_mutex_lock(&session_p->session_mutex); ses_lock_held = B_TRUE; /* * This active flag will remain ON until application calls either * C_Encrypt or C_EncryptFinal to actually obtain the final piece * of ciphertext. */ session_p->encrypt.flags = CRYPTO_OPERATION_ACTIVE; /* set up key data */ if (!key_p->is_lib_obj) { encrypt_init.ei_key.ck_format = CRYPTO_KEY_REFERENCE; encrypt_init.ei_key.ck_obj_id = key_p->k_handle; } else { if (key_p->class == CKO_SECRET_KEY) { encrypt_init.ei_key.ck_format = CRYPTO_KEY_RAW; encrypt_init.ei_key.ck_data = get_symmetric_key_value(key_p); if (encrypt_init.ei_key.ck_data == NULL) { rv = CKR_HOST_MEMORY; goto clean_exit; } encrypt_init.ei_key.ck_length = OBJ_SEC(key_p)->sk_value_len << 3; } else if (key_p->key_type == CKK_RSA) { if (get_rsa_public_key(key_p, &encrypt_init.ei_key) != CKR_OK) { rv = CKR_HOST_MEMORY; goto clean_exit; } } else { rv = CKR_KEY_TYPE_INCONSISTENT; goto clean_exit; } } encrypt_init.ei_session = session_p->k_session; session_p->encrypt.mech = *pMechanism; /* Cache this capability value for efficiency */ if (INPLACE_MECHANISM(session_p->encrypt.mech.mechanism)) { session_p->encrypt.flags |= CRYPTO_OPERATION_INPLACE_OK; } (void) pthread_mutex_unlock(&session_p->session_mutex); ses_lock_held = B_FALSE; encrypt_init.ei_mech.cm_type = k_mech_type; encrypt_init.ei_mech.cm_param = pMechanism->pParameter; encrypt_init.ei_mech.cm_param_len = pMechanism->ulParameterLen; /* * PKCS#11 uses CK_CCM_PARAMS as its mechanism parameter, while the * kernel uses CK_AES_CCM_PARAMS. Unlike * CK_GCM_PARAMS / CK_AES_GCM_PARAMS, the two definitions are not * equivalent -- the fields are defined in different orders, so * we much translate. */ if (session_p->encrypt.mech.mechanism == CKM_AES_CCM) { if (pMechanism->ulParameterLen != sizeof (CK_CCM_PARAMS)) { rv = CKR_MECHANISM_PARAM_INVALID; goto clean_exit; } p11_to_kernel_ccm_params(pMechanism->pParameter, &ccm_params); encrypt_init.ei_mech.cm_param = (caddr_t)&ccm_params; encrypt_init.ei_mech.cm_param_len = sizeof (ccm_params); } while ((r = ioctl(kernel_fd, CRYPTO_ENCRYPT_INIT, &encrypt_init)) < 0) { if (errno != EINTR) break; } if (r < 0) { rv = CKR_FUNCTION_FAILED; } else { if (encrypt_init.ei_return_value != CRYPTO_SUCCESS) { rv = crypto2pkcs11_error_number( encrypt_init.ei_return_value); } } /* Free memory allocated for decrypt_init.di_key */ if (key_p->is_lib_obj) { if (key_p->class == CKO_SECRET_KEY) { free(encrypt_init.ei_key.ck_data); } else if (key_p->key_type == CKK_RSA) { free_key_attributes(&encrypt_init.ei_key); } } if (rv != CKR_OK) { (void) pthread_mutex_lock(&session_p->session_mutex); session_p->encrypt.flags &= ~CRYPTO_OPERATION_ACTIVE; ses_lock_held = B_TRUE; } clean_exit: /* * ccm_params does not contain any key material -- just lengths and * pointers, therefore it does not need to be zeroed on exit. */ OBJ_REFRELE(key_p); REFRELE(session_p, ses_lock_held); return (rv); } CK_RV C_Encrypt(CK_SESSION_HANDLE hSession, CK_BYTE_PTR pData, CK_ULONG ulDataLen, CK_BYTE_PTR pEncryptedData, CK_ULONG_PTR pulEncryptedDataLen) { CK_RV rv; kernel_session_t *session_p; boolean_t ses_lock_held = B_FALSE; boolean_t inplace; crypto_encrypt_t encrypt; int r; if (!kernel_initialized) return (CKR_CRYPTOKI_NOT_INITIALIZED); /* Obtain the session pointer. */ rv = handle2session(hSession, &session_p); if (rv != CKR_OK) return (rv); /* * Only check if pulEncryptedDataLen is NULL. * No need to check if pEncryptedData is NULL because * application might just ask for the length of buffer to hold * the ciphertext. */ if (pulEncryptedDataLen == NULL) { rv = CKR_ARGUMENTS_BAD; goto clean_exit; } /* * Some encryption algs (often combined mode ciphers such as AES-GCM) * allow 0-byte inputs to encrypt. */ if (pData == NULL && ulDataLen != 0) { rv = CKR_ARGUMENTS_BAD; goto clean_exit; } (void) pthread_mutex_lock(&session_p->session_mutex); ses_lock_held = B_TRUE; /* Application must call C_EncryptInit before calling C_Encrypt. */ if (!(session_p->encrypt.flags & CRYPTO_OPERATION_ACTIVE)) { REFRELE(session_p, ses_lock_held); return (CKR_OPERATION_NOT_INITIALIZED); } /* * C_Encrypt must be called without intervening C_EncryptUpdate * calls. */ if (session_p->encrypt.flags & CRYPTO_OPERATION_UPDATE) { /* * C_Encrypt can not be used to terminate a multi-part * operation, so we'll leave the active encrypt operation * flag on and let the application continue with the * encrypt update operation. */ REFRELE(session_p, ses_lock_held); return (CKR_FUNCTION_FAILED); } encrypt.ce_session = session_p->k_session; /* * Certain mechanisms, where the length of the ciphertext is * same as the transformed plaintext, can be optimized * by the kernel into an in-place operation. Unfortunately, * some applications use a ciphertext buffer that is larger * than it needs to be. We fix that here. */ inplace = (session_p->encrypt.flags & CRYPTO_OPERATION_INPLACE_OK) != 0; if (ulDataLen < *pulEncryptedDataLen && inplace) { encrypt.ce_encrlen = ulDataLen; } else { encrypt.ce_encrlen = *pulEncryptedDataLen; } (void) pthread_mutex_unlock(&session_p->session_mutex); ses_lock_held = B_FALSE; encrypt.ce_datalen = ulDataLen; encrypt.ce_databuf = (char *)pData; encrypt.ce_encrbuf = (char *)pEncryptedData; encrypt.ce_flags = ((inplace && (pEncryptedData != NULL)) || (pData == pEncryptedData)) && (encrypt.ce_encrlen == encrypt.ce_datalen) ? CRYPTO_INPLACE_OPERATION : 0; while ((r = ioctl(kernel_fd, CRYPTO_ENCRYPT, &encrypt)) < 0) { if (errno != EINTR) break; } if (r < 0) { rv = CKR_FUNCTION_FAILED; } else { rv = crypto2pkcs11_error_number(encrypt.ce_return_value); } if (rv == CKR_OK || rv == CKR_BUFFER_TOO_SMALL) *pulEncryptedDataLen = encrypt.ce_encrlen; if ((rv == CKR_BUFFER_TOO_SMALL) || (rv == CKR_OK && pEncryptedData == NULL)) { /* * We will not terminate the active encrypt operation flag, * when the application-supplied buffer is too small, or * the application asks for the length of buffer to hold * the ciphertext. */ REFRELE(session_p, ses_lock_held); return (rv); } clean_exit: /* * Terminates the active encrypt operation. * Application needs to call C_EncryptInit again for next * encrypt operation. */ (void) pthread_mutex_lock(&session_p->session_mutex); session_p->encrypt.flags = 0; ses_lock_held = B_TRUE; REFRELE(session_p, ses_lock_held); return (rv); } CK_RV C_EncryptUpdate(CK_SESSION_HANDLE hSession, CK_BYTE_PTR pPart, CK_ULONG ulPartLen, CK_BYTE_PTR pEncryptedPart, CK_ULONG_PTR pulEncryptedPartLen) { CK_RV rv; kernel_session_t *session_p; boolean_t ses_lock_held = B_FALSE; boolean_t inplace; crypto_encrypt_update_t encrypt_update; int r; if (!kernel_initialized) return (CKR_CRYPTOKI_NOT_INITIALIZED); /* Obtain the session pointer. */ rv = handle2session(hSession, &session_p); if (rv != CKR_OK) return (rv); if (pPart == NULL) { rv = CKR_ARGUMENTS_BAD; goto clean_exit; } /* * Only check if pulEncryptedPartLen is NULL. * No need to check if pEncryptedPart is NULL because * application might just ask for the length of buffer to hold * the ciphertext. */ if (pulEncryptedPartLen == NULL) { rv = CKR_ARGUMENTS_BAD; goto clean_exit; } (void) pthread_mutex_lock(&session_p->session_mutex); ses_lock_held = B_TRUE; /* * Application must call C_EncryptInit before calling * C_EncryptUpdate. */ if (!(session_p->encrypt.flags & CRYPTO_OPERATION_ACTIVE)) { REFRELE(session_p, ses_lock_held); return (CKR_OPERATION_NOT_INITIALIZED); } session_p->encrypt.flags |= CRYPTO_OPERATION_UPDATE; encrypt_update.eu_session = session_p->k_session; (void) pthread_mutex_unlock(&session_p->session_mutex); ses_lock_held = B_FALSE; encrypt_update.eu_datalen = ulPartLen; encrypt_update.eu_databuf = (char *)pPart; encrypt_update.eu_encrlen = *pulEncryptedPartLen; encrypt_update.eu_encrbuf = (char *)pEncryptedPart; inplace = (session_p->encrypt.flags & CRYPTO_OPERATION_INPLACE_OK) != 0; encrypt_update.eu_flags = ((inplace && (pEncryptedPart != NULL)) || (pPart == pEncryptedPart)) && (encrypt_update.eu_encrlen == encrypt_update.eu_datalen) ? CRYPTO_INPLACE_OPERATION : 0; while ((r = ioctl(kernel_fd, CRYPTO_ENCRYPT_UPDATE, &encrypt_update)) < 0) { if (errno != EINTR) break; } if (r < 0) { rv = CKR_FUNCTION_FAILED; } else { rv = crypto2pkcs11_error_number( encrypt_update.eu_return_value); } /* * If CKR_OK or CKR_BUFFER_TOO_SMALL, set the output length. * We don't terminate the current encryption operation. */ if (rv == CKR_OK || rv == CKR_BUFFER_TOO_SMALL) { *pulEncryptedPartLen = encrypt_update.eu_encrlen; REFRELE(session_p, ses_lock_held); return (rv); } clean_exit: /* * After an error occurred, terminate the current encrypt * operation by resetting the active and update flags. */ (void) pthread_mutex_lock(&session_p->session_mutex); session_p->encrypt.flags = 0; ses_lock_held = B_TRUE; REFRELE(session_p, ses_lock_held); return (rv); } CK_RV C_EncryptFinal(CK_SESSION_HANDLE hSession, CK_BYTE_PTR pLastEncryptedPart, CK_ULONG_PTR pulLastEncryptedPartLen) { CK_RV rv; kernel_session_t *session_p; boolean_t ses_lock_held = B_FALSE; crypto_encrypt_final_t encrypt_final; int r; if (!kernel_initialized) return (CKR_CRYPTOKI_NOT_INITIALIZED); /* Obtain the session pointer. */ rv = handle2session(hSession, &session_p); if (rv != CKR_OK) return (rv); if (pulLastEncryptedPartLen == NULL) { rv = CKR_ARGUMENTS_BAD; goto clean_exit; } (void) pthread_mutex_lock(&session_p->session_mutex); ses_lock_held = B_TRUE; /* * Application must call C_EncryptInit before calling * C_EncryptFinal. */ if (!(session_p->encrypt.flags & CRYPTO_OPERATION_ACTIVE)) { REFRELE(session_p, ses_lock_held); return (CKR_OPERATION_NOT_INITIALIZED); } encrypt_final.ef_session = session_p->k_session; (void) pthread_mutex_unlock(&session_p->session_mutex); ses_lock_held = B_FALSE; encrypt_final.ef_encrlen = *pulLastEncryptedPartLen; encrypt_final.ef_encrbuf = (char *)pLastEncryptedPart; while ((r = ioctl(kernel_fd, CRYPTO_ENCRYPT_FINAL, &encrypt_final)) < 0) { if (errno != EINTR) break; } if (r < 0) { rv = CKR_FUNCTION_FAILED; } else { rv = crypto2pkcs11_error_number(encrypt_final.ef_return_value); } if (rv == CKR_OK || rv == CKR_BUFFER_TOO_SMALL) *pulLastEncryptedPartLen = encrypt_final.ef_encrlen; if ((rv == CKR_BUFFER_TOO_SMALL) || (rv == CKR_OK && pLastEncryptedPart == NULL)) { /* * We will not terminate the active encrypt operation flag, * when the application-supplied buffer is too small, or * the application asks for the length of buffer to hold * the ciphertext. */ REFRELE(session_p, ses_lock_held); return (rv); } clean_exit: /* Terminates the active encrypt operation. */ (void) pthread_mutex_lock(&session_p->session_mutex); session_p->encrypt.flags = 0; ses_lock_held = B_TRUE; REFRELE(session_p, ses_lock_held); return (rv); } /* * 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. */ #include #include #include #include /* for pid */ #include #include #include "kernelGlobal.h" #include "kernelSession.h" #include "kernelSlot.h" #pragma init(kernel_init) #pragma fini(kernel_fini) static struct CK_FUNCTION_LIST functionList = { { 2, 20 }, /* version */ C_Initialize, C_Finalize, C_GetInfo, C_GetFunctionList, C_GetSlotList, C_GetSlotInfo, C_GetTokenInfo, C_GetMechanismList, C_GetMechanismInfo, C_InitToken, C_InitPIN, C_SetPIN, C_OpenSession, C_CloseSession, C_CloseAllSessions, C_GetSessionInfo, C_GetOperationState, C_SetOperationState, C_Login, C_Logout, C_CreateObject, C_CopyObject, C_DestroyObject, C_GetObjectSize, C_GetAttributeValue, C_SetAttributeValue, C_FindObjectsInit, C_FindObjects, C_FindObjectsFinal, C_EncryptInit, C_Encrypt, C_EncryptUpdate, C_EncryptFinal, C_DecryptInit, C_Decrypt, C_DecryptUpdate, C_DecryptFinal, C_DigestInit, C_Digest, C_DigestUpdate, C_DigestKey, C_DigestFinal, C_SignInit, C_Sign, C_SignUpdate, C_SignFinal, C_SignRecoverInit, C_SignRecover, C_VerifyInit, C_Verify, C_VerifyUpdate, C_VerifyFinal, C_VerifyRecoverInit, C_VerifyRecover, C_DigestEncryptUpdate, C_DecryptDigestUpdate, C_SignEncryptUpdate, C_DecryptVerifyUpdate, C_GenerateKey, C_GenerateKeyPair, C_WrapKey, C_UnwrapKey, C_DeriveKey, C_SeedRandom, C_GenerateRandom, C_GetFunctionStatus, C_CancelFunction, C_WaitForSlotEvent }; boolean_t kernel_initialized = B_FALSE; static pid_t kernel_pid = 0; extern pthread_mutex_t mechhash_mutex; int kernel_fd = -1; /* protects kernel_initialized and entrance to C_Initialize/Finalize */ static pthread_mutex_t globalmutex = PTHREAD_MUTEX_INITIALIZER; ses_to_be_freed_list_t ses_delay_freed; object_to_be_freed_list_t obj_delay_freed; kmh_elem_t **kernel_mechhash; /* Hash table for kCF mech numbers */ static void finalize_common(); static void cleanup_library(); static void kernel_init(); static void kernel_fini(); static void kernel_fork_prepare(); static void kernel_fork_after(); CK_RV C_Initialize(CK_VOID_PTR pInitArgs) { int initialize_pid; boolean_t supplied_ok; CK_RV rv = CKR_OK; /* * Grab lock to insure that only one thread enters this * function at a time. */ (void) pthread_mutex_lock(&globalmutex); initialize_pid = getpid(); if (kernel_initialized) { if (initialize_pid == kernel_pid) { /* * This process has called C_Initialize already */ (void) pthread_mutex_unlock(&globalmutex); return (CKR_CRYPTOKI_ALREADY_INITIALIZED); } else { /* * A fork has happened and the child is * reinitializing. Do a cleanup_library to close * out any state from the parent, and then * continue on. */ cleanup_library(); } } if (pInitArgs != NULL) { CK_C_INITIALIZE_ARGS *initargs1 = (CK_C_INITIALIZE_ARGS *) pInitArgs; /* pReserved must be NULL */ if (initargs1->pReserved != NULL) { (void) pthread_mutex_unlock(&globalmutex); return (CKR_ARGUMENTS_BAD); } /* * ALL supplied function pointers need to have the value * either NULL or non-NULL. */ supplied_ok = (initargs1->CreateMutex == NULL && initargs1->DestroyMutex == NULL && initargs1->LockMutex == NULL && initargs1->UnlockMutex == NULL) || (initargs1->CreateMutex != NULL && initargs1->DestroyMutex != NULL && initargs1->LockMutex != NULL && initargs1->UnlockMutex != NULL); if (!supplied_ok) { (void) pthread_mutex_unlock(&globalmutex); return (CKR_ARGUMENTS_BAD); } /* * When the CKF_OS_LOCKING_OK flag isn't set and mutex * function pointers are supplied by an application, * return an error. We must be able to use our own locks. */ if (!(initargs1->flags & CKF_OS_LOCKING_OK) && (initargs1->CreateMutex != NULL)) { (void) pthread_mutex_unlock(&globalmutex); return (CKR_CANT_LOCK); } } while ((kernel_fd = open(CRYPTO_DEVICE, O_RDWR)) < 0) { if (errno != EINTR) break; } if (kernel_fd < 0) { (void) pthread_mutex_unlock(&globalmutex); return (CKR_FUNCTION_FAILED); } /* Mark kernel_fd "close on exec" */ (void) fcntl(kernel_fd, F_SETFD, FD_CLOEXEC); /* Create the hash table */ kernel_mechhash = calloc(KMECH_HASHTABLE_SIZE, sizeof (void *)); if (kernel_mechhash == NULL) { (void) close(kernel_fd); (void) pthread_mutex_unlock(&globalmutex); return (CKR_HOST_MEMORY); } /* Initialize the slot table */ rv = kernel_slottable_init(); if (rv != CKR_OK) { free(kernel_mechhash); (void) close(kernel_fd); (void) pthread_mutex_unlock(&globalmutex); return (rv); } /* Initialize the object_to_be_freed list */ (void) pthread_mutex_init(&obj_delay_freed.obj_to_be_free_mutex, NULL); obj_delay_freed.count = 0; obj_delay_freed.first = NULL; obj_delay_freed.last = NULL; /* Initialize the session_to_be_freed list */ (void) pthread_mutex_init(&ses_delay_freed.ses_to_be_free_mutex, NULL); ses_delay_freed.count = 0; ses_delay_freed.first = NULL; ses_delay_freed.last = NULL; kernel_initialized = B_TRUE; kernel_pid = initialize_pid; (void) pthread_mutex_unlock(&globalmutex); return (CKR_OK); } /* * C_Finalize is a wrapper around finalize_common. The * globalmutex should be locked by C_Finalize(). */ CK_RV C_Finalize(CK_VOID_PTR pReserved) { int i; (void) pthread_mutex_lock(&globalmutex); if (!kernel_initialized) { (void) pthread_mutex_unlock(&globalmutex); return (CKR_CRYPTOKI_NOT_INITIALIZED); } /* Check to see if pReseved is NULL */ if (pReserved != NULL) { (void) pthread_mutex_unlock(&globalmutex); return (CKR_ARGUMENTS_BAD); } /* * Delete all the sessions for each slot and release the allocated * resources */ for (i = 0; i < slot_count; i++) { kernel_delete_all_sessions(i, B_FALSE); } finalize_common(); (void) pthread_mutex_unlock(&globalmutex); return (CKR_OK); } /* * finalize_common() does the work for C_Finalize. globalmutex * must be held before calling this function. */ static void finalize_common() { int i; kmh_elem_t *elem, *next; kernel_object_t *delay_free_obj, *tmpo; kernel_session_t *delay_free_ses, *tmps; /* * Free the resources allocated for the slot table and reset * slot_count to 0. */ if (slot_count > 0) { for (i = 0; i < slot_count; i++) { (void) pthread_mutex_destroy(&slot_table[i]->sl_mutex); (void) free(slot_table[i]); } (void) free(slot_table); slot_count = 0; } /* Close CRYPTO_DEVICE */ if (kernel_fd >= 0) { (void) close(kernel_fd); } /* Walk the hash table and free all entries */ for (i = 0; i < KMECH_HASHTABLE_SIZE; i++) { elem = kernel_mechhash[i]; while (elem != NULL) { next = elem->knext; free(elem); elem = next; } } free(kernel_mechhash); kernel_fd = -1; kernel_initialized = B_FALSE; kernel_pid = 0; /* * free all entries in the delay_freed list */ delay_free_obj = obj_delay_freed.first; while (delay_free_obj != NULL) { tmpo = delay_free_obj->next; free(delay_free_obj); delay_free_obj = tmpo; } (void) pthread_mutex_destroy(&obj_delay_freed.obj_to_be_free_mutex); delay_free_ses = ses_delay_freed.first; while (delay_free_ses != NULL) { tmps = delay_free_ses->next; free(delay_free_ses); delay_free_ses = tmps; } (void) pthread_mutex_destroy(&ses_delay_freed.ses_to_be_free_mutex); } /* * This function cleans up all the resources in the library (user space only) */ static void cleanup_library() { int i; /* * Delete all the sessions for each slot and release the allocated * resources from the library. The boolean argument TRUE indicates * that we only wants to clean up the resource in the library only. * We don't want to clean up the corresponding kernel part of * resources, because they are used by the parent process still. */ for (i = 0; i < slot_count; i++) { kernel_delete_all_sessions(i, B_TRUE); } finalize_common(); } static void kernel_init() { (void) pthread_atfork(kernel_fork_prepare, kernel_fork_after, kernel_fork_after); } /* * kernel_fini() function required to make sure complete cleanup * is done if pkcs11_kernel is ever unloaded without * a C_Finalize() call. */ static void kernel_fini() { (void) pthread_mutex_lock(&globalmutex); /* if we're not initilized, do not attempt to finalize */ if (!kernel_initialized) { (void) pthread_mutex_unlock(&globalmutex); return; } cleanup_library(); (void) pthread_mutex_unlock(&globalmutex); } CK_RV C_GetInfo(CK_INFO_PTR pInfo) { if (!kernel_initialized) return (CKR_CRYPTOKI_NOT_INITIALIZED); if (pInfo == NULL) { return (CKR_ARGUMENTS_BAD); } /* Check if the cryptoki was initialized */ pInfo->cryptokiVersion.major = CRYPTOKI_VERSION_MAJOR; pInfo->cryptokiVersion.minor = CRYPTOKI_VERSION_MINOR; (void) strncpy((char *)pInfo->manufacturerID, MANUFACTURER_ID, 32); pInfo->flags = 0; (void) strncpy((char *)pInfo->libraryDescription, LIBRARY_DESCRIPTION, 32); pInfo->libraryVersion.major = LIBRARY_VERSION_MAJOR; pInfo->libraryVersion.minor = LIBRARY_VERSION_MINOR; return (CKR_OK); } CK_RV C_GetFunctionList(CK_FUNCTION_LIST_PTR_PTR ppFunctionList) { if (ppFunctionList == NULL) { return (CKR_ARGUMENTS_BAD); } *ppFunctionList = &functionList; return (CKR_OK); } /* * PKCS#11 states that C_GetFunctionStatus should always return * CKR_FUNCTION_NOT_PARALLEL */ /*ARGSUSED*/ CK_RV C_GetFunctionStatus(CK_SESSION_HANDLE hSession) { return (CKR_FUNCTION_NOT_PARALLEL); } /* * Take out all mutexes before fork. * Order: * 1. globalmutex * 2. all slots mutexes (and all their sessions) via * kernel_acquire_all_slots_mutexes() * 3. obj_delay_freed.obj_to_be_free_mutex; * 4. ses_delay_freed.ses_to_be_free_mutex */ void kernel_fork_prepare() { (void) pthread_mutex_lock(&globalmutex); if (kernel_initialized) { kernel_acquire_all_slots_mutexes(); (void) pthread_mutex_lock( &obj_delay_freed.obj_to_be_free_mutex); (void) pthread_mutex_lock( &ses_delay_freed.ses_to_be_free_mutex); (void) pthread_mutex_lock(&mechhash_mutex); } } /* * Release in opposite order to kernel_fork_prepare(). * Function is used for parent and child. */ void kernel_fork_after() { if (kernel_initialized) { (void) pthread_mutex_unlock(&mechhash_mutex); (void) pthread_mutex_unlock( &ses_delay_freed.ses_to_be_free_mutex); (void) pthread_mutex_unlock( &obj_delay_freed.obj_to_be_free_mutex); kernel_release_all_slots_mutexes(); } (void) pthread_mutex_unlock(&globalmutex); } /* * PKCS#11 states that C_CancelFunction should always return * CKR_FUNCTION_NOT_PARALLEL */ /*ARGSUSED*/ CK_RV C_CancelFunction(CK_SESSION_HANDLE hSession) { return (CKR_FUNCTION_NOT_PARALLEL); } /* * 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 2007 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. * Copyright 2018, Joyent, Inc. */ #ifndef _KERNELGLOBAL_H #define _KERNELGLOBAL_H #ifdef __cplusplus extern "C" { #endif #include #include #include #include #include "kernelObject.h" typedef struct kmh_elem { CK_MECHANISM_TYPE type; crypto_mech_type_t kmech; /* kCF mech number */ struct kmh_elem *knext; /* Next in hash chain */ } kmh_elem_t; extern kmh_elem_t **kernel_mechhash; extern boolean_t kernel_initialized; extern int kernel_fd; #define KMECH_HASHTABLE_SIZE 67 #define CRYPTO_DEVICE "/dev/crypto" /* CK_INFO: Information about cryptoki */ #define CRYPTOKI_VERSION_MAJOR 2 #define CRYPTOKI_VERSION_MINOR 40 #define MANUFACTURER_ID "Sun Microsystems, Inc. " #define LIBRARY_DESCRIPTION "Sun Crypto pkcs11_kernel " #define LIBRARY_VERSION_MAJOR 1 #define LIBRARY_VERSION_MINOR 1 /* CK_SLOT_INFO: Information about our slot */ #define SLOT_DESCRIPTION "Sun Crypto pkcs11_kernel " \ " " #define HARDWARE_VERSION_MAJOR 0 #define HARDWARE_VERSION_MINOR 0 #define FIRMWARE_VERSION_MAJOR 0 #define FIRMWARE_VERSION_MINOR 0 #define INPLACE_MECHANISM(m) ((m) == CKM_DES_ECB || (m) == CKM_DES_CBC || \ (m) == CKM_DES3_ECB || (m) == CKM_DES3_CBC || (m) == CKM_AES_ECB || \ (m) == CKM_AES_CBC || (m) == CKM_RC4 || (m) == CKM_BLOWFISH_CBC) CK_RV kernel_mech(CK_MECHANISM_TYPE, crypto_mech_type_t *); unsigned char *get_symmetric_key_value(kernel_object_t *); CK_RV get_rsa_public_key(kernel_object_t *, crypto_key_t *); CK_RV get_rsa_private_key(kernel_object_t *, crypto_key_t *); CK_RV get_dsa_public_key(kernel_object_t *, crypto_key_t *); CK_RV get_dsa_private_key(kernel_object_t *, crypto_key_t *); CK_RV get_ec_public_key(kernel_object_t *, crypto_key_t *); CK_RV get_ec_private_key(kernel_object_t *, crypto_key_t *); void free_key_attributes(crypto_key_t *); void get_ulong_attr_from_template(CK_ULONG *, CK_ATTRIBUTE_PTR); CK_RV process_object_attributes(CK_ATTRIBUTE_PTR, CK_ULONG, caddr_t *, CK_BBOOL *); CK_RV get_object_attributes(CK_ATTRIBUTE_PTR, CK_ULONG, caddr_t); void free_object_attributes(caddr_t, CK_ULONG); CK_RV get_cka_private_value(kernel_session_t *, crypto_object_id_t, CK_BBOOL *); CK_RV process_found_objects(kernel_session_t *, CK_OBJECT_HANDLE *, CK_ULONG *, crypto_object_find_update_t); CK_RV get_mechanism_info(kernel_slot_t *, CK_MECHANISM_TYPE, CK_MECHANISM_INFO_PTR, uint32_t *); CK_RV kernel_decrypt_init(kernel_session_t *, kernel_object_t *, CK_MECHANISM_PTR); CK_RV kernel_decrypt(kernel_session_t *, CK_BYTE_PTR, CK_ULONG, CK_BYTE_PTR, CK_ULONG_PTR); CK_RV kernel_add_extra_attr(CK_ATTRIBUTE_PTR, kernel_object_t *); void p11_to_kernel_ccm_params(const CK_CCM_PARAMS *, CK_AES_CCM_PARAMS *); #ifdef __cplusplus } #endif #endif /* _KERNELGLOBAL_H */ /* * 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. * Copyright 2018, Joyent, Inc. */ #include #include #include #include #include #include "kernelGlobal.h" #include "kernelSession.h" #include "kernelObject.h" static boolean_t attribute_in_template(CK_ATTRIBUTE_TYPE type, CK_ATTRIBUTE_PTR t, CK_ULONG cnt) { int i; for (i = 0; i < cnt; i++) { if (t[i].type == type) return (B_TRUE); } return (B_FALSE); } /* * This routine returns modulus bytes rounded up to the nearest 8 byte * chunk. This is so we don't have to pass in max sized buffers for * returned attributes. Every unnecessary byte that we pass in results * in a kernel allocation. */ static ulong_t get_modulus_bytes(CK_ATTRIBUTE_PTR t, CK_ULONG cnt) { CK_ULONG modulus_len; int i; for (i = 0; i < cnt; i++) { if (t[i].type == CKA_MODULUS_BITS) { get_ulong_attr_from_template(&modulus_len, &t[i]); /* convert from bit length to byte length */ modulus_len = (modulus_len - 1) / 64 + 1; return (modulus_len * 8); } } return (0); } /* * Remove specified attribute from array. Storage for the attribute's * value is freed if 'free_attr' is TRUE. Attributes are shifted so they are * contiguous within the array, i.e. the next attribute is shifted into * the position of the removed attribute. Returns TRUE if specified * attribute is removed. */ static boolean_t remove_one_attribute(CK_ATTRIBUTE_PTR t, CK_ULONG type, uint_t count, boolean_t free_attr) { int i, j; for (i = 0, j = 0; i < count; i++) { if (t[i].type == type) { if (free_attr) { free(t[i].pValue); } continue; } if (i != j) { t[j].type = t[i].type; t[j].pValue = t[i].pValue; t[j].ulValueLen = t[i].ulValueLen; } j++; } if (j == count) return (B_FALSE); /* safety */ t[j].pValue = NULL; t[j].ulValueLen = 0; return (B_TRUE); } static boolean_t is_secret_key_template(CK_ATTRIBUTE_PTR pTemplate, CK_ULONG ulAttributeCount) { int i; for (i = 0; i < ulAttributeCount; i++) { if (pTemplate[i].type == CKA_CLASS && *(CK_OBJECT_CLASS *)(pTemplate[i].pValue) == CKO_SECRET_KEY) return (B_TRUE); } return (B_FALSE); } /* * Allocate a template with space for new_count entries and copy the * specified template into the new template. */ static CK_ATTRIBUTE_PTR grow_template(CK_ATTRIBUTE_PTR old_template, CK_ULONG old_count, CK_ULONG new_count) { CK_ATTRIBUTE_PTR new_template; new_template = malloc(new_count * sizeof (CK_ATTRIBUTE)); if (new_template != NULL) bcopy(old_template, new_template, old_count * sizeof (CK_ATTRIBUTE)); return (new_template); } /* * For fixed length keys such as DES, return the length based on * the key type. For variable length keys such as AES, take the * length from the CKA_VALUE_LEN attribute. */ static int get_key_len_from_template(CK_MECHANISM_PTR pMechanism, CK_ATTRIBUTE_PTR pTemplate, CK_ULONG ulAttributeCount, kernel_object_t *basekey_p, ulong_t *key_len) { boolean_t fixed_len_key = B_FALSE; ulong_t key_type; int i; for (i = 0; i < ulAttributeCount; i++) { if (pTemplate[i].type == CKA_KEY_TYPE) { get_ulong_attr_from_template(&key_type, &pTemplate[i]); break; } } /* CKA_KEY_TYPE must be present */ if (i == ulAttributeCount) return (CKR_TEMPLATE_INCOMPLETE); switch (key_type) { case CKK_DES: *key_len = 8; fixed_len_key = B_TRUE; break; case CKK_DES3: *key_len = 24; fixed_len_key = B_TRUE; break; case CKK_AES: case CKK_BLOWFISH: for (i = 0; i < ulAttributeCount; i++) { if (pTemplate[i].type == CKA_VALUE_LEN) { get_ulong_attr_from_template(key_len, &pTemplate[i]); break; } } /* CKA_VALUE_LEN must be present */ if (i == ulAttributeCount) return (CKR_TEMPLATE_INCOMPLETE); break; case CKK_GENERIC_SECRET: /* * The key will not be truncated, so we need to * get the max length for the mechanism. */ if (pMechanism->mechanism == CKM_DH_PKCS_DERIVE) { CK_ATTRIBUTE tmp; tmp.type = CKA_PRIME; tmp.pValue = NULL; /* get size of attribute */ if (kernel_get_attribute(basekey_p, &tmp) != CKR_OK) { return (CKR_ARGUMENTS_BAD); } *key_len = tmp.ulValueLen; } else if (pMechanism->mechanism == CKM_ECDH1_DERIVE) { *key_len = EC_MAX_VALUE_LEN; } else { return (CKR_ARGUMENTS_BAD); } break; default: return (CKR_ATTRIBUTE_VALUE_INVALID); } if (fixed_len_key && attribute_in_template(CKA_VALUE_LEN, pTemplate, ulAttributeCount)) return (CKR_TEMPLATE_INCONSISTENT); return (CKR_OK); } /* find specified attribute src template and copy to dest */ static int copy_attribute(CK_ULONG type, CK_ATTRIBUTE_PTR src, CK_ULONG src_cnt, CK_ATTRIBUTE_PTR dst) { int rv, i; for (i = 0; i < src_cnt; i++) { if (src[i].type == type) { rv = get_string_from_template(dst, &src[i]); break; } } /* * The public template didn't have attribute. */ if (i == src_cnt) { rv = CKR_TEMPLATE_INCOMPLETE; } return (rv); } static void free_attributes(caddr_t p, uint_t *countp) { if (*countp > 0) { free_object_attributes(p, *countp); *countp = 0; } } CK_RV key_gen_by_value(CK_MECHANISM_PTR pMechanism, CK_ATTRIBUTE_PTR pTemplate, CK_ULONG ulCount, kernel_session_t *session_p, crypto_mech_type_t k_mech_type, kernel_object_t *new_objp) { crypto_nostore_generate_key_t obj_ngk; char *key_buf = NULL; CK_ATTRIBUTE_PTR newTemplate = NULL; CK_BBOOL is_token_obj = FALSE; CK_RV rv = CKR_OK; ulong_t key_len = 0; uint_t attr_count; int r; obj_ngk.ngk_in_count = 0; obj_ngk.ngk_out_count = 0; rv = get_key_len_from_template(pMechanism, pTemplate, ulCount, NULL, &key_len); if (rv != CRYPTO_SUCCESS) goto failed_exit; if ((key_buf = malloc(key_len)) == NULL) { rv = CKR_HOST_MEMORY; goto failed_exit; } attr_count = ulCount + 1; newTemplate = grow_template(pTemplate, ulCount, attr_count); if (newTemplate == NULL) { rv = CKR_HOST_MEMORY; goto failed_exit; } /* Now add the CKA_VALUE attribute to template */ newTemplate[ulCount].type = CKA_VALUE; newTemplate[ulCount].pValue = (caddr_t)key_buf; newTemplate[ulCount].ulValueLen = key_len; rv = process_object_attributes(newTemplate, attr_count - 1, &obj_ngk.ngk_in_attributes, &is_token_obj); if (rv != CKR_OK) { goto failed_exit; } rv = process_object_attributes(&newTemplate[ulCount], 1, &obj_ngk.ngk_out_attributes, &is_token_obj); if (rv != CKR_OK) { goto failed_exit; } /* Cannot create a token object with a READ-ONLY session. */ if (is_token_obj && session_p->ses_RO) { rv = CKR_SESSION_READ_ONLY; goto failed_exit; } /* Call the CRYPTO_NOSTORE_GENERATE_KEY ioctl */ obj_ngk.ngk_session = session_p->k_session; obj_ngk.ngk_in_count = attr_count - 1; obj_ngk.ngk_out_count = 1; obj_ngk.ngk_mechanism.cm_type = k_mech_type; obj_ngk.ngk_mechanism.cm_param = pMechanism->pParameter; obj_ngk.ngk_mechanism.cm_param_len = pMechanism->ulParameterLen; while ((r = ioctl(kernel_fd, CRYPTO_NOSTORE_GENERATE_KEY, &obj_ngk)) < 0) { if (errno != EINTR) break; } if (r < 0) { rv = CKR_FUNCTION_FAILED; } else { rv = crypto2pkcs11_error_number(obj_ngk.ngk_return_value); } free_attributes(obj_ngk.ngk_in_attributes, &obj_ngk.ngk_in_count); if (rv != CKR_OK) { goto failed_exit; } rv = get_object_attributes(&newTemplate[ulCount], 1, obj_ngk.ngk_out_attributes); free_attributes(obj_ngk.ngk_out_attributes, &obj_ngk.ngk_out_count); if (rv != CRYPTO_SUCCESS) { goto failed_exit; } /* * CKA_VALUE_LEN is not stored with the secret key object, * so we remove it by shifting attributes down one. */ (void) remove_one_attribute(newTemplate, CKA_VALUE_LEN, attr_count, B_FALSE); rv = kernel_build_object(newTemplate, attr_count - 1, new_objp, session_p, KERNEL_GEN_KEY); if (rv != CRYPTO_SUCCESS) { goto failed_exit; } new_objp->is_lib_obj = B_TRUE; new_objp->session_handle = (CK_SESSION_HANDLE)session_p; free(newTemplate); freezero(key_buf, key_len); return (CKR_OK); failed_exit: free_attributes(obj_ngk.ngk_in_attributes, &obj_ngk.ngk_in_count); free_attributes(obj_ngk.ngk_out_attributes, &obj_ngk.ngk_out_count); freezero(key_buf, key_len); free(newTemplate); return (rv); } CK_RV C_GenerateKey(CK_SESSION_HANDLE hSession, CK_MECHANISM_PTR pMechanism, CK_ATTRIBUTE_PTR pTemplate, CK_ULONG ulCount, CK_OBJECT_HANDLE_PTR phKey) { CK_RV rv = CKR_OK; kernel_session_t *session_p; kernel_object_t *new_objp = NULL; kernel_slot_t *pslot; boolean_t ses_lock_held = B_FALSE; CK_BBOOL is_pri_obj; CK_BBOOL is_token_obj = FALSE; crypto_mech_type_t k_mech_type; int r; if (!kernel_initialized) return (CKR_CRYPTOKI_NOT_INITIALIZED); /* Obtain the session pointer */ rv = handle2session(hSession, &session_p); if (rv != CKR_OK) return (rv); if ((pMechanism == NULL) || (phKey == NULL)) { rv = CKR_ARGUMENTS_BAD; goto failed_exit; } if ((pTemplate == NULL) && (ulCount != 0)) { rv = CKR_ARGUMENTS_BAD; goto failed_exit; } /* Get the kernel's internal mechanism number. */ rv = kernel_mech(pMechanism->mechanism, &k_mech_type); if (rv != CKR_OK) { goto failed_exit; } /* Create an object wrapper in the library first */ new_objp = calloc(1, sizeof (kernel_object_t)); if (new_objp == NULL) { rv = CKR_HOST_MEMORY; goto failed_exit; } /* * Special Case: if token does not support object creation, * but does support key generation by value, then create a session * object and initialize with value returned by token. */ pslot = slot_table[session_p->ses_slotid]; if (!pslot->sl_func_list.fl_object_create) { rv = key_gen_by_value(pMechanism, pTemplate, ulCount, session_p, k_mech_type, new_objp); if (rv != CKR_OK) goto failed_exit; } else { crypto_object_generate_key_t obj_gk; /* Process the attributes */ rv = process_object_attributes(pTemplate, ulCount, &obj_gk.gk_attributes, &is_token_obj); if (rv != CKR_OK) { goto failed_exit; } /* Cannot create a token object with a READ-ONLY session. */ if (is_token_obj && session_p->ses_RO) { free_object_attributes(obj_gk.gk_attributes, ulCount); rv = CKR_SESSION_READ_ONLY; goto failed_exit; } /* Call the CRYPTO_GENERATE_KEY ioctl */ obj_gk.gk_session = session_p->k_session; obj_gk.gk_count = ulCount; obj_gk.gk_mechanism.cm_type = k_mech_type; obj_gk.gk_mechanism.cm_param = pMechanism->pParameter; obj_gk.gk_mechanism.cm_param_len = pMechanism->ulParameterLen; while ((r = ioctl(kernel_fd, CRYPTO_GENERATE_KEY, &obj_gk)) < 0) { if (errno != EINTR) break; } if (r < 0) { rv = CKR_FUNCTION_FAILED; } else { rv = crypto2pkcs11_error_number(obj_gk.gk_return_value); } free_object_attributes(obj_gk.gk_attributes, ulCount); if (rv != CKR_OK) { goto failed_exit; } /* Get the value of the CKA_PRIVATE attribute. */ rv = get_cka_private_value(session_p, obj_gk.gk_handle, &is_pri_obj); if (rv != CKR_OK) { goto failed_exit; } /* * Store the kernel object handle in the object wrapper and * initialize the library object. */ new_objp->k_handle = obj_gk.gk_handle; new_objp->is_lib_obj = B_FALSE; new_objp->session_handle = (CK_SESSION_HANDLE)session_p; new_objp->extra_attrlistp = NULL; if (is_pri_obj) new_objp->bool_attr_mask |= PRIVATE_BOOL_ON; else new_objp->bool_attr_mask &= ~PRIVATE_BOOL_ON; if (is_token_obj) new_objp->bool_attr_mask |= TOKEN_BOOL_ON; else new_objp->bool_attr_mask &= ~TOKEN_BOOL_ON; } (void) pthread_mutex_init(&new_objp->object_mutex, NULL); new_objp->magic_marker = KERNELTOKEN_OBJECT_MAGIC; /* * Add the new object to the slot's token object list if it is a * a token object. Otherwise, add it to the session's object list. */ if (is_token_obj) { pslot = slot_table[session_p->ses_slotid]; kernel_add_token_object_to_slot(new_objp, pslot); } else { kernel_add_object_to_session(new_objp, session_p); } *phKey = (CK_OBJECT_HANDLE)new_objp; REFRELE(session_p, ses_lock_held); return (rv); failed_exit: if (new_objp != NULL) { (void) free(new_objp); } REFRELE(session_p, ses_lock_held); return (rv); } CK_RV key_gen_rsa_by_value(CK_MECHANISM_PTR pMechanism, CK_ATTRIBUTE_PTR pPublicKeyTemplate, CK_ULONG ulPublicKeyAttributeCount, CK_ATTRIBUTE_PTR pPrivateKeyTemplate, CK_ULONG ulPrivateKeyAttributeCount, kernel_session_t *session_p, crypto_mech_type_t k_mech_type, kernel_object_t *new_pub_objp, kernel_object_t *new_pri_objp) { crypto_nostore_generate_key_pair_t obj_nkp; CK_ATTRIBUTE_PTR pubTemplate = NULL; CK_ATTRIBUTE_PTR priTemplate = NULL; CK_RV rv = CKR_OK; CK_BBOOL is_token_obj1 = FALSE; CK_BBOOL is_token_obj2 = FALSE; uint_t pub_attr_count, pri_attr_count; uint_t pub_out_attr_count = 0, pri_out_attr_count = 0; char public_modulus[512]; char public_exponent[8]; char private_exponent[512]; char private_modulus[512]; char prime_1[512]; char prime_2[512]; char exponent_1[512]; char exponent_2[512]; char coefficient[512]; CK_ULONG pub_class = CKO_PUBLIC_KEY; CK_ULONG pri_class = CKO_PRIVATE_KEY; CK_ULONG key_type; CK_ULONG modulus_bytes; boolean_t has_class, has_key_type, has_pub_exponent; int n, r; obj_nkp.nkp_in_public_count = 0; obj_nkp.nkp_out_public_count = 0; obj_nkp.nkp_in_private_count = 0; obj_nkp.nkp_out_private_count = 0; /* modulus bits must be present when generating a RSA key pair */ if (!attribute_in_template(CKA_MODULUS_BITS, pPublicKeyTemplate, ulPublicKeyAttributeCount)) { rv = CKR_TEMPLATE_INCOMPLETE; goto failed_exit; } modulus_bytes = get_modulus_bytes(pPublicKeyTemplate, ulPublicKeyAttributeCount); /* * Add CKA_MODULUS to the public template. * This attribute must not be in the template. */ if (attribute_in_template(CKA_MODULUS, pPublicKeyTemplate, ulPublicKeyAttributeCount)) { rv = CKR_TEMPLATE_INCONSISTENT; goto failed_exit; } has_class = attribute_in_template(CKA_CLASS, pPublicKeyTemplate, ulPublicKeyAttributeCount); has_key_type = attribute_in_template(CKA_KEY_TYPE, pPublicKeyTemplate, ulPublicKeyAttributeCount); has_pub_exponent = attribute_in_template(CKA_PUBLIC_EXPONENT, pPublicKeyTemplate, ulPublicKeyAttributeCount); pub_attr_count = ulPublicKeyAttributeCount + 1; if (!has_class) pub_attr_count++; if (!has_key_type) pub_attr_count++; if (!has_pub_exponent) pub_attr_count++; pubTemplate = grow_template(pPublicKeyTemplate, ulPublicKeyAttributeCount, pub_attr_count); if (pubTemplate == NULL) { rv = CKR_HOST_MEMORY; goto failed_exit; } n = ulPublicKeyAttributeCount; if (!has_class) { pubTemplate[n].type = CKA_CLASS; pubTemplate[n].pValue = (caddr_t)&pub_class; pubTemplate[n].ulValueLen = sizeof (pub_class); n++; } if (!has_key_type) { pubTemplate[n].type = CKA_KEY_TYPE; key_type = CKK_RSA; pubTemplate[n].pValue = (caddr_t)&key_type; pubTemplate[n].ulValueLen = sizeof (key_type); n++; } if (!has_pub_exponent) { pubTemplate[n].type = CKA_PUBLIC_EXPONENT; pubTemplate[n].pValue = (caddr_t)public_exponent; pubTemplate[n].ulValueLen = modulus_bytes; n++; pub_out_attr_count++; } pubTemplate[n].type = CKA_MODULUS; pubTemplate[n].pValue = (caddr_t)public_modulus; pubTemplate[n].ulValueLen = modulus_bytes; pub_out_attr_count++; rv = process_object_attributes(pubTemplate, pub_attr_count - pub_out_attr_count, &obj_nkp.nkp_in_public_attributes, &is_token_obj1); if (rv != CKR_OK) { goto failed_exit; } obj_nkp.nkp_in_public_count = pub_attr_count - pub_out_attr_count; rv = process_object_attributes( &pubTemplate[pub_attr_count - pub_out_attr_count], pub_out_attr_count, &obj_nkp.nkp_out_public_attributes, &is_token_obj1); if (rv != CKR_OK) { goto failed_exit; } obj_nkp.nkp_out_public_count = pub_out_attr_count; /* * Cannot create a token object with a READ-ONLY * session. */ if (is_token_obj1 && session_p->ses_RO) { rv = CKR_SESSION_READ_ONLY; goto failed_exit; } /* * Add CKA_MODULUS and CKA_PRIVATE_EXPONENT * to the private template. These attributes * must not be in the template. */ if (attribute_in_template(CKA_PRIVATE_EXPONENT, pPrivateKeyTemplate, ulPrivateKeyAttributeCount) || attribute_in_template(CKA_MODULUS, pPrivateKeyTemplate, ulPrivateKeyAttributeCount)) { rv = CKR_TEMPLATE_INCONSISTENT; goto failed_exit; } has_class = attribute_in_template(CKA_CLASS, pPrivateKeyTemplate, ulPrivateKeyAttributeCount); has_key_type = attribute_in_template(CKA_KEY_TYPE, pPrivateKeyTemplate, ulPrivateKeyAttributeCount); pri_attr_count = ulPrivateKeyAttributeCount + 7; if (!has_class) pri_attr_count++; if (!has_key_type) pri_attr_count++; /* allocate space for CKA_PUBLIC_EXPONENT */ priTemplate = grow_template(pPrivateKeyTemplate, ulPrivateKeyAttributeCount, pri_attr_count + 1); if (priTemplate == NULL) { rv = CKR_HOST_MEMORY; goto failed_exit; } n = ulPrivateKeyAttributeCount; if (!has_class) { priTemplate[n].type = CKA_CLASS; priTemplate[n].pValue = (caddr_t)&pri_class; priTemplate[n].ulValueLen = sizeof (pri_class); n++; } if (!has_key_type) { priTemplate[n].type = CKA_KEY_TYPE; key_type = CKK_RSA; priTemplate[n].pValue = (caddr_t)&key_type; priTemplate[n].ulValueLen = sizeof (key_type); n++; } priTemplate[n].type = CKA_MODULUS; priTemplate[n].pValue = (caddr_t)private_modulus; priTemplate[n].ulValueLen = modulus_bytes; pri_out_attr_count++; n++; priTemplate[n].type = CKA_PRIVATE_EXPONENT; priTemplate[n].pValue = (caddr_t)private_exponent; priTemplate[n].ulValueLen = modulus_bytes; pri_out_attr_count++; n++; priTemplate[n].type = CKA_PRIME_1; priTemplate[n].pValue = (caddr_t)prime_1; priTemplate[n].ulValueLen = modulus_bytes/2; pri_out_attr_count++; n++; priTemplate[n].type = CKA_PRIME_2; priTemplate[n].pValue = (caddr_t)prime_2; priTemplate[n].ulValueLen = modulus_bytes/2; pri_out_attr_count++; n++; priTemplate[n].type = CKA_EXPONENT_1; priTemplate[n].pValue = (caddr_t)exponent_1; priTemplate[n].ulValueLen = modulus_bytes/2; pri_out_attr_count++; n++; priTemplate[n].type = CKA_EXPONENT_2; priTemplate[n].pValue = (caddr_t)exponent_2; priTemplate[n].ulValueLen = modulus_bytes/2; pri_out_attr_count++; n++; priTemplate[n].type = CKA_COEFFICIENT; priTemplate[n].pValue = (caddr_t)coefficient; priTemplate[n].ulValueLen = modulus_bytes/2; pri_out_attr_count++; rv = process_object_attributes(priTemplate, pri_attr_count - pri_out_attr_count, &obj_nkp.nkp_in_private_attributes, &is_token_obj2); if (rv != CKR_OK) { goto failed_exit; } obj_nkp.nkp_in_private_count = pri_attr_count - pri_out_attr_count; rv = process_object_attributes( &priTemplate[pri_attr_count - pri_out_attr_count], pri_out_attr_count, &obj_nkp.nkp_out_private_attributes, &is_token_obj2); if (rv != CKR_OK) { goto failed_exit; } obj_nkp.nkp_out_private_count = pri_out_attr_count; /* * The public key and the private key need to contain the same * attribute values for CKA_TOKEN. */ if (is_token_obj1 != is_token_obj2) { rv = CKR_ATTRIBUTE_VALUE_INVALID; goto failed_exit; } /* Call the CRYPTO_NOSTORE_GENERATE_KEY_PAIR ioctl. */ obj_nkp.nkp_session = session_p-> k_session; obj_nkp.nkp_mechanism.cm_type = k_mech_type; obj_nkp.nkp_mechanism.cm_param = pMechanism->pParameter; obj_nkp.nkp_mechanism.cm_param_len = pMechanism->ulParameterLen; while ((r = ioctl(kernel_fd, CRYPTO_NOSTORE_GENERATE_KEY_PAIR, &obj_nkp)) < 0) { if (errno != EINTR) break; } if (r < 0) { rv = CKR_FUNCTION_FAILED; } else { rv = crypto2pkcs11_error_number(obj_nkp.nkp_return_value); } free_attributes(obj_nkp.nkp_in_public_attributes, &obj_nkp.nkp_in_public_count); free_attributes(obj_nkp.nkp_in_private_attributes, &obj_nkp.nkp_in_private_count); if (rv != CKR_OK) { goto failed_exit; } rv = get_object_attributes( &pubTemplate[pub_attr_count - pub_out_attr_count], pub_out_attr_count, obj_nkp.nkp_out_public_attributes); if (rv == CRYPTO_SUCCESS) { rv = get_object_attributes( &priTemplate[pri_attr_count - pri_out_attr_count], pri_out_attr_count, obj_nkp.nkp_out_private_attributes); } free_attributes(obj_nkp.nkp_out_public_attributes, &obj_nkp.nkp_out_public_count); free_attributes(obj_nkp.nkp_out_private_attributes, &obj_nkp.nkp_out_private_count); if (rv != CRYPTO_SUCCESS) { goto failed_exit; } /* store generated modulus and public exponent */ rv = kernel_build_object(pubTemplate, pub_attr_count, new_pub_objp, session_p, KERNEL_GEN_KEY); if (rv != CRYPTO_SUCCESS) { goto failed_exit; } /* * Copy CKA_PUBLIC_EXPONENT from the public template * to the private template. */ rv = copy_attribute(CKA_PUBLIC_EXPONENT, pubTemplate, pub_attr_count, &priTemplate[pri_attr_count]); if (rv != CRYPTO_SUCCESS) { goto failed_exit; } rv = kernel_build_object(priTemplate, pri_attr_count + 1, new_pri_objp, session_p, KERNEL_GEN_KEY); (void) free(priTemplate[pri_attr_count].pValue); if (rv != CRYPTO_SUCCESS) { goto failed_exit; } (void) free(pubTemplate); (void) free(priTemplate); new_pub_objp->is_lib_obj = B_TRUE; new_pri_objp->is_lib_obj = B_TRUE; new_pub_objp->session_handle = (CK_SESSION_HANDLE)session_p; new_pri_objp->session_handle = (CK_SESSION_HANDLE)session_p; (void) pthread_mutex_init(&new_pub_objp->object_mutex, NULL); new_pub_objp->magic_marker = KERNELTOKEN_OBJECT_MAGIC; (void) pthread_mutex_init(&new_pri_objp->object_mutex, NULL); new_pri_objp->magic_marker = KERNELTOKEN_OBJECT_MAGIC; return (CKR_OK); failed_exit: free_attributes(obj_nkp.nkp_in_public_attributes, &obj_nkp.nkp_in_public_count); free_attributes(obj_nkp.nkp_out_public_attributes, &obj_nkp.nkp_out_public_count); free_attributes(obj_nkp.nkp_in_private_attributes, &obj_nkp.nkp_in_private_count); free_attributes(obj_nkp.nkp_out_private_attributes, &obj_nkp.nkp_out_private_count); if (pubTemplate != NULL) { (void) free(pubTemplate); } if (priTemplate != NULL) { (void) free(priTemplate); } return (rv); } CK_RV key_gen_dh_by_value(CK_MECHANISM_PTR pMechanism, CK_ATTRIBUTE_PTR pPublicKeyTemplate, CK_ULONG ulPublicKeyAttributeCount, CK_ATTRIBUTE_PTR pPrivateKeyTemplate, CK_ULONG ulPrivateKeyAttributeCount, kernel_session_t *session_p, crypto_mech_type_t k_mech_type, kernel_object_t *new_pub_objp, kernel_object_t *new_pri_objp) { crypto_nostore_generate_key_pair_t obj_nkp; CK_ATTRIBUTE_PTR pubTemplate = NULL; CK_ATTRIBUTE_PTR priTemplate = NULL; CK_RV rv = CKR_OK; CK_BBOOL is_token_obj1 = FALSE; CK_BBOOL is_token_obj2 = FALSE; uint_t pub_attr_count, pri_attr_count; uint_t pub_out_attr_count = 0, pri_out_attr_count = 0; char public_value[256]; char private_value[256]; CK_ULONG pub_class = CKO_PUBLIC_KEY; CK_ULONG pri_class = CKO_PRIVATE_KEY; CK_ULONG key_type; boolean_t has_class, has_key_type; int n, r; obj_nkp.nkp_in_public_count = 0; obj_nkp.nkp_out_public_count = 0; obj_nkp.nkp_in_private_count = 0; obj_nkp.nkp_out_private_count = 0; /* * Add CKA_VALUE to the public template. * This attribute must not be in the template. */ if (attribute_in_template(CKA_VALUE, pPublicKeyTemplate, ulPublicKeyAttributeCount)) { rv = CKR_TEMPLATE_INCONSISTENT; goto failed_exit; } has_class = attribute_in_template(CKA_CLASS, pPublicKeyTemplate, ulPublicKeyAttributeCount); has_key_type = attribute_in_template(CKA_KEY_TYPE, pPublicKeyTemplate, ulPublicKeyAttributeCount); pub_attr_count = ulPublicKeyAttributeCount + 1; if (!has_class) pub_attr_count++; if (!has_key_type) pub_attr_count++; pubTemplate = grow_template(pPublicKeyTemplate, ulPublicKeyAttributeCount, pub_attr_count); if (pubTemplate == NULL) { rv = CKR_HOST_MEMORY; goto failed_exit; } n = ulPublicKeyAttributeCount; if (!has_class) { pubTemplate[n].type = CKA_CLASS; pubTemplate[n].pValue = (caddr_t)&pub_class; pubTemplate[n].ulValueLen = sizeof (pub_class); n++; } if (!has_key_type) { pubTemplate[n].type = CKA_KEY_TYPE; key_type = CKK_DH; pubTemplate[n].pValue = (caddr_t)&key_type; pubTemplate[n].ulValueLen = sizeof (key_type); n++; } pubTemplate[n].type = CKA_VALUE; pubTemplate[n].pValue = (caddr_t)public_value; pubTemplate[n].ulValueLen = sizeof (public_value); pub_out_attr_count++; rv = process_object_attributes(pubTemplate, pub_attr_count - pub_out_attr_count, &obj_nkp.nkp_in_public_attributes, &is_token_obj1); if (rv != CKR_OK) { goto failed_exit; } obj_nkp.nkp_in_public_count = pub_attr_count - pub_out_attr_count; rv = process_object_attributes( &pubTemplate[pub_attr_count - pub_out_attr_count], pub_out_attr_count, &obj_nkp.nkp_out_public_attributes, &is_token_obj1); if (rv != CKR_OK) { goto failed_exit; } obj_nkp.nkp_out_public_count = pub_out_attr_count; /* * Cannot create a token object with a READ-ONLY * session. */ if (is_token_obj1 && session_p->ses_RO) { rv = CKR_SESSION_READ_ONLY; goto failed_exit; } /* * CKA_BASE, CKA_PRIME, and CKA_VALUE must not appear * in private template. */ if (attribute_in_template(CKA_BASE, pPrivateKeyTemplate, ulPrivateKeyAttributeCount) || attribute_in_template(CKA_PRIME, pPrivateKeyTemplate, ulPrivateKeyAttributeCount) || attribute_in_template(CKA_VALUE, pPrivateKeyTemplate, ulPrivateKeyAttributeCount)) { rv = CKR_TEMPLATE_INCONSISTENT; goto failed_exit; } if (attribute_in_template(CKA_VALUE, pPrivateKeyTemplate, ulPrivateKeyAttributeCount)) { rv = CKR_TEMPLATE_INCONSISTENT; goto failed_exit; } has_class = attribute_in_template(CKA_CLASS, pPrivateKeyTemplate, ulPrivateKeyAttributeCount); has_key_type = attribute_in_template(CKA_KEY_TYPE, pPrivateKeyTemplate, ulPrivateKeyAttributeCount); pri_attr_count = ulPrivateKeyAttributeCount + 1; if (!has_class) pri_attr_count++; if (!has_key_type) pri_attr_count++; /* allocate space for CKA_BASE and CKA_PRIME */ priTemplate = grow_template(pPrivateKeyTemplate, ulPrivateKeyAttributeCount, pri_attr_count + 2); if (priTemplate == NULL) { rv = CKR_HOST_MEMORY; goto failed_exit; } n = ulPrivateKeyAttributeCount; if (!has_class) { priTemplate[n].type = CKA_CLASS; priTemplate[n].pValue = (caddr_t)&pri_class; priTemplate[n].ulValueLen = sizeof (pri_class); n++; } if (!has_key_type) { priTemplate[n].type = CKA_KEY_TYPE; key_type = CKK_DH; priTemplate[n].pValue = (caddr_t)&key_type; priTemplate[n].ulValueLen = sizeof (key_type); n++; } priTemplate[n].type = CKA_VALUE; priTemplate[n].pValue = (caddr_t)private_value; priTemplate[n].ulValueLen = sizeof (private_value); pri_out_attr_count++; rv = process_object_attributes(priTemplate, pri_attr_count - pri_out_attr_count, &obj_nkp.nkp_in_private_attributes, &is_token_obj2); if (rv != CKR_OK) { goto failed_exit; } obj_nkp.nkp_in_private_count = pri_attr_count - pri_out_attr_count; rv = process_object_attributes( &priTemplate[pri_attr_count - pri_out_attr_count], pri_out_attr_count, &obj_nkp.nkp_out_private_attributes, &is_token_obj2); if (rv != CKR_OK) { goto failed_exit; } obj_nkp.nkp_out_private_count = pri_out_attr_count; /* * The public key and the private key need to contain the same * attribute values for CKA_TOKEN. */ if (is_token_obj1 != is_token_obj2) { rv = CKR_ATTRIBUTE_VALUE_INVALID; goto failed_exit; } /* Call the CRYPTO_NOSTORE_GENERATE_KEY_PAIR ioctl. */ obj_nkp.nkp_session = session_p-> k_session; obj_nkp.nkp_mechanism.cm_type = k_mech_type; obj_nkp.nkp_mechanism.cm_param = pMechanism->pParameter; obj_nkp.nkp_mechanism.cm_param_len = pMechanism->ulParameterLen; while ((r = ioctl(kernel_fd, CRYPTO_NOSTORE_GENERATE_KEY_PAIR, &obj_nkp)) < 0) { if (errno != EINTR) break; } if (r < 0) { rv = CKR_FUNCTION_FAILED; } else { rv = crypto2pkcs11_error_number(obj_nkp.nkp_return_value); } free_attributes(obj_nkp.nkp_in_public_attributes, &obj_nkp.nkp_in_public_count); free_attributes(obj_nkp.nkp_in_private_attributes, &obj_nkp.nkp_in_private_count); if (rv != CKR_OK) { goto failed_exit; } rv = get_object_attributes( &pubTemplate[pub_attr_count - pub_out_attr_count], pub_out_attr_count, obj_nkp.nkp_out_public_attributes); if (rv == CRYPTO_SUCCESS) { rv = get_object_attributes( &priTemplate[pri_attr_count - pri_out_attr_count], pri_out_attr_count, obj_nkp.nkp_out_private_attributes); } free_attributes(obj_nkp.nkp_out_public_attributes, &obj_nkp.nkp_out_public_count); free_attributes(obj_nkp.nkp_out_private_attributes, &obj_nkp.nkp_out_private_count); if (rv != CRYPTO_SUCCESS) { goto failed_exit; } rv = kernel_build_object(pubTemplate, pub_attr_count, new_pub_objp, session_p, KERNEL_GEN_KEY); if (rv != CRYPTO_SUCCESS) { goto failed_exit; } /* * Copy CKA_BASE and CKA_PRIME from the public template * to the private template. */ rv = copy_attribute(CKA_BASE, pubTemplate, pub_attr_count, &priTemplate[pri_attr_count]); if (rv != CRYPTO_SUCCESS) { goto failed_exit; } rv = copy_attribute(CKA_PRIME, pubTemplate, pub_attr_count, &priTemplate[pri_attr_count + 1]); if (rv != CRYPTO_SUCCESS) { (void) free(priTemplate[pri_attr_count].pValue); goto failed_exit; } /* +2 to account for CKA_BASE and CKA_PRIME */ rv = kernel_build_object(priTemplate, pri_attr_count + 2, new_pri_objp, session_p, KERNEL_GEN_KEY); (void) free(priTemplate[pri_attr_count].pValue); (void) free(priTemplate[pri_attr_count + 1].pValue); if (rv != CRYPTO_SUCCESS) { goto failed_exit; } (void) free(pubTemplate); (void) free(priTemplate); new_pub_objp->is_lib_obj = B_TRUE; new_pri_objp->is_lib_obj = B_TRUE; new_pub_objp->session_handle = (CK_SESSION_HANDLE)session_p; new_pri_objp->session_handle = (CK_SESSION_HANDLE)session_p; (void) pthread_mutex_init(&new_pub_objp->object_mutex, NULL); new_pub_objp->magic_marker = KERNELTOKEN_OBJECT_MAGIC; (void) pthread_mutex_init(&new_pri_objp->object_mutex, NULL); new_pri_objp->magic_marker = KERNELTOKEN_OBJECT_MAGIC; return (CKR_OK); failed_exit: free_attributes(obj_nkp.nkp_in_public_attributes, &obj_nkp.nkp_in_public_count); free_attributes(obj_nkp.nkp_out_public_attributes, &obj_nkp.nkp_out_public_count); free_attributes(obj_nkp.nkp_in_private_attributes, &obj_nkp.nkp_in_private_count); free_attributes(obj_nkp.nkp_out_private_attributes, &obj_nkp.nkp_out_private_count); if (pubTemplate != NULL) { (void) free(pubTemplate); } if (priTemplate != NULL) { (void) free(priTemplate); } return (rv); } CK_RV key_gen_ec_by_value(CK_MECHANISM_PTR pMechanism, CK_ATTRIBUTE_PTR pPublicKeyTemplate, CK_ULONG ulPublicKeyAttributeCount, CK_ATTRIBUTE_PTR pPrivateKeyTemplate, CK_ULONG ulPrivateKeyAttributeCount, kernel_session_t *session_p, crypto_mech_type_t k_mech_type, kernel_object_t *new_pub_objp, kernel_object_t *new_pri_objp) { crypto_nostore_generate_key_pair_t obj_nkp; CK_ATTRIBUTE_PTR pubTemplate = NULL; CK_ATTRIBUTE_PTR priTemplate = NULL; CK_RV rv = CKR_OK; CK_BBOOL is_token_obj1 = FALSE; CK_BBOOL is_token_obj2 = FALSE; uint_t pub_attr_count, pri_attr_count; uint_t pub_out_attr_count = 0, pri_out_attr_count = 0; char value[EC_MAX_VALUE_LEN]; char point[EC_MAX_POINT_LEN]; CK_ULONG pub_class = CKO_PUBLIC_KEY; CK_ULONG pri_class = CKO_PRIVATE_KEY; CK_ULONG key_type; boolean_t has_class, has_key_type; int n, r; obj_nkp.nkp_in_public_count = 0; obj_nkp.nkp_out_public_count = 0; obj_nkp.nkp_in_private_count = 0; obj_nkp.nkp_out_private_count = 0; /* * Add CKA_EC_POINT to the public template. * This is the generated value Q. This attribute * must not be in the template. */ if (attribute_in_template(CKA_EC_POINT, pPublicKeyTemplate, ulPublicKeyAttributeCount)) { rv = CKR_TEMPLATE_INCONSISTENT; goto failed_exit; } has_class = attribute_in_template(CKA_CLASS, pPublicKeyTemplate, ulPublicKeyAttributeCount); has_key_type = attribute_in_template(CKA_KEY_TYPE, pPublicKeyTemplate, ulPublicKeyAttributeCount); pub_attr_count = ulPublicKeyAttributeCount + 1; if (!has_class) pub_attr_count++; if (!has_key_type) pub_attr_count++; pubTemplate = grow_template(pPublicKeyTemplate, ulPublicKeyAttributeCount, pub_attr_count); if (pubTemplate == NULL) { rv = CKR_HOST_MEMORY; goto failed_exit; } n = ulPublicKeyAttributeCount; if (!has_class) { pubTemplate[n].type = CKA_CLASS; pubTemplate[n].pValue = (caddr_t)&pub_class; pubTemplate[n].ulValueLen = sizeof (pub_class); n++; } if (!has_key_type) { pubTemplate[n].type = CKA_KEY_TYPE; key_type = CKK_EC; pubTemplate[n].pValue = (caddr_t)&key_type; pubTemplate[n].ulValueLen = sizeof (key_type); n++; } pubTemplate[n].type = CKA_EC_POINT; pubTemplate[n].pValue = (caddr_t)point; pubTemplate[n].ulValueLen = sizeof (point); pub_out_attr_count++; rv = process_object_attributes(pubTemplate, pub_attr_count - pub_out_attr_count, &obj_nkp.nkp_in_public_attributes, &is_token_obj1); if (rv != CKR_OK) { goto failed_exit; } obj_nkp.nkp_in_public_count = pub_attr_count - pub_out_attr_count; rv = process_object_attributes( &pubTemplate[pub_attr_count - pub_out_attr_count], pub_out_attr_count, &obj_nkp.nkp_out_public_attributes, &is_token_obj1); if (rv != CKR_OK) { goto failed_exit; } obj_nkp.nkp_out_public_count = pub_out_attr_count; /* * Cannot create a token object with a READ-ONLY * session. */ if (is_token_obj1 && session_p->ses_RO) { rv = CKR_SESSION_READ_ONLY; goto failed_exit; } /* * CKA_EC_PARAMS and CKA_VALUE must not appear in * private template. */ if (attribute_in_template(CKA_EC_PARAMS, pPrivateKeyTemplate, ulPrivateKeyAttributeCount) || attribute_in_template(CKA_VALUE, pPrivateKeyTemplate, ulPrivateKeyAttributeCount)) { rv = CKR_TEMPLATE_INCONSISTENT; goto failed_exit; } has_class = attribute_in_template(CKA_CLASS, pPrivateKeyTemplate, ulPrivateKeyAttributeCount); has_key_type = attribute_in_template(CKA_KEY_TYPE, pPrivateKeyTemplate, ulPrivateKeyAttributeCount); pri_attr_count = ulPrivateKeyAttributeCount + 1; if (!has_class) pri_attr_count++; if (!has_key_type) pri_attr_count++; /* allocate space for CKA_EC_PARAMS */ priTemplate = grow_template(pPrivateKeyTemplate, ulPrivateKeyAttributeCount, pri_attr_count + 1); if (priTemplate == NULL) { rv = CKR_HOST_MEMORY; goto failed_exit; } n = ulPrivateKeyAttributeCount; if (!has_class) { priTemplate[n].type = CKA_CLASS; priTemplate[n].pValue = (caddr_t)&pri_class; priTemplate[n].ulValueLen = sizeof (pri_class); n++; } if (!has_key_type) { priTemplate[n].type = CKA_KEY_TYPE; key_type = CKK_EC; priTemplate[n].pValue = (caddr_t)&key_type; priTemplate[n].ulValueLen = sizeof (key_type); n++; } priTemplate[n].type = CKA_VALUE; priTemplate[n].pValue = (caddr_t)value; priTemplate[n].ulValueLen = sizeof (value); pri_out_attr_count++; rv = process_object_attributes(priTemplate, pri_attr_count - pri_out_attr_count, &obj_nkp.nkp_in_private_attributes, &is_token_obj2); if (rv != CKR_OK) { goto failed_exit; } obj_nkp.nkp_in_private_count = pri_attr_count - pri_out_attr_count; rv = process_object_attributes( &priTemplate[pri_attr_count - pri_out_attr_count], pri_out_attr_count, &obj_nkp.nkp_out_private_attributes, &is_token_obj2); if (rv != CKR_OK) { goto failed_exit; } obj_nkp.nkp_out_private_count = pri_out_attr_count; /* * The public key and the private key need to contain the same * attribute values for CKA_TOKEN. */ if (is_token_obj1 != is_token_obj2) { rv = CKR_ATTRIBUTE_VALUE_INVALID; goto failed_exit; } /* Call the CRYPTO_NOSTORE_GENERATE_KEY_PAIR ioctl. */ obj_nkp.nkp_session = session_p-> k_session; obj_nkp.nkp_mechanism.cm_type = k_mech_type; obj_nkp.nkp_mechanism.cm_param = pMechanism->pParameter; obj_nkp.nkp_mechanism.cm_param_len = pMechanism->ulParameterLen; while ((r = ioctl(kernel_fd, CRYPTO_NOSTORE_GENERATE_KEY_PAIR, &obj_nkp)) < 0) { if (errno != EINTR) break; } if (r < 0) { rv = CKR_FUNCTION_FAILED; } else { rv = crypto2pkcs11_error_number(obj_nkp.nkp_return_value); } free_attributes(obj_nkp.nkp_in_public_attributes, &obj_nkp.nkp_in_public_count); free_attributes(obj_nkp.nkp_in_private_attributes, &obj_nkp.nkp_in_private_count); if (rv != CKR_OK) { goto failed_exit; } rv = get_object_attributes( &pubTemplate[pub_attr_count - pub_out_attr_count], pub_out_attr_count, obj_nkp.nkp_out_public_attributes); if (rv == CRYPTO_SUCCESS) { rv = get_object_attributes( &priTemplate[pri_attr_count - pri_out_attr_count], pri_out_attr_count, obj_nkp.nkp_out_private_attributes); } free_attributes(obj_nkp.nkp_out_public_attributes, &obj_nkp.nkp_out_public_count); free_attributes(obj_nkp.nkp_out_private_attributes, &obj_nkp.nkp_out_private_count); if (rv != CRYPTO_SUCCESS) { goto failed_exit; } rv = kernel_build_object(pubTemplate, pub_attr_count, new_pub_objp, session_p, KERNEL_GEN_KEY); if (rv != CRYPTO_SUCCESS) { goto failed_exit; } /* * Copy CKA_EC_PARAMS from the public template to the * private template. */ rv = copy_attribute(CKA_EC_PARAMS, pubTemplate, pub_attr_count, &priTemplate[pri_attr_count]); if (rv != CRYPTO_SUCCESS) { goto failed_exit; } /* +1 to account for CKA_EC_PARAMS */ rv = kernel_build_object(priTemplate, pri_attr_count + 1, new_pri_objp, session_p, KERNEL_GEN_KEY); (void) free(priTemplate[pri_attr_count].pValue); if (rv != CRYPTO_SUCCESS) { goto failed_exit; } (void) free(pubTemplate); (void) free(priTemplate); new_pub_objp->is_lib_obj = B_TRUE; new_pri_objp->is_lib_obj = B_TRUE; new_pub_objp->session_handle = (CK_SESSION_HANDLE)session_p; new_pri_objp->session_handle = (CK_SESSION_HANDLE)session_p; (void) pthread_mutex_init(&new_pub_objp->object_mutex, NULL); new_pub_objp->magic_marker = KERNELTOKEN_OBJECT_MAGIC; (void) pthread_mutex_init(&new_pri_objp->object_mutex, NULL); new_pri_objp->magic_marker = KERNELTOKEN_OBJECT_MAGIC; return (CKR_OK); failed_exit: free_attributes(obj_nkp.nkp_in_public_attributes, &obj_nkp.nkp_in_public_count); free_attributes(obj_nkp.nkp_out_public_attributes, &obj_nkp.nkp_out_public_count); free_attributes(obj_nkp.nkp_in_private_attributes, &obj_nkp.nkp_in_private_count); free_attributes(obj_nkp.nkp_out_private_attributes, &obj_nkp.nkp_out_private_count); if (pubTemplate != NULL) { (void) free(pubTemplate); } if (priTemplate != NULL) { (void) free(priTemplate); } return (rv); } CK_RV C_GenerateKeyPair(CK_SESSION_HANDLE hSession, CK_MECHANISM_PTR pMechanism, CK_ATTRIBUTE_PTR pPublicKeyTemplate, CK_ULONG ulPublicKeyAttributeCount, CK_ATTRIBUTE_PTR pPrivateKeyTemplate, CK_ULONG ulPrivateKeyAttributeCount, CK_OBJECT_HANDLE_PTR phPublicKey, CK_OBJECT_HANDLE_PTR phPrivateKey) { CK_RV rv = CKR_OK; kernel_session_t *session_p; kernel_object_t *new_pub_objp = NULL; kernel_object_t *new_pri_objp = NULL; kernel_slot_t *pslot; boolean_t ses_lock_held = B_FALSE; CK_BBOOL is_pri_obj1; CK_BBOOL is_pri_obj2; CK_BBOOL is_token_obj1 = FALSE; CK_BBOOL is_token_obj2 = FALSE; crypto_mech_type_t k_mech_type; int r; CK_RV (*func)(CK_MECHANISM_PTR, CK_ATTRIBUTE_PTR, CK_ULONG, CK_ATTRIBUTE_PTR, CK_ULONG, kernel_session_t *, crypto_mech_type_t, kernel_object_t *, kernel_object_t *); if (!kernel_initialized) return (CKR_CRYPTOKI_NOT_INITIALIZED); /* Obtain the session pointer. */ rv = handle2session(hSession, &session_p); if (rv != CKR_OK) return (rv); if ((pMechanism == NULL) || (phPublicKey == NULL) || (phPrivateKey == NULL)) { rv = CKR_ARGUMENTS_BAD; goto failed_exit; } if ((pPublicKeyTemplate == NULL) && (ulPublicKeyAttributeCount != 0)) { rv = CKR_ARGUMENTS_BAD; goto failed_exit; } if ((pPrivateKeyTemplate == NULL) && (ulPrivateKeyAttributeCount != 0)) { rv = CKR_ARGUMENTS_BAD; goto failed_exit; } /* Get the kernel's internal mechanism number. */ rv = kernel_mech(pMechanism->mechanism, &k_mech_type); if (rv != CKR_OK) { goto failed_exit; } /* Create an object wrapper for the public key */ new_pub_objp = calloc(1, sizeof (kernel_object_t)); if (new_pub_objp == NULL) { rv = CKR_HOST_MEMORY; goto failed_exit; } /* Create an object wrapper for the private key. */ new_pri_objp = calloc(1, sizeof (kernel_object_t)); if (new_pri_objp == NULL) { rv = CKR_HOST_MEMORY; goto failed_exit; } /* * Special Case: if token does not support object creation, * but does support key generation by value, then create a session * object and initialize with values returned by token. */ pslot = slot_table[session_p->ses_slotid]; if (!pslot->sl_func_list.fl_object_create) { switch (pMechanism->mechanism) { case CKM_RSA_PKCS_KEY_PAIR_GEN: func = key_gen_rsa_by_value; break; case CKM_DH_PKCS_KEY_PAIR_GEN: func = key_gen_dh_by_value; break; case CKM_EC_KEY_PAIR_GEN: func = key_gen_ec_by_value; break; default: rv = CKR_MECHANISM_INVALID; goto failed_exit; } rv = (*func)(pMechanism, pPublicKeyTemplate, ulPublicKeyAttributeCount, pPrivateKeyTemplate, ulPrivateKeyAttributeCount, session_p, k_mech_type, new_pub_objp, new_pri_objp); if (rv != CKR_OK) goto failed_exit; } else { crypto_object_generate_key_pair_t obj_kp; /* Process the public key attributes. */ rv = process_object_attributes(pPublicKeyTemplate, ulPublicKeyAttributeCount, &obj_kp.kp_public_attributes, &is_token_obj1); if (rv != CKR_OK) { goto failed_exit; } /* Cannot create a token object with a READ-ONLY session. */ if (is_token_obj1 && session_p->ses_RO) { free_object_attributes(obj_kp.kp_public_attributes, ulPublicKeyAttributeCount); rv = CKR_SESSION_READ_ONLY; goto failed_exit; } /* Process the private key attributes. */ rv = process_object_attributes(pPrivateKeyTemplate, ulPrivateKeyAttributeCount, &obj_kp.kp_private_attributes, &is_token_obj2); if (rv != CKR_OK) { free_object_attributes(obj_kp.kp_public_attributes, ulPublicKeyAttributeCount); goto failed_exit; } /* * The public key and the private key need to contain the same * attribute values for CKA_TOKEN. */ if (is_token_obj1 != is_token_obj2) { free_object_attributes(obj_kp.kp_public_attributes, ulPublicKeyAttributeCount); free_object_attributes(obj_kp.kp_private_attributes, ulPrivateKeyAttributeCount); rv = CKR_ATTRIBUTE_VALUE_INVALID; goto failed_exit; } /* Call the CRYPTO_GENERATE_KEY_PAIR ioctl. */ obj_kp.kp_session = session_p-> k_session; obj_kp.kp_mechanism.cm_type = k_mech_type; obj_kp.kp_mechanism.cm_param = pMechanism->pParameter; obj_kp.kp_mechanism.cm_param_len = pMechanism->ulParameterLen; obj_kp.kp_public_count = ulPublicKeyAttributeCount; obj_kp.kp_private_count = ulPrivateKeyAttributeCount; while ((r = ioctl(kernel_fd, CRYPTO_GENERATE_KEY_PAIR, &obj_kp)) < 0) { if (errno != EINTR) break; } if (r < 0) { rv = CKR_FUNCTION_FAILED; } else { rv = crypto2pkcs11_error_number(obj_kp.kp_return_value); } free_object_attributes(obj_kp.kp_public_attributes, ulPublicKeyAttributeCount); free_object_attributes(obj_kp.kp_private_attributes, ulPrivateKeyAttributeCount); if (rv != CKR_OK) goto failed_exit; /* Get the CKA_PRIVATE value for the key pair. */ rv = get_cka_private_value(session_p, obj_kp.kp_public_handle, &is_pri_obj1); if (rv != CKR_OK) { goto failed_exit; } rv = get_cka_private_value(session_p, obj_kp.kp_private_handle, &is_pri_obj2); if (rv != CKR_OK) { goto failed_exit; } /* * Store the kernel public key handle into the public key * object and finish the public key object initialization. */ new_pub_objp->is_lib_obj = B_FALSE; new_pub_objp->k_handle = obj_kp.kp_public_handle; new_pub_objp->session_handle = (CK_SESSION_HANDLE)session_p; new_pub_objp->extra_attrlistp = NULL; if (is_pri_obj1) new_pub_objp->bool_attr_mask |= PRIVATE_BOOL_ON; else new_pub_objp->bool_attr_mask &= ~PRIVATE_BOOL_ON; if (is_token_obj1) new_pub_objp->bool_attr_mask |= TOKEN_BOOL_ON; else new_pub_objp->bool_attr_mask &= ~TOKEN_BOOL_ON; (void) pthread_mutex_init(&new_pub_objp->object_mutex, NULL); new_pub_objp->magic_marker = KERNELTOKEN_OBJECT_MAGIC; /* * Store the kernel private key handle into the private key * object and finish the private key object initialization. */ new_pri_objp->is_lib_obj = B_FALSE; new_pri_objp->k_handle = obj_kp.kp_private_handle; new_pri_objp->session_handle = (CK_SESSION_HANDLE)session_p; new_pri_objp->extra_attrlistp = NULL; if (is_pri_obj2) new_pri_objp->bool_attr_mask |= PRIVATE_BOOL_ON; else new_pri_objp->bool_attr_mask &= ~PRIVATE_BOOL_ON; if (is_token_obj2) new_pri_objp->bool_attr_mask |= TOKEN_BOOL_ON; else new_pri_objp->bool_attr_mask &= ~TOKEN_BOOL_ON; } (void) pthread_mutex_init(&new_pri_objp->object_mutex, NULL); new_pri_objp->magic_marker = KERNELTOKEN_OBJECT_MAGIC; /* * Add the new pub/pri objects to the slot's token list if they are * token objects. Otherwise, add them to the session's object list. */ if (is_token_obj1) { /* is_token_obj1 == is_token_obj2 */ pslot = slot_table[session_p->ses_slotid]; kernel_add_token_object_to_slot(new_pub_objp, pslot); kernel_add_token_object_to_slot(new_pri_objp, pslot); } else { kernel_add_object_to_session(new_pub_objp, session_p); kernel_add_object_to_session(new_pri_objp, session_p); } *phPublicKey = (CK_OBJECT_HANDLE)new_pub_objp; *phPrivateKey = (CK_OBJECT_HANDLE)new_pri_objp; REFRELE(session_p, ses_lock_held); return (rv); failed_exit: if (new_pub_objp != NULL) { (void) free(new_pub_objp); } if (new_pri_objp != NULL) { (void) free(new_pri_objp); } REFRELE(session_p, ses_lock_held); return (rv); } CK_RV C_WrapKey(CK_SESSION_HANDLE hSession, CK_MECHANISM_PTR pMechanism, CK_OBJECT_HANDLE hWrappingKey, CK_OBJECT_HANDLE hKey, CK_BYTE_PTR pWrappedKey, CK_ULONG_PTR pulWrappedKeyLen) { CK_RV rv = CKR_OK; kernel_session_t *session_p; boolean_t ses_lock_held = B_FALSE; kernel_object_t *wrappingkey_p; kernel_object_t *key_p; crypto_mech_type_t k_mech_type; crypto_object_wrap_key_t obj_wrapkey; int r; if (!kernel_initialized) return (CKR_CRYPTOKI_NOT_INITIALIZED); if (pulWrappedKeyLen == NULL || pMechanism == NULL) { return (CKR_ARGUMENTS_BAD); } /* * Obtain the session pointer. Also, increment the session * reference count. */ rv = handle2session(hSession, &session_p); if (rv != CKR_OK) return (rv); /* Get the kernel's internal mechanism number. */ rv = kernel_mech(pMechanism->mechanism, &k_mech_type); if (rv != CKR_OK) { REFRELE(session_p, ses_lock_held); return (rv); } /* Obtain the wrapping key object pointer. */ HANDLE2OBJECT(hWrappingKey, wrappingkey_p, rv); if (rv != CKR_OK) { REFRELE(session_p, ses_lock_held); return (rv); } /* Obtain the to_be_wrapped key object pointer. */ HANDLE2OBJECT(hKey, key_p, rv); if (rv != CKR_OK) { OBJ_REFRELE(wrappingkey_p); REFRELE(session_p, ses_lock_held); return (rv); } /* Make the CRYPTO_OBJECT_WRAP_KEY ioctl call. */ obj_wrapkey.wk_session = session_p->k_session; obj_wrapkey.wk_mechanism.cm_type = k_mech_type; obj_wrapkey.wk_mechanism.cm_param = pMechanism->pParameter; obj_wrapkey.wk_mechanism.cm_param_len = pMechanism->ulParameterLen; obj_wrapkey.wk_wrapping_key.ck_format = CRYPTO_KEY_REFERENCE; obj_wrapkey.wk_wrapping_key.ck_obj_id = wrappingkey_p->k_handle; obj_wrapkey.wk_object_handle = key_p->k_handle; obj_wrapkey.wk_wrapped_key_len = *pulWrappedKeyLen; obj_wrapkey.wk_wrapped_key = (char *)pWrappedKey; while ((r = ioctl(kernel_fd, CRYPTO_WRAP_KEY, &obj_wrapkey)) < 0) { if (errno != EINTR) break; } if (r < 0) { rv = CKR_FUNCTION_FAILED; } else { rv = crypto2pkcs11_error_number(obj_wrapkey.wk_return_value); } /* * Besides rv == CKR_OK, we will set the value of pulWrappedKeyLen * when the applciation-supplied wrapped key buffer is too small. * The situation that the application only asks for the length of * the wrapped key is covered in rv == CKR_OK. */ if (rv == CKR_OK || rv == CKR_BUFFER_TOO_SMALL) { *pulWrappedKeyLen = obj_wrapkey.wk_wrapped_key_len; } OBJ_REFRELE(key_p); OBJ_REFRELE(wrappingkey_p); REFRELE(session_p, ses_lock_held); return (rv); } CK_RV C_UnwrapKey(CK_SESSION_HANDLE hSession, CK_MECHANISM_PTR pMechanism, CK_OBJECT_HANDLE hUnwrappingKey, CK_BYTE_PTR pWrappedKey, CK_ULONG ulWrappedKeyLen, CK_ATTRIBUTE_PTR pTemplate, CK_ULONG ulAttributeCount, CK_OBJECT_HANDLE_PTR phKey) { CK_RV rv = CKR_OK; kernel_session_t *session_p; kernel_object_t *unwrappingkey_p; kernel_object_t *new_objp = NULL; kernel_slot_t *pslot; boolean_t ses_lock_held = B_FALSE; CK_BBOOL is_pri_obj; CK_BBOOL is_token_obj = FALSE; CK_MECHANISM_INFO info; uint32_t k_mi_flags; CK_BYTE *clear_key_val = NULL; CK_ULONG ulDataLen; CK_ATTRIBUTE_PTR newTemplate = NULL; crypto_mech_type_t k_mech_type; crypto_object_unwrap_key_t obj_unwrapkey; int r; if (!kernel_initialized) return (CKR_CRYPTOKI_NOT_INITIALIZED); if (pMechanism == NULL || pWrappedKey == NULL || phKey == NULL) { return (CKR_ARGUMENTS_BAD); } if ((pTemplate == NULL) && (ulAttributeCount != 0)) { return (CKR_ARGUMENTS_BAD); } /* Obtain the session pointer. */ rv = handle2session(hSession, &session_p); if (rv != CKR_OK) return (rv); /* Obtain the wrapping key object pointer. */ HANDLE2OBJECT(hUnwrappingKey, unwrappingkey_p, rv); if (rv != CKR_OK) { REFRELE(session_p, ses_lock_held); return (rv); } /* * If the HW provider doesn't support C_UnwrapKey, we will try * to emulate it in the library. */ pslot = slot_table[session_p->ses_slotid]; if ((!pslot->sl_func_list.fl_object_create) && (!pslot->sl_func_list.fl_key_unwrap)) { rv = get_mechanism_info(pslot, pMechanism->mechanism, &info, &k_mi_flags); if (rv != CKR_OK) { goto failed_exit; } /* * If the mechanism flag doesn't have CKF_UNWRAP, and it's * an unwrapping of a secret key object, then help this * out with a decryption followed by an object creation. */ if (!(k_mi_flags & CRYPTO_FG_UNWRAP) && (k_mi_flags & CRYPTO_FG_DECRYPT) && (is_secret_key_template(pTemplate, ulAttributeCount))) { /* First allocate space for the recovered key value */ clear_key_val = malloc(ulWrappedKeyLen); if (clear_key_val == NULL) { rv = CKR_HOST_MEMORY; goto failed_exit; } rv = kernel_decrypt_init(session_p, unwrappingkey_p, pMechanism); if (rv != CKR_OK) { goto failed_exit; } ulDataLen = ulWrappedKeyLen; rv = kernel_decrypt(session_p, pWrappedKey, ulWrappedKeyLen, clear_key_val, &ulDataLen); if (rv != CKR_OK) { goto failed_exit; } newTemplate = grow_template(pTemplate, ulAttributeCount, ulAttributeCount + 1); if (newTemplate == NULL) { rv = CKR_HOST_MEMORY; goto failed_exit; } /* Now add the CKA_VALUE attribute to template */ newTemplate[ulAttributeCount].type = CKA_VALUE; newTemplate[ulAttributeCount].pValue = clear_key_val; newTemplate[ulAttributeCount].ulValueLen = ulDataLen; /* Finally create the key, based on the new template */ rv = kernel_add_object(newTemplate, ulAttributeCount + 1, phKey, session_p); (void) free(clear_key_val); (void) free(newTemplate); OBJ_REFRELE(unwrappingkey_p); REFRELE(session_p, ses_lock_held); return (rv); } else { rv = CKR_FUNCTION_FAILED; goto failed_exit; } } /* * If we come here, the HW provider must have registered the unwrapkey * entry. Therefore, the unwrap key will be performed in the HW * provider. */ rv = kernel_mech(pMechanism->mechanism, &k_mech_type); if (rv != CKR_OK) { goto failed_exit; } /* Create an object wrapper for the new key in the library first */ new_objp = calloc(1, sizeof (kernel_object_t)); if (new_objp == NULL) { rv = CKR_HOST_MEMORY; goto failed_exit; } /* Process the attributes */ rv = process_object_attributes(pTemplate, ulAttributeCount, &obj_unwrapkey.uk_attributes, &is_token_obj); if (rv != CKR_OK) { goto failed_exit; } /* Cannot create a token object with a READ-ONLY session. */ if (is_token_obj && session_p->ses_RO) { free_object_attributes(obj_unwrapkey.uk_attributes, ulAttributeCount); rv = CKR_SESSION_READ_ONLY; goto failed_exit; } /* Make the CRYPTO_UNWRAP_KEY ioctl call. */ obj_unwrapkey.uk_session = session_p->k_session; obj_unwrapkey.uk_mechanism.cm_type = k_mech_type; obj_unwrapkey.uk_mechanism.cm_param = pMechanism->pParameter; obj_unwrapkey.uk_mechanism.cm_param_len = pMechanism->ulParameterLen; obj_unwrapkey.uk_unwrapping_key.ck_format = CRYPTO_KEY_REFERENCE; obj_unwrapkey.uk_unwrapping_key.ck_obj_id = unwrappingkey_p->k_handle; obj_unwrapkey.uk_wrapped_key = (char *)pWrappedKey; obj_unwrapkey.uk_wrapped_key_len = ulWrappedKeyLen; obj_unwrapkey.uk_count = ulAttributeCount; while ((r = ioctl(kernel_fd, CRYPTO_UNWRAP_KEY, &obj_unwrapkey)) < 0) { if (errno != EINTR) break; } if (r < 0) { rv = CKR_FUNCTION_FAILED; } else { rv = crypto2pkcs11_error_number(obj_unwrapkey.uk_return_value); } free_object_attributes(obj_unwrapkey.uk_attributes, ulAttributeCount); if (rv != CKR_OK) { goto failed_exit; } /* Get the CKA_PRIVATE value for the unwrapped key. */ rv = get_cka_private_value(session_p, obj_unwrapkey.uk_object_handle, &is_pri_obj); if (rv != CKR_OK) { goto failed_exit; } /* * Store the kernel object handle in the new key object wrapper and * initialize it. */ new_objp->k_handle = obj_unwrapkey.uk_object_handle; new_objp->is_lib_obj = B_FALSE; new_objp->session_handle = (CK_SESSION_HANDLE)session_p; new_objp->extra_attrlistp = NULL; if (is_pri_obj) new_objp->bool_attr_mask |= PRIVATE_BOOL_ON; else new_objp->bool_attr_mask &= ~PRIVATE_BOOL_ON; if (is_token_obj) new_objp->bool_attr_mask |= TOKEN_BOOL_ON; else new_objp->bool_attr_mask &= ~TOKEN_BOOL_ON; (void) pthread_mutex_init(&new_objp->object_mutex, NULL); new_objp->magic_marker = KERNELTOKEN_OBJECT_MAGIC; /* * Add the new object to the slot's token object list if it is a * a token object. Otherwise, add it to the session's object list. */ if (is_token_obj) { pslot = slot_table[session_p->ses_slotid]; kernel_add_token_object_to_slot(new_objp, pslot); } else { kernel_add_object_to_session(new_objp, session_p); } *phKey = (CK_OBJECT_HANDLE)new_objp; OBJ_REFRELE(unwrappingkey_p); REFRELE(session_p, ses_lock_held); return (rv); failed_exit: OBJ_REFRELE(unwrappingkey_p); if (new_objp != NULL) (void) free(new_objp); if (clear_key_val != NULL) (void) free(clear_key_val); if (newTemplate != NULL) (void) free(newTemplate); REFRELE(session_p, ses_lock_held); return (rv); } /* * Get sufficient attributes from a base key to pass by value in a * crypto_key structure. Storage for attributes is allocated. * For EC public keys, it is CKA_EC_PARAMS and CKA_EC_POINT. * For EC private keys, it is CKA_EC_PARAMS and CKA_VALUE. */ static int get_base_key_attributes(kernel_object_t *base_key, crypto_key_t *key_by_value) { CK_ATTRIBUTE tmp; crypto_object_attribute_t *attrs = NULL; biginteger_t *big; int i, count = 0, rv; switch (base_key->key_type) { case CKK_EC: count = 2; attrs = malloc(count * sizeof (crypto_object_attribute_t)); if (attrs == NULL) { rv = CKR_HOST_MEMORY; goto out; } bzero(attrs, count * sizeof (crypto_object_attribute_t)); (void) pthread_mutex_lock(&base_key->object_mutex); if (!base_key->is_lib_obj) { rv = CRYPTO_ARGUMENTS_BAD; goto out; } if (base_key->class != CKO_PUBLIC_KEY && base_key->class != CKO_PRIVATE_KEY) { rv = CRYPTO_ARGUMENTS_BAD; goto out; } /* * Both public and private EC keys should have * a CKA_EC_PARAMS attribute. */ tmp.type = CKA_EC_PARAMS; tmp.pValue = NULL; /* get size of attribute */ rv = kernel_get_attribute(base_key, &tmp); if (rv != CKR_OK) { goto out; } tmp.pValue = malloc(tmp.ulValueLen); if (tmp.pValue == NULL) { rv = CKR_HOST_MEMORY; goto out; } rv = kernel_get_attribute(base_key, &tmp); if (rv != CKR_OK) { free(tmp.pValue); goto out; } attrs[0].oa_type = tmp.type; attrs[0].oa_value = tmp.pValue; attrs[0].oa_value_len = tmp.ulValueLen; switch (base_key->class) { case CKO_PUBLIC_KEY: big = OBJ_PUB_EC_POINT(base_key); tmp.type = CKA_EC_POINT; break; case CKO_PRIVATE_KEY: big = OBJ_PRI_EC_VALUE(base_key); tmp.type = CKA_VALUE; break; default: rv = CKR_ATTRIBUTE_TYPE_INVALID; goto out; } tmp.ulValueLen = big->big_value_len; tmp.pValue = malloc(tmp.ulValueLen); if (tmp.pValue == NULL) { rv = CKR_HOST_MEMORY; goto out; } rv = kernel_get_attribute(base_key, &tmp); if (rv != CKR_OK) { free(tmp.pValue); goto out; } attrs[1].oa_type = tmp.type; attrs[1].oa_value = tmp.pValue; attrs[1].oa_value_len = tmp.ulValueLen; key_by_value->ck_attrs = attrs; key_by_value->ck_count = 2; break; case CKK_DH: count = 3; attrs = malloc(count * sizeof (crypto_object_attribute_t)); if (attrs == NULL) { rv = CKR_HOST_MEMORY; goto out; } bzero(attrs, count * sizeof (crypto_object_attribute_t)); (void) pthread_mutex_lock(&base_key->object_mutex); if (!base_key->is_lib_obj) { rv = CRYPTO_ARGUMENTS_BAD; goto out; } if (base_key->class != CKO_PRIVATE_KEY) { rv = CRYPTO_ARGUMENTS_BAD; goto out; } tmp.type = CKA_BASE; tmp.pValue = NULL; /* get size of attribute */ rv = kernel_get_attribute(base_key, &tmp); if (rv != CKR_OK) { goto out; } tmp.pValue = malloc(tmp.ulValueLen); if (tmp.pValue == NULL) { rv = CKR_HOST_MEMORY; goto out; } rv = kernel_get_attribute(base_key, &tmp); if (rv != CKR_OK) { free(tmp.pValue); goto out; } attrs[0].oa_type = tmp.type; attrs[0].oa_value = tmp.pValue; attrs[0].oa_value_len = tmp.ulValueLen; tmp.type = CKA_PRIME; tmp.pValue = NULL; /* get size of attribute */ rv = kernel_get_attribute(base_key, &tmp); if (rv != CKR_OK) { goto out; } tmp.pValue = malloc(tmp.ulValueLen); if (tmp.pValue == NULL) { rv = CKR_HOST_MEMORY; goto out; } rv = kernel_get_attribute(base_key, &tmp); if (rv != CKR_OK) { free(tmp.pValue); goto out; } attrs[1].oa_type = tmp.type; attrs[1].oa_value = tmp.pValue; attrs[1].oa_value_len = tmp.ulValueLen; big = OBJ_PRI_DH_VALUE(base_key); tmp.type = CKA_VALUE; tmp.ulValueLen = big->big_value_len; tmp.pValue = malloc(tmp.ulValueLen); if (tmp.pValue == NULL) { rv = CKR_HOST_MEMORY; goto out; } rv = kernel_get_attribute(base_key, &tmp); if (rv != CKR_OK) { free(tmp.pValue); goto out; } attrs[2].oa_type = tmp.type; attrs[2].oa_value = tmp.pValue; attrs[2].oa_value_len = tmp.ulValueLen; key_by_value->ck_attrs = attrs; key_by_value->ck_count = 3; break; default: rv = CKR_ATTRIBUTE_TYPE_INVALID; goto out; } (void) pthread_mutex_unlock(&base_key->object_mutex); return (CKR_OK); out: (void) pthread_mutex_unlock(&base_key->object_mutex); if (attrs != NULL) { for (i = 0; i < count; i++) { if (attrs[i].oa_value != NULL) free(attrs[i].oa_value); } free(attrs); } return (rv); } CK_RV derive_key_by_value(CK_MECHANISM_PTR pMechanism, CK_ATTRIBUTE_PTR pTemplate, CK_ULONG ulAttributeCount, kernel_session_t *session_p, crypto_mech_type_t k_mech_type, kernel_object_t *basekey_p, kernel_object_t *new_objp) { crypto_nostore_derive_key_t obj_ndk; char *key_buf = NULL; CK_ATTRIBUTE_PTR newTemplate = NULL; CK_BBOOL is_token_obj = FALSE; CK_RV rv = CKR_OK; CK_ULONG secret_class = CKO_SECRET_KEY; ulong_t key_len = 0; uint_t attr_count = 0; boolean_t removed; boolean_t has_class; int r, n; obj_ndk.ndk_in_count = 0; obj_ndk.ndk_out_count = 0; obj_ndk.ndk_base_key.ck_count = 0; rv = get_key_len_from_template(pMechanism, pTemplate, ulAttributeCount, basekey_p, &key_len); if (rv != CKR_OK) { goto failed_exit; } if ((key_buf = malloc(key_len)) == NULL) { rv = CKR_HOST_MEMORY; goto failed_exit; } has_class = attribute_in_template(CKA_CLASS, pTemplate, ulAttributeCount); attr_count = ulAttributeCount + 1; if (!has_class) attr_count++; newTemplate = grow_template(pTemplate, ulAttributeCount, attr_count); if (newTemplate == NULL) { rv = CKR_HOST_MEMORY; goto failed_exit; } n = ulAttributeCount; if (!has_class) { newTemplate[n].type = CKA_CLASS; newTemplate[n].pValue = (caddr_t)&secret_class; newTemplate[n].ulValueLen = sizeof (secret_class); n++; } /* Add CKA_VALUE to the template */ newTemplate[n].type = CKA_VALUE; newTemplate[n].pValue = (caddr_t)key_buf; newTemplate[n].ulValueLen = key_len; rv = process_object_attributes(newTemplate, attr_count - 1, &obj_ndk.ndk_in_attributes, &is_token_obj); if (rv != CKR_OK) { goto failed_exit; } obj_ndk.ndk_in_count = attr_count - 1; rv = process_object_attributes(&newTemplate[attr_count - 1], 1, &obj_ndk.ndk_out_attributes, &is_token_obj); if (rv != CKR_OK) { goto failed_exit; } obj_ndk.ndk_out_count = 1; /* Cannot create a token object with a READ-ONLY session. */ if (is_token_obj && session_p->ses_RO) { rv = CKR_SESSION_READ_ONLY; goto failed_exit; } obj_ndk.ndk_session = session_p->k_session; obj_ndk.ndk_mechanism.cm_type = k_mech_type; obj_ndk.ndk_mechanism.cm_param = pMechanism->pParameter; obj_ndk.ndk_mechanism.cm_param_len = pMechanism->ulParameterLen; /* * Obtain the attributes of base key and pass them by value. */ rv = get_base_key_attributes(basekey_p, &obj_ndk.ndk_base_key); if (rv != CKR_OK) { goto failed_exit; } obj_ndk.ndk_base_key.ck_format = CRYPTO_KEY_ATTR_LIST; while ((r = ioctl(kernel_fd, CRYPTO_NOSTORE_DERIVE_KEY, &obj_ndk)) < 0) { if (errno != EINTR) break; } if (r < 0) { rv = CKR_FUNCTION_FAILED; } else { rv = crypto2pkcs11_error_number(obj_ndk.ndk_return_value); } free_attributes(obj_ndk.ndk_in_attributes, &obj_ndk.ndk_in_count); free_attributes((caddr_t)obj_ndk.ndk_base_key.ck_attrs, &obj_ndk.ndk_base_key.ck_count); if (rv != CKR_OK) { goto failed_exit; } rv = get_object_attributes(&newTemplate[attr_count - 1], 1, obj_ndk.ndk_out_attributes); free_attributes(obj_ndk.ndk_out_attributes, &obj_ndk.ndk_out_count); if (rv != CRYPTO_SUCCESS) { goto failed_exit; } removed = remove_one_attribute(newTemplate, CKA_VALUE_LEN, attr_count, B_FALSE); rv = kernel_build_object(newTemplate, removed ? attr_count - 1 : attr_count, new_objp, session_p, KERNEL_GEN_KEY); if (rv != CRYPTO_SUCCESS) { goto failed_exit; } free(key_buf); free(newTemplate); new_objp->is_lib_obj = B_TRUE; new_objp->session_handle = (CK_SESSION_HANDLE)session_p; return (CKR_OK); failed_exit: if (key_buf != NULL) free(key_buf); if (newTemplate != NULL) free(newTemplate); free_attributes(obj_ndk.ndk_in_attributes, &obj_ndk.ndk_in_count); free_attributes(obj_ndk.ndk_out_attributes, &obj_ndk.ndk_out_count); free_attributes((caddr_t)obj_ndk.ndk_base_key.ck_attrs, &obj_ndk.ndk_base_key.ck_count); return (rv); } CK_RV C_DeriveKey(CK_SESSION_HANDLE hSession, CK_MECHANISM_PTR pMechanism, CK_OBJECT_HANDLE hBaseKey, CK_ATTRIBUTE_PTR pTemplate, CK_ULONG ulAttributeCount, CK_OBJECT_HANDLE_PTR phKey) { CK_RV rv = CKR_OK; kernel_session_t *session_p; kernel_object_t *basekey_p; kernel_object_t *new_objp; kernel_slot_t *pslot; boolean_t ses_lock_held = B_FALSE; CK_BBOOL is_pri_obj; CK_BBOOL is_token_obj = FALSE; crypto_mech_type_t k_mech_type; int r; if (!kernel_initialized) return (CKR_CRYPTOKI_NOT_INITIALIZED); /* Obtain the session pointer. */ rv = handle2session(hSession, &session_p); if (rv != CKR_OK) return (rv); if (pMechanism == NULL) { REFRELE(session_p, ses_lock_held); return (CKR_ARGUMENTS_BAD); } if ((pTemplate == NULL && ulAttributeCount != 0) || (pTemplate != NULL && ulAttributeCount == 0)) { REFRELE(session_p, ses_lock_held); return (CKR_ARGUMENTS_BAD); } /* Obtain the base key object pointer. */ HANDLE2OBJECT(hBaseKey, basekey_p, rv); if (rv != CKR_OK) { REFRELE(session_p, ses_lock_held); return (rv); } /* Get the kernel's internal mechanism number. */ rv = kernel_mech(pMechanism->mechanism, &k_mech_type); if (rv != CKR_OK) { goto failed_exit; } /* Create an object wrapper in the library for the generated key. */ new_objp = calloc(1, sizeof (kernel_object_t)); if (new_objp == NULL) { rv = CKR_HOST_MEMORY; goto failed_exit; } /* * Special Case: if token does not support object creation, * but does support key derivation by value, then create a session * object and initialize with values returned by token. */ pslot = slot_table[session_p->ses_slotid]; if (!pslot->sl_func_list.fl_object_create) { rv = derive_key_by_value(pMechanism, pTemplate, ulAttributeCount, session_p, k_mech_type, basekey_p, new_objp); if (rv != CKR_OK) goto failed_exit; } else { crypto_derive_key_t obj_dk; rv = process_object_attributes(pTemplate, ulAttributeCount, &obj_dk.dk_attributes, &is_token_obj); if (rv != CKR_OK) { goto failed_exit; } /* Cannot create a token object with a READ-ONLY session. */ if (is_token_obj && session_p->ses_RO) { free_object_attributes(obj_dk.dk_attributes, ulAttributeCount); rv = CKR_SESSION_READ_ONLY; goto failed_exit; } obj_dk.dk_session = session_p->k_session; obj_dk.dk_mechanism.cm_type = k_mech_type; obj_dk.dk_mechanism.cm_param = pMechanism->pParameter; obj_dk.dk_mechanism.cm_param_len = pMechanism->ulParameterLen; obj_dk.dk_base_key.ck_format = CRYPTO_KEY_REFERENCE; obj_dk.dk_base_key.ck_obj_id = basekey_p->k_handle; obj_dk.dk_count = ulAttributeCount; while ((r = ioctl(kernel_fd, CRYPTO_DERIVE_KEY, &obj_dk)) < 0) { if (errno != EINTR) break; } if (r < 0) { rv = CKR_FUNCTION_FAILED; } else { rv = crypto2pkcs11_error_number(obj_dk.dk_return_value); } free_object_attributes(obj_dk.dk_attributes, ulAttributeCount); if (rv != CKR_OK) { goto failed_exit; } /* Get the CKA_PRIVATE value for the derived key. */ rv = get_cka_private_value(session_p, obj_dk.dk_object_handle, &is_pri_obj); if (rv != CKR_OK) { goto failed_exit; } /* * Store the kernel object handle into the new derived key * object and finish the object initialization. */ new_objp->is_lib_obj = B_FALSE; new_objp->k_handle = obj_dk.dk_object_handle; new_objp->session_handle = (CK_SESSION_HANDLE)session_p; new_objp->extra_attrlistp = NULL; if (is_pri_obj) new_objp->bool_attr_mask |= PRIVATE_BOOL_ON; else new_objp->bool_attr_mask &= ~PRIVATE_BOOL_ON; if (is_token_obj) new_objp->bool_attr_mask |= TOKEN_BOOL_ON; else new_objp->bool_attr_mask &= ~TOKEN_BOOL_ON; } (void) pthread_mutex_init(&new_objp->object_mutex, NULL); new_objp->magic_marker = KERNELTOKEN_OBJECT_MAGIC; /* * Add the new derived object to the slot's token list if it is a * token object. Otherwise, add it to the session's object list. */ if (is_token_obj) { pslot = slot_table[session_p->ses_slotid]; kernel_add_token_object_to_slot(new_objp, pslot); } else { kernel_add_object_to_session(new_objp, session_p); } *phKey = (CK_OBJECT_HANDLE)new_objp; OBJ_REFRELE(basekey_p); REFRELE(session_p, ses_lock_held); return (rv); failed_exit: OBJ_REFRELE(basekey_p); if (new_objp != NULL) { (void) free(new_objp); } REFRELE(session_p, ses_lock_held); return (rv); } /* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License, Version 1.0 only * (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 2005 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ #include #include #include #include #include "kernelGlobal.h" #include "kernelObject.h" #include "kernelSession.h" #include #include #include CK_RV C_CreateObject(CK_SESSION_HANDLE hSession, CK_ATTRIBUTE_PTR pTemplate, CK_ULONG ulCount, CK_OBJECT_HANDLE_PTR phObject) { CK_RV rv; kernel_session_t *session_p; boolean_t ses_lock_held = B_FALSE; if (!kernel_initialized) return (CKR_CRYPTOKI_NOT_INITIALIZED); if ((pTemplate == NULL) || (ulCount == 0) || (phObject == NULL)) { return (CKR_ARGUMENTS_BAD); } /* * Obtain the session pointer. Also, increment the session * reference count. */ rv = handle2session(hSession, &session_p); if (rv != CKR_OK) return (rv); /* Create a new object. */ rv = kernel_add_object(pTemplate, ulCount, phObject, session_p); /* * Decrement the session reference count. * We do not hold the session lock. */ REFRELE(session_p, ses_lock_held); return (rv); } CK_RV C_CopyObject(CK_SESSION_HANDLE hSession, CK_OBJECT_HANDLE hObject, CK_ATTRIBUTE_PTR pTemplate, CK_ULONG ulCount, CK_OBJECT_HANDLE_PTR phNewObject) { CK_RV rv; kernel_session_t *session_p; boolean_t ses_lock_held = B_FALSE; kernel_object_t *old_object; kernel_object_t *new_object = NULL; crypto_object_copy_t object_copy; CK_BBOOL is_pri_obj = FALSE; CK_BBOOL is_token_obj = FALSE; kernel_slot_t *pslot; int i, r; if (!kernel_initialized) return (CKR_CRYPTOKI_NOT_INITIALIZED); /* Check arguments */ if (((ulCount > 0) && (pTemplate == NULL)) || (phNewObject == NULL)) { return (CKR_ARGUMENTS_BAD); } /* * Obtain the session pointer. Also, increment the session * reference count. */ rv = handle2session(hSession, &session_p); if (rv != CKR_OK) return (rv); /* Obtain the object pointer. */ HANDLE2OBJECT(hObject, old_object, rv); if (rv != CKR_OK) { /* * Decrement the session reference count. * We do not hold the session lock. */ REFRELE(session_p, ses_lock_held); return (rv); } (void) pthread_mutex_lock(&old_object->object_mutex); if (old_object->is_lib_obj) { /* * Copy the old object to a new object. * The 3rd argument with TRUE value indicates that * everything in the object will be duplicated. */ rv = kernel_copy_object(old_object, &new_object, B_TRUE, session_p); (void) pthread_mutex_unlock(&old_object->object_mutex); if ((rv != CKR_OK) || (new_object == NULL)) { /* * Most likely we ran out of space. * Decrement the session reference count. * We do not hold the session lock. */ OBJ_REFRELE(old_object); REFRELE(session_p, ses_lock_held); return (rv); } new_object->is_lib_obj = B_TRUE; /* Modify the object attribute if requested */ for (i = 0; i < ulCount; i++) { /* Set the requested attribute into the new object. */ rv = kernel_set_attribute(new_object, &pTemplate[i], B_TRUE, session_p); if (rv != CKR_OK) { kernel_cleanup_object(new_object); /* * Decrement the session reference count. * We do not hold the session lock. */ OBJ_REFRELE(old_object); REFRELE(session_p, ses_lock_held); return (rv); } } /* Insert the new object into this session's object list. */ kernel_add_object_to_session(new_object, session_p); /* * Decrement the session reference count. * We do not hold the session lock. */ OBJ_REFRELE(old_object); REFRELE(session_p, ses_lock_held); /* set handle of the new object */ *phNewObject = (CK_ULONG)new_object; } else { /* * The old object was created in the HW provider. * First, create an object wrapper in library. */ new_object = calloc(1, sizeof (kernel_object_t)); if (new_object == NULL) { (void) pthread_mutex_unlock(&old_object->object_mutex); OBJ_REFRELE(old_object); REFRELE(session_p, ses_lock_held); return (CKR_HOST_MEMORY); } /* Call CRYPTO_OBJECT_COPY ioctl to get a new object. */ object_copy.oc_session = session_p->k_session; object_copy.oc_handle = old_object->k_handle; (void) pthread_mutex_unlock(&old_object->object_mutex); object_copy.oc_count = ulCount; object_copy.oc_new_attributes = NULL; if (ulCount > 0) { rv = process_object_attributes(pTemplate, ulCount, &object_copy.oc_new_attributes, &is_token_obj); if (rv != CKR_OK) { goto failed_cleanup; } } while ((r = ioctl(kernel_fd, CRYPTO_OBJECT_COPY, &object_copy)) < 0) { if (errno != EINTR) break; } if (r < 0) { rv = CKR_FUNCTION_FAILED; } else { rv = crypto2pkcs11_error_number( object_copy.oc_return_value); } /* Free the attributes' space allocated for ioctl */ free_object_attributes(object_copy.oc_new_attributes, ulCount); if (rv != CKR_OK) { goto failed_cleanup; } /* * Store the kernel object handle in the object wrapper and * get the CKA_PRIVATE value of the new object. */ new_object->k_handle = object_copy.oc_new_handle; rv = get_cka_private_value(session_p, new_object->k_handle, &is_pri_obj); if (rv != CKR_OK) { goto failed_cleanup; } /* * Initialize other field of the object wrapper. */ new_object->is_lib_obj = B_FALSE; new_object->magic_marker = KERNELTOKEN_OBJECT_MAGIC; new_object->session_handle = (CK_SESSION_HANDLE)session_p; (void) pthread_mutex_init(&new_object->object_mutex, NULL); if (is_pri_obj) new_object->bool_attr_mask |= PRIVATE_BOOL_ON; else new_object->bool_attr_mask &= ~PRIVATE_BOOL_ON; if (is_token_obj) new_object->bool_attr_mask |= TOKEN_BOOL_ON; else new_object->bool_attr_mask &= ~TOKEN_BOOL_ON; /* * Add the new copied object into the slot's token list * or the session list. We don't hold the slot lock. */ if (is_token_obj) { pslot = slot_table[session_p->ses_slotid]; /* * Decrement the session reference count. * We do not hold the session lock. */ OBJ_REFRELE(old_object); REFRELE(session_p, ses_lock_held); /* Add into the slot token object list. */ kernel_add_token_object_to_slot(new_object, pslot); } else { kernel_add_object_to_session(new_object, session_p); /* * Decrement the session reference count. * We do not hold the session lock. */ OBJ_REFRELE(old_object); REFRELE(session_p, ses_lock_held); } /* set handle of the new object */ *phNewObject = (CK_ULONG)new_object; } return (rv); failed_cleanup: if (new_object != NULL) { (void) free(new_object); } OBJ_REFRELE(old_object); REFRELE(session_p, ses_lock_held); return (rv); } CK_RV C_DestroyObject(CK_SESSION_HANDLE hSession, CK_OBJECT_HANDLE hObject) { CK_RV rv; kernel_object_t *object_p; kernel_session_t *session_p = (kernel_session_t *)(hSession); kernel_slot_t *pslot; boolean_t ses_lock_held = B_FALSE; CK_SESSION_HANDLE creating_session; if (!kernel_initialized) return (CKR_CRYPTOKI_NOT_INITIALIZED); /* * The reason that we don't call handle2session is because * the argument hSession may not be the creating_session of * the object to be destroyed, and we want to avoid the lock * contention. The handle2session will be called later for * the creating_session. */ if ((session_p == NULL) || (session_p->magic_marker != KERNELTOKEN_SESSION_MAGIC)) { return (CKR_SESSION_HANDLE_INVALID); } /* Obtain the object pointer without incrementing reference count. */ HANDLE2OBJECT_DESTROY(hObject, object_p, rv); if (rv != CKR_OK) { return (rv); } /* Only session objects can be destroyed at a read-only session. */ if ((session_p->ses_RO) && (object_p->bool_attr_mask & TOKEN_BOOL_ON)) { return (CKR_SESSION_READ_ONLY); } /* * If the object is a session object, obtain the session handle * which object belongs to. For a token object, we will use the * session handle from the caller, because the session used to * create the token object may no longer exist. */ if (!(object_p->bool_attr_mask & TOKEN_BOOL_ON)) creating_session = object_p->session_handle; else creating_session = hSession; rv = handle2session(creating_session, &session_p); if (rv != CKR_OK) { return (rv); } /* * Set OBJECT_IS_DELETING flag so any access to this * object will be rejected. */ (void) pthread_mutex_lock(&object_p->object_mutex); if (object_p->obj_delete_sync & OBJECT_IS_DELETING) { (void) pthread_mutex_unlock(&object_p->object_mutex); REFRELE(session_p, ses_lock_held); return (CKR_OBJECT_HANDLE_INVALID); } object_p->obj_delete_sync |= OBJECT_IS_DELETING; (void) pthread_mutex_unlock(&object_p->object_mutex); if (object_p->bool_attr_mask & TOKEN_BOOL_ON) { /* * The first FALSE boolean argument indicates that the caller * does not hold the slot lock. The second FALSE boolean * argument indicates that the caller wants to clean up the * object in the HW provider also. */ pslot = slot_table[session_p->ses_slotid]; rv = kernel_delete_token_object(pslot, session_p, object_p, B_FALSE, B_FALSE); } else { /* * The first FALSE boolean argument indicates that the caller * does not hold the session lock. The second FALSE boolean * argument indicates that the caller wants to clean the object * in the HW provider also. */ rv = kernel_delete_session_object(session_p, object_p, B_FALSE, B_FALSE); } /* * Decrement the session reference count. * We do not hold the session lock. */ REFRELE(session_p, ses_lock_held); return (rv); } CK_RV C_GetAttributeValue(CK_SESSION_HANDLE hSession, CK_OBJECT_HANDLE hObject, CK_ATTRIBUTE_PTR pTemplate, CK_ULONG ulCount) { CK_RV rv = CKR_OK, rv1 = CKR_OK; kernel_object_t *object_p; kernel_session_t *session_p; boolean_t ses_lock_held = B_FALSE; crypto_object_get_attribute_value_t obj_get_attr; int i, r; if (!kernel_initialized) return (CKR_CRYPTOKI_NOT_INITIALIZED); if ((pTemplate == NULL) || (ulCount == 0)) return (CKR_ARGUMENTS_BAD); /* * Obtain the session pointer. Also, increment the session * reference count. */ rv = handle2session(hSession, &session_p); if (rv != CKR_OK) return (rv); /* Obtain the object pointer. */ HANDLE2OBJECT(hObject, object_p, rv); if (rv != CKR_OK) { /* * Decrement the session reference count. * We do not hold the session lock. */ REFRELE(session_p, ses_lock_held); return (rv); } /* Acquire the lock on the object. */ (void) pthread_mutex_lock(&object_p->object_mutex); if (object_p->is_lib_obj) { /* * The object was created in the library. The library * contains the value information of each attribute. */ for (i = 0; i < ulCount; i++) { /* * Get the value of each attribute in the template. * (We must process EVERY attribute in the template.) */ rv = kernel_get_attribute(object_p, &pTemplate[i]); if (rv != CKR_OK) /* At least we catch some type of error. */ rv1 = rv; } rv = rv1; (void) pthread_mutex_unlock(&object_p->object_mutex); } else { /* * The object was created in HW provider, call ioctl to get * the values of attributes. */ obj_get_attr.og_session = session_p->k_session; obj_get_attr.og_handle = object_p->k_handle; (void) pthread_mutex_unlock(&object_p->object_mutex); obj_get_attr.og_count = ulCount; rv = process_object_attributes(pTemplate, ulCount, &obj_get_attr.og_attributes, NULL); if (rv != CKR_OK) { goto clean_exit; } while ((r = ioctl(kernel_fd, CRYPTO_OBJECT_GET_ATTRIBUTE_VALUE, &obj_get_attr)) < 0) { if (errno != EINTR) break; } if (r < 0) { rv = CKR_FUNCTION_FAILED; } else { rv = crypto2pkcs11_error_number( obj_get_attr.og_return_value); } /* * The error codes CKR_ATTRIBUTE_SENSITIVE, * CKR_ATTRIBUTE_TYPE_INVALID, and CKR_BUFFER_TOO_SMALL * do not denote true errors for this function. If a call * returns any of these three values, then the call must * nonetheless have processed every attribute in the * template. Every attribute in the template whose value * can be returned will be returned. */ if ((rv == CKR_OK) || (rv == CKR_ATTRIBUTE_SENSITIVE) || (rv == CKR_ATTRIBUTE_TYPE_INVALID) || (rv == CKR_BUFFER_TOO_SMALL)) { rv1 = get_object_attributes(pTemplate, ulCount, obj_get_attr.og_attributes); if (rv1 != CKR_OK) { rv = rv1; } } /* Free the attributes' allocated for the ioctl call. */ free_object_attributes(obj_get_attr.og_attributes, ulCount); } clean_exit: /* * Decrement the session reference count. * We do not hold the session lock. */ OBJ_REFRELE(object_p); REFRELE(session_p, ses_lock_held); return (rv); } CK_RV C_SetAttributeValue(CK_SESSION_HANDLE hSession, CK_OBJECT_HANDLE hObject, CK_ATTRIBUTE_PTR pTemplate, CK_ULONG ulCount) { CK_RV rv = CKR_OK; kernel_object_t *object_p; kernel_object_t *new_object = NULL; kernel_session_t *session_p; boolean_t ses_lock_held = B_FALSE; crypto_object_set_attribute_value_t obj_set_attr; int i, r; if (!kernel_initialized) return (CKR_CRYPTOKI_NOT_INITIALIZED); if ((pTemplate == NULL) || (ulCount == 0)) return (CKR_ARGUMENTS_BAD); /* * Obtain the session pointer. Also, increment the session * reference count. */ rv = handle2session(hSession, &session_p); if (rv != CKR_OK) return (rv); /* Obtain the object pointer. */ HANDLE2OBJECT(hObject, object_p, rv); if (rv != CKR_OK) { /* * Decrement the session reference count. * We do not hold the session lock. */ REFRELE(session_p, ses_lock_held); return (rv); } /* lock the object */ (void) pthread_mutex_lock(&object_p->object_mutex); /* * If the object was created in the HW provider, changing its * attributes' values need to be done in the provider too. */ if (!object_p->is_lib_obj) { /* Cannot modify a token object with a READ-ONLY session */ if (session_p->ses_RO && (object_p->bool_attr_mask & TOKEN_BOOL_ON)) { (void) pthread_mutex_unlock(&object_p->object_mutex); rv = CKR_SESSION_READ_ONLY; goto clean_exit; } obj_set_attr.sa_session = session_p->k_session; obj_set_attr.sa_handle = object_p->k_handle; (void) pthread_mutex_unlock(&object_p->object_mutex); obj_set_attr.sa_count = ulCount; rv = process_object_attributes(pTemplate, ulCount, &obj_set_attr.sa_attributes, NULL); if (rv != CKR_OK) { goto clean_exit; } while ((r = ioctl(kernel_fd, CRYPTO_OBJECT_SET_ATTRIBUTE_VALUE, &obj_set_attr)) < 0) { if (errno != EINTR) break; } if (r < 0) { rv = CKR_FUNCTION_FAILED; } else { rv = crypto2pkcs11_error_number( obj_set_attr.sa_return_value); } /* Free the attributes' space allocated for the ioctl call. */ free_object_attributes(obj_set_attr.sa_attributes, ulCount); goto clean_exit; } /* * if we come here, the object must have been created in the * library. The work will be done completely in the library. * * Copy the old object to a new object. We work on the copied * version because in case of error we still keep the old one * intact. */ rv = kernel_copy_object(object_p, &new_object, B_FALSE, NULL); (void) pthread_mutex_unlock(&object_p->object_mutex); if ((rv != CKR_OK) || (new_object == NULL)) { /* * Most likely we ran out of space. * Decrement the session reference count. * We do not hold the session lock. */ goto clean_exit; } for (i = 0; i < ulCount; i++) { /* Set the requested attribute into the new object. */ rv = kernel_set_attribute(new_object, &pTemplate[i], B_FALSE, session_p); if (rv != CKR_OK) { kernel_cleanup_object(new_object); goto clean_exit; } } /* * We've successfully set all the requested attributes. * Merge the new object with the old object, then destory * the new one. The reason to do the merging is because we * have to keep the original object handle (address of object). */ (void) pthread_mutex_lock(&object_p->object_mutex); kernel_merge_object(object_p, new_object); (void) pthread_mutex_unlock(&object_p->object_mutex); clean_exit: if (new_object != NULL) (void) free(new_object); /* * Decrement the session reference count. * We do not hold the session lock. */ OBJ_REFRELE(object_p); REFRELE(session_p, ses_lock_held); return (rv); } CK_RV C_GetObjectSize(CK_SESSION_HANDLE hSession, CK_OBJECT_HANDLE hObject, CK_ULONG_PTR pulSize) { CK_RV rv = CKR_OK; kernel_object_t *object_p; kernel_session_t *session_p; boolean_t ses_lock_held = B_FALSE; crypto_object_get_size_t obj_gs; int r; if (!kernel_initialized) return (CKR_CRYPTOKI_NOT_INITIALIZED); /* Check if pulSize is valid */ if (pulSize == NULL) { return (CKR_ARGUMENTS_BAD); } /* * Obtain the session pointer. Also, increment the session * reference count. */ rv = handle2session(hSession, &session_p); if (rv != CKR_OK) return (rv); /* Obtain the object pointer. */ HANDLE2OBJECT(hObject, object_p, rv); if (rv != CKR_OK) { /* * Decrement the session reference count. * We do not hold the session lock. */ REFRELE(session_p, ses_lock_held); return (rv); } /* Acquire the lock on the object. */ (void) pthread_mutex_lock(&object_p->object_mutex); if (!object_p->is_lib_obj) { /* * The object was created in HW provider, call the * CRYPTO_OBJECT_GET_SIZE ioctl. */ obj_gs.gs_session = session_p->k_session; obj_gs.gs_handle = object_p->k_handle; (void) pthread_mutex_unlock(&object_p->object_mutex); while ((r = ioctl(kernel_fd, CRYPTO_OBJECT_GET_SIZE, &obj_gs)) < 0) { if (errno != EINTR) break; } if (r < 0) { rv = CKR_FUNCTION_FAILED; } else { rv = crypto2pkcs11_error_number( obj_gs.gs_return_value); } if (rv == CKR_OK) { *pulSize = obj_gs.gs_size; } } else { rv = kernel_get_object_size(object_p, pulSize); (void) pthread_mutex_unlock(&object_p->object_mutex); } /* * Decrement the session reference count. * We do not hold the session lock. */ OBJ_REFRELE(object_p); REFRELE(session_p, ses_lock_held); return (rv); } CK_RV C_FindObjectsInit(CK_SESSION_HANDLE sh, CK_ATTRIBUTE_PTR pTemplate, CK_ULONG ulCount) { CK_RV rv; kernel_session_t *session_p; boolean_t ses_lock_held = B_FALSE; kernel_slot_t *pslot; crypto_object_find_init_t obj_fi; int r; if (!kernel_initialized) return (CKR_CRYPTOKI_NOT_INITIALIZED); /* Check the arguments */ if ((ulCount > 0) && (pTemplate == NULL)) { return (CKR_ARGUMENTS_BAD); } /* * Obtain the session pointer. Also, increment the session * reference count. */ rv = handle2session(sh, &session_p); if (rv != CKR_OK) return (rv); /* Acquire the session lock */ (void) pthread_mutex_lock(&session_p->session_mutex); ses_lock_held = B_TRUE; /* Check to see if find operation is already active */ if (session_p->find_objects.flags & CRYPTO_OPERATION_ACTIVE) { /* decrement the session count, and unlock the mutex */ REFRELE(session_p, ses_lock_held); return (CKR_OPERATION_ACTIVE); } else { /* * This active flag will remain ON until application calls * C_FindObjectsFinal. */ session_p->find_objects.flags = CRYPTO_OPERATION_ACTIVE; } /* * If the HW provider supports object creation, we call the * CRYPTO_OBJECT_FIND_INIT ioctl to initialize object finding. * Otherwise, all the objects are created in the library and we * do the find objects solely in the library. */ pslot = slot_table[session_p->ses_slotid]; if (pslot->sl_func_list.fl_object_create) { obj_fi.fi_session = session_p->k_session; (void) pthread_mutex_unlock(&session_p->session_mutex); ses_lock_held = B_FALSE; obj_fi.fi_count = ulCount; rv = process_object_attributes(pTemplate, ulCount, &obj_fi.fi_attributes, NULL); if (rv == CKR_OK) { while ((r = ioctl(kernel_fd, CRYPTO_OBJECT_FIND_INIT, &obj_fi)) < 0) { if (errno != EINTR) break; } if (r < 0) { rv = CKR_FUNCTION_FAILED; } else { rv = crypto2pkcs11_error_number( obj_fi.fi_return_value); } } /* Free the attributes' space allocated for the ioctl call. */ free_object_attributes(obj_fi.fi_attributes, ulCount); } else { (void) pthread_mutex_unlock(&session_p->session_mutex); ses_lock_held = B_FALSE; rv = kernel_find_objects_init(session_p, pTemplate, ulCount); } if (rv != CKR_OK) { (void) pthread_mutex_lock(&session_p->session_mutex); session_p->find_objects.flags = 0; (void) pthread_mutex_unlock(&session_p->session_mutex); } /* decrement the session count, and unlock the mutex */ REFRELE(session_p, ses_lock_held); return (rv); } CK_RV C_FindObjects(CK_SESSION_HANDLE sh, CK_OBJECT_HANDLE_PTR phObject, CK_ULONG ulMaxObjectCount, CK_ULONG_PTR pulObjectCount) { CK_RV rv = CKR_OK; kernel_slot_t *pslot; kernel_session_t *session_p; boolean_t ses_lock_held = B_FALSE; crypto_object_find_update_t obj_fu; int r; if (!kernel_initialized) return (CKR_CRYPTOKI_NOT_INITIALIZED); /* check for invalid arguments */ if (((phObject == NULL) && (ulMaxObjectCount != 0)) || (pulObjectCount == NULL)) { return (CKR_ARGUMENTS_BAD); } if (ulMaxObjectCount == 0) { /* don't need to do anything, just return */ *pulObjectCount = 0; return (CKR_OK); } /* * Obtain the session pointer. Also, increment the session * reference count. */ rv = handle2session(sh, &session_p); if (rv != CKR_OK) return (rv); /* Acquire the slot lock */ pslot = slot_table[session_p->ses_slotid]; (void) pthread_mutex_lock(&pslot->sl_mutex); /* Acquire the session lock */ (void) pthread_mutex_lock(&session_p->session_mutex); ses_lock_held = B_TRUE; /* Check to see if find operation is active */ if (!(session_p->find_objects.flags & CRYPTO_OPERATION_ACTIVE)) { rv = CKR_OPERATION_NOT_INITIALIZED; goto clean_exit; } /* * Similar to C_FindObjectInit(), if the HW provider supports object * creation, we call the respective ioctl to find objects. * Otherwise, all the objects are created in the library and we do * the find objects solely in the library. */ if (pslot->sl_func_list.fl_object_create) { obj_fu.fu_session = session_p->k_session; obj_fu.fu_max_count = ulMaxObjectCount; obj_fu.fu_handles = (char *)calloc(1, ulMaxObjectCount * sizeof (crypto_object_id_t)); if (obj_fu.fu_handles == NULL) { rv = CKR_HOST_MEMORY; goto clean_exit; } while ((r = ioctl(kernel_fd, CRYPTO_OBJECT_FIND_UPDATE, &obj_fu)) < 0) { if (errno != EINTR) break; } if (r < 0) { rv = CKR_FUNCTION_FAILED; } else { rv = crypto2pkcs11_error_number( obj_fu.fu_return_value); } if (rv == CKR_OK) { rv = process_found_objects(session_p, phObject, pulObjectCount, obj_fu); } free(obj_fu.fu_handles); } else { kernel_find_objects(session_p, phObject, ulMaxObjectCount, pulObjectCount); rv = CKR_OK; } clean_exit: /* decrement the session count, and release the session lock */ REFRELE(session_p, ses_lock_held); /* release the slot lock */ (void) pthread_mutex_unlock(&pslot->sl_mutex); return (rv); } CK_RV C_FindObjectsFinal(CK_SESSION_HANDLE sh) { kernel_session_t *session_p; CK_RV rv; boolean_t ses_lock_held = B_FALSE; kernel_slot_t *pslot; crypto_object_find_final_t obj_ff; int r; if (!kernel_initialized) return (CKR_CRYPTOKI_NOT_INITIALIZED); /* * Obtain the session pointer. Also, increment the session * reference count. */ rv = handle2session(sh, &session_p); if (rv != CKR_OK) return (rv); /* Acquire the session lock */ (void) pthread_mutex_lock(&session_p->session_mutex); ses_lock_held = B_TRUE; /* Check to see if find operation is active */ if (!(session_p->find_objects.flags & CRYPTO_OPERATION_ACTIVE)) { REFRELE(session_p, ses_lock_held); return (CKR_OPERATION_NOT_INITIALIZED); } /* * Similar to C_FindObjectInit(), if the HW provider supports object * creation, we need to call the CRYPTO_OBJECT_FIND_FINAL ioctl. */ pslot = slot_table[session_p->ses_slotid]; if (pslot->sl_func_list.fl_object_create) { obj_ff.ff_session = session_p->k_session; while ((r = ioctl(kernel_fd, CRYPTO_OBJECT_FIND_FINAL, &obj_ff)) < 0) { if (errno != EINTR) break; } if (r < 0) { rv = CKR_FUNCTION_FAILED; } else { rv = crypto2pkcs11_error_number( obj_ff.ff_return_value); } /* only need to reset find_objects.flags */ if (rv == CKR_OK) { session_p->find_objects.flags = 0; } } else { /* * The find object operations were done in the library, we * need to cleanup find_objects context. */ kernel_find_objects_final(session_p); rv = CKR_OK; } /* decrement the session count, and release the lock */ REFRELE(session_p, ses_lock_held); return (rv); } /* * 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. */ #ifndef _KERNELOBJECT_H #define _KERNELOBJECT_H #ifdef __cplusplus extern "C" { #endif #include #include "kernelSession.h" #include "kernelSlot.h" #define KERNELTOKEN_OBJECT_MAGIC 0xECF0B003 #define KERNEL_CREATE_OBJ 1 #define KERNEL_GEN_KEY 2 #define RSA_PRI_ATTR_COUNT 7 #define RSA_PUB_ATTR_COUNT 3 #define DSA_ATTR_COUNT 4 #define EC_ATTR_COUNT 2 /* * Secret key Struct */ typedef struct secret_key_obj { CK_BYTE *sk_value; CK_ULONG sk_value_len; } secret_key_obj_t; /* * This structure is used to hold the attributes in the * Extra Attribute List. */ typedef struct attribute_info { CK_ATTRIBUTE attr; struct attribute_info *next; } attribute_info_t; typedef attribute_info_t *CK_ATTRIBUTE_INFO_PTR; /* * biginteger Struct */ typedef struct biginteger { CK_BYTE *big_value; CK_ULONG big_value_len; } biginteger_t; /* * PKCS11: RSA Public Key Object Attributes */ typedef struct rsa_pub_key { biginteger_t modulus; CK_ULONG modulus_bits; biginteger_t pub_exponent; } rsa_pub_key_t; /* * PKCS11: DSA Public Key Object Attributes */ typedef struct dsa_pub_key { biginteger_t prime; biginteger_t subprime; biginteger_t base; biginteger_t value; } dsa_pub_key_t; /* * PKCS11: Diffie-Hellman Public Key Object Attributes */ typedef struct dh_pub_key { biginteger_t prime; biginteger_t base; biginteger_t value; } dh_pub_key_t; /* * PKCS11: EC Public Key Object Attributes */ typedef struct ec_pub_key { biginteger_t point; } ec_pub_key_t; /* * Public Key Main Struct */ typedef struct public_key_obj { union { rsa_pub_key_t rsa_pub_key; /* RSA public key */ dsa_pub_key_t dsa_pub_key; /* DSA public key */ dh_pub_key_t dh_pub_key; /* DH public key */ ec_pub_key_t ec_pub_key; /* EC public key */ } key_type_u; } public_key_obj_t; /* * PKCS11: RSA Private Key Object Attributes */ typedef struct rsa_pri_key { biginteger_t modulus; biginteger_t pub_exponent; biginteger_t pri_exponent; biginteger_t prime_1; biginteger_t prime_2; biginteger_t exponent_1; biginteger_t exponent_2; biginteger_t coefficient; } rsa_pri_key_t; /* * PKCS11: DSA Private Key Object Attributes */ typedef struct dsa_pri_key { biginteger_t prime; biginteger_t subprime; biginteger_t base; biginteger_t value; } dsa_pri_key_t; /* * PKCS11: Diffie-Hellman Private Key Object Attributes */ typedef struct dh_pri_key { biginteger_t prime; biginteger_t base; biginteger_t value; CK_ULONG value_bits; } dh_pri_key_t; /* * PKCS11: EC Private Key Object Attributes */ typedef struct ec_pri_key { biginteger_t value; } ec_pri_key_t; /* * Private Key Main Struct */ typedef struct private_key_obj { union { rsa_pri_key_t rsa_pri_key; /* RSA private key */ dsa_pri_key_t dsa_pri_key; /* DSA private key */ dh_pri_key_t dh_pri_key; /* DH private key */ ec_pri_key_t ec_pri_key; /* EC private key */ } key_type_u; } private_key_obj_t; /* * This is the main structure of the Objects. */ typedef struct object { boolean_t is_lib_obj; /* default is TRUE */ crypto_object_id_t k_handle; /* Generic common fields. Always present */ CK_OBJECT_CLASS class; CK_KEY_TYPE key_type; CK_ULONG magic_marker; uint64_t bool_attr_mask; CK_MECHANISM_TYPE mechanism; /* Fields for access and arbitration */ pthread_mutex_t object_mutex; struct object *next; struct object *prev; /* Extra non-boolean attribute list */ CK_ATTRIBUTE_INFO_PTR extra_attrlistp; CK_ULONG extra_attrcount; /* For each object, only one object class is presented */ union { secret_key_obj_t *secret_key; public_key_obj_t *public_key; private_key_obj_t *private_key; } object_class_u; /* Session handle that the object belongs to */ CK_SESSION_HANDLE session_handle; uint32_t obj_refcnt; /* object reference count */ pthread_cond_t obj_free_cond; /* cond variable for signal and wait */ uint32_t obj_delete_sync; /* object delete sync flags */ } kernel_object_t; typedef struct find_context { kernel_object_t **objs_found; CK_ULONG num_results; CK_ULONG next_result_index; /* next result object to return */ } find_context_t; /* * The following structure is used to link the to-be-freed session * objects into a linked list. The objects on this linked list have * not yet been freed via free() after C_DestroyObject() call; instead * they are added to this list. The actual free will take place when * the number of objects queued reaches MAX_OBJ_TO_BE_FREED, at which * time the first object in the list will be freed. */ #define MAX_OBJ_TO_BE_FREED 300 typedef struct obj_to_be_freed_list { kernel_object_t *first; /* points to first obj in the list */ kernel_object_t *last; /* points to last obj in the list */ uint32_t count; /* current total objs in the list */ pthread_mutex_t obj_to_be_free_mutex; } object_to_be_freed_list_t; extern object_to_be_freed_list_t obj_delay_freed; /* * The following definitions are the shortcuts */ /* * Secret Key Object Attributes */ #define OBJ_SEC(o) \ (o->object_class_u.secret_key) #define OBJ_SEC_VALUE(o) \ (o->object_class_u.secret_key->sk_value) #define OBJ_SEC_VALUE_LEN(o) \ (o->object_class_u.secret_key->sk_value_len) /* * RSA Public Key Object Attributes */ #define OBJ_PUB(o) \ ((o)->object_class_u.public_key) #define KEY_PUB_RSA(k) \ &((k)->key_type_u.rsa_pub_key) #define OBJ_PUB_RSA_MOD(o) \ &((o)->object_class_u.public_key->key_type_u.rsa_pub_key.modulus) #define KEY_PUB_RSA_MOD(k) \ &((k)->key_type_u.rsa_pub_key.modulus) #define OBJ_PUB_RSA_PUBEXPO(o) \ &((o)->object_class_u.public_key->key_type_u.rsa_pub_key.pub_exponent) #define KEY_PUB_RSA_PUBEXPO(k) \ &((k)->key_type_u.rsa_pub_key.pub_exponent) #define OBJ_PUB_RSA_MOD_BITS(o) \ ((o)->object_class_u.public_key->key_type_u.rsa_pub_key.modulus_bits) #define KEY_PUB_RSA_MOD_BITS(k) \ ((k)->key_type_u.rsa_pub_key.modulus_bits) /* * DSA Public Key Object Attributes */ #define KEY_PUB_DSA(k) \ &((k)->key_type_u.dsa_pub_key) #define OBJ_PUB_DSA_PRIME(o) \ &((o)->object_class_u.public_key->key_type_u.dsa_pub_key.prime) #define KEY_PUB_DSA_PRIME(k) \ &((k)->key_type_u.dsa_pub_key.prime) #define OBJ_PUB_DSA_SUBPRIME(o) \ &((o)->object_class_u.public_key->key_type_u.dsa_pub_key.subprime) #define KEY_PUB_DSA_SUBPRIME(k) \ &((k)->key_type_u.dsa_pub_key.subprime) #define OBJ_PUB_DSA_BASE(o) \ &((o)->object_class_u.public_key->key_type_u.dsa_pub_key.base) #define KEY_PUB_DSA_BASE(k) \ &((k)->key_type_u.dsa_pub_key.base) #define OBJ_PUB_DSA_VALUE(o) \ &((o)->object_class_u.public_key->key_type_u.dsa_pub_key.value) #define KEY_PUB_DSA_VALUE(k) \ &((k)->key_type_u.dsa_pub_key.value) /* * Diffie-Hellman Public Key Object Attributes */ #define KEY_PUB_DH(k) \ &((k)->key_type_u.dh_pub_key) #define OBJ_PUB_DH_PRIME(o) \ &((o)->object_class_u.public_key->key_type_u.dh_pub_key.prime) #define KEY_PUB_DH_PRIME(k) \ &((k)->key_type_u.dh_pub_key.prime) #define OBJ_PUB_DH_BASE(o) \ &((o)->object_class_u.public_key->key_type_u.dh_pub_key.base) #define KEY_PUB_DH_BASE(k) \ &((k)->key_type_u.dh_pub_key.base) #define OBJ_PUB_DH_VALUE(o) \ &((o)->object_class_u.public_key->key_type_u.dh_pub_key.value) #define KEY_PUB_DH_VALUE(k) \ &((k)->key_type_u.dh_pub_key.value) /* * EC Public Key Object Attributes */ #define OBJ_PUB_EC_POINT(o) \ &((o)->object_class_u.public_key->key_type_u.ec_pub_key.point) #define KEY_PUB_EC_POINT(k) \ &((k)->key_type_u.ec_pub_key.point) /* * RSA Private Key Object Attributes */ #define OBJ_PRI(o) \ ((o)->object_class_u.private_key) #define KEY_PRI_RSA(k) \ &((k)->key_type_u.rsa_pri_key) #define OBJ_PRI_RSA_MOD(o) \ &((o)->object_class_u.private_key->key_type_u.rsa_pri_key.modulus) #define KEY_PRI_RSA_MOD(k) \ &((k)->key_type_u.rsa_pri_key.modulus) #define OBJ_PRI_RSA_PUBEXPO(o) \ &((o)->object_class_u.private_key->key_type_u.rsa_pri_key.pub_exponent) #define KEY_PRI_RSA_PUBEXPO(k) \ &((k)->key_type_u.rsa_pri_key.pub_exponent) #define OBJ_PRI_RSA_PRIEXPO(o) \ &((o)->object_class_u.private_key->key_type_u.rsa_pri_key.pri_exponent) #define KEY_PRI_RSA_PRIEXPO(k) \ &((k)->key_type_u.rsa_pri_key.pri_exponent) #define OBJ_PRI_RSA_PRIME1(o) \ &((o)->object_class_u.private_key->key_type_u.rsa_pri_key.prime_1) #define KEY_PRI_RSA_PRIME1(k) \ &((k)->key_type_u.rsa_pri_key.prime_1) #define OBJ_PRI_RSA_PRIME2(o) \ &((o)->object_class_u.private_key->key_type_u.rsa_pri_key.prime_2) #define KEY_PRI_RSA_PRIME2(k) \ &((k)->key_type_u.rsa_pri_key.prime_2) #define OBJ_PRI_RSA_EXPO1(o) \ &((o)->object_class_u.private_key->key_type_u.rsa_pri_key.exponent_1) #define KEY_PRI_RSA_EXPO1(k) \ &((k)->key_type_u.rsa_pri_key.exponent_1) #define OBJ_PRI_RSA_EXPO2(o) \ &((o)->object_class_u.private_key->key_type_u.rsa_pri_key.exponent_2) #define KEY_PRI_RSA_EXPO2(k) \ &((k)->key_type_u.rsa_pri_key.exponent_2) #define OBJ_PRI_RSA_COEF(o) \ &((o)->object_class_u.private_key->key_type_u.rsa_pri_key.coefficient) #define KEY_PRI_RSA_COEF(k) \ &((k)->key_type_u.rsa_pri_key.coefficient) /* * DSA Private Key Object Attributes */ #define KEY_PRI_DSA(k) \ &((k)->key_type_u.dsa_pri_key) #define OBJ_PRI_DSA_PRIME(o) \ &((o)->object_class_u.private_key->key_type_u.dsa_pri_key.prime) #define KEY_PRI_DSA_PRIME(k) \ &((k)->key_type_u.dsa_pri_key.prime) #define OBJ_PRI_DSA_SUBPRIME(o) \ &((o)->object_class_u.private_key->key_type_u.dsa_pri_key.subprime) #define KEY_PRI_DSA_SUBPRIME(k) \ &((k)->key_type_u.dsa_pri_key.subprime) #define OBJ_PRI_DSA_BASE(o) \ &((o)->object_class_u.private_key->key_type_u.dsa_pri_key.base) #define KEY_PRI_DSA_BASE(k) \ &((k)->key_type_u.dsa_pri_key.base) #define OBJ_PRI_DSA_VALUE(o) \ &((o)->object_class_u.private_key->key_type_u.dsa_pri_key.value) #define KEY_PRI_DSA_VALUE(k) \ &((k)->key_type_u.dsa_pri_key.value) /* * Diffie-Hellman Private Key Object Attributes */ #define KEY_PRI_DH(k) \ &((k)->key_type_u.dh_pri_key) #define OBJ_PRI_DH_PRIME(o) \ &((o)->object_class_u.private_key->key_type_u.dh_pri_key.prime) #define KEY_PRI_DH_PRIME(k) \ &((k)->key_type_u.dh_pri_key.prime) #define OBJ_PRI_DH_BASE(o) \ &((o)->object_class_u.private_key->key_type_u.dh_pri_key.base) #define KEY_PRI_DH_BASE(k) \ &((k)->key_type_u.dh_pri_key.base) #define OBJ_PRI_DH_VALUE(o) \ &((o)->object_class_u.private_key->key_type_u.dh_pri_key.value) #define KEY_PRI_DH_VALUE(k) \ &((k)->key_type_u.dh_pri_key.value) #define OBJ_PRI_DH_VAL_BITS(o) \ ((o)->object_class_u.private_key->key_type_u.dh_pri_key.value_bits) #define KEY_PRI_DH_VAL_BITS(k) \ ((k)->key_type_u.dh_pri_key.value_bits) /* * EC Private Key Object Attributes */ #define OBJ_PRI_EC_VALUE(o) \ &((o)->object_class_u.private_key->key_type_u.ec_pri_key.value) #define KEY_PRI_EC_VALUE(k) \ &((k)->key_type_u.ec_pri_key.value) /* * key related attributes with CK_BBOOL data type */ #define DERIVE_BOOL_ON 0x00000001 #define LOCAL_BOOL_ON 0x00000002 #define SENSITIVE_BOOL_ON 0x00000004 #define SECONDARY_AUTH_BOOL_ON 0x00000008 #define ENCRYPT_BOOL_ON 0x00000010 #define DECRYPT_BOOL_ON 0x00000020 #define SIGN_BOOL_ON 0x00000040 #define SIGN_RECOVER_BOOL_ON 0x00000080 #define VERIFY_BOOL_ON 0x00000100 #define VERIFY_RECOVER_BOOL_ON 0x00000200 #define WRAP_BOOL_ON 0x00000400 #define UNWRAP_BOOL_ON 0x00000800 #define TRUSTED_BOOL_ON 0x00001000 #define EXTRACTABLE_BOOL_ON 0x00002000 #define ALWAYS_SENSITIVE_BOOL_ON 0x00004000 #define NEVER_EXTRACTABLE_BOOL_ON 0x00008000 #define PRIVATE_BOOL_ON 0x00010000 #define TOKEN_BOOL_ON 0x00020000 #define MODIFIABLE_BOOL_ON 0x00040000 #define SECRET_KEY_DEFAULT (ENCRYPT_BOOL_ON|\ DECRYPT_BOOL_ON|\ SIGN_BOOL_ON|\ VERIFY_BOOL_ON|\ WRAP_BOOL_ON|\ UNWRAP_BOOL_ON|\ EXTRACTABLE_BOOL_ON|\ MODIFIABLE_BOOL_ON) #define PUBLIC_KEY_DEFAULT (ENCRYPT_BOOL_ON|\ WRAP_BOOL_ON|\ VERIFY_BOOL_ON|\ VERIFY_RECOVER_BOOL_ON|\ MODIFIABLE_BOOL_ON) #define PRIVATE_KEY_DEFAULT (DECRYPT_BOOL_ON|\ UNWRAP_BOOL_ON|\ SIGN_BOOL_ON|\ SIGN_RECOVER_BOOL_ON|\ EXTRACTABLE_BOOL_ON|\ MODIFIABLE_BOOL_ON) /* * Flag definitions for obj_delete_sync */ #define OBJECT_IS_DELETING 1 /* Object is in a deleting state */ #define OBJECT_REFCNT_WAITING 2 /* Waiting for object reference */ /* count to become zero */ /* * This macro is used to type cast an object handle to a pointer to * the object struct. Also, it checks to see if the object struct * is tagged with an object magic number. This is to detect when an * application passes a bogus object pointer. * Also, it checks to see if the object is in the deleting state that * another thread is performing. If not, increment the object reference * count by one. This is to prevent this object from being deleted by * other thread. */ #define HANDLE2OBJECT_COMMON(hObject, object_p, rv, REFCNT_CODE) { \ object_p = (kernel_object_t *)(hObject); \ if ((object_p == NULL) || \ (object_p->magic_marker != KERNELTOKEN_OBJECT_MAGIC)) {\ rv = CKR_OBJECT_HANDLE_INVALID; \ } else { \ (void) pthread_mutex_lock(&object_p->object_mutex); \ if (!(object_p->obj_delete_sync & OBJECT_IS_DELETING)) { \ REFCNT_CODE; \ rv = CKR_OK; \ } else { \ rv = CKR_OBJECT_HANDLE_INVALID; \ } \ (void) pthread_mutex_unlock(&object_p->object_mutex); \ } \ } #define HANDLE2OBJECT(hObject, object_p, rv) \ HANDLE2OBJECT_COMMON(hObject, object_p, rv, object_p->obj_refcnt++) #define HANDLE2OBJECT_DESTROY(hObject, object_p, rv) \ HANDLE2OBJECT_COMMON(hObject, object_p, rv, /* no refcnt increment */) #define OBJ_REFRELE(object_p) { \ (void) pthread_mutex_lock(&object_p->object_mutex); \ if ((--object_p->obj_refcnt) == 0 && \ (object_p->obj_delete_sync & OBJECT_REFCNT_WAITING)) { \ (void) pthread_cond_signal(&object_p->obj_free_cond); \ } \ (void) pthread_mutex_unlock(&object_p->object_mutex); \ } /* * Function Prototypes. */ void kernel_cleanup_object(kernel_object_t *objp); CK_RV kernel_add_object(CK_ATTRIBUTE_PTR pTemplate, CK_ULONG ulCount, CK_ULONG *objecthandle_p, kernel_session_t *sp); CK_RV kernel_delete_session_object(kernel_session_t *sp, kernel_object_t *objp, boolean_t lock_held, boolean_t wrapper_only); void kernel_cleanup_extra_attr(kernel_object_t *object_p); CK_RV kernel_copy_extra_attr(CK_ATTRIBUTE_INFO_PTR old_attrp, kernel_object_t *object_p); void kernel_cleanup_object_bigint_attrs(kernel_object_t *object_p); CK_RV kernel_build_object(CK_ATTRIBUTE_PTR template, CK_ULONG ulAttrNum, kernel_object_t *new_object, kernel_session_t *sp, uint_t); CK_RV kernel_copy_object(kernel_object_t *old_object, kernel_object_t **new_object, boolean_t copy_everything, kernel_session_t *sp); void kernel_merge_object(kernel_object_t *old_object, kernel_object_t *new_object); CK_RV kernel_get_attribute(kernel_object_t *object_p, CK_ATTRIBUTE_PTR template); CK_RV kernel_set_attribute(kernel_object_t *object_p, CK_ATTRIBUTE_PTR template, boolean_t copy, kernel_session_t *sp); void copy_bigint_attr(biginteger_t *src, biginteger_t *dst); void kernel_add_object_to_session(kernel_object_t *objp, kernel_session_t *sp); CK_RV kernel_copy_public_key_attr(public_key_obj_t *old_pub_key_obj_p, public_key_obj_t **new_pub_key_obj_p, CK_KEY_TYPE key_type); CK_RV kernel_copy_private_key_attr(private_key_obj_t *old_pri_key_obj_p, private_key_obj_t **new_pri_key_obj_p, CK_KEY_TYPE key_type); CK_RV kernel_copy_secret_key_attr(secret_key_obj_t *old_secret_key_obj_p, secret_key_obj_t **new_secret_key_obj_p); CK_RV kernel_validate_attr(CK_ATTRIBUTE_PTR template, CK_ULONG ulAttrNum, CK_OBJECT_CLASS *class); CK_RV kernel_find_objects_init(kernel_session_t *sp, CK_ATTRIBUTE_PTR pTemplate, CK_ULONG ulCount); void kernel_find_objects_final(kernel_session_t *sp); void kernel_find_objects(kernel_session_t *sp, CK_OBJECT_HANDLE *obj_found, CK_ULONG max_obj_requested, CK_ULONG *found_obj_count); void kernel_process_find_attr(CK_OBJECT_CLASS *pclasses, CK_ULONG *num_result_pclasses, CK_ATTRIBUTE_PTR pTemplate, CK_ULONG ulCount); boolean_t kernel_find_match_attrs(kernel_object_t *obj, CK_OBJECT_CLASS *pclasses, CK_ULONG num_pclasses, CK_ATTRIBUTE *tmpl_attr, CK_ULONG num_attr); CK_ATTRIBUTE_PTR get_extra_attr(CK_ATTRIBUTE_TYPE type, kernel_object_t *obj); CK_RV get_string_from_template(CK_ATTRIBUTE_PTR dest, CK_ATTRIBUTE_PTR src); void string_attr_cleanup(CK_ATTRIBUTE_PTR template); void kernel_add_token_object_to_slot(kernel_object_t *objp, kernel_slot_t *pslot); void kernel_remove_token_object_from_slot(kernel_slot_t *pslot, kernel_object_t *objp); CK_RV kernel_delete_token_object(kernel_slot_t *pslot, kernel_session_t *sp, kernel_object_t *obj, boolean_t lock_held, boolean_t wrapper_only); void kernel_cleanup_pri_objects_in_slot(kernel_slot_t *pslot, kernel_session_t *sp); CK_RV kernel_get_object_size(kernel_object_t *objp, CK_ULONG_PTR pulSize); void kernel_object_delay_free(kernel_object_t *objp); #ifdef __cplusplus } #endif #endif /* _KERNELOBJECT_H */ /* * 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. * Copyright (c) 2018, Joyent, Inc. */ #include #include #include #include #include #include #include "kernelGlobal.h" #include "kernelObject.h" #include "kernelSession.h" #include "kernelSlot.h" /* * Add an object to the session's object list. * * This function will acquire the lock on the session, and release * that lock after adding the object to the session's object list. */ void kernel_add_object_to_session(kernel_object_t *objp, kernel_session_t *sp) { /* Acquire the session lock. */ (void) pthread_mutex_lock(&sp->session_mutex); /* Insert the new object in front of session's object list. */ if (sp->object_list == NULL) { sp->object_list = objp; objp->next = NULL; objp->prev = NULL; } else { sp->object_list->prev = objp; objp->next = sp->object_list; objp->prev = NULL; sp->object_list = objp; } /* Release the session lock. */ (void) pthread_mutex_unlock(&sp->session_mutex); } /* * Clean up and release the storage allocated to the object. * * The function is called either with the object lock being held * (by caller kernel_delete_object()), or there is no object lock * yet (by kernel_build_XXX_object() during creating an object). */ void kernel_cleanup_object(kernel_object_t *objp) { /* * Free the storage allocated to a secret key object. */ if (objp->class == CKO_SECRET_KEY) { if (OBJ_SEC(objp) != NULL && OBJ_SEC_VALUE(objp) != NULL) { freezero(OBJ_SEC_VALUE(objp), OBJ_SEC_VALUE_LEN(objp)); OBJ_SEC_VALUE(objp) = NULL; OBJ_SEC_VALUE_LEN(objp) = 0; } free(OBJ_SEC(objp)); OBJ_SEC(objp) = NULL; } else { kernel_cleanup_object_bigint_attrs(objp); } /* * Free the storage allocated to the extra attribute list. */ kernel_cleanup_extra_attr(objp); } /* * Create a new object. Copy the attributes that can be modified * (in the boolean attribute mask field and extra attribute list) * from the old object to the new object. * * The caller of this function holds the lock on the old object. */ CK_RV kernel_copy_object(kernel_object_t *old_object, kernel_object_t **new_object, boolean_t copy_everything, kernel_session_t *sp) { CK_RV rv = CKR_OK; kernel_object_t *new_objp = NULL; CK_ATTRIBUTE_INFO_PTR attrp; /* Allocate new object. */ new_objp = calloc(1, sizeof (kernel_object_t)); if (new_objp == NULL) return (CKR_HOST_MEMORY); new_objp->class = old_object->class; new_objp->bool_attr_mask = old_object->bool_attr_mask; attrp = old_object->extra_attrlistp; while (attrp) { /* * Copy the attribute_info struct from the old * object to a new attribute_info struct, and add * that new struct to the extra attribute list * of the new object. */ rv = kernel_copy_extra_attr(attrp, new_objp); if (rv != CKR_OK) { kernel_cleanup_extra_attr(new_objp); free(new_objp); return (rv); } attrp = attrp->next; } *new_object = new_objp; if (!copy_everything) { /* done with copying all information that can be modified */ return (CKR_OK); } /* * Copy the rest of the object. * Certain fields that are not appropriate for coping will be * initialized. */ new_objp->key_type = old_object->key_type; new_objp->magic_marker = old_object->magic_marker; new_objp->mechanism = old_object->mechanism; new_objp->session_handle = (CK_SESSION_HANDLE)sp; (void) pthread_mutex_init(&(new_objp->object_mutex), NULL); /* copy key related information */ switch (new_objp->class) { case CKO_PUBLIC_KEY: rv = kernel_copy_public_key_attr(OBJ_PUB(old_object), &(OBJ_PUB(new_objp)), new_objp->key_type); break; case CKO_PRIVATE_KEY: rv = kernel_copy_private_key_attr(OBJ_PRI(old_object), &(OBJ_PRI(new_objp)), new_objp->key_type); break; case CKO_SECRET_KEY: rv = kernel_copy_secret_key_attr(OBJ_SEC(old_object), &(OBJ_SEC(new_objp))); break; default: /* should never be this case */ break; } if (rv != CKR_OK) { /* * don't need to cleanup the memory from failure of copying * any key related stuff. Each individual function for * copying key attr will free the memory if it fails */ kernel_cleanup_extra_attr(new_objp); free(new_objp); } return (rv); } /* * Copy the attributes (in the boolean attribute mask field and * extra attribute list) from the new object back to the original * object. Also, clean up and release all the storage in the extra * attribute list of the original object. * * The caller of this function holds the lock on the old object. */ void kernel_merge_object(kernel_object_t *old_object, kernel_object_t *new_object) { old_object->bool_attr_mask = new_object->bool_attr_mask; kernel_cleanup_extra_attr(old_object); old_object->extra_attrlistp = new_object->extra_attrlistp; } /* * Create a new object struct. If it is a session object, add the object to * the session's object list. If it is a token object, add it to the slot's * token object list. The caller does not hold the slot lock. */ CK_RV kernel_add_object(CK_ATTRIBUTE_PTR pTemplate, CK_ULONG ulCount, CK_ULONG *objecthandle_p, kernel_session_t *sp) { CK_RV rv = CKR_OK; kernel_object_t *new_objp = NULL; kernel_slot_t *pslot; crypto_object_create_t objc; CK_BBOOL is_pri_obj; CK_BBOOL is_token_obj = B_FALSE; int r; new_objp = calloc(1, sizeof (kernel_object_t)); if (new_objp == NULL) { rv = CKR_HOST_MEMORY; goto fail_cleanup; } new_objp->extra_attrlistp = NULL; new_objp->is_lib_obj = B_TRUE; /* * If the HW provider supports object creation, create the object * in the HW provider by calling the CRYPTO_OBJECT_CREATE ioctl. * Otherwise, create the object in the library. */ pslot = slot_table[sp->ses_slotid]; if (pslot->sl_func_list.fl_object_create) { new_objp->is_lib_obj = B_FALSE; objc.oc_session = sp->k_session; objc.oc_count = ulCount; rv = process_object_attributes(pTemplate, ulCount, &objc.oc_attributes, &is_token_obj); if (rv != CKR_OK) { goto fail_cleanup; } /* Cannot create a token object with a READ-ONLY session */ if (is_token_obj && sp->ses_RO) { free_object_attributes(objc.oc_attributes, ulCount); rv = CKR_SESSION_READ_ONLY; goto fail_cleanup; } while ((r = ioctl(kernel_fd, CRYPTO_OBJECT_CREATE, &objc)) < 0) { if (errno != EINTR) break; } if (r < 0) { rv = CKR_FUNCTION_FAILED; } else { rv = crypto2pkcs11_error_number(objc.oc_return_value); } free_object_attributes(objc.oc_attributes, ulCount); if (rv != CKR_OK) { goto fail_cleanup; } /* Get the CKA_PRIVATE value of this object. */ new_objp->k_handle = objc.oc_handle; rv = get_cka_private_value(sp, new_objp->k_handle, &is_pri_obj); if (rv != CKR_OK) { goto fail_cleanup; } /* Set the PRIVATE_BOOL_ON and TOKEN_BOOL_ON attributes */ if (is_pri_obj) new_objp->bool_attr_mask |= PRIVATE_BOOL_ON; else new_objp->bool_attr_mask &= ~PRIVATE_BOOL_ON; if (is_token_obj) new_objp->bool_attr_mask |= TOKEN_BOOL_ON; else new_objp->bool_attr_mask &= ~TOKEN_BOOL_ON; } else { /* * Create the object in the library. * Validate attribute template and fill in the attributes * in the kernel_object_t. */ rv = kernel_build_object(pTemplate, ulCount, new_objp, sp, KERNEL_CREATE_OBJ); if (rv != CKR_OK) { goto fail_cleanup; } } /* Initialize the rest of stuffs in kernel_object_t. */ (void) pthread_mutex_init(&new_objp->object_mutex, NULL); new_objp->magic_marker = KERNELTOKEN_OBJECT_MAGIC; new_objp->session_handle = (CK_SESSION_HANDLE)sp; if (is_token_obj) { /* Add the new object to the slot's token object list. */ pslot = slot_table[sp->ses_slotid]; kernel_add_token_object_to_slot(new_objp, pslot); } else { /* Add the new object to the session's object list. */ kernel_add_object_to_session(new_objp, sp); } /* Type casting the address of an object struct to an object handle. */ *objecthandle_p = (CK_ULONG)new_objp; return (CKR_OK); fail_cleanup: if (new_objp) { /* * If the object is created in the HW provider, the storage * allocated for the ioctl call is always cleaned up after * the call. If the object is created in the library, * the storage allocated inside of this object should * have been cleaned up in the kernel_build_object() * after an error occurred. Therefore, we can safely * free the object. */ free(new_objp); } return (rv); } /* * Remove an object from the session's object list. * * The caller of this function holds the session lock. */ CK_RV kernel_remove_object_from_session(kernel_object_t *objp, kernel_session_t *sp) { kernel_object_t *tmp_objp; boolean_t found = B_FALSE; /* * Remove the object from the session's object list. */ if ((sp == NULL) || (sp->magic_marker != KERNELTOKEN_SESSION_MAGIC)) { return (CKR_SESSION_HANDLE_INVALID); } if ((sp->object_list == NULL) || (objp == NULL) || (objp->magic_marker != KERNELTOKEN_OBJECT_MAGIC)) { return (CKR_OBJECT_HANDLE_INVALID); } tmp_objp = sp->object_list; while (tmp_objp) { if (tmp_objp == objp) { found = B_TRUE; break; } tmp_objp = tmp_objp->next; } if (!found) return (CKR_OBJECT_HANDLE_INVALID); if (sp->object_list == objp) { /* Object is the first one in the list. */ if (objp->next) { sp->object_list = objp->next; objp->next->prev = NULL; } else { /* Object is the only one in the list. */ sp->object_list = NULL; } } else { /* Object is not the first one in the list. */ if (objp->next) { /* Object is in the middle of the list. */ objp->prev->next = objp->next; objp->next->prev = objp->prev; } else { /* Object is the last one in the list. */ objp->prev->next = NULL; } } return (CKR_OK); } static void kernel_delete_object_cleanup(kernel_object_t *objp, boolean_t wrapper_only) { /* Acquire the lock on the object. */ (void) pthread_mutex_lock(&objp->object_mutex); /* * Make sure another thread hasn't freed the object. */ if (objp->magic_marker != KERNELTOKEN_OBJECT_MAGIC) { (void) pthread_mutex_unlock(&objp->object_mutex); return; } /* * The deletion of an object must be blocked when the object * reference count is not zero. This means if any object related * operation starts prior to the delete object operation gets in, * the object deleting thread must wait for the non-deleting * operation to be completed before it can proceed the delete * operation. * * Unless we are being forced to shut everything down, this only * happens if the library's _fini() is running not if someone * explicitly called C_Finalize(). */ if (wrapper_only) { objp->obj_refcnt = 0; } while (objp->obj_refcnt != 0) { /* * We set the OBJECT_REFCNT_WAITING flag before we put * this deleting thread in a wait state, so other non-deleting * operation thread will signal to wake it up only when * the object reference count becomes zero and this flag * is set. */ objp->obj_delete_sync |= OBJECT_REFCNT_WAITING; (void) pthread_cond_wait(&objp->obj_free_cond, &objp->object_mutex); } objp->obj_delete_sync &= ~OBJECT_REFCNT_WAITING; /* Mark object as no longer valid. */ objp->magic_marker = 0; (void) pthread_cond_destroy(&objp->obj_free_cond); } /* * Delete a session object: * - Remove the object from the session's object list. * - Release the storage allocated to the object. * * The boolean argument ses_lock_held is used to indicate that whether * the caller holds the session lock or not. * - When called by kernel_delete_all_objects_in_session() or * kernel_delete_pri_objects_in_slot() -- ses_lock_held = TRUE. * * The boolean argument wrapper_only is used to indicate that whether * the caller only wants to clean up the object wrapper from the library and * needs not to make an ioctl call. * - This argument only applies to the object created in the provider level. * - When called by kernel_cleanup_pri_objects_in_slot(), wrapper_only is TRUE. * - When called by C_DestroyObject(), wrapper_only is FALSE. * - When called by kernel_delete_all_objects_in_session(), the value of * wrapper_only depends on its caller. */ CK_RV kernel_delete_session_object(kernel_session_t *sp, kernel_object_t *objp, boolean_t ses_lock_held, boolean_t wrapper_only) { CK_RV rv = CKR_OK; crypto_object_destroy_t obj_destroy; /* * Check to see if the caller holds the lock on the session. * If not, we need to acquire that lock in order to proceed. */ if (!ses_lock_held) { /* Acquire the session lock. */ (void) pthread_mutex_lock(&sp->session_mutex); } /* Remove the object from the session's object list first. */ rv = kernel_remove_object_from_session(objp, sp); if (!ses_lock_held) { /* * If the session lock is obtained by this function, * then release that lock after removing the object * from session's object list. * We want the releasing of the object storage to * be done without holding the session lock. */ (void) pthread_mutex_unlock(&sp->session_mutex); } if (rv != CKR_OK) return (rv); kernel_delete_object_cleanup(objp, wrapper_only); /* Destroy the object. */ if (objp->is_lib_obj) { /* * If this object is created in the library, cleanup the * contents of this object such as free all the storage * allocated for this object. */ kernel_cleanup_object(objp); } else { /* * This object is created in the HW provider. If wrapper_only * is FALSE, make an ioctl call to destroy it in kernel. */ if (!wrapper_only) { obj_destroy.od_session = sp->k_session; obj_destroy.od_handle = objp->k_handle; while (ioctl(kernel_fd, CRYPTO_OBJECT_DESTROY, &obj_destroy) < 0) { if (errno != EINTR) break; } /* * Ignore ioctl return codes for a session object. * If the kernel can not delete a session object, it * is likely caused by the HW provider. There's not * much that can be done. The library will still * cleanup the object wrapper in the library. The HW * provider will destroy all session objects when * the application exits. */ } } /* Reset OBJECT_IS_DELETING flag. */ objp->obj_delete_sync &= ~OBJECT_IS_DELETING; (void) pthread_mutex_unlock(&objp->object_mutex); /* Destroy the object lock */ (void) pthread_mutex_destroy(&objp->object_mutex); /* Free the object itself */ kernel_object_delay_free(objp); return (CKR_OK); } /* * Delete all the objects in a session. The caller holds the lock * on the session. If the wrapper_only argument is TRUE, the caller only * want to clean up object wrappers in the library. */ void kernel_delete_all_objects_in_session(kernel_session_t *sp, boolean_t wrapper_only) { kernel_object_t *objp = sp->object_list; kernel_object_t *objp1; /* Delete all the objects in the session. */ while (objp) { objp1 = objp->next; /* * Delete an session object by calling * kernel_delete_session_object(): * - The 3rd TRUE boolean argument indicates that the caller * holds the session lock. * - The 4th boolean argument indicates whether we only want * clean up object wrappers in the library. */ (void) kernel_delete_session_object(sp, objp, B_TRUE, wrapper_only); objp = objp1; } } static CK_RV add_to_search_result(kernel_object_t *obj, find_context_t *fcontext, CK_ULONG *num_result_alloc) { /* * allocate space for storing results if the currently * allocated space is not enough */ if (*num_result_alloc <= fcontext->num_results) { fcontext->objs_found = realloc(fcontext->objs_found, sizeof (kernel_object_t *) * (*num_result_alloc + BUFSIZ)); if (fcontext->objs_found == NULL) { return (CKR_HOST_MEMORY); } *num_result_alloc += BUFSIZ; } (fcontext->objs_found)[(fcontext->num_results)++] = obj; return (CKR_OK); } static CK_RV search_for_objects(kernel_session_t *sp, CK_ATTRIBUTE_PTR pTemplate, CK_ULONG ulCount, find_context_t *fcontext) { kernel_session_t *session_p; kernel_object_t *obj; CK_OBJECT_CLASS pclasses[6]; /* classes attrs possibly exist */ CK_ULONG num_pclasses; /* number of possible classes */ CK_ULONG num_result_alloc = 0; /* spaces allocated for results */ CK_RV rv = CKR_OK; kernel_slot_t *pslot; if (ulCount > 0) { /* there are some search requirement */ kernel_process_find_attr(pclasses, &num_pclasses, pTemplate, ulCount); } /* Acquire the slot lock */ pslot = slot_table[sp->ses_slotid]; (void) pthread_mutex_lock(&pslot->sl_mutex); /* * Go through all objects in each session. * Acquire individual session lock for the session * we are searching. */ session_p = pslot->sl_sess_list; while (session_p) { (void) pthread_mutex_lock(&session_p->session_mutex); obj = session_p->object_list; while (obj) { (void) pthread_mutex_lock(&obj->object_mutex); if (ulCount > 0) { if (kernel_find_match_attrs(obj, pclasses, num_pclasses, pTemplate, ulCount)) { rv = add_to_search_result( obj, fcontext, &num_result_alloc); } } else { /* no search criteria, just record the object */ rv = add_to_search_result(obj, fcontext, &num_result_alloc); } (void) pthread_mutex_unlock(&obj->object_mutex); if (rv != CKR_OK) { (void) pthread_mutex_unlock( &session_p->session_mutex); goto cleanup; } obj = obj->next; } (void) pthread_mutex_unlock(&session_p->session_mutex); session_p = session_p->next; } cleanup: /* Release the slot lock */ (void) pthread_mutex_unlock(&pslot->sl_mutex); return (rv); } /* * Initialize the context for C_FindObjects() calls */ CK_RV kernel_find_objects_init(kernel_session_t *sp, CK_ATTRIBUTE_PTR pTemplate, CK_ULONG ulCount) { CK_RV rv = CKR_OK; CK_OBJECT_CLASS class; /* for kernel_validate_attr(). Value unused */ find_context_t *fcontext; if (ulCount) { rv = kernel_validate_attr(pTemplate, ulCount, &class); /* Make sure all attributes in template are valid */ if (rv != CKR_OK) { return (rv); } } /* prepare the find context */ fcontext = calloc(1, sizeof (find_context_t)); if (fcontext == NULL) { return (CKR_HOST_MEMORY); } rv = search_for_objects(sp, pTemplate, ulCount, fcontext); if (rv != CKR_OK) { free(fcontext); return (rv); } /* store the find_context in the session */ sp->find_objects.context = (CK_VOID_PTR)fcontext; return (rv); } void kernel_find_objects_final(kernel_session_t *sp) { find_context_t *fcontext; fcontext = sp->find_objects.context; sp->find_objects.context = NULL; sp->find_objects.flags = 0; if (fcontext->objs_found != NULL) { free(fcontext->objs_found); } free(fcontext); } void kernel_find_objects(kernel_session_t *sp, CK_OBJECT_HANDLE *obj_found, CK_ULONG max_obj_requested, CK_ULONG *found_obj_count) { find_context_t *fcontext; CK_ULONG num_obj_found = 0; CK_ULONG i; kernel_object_t *obj; fcontext = sp->find_objects.context; for (i = fcontext->next_result_index; ((num_obj_found < max_obj_requested) && (i < fcontext->num_results)); i++) { obj = fcontext->objs_found[i]; if (obj != NULL) { (void) pthread_mutex_lock(&obj->object_mutex); /* a sanity check to make sure the obj is still valid */ if (obj->magic_marker == KERNELTOKEN_OBJECT_MAGIC) { obj_found[num_obj_found] = (CK_OBJECT_HANDLE)obj; num_obj_found++; } (void) pthread_mutex_unlock(&obj->object_mutex); } } fcontext->next_result_index = i; *found_obj_count = num_obj_found; } /* * Add an token object to the token object list in slot. * * This function will acquire the lock on the slot, and release * that lock after adding the object to the slot's token object list. */ void kernel_add_token_object_to_slot(kernel_object_t *objp, kernel_slot_t *pslot) { /* Acquire the slot lock. */ (void) pthread_mutex_lock(&pslot->sl_mutex); /* Insert the new object in front of slot's token object list. */ if (pslot->sl_tobj_list == NULL) { pslot->sl_tobj_list = objp; objp->next = NULL; objp->prev = NULL; } else { pslot->sl_tobj_list->prev = objp; objp->next = pslot->sl_tobj_list; objp->prev = NULL; pslot->sl_tobj_list = objp; } /* Release the slot lock. */ (void) pthread_mutex_unlock(&pslot->sl_mutex); } /* * Remove an token object from the slot's token object list. * This routine is called by kernel_delete_token_object(). * The caller of this function hold the slot lock. */ void kernel_remove_token_object_from_slot(kernel_slot_t *pslot, kernel_object_t *objp) { if (pslot->sl_tobj_list == objp) { /* Object is the first one in the list */ if (objp->next) { pslot->sl_tobj_list = objp->next; objp->next->prev = NULL; } else { /* Object is the only one in the list. */ pslot->sl_tobj_list = NULL; } } else { /* Object is not the first one in the list. */ if (objp->next) { /* Object is in the middle of the list. */ objp->prev->next = objp->next; objp->next->prev = objp->prev; } else { /* Object is the last one in the list. */ objp->prev->next = NULL; } } } /* * Delete a token object: * - Remove the object from the slot's token object list. * - Release the storage allocated to the object. * * The boolean argument slot_lock_held is used to indicate that whether * the caller holds the slot lock or not. When the caller does not hold * the slot lock, this function will acquire that lock in order to proceed, * and also release that lock before returning to caller. * * The boolean argument wrapper_only is used to indicate that whether * the caller only wants to the object wrapper from library. */ CK_RV kernel_delete_token_object(kernel_slot_t *pslot, kernel_session_t *sp, kernel_object_t *objp, boolean_t slot_lock_held, boolean_t wrapper_only) { CK_RV rv; crypto_object_destroy_t obj_destroy; int r; /* * Check to see if the caller holds the lock on the slot. * If not, we need to acquire that lock in order to proceed. */ if (!slot_lock_held) { (void) pthread_mutex_lock(&pslot->sl_mutex); } /* Remove the object from the slot's token object list first. */ kernel_remove_token_object_from_slot(pslot, objp); /* Release the slot lock if the call doesn't hold the lock. */ if (!slot_lock_held) { (void) pthread_mutex_unlock(&pslot->sl_mutex); } kernel_delete_object_cleanup(objp, wrapper_only); if (!wrapper_only) { obj_destroy.od_session = sp->k_session; obj_destroy.od_handle = objp->k_handle; while ((r = ioctl(kernel_fd, CRYPTO_OBJECT_DESTROY, &obj_destroy)) < 0) { if (errno != EINTR) break; } if (r < 0) { rv = CKR_FUNCTION_FAILED; } else { rv = crypto2pkcs11_error_number( obj_destroy.od_return_value); } /* * Could not destroy an object from kernel. Write a warning * in syslog, but we still clean up the object wrapper in * the library. */ if (rv != CKR_OK) { cryptoerror(LOG_ERR, "pkcs11_kernel: Could not " "destroy an object in kernel."); } } (void) pthread_mutex_unlock(&objp->object_mutex); /* Destroy the object lock */ (void) pthread_mutex_destroy(&objp->object_mutex); /* Free the object itself */ kernel_object_delay_free(objp); return (CKR_OK); } /* * Clean up private object wrappers in this slot. The caller holds the slot * lock. */ void kernel_cleanup_pri_objects_in_slot(kernel_slot_t *pslot, kernel_session_t *cur_sp) { kernel_session_t *session_p; kernel_object_t *objp; kernel_object_t *objp1; /* * Delete every private token object from the slot' token object list */ objp = pslot->sl_tobj_list; while (objp) { objp1 = objp->next; /* * The first TRUE boolean argument indicates that the caller * hold the slot lock. The second TRUE boolean argument * indicates that the caller just wants to clean up the object * wrapper from the library only. */ if (objp->bool_attr_mask & PRIVATE_BOOL_ON) { (void) kernel_delete_token_object(pslot, cur_sp, objp, B_TRUE, B_TRUE); } objp = objp1; } /* * Walk through all the sessions in this slot and delete every * private object. */ session_p = pslot->sl_sess_list; while (session_p) { /* Delete all the objects in the session. */ objp = session_p->object_list; while (objp) { objp1 = objp->next; /* * The FALSE boolean argument indicates that the * caller does not hold the session lock. The TRUE * boolean argument indicates that the caller just * want to clean upt the object wrapper from the * library only. */ if (objp->bool_attr_mask & PRIVATE_BOOL_ON) { (void) kernel_delete_session_object(session_p, objp, B_FALSE, B_TRUE); } objp = objp1; } session_p = session_p->next; } } /* * Get the object size in bytes for the objects created in the library. */ CK_RV kernel_get_object_size(kernel_object_t *obj, CK_ULONG_PTR pulSize) { CK_RV rv = CKR_OK; CK_ULONG obj_size; biginteger_t *big; obj_size = sizeof (kernel_object_t); switch (obj->class) { case CKO_PUBLIC_KEY: if (obj->key_type == CKK_RSA) { big = OBJ_PUB_RSA_PUBEXPO(obj); obj_size += big->big_value_len; big = OBJ_PUB_RSA_MOD(obj); obj_size += big->big_value_len; } else if (obj->key_type == CKK_DSA) { big = OBJ_PUB_DSA_PRIME(obj); obj_size += big->big_value_len; big = OBJ_PUB_DSA_SUBPRIME(obj); obj_size += big->big_value_len; big = OBJ_PUB_DSA_BASE(obj); obj_size += big->big_value_len; big = OBJ_PUB_DSA_VALUE(obj); obj_size += big->big_value_len; } else if (obj->key_type == CKK_EC) { big = OBJ_PUB_EC_POINT(obj); obj_size += big->big_value_len; } else { rv = CKR_OBJECT_HANDLE_INVALID; } break; case CKO_PRIVATE_KEY: if (obj->key_type == CKK_RSA) { big = OBJ_PRI_RSA_MOD(obj); obj_size += big->big_value_len; big = OBJ_PRI_RSA_PUBEXPO(obj); /* optional */ if (big != NULL) { obj_size += big->big_value_len; } big = OBJ_PRI_RSA_PRIEXPO(obj); obj_size += big->big_value_len; big = OBJ_PRI_RSA_PRIME1(obj); /* optional */ if (big != NULL) { obj_size += big->big_value_len; } big = OBJ_PRI_RSA_PRIME2(obj); /* optional */ if (big != NULL) { obj_size += big->big_value_len; } big = OBJ_PRI_RSA_EXPO1(obj); /* optional */ if (big != NULL) { obj_size += big->big_value_len; } big = OBJ_PRI_RSA_EXPO2(obj); /* optional */ if (big != NULL) { obj_size += big->big_value_len; } big = OBJ_PRI_RSA_COEF(obj); /* optional */ if (big != NULL) { obj_size += big->big_value_len; } } else if (obj->key_type == CKK_DSA) { big = OBJ_PRI_DSA_PRIME(obj); obj_size += big->big_value_len; big = OBJ_PRI_DSA_SUBPRIME(obj); obj_size += big->big_value_len; big = OBJ_PRI_DSA_BASE(obj); obj_size += big->big_value_len; big = OBJ_PRI_DSA_VALUE(obj); obj_size += big->big_value_len; } else if (obj->key_type == CKK_EC) { big = OBJ_PRI_EC_VALUE(obj); obj_size += big->big_value_len; } else { rv = CKR_OBJECT_HANDLE_INVALID; } break; case CKO_SECRET_KEY: obj_size += OBJ_SEC_VALUE_LEN(obj); break; default: rv = CKR_OBJECT_HANDLE_INVALID; } if (rv == CKR_OK) { *pulSize = obj_size; } return (rv); } /* * This function adds the to-be-freed session object to a linked list. * When the number of objects queued in the linked list reaches the * maximum threshold MAX_OBJ_TO_BE_FREED, it will free the first * object (FIFO) in the list. */ void kernel_object_delay_free(kernel_object_t *objp) { kernel_object_t *tmp; (void) pthread_mutex_lock(&obj_delay_freed.obj_to_be_free_mutex); /* Add the newly deleted object at the end of the list */ objp->next = NULL; if (obj_delay_freed.first == NULL) { obj_delay_freed.last = objp; obj_delay_freed.first = objp; } else { obj_delay_freed.last->next = objp; obj_delay_freed.last = objp; } if (++obj_delay_freed.count >= MAX_OBJ_TO_BE_FREED) { /* * Free the first object in the list only if * the total count reaches maximum threshold. */ obj_delay_freed.count--; tmp = obj_delay_freed.first->next; free(obj_delay_freed.first); obj_delay_freed.first = tmp; } (void) pthread_mutex_unlock(&obj_delay_freed.obj_to_be_free_mutex); } /* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License, Version 1.0 only * (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 2005 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ #include "kernelGlobal.h" #include #include #include #include CK_RV C_SeedRandom(CK_SESSION_HANDLE hSession, CK_BYTE_PTR pSeed, CK_ULONG ulSeedLen) { kernel_session_t *session_p; crypto_seed_random_t seed_random; boolean_t ses_lock_held = B_FALSE; CK_RV rv; int r; if (!kernel_initialized) return (CKR_CRYPTOKI_NOT_INITIALIZED); /* Obtain the session pointer. */ rv = handle2session(hSession, &session_p); if (rv != CKR_OK) return (rv); if ((pSeed == NULL) || (ulSeedLen == 0)) { REFRELE(session_p, ses_lock_held); return (CKR_ARGUMENTS_BAD); } seed_random.sr_session = session_p->k_session; seed_random.sr_seedbuf = (caddr_t)pSeed; seed_random.sr_seedlen = ulSeedLen; while ((r = ioctl(kernel_fd, CRYPTO_SEED_RANDOM, &seed_random)) < 0) { if (errno != EINTR) break; } if (r < 0) { rv = CKR_FUNCTION_FAILED; } else { if (seed_random.sr_return_value != CRYPTO_SUCCESS) { rv = crypto2pkcs11_error_number( seed_random.sr_return_value); } else { rv = CKR_OK; } } REFRELE(session_p, ses_lock_held); return (rv); } CK_RV C_GenerateRandom(CK_SESSION_HANDLE hSession, CK_BYTE_PTR pRandomData, CK_ULONG ulRandomLen) { kernel_session_t *session_p; crypto_generate_random_t generate_random; boolean_t ses_lock_held = B_FALSE; CK_RV rv; int r; if (!kernel_initialized) return (CKR_CRYPTOKI_NOT_INITIALIZED); /* Obtain the session pointer. */ rv = handle2session(hSession, &session_p); if (rv != CKR_OK) return (rv); if ((pRandomData == NULL) || (ulRandomLen == 0)) { REFRELE(session_p, ses_lock_held); return (CKR_ARGUMENTS_BAD); } generate_random.gr_session = session_p->k_session; generate_random.gr_buf = (caddr_t)pRandomData; generate_random.gr_buflen = ulRandomLen; while ((r = ioctl(kernel_fd, CRYPTO_GENERATE_RANDOM, &generate_random)) < 0) { if (errno != EINTR) break; } if (r < 0) { rv = CKR_FUNCTION_FAILED; } else { if (generate_random.gr_return_value != CRYPTO_SUCCESS) { rv = crypto2pkcs11_error_number( generate_random.gr_return_value); } else { rv = CKR_OK; } } REFRELE(session_p, ses_lock_held); return (rv); } /* * 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. */ #include #include #include #include #include "kernelGlobal.h" #include "kernelSession.h" #include "kernelSlot.h" #include "kernelEmulate.h" CK_RV C_OpenSession(CK_SLOT_ID slotID, CK_FLAGS flags, CK_VOID_PTR pApplication, CK_NOTIFY Notify, CK_SESSION_HANDLE_PTR phSession) { CK_RV rv = CKR_OK; kernel_slot_t *pslot; if (!kernel_initialized) return (CKR_CRYPTOKI_NOT_INITIALIZED); /* * For legacy reasons, the CKF_SERIAL_SESSION bit must always * be set. */ if (!(flags & CKF_SERIAL_SESSION)) return (CKR_SESSION_PARALLEL_NOT_SUPPORTED); if (phSession == NULL) return (CKR_ARGUMENTS_BAD); if (slotID >= slot_count) { return (CKR_SLOT_ID_INVALID); } /* * Acquire the slot lock to protect sl_state and sl_sess_list. * These two fields need to be protected atomically, even though * "sl_sess_list" is updated in kernel_add_session(). */ pslot = slot_table[slotID]; (void) pthread_mutex_lock(&pslot->sl_mutex); /* If SO is logged in the slot, only the RW session is allowed. */ if ((pslot->sl_state == CKU_SO) && !(flags & CKF_RW_SESSION)) { (void) pthread_mutex_unlock(&pslot->sl_mutex); return (CKR_SESSION_READ_WRITE_SO_EXISTS); } /* Create a new session */ rv = kernel_add_session(slotID, flags, pApplication, Notify, phSession); (void) pthread_mutex_unlock(&pslot->sl_mutex); return (rv); } CK_RV C_CloseSession(CK_SESSION_HANDLE hSession) { CK_RV rv; kernel_session_t *session_p; boolean_t ses_lock_held = B_FALSE; if (!kernel_initialized) return (CKR_CRYPTOKI_NOT_INITIALIZED); /* * Obtain the session pointer. Also, increment the session * reference count. */ rv = handle2session(hSession, &session_p); if (rv != CKR_OK) return (rv); (void) pthread_mutex_lock(&session_p->session_mutex); ses_lock_held = B_TRUE; /* * Set SESSION_IS_CLOSING flag so any access to this * session will be rejected. */ if (session_p->ses_close_sync & SESSION_IS_CLOSING) { REFRELE(session_p, ses_lock_held); return (CKR_SESSION_CLOSED); } session_p->ses_close_sync |= SESSION_IS_CLOSING; /* * Decrement the session reference count. * We hold the session lock, and REFRELE() * will release the session lock for us. */ REFRELE(session_p, ses_lock_held); /* * Delete a session by calling kernel_delete_session() with * a session pointer and two boolean arguments. The 3rd argument * boolean value FALSE indicates that the caller does not * hold the slot lock. The 4th argument boolean value B_FALSE * indicates that we want to delete all the objects completely. * * kernel_delete_session() will reset SESSION_IS_CLOSING * flag after it is done. */ kernel_delete_session(session_p->ses_slotid, session_p, B_FALSE, B_FALSE); return (rv); } CK_RV C_CloseAllSessions(CK_SLOT_ID slotID) { if (!kernel_initialized) return (CKR_CRYPTOKI_NOT_INITIALIZED); /* Delete all the sessions and release the allocated resources */ kernel_delete_all_sessions(slotID, B_FALSE); return (CKR_OK); } /* * Utility routine to get CK_STATE value for a session. * The caller should not be holding the session lock. */ static CK_STATE get_ses_state(kernel_session_t *session_p) { CK_STATE state; kernel_slot_t *pslot; pslot = slot_table[session_p->ses_slotid]; (void) pthread_mutex_lock(&pslot->sl_mutex); if (pslot->sl_state == CKU_PUBLIC) { state = (session_p->ses_RO) ? CKS_RO_PUBLIC_SESSION : CKS_RW_PUBLIC_SESSION; } else if (pslot->sl_state == CKU_USER) { state = (session_p->ses_RO) ? CKS_RO_USER_FUNCTIONS : CKS_RW_USER_FUNCTIONS; } else if (pslot->sl_state == CKU_SO) { state = CKS_RW_SO_FUNCTIONS; } (void) pthread_mutex_unlock(&pslot->sl_mutex); return (state); } CK_RV C_GetSessionInfo(CK_SESSION_HANDLE hSession, CK_SESSION_INFO_PTR pInfo) { kernel_session_t *session_p; CK_RV rv; boolean_t ses_lock_held = B_FALSE; if (!kernel_initialized) return (CKR_CRYPTOKI_NOT_INITIALIZED); if (pInfo == NULL) return (CKR_ARGUMENTS_BAD); /* * Obtain the session pointer. Also, increment the session * reference count. */ rv = handle2session(hSession, &session_p); if (rv != CKR_OK) return (rv); /* Provide information for the specified session */ pInfo->slotID = session_p->ses_slotid; pInfo->flags = session_p->flags; pInfo->ulDeviceError = 0; pInfo->state = get_ses_state(session_p); /* * Decrement the session reference count. */ REFRELE(session_p, ses_lock_held); return (CKR_OK); } /* * Save the state in pOperationState. The data format is: * 1. Total length (including this field) * 2. session state * 3. crypto_active_op_t structure * 4. digest_buf_t's data buffer contents */ static CK_RV kernel_get_operationstate(kernel_session_t *session_p, CK_STATE ses_state, CK_BYTE_PTR pOperationState, CK_ULONG_PTR pulOperationStateLen) { int op_data_len = 0; CK_BYTE_PTR dst; digest_buf_t *bufp; if (!(session_p->digest.flags & CRYPTO_EMULATE)) { /* * Return CKR_OPERATION_NOT_INITIALIZED if the slot * is capable of C_GetOperationState(). Return * CKR_FUNCTION_NOT_SUPPORTED otherwise. * * We return these codes because some clients * check the return code to determine if C_GetOperationState() * is supported. */ if (slot_table[session_p->ses_slotid]->sl_flags & CRYPTO_LIMITED_HASH_SUPPORT) return (CKR_OPERATION_NOT_INITIALIZED); else return (CKR_FUNCTION_NOT_SUPPORTED); } /* * XXX Need to support this case in future. * This is the case where we exceeded SLOT_HASH_MAX_INDATA_LEN and * hence started using libmd. SLOT_HASH_MAX_INDATA_LEN is at least * 64K for current crypto framework providers and web servers * do not need to clone digests that big for SSL operations. */ if (session_p->digest.flags & CRYPTO_EMULATE_USING_SW) { return (CKR_STATE_UNSAVEABLE); } /* Check to see if this is an unsupported operation. */ if (session_p->encrypt.flags & CRYPTO_OPERATION_ACTIVE || session_p->decrypt.flags & CRYPTO_OPERATION_ACTIVE || session_p->sign.flags & CRYPTO_OPERATION_ACTIVE || session_p->verify.flags & CRYPTO_OPERATION_ACTIVE) { return (CKR_STATE_UNSAVEABLE); } /* Check to see if digest operation is active. */ if (!(session_p->digest.flags & CRYPTO_OPERATION_ACTIVE)) { return (CKR_OPERATION_NOT_INITIALIZED); } bufp = session_p->digest.context; op_data_len = sizeof (int); op_data_len += sizeof (CK_STATE); op_data_len += sizeof (crypto_active_op_t); op_data_len += bufp->indata_len; if (pOperationState == NULL_PTR) { *pulOperationStateLen = op_data_len; return (CKR_OK); } else { if (*pulOperationStateLen < op_data_len) { *pulOperationStateLen = op_data_len; return (CKR_BUFFER_TOO_SMALL); } } dst = pOperationState; /* Save total length */ bcopy(&op_data_len, dst, sizeof (int)); dst += sizeof (int); /* Save session state */ bcopy(&ses_state, dst, sizeof (CK_STATE)); dst += sizeof (CK_STATE); /* Save crypto_active_op_t */ bcopy(&session_p->digest, dst, sizeof (crypto_active_op_t)); dst += sizeof (crypto_active_op_t); /* Save the data buffer */ bcopy(bufp->buf, dst, bufp->indata_len); *pulOperationStateLen = op_data_len; return (CKR_OK); } CK_RV C_GetOperationState(CK_SESSION_HANDLE hSession, CK_BYTE_PTR pOperationState, CK_ULONG_PTR pulOperationStateLen) { CK_RV rv; CK_STATE ses_state; kernel_session_t *session_p; boolean_t ses_lock_held = B_TRUE; if (!kernel_initialized) return (CKR_CRYPTOKI_NOT_INITIALIZED); if (pulOperationStateLen == NULL_PTR) return (CKR_ARGUMENTS_BAD); /* * Obtain the session pointer. Also, increment the session * reference count. */ rv = handle2session(hSession, &session_p); if (rv != CKR_OK) return (rv); ses_state = get_ses_state(session_p); (void) pthread_mutex_lock(&session_p->session_mutex); rv = kernel_get_operationstate(session_p, ses_state, pOperationState, pulOperationStateLen); REFRELE(session_p, ses_lock_held); return (rv); } /* * Restore the state from pOperationState. The data format is: * 1. Total length (including this field) * 2. session state * 3. crypto_active_op_t structure * 4. digest_buf_t's data buffer contents */ static CK_RV kernel_set_operationstate(kernel_session_t *session_p, CK_STATE ses_state, CK_BYTE_PTR pOperationState, CK_ULONG ulOperationStateLen, CK_OBJECT_HANDLE hEncryptionKey, CK_OBJECT_HANDLE hAuthenticationKey) { CK_RV rv; CK_BYTE_PTR src; CK_STATE src_ses_state; int expected_len, indata_len; digest_buf_t *bufp; crypto_active_op_t tmp_op; if ((hAuthenticationKey != 0) || (hEncryptionKey != 0)) return (CKR_KEY_NOT_NEEDED); src = pOperationState; /* Get total length field */ bcopy(src, &expected_len, sizeof (int)); if (ulOperationStateLen < expected_len) return (CKR_SAVED_STATE_INVALID); /* compute the data buffer length */ indata_len = expected_len - sizeof (int) - sizeof (CK_STATE) - sizeof (crypto_active_op_t); if (indata_len > SLOT_HASH_MAX_INDATA_LEN(session_p)) return (CKR_SAVED_STATE_INVALID); src += sizeof (int); /* Get session state */ bcopy(src, &src_ses_state, sizeof (CK_STATE)); if (ses_state != src_ses_state) return (CKR_SAVED_STATE_INVALID); src += sizeof (CK_STATE); /* * Restore crypto_active_op_t. We need to use a temporary * buffer to avoid modifying the source session's buffer. */ bcopy(src, &tmp_op, sizeof (crypto_active_op_t)); if (tmp_op.flags & CRYPTO_EMULATE_USING_SW) return (CKR_SAVED_STATE_INVALID); session_p->digest.mech = tmp_op.mech; session_p->digest.flags = tmp_op.flags; src += sizeof (crypto_active_op_t); /* This routine reuses the session's existing buffer if possible */ rv = emulate_buf_init(session_p, indata_len, OP_DIGEST); if (rv != CKR_OK) return (rv); bufp = session_p->digest.context; bufp->indata_len = indata_len; /* Restore the data buffer */ bcopy(src, bufp->buf, bufp->indata_len); return (CKR_OK); } CK_RV C_SetOperationState(CK_SESSION_HANDLE hSession, CK_BYTE_PTR pOperationState, CK_ULONG ulOperationStateLen, CK_OBJECT_HANDLE hEncryptionKey, CK_OBJECT_HANDLE hAuthenticationKey) { CK_RV rv; CK_STATE ses_state; kernel_session_t *session_p; boolean_t ses_lock_held = B_TRUE; if (!kernel_initialized) return (CKR_CRYPTOKI_NOT_INITIALIZED); if ((pOperationState == NULL_PTR) || (ulOperationStateLen == 0)) return (CKR_ARGUMENTS_BAD); rv = handle2session(hSession, &session_p); if (rv != CKR_OK) return (rv); ses_state = get_ses_state(session_p); (void) pthread_mutex_lock(&session_p->session_mutex); rv = kernel_set_operationstate(session_p, ses_state, pOperationState, ulOperationStateLen, hEncryptionKey, hAuthenticationKey); REFRELE(session_p, ses_lock_held); return (rv); } CK_RV C_Login(CK_SESSION_HANDLE hSession, CK_USER_TYPE userType, CK_UTF8CHAR_PTR pPin, CK_ULONG ulPinLen) { CK_RV rv = CKR_OK; kernel_session_t *session_p; kernel_slot_t *pslot; boolean_t ses_lock_held = B_FALSE; crypto_login_t c_login; int r; if (!kernel_initialized) return (CKR_CRYPTOKI_NOT_INITIALIZED); if ((userType != CKU_SO) && (userType != CKU_USER)) { return (CKR_USER_TYPE_INVALID); } /* * Obtain the session pointer. Also, increment the session * reference count. */ rv = handle2session(hSession, &session_p); if (rv != CKR_OK) return (rv); /* Acquire the slot lock */ pslot = slot_table[session_p->ses_slotid]; (void) pthread_mutex_lock(&pslot->sl_mutex); /* Check if the slot is logged in already */ if ((pslot->sl_state == CKU_USER) || (pslot->sl_state == CKU_SO)) { rv = CKR_USER_ALREADY_LOGGED_IN; goto clean_exit; } /* To login as SO, every session in this slot needs to be R/W */ if (userType == CKU_SO) { kernel_session_t *sp; boolean_t found; found = B_FALSE; sp = pslot->sl_sess_list; while (sp) { /* * Need not to lock individual sessions before * accessing their "ses_RO" and "next" fields, * because they are always accessed under the * slot's mutex protection. */ if (sp->ses_RO) { found = B_TRUE; break; } sp = sp->next; } if (found) { rv = CKR_SESSION_READ_ONLY_EXISTS; goto clean_exit; } } /* Now make the ioctl call; no need to acquire the session lock. */ c_login.co_session = session_p->k_session; c_login.co_user_type = userType; c_login.co_pin_len = ulPinLen; c_login.co_pin = (char *)pPin; while ((r = ioctl(kernel_fd, CRYPTO_LOGIN, &c_login)) < 0) { if (errno != EINTR) break; } if (r < 0) { rv = CKR_FUNCTION_FAILED; } else { rv = crypto2pkcs11_error_number(c_login.co_return_value); } if (rv == CKR_OK) { /* Set the slot's session state. */ pslot->sl_state = userType; } clean_exit: REFRELE(session_p, ses_lock_held); (void) pthread_mutex_unlock(&pslot->sl_mutex); return (rv); } CK_RV C_Logout(CK_SESSION_HANDLE hSession) { CK_RV rv = CKR_OK; kernel_session_t *session_p; kernel_slot_t *pslot; boolean_t ses_lock_held = B_FALSE; crypto_logout_t c_logout; int r; if (!kernel_initialized) return (CKR_CRYPTOKI_NOT_INITIALIZED); /* * Obtain the session pointer. Also, increment the session * reference count. */ rv = handle2session(hSession, &session_p); if (rv != CKR_OK) return (rv); /* Acquire the slot lock. */ pslot = slot_table[session_p->ses_slotid]; (void) pthread_mutex_lock(&pslot->sl_mutex); /* Check if the user or SO was logged in */ if (pslot->sl_state == CKU_PUBLIC) { rv = CKR_USER_NOT_LOGGED_IN; goto clean_exit; } /* Now make the ioctl call. No need to acquire the session lock. */ c_logout.cl_session = session_p->k_session; while ((r = ioctl(kernel_fd, CRYPTO_LOGOUT, &c_logout)) < 0) { if (errno != EINTR) break; } if (r < 0) { rv = CKR_FUNCTION_FAILED; } else { rv = crypto2pkcs11_error_number(c_logout.cl_return_value); } if (rv != CKR_OK) { goto clean_exit; } /* * If this slot was logged in as USER previously, we need to clean up * all private object wrappers in library for this slot. */ kernel_cleanup_pri_objects_in_slot(pslot, session_p); if (rv == CKR_OK) { /* Reset the slot's session state. */ pslot->sl_state = CKU_PUBLIC; } clean_exit: REFRELE(session_p, ses_lock_held); (void) pthread_mutex_unlock(&pslot->sl_mutex); return (rv); } /* * 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. */ #ifndef _KERNELSESSION_H #define _KERNELSESSION_H #ifdef __cplusplus extern "C" { #endif #include #include #include #define KERNELTOKEN_SESSION_MAGIC 0xECF00003 typedef struct crypto_active_op { CK_MECHANISM mech; void *context; uint32_t flags; } crypto_active_op_t; #define EDIGEST_LENGTH 1024 /* Used for emulating digest and HMAC mechs */ typedef struct digest_buf { uint8_t *buf; int buf_len; int indata_len; void *soft_sp; } digest_buf_t; /* * Definition for flags in crypto_active_op_t * * CRYPTO_EMULATE flag is set for a digest or sign/verify with a HMAC * mechanism, if the session slot has a CRYPTO_LIMITED_HASH_SUPPORT flag set. * CRYPTO_EMULATE_USING_SW flag is meaningful only when CRYPTO_EMULATE flag * is set. And CRYPTO_EMULATE_UPDATE_DONE flag is meaningful only when * CRYPTO_EMULATE_USING_SW flag is set. */ #define CRYPTO_OPERATION_ACTIVE 0x00000001 /* Cryptoki operation is active */ #define CRYPTO_OPERATION_UPDATE 0x00000002 /* Cryptoki multi-part op active */ #define CRYPTO_EMULATE 0x00000004 /* op needs emulation */ #define CRYPTO_EMULATE_USING_SW 0x00000008 /* ... use software */ #define CRYPTO_EMULATE_UPDATE_DONE 0x00000010 /* did at least one update */ #define CRYPTO_EMULATE_INIT_DONE 0x00000020 /* did init */ #define CRYPTO_OPERATION_INPLACE_OK 0x00000040 /* INPLACE_MECHANISM is true */ typedef struct session { CK_ULONG magic_marker; /* magic # be validated for integrity */ pthread_mutex_t session_mutex; /* session's mutex lock */ pthread_mutex_t ses_free_mutex; /* mutex used during closing session */ pthread_cond_t ses_free_cond; /* cond variable for signal and wait */ uint32_t ses_refcnt; /* session reference count */ uint32_t ses_close_sync; /* session closing flags */ crypto_session_id_t k_session; /* kernel session ID */ boolean_t ses_RO; /* RO or RW session flag */ CK_SLOT_ID ses_slotid; /* slotID saved from C_OpenSession() */ /* Place holder for parameters passed in the C_OpenSession */ CK_FLAGS flags; CK_NOTIFY Notify; CK_VOID_PTR pApplication; /* Pointers to form the global session list */ struct session *next; /* points to next session on the list */ struct session *prev; /* points to prev session on the list */ struct object *object_list; /* points to list of objects */ crypto_active_op_t digest; /* context of active digest operation */ crypto_active_op_t encrypt; /* context of active encrypt op */ crypto_active_op_t decrypt; /* context of active decrypt op */ crypto_active_op_t sign; /* context of active sign op */ crypto_active_op_t verify; /* context of active verify op */ crypto_active_op_t find_objects; } kernel_session_t; /* * The following structure is used to link the to-be-freed sessions * into a linked list. The sessions on this linked list have * not yet been freed via free() after C_CloseSession() call; instead * they are added to this list. The actual free will take place when * the number of sessions queued reaches MAX_SES_TO_BE_FREED, at which * time the first session in the list will be freed. */ #define MAX_SES_TO_BE_FREED 300 typedef struct ses_to_be_freed_list { kernel_session_t *first; /* points to the first session in the list */ kernel_session_t *last; /* points to the last session in the list */ uint32_t count; /* current total sessions in the list */ pthread_mutex_t ses_to_be_free_mutex; } ses_to_be_freed_list_t; extern ses_to_be_freed_list_t ses_delay_freed; /* * Flag definitions for ses_close_sync */ #define SESSION_IS_CLOSING 1 /* Session is in a closing state */ #define SESSION_REFCNT_WAITING 2 /* Waiting for session reference */ /* count to become zero */ /* * This macro is used to decrement the session reference count by one. * * The caller of this macro uses the argument lock_held to indicate that * whether the caller holds the lock on the session or not. * * REFRELE macro does the following: * 1) Get the session lock if the caller does not hold it. * 2) Decrement the session reference count by one. * 3) If the session reference count becomes zero after being decremented, * and there is a closing session thread in the wait state, then * call pthread_cond_signal() to wake up that thread who is blocked * in the session deletion routine due to non-zero reference ount. * 4) Always release the session lock. */ #define REFRELE(s, ses_lock_held) { \ if (!ses_lock_held) \ (void) pthread_mutex_lock(&s->session_mutex); \ if ((--((s)->ses_refcnt) == 0) && \ (s->ses_close_sync & SESSION_REFCNT_WAITING)) { \ (void) pthread_mutex_unlock(&s->session_mutex); \ (void) pthread_cond_signal(&s->ses_free_cond); \ } else { \ (void) pthread_mutex_unlock(&s->session_mutex); \ } \ } /* * Function Prototypes. */ CK_RV handle2session(CK_SESSION_HANDLE hSession, kernel_session_t **session_p); void kernel_delete_all_sessions(CK_SLOT_ID slotID, boolean_t wrapper_only); void kernel_delete_all_objects_in_session(kernel_session_t *sp, boolean_t wrapper_only); CK_RV kernel_add_session(CK_SLOT_ID slotID, CK_FLAGS flags, CK_VOID_PTR pApplication, CK_NOTIFY notify, CK_ULONG *phSession); void kernel_delete_session(CK_SLOT_ID slotID, kernel_session_t *sp, boolean_t lock_held, boolean_t wrapper_only); void kernel_session_delay_free(kernel_session_t *sp); void kernel_acquire_all_slots_mutexes(); void kernel_release_all_slots_mutexes(); #ifdef __cplusplus } #endif #endif /* _KERNELSESSION_H */ /* * 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. */ #include #include #include #include #include #include #include #include "kernelGlobal.h" #include "kernelSession.h" #include "kernelSlot.h" #include "kernelEmulate.h" static pthread_mutex_t delete_sessions_mutex = PTHREAD_MUTEX_INITIALIZER; /* * Delete all the sessions. First, obtain the slot lock. * Then start to delete one session at a time. The boolean wrapper_only * argument indicates that whether the caller only wants to clean up the * session wrappers and the object wrappers in the library. * - When this function is called by C_CloseAllSessions or indirectly by * C_Finalize, wrapper_only is FALSE. * - When this function is called by cleanup_child, wrapper_only is TRUE. */ void kernel_delete_all_sessions(CK_SLOT_ID slotID, boolean_t wrapper_only) { kernel_session_t *session_p; kernel_slot_t *pslot; (void) pthread_mutex_lock(&delete_sessions_mutex); pslot = slot_table[slotID]; /* * Delete all the sessions in the slot's session list. * The routine kernel_delete_session() updates the linked list. * So, we do not need to maintain the list here. */ for (;;) { (void) pthread_mutex_lock(&pslot->sl_mutex); if (pslot->sl_sess_list == NULL) break; session_p = pslot->sl_sess_list; /* * Set SESSION_IS_CLOSING flag so any access to this * session will be rejected. */ (void) pthread_mutex_lock(&session_p->session_mutex); if (session_p->ses_close_sync & SESSION_IS_CLOSING) { (void) pthread_mutex_unlock(&session_p->session_mutex); continue; } session_p->ses_close_sync |= SESSION_IS_CLOSING; (void) pthread_mutex_unlock(&session_p->session_mutex); (void) pthread_mutex_unlock(&pslot->sl_mutex); kernel_delete_session(slotID, session_p, B_FALSE, wrapper_only); } (void) pthread_mutex_unlock(&pslot->sl_mutex); (void) pthread_mutex_unlock(&delete_sessions_mutex); } /* * Create a new session struct, and add it to the slot's session list. * * This function is called by C_OpenSession(), which hold the slot lock. */ CK_RV kernel_add_session(CK_SLOT_ID slotID, CK_FLAGS flags, CK_VOID_PTR pApplication, CK_NOTIFY notify, CK_ULONG *sessionhandle_p) { CK_RV rv = CKR_OK; kernel_session_t *new_sp = NULL; crypto_open_session_t open_session; kernel_slot_t *pslot; int r; /* Allocate a new session struct */ new_sp = calloc(1, sizeof (kernel_session_t)); if (new_sp == NULL) { return (CKR_HOST_MEMORY); } new_sp->magic_marker = KERNELTOKEN_SESSION_MAGIC; new_sp->pApplication = pApplication; new_sp->Notify = notify; new_sp->flags = flags; new_sp->ses_RO = (flags & CKF_RW_SESSION) ? B_FALSE : B_TRUE; new_sp->ses_slotid = slotID; new_sp->object_list = NULL; new_sp->ses_refcnt = 0; new_sp->ses_close_sync = 0; /* Initialize the lock for the newly created session */ if (pthread_mutex_init(&new_sp->session_mutex, NULL) != 0) { free(new_sp); return (CKR_CANT_LOCK); } pslot = slot_table[slotID]; open_session.os_provider_id = pslot->sl_provider_id; open_session.os_flags = flags; while ((r = ioctl(kernel_fd, CRYPTO_OPEN_SESSION, &open_session)) < 0) { if (errno != EINTR) break; } if (r < 0) { rv = CKR_FUNCTION_FAILED; } else { rv = crypto2pkcs11_error_number(open_session.os_return_value); } if (rv != CKR_OK) { (void) pthread_mutex_destroy(&new_sp->session_mutex); free(new_sp); return (rv); } new_sp->k_session = open_session.os_session; (void) pthread_mutex_init(&new_sp->ses_free_mutex, NULL); (void) pthread_cond_init(&new_sp->ses_free_cond, NULL); /* Insert the new session in front of the slot's session list */ if (pslot->sl_sess_list == NULL) { pslot->sl_sess_list = new_sp; new_sp->prev = NULL; new_sp->next = NULL; } else { pslot->sl_sess_list->prev = new_sp; new_sp->next = pslot->sl_sess_list; new_sp->prev = NULL; pslot->sl_sess_list = new_sp; } /* Type casting the address of a session struct to a session handle */ *sessionhandle_p = (CK_ULONG)new_sp; return (CKR_OK); } /* * Delete a session: * - Remove the session from the slot's session list. * - Release all the objects created by the session. * * The boolean argument slot_lock_held is used to indicate that whether * the caller of this function holds the slot lock or not. * - When called by kernel_delete_all_sessions(), which is called by * C_Finalize() or C_CloseAllSessions() -- slot_lock_held = TRUE. * - When called by C_CloseSession() -- slot_lock_held = FALSE. */ void kernel_delete_session(CK_SLOT_ID slotID, kernel_session_t *session_p, boolean_t slot_lock_held, boolean_t wrapper_only) { crypto_session_id_t k_session; crypto_close_session_t close_session; kernel_slot_t *pslot; kernel_object_t *objp; kernel_object_t *objp1; /* * Check to see if the caller holds the lock on the global * session list. If not, we need to acquire that lock in * order to proceed. */ pslot = slot_table[slotID]; if (!slot_lock_held) { /* Acquire the slot lock */ (void) pthread_mutex_lock(&pslot->sl_mutex); } /* * Remove the session from the slot's session list first. */ if (pslot->sl_sess_list == session_p) { /* Session is the first one in the list */ if (session_p->next) { pslot->sl_sess_list = session_p->next; session_p->next->prev = NULL; } else { /* Session is the only one in the list */ pslot->sl_sess_list = NULL; } } else { /* Session is not the first one in the list */ if (session_p->next) { /* Session is in the middle of the list */ session_p->prev->next = session_p->next; session_p->next->prev = session_p->prev; } else { /* Session is the last one in the list */ session_p->prev->next = NULL; } } if (!slot_lock_held) { /* * If the slot lock is obtained by * this function, then release that lock after * removing the session from session linked list. * We want the releasing of the objects of the * session, and freeing of the session itself to * be done without holding the slot's session list * lock. */ (void) pthread_mutex_unlock(&pslot->sl_mutex); } /* Acquire the individual session lock */ (void) pthread_mutex_lock(&session_p->session_mutex); /* * Make sure another thread hasn't freed the session. */ if (session_p->magic_marker != KERNELTOKEN_SESSION_MAGIC) { (void) pthread_mutex_unlock(&session_p->session_mutex); return; } /* * The deletion of a session must be blocked when the session reference * count is not zero. This means that if the thread that is attempting * to close the session must wait until the prior operations on this * session are finished. * * Unless we are being forced to shut everything down, this only * happens if the library's _fini() is running not if someone * explicitly called C_Finalize(). */ (void) pthread_mutex_lock(&session_p->ses_free_mutex); if (wrapper_only) { session_p->ses_refcnt = 0; } while (session_p->ses_refcnt != 0) { /* * We set the SESSION_REFCNT_WAITING flag before we put * this closing thread in a wait state, so other non-closing * operation thread will wake it up only when * the session reference count becomes zero and this flag * is set. */ session_p->ses_close_sync |= SESSION_REFCNT_WAITING; (void) pthread_mutex_unlock(&session_p->session_mutex); (void) pthread_cond_wait(&session_p->ses_free_cond, &session_p->ses_free_mutex); (void) pthread_mutex_lock(&session_p->session_mutex); } session_p->ses_close_sync &= ~SESSION_REFCNT_WAITING; /* Mark session as no longer valid. */ session_p->magic_marker = 0; (void) pthread_mutex_unlock(&session_p->ses_free_mutex); (void) pthread_mutex_destroy(&session_p->ses_free_mutex); (void) pthread_cond_destroy(&session_p->ses_free_cond); /* * Remove all the objects created in this session, waiting * until each object's refcnt is 0. */ kernel_delete_all_objects_in_session(session_p, wrapper_only); /* In case application did not call Final */ if (session_p->digest.context != NULL) { digest_buf_t *bufp = session_p->digest.context; if (bufp->buf != NULL) { free_soft_ctx(get_sp(&session_p->digest), OP_DIGEST); bzero(bufp->buf, bufp->indata_len); free(bufp->buf); } free(bufp); } if (session_p->encrypt.context != NULL) free(session_p->encrypt.context); if (session_p->decrypt.context != NULL) free(session_p->decrypt.context); if (session_p->sign.context != NULL) { digest_buf_t *bufp = session_p->sign.context; if (bufp->buf != NULL) { free_soft_ctx(get_sp(&session_p->sign), OP_SIGN); bzero(bufp->buf, bufp->indata_len); free(bufp->buf); } free(bufp); } if (session_p->verify.context != NULL) { digest_buf_t *bufp = session_p->verify.context; if (bufp->buf != NULL) { free_soft_ctx(get_sp(&session_p->verify), OP_VERIFY); bzero(bufp->buf, bufp->indata_len); free(bufp->buf); } free(bufp); } k_session = session_p->k_session; /* Reset SESSION_IS_CLOSING flag. */ session_p->ses_close_sync &= ~SESSION_IS_CLOSING; (void) pthread_mutex_unlock(&session_p->session_mutex); /* Destroy the individual session lock */ (void) pthread_mutex_destroy(&session_p->session_mutex); if (!wrapper_only) { close_session.cs_session = k_session; while (ioctl(kernel_fd, CRYPTO_CLOSE_SESSION, &close_session) < 0) { if (errno != EINTR) break; } /* * Ignore ioctl return codes. If the library tells the kernel * to close a session and the kernel says "I don't know what * session you're talking about", there's not much that can be * done. All sessions in the kernel will be closed when the * application exits and closes /dev/crypto. */ } kernel_session_delay_free(session_p); /* * If there is no more session remained in this slot, reset the slot's * session state to CKU_PUBLIC. Also, clean up all the token object * wrappers in the library for this slot. */ /* Acquire the slot lock if lock is not held */ if (!slot_lock_held) { (void) pthread_mutex_lock(&pslot->sl_mutex); } if (pslot->sl_sess_list == NULL) { /* Reset the session auth state. */ pslot->sl_state = CKU_PUBLIC; /* Clean up token object wrappers. */ objp = pslot->sl_tobj_list; while (objp) { objp1 = objp->next; (void) pthread_mutex_destroy(&objp->object_mutex); (void) kernel_object_delay_free(objp); objp = objp1; } pslot->sl_tobj_list = NULL; } /* Release the slot lock if lock is not held */ if (!slot_lock_held) { (void) pthread_mutex_unlock(&pslot->sl_mutex); } } /* * This function is used to type cast a session handle to a pointer to * the session struct. Also, it does the following things: * 1) Check to see if the session struct is tagged with a session * magic number. This is to detect when an application passes * a bogus session pointer. * 2) Acquire the locks on the designated session. * 3) Check to see if the session is in the closing state that another * thread is performing. * 4) Increment the session reference count by one. This is to prevent * this session from being closed by other thread. * 5) Release the locks on the designated session. */ CK_RV handle2session(CK_SESSION_HANDLE hSession, kernel_session_t **session_p) { kernel_session_t *sp = (kernel_session_t *)(hSession); CK_RV rv; if ((sp == NULL) || (sp->magic_marker != KERNELTOKEN_SESSION_MAGIC)) { return (CKR_SESSION_HANDLE_INVALID); } else { (void) pthread_mutex_lock(&sp->session_mutex); if (sp->ses_close_sync & SESSION_IS_CLOSING) { rv = CKR_SESSION_CLOSED; } else { /* Increment session ref count. */ sp->ses_refcnt++; rv = CKR_OK; } (void) pthread_mutex_unlock(&sp->session_mutex); } if (rv == CKR_OK) *session_p = sp; return (rv); } /* * This function adds the to-be-freed session to a linked list. * When the number of sessions queued in the linked list reaches the * maximum threshold MAX_SES_TO_BE_FREED, it will free the first * session (FIFO) in the list. */ void kernel_session_delay_free(kernel_session_t *sp) { kernel_session_t *tmp; (void) pthread_mutex_lock(&ses_delay_freed.ses_to_be_free_mutex); /* Add the newly deleted session at the end of the list */ sp->next = NULL; if (ses_delay_freed.first == NULL) { ses_delay_freed.last = sp; ses_delay_freed.first = sp; } else { ses_delay_freed.last->next = sp; ses_delay_freed.last = sp; } if (++ses_delay_freed.count >= MAX_SES_TO_BE_FREED) { /* * Free the first session in the list only if * the total count reaches maximum threshold. */ ses_delay_freed.count--; tmp = ses_delay_freed.first->next; free(ses_delay_freed.first); ses_delay_freed.first = tmp; } (void) pthread_mutex_unlock(&ses_delay_freed.ses_to_be_free_mutex); } /* * Acquire all slots' mutexes and all their sessions' mutexes. * Order: * 1. delete_sessions_mutex * for each slot: * 2. pslot->sl_mutex * for each session: * 3. session_p->session_mutex * 4. session_p->ses_free_mutex */ void kernel_acquire_all_slots_mutexes() { int slotID; kernel_slot_t *pslot; kernel_session_t *session_p; (void) pthread_mutex_lock(&delete_sessions_mutex); for (slotID = 0; slotID < slot_count; slotID++) { pslot = slot_table[slotID]; (void) pthread_mutex_lock(&pslot->sl_mutex); /* Iterate through sessions acquiring all mutexes */ session_p = pslot->sl_sess_list; while (session_p) { struct object *objp; (void) pthread_mutex_lock(&session_p->session_mutex); (void) pthread_mutex_lock(&session_p->ses_free_mutex); objp = session_p->object_list; while (objp) { (void) pthread_mutex_lock(&objp->object_mutex); objp = objp->next; } session_p = session_p->next; } } } /* Release in opposite order to kernel_acquire_all_slots_mutexes(). */ void kernel_release_all_slots_mutexes() { int slotID; kernel_slot_t *pslot; kernel_session_t *session_p; for (slotID = 0; slotID < slot_count; slotID++) { pslot = slot_table[slotID]; /* Iterate through sessions releasing all mutexes */ session_p = pslot->sl_sess_list; while (session_p) { struct object *objp; objp = session_p->object_list; while (objp) { (void) pthread_mutex_unlock( &objp->object_mutex); objp = objp->next; } (void) pthread_mutex_unlock(&session_p->ses_free_mutex); (void) pthread_mutex_unlock(&session_p->session_mutex); session_p = session_p->next; } (void) pthread_mutex_unlock(&pslot->sl_mutex); } (void) pthread_mutex_unlock(&delete_sessions_mutex); } /* * 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. */ #include #include #include #include "kernelGlobal.h" #include "kernelObject.h" #include "kernelSession.h" #include "kernelEmulate.h" CK_RV C_SignInit(CK_SESSION_HANDLE hSession, CK_MECHANISM_PTR pMechanism, CK_OBJECT_HANDLE hKey) { CK_RV rv; kernel_session_t *session_p; kernel_object_t *key_p; boolean_t ses_lock_held = B_FALSE; crypto_sign_init_t sign_init; crypto_mech_type_t k_mech_type; int r; if (!kernel_initialized) return (CKR_CRYPTOKI_NOT_INITIALIZED); if (pMechanism == NULL) { return (CKR_ARGUMENTS_BAD); } /* Get the kernel's internal mechanism number. */ rv = kernel_mech(pMechanism->mechanism, &k_mech_type); if (rv != CKR_OK) { return (rv); } /* Obtain the session pointer. */ rv = handle2session(hSession, &session_p); if (rv != CKR_OK) return (rv); /* Obtain the object pointer. */ HANDLE2OBJECT(hKey, key_p, rv); if (rv != CKR_OK) { REFRELE(session_p, ses_lock_held); return (rv); } /* Check to see if key object supports signature. */ if (key_p->is_lib_obj && !(key_p->bool_attr_mask & SIGN_BOOL_ON)) { rv = CKR_KEY_TYPE_INCONSISTENT; goto clean_exit; } (void) pthread_mutex_lock(&session_p->session_mutex); ses_lock_held = B_TRUE; /* * This active flag will remain ON until application calls either * C_Sign or C_SignFinal to actually obtain the signature. */ session_p->sign.flags = CRYPTO_OPERATION_ACTIVE; sign_init.si_session = session_p->k_session; (void) pthread_mutex_unlock(&session_p->session_mutex); ses_lock_held = B_FALSE; if (!key_p->is_lib_obj) { sign_init.si_key.ck_format = CRYPTO_KEY_REFERENCE; sign_init.si_key.ck_obj_id = key_p->k_handle; } else { if (key_p->class == CKO_SECRET_KEY) { sign_init.si_key.ck_format = CRYPTO_KEY_RAW; sign_init.si_key.ck_data = get_symmetric_key_value(key_p); if (sign_init.si_key.ck_data == NULL) { rv = CKR_HOST_MEMORY; goto clean_exit; } sign_init.si_key.ck_length = OBJ_SEC(key_p)->sk_value_len << 3; } else if (key_p->key_type == CKK_RSA) { rv = get_rsa_private_key(key_p, &sign_init.si_key); if (rv != CKR_OK) { goto clean_exit; } } else if (key_p->key_type == CKK_DSA) { rv = get_dsa_private_key(key_p, &sign_init.si_key); if (rv != CKR_OK) { goto clean_exit; } } else if (key_p->key_type == CKK_EC) { rv = get_ec_private_key(key_p, &sign_init.si_key); if (rv != CKR_OK) { goto clean_exit; } } else { rv = CKR_KEY_TYPE_INCONSISTENT; goto clean_exit; } } sign_init.si_mech.cm_type = k_mech_type; sign_init.si_mech.cm_param = pMechanism->pParameter; sign_init.si_mech.cm_param_len = pMechanism->ulParameterLen; while ((r = ioctl(kernel_fd, CRYPTO_SIGN_INIT, &sign_init)) < 0) { if (errno != EINTR) break; } if (r < 0) { rv = CKR_FUNCTION_FAILED; } else { rv = crypto2pkcs11_error_number(sign_init.si_return_value); } if (rv == CKR_OK && SLOT_HAS_LIMITED_HMAC(session_p) && is_hmac(pMechanism->mechanism)) { if (key_p->is_lib_obj && key_p->class == CKO_SECRET_KEY) { (void) pthread_mutex_lock(&session_p->session_mutex); session_p->sign.flags |= CRYPTO_EMULATE; (void) pthread_mutex_unlock(&session_p->session_mutex); rv = emulate_init(session_p, pMechanism, &(sign_init.si_key), OP_SIGN); } else { rv = CKR_ARGUMENTS_BAD; } } if (key_p->is_lib_obj) { if (key_p->class == CKO_SECRET_KEY) { free(sign_init.si_key.ck_data); } else { free_key_attributes(&sign_init.si_key); } } if (rv != CKR_OK) { (void) pthread_mutex_lock(&session_p->session_mutex); session_p->sign.flags &= ~CRYPTO_OPERATION_ACTIVE; ses_lock_held = B_TRUE; } clean_exit: OBJ_REFRELE(key_p); REFRELE(session_p, ses_lock_held); return (rv); } CK_RV C_Sign(CK_SESSION_HANDLE hSession, CK_BYTE_PTR pData, CK_ULONG ulDataLen, CK_BYTE_PTR pSignature, CK_ULONG_PTR pulSignatureLen) { CK_RV rv; kernel_session_t *session_p; boolean_t ses_lock_held = B_FALSE; crypto_sign_t sign; int r; if (!kernel_initialized) return (CKR_CRYPTOKI_NOT_INITIALIZED); /* Obtain the session pointer */ rv = handle2session(hSession, &session_p); if (rv != CKR_OK) return (rv); if (pulSignatureLen == NULL) { rv = CKR_ARGUMENTS_BAD; goto clean_exit; } (void) pthread_mutex_lock(&session_p->session_mutex); ses_lock_held = B_TRUE; /* Application must call C_SignInit before calling C_Sign. */ if (!(session_p->sign.flags & CRYPTO_OPERATION_ACTIVE)) { REFRELE(session_p, ses_lock_held); return (CKR_OPERATION_NOT_INITIALIZED); } /* * C_Sign must be called without intervening C_SignUpdate * calls. */ if (session_p->sign.flags & CRYPTO_OPERATION_UPDATE) { /* * C_Sign can not be used to terminate a multi-part * operation, so we'll leave the active sign operation * flag on and let the application continue with the * sign update operation. */ REFRELE(session_p, ses_lock_held); return (CKR_FUNCTION_FAILED); } if (session_p->sign.flags & CRYPTO_EMULATE) { if ((ulDataLen < SLOT_THRESHOLD(session_p)) || (ulDataLen > SLOT_HMAC_MAX_INDATA_LEN(session_p))) { session_p->sign.flags |= CRYPTO_EMULATE_USING_SW; (void) pthread_mutex_unlock(&session_p->session_mutex); ses_lock_held = B_FALSE; rv = do_soft_hmac_sign(get_spp(&session_p->sign), pData, ulDataLen, pSignature, pulSignatureLen, OP_SINGLE); goto done; } else { free_soft_ctx(get_sp(&session_p->sign), OP_SIGN); } } sign.cs_session = session_p->k_session; (void) pthread_mutex_unlock(&session_p->session_mutex); ses_lock_held = B_FALSE; sign.cs_datalen = ulDataLen; sign.cs_databuf = (char *)pData; sign.cs_signlen = *pulSignatureLen; sign.cs_signbuf = (char *)pSignature; while ((r = ioctl(kernel_fd, CRYPTO_SIGN, &sign)) < 0) { if (errno != EINTR) break; } if (r < 0) { rv = CKR_FUNCTION_FAILED; } else { rv = crypto2pkcs11_error_number(sign.cs_return_value); } if (rv == CKR_OK || rv == CKR_BUFFER_TOO_SMALL) *pulSignatureLen = sign.cs_signlen; done: if ((rv == CKR_BUFFER_TOO_SMALL) || (rv == CKR_OK && pSignature == NULL)) { /* * We will not terminate the active sign operation flag, * when the application-supplied buffer is too small, or * the application asks for the length of buffer to hold * the signature. */ REFRELE(session_p, ses_lock_held); return (rv); } clean_exit: /* * Terminates the active sign operation. * Application needs to call C_SignInit again for next * sign operation. */ (void) pthread_mutex_lock(&session_p->session_mutex); ses_lock_held = B_TRUE; REINIT_OPBUF(&session_p->sign); session_p->sign.flags = 0; REFRELE(session_p, ses_lock_held); return (rv); } CK_RV C_SignUpdate(CK_SESSION_HANDLE hSession, CK_BYTE_PTR pPart, CK_ULONG ulPartLen) { CK_RV rv; kernel_session_t *session_p; boolean_t ses_lock_held = B_FALSE; crypto_sign_update_t sign_update; int r; if (!kernel_initialized) return (CKR_CRYPTOKI_NOT_INITIALIZED); /* Obtain the session pointer */ rv = handle2session(hSession, &session_p); if (rv != CKR_OK) return (rv); if (pPart == NULL) { rv = CKR_ARGUMENTS_BAD; goto clean_exit; } (void) pthread_mutex_lock(&session_p->session_mutex); ses_lock_held = B_TRUE; /* * Application must call C_SignInit before calling * C_SignUpdate. */ if (!(session_p->sign.flags & CRYPTO_OPERATION_ACTIVE)) { REFRELE(session_p, ses_lock_held); return (CKR_OPERATION_NOT_INITIALIZED); } session_p->sign.flags |= CRYPTO_OPERATION_UPDATE; if (session_p->sign.flags & CRYPTO_EMULATE) { (void) pthread_mutex_unlock(&session_p->session_mutex); ses_lock_held = B_FALSE; rv = emulate_update(session_p, pPart, ulPartLen, OP_SIGN); goto done; } sign_update.su_session = session_p->k_session; (void) pthread_mutex_unlock(&session_p->session_mutex); ses_lock_held = B_FALSE; sign_update.su_datalen = ulPartLen; sign_update.su_databuf = (char *)pPart; while ((r = ioctl(kernel_fd, CRYPTO_SIGN_UPDATE, &sign_update)) < 0) { if (errno != EINTR) break; } if (r < 0) { rv = CKR_FUNCTION_FAILED; } else { rv = crypto2pkcs11_error_number(sign_update.su_return_value); } done: if (rv == CKR_OK) { REFRELE(session_p, ses_lock_held); return (rv); } clean_exit: /* * After an error occurred, terminate the current sign * operation by resetting the active and update flags. */ (void) pthread_mutex_lock(&session_p->session_mutex); ses_lock_held = B_TRUE; REINIT_OPBUF(&session_p->sign); session_p->sign.flags = 0; REFRELE(session_p, ses_lock_held); return (rv); } CK_RV C_SignFinal(CK_SESSION_HANDLE hSession, CK_BYTE_PTR pSignature, CK_ULONG_PTR pulSignatureLen) { CK_RV rv; kernel_session_t *session_p; boolean_t ses_lock_held = B_FALSE; crypto_sign_final_t sign_final; int r; if (!kernel_initialized) return (CKR_CRYPTOKI_NOT_INITIALIZED); /* Obtain the session pointer */ rv = handle2session(hSession, &session_p); if (rv != CKR_OK) return (rv); if (pulSignatureLen == NULL) { rv = CKR_ARGUMENTS_BAD; goto clean_exit; } (void) pthread_mutex_lock(&session_p->session_mutex); ses_lock_held = B_TRUE; /* * Application must call C_SignInit before calling * C_SignFinal. */ if (!(session_p->sign.flags & CRYPTO_OPERATION_ACTIVE)) { REFRELE(session_p, ses_lock_held); return (CKR_OPERATION_NOT_INITIALIZED); } /* The order of checks is important here */ if (session_p->sign.flags & CRYPTO_EMULATE_USING_SW) { if (session_p->sign.flags & CRYPTO_EMULATE_UPDATE_DONE) { (void) pthread_mutex_unlock(&session_p->session_mutex); ses_lock_held = B_FALSE; rv = do_soft_hmac_sign(get_spp(&session_p->sign), NULL, 0, pSignature, pulSignatureLen, OP_FINAL); } else { /* * We end up here if an earlier C_SignFinal() call * took the C_Sign() path and it had returned * CKR_BUFFER_TOO_SMALL. */ digest_buf_t *bufp = session_p->sign.context; (void) pthread_mutex_unlock(&session_p->session_mutex); ses_lock_held = B_FALSE; if (bufp == NULL || bufp->buf == NULL) { rv = CKR_ARGUMENTS_BAD; goto clean_exit; } rv = do_soft_hmac_sign(get_spp(&session_p->sign), bufp->buf, bufp->indata_len, pSignature, pulSignatureLen, OP_SINGLE); } goto done; } else if (session_p->sign.flags & CRYPTO_EMULATE) { digest_buf_t *bufp = session_p->sign.context; /* * We are emulating a single-part operation now. * So, clear the flag. */ session_p->sign.flags &= ~CRYPTO_OPERATION_UPDATE; if (bufp == NULL || bufp->buf == NULL) { rv = CKR_ARGUMENTS_BAD; goto clean_exit; } REFRELE(session_p, ses_lock_held); rv = C_Sign(hSession, bufp->buf, bufp->indata_len, pSignature, pulSignatureLen); return (rv); } sign_final.sf_session = session_p->k_session; (void) pthread_mutex_unlock(&session_p->session_mutex); ses_lock_held = B_FALSE; sign_final.sf_signlen = *pulSignatureLen; sign_final.sf_signbuf = (char *)pSignature; while ((r = ioctl(kernel_fd, CRYPTO_SIGN_FINAL, &sign_final)) < 0) { if (errno != EINTR) break; } if (r < 0) { rv = CKR_FUNCTION_FAILED; } else { rv = crypto2pkcs11_error_number(sign_final.sf_return_value); } if (rv == CKR_OK || rv == CKR_BUFFER_TOO_SMALL) *pulSignatureLen = sign_final.sf_signlen; done: if ((rv == CKR_BUFFER_TOO_SMALL) || (rv == CKR_OK && pSignature == NULL)) { /* * We will not terminate the active sign operation flag, * when the application-supplied buffer is too small, or * the application asks for the length of buffer to hold * the signature. */ REFRELE(session_p, ses_lock_held); return (rv); } clean_exit: /* Terminates the active sign operation */ (void) pthread_mutex_lock(&session_p->session_mutex); ses_lock_held = B_TRUE; REINIT_OPBUF(&session_p->sign); session_p->sign.flags = 0; REFRELE(session_p, ses_lock_held); return (rv); } CK_RV C_SignRecoverInit(CK_SESSION_HANDLE hSession, CK_MECHANISM_PTR pMechanism, CK_OBJECT_HANDLE hKey) { CK_RV rv; kernel_session_t *session_p; kernel_object_t *key_p; boolean_t ses_lock_held = B_FALSE; crypto_sign_recover_init_t sr_init; crypto_mech_type_t k_mech_type; int r; if (!kernel_initialized) return (CKR_CRYPTOKI_NOT_INITIALIZED); if (pMechanism == NULL) { return (CKR_ARGUMENTS_BAD); } /* Get the kernel's internal mechanism number. */ rv = kernel_mech(pMechanism->mechanism, &k_mech_type); if (rv != CKR_OK) return (rv); /* Obtain the session pointer. */ rv = handle2session(hSession, &session_p); if (rv != CKR_OK) return (rv); /* Obtain the object pointer. */ HANDLE2OBJECT(hKey, key_p, rv); if (rv != CKR_OK) { REFRELE(session_p, ses_lock_held); return (rv); } /* * Check to see if key object is a RSA key and if it supports * sign_recover. */ if (key_p->is_lib_obj && !((key_p->key_type == CKK_RSA) && (key_p->bool_attr_mask & SIGN_RECOVER_BOOL_ON))) { rv = CKR_KEY_TYPE_INCONSISTENT; goto clean_exit; } (void) pthread_mutex_lock(&session_p->session_mutex); ses_lock_held = B_TRUE; /* * This active flag will remain ON until application calls * C_SignRecover to actually obtain the signature. */ session_p->sign.flags = CRYPTO_OPERATION_ACTIVE; /* Set up the key data */ if (!key_p->is_lib_obj) { sr_init.ri_key.ck_format = CRYPTO_KEY_REFERENCE; sr_init.ri_key.ck_obj_id = key_p->k_handle; } else { if (key_p->key_type == CKK_RSA) { if (get_rsa_private_key(key_p, &sr_init.ri_key) != CKR_OK) { rv = CKR_HOST_MEMORY; goto clean_exit; } } else { rv = CKR_KEY_TYPE_INCONSISTENT; goto clean_exit; } } sr_init.ri_session = session_p->k_session; (void) pthread_mutex_unlock(&session_p->session_mutex); ses_lock_held = B_FALSE; sr_init.ri_mech.cm_type = k_mech_type; sr_init.ri_mech.cm_param = pMechanism->pParameter; sr_init.ri_mech.cm_param_len = pMechanism->ulParameterLen; while ((r = ioctl(kernel_fd, CRYPTO_SIGN_RECOVER_INIT, &sr_init)) < 0) { if (errno != EINTR) break; } if (r < 0) { rv = CKR_FUNCTION_FAILED; } else { rv = crypto2pkcs11_error_number(sr_init.ri_return_value); } if (key_p->is_lib_obj) { free_key_attributes(&sr_init.ri_key); } if (rv != CKR_OK) { (void) pthread_mutex_lock(&session_p->session_mutex); session_p->sign.flags &= ~CRYPTO_OPERATION_ACTIVE; ses_lock_held = B_TRUE; } clean_exit: OBJ_REFRELE(key_p); REFRELE(session_p, ses_lock_held); return (rv); } CK_RV C_SignRecover(CK_SESSION_HANDLE hSession, CK_BYTE_PTR pData, CK_ULONG ulDataLen, CK_BYTE_PTR pSignature, CK_ULONG_PTR pulSignatureLen) { CK_RV rv; kernel_session_t *session_p; boolean_t ses_lock_held = B_FALSE; crypto_sign_recover_t sign_recover; int r; if (!kernel_initialized) return (CKR_CRYPTOKI_NOT_INITIALIZED); /* Obatin the session pointer */ rv = handle2session(hSession, &session_p); if (rv != CKR_OK) return (rv); if (pulSignatureLen == NULL) { rv = CKR_ARGUMENTS_BAD; goto clean_exit; } (void) pthread_mutex_lock(&session_p->session_mutex); ses_lock_held = B_TRUE; /* Application must call C_SignInit before calling C_Sign. */ if (!(session_p->sign.flags & CRYPTO_OPERATION_ACTIVE)) { REFRELE(session_p, ses_lock_held); return (CKR_OPERATION_NOT_INITIALIZED); } sign_recover.sr_session = session_p->k_session; (void) pthread_mutex_unlock(&session_p->session_mutex); ses_lock_held = B_FALSE; sign_recover.sr_datalen = ulDataLen; sign_recover.sr_databuf = (char *)pData; sign_recover.sr_signlen = *pulSignatureLen; sign_recover.sr_signbuf = (char *)pSignature; while ((r = ioctl(kernel_fd, CRYPTO_SIGN_RECOVER, &sign_recover)) < 0) { if (errno != EINTR) break; } if (r < 0) { rv = CKR_FUNCTION_FAILED; } else { rv = crypto2pkcs11_error_number(sign_recover.sr_return_value); } if (rv == CKR_OK || rv == CKR_BUFFER_TOO_SMALL) *pulSignatureLen = sign_recover.sr_signlen; if ((rv == CKR_BUFFER_TOO_SMALL) || (rv == CKR_OK && pSignature == NULL)) { /* * We will not terminate the active sign operation flag, * when the application-supplied buffer is too small, or * the application asks for the length of buffer to hold * the signature. */ REFRELE(session_p, ses_lock_held); return (rv); } clean_exit: /* * Terminates the active sign operation. * Application needs to call C_SignInit again for next * sign operation. */ (void) pthread_mutex_lock(&session_p->session_mutex); ses_lock_held = B_TRUE; session_p->sign.flags = 0; REFRELE(session_p, ses_lock_held); return (rv); } /* * 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. */ #ifndef _KERNEL_SLOT_H #define _KERNEL_SLOT_H #ifdef __cplusplus extern "C" { #endif #include "kernelSession.h" #include #define CKU_PUBLIC 2 /* default session auth. state */ typedef struct cipher_mechs_threshold { int mech_type; uint32_t mech_threshold; } cipher_mechs_threshold_t; /* * This slot has limited hash support. It can not do multi-part * hashing (updates). */ #define CRYPTO_LIMITED_HASH_SUPPORT 0x00000001 /* * This slot has limited hmac support. It can not do multi-part * hmac (updates). */ #define CRYPTO_LIMITED_HMAC_SUPPORT 0x00000002 typedef struct kernel_slot { CK_SLOT_ID sl_provider_id; /* kernel provider ID */ crypto_function_list_t sl_func_list; /* function list */ kernel_session_t *sl_sess_list; /* all open sessions */ CK_USER_TYPE sl_state; /* session's auth. state */ struct object *sl_tobj_list; /* token object list */ pthread_mutex_t sl_mutex; /* * The valid values are defined above. */ uint32_t sl_flags; /* * The maximum input data that can be digested by this slot. * Used only if CRYPTO_LIMITED_HASH_SUPPORT is set in sl_flags. */ int sl_hash_max_inlen; /* * The maximum input data that can be hmac'ed by this slot. * Used only if CRYPTO_LIMITED_HMAC_SUPPORT is set in sl_flags. */ int sl_hmac_max_inlen; /* * The threshold for input data size. We use this slot * only if data size is at or above this value. Used only if * CRYPTO_LIMITED_HASH_SUPPORT or CRYPTO_LIMITED_HMAC_SUPPORT is set. */ int sl_threshold; int total_threshold_count; cipher_mechs_threshold_t sl_mechs_threshold[MAX_NUM_THRESHOLD]; } kernel_slot_t; extern CK_ULONG slot_count; extern kernel_slot_t **slot_table; /* * Function Prototypes. */ CK_RV kernel_slottable_init(); #ifdef __cplusplus } #endif #endif /* _KERNEL_SLOT_H */ /* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License, Version 1.0 only * (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 2005 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. * * Copyright 2023 RackTop Systems, Inc. */ #include #include #include #include #include #include #include #include #include #include "kernelGlobal.h" #include "kernelSlot.h" /* ARGSUSED */ CK_RV C_GetSlotList(CK_BBOOL tokenPresent, CK_SLOT_ID_PTR pSlotList, CK_ULONG_PTR pulCount) { int i; if (!kernel_initialized) return (CKR_CRYPTOKI_NOT_INITIALIZED); if (pulCount == NULL) { return (CKR_ARGUMENTS_BAD); } if (pSlotList == NULL) { *pulCount = slot_count; return (CKR_OK); } if (*pulCount < slot_count) { *pulCount = slot_count; return (CKR_BUFFER_TOO_SMALL); } *pulCount = slot_count; /* * The slotID returned to an application will be the index to * the slot_table. The library will map to the provider_id when * making any ioctl call. */ for (i = 0; i < slot_count; i++) { pSlotList[i] = i; } return (CKR_OK); } CK_RV C_GetSlotInfo(CK_SLOT_ID slotID, CK_SLOT_INFO_PTR pInfo) { CK_RV rv; crypto_get_provider_info_t gi; int r; if (!kernel_initialized) return (CKR_CRYPTOKI_NOT_INITIALIZED); if (slotID >= slot_count) { return (CKR_SLOT_ID_INVALID); } if (pInfo == NULL) return (CKR_ARGUMENTS_BAD); /* kernel provider numbers start with 0 */ gi.gi_provider_id = slot_table[slotID]->sl_provider_id; while ((r = ioctl(kernel_fd, CRYPTO_GET_PROVIDER_INFO, &gi)) < 0) { if (errno != EINTR) break; } if (r < 0) { rv = CKR_FUNCTION_FAILED; } else { if (gi.gi_return_value != CRYPTO_SUCCESS) { rv = crypto2pkcs11_error_number( gi.gi_return_value); } else { rv = CKR_OK; } } if (rv == CKR_OK) { bcopy(gi.gi_provider_data.pd_prov_desc, pInfo->slotDescription, CRYPTO_PROVIDER_DESCR_MAX_LEN); bcopy(gi.gi_provider_data.pd_manufacturerID, pInfo->manufacturerID, CRYPTO_EXT_SIZE_MANUF); pInfo->flags = CKF_TOKEN_PRESENT | CKF_HW_SLOT; pInfo->hardwareVersion.major = gi.gi_provider_data.pd_hardware_version.cv_major; pInfo->hardwareVersion.minor = gi.gi_provider_data.pd_hardware_version.cv_minor; pInfo->firmwareVersion.major = gi.gi_provider_data.pd_firmware_version.cv_major; pInfo->firmwareVersion.minor = gi.gi_provider_data.pd_firmware_version.cv_minor; } return (rv); } CK_RV C_GetTokenInfo(CK_SLOT_ID slotID, CK_TOKEN_INFO_PTR pInfo) { CK_RV rv; crypto_get_provider_info_t gi; int r; if (!kernel_initialized) return (CKR_CRYPTOKI_NOT_INITIALIZED); if (slotID >= slot_count) return (CKR_SLOT_ID_INVALID); if (pInfo == NULL) return (CKR_ARGUMENTS_BAD); gi.gi_provider_id = slot_table[slotID]->sl_provider_id; while ((r = ioctl(kernel_fd, CRYPTO_GET_PROVIDER_INFO, &gi)) < 0) { if (errno != EINTR) break; } if (r < 0) { rv = CKR_FUNCTION_FAILED; } else { rv = crypto2pkcs11_error_number(gi.gi_return_value); } if (rv == CKR_OK) { bcopy(gi.gi_provider_data.pd_label, pInfo->label, CRYPTO_EXT_SIZE_LABEL); bcopy(gi.gi_provider_data.pd_manufacturerID, pInfo->manufacturerID, CRYPTO_EXT_SIZE_MANUF); bcopy(gi.gi_provider_data.pd_model, pInfo->model, CRYPTO_EXT_SIZE_MODEL); bcopy(gi.gi_provider_data.pd_serial_number, pInfo->serialNumber, CRYPTO_EXT_SIZE_SERIAL); pInfo->flags = gi.gi_provider_data.pd_flags; pInfo->ulMaxSessionCount = gi.gi_provider_data.pd_max_session_count; pInfo->ulSessionCount = gi.gi_provider_data.pd_session_count; pInfo->ulMaxRwSessionCount = gi.gi_provider_data.pd_max_rw_session_count; pInfo->ulRwSessionCount = gi.gi_provider_data.pd_rw_session_count; pInfo->ulMaxPinLen = gi.gi_provider_data.pd_max_pin_len; pInfo->ulMinPinLen = gi.gi_provider_data.pd_min_pin_len; pInfo->ulTotalPublicMemory = gi.gi_provider_data.pd_total_public_memory; pInfo->ulFreePublicMemory = gi.gi_provider_data.pd_free_public_memory; pInfo->ulTotalPrivateMemory = gi.gi_provider_data.pd_total_private_memory; pInfo->ulFreePrivateMemory = gi.gi_provider_data.pd_free_private_memory; pInfo->hardwareVersion.major = gi.gi_provider_data.pd_hardware_version.cv_major; pInfo->hardwareVersion.minor = gi.gi_provider_data.pd_hardware_version.cv_minor; pInfo->firmwareVersion.major = gi.gi_provider_data.pd_firmware_version.cv_major; pInfo->firmwareVersion.minor = gi.gi_provider_data.pd_firmware_version.cv_minor; (void) strncpy((char *)pInfo->utcTime, (const char *)gi.gi_provider_data.pd_time, CRYPTO_EXT_SIZE_TIME); } return (rv); } /*ARGSUSED*/ CK_RV C_WaitForSlotEvent(CK_FLAGS flags, CK_SLOT_ID_PTR pSlot, CK_VOID_PTR pReserved) { if (!kernel_initialized) return (CKR_CRYPTOKI_NOT_INITIALIZED); return (CKR_FUNCTION_NOT_SUPPORTED); } CK_RV C_GetMechanismList(CK_SLOT_ID slotID, CK_MECHANISM_TYPE_PTR pMechanismList, CK_ULONG_PTR pulCount) { CK_MECHANISM_TYPE type; CK_RV rv; CK_FLAGS flags; CK_ULONG specified_count, count = 0; crypto_get_provider_mechanisms_t *pm, tmp; crypto_get_provider_mechanism_info_t mechanism_info; crypto_provider_id_t provider_id; size_t alloc_bytes; int i, r; if (!kernel_initialized) return (CKR_CRYPTOKI_NOT_INITIALIZED); if (slotID >= slot_count) return (CKR_SLOT_ID_INVALID); /* kernel provider numbers start with 0 */ provider_id = slot_table[slotID]->sl_provider_id; if (pMechanismList != NULL) { if (pulCount == NULL) { return (CKR_ARGUMENTS_BAD); } else if (*pulCount == 0) { return (CKR_ARGUMENTS_BAD); } } specified_count = *pulCount; tmp.pm_provider_id = provider_id; tmp.pm_count = 0; while ((r = ioctl(kernel_fd, CRYPTO_GET_PROVIDER_MECHANISMS, &tmp)) < 0) { if (errno != EINTR) break; } if (r < 0) { return (CKR_FUNCTION_FAILED); } else { if (tmp.pm_return_value != CRYPTO_SUCCESS) { rv = crypto2pkcs11_error_number(tmp.pm_return_value); return (rv); } alloc_bytes = sizeof (crypto_get_provider_mechanisms_t) + (tmp.pm_count - 1) * sizeof (crypto_mech_name_t); } pm = malloc(alloc_bytes); if (pm == NULL) return (CKR_HOST_MEMORY); pm->pm_provider_id = provider_id; pm->pm_count = tmp.pm_count; while ((r = ioctl(kernel_fd, CRYPTO_GET_PROVIDER_MECHANISMS, pm)) < 0) { if (errno != EINTR) break; } if (r < 0) { rv = CKR_FUNCTION_FAILED; } else { rv = crypto2pkcs11_error_number(pm->pm_return_value); } if (rv != CKR_OK && rv != CKR_BUFFER_TOO_SMALL) goto clean_exit; for (i = 0; i < pm->pm_count; i++) { mechanism_info.mi_provider_id = provider_id; bcopy(&pm->pm_list[i][0], mechanism_info.mi_mechanism_name, sizeof (crypto_mech_name_t)); /* * Get each mechanism's flags. * The ioctl should not fail since the mechanism info is * already in the kernel and a call doesn't have to be made * to the provider. If it fails, nothing can be done other * than skip the mechanism. */ while ((r = ioctl(kernel_fd, CRYPTO_GET_PROVIDER_MECHANISM_INFO, &mechanism_info)) < 0) { if (errno != EINTR) break; } if (r < 0) { continue; } if (mechanism_info.mi_return_value != CRYPTO_SUCCESS) continue; flags = mechanism_info.mi_flags; /* * Atomic flags are not part of PKCS#11 so we filter * them out here. * Neither is CRYPTO_FG_MAC. */ 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); /* mechanism has no PKCS#11 flags, so don't report it */ if (flags == 0) continue; /* * The kernel framework has a pseudo mechanism * for RNG which we remove from the list of mechanisms. */ if (strcmp(&pm->pm_list[i][0], "random") != 0) { if (pkcs11_str2mech(&pm->pm_list[i][0], &type) != CKR_OK) continue; if (pMechanismList != NULL && rv == CKR_OK && (count < specified_count)) pMechanismList[count] = type; count++; } } if (pMechanismList != NULL && (count > specified_count)) rv = CKR_BUFFER_TOO_SMALL; *pulCount = count; clean_exit: free(pm); return (rv); } CK_RV C_GetMechanismInfo(CK_SLOT_ID slotID, CK_MECHANISM_TYPE type, CK_MECHANISM_INFO_PTR pInfo) { uint32_t k_mi_flags; CK_RV rv; if (!kernel_initialized) return (CKR_CRYPTOKI_NOT_INITIALIZED); if (slotID >= slot_count) return (CKR_SLOT_ID_INVALID); if (pInfo == NULL) { return (CKR_ARGUMENTS_BAD); } rv = get_mechanism_info(slot_table[slotID], type, pInfo, &k_mi_flags); return (rv); } /*ARGSUSED*/ CK_RV C_InitToken(CK_SLOT_ID slotID, CK_UTF8CHAR_PTR pPin, CK_ULONG ulPinLen, CK_UTF8CHAR_PTR pLabel) { if (!kernel_initialized) return (CKR_CRYPTOKI_NOT_INITIALIZED); return (CKR_FUNCTION_NOT_SUPPORTED); } /*ARGSUSED*/ CK_RV C_InitPIN(CK_SESSION_HANDLE hSession, CK_UTF8CHAR_PTR pPin, CK_ULONG ulPinLen) { if (!kernel_initialized) return (CKR_CRYPTOKI_NOT_INITIALIZED); return (CKR_FUNCTION_NOT_SUPPORTED); } CK_RV C_SetPIN(CK_SESSION_HANDLE hSession, CK_UTF8CHAR_PTR pOldPin, CK_ULONG ulOldLen, CK_UTF8CHAR_PTR pNewPin, CK_ULONG ulNewLen) { CK_RV rv = CKR_OK; kernel_session_t *session_p; boolean_t ses_lock_held = B_FALSE; crypto_set_pin_t setpin; int r; if (!kernel_initialized) return (CKR_CRYPTOKI_NOT_INITIALIZED); /* * Obtain the session pointer. Also, increment the session * reference count. */ rv = handle2session(hSession, &session_p); if (rv != CKR_OK) return (rv); /* Make sure it is a RW session. */ if (session_p->ses_RO) { rv = CKR_SESSION_READ_ONLY; REFRELE(session_p, ses_lock_held); return (rv); } /* Lock the session and make the CRYPTO_SET_PIN ioctl call. */ (void) pthread_mutex_lock(&session_p->session_mutex); ses_lock_held = B_TRUE; setpin.sp_session = session_p->k_session; setpin.sp_old_pin = (char *)pOldPin; setpin.sp_old_len = ulOldLen; setpin.sp_new_pin = (char *)pNewPin; setpin.sp_new_len = ulNewLen; while ((r = ioctl(kernel_fd, CRYPTO_SET_PIN, &setpin)) < 0) { if (errno != EINTR) break; } if (r < 0) { rv = CKR_FUNCTION_FAILED; } else { rv = crypto2pkcs11_error_number(setpin.sp_return_value); } REFRELE(session_p, ses_lock_held); return (rv); } /* * 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. */ #include #include #include #include #include "kernelGlobal.h" #include "kernelSlot.h" CK_ULONG slot_count = 0; kernel_slot_t **slot_table; static CK_RV kernel_get_slot_number() { CK_RV rv; crypto_get_provider_list_t *pl; int r; pl = malloc(sizeof (crypto_get_provider_list_t)); if (pl == NULL) return (CKR_HOST_MEMORY); pl->pl_count = 0; while ((r = ioctl(kernel_fd, CRYPTO_GET_PROVIDER_LIST, pl)) < 0) { if (errno != EINTR) break; } if (r < 0) { rv = CKR_FUNCTION_FAILED; } else { if (pl->pl_return_value != CRYPTO_SUCCESS) { rv = crypto2pkcs11_error_number(pl->pl_return_value); } else { rv = CKR_OK; } } if (rv == CKR_OK) { slot_count = pl->pl_count; } (void) free(pl); return (rv); } /* * This function will be used by metaslot to get the kernel * provider's threshold value for the supported mechanisms. */ void _SUNW_GetThreshold(void *thresholdp) { cipher_mechs_threshold_t *tp = (cipher_mechs_threshold_t *)thresholdp; kernel_slot_t *pslot; int i; /* * We alway use the 1st slot in the kernel to * get the threshold because all the kernel * slots will have the same threshold value * with the same mechanism. */ pslot = slot_table[0]; for (i = 0; i < pslot->total_threshold_count; i++) { tp[i].mech_type = pslot->sl_mechs_threshold[i].mech_type; tp[i].mech_threshold = pslot->sl_mechs_threshold[i].mech_threshold; } } /* * To retrieve the crypto_function_list structure with boolean entries * indicating which functions are supported by the hardware provider which * is specified by the slot ID. */ static CK_RV kernel_get_func_list(kernel_slot_t *pslot) { CK_RV rv = CKR_OK; crypto_get_function_list_t fl; int r; int i; (void) memset(&fl, 0, sizeof (fl)); fl.fl_provider_id = pslot->sl_provider_id; while ((r = ioctl(kernel_fd, CRYPTO_GET_FUNCTION_LIST, &fl)) < 0) { if (errno != EINTR) break; } if (r < 0) { rv = CKR_FUNCTION_FAILED; } else { if (fl.fl_return_value == 0) { rv = CKR_OK; } else { rv = crypto2pkcs11_error_number(fl.fl_return_value); } } if (rv != CKR_OK) { return (rv); } /* copy data structure received from kernel */ pslot->sl_func_list = fl.fl_list; pslot->sl_flags = 0; if (fl.fl_list.prov_is_hash_limited) { pslot->sl_flags |= CRYPTO_LIMITED_HASH_SUPPORT; pslot->sl_hash_max_inlen = fl.fl_list.prov_hash_limit; } if (fl.fl_list.prov_is_hmac_limited) { pslot->sl_flags |= CRYPTO_LIMITED_HMAC_SUPPORT; pslot->sl_hmac_max_inlen = fl.fl_list.prov_hmac_limit; } if (fl.fl_list.prov_is_hash_limited | fl.fl_list.prov_is_hmac_limited) { pslot->sl_threshold = fl.fl_list.prov_hash_threshold; } pslot->total_threshold_count = fl.fl_list.total_threshold_count; for (i = 0; i < pslot->total_threshold_count; i++) { pslot->sl_mechs_threshold[i].mech_type = fl.fl_list.fl_threshold[i].mech_type; pslot->sl_mechs_threshold[i].mech_threshold = fl.fl_list.fl_threshold[i].mech_threshold; } return (CKR_OK); } /* * Initialize the slot table. * * This function is called from C_Initialize() only. Since C_Initialize() * holds the global mutex lock, there is no need to acquire another lock * in this routine to protect the slot table. */ CK_RV kernel_slottable_init() { int i, cur_slot_num = 0; CK_RV rv = CKR_OK; crypto_get_provider_list_t *pl = NULL; int r; /* * Find out how many slots are presented from kernel hardware * providers. If there is no slot presented, just return. */ rv = kernel_get_slot_number(); if (rv != CKR_OK || slot_count == 0) { return (rv); } /* Allocate space for the slot table */ slot_table = malloc(sizeof (kernel_slot_t *) * slot_count); if (slot_table == NULL) { return (CKR_HOST_MEMORY); } /* For each slot, allocate space and initialize the slot's mutex. */ for (i = 0; i < slot_count; i++) { slot_table[i] = malloc(sizeof (kernel_slot_t)); if (slot_table[i] == NULL) { rv = CKR_HOST_MEMORY; goto failed; } slot_table[i]->sl_sess_list = NULL; slot_table[i]->sl_tobj_list = NULL; slot_table[i]->sl_state = CKU_PUBLIC; /* Initialize this slot's mutex */ if (pthread_mutex_init(&slot_table[i]->sl_mutex, NULL) != 0) { rv = CKR_FUNCTION_FAILED; (void) free(slot_table[i]); goto failed; } cur_slot_num = i; } /* * Get the provider ID for each slot from kernel and save it in the * slot table. */ pl = malloc(slot_count * sizeof (crypto_get_provider_list_t)); if (pl == NULL) { rv = CKR_HOST_MEMORY; goto failed; } pl->pl_count = slot_count; while ((r = ioctl(kernel_fd, CRYPTO_GET_PROVIDER_LIST, pl)) < 0) { if (errno != EINTR) break; } if (r < 0) { rv = CKR_FUNCTION_FAILED; goto failed; } else { if (pl->pl_return_value != CRYPTO_SUCCESS) { rv = crypto2pkcs11_error_number(pl->pl_return_value); goto failed; } else { rv = CKR_OK; } } for (i = 0; i < slot_count; i++) { slot_table[i]->sl_provider_id = pl->pl_list[i].pe_provider_id; } /* * Get the function list for each slot from kernel and save it in * the slot table. */ for (i = 0; i < slot_count; i++) { rv = kernel_get_func_list(slot_table[i]); if (rv != CKR_OK) { goto failed; } } (void) free(pl); return (CKR_OK); failed: for (i = 0; i < cur_slot_num; i++) { (void) pthread_mutex_destroy(&slot_table[i]->sl_mutex); (void) free(slot_table[i]); } (void) free(slot_table); (void) free(pl); return (rv); } /* * 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 2007 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. * Copyright (c) 2018, Joyent, Inc. */ #include #include #include #include #include #include #include #include "softSession.h" #include "softObject.h" #include "softOps.h" #include "softMAC.h" #include "kernelSoftCommon.h" /* * Do the operation(s) specified by opflag. */ CK_RV do_soft_digest(void **s, CK_MECHANISM_PTR pMechanism, CK_BYTE_PTR pData, CK_ULONG ulDataLen, CK_BYTE_PTR pDigest, CK_ULONG_PTR pulDigestLen, int opflag) { soft_session_t *session_p; CK_RV rv = CKR_ARGUMENTS_BAD; session_p = *((soft_session_t **)s); if (session_p == NULL) { if (!(opflag & OP_INIT)) { return (CKR_ARGUMENTS_BAD); } session_p = calloc(1, sizeof (soft_session_t)); /* * Initialize the lock for the newly created session. * We do only the minimum needed setup for the * soft_digest* routines to succeed. */ if (pthread_mutex_init(&session_p->session_mutex, NULL) != 0) { free(session_p); return (CKR_CANT_LOCK); } *s = session_p; } else if (opflag & OP_INIT) { free_soft_ctx(session_p, OP_DIGEST); } if (opflag & OP_INIT) { rv = soft_digest_init(session_p, pMechanism); if (rv != CKR_OK) return (rv); } if (opflag & OP_SINGLE) { rv = soft_digest(session_p, pData, ulDataLen, pDigest, pulDigestLen); } else { if (opflag & OP_UPDATE) { rv = soft_digest_update(session_p, pData, ulDataLen); if (rv != CKR_OK) return (rv); } if (opflag & OP_FINAL) { rv = soft_digest_final(session_p, pDigest, pulDigestLen); } } return (rv); } /* * opflag specifies whether this is a sign or verify. */ CK_RV do_soft_hmac_init(void **s, CK_MECHANISM_PTR pMechanism, CK_BYTE_PTR kval, CK_ULONG klen, int opflag) { CK_RV rv; soft_object_t keyobj; secret_key_obj_t skeyobj; soft_object_t *key_p; soft_session_t *session_p; session_p = *((soft_session_t **)s); if (session_p == NULL) { session_p = calloc(1, sizeof (soft_session_t)); /* See comments in do_soft_digest() above */ if (pthread_mutex_init(&session_p->session_mutex, NULL) != 0) { free(session_p); return (CKR_CANT_LOCK); } *s = session_p; } else if (opflag & OP_INIT) { free_soft_ctx(session_p, opflag); } /* Do the minimum needed setup for the call to succeed */ key_p = &keyobj; bzero(key_p, sizeof (soft_object_t)); key_p->class = CKO_SECRET_KEY; key_p->key_type = CKK_GENERIC_SECRET; bzero(&skeyobj, sizeof (secret_key_obj_t)); OBJ_SEC(key_p) = &skeyobj; OBJ_SEC_VALUE(key_p) = kval; OBJ_SEC_VALUE_LEN(key_p) = klen; rv = soft_hmac_sign_verify_init_common(session_p, pMechanism, key_p, opflag & OP_SIGN); return (rv); } /* * opflag specifies whether this is a sign or verify. */ CK_RV do_soft_hmac_update(void **s, CK_BYTE_PTR pData, CK_ULONG ulDataLen, int opflag) { soft_session_t *session_p; session_p = *((soft_session_t **)s); if (session_p == NULL) { return (CKR_ARGUMENTS_BAD); } return (soft_hmac_sign_verify_update(session_p, pData, ulDataLen, opflag & OP_SIGN)); } /* * opflag specifies whether this is a final or single. */ CK_RV do_soft_hmac_sign(void **s, CK_BYTE_PTR pData, CK_ULONG ulDataLen, CK_BYTE_PTR pSignature, CK_ULONG_PTR pulSignatureLen, int opflag) { CK_RV rv; soft_session_t *session_p; CK_BYTE hmac[SHA512_DIGEST_LENGTH]; /* use the maximum size */ session_p = *((soft_session_t **)s); if (session_p == NULL || !(opflag & OP_SINGLE || opflag & OP_FINAL)) { return (CKR_ARGUMENTS_BAD); } rv = soft_hmac_sign_verify_common(session_p, pData, ulDataLen, (pSignature != NULL ? hmac : NULL), pulSignatureLen, B_TRUE); if ((rv == CKR_OK) && (pSignature != NULL)) { (void) memcpy(pSignature, hmac, *pulSignatureLen); } return (rv); } /* * opflag specifies whether this is a final or single. */ CK_RV do_soft_hmac_verify(void **s, CK_BYTE_PTR pData, CK_ULONG ulDataLen, CK_BYTE_PTR pSignature, CK_ULONG ulSignatureLen, int opflag) { CK_RV rv; CK_ULONG len; soft_session_t *session_p; soft_hmac_ctx_t *hmac_ctx; CK_BYTE hmac[SHA512_DIGEST_LENGTH]; /* use the maximum size */ session_p = *((soft_session_t **)s); if (session_p == NULL || !(opflag & OP_SINGLE || opflag & OP_FINAL)) { return (CKR_ARGUMENTS_BAD); } hmac_ctx = (soft_hmac_ctx_t *)session_p->verify.context; len = hmac_ctx->hmac_len; rv = soft_hmac_sign_verify_common(session_p, pData, ulDataLen, hmac, &len, B_FALSE); if (rv == CKR_OK) { if (len != ulSignatureLen) { rv = CKR_SIGNATURE_LEN_RANGE; } if (memcmp(hmac, pSignature, len) != 0) { rv = CKR_SIGNATURE_INVALID; } } return (rv); } /* * Helper routine to handle the case when the ctx is abandoned. */ void free_soft_ctx(void *s, int opflag) { soft_session_t *session_p; session_p = (soft_session_t *)s; if (session_p == NULL) return; if (opflag & OP_SIGN) { freezero(session_p->sign.context, sizeof (soft_hmac_ctx_t)); session_p->sign.context = NULL; session_p->sign.flags = 0; } else if (opflag & OP_VERIFY) { freezero(session_p->verify.context, sizeof (soft_hmac_ctx_t)); session_p->verify.context = NULL; session_p->verify.flags = 0; } else { free(session_p->digest.context); session_p->digest.context = NULL; session_p->digest.flags = 0; } } /* * 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 2007 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ #ifndef _KERNEL_SOFT_COMMON_H #define _KERNEL_SOFT_COMMON_H #ifdef __cplusplus extern "C" { #endif #include #include #define OP_INIT 0x01 #define OP_UPDATE 0x02 #define OP_FINAL 0x04 #define OP_SINGLE 0x08 #define OP_DIGEST 0x10 #define OP_SIGN 0x20 #define OP_VERIFY 0x40 void free_soft_ctx(void *s, int opflag); CK_RV do_soft_digest(void **s, CK_MECHANISM_PTR pMechanism, CK_BYTE_PTR pBuf, CK_ULONG ulBufLen, CK_BYTE_PTR pDigest, CK_ULONG_PTR pulDigestLen, int opflag); CK_RV do_soft_hmac_init(void **s, CK_MECHANISM_PTR pMechanism, CK_BYTE_PTR kval, CK_ULONG klen, int opflag); CK_RV do_soft_hmac_update(void **s, CK_BYTE_PTR pBuf, CK_ULONG ulBufLen, int opflag); CK_RV do_soft_hmac_sign(void **s, CK_BYTE_PTR pBuf, CK_ULONG ulBufLen, CK_BYTE_PTR pSignature, CK_ULONG_PTR pulSignatureLen, int opflag); CK_RV do_soft_hmac_verify(void **s, CK_BYTE_PTR pBuf, CK_ULONG ulBufLen, CK_BYTE_PTR pSignature, CK_ULONG ulSignatureLen, int opflag); #ifdef __cplusplus } #endif #endif /* _KERNEL_SOFT_COMMON_H */ /* * 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 #include #include #include #include #include #include #include #include #include "kernelGlobal.h" #include "kernelObject.h" #include "kernelSlot.h" #define ENCODE_ATTR(type, value, len) { \ cur_attr->oa_type = type; \ (void) memcpy(ptr, value, len); \ cur_attr->oa_value = ptr; \ cur_attr->oa_value_len = len; \ cur_attr++; \ } #define 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; } /* * 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. */ #include #include #include #include #include "kernelGlobal.h" #include "kernelObject.h" #include "kernelSession.h" #include "kernelEmulate.h" CK_RV C_VerifyInit(CK_SESSION_HANDLE hSession, CK_MECHANISM_PTR pMechanism, CK_OBJECT_HANDLE hKey) { CK_RV rv; kernel_session_t *session_p; kernel_object_t *key_p; boolean_t ses_lock_held = B_FALSE; crypto_verify_init_t verify_init; crypto_mech_type_t k_mech_type; int r; if (!kernel_initialized) return (CKR_CRYPTOKI_NOT_INITIALIZED); if (pMechanism == NULL) { return (CKR_ARGUMENTS_BAD); } /* Get the kernel's internal mechanism number. */ rv = kernel_mech(pMechanism->mechanism, &k_mech_type); if (rv != CKR_OK) return (rv); /* Obtain the session pointer. */ rv = handle2session(hSession, &session_p); if (rv != CKR_OK) return (rv); /* Obtain the object pointer. */ HANDLE2OBJECT(hKey, key_p, rv); if (rv != CKR_OK) { REFRELE(session_p, ses_lock_held); return (rv); } /* Check to see if key object supports verification. */ if (key_p->is_lib_obj && !(key_p->bool_attr_mask & VERIFY_BOOL_ON)) { rv = CKR_KEY_TYPE_INCONSISTENT; goto clean_exit; } (void) pthread_mutex_lock(&session_p->session_mutex); ses_lock_held = B_TRUE; /* * This active flag will remain ON until application calls either * C_Verify or C_VerifyFinal to verify a signature on data. */ session_p->verify.flags = CRYPTO_OPERATION_ACTIVE; if (!key_p->is_lib_obj) { verify_init.vi_key.ck_format = CRYPTO_KEY_REFERENCE; verify_init.vi_key.ck_obj_id = key_p->k_handle; } else { if (key_p->class == CKO_SECRET_KEY) { verify_init.vi_key.ck_format = CRYPTO_KEY_RAW; verify_init.vi_key.ck_data = get_symmetric_key_value(key_p); if (verify_init.vi_key.ck_data == NULL) { rv = CKR_HOST_MEMORY; goto clean_exit; } verify_init.vi_key.ck_length = OBJ_SEC(key_p)->sk_value_len << 3; } else if (key_p->key_type == CKK_RSA) { if (get_rsa_public_key(key_p, &verify_init.vi_key) != CKR_OK) { rv = CKR_HOST_MEMORY; goto clean_exit; } } else if (key_p->key_type == CKK_DSA) { if (get_dsa_public_key(key_p, &verify_init.vi_key) != CKR_OK) { rv = CKR_HOST_MEMORY; goto clean_exit; } } else if (key_p->key_type == CKK_EC) { if (get_ec_public_key(key_p, &verify_init.vi_key) != CKR_OK) { rv = CKR_HOST_MEMORY; goto clean_exit; } } else { rv = CKR_KEY_TYPE_INCONSISTENT; goto clean_exit; } } verify_init.vi_session = session_p->k_session; (void) pthread_mutex_unlock(&session_p->session_mutex); ses_lock_held = B_FALSE; verify_init.vi_mech.cm_type = k_mech_type; verify_init.vi_mech.cm_param = pMechanism->pParameter; verify_init.vi_mech.cm_param_len = pMechanism->ulParameterLen; while ((r = ioctl(kernel_fd, CRYPTO_VERIFY_INIT, &verify_init)) < 0) { if (errno != EINTR) break; } if (r < 0) { rv = CKR_FUNCTION_FAILED; } else { rv = crypto2pkcs11_error_number(verify_init.vi_return_value); } if (rv == CKR_OK && SLOT_HAS_LIMITED_HMAC(session_p) && is_hmac(pMechanism->mechanism)) { if (key_p->is_lib_obj && key_p->class == CKO_SECRET_KEY) { (void) pthread_mutex_lock(&session_p->session_mutex); session_p->verify.flags |= CRYPTO_EMULATE; (void) pthread_mutex_unlock(&session_p->session_mutex); rv = emulate_init(session_p, pMechanism, &(verify_init.vi_key), OP_VERIFY); } else { rv = CKR_FUNCTION_FAILED; } } /* free the memory allocated for verify_init.vi_key */ if (key_p->is_lib_obj) { if (key_p->class == CKO_SECRET_KEY) { free(verify_init.vi_key.ck_data); } else { free_key_attributes(&verify_init.vi_key); } } if (rv != CKR_OK) { (void) pthread_mutex_lock(&session_p->session_mutex); session_p->verify.flags &= ~CRYPTO_OPERATION_ACTIVE; ses_lock_held = B_TRUE; } clean_exit: OBJ_REFRELE(key_p); REFRELE(session_p, ses_lock_held); return (rv); } CK_RV C_Verify(CK_SESSION_HANDLE hSession, CK_BYTE_PTR pData, CK_ULONG ulDataLen, CK_BYTE_PTR pSignature, CK_ULONG ulSignatureLen) { CK_RV rv; kernel_session_t *session_p; boolean_t ses_lock_held = B_FALSE; crypto_verify_t verify; int r; if (!kernel_initialized) return (CKR_CRYPTOKI_NOT_INITIALIZED); /* Obatin the session pointer */ rv = handle2session(hSession, &session_p); if (rv != CKR_OK) return (rv); (void) pthread_mutex_lock(&session_p->session_mutex); ses_lock_held = B_TRUE; /* Application must call C_VerifyInit before calling C_Verify. */ if (!(session_p->verify.flags & CRYPTO_OPERATION_ACTIVE)) { REFRELE(session_p, ses_lock_held); return (CKR_OPERATION_NOT_INITIALIZED); } /* * C_Verify must be called without intervening C_VerifyUpdate * calls. */ if (session_p->verify.flags & CRYPTO_OPERATION_UPDATE) { /* * C_Verify can not be used to terminate a multi-part * operation, so we'll leave the active verify operation * flag on and let the application continue with the * verify update operation. */ REFRELE(session_p, ses_lock_held); return (CKR_FUNCTION_FAILED); } if (session_p->verify.flags & CRYPTO_EMULATE) { if ((ulDataLen < SLOT_THRESHOLD(session_p)) || (ulDataLen > SLOT_HMAC_MAX_INDATA_LEN(session_p))) { session_p->verify.flags |= CRYPTO_EMULATE_USING_SW; (void) pthread_mutex_unlock(&session_p->session_mutex); ses_lock_held = B_FALSE; rv = do_soft_hmac_verify(get_spp(&session_p->verify), pData, ulDataLen, pSignature, ulSignatureLen, OP_SINGLE); goto clean_exit; } else { free_soft_ctx(get_sp(&session_p->verify), OP_VERIFY); } } verify.cv_session = session_p->k_session; (void) pthread_mutex_unlock(&session_p->session_mutex); ses_lock_held = B_FALSE; verify.cv_datalen = ulDataLen; verify.cv_databuf = (char *)pData; verify.cv_signlen = ulSignatureLen; verify.cv_signbuf = (char *)pSignature; while ((r = ioctl(kernel_fd, CRYPTO_VERIFY, &verify)) < 0) { if (errno != EINTR) break; } if (r < 0) { rv = CKR_FUNCTION_FAILED; } else { rv = crypto2pkcs11_error_number(verify.cv_return_value); } clean_exit: /* * Always terminate the active verify operation. * Application needs to call C_VerifyInit again for next * verify operation. */ (void) pthread_mutex_lock(&session_p->session_mutex); ses_lock_held = B_TRUE; REINIT_OPBUF(&session_p->verify); session_p->verify.flags = 0; REFRELE(session_p, ses_lock_held); return (rv); } CK_RV C_VerifyUpdate(CK_SESSION_HANDLE hSession, CK_BYTE_PTR pPart, CK_ULONG ulPartLen) { CK_RV rv; kernel_session_t *session_p; boolean_t ses_lock_held = B_FALSE; crypto_verify_update_t verify_update; int r; if (!kernel_initialized) return (CKR_CRYPTOKI_NOT_INITIALIZED); /* Obtain the session pointer */ rv = handle2session(hSession, &session_p); if (rv != CKR_OK) return (rv); if (pPart == NULL) { rv = CKR_ARGUMENTS_BAD; goto clean_exit; } (void) pthread_mutex_lock(&session_p->session_mutex); ses_lock_held = B_TRUE; /* * Application must call C_VerifyInit before calling * C_VerifyUpdate. */ if (!(session_p->verify.flags & CRYPTO_OPERATION_ACTIVE)) { REFRELE(session_p, ses_lock_held); return (CKR_OPERATION_NOT_INITIALIZED); } session_p->verify.flags |= CRYPTO_OPERATION_UPDATE; if (session_p->verify.flags & CRYPTO_EMULATE) { (void) pthread_mutex_unlock(&session_p->session_mutex); ses_lock_held = B_FALSE; rv = emulate_update(session_p, pPart, ulPartLen, OP_VERIFY); goto done; } verify_update.vu_session = session_p->k_session; (void) pthread_mutex_unlock(&session_p->session_mutex); ses_lock_held = B_FALSE; verify_update.vu_datalen = ulPartLen; verify_update.vu_databuf = (char *)pPart; while ((r = ioctl(kernel_fd, CRYPTO_VERIFY_UPDATE, &verify_update)) < 0) { if (errno != EINTR) break; } if (r < 0) { rv = CKR_FUNCTION_FAILED; } else { rv = crypto2pkcs11_error_number(verify_update.vu_return_value); } done: if (rv == CKR_OK) { REFRELE(session_p, ses_lock_held); return (rv); } clean_exit: /* * After an error occurred, terminate the current verify * operation by resetting the active and update flags. */ (void) pthread_mutex_lock(&session_p->session_mutex); ses_lock_held = B_TRUE; REINIT_OPBUF(&session_p->verify); session_p->verify.flags = 0; REFRELE(session_p, ses_lock_held); return (rv); } CK_RV C_VerifyFinal(CK_SESSION_HANDLE hSession, CK_BYTE_PTR pSignature, CK_ULONG ulSignatureLen) { CK_RV rv; kernel_session_t *session_p; boolean_t ses_lock_held = B_FALSE; crypto_verify_final_t verify_final; int r; if (!kernel_initialized) return (CKR_CRYPTOKI_NOT_INITIALIZED); /* Obtain the session pointer */ rv = handle2session(hSession, &session_p); if (rv != CKR_OK) return (rv); (void) pthread_mutex_lock(&session_p->session_mutex); ses_lock_held = B_TRUE; /* * Application must call C_VerifyInit before calling * C_VerifyFinal. */ if (!(session_p->verify.flags & CRYPTO_OPERATION_ACTIVE)) { REFRELE(session_p, ses_lock_held); return (CKR_OPERATION_NOT_INITIALIZED); } /* The order of checks is important here */ if (session_p->verify.flags & CRYPTO_EMULATE_USING_SW) { if (session_p->verify.flags & CRYPTO_EMULATE_UPDATE_DONE) { (void) pthread_mutex_unlock(&session_p->session_mutex); ses_lock_held = B_FALSE; rv = do_soft_hmac_verify(get_spp(&session_p->verify), NULL, 0, pSignature, ulSignatureLen, OP_FINAL); } else { /* * We should not end up here even if an earlier * C_VerifyFinal() call took the C_Verify() path as * it never returns CKR_BUFFER_TOO_SMALL. */ (void) pthread_mutex_unlock(&session_p->session_mutex); ses_lock_held = B_FALSE; rv = CKR_ARGUMENTS_BAD; } goto clean_exit; } else if (session_p->verify.flags & CRYPTO_EMULATE) { digest_buf_t *bufp = session_p->verify.context; /* * We are emulating a single-part operation now. * So, clear the flag. */ session_p->verify.flags &= ~CRYPTO_OPERATION_UPDATE; if (bufp == NULL || bufp->buf == NULL) { rv = CKR_ARGUMENTS_BAD; goto clean_exit; } REFRELE(session_p, ses_lock_held); rv = C_Verify(hSession, bufp->buf, bufp->indata_len, pSignature, ulSignatureLen); return (rv); } verify_final.vf_session = session_p->k_session; (void) pthread_mutex_unlock(&session_p->session_mutex); ses_lock_held = B_FALSE; verify_final.vf_signlen = ulSignatureLen; verify_final.vf_signbuf = (char *)pSignature; while ((r = ioctl(kernel_fd, CRYPTO_VERIFY_FINAL, &verify_final)) < 0) { if (errno != EINTR) break; } if (r < 0) { rv = CKR_FUNCTION_FAILED; } else { rv = crypto2pkcs11_error_number(verify_final.vf_return_value); } clean_exit: /* Always terminate the active verify operation */ (void) pthread_mutex_lock(&session_p->session_mutex); ses_lock_held = B_TRUE; REINIT_OPBUF(&session_p->verify); session_p->verify.flags = 0; REFRELE(session_p, ses_lock_held); return (rv); } CK_RV C_VerifyRecoverInit(CK_SESSION_HANDLE hSession, CK_MECHANISM_PTR pMechanism, CK_OBJECT_HANDLE hKey) { CK_RV rv; kernel_session_t *session_p; kernel_object_t *key_p; boolean_t ses_lock_held = B_FALSE; crypto_verify_recover_init_t vr_init; crypto_mech_type_t k_mech_type; int r; if (!kernel_initialized) return (CKR_CRYPTOKI_NOT_INITIALIZED); if (pMechanism == NULL) { return (CKR_ARGUMENTS_BAD); } /* Get the kernel's internal mechanism number. */ rv = kernel_mech(pMechanism->mechanism, &k_mech_type); if (rv != CKR_OK) return (rv); /* Obtain the session pointer. */ rv = handle2session(hSession, &session_p); if (rv != CKR_OK) return (rv); /* Obtain the object pointer. */ HANDLE2OBJECT(hKey, key_p, rv); if (rv != CKR_OK) { REFRELE(session_p, ses_lock_held); return (rv); } /* * Check to see if key object is a RSA key and if it supports * verify_recover. */ if (key_p->is_lib_obj && !((key_p->key_type == CKK_RSA) && (key_p->bool_attr_mask & VERIFY_RECOVER_BOOL_ON))) { rv = CKR_KEY_TYPE_INCONSISTENT; goto clean_exit; } (void) pthread_mutex_lock(&session_p->session_mutex); ses_lock_held = B_TRUE; /* * This active flag will remain ON until application calls * C_VerifyRecover to verify a signature on data. */ session_p->verify.flags = CRYPTO_OPERATION_ACTIVE; /* Set up the key data */ if (!key_p->is_lib_obj) { vr_init.ri_key.ck_format = CRYPTO_KEY_REFERENCE; vr_init.ri_key.ck_obj_id = key_p->k_handle; } else { if (key_p->key_type == CKK_RSA) { if (get_rsa_public_key(key_p, &vr_init.ri_key) != CKR_OK) { rv = CKR_HOST_MEMORY; goto clean_exit; } } else { rv = CKR_KEY_TYPE_INCONSISTENT; goto clean_exit; } } vr_init.ri_session = session_p->k_session; (void) pthread_mutex_unlock(&session_p->session_mutex); ses_lock_held = B_FALSE; vr_init.ri_mech.cm_type = k_mech_type; vr_init.ri_mech.cm_param = pMechanism->pParameter; vr_init.ri_mech.cm_param_len = pMechanism->ulParameterLen; while ((r = ioctl(kernel_fd, CRYPTO_VERIFY_RECOVER_INIT, &vr_init)) < 0) { if (errno != EINTR) break; } if (r < 0) { rv = CKR_FUNCTION_FAILED; } else { rv = crypto2pkcs11_error_number(vr_init.ri_return_value); } /* free the memory allocated for vr_init.ri_key */ if (key_p->is_lib_obj) { free_key_attributes(&vr_init.ri_key); } if (rv != CKR_OK) { (void) pthread_mutex_lock(&session_p->session_mutex); session_p->verify.flags &= ~CRYPTO_OPERATION_ACTIVE; ses_lock_held = B_TRUE; } clean_exit: OBJ_REFRELE(key_p); REFRELE(session_p, ses_lock_held); return (rv); } CK_RV C_VerifyRecover(CK_SESSION_HANDLE hSession, CK_BYTE_PTR pSignature, CK_ULONG ulSignatureLen, CK_BYTE_PTR pData, CK_ULONG_PTR pulDataLen) { CK_RV rv; kernel_session_t *session_p; boolean_t ses_lock_held = B_FALSE; crypto_verify_recover_t verify_recover; int r; if (!kernel_initialized) return (CKR_CRYPTOKI_NOT_INITIALIZED); /* Obtain the session pointer */ rv = handle2session(hSession, &session_p); if (rv != CKR_OK) return (rv); if (pSignature == NULL || pulDataLen == NULL) { rv = CKR_ARGUMENTS_BAD; goto clean_exit; } (void) pthread_mutex_lock(&session_p->session_mutex); ses_lock_held = B_TRUE; /* * Application must call C_VerifyRecoverInit before calling * C_Verify. */ if (!(session_p->verify.flags & CRYPTO_OPERATION_ACTIVE)) { REFRELE(session_p, ses_lock_held); return (CKR_OPERATION_NOT_INITIALIZED); } verify_recover.vr_session = session_p->k_session; (void) pthread_mutex_unlock(&session_p->session_mutex); ses_lock_held = B_FALSE; verify_recover.vr_signlen = ulSignatureLen; verify_recover.vr_signbuf = (char *)pSignature; verify_recover.vr_datalen = *pulDataLen; verify_recover.vr_databuf = (char *)pData; while ((r = ioctl(kernel_fd, CRYPTO_VERIFY_RECOVER, &verify_recover)) < 0) { if (errno != EINTR) break; } if (r < 0) { rv = CKR_FUNCTION_FAILED; } else { rv = crypto2pkcs11_error_number( verify_recover.vr_return_value); } if (rv == CKR_OK || rv == CKR_BUFFER_TOO_SMALL) *pulDataLen = verify_recover.vr_datalen; if ((rv == CKR_BUFFER_TOO_SMALL) || (rv == CKR_OK && pData == NULL)) { /* * We will not terminate the active verify operation flag, * when the application-supplied buffer is too small, or * the application asks for the length of buffer to hold * the recovered data. */ REFRELE(session_p, ses_lock_held); return (rv); } clean_exit: /* * Always terminate the active verify operation. * Application needs to call C_VerifyInit again for next * verify operation. */ (void) pthread_mutex_lock(&session_p->session_mutex); session_p->verify.flags = 0; ses_lock_held = B_TRUE; REFRELE(session_p, ses_lock_held); return (rv); } # # 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) 2006, 2010, Oracle and/or its affiliates. All rights reserved. # # # MAPFILE HEADER START # # WARNING: STOP NOW. DO NOT MODIFY THIS FILE. # Object versioning must comply with the rules detailed in # # usr/src/lib/README.mapfiles # # You should not be making modifications here until you've read the most current # copy of that file. If you need help, contact a gatekeeper for guidance. # # MAPFILE HEADER END # $mapfile_version 2 SYMBOL_VERSION SUNW_1.1 { global: C_CancelFunction; C_CloseAllSessions; C_CloseSession; C_CopyObject; C_CreateObject; C_Decrypt; C_DecryptDigestUpdate; C_DecryptFinal; C_DecryptInit; C_DecryptUpdate; C_DecryptVerifyUpdate; C_DeriveKey; C_DestroyObject; C_Digest; C_DigestEncryptUpdate; C_DigestFinal; C_DigestInit; C_DigestKey; C_DigestUpdate; C_Encrypt; C_EncryptFinal; C_EncryptInit; C_EncryptUpdate; C_Finalize; C_FindObjects; C_FindObjectsFinal; C_FindObjectsInit; C_GenerateKey; C_GenerateKeyPair; C_GenerateRandom; C_GetAttributeValue; C_GetFunctionList; C_GetFunctionStatus; C_GetInfo; C_GetMechanismInfo; C_GetMechanismList; C_GetObjectSize; C_GetOperationState; C_GetSessionInfo; C_GetSlotInfo; C_GetSlotList; C_GetTokenInfo; C_Initialize; C_InitPIN; C_InitToken; C_Login; C_Logout; C_OpenSession; C_SeedRandom; C_SetAttributeValue; C_SetOperationState; C_SetPIN; C_Sign; C_SignEncryptUpdate; C_SignFinal; C_SignInit; C_SignRecover; C_SignRecoverInit; C_SignUpdate; C_UnwrapKey; C_Verify; C_VerifyFinal; C_VerifyInit; C_VerifyRecover; C_VerifyRecoverInit; C_VerifyUpdate; C_WaitForSlotEvent; C_WrapKey; }; SYMBOL_VERSION SUNWprivate { global: _SUNW_GetThreshold; local: *; };