2N/A/*
2N/A * CDDL HEADER START
2N/A *
2N/A * The contents of this file are subject to the terms of the
2N/A * Common Development and Distribution License (the "License").
2N/A * You may not use this file except in compliance with the License.
2N/A *
2N/A * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
2N/A * or http://www.opensolaris.org/os/licensing.
2N/A * See the License for the specific language governing permissions
2N/A * and limitations under the License.
2N/A *
2N/A * When distributing Covered Code, include this CDDL HEADER in each
2N/A * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
2N/A * If applicable, add the following below this CDDL HEADER, with the
2N/A * fields enclosed by brackets "[]" replaced with your own identifying
2N/A * information: Portions Copyright [yyyy] [name of copyright owner]
2N/A *
2N/A * CDDL HEADER END
2N/A *
2N/A * Copyright (c) 2010, 2011, Oracle and/or its affiliates. All rights reserved.
2N/A */
2N/A
2N/A#include <stdio.h>
2N/A#include <malloc.h>
2N/A#include <memory.h>
2N/A#include <strings.h>
2N/A#include <fcntl.h>
2N/A#include <errno.h>
2N/A#include <ctype.h>
2N/A#include <sys/types.h>
2N/A#include <sys/stat.h>
2N/A#include <cryptoutil.h>
2N/A#include <unistd.h>
2N/A#include <utmpx.h>
2N/A#include <pthread.h>
2N/A#include <pwd.h>
2N/A#include <sha2.h>
2N/A#include <security/cryptoki.h>
2N/A#include <aes_impl.h>
2N/A#include <sys/avl.h>
2N/A
2N/A#include "kmsSession.h"
2N/A#include "kmsGlobal.h"
2N/A#include "kmsObject.h"
2N/A
2N/Astatic CK_RV
2N/AGetPKCS11StatusFromAgentStatus(KMS_AGENT_STATUS status);
2N/A
2N/Astatic char keystore_path[BUFSIZ];
2N/Astatic boolean_t keystore_path_initialized = B_FALSE;
2N/Astatic time_t last_objlist_mtime = 0;
2N/Apthread_mutex_t flock_mutex = PTHREAD_MUTEX_INITIALIZER;
2N/Astatic boolean_t initialized = B_FALSE;
2N/A
2N/Astatic struct flock fl = {
2N/A 0,
2N/A 0,
2N/A 0,
2N/A 0,
2N/A 0,
2N/A 0,
2N/A {0, 0, 0, 0}
2N/A};
2N/A
2N/A#define KEYSTORE_PATH "/var/user/"
2N/A#define ALTERNATE_KEYSTORE_PATH "KMSTOKEN_DIR"
2N/A#define KMS_PROFILE_FILENAME "profile.cfg"
2N/A#define KMS_DATAUNIT_DESCRIPTION "Oracle PKCS11/KMS"
2N/A#define KMS_ATTR_DESC_PFX "PKCS#11v2.20: "
2N/A#define KMSTOKEN_CONFIG_FILENAME "kmstoken.cfg"
2N/A#define KMSTOKEN_LABELLIST_FILENAME "objlabels.lst"
2N/A
2N/Astatic void
2N/Akms_parse_labels(avl_tree_t *, char *, int);
2N/A
2N/Astatic void
2N/Akms_hash_string(char *label, uchar_t *hash)
2N/A{
2N/A SHA2_CTX ctx;
2N/A
2N/A SHA2Init(SHA256, &ctx);
2N/A SHA2Update(&ctx, label, strlen(label));
2N/A SHA2Final(hash, &ctx);
2N/A}
2N/A
2N/Astatic char *
2N/Aget_username(char *username, int len)
2N/A{
2N/A struct passwd pwd, *user_info;
2N/A long buflen;
2N/A char *pwdbuf = NULL;
2N/A
2N/A bzero(username, len);
2N/A
2N/A buflen = sysconf(_SC_GETPW_R_SIZE_MAX);
2N/A if (buflen == -1)
2N/A return (username); /* should not happen */
2N/A
2N/A pwdbuf = calloc(1, buflen);
2N/A if (pwdbuf == NULL)
2N/A return (username); /* zero-ed earlier */
2N/A
2N/A user_info = getpwuid_r(getuid(), &pwd, pwdbuf, buflen);
2N/A
2N/A if (user_info != NULL)
2N/A (void) strlcpy(username, user_info->pw_name, len);
2N/A
2N/A free(pwdbuf);
2N/A return (username);
2N/A}
2N/A
2N/Astatic char *
2N/Akms_get_keystore_path()
2N/A{
2N/A char *env_val;
2N/A char username[sizeof (((struct utmpx *)0)->ut_user)];
2N/A
2N/A if (!keystore_path_initialized) {
2N/A env_val = getenv(ALTERNATE_KEYSTORE_PATH);
2N/A bzero(keystore_path, sizeof (keystore_path));
2N/A /*
2N/A * If it isn't set or is set to the empty string use the
2N/A * default location. We need to check for the empty string
2N/A * because some users "unset" environment variables by giving
2N/A * them no value, this isn't the same thing as removing it
2N/A * from the environment.
2N/A */
2N/A if ((env_val == NULL) || (strcmp(env_val, "") == 0)) {
2N/A /*
2N/A * Alternate path not specified,
2N/A * use /var/user/$USER/kms
2N/A */
2N/A (void) snprintf(keystore_path,
2N/A sizeof (keystore_path), "%s/%s/kms",
2N/A KEYSTORE_PATH,
2N/A get_username(username, sizeof (username)));
2N/A } else {
2N/A (void) strlcpy(keystore_path, env_val,
2N/A sizeof (keystore_path));
2N/A }
2N/A keystore_path_initialized = B_TRUE;
2N/A }
2N/A return (keystore_path);
2N/A}
2N/A
2N/Astatic char *
2N/Aget_non_comment_line(char *cfgbuf, size_t cfglen, char *buf, size_t buflen)
2N/A{
2N/A char *s = cfgbuf;
2N/A char *end = cfgbuf + cfglen;
2N/A char *f;
2N/A
2N/A /* Skip over blank lines CR/LF */
2N/A while (s < end && (isspace(*s) || (*s == '#'))) {
2N/A /* check for comment sign */
2N/A if (*s == '#') {
2N/A /* skip the rest of the line */
2N/A while (*s != '\n' && *s != '\r' && s < end)
2N/A s++;
2N/A }
2N/A if ((s < end) && isspace(*s))
2N/A s++;
2N/A }
2N/A
2N/A if (s < end) {
2N/A char save, *e;
2N/A f = s; /* mark the beginning. */
2N/A /* Find the end of the line and null terminate it. */
2N/A while (*s != '\n' && *s != '\r' && s < end) s++;
2N/A save = *s;
2N/A *s = 0x00;
2N/A (void) strncpy(buf, f, buflen);
2N/A *s = save;
2N/A
2N/A /* Strip trailing whitespace */
2N/A f = buf;
2N/A e = f + strlen(buf) - 1;
2N/A while (e >= f && isspace(*e)) {
2N/A *e = 0x00;
2N/A e--;
2N/A }
2N/A } else {
2N/A /* If we reached the end, return NULL */
2N/A s = NULL;
2N/A }
2N/Adone:
2N/A return (s);
2N/A}
2N/A
2N/Astatic int
2N/Aflock_fd(int fd, int cmd, pthread_mutex_t *mutex)
2N/A{
2N/A int ret = 0;
2N/A
2N/A (void) pthread_mutex_lock(mutex);
2N/A
2N/A fl.l_type = cmd;
2N/A
2N/A while ((ret = fcntl(fd, F_SETLKW, &fl)) == -1) {
2N/A if (errno != EINTR)
2N/A break;
2N/A }
2N/A (void) pthread_mutex_unlock(mutex);
2N/A return (ret);
2N/A}
2N/A
2N/A/*
2N/A * Open the keystore description file in the specified mode.
2N/A */
2N/Astatic int
2N/Aopen_and_lock_file(char *filename, int cmd, mode_t mode,
2N/A pthread_mutex_t *mutex)
2N/A{
2N/A int fd;
2N/A
2N/A fd = open_nointr(filename, mode|O_NONBLOCK);
2N/A if (fd < 0)
2N/A return (fd);
2N/A
2N/A if (flock_fd(fd, cmd, mutex)) {
2N/A if (fd > 0)
2N/A (void) close(fd);
2N/A return (-1);
2N/A }
2N/A
2N/A return (fd);
2N/A}
2N/A
2N/Astatic int
2N/Akms_slurp_fd(int fd, char *buf, size_t buflen)
2N/A{
2N/A int n, total = 0;
2N/A do {
2N/A n = readn_nointr(fd, &buf[total], buflen - total);
2N/A if (n != (buflen - total))
2N/A break;
2N/A else
2N/A total += n;
2N/A } while (total < buflen);
2N/A return (total);
2N/A}
2N/A
2N/Astatic int
2N/Akms_slurp_file(char *file, char *buf, size_t buflen)
2N/A{
2N/A int fd, total = 0;
2N/A
2N/A fd = open_and_lock_file(file, F_RDLCK, O_RDONLY, &flock_mutex);
2N/A if (fd == -1)
2N/A return (-1);
2N/A
2N/A total = kms_slurp_fd(fd, buf, buflen);
2N/A
2N/A if (flock_fd(fd, F_UNLCK, &flock_mutex))
2N/A total = -1;
2N/A
2N/A (void) close(fd);
2N/A
2N/A return (total);
2N/A}
2N/A
2N/A/*
2N/A * The KMS token is considered "initialized" if the file with the token
2N/A * configuration information is present.
2N/A */
2N/ACK_BBOOL
2N/Akms_is_initialized()
2N/A{
2N/A CK_BBOOL rv;
2N/A char *ksdir;
2N/A char cfgfile_path[BUFSIZ];
2N/A struct stat statp;
2N/A
2N/A ksdir = kms_get_keystore_path();
2N/A if (ksdir == NULL)
2N/A return (CKR_FUNCTION_FAILED);
2N/A
2N/A (void) snprintf(cfgfile_path, sizeof (cfgfile_path),
2N/A "%s/%s", ksdir, KMSTOKEN_CONFIG_FILENAME);
2N/A
2N/A if (stat(cfgfile_path, &statp))
2N/A rv = FALSE;
2N/A else
2N/A rv = TRUE;
2N/A
2N/A return (rv);
2N/A}
2N/A
2N/Astatic CK_RV
2N/Akms_read_config_data(char *path, kms_cfg_info_t *cfginfo)
2N/A{
2N/A CK_RV rv = CKR_OK;
2N/A char *cfgbuf = NULL;
2N/A char *ptr;
2N/A char buf[BUFSIZ];
2N/A size_t buflen = 0, remain;
2N/A struct stat statp;
2N/A
2N/A if (path == NULL || cfginfo == NULL)
2N/A return (CKR_ARGUMENTS_BAD);
2N/A
2N/A if (stat(path, &statp) == -1) {
2N/A return (CKR_FUNCTION_FAILED);
2N/A }
2N/A
2N/A cfgbuf = calloc(1, statp.st_size);
2N/A if (cfgbuf == NULL)
2N/A return (CKR_HOST_MEMORY);
2N/A
2N/A buflen = kms_slurp_file(path, cfgbuf, statp.st_size);
2N/A if (buflen != statp.st_size) {
2N/A free(cfgbuf);
2N/A return (CKR_FUNCTION_FAILED);
2N/A }
2N/A
2N/A remain = buflen;
2N/A ptr = cfgbuf;
2N/A ptr = get_non_comment_line(ptr, remain,
2N/A cfginfo->name, sizeof (cfginfo->name));
2N/A if (ptr == NULL) {
2N/A rv = CKR_FUNCTION_FAILED;
2N/A goto done;
2N/A }
2N/A remain = buflen - (ptr - cfgbuf);
2N/A ptr = get_non_comment_line(ptr, remain,
2N/A cfginfo->agentId, sizeof (cfginfo->agentId));
2N/A if (ptr == 0) {
2N/A rv = CKR_FUNCTION_FAILED;
2N/A goto done;
2N/A }
2N/A remain = buflen - (ptr - cfgbuf);
2N/A ptr = get_non_comment_line(ptr, remain,
2N/A cfginfo->agentAddr, sizeof (cfginfo->agentAddr));
2N/A if (ptr == 0) {
2N/A rv = CKR_FUNCTION_FAILED;
2N/A goto done;
2N/A }
2N/A remain = buflen - (ptr - cfgbuf);
2N/A ptr = get_non_comment_line(ptr, remain, buf, sizeof (buf));
2N/A if (ptr == 0) {
2N/A rv = CKR_FUNCTION_FAILED;
2N/A goto done;
2N/A }
2N/A cfginfo->transTimeout = atoi(buf);
2N/A
2N/A remain = buflen - (ptr - cfgbuf);
2N/A ptr = get_non_comment_line(ptr, remain, buf, sizeof (buf));
2N/A if (ptr == 0) {
2N/A rv = CKR_FUNCTION_FAILED;
2N/A goto done;
2N/A }
2N/A cfginfo->failoverLimit = atoi(buf);
2N/A
2N/A remain = buflen - (ptr - cfgbuf);
2N/A ptr = get_non_comment_line(ptr, remain, buf, sizeof (buf));
2N/A if (ptr == 0) {
2N/A rv = CKR_FUNCTION_FAILED;
2N/A goto done;
2N/A }
2N/A cfginfo->discoveryFreq = atoi(buf);
2N/A
2N/A remain = buflen - (ptr - cfgbuf);
2N/A ptr = get_non_comment_line(ptr, remain, buf, sizeof (buf));
2N/A if (ptr == 0) {
2N/A rv = CKR_FUNCTION_FAILED;
2N/A goto done;
2N/A }
2N/A cfginfo->securityMode = atoi(buf);
2N/Adone:
2N/A if (cfgbuf != NULL)
2N/A free(cfgbuf);
2N/A return (rv);
2N/A}
2N/A
2N/ACK_BBOOL
2N/Akms_is_pin_set()
2N/A{
2N/A CK_BBOOL rv = TRUE;
2N/A kms_cfg_info_t kmscfg;
2N/A struct stat statp;
2N/A char *ksdir;
2N/A char filepath[BUFSIZ];
2N/A
2N/A ksdir = kms_get_keystore_path();
2N/A if (ksdir == NULL)
2N/A return (FALSE);
2N/A
2N/A (void) snprintf(filepath, sizeof (filepath),
2N/A "%s/%s", ksdir, KMSTOKEN_CONFIG_FILENAME);
2N/A
2N/A if ((rv = kms_read_config_data(filepath, &kmscfg)))
2N/A return (FALSE);
2N/A
2N/A /*
2N/A * The PK12 file is only established once the user has enrolled
2N/A * and is thus considered having a PIN set.
2N/A */
2N/A (void) snprintf(filepath, sizeof (filepath),
2N/A "%s/%s/%s", ksdir, kmscfg.name, CLIENT_PK12_FILE);
2N/A
2N/A if (stat(filepath, &statp))
2N/A rv = FALSE; /* file doesn't exist. */
2N/A else
2N/A rv = TRUE; /* File exists, PIN is set */
2N/A
2N/A return (rv);
2N/A}
2N/A
2N/Avoid
2N/Akms_clear_label_list(avl_tree_t *tree)
2N/A{
2N/A void *cookie = NULL;
2N/A objlabel_t *node;
2N/A
2N/A while ((node = avl_destroy_nodes(tree, &cookie)) != NULL) {
2N/A free(node->label);
2N/A free(node);
2N/A }
2N/A}
2N/A
2N/A
2N/Avoid
2N/Aadd_label_node(avl_tree_t *tree, char *label)
2N/A{
2N/A avl_index_t where;
2N/A objlabel_t *node;
2N/A objlabel_t *newnode;
2N/A int i;
2N/A
2N/A if (tree == NULL || label == NULL)
2N/A return;
2N/A
2N/A /* Remove trailing CR */
2N/A i = strlen(label) - 1;
2N/A while (i > 0 && label[i] == '\n')
2N/A label[i--] = 0x00;
2N/A
2N/A newnode = calloc(1, sizeof (objlabel_t));
2N/A newnode->label = (char *)strdup(label);
2N/A if (newnode->label == NULL) {
2N/A free(newnode);
2N/A return;
2N/A }
2N/A /* see if this entry already exists */
2N/A node = avl_find(tree, newnode, &where);
2N/A if (node == NULL) {
2N/A avl_insert(tree, newnode, where);
2N/A } else {
2N/A /* It's a dup, don't add it */
2N/A free(newnode->label);
2N/A free(newnode);
2N/A
2N/A /*
2N/A * If its a dup that was already marked for deletion,
2N/A * remove it.
2N/A */
2N/A if (node->flags & DELETE_FLAG) {
2N/A avl_remove(tree, node);
2N/A free(node->label);
2N/A free(node);
2N/A }
2N/A }
2N/A}
2N/A
2N/Astatic void
2N/Akms_parse_labels(avl_tree_t *tree, char *buf, int buflen)
2N/A{
2N/A int remain;
2N/A char *ptr;
2N/A char buffer[BUFSIZ];
2N/A /*
2N/A * Read each line and add it as a label node.
2N/A * We don't need to clear the label list because
2N/A * "add_label_node" checks for dups.
2N/A */
2N/A remain = buflen;
2N/A ptr = buf;
2N/A while (remain > 0) {
2N/A ptr = get_non_comment_line(ptr, remain,
2N/A buffer, sizeof (buffer));
2N/A if (ptr == NULL) {
2N/A return;
2N/A }
2N/A add_label_node(tree, buffer);
2N/A remain = buflen - (ptr - buf);
2N/A }
2N/A}
2N/A
2N/ACK_RV
2N/Akms_reload_labels(kms_session_t *sp)
2N/A{
2N/A CK_RV rv = CKR_OK;
2N/A char *cfgbuf = NULL;
2N/A size_t buflen;
2N/A struct stat statp;
2N/A char *ksdir;
2N/A char labelfile[BUFSIZ];
2N/A
2N/A ksdir = kms_get_keystore_path();
2N/A if (ksdir == NULL)
2N/A return (CKR_GENERAL_ERROR);
2N/A
2N/A (void) snprintf(labelfile, sizeof (labelfile),
2N/A "%s/%s", ksdir, KMSTOKEN_LABELLIST_FILENAME);
2N/A
2N/A bzero(&statp, sizeof (statp));
2N/A if (stat(labelfile, &statp) == -1) {
2N/A if (errno == ENOENT) {
2N/A FILE *fp;
2N/A /* Create it */
2N/A fp = fopen(labelfile, "w");
2N/A if (fp == NULL)
2N/A return (CKR_GENERAL_ERROR);
2N/A (void) fclose(fp);
2N/A /* Re-stat to get current times */
2N/A if (stat(labelfile, &statp) == -1)
2N/A return (CKR_GENERAL_ERROR);
2N/A last_objlist_mtime = statp.st_mtime;
2N/A }
2N/A }
2N/A
2N/A /* if nothing to read or the timestamp hasnt changed, return */
2N/A if (statp.st_size == 0 || statp.st_mtime == last_objlist_mtime) {
2N/A return (CKR_OK);
2N/A }
2N/A
2N/A cfgbuf = calloc(1, statp.st_size);
2N/A if (cfgbuf == NULL)
2N/A return (CKR_HOST_MEMORY);
2N/A
2N/A buflen = kms_slurp_file(labelfile, cfgbuf, statp.st_size);
2N/A if (buflen != statp.st_size) {
2N/A free(cfgbuf);
2N/A return (CKR_FUNCTION_FAILED);
2N/A }
2N/A
2N/A kms_parse_labels(&sp->objlabel_tree, cfgbuf, buflen);
2N/Aend:
2N/A if (cfgbuf)
2N/A free(cfgbuf);
2N/A
2N/A return (rv);
2N/A}
2N/A
2N/Astatic CK_RV
2N/Akms_get_object_label(kms_object_t *obj, char *label, int len)
2N/A{
2N/A CK_RV rv = CKR_OK;
2N/A CK_ATTRIBUTE stLabel;
2N/A
2N/A bzero(label, len);
2N/A
2N/A stLabel.type = CKA_LABEL;
2N/A stLabel.pValue = label;
2N/A stLabel.ulValueLen = len;
2N/A
2N/A /*
2N/A * The caller MUST provide a CKA_LABEL when deleting.
2N/A */
2N/A rv = kms_get_attribute(obj, &stLabel);
2N/A
2N/A return (rv);
2N/A}
2N/A
2N/A/*
2N/A * Retrieve a data unit associated with the label.
2N/A */
2N/Astatic CK_RV
2N/Akms_get_data_unit(kms_session_t *session, char *label,
2N/A KMSAgent_DataUnit *pDataUnit)
2N/A{
2N/A KMS_AGENT_STATUS status;
2N/A const utf8cstr pDescription = KMS_DATAUNIT_DESCRIPTION;
2N/A uchar_t externalUniqueId[SHA256_DIGEST_LENGTH];
2N/A
2N/A /* Find the data unit that holds the key */
2N/A kms_hash_string(label, externalUniqueId);
2N/A
2N/A status = KMSAgent_RetrieveDataUnitByExternalUniqueID(
2N/A &session->kmsProfile,
2N/A (const unsigned char *)externalUniqueId,
2N/A sizeof (externalUniqueId),
2N/A label,
2N/A pDescription,
2N/A pDataUnit);
2N/A
2N/A if (status != KMS_AGENT_STATUS_OK) {
2N/A return (GetPKCS11StatusFromAgentStatus(status));
2N/A }
2N/A
2N/A return (CKR_OK);
2N/A}
2N/A
2N/Astatic CK_RV
2N/Akms_decode_description(char *description, kms_object_t *pKey)
2N/A{
2N/A CK_RV rv = CKR_OK;
2N/A char *ptr;
2N/A uint32_t keylen;
2N/A u_longlong_t boolattrs;
2N/A
2N/A /* If it doesn't start with the expected prefix, return */
2N/A if (strncmp(description, KMS_ATTR_DESC_PFX,
2N/A strlen(KMS_ATTR_DESC_PFX)))
2N/A return (rv);
2N/A
2N/A ptr = description + strlen(KMS_ATTR_DESC_PFX);
2N/A
2N/A /*
2N/A * Decode as follows:
2N/A * CK_OBJECT_CLASS (2 bytes)
2N/A * CK_KEY_TYPE (2 bytes)
2N/A * CKA_VALUE_LEN (4 bytes)
2N/A * CK_CERTIFICATE_TYPE (2 bytes - not used)
2N/A * CK_MECHANISM_TYPE (4 bytes)
2N/A * boolean attributes (3 bytes)
2N/A * extra attributes (1 byte)
2N/A * non-boolean attributes
2N/A */
2N/A if (sscanf(ptr,
2N/A "%02lx%02lx%02x00%04lx%06llx00",
2N/A &pKey->class,
2N/A &pKey->key_type,
2N/A &keylen,
2N/A &pKey->mechanism,
2N/A &boolattrs) != 5)
2N/A /* We didn't get the full set of attributes */
2N/A rv = CKR_ATTRIBUTE_TYPE_INVALID;
2N/A pKey->bool_attr_mask = boolattrs;
2N/A
2N/A return (rv);
2N/A}
2N/A
2N/A/*
2N/A * Create a new PKCS#11 object record for the KMSAgent_Key.
2N/A */
2N/Astatic CK_RV
2N/Akms_new_key_object(
2N/A char *label,
2N/A KMSAgent_DataUnit *dataUnit,
2N/A KMSAgent_Key *pKey,
2N/A kms_object_t **pObj)
2N/A{
2N/A CK_RV rv = CKR_OK;
2N/A CK_BBOOL bTrue = B_TRUE;
2N/A CK_BBOOL bFalse = B_FALSE;
2N/A CK_KEY_TYPE keytype = CKK_AES;
2N/A CK_OBJECT_CLASS class = CKO_SECRET_KEY;
2N/A CK_ULONG keylen;
2N/A kms_object_t *newObj;
2N/A
2N/A CK_ATTRIBUTE template[] = {
2N/A {CKA_TOKEN, NULL, sizeof (bTrue)}, /* 0 */
2N/A {CKA_LABEL, NULL, 0}, /* 1 */
2N/A {CKA_KEY_TYPE, NULL, sizeof (keytype)}, /* 2 */
2N/A {CKA_CLASS, NULL, sizeof (class)}, /* 3 */
2N/A {CKA_VALUE, NULL, NULL}, /* 4 */
2N/A {CKA_VALUE_LEN, NULL, NULL}, /* 5 */
2N/A {CKA_PRIVATE, NULL, sizeof (bTrue)}, /* 6 */
2N/A {CKA_ENCRYPT, NULL, sizeof (bTrue)}, /* 7 */
2N/A {CKA_DECRYPT, NULL, sizeof (bTrue)}, /* 8 */
2N/A };
2N/A
2N/A keylen = (CK_ULONG)pKey->m_iKeyLength;
2N/A
2N/A template[0].pValue = &bTrue;
2N/A template[1].pValue = label;
2N/A template[1].ulValueLen = strlen(label);
2N/A template[2].pValue = &keytype;
2N/A template[3].pValue = &class;
2N/A template[4].pValue = pKey->m_acKey;
2N/A template[4].ulValueLen = pKey->m_iKeyLength;
2N/A template[5].pValue = &keylen;
2N/A template[5].ulValueLen = sizeof (keylen);
2N/A template[6].pValue = &bTrue;
2N/A if (pKey->m_iKeyState == KMS_KEY_STATE_ACTIVE_PROTECT_AND_PROCESS) {
2N/A template[7].pValue = &bTrue; /* CKA_ENCRYPT */
2N/A template[8].pValue = &bTrue; /* CKA_DECRYPT */
2N/A } else { /* Can only be one of the decrypt-only states. */
2N/A template[7].pValue = &bFalse; /* !CKA_ENCRYPT */
2N/A template[8].pValue = &bTrue; /* CKA_DECRYPT */
2N/A }
2N/A
2N/A newObj = kms_new_object();
2N/A if (newObj == NULL)
2N/A return (CKR_HOST_MEMORY);
2N/A
2N/A /*
2N/A * Decode the DataUnit description field to find various
2N/A * object attributes.
2N/A */
2N/A rv = kms_decode_description(dataUnit->m_acDescription, newObj);
2N/A if (rv) {
2N/A free(newObj);
2N/A return (rv);
2N/A }
2N/A /*
2N/A * Set the template keytype and class according to the
2N/A * data parsed from the description.
2N/A */
2N/A if (newObj->key_type)
2N/A keytype = newObj->key_type;
2N/A if (newObj->class)
2N/A class = newObj->class;
2N/A
2N/A rv = kms_build_object(template, 9, newObj);
2N/A if (rv) {
2N/A free(newObj);
2N/A return (rv);
2N/A }
2N/A
2N/A newObj->bool_attr_mask |= TOKEN_BOOL_ON;
2N/A
2N/A *pObj = newObj;
2N/A return (rv);
2N/A}
2N/A
2N/Astatic CK_RV
2N/Akms_get_data_unit_keys(kms_session_t *sp, KMSAgent_DataUnit *dataUnit,
2N/A KMSAgent_ArrayOfKeys **keylist, int *numkeys)
2N/A{
2N/A CK_RV rv = CKR_OK;
2N/A KMSAgent_ArrayOfKeys *kmskeys = NULL;
2N/A KMS_AGENT_STATUS status;
2N/A int keysLeft = 0;
2N/A
2N/A status = KMSAgent_RetrieveDataUnitKeys(
2N/A &sp->kmsProfile, dataUnit,
2N/A KMS_MAX_PAGE_SIZE, 0,
2N/A (int * const)&keysLeft,
2N/A NULL, /* KeyID */
2N/A &kmskeys);
2N/A
2N/A if (status != KMS_AGENT_STATUS_OK) {
2N/A return (GetPKCS11StatusFromAgentStatus(status));
2N/A }
2N/A
2N/A if (keylist != NULL && kmskeys != NULL)
2N/A *keylist = kmskeys;
2N/A
2N/A if (numkeys != NULL && kmskeys != NULL)
2N/A *numkeys = kmskeys->m_iSize;
2N/A
2N/A if (keylist == NULL && kmskeys != NULL)
2N/A KMSAgent_FreeArrayOfKeys(kmskeys);
2N/A
2N/A return (rv);
2N/A}
2N/A
2N/A/*
2N/A * Retrieve a key from KMS. We can't use "RetrieveKey" because
2N/A * we don't know the key id. Instead get all keys associated
2N/A * with our data unit (there should be only 1.
2N/A */
2N/ACK_RV
2N/AKMS_RetrieveKeyObj(kms_session_t *sp, char *label, kms_object_t **pobj)
2N/A{
2N/A CK_RV rv = CKR_OK;
2N/A KMSAgent_DataUnit dataUnit;
2N/A KMSAgent_ArrayOfKeys *kmsKeys = NULL;
2N/A KMSAgent_Key *pKey;
2N/A
2N/A rv = kms_get_data_unit(sp, label, &dataUnit);
2N/A if (rv != CKR_OK)
2N/A return (rv);
2N/A
2N/A if (dataUnit.m_iDataUnitState == KMS_DATA_UNIT_STATE_NO_KEY ||
2N/A dataUnit.m_iDataUnitState == KMS_DATA_UNIT_STATE_SHREDDED) {
2N/A /* No keys available. CKR_OK is still valid status */
2N/A *pobj = NULL;
2N/A return (CKR_OK);
2N/A }
2N/A rv = kms_get_data_unit_keys(sp, &dataUnit, &kmsKeys, NULL);
2N/A if (rv != CKR_OK)
2N/A return (rv);
2N/A
2N/A /* No keys is a valid response */
2N/A if (kmsKeys == NULL || kmsKeys->m_iSize == 0) {
2N/A *pobj = NULL;
2N/A return (CKR_OK);
2N/A }
2N/A
2N/A pKey = &kmsKeys->m_pKeys[0];
2N/A
2N/A /*
2N/A * Make sure the key is in a useable state.
2N/A */
2N/A if (pKey->m_iKeyState == KMS_KEY_STATE_ACTIVE_PROTECT_AND_PROCESS ||
2N/A pKey->m_iKeyState == KMS_KEY_STATE_ACTIVE_PROCESS_ONLY ||
2N/A pKey->m_iKeyState == KMS_KEY_STATE_DEACTIVATED ||
2N/A pKey->m_iKeyState == KMS_KEY_STATE_COMPROMISED) {
2N/A rv = kms_new_key_object(label, &dataUnit, pKey, pobj);
2N/A } else {
2N/A *pobj = NULL;
2N/A rv = CKR_KEY_HANDLE_INVALID;
2N/A }
2N/A
2N/A KMSAgent_FreeArrayOfKeys(kmsKeys);
2N/A return (rv);
2N/A}
2N/A
2N/ACK_RV
2N/AKMS_RefreshObjectList(kms_session_t *sp, kms_slot_t *pslot)
2N/A{
2N/A kms_object_t *pObj;
2N/A char label[BUFSIZ];
2N/A CK_RV rv;
2N/A objlabel_t *node;
2N/A
2N/A rv = kms_reload_labels(sp);
2N/A if (rv != CKR_OK)
2N/A return (rv);
2N/A
2N/A /*
2N/A * If an object is not in the list, reload it from KMS.
2N/A */
2N/A node = avl_first(&sp->objlabel_tree);
2N/A while (node != NULL) {
2N/A boolean_t found = FALSE;
2N/A /* Search object list for matching object */
2N/A pObj = pslot->sl_tobj_list;
2N/A
2N/A /* Skip nodes that are marked for deletion */
2N/A if (node->flags & DELETE_FLAG) {
2N/A node = AVL_NEXT(&sp->objlabel_tree, node);
2N/A continue;
2N/A }
2N/A
2N/A while (pObj != NULL && !found) {
2N/A (void) pthread_mutex_lock(&pObj->object_mutex);
2N/A if ((rv = kms_get_object_label(pObj, label,
2N/A sizeof (label))) != CKR_OK) {
2N/A (void) pthread_mutex_unlock(
2N/A &pObj->object_mutex);
2N/A return (rv);
2N/A }
2N/A (void) pthread_mutex_unlock(&pObj->object_mutex);
2N/A found = (strcmp(label, node->label) == 0);
2N/A pObj = pObj->next;
2N/A }
2N/A if (!found) {
2N/A /*
2N/A * Fetch KMS key and prepend it to the
2N/A * token object list for the slot.
2N/A */
2N/A rv = KMS_RetrieveKeyObj(sp, node->label, &pObj);
2N/A if (rv == CKR_OK && pObj != NULL) {
2N/A if (pslot->sl_tobj_list == NULL) {
2N/A pslot->sl_tobj_list = pObj;
2N/A pObj->prev = NULL;
2N/A pObj->next = NULL;
2N/A } else {
2N/A pObj->next = pslot->sl_tobj_list;
2N/A pObj->prev = NULL;
2N/A pslot->sl_tobj_list = pObj;
2N/A }
2N/A }
2N/A }
2N/A node = AVL_NEXT(&sp->objlabel_tree, node);
2N/A }
2N/A return (rv);
2N/A}
2N/A
2N/ACK_RV
2N/AKMS_Initialize(void)
2N/A{
2N/A char *ksdir;
2N/A struct stat fn_stat;
2N/A KMS_AGENT_STATUS kmsrv;
2N/A
2N/A ksdir = kms_get_keystore_path();
2N/A if (ksdir == NULL)
2N/A return (CKR_GENERAL_ERROR);
2N/A
2N/A /*
2N/A * If the keystore directory doesn't exist, create it.
2N/A */
2N/A if ((stat(ksdir, &fn_stat) != 0) && (errno == ENOENT)) {
2N/A if (mkdir(ksdir, S_IRUSR|S_IWUSR|S_IXUSR) < 0) {
2N/A if (errno != EEXIST)
2N/A return (CKR_GENERAL_ERROR);
2N/A }
2N/A }
2N/A
2N/A if ((kmsrv = KMSAgent_InitializeLibrary(ksdir, FALSE)) !=
2N/A KMS_AGENT_STATUS_OK) {
2N/A return (GetPKCS11StatusFromAgentStatus(kmsrv));
2N/A }
2N/A
2N/A initialized = B_TRUE;
2N/A return (CKR_OK);
2N/A}
2N/A
2N/ACK_RV
2N/AKMS_Finalize()
2N/A{
2N/A if (!initialized)
2N/A return (CKR_OK);
2N/A
2N/A last_objlist_mtime = 0;
2N/A if ((KMSAgent_FinalizeLibrary() == KMS_AGENT_STATUS_OK)) {
2N/A initialized = B_FALSE;
2N/A return (CKR_OK);
2N/A }
2N/A return (CKR_FUNCTION_FAILED);
2N/A}
2N/A
2N/ACK_RV
2N/AKMS_ChangeLocalPWD(kms_session_t *session,
2N/A const char *pOldPassword,
2N/A const char *pNewPassword)
2N/A{
2N/A KMS_AGENT_STATUS status;
2N/A
2N/A status = KMSAgent_ChangeLocalPWD(
2N/A &session->kmsProfile,
2N/A (char * const)pOldPassword,
2N/A (char * const)pNewPassword);
2N/A
2N/A return (GetPKCS11StatusFromAgentStatus(status));
2N/A}
2N/A
2N/ACK_RV
2N/AKMS_GetConfigInfo(kms_cfg_info_t *cfginfo)
2N/A{
2N/A CK_RV rv = CKR_OK;
2N/A char cfgfile_path[BUFSIZ];
2N/A char *ksdir = kms_get_keystore_path();
2N/A
2N/A if (ksdir == NULL)
2N/A return (CKR_GENERAL_ERROR);
2N/A
2N/A (void) snprintf(cfgfile_path, sizeof (cfgfile_path),
2N/A "%s/%s", ksdir, KMSTOKEN_CONFIG_FILENAME);
2N/A
2N/A rv = kms_read_config_data(cfgfile_path, cfginfo);
2N/A
2N/A return (rv);
2N/A}
2N/A
2N/ACK_RV
2N/AKMS_LoadProfile(KMSClientProfile *profile,
2N/A kms_cfg_info_t *kmscfg,
2N/A const char *pPassword,
2N/A size_t iPasswordLength)
2N/A{
2N/A KMS_AGENT_STATUS status;
2N/A CK_RV rv;
2N/A char *sPassword;
2N/A char cfgfile_path[BUFSIZ];
2N/A char *ksdir;
2N/A
2N/A sPassword = calloc(1, iPasswordLength + 1);
2N/A if (sPassword == NULL)
2N/A return (CKR_FUNCTION_FAILED);
2N/A
2N/A (void) memcpy(sPassword, pPassword, iPasswordLength);
2N/A
2N/A ksdir = kms_get_keystore_path();
2N/A if (ksdir == NULL) {
2N/A free(sPassword);
2N/A return (CKR_GENERAL_ERROR);
2N/A }
2N/A
2N/A (void) snprintf(cfgfile_path, sizeof (cfgfile_path),
2N/A "%s/%s", ksdir, KMSTOKEN_CONFIG_FILENAME);
2N/A
2N/A if ((rv = kms_read_config_data(cfgfile_path, kmscfg))) {
2N/A free(sPassword);
2N/A return (rv);
2N/A }
2N/A
2N/A /* First, try to load existing profile */
2N/A status = KMSAgent_LoadProfile(
2N/A profile,
2N/A kmscfg->name,
2N/A kmscfg->agentId,
2N/A sPassword,
2N/A kmscfg->agentAddr,
2N/A kmscfg->transTimeout,
2N/A kmscfg->failoverLimit,
2N/A kmscfg->discoveryFreq,
2N/A kmscfg->securityMode);
2N/A
2N/A free(sPassword);
2N/A return (GetPKCS11StatusFromAgentStatus(status));
2N/A}
2N/A
2N/Astatic CK_RV
2N/AGetPKCS11StatusFromAgentStatus(KMS_AGENT_STATUS status)
2N/A{
2N/A switch (status) {
2N/A case KMS_AGENT_STATUS_OK:
2N/A return (CKR_OK);
2N/A
2N/A
2N/A case KMS_AGENT_STATUS_NO_MEMORY:
2N/A return (CKR_HOST_MEMORY);
2N/A
2N/A case KMS_AGENT_STATUS_INVALID_PARAMETER:
2N/A return (CKR_ARGUMENTS_BAD);
2N/A
2N/A case KMS_AGENT_STATUS_PROFILE_NOT_LOADED:
2N/A return (CKR_CRYPTOKI_NOT_INITIALIZED);
2N/A
2N/A case KMS_AGENT_STATUS_PROFILE_ALREADY_LOADED:
2N/A return (CKR_USER_ANOTHER_ALREADY_LOGGED_IN);
2N/A
2N/A case KMS_AGENT_STATUS_FIPS_KAT_AES_KEYWRAP_ERROR:
2N/A case KMS_AGENT_STATUS_FIPS_KAT_AES_ECB_ERROR:
2N/A case KMS_AGENT_STATUS_FIPS_KAT_HMAC_SHA1_ERROR:
2N/A return (CKR_DEVICE_ERROR);
2N/A
2N/A case KMS_AGENT_STATUS_ACCESS_DENIED:
2N/A case KMS_AGENT_LOCAL_AUTH_FAILURE:
2N/A return (CKR_PIN_INCORRECT);
2N/A
2N/A case KMS_AGENT_STATUS_GENERIC_ERROR:
2N/A case KMS_AGENT_STATUS_KMS_NO_READY_KEYS:
2N/A case KMS_AGENT_STATUS_KMS_UNAVAILABLE:
2N/A case KMS_AGENT_STATUS_NO_FIPS_KMAS_AVAILABLE:
2N/A case KMS_AGENT_STATUS_SERVER_BUSY:
2N/A case KMS_AGENT_STATUS_EXTERNAL_UNIQUE_ID_EXISTS:
2N/A case KMS_AGENT_STATUS_DATA_UNIT_ID_NOT_FOUND_EXTERNAL_ID_EXISTS:
2N/A case KMS_AGENT_STATUS_KEY_DOES_NOT_EXIST:
2N/A case KMS_AGENT_STATUS_KEY_DESTROYED:
2N/A case KMS_AGENT_AES_KEY_UNWRAP_ERROR:
2N/A case KMS_AGENT_AES_KEY_WRAP_SETUP_ERROR:
2N/A case KMS_AGENT_STATUS_KEY_CALLOUT_FAILURE:
2N/A default:
2N/A return (CKR_GENERAL_ERROR);
2N/A }
2N/A}
2N/A
2N/Avoid
2N/AKMS_UnloadProfile(KMSClientProfile *kmsProfile)
2N/A{
2N/A (void) KMSAgent_UnloadProfile(kmsProfile);
2N/A}
2N/A
2N/A/*
2N/A * kms_update_label_file
2N/A *
2N/A * KMS doesn't provide an API to allow one to query for available
2N/A * data units (which map 1-1 to keys). To allow for PKCS11 to
2N/A * query for a list of available objects, we keep a local list
2N/A * and update it when an object is added or deleted.
2N/A */
2N/ACK_RV
2N/Akms_update_label_file(kms_session_t *sp)
2N/A{
2N/A CK_RV rv = CKR_OK;
2N/A objlabel_t *node;
2N/A char *ksdir, labelfile[BUFSIZ];
2N/A FILE *fp;
2N/A int fd;
2N/A struct stat statp;
2N/A
2N/A ksdir = kms_get_keystore_path();
2N/A if (ksdir == NULL)
2N/A return (CKR_GENERAL_ERROR);
2N/A
2N/A (void) snprintf(labelfile, sizeof (labelfile),
2N/A "%s/%s", ksdir, KMSTOKEN_LABELLIST_FILENAME);
2N/A
2N/A /* Open the label file with a WRITE lock, but for READ/WRITE access */
2N/A fd = open_and_lock_file(labelfile, F_WRLCK, O_RDWR | O_CREAT,
2N/A &flock_mutex);
2N/A if (fd == -1)
2N/A return (CKR_GENERAL_ERROR);
2N/A
2N/A if (fstat(fd, &statp) == -1) {
2N/A (void) flock_fd(fd, F_UNLCK, &flock_mutex);
2N/A (void) close(fd);
2N/A return (CKR_GENERAL_ERROR);
2N/A }
2N/A if (statp.st_size > 0 && last_objlist_mtime != statp.st_mtime) {
2N/A /* Re-read the label file and merge the list. */
2N/A char *buf = malloc(statp.st_size);
2N/A int bytes;
2N/A if (buf == NULL) {
2N/A (void) flock_fd(fd, F_UNLCK, &flock_mutex);
2N/A (void) close(fd);
2N/A return (CKR_GENERAL_ERROR);
2N/A }
2N/A bytes = kms_slurp_fd(fd, buf, statp.st_size);
2N/A
2N/A /* merge the label list */
2N/A kms_parse_labels(&sp->objlabel_tree, buf, bytes);
2N/A free(buf);
2N/A }
2N/A /* Truncate the file in preparation for updating it */
2N/A if (ftruncate(fd, 0) == -1) {
2N/A (void) flock_fd(fd, F_UNLCK, &flock_mutex);
2N/A (void) close(fd);
2N/A return (CKR_GENERAL_ERROR);
2N/A }
2N/A (void) lseek(fd, 0, SEEK_SET);
2N/A
2N/A fp = fdopen(fd, "w+");
2N/A if (fp == NULL) {
2N/A (void) flock_fd(fd, F_UNLCK, &flock_mutex);
2N/A (void) close(fd);
2N/A return (CKR_GENERAL_ERROR);
2N/A }
2N/A
2N/A node = avl_first(&sp->objlabel_tree);
2N/A while (node != NULL) {
2N/A /* remove nodes marked for deletion */
2N/A if (node->flags & DELETE_FLAG) {
2N/A objlabel_t *tnode = node;
2N/A
2N/A /* get the next one */
2N/A node = AVL_NEXT(&sp->objlabel_tree, node);
2N/A
2N/A /* remove and free the old node data */
2N/A avl_remove(&sp->objlabel_tree, tnode);
2N/A free(tnode->label);
2N/A free(tnode);
2N/A continue;
2N/A } else if (node->label != NULL)
2N/A (void) fprintf(fp, "%s\n", node->label);
2N/A node = AVL_NEXT(&sp->objlabel_tree, node);
2N/A }
2N/A
2N/A /* Update the last mtime */
2N/A (void) fflush(fp);
2N/A if (fstat(fd, &statp) == 0)
2N/A last_objlist_mtime = statp.st_mtime;
2N/A
2N/A (void) flock_fd(fd, F_UNLCK, &flock_mutex);
2N/A (void) fclose(fp);
2N/A
2N/A return (rv);
2N/A}
2N/A
2N/A/*
2N/A * Destroy a key in the KMS by disassociating an entire data unit.
2N/A * The KMSAgent API does not have an interface for destroying an
2N/A * individual key.
2N/A */
2N/ACK_RV
2N/AKMS_DestroyKey(kms_session_t *session, kms_object_t *i_oKey)
2N/A{
2N/A CK_RV rv;
2N/A KMSAgent_DataUnit oDataUnit;
2N/A KMS_AGENT_STATUS status;
2N/A char label[BUFSIZ];
2N/A objlabel_t labelnode, *tnode;
2N/A avl_index_t where = 0;
2N/A
2N/A /*
2N/A * The caller MUST provide a CKA_LABEL when deleting.
2N/A */
2N/A (void) pthread_mutex_lock(&i_oKey->object_mutex);
2N/A if ((rv = kms_get_object_label(i_oKey, label, sizeof (label)))) {
2N/A (void) pthread_mutex_unlock(&i_oKey->object_mutex);
2N/A return (rv);
2N/A }
2N/A
2N/A rv = kms_get_data_unit(session, label, &oDataUnit);
2N/A if (rv != CKR_OK)
2N/A return (rv);
2N/A
2N/A status = KMSAgent_DisassociateDataUnitKeys(
2N/A &session->kmsProfile, &oDataUnit);
2N/A
2N/A /*
2N/A * Remove the label from the label list and update
2N/A * the file that tracks active keys.
2N/A */
2N/A bzero(&labelnode, sizeof (labelnode));
2N/A labelnode.label = label;
2N/A
2N/A if ((tnode = avl_find(&session->objlabel_tree,
2N/A &labelnode, &where)) != NULL)
2N/A /* Mark it for deletion */
2N/A tnode->flags |= DELETE_FLAG;
2N/A
2N/A /* rewrite the list of labels to disk */
2N/A rv = kms_update_label_file(session);
2N/A if (rv)
2N/A /* Ignore error here */
2N/A rv = CKR_OK;
2N/A
2N/A (void) pthread_mutex_unlock(&i_oKey->object_mutex);
2N/A
2N/A return (GetPKCS11StatusFromAgentStatus(status));
2N/A}
2N/A
2N/Avoid
2N/Akms_encode_attributes(kms_object_t *pKey, char *attrstr, int len)
2N/A{
2N/A char *ptr;
2N/A
2N/A bzero(attrstr, len);
2N/A
2N/A (void) strlcpy(attrstr, KMS_ATTR_DESC_PFX, len);
2N/A ptr = attrstr + strlen(attrstr);
2N/A
2N/A /*
2N/A * Encode as follows:
2N/A * CK_OBJECT_CLASS (2 bytes)
2N/A * CK_KEY_TYPE (2 bytes)
2N/A * CKA_VALUE_LEN (4 bytes)
2N/A * CK_CERTIFICATE_TYPE (2 bytes - not used)
2N/A * CK_MECHANISM_TYPE (4 bytes)
2N/A * boolean attributes (3 bytes)
2N/A * extra attributes (1 byte)
2N/A * non-boolean attributes
2N/A */
2N/A (void) snprintf(ptr, len - strlen(attrstr),
2N/A "%02lx%02lx%02x00%04lx%06x00",
2N/A pKey->class,
2N/A pKey->key_type,
2N/A 32,
2N/A pKey->mechanism,
2N/A (uint32_t)(pKey->bool_attr_mask & 0x00FFFFFF));
2N/A}
2N/A
2N/ACK_RV
2N/AKMS_GenerateKey(kms_session_t *session, kms_object_t *i_oKey)
2N/A{
2N/A CK_RV rv;
2N/A CK_ATTRIBUTE stLabel;
2N/A KMSAgent_DataUnit oDataUnit;
2N/A KMSAgent_Key oKey;
2N/A KMS_AGENT_STATUS status;
2N/A char label[128];
2N/A uchar_t externalUniqueId[SHA256_DIGEST_LENGTH];
2N/A char pDescription[KMS_MAX_DESCRIPTION + 1];
2N/A
2N/A (void) pthread_mutex_lock(&i_oKey->object_mutex);
2N/A
2N/A stLabel.type = CKA_LABEL;
2N/A stLabel.pValue = label;
2N/A stLabel.ulValueLen = sizeof (label);
2N/A
2N/A /*
2N/A * The caller MUST provide a CKA_LABEL for storing in the KMS.
2N/A */
2N/A if ((rv = kms_get_attribute(i_oKey, &stLabel)) != CKR_OK) {
2N/A (void) pthread_mutex_unlock(&i_oKey->object_mutex);
2N/A return (rv);
2N/A }
2N/A
2N/A label[stLabel.ulValueLen] = '\0';
2N/A
2N/A kms_hash_string(label, externalUniqueId);
2N/A
2N/A /* Encode attributes in Description */
2N/A kms_encode_attributes(i_oKey, pDescription,
2N/A sizeof (pDescription));
2N/A
2N/A status = KMSAgent_CreateDataUnit(
2N/A &session->kmsProfile,
2N/A (const unsigned char *)externalUniqueId,
2N/A sizeof (externalUniqueId),
2N/A label, /* externalTag */
2N/A pDescription,
2N/A &oDataUnit);
2N/A
2N/A /*
2N/A * If the DataUnit exists, check to see if it has any keys.
2N/A * If it has no keys, then it is OK to continue.
2N/A */
2N/A if (status == KMS_AGENT_STATUS_EXTERNAL_UNIQUE_ID_EXISTS) {
2N/A int numkeys = 0;
2N/A
2N/A rv = kms_get_data_unit(session, label, &oDataUnit);
2N/A if (rv != CKR_OK)
2N/A return (rv);
2N/A
2N/A rv = kms_get_data_unit_keys(session,
2N/A &oDataUnit, NULL, &numkeys);
2N/A
2N/A if (rv != CKR_OK || numkeys > 0)
2N/A /*
2N/A * This would be better if there were PKCS#11
2N/A * error codes for duplicate objects or
2N/A * something like that.
2N/A */
2N/A return (CKR_ARGUMENTS_BAD);
2N/A
2N/A /* If no keys associated with data unit, continue */
2N/A status = KMS_AGENT_STATUS_OK;
2N/A }
2N/A
2N/A if (status != KMS_AGENT_STATUS_OK) {
2N/A (void) pthread_mutex_unlock(&i_oKey->object_mutex);
2N/A return (GetPKCS11StatusFromAgentStatus(status));
2N/A }
2N/A
2N/A status = KMSAgent_CreateKey(&session->kmsProfile,
2N/A &oDataUnit, "", &oKey);
2N/A
2N/A if (status != KMS_AGENT_STATUS_OK) {
2N/A /*
2N/A * Clean up the old data unit.
2N/A */
2N/A (void) pthread_mutex_unlock(&i_oKey->object_mutex);
2N/A return (GetPKCS11StatusFromAgentStatus(status));
2N/A }
2N/A
2N/A /*
2N/A * KMS Agent only creates AES-256 keys, so ignore what the user
2N/A * requested at this point.
2N/A */
2N/A OBJ_SEC_VALUE(i_oKey) = malloc(oKey.m_iKeyLength);
2N/A if (OBJ_SEC_VALUE(i_oKey) == NULL) {
2N/A (void) pthread_mutex_unlock(&i_oKey->object_mutex);
2N/A return (CKR_HOST_MEMORY);
2N/A }
2N/A (void) memcpy(OBJ_SEC_VALUE(i_oKey), oKey.m_acKey,
2N/A oKey.m_iKeyLength);
2N/A OBJ_SEC_VALUE_LEN(i_oKey) = oKey.m_iKeyLength;
2N/A
2N/A /*
2N/A * Add the label to the local list of available objects
2N/A */
2N/A add_label_node(&session->objlabel_tree, label);
2N/A
2N/A rv = kms_update_label_file(session);
2N/A
2N/A (void) pthread_mutex_unlock(&i_oKey->object_mutex);
2N/A
2N/A return (GetPKCS11StatusFromAgentStatus(status));
2N/A}