Mercurial > illumos > illumos-gate
view usr/src/lib/pkcs11/pkcs11_kms/common/kmsKeystoreUtil.c @ 12720:3db6e0082404
PSARC 2010/195 PKCS11 KMS Provider
6944296 Solaris needs a PKCS#11 provider to allow access to KMS keystore functionality
author | Wyllys Ingersoll <Wyllys.Ingersoll@Sun.COM> |
---|---|
date | Mon, 28 Jun 2010 16:04:11 -0700 |
parents | |
children |
line wrap: on
line source
/* * 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)); }