* 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
785 lines
30 KiB
Diff
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
|