diff options
Diffstat (limited to 'usr/src/lib/pkcs11')
32 files changed, 9990 insertions, 3 deletions
diff --git a/usr/src/lib/pkcs11/Makefile b/usr/src/lib/pkcs11/Makefile index 7d14efdd5c..61351cd3c0 100644 --- a/usr/src/lib/pkcs11/Makefile +++ b/usr/src/lib/pkcs11/Makefile @@ -18,9 +18,7 @@ # # CDDL HEADER END # -# -# Copyright 2010 Sun Microsystems, Inc. All rights reserved. -# Use is subject to license terms. +# Copyright (c) 2003, 2010, Oracle and/or its affiliates. All rights reserved. # # lib/pkcs11/Makefile @@ -38,6 +36,7 @@ SUBDIRS = \ libsoftcrypto \ libkcfd \ pkcs11_kernel \ + pkcs11_kms \ pkcs11_softtoken \ pkcs11_tpm diff --git a/usr/src/lib/pkcs11/pkcs11_kms/Makefile b/usr/src/lib/pkcs11/pkcs11_kms/Makefile new file mode 100644 index 0000000000..f82f4a6763 --- /dev/null +++ b/usr/src/lib/pkcs11/pkcs11_kms/Makefile @@ -0,0 +1,43 @@ +# +# 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) 2010, Oracle and/or its affiliates. All rights reserved. +# + +include $(SRC)/lib/Makefile.lib + +SUBDIRS = $(MACH) +$(BUILD64)SUBDIRS += $(MACH64) + +all := TARGET= all +check := TARGET= check +clean := TARGET= clean +clobber := TARGET= clobber +install := TARGET= install +lint := TARGET= lint + +.KEEP_STATE: + +all clean clobber install lint: $(SUBDIRS) + +$(SUBDIRS): FRC + @cd $@; pwd; $(MAKE) $(TARGET) + +FRC: diff --git a/usr/src/lib/pkcs11/pkcs11_kms/Makefile.com b/usr/src/lib/pkcs11/pkcs11_kms/Makefile.com new file mode 100644 index 0000000000..d9f7e19b22 --- /dev/null +++ b/usr/src/lib/pkcs11/pkcs11_kms/Makefile.com @@ -0,0 +1,99 @@ +# +# 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. +# + +LIBRARY= pkcs11_kms.a +VERS= .1 + +CORE_OBJECTS= \ + kmsAESCrypt.o \ + kmsAttributeUtil.o \ + kmsDecrypt.o \ + kmsDigest.o \ + kmsDualCrypt.o \ + kmsEncrypt.o \ + kmsGeneral.o \ + kmsKeys.o \ + kmsKeystoreUtil.o \ + kmsObject.o \ + kmsObjectUtil.o \ + kmsRand.o \ + kmsSession.o \ + kmsSessionUtil.o \ + kmsSign.o \ + kmsSlottable.o \ + kmsSlotToken.o \ + kmsVerify.o + +OBJECTS= $(CORE_OBJECTS) + +AESDIR= $(SRC)/common/crypto/aes +KMSAGENTDIR= $(SRC)/lib/libkmsagent/common + +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 -lsoftcrypto -lmd -lavl -lkmsagent + +CFLAGS += $(CCVERBOSE) + +CPPFLAGS += -DUSESOLARIS_AES -DKMSUSERPKCS12 + +ROOTLIBDIR= $(ROOT)/usr/lib/security +ROOTLIBDIR64= $(ROOT)/usr/lib/security/$(MACH64) + +lint \ +pics/kmsAESCrypt.o \ +pics/kmsEncrypt.o \ +pics/kmsDecrypt.o \ +pics/kmsSlotToken.o \ +pics/kmsKeystoreUtil.o \ +pics/kmsAttributeUtil.o := CPPFLAGS += -I$(AESDIR) -I$(SRC)/common/crypto + +CPPFLAGS += -I$(KMSAGENTDIR) + +.KEEP_STATE: + +all: $(LIBS) + +# +# -lkmsagent is not here because it is C++ and we don't lint C++ code. +# +LINTLDLIBS = -lc -lcryptoutil -lavl -lmd -lsoftcrypto + +LINTFLAGS64 += -errchk=longptr64 -errtags=yes + +lintcheck := SRCS = $(CORESRCS) +lintcheck := LDLIBS = -L$(ROOT)/lib -L$(ROOT)/usr/lib $(LINTLDLIBS) + +lintother: $$(OSRCS) + $(LINT.c) $(LINTCHECKFLAGS) $(OSRCS) $(LINTLDLIBS) + +lint: lintcheck + +include $(SRC)/lib/Makefile.targ diff --git a/usr/src/lib/pkcs11/pkcs11_kms/amd64/Makefile b/usr/src/lib/pkcs11/pkcs11_kms/amd64/Makefile new file mode 100644 index 0000000000..99c7ac2351 --- /dev/null +++ b/usr/src/lib/pkcs11/pkcs11_kms/amd64/Makefile @@ -0,0 +1,36 @@ +# +# 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) 2010, Oracle and/or its affiliates. All rights reserved. +# + + +include ../Makefile.com +include ../../../Makefile.lib.64 + +LINTCHECKFLAGS += -erroff=E_INCONS_ARG_DECL2 \ + -erroff=E_INCONS_ARG_USED2 \ + -erroff=E_INCONS_VAL_TYPE_DECL2 \ + -erroff=E_INCONS_VAL_TYPE_USED2 \ + -erroff=E_FUNC_DECL_VAR_ARG2 + +install: all $(ROOTLIBS64) $(ROOTLINKS64) diff --git a/usr/src/lib/pkcs11/pkcs11_kms/common/kmsAESCrypt.c b/usr/src/lib/pkcs11/pkcs11_kms/common/kmsAESCrypt.c new file mode 100644 index 0000000000..0e384e0043 --- /dev/null +++ b/usr/src/lib/pkcs11/pkcs11_kms/common/kmsAESCrypt.c @@ -0,0 +1,1046 @@ +/* + * 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 <pthread.h> +#include <stdlib.h> +#include <string.h> +#include <strings.h> +#include <sys/types.h> +#include <security/cryptoki.h> +#include <aes_impl.h> + +#include "kmsSession.h" +#include "kmsObject.h" +#include "kmsCrypt.h" + +/* + * Add padding bytes with the value of length of padding. + */ +static void +kms_add_pkcs7_padding(CK_BYTE *buf, int block_size, CK_ULONG data_len) +{ + + ulong_t i, pad_len; + CK_BYTE pad_value; + + pad_len = block_size - (data_len % block_size); + pad_value = (CK_BYTE)pad_len; + + for (i = 0; i < pad_len; i++) + buf[i] = pad_value; +} + +/* + * Remove padding bytes. + */ +static CK_RV +kms_remove_pkcs7_padding(CK_BYTE *pData, CK_ULONG padded_len, + CK_ULONG *pulDataLen, int block_size) +{ + + CK_BYTE pad_value; + ulong_t i; + + pad_value = pData[padded_len - 1]; + + + /* Make sure there is a valid padding value. */ + if ((pad_value == 0) || (pad_value > block_size)) + return (CKR_ENCRYPTED_DATA_INVALID); + + for (i = padded_len - pad_value; i < padded_len; i++) + if (pad_value != pData[i]) + return (CKR_ENCRYPTED_DATA_INVALID); + + *pulDataLen = padded_len - pad_value; + return (CKR_OK); +} + +/* + * Allocate context for the active encryption or decryption operation, and + * generate AES key schedule to speed up the operation. + */ +CK_RV +kms_aes_crypt_init_common(kms_session_t *session_p, + CK_MECHANISM_PTR pMechanism, kms_object_t *key_p, + boolean_t encrypt) +{ + size_t size; + kms_aes_ctx_t *kms_aes_ctx; + + kms_aes_ctx = calloc(1, sizeof (kms_aes_ctx_t)); + if (kms_aes_ctx == NULL) { + return (CKR_HOST_MEMORY); + } + + kms_aes_ctx->key_sched = aes_alloc_keysched(&size, 0); + + if (kms_aes_ctx->key_sched == NULL) { + free(kms_aes_ctx); + return (CKR_HOST_MEMORY); + } + + kms_aes_ctx->keysched_len = size; + + (void) pthread_mutex_lock(&session_p->session_mutex); + if (encrypt) { + /* Called by C_EncryptInit. */ + session_p->encrypt.context = kms_aes_ctx; + session_p->encrypt.mech.mechanism = pMechanism->mechanism; + } else { + /* Called by C_DecryptInit. */ + session_p->decrypt.context = kms_aes_ctx; + session_p->decrypt.mech.mechanism = pMechanism->mechanism; + } + (void) pthread_mutex_unlock(&session_p->session_mutex); + + /* + * If this is a non-sensitive key and it does NOT have + * a key schedule yet, then allocate one and expand it. + * Otherwise, if it's a non-sensitive key, and it DOES have + * a key schedule already attached to it, just copy the + * pre-expanded schedule to the context and avoid the + * extra key schedule expansion operation. + */ + if (!(key_p->bool_attr_mask & SENSITIVE_BOOL_ON)) { + if (OBJ_KEY_SCHED(key_p) == NULL) { + void *ks; + + (void) pthread_mutex_lock(&key_p->object_mutex); + if (OBJ_KEY_SCHED(key_p) == NULL) { + ks = aes_alloc_keysched(&size, 0); + if (ks == NULL) { + (void) pthread_mutex_unlock( + &key_p->object_mutex); + free(kms_aes_ctx); + return (CKR_HOST_MEMORY); + } +#ifdef __sparcv9 + /* LINTED */ + aes_init_keysched(OBJ_SEC_VALUE(key_p), (uint_t) + (OBJ_SEC_VALUE_LEN(key_p) * 8), ks); +#else /* !__sparcv9 */ + aes_init_keysched(OBJ_SEC_VALUE(key_p), + (OBJ_SEC_VALUE_LEN(key_p) * 8), ks); +#endif /* __sparcv9 */ + OBJ_KEY_SCHED_LEN(key_p) = size; + OBJ_KEY_SCHED(key_p) = ks; + } + (void) pthread_mutex_unlock(&key_p->object_mutex); + } + (void) memcpy(kms_aes_ctx->key_sched, OBJ_KEY_SCHED(key_p), + OBJ_KEY_SCHED_LEN(key_p)); + kms_aes_ctx->keysched_len = OBJ_KEY_SCHED_LEN(key_p); + } else { + /* + * Initialize key schedule for AES. aes_init_keysched() + * requires key length in bits. + */ +#ifdef __sparcv9 + /* LINTED */ + aes_init_keysched(OBJ_SEC_VALUE(key_p), (uint_t) + (OBJ_SEC_VALUE_LEN(key_p) * 8), kms_aes_ctx->key_sched); +#else /* !__sparcv9 */ + aes_init_keysched(OBJ_SEC_VALUE(key_p), + (OBJ_SEC_VALUE_LEN(key_p) * 8), kms_aes_ctx->key_sched); +#endif /* __sparcv9 */ + } + return (CKR_OK); +} + +/* + * kms_aes_encrypt_common() + * + * Arguments: + * session_p: pointer to kms_session_t struct + * pData: pointer to the input data to be encrypted + * ulDataLen: length of the input data + * pEncrypted: pointer to the output data after encryption + * pulEncryptedLen: pointer to the length of the output data + * update: boolean flag indicates caller is kms_encrypt + * or kms_encrypt_update + * + * Description: + * This function calls the corresponding encrypt routine based + * on the mechanism. + * + * Returns: + * CKR_OK: success + * CKR_BUFFER_TOO_SMALL: the output buffer provided by application + * is too small + * CKR_FUNCTION_FAILED: encrypt function failed + * CKR_DATA_LEN_RANGE: the input data is not a multiple of blocksize + */ +CK_RV +kms_aes_encrypt_common(kms_session_t *session_p, CK_BYTE_PTR pData, + CK_ULONG ulDataLen, CK_BYTE_PTR pEncrypted, + CK_ULONG_PTR pulEncryptedLen, boolean_t update) +{ + + int rc = 0; + CK_RV rv = CKR_OK; + kms_aes_ctx_t *kms_aes_ctx = + (kms_aes_ctx_t *)session_p->encrypt.context; + aes_ctx_t *aes_ctx; + CK_MECHANISM_TYPE mechanism = session_p->encrypt.mech.mechanism; + CK_BYTE *in_buf = NULL; + CK_BYTE *out_buf = NULL; + CK_ULONG out_len; + CK_ULONG total_len; + CK_ULONG remain; + + if (mechanism == CKM_AES_CTR) + goto do_encryption; + + /* + * AES only takes input length that is a multiple of blocksize + * for C_Encrypt function with the mechanism CKM_AES_ECB or + * CKM_AES_CBC. + * + * AES allows any input length for C_Encrypt function with the + * mechanism CKM_AES_CBC_PAD and for C_EncryptUpdate function. + */ + if ((!update) && (mechanism != CKM_AES_CBC_PAD)) { + if ((ulDataLen % AES_BLOCK_LEN) != 0) { + rv = CKR_DATA_LEN_RANGE; + goto cleanup; + } + } + + if (!update) { + /* + * Called by C_Encrypt + */ + if (mechanism == CKM_AES_CBC_PAD) { + /* + * For CKM_AES_CBC_PAD, compute output length to + * count for the padding. If the length of input + * data is a multiple of blocksize, then make output + * length to be the sum of the input length and + * one blocksize. Otherwise, output length will + * be rounded up to the next multiple of blocksize. + */ + out_len = AES_BLOCK_LEN * + (ulDataLen / AES_BLOCK_LEN + 1); + } else { + /* + * For non-padding mode, the output length will + * be same as the input length. + */ + out_len = ulDataLen; + } + + /* + * If application asks for the length of the output buffer + * to hold the ciphertext? + */ + if (pEncrypted == NULL) { + *pulEncryptedLen = out_len; + return (CKR_OK); + } + + /* Is the application-supplied buffer large enough? */ + if (*pulEncryptedLen < out_len) { + *pulEncryptedLen = out_len; + return (CKR_BUFFER_TOO_SMALL); + } + + /* Encrypt pad bytes in a separate operation */ + if (mechanism == CKM_AES_CBC_PAD) { + out_len -= AES_BLOCK_LEN; + } + + in_buf = pData; + out_buf = pEncrypted; + } else { + /* + * Called by C_EncryptUpdate + * + * Add the lengths of last remaining data and current + * plaintext together to get the total input length. + */ + total_len = kms_aes_ctx->remain_len + ulDataLen; + + /* + * If the total input length is less than one blocksize, + * or if the total input length is just one blocksize and + * the mechanism is CKM_AES_CBC_PAD, we will need to delay + * encryption until when more data comes in next + * C_EncryptUpdate or when C_EncryptFinal is called. + */ + if ((total_len < AES_BLOCK_LEN) || + ((mechanism == CKM_AES_CBC_PAD) && + (total_len == AES_BLOCK_LEN))) { + if (pEncrypted != NULL) { + /* + * Save input data and its length in + * the remaining buffer of AES context. + */ + (void) memcpy(kms_aes_ctx->data + + kms_aes_ctx->remain_len, pData, ulDataLen); + kms_aes_ctx->remain_len += ulDataLen; + } + + /* Set encrypted data length to 0. */ + *pulEncryptedLen = 0; + return (CKR_OK); + } + + /* Compute the length of remaing data. */ + remain = total_len % AES_BLOCK_LEN; + + /* + * Make sure that the output length is a multiple of + * blocksize. + */ + out_len = total_len - remain; + + /* + * If application asks for the length of the output buffer + * to hold the ciphertext? + */ + if (pEncrypted == NULL) { + *pulEncryptedLen = out_len; + return (CKR_OK); + } + + /* Is the application-supplied buffer large enough? */ + if (*pulEncryptedLen < out_len) { + *pulEncryptedLen = out_len; + return (CKR_BUFFER_TOO_SMALL); + } + + if (kms_aes_ctx->remain_len != 0) { + /* + * Copy last remaining data and current input data + * to the output buffer. + */ + (void) memmove(pEncrypted + kms_aes_ctx->remain_len, + pData, out_len - kms_aes_ctx->remain_len); + (void) memcpy(pEncrypted, kms_aes_ctx->data, + kms_aes_ctx->remain_len); + bzero(kms_aes_ctx->data, kms_aes_ctx->remain_len); + + in_buf = pEncrypted; + } else { + in_buf = pData; + } + out_buf = pEncrypted; + } + +do_encryption: + /* + * Begin Encryption now. + */ + switch (mechanism) { + + case CKM_AES_CBC: + case CKM_AES_CBC_PAD: + { + crypto_data_t out; + + out.cd_format = CRYPTO_DATA_RAW; + out.cd_offset = 0; + out.cd_length = out_len; + out.cd_raw.iov_base = (char *)out_buf; + out.cd_raw.iov_len = out_len; + + /* Encrypt multiple blocks of data. */ + rc = aes_encrypt_contiguous_blocks( + (aes_ctx_t *)kms_aes_ctx->aes_cbc, + (char *)in_buf, out_len, &out); + + if (rc != 0) + goto encrypt_failed; + + if (update) { + /* + * For encrypt update, if there is remaining data, + * save it and its length in the context. + */ + if (remain != 0) + (void) memcpy(kms_aes_ctx->data, pData + + (ulDataLen - remain), remain); + kms_aes_ctx->remain_len = remain; + } else if (mechanism == CKM_AES_CBC_PAD) { + /* + * Save the remainder of the input + * block in a temporary block because + * we dont want to overrun the buffer + * by tacking on pad bytes. + */ + CK_BYTE tmpblock[AES_BLOCK_LEN]; + (void) memcpy(tmpblock, in_buf + out_len, + ulDataLen - out_len); + kms_add_pkcs7_padding(tmpblock + + (ulDataLen - out_len), + AES_BLOCK_LEN, ulDataLen - out_len); + + out.cd_offset = out_len; + out.cd_length = AES_BLOCK_LEN; + out.cd_raw.iov_base = (char *)out_buf; + out.cd_raw.iov_len = out_len + AES_BLOCK_LEN; + + /* Encrypt last block containing pad bytes. */ + rc = aes_encrypt_contiguous_blocks( + (aes_ctx_t *)kms_aes_ctx->aes_cbc, + (char *)tmpblock, AES_BLOCK_LEN, &out); + + out_len += AES_BLOCK_LEN; + } + + if (rc == 0) { + *pulEncryptedLen = out_len; + break; + } +encrypt_failed: + *pulEncryptedLen = 0; + rv = CKR_FUNCTION_FAILED; + goto cleanup; + } + default: + rv = CKR_MECHANISM_INVALID; + goto cleanup; + } /* end switch */ + + if (update) + return (CKR_OK); + + /* + * The following code will be executed if the caller is + * kms_encrypt() or an error occurred. The encryption + * operation will be terminated so we need to do some cleanup. + */ +cleanup: + (void) pthread_mutex_lock(&session_p->session_mutex); + aes_ctx = (aes_ctx_t *)kms_aes_ctx->aes_cbc; + if (aes_ctx != NULL) { + bzero(aes_ctx->ac_keysched, aes_ctx->ac_keysched_len); + free(kms_aes_ctx->aes_cbc); + } + + bzero(kms_aes_ctx->key_sched, kms_aes_ctx->keysched_len); + free(kms_aes_ctx->key_sched); + free(session_p->encrypt.context); + session_p->encrypt.context = NULL; + (void) pthread_mutex_unlock(&session_p->session_mutex); + + return (rv); +} + + +/* + * kms_aes_decrypt_common() + * + * Arguments: + * session_p: pointer to kms_session_t struct + * pEncrypted: pointer to the input data to be decrypted + * ulEncryptedLen: length of the input data + * pData: pointer to the output data + * pulDataLen: pointer to the length of the output data + * Update: boolean flag indicates caller is kms_decrypt + * or kms_decrypt_update + * + * Description: + * This function calls the corresponding decrypt routine based + * on the mechanism. + * + * Returns: + * CKR_OK: success + * CKR_BUFFER_TOO_SMALL: the output buffer provided by application + * is too small + * CKR_ENCRYPTED_DATA_LEN_RANGE: the input data is not a multiple + * of blocksize + * CKR_FUNCTION_FAILED: decrypt function failed + */ +CK_RV +kms_aes_decrypt_common(kms_session_t *session_p, CK_BYTE_PTR pEncrypted, + CK_ULONG ulEncryptedLen, CK_BYTE_PTR pData, + CK_ULONG_PTR pulDataLen, boolean_t update) +{ + + int rc = 0; + CK_RV rv = CKR_OK; + kms_aes_ctx_t *kms_aes_ctx = + (kms_aes_ctx_t *)session_p->decrypt.context; + aes_ctx_t *aes_ctx; + CK_MECHANISM_TYPE mechanism = session_p->decrypt.mech.mechanism; + CK_BYTE *in_buf = NULL; + CK_BYTE *out_buf = NULL; + CK_ULONG out_len; + CK_ULONG total_len; + CK_ULONG remain; + + if (mechanism == CKM_AES_CTR) + goto do_decryption; + + /* + * AES only takes input length that is a multiple of 16 bytes + * for C_Decrypt function with the mechanism CKM_AES_ECB, + * CKM_AES_CBC or CKM_AES_CBC_PAD. + * + * AES allows any input length for C_DecryptUpdate function. + */ + if (!update) { + /* + * Called by C_Decrypt + */ + if ((ulEncryptedLen % AES_BLOCK_LEN) != 0) { + rv = CKR_ENCRYPTED_DATA_LEN_RANGE; + goto cleanup; + } + + /* + * If application asks for the length of the output buffer + * to hold the plaintext? + */ + if (pData == NULL) { + *pulDataLen = ulEncryptedLen; + return (CKR_OK); + } + + /* Is the application-supplied buffer large enough? */ + if (mechanism != CKM_AES_CBC_PAD) { + if (*pulDataLen < ulEncryptedLen) { + *pulDataLen = ulEncryptedLen; + return (CKR_BUFFER_TOO_SMALL); + } + out_len = ulEncryptedLen; + } else { + /* + * For CKM_AES_CBC_PAD, we don't know how + * many bytes for padding at this time, so + * we'd assume one block was padded. + */ + if (*pulDataLen < (ulEncryptedLen - AES_BLOCK_LEN)) { + *pulDataLen = ulEncryptedLen - AES_BLOCK_LEN; + return (CKR_BUFFER_TOO_SMALL); + } + out_len = ulEncryptedLen - AES_BLOCK_LEN; + } + in_buf = pEncrypted; + out_buf = pData; + } else { + /* + * Called by C_DecryptUpdate + * + * Add the lengths of last remaining data and current + * input data together to get the total input length. + */ + total_len = kms_aes_ctx->remain_len + ulEncryptedLen; + + /* + * If the total input length is less than one blocksize, + * or if the total input length is just one blocksize and + * the mechanism is CKM_AES_CBC_PAD, we will need to delay + * decryption until when more data comes in next + * C_DecryptUpdate or when C_DecryptFinal is called. + */ + if ((total_len < AES_BLOCK_LEN) || + ((mechanism == CKM_AES_CBC_PAD) && + (total_len == AES_BLOCK_LEN))) { + if (pData != NULL) { + /* + * Save input data and its length in + * the remaining buffer of AES context. + */ + (void) memcpy(kms_aes_ctx->data + + kms_aes_ctx->remain_len, + pEncrypted, ulEncryptedLen); + kms_aes_ctx->remain_len += ulEncryptedLen; + } + + /* Set output data length to 0. */ + *pulDataLen = 0; + return (CKR_OK); + } + + /* Compute the length of remaing data. */ + remain = total_len % AES_BLOCK_LEN; + + /* + * Make sure that the output length is a multiple of + * blocksize. + */ + out_len = total_len - remain; + + if (mechanism == CKM_AES_CBC_PAD) { + /* + * If the input data length is a multiple of + * blocksize, then save the last block of input + * data in the remaining buffer. C_DecryptFinal + * will handle this last block of data. + */ + if (remain == 0) { + remain = AES_BLOCK_LEN; + out_len -= AES_BLOCK_LEN; + } + } + + /* + * If application asks for the length of the output buffer + * to hold the plaintext? + */ + if (pData == NULL) { + *pulDataLen = out_len; + return (CKR_OK); + } + + /* + * Is the application-supplied buffer large enough? + */ + if (*pulDataLen < out_len) { + *pulDataLen = out_len; + return (CKR_BUFFER_TOO_SMALL); + } + + if (kms_aes_ctx->remain_len != 0) { + /* + * Copy last remaining data and current input data + * to the output buffer. + */ + (void) memmove(pData + kms_aes_ctx->remain_len, + pEncrypted, out_len - kms_aes_ctx->remain_len); + (void) memcpy(pData, kms_aes_ctx->data, + kms_aes_ctx->remain_len); + bzero(kms_aes_ctx->data, kms_aes_ctx->remain_len); + + in_buf = pData; + } else { + in_buf = pEncrypted; + } + out_buf = pData; + } + +do_decryption: + /* + * Begin Decryption. + */ + switch (mechanism) { + case CKM_AES_CBC: + case CKM_AES_CBC_PAD: + { + crypto_data_t out; + CK_ULONG rem_len; + uint8_t last_block[AES_BLOCK_LEN]; + + out.cd_format = CRYPTO_DATA_RAW; + out.cd_offset = 0; + out.cd_length = out_len; + out.cd_raw.iov_base = (char *)out_buf; + out.cd_raw.iov_len = out_len; + + /* Decrypt multiple blocks of data. */ + rc = aes_decrypt_contiguous_blocks( + (aes_ctx_t *)kms_aes_ctx->aes_cbc, + (char *)in_buf, out_len, &out); + + if (rc != 0) + goto decrypt_failed; + + if ((mechanism == CKM_AES_CBC_PAD) && (!update)) { + /* Decrypt last block containing pad bytes. */ + out.cd_offset = 0; + out.cd_length = AES_BLOCK_LEN; + out.cd_raw.iov_base = (char *)last_block; + out.cd_raw.iov_len = AES_BLOCK_LEN; + + /* Decrypt last block containing pad bytes. */ + rc = aes_decrypt_contiguous_blocks( + (aes_ctx_t *)kms_aes_ctx->aes_cbc, + (char *)in_buf + out_len, AES_BLOCK_LEN, &out); + + if (rc != 0) + goto decrypt_failed; + + /* + * Remove padding bytes after decryption of + * ciphertext block to produce the original + * plaintext. + */ + rv = kms_remove_pkcs7_padding(last_block, + AES_BLOCK_LEN, &rem_len, AES_BLOCK_LEN); + if (rv == CKR_OK) { + if (rem_len != 0) + (void) memcpy(out_buf + out_len, + last_block, rem_len); + *pulDataLen = out_len + rem_len; + } else { + *pulDataLen = 0; + goto cleanup; + } + } else { + *pulDataLen = out_len; + } + + if (update) { + /* + * For decrypt update, if there is remaining data, + * save it and its length in the context. + */ + if (remain != 0) + (void) memcpy(kms_aes_ctx->data, pEncrypted + + (ulEncryptedLen - remain), remain); + kms_aes_ctx->remain_len = remain; + } + + if (rc == 0) + break; +decrypt_failed: + *pulDataLen = 0; + rv = CKR_FUNCTION_FAILED; + goto cleanup; + } + default: + rv = CKR_MECHANISM_INVALID; + goto cleanup; + } /* end switch */ + + if (update) + return (CKR_OK); + + /* + * The following code will be executed if the caller is + * kms_decrypt() or an error occurred. The decryption + * operation will be terminated so we need to do some cleanup. + */ +cleanup: + (void) pthread_mutex_lock(&session_p->session_mutex); + aes_ctx = (aes_ctx_t *)kms_aes_ctx->aes_cbc; + if (aes_ctx != NULL) { + bzero(aes_ctx->ac_keysched, aes_ctx->ac_keysched_len); + free(kms_aes_ctx->aes_cbc); + } + + bzero(kms_aes_ctx->key_sched, kms_aes_ctx->keysched_len); + free(kms_aes_ctx->key_sched); + free(session_p->decrypt.context); + session_p->decrypt.context = NULL; + (void) pthread_mutex_unlock(&session_p->session_mutex); + + return (rv); +} + + +/* + * Allocate and initialize a context for AES CBC mode of operation. + */ +void * +aes_cbc_ctx_init(void *key_sched, size_t size, uint8_t *ivec) +{ + aes_ctx_t *aes_ctx; + + if ((aes_ctx = calloc(1, sizeof (aes_ctx_t))) == NULL) + return (NULL); + + aes_ctx->ac_keysched = key_sched; + + (void) memcpy(&aes_ctx->ac_iv[0], ivec, AES_BLOCK_LEN); + + aes_ctx->ac_lastp = (uint8_t *)aes_ctx->ac_iv; + aes_ctx->ac_keysched_len = size; + aes_ctx->ac_flags |= CBC_MODE; + + return ((void *)aes_ctx); +} + +/* + * kms_encrypt_final() + * + * Arguments: + * session_p: pointer to kms_session_t struct + * pLastEncryptedPart: pointer to the last encrypted data part + * pulLastEncryptedPartLen: pointer to the length of the last + * encrypted data part + * + * Description: + * called by C_EncryptFinal(). + * + * Returns: + * CKR_OK: success + * CKR_FUNCTION_FAILED: encrypt final function failed + * CKR_DATA_LEN_RANGE: remaining buffer contains bad length + */ +CK_RV +kms_aes_encrypt_final(kms_session_t *session_p, CK_BYTE_PTR pLastEncryptedPart, + CK_ULONG_PTR pulLastEncryptedPartLen) +{ + + CK_MECHANISM_TYPE mechanism = session_p->encrypt.mech.mechanism; + CK_ULONG out_len; + CK_RV rv = CKR_OK; + int rc; + + (void) pthread_mutex_lock(&session_p->session_mutex); + + if (session_p->encrypt.context == NULL) { + rv = CKR_OPERATION_NOT_INITIALIZED; + *pulLastEncryptedPartLen = 0; + + } + if (mechanism == CKM_AES_CBC_PAD) { + kms_aes_ctx_t *aes_ctx; + + aes_ctx = (kms_aes_ctx_t *)session_p->encrypt.context; + /* + * For CKM_AES_CBC_PAD, compute output length with + * padding. If the remaining buffer has one block + * of data, then output length will be two blocksize of + * ciphertext. If the remaining buffer has less than + * one block of data, then output length will be + * one blocksize. + */ + if (aes_ctx->remain_len == AES_BLOCK_LEN) + out_len = 2 * AES_BLOCK_LEN; + else + out_len = AES_BLOCK_LEN; + + if (pLastEncryptedPart == NULL) { + /* + * Application asks for the length of the output + * buffer to hold the ciphertext. + */ + *pulLastEncryptedPartLen = out_len; + goto clean1; + } else { + crypto_data_t out; + + /* Copy remaining data to the output buffer. */ + (void) memcpy(pLastEncryptedPart, aes_ctx->data, + aes_ctx->remain_len); + + /* + * Add padding bytes prior to encrypt final. + */ + kms_add_pkcs7_padding(pLastEncryptedPart + + aes_ctx->remain_len, AES_BLOCK_LEN, + aes_ctx->remain_len); + + out.cd_format = CRYPTO_DATA_RAW; + out.cd_offset = 0; + out.cd_length = out_len; + out.cd_raw.iov_base = (char *)pLastEncryptedPart; + out.cd_raw.iov_len = out_len; + + /* Encrypt multiple blocks of data. */ + rc = aes_encrypt_contiguous_blocks( + (aes_ctx_t *)aes_ctx->aes_cbc, + (char *)pLastEncryptedPart, out_len, &out); + + if (rc == 0) { + *pulLastEncryptedPartLen = out_len; + } else { + *pulLastEncryptedPartLen = 0; + rv = CKR_FUNCTION_FAILED; + } + + /* Cleanup memory space. */ + free(aes_ctx->aes_cbc); + bzero(aes_ctx->key_sched, + aes_ctx->keysched_len); + free(aes_ctx->key_sched); + } + } else if (mechanism == CKM_AES_CBC) { + kms_aes_ctx_t *aes_ctx; + + aes_ctx = (kms_aes_ctx_t *)session_p->encrypt.context; + /* + * CKM_AES_CBC and CKM_AES_ECB does not do any padding, + * so when the final is called, the remaining buffer + * should not contain any more data. + */ + *pulLastEncryptedPartLen = 0; + if (aes_ctx->remain_len != 0) { + rv = CKR_DATA_LEN_RANGE; + } else { + if (pLastEncryptedPart == NULL) + goto clean1; + } + + /* Cleanup memory space. */ + free(aes_ctx->aes_cbc); + bzero(aes_ctx->key_sched, aes_ctx->keysched_len); + free(aes_ctx->key_sched); + } else { + rv = CKR_MECHANISM_INVALID; + } + + free(session_p->encrypt.context); + session_p->encrypt.context = NULL; +clean1: + (void) pthread_mutex_unlock(&session_p->session_mutex); + return (rv); +} + +/* + * kms_decrypt_final() + * + * Arguments: + * session_p: pointer to kms_session_t struct + * pLastPart: pointer to the last recovered data part + * pulLastPartLen: pointer to the length of the last recovered data part + * + * Description: + * called by C_DecryptFinal(). + * + * Returns: + * CKR_OK: success + * CKR_FUNCTION_FAILED: decrypt final function failed + * CKR_ENCRYPTED_DATA_LEN_RANGE: remaining buffer contains bad length + */ +CK_RV +kms_aes_decrypt_final(kms_session_t *session_p, CK_BYTE_PTR pLastPart, + CK_ULONG_PTR pulLastPartLen) +{ + + CK_MECHANISM_TYPE mechanism = session_p->decrypt.mech.mechanism; + CK_ULONG out_len; + CK_RV rv = CKR_OK; + int rc; + + (void) pthread_mutex_lock(&session_p->session_mutex); + + if (session_p->decrypt.context == NULL) { + rv = CKR_OPERATION_NOT_INITIALIZED; + *pulLastPartLen = 0; + goto clean2; + } + switch (mechanism) { + + case CKM_AES_CBC_PAD: + { + kms_aes_ctx_t *kms_aes_ctx; + kms_aes_ctx = (kms_aes_ctx_t *)session_p->decrypt.context; + + /* + * We should have only one block of data left in the + * remaining buffer. + */ + if (kms_aes_ctx->remain_len != AES_BLOCK_LEN) { + *pulLastPartLen = 0; + rv = CKR_ENCRYPTED_DATA_LEN_RANGE; + /* Cleanup memory space. */ + free(kms_aes_ctx->aes_cbc); + bzero(kms_aes_ctx->key_sched, + kms_aes_ctx->keysched_len); + free(kms_aes_ctx->key_sched); + + goto clean1; + } + + out_len = AES_BLOCK_LEN; + + /* + * If application asks for the length of the output buffer + * to hold the plaintext? + */ + if (pLastPart == NULL) { + *pulLastPartLen = out_len; + rv = CKR_OK; + goto clean2; + } else { + crypto_data_t out; + + /* Copy remaining data to the output buffer. */ + (void) memcpy(pLastPart, kms_aes_ctx->data, + AES_BLOCK_LEN); + + out.cd_format = CRYPTO_DATA_RAW; + out.cd_offset = 0; + out.cd_length = AES_BLOCK_LEN; + out.cd_raw.iov_base = (char *)pLastPart; + out.cd_raw.iov_len = AES_BLOCK_LEN; + + /* Decrypt final block of data. */ + rc = aes_decrypt_contiguous_blocks( + (aes_ctx_t *)kms_aes_ctx->aes_cbc, + (char *)pLastPart, AES_BLOCK_LEN, &out); + + if (rc == 0) { + /* + * Remove padding bytes after decryption of + * ciphertext block to produce the original + * plaintext. + */ + rv = kms_remove_pkcs7_padding(pLastPart, + AES_BLOCK_LEN, &out_len, AES_BLOCK_LEN); + if (rv != CKR_OK) + *pulLastPartLen = 0; + else + *pulLastPartLen = out_len; + } else { + *pulLastPartLen = 0; + rv = CKR_FUNCTION_FAILED; + } + + /* Cleanup memory space. */ + free(kms_aes_ctx->aes_cbc); + bzero(kms_aes_ctx->key_sched, + kms_aes_ctx->keysched_len); + free(kms_aes_ctx->key_sched); + + } + + break; + } + + case CKM_AES_CBC: + { + kms_aes_ctx_t *kms_aes_ctx; + + kms_aes_ctx = (kms_aes_ctx_t *)session_p->decrypt.context; + /* + * CKM_AES_CBC and CKM_AES_ECB does not do any padding, + * so when the final is called, the remaining buffer + * should not contain any more data. + */ + *pulLastPartLen = 0; + if (kms_aes_ctx->remain_len != 0) { + rv = CKR_ENCRYPTED_DATA_LEN_RANGE; + } else { + if (pLastPart == NULL) + goto clean2; + } + + /* Cleanup memory space. */ + free(kms_aes_ctx->aes_cbc); + bzero(kms_aes_ctx->key_sched, kms_aes_ctx->keysched_len); + free(kms_aes_ctx->key_sched); + + break; + } + default: + /* PKCS11: The mechanism only supports single-part operation. */ + rv = CKR_MECHANISM_INVALID; + break; + } + +clean1: + free(session_p->decrypt.context); + session_p->decrypt.context = NULL; + +clean2: + (void) pthread_mutex_unlock(&session_p->session_mutex); + + return (rv); + +} diff --git a/usr/src/lib/pkcs11/pkcs11_kms/common/kmsAttributeUtil.c b/usr/src/lib/pkcs11/pkcs11_kms/common/kmsAttributeUtil.c new file mode 100644 index 0000000000..69d62bec2b --- /dev/null +++ b/usr/src/lib/pkcs11/pkcs11_kms/common/kmsAttributeUtil.c @@ -0,0 +1,1726 @@ +/* + * 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 <stdlib.h> +#include <string.h> +#include <security/cryptoki.h> +#include <sys/crypto/common.h> +#include <aes_impl.h> +#include "kmsGlobal.h" +#include "kmsObject.h" +#include "kmsSession.h" +#include "kmsSlot.h" + +/* + * This attribute table is used by the kms_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 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 +}; + +/* + * Validate the attribute by using binary search algorithm. + */ +CK_RV +kms_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 +kms_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 = kms_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 +kms_cleanup_extra_attr(kms_object_t *object_p) +{ + + CK_ATTRIBUTE_INFO_PTR extra_attr; + CK_ATTRIBUTE_INFO_PTR tmp; + + if (object_p == NULL) + return; + + 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 +kms_add_extra_attr(CK_ATTRIBUTE_PTR template, kms_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 +kms_copy_extra_attr(CK_ATTRIBUTE_INFO_PTR old_attrp, + kms_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(kms_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(kms_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 (kms_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 boolean data type attribute value from an object for the + * specified attribute to the template. + */ +CK_RV +get_bool_attr_from_object(kms_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(kms_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); + } +} + +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; + } +} + +/* + * 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. + */ +static CK_RV +kms_parse_common_attrs(CK_ATTRIBUTE_PTR template, uint64_t *attr_mask_p) +{ + CK_RV rv = CKR_OK; + kms_slot_t *pslot = get_slotinfo(); + + switch (template->type) { + case CKA_CLASS: + break; + case CKA_TOKEN: + if ((*(CK_BBOOL *)template->pValue) == TRUE) + *attr_mask_p |= TOKEN_BOOL_ON; + 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_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 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 +kms_build_secret_key_object(CK_ATTRIBUTE_PTR template, + CK_ULONG ulAttrNum, kms_object_t *new_object) +{ + int i; + CK_KEY_TYPE keytype = (CK_KEY_TYPE)~0UL; + uint64_t attr_mask; + CK_RV rv = CKR_OK; + int isLabel = 0; + /* Must not set flags */ + int isValueLen = 0; + CK_ATTRIBUTE string_tmp; + secret_key_obj_t *sck; + + string_tmp.pValue = NULL; + + /* + * If the object was pulled from the KMS, the + * attributes are encoded in the object record + * before this function is called, we don't + * want to overwrite them unless the attribute + * template says differently. + */ + if (new_object->bool_attr_mask != 0) + attr_mask = new_object->bool_attr_mask; + else + attr_mask = SECRET_KEY_DEFAULT; + + /* 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 = kms_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: + 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; + if (template[i].pValue != NULL) + sck->sk_value_len = + *(CK_ULONG_PTR)template[i].pValue; + else + sck->sk_value_len = 0; + 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 = kms_parse_common_attrs(&template[i], &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_AES: + if (!isValueLen) { + rv = CKR_TEMPLATE_INCOMPLETE; + goto fail_cleanup; + } + if (sck->sk_value_len != AES_MIN_KEY_BYTES && + sck->sk_value_len != AES_192_KEY_BYTES && + sck->sk_value_len != AES_MAX_KEY_BYTES) { + rv = CKR_ATTRIBUTE_VALUE_INVALID; + goto fail_cleanup; + } + break; + + case CKK_RC4: + case CKK_GENERIC_SECRET: + case CKK_BLOWFISH: + case CKK_DES: + case CKK_DES2: + case CKK_DES3: + default: + rv = CKR_TEMPLATE_INCONSISTENT; + goto fail_cleanup; + } + + /* Set up object. */ + new_object->bool_attr_mask = attr_mask; + if (isLabel) { + rv = kms_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. + */ + kms_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 + */ +CK_RV +kms_build_object(CK_ATTRIBUTE_PTR template, CK_ULONG ulAttrNum, + kms_object_t *new_object) +{ + 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 = kms_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_SECRET_KEY: + rv = kms_build_secret_key_object(template, ulAttrNum, + new_object); + break; + + case CKO_DOMAIN_PARAMETERS: + case CKO_DATA: + case CKO_CERTIFICATE: + case CKO_HW_FEATURE: + case CKO_VENDOR_DEFINED: + case CKO_PUBLIC_KEY: + case CKO_PRIVATE_KEY: + 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 +kms_get_common_attrs(kms_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); + } + + *((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)); + break; + + 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 +kms_get_common_key_attrs(kms_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 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. + */ +CK_RV +kms_get_secret_key_attribute(kms_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_AES: + /* + * 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); + } + + case CKK_RC4: + case CKK_GENERIC_SECRET: + case CKK_RC5: + case CKK_DES: + case CKK_DES2: + case CKK_DES3: + case CKK_CDMF: + case CKK_BLOWFISH: + 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 = kms_get_common_key_attrs(object_p, template); + if (rv == CKR_ATTRIBUTE_TYPE_INVALID) { + rv = kms_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 +kms_get_attribute(kms_object_t *object_p, CK_ATTRIBUTE_PTR template) +{ + + CK_RV rv = CKR_OK; + CK_OBJECT_CLASS class = object_p->class; + + switch (class) { + case CKO_SECRET_KEY: + rv = kms_get_secret_key_attribute(object_p, template); + break; + + case CKO_PRIVATE_KEY: + case CKO_PUBLIC_KEY: + 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). + */ +static CK_RV +kms_set_common_key_attribute(kms_object_t *object_p, + CK_ATTRIBUTE_PTR template, boolean_t copy) +{ + + kms_slot_t *pslot = get_slotinfo(); + 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_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 Secret Key Object. + * + * Rule: The attributes marked with footnote number "8" in the PKCS11 + * spec may be modified (p.88 in PKCS11 spec.). + */ +static CK_RV +kms_set_secret_key_attribute(kms_object_t *object_p, + CK_ATTRIBUTE_PTR template, boolean_t copy) +{ + 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 (kms_set_common_key_attribute(object_p, + template, copy)); + } + + /* + * 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 +kms_set_attribute(kms_object_t *object_p, CK_ATTRIBUTE_PTR template, + boolean_t copy) +{ + + CK_RV rv = CKR_OK; + CK_OBJECT_CLASS class = object_p->class; + + switch (class) { + + case CKO_SECRET_KEY: + rv = kms_set_secret_key_attribute(object_p, template, + copy); + break; + + case CKO_PUBLIC_KEY: + case CKO_PRIVATE_KEY: + 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); +} + +CK_RV +kms_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 +kms_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 secret_found = B_FALSE; + int num_secret_key_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_secret_key_attrs = + sizeof (SECRET_KEY_ATTRS) / sizeof (CK_ATTRIBUTE_TYPE); + + /* + * Get the list of objects class that might contain + * some attributes. + */ + for (i = 0; i < ulCount; i++) { + 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; + } + } + } + } + *num_result_pclasses = num_pclasses; +} + + +boolean_t +kms_find_match_attrs(kms_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; + boolean_t compare_attr, 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_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: + attr_mask = (obj->bool_attr_mask) & TOKEN_BOOL_ON; + 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: + break; + default: + return (B_FALSE); + } + break; + case CKA_VALUE_BITS: + case CKA_PRIME_BITS: + case CKA_SUBPRIME_BITS: + 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_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, kms_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); +} diff --git a/usr/src/lib/pkcs11/pkcs11_kms/common/kmsCrypt.h b/usr/src/lib/pkcs11/pkcs11_kms/common/kmsCrypt.h new file mode 100644 index 0000000000..ade758af93 --- /dev/null +++ b/usr/src/lib/pkcs11/pkcs11_kms/common/kmsCrypt.h @@ -0,0 +1,75 @@ +/* + * 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. + */ + +#ifndef _KMSCRYPT_H +#define _KMSCRYPT_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include <sys/types.h> +#include <security/pkcs11t.h> +#ifdef USESOLARIS_AES +#include <modes/modes.h> +#else +#include <aes_cbc_crypt.h> +#define CBC_MODE AES_CBC_MODE +#endif +#include <aes_impl.h> +#include "kmsObject.h" +#include "kmsSession.h" + +typedef struct kms_aes_ctx { + void *key_sched; /* pointer to key schedule */ + size_t keysched_len; /* Length of the key schedule */ + uint8_t ivec[AES_BLOCK_LEN]; /* initialization vector */ + uint8_t data[AES_BLOCK_LEN]; /* for use by update */ + size_t remain_len; /* for use by update */ + void *aes_cbc; /* to be used by CBC mode */ +} kms_aes_ctx_t; + +/* + * Function Prototypes. + */ +void *aes_cbc_ctx_init(void *, size_t, uint8_t *); + +CK_RV kms_aes_crypt_init_common(kms_session_t *, CK_MECHANISM_PTR, + kms_object_t *, boolean_t); + +CK_RV kms_aes_encrypt_common(kms_session_t *, CK_BYTE_PTR, CK_ULONG, + CK_BYTE_PTR, CK_ULONG_PTR, boolean_t); + +CK_RV kms_aes_decrypt_common(kms_session_t *, CK_BYTE_PTR, CK_ULONG, + CK_BYTE_PTR, CK_ULONG_PTR, boolean_t); + +CK_RV kms_aes_encrypt_final(kms_session_t *, CK_BYTE_PTR, CK_ULONG_PTR); +CK_RV kms_aes_decrypt_final(kms_session_t *, CK_BYTE_PTR, CK_ULONG_PTR); + +void kms_crypt_cleanup(kms_session_t *, boolean_t, boolean_t); + +#ifdef __cplusplus +} +#endif + +#endif /* _KMSCRYPT_H */ diff --git a/usr/src/lib/pkcs11/pkcs11_kms/common/kmsDecrypt.c b/usr/src/lib/pkcs11/pkcs11_kms/common/kmsDecrypt.c new file mode 100644 index 0000000000..96d4bdcb30 --- /dev/null +++ b/usr/src/lib/pkcs11/pkcs11_kms/common/kmsDecrypt.c @@ -0,0 +1,405 @@ +/* + * 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 <string.h> +#include <strings.h> +#include <security/cryptoki.h> +#include "kmsGlobal.h" +#include "kmsCrypt.h" + + +/* + * kms_decrypt_init() + * + * Arguments: + * session_p: pointer to kms_session_t struct + * pMechanism: pointer to CK_MECHANISM struct provided by application + * key_p: pointer to key kms_object_t struct + * + * Description: + * called by C_DecryptInit(). This function calls the corresponding + * decrypt init routine based on the mechanism. + * + * Returns: + * CKR_OK: success + * CKR_HOST_MEMORY: run out of system memory + * CKR_MECHANISM_PARAM_INVALID: invalid parameters in mechanism + * CKR_MECHANISM_INVALID: invalid mechanism type + * CKR_KEY_TYPE_INCONSISTENT: incorrect type of key to use + * with the specified mechanism + */ +CK_RV +kms_decrypt_init(kms_session_t *session_p, CK_MECHANISM_PTR pMechanism, + kms_object_t *key_p) +{ + + CK_RV rv; + + switch (pMechanism->mechanism) { + case CKM_AES_CBC: + case CKM_AES_CBC_PAD: + { + kms_aes_ctx_t *kms_aes_ctx; + + if (key_p->key_type != CKK_AES) { + return (CKR_KEY_TYPE_INCONSISTENT); + } + + if ((pMechanism->pParameter == NULL) || + (pMechanism->ulParameterLen != AES_BLOCK_LEN)) { + return (CKR_MECHANISM_PARAM_INVALID); + } + + rv = kms_aes_crypt_init_common(session_p, pMechanism, + key_p, B_FALSE); + + if (rv != CKR_OK) + return (rv); + + (void) pthread_mutex_lock(&session_p->session_mutex); + + kms_aes_ctx = (kms_aes_ctx_t *)session_p->decrypt.context; + + /* Save Initialization Vector (IV) in the context. */ + (void) memcpy(kms_aes_ctx->ivec, pMechanism->pParameter, + AES_BLOCK_LEN); + + /* Allocate a context for AES cipher-block chaining. */ + kms_aes_ctx->aes_cbc = (void *)aes_cbc_ctx_init( + kms_aes_ctx->key_sched, kms_aes_ctx->keysched_len, + kms_aes_ctx->ivec); + + if (kms_aes_ctx->aes_cbc == NULL) { + bzero(kms_aes_ctx->key_sched, + kms_aes_ctx->keysched_len); + free(kms_aes_ctx->key_sched); + free(session_p->decrypt.context); + session_p->decrypt.context = NULL; + (void) pthread_mutex_unlock(&session_p->session_mutex); + return (CKR_HOST_MEMORY); + } + + (void) pthread_mutex_unlock(&session_p->session_mutex); + + return (rv); + } + default: + return (CKR_MECHANISM_INVALID); + } +} + +CK_RV +C_DecryptInit(CK_SESSION_HANDLE hSession, CK_MECHANISM_PTR pMechanism, + CK_OBJECT_HANDLE hKey) +{ + CK_RV rv; + kms_session_t *session_p; + kms_object_t *key_p; + boolean_t lock_held = B_FALSE; + + if (!kms_initialized) + return (CKR_CRYPTOKI_NOT_INITIALIZED); + + /* Obtain the session pointer. */ + rv = handle2session(hSession, &session_p); + if (rv != CKR_OK) + return (rv); + + if (pMechanism == NULL) { + rv = CKR_ARGUMENTS_BAD; + goto clean_exit; + } + + /* Obtain the object pointer. */ + HANDLE2OBJECT(hKey, key_p, rv); + if (rv != CKR_OK) + goto clean_exit; + + /* Check to see if key object allows for decryption. */ + if (!(key_p->bool_attr_mask & DECRYPT_BOOL_ON)) { + rv = CKR_KEY_FUNCTION_NOT_PERMITTED; + goto clean_exit1; + } + + (void) pthread_mutex_lock(&session_p->session_mutex); + lock_held = B_TRUE; + + /* Check to see if decrypt operation is already active. */ + if (session_p->decrypt.flags & CRYPTO_OPERATION_ACTIVE) { + /* free the memory to avoid memory leak */ + kms_crypt_cleanup(session_p, B_FALSE, lock_held); + } + + /* + * 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; + + (void) pthread_mutex_unlock(&session_p->session_mutex); + lock_held = B_FALSE; + + rv = kms_decrypt_init(session_p, pMechanism, key_p); + + if (rv != CKR_OK) { + (void) pthread_mutex_lock(&session_p->session_mutex); + session_p->decrypt.flags &= ~CRYPTO_OPERATION_ACTIVE; + lock_held = B_TRUE; + } + +clean_exit1: + OBJ_REFRELE(key_p); +clean_exit: + REFRELE(session_p, lock_held); + 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; + kms_session_t *session_p; + boolean_t lock_held = B_FALSE; + + if (!kms_initialized) + return (CKR_CRYPTOKI_NOT_INITIALIZED); + + /* Obatin the session pointer. */ + rv = handle2session(hSession, &session_p); + if (rv != CKR_OK) + return (rv); + + /* + * Only check if input buffer is null. How to handle zero input + * length depents on the mechanism in use. For secret key mechanisms, + * unpadded ones yield zero length output, but padded ones always + * result in smaller than original, possibly zero, length output. + */ + if (pEncryptedData == NULL) { + rv = CKR_ARGUMENTS_BAD; + goto clean_exit; + } + + /* + * 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; + } + + (void) pthread_mutex_lock(&session_p->session_mutex); + lock_held = B_TRUE; + + /* Application must call C_DecryptInit before calling C_Decrypt. */ + if (!(session_p->decrypt.flags & CRYPTO_OPERATION_ACTIVE)) { + REFRELE(session_p, lock_held); + return (CKR_OPERATION_NOT_INITIALIZED); + } + + /* + * C_Decrypt must be called without intervening C_DecryptUpdate + * calls. + */ + if (session_p->decrypt.flags & CRYPTO_OPERATION_UPDATE) { + /* + * C_Decrypt can not be used to terminate a multi-part + * operation, so we'll leave the active decrypt operation + * flag on and let the application continue with the + * decrypt update operation. + */ + REFRELE(session_p, lock_held); + return (CKR_FUNCTION_FAILED); + } + + (void) pthread_mutex_unlock(&session_p->session_mutex); + lock_held = B_FALSE; + + rv = kms_aes_decrypt_common(session_p, pEncryptedData, + ulEncryptedData, pData, pulDataLen, FALSE); + + if ((rv == CKR_BUFFER_TOO_SMALL) || + (pData == NULL && rv == CKR_OK)) { + /* + * 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, lock_held); + return (rv); + } + +clean_exit: + /* Clear context, free key, and release session counter */ + kms_crypt_cleanup(session_p, B_FALSE, B_FALSE); + + 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; + kms_session_t *session_p; + boolean_t lock_held = B_FALSE; + + if (!kms_initialized) + return (CKR_CRYPTOKI_NOT_INITIALIZED); + + /* Obtain the session pointer. */ + rv = handle2session(hSession, &session_p); + if (rv != CKR_OK) + return (rv); + + /* + * Only check if input buffer is null. How to handle zero input + * length depents on the mechanism in use. For secret key mechanisms, + * unpadded ones yeild zero length output, but padded ones always + * result in smaller than original, possibly zero, length output. + */ + 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); + lock_held = B_TRUE; + + /* + * Application must call C_DecryptInit before calling + * C_DecryptUpdate. + */ + if (!(session_p->decrypt.flags & CRYPTO_OPERATION_ACTIVE)) { + REFRELE(session_p, lock_held); + return (CKR_OPERATION_NOT_INITIALIZED); + } + + session_p->decrypt.flags |= CRYPTO_OPERATION_UPDATE; + + (void) pthread_mutex_unlock(&session_p->session_mutex); + lock_held = B_FALSE; + + rv = kms_aes_decrypt_common(session_p, pEncryptedPart, + ulEncryptedPartLen, pPart, pulPartLen, B_TRUE); + + /* + * If CKR_OK or CKR_BUFFER_TOO_SMALL, don't terminate the + * current decryption operation. + */ + if ((rv == CKR_OK) || (rv == CKR_BUFFER_TOO_SMALL)) { + REFRELE(session_p, lock_held); + return (rv); + } + +clean_exit: + /* + * After an error occurred, terminate the current decrypt + * operation by resetting the active and update flags. + */ + kms_crypt_cleanup(session_p, B_FALSE, lock_held); + + return (rv); +} + +CK_RV +C_DecryptFinal(CK_SESSION_HANDLE hSession, CK_BYTE_PTR pLastPart, + CK_ULONG_PTR pulLastPartLen) +{ + + CK_RV rv; + kms_session_t *session_p; + boolean_t lock_held = B_FALSE; + + if (!kms_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); + lock_held = B_TRUE; + + /* + * Application must call C_DecryptInit before calling + * C_DecryptFinal. + */ + if (!(session_p->decrypt.flags & CRYPTO_OPERATION_ACTIVE)) { + REFRELE(session_p, lock_held); + return (CKR_OPERATION_NOT_INITIALIZED); + } + + (void) pthread_mutex_unlock(&session_p->session_mutex); + lock_held = B_FALSE; + + rv = kms_aes_decrypt_final(session_p, pLastPart, pulLastPartLen); + + if ((rv == CKR_BUFFER_TOO_SMALL) || + (pLastPart == NULL && rv == CKR_OK)) { + /* + * 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, lock_held); + return (rv); + } + + /* Terminates the active encrypt operation. */ + (void) pthread_mutex_lock(&session_p->session_mutex); + session_p->decrypt.flags = 0; + lock_held = B_TRUE; + REFRELE(session_p, lock_held); + return (rv); + +clean_exit: + /* Terminates the active decrypt operation */ + kms_crypt_cleanup(session_p, B_FALSE, lock_held); + + return (rv); +} diff --git a/usr/src/lib/pkcs11/pkcs11_kms/common/kmsDigest.c b/usr/src/lib/pkcs11/pkcs11_kms/common/kmsDigest.c new file mode 100644 index 0000000000..5472ccb15b --- /dev/null +++ b/usr/src/lib/pkcs11/pkcs11_kms/common/kmsDigest.c @@ -0,0 +1,78 @@ +/* + * 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 <security/cryptoki.h> +#include "kmsGlobal.h" + +/*ARGSUSED*/ +CK_RV +C_DigestInit(CK_SESSION_HANDLE hSession, CK_MECHANISM_PTR pMechanism) +{ + if (!kms_initialized) + return (CKR_CRYPTOKI_NOT_INITIALIZED); + + return (CKR_FUNCTION_NOT_SUPPORTED); +} + +/*ARGSUSED*/ +CK_RV +C_Digest(CK_SESSION_HANDLE hSession, CK_BYTE_PTR pData, CK_ULONG ulDataLen, + CK_BYTE_PTR pDigest, CK_ULONG_PTR pulDigestLen) +{ + if (!kms_initialized) + return (CKR_CRYPTOKI_NOT_INITIALIZED); + + return (CKR_FUNCTION_NOT_SUPPORTED); +} + +/*ARGSUSED*/ +CK_RV +C_DigestUpdate(CK_SESSION_HANDLE hSession, CK_BYTE_PTR pPart, + CK_ULONG ulPartLen) +{ + if (!kms_initialized) + return (CKR_CRYPTOKI_NOT_INITIALIZED); + + return (CKR_FUNCTION_NOT_SUPPORTED); +} + +/*ARGSUSED*/ +CK_RV +C_DigestKey(CK_SESSION_HANDLE hSession, CK_OBJECT_HANDLE hKey) +{ + if (!kms_initialized) + return (CKR_CRYPTOKI_NOT_INITIALIZED); + + return (CKR_FUNCTION_NOT_SUPPORTED); +} + +/*ARGSUSED*/ +CK_RV +C_DigestFinal(CK_SESSION_HANDLE hSession, CK_BYTE_PTR pDigest, + CK_ULONG_PTR pulDigestLen) +{ + if (!kms_initialized) + return (CKR_CRYPTOKI_NOT_INITIALIZED); + + return (CKR_FUNCTION_NOT_SUPPORTED); +} diff --git a/usr/src/lib/pkcs11/pkcs11_kms/common/kmsDualCrypt.c b/usr/src/lib/pkcs11/pkcs11_kms/common/kmsDualCrypt.c new file mode 100644 index 0000000000..1bde2b0946 --- /dev/null +++ b/usr/src/lib/pkcs11/pkcs11_kms/common/kmsDualCrypt.c @@ -0,0 +1,74 @@ +/* + * 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 <security/cryptoki.h> +#include "kmsGlobal.h" + +/*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 (!kms_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 (!kms_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 (!kms_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 (!kms_initialized) + return (CKR_CRYPTOKI_NOT_INITIALIZED); + + return (CKR_FUNCTION_NOT_SUPPORTED); +} diff --git a/usr/src/lib/pkcs11/pkcs11_kms/common/kmsEncrypt.c b/usr/src/lib/pkcs11/pkcs11_kms/common/kmsEncrypt.c new file mode 100644 index 0000000000..38ef8add89 --- /dev/null +++ b/usr/src/lib/pkcs11/pkcs11_kms/common/kmsEncrypt.c @@ -0,0 +1,424 @@ +/* + * 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 <string.h> +#include <strings.h> +#include <security/cryptoki.h> +#include "kmsGlobal.h" +#include "kmsCrypt.h" + +static CK_RV +kms_encrypt_init(kms_session_t *session_p, CK_MECHANISM_PTR pMechanism, + kms_object_t *key_p) +{ + CK_RV rv = CKR_OK; + kms_aes_ctx_t *kms_aes_ctx; + + if (pMechanism->mechanism != CKM_AES_CBC && + pMechanism->mechanism != CKM_AES_CBC_PAD) + return (CKR_MECHANISM_INVALID); + + if (key_p->key_type != CKK_AES) { + return (CKR_KEY_TYPE_INCONSISTENT); + } + + if ((pMechanism->pParameter == NULL) || + (pMechanism->ulParameterLen != AES_BLOCK_LEN)) { + return (CKR_MECHANISM_PARAM_INVALID); + } + + rv = kms_aes_crypt_init_common(session_p, pMechanism, + key_p, B_TRUE); + + if (rv != CKR_OK) + return (rv); + + (void) pthread_mutex_lock(&session_p->session_mutex); + + kms_aes_ctx = (kms_aes_ctx_t *)session_p->encrypt.context; + /* Copy Initialization Vector (IV) into the context. */ + + (void) memcpy(kms_aes_ctx->ivec, pMechanism->pParameter, + AES_BLOCK_LEN); + + /* Allocate a context for AES cipher-block chaining. */ + kms_aes_ctx->aes_cbc = (void *)aes_cbc_ctx_init( + kms_aes_ctx->key_sched, kms_aes_ctx->keysched_len, + kms_aes_ctx->ivec); + + if (kms_aes_ctx->aes_cbc == NULL) { + bzero(kms_aes_ctx->key_sched, + kms_aes_ctx->keysched_len); + free(kms_aes_ctx->key_sched); + free(session_p->encrypt.context); + session_p->encrypt.context = NULL; + rv = CKR_HOST_MEMORY; + } + + (void) pthread_mutex_unlock(&session_p->session_mutex); + + return (rv); +} + +void +kms_crypt_cleanup(kms_session_t *session_p, boolean_t encrypt, + boolean_t lock_held) +{ + kms_active_op_t *active_op; + boolean_t lock_true = B_TRUE; + kms_aes_ctx_t *kms_aes_ctx; + aes_ctx_t *aes_ctx; + + if (!lock_held) + (void) pthread_mutex_lock(&session_p->session_mutex); + + active_op = (encrypt) ? &(session_p->encrypt) : &(session_p->decrypt); + if (active_op->mech.mechanism != CKM_AES_CBC && + active_op->mech.mechanism != CKM_AES_CBC_PAD) + return; + + kms_aes_ctx = (kms_aes_ctx_t *)active_op->context; + + if (kms_aes_ctx != NULL) { + aes_ctx = (aes_ctx_t *)kms_aes_ctx->aes_cbc; + if (aes_ctx != NULL) { + bzero(aes_ctx->ac_keysched, aes_ctx->ac_keysched_len); + free(kms_aes_ctx->aes_cbc); + bzero(kms_aes_ctx->key_sched, + kms_aes_ctx->keysched_len); + free(kms_aes_ctx->key_sched); + } + } + if (active_op->context != NULL) { + free(active_op->context); + active_op->context = NULL; + } + active_op->flags = 0; + if (!lock_held) + REFRELE(session_p, lock_true); +} + +CK_RV +C_EncryptInit(CK_SESSION_HANDLE hSession, CK_MECHANISM_PTR pMechanism, + CK_OBJECT_HANDLE hKey) +{ + CK_RV rv; + kms_session_t *session_p; + kms_object_t *key_p; + boolean_t lock_held = B_FALSE; + + if (!kms_initialized) + return (CKR_CRYPTOKI_NOT_INITIALIZED); + + /* Obtain the session pointer. */ + rv = handle2session(hSession, &session_p); + if (rv != CKR_OK) + return (rv); + + if (pMechanism == NULL) { + rv = CKR_ARGUMENTS_BAD; + goto clean_exit; + } + + if (pMechanism->mechanism != CKM_AES_CBC && + pMechanism->mechanism != CKM_AES_CBC_PAD) + return (CKR_MECHANISM_INVALID); + + /* Obtain the object pointer. */ + HANDLE2OBJECT(hKey, key_p, rv); + if (rv != CKR_OK) + goto clean_exit; + + /* Check to see if key object allows for encryption. */ + if (!(key_p->bool_attr_mask & ENCRYPT_BOOL_ON)) { + rv = CKR_KEY_FUNCTION_NOT_PERMITTED; + goto clean_exit1; + } + + (void) pthread_mutex_lock(&session_p->session_mutex); + lock_held = B_TRUE; + + /* Check to see if encrypt operation is already active. */ + if (session_p->encrypt.flags & CRYPTO_OPERATION_ACTIVE) { + /* free the memory to avoid memory leak */ + kms_crypt_cleanup(session_p, B_TRUE, lock_held); + } + + /* + * 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; + + (void) pthread_mutex_unlock(&session_p->session_mutex); + lock_held = B_FALSE; + + rv = kms_encrypt_init(session_p, pMechanism, key_p); + + if (rv != CKR_OK) { + (void) pthread_mutex_lock(&session_p->session_mutex); + session_p->encrypt.flags &= ~CRYPTO_OPERATION_ACTIVE; + lock_held = B_TRUE; + } + +clean_exit1: + OBJ_REFRELE(key_p); +clean_exit: + REFRELE(session_p, 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; + kms_session_t *session_p; + boolean_t lock_held = B_FALSE; + + if (!kms_initialized) + return (CKR_CRYPTOKI_NOT_INITIALIZED); + + /* Obtain the session pointer. */ + rv = handle2session(hSession, &session_p); + if (rv != CKR_OK) + return (rv); + + /* + * Only check if input buffer is null. How to handle zero input + * length depends on the mechanism in use. For secret key mechanisms, + * unpadded ones yield zero length output, but padded ones always + * result in greater than zero length output. + */ + if (pData == NULL) { + rv = CKR_ARGUMENTS_BAD; + goto clean_exit; + } + + /* + * 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; + } + + (void) pthread_mutex_lock(&session_p->session_mutex); + lock_held = B_TRUE; + + /* Application must call C_EncryptInit before calling C_Encrypt. */ + if (!(session_p->encrypt.flags & CRYPTO_OPERATION_ACTIVE)) { + REFRELE(session_p, 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, lock_held); + return (CKR_FUNCTION_FAILED); + } + + (void) pthread_mutex_unlock(&session_p->session_mutex); + lock_held = B_FALSE; + + rv = kms_aes_encrypt_common(session_p, pData, ulDataLen, pEncryptedData, + pulEncryptedDataLen, 0); + + if ((rv == CKR_BUFFER_TOO_SMALL) || + (pEncryptedData == NULL && rv == CKR_OK)) { + /* + * 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, lock_held); + return (rv); + } + +clean_exit: + /* Clear context, free key, and release session counter */ + kms_crypt_cleanup(session_p, B_TRUE, B_FALSE); + 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; + kms_session_t *session_p; + boolean_t lock_held = B_FALSE; + + if (!kms_initialized) + return (CKR_CRYPTOKI_NOT_INITIALIZED); + + /* Obtain the session pointer. */ + rv = handle2session(hSession, &session_p); + if (rv != CKR_OK) + return (rv); + + /* + * Only check if input buffer is null. How to handle zero input + * length depends on the mechanism in use. For secret key mechanisms, + * unpadded ones yeild zero length output, but padded ones always + * result in greater than zero length output. + */ + 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); + lock_held = B_TRUE; + + /* + * Application must call C_EncryptInit before calling + * C_EncryptUpdate. + */ + if (!(session_p->encrypt.flags & CRYPTO_OPERATION_ACTIVE)) { + REFRELE(session_p, lock_held); + return (CKR_OPERATION_NOT_INITIALIZED); + } + + session_p->encrypt.flags |= CRYPTO_OPERATION_UPDATE; + + (void) pthread_mutex_unlock(&session_p->session_mutex); + lock_held = B_FALSE; + + rv = kms_aes_encrypt_common(session_p, pPart, ulPartLen, + pEncryptedPart, pulEncryptedPartLen, B_TRUE); + + /* + * If CKR_OK or CKR_BUFFER_TOO_SMALL, don't terminate the + * current encryption operation. + */ + if ((rv == CKR_OK) || (rv == CKR_BUFFER_TOO_SMALL)) { + REFRELE(session_p, lock_held); + return (rv); + } + +clean_exit: + /* + * After an error occurred, terminate the current encrypt + * operation by resetting the active and update flags. + */ + kms_crypt_cleanup(session_p, B_TRUE, lock_held); + + return (rv); +} + + +CK_RV +C_EncryptFinal(CK_SESSION_HANDLE hSession, CK_BYTE_PTR pLastEncryptedPart, + CK_ULONG_PTR pulLastEncryptedPartLen) +{ + + CK_RV rv; + kms_session_t *session_p; + boolean_t lock_held = B_FALSE; + + if (!kms_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); + lock_held = B_TRUE; + + /* + * Application must call C_EncryptInit before calling + * C_EncryptFinal. + */ + if (!(session_p->encrypt.flags & CRYPTO_OPERATION_ACTIVE)) { + REFRELE(session_p, lock_held); + return (CKR_OPERATION_NOT_INITIALIZED); + } + + (void) pthread_mutex_unlock(&session_p->session_mutex); + lock_held = B_FALSE; + + rv = kms_aes_encrypt_final(session_p, pLastEncryptedPart, + pulLastEncryptedPartLen); + + if ((rv == CKR_BUFFER_TOO_SMALL) || + (pLastEncryptedPart == NULL && rv == CKR_OK)) { + /* + * 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, lock_held); + return (rv); + } + + /* Terminates the active encrypt operation. */ + (void) pthread_mutex_lock(&session_p->session_mutex); + session_p->encrypt.flags = 0; + lock_held = B_TRUE; + REFRELE(session_p, lock_held); + + return (rv); + +clean_exit: + /* Terminates the active encrypt operation. */ + kms_crypt_cleanup(session_p, B_TRUE, lock_held); + + return (rv); +} diff --git a/usr/src/lib/pkcs11/pkcs11_kms/common/kmsGeneral.c b/usr/src/lib/pkcs11/pkcs11_kms/common/kmsGeneral.c new file mode 100644 index 0000000000..192d53827f --- /dev/null +++ b/usr/src/lib/pkcs11/pkcs11_kms/common/kmsGeneral.c @@ -0,0 +1,548 @@ +/* + * 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 <fcntl.h> +#include <pthread.h> +#include <strings.h> +#include <unistd.h> /* for pid */ +#include <errno.h> +#include <security/cryptoki.h> + +#include "kmsKeystoreUtil.h" +#include "kmsGlobal.h" +#include "kmsSession.h" +#include "kmsSlot.h" + +/* + * PKCS#11 KMS Crypto provider module. + * + * This module provides access to a Key Management System (v 2.0) + * through the Solaris Cryptographic Framework interfaces (PKCS#11). + * + * PREREQUISITES + * ============= + * 1. You must have access to a KMS on the network and you must + * know the IP address and name of the "Agent" assigned to + * you and the passphrase needed to access the Agent information. + * + * 2. The token configuration must be completed prior + * to using this provider using the kmscfg(1m) utility. + * + * This provider provides support for 3 AES mechanisms: + * CKM_AES_KEY_GEN (for 256 bit keys only) + * CKM_AES_CBC (encrypt/decrypt) + * CKM_AES_CBC_PAD (encrypt/decrypt) + * + * DETAILS + * ======= + * Each user has their own local configuration for the KMS. + * The local configuration information is typically located + * in a private token directory - /var/tmp/kms/$USERNAME + * The location may be overridden using an environment variable + * $KMSTOKEN_DIR. The user's private token namespace is configured + * using kmscfg(1M) which establishes the directory and populates + * it with a simple configuration file that this module later uses + * to access the KMS. + * + * INITIALIZING + * ============ + * Once the token configuration is established, C_InitToken + * is used to initialize the first contact with the KMS. This + * will cause the provider to contact the KMS and download + * the profile configuration data, a server certificate, and a + * private entity key and certificate (in a PKCS#12 file). + * Once the above data is collected it is stored under $KMSTOKEN_DIR. + * The user may then proceed with normal PKCS#11 activity. + * + * LOGIN + * ===== + * The concept of a "Login" is established when the user provides + * a PIN that will successfully unwrap the private data in the + * PKCS#12 file downloaded earlier when C_InitToken was called. + * If the PKCS#12 file is successfully opened, then the user + * is considered "logged in" and may use the private key and + * certificate to initiate secure communications with the KMS. + * + * CHANGE PIN + * ========== + * The C_SetPIN interface may be used to change the passphrase + * on the PKCS#12 file and thus effectively change the passphrase + * for the token itself (even though the wrapped private key and + * certificate do not change). + * + * KEY STORAGE + * =========== + * Keys generated in the KMS are always kept securely in the KMS. + * The local token area contains only a list of CKA_LABEL values + * for all successfully created keys, no sensitive key data + * is stored on the client system. When a key is "destroyed", the + * local references to that key's label is removed and it is no + * longer visible to the token provider. + * + * NOTE: The KMS itself does not have an interface for destroying + * keys, it only allows for the keys to be disassociated from + * a particular "DataUnit". Key labels should not be re-used. + */ +#pragma init(kms_init) +#pragma fini(kms_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 kms_initialized = B_FALSE; +static pid_t kms_pid = 0; + + +/* protects kms_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; +kms_elem_t **kms_mechhash; /* Hash table for kCF mech numbers */ + +static void kms_finalize_common(); +static void kms_cleanup_library(); +static void kms_init(); +static void kms_fini(); +static void kms_fork_prepare(); +static void kms_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 (kms_initialized) { + if (initialize_pid == kms_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 kms_cleanup_library to close + * out any state from the parent, and then + * continue on. + */ + kms_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); + } + } + + /* Create the hash table */ + kms_mechhash = calloc(KMECH_HASHTABLE_SIZE, sizeof (void *)); + if (kms_mechhash == NULL) { + (void) pthread_mutex_unlock(&globalmutex); + return (CKR_HOST_MEMORY); + } + + /* Initialize the slot table */ + rv = kms_slottable_init(); + if (rv != CKR_OK) { + free(kms_mechhash); + goto end; + } + + /* 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; + + rv = KMS_Initialize(); + if (rv != CKR_OK) { + free(kms_mechhash); + goto end; + } + + kms_initialized = B_TRUE; + kms_pid = initialize_pid; + +end: + (void) pthread_mutex_unlock(&globalmutex); + + return (CKR_OK); +} + +/* + * C_Finalize is a wrapper around kms_finalize_common. The + * globalmutex should be locked by C_Finalize(). + */ +CK_RV +C_Finalize(CK_VOID_PTR pReserved) +{ + (void) pthread_mutex_lock(&globalmutex); + + if (!kms_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 + */ + kms_delete_all_sessions(B_FALSE); + + kms_finalize_common(); + + (void) pthread_mutex_unlock(&globalmutex); + + return (CKR_OK); +} + +/* + * kms_finalize_common() does the work for C_Finalize. globalmutex + * must be held before calling this function. + */ +static void +kms_finalize_common() { + + int i; + kms_elem_t *elem, *next; + kms_object_t *delay_free_obj, *tmpo; + kms_session_t *delay_free_ses, *tmps; + + cleanup_slottable(); + /* Walk the hash table and free all entries */ + for (i = 0; i < KMECH_HASHTABLE_SIZE; i++) { + elem = kms_mechhash[i]; + while (elem != NULL) { + next = elem->knext; + free(elem); + elem = next; + } + } + + free(kms_mechhash); + + kms_mechhash = NULL; + kms_initialized = B_FALSE; + kms_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; + } + obj_delay_freed.count = 0; + obj_delay_freed.first = NULL; + obj_delay_freed.last = NULL; + (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; + } + ses_delay_freed.count = 0; + ses_delay_freed.first = NULL; + ses_delay_freed.last = NULL; + (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 +kms_cleanup_library() +{ + kms_slot_t *pslot = get_slotinfo(); + + if (pslot) + kms_cleanup_pri_objects_in_slot(pslot, NULL); + + /* + * 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. + */ + kms_delete_all_sessions(B_TRUE); + + kms_finalize_common(); +} + +static void +kms_init() +{ + (void) pthread_atfork(kms_fork_prepare, kms_fork_after, + kms_fork_after); +} + +/* + * kms_fini() function required to make sure complete cleanup + * is done if pkcs11_kms is ever unloaded without + * a C_Finalize() call. + */ +static void +kms_fini() +{ + (void) pthread_mutex_lock(&globalmutex); + + (void) KMS_Finalize(); + + /* if we're not initilized, do not attempt to finalize */ + if (!kms_initialized) { + (void) pthread_mutex_unlock(&globalmutex); + return; + } + + kms_cleanup_library(); + + (void) pthread_mutex_unlock(&globalmutex); +} + +CK_RV +C_GetInfo(CK_INFO_PTR pInfo) +{ + if (!kms_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 + * kms_acquire_all_slots_mutexes() + * 3. obj_delay_freed.obj_to_be_free_mutex; + * 4. ses_delay_freed.ses_to_be_free_mutex + */ +void +kms_fork_prepare() +{ + (void) pthread_mutex_lock(&globalmutex); + if (kms_initialized) { + kms_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); + } +} + +/* + * Release in opposite order to kms_fork_prepare(). + * Function is used for parent and child. + */ +void +kms_fork_after() +{ + if (kms_initialized) { + (void) pthread_mutex_unlock( + &ses_delay_freed.ses_to_be_free_mutex); + (void) pthread_mutex_unlock( + &obj_delay_freed.obj_to_be_free_mutex); + kms_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); +} diff --git a/usr/src/lib/pkcs11/pkcs11_kms/common/kmsGlobal.h b/usr/src/lib/pkcs11/pkcs11_kms/common/kmsGlobal.h new file mode 100644 index 0000000000..224fd357fc --- /dev/null +++ b/usr/src/lib/pkcs11/pkcs11_kms/common/kmsGlobal.h @@ -0,0 +1,88 @@ +/* + * 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) 2010, Oracle and/or its affiliates. All rights reserved. + */ + +#ifndef _KMSGLOBAL_H +#define _KMSGLOBAL_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include <sys/crypto/common.h> +#include <security/cryptoki.h> +#include <security/pkcs11t.h> +#include "kmsObject.h" + +typedef struct kms_elem { + CK_MECHANISM_TYPE type; + struct kms_elem *knext; /* Next in hash chain */ +} kms_elem_t; + +extern kms_elem_t **kms_mechhash; +extern boolean_t kms_initialized; + +#define KMECH_HASHTABLE_SIZE 67 + +/* CK_INFO: Information about cryptoki */ +#define CRYPTOKI_VERSION_MAJOR 2 +#define CRYPTOKI_VERSION_MINOR 20 +#define MANUFACTURER_ID "Oracle Corporation " +#define LIBRARY_DESCRIPTION "Oracle Key Management System " +#define LIBRARY_VERSION_MAJOR 1 +#define LIBRARY_VERSION_MINOR 0 + +/* CK_SLOT_INFO: Information about our slot */ +#define SLOT_DESCRIPTION "Oracle Key Management System " \ + " " +#define KMS_TOKEN_LABEL "KMS " +#define KMS_TOKEN_MODEL " " +#define KMS_TOKEN_SERIAL " " +#define KMS_TOKEN_FLAGS CKF_LOGIN_REQUIRED +#define MAX_PIN_LEN 256 +#define MIN_PIN_LEN 1 +#define HARDWARE_VERSION_MAJOR 0 +#define HARDWARE_VERSION_MINOR 0 +#define FIRMWARE_VERSION_MAJOR 0 +#define FIRMWARE_VERSION_MINOR 0 + +CK_RV crypto2pkcs11_error_number(uint_t); +CK_RV kms_mech(CK_MECHANISM_TYPE); +unsigned char *get_symmetric_key_value(kms_object_t *); +void free_key_attributes(); + +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 process_found_objects(kms_session_t *, CK_OBJECT_HANDLE *, + CK_ULONG *); +CK_RV get_mechanism_info(kms_slot_t *, CK_MECHANISM_TYPE, + CK_MECHANISM_INFO_PTR, uint32_t *); +CK_RV kms_add_extra_attr(CK_ATTRIBUTE_PTR, kms_object_t *); + +#ifdef __cplusplus +} +#endif + +#endif /* _KMSGLOBAL_H */ diff --git a/usr/src/lib/pkcs11/pkcs11_kms/common/kmsKeys.c b/usr/src/lib/pkcs11/pkcs11_kms/common/kmsKeys.c new file mode 100644 index 0000000000..80d34542aa --- /dev/null +++ b/usr/src/lib/pkcs11/pkcs11_kms/common/kmsKeys.c @@ -0,0 +1,179 @@ +/* + * 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 <strings.h> +#include <errno.h> +#include <security/cryptoki.h> +#include <cryptoutil.h> +#include "kmsGlobal.h" +#include "kmsSession.h" +#include "kmsObject.h" +#include "kmsKeystoreUtil.h" + +static CK_RV +kms_generate_softkey(kms_object_t *keyp) +{ + if ((OBJ_SEC_VALUE(keyp) = malloc(OBJ_SEC_VALUE_LEN(keyp))) == NULL) + return (CKR_HOST_MEMORY); + + if (pkcs11_get_urandom(OBJ_SEC_VALUE(keyp), + OBJ_SEC_VALUE_LEN(keyp)) < 0) + return (CKR_DEVICE_ERROR); + + return (CKR_OK); +} + +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; + kms_session_t *session_p; + kms_object_t *new_objp = NULL; + kms_slot_t *pslot; + boolean_t ses_lock_held = B_FALSE; + + if (!kms_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; + } + + switch (pMechanism->mechanism) { + case CKM_AES_KEY_GEN: + break; + default: + rv = CKR_MECHANISM_INVALID; + goto failed_exit; + break; + } + + /* Create an object record */ + new_objp = kms_new_object(); + if (new_objp == NULL) + return (CKR_HOST_MEMORY); + + new_objp->mechanism = pMechanism->mechanism; + rv = kms_build_object(pTemplate, ulCount, new_objp); + if (rv != CKR_OK) + goto failed_exit; + + /* + * Generate the KMS key. + * + * This will put the AES key value from the KMS key into the + * key object record. + */ + if (new_objp->bool_attr_mask & TOKEN_BOOL_ON) + rv = KMS_GenerateKey(session_p, new_objp); + else + rv = kms_generate_softkey(new_objp); + + if (rv != CKR_OK) + goto failed_exit; + + if (new_objp->bool_attr_mask & TOKEN_BOOL_ON) { + pslot = get_slotinfo(); + if (pslot == NULL) { + rv = CKR_GENERAL_ERROR; + goto failed_exit; + } + kms_add_token_object_to_slot(new_objp, pslot); + } else { + kms_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); +} + +/*ARGSUSED*/ +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) +{ + if (!kms_initialized) + return (CKR_CRYPTOKI_NOT_INITIALIZED); + + return (CKR_FUNCTION_NOT_SUPPORTED); +} + +/*ARGSUSED*/ +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) +{ + if (!kms_initialized) + return (CKR_CRYPTOKI_NOT_INITIALIZED); + + return (CKR_FUNCTION_NOT_SUPPORTED); +} + +/*ARGSUSED*/ +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) +{ + if (!kms_initialized) + return (CKR_CRYPTOKI_NOT_INITIALIZED); + + return (CKR_FUNCTION_NOT_SUPPORTED); +} + +/*ARGSUSED*/ +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) +{ + if (!kms_initialized) + return (CKR_CRYPTOKI_NOT_INITIALIZED); + + return (CKR_FUNCTION_NOT_SUPPORTED); +} diff --git a/usr/src/lib/pkcs11/pkcs11_kms/common/kmsKeystoreUtil.c b/usr/src/lib/pkcs11/pkcs11_kms/common/kmsKeystoreUtil.c new file mode 100644 index 0000000000..2176aed030 --- /dev/null +++ b/usr/src/lib/pkcs11/pkcs11_kms/common/kmsKeystoreUtil.c @@ -0,0 +1,1227 @@ +/* + * 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) 2010, Oracle and/or its affiliates. All rights reserved. + */ + +#include <stdio.h> +#include <malloc.h> +#include <memory.h> +#include <strings.h> +#include <fcntl.h> +#include <errno.h> +#include <ctype.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <cryptoutil.h> +#include <unistd.h> +#include <utmpx.h> +#include <pthread.h> +#include <pwd.h> +#include <sha2.h> +#include <security/cryptoki.h> +#include <aes_impl.h> +#include <sys/avl.h> + +#include "kmsSession.h" +#include "kmsGlobal.h" +#include "kmsObject.h" + +static CK_RV +GetPKCS11StatusFromAgentStatus(KMS_AGENT_STATUS status); + +static char keystore_path[BUFSIZ]; +static boolean_t keystore_path_initialized = B_FALSE; +static time_t last_objlist_mtime = 0; +pthread_mutex_t objlist_mutex = PTHREAD_MUTEX_INITIALIZER; +pthread_mutex_t flock_mutex = PTHREAD_MUTEX_INITIALIZER; + +static struct flock fl = { + 0, + 0, + 0, + 0, + 0, + 0, + {0, 0, 0, 0} +}; + +#define KEYSTORE_PATH "/var/kms" +#define ALTERNATE_KEYSTORE_PATH "KMSTOKEN_DIR" +#define KMS_PROFILE_FILENAME "profile.cfg" +#define KMS_DATAUNIT_DESCRIPTION "Oracle PKCS11/KMS" +#define KMS_ATTR_DESC_PFX "PKCS#11v2.20: " +#define KMSTOKEN_CONFIG_FILENAME "kmstoken.cfg" +#define KMSTOKEN_LABELLIST_FILENAME "objlabels.lst" + +static void +kms_hash_string(char *label, uchar_t *hash) +{ + SHA2_CTX ctx; + + SHA2Init(SHA256, &ctx); + SHA2Update(&ctx, label, strlen(label)); + SHA2Final(hash, &ctx); +} + +static char * +get_username(char *username, int len) +{ + struct passwd pwd, *user_info; + long buflen; + char *pwdbuf = NULL; + + bzero(username, len); + + buflen = sysconf(_SC_GETPW_R_SIZE_MAX); + if (buflen == -1) + return (username); /* should not happen */ + + pwdbuf = calloc(1, buflen); + if (pwdbuf == NULL) + return (username); /* zero-ed earlier */ + + user_info = getpwuid_r(getuid(), &pwd, pwdbuf, buflen); + + if (user_info != NULL) + (void) strlcpy(username, user_info->pw_name, len); + + free(pwdbuf); + return (username); +} + +static char * +kms_get_keystore_path() +{ + char *env_val; + char username[sizeof (((struct utmpx *)0)->ut_user)]; + + if (!keystore_path_initialized) { + env_val = getenv(ALTERNATE_KEYSTORE_PATH); + bzero(keystore_path, sizeof (keystore_path)); + /* + * If it isn't set or is set to the empty string use the + * default location. We need to check for the empty string + * because some users "unset" environment variables by giving + * them no value, this isn't the same thing as removing it + * from the environment. + */ + if ((env_val == NULL) || (strcmp(env_val, "") == 0)) { + /* alternate path not specified, use /var/kms/$USER */ + (void) snprintf(keystore_path, + sizeof (keystore_path), "%s/%s", + KEYSTORE_PATH, + get_username(username, sizeof (username))); + } else { + (void) strlcpy(keystore_path, env_val, + sizeof (keystore_path)); + } + keystore_path_initialized = B_TRUE; + } + return (keystore_path); +} + +static char * +get_non_comment_line(char *cfgbuf, size_t cfglen, char *buf, size_t buflen) +{ + char *s = cfgbuf; + char *end = cfgbuf + cfglen; + char *f; + + /* Skip over blank lines CR/LF */ + while (s < end && (*s == '#' || *s == '\n' || *s == '\r')) { + /* check for comment sign */ + if (*s == '#') { + /* skip the rest of the line */ + while ((*s != '\n' || *s == '\r') && s < end) + s++; + } + if ((s < end) && (*s == '\n' || *s == '\r')) + s++; + } + + if (s < end) { + char save, *e; + f = s; /* mark the beginning. */ + /* Find the end of the line and null terminate it. */ + while (*s != '\n' && *s != '\r' && *s != '#' && s < end) s++; + save = *s; + *s = 0x00; + (void) strncpy(buf, f, buflen); + *s = save; + + /* Strip trailing whitespace */ + f = buf; + e = f + strlen(buf) - 1; + while (e >= f && isspace(*e)) { + *e = 0x00; + e--; + } + + } else { + /* If we reached the end, return NULL */ + s = NULL; + } +done: + return (s); +} + +static int +flock_fd(int fd, int cmd, pthread_mutex_t *mutex) +{ + int ret = 0; + + (void) pthread_mutex_lock(mutex); + + fl.l_type = cmd; + + while ((ret = fcntl(fd, F_SETLKW, &fl)) == -1) { + if (errno != EINTR) + break; + } + (void) pthread_mutex_unlock(mutex); + return (ret); +} + +/* + * Open the keystore description file in the specified mode. + * If the keystore doesn't exist, the "do_create_keystore" + * argument determines if the keystore should be created + */ +static int +open_and_lock_file(char *filename, int cmd, mode_t mode, + pthread_mutex_t *mutex) +{ + int fd; + + fd = open_nointr(filename, mode|O_NONBLOCK); + if (fd < 0) + return (fd); + + if (flock_fd(fd, cmd, mutex)) { + if (fd > 0) + (void) close(fd); + return (-1); + } + + return (fd); +} + +static int +kms_slurp_file(char *file, char *buf, size_t buflen) +{ + int n, fd, total = 0; + + fd = open_and_lock_file(file, F_RDLCK, O_RDONLY, &flock_mutex); + if (fd == -1) + return (-1); + + do { + n = readn_nointr(fd, &buf[total], buflen - total); + if (n != (buflen - total)) + break; + else + total += n; + } while (total < buflen); + + if (flock_fd(fd, F_UNLCK, &flock_mutex)) + total = -1; + + (void) close(fd); + + return (total); +} + +/* + * The KMS token is considered "initialized" if the file with the token + * configuration information is present. + */ +CK_BBOOL +kms_is_initialized() +{ + CK_BBOOL rv; + char *ksdir; + char cfgfile_path[BUFSIZ]; + struct stat statp; + + ksdir = kms_get_keystore_path(); + if (ksdir == NULL) + return (CKR_FUNCTION_FAILED); + + (void) snprintf(cfgfile_path, sizeof (cfgfile_path), + "%s/%s", ksdir, KMSTOKEN_CONFIG_FILENAME); + + if (stat(cfgfile_path, &statp)) + rv = FALSE; + else + rv = TRUE; + + return (rv); +} + +static CK_RV +kms_read_config_data(char *path, kms_cfg_info_t *cfginfo) +{ + CK_RV rv = CKR_OK; + char *cfgbuf = NULL; + char *ptr; + char buf[BUFSIZ]; + size_t buflen = 0, remain; + struct stat statp; + + if (path == NULL || cfginfo == NULL) + return (CKR_ARGUMENTS_BAD); + + if (stat(path, &statp) == -1) { + return (CKR_FUNCTION_FAILED); + } + + cfgbuf = calloc(1, statp.st_size); + if (cfgbuf == NULL) + return (CKR_HOST_MEMORY); + + buflen = kms_slurp_file(path, cfgbuf, statp.st_size); + if (buflen != statp.st_size) { + free(cfgbuf); + return (CKR_FUNCTION_FAILED); + } + + remain = buflen; + ptr = cfgbuf; + ptr = get_non_comment_line(ptr, remain, + cfginfo->name, sizeof (cfginfo->name)); + if (ptr == NULL) { + rv = CKR_FUNCTION_FAILED; + goto done; + } + remain = buflen - (ptr - cfgbuf); + ptr = get_non_comment_line(ptr, remain, + cfginfo->agentId, sizeof (cfginfo->agentId)); + if (ptr == 0) { + rv = CKR_FUNCTION_FAILED; + goto done; + } + remain = buflen - (ptr - cfgbuf); + ptr = get_non_comment_line(ptr, remain, + cfginfo->agentAddr, sizeof (cfginfo->agentAddr)); + if (ptr == 0) { + rv = CKR_FUNCTION_FAILED; + goto done; + } + remain = buflen - (ptr - cfgbuf); + ptr = get_non_comment_line(ptr, remain, buf, sizeof (buf)); + if (ptr == 0) { + rv = CKR_FUNCTION_FAILED; + goto done; + } + cfginfo->transTimeout = atoi(buf); + + remain = buflen - (ptr - cfgbuf); + ptr = get_non_comment_line(ptr, remain, buf, sizeof (buf)); + if (ptr == 0) { + rv = CKR_FUNCTION_FAILED; + goto done; + } + cfginfo->failoverLimit = atoi(buf); + + remain = buflen - (ptr - cfgbuf); + ptr = get_non_comment_line(ptr, remain, buf, sizeof (buf)); + if (ptr == 0) { + rv = CKR_FUNCTION_FAILED; + goto done; + } + cfginfo->discoveryFreq = atoi(buf); + + remain = buflen - (ptr - cfgbuf); + ptr = get_non_comment_line(ptr, remain, buf, sizeof (buf)); + if (ptr == 0) { + rv = CKR_FUNCTION_FAILED; + goto done; + } + cfginfo->securityMode = atoi(buf); +done: + if (cfgbuf != NULL) + free(cfgbuf); + return (rv); +} + +CK_BBOOL +kms_is_pin_set() +{ + CK_BBOOL rv = TRUE; + kms_cfg_info_t kmscfg; + struct stat statp; + char *ksdir; + char filepath[BUFSIZ]; + + ksdir = kms_get_keystore_path(); + if (ksdir == NULL) + return (FALSE); + + (void) snprintf(filepath, sizeof (filepath), + "%s/%s", ksdir, KMSTOKEN_CONFIG_FILENAME); + + if ((rv = kms_read_config_data(filepath, &kmscfg))) + return (FALSE); + + /* + * The PK12 file is only established once the user has enrolled + * and is thus considered having a PIN set. + */ + (void) snprintf(filepath, sizeof (filepath), + "%s/%s/%s", ksdir, kmscfg.agentId, CLIENT_PK12_FILE); + + if (stat(filepath, &statp)) + rv = FALSE; /* file doesn't exist. */ + else + rv = TRUE; /* File exists, PIN is set */ + + return (rv); +} + +void +kms_clear_label_list(avl_tree_t *tree) +{ + void *cookie = NULL; + objlabel_t *node; + + while ((node = avl_destroy_nodes(tree, &cookie)) != NULL) { + free(node->label); + free(node); + } +} + +static void +add_label_node(avl_tree_t *tree, char *label) +{ + avl_index_t where; + objlabel_t *node; + objlabel_t *newnode; + int i; + + if (tree == NULL || label == NULL) + return; + + /* Remove trailing CR */ + i = strlen(label) - 1; + while (i > 0 && label[i] == '\n') + label[i--] = 0x00; + + newnode = calloc(1, sizeof (objlabel_t)); + newnode->label = (char *)strdup(label); + if (newnode->label == NULL) { + free(newnode); + return; + } + /* see if this entry already exists */ + node = avl_find(tree, newnode, &where); + if (node == NULL) { + avl_insert(tree, newnode, where); + } else { + /* It's a dup, don't add it */ + free(newnode->label); + free(newnode); + } +} + +CK_RV +kms_reload_labels(kms_session_t *sp) +{ + CK_RV rv = CKR_OK; + char *cfgbuf = NULL, *ptr, buffer[BUFSIZ]; + size_t buflen, remain; + struct stat statp; + char *ksdir; + char labelfile[BUFSIZ]; + + ksdir = kms_get_keystore_path(); + if (ksdir == NULL) + return (CKR_GENERAL_ERROR); + + (void) snprintf(labelfile, sizeof (labelfile), + "%s/%s", ksdir, KMSTOKEN_LABELLIST_FILENAME); + + bzero(&statp, sizeof (statp)); + if (stat(labelfile, &statp) == -1) { + if (errno == ENOENT) { + FILE *fp; + /* Create it */ + fp = fopen(labelfile, "w"); + if (fp == NULL) + return (CKR_GENERAL_ERROR); + (void) fclose(fp); + } + } + + if (statp.st_size == 0) { + return (CKR_OK); + } + + cfgbuf = calloc(1, statp.st_size); + if (cfgbuf == NULL) + return (CKR_HOST_MEMORY); + + buflen = kms_slurp_file(labelfile, cfgbuf, statp.st_size); + if (buflen != statp.st_size) { + free(cfgbuf); + return (CKR_FUNCTION_FAILED); + } + + if (statp.st_mtime == last_objlist_mtime) { + /* No change */ + goto end; + } + + /* If we got here, we need to refresh the entire list */ + kms_clear_label_list(&sp->objlabel_tree); + + /* + * Read each line and add it as a label node. + */ + remain = buflen; + ptr = cfgbuf; + while (remain > 0) { + ptr = get_non_comment_line(ptr, remain, + buffer, sizeof (buffer)); + if (ptr == NULL) { + goto end; + } + add_label_node(&sp->objlabel_tree, buffer); + remain = buflen - (ptr - cfgbuf); + } +end: + if (cfgbuf) + free(cfgbuf); + + return (rv); +} + +static CK_RV +kms_get_object_label(kms_object_t *obj, char *label, int len) +{ + CK_RV rv = CKR_OK; + CK_ATTRIBUTE stLabel; + + bzero(label, len); + + stLabel.type = CKA_LABEL; + stLabel.pValue = label; + stLabel.ulValueLen = len; + + /* + * The caller MUST provide a CKA_LABEL when deleting. + */ + rv = kms_get_attribute(obj, &stLabel); + + return (rv); +} + +/* + * Retrieve a data unit associated with the label. + */ +static CK_RV +kms_get_data_unit(kms_session_t *session, char *label, + KMSAgent_DataUnit *pDataUnit) +{ + KMS_AGENT_STATUS status; + const utf8cstr pDescription = KMS_DATAUNIT_DESCRIPTION; + uchar_t externalUniqueId[SHA256_DIGEST_LENGTH]; + + /* Find the data unit that holds the key */ + kms_hash_string(label, externalUniqueId); + + status = KMSAgent_RetrieveDataUnitByExternalUniqueID( + &session->kmsProfile, + (const unsigned char *)externalUniqueId, + sizeof (externalUniqueId), + label, + pDescription, + pDataUnit); + + if (status != KMS_AGENT_STATUS_OK) { + return (GetPKCS11StatusFromAgentStatus(status)); + } + + return (CKR_OK); +} + +static CK_RV +kms_decode_description(char *description, kms_object_t *pKey) +{ + CK_RV rv = CKR_OK; + char *ptr; + uint32_t keylen; + u_longlong_t boolattrs; + + /* If it doesn't start with the expected prefix, return */ + if (strncmp(description, KMS_ATTR_DESC_PFX, + strlen(KMS_ATTR_DESC_PFX))) + return (rv); + + ptr = description + strlen(KMS_ATTR_DESC_PFX); + + /* + * Decode as follows: + * CK_OBJECT_CLASS (2 bytes) + * CK_KEY_TYPE (2 bytes) + * CKA_VALUE_LEN (4 bytes) + * CK_CERTIFICATE_TYPE (2 bytes - not used) + * CK_MECHANISM_TYPE (4 bytes) + * boolean attributes (3 bytes) + * extra attributes (1 byte) + * non-boolean attributes + */ + if (sscanf(ptr, + "%02lx%02lx%02x00%04lx%06llx00", + &pKey->class, + &pKey->key_type, + &keylen, + &pKey->mechanism, + &boolattrs) != 5) + /* We didn't get the full set of attributes */ + rv = CKR_ATTRIBUTE_TYPE_INVALID; + pKey->bool_attr_mask = boolattrs; + + return (rv); +} + +/* + * Create a new PKCS#11 object record for the KMSAgent_Key. + */ +static CK_RV +kms_new_key_object( + char *label, + KMSAgent_DataUnit *dataUnit, + KMSAgent_Key *pKey, + kms_object_t **pObj) +{ + CK_RV rv = CKR_OK; + CK_BBOOL bTrue = B_TRUE; + CK_KEY_TYPE keytype = CKK_AES; + CK_OBJECT_CLASS class = CKO_SECRET_KEY; + CK_ULONG keylen; + kms_object_t *newObj; + + CK_ATTRIBUTE template[] = { + {CKA_TOKEN, NULL, sizeof (bTrue)}, + {CKA_LABEL, NULL, 0}, + {CKA_KEY_TYPE, NULL, sizeof (keytype)}, + {CKA_CLASS, NULL, sizeof (class)}, + {CKA_VALUE, NULL, NULL}, + {CKA_VALUE_LEN, NULL, NULL}, + {CKA_PRIVATE, NULL, sizeof (bTrue)}, + }; + + keylen = (CK_ULONG)pKey->m_iKeyLength; + + template[0].pValue = &bTrue; + template[1].pValue = label; + template[1].ulValueLen = strlen(label); + template[2].pValue = &keytype; + template[3].pValue = &class; + template[4].pValue = pKey->m_acKey; + template[4].ulValueLen = pKey->m_iKeyLength; + template[5].pValue = &keylen; + template[5].ulValueLen = sizeof (keylen); + template[6].pValue = &bTrue; + + newObj = kms_new_object(); + if (newObj == NULL) + return (CKR_HOST_MEMORY); + + /* + * Decode the DataUnit description field to find various + * object attributes. + */ + rv = kms_decode_description(dataUnit->m_acDescription, newObj); + if (rv) { + free(newObj); + return (rv); + } + /* + * Set the template keytype and class according to the + * data parsed from the description. + */ + if (newObj->key_type) + keytype = newObj->key_type; + if (newObj->class) + class = newObj->class; + + rv = kms_build_object(template, 7, newObj); + if (rv) { + free(newObj); + return (rv); + } + + newObj->bool_attr_mask |= TOKEN_BOOL_ON; + + *pObj = newObj; + return (rv); +} + +static CK_RV +kms_get_data_unit_keys(kms_session_t *sp, KMSAgent_DataUnit *dataUnit, + KMSAgent_ArrayOfKeys **keylist, int *numkeys) +{ + CK_RV rv = CKR_OK; + KMSAgent_ArrayOfKeys *kmskeys = NULL; + KMS_AGENT_STATUS status; + int keysLeft = 0; + + status = KMSAgent_RetrieveDataUnitKeys( + &sp->kmsProfile, dataUnit, + KMS_MAX_PAGE_SIZE, 0, + (int * const)&keysLeft, + NULL, /* KeyID */ + &kmskeys); + + if (status != KMS_AGENT_STATUS_OK) { + return (GetPKCS11StatusFromAgentStatus(status)); + } + + if (keylist != NULL && kmskeys != NULL) + *keylist = kmskeys; + + if (numkeys != NULL && kmskeys != NULL) + *numkeys = kmskeys->m_iSize; + + if (keylist == NULL && kmskeys != NULL) + KMSAgent_FreeArrayOfKeys(kmskeys); + + return (rv); +} + + +/* + * Retrieve a key from KMS. We can't use "RetrieveKey" because + * we don't know the key id. Instead get all keys associated + * with our data unit (there should be only 1. + */ +CK_RV +KMS_RetrieveKeyObj(kms_session_t *sp, char *label, kms_object_t **pobj) +{ + CK_RV rv = CKR_OK; + KMSAgent_DataUnit dataUnit; + KMSAgent_ArrayOfKeys *kmsKeys = NULL; + KMSAgent_Key *pKey; + + rv = kms_get_data_unit(sp, label, &dataUnit); + if (rv != CKR_OK) + return (rv); + + rv = kms_get_data_unit_keys(sp, &dataUnit, &kmsKeys, NULL); + + if (rv != CKR_OK || kmsKeys == NULL || kmsKeys->m_iSize == 0) + return (CKR_GENERAL_ERROR); + + pKey = &kmsKeys->m_pKeys[0]; + + rv = kms_new_key_object(label, &dataUnit, pKey, pobj); + + KMSAgent_FreeArrayOfKeys(kmsKeys); + return (rv); +} + +CK_RV +KMS_RefreshObjectList(kms_session_t *sp, kms_slot_t *pslot) +{ + kms_object_t *pObj; + char label[BUFSIZ]; + CK_RV rv; + objlabel_t *node; + + rv = kms_reload_labels(sp); + if (rv != CKR_OK) + return (rv); + + /* + * If an object is not in the list, reload it from KMS. + */ + node = avl_first(&sp->objlabel_tree); + while (node != NULL) { + boolean_t found = FALSE; + /* Search object list for matching object */ + pObj = pslot->sl_tobj_list; + while (pObj != NULL && !found) { + (void) pthread_mutex_lock(&pObj->object_mutex); + if ((rv = kms_get_object_label(pObj, label, + sizeof (label))) != CKR_OK) { + (void) pthread_mutex_unlock( + &pObj->object_mutex); + return (rv); + } + (void) pthread_mutex_unlock(&pObj->object_mutex); + found = (strcmp(label, node->label) == 0); + pObj = pObj->next; + } + if (!found) { + /* + * Fetch KMS key and prepend it to the + * token object list for the slot. + */ + rv = KMS_RetrieveKeyObj(sp, node->label, &pObj); + if (rv == CKR_OK) { + if (pslot->sl_tobj_list == NULL) { + pslot->sl_tobj_list = pObj; + pObj->prev = NULL; + pObj->next = NULL; + } else { + pObj->next = pslot->sl_tobj_list; + pObj->prev = NULL; + pslot->sl_tobj_list = pObj; + } + } + } + node = AVL_NEXT(&sp->objlabel_tree, node); + } + return (rv); +} + +CK_RV +KMS_Initialize(void) +{ + char *ksdir; + struct stat fn_stat; + KMS_AGENT_STATUS kmsrv; + + ksdir = kms_get_keystore_path(); + if (ksdir == NULL) + return (CKR_GENERAL_ERROR); + + /* + * If the keystore directory doesn't exist, create it. + */ + if ((stat(ksdir, &fn_stat) != 0) && (errno == ENOENT)) { + if (mkdir(ksdir, S_IRUSR|S_IWUSR|S_IXUSR) < 0) { + if (errno != EEXIST) + return (CKR_GENERAL_ERROR); + } + } + + if ((kmsrv = KMSAgent_InitializeLibrary(ksdir, FALSE)) != + KMS_AGENT_STATUS_OK) { + return (GetPKCS11StatusFromAgentStatus(kmsrv)); + } + + return (CKR_OK); +} + +CK_RV +KMS_Finalize() +{ + last_objlist_mtime = 0; + + return (KMSAgent_FinalizeLibrary() == KMS_AGENT_STATUS_OK) ? + CKR_OK : CKR_FUNCTION_FAILED; +} + +CK_RV +KMS_ChangeLocalPWD(kms_session_t *session, + const char *pOldPassword, + const char *pNewPassword) +{ + KMS_AGENT_STATUS status; + + status = KMSAgent_ChangeLocalPWD( + &session->kmsProfile, + (char * const)pOldPassword, + (char * const)pNewPassword); + + return (GetPKCS11StatusFromAgentStatus(status)); +} + +CK_RV +KMS_GetConfigInfo(kms_cfg_info_t *cfginfo) +{ + CK_RV rv = CKR_OK; + char cfgfile_path[BUFSIZ]; + char *ksdir = kms_get_keystore_path(); + + if (ksdir == NULL) + return (CKR_GENERAL_ERROR); + + (void) snprintf(cfgfile_path, sizeof (cfgfile_path), + "%s/%s", ksdir, KMSTOKEN_CONFIG_FILENAME); + + rv = kms_read_config_data(cfgfile_path, cfginfo); + + return (rv); +} + +CK_RV +KMS_LoadProfile(KMSClientProfile *profile, + kms_cfg_info_t *kmscfg, + const char *pPassword, + size_t iPasswordLength) +{ + KMS_AGENT_STATUS status; + CK_RV rv; + char *sPassword; + char cfgfile_path[BUFSIZ]; + char *ksdir; + + sPassword = calloc(1, iPasswordLength + 1); + if (sPassword == NULL) + return (CKR_FUNCTION_FAILED); + + (void) memcpy(sPassword, pPassword, iPasswordLength); + + ksdir = kms_get_keystore_path(); + if (ksdir == NULL) + return (CKR_GENERAL_ERROR); + + (void) snprintf(cfgfile_path, sizeof (cfgfile_path), + "%s/%s", ksdir, KMSTOKEN_CONFIG_FILENAME); + + if ((rv = kms_read_config_data(cfgfile_path, kmscfg))) { + free(sPassword); + return (rv); + } + + /* First, try to load existing profile */ + status = KMSAgent_LoadProfile( + profile, + kmscfg->name, + kmscfg->agentId, + sPassword, + kmscfg->agentAddr, + kmscfg->transTimeout, + kmscfg->failoverLimit, + kmscfg->discoveryFreq, + kmscfg->securityMode); + + free(sPassword); + return (GetPKCS11StatusFromAgentStatus(status)); +} + +static CK_RV +GetPKCS11StatusFromAgentStatus(KMS_AGENT_STATUS status) +{ + switch (status) { + case KMS_AGENT_STATUS_OK: + return (CKR_OK); + + case KMS_AGENT_STATUS_GENERIC_ERROR: + return (CKR_GENERAL_ERROR); + + case KMS_AGENT_STATUS_NO_MEMORY: + return (CKR_HOST_MEMORY); + + case KMS_AGENT_STATUS_INVALID_PARAMETER: + return (CKR_ARGUMENTS_BAD); + + case KMS_AGENT_STATUS_PROFILE_NOT_LOADED: + return (CKR_CRYPTOKI_NOT_INITIALIZED); + + case KMS_AGENT_STATUS_KMS_UNAVAILABLE: + case KMS_AGENT_STATUS_KMS_NO_READY_KEYS: + return (CKR_DEVICE_MEMORY); + + case KMS_AGENT_STATUS_NO_FIPS_KMAS_AVAILABLE: + return (CKR_GENERAL_ERROR); + + case KMS_AGENT_STATUS_PROFILE_ALREADY_LOADED: + return (CKR_USER_ANOTHER_ALREADY_LOGGED_IN); + + case KMS_AGENT_STATUS_FIPS_KAT_AES_KEYWRAP_ERROR: + case KMS_AGENT_STATUS_FIPS_KAT_AES_ECB_ERROR: + case KMS_AGENT_STATUS_FIPS_KAT_HMAC_SHA1_ERROR: + return (CKR_DEVICE_ERROR); + + case KMS_AGENT_STATUS_ACCESS_DENIED: + case KMS_AGENT_LOCAL_AUTH_FAILURE: + return (CKR_PIN_INCORRECT); + + case KMS_AGENT_STATUS_SERVER_BUSY: + case KMS_AGENT_STATUS_EXTERNAL_UNIQUE_ID_EXISTS: + case KMS_AGENT_STATUS_DATA_UNIT_ID_NOT_FOUND_EXTERNAL_ID_EXISTS: + case KMS_AGENT_STATUS_KEY_DOES_NOT_EXIST: + case KMS_AGENT_STATUS_KEY_DESTROYED: + case KMS_AGENT_AES_KEY_UNWRAP_ERROR: + case KMS_AGENT_AES_KEY_WRAP_SETUP_ERROR: + case KMS_AGENT_STATUS_KEY_CALLOUT_FAILURE: + default: + return (CKR_GENERAL_ERROR); + } +} + +void +KMS_UnloadProfile(KMSClientProfile *kmsProfile) +{ + (void) KMSAgent_UnloadProfile(kmsProfile); +} + +/* + * kms_update_label_file + * + * KMS doesn't provide an API to allow one to query for available + * data units (which map 1-1 to keys). To allow for PKCS11 to + * query for a list of available objects, we keep a local list + * and update it when an object is added or deleted. + */ +static CK_RV +kms_update_label_file(kms_session_t *sp) +{ + CK_RV rv = CKR_OK; + objlabel_t *node; + char *ksdir, *tmpfile, labelfile[BUFSIZ]; + FILE *fp; + int fd; + struct stat statp; + + ksdir = kms_get_keystore_path(); + if (ksdir == NULL) + return (CKR_GENERAL_ERROR); + + (void) snprintf(labelfile, sizeof (labelfile), + "%s/%s", ksdir, KMSTOKEN_LABELLIST_FILENAME); + + tmpfile = tempnam(ksdir, "kmspk11"); + if (tmpfile == NULL) + return (CKR_HOST_MEMORY); + + fp = fopen(tmpfile, "w"); + if (fp == NULL) { + free(tmpfile); + return (CKR_GENERAL_ERROR); + } + + /* Lock it even though its a temporary file */ + fd = fileno(fp); + if ((rv = flock_fd(fd, F_WRLCK, &objlist_mutex))) { + (void) fclose(fp); + free(tmpfile); + return (rv); + } + + node = avl_first(&sp->objlabel_tree); + while (node != NULL) { + if (node->label != NULL) + (void) fprintf(fp, "%s\n", node->label); + node = AVL_NEXT(&sp->objlabel_tree, node); + } + + /* Update the last mtime */ + if (fstat(fd, &statp) == 0) { + last_objlist_mtime = statp.st_mtime; + } + + (void) flock_fd(fd, F_UNLCK, &objlist_mutex); + (void) fclose(fp); + + (void) unlink(labelfile); + if (rename(tmpfile, labelfile)) + rv = CKR_GENERAL_ERROR; + + free(tmpfile); + return (rv); +} + +/* + * Destroy a key in the KMS by disassociating an entire data unit. + * The KMSAgent API does not have an interface for destroying an + * individual key. + */ +CK_RV +KMS_DestroyKey(kms_session_t *session, kms_object_t *i_oKey) +{ + CK_RV rv; + KMSAgent_DataUnit oDataUnit; + KMS_AGENT_STATUS status; + char label[BUFSIZ]; + objlabel_t labelnode, *tnode; + avl_index_t where = 0; + + /* + * The caller MUST provide a CKA_LABEL when deleting. + */ + (void) pthread_mutex_lock(&i_oKey->object_mutex); + if ((rv = kms_get_object_label(i_oKey, label, sizeof (label)))) { + (void) pthread_mutex_unlock(&i_oKey->object_mutex); + return (rv); + } + + rv = kms_get_data_unit(session, label, &oDataUnit); + if (rv != CKR_OK) + return (rv); + + status = KMSAgent_DisassociateDataUnitKeys( + &session->kmsProfile, &oDataUnit); + + /* + * Remove the label from the label list and update + * the file that tracks active keys. + */ + bzero(&labelnode, sizeof (labelnode)); + labelnode.label = label; + + if ((tnode = avl_find(&session->objlabel_tree, + &labelnode, &where)) != NULL) + avl_remove(&session->objlabel_tree, tnode); + + /* rewrite the list of labels to disk */ + rv = kms_update_label_file(session); + if (rv) + /* Ignore error here */ + rv = CKR_OK; + + (void) pthread_mutex_unlock(&i_oKey->object_mutex); + + return (GetPKCS11StatusFromAgentStatus(status)); +} + +void +kms_encode_attributes(kms_object_t *pKey, char *attrstr, int len) +{ + char *ptr; + + bzero(attrstr, len); + + (void) strlcpy(attrstr, KMS_ATTR_DESC_PFX, len); + ptr = attrstr + strlen(attrstr); + + /* + * Encode as follows: + * CK_OBJECT_CLASS (2 bytes) + * CK_KEY_TYPE (2 bytes) + * CKA_VALUE_LEN (4 bytes) + * CK_CERTIFICATE_TYPE (2 bytes - not used) + * CK_MECHANISM_TYPE (4 bytes) + * boolean attributes (3 bytes) + * extra attributes (1 byte) + * non-boolean attributes + */ + (void) snprintf(ptr, len - strlen(attrstr), + "%02x%02x%02x00%04x%06x00", + pKey->class, + pKey->key_type, + 32, + pKey->mechanism, + (pKey->bool_attr_mask & 0x00FFFFFF)); +} + +CK_RV +KMS_GenerateKey(kms_session_t *session, kms_object_t *i_oKey) +{ + CK_RV rv; + CK_ATTRIBUTE stLabel; + KMSAgent_DataUnit oDataUnit; + KMSAgent_Key oKey; + KMS_AGENT_STATUS status; + char label[128]; + uchar_t externalUniqueId[SHA256_DIGEST_LENGTH]; + char pDescription[KMS_MAX_DESCRIPTION + 1]; + + (void) pthread_mutex_lock(&i_oKey->object_mutex); + + stLabel.type = CKA_LABEL; + stLabel.pValue = label; + stLabel.ulValueLen = sizeof (label); + + /* + * The caller MUST provide a CKA_LABEL for storing in the KMS. + */ + if ((rv = kms_get_attribute(i_oKey, &stLabel)) != CKR_OK) { + (void) pthread_mutex_unlock(&i_oKey->object_mutex); + return (rv); + } + + label[stLabel.ulValueLen] = '\0'; + + kms_hash_string(label, externalUniqueId); + + /* Encode attributes in Description */ + kms_encode_attributes(i_oKey, pDescription, + sizeof (pDescription)); + + status = KMSAgent_CreateDataUnit( + &session->kmsProfile, + (const unsigned char *)externalUniqueId, + sizeof (externalUniqueId), + label, /* externalTag */ + pDescription, + &oDataUnit); + + /* + * If the DataUnit exists, check to see if it has any keys. + * If it has no keys, then it is OK to continue. + */ + if (status == KMS_AGENT_STATUS_EXTERNAL_UNIQUE_ID_EXISTS) { + int numkeys = 0; + + rv = kms_get_data_unit(session, label, &oDataUnit); + if (rv != CKR_OK) + return (rv); + + rv = kms_get_data_unit_keys(session, + &oDataUnit, NULL, &numkeys); + + if (rv != CKR_OK || numkeys > 0) + /* + * This would be better if there were PKCS#11 + * error codes for duplicate objects or + * something like that. + */ + return (CKR_ARGUMENTS_BAD); + + /* If no keys associated with data unit, continue */ + status = KMS_AGENT_STATUS_OK; + } + + if (status != KMS_AGENT_STATUS_OK) { + (void) pthread_mutex_unlock(&i_oKey->object_mutex); + return (GetPKCS11StatusFromAgentStatus(status)); + } + + status = KMSAgent_CreateKey(&session->kmsProfile, + &oDataUnit, "", &oKey); + + if (status != KMS_AGENT_STATUS_OK) { + /* + * Clean up the old data unit. + */ + (void) pthread_mutex_unlock(&i_oKey->object_mutex); + return (GetPKCS11StatusFromAgentStatus(status)); + } + + /* + * KMS Agent only creates AES-256 keys, so ignore what the user + * requested at this point. + */ + OBJ_SEC_VALUE(i_oKey) = malloc(oKey.m_iKeyLength); + if (OBJ_SEC_VALUE(i_oKey) == NULL) { + (void) pthread_mutex_unlock(&i_oKey->object_mutex); + return (CKR_HOST_MEMORY); + } + (void) memcpy(OBJ_SEC_VALUE(i_oKey), oKey.m_acKey, + oKey.m_iKeyLength); + OBJ_SEC_VALUE_LEN(i_oKey) = oKey.m_iKeyLength; + + /* + * Add the label to the local list of available objects + */ + add_label_node(&session->objlabel_tree, label); + + rv = kms_update_label_file(session); + + (void) pthread_mutex_unlock(&i_oKey->object_mutex); + + return (GetPKCS11StatusFromAgentStatus(status)); +} diff --git a/usr/src/lib/pkcs11/pkcs11_kms/common/kmsKeystoreUtil.h b/usr/src/lib/pkcs11/pkcs11_kms/common/kmsKeystoreUtil.h new file mode 100644 index 0000000000..02e627f9fa --- /dev/null +++ b/usr/src/lib/pkcs11/pkcs11_kms/common/kmsKeystoreUtil.h @@ -0,0 +1,57 @@ +/* + * 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) 2010, Oracle and/or its affiliates. All rights reserved. + */ + +#ifndef _KEYSTOREUTIL_H +#define _KEYSTOREUTIL_H + +#ifdef __cplusplus +extern "C" +{ +#endif + +#include "kmsSession.h" +#include "kmsObject.h" + +CK_RV KMS_Initialize(void); +CK_RV KMS_Finalize(); +CK_RV KMS_LoadProfile(KMSClientProfile *, kms_cfg_info_t *, + const char *, size_t); +CK_RV KMS_GenerateKey(kms_session_t *, kms_object_t *); +CK_RV KMS_DestroyKey(kms_session_t *, kms_object_t *); +void KMS_UnloadProfile(KMSClientProfile *); +CK_RV KMS_RefreshObjectList(kms_session_t *, kms_slot_t *); +CK_RV KMS_ChangeLocalPWD(kms_session_t *, const char *, const char *); +CK_RV KMS_GetConfigInfo(kms_cfg_info_t *); + +CK_BBOOL kms_is_initialized(); +CK_BBOOL kms_is_pin_set(); +CK_RV kms_reload_labels(kms_session_t *); +void kms_clear_label_list(avl_tree_t *); + +#ifdef __cplusplus +} +#endif + +#endif /* _KEYSTOREUTIL_H */ diff --git a/usr/src/lib/pkcs11/pkcs11_kms/common/kmsObject.c b/usr/src/lib/pkcs11/pkcs11_kms/common/kmsObject.c new file mode 100644 index 0000000000..2ddd76fab9 --- /dev/null +++ b/usr/src/lib/pkcs11/pkcs11_kms/common/kmsObject.c @@ -0,0 +1,664 @@ +/* + * 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 <pthread.h> +#include <stdlib.h> +#include <errno.h> +#include <errno.h> +#include <string.h> +#include <security/cryptoki.h> +#include "kmsGlobal.h" +#include "kmsObject.h" +#include "kmsSession.h" + +CK_RV +C_CreateObject(CK_SESSION_HANDLE hSession, + CK_ATTRIBUTE_PTR pTemplate, + CK_ULONG ulCount, + CK_OBJECT_HANDLE_PTR phObject) +{ + + CK_RV rv; + kms_session_t *session_p; + boolean_t ses_lock_held = B_FALSE; + + if (!kms_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 = kms_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; + kms_session_t *session_p; + boolean_t ses_lock_held = B_FALSE; + kms_object_t *old_object; + kms_object_t *new_object = NULL; + int i; + + if (!kms_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 = kms_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 = kms_set_attribute(new_object, &pTemplate[i], + B_TRUE); + + if (rv != CKR_OK) { + kms_cleanup_object(new_object); + OBJ_REFRELE(old_object); + REFRELE(session_p, ses_lock_held); + return (rv); + } + } + + /* Insert the new object into this session's object list. */ + kms_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) kms_free_object(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; + kms_object_t *object_p; + kms_session_t *session_p = (kms_session_t *)(hSession); + kms_slot_t *pslot; + boolean_t ses_lock_held = B_FALSE; + CK_SESSION_HANDLE creating_session; + + if (!kms_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 != KMSTOKEN_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 = get_slotinfo(); + rv = kms_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 = kms_delete_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; + kms_object_t *object_p; + kms_session_t *session_p; + boolean_t ses_lock_held = B_FALSE; + int i; + + if (!kms_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); + + /* + * 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 = kms_get_attribute(object_p, &pTemplate[i]); + if (rv != CKR_OK) + rv1 = rv; + } + (void) pthread_mutex_unlock(&object_p->object_mutex); + +clean_exit: + /* + * Decrement the session reference count. + * We do not hold the session lock. + */ + OBJ_REFRELE(object_p); + REFRELE(session_p, ses_lock_held); + rv = rv1; + 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; + kms_object_t *object_p; + kms_object_t *new_object = NULL; + kms_session_t *session_p; + boolean_t ses_lock_held = B_FALSE; + int i; + + if (!kms_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; + } + } + + /* + * 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 = kms_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 = kms_set_attribute(new_object, &pTemplate[i], B_FALSE); + + if (rv != CKR_OK) { + kms_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); + kms_merge_object(object_p, new_object); + (void) pthread_mutex_unlock(&object_p->object_mutex); + +clean_exit: + if (new_object != NULL) + (void) kms_free_object(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; + kms_object_t *object_p; + kms_session_t *session_p; + boolean_t ses_lock_held = B_FALSE; + + if (!kms_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); + + rv = kms_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; + kms_session_t *session_p; + boolean_t ses_lock_held = B_FALSE; + + if (!kms_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; + } + (void) pthread_mutex_unlock(&session_p->session_mutex); + + /* + * If the KMS provider supports object creation, we call the + * CRYPTO_OBJECT_FIND_INIT to initialize object finding. + * Otherwise, all the objects are created in the library and we + * do the find objects solely in the library. + */ + rv = kms_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; + kms_slot_t *pslot = NULL; + kms_session_t *session_p; + boolean_t ses_lock_held = B_FALSE; + + if (!kms_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 = get_slotinfo(); + (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 KMS provider supports object + * creation, we need to find objects. + * Otherwise, all the objects are created in the library and we do + * the find objects solely in the library. + */ + + rv = kms_find_objects(session_p, phObject, + ulMaxObjectCount, pulObjectCount); + +clean_exit: + /* decrement the session count, and release the session lock */ + REFRELE(session_p, ses_lock_held); + + /* release the slot lock */ + if (pslot) + (void) pthread_mutex_unlock(&pslot->sl_mutex); + + return (rv); +} + +CK_RV +C_FindObjectsFinal(CK_SESSION_HANDLE sh) +{ + kms_session_t *session_p; + CK_RV rv; + boolean_t ses_lock_held = B_FALSE; + + if (!kms_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 KMS provider supports object + * creation, we need to finalize the search on the KMS side. + */ + kms_find_objects_final(session_p); + + /* decrement the session count, and release the lock */ + REFRELE(session_p, ses_lock_held); + return (rv); +} diff --git a/usr/src/lib/pkcs11/pkcs11_kms/common/kmsObject.h b/usr/src/lib/pkcs11/pkcs11_kms/common/kmsObject.h new file mode 100644 index 0000000000..afd5803854 --- /dev/null +++ b/usr/src/lib/pkcs11/pkcs11_kms/common/kmsObject.h @@ -0,0 +1,308 @@ +/* + * 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. + */ + +#ifndef _KMSOBJECT_H +#define _KMSOBJECT_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include <security/pkcs11t.h> +#include "kmsSession.h" +#include "kmsSlot.h" + +#define KMSTOKEN_OBJECT_MAGIC 0xECF0B004 + +#define KMS_CREATE_OBJ 1 +#define KMS_GEN_KEY 2 + +/* + * Secret key Struct + */ +typedef struct secret_key_obj { + CK_BYTE *sk_value; + CK_ULONG sk_value_len; + void *key_sched; + size_t keysched_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; + +/* + * This is the main structure of the Objects. + */ +typedef struct object { + boolean_t is_lib_obj; /* default is TRUE */ + + /* 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; + } 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 */ +} kms_object_t; + +typedef struct find_context { + kms_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 { + kms_object_t *first; /* points to first obj in the list */ + kms_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) +#define OBJ_KEY_SCHED(o) \ + ((o)->object_class_u.secret_key->key_sched) +#define OBJ_KEY_SCHED_LEN(o) \ + ((o)->object_class_u.secret_key->keysched_len) + +/* + * 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) + +/* + * 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 = (kms_object_t *)(hObject); \ + if ((object_p == NULL) || \ + (object_p->magic_marker != KMSTOKEN_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 kms_cleanup_object(kms_object_t *objp); + +CK_RV kms_add_object(CK_ATTRIBUTE_PTR pTemplate, CK_ULONG ulCount, + CK_ULONG *objecthandle_p, kms_session_t *sp); + +CK_RV kms_delete_object(kms_session_t *, kms_object_t *, + boolean_t, boolean_t); + +void kms_cleanup_extra_attr(kms_object_t *object_p); + +CK_RV kms_copy_extra_attr(CK_ATTRIBUTE_INFO_PTR old_attrp, + kms_object_t *object_p); + +void kms_cleanup_object_bigint_attrs(kms_object_t *object_p); + +CK_RV kms_build_object(CK_ATTRIBUTE_PTR, CK_ULONG, kms_object_t *); + +CK_RV kms_copy_object(kms_object_t *old_object, + kms_object_t **new_object, boolean_t copy_everything, + kms_session_t *sp); + +void kms_merge_object(kms_object_t *old_object, + kms_object_t *new_object); + +CK_RV kms_get_attribute(kms_object_t *object_p, + CK_ATTRIBUTE_PTR template); + +CK_RV kms_set_attribute(kms_object_t *, CK_ATTRIBUTE_PTR, boolean_t); + +void kms_add_object_to_session(kms_object_t *objp, kms_session_t *sp); + +CK_RV kms_copy_secret_key_attr(secret_key_obj_t *old_secret_key_obj_p, + secret_key_obj_t **new_secret_key_obj_p); + +CK_RV kms_validate_attr(CK_ATTRIBUTE_PTR template, CK_ULONG ulAttrNum, + CK_OBJECT_CLASS *class); + +CK_RV kms_find_objects_init(kms_session_t *sp, + CK_ATTRIBUTE_PTR pTemplate, CK_ULONG ulCount); + +void kms_find_objects_final(kms_session_t *sp); + +CK_RV kms_find_objects(kms_session_t *sp, + CK_OBJECT_HANDLE *obj_found, CK_ULONG max_obj_requested, + CK_ULONG *found_obj_count); + +void kms_process_find_attr(CK_OBJECT_CLASS *pclasses, + CK_ULONG *num_result_pclasses, CK_ATTRIBUTE_PTR pTemplate, + CK_ULONG ulCount); + +boolean_t kms_find_match_attrs(kms_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, kms_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 kms_add_token_object_to_slot(kms_object_t *objp, + kms_slot_t *pslot); + +void kms_remove_token_object_from_slot(kms_slot_t *pslot, + kms_object_t *objp); + +CK_RV kms_delete_token_object(kms_slot_t *pslot, kms_session_t *sp, + kms_object_t *obj, boolean_t lock_held, boolean_t wrapper_only); + +void kms_cleanup_pri_objects_in_slot(kms_slot_t *pslot, + kms_session_t *sp); + +CK_RV kms_get_object_size(kms_object_t *objp, CK_ULONG_PTR pulSize); + +void kms_object_delay_free(kms_object_t *); + +kms_object_t *kms_new_object(); +void kms_free_object(kms_object_t *); + +#ifdef __cplusplus +} +#endif + +#endif /* _KMSOBJECT_H */ diff --git a/usr/src/lib/pkcs11/pkcs11_kms/common/kmsObjectUtil.c b/usr/src/lib/pkcs11/pkcs11_kms/common/kmsObjectUtil.c new file mode 100644 index 0000000000..64ddc11060 --- /dev/null +++ b/usr/src/lib/pkcs11/pkcs11_kms/common/kmsObjectUtil.c @@ -0,0 +1,930 @@ +/* + * 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 <stdio.h> +#include <stdlib.h> +#include <strings.h> +#include <errno.h> +#include <security/cryptoki.h> +#include <cryptoutil.h> + +#include "kmsGlobal.h" +#include "kmsObject.h" +#include "kmsSession.h" +#include "kmsSlot.h" +#include "kmsKeystoreUtil.h" + +kms_object_t * +kms_new_object() +{ + kms_object_t *obj; + + obj = calloc(1, sizeof (kms_object_t)); + if (obj == NULL) + return (NULL); + + (void) pthread_cond_init(&obj->obj_free_cond, NULL); + (void) pthread_mutex_init(&obj->object_mutex, NULL); + obj->magic_marker = KMSTOKEN_OBJECT_MAGIC; + + return (obj); +} + +/* + * 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 +kms_add_object_to_session(kms_object_t *objp, kms_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 kms_delete_object()), or there is no object lock + * yet (by kms_build_XXX_object() during creating an object). + */ +void +kms_cleanup_object(kms_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) { + bzero(OBJ_SEC_VALUE(objp), OBJ_SEC_VALUE_LEN(objp)); + free(OBJ_SEC_VALUE(objp)); + OBJ_SEC_VALUE(objp) = NULL; + OBJ_SEC_VALUE_LEN(objp) = 0; + } + if (OBJ_SEC(objp) != NULL) + free(OBJ_SEC(objp)); + + OBJ_SEC(objp) = NULL; + } + + /* + * Free the storage allocated to the extra attribute list. + */ + kms_cleanup_extra_attr(objp); +} + +void +kms_free_object(kms_object_t *obj) +{ + (void) pthread_cond_destroy(&obj->obj_free_cond); + (void) pthread_mutex_destroy(&obj->object_mutex); + + kms_cleanup_object(obj); + + free(obj); +} + +/* + * 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 +kms_copy_object(kms_object_t *old_object, kms_object_t **new_object, + boolean_t copy_everything, kms_session_t *sp) +{ + CK_RV rv = CKR_OK; + kms_object_t *new_objp = NULL; + CK_ATTRIBUTE_INFO_PTR attrp; + + /* Allocate new object. */ + new_objp = kms_new_object(); + 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 = kms_copy_extra_attr(attrp, new_objp); + if (rv != CKR_OK) { + kms_free_object(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; + + /* copy key related information */ + switch (new_objp->class) { + case CKO_SECRET_KEY: + rv = kms_copy_secret_key_attr(OBJ_SEC(old_object), + &(OBJ_SEC(new_objp))); + break; + default: + /* should never be this case */ + break; + } + if (rv != CKR_OK) { + kms_free_object(new_objp); + *new_object = NULL; + } + 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 +kms_merge_object(kms_object_t *old_object, kms_object_t *new_object) +{ + old_object->bool_attr_mask = new_object->bool_attr_mask; + kms_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 +kms_add_object(CK_ATTRIBUTE_PTR pTemplate, CK_ULONG ulCount, + CK_ULONG *objecthandle_p, kms_session_t *sp) +{ + CK_RV rv = CKR_OK; + kms_object_t *new_objp = NULL; + kms_slot_t *pslot; + CK_ATTRIBUTE pritmpl; + CK_BBOOL is_pri_obj, is_token_obj; + + new_objp = kms_new_object(); + if (new_objp == NULL) + return (CKR_HOST_MEMORY); + + rv = kms_build_object(pTemplate, ulCount, new_objp); + if (rv != CKR_OK) + goto fail_cleanup; + + /* Cannot create a token object with a READ-ONLY session */ + pritmpl.type = CKA_TOKEN; + pritmpl.pValue = &is_token_obj; + pritmpl.ulValueLen = sizeof (is_token_obj); + rv = kms_get_attribute(new_objp, &pritmpl); + if (rv != CKR_OK) + goto fail_cleanup; + + if (is_token_obj && sp->ses_RO) { + rv = CKR_SESSION_READ_ONLY; + goto fail_cleanup; + } + + /* + * If the KMS supports object creation, create the object + * in the KMS. Otherwise, create the object in the library. + */ + + /* Get the CKA_PRIVATE value of this object. */ + pritmpl.type = CKA_PRIVATE; + pritmpl.pValue = &is_pri_obj; + pritmpl.ulValueLen = sizeof (is_pri_obj); + + rv = kms_get_attribute(new_objp, &pritmpl); + 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; + + new_objp->session_handle = (CK_SESSION_HANDLE)sp; + + if (is_token_obj) { + /* Add the new object to the slot's token object list. */ + pslot = get_slotinfo(); + kms_add_token_object_to_slot(new_objp, pslot); + } else { + /* Add the new object to the session's object list. */ + kms_add_object_to_session(new_objp, sp); + } + + /* Type casting the address of an object struct to an object handle. */ + if (rv == CKR_OK) + *objecthandle_p = (CK_ULONG)new_objp; + +fail_cleanup: + if (rv != CKR_OK) { + kms_free_object(new_objp); + } + return (rv); +} + +/* + * Remove an object from the session's object list. + * + * The caller of this function holds the session lock. + */ +CK_RV +kms_remove_object_from_session(kms_object_t *objp, kms_session_t *sp) +{ + kms_object_t *tmp_objp; + boolean_t found = B_FALSE; + + /* + * Remove the object from the session's object list. + */ + if ((sp == NULL) || + (sp->magic_marker != KMSTOKEN_SESSION_MAGIC)) { + return (CKR_SESSION_HANDLE_INVALID); + } + + if ((sp->object_list == NULL) || (objp == NULL) || + (objp->magic_marker != KMSTOKEN_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); +} + +/* + * 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 +kms_object_delay_free(kms_object_t *objp) +{ + kms_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; + kms_free_object(obj_delay_freed.first); + obj_delay_freed.first = tmp; + } + (void) pthread_mutex_unlock(&obj_delay_freed.obj_to_be_free_mutex); +} + +static void +kms_delete_object_cleanup(kms_object_t *objp, boolean_t force) +{ + /* 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 != KMSTOKEN_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 (force) { + 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; + kms_cleanup_object(objp); + + objp->obj_delete_sync &= ~OBJECT_IS_DELETING; + (void) pthread_mutex_unlock(&objp->object_mutex); + + if (objp->bool_attr_mask & TOKEN_BOOL_ON) + free(objp); + else + kms_object_delay_free(objp); +} + +/* + * 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 kms_delete_all_objects_in_session() or + * kms_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 call to KMS. + * - This argument only applies to the object created in the provider level. + * - When called by kms_cleanup_pri_objects_in_slot(), wrapper_only is TRUE. + * - When called by C_DestroyObject(), wrapper_only is FALSE. + * - When called by kms_delete_all_objects_in_session(), the value of + * wrapper_only depends on its caller. + */ +CK_RV +kms_delete_object(kms_session_t *sp, kms_object_t *objp, + boolean_t ses_lock_held, boolean_t wrapper_only) +{ + CK_RV rv = CKR_OK; + + /* + * 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. */ + if ((rv = kms_remove_object_from_session(objp, sp))) { + if (!ses_lock_held) + (void) pthread_mutex_unlock(&sp->session_mutex); + return (rv); + } + + if (!wrapper_only) + (void) pthread_mutex_unlock(&sp->session_mutex); + + kms_delete_object_cleanup(objp, wrapper_only); + + return (rv); +} + +/* + * 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 +kms_delete_all_objects_in_session(kms_session_t *sp, + boolean_t wrapper_only) +{ + kms_object_t *objp = sp->object_list; + kms_object_t *objp1; + + /* Delete all the objects in the session. */ + while (objp) { + objp1 = objp->next; + (void) kms_delete_object(sp, objp, B_TRUE, + wrapper_only); + + objp = objp1; + } +} + +static CK_RV +add_to_search_result(kms_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 (kms_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(kms_session_t *sp, CK_ATTRIBUTE_PTR pTemplate, + CK_ULONG ulCount, find_context_t *fcontext) +{ + kms_session_t *session_p; + kms_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; + kms_slot_t *pslot = NULL; + boolean_t token_specified = B_FALSE; + boolean_t token_flag_val = B_FALSE; + int i; + + if (ulCount > 0) { + /* there are some search requirement */ + kms_process_find_attr(pclasses, &num_pclasses, + pTemplate, ulCount); + } + + /* + * look through template and see if it explicitly specifies + * whether we need to look for token objects or not + */ + for (i = 0; i < ulCount; i++) { + if (pTemplate[i].type == CKA_TOKEN) { + token_specified = B_TRUE; + token_flag_val = *((CK_BBOOL *)pTemplate[i].pValue); + break; + } + } + + pslot = get_slotinfo(); + + /* Acquire the slot lock */ + if (token_flag_val || !token_specified) { + (void) pthread_mutex_lock(&pslot->sl_mutex); + /* + * Make sure the object list is current. + */ + rv = KMS_RefreshObjectList(sp, pslot); + if (rv != CKR_OK) { + (void) pthread_mutex_unlock(&pslot->sl_mutex); + return (rv); + } + + obj = pslot->sl_tobj_list; + while (obj) { + (void) pthread_mutex_lock(&obj->object_mutex); + if (((token_specified) && (ulCount > 1)) || + ((!token_specified) && (ulCount > 0))) { + if (kms_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) { + goto cleanup; + } + obj = obj->next; + } + (void) pthread_mutex_unlock(&pslot->sl_mutex); + } + + if (token_flag_val) { + return (rv); + } + + /* + * 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 (kms_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 +kms_find_objects_init(kms_session_t *sp, CK_ATTRIBUTE_PTR pTemplate, + CK_ULONG ulCount) +{ + CK_RV rv = CKR_OK; + CK_OBJECT_CLASS class; /* for kms_validate_attr(). Value unused */ + find_context_t *fcontext; + + if (ulCount) { + rv = kms_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 +kms_find_objects_final(kms_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); +} + +CK_RV +kms_find_objects(kms_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; + kms_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 == KMSTOKEN_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; + return (CKR_OK); +} + +/* + * 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 +kms_add_token_object_to_slot(kms_object_t *objp, kms_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 kms_delete_token_object(). + * The caller of this function hold the slot lock. + */ +void +kms_remove_token_object_from_slot(kms_slot_t *pslot, + kms_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. */ + if (objp->prev) + objp->prev->next = objp->next; + objp->next->prev = objp->prev; + } else if (objp->prev) { + /* 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 +kms_delete_token_object(kms_slot_t *pslot, kms_session_t *sp, + kms_object_t *objp, boolean_t slot_lock_held, boolean_t wrapper_only) +{ + CK_RV rv = CKR_OK; + + if (!slot_lock_held) { + (void) pthread_mutex_lock(&pslot->sl_mutex); + } + if (!wrapper_only && objp->class == CKO_SECRET_KEY) { + /* Delete from KMS */ + rv = KMS_DestroyKey(sp, objp); + } + + /* Remove the object from the slot's token object list first. */ + kms_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); + } + + kms_delete_object_cleanup(objp, wrapper_only); + + return (rv); +} + +/* + * Clean up private object wrappers in this slot. The caller holds the slot + * lock. + */ +void +kms_cleanup_pri_objects_in_slot(kms_slot_t *pslot, + kms_session_t *cur_sp) +{ + kms_session_t *session_p; + kms_object_t *objp; + kms_object_t *objp1; + + /* + * Delete every private token object from + * the slot token object list. + */ + (void) pthread_mutex_lock(&pslot->sl_mutex); + 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) kms_delete_token_object(pslot, cur_sp, objp, + B_TRUE, B_TRUE); + } + objp = objp1; + } + + (void) pthread_mutex_unlock(&pslot->sl_mutex); + /* + * 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) kms_delete_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 +kms_get_object_size(kms_object_t *obj, CK_ULONG_PTR pulSize) +{ + CK_RV rv = CKR_OK; + CK_ULONG obj_size; + + obj_size = sizeof (kms_object_t); + + switch (obj->class) { + 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); +} diff --git a/usr/src/lib/pkcs11/pkcs11_kms/common/kmsRand.c b/usr/src/lib/pkcs11/pkcs11_kms/common/kmsRand.c new file mode 100644 index 0000000000..088c09c204 --- /dev/null +++ b/usr/src/lib/pkcs11/pkcs11_kms/common/kmsRand.c @@ -0,0 +1,48 @@ +/* + * 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 <security/cryptoki.h> +#include "kmsGlobal.h" + +/*ARGSUSED*/ +CK_RV +C_SeedRandom(CK_SESSION_HANDLE hSession, CK_BYTE_PTR pSeed, CK_ULONG ulSeedLen) +{ + if (!kms_initialized) + return (CKR_CRYPTOKI_NOT_INITIALIZED); + + return (CKR_FUNCTION_NOT_SUPPORTED); +} + +/*ARGSUSED*/ +CK_RV +C_GenerateRandom(CK_SESSION_HANDLE hSession, CK_BYTE_PTR pRandomData, + CK_ULONG ulRandomLen) +{ + if (!kms_initialized) + return (CKR_CRYPTOKI_NOT_INITIALIZED); + + return (CKR_FUNCTION_NOT_SUPPORTED); +} diff --git a/usr/src/lib/pkcs11/pkcs11_kms/common/kmsSession.c b/usr/src/lib/pkcs11/pkcs11_kms/common/kmsSession.c new file mode 100644 index 0000000000..bdde865b56 --- /dev/null +++ b/usr/src/lib/pkcs11/pkcs11_kms/common/kmsSession.c @@ -0,0 +1,355 @@ +/* + * 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 <pthread.h> +#include <errno.h> +#include <security/cryptoki.h> +#include "kmsGlobal.h" +#include "kmsSession.h" +#include "kmsSlot.h" +#include "kmsKeystoreUtil.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; + kms_slot_t *pslot; + + if (!kms_initialized) + return (CKR_CRYPTOKI_NOT_INITIALIZED); + + if (!(flags & CKF_SERIAL_SESSION)) + return (CKR_SESSION_PARALLEL_NOT_SUPPORTED); + + if (phSession == NULL) + return (CKR_ARGUMENTS_BAD); + + if (slotID != KMS_TOKEN_SLOTID) { + 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 kms_add_session(). + */ + pslot = get_slotinfo(); + (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 = kms_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; + + kms_session_t *session_p; + boolean_t ses_lock_held = B_FALSE; + + if (!kms_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 kms_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. + * + * kms_delete_session() will reset SESSION_IS_CLOSING + * flag after it is done. + */ + kms_delete_session(session_p, B_FALSE, B_FALSE); + return (rv); +} + +/*ARGSUSED*/ +CK_RV +C_CloseAllSessions(CK_SLOT_ID slotID) +{ + if (!kms_initialized) + return (CKR_CRYPTOKI_NOT_INITIALIZED); + + /* Delete all the sessions and release the allocated resources */ + kms_delete_all_sessions(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(kms_session_t *session_p) +{ + CK_STATE state; + kms_slot_t *pslot; + + pslot = get_slotinfo(); + (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) +{ + kms_session_t *session_p; + CK_RV rv; + boolean_t ses_lock_held = B_FALSE; + + if (!kms_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 (rv); +} + +/*ARGSUSED*/ +CK_RV +C_GetOperationState(CK_SESSION_HANDLE hSession, CK_BYTE_PTR pOperationState, + CK_ULONG_PTR pulOperationStateLen) +{ + if (!kms_initialized) + return (CKR_CRYPTOKI_NOT_INITIALIZED); + + return (CKR_FUNCTION_NOT_SUPPORTED); +} + +/*ARGSUSED*/ +CK_RV +C_SetOperationState(CK_SESSION_HANDLE hSession, CK_BYTE_PTR pOperationState, + CK_ULONG ulOperationStateLen, CK_OBJECT_HANDLE hEncryptionKey, + CK_OBJECT_HANDLE hAuthenticationKey) +{ + if (!kms_initialized) + return (CKR_CRYPTOKI_NOT_INITIALIZED); + + return (CKR_FUNCTION_NOT_SUPPORTED); +} + +CK_RV +C_Login(CK_SESSION_HANDLE hSession, CK_USER_TYPE userType, + CK_UTF8CHAR_PTR pPin, CK_ULONG ulPinLen) +{ + CK_RV rv = CKR_OK; + kms_session_t *session_p; + kms_slot_t *pslot; + boolean_t ses_lock_held = B_FALSE; + + if (!kms_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 = get_slotinfo(); + (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) { + kms_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; + } + } + + /* + * Login to KMS by attempting to load the profile using + * the given password. + */ + rv = KMS_LoadProfile( + &session_p->kmsProfile, + &session_p->configInfo, + (const char *)pPin, + (size_t)ulPinLen); + + 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; + kms_session_t *session_p; + kms_slot_t *pslot; + boolean_t ses_lock_held = B_FALSE; + + if (!kms_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 = get_slotinfo(); + (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; + } + + KMS_UnloadProfile(&session_p->kmsProfile); + + /* + * If this slot was logged in as USER previously, we need to clean up + * all private object wrappers in library for this slot. + */ + kms_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); +} diff --git a/usr/src/lib/pkcs11/pkcs11_kms/common/kmsSession.h b/usr/src/lib/pkcs11/pkcs11_kms/common/kmsSession.h new file mode 100644 index 0000000000..3f8db9d250 --- /dev/null +++ b/usr/src/lib/pkcs11/pkcs11_kms/common/kmsSession.h @@ -0,0 +1,188 @@ +/* + * 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) 2010, Oracle and/or its affiliates. All rights reserved. + */ + +#ifndef _KMSSESSION_H +#define _KMSSESSION_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include <stdio.h> +#include <pthread.h> +#include <sys/avl.h> +#include <security/pkcs11t.h> + +#define K_SOLARIS_PLATFORM +#include "KMSAgent.h" + +#define KMSTOKEN_SESSION_MAGIC 0xECF00004 + +#define CRYPTO_OPERATION_ACTIVE 0x01 +#define CRYPTO_OPERATION_UPDATE 0x02 + +typedef struct { + CK_MECHANISM mech; + void *context; + uint32_t flags; +} kms_active_op_t; + +typedef struct { + char *label; + avl_node_t nodep; +} objlabel_t; + +#define KMSOFFSETOF(s, m) ((size_t)(&(((s *)0)->m))) + +/* + * Data stored in the KMS profile config file. + */ +typedef struct { + char name[BUFSIZ]; + char agentId[BUFSIZ]; + char agentAddr[BUFSIZ]; + int transTimeout; + int failoverLimit; + int discoveryFreq; + int securityMode; +} kms_cfg_info_t; + +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 */ + 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 */ + + kms_active_op_t find_objects; + kms_active_op_t encrypt; + kms_active_op_t decrypt; + + kms_cfg_info_t configInfo; + + avl_tree_t objlabel_tree; + KMSClientProfile kmsProfile; +} kms_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 { + kms_session_t *first; /* points to the first session in the list */ + kms_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; +extern CK_ULONG kms_session_cnt; +extern CK_ULONG kms_session_rw_cnt; + +/* + * 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, kms_session_t **session_p); + +void +kms_delete_all_sessions(boolean_t wrapper_only); + +void +kms_delete_all_objects_in_session(kms_session_t *sp, + boolean_t wrapper_only); + +CK_RV +kms_add_session(CK_SLOT_ID slotID, CK_FLAGS flags, + CK_VOID_PTR pApplication, CK_NOTIFY notify, CK_ULONG *phSession); + +void +kms_delete_session(kms_session_t *sp, + boolean_t lock_held, boolean_t wrapper_only); + +void +kms_session_delay_free(kms_session_t *sp); + +void kms_acquire_all_slots_mutexes(); +void kms_release_all_slots_mutexes(); + +#ifdef __cplusplus +} +#endif + +#endif /* _KMSSESSION_H */ diff --git a/usr/src/lib/pkcs11/pkcs11_kms/common/kmsSessionUtil.c b/usr/src/lib/pkcs11/pkcs11_kms/common/kmsSessionUtil.c new file mode 100644 index 0000000000..f46cda9b7c --- /dev/null +++ b/usr/src/lib/pkcs11/pkcs11_kms/common/kmsSessionUtil.c @@ -0,0 +1,491 @@ +/* + * 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 <pthread.h> +#include <syslog.h> +#include <stdlib.h> +#include <string.h> +#include <errno.h> +#include <security/cryptoki.h> +#include "kmsGlobal.h" +#include "kmsSession.h" +#include "kmsSlot.h" +#include "kmsKeystoreUtil.h" + +static pthread_mutex_t delete_sessions_mutex = PTHREAD_MUTEX_INITIALIZER; +CK_ULONG kms_session_cnt = 0; +CK_ULONG kms_session_rw_cnt = 0; + +/* + * 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 +kms_delete_all_sessions(boolean_t wrapper_only) +{ + kms_session_t *session_p; + kms_slot_t *pslot; + + (void) pthread_mutex_lock(&delete_sessions_mutex); + + pslot = get_slotinfo(); + + /* + * Delete all the sessions in the slot's session list. + * The routine kms_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); + kms_delete_session(session_p, B_FALSE, wrapper_only); + } + (void) pthread_mutex_unlock(&pslot->sl_mutex); + (void) pthread_mutex_unlock(&delete_sessions_mutex); +} + +static int +labelcmp(const void *a, const void *b) +{ + objlabel_t *obja = (objlabel_t *)a; + objlabel_t *objb = (objlabel_t *)b; + int n; + + if (obja == NULL || obja->label == NULL) + return (-1); + if (objb == NULL || objb->label == NULL) + return (1); + + n = strcmp((char *)obja->label, (char *)objb->label); + if (n == 0) + return (0); + else if (n < 0) + return (-1); + return (1); +} + +/* + * 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 +kms_add_session(CK_SLOT_ID slotID, CK_FLAGS flags, CK_VOID_PTR pApplication, + CK_NOTIFY notify, CK_ULONG *sessionhandle_p) +{ + kms_session_t *new_sp = NULL; + kms_slot_t *pslot; + CK_RV rv; + + /* Allocate a new session struct */ + new_sp = calloc(1, sizeof (kms_session_t)); + if (new_sp == NULL) { + return (CKR_HOST_MEMORY); + } + + new_sp->magic_marker = KMSTOKEN_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; + + avl_create(&new_sp->objlabel_tree, labelcmp, sizeof (objlabel_t), + KMSOFFSETOF(objlabel_t, nodep)); + + rv = kms_reload_labels(new_sp); + if (rv != CKR_OK) { + free(new_sp); + return (rv); + } + + /* 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 = get_slotinfo(); + + (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 kms_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 +kms_delete_session(kms_session_t *session_p, + boolean_t slot_lock_held, boolean_t wrapper_only) +{ + kms_slot_t *pslot; + kms_object_t *objp; + kms_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 = get_slotinfo(); + 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 != KMSTOKEN_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. + */ + kms_delete_all_objects_in_session(session_p, wrapper_only); + + /* Close the KMS agent profile. */ + KMS_UnloadProfile(&session_p->kmsProfile); + + /* Reset SESSION_IS_CLOSING flag. */ + session_p->ses_close_sync &= ~SESSION_IS_CLOSING; + + kms_clear_label_list(&session_p->objlabel_tree); + avl_destroy(&session_p->objlabel_tree); + + (void) pthread_mutex_unlock(&session_p->session_mutex); + /* Destroy the individual session lock */ + (void) pthread_mutex_destroy(&session_p->session_mutex); + + kms_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) kms_cleanup_object(objp); + 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, kms_session_t **session_p) +{ + kms_session_t *sp = (kms_session_t *)(hSession); + CK_RV rv; + + if ((sp == NULL) || + (sp->magic_marker != KMSTOKEN_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 +kms_session_delay_free(kms_session_t *sp) +{ + kms_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 +kms_acquire_all_slots_mutexes() +{ + kms_slot_t *pslot; + kms_session_t *session_p; + + (void) pthread_mutex_lock(&delete_sessions_mutex); + + pslot = get_slotinfo(); + (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 kms_acquire_all_slots_mutexes(). */ +void +kms_release_all_slots_mutexes() +{ + kms_slot_t *pslot; + kms_session_t *session_p; + + pslot = get_slotinfo(); + + /* 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; + } + + /* + * acquired in "acquire_all_slots_mutexes" which only + * happens just prior to a fork. + */ + (void) pthread_mutex_unlock(&pslot->sl_mutex); + (void) pthread_mutex_unlock(&delete_sessions_mutex); +} diff --git a/usr/src/lib/pkcs11/pkcs11_kms/common/kmsSign.c b/usr/src/lib/pkcs11/pkcs11_kms/common/kmsSign.c new file mode 100644 index 0000000000..9e2fe28784 --- /dev/null +++ b/usr/src/lib/pkcs11/pkcs11_kms/common/kmsSign.c @@ -0,0 +1,92 @@ +/* + * 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 <security/cryptoki.h> +#include "kmsGlobal.h" + +/*ARGSUSED*/ +CK_RV +C_SignInit(CK_SESSION_HANDLE hSession, CK_MECHANISM_PTR pMechanism, + CK_OBJECT_HANDLE hKey) +{ + if (!kms_initialized) + return (CKR_CRYPTOKI_NOT_INITIALIZED); + + return (CKR_FUNCTION_NOT_SUPPORTED); +} + +/*ARGSUSED*/ +CK_RV +C_Sign(CK_SESSION_HANDLE hSession, CK_BYTE_PTR pData, CK_ULONG ulDataLen, + CK_BYTE_PTR pSignature, CK_ULONG_PTR pulSignatureLen) +{ + if (!kms_initialized) + return (CKR_CRYPTOKI_NOT_INITIALIZED); + + return (CKR_FUNCTION_NOT_SUPPORTED); +} + + +/*ARGSUSED*/ +CK_RV +C_SignUpdate(CK_SESSION_HANDLE hSession, CK_BYTE_PTR pPart, + CK_ULONG ulPartLen) +{ + if (!kms_initialized) + return (CKR_CRYPTOKI_NOT_INITIALIZED); + return (CKR_FUNCTION_NOT_SUPPORTED); +} + +/*ARGSUSED*/ +CK_RV +C_SignFinal(CK_SESSION_HANDLE hSession, CK_BYTE_PTR pSignature, + CK_ULONG_PTR pulSignatureLen) +{ + if (!kms_initialized) + return (CKR_CRYPTOKI_NOT_INITIALIZED); + + return (CKR_FUNCTION_NOT_SUPPORTED); +} + +/*ARGSUSED*/ +CK_RV +C_SignRecoverInit(CK_SESSION_HANDLE hSession, CK_MECHANISM_PTR pMechanism, + CK_OBJECT_HANDLE hKey) +{ + if (!kms_initialized) + return (CKR_CRYPTOKI_NOT_INITIALIZED); + + return (CKR_FUNCTION_NOT_SUPPORTED); + +} + +/*ARGSUSED*/ +CK_RV +C_SignRecover(CK_SESSION_HANDLE hSession, CK_BYTE_PTR pData, + CK_ULONG ulDataLen, CK_BYTE_PTR pSignature, CK_ULONG_PTR pulSignatureLen) +{ + if (!kms_initialized) + return (CKR_CRYPTOKI_NOT_INITIALIZED); + + return (CKR_FUNCTION_NOT_SUPPORTED); +} diff --git a/usr/src/lib/pkcs11/pkcs11_kms/common/kmsSlot.h b/usr/src/lib/pkcs11/pkcs11_kms/common/kmsSlot.h new file mode 100644 index 0000000000..f1bb38f3d1 --- /dev/null +++ b/usr/src/lib/pkcs11/pkcs11_kms/common/kmsSlot.h @@ -0,0 +1,63 @@ +/* + * 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) 2010, Oracle and/or its affiliates. All rights reserved. + */ + +#ifndef _KMS_SLOT_H +#define _KMS_SLOT_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include "kmsSession.h" + +#define CKU_PUBLIC 2 /* default session auth. state */ + +typedef struct kms_slot { + CK_SLOT_ID sl_provider_id; /* kernel provider ID */ + kms_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; + int total_threshold_count; +} kms_slot_t; + +#define KMS_TOKEN_SLOTID 1 +#define KMS_SLOTS 1 + +/* + * Function Prototypes. + */ +CK_RV kms_slottable_init(); +void cleanup_slottable(); +kms_slot_t *get_slotinfo(); + +#ifdef __cplusplus +} +#endif + +#endif /* _KMS_SLOT_H */ diff --git a/usr/src/lib/pkcs11/pkcs11_kms/common/kmsSlotToken.c b/usr/src/lib/pkcs11/pkcs11_kms/common/kmsSlotToken.c new file mode 100644 index 0000000000..0ee47521f0 --- /dev/null +++ b/usr/src/lib/pkcs11/pkcs11_kms/common/kmsSlotToken.c @@ -0,0 +1,386 @@ +/* + * 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 <stdlib.h> +#include <strings.h> +#include <security/cryptoki.h> +#include <cryptoutil.h> +#include <errno.h> +#include <aes_impl.h> + +#include "kmsGlobal.h" +#include "kmsSlot.h" +#include "kmsKeystoreUtil.h" + +/* + * Just basic AES mechanisms (for now...) + */ +static CK_MECHANISM_TYPE kms_mechanisms[] = { + CKM_AES_KEY_GEN, + CKM_AES_CBC, + CKM_AES_CBC_PAD +}; + +/* + * KMS only supports 256 bit keys, so the range below is MAX-MAX + * instead of MIN-MAX. + */ +static CK_MECHANISM_INFO kms_mechanism_info[] = { + {AES_MAX_KEY_BYTES, AES_MAX_KEY_BYTES, CKF_GENERATE}, + {AES_MAX_KEY_BYTES, AES_MAX_KEY_BYTES, CKF_ENCRYPT|CKF_DECRYPT| + CKF_WRAP|CKF_UNWRAP}, /* CKM_AES_CBC */ + {AES_MAX_KEY_BYTES, AES_MAX_KEY_BYTES, CKF_ENCRYPT|CKF_DECRYPT| + CKF_WRAP|CKF_UNWRAP} /* CKM_AES_CBC_PAD */ +}; + +/* ARGSUSED */ +CK_RV +C_GetSlotList(CK_BBOOL tokenPresent, CK_SLOT_ID_PTR pSlotList, + CK_ULONG_PTR pulCount) +{ + if (!kms_initialized) + return (CKR_CRYPTOKI_NOT_INITIALIZED); + + if (pulCount == NULL) { + return (CKR_ARGUMENTS_BAD); + } + + /* + * If KMS is not available or initialized, return 0 slots + * but CKR_OK status. + */ + if (!kms_is_initialized()) { + *pulCount = 0; + return (CKR_OK); + } + + if (pSlotList == NULL) { + *pulCount = KMS_SLOTS; + return (CKR_OK); + } + + if (*pulCount < KMS_SLOTS) { + *pulCount = KMS_SLOTS; + return (CKR_BUFFER_TOO_SMALL); + } + + *pulCount = 1; + pSlotList[0] = KMS_TOKEN_SLOTID; + + return (CKR_OK); +} + +CK_RV +C_GetSlotInfo(CK_SLOT_ID slotID, CK_SLOT_INFO_PTR pInfo) +{ + if (!kms_initialized) + return (CKR_CRYPTOKI_NOT_INITIALIZED); + + if (slotID != KMS_TOKEN_SLOTID || + !kms_is_initialized()) { + return (CKR_SLOT_ID_INVALID); + } + + if (pInfo == NULL) + return (CKR_ARGUMENTS_BAD); + + /* Provide information about the slot in the provided buffer */ + (void) strncpy((char *)pInfo->slotDescription, SLOT_DESCRIPTION, + 64); + (void) strncpy((char *)pInfo->manufacturerID, MANUFACTURER_ID, 32); + pInfo->flags = CKF_TOKEN_PRESENT; + pInfo->hardwareVersion.major = HARDWARE_VERSION_MAJOR; + pInfo->hardwareVersion.minor = HARDWARE_VERSION_MINOR; + pInfo->firmwareVersion.major = FIRMWARE_VERSION_MAJOR; + pInfo->firmwareVersion.minor = FIRMWARE_VERSION_MINOR; + + return (CKR_OK); +} + +CK_RV +C_GetTokenInfo(CK_SLOT_ID slotID, CK_TOKEN_INFO_PTR pInfo) +{ + kms_cfg_info_t kmscfg; + KMSAGENT_PROFILE_FLAGS kmsflags = 0; + + if (!kms_initialized) + return (CKR_CRYPTOKI_NOT_INITIALIZED); + + if (slotID != KMS_TOKEN_SLOTID || + !kms_is_initialized()) + return (CKR_SLOT_ID_INVALID); + + if (pInfo == NULL) + return (CKR_ARGUMENTS_BAD); + + /* Provide information about a token in the provided buffer */ + (void) strncpy((char *)pInfo->label, KMS_TOKEN_LABEL, 32); + (void) strncpy((char *)pInfo->manufacturerID, MANUFACTURER_ID, 32); + (void) strncpy((char *)pInfo->model, KMS_TOKEN_MODEL, 16); + (void) strncpy((char *)pInfo->serialNumber, KMS_TOKEN_SERIAL, 16); + + pInfo->flags = KMS_TOKEN_FLAGS; + pInfo->ulMaxSessionCount = CK_EFFECTIVELY_INFINITE; + pInfo->ulSessionCount = kms_session_cnt; + pInfo->ulMaxRwSessionCount = CK_EFFECTIVELY_INFINITE; + pInfo->ulRwSessionCount = kms_session_rw_cnt; + pInfo->ulMaxPinLen = MAX_PIN_LEN; + pInfo->ulMinPinLen = MIN_PIN_LEN; + pInfo->ulTotalPublicMemory = CK_UNAVAILABLE_INFORMATION; + pInfo->ulFreePublicMemory = CK_UNAVAILABLE_INFORMATION; + pInfo->ulTotalPrivateMemory = CK_UNAVAILABLE_INFORMATION; + pInfo->ulFreePrivateMemory = CK_UNAVAILABLE_INFORMATION; + pInfo->hardwareVersion.major = HARDWARE_VERSION_MAJOR; + pInfo->hardwareVersion.minor = HARDWARE_VERSION_MINOR; + pInfo->firmwareVersion.major = FIRMWARE_VERSION_MAJOR; + pInfo->firmwareVersion.minor = FIRMWARE_VERSION_MINOR; + (void) memset(pInfo->utcTime, ' ', 16); + + if (KMS_GetConfigInfo(&kmscfg) == CKR_OK && + KMSAgent_GetProfileStatus(kmscfg.name, &kmsflags) == + KMS_AGENT_STATUS_OK) { + + if ((kmsflags & KMSAGENT_PROFILE_EXISTS_FLAG) && + (kmsflags & KMSAGENT_CLIENTKEY_EXISTS_FLAG)) + pInfo->flags |= CKF_TOKEN_INITIALIZED; + else + pInfo->flags &= ~CKF_TOKEN_INITIALIZED; + } + return (CKR_OK); +} + +/*ARGSUSED*/ +CK_RV +C_WaitForSlotEvent(CK_FLAGS flags, CK_SLOT_ID_PTR pSlot, CK_VOID_PTR pReserved) +{ + if (!kms_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) +{ + int i; + ulong_t mechnum; + + /* + * Just check to see if the library has been + * properly initialized. + */ + if (!kms_initialized) + return (CKR_CRYPTOKI_NOT_INITIALIZED); + + /* + * This is different from above check, this verifies that + * the KMS token is actually configured. + */ + if (slotID != KMS_TOKEN_SLOTID || + !kms_is_initialized()) + return (CKR_SLOT_ID_INVALID); + + mechnum = sizeof (kms_mechanisms) / sizeof (CK_MECHANISM_TYPE); + if (pMechanismList == NULL) { + *pulCount = mechnum; + return (CKR_OK); + } + if (*pulCount < mechnum) { + *pulCount = mechnum; + return (CKR_BUFFER_TOO_SMALL); + } + for (i = 0; i < mechnum; i++) + pMechanismList[i] = kms_mechanisms[i]; + + *pulCount = mechnum; + + return (CKR_OK); +} + +CK_RV +C_GetMechanismInfo(CK_SLOT_ID slotID, CK_MECHANISM_TYPE type, + CK_MECHANISM_INFO_PTR pInfo) +{ + CK_ULONG mechnum, i; + + if (!kms_initialized) + return (CKR_CRYPTOKI_NOT_INITIALIZED); + + if (slotID != KMS_TOKEN_SLOTID || + !kms_is_initialized()) + return (CKR_SLOT_ID_INVALID); + + if (pInfo == NULL) { + return (CKR_ARGUMENTS_BAD); + } + + mechnum = sizeof (kms_mechanisms) / sizeof (CK_MECHANISM_TYPE); + for (i = 0; i < mechnum; i++) { + if (kms_mechanisms[i] == type) + break; + } + + if (i == mechnum) + /* unsupported mechanism */ + return (CKR_MECHANISM_INVALID); + + pInfo->ulMinKeySize = kms_mechanism_info[i].ulMinKeySize; + pInfo->ulMaxKeySize = kms_mechanism_info[i].ulMaxKeySize; + pInfo->flags = kms_mechanism_info[i].flags; + + return (CKR_OK); +} + +/*ARGSUSED*/ +CK_RV +C_InitToken(CK_SLOT_ID slotID, CK_UTF8CHAR_PTR pPin, CK_ULONG ulPinLen, + CK_UTF8CHAR_PTR pLabel) +{ + CK_RV rv = CKR_FUNCTION_FAILED; + kms_cfg_info_t kmscfg; + KMSAGENT_PROFILE_FLAGS kmsflags; + + if (!kms_initialized) + return (CKR_CRYPTOKI_NOT_INITIALIZED); + + if (slotID != KMS_TOKEN_SLOTID || + !kms_is_initialized()) + return (CKR_SLOT_ID_INVALID); + + if (KMS_GetConfigInfo(&kmscfg) != CKR_OK || + KMSAgent_GetProfileStatus(kmscfg.name, &kmsflags) != + KMS_AGENT_STATUS_OK) + return (CKR_FUNCTION_FAILED); + + if (!(kmsflags & KMSAGENT_PROFILE_EXISTS_FLAG) || + !(kmsflags & KMSAGENT_CLIENTKEY_EXISTS_FLAG)) { + KMSClientProfile kmsProfile; + /* + * Attempt to enroll and load a KMS profile. + * This will force the KMSAgent library to fetch + * the profile, the CA certificate, and the + * client private key and store them locally so that + * the KMS agent API can be used later. + */ + rv = KMS_LoadProfile( + &kmsProfile, + &kmscfg, + (const char *)pPin, + (size_t)ulPinLen); + + if (rv == CKR_OK) + KMS_UnloadProfile(&kmsProfile); + } + return (rv); +} + +/*ARGSUSED*/ +CK_RV +C_InitPIN(CK_SESSION_HANDLE hSession, CK_UTF8CHAR_PTR pPin, CK_ULONG ulPinLen) +{ + if (!kms_initialized) + return (CKR_CRYPTOKI_NOT_INITIALIZED); + + /* + * Could be supported once the agent library supports + * storing the client certificate in a PKCS#12 file. + */ + 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; + kms_session_t *session_p; + boolean_t ses_lock_held = B_FALSE; + + if (!kms_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); + } + + /* + * If the token is not yet initialized, we cannot set the pin. + */ + if (!kms_is_initialized()) { + REFRELE(session_p, ses_lock_held); + return (CKR_FUNCTION_FAILED); + } + + if (pOldPin == NULL || ulOldLen == 0 || + pNewPin == NULL || ulNewLen == 0) { + REFRELE(session_p, ses_lock_held); + return (CKR_ARGUMENTS_BAD); + } + + if (!kms_is_pin_set()) { + /* + * We don't yet support this mode since + * the KMS private key file will automatically + * be generated using the KMS Agent passphrase + * which is initialized out-of-band. + */ + rv = CKR_FUNCTION_NOT_SUPPORTED; + + } else { + /* + * Login to KMS by attempting to load the profile using + * the given password. + */ + rv = KMS_LoadProfile(&session_p->kmsProfile, + &session_p->configInfo, + (const char *)pOldPin, + (size_t)ulOldLen); + if (rv == CKR_USER_ANOTHER_ALREADY_LOGGED_IN) + rv = CKR_OK; + + if (rv == CKR_OK) + rv = KMS_ChangeLocalPWD(session_p, + (const char *)pOldPin, + (const char *)pNewPin); + } + + REFRELE(session_p, ses_lock_held); + return (rv); +} diff --git a/usr/src/lib/pkcs11/pkcs11_kms/common/kmsSlottable.c b/usr/src/lib/pkcs11/pkcs11_kms/common/kmsSlottable.c new file mode 100644 index 0000000000..c34954dc51 --- /dev/null +++ b/usr/src/lib/pkcs11/pkcs11_kms/common/kmsSlottable.c @@ -0,0 +1,77 @@ +/* + * 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) 2010, Oracle and/or its affiliates. All rights reserved. + */ + +#include <errno.h> +#include <security/cryptoki.h> +#include "kmsGlobal.h" +#include "kmsSlot.h" + +static kms_slot_t *slotinfo = NULL; + +/* + * Initialize the slotinfo record. + * + * 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 +kms_slottable_init() +{ + CK_RV rv = CKR_OK; + + /* Allocate space for the slot table */ + slotinfo = calloc(KMS_SLOTS, sizeof (kms_slot_t)); + if (slotinfo == NULL) + return (CKR_HOST_MEMORY); + + slotinfo->sl_sess_list = NULL; + slotinfo->sl_tobj_list = NULL; + slotinfo->sl_state = CKU_PUBLIC; + + /* Initialize this slot's mutex */ + if (pthread_mutex_init(&slotinfo->sl_mutex, NULL) != 0) { + (void) free(slotinfo); + slotinfo = NULL; + return (CKR_FUNCTION_FAILED); + } + + return (rv); +} + +void +cleanup_slottable() +{ + if (slotinfo != NULL) { + (void) pthread_mutex_destroy(&slotinfo->sl_mutex); + (void) free(slotinfo); + slotinfo = NULL; + } +} + +kms_slot_t * +get_slotinfo() +{ + return (slotinfo); +} diff --git a/usr/src/lib/pkcs11/pkcs11_kms/common/kmsVerify.c b/usr/src/lib/pkcs11/pkcs11_kms/common/kmsVerify.c new file mode 100644 index 0000000000..64db49c9a3 --- /dev/null +++ b/usr/src/lib/pkcs11/pkcs11_kms/common/kmsVerify.c @@ -0,0 +1,91 @@ +/* + * 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) 2010, Oracle and/or its affiliates. All rights reserved. + */ + +#include <security/cryptoki.h> +#include "kmsGlobal.h" + +/*ARGSUSED*/ +CK_RV +C_VerifyInit(CK_SESSION_HANDLE hSession, CK_MECHANISM_PTR pMechanism, + CK_OBJECT_HANDLE hKey) +{ + if (!kms_initialized) + return (CKR_CRYPTOKI_NOT_INITIALIZED); + + return (CKR_FUNCTION_NOT_SUPPORTED); +} + +/*ARGSUSED*/ +CK_RV +C_Verify(CK_SESSION_HANDLE hSession, CK_BYTE_PTR pData, CK_ULONG ulDataLen, + CK_BYTE_PTR pSignature, CK_ULONG ulSignatureLen) +{ + if (!kms_initialized) + return (CKR_CRYPTOKI_NOT_INITIALIZED); + + return (CKR_FUNCTION_NOT_SUPPORTED); +} + +/*ARGSUSED*/ +CK_RV +C_VerifyUpdate(CK_SESSION_HANDLE hSession, CK_BYTE_PTR pPart, + CK_ULONG ulPartLen) +{ + if (!kms_initialized) + return (CKR_CRYPTOKI_NOT_INITIALIZED); + + return (CKR_FUNCTION_NOT_SUPPORTED); +} + +/*ARGSUSED*/ +CK_RV +C_VerifyFinal(CK_SESSION_HANDLE hSession, CK_BYTE_PTR pSignature, + CK_ULONG ulSignatureLen) +{ + if (!kms_initialized) + return (CKR_CRYPTOKI_NOT_INITIALIZED); + + return (CKR_FUNCTION_NOT_SUPPORTED); +} + +/*ARGSUSED*/ +CK_RV +C_VerifyRecoverInit(CK_SESSION_HANDLE hSession, CK_MECHANISM_PTR pMechanism, + CK_OBJECT_HANDLE hKey) +{ + if (!kms_initialized) + return (CKR_CRYPTOKI_NOT_INITIALIZED); + + return (CKR_FUNCTION_NOT_SUPPORTED); +} + +/*ARGSUSED*/ +CK_RV +C_VerifyRecover(CK_SESSION_HANDLE hSession, CK_BYTE_PTR pSignature, + CK_ULONG ulSignatureLen, CK_BYTE_PTR pData, CK_ULONG_PTR pulDataLen) +{ + if (!kms_initialized) + return (CKR_CRYPTOKI_NOT_INITIALIZED); + + return (CKR_FUNCTION_NOT_SUPPORTED); +} diff --git a/usr/src/lib/pkcs11/pkcs11_kms/common/mapfile-vers b/usr/src/lib/pkcs11/pkcs11_kms/common/mapfile-vers new file mode 100644 index 0000000000..a66e3742f7 --- /dev/null +++ b/usr/src/lib/pkcs11/pkcs11_kms/common/mapfile-vers @@ -0,0 +1,98 @@ +# +# 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) 2010, Oracle and/or its affiliates. All rights reserved. +# + +$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; + local: + *; +}; diff --git a/usr/src/lib/pkcs11/pkcs11_kms/i386/Makefile b/usr/src/lib/pkcs11/pkcs11_kms/i386/Makefile new file mode 100644 index 0000000000..5b0478c540 --- /dev/null +++ b/usr/src/lib/pkcs11/pkcs11_kms/i386/Makefile @@ -0,0 +1,29 @@ +# +# 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) 2010, Oracle and/or its affiliates. All rights reserved. +# + + +include ../Makefile.com + +install: all $(ROOTLIBS) $(ROOTLINKS) diff --git a/usr/src/lib/pkcs11/pkcs11_kms/sparc/Makefile b/usr/src/lib/pkcs11/pkcs11_kms/sparc/Makefile new file mode 100644 index 0000000000..f51825ef6c --- /dev/null +++ b/usr/src/lib/pkcs11/pkcs11_kms/sparc/Makefile @@ -0,0 +1,27 @@ +# +# 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) 2010, Oracle and/or its affiliates. All rights reserved. +# +include ../Makefile.com + +install: all $(ROOTLIBS) $(ROOTLINKS) diff --git a/usr/src/lib/pkcs11/pkcs11_kms/sparcv9/Makefile b/usr/src/lib/pkcs11/pkcs11_kms/sparcv9/Makefile new file mode 100644 index 0000000000..99c7ac2351 --- /dev/null +++ b/usr/src/lib/pkcs11/pkcs11_kms/sparcv9/Makefile @@ -0,0 +1,36 @@ +# +# 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) 2010, Oracle and/or its affiliates. All rights reserved. +# + + +include ../Makefile.com +include ../../../Makefile.lib.64 + +LINTCHECKFLAGS += -erroff=E_INCONS_ARG_DECL2 \ + -erroff=E_INCONS_ARG_USED2 \ + -erroff=E_INCONS_VAL_TYPE_DECL2 \ + -erroff=E_INCONS_VAL_TYPE_USED2 \ + -erroff=E_FUNC_DECL_VAR_ARG2 + +install: all $(ROOTLIBS64) $(ROOTLINKS64) |