Compare commits
No commits in common. "factory" and "devel" have entirely different histories.
@ -44,14 +44,6 @@ debug_mesg () {
|
||||
esac
|
||||
}
|
||||
|
||||
add_cio_channel() {
|
||||
echo "$* # ${DATE}" >> /boot/zipl/active_devices.txt
|
||||
}
|
||||
|
||||
remove_cio_channel() {
|
||||
[ -w /boot/zipl/active_devices.txt ] && sed -i -e "/^${1}/d" /boot/zipl/active_devices.txt
|
||||
}
|
||||
|
||||
usage(){
|
||||
echo "Usage: ${0} <read channel> <write channel> <online> [<protocol>]"
|
||||
echo " read/write channel = x.y.ssss where"
|
||||
@ -120,9 +112,3 @@ RC=${?}
|
||||
if [ ${RC} -ne 0 ]; then
|
||||
exit ${RC}
|
||||
fi
|
||||
|
||||
if [ ${ON_OFF} == 1 ]; then
|
||||
add_cio_channel "${CTC_READ_CHAN},${CTC_WRITE_CHAN}"
|
||||
else remove_cio_channel "${CTC_READ_CHAN}"
|
||||
remove_cio_channel "${CTC_WRITE_CHAN}"
|
||||
fi
|
||||
|
@ -43,14 +43,6 @@ debug_mesg () {
|
||||
esac
|
||||
}
|
||||
|
||||
add_cio_channel() {
|
||||
echo "$* # ${DATE}" >> /boot/zipl/active_devices.txt
|
||||
}
|
||||
|
||||
remove_cio_channel() {
|
||||
[ -w /boot/zipl/active_devices.txt ] && sed -i -e "/^${1}/d" /boot/zipl/active_devices.txt
|
||||
}
|
||||
|
||||
usage(){
|
||||
echo "Usage: ${0} [-f -t <dasd_type> ] <ccwid> <online> [use_diag]"
|
||||
echo
|
||||
@ -165,9 +157,4 @@ elif [ ${ON_OFF} == 1 ]; then
|
||||
fi
|
||||
fi
|
||||
|
||||
if [ ${ON_OFF} == 1 ]; then
|
||||
add_cio_channel "${CCW_CHAN_ID}"
|
||||
else remove_cio_channel "${CCW_CHAN_ID}"
|
||||
fi
|
||||
|
||||
exit ${exitcode}
|
||||
|
@ -43,14 +43,6 @@ debug_mesg () {
|
||||
esac
|
||||
}
|
||||
|
||||
add_cio_channel() {
|
||||
echo "$* # ${DATE}" >> /boot/zipl/active_devices.txt
|
||||
}
|
||||
|
||||
remove_cio_channel() {
|
||||
[ -w /boot/zipl/active_devices.txt ] && sed -i -e "/^${1}/d" /boot/zipl/active_devices.txt
|
||||
}
|
||||
|
||||
usage(){
|
||||
echo "Usage: ${0} [-f -t <dasd_type> ] <ccwid> <online> [use_diag]"
|
||||
echo
|
||||
@ -165,9 +157,4 @@ elif [ ${ON_OFF} == 1 ]; then
|
||||
fi
|
||||
fi
|
||||
|
||||
if [ ${ON_OFF} == 1 ]; then
|
||||
add_cio_channel "${CCW_CHAN_ID}"
|
||||
else remove_cio_channel "${CCW_CHAN_ID}"
|
||||
fi
|
||||
|
||||
exit ${exitcode}
|
||||
|
@ -1,10 +1,7 @@
|
||||
#
|
||||
# Copyright (c) 2018-2024 SUSE LINUX GmbH, Nuernberg, Germany.
|
||||
# Copyright (c) 2018 SUSE LINUX GmbH, Nuernberg, Germany.
|
||||
# All rights reserved.
|
||||
#
|
||||
|
||||
# load pkey module at boot time
|
||||
pkey
|
||||
pkey_cca
|
||||
pkey_ep11
|
||||
pkey_pckmo
|
||||
|
@ -48,14 +48,6 @@ debug_mesg () {
|
||||
esac
|
||||
}
|
||||
|
||||
add_cio_channel() {
|
||||
echo "$* # ${DATE}" >> /boot/zipl/active_devices.txt
|
||||
}
|
||||
|
||||
remove_cio_channel() {
|
||||
[ -w /boot/zipl/active_devices.txt ] && sed -i -e "/^${1}/d" /boot/zipl/active_devices.txt
|
||||
}
|
||||
|
||||
usage(){
|
||||
echo "Usage: ${0} [options] <read chan> <write chan> <data chan> <online>"
|
||||
echo " -i Configure IP takeover"
|
||||
@ -165,10 +157,3 @@ RC=${?}
|
||||
if [ ${RC} -ne 0 ]; then
|
||||
exit ${RC}
|
||||
fi
|
||||
|
||||
if [ ${ON_OFF} == 1 ]; then
|
||||
add_cio_channel "${QETH_READ_CHAN},${QETH_WRITE_CHAN},${QETH_DATA_CHAN}"
|
||||
else remove_cio_channel "${QETH_READ_CHAN}"
|
||||
remove_cio_channel "${QETH_WRITE_CHAN}"
|
||||
remove_cio_channel "${QETH_DATA_CHAN}"
|
||||
fi
|
||||
|
@ -1,67 +0,0 @@
|
||||
From 2d26a63806d2847f549c06276070a636a61bcb80 Mon Sep 17 00:00:00 2001
|
||||
From: Eduard Shishkin <edward6@linux.ibm.com>
|
||||
Date: Wed, 4 Dec 2024 13:37:46 +0100
|
||||
Subject: [PATCH s390-tools] zipl_helper.device-mapper: add missed step in
|
||||
logical device resolution
|
||||
|
||||
This fixes 670bf3e
|
||||
|
||||
Preparing a loop device for IPL by zipl tool, using its partition as
|
||||
zipl target, leads to inconsistent installation setup. The problem is in
|
||||
a missed step in the procedure of logical device resolution performed
|
||||
by the script zipl_helper.device-mapper:
|
||||
|
||||
\# lsblk
|
||||
|
||||
NAME MAJ:MIN RM SIZE RO TYPE MOUNTPOINT
|
||||
loop0 7:0 0 5G 0 loop
|
||||
|-loop0p1 253:15 0 128M 0 part
|
||||
`-loop0p2 253:16 0 4.9G 0 part /mnt
|
||||
|
||||
\# ./zipl_helper.device-mapper 253:16
|
||||
|
||||
Expected result:
|
||||
|
||||
targetbase=7:0
|
||||
targettype=SCSI
|
||||
targetblocksize=4096
|
||||
targetoffset=32784
|
||||
|
||||
Actual result:
|
||||
|
||||
targetbase=253:16
|
||||
targettype=SCSI
|
||||
targetblocksize=4096
|
||||
targetoffset=32784
|
||||
|
||||
The fixup adds a missed resolution step.
|
||||
|
||||
Reference-ID: LTC210771
|
||||
Signed-off-by: Eduard Shishkin <edward6@linux.ibm.com>
|
||||
---
|
||||
zipl/src/zipl_helper.device-mapper.c | 4 ++--
|
||||
1 file changed, 2 insertions(+), 2 deletions(-)
|
||||
|
||||
diff --git a/zipl/src/zipl_helper.device-mapper.c b/zipl/src/zipl_helper.device-mapper.c
|
||||
index aca52be1..918c5aba 100644
|
||||
--- a/zipl/src/zipl_helper.device-mapper.c
|
||||
+++ b/zipl/src/zipl_helper.device-mapper.c
|
||||
@@ -1306,13 +1306,13 @@ static int complete_physical_device(struct physical_device *pd, dev_t *base_dev)
|
||||
*base_dev = base_entry->dev.dev;
|
||||
} else {
|
||||
/*
|
||||
- * In this case base device is the uppermost logical
|
||||
+ * In this case base device is the uppermost
|
||||
* device which provides access to boot sectors
|
||||
*/
|
||||
base_entry = find_base_entry(pd->dmpath, dc->bootsectors);
|
||||
if (!base_entry)
|
||||
return -1;
|
||||
- *base_dev = base_entry->dev.dev;
|
||||
+ *base_dev = first_device_by_target_data(base_entry->target);
|
||||
}
|
||||
/* Check for valid offset of filesystem */
|
||||
if ((pd->offset % (dc->blocksize / SECTOR_SIZE)) != 0) {
|
||||
--
|
||||
2.39.0
|
||||
|
@ -1,299 +0,0 @@
|
||||
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
|
@ -1,63 +0,0 @@
|
||||
From 592a016a1095fa9813f0bae8256433ba5af4ab9b Mon Sep 17 00:00:00 2001
|
||||
From: Eduard Shishkin <edward6@linux.ibm.com>
|
||||
Date: Sat, 7 Dec 2024 12:48:12 +0100
|
||||
Subject: [PATCH s390-tools 2/2] zipl/src: fix imprecise check that file is on
|
||||
specified device
|
||||
|
||||
This fixes c0f02d2
|
||||
|
||||
The check that file is on specified disk is imprecise: In case when
|
||||
target parameters are specified by user, the check compares a logical
|
||||
device with a base disk, which is incorrect.
|
||||
|
||||
The fixup makes the check compare base disks (a specified one with
|
||||
the base disk determined by disk_get_info() procedure).
|
||||
|
||||
Signed-off-by: Eduard Shishkin <edward6@linux.ibm.com>
|
||||
---
|
||||
zipl/src/bootmap.c | 9 +++++----
|
||||
1 file changed, 5 insertions(+), 4 deletions(-)
|
||||
|
||||
diff --git a/zipl/src/bootmap.c b/zipl/src/bootmap.c
|
||||
index 7d340156..880b93ce 100644
|
||||
--- a/zipl/src/bootmap.c
|
||||
+++ b/zipl/src/bootmap.c
|
||||
@@ -299,14 +299,15 @@ create_component_header(void* buffer, component_header_type type)
|
||||
}
|
||||
|
||||
/*
|
||||
- * Not precise check that the file FILENAME locates on specified physical DISK.
|
||||
+ * Not precise check that the file FILENAME locates on the physical
|
||||
+ * disk specified by WHERE.
|
||||
*
|
||||
* Try to auto-detect parameters of the disk which the file locates on
|
||||
* and compare found device-ID with DISK.
|
||||
* Return 0, if auto-detection succeeded, and it is proven that the
|
||||
* file does NOT locate on DISK. Otherwise, return 1.
|
||||
*/
|
||||
-static int file_is_on_disk(const char *filename, dev_t disk)
|
||||
+static int file_is_on_disk(const char *filename, struct disk_info *where)
|
||||
{
|
||||
/*
|
||||
* Retrieve info of the underlying disk without any user hints
|
||||
@@ -331,7 +332,7 @@ static int file_is_on_disk(const char *filename, dev_t disk)
|
||||
"Warning: Preparing a logical device for boot might fail\n");
|
||||
return 1;
|
||||
}
|
||||
- if (info->device != disk) {
|
||||
+ if (info->basedisks[0] != where->basedisks[0]) {
|
||||
disk_free_info(info);
|
||||
return 0;
|
||||
}
|
||||
@@ -378,7 +379,7 @@ static int add_component_file_range(struct install_set *bis,
|
||||
return -1;
|
||||
}
|
||||
} else {
|
||||
- if (!file_is_on_disk(filename, bis->info->device)) {
|
||||
+ if (!file_is_on_disk(filename, bis->info)) {
|
||||
error_reason("File is not on target device");
|
||||
return -1;
|
||||
}
|
||||
--
|
||||
2.39.0
|
||||
|
@ -1,823 +0,0 @@
|
||||
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)
|
@ -1,340 +0,0 @@
|
||||
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 {
|
@ -1,784 +0,0 @@
|
||||
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
|
@ -1,137 +0,0 @@
|
||||
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 "
|
@ -1,145 +0,0 @@
|
||||
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) {
|
@ -1,407 +0,0 @@
|
||||
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,31 +0,0 @@
|
||||
From caaf2b2116235d282c2561f0bf6f62b0033c78c4 Mon Sep 17 00:00:00 2001
|
||||
From: Jakob Naucke <naucke@linux.ibm.com>
|
||||
Date: Wed, 15 Jan 2025 17:36:01 +0100
|
||||
Subject: [PATCH] rust/pvimg: Fix flag parsing for allowing dump
|
||||
MIME-Version: 1.0
|
||||
Content-Type: text/plain; charset=UTF-8
|
||||
Content-Transfer-Encoding: 8bit
|
||||
|
||||
Parsing of pvimg flags did not pick up allowing dumping correctly.
|
||||
|
||||
Fixes: f4cf4ae6ebb1 (rust: Add a new tool called 'pvimg')
|
||||
Reviewed-by: Marc Hartmayer <mhartmay@linux.ibm.com>
|
||||
Signed-off-by: Jakob Naucke <naucke@linux.ibm.com>
|
||||
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
|
||||
---
|
||||
rust/pvimg/src/cmd/create.rs | 2 +-
|
||||
1 file changed, 1 insertion(+), 1 deletion(-)
|
||||
|
||||
diff --git a/rust/pvimg/src/cmd/create.rs b/rust/pvimg/src/cmd/create.rs
|
||||
index 3e2ca655..c9d39745 100644
|
||||
--- a/rust/pvimg/src/cmd/create.rs
|
||||
+++ b/rust/pvimg/src/cmd/create.rs
|
||||
@@ -55,7 +55,7 @@ fn parse_flags(
|
||||
.and(Some(PcfV1::all_disabled([PcfV1::AllowDumping]))),
|
||||
lf.enable_dump
|
||||
.filter(|x| *x)
|
||||
- .and(Some(PcfV1::all_disabled([PcfV1::AllowDumping]))),
|
||||
+ .and(Some(PcfV1::all_enabled([PcfV1::AllowDumping]))),
|
||||
lf.disable_pckmo
|
||||
.filter(|x| *x)
|
||||
.and(Some(PcfV1::all_disabled([
|
@ -1,32 +0,0 @@
|
||||
From 7bc12d0202d5819442dd4c32755feb5eb19af70b Mon Sep 17 00:00:00 2001
|
||||
From: Marc Hartmayer <mhartmay@linux.ibm.com>
|
||||
Date: Wed, 8 Jan 2025 12:33:05 +0100
|
||||
Subject: [PATCH] rust/pvimg: Document the change from '--comm-key' to '--cck'
|
||||
in the help message
|
||||
|
||||
This fixes problems when users search for '--comm-key' in the help
|
||||
message.
|
||||
|
||||
Fixes: 5b6d7a467dc3 ("rust/pvimg: Add '--cck <FILE>' command line option and make '--comm-key' an alias")
|
||||
Reviewed-by: Steffen Eiden <seiden@linux.ibm.com>
|
||||
Reviewed-by: Nico Boehr <nrb@linux.ibm.com>
|
||||
Signed-off-by: Marc Hartmayer <mhartmay@linux.ibm.com>
|
||||
Signed-off-by: Steffen Eiden <seiden@linux.ibm.com>
|
||||
---
|
||||
rust/pvimg/src/cli.rs | 3 ++-
|
||||
1 file changed, 2 insertions(+), 1 deletion(-)
|
||||
|
||||
diff --git a/rust/pvimg/src/cli.rs b/rust/pvimg/src/cli.rs
|
||||
index 446e9b74..f5a8c308 100644
|
||||
--- a/rust/pvimg/src/cli.rs
|
||||
+++ b/rust/pvimg/src/cli.rs
|
||||
@@ -330,7 +330,8 @@ pub struct CreateBootImageArgs {
|
||||
|
||||
/// Use the content of FILE as the customer-communication key (CCK).
|
||||
///
|
||||
- /// The file must contain exactly 32 bytes of data.
|
||||
+ /// The file must contain exactly 32 bytes of data. This option used to be
|
||||
+ /// called '--comm-key' in previous versions.
|
||||
#[arg(long, value_name = "FILE", visible_alias = "comm-key")]
|
||||
pub cck: Option<PathBuf>,
|
||||
|
BIN
s390-tools-2.31.0.tar.gz
(Stored with Git LFS)
Normal file
BIN
s390-tools-2.31.0.tar.gz
(Stored with Git LFS)
Normal file
Binary file not shown.
51
s390-tools-2.34-Fix-Rust-compilation-errors.patch
Normal file
51
s390-tools-2.34-Fix-Rust-compilation-errors.patch
Normal file
@ -0,0 +1,51 @@
|
||||
From 6a55d0c2e57952600164822dd100e8247b4b010f Mon Sep 17 00:00:00 2001
|
||||
From: Steffen Eiden <seiden@linux.ibm.com>
|
||||
Date: Fri, 23 Aug 2024 09:16:26 +0200
|
||||
Subject: [PATCH] rust/pv: Lower most lints to warn
|
||||
MIME-Version: 1.0
|
||||
Content-Type: text/plain; charset=UTF-8
|
||||
Content-Transfer-Encoding: 8bit
|
||||
|
||||
Lower the lint level to warn for the styling lints.
|
||||
This avoids compile issues during packaging for newer tooling with
|
||||
potential more lint findings.
|
||||
Still deny compiling if a public symbol has no documentation.
|
||||
|
||||
Fixes: https://github.com/ibm-s390-linux/s390-tools/issues/173
|
||||
Reviewed-by: Jan Höppner <hoeppner@linux.ibm.com>
|
||||
Signed-off-by: Steffen Eiden <seiden@linux.ibm.com>
|
||||
---
|
||||
rust/pv/src/lib.rs | 4 ++--
|
||||
rust/pv_core/src/lib.rs | 4 ++--
|
||||
2 files changed, 4 insertions(+), 4 deletions(-)
|
||||
|
||||
diff --git a/rust/pv/src/lib.rs b/rust/pv/src/lib.rs
|
||||
index 9a647617..1084f8e8 100644
|
||||
--- a/rust/pv/src/lib.rs
|
||||
+++ b/rust/pv/src/lib.rs
|
||||
@@ -2,8 +2,8 @@
|
||||
//
|
||||
// Copyright IBM Corp. 2023, 2024
|
||||
|
||||
-#![deny(
|
||||
- missing_docs,
|
||||
+#![deny(missing_docs)]
|
||||
+#![warn(
|
||||
missing_debug_implementations,
|
||||
trivial_numeric_casts,
|
||||
unstable_features,
|
||||
diff --git a/rust/pv_core/src/lib.rs b/rust/pv_core/src/lib.rs
|
||||
index 1356c1b7..b617b8f9 100644
|
||||
--- a/rust/pv_core/src/lib.rs
|
||||
+++ b/rust/pv_core/src/lib.rs
|
||||
@@ -1,8 +1,8 @@
|
||||
// SPDX-License-Identifier: MIT
|
||||
//
|
||||
// Copyright IBM Corp. 2023, 2024
|
||||
-#![deny(
|
||||
- missing_docs,
|
||||
+#![deny(missing_docs)]
|
||||
+#![warn(
|
||||
missing_debug_implementations,
|
||||
trivial_numeric_casts,
|
||||
unstable_features,
|
3
s390-tools-2.34.0.tar.gz
Normal file
3
s390-tools-2.34.0.tar.gz
Normal file
@ -0,0 +1,3 @@
|
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:ea4758c4e460d7f7e040e6aedf68b1be32d63fecb733358b08182f6b9b7440a2
|
||||
size 2114507
|
BIN
s390-tools-2.36.0.tar.gz
(Stored with Git LFS)
BIN
s390-tools-2.36.0.tar.gz
(Stored with Git LFS)
Binary file not shown.
@ -1,64 +0,0 @@
|
||||
From dff965465ca9d9c4edaf0f90eadd9a6de335b354 Mon Sep 17 00:00:00 2001
|
||||
From: Niklas Schnelle <schnelle@linux.ibm.com>
|
||||
Date: Fri, 6 Dec 2024 15:28:08 +0100
|
||||
Subject: [PATCH] opticsmon: Fix runaway loop in on_link_change()
|
||||
MIME-Version: 1.0
|
||||
Content-Type: text/plain; charset=UTF-8
|
||||
Content-Transfer-Encoding: 8bit
|
||||
|
||||
When on_link_change() gets called with a netdev that would be monitored
|
||||
but hasn't entered zpci_list yet, reloads is 1 after the loops and
|
||||
a reload occurs. Then the netdev is found in the list and reloads
|
||||
becomes -1 which incorrectly triggers more reloads until underflow.
|
||||
Fix this by returning once the device is found. Also just check for
|
||||
reloads being larger than zero.
|
||||
|
||||
Fixes: c34adb9cabee ("opticsmon: Introduce opticsmon tool")
|
||||
Reviewed-by: Halil Pasic <pasic@linux.ibm.com>
|
||||
Signed-off-by: Niklas Schnelle <schnelle@linux.ibm.com>
|
||||
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
|
||||
---
|
||||
opticsmon/opticsmon.c | 10 +++++-----
|
||||
1 file changed, 5 insertions(+), 5 deletions(-)
|
||||
|
||||
diff --git a/opticsmon/opticsmon.c b/opticsmon/opticsmon.c
|
||||
index c2f355e2..50dd8d7f 100644
|
||||
--- a/opticsmon/opticsmon.c
|
||||
+++ b/opticsmon/opticsmon.c
|
||||
@@ -280,16 +280,15 @@ void on_link_change(struct zpci_netdev *netdev, void *arg)
|
||||
if (!ctx->zpci_list || util_list_is_empty(ctx->zpci_list))
|
||||
zpci_list_reload(&ctx->zpci_list);
|
||||
|
||||
-reload:
|
||||
+find:
|
||||
util_list_iterate(ctx->zpci_list, zdev) {
|
||||
for (i = 0; i < zdev->num_netdevs; i++) {
|
||||
if (!strcmp(zdev->netdevs[i].name, netdev->name)) {
|
||||
- reloads--;
|
||||
/* Skip data collection if operational state is
|
||||
* unchanged
|
||||
*/
|
||||
if (zdev->netdevs[i].operstate == netdev->operstate)
|
||||
- continue;
|
||||
+ return;
|
||||
/* Update operation state for VFs even though
|
||||
* they are skipped just for a consistent view
|
||||
*/
|
||||
@@ -297,14 +296,15 @@ void on_link_change(struct zpci_netdev *netdev, void *arg)
|
||||
/* Only collect optics data for PFs */
|
||||
if (!zpci_is_vf(zdev))
|
||||
dump_adapter_data(ctx, zdev);
|
||||
+ return;
|
||||
}
|
||||
}
|
||||
}
|
||||
/* Might be a new device, reload list of devices and retry */
|
||||
- if (reloads) {
|
||||
+ if (reloads > 0) {
|
||||
zpci_list_reload(&ctx->zpci_list);
|
||||
reloads--;
|
||||
- goto reload;
|
||||
+ goto find;
|
||||
}
|
||||
}
|
||||
|
@ -1,129 +0,0 @@
|
||||
From cf5560a100b5552e2eeeaac9c60a88ae77233530 Mon Sep 17 00:00:00 2001
|
||||
From: Niklas Schnelle <schnelle@linux.ibm.com>
|
||||
Date: Mon, 9 Dec 2024 15:08:03 +0100
|
||||
Subject: [PATCH] libzpci: opticsmon: Refactor on_link_change() using new
|
||||
zpci_find_by_netdev()
|
||||
MIME-Version: 1.0
|
||||
Content-Type: text/plain; charset=UTF-8
|
||||
Content-Transfer-Encoding: 8bit
|
||||
|
||||
Finding a PCI device given the name of a netdev seems generally useful
|
||||
so pull this out into a new zpci_find_by_netdev() function in libzpci
|
||||
and use this to simplify on_link_change() removing the need for
|
||||
backwards goto.
|
||||
|
||||
Reviewed-by: Halil Pasic <pasic@linux.ibm.com>
|
||||
Reviewed-by: Jan Höppner <hoeppner@linux.ibm.com>
|
||||
Signed-off-by: Niklas Schnelle <schnelle@linux.ibm.com>
|
||||
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
|
||||
---
|
||||
include/lib/pci_list.h | 3 +++
|
||||
libzpci/pci_list.c | 31 +++++++++++++++++++++++++++++++
|
||||
opticsmon/opticsmon.c | 27 +++++++++++----------------
|
||||
3 files changed, 45 insertions(+), 16 deletions(-)
|
||||
|
||||
diff --git a/include/lib/pci_list.h b/include/lib/pci_list.h
|
||||
index 829ec244..5b2918bc 100644
|
||||
--- a/include/lib/pci_list.h
|
||||
+++ b/include/lib/pci_list.h
|
||||
@@ -93,4 +93,7 @@ const char *zpci_pft_str(struct zpci_dev *zdev);
|
||||
const char *zpci_operstate_str(operstate_t state);
|
||||
operstate_t zpci_operstate_from_str(const char *oper_str);
|
||||
|
||||
+struct zpci_dev *zpci_find_by_netdev(struct util_list *zpci_list, char *netdev_name,
|
||||
+ struct zpci_netdev **netdev);
|
||||
+
|
||||
#endif /* LIB_ZPCI_PCI_LIST_H */
|
||||
diff --git a/libzpci/pci_list.c b/libzpci/pci_list.c
|
||||
index 10f64e89..e0d56e44 100644
|
||||
--- a/libzpci/pci_list.c
|
||||
+++ b/libzpci/pci_list.c
|
||||
@@ -356,3 +356,34 @@ void zpci_free_dev_list(struct util_list *zpci_list)
|
||||
}
|
||||
util_list_free(zpci_list);
|
||||
}
|
||||
+
|
||||
+/**
|
||||
+ * Find a PCI device given the name of a netdev
|
||||
+ *
|
||||
+ * This function allows finding a PCI device when only the name of one
|
||||
+ * of its netdevs is known.
|
||||
+ *
|
||||
+ * @param[in] zpci_list The device list to search
|
||||
+ * @param[in] netdev_name The name of the netdev
|
||||
+ * @param[out] netdev Pointer to store the netdev or NULL if
|
||||
+ * only the PCI device is needed
|
||||
+ *
|
||||
+ * @return The PCI device if one is found NULL otherwise
|
||||
+ */
|
||||
+struct zpci_dev *zpci_find_by_netdev(struct util_list *zpci_list, char *netdev_name,
|
||||
+ struct zpci_netdev **netdev)
|
||||
+{
|
||||
+ struct zpci_dev *zdev = NULL;
|
||||
+ int i;
|
||||
+
|
||||
+ util_list_iterate(zpci_list, zdev) {
|
||||
+ for (i = 0; i < zdev->num_netdevs; i++) {
|
||||
+ if (!strcmp(zdev->netdevs[i].name, netdev_name)) {
|
||||
+ if (netdev)
|
||||
+ *netdev = &zdev->netdevs[i];
|
||||
+ return zdev;
|
||||
+ }
|
||||
+ }
|
||||
+ }
|
||||
+ return NULL;
|
||||
+}
|
||||
diff --git a/opticsmon/opticsmon.c b/opticsmon/opticsmon.c
|
||||
index 50dd8d7f..7ecaa125 100644
|
||||
--- a/opticsmon/opticsmon.c
|
||||
+++ b/opticsmon/opticsmon.c
|
||||
@@ -274,38 +274,33 @@ static int oneshot_mode(struct opticsmon_ctx *ctx)
|
||||
void on_link_change(struct zpci_netdev *netdev, void *arg)
|
||||
{
|
||||
struct opticsmon_ctx *ctx = arg;
|
||||
- struct zpci_dev *zdev;
|
||||
- int i, reloads = 1;
|
||||
-
|
||||
- if (!ctx->zpci_list || util_list_is_empty(ctx->zpci_list))
|
||||
- zpci_list_reload(&ctx->zpci_list);
|
||||
+ struct zpci_netdev *found_netdev;
|
||||
+ struct zpci_dev *zdev = NULL;
|
||||
+ int reloads = 1;
|
||||
|
||||
-find:
|
||||
- util_list_iterate(ctx->zpci_list, zdev) {
|
||||
- for (i = 0; i < zdev->num_netdevs; i++) {
|
||||
- if (!strcmp(zdev->netdevs[i].name, netdev->name)) {
|
||||
+ do {
|
||||
+ if (ctx->zpci_list) {
|
||||
+ zdev = zpci_find_by_netdev(ctx->zpci_list, netdev->name, &found_netdev);
|
||||
+ if (zdev) {
|
||||
/* Skip data collection if operational state is
|
||||
* unchanged
|
||||
*/
|
||||
- if (zdev->netdevs[i].operstate == netdev->operstate)
|
||||
+ if (found_netdev->operstate == netdev->operstate)
|
||||
return;
|
||||
/* Update operation state for VFs even though
|
||||
* they are skipped just for a consistent view
|
||||
*/
|
||||
- zdev->netdevs[i].operstate = netdev->operstate;
|
||||
+ found_netdev->operstate = netdev->operstate;
|
||||
/* Only collect optics data for PFs */
|
||||
if (!zpci_is_vf(zdev))
|
||||
dump_adapter_data(ctx, zdev);
|
||||
return;
|
||||
}
|
||||
}
|
||||
- }
|
||||
- /* Might be a new device, reload list of devices and retry */
|
||||
- if (reloads > 0) {
|
||||
+ /* Could be uninitalized list or a new device, retry after reload */
|
||||
zpci_list_reload(&ctx->zpci_list);
|
||||
reloads--;
|
||||
- goto find;
|
||||
- }
|
||||
+ } while (reloads > 0);
|
||||
}
|
||||
|
||||
#define MAX_EVENTS 8
|
@ -1,147 +0,0 @@
|
||||
From 1e44ace41de3cbd744b22a8f9835473b091186e0 Mon Sep 17 00:00:00 2001
|
||||
From: Steffen Eiden <seiden@linux.ibm.com>
|
||||
Date: Thu, 18 Jul 2024 10:55:45 +0200
|
||||
Subject: [PATCH] rust/pvsecret: Refactor writing secret
|
||||
MIME-Version: 1.0
|
||||
Content-Type: text/plain; charset=UTF-8
|
||||
Content-Transfer-Encoding: 8bit
|
||||
|
||||
Refactor the writing of secret-type dependent output files to ease
|
||||
extensions.
|
||||
|
||||
Reviewed-by: Marc Hartmayer <marc@linux.ibm.com>
|
||||
Reviewed-by: Christoph Schlameuss <schlameuss@linux.ibm.com>
|
||||
Signed-off-by: Steffen Eiden <seiden@linux.ibm.com>
|
||||
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
|
||||
---
|
||||
rust/pv/src/uvsecret/guest_secret.rs | 2 +-
|
||||
rust/pvsecret/src/cmd/create.rs | 89 +++++++++++++++-------------
|
||||
2 files changed, 48 insertions(+), 43 deletions(-)
|
||||
|
||||
diff --git a/rust/pv/src/uvsecret/guest_secret.rs b/rust/pv/src/uvsecret/guest_secret.rs
|
||||
index 509691fa..4f1db31c 100644
|
||||
--- a/rust/pv/src/uvsecret/guest_secret.rs
|
||||
+++ b/rust/pv/src/uvsecret/guest_secret.rs
|
||||
@@ -68,7 +68,7 @@ impl GuestSecret {
|
||||
}
|
||||
|
||||
/// Reference to the confidential data
|
||||
- pub(crate) fn confidential(&self) -> &[u8] {
|
||||
+ pub fn confidential(&self) -> &[u8] {
|
||||
match &self {
|
||||
Self::Null => &[],
|
||||
Self::Association { secret, .. } => secret.value().as_slice(),
|
||||
diff --git a/rust/pvsecret/src/cmd/create.rs b/rust/pvsecret/src/cmd/create.rs
|
||||
index 808b29e1..9251c38c 100644
|
||||
--- a/rust/pvsecret/src/cmd/create.rs
|
||||
+++ b/rust/pvsecret/src/cmd/create.rs
|
||||
@@ -62,7 +62,7 @@ pub fn create(opt: &CreateSecretOpt) -> Result<()> {
|
||||
write_out(&opt.output, ser_asrbc, "add-secret request")?;
|
||||
info!("Successfully wrote the request to '{}'", &opt.output);
|
||||
|
||||
- write_secret(&opt.secret, &asrcb, &opt.output)
|
||||
+ write_secret(&opt.secret, asrcb.guest_secret(), &opt.output)
|
||||
}
|
||||
|
||||
/// Read+parse the first key from the buffer.
|
||||
@@ -206,54 +206,59 @@ fn read_cuid(asrcb: &mut AddSecretRequest, opt: &CreateSecretOpt) -> Result<()>
|
||||
Ok(())
|
||||
}
|
||||
|
||||
+// Write non confidential data (=name+id) to a yaml stdout
|
||||
+fn write_yaml<P: AsRef<Path>>(
|
||||
+ name: &str,
|
||||
+ guest_secret: &GuestSecret,
|
||||
+ stdout: &bool,
|
||||
+ outp_path: P,
|
||||
+) -> Result<()> {
|
||||
+ debug!("Non-confidential secret information: {guest_secret:x?}");
|
||||
+
|
||||
+ let secret_info = serde_yaml::to_string(guest_secret)?;
|
||||
+ if stdout.to_owned() {
|
||||
+ println!("{secret_info}");
|
||||
+ return Ok(());
|
||||
+ }
|
||||
+
|
||||
+ let gen_name: String = name
|
||||
+ .chars()
|
||||
+ .map(|c| if c.is_whitespace() { '_' } else { c })
|
||||
+ .collect();
|
||||
+ let mut yaml_path = outp_path
|
||||
+ .as_ref()
|
||||
+ .parent()
|
||||
+ .with_context(|| format!("Cannot open directory of {:?}", outp_path.as_ref()))?
|
||||
+ .to_owned();
|
||||
+ yaml_path.push(gen_name);
|
||||
+ yaml_path.set_extension("yaml");
|
||||
+ write_out(&yaml_path, secret_info, "secret information")?;
|
||||
+ warn!(
|
||||
+ "Successfully wrote secret info to '{}'",
|
||||
+ yaml_path.display().to_string()
|
||||
+ );
|
||||
+ Ok(())
|
||||
+}
|
||||
+
|
||||
/// Write the generated secret (if any) to the specified output stream
|
||||
fn write_secret<P: AsRef<Path>>(
|
||||
secret: &AddSecretType,
|
||||
- asrcb: &AddSecretRequest,
|
||||
+ guest_secret: &GuestSecret,
|
||||
outp_path: P,
|
||||
) -> Result<()> {
|
||||
- if let AddSecretType::Association {
|
||||
- name,
|
||||
- stdout,
|
||||
- output_secret: secret_out,
|
||||
- ..
|
||||
- } = secret
|
||||
- {
|
||||
- let gen_name: String = name
|
||||
- .chars()
|
||||
- .map(|c| if c.is_whitespace() { '_' } else { c })
|
||||
- .collect();
|
||||
- let mut gen_path = outp_path
|
||||
- .as_ref()
|
||||
- .parent()
|
||||
- .with_context(|| format!("Cannot open directory of {:?}", outp_path.as_ref()))?
|
||||
- .to_owned();
|
||||
- gen_path.push(format!("{gen_name}.yaml"));
|
||||
-
|
||||
- // write non confidential data (=name+id) to a yaml
|
||||
- let secret_info = serde_yaml::to_string(asrcb.guest_secret())?;
|
||||
- if stdout.to_owned() {
|
||||
- println!("{secret_info}");
|
||||
- } else {
|
||||
- write_out(&gen_path, secret_info, "association secret info")?;
|
||||
- debug!(
|
||||
- "Non-confidential secret information: {:x?}",
|
||||
- asrcb.guest_secret()
|
||||
- );
|
||||
- warn!(
|
||||
- "Successfully wrote association info to '{}'",
|
||||
- gen_path.display()
|
||||
- );
|
||||
- }
|
||||
-
|
||||
- if let Some(path) = secret_out {
|
||||
- if let GuestSecret::Association { secret, .. } = asrcb.guest_secret() {
|
||||
- write_out(path, secret.value(), "Association secret")?
|
||||
- } else {
|
||||
- unreachable!("The secret type has to be `association` at this point (bug)!")
|
||||
+ match secret {
|
||||
+ AddSecretType::Association {
|
||||
+ name,
|
||||
+ stdout,
|
||||
+ output_secret,
|
||||
+ ..
|
||||
+ } => {
|
||||
+ write_yaml(name, guest_secret, stdout, outp_path)?;
|
||||
+ if let Some(path) = output_secret {
|
||||
+ write_out(path, guest_secret.confidential(), "Association secret")?
|
||||
}
|
||||
- info!("Successfully wrote generated association secret to '{path}'");
|
||||
}
|
||||
+ _ => (),
|
||||
};
|
||||
Ok(())
|
||||
}
|
@ -1,467 +0,0 @@
|
||||
From d1636168b26cc842bc0766235c8a4f2da9663f20 Mon Sep 17 00:00:00 2001
|
||||
From: Steffen Eiden <seiden@linux.ibm.com>
|
||||
Date: Tue, 5 Mar 2024 10:46:29 +0100
|
||||
Subject: [PATCH] rust/pv: Support for writing data in PEM format
|
||||
MIME-Version: 1.0
|
||||
Content-Type: text/plain; charset=UTF-8
|
||||
Content-Transfer-Encoding: 8bit
|
||||
|
||||
Use existing OpenSSL functionalities to create PEM files containing
|
||||
arbitrary data.
|
||||
|
||||
Acked-by: Marc Hartmayer <marc@linux.ibm.com>
|
||||
Acked-by: Christoph Schlameuss <schlameuss@linux.ibm.com>
|
||||
Signed-off-by: Steffen Eiden <seiden@linux.ibm.com>
|
||||
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
|
||||
---
|
||||
rust/pv/src/error.rs | 3 +
|
||||
rust/pv/src/lib.rs | 6 +
|
||||
rust/pv/src/openssl_extensions/bio.rs | 85 +++++++
|
||||
rust/pv/src/openssl_extensions/mod.rs | 2 +
|
||||
.../src/openssl_extensions/stackable_crl.rs | 41 +---
|
||||
rust/pv/src/pem_utils.rs | 222 ++++++++++++++++++
|
||||
6 files changed, 321 insertions(+), 38 deletions(-)
|
||||
create mode 100644 rust/pv/src/openssl_extensions/bio.rs
|
||||
create mode 100644 rust/pv/src/pem_utils.rs
|
||||
|
||||
diff --git a/rust/pv/src/error.rs b/rust/pv/src/error.rs
|
||||
index af85e93e..3ba808f2 100644
|
||||
--- a/rust/pv/src/error.rs
|
||||
+++ b/rust/pv/src/error.rs
|
||||
@@ -106,6 +106,9 @@ pub enum Error {
|
||||
)]
|
||||
AddDataMissing(&'static str),
|
||||
|
||||
+ #[error("An ASCII string was expected, but non-ASCII characters were received.")]
|
||||
+ NonAscii,
|
||||
+
|
||||
// errors from other crates
|
||||
#[error(transparent)]
|
||||
PvCore(#[from] pv_core::Error),
|
||||
diff --git a/rust/pv/src/lib.rs b/rust/pv/src/lib.rs
|
||||
index 7a33210c..ec31b9a4 100644
|
||||
--- a/rust/pv/src/lib.rs
|
||||
+++ b/rust/pv/src/lib.rs
|
||||
@@ -37,6 +37,7 @@ mod brcb;
|
||||
mod crypto;
|
||||
mod error;
|
||||
mod openssl_extensions;
|
||||
+mod pem_utils;
|
||||
mod req;
|
||||
mod utils;
|
||||
mod uvattest;
|
||||
@@ -71,6 +72,11 @@ pub mod attest {
|
||||
};
|
||||
}
|
||||
|
||||
+/// Definitions and functions to write objects in PEM format
|
||||
+pub mod pem {
|
||||
+ pub use crate::pem_utils::Pem;
|
||||
+}
|
||||
+
|
||||
/// Miscellaneous functions and definitions
|
||||
pub mod misc {
|
||||
pub use pv_core::misc::*;
|
||||
diff --git a/rust/pv/src/openssl_extensions/bio.rs b/rust/pv/src/openssl_extensions/bio.rs
|
||||
new file mode 100644
|
||||
index 00000000..73528eed
|
||||
--- /dev/null
|
||||
+++ b/rust/pv/src/openssl_extensions/bio.rs
|
||||
@@ -0,0 +1,85 @@
|
||||
+// SPDX-License-Identifier: MIT
|
||||
+//
|
||||
+// Copyright IBM Corp. 2024
|
||||
+
|
||||
+use core::slice;
|
||||
+use openssl::error::ErrorStack;
|
||||
+use openssl_sys::BIO_new_mem_buf;
|
||||
+use std::ffi::c_int;
|
||||
+use std::{marker::PhantomData, ptr};
|
||||
+
|
||||
+pub struct BioMem(*mut openssl_sys::BIO);
|
||||
+
|
||||
+impl Drop for BioMem {
|
||||
+ fn drop(&mut self) {
|
||||
+ // SAFETY: Pointer is valid. The pointer value is dropped after the free.
|
||||
+ unsafe {
|
||||
+ openssl_sys::BIO_free_all(self.0);
|
||||
+ }
|
||||
+ }
|
||||
+}
|
||||
+
|
||||
+impl BioMem {
|
||||
+ pub fn new() -> Result<Self, ErrorStack> {
|
||||
+ openssl_sys::init();
|
||||
+
|
||||
+ // SAFETY: Returns a valid pointer or null. null-case is tested right after this.
|
||||
+ let bio = unsafe { openssl_sys::BIO_new(openssl_sys::BIO_s_mem()) };
|
||||
+ match bio.is_null() {
|
||||
+ true => Err(ErrorStack::get()),
|
||||
+ false => Ok(Self(bio)),
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ pub fn as_ptr(&self) -> *mut openssl_sys::BIO {
|
||||
+ self.0
|
||||
+ }
|
||||
+
|
||||
+ /// Copies the content of this slice into a Vec
|
||||
+ pub fn to_vec(&self) -> Vec<u8> {
|
||||
+ let buf;
|
||||
+ // SAFTEY: BIO provides a continuous memory that can be used to build a slice.
|
||||
+ unsafe {
|
||||
+ let mut ptr = ptr::null_mut();
|
||||
+ let len = openssl_sys::BIO_get_mem_data(self.0, &mut ptr);
|
||||
+ buf = slice::from_raw_parts(ptr as *const _ as *const _, len as usize)
|
||||
+ }
|
||||
+ buf.to_vec()
|
||||
+ }
|
||||
+}
|
||||
+
|
||||
+pub struct BioMemSlice<'a>(*mut openssl_sys::BIO, PhantomData<&'a [u8]>);
|
||||
+impl Drop for BioMemSlice<'_> {
|
||||
+ fn drop(&mut self) {
|
||||
+ // SAFETY: Pointer is valid. The pointer value is dropped after the free.
|
||||
+ unsafe {
|
||||
+ openssl_sys::BIO_free_all(self.0);
|
||||
+ }
|
||||
+ }
|
||||
+}
|
||||
+
|
||||
+impl<'a> BioMemSlice<'a> {
|
||||
+ pub fn new(buf: &'a [u8]) -> Result<BioMemSlice<'a>, ErrorStack> {
|
||||
+ openssl_sys::init();
|
||||
+
|
||||
+ // SAFETY: `buf` is a slice (i.e. pointer+size) pointing to a valid memory region.
|
||||
+ // So the resulting bio is valid. Lifetime of the slice is connected by this Rust
|
||||
+ // structure.
|
||||
+ assert!(buf.len() <= c_int::MAX as usize);
|
||||
+ let bio = unsafe {
|
||||
+ {
|
||||
+ let r = BIO_new_mem_buf(buf.as_ptr() as *const _, buf.len() as c_int);
|
||||
+ match r.is_null() {
|
||||
+ true => Err(ErrorStack::get()),
|
||||
+ false => Ok(r),
|
||||
+ }
|
||||
+ }?
|
||||
+ };
|
||||
+
|
||||
+ Ok(BioMemSlice(bio, PhantomData))
|
||||
+ }
|
||||
+
|
||||
+ pub fn as_ptr(&self) -> *mut openssl_sys::BIO {
|
||||
+ self.0
|
||||
+ }
|
||||
+}
|
||||
diff --git a/rust/pv/src/openssl_extensions/mod.rs b/rust/pv/src/openssl_extensions/mod.rs
|
||||
index fab26638..f6234e5d 100644
|
||||
--- a/rust/pv/src/openssl_extensions/mod.rs
|
||||
+++ b/rust/pv/src/openssl_extensions/mod.rs
|
||||
@@ -6,8 +6,10 @@
|
||||
|
||||
/// Extensions to the rust-openssl crate
|
||||
mod akid;
|
||||
+mod bio;
|
||||
mod crl;
|
||||
mod stackable_crl;
|
||||
|
||||
pub use akid::*;
|
||||
+pub use bio::*;
|
||||
pub use crl::*;
|
||||
diff --git a/rust/pv/src/openssl_extensions/stackable_crl.rs b/rust/pv/src/openssl_extensions/stackable_crl.rs
|
||||
index aef7cf86..12a9f9de 100644
|
||||
--- a/rust/pv/src/openssl_extensions/stackable_crl.rs
|
||||
+++ b/rust/pv/src/openssl_extensions/stackable_crl.rs
|
||||
@@ -2,16 +2,14 @@
|
||||
//
|
||||
// Copyright IBM Corp. 2023
|
||||
|
||||
-use std::{marker::PhantomData, ptr};
|
||||
-
|
||||
+use crate::openssl_extensions::bio::BioMemSlice;
|
||||
use foreign_types::{ForeignType, ForeignTypeRef};
|
||||
use openssl::{
|
||||
error::ErrorStack,
|
||||
stack::Stackable,
|
||||
x509::{X509Crl, X509CrlRef},
|
||||
};
|
||||
-use openssl_sys::BIO_new_mem_buf;
|
||||
-use std::ffi::c_int;
|
||||
+use std::ptr;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct StackableX509Crl(*mut openssl_sys::X509_CRL);
|
||||
@@ -62,44 +60,11 @@ impl Stackable for StackableX509Crl {
|
||||
type StackType = openssl_sys::stack_st_X509_CRL;
|
||||
}
|
||||
|
||||
-pub struct MemBioSlice<'a>(*mut openssl_sys::BIO, PhantomData<&'a [u8]>);
|
||||
-impl Drop for MemBioSlice<'_> {
|
||||
- fn drop(&mut self) {
|
||||
- unsafe {
|
||||
- openssl_sys::BIO_free_all(self.0);
|
||||
- }
|
||||
- }
|
||||
-}
|
||||
-
|
||||
-impl<'a> MemBioSlice<'a> {
|
||||
- pub fn new(buf: &'a [u8]) -> Result<MemBioSlice<'a>, ErrorStack> {
|
||||
- openssl_sys::init();
|
||||
-
|
||||
- assert!(buf.len() <= c_int::MAX as usize);
|
||||
- let bio = unsafe {
|
||||
- {
|
||||
- let r = BIO_new_mem_buf(buf.as_ptr() as *const _, buf.len() as c_int);
|
||||
- if r.is_null() {
|
||||
- Err(ErrorStack::get())
|
||||
- } else {
|
||||
- Ok(r)
|
||||
- }
|
||||
- }?
|
||||
- };
|
||||
-
|
||||
- Ok(MemBioSlice(bio, PhantomData))
|
||||
- }
|
||||
-
|
||||
- pub fn as_ptr(&self) -> *mut openssl_sys::BIO {
|
||||
- self.0
|
||||
- }
|
||||
-}
|
||||
-
|
||||
impl StackableX509Crl {
|
||||
pub fn stack_from_pem(pem: &[u8]) -> Result<Vec<X509Crl>, ErrorStack> {
|
||||
unsafe {
|
||||
openssl_sys::init();
|
||||
- let bio = MemBioSlice::new(pem)?;
|
||||
+ let bio = BioMemSlice::new(pem)?;
|
||||
|
||||
let mut crls = vec![];
|
||||
loop {
|
||||
diff --git a/rust/pv/src/pem_utils.rs b/rust/pv/src/pem_utils.rs
|
||||
new file mode 100644
|
||||
index 00000000..e6462519
|
||||
--- /dev/null
|
||||
+++ b/rust/pv/src/pem_utils.rs
|
||||
@@ -0,0 +1,222 @@
|
||||
+// SPDX-License-Identifier: MIT
|
||||
+//
|
||||
+// Copyright IBM Corp. 2024
|
||||
+
|
||||
+use crate::Result;
|
||||
+use crate::{openssl_extensions::BioMem, Error};
|
||||
+use openssl::error::ErrorStack;
|
||||
+use pv_core::request::Confidential;
|
||||
+use std::{
|
||||
+ ffi::{c_char, CString},
|
||||
+ fmt::Display,
|
||||
+};
|
||||
+
|
||||
+mod ffi {
|
||||
+ use openssl_sys::BIO;
|
||||
+ use std::ffi::{c_char, c_int, c_long, c_uchar};
|
||||
+ extern "C" {
|
||||
+ pub fn PEM_write_bio(
|
||||
+ bio: *mut BIO,
|
||||
+ name: *const c_char,
|
||||
+ header: *const c_char,
|
||||
+ data: *const c_uchar,
|
||||
+ len: c_long,
|
||||
+ ) -> c_int;
|
||||
+ }
|
||||
+}
|
||||
+
|
||||
+/// Thin wrapper around [`CString`] only containing ASCII chars.
|
||||
+#[derive(Debug)]
|
||||
+struct AsciiCString(CString);
|
||||
+
|
||||
+impl AsciiCString {
|
||||
+ /// Convert from string
|
||||
+ ///
|
||||
+ /// # Returns
|
||||
+ /// Error if string is not ASCII or contains null chars
|
||||
+ pub(crate) fn from_str(s: &str) -> Result<Self> {
|
||||
+ match s.is_ascii() {
|
||||
+ true => Ok(Self(CString::new(s).map_err(|_| Error::NonAscii)?)),
|
||||
+ false => Err(Error::NonAscii),
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ fn as_ptr(&self) -> *const c_char {
|
||||
+ self.0.as_ptr()
|
||||
+ }
|
||||
+}
|
||||
+
|
||||
+/// Helper struct to construct the PEM format
|
||||
+#[derive(Debug)]
|
||||
+struct InnerPem<'d> {
|
||||
+ name: AsciiCString,
|
||||
+ header: Option<AsciiCString>,
|
||||
+ data: &'d [u8],
|
||||
+}
|
||||
+
|
||||
+impl<'d> InnerPem<'d> {
|
||||
+ fn new(name: &str, header: Option<String>, data: &'d [u8]) -> Result<Self> {
|
||||
+ Ok(Self {
|
||||
+ name: AsciiCString::from_str(name)?,
|
||||
+ header: match header {
|
||||
+ Some(h) => Some(AsciiCString::from_str(&h)?),
|
||||
+ None => None,
|
||||
+ },
|
||||
+ data,
|
||||
+ })
|
||||
+ }
|
||||
+
|
||||
+ /// Generate PEM representation of the data
|
||||
+ fn to_pem(&self) -> Result<Vec<u8>> {
|
||||
+ let bio = BioMem::new()?;
|
||||
+ let hdr_ptr = match self.header {
|
||||
+ // avoid moving variable -> use reference
|
||||
+ Some(ref h) => h.as_ptr(),
|
||||
+ None => std::ptr::null(),
|
||||
+ };
|
||||
+
|
||||
+ // SAFETY:
|
||||
+ // All pointers point to valid C strings or memory regions
|
||||
+ let rc = unsafe {
|
||||
+ ffi::PEM_write_bio(
|
||||
+ bio.as_ptr(),
|
||||
+ self.name.as_ptr(),
|
||||
+ hdr_ptr,
|
||||
+ self.data.as_ptr(),
|
||||
+ self.data.len() as std::ffi::c_long,
|
||||
+ )
|
||||
+ };
|
||||
+
|
||||
+ match rc {
|
||||
+ 1 => Err(Error::InternalSsl("Could not write PEM", ErrorStack::get())),
|
||||
+ _ => Ok(bio.to_vec()),
|
||||
+ }
|
||||
+ }
|
||||
+}
|
||||
+
|
||||
+/// Data in PEM format
|
||||
+///
|
||||
+/// Displays into a printable PEM structure.
|
||||
+/// Must be constructed from another structure in this library.
|
||||
+///
|
||||
+/// ```rust,ignore
|
||||
+/// let pem: Pem = ...;
|
||||
+/// println!("PEM {pem}");
|
||||
+/// ```
|
||||
+/// ```PEM
|
||||
+///-----BEGIN <name>-----
|
||||
+///<header>
|
||||
+///
|
||||
+///<Base64 formatted binary data>
|
||||
+///-----END <name>-----
|
||||
+
|
||||
+#[derive(Debug)]
|
||||
+pub struct Pem {
|
||||
+ pem: Confidential<String>,
|
||||
+}
|
||||
+
|
||||
+#[allow(unused)]
|
||||
+impl Pem {
|
||||
+ /// Create a new PEM structure.
|
||||
+ ///
|
||||
+ /// # Errors
|
||||
+ ///
|
||||
+ /// This function will return an error if name or header contain non-ASCII chars, or OpenSSL
|
||||
+ /// could not generate the PEM (very likely due to OOM).
|
||||
+ pub(crate) fn new<D, H>(name: &str, header: H, data: D) -> Result<Self>
|
||||
+ where
|
||||
+ D: AsRef<[u8]>,
|
||||
+ H: Into<Option<String>>,
|
||||
+ {
|
||||
+ let mut header = header.into();
|
||||
+ let header = match header {
|
||||
+ Some(h) if h.ends_with('\n') => Some(h),
|
||||
+ Some(h) if h.is_empty() => None,
|
||||
+ Some(mut h) => {
|
||||
+ h.push('\n');
|
||||
+ Some(h)
|
||||
+ }
|
||||
+ None => None,
|
||||
+ };
|
||||
+
|
||||
+ let inner_pem = InnerPem::new(name, header, data.as_ref())?;
|
||||
+
|
||||
+ // Create the PEM format eagerly so that to_string/display cannot fail because of ASCII or OpenSSL Errors
|
||||
+ // Both error should be very unlikely
|
||||
+ // OpenSSL should be able to create PEM if there is enough memory and produce a non-null
|
||||
+ // terminated ASCII-string
|
||||
+ // Unwrap succeeds it's all ASCII
|
||||
+ // Std lib implements all the conversations without a copy
|
||||
+ let pem = CString::new(inner_pem.to_pem()?)
|
||||
+ .map_err(|_| Error::NonAscii)?
|
||||
+ .into_string()
|
||||
+ .unwrap()
|
||||
+ .into();
|
||||
+
|
||||
+ Ok(Self { pem })
|
||||
+ }
|
||||
+
|
||||
+ /// Converts the PEM-data into a byte vector.
|
||||
+ ///
|
||||
+ /// This consumes the `PEM`.
|
||||
+ #[inline]
|
||||
+ #[must_use = "`self` will be dropped if the result is not used"]
|
||||
+ pub fn into_bytes(self) -> Confidential<Vec<u8>> {
|
||||
+ self.pem.into_inner().into_bytes().into()
|
||||
+ }
|
||||
+}
|
||||
+
|
||||
+impl Display for Pem {
|
||||
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
+ self.pem.value().fmt(f)
|
||||
+ }
|
||||
+}
|
||||
+
|
||||
+#[cfg(test)]
|
||||
+mod test {
|
||||
+ use super::*;
|
||||
+
|
||||
+ #[test]
|
||||
+ fn no_data() {
|
||||
+ const EXP: &str =
|
||||
+ "-----BEGIN PEM test-----\ntest hdr value: 17\n\n-----END PEM test-----\n";
|
||||
+ let test_pem = Pem::new("PEM test", "test hdr value: 17".to_string(), []).unwrap();
|
||||
+ let pem_str = test_pem.to_string();
|
||||
+ assert_eq!(pem_str, EXP);
|
||||
+ }
|
||||
+
|
||||
+ #[test]
|
||||
+ fn no_hdr() {
|
||||
+ const EXP: &str =
|
||||
+ "-----BEGIN PEM test-----\ndmVyeSBzZWNyZXQga2V5\n-----END PEM test-----\n";
|
||||
+ let test_pem = Pem::new("PEM test", None, "very secret key").unwrap();
|
||||
+ let pem_str = test_pem.to_string();
|
||||
+ assert_eq!(pem_str, EXP);
|
||||
+ }
|
||||
+
|
||||
+ #[test]
|
||||
+ fn some_data() {
|
||||
+ const EXP: &str= "-----BEGIN PEM test-----\ntest hdr value: 17\n\ndmVyeSBzZWNyZXQga2V5\n-----END PEM test-----\n";
|
||||
+ let test_pem = Pem::new(
|
||||
+ "PEM test",
|
||||
+ "test hdr value: 17".to_string(),
|
||||
+ "very secret key",
|
||||
+ )
|
||||
+ .unwrap();
|
||||
+ let pem_str = test_pem.to_string();
|
||||
+ assert_eq!(pem_str, EXP);
|
||||
+ }
|
||||
+
|
||||
+ #[test]
|
||||
+ fn data_linebreak() {
|
||||
+ const EXP: &str= "-----BEGIN PEM test-----\ntest hdr value: 17\n\ndmVyeSBzZWNyZXQga2V5\n-----END PEM test-----\n";
|
||||
+ let test_pem = Pem::new(
|
||||
+ "PEM test",
|
||||
+ "test hdr value: 17\n".to_string(),
|
||||
+ "very secret key",
|
||||
+ )
|
||||
+ .unwrap();
|
||||
+ let pem_str = test_pem.to_string();
|
||||
+ assert_eq!(pem_str, EXP);
|
||||
+ }
|
||||
+}
|
@ -1,57 +0,0 @@
|
||||
From 69eb06f39e5134565babfe96c66a3786c0a571cf Mon Sep 17 00:00:00 2001
|
||||
From: Steffen Eiden <seiden@linux.ibm.com>
|
||||
Date: Tue, 20 Feb 2024 14:50:47 +0100
|
||||
Subject: [PATCH] rust/pv_core: Update ffi.rs to linux/uvdevice.h v6.13
|
||||
MIME-Version: 1.0
|
||||
Content-Type: text/plain; charset=UTF-8
|
||||
Content-Transfer-Encoding: 8bit
|
||||
|
||||
While at it, add a file global #[allow(dead_code)].
|
||||
The file is a rustified copy of linux/arch/s390/include/uapi/asm/uvdevice.h
|
||||
and there might be things that are not needed here but are defined in that header.
|
||||
|
||||
Acked-by: Marc Hartmayer <marc@linux.ibm.com>
|
||||
Reviewed-by: Christoph Schlameuss <schlameuss@linux.ibm.com>
|
||||
Signed-off-by: Steffen Eiden <seiden@linux.ibm.com>
|
||||
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
|
||||
---
|
||||
rust/pv_core/src/uvdevice/ffi.rs | 11 +++++++++--
|
||||
1 file changed, 9 insertions(+), 2 deletions(-)
|
||||
|
||||
diff --git a/rust/pv_core/src/uvdevice/ffi.rs b/rust/pv_core/src/uvdevice/ffi.rs
|
||||
index bbcc5867..3d9998db 100644
|
||||
--- a/rust/pv_core/src/uvdevice/ffi.rs
|
||||
+++ b/rust/pv_core/src/uvdevice/ffi.rs
|
||||
@@ -2,6 +2,13 @@
|
||||
//
|
||||
// Copyright IBM Corp. 2023
|
||||
|
||||
+// This file is a rustified copy of linux/arch/s390/include/uapi/asm/uvdevice.h
|
||||
+// There might be things that are not needed here but nontheless defined in that header.
|
||||
+// Those two files should be in sync -> there might be unused/dead code.
|
||||
+//
|
||||
+// The `UVIO_IOCTL_*` and `UVIO_SUPP_*` macros
|
||||
+#![allow(dead_code)]
|
||||
+
|
||||
use std::mem::size_of;
|
||||
|
||||
use crate::{assert_size, static_assert};
|
||||
@@ -11,9 +18,8 @@ pub const UVIO_ATT_ARCB_MAX_LEN: usize = 0x100000;
|
||||
pub const UVIO_ATT_MEASUREMENT_MAX_LEN: usize = 0x8000;
|
||||
pub const UVIO_ATT_ADDITIONAL_MAX_LEN: usize = 0x8000;
|
||||
pub const UVIO_ADD_SECRET_MAX_LEN: usize = 0x100000;
|
||||
-#[allow(unused)]
|
||||
-// here for completeness
|
||||
pub const UVIO_LIST_SECRETS_LEN: usize = 0x1000;
|
||||
+pub const UVIO_RETR_SECRET_MAX_LEN: usize = 0x2000;
|
||||
|
||||
// equal to ascii 'u'
|
||||
pub const UVIO_TYPE_UVC: u8 = 117u8;
|
||||
@@ -23,6 +29,7 @@ pub const UVIO_IOCTL_ATT_NR: u8 = 1;
|
||||
pub const UVIO_IOCTL_ADD_SECRET_NR: u8 = 2;
|
||||
pub const UVIO_IOCTL_LIST_SECRETS_NR: u8 = 3;
|
||||
pub const UVIO_IOCTL_LOCK_SECRETS_NR: u8 = 4;
|
||||
+pub const UVIO_IOCTL_RETR_SECRET_NR: u8 = 5;
|
||||
|
||||
/// Uvdevice IOCTL control block
|
||||
/// Programs can use this struct to communicate with the uvdevice via IOCTLs
|
@ -1,204 +0,0 @@
|
||||
From 01cd81ecf5d1a7e1e504ae1b67692cf63cd4b51d Mon Sep 17 00:00:00 2001
|
||||
From: Steffen Eiden <seiden@linux.ibm.com>
|
||||
Date: Tue, 5 Mar 2024 11:56:57 +0100
|
||||
Subject: [PATCH] rust/pv_core: Retrieve Secret UVC
|
||||
MIME-Version: 1.0
|
||||
Content-Type: text/plain; charset=UTF-8
|
||||
Content-Transfer-Encoding: 8bit
|
||||
|
||||
Create the uvdevice-IOCTL functionality for the new Retrieve Secret UVC.
|
||||
|
||||
Reviewed-by: Christoph Schlameuss <schlameuss@linux.ibm.com>
|
||||
Acked-by: Marc Hartmayer <marc@linux.ibm.com>
|
||||
Signed-off-by: Steffen Eiden <seiden@linux.ibm.com>
|
||||
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
|
||||
---
|
||||
rust/pv_core/src/error.rs | 7 ++
|
||||
rust/pv_core/src/lib.rs | 2 +-
|
||||
rust/pv_core/src/uvdevice/secret.rs | 97 +++++++++++++++++++++++-
|
||||
rust/pv_core/src/uvdevice/secret_list.rs | 15 ++++
|
||||
4 files changed, 118 insertions(+), 3 deletions(-)
|
||||
|
||||
diff --git a/rust/pv_core/src/error.rs b/rust/pv_core/src/error.rs
|
||||
index 20fca24d..ba7b7e26 100644
|
||||
--- a/rust/pv_core/src/error.rs
|
||||
+++ b/rust/pv_core/src/error.rs
|
||||
@@ -4,6 +4,8 @@
|
||||
|
||||
use std::path::PathBuf;
|
||||
|
||||
+use crate::uv::SecretId;
|
||||
+
|
||||
/// Result type for this crate
|
||||
pub type Result<T, E = Error> = std::result::Result<T, E>;
|
||||
|
||||
@@ -70,6 +72,11 @@ pub enum Error {
|
||||
#[error("The attestation request does not specify a measurement size or measurement data.")]
|
||||
BinArcbNoMeasurement,
|
||||
|
||||
+ #[error(
|
||||
+ "The secret with the ID {id} cannot be retrieved. The requested size is too large ({size})"
|
||||
+ )]
|
||||
+ InvalidRetrievableSecretType { id: SecretId, size: usize },
|
||||
+
|
||||
// errors from other crates
|
||||
#[error(transparent)]
|
||||
Io(#[from] std::io::Error),
|
||||
diff --git a/rust/pv_core/src/lib.rs b/rust/pv_core/src/lib.rs
|
||||
index 349c0b28..5922211f 100644
|
||||
--- a/rust/pv_core/src/lib.rs
|
||||
+++ b/rust/pv_core/src/lib.rs
|
||||
@@ -32,7 +32,7 @@ pub mod misc {
|
||||
/// [`crate::uv::UvCmd`]
|
||||
pub mod uv {
|
||||
pub use crate::uvdevice::attest::AttestationCmd;
|
||||
- pub use crate::uvdevice::secret::{AddCmd, ListCmd, LockCmd};
|
||||
+ pub use crate::uvdevice::secret::{AddCmd, ListCmd, LockCmd, RetrieveCmd};
|
||||
pub use crate::uvdevice::secret_list::{ListableSecretType, SecretEntry, SecretId, SecretList};
|
||||
pub use crate::uvdevice::{ConfigUid, UvCmd, UvDevice, UvDeviceInfo, UvFlags, UvcSuccess};
|
||||
}
|
||||
diff --git a/rust/pv_core/src/uvdevice/secret.rs b/rust/pv_core/src/uvdevice/secret.rs
|
||||
index 6c22b6ed..263f17d5 100644
|
||||
--- a/rust/pv_core/src/uvdevice/secret.rs
|
||||
+++ b/rust/pv_core/src/uvdevice/secret.rs
|
||||
@@ -3,8 +3,15 @@
|
||||
// Copyright IBM Corp. 2023
|
||||
|
||||
use super::ffi;
|
||||
-use crate::{request::MagicValue, uv::UvCmd, uvsecret::AddSecretMagic, Error, Result, PAGESIZE};
|
||||
-use std::io::Read;
|
||||
+use crate::{
|
||||
+ request::{Confidential, MagicValue},
|
||||
+ uv::{SecretEntry, UvCmd},
|
||||
+ uvsecret::AddSecretMagic,
|
||||
+ Error, Result, PAGESIZE,
|
||||
+};
|
||||
+use log::debug;
|
||||
+use std::{io::Read, mem::size_of_val};
|
||||
+use zerocopy::AsBytes;
|
||||
|
||||
/// _List Secrets_ Ultravisor command.
|
||||
///
|
||||
@@ -116,3 +123,89 @@ impl UvCmd for LockCmd {
|
||||
}
|
||||
}
|
||||
}
|
||||
+
|
||||
+/// Retrieve a secret value from UV store
|
||||
+#[derive(Debug)]
|
||||
+pub struct RetrieveCmd {
|
||||
+ entry: SecretEntry,
|
||||
+ key: Confidential<Vec<u8>>,
|
||||
+}
|
||||
+
|
||||
+impl RetrieveCmd {
|
||||
+ /// Maximum size of a retrieved key (=2 pages)
|
||||
+ pub const MAX_SIZE: usize = ffi::UVIO_RETR_SECRET_MAX_LEN;
|
||||
+
|
||||
+ /// Create a retrieve-secret UVC from a [`SecretEntry`].
|
||||
+ ///
|
||||
+ /// This uses the index of the secret entry for the UVC.
|
||||
+ pub fn from_entry(entry: SecretEntry) -> Result<Self> {
|
||||
+ entry.try_into()
|
||||
+ }
|
||||
+
|
||||
+ /// Transform a [`RetrieveCmd`] into a key-vector.
|
||||
+ ///
|
||||
+ /// Only makes sense to call after a successful UVC execution.
|
||||
+ pub fn into_key(self) -> Confidential<Vec<u8>> {
|
||||
+ self.key
|
||||
+ }
|
||||
+
|
||||
+ /// Get the secret entry
|
||||
+ ///
|
||||
+ /// Get the secret entry that is used as metadata to retrieve the secret
|
||||
+ pub fn meta_data(&self) -> &SecretEntry {
|
||||
+ &self.entry
|
||||
+ }
|
||||
+}
|
||||
+
|
||||
+impl TryFrom<SecretEntry> for RetrieveCmd {
|
||||
+ type Error = Error;
|
||||
+
|
||||
+ fn try_from(entry: SecretEntry) -> Result<Self> {
|
||||
+ let len = entry.secret_size() as usize;
|
||||
+
|
||||
+ // Next to impossible if the secret entry is a valid response from UV
|
||||
+ if len > Self::MAX_SIZE {
|
||||
+ return Err(Error::InvalidRetrievableSecretType {
|
||||
+ id: entry.secret_id().to_owned(),
|
||||
+ size: len,
|
||||
+ });
|
||||
+ }
|
||||
+
|
||||
+ // Ensure that an u16 fits into the buffer.
|
||||
+ let size = std::cmp::max(size_of_val(&entry.index()), len);
|
||||
+ debug!("Create a buf with {} elements", size);
|
||||
+ let mut buf = vec![0; size];
|
||||
+ // The IOCTL expects the secret index in the first two bytes of the buffer. They will be
|
||||
+ // overwritten in the response
|
||||
+ entry.index_be().write_to_prefix(&mut buf).unwrap();
|
||||
+ Ok(Self {
|
||||
+ entry,
|
||||
+ key: buf.into(),
|
||||
+ })
|
||||
+ }
|
||||
+}
|
||||
+
|
||||
+impl UvCmd for RetrieveCmd {
|
||||
+ const UV_IOCTL_NR: u8 = ffi::UVIO_IOCTL_RETR_SECRET_NR;
|
||||
+
|
||||
+ fn rc_fmt(&self, rc: u16, _: u16) -> Option<&'static str> {
|
||||
+ match rc {
|
||||
+ // should not appear (TM), software creates request from a list item
|
||||
+ 0x0009 => Some("the allocated buffer is to small to store the secret"),
|
||||
+ // should not appear (TM), kernel allocates the memory
|
||||
+ 0x0102 => {
|
||||
+ Some("access exception recognized when accessing retrieved secret storage area")
|
||||
+ }
|
||||
+ // should not appear (TM), software creates request from a list item
|
||||
+ 0x010f => Some("the Secret Store is empty"),
|
||||
+ // should not appear (TM), software creates request from a list item
|
||||
+ 0x0110 => Some("the Secret Store does not contain a secret with the specified index"),
|
||||
+ 0x0111 => Some("the secret is not retrievable"),
|
||||
+ _ => None,
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ fn data(&mut self) -> Option<&mut [u8]> {
|
||||
+ Some(self.key.value_mut())
|
||||
+ }
|
||||
+}
|
||||
diff --git a/rust/pv_core/src/uvdevice/secret_list.rs b/rust/pv_core/src/uvdevice/secret_list.rs
|
||||
index d20928b5..0a8af504 100644
|
||||
--- a/rust/pv_core/src/uvdevice/secret_list.rs
|
||||
+++ b/rust/pv_core/src/uvdevice/secret_list.rs
|
||||
@@ -110,6 +110,11 @@ impl SecretEntry {
|
||||
self.index.get()
|
||||
}
|
||||
|
||||
+ /// Returns the index of this [`SecretEntry`] in BE.
|
||||
+ pub(crate) fn index_be(&self) -> &U16<BigEndian> {
|
||||
+ &self.index
|
||||
+ }
|
||||
+
|
||||
/// Returns the secret type of this [`SecretEntry`].
|
||||
pub fn stype(&self) -> ListableSecretType {
|
||||
self.stype.into()
|
||||
@@ -127,6 +132,16 @@ impl SecretEntry {
|
||||
pub fn id(&self) -> &[u8] {
|
||||
self.id.as_ref()
|
||||
}
|
||||
+
|
||||
+ /// Get the id as [`SecretId`] reference
|
||||
+ pub(crate) fn secret_id(&self) -> &SecretId {
|
||||
+ &self.id
|
||||
+ }
|
||||
+
|
||||
+ /// Returns the secret size of this [`SecretEntry`].
|
||||
+ pub fn secret_size(&self) -> u32 {
|
||||
+ self.len.get()
|
||||
+ }
|
||||
}
|
||||
|
||||
impl Display for SecretEntry {
|
@ -1,710 +0,0 @@
|
||||
From 4af137f4fad8638169ccf0ddcb6dc4b0fe8fb1c1 Mon Sep 17 00:00:00 2001
|
||||
From: Steffen Eiden <seiden@linux.ibm.com>
|
||||
Date: Tue, 5 Mar 2024 12:16:44 +0100
|
||||
Subject: [PATCH] rust/pv_core: Support for listing Retrievable Secrets
|
||||
MIME-Version: 1.0
|
||||
Content-Type: text/plain; charset=UTF-8
|
||||
Content-Transfer-Encoding: 8bit
|
||||
|
||||
Add support for listing retrievable secrets in the List Secrets UVC.
|
||||
|
||||
Acked-by: Marc Hartmayer <marc@linux.ibm.com>
|
||||
Reviewed-by: Christoph Schlameuss <schlameuss@linux.ibm.com>
|
||||
Signed-off-by: Steffen Eiden <seiden@linux.ibm.com>
|
||||
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
|
||||
---
|
||||
rust/pv_core/src/lib.rs | 2 +
|
||||
rust/pv_core/src/uvdevice.rs | 1 +
|
||||
rust/pv_core/src/uvdevice/retr_secret.rs | 399 +++++++++++++++++++++++
|
||||
rust/pv_core/src/uvdevice/secret_list.rs | 157 +++++++--
|
||||
4 files changed, 536 insertions(+), 23 deletions(-)
|
||||
create mode 100644 rust/pv_core/src/uvdevice/retr_secret.rs
|
||||
|
||||
diff --git a/rust/pv_core/src/lib.rs b/rust/pv_core/src/lib.rs
|
||||
index 5922211f..caebfcea 100644
|
||||
--- a/rust/pv_core/src/lib.rs
|
||||
+++ b/rust/pv_core/src/lib.rs
|
||||
@@ -32,6 +32,8 @@ pub mod misc {
|
||||
/// [`crate::uv::UvCmd`]
|
||||
pub mod uv {
|
||||
pub use crate::uvdevice::attest::AttestationCmd;
|
||||
+ pub use crate::uvdevice::retr_secret::RetrievableSecret;
|
||||
+ pub use crate::uvdevice::retr_secret::{AesSizes, AesXtsSizes, EcCurves, HmacShaSizes};
|
||||
pub use crate::uvdevice::secret::{AddCmd, ListCmd, LockCmd, RetrieveCmd};
|
||||
pub use crate::uvdevice::secret_list::{ListableSecretType, SecretEntry, SecretId, SecretList};
|
||||
pub use crate::uvdevice::{ConfigUid, UvCmd, UvDevice, UvDeviceInfo, UvFlags, UvcSuccess};
|
||||
diff --git a/rust/pv_core/src/uvdevice.rs b/rust/pv_core/src/uvdevice.rs
|
||||
index d4176815..e9848243 100644
|
||||
--- a/rust/pv_core/src/uvdevice.rs
|
||||
+++ b/rust/pv_core/src/uvdevice.rs
|
||||
@@ -25,6 +25,7 @@ mod info;
|
||||
mod test;
|
||||
pub(crate) use ffi::uv_ioctl;
|
||||
pub mod attest;
|
||||
+pub mod retr_secret;
|
||||
pub mod secret;
|
||||
pub mod secret_list;
|
||||
|
||||
diff --git a/rust/pv_core/src/uvdevice/retr_secret.rs b/rust/pv_core/src/uvdevice/retr_secret.rs
|
||||
new file mode 100644
|
||||
index 00000000..490152b4
|
||||
--- /dev/null
|
||||
+++ b/rust/pv_core/src/uvdevice/retr_secret.rs
|
||||
@@ -0,0 +1,399 @@
|
||||
+// SPDX-License-Identifier: MIT
|
||||
+//
|
||||
+// Copyright IBM Corp. 2024
|
||||
+
|
||||
+use crate::uv::{ListableSecretType, RetrieveCmd};
|
||||
+use serde::{Deserialize, Serialize, Serializer};
|
||||
+use std::fmt::Display;
|
||||
+
|
||||
+/// Allowed sizes for AES keys
|
||||
+#[non_exhaustive]
|
||||
+#[derive(PartialEq, Eq, Debug)]
|
||||
+pub enum AesSizes {
|
||||
+ /// 128 bit key
|
||||
+ Bits128,
|
||||
+ /// 192 bit key
|
||||
+ Bits192,
|
||||
+ /// 256 bit key
|
||||
+ Bits256,
|
||||
+}
|
||||
+
|
||||
+impl AesSizes {
|
||||
+ /// Construct the key-size from the bit-size.
|
||||
+ ///
|
||||
+ /// Returns [`None`] if the bit-size is not supported.
|
||||
+ pub fn from_bits(bits: u32) -> Option<Self> {
|
||||
+ match bits {
|
||||
+ 128 => Some(Self::Bits128),
|
||||
+ 192 => Some(Self::Bits192),
|
||||
+ 256 => Some(Self::Bits256),
|
||||
+ _ => None,
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ /// Returns the bit-size for the key-type
|
||||
+ const fn bit_size(&self) -> u32 {
|
||||
+ match self {
|
||||
+ Self::Bits128 => 128,
|
||||
+ Self::Bits192 => 192,
|
||||
+ Self::Bits256 => 256,
|
||||
+ }
|
||||
+ }
|
||||
+}
|
||||
+
|
||||
+impl Display for AesSizes {
|
||||
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
+ write!(f, "{}", self.bit_size())
|
||||
+ }
|
||||
+}
|
||||
+
|
||||
+/// Allowed sizes for AES-XTS keys
|
||||
+#[non_exhaustive]
|
||||
+#[derive(PartialEq, Eq, Debug)]
|
||||
+pub enum AesXtsSizes {
|
||||
+ /// Two AES 128 bit keys
|
||||
+ Bits128,
|
||||
+ /// Two AES 256 bit keys
|
||||
+ Bits256,
|
||||
+}
|
||||
+
|
||||
+impl AesXtsSizes {
|
||||
+ /// Construct the key-size from the bit-size.
|
||||
+ ///
|
||||
+ /// It's a key containing two keys; bit-size is half the number of bits it has
|
||||
+ /// Returns [`None`] if the bit-size is not supported.
|
||||
+ pub fn from_bits(bits: u32) -> Option<Self> {
|
||||
+ match bits {
|
||||
+ 128 => Some(Self::Bits128),
|
||||
+ 256 => Some(Self::Bits256),
|
||||
+ _ => None,
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ /// Returns the bit-size for the key-type
|
||||
+ ///
|
||||
+ /// It's a key containing two keys: bit-size is half the number of bits it has
|
||||
+ const fn bit_size(&self) -> u32 {
|
||||
+ match self {
|
||||
+ Self::Bits128 => 128,
|
||||
+ Self::Bits256 => 256,
|
||||
+ }
|
||||
+ }
|
||||
+}
|
||||
+
|
||||
+impl Display for AesXtsSizes {
|
||||
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
+ write!(f, "{}", self.bit_size())
|
||||
+ }
|
||||
+}
|
||||
+
|
||||
+/// Allowed sizes for HMAC-SHA keys
|
||||
+#[non_exhaustive]
|
||||
+#[derive(PartialEq, Eq, Debug)]
|
||||
+pub enum HmacShaSizes {
|
||||
+ /// SHA 256 bit
|
||||
+ Sha256,
|
||||
+ /// SHA 512 bit
|
||||
+ Sha512,
|
||||
+}
|
||||
+
|
||||
+impl HmacShaSizes {
|
||||
+ /// Construct the key-size from the sha-size.
|
||||
+ ///
|
||||
+ /// FW expects maximum resistance keys (double the SHA size).
|
||||
+ /// The `sha_size` is half of the number of bits in the key
|
||||
+ /// Returns [`None`] if the `sha_size` is not supported.
|
||||
+ pub fn from_sha_size(sha_size: u32) -> Option<Self> {
|
||||
+ match sha_size {
|
||||
+ 256 => Some(Self::Sha256),
|
||||
+ 512 => Some(Self::Sha512),
|
||||
+ _ => None,
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ /// Returns the sha-size for the key-type
|
||||
+ ///
|
||||
+ /// FW expects maximum resistance keys (double the SHA size).
|
||||
+ /// The `sha_size` is half of the number of bits in the key
|
||||
+ const fn sha_size(&self) -> u32 {
|
||||
+ match self {
|
||||
+ Self::Sha256 => 256,
|
||||
+ Self::Sha512 => 512,
|
||||
+ }
|
||||
+ }
|
||||
+}
|
||||
+
|
||||
+impl Display for HmacShaSizes {
|
||||
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
+ write!(f, "{}", self.sha_size())
|
||||
+ }
|
||||
+}
|
||||
+
|
||||
+/// Allowed curves for EC private keys
|
||||
+#[non_exhaustive]
|
||||
+#[derive(PartialEq, Eq, Debug)]
|
||||
+pub enum EcCurves {
|
||||
+ /// secp256r1 or prime256v1 curve
|
||||
+ Secp256R1,
|
||||
+ /// secp384p1 curve
|
||||
+ Secp384R1,
|
||||
+ /// secp521r1 curve
|
||||
+ Secp521R1,
|
||||
+ /// ed25519 curve
|
||||
+ Ed25519,
|
||||
+ /// ed448 curve
|
||||
+ Ed448,
|
||||
+}
|
||||
+
|
||||
+impl EcCurves {
|
||||
+ const fn exp_size(&self) -> usize {
|
||||
+ match self {
|
||||
+ Self::Secp256R1 => 32,
|
||||
+ Self::Secp384R1 => 48,
|
||||
+ Self::Secp521R1 => 80,
|
||||
+ Self::Ed25519 => 32,
|
||||
+ Self::Ed448 => 64,
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ /// Resizes the raw key to the expected size.
|
||||
+ ///
|
||||
+ /// See [`Vec::resize`]
|
||||
+ pub fn resize_raw_key(&self, mut raw: Vec<u8>) -> Vec<u8> {
|
||||
+ raw.resize(self.exp_size(), 0);
|
||||
+ raw
|
||||
+ }
|
||||
+}
|
||||
+
|
||||
+// The names have to stay constant, otherwise the PEM contains invalid types
|
||||
+impl Display for EcCurves {
|
||||
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
+ match self {
|
||||
+ Self::Secp256R1 => write!(f, "SECP256R1"),
|
||||
+ Self::Secp384R1 => write!(f, "SECP384R1"),
|
||||
+ Self::Secp521R1 => write!(f, "SECP521R1"),
|
||||
+ Self::Ed25519 => write!(f, "ED25519"),
|
||||
+ Self::Ed448 => write!(f, "ED448"),
|
||||
+ }
|
||||
+ }
|
||||
+}
|
||||
+
|
||||
+/// Retrievable Secret types
|
||||
+#[non_exhaustive]
|
||||
+#[derive(PartialEq, Eq, Debug)]
|
||||
+pub enum RetrievableSecret {
|
||||
+ /// Plain-text secret
|
||||
+ PlainText,
|
||||
+ /// Protected AES key
|
||||
+ Aes(AesSizes),
|
||||
+ /// Protected AES-XTS key
|
||||
+ AesXts(AesXtsSizes),
|
||||
+ /// Protected HMAC-SHA key
|
||||
+ HmacSha(HmacShaSizes),
|
||||
+ /// Protected EC-private key
|
||||
+ Ec(EcCurves),
|
||||
+}
|
||||
+
|
||||
+// The names have to stay constant, otherwise the PEM contains invalid/unknown types
|
||||
+impl Display for RetrievableSecret {
|
||||
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
+ // Alternate representation: Omit sizes/curves
|
||||
+ if f.alternate() {
|
||||
+ match self {
|
||||
+ Self::PlainText => write!(f, "PLAINTEXT"),
|
||||
+ Self::Aes(_) => write!(f, "AES-KEY"),
|
||||
+ Self::AesXts(_) => write!(f, "AES-XTS-KEY"),
|
||||
+ Self::HmacSha(_) => write!(f, "HMAC-SHA-KEY"),
|
||||
+ Self::Ec(_) => write!(f, "EC-PRIVATE-KEY"),
|
||||
+ }
|
||||
+ } else {
|
||||
+ match self {
|
||||
+ Self::PlainText => write!(f, "PLAINTEXT"),
|
||||
+ Self::Aes(s) => write!(f, "AES-{s}-KEY"),
|
||||
+ Self::AesXts(s) => write!(f, "AES-XTS-{s}-KEY"),
|
||||
+ Self::HmacSha(s) => write!(f, "HMAC-SHA-{s}-KEY"),
|
||||
+ Self::Ec(c) => write!(f, "EC-{c}-PRIVATE-KEY"),
|
||||
+ }
|
||||
+ }
|
||||
+ }
|
||||
+}
|
||||
+
|
||||
+impl RetrievableSecret {
|
||||
+ /// Report expected input types
|
||||
+ pub fn expected(&self) -> String {
|
||||
+ match self {
|
||||
+ Self::PlainText => format!("less than {}", RetrieveCmd::MAX_SIZE),
|
||||
+ Self::Aes(_) => "128, 192, or 256".to_string(),
|
||||
+ Self::AesXts(_) => "128 or 256".to_string(),
|
||||
+ Self::HmacSha(_) => "256 or 512".to_string(),
|
||||
+ Self::Ec(_) => "secp256r1, secp384r1, secp521r1, ed25519, or ed448".to_string(),
|
||||
+ }
|
||||
+ }
|
||||
+}
|
||||
+
|
||||
+impl From<&RetrievableSecret> for u16 {
|
||||
+ fn from(value: &RetrievableSecret) -> Self {
|
||||
+ match value {
|
||||
+ RetrievableSecret::PlainText => ListableSecretType::PLAINTEXT,
|
||||
+ RetrievableSecret::Aes(AesSizes::Bits128) => ListableSecretType::AES_128_KEY,
|
||||
+ RetrievableSecret::Aes(AesSizes::Bits192) => ListableSecretType::AES_192_KEY,
|
||||
+ RetrievableSecret::Aes(AesSizes::Bits256) => ListableSecretType::AES_256_KEY,
|
||||
+ RetrievableSecret::AesXts(AesXtsSizes::Bits128) => ListableSecretType::AES_128_XTS_KEY,
|
||||
+ RetrievableSecret::AesXts(AesXtsSizes::Bits256) => ListableSecretType::AES_256_XTS_KEY,
|
||||
+ RetrievableSecret::HmacSha(HmacShaSizes::Sha256) => {
|
||||
+ ListableSecretType::HMAC_SHA_256_KEY
|
||||
+ }
|
||||
+ RetrievableSecret::HmacSha(HmacShaSizes::Sha512) => {
|
||||
+ ListableSecretType::HMAC_SHA_512_KEY
|
||||
+ }
|
||||
+ RetrievableSecret::Ec(EcCurves::Secp256R1) => ListableSecretType::ECDSA_P256_KEY,
|
||||
+ RetrievableSecret::Ec(EcCurves::Secp384R1) => ListableSecretType::ECDSA_P384_KEY,
|
||||
+ RetrievableSecret::Ec(EcCurves::Secp521R1) => ListableSecretType::ECDSA_P521_KEY,
|
||||
+ RetrievableSecret::Ec(EcCurves::Ed25519) => ListableSecretType::ECDSA_ED25519_KEY,
|
||||
+ RetrievableSecret::Ec(EcCurves::Ed448) => ListableSecretType::ECDSA_ED448_KEY,
|
||||
+ }
|
||||
+ }
|
||||
+}
|
||||
+
|
||||
+// serializes to: <secret type nb> (String name)
|
||||
+impl Serialize for RetrievableSecret {
|
||||
+ fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
||||
+ where
|
||||
+ S: Serializer,
|
||||
+ {
|
||||
+ let id: u16 = self.into();
|
||||
+ serializer.serialize_str(&format!("{id} ({self})"))
|
||||
+ }
|
||||
+}
|
||||
+
|
||||
+/// deserializes from the secret type nb only
|
||||
+impl<'de> Deserialize<'de> for RetrievableSecret {
|
||||
+ fn deserialize<D>(de: D) -> Result<Self, D::Error>
|
||||
+ where
|
||||
+ D: serde::Deserializer<'de>,
|
||||
+ {
|
||||
+ struct RetrSecretVisitor;
|
||||
+ impl<'de> serde::de::Visitor<'de> for RetrSecretVisitor {
|
||||
+ type Value = RetrievableSecret;
|
||||
+
|
||||
+ fn expecting(&self, fmt: &mut std::fmt::Formatter) -> std::fmt::Result {
|
||||
+ fmt.write_str(
|
||||
+ "a retrievable secret type: `<number> (String name)` number in [3,10]|[17,21]",
|
||||
+ )
|
||||
+ }
|
||||
+ fn visit_str<E>(self, s: &str) -> Result<Self::Value, E>
|
||||
+ where
|
||||
+ E: serde::de::Error,
|
||||
+ {
|
||||
+ let (n, _) = s.split_once(' ').ok_or(serde::de::Error::invalid_value(
|
||||
+ serde::de::Unexpected::Str(s),
|
||||
+ &self,
|
||||
+ ))?;
|
||||
+ let id: u16 = n.parse().map_err(|_| {
|
||||
+ serde::de::Error::invalid_value(serde::de::Unexpected::Str(n), &self)
|
||||
+ })?;
|
||||
+ let listable: ListableSecretType = id.into();
|
||||
+ match listable {
|
||||
+ ListableSecretType::Retrievable(r) => Ok(r),
|
||||
+ _ => Err(serde::de::Error::invalid_value(
|
||||
+ serde::de::Unexpected::Unsigned(id.into()),
|
||||
+ &self,
|
||||
+ )),
|
||||
+ }
|
||||
+ }
|
||||
+ }
|
||||
+ de.deserialize_str(RetrSecretVisitor)
|
||||
+ }
|
||||
+}
|
||||
+
|
||||
+#[cfg(test)]
|
||||
+mod test {
|
||||
+ use serde_test::{assert_tokens, Token};
|
||||
+
|
||||
+ use super::*;
|
||||
+
|
||||
+ #[test]
|
||||
+ fn retr_serde_plain() {
|
||||
+ let retr = RetrievableSecret::PlainText;
|
||||
+ assert_tokens(&retr, &[Token::Str("3 (PLAINTEXT)")]);
|
||||
+ }
|
||||
+
|
||||
+ #[test]
|
||||
+ fn retr_serde_aes() {
|
||||
+ let retr = RetrievableSecret::Aes(AesSizes::Bits192);
|
||||
+ assert_tokens(&retr, &[Token::Str("5 (AES-192-KEY)")]);
|
||||
+ }
|
||||
+
|
||||
+ #[test]
|
||||
+ fn retr_serde_aes_xts() {
|
||||
+ let retr = RetrievableSecret::AesXts(AesXtsSizes::Bits128);
|
||||
+ assert_tokens(&retr, &[Token::Str("7 (AES-XTS-128-KEY)")]);
|
||||
+ }
|
||||
+
|
||||
+ #[test]
|
||||
+ fn retr_serde_hmac() {
|
||||
+ let retr = RetrievableSecret::HmacSha(HmacShaSizes::Sha256);
|
||||
+ assert_tokens(&retr, &[Token::Str("9 (HMAC-SHA-256-KEY)")]);
|
||||
+ }
|
||||
+
|
||||
+ #[test]
|
||||
+ fn retr_serde_es() {
|
||||
+ let retr = RetrievableSecret::Ec(EcCurves::Secp521R1);
|
||||
+ assert_tokens(&retr, &[Token::Str("19 (EC-SECP521R1-PRIVATE-KEY)")]);
|
||||
+ }
|
||||
+
|
||||
+ // Ensure that the string representation of the retrievable types stay constant, or PEM will have
|
||||
+ // different, incompatible types
|
||||
+ #[test]
|
||||
+ fn stable_type_names() {
|
||||
+ assert_eq!("PLAINTEXT", RetrievableSecret::PlainText.to_string());
|
||||
+ assert_eq!(
|
||||
+ "AES-128-KEY",
|
||||
+ RetrievableSecret::Aes(AesSizes::Bits128).to_string()
|
||||
+ );
|
||||
+ assert_eq!(
|
||||
+ "AES-192-KEY",
|
||||
+ RetrievableSecret::Aes(AesSizes::Bits192).to_string()
|
||||
+ );
|
||||
+ assert_eq!(
|
||||
+ "AES-256-KEY",
|
||||
+ RetrievableSecret::Aes(AesSizes::Bits256).to_string()
|
||||
+ );
|
||||
+ assert_eq!(
|
||||
+ "AES-XTS-128-KEY",
|
||||
+ RetrievableSecret::AesXts(AesXtsSizes::Bits128).to_string()
|
||||
+ );
|
||||
+ assert_eq!(
|
||||
+ "AES-XTS-256-KEY",
|
||||
+ RetrievableSecret::AesXts(AesXtsSizes::Bits256).to_string()
|
||||
+ );
|
||||
+ assert_eq!(
|
||||
+ "HMAC-SHA-256-KEY",
|
||||
+ RetrievableSecret::HmacSha(HmacShaSizes::Sha256).to_string()
|
||||
+ );
|
||||
+ assert_eq!(
|
||||
+ "HMAC-SHA-512-KEY",
|
||||
+ RetrievableSecret::HmacSha(HmacShaSizes::Sha512).to_string()
|
||||
+ );
|
||||
+ assert_eq!(
|
||||
+ "EC-SECP256R1-PRIVATE-KEY",
|
||||
+ RetrievableSecret::Ec(EcCurves::Secp256R1).to_string()
|
||||
+ );
|
||||
+ assert_eq!(
|
||||
+ "EC-SECP384R1-PRIVATE-KEY",
|
||||
+ RetrievableSecret::Ec(EcCurves::Secp384R1).to_string()
|
||||
+ );
|
||||
+ assert_eq!(
|
||||
+ "EC-SECP521R1-PRIVATE-KEY",
|
||||
+ RetrievableSecret::Ec(EcCurves::Secp521R1).to_string()
|
||||
+ );
|
||||
+ assert_eq!(
|
||||
+ "EC-ED25519-PRIVATE-KEY",
|
||||
+ RetrievableSecret::Ec(EcCurves::Ed25519).to_string()
|
||||
+ );
|
||||
+ assert_eq!(
|
||||
+ "EC-ED448-PRIVATE-KEY",
|
||||
+ RetrievableSecret::Ec(EcCurves::Ed448).to_string()
|
||||
+ );
|
||||
+ }
|
||||
+}
|
||||
diff --git a/rust/pv_core/src/uvdevice/secret_list.rs b/rust/pv_core/src/uvdevice/secret_list.rs
|
||||
index 0a8af504..4e955010 100644
|
||||
--- a/rust/pv_core/src/uvdevice/secret_list.rs
|
||||
+++ b/rust/pv_core/src/uvdevice/secret_list.rs
|
||||
@@ -2,9 +2,14 @@
|
||||
//
|
||||
// Copyright IBM Corp. 2024
|
||||
|
||||
-use crate::assert_size;
|
||||
-use crate::{misc::to_u16, uv::ListCmd, uvdevice::UvCmd, Error, Result};
|
||||
-use byteorder::{BigEndian, ReadBytesExt, WriteBytesExt};
|
||||
+use crate::{
|
||||
+ assert_size,
|
||||
+ misc::to_u16,
|
||||
+ uv::{AesSizes, AesXtsSizes, EcCurves, HmacShaSizes, ListCmd, RetrievableSecret},
|
||||
+ uvdevice::UvCmd,
|
||||
+ Error, Result,
|
||||
+};
|
||||
+use byteorder::{BigEndian, ByteOrder, ReadBytesExt, WriteBytesExt};
|
||||
use serde::{Deserialize, Serialize, Serializer};
|
||||
use std::{
|
||||
fmt::Display,
|
||||
@@ -18,7 +23,7 @@ use zerocopy::{AsBytes, FromBytes, FromZeroes, U16, U32};
|
||||
///
|
||||
/// (de)serializes itself in/from a hex-string
|
||||
#[repr(C)]
|
||||
-#[derive(PartialEq, Eq, AsBytes, FromZeroes, FromBytes, Debug, Clone)]
|
||||
+#[derive(PartialEq, Eq, AsBytes, FromZeroes, FromBytes, Debug, Clone, Default)]
|
||||
pub struct SecretId([u8; Self::ID_SIZE]);
|
||||
assert_size!(SecretId, SecretId::ID_SIZE);
|
||||
|
||||
@@ -94,11 +99,11 @@ impl SecretEntry {
|
||||
/// Create a new entry for a [`SecretList`].
|
||||
///
|
||||
/// The content of this entry will very likely not represent the status of the guest in the
|
||||
- /// Ultravisor. Use of [`SecretList::decode`] in any non-test environments is encuraged.
|
||||
+ /// Ultravisor. Use of [`SecretList::decode`] in any non-test environments is encouraged.
|
||||
pub fn new(index: u16, stype: ListableSecretType, id: SecretId, secret_len: u32) -> Self {
|
||||
Self {
|
||||
index: index.into(),
|
||||
- stype: stype.into(),
|
||||
+ stype: U16::new(stype.into()),
|
||||
len: secret_len.into(),
|
||||
res_8: 0,
|
||||
id,
|
||||
@@ -117,7 +122,7 @@ impl SecretEntry {
|
||||
|
||||
/// Returns the secret type of this [`SecretEntry`].
|
||||
pub fn stype(&self) -> ListableSecretType {
|
||||
- self.stype.into()
|
||||
+ self.stype.get().into()
|
||||
}
|
||||
|
||||
/// Returns a reference to the id of this [`SecretEntry`].
|
||||
@@ -146,7 +151,7 @@ impl SecretEntry {
|
||||
|
||||
impl Display for SecretEntry {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
- let stype: ListableSecretType = self.stype.into();
|
||||
+ let stype: ListableSecretType = self.stype.get().into();
|
||||
writeln!(f, "{} {}:", self.index, stype)?;
|
||||
write!(f, " ")?;
|
||||
for b in self.id.as_ref() {
|
||||
@@ -298,51 +303,115 @@ fn ser_u16<S: Serializer>(v: &U16<BigEndian>, ser: S) -> Result<S::Ok, S::Error>
|
||||
pub enum ListableSecretType {
|
||||
/// Association Secret
|
||||
Association,
|
||||
+ /// Retrievable key
|
||||
+ Retrievable(RetrievableSecret),
|
||||
+
|
||||
/// Invalid secret type, that should never appear in a list
|
||||
///
|
||||
/// 0 is reserved
|
||||
- /// 1 is Null secret, with no id and not listable
|
||||
+ /// 1 is Null secret, with no id and not list-able
|
||||
Invalid(u16),
|
||||
/// Unknown secret type
|
||||
Unknown(u16),
|
||||
}
|
||||
|
||||
impl ListableSecretType {
|
||||
- /// UV type id for an association secret
|
||||
- pub const ASSOCIATION: u16 = 0x0002;
|
||||
- /// UV type id for a null secret
|
||||
- pub const NULL: u16 = 0x0001;
|
||||
const RESERVED_0: u16 = 0x0000;
|
||||
+ /// UV secret-type id for a null secret
|
||||
+ pub const NULL: u16 = 0x0001;
|
||||
+ /// UV secret-type id for an association secret
|
||||
+ pub const ASSOCIATION: u16 = 0x0002;
|
||||
+ /// UV secret-type id for a plain text secret
|
||||
+ pub const PLAINTEXT: u16 = 0x0003;
|
||||
+ /// UV secret-type id for an aes-128-key secret
|
||||
+ pub const AES_128_KEY: u16 = 0x0004;
|
||||
+ /// UV secret-type id for an aes-192-key secret
|
||||
+ pub const AES_192_KEY: u16 = 0x0005;
|
||||
+ /// UV secret-type id for an aes-256-key secret
|
||||
+ pub const AES_256_KEY: u16 = 0x0006;
|
||||
+ /// UV secret-type id for an aes-xts-128-key secret
|
||||
+ pub const AES_128_XTS_KEY: u16 = 0x0007;
|
||||
+ /// UV secret-type id for an aes-xts-256-key secret
|
||||
+ pub const AES_256_XTS_KEY: u16 = 0x0008;
|
||||
+ /// UV secret-type id for an hmac-sha-256-key secret
|
||||
+ pub const HMAC_SHA_256_KEY: u16 = 0x0009;
|
||||
+ /// UV secret-type id for an hmac-sha-512-key secret
|
||||
+ pub const HMAC_SHA_512_KEY: u16 = 0x000a;
|
||||
+ // 0x000b - 0x0010 reserved
|
||||
+ /// UV secret-type id for an ecdsa-p256-private-key secret
|
||||
+ pub const ECDSA_P256_KEY: u16 = 0x0011;
|
||||
+ /// UV secret-type id for an ecdsa-p384-private-key secret
|
||||
+ pub const ECDSA_P384_KEY: u16 = 0x0012;
|
||||
+ /// UV secret-type id for an ecdsa-p521-private-key secret
|
||||
+ pub const ECDSA_P521_KEY: u16 = 0x0013;
|
||||
+ /// UV secret-type id for an ed25519-private-key secret
|
||||
+ pub const ECDSA_ED25519_KEY: u16 = 0x0014;
|
||||
+ /// UV secret-type id for an ed448-private-key secret
|
||||
+ pub const ECDSA_ED448_KEY: u16 = 0x0015;
|
||||
}
|
||||
|
||||
impl Display for ListableSecretType {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
match self {
|
||||
Self::Association => write!(f, "Association"),
|
||||
- Self::Invalid(n) => write!(f, "Invalid({n})"),
|
||||
- Self::Unknown(n) => write!(f, "Unknown({n})"),
|
||||
+ Self::Invalid(n) => write!(f, "Invalid(0x{n:04x})"),
|
||||
+ Self::Unknown(n) => write!(f, "Unknown(0x{n:04x})"),
|
||||
+ Self::Retrievable(r) => write!(f, "{r}"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
-impl From<U16<BigEndian>> for ListableSecretType {
|
||||
- fn from(value: U16<BigEndian>) -> Self {
|
||||
- match value.get() {
|
||||
+impl<O: ByteOrder> From<U16<O>> for ListableSecretType {
|
||||
+ fn from(value: U16<O>) -> Self {
|
||||
+ value.get().into()
|
||||
+ }
|
||||
+}
|
||||
+
|
||||
+impl From<u16> for ListableSecretType {
|
||||
+ fn from(value: u16) -> Self {
|
||||
+ match value {
|
||||
Self::RESERVED_0 => Self::Invalid(Self::RESERVED_0),
|
||||
Self::NULL => Self::Invalid(Self::NULL),
|
||||
Self::ASSOCIATION => Self::Association,
|
||||
+ Self::PLAINTEXT => Self::Retrievable(RetrievableSecret::PlainText),
|
||||
+ Self::AES_128_KEY => Self::Retrievable(RetrievableSecret::Aes(AesSizes::Bits128)),
|
||||
+ Self::AES_192_KEY => Self::Retrievable(RetrievableSecret::Aes(AesSizes::Bits192)),
|
||||
+ Self::AES_256_KEY => Self::Retrievable(RetrievableSecret::Aes(AesSizes::Bits256)),
|
||||
+ Self::AES_128_XTS_KEY => {
|
||||
+ Self::Retrievable(RetrievableSecret::AesXts(AesXtsSizes::Bits128))
|
||||
+ }
|
||||
+ Self::AES_256_XTS_KEY => {
|
||||
+ Self::Retrievable(RetrievableSecret::AesXts(AesXtsSizes::Bits256))
|
||||
+ }
|
||||
+ Self::HMAC_SHA_256_KEY => {
|
||||
+ Self::Retrievable(RetrievableSecret::HmacSha(HmacShaSizes::Sha256))
|
||||
+ }
|
||||
+ Self::HMAC_SHA_512_KEY => {
|
||||
+ Self::Retrievable(RetrievableSecret::HmacSha(HmacShaSizes::Sha512))
|
||||
+ }
|
||||
+ Self::ECDSA_P256_KEY => Self::Retrievable(RetrievableSecret::Ec(EcCurves::Secp256R1)),
|
||||
+ Self::ECDSA_P384_KEY => Self::Retrievable(RetrievableSecret::Ec(EcCurves::Secp384R1)),
|
||||
+ Self::ECDSA_P521_KEY => Self::Retrievable(RetrievableSecret::Ec(EcCurves::Secp521R1)),
|
||||
+ Self::ECDSA_ED25519_KEY => Self::Retrievable(RetrievableSecret::Ec(EcCurves::Ed25519)),
|
||||
+ Self::ECDSA_ED448_KEY => Self::Retrievable(RetrievableSecret::Ec(EcCurves::Ed448)),
|
||||
n => Self::Unknown(n),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
-impl From<ListableSecretType> for U16<BigEndian> {
|
||||
+impl<O: ByteOrder> From<ListableSecretType> for U16<O> {
|
||||
+ fn from(value: ListableSecretType) -> Self {
|
||||
+ Self::new(value.into())
|
||||
+ }
|
||||
+}
|
||||
+
|
||||
+impl From<ListableSecretType> for u16 {
|
||||
fn from(value: ListableSecretType) -> Self {
|
||||
match value {
|
||||
ListableSecretType::Association => ListableSecretType::ASSOCIATION,
|
||||
ListableSecretType::Invalid(n) | ListableSecretType::Unknown(n) => n,
|
||||
+ ListableSecretType::Retrievable(r) => (&r).into(),
|
||||
}
|
||||
- .into()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -363,8 +432,8 @@ where
|
||||
where
|
||||
E: serde::de::Error,
|
||||
{
|
||||
- if s.len() != SecretId::ID_SIZE * 2 + 2 {
|
||||
- return Err(serde::de::Error::invalid_length(s.len(), &self));
|
||||
+ if s.len() != SecretId::ID_SIZE * 2 + "0x".len() {
|
||||
+ return Err(serde::de::Error::invalid_length(s.len() - 2, &self));
|
||||
}
|
||||
let nb = s.strip_prefix("0x").ok_or_else(|| {
|
||||
serde::de::Error::invalid_value(serde::de::Unexpected::Str(s), &self)
|
||||
@@ -385,7 +454,6 @@ mod test {
|
||||
|
||||
use super::*;
|
||||
use std::io::{BufReader, BufWriter, Cursor};
|
||||
-
|
||||
#[test]
|
||||
fn dump_secret_entry() {
|
||||
const EXP: &[u8] = &[
|
||||
@@ -516,4 +584,47 @@ mod test {
|
||||
)],
|
||||
)
|
||||
}
|
||||
+
|
||||
+ #[test]
|
||||
+ fn secret_list_ser() {
|
||||
+ let list = SecretList {
|
||||
+ total_num_secrets: 0x112,
|
||||
+ secrets: vec![SecretEntry {
|
||||
+ index: 1.into(),
|
||||
+ stype: 2.into(),
|
||||
+ len: 32.into(),
|
||||
+ res_8: 0,
|
||||
+ id: SecretId::from([0; 32]),
|
||||
+ }],
|
||||
+ };
|
||||
+
|
||||
+ assert_ser_tokens(
|
||||
+ &list,
|
||||
+ &[
|
||||
+ Token::Struct {
|
||||
+ name: "SecretList",
|
||||
+ len: 2,
|
||||
+ },
|
||||
+ Token::String("total_num_secrets"),
|
||||
+ Token::U64(0x112),
|
||||
+ Token::String("secrets"),
|
||||
+ Token::Seq { len: Some(1) },
|
||||
+ Token::Struct {
|
||||
+ name: "SecretEntry",
|
||||
+ len: (4),
|
||||
+ },
|
||||
+ Token::String("index"),
|
||||
+ Token::U16(1),
|
||||
+ Token::String("stype"),
|
||||
+ Token::U16(2),
|
||||
+ Token::String("len"),
|
||||
+ Token::U32(32),
|
||||
+ Token::String("id"),
|
||||
+ Token::String("0x0000000000000000000000000000000000000000000000000000000000000000"),
|
||||
+ Token::StructEnd,
|
||||
+ Token::SeqEnd,
|
||||
+ Token::StructEnd,
|
||||
+ ],
|
||||
+ )
|
||||
+ }
|
||||
}
|
@ -1,877 +0,0 @@
|
||||
From fd024387d710887bd2016658c44d4762a08c791c Mon Sep 17 00:00:00 2001
|
||||
From: Steffen Eiden <seiden@linux.ibm.com>
|
||||
Date: Tue, 5 Mar 2024 12:19:22 +0100
|
||||
Subject: [PATCH] rust/pv: Retrievable secrets support
|
||||
MIME-Version: 1.0
|
||||
Content-Type: text/plain; charset=UTF-8
|
||||
Content-Transfer-Encoding: 8bit
|
||||
|
||||
Support retrievable secret for Add-Secret requests.
|
||||
|
||||
Acked-by: Marc Hartmayer <marc@linux.ibm.com>
|
||||
Reviewed-by: Christoph Schlameuss <schlameuss@linux.ibm.com>
|
||||
Signed-off-by: Steffen Eiden <seiden@linux.ibm.com>
|
||||
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
|
||||
---
|
||||
rust/pv/src/crypto.rs | 3 +-
|
||||
rust/pv/src/error.rs | 8 +
|
||||
rust/pv/src/lib.rs | 8 +-
|
||||
rust/pv/src/uvsecret.rs | 1 +
|
||||
rust/pv/src/uvsecret/guest_secret.rs | 399 +++++++++++++++++++++++++--
|
||||
rust/pv/src/uvsecret/retr_secret.rs | 234 ++++++++++++++++
|
||||
6 files changed, 631 insertions(+), 22 deletions(-)
|
||||
create mode 100644 rust/pv/src/uvsecret/retr_secret.rs
|
||||
|
||||
diff --git a/rust/pv/src/crypto.rs b/rust/pv/src/crypto.rs
|
||||
index 8f11d2b4..ebc85f72 100644
|
||||
--- a/rust/pv/src/crypto.rs
|
||||
+++ b/rust/pv/src/crypto.rs
|
||||
@@ -29,7 +29,6 @@ pub type Aes256XtsKey = Confidential<[u8; SymKeyType::AES_256_XTS_KEY_LEN]>;
|
||||
|
||||
/// SHA-512 digest length (in bytes)
|
||||
pub const SHA_512_HASH_LEN: usize = 64;
|
||||
-
|
||||
#[allow(dead_code)]
|
||||
pub(crate) const SHA_256_HASH_LEN: u32 = 32;
|
||||
#[allow(dead_code)]
|
||||
@@ -60,6 +59,8 @@ impl SymKeyType {
|
||||
pub const AES_256_XTS_KEY_LEN: usize = 64;
|
||||
/// AES256-XTS tweak length (in bytes)
|
||||
pub const AES_256_XTS_TWEAK_LEN: usize = 16;
|
||||
+ /// AES256 GCM Block length
|
||||
+ pub const AES_256_GCM_BLOCK_LEN: usize = 16;
|
||||
|
||||
/// Returns the tag length of the [`SymKeyType`] if it is an AEAD key
|
||||
pub const fn tag_len(&self) -> Option<usize> {
|
||||
diff --git a/rust/pv/src/error.rs b/rust/pv/src/error.rs
|
||||
index 3ba808f2..601b40f0 100644
|
||||
--- a/rust/pv/src/error.rs
|
||||
+++ b/rust/pv/src/error.rs
|
||||
@@ -109,6 +109,14 @@ pub enum Error {
|
||||
#[error("An ASCII string was expected, but non-ASCII characters were received.")]
|
||||
NonAscii,
|
||||
|
||||
+ #[error("Incorrect {what} for a {kind}. Is: {value}; expected: {exp}")]
|
||||
+ RetrInvKey {
|
||||
+ what: &'static str,
|
||||
+ kind: String,
|
||||
+ value: String,
|
||||
+ exp: String,
|
||||
+ },
|
||||
+
|
||||
// errors from other crates
|
||||
#[error(transparent)]
|
||||
PvCore(#[from] pv_core::Error),
|
||||
diff --git a/rust/pv/src/lib.rs b/rust/pv/src/lib.rs
|
||||
index ec31b9a4..43375669 100644
|
||||
--- a/rust/pv/src/lib.rs
|
||||
+++ b/rust/pv/src/lib.rs
|
||||
@@ -104,7 +104,12 @@ pub mod request {
|
||||
|
||||
/// Reexports some useful OpenSSL symbols
|
||||
pub mod openssl {
|
||||
- pub use openssl::{error::ErrorStack, hash::DigestBytes, pkey, x509};
|
||||
+ pub use openssl::{error::ErrorStack, hash::DigestBytes, nid::Nid, pkey, x509};
|
||||
+ // rust-OpenSSL does not define these NIDs
|
||||
+ #[allow(missing_docs)]
|
||||
+ pub const NID_ED25519: Nid = Nid::from_raw(openssl_sys::NID_ED25519);
|
||||
+ #[allow(missing_docs)]
|
||||
+ pub const NID_ED448: Nid = Nid::from_raw(openssl_sys::NID_ED448);
|
||||
}
|
||||
|
||||
pub use pv_core::request::*;
|
||||
@@ -118,6 +123,7 @@ pub mod secret {
|
||||
asrcb::{AddSecretFlags, AddSecretRequest, AddSecretVersion},
|
||||
ext_secret::ExtSecret,
|
||||
guest_secret::GuestSecret,
|
||||
+ retr_secret::{IbmProtectedKey, RetrievedSecret},
|
||||
user_data::verify_asrcb_and_get_user_data,
|
||||
};
|
||||
}
|
||||
diff --git a/rust/pv/src/uvsecret.rs b/rust/pv/src/uvsecret.rs
|
||||
index 343e4b05..c3b43bba 100644
|
||||
--- a/rust/pv/src/uvsecret.rs
|
||||
+++ b/rust/pv/src/uvsecret.rs
|
||||
@@ -10,4 +10,5 @@
|
||||
pub mod asrcb;
|
||||
pub mod ext_secret;
|
||||
pub mod guest_secret;
|
||||
+pub mod retr_secret;
|
||||
pub mod user_data;
|
||||
diff --git a/rust/pv/src/uvsecret/guest_secret.rs b/rust/pv/src/uvsecret/guest_secret.rs
|
||||
index 4f1db31c..3bad6d3c 100644
|
||||
--- a/rust/pv/src/uvsecret/guest_secret.rs
|
||||
+++ b/rust/pv/src/uvsecret/guest_secret.rs
|
||||
@@ -4,20 +4,34 @@
|
||||
|
||||
#[allow(unused_imports)] // used for more convenient docstring
|
||||
use super::asrcb::AddSecretRequest;
|
||||
-use crate::assert_size;
|
||||
use crate::{
|
||||
- crypto::{hash, random_array},
|
||||
- request::Confidential,
|
||||
- Result,
|
||||
+ assert_size,
|
||||
+ crypto::{hash, random_array, SymKeyType},
|
||||
+ request::{
|
||||
+ openssl::{NID_ED25519, NID_ED448},
|
||||
+ Confidential,
|
||||
+ },
|
||||
+ uv::{
|
||||
+ AesSizes, AesXtsSizes, EcCurves, HmacShaSizes, ListableSecretType, RetrievableSecret,
|
||||
+ RetrieveCmd, SecretId,
|
||||
+ },
|
||||
+ Error, Result,
|
||||
};
|
||||
use byteorder::BigEndian;
|
||||
-use openssl::hash::MessageDigest;
|
||||
-use pv_core::uv::{ListableSecretType, SecretId};
|
||||
+use openssl::{
|
||||
+ hash::MessageDigest,
|
||||
+ nid::Nid,
|
||||
+ pkey::{Id, PKey, Private},
|
||||
+};
|
||||
+use pv_core::static_assert;
|
||||
use serde::{Deserialize, Serialize};
|
||||
-use std::{convert::TryInto, fmt::Display};
|
||||
+use std::fmt::Display;
|
||||
use zerocopy::{AsBytes, U16, U32};
|
||||
|
||||
const ASSOC_SECRET_SIZE: usize = 32;
|
||||
+/// Maximum size of a plain-text secret payload (8190)
|
||||
+pub(crate) const MAX_SIZE_PLAIN_PAYLOAD: usize = RetrieveCmd::MAX_SIZE - 2;
|
||||
+static_assert!(MAX_SIZE_PLAIN_PAYLOAD == 8190);
|
||||
|
||||
/// A Secret to be added in [`AddSecretRequest`]
|
||||
#[derive(Debug, Serialize, Deserialize, PartialEq, Eq)]
|
||||
@@ -36,13 +50,60 @@ pub enum GuestSecret {
|
||||
#[serde(skip)]
|
||||
secret: Confidential<[u8; ASSOC_SECRET_SIZE]>,
|
||||
},
|
||||
+ /// Retrievable key
|
||||
+ ///
|
||||
+ /// Create Retrievables using [`GuestSecret::retrievable`]
|
||||
+ /// Secret size is always valid for the type/kind
|
||||
+ Retrievable {
|
||||
+ /// Retrievable secret type
|
||||
+ kind: RetrievableSecret,
|
||||
+ /// Name of the secret
|
||||
+ name: String,
|
||||
+ /// SHA256 hash of [`GuestSecret::RetrievableKey::name`]
|
||||
+ id: SecretId,
|
||||
+ /// Confidential actual retrievable secret (32 bytes)
|
||||
+ #[serde(skip)]
|
||||
+ secret: Confidential<Vec<u8>>,
|
||||
+ },
|
||||
+}
|
||||
+
|
||||
+macro_rules! retr_constructor {
|
||||
+ ($(#[$err:meta])* | $(#[$kind:meta])* => $type: ty, $func: ident) => {
|
||||
+ /// Create a new
|
||||
+ $(#[$kind])*
|
||||
+ /// [`GuestSecret::Retrievable`] secret.
|
||||
+ ///
|
||||
+ /// * `name` - Name of the secret. Will be hashed into a 32 byte id
|
||||
+ /// * `secret` - the secret value
|
||||
+ ///
|
||||
+ /// # Errors
|
||||
+ ///
|
||||
+ $(#[$err])*
|
||||
+ pub fn $func(name: &str, secret: $type) -> Result<Self> {
|
||||
+ let (kind, secret) = $func(secret)?;
|
||||
+ Ok(Self::Retrievable {
|
||||
+ kind,
|
||||
+ name: name.to_string(),
|
||||
+ id: Self::name_to_id(name)?,
|
||||
+ secret,
|
||||
+ })
|
||||
+ }
|
||||
+ };
|
||||
}
|
||||
|
||||
impl GuestSecret {
|
||||
+ fn name_to_id(name: &str) -> Result<SecretId> {
|
||||
+ let id: [u8; SecretId::ID_SIZE] = hash(MessageDigest::sha256(), name.as_bytes())?
|
||||
+ .to_vec()
|
||||
+ .try_into()
|
||||
+ .unwrap();
|
||||
+ Ok(id.into())
|
||||
+ }
|
||||
+
|
||||
/// Create a new [`GuestSecret::Association`].
|
||||
///
|
||||
/// * `name` - Name of the secret. Will be hashed into a 32 byte id
|
||||
- /// * `secret` - Value of the secret. Ranom if [`Option::None`]
|
||||
+ /// * `secret` - Value of the secret. Random if [`Option::None`]
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
@@ -51,10 +112,6 @@ impl GuestSecret {
|
||||
where
|
||||
O: Into<Option<[u8; ASSOC_SECRET_SIZE]>>,
|
||||
{
|
||||
- let id: [u8; SecretId::ID_SIZE] = hash(MessageDigest::sha256(), name.as_bytes())?
|
||||
- .to_vec()
|
||||
- .try_into()
|
||||
- .unwrap();
|
||||
let secret = match secret.into() {
|
||||
Some(s) => s,
|
||||
None => random_array()?,
|
||||
@@ -62,16 +119,28 @@ impl GuestSecret {
|
||||
|
||||
Ok(Self::Association {
|
||||
name: name.to_string(),
|
||||
- id: id.into(),
|
||||
+ id: Self::name_to_id(name)?,
|
||||
secret: secret.into(),
|
||||
})
|
||||
}
|
||||
|
||||
+ retr_constructor!(#[doc = r"This function will return an error if the secret is larger than 8 pages"]
|
||||
+ | #[doc = r"plaintext"] => Confidential<Vec<u8>>, plaintext);
|
||||
+ retr_constructor!(#[doc = r"This function will return an error if OpenSSL cannot create a hash or the secret size is invalid"]
|
||||
+ | #[doc = r"AES Key"] => Confidential<Vec<u8>>, aes);
|
||||
+ retr_constructor!(#[doc = r"This function will return an error if OpenSSL cannot create a hash or the secret size is invalid"]
|
||||
+ | #[doc = r"AES-XTS Key"] => Confidential<Vec<u8>>, aes_xts);
|
||||
+ retr_constructor!(#[doc = r"This function will return an error if OpenSSL cannot create a hash or the secret size is invalid"]
|
||||
+ | #[doc = r"HMAC-SHA Key"] => Confidential<Vec<u8>>, hmac_sha);
|
||||
+ retr_constructor!(#[doc = r"This function will return an error if OpenSSL cannot create a hash or the curve is invalid"]
|
||||
+ | #[doc = r"EC PRIVATE Key"] => PKey<Private>, ec);
|
||||
+
|
||||
/// Reference to the confidential data
|
||||
pub fn confidential(&self) -> &[u8] {
|
||||
match &self {
|
||||
Self::Null => &[],
|
||||
Self::Association { secret, .. } => secret.value().as_slice(),
|
||||
+ Self::Retrievable { secret, .. } => secret.value(),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -79,7 +148,7 @@ impl GuestSecret {
|
||||
pub(crate) fn auth(&self) -> SecretAuth {
|
||||
match &self {
|
||||
Self::Null => SecretAuth::Null,
|
||||
- // Panic: every non null secret type is listable -> no panic
|
||||
+ // Panic: every non null secret type is list-able -> no panic
|
||||
listable => {
|
||||
SecretAuth::Listable(ListableSecretHdr::from_guest_secret(listable).unwrap())
|
||||
}
|
||||
@@ -92,6 +161,7 @@ impl GuestSecret {
|
||||
// Null is not listable, but the ListableSecretType provides the type constant (1)
|
||||
Self::Null => ListableSecretType::NULL,
|
||||
Self::Association { .. } => ListableSecretType::ASSOCIATION,
|
||||
+ Self::Retrievable { kind, .. } => kind.into(),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -100,6 +170,7 @@ impl GuestSecret {
|
||||
match self {
|
||||
Self::Null => 0,
|
||||
Self::Association { secret, .. } => secret.value().len() as u32,
|
||||
+ Self::Retrievable { secret, .. } => secret.value().len() as u32,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -107,18 +178,157 @@ impl GuestSecret {
|
||||
fn id(&self) -> Option<SecretId> {
|
||||
match self {
|
||||
Self::Null => None,
|
||||
- Self::Association { id, .. } => Some(id.to_owned()),
|
||||
+ Self::Association { id, .. } | Self::Retrievable { id, .. } => Some(id.to_owned()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+type RetrKeyInfo = (RetrievableSecret, Confidential<Vec<u8>>);
|
||||
+
|
||||
+fn extend_to_multiple(mut key: Vec<u8>, multiple: usize) -> Confidential<Vec<u8>> {
|
||||
+ match key.len().checked_rem(multiple) {
|
||||
+ Some(0) | None => key,
|
||||
+ Some(m) => {
|
||||
+ key.resize(key.len() + multiple - m, 0);
|
||||
+ key
|
||||
+ }
|
||||
+ }
|
||||
+ .into()
|
||||
+}
|
||||
+
|
||||
+/// Get a plain-text key
|
||||
+///
|
||||
+/// ```none
|
||||
+/// size U16<BigEndian> | payload (0-8190) bytes
|
||||
+/// ```
|
||||
+fn plaintext(inp: Confidential<Vec<u8>>) -> Result<RetrKeyInfo> {
|
||||
+ let key_len = inp.value().len();
|
||||
+ if key_len > RetrieveCmd::MAX_SIZE {
|
||||
+ return Err(Error::RetrInvKey {
|
||||
+ what: "key size",
|
||||
+ value: key_len.to_string(),
|
||||
+ kind: RetrievableSecret::PlainText.to_string(),
|
||||
+ exp: RetrievableSecret::PlainText.expected(),
|
||||
+ });
|
||||
+ }
|
||||
+ let mut key = Vec::with_capacity(2 + inp.value().len());
|
||||
+ let key_len: U16<BigEndian> = (key_len as u16).into();
|
||||
+ key.extend_from_slice(key_len.as_bytes());
|
||||
+ key.extend_from_slice(inp.value());
|
||||
+ let key = extend_to_multiple(key, SymKeyType::AES_256_GCM_BLOCK_LEN);
|
||||
+
|
||||
+ Ok((RetrievableSecret::PlainText, key))
|
||||
+}
|
||||
+
|
||||
+/// Get an AES-key
|
||||
+fn aes(key: Confidential<Vec<u8>>) -> Result<RetrKeyInfo> {
|
||||
+ let key_len = key.value().len() as u32;
|
||||
+ let bit_size = bitsize(key_len);
|
||||
+ match AesSizes::from_bits(bit_size) {
|
||||
+ Some(size) => Ok((RetrievableSecret::Aes(size), key)),
|
||||
+ None => {
|
||||
+ // Use some AES type to get exp sizes and name
|
||||
+ let kind = RetrievableSecret::Aes(AesSizes::Bits128);
|
||||
+ Err(Error::RetrInvKey {
|
||||
+ what: "key size",
|
||||
+ value: bit_size.to_string(),
|
||||
+ kind: format!("{kind:#}"),
|
||||
+ exp: kind.expected(),
|
||||
+ })
|
||||
+ }
|
||||
+ }
|
||||
+}
|
||||
+
|
||||
+/// Get an AES-XTS-key
|
||||
+fn aes_xts(key: Confidential<Vec<u8>>) -> Result<RetrKeyInfo> {
|
||||
+ let key_len = key.value().len() as u32;
|
||||
+ let bit_size = bitsize(key_len / 2);
|
||||
+ match AesXtsSizes::from_bits(bit_size) {
|
||||
+ Some(size) => Ok((RetrievableSecret::AesXts(size), key)),
|
||||
+ None => {
|
||||
+ // Use some AES-XTS type to get exp sizes and name
|
||||
+ let kind = RetrievableSecret::AesXts(AesXtsSizes::Bits128);
|
||||
+ Err(Error::RetrInvKey {
|
||||
+ what: "key size",
|
||||
+ value: bit_size.to_string(),
|
||||
+ kind: format!("{kind:#}"),
|
||||
+ exp: kind.expected(),
|
||||
+ })
|
||||
+ }
|
||||
+ }
|
||||
+}
|
||||
+
|
||||
+/// Get an HMAC-SHA-key
|
||||
+fn hmac_sha(key: Confidential<Vec<u8>>) -> Result<RetrKeyInfo> {
|
||||
+ let key_len = key.value().len() as u32;
|
||||
+ let size = bitsize(key_len / 2);
|
||||
+ match HmacShaSizes::from_sha_size(size) {
|
||||
+ Some(size) => Ok((RetrievableSecret::HmacSha(size), key)),
|
||||
+ None => {
|
||||
+ // Use some HMAC type to get exp sizes and name
|
||||
+ let kind = RetrievableSecret::HmacSha(HmacShaSizes::Sha256);
|
||||
+ Err(Error::RetrInvKey {
|
||||
+ what: "key size",
|
||||
+ value: size.to_string(),
|
||||
+ kind: format!("{kind:#}"),
|
||||
+ exp: kind.expected(),
|
||||
+ })
|
||||
+ }
|
||||
+ }
|
||||
+}
|
||||
+
|
||||
+/// Get an EC-private-key
|
||||
+fn ec(key: PKey<Private>) -> Result<RetrKeyInfo> {
|
||||
+ let (key, nid) = match key.id() {
|
||||
+ Id::EC => {
|
||||
+ let ec_key = key.ec_key()?;
|
||||
+ let key = ec_key.private_key().to_vec();
|
||||
+ let nid = ec_key.group().curve_name().unwrap_or(Nid::UNDEF);
|
||||
+ (key, nid)
|
||||
+ }
|
||||
+ // ED keys are not handled via the EC struct in OpenSSL.
|
||||
+ id @ (Id::ED25519 | Id::ED448) => {
|
||||
+ let key = key.raw_private_key()?;
|
||||
+ let nid = Nid::from_raw(id.as_raw());
|
||||
+ (key, nid)
|
||||
+ }
|
||||
+ _ => (vec![], Nid::UNDEF),
|
||||
+ };
|
||||
+
|
||||
+ let kind = match nid {
|
||||
+ Nid::X9_62_PRIME256V1 => EcCurves::Secp256R1,
|
||||
+ Nid::SECP384R1 => EcCurves::Secp384R1,
|
||||
+ Nid::SECP521R1 => EcCurves::Secp521R1,
|
||||
+ NID_ED25519 => EcCurves::Ed25519,
|
||||
+ NID_ED448 => EcCurves::Ed448,
|
||||
+ nid => {
|
||||
+ // Use some EC type to get exp sizes and name
|
||||
+ let ec = RetrievableSecret::Ec(EcCurves::Secp521R1);
|
||||
+ return Err(Error::RetrInvKey {
|
||||
+ what: "curve or format",
|
||||
+ kind: format!("{ec:#}"),
|
||||
+ value: nid.long_name()?.to_string(),
|
||||
+ exp: ec.expected(),
|
||||
+ });
|
||||
+ }
|
||||
+ };
|
||||
+
|
||||
+ let key = kind.resize_raw_key(key);
|
||||
+ Ok((RetrievableSecret::Ec(kind), key.into()))
|
||||
+}
|
||||
+
|
||||
+#[inline(always)]
|
||||
+const fn bitsize(bytesize: u32) -> u32 {
|
||||
+ bytesize * 8
|
||||
+}
|
||||
+
|
||||
impl Display for GuestSecret {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
match self {
|
||||
Self::Null => write!(f, "Meta"),
|
||||
gs => {
|
||||
let kind: U16<BigEndian> = gs.kind().into();
|
||||
- let st: ListableSecretType = kind.into();
|
||||
+ let st: ListableSecretType = kind.get().into();
|
||||
write!(f, "{st}")
|
||||
}
|
||||
}
|
||||
@@ -153,20 +363,24 @@ assert_size!(ListableSecretHdr, 0x30);
|
||||
|
||||
impl ListableSecretHdr {
|
||||
fn from_guest_secret(gs: &GuestSecret) -> Option<Self> {
|
||||
- let id = gs.id()?;
|
||||
Some(Self {
|
||||
res0: 0,
|
||||
kind: gs.kind().into(),
|
||||
secret_len: gs.secret_len().into(),
|
||||
res8: 0,
|
||||
- id,
|
||||
+ id: gs.id()?,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
+
|
||||
+ use super::HmacShaSizes as HmacSizes;
|
||||
+ use super::RetrievableSecret::*;
|
||||
use super::*;
|
||||
+ use openssl::ec::{EcGroup, EcKey};
|
||||
+ use pv_core::uv::AesSizes;
|
||||
use serde_test::{assert_tokens, Token};
|
||||
|
||||
#[test]
|
||||
@@ -187,8 +401,103 @@ mod test {
|
||||
assert_eq!(secret, exp);
|
||||
}
|
||||
|
||||
+ macro_rules! retr_test {
|
||||
+ ($name: ident, $func: ident, $size: expr, $exp_kind: expr) => {
|
||||
+ #[test]
|
||||
+ fn $name() {
|
||||
+ let secret_value = vec![0x11; $size];
|
||||
+ let name = "test retr secret".to_string();
|
||||
+ let secret = GuestSecret::$func(&name, secret_value.clone().into()).unwrap();
|
||||
+ let exp_id = [
|
||||
+ 0x61, 0x2c, 0xd6, 0x3e, 0xa8, 0xf2, 0xc1, 0x15, 0xc1, 0xe, 0x15, 0xb8, 0x8a,
|
||||
+ 0x90, 0x16, 0xc1, 0x55, 0xef, 0x9c, 0x7c, 0x2c, 0x8e, 0x56, 0xd0, 0x78, 0x4c,
|
||||
+ 0x8a, 0x1d, 0xc9, 0x3a, 0x80, 0xba,
|
||||
+ ];
|
||||
+ let exp = GuestSecret::Retrievable {
|
||||
+ kind: $exp_kind,
|
||||
+ name,
|
||||
+ id: exp_id.into(),
|
||||
+ secret: secret_value.into(),
|
||||
+ };
|
||||
+ assert_eq!(exp, secret);
|
||||
+ }
|
||||
+ };
|
||||
+ }
|
||||
+
|
||||
+ retr_test!(retr_aes_128, aes, 16, Aes(AesSizes::Bits128));
|
||||
+ retr_test!(retr_aes_192, aes, 24, Aes(AesSizes::Bits192));
|
||||
+ retr_test!(retr_aes_256, aes, 32, Aes(AesSizes::Bits256));
|
||||
+ retr_test!(retr_aes_xts_128, aes_xts, 32, AesXts(AesXtsSizes::Bits128));
|
||||
+ retr_test!(retr_aes_xts_256, aes_xts, 64, AesXts(AesXtsSizes::Bits256));
|
||||
+ retr_test!(retr_aes_hmac_256, hmac_sha, 64, HmacSha(HmacSizes::Sha256));
|
||||
+ retr_test!(retr_aes_hmac_512, hmac_sha, 128, HmacSha(HmacSizes::Sha512));
|
||||
+
|
||||
+ #[test]
|
||||
+ fn plaintext_no_pad() {
|
||||
+ let key = vec![0, 14, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7];
|
||||
+ let name = "PLAINTEXT_PAD".to_string();
|
||||
+ let secret = GuestSecret::plaintext(&name, key[2..].to_vec().into()).unwrap();
|
||||
+ let exp_id = [
|
||||
+ 15, 123, 176, 210, 135, 231, 220, 232, 148, 93, 198, 195, 165, 212, 214, 129, 45, 1,
|
||||
+ 94, 11, 167, 18, 151, 15, 120, 254, 13, 109, 173, 186, 37, 74,
|
||||
+ ];
|
||||
+ let exp = GuestSecret::Retrievable {
|
||||
+ kind: PlainText,
|
||||
+ name,
|
||||
+ id: exp_id.into(),
|
||||
+ secret: key.into(),
|
||||
+ };
|
||||
+
|
||||
+ assert_eq!(secret, exp);
|
||||
+ }
|
||||
+
|
||||
#[test]
|
||||
- fn ap_asc_parse() {
|
||||
+ fn plaintext_pad() {
|
||||
+ let key = vec![0, 10, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 0, 0, 0, 0];
|
||||
+ let name = "PLAINTEXT_PAD".to_string();
|
||||
+ let secret = GuestSecret::plaintext(&name, key[2..12].to_vec().into()).unwrap();
|
||||
+ let exp_id = [
|
||||
+ 15, 123, 176, 210, 135, 231, 220, 232, 148, 93, 198, 195, 165, 212, 214, 129, 45, 1,
|
||||
+ 94, 11, 167, 18, 151, 15, 120, 254, 13, 109, 173, 186, 37, 74,
|
||||
+ ];
|
||||
+ let exp = GuestSecret::Retrievable {
|
||||
+ kind: PlainText,
|
||||
+ name,
|
||||
+ id: exp_id.into(),
|
||||
+ secret: key.into(),
|
||||
+ };
|
||||
+
|
||||
+ assert_eq!(secret, exp);
|
||||
+ }
|
||||
+
|
||||
+ #[track_caller]
|
||||
+ fn test_ec(grp: Nid, exp_kind: EcCurves, exp_len: usize) {
|
||||
+ let key = match grp {
|
||||
+ NID_ED25519 => PKey::generate_ed25519().unwrap(),
|
||||
+ NID_ED448 => PKey::generate_ed448().unwrap(),
|
||||
+ nid => {
|
||||
+ let group = EcGroup::from_curve_name(nid).unwrap();
|
||||
+ let key = EcKey::generate(&group).unwrap();
|
||||
+ PKey::from_ec_key(key).unwrap()
|
||||
+ }
|
||||
+ };
|
||||
+ let (kind, key) = ec(key).unwrap();
|
||||
+
|
||||
+ assert_eq!(kind, Ec(exp_kind));
|
||||
+ assert_eq!(key.value().len(), exp_len);
|
||||
+ }
|
||||
+
|
||||
+ #[test]
|
||||
+ fn retr_ec() {
|
||||
+ test_ec(Nid::X9_62_PRIME256V1, EcCurves::Secp256R1, 32);
|
||||
+ test_ec(Nid::SECP384R1, EcCurves::Secp384R1, 48);
|
||||
+ test_ec(Nid::SECP521R1, EcCurves::Secp521R1, 80);
|
||||
+ test_ec(NID_ED25519, EcCurves::Ed25519, 32);
|
||||
+ test_ec(NID_ED448, EcCurves::Ed448, 64);
|
||||
+ }
|
||||
+
|
||||
+ #[test]
|
||||
+ fn asc_parse() {
|
||||
let id = [
|
||||
0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef, 0x01, 0x23, 0x45, 0x67, 0x89, 0xab,
|
||||
0xcd, 0xef, 0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef, 0x01, 0x23, 0x45, 0x67,
|
||||
@@ -217,6 +526,39 @@ mod test {
|
||||
);
|
||||
}
|
||||
|
||||
+ #[test]
|
||||
+ fn retrievable_parse() {
|
||||
+ let id = [
|
||||
+ 0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef, 0x01, 0x23, 0x45, 0x67, 0x89, 0xab,
|
||||
+ 0xcd, 0xef, 0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef, 0x01, 0x23, 0x45, 0x67,
|
||||
+ 0x89, 0xab, 0xcd, 0xef,
|
||||
+ ];
|
||||
+ let asc = GuestSecret::Retrievable {
|
||||
+ kind: PlainText,
|
||||
+ name: "test123".to_string(),
|
||||
+ id: id.into(),
|
||||
+ secret: vec![].into(),
|
||||
+ };
|
||||
+
|
||||
+ assert_tokens(
|
||||
+ &asc,
|
||||
+ &[
|
||||
+ Token::StructVariant {
|
||||
+ name: "GuestSecret",
|
||||
+ variant: "Retrievable",
|
||||
+ len: 3,
|
||||
+ },
|
||||
+ Token::String("kind"),
|
||||
+ Token::String("3 (PLAINTEXT)"),
|
||||
+ Token::String("name"),
|
||||
+ Token::String("test123"),
|
||||
+ Token::String("id"),
|
||||
+ Token::String("0x0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"),
|
||||
+ Token::StructVariantEnd,
|
||||
+ ],
|
||||
+ );
|
||||
+ }
|
||||
+
|
||||
#[test]
|
||||
fn guest_secret_bin_null() {
|
||||
let gs = GuestSecret::Null;
|
||||
@@ -228,7 +570,7 @@ mod test {
|
||||
}
|
||||
|
||||
#[test]
|
||||
- fn guest_secret_bin_ap() {
|
||||
+ fn guest_secret_bin_asoc() {
|
||||
let gs = GuestSecret::Association {
|
||||
name: "test".to_string(),
|
||||
id: [1; 32].into(),
|
||||
@@ -241,4 +583,21 @@ mod test {
|
||||
assert_eq!(exp, gs_bytes_auth.get());
|
||||
assert_eq!(&[2; 32], gs.confidential());
|
||||
}
|
||||
+
|
||||
+ #[test]
|
||||
+ fn guest_secret_bin_retr() {
|
||||
+ let gs = GuestSecret::Retrievable {
|
||||
+ kind: PlainText,
|
||||
+ name: "test".to_string(),
|
||||
+ id: [1; 32].into(),
|
||||
+ secret: vec![2; 32].into(),
|
||||
+ };
|
||||
+ let auth = gs.auth();
|
||||
+ let gs_bytes_auth = auth.get();
|
||||
+ let mut exp = vec![0u8, 0, 0, 3, 0, 0, 0, 0x20, 0, 0, 0, 0, 0, 0, 0, 0];
|
||||
+ exp.extend([1; 32]);
|
||||
+
|
||||
+ assert_eq!(exp, gs_bytes_auth);
|
||||
+ assert_eq!(&[2; 32], gs.confidential());
|
||||
+ }
|
||||
}
|
||||
diff --git a/rust/pv/src/uvsecret/retr_secret.rs b/rust/pv/src/uvsecret/retr_secret.rs
|
||||
new file mode 100644
|
||||
index 00000000..5fad016f
|
||||
--- /dev/null
|
||||
+++ b/rust/pv/src/uvsecret/retr_secret.rs
|
||||
@@ -0,0 +1,234 @@
|
||||
+// SPDX-License-Identifier: MIT
|
||||
+//
|
||||
+// Copyright IBM Corp. 2024
|
||||
+
|
||||
+use crate::{pem::Pem, uvsecret::guest_secret::MAX_SIZE_PLAIN_PAYLOAD, Result};
|
||||
+
|
||||
+use byteorder::BigEndian;
|
||||
+use log::warn;
|
||||
+use pv_core::{
|
||||
+ request::Confidential,
|
||||
+ uv::{ListableSecretType, RetrievableSecret, RetrieveCmd},
|
||||
+};
|
||||
+use zerocopy::{FromBytes, U16};
|
||||
+
|
||||
+/// An IBM Protected Key
|
||||
+///
|
||||
+/// A protected key, writeable as pem.
|
||||
+///
|
||||
+/// Will convert into PEM as:
|
||||
+/// ```PEM
|
||||
+///-----BEGIN IBM PROTECTED KEY-----
|
||||
+///kind: <name>
|
||||
+///
|
||||
+///<protected key in base64>
|
||||
+///-----END IBM PROTECTED KEY-----
|
||||
+/// ```
|
||||
+#[derive(Debug, PartialEq, Eq)]
|
||||
+pub struct IbmProtectedKey {
|
||||
+ kind: ListableSecretType,
|
||||
+ key: Confidential<Vec<u8>>,
|
||||
+}
|
||||
+
|
||||
+impl IbmProtectedKey {
|
||||
+ /// Get the binary representation of the key.
|
||||
+ pub fn data(&self) -> &[u8] {
|
||||
+ self.key.value()
|
||||
+ }
|
||||
+
|
||||
+ /// Converts a [`IbmProtectedKey`] into a vector.
|
||||
+ pub fn into_bytes(self) -> Confidential<Vec<u8>> {
|
||||
+ self.key
|
||||
+ }
|
||||
+
|
||||
+ /// Get the data in PEM format.
|
||||
+ ///
|
||||
+ /// # Errors
|
||||
+ ///
|
||||
+ /// This function will return an error if the PEM conversion failed (very unlikely).
|
||||
+ pub fn to_pem(&self) -> Result<Pem> {
|
||||
+ Pem::new(
|
||||
+ "IBM PROTECTED KEY",
|
||||
+ format!("kind: {}", self.kind),
|
||||
+ self.key.value(),
|
||||
+ )
|
||||
+ }
|
||||
+
|
||||
+ fn new<K>(kind: ListableSecretType, key: K) -> Self
|
||||
+ where
|
||||
+ K: Into<Confidential<Vec<u8>>>,
|
||||
+ {
|
||||
+ Self {
|
||||
+ kind,
|
||||
+ key: key.into(),
|
||||
+ }
|
||||
+ }
|
||||
+}
|
||||
+
|
||||
+impl From<RetrieveCmd> for RetrievedSecret {
|
||||
+ fn from(value: RetrieveCmd) -> Self {
|
||||
+ let kind = value.meta_data().stype();
|
||||
+ let key = value.into_key();
|
||||
+
|
||||
+ match kind {
|
||||
+ ListableSecretType::Retrievable(RetrievableSecret::PlainText) => {
|
||||
+ // Will not run into default, retrieve has a granularity of 16 bytes and 16 bytes is the
|
||||
+ // minimum size
|
||||
+ let len = U16::<BigEndian>::read_from_prefix(key.value())
|
||||
+ .unwrap_or_default()
|
||||
+ .get() as usize;
|
||||
+
|
||||
+ // Test if the plain text secret has a size:
|
||||
+ // 1. len <= 8190
|
||||
+ // 2. first two bytes are max 15 less than buffer-size+2
|
||||
+ // 3. bytes after len + 2 are zero
|
||||
+ match len <= MAX_SIZE_PLAIN_PAYLOAD
|
||||
+ && key.value().len() - (len + 2) < 15
|
||||
+ && key.value()[len + 2..].iter().all(|c| *c == 0)
|
||||
+ {
|
||||
+ false => Self::Plaintext(key),
|
||||
+ true => Self::Plaintext(key.value()[2..len + 2].to_vec().into()),
|
||||
+ }
|
||||
+ }
|
||||
+ kind => {
|
||||
+ match kind {
|
||||
+ ListableSecretType::Retrievable(_) => (),
|
||||
+ _ => warn!("Retrieved an unretrievable Secret! Will continue; interpreting it as a protected key."),
|
||||
+ }
|
||||
+ Self::ProtectedKey(IbmProtectedKey::new(kind, key))
|
||||
+ }
|
||||
+ }
|
||||
+ }
|
||||
+}
|
||||
+
|
||||
+/// A retrieved Secret.
|
||||
+#[derive(Debug, PartialEq, Eq)]
|
||||
+pub enum RetrievedSecret {
|
||||
+ /// A plaintext secret
|
||||
+ Plaintext(Confidential<Vec<u8>>),
|
||||
+ /// An [`IbmProtectedKey`]
|
||||
+ ProtectedKey(IbmProtectedKey),
|
||||
+}
|
||||
+
|
||||
+impl RetrievedSecret {
|
||||
+ /// Create a new IBM PROTECTED KEY object
|
||||
+ pub fn from_cmd(cmd: RetrieveCmd) -> Self {
|
||||
+ cmd.into()
|
||||
+ }
|
||||
+
|
||||
+ /// Get the binary representation of the key.
|
||||
+ pub fn data(&self) -> &[u8] {
|
||||
+ match self {
|
||||
+ RetrievedSecret::Plaintext(p) => p.value(),
|
||||
+ RetrievedSecret::ProtectedKey(p) => p.data(),
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ /// Converts a [`IbmProtectedKey`] into a vector.
|
||||
+ pub fn into_bytes(self) -> Confidential<Vec<u8>> {
|
||||
+ match self {
|
||||
+ RetrievedSecret::Plaintext(p) => p,
|
||||
+ RetrievedSecret::ProtectedKey(p) => p.into_bytes(),
|
||||
+ }
|
||||
+ }
|
||||
+ /// Get the data in PEM format.
|
||||
+ ///
|
||||
+ /// # Errors
|
||||
+ ///
|
||||
+ /// This function will return an error if the PEM conversion failed (very unlikely).
|
||||
+ pub fn to_pem(&self) -> Result<Pem> {
|
||||
+ match self {
|
||||
+ RetrievedSecret::Plaintext(p) => Pem::new("PLAINTEXT SECRET", None, p.value()),
|
||||
+ RetrievedSecret::ProtectedKey(p) => p.to_pem(),
|
||||
+ }
|
||||
+ }
|
||||
+}
|
||||
+
|
||||
+#[cfg(test)]
|
||||
+mod test {
|
||||
+ use super::*;
|
||||
+ use pv_core::uv::*;
|
||||
+
|
||||
+ fn mk_retr(secret: &[u8]) -> RetrievedSecret {
|
||||
+ let entry = SecretEntry::new(
|
||||
+ 0,
|
||||
+ ListableSecretType::Retrievable(RetrievableSecret::PlainText),
|
||||
+ SecretId::default(),
|
||||
+ secret.len() as u32,
|
||||
+ );
|
||||
+ let mut cmd = RetrieveCmd::from_entry(entry).unwrap();
|
||||
+ cmd.data().unwrap().copy_from_slice(secret);
|
||||
+ RetrievedSecret::from_cmd(cmd)
|
||||
+ }
|
||||
+
|
||||
+ #[test]
|
||||
+ fn from_retr_cmd() {
|
||||
+ let secret = vec![0, 10, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0xa, 0, 0, 0, 0];
|
||||
+ let prot_key = mk_retr(&secret);
|
||||
+ let exp = RetrievedSecret::Plaintext(secret[2..12].to_vec().into());
|
||||
+ assert_eq!(prot_key, exp);
|
||||
+ }
|
||||
+
|
||||
+ #[test]
|
||||
+ fn from_retr_inv_size() {
|
||||
+ let secret = vec![0x20; 32];
|
||||
+ let prot_key = mk_retr(&secret);
|
||||
+ let exp = RetrievedSecret::Plaintext(secret.into());
|
||||
+ assert_eq!(prot_key, exp);
|
||||
+ }
|
||||
+
|
||||
+ #[test]
|
||||
+ fn from_retr_inv_no_zero_after_end() {
|
||||
+ let secret = vec![0, 10, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0xa, 1, 0, 0, 0];
|
||||
+ let prot_key = mk_retr(&secret);
|
||||
+ let exp = RetrievedSecret::Plaintext(secret.into());
|
||||
+ assert_eq!(prot_key, exp);
|
||||
+ }
|
||||
+
|
||||
+ #[test]
|
||||
+ fn from_retr_inv_to_much_padding() {
|
||||
+ let secret = vec![
|
||||
+ 0, 10, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0xa, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
+ 0, 0, 0, 0,
|
||||
+ ];
|
||||
+ let prot_key = mk_retr(&secret);
|
||||
+ let exp = RetrievedSecret::Plaintext(secret.into());
|
||||
+ assert_eq!(prot_key, exp);
|
||||
+ }
|
||||
+
|
||||
+ #[test]
|
||||
+ fn from_retr_0_size() {
|
||||
+ let secret = vec![0x00; 32];
|
||||
+ let prot_key = mk_retr(&secret);
|
||||
+ let exp = RetrievedSecret::Plaintext(secret.into());
|
||||
+ assert_eq!(prot_key, exp);
|
||||
+ }
|
||||
+
|
||||
+ #[test]
|
||||
+ fn plain_text_pem() {
|
||||
+ let exp = "\
|
||||
+ -----BEGIN PLAINTEXT SECRET-----\n\
|
||||
+ ERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERER\n\
|
||||
+ -----END PLAINTEXT SECRET-----\n";
|
||||
+ let prot = RetrievedSecret::Plaintext(vec![17; 48].into());
|
||||
+ let pem = prot.to_pem().unwrap();
|
||||
+ let pem_str = pem.to_string();
|
||||
+ assert_eq!(pem_str, exp);
|
||||
+ }
|
||||
+
|
||||
+ #[test]
|
||||
+ fn prot_key_pem() {
|
||||
+ let exp = "\
|
||||
+ -----BEGIN IBM PROTECTED KEY-----\n\
|
||||
+ kind: AES-128-KEY\n\n\
|
||||
+ ERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERER\n\
|
||||
+ -----END IBM PROTECTED KEY-----\n";
|
||||
+ let prot = IbmProtectedKey::new(
|
||||
+ ListableSecretType::Retrievable(RetrievableSecret::Aes(AesSizes::Bits128)),
|
||||
+ vec![17; 48],
|
||||
+ );
|
||||
+ let pem = prot.to_pem().unwrap();
|
||||
+ let pem_str = pem.to_string();
|
||||
+ assert_eq!(pem_str, exp);
|
||||
+ }
|
||||
+}
|
@ -1,95 +0,0 @@
|
||||
From a14f9d4edcc5db0d54e4fbe3ec3d98c7c270bf8e Mon Sep 17 00:00:00 2001
|
||||
From: Steffen Eiden <seiden@linux.ibm.com>
|
||||
Date: Fri, 13 Dec 2024 15:04:02 +0100
|
||||
Subject: [PATCH] rust/pvsecret: Improve CLI
|
||||
MIME-Version: 1.0
|
||||
Content-Type: text/plain; charset=UTF-8
|
||||
Content-Transfer-Encoding: 8bit
|
||||
|
||||
Improve the wording of the help/man text/
|
||||
|
||||
Acked-by: Marc Hartmayer <marc@linux.ibm.com>
|
||||
Reviewed-by: Christoph Schlameuss <schlameuss@linux.ibm.com>
|
||||
Signed-off-by: Steffen Eiden <seiden@linux.ibm.com>
|
||||
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
|
||||
---
|
||||
rust/pvsecret/src/cli.rs | 26 +++++++++++++-------------
|
||||
1 file changed, 13 insertions(+), 13 deletions(-)
|
||||
|
||||
diff --git a/rust/pvsecret/src/cli.rs b/rust/pvsecret/src/cli.rs
|
||||
index 6deaaebd..c4b9f2b3 100644
|
||||
--- a/rust/pvsecret/src/cli.rs
|
||||
+++ b/rust/pvsecret/src/cli.rs
|
||||
@@ -37,8 +37,8 @@ pub struct CreateSecretOpt {
|
||||
|
||||
/// Specifies the header of the guest image.
|
||||
///
|
||||
- /// Can be an IBM Secure Execution image created by genprotimg or an extracted IBM Secure
|
||||
- /// Execution header. The header must start at a page boundary.
|
||||
+ /// Can be an IBM Secure Execution image created by 'pvimg/genprotimg' or an
|
||||
+ /// extracted IBM Secure Execution header.
|
||||
#[arg(long, value_name = "FILE", value_hint = ValueHint::FilePath)]
|
||||
pub hdr: String,
|
||||
|
||||
@@ -150,12 +150,12 @@ pub enum AddSecretType {
|
||||
|
||||
/// Create an association secret.
|
||||
///
|
||||
- /// Use an association secret to connect a trusted I/O device to a guest. The `pvapconfig` tool
|
||||
+ /// Use an association secret to connect a trusted I/O device to a guest. The 'pvapconfig' tool
|
||||
/// provides more information about association secrets.
|
||||
Association {
|
||||
- /// String to identify the new secret.
|
||||
+ /// String that identifies the new secret.
|
||||
///
|
||||
- /// The actual secret is set with --input-secret. The name is saved in `NAME.yaml` with
|
||||
+ /// The actual secret is set with '--input-secret'. The name is saved in `NAME.yaml` with
|
||||
/// white-spaces mapped to `_`.
|
||||
name: String,
|
||||
|
||||
@@ -166,15 +166,15 @@ pub enum AddSecretType {
|
||||
stdout: bool,
|
||||
|
||||
/// Path from which to read the plaintext secret. Uses a random secret if not specified.
|
||||
- #[arg(long, value_name = "FILE", value_hint = ValueHint::FilePath, conflicts_with("output_secret"))]
|
||||
+ #[arg(long, value_name = "SECRET-FILE", value_hint = ValueHint::FilePath, conflicts_with("output_secret"))]
|
||||
input_secret: Option<String>,
|
||||
|
||||
- /// Save the generated secret as plaintext in FILE.
|
||||
+ /// Save the generated secret as plaintext in SECRET-FILE.
|
||||
///
|
||||
/// The generated secret can be used to generate add-secret requests for a different guest
|
||||
- /// with the same secret using --input-secret. Destroy the secret when it is not used
|
||||
+ /// with the same secret using '--input-secret'. Destroy the secret when it is not used
|
||||
/// anymore.
|
||||
- #[arg(long, value_name = "FILE", value_hint = ValueHint::FilePath,)]
|
||||
+ #[arg(long, value_name = "SECRET-FILE", value_hint = ValueHint::FilePath,)]
|
||||
output_secret: Option<String>,
|
||||
},
|
||||
}
|
||||
@@ -243,13 +243,13 @@ pub enum Command {
|
||||
/// Create a new add-secret request.
|
||||
///
|
||||
/// Create add-secret requests for IBM Secure Execution guests. Only create these requests in a
|
||||
- /// trusted environment, such as your workstation. The `pvattest create` command creates a
|
||||
+ /// trusted environment, such as your workstation. The 'pvattest create' command creates a
|
||||
/// randomly generated key to protect the request. The generated requests can then be added on
|
||||
- /// an IBM Secure Execution guest using `pvsecret add`. The guest can then use the secrets with
|
||||
+ /// an IBM Secure Execution guest using 'pvsecret add'. The guest can then use the secrets with
|
||||
/// the use case depending on the secret type.
|
||||
Create(Box<CreateSecretOpt>),
|
||||
|
||||
- /// Perform an add-secret request (s390x only).
|
||||
+ /// Submit an add-secret request to the Ultravisor (s390x only).
|
||||
///
|
||||
/// Perform an add-secret request using a previously generated add-secret request. Only
|
||||
/// available on s390x.
|
||||
@@ -258,7 +258,7 @@ pub enum Command {
|
||||
/// Lock the secret-store (s390x only).
|
||||
///
|
||||
/// Lock the secret store (s390x only). After this command executed successfully, all
|
||||
- /// add-secret requests will fail. Only available on s390x.
|
||||
+ /// subsequent add-secret requests will fail. Only available on s390x.
|
||||
Lock,
|
||||
|
||||
/// List all ultravisor secrets (s390x only).
|
@ -1,423 +0,0 @@
|
||||
From 93da795520ca2f0a73cfbfc951a9b16437a1b95b Mon Sep 17 00:00:00 2001
|
||||
From: Steffen Eiden <seiden@linux.ibm.com>
|
||||
Date: Mon, 19 Feb 2024 15:15:16 +0100
|
||||
Subject: [PATCH] rust/pvsecret: Add support for retrievable secrets
|
||||
MIME-Version: 1.0
|
||||
Content-Type: text/plain; charset=UTF-8
|
||||
Content-Transfer-Encoding: 8bit
|
||||
|
||||
Support for creating and retrieving retrievable secrets.
|
||||
|
||||
Acked-by: Marc Hartmayer <marc@linux.ibm.com>
|
||||
Reviewed-by: Christoph Schlameuss <schlameuss@linux.ibm.com>
|
||||
Signed-off-by: Steffen Eiden <seiden@linux.ibm.com>
|
||||
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
|
||||
---
|
||||
rust/pvsecret/src/cli.rs | 129 +++++++++++++++++++++++++++++++-
|
||||
rust/pvsecret/src/cmd.rs | 6 +-
|
||||
rust/pvsecret/src/cmd/create.rs | 30 +++++++-
|
||||
rust/pvsecret/src/cmd/list.rs | 12 ++-
|
||||
rust/pvsecret/src/cmd/retr.rs | 62 +++++++++++++++
|
||||
rust/pvsecret/src/main.rs | 1 +
|
||||
6 files changed, 230 insertions(+), 10 deletions(-)
|
||||
create mode 100644 rust/pvsecret/src/cmd/retr.rs
|
||||
|
||||
diff --git a/rust/pvsecret/src/cli.rs b/rust/pvsecret/src/cli.rs
|
||||
index c4b9f2b3..4e747682 100644
|
||||
--- a/rust/pvsecret/src/cli.rs
|
||||
+++ b/rust/pvsecret/src/cli.rs
|
||||
@@ -1,7 +1,10 @@
|
||||
// SPDX-License-Identifier: MIT
|
||||
//
|
||||
-// Copyright IBM Corp. 2023
|
||||
+// Copyright IBM Corp. 2023, 2024
|
||||
|
||||
+use std::fmt::Display;
|
||||
+
|
||||
+use clap::error::ErrorKind::ValueValidation;
|
||||
use clap::{ArgGroup, Args, CommandFactory, Parser, Subcommand, ValueEnum, ValueHint};
|
||||
use utils::{CertificateOptions, DeprecatedVerbosityOptions, STDOUT};
|
||||
|
||||
@@ -177,6 +180,72 @@ pub enum AddSecretType {
|
||||
#[arg(long, value_name = "SECRET-FILE", value_hint = ValueHint::FilePath,)]
|
||||
output_secret: Option<String>,
|
||||
},
|
||||
+
|
||||
+ /// Create a retrievable secret.
|
||||
+ ///
|
||||
+ /// A retrievable secret is stored in the per-guest storage of the Ultravisor. A SE-guest can
|
||||
+ /// retrieve the secret at runtime and use it. All retrievable secrets, but the plaintext
|
||||
+ /// secret, are retrieved as wrapped/protected key objects and only usable inside the current,
|
||||
+ /// running SE-guest instance.
|
||||
+ #[command(visible_alias = "retr")]
|
||||
+ Retrievable {
|
||||
+ /// String that identifies the new secret.
|
||||
+ ///
|
||||
+ /// The actual secret is set with '--secret'. The name is saved in `NAME.yaml` with
|
||||
+ /// white-spaces mapped to `_`.
|
||||
+ name: String,
|
||||
+
|
||||
+ /// Print the hashed name to stdout.
|
||||
+ ///
|
||||
+ /// The hashed name is not written to `NAME.yaml`
|
||||
+ #[arg(long)]
|
||||
+ stdout: bool,
|
||||
+
|
||||
+ /// Use SECRET-FILE as retrievable secret
|
||||
+ #[arg(long, value_name = "SECRET-FILE", value_hint = ValueHint::FilePath)]
|
||||
+ secret: String,
|
||||
+
|
||||
+ /// Specify the secret type.
|
||||
+ ///
|
||||
+ /// Limitations to the input data apply depending on the secret type.
|
||||
+ #[arg(long = "type", value_name = "TYPE")]
|
||||
+ kind: RetrieveableSecretInpKind,
|
||||
+ },
|
||||
+}
|
||||
+
|
||||
+#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, ValueEnum, Debug)]
|
||||
+pub enum RetrieveableSecretInpKind {
|
||||
+ /// A plaintext secret.
|
||||
+ /// Can be any file up to 8190 bytes long
|
||||
+ Plain,
|
||||
+ /// An AES key.
|
||||
+ /// Must be a plain byte file 128, 192, or 256 bit long.
|
||||
+ Aes,
|
||||
+ /// An AES-XTS key.
|
||||
+ /// Must be a plain byte file 512, or 1024 bit long.
|
||||
+ AesXts,
|
||||
+ /// A HMAC-SHA key.
|
||||
+ /// Must be a plain byte file 512, or 1024 bit long.
|
||||
+ HmacSha,
|
||||
+ /// An elliptic curve private key.
|
||||
+ /// Must be a PEM or DER file.
|
||||
+ Ec,
|
||||
+}
|
||||
+
|
||||
+impl Display for RetrieveableSecretInpKind {
|
||||
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
+ write!(
|
||||
+ f,
|
||||
+ "{}",
|
||||
+ match self {
|
||||
+ Self::Plain => "PLAINTEXT",
|
||||
+ Self::Aes => "AES KEY",
|
||||
+ Self::AesXts => "AES-XTS KEY",
|
||||
+ Self::HmacSha => "HMAC-SHA KEY",
|
||||
+ Self::Ec => "EC PRIVATE KEY",
|
||||
+ }
|
||||
+ )
|
||||
+ }
|
||||
}
|
||||
|
||||
// all members s390x only
|
||||
@@ -238,6 +307,56 @@ pub struct VerifyOpt {
|
||||
pub output: String,
|
||||
}
|
||||
|
||||
+// all members s390x only
|
||||
+#[derive(Args, Debug)]
|
||||
+pub struct RetrSecretOptions {
|
||||
+ /// Specify the secret ID to be retrieved.
|
||||
+ ///
|
||||
+ /// Input type depends on '--inform'. If `yaml` (default) is specified, it must be a yaml
|
||||
+ /// created by the create subcommand of this tool. If `hex` is specified, it must be a hex
|
||||
+ /// 32-byte unsigned big endian number string. Leading zeros are required.
|
||||
+ #[cfg(target_arch = "s390x")]
|
||||
+ #[arg(value_name = "ID", value_hint = ValueHint::FilePath)]
|
||||
+ pub input: String,
|
||||
+
|
||||
+ /// Specify the output path to place the secret value
|
||||
+ #[cfg(target_arch = "s390x")]
|
||||
+ #[arg(short, long, value_name = "FILE", default_value = STDOUT, value_hint = ValueHint::FilePath)]
|
||||
+ pub output: String,
|
||||
+
|
||||
+ /// Define input type for the Secret ID
|
||||
+ #[cfg(target_arch = "s390x")]
|
||||
+ #[arg(long, value_enum, default_value_t)]
|
||||
+ pub inform: RetrInpFmt,
|
||||
+
|
||||
+ /// Define the output format for the retrieved secret
|
||||
+ #[cfg(target_arch = "s390x")]
|
||||
+ #[arg(long, value_enum, default_value_t)]
|
||||
+ pub outform: RetrOutFmt,
|
||||
+}
|
||||
+
|
||||
+#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, ValueEnum, Debug, Default)]
|
||||
+pub enum RetrInpFmt {
|
||||
+ /// Use a yaml file
|
||||
+ #[default]
|
||||
+ Yaml,
|
||||
+ /// Use a hex string.
|
||||
+ Hex,
|
||||
+}
|
||||
+
|
||||
+#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, ValueEnum, Debug, Default)]
|
||||
+pub enum RetrOutFmt {
|
||||
+ /// Write the secret as PEM.
|
||||
+ ///
|
||||
+ /// File starts with `-----BEGIN IBM PROTECTED KEY----` and `-----BEGIN
|
||||
+ /// PLAINTEXT SECRET-----` for plaintext secrets it contains one header
|
||||
+ /// line with the type information and the base64 protected key
|
||||
+ #[default]
|
||||
+ Pem,
|
||||
+ /// Write the secret in binary.
|
||||
+ Bin,
|
||||
+}
|
||||
+
|
||||
#[derive(Subcommand, Debug)]
|
||||
pub enum Command {
|
||||
/// Create a new add-secret request.
|
||||
@@ -274,6 +393,10 @@ pub enum Command {
|
||||
/// provided key. Outputs the arbitrary user-data.
|
||||
Verify(VerifyOpt),
|
||||
|
||||
+ /// Retrieve a secret from the UV secret store (s390x only).
|
||||
+ #[command(visible_alias = "retr")]
|
||||
+ Retrieve(RetrSecretOptions),
|
||||
+
|
||||
/// Print version information and exit.
|
||||
#[command(aliases(["--version"]), hide(true))]
|
||||
Version,
|
||||
@@ -294,13 +417,13 @@ pub fn validate_cli(cli: &CliOptions) -> Result<(), clap::Error> {
|
||||
}
|
||||
if secret_out == &Some(format!("{name}.yaml")) {
|
||||
return Err(CliOptions::command().error(
|
||||
- clap::error::ErrorKind::ValueValidation,
|
||||
+ ValueValidation,
|
||||
format!("Secret output file and the secret name '{name}.yaml' are the same."),
|
||||
));
|
||||
}
|
||||
if format!("{name}.yaml") == opt.output {
|
||||
return Err(CliOptions::command().error(
|
||||
- clap::error::ErrorKind::ValueValidation,
|
||||
+ ValueValidation,
|
||||
format!(
|
||||
"output file and the secret name '{}' are the same.",
|
||||
&opt.output
|
||||
diff --git a/rust/pvsecret/src/cmd.rs b/rust/pvsecret/src/cmd.rs
|
||||
index a826fb31..10d99a5b 100644
|
||||
--- a/rust/pvsecret/src/cmd.rs
|
||||
+++ b/rust/pvsecret/src/cmd.rs
|
||||
@@ -16,6 +16,8 @@ mod add;
|
||||
mod list;
|
||||
#[cfg(target_arch = "s390x")]
|
||||
mod lock;
|
||||
+#[cfg(target_arch = "s390x")]
|
||||
+mod retr;
|
||||
|
||||
// Commands (directly) related to UVCs are only available on s389x
|
||||
#[cfg(target_arch = "s390x")]
|
||||
@@ -24,12 +26,13 @@ mod uv_cmd {
|
||||
pub use add::add;
|
||||
pub use list::list;
|
||||
pub use lock::lock;
|
||||
+ pub use retr::retr;
|
||||
pub const UV_CMD_FN: &[&str] = &["+add", "+lock", "+list"];
|
||||
}
|
||||
|
||||
#[cfg(not(target_arch = "s390x"))]
|
||||
mod uv_cmd {
|
||||
- use crate::cli::{AddSecretOpt, ListSecretOpt};
|
||||
+ use crate::cli::{AddSecretOpt, ListSecretOpt, RetrSecretOptions};
|
||||
use anyhow::{bail, Result};
|
||||
macro_rules! not_supp {
|
||||
($name: ident $( ,$opt: ty )?) => {
|
||||
@@ -40,6 +43,7 @@ mod uv_cmd {
|
||||
}
|
||||
not_supp!(add, AddSecretOpt);
|
||||
not_supp!(list, ListSecretOpt);
|
||||
+ not_supp!(retr, RetrSecretOptions);
|
||||
not_supp!(lock);
|
||||
pub const UV_CMD_FN: &[&str] = &[];
|
||||
}
|
||||
diff --git a/rust/pvsecret/src/cmd/create.rs b/rust/pvsecret/src/cmd/create.rs
|
||||
index 9251c38c..73089a12 100644
|
||||
--- a/rust/pvsecret/src/cmd/create.rs
|
||||
+++ b/rust/pvsecret/src/cmd/create.rs
|
||||
@@ -4,7 +4,6 @@
|
||||
|
||||
use std::path::Path;
|
||||
|
||||
-use crate::cli::{AddSecretType, CreateSecretFlags, CreateSecretOpt};
|
||||
use anyhow::{anyhow, bail, Context, Error, Result};
|
||||
use log::{debug, info, trace, warn};
|
||||
use pv::{
|
||||
@@ -22,6 +21,8 @@ use pv::{
|
||||
use serde_yaml::Value;
|
||||
use utils::get_writer_from_cli_file_arg;
|
||||
|
||||
+use crate::cli::{AddSecretType, CreateSecretFlags, CreateSecretOpt, RetrieveableSecretInpKind};
|
||||
+
|
||||
fn write_out<P, D>(path: &P, data: D, ctx: &str) -> pv::Result<()>
|
||||
where
|
||||
P: AsRef<Path>,
|
||||
@@ -32,6 +33,23 @@ where
|
||||
Ok(())
|
||||
}
|
||||
|
||||
+fn retrievable(name: &str, secret: &str, kind: &RetrieveableSecretInpKind) -> Result<GuestSecret> {
|
||||
+ let secret_data = read_file(secret, &format!("retrievable {kind}"))?.into();
|
||||
+
|
||||
+ match kind {
|
||||
+ RetrieveableSecretInpKind::Plain => GuestSecret::plaintext(name, secret_data),
|
||||
+ RetrieveableSecretInpKind::Aes => GuestSecret::aes(name, secret_data),
|
||||
+ RetrieveableSecretInpKind::AesXts => GuestSecret::aes_xts(name, secret_data),
|
||||
+ RetrieveableSecretInpKind::HmacSha => GuestSecret::hmac_sha(name, secret_data),
|
||||
+ RetrieveableSecretInpKind::Ec => GuestSecret::ec(
|
||||
+ name,
|
||||
+ read_private_key(secret_data.value())
|
||||
+ .with_context(|| format!("Cannot read {secret} as {kind} from PEM or DER"))?,
|
||||
+ ),
|
||||
+ }
|
||||
+ .map_err(Error::from)
|
||||
+}
|
||||
+
|
||||
/// Prepare an add-secret request
|
||||
pub fn create(opt: &CreateSecretOpt) -> Result<()> {
|
||||
if pv_guest_bit_set() {
|
||||
@@ -88,6 +106,9 @@ fn build_asrcb(opt: &CreateSecretOpt) -> Result<AddSecretRequest> {
|
||||
input_secret: None,
|
||||
..
|
||||
} => GuestSecret::association(name, None)?,
|
||||
+ AddSecretType::Retrievable {
|
||||
+ name, secret, kind, ..
|
||||
+ } => retrievable(name, secret, kind)?,
|
||||
};
|
||||
trace!("AddSecret: {secret:x?}");
|
||||
|
||||
@@ -136,7 +157,9 @@ fn build_asrcb(opt: &CreateSecretOpt) -> Result<AddSecretRequest> {
|
||||
.as_ref()
|
||||
.map(|p| read_file(p, "User-signing key"))
|
||||
.transpose()?
|
||||
- .map(|buf| read_private_key(&buf))
|
||||
+ .map(|buf| {
|
||||
+ read_private_key(&buf).context("Cannot read {secret} as private key from PEM or DER")
|
||||
+ })
|
||||
.transpose()?;
|
||||
|
||||
if user_data.is_some() || user_key.is_some() {
|
||||
@@ -258,6 +281,9 @@ fn write_secret<P: AsRef<Path>>(
|
||||
write_out(path, guest_secret.confidential(), "Association secret")?
|
||||
}
|
||||
}
|
||||
+ AddSecretType::Retrievable { name, stdout, .. } => {
|
||||
+ write_yaml(name, guest_secret, stdout, outp_path)?
|
||||
+ }
|
||||
_ => (),
|
||||
};
|
||||
Ok(())
|
||||
diff --git a/rust/pvsecret/src/cmd/list.rs b/rust/pvsecret/src/cmd/list.rs
|
||||
index f7e3a72b..0bd9eca4 100644
|
||||
--- a/rust/pvsecret/src/cmd/list.rs
|
||||
+++ b/rust/pvsecret/src/cmd/list.rs
|
||||
@@ -3,21 +3,25 @@
|
||||
// Copyright IBM Corp. 2023
|
||||
|
||||
use crate::cli::{ListSecretOpt, ListSecretOutputType};
|
||||
-use anyhow::{Context, Result};
|
||||
+use anyhow::{Context, Error, Result};
|
||||
use log::warn;
|
||||
use pv::uv::{ListCmd, SecretList, UvDevice, UvcSuccess};
|
||||
use utils::{get_writer_from_cli_file_arg, STDOUT};
|
||||
|
||||
/// Do a List Secrets UVC
|
||||
-pub fn list(opt: &ListSecretOpt) -> Result<()> {
|
||||
- let uv = UvDevice::open()?;
|
||||
+pub fn list_uvc(uv: &UvDevice) -> Result<SecretList> {
|
||||
let mut cmd = ListCmd::default();
|
||||
match uv.send_cmd(&mut cmd)? {
|
||||
UvcSuccess::RC_SUCCESS => (),
|
||||
UvcSuccess::RC_MORE_DATA => warn!("There is more data available than expected"),
|
||||
};
|
||||
+ cmd.try_into().map_err(Error::new)
|
||||
+}
|
||||
|
||||
- let secret_list: SecretList = cmd.try_into()?;
|
||||
+/// Do a List Secrets UVC and output the list in the requested format
|
||||
+pub fn list(opt: &ListSecretOpt) -> Result<()> {
|
||||
+ let uv = UvDevice::open()?;
|
||||
+ let secret_list = list_uvc(&uv)?;
|
||||
let mut wr_out = get_writer_from_cli_file_arg(&opt.output)?;
|
||||
|
||||
match &opt.format {
|
||||
diff --git a/rust/pvsecret/src/cmd/retr.rs b/rust/pvsecret/src/cmd/retr.rs
|
||||
new file mode 100644
|
||||
index 00000000..7f7704cc
|
||||
--- /dev/null
|
||||
+++ b/rust/pvsecret/src/cmd/retr.rs
|
||||
@@ -0,0 +1,62 @@
|
||||
+// SPDX-License-Identifier: MIT
|
||||
+//
|
||||
+// Copyright IBM Corp. 2024
|
||||
+
|
||||
+use super::list::list_uvc;
|
||||
+use crate::cli::{RetrInpFmt, RetrOutFmt, RetrSecretOptions};
|
||||
+use anyhow::{anyhow, bail, Context, Result};
|
||||
+use log::{debug, info};
|
||||
+use pv::{
|
||||
+ misc::open_file,
|
||||
+ misc::write,
|
||||
+ secret::{GuestSecret, RetrievedSecret},
|
||||
+ uv::{RetrieveCmd, SecretId, UvDevice},
|
||||
+};
|
||||
+use utils::get_writer_from_cli_file_arg;
|
||||
+
|
||||
+fn retrieve(id: &SecretId) -> Result<RetrievedSecret> {
|
||||
+ let uv = UvDevice::open()?;
|
||||
+ let secrets = list_uvc(&uv)?;
|
||||
+ let secret = secrets
|
||||
+ .into_iter()
|
||||
+ .find(|s| s.id() == id.as_ref())
|
||||
+ .ok_or(anyhow!(
|
||||
+ "The UV secret-store has no secret with the ID {id}"
|
||||
+ ))?;
|
||||
+
|
||||
+ info!("Try to retrieve secret at index: {}", secret.index());
|
||||
+ debug!("Try to retrieve: {secret:?}");
|
||||
+
|
||||
+ let mut uv_cmd = RetrieveCmd::from_entry(secret)?;
|
||||
+ uv.send_cmd(&mut uv_cmd)?;
|
||||
+
|
||||
+ Ok(RetrievedSecret::from_cmd(uv_cmd))
|
||||
+}
|
||||
+
|
||||
+pub fn retr(opt: &RetrSecretOptions) -> Result<()> {
|
||||
+ let mut output = get_writer_from_cli_file_arg(&opt.output)?;
|
||||
+ let id = match &opt.inform {
|
||||
+ RetrInpFmt::Yaml => match serde_yaml::from_reader(&mut open_file(&opt.input)?)? {
|
||||
+ GuestSecret::Retrievable { id, .. } => id,
|
||||
+ gs => bail!("The file contains a {gs}-secret, which is not retrievable."),
|
||||
+ },
|
||||
+ RetrInpFmt::Hex => {
|
||||
+ serde_yaml::from_str(&opt.input).context("Cannot parse SecretId information")?
|
||||
+ }
|
||||
+ };
|
||||
+
|
||||
+ let retr_secret =
|
||||
+ retrieve(&id).context("Could not retrieve the secret from the UV secret store.")?;
|
||||
+
|
||||
+ let out_data = match opt.outform {
|
||||
+ RetrOutFmt::Bin => retr_secret.into_bytes(),
|
||||
+ RetrOutFmt::Pem => retr_secret.to_pem()?.into_bytes(),
|
||||
+ };
|
||||
+ write(
|
||||
+ &mut output,
|
||||
+ out_data.value(),
|
||||
+ &opt.output,
|
||||
+ "IBM Protected Key",
|
||||
+ )?;
|
||||
+ Ok(())
|
||||
+}
|
||||
diff --git a/rust/pvsecret/src/main.rs b/rust/pvsecret/src/main.rs
|
||||
index 502a6ea0..883a3ee2 100644
|
||||
--- a/rust/pvsecret/src/main.rs
|
||||
+++ b/rust/pvsecret/src/main.rs
|
||||
@@ -45,6 +45,7 @@ fn main() -> ExitCode {
|
||||
Command::Create(opt) => cmd::create(opt),
|
||||
Command::Version => Ok(print_version!("2024", log_level; FEATURES.concat())),
|
||||
Command::Verify(opt) => cmd::verify(opt),
|
||||
+ Command::Retrieve(opt) => cmd::retr(opt),
|
||||
};
|
||||
|
||||
match res {
|
@ -1,313 +0,0 @@
|
||||
From 256289a30aa5d3f6a4d2631dea69d1dc47205150 Mon Sep 17 00:00:00 2001
|
||||
From: Steffen Eiden <seiden@linux.ibm.com>
|
||||
Date: Wed, 12 Jun 2024 16:23:31 +0200
|
||||
Subject: [PATCH] rust/pv_core: Refactor secret list
|
||||
MIME-Version: 1.0
|
||||
Content-Type: text/plain; charset=UTF-8
|
||||
Content-Transfer-Encoding: 8bit
|
||||
|
||||
Improve the secret list implementation. Use structs+{As,From}Bytes
|
||||
instead of arbitrary seeks and reads/writes to parse the secret list.
|
||||
|
||||
Acked-by: Marc Hartmayer <marc@linux.ibm.com>
|
||||
Reviewed-by: Christoph Schlameuss <schlameuss@linux.ibm.com>
|
||||
Signed-off-by: Steffen Eiden <seiden@linux.ibm.com>
|
||||
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
|
||||
---
|
||||
rust/pv_core/src/uvdevice.rs | 10 ++
|
||||
rust/pv_core/src/uvdevice/secret_list.rs | 124 ++++++++++++++---------
|
||||
2 files changed, 86 insertions(+), 48 deletions(-)
|
||||
|
||||
diff --git a/rust/pv_core/src/uvdevice.rs b/rust/pv_core/src/uvdevice.rs
|
||||
index e9848243..e701366d 100644
|
||||
--- a/rust/pv_core/src/uvdevice.rs
|
||||
+++ b/rust/pv_core/src/uvdevice.rs
|
||||
@@ -163,6 +163,16 @@ pub enum UvcSuccess {
|
||||
RC_MORE_DATA = UvDevice::RC_MORE_DATA,
|
||||
}
|
||||
|
||||
+impl UvcSuccess {
|
||||
+ /// Returns true if there is more data available
|
||||
+ pub fn more_data(&self) -> bool {
|
||||
+ match self {
|
||||
+ Self::RC_SUCCESS => false,
|
||||
+ Self::RC_MORE_DATA => true,
|
||||
+ }
|
||||
+ }
|
||||
+}
|
||||
+
|
||||
/// The `UvDevice` is a (virtual) device on s390 machines to send Ultravisor commands(UVCs) from
|
||||
/// userspace.
|
||||
///
|
||||
diff --git a/rust/pv_core/src/uvdevice/secret_list.rs b/rust/pv_core/src/uvdevice/secret_list.rs
|
||||
index 4e955010..d7c268c9 100644
|
||||
--- a/rust/pv_core/src/uvdevice/secret_list.rs
|
||||
+++ b/rust/pv_core/src/uvdevice/secret_list.rs
|
||||
@@ -4,16 +4,16 @@
|
||||
|
||||
use crate::{
|
||||
assert_size,
|
||||
- misc::to_u16,
|
||||
uv::{AesSizes, AesXtsSizes, EcCurves, HmacShaSizes, ListCmd, RetrievableSecret},
|
||||
uvdevice::UvCmd,
|
||||
Error, Result,
|
||||
};
|
||||
-use byteorder::{BigEndian, ByteOrder, ReadBytesExt, WriteBytesExt};
|
||||
+use byteorder::{BigEndian, ByteOrder};
|
||||
use serde::{Deserialize, Serialize, Serializer};
|
||||
use std::{
|
||||
fmt::Display,
|
||||
io::{Cursor, Read, Seek, Write},
|
||||
+ mem::size_of,
|
||||
slice::Iter,
|
||||
vec::IntoIter,
|
||||
};
|
||||
@@ -31,7 +31,7 @@ impl SecretId {
|
||||
/// Size in bytes of the [`SecretId`]
|
||||
pub const ID_SIZE: usize = 32;
|
||||
|
||||
- /// Create a [`SecretId`] forom a buffer.
|
||||
+ /// Create a [`SecretId`] from a buffer.
|
||||
pub fn from(buf: [u8; Self::ID_SIZE]) -> Self {
|
||||
buf.into()
|
||||
}
|
||||
@@ -120,7 +120,7 @@ impl SecretEntry {
|
||||
&self.index
|
||||
}
|
||||
|
||||
- /// Returns the secret type of this [`SecretEntry`].
|
||||
+ /// Returns the secret type of this [`SecretEntry`]
|
||||
pub fn stype(&self) -> ListableSecretType {
|
||||
self.stype.get().into()
|
||||
}
|
||||
@@ -161,12 +161,45 @@ impl Display for SecretEntry {
|
||||
}
|
||||
}
|
||||
|
||||
+#[repr(C)]
|
||||
+#[derive(Debug, FromBytes, AsBytes, FromZeroes, Clone, PartialEq, Eq, Default, Serialize)]
|
||||
+struct SecretListHdr {
|
||||
+ #[serde(skip)]
|
||||
+ num_secrets_stored: U16<BigEndian>,
|
||||
+ #[serde(serialize_with = "ser_u16")]
|
||||
+ total_num_secrets: U16<BigEndian>,
|
||||
+ #[serde(skip)]
|
||||
+ next_secret_idx: U16<BigEndian>,
|
||||
+ #[serde(skip)]
|
||||
+ reserved_06: u16,
|
||||
+ #[serde(skip)]
|
||||
+ reserved_08: u64,
|
||||
+}
|
||||
+
|
||||
+impl SecretListHdr {
|
||||
+ fn new(num_secrets_stored: u16, total_num_secrets: u16, next_secret_idx: u16) -> Self {
|
||||
+ Self {
|
||||
+ num_secrets_stored: num_secrets_stored.into(),
|
||||
+ total_num_secrets: total_num_secrets.into(),
|
||||
+ next_secret_idx: next_secret_idx.into(),
|
||||
+ reserved_06: 0,
|
||||
+ reserved_08: 0,
|
||||
+ }
|
||||
+ }
|
||||
+}
|
||||
+assert_size!(SecretListHdr, 16);
|
||||
+
|
||||
/// List of secrets used to parse the [`crate::uv::ListCmd`] result.
|
||||
///
|
||||
-/// The list should not hold more than 0xffffffff elements
|
||||
-#[derive(Debug, PartialEq, Eq, Serialize)]
|
||||
+/// The list should ONLY be created from an UV-Call result using either:
|
||||
+/// - [`TryInto::try_into`] from [`ListCmd`]
|
||||
+/// - [`SecretList::decode`]
|
||||
+/// Any other ways can create invalid lists that do not represent the UV secret store.
|
||||
+/// The list must not hold more than [`u32::MAX`] elements
|
||||
+#[derive(Debug, PartialEq, Eq, Serialize, Default)]
|
||||
pub struct SecretList {
|
||||
- total_num_secrets: usize,
|
||||
+ #[serde(flatten)]
|
||||
+ hdr: SecretListHdr,
|
||||
secrets: Vec<SecretEntry>,
|
||||
}
|
||||
|
||||
@@ -202,10 +235,14 @@ impl SecretList {
|
||||
/// The content of this list will very likely not represent the status of the guest in the
|
||||
/// Ultravisor. Use of [`SecretList::decode`] in any non-test environments is encuraged.
|
||||
pub fn new(total_num_secrets: u16, secrets: Vec<SecretEntry>) -> Self {
|
||||
- Self {
|
||||
- total_num_secrets: total_num_secrets as usize,
|
||||
+ Self::new_with_hdr(
|
||||
+ SecretListHdr::new(total_num_secrets, total_num_secrets, 0),
|
||||
secrets,
|
||||
- }
|
||||
+ )
|
||||
+ }
|
||||
+
|
||||
+ fn new_with_hdr(hdr: SecretListHdr, secrets: Vec<SecretEntry>) -> Self {
|
||||
+ Self { hdr, secrets }
|
||||
}
|
||||
|
||||
/// Returns an iterator over the slice.
|
||||
@@ -229,19 +266,12 @@ impl SecretList {
|
||||
///
|
||||
/// This number may be not equal to the provided number of [`SecretEntry`]
|
||||
pub fn total_num_secrets(&self) -> usize {
|
||||
- self.total_num_secrets
|
||||
+ self.hdr.total_num_secrets.get() as usize
|
||||
}
|
||||
|
||||
/// Encodes the list in the same binary format the UV would do
|
||||
pub fn encode<T: Write>(&self, w: &mut T) -> Result<()> {
|
||||
- let num_s = to_u16(self.secrets.len()).ok_or(Error::ManySecrets)?;
|
||||
- w.write_u16::<BigEndian>(num_s)?;
|
||||
- w.write_u16::<BigEndian>(
|
||||
- self.total_num_secrets
|
||||
- .try_into()
|
||||
- .map_err(|_| Error::ManySecrets)?,
|
||||
- )?;
|
||||
- w.write_all(&[0u8; 12])?;
|
||||
+ w.write_all(self.hdr.as_bytes())?;
|
||||
for secret in &self.secrets {
|
||||
w.write_all(secret.as_bytes())?;
|
||||
}
|
||||
@@ -250,19 +280,20 @@ impl SecretList {
|
||||
|
||||
/// Decodes the list from the binary format of the UV into this internal representation
|
||||
pub fn decode<R: Read + Seek>(r: &mut R) -> std::io::Result<Self> {
|
||||
- let num_s = r.read_u16::<BigEndian>()?;
|
||||
- let total_num_secrets = r.read_u16::<BigEndian>()? as usize;
|
||||
- let mut v: Vec<SecretEntry> = Vec::with_capacity(num_s as usize);
|
||||
- r.seek(std::io::SeekFrom::Current(12))?; // skip reserved bytes
|
||||
+ let mut buf = [0u8; size_of::<SecretListHdr>()];
|
||||
+ r.read_exact(&mut buf)?;
|
||||
+ let hdr = SecretListHdr::ref_from(&buf).unwrap();
|
||||
+
|
||||
let mut buf = [0u8; SecretEntry::STRUCT_SIZE];
|
||||
- for _ in 0..num_s {
|
||||
+ let mut v = Vec::with_capacity(hdr.num_secrets_stored.get() as usize);
|
||||
+ for _ in 0..hdr.num_secrets_stored.get() {
|
||||
r.read_exact(&mut buf)?;
|
||||
// cannot fail. buffer has the same size as the secret entry
|
||||
let secr = SecretEntry::read_from(buf.as_slice()).unwrap();
|
||||
v.push(secr);
|
||||
}
|
||||
Ok(Self {
|
||||
- total_num_secrets,
|
||||
+ hdr: hdr.clone(),
|
||||
secrets: v,
|
||||
})
|
||||
}
|
||||
@@ -278,7 +309,7 @@ impl TryFrom<ListCmd> for SecretList {
|
||||
|
||||
impl Display for SecretList {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
- writeln!(f, "Total number of secrets: {}", self.total_num_secrets)?;
|
||||
+ writeln!(f, "Total number of secrets: {}", self.total_num_secrets())?;
|
||||
if !self.secrets.is_empty() {
|
||||
writeln!(f)?;
|
||||
}
|
||||
@@ -481,8 +512,8 @@ mod test {
|
||||
let buf = [
|
||||
0x00u8, 0x01, // num secr stored
|
||||
0x01, 0x12, // total num secrets
|
||||
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
- 0x00, // reserved
|
||||
+ 0x01, 0x01, // next valid idx
|
||||
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // reserved
|
||||
// secret
|
||||
0x00, 0x01, 0x00, 0x02, // idx + type
|
||||
0x00, 0x00, 0x00, 0x20, // len
|
||||
@@ -493,16 +524,16 @@ mod test {
|
||||
0x00, 0x00, 0x00, 0x00,
|
||||
];
|
||||
|
||||
- let exp = SecretList {
|
||||
- total_num_secrets: 0x112,
|
||||
- secrets: vec![SecretEntry {
|
||||
+ let exp = SecretList::new_with_hdr(
|
||||
+ SecretListHdr::new(0x001, 0x112, 0x101),
|
||||
+ vec![SecretEntry {
|
||||
index: 1.into(),
|
||||
stype: 2.into(),
|
||||
len: 32.into(),
|
||||
res_8: 0,
|
||||
id: SecretId::from([0; 32]),
|
||||
}],
|
||||
- };
|
||||
+ );
|
||||
|
||||
let mut br = BufReader::new(Cursor::new(buf));
|
||||
let sl = SecretList::decode(&mut br).unwrap();
|
||||
@@ -514,8 +545,8 @@ mod test {
|
||||
const EXP: &[u8] = &[
|
||||
0x00, 0x01, // num secr stored
|
||||
0x01, 0x12, // total num secrets
|
||||
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
- 0x00, // reserved
|
||||
+ 0x01, 0x01, // next valid idx
|
||||
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // reserved
|
||||
// secret
|
||||
0x00, 0x01, 0x00, 0x02, // idx + type
|
||||
0x00, 0x00, 0x00, 0x20, // len
|
||||
@@ -526,16 +557,16 @@ mod test {
|
||||
0x00, 0x00, 0x00, 0x00,
|
||||
];
|
||||
|
||||
- let sl = SecretList {
|
||||
- total_num_secrets: 0x112,
|
||||
- secrets: vec![SecretEntry {
|
||||
+ let sl = SecretList::new_with_hdr(
|
||||
+ SecretListHdr::new(0x001, 0x112, 0x101),
|
||||
+ vec![SecretEntry {
|
||||
index: 1.into(),
|
||||
stype: 2.into(),
|
||||
len: 32.into(),
|
||||
res_8: 0,
|
||||
id: SecretId::from([0; 32]),
|
||||
}],
|
||||
- };
|
||||
+ );
|
||||
|
||||
let mut buf = [0u8; 0x40];
|
||||
{
|
||||
@@ -587,26 +618,23 @@ mod test {
|
||||
|
||||
#[test]
|
||||
fn secret_list_ser() {
|
||||
- let list = SecretList {
|
||||
- total_num_secrets: 0x112,
|
||||
- secrets: vec![SecretEntry {
|
||||
+ let list = SecretList::new_with_hdr(
|
||||
+ SecretListHdr::new(0x001, 0x112, 0x101),
|
||||
+ vec![SecretEntry {
|
||||
index: 1.into(),
|
||||
stype: 2.into(),
|
||||
len: 32.into(),
|
||||
res_8: 0,
|
||||
id: SecretId::from([0; 32]),
|
||||
}],
|
||||
- };
|
||||
+ );
|
||||
|
||||
assert_ser_tokens(
|
||||
&list,
|
||||
&[
|
||||
- Token::Struct {
|
||||
- name: "SecretList",
|
||||
- len: 2,
|
||||
- },
|
||||
+ Token::Map { len: None },
|
||||
Token::String("total_num_secrets"),
|
||||
- Token::U64(0x112),
|
||||
+ Token::U16(0x112),
|
||||
Token::String("secrets"),
|
||||
Token::Seq { len: Some(1) },
|
||||
Token::Struct {
|
||||
@@ -623,7 +651,7 @@ mod test {
|
||||
Token::String("0x0000000000000000000000000000000000000000000000000000000000000000"),
|
||||
Token::StructEnd,
|
||||
Token::SeqEnd,
|
||||
- Token::StructEnd,
|
||||
+ Token::MapEnd,
|
||||
],
|
||||
)
|
||||
}
|
@ -1,111 +0,0 @@
|
||||
From 93216d916c479ee1292aa1d598ac9c0e7f585bd8 Mon Sep 17 00:00:00 2001
|
||||
From: Steffen Eiden <seiden@linux.ibm.com>
|
||||
Date: Wed, 12 Jun 2024 16:35:15 +0200
|
||||
Subject: [PATCH] rust/pv*: Support longer secret lists
|
||||
MIME-Version: 1.0
|
||||
Content-Type: text/plain; charset=UTF-8
|
||||
Content-Transfer-Encoding: 8bit
|
||||
|
||||
Make use of the enhanced list secrets UAPI for the uvdevice in the latest kernel
|
||||
version. This allows fetching secret lists with more than 85 entries via
|
||||
reserving more userspace memory in the IOCTL argument.
|
||||
|
||||
While at it, move the errno readout next to the ioctl-syscall.
|
||||
|
||||
Acked-by: Marc Hartmayer <marc@linux.ibm.com>
|
||||
Reviewed-by: Christoph Schlameuss <schlameuss@linux.ibm.com>
|
||||
Signed-off-by: Steffen Eiden <seiden@linux.ibm.com>
|
||||
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
|
||||
---
|
||||
rust/pv_core/src/uvdevice.rs | 6 ++++--
|
||||
rust/pv_core/src/uvdevice/secret.rs | 11 +++++++++++
|
||||
rust/pvsecret/src/cmd/list.rs | 28 +++++++++++++++++++++-------
|
||||
3 files changed, 36 insertions(+), 9 deletions(-)
|
||||
|
||||
diff --git a/rust/pv_core/src/uvdevice.rs b/rust/pv_core/src/uvdevice.rs
|
||||
index e701366d..689748a1 100644
|
||||
--- a/rust/pv_core/src/uvdevice.rs
|
||||
+++ b/rust/pv_core/src/uvdevice.rs
|
||||
@@ -59,11 +59,13 @@ fn ioctl_raw(raw_fd: RawFd, cmd: c_ulong, cb: &mut IoctlCb) -> Result<()> {
|
||||
rc = ioctl(raw_fd, cmd, cb.as_ptr_mut());
|
||||
}
|
||||
|
||||
+ // NOTE io::Error handles all errnos ioctl uses
|
||||
+ let errno = std::io::Error::last_os_error();
|
||||
+
|
||||
debug!("ioctl resulted with {cb:?}");
|
||||
match rc {
|
||||
0 => Ok(()),
|
||||
- // NOTE io::Error handles all errnos ioctl uses
|
||||
- _ => Err(std::io::Error::last_os_error().into()),
|
||||
+ _ => Err(errno.into()),
|
||||
}
|
||||
}
|
||||
|
||||
diff --git a/rust/pv_core/src/uvdevice/secret.rs b/rust/pv_core/src/uvdevice/secret.rs
|
||||
index 263f17d5..cb5b7233 100644
|
||||
--- a/rust/pv_core/src/uvdevice/secret.rs
|
||||
+++ b/rust/pv_core/src/uvdevice/secret.rs
|
||||
@@ -24,6 +24,17 @@ impl ListCmd {
|
||||
Self(vec![0; size])
|
||||
}
|
||||
|
||||
+ /// Create a new list secrets command with `pages` capacity.
|
||||
+ ///
|
||||
+ /// * `pages` - number pf pages to allocate for this IOCTL
|
||||
+ ///
|
||||
+ /// # Panic
|
||||
+ /// This function will trigger a panic if the allocation size is larger than [`usize::MAX`].
|
||||
+ /// Very likely an OOM situation occurs way before this!
|
||||
+ pub fn with_pages(pages: usize) -> Self {
|
||||
+ Self::with_size(pages * PAGESIZE)
|
||||
+ }
|
||||
+
|
||||
/// Create a new list secrets command with a one page capacity
|
||||
pub fn new() -> Self {
|
||||
Self::with_size(PAGESIZE)
|
||||
diff --git a/rust/pvsecret/src/cmd/list.rs b/rust/pvsecret/src/cmd/list.rs
|
||||
index 0bd9eca4..56294cac 100644
|
||||
--- a/rust/pvsecret/src/cmd/list.rs
|
||||
+++ b/rust/pvsecret/src/cmd/list.rs
|
||||
@@ -2,19 +2,33 @@
|
||||
//
|
||||
// Copyright IBM Corp. 2023
|
||||
|
||||
+use std::io::ErrorKind;
|
||||
+
|
||||
use crate::cli::{ListSecretOpt, ListSecretOutputType};
|
||||
use anyhow::{Context, Error, Result};
|
||||
-use log::warn;
|
||||
-use pv::uv::{ListCmd, SecretList, UvDevice, UvcSuccess};
|
||||
+use log::{info, warn};
|
||||
+use pv::uv::{ListCmd, SecretList, UvDevice};
|
||||
use utils::{get_writer_from_cli_file_arg, STDOUT};
|
||||
|
||||
+const SECRET_LIST_BUF_SIZE: usize = 4;
|
||||
+
|
||||
/// Do a List Secrets UVC
|
||||
pub fn list_uvc(uv: &UvDevice) -> Result<SecretList> {
|
||||
- let mut cmd = ListCmd::default();
|
||||
- match uv.send_cmd(&mut cmd)? {
|
||||
- UvcSuccess::RC_SUCCESS => (),
|
||||
- UvcSuccess::RC_MORE_DATA => warn!("There is more data available than expected"),
|
||||
- };
|
||||
+ let mut cmd = ListCmd::with_pages(SECRET_LIST_BUF_SIZE);
|
||||
+ let more_data = match uv.send_cmd(&mut cmd) {
|
||||
+ Ok(v) => Ok(v),
|
||||
+ Err(pv::PvCoreError::Io(e)) if e.kind() == ErrorKind::InvalidInput => {
|
||||
+ info!("Uvdevice does not suport longer list. Fallback to one page list.");
|
||||
+ cmd = ListCmd::default();
|
||||
+ uv.send_cmd(&mut cmd)
|
||||
+ }
|
||||
+ Err(e) => Err(e),
|
||||
+ }?
|
||||
+ .more_data();
|
||||
+ if more_data {
|
||||
+ warn!("The secret list contains more data but the uvdevice cannot show all.");
|
||||
+ }
|
||||
+
|
||||
cmd.try_into().map_err(Error::new)
|
||||
}
|
||||
|
@ -1,387 +0,0 @@
|
||||
From ff04f76257791593c8f92374f295a0c478e3b0f7 Mon Sep 17 00:00:00 2001
|
||||
From: Steffen Eiden <seiden@linux.ibm.com>
|
||||
Date: Mon, 5 Aug 2024 09:34:47 +0200
|
||||
Subject: [PATCH] rust/pv*: Allow the use of non-hashes secret IDs
|
||||
MIME-Version: 1.0
|
||||
Content-Type: text/plain; charset=UTF-8
|
||||
Content-Transfer-Encoding: 8bit
|
||||
|
||||
Secret IDs identify a secret in the store. Tooling (pvsecret) calculates
|
||||
them by hashing a user-defined string. With this patch it is now
|
||||
possible to skip the hash step and directly use the input string as the
|
||||
ID. Up to the first 31 bytes of the input ASCII-string are used. The last byte
|
||||
is the NUL char. During list pvsecret tries to interpret the secret
|
||||
as ASCII string and if possible displays the ASCII characters alongside
|
||||
the hex number.
|
||||
|
||||
Also, use the Upper/Lower Hex formatters for the hexstring formatting of
|
||||
SecretId. Display will, additionally show the ASCII representation if
|
||||
applicable.
|
||||
|
||||
While at it, use Self wherever possible.
|
||||
|
||||
Acked-by: Marc Hartmayer <marc@linux.ibm.com>
|
||||
Reviewed-by: Christoph Schlameuss <schlameuss@linux.ibm.com>
|
||||
Signed-off-by: Steffen Eiden <seiden@linux.ibm.com>
|
||||
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
|
||||
---
|
||||
rust/pv/src/uvsecret/guest_secret.rs | 16 ++-
|
||||
rust/pv_core/src/uvdevice/secret_list.rs | 163 ++++++++++++++++++++---
|
||||
rust/pvsecret/src/cli.rs | 8 ++
|
||||
rust/pvsecret/src/cmd/create.rs | 4 +-
|
||||
rust/pvsecret/src/cmd/retr.rs | 14 +-
|
||||
5 files changed, 184 insertions(+), 21 deletions(-)
|
||||
|
||||
diff --git a/rust/pv/src/uvsecret/guest_secret.rs b/rust/pv/src/uvsecret/guest_secret.rs
|
||||
index 3bad6d3c..87b58bb8 100644
|
||||
--- a/rust/pv/src/uvsecret/guest_secret.rs
|
||||
+++ b/rust/pv/src/uvsecret/guest_secret.rs
|
||||
@@ -92,7 +92,8 @@ macro_rules! retr_constructor {
|
||||
}
|
||||
|
||||
impl GuestSecret {
|
||||
- fn name_to_id(name: &str) -> Result<SecretId> {
|
||||
+ /// Hashes the name with sha256
|
||||
+ pub fn name_to_id(name: &str) -> Result<SecretId> {
|
||||
let id: [u8; SecretId::ID_SIZE] = hash(MessageDigest::sha256(), name.as_bytes())?
|
||||
.to_vec()
|
||||
.try_into()
|
||||
@@ -135,6 +136,19 @@ impl GuestSecret {
|
||||
retr_constructor!(#[doc = r"This function will return an error if OpenSSL cannot create a hash or the curve is invalid"]
|
||||
| #[doc = r"EC PRIVATE Key"] => PKey<Private>, ec);
|
||||
|
||||
+ /// Use the name as ID, do not hash it
|
||||
+ pub fn no_hash_name(&mut self) {
|
||||
+ match self {
|
||||
+ Self::Null => (),
|
||||
+ Self::Association {
|
||||
+ name, ref mut id, ..
|
||||
+ }
|
||||
+ | Self::Retrievable {
|
||||
+ name, ref mut id, ..
|
||||
+ } => id.clone_from(&SecretId::from_string(name)),
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
/// Reference to the confidential data
|
||||
pub fn confidential(&self) -> &[u8] {
|
||||
match &self {
|
||||
diff --git a/rust/pv_core/src/uvdevice/secret_list.rs b/rust/pv_core/src/uvdevice/secret_list.rs
|
||||
index d7c268c9..7c7e63b5 100644
|
||||
--- a/rust/pv_core/src/uvdevice/secret_list.rs
|
||||
+++ b/rust/pv_core/src/uvdevice/secret_list.rs
|
||||
@@ -11,7 +11,9 @@ use crate::{
|
||||
use byteorder::{BigEndian, ByteOrder};
|
||||
use serde::{Deserialize, Serialize, Serializer};
|
||||
use std::{
|
||||
- fmt::Display,
|
||||
+ cmp::min,
|
||||
+ ffi::CStr,
|
||||
+ fmt::{Debug, Display, LowerHex, UpperHex},
|
||||
io::{Cursor, Read, Seek, Write},
|
||||
mem::size_of,
|
||||
slice::Iter,
|
||||
@@ -35,6 +37,33 @@ impl SecretId {
|
||||
pub fn from(buf: [u8; Self::ID_SIZE]) -> Self {
|
||||
buf.into()
|
||||
}
|
||||
+
|
||||
+ /// Create a Id from a string
|
||||
+ ///
|
||||
+ /// Uses the first 31 bytes from `name` as id
|
||||
+ /// Does not hash anything. Byte 32 is the NUL char
|
||||
+ pub fn from_string(name: &str) -> Self {
|
||||
+ let len = min(name.len(), Self::ID_SIZE - 1);
|
||||
+ let mut res = Self::default();
|
||||
+ res.0[0..len].copy_from_slice(&name.as_bytes()[0..len]);
|
||||
+ res
|
||||
+ }
|
||||
+
|
||||
+ /// Tries to represent the Id as printable-ASCII string
|
||||
+ pub fn as_ascii(&self) -> Option<&str> {
|
||||
+ if let Ok(t) = CStr::from_bytes_until_nul(&self.0) {
|
||||
+ if let Ok(t) = t.to_str() {
|
||||
+ if !t.is_empty()
|
||||
+ && t.chars()
|
||||
+ .all(|c| c.is_ascii_whitespace() | c.is_ascii_graphic())
|
||||
+ && self.0[t.len()..].iter().all(|b| *b == 0)
|
||||
+ {
|
||||
+ return Some(t);
|
||||
+ }
|
||||
+ }
|
||||
+ };
|
||||
+ None
|
||||
+ }
|
||||
}
|
||||
|
||||
impl Serialize for SecretId {
|
||||
@@ -42,8 +71,8 @@ impl Serialize for SecretId {
|
||||
where
|
||||
S: Serializer,
|
||||
{
|
||||
- // calls Display at one point
|
||||
- ser.serialize_str(&self.to_string())
|
||||
+ // calls LowerHex at one point
|
||||
+ ser.serialize_str(&format!("{self:#x}"))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -56,12 +85,36 @@ impl<'de> Deserialize<'de> for SecretId {
|
||||
}
|
||||
}
|
||||
|
||||
+impl UpperHex for SecretId {
|
||||
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
+ if f.alternate() {
|
||||
+ write!(f, "0x")?;
|
||||
+ }
|
||||
+ for b in self.0 {
|
||||
+ write!(f, "{b:02X}")?;
|
||||
+ }
|
||||
+ Ok(())
|
||||
+ }
|
||||
+}
|
||||
+
|
||||
+impl LowerHex for SecretId {
|
||||
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
+ if f.alternate() {
|
||||
+ write!(f, "0x")?;
|
||||
+ }
|
||||
+ for b in self.0 {
|
||||
+ write!(f, "{b:02x}")?;
|
||||
+ }
|
||||
+ Ok(())
|
||||
+ }
|
||||
+}
|
||||
+
|
||||
impl Display for SecretId {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
- let mut s = String::with_capacity(32 * 2 + 2);
|
||||
- s.push_str("0x");
|
||||
- let s = self.0.iter().fold(s, |acc, e| acc + &format!("{e:02x}"));
|
||||
- write!(f, "{s}")
|
||||
+ if let Some(s) = self.as_ascii() {
|
||||
+ write!(f, "{s} | ")?;
|
||||
+ }
|
||||
+ write!(f, "{self:#x}")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -79,7 +132,7 @@ impl AsRef<[u8]> for SecretId {
|
||||
|
||||
/// A secret in a [`SecretList`]
|
||||
#[repr(C)]
|
||||
-#[derive(Debug, PartialEq, Eq, AsBytes, FromZeroes, FromBytes, Serialize)]
|
||||
+#[derive(Debug, Clone, PartialEq, Eq, AsBytes, FromZeroes, FromBytes, Serialize)]
|
||||
pub struct SecretEntry {
|
||||
#[serde(serialize_with = "ser_u16")]
|
||||
index: U16<BigEndian>,
|
||||
@@ -153,11 +206,7 @@ impl Display for SecretEntry {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
let stype: ListableSecretType = self.stype.get().into();
|
||||
writeln!(f, "{} {}:", self.index, stype)?;
|
||||
- write!(f, " ")?;
|
||||
- for b in self.id.as_ref() {
|
||||
- write!(f, "{b:02x}")?;
|
||||
- }
|
||||
- Ok(())
|
||||
+ write!(f, " {}", self.id)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -269,6 +318,11 @@ impl SecretList {
|
||||
self.hdr.total_num_secrets.get() as usize
|
||||
}
|
||||
|
||||
+ /// Find the first [`SecretEntry`] that has the provided [`SecretId`]
|
||||
+ pub fn find(&self, id: &SecretId) -> Option<SecretEntry> {
|
||||
+ self.iter().find(|e| e.id() == id.as_ref()).cloned()
|
||||
+ }
|
||||
+
|
||||
/// Encodes the list in the same binary format the UV would do
|
||||
pub fn encode<T: Write>(&self, w: &mut T) -> Result<()> {
|
||||
w.write_all(self.hdr.as_bytes())?;
|
||||
@@ -456,7 +510,7 @@ where
|
||||
type Value = [u8; SecretId::ID_SIZE];
|
||||
|
||||
fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
|
||||
- formatter.write_str("a `32 bytes long hexstring` prepended with 0x")
|
||||
+ formatter.write_str("a `32 bytes (=64 character) long hexstring` prepended with 0x")
|
||||
}
|
||||
|
||||
fn visit_str<E>(self, s: &str) -> Result<Self::Value, E>
|
||||
@@ -464,7 +518,10 @@ where
|
||||
E: serde::de::Error,
|
||||
{
|
||||
if s.len() != SecretId::ID_SIZE * 2 + "0x".len() {
|
||||
- return Err(serde::de::Error::invalid_length(s.len() - 2, &self));
|
||||
+ return Err(serde::de::Error::invalid_length(
|
||||
+ s.len().saturating_sub("0x".len()),
|
||||
+ &self,
|
||||
+ ));
|
||||
}
|
||||
let nb = s.strip_prefix("0x").ok_or_else(|| {
|
||||
serde::de::Error::invalid_value(serde::de::Unexpected::Str(s), &self)
|
||||
@@ -655,4 +712,80 @@ mod test {
|
||||
],
|
||||
)
|
||||
}
|
||||
+
|
||||
+ #[test]
|
||||
+ fn secret_id_display() {
|
||||
+ let text = "Fancy secret ID";
|
||||
+ let id = SecretId::from_string(text);
|
||||
+
|
||||
+ let exp =
|
||||
+ "Fancy secret ID | 0x46616e6379207365637265742049440000000000000000000000000000000000";
|
||||
+ assert_eq!(id.to_string(), exp);
|
||||
+ }
|
||||
+
|
||||
+ #[test]
|
||||
+ fn secret_id_long_name() {
|
||||
+ let text = "the most fanciest secret ID you ever seen in the time the universe exists";
|
||||
+ let id = SecretId::from_string(text);
|
||||
+ let exp =
|
||||
+ "the most fanciest secret ID you | 0x746865206d6f73742066616e63696573742073656372657420494420796f7500";
|
||||
+ assert_eq!(id.to_string(), exp);
|
||||
+ }
|
||||
+
|
||||
+ #[test]
|
||||
+ fn secret_id_no_ascii_name() {
|
||||
+ let text = [0; 32];
|
||||
+ let id = SecretId::from(text);
|
||||
+
|
||||
+ let exp = "0x0000000000000000000000000000000000000000000000000000000000000000";
|
||||
+ assert_eq!(id.to_string(), exp);
|
||||
+ }
|
||||
+
|
||||
+ #[test]
|
||||
+ fn secret_id_no_ascii_name2() {
|
||||
+ let text = [
|
||||
+ 0x25, 0x55, 3, 4, 50, 0, 6, 0, 8, 0, 0, 0, 0, 0, 0, 0, 90, 0, 0xa, 0, 0, 0, 0, 0xf, 0,
|
||||
+ 0, 0, 0, 0, 0, 0, 0,
|
||||
+ ];
|
||||
+ let id = SecretId::from(text);
|
||||
+ assert_eq!(id.as_ascii(), None);
|
||||
+ }
|
||||
+
|
||||
+ #[test]
|
||||
+ fn secret_id_no_ascii_name3() {
|
||||
+ let text = [
|
||||
+ 0x25, 0x55, 0, 4, 50, 0, 6, 0, 8, 0, 0, 0, 0, 0, 0, 0, 90, 0, 0xa, 0, 0, 0, 0, 0xf, 0,
|
||||
+ 0, 0, 0, 0, 0, 0, 0,
|
||||
+ ];
|
||||
+ let id = SecretId::from(text);
|
||||
+ assert_eq!(id.as_ascii(), None);
|
||||
+ }
|
||||
+
|
||||
+ #[test]
|
||||
+ fn secret_id_hex() {
|
||||
+ let id_str = "Nice Test 123";
|
||||
+ let id = SecretId::from_string(id_str);
|
||||
+
|
||||
+ let s = format!("{id:#x}");
|
||||
+ assert_eq!(
|
||||
+ s,
|
||||
+ "0x4e69636520546573742031323300000000000000000000000000000000000000"
|
||||
+ );
|
||||
+ let s = format!("{id:x}");
|
||||
+ assert_eq!(
|
||||
+ s,
|
||||
+ "4e69636520546573742031323300000000000000000000000000000000000000"
|
||||
+ );
|
||||
+ let s = format!("{id:#X}");
|
||||
+ assert_eq!(
|
||||
+ s,
|
||||
+ "0x4E69636520546573742031323300000000000000000000000000000000000000"
|
||||
+ );
|
||||
+
|
||||
+ let s = format!("{id:X}");
|
||||
+ assert_eq!(
|
||||
+ s,
|
||||
+ "4E69636520546573742031323300000000000000000000000000000000000000"
|
||||
+ );
|
||||
+ }
|
||||
}
|
||||
diff --git a/rust/pvsecret/src/cli.rs b/rust/pvsecret/src/cli.rs
|
||||
index 4e747682..d858fc29 100644
|
||||
--- a/rust/pvsecret/src/cli.rs
|
||||
+++ b/rust/pvsecret/src/cli.rs
|
||||
@@ -141,6 +141,12 @@ pub struct CreateSecretOpt {
|
||||
/// by default.
|
||||
#[arg(long, value_name = "FILE", value_hint = ValueHint::FilePath,)]
|
||||
pub user_sign_key: Option<String>,
|
||||
+
|
||||
+ /// Do not hash the name, use it directly as secret ID.
|
||||
+ ///
|
||||
+ /// Ignored for meta-secrets.
|
||||
+ #[arg(long)]
|
||||
+ pub use_name: bool,
|
||||
}
|
||||
|
||||
#[derive(Subcommand, Debug)]
|
||||
@@ -342,6 +348,8 @@ pub enum RetrInpFmt {
|
||||
Yaml,
|
||||
/// Use a hex string.
|
||||
Hex,
|
||||
+ /// Use a name-string. Will hash it if no secret with the name found.
|
||||
+ Name,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, ValueEnum, Debug, Default)]
|
||||
diff --git a/rust/pvsecret/src/cmd/create.rs b/rust/pvsecret/src/cmd/create.rs
|
||||
index 73089a12..fab37e67 100644
|
||||
--- a/rust/pvsecret/src/cmd/create.rs
|
||||
+++ b/rust/pvsecret/src/cmd/create.rs
|
||||
@@ -94,7 +94,7 @@ fn read_private_key(buf: &[u8]) -> Result<PKey<Private>> {
|
||||
fn build_asrcb(opt: &CreateSecretOpt) -> Result<AddSecretRequest> {
|
||||
debug!("Build add-secret request");
|
||||
|
||||
- let secret = match &opt.secret {
|
||||
+ let mut secret = match &opt.secret {
|
||||
AddSecretType::Meta => GuestSecret::Null,
|
||||
AddSecretType::Association {
|
||||
name,
|
||||
@@ -112,6 +112,8 @@ fn build_asrcb(opt: &CreateSecretOpt) -> Result<AddSecretRequest> {
|
||||
};
|
||||
trace!("AddSecret: {secret:x?}");
|
||||
|
||||
+ opt.use_name.then(|| secret.no_hash_name());
|
||||
+
|
||||
let mut flags = match &opt.pcf {
|
||||
Some(v) => (&try_parse_u64(v, "pcf")?).into(),
|
||||
None => AddSecretFlags::default(),
|
||||
diff --git a/rust/pvsecret/src/cmd/retr.rs b/rust/pvsecret/src/cmd/retr.rs
|
||||
index 7f7704cc..ad3e91c3 100644
|
||||
--- a/rust/pvsecret/src/cmd/retr.rs
|
||||
+++ b/rust/pvsecret/src/cmd/retr.rs
|
||||
@@ -17,12 +17,17 @@ use utils::get_writer_from_cli_file_arg;
|
||||
fn retrieve(id: &SecretId) -> Result<RetrievedSecret> {
|
||||
let uv = UvDevice::open()?;
|
||||
let secrets = list_uvc(&uv)?;
|
||||
- let secret = secrets
|
||||
- .into_iter()
|
||||
- .find(|s| s.id() == id.as_ref())
|
||||
+ let secret = match secrets.find(id) {
|
||||
+ Some(s) => s,
|
||||
+ // hash it + try again if it is ASCII-representable
|
||||
+ None => match id.as_ascii() {
|
||||
+ Some(s) => secrets.find(&GuestSecret::name_to_id(s)?),
|
||||
+ None => None,
|
||||
+ }
|
||||
.ok_or(anyhow!(
|
||||
"The UV secret-store has no secret with the ID {id}"
|
||||
- ))?;
|
||||
+ ))?,
|
||||
+ };
|
||||
|
||||
info!("Try to retrieve secret at index: {}", secret.index());
|
||||
debug!("Try to retrieve: {secret:?}");
|
||||
@@ -43,6 +48,7 @@ pub fn retr(opt: &RetrSecretOptions) -> Result<()> {
|
||||
RetrInpFmt::Hex => {
|
||||
serde_yaml::from_str(&opt.input).context("Cannot parse SecretId information")?
|
||||
}
|
||||
+ RetrInpFmt::Name => SecretId::from_string(&opt.input),
|
||||
};
|
||||
|
||||
let retr_secret =
|
File diff suppressed because it is too large
Load Diff
@ -1,334 +0,0 @@
|
||||
From cf51ac786095f2a1a17d04fea9ee73271438d247 Mon Sep 17 00:00:00 2001
|
||||
From: Marc Hartmayer <mhartmay@linux.ibm.com>
|
||||
Date: Wed, 11 Dec 2024 19:25:59 +0100
|
||||
Subject: [PATCH] rust/pvimg: Add '--(enable|disable)-image-encryption' flags
|
||||
to 'pvimg create'
|
||||
MIME-Version: 1.0
|
||||
Content-Type: text/plain; charset=UTF-8
|
||||
Content-Transfer-Encoding: 8bit
|
||||
|
||||
With runtime attestation it might be useful to have non-encrypted Secure
|
||||
Execution images. This patch adds the support for this to the 'pvimg
|
||||
create' and 'genprotimg' commands.
|
||||
|
||||
Reviewed-by: Steffen Eiden <seiden@linux.ibm.com>
|
||||
Acked-by: Hendrik Brueckner <brueckner@linux.ibm.com>
|
||||
Signed-off-by: Marc Hartmayer <mhartmay@linux.ibm.com>
|
||||
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
|
||||
---
|
||||
rust/pvimg/man/genprotimg.1 | 26 +++++++++++++++++++++-----
|
||||
rust/pvimg/man/pvimg-create.1 | 26 +++++++++++++++++++++-----
|
||||
rust/pvimg/man/pvimg-info.1 | 10 +++++-----
|
||||
rust/pvimg/man/pvimg-test.1 | 10 +++++-----
|
||||
rust/pvimg/man/pvimg.1 | 10 +++++-----
|
||||
rust/pvimg/src/cli.rs | 18 ++++++++++++++++++
|
||||
rust/pvimg/src/cmd/create.rs | 10 ++++++++++
|
||||
7 files changed, 85 insertions(+), 25 deletions(-)
|
||||
|
||||
diff --git a/rust/pvimg/man/genprotimg.1 b/rust/pvimg/man/genprotimg.1
|
||||
index 46a91aa4..3f4949e9 100644
|
||||
--- a/rust/pvimg/man/genprotimg.1
|
||||
+++ b/rust/pvimg/man/genprotimg.1
|
||||
@@ -3,11 +3,11 @@
|
||||
.\" it under the terms of the MIT license. See LICENSE for details.
|
||||
.\"
|
||||
|
||||
-.TH genprotimg 1 "2024-12-05" "s390-tools" "Genprotimg Manual"
|
||||
+.TH genprotimg 1 "2024-12-11" "s390-tools" "Genprotimg Manual"
|
||||
.nh
|
||||
.ad l
|
||||
.SH NAME
|
||||
-\fBgenprotimg\fP - Create an IBM Secure Execution image
|
||||
+\fBgenprotimg\fP \- Create an IBM Secure Execution image
|
||||
\fB
|
||||
.SH SYNOPSIS
|
||||
.nf
|
||||
@@ -196,6 +196,22 @@ Disable the support for backup target keys (default).
|
||||
.RE
|
||||
.RE
|
||||
.PP
|
||||
+\-\-enable\-image\-encryption
|
||||
+.RS 4
|
||||
+Enable encryption of the image components (default). The image components are:
|
||||
+the kernel, ramdisk, and kernel command line.
|
||||
+.RE
|
||||
+.RE
|
||||
+.PP
|
||||
+\-\-disable\-image\-encryption
|
||||
+.RS 4
|
||||
+Disable encryption of the image components. The image components are: the
|
||||
+kernel, ramdisk, and kernel command line. Use only if the components used do not
|
||||
+contain any confidential content (for example, secrets like non\-public
|
||||
+cryptographic keys).
|
||||
+.RE
|
||||
+.RE
|
||||
+.PP
|
||||
\-v, \-\-verbose
|
||||
.RS 4
|
||||
Provide more detailed output.
|
||||
@@ -222,16 +238,16 @@ Print help (see a summary with \fB\-h\fR).
|
||||
|
||||
.SH EXIT STATUS
|
||||
.TP 8
|
||||
-.B 0 - Program finished successfully
|
||||
+.B 0 \- Program finished successfully
|
||||
The command was executed successfully.
|
||||
.RE
|
||||
.TP 8
|
||||
-.B 1 - Generic error
|
||||
+.B 1 \- Generic error
|
||||
Something went wrong during the operation. Refer to the error
|
||||
message.
|
||||
.RE
|
||||
.TP 8
|
||||
-.B 2 - Usage error
|
||||
+.B 2 \- Usage error
|
||||
The command was used incorrectly, for example: unsupported command
|
||||
line flag, or wrong number of arguments.
|
||||
.RE
|
||||
diff --git a/rust/pvimg/man/pvimg-create.1 b/rust/pvimg/man/pvimg-create.1
|
||||
index aba197fa..dae1cf18 100644
|
||||
--- a/rust/pvimg/man/pvimg-create.1
|
||||
+++ b/rust/pvimg/man/pvimg-create.1
|
||||
@@ -3,11 +3,11 @@
|
||||
.\" it under the terms of the MIT license. See LICENSE for details.
|
||||
.\"
|
||||
|
||||
-.TH pvimg-create 1 "2024-12-05" "s390-tools" "Pvimg Manual"
|
||||
+.TH pvimg-create 1 "2024-12-11" "s390-tools" "Pvimg Manual"
|
||||
.nh
|
||||
.ad l
|
||||
.SH NAME
|
||||
-\fBpvimg create\fP - Create an IBM Secure Execution image
|
||||
+\fBpvimg create\fP \- Create an IBM Secure Execution image
|
||||
\fB
|
||||
.SH SYNOPSIS
|
||||
.nf
|
||||
@@ -195,6 +195,22 @@ Disable the support for backup target keys (default).
|
||||
.RE
|
||||
.RE
|
||||
.PP
|
||||
+\-\-enable\-image\-encryption
|
||||
+.RS 4
|
||||
+Enable encryption of the image components (default). The image components are:
|
||||
+the kernel, ramdisk, and kernel command line.
|
||||
+.RE
|
||||
+.RE
|
||||
+.PP
|
||||
+\-\-disable\-image\-encryption
|
||||
+.RS 4
|
||||
+Disable encryption of the image components. The image components are: the
|
||||
+kernel, ramdisk, and kernel command line. Use only if the components used do not
|
||||
+contain any confidential content (for example, secrets like non\-public
|
||||
+cryptographic keys).
|
||||
+.RE
|
||||
+.RE
|
||||
+.PP
|
||||
\-h, \-\-help
|
||||
.RS 4
|
||||
Print help (see a summary with \fB\-h\fR).
|
||||
@@ -203,16 +219,16 @@ Print help (see a summary with \fB\-h\fR).
|
||||
|
||||
.SH EXIT STATUS
|
||||
.TP 8
|
||||
-.B 0 - Program finished successfully
|
||||
+.B 0 \- Program finished successfully
|
||||
The command was executed successfully.
|
||||
.RE
|
||||
.TP 8
|
||||
-.B 1 - Generic error
|
||||
+.B 1 \- Generic error
|
||||
Something went wrong during the operation. Refer to the error
|
||||
message.
|
||||
.RE
|
||||
.TP 8
|
||||
-.B 2 - Usage error
|
||||
+.B 2 \- Usage error
|
||||
The command was used incorrectly, for example: unsupported command
|
||||
line flag, or wrong number of arguments.
|
||||
.RE
|
||||
diff --git a/rust/pvimg/man/pvimg-info.1 b/rust/pvimg/man/pvimg-info.1
|
||||
index e88cbe49..d2726c35 100644
|
||||
--- a/rust/pvimg/man/pvimg-info.1
|
||||
+++ b/rust/pvimg/man/pvimg-info.1
|
||||
@@ -3,11 +3,11 @@
|
||||
.\" it under the terms of the MIT license. See LICENSE for details.
|
||||
.\"
|
||||
|
||||
-.TH pvimg-info 1 "2024-12-05" "s390-tools" "Pvimg Manual"
|
||||
+.TH pvimg-info 1 "2024-12-11" "s390-tools" "Pvimg Manual"
|
||||
.nh
|
||||
.ad l
|
||||
.SH NAME
|
||||
-\fBpvimg info\fP - Print information about the IBM Secure Execution image
|
||||
+\fBpvimg info\fP \- Print information about the IBM Secure Execution image
|
||||
\fB
|
||||
.SH SYNOPSIS
|
||||
.nf
|
||||
@@ -51,16 +51,16 @@ Print help (see a summary with \fB\-h\fR).
|
||||
|
||||
.SH EXIT STATUS
|
||||
.TP 8
|
||||
-.B 0 - Program finished successfully
|
||||
+.B 0 \- Program finished successfully
|
||||
The command was executed successfully.
|
||||
.RE
|
||||
.TP 8
|
||||
-.B 1 - Generic error
|
||||
+.B 1 \- Generic error
|
||||
Something went wrong during the operation. Refer to the error
|
||||
message.
|
||||
.RE
|
||||
.TP 8
|
||||
-.B 2 - Usage error
|
||||
+.B 2 \- Usage error
|
||||
The command was used incorrectly, for example: unsupported command
|
||||
line flag, or wrong number of arguments.
|
||||
.RE
|
||||
diff --git a/rust/pvimg/man/pvimg-test.1 b/rust/pvimg/man/pvimg-test.1
|
||||
index 901c7edb..4fb7d73f 100644
|
||||
--- a/rust/pvimg/man/pvimg-test.1
|
||||
+++ b/rust/pvimg/man/pvimg-test.1
|
||||
@@ -3,11 +3,11 @@
|
||||
.\" it under the terms of the MIT license. See LICENSE for details.
|
||||
.\"
|
||||
|
||||
-.TH pvimg-test 1 "2024-12-05" "s390-tools" "Pvimg Manual"
|
||||
+.TH pvimg-test 1 "2024-12-11" "s390-tools" "Pvimg Manual"
|
||||
.nh
|
||||
.ad l
|
||||
.SH NAME
|
||||
-\fBpvimg test\fP - Test different aspects of an existing IBM Secure Execution image
|
||||
+\fBpvimg test\fP \- Test different aspects of an existing IBM Secure Execution image
|
||||
\fB
|
||||
.SH SYNOPSIS
|
||||
.nf
|
||||
@@ -54,16 +54,16 @@ Print help (see a summary with \fB\-h\fR).
|
||||
|
||||
.SH EXIT STATUS
|
||||
.TP 8
|
||||
-.B 0 - Program finished successfully
|
||||
+.B 0 \- Program finished successfully
|
||||
The command was executed successfully.
|
||||
.RE
|
||||
.TP 8
|
||||
-.B 1 - Generic error
|
||||
+.B 1 \- Generic error
|
||||
Something went wrong during the operation. Refer to the error
|
||||
message.
|
||||
.RE
|
||||
.TP 8
|
||||
-.B 2 - Usage error
|
||||
+.B 2 \- Usage error
|
||||
The command was used incorrectly, for example: unsupported command
|
||||
line flag, or wrong number of arguments.
|
||||
.RE
|
||||
diff --git a/rust/pvimg/man/pvimg.1 b/rust/pvimg/man/pvimg.1
|
||||
index 37c8e978..5676b61d 100644
|
||||
--- a/rust/pvimg/man/pvimg.1
|
||||
+++ b/rust/pvimg/man/pvimg.1
|
||||
@@ -3,11 +3,11 @@
|
||||
.\" it under the terms of the MIT license. See LICENSE for details.
|
||||
.\"
|
||||
|
||||
-.TH pvimg 1 "2024-12-05" "s390-tools" "Pvimg Manual"
|
||||
+.TH pvimg 1 "2024-12-11" "s390-tools" "Pvimg Manual"
|
||||
.nh
|
||||
.ad l
|
||||
.SH NAME
|
||||
-\fBpvimg\fP - Create and inspect IBM Secure Execution images
|
||||
+\fBpvimg\fP \- Create and inspect IBM Secure Execution images
|
||||
\fB
|
||||
.SH SYNOPSIS
|
||||
.nf
|
||||
@@ -69,16 +69,16 @@ Print help (see a summary with \fB\-h\fR).
|
||||
|
||||
.SH EXIT STATUS
|
||||
.TP 8
|
||||
-.B 0 - Program finished successfully
|
||||
+.B 0 \- Program finished successfully
|
||||
The command was executed successfully.
|
||||
.RE
|
||||
.TP 8
|
||||
-.B 1 - Generic error
|
||||
+.B 1 \- Generic error
|
||||
Something went wrong during the operation. Refer to the error
|
||||
message.
|
||||
.RE
|
||||
.TP 8
|
||||
-.B 2 - Usage error
|
||||
+.B 2 \- Usage error
|
||||
The command was used incorrectly, for example: unsupported command
|
||||
line flag, or wrong number of arguments.
|
||||
.RE
|
||||
diff --git a/rust/pvimg/src/cli.rs b/rust/pvimg/src/cli.rs
|
||||
index 2ca4e901..12f0b764 100644
|
||||
--- a/rust/pvimg/src/cli.rs
|
||||
+++ b/rust/pvimg/src/cli.rs
|
||||
@@ -140,6 +140,20 @@ pub struct CreateBootImageLegacyFlags {
|
||||
/// Disable the support for backup target keys (default).
|
||||
#[arg(long, action = clap::ArgAction::SetTrue, conflicts_with="enable_backup_keys", group="header-flags")]
|
||||
pub disable_backup_keys: Option<bool>,
|
||||
+
|
||||
+ /// Enable encryption of the image components (default).
|
||||
+ ///
|
||||
+ /// The image components are: the kernel, ramdisk, and kernel command line.
|
||||
+ #[arg(long, action = clap::ArgAction::SetTrue, group="header-flags")]
|
||||
+ pub enable_image_encryption: Option<bool>,
|
||||
+
|
||||
+ /// Disable encryption of the image components.
|
||||
+ ///
|
||||
+ /// The image components are: the kernel, ramdisk, and kernel command line.
|
||||
+ /// Use only if the components used do not contain any confidential content
|
||||
+ /// (for example, secrets like non-public cryptographic keys).
|
||||
+ #[arg(long, action = clap::ArgAction::SetTrue, conflicts_with="enable_image_encryption", group="header-flags")]
|
||||
+ pub disable_image_encryption: Option<bool>,
|
||||
}
|
||||
|
||||
#[non_exhaustive]
|
||||
@@ -476,6 +490,8 @@ mod test {
|
||||
flat_map_collect(insert(mvca.clone(), vec![CliOption::new("enable-pckmo", ["--enable-pckmo"])])),
|
||||
flat_map_collect(insert(mvca.clone(), vec![CliOption::new("enable-pckmo-hmac", ["--enable-pckmo-hmac"])])),
|
||||
flat_map_collect(insert(mvca.clone(), vec![CliOption::new("enable-backup-keys", ["--enable-backup-keys"])])),
|
||||
+ flat_map_collect(insert(mvca.clone(), vec![CliOption::new("disable-image-encryption", ["--disable-image-encryption"])])),
|
||||
+ flat_map_collect(insert(mvca.clone(), vec![CliOption::new("enable-image-encryption", ["--enable-image-encryption"])])),
|
||||
];
|
||||
let invalid_create_args = [
|
||||
flat_map_collect(remove(mvcanv.clone(), "no-verify")),
|
||||
@@ -501,6 +517,8 @@ mod test {
|
||||
CliOption::new("x-pcf2", ["--x-pcf", "0x0"])])),
|
||||
flat_map_collect(insert(mvca.clone(), vec![CliOption::new("enable-pckmo", ["--enable-pckmo"]),
|
||||
CliOption::new("disable-pckmo", ["--disable-pckmo"])])),
|
||||
+ flat_map_collect(insert(mvca.clone(), vec![CliOption::new("enable-image-encryption", ["--enable-image-encryption"]),
|
||||
+ CliOption::new("disable-image-encryption", ["--disable-image-encryption"])])),
|
||||
];
|
||||
|
||||
let mut genprotimg_valid_args = vec![
|
||||
diff --git a/rust/pvimg/src/cmd/create.rs b/rust/pvimg/src/cmd/create.rs
|
||||
index b696d790..475d3523 100644
|
||||
--- a/rust/pvimg/src/cmd/create.rs
|
||||
+++ b/rust/pvimg/src/cmd/create.rs
|
||||
@@ -80,6 +80,12 @@ fn parse_flags(
|
||||
lf.enable_backup_keys
|
||||
.filter(|x| *x)
|
||||
.and(Some(PcfV1::all_enabled([PcfV1::BackupTargetKeys]))),
|
||||
+ lf.disable_image_encryption
|
||||
+ .filter(|x| *x)
|
||||
+ .and(Some(PcfV1::all_enabled([PcfV1::NoComponentEncryption]))),
|
||||
+ lf.enable_image_encryption
|
||||
+ .filter(|x| *x)
|
||||
+ .and(Some(PcfV1::all_disabled([PcfV1::NoComponentEncryption]))),
|
||||
]
|
||||
.into_iter()
|
||||
.flatten()
|
||||
@@ -135,6 +141,10 @@ pub fn create(opt: &CreateBootImageArgs) -> Result<OwnExitCode> {
|
||||
read_user_provided_keys(opt.comm_key.as_deref(), &opt.experimental_args)?;
|
||||
let (plaintext_flags, secret_flags) = parse_flags(opt)?;
|
||||
|
||||
+ if plaintext_flags.is_set(PcfV1::NoComponentEncryption) {
|
||||
+ warn!("The components encryption is disabled, make sure that the components do not contain any confidential content.");
|
||||
+ }
|
||||
+
|
||||
let mut components = components(&opt.component_paths)?;
|
||||
if opt.no_component_check {
|
||||
warn!("The component check is turned off!");
|
@ -1,167 +0,0 @@
|
||||
From 5b6d7a467dc342c9c25a0af72b2d5546798cdc94 Mon Sep 17 00:00:00 2001
|
||||
From: Marc Hartmayer <mhartmay@linux.ibm.com>
|
||||
Date: Thu, 12 Dec 2024 20:19:56 +0100
|
||||
Subject: [PATCH] rust/pvimg: Add '--cck <FILE>' command line option and make
|
||||
'--comm-key' an alias
|
||||
MIME-Version: 1.0
|
||||
Content-Type: text/plain; charset=UTF-8
|
||||
Content-Transfer-Encoding: 8bit
|
||||
|
||||
Add '--cck <FILE>' as an command line option and make '--comm-key' an
|
||||
alias of it. This makes the command line more similar to the other
|
||||
Secure Execution related PV-tools (e.g. pvattest and pvsecret).
|
||||
|
||||
Suggested-by: Reinhard Bündgen <buendgen@de.ibm.com>
|
||||
Reviewed-by: Steffen Eiden <seiden@linux.ibm.com>
|
||||
Signed-off-by: Marc Hartmayer <mhartmay@linux.ibm.com>
|
||||
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
|
||||
---
|
||||
rust/pvimg/man/genprotimg.1 | 11 +++++------
|
||||
rust/pvimg/man/pvimg-create.1 | 11 +++++------
|
||||
rust/pvimg/src/cli.rs | 14 ++++++++------
|
||||
rust/pvimg/src/cmd/create.rs | 3 +--
|
||||
4 files changed, 19 insertions(+), 20 deletions(-)
|
||||
|
||||
Index: s390-tools-2.36.0/rust/pvimg/man/genprotimg.1
|
||||
===================================================================
|
||||
--- s390-tools-2.36.0.orig/rust/pvimg/man/genprotimg.1
|
||||
+++ s390-tools-2.36.0/rust/pvimg/man/genprotimg.1
|
||||
@@ -123,7 +123,7 @@ Overwrite an existing Secure Execution b
|
||||
.RE
|
||||
.RE
|
||||
.PP
|
||||
-\-\-comm\-key <FILE>
|
||||
+\-\-cck, \-\-comm\-key <FILE>
|
||||
.RS 4
|
||||
Use the content of FILE as the customer\-communication key (CCK). The file must
|
||||
contain exactly 32 bytes of data.
|
||||
@@ -133,7 +133,7 @@ contain exactly 32 bytes of data.
|
||||
\-\-enable\-dump
|
||||
.RS 4
|
||||
Enable Secure Execution guest dump support. This option requires the
|
||||
-\fB\-\-comm\-key\fR option.
|
||||
+\fB\-\-cck\fR option.
|
||||
.RE
|
||||
.RE
|
||||
.PP
|
||||
@@ -146,8 +146,7 @@ Disable Secure Execution guest dump supp
|
||||
\-\-enable\-cck\-extension\-secret
|
||||
.RS 4
|
||||
Add\-secret requests must provide an extension secret that matches the
|
||||
-CCK\-derived extension secret. This option requires the \fB\-\-comm\-key\fR
|
||||
-option.
|
||||
+CCK\-derived extension secret. This option requires the \fB\-\-cck\fR option.
|
||||
.RE
|
||||
.RE
|
||||
.PP
|
||||
@@ -268,7 +267,7 @@ Generate an IBM Secure Execution image:
|
||||
|
||||
Generate an IBM Secure Execution image with Secure Execution guest dump support:
|
||||
.PP
|
||||
-.B genprotimg \-i \fI\,/boot/vmlinuz\/\fR \-r \fI\,/boot/initrd.img\/\fR \-p \fI\,parmfile\/\fR \-k \fI\,host_key.crt\/\fR \-C \fI\,ibm-z-host-key-signing.crt\/\fR \-C \fI\,DigiCertCA.crt\fR \-o \fI\,/boot/secure-linux\/\fR \-\-enable\-dump \-\-comm\-key \fI\,comm-key\fR
|
||||
+.B genprotimg \-i \fI\,/boot/vmlinuz\/\fR \-r \fI\,/boot/initrd.img\/\fR \-p \fI\,parmfile\/\fR \-k \fI\,host_key.crt\/\fR \-C \fI\,ibm-z-host-key-signing.crt\/\fR \-C \fI\,DigiCertCA.crt\fR \-o \fI\,/boot/secure-linux\/\fR \-\-enable\-dump \-\-cck \fI\,comm-key\fR
|
||||
.SH NOTES
|
||||
.IP "1." 4
|
||||
The \fBgenprotimg\fR(1) command is a symbolic link to the \fBpvimg-create\fR(1) command.
|
||||
Index: s390-tools-2.36.0/rust/pvimg/man/pvimg-create.1
|
||||
===================================================================
|
||||
--- s390-tools-2.36.0.orig/rust/pvimg/man/pvimg-create.1
|
||||
+++ s390-tools-2.36.0/rust/pvimg/man/pvimg-create.1
|
||||
@@ -122,7 +122,7 @@ Overwrite an existing Secure Execution b
|
||||
.RE
|
||||
.RE
|
||||
.PP
|
||||
-\-\-comm\-key <FILE>
|
||||
+\-\-cck, \-\-comm\-key <FILE>
|
||||
.RS 4
|
||||
Use the content of FILE as the customer\-communication key (CCK). The file must
|
||||
contain exactly 32 bytes of data.
|
||||
@@ -132,7 +132,7 @@ contain exactly 32 bytes of data.
|
||||
\-\-enable\-dump
|
||||
.RS 4
|
||||
Enable Secure Execution guest dump support. This option requires the
|
||||
-\fB\-\-comm\-key\fR option.
|
||||
+\fB\-\-cck\fR option.
|
||||
.RE
|
||||
.RE
|
||||
.PP
|
||||
@@ -145,8 +145,7 @@ Disable Secure Execution guest dump supp
|
||||
\-\-enable\-cck\-extension\-secret
|
||||
.RS 4
|
||||
Add\-secret requests must provide an extension secret that matches the
|
||||
-CCK\-derived extension secret. This option requires the \fB\-\-comm\-key\fR
|
||||
-option.
|
||||
+CCK\-derived extension secret. This option requires the \fB\-\-cck\fR option.
|
||||
.RE
|
||||
.RE
|
||||
.PP
|
||||
@@ -249,7 +248,7 @@ Generate an IBM Secure Execution image:
|
||||
|
||||
Generate an IBM Secure Execution image with Secure Execution guest dump support:
|
||||
.PP
|
||||
-.B pvimg create \-i \fI\,/boot/vmlinuz\/\fR \-r \fI\,/boot/initrd.img\/\fR \-p \fI\,parmfile\/\fR \-k \fI\,host_key.crt\/\fR \-C \fI\,ibm-z-host-key-signing.crt\/\fR \-C \fI\,DigiCertCA.crt\fR \-o \fI\,/boot/secure-linux\/\fR \-\-enable\-dump \-\-comm\-key \fI\,comm-key\fR
|
||||
+.B pvimg create \-i \fI\,/boot/vmlinuz\/\fR \-r \fI\,/boot/initrd.img\/\fR \-p \fI\,parmfile\/\fR \-k \fI\,host_key.crt\/\fR \-C \fI\,ibm-z-host-key-signing.crt\/\fR \-C \fI\,DigiCertCA.crt\fR \-o \fI\,/boot/secure-linux\/\fR \-\-enable\-dump \-\-cck \fI\,comm-key\fR
|
||||
.SH NOTES
|
||||
.IP "1." 4
|
||||
The \fBgenprotimg\fR(1) command is a symbolic link to the \fBpvimg-create\fR(1) command.
|
||||
Index: s390-tools-2.36.0/rust/pvimg/src/cli.rs
|
||||
===================================================================
|
||||
--- s390-tools-2.36.0.orig/rust/pvimg/src/cli.rs
|
||||
+++ s390-tools-2.36.0/rust/pvimg/src/cli.rs
|
||||
@@ -96,8 +96,8 @@ pub struct ComponentPaths {
|
||||
#[command(group(ArgGroup::new("header-flags").multiple(true).conflicts_with_all(["x_pcf", "x_scf"])))]
|
||||
pub struct CreateBootImageLegacyFlags {
|
||||
/// Enable Secure Execution guest dump support. This option requires the
|
||||
- /// '--comm-key' option.
|
||||
- #[arg(long, action = clap::ArgAction::SetTrue, requires="comm_key", group="header-flags")]
|
||||
+ /// '--cck' option.
|
||||
+ #[arg(long, action = clap::ArgAction::SetTrue, requires="cck", group="header-flags")]
|
||||
pub enable_dump: Option<bool>,
|
||||
|
||||
/// Disable Secure Execution guest dump support (default).
|
||||
@@ -105,9 +105,9 @@ pub struct CreateBootImageLegacyFlags {
|
||||
pub disable_dump: Option<bool>,
|
||||
|
||||
/// Add-secret requests must provide an extension secret that matches the
|
||||
- /// CCK-derived extension secret. This option requires the '--comm-key'
|
||||
+ /// CCK-derived extension secret. This option requires the '--cck'
|
||||
/// option.
|
||||
- #[arg(long, action = clap::ArgAction::SetTrue, requires="comm_key", group="header-flags")]
|
||||
+ #[arg(long, action = clap::ArgAction::SetTrue, requires="cck", group="header-flags")]
|
||||
pub enable_cck_extension_secret: Option<bool>,
|
||||
|
||||
/// Add-secret requests don't have to provide the CCK-derived extension
|
||||
@@ -328,8 +328,8 @@ pub struct CreateBootImageArgs {
|
||||
/// Use the content of FILE as the customer-communication key (CCK).
|
||||
///
|
||||
/// The file must contain exactly 32 bytes of data.
|
||||
- #[arg(long, value_name = "FILE")]
|
||||
- pub comm_key: Option<PathBuf>,
|
||||
+ #[arg(long, value_name = "FILE", visible_alias = "comm-key")]
|
||||
+ pub cck: Option<PathBuf>,
|
||||
|
||||
#[clap(flatten)]
|
||||
pub legacy_flags: CreateBootImageLegacyFlags,
|
||||
@@ -482,6 +482,8 @@ mod test {
|
||||
flat_map_collect(insert(mvca.clone(), vec![CliOption::new("enable-dump", ["--enable-dump"]),
|
||||
CliOption::new("comm-key", ["--comm-key", "/dev/null"])])),
|
||||
flat_map_collect(insert(mvca.clone(), vec![CliOption::new("enable-dump", ["--enable-dump"]),
|
||||
+ CliOption::new("comm-key", ["--cck", "/dev/null"])])),
|
||||
+ flat_map_collect(insert(mvca.clone(), vec![CliOption::new("enable-dump", ["--enable-dump"]),
|
||||
CliOption::new("comm-key", ["--comm-key", "/dev/null"])])),
|
||||
flat_map_collect(insert(mvca.clone(), vec![CliOption::new("x-pcf", ["--x-pcf", "0x0"]),
|
||||
CliOption::new("x-scf", ["--x-scf", "0x0"])])),
|
||||
Index: s390-tools-2.36.0/rust/pvimg/src/cmd/create.rs
|
||||
===================================================================
|
||||
--- s390-tools-2.36.0.orig/rust/pvimg/src/cmd/create.rs
|
||||
+++ s390-tools-2.36.0/rust/pvimg/src/cmd/create.rs
|
||||
@@ -137,8 +137,7 @@ pub fn create(opt: &CreateBootImageArgs)
|
||||
let verified_host_keys = opt
|
||||
.certificate_args
|
||||
.get_verified_hkds("Secure Execution image")?;
|
||||
- let user_provided_keys =
|
||||
- read_user_provided_keys(opt.comm_key.as_deref(), &opt.experimental_args)?;
|
||||
+ let user_provided_keys = read_user_provided_keys(opt.cck.as_deref(), &opt.experimental_args)?;
|
||||
let (plaintext_flags, secret_flags) = parse_flags(opt)?;
|
||||
|
||||
if plaintext_flags.is_set(PcfV1::NoComponentEncryption) {
|
@ -1,58 +0,0 @@
|
||||
From 560b276f7e9938475af921c8ebd4cd05910dbf31 Mon Sep 17 00:00:00 2001
|
||||
From: Marc Hartmayer <mhartmay@linux.ibm.com>
|
||||
Date: Fri, 6 Dec 2024 20:45:36 +0100
|
||||
Subject: [PATCH] rust/pvimg: Fix possible 'range start index out of range for
|
||||
slice' error
|
||||
MIME-Version: 1.0
|
||||
Content-Type: text/plain; charset=UTF-8
|
||||
Content-Transfer-Encoding: 8bit
|
||||
|
||||
Fix possible 'range start index 16 out of range for slice of length 0'
|
||||
error by adding a check of the slice data length.
|
||||
|
||||
Fixes: f4cf4ae6ebb1 ("rust: Add a new tool called 'pvimg'")
|
||||
Reviewed-by: Steffen Eiden <seiden@linux.ibm.com>
|
||||
Signed-off-by: Marc Hartmayer <mhartmay@linux.ibm.com>
|
||||
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
|
||||
---
|
||||
rust/pvimg/src/pv_utils/se_hdr/brb.rs | 23 +++++++++++++++++++++++
|
||||
1 file changed, 23 insertions(+)
|
||||
|
||||
diff --git a/rust/pvimg/src/pv_utils/se_hdr/brb.rs b/rust/pvimg/src/pv_utils/se_hdr/brb.rs
|
||||
index f7ae1bc9..ac3a2e6e 100644
|
||||
--- a/rust/pvimg/src/pv_utils/se_hdr/brb.rs
|
||||
+++ b/rust/pvimg/src/pv_utils/se_hdr/brb.rs
|
||||
@@ -259,6 +259,10 @@ impl SeHdr {
|
||||
return Err(Error::InvalidSeHdr);
|
||||
}
|
||||
|
||||
+ if sehs <= common_size {
|
||||
+ return Err(Error::InvalidSeHdr);
|
||||
+ }
|
||||
+
|
||||
data.resize(sehs, 0);
|
||||
reader.read_exact(&mut data[common_size..])?;
|
||||
Self::try_from_data(&data)
|
||||
@@ -366,3 +370,22 @@ impl AeadCipherTrait for SeHdrPlain {
|
||||
self.data.aead_tag_size()
|
||||
}
|
||||
}
|
||||
+
|
||||
+#[cfg(test)]
|
||||
+mod tests {
|
||||
+ use std::io::Cursor;
|
||||
+
|
||||
+ use super::SeHdr;
|
||||
+ use crate::error::Error;
|
||||
+
|
||||
+ #[test]
|
||||
+ fn test_sehdr_try_from_io() {
|
||||
+ // Invalid SeHdr as `sehs` is set to 0
|
||||
+ assert!(matches!(
|
||||
+ SeHdr::try_from_io(Cursor::new([
|
||||
+ 73, 66, 77, 83, 101, 99, 69, 120, 0, 0, 1, 0, 0, 0, 0, 0, 2, 0, 8
|
||||
+ ])),
|
||||
+ Err(Error::InvalidSeHdr)
|
||||
+ ));
|
||||
+ }
|
||||
+}
|
@ -1,51 +0,0 @@
|
||||
From 3f6572e901ddcc654021c4302cb2a99999acb87a Mon Sep 17 00:00:00 2001
|
||||
From: Marc Hartmayer <mhartmay@linux.ibm.com>
|
||||
Date: Wed, 18 Dec 2024 13:41:13 +0100
|
||||
Subject: [PATCH] rust/utils: mkdtemp: fix memory leak
|
||||
MIME-Version: 1.0
|
||||
Content-Type: text/plain; charset=UTF-8
|
||||
Content-Transfer-Encoding: 8bit
|
||||
|
||||
Fix memory leak of @template_raw. The documentation of CString::into_raw
|
||||
reads:
|
||||
|
||||
"Consumes the CString and transfers ownership of the string to a C
|
||||
caller.
|
||||
...
|
||||
Failure to call CString::from_raw will lead to a memory leak." [1]
|
||||
|
||||
Let's fix the memory leak by always calling `CString::from_raw` and
|
||||
therefore reclaim the ownership.
|
||||
|
||||
[1] https://doc.rust-lang.org/std/ffi/struct.CString.html#method.into_raw
|
||||
|
||||
Fixes: e56acf4f14b0 ("pv_core: add `TemporaryDirectory`")
|
||||
Reviewed-by: Steffen Eiden <seiden@linux.ibm.com>
|
||||
Signed-off-by: Marc Hartmayer <mhartmay@linux.ibm.com>
|
||||
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
|
||||
---
|
||||
rust/utils/src/tmpfile.rs | 7 ++++---
|
||||
1 file changed, 4 insertions(+), 3 deletions(-)
|
||||
|
||||
diff --git a/rust/utils/src/tmpfile.rs b/rust/utils/src/tmpfile.rs
|
||||
index 07acdba8..883d5586 100644
|
||||
--- a/rust/utils/src/tmpfile.rs
|
||||
+++ b/rust/utils/src/tmpfile.rs
|
||||
@@ -16,13 +16,14 @@ fn mkdtemp<P: AsRef<Path>>(template: P) -> Result<PathBuf, std::io::Error> {
|
||||
// SAFETY: template_raw is a valid CString because it was generated by
|
||||
// the `CString::new`.
|
||||
let ret = libc::mkdtemp(template_raw);
|
||||
+ // SAFETY: `template_raw` is still a valid CString because it was
|
||||
+ // generated by `CString::new` and modified by `libc::mkdtemp`.
|
||||
+ let path_cstr = std::ffi::CString::from_raw(template_raw);
|
||||
|
||||
if ret.is_null() {
|
||||
+ drop(path_cstr);
|
||||
Err(std::io::Error::last_os_error())
|
||||
} else {
|
||||
- // SAFETY: `template_raw` is still a valid CString because it was
|
||||
- // generated by `CString::new` and modified by `libc::mkdtemp`.
|
||||
- let path_cstr = std::ffi::CString::from_raw(template_raw);
|
||||
let path = OsStr::from_bytes(path_cstr.as_bytes());
|
||||
let path = std::path::PathBuf::from(path);
|
||||
|
@ -1,334 +0,0 @@
|
||||
From 944581eaefe4c6887790f2b8ed39c9ee76146c55 Mon Sep 17 00:00:00 2001
|
||||
From: Marc Hartmayer <mhartmay@linux.ibm.com>
|
||||
Date: Tue, 17 Dec 2024 11:58:01 +0100
|
||||
Subject: [PATCH] rust/pvimg: Add upper estimates for the Secure Execution
|
||||
header
|
||||
MIME-Version: 1.0
|
||||
Content-Type: text/plain; charset=UTF-8
|
||||
Content-Transfer-Encoding: 8bit
|
||||
|
||||
A Secure Execution header V1 can be at maximum two pages large, optional
|
||||
items are not supported, and the size of the encrypted part cannot be
|
||||
larger than the total size of the Secure Execution header add this as
|
||||
Deku assertions and additional conditions to the code. In addition, add
|
||||
a check for the number of key slots.
|
||||
|
||||
Fixes: f4cf4ae6ebb1 ("rust: Add a new tool called 'pvimg'")
|
||||
Reviewed-by: Steffen Eiden <seiden@linux.ibm.com>
|
||||
Signed-off-by: Marc Hartmayer <mhartmay@linux.ibm.com>
|
||||
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
|
||||
---
|
||||
rust/pvimg/src/pv_utils/error.rs | 3 +
|
||||
rust/pvimg/src/pv_utils/se_hdr/brb.rs | 50 +++++++++++++---
|
||||
rust/pvimg/src/pv_utils/se_hdr/builder.rs | 10 +++-
|
||||
rust/pvimg/src/pv_utils/se_hdr/hdr_v1.rs | 71 ++++++++++++++++++++---
|
||||
rust/pvimg/src/pv_utils/uvdata.rs | 18 ++++--
|
||||
5 files changed, 130 insertions(+), 22 deletions(-)
|
||||
|
||||
diff --git a/rust/pvimg/src/pv_utils/error.rs b/rust/pvimg/src/pv_utils/error.rs
|
||||
index 2a176276..a12c4a22 100644
|
||||
--- a/rust/pvimg/src/pv_utils/error.rs
|
||||
+++ b/rust/pvimg/src/pv_utils/error.rs
|
||||
@@ -30,6 +30,9 @@ pub enum Error {
|
||||
#[error("Invalid Secure Execution header")]
|
||||
InvalidSeHdr,
|
||||
|
||||
+ #[error("Secure Execution header size {given} is larger than the maximum of {maximum} bytes")]
|
||||
+ InvalidSeHdrTooLarge { given: usize, maximum: usize },
|
||||
+
|
||||
#[error("Invalid component metadata.")]
|
||||
InvalidComponentMetadata,
|
||||
|
||||
diff --git a/rust/pvimg/src/pv_utils/se_hdr/brb.rs b/rust/pvimg/src/pv_utils/se_hdr/brb.rs
|
||||
index ac3a2e6e..b8dadba1 100644
|
||||
--- a/rust/pvimg/src/pv_utils/se_hdr/brb.rs
|
||||
+++ b/rust/pvimg/src/pv_utils/se_hdr/brb.rs
|
||||
@@ -171,8 +171,8 @@ impl AeadCipherTrait for SeHdr {
|
||||
}
|
||||
|
||||
impl AeadDataTrait for SeHdr {
|
||||
- fn aad(&self) -> Vec<u8> {
|
||||
- [serialize_to_bytes(&self.common).unwrap(), self.data.aad()].concat()
|
||||
+ fn aad(&self) -> Result<Vec<u8>> {
|
||||
+ Ok([serialize_to_bytes(&self.common)?, self.data.aad()?].concat())
|
||||
}
|
||||
|
||||
fn data(&self) -> Vec<u8> {
|
||||
@@ -265,7 +265,7 @@ impl SeHdr {
|
||||
|
||||
data.resize(sehs, 0);
|
||||
reader.read_exact(&mut data[common_size..])?;
|
||||
- Self::try_from_data(&data)
|
||||
+ Self::try_from_data(&data).map_err(|_| Error::InvalidSeHdr)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -342,13 +342,13 @@ impl UvDataPlainTrait for SeHdrPlain {
|
||||
}
|
||||
|
||||
impl AeadPlainDataTrait for SeHdrPlain {
|
||||
- fn aad(&self) -> Vec<u8> {
|
||||
- let data_aad = self.data.aad();
|
||||
+ fn aad(&self) -> Result<Vec<u8>> {
|
||||
+ let data_aad = self.data.aad()?;
|
||||
|
||||
- [serialize_to_bytes(&self.common).unwrap(), data_aad].concat()
|
||||
+ Ok([serialize_to_bytes(&self.common)?, data_aad].concat())
|
||||
}
|
||||
|
||||
- fn data(&self) -> Confidential<Vec<u8>> {
|
||||
+ fn data(&self) -> Result<Confidential<Vec<u8>>> {
|
||||
self.data.data()
|
||||
}
|
||||
|
||||
@@ -387,5 +387,41 @@ mod tests {
|
||||
])),
|
||||
Err(Error::InvalidSeHdr)
|
||||
));
|
||||
+
|
||||
+ // Invalid SeHdr as the `sehs` is too large.
|
||||
+ assert!(matches!(
|
||||
+ SeHdr::try_from_io(Cursor::new([
|
||||
+ 73, 66, 77, 83, 101, 99, 69, 120, 0, 0, 1, 0, 0, 0, 1, 255, 65, 65, 65, 65, 67, 0,
|
||||
+ 65, 17, 65, 0, 65, 65, 65, 65, 65, 65, 91, 91, 180, 91, 91, 91, 91, 91, 91, 91, 91,
|
||||
+ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
|
||||
+ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
|
||||
+ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
|
||||
+ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 241, 241,
|
||||
+ 241, 241, 241, 91, 91, 91, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112,
|
||||
+ 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 80,
|
||||
+ 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112,
|
||||
+ 112, 112, 112, 112, 91, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112,
|
||||
+ 112, 112, 112, 112, 112, 112, 112, 0, 0, 0, 0, 101, 99, 255, 255, 255, 255, 255,
|
||||
+ 255, 255, 255, 255, 255, 255, 65, 65, 65, 65, 67, 0, 65, 17, 65, 0, 65, 65, 65, 65,
|
||||
+ 65, 65, 91, 91, 180, 91, 91, 91, 91, 91, 91, 91, 91, 255, 255, 255, 255, 255, 255,
|
||||
+ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
|
||||
+ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
|
||||
+ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
|
||||
+ 255, 255, 255, 255, 255, 255, 255, 255, 241, 241, 241, 241, 241, 91, 91, 91, 112,
|
||||
+ 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112,
|
||||
+ 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 80, 112, 112, 112, 112, 112, 112,
|
||||
+ 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 91, 112, 112,
|
||||
+ 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 73, 66, 77, 83, 101, 99, 69, 120,
|
||||
+ 0, 112, 112, 0, 1, 0, 0, 0, 0, 101, 99, 255, 255, 255, 255, 255, 255, 255, 255,
|
||||
+ 255, 255, 255, 65, 65, 65, 65, 67, 0, 65, 17, 65, 0, 65, 65, 65, 65, 65, 65, 91,
|
||||
+ 91, 180, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91,
|
||||
+ 91, 91, 112, 112, 112, 112, 112, 73, 66, 77, 83, 101, 99, 69, 120, 0, 0, 1, 0, 0,
|
||||
+ 0, 0, 48, 53, 53, 53, 53, 53, 53, 53, 91, 91, 91, 241, 241, 46, 49, 49, 0, 49, 49,
|
||||
+ 0, 0, 112, 112, 112, 91, 0, 0, 0, 0, 9, 0, 49, 50, 22, 241, 241, 241, 241, 241,
|
||||
+ 241, 241, 241, 241, 241, 241, 91, 91, 91, 91, 91, 255, 251, 0, 0, 91, 91, 91, 91,
|
||||
+ 91, 91, 91, 91, 91, 91, 91, 0, 0, 91, 0, 0, 10, 91, 91, 91, 65, 65, 65, 65
|
||||
+ ])),
|
||||
+ Err(Error::InvalidSeHdr)
|
||||
+ ));
|
||||
}
|
||||
}
|
||||
diff --git a/rust/pvimg/src/pv_utils/se_hdr/builder.rs b/rust/pvimg/src/pv_utils/se_hdr/builder.rs
|
||||
index ba6de898..93bcc7af 100644
|
||||
--- a/rust/pvimg/src/pv_utils/se_hdr/builder.rs
|
||||
+++ b/rust/pvimg/src/pv_utils/se_hdr/builder.rs
|
||||
@@ -230,8 +230,14 @@ mod tests {
|
||||
|
||||
let decrypted = bin.decrypt(&prot_key).expect("BUG");
|
||||
assert_eq!(bin.common, decrypted.common);
|
||||
- assert_eq!(bin.aad(), decrypted.aad());
|
||||
- assert_ne!(&bin.data(), decrypted.data().value());
|
||||
+ assert_eq!(
|
||||
+ bin.aad().expect("should not fail"),
|
||||
+ decrypted.aad().expect("should not fail")
|
||||
+ );
|
||||
+ assert_ne!(
|
||||
+ &bin.data(),
|
||||
+ decrypted.data().expect("should not fail").value()
|
||||
+ );
|
||||
let _decrypted_hdrv1: SeHdrDataV1 = decrypted.data.try_into().expect("BUG");
|
||||
}
|
||||
|
||||
diff --git a/rust/pvimg/src/pv_utils/se_hdr/hdr_v1.rs b/rust/pvimg/src/pv_utils/se_hdr/hdr_v1.rs
|
||||
index a7f2f609..b179d50d 100644
|
||||
--- a/rust/pvimg/src/pv_utils/se_hdr/hdr_v1.rs
|
||||
+++ b/rust/pvimg/src/pv_utils/se_hdr/hdr_v1.rs
|
||||
@@ -19,6 +19,7 @@ use serde::{Serialize, Serializer};
|
||||
use super::keys::phkh_v1;
|
||||
use crate::{
|
||||
error::Error,
|
||||
+ misc::PAGESIZE,
|
||||
pv_utils::{
|
||||
error::Result,
|
||||
se_hdr::{
|
||||
@@ -51,11 +52,14 @@ struct HdrSizesV1 {
|
||||
#[derive(Debug, Clone, PartialEq, Eq, DekuRead, DekuWrite, Serialize)]
|
||||
#[deku(endian = "endian", ctx = "endian: Endian", ctx_default = "Endian::Big")]
|
||||
struct SeHdrAadV1 {
|
||||
+ #[deku(assert = "*sehs <= SeHdrDataV1::MAX_SIZE.try_into().unwrap()")]
|
||||
sehs: u32,
|
||||
#[serde(serialize_with = "ser_hex")]
|
||||
iv: [u8; SymKeyType::AES_256_GCM_IV_LEN],
|
||||
res1: u32,
|
||||
+ #[deku(assert = "*nks <= (*sehs).into()", update = "self.keyslots.len()")]
|
||||
nks: u64,
|
||||
+ #[deku(assert = "*sea <= (*sehs).into()")]
|
||||
sea: u64,
|
||||
nep: u64,
|
||||
#[serde(serialize_with = "ser_lower_hex")]
|
||||
@@ -118,6 +122,7 @@ pub struct SeHdrConfV1 {
|
||||
psw: PSW,
|
||||
#[serde(serialize_with = "ser_lower_hex")]
|
||||
scf: u64,
|
||||
+ #[deku(assert_eq = "0")]
|
||||
noi: u32,
|
||||
res2: u32,
|
||||
#[deku(count = "noi")]
|
||||
@@ -200,6 +205,7 @@ where
|
||||
}
|
||||
|
||||
impl SeHdrDataV1 {
|
||||
+ const MAX_SIZE: usize = 2 * PAGESIZE;
|
||||
const PCF_DEFAULT: u64 = 0x0;
|
||||
const SCF_DEFAULT: u64 = 0x0;
|
||||
|
||||
@@ -241,7 +247,14 @@ impl SeHdrDataV1 {
|
||||
tag: SeHdrTagV1::default(),
|
||||
};
|
||||
let hdr_size = ret.size()?;
|
||||
- ret.aad.sehs = hdr_size.phs.try_into()?;
|
||||
+ let phs = hdr_size.phs.try_into()?;
|
||||
+ if phs > Self::MAX_SIZE {
|
||||
+ return Err(Error::InvalidSeHdrTooLarge {
|
||||
+ given: phs,
|
||||
+ maximum: Self::MAX_SIZE,
|
||||
+ });
|
||||
+ }
|
||||
+ ret.aad.sehs = phs.try_into()?;
|
||||
ret.aad.sea = hdr_size.sea;
|
||||
Ok(ret)
|
||||
}
|
||||
@@ -494,8 +507,8 @@ impl KeyExchangeTrait for SeHdrBinV1 {
|
||||
}
|
||||
|
||||
impl AeadDataTrait for SeHdrBinV1 {
|
||||
- fn aad(&self) -> Vec<u8> {
|
||||
- serialize_to_bytes(&self.aad).unwrap()
|
||||
+ fn aad(&self) -> Result<Vec<u8>> {
|
||||
+ serialize_to_bytes(&self.aad)
|
||||
}
|
||||
|
||||
fn data(&self) -> Vec<u8> {
|
||||
@@ -508,12 +521,12 @@ impl AeadDataTrait for SeHdrBinV1 {
|
||||
}
|
||||
|
||||
impl AeadPlainDataTrait for SeHdrDataV1 {
|
||||
- fn aad(&self) -> Vec<u8> {
|
||||
- serialize_to_bytes(&self.aad).unwrap()
|
||||
+ fn aad(&self) -> Result<Vec<u8>> {
|
||||
+ serialize_to_bytes(&self.aad)
|
||||
}
|
||||
|
||||
- fn data(&self) -> Confidential<Vec<u8>> {
|
||||
- serialize_to_bytes(self.data.value()).unwrap().into()
|
||||
+ fn data(&self) -> Result<Confidential<Vec<u8>>> {
|
||||
+ Ok(serialize_to_bytes(self.data.value())?.into())
|
||||
}
|
||||
|
||||
fn tag(&self) -> Vec<u8> {
|
||||
@@ -610,4 +623,48 @@ mod tests {
|
||||
assert_eq!(psw, hdr_data_v1.data.value().psw);
|
||||
assert_eq!(cck.value(), hdr_data_v1.data.value().cck.value());
|
||||
}
|
||||
+
|
||||
+ #[test]
|
||||
+ fn max_size_sehdr_test() {
|
||||
+ const MAX_HOST_KEYS: usize = 95;
|
||||
+
|
||||
+ let (_, host_key) = get_test_key_and_cert();
|
||||
+ let pub_key = host_key.public_key().unwrap();
|
||||
+ let host_keys_max: Vec<_> = (0..MAX_HOST_KEYS).map(|_| pub_key.clone()).collect();
|
||||
+ let too_many_host_keys: Vec<_> = (0..MAX_HOST_KEYS + 1).map(|_| pub_key.clone()).collect();
|
||||
+ let xts_key = Confidential::new([0x3; SymKeyType::AES_256_XTS_KEY_LEN]);
|
||||
+ let meta = ComponentMetadataV1 {
|
||||
+ ald: [0x1; SHA_512_HASH_LEN],
|
||||
+ pld: [0x2; SHA_512_HASH_LEN],
|
||||
+ tld: [0x3; SHA_512_HASH_LEN],
|
||||
+ nep: 3,
|
||||
+ key: xts_key,
|
||||
+ };
|
||||
+ let psw = PSW {
|
||||
+ addr: 1234,
|
||||
+ mask: 5678,
|
||||
+ };
|
||||
+
|
||||
+ let mut builder = SeHdrBuilder::new(SeHdrVersion::V1, psw.clone(), meta.clone())
|
||||
+ .expect("should not fail");
|
||||
+ builder
|
||||
+ .add_hostkeys(&host_keys_max)
|
||||
+ .expect("should not fail")
|
||||
+ .with_components(meta.clone())
|
||||
+ .expect("should not fail");
|
||||
+ let bin = builder.build().expect("should not fail");
|
||||
+ assert_eq!(bin.common.version, SeHdrVersion::V1);
|
||||
+ let hdr_v1: SeHdrBinV1 = bin.data.try_into().expect("should not fail");
|
||||
+ assert_eq!(hdr_v1.aad.sehs, 8160);
|
||||
+
|
||||
+ let mut builder = SeHdrBuilder::new(SeHdrVersion::V1, psw.clone(), meta.clone())
|
||||
+ .expect("should not fail");
|
||||
+
|
||||
+ builder
|
||||
+ .add_hostkeys(&too_many_host_keys)
|
||||
+ .expect("should not fail")
|
||||
+ .with_components(meta)
|
||||
+ .expect("should not fail");
|
||||
+ assert!(matches!(builder.build(), Err(Error::InvalidSeHdr)));
|
||||
+ }
|
||||
}
|
||||
diff --git a/rust/pvimg/src/pv_utils/uvdata.rs b/rust/pvimg/src/pv_utils/uvdata.rs
|
||||
index b0ec355a..c6ed9567 100644
|
||||
--- a/rust/pvimg/src/pv_utils/uvdata.rs
|
||||
+++ b/rust/pvimg/src/pv_utils/uvdata.rs
|
||||
@@ -34,7 +34,7 @@ pub trait AeadCipherTrait {
|
||||
#[enum_dispatch]
|
||||
pub trait AeadDataTrait {
|
||||
/// Returns the authenticated associated data.
|
||||
- fn aad(&self) -> Vec<u8>;
|
||||
+ fn aad(&self) -> Result<Vec<u8>>;
|
||||
|
||||
/// Returns the encrypted data.
|
||||
fn data(&self) -> Vec<u8>;
|
||||
@@ -47,10 +47,10 @@ pub trait AeadDataTrait {
|
||||
#[enum_dispatch]
|
||||
pub trait AeadPlainDataTrait {
|
||||
/// Returns the authenticated associated data.
|
||||
- fn aad(&self) -> Vec<u8>;
|
||||
+ fn aad(&self) -> Result<Vec<u8>>;
|
||||
|
||||
/// Returns the unencrypted data.
|
||||
- fn data(&self) -> Confidential<Vec<u8>>;
|
||||
+ fn data(&self) -> Result<Confidential<Vec<u8>>>;
|
||||
|
||||
/// Returns the tag data.
|
||||
fn tag(&self) -> Vec<u8>;
|
||||
@@ -124,8 +124,14 @@ pub trait UvDataPlainTrait:
|
||||
expected: self.aead_key_type().to_string(),
|
||||
});
|
||||
}
|
||||
- let aad = self.aad();
|
||||
- let unecrypted_data = self.data();
|
||||
+ let aad = self.aad().map_err(|err| match err {
|
||||
+ Error::Deku(_) => Error::InvalidSeHdr,
|
||||
+ err => err,
|
||||
+ })?;
|
||||
+ let unecrypted_data = self.data().map_err(|err| match err {
|
||||
+ Error::Deku(_) => Error::InvalidSeHdr,
|
||||
+ err => err,
|
||||
+ })?;
|
||||
let iv = self.iv();
|
||||
let result = encrypt_aead(key, iv, &aad, unecrypted_data.value())?;
|
||||
Self::C::try_from_data(&result.into_buf())
|
||||
@@ -169,7 +175,7 @@ pub trait UvDataTrait: AeadDataTrait + AeadCipherTrait + KeyExchangeTrait + Clon
|
||||
}
|
||||
|
||||
let tag_size = self.aead_tag_size();
|
||||
- let aad = self.aad();
|
||||
+ let aad = self.aad()?;
|
||||
let unecrypted_data = self.data();
|
||||
let iv = self.iv();
|
||||
let tag = self.tag();
|
@ -1,101 +0,0 @@
|
||||
From 6e48c5ebaa26c6bd2a1bc33ccf36ed8bd6946358 Mon Sep 17 00:00:00 2001
|
||||
From: Marc Hartmayer <mhartmay@linux.ibm.com>
|
||||
Date: Tue, 17 Dec 2024 18:13:31 +0100
|
||||
Subject: [PATCH] pvimg: info: Rename '--key' into '--hdr-key' and use '--key'
|
||||
as an alias
|
||||
MIME-Version: 1.0
|
||||
Content-Type: text/plain; charset=UTF-8
|
||||
Content-Transfer-Encoding: 8bit
|
||||
|
||||
Rename '--key' into '--hdr-key' and use '--key' as an (non-visible)
|
||||
alias for '--hdr-key' in order to keep the command line backwards
|
||||
compatible. The chances of someone using '--key' are very low, as this
|
||||
version has not yet been released by any OS distribution.
|
||||
|
||||
This change makes the command line options for the different subcommands
|
||||
more consistent and therefore easier to use.
|
||||
|
||||
Suggested-by: Reinhard Bündgen <buendgen@de.ibm.com>
|
||||
Acked-by: Hendrik Brueckner <brueckner@linux.ibm.com>
|
||||
Reviewed-by: Steffen Eiden <seiden@linux.ibm.com>
|
||||
Signed-off-by: Marc Hartmayer <mhartmay@linux.ibm.com>
|
||||
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
|
||||
---
|
||||
rust/pvimg/man/pvimg-info.1 | 2 +-
|
||||
rust/pvimg/src/cli.rs | 22 +++++++++++++++++++---
|
||||
rust/pvimg/src/cmd/info.rs | 2 +-
|
||||
3 files changed, 21 insertions(+), 5 deletions(-)
|
||||
|
||||
Index: s390-tools-2.36.0/rust/pvimg/man/pvimg-info.1
|
||||
===================================================================
|
||||
--- s390-tools-2.36.0.orig/rust/pvimg/man/pvimg-info.1
|
||||
+++ s390-tools-2.36.0/rust/pvimg/man/pvimg-info.1
|
||||
@@ -37,7 +37,7 @@ Possible values:
|
||||
.RE
|
||||
.RE
|
||||
.PP
|
||||
-\-\-key <FILE>
|
||||
+\-\-hdr\-key <FILE>
|
||||
.RS 4
|
||||
Use the key in FILE to decrypt the Secure Execution header.
|
||||
.RE
|
||||
Index: s390-tools-2.36.0/rust/pvimg/src/cli.rs
|
||||
===================================================================
|
||||
--- s390-tools-2.36.0.orig/rust/pvimg/src/cli.rs
|
||||
+++ s390-tools-2.36.0/rust/pvimg/src/cli.rs
|
||||
@@ -192,8 +192,8 @@ pub struct InfoArgs {
|
||||
pub format: OutputFormat,
|
||||
|
||||
/// Use the key in FILE to decrypt the Secure Execution header.
|
||||
- #[arg(long, value_name = "FILE", value_hint = ValueHint::FilePath,)]
|
||||
- pub key: Option<PathBuf>,
|
||||
+ #[arg(long, value_name = "FILE", value_hint = ValueHint::FilePath, alias = "key")]
|
||||
+ pub hdr_key: Option<PathBuf>,
|
||||
}
|
||||
|
||||
#[derive(Args, Debug)]
|
||||
@@ -710,6 +710,22 @@ mod test {
|
||||
CliOption::new("image", ["/dev/null"]),
|
||||
],
|
||||
)),
|
||||
+ flat_map_collect(insert(
|
||||
+ args.clone(),
|
||||
+ vec![
|
||||
+ CliOption::new("hdr-key", ["--hdr-key", "/dev/null"]),
|
||||
+ CliOption::new("format", ["--format=json"]),
|
||||
+ CliOption::new("image", ["/dev/null"]),
|
||||
+ ],
|
||||
+ )),
|
||||
+ flat_map_collect(insert(
|
||||
+ args.clone(),
|
||||
+ vec![
|
||||
+ CliOption::new("hdr-key", ["--key", "/dev/null"]),
|
||||
+ CliOption::new("format", ["--format=json"]),
|
||||
+ CliOption::new("image", ["/dev/null"]),
|
||||
+ ],
|
||||
+ )),
|
||||
// separation between keyword and positional args works
|
||||
flat_map_collect(insert(
|
||||
args.clone(),
|
||||
@@ -750,7 +766,7 @@ mod test {
|
||||
|
||||
// Test for invalid combinations
|
||||
// Input is missing
|
||||
- let mut pvimg_invalid_args = vec![vec!["pvimg", "test"]];
|
||||
+ let mut pvimg_invalid_args = vec![vec!["pvimg", "info"]];
|
||||
|
||||
for create_args in &valid_test_args {
|
||||
pvimg_valid_args.push(
|
||||
Index: s390-tools-2.36.0/rust/pvimg/src/cmd/info.rs
|
||||
===================================================================
|
||||
--- s390-tools-2.36.0.orig/rust/pvimg/src/cmd/info.rs
|
||||
+++ s390-tools-2.36.0/rust/pvimg/src/cmd/info.rs
|
||||
@@ -27,7 +27,7 @@ pub fn info(opt: &InfoArgs) -> Result<Ow
|
||||
|
||||
SeHdr::seek_sehdr(&mut input, None)?;
|
||||
let hdr = SeHdr::try_from_io(input)?;
|
||||
- if let Some(key_path) = &opt.key {
|
||||
+ if let Some(key_path) = &opt.hdr_key {
|
||||
let key =
|
||||
SymKey::try_from_data(hdr.key_type(), read_file(key_path, "Reading key")?.into())?;
|
||||
serde_json::to_writer_pretty(&mut output, &hdr.decrypt(&key)?)?;
|
@ -0,0 +1,286 @@
|
||||
Index: s390-tools-service/rust/pv/src/verify.rs
|
||||
===================================================================
|
||||
--- s390-tools-service.orig/rust/pv/src/verify.rs
|
||||
+++ s390-tools-service/rust/pv/src/verify.rs
|
||||
@@ -3,10 +3,11 @@
|
||||
// Copyright IBM Corp. 2023
|
||||
|
||||
use core::slice;
|
||||
-use log::debug;
|
||||
+use log::{debug, trace};
|
||||
+use openssl::error::ErrorStack;
|
||||
use openssl::stack::Stack;
|
||||
use openssl::x509::store::X509Store;
|
||||
-use openssl::x509::{CrlStatus, X509Ref, X509StoreContext, X509};
|
||||
+use openssl::x509::{CrlStatus, X509NameRef, X509Ref, X509StoreContext, X509StoreContextRef, X509};
|
||||
use openssl_extensions::crl::StackableX509Crl;
|
||||
use openssl_extensions::crl::X509StoreContextExtension;
|
||||
|
||||
@@ -82,8 +83,8 @@ impl HkdVerifier for CertVerifier {
|
||||
if verified_crls.is_empty() {
|
||||
bail_hkd_verify!(NoCrl);
|
||||
}
|
||||
- for crl in &verified_crls {
|
||||
- match crl.get_by_cert(&hkd.to_owned()) {
|
||||
+ for crl in verified_crls {
|
||||
+ match crl.get_by_serial(hkd.serial_number()) {
|
||||
CrlStatus::NotRevoked => (),
|
||||
_ => bail_hkd_verify!(HdkRevoked),
|
||||
}
|
||||
@@ -94,21 +95,54 @@ impl HkdVerifier for CertVerifier {
|
||||
}
|
||||
|
||||
impl CertVerifier {
|
||||
+ fn quirk_crls(
|
||||
+ ctx: &mut X509StoreContextRef,
|
||||
+ subject: &X509NameRef,
|
||||
+ ) -> Result<Stack<StackableX509Crl>, ErrorStack> {
|
||||
+ match ctx.crls(subject) {
|
||||
+ Ok(ret) if !ret.is_empty() => return Ok(ret),
|
||||
+ _ => (),
|
||||
+ }
|
||||
+
|
||||
+ // Armonk/Poughkeepsie fixup
|
||||
+ trace!("quirk_crls: Try Locality");
|
||||
+ if let Some(locality_subject) = helper::armonk_locality_fixup(subject) {
|
||||
+ match ctx.crls(&locality_subject) {
|
||||
+ Ok(ret) if !ret.is_empty() => return Ok(ret),
|
||||
+ _ => (),
|
||||
+ }
|
||||
+
|
||||
+ // reorder
|
||||
+ trace!("quirk_crls: Try Locality+Reorder");
|
||||
+ if let Ok(locality_ordered_subject) = helper::reorder_x509_names(&locality_subject) {
|
||||
+ match ctx.crls(&locality_ordered_subject) {
|
||||
+ Ok(ret) if !ret.is_empty() => return Ok(ret),
|
||||
+ _ => (),
|
||||
+ }
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ // reorder unchanged loaciliy subject
|
||||
+ trace!("quirk_crls: Try Reorder");
|
||||
+ if let Ok(ordered_subject) = helper::reorder_x509_names(subject) {
|
||||
+ match ctx.crls(&ordered_subject) {
|
||||
+ Ok(ret) if !ret.is_empty() => return Ok(ret),
|
||||
+ _ => (),
|
||||
+ }
|
||||
+ }
|
||||
+ // nothing found, return empty stack
|
||||
+ Stack::new()
|
||||
+ }
|
||||
+
|
||||
///Download the CLRs that a HKD refers to.
|
||||
pub fn hkd_crls(&self, hkd: &X509Ref) -> Result<Stack<StackableX509Crl>> {
|
||||
let mut ctx = X509StoreContext::new()?;
|
||||
// Unfortunately we cannot use a dedicated function here and have to use a closure (E0434)
|
||||
// Otherwise, we cannot refer to self
|
||||
+ // Search for local CRLs
|
||||
let mut crls = ctx.init_opt(&self.store, None, None, |ctx| {
|
||||
let subject = self.ibm_z_sign_key.subject_name();
|
||||
- match ctx.crls(subject) {
|
||||
- Ok(crls) => Ok(crls),
|
||||
- _ => {
|
||||
- // reorder the name and try again
|
||||
- let broken_subj = helper::reorder_x509_names(subject)?;
|
||||
- ctx.crls(&broken_subj).or_else(helper::stack_err_hlp)
|
||||
- }
|
||||
- }
|
||||
+ Self::quirk_crls(ctx, subject)
|
||||
})?;
|
||||
|
||||
if !self.offline {
|
||||
Index: s390-tools-service/rust/pv/src/verify/helper.rs
|
||||
===================================================================
|
||||
--- s390-tools-service.orig/rust/pv/src/verify/helper.rs
|
||||
+++ s390-tools-service/rust/pv/src/verify/helper.rs
|
||||
@@ -13,7 +13,7 @@ use openssl::{
|
||||
error::ErrorStack,
|
||||
nid::Nid,
|
||||
ssl::SslFiletype,
|
||||
- stack::{Stack, Stackable},
|
||||
+ stack::Stack,
|
||||
x509::{
|
||||
store::{File, X509Lookup, X509StoreBuilder, X509StoreBuilderRef, X509StoreRef},
|
||||
verify::{X509VerifyFlags, X509VerifyParam},
|
||||
@@ -25,6 +25,7 @@ use openssl_extensions::{
|
||||
akid::{AkidCheckResult, AkidExtension},
|
||||
crl::X509StoreExtension,
|
||||
};
|
||||
+use std::str::from_utf8;
|
||||
use std::{cmp::Ordering, ffi::c_int, time::Duration, usize};
|
||||
|
||||
/// Minimum security level for the keys/certificates used to establish a chain of
|
||||
@@ -39,7 +40,6 @@ const SECURITY_CHAIN_MAX_LEN: c_int = 2;
|
||||
/// verifies that the HKD
|
||||
/// * has enough security bits
|
||||
/// * is inside its validity period
|
||||
-/// * issuer name is the subject name of the [`sign_key`]
|
||||
/// * the Authority Key ID matches the Signing Key ID of the [`sign_key`]
|
||||
pub fn verify_hkd_options(hkd: &X509Ref, sign_key: &X509Ref) -> Result<()> {
|
||||
let hk_pkey = hkd.public_key()?;
|
||||
@@ -53,9 +53,6 @@ pub fn verify_hkd_options(hkd: &X509Ref,
|
||||
// verify that the hkd is still valid
|
||||
check_validity_period(hkd.not_before(), hkd.not_after())?;
|
||||
|
||||
- // check if hkd.issuer_name == issuer.subject
|
||||
- check_x509_name_equal(sign_key.subject_name(), hkd.issuer_name())?;
|
||||
-
|
||||
// verify that the AKID of the hkd matches the SKID of the issuer
|
||||
if let Some(akid) = hkd.akid() {
|
||||
if akid.check(sign_key) != AkidCheckResult::OK {
|
||||
@@ -75,9 +72,6 @@ pub fn verify_crl(crl: &X509CrlRef, issu
|
||||
return None;
|
||||
}
|
||||
}
|
||||
-
|
||||
- check_x509_name_equal(crl.issuer_name(), issuer.subject_name()).ok()?;
|
||||
-
|
||||
match crl.verify(issuer.public_key().ok()?.as_ref()).ok()? {
|
||||
true => Some(()),
|
||||
false => None,
|
||||
@@ -207,7 +201,8 @@ pub fn download_crls_into_store(store: &
|
||||
//Asn1StringRef::as_slice aka ASN1_STRING_get0_data gives a string without \0 delimiter
|
||||
const IBM_Z_COMMON_NAME: &[u8; 43usize] = b"International Business Machines Corporation";
|
||||
const IBM_Z_COUNTRY_NAME: &[u8; 2usize] = b"US";
|
||||
-const IBM_Z_LOCALITY_NAME: &[u8; 12usize] = b"Poughkeepsie";
|
||||
+const IBM_Z_LOCALITY_NAME_POUGHKEEPSIE: &[u8; 12usize] = b"Poughkeepsie";
|
||||
+const IBM_Z_LOCALITY_NAME_ARMONK: &[u8; 6usize] = b"Armonk";
|
||||
const IBM_Z_ORGANIZATIONAL_UNIT_NAME_SUFFIX: &str = "Key Signing Service";
|
||||
const IBM_Z_ORGANIZATION_NAME: &[u8; 43usize] = b"International Business Machines Corporation";
|
||||
const IBM_Z_STATE: &[u8; 8usize] = b"New York";
|
||||
@@ -226,7 +221,8 @@ fn is_ibm_signing_cert(cert: &X509) -> b
|
||||
if subj.entries().count() != IMB_Z_ENTRY_COUNT
|
||||
|| !name_data_eq(subj, Nid::COUNTRYNAME, IBM_Z_COUNTRY_NAME)
|
||||
|| !name_data_eq(subj, Nid::STATEORPROVINCENAME, IBM_Z_STATE)
|
||||
- || !name_data_eq(subj, Nid::LOCALITYNAME, IBM_Z_LOCALITY_NAME)
|
||||
+ || !(name_data_eq(subj, Nid::LOCALITYNAME, IBM_Z_LOCALITY_NAME_POUGHKEEPSIE)
|
||||
+ || name_data_eq(subj, Nid::LOCALITYNAME, IBM_Z_LOCALITY_NAME_ARMONK))
|
||||
|| !name_data_eq(subj, Nid::ORGANIZATIONNAME, IBM_Z_ORGANIZATION_NAME)
|
||||
|| !name_data_eq(subj, Nid::COMMONNAME, IBM_Z_COMMON_NAME)
|
||||
{
|
||||
@@ -367,24 +363,6 @@ fn check_validity_period(not_before: &As
|
||||
}
|
||||
}
|
||||
|
||||
-fn check_x509_name_equal(lhs: &X509NameRef, rhs: &X509NameRef) -> Result<()> {
|
||||
- if lhs.entries().count() != rhs.entries().count() {
|
||||
- bail_hkd_verify!(IssuerMismatch);
|
||||
- }
|
||||
-
|
||||
- for l in lhs.entries() {
|
||||
- // search for the matching value in the rhs names
|
||||
- // found none? -> names are not equal
|
||||
- if !rhs
|
||||
- .entries()
|
||||
- .any(|r| l.data().as_slice() == r.data().as_slice())
|
||||
- {
|
||||
- bail_hkd_verify!(IssuerMismatch);
|
||||
- }
|
||||
- }
|
||||
- Ok(())
|
||||
-}
|
||||
-
|
||||
const NIDS_CORRECT_ORDER: [Nid; 6] = [
|
||||
Nid::COUNTRYNAME,
|
||||
Nid::ORGANIZATIONNAME,
|
||||
@@ -407,13 +385,28 @@ pub fn reorder_x509_names(subject: &X509
|
||||
Ok(correct_subj.build())
|
||||
}
|
||||
|
||||
-pub fn stack_err_hlp<T: Stackable>(
|
||||
- e: ErrorStack,
|
||||
-) -> std::result::Result<Stack<T>, openssl::error::ErrorStack> {
|
||||
- match e.errors().len() {
|
||||
- 0 => Stack::<T>::new(),
|
||||
- _ => Err(e),
|
||||
+/**
|
||||
+* Workaround for potential locality mismatches between CRLs and Certs
|
||||
+* # Return
|
||||
+* fixed subject or none if locality was not Armonk or any OpenSSL error
|
||||
+*/
|
||||
+pub fn armonk_locality_fixup(subject: &X509NameRef) -> Option<X509Name> {
|
||||
+ if !name_data_eq(subject, Nid::LOCALITYNAME, IBM_Z_LOCALITY_NAME_ARMONK) {
|
||||
+ return None;
|
||||
+ }
|
||||
+
|
||||
+ let mut ret = X509Name::builder().ok()?;
|
||||
+ for entry in subject.entries() {
|
||||
+ match entry.object().nid() {
|
||||
+ nid @ Nid::LOCALITYNAME => ret
|
||||
+ .append_entry_by_nid(nid, from_utf8(IBM_Z_LOCALITY_NAME_POUGHKEEPSIE).ok()?)
|
||||
+ .ok()?,
|
||||
+ _ => {
|
||||
+ ret.append_entry(entry).ok()?;
|
||||
+ }
|
||||
+ }
|
||||
}
|
||||
+ Some(ret.build())
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
@@ -451,20 +444,6 @@ mod test {
|
||||
));
|
||||
}
|
||||
|
||||
- #[test]
|
||||
- fn x509_name_equal() {
|
||||
- let sign_crt = load_gen_cert("ibm.crt");
|
||||
- let hkd = load_gen_cert("host.crt");
|
||||
- let other = load_gen_cert("inter_ca.crt");
|
||||
-
|
||||
- assert!(super::check_x509_name_equal(sign_crt.subject_name(), hkd.issuer_name()).is_ok(),);
|
||||
-
|
||||
- assert!(matches!(
|
||||
- super::check_x509_name_equal(other.subject_name(), hkd.subject_name()),
|
||||
- Err(Error::HkdVerify(IssuerMismatch))
|
||||
- ));
|
||||
- }
|
||||
-
|
||||
#[test]
|
||||
fn is_ibm_z_sign_key() {
|
||||
let ibm_crt = load_gen_cert("ibm.crt");
|
||||
Index: s390-tools-service/rust/pv/src/verify/test.rs
|
||||
===================================================================
|
||||
--- s390-tools-service.orig/rust/pv/src/verify/test.rs
|
||||
+++ s390-tools-service/rust/pv/src/verify/test.rs
|
||||
@@ -84,7 +84,6 @@ fn verify_online() {
|
||||
let inter_crt = get_cert_asset_path_string("inter_ca.crt");
|
||||
let ibm_crt = get_cert_asset_path_string("ibm.crt");
|
||||
let hkd_revoked = load_gen_cert("host_rev.crt");
|
||||
- let hkd_inv = load_gen_cert("host_invalid_signing_key.crt");
|
||||
let hkd_exp = load_gen_cert("host_crt_expired.crt");
|
||||
let hkd = load_gen_cert("host.crt");
|
||||
|
||||
@@ -112,11 +111,6 @@ fn verify_online() {
|
||||
));
|
||||
|
||||
assert!(matches!(
|
||||
- verifier.verify(&hkd_inv),
|
||||
- Err(Error::HkdVerify(IssuerMismatch))
|
||||
- ));
|
||||
-
|
||||
- assert!(matches!(
|
||||
verifier.verify(&hkd_exp),
|
||||
Err(Error::HkdVerify(AfterValidity))
|
||||
));
|
||||
@@ -130,7 +124,6 @@ fn verify_offline() {
|
||||
let ibm_crt = get_cert_asset_path_string("ibm.crt");
|
||||
let ibm_crl = get_cert_asset_path_string("ibm.crl");
|
||||
let hkd_revoked = load_gen_cert("host_rev.crt");
|
||||
- let hkd_inv = load_gen_cert("host_invalid_signing_key.crt");
|
||||
let hkd_exp = load_gen_cert("host_crt_expired.crt");
|
||||
let hkd = load_gen_cert("host.crt");
|
||||
|
||||
@@ -149,11 +142,6 @@ fn verify_offline() {
|
||||
));
|
||||
|
||||
assert!(matches!(
|
||||
- verifier.verify(&hkd_inv),
|
||||
- Err(Error::HkdVerify(IssuerMismatch))
|
||||
- ));
|
||||
-
|
||||
- assert!(matches!(
|
||||
verifier.verify(&hkd_exp),
|
||||
Err(Error::HkdVerify(AfterValidity))
|
||||
));
|
@ -0,0 +1,97 @@
|
||||
From 3ea6d6dfd2eb120ffee4c44ff51b7e9e7a9097a6 Mon Sep 17 00:00:00 2001
|
||||
From: Thomas Blume <Thomas.Blume@suse.com>
|
||||
Date: Thu, 28 Mar 2024 13:32:46 +0100
|
||||
Subject: [PATCH] parse ipl device for activation
|
||||
|
||||
ported from dracut modules
|
||||
---
|
||||
zdev/dracut/95zdev/parse-dasd.sh | 15 ++++++++---
|
||||
zdev/dracut/95zdev/parse-zfcp.sh | 46 +++++++++++++++++++-------------
|
||||
2 files changed, 39 insertions(+), 22 deletions(-)
|
||||
|
||||
diff --git a/zdev/dracut/95zdev/parse-dasd.sh b/zdev/dracut/95zdev/parse-dasd.sh
|
||||
index a97801f..eb2fa64 100644
|
||||
--- a/zdev/dracut/95zdev/parse-dasd.sh
|
||||
+++ b/zdev/dracut/95zdev/parse-dasd.sh
|
||||
@@ -27,9 +27,18 @@ zdev_vinfo() {
|
||||
|
||||
zdev_parse_rd_dasd() {
|
||||
local _zdev_dasd _zdev_dasd_list
|
||||
- for _zdev_dasd in $(getargs rd.dasd -d 'rd_DASD='); do
|
||||
- _zdev_dasd_list="${_zdev_dasd_list:+${_zdev_dasd_list},}$_zdev_dasd"
|
||||
- done
|
||||
+ # autodetect active bootdev from zipl device
|
||||
+ if ! getargbool 0 'rd.dasd' \
|
||||
+ && [[ -f /sys/firmware/ipl/ipl_type ]] \
|
||||
+ && [[ $(< /sys/firmware/ipl/ipl_type) == "ccw" ]]; then
|
||||
+ read -r _ccw < /sys/firmware/ipl/device
|
||||
+
|
||||
+ chzdev --offline --existing --enable --active dasd "$_ccw"
|
||||
+ else
|
||||
+ for _zdev_dasd in $(getargs rd.dasd -d 'rd_DASD='); do
|
||||
+ _zdev_dasd_list="${_zdev_dasd_list:+${_zdev_dasd_list},}$_zdev_dasd"
|
||||
+ done
|
||||
+ fi
|
||||
echo "$_zdev_dasd_list"
|
||||
}
|
||||
|
||||
diff --git a/zdev/dracut/95zdev/parse-zfcp.sh b/zdev/dracut/95zdev/parse-zfcp.sh
|
||||
index 715aa00..6279beb 100644
|
||||
--- a/zdev/dracut/95zdev/parse-zfcp.sh
|
||||
+++ b/zdev/dracut/95zdev/parse-zfcp.sh
|
||||
@@ -12,25 +12,33 @@
|
||||
|
||||
zdev_zfcp_base_args="--no-settle --yes --no-root-update --force"
|
||||
|
||||
-for zdev_zfcp_arg in $(getargs rd.zfcp -d 'rd_ZFCP='); do
|
||||
- (
|
||||
- IFS_SAVED="$IFS"
|
||||
- IFS="," # did not work in front of built-in set command below
|
||||
- # shellcheck disable=SC2086
|
||||
- set -- $zdev_zfcp_arg
|
||||
- IFS=":" args="$*"
|
||||
- IFS="$IFS_SAVED"
|
||||
- echo "rd.zfcp ${zdev_zfcp_arg} :" | zdev_vinfo
|
||||
- if [ "$#" -eq 1 ]; then
|
||||
+# autodetect active bootdev from zipl device
|
||||
+if ! getargbool 0 'rd.zfcp' \
|
||||
+ && [[ -f /sys/firmware/ipl/ipl_type ]] \
|
||||
+ && [[ $(< /sys/firmware/ipl/ipl_type) == "fcp" ]]; then
|
||||
+ chzdev --offline --existing --enable --active zfcp-host 2>&1 | zdev_vinfo
|
||||
+else
|
||||
+ for zdev_zfcp_arg in $(getargs rd.zfcp -d 'rd_ZFCP='); do
|
||||
+ (
|
||||
+ IFS_SAVED="$IFS"
|
||||
+ IFS="," # did not work in front of built-in set command below
|
||||
# shellcheck disable=SC2086
|
||||
- chzdev --enable --persistent $zdev_zfcp_base_args \
|
||||
- zfcp-host "$args" 2>&1 | zdev_vinfo
|
||||
- else
|
||||
- # shellcheck disable=SC2086
|
||||
- chzdev --enable --persistent $zdev_zfcp_base_args \
|
||||
- zfcp-lun "$args" 2>&1 | zdev_vinfo
|
||||
- fi
|
||||
- )
|
||||
-done
|
||||
+ set -- $zdev_zfcp_arg
|
||||
+ IFS=":" args="$*"
|
||||
+ IFS="$IFS_SAVED"
|
||||
+ echo "rd.zfcp ${zdev_zfcp_arg} :" | zdev_vinfo
|
||||
+ if [ "$#" -eq 1 ]; then
|
||||
+ # shellcheck disable=SC2086
|
||||
+ chzdev --enable --persistent $zdev_zfcp_base_args \
|
||||
+ zfcp-host "$args" 2>&1 | zdev_vinfo
|
||||
+ else
|
||||
+ # shellcheck disable=SC2086
|
||||
+ chzdev --enable --persistent $zdev_zfcp_base_args \
|
||||
+ zfcp-lun "$args" 2>&1 | zdev_vinfo
|
||||
+ fi
|
||||
+ )
|
||||
+ done
|
||||
+fi
|
||||
+
|
||||
unset zdev_zfcp_arg
|
||||
unset zdev_zfcp_base_args
|
||||
--
|
||||
2.44.0
|
||||
|
@ -0,0 +1,304 @@
|
||||
Index: s390-tools-service/genprotimg/src/include/pv_crypto_def.h
|
||||
===================================================================
|
||||
--- s390-tools-service.orig/genprotimg/src/include/pv_crypto_def.h
|
||||
+++ s390-tools-service/genprotimg/src/include/pv_crypto_def.h
|
||||
@@ -17,7 +17,8 @@
|
||||
/* IBM signing key subject */
|
||||
#define PV_IBM_Z_SUBJECT_COMMON_NAME "International Business Machines Corporation"
|
||||
#define PV_IBM_Z_SUBJECT_COUNTRY_NAME "US"
|
||||
-#define PV_IBM_Z_SUBJECT_LOCALITY_NAME "Poughkeepsie"
|
||||
+#define PV_IBM_Z_SUBJECT_LOCALITY_NAME_POUGHKEEPSIE "Poughkeepsie"
|
||||
+#define PV_IBM_Z_SUBJECT_LOCALITY_NAME_ARMONK "Armonk"
|
||||
#define PV_IBM_Z_SUBJECT_ORGANIZATIONONAL_UNIT_NAME_SUFFIX "Key Signing Service"
|
||||
#define PV_IBM_Z_SUBJECT_ORGANIZATION_NAME "International Business Machines Corporation"
|
||||
#define PV_IBM_Z_SUBJECT_STATE "New York"
|
||||
Index: s390-tools-service/genprotimg/src/utils/crypto.c
|
||||
===================================================================
|
||||
--- s390-tools-service.orig/genprotimg/src/utils/crypto.c
|
||||
+++ s390-tools-service/genprotimg/src/utils/crypto.c
|
||||
@@ -664,62 +664,9 @@ static gboolean x509_name_data_by_nid_eq
|
||||
return memcmp(data, y, data_len) == 0;
|
||||
}
|
||||
|
||||
-static gboolean own_X509_NAME_ENTRY_equal(const X509_NAME_ENTRY *x,
|
||||
- const X509_NAME_ENTRY *y)
|
||||
-{
|
||||
- const ASN1_OBJECT *x_obj = X509_NAME_ENTRY_get_object(x);
|
||||
- const ASN1_STRING *x_data = X509_NAME_ENTRY_get_data(x);
|
||||
- const ASN1_OBJECT *y_obj = X509_NAME_ENTRY_get_object(y);
|
||||
- const ASN1_STRING *y_data = X509_NAME_ENTRY_get_data(y);
|
||||
- gint x_len = ASN1_STRING_length(x_data);
|
||||
- gint y_len = ASN1_STRING_length(y_data);
|
||||
-
|
||||
- if (x_len < 0 || x_len != y_len)
|
||||
- return FALSE;
|
||||
-
|
||||
- /* ASN1_STRING_cmp(x_data, y_data) == 0 doesn't work because it also
|
||||
- * compares the type, which is sometimes different.
|
||||
- */
|
||||
- return OBJ_cmp(x_obj, y_obj) == 0 &&
|
||||
- memcmp(ASN1_STRING_get0_data(x_data),
|
||||
- ASN1_STRING_get0_data(y_data),
|
||||
- (unsigned long)x_len) == 0;
|
||||
-}
|
||||
-
|
||||
-static gboolean own_X509_NAME_equal(const X509_NAME *x, const X509_NAME *y)
|
||||
-{
|
||||
- gint x_count = X509_NAME_entry_count(x);
|
||||
- gint y_count = X509_NAME_entry_count(y);
|
||||
-
|
||||
- if (x != y && (!x || !y))
|
||||
- return FALSE;
|
||||
-
|
||||
- if (x_count != y_count)
|
||||
- return FALSE;
|
||||
-
|
||||
- for (gint i = 0; i < x_count; i++) {
|
||||
- const X509_NAME_ENTRY *entry_i = X509_NAME_get_entry(x, i);
|
||||
- gboolean entry_found = FALSE;
|
||||
-
|
||||
- for (gint j = 0; j < y_count; j++) {
|
||||
- const X509_NAME_ENTRY *entry_j =
|
||||
- X509_NAME_get_entry(y, j);
|
||||
-
|
||||
- if (own_X509_NAME_ENTRY_equal(entry_i, entry_j)) {
|
||||
- entry_found = TRUE;
|
||||
- break;
|
||||
- }
|
||||
- }
|
||||
-
|
||||
- if (!entry_found)
|
||||
- return FALSE;
|
||||
- }
|
||||
- return TRUE;
|
||||
-}
|
||||
-
|
||||
/* Checks whether the subject of @cert is a IBM signing key subject. For this we
|
||||
* must check that the subject is equal to: 'C = US, ST = New York, L =
|
||||
- * Poughkeepsie, O = International Business Machines Corporation, CN =
|
||||
+ * Poughkeepsie or Armonk, O = International Business Machines Corporation, CN =
|
||||
* International Business Machines Corporation' and the organization unit (OUT)
|
||||
* must end with the suffix ' Key Signing Service'.
|
||||
*/
|
||||
@@ -743,8 +690,10 @@ static gboolean has_ibm_signing_subject(
|
||||
PV_IBM_Z_SUBJECT_STATE))
|
||||
return FALSE;
|
||||
|
||||
- if (!x509_name_data_by_nid_equal(subject, NID_localityName,
|
||||
- PV_IBM_Z_SUBJECT_LOCALITY_NAME))
|
||||
+ if (!(x509_name_data_by_nid_equal(subject, NID_localityName,
|
||||
+ PV_IBM_Z_SUBJECT_LOCALITY_NAME_POUGHKEEPSIE) ||
|
||||
+ x509_name_data_by_nid_equal(subject, NID_localityName,
|
||||
+ PV_IBM_Z_SUBJECT_LOCALITY_NAME_ARMONK)))
|
||||
return FALSE;
|
||||
|
||||
if (!x509_name_data_by_nid_equal(subject, NID_organizationName,
|
||||
@@ -806,6 +755,39 @@ static X509_NAME *x509_name_reorder_attr
|
||||
return g_steal_pointer(&ret);
|
||||
}
|
||||
|
||||
+/** Replace locality 'Armonk' with 'Pougkeepsie'. If Armonk was not set return
|
||||
+ * `NULL`.
|
||||
+ */
|
||||
+static X509_NAME *x509_armonk_locality_fixup(const X509_NAME *name)
|
||||
+{
|
||||
+ g_autoptr(X509_NAME) ret = NULL;
|
||||
+ int pos;
|
||||
+
|
||||
+ /* Check if ``L=Armonk`` */
|
||||
+ if (!x509_name_data_by_nid_equal((X509_NAME *)name, NID_localityName,
|
||||
+ PV_IBM_Z_SUBJECT_LOCALITY_NAME_ARMONK))
|
||||
+ return NULL;
|
||||
+
|
||||
+ ret = X509_NAME_dup(name);
|
||||
+ if (!ret)
|
||||
+ g_abort();
|
||||
+
|
||||
+ pos = X509_NAME_get_index_by_NID(ret, NID_localityName, -1);
|
||||
+ if (pos == -1)
|
||||
+ return NULL;
|
||||
+
|
||||
+ X509_NAME_ENTRY_free(X509_NAME_delete_entry(ret, pos));
|
||||
+
|
||||
+ /* Create a new name entry at the same position as before */
|
||||
+ if (X509_NAME_add_entry_by_NID(
|
||||
+ ret, NID_localityName, MBSTRING_UTF8,
|
||||
+ (const unsigned char *)&PV_IBM_Z_SUBJECT_LOCALITY_NAME_POUGHKEEPSIE,
|
||||
+ sizeof(PV_IBM_Z_SUBJECT_LOCALITY_NAME_POUGHKEEPSIE) - 1, pos, 0) != 1)
|
||||
+ return NULL;
|
||||
+
|
||||
+ return g_steal_pointer(&ret);
|
||||
+}
|
||||
+
|
||||
/* In RFC 5280 the attributes of a (subject/issuer) name is not mandatory
|
||||
* ordered. The problem is that our certificates are not consistent in the order
|
||||
* (see https://tools.ietf.org/html/rfc5280#section-4.1.2.4 for details).
|
||||
@@ -828,24 +810,10 @@ X509_NAME *c2b_name(const X509_NAME *nam
|
||||
return X509_NAME_dup((X509_NAME *)name);
|
||||
}
|
||||
|
||||
-/* Verify that: subject(issuer) == issuer(crl) and SKID(issuer) == AKID(crl) */
|
||||
+/* Verify that SKID(issuer) == AKID(crl) if available */
|
||||
static gint check_crl_issuer(X509_CRL *crl, X509 *issuer, GError **err)
|
||||
{
|
||||
- const X509_NAME *crl_issuer = X509_CRL_get_issuer(crl);
|
||||
- const X509_NAME *issuer_subject = X509_get_subject_name(issuer);
|
||||
- AUTHORITY_KEYID *akid = NULL;
|
||||
-
|
||||
- if (!own_X509_NAME_equal(issuer_subject, crl_issuer)) {
|
||||
- g_autofree char *issuer_subject_str = X509_NAME_oneline(issuer_subject,
|
||||
- NULL, 0);
|
||||
- g_autofree char *crl_issuer_str = X509_NAME_oneline(crl_issuer, NULL, 0);
|
||||
-
|
||||
- g_set_error(err, PV_CRYPTO_ERROR,
|
||||
- PV_CRYPTO_ERROR_CRL_SUBJECT_ISSUER_MISMATCH,
|
||||
- _("issuer mismatch:\n%s\n%s"),
|
||||
- issuer_subject_str, crl_issuer_str);
|
||||
- return -1;
|
||||
- }
|
||||
+ g_autoptr(AUTHORITY_KEYID) akid = NULL;
|
||||
|
||||
/* If AKID(@crl) is specified it must match with SKID(@issuer) */
|
||||
akid = X509_CRL_get_ext_d2i(crl, NID_authority_key_identifier, NULL, NULL);
|
||||
@@ -881,7 +849,6 @@ gint check_crl_valid_for_cert(X509_CRL *
|
||||
return -1;
|
||||
}
|
||||
|
||||
- /* check that the @crl issuer matches with the subject name of @cert*/
|
||||
if (check_crl_issuer(crl, cert, err) < 0)
|
||||
return -1;
|
||||
|
||||
@@ -910,6 +877,60 @@ gint check_crl_valid_for_cert(X509_CRL *
|
||||
return 0;
|
||||
}
|
||||
|
||||
+/* This function contains work-arounds for some known subject(CRT)<->issuer(CRL)
|
||||
+ * issues.
|
||||
+ */
|
||||
+static STACK_OF_X509_CRL *quirk_X509_STORE_ctx_get1_crls(X509_STORE_CTX *ctx,
|
||||
+ const X509_NAME *subject, GError **err)
|
||||
+{
|
||||
+ g_autoptr(X509_NAME) fixed_subject = NULL;
|
||||
+ g_autoptr(STACK_OF_X509_CRL) ret = NULL;
|
||||
+
|
||||
+ ret = Pv_X509_STORE_CTX_get1_crls(ctx, subject);
|
||||
+ if (ret && sk_X509_CRL_num(ret) > 0)
|
||||
+ return g_steal_pointer(&ret);
|
||||
+
|
||||
+ /* Workaround to fix the mismatch between issuer name of the * IBM
|
||||
+ * signing CRLs and the IBM signing key subject name. Locality name has
|
||||
+ * changed from Poughkeepsie to Armonk.
|
||||
+ */
|
||||
+ fixed_subject = x509_armonk_locality_fixup(subject);
|
||||
+ /* Was the locality replaced? */
|
||||
+ if (fixed_subject) {
|
||||
+ X509_NAME *tmp;
|
||||
+
|
||||
+ sk_X509_CRL_free(ret);
|
||||
+ ret = Pv_X509_STORE_CTX_get1_crls(ctx, fixed_subject);
|
||||
+ if (ret && sk_X509_CRL_num(ret) > 0)
|
||||
+ return g_steal_pointer(&ret);
|
||||
+
|
||||
+ /* Workaround to fix the ordering mismatch between issuer name
|
||||
+ * of the IBM signing CRLs and the IBM signing key subject name.
|
||||
+ */
|
||||
+ tmp = fixed_subject;
|
||||
+ fixed_subject = c2b_name(fixed_subject);
|
||||
+ X509_NAME_free(tmp);
|
||||
+ sk_X509_CRL_free(ret);
|
||||
+ ret = Pv_X509_STORE_CTX_get1_crls(ctx, fixed_subject);
|
||||
+ if (ret && sk_X509_CRL_num(ret) > 0)
|
||||
+ return g_steal_pointer(&ret);
|
||||
+ X509_NAME_free(fixed_subject);
|
||||
+ fixed_subject = NULL;
|
||||
+ }
|
||||
+
|
||||
+ /* Workaround to fix the ordering mismatch between issuer name of the
|
||||
+ * IBM signing CRLs and the IBM signing key subject name.
|
||||
+ */
|
||||
+ fixed_subject = c2b_name(subject);
|
||||
+ sk_X509_CRL_free(ret);
|
||||
+ ret = Pv_X509_STORE_CTX_get1_crls(ctx, fixed_subject);
|
||||
+ if (ret && sk_X509_CRL_num(ret) > 0)
|
||||
+ return g_steal_pointer(&ret);
|
||||
+
|
||||
+ g_set_error(err, PV_CRYPTO_ERROR, PV_CRYPTO_ERROR_NO_CRL, _("no CRL found"));
|
||||
+ return NULL;
|
||||
+}
|
||||
+
|
||||
/* Given a certificate @cert try to find valid revocation lists in @ctx. If no
|
||||
* valid CRL was found NULL is returned.
|
||||
*/
|
||||
@@ -927,20 +948,9 @@ STACK_OF_X509_CRL *store_ctx_find_valid_
|
||||
return NULL;
|
||||
}
|
||||
|
||||
- ret = X509_STORE_CTX_get1_crls(ctx, subject);
|
||||
- if (!ret) {
|
||||
- /* Workaround to fix the mismatch between issuer name of the
|
||||
- * IBM Z signing CRLs and the IBM Z signing key subject name.
|
||||
- */
|
||||
- g_autoptr(X509_NAME) broken_subject = c2b_name(subject);
|
||||
-
|
||||
- ret = X509_STORE_CTX_get1_crls(ctx, broken_subject);
|
||||
- if (!ret) {
|
||||
- g_set_error(err, PV_CRYPTO_ERROR, PV_CRYPTO_ERROR_NO_CRL,
|
||||
- _("no CRL found"));
|
||||
- return NULL;
|
||||
- }
|
||||
- }
|
||||
+ ret = quirk_X509_STORE_ctx_get1_crls(ctx, subject, err);
|
||||
+ if (!ret)
|
||||
+ return NULL;
|
||||
|
||||
/* Filter out non-valid CRLs for @cert */
|
||||
for (gint i = 0; i < sk_X509_CRL_num(ret); i++) {
|
||||
@@ -1328,32 +1338,14 @@ gint check_chain_parameters(const STACK_
|
||||
|
||||
/* It's almost the same as X509_check_issed from OpenSSL does except that we
|
||||
* don't check the key usage of the potential issuer. This means we check:
|
||||
- * 1. issuer_name(cert) == subject_name(issuer)
|
||||
- * 2. Check whether the akid(cert) (if available) matches the issuer skid
|
||||
- * 3. Check that the cert algrithm matches the subject algorithm
|
||||
- * 4. Verify the signature of certificate @cert is using the public key of
|
||||
+ * 1. Check whether the akid(cert) (if available) matches the issuer skid
|
||||
+ * 2. Check that the cert algrithm matches the subject algorithm
|
||||
+ * 3. Verify the signature of certificate @cert is using the public key of
|
||||
* @issuer.
|
||||
*/
|
||||
static gint check_host_key_issued(X509 *cert, X509 *issuer, GError **err)
|
||||
{
|
||||
- const X509_NAME *issuer_subject = X509_get_subject_name(issuer);
|
||||
- const X509_NAME *cert_issuer = X509_get_issuer_name(cert);
|
||||
- AUTHORITY_KEYID *akid = NULL;
|
||||
-
|
||||
- /* We cannot use X509_NAME_cmp() because it considers the order of the
|
||||
- * X509_NAME_Entries.
|
||||
- */
|
||||
- if (!own_X509_NAME_equal(issuer_subject, cert_issuer)) {
|
||||
- g_autofree char *issuer_subject_str =
|
||||
- X509_NAME_oneline(issuer_subject, NULL, 0);
|
||||
- g_autofree char *cert_issuer_str =
|
||||
- X509_NAME_oneline(cert_issuer, NULL, 0);
|
||||
- g_set_error(err, PV_CRYPTO_ERROR,
|
||||
- PV_CRYPTO_ERROR_CERT_SUBJECT_ISSUER_MISMATCH,
|
||||
- _("Subject issuer mismatch:\n'%s'\n'%s'"),
|
||||
- issuer_subject_str, cert_issuer_str);
|
||||
- return -1;
|
||||
- }
|
||||
+ g_autoptr(AUTHORITY_KEYID) akid = NULL;
|
||||
|
||||
akid = X509_get_ext_d2i(cert, NID_authority_key_identifier, NULL, NULL);
|
||||
if (akid && X509_check_akid(issuer, akid) != X509_V_OK) {
|
||||
Index: s390-tools-service/genprotimg/src/utils/crypto.h
|
||||
===================================================================
|
||||
--- s390-tools-service.orig/genprotimg/src/utils/crypto.h
|
||||
+++ s390-tools-service/genprotimg/src/utils/crypto.h
|
||||
@@ -75,6 +75,7 @@ void x509_pair_free(x509_pair *pair);
|
||||
/* Register auto cleanup functions */
|
||||
WRAPPED_G_DEFINE_AUTOPTR_CLEANUP_FUNC(ASN1_INTEGER, ASN1_INTEGER_free)
|
||||
WRAPPED_G_DEFINE_AUTOPTR_CLEANUP_FUNC(ASN1_OCTET_STRING, ASN1_OCTET_STRING_free)
|
||||
+WRAPPED_G_DEFINE_AUTOPTR_CLEANUP_FUNC(AUTHORITY_KEYID, AUTHORITY_KEYID_free)
|
||||
WRAPPED_G_DEFINE_AUTOPTR_CLEANUP_FUNC(BIGNUM, BN_free)
|
||||
WRAPPED_G_DEFINE_AUTOPTR_CLEANUP_FUNC(BIO, BIO_free_all)
|
||||
WRAPPED_G_DEFINE_AUTOPTR_CLEANUP_FUNC(BN_CTX, BN_CTX_free)
|
@ -0,0 +1,224 @@
|
||||
Index: s390-tools-service/include/libpv/cert.h
|
||||
===================================================================
|
||||
--- s390-tools-service.orig/include/libpv/cert.h
|
||||
+++ s390-tools-service/include/libpv/cert.h
|
||||
@@ -16,7 +16,8 @@
|
||||
|
||||
#define PV_IBM_Z_SUBJECT_COMMON_NAME "International Business Machines Corporation"
|
||||
#define PV_IBM_Z_SUBJECT_COUNTRY_NAME "US"
|
||||
-#define PV_IBM_Z_SUBJECT_LOCALITY_NAME "Poughkeepsie"
|
||||
+#define PV_IBM_Z_SUBJECT_LOCALITY_NAME_POUGHKEEPSIE "Poughkeepsie"
|
||||
+#define PV_IBM_Z_SUBJECT_LOCALITY_NAME_ARMONK "Armonk"
|
||||
#define PV_IBM_Z_SUBJECT_ORGANIZATIONAL_UNIT_NAME_SUFFIX "Key Signing Service"
|
||||
#define PV_IBM_Z_SUBJECT_ORGANIZATION_NAME "International Business Machines Corporation"
|
||||
#define PV_IBM_Z_SUBJECT_STATE "New York"
|
||||
Index: s390-tools-service/libpv/cert.c
|
||||
===================================================================
|
||||
--- s390-tools-service.orig/libpv/cert.c
|
||||
+++ s390-tools-service/libpv/cert.c
|
||||
@@ -857,7 +857,7 @@ static gboolean x509_name_data_by_nid_eq
|
||||
|
||||
/* Checks whether the subject of @cert is a IBM signing key subject. For this we
|
||||
* must check that the subject is equal to: 'C = US, ST = New York, L =
|
||||
- * Poughkeepsie, O = International Business Machines Corporation, CN =
|
||||
+ * Poughkeepsie or Armonk, O = International Business Machines Corporation, CN =
|
||||
* International Business Machines Corporation' and the organization unit (OUT)
|
||||
* must end with the suffix ' Key Signing Service'.
|
||||
*/
|
||||
@@ -879,7 +879,10 @@ static gboolean has_ibm_signing_subject(
|
||||
if (!x509_name_data_by_nid_equal(subject, NID_stateOrProvinceName, PV_IBM_Z_SUBJECT_STATE))
|
||||
return FALSE;
|
||||
|
||||
- if (!x509_name_data_by_nid_equal(subject, NID_localityName, PV_IBM_Z_SUBJECT_LOCALITY_NAME))
|
||||
+ if (!(x509_name_data_by_nid_equal(subject, NID_localityName,
|
||||
+ PV_IBM_Z_SUBJECT_LOCALITY_NAME_POUGHKEEPSIE) ||
|
||||
+ x509_name_data_by_nid_equal(subject, NID_localityName,
|
||||
+ PV_IBM_Z_SUBJECT_LOCALITY_NAME_ARMONK)))
|
||||
return FALSE;
|
||||
|
||||
if (!x509_name_data_by_nid_equal(subject, NID_organizationName,
|
||||
@@ -1085,10 +1088,9 @@ static int check_signature_algo_match(co
|
||||
|
||||
/* It's almost the same as X509_check_issed from OpenSSL does except that we
|
||||
* don't check the key usage of the potential issuer. This means we check:
|
||||
- * 1. issuer_name(cert) == subject_name(issuer)
|
||||
- * 2. Check whether the akid(cert) (if available) matches the issuer skid
|
||||
- * 3. Check that the cert algrithm matches the subject algorithm
|
||||
- * 4. Verify the signature of certificate @cert is using the public key of
|
||||
+ * 1. Check whether the akid(cert) (if available) matches the issuer skid
|
||||
+ * 2. Check that the cert algrithm matches the subject algorithm
|
||||
+ * 3. Verify the signature of certificate @cert is using the public key of
|
||||
* @issuer.
|
||||
*/
|
||||
static int check_host_key_issued(X509 *cert, X509 *issuer, GError **error)
|
||||
@@ -1097,19 +1099,6 @@ static int check_host_key_issued(X509 *c
|
||||
const X509_NAME *cert_issuer = X509_get_issuer_name(cert);
|
||||
g_autoptr(AUTHORITY_KEYID) akid = NULL;
|
||||
|
||||
- /* We cannot use X509_NAME_cmp() because it considers the order of the
|
||||
- * X509_NAME_Entries.
|
||||
- */
|
||||
- if (!own_X509_NAME_equal(issuer_subject, cert_issuer)) {
|
||||
- g_autofree char *issuer_subject_str = pv_X509_NAME_oneline(issuer_subject);
|
||||
- g_autofree char *cert_issuer_str = pv_X509_NAME_oneline(cert_issuer);
|
||||
-
|
||||
- g_set_error(error, PV_CERT_ERROR, PV_CERT_ERROR_CERT_SUBJECT_ISSUER_MISMATCH,
|
||||
- _("Subject issuer mismatch:\n'%s'\n'%s'"), issuer_subject_str,
|
||||
- cert_issuer_str);
|
||||
- return -1;
|
||||
- }
|
||||
-
|
||||
akid = X509_get_ext_d2i(cert, NID_authority_key_identifier, NULL, NULL);
|
||||
if (akid && X509_check_akid(issuer, akid) != X509_V_OK) {
|
||||
g_set_error(error, PV_CERT_ERROR, PV_CERT_ERROR_SKID_AKID_MISMATCH,
|
||||
@@ -1286,21 +1275,10 @@ int pv_verify_cert(X509_STORE_CTX *ctx,
|
||||
return 0;
|
||||
}
|
||||
|
||||
-/* Verify that: subject(issuer) == issuer(crl) and SKID(issuer) == AKID(crl) */
|
||||
+/* Verify that SKID(issuer) == AKID(crl) */
|
||||
static int check_crl_issuer(X509_CRL *crl, X509 *issuer, GError **error)
|
||||
{
|
||||
- const X509_NAME *crl_issuer = X509_CRL_get_issuer(crl);
|
||||
- const X509_NAME *issuer_subject = X509_get_subject_name(issuer);
|
||||
- AUTHORITY_KEYID *akid = NULL;
|
||||
-
|
||||
- if (!own_X509_NAME_equal(issuer_subject, crl_issuer)) {
|
||||
- g_autofree char *issuer_subject_str = pv_X509_NAME_oneline(issuer_subject);
|
||||
- g_autofree char *crl_issuer_str = pv_X509_NAME_oneline(crl_issuer);
|
||||
-
|
||||
- g_set_error(error, PV_CERT_ERROR, PV_CERT_ERROR_CRL_SUBJECT_ISSUER_MISMATCH,
|
||||
- _("issuer mismatch:\n%s\n%s"), issuer_subject_str, crl_issuer_str);
|
||||
- return -1;
|
||||
- }
|
||||
+ g_autoptr(AUTHORITY_KEYID) akid = NULL;
|
||||
|
||||
/* If AKID(@crl) is specified it must match with SKID(@issuer) */
|
||||
akid = X509_CRL_get_ext_d2i(crl, NID_authority_key_identifier, NULL, NULL);
|
||||
@@ -1325,7 +1303,6 @@ int pv_verify_crl(X509_CRL *crl, X509 *c
|
||||
return -1;
|
||||
}
|
||||
|
||||
- /* check that the @crl issuer matches with the subject name of @cert*/
|
||||
if (check_crl_issuer(crl, cert, error) < 0)
|
||||
return -1;
|
||||
|
||||
@@ -1393,6 +1370,93 @@ int pv_check_chain_parameters(const STAC
|
||||
return 0;
|
||||
}
|
||||
|
||||
+/** Replace locality 'Armonk' with 'Pougkeepsie'. If Armonk was not set return
|
||||
+ * `NULL`.
|
||||
+ */
|
||||
+static X509_NAME *x509_armonk_locality_fixup(const X509_NAME *name)
|
||||
+{
|
||||
+ g_autoptr(X509_NAME) ret = NULL;
|
||||
+ int pos;
|
||||
+
|
||||
+ /* Check if ``L=Armonk`` */
|
||||
+ if (!x509_name_data_by_nid_equal((X509_NAME *)name, NID_localityName,
|
||||
+ PV_IBM_Z_SUBJECT_LOCALITY_NAME_ARMONK))
|
||||
+ return NULL;
|
||||
+
|
||||
+ ret = X509_NAME_dup(name);
|
||||
+ if (!ret)
|
||||
+ g_abort();
|
||||
+
|
||||
+ pos = X509_NAME_get_index_by_NID(ret, NID_localityName, -1);
|
||||
+ if (pos == -1)
|
||||
+ return NULL;
|
||||
+
|
||||
+ X509_NAME_ENTRY_free(X509_NAME_delete_entry(ret, pos));
|
||||
+
|
||||
+ /* Create a new name entry at the same position as before */
|
||||
+ if (X509_NAME_add_entry_by_NID(
|
||||
+ ret, NID_localityName, MBSTRING_UTF8,
|
||||
+ (const unsigned char *)&PV_IBM_Z_SUBJECT_LOCALITY_NAME_POUGHKEEPSIE,
|
||||
+ sizeof(PV_IBM_Z_SUBJECT_LOCALITY_NAME_POUGHKEEPSIE) - 1, pos, 0) != 1)
|
||||
+ return NULL;
|
||||
+
|
||||
+ return g_steal_pointer(&ret);
|
||||
+}
|
||||
+
|
||||
+/* This function contains work-arounds for some known subject(CRT)<->issuer(CRL)
|
||||
+ * issues.
|
||||
+ */
|
||||
+static STACK_OF_X509_CRL *quirk_X509_STORE_ctx_get1_crls(X509_STORE_CTX *ctx,
|
||||
+ const X509_NAME *subject, GError **err)
|
||||
+{
|
||||
+ g_autoptr(X509_NAME) fixed_subject = NULL;
|
||||
+ g_autoptr(STACK_OF_X509_CRL) ret = NULL;
|
||||
+
|
||||
+ ret = pv_X509_STORE_CTX_get1_crls(ctx, subject);
|
||||
+ if (ret && sk_X509_CRL_num(ret) > 0)
|
||||
+ return g_steal_pointer(&ret);
|
||||
+
|
||||
+ /* Workaround to fix the mismatch between issuer name of the * IBM
|
||||
+ * signing CRLs and the IBM signing key subject name. Locality name has
|
||||
+ * changed from Poughkeepsie to Armonk.
|
||||
+ */
|
||||
+ fixed_subject = x509_armonk_locality_fixup(subject);
|
||||
+ /* Was the locality replaced? */
|
||||
+ if (fixed_subject) {
|
||||
+ X509_NAME *tmp;
|
||||
+
|
||||
+ sk_X509_CRL_free(ret);
|
||||
+ ret = pv_X509_STORE_CTX_get1_crls(ctx, fixed_subject);
|
||||
+ if (ret && sk_X509_CRL_num(ret) > 0)
|
||||
+ return g_steal_pointer(&ret);
|
||||
+
|
||||
+ /* Workaround to fix the ordering mismatch between issuer name
|
||||
+ * of the IBM signing CRLs and the IBM signing key subject name.
|
||||
+ */
|
||||
+ tmp = fixed_subject;
|
||||
+ fixed_subject = pv_c2b_name(fixed_subject);
|
||||
+ X509_NAME_free(tmp);
|
||||
+ sk_X509_CRL_free(ret);
|
||||
+ ret = pv_X509_STORE_CTX_get1_crls(ctx, fixed_subject);
|
||||
+ if (ret && sk_X509_CRL_num(ret) > 0)
|
||||
+ return g_steal_pointer(&ret);
|
||||
+ X509_NAME_free(fixed_subject);
|
||||
+ fixed_subject = NULL;
|
||||
+ }
|
||||
+
|
||||
+ /* Workaround to fix the ordering mismatch between issuer name of the
|
||||
+ * IBM signing CRLs and the IBM signing key subject name.
|
||||
+ */
|
||||
+ fixed_subject = pv_c2b_name(subject);
|
||||
+ sk_X509_CRL_free(ret);
|
||||
+ ret = pv_X509_STORE_CTX_get1_crls(ctx, fixed_subject);
|
||||
+ if (ret && sk_X509_CRL_num(ret) > 0)
|
||||
+ return g_steal_pointer(&ret);
|
||||
+
|
||||
+ g_set_error(err, PV_CERT_ERROR, PV_CERT_ERROR_NO_CRL, _("no CRL found"));
|
||||
+ return NULL;
|
||||
+}
|
||||
+
|
||||
/* Given a certificate @cert try to find valid revocation lists in @ctx. If no
|
||||
* valid CRL was found NULL is returned.
|
||||
*/
|
||||
@@ -1412,21 +1476,9 @@ STACK_OF_X509_CRL *pv_store_ctx_find_val
|
||||
return NULL;
|
||||
}
|
||||
|
||||
- ret = pv_X509_STORE_CTX_get1_crls(ctx, subject);
|
||||
- if (!ret) {
|
||||
- /* Workaround to fix the mismatch between issuer name of the
|
||||
- * IBM Z signing CRLs and the IBM Z signing key subject name.
|
||||
- */
|
||||
- g_autoptr(X509_NAME) broken_subject = pv_c2b_name(subject);
|
||||
-
|
||||
- ret = pv_X509_STORE_CTX_get1_crls(ctx, broken_subject);
|
||||
- if (!ret) {
|
||||
- g_set_error(error, PV_CERT_ERROR, PV_CERT_ERROR_NO_CRL, _("no CRL found"));
|
||||
- g_info("ERROR: %s", (*error)->message);
|
||||
- return NULL;
|
||||
- }
|
||||
- }
|
||||
-
|
||||
+ ret = quirk_X509_STORE_ctx_get1_crls(ctx, subject, error);
|
||||
+ if (!ret)
|
||||
+ return NULL;
|
||||
/* Filter out non-valid CRLs for @cert */
|
||||
for (int i = 0; i < sk_X509_CRL_num(ret); i++) {
|
||||
X509_CRL *crl = sk_X509_CRL_value(ret, i);
|
25
s390-tools-sles15sp6-04-pvattest-Fix-root-ca-parsing.patch
Normal file
25
s390-tools-sles15sp6-04-pvattest-Fix-root-ca-parsing.patch
Normal file
@ -0,0 +1,25 @@
|
||||
Index: s390-tools-service/pvattest/src/argparse.c
|
||||
===================================================================
|
||||
--- s390-tools-service.orig/pvattest/src/argparse.c
|
||||
+++ s390-tools-service/pvattest/src/argparse.c
|
||||
@@ -190,13 +190,13 @@ static gboolean hex_str_toull(const char
|
||||
}
|
||||
|
||||
/* NOTE REQUIRED */
|
||||
-#define _entry_root_ca(__arg_data, __indent) \
|
||||
- { \
|
||||
- .long_name = "root-ca", .short_name = 0, .flags = G_OPTION_FLAG_NONE, \
|
||||
- .arg = G_OPTION_ARG_FILENAME_ARRAY, .arg_data = __arg_data, \
|
||||
- .description = "Use FILE as the trusted root CA instead the\n" __indent \
|
||||
- "root CAs that are installed on the system (optional).\n", \
|
||||
- .arg_description = "FILE", \
|
||||
+#define _entry_root_ca(__arg_data, __indent) \
|
||||
+ { \
|
||||
+ .long_name = "root-ca", .short_name = 0, .flags = G_OPTION_FLAG_NONE, \
|
||||
+ .arg = G_OPTION_ARG_FILENAME, .arg_data = __arg_data, \
|
||||
+ .description = "Use FILE as the trusted root CA instead the\n" __indent \
|
||||
+ "root CAs that are installed on the system (optional).\n", \
|
||||
+ .arg_description = "FILE", \
|
||||
}
|
||||
|
||||
/* NOTE REQUIRED */
|
92
s390-tools-sles15sp6-genprotimg-makefile.patch
Normal file
92
s390-tools-sles15sp6-genprotimg-makefile.patch
Normal file
@ -0,0 +1,92 @@
|
||||
From 0748d365a60477c96cb9f6a12e9dbe547d549e1f Mon Sep 17 00:00:00 2001
|
||||
From: Marc Hartmayer <mhartmay@linux.ibm.com>
|
||||
Date: Tue, 12 Mar 2024 09:33:19 +0000
|
||||
Subject: [PATCH] genprotimg/**/Makefile: Fix staged installs
|
||||
|
||||
Fix the support for staged installs. The Makefile variable `PKGDATADIR`
|
||||
uses `DESTDIR` for all Makefile target, but actually it should only be
|
||||
used for the `install*` and `uninstall*` targets. [1] Fix this by using
|
||||
`DESTDIR` only for `install*` targets - uninstall* targets are not
|
||||
supported by s390-tools.
|
||||
|
||||
Before this change, if `DESTDIR` was set for staged installs,
|
||||
`genprotimg` has tried to find the bootloader binaries at the temporary
|
||||
installation path `$DESTDIR$(TOOLS_DATADIR)/genprotimg/` instead of
|
||||
`$(TOOLS_DATADIR)/genprotimg`.
|
||||
|
||||
[1] https://www.gnu.org/prep/standards/html_node/DESTDIR.html
|
||||
|
||||
Fixes: 65b9fc442c1a ("genprotimg: introduce new tool for the creation of PV images")
|
||||
Reviewed-by: Steffen Eiden <seiden@linux.ibm.com>
|
||||
Signed-off-by: Marc Hartmayer <mhartmay@linux.ibm.com>
|
||||
Signed-off-by: Steffen Eiden <seiden@linux.ibm.com>
|
||||
---
|
||||
genprotimg/Makefile | 6 +++---
|
||||
genprotimg/boot/Makefile | 8 ++++----
|
||||
genprotimg/src/Makefile | 2 +-
|
||||
3 files changed, 8 insertions(+), 8 deletions(-)
|
||||
|
||||
diff --git a/genprotimg/Makefile b/genprotimg/Makefile
|
||||
index 8c9f7048..6a2e37e4 100644
|
||||
--- a/genprotimg/Makefile
|
||||
+++ b/genprotimg/Makefile
|
||||
@@ -3,7 +3,7 @@ include ../common.mak
|
||||
|
||||
.DEFAULT_GOAL := all
|
||||
|
||||
-PKGDATADIR := "$(DESTDIR)$(TOOLS_DATADIR)/genprotimg"
|
||||
+PKGDATADIR := "$(TOOLS_DATADIR)/genprotimg"
|
||||
TESTS :=
|
||||
SUBDIRS := boot src man
|
||||
RECURSIVE_TARGETS := all-recursive install-recursive clean-recursive
|
||||
@@ -11,8 +11,8 @@ RECURSIVE_TARGETS := all-recursive install-recursive clean-recursive
|
||||
all: all-recursive
|
||||
|
||||
install: install-recursive
|
||||
- $(INSTALL) -d -m 755 "$(PKGDATADIR)"
|
||||
- $(INSTALL) -g $(GROUP) -o $(OWNER) -m 755 samples/check_hostkeydoc "$(PKGDATADIR)"
|
||||
+ $(INSTALL) -d -m 755 "$(DESTDIR)$(PKGDATADIR)"
|
||||
+ $(INSTALL) -g $(GROUP) -o $(OWNER) -m 755 samples/check_hostkeydoc "$(DESTDIR)$(PKGDATADIR)"
|
||||
|
||||
clean: clean-recursive
|
||||
|
||||
diff --git a/genprotimg/boot/Makefile b/genprotimg/boot/Makefile
|
||||
index 799df9cc..73f3c9a8 100644
|
||||
--- a/genprotimg/boot/Makefile
|
||||
+++ b/genprotimg/boot/Makefile
|
||||
@@ -7,7 +7,7 @@ DEBUG_FILES := $(addsuffix .debug,$(FILES))
|
||||
ifeq ($(HOST_ARCH),s390x)
|
||||
ZIPL_DIR := $(rootdir)/zipl
|
||||
ZIPL_BOOT_DIR := $(ZIPL_DIR)/boot
|
||||
-PKGDATADIR := $(DESTDIR)$(TOOLS_DATADIR)/genprotimg
|
||||
+PKGDATADIR := $(TOOLS_DATADIR)/genprotimg
|
||||
|
||||
INCLUDE_PATHS := $(ZIPL_BOOT_DIR) $(ZIPL_DIR)/include $(rootdir)/include
|
||||
INCLUDE_PARMS := $(addprefix -I,$(INCLUDE_PATHS))
|
||||
@@ -86,9 +86,9 @@ stage3b.elf: head.o $(ZIPL_OBJS)
|
||||
@chmod a-x $@
|
||||
|
||||
install: stage3a.bin stage3b_reloc.bin
|
||||
- $(INSTALL) -d -m 755 "$(PKGDATADIR)"
|
||||
- $(INSTALL) -g $(GROUP) -o $(OWNER) -m 644 stage3a.bin "$(PKGDATADIR)"
|
||||
- $(INSTALL) -g $(GROUP) -o $(OWNER) -m 644 stage3b_reloc.bin "$(PKGDATADIR)"
|
||||
+ $(INSTALL) -d -m 755 "$(DESTDIR)$(PKGDATADIR)"
|
||||
+ $(INSTALL) -g $(GROUP) -o $(OWNER) -m 644 stage3a.bin "$(DESTDIR)$(PKGDATADIR)"
|
||||
+ $(INSTALL) -g $(GROUP) -o $(OWNER) -m 644 stage3b_reloc.bin "$(DESTDIR)$(PKGDATADIR)"
|
||||
|
||||
else
|
||||
# Don't generate the dependency files (see `common.mak` for the
|
||||
diff --git a/genprotimg/src/Makefile b/genprotimg/src/Makefile
|
||||
index 08734bff..d447e6cf 100644
|
||||
--- a/genprotimg/src/Makefile
|
||||
+++ b/genprotimg/src/Makefile
|
||||
@@ -3,7 +3,7 @@ include ../../common.mak
|
||||
|
||||
bin_PROGRAM = genprotimg
|
||||
|
||||
-PKGDATADIR ?= "$(DESTDIR)$(TOOLS_DATADIR)/genprotimg"
|
||||
+PKGDATADIR ?= "$(TOOLS_DATADIR)/genprotimg"
|
||||
SRC_DIR := $(dir $(realpath $(firstword $(MAKEFILE_LIST))))
|
||||
TOP_SRCDIR := $(SRC_DIR)/../
|
||||
ROOT_DIR = $(TOP_SRC_DIR)/../../
|
||||
|
@ -1,154 +1,3 @@
|
||||
-------------------------------------------------------------------
|
||||
Mon Feb 3 07:51:27 UTC 2025 - Nikolay Gueorguiev <nikolay.gueorguiev@suse.com>
|
||||
|
||||
- Applied more additional patches (jsc#PED-11870, jec#PED-11958)
|
||||
* s390-tools-08-rust-pvimg-Fix-flag-parsing-for-allowing-dump.patch
|
||||
* s390-tools-09-rust-pvimg-Document-the-change-from--comm-key-to--cck.patch
|
||||
|
||||
-------------------------------------------------------------------
|
||||
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>
|
||||
|
||||
- Applied backport patches from s390-tools 2.37 to 2.36 ( jsc#PED-11870 )
|
||||
( jsc#IBM-1447, jsc#IBM-1062 )
|
||||
* s390-tools-General-update-01.patch
|
||||
* s390-tools-General-update-02.patch
|
||||
* s390-tools-General-update-03.patch
|
||||
* s390-tools-General-update-04.patch
|
||||
* s390-tools-General-update-05.patch
|
||||
* s390-tools-General-update-06.patch
|
||||
* s390-tools-General-update-07.patch
|
||||
* s390-tools-General-update-08.patch
|
||||
* s390-tools-General-update-09.patch
|
||||
* s390-tools-General-update-10.patch
|
||||
* s390-tools-General-update-11.patch
|
||||
* s390-tools-General-update-12.patch
|
||||
* s390-tools-Additional-update-01.patch
|
||||
* s390-tools-Additional-update-02.patch
|
||||
( jsc#IBM-1570, jsc#IBM-1571 )
|
||||
* s390-tools-Support-unencrypted-SE-images-01.patch
|
||||
( jsc#IBM-1572, jsc#IBM-1573 )
|
||||
* s390-tools-pvimg-info-command-01.patch
|
||||
* s390-tools-pvimg-info-command-02.patch
|
||||
* s390-tools-pvimg-info-command-03.patch
|
||||
* s390-tools-pvimg-info-command-04.patch
|
||||
( jsc#IBM-1576, jsc#IBM-1577 )
|
||||
* s390-tools-pvimg-additional-01.patch
|
||||
- Renamed patches from - to
|
||||
* s390-tools-01-opticsmon-Fix-runaway-loop-in-on_link_change.patch
|
||||
to
|
||||
s390-tools-Additional-update-01.patch
|
||||
* s390-tools-02-libzpci-opticsmon-Refactor-on_link_change-using-new.patch
|
||||
to
|
||||
s390-tools-Additional-update-02.patch
|
||||
* s390-tools-03-rust-pvimg-Add-enable-disable-image-encryption-flags-to-pvimg-create.patch
|
||||
to
|
||||
s390-tools-Support-unencrypted-SE-images-01.patch
|
||||
- Revendored vendor.tar.gz
|
||||
|
||||
-------------------------------------------------------------------
|
||||
Tue Jan 7 08:59:16 UTC 2025 - Nikolay Gueorguiev <nikolay.gueorguiev@suse.com>
|
||||
|
||||
- Applied a patch for '--(enable|disable)-image-encryption' flags for 'pvimg create' (jsc#PED-11870)
|
||||
* s390-tools-03-rust-pvimg-Add-enable-disable-image-encryption-flags-to-pvimg-create.patch
|
||||
|
||||
-------------------------------------------------------------------
|
||||
Tue Dec 31 09:59:27 UTC 2024 - Nikolay Gueorguiev <nikolay.gueorguiev@suse.com>
|
||||
|
||||
- Applied patches (jsc#PED-9591, jsc#PED-10303)
|
||||
* s390-tools-01-opticsmon-Fix-runaway-loop-in-on_link_change.patch
|
||||
* s390-tools-02-libzpci-opticsmon-Refactor-on_link_change-using-new.patch
|
||||
|
||||
-------------------------------------------------------------------
|
||||
Mon Dec 9 09:49:52 UTC 2024 - Nikolay Gueorguiev <nikolay.gueorguiev@suse.com>
|
||||
|
||||
- Upgrade s390-tools to version 2.36 (jsc#PED-10303, jsc#PED-9591)
|
||||
* s390-tools: Define Rust MSRV as 1.75.0
|
||||
* Add new tools / libraries:
|
||||
- cpacfinfo: Tool to provide CPACF information
|
||||
- opticsmon: Tools to monitor optical modules for directly attached PCI based NICs
|
||||
- pvimg: Rust rewrite of genprotimg
|
||||
* Changes of existing tools:
|
||||
- chpstat: Add data bandwidth utilization column
|
||||
- chpstat: Add support for full CMCB
|
||||
- chpstat: Add support for new CMG types
|
||||
- dbginfo.sh: add overview commands and crypto update
|
||||
- hyptop: Support for structured output (json, json-seq, csv)
|
||||
- lszfcp: Add missing fallback marker for non-good fc_host port_state
|
||||
- lszfcp: Improve speed with many SCSI devices
|
||||
- pvattest: Add attestation policy check command
|
||||
- zipl: Add support of partitions of mirror md-devices
|
||||
* Bug Fixes:
|
||||
- lszcrypt: Fix wrong state showing up for removed AP queue within SE guest
|
||||
- lszfcp: Show device names line for zfcp_units without SCSI device
|
||||
- Revendored vendor.tar.gz
|
||||
- Applied additional patch (bsc#1233889, bsc#1233079)
|
||||
* s390-tools-02-zipl-src-fix-imprecise-check-that-file-is-on-specifi.patch
|
||||
|
||||
-------------------------------------------------------------------
|
||||
Thu Dec 5 15:13:49 UTC 2024 - Nikolay Gueorguiev <nikolay.gueorguiev@suse.com>
|
||||
|
||||
- Applied a patch (bsc#1233889)
|
||||
* s390-tools-01-zipl_helper.device-mapper-add-missed-step-in-logical.patch
|
||||
- Amended the /usr/lib/modules-load.d/pkey.conf (bsc#1233233). Added
|
||||
* pkey_cca
|
||||
* pkey_ep11
|
||||
* pkey_pckmo
|
||||
|
||||
-------------------------------------------------------------------
|
||||
Tue Nov 5 07:39:58 UTC 2024 - Nikolay Gueorguiev <nikolay.gueorguiev@suse.com>
|
||||
|
||||
- Amended the *_configure scripts to update again the SUSE's specific file
|
||||
'/boot/zipl/active_devices.txt' (bsc#1232474, bsc#1216257)
|
||||
* ctc_configure
|
||||
* dasd_configure
|
||||
* qeth_configure
|
||||
* zfcp_host_configure
|
||||
|
||||
-------------------------------------------------------------------
|
||||
Tue Oct 8 10:35:04 UTC 2024 - Nikolay Gueorguiev <nikolay.gueorguiev@suse.com>
|
||||
|
||||
* Upgrade s390-tools to version 2.35 (jsc#PED-9591, jsc#PED-10303)
|
||||
* Changes of existing tools:
|
||||
- cpacfstats: Add support for FULL XTS (MSA 10) and HMAC (MSA 11) PAI counter
|
||||
- cpuplugd: Make cpuplugd compatible with hiperdispatch
|
||||
- dbginfo.sh: Add network sockstat info
|
||||
- pvapconfig: s390x exclusive build
|
||||
- zdev: Add option to select IPL device
|
||||
- zdump/dfo_s390: Support s390 DFO for vr-kernel dumps
|
||||
- zipl: Add support of mirror devices
|
||||
* Bug Fixes:
|
||||
- (genprotimg|zipl)/boot: discard .note.package ELF section to save memory
|
||||
- netboot/mk-s390image: Fix size when argument is a symlink
|
||||
- ziorep_config: Fix warning message when multipath device is not there.
|
||||
- zipl: Fix problems when target parameters are specified by user
|
||||
- zipl: Fix segfault when creating device-based dumps with '--dry-run'
|
||||
* Removed obsolete patches
|
||||
- s390-tools-2.34-Fix-Rust-compilation-errors.patch
|
||||
- s390-tools-01-zipl-src-add-basic-support-for-multiple-target-base-disks.patch
|
||||
- s390-tools-02-zipl-src-add-basic-support-for-multiple-target-base-disks.patch
|
||||
* Revendored vendor.tar.gz
|
||||
|
||||
-------------------------------------------------------------------
|
||||
Mon Sep 16 12:49:55 UTC 2024 - Nikolay Gueorguiev <nikolay.gueorguiev@suse.com>
|
||||
|
||||
* Applied patches (bsc#1230345)
|
||||
- zipl/src: add basic support for multiple target base disks
|
||||
- s390-tools-01-zipl-src-add-basic-support-for-multiple-target-base-disks.patch
|
||||
- s390-tools-02-zipl-src-add-basic-support-for-multiple-target-base-disks.patch
|
||||
|
||||
-------------------------------------------------------------------
|
||||
Mon Aug 26 09:17:17 UTC 2024 - Nikolay Gueorguiev <nikolay.gueorguiev@suse.com>
|
||||
|
||||
|
@ -1,7 +1,7 @@
|
||||
#
|
||||
# spec file for package s390-tools
|
||||
#
|
||||
# Copyright (c) 2025 SUSE LLC
|
||||
# Copyright (c) 2024 SUSE LLC
|
||||
#
|
||||
# All modifications and additions to the file contributed by third parties
|
||||
# remain the property of their copyright owners, unless otherwise agreed
|
||||
@ -33,7 +33,7 @@
|
||||
%endif
|
||||
|
||||
Name: s390-tools
|
||||
Version: 2.36.0
|
||||
Version: 2.34.0
|
||||
Release: 0
|
||||
Summary: S/390 tools like zipl and dasdfmt for s390x (plus selected tools for x86_64)
|
||||
License: MIT
|
||||
@ -153,58 +153,20 @@ Patch910: s390-tools-sles15sp1-11-zdev-Do-not-call-zipl-on-initrd-update.p
|
||||
Patch911: s390-tools-sles15sp5-remove-no-pie-link-arguments.patch
|
||||
Patch912: s390-tools-ALP-zdev-live.patch
|
||||
Patch913: s390-tools-sles15sp6-kdump-initrd-59-zfcp-compat-rules.patch
|
||||
###
|
||||
Patch914: s390-tools-01-zipl_helper.device-mapper-add-missed-step-in-logical.patch
|
||||
Patch915: s390-tools-02-zipl-src-fix-imprecise-check-that-file-is-on-specifi.patch
|
||||
###
|
||||
Patch920: s390-tools-General-update-01.patch
|
||||
Patch921: s390-tools-General-update-02.patch
|
||||
Patch922: s390-tools-General-update-03.patch
|
||||
Patch923: s390-tools-General-update-04.patch
|
||||
Patch924: s390-tools-General-update-05.patch
|
||||
Patch925: s390-tools-General-update-06.patch
|
||||
Patch926: s390-tools-General-update-07.patch
|
||||
Patch927: s390-tools-General-update-08.patch
|
||||
Patch928: s390-tools-General-update-09.patch
|
||||
Patch929: s390-tools-General-update-10.patch
|
||||
Patch930: s390-tools-General-update-11.patch
|
||||
Patch931: s390-tools-General-update-12.patch
|
||||
###
|
||||
Patch935: s390-tools-Additional-update-01.patch
|
||||
Patch936: s390-tools-Additional-update-02.patch
|
||||
###
|
||||
Patch950: s390-tools-pvimg-info-command-01.patch
|
||||
Patch951: s390-tools-pvimg-info-command-02.patch
|
||||
Patch952: s390-tools-pvimg-info-command-03.patch
|
||||
###
|
||||
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
|
||||
Patch977: s390-tools-08-rust-pvimg-Fix-flag-parsing-for-allowing-dump.patch
|
||||
Patch978: s390-tools-09-rust-pvimg-Document-the-change-from--comm-key-to--cck.patch
|
||||
###
|
||||
Patch990: s390-tools-slfo-01-parse-ipl-device-for-activation.patch
|
||||
Patch914: s390-tools-slfo-01-parse-ipl-device-for-activation.patch
|
||||
Patch915: s390-tools-2.34-Fix-Rust-compilation-errors.patch
|
||||
###
|
||||
|
||||
BuildRequires: curl-devel
|
||||
BuildRequires: dracut
|
||||
BuildRequires: fuse3-devel
|
||||
BuildRequires: gcc
|
||||
BuildRequires: gcc-c++
|
||||
BuildRequires: gcc13
|
||||
BuildRequires: gcc13-c++
|
||||
BuildRequires: gettext-tools
|
||||
BuildRequires: glib2-devel
|
||||
BuildRequires: glibc-devel-static
|
||||
BuildRequires: libcryptsetup-devel > 2.0.3
|
||||
BuildRequires: libjson-c-devel
|
||||
BuildRequires: libnl3-devel
|
||||
BuildRequires: libxml2-devel
|
||||
BuildRequires: mdevctl
|
||||
BuildRequires: ncurses-devel
|
||||
@ -263,13 +225,12 @@ zipl - boot loader and dump DASD initializer
|
||||
zgetdump - tool to get linux system dumps from DASD
|
||||
|
||||
- x86_64
|
||||
pvimg - create a protected virtualization image (genprotimg)
|
||||
genprotimg - create a protected virtualization image
|
||||
pvattest - create, perform, and verify protected virtualization attestation measurements
|
||||
pvsecret - manage secrets for IBM Secure Execution guests.
|
||||
pvapconfig - used to automatically set up the AP configuration within an IBM Secure Execution guest.
|
||||
|
||||
Warning: There is an auxiliary data package - s390-tools-genprotimg-data.
|
||||
To install s390-tools properly, please use:
|
||||
'sudo zypper install s390-tools s390-tools-genprotimg-data'
|
||||
Note: Auxiliary data package - s390-tools-genprotimg-data
|
||||
|
||||
%package -n osasnmpd
|
||||
Summary: OSA-Express SNMP subagent
|
||||
@ -388,11 +349,11 @@ BuildArch: noarch
|
||||
Requires(pre): filesystem
|
||||
|
||||
%description genprotimg-data
|
||||
The pvimg (genprotimg) allows preparing and analyzing boot images
|
||||
The genprotimg allows preparing and analyzing boot images
|
||||
in the realm of IBM Secure Execution on a trusted environment,
|
||||
such as the laptop of an admin by limiting the build targets
|
||||
depending on the defined or detected host architecture.
|
||||
This package provides auxiliary data used by pvimg(genprotimg).
|
||||
This package provides auxiliary data used by genprotimg.
|
||||
|
||||
### *** s390x ************************************************************************* ###
|
||||
%ifarch s390x
|
||||
@ -402,7 +363,7 @@ This package provides auxiliary data used by pvimg(genprotimg).
|
||||
|
||||
cp -vi %{SOURCE22} CAUTION
|
||||
|
||||
install -D -m 0644 %{SOURCE200} .cargo/config.toml
|
||||
install -D -m 0644 %{SOURCE200} .cargo/config
|
||||
tar -xzf %{SOURCE201}
|
||||
|
||||
%build
|
||||
@ -419,9 +380,11 @@ export KERNELIMAGE_MAKEFLAGS="%%{?_smp_mflags}"
|
||||
DISTRELEASE=%{release} \
|
||||
UDEVRUNDIR=/run/udev \
|
||||
HAVE_CARGO=1 \
|
||||
HAVE_DRACUT=1
|
||||
HAVE_DRACUT=1 \
|
||||
CC=gcc-13 \
|
||||
CXX=g++-13
|
||||
### all
|
||||
gcc -static -o read_values ${OPT_FLAGS} %{SOURCE86} -lqc
|
||||
gcc-13 -static -o read_values ${OPT_FLAGS} %{SOURCE86} -lqc
|
||||
|
||||
%install
|
||||
mkdir -p %{buildroot}/boot/zipl
|
||||
@ -432,7 +395,9 @@ mkdir -p %{buildroot}%{_sysconfdir}/zkey/repository
|
||||
SYSTEMDSYSTEMUNITDIR=%{_unitdir} \
|
||||
UDEVRUNDIR=/run/udev \
|
||||
HAVE_CARGO=1 \
|
||||
HAVE_DRACUT=1
|
||||
HAVE_DRACUT=1 \
|
||||
CC=gcc-13 \
|
||||
CXX=g++-13
|
||||
### all
|
||||
|
||||
# The make install command puts things in /etc/sysconfig and not the
|
||||
@ -754,6 +719,7 @@ done
|
||||
%dir %{_prefix}/lib/systemd/scripts
|
||||
%dir %{_datadir}/s390-tools
|
||||
%dir %{_datadir}/s390-tools/netboot
|
||||
%dir %{_datadir}/s390-tools/genprotimg
|
||||
%dir %{_prefix}/lib/dracut/modules.d/95zdev
|
||||
%dir %{_prefix}/lib/dracut/modules.d/95zdev-kdump
|
||||
%dir %{_prefix}/lib/dracut/modules.d/96zdev-live
|
||||
@ -776,8 +742,8 @@ done
|
||||
%dir /etc/mdevctl.d/scripts.d/callouts/
|
||||
###
|
||||
%exclude /lib/s390-tools/stage3.bin
|
||||
%exclude %{_datadir}/s390-tools/pvimg/stage3a.bin
|
||||
%exclude %{_datadir}/s390-tools/pvimg/stage3b_reloc.bin
|
||||
%exclude %{_datadir}/s390-tools/genprotimg/stage3a.bin
|
||||
%exclude %{_datadir}/s390-tools/genprotimg/stage3b_reloc.bin
|
||||
###
|
||||
|
||||
%files -n osasnmpd -f %{_builddir}/%{name}.osasnmp
|
||||
@ -828,9 +794,8 @@ done
|
||||
### genprotimg
|
||||
%files genprotimg-data
|
||||
/lib/s390-tools/stage3.bin
|
||||
%dir %{_datadir}/s390-tools/pvimg
|
||||
%{_datadir}/s390-tools/pvimg/stage3a.bin
|
||||
%{_datadir}/s390-tools/pvimg/stage3b_reloc.bin
|
||||
%{_datadir}/s390-tools/genprotimg/stage3a.bin
|
||||
%{_datadir}/s390-tools/genprotimg/stage3b_reloc.bin
|
||||
|
||||
### _endif
|
||||
### *** !s390x ************************************************************************* ###
|
||||
@ -840,13 +805,12 @@ done
|
||||
%prep
|
||||
%autosetup -p1
|
||||
|
||||
install -D -m 0644 %{SOURCE200} .cargo/config.toml
|
||||
install -D -m 0644 %{SOURCE200} .cargo/config
|
||||
tar -xzf %{SOURCE201}
|
||||
|
||||
%build
|
||||
export OPT_FLAGS="%{optflags}"
|
||||
export KERNELIMAGE_MAKEFLAGS="%%{?_smp_mflags}"
|
||||
|
||||
%make_build \
|
||||
DISTRELEASE=%{release} \
|
||||
UDEVRUNDIR=/run/udev \
|
||||
@ -854,7 +818,6 @@ export KERNELIMAGE_MAKEFLAGS="%%{?_smp_mflags}"
|
||||
HAVE_DRACUT=1
|
||||
|
||||
%install
|
||||
|
||||
%make_install \
|
||||
DISTRELEASE=%{release} \
|
||||
SYSTEMDSYSTEMUNITDIR=%{_unitdir} \
|
||||
@ -865,8 +828,8 @@ export KERNELIMAGE_MAKEFLAGS="%%{?_smp_mflags}"
|
||||
%files
|
||||
%{_prefix}/bin/*
|
||||
%dir %{_datadir}/s390-tools
|
||||
%dir %{_datadir}/s390-tools/pvimg
|
||||
%{_datadir}/s390-tools/pvimg/check_hostkeydoc
|
||||
%dir %{_datadir}/s390-tools/genprotimg
|
||||
%{_datadir}/s390-tools/genprotimg/check_hostkeydoc
|
||||
%{_mandir}/man1/*
|
||||
|
||||
%endif
|
||||
|
@ -1,3 +1,3 @@
|
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:a006e81236e6479a7141d04c11af6181b5baeee9bd1cd9140003ea79738a33bf
|
||||
size 46331845
|
||||
oid sha256:14f5559f64640cf3876d41f0168294afefd444ee28a9a0a5175ca61787a481e9
|
||||
size 43464873
|
||||
|
@ -38,14 +38,6 @@ debug_mesg () {
|
||||
esac
|
||||
}
|
||||
|
||||
add_cio_channel() {
|
||||
echo "$* # ${DATE}" >> /boot/zipl/active_devices.txt
|
||||
}
|
||||
|
||||
remove_cio_channel() {
|
||||
[ -w /boot/zipl/active_devices.txt ] && sed -i -e "/^${1}/d" /boot/zipl/active_devices.txt
|
||||
}
|
||||
|
||||
usage(){
|
||||
echo "Usage: ${0} <ccwid> <online>"
|
||||
echo " ccwid = x.y.ssss where"
|
||||
@ -88,8 +80,3 @@ RC=${?}
|
||||
if [ ${RC} -ne 0 ]; then
|
||||
exit ${RC}
|
||||
fi
|
||||
|
||||
if [ ${ON_OFF} == 1 ]; then
|
||||
add_cio_channel "${CCW_CHAN_ID}"
|
||||
else remove_cio_channel "${CCW_CHAN_ID}"
|
||||
fi
|
||||
|
Loading…
x
Reference in New Issue
Block a user