diff --git a/zkey/keystore.c b/zkey/keystore.c index f0e3fe69..a0566ce4 100644 --- a/zkey/keystore.c +++ b/zkey/keystore.c @@ -1200,6 +1200,7 @@ struct volume_check { const char *name; const char *volume; bool set; + bool nocheck; }; /** @@ -1289,7 +1290,7 @@ static int _keystore_volume_check(const char *volume, bool remove, bool set, goto out; } - if (remove) { + if (remove || info->nocheck) { rc = 0; goto out; } @@ -1602,6 +1603,8 @@ static int _keystore_set_default_properties(struct properties *key_props) * key (optional, can be NULL) * @param[in] noapqncheck if true, the specified APQN(s) are not checked for * existence and type. + * @param[i] novolscheck if true, the specified Volume(s) are not checked for + * existence or duplicate use * @param[in] sector_size the sector size to use with dm-crypt. It must be power * of two and in range 512 - 4096 bytes. 0 means that * the sector size is not specified and the system @@ -1615,7 +1618,7 @@ static int _keystore_create_info_props(struct keystore *keystore, const char *name, const char *description, const char *volumes, const char *apqns, - bool noapqncheck, + bool noapqncheck, bool novolcheck, size_t sector_size, const char *volume_type, const char *key_type, @@ -1623,7 +1626,7 @@ static int _keystore_create_info_props(struct keystore *keystore, struct properties **props) { struct volume_check vol_check = { .keystore = keystore, .name = name, - .set = 0 }; + .set = 0, .nocheck = novolcheck }; struct apqn_check apqn_check = { .noonlinecheck = noapqncheck, .nomsg = 0, .cardtype = get_card_type_for_keytype( @@ -1747,7 +1750,7 @@ static int _keystore_create_info_file(struct keystore *keystore, int rc; rc = _keystore_create_info_props(keystore, name, description, volumes, - apqns, noapqncheck, sector_size, + apqns, noapqncheck, false, sector_size, volume_type, key_type, kms, &key_props); if (rc != 0) @@ -1989,9 +1992,9 @@ int keystore_generate_key_kms(struct keystore *keystore, const char *name, pr_verbose(keystore, "APQNs for keytype %s: '%s'", key_type, apqns); rc = _keystore_create_info_props(keystore, name, description, volumes, - apqns, false, sector_size, volume_type, - key_type, kms_info->plugin_name, - &key_props); + apqns, false, false, sector_size, + volume_type, key_type, + kms_info->plugin_name, &key_props); if (rc != 0) goto out_free_key_filenames; @@ -2261,7 +2264,7 @@ int keystore_change_key(struct keystore *keystore, const char *name, long int sector_size, const char *volume_type) { struct volume_check vol_check = { .keystore = keystore, .name = name, - .set = 0 }; + .set = 0, .nocheck = 0 }; struct apqn_check apqn_check = { .noonlinecheck = noapqncheck, .nomsg = 0 }; struct key_filenames file_names = { NULL, NULL, NULL }; @@ -3427,8 +3430,8 @@ int keystore_reencipher_key(struct keystore *keystore, const char *name_filter, int keystore_copy_key(struct keystore *keystore, const char *name, const char *newname, const char *volumes, bool local) { - struct volume_check vol_check = { .keystore = keystore, - .name = newname, .set = 0 }; + struct volume_check vol_check = { .keystore = keystore, .name = newname, + .set = 0, .nocheck = 0 }; struct key_filenames file_names = { NULL, NULL, NULL }; struct key_filenames new_names = { NULL, NULL, NULL }; struct properties *key_prop = NULL; @@ -4887,6 +4890,341 @@ int keystore_msg_for_kms_key(struct keystore *keystore, const char *key_type, return msg_for_key.num_keys == 0 ? -ENOENT : 0; } +struct kms_import { + struct keystore *keystore; + bool batch_mode; + bool novolcheck; + unsigned long num_imported; + unsigned long num_skipped; + unsigned long num_failed; +}; + +/** + * Callback used with the keystore_import_kms_keys() function. Called for each + * key. + * + * @param[in] key1_id the key-ID of the key (1st key of an XTS key) + * @param[in] key1_label the label of the key (1st key of an XTS key) + * @param[in] key2_id the key-ID of the 2nd XTS key, NULL if not XTS + * @param[in] key2_label the label of the 2nd XTS key, NULL if not XTS + * @param[in] xts if true, this is an XTS key pair + * @param[in] name the zkey name of the key + * @param[in] key_type the type of the key (CCA-AESDATA, etc) + * @param[in] key_bits the key size in bits + * @param[in] description the description of the key (can be NULL) + * @param[in] cipher the cipher of the key (can be NULL) + * @param[in] iv_mode the IV-mode of the key (can be NULL) + * @param[in] volumes the associated volumes of the key (can be NULL) + * @param[in] volume_type the volume type of the volume (can be NULL) + * @param[in] sector_size the sector size of the volume (0 means default) + * @param[in] addl_info_argz an argz string containing additional KMS plugin + * specific infos to be displayed, or NULL if none. + * @param[in] addl_info_len length of the argz string in addl_info_argz + * @param[in] private_data the private data pointer + * + * @returns 0 on success, or a negative errno in case of an error. + */ +static int _keystore_process_kms_import(const char *key1_id, + const char *key1_label, + const char *key2_id, + const char *key2_label, + bool xts, const char *name, + const char *UNUSED(key_type), + size_t UNUSED(key_bits), + const char *description, + const char *UNUSED(cipher), + const char *UNUSED(iv_mode), + const char *volumes, + const char *volume_type, + size_t sector_size, + const char *UNUSED(addl_info_argz), + size_t UNUSED(addl_info_len), + void *private_data) +{ + struct key_filenames file_names = { NULL, NULL, NULL }; + struct kms_import *import_data = private_data; + u8 secure_key[2 * MAX_SECURE_KEY_SIZE]; + struct properties *key_props = NULL; + char vp[VERIFICATION_PATTERN_LEN]; + const char *key_name = name; + struct keystore *keystore; + size_t alt_name_len = 0; + size_t secure_key_size; + bool fatal_err = false; + char *alt_name = NULL; + const char *key_type; + char *apqns = NULL; + int rc; + + keystore = import_data->keystore; + + rc = _keystore_get_key_filenames(keystore, key_name, &file_names); + if (rc != 0) + goto out; + +check_duplicate_key: + rc = _keystore_ensure_keyfiles_not_exist(&file_names, key_name); + if (rc == -EEXIST) { + if (import_data->batch_mode) { + rc = 1; + goto out; + } + + printf("%s: Do you want to enter an alternate name [y/N]? ", + program_invocation_short_name); + if (!prompt_for_yes(keystore->verbose)) { + rc = 1; + goto out; + } + +prompt_alt_name: + printf("%s: Alternate name: ", program_invocation_short_name); + rc = getline(&alt_name, &alt_name_len, stdin); + if (rc <= 1) { + rc = 1; + goto out; + } + + if (alt_name[strlen(alt_name) - 1] == '\n') + alt_name[strlen(alt_name) - 1] = '\0'; + + key_name = alt_name; + _keystore_free_key_filenames(&file_names); + rc = _keystore_get_key_filenames(keystore, key_name, + &file_names); + if (rc != 0) + goto prompt_alt_name; + + goto check_duplicate_key; + } else if (rc != 0) { + goto out; + } + + secure_key_size = sizeof(secure_key); + rc = import_kms_key(keystore->kms_info, key1_id, key2_id, xts, key_name, + secure_key, &secure_key_size, keystore->verbose); + if (rc != 0) { + warnx("KMS plugin '%s' failed to import key '%s': %s", + keystore->kms_info->plugin_name, key_name, strerror(-rc)); + print_last_kms_error(keystore->kms_info); + if (rc == -ENOTSUP) + fatal_err = true; + goto out; + } + + key_type = get_key_type(secure_key, secure_key_size); + if (key_type == NULL) { + warnx("Key '%s' is not a valid secure key", key_name); + free(secure_key); + rc = -EINVAL; + goto out; + } + + rc = get_kms_apqns_for_key_type(keystore->kms_info, key_type, true, + &apqns, keystore->verbose); + if (rc != 0) { + if (rc == -ENOTSUP) + warnx("Key-type not supported by the KMS plugin '%s'", + keystore->kms_info->plugin_name); + goto out; + } + + pr_verbose(keystore, "APQNs for keytype %s: '%s'", key_type, apqns); + + rc = _keystore_create_info_props(keystore, key_name, description, + volumes, apqns, false, + import_data->novolcheck, + sector_size, volume_type, key_type, + keystore->kms_info->plugin_name, + &key_props); + if (rc != 0) + goto out; + + rc = properties_set(key_props, xts ? PROP_NAME_KMS_XTS_KEY1_ID : + PROP_NAME_KMS_KEY_ID, key1_id); + if (rc != 0) { + pr_verbose(keystore, "Failed to set key id of key #1: %s", + strerror(-rc)); + goto out; + } + + rc = properties_set(key_props, xts ? PROP_NAME_KMS_XTS_KEY1_LABEL : + PROP_NAME_KMS_KEY_LABEL, key1_label); + if (rc != 0) { + pr_verbose(keystore, "Failed to set key label of key #1: %s", + strerror(-rc)); + goto out; + } + + if (xts) { + rc = properties_set(key_props, PROP_NAME_KMS_XTS_KEY2_ID, + key2_id); + if (rc != 0) { + pr_verbose(keystore, "Failed to set key id of key #2: " + "%s", strerror(-rc)); + goto out; + } + + rc = properties_set(key_props, PROP_NAME_KMS_XTS_KEY2_LABEL, + key2_label); + if (rc != 0) { + pr_verbose(keystore, "Failed to set key label of key " + "#2: %s", strerror(-rc)); + goto out; + } + } + + rc = write_secure_key(file_names.skey_filename, secure_key, + secure_key_size, keystore->verbose); + if (rc != 0) + goto out; + + rc = _keystore_set_file_permission(keystore, file_names.skey_filename); + if (rc != 0) + goto out_remove; + + rc = generate_key_verification_pattern(secure_key, secure_key_size, + vp, sizeof(vp), + keystore->verbose); + if (rc != 0) { + warnx("Failed to generate the key verification pattern: %s", + strerror(-rc)); + warnx("Make sure that kernel module 'paes_s390' is loaded and " + "that the 'paes' cipher is available"); + fatal_err = true; + goto out_remove; + } + + rc = properties_set(key_props, PROP_NAME_KEY_VP, vp); + if (rc != 0) { + pr_verbose(keystore, "Failed to set verification pattern of " + "key: %s", strerror(-rc)); + + goto out_remove; + } + + rc = properties_save(key_props, file_names.info_filename, 1); + if (rc != 0) { + pr_verbose(keystore, + "Key info file '%s' could not be written: %s", + file_names.info_filename, strerror(-rc)); + goto out; + } + + rc = _keystore_set_file_permission(keystore, file_names.info_filename); + if (rc != 0) { + remove(file_names.info_filename); + goto out_remove; + } + +out_remove: + if (rc != 0) { + remove(file_names.skey_filename); + remove(file_names.info_filename); + } + +out: + if (rc == 0) { + printf("Successfully imported key '%s'\n", key_name); + import_data->num_imported++; + } else if (rc < 0) { + warnx("Failed to import key '%s': %s", key_name, strerror(-rc)); + import_data->num_failed++; + } else { + warnx("Skipping key '%s'", key_name); + import_data->num_skipped++; + } + + _keystore_free_key_filenames(&file_names); + if (alt_name != NULL) + free(alt_name); + if (apqns != NULL) + free(apqns); + if (key_props != NULL) + properties_free(key_props); + + return fatal_err ? rc : 0; +} + +/** + * Imports secure keys from the KMS and adds it to the key store + * + * @param[in] keystore the key store + * @param[in] label_filter the KMS label filter. Can contain wild cards. + * NULL means no name filter. + * @param[in] name_filter the name filter. Can contain wild cards. + * NULL means no name filter. + * @param[in] volume_filter the volume filter. Can contain wild cards, and + * mutliple volume filters separated by commas. + * If the filter does not contain the ':dm-name' + * part, then the volumes are matched without the + * dm-name part. If the filter contains the + * ':dm-name' part, then the filter is matched + * including the dm-name part. + * NULL means no volume filter. + * @param[in] volume_type If not NULL, specifies the volume type. + * @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] batch_mode if true, suppress alternate name prompts if a key + * with an already existing name is to be imported. + * @param[in] novolcheck if true, do not check the associated volumes for + * existence and duplicate use + * + * @returns 0 for success or a negative errno in case of an error + */ +int keystore_import_kms_keys(struct keystore *keystore, + const char *label_filter, + const char *name_filter, + const char *volume_filter, + const char *volume_type, + struct kms_option *kms_options, + size_t num_kms_options, + bool batch_mode, bool novolcheck) +{ + struct kms_import import_data = { 0 }; + int rc; + + util_assert(keystore != NULL, "Internal error: keystore is NULL"); + + if (keystore->kms_info->plugin_lib == NULL) { + warnx("The repository is not bound to a KMS plugin"); + return -ENOENT; + } + + if (volume_type != NULL && + !_keystore_valid_volume_type(volume_type)) { + warnx("Invalid volume-type specified"); + return -EINVAL; + } + + import_data.keystore = keystore; + import_data.batch_mode = batch_mode; + import_data.novolcheck = novolcheck; + import_data.num_imported = 0; + import_data.num_skipped = 0; + import_data.num_failed = 0; + + rc = process_kms_keys(keystore->kms_info, label_filter, name_filter, + volume_filter, volume_type, + kms_options, num_kms_options, + _keystore_process_kms_import, &import_data, + keystore->verbose); + if (rc != 0) { + pr_verbose(keystore, "Failed to import kms keys: %s", + strerror(-rc)); + } else { + printf("%lu keys imported, %lu keys skipped, %lu keys " + "failed to import\n", + import_data.num_imported, import_data.num_skipped, + import_data.num_failed); + if (import_data.num_failed > 0) + rc = -EIO; + } + + return rc; +} + /** * Frees a keystore object * diff --git a/zkey/keystore.h b/zkey/keystore.h index 141522d4..d143e743 100644 --- a/zkey/keystore.h +++ b/zkey/keystore.h @@ -126,6 +126,15 @@ int keystore_kms_keys_unbind(struct keystore *keystore); int keystore_msg_for_kms_key(struct keystore *keystore, const char *key_type, const char *msg); +int keystore_import_kms_keys(struct keystore *keystore, + const char *label_filter, + const char *name_filter, + const char *volume_filter, + const char *volume_type, + struct kms_option *kms_options, + size_t num_kms_options, + bool batch_mode, bool novolcheck); + void keystore_free(struct keystore *keystore); diff --git a/zkey/kms.c b/zkey/kms.c index dddc0acd..2cd6493d 100644 --- a/zkey/kms.c +++ b/zkey/kms.c @@ -2987,3 +2987,129 @@ int list_kms_keys(struct kms_info *kms_info, const char *label_filter, return rc; } + +/** + * Imports a KMS managed key. + * + * @param[in] kms_info information of the currently bound plugin. + * @param[in] key1_id the id of the key to import (1st key for XTS) + * @param[in] key2_id the id of the 2nd XTS key to import (NULL if not + * XTS) + * @param[in] xts if true, an XTS key is to be imported + * @param[in] name the zkey-name under which the key is imported + * @param[in] key_blob a buffer to return the key blob. The size of the + * buffer is specified in key_blob_length + * @param[out] key_blob_length on entry: the size of the key_blob buffer. + * on exit: the size of the key blob returned. + * @param[in] verbose if true, verbose messages are printed + * + * @returns 0 for success or a negative errno in case of an error. + */ +int import_kms_key(struct kms_info *kms_info, const char *key1_id, + const char *key2_id, bool xts, const char *name, + unsigned char *key_blob, size_t *key_blob_length, + bool verbose) +{ + size_t key_blob_size, key_blob_ofs, key_size = 0; + struct kms_property kms_prop; + char *sys_name = NULL; + int rc = 0; + + util_assert(kms_info != NULL, "Internal error: kms_info is NULL"); + util_assert(key1_id != NULL, "Internal error: key1_id is NULL"); + util_assert(xts == false || key2_id != NULL, + "Internal error: key2_id 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"); + + if (kms_info->plugin_lib == NULL) { + warnx("The repository is not bound to a KMS plugin"); + return -ENOENT; + } + + if (kms_info->funcs->kms_import_key == NULL || + kms_info->funcs->kms_set_key_properties == NULL) { + pr_verbose(verbose, "The KMS plugin does not support to " + "import keys"); + return -ENOTSUP; + } + + sys_name = _get_system_specific_prop_name(KMS_KEY_PROP_NAME); + if (sys_name == NULL) + return -ENOMEM; + + key_blob_size = *key_blob_length; + memset(key_blob, 0, key_blob_size); + + rc = kms_info->funcs->kms_import_key(kms_info->handle, key1_id, + key_blob, &key_blob_size); + if (rc != 0) { + pr_verbose(verbose, "KMS plugin failed to import key '%s': %s", + key1_id, strerror(-rc)); + goto out; + } + + if (is_cca_aes_data_key(key_blob, key_blob_size)) + key_size = AESDATA_KEY_SIZE; + else if (is_cca_aes_cipher_key(key_blob, key_blob_size)) + key_size = AESCIPHER_KEY_SIZE; + else if (is_ep11_aes_key(key_blob, key_blob_size)) + key_size = EP11_KEY_SIZE; + + if (key_size == 0 || key_blob_size > key_size) { + pr_verbose(verbose, "Key '%s' has an unknown or unsupported " + "key type", key1_id); + rc = -EIO; + goto out; + } + + key_blob_ofs = key_size; + + if (xts) { + key_blob_size = key_size; + rc = kms_info->funcs->kms_import_key(kms_info->handle, key2_id, + key_blob + key_blob_ofs, + &key_blob_size); + if (rc != 0) { + pr_verbose(verbose, "KMS plugin failed to import key #2" + "'%s': %s", key2_id, strerror(-rc)); + goto out; + } + + key_blob_ofs += key_size; + } + + *key_blob_length = key_blob_ofs; + + kms_prop.name = sys_name; + kms_prop.value = name; + + rc = kms_info->funcs->kms_set_key_properties(kms_info->handle, key1_id, + &kms_prop, 1); + if (rc != 0) { + pr_verbose(verbose, "KMS plugin failed to set properties of " + "key '%s': %s", key1_id, strerror(-rc)); + goto out; + } + + if (xts) { + rc = kms_info->funcs->kms_set_key_properties(kms_info->handle, + key2_id, + &kms_prop, 1); + if (rc != 0) { + pr_verbose(verbose, "KMS plugin failed to set " + "properties of key #2 '%s': %s", key1_id, + strerror(-rc)); + goto out; + } + } + +out: + if (sys_name != NULL) + free(sys_name); + if (rc != 0) + *key_blob_length = 0; + + return rc; +} diff --git a/zkey/kms.h b/zkey/kms.h index 74fa480f..75088032 100644 --- a/zkey/kms.h +++ b/zkey/kms.h @@ -110,4 +110,9 @@ int list_kms_keys(struct kms_info *kms_info, const char *label_filter, const char *volume_type, struct kms_option *kms_options, size_t num_kms_options, bool verbose); +int import_kms_key(struct kms_info *kms_info, const char *key1_id, + const char *key2_id, bool xts, const char *name, + unsigned char *key_blob, size_t *key_blob_length, + bool verbose); + #endif diff --git a/zkey/zkey.1 b/zkey/zkey.1 index 6d323a90..84f4af8f 100644 --- a/zkey/zkey.1 +++ b/zkey/zkey.1 @@ -1065,6 +1065,50 @@ command displays the attributes of the secure keys, such as key label, key name, whether it is a secure key that can be used for the XTS cipher mode, the textual description, associated volumes, the volume type, and sector size. . +.SS "Import secure keys managed by a key management system into the repository" +. +.B zkey kms +.BR import | im +.RB [ \-\-label | \-B +.IR key-label ] +.RB [ \-\-name | \-N +.IR key-name ] +.RB [ \-\-volumes | \-l +.IR volume1[:dmname1][,volume2[:dmname2][,...]] ] +.RB [ \-\-volume-type | \-t +.IR type ] +.RB [ \-\-batch\-mode | \-q ] +.RB [ \-\-no\-volume\-check ] +.RB [ KMS\-plugin\ specific\ options ] +.RB [ \-\-verbose | \-V ] +. +.PP +Use the +.B kms import +command to import secure keys managed by a key management system (KMS) into the +secure key repository. +You can filter the list of keys to be imported by key label, key name, +associated volumes, and volume type. You can use wildcards for the key label, +key name, and associated volumes. The device-mapper name of an associated +volume can be omitted. If it is specified, then only those keys are listed that +are associated with the specified volume and device-mapper name. +.PP +A key management system plugin may offer plugin specific options that can be +specified with the \fBkms import\fP command. Use \fBkms import \-\-help\fP +to display the plugin specific options and their meaning. +.PP +If a secure key with the same name as a key to be imported already exists in +the repository, then you are prompted to enter an alternate name. You can skip +the import of that key, or enter an alternate name. If option +\fB\-\-batch\-mode\fP is specified, then already existing keys are skipped. +.PP +If a key to be imported is associated with one or multiple volumes, it is +verified that the volumes are available, and are not already associated with +another secure key in the repository. If one of the volumes or all of them are +not available, or are already associated with another secure key, the import +fails. Use option \fB\-\-no\-volume\-check\fP to omit the volume check, and +import the keys even if the associated volume(s) do not exist. +. . . .SH OPTIONS @@ -1756,7 +1800,52 @@ has been compiled with LUKS2 support enabled. . . . - +.SS "Options for the kms import command" +.TP +.BR \-B ", " \-\-label\~\fIkey-label\fP +Specifies the label of the secure key in the key management system (KMS). +You can use wildcards to select multiple secure keys. +When wildcards are used you must quote the value. +Only keys with labels that match the pattern are imported. +.TP +.BR \-N ", " \-\-name\~\fIkey-name\fP +Specifies the name of the secure key in the key management system (KMS). +You can use wildcards to select multiple secure keys. +When wildcards are used you must quote the value. +Only keys with names that match the pattern are imported. +.TP +.BR \-l ", " \-\-volumes\~\fIvolume1[:dmname1][,volume2[:dmname2][,...]]\fP +Specifies a comma-separated list of volumes (block devices) which are +associated with the secure AES key in the key management system (KMS). Only +those keys are imported, which are associated with the specified volumes. +The volume association also contains the device-mapper name, separated by a +colon, used with dm-crypt. You can omit the device-mapper name; if it is +specified then only those keys are listed that are associated with the +specified volume and device-mapper name. You can use wildcards to specify +the volumes and device-mapper names. +When wildcards are used you must quote the value. +.TP +.BR \-t ", " \-\-volume-type\~\fItype\fP +Specifies the volume type of the associated volumes used with dm-crypt. Possible +values are \fBplain\fP and \fBluks2\fP. Only keys with the specified volume +type are imported. +This option is only available if +.B zkey +has been compiled with LUKS2 support enabled. +.TP +.BR \-q ", " \-\-batch\-mode +Suppress prompts to skip or to enter an anternate name, if a secure key with the +same name as the secure key to be imported already exists in the repository. +When this option is specified, then keys with an altready existing name are +skipped. +.TP +.BR \-\-no\-volume\-check +Do not check if the volume(s) associated with the to be imported secure key(s) +are available, or are already associated with other secure keys in the +repository. +. +. +. .SS "General options" .TP .BR \-V ", " \-\-verbose diff --git a/zkey/zkey.c b/zkey/zkey.c index c17a00a1..4a73f251 100644 --- a/zkey/zkey.c +++ b/zkey/zkey.c @@ -72,6 +72,7 @@ static struct zkey_globals { char *volumes; char *apqns; bool noapqncheck; + bool novolcheck; long int sector_size; char *volume_type; char *newname; @@ -128,6 +129,7 @@ static struct zkey_globals { #define COMMAND_KMS_CONFIGURE "configure" #define COMMAND_KMS_REENCIPHER "reencipher" #define COMMAND_KMS_LIST "list" +#define COMMAND_KMS_IMPORT "import" #define OPT_COMMAND_PLACEHOLDER "PLACEHOLDER" @@ -150,6 +152,7 @@ static struct zkey_globals { #define OPT_CRYPTSETUP_OPEN 260 #define OPT_CRYPTSETUP_FORMAT 261 #define OPT_NO_APQN_CHECK 262 +#define OPT_NO_VOLUME_CHECK 263 /* * Configuration of command line options @@ -961,6 +964,66 @@ static struct util_opt opt_vec[] = { .command = COMMAND_KMS " " COMMAND_KMS_LIST, }, #endif + /***********************************************************/ + { + .flags = UTIL_OPT_FLAG_SECTION, + .desc = "OPTIONS", + .command = COMMAND_KMS " " COMMAND_KMS_IMPORT, + }, + { + .option = { "label", required_argument, NULL, 'B'}, + .argument = "LABEL", + .desc = "Label of the secure AES keys as known by the KMS that " + "are to be imported. You can use wildcards to select " + "the keys to be imported.", + .command = COMMAND_KMS " " COMMAND_KMS_IMPORT, + }, + { + .option = { "name", required_argument, NULL, 'N'}, + .argument = "NAME", + .desc = "Name of the secure AES keys as known by zkey that " + "are to be imported. You can use wildcards to select " + "the keys to be imported.", + .command = COMMAND_KMS " " COMMAND_KMS_IMPORT, + }, + { + .option = { "volumes", required_argument, NULL, 'l'}, + .argument = "VOLUME[:DMNAME][,...]", + .desc = "Comma-separated pairs of volume and device-mapper " + "names that are associated with the secure AES key in " + "the KMS. Use this option to import all keys " + "associated with specific volumes. The device-mapper " + "name (DMNAME) is optional. If specified, only those " + "keys are listed where both, the volume and the device-" + "mapper name matches.", + .command = COMMAND_KMS " " COMMAND_KMS_IMPORT, + }, +#ifdef HAVE_LUKS2_SUPPORT + { + .option = { "volume-type", required_argument, NULL, 't'}, + .argument = "type", + .desc = "The type of the associated volume(s). Possible values " + "are 'plain' and 'luks2'. Use this option to import " + "all keys with the specified volumes type.", + .command = COMMAND_KMS " " COMMAND_KMS_IMPORT, + }, +#endif + { + .option = {"batch-mode", 0, NULL, 'q'}, + .desc = "Suppresses alternate name questions. When importing a " + "key with a name that already exists in the " + "repository, do not prompt for an alternate name, but " + "skip the import of the duplicate key.", + .command = COMMAND_KMS " " COMMAND_KMS_IMPORT, + }, + { + .option = {"no-volume-check", 0, NULL, OPT_NO_VOLUME_CHECK}, + .desc = "Do not check if the volume(s) associated with the " + "secure key(s) to be imported are available, or are " + "already associated with other secure keys.", + .command = COMMAND_KMS " " COMMAND_KMS_IMPORT, + .flags = UTIL_OPT_FLAG_NOSHORT, + }, /***********************************************************/ OPT_PLACEHOLDER, OPT_PLACEHOLDER, @@ -1063,6 +1126,7 @@ static int command_kms_info(void); static int command_kms_configure(void); static int command_kms_reencipher(void); static int command_kms_list(void); +static int command_kms_import(void); static struct zkey_command zkey_kms_commands[] = { { @@ -1150,6 +1214,20 @@ static struct zkey_command zkey_kms_commands[] = { .need_kms_login = 1, .kms_plugin_opts_cmd = KMS_COMMAND_LIST, }, + { + .command = COMMAND_KMS_IMPORT, + .abbrev_len = 2, + .function = command_kms_import, + .short_desc = "Imports secure keys managed by a key management " + "system", + .long_desc = "Imports secure keys managed by a key management " + "system (KMS) into the repository", + .need_keystore = 1, + .has_options = 1, + .use_kms_plugin = 1, + .need_kms_login = 1, + .kms_plugin_opts_cmd = KMS_COMMAND_LIST_IMPORT, + }, { .command = NULL } }; @@ -2531,6 +2609,29 @@ static int command_kms_list(void) return rc != 0 ? EXIT_FAILURE : EXIT_SUCCESS; } +/* + * Command handler for 'kms import'. + * + * Imports secure keys managed by a KMS + */ +static int command_kms_import(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 = keystore_import_kms_keys(g.keystore, g.label, g.name, g.volumes, + g.volume_type, g.kms_options, + g.num_kms_options, g.batch_mode, + g.novolcheck); + + 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 @@ -2761,6 +2862,9 @@ int main(int argc, char *argv[]) case OPT_NO_APQN_CHECK: g.noapqncheck = 1; break; + case OPT_NO_VOLUME_CHECK: + g.novolcheck = 1; + break; case 'S': g.sector_size = strtol(optarg, &endp, 0); if (*optarg == '\0' || *endp != '\0' ||