* s390-tools-01-zkey-Add-support-for-retrieving-a-list-of-ultravisor-secrets.patch * s390-tools-02-zkey-Add-the--pvsecrets-list-command.patch * s390-tools-03-zkey-Add-PVSECRETS-AES-key-type.patch * s390-tools-04-zkey-Add-the-pvsecrets-import-command.patch * s390-tools-05-zkey-Reject-key-generation-and-APQN-association-for-PVSECRET-AES-keys.patch * s390-tools-06-zkey-Reject-re-enciphering-of-PVSECRET-AES-keys.patch * s390-tools-07-zkey-Support-validation-of-key-of-type-PVSECRET-AES.patch - Revendored vendor.tar.gz OBS-URL: https://build.opensuse.org/package/show/Base:System/s390-tools?expand=0&rev=245
824 lines
25 KiB
Diff
824 lines
25 KiB
Diff
From 5ce79ea667ea946e6591fe898db13becad018667 Mon Sep 17 00:00:00 2001
|
|
From: Ingo Franzki <ifranzki@linux.ibm.com>
|
|
Date: Thu, 15 Feb 2024 11:22:04 +0100
|
|
Subject: [PATCH] zkey: Add the 'pvsecrets list' command
|
|
|
|
The 'pvsecrets list' command lists the available protected virtualization
|
|
secrets. By default, only those pvsecret types are listed, that can be used
|
|
with zkey. If option '--all/-a' is specified, then all pvsecret types are
|
|
listed. Nevertheless, pvsecret types not supported by zkey can not be used
|
|
with zkey.
|
|
|
|
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/pvsecrets.c | 393 +++++++++++++++++++++++++++++++++++++++++++++++
|
|
zkey/pvsecrets.h | 4 +
|
|
zkey/zkey.1 | 91 ++++++++++-
|
|
zkey/zkey.c | 135 +++++++++++++++-
|
|
4 files changed, 619 insertions(+), 4 deletions(-)
|
|
|
|
diff --git a/zkey/pvsecrets.c b/zkey/pvsecrets.c
|
|
index 2874fdf1..7f28feba 100644
|
|
--- a/zkey/pvsecrets.c
|
|
+++ b/zkey/pvsecrets.c
|
|
@@ -7,6 +7,7 @@
|
|
* it under the terms of the MIT license. See LICENSE for details.
|
|
*/
|
|
|
|
+#include <ctype.h>
|
|
#include <err.h>
|
|
#include <errno.h>
|
|
#include <fcntl.h>
|
|
@@ -17,14 +18,60 @@
|
|
#include <sys/types.h>
|
|
#include <unistd.h>
|
|
|
|
+#include <openssl/sha.h>
|
|
+
|
|
#include "lib/util_base.h"
|
|
#include "lib/util_file.h"
|
|
#include "lib/util_libc.h"
|
|
#include "lib/util_panic.h"
|
|
#include "lib/util_path.h"
|
|
+#include "lib/util_rec.h"
|
|
|
|
#include "pvsecrets.h"
|
|
|
|
+struct pvsecret_type_info {
|
|
+ u16 type;
|
|
+ const char *name;
|
|
+ bool zkey_usage;
|
|
+};
|
|
+
|
|
+static const struct pvsecret_type_info pvsecret_type_info[] = {
|
|
+ { .type = UV_SECRET_TYPE_NULL, .name = "NULL",
|
|
+ .zkey_usage = false },
|
|
+ { .type = UV_SECRET_TYPE_AP_ASSOCIATION, .name = "AP-ASSOCIATION",
|
|
+ .zkey_usage = false },
|
|
+ { .type = UV_SECRET_TYPE_PLAIN_TEXT, .name = "PLAIN-TEXT",
|
|
+ .zkey_usage = false },
|
|
+ { .type = UV_SECRET_TYPE_AES_128, .name = "AES-128",
|
|
+ .zkey_usage = true },
|
|
+ { .type = UV_SECRET_TYPE_AES_192, .name = "AES-192",
|
|
+ .zkey_usage = true },
|
|
+ { .type = UV_SECRET_TYPE_AES_256, .name = "AES-256",
|
|
+ .zkey_usage = true },
|
|
+ { .type = UV_SECRET_TYPE_AES_XTS_128, .name = "AES-XTS-128",
|
|
+ .zkey_usage = true },
|
|
+ { .type = UV_SECRET_TYPE_AES_XTS_256, .name = "AES-XTS-256",
|
|
+ .zkey_usage = true },
|
|
+ { .type = UV_SECRET_TYPE_HMAC_SHA_256, .name = "HMAC-SHA-256",
|
|
+ .zkey_usage = false },
|
|
+ { .type = UV_SECRET_TYPE_HMAC_SHA_512, .name = "HMAC-SHA-512",
|
|
+ .zkey_usage = false },
|
|
+ { .type = UV_SECRET_TYPE_ECDSA_P256, .name = "ECDSA-P256",
|
|
+ .zkey_usage = false },
|
|
+ { .type = UV_SECRET_TYPE_ECDSA_P384, .name = "ECDSA-P384",
|
|
+ .zkey_usage = false },
|
|
+ { .type = UV_SECRET_TYPE_ECDSA_P521, .name = "ECDSA-P521",
|
|
+ .zkey_usage = false },
|
|
+ { .type = UV_SECRET_TYPE_EDDSA_ED25519, .name = "EDDSA-ED25519",
|
|
+ .zkey_usage = false },
|
|
+ { .type = UV_SECRET_TYPE_EDDSA_ED448, .name = "EDDSA-ED448",
|
|
+ .zkey_usage = false },
|
|
+ { .type = UV_SECRET_TYPE_INVALID, }
|
|
+};
|
|
+
|
|
+#define PVSECRETS_REC_ID "Secret ID"
|
|
+#define PVSECRETS_REC_TYPE "Type"
|
|
+
|
|
#define pr_verbose(verbose, fmt...) do { \
|
|
if (verbose) \
|
|
warnx(fmt); \
|
|
@@ -159,3 +206,349 @@ static int uv_list_secrets(int uv_fd, int (*cb)(u16 idx, u16 type, u32 len,
|
|
|
|
return rc;
|
|
}
|
|
+
|
|
+/**
|
|
+ * Returns true if the secret type is supported by zkey
|
|
+ *
|
|
+ * @param type the secret type
|
|
+ *
|
|
+ * @returns true if the type is supported, false otherwise
|
|
+ */
|
|
+static bool is_pvsecret_type_supported(u16 type)
|
|
+{
|
|
+ unsigned int i;
|
|
+
|
|
+ for (i = 0; pvsecret_type_info[i].type != UV_SECRET_TYPE_INVALID; i++) {
|
|
+ if (pvsecret_type_info[i].type == type)
|
|
+ return pvsecret_type_info[i].zkey_usage;
|
|
+ }
|
|
+
|
|
+ return false;
|
|
+}
|
|
+
|
|
+/**
|
|
+ * Returns the secret type name for the specified secret type
|
|
+ *
|
|
+ * @param type the secret type
|
|
+ *
|
|
+ * @returns a constant string containing the type name
|
|
+ */
|
|
+static const char *get_pvsecret_type_name(u16 type)
|
|
+{
|
|
+ unsigned int i;
|
|
+
|
|
+ for (i = 0; pvsecret_type_info[i].type != UV_SECRET_TYPE_INVALID; i++) {
|
|
+ if (pvsecret_type_info[i].type == type)
|
|
+ return pvsecret_type_info[i].name;
|
|
+ }
|
|
+
|
|
+ return "[UNKNOWN]";
|
|
+}
|
|
+
|
|
+/**
|
|
+ * Returns the secret type for the specified type name
|
|
+ *
|
|
+ * @param name the secret type name
|
|
+ *
|
|
+ * @returns the secret type or UV_SECRET_TYPE_INVALID if unknown.
|
|
+ */
|
|
+static u16 get_pvsecret_type_by_name(const char *name)
|
|
+{
|
|
+ unsigned int i;
|
|
+
|
|
+ for (i = 0; pvsecret_type_info[i].type != UV_SECRET_TYPE_INVALID; i++) {
|
|
+ if (strcasecmp(pvsecret_type_info[i].name, name) == 0)
|
|
+ return pvsecret_type_info[i].type;
|
|
+ }
|
|
+
|
|
+ return UV_SECRET_TYPE_INVALID;
|
|
+}
|
|
+
|
|
+/**
|
|
+ * Parses a 32 byte hex string into a 32 byte binary secret ID
|
|
+ *
|
|
+ * @param id_str the hex string to parse
|
|
+ * @param id the output buffer to store the secret ID
|
|
+ *
|
|
+ * @returns 0 for success or a negative errno in case of an error
|
|
+ */
|
|
+static int parse_secret_id_str(const char *id_str,
|
|
+ unsigned char id[UV_SECRET_ID_LEN])
|
|
+{
|
|
+ char hex[3] = { 0 };
|
|
+ unsigned long val;
|
|
+ unsigned int i;
|
|
+ char *endptr;
|
|
+
|
|
+ util_assert(id_str != NULL, "Internal error: id_str is NULL");
|
|
+ util_assert(id != NULL, "Internal error: id is NULL");
|
|
+
|
|
+ if (strncasecmp(id_str, "0x", 2) == 0)
|
|
+ id_str += 2;
|
|
+
|
|
+ if (strlen(id_str) != UV_SECRET_ID_LEN * 2)
|
|
+ return -EINVAL;
|
|
+
|
|
+ for (i = 0; i < UV_SECRET_ID_LEN; i++) {
|
|
+ hex[0] = id_str[i * 2];
|
|
+ hex[1] = id_str[i * 2 + 1];
|
|
+
|
|
+ errno = 0;
|
|
+ val = strtoul(hex, &endptr, 16);
|
|
+ if (errno != 0 || *endptr != '\0' || val > 0xff)
|
|
+ return -EINVAL;
|
|
+
|
|
+ id[i] = val;
|
|
+ }
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+/**
|
|
+ * Get the 32 byte binary secret ID from the secret name by calculating the
|
|
+ * SHA-256 has from the name.
|
|
+ *
|
|
+ * @param name the name of the secret
|
|
+ * @param id the output buffer to store the secret ID
|
|
+ *
|
|
+ * @returns 0 for success or a negative errno in case of an error
|
|
+ */
|
|
+static int get_secret_id_from_name(const char *name,
|
|
+ unsigned char id[UV_SECRET_ID_LEN])
|
|
+{
|
|
+ util_assert(name != NULL, "Internal error: id_str is NULL");
|
|
+ util_assert(id != NULL, "Internal error: id is NULL");
|
|
+ util_assert(UV_SECRET_ID_LEN == SHA256_DIGEST_LENGTH,
|
|
+ "Internal error: UV_SECRET_ID_LEN != SHA256_DIGEST_LENGTH");
|
|
+
|
|
+ if (SHA256((const unsigned char *)name, strlen(name), id) != id)
|
|
+ return -EIO;
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+/**
|
|
+ * Gets the binary 32 byte secret id from either a hex string or a secret name.
|
|
+ *
|
|
+ * @param hex the secret id as hex string. Can be NULL.
|
|
+ * @param name the secret name. Can be NULL. If the id
|
|
+ * parameter is non-NULL, then this parameter is
|
|
+ * ignored.
|
|
+ * @param id Output: the 32 byte binary secret id.
|
|
+ * @param id_str Output: the secret id in printable ascii chars
|
|
+ * form, if name is non-NULL and the name length is
|
|
+ * less than UV_SECRET_ID_LEN.
|
|
+ *
|
|
+ * @returns 0 on success, a negative errno in case of an error.
|
|
+ * If neither the hex string nor the secret name is specified, 1 is returned,
|
|
+ * and the id parameter is not modified.
|
|
+ */
|
|
+static int get_secret_id_from_hex_or_name(const char *hex, const char *name,
|
|
+ unsigned char id[UV_SECRET_ID_LEN],
|
|
+ char id_name[UV_SECRET_ID_LEN])
|
|
+{
|
|
+ int rc;
|
|
+
|
|
+ util_assert(id != NULL, "Internal error: id is NULL");
|
|
+
|
|
+ if (hex != NULL) {
|
|
+ rc = parse_secret_id_str(hex, id);
|
|
+ if (rc != 0) {
|
|
+ warnx("Invalid pvsecret id specified: '%s'", hex);
|
|
+ return rc;
|
|
+ }
|
|
+
|
|
+ return 0;
|
|
+ }
|
|
+
|
|
+ if (name != NULL) {
|
|
+ rc = get_secret_id_from_name(name, id);
|
|
+ if (rc != 0) {
|
|
+ warnx("Failed to get the ID from pvsecret name: '%s'",
|
|
+ name);
|
|
+ return rc;
|
|
+ }
|
|
+
|
|
+ if (strlen(name) < UV_SECRET_ID_LEN) {
|
|
+ strncpy(id_name, name, UV_SECRET_ID_LEN);
|
|
+ id_name[UV_SECRET_ID_LEN - 1] = '\0';
|
|
+ }
|
|
+
|
|
+ return 0;
|
|
+ }
|
|
+
|
|
+ return 1;
|
|
+}
|
|
+
|
|
+/**
|
|
+ * Checks if the secret id is printable. To be printable, all characters up to
|
|
+ * the first zero byte must be printable. All bytes after the first zero byte
|
|
+ * must be all zero. There must be at least one zero byte as the very last byte
|
|
+ * of the id.
|
|
+ *
|
|
+ * @param id the ID of the secret
|
|
+ * @param name Output: the id in the printable form and enclosed
|
|
+ * in single quotes if the id is printable. The max
|
|
+ * length of the name buffer is UV_SECRET_ID_LEN + 2:
|
|
+ * A starting quote, up to UV_SECRET_ID_LEN-1 chars,
|
|
+ * an ending quote and a zero termination byte.
|
|
+ *
|
|
+ * @returns true if the id is printable, false otherwise.
|
|
+ */
|
|
+static bool is_printable_name(const u8 id[UV_SECRET_ID_LEN],
|
|
+ char name[UV_SECRET_ID_LEN + 2])
|
|
+{
|
|
+ bool end_found = false, printable_name = false;
|
|
+ unsigned int i;
|
|
+
|
|
+ name[0] = '\'';
|
|
+ for (i = 0; i < UV_SECRET_ID_LEN; i++) {
|
|
+ if (!end_found) {
|
|
+ if (id[i] == '\0') {
|
|
+ name[1 + i] = '\'';
|
|
+ end_found = true;
|
|
+ } else if (isprint(id[i])) {
|
|
+ name[1 + i] = id[i];
|
|
+ printable_name = true;
|
|
+ } else {
|
|
+ printable_name = false;
|
|
+ end_found = true;
|
|
+ }
|
|
+ } else if (id[i] != '\0') {
|
|
+ printable_name = false;
|
|
+ }
|
|
+ }
|
|
+ if (!end_found)
|
|
+ printable_name = false;
|
|
+
|
|
+ return printable_name;
|
|
+}
|
|
+
|
|
+struct list_secrets_data {
|
|
+ struct util_rec *rec;
|
|
+ bool all;
|
|
+ bool hex;
|
|
+ u16 type_filter;
|
|
+ bool id_filter;
|
|
+ char name[UV_SECRET_ID_LEN];
|
|
+ unsigned char id[UV_SECRET_ID_LEN];
|
|
+ unsigned int matched;
|
|
+};
|
|
+
|
|
+/**
|
|
+ * Callback used with pvsecrets_list function. 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_list_cb(u16 UNUSED(idx), u16 type, u32 UNUSED(len),
|
|
+ const u8 id[UV_SECRET_ID_LEN], void *cb_private)
|
|
+{
|
|
+ struct list_secrets_data *list_data = cb_private;
|
|
+ char name[2 + UV_SECRET_ID_LEN] = { 0 };
|
|
+ char hex[2 * UV_SECRET_ID_LEN + 1] = { 0 };
|
|
+ unsigned int i;
|
|
+
|
|
+ if (!list_data->all && !is_pvsecret_type_supported(type))
|
|
+ return 0;
|
|
+
|
|
+ if (list_data->type_filter != 0 && type != list_data->type_filter)
|
|
+ return 0;
|
|
+
|
|
+ if (list_data->id_filter &&
|
|
+ memcmp(id, list_data->name, UV_SECRET_ID_LEN) != 0 &&
|
|
+ memcmp(id, list_data->id, UV_SECRET_ID_LEN) != 0)
|
|
+ return 0;
|
|
+
|
|
+ for (i = 0; i < UV_SECRET_ID_LEN; i++)
|
|
+ sprintf(&hex[i * 2], "%02x", id[i]);
|
|
+
|
|
+ if (!list_data->hex && is_printable_name(id, name))
|
|
+ util_rec_set(list_data->rec, PVSECRETS_REC_ID, name);
|
|
+ else
|
|
+ util_rec_set(list_data->rec, PVSECRETS_REC_ID, hex);
|
|
+ util_rec_set(list_data->rec, PVSECRETS_REC_TYPE,
|
|
+ get_pvsecret_type_name(type));
|
|
+
|
|
+ if (list_data->matched == 0)
|
|
+ util_rec_print_hdr(list_data->rec);
|
|
+
|
|
+ util_rec_print(list_data->rec);
|
|
+
|
|
+ list_data->matched++;
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+/**
|
|
+ * Lists protected virtualization secrets.
|
|
+ *
|
|
+ * @param uv_fd the file descriptor of the ultravisor device
|
|
+ * @param all if true, all secret types are listed
|
|
+ * @param hex if true, list the secret ID in hex, even if the
|
|
+ * secret ID would be printable
|
|
+ * @param type_filter only display secrets of the specified secret type.
|
|
+ * Can be NULL.
|
|
+ * @param secret_id the secret id to list. Can be NULL.
|
|
+ * @param secret_name the secret name to list. Can be NULL. If the id
|
|
+ * parameter is non-NULL, then this parameter is
|
|
+ * ignored.
|
|
+ * @param verbose if true, verbose messages are printed
|
|
+ *
|
|
+ * @returns 0 on success, a negative errno in case of an error
|
|
+ */
|
|
+int pvsecrets_list(int uv_fd, bool all, bool hex, const char *type_filter,
|
|
+ const char *secret_id, const char *secret_name,
|
|
+ bool verbose)
|
|
+{
|
|
+ struct list_secrets_data list_data = { 0 };
|
|
+ int rc;
|
|
+
|
|
+ util_assert(uv_fd != -1, "Internal error: uv_fd is -1");
|
|
+
|
|
+ list_data.all = all;
|
|
+ list_data.hex = hex;
|
|
+ list_data.type_filter = UV_SECRET_TYPE_INVALID;
|
|
+
|
|
+ if (type_filter != NULL) {
|
|
+ list_data.type_filter = get_pvsecret_type_by_name(type_filter);
|
|
+ if (list_data.type_filter == UV_SECRET_TYPE_INVALID) {
|
|
+ warnx("Invalid pvsecret type specified: %s",
|
|
+ type_filter);
|
|
+ return -EINVAL;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ if (secret_id != NULL || secret_name != NULL) {
|
|
+ rc = get_secret_id_from_hex_or_name(secret_id, secret_name,
|
|
+ list_data.id,
|
|
+ list_data.name);
|
|
+ if (rc < 0)
|
|
+ return rc;
|
|
+
|
|
+ list_data.id_filter = true;
|
|
+ }
|
|
+
|
|
+ list_data.rec = util_rec_new_wide("-");
|
|
+ util_rec_def(list_data.rec, PVSECRETS_REC_ID, UTIL_REC_ALIGN_LEFT,
|
|
+ UV_SECRET_ID_LEN * 2, PVSECRETS_REC_ID);
|
|
+ util_rec_def(list_data.rec, PVSECRETS_REC_TYPE, UTIL_REC_ALIGN_LEFT,
|
|
+ 12, PVSECRETS_REC_TYPE);
|
|
+
|
|
+ rc = uv_list_secrets(uv_fd, pvsecrets_list_cb, &list_data, verbose);
|
|
+ if (rc != 0) {
|
|
+ warnx("Failed to list protected virtualization secrets: %s",
|
|
+ strerror(-rc));
|
|
+ }
|
|
+
|
|
+ util_rec_free(list_data.rec);
|
|
+
|
|
+ if (list_data.matched == 0)
|
|
+ rc = -ENOENT;
|
|
+
|
|
+ return rc;
|
|
+}
|
|
diff --git a/zkey/pvsecrets.h b/zkey/pvsecrets.h
|
|
index 2667e859..6acebfdd 100644
|
|
--- a/zkey/pvsecrets.h
|
|
+++ b/zkey/pvsecrets.h
|
|
@@ -86,4 +86,8 @@ struct uvio_list_secrets {
|
|
|
|
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);
|
|
+
|
|
#endif
|
|
diff --git a/zkey/zkey.1 b/zkey/zkey.1
|
|
index 8c5a09a7..4386629f 100644
|
|
--- a/zkey/zkey.1
|
|
+++ b/zkey/zkey.1
|
|
@@ -1,8 +1,8 @@
|
|
-.\" Copyright IBM Corp. 2017, 2020
|
|
+.\" Copyright IBM Corp. 2017, 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.
|
|
.\"
|
|
-.TH ZKEY 1 "July 2020" "s390-tools"
|
|
+.TH ZKEY 1 "February 2024" "s390-tools"
|
|
.SH NAME
|
|
zkey \- Manage secure AES keys
|
|
.
|
|
@@ -1162,6 +1162,54 @@ fails. Use option \fB\-\-no\-volume\-check\fP to omit the volume check, and
|
|
refresh the keys even if the associated volume(s) do not exist.
|
|
.
|
|
.
|
|
+.SH COMMANDS FOR PROTECTED VIRTUALIZATION
|
|
+.
|
|
+Use the \fBpvsecrets\fP command to work with protected virtualization (PV)
|
|
+secrets. Protected virtualization secrets can be made available to a secure
|
|
+execution guest and can be used only within that guest.
|
|
+The \fBpvsecrets\fP command provides subcommands for protected
|
|
+virtualization specific operations. Use \fBzkey pvsecrets \-\-help\fP to show
|
|
+the available subcommands. These subcommands only work when running in a
|
|
+secure execution guest. Only the \fBroot\fP user is allowed to perform these
|
|
+subcommands.
|
|
+.
|
|
+.SS "List available protected virtualization secrets"
|
|
+.
|
|
+.B zkey pvsecrets
|
|
+.BR list | li
|
|
+.RB [ \-\-all | \-A ]
|
|
+.RB [ \-\-hex | \-H ]
|
|
+.RB [ \-\-pvsecret\-type | \-T
|
|
+.IR pvsecret\-type ]
|
|
+.RB [ \-\-pvsecret\-id | \-I
|
|
+.IR pvsecret\-id ]
|
|
+.RB [ \-\-pvsecret\-name | \-e
|
|
+.IR pvsecret\-name ]
|
|
+.RB [ \-\-verbose | \-V ]
|
|
+.
|
|
+.PP
|
|
+Use the
|
|
+.B pvsecrets list
|
|
+command to display a list of protected virtualization (PV) secrets. It displays
|
|
+the pvsecret ID as hex string of 32 bytes or as printable name enclosed in
|
|
+single quotes (\fB'\fP), if the pvsecret ID consists of only printable
|
|
+characters. Specify the \fB\-\-hex\fP option to list all pvsecret IDs as hex
|
|
+string. The
|
|
+.B pvsecrets list
|
|
+command also shows the pvsecret type of each secret.
|
|
+.PP
|
|
+You can filter the list of pvsecrets by pvsecret ID, pvsecret name and pvsecret
|
|
+type. Either the \fB\-\-pvsecret\-id\fP option or the
|
|
+\fB\-\-pvsecret\-name\fP option can be specified. By default the
|
|
+\fBpvsecrets list\fP command displays only those pvsecrets with types that
|
|
+are supported by the \fBzkey\fP tool. To list all pvsecret types, specify the
|
|
+\fB\-\-all\fP option.
|
|
+.PP
|
|
+This command is only available when running in a secure execution guest.
|
|
+Only the \fBroot\fP user is allowed to perform this command.
|
|
+.
|
|
+.
|
|
+.
|
|
.
|
|
.SH OPTIONS
|
|
.SS "Options for the generate command"
|
|
@@ -2030,6 +2078,45 @@ repository. This option only has an effect when specified together with option
|
|
.
|
|
.
|
|
.
|
|
+.SS "Options for the pvsecrets list command"
|
|
+.TP
|
|
+.BR \-A ", " \-\-all
|
|
+List all protected virtualization (PV) secret types, not only those that can be
|
|
+used with zkey.
|
|
+.TP
|
|
+.BR \-H ", " \-\-hex
|
|
+Show all protected virtualization (PV) secret IDs in hex, even if the ID
|
|
+contains only printable characters.
|
|
+.TP
|
|
+.BR \-T ", " \-\-pvsecret\-type\~\fIpvsecret\-type\fP
|
|
+Type of the protected virtualization (PV) secret to list. If omitted, all
|
|
+secret types are listed. Possible values are: \fBPLAIN\-TEXT\fP, \fBAES\-128\fP,
|
|
+\fBAES\-192\fP, \fBAES\-256\fP, \fBAES\-XTS\-128\fP, \fBAES\-XTS\-256\fP,
|
|
+\fBHMAC\-SHA\-256\fP, \fBHMAC\-SHA\-512\fP, \fBECDSA\-P256\fP,
|
|
+\fBECDSA\-P384\fP, \fBECDSA\-P521\fP, \fBEDDSA\-ED25519\fP, and
|
|
+\fBEDDSA\-ED448\fP.
|
|
+.TP
|
|
+.BR \-I ", " \-\-pvsecret\-id\~\fIpvsecret\-id\fP
|
|
+ID of the protected virtualization (PV) secret to list. 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 list. 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.
|
|
+.
|
|
+.
|
|
+.
|
|
.SS "General options"
|
|
.TP
|
|
.BR \-V ", " \-\-verbose
|
|
diff --git a/zkey/zkey.c b/zkey/zkey.c
|
|
index 7c909ff0..adc48d60 100644
|
|
--- a/zkey/zkey.c
|
|
+++ b/zkey/zkey.c
|
|
@@ -1,7 +1,7 @@
|
|
/*
|
|
* zkey - Generate, re-encipher, and validate secure keys
|
|
*
|
|
- * Copyright IBM Corp. 2017, 2020
|
|
+ * Copyright IBM Corp. 2017, 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.
|
|
@@ -34,6 +34,7 @@
|
|
#include "pkey.h"
|
|
#include "utils.h"
|
|
#include "kms.h"
|
|
+#include "pvsecrets.h"
|
|
|
|
/*
|
|
* Program configuration
|
|
@@ -46,7 +47,7 @@ static const struct util_prg prg = {
|
|
{
|
|
.owner = "IBM Corp.",
|
|
.pub_first = 2017,
|
|
- .pub_last = 2020,
|
|
+ .pub_last = 2024,
|
|
},
|
|
UTIL_PRG_COPYRIGHT_END
|
|
}
|
|
@@ -93,10 +94,16 @@ static struct zkey_globals {
|
|
bool open;
|
|
bool format;
|
|
bool refresh_properties;
|
|
+ bool all;
|
|
+ bool hex;
|
|
+ char *secret_type;
|
|
+ char *secret_id;
|
|
+ char *secret_name;
|
|
struct ext_lib lib;
|
|
struct cca_lib cca;
|
|
struct ep11_lib ep11;
|
|
int pkey_fd;
|
|
+ int uv_fd;
|
|
struct keystore *keystore;
|
|
struct kms_info kms_info;
|
|
int first_kms_option;
|
|
@@ -104,6 +111,7 @@ static struct zkey_globals {
|
|
size_t num_kms_options;
|
|
} g = {
|
|
.pkey_fd = -1,
|
|
+ .uv_fd = -1,
|
|
.sector_size = -1,
|
|
.lib.cca = &g.cca,
|
|
.lib.ep11 = &g.ep11,
|
|
@@ -135,6 +143,8 @@ static struct zkey_globals {
|
|
#define COMMAND_KMS_LIST "list"
|
|
#define COMMAND_KMS_IMPORT "import"
|
|
#define COMMAND_KMS_REFRESH "refresh"
|
|
+#define COMMAND_PVSECRETS "pvsecrets"
|
|
+#define COMMAND_PVSECRETS_LIST "list"
|
|
|
|
#define OPT_COMMAND_PLACEHOLDER "PLACEHOLDER"
|
|
|
|
@@ -1182,6 +1192,48 @@ static struct util_opt opt_vec[] = {
|
|
.flags = UTIL_OPT_FLAG_NOSHORT,
|
|
},
|
|
/***********************************************************/
|
|
+ {
|
|
+ .flags = UTIL_OPT_FLAG_SECTION,
|
|
+ .desc = "OPTIONS",
|
|
+ .command = COMMAND_PVSECRETS " " COMMAND_PVSECRETS_LIST,
|
|
+ },
|
|
+ {
|
|
+ .option = {"all", 0, NULL, 'A'},
|
|
+ .desc = "List all protected virtualization (PV) secret types, "
|
|
+ "not only those that can be used with zkey.",
|
|
+ .command = COMMAND_PVSECRETS " " COMMAND_PVSECRETS_LIST,
|
|
+ },
|
|
+ {
|
|
+ .option = {"hex", 0, NULL, 'H'},
|
|
+ .desc = "Show all protected virtualization (PV) secret IDs in "
|
|
+ "hex, even if the ID contains only printable "
|
|
+ "characters.",
|
|
+ .command = COMMAND_PVSECRETS " " COMMAND_PVSECRETS_LIST,
|
|
+ },
|
|
+ {
|
|
+ .option = { "pvsecret-type", required_argument, NULL, 'T'},
|
|
+ .argument = "PVSECRET-TYPE",
|
|
+ .desc = "Type of the protected virtualization (PV) secret to "
|
|
+ "list. If omitted, all pvsecret types are listed.",
|
|
+ .command = COMMAND_PVSECRETS " " COMMAND_PVSECRETS_LIST,
|
|
+ },
|
|
+ {
|
|
+ .option = { "pvsecret-id", required_argument, NULL, 'I'},
|
|
+ .argument = "PVSECRET-ID",
|
|
+ .desc = "ID of the protected virtualization (PV) secret to "
|
|
+ "list. Either '--pvsecret-id/-I' or "
|
|
+ "'--pvsecret-name/-e' can be specified, but not both.",
|
|
+ .command = COMMAND_PVSECRETS " " COMMAND_PVSECRETS_LIST,
|
|
+ },
|
|
+ {
|
|
+ .option = { "pvsecret-name", required_argument, NULL, 'e'},
|
|
+ .argument = "PVSECRET-NAME",
|
|
+ .desc = "Name of the protected virtualization (PV) secret to "
|
|
+ "list. Either '--pvsecret-name/-e' or "
|
|
+ "'--pvsecret-id/-I' can be specified, but not both.",
|
|
+ .command = COMMAND_PVSECRETS " " COMMAND_PVSECRETS_LIST,
|
|
+ },
|
|
+ /***********************************************************/
|
|
OPT_PLACEHOLDER,
|
|
OPT_PLACEHOLDER,
|
|
OPT_PLACEHOLDER,
|
|
@@ -1249,6 +1301,7 @@ struct zkey_command {
|
|
int need_cca_library;
|
|
int need_ep11_library;
|
|
int need_pkey_device;
|
|
+ int need_uv_device;
|
|
char *short_desc;
|
|
char *long_desc;
|
|
int has_options;
|
|
@@ -1285,6 +1338,7 @@ static int command_kms_reencipher(void);
|
|
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 struct zkey_command zkey_kms_commands[] = {
|
|
{
|
|
@@ -1402,6 +1456,22 @@ static struct zkey_command zkey_kms_commands[] = {
|
|
{ .command = NULL }
|
|
};
|
|
|
|
+static struct zkey_command zkey_pvsecrets_commands[] = {
|
|
+ {
|
|
+ .command = COMMAND_PVSECRETS_LIST,
|
|
+ .abbrev_len = 2,
|
|
+ .function = command_pvsecrets_list,
|
|
+ .short_desc = "Lists protected virtualization (PV) secrets",
|
|
+ .long_desc = "Lists available protected virtualization (PV) "
|
|
+ "secrets. This command is only available when "
|
|
+ "running in a secure execution guest. Only the "
|
|
+ "'root' user is allowed to perform this command",
|
|
+ .has_options = 1,
|
|
+ .need_uv_device = 1,
|
|
+ },
|
|
+ { .command = NULL }
|
|
+};
|
|
+
|
|
static struct zkey_command zkey_commands[] = {
|
|
{
|
|
.command = COMMAND_GENERATE,
|
|
@@ -1565,6 +1635,21 @@ static struct zkey_command zkey_commands[] = {
|
|
.has_options = 1,
|
|
.sub_commands = zkey_kms_commands,
|
|
},
|
|
+ {
|
|
+ .command = COMMAND_PVSECRETS,
|
|
+ .abbrev_len = 2,
|
|
+ .short_desc = "Protected virtualization (PV) support",
|
|
+ .long_desc = "Provides subcommands for working with protected "
|
|
+ "virtualization (PV) secrets. Protected "
|
|
+ "virtualization secrets can be made available to "
|
|
+ "a secure execution guest and can be used only "
|
|
+ "within that guest. Thus, these subcommands are "
|
|
+ "only available when running in a secure "
|
|
+ "execution guest. Only the 'root' user is allowed "
|
|
+ "to perform these subcommands.",
|
|
+ .has_options = 1,
|
|
+ .sub_commands = zkey_pvsecrets_commands,
|
|
+ },
|
|
{ .command = NULL }
|
|
};
|
|
|
|
@@ -2899,6 +2984,28 @@ static int command_kms_refresh(void)
|
|
return rc != 0 ? EXIT_FAILURE : EXIT_SUCCESS;
|
|
}
|
|
|
|
+/*
|
|
+ * Command handler for 'pvsecrets list'.
|
|
+ *
|
|
+ * Lists available protected virtualization secrets
|
|
+ */
|
|
+static int command_pvsecrets_list(void)
|
|
+{
|
|
+ int rc;
|
|
+
|
|
+ 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;
|
|
+ }
|
|
+
|
|
+ rc = pvsecrets_list(g.uv_fd, g.all, g.hex, g.secret_type, g.secret_id,
|
|
+ g.secret_name, 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
|
|
@@ -3234,6 +3341,21 @@ int main(int argc, char *argv[])
|
|
case OPT_REMOVE_DUMMY_PASSPHRASE:
|
|
g.remove_passphrase = 1;
|
|
break;
|
|
+ case 'A':
|
|
+ g.all = 1;
|
|
+ break;
|
|
+ case 'H':
|
|
+ g.hex = 1;
|
|
+ break;
|
|
+ case 'T':
|
|
+ g.secret_type = optarg;
|
|
+ break;
|
|
+ case 'I':
|
|
+ g.secret_id = optarg;
|
|
+ break;
|
|
+ case 'e':
|
|
+ g.secret_name = optarg;
|
|
+ break;
|
|
case 'h':
|
|
print_help(command, sub_command);
|
|
return EXIT_SUCCESS;
|
|
@@ -3294,6 +3416,13 @@ int main(int argc, char *argv[])
|
|
goto out;
|
|
}
|
|
}
|
|
+ if (cmd->need_uv_device) {
|
|
+ g.uv_fd = uv_open_device(g.verbose);
|
|
+ if (g.uv_fd == -1) {
|
|
+ rc = EXIT_FAILURE;
|
|
+ goto out;
|
|
+ }
|
|
+ }
|
|
|
|
if (g.kms_info.plugin_lib != NULL) {
|
|
rc = init_kms_plugin(&g.kms_info, g.verbose);
|
|
@@ -3323,6 +3452,8 @@ int main(int argc, char *argv[])
|
|
dlclose(g.ep11.lib_ep11);
|
|
if (g.pkey_fd >= 0)
|
|
close(g.pkey_fd);
|
|
+ if (g.uv_fd >= 0)
|
|
+ close(g.uv_fd);
|
|
if (g.keystore)
|
|
keystore_free(g.keystore);
|
|
if (g.kms_options != NULL)
|