zkey: Add more key management system specific commands

Add the following sub-commands for key management system plugin
handling:
- configure:  Configure a KMS plugin
- reencipher: Re-encipher secure keys used by a KMS plugin

Signed-off-by: Ingo Franzki <ifranzki@linux.ibm.com>
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
This commit is contained in:
Ingo Franzki
2020-06-03 17:24:54 +02:00
committed by Jan Höppner
parent 3fa511cfb9
commit 35dd59d04c
5 changed files with 1285 additions and 1 deletions

View File

@@ -73,7 +73,7 @@ properties.o: check-dep-zkey properties.c properties.h
keystore.o: keystore.c keystore.h properties.h pkey.h cca.h ep11.h utils.h
zkey-cryptsetup.o: check-dep-zkey-cryptsetup zkey-cryptsetup.c pkey.h cca.h \
ep11.h misc.h utils.h
kms.o: kms.c kms.h kms-plugin.h
kms.o: kms.c kms.h kms-plugin.h utils.h pkey.h
zkey: LDLIBS = -ldl -lcrypto
zkey: zkey.o pkey.o cca.o ep11.o properties.o keystore.o utils.o kms.o $(libs)

View File

@@ -11,6 +11,7 @@
#include <dirent.h>
#include <err.h>
#include <errno.h>
#include <regex.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
@@ -24,8 +25,11 @@
#include "lib/util_libc.h"
#include "lib/util_rec.h"
#include "lib/util_panic.h"
#include "lib/util_prg.h"
#include "kms.h"
#include "utils.h"
#include "pkey.h"
#define ENVVAR_ZKEY_REPOSITORY "ZKEY_REPOSITORY"
#define DEFAULT_KEYSTORE "/etc/zkey/repository"
@@ -37,6 +41,8 @@
#define KMS_CONFIG_PROP_KMS "kms"
#define KMS_CONFIG_PROP_KMS_CONFIG "config"
#define KMS_CONFIG_PROP_APQNS "apqns"
#define KMS_CONFIG_PROP_CCA_APQNS "ep11_apqns"
#define KMS_CONFIG_PROP_EP11_APQNS "cca_apqns"
#define KMS_CONFIG_LOCAL "local"
static const char * const key_types[] = {
@@ -981,3 +987,913 @@ int handle_kms_option(struct kms_info *kms_info, struct util_opt *opt_vec,
return -ENOENT;
}
struct card_info {
enum card_type type;
int min_level;
struct fw_version min_fw_version;
struct kms_apqn *apqns;
size_t num_apqns;
};
/**
* Parses an APQN and checks if it is online.
*
* @param[in] apqn the APQN to parse
* @param[out] kms_apqn the parse APQN is filled in
* @param{in] check if true, the APQN is checked to be online
* @param[in] cards An array of cards types with its requirements
* @param[in] num_cards The number of elements in above array
*
* @returns 0 for success or a negative errno in case of an error.
*/
static int _parse_and_check_apqn(const char *apqn, struct kms_apqn *kms_apqn,
bool check, struct card_info *cards,
size_t num_cards)
{
struct card_info *card_info = NULL;
struct fw_version fw_version;
int rc, card, domain, level;
enum card_type type;
regmatch_t pmatch[1];
regex_t reg_buf;
unsigned int num;
size_t i;
rc = regcomp(&reg_buf, "[[:xdigit:]]+\\.[[:xdigit:]]", REG_EXTENDED);
if (rc != 0)
return -EIO;
rc = regexec(&reg_buf, apqn, (size_t)1, pmatch, 0);
if (rc != 0) {
warnx("The APQN '%s' is not valid", apqn);
rc = -EINVAL;
goto out;
}
if (sscanf(apqn, "%x.%x%n", &card, &domain, &num) != 2 ||
num != strlen(apqn) || card < 0 || card > 0xff ||
domain < 0 || domain > 0xFFFF) {
warnx("The APQN '%s' is not valid", apqn);
rc = -EINVAL;
goto out;
}
kms_apqn->card = card;
kms_apqn->domain = domain;
if (!check) {
rc = 0;
goto out;
}
type = sysfs_get_card_type(card);
if (type == -1) {
warnx("The APQN %02x.%04x is not available or has an "
"unsupported type", card, domain);
rc = -EIO;
goto out;
}
rc = sysfs_is_apqn_online(card, domain, CARD_TYPE_ANY);
if (rc != 1) {
warnx("The APQN %02x.%04x is not available or not online",
card, domain);
rc = -EIO;
goto out;
}
for (i = 0, card_info = NULL; i < num_cards; i++) {
if (cards[i].type == type) {
card_info = &cards[i];
break;
}
}
if (card_info == NULL) {
warnx("APQN %02x.%04x: The card type is not supported by the "
"KMS plugin", card, domain);
rc = -EIO;
goto out;
}
level = sysfs_get_card_level(card);
if (level < card_info->min_level) {
warnx("APQN %02x.%04x: The card level is less than CEX%dn.",
card, domain, card_info->min_level);
rc = -EIO;
goto out;
}
rc = sysfs_get_firmware_version(card, &fw_version, false);
if (rc == 0) {
if (fw_version.api_ordinal <
card_info->min_fw_version.api_ordinal) {
warnx("APQN %02x.%04x: The firmware version is too "
"less", card, domain);
rc = -EIO;
goto out;
}
if (card_info->min_level > 0 && card_info->min_level == level &&
(fw_version.major < card_info->min_fw_version.major ||
(fw_version.major == card_info->min_fw_version.major &&
fw_version.minor < card_info->min_fw_version.minor))) {
warnx("APQN %02x.%04x: The firmware version is too "
"less", card, domain);
rc = -EIO;
goto out;
}
}
rc = 0;
out:
regfree(&reg_buf);
return rc;
}
/**
* Add APQNs to the current APQN association of the KMS plugin
*
* @param[in] kms_info information of the currently bound plugin.
* @param[in] apqns the APQNs specification from --apqns option
* @param[in] cards An array of card types with its requirements
* @param[in] num_cardsqs The number of elements in above array
*
* @returns 0 for success or a negative errno in case of an error.
*/
static int _add_kms_apqns(struct kms_info *kms_info, const char *apqns,
struct card_info *cards, size_t num_cards)
{
struct kms_apqn kms_apqn;
char **new_apqns;
int i, rc = 0;
size_t k;
new_apqns = str_list_split(apqns);
for (i = 0; new_apqns[i] != NULL; i++) {
rc = _parse_and_check_apqn(new_apqns[i], &kms_apqn, true,
cards, num_cards);
if (rc != 0)
goto out;
for (k = 0; k < kms_info->num_apqns; k++) {
if (kms_apqn.card == kms_info->apqns[k].card &&
kms_apqn.domain == kms_info->apqns[k].domain) {
warnx("APQN %02x.%04x is already associated "
"with the KMS plugin", kms_apqn.card,
kms_apqn.domain);
rc = -EEXIST;
goto out;
}
}
ARRAY_ADD(kms_info->apqns, kms_info->num_apqns,
sizeof(struct kms_apqn), &kms_apqn);
}
out:
str_list_free_string_array(new_apqns);
return rc;
}
/**
* Remove APQNs from the current APQN association of the KMS plugin
*
* @param[in] kms_info information of the currently bound plugin.
* @param[in] apqns the APQNs specification from --apqns option
* @param[in] cards An array of card types with its requirements
* @param[in] num_cards The number of elements in above array
*
* @returns 0 for success or a negative errno in case of an error.
*/
static int _remove_kms_apqns(struct kms_info *kms_info, const char *apqns,
struct card_info *cards, size_t num_cards)
{
struct kms_apqn kms_apqn;
char **rem_apqns;
int i, rc = 0;
size_t k;
rem_apqns = str_list_split(apqns);
for (i = 0; rem_apqns[i] != NULL; i++) {
rc = _parse_and_check_apqn(rem_apqns[i], &kms_apqn, false,
cards, num_cards);
if (rc != 0)
goto out;
for (k = 0; k < kms_info->num_apqns; k++) {
if (kms_apqn.card == kms_info->apqns[k].card &&
kms_apqn.domain == kms_info->apqns[k].domain) {
ARRAY_REMOVE(kms_info->apqns,
kms_info->num_apqns,
sizeof(struct kms_apqn), k);
rc = 0;
goto out;
}
}
warnx("APQN %02x.%04x is not associated with the KMS plugin",
kms_apqn.card, kms_apqn.domain);
rc = -ENOENT;
goto out;
}
out:
free(rem_apqns);
return rc;
}
/**
* Set the APQN association of the KMS plugin
*
* @param[in] kms_info information of the currently bound plugin.
* @param[in] apqns the APQNs specification from --apqns option
* @param[in] cards An array of card types with its requirements
* @param[in] num_cards The number of elements in above array
*
* @returns 0 for success or a negative errno in case of an error.
*/
static int _set_kms_apqns(struct kms_info *kms_info, const char *apqns,
struct card_info *cards, size_t num_cards)
{
if (kms_info->apqns != NULL)
free(kms_info->apqns);
kms_info->apqns = NULL;
kms_info->num_apqns = 0;
if (strlen(apqns) == 0) {
/* Indicate empty list specified */
kms_info->apqns = util_malloc(sizeof(struct kms_apqn));
return 0;
}
return _add_kms_apqns(kms_info, apqns, cards, num_cards);
}
/**
* Change the APQN association of the KMS plugin
*
* @param[in] kms_info information of the currently bound plugin.
* @param[in] apqns the APQNs specification from --apqns option
* @param[in] cards An array of card types with its requirements
* @param[in] num_cards The number of elements in above array
*
* @returns 0 for success or a negative errno in case of an error.
*/
static int _change_kms_apqns(struct kms_info *kms_info, const char *apqns,
struct card_info *cards, size_t num_cards)
{
switch (*apqns) {
case '+':
if (kms_info->apqns != NULL && kms_info->num_apqns > 0)
return _add_kms_apqns(kms_info, &apqns[1], cards,
num_cards);
else
return _set_kms_apqns(kms_info, &apqns[1], cards,
num_cards);
case '-':
if (kms_info->apqns != NULL && kms_info->num_apqns > 0)
return _remove_kms_apqns(kms_info, &apqns[1], cards,
num_cards);
warnx("No APQNs are currently associated with the KMS plugin");
return -ENOENT;
default:
return _set_kms_apqns(kms_info, apqns, cards, num_cards);
}
}
/**
* Returns the crypto card requirements per card type base on the key types that
* the KMS plugin supports.
*
* @param[in] kms_info information of the currently bound plugin.
* @param[out] cards on return: An array of card types with its
* requirements. The caller must free this array if
* no longer used.
* @param[out] num_cards on return: The number of elements in above array
* @param[in] verbose if true, verbose messages are printed
*
* @returns the card type
*/
static int _get_supported_card_types(struct kms_info *kms_info,
struct card_info **cards,
size_t *num_cards, bool verbose)
{
struct card_info new_card = { 0 };
const struct fw_version *fw_ver;
struct card_info *req;
enum card_type type;
int i, level;
size_t k;
util_assert(kms_info != NULL, "Internal error: kms_info is NULL");
util_assert(cards != NULL, "Internal error: reqs is NULL");
util_assert(num_cards != NULL, "Internal error: num_reqs is NULL");
*cards = NULL;
*num_cards = 0;
if (kms_info->funcs->kms_supports_key_type == NULL)
return 0;
for (i = 0; key_types[i] != NULL; i++) {
if (kms_info->funcs->kms_supports_key_type(kms_info->handle,
key_types[i])) {
pr_verbose(verbose, "KMS plugin supports key type %s",
key_types[i]);
type = get_card_type_for_keytype(key_types[i]);
level = get_min_card_level_for_keytype(key_types[i]);
fw_ver = get_min_fw_version_for_keytype(key_types[i]);
for (req = NULL, k = 0; k < *num_cards; k++) {
if ((*cards)[k].type == type)
req = &(*cards)[k];
}
if (req == NULL) {
new_card.type = type;
new_card.min_level = level;
if (fw_ver != NULL)
new_card.min_fw_version = *fw_ver;
ARRAY_ADD(*cards, *num_cards,
sizeof(struct card_info),
&new_card);
req = &(*cards)[*num_cards - 1];
}
if (req->min_level < level) {
req->min_level = level;
if (fw_ver != NULL) {
if (req->min_fw_version.api_ordinal <
fw_ver->api_ordinal ||
req->min_fw_version.major <
fw_ver->major ||
(req->min_fw_version.major ==
fw_ver->major &&
req->min_fw_version.minor <
fw_ver->minor))
req->min_fw_version = *fw_ver;
}
}
pr_verbose(verbose, "Card type: %d", req->type);
pr_verbose(verbose, "Card level: %d", req->min_level);
pr_verbose(verbose, "Fw version: %d.%d (API: %d)",
req->min_fw_version.major,
req->min_fw_version.minor,
req->min_fw_version.api_ordinal);
}
}
pr_verbose(verbose, "%lu card type are supported", *num_cards);
return 0;
}
/**
* Builds an APQN list per card type.
*
* @param[in] kms_info information of the currently bound plugin.
* @param[in] cards An array of card types to build APQNs lists for
* @param[in] num_cards The number of elements in above array
*
* @returns 0 for success or a negative errno in case of an error.
*/
static int _get_apqns_per_card_type(struct kms_info *kms_info,
struct card_info *cards, size_t num_cards)
{
enum card_type type;
size_t i, k;
bool found;
for (i = 0; i < kms_info->num_apqns; i++) {
if (!sysfs_is_apqn_online(kms_info->apqns[i].card,
kms_info->apqns[i].domain, 0)) {
warnx("The APQN %02x.%04x is not online",
kms_info->apqns[i].card,
kms_info->apqns[i].domain);
return -EIO;
}
type = sysfs_get_card_type(kms_info->apqns[i].card);
for (k = 0, found = false; k < num_cards; k++) {
if (cards[k].type == type) {
ARRAY_ADD(cards[k].apqns, cards[k].num_apqns,
sizeof(struct kms_apqn),
&kms_info->apqns[i]);
found = true;
}
}
if (!found) {
warnx("The APQN %02x.%04x is not available of has an "
"unsupported type", kms_info->apqns[i].card,
kms_info->apqns[i].domain);
return -EIO;
}
}
return 0;
}
/**
* Build an APQN string from an APQN array
*
* @param[in] apqns An array of APQNs
* @param[in] num_apqns The number of elements in above array
*
* @return an allocated string with the APQNs
*/
static char *_build_apqn_string(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;
}
/**
* Update the APQNS properties in the KMS properties to reflect the list of
* APQNs contained in kms_info and supported card types.
*
* @param[in] kms_info information of the currently bound plugin.
* @param[in] cards An array of card types supported
* @param[in] num_cards The number of elements in above array
* @param[in] verbose if true, verbose messages are printed
*
* @returns 0 for success or a negative errno in case of an error.
*/
static int _update_apqns_properties(struct kms_info *kms_info,
struct card_info *cards, size_t num_cards,
bool verbose)
{
char *apqns = NULL;
char *prop;
size_t i;
int rc;
rc = properties_remove(kms_info->props,
KMS_CONFIG_PROP_CCA_APQNS);
if (rc != 0 && rc != -ENOENT) {
pr_verbose(verbose, "Failed to remove the APQNS "
"property: %s", strerror(-rc));
return rc;
}
rc = properties_remove(kms_info->props,
KMS_CONFIG_PROP_EP11_APQNS);
if (rc != 0 && rc != -ENOENT) {
pr_verbose(verbose, "Failed to remove the APQNS "
"property: %s", strerror(-rc));
return rc;
}
if (kms_info->num_apqns == 0) {
rc = properties_remove(kms_info->props, KMS_CONFIG_PROP_APQNS);
if (rc != 0 && rc != -ENOENT) {
pr_verbose(verbose, "Failed to remove the APQNS "
"property: %s", strerror(-rc));
return rc;
}
pr_verbose(verbose, "APQNs: none");
return 0;
}
apqns = _build_apqn_string(kms_info->apqns, kms_info->num_apqns);
pr_verbose(verbose, "APQNs: '%s'", apqns);
rc = properties_set(kms_info->props, KMS_CONFIG_PROP_APQNS, apqns);
free(apqns);
if (rc != 0) {
pr_verbose(verbose, "Failed to set the APQNS property: %s",
strerror(-rc));
goto out;
}
for (i = 0; i < num_cards; i++) {
switch (cards[i].type) {
case CARD_TYPE_CCA:
prop = KMS_CONFIG_PROP_CCA_APQNS;
break;
case CARD_TYPE_EP11:
prop = KMS_CONFIG_PROP_EP11_APQNS;
break;
default:
continue;
}
apqns = _build_apqn_string(cards[i].apqns, cards[i].num_apqns);
pr_verbose(verbose, "%s: '%s'", prop, apqns);
rc = properties_set(kms_info->props, prop, apqns);
free(apqns);
if (rc != 0) {
pr_verbose(verbose, "Failed to set the %s property: %s",
prop, strerror(-rc));
goto out;
}
}
out:
return rc;
}
/**
* Cross checks the APQNs per card type
*
* @param[in] kms_info information of the currently bound plugin.
* @param[in] cards An array of card types to build APQNs lists for
* @param[in] num_cards The number of elements in above array
* @param[in] verbose if true, verbose messages are printed
*
* @returns 0 for success or a negative errno in case of an error.
*/
static int _cross_check_apqns(struct card_info *cards, size_t num_cards,
bool verbose)
{
char *apqns;
size_t i;
int rc;
for (i = 0; i < num_cards; i++) {
if (cards[i].num_apqns == 0)
continue;
apqns = _build_apqn_string(cards[i].apqns, cards[i].num_apqns);
rc = cross_check_apqns(apqns, NULL, cards[i].min_level,
&cards[i].min_fw_version, cards[i].type,
true, verbose);
free(apqns);
if (rc == -ENOTSUP)
continue;
if (rc != 0)
return rc;
}
return 0;
}
/**
* Checks existing KMS-bound keys in the keystore of the new set of APQNs
* would make them unusable. The user is prompted if so.
*
* @param[in] keystore the keystore
* @param[in] cards An array of card types
* @param[in] num_cards The number of elements in above array
*
* @returns 0 for success or a negative errno in case of an error.
*/
static int _check_keystore_keys(struct keystore *keystore,
struct card_info *cards, size_t num_cards)
{
struct kms_info *kms_info = keystore->kms_info;
struct card_info *card = NULL;
enum card_type type;
size_t i, k;
char *msg;
int rc;
for (i = 0; key_types[i] != NULL; i++) {
type = get_card_type_for_keytype(key_types[i]);
if (type <= 0)
continue;
for (k = 0; k < num_cards; k++) {
if (cards[k].type == type) {
card = &cards[k];
break;
}
}
if (card == NULL)
continue;
pr_verbose(keystore->verbose, "Checking repository for "
"KMS-bound keys of type %s", key_types[i]);
if (card->num_apqns > 0) {
pr_verbose(keystore->verbose, "Set of APQNs for key "
"type %s is not empty", key_types[i]);
continue;
}
util_asprintf(&msg, "The following keys of type '%s' are bound "
"to KMS plugin '%s', and may become unusable "
"with this set of APQNs:", key_types[i],
kms_info->plugin_name);
rc = keystore_msg_for_kms_key(keystore, key_types[i], msg);
free(msg);
if (rc == -ENOENT) {
pr_verbose(keystore->verbose, "No keys of type %s "
"found", key_types[i]);
continue;
}
if (rc != 0)
return rc;
printf("Do you want to continue [y/N]? ");
if (!prompt_for_yes(keystore->verbose)) {
warnx("Operation aborted");
return -ECANCELED;
}
}
return 0;
}
/**
* Updates existing KMS-bound keys in the keystore with the set of APQNs that
* match the key type.
*
* @param[in] keystore the keystore
* @param[in] cards An array of card types
* @param[in] num_cards The number of elements in above array
*
* @returns 0 for success or a negative errno in case of an error.
*/
static int _update_keystore_keys(struct keystore *keystore,
struct card_info *cards, size_t num_cards)
{
struct card_info *card = NULL;
enum card_type type;
size_t i, k;
char *apqns;
int rc;
for (i = 0; key_types[i] != NULL; i++) {
type = get_card_type_for_keytype(key_types[i]);
if (type <= 0)
continue;
for (k = 0; k < num_cards; k++) {
if (cards[k].type == type) {
card = &cards[k];
break;
}
}
if (card == NULL)
continue;
apqns = _build_apqn_string(card->apqns, card->num_apqns);
pr_verbose(keystore->verbose, "Changing KMS-bound keys of "
"type %s to: '%s'", key_types[i], apqns);
rc = keystore_kms_keys_set_property(keystore, key_types[i],
PROP_NAME_APQNS, apqns);
free(apqns);
if (rc != 0) {
warnx("Failed to update APQNs for KMS-bound keys of "
"type %s: %s", key_types[i], strerror(-rc));
return rc;
}
}
return 0;
}
/**
* Performs configuration of the KMS plugin.
*
* @param[in] keystore the keystore
* @param[in] apqns the APQNs specification from --apqns option, or
* NULL if the option was not specified
* @param[in] kms_options an array of KMS options specified, or NULL if no
* KMS options have been specified
* @param[in] num_kms_options the number of options in above array
* @param[in] has_plugin_optins if true, then the KMS plugin uses KMS specific
* options, false of no options are supported
* @param[in] verbose if true, verbose messages are printed
*
* @returns 0 for success or a negative errno in case of an error.
* -EAGAIN to indicate that the specified configuration was accepted so far, but
* the configuration is still incomplete, and needs to be completed.
*/
int configure_kms_plugin(struct keystore *keystore, const char *apqns,
struct kms_option *kms_options, size_t num_kms_options,
bool has_plugin_optins, bool verbose)
{
struct card_info *cards = NULL;
struct kms_info *kms_info;
bool incomplete = false;
size_t i, num_cards = 0;
int rc = 0;
util_assert(keystore != NULL, "Internal error: keystore is NULL");
kms_info = keystore->kms_info;
if (kms_info->plugin_lib == NULL) {
rc = -ENOENT;
warnx("The repository is not bound to a KMS plugin");
goto out;
}
if (apqns == NULL) {
if (kms_info->apqns == NULL || kms_info->num_apqns == 0) {
warnx("Option '--apqns|-a' is required when no APQNS "
"are associated with the KMS plugin");
util_prg_print_parse_error();
rc = -EINVAL;
goto out;
}
if (has_plugin_optins && num_kms_options == 0) {
warnx("At least one option is required");
util_prg_print_parse_error();
rc = -EINVAL;
goto out;
}
}
if (apqns != NULL) {
rc = _get_supported_card_types(kms_info, &cards, &num_cards,
verbose);
if (rc != 0)
goto out;
if (num_cards == 0) {
warnx("The KMS plugin does not support any key type");
rc = -EIO;
goto out;
}
rc = _change_kms_apqns(kms_info, apqns, cards, num_cards);
if (rc != 0)
goto out;
rc = _get_apqns_per_card_type(kms_info, cards, num_cards);
if (rc != 0)
goto out;
rc = _cross_check_apqns(cards, num_cards, verbose);
if (rc != 0)
goto out;
rc = _check_keystore_keys(keystore, cards, num_cards);
if (rc != 0)
goto out;
rc = _update_apqns_properties(kms_info, cards, num_cards,
verbose);
if (rc != 0) {
warnx("Failed to update the APQNs: %s", strerror(-rc));
goto out;
}
}
if (kms_info->funcs->kms_configure != NULL) {
rc = kms_info->funcs->kms_configure(kms_info->handle,
apqns != NULL ?
kms_info->apqns : NULL,
apqns != NULL ?
kms_info->num_apqns : 0,
kms_options,
num_kms_options);
if (rc == -EAGAIN) {
incomplete = true;
rc = 0;
}
if (rc != 0) {
warnx("Failed to configure the KMS plugin: '%s'",
strerror(-rc));
print_last_kms_error(kms_info);
goto out;
}
}
if (apqns != NULL) {
rc = _save_kms_properties(keystore, kms_info->props, verbose);
if (rc != 0)
goto out;
rc = _update_keystore_keys(keystore, cards, num_cards);
if (rc != 0)
goto out;
if (kms_info->num_apqns == 0)
incomplete = true;
}
out:
if (cards != NULL) {
for (i = 0; i < num_cards; i++)
if (cards[i].apqns != NULL)
free(cards[i].apqns);
free(cards);
}
if (rc == 0 && incomplete)
rc = -EAGAIN;
return rc;
}
/**
* Performs re-enciphering of secure keys internally used by the KMS plugin
*
* @param[in] kms_info information of the currently bound plugin.
* @param[in] from_old If true the keys are reenciphered from the OLD to
* the CURRENT master key.
* @param[in] to_new If true the keys are reenciphered from the CURRENT
* to the OLD master key.
* @param[in] inplace if true, the key will be re-enciphere in-place
* @param[in] staged if true, the key will be re-enciphere not in-place
* @param[in] complete if true, a pending re-encipherment is completed
* @param[in] kms_options an array of KMS options specified, or NULL if no
* KMS options have been specified
* @param[in] num_kms_options the number of options in above array
* @param[in] verbose if true, verbose messages are printed
*
* @returns 0 for success or a negative errno in case of an error.
* -EAGAIN to indicate that the specified configuration was accepted so far, but
* the configuration is still incomplete, and needs to be completed.
*/
int reencipher_kms(struct kms_info *kms_info, bool from_old, bool to_new,
bool inplace, bool staged, bool complete,
struct kms_option *kms_options, size_t num_kms_options,
bool verbose)
{
enum kms_reencipher_mode mode = KMS_REENC_MODE_AUTO;
enum kms_reenc_mkreg mkreg = KMS_REENC_MKREG_AUTO;
int rc = 0;
util_assert(kms_info != NULL, "Internal error: kms_info is NULL");
if (kms_info->plugin_lib == NULL) {
rc = -ENOENT;
warnx("The repository is not bound to a KMS plugin");
goto out;
}
if (kms_info->funcs->kms_reenciper == NULL) {
pr_verbose(verbose, "The KMS plugin does not support "
"reencipher");
goto out;
}
if (inplace)
mode = KMS_REENC_MODE_IN_PLACE;
else if (staged)
mode = KMS_REENC_MODE_STAGED;
else if (complete)
mode = KMS_REENC_MODE_STAGED_COMPLETE;
if (from_old && !to_new) {
mkreg = KMS_REENC_MKREG_FROM_OLD;
if (mode == KMS_REENC_MODE_AUTO)
mode = KMS_REENC_MODE_IN_PLACE;
} else if (to_new && !from_old) {
mkreg = KMS_REENC_MKREG_TO_NEW;
if (mode == KMS_REENC_MODE_AUTO)
mode = KMS_REENC_MODE_STAGED;
} else if (from_old && to_new) {
mkreg = KMS_REENC_MKREG_FROM_OLD_TO_NEW;
if (mode == KMS_REENC_MODE_AUTO)
mode = KMS_REENC_MODE_STAGED;
}
rc = kms_info->funcs->kms_reenciper(kms_info->handle, mode, mkreg,
kms_options, num_kms_options);
if (rc != 0) {
warnx("Failed to reencipher KMS plugin internal keys: %s",
strerror(-rc));
print_last_kms_error(kms_info);
goto out;
}
out:
return rc;
}

View File

@@ -57,4 +57,13 @@ int handle_kms_option(struct kms_info *kms_info, struct util_opt *opt_vec,
const char *optarg, struct kms_option **kms_options,
size_t *num_kms_options, bool verbose);
int configure_kms_plugin(struct keystore *keystore, const char *apqns,
struct kms_option *kms_options, size_t num_kms_options,
bool has_plugin_optins, bool verbose);
int reencipher_kms(struct kms_info *kms_info, bool from_old, bool to_new,
bool inplace, bool staged, bool complete,
struct kms_option *kms_options, size_t num_kms_options,
bool verbose);
#endif

View File

@@ -860,6 +860,137 @@ Use the
command to display information about the currently bound key management system
plugin (KMS plugin) and its configuration.
.
.SS "Configure or re\-configure a key management system plugin"
.
.B zkey kms
.BR configure | con
.RB [ \-\-apqns | \-a
.IR [+|-]card1.domain1[,card2.domain2[,...]] ]
.RB [ KMS\-plugin\ specific\ options ]
.RB [ \-\-verbose | \-V ]
.
.PP
Use the
.B kms configure
command to configure or re\-configure the currently bound key management system
plugin (KMS plugin). As a minimum, you must associate APQNs with the key
management system plugin. The plugin may require an initial configuration
before it is fully functioning. Once configured, a plugin may allow you to
change some configuration settings.
.PP
A key management system plugin may offer plugin specific options that can be
specified with the \fBkms configure\fP command. Use \fBkms configure
\-\-help\fP to display the plugin specific options and their meaning.
.PP
Configuring a key management system plugin may be a multi-step task. You can
supply all configuration options at once or use the \fBkms configure\fP
command several times supplying only one or a few configuration options each
time, dependent on what the plugin supports. A plugin may also require to
perform additional intermediate steps between two configuration attempts,
e.g. perform tasks in the key management system user interface, or elsewhere.
.PP
Use the \fBkms info\fP command to display information about the key management
system plugin and its current configuration.
.
.SS "Re-encipher secure keys used by a key management system plugin"
.
.B zkey kms
.BR reencipher | re
.RB [ \-\-to\-new | \-n ]
.RB [ \-\-from\-old | \-o ]
.RB [ \-\-in-place | \-i ]
.RB [ \-\-staged | \-s ]
.RB [ \-\-complete | \-c ]
.RB [ KMS\-plugin\ specific\ options ]
.RB [ \-\-verbose | \-V ]
.
.PP
Use the
.B kms reencipher
command to re-encipher secure keys internally used by the currently bound
key management system plugin (KMS plugin) with a new master key. This command
must be run when the master keys of the CCA or EP11 cryptographic adapter,
that are associated with the plugin have been changed. Dependent on what key
types the key management system plugin supports, it may internally use secure
keys with CCA or EP11 cryptographic adapters, or both.
.PP
A key management system plugin may offer plugin specific options that can be
specified with the \fBkms reencipher\fP command. Use \fBkms reencipher
\-\-help\fP to display the plugin specific options and their meaning.
.PP
.B Note:
The \fBkms reencipher\fP command does \fBnot\fP re-encipher secure keys that
have been generated by or have been imported from the key management system
plugin and are stored in the repository. Use the regular \fBreencipher\fP
command to re-encipher those secure keys.
.PP
The CCA cryptographic adapter has three different registers to store
master keys:
.RS 2
.IP "\(bu" 2
The \fBCURRENT\fP register contains the current master key.
.
.IP "\(bu" 2
The \fBOLD\fP register contains the previously used master key.
Secure keys enciphered with the master key contained in the \fBOLD\fP
register can still be used until the master key is changed again.
.
.IP "\(bu" 2
The \fBNEW\fP register contains the new master key to be set.
The master key in the \fBNEW\fP register cannot be used until it is made
the current master key. You can pro-actively re-encipher a secure key with the
\fBNEW\fP master key before this key is made the \fBCURRENT\fP key. Use the
.B \-\-to-new
option to do this.
.RE
.PP
\fBNote:\fP An EP11 cryptographic adapter has only two registers to store master
keys, \fBCURRENT\fP and \fBNEW\fP.
.PP
Use the
.B \-\-from\-old
option to re-encipher a secure key that is currently enciphered with
the master key in the \fBOLD\fP register with the master key in the
\fBCURRENT\fP register. This option is only available for CCA-type secure keys.
.PP
.PP
If both the
.B \-\-from-old
and
.B \-\-to-new
options are specified, a secure key that is currently enciphered
with the master key in the \fBOLD\fP register is re-enciphered with the
master key in the \fBNEW\fP register.
.PP
If both options are omitted, the key management system plugin may
automatically detect whether the secure key is currently enciphered with the
master key in the \fBOLD\fP register or with the master key in the \fBCURRENT\fP
register. If currently enciphered with the master key in the \fBOLD\fP register,
it is re-enciphered with the master key in the \fBCURRENT\fP register.
If currently enciphered with the master key in the \fBCURRENT\fP
register, it is re-enciphered with the master key in the \fBNEW\fP register.
If for this case the \fBNEW\fP register does not contain a valid master key,
then the re-encipher operation fails.
.PP
Re-enciphering a secure key used by a key management system plugin can be
performed \fBin-place\fP, or in \fBstaged\fP mode.
.PP
\fB"In-place"\fP immediately replaces the secure key with the re-enciphered
secure key. Re-enciphering from \fBOLD\fP to \fBCURRENT\fP is performed
in-place per default. You can use option \fB\-\-in-place\fP to force an
in-place re-enciphering for the \fBCURRENT\fP to \fBNEW\fP case.
A secure key that was re-enciphered in-place from \fBCURRENT\fP to \fBNEW\fP
is no longer valid, until the new CCA or EP11 master key has been made the
current one.
.PP
\fBStaged\fP mode means that the re-enciphered secure key is stored separately.
Thus the current secure key is still valid at this point. Once the new CCA or
EP11 master key has been set (made active), you must rerun the \fBkms
reencipher\fP command with option \fB\-\-complete\fP to complete the staged
re-enciphering. Re-enciphering from \fBCURRENT\fP to \fBNEW\fP is performed in
staged mode per default. You can use option \fB\-\-staged\fP to force a staged
re-enciphering for the \fBOLD\fP to \fBCURRENT\fP case.
.
.
.
.SH OPTIONS
@@ -1426,6 +1557,68 @@ to convert a secure key without prompting for a confirmation.
.
.
.
.SS "Options for the kms configure command"
.TP
.BR \-a ", " \-\-apqns\~\fI[+|-]card1.domain1[,card2.domain2[,...]]\fP
Specifies a comma-separated list of cryptographic adapters in CCA or EP11
coprocessor mode (APQN) which are associated with the key management system
plugin. Each APQN association specifies a card and domain number separated
by a period (like lszcrypt displays it).
To add an APQN to the associated APQNs, prefix the APQN with a \fI+\fP.
To remove an APQN from the associated APQNs, prefix the APQN with a \fI-\fP.
To set (replace) the APQN association do not specify a prefix.
You cannot mix \fI+\fP and \fI-\fP in one specification. You can either add or
remove (or set) the associations with one command.
All APQNs being added or set (replaced) must be online.
.TP
.B KMS-plugin specific options
A key management system plugin may offer plugin specific options that can be
specified with the kms configure command. Use \fBkms configure \-\-help\fP to
display the plugin specific options and their meaning.
.
.
.
.SS "Options for the kms reencipher command"
.TP
.BR \-n ", " \-\-to\-new
Re-enciphers key management system plugin internal secure keys that are
currently enciphered with the master key in the CURRENT register with the
master key in the NEW register.
.TP
.BR \-o ", " \-\-from\-old
Re-enciphers key management system plugin internal secure keys that are
currently enciphered with the master key in the OLD register with the master
key in the CURRENT register.
This option is only available for CCA-type secure keys.
.TP
.BR \-i ", " \-\-in-place
Forces an in-place re-enciphering of key management system plugin internal
secure keys. "In-place" immediately replaces the secure key with the
re-enciphered secure key. Re-enciphering from OLD to CURRENT is performed
in-place per default.
.TP
.BR \-s ", " \-\-staged
Forces that the re-enciphering of key management system plugin internal secure
keys is performed in staged mode. Staged mode means that the re-enciphered
secure keys are stored separately. Thus the current secure keys are still valid
at this point. Once the new CCA or EP11 master keys have been set (made active),
you must rerun the \fBkms reencipher\fP command with option \fB\-\-complete\fP
to complete the staged re-enciphering.
Re-enciphering from CURRENT to NEW is performed in staged mode per default.
.TP
.BR \-p ", " \-\-complete
Completes a staged re-enciphering. Use this option after the new CCA or EP11
master keys have been set (made active). This option replaces the secure keys by
their re-enciphered versions.
.TP
.B KMS-plugin specific options
A key management system plugin may offer plugin specific options that can be
specified with the kms reencipher command. Use \fBkms reencipher \-\-help\fP to
display the plugin specific options and their meaning.
.
.
.
.SS "General options"
.TP
.BR \-V ", " \-\-verbose

View File

@@ -124,6 +124,8 @@ static struct zkey_globals {
#define COMMAND_KMS_BIND "bind"
#define COMMAND_KMS_UNBIND "unbind"
#define COMMAND_KMS_INFO "info"
#define COMMAND_KMS_CONFIGURE "configure"
#define COMMAND_KMS_REENCIPHER "reencipher"
#define OPT_COMMAND_PLACEHOLDER "PLACEHOLDER"
@@ -836,6 +838,67 @@ static struct util_opt opt_vec[] = {
.command = COMMAND_CONVERT,
},
/***********************************************************/
{
.flags = UTIL_OPT_FLAG_SECTION,
.desc = "OPTIONS",
.command = COMMAND_KMS " " COMMAND_KMS_CONFIGURE,
},
{
.option = { "apqns", required_argument, NULL, 'a'},
.argument = "[+|-]CARD.DOMAIN[,...]",
.desc = "Comma-separated pairs of crypto cards and domains "
"that are associated with the key management system "
"(KMS) plugin that the repository is bound to, and all "
"keys generated with the KMS. To add pairs of crypto "
"cards and domains to the key, specify "
"'+CARD.DOMAIN[,...]'. To remove pairs of crypto cards "
"and domains from the key specify '-CARD.DOMAIN[,...]'",
.command = COMMAND_KMS " " COMMAND_KMS_CONFIGURE,
},
/***********************************************************/
{
.flags = UTIL_OPT_FLAG_SECTION,
.desc = "OPTIONS",
.command = COMMAND_KMS " " COMMAND_KMS_REENCIPHER,
},
{
.option = {"to-new", 0, NULL, 'n'},
.desc = "Re-enciphers KMS plugin internal secure keys that are "
"currently enciphered with the master key in the "
"CURRENT register with the master key in the NEW "
"register.",
.command = COMMAND_KMS " " COMMAND_KMS_REENCIPHER,
},
{
.option = {"from-old", 0, NULL, 'o'},
.desc = "Re-enciphers KMS plugin internal secure keys that are "
"currently enciphered with the master key in the OLD "
"register with the master key in the CURRENT register.",
.command = COMMAND_KMS " " COMMAND_KMS_REENCIPHER,
},
{
.option = {"complete", 0, NULL, 'p'},
.desc = "Completes a staged re-enciphering. Use this option "
"after the new master key has been set (made "
"active).",
.command = COMMAND_KMS " " COMMAND_KMS_REENCIPHER,
},
{
.option = {"in-place", 0, NULL, 'i'},
.desc = "Forces an in-place re-enchipering of the KMS plugin "
"internal secure keys. Re-enciphering from OLD to "
"CURRENT is performed in-place per default.",
.command = COMMAND_KMS " " COMMAND_KMS_REENCIPHER,
},
{
.option = {"staged", 0, NULL, 's'},
.desc = "Forces that the re-enciphering of the KMS plugin "
"internal secure keys is performed in staged mode. "
"Re-enciphering from CURRENT to NEW is performed in "
"staged mode per default.",
.command = COMMAND_KMS " " COMMAND_KMS_REENCIPHER,
},
/***********************************************************/
OPT_PLACEHOLDER,
OPT_PLACEHOLDER,
OPT_PLACEHOLDER,
@@ -933,6 +996,8 @@ static int command_kms_plugins(void);
static int command_kms_bind(void);
static int command_kms_unbind(void);
static int command_kms_info(void);
static int command_kms_configure(void);
static int command_kms_reencipher(void);
static struct zkey_command zkey_kms_commands[] = {
{
@@ -981,6 +1046,31 @@ static struct zkey_command zkey_kms_commands[] = {
.has_options = 1,
.use_kms_plugin = 1,
},
{
.command = COMMAND_KMS_CONFIGURE,
.abbrev_len = 3,
.function = command_kms_configure,
.short_desc = "Configures a key management system plugin",
.long_desc = "Configures or re-configures the current key "
"management system (KMS) plugin",
.need_keystore = 1,
.has_options = 1,
.use_kms_plugin = 1,
.kms_plugin_opts_cmd = KMS_COMMAND_CONFIGURE,
},
{
.command = COMMAND_KMS_REENCIPHER,
.abbrev_len = 2,
.function = command_kms_reencipher,
.short_desc = "Re-enciphers secure keys used by a key "
"management system plugin",
.long_desc = "Re-enciphers secure keys internally used by a "
"key management system (KMS) plugin",
.need_keystore = 1,
.has_options = 1,
.use_kms_plugin = 1,
.kms_plugin_opts_cmd = KMS_COMMAND_REENCIPHER,
},
{ .command = NULL }
};
@@ -2217,6 +2307,82 @@ static int command_kms_info(void)
return rc != 0 ? EXIT_FAILURE : EXIT_SUCCESS;
}
/*
* Command handler for 'kms configure'.
*
* Configures or re-configures a KMS plugin
*/
static int command_kms_configure(void)
{
int rc;
if (g.kms_info.plugin_lib == NULL) {
rc = -ENOENT;
warnx("The repository is not bound to a KMS plugin");
return EXIT_FAILURE;
}
rc = configure_kms_plugin(g.keystore, g.apqns, g.kms_options,
g.num_kms_options, g.first_kms_option >= 0,
g.verbose);
if (rc == -EAGAIN) {
util_print_indented("The KMS plugin has accepted the "
"configuration so far, but requires further"
" configuration. Run 'zkey kms info' to "
"find out which KMS plugin settings still "
"require configuration, and run 'zkey kms "
"configure' again with the appropriate "
"options to complete the KMS "
"configuration process", 0);
rc = 0;
}
return rc != 0 ? EXIT_FAILURE : EXIT_SUCCESS;
}
/*
* Command handler for 'kms reencipher'.
*
* Reenciphers secure keys internally used by a KMS plugin
*/
static int command_kms_reencipher(void)
{
int rc;
if (g.kms_info.plugin_lib == NULL) {
rc = -ENOENT;
warnx("The repository is not bound to a KMS plugin");
return EXIT_FAILURE;
}
if (g.inplace && g.staged) {
warnx("Either '--in-place|-i' or '--staged|-s' can be "
"specified, but not both");
util_prg_print_parse_error();
return EXIT_FAILURE;
}
if (g.complete) {
if (g.inplace) {
warnx("Option '--in-place|-i' is not valid together "
"with '--complete|-p'");
util_prg_print_parse_error();
return EXIT_FAILURE;
}
if (g.staged) {
warnx("Option '--staged|-s' is not valid together "
"with '--complete|-p'");
util_prg_print_parse_error();
return EXIT_FAILURE;
}
}
rc = reencipher_kms(&g.kms_info, g.fromold, g.tonew, g.inplace,
g.staged, g.complete, g.kms_options,
g.num_kms_options, g.verbose);
return rc != 0 ? EXIT_FAILURE : EXIT_SUCCESS;
}
/**
* Opens the keystore. The keystore directory is either the
* default directory or as specified in an environment variable