Accepting request 1241295 from Base:System
OBS-URL: https://build.opensuse.org/request/show/1241295 OBS-URL: https://build.opensuse.org/package/show/openSUSE:Factory/s390-tools?expand=0&rev=89
This commit is contained in:
commit
8474887a05
@ -0,0 +1,299 @@
|
||||
From 8c4b2872b8e24c1a27d8201beb5979c66ac05268 Mon Sep 17 00:00:00 2001
|
||||
From: Ingo Franzki <ifranzki@linux.ibm.com>
|
||||
Date: Thu, 15 Feb 2024 09:08:43 +0100
|
||||
Subject: [PATCH] zkey: Add support for retrieving a list of ultravisor secrets
|
||||
|
||||
Add functions to interface with the ultravisor device (/dev/uv) when
|
||||
running in a secure execution guest to retrieve a list of available
|
||||
secrets.
|
||||
|
||||
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/Makefile | 4 +-
|
||||
zkey/pvsecrets.c | 161 +++++++++++++++++++++++++++++++++++++++++++++++
|
||||
zkey/pvsecrets.h | 89 ++++++++++++++++++++++++++
|
||||
3 files changed, 253 insertions(+), 1 deletion(-)
|
||||
create mode 100644 zkey/pvsecrets.c
|
||||
create mode 100644 zkey/pvsecrets.h
|
||||
|
||||
diff --git a/zkey/Makefile b/zkey/Makefile
|
||||
index 501c5ebf..cbecf125 100644
|
||||
--- a/zkey/Makefile
|
||||
+++ b/zkey/Makefile
|
||||
@@ -92,9 +92,11 @@ keystore.o: keystore.c keystore.h properties.h pkey.h cca.h ep11.h utils.h
|
||||
zkey-cryptsetup.o: check-dep-zkey-cryptsetup zkey-cryptsetup.c pkey.h cca.h \
|
||||
ep11.h misc.h utils.h
|
||||
kms.o: kms.c kms.h kms-plugin.h utils.h pkey.h
|
||||
+pvsecrets.o: pvsecrets.h
|
||||
|
||||
zkey: LDLIBS = -ldl -lcrypto
|
||||
-zkey: zkey.o pkey.o cca.o ep11.o properties.o keystore.o utils.o kms.o $(libs)
|
||||
+zkey: zkey.o pkey.o cca.o ep11.o properties.o keystore.o utils.o kms.o \
|
||||
+ pvsecrets.o $(libs)
|
||||
$(LINK) $(ALL_LDFLAGS) $^ $(LDLIBS) -o $@
|
||||
|
||||
zkey-cryptsetup: LDLIBS = -ldl -lcryptsetup -ljson-c -lcrypto
|
||||
diff --git a/zkey/pvsecrets.c b/zkey/pvsecrets.c
|
||||
new file mode 100644
|
||||
index 00000000..2874fdf1
|
||||
--- /dev/null
|
||||
+++ b/zkey/pvsecrets.c
|
||||
@@ -0,0 +1,161 @@
|
||||
+/*
|
||||
+ * zkey - Generate, re-encipher, and validate secure keys
|
||||
+ *
|
||||
+ * Copyright IBM Corp. 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.
|
||||
+ */
|
||||
+
|
||||
+#include <err.h>
|
||||
+#include <errno.h>
|
||||
+#include <fcntl.h>
|
||||
+#include <stdbool.h>
|
||||
+#include <string.h>
|
||||
+#include <stdint.h>
|
||||
+#include <sys/ioctl.h>
|
||||
+#include <sys/types.h>
|
||||
+#include <unistd.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 "pvsecrets.h"
|
||||
+
|
||||
+#define pr_verbose(verbose, fmt...) do { \
|
||||
+ if (verbose) \
|
||||
+ warnx(fmt); \
|
||||
+ } while (0)
|
||||
+
|
||||
+/**
|
||||
+ * Opens the ultravisor device and returns its file descriptor.
|
||||
+ * This only succeeds when running in a secure execution guest.
|
||||
+ * A failure of this function indicates that it is not running in a secure
|
||||
+ * execution guest.
|
||||
+ *
|
||||
+ * @param verbose if true, verbose messages are printed
|
||||
+ *
|
||||
+ * @returns the file descriptor or -1 to indicate an error
|
||||
+ */
|
||||
+int uv_open_device(bool verbose)
|
||||
+{
|
||||
+ unsigned int pvguest = 0, max_retr_secrets = 0;
|
||||
+ char *path = NULL;
|
||||
+ int uv_fd, err;
|
||||
+
|
||||
+ uv_fd = open(UVDEVICE, O_RDWR);
|
||||
+ if (uv_fd < 0) {
|
||||
+ err = errno;
|
||||
+ warnx("File '%s:' %s\n", UVDEVICE, strerror(errno));
|
||||
+ if (err == EACCES)
|
||||
+ warnx("Only the 'root' user is allowed to perform "
|
||||
+ "this command");
|
||||
+ else
|
||||
+ warnx("Ensure that you are running in a secure "
|
||||
+ "execution guest, and that the 'uvdevice' "
|
||||
+ "kernel module is loaded.");
|
||||
+ return -1;
|
||||
+ }
|
||||
+
|
||||
+ path = util_path_sysfs(SYSFS_UV);
|
||||
+ if (util_file_read_ui(&pvguest, 10, SYSFS_UV_PV_GUEST, path) != 0 ||
|
||||
+ pvguest != 1) {
|
||||
+ warnx("You are not running in a secure execution guest.");
|
||||
+ goto error;
|
||||
+ }
|
||||
+
|
||||
+ if (util_file_read_ui(&max_retr_secrets, 10, SYSFS_UV_MAX_SECRETS,
|
||||
+ path) != 0 ||
|
||||
+ max_retr_secrets == 0) {
|
||||
+ warnx("The ultravisor device is at a too old version, or "
|
||||
+ "the ultravisor does not support retrievable secrets.");
|
||||
+ goto error;
|
||||
+ }
|
||||
+ free(path);
|
||||
+
|
||||
+ pr_verbose(verbose, "Device '%s' has been opened successfully",
|
||||
+ UVDEVICE);
|
||||
+ return uv_fd;
|
||||
+
|
||||
+error:
|
||||
+ free(path);
|
||||
+ close(uv_fd);
|
||||
+
|
||||
+ return -1;
|
||||
+}
|
||||
+
|
||||
+/**
|
||||
+ * Retrieves a list of secrets from the ultravisor. Calls the supplied callback
|
||||
+ * function for each secret found.
|
||||
+ *
|
||||
+ * @param uv_fd the file descriptor of the ultravisor device
|
||||
+ * @param cb the callback function
|
||||
+ * @param cb_private private data to pass to the callback function
|
||||
+ * @param verbose if true, verbose messages are printed
|
||||
+ *
|
||||
+ * @returns 0 on success, a negative errno in case of an error
|
||||
+ */
|
||||
+static int uv_list_secrets(int uv_fd, int (*cb)(u16 idx, u16 type, u32 len,
|
||||
+ const u8 id[UV_SECRET_ID_LEN],
|
||||
+ void *cb_private),
|
||||
+ void *cb_private, bool verbose)
|
||||
+{
|
||||
+ struct uvio_list_secrets *list;
|
||||
+ struct uvio_ioctl_cb io;
|
||||
+ unsigned int i;
|
||||
+ int rc;
|
||||
+
|
||||
+ util_assert(uv_fd != -1, "Internal error: uv_fd is -1");
|
||||
+ util_assert(cb != NULL, "Internal error: cb is NULL");
|
||||
+
|
||||
+ list = util_zalloc(UVIO_LIST_SECRETS_MAX_LEN);
|
||||
+
|
||||
+ memset(&io, 0, sizeof(io));
|
||||
+ io.argument_addr = list;
|
||||
+ io.argument_len = UVIO_LIST_SECRETS_MAX_LEN;
|
||||
+
|
||||
+ rc = ioctl(uv_fd, UVIO_IOCTL_LIST_SECRETS, &io);
|
||||
+ if (rc != 0) {
|
||||
+ rc = -errno;
|
||||
+
|
||||
+ pr_verbose(verbose, "ioctl UVIO_IOCTL_LIST_SECRETS: %s",
|
||||
+ strerror(-rc));
|
||||
+
|
||||
+ if (rc == -ENOTTY || rc == -EINVAL)
|
||||
+ warnx("The ultravisor device is at a too old version");
|
||||
+
|
||||
+ goto out;
|
||||
+ }
|
||||
+
|
||||
+ if (io.uv_rc != UVIO_RC_SUCCESS) {
|
||||
+ pr_verbose(verbose, "ioctl UVIO_IOCTL_LIST_SECRETS' uv_rc: %u",
|
||||
+ io.uv_rc);
|
||||
+ rc = -EIO;
|
||||
+ goto out;
|
||||
+ }
|
||||
+
|
||||
+ pr_verbose(verbose, "Number of secrets: %u", list->num_secrets_stored);
|
||||
+
|
||||
+ for (i = 0; i < list->num_secrets_stored &&
|
||||
+ i < ARRAY_SIZE(list->secret_entries); i++) {
|
||||
+ if (list->secret_entries[i].secret_type <=
|
||||
+ UV_SECRET_TYPE_AP_ASSOCIATION)
|
||||
+ continue;
|
||||
+
|
||||
+ rc = cb(list->secret_entries[i].secret_idx,
|
||||
+ list->secret_entries[i].secret_type,
|
||||
+ list->secret_entries[i].secret_len,
|
||||
+ list->secret_entries[i].secret_id,
|
||||
+ cb_private);
|
||||
+ if (rc != 0)
|
||||
+ break;
|
||||
+ }
|
||||
+
|
||||
+out:
|
||||
+ free(list);
|
||||
+
|
||||
+ return rc;
|
||||
+}
|
||||
diff --git a/zkey/pvsecrets.h b/zkey/pvsecrets.h
|
||||
new file mode 100644
|
||||
index 00000000..2667e859
|
||||
--- /dev/null
|
||||
+++ b/zkey/pvsecrets.h
|
||||
@@ -0,0 +1,89 @@
|
||||
+/*
|
||||
+ * zkey - Generate, re-encipher, and validate secure keys
|
||||
+ *
|
||||
+ * This header file defines functions for the PV secrets support as well
|
||||
+ * as the interface to the uv kernel module.
|
||||
+ *
|
||||
+ * Copyright IBM Corp. 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.
|
||||
+ */
|
||||
+
|
||||
+#ifndef PVSECRETS_H
|
||||
+#define PVSECRETS_H
|
||||
+
|
||||
+#include "lib/zt_common.h"
|
||||
+
|
||||
+/*
|
||||
+ * Definitions for the /dev/uv kernel module interface
|
||||
+ */
|
||||
+#define UVDEVICE "/dev/uv"
|
||||
+#define SYSFS_UV "firmware/uv"
|
||||
+#define SYSFS_UV_PV_GUEST "%s/prot_virt_guest"
|
||||
+#define SYSFS_UV_MAX_SECRETS "%s/query/max_retr_secrets"
|
||||
+
|
||||
+struct uvio_ioctl_cb {
|
||||
+ u32 flags;
|
||||
+ u16 uv_rc; /* UV header rc value */
|
||||
+ u16 uv_rrc; /* UV header rrc value */
|
||||
+ void *argument_addr; /* Userspace address of uvio argument */
|
||||
+ u32 argument_len;
|
||||
+ u8 reserved14[0x40 - 0x14]; /* must be zero */
|
||||
+};
|
||||
+
|
||||
+#define UVIO_IOCTL_LIST_SECRETS_NR 3
|
||||
+
|
||||
+#define UVIO_TYPE_UVC 'u'
|
||||
+#define UVIO_IOCTL(nr) _IOWR(UVIO_TYPE_UVC, \
|
||||
+ nr, struct uvio_ioctl_cb)
|
||||
+#define UVIO_IOCTL_LIST_SECRETS UVIO_IOCTL( \
|
||||
+ UVIO_IOCTL_LIST_SECRETS_NR)
|
||||
+
|
||||
+#define UVIO_RC_SUCCESS 0x0001
|
||||
+#define UVIO_RC_MORE_DATA 0x0100
|
||||
+
|
||||
+#define UV_SECRET_TYPE_INVALID 0x00
|
||||
+#define UV_SECRET_TYPE_NULL 0x01
|
||||
+#define UV_SECRET_TYPE_AP_ASSOCIATION 0x02
|
||||
+#define UV_SECRET_TYPE_PLAIN_TEXT 0x03
|
||||
+#define UV_SECRET_TYPE_AES_128 0x04
|
||||
+#define UV_SECRET_TYPE_AES_192 0x05
|
||||
+#define UV_SECRET_TYPE_AES_256 0x06
|
||||
+#define UV_SECRET_TYPE_AES_XTS_128 0x07
|
||||
+#define UV_SECRET_TYPE_AES_XTS_256 0x08
|
||||
+#define UV_SECRET_TYPE_HMAC_SHA_256 0x09
|
||||
+#define UV_SECRET_TYPE_HMAC_SHA_512 0x0a
|
||||
+#define UV_SECRET_TYPE_ECDSA_P256 0x11
|
||||
+#define UV_SECRET_TYPE_ECDSA_P384 0x12
|
||||
+#define UV_SECRET_TYPE_ECDSA_P521 0x13
|
||||
+#define UV_SECRET_TYPE_EDDSA_ED25519 0x14
|
||||
+#define UV_SECRET_TYPE_EDDSA_ED448 0x15
|
||||
+
|
||||
+#define UV_SECRET_ID_LEN 32
|
||||
+
|
||||
+#define UVIO_LIST_SECRETS_MAX_LEN 0x8000
|
||||
+
|
||||
+struct uvio_list_secret_entry {
|
||||
+ u16 secret_idx;
|
||||
+ u16 secret_type;
|
||||
+ u32 secret_len;
|
||||
+ u64 reserved;
|
||||
+ u8 secret_id[UV_SECRET_ID_LEN];
|
||||
+} __packed;
|
||||
+
|
||||
+#define UVIO_MAX_SECRET_ENTRIES ((UVIO_LIST_SECRETS_MAX_LEN - 16) / \
|
||||
+ sizeof(struct uvio_list_secret_entry))
|
||||
+
|
||||
+struct uvio_list_secrets {
|
||||
+ u16 num_secrets_stored;
|
||||
+ u16 num_secrets_total;
|
||||
+ u16 next_secret_idx;
|
||||
+ u16 reserved1;
|
||||
+ u64 reserved2;
|
||||
+ struct uvio_list_secret_entry secret_entries[UVIO_MAX_SECRET_ENTRIES];
|
||||
+} __packed;
|
||||
+
|
||||
+int uv_open_device(bool verbose);
|
||||
+
|
||||
+#endif
|
823
s390-tools-02-zkey-Add-the--pvsecrets-list-command.patch
Normal file
823
s390-tools-02-zkey-Add-the--pvsecrets-list-command.patch
Normal file
@ -0,0 +1,823 @@
|
||||
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)
|
340
s390-tools-03-zkey-Add-PVSECRETS-AES-key-type.patch
Normal file
340
s390-tools-03-zkey-Add-PVSECRETS-AES-key-type.patch
Normal file
@ -0,0 +1,340 @@
|
||||
From fdf66dc148c09f1d3300cd3378e3f75a83c6214e Mon Sep 17 00:00:00 2001
|
||||
From: Ingo Franzki <ifranzki@linux.ibm.com>
|
||||
Date: Thu, 15 Feb 2024 16:56:04 +0100
|
||||
Subject: [PATCH] zkey: Add PVSECRETS-AES key type
|
||||
|
||||
Add the definitions and utility functions for the PVSECRETS-AES key type.
|
||||
A PVSECRETS-AES key token contains the secret id of a protected
|
||||
virtualization secret. It does not contain the key material, just a
|
||||
reference to the key in the ultravisor.
|
||||
|
||||
When such a key token is used to perform crypto operations later on, the
|
||||
PAES kernel cipher will obtain the protected key belonging to this secret
|
||||
id with the help of the pkey kernel module.
|
||||
|
||||
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 | 4 +-
|
||||
zkey/pkey.c | 110 ++++++++++++++++++++++++++++++++++++++++++++++-
|
||||
zkey/pkey.h | 38 ++++++++++++----
|
||||
zkey/pvsecrets.h | 4 +-
|
||||
4 files changed, 143 insertions(+), 13 deletions(-)
|
||||
|
||||
diff --git a/zkey/keystore.c b/zkey/keystore.c
|
||||
index 9589d82d..771bc08d 100644
|
||||
--- a/zkey/keystore.c
|
||||
+++ b/zkey/keystore.c
|
||||
@@ -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.
|
||||
@@ -360,6 +360,8 @@ static int _keystore_valid_key_type(const char *key_type)
|
||||
return 1;
|
||||
if (strcasecmp(key_type, KEY_TYPE_EP11_AES) == 0)
|
||||
return 1;
|
||||
+ if (strcasecmp(key_type, KEY_TYPE_PVSECRET_AES) == 0)
|
||||
+ return 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
diff --git a/zkey/pkey.c b/zkey/pkey.c
|
||||
index 821978bd..53c0a550 100644
|
||||
--- a/zkey/pkey.c
|
||||
+++ b/zkey/pkey.c
|
||||
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
* zkey - Generate, re-encipher, and validate secure keys
|
||||
*
|
||||
- * Copyright IBM Corp. 2018
|
||||
+ * 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.
|
||||
@@ -26,6 +26,7 @@
|
||||
#include "lib/util_panic.h"
|
||||
|
||||
#include "pkey.h"
|
||||
+#include "pvsecrets.h"
|
||||
#include "utils.h"
|
||||
|
||||
#ifndef AF_ALG
|
||||
@@ -1719,6 +1720,38 @@ bool is_ep11_key_session_bound(const u8 *key, size_t key_size)
|
||||
}
|
||||
}
|
||||
|
||||
+/**
|
||||
+ * Check if the specified key is a PVSECRET-AES key token.
|
||||
+ *
|
||||
+ * @param[in] key the secure key token
|
||||
+ * @param[in] key_size the size of the secure key
|
||||
+ *
|
||||
+ * @returns true if the key is a PVSECRET token type
|
||||
+ */
|
||||
+bool is_pvsecret_aes_key(const u8 *key, size_t key_size)
|
||||
+{
|
||||
+ struct pvsecrettoken *pvsecret = (struct pvsecrettoken *)key;
|
||||
+
|
||||
+ if (key == NULL || key_size < PVSECRET_KEY_SIZE)
|
||||
+ return false;
|
||||
+
|
||||
+ if (pvsecret->hdr.type != TOKEN_TYPE_NON_CCA)
|
||||
+ return false;
|
||||
+ if (pvsecret->hdr.version != TOKEN_VERSION_PVSECRET)
|
||||
+ return false;
|
||||
+
|
||||
+ switch (pvsecret->secret_type) {
|
||||
+ case UV_SECRET_TYPE_AES_128:
|
||||
+ case UV_SECRET_TYPE_AES_192:
|
||||
+ case UV_SECRET_TYPE_AES_256:
|
||||
+ case UV_SECRET_TYPE_AES_XTS_128:
|
||||
+ case UV_SECRET_TYPE_AES_XTS_256:
|
||||
+ return true;
|
||||
+ default:
|
||||
+ return false;
|
||||
+ }
|
||||
+}
|
||||
+
|
||||
/**
|
||||
* Check if the specified key is an XTS type key
|
||||
*
|
||||
@@ -1729,6 +1762,8 @@ bool is_ep11_key_session_bound(const u8 *key, size_t key_size)
|
||||
*/
|
||||
bool is_xts_key(const u8 *key, size_t key_size)
|
||||
{
|
||||
+ struct pvsecrettoken *pvsecret = (struct pvsecrettoken *)key;
|
||||
+
|
||||
if (is_cca_aes_data_key(key, key_size)) {
|
||||
if (key_size == 2 * AESDATA_KEY_SIZE &&
|
||||
is_cca_aes_data_key(key + AESDATA_KEY_SIZE,
|
||||
@@ -1749,11 +1784,41 @@ bool is_xts_key(const u8 *key, size_t key_size)
|
||||
is_ep11_aes_key_with_header(key + EP11_AES_KEY_SIZE,
|
||||
key_size - EP11_AES_KEY_SIZE))
|
||||
return true;
|
||||
+ } else if (is_pvsecret_aes_key(key, key_size)) {
|
||||
+ switch (pvsecret->secret_type) {
|
||||
+ case UV_SECRET_TYPE_AES_XTS_128:
|
||||
+ case UV_SECRET_TYPE_AES_XTS_256:
|
||||
+ return true;
|
||||
+ default:
|
||||
+ return false;
|
||||
+ }
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
+/**
|
||||
+ * Check if the specified key is a secure key type (i.e. requires a crypto card)
|
||||
+ *
|
||||
+ * @param[in] key the secure key token
|
||||
+ * @param[in] key_size the size of the secure key
|
||||
+ *
|
||||
+ * @returns true if the key is a secure key type
|
||||
+ */
|
||||
+bool is_secure_key(const u8 *key, size_t key_size)
|
||||
+{
|
||||
+ if (is_cca_aes_data_key(key, key_size))
|
||||
+ return true;
|
||||
+ if (is_cca_aes_cipher_key(key, key_size))
|
||||
+ return true;
|
||||
+ if (is_ep11_aes_key(key, key_size))
|
||||
+ return true;
|
||||
+ if (is_ep11_aes_key_with_header(key, key_size))
|
||||
+ return true;
|
||||
+
|
||||
+ return false;
|
||||
+}
|
||||
+
|
||||
/**
|
||||
* Gets the size in bits of the effective key of the specified secure key
|
||||
*
|
||||
@@ -1771,6 +1836,7 @@ int get_key_bit_size(const u8 *key, size_t key_size, size_t *bitsize)
|
||||
struct aescipherkeytoken *cipherkey = (struct aescipherkeytoken *)key;
|
||||
struct ep11keytoken *ep11key = (struct ep11keytoken *)key;
|
||||
struct ep11kblob_header *hdr = (struct ep11kblob_header *)key;
|
||||
+ struct pvsecrettoken *pvsecret = (struct pvsecrettoken *)key;
|
||||
|
||||
util_assert(bitsize != NULL, "Internal error: bitsize is NULL");
|
||||
|
||||
@@ -1805,6 +1871,26 @@ int get_key_bit_size(const u8 *key, size_t key_size, size_t *bitsize)
|
||||
(key + EP11_AES_KEY_SIZE);
|
||||
*bitsize += hdr->bitlen;
|
||||
}
|
||||
+ } else if (is_pvsecret_aes_key(key, key_size)) {
|
||||
+ switch (pvsecret->secret_type) {
|
||||
+ case UV_SECRET_TYPE_AES_128:
|
||||
+ *bitsize = 128;
|
||||
+ break;
|
||||
+ case UV_SECRET_TYPE_AES_192:
|
||||
+ *bitsize = 192;
|
||||
+ break;
|
||||
+ case UV_SECRET_TYPE_AES_256:
|
||||
+ *bitsize = 256;
|
||||
+ break;
|
||||
+ case UV_SECRET_TYPE_AES_XTS_128:
|
||||
+ *bitsize = 128 * 2;
|
||||
+ break;
|
||||
+ case UV_SECRET_TYPE_AES_XTS_256:
|
||||
+ *bitsize = 256 * 2;
|
||||
+ break;
|
||||
+ default:
|
||||
+ return -EINVAL;
|
||||
+ }
|
||||
} else {
|
||||
return -EINVAL;
|
||||
}
|
||||
@@ -1830,9 +1916,31 @@ const char *get_key_type(const u8 *key, size_t key_size)
|
||||
return KEY_TYPE_EP11_AES;
|
||||
if (is_ep11_aes_key_with_header(key, key_size))
|
||||
return KEY_TYPE_EP11_AES;
|
||||
+ if (is_pvsecret_aes_key(key, key_size))
|
||||
+ return KEY_TYPE_PVSECRET_AES;
|
||||
+
|
||||
return NULL;
|
||||
}
|
||||
|
||||
+/**
|
||||
+ * Returns true if the key type is a secure key type
|
||||
+ *
|
||||
+ * @param[in] key_type the type of the key
|
||||
+ *
|
||||
+ * @returns true if the key type is a secure key type, false otherwise
|
||||
+ */
|
||||
+bool is_secure_key_type(const char *key_type)
|
||||
+{
|
||||
+ if (strcasecmp(key_type, KEY_TYPE_CCA_AESCIPHER) == 0)
|
||||
+ return true;
|
||||
+ if (strcasecmp(key_type, KEY_TYPE_CCA_AESDATA) == 0)
|
||||
+ return true;
|
||||
+ if (strcasecmp(key_type, KEY_TYPE_EP11_AES) == 0)
|
||||
+ return true;
|
||||
+
|
||||
+ return false;
|
||||
+}
|
||||
+
|
||||
/**
|
||||
* Returns the minimum card level for a specific key type
|
||||
*
|
||||
diff --git a/zkey/pkey.h b/zkey/pkey.h
|
||||
index 3b57c5f0..ce3bd8bd 100644
|
||||
--- a/zkey/pkey.h
|
||||
+++ b/zkey/pkey.h
|
||||
@@ -4,7 +4,7 @@
|
||||
* This header file defines the interface to the pkey kernel module.
|
||||
* It defines a set of IOCTL commands with its associated structures.
|
||||
*
|
||||
- * Copyright IBM Corp. 2017, 2018
|
||||
+ * 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.
|
||||
@@ -41,6 +41,8 @@ struct tokenheader {
|
||||
#define TOKEN_VERSION_EP11_AES 0x03
|
||||
#define TOKEN_VERSION_EP11_AES_WITH_HEADER 0x06
|
||||
#define TOKEN_VERSION_EP11_ECC_WITH_HEADER 0x07
|
||||
+/* 0x08 is reserved for internal use */
|
||||
+#define TOKEN_VERSION_PVSECRET 0x09
|
||||
|
||||
struct aesdatakeytoken {
|
||||
u8 type; /* TOKEN_TYPE_INTERNAL (0x01) for internal key token */
|
||||
@@ -116,6 +118,15 @@ struct ep11keytoken {
|
||||
u8 padding[64];
|
||||
} __packed;
|
||||
|
||||
+#define UV_SECRET_ID_LEN 32
|
||||
+
|
||||
+struct pvsecrettoken {
|
||||
+ struct tokenheader hdr;
|
||||
+ u16 secret_type; /* the secret type as the UV told us */
|
||||
+ u16 secret_len; /* length in bytes of the secret */
|
||||
+ u8 secretid[UV_SECRET_ID_LEN]; /* the secret id for this secret */
|
||||
+} __packed;
|
||||
+
|
||||
#define ZERO_SESSION \
|
||||
"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
|
||||
|
||||
@@ -124,21 +135,26 @@ struct ep11keytoken {
|
||||
#define EP11_KEY_SIZE sizeof(struct ep11keytoken)
|
||||
#define EP11_AES_KEY_SIZE (sizeof(struct ep11kblob_header) + \
|
||||
sizeof(struct ep11keytoken))
|
||||
+#define PVSECRET_KEY_SIZE sizeof(struct pvsecrettoken)
|
||||
|
||||
/* MAX/MIN from zt_common.h produces warnings for variable length arrays */
|
||||
#define _MIN(a, b) ((a) < (b) ? (a) : (b))
|
||||
#define _MAX(a, b) ((a) > (b) ? (a) : (b))
|
||||
|
||||
#define MAX_SECURE_KEY_SIZE _MAX( \
|
||||
- _MAX(EP11_KEY_SIZE, \
|
||||
- EP11_AES_KEY_SIZE), \
|
||||
- _MAX(AESDATA_KEY_SIZE, \
|
||||
- AESCIPHER_KEY_SIZE))
|
||||
+ _MAX( \
|
||||
+ _MAX(EP11_KEY_SIZE, \
|
||||
+ EP11_AES_KEY_SIZE), \
|
||||
+ _MAX(AESDATA_KEY_SIZE, \
|
||||
+ AESCIPHER_KEY_SIZE)), \
|
||||
+ PVSECRET_KEY_SIZE)
|
||||
#define MIN_SECURE_KEY_SIZE _MIN( \
|
||||
- _MIN(EP11_KEY_SIZE, \
|
||||
- EP11_AES_KEY_SIZE), \
|
||||
- _MIN(AESDATA_KEY_SIZE, \
|
||||
- AESCIPHER_KEY_SIZE))
|
||||
+ _MIN( \
|
||||
+ _MIN(EP11_KEY_SIZE, \
|
||||
+ EP11_AES_KEY_SIZE), \
|
||||
+ _MIN(AESDATA_KEY_SIZE, \
|
||||
+ AESCIPHER_KEY_SIZE)), \
|
||||
+ PVSECRET_KEY_SIZE)
|
||||
|
||||
struct pkey_seckey {
|
||||
u8 seckey[AESDATA_KEY_SIZE]; /* the secure key blob */
|
||||
@@ -285,6 +301,7 @@ struct pkey_apqns4keytype {
|
||||
#define KEY_TYPE_CCA_AESDATA "CCA-AESDATA"
|
||||
#define KEY_TYPE_CCA_AESCIPHER "CCA-AESCIPHER"
|
||||
#define KEY_TYPE_EP11_AES "EP11-AES"
|
||||
+#define KEY_TYPE_PVSECRET_AES "PVSECRET-AES"
|
||||
|
||||
#define DEFAULT_KEYBITS 256
|
||||
#define PAES_BLOCK_SIZE 16
|
||||
@@ -342,9 +359,12 @@ bool is_cca_aes_cipher_key(const u8 *key, size_t key_size);
|
||||
bool is_ep11_aes_key(const u8 *key, size_t key_size);
|
||||
bool is_ep11_aes_key_with_header(const u8 *key, size_t key_size);
|
||||
bool is_ep11_key_session_bound(const u8 *key, size_t key_size);
|
||||
+bool is_pvsecret_aes_key(const u8 *key, size_t key_size);
|
||||
bool is_xts_key(const u8 *key, size_t key_size);
|
||||
+bool is_secure_key(const u8 *key, size_t key_size);
|
||||
int get_key_bit_size(const u8 *key, size_t key_size, size_t *bitsize);
|
||||
const char *get_key_type(const u8 *key, size_t key_size);
|
||||
+bool is_secure_key_type(const char *key_type);
|
||||
int get_min_card_level_for_keytype(const char *key_type);
|
||||
const struct fw_version *get_min_fw_version_for_keytype(const char *key_type);
|
||||
enum card_type get_card_type_for_keytype(const char *key_type);
|
||||
diff --git a/zkey/pvsecrets.h b/zkey/pvsecrets.h
|
||||
index 6acebfdd..ad844035 100644
|
||||
--- a/zkey/pvsecrets.h
|
||||
+++ b/zkey/pvsecrets.h
|
||||
@@ -15,6 +15,8 @@
|
||||
|
||||
#include "lib/zt_common.h"
|
||||
|
||||
+#include "pkey.h"
|
||||
+
|
||||
/*
|
||||
* Definitions for the /dev/uv kernel module interface
|
||||
*/
|
||||
@@ -60,8 +62,6 @@ struct uvio_ioctl_cb {
|
||||
#define UV_SECRET_TYPE_EDDSA_ED25519 0x14
|
||||
#define UV_SECRET_TYPE_EDDSA_ED448 0x15
|
||||
|
||||
-#define UV_SECRET_ID_LEN 32
|
||||
-
|
||||
#define UVIO_LIST_SECRETS_MAX_LEN 0x8000
|
||||
|
||||
struct uvio_list_secret_entry {
|
784
s390-tools-04-zkey-Add-the-pvsecrets-import-command.patch
Normal file
784
s390-tools-04-zkey-Add-the-pvsecrets-import-command.patch
Normal file
@ -0,0 +1,784 @@
|
||||
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
|
@ -0,0 +1,137 @@
|
||||
From 5276d408fd10669b3d8e623455778a675e8dc149 Mon Sep 17 00:00:00 2001
|
||||
From: Ingo Franzki <ifranzki@linux.ibm.com>
|
||||
Date: Mon, 19 Feb 2024 10:21:06 +0100
|
||||
Subject: [PATCH] zkey: Reject key generation and APQN association for
|
||||
PVSECRET-AES keys
|
||||
|
||||
Keys of type PVSECRET-AES can not be generated using 'zkey generate'.
|
||||
Furthermore, APQNs can not be associated with keys of type PVSECRET-AES
|
||||
via 'zkey change'. Reject that with a proper error message.
|
||||
|
||||
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 | 32 +++++++++++++++++++++++---------
|
||||
zkey/zkey.1 | 7 +++++++
|
||||
zkey/zkey.c | 5 +++++
|
||||
3 files changed, 35 insertions(+), 9 deletions(-)
|
||||
|
||||
diff --git a/zkey/keystore.c b/zkey/keystore.c
|
||||
index cde0caf5..db62e0a6 100644
|
||||
--- a/zkey/keystore.c
|
||||
+++ b/zkey/keystore.c
|
||||
@@ -2009,6 +2009,12 @@ int keystore_generate_key(struct keystore *keystore, const char *name,
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
+ if (!is_secure_key_type(key_type)) {
|
||||
+ warnx("Keys of type %s can not be generated. Use 'zkey "
|
||||
+ "pvsecret import' instead", key_type);
|
||||
+ return -EINVAL;
|
||||
+ }
|
||||
+
|
||||
rc = _keystore_get_key_filenames(keystore, name, &file_names);
|
||||
if (rc != 0)
|
||||
goto out_free_key_filenames;
|
||||
@@ -2535,9 +2541,9 @@ int keystore_change_key(struct keystore *keystore, const char *name,
|
||||
const char *null_ptr = NULL;
|
||||
char *upd_volumes = NULL;
|
||||
size_t secure_key_size;
|
||||
+ u8 *secure_key = NULL;
|
||||
u8 mkvp[MKVP_LENGTH];
|
||||
char sect_size[30];
|
||||
- u8 *secure_key;
|
||||
bool kms_bound;
|
||||
int rc;
|
||||
|
||||
@@ -2589,13 +2595,6 @@ int keystore_change_key(struct keystore *keystore, const char *name,
|
||||
goto out;
|
||||
}
|
||||
|
||||
- rc = _keystore_change_association(key_props, PROP_NAME_APQNS,
|
||||
- apqns, "APQN",
|
||||
- _keystore_apqn_check,
|
||||
- &apqn_check);
|
||||
- if (rc != 0)
|
||||
- goto out;
|
||||
-
|
||||
secure_key = read_secure_key(file_names.skey_filename,
|
||||
&secure_key_size,
|
||||
keystore->verbose);
|
||||
@@ -2604,11 +2603,24 @@ int keystore_change_key(struct keystore *keystore, const char *name,
|
||||
goto out;
|
||||
}
|
||||
|
||||
+ if (!is_secure_key(secure_key, secure_key_size)) {
|
||||
+ warnx("No APQNs can be associated with keys of type %s",
|
||||
+ get_key_type(secure_key, secure_key_size));
|
||||
+ rc = -EINVAL;
|
||||
+ goto out;
|
||||
+ }
|
||||
+
|
||||
+ rc = _keystore_change_association(key_props, PROP_NAME_APQNS,
|
||||
+ apqns, "APQN",
|
||||
+ _keystore_apqn_check,
|
||||
+ &apqn_check);
|
||||
+ if (rc != 0)
|
||||
+ goto out;
|
||||
+
|
||||
rc = get_master_key_verification_pattern(secure_key,
|
||||
secure_key_size,
|
||||
mkvp,
|
||||
keystore->verbose);
|
||||
- free(secure_key);
|
||||
if (rc)
|
||||
goto out;
|
||||
|
||||
@@ -2742,6 +2754,8 @@ int keystore_change_key(struct keystore *keystore, const char *name,
|
||||
free(upd_volumes);
|
||||
if (upd_volume_type != NULL)
|
||||
free(upd_volume_type);
|
||||
+ if (secure_key != NULL)
|
||||
+ free(secure_key);
|
||||
|
||||
if (rc != 0)
|
||||
pr_verbose(keystore, "Failed to change key '%s': %s",
|
||||
diff --git a/zkey/zkey.1 b/zkey/zkey.1
|
||||
index ba71a839..baaf8478 100644
|
||||
--- a/zkey/zkey.1
|
||||
+++ b/zkey/zkey.1
|
||||
@@ -402,6 +402,9 @@ additional information can be associated with a secure key using the
|
||||
.B \-\-sector\-size
|
||||
options.
|
||||
.PP
|
||||
+Keys of type \fBPVSECRET\-AES\fP do not use a cryptographic adapter, thus APQNs
|
||||
+can not be associated with them.
|
||||
+.PP
|
||||
.B Note:
|
||||
The \fBimport\fP command requires the CCA host library (libcsulcca.so)
|
||||
to be installed when secure keys of type \fBCCA\-AESCIPHER\fP are imported.
|
||||
@@ -564,6 +567,10 @@ APQNs that are associated with the key management system plugin.
|
||||
Other associated information is also changed in the key management system when
|
||||
changed using the change command.
|
||||
.PP
|
||||
+For keys of type \fBPVSECRET\-AES\fP you can not change or set the APQN
|
||||
+association. These keys do not use a cryptographic adapter, thus APQNs can not
|
||||
+be associated with them.
|
||||
+.PP
|
||||
.B Note:
|
||||
The secure key itself cannot be changed, only information about the secure
|
||||
key is changed. To rename a secure key, use the \fBrename\fP command.
|
||||
diff --git a/zkey/zkey.c b/zkey/zkey.c
|
||||
index 6e9b32af..36bdbcc0 100644
|
||||
--- a/zkey/zkey.c
|
||||
+++ b/zkey/zkey.c
|
||||
@@ -2001,6 +2001,11 @@ static int command_generate(void)
|
||||
return command_generate_repository();
|
||||
if (g.key_type == NULL)
|
||||
g.key_type = KEY_TYPE_CCA_AESDATA;
|
||||
+ if (!is_secure_key_type(g.key_type)) {
|
||||
+ warnx("Keys of type '%s' can not be generated. Use 'zkey "
|
||||
+ "pvsecret import' instead", g.key_type);
|
||||
+ return -EXIT_FAILURE;
|
||||
+ }
|
||||
if (g.pos_arg != NULL) {
|
||||
if (g.volumes != NULL) {
|
||||
warnx("Option '--volumes|-l' is not valid for "
|
@ -0,0 +1,145 @@
|
||||
From a8eb2bd4e7e74445c953906b33d450c2ace5223f Mon Sep 17 00:00:00 2001
|
||||
From: Ingo Franzki <ifranzki@linux.ibm.com>
|
||||
Date: Mon, 19 Feb 2024 11:26:41 +0100
|
||||
Subject: [PATCH] zkey: Reject re-enciphering of PVSECRET-AES keys
|
||||
|
||||
Keys of type PVSECRET-AES can not be reenciphered using 'zkey reencipher'
|
||||
or 'zkey-cryptsetup reencipher'. Reject that with a proper error message.
|
||||
|
||||
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 | 9 +++++++++
|
||||
zkey/zkey-cryptsetup.1 | 8 ++++++--
|
||||
zkey/zkey-cryptsetup.c | 16 ++++++++++++----
|
||||
zkey/zkey.1 | 4 ++++
|
||||
zkey/zkey.c | 7 +++++++
|
||||
5 files changed, 38 insertions(+), 6 deletions(-)
|
||||
|
||||
diff --git a/zkey/keystore.c b/zkey/keystore.c
|
||||
index db62e0a6..4f795a28 100644
|
||||
--- a/zkey/keystore.c
|
||||
+++ b/zkey/keystore.c
|
||||
@@ -3567,6 +3567,15 @@ static int _keystore_process_reencipher(struct keystore *keystore,
|
||||
goto out;
|
||||
}
|
||||
|
||||
+ if (!is_secure_key(secure_key, secure_key_size)) {
|
||||
+ warnx("Key '%s' is of type %s and can not be re-enciphered, "
|
||||
+ "skipping", name, get_key_type(secure_key,
|
||||
+ secure_key_size));
|
||||
+ info->num_skipped++;
|
||||
+ rc = 0;
|
||||
+ goto out;
|
||||
+ }
|
||||
+
|
||||
apqns = properties_get(properties, PROP_NAME_APQNS);
|
||||
if (apqns != NULL)
|
||||
apqn_list = str_list_split(apqns);
|
||||
diff --git a/zkey/zkey-cryptsetup.1 b/zkey/zkey-cryptsetup.1
|
||||
index c455f845..185edab9 100644
|
||||
--- a/zkey/zkey-cryptsetup.1
|
||||
+++ b/zkey/zkey-cryptsetup.1
|
||||
@@ -1,8 +1,8 @@
|
||||
-.\" Copyright IBM Corp. 2018
|
||||
+.\" 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.
|
||||
.\"
|
||||
-.TH ZKEY\-CRYPTSETUP 1 "May 2018" "s390-tools"
|
||||
+.TH ZKEY\-CRYPTSETUP 1 "February 2024" "s390-tools"
|
||||
.SH NAME
|
||||
zkey\-cryptsetup \- Manage secure AES volume keys of volumes encrypted with
|
||||
\fBLUKS2\fP and the \fBpaes\fP cipher
|
||||
@@ -115,6 +115,10 @@ command to re-encipher a secure AES volume key of a volume encrypted with
|
||||
re-enciphered when the master key of the cryptographic adapter in CCA or EP11
|
||||
coprocessor mode changes.
|
||||
.PP
|
||||
+Volume keys of type \fBPVSECRET\-AES\fP can not be re-enciphered. These keys do
|
||||
+not use a cryptographic adapter, thus they do not need to be re-enciphered when
|
||||
+the master key of a cryptographic adapter changes.
|
||||
+.PP
|
||||
The cryptographic adapter in CCA coprocessor mode has three different registers
|
||||
to store master keys:
|
||||
.RS 2
|
||||
diff --git a/zkey/zkey-cryptsetup.c b/zkey/zkey-cryptsetup.c
|
||||
index 8b55f7d1..2b018a2a 100644
|
||||
--- a/zkey/zkey-cryptsetup.c
|
||||
+++ b/zkey/zkey-cryptsetup.c
|
||||
@@ -2,7 +2,7 @@
|
||||
* zkey-cryptsetup - Re-encipher or validate volume keys of volumes
|
||||
* encrypted with LUKS2 and the paes cipher.
|
||||
*
|
||||
- * Copyright IBM Corp. 2018
|
||||
+ * 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.
|
||||
@@ -82,7 +82,7 @@ static const struct util_prg prg = {
|
||||
{
|
||||
.owner = "IBM Corp.",
|
||||
.pub_first = 2018,
|
||||
- .pub_last = 2018,
|
||||
+ .pub_last = 2024,
|
||||
},
|
||||
UTIL_PRG_COPYRIGHT_END
|
||||
}
|
||||
@@ -1609,14 +1609,22 @@ static int reencipher_prepare(int token)
|
||||
if (rc < 0)
|
||||
goto out;
|
||||
|
||||
+ securekeysize = keysize - integrity_keysize;
|
||||
+
|
||||
+ if (!is_secure_key((u8 *)key, securekeysize)) {
|
||||
+ warnx("The volume key of device '%s' is of type %s and can "
|
||||
+ "not be re-enciphered", g.pos_arg,
|
||||
+ get_key_type((u8 *)key, securekeysize));
|
||||
+ rc = -EINVAL;
|
||||
+ goto out;
|
||||
+ }
|
||||
+
|
||||
reenc_tok.original_keyslot = rc;
|
||||
|
||||
rc = ensure_is_active_keylot(reenc_tok.original_keyslot);
|
||||
if (rc != 0)
|
||||
goto out;
|
||||
|
||||
- securekeysize = keysize - integrity_keysize;
|
||||
-
|
||||
rc = generate_key_verification_pattern((u8 *)key, securekeysize,
|
||||
reenc_tok.verification_pattern,
|
||||
sizeof(reenc_tok.verification_pattern),
|
||||
diff --git a/zkey/zkey.1 b/zkey/zkey.1
|
||||
index baaf8478..316db5f0 100644
|
||||
--- a/zkey/zkey.1
|
||||
+++ b/zkey/zkey.1
|
||||
@@ -266,6 +266,10 @@ command to re-encipher an existing secure key with a new master key.
|
||||
A secure key must be re-enciphered when the master key of the CCA or EP11
|
||||
cryptographic adapter changes.
|
||||
.PP
|
||||
+Keys of type \fBPVSECRET\-AES\fP can not be re-enciphered. These keys do not
|
||||
+use a cryptographic adapter, thus they do not need to be re-enciphered when the
|
||||
+master of a cryptographic adapter changes.
|
||||
+.PP
|
||||
The CCA cryptographic adapter has three different registers to store
|
||||
master keys:
|
||||
.RS 2
|
||||
diff --git a/zkey/zkey.c b/zkey/zkey.c
|
||||
index 36bdbcc0..90b46106 100644
|
||||
--- a/zkey/zkey.c
|
||||
+++ b/zkey/zkey.c
|
||||
@@ -2118,6 +2118,13 @@ static int command_reencipher_file(void)
|
||||
if (secure_key == NULL)
|
||||
return EXIT_FAILURE;
|
||||
|
||||
+ if (!is_secure_key(secure_key, secure_key_size)) {
|
||||
+ warnx("A key of type %s can not be re-enciphered",
|
||||
+ get_key_type(secure_key, secure_key_size));
|
||||
+ rc = EXIT_FAILURE;
|
||||
+ goto out;
|
||||
+ }
|
||||
+
|
||||
rc = validate_secure_key(g.pkey_fd, secure_key, secure_key_size, NULL,
|
||||
&is_old_mk, NULL, g.verbose);
|
||||
if (rc != 0) {
|
@ -0,0 +1,407 @@
|
||||
From 833a8e7309ebf0ce70f2ee989ced5f87d6c3550b Mon Sep 17 00:00:00 2001
|
||||
From: Ingo Franzki <ifranzki@linux.ibm.com>
|
||||
Date: Mon, 19 Feb 2024 10:25:54 +0100
|
||||
Subject: [PATCH] zkey: Support validation of key of type PVSECRET-AES
|
||||
|
||||
Keys of type PVSECRET-AES can also be verified via the pkey IOCTL
|
||||
PKEY_VERIFYKEY2, but the card and domain fields must be zero, because such
|
||||
a key does not use a crypto card. Also XTS keys of type PVSRCRET-AES are
|
||||
not represented by 2 concatenated keys but by just one key of type
|
||||
PVSECRET-AES. Thus, special handling is required for XTS keys.
|
||||
|
||||
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 | 50 ++++++++++++++++++++++--------------
|
||||
zkey/pkey.c | 46 +++++++++++++++++++++-------------
|
||||
zkey/zkey-cryptsetup.1 | 4 ++-
|
||||
zkey/zkey-cryptsetup.c | 45 +++++++++++++++++++--------------
|
||||
zkey/zkey.1 | 8 +++---
|
||||
zkey/zkey.c | 57 ++++++++++++++++++++++++------------------
|
||||
6 files changed, 126 insertions(+), 84 deletions(-)
|
||||
|
||||
diff --git a/zkey/keystore.c b/zkey/keystore.c
|
||||
index 4f795a28..58f27df2 100644
|
||||
--- a/zkey/keystore.c
|
||||
+++ b/zkey/keystore.c
|
||||
@@ -3055,19 +3055,25 @@ static void _keystore_print_record(struct util_rec *rec,
|
||||
util_rec_set(rec, REC_XTS, is_xts ? "Yes" : "No");
|
||||
util_rec_set(rec, REC_KEY_TYPE, key_type);
|
||||
if (validation) {
|
||||
- if (valid)
|
||||
- util_rec_set(rec, REC_MASTERKEY,
|
||||
- "%s master key (MKVP: %s)",
|
||||
- is_old_mk ? "OLD" : "CURRENT",
|
||||
- printable_mkvp(
|
||||
- get_card_type_for_keytype(key_type),
|
||||
- mkvp));
|
||||
- else
|
||||
- util_rec_set(rec, REC_MASTERKEY,
|
||||
- "(unknown, MKVP: %s)",
|
||||
- printable_mkvp(
|
||||
- get_card_type_for_keytype(key_type),
|
||||
- mkvp));
|
||||
+ if (mkvp != NULL) {
|
||||
+ if (valid)
|
||||
+ util_rec_set(rec, REC_MASTERKEY,
|
||||
+ "%s master key (MKVP: %s)",
|
||||
+ is_old_mk ? "OLD" : "CURRENT",
|
||||
+ printable_mkvp(
|
||||
+ get_card_type_for_keytype(
|
||||
+ key_type),
|
||||
+ mkvp));
|
||||
+ else
|
||||
+ util_rec_set(rec, REC_MASTERKEY,
|
||||
+ "(unknown, MKVP: %s)",
|
||||
+ printable_mkvp(
|
||||
+ get_card_type_for_keytype(
|
||||
+ key_type),
|
||||
+ mkvp));
|
||||
+ } else {
|
||||
+ util_rec_set(rec, REC_MASTERKEY, "(none)");
|
||||
+ }
|
||||
}
|
||||
if (volumes_argz != NULL)
|
||||
util_rec_set_argz(rec, REC_VOLUMES, volumes_argz,
|
||||
@@ -3294,17 +3300,22 @@ static int _keystore_process_validate(struct keystore *keystore,
|
||||
valid = 1;
|
||||
}
|
||||
|
||||
- rc = get_master_key_verification_pattern(secure_key, secure_key_size,
|
||||
- mkvp, keystore->verbose);
|
||||
- if (rc != 0)
|
||||
- goto out;
|
||||
+ if (is_secure_key(secure_key, secure_key_size)) {
|
||||
+ rc = get_master_key_verification_pattern(secure_key,
|
||||
+ secure_key_size,
|
||||
+ mkvp,
|
||||
+ keystore->verbose);
|
||||
+ if (rc != 0)
|
||||
+ goto out;
|
||||
+ }
|
||||
|
||||
_keystore_print_record(info->rec, name, properties, 1,
|
||||
file_names->skey_filename, secure_key_size,
|
||||
is_xts_key(secure_key, secure_key_size),
|
||||
clear_key_bitsize, valid, is_old_mk,
|
||||
_keystore_reencipher_key_exists(file_names),
|
||||
- mkvp,
|
||||
+ is_secure_key(secure_key, secure_key_size) ?
|
||||
+ mkvp : NULL,
|
||||
_keystore_passphrase_file_exists(file_names) ?
|
||||
file_names->pass_filename : NULL);
|
||||
|
||||
@@ -3316,7 +3327,8 @@ static int _keystore_process_validate(struct keystore *keystore,
|
||||
"master key\n", 0);
|
||||
info->num_warnings++;
|
||||
}
|
||||
- if (info->noapqncheck == 0)
|
||||
+ if (info->noapqncheck == 0 &&
|
||||
+ is_secure_key(secure_key, secure_key_size))
|
||||
if (_keystore_display_apqn_status(keystore, properties,
|
||||
mkvp) != 0)
|
||||
info->num_warnings++;
|
||||
diff --git a/zkey/pkey.c b/zkey/pkey.c
|
||||
index 53c0a550..25deb05a 100644
|
||||
--- a/zkey/pkey.c
|
||||
+++ b/zkey/pkey.c
|
||||
@@ -1287,33 +1287,43 @@ int validate_secure_key(int pkey_fd,
|
||||
{
|
||||
struct pkey_verifykey2 verifykey2;
|
||||
struct pkey_apqn *list = NULL;
|
||||
+ bool xts, valid, securekey;
|
||||
u32 i, list_entries = 0;
|
||||
- bool xts, valid;
|
||||
- u32 flags;
|
||||
+ u32 flags = 0;
|
||||
int rc;
|
||||
|
||||
util_assert(pkey_fd != -1, "Internal error: pkey_fd is -1");
|
||||
util_assert(secure_key != NULL, "Internal error: secure_key is NULL");
|
||||
|
||||
- xts = is_xts_key(secure_key, secure_key_size);
|
||||
+ securekey = is_secure_key(secure_key, secure_key_size);
|
||||
+ xts = securekey ? is_xts_key(secure_key, secure_key_size) : false;
|
||||
|
||||
- flags = PKEY_FLAGS_MATCH_CUR_MKVP;
|
||||
- if (is_cca_aes_data_key(secure_key, secure_key_size) ||
|
||||
- is_cca_aes_cipher_key(secure_key, secure_key_size))
|
||||
- flags |= PKEY_FLAGS_MATCH_ALT_MKVP;
|
||||
+ if (securekey) {
|
||||
+ flags = PKEY_FLAGS_MATCH_CUR_MKVP;
|
||||
+ if (is_cca_aes_data_key(secure_key, secure_key_size) ||
|
||||
+ is_cca_aes_cipher_key(secure_key, secure_key_size))
|
||||
+ flags |= PKEY_FLAGS_MATCH_ALT_MKVP;
|
||||
|
||||
- rc = build_apqn_list_for_key(pkey_fd, secure_key,
|
||||
- HALF_KEYSIZE_FOR_XTS(secure_key_size, xts),
|
||||
- flags, apqns, &list, &list_entries,
|
||||
- verbose);
|
||||
- if (rc != 0) {
|
||||
- pr_verbose(verbose, "Failed to build a list of APQNs that can "
|
||||
- "validate this secure key: %s", strerror(-rc));
|
||||
- return rc;
|
||||
+ rc = build_apqn_list_for_key(pkey_fd, secure_key,
|
||||
+ HALF_KEYSIZE_FOR_XTS(
|
||||
+ secure_key_size, xts),
|
||||
+ flags, apqns, &list, &list_entries,
|
||||
+ verbose);
|
||||
+ if (rc != 0) {
|
||||
+ pr_verbose(verbose, "Failed to build a list of APQNs "
|
||||
+ "that can validate this secure "
|
||||
+ "key: %s", strerror(-rc));
|
||||
+ return rc;
|
||||
+ }
|
||||
+ } else {
|
||||
+ list = util_malloc(sizeof(struct pkey_apqn));
|
||||
+ list[0].card = 0;
|
||||
+ list[0].domain = 0;
|
||||
+ list_entries = 1;
|
||||
}
|
||||
|
||||
if (is_old_mk != NULL)
|
||||
- *is_old_mk = true;
|
||||
+ *is_old_mk = securekey ? true : false;
|
||||
if (clear_key_bitsize != NULL)
|
||||
*clear_key_bitsize = 0;
|
||||
|
||||
@@ -1333,7 +1343,7 @@ int validate_secure_key(int pkey_fd,
|
||||
continue;
|
||||
}
|
||||
|
||||
- if (is_xts_key(secure_key, secure_key_size)) {
|
||||
+ if (xts) {
|
||||
rc = validate_secure_xts_key(pkey_fd, &list[i],
|
||||
secure_key,
|
||||
secure_key_size,
|
||||
@@ -1358,7 +1368,7 @@ int validate_secure_key(int pkey_fd,
|
||||
* If at least one of the APQNs have a matching current MK,
|
||||
* then don't report OLD, even if some match the old MK.
|
||||
*/
|
||||
- if (is_old_mk &&
|
||||
+ if (securekey && is_old_mk &&
|
||||
(verifykey2.flags & PKEY_FLAGS_MATCH_CUR_MKVP))
|
||||
*is_old_mk = false;
|
||||
}
|
||||
diff --git a/zkey/zkey-cryptsetup.1 b/zkey/zkey-cryptsetup.1
|
||||
index 185edab9..ffd600d4 100644
|
||||
--- a/zkey/zkey-cryptsetup.1
|
||||
+++ b/zkey/zkey-cryptsetup.1
|
||||
@@ -68,7 +68,9 @@ It also displays the attributes of the secure key, such as key size, whether
|
||||
it is a secure key that can be used for the XTS cipher mode, and the master key
|
||||
register (CURRENT or OLD) with which the secure key is enciphered.
|
||||
For further information about master key registers, see the
|
||||
-\fBreencipher\fP command.
|
||||
+\fBreencipher\fP command. Keys of type \fBPVSECRET\-AES\fP do not use a
|
||||
+cryptographic adapter, thus no master key information is displayed for such
|
||||
+keys.
|
||||
.PP
|
||||
To open a key slot contained in the LUKS2 header of the volume, a passphrase is
|
||||
required. You are prompted for the passphrase, unless option
|
||||
diff --git a/zkey/zkey-cryptsetup.c b/zkey/zkey-cryptsetup.c
|
||||
index 2b018a2a..65716f3b 100644
|
||||
--- a/zkey/zkey-cryptsetup.c
|
||||
+++ b/zkey/zkey-cryptsetup.c
|
||||
@@ -1346,8 +1346,7 @@ static int check_keysize_and_cipher_mode(const u8 *key, size_t keysize)
|
||||
}
|
||||
|
||||
if (strncmp(crypt_get_cipher_mode(g.cd), "xts", 3) == 0) {
|
||||
- if (keysize < 2 * MIN_SECURE_KEY_SIZE ||
|
||||
- (key != NULL && !is_xts_key(key, keysize))) {
|
||||
+ if (key != NULL && !is_xts_key(key, keysize)) {
|
||||
warnx("The volume key size %lu is not valid for the "
|
||||
"cipher mode '%s'", keysize,
|
||||
crypt_get_cipher_mode(g.cd));
|
||||
@@ -1539,8 +1538,9 @@ static int validate_keyslot(int keyslot, char **key, size_t *keysize,
|
||||
rc = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
- pr_verbose("Volume key is currently enciphered with %s master key",
|
||||
- is_old ? "OLD" : "CURRENT");
|
||||
+ if (is_secure_key((u8 *)vkey, vkeysize - ikeysize))
|
||||
+ pr_verbose("Volume key is currently enciphered with %s "
|
||||
+ "master key", is_old ? "OLD" : "CURRENT");
|
||||
|
||||
if (key != NULL)
|
||||
*key = vkey;
|
||||
@@ -2023,12 +2023,14 @@ static int command_validate(void)
|
||||
vp_tok_avail = 1;
|
||||
}
|
||||
|
||||
- rc = get_master_key_verification_pattern((u8 *)key, seckeysize,
|
||||
- mkvp, g.verbose);
|
||||
- if (rc != 0) {
|
||||
- warnx("Failed to get the master key verification pattern: %s",
|
||||
- strerror(-rc));
|
||||
- goto out;
|
||||
+ if (is_secure_key((u8 *)key, seckeysize)) {
|
||||
+ rc = get_master_key_verification_pattern((u8 *)key, seckeysize,
|
||||
+ mkvp, g.verbose);
|
||||
+ if (rc != 0) {
|
||||
+ warnx("Failed to get the master key verification "
|
||||
+ "pattern: %s", strerror(-rc));
|
||||
+ goto out;
|
||||
+ }
|
||||
}
|
||||
|
||||
key_type = get_key_type((u8 *)key, seckeysize);
|
||||
@@ -2041,15 +2043,19 @@ static int command_validate(void)
|
||||
printf(" Key type: %s\n", key_type);
|
||||
if (is_valid) {
|
||||
printf(" Clear key size: %lu bits\n", clear_keysize);
|
||||
- printf(" Enciphered with: %s master key (MKVP: "
|
||||
- "%s)\n", is_old_mk ? "OLD" : "CURRENT",
|
||||
- printable_mkvp(get_card_type_for_keytype(key_type),
|
||||
- mkvp));
|
||||
+ if (is_secure_key((u8 *)key, seckeysize)) {
|
||||
+ printf(" Enciphered with: %s master key (MKVP: "
|
||||
+ "%s)\n", is_old_mk ? "OLD" : "CURRENT",
|
||||
+ printable_mkvp(get_card_type_for_keytype(
|
||||
+ key_type), mkvp));
|
||||
+ }
|
||||
} else {
|
||||
printf(" Clear key size: (unknown)\n");
|
||||
- printf(" Enciphered with: (unknown, MKVP: %s)\n",
|
||||
- printable_mkvp(get_card_type_for_keytype(key_type),
|
||||
- mkvp));
|
||||
+ if (is_secure_key((u8 *)key, seckeysize)) {
|
||||
+ printf(" Enciphered with: (unknown, MKVP: %s)\n",
|
||||
+ printable_mkvp(get_card_type_for_keytype(
|
||||
+ key_type), mkvp));
|
||||
+ }
|
||||
}
|
||||
if (vp_tok_avail)
|
||||
print_verification_pattern(vp_tok.verification_pattern);
|
||||
@@ -2065,7 +2071,7 @@ static int command_validate(void)
|
||||
if (!is_valid)
|
||||
printf("\nATTENTION: The secure volume key is not valid.\n");
|
||||
|
||||
- if (is_old_mk)
|
||||
+ if (is_secure_key((u8 *)key, seckeysize) && is_old_mk)
|
||||
util_print_indented("\nWARNING: The secure volume key is "
|
||||
"currently enciphered with the OLD "
|
||||
"master key. To mitigate the danger of "
|
||||
@@ -2195,7 +2201,8 @@ static int command_setkey(void)
|
||||
goto out;
|
||||
}
|
||||
|
||||
- if (is_old_mk) {
|
||||
+ if (is_secure_key(newkey, newkey_size - integrity_keysize) &&
|
||||
+ is_old_mk) {
|
||||
util_asprintf(&msg, "The secure key in file '%s' is "
|
||||
"enciphered with the master key in the OLD "
|
||||
"master key register. Do you want to set this "
|
||||
diff --git a/zkey/zkey.1 b/zkey/zkey.1
|
||||
index 316db5f0..b44eadf1 100644
|
||||
--- a/zkey/zkey.1
|
||||
+++ b/zkey/zkey.1
|
||||
@@ -209,7 +209,9 @@ It also displays the attributes of the secure key, such as key sizes, whether
|
||||
it is a secure key that can be used for the XTS cipher mode, the master key
|
||||
register (CURRENT or OLD) with which the secure key is enciphered, and other key
|
||||
attributes. For further information about master key registers, see the
|
||||
-\fBreencipher\fP command.
|
||||
+\fBreencipher\fP command. Keys of type \fBPVSECRET\-AES\fP do not use a
|
||||
+cryptographic adapter, thus no master key information is displayed for such
|
||||
+keys.
|
||||
.PP
|
||||
The secure key can either be contained in a file in the file system, or in a
|
||||
secure key repository. To validate a secure key contained in a file, specify
|
||||
@@ -1599,8 +1601,8 @@ This option is only used for secure keys contained in the secure key repository.
|
||||
.TP
|
||||
.BR \-K ", " \-\-key\-type\~\fItype\fP
|
||||
Specifies the key type of the secure key. Possible values are
|
||||
-\fBCCA\-AESDATA\fP, \fBCCA\-AESCIPHER\fP, and \fBEP11\-AES\fP. Only keys with
|
||||
-the specified key type are listed.
|
||||
+\fBCCA\-AESDATA\fP, \fBCCA\-AESCIPHER\fP, \fBEP11\-AES\fP, and
|
||||
+\fBPVSECRET\-AES\fP. Only keys with the specified key type are listed.
|
||||
This option is only used for secure keys contained in the secure key repository.
|
||||
.TP
|
||||
.BR \-L ", " \-\-local\fP
|
||||
diff --git a/zkey/zkey.c b/zkey/zkey.c
|
||||
index 90b46106..39a527c4 100644
|
||||
--- a/zkey/zkey.c
|
||||
+++ b/zkey/zkey.c
|
||||
@@ -558,9 +558,10 @@ static struct util_opt opt_vec[] = {
|
||||
.option = { "key-type", required_argument, NULL, 'K'},
|
||||
.argument = "type",
|
||||
.desc = "The type of the key. Possible values are '"
|
||||
- KEY_TYPE_CCA_AESDATA"', '"KEY_TYPE_CCA_AESCIPHER"' "
|
||||
- "and '"KEY_TYPE_EP11_AES"'. Use this option to list "
|
||||
- "all keys with the specified key type.",
|
||||
+ KEY_TYPE_CCA_AESDATA "', '" KEY_TYPE_CCA_AESCIPHER
|
||||
+ "', '" KEY_TYPE_EP11_AES "', and '"
|
||||
+ KEY_TYPE_PVSECRET_AES "'. Use this option to list all "
|
||||
+ "keys with the specified key type.",
|
||||
.command = COMMAND_LIST,
|
||||
},
|
||||
{
|
||||
@@ -2345,13 +2346,16 @@ static int command_validate_file(void)
|
||||
goto out;
|
||||
}
|
||||
|
||||
- rc = get_master_key_verification_pattern(secure_key, secure_key_size,
|
||||
- mkvp, g.verbose);
|
||||
- if (rc != 0) {
|
||||
- warnx("Failed to get the master key verification pattern: %s",
|
||||
- strerror(-rc));
|
||||
- rc = EXIT_FAILURE;
|
||||
- goto out;
|
||||
+ if (is_secure_key(secure_key, secure_key_size)) {
|
||||
+ rc = get_master_key_verification_pattern(secure_key,
|
||||
+ secure_key_size,
|
||||
+ mkvp, g.verbose);
|
||||
+ if (rc != 0) {
|
||||
+ warnx("Failed to get the master key verification "
|
||||
+ "pattern: %s", strerror(-rc));
|
||||
+ rc = EXIT_FAILURE;
|
||||
+ goto out;
|
||||
+ }
|
||||
}
|
||||
|
||||
key_type = get_key_type(secure_key, secure_key_size);
|
||||
@@ -2363,25 +2367,30 @@ static int command_validate_file(void)
|
||||
printf(" Clear key size: %lu bits\n", clear_key_size);
|
||||
printf(" XTS type key: %s\n",
|
||||
is_xts_key(secure_key, secure_key_size) ? "Yes" : "No");
|
||||
- printf(" Enciphered with: %s master key (MKVP: %s)\n",
|
||||
- is_old_mk ? "OLD" : "CURRENT",
|
||||
- printable_mkvp(get_card_type_for_keytype(key_type), mkvp));
|
||||
+ if (is_secure_key(secure_key, secure_key_size)) {
|
||||
+ printf(" Enciphered with: %s master key (MKVP: %s)\n",
|
||||
+ is_old_mk ? "OLD" : "CURRENT",
|
||||
+ printable_mkvp(get_card_type_for_keytype(key_type),
|
||||
+ mkvp));
|
||||
+ }
|
||||
printf(" Verification pattern: %.*s\n", VERIFICATION_PATTERN_LEN / 2,
|
||||
vp);
|
||||
printf(" %.*s\n", VERIFICATION_PATTERN_LEN / 2,
|
||||
&vp[VERIFICATION_PATTERN_LEN / 2]);
|
||||
|
||||
- rc = cross_check_apqns(NULL, mkvp,
|
||||
- get_min_card_level_for_keytype(key_type),
|
||||
- get_min_fw_version_for_keytype(key_type),
|
||||
- get_card_type_for_keytype(key_type),
|
||||
- true, g.verbose);
|
||||
- if (rc == -EINVAL)
|
||||
- return EXIT_FAILURE;
|
||||
- if (rc != 0 && rc != -ENOTSUP) {
|
||||
- warnx("Your master key setup is improper");
|
||||
- rc = EXIT_FAILURE;
|
||||
- goto out;
|
||||
+ if (is_secure_key(secure_key, secure_key_size)) {
|
||||
+ rc = cross_check_apqns(NULL, mkvp,
|
||||
+ get_min_card_level_for_keytype(key_type),
|
||||
+ get_min_fw_version_for_keytype(key_type),
|
||||
+ get_card_type_for_keytype(key_type),
|
||||
+ true, g.verbose);
|
||||
+ if (rc == -EINVAL)
|
||||
+ return EXIT_FAILURE;
|
||||
+ if (rc != 0 && rc != -ENOTSUP) {
|
||||
+ warnx("Your master key setup is improper");
|
||||
+ rc = EXIT_FAILURE;
|
||||
+ goto out;
|
||||
+ }
|
||||
}
|
||||
|
||||
out:
|
@ -1,3 +1,16 @@
|
||||
-------------------------------------------------------------------
|
||||
Thu Jan 30 08:19:47 UTC 2025 - Nikolay Gueorguiev <nikolay.gueorguiev@suse.com>
|
||||
|
||||
- Applied additional patches ( jsc#PED-9561 ( jsc#IBM-1447 ) )
|
||||
* 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
|
||||
|
||||
-------------------------------------------------------------------
|
||||
Thu Jan 9 07:05:53 UTC 2025 - Nikolay Gueorguiev <nikolay.gueorguiev@suse.com>
|
||||
|
||||
|
@ -181,6 +181,14 @@ Patch960: s390-tools-Support-unencrypted-SE-images-01.patch
|
||||
Patch961: s390-tools-pvimg-info-command-04.patch
|
||||
Patch962: s390-tools-pvimg-additional-01.patch
|
||||
###
|
||||
Patch970: s390-tools-01-zkey-Add-support-for-retrieving-a-list-of-ultravisor-secrets.patch
|
||||
Patch971: s390-tools-02-zkey-Add-the--pvsecrets-list-command.patch
|
||||
Patch972: s390-tools-03-zkey-Add-PVSECRETS-AES-key-type.patch
|
||||
Patch973: s390-tools-04-zkey-Add-the-pvsecrets-import-command.patch
|
||||
Patch974: s390-tools-05-zkey-Reject-key-generation-and-APQN-association-for-PVSECRET-AES-keys.patch
|
||||
Patch975: s390-tools-06-zkey-Reject-re-enciphering-of-PVSECRET-AES-keys.patch
|
||||
Patch976: s390-tools-07-zkey-Support-validation-of-key-of-type-PVSECRET-AES.patch
|
||||
###
|
||||
Patch990: s390-tools-slfo-01-parse-ipl-device-for-activation.patch
|
||||
###
|
||||
|
||||
|
BIN
vendor.tar.gz
(Stored with Git LFS)
BIN
vendor.tar.gz
(Stored with Git LFS)
Binary file not shown.
Loading…
x
Reference in New Issue
Block a user