s390-tools/s390-tools-04-zkey-Add-the-pvsecrets-import-command.patch
Nikolay Gueorguiev 27cc4620be - Applied more additional patches (jsc#PED-11870, jec#PED-11958)
* s390-tools-08-rust-pvimg-Fix-flag-parsing-for-allowing-dump.patch
  * s390-tools-09-rust-pvimg-Document-the-change-from--comm-key-to--cck.patch

OBS-URL: https://build.opensuse.org/package/show/Base:System/s390-tools?expand=0&rev=247
2025-02-03 08:20:55 +00:00

785 lines
30 KiB
Diff

From 95bf7eb285f39a8f827cc013393cc69b1265cd68 Mon Sep 17 00:00:00 2001
From: Ingo Franzki <ifranzki@linux.ibm.com>
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 <ifranzki@linux.ibm.com>
Reviewed-by: Jorg Schmidbauer <jschmidb@de.ibm.com>
Signed-off-by: Steffen Eiden <seiden@linux.ibm.com>
---
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