From 8c4b2872b8e24c1a27d8201beb5979c66ac05268 Mon Sep 17 00:00:00 2001 From: Ingo Franzki 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 Reviewed-by: Jorg Schmidbauer Signed-off-by: Steffen Eiden --- 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 +#include +#include +#include +#include +#include +#include +#include +#include + +#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