mirror of
https://github.com/ibm-s390-linux/s390-tools.git
synced 2026-08-05 02:14:52 +00:00
List (find) and import a key that exists in a KMIP server into the zkey repository. Retrieve the key attributes of a key stored in a KMIP server, and extract the zkey properties from it. Signed-off-by: Ingo Franzki <ifranzki@linux.ibm.com> Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
6852 lines
201 KiB
C
6852 lines
201 KiB
C
/*
|
|
* zkey-kmip - KMIP zkey KMS plugin
|
|
*
|
|
* Copyright IBM Corp. 2021
|
|
*
|
|
* s390-tools is free software; you can redistribute it and/or modify
|
|
* it under the terms of the MIT license. See LICENSE for details.
|
|
*/
|
|
|
|
#include <ctype.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <dlfcn.h>
|
|
#include <stdarg.h>
|
|
#include <string.h>
|
|
#include <errno.h>
|
|
#include <err.h>
|
|
#include <fnmatch.h>
|
|
#include <sys/utsname.h>
|
|
|
|
#include <openssl/objects.h>
|
|
|
|
#include "lib/zt_common.h"
|
|
#include "lib/util_libc.h"
|
|
#include "lib/util_panic.h"
|
|
#include "lib/util_path.h"
|
|
#include "lib/util_base.h"
|
|
|
|
#include "zkey-kmip.h"
|
|
#include "../kms-plugin.h"
|
|
#include "../cca.h"
|
|
#include "../ep11.h"
|
|
#include "../utils.h"
|
|
#include "../pkey.h"
|
|
#include "../properties.h"
|
|
|
|
#include "libseckey/sk_utilities.h"
|
|
#include "libseckey/sk_ep11.h"
|
|
|
|
#if OPENSSL_VERSION_PREREQ(3, 0)
|
|
#include <openssl/core_names.h>
|
|
#endif
|
|
|
|
#define _set_error(ph, fmt...) plugin_set_error(&(ph)->pd, fmt)
|
|
|
|
typedef void (*t_CSNDSYI)(long *return_code,
|
|
long *reason_code,
|
|
long *exit_data_length,
|
|
unsigned char *exit_data,
|
|
long *rule_array_count,
|
|
unsigned char *rule_array,
|
|
long *RSA_enciphered_key_length,
|
|
unsigned char *RSA_enciphered_key,
|
|
long *RSA_private_key_identifier_length,
|
|
unsigned char *RSA_private_key_identifier,
|
|
long *target_key_identifier_length,
|
|
unsigned char *target_key_identifier);
|
|
|
|
typedef CK_RV (*m_UnwrapKey_t)(const CK_BYTE_PTR wrapped, CK_ULONG wlen,
|
|
const unsigned char *kek, size_t keklen,
|
|
const unsigned char *mackey,
|
|
size_t mklen, const unsigned char *pin,
|
|
size_t pinlen,
|
|
const CK_MECHANISM_PTR uwmech,
|
|
const CK_ATTRIBUTE_PTR ptempl,
|
|
CK_ULONG pcount,
|
|
unsigned char *unwrapped, size_t *uwlen,
|
|
CK_BYTE_PTR csum, CK_ULONG * cslen,
|
|
target_t target);
|
|
|
|
#define CKO_SECRET_KEY 0x00000004
|
|
#define CKK_AES 0x0000001F
|
|
#define CKA_IBM_PROTKEY_EXTRACTABLE 0x8001000C
|
|
|
|
#define KMS_KEY_PROP_DESCRIPTION "description"
|
|
#define KMS_KEY_PROP_XTS_KEY1_ID "xts-key1-id"
|
|
#define KMS_KEY_PROP_XTS_KEY2_ID "xts-key2-id"
|
|
|
|
#define KMIP_KEY_TYPE_ANY "(any)"
|
|
|
|
#define FREE_AND_SET_NULL(ptr) \
|
|
do { \
|
|
if ((ptr) != NULL) \
|
|
free((void *)ptr); \
|
|
(ptr) = NULL; \
|
|
} while (0)
|
|
|
|
#define CHECK_ERROR(cond, rc_var, rc, text, ph, label) \
|
|
do { \
|
|
if (cond) { \
|
|
(rc_var) = (rc); \
|
|
pr_verbose((&ph->pd), "%s: %s", (text), \
|
|
strerror(-(rc_var))); \
|
|
_set_error((ph), "%s: %s", (text), \
|
|
strerror(-(rc_var))); \
|
|
goto label; \
|
|
} \
|
|
} while (0)
|
|
|
|
struct kmip_enum_name {
|
|
uint32_t value;
|
|
const char *name;
|
|
};
|
|
|
|
static const struct kmip_enum_name required_operations[] = {
|
|
{ .value = KMIP_OPERATION_QUERY, .name = "Query" },
|
|
{ .value = KMIP_OPERATION_CREATE, .name = "Create" },
|
|
{ .value = KMIP_OPERATION_REGISTER, .name = "Register" },
|
|
{ .value = KMIP_OPERATION_ACTIVATE, .name = "Activate" },
|
|
{ .value = KMIP_OPERATION_REVOKE, .name = "Revoke" },
|
|
{ .value = KMIP_OPERATION_DESTROY, .name = "Destroy" },
|
|
{ .value = KMIP_OPERATION_GET, .name = "Get" },
|
|
{ .value = KMIP_OPERATION_LOCATE, .name = "Locate" },
|
|
{ .value = KMIP_OPERATION_GET_ATTRIBUTE_LIST,
|
|
.name = "Get Attribute List" },
|
|
{ .value = KMIP_OPERATION_GET_ATTRIBUTES,
|
|
.name = "Get Attributes" },
|
|
{ .value = KMIP_OPERATION_ADD_ATTRIBUTE,
|
|
.name = "Add Attribute" },
|
|
{ .value = KMIP_OPERATION_DELETE_ATTRIBUTE,
|
|
.name = "Delete Attribute" },
|
|
{ .value = 0, .name = NULL },
|
|
};
|
|
|
|
static const struct kmip_enum_name required_objtypes[] = {
|
|
{ .value = KMIP_OBJECT_TYPE_SYMMETRIC_KEY, .name = "Symmetric Key" },
|
|
{ .value = KMIP_OBJECT_TYPE_PUBLIC_KEY, .name = "Public Key" },
|
|
{ .value = 0, .name = NULL },
|
|
};
|
|
|
|
static const struct kmip_version kmip_version_1_0 = {
|
|
.major = 1, .minor = 0,
|
|
};
|
|
|
|
static const struct kmip_version kmip_version_1_2 = {
|
|
.major = 1, .minor = 2,
|
|
};
|
|
|
|
static const struct kmip_enum_name kmip_result_statuses[] = {
|
|
{ .value = KMIP_RESULT_STATUS_SUCCESS, .name = "Success" },
|
|
{ .value = KMIP_RESULT_STATUS_OPERATION_FAILED,
|
|
.name = "Operation Failed" },
|
|
{ .value = KMIP_RESULT_STATUS_OPERATION_PENDING,
|
|
.name = "Operation Pending" },
|
|
{ .value = KMIP_RESULT_STATUS_OPERATION_UNDONE,
|
|
.name = "Operation Undone" },
|
|
{ .value = 0, .name = NULL },
|
|
};
|
|
|
|
static const struct kmip_enum_name kmip_result_reasons[] = {
|
|
{ .value = KMIP_RESULT_REASON_ITEM_NOT_FOUND,
|
|
.name = "Item Not Found" },
|
|
{ .value = KMIP_RESULT_REASON_RESPONSE_TOO_LARGE,
|
|
.name = "Response Too Large" },
|
|
{ .value = KMIP_RESULT_REASON_AUTH_NOT_SUCCESSFUL,
|
|
.name = "Authentication Not Successful" },
|
|
{ .value = KMIP_RESULT_REASON_INVALID_MESSAGE,
|
|
.name = "Invalid Message" },
|
|
{ .value = KMIP_RESULT_REASON_OPERATION_NOT_SUCCESSFUL,
|
|
.name = "Operation Not Supported" },
|
|
{ .value = KMIP_RESULT_REASON_MISSING_DATA, .name = "Missing Data" },
|
|
{ .value = KMIP_RESULT_REASON_INVALIUD_FIELD, .name = "Invalid Field" },
|
|
{ .value = KMIP_RESULT_REASON_FEATURE_NOT_SUPPORTED,
|
|
.name = "Feature Not Supported" },
|
|
{ .value = KMIP_RESULT_REASON_OP_CANCELED_BY_REQUESTOR,
|
|
.name = "Operation Canceled By Requeste" },
|
|
{ .value = KMIP_RESULT_REASON_CRYPTOGRAPHIC_FAILURE,
|
|
.name = "Cryptographic Failure" },
|
|
{ .value = KMIP_RESULT_REASON_ILLEGAL_OPERATION,
|
|
.name = "Illegal Operation" },
|
|
{ .value = KMIP_RESULT_REASON_PERMISSION_DENIED,
|
|
.name = "Permission Denied" },
|
|
{ .value = KMIP_RESULT_REASON_OBJECT_ARCHIVED,
|
|
.name = "Object Archived" },
|
|
{ .value = KMIP_RESULT_REASON_INDEX_OUT_OF_BOUNDS,
|
|
.name = "Index Out Of Bounds" },
|
|
{ .value = KMIP_RESULT_REASON_APP_NAMESPACE_NOT_SUPPORTED,
|
|
.name = "Application Namespace Not Supported" },
|
|
{ .value = KMIP_RESULT_REASON_KEY_FORMAT_TYPE_NOT_SUPPORTED,
|
|
.name = "Key Format Type Not Supported" },
|
|
{ .value = KMIP_RESULT_REASON_KEY_COMPRESSION_TYPE_NOT_SUPPORTED,
|
|
.name = "Key Compression Type Not Supported" },
|
|
{ .value = KMIP_RESULT_REASON_ENCODING_OPTION_ERROR,
|
|
.name = "Encoding Option Error" },
|
|
{ .value = KMIP_RESULT_REASON_KEY_VALUE_NOT_PRESENT,
|
|
.name = "Key Value Not Present" },
|
|
{ .value = KMIP_RESULT_REASON_ATTESTATION_REQUIRED,
|
|
.name = "Attestation Required" },
|
|
{ .value = KMIP_RESULT_REASON_ATTESTATION_FAILED,
|
|
.name = "Attestation Failed" },
|
|
{ .value = KMIP_RESULT_REASON_SENSITIVE, .name = "Sensitive" },
|
|
{ .value = KMIP_RESULT_REASON_NOT_EXTRACTABLE,
|
|
.name = "Not Extractable" },
|
|
{ .value = KMIP_RESULT_REASON_OBJECT_ALREADY_EXISTS,
|
|
.name = "Object Already Exists" },
|
|
{ .value = KMIP_RESULT_REASON_INVALID_TICKET,
|
|
.name = "Invalid Ticket" },
|
|
{ .value = KMIP_RESULT_REASON_USAGE_LIMIT_EXCEEDED,
|
|
.name = "Usage Limit Exceeded" },
|
|
{ .value = KMIP_RESULT_REASON_NUMERIC_RANGE, .name = "Numeric Range" },
|
|
{ .value = KMIP_RESULT_REASON_INVALID_DATA_TYPE,
|
|
.name = "Invalid Data Type" },
|
|
{ .value = KMIP_RESULT_REASON_READ_ONLY_ATTRIBUTE,
|
|
.name = "Read Only Attribute" },
|
|
{ .value = KMIP_RESULT_REASON_MULTI_VALUED_ATTRIBUTE,
|
|
.name = "Multi Valued Attribute" },
|
|
{ .value = KMIP_RESULT_REASON_UNSUPPORTED_ATTRIBUTE,
|
|
.name = "Unsupported Attribute" },
|
|
{ .value = KMIP_RESULT_REASON_ATTRIBUTE_INSTANCE_NOT_FOUND,
|
|
.name = "Attribute Instance Not Found" },
|
|
{ .value = KMIP_RESULT_REASON_ATTRIBUTE_NOT_FOUND,
|
|
.name = "Attribute Not Found" },
|
|
{ .value = KMIP_RESULT_REASON_ATTRIBUTE_READ_ONLY,
|
|
.name = "Attribute Read Only" },
|
|
{ .value = KMIP_RESULT_REASON_ATTRIBUTE_SINGLE_VALUED,
|
|
.name = "Attribute Single Valued" },
|
|
{ .value = KMIP_RESULT_REASON_BAD_CRYPTOGRAPHIC_PARAMETERS,
|
|
.name = "Bad Cryptographic Parameters" },
|
|
{ .value = KMIP_RESULT_REASON_BAD_PASSWORD, .name = "Bad Password" },
|
|
{ .value = KMIP_RESULT_REASON_CODEC_ERROR, .name = "Codec Error" },
|
|
{ .value = KMIP_RESULT_REASON_ILLEGAL_OBJECT_TYPE,
|
|
.name = "Illegal Object Type" },
|
|
{ .value = KMIP_RESULT_REASON_INCOMPATIBLE_CRYPTO_USAGE_MASK,
|
|
.name = "Incompatible Cryptographic Usage Mask" },
|
|
{ .value = KMIP_RESULT_REASON_INTERNAL_SERVER_ERROR,
|
|
.name = "Internal Server Error" },
|
|
{ .value = KMIP_RESULT_REASON_INVALID_ASYNC_CORRELATION_VALUE,
|
|
.name = "Invalid Asynchronous Correlation Value" },
|
|
{ .value = KMIP_RESULT_REASON_INVALID_ATTRIBUTE,
|
|
.name = "Invalid Attribute" },
|
|
{ .value = KMIP_RESULT_REASON_INVALID_ATTRIBUTE_VALUE,
|
|
.name = "Invalid Attribute Value" },
|
|
{ .value = KMIP_RESULT_REASON_INVALID_CORRELATION_VALUE,
|
|
.name = "Invalid Correlation Value" },
|
|
{ .value = KMIP_RESULT_REASON_INVALID_CSR, .name = "Invalid CSR" },
|
|
{ .value = KMIP_RESULT_REASON_INVALID_OBJECT_TYPE,
|
|
.name = "Invalid Object Type" },
|
|
{ .value = KMIP_RESULT_REASON_KEY_WRAP_TYPE_NOT_SUPPORTED,
|
|
.name = "Key Wrap Type Not Supported" },
|
|
{ .value = KMIP_RESULT_REASON_MISSING_INITIALIZATION_VECTOR,
|
|
.name = "Missing Initialization Vector" },
|
|
{ .value = KMIP_RESULT_REASON_NOT_UNIQUE_NAME_ATTRIBUTE,
|
|
.name = "Non Unique Name Attribute" },
|
|
{ .value = KMIP_RESULT_REASON_OBJECT_DESTROYED,
|
|
.name = "Object Destroyed" },
|
|
{ .value = KMIP_RESULT_REASON_OBJECT_NOT_FOUND,
|
|
.name = "Object Not Found" },
|
|
{ .value = KMIP_RESULT_REASON_NOT_AUTHORISED,
|
|
.name = "Not Authorised" },
|
|
{ .value = KMIP_RESULT_REASON_SERVER_LIMIT_EXCEEDED,
|
|
.name = "Server Limit Exceeded" },
|
|
{ .value = KMIP_RESULT_REASON_UNKNOWN_ENUMERATION,
|
|
.name = "Unknown Enumeration" },
|
|
{ .value = KMIP_RESULT_REASON_UNKNOWN_MESSAGE_EXTENSION,
|
|
.name = "Unknown Message Extension" },
|
|
{ .value = KMIP_RESULT_REASON_UNKNOWN_TAG, .name = "Unknown Tag" },
|
|
{ .value = KMIP_RESULT_REASON_UNSUPPORTED_CRYPTO_PARAMETERS,
|
|
.name = "Unsupported Cryptographic Parameters" },
|
|
{ .value = KMIP_RESULT_REASON_UNSUPPORTED_PROTOCOL_VERSION,
|
|
.name = "Unsupported Protocol Version" },
|
|
{ .value = KMIP_RESULT_REASON_WRAPPING_OBJECT_ARCHIVED,
|
|
.name = "Wrapping Object Archived" },
|
|
{ .value = KMIP_RESULT_REASON_WRAPPING_OBJECT_DESTROYED,
|
|
.name = "Wrapping Object Destroyed" },
|
|
{ .value = KMIP_RESULT_REASON_WRAPPING_OBJECT_NOT_FOUND,
|
|
.name = "Wrapping Object Not Found" },
|
|
{ .value = KMIP_RESULT_REASON_WRONG_KEY_LIFECYCLE_STATE,
|
|
.name = "Wrong Key Lifecycle State" },
|
|
{ .value = KMIP_RESULT_REASON_PROTECTION_STORAGE_UNAVAILABLE,
|
|
.name = "Protection Storage Unavailable" },
|
|
{ .value = KMIP_RESULT_REASON_PKCS_11_CODE_ERROR,
|
|
.name = "PKCS#11 Codec Error" },
|
|
{ .value = KMIP_RESULT_REASON_PKCS_11_INVALID_FUNCTION,
|
|
.name = "PKCS#11 Invalid Function" },
|
|
{ .value = KMIP_RESULT_REASON_PKCS_11_INVALID_INTERFACE,
|
|
.name = "PKCS#11 Invalid Interface" },
|
|
{ .value = KMIP_RESULT_REASON_PRIVATE_PROT_STORAGE_UNAVAILABLE,
|
|
.name = "Private Protection Storage Unavailable" },
|
|
{ .value = KMIP_RESULT_REASON_PUBLIC_PROT_STORAGE_UNAVAILABLE,
|
|
.name = "Public Protection Storage Unavailable" },
|
|
{ .value = KMIP_RESULT_REASON_UNKNOWN_OBJECT_GROUP,
|
|
.name = "Unknown Object Group" },
|
|
{ .value = KMIP_RESULT_REASON_CONSTRAINT_VIOLATION,
|
|
.name = "Constraint Violation" },
|
|
{ .value = KMIP_RESULT_REASON_DUPLICATE_PROCESS_REQUEST,
|
|
.name = "Duplicate Process Request" },
|
|
{ .value = KMIP_RESULT_REASON_GENERAL_FAILURE,
|
|
.name = "General Failure" },
|
|
{ .value = 0, .name = NULL },
|
|
};
|
|
|
|
|
|
/**
|
|
* Informs a KMS plugin that it is bound to a zkey repository.
|
|
*
|
|
* Note: This function is called before kms_initialize()!
|
|
*
|
|
* @param config_path name of a directory where the KMS plugin can store
|
|
* its configuration and other files it needs to store
|
|
*
|
|
* @returns 0 on success, or a negative errno in case of an error.
|
|
*/
|
|
int kms_bind(const char *UNUSED(config_path))
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* Checks if the plugin configuration is complete. Sets the appropriate flags
|
|
* in the plugin handle
|
|
*
|
|
* @param ph the plugin handle
|
|
*/
|
|
static void _check_config_complete(struct plugin_handle *ph)
|
|
{
|
|
ph->apqns_configured =
|
|
plugin_check_property(&ph->pd, KMIP_CONFIG_APQNS) &&
|
|
plugin_check_property(&ph->pd, KMIP_CONFIG_APQN_TYPE) &&
|
|
ph->card_type != CARD_TYPE_ANY;
|
|
|
|
ph->identity_key_generated =
|
|
plugin_check_property(&ph->pd, KMIP_CONFIG_IDENTITY_KEY) &&
|
|
plugin_check_property(&ph->pd,
|
|
KMIP_CONFIG_IDENTITY_KEY_ALGORITHM) &&
|
|
plugin_check_property(&ph->pd, KMIP_CONFIG_IDENTITY_KEY_PARAMS);
|
|
|
|
ph->client_cert_avail =
|
|
plugin_check_property(&ph->pd,
|
|
KMIP_CONFIG_CLIENT_CERTIFICATE) &&
|
|
plugin_check_property(&ph->pd,
|
|
KMIP_CONFIG_CLIENT_CERT_ALGORITHM);
|
|
|
|
ph->connection_configured =
|
|
plugin_check_property(&ph->pd, KMIP_CONFIG_SERVER) &&
|
|
plugin_check_property(&ph->pd, KMIP_CONFIG_SERVER_INFO) &&
|
|
plugin_check_property(&ph->pd, KMIP_CONFIG_PROFILE) &&
|
|
plugin_check_property(&ph->pd,
|
|
KMIP_CONFIG_VERIFY_SERVER_CERT) &&
|
|
plugin_check_property(&ph->pd, KMIP_CONFIG_VERIFY_HOSTNAME) &&
|
|
plugin_check_property(&ph->pd, KMIP_CONFIG_PROTOCOL_VERSION);
|
|
|
|
ph->wrapping_key_avail =
|
|
plugin_check_property(&ph->pd, KMIP_CONFIG_WRAPPING_KEY) &&
|
|
plugin_check_property(&ph->pd,
|
|
KMIP_CONFIG_WRAPPING_KEY_ALGORITHM) &&
|
|
plugin_check_property(&ph->pd,
|
|
KMIP_CONFIG_WRAPPING_KEY_PARAMS) &&
|
|
plugin_check_property(&ph->pd, KMIP_CONFIG_WRAPPING_KEY_ID);
|
|
|
|
ph->config_complete = ph->apqns_configured &&
|
|
ph->identity_key_generated &&
|
|
ph->client_cert_avail &&
|
|
ph->connection_configured &&
|
|
ph->wrapping_key_avail;
|
|
}
|
|
|
|
/**
|
|
* Returns a textual name of the specified card type.
|
|
*
|
|
* @param card_type the card type
|
|
*
|
|
* @returns a constant string, or NULL if an invalid card type is specified
|
|
*/
|
|
static const char *_card_type_to_str(enum card_type card_type)
|
|
{
|
|
switch (card_type) {
|
|
case CARD_TYPE_CCA:
|
|
return KMIP_APQN_TYPE_CCA;
|
|
case CARD_TYPE_EP11:
|
|
return KMIP_APQN_TYPE_EP11;
|
|
default:
|
|
return NULL;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Returns the card type for the textual name of the card type.
|
|
*
|
|
* @param card_type the card type as string
|
|
*
|
|
* @returns the card type value, or CARD_TYPE_ANY if unknown
|
|
*/
|
|
static enum card_type _card_type_from_str(const char *card_type)
|
|
{
|
|
if (strcmp(card_type, KMIP_APQN_TYPE_CCA) == 0)
|
|
return CARD_TYPE_CCA;
|
|
if (strcmp(card_type, KMIP_APQN_TYPE_EP11) == 0)
|
|
return CARD_TYPE_EP11;
|
|
|
|
return CARD_TYPE_ANY;
|
|
}
|
|
|
|
/**
|
|
* Unloads the CCA library
|
|
*
|
|
* @param ph the plugin handle
|
|
*/
|
|
static void _terminate_cca_library(struct plugin_handle *ph)
|
|
{
|
|
if (ph->cca_lib.cca_lib != NULL)
|
|
dlclose(ph->cca_lib.cca_lib);
|
|
ph->cca_lib.cca_lib = NULL;
|
|
}
|
|
|
|
/*
|
|
* Sets up the CCA library structure in the handle. Load the CCA library
|
|
* and selects one of the associated APQNs.
|
|
*
|
|
* @param ph the plugin handle
|
|
* @param apqns the associated APQNs
|
|
*
|
|
* @returns 0 on success, a negative errno in case of an error.
|
|
*/
|
|
static int _setup_cca_library(struct plugin_handle *ph, const char *apqns)
|
|
{
|
|
int rc;
|
|
|
|
_terminate_cca_library(ph);
|
|
|
|
rc = select_cca_adapter_by_apqns(&ph->pd, apqns, &ph->cca);
|
|
if (rc != 0) {
|
|
_set_error(ph, "Failed to select one of the associated APQNs: "
|
|
"%s", apqns);
|
|
_terminate_cca_library(ph);
|
|
}
|
|
|
|
ph->cca_lib.cca_lib = ph->cca.lib_csulcca;
|
|
|
|
return rc;
|
|
}
|
|
|
|
/**
|
|
* Unloads the Ep11 library
|
|
*
|
|
* @param ph the plugin handle
|
|
*/
|
|
static void _terminate_ep11_library(struct plugin_handle *ph)
|
|
{
|
|
if (ph->ep11.lib_ep11 == NULL)
|
|
return;
|
|
|
|
if (ph->ep11_lib.target != 0) {
|
|
free_ep11_target_for_apqn(&ph->ep11, ph->ep11_lib.target);
|
|
ph->ep11_lib.target = 0;
|
|
}
|
|
ph->ep11_lib.ep11_lib = NULL;
|
|
|
|
if (ph->ep11.lib_ep11 != NULL)
|
|
dlclose(ph->ep11.lib_ep11);
|
|
memset(&ph->ep11, 0, sizeof(ph->ep11));
|
|
}
|
|
|
|
/*
|
|
* Sets up the EP11 library structure in the handle. Load the EP11 library
|
|
* and sets up the EP11 target with the APQNs specified
|
|
*
|
|
* @param ph the plugin handle
|
|
* @param apqns the associated APQNs
|
|
*
|
|
* @returns 0 on success, a negative errno in case of an error.
|
|
*/
|
|
static int _setup_ep11_library(struct plugin_handle *ph, const char *apqns)
|
|
{
|
|
unsigned int card, domain;
|
|
bool selected = false;
|
|
char **apqn_list;
|
|
int rc, i;
|
|
|
|
rc = load_ep11_library(&ph->ep11, ph->pd.verbose);
|
|
if (rc != 0) {
|
|
_set_error(ph, "Failed load the EP11 host library");
|
|
return rc;
|
|
}
|
|
|
|
apqn_list = str_list_split(apqns);
|
|
for (i = 0; apqn_list[i] != NULL; i++) {
|
|
if (sscanf(apqn_list[i], "%x.%x", &card, &domain) != 2)
|
|
continue;
|
|
|
|
if (sysfs_is_apqn_online(card, domain, CARD_TYPE_EP11) != 1)
|
|
continue;
|
|
|
|
rc = get_ep11_target_for_apqn(&ph->ep11, card, domain,
|
|
&ph->ep11_lib.target,
|
|
ph->pd.verbose);
|
|
if (rc != 0) {
|
|
_set_error(ph, "Failed to get EP11 target for "
|
|
"APQN %02x.%04x: %s", card, domain,
|
|
strerror(-rc));
|
|
goto out;
|
|
}
|
|
|
|
selected = true;
|
|
break;
|
|
}
|
|
|
|
if (!selected) {
|
|
_set_error(ph, "None of the associated APQNs is "
|
|
"available: %s", apqns);
|
|
rc = -ENODEV;
|
|
goto out;
|
|
}
|
|
|
|
pr_verbose(&ph->pd, "Selected APQN %02x.%04x", card, domain);
|
|
|
|
ph->ep11_lib.ep11_lib = ph->ep11.lib_ep11;
|
|
|
|
out:
|
|
if (apqn_list != NULL)
|
|
str_list_free_string_array(apqn_list);
|
|
|
|
if (rc != 0)
|
|
_terminate_ep11_library(ph);
|
|
|
|
return rc;
|
|
}
|
|
|
|
/**
|
|
* Terminates the external secure key library structure in the handle
|
|
* and the OpenSSL secure key interface.
|
|
*
|
|
* @param ph the plugin handle
|
|
*/
|
|
static void _terminate_ext_lib(struct plugin_handle *ph)
|
|
{
|
|
if (ph->ext_lib.type != 0)
|
|
SK_OPENSSL_term();
|
|
|
|
switch (ph->ext_lib.type) {
|
|
case SK_EXT_LIB_CCA:
|
|
_terminate_cca_library(ph);
|
|
ph->ext_lib.cca = NULL;
|
|
break;
|
|
|
|
case SK_EXT_LIB_EP11:
|
|
_terminate_ep11_library(ph);
|
|
ph->ext_lib.ep11 = NULL;
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
|
|
ph->ext_lib.type = 0;
|
|
}
|
|
|
|
/**
|
|
* Initializes the external secure key library structure in the handle
|
|
* with the information from the associated APQNs. Also initializes the
|
|
* OpenSSL secure key interface.
|
|
*
|
|
* @param ph the plugin handle
|
|
*
|
|
* @returns 0 on success, a negative errno in case of an error.
|
|
*/
|
|
static int _setup_ext_lib(struct plugin_handle *ph)
|
|
{
|
|
char *apqns;
|
|
int rc = 0;
|
|
|
|
if (ph->ext_lib.type != 0)
|
|
return 0;
|
|
|
|
if (!ph->apqns_configured) {
|
|
_set_error(ph, "The configuration is incomplete, you must "
|
|
"first configure the APQNs used with this plugin.");
|
|
return -EINVAL;
|
|
}
|
|
|
|
apqns = properties_get(ph->pd.properties, KMIP_CONFIG_APQNS);
|
|
if (apqns == NULL) {
|
|
_set_error(ph, "No APQN are associated with the plugin.");
|
|
return -ENODEV;
|
|
}
|
|
|
|
pr_verbose(&ph->pd, "Associated APQNs: %s", apqns);
|
|
|
|
switch (ph->card_type) {
|
|
case CARD_TYPE_CCA:
|
|
rc = _setup_cca_library(ph, apqns);
|
|
if (rc != 0)
|
|
goto out;
|
|
|
|
ph->ext_lib.type = SK_EXT_LIB_CCA;
|
|
ph->ext_lib.cca = &ph->cca_lib;
|
|
break;
|
|
|
|
case CARD_TYPE_EP11:
|
|
rc = _setup_ep11_library(ph, apqns);
|
|
if (rc != 0)
|
|
goto out;
|
|
|
|
ph->ext_lib.type = SK_EXT_LIB_EP11;
|
|
ph->ext_lib.ep11 = &ph->ep11_lib;
|
|
break;
|
|
|
|
default:
|
|
_set_error(ph, "The configuration is incomplete, you must "
|
|
"first configure the APQNs used with this plugin.");
|
|
return -EINVAL;
|
|
}
|
|
|
|
rc = SK_OPENSSL_init(ph->pd.verbose);
|
|
if (rc != 0)
|
|
_terminate_ext_lib(ph);
|
|
|
|
out:
|
|
free(apqns);
|
|
|
|
return rc;
|
|
}
|
|
|
|
/**
|
|
* Gets the client key as PKEY
|
|
*
|
|
* @param ph the plugin handle
|
|
* @param pkey on return: the client key as pkey
|
|
*
|
|
* @returns a KMS plugin handle, or NULL in case of an error.
|
|
*/
|
|
static int _get_client_key(struct plugin_handle *ph, EVP_PKEY **pkey)
|
|
{
|
|
unsigned char identity_key[KMIP_MAX_KEY_TOKEN_SIZE] = { 0 };
|
|
size_t identity_key_size = sizeof(identity_key);
|
|
bool rsa_pss = false;
|
|
char *cert_algo;
|
|
int rc;
|
|
|
|
if (ph->identity_secure_key == NULL)
|
|
return -EINVAL;
|
|
|
|
rc = SK_UTIL_read_key_blob(ph->identity_secure_key, identity_key,
|
|
&identity_key_size);
|
|
if (rc != 0) {
|
|
_set_error(ph, "Failed to load the identity key from '%s': %s",
|
|
ph->identity_secure_key, strerror(-rc));
|
|
return rc;
|
|
}
|
|
|
|
cert_algo = properties_get(ph->pd.properties,
|
|
KMIP_CONFIG_CLIENT_CERT_ALGORITHM);
|
|
if (cert_algo != NULL) {
|
|
rsa_pss = (strcmp(cert_algo, KMIP_KEY_ALGORITHM_RSA_PSS) == 0);
|
|
free(cert_algo);
|
|
}
|
|
|
|
rc = SK_OPENSSL_get_secure_key_as_pkey(identity_key, identity_key_size,
|
|
rsa_pss, pkey, &ph->ext_lib,
|
|
ph->pd.verbose);
|
|
if (rc != 0) {
|
|
_set_error(ph, "Failed to get the PKEY from the identity key: "
|
|
"%s", strerror(-rc));
|
|
return rc;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
/**
|
|
* Gets the KMIP config structure contents from the plugin properties
|
|
*
|
|
* @param ph the plugin handle
|
|
*
|
|
* @returns a KMS plugin handle, or NULL in case of an error.
|
|
*/
|
|
static int _get_kmip_config(struct plugin_handle *ph)
|
|
{
|
|
|
|
char *tmp;
|
|
int rc;
|
|
|
|
if (ph->server == NULL || ph->profile == NULL)
|
|
return 0;
|
|
|
|
rc = _setup_ext_lib(ph);
|
|
if (rc != 0)
|
|
return rc;
|
|
|
|
rc = _get_client_key(ph, &ph->kmip_config.tls_client_key);
|
|
if (rc != 0)
|
|
return rc;
|
|
|
|
ph->kmip_config.transport = ph->profile->transport;
|
|
ph->kmip_config.encoding = ph->profile->encoding;
|
|
|
|
if (strncmp(ph->server, "https://", 8) == 0) {
|
|
/* User overrides transport to HTTPS */
|
|
ph->kmip_config.transport = KMIP_TRANSPORT_HTTPS;
|
|
ph->kmip_config.server = util_strdup(ph->server);
|
|
} else if (ph->kmip_config.transport == KMIP_TRANSPORT_HTTPS) {
|
|
/* HTTPS selected, but no URL specified: build URL */
|
|
util_asprintf((char **)&ph->kmip_config.server, "https://%s%s",
|
|
ph->server, ph->profile->https_uri);
|
|
} else {
|
|
ph->kmip_config.server = util_strdup(ph->server);
|
|
}
|
|
|
|
ph->kmip_config.tls_client_cert = properties_get(ph->pd.properties,
|
|
KMIP_CONFIG_CLIENT_CERTIFICATE);
|
|
|
|
ph->kmip_config.tls_ca = properties_get(ph->pd.properties,
|
|
KMIP_CONFIG_CA_BUNDLE);
|
|
|
|
ph->kmip_config.tls_issuer_cert = NULL;
|
|
ph->kmip_config.tls_pinned_pubkey = properties_get(ph->pd.properties,
|
|
KMIP_CONFIG_SERVER_PUBKEY);
|
|
ph->kmip_config.tls_server_cert = properties_get(ph->pd.properties,
|
|
KMIP_CONFIG_SERVER_CERT);
|
|
tmp = properties_get(ph->pd.properties, KMIP_CONFIG_VERIFY_SERVER_CERT);
|
|
ph->kmip_config.tls_verify_peer =
|
|
(tmp != NULL && strcasecmp(tmp, "yes") == 0);
|
|
if (tmp != NULL)
|
|
free(tmp);
|
|
tmp = properties_get(ph->pd.properties, KMIP_CONFIG_VERIFY_HOSTNAME);
|
|
ph->kmip_config.tls_verify_host =
|
|
(tmp != NULL && strcasecmp(tmp, "yes") == 0);
|
|
if (tmp != NULL)
|
|
free(tmp);
|
|
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* Frees the KMIP config structure contents
|
|
*
|
|
* @param ph the plugin handle
|
|
*/
|
|
static void _free_kmip_config(struct plugin_handle *ph)
|
|
{
|
|
if (ph->kmip_config.server != NULL)
|
|
free((void *)ph->kmip_config.server);
|
|
if (ph->kmip_config.tls_client_key != NULL)
|
|
EVP_PKEY_free(ph->kmip_config.tls_client_key);
|
|
if (ph->kmip_config.tls_client_cert != NULL)
|
|
free((void *)ph->kmip_config.tls_client_cert);
|
|
if (ph->kmip_config.tls_ca != NULL)
|
|
free((void *)ph->kmip_config.tls_ca);
|
|
if (ph->kmip_config.tls_issuer_cert != NULL)
|
|
free((void *)ph->kmip_config.tls_issuer_cert);
|
|
if (ph->kmip_config.tls_pinned_pubkey != NULL)
|
|
free((void *)ph->kmip_config.tls_pinned_pubkey);
|
|
if (ph->kmip_config.tls_server_cert != NULL)
|
|
free((void *)ph->kmip_config.tls_server_cert);
|
|
if (ph->kmip_config.tls_cipher_list != NULL)
|
|
free((void *)ph->kmip_config.tls_cipher_list);
|
|
if (ph->kmip_config.tls13_cipher_list != NULL)
|
|
free((void *)ph->kmip_config.tls13_cipher_list);
|
|
|
|
memset(&ph->kmip_config, 0, sizeof(ph->kmip_config));
|
|
}
|
|
|
|
/**
|
|
* Initializes a KMS plugin for usage by zkey. When a repository is bound to a
|
|
* KMS plugin, zkey calls this function when opening the repository.
|
|
*
|
|
* @param config_path name of a directory where the KMS plugin can store
|
|
* its configuration and other files it needs to store
|
|
* @param verbose if true, the plugin should write verbose or debug
|
|
* messages to stderr during further processing.
|
|
*
|
|
* @returns a KMS plugin handle, or NULL in case of an error.
|
|
*/
|
|
kms_handle_t kms_initialize(const char *config_path, bool verbose)
|
|
{
|
|
struct plugin_handle *ph;
|
|
char *apqn_type = NULL;
|
|
char *tmp;
|
|
int rc;
|
|
|
|
util_assert(config_path != NULL, "Internal error: config_path is NULL");
|
|
|
|
ph = util_malloc(sizeof(struct plugin_handle));
|
|
memset(ph, 0, sizeof(struct plugin_handle));
|
|
|
|
rc = plugin_init(&ph->pd, "zkey-kmip", config_path,
|
|
KMIP_CONFIG_FILE, verbose);
|
|
if (rc != 0)
|
|
goto error;
|
|
|
|
_check_config_complete(ph);
|
|
pr_verbose(&ph->pd, "Plugin configuration is %scomplete",
|
|
ph->config_complete ? "" : "in");
|
|
|
|
ph->card_type = CARD_TYPE_ANY;
|
|
apqn_type = properties_get(ph->pd.properties, KMIP_CONFIG_APQN_TYPE);
|
|
if (apqn_type != NULL) {
|
|
ph->card_type = _card_type_from_str(apqn_type);
|
|
free(apqn_type);
|
|
if (ph->card_type == CARD_TYPE_ANY) {
|
|
pr_verbose(&ph->pd, "APQN type invalid: %s", apqn_type);
|
|
goto error;
|
|
}
|
|
}
|
|
|
|
ph->identity_secure_key = properties_get(ph->pd.properties,
|
|
KMIP_CONFIG_IDENTITY_KEY);
|
|
ph->server = properties_get(ph->pd.properties, KMIP_CONFIG_SERVER);
|
|
|
|
tmp = properties_get(ph->pd.properties, KMIP_CONFIG_PROFILE);
|
|
if (tmp != NULL) {
|
|
rc = profile_find_by_name(ph, tmp, &ph->profile);
|
|
free(tmp);
|
|
if (rc != 0)
|
|
goto error;
|
|
}
|
|
|
|
tmp = properties_get(ph->pd.properties, KMIP_CONFIG_PROTOCOL_VERSION);
|
|
if (tmp != NULL &&
|
|
strcmp(tmp, KMIP_CONFIG_PROTOCOL_VERSION_PROFILE) != 0) {
|
|
if (sscanf(tmp, "%u.%u", &ph->kmip_version.major,
|
|
&ph->kmip_version.minor) != 2) {
|
|
_set_error(ph, "Invalid value for '%s': '%s'",
|
|
KMIP_CONFIG_PROTOCOL_VERSION, tmp);
|
|
rc = -EINVAL;
|
|
free(tmp);
|
|
goto error;
|
|
}
|
|
}
|
|
if (tmp != NULL)
|
|
free(tmp);
|
|
|
|
rc = _get_kmip_config(ph);
|
|
if (rc != 0)
|
|
goto error;
|
|
|
|
return (kms_handle_t)ph;
|
|
|
|
error:
|
|
if (strlen(ph->pd.error_msg) > 0)
|
|
warnx("%s", ph->pd.error_msg);
|
|
|
|
kms_terminate(ph);
|
|
return NULL;
|
|
}
|
|
|
|
/**
|
|
* Terminates the use of a KMS plugin. When a repository is bound to a KMS
|
|
* plugin, zkey calls this function when closing the repository.
|
|
*
|
|
* @param handle the KMS plugin handle obtained from kms_initialize()
|
|
*
|
|
* @returns 0 on success, or a negative errno in case of an error.
|
|
* Function kms_get_last_error() can be used to obtain more details about the
|
|
* error.
|
|
*/
|
|
int kms_terminate(const kms_handle_t handle)
|
|
{
|
|
struct plugin_handle *ph = handle;
|
|
|
|
util_assert(handle != NULL, "Internal error: handle is NULL");
|
|
|
|
pr_verbose(&ph->pd, "Plugin terminating");
|
|
|
|
if (ph->connection != NULL)
|
|
kmip_connection_free(ph->connection);
|
|
|
|
_free_kmip_config(ph);
|
|
|
|
if (ph->identity_secure_key != NULL)
|
|
free((void *)ph->identity_secure_key);
|
|
if (ph->server != NULL)
|
|
free((void *)ph->server);
|
|
if (ph->profile != NULL)
|
|
profile_free(ph->profile);
|
|
|
|
_terminate_ext_lib(ph);
|
|
plugin_term(&ph->pd);
|
|
free(ph);
|
|
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* Returns a textual message about the last occurred error that occurred in the
|
|
* last called KMS plugin function. If no error occurred (i.e. the last plugin
|
|
* function returned rc = 0), then NULL is returned.
|
|
* The returned string is static or contained within the handle. It is valid
|
|
* only until the next KMS plugin function is called.
|
|
*
|
|
* @param handle the KMS plugin handle obtained from kms_initialize()
|
|
*
|
|
* @returns an error message of NULL
|
|
*/
|
|
const char *kms_get_last_error(const kms_handle_t handle)
|
|
{
|
|
struct plugin_handle *ph = handle;
|
|
|
|
util_assert(handle != NULL, "Internal error: handle is NULL");
|
|
|
|
pr_verbose(&ph->pd, "Last error: '%s'", ph->pd.error_msg);
|
|
|
|
if (strlen(ph->pd.error_msg) == 0)
|
|
return NULL;
|
|
|
|
return ph->pd.error_msg;
|
|
}
|
|
|
|
/**
|
|
* Returns true if the KMS plugin supports the specified key type.
|
|
*
|
|
* @param handle the KMS plugin handle obtained from kms_initialize()
|
|
* @param key_type the zkey key type, euch as 'CCA-AESDATA',
|
|
* 'CCA-AESCIPHER', 'EP11-AES'.
|
|
*
|
|
* @returns true if the KMS plugin supports the key type, false otherwise.
|
|
*/
|
|
bool kms_supports_key_type(const kms_handle_t handle,
|
|
const char *key_type)
|
|
{
|
|
struct plugin_handle *ph = handle;
|
|
|
|
util_assert(handle != NULL, "Internal error: handle is NULL");
|
|
util_assert(key_type != NULL, "Internal error: key_type is NULL");
|
|
|
|
plugin_clear_error(&ph->pd);
|
|
|
|
switch (ph->card_type) {
|
|
case CARD_TYPE_CCA:
|
|
if (strcasecmp(key_type, KEY_TYPE_CCA_AESDATA) == 0)
|
|
return true;
|
|
if (strcasecmp(key_type, KEY_TYPE_CCA_AESCIPHER) == 0)
|
|
return true;
|
|
break;
|
|
case CARD_TYPE_EP11:
|
|
if (strcasecmp(key_type, KEY_TYPE_EP11_AES) == 0)
|
|
return true;
|
|
break;
|
|
default:
|
|
if (strcasecmp(key_type, KEY_TYPE_CCA_AESDATA) == 0)
|
|
return true;
|
|
if (strcasecmp(key_type, KEY_TYPE_CCA_AESCIPHER) == 0)
|
|
return true;
|
|
if (strcasecmp(key_type, KEY_TYPE_EP11_AES) == 0)
|
|
return true;
|
|
break;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
/**
|
|
* Displays information about the KMS Plugin and its current configuration on
|
|
* stdout.
|
|
*
|
|
* @param handle the KMS plugin handle obtained from kms_initialize()
|
|
*
|
|
* @returns 0 on success, or a negative errno in case of an error.
|
|
* Function kms_get_last_error() can be used to obtain more details about the
|
|
* error.
|
|
*/
|
|
int kms_display_info(const kms_handle_t handle)
|
|
{
|
|
struct plugin_handle *ph = handle;
|
|
X509 *cert = NULL;
|
|
char *tmp = NULL;
|
|
bool rsa;
|
|
BIO *b;
|
|
|
|
util_assert(handle != NULL, "Internal error: handle is NULL");
|
|
|
|
pr_verbose(&ph->pd, "Display Info");
|
|
|
|
plugin_clear_error(&ph->pd);
|
|
|
|
tmp = properties_get(ph->pd.properties,
|
|
KMIP_CONFIG_IDENTITY_KEY_ALGORITHM);
|
|
if (tmp != NULL) {
|
|
printf(" Identity key: %s", tmp);
|
|
rsa = strcmp(tmp, KMIP_KEY_ALGORITHM_RSA) == 0;
|
|
free(tmp);
|
|
tmp = properties_get(ph->pd.properties,
|
|
KMIP_CONFIG_IDENTITY_KEY_PARAMS);
|
|
if (tmp != NULL) {
|
|
printf(" (%s%s)", tmp, rsa ? " bits" : "");
|
|
free(tmp);
|
|
}
|
|
printf("\n");
|
|
} else {
|
|
printf(" Identity key: (configuration required)\n");
|
|
}
|
|
tmp = properties_get(ph->pd.properties, KMIP_CONFIG_IDENTITY_KEY_REENC);
|
|
if (tmp != NULL) {
|
|
printf(" (re-enciphering pending)\n");
|
|
free(tmp);
|
|
}
|
|
|
|
tmp = properties_get(ph->pd.properties,
|
|
KMIP_CONFIG_CLIENT_CERTIFICATE);
|
|
if (tmp != NULL) {
|
|
SK_UTIL_read_x509_certificate(tmp, &cert);
|
|
free(tmp);
|
|
if (cert != NULL) {
|
|
b = BIO_new_fp(stdout, BIO_NOCLOSE);
|
|
BIO_printf(b,
|
|
" Client certificate: Subject:\n");
|
|
X509_NAME_print_ex(b, X509_get_subject_name(cert), 26,
|
|
XN_FLAG_SEP_MULTILINE);
|
|
BIO_printf(b,
|
|
"\n Issuer:\n");
|
|
X509_NAME_print_ex(b, X509_get_issuer_name(cert), 26,
|
|
XN_FLAG_SEP_MULTILINE);
|
|
BIO_printf(b, "\n Validity:\n");
|
|
BIO_printf(b,
|
|
" Not before: ");
|
|
ASN1_TIME_print(b, X509_get0_notBefore(cert));
|
|
BIO_printf(b,
|
|
"\n Not after: ");
|
|
ASN1_TIME_print(b, X509_get0_notAfter(cert));
|
|
BIO_printf(b,
|
|
"\n Serial Number: ");
|
|
i2a_ASN1_INTEGER(b, X509_get0_serialNumber(cert));
|
|
BIO_printf(b, "\n");
|
|
BIO_free(b);
|
|
X509_free(cert);
|
|
} else {
|
|
printf(" Client certificate: (error)\n");
|
|
}
|
|
} else {
|
|
printf(" Client certificate: (configuration required)\n");
|
|
return 0;
|
|
}
|
|
|
|
tmp = properties_get(ph->pd.properties, KMIP_CONFIG_SERVER);
|
|
printf(" KMIP server: %s\n", tmp != NULL ? tmp :
|
|
"(configuration required)");
|
|
if (tmp != NULL)
|
|
free(tmp);
|
|
else
|
|
return 0;
|
|
tmp = properties_get(ph->pd.properties, KMIP_CONFIG_SERVER_INFO);
|
|
if (tmp != NULL) {
|
|
printf(" KMIP server info: %s\n", tmp);
|
|
free(tmp);
|
|
}
|
|
tmp = properties_get(ph->pd.properties, KMIP_CONFIG_PROFILE);
|
|
printf(" KMIP plugin profile: %s\n", tmp != NULL ? tmp :
|
|
"(configuration required)");
|
|
if (tmp != NULL)
|
|
free(tmp);
|
|
else
|
|
return 0;
|
|
if (ph->kmip_version.major != 0)
|
|
printf(" KMIP version: %u.%u\n",
|
|
ph->kmip_version.major, ph->kmip_version.minor);
|
|
else if (ph->profile != NULL && ph->profile->kmip_version.major != 0)
|
|
printf(" KMIP version: %u.%u (from profile)\n",
|
|
ph->profile->kmip_version.major,
|
|
ph->profile->kmip_version.minor);
|
|
else
|
|
printf(" KMIP version: (configuration required)\n");
|
|
tmp = properties_get(ph->pd.properties, KMIP_CONFIG_CA_BUNDLE);
|
|
printf(" CA-bundle: %s\n", tmp != NULL ? tmp :
|
|
"System's CA certificates");
|
|
if (tmp != NULL)
|
|
free(tmp);
|
|
tmp = properties_get(ph->pd.properties, KMIP_CONFIG_SERVER_CERT);
|
|
if (tmp != NULL) {
|
|
printf(" Trusting the server certificate\n");
|
|
free(tmp);
|
|
}
|
|
tmp = properties_get(ph->pd.properties, KMIP_CONFIG_SERVER_PUBKEY);
|
|
if (tmp != NULL) {
|
|
printf(" Using server public key pinning\n");
|
|
free(tmp);
|
|
}
|
|
tmp = properties_get(ph->pd.properties, KMIP_CONFIG_VERIFY_SERVER_CERT);
|
|
if (tmp != NULL && strcasecmp(tmp, "yes") == 0)
|
|
printf(" The server's certificate must be valid\n");
|
|
else
|
|
printf(" The server's certificate is not verified\n");
|
|
if (tmp != NULL)
|
|
free(tmp);
|
|
tmp = properties_get(ph->pd.properties, KMIP_CONFIG_VERIFY_HOSTNAME);
|
|
if (tmp != NULL) {
|
|
if (strcasecmp(tmp, "yes") == 0)
|
|
printf(" The server's certificate must match the "
|
|
"hostname\n");
|
|
free(tmp);
|
|
}
|
|
|
|
if (ph->profile != NULL) {
|
|
switch (ph->profile->auth_scheme) {
|
|
case KMIP_PROFILE_AUTH_TLS_CLIENT_CERT:
|
|
printf(" Authentication: TLS Client "
|
|
"Authentication\n");
|
|
break;
|
|
default:
|
|
printf(" Authentication: (unknown)\n");
|
|
break;
|
|
}
|
|
}
|
|
|
|
tmp = properties_get(ph->pd.properties, KMIP_CONFIG_WRAPPING_KEY_ID);
|
|
if (tmp != NULL) {
|
|
printf(" Wrapping key ID: %s\n", tmp);
|
|
free(tmp);
|
|
tmp = properties_get(ph->pd.properties,
|
|
KMIP_CONFIG_WRAPPING_KEY_LABEL);
|
|
if (tmp != NULL) {
|
|
printf(" Wrapping key label: %s\n", tmp);
|
|
free(tmp);
|
|
}
|
|
tmp = properties_get(ph->pd.properties,
|
|
KMIP_CONFIG_WRAPPING_KEY_ALGORITHM);
|
|
printf(" Wrapping algorithm: %s", tmp);
|
|
rsa = strcmp(tmp, KMIP_KEY_ALGORITHM_RSA) == 0;
|
|
free(tmp);
|
|
tmp = properties_get(ph->pd.properties,
|
|
KMIP_CONFIG_WRAPPING_KEY_PARAMS);
|
|
if (tmp != NULL) {
|
|
printf(" (%s%s)", tmp, rsa ? " bits" : "");
|
|
free(tmp);
|
|
}
|
|
if (ph->profile != NULL) {
|
|
switch (ph->profile->wrap_padding_method) {
|
|
case KMIP_PADDING_METHOD_PKCS_1_5:
|
|
printf(" with PKCS 1.5 padding");
|
|
break;
|
|
case KMIP_PADDING_METHOD_OAEP:
|
|
printf(" with OAEP padding");
|
|
switch (ph->profile->wrap_hashing_algo) {
|
|
case KMIP_HASHING_ALGO_SHA_1:
|
|
printf(" using SHA-1");
|
|
break;
|
|
case KMIP_HASHING_ALGO_SHA_256:
|
|
printf(" using SHA-256");
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
printf("\n");
|
|
tmp = properties_get(ph->pd.properties,
|
|
KMIP_CONFIG_WRAPPING_KEY_REENC);
|
|
if (tmp != NULL) {
|
|
printf(
|
|
" (re-enciphering pending)\n");
|
|
free(tmp);
|
|
}
|
|
} else {
|
|
printf(" Wrapping key ID: (configuration required)\n");
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
#define OPT_TLS_PIN_SERVER_PUBKEY 256
|
|
#define OPT_TLS_TRUST_SERVER_CERT 257
|
|
#define OPT_TLS_DONT_VERIFY_SERVER_CERT 258
|
|
#define OPT_TLS_VERIFY_HOSTNAME 259
|
|
|
|
static const struct util_opt configure_options[] = {
|
|
{
|
|
.flags = UTIL_OPT_FLAG_SECTION,
|
|
.desc = "KMIP SPECIFIC OPTIONS FOR IDENTITY KEY GENERATION",
|
|
.command = KMS_COMMAND_CONFIGURE,
|
|
},
|
|
{
|
|
.option = { "gen-identity-key", required_argument, NULL, 'i'},
|
|
.argument = "KEY-SPEC",
|
|
.desc = "Generates an identity key for the KMIP plugin. The "
|
|
"identity key is a secure ECC or RSA key. The identity "
|
|
"key is automatically generated with the default "
|
|
"values ECC with curve secp521r1 when a certificate "
|
|
"signing request (CSR) or self-signed certificate is "
|
|
"to be generated and no identity key is available. Use "
|
|
"this option to generate or regenerate a new identity "
|
|
"key with with specific parameters. You need to "
|
|
"regenerate a certificate with the newly generated "
|
|
"identity key and reregister this certificate with the "
|
|
"KMIP server.",
|
|
.command = KMS_COMMAND_CONFIGURE,
|
|
},
|
|
{
|
|
.flags = UTIL_OPT_FLAG_SECTION,
|
|
.desc = "KMIP SPECIFIC OPTIONS FOR CERTIFICATE GENERATION",
|
|
.command = KMS_COMMAND_CONFIGURE,
|
|
},
|
|
{
|
|
.option = { "gen-csr", required_argument, NULL, 'c'},
|
|
.argument = "CSR-PEM-FILE",
|
|
.desc = "Generates a certificate signing request (CSR) with "
|
|
"the identity key and stores it in the specified PEM "
|
|
"file. Pass this CSR to a certificate authority (CA) "
|
|
"to request a CA-signed certificate for the KMIP "
|
|
"plugin. You need to register the certificate with the "
|
|
"KMIP server. Registering a client certificate with "
|
|
"the KMIP server is a manual procedure, and is "
|
|
"specific to the KMIP server used. The KMIP server "
|
|
"accepts communication with the KMIP plugin only after "
|
|
"the certificate was registered. You must also specify "
|
|
"the CA-signed certificate with the 'zkey kms "
|
|
"configure --client-cert' option so that the KMIP "
|
|
"plugin uses it for communicating with the KMIP."
|
|
"server.",
|
|
.command = KMS_COMMAND_CONFIGURE,
|
|
},
|
|
{
|
|
.option = { "gen-self-signed-cert", required_argument, NULL,
|
|
'C'},
|
|
.argument = "CERT-PEM-FILE",
|
|
.desc = "Generates a self-signed certificate with the "
|
|
"identity key and stores it in the specified PEM "
|
|
"file. You need to register the certificate with the "
|
|
"KMIP server. Registering a client certificate with "
|
|
"the KMIP server is a manual procedure, and is "
|
|
"specific to the KMIP server used. The KMIP server "
|
|
"accepts communication with the KMIP plugin only after "
|
|
"the certificate was registered.",
|
|
.command = KMS_COMMAND_CONFIGURE,
|
|
},
|
|
{
|
|
.option = { "cert-subject", required_argument, NULL, 's'},
|
|
.argument = "SUBJECT-RDNS",
|
|
.desc = "Specifies the subject name for generating a "
|
|
"certificate signing request (CSR) or self-signed "
|
|
"certificate, in the form '<type>=<value>(;<type>="
|
|
"<value>)*[;]' with types recognized by OpenSSL.",
|
|
.command = KMS_COMMAND_CONFIGURE,
|
|
},
|
|
{
|
|
.option = { "cert-extensions", required_argument, NULL, 'e'},
|
|
.argument = "EXTENSIONS",
|
|
.desc = "Specifies the certificate extensions for generating a "
|
|
"certificate signing request (CSR) or self-signed "
|
|
"certificate, in the form '<name>=[critical,]<value(s)>"
|
|
" (;<name>=[critical,]<value(s)>)*[;]' with extension "
|
|
"names and values recognized by OpenSSL. A certificate "
|
|
"used to authenticate at a KMIP server usually needs "
|
|
"the 'TLS Web client authentication' extended-key-"
|
|
"usage certificate extension. Additionally, the "
|
|
"'Common Name' field or the 'Subject Alternate Name' "
|
|
"extension must match the host name (or IP address) of "
|
|
"the client system. If no extended-key-usage extension "
|
|
"is specified, then a 'TLS Web client authentication' "
|
|
"extension ('extendedKeyUsage = clientAuth') is "
|
|
"automatically added. If no 'Subject Alternate Name' "
|
|
"extension is specified, then an 'Subject Alternate "
|
|
"Name' extension with the system's host name "
|
|
"(subjectAltName = DNS:hostname) is automatically "
|
|
"added.",
|
|
.command = KMS_COMMAND_CONFIGURE,
|
|
},
|
|
{
|
|
.option = { "renew-cert", required_argument, NULL, 'N'},
|
|
.argument = "CERT-PEM-FILE",
|
|
.desc = "Specifies an existing PEM file that contains the "
|
|
"certificate to be renewed. The subject name and "
|
|
"extensions of the certificate are used to generate "
|
|
"the certificate signing request (CSR) or renewed "
|
|
"self-signed certificate.",
|
|
.command = KMS_COMMAND_CONFIGURE,
|
|
},
|
|
{
|
|
.option = { "csr-new-header", 0, NULL, 'n'},
|
|
.desc = "Adds the word 'NEW' to the PEM file header and footer "
|
|
"lines on the certificate signing request. Some "
|
|
"software and some CAs require this marking.",
|
|
.command = KMS_COMMAND_CONFIGURE,
|
|
},
|
|
{
|
|
.option = { "cert-validity-days", required_argument, NULL, 'd'},
|
|
.argument = "DAYS",
|
|
.desc = "Specifies the number of days the self-signed "
|
|
"certificate is valid. The default is 30 days.",
|
|
.command = KMS_COMMAND_CONFIGURE,
|
|
},
|
|
{
|
|
.option = { "cert-digest", required_argument, NULL, 'D'},
|
|
.argument = "DIGEST",
|
|
.desc = "Specifies the digest algorithm to use when generating "
|
|
"a certificate signing request or self-signed "
|
|
"certificate. The default is determined by OpenSSL.",
|
|
.command = KMS_COMMAND_CONFIGURE,
|
|
},
|
|
{
|
|
.option = { "cert-rsa-pss", 0, NULL, 'P'},
|
|
.desc = "Uses the RSA-PSS algorithm to sign the certificate "
|
|
"signing request or the self-signed certificate. This "
|
|
"option is accepted only when the identity key type is "
|
|
"RSA, it is ignored otherwise.",
|
|
.command = KMS_COMMAND_CONFIGURE,
|
|
},
|
|
{
|
|
.flags = UTIL_OPT_FLAG_SECTION,
|
|
.desc = "KMIP SPECIFIC OPTIONS FOR CERTIFICATE REGISTRATION",
|
|
.command = KMS_COMMAND_CONFIGURE,
|
|
},
|
|
{
|
|
.option = { "client-cert", required_argument, NULL,
|
|
'r'},
|
|
.argument = "CERT-PEM-FILE",
|
|
.desc = "Uses a CA-signed certificate for authenticating the "
|
|
"KMIP plugin at the KMIP server. The certificate must "
|
|
"be registered with the KMIP server. Registering a "
|
|
"client certificate with the KMIP server is a manual "
|
|
"procedure, and is specific to the KMIP server used. "
|
|
"The KMIP server accepts communication with the KMIP "
|
|
"plugin only after the certificate has been "
|
|
"registered.",
|
|
.command = KMS_COMMAND_CONFIGURE,
|
|
},
|
|
{
|
|
.flags = UTIL_OPT_FLAG_SECTION,
|
|
.desc = "KMIP SPECIFIC OPTIONS FOR THE SERVER CONNECTION",
|
|
.command = KMS_COMMAND_CONFIGURE,
|
|
},
|
|
{
|
|
.option = { "kmip-server", required_argument, NULL, 'S'},
|
|
.argument = "KMIP-SERVER",
|
|
.desc = "Specifies the hostname or IP address of the KMIP "
|
|
"server, and an optional port number separated by a "
|
|
"colon. If no port number is specified, 5696 is used "
|
|
"for KMIP. To use HTTPS transport, specify the URL, "
|
|
"starting with 'https://', followed by the hostname or "
|
|
"IP address of the KMIP server, an optional port "
|
|
"number, and an URI (for example '/kmip').",
|
|
.command = KMS_COMMAND_CONFIGURE,
|
|
},
|
|
{
|
|
.option = { "profile", required_argument, NULL, 'p'},
|
|
.argument = "PROFILE-NAME",
|
|
.desc = "Specifies the name of the KMIP plugin profile to use "
|
|
"with the KMIP server connection. If no profile name "
|
|
"is specified, the KMIP plugin queries the KMIP server "
|
|
"information and attempts to match a profile to the "
|
|
"information. If no profile matches, the default "
|
|
"profile is used. Profiles are contained in the "
|
|
"directory '/etc/zkey/kmip/profiles'. You can set the "
|
|
"location of the profiles by using the environment "
|
|
"variable 'ZKEY_KMIP_PROFILES'.",
|
|
.command = KMS_COMMAND_CONFIGURE,
|
|
},
|
|
{
|
|
.option = { "tls-ca-bundle", required_argument, NULL, 'b'},
|
|
.argument = "CA-BUNDLE",
|
|
.desc = "Specifies the CA-bundle PEM file or directory "
|
|
"containing the CA certificates that are used to "
|
|
"verify the KMIP server certificate during TLS "
|
|
"handshake. If the option specifies a directory path, "
|
|
"the directory must have been prepared with the "
|
|
"'c_rehash' utility of OpenSSL. Default is to use the "
|
|
"system CA certificates.",
|
|
.command = KMS_COMMAND_CONFIGURE,
|
|
},
|
|
{
|
|
.option = { "tls-pin-server-pubkey", 0, NULL,
|
|
OPT_TLS_PIN_SERVER_PUBKEY },
|
|
.flags = UTIL_OPT_FLAG_NOSHORT,
|
|
.desc = "Pins the public key of the KMIP server. With a pinned "
|
|
"key, the KMIP plugin verifies that every connection "
|
|
"uses the same KMIP server-certificate public key that "
|
|
"was also used to configure the connection to the KMIP "
|
|
"server. This option can be used only with CA-signed "
|
|
"KMIP server certificates.",
|
|
.command = KMS_COMMAND_CONFIGURE,
|
|
},
|
|
{
|
|
.option = { "tls-trust-server-cert", 0, NULL,
|
|
OPT_TLS_TRUST_SERVER_CERT },
|
|
.flags = UTIL_OPT_FLAG_NOSHORT,
|
|
.desc = "Trusts the certificate of the KMIP server even if it "
|
|
"is a self-signed certificate, or it can not be "
|
|
"verified due to other reasons. Use this option "
|
|
"instead of the '--tls-pin-server-pubkey' option when "
|
|
"you are using self-signed KMIP server certificates.",
|
|
.command = KMS_COMMAND_CONFIGURE,
|
|
},
|
|
{
|
|
.option = { "tls-dont-verify-server-cert", 0, NULL,
|
|
OPT_TLS_DONT_VERIFY_SERVER_CERT },
|
|
.flags = UTIL_OPT_FLAG_NOSHORT,
|
|
.desc = "Do not verify the authenticity of the certificate of "
|
|
"the KMIP server. For self-signed KMIP server "
|
|
"certificates, this is the default. Use the "
|
|
"'--tls-pin-server-cert' option to ensure the "
|
|
"authenticity of the self-signed certificate "
|
|
"explicitly. For CA-signed KMIP server certificates, "
|
|
"the default is to verify them. This option disables "
|
|
"the verification.",
|
|
.command = KMS_COMMAND_CONFIGURE,
|
|
},
|
|
{
|
|
.option = { "tls-verify-hostname", 0, NULL,
|
|
OPT_TLS_VERIFY_HOSTNAME },
|
|
.flags = UTIL_OPT_FLAG_NOSHORT,
|
|
.desc = "Verifies that the KMIP server certificates 'Common "
|
|
"Name' field or a 'Subject Alternate Name' field "
|
|
"matches the hostname that is used to connect to the "
|
|
"KMIP server.",
|
|
.command = KMS_COMMAND_CONFIGURE,
|
|
},
|
|
{
|
|
.flags = UTIL_OPT_FLAG_SECTION,
|
|
.desc = "KMIP SPECIFIC OPTIONS FOR WRAPPING KEY GENERATION",
|
|
.command = KMS_COMMAND_CONFIGURE,
|
|
},
|
|
{
|
|
.option = { "gen-wrapping-key", 0, NULL, 'w' },
|
|
.desc = "Generates a new wrapping key (key-encrypting key) "
|
|
"based on the settings in the profile and registers it "
|
|
"with the KMIP server. A wrapping key is automatically "
|
|
"generated when the KMIP server connection is "
|
|
"configured. Use this option to generate a new "
|
|
"wrapping key at a later time.",
|
|
.command = KMS_COMMAND_CONFIGURE,
|
|
},
|
|
{
|
|
.option = { "label", required_argument, NULL, 'B'},
|
|
.argument = "LABEL",
|
|
.desc = "Specifies an optional human-readable identifier of "
|
|
"the wrapping key stored in the 'Name' KMIP attribute "
|
|
"of the key. KMIP names must usually be unique within "
|
|
"the KMIP server.",
|
|
.command = KMS_COMMAND_CONFIGURE,
|
|
},
|
|
UTIL_OPT_END,
|
|
};
|
|
|
|
static const struct util_opt generate_options[] = {
|
|
{
|
|
.flags = UTIL_OPT_FLAG_SECTION,
|
|
.desc = "KMIP SPECIFIC OPTIONS",
|
|
.command = KMS_COMMAND_GENERATE,
|
|
},
|
|
{
|
|
.option = { "label", required_argument, NULL, 'B'},
|
|
.argument = "LABEL[:LABEL]",
|
|
.desc = "Specifies an optional human-readable identifier of "
|
|
"the key or keys stored in the 'Name' KMIP attribute "
|
|
"of the key. KMIP names must be unique within the KMIP "
|
|
"server. For XTS type keys, two different labels must "
|
|
"be specified, separated by a colon.",
|
|
.command = KMS_COMMAND_GENERATE,
|
|
},
|
|
UTIL_OPT_END,
|
|
};
|
|
|
|
static const struct util_opt remove_options[] = {
|
|
{
|
|
.flags = UTIL_OPT_FLAG_SECTION,
|
|
.desc = "KMIP SPECIFIC OPTIONS",
|
|
.command = KMS_COMMAND_REMOVE,
|
|
},
|
|
{
|
|
.option = { "state", required_argument, NULL, 's'},
|
|
.argument = "STATE",
|
|
.desc = "The state to which to change the key in the KMIP "
|
|
"server, after removing the secure key from the local "
|
|
"secure key repository. Possible states are "
|
|
"'DEACTIVATED', 'COMPROMISED', 'DESTROYED', and "
|
|
"'DESTROYED-COMPROMISED'. If this option is not "
|
|
"specified, the state of the key in the KMIP server is "
|
|
"not changed, but the key is removed from the local "
|
|
"secure key repository only.",
|
|
.command = KMS_COMMAND_REMOVE,
|
|
},
|
|
UTIL_OPT_END,
|
|
};
|
|
|
|
static const struct util_opt list_import_options[] = {
|
|
{
|
|
.flags = UTIL_OPT_FLAG_SECTION,
|
|
.desc = "KMIP SPECIFIC OPTIONS",
|
|
.command = KMS_COMMAND_LIST_IMPORT,
|
|
},
|
|
{
|
|
.option = { "key-type", required_argument, NULL, 'K'},
|
|
.argument = "type",
|
|
.desc = "The type of the key to import. Possible values are '"
|
|
KEY_TYPE_CCA_AESDATA"', '"KEY_TYPE_CCA_AESCIPHER"' "
|
|
"and '"KEY_TYPE_EP11_AES"'. When this option is "
|
|
"omitted, the default is '"KEY_TYPE_CCA_AESDATA"' "
|
|
"when the KMIP plugin is bound to CCA-type APQNs, or "
|
|
"'"KEY_TYPE_EP11_AES"' when the KMIP plugin is bound "
|
|
"to EP11-type APQNs.",
|
|
.command = KMS_COMMAND_LIST_IMPORT,
|
|
},
|
|
UTIL_OPT_END,
|
|
};
|
|
|
|
/**
|
|
* Returns a list of KMS specific command line options that zkey should accept
|
|
* and pass to the appropriate KMS plugin function. The option list must be
|
|
* terminated by an UTIL_OPT_END entry (see util_opt.h). The options returned
|
|
* must not interfere with the already defined options of the zkey command.
|
|
* Field 'command' of the returned options should either be NULL or specify
|
|
* the command that it is for.
|
|
*
|
|
* If max_opts is not -1, then only up to max_opts options are allowed. If more
|
|
* options are returned, only up to max_opts options are used by zkey.
|
|
*
|
|
* @param command the command for which the KMS-specific options are
|
|
* to be returned, see KMS_COMMAND_xxx defines
|
|
* @param max_opts maximum number of options allowed. If -1 then there
|
|
* is no limit.
|
|
*
|
|
* @returns a list of options terminated by an UTIL_OPT_END entry, or NULL in
|
|
* case of an error.
|
|
* Function kms_get_last_error() can be used to obtain more details about the
|
|
* error.
|
|
*/
|
|
const struct util_opt *kms_get_command_options(const char *command,
|
|
int UNUSED(max_opts))
|
|
{
|
|
util_assert(command != NULL, "Internal error: command is NULL");
|
|
|
|
if (strcasecmp(command, KMS_COMMAND_CONFIGURE) == 0)
|
|
return configure_options;
|
|
if (strcasecmp(command, KMS_COMMAND_GENERATE) == 0)
|
|
return generate_options;
|
|
if (strcasecmp(command, KMS_COMMAND_REMOVE) == 0)
|
|
return remove_options;
|
|
if (strcasecmp(command, KMS_COMMAND_LIST_IMPORT) == 0)
|
|
return list_import_options;
|
|
|
|
return NULL;
|
|
}
|
|
|
|
struct config_options {
|
|
const char *generate_identity_key;
|
|
const char *sscert_pem_file;
|
|
const char *csr_pem_file;
|
|
const char *cert_subject;
|
|
const char *cert_extensions;
|
|
const char *renew_cert_pem_file;
|
|
bool csr_new_header;
|
|
const char *cert_validity_days;
|
|
const char *cert_digest;
|
|
bool cert_rsa_pss;
|
|
const char *client_cert;
|
|
const char *kmip_server;
|
|
const char *profile;
|
|
const char *tls_ca_bundle;
|
|
bool tls_pin_server_pubkey;
|
|
bool tls_trust_server_cert;
|
|
bool tls_dont_verify_server_cert;
|
|
bool tls_verify_hostname;
|
|
bool gen_wrapping_key;
|
|
const char *wrapping_key_label;
|
|
};
|
|
|
|
/**
|
|
* Check the specified APQns and assure that they are all of the right type.
|
|
*
|
|
* @param ph the plugin handle
|
|
* @param apqns a list of APQNs to associate with the KMS plugin, or
|
|
* NULL if no APQNs are specified.
|
|
* @param num_apqns number of APQNs in above array. 0 if no APQNs are
|
|
* specified.
|
|
*
|
|
* @returns 0 on success, a negative errno in case of an error.
|
|
*/
|
|
static int _check_apqns(struct plugin_handle *ph, const struct kms_apqn *apqns,
|
|
size_t num_apqns)
|
|
{
|
|
size_t i;
|
|
int rc;
|
|
|
|
if (num_apqns == 0)
|
|
return 0;
|
|
|
|
if (ph->card_type == CARD_TYPE_ANY) {
|
|
/*
|
|
* No APQNs configured yet, accept any APQN type, but all must
|
|
* be of the same type.
|
|
*/
|
|
ph->card_type = sysfs_get_card_type(apqns[0].card);
|
|
if (ph->card_type == CARD_TYPE_ANY) {
|
|
_set_error(ph, "The APQN %02x.%04x is not available or "
|
|
"has an unsupported type", apqns[0].card,
|
|
apqns[0].domain);
|
|
return -EINVAL;
|
|
}
|
|
}
|
|
|
|
pr_verbose(&ph->pd, "Check APQNs for card type %s",
|
|
_card_type_to_str(ph->card_type));
|
|
|
|
for (i = 0; i < num_apqns; i++) {
|
|
rc = sysfs_is_apqn_online(apqns[i].card, apqns[i].domain,
|
|
ph->card_type);
|
|
if (rc != 1) {
|
|
_set_error(ph, "APQN %02x.%04x is not of the right "
|
|
"type. The plugin is configured to use "
|
|
"APQNs of type %s", apqns[i].card,
|
|
apqns[i].domain,
|
|
_card_type_to_str(ph->card_type));
|
|
|
|
return -EINVAL;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* Parse a key specification and setup the key gen info struct
|
|
*
|
|
* @param ph the plugin handle
|
|
* @param key_spec the key specification (ECC:CURVE or RSA:KEYBITS).
|
|
* If NULL, the default key specification is used.
|
|
*
|
|
* @returns 0 on success, a negative errno in case of an error.
|
|
*/
|
|
static int _parse_key_spec(struct plugin_handle *ph, const char *key_spec,
|
|
struct sk_key_gen_info *gen_info)
|
|
{
|
|
char *copy = NULL, *algorithm, *params;
|
|
int rc = 0;
|
|
|
|
copy = util_strdup(key_spec);
|
|
|
|
algorithm = strtok(copy, ":");
|
|
if (algorithm == NULL) {
|
|
_set_error(ph, "Invalid key specification format: '%s'",
|
|
key_spec);
|
|
rc = -EINVAL;
|
|
goto out;
|
|
}
|
|
|
|
params = strtok(NULL, ":");
|
|
if (params == NULL) {
|
|
_set_error(ph, "Invalid key specification format: '%s'",
|
|
key_spec);
|
|
rc = -EINVAL;
|
|
goto out;
|
|
}
|
|
|
|
if (strcasecmp(algorithm, KMIP_KEY_ALGORITHM_RSA) == 0) {
|
|
gen_info->type = SK_KEY_TYPE_RSA;
|
|
} else if (strcasecmp(algorithm, KMIP_KEY_ALGORITHM_ECC) == 0) {
|
|
gen_info->type = SK_KEY_TYPE_EC;
|
|
} else {
|
|
_set_error(ph, "Invalid key algorithm: '%s'", key_spec);
|
|
rc = -EINVAL;
|
|
goto out;
|
|
}
|
|
|
|
switch (gen_info->type) {
|
|
case SK_KEY_TYPE_RSA:
|
|
gen_info->rsa.modulus_bits = atol(params);
|
|
switch (gen_info->rsa.modulus_bits) {
|
|
case 512:
|
|
case 1024:
|
|
case 2048:
|
|
case 4096:
|
|
break;
|
|
default:
|
|
_set_error(ph, "Invalid RSA key bits: '%s'", key_spec);
|
|
rc = -EINVAL;
|
|
goto out;
|
|
}
|
|
gen_info->rsa.pub_exp = 65537;
|
|
gen_info->rsa.x9_31 = false;
|
|
break;
|
|
|
|
case SK_KEY_TYPE_EC:
|
|
gen_info->ec.curve_nid = OBJ_txt2nid(params);
|
|
if (gen_info->ec.curve_nid == NID_undef) {
|
|
_set_error(ph, "Invalid ECC curve: '%s'", key_spec);
|
|
rc = -EINVAL;
|
|
goto out;
|
|
}
|
|
break;
|
|
}
|
|
|
|
out:
|
|
free(copy);
|
|
return rc;
|
|
}
|
|
|
|
/**
|
|
* Generates (or re-generates) a identity key for the plugin using the
|
|
* specified key specification, or the default key specifications, of none
|
|
* is specified.
|
|
*
|
|
* @param ph the plugin handle
|
|
* @param key_spec the key specification (ECC:CURVE or RSA:KEYBITS).
|
|
* If NULL, the default key specification is used.
|
|
*
|
|
* @returns 0 on success, a negative errno in case of an error.
|
|
*/
|
|
static int _generate_identity_key(struct plugin_handle *ph,
|
|
const char *key_spec)
|
|
{
|
|
unsigned char identity_key[KMIP_MAX_KEY_TOKEN_SIZE] = { 0 };
|
|
size_t identity_key_size = sizeof(identity_key);
|
|
char *reenc_file = NULL, *client_cert = NULL;
|
|
struct sk_key_gen_info gen_info = { 0 };
|
|
char tmp[200];
|
|
int rc;
|
|
|
|
if (key_spec == NULL)
|
|
key_spec = KMIP_DEFAULT_IDENTITY_KEY_SPEC;
|
|
|
|
_check_config_complete(ph);
|
|
|
|
if (!ph->apqns_configured) {
|
|
_set_error(ph, "The configuration is incomplete, you must "
|
|
"first configure the APQNs used with this plugin.");
|
|
return -EINVAL;
|
|
}
|
|
|
|
rc = _parse_key_spec(ph, key_spec, &gen_info);
|
|
if (rc != 0)
|
|
return rc;
|
|
|
|
if (ph->identity_secure_key != NULL) {
|
|
printf("ATTENTION: An identity key already exists\n");
|
|
util_print_indented("When you generate a new identity key, "
|
|
"you must re-generate a certificate and "
|
|
"re-register it with the KMIP server.", 0);
|
|
printf("%s: Re-generate the identity key [y/N]? ",
|
|
program_invocation_short_name);
|
|
if (!prompt_for_yes(ph->pd.verbose)) {
|
|
_set_error(ph, "Operation aborted by user");
|
|
return -ECANCELED;
|
|
}
|
|
} else {
|
|
util_asprintf((char **)&ph->identity_secure_key,
|
|
"%s/%s", ph->pd.config_path,
|
|
KMIP_CONFIG_IDENTITY_KEY_FILE);
|
|
|
|
rc = plugin_set_or_remove_property(&ph->pd,
|
|
KMIP_CONFIG_IDENTITY_KEY,
|
|
ph->identity_secure_key);
|
|
if (rc != 0)
|
|
goto out;
|
|
}
|
|
|
|
switch (gen_info.type) {
|
|
case SK_KEY_TYPE_RSA:
|
|
rc = plugin_set_or_remove_property(&ph->pd,
|
|
KMIP_CONFIG_IDENTITY_KEY_ALGORITHM,
|
|
KMIP_KEY_ALGORITHM_RSA);
|
|
if (rc != 0)
|
|
goto out;
|
|
sprintf(tmp, "%lu", gen_info.rsa.modulus_bits);
|
|
rc = plugin_set_or_remove_property(&ph->pd,
|
|
KMIP_CONFIG_IDENTITY_KEY_PARAMS,
|
|
tmp);
|
|
if (rc != 0)
|
|
goto out;
|
|
break;
|
|
|
|
case SK_KEY_TYPE_EC:
|
|
rc = plugin_set_or_remove_property(&ph->pd,
|
|
KMIP_CONFIG_IDENTITY_KEY_ALGORITHM,
|
|
KMIP_KEY_ALGORITHM_ECC);
|
|
if (rc != 0)
|
|
goto out;
|
|
rc = plugin_set_or_remove_property(&ph->pd,
|
|
KMIP_CONFIG_IDENTITY_KEY_PARAMS,
|
|
OBJ_nid2sn(gen_info.ec.curve_nid));
|
|
if (rc != 0)
|
|
goto out;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
rc = _setup_ext_lib(ph);
|
|
if (rc != 0)
|
|
goto out;
|
|
|
|
rc = SK_OPENSSL_generate_secure_key(identity_key, &identity_key_size,
|
|
&gen_info, &ph->ext_lib,
|
|
ph->pd.verbose);
|
|
if (rc != 0) {
|
|
_set_error(ph, "Failed to generate the identity key: %s",
|
|
strerror(-rc));
|
|
goto out;
|
|
}
|
|
|
|
rc = SK_UTIL_write_key_blob(ph->identity_secure_key, identity_key,
|
|
identity_key_size);
|
|
if (rc != 0) {
|
|
_set_error(ph, "Failed to write the identity key into file "
|
|
"'%s': %s", ph->identity_secure_key, strerror(-rc));
|
|
goto out;
|
|
}
|
|
|
|
rc = plugin_set_file_permission(&ph->pd, ph->identity_secure_key);
|
|
if (rc != 0)
|
|
goto out;
|
|
|
|
reenc_file = properties_get(ph->pd.properties,
|
|
KMIP_CONFIG_IDENTITY_KEY_REENC);
|
|
if (reenc_file != NULL) {
|
|
remove(reenc_file);
|
|
free(reenc_file);
|
|
properties_remove(ph->pd.properties,
|
|
KMIP_CONFIG_IDENTITY_KEY_REENC);
|
|
}
|
|
|
|
client_cert = properties_get(ph->pd.properties,
|
|
KMIP_CONFIG_CLIENT_CERTIFICATE);
|
|
if (client_cert != NULL) {
|
|
remove(client_cert);
|
|
free(client_cert);
|
|
properties_remove(ph->pd.properties,
|
|
KMIP_CONFIG_CLIENT_CERTIFICATE);
|
|
properties_remove(ph->pd.properties,
|
|
KMIP_CONFIG_CLIENT_CERT_ALGORITHM);
|
|
}
|
|
|
|
pr_verbose(&ph->pd, "Generated identity key into '%s'",
|
|
ph->identity_secure_key);
|
|
|
|
out:
|
|
|
|
|
|
return rc;
|
|
}
|
|
|
|
/**
|
|
* Add client authentication specific certificate extensions, if they are not
|
|
* already contained. The extension list is reallocated, if required.
|
|
* If no extended key usage extension is specified, then an 'TLS Web client
|
|
* authentication' extension ('extendedKeyUsage=clientAuth') is added.
|
|
* If no 'Subject Alternate Name' extension is specified, then an 'Subject
|
|
* Alternate Name' extension with the system's host name (subjectAltName=
|
|
* DNS:hostname) is added.
|
|
*
|
|
* @param ph the plugin handle
|
|
* @param extension_list the list of extensions
|
|
* @param num_extensions the number of extensions
|
|
* @param exts Stack of extensions to add of NULL.
|
|
*
|
|
* @returns 0 on success, a negative errno in case of an error.
|
|
*/
|
|
static int _add_client_auth_extensions(struct plugin_handle *ph,
|
|
char ***extension_list,
|
|
size_t *num_extensions,
|
|
const STACK_OF(X509_EXTENSION) *exts)
|
|
{
|
|
bool keyusage_found = false;
|
|
bool altname_found = false;
|
|
struct utsname utsname;
|
|
int rc, count, k, nid;
|
|
X509_EXTENSION *ex;
|
|
size_t elements;
|
|
char **list;
|
|
size_t i;
|
|
|
|
for (i = 0; i < *num_extensions; i++) {
|
|
if (strncmp((*extension_list)[i], KMIP_CERT_EXT_KEY_USAGE,
|
|
strlen(KMIP_CERT_EXT_KEY_USAGE)) == 0)
|
|
keyusage_found = true;
|
|
if (strncmp((*extension_list)[i],
|
|
KMIP_CERT_EXT_SUBJECT_ALT_NAME,
|
|
strlen(KMIP_CERT_EXT_SUBJECT_ALT_NAME)) == 0)
|
|
altname_found = true;
|
|
}
|
|
|
|
if (exts != NULL) {
|
|
count = sk_X509_EXTENSION_num(exts);
|
|
for (k = 0; k < count; k++) {
|
|
ex = sk_X509_EXTENSION_value(exts, k);
|
|
nid = OBJ_obj2nid(X509_EXTENSION_get_object(ex));
|
|
|
|
switch (nid) {
|
|
case NID_subject_alt_name:
|
|
altname_found = true;
|
|
break;
|
|
case NID_ext_key_usage:
|
|
keyusage_found = true;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (keyusage_found && altname_found)
|
|
return 0;
|
|
|
|
elements = *num_extensions;
|
|
if (!keyusage_found)
|
|
elements++;
|
|
|
|
if (!altname_found) {
|
|
elements++;
|
|
|
|
if (uname(&utsname) != 0) {
|
|
rc = -errno;
|
|
_set_error(ph, "Failed to obtain the system's "
|
|
"hostname: %s", strerror(-rc));
|
|
return rc;
|
|
}
|
|
}
|
|
|
|
list = util_realloc(*extension_list, elements * sizeof(char *));
|
|
i = 0;
|
|
|
|
if (!keyusage_found) {
|
|
list[*num_extensions + i] =
|
|
util_strdup(KMIP_CERT_EXT_KEY_USAGE_CLIENT_AUTH);
|
|
i++;
|
|
}
|
|
|
|
if (!altname_found) {
|
|
list[*num_extensions + i] = NULL;
|
|
util_asprintf(&list[*num_extensions + i],
|
|
KMIP_CERT_EXT_SUBJECT_ALT_NAME_DNS,
|
|
utsname.nodename);
|
|
i++;
|
|
}
|
|
|
|
*extension_list = list;
|
|
*num_extensions = elements;
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* Generates certificate signing request or self-signed certificate using the
|
|
* identity key
|
|
*
|
|
* @param ph the plugin handle
|
|
* @param csr_pem_file name of the PEM file to store a CSR to. NULL if no
|
|
* CSR is to be generated.
|
|
* @param sscert_pem_file name of the PEM file to store a self-signed
|
|
* certificate to. NULL if no certificate is to be
|
|
* generated.
|
|
* @param subject the subject RNDs separated by semicolon (;). Can be
|
|
* NULL if a renew certificate is specified.
|
|
* @param extensions the extensions separated by semicolon (;). Can be
|
|
* NULL.
|
|
* @param renew_cert_pem_file name of a PEM file containing a certificate to
|
|
* renew. Can be NULL.
|
|
* @param csr_new_header if true output NEW header and footer lines in CSR
|
|
* @param validity_days the number of days the certificate is valid. Only
|
|
* valid when generating a self-signed certificate.
|
|
* Can be NULL.
|
|
* @param digest the digest to use with CSR and certificates. Can be
|
|
* NULL
|
|
* @param rsa_pss if true, RSA-PSS is used with RSA-based identity
|
|
* keys
|
|
*
|
|
* @returns 0 on success, a negative errno in case of an error.
|
|
*/
|
|
static int _generate_csr_sscert(struct plugin_handle *ph,
|
|
const char *csr_pem_file,
|
|
const char *sscert_pem_file,
|
|
const char *subject, const char *extensions,
|
|
const char *renew_cert_pem_file,
|
|
bool csr_new_header, const char *validity_days,
|
|
const char *digest, bool rsa_pss)
|
|
{
|
|
struct sk_rsa_pss_params rsa_pss_parms = {
|
|
.salt_len = RSA_PSS_SALTLEN_DIGEST, .mgf_digest_nid = 0 };
|
|
unsigned char identity_key[KMIP_MAX_KEY_TOKEN_SIZE] = { 0 };
|
|
size_t identity_key_size = sizeof(identity_key);
|
|
char **subject_rdn_list = NULL;
|
|
char **extension_list = NULL;
|
|
size_t num_subject_rdns = 0;
|
|
int digest_nid = NID_undef;
|
|
size_t num_extensions = 0;
|
|
char *client_cert = NULL;
|
|
X509 *renew_cert = NULL;
|
|
const char *cert_algo;
|
|
X509_REQ *csr = NULL;
|
|
X509 *ss_cert = NULL;
|
|
int days = 30;
|
|
int rc = 0;
|
|
size_t i;
|
|
|
|
_check_config_complete(ph);
|
|
|
|
if (!ph->apqns_configured) {
|
|
_set_error(ph, "The configuration is incomplete, you must "
|
|
"first configure the APQNs used with this plugin.");
|
|
return -EINVAL;
|
|
}
|
|
if (!ph->identity_key_generated) {
|
|
_set_error(ph, "The configuration is incomplete, you must "
|
|
"first generate the identity key.");
|
|
return -EINVAL;
|
|
}
|
|
|
|
if (csr_pem_file != NULL && sscert_pem_file != NULL) {
|
|
_set_error(ph, "Either '--gen-csr' or option "
|
|
"'--gen-self-signed-cert' can be specified.");
|
|
return -EINVAL;
|
|
}
|
|
if (csr_new_header && csr_pem_file == NULL) {
|
|
_set_error(ph, "Option '--csr-new-header' is only valid with "
|
|
"option '--gen-csr'.");
|
|
return -EINVAL;
|
|
}
|
|
if (validity_days != NULL && sscert_pem_file == NULL) {
|
|
_set_error(ph, "Option '--cert-validity-days' is only valid "
|
|
"with option '--gen-self-signed-cert'.");
|
|
return -EINVAL;
|
|
}
|
|
if (subject == NULL && renew_cert_pem_file == NULL) {
|
|
_set_error(ph, "Option '--cert-subject' is required, unless "
|
|
" option '--renew-cert' is specified.");
|
|
return -EINVAL;
|
|
}
|
|
|
|
if (sscert_pem_file != NULL && ph->client_cert_avail) {
|
|
printf("ATTENTION: A client certificate already exists\n");
|
|
util_print_indented("When you generate a new client "
|
|
"certificate, the existing certificate is "
|
|
"removed and must re-register the newly "
|
|
"created certificate with the KMIP server "
|
|
"and the KMIP plugin before you can "
|
|
"communicate with the KMIP server", 0);
|
|
printf("%s: Re-generate the client certificate [y/N]? ",
|
|
program_invocation_short_name);
|
|
if (!prompt_for_yes(ph->pd.verbose)) {
|
|
_set_error(ph, "Operation aborted by user");
|
|
return -ECANCELED;
|
|
}
|
|
}
|
|
|
|
if (validity_days != NULL) {
|
|
days = atoi(validity_days);
|
|
if (days <= 0) {
|
|
_set_error(ph, "Invalid validity days: '%s'",
|
|
validity_days);
|
|
return -EINVAL;
|
|
}
|
|
}
|
|
|
|
if (digest != NULL) {
|
|
digest_nid = OBJ_txt2nid(digest);
|
|
if (digest_nid == NID_undef) {
|
|
_set_error(ph, "Invalid digest: '%s'", digest);
|
|
return -EINVAL;
|
|
}
|
|
}
|
|
|
|
if (subject != NULL) {
|
|
rc = parse_list(subject, &subject_rdn_list, &num_subject_rdns);
|
|
if (rc != 0)
|
|
goto out;
|
|
}
|
|
|
|
if (extensions != NULL) {
|
|
rc = parse_list(extensions, &extension_list, &num_extensions);
|
|
if (rc != 0)
|
|
goto out;
|
|
}
|
|
|
|
if (renew_cert_pem_file != NULL) {
|
|
rc = SK_UTIL_read_x509_certificate(renew_cert_pem_file,
|
|
&renew_cert);
|
|
if (rc != 0) {
|
|
_set_error(ph, "Failed to load the renew certificate "
|
|
"from'%s'", renew_cert_pem_file);
|
|
goto out;
|
|
}
|
|
}
|
|
|
|
rc = _add_client_auth_extensions(ph, &extension_list, &num_extensions,
|
|
renew_cert != NULL ?
|
|
X509_get0_extensions(renew_cert)
|
|
: NULL);
|
|
if (rc != 0)
|
|
goto out;
|
|
|
|
rc = _setup_ext_lib(ph);
|
|
if (rc != 0)
|
|
goto out;
|
|
|
|
rc = SK_UTIL_read_key_blob(ph->identity_secure_key, identity_key,
|
|
&identity_key_size);
|
|
if (rc != 0) {
|
|
_set_error(ph, "Failed to load the identity key from '%s': %s",
|
|
ph->identity_secure_key, strerror(-rc));
|
|
goto out;
|
|
}
|
|
|
|
if (csr_pem_file != NULL) {
|
|
rc = SK_OPENSSL_generate_csr(identity_key, identity_key_size,
|
|
(const char **)subject_rdn_list,
|
|
num_subject_rdns, true, renew_cert,
|
|
(const char **)extension_list,
|
|
num_extensions, digest_nid,
|
|
rsa_pss ? &rsa_pss_parms : NULL,
|
|
&csr, &ph->ext_lib, ph->pd.verbose);
|
|
} else {
|
|
rc = SK_OPENSSL_generate_ss_cert(identity_key,
|
|
identity_key_size,
|
|
(const char **)subject_rdn_list,
|
|
num_subject_rdns, true,
|
|
renew_cert,
|
|
(const char **)extension_list,
|
|
num_extensions, days,
|
|
digest_nid,
|
|
rsa_pss ? &rsa_pss_parms :
|
|
NULL,
|
|
&ss_cert, &ph->ext_lib,
|
|
ph->pd.verbose);
|
|
}
|
|
switch (rc) {
|
|
case 0:
|
|
break;
|
|
case -EBADMSG:
|
|
_set_error(ph, "The subject or extensions could not be parsed "
|
|
"or are not recognized by OpenSSL.");
|
|
rc = -EINVAL;
|
|
goto out;
|
|
case -EEXIST:
|
|
_set_error(ph, "One of the subject name entries or extensions "
|
|
"is a duplicate.");
|
|
rc = -EINVAL;
|
|
goto out;
|
|
case -ENOTSUP:
|
|
_set_error(ph, "The specified digest is not supported.");
|
|
rc = -EINVAL;
|
|
goto out;
|
|
default:
|
|
_set_error(ph, "Failed to generate the %s: %s",
|
|
csr_pem_file != NULL ? "certificate signing request"
|
|
: "self-signed certificate",
|
|
strerror(-rc));
|
|
goto out;
|
|
}
|
|
|
|
if (csr_pem_file != NULL) {
|
|
rc = SK_UTIL_write_x509_request(csr_pem_file, csr,
|
|
csr_new_header);
|
|
if (rc != 0) {
|
|
_set_error(ph, "Failed to write the certificate "
|
|
"signing request to '%s'", csr_pem_file);
|
|
goto out;
|
|
}
|
|
|
|
pr_verbose(&ph->pd, "Generated certificate signing request "
|
|
"into '%s'", csr_pem_file);
|
|
} else {
|
|
switch (EVP_PKEY_id(X509_get0_pubkey(ss_cert))) {
|
|
case EVP_PKEY_RSA:
|
|
cert_algo = KMIP_KEY_ALGORITHM_RSA;
|
|
break;
|
|
case EVP_PKEY_RSA_PSS:
|
|
cert_algo = KMIP_KEY_ALGORITHM_RSA_PSS;
|
|
rsa_pss = true;
|
|
break;
|
|
case EVP_PKEY_EC:
|
|
cert_algo = KMIP_KEY_ALGORITHM_ECC;
|
|
break;
|
|
default:
|
|
_set_error(ph, "Unsupported certificate algorithm");
|
|
rc = -EINVAL;
|
|
goto out;
|
|
}
|
|
|
|
rc = SK_UTIL_write_x509_certificate(sscert_pem_file, ss_cert);
|
|
if (rc != 0) {
|
|
_set_error(ph, "Failed to write the self-signed "
|
|
"certificate to '%s'", sscert_pem_file);
|
|
goto out;
|
|
}
|
|
|
|
util_asprintf(&client_cert, "%s/%s", ph->pd.config_path,
|
|
KMIP_CONFIG_CLIENT_CERTIFICATE_FILE);
|
|
|
|
rc = plugin_set_or_remove_property(&ph->pd,
|
|
KMIP_CONFIG_CLIENT_CERTIFICATE, client_cert);
|
|
if (rc != 0)
|
|
goto out;
|
|
|
|
rc = plugin_set_or_remove_property(&ph->pd,
|
|
KMIP_CONFIG_CLIENT_CERT_ALGORITHM, cert_algo);
|
|
if (rc != 0)
|
|
goto out;
|
|
|
|
rc = SK_UTIL_write_x509_certificate(client_cert, ss_cert);
|
|
if (rc != 0) {
|
|
_set_error(ph, "Failed to write the self-signed "
|
|
"certificate to '%s'", client_cert);
|
|
goto out;
|
|
}
|
|
|
|
pr_verbose(&ph->pd, "Generated self-signed certificate into "
|
|
"'%s' and '%s'", sscert_pem_file, client_cert);
|
|
}
|
|
|
|
out:
|
|
if (subject_rdn_list != NULL) {
|
|
for (i = 0; i < num_subject_rdns; i++)
|
|
free(subject_rdn_list[i]);
|
|
free(subject_rdn_list);
|
|
}
|
|
if (extension_list != NULL) {
|
|
for (i = 0; i < num_extensions; i++)
|
|
free(extension_list[i]);
|
|
free(extension_list);
|
|
}
|
|
if (renew_cert != NULL)
|
|
X509_free(renew_cert);
|
|
if (ss_cert != NULL)
|
|
X509_free(ss_cert);
|
|
if (csr != NULL)
|
|
X509_REQ_free(csr);
|
|
if (client_cert != NULL)
|
|
free(client_cert);
|
|
|
|
return rc;
|
|
}
|
|
|
|
/**
|
|
* Checks that none of the options for generating a CSR or self-signed
|
|
* certificate is specified, and sets up the error message and return code if
|
|
* so.
|
|
*
|
|
* @param ph the plugin handle
|
|
* @param opts the config options structure
|
|
*
|
|
* @returns 0 on success, a negative errno in case of an error.
|
|
*/
|
|
static int _error_gen_csr_sscert_opts(struct plugin_handle *ph,
|
|
struct config_options *opts)
|
|
{
|
|
int rc = 0;
|
|
|
|
if (opts->cert_subject != NULL) {
|
|
_set_error(ph, "Option '--cert-subject' is only valid "
|
|
"together with options '--gen-csr' or "
|
|
"'--gen-self-signed-cert'.");
|
|
rc = -EINVAL;
|
|
goto out;
|
|
}
|
|
if (opts->cert_extensions != NULL) {
|
|
_set_error(ph, "Option '--cert-extensions' is only "
|
|
"valid together with options '--gen-csr' or "
|
|
"'--gen-self-signed-cert'.");
|
|
rc = -EINVAL;
|
|
goto out;
|
|
}
|
|
if (opts->renew_cert_pem_file != NULL) {
|
|
_set_error(ph, "Option '--renew-cert' is only "
|
|
"valid together with options '--gen-csr' or "
|
|
"'--gen-self-signed-cert'.");
|
|
rc = -EINVAL;
|
|
goto out;
|
|
}
|
|
if (opts->csr_new_header == true) {
|
|
_set_error(ph, "Option '--csr-new-header' is only "
|
|
"valid together with option '--gen-csr'.");
|
|
rc = -EINVAL;
|
|
goto out;
|
|
}
|
|
if (opts->cert_validity_days != NULL) {
|
|
_set_error(ph, "Option '--cert-validity-days' is only "
|
|
"valid together with option "
|
|
"'--gen-self-signed-cert'.");
|
|
rc = -EINVAL;
|
|
goto out;
|
|
}
|
|
if (opts->cert_digest != NULL) {
|
|
_set_error(ph, "Option '--cert-digest' is only "
|
|
"valid together with options '--gen-csr' or "
|
|
"'--gen-self-signed-cert'.");
|
|
rc = -EINVAL;
|
|
goto out;
|
|
}
|
|
if (opts->cert_rsa_pss == true) {
|
|
_set_error(ph, "Option '--cert-rsa-pss' is only "
|
|
"valid together with option '--gen-csr' or "
|
|
"'--gen-self-signed-cert'");
|
|
rc = -EINVAL;
|
|
goto out;
|
|
}
|
|
|
|
out:
|
|
return rc;
|
|
}
|
|
|
|
/**
|
|
* Connects to the KMIP server
|
|
*
|
|
* @param ph the plugin handle
|
|
*
|
|
* @returns 0 on success, a negative errno in case of an error.
|
|
*/
|
|
static int _connect_to_server(struct plugin_handle *ph)
|
|
{
|
|
int rc;
|
|
|
|
if (ph->connection != NULL)
|
|
kmip_connection_free(ph->connection);
|
|
ph->connection = NULL;
|
|
|
|
rc = kmip_connection_new(&ph->kmip_config, &ph->connection,
|
|
ph->pd.verbose);
|
|
if (rc != 0) {
|
|
_set_error(ph, "Failed to connect to KMIP server at '%s': "
|
|
"%s", ph->kmip_config.server, strerror(-rc));
|
|
return rc;
|
|
}
|
|
|
|
if (ph->kmip_version.major == 0)
|
|
ph->kmip_version = ph->profile->kmip_version;
|
|
|
|
pr_verbose(&ph->pd, "Protocol version: %u.%u", ph->kmip_version.major,
|
|
ph->kmip_version.minor);
|
|
kmip_set_default_protocol_version(&ph->kmip_version);
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* Returns the name of the enumeration value.
|
|
*
|
|
* @param values the list of enumeration values
|
|
* @param value the value
|
|
*
|
|
* @returns a constant string
|
|
*/
|
|
static const char *_enum_value_to_str(const struct kmip_enum_name *values,
|
|
uint32_t value)
|
|
{
|
|
unsigned int i;
|
|
|
|
for (i = 0; values[i].name != NULL; i++) {
|
|
if (values[i].value == value)
|
|
return values[i].name;
|
|
}
|
|
|
|
return "UNKNOWN";
|
|
}
|
|
|
|
/**
|
|
* Check a KMIP response and extract information from it.
|
|
*
|
|
* @param ph the plugin handle
|
|
* @param resp the response KMIP node
|
|
* @param batch_item the batch item index (staring at 0)
|
|
* @param operation the operation (to verify the batch item)
|
|
* @param payload On return : the payload of this batch item
|
|
*
|
|
* @returns 0 on success, a negative errno in case of an error.
|
|
*/
|
|
static int _check_kmip_response(struct plugin_handle *ph,
|
|
struct kmip_node *resp, int32_t batch_item,
|
|
enum kmip_operation operation,
|
|
struct kmip_node **payload)
|
|
{
|
|
struct kmip_node *resp_hdr = NULL, *resp_bi = NULL;
|
|
enum kmip_result_status status = 0;
|
|
enum kmip_result_reason reason = 0;
|
|
const char *message = NULL;
|
|
int32_t batch_count;
|
|
int rc;
|
|
|
|
rc = kmip_get_response(resp, &resp_hdr, 0, NULL);
|
|
CHECK_ERROR(rc != 0, rc, rc, "Get KMIP response header failed",
|
|
ph, out);
|
|
|
|
rc = kmip_get_response_header(resp_hdr, NULL, NULL, NULL, NULL,
|
|
&batch_count);
|
|
CHECK_ERROR(rc != 0, rc, rc, "Get KMIP response header infos failed",
|
|
ph, out);
|
|
CHECK_ERROR(batch_item >= batch_count, rc, -EBADMSG,
|
|
"Response contains less batch items than expected",
|
|
ph, out);
|
|
|
|
rc = kmip_get_response(resp, NULL, batch_item, &resp_bi);
|
|
CHECK_ERROR(rc != 0, rc, rc, "Get KMIP response batch item failed",
|
|
ph, out);
|
|
|
|
rc = kmip_get_response_batch_item(resp_bi, NULL, NULL, NULL, &status,
|
|
&reason, &message, NULL, NULL,
|
|
payload);
|
|
CHECK_ERROR(rc != 0, rc, rc, "Get KMIP response status infos failed",
|
|
ph, out);
|
|
|
|
pr_verbose(&ph->pd, "KMIP response, operation: %d, status: %d, "
|
|
"reason: %d message: '%s'", operation, status, reason,
|
|
message ? message : "(none)");
|
|
|
|
if (status != KMIP_RESULT_STATUS_SUCCESS) {
|
|
_set_error(ph, "KMIP Request failed: Operation: '%s', "
|
|
"Status: '%s', Reason: '%s', Message: '%s'",
|
|
_enum_value_to_str(required_operations, operation),
|
|
_enum_value_to_str(kmip_result_statuses, status),
|
|
_enum_value_to_str(kmip_result_reasons, reason),
|
|
message ? message : "(none)");
|
|
rc = -EBADMSG;
|
|
goto out;
|
|
}
|
|
out:
|
|
kmip_node_free(resp_hdr);
|
|
kmip_node_free(resp_bi);
|
|
|
|
return rc;
|
|
}
|
|
|
|
/**
|
|
* Build a KMIP request with the up to 2 operations and payloads
|
|
*
|
|
* @param ph the plugin handle
|
|
* @param operation1 The 1st operation to perform
|
|
* @param req_pl1 the request payload of the 1st operation
|
|
* @param operation2 The 2nd operation to perform (or 0)
|
|
* @param req_pl2 the request payload of the 2nd operation (or NULL)
|
|
* @param req On return: the created request.
|
|
* @param batch_err_opt Batch error option
|
|
*
|
|
* @returns 0 on success, a negative errno in case of an error.
|
|
*/
|
|
static int _build_kmip_request2(struct plugin_handle *ph,
|
|
enum kmip_operation operation1,
|
|
struct kmip_node *req_pl1,
|
|
enum kmip_operation operation2,
|
|
struct kmip_node *req_pl2,
|
|
struct kmip_node **req,
|
|
enum kmip_batch_error_cont_option batch_err_opt)
|
|
{
|
|
struct kmip_node *req_bi1 = NULL, *req_bi2 = NULL, *req_hdr = NULL;
|
|
int rc = 0;
|
|
|
|
req_bi1 = kmip_new_request_batch_item(operation1, NULL, 0, req_pl1);
|
|
CHECK_ERROR(req_bi1 == NULL, rc, -ENOMEM, "Allocate KMIP node failed",
|
|
ph, out);
|
|
|
|
if (operation2 != 0) {
|
|
req_bi2 = kmip_new_request_batch_item(operation2, NULL, 0,
|
|
req_pl2);
|
|
CHECK_ERROR(req_bi2 == NULL, rc, -ENOMEM,
|
|
"Allocate KMIP node failed", ph, out);
|
|
}
|
|
|
|
req_hdr = kmip_new_request_header(NULL, 0, NULL, NULL, false, NULL,
|
|
batch_err_opt, true,
|
|
operation2 != 0 ? 2 : 1);
|
|
CHECK_ERROR(req_hdr == NULL, rc, -ENOMEM, "Allocate KMIP node failed",
|
|
ph, out);
|
|
|
|
*req = kmip_new_request_va(req_hdr, 2, req_bi1, req_bi2);
|
|
CHECK_ERROR(*req == NULL, rc, -ENOMEM, "Allocate KMIP node failed",
|
|
ph, out);
|
|
|
|
out:
|
|
kmip_node_free(req_bi1);
|
|
kmip_node_free(req_bi2);
|
|
kmip_node_free(req_hdr);
|
|
|
|
return rc;
|
|
}
|
|
|
|
/**
|
|
* Perform a KMIP request with up to 2 operations and payloads.
|
|
* Returns the response payloads.
|
|
*
|
|
* @param ph the plugin handle
|
|
* @param operation1 The 1st operation to perform
|
|
* @param req_pl1 the request payload if the 1st operation
|
|
* @param resp_pl 1 On return: the response payload.
|
|
* @param operation2 The 2nd operation to perform (or zero)
|
|
* @param req_pl2 the request payload of the 2nd operation (or NULL)
|
|
* @param resp_pl2 On return: the response payload.
|
|
* @param batch_err_opt Batch error option
|
|
*
|
|
* @returns 0 on success, a negative errno in case of an error.
|
|
*/
|
|
static int _perform_kmip_request2(struct plugin_handle *ph,
|
|
enum kmip_operation operation1,
|
|
struct kmip_node *req_pl1,
|
|
struct kmip_node **resp_pl1,
|
|
enum kmip_operation operation2,
|
|
struct kmip_node *req_pl2,
|
|
struct kmip_node **resp_pl2,
|
|
enum kmip_batch_error_cont_option batch_err_opt)
|
|
{
|
|
struct kmip_node *req = NULL, *resp = NULL;
|
|
int rc;
|
|
|
|
if (operation2 != 0)
|
|
pr_verbose(&ph->pd, "Perform KMIP request, operations: %d, %d",
|
|
operation1, operation2);
|
|
else
|
|
pr_verbose(&ph->pd, "Perform KMIP request, operation: %d",
|
|
operation1);
|
|
|
|
|
|
rc = _build_kmip_request2(ph, operation1, req_pl1, operation2, req_pl2,
|
|
&req, batch_err_opt);
|
|
if (rc != 0)
|
|
goto out;
|
|
|
|
rc = kmip_connection_perform(ph->connection, req, &resp,
|
|
ph->pd.verbose);
|
|
if (rc != 0) {
|
|
_set_error(ph, "Failed to perform KMIP request: %s",
|
|
strerror(-rc));
|
|
}
|
|
|
|
rc = _check_kmip_response(ph, resp, 0, operation1, resp_pl1);
|
|
if (rc != 0 && batch_err_opt == KMIP_BATCH_ERR_CONT_CONTINUE &&
|
|
operation2 != 0) {
|
|
rc = 0;
|
|
plugin_clear_error(&ph->pd);
|
|
}
|
|
if (rc != 0)
|
|
goto out;
|
|
|
|
if (operation2 != 0) {
|
|
rc = _check_kmip_response(ph, resp, 1, operation2, resp_pl2);
|
|
if (rc != 0)
|
|
goto out;
|
|
}
|
|
|
|
out:
|
|
kmip_node_free(req);
|
|
kmip_node_free(resp);
|
|
|
|
return rc;
|
|
}
|
|
|
|
/**
|
|
* Perform a KMIP request with the specified operation and payload. Returns the
|
|
* response payload.
|
|
*
|
|
* @param ph the plugin handle
|
|
* @param operation The operation to perform
|
|
* @param req_pl the request payload
|
|
* @param resp_pl On return: the response payload.
|
|
*
|
|
* @returns 0 on success, a negative errno in case of an error.
|
|
*/
|
|
static int _perform_kmip_request(struct plugin_handle *ph,
|
|
enum kmip_operation operation,
|
|
struct kmip_node *req_pl,
|
|
struct kmip_node **resp_pl)
|
|
{
|
|
return _perform_kmip_request2(ph, operation, req_pl, resp_pl, 0, NULL,
|
|
NULL, KMIP_BATCH_ERR_CONT_STOP);
|
|
}
|
|
|
|
|
|
/**
|
|
* Checks if all required enumeration values are contained in the query
|
|
* response payload
|
|
*
|
|
* @param ph the plugin handle
|
|
* @param query_function the query function to check
|
|
* @param enum_name the enumeration name (for error message)
|
|
* @param required the list of required values
|
|
* @param query_pl the QUERY response payload node
|
|
*
|
|
* @returns 0 on success, a negative errno in case of an error.
|
|
*/
|
|
static int _check_required_enum_values(struct plugin_handle *ph,
|
|
enum kmip_query_function query_function,
|
|
const char *enum_name,
|
|
const struct kmip_enum_name *required,
|
|
struct kmip_node *query_pl)
|
|
{
|
|
struct kmip_node *info = NULL;
|
|
unsigned int i, k;
|
|
bool found;
|
|
int rc;
|
|
|
|
for (i = 0; required[i].value != 0; i++) {
|
|
for (k = 0, found = false; !found; k++) {
|
|
rc = kmip_get_query_response_payload(query_pl,
|
|
query_function, NULL, k, &info);
|
|
if (rc != 0)
|
|
break;
|
|
|
|
if (kmip_node_get_enumeration(info) ==
|
|
required[i].value)
|
|
found = true;
|
|
kmip_node_free(info);
|
|
}
|
|
|
|
if (!found) {
|
|
_set_error(ph, "KMIP server does not support required "
|
|
"%s '%s'", enum_name, required[i].name);
|
|
return -EINVAL;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* Queries the KMIP server, checks if it supports all required features,
|
|
* and returns the server information string.
|
|
*
|
|
* @param ph the plugin handle
|
|
* @param server_info On return : the server information string. Must be
|
|
* freed by the caller
|
|
*
|
|
* @returns 0 on success, a negative errno in case of an error.
|
|
*/
|
|
static int _check_kmip_server(struct plugin_handle *ph, char **server_info)
|
|
{
|
|
struct kmip_node *req_pl = NULL, *resp_pl = NULL, *serv_info = NULL;
|
|
const char *info;
|
|
int rc = 0;
|
|
|
|
req_pl = kmip_new_query_request_payload_va(3, KMIP_QUERY_OPERATIONS,
|
|
KMIP_QUERY_OBJECTS, KMIP_QUERY_SERVER_INFORMATION);
|
|
CHECK_ERROR(req_pl == NULL, rc, -ENOMEM, "Allocate KMIP node failed",
|
|
ph, out);
|
|
|
|
rc = _perform_kmip_request(ph, KMIP_OPERATION_QUERY, req_pl, &resp_pl);
|
|
if (rc != 0)
|
|
goto out;
|
|
|
|
rc = _check_required_enum_values(ph, KMIP_QUERY_OPERATIONS, "operation",
|
|
required_operations, resp_pl);
|
|
if (rc != 0)
|
|
goto out;
|
|
|
|
rc = _check_required_enum_values(ph, KMIP_QUERY_OBJECTS, "object type",
|
|
required_objtypes, resp_pl);
|
|
if (rc != 0)
|
|
goto out;
|
|
|
|
rc = kmip_get_query_response_payload(resp_pl,
|
|
KMIP_QUERY_SERVER_INFORMATION,
|
|
NULL, 0, &serv_info);
|
|
CHECK_ERROR(rc != 0, rc, rc, "Failed to get server version",
|
|
ph, out);
|
|
|
|
info = kmip_node_get_text_string(serv_info);
|
|
CHECK_ERROR(info == NULL, rc, -EBADMSG, "Failed to get server version",
|
|
ph, out);
|
|
|
|
pr_verbose(&ph->pd, "Server info: '%s'", info);
|
|
|
|
*server_info = util_strdup(info);
|
|
|
|
out:
|
|
kmip_node_free(req_pl);
|
|
kmip_node_free(resp_pl);
|
|
kmip_node_free(serv_info);
|
|
|
|
return rc;
|
|
}
|
|
|
|
/**
|
|
* Discovers the KMIP protocol versions that the KMIP server supports
|
|
*
|
|
* @param ph the plugin handle
|
|
* @param version On return : the highest KMIP version that the server
|
|
* and the KMIP client supports
|
|
*
|
|
* @returns 0 on success, a negative errno in case of an error.
|
|
*/
|
|
static int _discover_kmip_versions(struct plugin_handle *ph,
|
|
struct kmip_version *version)
|
|
{
|
|
struct kmip_node *req_pl = NULL, *resp_pl = NULL;
|
|
int rc = 0;
|
|
|
|
req_pl = kmip_new_discover_versions_payload(-1, NULL);
|
|
CHECK_ERROR(req_pl == NULL, rc, -ENOMEM, "Allocate KMIP node failed",
|
|
ph, out);
|
|
|
|
rc = _perform_kmip_request(ph, KMIP_OPERATION_DISCOVER_VERSIONS,
|
|
req_pl, &resp_pl);
|
|
if (rc != 0)
|
|
goto out;
|
|
|
|
rc = kmip_get_discover_versions_response_payload(resp_pl, NULL, 0,
|
|
version);
|
|
CHECK_ERROR(rc != 0, rc, rc, "Failed to get discover version response",
|
|
ph, out);
|
|
|
|
out:
|
|
kmip_node_free(req_pl);
|
|
kmip_node_free(resp_pl);
|
|
|
|
return rc;
|
|
}
|
|
|
|
/**
|
|
* Configures the connection to the KMIP server
|
|
*
|
|
* @param ph the plugin handle
|
|
* @param kmip_server the KMIP server
|
|
* @param profil the profile to use
|
|
* @param tls_ca_bundle the file or directory name of the CA bundle to use
|
|
* @param tls_pin_server_pubkey if true, pin the server public key
|
|
* @param tls_trust_server_cert if true, trust the server certificate
|
|
* @param tls_dont_verify_server_cert if true, don't verify the server cert
|
|
* @param tls_verify_hostname if true verify the server's hostname
|
|
*
|
|
* @returns 0 on success, a negative errno in case of an error.
|
|
*/
|
|
static int _configure_connection(struct plugin_handle *ph,
|
|
const char *kmip_server,
|
|
const char *profile,
|
|
const char *tls_ca_bundle,
|
|
bool tls_pin_server_pubkey,
|
|
bool tls_trust_server_cert,
|
|
bool tls_dont_verify_server_cert,
|
|
bool tls_verify_hostname)
|
|
{
|
|
char *server_pubkey_temp = NULL;
|
|
char *server_pubkey_file = NULL;
|
|
char *server_cert_file = NULL;
|
|
char *server_cert_temp = NULL;
|
|
char *server_info = NULL;
|
|
bool self_signed = false;
|
|
bool verified = false;
|
|
bool valid = false;
|
|
char *file_name;
|
|
char tmp[50];
|
|
int rc;
|
|
|
|
if (tls_pin_server_pubkey && tls_trust_server_cert) {
|
|
_set_error(ph, "Option ' --tls-pin-server-pubkey' is not valid "
|
|
"together with option '--tls-pin-server-cert");
|
|
return -EINVAL;
|
|
}
|
|
|
|
if (!ph->apqns_configured) {
|
|
_set_error(ph, "The configuration is incomplete, you must "
|
|
"first configure the APQNs used with this plugin.");
|
|
return -EINVAL;
|
|
}
|
|
if (!ph->identity_key_generated) {
|
|
_set_error(ph, "The configuration is incomplete, you must "
|
|
"first generate the identity key.");
|
|
return -EINVAL;
|
|
}
|
|
if (!ph->client_cert_avail) {
|
|
_set_error(ph, "The configuration is incomplete, you must "
|
|
"first register the client certificate.");
|
|
return -EINVAL;
|
|
}
|
|
|
|
if (ph->server != NULL) {
|
|
util_print_indented("ATTENTION: The KMIP server connection "
|
|
"is already configured\n"
|
|
"When you re-configure the KMIP server "
|
|
"connection, you might need to re-register "
|
|
"this zkey client with the changed KMIP "
|
|
"server.", 0);
|
|
printf("%s: Re-configure the KMIP server connection [y/N]? ",
|
|
program_invocation_short_name);
|
|
if (!prompt_for_yes(ph->pd.verbose)) {
|
|
_set_error(ph, "Operation aborted by user");
|
|
return -ECANCELED;
|
|
}
|
|
}
|
|
|
|
FREE_AND_SET_NULL(ph->server);
|
|
ph->server = util_strdup(kmip_server);
|
|
|
|
if (ph->profile != NULL)
|
|
profile_free(ph->profile);
|
|
ph->profile = NULL;
|
|
|
|
rc = profile_find_by_name(ph, profile != NULL ? profile :
|
|
KMIP_PROFILES_DEFAULT_PROFILE_NAME,
|
|
&ph->profile);
|
|
if (rc != 0)
|
|
return rc;
|
|
|
|
if (ph->profile->kmip_version.major != 0)
|
|
ph->kmip_version = ph->profile->kmip_version;
|
|
else
|
|
ph->kmip_version = kmip_version_1_0;
|
|
|
|
rc = plugin_set_or_remove_property(&ph->pd, KMIP_CONFIG_SERVER,
|
|
ph->server);
|
|
if (rc != 0)
|
|
return rc;
|
|
|
|
rc = plugin_set_or_remove_property(&ph->pd, KMIP_CONFIG_PROFILE,
|
|
profile);
|
|
if (rc != 0)
|
|
return rc;
|
|
|
|
rc = plugin_set_or_remove_property(&ph->pd, KMIP_CONFIG_CA_BUNDLE,
|
|
tls_ca_bundle);
|
|
if (rc != 0)
|
|
return rc;
|
|
|
|
/* Establish initial KMIP config */
|
|
_free_kmip_config(ph);
|
|
rc = _get_kmip_config(ph);
|
|
if (rc != 0)
|
|
return rc;
|
|
|
|
/* Connect to the server the 1st time to get its certificate */
|
|
util_asprintf(&server_cert_temp, "%s/%s-tmp", ph->pd.config_path,
|
|
KMIP_CONFIG_SERVER_CERT_FILE);
|
|
util_asprintf(&server_pubkey_temp, "%s/%s-tmp", ph->pd.config_path,
|
|
KMIP_CONFIG_SERVER_PUBKEY_FILE);
|
|
|
|
rc = kmip_connection_get_server_cert(ph->kmip_config.server,
|
|
ph->kmip_config.transport,
|
|
ph->kmip_config.tls_ca,
|
|
ph->kmip_config.tls_client_key,
|
|
ph->kmip_config.tls_client_cert,
|
|
server_cert_temp,
|
|
server_pubkey_temp,
|
|
NULL, &verified, ph->pd.verbose);
|
|
if (rc != 0) {
|
|
_set_error(ph, "Failed to connect to KMIP server at '%s': "
|
|
"%s", ph->kmip_config.server, strerror(-rc));
|
|
goto out;
|
|
}
|
|
|
|
rc = plugin_check_certificate(&ph->pd, server_cert_temp, &self_signed,
|
|
&valid);
|
|
if (rc != 0) {
|
|
_set_error(ph, "Failed to check certificate PEM file '%s': %s",
|
|
server_cert_temp, strerror(-rc));
|
|
goto out;
|
|
}
|
|
|
|
pr_verbose(&ph->pd, "verified: %d", verified);
|
|
pr_verbose(&ph->pd, "self-signed: %d", self_signed);
|
|
pr_verbose(&ph->pd, "valid: %d", valid);
|
|
|
|
util_print_indented("The KMIP server presented the following "
|
|
"certificate to identify itself:", 0);
|
|
|
|
rc = plugin_print_certificates(&ph->pd, server_cert_temp);
|
|
if (rc != 0) {
|
|
_set_error(ph, "Failed to print the server certificate: %s",
|
|
strerror(-rc));
|
|
goto out;
|
|
}
|
|
|
|
printf("\n");
|
|
if (!valid)
|
|
printf("ATTENTION: The certificate is expired or not yet "
|
|
"valid.\n");
|
|
if (self_signed) {
|
|
printf("ATTENTION: The certificate is self-signed "
|
|
"and thus could not be verified.\n");
|
|
} else if (!verified) {
|
|
if (!tls_dont_verify_server_cert) {
|
|
if (tls_ca_bundle != NULL)
|
|
_set_error(ph, "The certificate could not be "
|
|
"verified using the specified CA "
|
|
"bundle '%s'. Use option "
|
|
"'--tls-dont-verify-server-cert' to "
|
|
"connect to this server anyway.",
|
|
tls_ca_bundle);
|
|
else
|
|
_set_error(ph, "The certificate could not be "
|
|
"verified using the system's "
|
|
"CA certificates. Use option "
|
|
"'--tls-dont-verify-server-cert' to "
|
|
"connect to this server anyway.");
|
|
rc = -EINVAL;
|
|
goto out;
|
|
}
|
|
}
|
|
printf("%s: Is this the KMIP server you intend to work with "
|
|
"[y/N]? ", program_invocation_short_name);
|
|
if (!prompt_for_yes(ph->pd.verbose)) {
|
|
_set_error(ph, "Operation aborted by user");
|
|
rc = -ECANCELED;
|
|
goto out;
|
|
}
|
|
|
|
ph->kmip_config.tls_verify_peer = !self_signed || tls_trust_server_cert;
|
|
if (tls_dont_verify_server_cert)
|
|
ph->kmip_config.tls_verify_peer = false;
|
|
|
|
rc = plugin_set_or_remove_property(&ph->pd,
|
|
KMIP_CONFIG_VERIFY_SERVER_CERT,
|
|
ph->kmip_config.tls_verify_peer ?
|
|
"yes" : "no");
|
|
if (rc != 0)
|
|
goto out;
|
|
|
|
ph->kmip_config.tls_verify_host = tls_verify_hostname;
|
|
rc = plugin_set_or_remove_property(&ph->pd,
|
|
KMIP_CONFIG_VERIFY_HOSTNAME,
|
|
ph->kmip_config.tls_verify_host ?
|
|
"yes" : "no");
|
|
if (rc != 0)
|
|
goto out;
|
|
|
|
/* Establish a connection to the server with the initial config */
|
|
rc = _connect_to_server(ph);
|
|
if (rc != 0)
|
|
goto out;
|
|
|
|
rc = _check_kmip_server(ph, &server_info);
|
|
if (rc != 0)
|
|
goto out;
|
|
|
|
rc = plugin_set_or_remove_property(&ph->pd, KMIP_CONFIG_SERVER_INFO,
|
|
server_info);
|
|
if (rc != 0)
|
|
return rc;
|
|
|
|
if (profile == NULL) {
|
|
/* Try to match a profile for the server */
|
|
if (ph->profile != NULL)
|
|
profile_free(ph->profile);
|
|
|
|
rc = profile_find_by_server_info(ph, server_info, &ph->profile);
|
|
if (rc != 0)
|
|
return rc;
|
|
|
|
pr_verbose(&ph->pd, "Profile selected: %s", ph->profile->name);
|
|
|
|
rc = plugin_set_or_remove_property(&ph->pd, KMIP_CONFIG_PROFILE,
|
|
ph->profile->name);
|
|
if (rc != 0)
|
|
return rc;
|
|
|
|
/* re-establish the kmip configuration with the new profile */
|
|
kmip_connection_free(ph->connection);
|
|
ph->connection = NULL;
|
|
|
|
_free_kmip_config(ph);
|
|
rc = _get_kmip_config(ph);
|
|
if (rc != 0)
|
|
return rc;
|
|
|
|
ph->kmip_version = ph->profile->kmip_version;
|
|
|
|
/* re-establish the connection with the new configuration */
|
|
rc = _connect_to_server(ph);
|
|
if (rc != 0)
|
|
goto out;
|
|
}
|
|
|
|
/* discover the KMIP protocol version if not pre-set by the profile */
|
|
if (ph->profile->kmip_version.major == 0) {
|
|
rc = _discover_kmip_versions(ph, &ph->kmip_version);
|
|
if (rc != 0) {
|
|
pr_verbose(&ph->pd, "DISCOVER-VERSION failed, retry "
|
|
"with KMIP v1.2");
|
|
plugin_clear_error(&ph->pd);
|
|
|
|
kmip_set_default_protocol_version(&kmip_version_1_2);
|
|
|
|
rc = _discover_kmip_versions(ph, &ph->kmip_version);
|
|
if (rc != 0) {
|
|
pr_verbose(&ph->pd, "2nd DISCOVER-VERSION "
|
|
"failed, assume KMIP server only "
|
|
"supports v1.0");
|
|
plugin_clear_error(&ph->pd);
|
|
|
|
ph->kmip_version = kmip_version_1_0;
|
|
rc = 0;
|
|
}
|
|
}
|
|
|
|
pr_verbose(&ph->pd, "Discovered protocol version: %u.%u",
|
|
ph->kmip_version.major, ph->kmip_version.minor);
|
|
|
|
kmip_set_default_protocol_version(&ph->kmip_version);
|
|
|
|
sprintf(tmp, "%u.%u", ph->kmip_version.major,
|
|
ph->kmip_version.minor);
|
|
rc = plugin_set_or_remove_property(&ph->pd,
|
|
KMIP_CONFIG_PROTOCOL_VERSION,
|
|
tmp);
|
|
if (rc != 0)
|
|
goto out;
|
|
} else {
|
|
rc = plugin_set_or_remove_property(&ph->pd,
|
|
KMIP_CONFIG_PROTOCOL_VERSION,
|
|
KMIP_CONFIG_PROTOCOL_VERSION_PROFILE);
|
|
if (rc != 0)
|
|
goto out;
|
|
}
|
|
|
|
if (ph->profile->auth_scheme != KMIP_PROFILE_AUTH_TLS_CLIENT_CERT) {
|
|
_set_error(ph, "Unsupported authentication scheme: %d",
|
|
ph->profile->auth_scheme);
|
|
rc = -EINVAL;
|
|
goto out;
|
|
}
|
|
|
|
FREE_AND_SET_NULL(ph->kmip_config.tls_server_cert);
|
|
util_asprintf(&server_cert_file, "%s/%s", ph->pd.config_path,
|
|
KMIP_CONFIG_SERVER_CERT_FILE);
|
|
if (tls_trust_server_cert) {
|
|
ph->kmip_config.tls_server_cert = util_strdup(server_cert_file);
|
|
rc = plugin_activate_temp_file(&ph->pd, server_cert_temp,
|
|
server_cert_file);
|
|
if (rc != 0)
|
|
goto out;
|
|
} else {
|
|
remove(server_cert_file);
|
|
}
|
|
rc = plugin_set_or_remove_property(&ph->pd, KMIP_CONFIG_SERVER_CERT,
|
|
tls_trust_server_cert ?
|
|
server_cert_file : NULL);
|
|
if (rc != 0)
|
|
goto out;
|
|
|
|
FREE_AND_SET_NULL(ph->kmip_config.tls_pinned_pubkey);
|
|
util_asprintf(&server_pubkey_file, "%s/%s", ph->pd.config_path,
|
|
KMIP_CONFIG_SERVER_PUBKEY_FILE);
|
|
if (tls_pin_server_pubkey) {
|
|
ph->kmip_config.tls_pinned_pubkey =
|
|
util_strdup(server_pubkey_file);
|
|
rc = plugin_activate_temp_file(&ph->pd, server_pubkey_temp,
|
|
server_pubkey_file);
|
|
if (rc != 0)
|
|
goto out;
|
|
} else {
|
|
remove(server_pubkey_file);
|
|
}
|
|
rc = plugin_set_or_remove_property(&ph->pd,
|
|
KMIP_CONFIG_SERVER_PUBKEY,
|
|
tls_pin_server_pubkey ?
|
|
server_pubkey_file : NULL);
|
|
if (rc != 0)
|
|
goto out;
|
|
|
|
/* Remove any wrapping key properties from previous configuration */
|
|
file_name = properties_get(ph->pd.properties, KMIP_CONFIG_WRAPPING_KEY);
|
|
if (file_name != NULL) {
|
|
remove(file_name);
|
|
free(file_name);
|
|
}
|
|
file_name = properties_get(ph->pd.properties,
|
|
KMIP_CONFIG_WRAPPING_KEY_REENC);
|
|
if (file_name != NULL) {
|
|
remove(file_name);
|
|
free(file_name);
|
|
}
|
|
rc = plugin_set_or_remove_property(&ph->pd, KMIP_CONFIG_WRAPPING_KEY,
|
|
NULL);
|
|
if (rc != 0)
|
|
goto out;
|
|
rc = plugin_set_or_remove_property(&ph->pd,
|
|
KMIP_CONFIG_WRAPPING_KEY_REENC,
|
|
NULL);
|
|
if (rc != 0)
|
|
goto out;
|
|
rc = plugin_set_or_remove_property(&ph->pd,
|
|
KMIP_CONFIG_WRAPPING_KEY_ALGORITHM,
|
|
NULL);
|
|
if (rc != 0)
|
|
goto out;
|
|
rc = plugin_set_or_remove_property(&ph->pd,
|
|
KMIP_CONFIG_WRAPPING_KEY_PARAMS,
|
|
NULL);
|
|
if (rc != 0)
|
|
goto out;
|
|
rc = plugin_set_or_remove_property(&ph->pd,
|
|
KMIP_CONFIG_WRAPPING_KEY_ID,
|
|
NULL);
|
|
if (rc != 0)
|
|
goto out;
|
|
rc = plugin_set_or_remove_property(&ph->pd,
|
|
KMIP_CONFIG_WRAPPING_KEY_LABEL,
|
|
NULL);
|
|
if (rc != 0)
|
|
goto out;
|
|
|
|
out:
|
|
if (server_cert_temp != NULL) {
|
|
remove(server_cert_temp);
|
|
free(server_cert_temp);
|
|
}
|
|
if (server_cert_file != NULL)
|
|
free(server_cert_file);
|
|
if (server_pubkey_temp != NULL) {
|
|
remove(server_pubkey_temp);
|
|
free(server_pubkey_temp);
|
|
}
|
|
if (server_pubkey_file != NULL)
|
|
free(server_pubkey_file);
|
|
if (server_info != NULL)
|
|
free(server_info);
|
|
|
|
return rc;
|
|
}
|
|
|
|
/**
|
|
* Checks that none of the options for seting up a connection is specified,
|
|
* and sets up the error message and return code if
|
|
* so.
|
|
*
|
|
* @param ph the plugin handle
|
|
* @param opts the config options structure
|
|
*
|
|
* @returns 0 on success, a negative errno in case of an error.
|
|
*/
|
|
static int _error_connection_opts(struct plugin_handle *ph,
|
|
struct config_options *opts)
|
|
{
|
|
int rc = 0;
|
|
|
|
if (opts->profile != NULL) {
|
|
_set_error(ph, "Option '--profile' is only valid "
|
|
"together with option '--kmip-server'.");
|
|
rc = -EINVAL;
|
|
goto out;
|
|
}
|
|
if (opts->tls_ca_bundle != NULL) {
|
|
_set_error(ph, "Option '--tls-ca-bundle' is only valid "
|
|
"together with option '--kmip-server'.");
|
|
rc = -EINVAL;
|
|
goto out;
|
|
}
|
|
if (opts->tls_pin_server_pubkey) {
|
|
_set_error(ph, "Option '--tls-pin-server-pubkey' is only valid "
|
|
"together with option '--kmip-server'.");
|
|
rc = -EINVAL;
|
|
goto out;
|
|
}
|
|
if (opts->tls_trust_server_cert) {
|
|
_set_error(ph, "Option '--tls-trust-server-cert' is only valid "
|
|
"together with option '--kmip-server'.");
|
|
rc = -EINVAL;
|
|
goto out;
|
|
}
|
|
if (opts->tls_dont_verify_server_cert) {
|
|
_set_error(ph, "Option '--tls-dont-verify-server-cert' is only "
|
|
"valid together with option '--kmip-server'.");
|
|
rc = -EINVAL;
|
|
goto out;
|
|
}
|
|
if (opts->tls_verify_hostname) {
|
|
_set_error(ph, "Option '--tls-verify-hostname' is only valid "
|
|
"together with option '--kmip-server'.");
|
|
rc = -EINVAL;
|
|
goto out;
|
|
}
|
|
|
|
out:
|
|
return rc;
|
|
}
|
|
|
|
/**
|
|
* Use a client certificate with the KMIP plugin. The client certificate's
|
|
* public key must match the identity key.
|
|
*
|
|
* @param ph the plugin handle
|
|
* @param client_cert The client certificate to use
|
|
*
|
|
* @returns 0 on success, a negative errno in case of an error.
|
|
*/
|
|
static int _use_client_cert(struct plugin_handle *ph, const char *client_cert)
|
|
{
|
|
unsigned char identity_key[KMIP_MAX_KEY_TOKEN_SIZE] = { 0 };
|
|
size_t identity_key_size = sizeof(identity_key);
|
|
char *client_cert_file = NULL;
|
|
EVP_PKEY *pkey = NULL;
|
|
bool rsa_pss = false;
|
|
X509 *cert = NULL;
|
|
char *cert_algo;
|
|
int rc;
|
|
|
|
_check_config_complete(ph);
|
|
|
|
if (!ph->apqns_configured) {
|
|
_set_error(ph, "The configuration is incomplete, you must "
|
|
"first configure the APQNs used with this plugin.");
|
|
return -EINVAL;
|
|
}
|
|
if (!ph->identity_key_generated) {
|
|
_set_error(ph, "The configuration is incomplete, you must "
|
|
"first generate the identity key.");
|
|
return -EINVAL;
|
|
}
|
|
|
|
if (ph->client_cert_avail) {
|
|
printf("ATTENTION: A client certificate already exists\n");
|
|
util_print_indented("When you set a new client certificate, "
|
|
"the existing certificate is removed and "
|
|
"you must re-register the new certificate "
|
|
"with the KMIP server before you can "
|
|
"communicate with the KMIP server", 0);
|
|
printf("%s: Set the new client certificate [y/N]? ",
|
|
program_invocation_short_name);
|
|
if (!prompt_for_yes(ph->pd.verbose)) {
|
|
_set_error(ph, "Operation aborted by user");
|
|
return -ECANCELED;
|
|
}
|
|
}
|
|
|
|
rc = _setup_ext_lib(ph);
|
|
if (rc != 0)
|
|
goto out;
|
|
|
|
rc = SK_UTIL_read_x509_certificate(client_cert, &cert);
|
|
if (rc != 0) {
|
|
_set_error(ph, "Failed to read the client certificate from "
|
|
"file '%s': %s", client_cert, strerror(-rc));
|
|
return rc;
|
|
}
|
|
|
|
if (ph->pd.verbose) {
|
|
pr_verbose(&ph->pd, "Client certificate read from '%s'",
|
|
client_cert);
|
|
X509_print_fp(stderr, cert);
|
|
}
|
|
|
|
rc = SK_UTIL_read_key_blob(ph->identity_secure_key, identity_key,
|
|
&identity_key_size);
|
|
if (rc != 0) {
|
|
_set_error(ph, "Failed to load the identity key from '%s': %s",
|
|
ph->identity_secure_key, strerror(-rc));
|
|
goto out;
|
|
}
|
|
|
|
switch (EVP_PKEY_id(X509_get0_pubkey(cert))) {
|
|
case EVP_PKEY_RSA:
|
|
cert_algo = KMIP_KEY_ALGORITHM_RSA;
|
|
break;
|
|
case EVP_PKEY_RSA_PSS:
|
|
cert_algo = KMIP_KEY_ALGORITHM_RSA_PSS;
|
|
rsa_pss = true;
|
|
break;
|
|
case EVP_PKEY_EC:
|
|
cert_algo = KMIP_KEY_ALGORITHM_ECC;
|
|
break;
|
|
default:
|
|
_set_error(ph, "Unsupported certificate algorithm");
|
|
rc = -EINVAL;
|
|
goto out;
|
|
}
|
|
|
|
rc = SK_OPENSSL_get_secure_key_as_pkey(identity_key, identity_key_size,
|
|
rsa_pss, &pkey, &ph->ext_lib,
|
|
ph->pd.verbose);
|
|
if (rc != 0) {
|
|
_set_error(ph, "Failed to get the PKEY from the identity key: "
|
|
"%s", strerror(-rc));
|
|
goto out;
|
|
}
|
|
|
|
#if !OPENSSL_VERSION_PREREQ(3, 0)
|
|
if (EVP_PKEY_cmp(X509_get0_pubkey(cert), pkey) != 1) {
|
|
#else
|
|
if (EVP_PKEY_eq(X509_get0_pubkey(cert), pkey) != 1) {
|
|
#endif
|
|
_set_error(ph, "The client certificate's public key does not "
|
|
"match the identity key.");
|
|
rc = -EINVAL;
|
|
goto out;
|
|
}
|
|
|
|
util_asprintf(&client_cert_file, "%s/%s", ph->pd.config_path,
|
|
KMIP_CONFIG_CLIENT_CERTIFICATE_FILE);
|
|
|
|
rc = plugin_set_or_remove_property(&ph->pd,
|
|
KMIP_CONFIG_CLIENT_CERTIFICATE, client_cert_file);
|
|
if (rc != 0)
|
|
goto out;
|
|
|
|
rc = plugin_set_or_remove_property(&ph->pd,
|
|
KMIP_CONFIG_CLIENT_CERT_ALGORITHM, cert_algo);
|
|
if (rc != 0)
|
|
goto out;
|
|
|
|
rc = SK_UTIL_write_x509_certificate(client_cert_file, cert);
|
|
if (rc != 0) {
|
|
_set_error(ph, "Failed to write the self-signed "
|
|
"certificate to '%s'", client_cert_file);
|
|
goto out;
|
|
}
|
|
|
|
pr_verbose(&ph->pd, "Client certificate stored in '%s'",
|
|
client_cert_file);
|
|
|
|
out:
|
|
if (pkey != NULL)
|
|
EVP_PKEY_free(pkey);
|
|
if (client_cert_file != NULL)
|
|
free(client_cert_file);
|
|
X509_free(cert);
|
|
|
|
return rc;
|
|
}
|
|
|
|
/**
|
|
* Set the state of a key.
|
|
*
|
|
* @param ph the plugin handle
|
|
* @param key_id the ID of the key to set the state
|
|
* @param state the new state
|
|
*
|
|
* @returns 0 on success, a negative errno in case of an error.
|
|
*/
|
|
static int _set_key_state(struct plugin_handle *ph, const char *key_id,
|
|
enum kmip_state state)
|
|
{
|
|
struct kmip_node *uid = NULL, *req_pl = NULL, *resp_pl = NULL;
|
|
enum kmip_operation operation;
|
|
int rc;
|
|
|
|
uid = kmip_new_unique_identifier(key_id, 0, 0);
|
|
CHECK_ERROR(uid == NULL, rc, -ENOMEM, "Allocate KMIP node failed",
|
|
ph, out);
|
|
|
|
switch (state) {
|
|
case KMIP_STATE_ACTIVE:
|
|
req_pl = kmip_new_activate_request_payload(uid);
|
|
operation = KMIP_OPERATION_ACTIVATE;
|
|
break;
|
|
case KMIP_STATE_DEACTIVATED:
|
|
req_pl = kmip_new_revoke_request_payload(uid,
|
|
KMIP_REVOK_RSN_SUPERSEDED, NULL, 0);
|
|
operation = KMIP_OPERATION_REVOKE;
|
|
break;
|
|
case KMIP_STATE_COMPROMISED:
|
|
req_pl = kmip_new_revoke_request_payload(uid,
|
|
KMIP_REVOK_RSN_KEY_COMPROMISE, NULL,
|
|
time(NULL));
|
|
operation = KMIP_OPERATION_REVOKE;
|
|
break;
|
|
case KMIP_STATE_DESTROYED:
|
|
req_pl = kmip_new_destroy_request_payload(uid);
|
|
operation = KMIP_OPERATION_DESTROY;
|
|
break;
|
|
default:
|
|
_set_error(ph, "Invalid state: %d", state);
|
|
rc = -EINVAL;
|
|
goto out;
|
|
}
|
|
CHECK_ERROR(req_pl == NULL, rc, -ENOMEM, "Allocate KMIP node failed",
|
|
ph, out);
|
|
|
|
rc = _perform_kmip_request(ph, operation, req_pl, &resp_pl);
|
|
|
|
out:
|
|
kmip_node_free(uid);
|
|
kmip_node_free(req_pl);
|
|
kmip_node_free(resp_pl);
|
|
|
|
return rc;
|
|
}
|
|
|
|
/**
|
|
* Returns true if the KMIP server supports the 'Sensitive' attribute.
|
|
* This is dependent on the profile settings, and the used KMIP protocol
|
|
* version (>= v1.4).
|
|
*
|
|
* @param ph the plugin handle
|
|
*
|
|
* @return true or false
|
|
*/
|
|
static bool _supports_sensitive_attr(struct plugin_handle *ph)
|
|
{
|
|
if (ph->kmip_version.major <= 1)
|
|
return false;
|
|
|
|
if (ph->kmip_version.major == 1 && ph->kmip_version.minor < 4)
|
|
return false;
|
|
|
|
return ph->profile->supports_sensitive_attr;
|
|
}
|
|
|
|
/**
|
|
* Returns true if the KMIP server supports the 'Description' attribute.
|
|
* This is dependent on the profile settings, and the used KMIP protocol
|
|
* version (>= v1.4).
|
|
*
|
|
* @param ph the plugin handle
|
|
*
|
|
* @return true or false
|
|
*/
|
|
static bool _supports_description_attr(struct plugin_handle *ph)
|
|
{
|
|
if (ph->kmip_version.major <= 1)
|
|
return false;
|
|
|
|
if (ph->kmip_version.major == 1 && ph->kmip_version.minor < 4)
|
|
return false;
|
|
|
|
return ph->profile->supports_description_attr;
|
|
}
|
|
|
|
/**
|
|
* Returns true if the KMIP server supports the 'Comment' attribute.
|
|
* This is dependent on the profile settings, and the used KMIP protocol
|
|
* version (>= v1.4).
|
|
*
|
|
* @param ph the plugin handle
|
|
*
|
|
* @return true or false
|
|
*/
|
|
static bool _supports_comment_attr(struct plugin_handle *ph)
|
|
{
|
|
if (ph->kmip_version.major <= 1)
|
|
return false;
|
|
|
|
if (ph->kmip_version.major == 1 && ph->kmip_version.minor < 4)
|
|
return false;
|
|
|
|
return ph->profile->supports_comment_attr;
|
|
}
|
|
|
|
/**
|
|
* Destroy a key.
|
|
*
|
|
* @param ph the plugin handle
|
|
* @param key_id the ID of the key to destroy
|
|
*
|
|
* @returns 0 on success, a negative errno in case of an error.
|
|
*/
|
|
static int _destroy_key(struct plugin_handle *ph, const char *key_id)
|
|
{
|
|
struct kmip_node *uid = NULL, *req_pl = NULL, *resp_pl = NULL;
|
|
int rc;
|
|
|
|
uid = kmip_new_unique_identifier(key_id, 0, 0);
|
|
CHECK_ERROR(uid == NULL, rc, -ENOMEM, "Allocate KMIP node failed",
|
|
ph, out);
|
|
|
|
req_pl = kmip_new_destroy_request_payload(uid);
|
|
CHECK_ERROR(req_pl == NULL, rc, -ENOMEM, "Allocate KMIP node failed",
|
|
ph, out);
|
|
|
|
rc = _perform_kmip_request(ph, KMIP_OPERATION_DESTROY, req_pl,
|
|
&resp_pl);
|
|
|
|
out:
|
|
kmip_node_free(uid);
|
|
kmip_node_free(req_pl);
|
|
kmip_node_free(resp_pl);
|
|
|
|
return rc;
|
|
}
|
|
|
|
/**
|
|
* Build Custom/Vendor attribute according to the Custom attribute style of the
|
|
* profile.
|
|
*
|
|
* @param ph the plugin handle
|
|
* @param name the attribute name
|
|
* @param value the attribute value
|
|
*
|
|
* @returns the attribute node or NULL in case of an error.
|
|
*/
|
|
static struct kmip_node *_build_custom_attr(struct plugin_handle *ph,
|
|
const char *name,
|
|
const char *value)
|
|
{
|
|
struct kmip_node *attr = NULL, *text;
|
|
char *v1_name = NULL;
|
|
|
|
text = kmip_node_new_text_string(KMIP_TAG_ATTRIBUTE_VALUE, NULL, value);
|
|
|
|
switch (ph->profile->cust_attr_scheme) {
|
|
case KMIP_PROFILE_CUST_ATTR_V1_STYLE:
|
|
util_asprintf(&v1_name, "zkey-%s", name);
|
|
attr = kmip_new_vendor_attribute("x", v1_name, text);
|
|
free(v1_name);
|
|
break;
|
|
case KMIP_PROFILE_CUST_ATTR_V2_STYLE:
|
|
attr = kmip_new_vendor_attribute("zkey", name, text);
|
|
break;
|
|
default:
|
|
_set_error(ph, "Invalid custom attribute style: %d",
|
|
ph->profile->cust_attr_scheme);
|
|
goto out;
|
|
}
|
|
|
|
out:
|
|
kmip_node_free(text);
|
|
return attr;
|
|
}
|
|
|
|
/**
|
|
* Build Description attribute, dependent on the profile settings.
|
|
* This is either a 'Description' attribute, a 'Comment' attribute, or a
|
|
* Custom/Vendor attribute according to the Custom attribute style of the
|
|
* profile.
|
|
*
|
|
* @param ph the plugin handle
|
|
* @param description the description text
|
|
*
|
|
* @returns the attribute node or NULL in case of an error.
|
|
*/
|
|
static struct kmip_node *_build_description_attr(struct plugin_handle *ph,
|
|
const char *description)
|
|
{
|
|
if (_supports_description_attr(ph))
|
|
return kmip_new_description(description);
|
|
|
|
if (_supports_comment_attr(ph))
|
|
return kmip_new_comment(description);
|
|
|
|
return _build_custom_attr(ph, KMS_KEY_PROP_DESCRIPTION, description);
|
|
}
|
|
|
|
/**
|
|
* Build Custom/Vendor attribute reference according to the Custom attribute
|
|
* style of the profile.
|
|
*
|
|
* @param ph the plugin handle
|
|
* @param name the attribute name
|
|
*
|
|
* @returns the attribute node or NULL in case of an error.
|
|
*/
|
|
static struct kmip_node *_build_custom_attr_ref(struct plugin_handle *ph,
|
|
const char *name)
|
|
{
|
|
struct kmip_node *attr_ref = NULL;
|
|
char *v1_name = NULL;
|
|
|
|
switch (ph->profile->cust_attr_scheme) {
|
|
case KMIP_PROFILE_CUST_ATTR_V1_STYLE:
|
|
util_asprintf(&v1_name, "zkey-%s", name);
|
|
attr_ref = kmip_new_attribute_reference(0, "x", v1_name);
|
|
free(v1_name);
|
|
break;
|
|
case KMIP_PROFILE_CUST_ATTR_V2_STYLE:
|
|
attr_ref = kmip_new_attribute_reference(0, "zkey", name);
|
|
break;
|
|
default:
|
|
_set_error(ph, "Invalid custom attribute style: %d",
|
|
ph->profile->cust_attr_scheme);
|
|
goto out;
|
|
}
|
|
|
|
out:
|
|
return attr_ref;
|
|
}
|
|
|
|
/**
|
|
* Build Description attribute reference, dependent on the profile settings.
|
|
* This is either a 'Description' attribute, a 'Comment' attribute, or a
|
|
* Custom/Vendor attribute according to the Custom attribute style of the
|
|
* profile.
|
|
*
|
|
* @param ph the plugin handle
|
|
*
|
|
* @returns the attribute node or NULL in case of an error.
|
|
*/
|
|
static struct kmip_node *_build_description_attr_ref(struct plugin_handle *ph)
|
|
{
|
|
if (_supports_description_attr(ph))
|
|
return kmip_new_attribute_reference(KMIP_TAG_DESCRIPTION,
|
|
NULL, NULL);
|
|
|
|
if (_supports_comment_attr(ph))
|
|
return kmip_new_attribute_reference(KMIP_TAG_COMMENT,
|
|
NULL, NULL);
|
|
|
|
return _build_custom_attr_ref(ph, KMS_KEY_PROP_DESCRIPTION);
|
|
}
|
|
|
|
/**
|
|
* Checks if the Attribute is a Custom/Vendor attribute that was set by this
|
|
* plugin, and returns its name and value.
|
|
*
|
|
* @param ph the plugin handle
|
|
* @param attr the custom/vendor attribute node
|
|
* @param name On return: the name of the custom attribute
|
|
* @param value On return: the value of the custom attribute
|
|
*
|
|
* @returns true if this is a custom/vendor attribute created by the plugin
|
|
*/
|
|
static bool _get_custom_attr(struct plugin_handle *ph, struct kmip_node *attr,
|
|
const char **name, const char **value)
|
|
{
|
|
struct kmip_node *attr_value = NULL;
|
|
const char *vendor_id, *attr_name;
|
|
bool ret = false;
|
|
int rc;
|
|
|
|
rc = kmip_get_vendor_attribute(attr, &vendor_id, &attr_name,
|
|
&attr_value);
|
|
if (rc != 0)
|
|
goto out;
|
|
|
|
switch (ph->profile->cust_attr_scheme) {
|
|
case KMIP_PROFILE_CUST_ATTR_V1_STYLE:
|
|
if (strcmp(vendor_id, "zkey") == 0)
|
|
break;
|
|
if (strcmp(vendor_id, "x") != 0 ||
|
|
strncmp(attr_name, "zkey-", 5) != 0) {
|
|
rc = -EBADMSG;
|
|
goto out;
|
|
}
|
|
attr_name += 5;
|
|
break;
|
|
case KMIP_PROFILE_CUST_ATTR_V2_STYLE:
|
|
if (strcmp(vendor_id, "zkey") != 0)
|
|
goto out;
|
|
break;
|
|
default:
|
|
goto out;
|
|
}
|
|
|
|
if (kmip_node_get_type(attr_value) != KMIP_TYPE_TEXT_STRING)
|
|
goto out;
|
|
|
|
if ((_supports_description_attr(ph) || _supports_comment_attr(ph)) &&
|
|
strcmp(attr_name, KMS_KEY_PROP_DESCRIPTION) == 0)
|
|
goto out;
|
|
|
|
if (ph->profile->supports_link_attr &&
|
|
(strcmp(attr_name, KMS_KEY_PROP_XTS_KEY1_ID) == 0 ||
|
|
strcmp(attr_name, KMS_KEY_PROP_XTS_KEY2_ID) == 0))
|
|
goto out;
|
|
|
|
if (name != NULL)
|
|
*name = attr_name;
|
|
if (value != NULL) {
|
|
*value = kmip_node_get_text_string(attr_value);
|
|
if (*value == NULL || strcmp(*value, " ") == 0)
|
|
*value = "";
|
|
}
|
|
|
|
ret = true;
|
|
|
|
out:
|
|
kmip_node_free(attr_value);
|
|
return ret;
|
|
}
|
|
|
|
/**
|
|
* Register and activate an RSA wrapping key.
|
|
*
|
|
* @param ph the plugin handle
|
|
* @param pkey the wrapping key as OpenSSL PKEY.
|
|
* @param wrapping_key_label the label name for the wrapping key (can be NULL)
|
|
*
|
|
* @returns 0 on success, a negative errno in case of an error.
|
|
*/
|
|
static int _register_rsa_wrapping_key(struct plugin_handle *ph, EVP_PKEY *pkey,
|
|
const char *wrapping_key_label)
|
|
{
|
|
struct kmip_node *kobj = NULL, *name_attr = NULL, *unique_id = NULL;
|
|
struct kmip_node *reg_req = NULL, *reg_resp = NULL, *descr_attr = NULL;
|
|
struct kmip_node *key = NULL, *kval = NULL, *kblock = NULL;
|
|
struct kmip_node *umask_attr = NULL, *cparams_attr = NULL;
|
|
struct kmip_node *act_req = NULL, *act_resp = NULL;
|
|
#if !OPENSSL_VERSION_PREREQ(3, 0)
|
|
const BIGNUM *modulus = NULL, *pub_exp = NULL;
|
|
#else
|
|
BIGNUM *modulus = NULL, *pub_exp = NULL;
|
|
#endif
|
|
const char *wrap_key_id = NULL;
|
|
char *description = NULL;
|
|
struct utsname utsname;
|
|
int rc;
|
|
|
|
pr_verbose(&ph->pd, "Wrapping key format: %d",
|
|
ph->profile->wrap_key_format);
|
|
pr_verbose(&ph->pd, "Wrap padding method: %d",
|
|
ph->profile->wrap_padding_method);
|
|
pr_verbose(&ph->pd, "Wrap hashing algorithm: %d",
|
|
ph->profile->wrap_hashing_algo);
|
|
|
|
switch (ph->profile->wrap_key_format) {
|
|
case KMIP_KEY_FORMAT_TYPE_PKCS_1:
|
|
key = kmip_new_pkcs1_public_key(pkey);
|
|
break;
|
|
case KMIP_KEY_FORMAT_TYPE_PKCS_8:
|
|
key = kmip_new_pkcs8_public_key(pkey);
|
|
break;
|
|
case KMIP_KEY_FORMAT_TYPE_TRANSPARENT_RSA_PUBLIC_KEY:
|
|
#if !OPENSSL_VERSION_PREREQ(3, 0)
|
|
modulus = RSA_get0_n(EVP_PKEY_get0_RSA(pkey));
|
|
pub_exp = RSA_get0_e(EVP_PKEY_get0_RSA(pkey));
|
|
#else
|
|
EVP_PKEY_get_bn_param(pkey, OSSL_PKEY_PARAM_RSA_N, &modulus);
|
|
EVP_PKEY_get_bn_param(pkey, OSSL_PKEY_PARAM_RSA_E, &pub_exp);
|
|
#endif
|
|
if (modulus == NULL || pub_exp == NULL) {
|
|
_set_error(ph, "Failed to get RSA public key parts");
|
|
rc = -EIO;
|
|
goto out;
|
|
}
|
|
|
|
key = kmip_new_transparent_rsa_public_key(modulus, pub_exp);
|
|
break;
|
|
default:
|
|
_set_error(ph, "Unsupported wrapping key format: %d",
|
|
ph->profile->wrap_key_format);
|
|
rc = -EINVAL;
|
|
goto out;
|
|
}
|
|
CHECK_ERROR(key == NULL, rc, -ENOMEM, "Allocate KMIP node failed",
|
|
ph, out);
|
|
|
|
kval = kmip_new_key_value_va(NULL, key, 0);
|
|
CHECK_ERROR(kval == NULL, rc, -ENOMEM, "Allocate KMIP node failed",
|
|
ph, out);
|
|
|
|
kblock = kmip_new_key_block(ph->profile->wrap_key_format, 0, kval,
|
|
ph->profile->wrap_key_algo,
|
|
ph->profile->wrap_key_size, NULL);
|
|
CHECK_ERROR(kblock == NULL, rc, -ENOMEM, "Allocate KMIP node failed",
|
|
ph, out);
|
|
|
|
kobj = kmip_new_public_key(kblock);
|
|
CHECK_ERROR(kobj == NULL, rc, -ENOMEM, "Allocate KMIP node failed",
|
|
ph, out);
|
|
|
|
if (wrapping_key_label != NULL) {
|
|
name_attr = kmip_new_name(wrapping_key_label,
|
|
KMIP_NAME_TYPE_UNINTERPRETED_TEXT_STRING);
|
|
CHECK_ERROR(name_attr == NULL, rc, -ENOMEM,
|
|
"Allocate KMIP node failed", ph, out);
|
|
}
|
|
|
|
umask_attr = kmip_new_cryptographic_usage_mask(
|
|
KMIP_CRY_USAGE_MASK_ENCRYPT |
|
|
KMIP_CRY_USAGE_MASK_WRAP_KEY);
|
|
CHECK_ERROR(umask_attr == NULL, rc, -ENOMEM,
|
|
"Allocate KMIP node failed", ph, out);
|
|
|
|
cparams_attr = kmip_new_cryptographic_parameters(NULL, 0,
|
|
ph->profile->wrap_padding_method,
|
|
ph->profile->wrap_padding_method ==
|
|
KMIP_PADDING_METHOD_OAEP ?
|
|
ph->profile->wrap_hashing_algo : 0,
|
|
KMIP_KEY_ROLE_TYPE_KEK, 0,
|
|
ph->profile->wrap_key_algo, NULL, NULL, NULL,
|
|
NULL, NULL, NULL, NULL, NULL,
|
|
ph->profile->wrap_padding_method ==
|
|
KMIP_PADDING_METHOD_OAEP ?
|
|
KMIP_MASK_GENERATOR_MGF1 : 0,
|
|
ph->profile->wrap_padding_method ==
|
|
KMIP_PADDING_METHOD_OAEP ?
|
|
ph->profile->wrap_hashing_algo : 0,
|
|
NULL);
|
|
CHECK_ERROR(cparams_attr == NULL, rc, -ENOMEM,
|
|
"Allocate KMIP node failed", ph, out);
|
|
|
|
if (uname(&utsname) != 0) {
|
|
rc = -errno;
|
|
_set_error(ph, "Failed to obtain the system's "
|
|
"hostname: %s", strerror(-rc));
|
|
goto out;
|
|
}
|
|
|
|
util_asprintf(&description, "Wrapping key for zkey client on system %s",
|
|
utsname.nodename);
|
|
descr_attr = _build_description_attr(ph, description);
|
|
free(description);
|
|
CHECK_ERROR(descr_attr == NULL, rc, -ENOMEM,
|
|
"Allocate KMIP node failed", ph, out);
|
|
|
|
reg_req = kmip_new_register_request_payload_va(NULL,
|
|
KMIP_OBJECT_TYPE_PUBLIC_KEY, kobj, NULL,
|
|
4, name_attr, umask_attr, cparams_attr,
|
|
descr_attr);
|
|
CHECK_ERROR(reg_req == NULL, rc, -ENOMEM, "Allocate KMIP node failed",
|
|
ph, out);
|
|
|
|
act_req = kmip_new_activate_request_payload(NULL); /* ID placeholder */
|
|
CHECK_ERROR(act_req == NULL, rc, -ENOMEM, "Allocate KMIP node failed",
|
|
ph, out);
|
|
|
|
rc = _perform_kmip_request2(ph, KMIP_OPERATION_REGISTER, reg_req,
|
|
®_resp, KMIP_OPERATION_ACTIVATE, act_req,
|
|
&act_resp, KMIP_BATCH_ERR_CONT_STOP);
|
|
if (rc != 0)
|
|
goto out;
|
|
|
|
rc = kmip_get_register_response_payload(reg_resp, &unique_id, NULL,
|
|
0, NULL);
|
|
CHECK_ERROR(rc != 0, rc, rc, "Failed to get key unique-id", ph, out);
|
|
|
|
rc = kmip_get_unique_identifier(unique_id, &wrap_key_id, NULL, NULL);
|
|
CHECK_ERROR(rc != 0, rc, rc, "Failed to get key unique-id", ph, out);
|
|
|
|
pr_verbose(&ph->pd, "Wrapping key ID: '%s'", wrap_key_id);
|
|
|
|
rc = plugin_set_or_remove_property(&ph->pd, KMIP_CONFIG_WRAPPING_KEY_ID,
|
|
wrap_key_id);
|
|
if (rc != 0)
|
|
goto out;
|
|
|
|
rc = plugin_set_or_remove_property(&ph->pd,
|
|
KMIP_CONFIG_WRAPPING_KEY_LABEL,
|
|
wrapping_key_label);
|
|
if (rc != 0)
|
|
goto out;
|
|
|
|
out:
|
|
kmip_node_free(key);
|
|
kmip_node_free(kval);
|
|
kmip_node_free(kblock);
|
|
kmip_node_free(kobj);
|
|
kmip_node_free(name_attr);
|
|
kmip_node_free(umask_attr);
|
|
kmip_node_free(cparams_attr);
|
|
kmip_node_free(descr_attr);
|
|
kmip_node_free(reg_req);
|
|
kmip_node_free(reg_resp);
|
|
kmip_node_free(act_req);
|
|
kmip_node_free(act_resp);
|
|
kmip_node_free(unique_id);
|
|
|
|
#if OPENSSL_VERSION_PREREQ(3, 0)
|
|
if (modulus != NULL)
|
|
BN_free(modulus);
|
|
if (pub_exp != NULL)
|
|
BN_free(pub_exp);
|
|
#endif
|
|
|
|
return rc;
|
|
}
|
|
|
|
|
|
/**
|
|
* (Re-)Generate a wrapping key using the settings from the profile.
|
|
* register the new wrapping key with the KMIP server, and deactivate any
|
|
* previous wrapping key (if any).
|
|
*
|
|
* @param ph the plugin handle
|
|
* @param wrapping_key_label the label name for the wrapping key (can be NULL)
|
|
*
|
|
* @returns 0 on success, a negative errno in case of an error.
|
|
*/
|
|
static int _generate_wrapping_key(struct plugin_handle *ph,
|
|
const char *wrapping_key_label)
|
|
{
|
|
unsigned char wrapping_key[KMIP_MAX_KEY_TOKEN_SIZE] = { 0 };
|
|
size_t wrapping_key_size = sizeof(wrapping_key);
|
|
struct sk_key_gen_info gen_info = { 0 };
|
|
char *wrapping_key_file_tmp = NULL;
|
|
char *wrapping_key_file = NULL;
|
|
char *prev_wrap_key_id = NULL;
|
|
char *reenc_file = NULL;
|
|
EVP_PKEY *pkey = NULL;
|
|
char tmp[200];
|
|
int rc = 0;
|
|
|
|
_check_config_complete(ph);
|
|
|
|
if (!ph->apqns_configured) {
|
|
_set_error(ph, "The configuration is incomplete, you must "
|
|
"first configure the APQNs used with this plugin.");
|
|
return -EINVAL;
|
|
}
|
|
if (!ph->connection_configured) {
|
|
_set_error(ph, "The configuration is incomplete, you must "
|
|
"first configure the KMIP server connection.");
|
|
return -EINVAL;
|
|
}
|
|
|
|
if (ph->connection == NULL) {
|
|
rc = _connect_to_server(ph);
|
|
if (rc != 0)
|
|
return rc;
|
|
}
|
|
|
|
prev_wrap_key_id = properties_get(ph->pd.properties,
|
|
KMIP_CONFIG_WRAPPING_KEY_ID);
|
|
pr_verbose(&ph->pd, "Previous wrapping key: '%s'", prev_wrap_key_id ?
|
|
prev_wrap_key_id : "(none)");
|
|
|
|
pr_verbose(&ph->pd, "Wrapping key algorithm: %d",
|
|
ph->profile->wrap_key_algo);
|
|
pr_verbose(&ph->pd, "Wrapping key size: %lu",
|
|
ph->profile->wrap_key_size);
|
|
|
|
util_asprintf(&wrapping_key_file_tmp, "%s/%s-tmp", ph->pd.config_path,
|
|
KMIP_CONFIG_WRAPPING_KEY_FILE);
|
|
util_asprintf((char **)&wrapping_key_file, "%s/%s", ph->pd.config_path,
|
|
KMIP_CONFIG_WRAPPING_KEY_FILE);
|
|
|
|
rc = plugin_set_or_remove_property(&ph->pd, KMIP_CONFIG_WRAPPING_KEY,
|
|
wrapping_key_file);
|
|
if (rc != 0)
|
|
goto out;
|
|
|
|
/* Generate the wrapping key */
|
|
switch (ph->profile->wrap_key_algo) {
|
|
case KMIP_CRYPTO_ALGO_RSA:
|
|
gen_info.type = SK_KEY_TYPE_RSA;
|
|
|
|
if (ph->profile->wrap_key_size == 0) {
|
|
_set_error(ph, "RSA Wrapping key size must be "
|
|
"specified");
|
|
rc = -EINVAL;
|
|
goto out;
|
|
}
|
|
gen_info.rsa.modulus_bits = ph->profile->wrap_key_size;
|
|
gen_info.rsa.pub_exp = 65537;
|
|
gen_info.rsa.x9_31 = false;
|
|
|
|
rc = plugin_set_or_remove_property(&ph->pd,
|
|
KMIP_CONFIG_WRAPPING_KEY_ALGORITHM,
|
|
KMIP_KEY_ALGORITHM_RSA);
|
|
if (rc != 0)
|
|
goto out;
|
|
|
|
sprintf(tmp, "%lu", gen_info.rsa.modulus_bits);
|
|
rc = plugin_set_or_remove_property(&ph->pd,
|
|
KMIP_CONFIG_WRAPPING_KEY_PARAMS, tmp);
|
|
if (rc != 0)
|
|
goto out;
|
|
|
|
break;
|
|
default:
|
|
_set_error(ph, "Unsupported wrapping key algorithm: %d",
|
|
ph->profile->wrap_key_algo);
|
|
rc = -EINVAL;
|
|
goto out;
|
|
}
|
|
|
|
rc = SK_OPENSSL_generate_secure_key(wrapping_key, &wrapping_key_size,
|
|
&gen_info, &ph->ext_lib,
|
|
ph->pd.verbose);
|
|
if (rc != 0) {
|
|
_set_error(ph, "Failed to generate the wrapping key: %s",
|
|
strerror(-rc));
|
|
goto out;
|
|
}
|
|
|
|
rc = SK_UTIL_write_key_blob(wrapping_key_file_tmp, wrapping_key,
|
|
wrapping_key_size);
|
|
if (rc != 0) {
|
|
_set_error(ph, "Failed to write the wrapping key into file "
|
|
"'%s': %s", wrapping_key_file_tmp, strerror(-rc));
|
|
goto out;
|
|
}
|
|
|
|
rc = plugin_set_file_permission(&ph->pd, wrapping_key_file_tmp);
|
|
if (rc != 0)
|
|
goto out;
|
|
|
|
/* Register the wrapping key with the KMIP server */
|
|
rc = SK_OPENSSL_get_secure_key_as_pkey(wrapping_key, wrapping_key_size,
|
|
false, &pkey, &ph->ext_lib,
|
|
ph->pd.verbose);
|
|
if (rc != 0) {
|
|
_set_error(ph, "Failed to get the PKEY from the wrapping key: "
|
|
"%s", strerror(-rc));
|
|
return rc;
|
|
}
|
|
|
|
switch (ph->profile->wrap_key_algo) {
|
|
case KMIP_CRYPTO_ALGO_RSA:
|
|
rc = _register_rsa_wrapping_key(ph, pkey, wrapping_key_label);
|
|
if (rc != 0)
|
|
goto out;
|
|
break;
|
|
|
|
default:
|
|
_set_error(ph, "Unsupported wrapping key algorithm: %d",
|
|
ph->profile->wrap_key_algo);
|
|
rc = -EINVAL;
|
|
goto out;
|
|
}
|
|
|
|
/* Activate the newly created wrapping key */
|
|
rc = plugin_activate_temp_file(&ph->pd, wrapping_key_file_tmp,
|
|
wrapping_key_file);
|
|
if (rc != 0)
|
|
goto out;
|
|
|
|
reenc_file = properties_get(ph->pd.properties,
|
|
KMIP_CONFIG_WRAPPING_KEY_REENC);
|
|
if (reenc_file != NULL) {
|
|
remove(reenc_file);
|
|
free(reenc_file);
|
|
properties_remove(ph->pd.properties,
|
|
KMIP_CONFIG_WRAPPING_KEY_REENC);
|
|
}
|
|
|
|
/* Deactivate and destroy previous wrapping key */
|
|
if (prev_wrap_key_id != NULL) {
|
|
rc = _set_key_state(ph, prev_wrap_key_id,
|
|
KMIP_STATE_DEACTIVATED);
|
|
if (rc != 0) {
|
|
/* Ignore error, just issue warning message */
|
|
warnx("WARNING: Failed to deactivate the previous "
|
|
"wrapping key '%s'", prev_wrap_key_id);
|
|
rc = 0;
|
|
plugin_clear_error(&ph->pd);
|
|
goto out;
|
|
}
|
|
|
|
printf("%s: Destroy the previous wrapping key at the KMIP "
|
|
"server [y/N]? ", program_invocation_short_name);
|
|
if (!prompt_for_yes(ph->pd.verbose))
|
|
goto out;
|
|
|
|
rc = _destroy_key(ph, prev_wrap_key_id);
|
|
if (rc != 0) {
|
|
/* Ignore error, just issue warning message */
|
|
warnx("WARNING: Failed to destroy the previous "
|
|
"wrapping key '%s'", prev_wrap_key_id);
|
|
rc = 0;
|
|
plugin_clear_error(&ph->pd);
|
|
}
|
|
}
|
|
|
|
out:
|
|
if (wrapping_key_file_tmp != NULL) {
|
|
remove(wrapping_key_file_tmp);
|
|
free(wrapping_key_file_tmp);
|
|
}
|
|
if (wrapping_key_file != NULL)
|
|
free(wrapping_key_file);
|
|
if (prev_wrap_key_id != NULL)
|
|
free(prev_wrap_key_id);
|
|
if (pkey != NULL)
|
|
EVP_PKEY_free(pkey);
|
|
|
|
return rc;
|
|
}
|
|
|
|
/**
|
|
* Configures (or re-configures) a KMS plugin. This function can be called
|
|
* several times to configure a KMS plugin is several steps (if supported by the
|
|
* KMS plugin). In case a configuration is not fully complete, this function
|
|
* may return -EAGAIN to indicate that it has accepted the configuration so far,
|
|
* but the configuration needs to be completed.
|
|
*
|
|
* A KMS plugin must be associated with at least one APQN. Thus, in a multi-step
|
|
* configuration, a list f APQNs must be specified at least once.
|
|
*
|
|
* @param handle the KMS plugin handle obtained from kms_initialize()
|
|
* @param apqns a list of APQNs to associate with the KMS plugin, or
|
|
* NULL if no APQNs are specified.
|
|
* @param num_apqns number of APQNs in above array. 0 if no APQNs are
|
|
* specified.
|
|
* @param options a list of options as specified by the user. These
|
|
* options are a subset of the possible options as
|
|
* returned by kms_get_command_options() with command
|
|
* KMS_COMMAND_CONFIGURE.
|
|
* @param num_options number of options in above array.
|
|
*
|
|
* @returns 0 on success, or a negative errno in case of an error.
|
|
* Function kms_get_last_error() can be used to obtain more details about the
|
|
* error.
|
|
* -EAGAIN to indicate that the specified configuration was accepted so far, but
|
|
* the configuration is still incomplete, and needs to be completed.
|
|
*/
|
|
int kms_configure(const kms_handle_t handle,
|
|
const struct kms_apqn *apqns, size_t num_apqns,
|
|
const struct kms_option *options, size_t num_options)
|
|
{
|
|
struct config_options opts = { 0 };
|
|
struct plugin_handle *ph = handle;
|
|
bool config_changed = false;
|
|
char *apqn_str = NULL;
|
|
int rc = 0;
|
|
size_t i;
|
|
|
|
util_assert(handle != NULL, "Internal error: handle is NULL");
|
|
util_assert(num_apqns == 0 || apqns != NULL,
|
|
"Internal error: apqns is NULL but num_apqns > 0");
|
|
util_assert(num_options == 0 || options != NULL,
|
|
"Internal error: options is NULL but num_options > 0 ");
|
|
|
|
pr_verbose(&ph->pd, "Configure");
|
|
for (i = 0; i < num_apqns; i++) {
|
|
pr_verbose(&ph->pd, " APQN: %02x.%04x", apqns[i].card,
|
|
apqns[i].domain);
|
|
}
|
|
for (i = 0; i < num_options; i++) {
|
|
if (isalnum(options[i].option))
|
|
pr_verbose(&ph->pd, " Option '%c': '%s'",
|
|
options[i].option,
|
|
options[i].argument != NULL ?
|
|
options[i].argument : "(null)");
|
|
else
|
|
pr_verbose(&ph->pd, " Option %d: '%s'",
|
|
options[i].option,
|
|
options[i].argument != NULL ?
|
|
options[i].argument : "(null)");
|
|
}
|
|
|
|
plugin_clear_error(&ph->pd);
|
|
|
|
if (apqns != NULL) {
|
|
rc = _check_apqns(ph, apqns, num_apqns);
|
|
if (rc != 0)
|
|
goto out;
|
|
|
|
if (num_apqns > 0 && ph->card_type == CARD_TYPE_CCA) {
|
|
rc = cross_check_cca_apka_apqns(&ph->pd, apqns,
|
|
num_apqns);
|
|
if (rc != 0) {
|
|
_set_error(ph, "Your CCA APKA master key setup "
|
|
"is improper");
|
|
goto out;
|
|
}
|
|
}
|
|
|
|
apqn_str = build_kms_apqn_string(apqns, num_apqns);
|
|
rc = properties_set(ph->pd.properties, KMIP_CONFIG_APQNS,
|
|
apqn_str);
|
|
if (rc != 0) {
|
|
_set_error(ph, "Failed to set APQNs property: %s",
|
|
strerror(-rc));
|
|
goto out;
|
|
}
|
|
|
|
rc = properties_set(ph->pd.properties, KMIP_CONFIG_APQN_TYPE,
|
|
_card_type_to_str(ph->card_type));
|
|
if (rc != 0) {
|
|
_set_error(ph, "Failed to set APQN-Type property: %s",
|
|
strerror(-rc));
|
|
goto out;
|
|
}
|
|
|
|
config_changed = true;
|
|
}
|
|
|
|
for (i = 0; i < num_options; i++) {
|
|
switch (options[i].option) {
|
|
case 'i':
|
|
opts.generate_identity_key = options[i].argument;
|
|
break;
|
|
case 'c':
|
|
opts.csr_pem_file = options[i].argument;
|
|
break;
|
|
case 'C':
|
|
opts.sscert_pem_file = options[i].argument;
|
|
break;
|
|
case 's':
|
|
opts.cert_subject = options[i].argument;
|
|
break;
|
|
case 'e':
|
|
opts.cert_extensions = options[i].argument;
|
|
break;
|
|
case 'N':
|
|
opts.renew_cert_pem_file = options[i].argument;
|
|
break;
|
|
case 'n':
|
|
opts.csr_new_header = true;
|
|
break;
|
|
case 'd':
|
|
opts.cert_validity_days = options[i].argument;
|
|
break;
|
|
case 'D':
|
|
opts.cert_digest = options[i].argument;
|
|
break;
|
|
case 'P':
|
|
opts.cert_rsa_pss = true;
|
|
break;
|
|
case 'r':
|
|
opts.client_cert = options[i].argument;
|
|
break;
|
|
case 'S':
|
|
opts.kmip_server = options[i].argument;
|
|
break;
|
|
case 'p':
|
|
opts.profile = options[i].argument;
|
|
break;
|
|
case 'b':
|
|
opts.tls_ca_bundle = options[i].argument;
|
|
break;
|
|
case OPT_TLS_PIN_SERVER_PUBKEY:
|
|
opts.tls_pin_server_pubkey = true;
|
|
break;
|
|
case OPT_TLS_TRUST_SERVER_CERT:
|
|
opts.tls_trust_server_cert = true;
|
|
break;
|
|
case OPT_TLS_DONT_VERIFY_SERVER_CERT:
|
|
opts.tls_dont_verify_server_cert = true;
|
|
break;
|
|
case OPT_TLS_VERIFY_HOSTNAME:
|
|
opts.tls_verify_hostname = true;
|
|
break;
|
|
case 'w':
|
|
opts.gen_wrapping_key = true;
|
|
break;
|
|
case 'B':
|
|
opts.wrapping_key_label = options[i].argument;
|
|
break;
|
|
default:
|
|
rc = -EINVAL;
|
|
if (isalnum(options[i].option))
|
|
_set_error(ph, "Unsupported option '%c'",
|
|
options[i].option);
|
|
else
|
|
_set_error(ph, "Unsupported option %d",
|
|
options[i].option);
|
|
goto out;
|
|
}
|
|
}
|
|
|
|
if (opts.generate_identity_key != NULL) {
|
|
rc = _generate_identity_key(ph, opts.generate_identity_key);
|
|
if (rc != 0)
|
|
goto out;
|
|
|
|
config_changed = true;
|
|
}
|
|
|
|
if (opts.csr_pem_file != NULL || opts.sscert_pem_file != NULL) {
|
|
if (opts.client_cert != NULL) {
|
|
_set_error(ph, "Option '--client-cert' in not valid "
|
|
"together with options '--gen-csr' or "
|
|
"'--gen-self-signed-cert'.");
|
|
rc = -EINVAL;
|
|
goto out;
|
|
}
|
|
|
|
if (!ph->identity_key_generated) {
|
|
/* Generate identity key with default key-specs */
|
|
rc = _generate_identity_key(ph, NULL);
|
|
if (rc != 0)
|
|
goto out;
|
|
|
|
config_changed = true;
|
|
}
|
|
|
|
rc = _generate_csr_sscert(ph, opts.csr_pem_file,
|
|
opts.sscert_pem_file,
|
|
opts.cert_subject,
|
|
opts.cert_extensions,
|
|
opts.renew_cert_pem_file,
|
|
opts.csr_new_header,
|
|
opts.cert_validity_days,
|
|
opts.cert_digest,
|
|
opts.cert_rsa_pss);
|
|
config_changed = true;
|
|
} else {
|
|
rc = _error_gen_csr_sscert_opts(ph, &opts);
|
|
}
|
|
if (rc != 0)
|
|
goto out;
|
|
|
|
if (opts.client_cert != NULL) {
|
|
rc = _use_client_cert(ph, opts.client_cert);
|
|
if (rc != 0)
|
|
goto out;
|
|
|
|
config_changed = true;
|
|
}
|
|
|
|
if (opts.kmip_server != NULL) {
|
|
rc = _configure_connection(ph, opts.kmip_server,
|
|
opts.profile,
|
|
opts.tls_ca_bundle,
|
|
opts.tls_pin_server_pubkey,
|
|
opts.tls_trust_server_cert,
|
|
opts.tls_dont_verify_server_cert,
|
|
opts.tls_verify_hostname);
|
|
config_changed = true;
|
|
opts.gen_wrapping_key = true;
|
|
} else {
|
|
rc = _error_connection_opts(ph, &opts);
|
|
}
|
|
if (rc != 0)
|
|
goto out;
|
|
|
|
if (opts.gen_wrapping_key) {
|
|
rc = _generate_wrapping_key(ph, opts.wrapping_key_label);
|
|
if (rc != 0)
|
|
goto out;
|
|
|
|
config_changed = true;
|
|
}
|
|
|
|
out:
|
|
if (apqn_str != NULL)
|
|
free(apqn_str);
|
|
|
|
if (rc == 0) {
|
|
if (config_changed) {
|
|
rc = plugin_save_config(&ph->pd);
|
|
if (rc != 0)
|
|
goto ret;
|
|
|
|
_check_config_complete(ph);
|
|
pr_verbose(&ph->pd,
|
|
"Plugin configuration is %scomplete",
|
|
ph->config_complete ? "" : "in");
|
|
}
|
|
|
|
if (!ph->config_complete)
|
|
rc = -EAGAIN;
|
|
}
|
|
|
|
ret:
|
|
return rc;
|
|
}
|
|
|
|
/**
|
|
* De-configures a KMS plugin. This is called by zkey when a repository is
|
|
* unbound from a KMS plugin. It gives the KMS plugin the chance to gracefully
|
|
* remove any files that the plugin has stored in its config directory. zkey
|
|
* will unconditionally remove all left over files when this function returns.
|
|
*
|
|
* @param handle the KMS plugin handle obtained from kms_initialize()
|
|
*
|
|
* @returns 0 on success, or a negative errno in case of an error.
|
|
* Function kms_get_last_error() can be used to obtain more details about the
|
|
* error.
|
|
*/
|
|
int kms_deconfigure(const kms_handle_t handle)
|
|
{
|
|
struct plugin_handle *ph = handle;
|
|
|
|
util_assert(handle != NULL, "Internal error: handle is NULL");
|
|
|
|
pr_verbose(&ph->pd, "Deconfigure");
|
|
|
|
plugin_clear_error(&ph->pd);
|
|
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* Allows the KMS plugin to perform a login to the KMS (if required). This
|
|
* function is called at least once before any key operation function, typically
|
|
* shortly after opening the repository.
|
|
* The KMS plugin may prompt the user (by reading from stdin) for its
|
|
* credentials, if needed.
|
|
*
|
|
* It is suggested that a KMS plugin performs a login with the KMS once, and
|
|
* stores a login token (or similar) in its config directory. The next time
|
|
* the kms_login function is called, the login token can be reused (if still
|
|
* valid). This avoids to prompt the user for every key operation.
|
|
*
|
|
* @param handle the KMS plugin handle obtained from kms_initialize()
|
|
*
|
|
* @returns 0 on success, or a negative errno in case of an error.
|
|
* Function kms_get_last_error() can be used to obtain more details about the
|
|
* error.
|
|
*/
|
|
int kms_login(const kms_handle_t handle)
|
|
{
|
|
struct plugin_handle *ph = handle;
|
|
|
|
util_assert(handle != NULL, "Internal error: handle is NULL");
|
|
|
|
pr_verbose(&ph->pd, "Login");
|
|
|
|
plugin_clear_error(&ph->pd);
|
|
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* Completes re-enciphering of a secure key
|
|
*
|
|
* @param ph the plugin handle
|
|
* @param key_file_prop the property name containing the key file name
|
|
* @param reenc_file_prop the property name containing the re-enciphered key
|
|
* file name
|
|
*
|
|
* @returns 0 on success, a negative errno in case of an error.
|
|
*/
|
|
static int _complete_reencipher(struct plugin_handle *ph,
|
|
const char *key_file_prop,
|
|
const char *reenc_file_prop)
|
|
{
|
|
char *key_file, *reenc_file;
|
|
int rc = 0;
|
|
|
|
key_file = properties_get(ph->pd.properties, key_file_prop);
|
|
reenc_file = properties_get(ph->pd.properties, reenc_file_prop);
|
|
|
|
if (key_file == NULL || reenc_file == NULL)
|
|
goto out;
|
|
|
|
rc = remove(key_file);
|
|
if (rc != 0) {
|
|
rc = -errno;
|
|
_set_error(ph, "Failed to remove file '%s': %s",
|
|
key_file, strerror(-rc));
|
|
goto out;
|
|
}
|
|
|
|
rc = rename(reenc_file, key_file);
|
|
if (rc != 0) {
|
|
rc = -errno;
|
|
_set_error(ph, "Failed to rename file '%s' to '%s': %s",
|
|
reenc_file, key_file, strerror(-rc));
|
|
goto out;
|
|
}
|
|
|
|
rc = properties_remove(ph->pd.properties, reenc_file_prop);
|
|
if (rc != 0) {
|
|
_set_error(ph, "Failed to remove property %s: %s",
|
|
reenc_file_prop, strerror(-rc));
|
|
goto out;
|
|
}
|
|
|
|
out:
|
|
if (key_file != NULL)
|
|
free(key_file);
|
|
if (reenc_file != NULL)
|
|
free(reenc_file);
|
|
|
|
return rc;
|
|
}
|
|
|
|
/**
|
|
* Re-enciphering a secure key
|
|
*
|
|
* @param ph the plugin handle
|
|
* @param to_new if true reencipher from CURRENT to NEW.
|
|
* @param key_file_name file name of the secure key file
|
|
* @param reenc_file_name file name of the re-enciphered key file
|
|
*
|
|
* @returns 0 on success, a negative errno in case of an error.
|
|
*/
|
|
static int _reencipher_key(struct plugin_handle *ph, bool to_new,
|
|
const char *key_file_name,
|
|
const char *reenc_file_name)
|
|
{
|
|
unsigned char secure_key[KMIP_MAX_KEY_TOKEN_SIZE] = { 0 };
|
|
size_t secure_key_size = sizeof(secure_key);
|
|
int rc;
|
|
|
|
rc = SK_UTIL_read_key_blob(key_file_name, secure_key, &secure_key_size);
|
|
if (rc != 0) {
|
|
_set_error(ph, "Failed to load the secure key from '%s': %s",
|
|
key_file_name, strerror(-rc));
|
|
return rc;
|
|
}
|
|
|
|
rc = SK_OPENSSL_reencipher_secure_key(secure_key, secure_key_size,
|
|
to_new, &ph->ext_lib,
|
|
ph->pd.verbose);
|
|
if (rc != 0) {
|
|
_set_error(ph, "Failed to re-encipher the secure key from file "
|
|
"'%s': %s", key_file_name, strerror(-rc));
|
|
return rc;
|
|
}
|
|
|
|
rc = SK_UTIL_write_key_blob(reenc_file_name, secure_key,
|
|
secure_key_size);
|
|
if (rc != 0) {
|
|
_set_error(ph, "Failed to write the secure key into '%s': %s",
|
|
reenc_file_name, strerror(-rc));
|
|
return rc;
|
|
}
|
|
|
|
rc = plugin_set_file_permission(&ph->pd, reenc_file_name);
|
|
if (rc != 0)
|
|
return rc;
|
|
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* Called when the master keys of an APQN associated with the KMS plugin has
|
|
* been changed. The KMS plugin can then re-encipher all its secure keys (if
|
|
* any) that it has stored in its config directory.
|
|
*
|
|
* Keys that have been generated by the KMS plugin and stored in the zkey
|
|
* repository do not need to be re-enciphered by the KMS plugin. Those are
|
|
* re-enciphered by zkey without the help of the KMS plugin.
|
|
*
|
|
* HSM have different master key registers. Typically a CURRENT and a NEW master
|
|
* key register exists. The NEW register may be loaded with the new to be set
|
|
* master key, and secure keys can be re-enciphered with it proactively.
|
|
*
|
|
* CCA also supports an OLD master key register, that contains the previously
|
|
* used master key. You thus can re-encipher a secure key that is currently
|
|
* enciphered with the master key from the OLD register with the master key
|
|
* from the CURRENT register.
|
|
*
|
|
* HSMs may also support different master keys for different key types or
|
|
* algorithms. It is up to the KMS plugin to know which master key registers
|
|
* are used for its secure keys
|
|
*
|
|
* A staged re-encipherment is performed by re-enciphering a secure key with
|
|
* the new HSM master key, without making it available for use in the first
|
|
* stage. Only when the staged re-encipherment is completed, then the previously
|
|
* re-enciphered secure key is make available for use and the old on is removed.
|
|
*
|
|
* An in-place re-encipherment replaces the secure key right away with its
|
|
* re-enciphered version.
|
|
*
|
|
* @param handle the KMS plugin handle obtained from kms_initialize()
|
|
* @param mode Re-encipherment mode
|
|
* @param mkreg Re-encipherment register selection
|
|
* @param options a list of options as specified by the user. These
|
|
* options are a subset of the possible options as
|
|
* returned by kms_get_command_options() with command
|
|
* KMS_COMMAND_REENCIPHER.
|
|
* @param num_options number of options in above array.
|
|
*
|
|
* @returns 0 on success, or a negative errno in case of an error.
|
|
* Function kms_get_last_error() can be used to obtain more details about the
|
|
* error.
|
|
*/
|
|
int kms_reenciper(const kms_handle_t handle, enum kms_reencipher_mode mode,
|
|
enum kms_reenc_mkreg mkreg,
|
|
const struct kms_option *options, size_t num_options)
|
|
{
|
|
char *ident_reenc_file = NULL, *ident_key_file = NULL;
|
|
char *wrap_reenc_file = NULL, *wrap_key_file = NULL;
|
|
struct plugin_handle *ph = handle;
|
|
size_t i;
|
|
int rc = 0;
|
|
|
|
util_assert(handle != NULL, "Internal error: handle is NULL");
|
|
util_assert(num_options == 0 || options != NULL,
|
|
"Internal error: options is NULL but num_options > 0 ");
|
|
|
|
pr_verbose(&ph->pd, "Re-encipher mode: %d, kmreg=%d", mode, mkreg);
|
|
for (i = 0; i < num_options; i++) {
|
|
if (isalnum(options[i].option))
|
|
pr_verbose(&ph->pd, " Option '%c': '%s'",
|
|
options[i].option,
|
|
options[i].argument != NULL ?
|
|
options[i].argument : "(null)");
|
|
else
|
|
pr_verbose(&ph->pd, " Option %d: '%s'",
|
|
options[i].option,
|
|
options[i].argument != NULL ?
|
|
options[i].argument : "(null)");
|
|
}
|
|
|
|
plugin_clear_error(&ph->pd);
|
|
|
|
if (ph->identity_secure_key == NULL)
|
|
return 0;
|
|
|
|
ident_reenc_file = properties_get(ph->pd.properties,
|
|
KMIP_CONFIG_IDENTITY_KEY_REENC);
|
|
if (ident_reenc_file != NULL && mode == KMS_REENC_MODE_AUTO)
|
|
mode = KMS_REENC_MODE_STAGED_COMPLETE;
|
|
|
|
if (mode == KMS_REENC_MODE_STAGED_COMPLETE) {
|
|
if (ident_reenc_file == NULL) {
|
|
_set_error(ph, "Staged re-enciphering is not pending");
|
|
rc = -EINVAL;
|
|
goto out;
|
|
}
|
|
|
|
printf("Completing re-enciphering of KMIP plugin keys.\n");
|
|
|
|
rc = _complete_reencipher(ph, KMIP_CONFIG_IDENTITY_KEY,
|
|
KMIP_CONFIG_IDENTITY_KEY_REENC);
|
|
if (rc != 0)
|
|
goto out;
|
|
|
|
rc = _complete_reencipher(ph, KMIP_CONFIG_WRAPPING_KEY,
|
|
KMIP_CONFIG_WRAPPING_KEY_REENC);
|
|
if (rc != 0)
|
|
goto out;
|
|
|
|
rc = plugin_save_config(&ph->pd);
|
|
if (rc != 0)
|
|
goto out;
|
|
|
|
printf("Successfully completed re-enciphering of KMIP plugin "
|
|
"keys.\n");
|
|
|
|
rc = 0;
|
|
goto out;
|
|
}
|
|
|
|
if (ident_reenc_file != NULL)
|
|
free(ident_reenc_file);
|
|
ident_reenc_file = NULL;
|
|
|
|
if (ph->card_type == CARD_TYPE_EP11 &&
|
|
(mkreg == KMS_REENC_MKREG_FROM_OLD ||
|
|
mkreg == KMS_REENC_MKREG_FROM_OLD_TO_NEW)) {
|
|
_set_error(ph, "ERROR: An APQN of a IBM cryptographic adapter "
|
|
"in EP11 coprocessor mode does not have an OLD "
|
|
"master key register. Thus, you can not re-encipher "
|
|
"a secure key of type 'EP11-AES' from the OLD to "
|
|
"the CURRENT/NEW master key register.");
|
|
rc = -EINVAL;
|
|
goto out;
|
|
}
|
|
|
|
switch (mkreg) {
|
|
case KMS_REENC_MKREG_AUTO:
|
|
case KMS_REENC_MKREG_TO_NEW:
|
|
if (mode == KMS_REENC_MODE_AUTO)
|
|
mode = KMS_REENC_MODE_STAGED;
|
|
|
|
printf("Re-enciphering the KMIP plugin keys with the master "
|
|
"key in the NEW register.\n");
|
|
break;
|
|
|
|
case KMS_REENC_MKREG_FROM_OLD:
|
|
if (mode == KMS_REENC_MODE_AUTO)
|
|
mode = KMS_REENC_MODE_IN_PLACE;
|
|
|
|
printf("Re-enciphering the KMIP plugin keys with the master "
|
|
"key in the CURRENT register.\n");
|
|
break;
|
|
|
|
case KMS_REENC_MKREG_FROM_OLD_TO_NEW:
|
|
if (mode == KMS_REENC_MODE_AUTO)
|
|
mode = KMS_REENC_MODE_STAGED;
|
|
|
|
printf("Re-enciphering the KMIP plugin keys with the master "
|
|
"key in the CURRENT and then the NEW register.\n");
|
|
break;
|
|
|
|
default:
|
|
_set_error(ph, "Invalid re-encipher MK register selection");
|
|
rc = -EINVAL;
|
|
goto out;
|
|
}
|
|
|
|
ident_key_file = properties_get(ph->pd.properties,
|
|
KMIP_CONFIG_IDENTITY_KEY);
|
|
if (ident_key_file == NULL)
|
|
goto out;
|
|
|
|
wrap_key_file = properties_get(ph->pd.properties,
|
|
KMIP_CONFIG_WRAPPING_KEY);
|
|
if (mode == KMS_REENC_MODE_STAGED) {
|
|
util_asprintf(&ident_reenc_file, "%s/%s",
|
|
ph->pd.config_path,
|
|
KMIP_CONFIG_IDENTITY_KEY_REENC_FILE);
|
|
util_asprintf(&wrap_reenc_file, "%s/%s",
|
|
ph->pd.config_path,
|
|
KMIP_CONFIG_WRAPPING_KEY_REENC_FILE);
|
|
}
|
|
|
|
if (mkreg == KMS_REENC_MKREG_AUTO || mkreg == KMS_REENC_MKREG_TO_NEW ||
|
|
mkreg == KMS_REENC_MKREG_FROM_OLD_TO_NEW) {
|
|
rc = _reencipher_key(ph, true, ident_key_file,
|
|
ident_reenc_file ? ident_reenc_file :
|
|
ident_key_file);
|
|
if (rc != 0)
|
|
goto out;
|
|
|
|
if (wrap_key_file != NULL) {
|
|
rc = _reencipher_key(ph, true, wrap_key_file,
|
|
wrap_reenc_file ? wrap_reenc_file :
|
|
wrap_key_file);
|
|
if (rc != 0)
|
|
goto out;
|
|
}
|
|
}
|
|
|
|
if (mkreg == KMS_REENC_MKREG_FROM_OLD) {
|
|
rc = _reencipher_key(ph, false, ident_key_file,
|
|
ident_reenc_file ? ident_reenc_file :
|
|
ident_key_file);
|
|
if (rc != 0)
|
|
goto out;
|
|
|
|
if (wrap_key_file != NULL) {
|
|
rc = _reencipher_key(ph, false, wrap_key_file,
|
|
wrap_reenc_file ? wrap_reenc_file :
|
|
wrap_key_file);
|
|
if (rc != 0)
|
|
goto out;
|
|
}
|
|
}
|
|
|
|
if (mkreg == KMS_REENC_MKREG_FROM_OLD_TO_NEW) {
|
|
rc = _reencipher_key(ph, false, ident_reenc_file ?
|
|
ident_reenc_file : ident_key_file,
|
|
ident_reenc_file ? ident_reenc_file :
|
|
ident_key_file);
|
|
if (rc != 0)
|
|
goto out;
|
|
|
|
if (wrap_key_file != NULL) {
|
|
rc = _reencipher_key(ph, false, wrap_reenc_file ?
|
|
wrap_reenc_file : wrap_key_file,
|
|
wrap_reenc_file ? wrap_reenc_file :
|
|
wrap_key_file);
|
|
if (rc != 0)
|
|
goto out;
|
|
}
|
|
}
|
|
|
|
if (mode == KMS_REENC_MODE_STAGED) {
|
|
rc = plugin_set_or_remove_property(&ph->pd,
|
|
KMIP_CONFIG_IDENTITY_KEY_REENC,
|
|
ident_reenc_file);
|
|
if (rc != 0)
|
|
goto out;
|
|
|
|
rc = plugin_set_or_remove_property(&ph->pd,
|
|
KMIP_CONFIG_WRAPPING_KEY_REENC,
|
|
wrap_reenc_file);
|
|
if (rc != 0)
|
|
goto out;
|
|
} else {
|
|
rc = plugin_set_or_remove_property(&ph->pd,
|
|
KMIP_CONFIG_IDENTITY_KEY_REENC, NULL);
|
|
if (rc != 0)
|
|
goto out;
|
|
|
|
rc = plugin_set_or_remove_property(&ph->pd,
|
|
KMIP_CONFIG_WRAPPING_KEY_REENC, NULL);
|
|
if (rc != 0)
|
|
goto out;
|
|
}
|
|
|
|
rc = plugin_save_config(&ph->pd);
|
|
if (rc != 0)
|
|
goto out;
|
|
|
|
rc = 0;
|
|
|
|
if (mode == KMS_REENC_MODE_STAGED)
|
|
util_print_indented("Staged re-enciphering is initiated for "
|
|
"the KMIP plugin keys. After the NEW "
|
|
"master key has been set to become the "
|
|
"CURRENT master key run 'zkey kms "
|
|
"reencipher' with option '--complete' to "
|
|
"complete the re-enciphering process.", 0);
|
|
else
|
|
printf("Successfully re-enciphered the KMIP plugin keys\n");
|
|
|
|
out:
|
|
if (rc != 0 && ident_reenc_file != NULL)
|
|
remove(ident_reenc_file);
|
|
if (ident_reenc_file != NULL)
|
|
free(ident_reenc_file);
|
|
if (ident_key_file != NULL)
|
|
free(ident_key_file);
|
|
|
|
if (rc != 0 && wrap_reenc_file != NULL)
|
|
remove(wrap_reenc_file);
|
|
if (wrap_reenc_file != NULL)
|
|
free(wrap_reenc_file);
|
|
if (wrap_key_file != NULL)
|
|
free(wrap_key_file);
|
|
|
|
return rc;
|
|
}
|
|
|
|
/**
|
|
* Parse a label for use with the key mode. For a non-XTS key, the label must
|
|
* not contain a colon. For an XTS key, split the label at the colon and
|
|
* return the desired part.
|
|
*
|
|
* @param ph the plugin handle
|
|
* @param label the label to parse: 'label' or 'label1:label2'
|
|
* @param key_mode the key mode to parse the label for
|
|
*
|
|
* @returns the label part to use for the key mode. The returned string must be
|
|
* freed by the caller. NULL is returnd in case of an error.
|
|
*/
|
|
static char *_parse_label(struct plugin_handle *ph, const char *label,
|
|
enum kms_key_mode key_mode)
|
|
{
|
|
char *tok, *ret = NULL;
|
|
|
|
tok = strchr(label, ':');
|
|
|
|
switch (key_mode) {
|
|
case KMS_KEY_MODE_NON_XTS:
|
|
if (tok != NULL) {
|
|
_set_error(ph, "Label can not contain a colon");
|
|
return NULL;
|
|
}
|
|
|
|
ret = util_strdup(label);
|
|
break;
|
|
|
|
case KMS_KEY_MODE_XTS_1:
|
|
if (tok == NULL) {
|
|
_set_error(ph, "For an XTS key two labels must "
|
|
"be specified, separated by a colon");
|
|
return NULL;
|
|
}
|
|
|
|
ret = util_zalloc(tok - label + 1);
|
|
strncpy(ret, label, tok - label);
|
|
break;
|
|
|
|
case KMS_KEY_MODE_XTS_2:
|
|
if (tok == NULL) {
|
|
_set_error(ph, "For an XTS key two labels must "
|
|
"be specified, separated by a colon");
|
|
return NULL;
|
|
}
|
|
|
|
ret = util_strdup(tok + 1);
|
|
break;
|
|
|
|
default:
|
|
_set_error(ph, "Unsupported key mode: %d", key_mode);
|
|
return NULL;
|
|
}
|
|
|
|
if (strlen(ret) == 0 || ret[0] == ' ') {
|
|
_set_error(ph, "The specified label is invalid: '%s'", ret);
|
|
free(ret);
|
|
return NULL;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
/**
|
|
* Build an KMIP attribute from a KMS property. Some KMS properties are
|
|
* converted into specific KMIP attributes, the others into Custom/Vendor
|
|
* attributes.
|
|
*
|
|
* @param ph the plugin handle
|
|
* @param prop the KMS property to build an Attribute for
|
|
*
|
|
* @returns The KMIP attribute node, or NULL in case of an error
|
|
*/
|
|
static struct kmip_node *_build_attr_from_prop(struct plugin_handle *ph,
|
|
const struct kms_property *prop)
|
|
{
|
|
struct kmip_node *attr, *linked_id;
|
|
const char *value = prop->value;
|
|
|
|
/* No empty values are allowed */
|
|
if (value == NULL || strlen(value) == 0)
|
|
value = " ";
|
|
|
|
if (strcmp(prop->name, KMS_KEY_PROP_DESCRIPTION) == 0)
|
|
return _build_description_attr(ph, value);
|
|
|
|
if (ph->profile->supports_link_attr &&
|
|
strcmp(prop->name, KMS_KEY_PROP_XTS_KEY1_ID) == 0) {
|
|
linked_id = kmip_new_linked_object_identifier(value, 0, 0);
|
|
if (linked_id == NULL)
|
|
return NULL;
|
|
|
|
attr = kmip_new_link(KMIP_LINK_TYPE_PREVIOUS, linked_id);
|
|
kmip_node_free(linked_id);
|
|
return attr;
|
|
}
|
|
|
|
if (ph->profile->supports_link_attr &&
|
|
strcmp(prop->name, KMS_KEY_PROP_XTS_KEY2_ID) == 0) {
|
|
linked_id = kmip_new_linked_object_identifier(value, 0, 0);
|
|
if (linked_id == NULL)
|
|
return NULL;
|
|
|
|
attr = kmip_new_link(KMIP_LINK_TYPE_NEXT, linked_id);
|
|
kmip_node_free(linked_id);
|
|
return attr;
|
|
}
|
|
|
|
return _build_custom_attr(ph, prop->name, value);
|
|
}
|
|
|
|
/**
|
|
* Build an KMIP attribute reference from a KMS property. Some KMS properties
|
|
* are converted into specific KMIP attributes, the others into Custom/Vendor
|
|
* attributes.
|
|
*
|
|
* @param ph the plugin handle
|
|
* @param prop the KMS property to build an Attribute reference for
|
|
*
|
|
* @returns The KMIP attribute node, or NULL in case of an error
|
|
*/
|
|
static struct kmip_node *_build_attr_ref_from_prop(struct plugin_handle *ph,
|
|
const struct kms_property *prop)
|
|
{
|
|
if (strcmp(prop->name, KMS_KEY_PROP_DESCRIPTION) == 0)
|
|
return _build_description_attr_ref(ph);
|
|
|
|
if (ph->profile->supports_link_attr &&
|
|
(strcmp(prop->name, KMS_KEY_PROP_XTS_KEY1_ID) == 0 ||
|
|
strcmp(prop->name, KMS_KEY_PROP_XTS_KEY2_ID) == 0))
|
|
return kmip_new_attribute_reference(KMIP_TAG_LINK, NULL, NULL);
|
|
|
|
return _build_custom_attr_ref(ph, prop->name);
|
|
}
|
|
|
|
/**
|
|
* Generate an AES key at the KMIP server.
|
|
*
|
|
* @param ph the plugin handle
|
|
* @param key_bits the key size in bits (128, 196, 256)
|
|
* @param properties the KMS properties to set
|
|
* @param num_properties number of KMS properties
|
|
* @param label the key label (can be NULL)
|
|
* @param key_id On return: the key id. Must be freed by the caller.
|
|
*
|
|
* @returns 0 on success, a negative errno in case of an error.
|
|
*/
|
|
static int _generate_aes_key(struct plugin_handle *ph, size_t key_bits,
|
|
const struct kms_property *properties,
|
|
size_t num_properties,
|
|
const char *label, char **key_id)
|
|
{
|
|
struct kmip_node *act_req = NULL, *act_resp = NULL, *unique_id = NULL;
|
|
struct kmip_node **attrs = NULL, *crea_req = NULL, *crea_resp = NULL;
|
|
unsigned int num_attrs, i, idx = 0;
|
|
const char *uid;
|
|
int rc = 0;
|
|
|
|
num_attrs = 3 + (_supports_sensitive_attr(ph) ? 1 : 0) +
|
|
(label != NULL ? 1 : 0) + num_properties;
|
|
attrs = util_zalloc(num_attrs * sizeof(struct kmip_node *));
|
|
|
|
attrs[idx] = kmip_new_cryptographic_algorithm(KMIP_CRYPTO_ALGO_AES);
|
|
CHECK_ERROR(attrs[idx] == NULL, rc, -ENOMEM,
|
|
"Allocate KMIP node failed", ph, out);
|
|
idx++;
|
|
|
|
attrs[idx] = kmip_new_cryptographic_length(key_bits);
|
|
CHECK_ERROR(attrs[idx] == NULL, rc, -ENOMEM,
|
|
"Allocate KMIP node failed", ph, out);
|
|
idx++;
|
|
|
|
attrs[idx] = kmip_new_cryptographic_usage_mask(
|
|
KMIP_CRY_USAGE_MASK_ENCRYPT | KMIP_CRY_USAGE_MASK_DECRYPT);
|
|
CHECK_ERROR(attrs[idx] == NULL, rc, -ENOMEM,
|
|
"Allocate KMIP node failed", ph, out);
|
|
idx++;
|
|
|
|
if (_supports_sensitive_attr(ph)) {
|
|
attrs[idx] = kmip_new_sensitive(true);
|
|
CHECK_ERROR(attrs[idx] == NULL, rc, -ENOMEM,
|
|
"Allocate KMIP node failed", ph, out);
|
|
idx++;
|
|
}
|
|
|
|
if (label != NULL) {
|
|
attrs[idx] = kmip_new_name(label,
|
|
KMIP_NAME_TYPE_UNINTERPRETED_TEXT_STRING);
|
|
CHECK_ERROR(attrs[idx] == NULL, rc, -ENOMEM,
|
|
"Allocate KMIP node failed", ph, out);
|
|
idx++;
|
|
}
|
|
|
|
for (i = 0; i < num_properties; i++) {
|
|
attrs[idx] = _build_attr_from_prop(ph, &properties[i]);
|
|
CHECK_ERROR(attrs[idx] == NULL, rc, -ENOMEM,
|
|
"Allocate KMIP node failed", ph, out);
|
|
idx++;
|
|
}
|
|
|
|
crea_req = kmip_new_create_request_payload(NULL,
|
|
KMIP_OBJECT_TYPE_SYMMETRIC_KEY, NULL,
|
|
num_attrs, attrs);
|
|
CHECK_ERROR(crea_req == NULL, rc, -ENOMEM, "Allocate KMIP node failed",
|
|
ph, out);
|
|
|
|
act_req = kmip_new_activate_request_payload(NULL); /* ID placeholder */
|
|
CHECK_ERROR(act_req == NULL, rc, -ENOMEM, "Allocate KMIP node failed",
|
|
ph, out);
|
|
|
|
rc = _perform_kmip_request2(ph, KMIP_OPERATION_CREATE, crea_req,
|
|
&crea_resp, KMIP_OPERATION_ACTIVATE,
|
|
act_req, &act_resp,
|
|
KMIP_BATCH_ERR_CONT_STOP);
|
|
if (rc != 0)
|
|
goto out;
|
|
|
|
rc = kmip_get_create_response_payload(crea_resp, NULL, &unique_id,
|
|
NULL, 0, NULL);
|
|
CHECK_ERROR(rc != 0, rc, rc, "Failed to get key unique-id", ph, out);
|
|
|
|
rc = kmip_get_unique_identifier(unique_id, &uid, NULL, NULL);
|
|
CHECK_ERROR(rc != 0, rc, rc, "Failed to get key unique-id", ph, out);
|
|
|
|
pr_verbose(&ph->pd, "Key ID: '%s'", uid);
|
|
|
|
*key_id = util_strdup(uid);
|
|
|
|
out:
|
|
if (attrs != NULL) {
|
|
for (i = 0; i < num_attrs; i++)
|
|
kmip_node_free(attrs[i]);
|
|
free(attrs);
|
|
}
|
|
kmip_node_free(crea_req);
|
|
kmip_node_free(crea_resp);
|
|
kmip_node_free(act_req);
|
|
kmip_node_free(act_resp);
|
|
|
|
return rc;
|
|
}
|
|
|
|
/**
|
|
* Retrieves an AES key from the KMIP server. The key is wrapped with the
|
|
* RSA wrapping key.
|
|
*
|
|
* @param ph the plugin handle
|
|
* @param key_id the key id of the key to get
|
|
* @param wrapped_key On return: an allocated buffer with the wrapped key.
|
|
* Must be freed by the caller.
|
|
* @param wrapped_key_len On return: the size of the wrapped key.
|
|
* @param key_bits On return the cryptographic size of the key in bits
|
|
*
|
|
* @returns 0 on success, a negative errno in case of an error.
|
|
*/
|
|
static int _get_key_rsa_wrapped(struct plugin_handle *ph, const char *key_id,
|
|
unsigned char **wrapped_key,
|
|
size_t *wrapped_key_len, size_t *key_bits)
|
|
{
|
|
struct kmip_node *cparams = NULL, *wrap_id = NULL, *wkey_info = NULL;
|
|
struct kmip_node *wrap_spec = NULL, *req_pl = NULL, *resp_pl = NULL;
|
|
struct kmip_node *uid = NULL, *kobj = NULL, *kblock = NULL;
|
|
struct kmip_node *kval = NULL, *wrap = NULL, *key = NULL;
|
|
struct kmip_node *wkinfo = NULL, *wcparms = NULL;
|
|
enum kmip_hashing_algo halgo, mgfhalgo;
|
|
enum kmip_wrapping_method wmethod;
|
|
enum kmip_key_format_type ftype;
|
|
enum kmip_padding_method pmeth;
|
|
enum kmip_encoding_option enc;
|
|
enum kmip_mask_generator mgf;
|
|
enum kmip_object_type otype;
|
|
enum kmip_crypto_algo algo;
|
|
const unsigned char *kdata;
|
|
char *wrap_key_id = NULL;
|
|
uint32_t klen;
|
|
int32_t bits;
|
|
int rc = 0;
|
|
|
|
pr_verbose(&ph->pd, "Wrap padding method: %d",
|
|
ph->profile->wrap_padding_method);
|
|
pr_verbose(&ph->pd, "Wrap hashing algorithm: %d",
|
|
ph->profile->wrap_hashing_algo);
|
|
|
|
wrap_key_id = properties_get(ph->pd.properties,
|
|
KMIP_CONFIG_WRAPPING_KEY_ID);
|
|
if (wrap_key_id == NULL) {
|
|
_set_error(ph, "Wrapping key ID is not available");
|
|
return -EINVAL;
|
|
}
|
|
|
|
pr_verbose(&ph->pd, "Wrapping key id: '%s'", wrap_key_id);
|
|
|
|
cparams = kmip_new_cryptographic_parameters(NULL, 0,
|
|
ph->profile->wrap_padding_method,
|
|
ph->profile->wrap_padding_method ==
|
|
KMIP_PADDING_METHOD_OAEP ?
|
|
ph->profile->wrap_hashing_algo : 0,
|
|
KMIP_KEY_ROLE_TYPE_KEK, 0,
|
|
ph->profile->wrap_key_algo, NULL, NULL, NULL,
|
|
NULL, NULL, NULL, NULL, NULL,
|
|
ph->profile->wrap_padding_method ==
|
|
KMIP_PADDING_METHOD_OAEP ?
|
|
KMIP_MASK_GENERATOR_MGF1 : 0,
|
|
ph->profile->wrap_padding_method ==
|
|
KMIP_PADDING_METHOD_OAEP ?
|
|
ph->profile->wrap_hashing_algo : 0,
|
|
NULL);
|
|
CHECK_ERROR(cparams == NULL, rc, -ENOMEM, "Allocate KMIP node failed",
|
|
ph, out);
|
|
|
|
wrap_id = kmip_new_unique_identifier(wrap_key_id, 0, 0);
|
|
CHECK_ERROR(wrap_id == NULL, rc, -ENOMEM, "Allocate KMIP node failed",
|
|
ph, out);
|
|
|
|
wkey_info = kmip_new_key_info(false, wrap_id, cparams);
|
|
CHECK_ERROR(wkey_info == NULL, rc, -ENOMEM, "Allocate KMIP node failed",
|
|
ph, out);
|
|
|
|
wrap_spec = kmip_new_key_wrapping_specification_va(NULL,
|
|
KMIP_WRAPPING_METHOD_ENCRYPT, wkey_info, NULL,
|
|
KMIP_ENCODING_OPTION_NO, 0);
|
|
CHECK_ERROR(wrap_spec == NULL, rc, -ENOMEM, "Allocate KMIP node failed",
|
|
ph, out);
|
|
|
|
uid = kmip_new_unique_identifier(key_id, 0, 0);
|
|
CHECK_ERROR(uid == NULL, rc, -ENOMEM, "Allocate KMIP node failed",
|
|
ph, out);
|
|
|
|
req_pl = kmip_new_get_request_payload(NULL, uid,
|
|
KMIP_KEY_FORMAT_TYPE_RAW, 0, 0,
|
|
wrap_spec);
|
|
CHECK_ERROR(req_pl == NULL, rc, -ENOMEM, "Allocate KMIP node failed",
|
|
ph, out);
|
|
|
|
rc = _perform_kmip_request(ph, KMIP_OPERATION_GET, req_pl, &resp_pl);
|
|
if (rc != 0)
|
|
goto out;
|
|
|
|
rc = kmip_get_get_response_payload(resp_pl, &otype, NULL, &kobj);
|
|
CHECK_ERROR(rc != 0, rc, rc, "Failed to get wrapped key", ph, out);
|
|
CHECK_ERROR(otype != KMIP_OBJECT_TYPE_SYMMETRIC_KEY, rc, -EINVAL,
|
|
"Key is not a symmetric key", ph, out);
|
|
|
|
rc = kmip_get_symmetric_key(kobj, &kblock);
|
|
CHECK_ERROR(rc != 0, rc, rc, "Failed to get symmetric key", ph, out);
|
|
|
|
rc = kmip_get_key_block(kblock, &ftype, NULL, &kval, &algo, &bits,
|
|
&wrap);
|
|
CHECK_ERROR(rc != 0, rc, rc, "Failed to get key block", ph, out);
|
|
CHECK_ERROR(ftype != KMIP_KEY_FORMAT_TYPE_RAW, rc, -EINVAL,
|
|
"Key format is not RAW", ph, out);
|
|
CHECK_ERROR(algo != KMIP_CRYPTO_ALGO_AES, rc, -EINVAL,
|
|
"Key algorithm is not AES", ph, out);
|
|
CHECK_ERROR(bits < 128 || bits > 256, rc, -EINVAL,
|
|
"Key bit size is invalid", ph, out);
|
|
|
|
rc = kmip_get_key_wrapping_data(wrap, &wmethod, &wkinfo, NULL, NULL,
|
|
NULL, NULL, NULL, &enc);
|
|
CHECK_ERROR(rc != 0, rc, rc, "Failed to get wrapping data", ph, out);
|
|
CHECK_ERROR(wmethod != KMIP_WRAPPING_METHOD_ENCRYPT, rc, -EINVAL,
|
|
"Wrapping method is not 'Encrypt'", ph, out);
|
|
if (ph->kmip_version.major > 1 ||
|
|
(ph->kmip_version.major == 1 && ph->kmip_version.minor >= 2)) {
|
|
CHECK_ERROR(enc != KMIP_ENCODING_OPTION_NO, rc, -EINVAL,
|
|
"Encoding is not 'No encoding'", ph, out);
|
|
}
|
|
|
|
rc = kmip_get_key_info(wkinfo, NULL, &wcparms);
|
|
CHECK_ERROR(rc != 0, rc, rc, "Failed to get wrap key infos", ph, out);
|
|
|
|
rc = kmip_get_cryptographic_parameter(wcparms, NULL, &pmeth, &halgo,
|
|
NULL, NULL, &algo, NULL, NULL,
|
|
NULL, NULL, NULL, NULL, NULL,
|
|
NULL, &mgf, &mgfhalgo, NULL);
|
|
CHECK_ERROR(rc != 0, rc, rc, "Failed to get crypto params", ph, out);
|
|
if (ph->kmip_version.major > 1 ||
|
|
(ph->kmip_version.major == 1 && ph->kmip_version.minor >= 2)) {
|
|
CHECK_ERROR(algo != ph->profile->wrap_key_algo, rc, -EINVAL,
|
|
"wrap algorithm is not as expected", ph, out);
|
|
}
|
|
CHECK_ERROR(pmeth != ph->profile->wrap_padding_method, rc, -EINVAL,
|
|
"padding method is not as expected", ph, out);
|
|
if (ph->profile->wrap_padding_method == KMIP_PADDING_METHOD_OAEP) {
|
|
CHECK_ERROR(halgo != ph->profile->wrap_hashing_algo, rc,
|
|
-EINVAL, "hashing algorithm is not as expected",
|
|
ph, out);
|
|
if (ph->kmip_version.major > 1 ||
|
|
(ph->kmip_version.major == 1 &&
|
|
ph->kmip_version.minor >= 4)) {
|
|
CHECK_ERROR(mgf != KMIP_MASK_GENERATOR_MGF1, rc,
|
|
-EINVAL, "OAEP MGF is not as expected",
|
|
ph, out);
|
|
CHECK_ERROR(mgfhalgo != ph->profile->wrap_hashing_algo,
|
|
rc, -EINVAL, "MGF hashing algorithm is not "
|
|
"as expected", ph, out);
|
|
}
|
|
}
|
|
|
|
rc = kmip_get_key_value(kval, &key, NULL, 0, NULL);
|
|
CHECK_ERROR(rc != 0, rc, rc, "Failed to get key value", ph, out);
|
|
|
|
kdata = kmip_node_get_byte_string(key, &klen);
|
|
CHECK_ERROR(kdata == NULL, rc, -ENOMEM, "Failed to get key data",
|
|
ph, out);
|
|
|
|
pr_verbose(&ph->pd, "Wrapped key size: %u", klen);
|
|
*wrapped_key = util_malloc(klen);
|
|
*wrapped_key_len = klen;
|
|
memcpy(*wrapped_key, kdata, klen);
|
|
|
|
pr_verbose(&ph->pd, "AES key size: %u bits", bits);
|
|
*key_bits = bits;
|
|
|
|
out:
|
|
kmip_node_free(cparams);
|
|
kmip_node_free(wrap_id);
|
|
kmip_node_free(wkey_info);
|
|
kmip_node_free(wrap_spec);
|
|
kmip_node_free(uid);
|
|
kmip_node_free(req_pl);
|
|
kmip_node_free(resp_pl);
|
|
kmip_node_free(kobj);
|
|
kmip_node_free(kblock);
|
|
kmip_node_free(kval);
|
|
kmip_node_free(wrap);
|
|
kmip_node_free(wkinfo);
|
|
kmip_node_free(wcparms);
|
|
kmip_node_free(key);
|
|
|
|
if (wrap_key_id != NULL)
|
|
free(wrap_key_id);
|
|
|
|
return rc;
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
* Unwraps (Imports) an wrapped AES key with an CCA RSA wrapping key
|
|
*
|
|
* @param ph the plugin handle
|
|
* @param wrapping_key a buffer containing the wrapping secure key
|
|
* @param wrapping_key_len the size of the wrapping key
|
|
* @param wrapped_key a buffer containing the wrapped key
|
|
* @param wrapped_key_len the size of the wrapped key
|
|
* @param key_blob A buffer to store the secure key to
|
|
* @param key_blob_length On entry: the size of the key blob buffer.
|
|
* On return: the size of the key blob
|
|
*
|
|
* @returns 0 on success, a negative errno in case of an error.
|
|
*/
|
|
static int _cca_unwrap_key_rsa(struct plugin_handle *ph,
|
|
const unsigned char *wrapping_key,
|
|
size_t wrapping_key_len,
|
|
const unsigned char *wrapped_key,
|
|
size_t wrapped_key_len,
|
|
unsigned char *unwrapped_key,
|
|
size_t *unwrapped_key_len)
|
|
{
|
|
long return_code, reason_code, rule_array_count;
|
|
unsigned char rule_array[3 * 8] = { 0 };
|
|
t_CSNDSYI dll_CSNDSYI;
|
|
|
|
/* Get the Symmetric Key Import function */
|
|
dll_CSNDSYI = (t_CSNDSYI)dlsym(ph->cca_lib.cca_lib, "CSNDSYI");
|
|
if (dll_CSNDSYI == NULL) {
|
|
_set_error(ph, "CCA library function CSNDSYI is not available");
|
|
return -ELIBACC;
|
|
}
|
|
|
|
pr_verbose(&ph->pd, "Wrap padding method: %d",
|
|
ph->profile->wrap_padding_method);
|
|
pr_verbose(&ph->pd, "Wrap hashing algorithm: %d",
|
|
ph->profile->wrap_hashing_algo);
|
|
|
|
memset(unwrapped_key, 0, *unwrapped_key_len);
|
|
|
|
switch (ph->profile->wrap_padding_method) {
|
|
case KMIP_PADDING_METHOD_PKCS_1_5:
|
|
rule_array_count = 2;
|
|
memcpy(rule_array, "AES PKCS-1.2 ", 2 * 8);
|
|
break;
|
|
|
|
case KMIP_PADDING_METHOD_OAEP:
|
|
rule_array_count = 3;
|
|
switch (ph->profile->wrap_hashing_algo) {
|
|
case KMIP_HASHING_ALGO_SHA_1:
|
|
memcpy(rule_array, "AES PKCSOAEPSHA-1 ", 3 * 8);
|
|
break;
|
|
|
|
case KMIP_HASHING_ALGO_SHA_256:
|
|
memcpy(rule_array, "AES PKCSOAEPSHA-256 ", 3 * 8);
|
|
break;
|
|
|
|
default:
|
|
_set_error(ph, "Unsupported hashing algorithm: %d",
|
|
ph->profile->wrap_hashing_algo);
|
|
return -EINVAL;
|
|
}
|
|
break;
|
|
default:
|
|
_set_error(ph, "Unsupported padding method: %d",
|
|
ph->profile->wrap_padding_method);
|
|
return -EINVAL;
|
|
}
|
|
|
|
*unwrapped_key_len = AESDATA_KEY_SIZE;
|
|
|
|
dll_CSNDSYI(&return_code, &reason_code, NULL, NULL,
|
|
&rule_array_count, rule_array,
|
|
(long *)&wrapped_key_len, (unsigned char *)wrapped_key,
|
|
(long *)&wrapping_key_len, (unsigned char *)wrapping_key,
|
|
(long *)&unwrapped_key_len, unwrapped_key);
|
|
|
|
if (return_code != 0) {
|
|
_set_error(ph, "CCA CSNDSYI (SYMMETRIC KEY IMPORT) failed. "
|
|
" return: %ld, reason: %ld", return_code,
|
|
reason_code);
|
|
return -EIO;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* Unwraps an wrapped AES key with an EP11 RSA wrapping key
|
|
*
|
|
* @param ph the plugin handle
|
|
* @param wrapping_key a buffer containing the wrapping secure key
|
|
* @param wrapping_key_len the size of the wrapping key
|
|
* @param wrapped_key a buffer containing the wrapped key
|
|
* @param wrapped_key_len the size of the wrapped key
|
|
* @param key_blob A buffer to store the secure key to
|
|
* @param key_blob_length On entry: the size of the key blob buffer.
|
|
* On return: the size of the key blob *
|
|
* @returns 0 on success, a negative errno in case of an error.
|
|
*/
|
|
static int _ep11_unwrap_key_rsa(struct plugin_handle *ph,
|
|
const unsigned char *wrapping_key,
|
|
size_t wrapping_key_len,
|
|
const unsigned char *wrapped_key,
|
|
size_t wrapped_key_len,
|
|
unsigned char *unwrapped_key,
|
|
size_t *unwrapped_key_len)
|
|
{
|
|
CK_OBJECT_CLASS key_class = CKO_SECRET_KEY;
|
|
CK_RSA_PKCS_OAEP_PARAMS oaep_param = { 0 };
|
|
size_t csum_len, key_blob_len, bit_len;
|
|
CK_KEY_TYPE key_type = CKK_AES;
|
|
m_UnwrapKey_t dll_m_UnwrapKey;
|
|
const unsigned char *key_blob;
|
|
struct ep11keytoken *ep11key;
|
|
CK_MECHANISM mech = { 0 };
|
|
CK_BYTE csum[7] = { 0 };
|
|
CK_BBOOL ck_true = true;
|
|
CK_RV rv;
|
|
|
|
CK_ATTRIBUTE template[] = {
|
|
{ CKA_CLASS, &key_class, sizeof(key_class) },
|
|
{ CKA_KEY_TYPE, &key_type, sizeof(key_type) },
|
|
{ CKA_ENCRYPT, &ck_true, sizeof(ck_true) },
|
|
{ CKA_DECRYPT, &ck_true, sizeof(ck_true) },
|
|
{ CKA_SIGN, &ck_true, sizeof(ck_true) },
|
|
{ CKA_VERIFY, &ck_true, sizeof(ck_true) },
|
|
{ CKA_IBM_PROTKEY_EXTRACTABLE, &ck_true, sizeof(ck_true) },
|
|
};
|
|
|
|
dll_m_UnwrapKey =
|
|
(m_UnwrapKey_t)dlsym(ph->ep11.lib_ep11, "m_UnwrapKey");
|
|
if (dll_m_UnwrapKey == NULL) {
|
|
_set_error(ph, "EP11 library function m_UnwrapKey is not "
|
|
"available");
|
|
return -ELIBACC;
|
|
}
|
|
|
|
pr_verbose(&ph->pd, "Wrap padding method: %d",
|
|
ph->profile->wrap_padding_method);
|
|
pr_verbose(&ph->pd, "Wrap hashing algorithm: %d",
|
|
ph->profile->wrap_hashing_algo);
|
|
|
|
if (*unwrapped_key_len < sizeof(struct ep11keytoken)) {
|
|
_set_error(ph, "Key buffer is too small");
|
|
return -EINVAL;
|
|
}
|
|
memset(unwrapped_key, 0, *unwrapped_key_len);
|
|
|
|
switch (ph->profile->wrap_padding_method) {
|
|
case KMIP_PADDING_METHOD_PKCS_1_5:
|
|
mech.mechanism = CKM_RSA_PKCS;
|
|
break;
|
|
|
|
case KMIP_PADDING_METHOD_OAEP:
|
|
mech.mechanism = CKM_RSA_PKCS_OAEP;
|
|
mech.pParameter = &oaep_param;
|
|
mech.ulParameterLen = sizeof(oaep_param);
|
|
|
|
switch (ph->profile->wrap_hashing_algo) {
|
|
case KMIP_HASHING_ALGO_SHA_1:
|
|
oaep_param.hashAlg = CKM_SHA_1;
|
|
oaep_param.mgf = CKG_MGF1_SHA1;
|
|
break;
|
|
|
|
case KMIP_HASHING_ALGO_SHA_256:
|
|
oaep_param.hashAlg = CKM_SHA256;
|
|
oaep_param.mgf = CKG_MGF1_SHA256;
|
|
break;
|
|
|
|
default:
|
|
_set_error(ph, "Unsupported hashing algorithm: %d",
|
|
ph->profile->wrap_hashing_algo);
|
|
return -EINVAL;
|
|
}
|
|
break;
|
|
default:
|
|
_set_error(ph, "Unsupported padding method: %d",
|
|
ph->profile->wrap_padding_method);
|
|
return -EINVAL;
|
|
}
|
|
|
|
key_blob = SK_EP11_get_key_blob(wrapping_key, wrapping_key_len);
|
|
key_blob_len = SK_EP11_get_key_blob_size(wrapping_key,
|
|
wrapping_key_len);
|
|
if (key_blob == NULL || key_blob_len == 0) {
|
|
_set_error(ph, "Invalid EP11 key blob");
|
|
return -EINVAL;
|
|
}
|
|
|
|
csum_len = sizeof(csum);
|
|
|
|
rv = dll_m_UnwrapKey((unsigned char *)wrapped_key, wrapped_key_len,
|
|
key_blob, key_blob_len, NULL, 0, NULL, 0, &mech,
|
|
template, sizeof(template) / sizeof(CK_ATTRIBUTE),
|
|
unwrapped_key, unwrapped_key_len,
|
|
csum, &csum_len,
|
|
ph->ep11_lib.target);
|
|
pr_verbose(&ph->pd, "EP11 m_UnwrapKey: rv: 0x%08lx", rv);
|
|
if (rv != CKR_OK) {
|
|
_set_error(ph, "EP11 m_UnwrapKey failed, rv: 0x%08lx", rv);
|
|
return -EIO;
|
|
}
|
|
|
|
pr_verbose(&ph->pd, "unwrapped_key_len: %lu", *unwrapped_key_len);
|
|
if (*unwrapped_key_len > sizeof(struct ep11keytoken)) {
|
|
_set_error(ph, "Unwrapped EP11 key blob is too long");
|
|
return -EIO;
|
|
}
|
|
|
|
if (csum_len < 4) {
|
|
_set_error(ph, "EP11 m_UnwrapKey returned invalid key infos");
|
|
return -EIO;
|
|
}
|
|
bit_len = csum[csum_len - 1] + 256 * csum[csum_len - 2] +
|
|
256 * 256 * csum[csum_len - 3] +
|
|
256 * 256 * 256 * csum[csum_len - 4];
|
|
|
|
/* Setup the EP11 token header */
|
|
ep11key = (struct ep11keytoken *)unwrapped_key;
|
|
memset(&ep11key->session, 0, sizeof(ep11key->session));
|
|
ep11key->head.type = TOKEN_TYPE_NON_CCA;
|
|
ep11key->head.length = *unwrapped_key_len;
|
|
ep11key->head.version = TOKEN_VERSION_EP11_AES;
|
|
ep11key->head.keybitlen = bit_len;
|
|
|
|
pr_verbose(&ph->pd, "unwrapped bit length: %u",
|
|
ep11key->head.keybitlen);
|
|
|
|
/* return full length, blob is already zero padded */
|
|
*unwrapped_key_len = sizeof(struct ep11keytoken);
|
|
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* Unwraps an wrapped AES key with the RSA wrapping key
|
|
*
|
|
* @param ph the plugin handle
|
|
* @param wrapped_key a buffer containing the wrapped key
|
|
* @param wrapped_key_len the size of the wrapped key
|
|
* @param key_blob A buffer to store the secure key to
|
|
* @param key_blob_length On entry: the size of the key blob buffer.
|
|
* On return: the size of the key blob
|
|
* @param key_type The key type to unwrap
|
|
*
|
|
* @returns 0 on success, a negative errno in case of an error.
|
|
*/
|
|
static int _unwrap_key_rsa(struct plugin_handle *ph,
|
|
const unsigned char *wrapped_key,
|
|
size_t wrapped_key_len,
|
|
unsigned char *key_blob,
|
|
size_t *key_blob_length,
|
|
const char *key_type)
|
|
{
|
|
unsigned char wrapping_key[KMIP_MAX_KEY_TOKEN_SIZE] = { 0 };
|
|
unsigned char secure_key[MAX_SECURE_KEY_SIZE] = { 0 };
|
|
size_t wrapping_key_size = sizeof(wrapping_key);
|
|
size_t secure_key_len = sizeof(secure_key);
|
|
char *wrapping_key_file = NULL;
|
|
unsigned int out_len;
|
|
int rc;
|
|
|
|
wrapping_key_file = properties_get(ph->pd.properties,
|
|
KMIP_CONFIG_WRAPPING_KEY);
|
|
if (wrapping_key_file == NULL) {
|
|
_set_error(ph, "Wrapping key is not available");
|
|
return -EINVAL;
|
|
}
|
|
|
|
rc = SK_UTIL_read_key_blob(wrapping_key_file, wrapping_key,
|
|
&wrapping_key_size);
|
|
if (rc != 0) {
|
|
_set_error(ph, "Failed to load the secure key from '%s': %s",
|
|
wrapping_key_file, strerror(-rc));
|
|
goto out;
|
|
}
|
|
|
|
switch (ph->card_type) {
|
|
case CARD_TYPE_CCA:
|
|
rc = _cca_unwrap_key_rsa(ph, wrapping_key, wrapping_key_size,
|
|
wrapped_key, wrapped_key_len,
|
|
secure_key, &secure_key_len);
|
|
if (rc != 0)
|
|
goto out;
|
|
|
|
if (strcasecmp(key_type, KEY_TYPE_CCA_AESDATA) == 0) {
|
|
if (*key_blob_length < secure_key_len) {
|
|
_set_error(ph, "Secure key too large");
|
|
rc = -EINVAL;
|
|
goto out;
|
|
}
|
|
|
|
*key_blob_length = secure_key_len;
|
|
memcpy(key_blob, secure_key, secure_key_len);
|
|
goto out;
|
|
}
|
|
|
|
if (strcasecmp(key_type, KEY_TYPE_CCA_AESCIPHER) != 0) {
|
|
_set_error(ph, "Unsupported key type '%s'", key_type);
|
|
rc = -EINVAL;
|
|
goto out;
|
|
}
|
|
|
|
out_len = *key_blob_length;
|
|
rc = convert_aes_data_to_cipher_key(&ph->cca,
|
|
secure_key, secure_key_len,
|
|
key_blob, &out_len,
|
|
ph->pd.verbose);
|
|
if (rc != 0) {
|
|
_set_error(ph, "Converting the secure key from "
|
|
"CCA-AESDATA to CCA-AESCIPHER has failed");
|
|
goto out;
|
|
}
|
|
*key_blob_length = out_len;
|
|
|
|
rc = restrict_key_export(&ph->cca, key_blob, *key_blob_length,
|
|
ph->pd.verbose);
|
|
if (rc != 0) {
|
|
_set_error(ph, "Export restricting the secure key has "
|
|
"failed");
|
|
goto out;
|
|
}
|
|
break;
|
|
|
|
case CARD_TYPE_EP11:
|
|
rc = _ep11_unwrap_key_rsa(ph, wrapping_key, wrapping_key_size,
|
|
wrapped_key, wrapped_key_len,
|
|
secure_key, &secure_key_len);
|
|
if (rc != 0)
|
|
goto out;
|
|
|
|
*key_blob_length = secure_key_len;
|
|
memcpy(key_blob, secure_key, secure_key_len);
|
|
break;
|
|
|
|
default:
|
|
_set_error(ph, "Unsupported card type: %d", ph->card_type);
|
|
rc = -EINVAL;
|
|
break;
|
|
}
|
|
|
|
out:
|
|
if (wrapping_key_file != NULL)
|
|
free(wrapping_key_file);
|
|
|
|
return rc;
|
|
}
|
|
|
|
/**
|
|
* Check if the 'Always Sensitive' attribute is True for the key
|
|
*
|
|
* @param ph the plugin handle
|
|
* @param key_id the ID of the key to check
|
|
*
|
|
* @returns 0 on success, a negative errno in case of an error.
|
|
*/
|
|
static int _check_always_sensitive(struct plugin_handle *ph, const char *key_id)
|
|
{
|
|
struct kmip_node *uid = NULL, *req_pl = NULL, *resp_pl = NULL;
|
|
struct kmip_node *attr_ref = NULL, *attr = NULL;
|
|
int rc;
|
|
|
|
uid = kmip_new_unique_identifier(key_id, 0, 0);
|
|
CHECK_ERROR(uid == NULL, rc, -ENOMEM, "Allocate KMIP node failed",
|
|
ph, out);
|
|
|
|
attr_ref = kmip_new_attribute_reference(KMIP_TAG_ALWAYS_SENSITIVE, NULL,
|
|
NULL);
|
|
CHECK_ERROR(attr_ref == NULL, rc, -ENOMEM, "Allocate KMIP node failed",
|
|
ph, out);
|
|
|
|
req_pl = kmip_new_get_attributes_request_payload_va(NULL, uid, 1,
|
|
attr_ref);
|
|
CHECK_ERROR(req_pl == NULL, rc, -ENOMEM, "Allocate KMIP node failed",
|
|
ph, out);
|
|
|
|
rc = _perform_kmip_request(ph, KMIP_OPERATION_GET_ATTRIBUTES, req_pl,
|
|
&resp_pl);
|
|
if (rc != 0)
|
|
goto out;
|
|
|
|
rc = kmip_get_get_attributes_response_payload(resp_pl, NULL, NULL,
|
|
0, &attr);
|
|
CHECK_ERROR(rc != 0, rc, rc, "Failed to get attribute", ph, out);
|
|
CHECK_ERROR(kmip_node_get_tag(attr) != KMIP_TAG_ALWAYS_SENSITIVE, rc,
|
|
-EINVAL, "Unexpected attribute", ph, out);
|
|
|
|
if (kmip_node_get_boolean(attr) != true) {
|
|
_set_error(ph, "The 'Always Sensitive' attribute of the key "
|
|
"'%s' is false. This key might have been retrieved "
|
|
"in clear.", key_id);
|
|
rc = -EPERM;
|
|
goto out;
|
|
}
|
|
|
|
out:
|
|
kmip_node_free(uid);
|
|
kmip_node_free(attr_ref);
|
|
kmip_node_free(req_pl);
|
|
kmip_node_free(resp_pl);
|
|
kmip_node_free(attr);
|
|
|
|
return rc;
|
|
}
|
|
|
|
/**
|
|
* Retrieves an AES key from the KMIP server in the specified key type
|
|
*
|
|
* @param ph the plugin handle
|
|
* @param key_id the key id of the key to get
|
|
* @param key_blob A buffer to store the secure key to
|
|
* @param key_blob_length On entry: the size of the key blob buffer.
|
|
* On return: the size of the key blob
|
|
* @param key_type The key type to retrieve
|
|
* @param key_bits The expected key size, or 0 if it is not known
|
|
*
|
|
* @returns 0 on success, a negative errno in case of an error.
|
|
*/
|
|
static int _retrieve_key(struct plugin_handle *ph, const char *key_id,
|
|
unsigned char *key_blob, size_t *key_blob_length,
|
|
const char *key_type, size_t key_bits)
|
|
{
|
|
|
|
size_t bits = 0, wrapped_key_len = 0;
|
|
unsigned char *wrapped_key = NULL;
|
|
int rc = 0;
|
|
|
|
if (_supports_sensitive_attr(ph) &&
|
|
ph->profile->check_always_sensitive_attr) {
|
|
rc = _check_always_sensitive(ph, key_id);
|
|
if (rc != 0)
|
|
goto out;
|
|
}
|
|
|
|
pr_verbose(&ph->pd, "Wrapping key algorithm: %d",
|
|
ph->profile->wrap_key_algo);
|
|
|
|
switch (ph->profile->wrap_key_algo) {
|
|
case KMIP_CRYPTO_ALGO_RSA:
|
|
rc = _get_key_rsa_wrapped(ph, key_id, &wrapped_key,
|
|
&wrapped_key_len, &bits);
|
|
if (rc != 0)
|
|
goto out;
|
|
|
|
if (key_bits != 0 && key_bits != bits) {
|
|
_set_error(ph, "The retrieved key has an unexpected "
|
|
"key size: %u (expected %u)", bits,
|
|
key_bits);
|
|
rc = -EINVAL;
|
|
goto out;
|
|
}
|
|
|
|
rc = _unwrap_key_rsa(ph, wrapped_key, wrapped_key_len,
|
|
key_blob, key_blob_length, key_type);
|
|
if (rc != 0)
|
|
goto out;
|
|
break;
|
|
default:
|
|
_set_error(ph, "Unsupported wrapping key algorithm: %d",
|
|
ph->profile->wrap_key_algo);
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
out:
|
|
if (wrapped_key != NULL)
|
|
free(wrapped_key);
|
|
|
|
return rc;
|
|
}
|
|
|
|
|
|
/**
|
|
* Generates a key in or with the KMS and returns a secure key that is
|
|
* enciphered under the current HSM master key.
|
|
*
|
|
* @param handle the KMS plugin handle obtained from kms_initialize()
|
|
* @param key_type the zkey key type, euch as 'CCA-AESDATA',
|
|
* 'CCA-AESCIPHER', 'EP11-AES'.
|
|
* @param key_bits the key bit size (e.g. 256 for an AES 256 bit key).
|
|
* @param properties a list of properties to associate the key with
|
|
* @param num_properties the number of properties in above array
|
|
* @param options a list of options as specified by the user. These
|
|
* options are a subset of the possible options as
|
|
* returned by kms_get_command_options() with command
|
|
* KMS_COMMAND_GENERATE.
|
|
* @param num_options number of options in above array.
|
|
* @param key_blob a buffer to return the key blob. The size of the
|
|
* buffer is specified in key_blob_length
|
|
* @param key_blob_length on entry: the size of the key_blob buffer.
|
|
* on exit: the size of the key blob returned.
|
|
* @param key_id a buffer to return the key-ID of the generated key.
|
|
* The key-id is a textual identifier uniquely
|
|
* identifying a key in the KMS and the KMS plugin.
|
|
* The returned key-id contains the terminating zero.
|
|
* @paran key_id_size size of the key_id buffer. It should be at least
|
|
* KMS_KEY_ID_SIZE + 1 bytes large.
|
|
* @param key_label a buffer to return the key-label of the generated
|
|
* key. The key-label is a textual identifier used to
|
|
* identify a key in the user interface of the KMS.
|
|
* A key label may be equal to the key-ID, or it may
|
|
* different. The returned key-label contains the
|
|
* terminating zero.
|
|
* @paran key_label_size size of the key_lanble buffer. It should be at least
|
|
* KMS_KEY_LABEL_SIZE + 1 bytes large.
|
|
*
|
|
* @returns 0 on success, or a negative errno in case of an error.
|
|
* Function kms_get_last_error() can be used to obtain more details about the
|
|
* error.
|
|
*/
|
|
int kms_generate_key(const kms_handle_t handle, const char *key_type,
|
|
size_t key_bits, enum kms_key_mode key_mode,
|
|
const struct kms_property *properties,
|
|
size_t num_properties,
|
|
const struct kms_option *options, size_t num_options,
|
|
unsigned char *key_blob, size_t *key_blob_length,
|
|
char *key_id, size_t key_id_size,
|
|
char *key_label, size_t key_label_size)
|
|
{
|
|
struct plugin_handle *ph = handle;
|
|
char *label = NULL, *id = NULL;
|
|
size_t i;
|
|
int rc;
|
|
|
|
util_assert(handle != NULL, "Internal error: handle is NULL");
|
|
util_assert(num_properties == 0 || properties != NULL,
|
|
"Internal error: properties is NULL but num_properties"
|
|
" > 0 ");
|
|
util_assert(num_options == 0 || options != NULL,
|
|
"Internal error: options is NULL but num_options > 0 ");
|
|
util_assert(key_blob != NULL, "Internal error: key_blob is NULL");
|
|
util_assert(key_blob_length != NULL, "Internal error: key_blob_length "
|
|
"is NULL");
|
|
util_assert(key_id != NULL, "Internal error: key_id is NULL");
|
|
util_assert(key_label != NULL, "Internal error: key_label is NULL");
|
|
|
|
pr_verbose(&ph->pd, "Generate key: key-type: '%s', keybits: %lu, "
|
|
"mode: %d", key_type, key_bits, key_mode);
|
|
for (i = 0; i < num_properties; i++) {
|
|
util_assert(properties[i].name != NULL,
|
|
"Internal error: property name is NULL");
|
|
util_assert(properties[i].value != NULL,
|
|
"Internal error: property value is NULL");
|
|
pr_verbose(&ph->pd, " Property '%s': '%s'", properties[i].name,
|
|
properties[i].value);
|
|
}
|
|
for (i = 0; i < num_options; i++) {
|
|
if (isalnum(options[i].option))
|
|
pr_verbose(&ph->pd, " Option '%c': '%s'",
|
|
options[i].option,
|
|
options[i].argument != NULL ?
|
|
options[i].argument : "(null)");
|
|
else
|
|
pr_verbose(&ph->pd, " Option %d: '%s'",
|
|
options[i].option,
|
|
options[i].argument != NULL ?
|
|
options[i].argument : "(null)");
|
|
}
|
|
|
|
plugin_clear_error(&ph->pd);
|
|
|
|
if (!ph->config_complete) {
|
|
_set_error(ph, "The configuration is incomplete, run 'zkey "
|
|
"kms configure [OPTIONS]' to complete the "
|
|
"configuration.");
|
|
return -EINVAL;
|
|
}
|
|
|
|
if (ph->connection == NULL) {
|
|
rc = _connect_to_server(ph);
|
|
if (rc != 0)
|
|
return rc;
|
|
}
|
|
|
|
for (i = 0; i < num_options; i++) {
|
|
switch (options[i].option) {
|
|
case 'B':
|
|
if (label != NULL)
|
|
break;
|
|
label = _parse_label(ph, options[i].argument, key_mode);
|
|
if (label == NULL)
|
|
return -EINVAL;
|
|
break;
|
|
default:
|
|
if (isalnum(options[i].option))
|
|
_set_error(ph, "Unsupported option '%c'",
|
|
options[i].option);
|
|
else
|
|
_set_error(ph, "Unsupported option %d",
|
|
options[i].option);
|
|
rc = -EINVAL;
|
|
goto out;
|
|
}
|
|
}
|
|
|
|
pr_verbose(&ph->pd, "Label: '%s'", label ? label : "(none)");
|
|
|
|
if (key_bits == 0)
|
|
key_bits = DEFAULT_KEYBITS;
|
|
|
|
rc = _generate_aes_key(ph, key_bits, properties, num_properties, label,
|
|
&id);
|
|
if (rc != 0)
|
|
goto out;
|
|
|
|
rc = _retrieve_key(ph, id, key_blob, key_blob_length, key_type,
|
|
key_bits);
|
|
if (rc != 0)
|
|
goto out;
|
|
|
|
strncpy(key_id, id, key_id_size);
|
|
key_id[key_id_size - 1] = '\0';
|
|
|
|
strncpy(key_label, label != NULL ? label : id, key_label_size);
|
|
key_label[key_label_size - 1] = '\0';
|
|
|
|
pr_verbose(&ph->pd, "Generated key id: '%s'", key_id);
|
|
pr_verbose(&ph->pd, "Generated key label: '%s'", key_label);
|
|
|
|
out:
|
|
if (label != NULL)
|
|
free(label);
|
|
if (id != NULL)
|
|
free(id);
|
|
|
|
return rc;
|
|
}
|
|
|
|
/**
|
|
* Set (Add/Modify) or delete a key attribute.
|
|
*
|
|
* @param ph the plugin handle
|
|
* @param key_id the ID of the key to set/delet th attribute for
|
|
* @param prop the KMS property. If the value is NULL, the
|
|
* attribute is deleted
|
|
*
|
|
* @returns 0 on success, a negative errno in case of an error.
|
|
*/
|
|
static int _set_key_attribute(struct plugin_handle *ph, const char *key_id,
|
|
const struct kms_property *prop)
|
|
{
|
|
struct kmip_node *req1_pl = NULL, *resp1_pl = NULL, *req2_pl = NULL;
|
|
struct kmip_node *resp2_pl = NULL, *attr_ref = NULL, *attr = NULL;
|
|
struct kmip_node *uid1 = NULL, *uid2 = NULL;
|
|
enum kmip_operation op2 = 0;
|
|
int rc;
|
|
|
|
uid1 = kmip_new_unique_identifier(key_id, 0, 0);
|
|
CHECK_ERROR(uid1 == NULL, rc, -ENOMEM, "Allocate KMIP node failed",
|
|
ph, out);
|
|
|
|
attr_ref = _build_attr_ref_from_prop(ph, prop);
|
|
CHECK_ERROR(attr_ref == NULL, rc, -ENOMEM, "Allocate KMIP node failed",
|
|
ph, out);
|
|
|
|
req1_pl = kmip_new_delete_attribute_request_payload(NULL, uid1, NULL,
|
|
attr_ref);
|
|
CHECK_ERROR(req1_pl == NULL, rc, -ENOMEM, "Allocate KMIP node failed",
|
|
ph, out);
|
|
|
|
if (prop->value != NULL) {
|
|
uid2 = kmip_new_unique_identifier(key_id, 0, 0);
|
|
CHECK_ERROR(uid2 == NULL, rc, -ENOMEM,
|
|
"Allocate KMIP node failed", ph, out);
|
|
|
|
attr = _build_attr_from_prop(ph, prop);
|
|
CHECK_ERROR(attr == NULL, rc, -ENOMEM,
|
|
"Allocate KMIP node failed", ph, out);
|
|
|
|
req2_pl = kmip_new_add_attribute_request_payload(NULL, uid2,
|
|
attr);
|
|
CHECK_ERROR(req2_pl == NULL, rc, -ENOMEM,
|
|
"Allocate KMIP node failed", ph, out);
|
|
|
|
op2 = KMIP_OPERATION_ADD_ATTRIBUTE;
|
|
}
|
|
|
|
rc = _perform_kmip_request2(ph, KMIP_OPERATION_DELETE_ATTRIBUTE,
|
|
req1_pl, &resp1_pl, op2, req2_pl, &resp2_pl,
|
|
KMIP_BATCH_ERR_CONT_CONTINUE);
|
|
|
|
out:
|
|
kmip_node_free(uid1);
|
|
kmip_node_free(attr_ref);
|
|
kmip_node_free(req1_pl);
|
|
kmip_node_free(resp1_pl);
|
|
kmip_node_free(uid2);
|
|
kmip_node_free(attr);
|
|
kmip_node_free(req2_pl);
|
|
kmip_node_free(resp2_pl);
|
|
|
|
return rc;
|
|
}
|
|
|
|
/**
|
|
* Sets (adds/replaces/removes) properties of a key. Already existing properties
|
|
* with the same property name are replaced, non-existing properties are added.
|
|
* To remove a property, set the property value to NULL.
|
|
*
|
|
* @param handle the KMS plugin handle obtained from kms_initialize()
|
|
* @param key_id the key-ID to set the properties for
|
|
* @param properties a list of properties to set
|
|
* @param num_properties the number of properties in above array
|
|
*
|
|
* @returns 0 on success, or a negative errno in case of an error.
|
|
* Function kms_get_last_error() can be used to obtain more details about the
|
|
* error.
|
|
*/
|
|
int kms_set_key_properties(const kms_handle_t handle, const char *key_id,
|
|
const struct kms_property *properties,
|
|
size_t num_properties)
|
|
{
|
|
struct plugin_handle *ph = handle;
|
|
int rc = 0;
|
|
size_t i;
|
|
|
|
util_assert(handle != NULL, "Internal error: handle is NULL");
|
|
util_assert(key_id != NULL, "Internal error: key_id is NULL");
|
|
util_assert(num_properties == 0 || properties != NULL,
|
|
"Internal error: properties is NULL but num_properties"
|
|
" > 0 ");
|
|
|
|
pr_verbose(&ph->pd, "Set key properties: key-ID: '%s'", key_id);
|
|
for (i = 0; i < num_properties; i++) {
|
|
util_assert(properties[i].name != NULL,
|
|
"Internal error: property name is NULL");
|
|
|
|
pr_verbose(&ph->pd, " Property '%s': '%s'", properties[i].name,
|
|
properties[i].value != NULL ? properties[i].value :
|
|
"(null)");
|
|
}
|
|
|
|
plugin_clear_error(&ph->pd);
|
|
|
|
if (!ph->config_complete) {
|
|
_set_error(ph, "The configuration is incomplete, run 'zkey "
|
|
"kms configure [OPTIONS]' to complete the "
|
|
"configuration.");
|
|
return -EINVAL;
|
|
}
|
|
|
|
if (ph->connection == NULL) {
|
|
rc = _connect_to_server(ph);
|
|
if (rc != 0)
|
|
return rc;
|
|
}
|
|
|
|
for (i = 0; i < num_properties; i++) {
|
|
rc = _set_key_attribute(ph, key_id, &properties[i]);
|
|
if (rc != 0)
|
|
break;
|
|
}
|
|
|
|
return rc;
|
|
}
|
|
|
|
/**
|
|
* Ensures that the properties array is at least count elements large, if not
|
|
* then it is reallocated to be at least that large, and the size is updated.
|
|
*
|
|
* @param array the properties array. May be updated with a
|
|
* re-allocated array
|
|
* @param size the size of the array. May be updated with the
|
|
* new size of the array
|
|
* @param count The number of required elements
|
|
*/
|
|
static void _reealloc_props_array(struct kms_property **array,
|
|
unsigned int *size, unsigned int count)
|
|
{
|
|
if (*size >= count)
|
|
return;
|
|
|
|
(*size) += 10;
|
|
*array = util_realloc(*array, *size * sizeof(struct kms_property));
|
|
}
|
|
|
|
/**
|
|
* Get a list of key attributes that can be mapped to KMS properties.
|
|
* The returned list of properties must be freed by the caller. Each property
|
|
* name and value must be freed individually (using free()), as well as the
|
|
* complete array.
|
|
*
|
|
* @param ph the plugin handle
|
|
* @param key_id the ID of the key to get the attributes for
|
|
* @param properties On return: a list of properties
|
|
* @param num_properties On return: the number of properties in above array
|
|
* @param key_name On return: the 'Name' of the key or NULL if no Name.
|
|
* Must be breed by the caller. Can be NULL to
|
|
* retrieve the name.
|
|
* @param key_bits On return: the size of the key in bits. Can be NULL.
|
|
* @param state On return: the state of the key. Can be NULL.
|
|
* @param obj_type On return: the object type. Can be NULL.
|
|
* @param algo On return: the algorithm of the key. Can be NULL.
|
|
* @param sensitive On return: true if the key is sensitive
|
|
* @param always_sensitive On return: true if the key was always sensitive
|
|
*
|
|
* @returns 0 on success, a negative errno in case of an error.
|
|
*/
|
|
static int _get_key_attributes(struct plugin_handle *ph, const char *key_id,
|
|
struct kms_property **properties,
|
|
size_t *num_properties, char **key_name,
|
|
size_t *key_bits, enum kmip_state *state,
|
|
enum kmip_object_type *obj_type,
|
|
enum kmip_crypto_algo *algo,
|
|
bool *sensitive, bool *always_sensitive)
|
|
{
|
|
struct kmip_node *req_pl = NULL, *resp_pl = NULL, *uid = NULL;
|
|
struct kmip_node *attr = NULL, *linked_id = NULL;
|
|
const char *description, *id, *name, *value;
|
|
struct kms_property *props = NULL;
|
|
enum kmip_link_type link_type;
|
|
unsigned int i, k, count = 0;
|
|
int32_t key_size;
|
|
int rc;
|
|
|
|
if (key_name != NULL)
|
|
*key_name = NULL;
|
|
if (key_bits != NULL)
|
|
*key_bits = 0;
|
|
if (state != NULL)
|
|
*state = 0;
|
|
if (obj_type != NULL)
|
|
*obj_type = 0;
|
|
if (algo != NULL)
|
|
*algo = 0;
|
|
if (sensitive != NULL)
|
|
*sensitive = false;
|
|
if (always_sensitive != NULL)
|
|
*always_sensitive = false;
|
|
|
|
uid = kmip_new_unique_identifier(key_id, 0, 0);
|
|
CHECK_ERROR(uid == NULL, rc, -ENOMEM, "Allocate KMIP node failed",
|
|
ph, out);
|
|
|
|
/* With no Attr-Refs specified, all attributes are to be returned */
|
|
req_pl = kmip_new_get_attributes_request_payload(NULL, uid, 0, NULL);
|
|
CHECK_ERROR(req_pl == NULL, rc, -ENOMEM, "Allocate KMIP node failed",
|
|
ph, out);
|
|
|
|
rc = _perform_kmip_request(ph, KMIP_OPERATION_GET_ATTRIBUTES, req_pl,
|
|
&resp_pl);
|
|
if (rc != 0)
|
|
goto out;
|
|
|
|
for (i = 0, k = 0; ; i++) {
|
|
rc = kmip_get_get_attributes_response_payload(resp_pl, NULL,
|
|
NULL, i, &attr);
|
|
if (rc != 0)
|
|
break;
|
|
|
|
switch (kmip_node_get_tag(attr)) {
|
|
case KMIP_TAG_DESCRIPTION:
|
|
if (!_supports_description_attr(ph))
|
|
break;
|
|
|
|
rc = kmip_get_description(attr, &description);
|
|
CHECK_ERROR(rc != 0, rc, rc,
|
|
"Failed to get description", ph, out);
|
|
|
|
if (description == NULL ||
|
|
strcmp(description, " ") == 0)
|
|
description = "";
|
|
|
|
_reealloc_props_array(&props, &count, k + 1);
|
|
props[k].name = util_strdup(KMS_KEY_PROP_DESCRIPTION);
|
|
props[k].value = util_strdup(description);
|
|
k++;
|
|
break;
|
|
|
|
case KMIP_TAG_COMMENT:
|
|
if (!_supports_comment_attr(ph) ||
|
|
_supports_description_attr(ph))
|
|
break;
|
|
|
|
rc = kmip_get_comment(attr, &description);
|
|
CHECK_ERROR(rc != 0, rc, rc,
|
|
"Failed to get comment", ph, out);
|
|
|
|
if (description == NULL ||
|
|
strcmp(description, " ") == 0)
|
|
description = "";
|
|
|
|
_reealloc_props_array(&props, &count, k + 1);
|
|
props[k].name = util_strdup(KMS_KEY_PROP_DESCRIPTION);
|
|
props[k].value = util_strdup(description);
|
|
k++;
|
|
|
|
break;
|
|
|
|
case KMIP_TAG_LINK:
|
|
if (!ph->profile->supports_link_attr)
|
|
break;
|
|
|
|
rc = kmip_get_link(attr, &link_type, &linked_id);
|
|
CHECK_ERROR(rc != 0, rc, rc,
|
|
"Failed to get link", ph, out);
|
|
|
|
_reealloc_props_array(&props, &count, k + 1);
|
|
if (link_type == KMIP_LINK_TYPE_NEXT)
|
|
props[k].name =
|
|
util_strdup(KMS_KEY_PROP_XTS_KEY2_ID);
|
|
else if (link_type == KMIP_LINK_TYPE_PREVIOUS)
|
|
props[k].name =
|
|
util_strdup(KMS_KEY_PROP_XTS_KEY1_ID);
|
|
else
|
|
break;
|
|
|
|
rc = kmip_get_linked_object_identifier(linked_id, &id,
|
|
NULL, NULL);
|
|
CHECK_ERROR(rc != 0, rc, rc,
|
|
"Failed to get link ID", ph, out);
|
|
props[k].value = util_strdup(id);
|
|
k++;
|
|
|
|
kmip_node_free(linked_id);
|
|
linked_id = NULL;
|
|
break;
|
|
|
|
case KMIP_TAG_ATTRIBUTE: /* Custom/Vendor attribute */
|
|
if (!_get_custom_attr(ph, attr, &name, &value))
|
|
break;
|
|
|
|
_reealloc_props_array(&props, &count, k + 1);
|
|
props[k].name = util_strdup(name);
|
|
props[k].value = util_strdup(value);
|
|
k++;
|
|
break;
|
|
|
|
case KMIP_TAG_NAME:
|
|
if (key_name == NULL || *key_name != NULL)
|
|
break;
|
|
|
|
rc = kmip_get_name(attr, &name, NULL);
|
|
CHECK_ERROR(rc != 0, rc, rc,
|
|
"Failed to get key name", ph, out);
|
|
|
|
if (name == NULL || strcmp(name, " ") == 0)
|
|
name = "";
|
|
|
|
*key_name = util_strdup(name);
|
|
break;
|
|
|
|
case KMIP_TAG_CRYPTOGRAPHIC_LENGTH:
|
|
if (key_bits == NULL)
|
|
break;
|
|
|
|
rc = kmip_get_cryptographic_length(attr, &key_size);
|
|
CHECK_ERROR(rc != 0, rc, rc,
|
|
"Failed to get key size", ph, out);
|
|
|
|
*key_bits = key_size;
|
|
break;
|
|
|
|
case KMIP_TAG_CRYPTOGRAPHIC_ALGORITHM:
|
|
if (algo == NULL)
|
|
break;
|
|
|
|
rc = kmip_get_cryptographic_algorithm(attr, algo);
|
|
CHECK_ERROR(rc != 0, rc, rc,
|
|
"Failed to get key algorithm", ph, out);
|
|
break;
|
|
|
|
case KMIP_TAG_OBJECT_TYPE:
|
|
if (obj_type == NULL)
|
|
break;
|
|
|
|
rc = kmip_get_object_type(attr, obj_type);
|
|
CHECK_ERROR(rc != 0, rc, rc,
|
|
"Failed to get object type", ph, out);
|
|
break;
|
|
|
|
case KMIP_TAG_STATE:
|
|
if (state == NULL)
|
|
break;
|
|
|
|
rc = kmip_get_state(attr, state);
|
|
CHECK_ERROR(rc != 0, rc, rc,
|
|
"Failed to get key state", ph, out);
|
|
break;
|
|
|
|
case KMIP_TAG_SENSITIVE:
|
|
if (sensitive == NULL)
|
|
break;
|
|
|
|
rc = kmip_get_sensitive(attr, sensitive);
|
|
CHECK_ERROR(rc != 0, rc, rc,
|
|
"Failed to get sensitive flag", ph, out);
|
|
break;
|
|
|
|
case KMIP_TAG_ALWAYS_SENSITIVE:
|
|
if (always_sensitive == NULL)
|
|
break;
|
|
|
|
rc = kmip_get_always_sensitive(attr, always_sensitive);
|
|
CHECK_ERROR(rc != 0, rc, rc,
|
|
"Failed to get always sensitive flag",
|
|
ph, out);
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
|
|
kmip_node_free(attr);
|
|
attr = NULL;
|
|
}
|
|
|
|
*num_properties = k;
|
|
*properties = props;
|
|
|
|
rc = 0;
|
|
|
|
out:
|
|
kmip_node_free(uid);
|
|
kmip_node_free(req_pl);
|
|
kmip_node_free(resp_pl);
|
|
kmip_node_free(attr);
|
|
kmip_node_free(linked_id);
|
|
|
|
if (rc != 0 && props != NULL) {
|
|
for (i = 0; i < count; i++) {
|
|
free((char *)props[i].name);
|
|
free((char *)props[i].value);
|
|
}
|
|
free(props);
|
|
}
|
|
if (rc != 0 && key_name != NULL && *key_name != NULL) {
|
|
free(*key_name);
|
|
*key_name = NULL;
|
|
}
|
|
|
|
return rc;
|
|
}
|
|
|
|
/**
|
|
* Gets properties of a key.
|
|
*
|
|
* The returned list of properties must be freed by the caller. Each property
|
|
* name and value must be freed individually (using free()), as well as the
|
|
* complete array.
|
|
*
|
|
* @param handle the KMS plugin handle obtained from kms_initialize()
|
|
* @param key_id the key-ID to set the properties for
|
|
* @param properties On return: a list of properties
|
|
* @param num_properties On return: the number of properties in above array
|
|
*
|
|
* @returns 0 on success, or a negative errno in case of an error.
|
|
* Function kms_get_last_error() can be used to obtain more details about the
|
|
* error.
|
|
*/
|
|
int kms_get_key_properties(const kms_handle_t handle, const char *key_id,
|
|
struct kms_property **properties,
|
|
size_t *num_properties)
|
|
{
|
|
struct plugin_handle *ph = handle;
|
|
bool sensitive, always_sensitive;
|
|
enum kmip_object_type obj_type;
|
|
enum kmip_crypto_algo algo;
|
|
enum kmip_state state;
|
|
size_t i;
|
|
int rc;
|
|
|
|
util_assert(handle != NULL, "Internal error: handle is NULL");
|
|
util_assert(key_id != NULL, "Internal error: key_id is NULL");
|
|
util_assert(properties != NULL, "Internal error: properties is NULL");
|
|
util_assert(num_properties != NULL,
|
|
"Internal error: num_properties is NULL");
|
|
|
|
pr_verbose(&ph->pd, "Get key properties: key-ID: '%s'", key_id);
|
|
|
|
plugin_clear_error(&ph->pd);
|
|
|
|
if (!ph->config_complete) {
|
|
_set_error(ph, "The configuration is incomplete, run 'zkey "
|
|
"kms configure [OPTIONS]' to complete the "
|
|
"configuration.");
|
|
return -EINVAL;
|
|
}
|
|
|
|
if (ph->connection == NULL) {
|
|
rc = _connect_to_server(ph);
|
|
if (rc != 0)
|
|
return rc;
|
|
}
|
|
|
|
rc = _get_key_attributes(ph, key_id, properties, num_properties,
|
|
NULL, NULL, &state, &obj_type, &algo,
|
|
&sensitive, &always_sensitive);
|
|
if (rc != 0)
|
|
goto out;
|
|
|
|
if (state != KMIP_STATE_ACTIVE) {
|
|
_set_error(ph, "The key '%s' is not in state ACTIVE.", key_id);
|
|
rc = -EINVAL;
|
|
goto out;
|
|
}
|
|
|
|
if (obj_type != KMIP_OBJECT_TYPE_SYMMETRIC_KEY) {
|
|
_set_error(ph, "The key '%s' is not a symmetric key.", key_id);
|
|
rc = -EINVAL;
|
|
goto out;
|
|
}
|
|
|
|
if (algo != KMIP_CRYPTO_ALGO_AES) {
|
|
_set_error(ph, "The key '%s' is not an AES key.", key_id);
|
|
rc = -EINVAL;
|
|
goto out;
|
|
}
|
|
|
|
if (_supports_sensitive_attr(ph) && sensitive == false) {
|
|
_set_error(ph, "The key '%s' is not sensitive.", key_id);
|
|
rc = -EINVAL;
|
|
goto out;
|
|
}
|
|
|
|
if (_supports_sensitive_attr(ph) &&
|
|
ph->profile->check_always_sensitive_attr &&
|
|
always_sensitive == false) {
|
|
_set_error(ph, "The key '%s' was not always sensitive.",
|
|
key_id);
|
|
rc = -EINVAL;
|
|
goto out;
|
|
}
|
|
|
|
for (i = 0; i < *num_properties; i++) {
|
|
util_assert((*properties)[i].name != NULL,
|
|
"Internal error: property name is NULL");
|
|
|
|
pr_verbose(&ph->pd, " Property '%s': '%s'",
|
|
(*properties)[i].name, (*properties)[i].value);
|
|
}
|
|
|
|
out:
|
|
return rc;
|
|
}
|
|
|
|
/**
|
|
* Called when zkey removes a KMS-bound key from the zkey repository. The KMS
|
|
* plugin can then set the state of the key in the KMS, or remove it also from
|
|
* the KMS (this is usually not done).
|
|
*
|
|
* @param handle the KMS plugin handle obtained from kms_initialize()
|
|
* @param key_id the key-ID to set the properties for
|
|
* @param options a list of options as specified by the user. These
|
|
* options are a subset of the possible options as
|
|
* returned by kms_get_command_options() with command
|
|
* KMS_COMMAND_REMOVE.
|
|
* @param num_options number of options in above array.
|
|
*
|
|
* @returns 0 on success, or a negative errno in case of an error.
|
|
* Function kms_get_last_error() can be used to obtain more details about the
|
|
* error.
|
|
*/
|
|
int kms_remove_key(const kms_handle_t handle, const char *key_id,
|
|
const struct kms_option *options, size_t num_options)
|
|
{
|
|
struct plugin_handle *ph = handle;
|
|
char *state = NULL;
|
|
int rc = 0;
|
|
size_t i;
|
|
|
|
util_assert(handle != NULL, "Internal error: handle is NULL");
|
|
util_assert(key_id != NULL, "Internal error: key_id is NULL");
|
|
util_assert(num_options == 0 || options != NULL,
|
|
"Internal error: options is NULL but num_options > 0 ");
|
|
|
|
pr_verbose(&ph->pd, "Remove key: key-ID: '%s'", key_id);
|
|
for (i = 0; i < num_options; i++) {
|
|
if (isalnum(options[i].option))
|
|
pr_verbose(&ph->pd, " Option '%c': '%s'",
|
|
options[i].option,
|
|
options[i].argument != NULL ?
|
|
options[i].argument : "(null)");
|
|
else
|
|
pr_verbose(&ph->pd, " Option %d: '%s'",
|
|
options[i].option,
|
|
options[i].argument != NULL ?
|
|
options[i].argument : "(null)");
|
|
}
|
|
|
|
plugin_clear_error(&ph->pd);
|
|
|
|
for (i = 0; i < num_options; i++) {
|
|
switch (options[i].option) {
|
|
case 's':
|
|
state = util_strdup(options[i].argument);
|
|
util_str_toupper(state);
|
|
break;
|
|
default:
|
|
rc = -EINVAL;
|
|
if (isalnum(options[i].option))
|
|
_set_error(ph, "Unsupported option '%c'",
|
|
options[i].option);
|
|
else
|
|
_set_error(ph, "Unsupported option %d",
|
|
options[i].option);
|
|
goto out;
|
|
}
|
|
}
|
|
|
|
if (state == NULL)
|
|
goto out;
|
|
|
|
pr_verbose(&ph->pd, "State to set: '%s'", state);
|
|
if (strcmp(state, KMIP_KEY_STATE_DEACTIVATED) != 0 &&
|
|
strcmp(state, KMIP_KEY_STATE_COMPROMISED) != 0 &&
|
|
strcmp(state, KMIP_KEY_STATE_DESTROYED) != 0 &&
|
|
strcmp(state, KMIP_KEY_STATE_DESTROYED_COMPROMISED) != 0) {
|
|
_set_error(ph, "Invalid state specified: '%s'", state);
|
|
rc = -EINVAL;
|
|
goto out;
|
|
}
|
|
|
|
if (!ph->config_complete) {
|
|
_set_error(ph, "The configuration is incomplete, run 'zkey "
|
|
"kms configure [OPTIONS]' to complete the "
|
|
"configuration.");
|
|
return -EINVAL;
|
|
}
|
|
|
|
if (ph->connection == NULL) {
|
|
rc = _connect_to_server(ph);
|
|
if (rc != 0)
|
|
return rc;
|
|
}
|
|
|
|
if (strcmp(state, KMIP_KEY_STATE_DEACTIVATED) == 0 ||
|
|
strcmp(state, KMIP_KEY_STATE_DESTROYED) == 0) {
|
|
rc = _set_key_state(ph, key_id, KMIP_STATE_DEACTIVATED);
|
|
if (rc != 0)
|
|
goto out;
|
|
}
|
|
|
|
if (strcmp(state, KMIP_KEY_STATE_COMPROMISED) == 0 ||
|
|
strcmp(state, KMIP_KEY_STATE_DESTROYED_COMPROMISED) == 0) {
|
|
rc = _set_key_state(ph, key_id, KMIP_STATE_COMPROMISED);
|
|
if (rc != 0)
|
|
goto out;
|
|
}
|
|
|
|
if (strcmp(state, KMIP_KEY_STATE_DESTROYED) == 0 ||
|
|
strcmp(state, KMIP_KEY_STATE_DESTROYED_COMPROMISED) == 0) {
|
|
rc = _set_key_state(ph, key_id, KMIP_STATE_DESTROYED);
|
|
if (rc != 0)
|
|
goto out;
|
|
}
|
|
|
|
out:
|
|
if (state != NULL)
|
|
free(state);
|
|
|
|
return rc;
|
|
}
|
|
|
|
/**
|
|
* Process a located key item.
|
|
*
|
|
* @param ph the plugin handle
|
|
* @param key_id the ID of the key found
|
|
* @param label_pattern a pattern of the label used to filter the keys, or
|
|
* NULL if no label pattern is specified.
|
|
* @param key_type the key type
|
|
* @param callback a callback function that is called for each key that
|
|
* matches the filter (if any).
|
|
* @private_data a private pointer passed as is to the callback
|
|
* function. Can be used to pass user specific
|
|
* information to the callback.
|
|
*
|
|
* @returns 0 on success, a negative errno in case of an error.
|
|
*/
|
|
static int _process_list_item(struct plugin_handle *ph, const char *key_id,
|
|
const char *label_pattern, const char *key_type,
|
|
kms_list_callback callback, void *private_data)
|
|
{
|
|
size_t i, key_bits = 0, num_properties = 0;
|
|
struct kms_property *properties = NULL;
|
|
bool sensitive, always_sensitive;
|
|
enum kmip_object_type obj_type;
|
|
enum kmip_crypto_algo algo;
|
|
enum kmip_state state;
|
|
char *name = NULL;
|
|
int rc;
|
|
|
|
rc = _get_key_attributes(ph, key_id, &properties, &num_properties,
|
|
&name, &key_bits, &state, &obj_type, &algo,
|
|
&sensitive, &always_sensitive);
|
|
if (rc != 0)
|
|
goto out;
|
|
|
|
pr_verbose(&ph->pd, "Name: '%s'", name ? name : "(none)");
|
|
|
|
if (state != KMIP_STATE_ACTIVE) {
|
|
pr_verbose(&ph->pd, "State is not ACTIVE, skip.");
|
|
goto out;
|
|
}
|
|
|
|
if (obj_type != KMIP_OBJECT_TYPE_SYMMETRIC_KEY) {
|
|
pr_verbose(&ph->pd, "Object type is not Symmetric Key, skip.");
|
|
goto out;
|
|
}
|
|
|
|
if (algo != KMIP_CRYPTO_ALGO_AES) {
|
|
pr_verbose(&ph->pd, "Key algorithm is not AES, skip.");
|
|
goto out;
|
|
}
|
|
|
|
if (_supports_sensitive_attr(ph) && sensitive == false) {
|
|
pr_verbose(&ph->pd, "The key is not sensitive.");
|
|
goto out;
|
|
}
|
|
|
|
if (_supports_sensitive_attr(ph) &&
|
|
ph->profile->check_always_sensitive_attr &&
|
|
always_sensitive == false) {
|
|
pr_verbose(&ph->pd, "The key was not always sensitive.");
|
|
goto out;
|
|
}
|
|
|
|
if (label_pattern != NULL) {
|
|
if (fnmatch(label_pattern, name ? name : key_id, 0) != 0) {
|
|
pr_verbose(&ph->pd, "Label filter not matched");
|
|
goto out;
|
|
}
|
|
}
|
|
|
|
for (i = 0; i < num_properties; i++) {
|
|
pr_verbose(&ph->pd, " Property '%s': '%s'", properties[i].name,
|
|
properties[i].value != NULL ? properties[i].value :
|
|
"(null)");
|
|
}
|
|
|
|
rc = callback(key_id, name ? name : key_id, key_type ? key_type :
|
|
"(any)", key_bits, properties, num_properties, NULL, 0,
|
|
private_data);
|
|
|
|
out:
|
|
if (properties != NULL) {
|
|
for (i = 0; i < num_properties; i++) {
|
|
free((char *)properties[i].name);
|
|
free((char *)properties[i].value);
|
|
}
|
|
free(properties);
|
|
}
|
|
if (name != NULL)
|
|
free(name);
|
|
|
|
return rc;
|
|
}
|
|
|
|
/**
|
|
* List keys managed by the KMS. This list is independent of the zkey key
|
|
* repository. It lists keys as known by the KMS.
|
|
*
|
|
* @param handle the KMS plugin handle obtained from kms_initialize()
|
|
* @param label_pattern a pattern of the label used to filter the keys, or
|
|
* NULL if no label pattern is specified.
|
|
* @param properties a list of properties used to filter the keys, or
|
|
* NULL if no properties filter is specified.
|
|
* @param num_properties the number of properties in above array.
|
|
* @param options a list of options as specified by the user. These
|
|
* options are a subset of the possible options as
|
|
* returned by kms_get_command_options() with command
|
|
* KMS_COMMAND_LIST.
|
|
* @param num_options number of options in above array.*
|
|
* @param callback a callback function that is called for each key that
|
|
* matches the filter (if any).
|
|
* @private_data a private pointer passed as is to the callback
|
|
* function. Can be used to pass user specific
|
|
* information to the callback.
|
|
*
|
|
* @returns 0 on success, or a negative errno in case of an error.
|
|
* Function kms_get_last_error() can be used to obtain more details about the
|
|
* error.
|
|
*/
|
|
int kms_list_keys(const kms_handle_t handle, const char *label_pattern,
|
|
const struct kms_property *properties, size_t num_properties,
|
|
const struct kms_option *options, size_t num_options,
|
|
kms_list_callback callback, void *private_data)
|
|
{
|
|
struct kmip_node *req_pl = NULL, *resp_pl = NULL, *item_uid = NULL;
|
|
struct plugin_handle *ph = handle;
|
|
struct kmip_node **attrs = NULL;
|
|
bool label_filter = false;
|
|
char *key_type = NULL;
|
|
size_t num_attrs;
|
|
const char *id;
|
|
size_t i, k;
|
|
int rc = 0;
|
|
|
|
util_assert(handle != NULL, "Internal error: handle is NULL");
|
|
util_assert(num_properties == 0 || properties != NULL,
|
|
"Internal error: properties is NULL but num_properties "
|
|
"> 0 ");
|
|
util_assert(callback != NULL, "Internal error: callback is NULL");
|
|
|
|
pr_verbose(&ph->pd, "List Keys, label-pattern: '%s'",
|
|
label_pattern != NULL ? label_pattern : "(null)");
|
|
for (i = 0; i < num_properties; i++) {
|
|
util_assert(properties[i].name != NULL,
|
|
"Internal error: property name is NULL");
|
|
util_assert(properties[i].value != NULL,
|
|
"Internal error: property value is NULL");
|
|
pr_verbose(&ph->pd, " Property '%s': '%s'", properties[i].name,
|
|
properties[i].value);
|
|
}
|
|
for (i = 0; i < num_options; i++) {
|
|
if (isalnum(options[i].option))
|
|
pr_verbose(&ph->pd, " Option '%c': '%s'",
|
|
options[i].option,
|
|
options[i].argument != NULL ?
|
|
options[i].argument : "(null)");
|
|
else
|
|
pr_verbose(&ph->pd, " Option %d: '%s'",
|
|
options[i].option,
|
|
options[i].argument != NULL ?
|
|
options[i].argument : "(null)");
|
|
}
|
|
|
|
plugin_clear_error(&ph->pd);
|
|
|
|
for (i = 0; i < num_options; i++) {
|
|
switch (options[i].option) {
|
|
case 'K':
|
|
key_type = util_strdup(options[i].argument);
|
|
util_str_toupper(key_type);
|
|
break;
|
|
default:
|
|
rc = -EINVAL;
|
|
if (isalnum(options[i].option))
|
|
_set_error(ph, "Unsupported option '%c'",
|
|
options[i].option);
|
|
else
|
|
_set_error(ph, "Unsupported option %d",
|
|
options[i].option);
|
|
goto out;
|
|
}
|
|
}
|
|
|
|
if (key_type != NULL) {
|
|
switch (ph->card_type) {
|
|
case CARD_TYPE_CCA:
|
|
if (strcasecmp(key_type, KEY_TYPE_CCA_AESDATA) != 0 &&
|
|
strcasecmp(key_type, KEY_TYPE_CCA_AESCIPHER) != 0) {
|
|
_set_error(ph, "The KMIP plugin is bound to "
|
|
"CCA-type APQNs, and can only "
|
|
"import keys of type '%s' or '%s'.",
|
|
KEY_TYPE_CCA_AESDATA,
|
|
KEY_TYPE_CCA_AESCIPHER);
|
|
rc = -EINVAL;
|
|
goto out;
|
|
}
|
|
break;
|
|
case CARD_TYPE_EP11:
|
|
if (strcasecmp(key_type, KEY_TYPE_EP11_AES) != 0) {
|
|
_set_error(ph, "The KMIP plugin is bound to "
|
|
"CCA-type APQNs, and can only "
|
|
"import keys of type '%s'.",
|
|
KEY_TYPE_EP11_AES);
|
|
rc = -EINVAL;
|
|
goto out;
|
|
}
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (!ph->config_complete) {
|
|
_set_error(ph, "The configuration is incomplete, run 'zkey "
|
|
"kms configure [OPTIONS]' to complete the "
|
|
"configuration.");
|
|
return -EINVAL;
|
|
}
|
|
|
|
if (ph->connection == NULL) {
|
|
rc = _connect_to_server(ph);
|
|
if (rc != 0)
|
|
return rc;
|
|
}
|
|
|
|
if (label_pattern != NULL &&
|
|
strchr(label_pattern, '*') == NULL &&
|
|
strchr(label_pattern, '?') == NULL)
|
|
label_filter = true;
|
|
|
|
num_attrs = 3 + num_properties;
|
|
if (label_filter)
|
|
num_attrs += 1;
|
|
|
|
attrs = util_zalloc(num_attrs * sizeof(struct kmip_node *));
|
|
k = 0;
|
|
|
|
attrs[k] = kmip_new_state(KMIP_STATE_ACTIVE);
|
|
CHECK_ERROR(attrs[k] == NULL, rc, -ENOMEM,
|
|
"Allocate KMIP node failed", ph, out);
|
|
k++;
|
|
|
|
attrs[k] = kmip_new_object_type(KMIP_OBJECT_TYPE_SYMMETRIC_KEY);
|
|
CHECK_ERROR(attrs[k] == NULL, rc, -ENOMEM,
|
|
"Allocate KMIP node failed", ph, out);
|
|
k++;
|
|
|
|
attrs[k] = kmip_new_cryptographic_algorithm(KMIP_CRYPTO_ALGO_AES);
|
|
CHECK_ERROR(attrs[k] == NULL, rc, -ENOMEM,
|
|
"Allocate KMIP node failed", ph, out);
|
|
k++;
|
|
|
|
if (label_filter) {
|
|
attrs[k] = kmip_new_name(label_pattern,
|
|
KMIP_NAME_TYPE_UNINTERPRETED_TEXT_STRING);
|
|
CHECK_ERROR(attrs[k] == NULL, rc, -ENOMEM,
|
|
"Allocate KMIP node failed", ph, out);
|
|
k++;
|
|
}
|
|
|
|
for (i = 0; i < num_properties; i++) {
|
|
attrs[k] = _build_attr_from_prop(ph, &properties[i]);
|
|
CHECK_ERROR(attrs[k] == NULL, rc, -ENOMEM,
|
|
"Allocate KMIP node failed", ph, out);
|
|
k++;
|
|
}
|
|
|
|
req_pl = kmip_new_locate_request_payload(NULL, 0, 0, 0, 0,
|
|
num_attrs, attrs);
|
|
CHECK_ERROR(req_pl == NULL, rc, -ENOMEM, "Allocate KMIP node failed",
|
|
ph, out);
|
|
|
|
rc = _perform_kmip_request(ph, KMIP_OPERATION_LOCATE, req_pl, &resp_pl);
|
|
if (rc != 0)
|
|
goto out;
|
|
|
|
for (i = 0; ; i++) {
|
|
rc = kmip_get_locate_response_payload(resp_pl, NULL, NULL, i,
|
|
&item_uid);
|
|
if (rc != 0)
|
|
break;
|
|
|
|
rc = kmip_get_unique_identifier(item_uid, &id, NULL, NULL);
|
|
CHECK_ERROR(rc != 0, rc, rc, "Failed to get item id", ph, out);
|
|
|
|
pr_verbose(&ph->pd, "Item ID: '%s'", id);
|
|
|
|
rc = _process_list_item(ph, id, label_pattern, key_type,
|
|
callback, private_data);
|
|
if (rc != 0)
|
|
goto out;
|
|
|
|
kmip_node_free(item_uid);
|
|
item_uid = NULL;
|
|
}
|
|
|
|
rc = 0;
|
|
|
|
out:
|
|
if (key_type != NULL)
|
|
free(key_type);
|
|
|
|
if (attrs != NULL) {
|
|
for (i = 0; i < num_attrs; i++)
|
|
kmip_node_free(attrs[i]);
|
|
free(attrs);
|
|
}
|
|
|
|
kmip_node_free(req_pl);
|
|
kmip_node_free(resp_pl);
|
|
kmip_node_free(item_uid);
|
|
|
|
return rc;
|
|
}
|
|
|
|
/**
|
|
* Imports a key from the KMS and returns a secure key that is
|
|
* enciphered under the current HSM master key.
|
|
*
|
|
* @param handle the KMS plugin handle obtained from kms_initialize()
|
|
* @param key_id the key-ID of the key to import
|
|
* @param key_type the zkey key type, like 'CCA-AESDATA',
|
|
* 'CCA-AESCIPHER', 'EP11-AES'.
|
|
* @param key_blob a buffer to return the key blob. The size of the
|
|
* buffer is specified in key_blob_length
|
|
* @param key_blob_length on entry: the size of the key_blob buffer.
|
|
* on exit: the size of the key blob returned.
|
|
*
|
|
* @returns 0 on success, or a negative errno in case of an error.
|
|
* Function kms_get_last_error() can be used to obtain more details about the
|
|
* error.
|
|
*/
|
|
int kms_import_key2(const kms_handle_t handle, const char *key_id,
|
|
const char *key_type,
|
|
unsigned char *key_blob, size_t *key_blob_length)
|
|
{
|
|
struct plugin_handle *ph = handle;
|
|
int rc = 0;
|
|
|
|
util_assert(handle != NULL, "Internal error: handle is NULL");
|
|
util_assert(key_blob != NULL, "Internal error: key_blob is NULL");
|
|
util_assert(key_blob_length != NULL, "Internal error: key_blob_length "
|
|
"is NULL");
|
|
|
|
pr_verbose(&ph->pd, "Import Key, key-ID: '%s'", key_id);
|
|
|
|
plugin_clear_error(&ph->pd);
|
|
|
|
if (!ph->config_complete) {
|
|
_set_error(ph, "The configuration is incomplete, run 'zkey "
|
|
"kms configure [OPTIONS]' to complete the "
|
|
"configuration.");
|
|
return -EINVAL;
|
|
}
|
|
|
|
if (ph->connection == NULL) {
|
|
rc = _connect_to_server(ph);
|
|
if (rc != 0)
|
|
return rc;
|
|
}
|
|
|
|
switch (ph->card_type) {
|
|
case CARD_TYPE_CCA:
|
|
if (key_type == NULL ||
|
|
(key_type != NULL && strcmp(key_type,
|
|
KMIP_KEY_TYPE_ANY) == 0))
|
|
key_type = KEY_TYPE_CCA_AESDATA;
|
|
|
|
if (strcasecmp(key_type, KEY_TYPE_CCA_AESDATA) != 0 &&
|
|
strcasecmp(key_type, KEY_TYPE_CCA_AESCIPHER) != 0) {
|
|
_set_error(ph, "The KMIP plugin is bound to "
|
|
"CCA-type APQNs, and can only "
|
|
"import keys of type '%s' or '%s'.",
|
|
KEY_TYPE_CCA_AESDATA,
|
|
KEY_TYPE_CCA_AESCIPHER);
|
|
return -EINVAL;
|
|
}
|
|
break;
|
|
case CARD_TYPE_EP11:
|
|
if (key_type == NULL ||
|
|
(key_type != NULL && strcmp(key_type,
|
|
KMIP_KEY_TYPE_ANY) == 0))
|
|
key_type = KEY_TYPE_EP11_AES;
|
|
|
|
if (strcasecmp(key_type, KEY_TYPE_EP11_AES) != 0) {
|
|
_set_error(ph, "The KMIP plugin is bound to "
|
|
"EP11-type APQNs, and can only "
|
|
"import keys of type '%s'.",
|
|
KEY_TYPE_EP11_AES);
|
|
return -EINVAL;
|
|
}
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
rc = _retrieve_key(ph, key_id, key_blob, key_blob_length, key_type,
|
|
0);
|
|
|
|
return rc;
|
|
}
|
|
|
|
static const struct kms_functions kms_functions = {
|
|
.api_version = KMS_API_VERSION_2,
|
|
.kms_bind = kms_bind,
|
|
.kms_initialize = kms_initialize,
|
|
.kms_terminate = kms_terminate,
|
|
.kms_get_last_error = kms_get_last_error,
|
|
.kms_supports_key_type = kms_supports_key_type,
|
|
.kms_display_info = kms_display_info,
|
|
.kms_get_command_options = kms_get_command_options,
|
|
.kms_configure = kms_configure,
|
|
.kms_deconfigure = kms_deconfigure,
|
|
.kms_login = kms_login,
|
|
.kms_reenciper = kms_reenciper,
|
|
.kms_generate_key = kms_generate_key,
|
|
.kms_set_key_properties = kms_set_key_properties,
|
|
.kms_get_key_properties = kms_get_key_properties,
|
|
.kms_remove_key = kms_remove_key,
|
|
.kms_list_keys = kms_list_keys,
|
|
.kms_import_key2 = kms_import_key2,
|
|
};
|
|
|
|
/**
|
|
* Returns an address of a structure containing the KMS plugin functions.
|
|
* This function is exported by the KMS plugin, and its address is obtain
|
|
* via dlsym() after loading the plugin via dlopen().
|
|
* *
|
|
* @returns the address of a structure or NULL in case of an error.
|
|
*/
|
|
const struct kms_functions *kms_get_functions(void)
|
|
{
|
|
return &kms_functions;
|
|
}
|