summaryrefslogtreecommitdiff
path: root/usr/src/lib/pkcs11
diff options
context:
space:
mode:
Diffstat (limited to 'usr/src/lib/pkcs11')
-rw-r--r--usr/src/lib/pkcs11/Makefile5
-rw-r--r--usr/src/lib/pkcs11/pkcs11_kms/Makefile43
-rw-r--r--usr/src/lib/pkcs11/pkcs11_kms/Makefile.com99
-rw-r--r--usr/src/lib/pkcs11/pkcs11_kms/amd64/Makefile36
-rw-r--r--usr/src/lib/pkcs11/pkcs11_kms/common/kmsAESCrypt.c1046
-rw-r--r--usr/src/lib/pkcs11/pkcs11_kms/common/kmsAttributeUtil.c1726
-rw-r--r--usr/src/lib/pkcs11/pkcs11_kms/common/kmsCrypt.h75
-rw-r--r--usr/src/lib/pkcs11/pkcs11_kms/common/kmsDecrypt.c405
-rw-r--r--usr/src/lib/pkcs11/pkcs11_kms/common/kmsDigest.c78
-rw-r--r--usr/src/lib/pkcs11/pkcs11_kms/common/kmsDualCrypt.c74
-rw-r--r--usr/src/lib/pkcs11/pkcs11_kms/common/kmsEncrypt.c424
-rw-r--r--usr/src/lib/pkcs11/pkcs11_kms/common/kmsGeneral.c548
-rw-r--r--usr/src/lib/pkcs11/pkcs11_kms/common/kmsGlobal.h88
-rw-r--r--usr/src/lib/pkcs11/pkcs11_kms/common/kmsKeys.c179
-rw-r--r--usr/src/lib/pkcs11/pkcs11_kms/common/kmsKeystoreUtil.c1227
-rw-r--r--usr/src/lib/pkcs11/pkcs11_kms/common/kmsKeystoreUtil.h57
-rw-r--r--usr/src/lib/pkcs11/pkcs11_kms/common/kmsObject.c664
-rw-r--r--usr/src/lib/pkcs11/pkcs11_kms/common/kmsObject.h308
-rw-r--r--usr/src/lib/pkcs11/pkcs11_kms/common/kmsObjectUtil.c930
-rw-r--r--usr/src/lib/pkcs11/pkcs11_kms/common/kmsRand.c48
-rw-r--r--usr/src/lib/pkcs11/pkcs11_kms/common/kmsSession.c355
-rw-r--r--usr/src/lib/pkcs11/pkcs11_kms/common/kmsSession.h188
-rw-r--r--usr/src/lib/pkcs11/pkcs11_kms/common/kmsSessionUtil.c491
-rw-r--r--usr/src/lib/pkcs11/pkcs11_kms/common/kmsSign.c92
-rw-r--r--usr/src/lib/pkcs11/pkcs11_kms/common/kmsSlot.h63
-rw-r--r--usr/src/lib/pkcs11/pkcs11_kms/common/kmsSlotToken.c386
-rw-r--r--usr/src/lib/pkcs11/pkcs11_kms/common/kmsSlottable.c77
-rw-r--r--usr/src/lib/pkcs11/pkcs11_kms/common/kmsVerify.c91
-rw-r--r--usr/src/lib/pkcs11/pkcs11_kms/common/mapfile-vers98
-rw-r--r--usr/src/lib/pkcs11/pkcs11_kms/i386/Makefile29
-rw-r--r--usr/src/lib/pkcs11/pkcs11_kms/sparc/Makefile27
-rw-r--r--usr/src/lib/pkcs11/pkcs11_kms/sparcv9/Makefile36
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)