mirror of
https://github.com/ibm-s390-linux/s390-tools.git
synced 2026-08-05 02:14:52 +00:00
9dce6793ab
Retrieve several settings from EKMF Web after the connection to EKMF Web has been configured. This includes the EKMF Web server's public key, which is user later on to verify cryptographically signed responses. Also the key templates used by EKMF Web to generate keys for zkey are retrieved, and it is checked if the require feature 'Pervasive Encryption' is available. Signed-off-by: Ingo Franzki <ifranzki@linux.ibm.com> Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
3081 lines
92 KiB
C
3081 lines
92 KiB
C
/*
|
|
* zkey-ekmfweb - EKMFWeb zkey KMS plugin
|
|
*
|
|
* Copyright IBM Corp. 2020
|
|
*
|
|
* 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 <openssl/evp.h>
|
|
#include <openssl/x509.h>
|
|
#include <openssl/pem.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 "lib/util_rec.h"
|
|
|
|
#include "zkey-ekmfweb.h"
|
|
#include "../kms-plugin.h"
|
|
#include "../cca.h"
|
|
#include "../utils.h"
|
|
#include "../pkey.h"
|
|
#include "../properties.h"
|
|
|
|
#define pr_verbose(handle, fmt...) \
|
|
do { \
|
|
if (handle->verbose) { \
|
|
fprintf(stderr, "zkey-ekmfweb: "); \
|
|
fprintf(stderr, fmt); \
|
|
fprintf(stderr, "\n"); \
|
|
} \
|
|
} while (0)
|
|
|
|
#define FREE_AND_SET_NULL(ptr) \
|
|
do { \
|
|
if ((ptr) != NULL) \
|
|
free((void *)ptr); \
|
|
(ptr) = NULL; \
|
|
} while (0)
|
|
|
|
/**
|
|
* Clears the error message in the plugin handle
|
|
*
|
|
* @param ph the plugin handle
|
|
*/
|
|
static void _clear_error(struct plugin_handle *ph)
|
|
{
|
|
memset(ph->error_msg, 0, sizeof(ph->error_msg));
|
|
}
|
|
|
|
/**
|
|
* Sets the error message in the plugin handle
|
|
*
|
|
* @param ph the plugin handle
|
|
* @param fmt the format string for sprintf
|
|
*/
|
|
static void _set_error(struct plugin_handle *ph, const char *fmt, ...)
|
|
{
|
|
va_list ap;
|
|
|
|
va_start(ap, fmt);
|
|
vsnprintf(ph->error_msg, sizeof(ph->error_msg), fmt, ap);
|
|
va_end(ap);
|
|
}
|
|
|
|
/**
|
|
* 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;
|
|
}
|
|
|
|
/**
|
|
* Load the EKMFWeb plugin config file
|
|
*
|
|
* @param ph the plugin handle
|
|
*
|
|
* @returns 0 on success, a negative errno in case of an error.
|
|
*/
|
|
static int _load_config(struct plugin_handle *ph)
|
|
{
|
|
char *file_name = NULL;
|
|
int rc;
|
|
|
|
util_asprintf(&file_name, "%s/%s", ph->config_path,
|
|
EKMFWEB_CONFIG_FILE);
|
|
|
|
rc = properties_load(ph->properties, file_name, true);
|
|
if (rc != 0)
|
|
pr_verbose(ph, "Failed to load plugin config file '%s': %s",
|
|
file_name, strerror(-rc));
|
|
else
|
|
pr_verbose(ph, "Config file '%s' loaded", file_name);
|
|
|
|
free(file_name);
|
|
return rc;
|
|
}
|
|
|
|
/**
|
|
* Sets the file permissions of the file to the permissions and the group
|
|
* of configuration directory
|
|
*
|
|
* @param ph the plugin handle
|
|
* @param filename the name of the file to set permissions for
|
|
*
|
|
* @returns 0 on success, or a negative errno value on failure
|
|
*/
|
|
static int _set_file_permission(struct plugin_handle *ph, const char *filename)
|
|
{
|
|
int rc;
|
|
|
|
if (chmod(filename, ph->config_path_mode) != 0) {
|
|
rc = -errno;
|
|
_set_error(ph, "chmod failed on file '%s': %s", filename,
|
|
strerror(-rc));
|
|
return rc;
|
|
}
|
|
|
|
if (chown(filename, geteuid(), ph->config_path_owner) != 0) {
|
|
rc = -errno;
|
|
_set_error(ph, "chown failed on file '%s': %s", filename,
|
|
strerror(-rc));
|
|
return rc;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* Makes a temporary file an active file, by first removing the current active
|
|
* file (if existent), and then renaming the temporary file to the active file.
|
|
* The active file permissions are also set to the permissions and the group of
|
|
* configuration directory.
|
|
*
|
|
* @param ph the plugin handle
|
|
* @param temp_file the name of the temporary file
|
|
* @param active_file the name of the active file
|
|
*
|
|
* @returns 0 on success, or a negative errno value on failure
|
|
*/
|
|
static int _activate_temp_file(struct plugin_handle *ph, const char *temp_file,
|
|
const char *active_file)
|
|
{
|
|
int rc;
|
|
|
|
if (util_path_exists(active_file)) {
|
|
rc = remove(active_file);
|
|
if (rc != 0) {
|
|
rc = -errno;
|
|
_set_error(ph, "remove failed on file '%s': %s",
|
|
active_file, strerror(-rc));
|
|
return rc;
|
|
}
|
|
}
|
|
|
|
rc = rename(temp_file, active_file);
|
|
if (rc != 0) {
|
|
rc = -errno;
|
|
_set_error(ph, "rename failed on file '%s': %s",
|
|
temp_file, strerror(-rc));
|
|
return rc;
|
|
}
|
|
|
|
return _set_file_permission(ph, active_file);
|
|
}
|
|
|
|
/**
|
|
* Save the EKMFWeb plugin config file
|
|
*
|
|
* @param ph the plugin handle
|
|
*
|
|
* @returns 0 on success, a negative errno in case of an error.
|
|
*/
|
|
static int _save_config(struct plugin_handle *ph)
|
|
{
|
|
char *file_name = NULL;
|
|
int rc;
|
|
|
|
util_asprintf(&file_name, "%s/%s", ph->config_path,
|
|
EKMFWEB_CONFIG_FILE);
|
|
|
|
pr_verbose(ph, "Saving '%s'", file_name);
|
|
|
|
rc = properties_save(ph->properties, file_name, true);
|
|
if (rc != 0) {
|
|
_set_error(ph, "Failed to save plugin config file '%s': %s",
|
|
file_name, strerror(-rc));
|
|
goto out;
|
|
}
|
|
|
|
rc = _set_file_permission(ph, file_name);
|
|
if (rc != 0)
|
|
goto out;
|
|
|
|
out:
|
|
free(file_name);
|
|
return rc;
|
|
}
|
|
|
|
/**
|
|
* Base64-encodes the passphrase to make it unreadable.
|
|
*
|
|
* @param passphrase the passphrase to encode
|
|
*
|
|
* @returns the encoded passphrase or NULL in case of an error.
|
|
* The caller must free the string when no longer needed.
|
|
*/
|
|
char *_encode_passphrase(const char *passphrase)
|
|
{
|
|
int inlen, outlen, len;
|
|
char *out;
|
|
|
|
inlen = strlen(passphrase);
|
|
outlen = (inlen / 3) * 4;
|
|
if (inlen % 3 > 0)
|
|
outlen += 4;
|
|
|
|
out = util_malloc(outlen + 1);
|
|
memset(out, 0, outlen + 1);
|
|
|
|
len = EVP_EncodeBlock((unsigned char *)out, (unsigned char *)passphrase,
|
|
inlen);
|
|
if (len != outlen) {
|
|
free(out);
|
|
return NULL;
|
|
}
|
|
|
|
out[outlen] = '\0';
|
|
return out;
|
|
}
|
|
|
|
/**
|
|
* Base64-decodes the passphrase
|
|
*
|
|
* @param passphrase the passphrase to decode
|
|
*
|
|
* @returns the decoded passphrase or NULL in case of an error.
|
|
* The caller must free the string when no longer needed.
|
|
*/
|
|
char *_decode_passphrase(const char *passphrase)
|
|
{
|
|
int inlen, outlen, len;
|
|
char *out;
|
|
|
|
inlen = strlen(passphrase);
|
|
outlen = (inlen / 4) * 3;
|
|
if (inlen % 4 > 0)
|
|
outlen += 3;
|
|
|
|
out = util_malloc(outlen + 1);
|
|
memset(out, 0, outlen + 1);
|
|
|
|
len = EVP_DecodeBlock((unsigned char *)out, (unsigned char *)passphrase,
|
|
inlen);
|
|
if (len != outlen) {
|
|
free(out);
|
|
return NULL;
|
|
}
|
|
|
|
out[outlen] = '\0';
|
|
return out;
|
|
}
|
|
|
|
/**
|
|
* Sets or removes a property. If value is NULL it is removed, otherwise it
|
|
* is set.
|
|
*
|
|
* @param ph the plugin handle
|
|
* @param name the name of the property
|
|
* @param value the value of the property or NULL
|
|
*
|
|
* @returns 0 on success, a negative errno in case of an error.
|
|
*/
|
|
static int _set_or_remove_property(struct plugin_handle *ph, const char *name,
|
|
const char *value)
|
|
{
|
|
int rc = 0;
|
|
|
|
if (value != NULL) {
|
|
rc = properties_set(ph->properties, name, value);
|
|
if (rc != 0) {
|
|
_set_error(ph, "Failed to set property '%s': %s", name,
|
|
strerror(-rc));
|
|
goto out;
|
|
}
|
|
} else {
|
|
rc = properties_remove(ph->properties, name);
|
|
if (rc != 0 && rc != -ENOENT) {
|
|
_set_error(ph, "Failed to remove property '%s': %s",
|
|
name, strerror(-rc));
|
|
goto out;
|
|
}
|
|
rc = 0;
|
|
}
|
|
|
|
out:
|
|
return rc;
|
|
}
|
|
|
|
/**
|
|
* Checks if a plugin config propertiy is set and not empty
|
|
*
|
|
* @param ph the plugin handle
|
|
* @param name the name of the property
|
|
*
|
|
* @returns true if the property is set and is not empty, false otherwise
|
|
*/
|
|
static bool _check_property(struct plugin_handle *ph, const char *name)
|
|
{
|
|
bool ok = true;
|
|
char *value;
|
|
|
|
value = properties_get(ph->properties, name);
|
|
pr_verbose(ph, "Property '%s': %s", name,
|
|
value != NULL ? value : "(missing)");
|
|
|
|
ok &= (value != NULL && strlen(value) > 0);
|
|
|
|
if (value != NULL)
|
|
free(value);
|
|
|
|
return ok;
|
|
}
|
|
|
|
/**
|
|
* 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 = _check_property(ph, EKMFWEB_CONFIG_APQNS);
|
|
|
|
ph->connection_configured =
|
|
_check_property(ph, EKMFWEB_CONFIG_URL) &&
|
|
_check_property(ph, EKMFWEB_CONFIG_VERIFY_SERVER_CERT) &&
|
|
_check_property(ph, EKMFWEB_CONFIG_VERIFY_HOSTNAME);
|
|
|
|
ph->settings_retrieved =
|
|
_check_property(ph, EKMFWEB_CONFIG_EKMFWEB_PUBKEY);
|
|
|
|
ph->templates_retrieved =
|
|
_check_property(ph, EKMFWEB_CONFIG_TEMPLATE_XTS1) &&
|
|
_check_property(ph, EKMFWEB_CONFIG_TEMPLATE_XTS2) &&
|
|
_check_property(ph, EKMFWEB_CONFIG_TEMPLATE_NONXTS) &&
|
|
_check_property(ph, EKMFWEB_CONFIG_TEMPLATE_IDENTITY) &&
|
|
_check_property(ph, EKMFWEB_CONFIG_TEMPLATE_XTS1_LABEL) &&
|
|
_check_property(ph, EKMFWEB_CONFIG_TEMPLATE_XTS2_LABEL) &&
|
|
_check_property(ph, EKMFWEB_CONFIG_TEMPLATE_NONXTS_LABEL) &&
|
|
_check_property(ph, EKMFWEB_CONFIG_TEMPLATE_IDENTITY_LABEL) &&
|
|
_check_property(ph, EKMFWEB_CONFIG_TEMPLATE_XTS1_ID) &&
|
|
_check_property(ph, EKMFWEB_CONFIG_TEMPLATE_XTS2_ID) &&
|
|
_check_property(ph, EKMFWEB_CONFIG_TEMPLATE_NONXTS_ID) &&
|
|
_check_property(ph, EKMFWEB_CONFIG_TEMPLATE_IDENTITY_ID);
|
|
|
|
ph->config_complete = ph->apqns_configured &&
|
|
ph->connection_configured &&
|
|
ph->settings_retrieved &&
|
|
ph->templates_retrieved;
|
|
}
|
|
|
|
/**
|
|
* Gets the EKMF 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_ekmf_config(struct plugin_handle *ph)
|
|
{
|
|
char *tmp;
|
|
|
|
ph->ekmf_config.base_url = properties_get(ph->properties,
|
|
EKMFWEB_CONFIG_URL);
|
|
ph->ekmf_config.tls_ca = properties_get(ph->properties,
|
|
EKMFWEB_CONFIG_CA_BUNDLE);
|
|
ph->ekmf_config.tls_client_cert = properties_get(ph->properties,
|
|
EKMFWEB_CONFIG_CLIENT_CERT);
|
|
ph->ekmf_config.tls_client_key = properties_get(ph->properties,
|
|
EKMFWEB_CONFIG_CLIENT_KEY);
|
|
|
|
tmp = properties_get(ph->properties,
|
|
EKMFWEB_CONFIG_CLIENT_KEY_PASSPHRASE);
|
|
if (tmp != NULL) {
|
|
ph->ekmf_config.tls_client_key_passphrase =
|
|
_decode_passphrase(tmp);
|
|
free(tmp);
|
|
}
|
|
ph->ekmf_config.tls_issuer_cert = NULL;
|
|
ph->ekmf_config.tls_pinned_pubkey = properties_get(ph->properties,
|
|
EKMFWEB_CONFIG_SERVER_PUBKEY);
|
|
ph->ekmf_config.tls_server_cert = properties_get(ph->properties,
|
|
EKMFWEB_CONFIG_SERVER_CERT);
|
|
tmp = properties_get(ph->properties, EKMFWEB_CONFIG_VERIFY_SERVER_CERT);
|
|
ph->ekmf_config.tls_verify_peer =
|
|
(tmp != NULL && strcasecmp(tmp, "yes") == 0);
|
|
if (tmp != NULL)
|
|
free(tmp);
|
|
tmp = properties_get(ph->properties, EKMFWEB_CONFIG_VERIFY_HOSTNAME);
|
|
ph->ekmf_config.tls_verify_host =
|
|
(tmp != NULL && strcasecmp(tmp, "yes") == 0);
|
|
if (tmp != NULL)
|
|
free(tmp);
|
|
ph->ekmf_config.max_redirs = 0;
|
|
|
|
ph->ekmf_config.login_token = properties_get(ph->properties,
|
|
EKMFWEB_CONFIG_LOGIN_TOKEN);
|
|
|
|
ph->ekmf_config.ekmf_server_pubkey = properties_get(ph->properties,
|
|
EKMFWEB_CONFIG_EKMFWEB_PUBKEY);
|
|
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* Frees the EKMF config structure contents
|
|
*
|
|
* @param ph the plugin handle
|
|
*/
|
|
static void _free_ekmf_config(struct plugin_handle *ph)
|
|
{
|
|
if (ph->ekmf_config.base_url != NULL)
|
|
free((void *)ph->ekmf_config.base_url);
|
|
if (ph->ekmf_config.tls_ca != NULL)
|
|
free((void *)ph->ekmf_config.tls_ca);
|
|
if (ph->ekmf_config.tls_client_cert != NULL)
|
|
free((void *)ph->ekmf_config.tls_client_cert);
|
|
if (ph->ekmf_config.tls_client_key != NULL)
|
|
free((void *)ph->ekmf_config.tls_client_key);
|
|
if (ph->ekmf_config.tls_client_key_passphrase != NULL)
|
|
free((void *)ph->ekmf_config.tls_client_key_passphrase);
|
|
if (ph->ekmf_config.tls_issuer_cert != NULL)
|
|
free((void *)ph->ekmf_config.tls_issuer_cert);
|
|
if (ph->ekmf_config.tls_pinned_pubkey != NULL)
|
|
free((void *)ph->ekmf_config.tls_pinned_pubkey);
|
|
if (ph->ekmf_config.tls_server_cert != NULL)
|
|
free((void *)ph->ekmf_config.tls_server_cert);
|
|
if (ph->ekmf_config.login_token != NULL)
|
|
free((void *)ph->ekmf_config.login_token);
|
|
if (ph->ekmf_config.identity_secure_key != NULL)
|
|
free((void *)ph->ekmf_config.identity_secure_key);
|
|
if (ph->ekmf_config.ekmf_server_pubkey != NULL)
|
|
free((void *)ph->ekmf_config.ekmf_server_pubkey);
|
|
}
|
|
|
|
/**
|
|
* Removes the login token file, if the error indicates an authorization or
|
|
* authentication error (-EACCES or -EPERM)
|
|
*
|
|
* @param ph the plugin handle
|
|
* @param error the negative errno value of the last error
|
|
*/
|
|
static void _remove_login_token_if_error(struct plugin_handle *ph, int error)
|
|
{
|
|
switch (error) {
|
|
case -EACCES:
|
|
case -EPERM:
|
|
remove(ph->ekmf_config.login_token);
|
|
FREE_AND_SET_NULL(ph->ekmf_config.login_token);
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
/**
|
|
* 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;
|
|
struct stat sb;
|
|
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));
|
|
|
|
ph->config_path = util_strdup(config_path);
|
|
ph->verbose = verbose;
|
|
|
|
pr_verbose(ph, "Plugin initializing, config_path: '%s'", config_path);
|
|
|
|
if (stat(config_path, &sb) != 0) {
|
|
warnx("Can not access '%s': %s", config_path, strerror(errno));
|
|
goto error;
|
|
}
|
|
if (!S_ISDIR(sb.st_mode)) {
|
|
warnx("'%s' is not a directory", config_path);
|
|
goto error;
|
|
}
|
|
if (!util_path_is_readable(config_path) ||
|
|
!util_path_is_writable(config_path)) {
|
|
warnx("Permission denied for '%s'", config_path);
|
|
goto error;
|
|
}
|
|
if (sb.st_mode & S_IWOTH) {
|
|
warnx("Directory '%s' is writable for others, this is not "
|
|
"accepted", config_path);
|
|
goto error;
|
|
}
|
|
|
|
ph->config_path_owner = sb.st_gid;
|
|
ph->config_path_mode = sb.st_mode & (S_IRUSR | S_IWUSR |
|
|
S_IRGRP | S_IWGRP |
|
|
S_IROTH);
|
|
|
|
ph->properties = properties_new();
|
|
rc = _load_config(ph);
|
|
if (rc != 0 && rc != -EIO) {
|
|
warnx("Failed to load plugin config file: %s", strerror(-rc));
|
|
goto error;
|
|
}
|
|
|
|
rc = _get_ekmf_config(ph);
|
|
if (rc != 0)
|
|
goto error;
|
|
|
|
_check_config_complete(ph);
|
|
pr_verbose(ph, "Plugin configuration is %scomplete",
|
|
ph->config_complete ? "" : "in");
|
|
|
|
return (kms_handle_t)ph;
|
|
|
|
error:
|
|
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, "Plugin terminated");
|
|
|
|
_free_ekmf_config(ph);
|
|
|
|
if (ph->curl_handle != NULL)
|
|
ekmf_curl_destroy(ph->curl_handle);
|
|
|
|
if (ph->config_path != NULL)
|
|
free((void *)ph->config_path);
|
|
if (ph->properties != NULL)
|
|
properties_free(ph->properties);
|
|
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, "Last error: '%s'", ph->error_msg);
|
|
|
|
if (strlen(ph->error_msg) == 0)
|
|
return NULL;
|
|
|
|
return ph->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");
|
|
|
|
_clear_error(ph);
|
|
|
|
if (strcasecmp(key_type, KEY_TYPE_CCA_AESCIPHER) == 0)
|
|
return true;
|
|
|
|
return false;
|
|
}
|
|
|
|
/**
|
|
* Returns information about the public key in the PEM file
|
|
*
|
|
* @param ph the plugin handle
|
|
* @param pem_file the name of a PEM file containing the public key
|
|
* @param pkey_type on return: If not NULL, the PKEY type (EVP_PKEY_EC
|
|
* or EVP_PKEY_RSA)
|
|
* @param ecc_curve_nid on return: If not NULL and it is an ECC key, the
|
|
* OpenSSL NID of the curve of the ECC key.
|
|
* @param rsa_mod_bits on return: If not NULL and it is an RSA key, the
|
|
* modulus bit size of the RSA key.
|
|
*
|
|
* @returns 0 on success, or a negative errno in case of an error.
|
|
*/
|
|
static int _get_pub_key_info(struct plugin_handle *ph, const char *pem_file,
|
|
int *pkey_type, int *ecc_curve_nid,
|
|
int *rsa_mod_bits)
|
|
{
|
|
int rc = 0, curve_nid, mod_len;
|
|
EVP_PKEY *pkey;
|
|
FILE *fp;
|
|
|
|
fp = fopen(pem_file, "r");
|
|
if (fp == NULL) {
|
|
rc = -errno;
|
|
_set_error(ph, "Failed to open pubkey PEM file '%s': %s",
|
|
pem_file, strerror(-rc));
|
|
return rc;
|
|
}
|
|
|
|
pkey = PEM_read_PUBKEY(fp, NULL, NULL, NULL);
|
|
|
|
fclose(fp);
|
|
|
|
if (pkey == NULL) {
|
|
rc = -EIO;
|
|
_set_error(ph, "Failed to read pubkey from PEM file '%s': %s",
|
|
pem_file, strerror(-rc));
|
|
return rc;
|
|
}
|
|
|
|
if (pkey_type != NULL)
|
|
*pkey_type = EVP_PKEY_id(pkey);
|
|
|
|
switch (EVP_PKEY_id(pkey)) {
|
|
case EVP_PKEY_EC:
|
|
curve_nid = EC_GROUP_get_curve_name(EC_KEY_get0_group(
|
|
EVP_PKEY_get0_EC_KEY(pkey)));
|
|
if (ecc_curve_nid != NULL)
|
|
*ecc_curve_nid = curve_nid;
|
|
break;
|
|
|
|
case EVP_PKEY_RSA:
|
|
mod_len = BN_num_bits(RSA_get0_n(EVP_PKEY_get0_RSA(pkey)));
|
|
if (rsa_mod_bits != NULL)
|
|
*rsa_mod_bits = mod_len;
|
|
break;
|
|
|
|
default:
|
|
rc = -EIO;
|
|
_set_error(ph, "Unknown pubkey type: %d", EVP_PKEY_id(pkey));
|
|
break;
|
|
}
|
|
|
|
EVP_PKEY_free(pkey);
|
|
return rc;
|
|
}
|
|
|
|
/**
|
|
* 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)
|
|
{
|
|
int rc, type = 0, curve = 0, mod_bits = 0;
|
|
struct plugin_handle *ph = handle;
|
|
char *tmp = NULL;
|
|
|
|
util_assert(handle != NULL, "Internal error: handle is NULL");
|
|
|
|
pr_verbose(ph, "Display Info");
|
|
|
|
_clear_error(ph);
|
|
|
|
tmp = properties_get(ph->properties, EKMFWEB_CONFIG_URL);
|
|
printf(" EKMF Web server: %s\n", tmp != NULL ? tmp :
|
|
"(configuration required)");
|
|
if (tmp != NULL)
|
|
free(tmp);
|
|
else
|
|
return 0;
|
|
tmp = properties_get(ph->properties, EKMFWEB_CONFIG_CA_BUNDLE);
|
|
printf(" CA-bundle: %s\n", tmp != NULL ? tmp :
|
|
"System's CA certificates");
|
|
if (tmp != NULL)
|
|
free(tmp);
|
|
tmp = properties_get(ph->properties, EKMFWEB_CONFIG_CLIENT_CERT);
|
|
printf(" Client certificate: %s\n", tmp != NULL ? tmp : "(none)");
|
|
if (tmp != NULL)
|
|
free(tmp);
|
|
tmp = properties_get(ph->properties, EKMFWEB_CONFIG_CLIENT_KEY);
|
|
printf(" Client private key: %s\n", tmp != NULL ? tmp : "(none)");
|
|
if (tmp != NULL) {
|
|
free(tmp);
|
|
tmp = properties_get(ph->properties,
|
|
EKMFWEB_CONFIG_CLIENT_KEY_PASSPHRASE);
|
|
if (tmp != NULL) {
|
|
printf(" "
|
|
"(passphrase protected)\n");
|
|
free(tmp);
|
|
}
|
|
}
|
|
tmp = properties_get(ph->properties, EKMFWEB_CONFIG_SERVER_CERT);
|
|
if (tmp != NULL) {
|
|
printf(" Trusting the server certificate\n");
|
|
free(tmp);
|
|
}
|
|
tmp = properties_get(ph->properties, EKMFWEB_CONFIG_SERVER_PUBKEY);
|
|
if (tmp != NULL) {
|
|
printf(" Using server public key pinning\n");
|
|
free(tmp);
|
|
}
|
|
tmp = properties_get(ph->properties, EKMFWEB_CONFIG_VERIFY_SERVER_CERT);
|
|
if (tmp != NULL) {
|
|
if (strcasecmp(tmp, "yes") == 0)
|
|
printf(" The server's certificate must be valid\n");
|
|
free(tmp);
|
|
} else {
|
|
printf(" The server's certificate is not verified\n");
|
|
}
|
|
tmp = properties_get(ph->properties, EKMFWEB_CONFIG_VERIFY_HOSTNAME);
|
|
if (tmp != NULL) {
|
|
if (strcasecmp(tmp, "yes") == 0)
|
|
printf(" The server's certificate must match the "
|
|
"hostname\n");
|
|
free(tmp);
|
|
}
|
|
|
|
tmp = properties_get(ph->properties, EKMFWEB_CONFIG_EKMFWEB_PUBKEY);
|
|
if (tmp != NULL) {
|
|
rc = _get_pub_key_info(ph, tmp, &type, &curve, &mod_bits);
|
|
if (rc == 0) {
|
|
switch (type) {
|
|
case EVP_PKEY_EC:
|
|
printf(" EBMF Web public key: ECC (%s)\n",
|
|
OBJ_nid2sn(curve));
|
|
break;
|
|
case EVP_PKEY_RSA:
|
|
printf(" EBMF Web public key: RSA "
|
|
"(%d bits)\n", mod_bits);
|
|
break;
|
|
default:
|
|
printf(" EBMF Web public key: "
|
|
"(unknown key type)\n");
|
|
break;
|
|
}
|
|
} else {
|
|
printf(" EBMF Web public key: (not available)\n");
|
|
}
|
|
free(tmp);
|
|
} else {
|
|
printf(" EBMF Web public key: (configuration required)\n");
|
|
}
|
|
|
|
printf(" Key templates:\n");
|
|
tmp = properties_get(ph->properties, EKMFWEB_CONFIG_TEMPLATE_IDENTITY);
|
|
printf(" Identity: %s\n", tmp != NULL ? tmp :
|
|
"(configuration required)");
|
|
if (tmp != NULL)
|
|
free(tmp);
|
|
tmp = properties_get(ph->properties,
|
|
EKMFWEB_CONFIG_TEMPLATE_IDENTITY_LABEL);
|
|
if (tmp != NULL) {
|
|
printf(" Label template: %s\n", tmp);
|
|
free(tmp);
|
|
}
|
|
|
|
tmp = properties_get(ph->properties, EKMFWEB_CONFIG_TEMPLATE_XTS1);
|
|
printf(" XTS-Key1: %s\n", tmp != NULL ? tmp :
|
|
"(configuration required)");
|
|
if (tmp != NULL)
|
|
free(tmp);
|
|
tmp = properties_get(ph->properties,
|
|
EKMFWEB_CONFIG_TEMPLATE_XTS1_LABEL);
|
|
if (tmp != NULL) {
|
|
printf(" Label template: %s\n", tmp);
|
|
free(tmp);
|
|
}
|
|
|
|
tmp = properties_get(ph->properties, EKMFWEB_CONFIG_TEMPLATE_XTS2);
|
|
printf(" XTS-Key2: %s\n", tmp != NULL ? tmp :
|
|
"(configuration required)");
|
|
if (tmp != NULL)
|
|
free(tmp);
|
|
tmp = properties_get(ph->properties,
|
|
EKMFWEB_CONFIG_TEMPLATE_XTS2_LABEL);
|
|
if (tmp != NULL) {
|
|
printf(" Label template: %s\n", tmp);
|
|
free(tmp);
|
|
}
|
|
|
|
tmp = properties_get(ph->properties, EKMFWEB_CONFIG_TEMPLATE_NONXTS);
|
|
printf(" Non-XTS: %s\n", tmp != NULL ? tmp :
|
|
"(configuration required)");
|
|
if (tmp != NULL)
|
|
free(tmp);
|
|
tmp = properties_get(ph->properties,
|
|
EKMFWEB_CONFIG_TEMPLATE_NONXTS_LABEL);
|
|
if (tmp != NULL) {
|
|
printf(" Label template: %s\n", tmp);
|
|
free(tmp);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
#define OPT_TLS_CLIENT_CERT 256
|
|
#define OPT_TLS_CLIENT_KEY 257
|
|
#define OPT_TLS_CLIENT_KEY_PASSPHRASE 258
|
|
#define OPT_TLS_PIN_SERVER_PUBKEY 259
|
|
#define OPT_TLS_TRUST_SERVER_CERT 260
|
|
#define OPT_TLS_DONT_VERIFY_SERVER_CERT 261
|
|
#define OPT_TLS_VERIFY_HOSTNAME 262
|
|
|
|
const struct util_opt configure_options[] = {
|
|
{
|
|
.flags = UTIL_OPT_FLAG_SECTION,
|
|
.desc = "EKMFWEB SPECIFIC OPTIONS FOR THE SERVER CONNECTION",
|
|
.command = KMS_COMMAND_CONFIGURE,
|
|
},
|
|
{
|
|
.option = { "ekmfweb-url", required_argument, NULL, 'u'},
|
|
.argument = "URL",
|
|
.desc = "The URL of the EKMF Web server. The URL should start "
|
|
"with 'https://', and may contain a port number "
|
|
"separated by a colon. If no port number is specified, "
|
|
"443 is used for HTTPS.",
|
|
.command = KMS_COMMAND_CONFIGURE,
|
|
},
|
|
{
|
|
.option = { "tls-ca-bundle", required_argument, NULL, 'b'},
|
|
.argument = "CA-BUNDLE",
|
|
.desc = "The CA bundle PEM file or directory containing the CA "
|
|
"certificates used to verify the EKMF Web server "
|
|
"certificate during TLS handshake. If this specifies a "
|
|
"directory path, then this directory must have been "
|
|
"prepared with OpenSSL's c_rehash utility. Default are "
|
|
"the system CA certificates.",
|
|
.command = KMS_COMMAND_CONFIGURE,
|
|
},
|
|
{
|
|
.option = { "tls-client-cert", required_argument, NULL,
|
|
OPT_TLS_CLIENT_CERT },
|
|
.flags = UTIL_OPT_FLAG_NOSHORT,
|
|
.argument = "PEM-FILE",
|
|
.desc = "The PEM file containing the client's TLS certificate "
|
|
"for use with TLS client authentication.",
|
|
.command = KMS_COMMAND_CONFIGURE,
|
|
},
|
|
{
|
|
.option = { "tls-client-key", required_argument, NULL,
|
|
OPT_TLS_CLIENT_KEY },
|
|
.flags = UTIL_OPT_FLAG_NOSHORT,
|
|
.argument = "PEM-FILE",
|
|
.desc = "The PEM file containing the client's private key "
|
|
"for use with TLS client authentication.",
|
|
.command = KMS_COMMAND_CONFIGURE,
|
|
},
|
|
{
|
|
.option = { "tls-client-key-passphrase", required_argument,
|
|
NULL, OPT_TLS_CLIENT_KEY_PASSPHRASE },
|
|
.flags = UTIL_OPT_FLAG_NOSHORT,
|
|
.argument = "PASSPHRASE",
|
|
.desc = "If the PEM file is passphrase protected, this option "
|
|
"specifies the passphrase to unlock the PEM file that "
|
|
"is specified with option '--tls-client-key'.",
|
|
.command = KMS_COMMAND_CONFIGURE,
|
|
},
|
|
{
|
|
.option = { "tls-pin-server-pubkey", 0, NULL,
|
|
OPT_TLS_PIN_SERVER_PUBKEY },
|
|
.flags = UTIL_OPT_FLAG_NOSHORT,
|
|
.desc = "Pin the EKMF Web server's public key to verify on "
|
|
"every connection that the public key of the EKMF Web "
|
|
"server's certificate is the same that was used when "
|
|
"the connection to the EKMF Web server was configured. "
|
|
"This option can only be used with CA signed EKMF Web "
|
|
"server certificates.",
|
|
.command = KMS_COMMAND_CONFIGURE,
|
|
},
|
|
{
|
|
.option = { "tls-trust-server-cert", 0, NULL,
|
|
OPT_TLS_TRUST_SERVER_CERT },
|
|
.flags = UTIL_OPT_FLAG_NOSHORT,
|
|
.desc = "Trust the EKMF Web server's certificate even if it is "
|
|
"a self signed certificate, or could not be verified "
|
|
"due to other reasons. This option can be used instead "
|
|
"of option '--tls-pin-server-pubkey' with self signed "
|
|
"EKMF Web 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 EKMF Web "
|
|
"server's certificate. For self signed EKMF Web server "
|
|
"certificates, this is the default. Use option "
|
|
"'--tls-pin-server-cert' to ensure the self signed "
|
|
"certificate's authenticity explicitely. CA signed "
|
|
"EKMF Web server certificates are verified by default. "
|
|
"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 = "Verify that the EKMF Web server certificate's 'Common "
|
|
"Name' field or a 'Subject Alternate Name' field "
|
|
"matches the host name used to connect to the EKMF "
|
|
"Web server.",
|
|
.command = KMS_COMMAND_CONFIGURE,
|
|
},
|
|
{
|
|
.option = { "refresh-settings", 0, NULL, 'R' },
|
|
.desc = "Refresh the EKMF Web server settings. This is "
|
|
"automatically performed when the connection to the "
|
|
"EKMF Web server is (re-)configured. Use this option "
|
|
"when the settings of the already configured EKMF Web "
|
|
"server have changed",
|
|
.command = KMS_COMMAND_CONFIGURE,
|
|
},
|
|
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;
|
|
|
|
return NULL;
|
|
}
|
|
|
|
/**
|
|
* Queries the APKA master key states and verification patterns of the current
|
|
* CCA adapter
|
|
*
|
|
* @param ph the plugin handle
|
|
* @param cca the CCA library structure
|
|
* @param apka_mk_info the master key info of the APKA master key
|
|
*
|
|
* @returns 0 on success, a negative errno in case of an error.
|
|
*/
|
|
static int _get_cca_apka_mk_info(struct plugin_handle *ph, struct cca_lib *cca,
|
|
struct mk_info *apka_mk_info)
|
|
{
|
|
long exit_data_len = 0, rule_array_count, verb_data_length = 0;
|
|
unsigned char rule_array[16 * 8] = { 0, };
|
|
unsigned char exit_data[4] = { 0, };
|
|
long return_code, reason_code;
|
|
struct cca_staticsb {
|
|
u16 sym_old_mk_mdc4_len;
|
|
u16 sym_old_mk_mdc4_id;
|
|
u8 sym_old_mk_mdc4_hp[16];
|
|
u16 sym_cur_mk_mdc4_len;
|
|
u16 sym_cur_mk_mdc4_id;
|
|
u8 sym_cur_mk_mdc4_hp[16];
|
|
u16 sym_new_mk_mdc4_len;
|
|
u16 sym_new_mk_mdc4_id;
|
|
u8 sym_new_mk_mdc4_hp[16];
|
|
u16 asym_old_mk_mdc4_len;
|
|
u16 asym_old_mk_mdc4_id;
|
|
u8 asym_old_mk_mdc4_hp[16];
|
|
u16 asym_cur_mk_mdc4_len;
|
|
u16 asym_cur_mk_mdc4_id;
|
|
u8 asym_cur_mk_mdc4_hp[16];
|
|
u16 asym_new_mk_mdc4_len;
|
|
u16 asym_new_mk_mdc4_id;
|
|
u8 asym_new_mk_mdc4_hp[16];
|
|
u16 sym_old_mk_vp_len;
|
|
u16 sym_old_mk_vp_id;
|
|
u8 sym_old_mk_vp[8];
|
|
u16 sym_cur_mk_vp_len;
|
|
u16 sym_cur_mk_vp_id;
|
|
u8 sym_cur_mk_vp[8];
|
|
u16 sym_new_mk_vp_len;
|
|
u16 sym_new_mk_vp_id;
|
|
u8 sym_new_mk_vp[8];
|
|
u16 sym_new_mk_mkap_len;
|
|
u16 sym_new_mk_mkap_id;
|
|
u8 sym_new_mk_mkap[8];
|
|
u16 aes_old_mk_vp_len;
|
|
u16 aes_old_mk_vp_id;
|
|
u8 aes_old_mk_vp[8];
|
|
u16 aes_cur_mk_vp_len;
|
|
u16 aes_cur_mk_vp_id;
|
|
u8 aes_cur_mk_vp[8];
|
|
u16 aes_new_mk_vp_len;
|
|
u16 aes_new_mk_vp_id;
|
|
u8 aes_new_mk_vp[8];
|
|
u16 apka_old_mk_vp_len;
|
|
u16 apka_old_mk_vp_id;
|
|
u8 apka_old_mk_vp[8];
|
|
u16 apka_cur_mk_vp_len;
|
|
u16 apka_cur_mk_vp_id;
|
|
u8 apka_cur_mk_vp[8];
|
|
u16 apka_new_mk_vp_len;
|
|
u16 apka_new_mk_vp_id;
|
|
u8 apka_new_mk_vp[8];
|
|
} statis_csb = { 0 };
|
|
|
|
util_assert(cca != NULL, "Internal error: cca is NULL");
|
|
util_assert(apka_mk_info != NULL,
|
|
"Internal error: apka_mk_info is NULL");
|
|
|
|
memset(apka_mk_info, 0, sizeof(struct mk_info));
|
|
|
|
memset(rule_array, 0, sizeof(rule_array));
|
|
memcpy(rule_array, "STATICSB", 8);
|
|
rule_array_count = 1;
|
|
|
|
verb_data_length = sizeof(statis_csb);
|
|
|
|
cca->dll_CSUACFQ(&return_code, &reason_code,
|
|
&exit_data_len, exit_data,
|
|
&rule_array_count, rule_array,
|
|
&verb_data_length, (unsigned char *)&statis_csb);
|
|
|
|
pr_verbose(ph, "CSUACFQ (Cryptographic Facility Query) returned: "
|
|
"return_code: %ld, reason_code: %ld", return_code,
|
|
reason_code);
|
|
if (return_code != 0)
|
|
return -EIO;
|
|
|
|
switch (rule_array[10 * 8]) {
|
|
case '3':
|
|
apka_mk_info->new_mk.mk_state = MK_STATE_FULL;
|
|
break;
|
|
case '2':
|
|
apka_mk_info->new_mk.mk_state = MK_STATE_PARTIAL;
|
|
break;
|
|
case '1':
|
|
default:
|
|
apka_mk_info->new_mk.mk_state = MK_STATE_EMPTY;
|
|
break;
|
|
}
|
|
memcpy(apka_mk_info->new_mk.mkvp, statis_csb.apka_new_mk_vp,
|
|
sizeof(statis_csb.apka_new_mk_vp));
|
|
|
|
switch (rule_array[11 * 8]) {
|
|
case '2':
|
|
apka_mk_info->cur_mk.mk_state = MK_STATE_VALID;
|
|
break;
|
|
case '1':
|
|
default:
|
|
apka_mk_info->cur_mk.mk_state = MK_STATE_INVALID;
|
|
break;
|
|
}
|
|
memcpy(apka_mk_info->cur_mk.mkvp, statis_csb.apka_cur_mk_vp,
|
|
sizeof(statis_csb.apka_cur_mk_vp));
|
|
|
|
switch (rule_array[12 * 8]) {
|
|
case '2':
|
|
apka_mk_info->old_mk.mk_state = MK_STATE_VALID;
|
|
break;
|
|
case '1':
|
|
default:
|
|
apka_mk_info->old_mk.mk_state = MK_STATE_INVALID;
|
|
break;
|
|
}
|
|
memcpy(apka_mk_info->old_mk.mkvp, statis_csb.apka_old_mk_vp,
|
|
sizeof(statis_csb.apka_old_mk_vp));
|
|
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* Print the APKA master key infos of the selected APQNs
|
|
*
|
|
* @param ph the plugin handle
|
|
* @param cca CCA library structure
|
|
* @param apqns a list of APQNs
|
|
* @param num_apqns number of APQNs in above array
|
|
*
|
|
* @returns 0 on success, a negative errno in case of an error.
|
|
*/
|
|
|
|
static int _print_apka_mks(struct plugin_handle *ph, struct cca_lib *cca,
|
|
const struct kms_apqn *apqns, size_t num_apqns)
|
|
{
|
|
struct mk_info mk_info;
|
|
struct util_rec *rec;
|
|
enum card_type type;
|
|
int rc = 0, level;
|
|
size_t i;
|
|
|
|
rec = util_rec_new_wide("-");
|
|
util_rec_def(rec, "APQN", UTIL_REC_ALIGN_LEFT, 11, "CARD.DOMAIN");
|
|
util_rec_def(rec, "NEW", UTIL_REC_ALIGN_LEFT, 16, "NEW APKA MK");
|
|
util_rec_def(rec, "CUR", UTIL_REC_ALIGN_LEFT, 16, "CURRENT APKA MK");
|
|
util_rec_def(rec, "OLD", UTIL_REC_ALIGN_LEFT, 16, "OLD APKA MK");
|
|
util_rec_def(rec, "TYPE", UTIL_REC_ALIGN_LEFT, 6, "TYPE");
|
|
util_rec_print_hdr(rec);
|
|
|
|
for (i = 0; i < num_apqns; i++) {
|
|
rc = select_cca_adapter(cca, apqns[i].card, apqns[i].domain,
|
|
ph->verbose);
|
|
if (rc != 0) {
|
|
_set_error(ph, "Failed to select APQN %02x.%04x: %s",
|
|
apqns[i].card, apqns[i].domain,
|
|
strerror(-rc));
|
|
goto out;
|
|
}
|
|
|
|
rc = _get_cca_apka_mk_info(ph, cca, &mk_info);
|
|
if (rc != 0) {
|
|
_set_error(ph, "Failed to get the APKA master key "
|
|
"infos for APQN %02x.%04x: %s",
|
|
apqns[i].card, apqns[i].domain,
|
|
strerror(-rc));
|
|
goto out;
|
|
}
|
|
|
|
level = sysfs_get_card_level(apqns[i].card);
|
|
type = sysfs_get_card_type(apqns[i].card);
|
|
|
|
util_rec_set(rec, "APQN", "%02x.%04x", apqns[i].card,
|
|
apqns[i].domain);
|
|
|
|
if (mk_info.new_mk.mk_state == MK_STATE_FULL ||
|
|
mk_info.new_mk.mk_state == MK_STATE_COMMITTED)
|
|
util_rec_set(rec, "NEW", "%s",
|
|
printable_mkvp(type, mk_info.new_mk.mkvp));
|
|
else if (mk_info.new_mk.mk_state == MK_STATE_PARTIAL)
|
|
util_rec_set(rec, "NEW", "partially loaded");
|
|
else if (mk_info.new_mk.mk_state == MK_STATE_UNCOMMITTED)
|
|
util_rec_set(rec, "NEW", "uncommitted");
|
|
else
|
|
util_rec_set(rec, "NEW", "-");
|
|
|
|
if (mk_info.cur_mk.mk_state == MK_STATE_VALID)
|
|
util_rec_set(rec, "CUR", "%s",
|
|
printable_mkvp(type, mk_info.cur_mk.mkvp));
|
|
else
|
|
util_rec_set(rec, "CUR", "-");
|
|
|
|
if (mk_info.old_mk.mk_state == MK_STATE_VALID)
|
|
util_rec_set(rec, "OLD", "%s",
|
|
printable_mkvp(type, mk_info.old_mk.mkvp));
|
|
else
|
|
util_rec_set(rec, "OLD", "-");
|
|
|
|
if (level > 0 && type != CARD_TYPE_ANY)
|
|
util_rec_set(rec, "TYPE", "CEX%d%c", level,
|
|
type == CARD_TYPE_CCA ? 'C' : 'P');
|
|
else
|
|
util_rec_set(rec, "TYPE", "?");
|
|
|
|
util_rec_print(rec);
|
|
}
|
|
|
|
out:
|
|
util_rec_free(rec);
|
|
|
|
return rc;
|
|
}
|
|
|
|
/**
|
|
* Cross checks the APQNs associated with the plugin. Checks if the CCA master
|
|
* keys of all APQNs for the APKA master key are the same.
|
|
*
|
|
* @param ph the plugin handle
|
|
* @param apqns a list of APQNs
|
|
* @param num_apqns number of APQNs in above array
|
|
*
|
|
* @returns 0 on success, a negative errno in case of an error.
|
|
*/
|
|
static int _cross_check_apqns(struct plugin_handle *ph,
|
|
const struct kms_apqn *apqns, size_t num_apqns)
|
|
{
|
|
u8 new_mkvp[MKVP_LENGTH] = { 0, };
|
|
u8 mkvp[MKVP_LENGTH] = { 0, };
|
|
struct cca_lib cca = { 0 };
|
|
struct mk_info mk_info;
|
|
bool mismatch = false;
|
|
bool print = false;
|
|
int rc = -ENODEV;
|
|
char temp[200];
|
|
size_t i;
|
|
|
|
for (i = 0; i < num_apqns; i++) {
|
|
rc = select_cca_adapter(&cca, apqns[i].card, apqns[i].domain,
|
|
ph->verbose);
|
|
if (rc != 0) {
|
|
_set_error(ph, "Failed to select APQN %02x.%04x: %s",
|
|
apqns[i].card, apqns[i].domain,
|
|
strerror(-rc));
|
|
goto out;
|
|
}
|
|
|
|
rc = _get_cca_apka_mk_info(ph, &cca, &mk_info);
|
|
if (rc != 0) {
|
|
_set_error(ph, "Failed to get the APKA master key "
|
|
"infos for APQN %02x.%04x: %s",
|
|
apqns[i].card, apqns[i].domain,
|
|
strerror(-rc));
|
|
goto out;
|
|
}
|
|
|
|
if (mk_info.new_mk.mk_state == MK_STATE_PARTIAL) {
|
|
print = true;
|
|
sprintf(temp, "INFO: APQN %02x.%04x: The NEW APKA "
|
|
"master key register is only partially loaded.",
|
|
apqns[i].card, apqns[i].domain);
|
|
util_print_indented(temp, 0);
|
|
}
|
|
|
|
if (MKVP_ZERO(new_mkvp) &&
|
|
mk_info.new_mk.mk_state == MK_STATE_FULL)
|
|
memcpy(new_mkvp, mk_info.new_mk.mkvp, sizeof(new_mkvp));
|
|
|
|
if (mk_info.new_mk.mk_state == MK_STATE_FULL &&
|
|
!MKVP_EQ(mk_info.new_mk.mkvp, new_mkvp)) {
|
|
print = true;
|
|
sprintf(temp, "WARNING: APQN %02x.%04x: The NEW APKA "
|
|
"master key register contains a different "
|
|
"master key than the NEW APKA register of "
|
|
"other APQNs.", apqns[i].card, apqns[i].domain);
|
|
util_print_indented(temp, 0);
|
|
}
|
|
|
|
if (mk_info.cur_mk.mk_state != MK_STATE_VALID) {
|
|
mismatch = true;
|
|
print = true;
|
|
printf("WARNING: APQN %02x.%04x: No APKA master key is "
|
|
"set.\n", apqns[i].card, apqns[i].domain);
|
|
continue;
|
|
}
|
|
|
|
if (mk_info.old_mk.mk_state == MK_STATE_VALID &&
|
|
MKVP_EQ(mk_info.old_mk.mkvp, mk_info.cur_mk.mkvp)) {
|
|
print = true;
|
|
sprintf(temp, "INFO: APQN %02x.%04x: The OLD APKA "
|
|
"master key register contains the same master "
|
|
"key as the CURRENT APKA master key register.",
|
|
apqns[i].card, apqns[i].domain);
|
|
util_print_indented(temp, 0);
|
|
}
|
|
|
|
if (mk_info.new_mk.mk_state == MK_STATE_FULL &&
|
|
MKVP_EQ(mk_info.new_mk.mkvp, mk_info.cur_mk.mkvp)) {
|
|
print = true;
|
|
sprintf(temp, "INFO: APQN %02x.%04x: The NEW APKA "
|
|
"master key register contains the same master "
|
|
"key as the CURRENT APKA master key register.",
|
|
apqns[i].card, apqns[i].domain);
|
|
util_print_indented(temp, 0);
|
|
}
|
|
|
|
if (mk_info.new_mk.mk_state == MK_STATE_FULL &&
|
|
mk_info.old_mk.mk_state == MK_STATE_VALID &&
|
|
MKVP_EQ(mk_info.new_mk.mkvp, mk_info.old_mk.mkvp)) {
|
|
print = true;
|
|
sprintf(temp, "INFO: APQN %02x.%04x: The NEW APKA "
|
|
"master key register contains the same master "
|
|
"key as the OLD APKA master key register.",
|
|
apqns[i].card, apqns[i].domain);
|
|
util_print_indented(temp, 0);
|
|
}
|
|
|
|
if (MKVP_ZERO(mkvp))
|
|
memcpy(mkvp, mk_info.cur_mk.mkvp, sizeof(mkvp));
|
|
|
|
if (!MKVP_EQ(mk_info.cur_mk.mkvp, mkvp)) {
|
|
mismatch = true;
|
|
print = true;
|
|
sprintf(temp, "WARNING: APQN %02x.%04x: The CURRENT "
|
|
"APKA master key register contains a different "
|
|
"master key than the CURRENT APKA register of "
|
|
"other APQNs.", apqns[i].card, apqns[i].domain);
|
|
util_print_indented(temp, 0);
|
|
}
|
|
}
|
|
|
|
if (mismatch) {
|
|
_set_error(ph, "Your APKA master key setup is improper");
|
|
rc = -ENODEV;
|
|
}
|
|
|
|
if (print)
|
|
_print_apka_mks(ph, &cca, apqns, num_apqns);
|
|
|
|
out:
|
|
if (cca.lib_csulcca != NULL)
|
|
dlclose(cca.lib_csulcca);
|
|
|
|
return rc;
|
|
}
|
|
|
|
/**
|
|
* Build an APQN string from an APQN array
|
|
*
|
|
* @param apqns An array of APQNs
|
|
* @param num_apqns The number of elements in above array
|
|
*
|
|
* @return an allocated string with the APQNs
|
|
*/
|
|
static char *_build_apqn_string(const struct kms_apqn *apqns, size_t num_apqns)
|
|
{
|
|
char *apqn_str, *str;
|
|
size_t size, i;
|
|
|
|
if (num_apqns == 0) {
|
|
apqn_str = util_malloc(1);
|
|
*apqn_str = '\0';
|
|
return apqn_str;
|
|
}
|
|
|
|
size = num_apqns * 8; /* 'cc.dddd' plus ',' or '\0' */
|
|
apqn_str = util_malloc(size);
|
|
|
|
str = apqn_str;
|
|
for (i = 0; i < num_apqns; i++) {
|
|
if (i != 0) {
|
|
*str = ',';
|
|
str++;
|
|
}
|
|
|
|
sprintf(str, "%02x.%04x", apqns[i].card, apqns[i].domain);
|
|
str += 7;
|
|
}
|
|
|
|
return apqn_str;
|
|
}
|
|
|
|
struct template_cb_data {
|
|
const char *template;
|
|
struct ekmf_template_info **info;
|
|
};
|
|
|
|
/**
|
|
* Callback for ekmf_list_templates function to get template info by name
|
|
*
|
|
* @param curl_handle a CURL handle that can be used to perform further
|
|
* EKMFWeb functions within the callback.
|
|
* @param template_info a struct containing information about the template.
|
|
* If any of the information needs to be kept, then the
|
|
* callback function must make a copy of the
|
|
* information. The memory holding the information
|
|
* passed to the callback is no longer valid after the
|
|
* callback has returned.
|
|
* @param private the private pointer that was specified with the
|
|
* ekmf_list_templates invocation.
|
|
*
|
|
* @returns 0 on success, a negative errno in case of an error.
|
|
*/
|
|
static int _template_cb(CURL *UNUSED(curl_handle),
|
|
struct ekmf_template_info *template_info,
|
|
void *private)
|
|
{
|
|
struct template_cb_data *data = private;
|
|
int rc;
|
|
|
|
if (*data->info != NULL)
|
|
return 0;
|
|
|
|
if (strcmp(template_info->name, data->template) != 0)
|
|
return 0;
|
|
|
|
rc = ekmf_clone_template_info(template_info, data->info);
|
|
|
|
return rc;
|
|
}
|
|
|
|
/**
|
|
* Get information about a template by name
|
|
*
|
|
* @param ph the plugin handle
|
|
* @param template the name of the template
|
|
* @param info On return: the template info. Must be freed by the
|
|
* caller via ekmf_free_template_info.
|
|
*
|
|
* @returns 0 on success, a negative errno in case of an error.
|
|
*/
|
|
static int _get_template_by_name(struct plugin_handle *ph, const char *template,
|
|
struct ekmf_template_info **info)
|
|
{
|
|
struct template_cb_data data;
|
|
char *error_msg = NULL;
|
|
int rc;
|
|
|
|
*info = NULL;
|
|
|
|
data.template = template;
|
|
data.info = info;
|
|
|
|
rc = ekmf_list_templates(&ph->ekmf_config, &ph->curl_handle,
|
|
_template_cb, &data, template,
|
|
EKMFWEB_TEMPLATE_STATE_ACTIVE,
|
|
&error_msg, ph->verbose);
|
|
if (rc != 0) {
|
|
_set_error(ph, "Failed to get the template '%s': %s", template,
|
|
error_msg != NULL ? error_msg : strerror(-rc));
|
|
_remove_login_token_if_error(ph, rc);
|
|
goto out;
|
|
}
|
|
if (*info == NULL) {
|
|
rc = -ENOENT;
|
|
_set_error(ph, "Template '%s' does not exist", template);
|
|
goto out;
|
|
}
|
|
|
|
out:
|
|
if (error_msg != NULL)
|
|
free(error_msg);
|
|
|
|
return rc;
|
|
}
|
|
|
|
/**
|
|
* Check a template if it is using the desired settings
|
|
*
|
|
* @param ph the plugin handle
|
|
* @param info the template info
|
|
* @param keystore_type the expected keystore type
|
|
*
|
|
* @returns 0 on success, a negative errno in case of an error.
|
|
*/
|
|
static int _check_template(struct plugin_handle *ph,
|
|
struct ekmf_template_info *info,
|
|
const char *keystore_type)
|
|
{
|
|
int rc = 0;
|
|
|
|
if (strcmp(info->state, EKMFWEB_TEMPLATE_STATE_ACTIVE) != 0) {
|
|
if (strcmp(info->state, EKMFWEB_TEMPLATE_STATE_HISTORY) == 0)
|
|
_set_error(ph, "Template '%s' is in state '%s'. "
|
|
"If the template has been recently changed, "
|
|
"run 'zkey kms configure --refresh-settings'"
|
|
" to refresh the templates.", info->name,
|
|
EKMFWEB_TEMPLATE_STATE_HISTORY);
|
|
else
|
|
_set_error(ph, "Template '%s' is in state '%s', "
|
|
"but only templates in state '%s' can "
|
|
"be used.", info->name, info->state,
|
|
EKMFWEB_TEMPLATE_STATE_ACTIVE);
|
|
rc = -EINVAL;
|
|
goto out;
|
|
}
|
|
|
|
if (strcmp(info->key_state, EKMFWEB_KEY_STATE_ACTIVE) != 0) {
|
|
_set_error(ph, "Template '%s' generates key in state '%s', but "
|
|
"only templates that generate keys in state '%s' "
|
|
"are supported.", info->name, info->key_state,
|
|
EKMFWEB_KEY_STATE_ACTIVE);
|
|
rc = -EINVAL;
|
|
goto out;
|
|
}
|
|
|
|
if (strcmp(info->keystore_type, keystore_type) != 0) {
|
|
_set_error(ph, "Template '%s' uses key store type '%s', but "
|
|
"only key store type '%s' is supported for %s.",
|
|
info->name, info->keystore_type, keystore_type,
|
|
strcmp(keystore_type,
|
|
EKMFWEB_KEYSTORE_TYPE_PERV_ENCR) == 0 ?
|
|
"volume encryption keys" : "identity keys");
|
|
rc = -EINVAL;
|
|
goto out;
|
|
}
|
|
|
|
if (strcmp(info->keystore_type, EKMFWEB_KEYSTORE_TYPE_PERV_ENCR) == 0) {
|
|
if (strcmp(info->key_type, EKMFWEB_KEY_TYPE_CIPHER) != 0) {
|
|
_set_error(ph, "Template '%s' generates keys of type "
|
|
"'%s', but only key type '%s' is supported "
|
|
"for volume encryption keys.",
|
|
info->name, info->key_type,
|
|
EKMFWEB_KEY_TYPE_CIPHER);
|
|
rc = -EINVAL;
|
|
goto out;
|
|
}
|
|
|
|
if (strcmp(info->algorithm, EKMFWEB_KEY_ALGORITHM_AES) != 0) {
|
|
_set_error(ph, "Template '%s' generates keys with "
|
|
"algorithm '%s', but only algorithm '%s' is "
|
|
"supported for volume encryption keys.",
|
|
info->name, info->algorithm,
|
|
EKMFWEB_KEY_ALGORITHM_AES);
|
|
rc = -EINVAL;
|
|
goto out;
|
|
}
|
|
|
|
if (info->export_allowed == false) {
|
|
_set_error(ph, "Template '%s' generates key that are "
|
|
"not allowed to be exported, but only "
|
|
"templates that generate keys that are "
|
|
"allowed to be exported are supported for "
|
|
"volume encryption keys.",
|
|
info->name);
|
|
rc = -EINVAL;
|
|
goto out;
|
|
}
|
|
}
|
|
|
|
if (strcmp(info->keystore_type, EKMFWEB_KEYSTORE_TYPE_IDENTITY) == 0) {
|
|
if (strcmp(info->algorithm, EKMFWEB_KEY_ALGORITHM_ECC) != 0 &&
|
|
strcmp(info->algorithm, EKMFWEB_KEY_ALGORITHM_RSA) != 0) {
|
|
_set_error(ph, "Template '%s' generates keys with "
|
|
"algorithm '%s', but only algorithms '%s' "
|
|
"and '%s' are supported for identity keys.",
|
|
info->name, info->algorithm,
|
|
EKMFWEB_KEY_ALGORITHM_ECC,
|
|
EKMFWEB_KEY_ALGORITHM_RSA);
|
|
rc = -EINVAL;
|
|
goto out;
|
|
}
|
|
}
|
|
|
|
out:
|
|
return rc;
|
|
}
|
|
|
|
/**
|
|
* Check the 2 XTS templates
|
|
*
|
|
* @param ph the plugin handle
|
|
* @param xts1_info the template info if XTS key 1
|
|
* @param xts2_info the template info if XTS key 2
|
|
*
|
|
* @returns 0 on success, a negative errno in case of an error.
|
|
*/
|
|
static int _check_xts_templates(struct plugin_handle *ph,
|
|
struct ekmf_template_info *xts1_info,
|
|
struct ekmf_template_info *xts2_info)
|
|
{
|
|
size_t i;
|
|
|
|
if (strcasecmp(xts1_info->label_template,
|
|
xts2_info->label_template) == 0) {
|
|
_set_error(ph, "The 2 XTS templates can not have the same "
|
|
"label template.");
|
|
return -EINVAL;
|
|
}
|
|
|
|
if (xts1_info->key_size != xts2_info->key_size) {
|
|
_set_error(ph, "The 2 XTS templates must have the same key "
|
|
"size.");
|
|
return -EINVAL;
|
|
}
|
|
|
|
if (xts1_info->label_tags.num_tag_defs !=
|
|
xts2_info->label_tags.num_tag_defs) {
|
|
_set_error(ph, "The 2 XTS templates must have the same label "
|
|
"tags. The templates differ in the number of label "
|
|
"tags: '%s' '%s'", xts1_info->label_template,
|
|
xts2_info->label_template);
|
|
return -EINVAL;
|
|
}
|
|
|
|
for (i = 0; i < xts1_info->label_tags.num_tag_defs; i++) {
|
|
if (strcasecmp(xts1_info->label_tags.tag_defs[i].name,
|
|
xts2_info->label_tags.tag_defs[i].name) != 0) {
|
|
_set_error(ph, "The 2 XTS templates must have the same "
|
|
"label tags. Mismatch in tag '%s': "
|
|
"'%s' '%s'",
|
|
xts1_info->label_tags.tag_defs[i].name,
|
|
xts1_info->label_template,
|
|
xts2_info->label_template);
|
|
return -EINVAL;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
struct template_infos {
|
|
char *template;
|
|
struct ekmf_template_info *info;
|
|
const char *name_prop;
|
|
const char *label_prop;
|
|
const char *id_prop;
|
|
const char *keystore_type;
|
|
};
|
|
|
|
#define NUM_TEMPLATES 4
|
|
#define IDENTITY 0
|
|
#define XTS1 1
|
|
#define XTS2 2
|
|
#define NONXTS 3
|
|
|
|
/**
|
|
* Retrieves the key templates to be used by the plugin
|
|
*
|
|
* @param ph the plugin handle
|
|
*
|
|
* @returns 0 on success, a negative errno in case of an error.
|
|
*/
|
|
static int _get_templates(struct plugin_handle *ph)
|
|
{
|
|
struct template_infos tmpl[NUM_TEMPLATES] = {
|
|
{ .template = NULL, .info = NULL,
|
|
.name_prop = EKMFWEB_CONFIG_TEMPLATE_IDENTITY,
|
|
.label_prop = EKMFWEB_CONFIG_TEMPLATE_IDENTITY_LABEL,
|
|
.id_prop = EKMFWEB_CONFIG_TEMPLATE_IDENTITY_ID,
|
|
.keystore_type = EKMFWEB_KEYSTORE_TYPE_IDENTITY, },
|
|
{ .template = NULL, .info = NULL,
|
|
.name_prop = EKMFWEB_CONFIG_TEMPLATE_XTS1,
|
|
.label_prop = EKMFWEB_CONFIG_TEMPLATE_XTS1_LABEL,
|
|
.id_prop = EKMFWEB_CONFIG_TEMPLATE_XTS1_ID,
|
|
.keystore_type = EKMFWEB_KEYSTORE_TYPE_PERV_ENCR, },
|
|
{ .template = NULL, .info = NULL,
|
|
.name_prop = EKMFWEB_CONFIG_TEMPLATE_XTS2,
|
|
.label_prop = EKMFWEB_CONFIG_TEMPLATE_XTS2_LABEL,
|
|
.id_prop = EKMFWEB_CONFIG_TEMPLATE_XTS2_ID,
|
|
.keystore_type = EKMFWEB_KEYSTORE_TYPE_PERV_ENCR, },
|
|
{ .template = NULL, .info = NULL,
|
|
.name_prop = EKMFWEB_CONFIG_TEMPLATE_NONXTS,
|
|
.label_prop = EKMFWEB_CONFIG_TEMPLATE_NONXTS_LABEL,
|
|
.id_prop = EKMFWEB_CONFIG_TEMPLATE_NONXTS_ID,
|
|
.keystore_type = EKMFWEB_KEYSTORE_TYPE_PERV_ENCR, },
|
|
};
|
|
char *error_msg = NULL;
|
|
int t, rc;
|
|
|
|
rc = ekmf_get_settings(&ph->ekmf_config, &ph->curl_handle,
|
|
&tmpl[IDENTITY].template, &tmpl[XTS1].template,
|
|
&tmpl[XTS2].template, &tmpl[NONXTS].template,
|
|
&error_msg, ph->verbose);
|
|
if (rc != 0) {
|
|
_set_error(ph, "Failed to get settings from EKMF Web: %s",
|
|
error_msg != NULL ? error_msg : strerror(-rc));
|
|
goto out;
|
|
}
|
|
|
|
for (t = 0; t < NUM_TEMPLATES; t++) {
|
|
rc = _get_template_by_name(ph, tmpl[t].template, &tmpl[t].info);
|
|
if (rc != 0)
|
|
goto out;
|
|
}
|
|
|
|
rc = _check_xts_templates(ph, tmpl[XTS1].info, tmpl[XTS2].info);
|
|
if (rc != 0)
|
|
goto out;
|
|
|
|
for (t = 0; t < NUM_TEMPLATES; t++) {
|
|
rc = _check_template(ph, tmpl[t].info, tmpl[t].keystore_type);
|
|
if (rc != 0)
|
|
goto out;
|
|
|
|
rc = _set_or_remove_property(ph, tmpl[t].name_prop,
|
|
tmpl[t].template);
|
|
if (rc != 0)
|
|
goto out;
|
|
rc = _set_or_remove_property(ph, tmpl[t].label_prop,
|
|
tmpl[t].info->label_template);
|
|
if (rc != 0)
|
|
goto out;
|
|
rc = _set_or_remove_property(ph, tmpl[t].id_prop,
|
|
tmpl[t].info->uuid);
|
|
if (rc != 0)
|
|
goto out;
|
|
}
|
|
|
|
out:
|
|
for (t = 0; t < NUM_TEMPLATES; t++) {
|
|
if (tmpl[t].info != NULL)
|
|
ekmf_free_template_info(tmpl[t].info);
|
|
if (tmpl[t].template != NULL)
|
|
free(tmpl[t].template);
|
|
}
|
|
if (error_msg != NULL)
|
|
free(error_msg);
|
|
|
|
return rc;
|
|
}
|
|
|
|
/**
|
|
* Retrieves the EKMF Web system settings. This requires a login. If no
|
|
* valid login token is available, a login is performed.
|
|
*
|
|
* @param ph the plugin handle
|
|
*
|
|
* @returns 0 on success, a negative errno in case of an error.
|
|
*/
|
|
static int _get_ekmfweb_settings(struct plugin_handle *ph)
|
|
{
|
|
char *error_msg = NULL;
|
|
int rc;
|
|
|
|
_check_config_complete(ph);
|
|
|
|
if (ph->ekmf_config.login_token != NULL) {
|
|
remove(ph->ekmf_config.login_token);
|
|
FREE_AND_SET_NULL(ph->ekmf_config.login_token);
|
|
}
|
|
rc = _set_or_remove_property(ph, EKMFWEB_CONFIG_LOGIN_TOKEN, NULL);
|
|
if (rc != 0)
|
|
goto out;
|
|
rc = _set_or_remove_property(ph, EKMFWEB_CONFIG_PASSCODE_URL, NULL);
|
|
if (rc != 0)
|
|
goto out;
|
|
|
|
rc = ekmf_check_feature(&ph->ekmf_config, &ph->curl_handle,
|
|
&error_msg, ph->verbose);
|
|
if (rc != 0) {
|
|
if (rc == -ENOTSUP)
|
|
_set_error(ph, "%s", error_msg);
|
|
else
|
|
_set_error(ph, "Failed to check the features of the "
|
|
"EKMF Web server at '%s': %s",
|
|
ph->ekmf_config.base_url,
|
|
error_msg != NULL ? error_msg :
|
|
strerror(-rc));
|
|
goto out;
|
|
}
|
|
|
|
rc = kms_login((kms_handle_t)ph);
|
|
if (rc != 0)
|
|
goto out;
|
|
|
|
if (ph->ekmf_config.ekmf_server_pubkey != NULL)
|
|
remove(ph->ekmf_config.ekmf_server_pubkey);
|
|
FREE_AND_SET_NULL(ph->ekmf_config.ekmf_server_pubkey);
|
|
|
|
util_asprintf((char **)&ph->ekmf_config.ekmf_server_pubkey,
|
|
"%s/%s", ph->config_path,
|
|
EKMFWEB_CONFIG_EKMFWEB_PUBKEY_FILE);
|
|
|
|
rc = ekmf_get_public_key(&ph->ekmf_config, &ph->curl_handle,
|
|
&error_msg, ph->verbose);
|
|
if (rc != 0) {
|
|
_set_error(ph, "Failed to get the public key of the EKMF Web "
|
|
"server at '%s': %s", ph->ekmf_config.base_url,
|
|
error_msg != NULL ? error_msg : strerror(-rc));
|
|
_remove_login_token_if_error(ph, rc);
|
|
goto out;
|
|
}
|
|
|
|
rc = _set_file_permission(ph, ph->ekmf_config.ekmf_server_pubkey);
|
|
if (rc != 0)
|
|
goto out;
|
|
|
|
rc = _set_or_remove_property(ph, EKMFWEB_CONFIG_EKMFWEB_PUBKEY,
|
|
ph->ekmf_config.ekmf_server_pubkey);
|
|
if (rc != 0)
|
|
goto out;
|
|
|
|
rc = _get_templates(ph);
|
|
if (rc != 0)
|
|
goto out;
|
|
|
|
out:
|
|
if (error_msg != NULL)
|
|
free(error_msg);
|
|
|
|
return rc;
|
|
}
|
|
|
|
/**
|
|
* Check if the certificate is a self signed certificate, and if it is expired
|
|
* or not yet valid.
|
|
*
|
|
* @param ph the plugin handle
|
|
* @param cert_file the file name of the PEM file containing the cert
|
|
* @param self_signed on return: true if the cetr is a self signed cert
|
|
* @param valid on return: false if the cert is expired or not yet
|
|
* valid
|
|
* @returns 0 on success, a negative errno in case of an error.
|
|
*/
|
|
static int _check_certificate(struct plugin_handle *ph,
|
|
const char *cert_file, bool *self_signed,
|
|
bool *valid)
|
|
{
|
|
X509 *cert;
|
|
FILE *fp;
|
|
int rc;
|
|
|
|
fp = fopen(cert_file, "r");
|
|
if (fp == NULL) {
|
|
rc = -errno;
|
|
_set_error(ph, "Failed to open certificate PEM file '%s': %s",
|
|
cert_file, strerror(-rc));
|
|
return rc;
|
|
}
|
|
|
|
cert = PEM_read_X509(fp, NULL, NULL, NULL);
|
|
fclose(fp);
|
|
|
|
if (cert == NULL) {
|
|
_set_error(ph, "Failed to read certificate PEM file '%s'",
|
|
cert_file);
|
|
return -EIO;
|
|
}
|
|
|
|
|
|
*self_signed = (X509_NAME_cmp(X509_get_subject_name(cert),
|
|
X509_get_issuer_name(cert)) == 0);
|
|
|
|
*valid = (X509_cmp_current_time(X509_get0_notBefore(cert)) < 0 &&
|
|
X509_cmp_current_time(X509_get0_notAfter(cert)) > 0);
|
|
|
|
X509_free(cert);
|
|
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* Configures the connection to the EKMF Web server
|
|
*
|
|
* @param ph the plugin handle
|
|
* @param ekmfweb_url the URL of the EKMF Web server
|
|
* @param tls_ca_bundle the file or directory name of the CA bundle to use
|
|
* @param tls_client_cert the file name of the client certificate
|
|
* @param tls_client_key the file name of the client private key
|
|
* @param tls_client_key_passphrase the passphrase to unlock the key
|
|
* @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 *ekmfweb_url,
|
|
const char *tls_ca_bundle,
|
|
const char *tls_client_cert,
|
|
const char *tls_client_key,
|
|
const char *tls_client_key_passphrase,
|
|
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;
|
|
bool self_signed = false;
|
|
bool add_https = false;
|
|
bool verified = false;
|
|
bool valid = false;
|
|
char *error = NULL;
|
|
char *url = NULL;
|
|
int rc = 0;
|
|
char *tmp;
|
|
|
|
if (tls_client_cert != NULL && tls_client_key == NULL) {
|
|
_set_error(ph, "Option '--tls-client-key' is required when "
|
|
"option '--tls-client-cert' is specified.");
|
|
return -EINVAL;
|
|
}
|
|
if (tls_client_key != NULL && tls_client_cert == NULL) {
|
|
_set_error(ph, "Option '--tls-client-cert' is required when "
|
|
"option '--tls-client-key' is specified.");
|
|
return -EINVAL;
|
|
}
|
|
if (tls_client_key_passphrase != NULL && tls_client_key == NULL) {
|
|
_set_error(ph, "Option '--tls-client-key-passphrase' is only "
|
|
"valid together with option "
|
|
"'--tls-client-key'.");
|
|
return -EINVAL;
|
|
}
|
|
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->ekmf_config.base_url != NULL) {
|
|
util_print_indented("ATTENTION: The EKMF Web server connection "
|
|
"has already been configured!\n"
|
|
"When you re-configure the EKMF Web server "
|
|
"connection, you may need to re-register "
|
|
"this zkey client with the changed EKMF "
|
|
"Web server.", 0);
|
|
printf("%s: Re-configure the EKMF Web server connection "
|
|
"[y/N]? ",
|
|
program_invocation_short_name);
|
|
if (!prompt_for_yes(ph->verbose)) {
|
|
_set_error(ph, "Opertion aborted by user");
|
|
return -ECANCELED;
|
|
}
|
|
}
|
|
|
|
if (strncmp(ekmfweb_url, "http://", 6) == 0) {
|
|
_set_error(ph, "The use of insecured HTTP is not allowed.");
|
|
return -EINVAL;
|
|
}
|
|
|
|
if (strncmp(ekmfweb_url, "https://", 7) != 0)
|
|
add_https = true;
|
|
|
|
util_asprintf(&url, "%s%s", add_https ? "https://" : "", ekmfweb_url);
|
|
if (url[strlen(url) - 1] == '/')
|
|
url[strlen(url) - 1] = '\0';
|
|
|
|
pr_verbose(ph, "url: '%s'", url);
|
|
|
|
FREE_AND_SET_NULL(ph->ekmf_config.base_url);
|
|
ph->ekmf_config.base_url = url;
|
|
|
|
rc = properties_set(ph->properties, EKMFWEB_CONFIG_URL, url);
|
|
if (rc != 0) {
|
|
_set_error(ph, "Failed to set URL property: "
|
|
"%s", strerror(-rc));
|
|
goto out;
|
|
}
|
|
|
|
FREE_AND_SET_NULL(ph->ekmf_config.tls_ca);
|
|
if (tls_ca_bundle != NULL)
|
|
ph->ekmf_config.tls_ca = util_strdup(tls_ca_bundle);
|
|
rc = _set_or_remove_property(ph, EKMFWEB_CONFIG_CA_BUNDLE,
|
|
tls_ca_bundle);
|
|
|
|
FREE_AND_SET_NULL(ph->ekmf_config.tls_client_cert);
|
|
if (tls_client_cert != NULL)
|
|
ph->ekmf_config.tls_client_cert = util_strdup(tls_client_cert);
|
|
rc = _set_or_remove_property(ph, EKMFWEB_CONFIG_CLIENT_CERT,
|
|
tls_client_cert);
|
|
if (rc != 0)
|
|
goto out;
|
|
|
|
FREE_AND_SET_NULL(ph->ekmf_config.tls_client_key);
|
|
if (tls_client_key != NULL)
|
|
ph->ekmf_config.tls_client_key = util_strdup(tls_client_key);
|
|
rc = _set_or_remove_property(ph, EKMFWEB_CONFIG_CLIENT_KEY,
|
|
tls_client_key);
|
|
if (rc != 0)
|
|
goto out;
|
|
|
|
tmp = NULL;
|
|
FREE_AND_SET_NULL(ph->ekmf_config.tls_client_key_passphrase);
|
|
if (tls_client_key_passphrase != NULL) {
|
|
ph->ekmf_config.tls_client_key_passphrase =
|
|
util_strdup(tls_client_key_passphrase);
|
|
tmp = _encode_passphrase(tls_client_key_passphrase);
|
|
if (tmp == NULL) {
|
|
_set_error(ph, "Failed to encode the passphrase");
|
|
rc = -EIO;
|
|
goto out;
|
|
}
|
|
}
|
|
rc = _set_or_remove_property(ph, EKMFWEB_CONFIG_CLIENT_KEY_PASSPHRASE,
|
|
tmp);
|
|
if (tmp != NULL)
|
|
free(tmp);
|
|
if (rc != 0)
|
|
goto out;
|
|
|
|
util_asprintf(&server_cert_temp, "%s/%s-tmp", ph->config_path,
|
|
EKMFWEB_CONFIG_SERVER_CERT_FILE);
|
|
util_asprintf(&server_pubkey_temp, "%s/%s-tmp", ph->config_path,
|
|
EKMFWEB_CONFIG_SERVER_PUBKEY_FILE);
|
|
|
|
rc = ekmf_get_server_cert_chain(&ph->ekmf_config,
|
|
server_cert_temp,
|
|
server_pubkey_temp,
|
|
NULL, &verified,
|
|
&error, ph->verbose);
|
|
if (rc != 0) {
|
|
_set_error(ph, "Failed to connect to EKMF Web server at '%s': "
|
|
"%s", ph->ekmf_config.base_url,
|
|
error != NULL ? error : strerror(-rc));
|
|
goto out;
|
|
}
|
|
|
|
rc = _check_certificate(ph, server_cert_temp, &self_signed, &valid);
|
|
if (rc != 0)
|
|
goto out;
|
|
|
|
pr_verbose(ph, "verified: %d", verified);
|
|
pr_verbose(ph, "self signed: %d", self_signed);
|
|
pr_verbose(ph, "valid: %d", valid);
|
|
|
|
util_print_indented("The EKMF Web server presented the following "
|
|
"certificate to identify itself:", 0);
|
|
|
|
rc = ekmf_print_certificates(server_cert_temp, ph->verbose);
|
|
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 EKMF Web server you intent to work with "
|
|
"[y/N]? ", program_invocation_short_name);
|
|
if (!prompt_for_yes(ph->verbose)) {
|
|
_set_error(ph, "Opertion aborted by user");
|
|
rc = -ECANCELED;
|
|
goto out;
|
|
}
|
|
|
|
ph->ekmf_config.tls_verify_peer = !self_signed || tls_trust_server_cert;
|
|
if (tls_dont_verify_server_cert)
|
|
ph->ekmf_config.tls_verify_peer = false;
|
|
rc = _set_or_remove_property(ph, EKMFWEB_CONFIG_VERIFY_SERVER_CERT,
|
|
ph->ekmf_config.tls_verify_peer ?
|
|
"yes" : "no");
|
|
if (rc != 0)
|
|
goto out;
|
|
|
|
ph->ekmf_config.tls_verify_host = tls_verify_hostname;
|
|
rc = _set_or_remove_property(ph, EKMFWEB_CONFIG_VERIFY_HOSTNAME,
|
|
ph->ekmf_config.tls_verify_host ?
|
|
"yes" : "no");
|
|
if (rc != 0)
|
|
goto out;
|
|
|
|
rc = _get_ekmfweb_settings(ph);
|
|
if (rc != 0) {
|
|
util_print_indented("The server you are connected with is not "
|
|
"a valid EKMF Web server, or is not "
|
|
"configured properly", 0);
|
|
goto out;
|
|
}
|
|
|
|
FREE_AND_SET_NULL(ph->ekmf_config.tls_server_cert);
|
|
util_asprintf(&server_cert_file, "%s/%s", ph->config_path,
|
|
EKMFWEB_CONFIG_SERVER_CERT_FILE);
|
|
if (tls_trust_server_cert) {
|
|
ph->ekmf_config.tls_server_cert = util_strdup(server_cert_file);
|
|
rc = _activate_temp_file(ph, server_cert_temp,
|
|
server_cert_file);
|
|
if (rc != 0)
|
|
goto out;
|
|
} else {
|
|
remove(server_cert_file);
|
|
}
|
|
rc = _set_or_remove_property(ph, EKMFWEB_CONFIG_SERVER_CERT,
|
|
tls_trust_server_cert ?
|
|
server_cert_file : NULL);
|
|
if (rc != 0)
|
|
goto out;
|
|
|
|
FREE_AND_SET_NULL(ph->ekmf_config.tls_pinned_pubkey);
|
|
util_asprintf(&server_pubkey_file, "%s/%s", ph->config_path,
|
|
EKMFWEB_CONFIG_SERVER_PUBKEY_FILE);
|
|
if (tls_pin_server_pubkey) {
|
|
ph->ekmf_config.tls_pinned_pubkey =
|
|
util_strdup(server_pubkey_file);
|
|
rc = _activate_temp_file(ph, server_pubkey_temp,
|
|
server_pubkey_file);
|
|
if (rc != 0)
|
|
goto out;
|
|
} else {
|
|
remove(server_pubkey_file);
|
|
}
|
|
rc = _set_or_remove_property(ph, EKMFWEB_CONFIG_SERVER_PUBKEY,
|
|
tls_pin_server_pubkey ?
|
|
server_pubkey_file : 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 (error != NULL)
|
|
free(error);
|
|
|
|
return rc;
|
|
}
|
|
|
|
struct config_options {
|
|
const char *ekmfweb_url;
|
|
const char *tls_ca_bundle;
|
|
const char *tls_client_cert;
|
|
const char *tls_client_key;
|
|
const char *tls_client_key_passphrase;
|
|
bool tls_pin_server_pubkey;
|
|
bool tls_trust_server_cert;
|
|
bool tls_dont_verify_server_cert;
|
|
bool tls_verify_hostname;
|
|
bool refresh_settings;
|
|
};
|
|
|
|
/**
|
|
* 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->tls_ca_bundle != NULL) {
|
|
_set_error(ph, "Option '--tls-ca-bundle' is only valid "
|
|
"together with option '--ekmfweb-url'.");
|
|
rc = -EINVAL;
|
|
goto out;
|
|
}
|
|
if (opts->tls_client_cert != NULL) {
|
|
_set_error(ph, "Option '--tls-client-cert' is only valid "
|
|
"together with option '--ekmfweb-url'.");
|
|
rc = -EINVAL;
|
|
goto out;
|
|
}
|
|
if (opts->tls_client_key != NULL) {
|
|
_set_error(ph, "Option '--tls-client-key' is only valid "
|
|
"together with option '--ekmfweb-url'.");
|
|
rc = -EINVAL;
|
|
goto out;
|
|
}
|
|
if (opts->tls_client_key_passphrase != NULL) {
|
|
_set_error(ph, "Option '--tls-client-key-passphrase' is only "
|
|
"valid together with option '--ekmfweb-url'.");
|
|
rc = -EINVAL;
|
|
goto out;
|
|
}
|
|
if (opts->tls_pin_server_pubkey) {
|
|
_set_error(ph, "Option '--tls-pin-server-pubkey' is only valid "
|
|
"together with option '--ekmfweb-url'.");
|
|
rc = -EINVAL;
|
|
goto out;
|
|
}
|
|
if (opts->tls_trust_server_cert) {
|
|
_set_error(ph, "Option '--tls-trust-server-cert' is only valid "
|
|
"together with option '--ekmfweb-url'.");
|
|
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 '--ekmfweb-url'.");
|
|
rc = -EINVAL;
|
|
goto out;
|
|
}
|
|
if (opts->tls_verify_hostname) {
|
|
_set_error(ph, "Option '--tls-verify-hostname' is only valid "
|
|
"together with option '--ekmfweb-url'.");
|
|
rc = -EINVAL;
|
|
goto out;
|
|
}
|
|
|
|
out:
|
|
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, "Configure");
|
|
for (i = 0; i < num_apqns; i++) {
|
|
pr_verbose(ph, " APQN: %02x.%04x", apqns[i].card,
|
|
apqns[i].domain);
|
|
}
|
|
for (i = 0; i < num_options; i++) {
|
|
if (isalnum(options[i].option))
|
|
pr_verbose(ph, " Option '%c': '%s'", options[i].option,
|
|
options[i].argument != NULL ?
|
|
options[i].argument : "(null)");
|
|
else
|
|
pr_verbose(ph, " Option %d: '%s'", options[i].option,
|
|
options[i].argument != NULL ?
|
|
options[i].argument : "(null)");
|
|
}
|
|
|
|
_clear_error(ph);
|
|
|
|
if (apqns != NULL) {
|
|
if (num_apqns > 0) {
|
|
rc = _cross_check_apqns(ph, apqns, num_apqns);
|
|
if (rc != 0)
|
|
goto out;
|
|
}
|
|
|
|
apqn_str = _build_apqn_string(apqns, num_apqns);
|
|
rc = properties_set(ph->properties, EKMFWEB_CONFIG_APQNS,
|
|
apqn_str);
|
|
if (rc != 0) {
|
|
_set_error(ph, "Failed to set APQNs property: %s",
|
|
strerror(-rc));
|
|
goto out;
|
|
}
|
|
|
|
config_changed = true;
|
|
}
|
|
|
|
for (i = 0; i < num_options; i++) {
|
|
switch (options[i].option) {
|
|
case 'u':
|
|
opts.ekmfweb_url = options[i].argument;
|
|
break;
|
|
case 'b':
|
|
opts.tls_ca_bundle = options[i].argument;
|
|
break;
|
|
case OPT_TLS_CLIENT_CERT:
|
|
opts.tls_client_cert = options[i].argument;
|
|
break;
|
|
case OPT_TLS_CLIENT_KEY:
|
|
opts.tls_client_key = options[i].argument;
|
|
break;
|
|
case OPT_TLS_CLIENT_KEY_PASSPHRASE:
|
|
opts.tls_client_key_passphrase = 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 'R':
|
|
opts.refresh_settings = true;
|
|
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.ekmfweb_url != NULL) {
|
|
rc = _configure_connection(ph, opts.ekmfweb_url,
|
|
opts.tls_ca_bundle,
|
|
opts.tls_client_cert,
|
|
opts.tls_client_key,
|
|
opts.tls_client_key_passphrase,
|
|
opts.tls_pin_server_pubkey,
|
|
opts.tls_trust_server_cert,
|
|
opts.tls_dont_verify_server_cert,
|
|
opts.tls_verify_hostname);
|
|
if (rc == 0) {
|
|
config_changed = true;
|
|
opts.refresh_settings = false; /* Already done */
|
|
}
|
|
} else {
|
|
rc = _error_connection_opts(ph, &opts);
|
|
}
|
|
if (rc != 0)
|
|
goto out;
|
|
|
|
if (opts.refresh_settings) {
|
|
rc = _get_ekmfweb_settings(ph);
|
|
if (rc != 0)
|
|
goto out;
|
|
|
|
config_changed = true;
|
|
}
|
|
|
|
out:
|
|
if (apqn_str != NULL)
|
|
free(apqn_str);
|
|
|
|
if (rc == 0) {
|
|
if (config_changed) {
|
|
rc = _save_config(ph);
|
|
if (rc != 0)
|
|
goto ret;
|
|
|
|
_check_config_complete(ph);
|
|
pr_verbose(ph, "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, "Deconfigure");
|
|
|
|
_clear_error(ph);
|
|
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* Prompts the user for input on stdin, and returns the entered value.
|
|
* The returned string must be freed by the caller.
|
|
*
|
|
* @param ph the plugin handle
|
|
* @param msg the message to prompt for the input (can be NULL)
|
|
*
|
|
* @returns the entered value, or NULL in case of an error.
|
|
*/
|
|
static char *_prompt_for_input(struct plugin_handle *ph, const char *msg)
|
|
{
|
|
size_t input_len = 0;
|
|
char *input = NULL;
|
|
int rc;
|
|
|
|
while (input_len == 0 || input == NULL || strlen(input) < 1) {
|
|
if (msg != NULL)
|
|
printf("%s: %s: ", program_invocation_short_name, msg);
|
|
|
|
rc = getline(&input, &input_len, stdin);
|
|
if (rc < 0) {
|
|
_set_error(ph, "Failed to read from stdin: %s",
|
|
strerror(errno));
|
|
if (input != NULL)
|
|
free(input);
|
|
return NULL;
|
|
}
|
|
|
|
if (input != NULL && input[strlen(input) - 1] == '\n')
|
|
input[strlen(input) - 1] = '\0';
|
|
}
|
|
|
|
return input;
|
|
}
|
|
|
|
/**
|
|
* 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;
|
|
char *passcode_url = NULL;
|
|
char *error_msg = NULL;
|
|
char *passcode = NULL;
|
|
char *user_id = NULL;
|
|
bool valid = false;
|
|
int rc;
|
|
|
|
util_assert(handle != NULL, "Internal error: handle is NULL");
|
|
|
|
pr_verbose(ph, "Login");
|
|
|
|
_clear_error(ph);
|
|
|
|
if (!ph->connection_configured) {
|
|
_set_error(ph, "The configuration is incomplete, run 'zkey "
|
|
"kms configure [OPTIONS]' to complete the "
|
|
"configuration.");
|
|
return -EINVAL;
|
|
}
|
|
|
|
if (ph->ekmf_config.login_token != NULL) {
|
|
rc = ekmf_check_login_token(&ph->ekmf_config, &valid, NULL,
|
|
ph->verbose);
|
|
pr_verbose(ph, "Login token valid: %d", valid);
|
|
|
|
if (rc == 0 && valid)
|
|
return 0;
|
|
|
|
remove(ph->ekmf_config.login_token);
|
|
FREE_AND_SET_NULL(ph->ekmf_config.login_token);
|
|
|
|
rc = _set_or_remove_property(ph, EKMFWEB_CONFIG_LOGIN_TOKEN,
|
|
NULL);
|
|
if (rc != 0)
|
|
goto out;
|
|
}
|
|
|
|
passcode_url = properties_get(ph->properties,
|
|
EKMFWEB_CONFIG_PASSCODE_URL);
|
|
if (passcode_url == NULL) {
|
|
util_asprintf(&passcode_url, "%s%s", ph->ekmf_config.base_url,
|
|
EKMFWEB_PASSCODE_URL);
|
|
|
|
rc = _set_or_remove_property(ph, EKMFWEB_CONFIG_PASSCODE_URL,
|
|
passcode_url);
|
|
if (rc != 0)
|
|
goto out;
|
|
}
|
|
|
|
pr_verbose(ph, "passcode url: '%s'", passcode_url);
|
|
|
|
user_id = _prompt_for_input(ph, "EKMF Web user ID");
|
|
if (user_id == NULL) {
|
|
rc = -EIO;
|
|
goto out;
|
|
}
|
|
|
|
pr_verbose(ph, "User-id: '%s'", user_id);
|
|
|
|
util_print_indented("Go to the following web page in your web browser, "
|
|
"login with the same user ID as entered above and "
|
|
"your password, and obtain a one time passcode and "
|
|
"enter it here.", 0);
|
|
printf("%s\n", passcode_url);
|
|
|
|
passcode = _prompt_for_input(ph, "Passcode");
|
|
if (passcode == NULL) {
|
|
rc = -EIO;
|
|
goto out;
|
|
}
|
|
|
|
pr_verbose(ph, "Passcode: '%s'", passcode);
|
|
|
|
util_asprintf((char **)&ph->ekmf_config.login_token, "%s/%s",
|
|
ph->config_path, EKMFWEB_CONFIG_LOGIN_TOKEN_FILE);
|
|
|
|
rc = ekmf_login(&ph->ekmf_config, &ph->curl_handle, user_id, passcode,
|
|
&error_msg, ph->verbose);
|
|
if (rc != 0) {
|
|
_set_error(ph, "Failed to login to EKMF Web server at '%s': "
|
|
"%s", ph->ekmf_config.base_url,
|
|
error_msg != NULL ? error_msg : strerror(-rc));
|
|
goto out;
|
|
}
|
|
|
|
rc = _set_file_permission(ph, ph->ekmf_config.login_token);
|
|
if (rc != 0)
|
|
goto out;
|
|
|
|
rc = _set_or_remove_property(ph, EKMFWEB_CONFIG_LOGIN_TOKEN,
|
|
ph->ekmf_config.login_token);
|
|
if (rc != 0)
|
|
goto out;
|
|
|
|
rc = _save_config(ph);
|
|
if (rc != 0)
|
|
goto out;
|
|
|
|
out:
|
|
if (passcode_url != NULL)
|
|
free(passcode_url);
|
|
if (user_id != NULL)
|
|
free(user_id);
|
|
if (passcode != NULL)
|
|
free(passcode);
|
|
if (error_msg != NULL)
|
|
free(error_msg);
|
|
|
|
return rc;
|
|
}
|
|
|
|
/**
|
|
* 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)
|
|
{
|
|
struct plugin_handle *ph = handle;
|
|
size_t i;
|
|
|
|
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, "Re-encipher mode: %d, kmreg=%d", mode, mkreg);
|
|
for (i = 0; i < num_options; i++) {
|
|
if (isalnum(options[i].option))
|
|
pr_verbose(ph, " Option '%c': '%s'", options[i].option,
|
|
options[i].argument != NULL ?
|
|
options[i].argument : "(null)");
|
|
else
|
|
pr_verbose(ph, " Option %d: '%s'", options[i].option,
|
|
options[i].argument != NULL ?
|
|
options[i].argument : "(null)");
|
|
}
|
|
|
|
_clear_error(ph);
|
|
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* 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 *UNUSED(key_blob),
|
|
size_t *UNUSED(key_blob_length),
|
|
char *UNUSED(key_id), size_t UNUSED(key_id_size),
|
|
char *UNUSED(key_label), size_t UNUSED(key_label_size))
|
|
{
|
|
struct plugin_handle *ph = handle;
|
|
size_t i;
|
|
|
|
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 ");
|
|
|
|
pr_verbose(ph, "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, " Property '%s': '%s", properties[i].name,
|
|
properties[i].value);
|
|
}
|
|
for (i = 0; i < num_options; i++) {
|
|
if (isalnum(options[i].option))
|
|
pr_verbose(ph, " Option '%c': '%s'", options[i].option,
|
|
options[i].argument != NULL ?
|
|
options[i].argument : "(null)");
|
|
else
|
|
pr_verbose(ph, " Option %d: '%s'", options[i].option,
|
|
options[i].argument != NULL ?
|
|
options[i].argument : "(null)");
|
|
}
|
|
|
|
_clear_error(ph);
|
|
|
|
if (!ph->config_complete) {
|
|
_set_error(ph, "The configuration is incomplete, run 'zkey "
|
|
"kms configure [OPTIONS]' to complete the "
|
|
"configuration.");
|
|
return -EINVAL;
|
|
}
|
|
|
|
if (strcasecmp(key_type, KEY_TYPE_CCA_AESCIPHER) != 0) {
|
|
_set_error(ph, "Key type '%s' is not supported by EKMF Web",
|
|
key_type);
|
|
return -ENOTSUP;
|
|
}
|
|
|
|
_set_error(ph, "Not yet implemented");
|
|
return -ENOTSUP;
|
|
}
|
|
|
|
/**
|
|
* 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;
|
|
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, "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");
|
|
util_assert(properties[i].value != NULL,
|
|
"Internal error: property value is NULL");
|
|
pr_verbose(ph, " Property '%s': '%s", properties[i].name,
|
|
properties[i].value);
|
|
}
|
|
|
|
_clear_error(ph);
|
|
|
|
if (!ph->config_complete) {
|
|
_set_error(ph, "The configuration is incomplete, run 'zkey "
|
|
"kms configure [OPTIONS]' to complete the "
|
|
"configuration.");
|
|
return -EINVAL;
|
|
}
|
|
|
|
_set_error(ph, "Not yet implemented");
|
|
return -ENOTSUP;
|
|
}
|
|
|
|
/**
|
|
* 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;
|
|
|
|
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, "Get key properties: key-ID: '%s'", key_id);
|
|
|
|
_clear_error(ph);
|
|
|
|
if (!ph->config_complete) {
|
|
_set_error(ph, "The configuration is incomplete, run 'zkey "
|
|
"kms configure [OPTIONS]' to complete the "
|
|
"configuration.");
|
|
return -EINVAL;
|
|
}
|
|
|
|
_set_error(ph, "Not yet implemented");
|
|
return -ENOTSUP;
|
|
}
|
|
|
|
/**
|
|
* 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;
|
|
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, "Remove key: key-ID: '%s'", key_id);
|
|
for (i = 0; i < num_options; i++) {
|
|
if (isalnum(options[i].option))
|
|
pr_verbose(ph, " Option '%c': '%s'", options[i].option,
|
|
options[i].argument != NULL ?
|
|
options[i].argument : "(null)");
|
|
else
|
|
pr_verbose(ph, " Option %d: '%s'", options[i].option,
|
|
options[i].argument != NULL ?
|
|
options[i].argument : "(null)");
|
|
}
|
|
|
|
_clear_error(ph);
|
|
|
|
if (!ph->config_complete) {
|
|
_set_error(ph, "The configuration is incomplete, run 'zkey "
|
|
"kms configure [OPTIONS]' to complete the "
|
|
"configuration.");
|
|
return -EINVAL;
|
|
}
|
|
|
|
_set_error(ph, "Not yet implemented");
|
|
return -ENOTSUP;
|
|
}
|
|
|
|
/**
|
|
* 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 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 *UNUSED(private_data))
|
|
{
|
|
struct plugin_handle *ph = handle;
|
|
size_t i;
|
|
|
|
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, "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, " Property '%s': '%s", properties[i].name,
|
|
properties[i].value);
|
|
}
|
|
for (i = 0; i < num_options; i++) {
|
|
if (isalnum(options[i].option))
|
|
pr_verbose(ph, " Option '%c': '%s'", options[i].option,
|
|
options[i].argument != NULL ?
|
|
options[i].argument : "(null)");
|
|
else
|
|
pr_verbose(ph, " Option %d: '%s'", options[i].option,
|
|
options[i].argument != NULL ?
|
|
options[i].argument : "(null)");
|
|
}
|
|
|
|
_clear_error(ph);
|
|
|
|
if (!ph->config_complete) {
|
|
_set_error(ph, "The configuration is incomplete, run 'zkey "
|
|
"kms configure [OPTIONS]' to complete the "
|
|
"configuration.");
|
|
return -EINVAL;
|
|
}
|
|
|
|
_set_error(ph, "Not yet implemented");
|
|
return -ENOTSUP;
|
|
}
|
|
|
|
/**
|
|
* 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_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_key(const kms_handle_t handle, const char *key_id,
|
|
unsigned char *key_blob, size_t *key_blob_length)
|
|
{
|
|
struct plugin_handle *ph = handle;
|
|
|
|
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, "Import Key, key-ID: '%s'", key_id);
|
|
|
|
_clear_error(ph);
|
|
|
|
if (!ph->config_complete) {
|
|
_set_error(ph, "The configuration is incomplete, run 'zkey "
|
|
"kms configure [OPTIONS]' to complete the "
|
|
"configuration.");
|
|
return -EINVAL;
|
|
}
|
|
|
|
_set_error(ph, "Not yet implemented");
|
|
return -ENOTSUP;
|
|
}
|
|
|
|
static const struct kms_functions kms_functions = {
|
|
.api_version = KMS_API_VERSION_1,
|
|
.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_key = kms_import_key,
|
|
};
|
|
|
|
/**
|
|
* 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;
|
|
}
|