diff --git a/zkey/Makefile b/zkey/Makefile index 78602338..71ad46d8 100644 --- a/zkey/Makefile +++ b/zkey/Makefile @@ -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) diff --git a/zkey/kms.c b/zkey/kms.c index 214d2c08..d345175a 100644 --- a/zkey/kms.c +++ b/zkey/kms.c @@ -11,6 +11,7 @@ #include #include #include +#include #include #include #include @@ -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(®_buf, "[[:xdigit:]]+\\.[[:xdigit:]]", REG_EXTENDED); + if (rc != 0) + return -EIO; + + rc = regexec(®_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(®_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; +} diff --git a/zkey/kms.h b/zkey/kms.h index 5a2ab1c0..04f0628d 100644 --- a/zkey/kms.h +++ b/zkey/kms.h @@ -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 diff --git a/zkey/zkey.1 b/zkey/zkey.1 index d96a4070..5dfa99e4 100644 --- a/zkey/zkey.1 +++ b/zkey/zkey.1 @@ -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 diff --git a/zkey/zkey.c b/zkey/zkey.c index ab1ce5ac..4cb65eb8 100644 --- a/zkey/zkey.c +++ b/zkey/zkey.c @@ -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