From 95bf7eb285f39a8f827cc013393cc69b1265cd68 Mon Sep 17 00:00:00 2001 From: Ingo Franzki Date: Thu, 15 Feb 2024 15:14:04 +0100 Subject: [PATCH] zkey: Add the 'pvsecrets import' command The 'pvsecrets import' command imports a protected virtualization secret into the zkey key repository. Like other key import or key generation commands, additional information can be associated with the imported key, such as a textual description, the volume to encrypt with together with the volume type, the sector size, and a dummy passphrase. You can not associate a set of APQNs, since a protected virtualization secret does not need or use a crypto card. This command only works when running in a secure execution guest. Signed-off-by: Ingo Franzki Reviewed-by: Jorg Schmidbauer Signed-off-by: Steffen Eiden --- zkey/keystore.c | 112 +++++++++++++++++++++++---------- zkey/keystore.h | 9 ++- zkey/pvsecrets.c | 126 +++++++++++++++++++++++++++++++++++++ zkey/pvsecrets.h | 7 +++ zkey/zkey.1 | 134 ++++++++++++++++++++++++++++++++++++++++ zkey/zkey.c | 157 +++++++++++++++++++++++++++++++++++++++++++++++ 6 files changed, 513 insertions(+), 32 deletions(-) diff --git a/zkey/keystore.c b/zkey/keystore.c index 771bc08df..cde0caf58 100644 --- a/zkey/keystore.c +++ b/zkey/keystore.c @@ -2259,9 +2259,11 @@ int keystore_generate_key_kms(struct keystore *keystore, const char *name, } /** - * Imports a secure key from a file and adds it to the key store + * Imports a secure key from a buffer and adds it to the key store * * @param[in] keystore the key store + * @param[in] secure_key the buffer containing the key + * @param[in] secure_key_size the size of the key * @param[in] name the name of the key * @param[in] description textual description of the key (optional, can be NULL) * @param[in] volumes a comma separated list of volumes associated with this @@ -2274,7 +2276,6 @@ int keystore_generate_key_kms(struct keystore *keystore, const char *name, * of two and in range 512 - 4096 bytes. 0 means that * the sector size is not specified and the system * default is used. - * @param[in] import_file The name of a secure key containing the key to import * @param[in] volume_type the type of volume * @param[in] gen_passphrase if true, generate a (dummy) passphrase for LUKS2 * @param[in] passphrase_file the file name of a file containing a passphrase @@ -2283,25 +2284,23 @@ int keystore_generate_key_kms(struct keystore *keystore, const char *name, * * @returns 0 for success or a negative errno in case of an error */ -int keystore_import_key(struct keystore *keystore, const char *name, - const char *description, const char *volumes, - const char *apqns, bool noapqncheck, size_t sector_size, - const char *import_file, const char *volume_type, - bool gen_passphrase, const char *passphrase_file, - struct ext_lib *lib) +int keystore_import(struct keystore *keystore, unsigned char *secure_key, + size_t secure_key_size, const char *name, + const char *description, const char *volumes, + const char *apqns, bool noapqncheck, size_t sector_size, + const char *volume_type, bool gen_passphrase, + const char *passphrase_file, struct ext_lib *lib) { struct key_filenames file_names = { 0 }; struct properties *key_props = NULL; - size_t secure_key_size; const char *key_type; u8 mkvp[MKVP_LENGTH]; int selected = 1; - u8 *secure_key; int rc; util_assert(keystore != NULL, "Internal error: keystore is NULL"); util_assert(name != NULL, "Internal error: name is NULL"); - util_assert(import_file != NULL, "Internal error: import_file is NULL"); + util_assert(secure_key != NULL, "Internal error: secure_key is NULL"); rc = _keystore_get_key_filenames(keystore, name, &file_names); if (rc != 0) @@ -2311,27 +2310,29 @@ int keystore_import_key(struct keystore *keystore, const char *name, if (rc != 0) goto out_free_key_filenames; - secure_key = read_secure_key(import_file, &secure_key_size, - keystore->verbose); - if (secure_key == NULL) { - rc = -ENOENT; - goto out_free_key_filenames; - } - key_type = get_key_type(secure_key, secure_key_size); if (key_type == NULL) { warnx("Key '%s' is not a valid secure key", name); - free(secure_key); rc = -EINVAL; goto out_free_key_filenames; } + if (!is_secure_key(secure_key, secure_key_size)) { + if (apqns != NULL) { + warnx("No APQNs can be associated with keys of type %s", + key_type); + rc = -EINVAL; + goto out_free_props; + } + goto write_key; + } + rc = get_master_key_verification_pattern(secure_key, secure_key_size, mkvp, keystore->verbose); if (rc != 0) { warnx("Failed to get the master key verification pattern: %s", strerror(-rc)); - goto out_free_key; + goto out_free_props; } rc = cross_check_apqns(apqns, mkvp, @@ -2340,17 +2341,17 @@ int keystore_import_key(struct keystore *keystore, const char *name, get_card_type_for_keytype(key_type), true, keystore->verbose); if (rc == -EINVAL) - goto out_free_key; + goto out_free_props; if (rc != 0 && rc != -ENOTSUP && noapqncheck == 0) { warnx("Your master key setup is improper"); - goto out_free_key; + goto out_free_props; } if (is_cca_aes_cipher_key(secure_key, secure_key_size)) { if (lib->cca->lib_csulcca == NULL) { rc = load_cca_library(lib->cca, keystore->verbose); if (rc != 0) - goto out_free_key; + goto out_free_props; } rc = select_cca_adapter_by_mkvp(lib->cca, mkvp, apqns, @@ -2365,7 +2366,7 @@ int keystore_import_key(struct keystore *keystore, const char *name, warnx("No APQN found that is suitable for " "working with the secure AES key '%s'", name); rc = 0; - goto out_free_key; + goto out_free_props; } rc = restrict_key_export(lib->cca, secure_key, secure_key_size, @@ -2375,7 +2376,7 @@ int keystore_import_key(struct keystore *keystore, const char *name, "key: %s", strerror(-rc)); if (!selected) print_msg_for_cca_envvars("secure AES key"); - goto out_free_key; + goto out_free_props; } rc = check_aes_cipher_key(secure_key, secure_key_size); @@ -2386,15 +2387,14 @@ int keystore_import_key(struct keystore *keystore, const char *name, if (!prompt_for_yes(keystore->verbose)) { warnx("Operation aborted"); rc = -ECANCELED; - goto out_free_key; + goto out_free_props; } } } +write_key: rc = write_secure_key(file_names.skey_filename, secure_key, secure_key_size, keystore->verbose); - free(secure_key); - secure_key = NULL; if (rc != 0) goto out_free_props; @@ -2414,9 +2414,6 @@ int keystore_import_key(struct keystore *keystore, const char *name, "Successfully imported a secure key in '%s' and key info in '%s'", file_names.skey_filename, file_names.info_filename); -out_free_key: - if (secure_key != NULL) - free(secure_key); out_free_props: if (key_props != NULL) properties_free(key_props); @@ -2431,6 +2428,59 @@ int keystore_import_key(struct keystore *keystore, const char *name, return rc; } +/** + * Imports a secure key from a file and adds it to the key store + * + * @param[in] keystore the key store + * @param[in] name the name of the key + * @param[in] description textual description of the key (optional, can be NULL) + * @param[in] volumes a comma separated list of volumes associated with this + * key (optional, can be NULL) + * @param[in] apqns a comma separated list of APQNs associated with this + * key (optional, can be NULL) + * @param[in] noapqncheck if true, the specified APQN(s) are not checked for + * existence and type. + * @param[in] sector_size the sector size to use with dm-crypt. It must be a + * power of two and in range 512 - 4096 bytes. 0 means + * that the sector size is not specified and the system + * default is used. + * @param[in] import_file The name of a secure key containing the key to import + * @param[in] volume_type the type of volume + * @param[in] gen_passphrase if true, generate a (dummy) passphrase for LUKS2 + * @param[in] passphrase_file the file name of a file containing a passphrase + * for LUKS2 (optional, can be NULL) + * @param[in] lib the external library struct + * + * @returns 0 for success or a negative errno in case of an error + */ +int keystore_import_key(struct keystore *keystore, const char *name, + const char *description, const char *volumes, + const char *apqns, bool noapqncheck, size_t sector_size, + const char *import_file, const char *volume_type, + bool gen_passphrase, const char *passphrase_file, + struct ext_lib *lib) +{ + size_t secure_key_size; + u8 *secure_key; + int rc; + + util_assert(import_file != NULL, "Internal error: import_file is NULL"); + + secure_key = read_secure_key(import_file, &secure_key_size, + keystore->verbose); + if (secure_key == NULL) + return -ENOENT; + + rc = keystore_import(keystore, secure_key, secure_key_size, name, + description, volumes, apqns, noapqncheck, + sector_size, volume_type, gen_passphrase, + passphrase_file, lib); + + if (secure_key != NULL) + free(secure_key); + + return rc; +} /** * Changes properties of a key in the keystore. diff --git a/zkey/keystore.h b/zkey/keystore.h index 1443b5df4..b4cae9aae 100644 --- a/zkey/keystore.h +++ b/zkey/keystore.h @@ -3,7 +3,7 @@ * * Keystore handling functions * - * Copyright IBM Corp. 2018, 2020 + * Copyright IBM Corp. 2018, 2024 * * s390-tools is free software; you can redistribute it and/or modify * it under the terms of the MIT license. See LICENSE for details. @@ -65,6 +65,13 @@ int keystore_generate_key_kms(struct keystore *keystore, const char *name, struct kms_option *kms_options, size_t num_kms_options); +int keystore_import(struct keystore *keystore, unsigned char *secure_key, + size_t secure_key_size, const char *name, + const char *description, const char *volumes, + const char *apqns, bool noapqncheck, size_t sector_size, + const char *volume_type, bool gen_passphrase, + const char *passphrase_file, struct ext_lib *lib); + int keystore_import_key(struct keystore *keystore, const char *name, const char *description, const char *volumes, const char *apqns, bool noapqncheck, size_t sector_size, diff --git a/zkey/pvsecrets.c b/zkey/pvsecrets.c index 7f28febad..a4b3a5a83 100644 --- a/zkey/pvsecrets.c +++ b/zkey/pvsecrets.c @@ -552,3 +552,129 @@ int pvsecrets_list(int uv_fd, bool all, bool hex, const char *type_filter, return rc; } + +struct build_secret_key_blob_data { + unsigned char id[UV_SECRET_ID_LEN]; + char name[UV_SECRET_ID_LEN]; + struct pvsecrettoken token; + bool found; +}; + +/** + * Callback used to generate a pvsecrets key blob for a specific secret ID. + * Called for each secret. + * + * @param idx the index of the secret + * @param type the type of the secret + * @param id the ID of the secret + * @param cb_private callback private data + * + * @returns 0 on success, a negative errno in case of an error + */ +static int pvsecrets_build_key_blob_cb(u16 UNUSED(idx), u16 type, u32 len, + const u8 id[UV_SECRET_ID_LEN], + void *cb_private) +{ + struct build_secret_key_blob_data *build_blob_data = cb_private; + + if (build_blob_data->found) + return 0; + + if (memcmp(id, build_blob_data->name, UV_SECRET_ID_LEN) != 0 && + memcmp(id, build_blob_data->id, UV_SECRET_ID_LEN) != 0) + return 0; + + memset(&build_blob_data->token, 0, sizeof(build_blob_data->token)); + build_blob_data->token.hdr.type = TOKEN_TYPE_NON_CCA; + build_blob_data->token.hdr.version = TOKEN_VERSION_PVSECRET; + build_blob_data->token.secret_type = type; + build_blob_data->token.secret_len = len; + memcpy(build_blob_data->token.secretid, id, UV_SECRET_ID_LEN); + + build_blob_data->found = true; + + return 0; +} + +/** + * Imports a protected virtualization secure key from the UV and adds it to the + * key store + * + * @param keystore the key store + * @param uv_fd the file descriptor of the ultravisor device + * @param secret_id the secret id as 32 byte hex string. Can be NULL if + * secret_name is non-NULL. + * @param secret_name the secret name. Can be NULL if secret_id is non-NULL. + * @param name the name of the key in the repository + * @param description textual description of the key (optional, can be NULL) + * @param volumes a comma separated list of volumes associated with this + * key (optional, can be NULL) + * @param volume_type the type of volume + * @param sector_size the sector size to use with dm-crypt. It must be a + * power of two and in range 512 - 4096 bytes. 0 means + * that the sector size is not specified and the system + * default is used. + * @param gen_passphrase if true, generate a (dummy) passphrase for LUKS2 + * @param passphrase_file the file name of a file containing a passphrase + * for LUKS2 (optional, can be NULL) + * @param verbose if true, verbose messages are printed + * + * @returns 0 for success or a negative errno in case of an error + */ +int pvsecrets_import(struct keystore *keystore, int uv_fd, + const char *secret_id, const char *secret_name, + const char *name, const char *description, + const char *volumes, const char *volume_type, + long sector_size, bool gen_passphrase, + const char *passphrase_file, bool verbose) +{ + struct build_secret_key_blob_data build_blob_data = { 0 }; + int rc; + + util_assert(keystore != NULL, "Internal error: keystore is NULL"); + util_assert(uv_fd != -1, "Internal error: uv_fd is -1"); + util_assert(secret_id != NULL || secret_name != NULL, + "Internal error: secret_id and secrest_name is NULL"); + util_assert(name != NULL, "Internal error: name is NULL"); + + rc = get_secret_id_from_hex_or_name(secret_id, secret_name, + build_blob_data.id, + build_blob_data.name); + if (rc < 0) + return rc; + if (rc > 0) + return -EINVAL; + + rc = uv_list_secrets(uv_fd, pvsecrets_build_key_blob_cb, + &build_blob_data, verbose); + if (rc != 0) { + warnx("Failed to import the pvsecret with %s '%s': %s", + secret_id != NULL ? "id" : "name", + secret_id != NULL ? secret_id : secret_name, + strerror(-rc)); + return rc; + } + + if (!build_blob_data.found) { + warnx("The pvsecret with %s '%s' does not exist", + secret_id != NULL ? "id" : "name", + secret_id != NULL ? secret_id : secret_name); + return -ENOENT; + } + + if (!is_pvsecret_type_supported(build_blob_data.token.secret_type)) { + warnx("The type of the pvsecret with %s '%s' is not supported " + "by zkey: %s", secret_id != NULL ? "id" : "name", + secret_id != NULL ? secret_id : secret_name, + get_pvsecret_type_name( + build_blob_data.token.secret_type)); + return -EINVAL; + } + + rc = keystore_import(keystore, (unsigned char *)&build_blob_data.token, + sizeof(build_blob_data.token), name, description, + volumes, NULL, false, sector_size, volume_type, + gen_passphrase, passphrase_file, NULL); + + return rc; +} diff --git a/zkey/pvsecrets.h b/zkey/pvsecrets.h index ad8440350..9503c5155 100644 --- a/zkey/pvsecrets.h +++ b/zkey/pvsecrets.h @@ -16,6 +16,7 @@ #include "lib/zt_common.h" #include "pkey.h" +#include "keystore.h" /* * Definitions for the /dev/uv kernel module interface @@ -89,5 +90,11 @@ int uv_open_device(bool verbose); int pvsecrets_list(int uv_fd, bool all, bool hex, const char *type_filter, const char *secret_id, const char *secret_name, bool verbose); +int pvsecrets_import(struct keystore *keystore, int uv_fd, + const char *secret_id, const char *secret_name, + const char *name, const char *description, + const char *volumes, const char *volume_type, + long sector_size, bool gen_passphrase, + const char *passphrase_file, bool verbose); #endif diff --git a/zkey/zkey.1 b/zkey/zkey.1 index 4386629f9..ba71a839b 100644 --- a/zkey/zkey.1 +++ b/zkey/zkey.1 @@ -1208,6 +1208,64 @@ are supported by the \fBzkey\fP tool. To list all pvsecret types, specify the This command is only available when running in a secure execution guest. Only the \fBroot\fP user is allowed to perform this command. . +.SS "Import protected virtualization secrets into the repository" +. +.B zkey pvsecrets +.BR import | im +.RB [ \-\-pvsecret\-id | \-I +.IR pvsecret\-id ] +.RB [ \-\-pvsecret\-name | \-e +.IR pvsecret\-name ] +.B \-\-name | \-N +.IR key\-name +.RB [ \-\-description | \-d +.IR description ] +.RB [ \-\-volumes | \-l +.IR volume1:dmname1[,volume2:dmname2[,...]] ] +.RB [ \-\-sector\-size | \-S +.IR bytes ] +.RB [ \-\-volume\-type | \-t +.IR type ] +.RB [ \-\-gen\-dummy\-passphrase ] +.RB [ \-\-set\-dummy\-passphrase +.IR passphrase\-file ] +.RB [ \-\-verbose | \-V ] +. +.PP +Use the +.B pvsecrets import +command to import a protected virtualization (PV) secret into the repository. +Either the \fB\-\-pvsecret\-id\fP option or the \fB\-\-pvsecret\-name\fP +option can be specified. If the pvsecret name is specified, then the +\fB\-\-name\fP option can be omitted. The name of the protected virtualization +secret key object in the repository will then be the same as the pvsecret name. +.PP +You can use the YAML file that was created when using the \fBpvsecret create\fP +command for adding the protected virtualization secret: +\fB\-\-pvsecret\-id "$(yq .id \fP\fIYAML\-FILE\fP\fB)"\fP or +\fB\-\-pvsecret\-name "$(yq .name \fP\fIYAML\-FILE\fP\fB)"\fP. +You might have to install the \fByq\fP package first. +.PP +A protected virtualization secret key object does not contain the key material, +but only a reference (i.e. the secret ID) to the key in the ultravisor. +When such a protected virtualization secret key object is used with +\fBdm\-crypt\fP and the \fBPAES\fP kernel cipher, the key material (i.e. a +protected key) is retrieved from the ultravisor and the crypto operation is +performed with it. +.PP +When importing a protected virtualization secret in a key repository, +additional information can be associated with it using the +.B \-\-description +, +.B \-\-volumes +, or the +.B \-\-sector\-size +options. APQNs can not be associated, because protected virtualization secrets +do not require a crypto card. +.PP +This command is only available when running in a secure execution guest. +Only the \fBroot\fP user is allowed to perform this command. +. . . . @@ -2117,6 +2175,82 @@ can be specified, but not both. . . . +.SS "Options for the pvsecrets import command" +.TP +.BR \-I ", " \-\-pvsecret\-id\~\fIpvsecret\-id\fP +ID of the protected virtualization (PV) secret to import. The pvsecret ID is a +32 byte hex string, optionally prefixed by \fB0x\fP. You can use the YAML file +that was created when using the \fBpvsecret create\fP command for adding the +protected virtualization secret: +\fB\-\-pvsecret\-id "$(yq .id \fP\fIYAML\-FILE\fP\fB)"\fP. +You might have to install the \fByq\fP package first. +Either the \fB\-\-pvsecret\-id\fP option or the \fB\-\-pvsecret\-name\fP option +can be specified, but not both. +.TP +.BR \-e ", " \-\-pvsecret\-name\~\fIpvsecret\-name\fP +Name of the protected virtualization (PV) secret to import. You can use the YAML +file that was created when using the \fBpvsecret create\fP command for adding +the protected virtualization secret: +\fB\-\-pvsecret\-name "$(yq .name \fP\fIYAML\-FILE\fP\fB)"\fP. +You might have to install the \fByq\fP package first. +Either the \fB\-\-pvsecret\-id\fP option or the \fB\-\-pvsecret\-name\fP option +can be specified, but not both. If the \fB\-\-pvsecret\-name\fP option is +specified, then the \fB\-\-name\fP option can be omitted. The name of the +protected virtualization secret key object in the repository will then be the +same as the pvsecret name. +.TP +.BR \-N ", " \-\-name\~\fIkey\-name\fP +Specifies the name of the protected virtualization secret key object in the key +repository. If the \fB\-\-pvsecret\-name\fP option is specified, then the +\fB\-\-name\fP option can be omitted. The name of the protected virtualization +secret in the repository will then be the same as the pvsecret name. +.TP +.BR \-d ", " \-\-description\~\fIdescription\fP +Specifies a textual description for the protected virtualization secret in the +key repository. +.TP +.BR \-l ", " \-\-volumes\~\fIvolume1:dmname1[,volume2:dmname2[,...]]\fP +Specifies a comma-separated list of volumes (block devices) which are +associated with the protected virtualization secret in the repository. These +volumes are to be encrypted using \fBdm\-crypt\fP with the protected +virtualization secret. The volume association also contains the device-mapper +name, separated by a colon, used with \fBdm\-crypt\fP. A specific volume can +only be associated with a single key. +.TP +.BR \-S ", " \-\-sector\-size\~\fIbytes\fP +Specifies the sector size in bytes used with \fBdm\-crypt\fP. It must be a power +of two and in the range of 512 to 4096 bytes. If omitted, the system default +sector size is used. +.TP +.BR \-t ", " \-\-volume\-type\~\fItype\fP +Specifies the volume type of the associated volumes used with \fBdm\-crypt\fP. +Possible values are \fBplain\fP and \fBluks2\fP. If omitted, \fBluks2\fP is +used. This option is only available if +.B zkey +has been compiled with LUKS2 support enabled. If LUKS2 support is not enabled, +the default volume type is \fBplain\fP. +.TP +.BR \-\-gen\-dummy\-passphrase +Generate a dummy passphrase randomly and associate it with the protected +virtualization secret used to encrypt LUKS2 volume(s). The LUKS2 passphrase is +of less or no relevance for the security of the volume(s), when a protected +virtualization secret is used to encrypt the volume(s), and can therefore be +stored insecurely inside the key repository. If for a certain usage the +passphrase is of relevance for security, then do not use this option. This +option can only be specified for keys with a volume type of \fBluks2\fP. +.TP +.BR \-\-set\-dummy\-passphrase\~\fIpassphrase\-file\fP +Set a dummy passphrase that is read from the specified file and associate it +with the protected virtualization secret used to encrypt LUKS2 volume(s). +The LUKS2 passphrase is of less or no relevance for the security of the +volume(s), when an protected virtualization secret is used to encrypt the +volume(s), and can therefore be stored insecurely inside the key repository. +If for a certain usage the passphrase is of relevance for security, then do +not use this option. This option can only be specified for keys with a volume +type of \fBluks2\fP. +. +. +. .SS "General options" .TP .BR \-V ", " \-\-verbose diff --git a/zkey/zkey.c b/zkey/zkey.c index adc48d60b..6e9b32af5 100644 --- a/zkey/zkey.c +++ b/zkey/zkey.c @@ -145,6 +145,7 @@ static struct zkey_globals { #define COMMAND_KMS_REFRESH "refresh" #define COMMAND_PVSECRETS "pvsecrets" #define COMMAND_PVSECRETS_LIST "list" +#define COMMAND_PVSECRETS_IMPORT "import" #define OPT_COMMAND_PLACEHOLDER "PLACEHOLDER" @@ -1234,6 +1235,103 @@ static struct util_opt opt_vec[] = { .command = COMMAND_PVSECRETS " " COMMAND_PVSECRETS_LIST, }, /***********************************************************/ + { + .flags = UTIL_OPT_FLAG_SECTION, + .desc = "OPTIONS", + .command = COMMAND_PVSECRETS " " COMMAND_PVSECRETS_IMPORT, + }, + { + .option = { "pvsecret-id", required_argument, NULL, 'I'}, + .argument = "PVSECRET-ID", + .desc = "ID of the protected virtualization (PV) secret to " + "import. Either '--pvsecret-id/-I' or " + "'--pvsecret-name/-e' can be specified, but not both.", + .command = COMMAND_PVSECRETS " " COMMAND_PVSECRETS_IMPORT, + }, + { + .option = { "pvsecret-name", required_argument, NULL, 'e'}, + .argument = "PVSECRET-NAME", + .desc = "Name of the protected virtualization (PV) secret to " + "import. Either '--pvsecret-name/-e' or " + "'--pvsecret-id/-I' can be specified, but not both. " + "If the '--pvsecret-name/-e' option is specified, but " + "the '--name/-N' option is omitted, then the imported " + "protected virtualisation secret will be named the " + "same as the pvsecret name.", + .command = COMMAND_PVSECRETS " " COMMAND_PVSECRETS_IMPORT, + }, + { + .option = { "name", required_argument, NULL, 'N'}, + .argument = "NAME", + .desc = "Name of the imported protected virtualisation secret " + "in the repository. If the '--name/-N' option is " + "omitted, but the '--pvsecret-name/-e' is specified, " + "then the imported protected virtualisation secret " + "will be named the same as the pvsecret name.", + .command = COMMAND_PVSECRETS " " COMMAND_PVSECRETS_IMPORT, + }, + { + .option = { "description", required_argument, NULL, 'd'}, + .argument = "DESCRIPTION", + .desc = "Textual description of the protected virtualisation " + "secret in the repository", + .command = COMMAND_PVSECRETS " " COMMAND_PVSECRETS_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 protected " + "virtualisation secret in the repository", + .command = COMMAND_PVSECRETS " " COMMAND_PVSECRETS_IMPORT, + }, + { + .option = { "sector-size", required_argument, NULL, 'S'}, + .argument = "512|4096", + .desc = "The sector size used with dm-crypt. It must be a power " + "of two and in range 512 - 4096 bytes. If this option " + "is omitted, the system default sector size (512) is " + "used", + .command = COMMAND_PVSECRETS " " COMMAND_PVSECRETS_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'. When this option is omitted, " + "the default is 'luks2'", + .command = COMMAND_PVSECRETS " " COMMAND_PVSECRETS_IMPORT, + }, +#endif + { + .option = { "gen-dummy-passphrase", 0, NULL, + OPT_GEN_DUMMY_PASSPHRASE}, + .desc = "Generate a dummy passphrase and associate it with the " + "protected virtualisation secret used to encrypt LUKS2 " + "volume(s). The LUKS2 passphrase is of less or no " + "relevance for the security of the volume(s), when an " + "protected virtualisation secret is used to encrypt " + "the volume(s), and can therefore be stored insecurely " + "inside the secure key repository.", + .flags = UTIL_OPT_FLAG_NOSHORT, + .command = COMMAND_PVSECRETS " " COMMAND_PVSECRETS_IMPORT, + }, + { + .option = { "set-dummy-passphrase", required_argument, NULL, + OPT_SET_DUMMY_PASSPHRASE}, + .argument = "passphrase-file", + .desc = "Set a dummy passphrase to be associated with the " + "protected virtualisation secret used to encrypt LUKS2 " + "volume(s). The LUKS2 passphrase is of less or no " + "relevance for the security of the volume(s), when a " + "protected virtualisation secret is used to encrypt " + "the volume(s), and can therefore be stored insecurely " + "inside the secure key repository.", + .flags = UTIL_OPT_FLAG_NOSHORT, + .command = COMMAND_PVSECRETS " " COMMAND_PVSECRETS_IMPORT, + }, + /***********************************************************/ OPT_PLACEHOLDER, OPT_PLACEHOLDER, OPT_PLACEHOLDER, @@ -1339,6 +1437,7 @@ static int command_kms_list(void); static int command_kms_import(void); static int command_kms_refresh(void); static int command_pvsecrets_list(void); +static int command_pvsecrets_import(void); static struct zkey_command zkey_kms_commands[] = { { @@ -1469,6 +1568,19 @@ static struct zkey_command zkey_pvsecrets_commands[] = { .has_options = 1, .need_uv_device = 1, }, + { + .command = COMMAND_PVSECRETS_IMPORT, + .abbrev_len = 2, + .function = command_pvsecrets_import, + .short_desc = "Imports a protected virtualization (PV) secret", + .long_desc = "Imports a protected virtualization (PV) secret " + "into the repository. This command is only " + "available when running in a secure execution " + "guest.", + .has_options = 1, + .need_keystore = 1, + .need_uv_device = 1, + }, { .command = NULL } }; @@ -3006,6 +3118,51 @@ static int command_pvsecrets_list(void) return rc != 0 ? EXIT_FAILURE : EXIT_SUCCESS; } +/* + * Command handler for 'pvsecrets import'. + * + * Import a protected virtualization secret into the repository + */ +static int command_pvsecrets_import(void) +{ + int rc; + + if (g.secret_id == NULL && g.secret_name == NULL) { + misc_print_required_parm("--pvsecret-id/-I or " + "--pvsecret-name/-e"); + return EXIT_FAILURE; + } + if (g.secret_id != NULL && g.secret_name != NULL) { + warnx("Either '--pvsecret-id/-I' or '--pvsecret-name/-e' can " + "be specified, but not both"); + util_prg_print_parse_error(); + return EXIT_FAILURE; + } + if (g.secret_name == NULL && g.name == NULL) { + misc_print_required_parm("--name/-N"); + return EXIT_FAILURE; + } + + if (g.sector_size < 0) + g.sector_size = 0; + + if (g.gen_passphrase && g.passphrase_file != NULL) { + warnx("Either '--gen-dummy-passphrase' or " + "'--set-dummy-passphrase' can be specified, but not " + "both"); + util_prg_print_parse_error(); + return EXIT_FAILURE; + } + + rc = pvsecrets_import(g.keystore, g.uv_fd, g.secret_id, g.secret_name, + g.name != NULL ? g.name : g.secret_name, + g.description, g.volumes, g.volume_type, + g.sector_size, g.gen_passphrase, + g.passphrase_file, 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