Compare commits

..

66 Commits

Author SHA1 Message Date
Jan Höppner 793c037ded New release s390-tools-2.37.0
Signed-off by: Jan Höppner <hoeppner@linux.ibm.com>
2025-02-07 15:47:07 +01:00
Jan Höppner 9f476217f5 rust: Upgrade dependencies
Upgrade all dependencies by running 'cargo upgrade' from cargo-edit.

Link: https://crates.io/crates/cargo-edit
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
2025-02-07 14:25:11 +01:00
Jan Höppner 49111d613b rust/pvattest/tests: Add missing unittest asset host.pem.crt
The host.pem.crt file required by pvattest/src/cmd/check/host_key.rs for
unittesting is missing upstream. Add it back in.

Fixes: https://github.com/ibm-s390-linux/s390-tools/issues/182
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
2025-02-07 14:25:11 +01:00
Steffen Eiden 26d2e2d786 rust/pv: Fix padding of retrievable private keys
Fix the padding of keys that are smaller than the architected slot for
the retrievable key. Previously the keys where appended with zeros.
However, processing software expects a left-padding.
Affected key types:
  * Ed448
  * SecP521

While at it, fix documentation in the retrievable key struct.

Fixes: fd024387d7 ("rust/pv: Retrievable secrets support")
Acked-by: Ingo Franzki <ifranzki@linux.ibm.com>
Signed-off-by: Steffen Eiden <seiden@linux.ibm.com>
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
2025-02-06 13:18:32 +01:00
Mikhail Zaslonko 7bd159de1d zdump: Add support for eckd_mv dumper v2
Use STAGE2_DUMPER_SIZE_MV size for eckd_mv dumper v2
in df_s390_dumper_read() to verify the dump tool.

Signed-off-by: Mikhail Zaslonko <zaslonko@linux.ibm.com>
Reviewed-by: Alexander Egorenkov <egorenar@linux.ibm.com>
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
2025-01-28 17:25:19 +01:00
Mikhail Zaslonko 605680d6fd zipl/boot: Increase section size for eckd_mv dumper
Update eckd2dump linker script to remove stage2 space constraints and
avoid linker section overlaps when using 'unfortunate' compiler
version & flags combination.
Since Multi-volume dumper size changes, we also update the dump-tool version
number for 'zgetdump' to distinguish.

Signed-off-by: Mikhail Zaslonko <zaslonko@linux.ibm.com>
Fixes: https://github.com/ibm-s390-linux/s390-tools/issues/171
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
2025-01-28 17:25:19 +01:00
Finn Callies f1a479a794 capcfstats: Update manpage to MSA 10 and MSA 11
The tooling already supports the PAI counters to the respective CPACF
functions introduced with MSA 10 and MSA 11 but the manpage did not
reflect that until now. The list of pai counters is moved to a new
section called APPENDIX to not block any important information with the
list.
Additionally a few double space after end of sentences are removed.

Acked-by: Steffen Eiden <seiden@linux.ibm.com>
Reviewed-by: Harald Freudenberger <freude@linux.ibm.com>
Signed-off-by: Finn Callies <fcallies@linux.ibm.com>
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
2025-01-28 17:25:19 +01:00
Finn Callies fa2d68e80c capcfstatsd: Update service unit to build on z/VM and KVM
cpacfstatsd can be run successfully on z/VM and KVM mashines, but the
service unit did not allow that. A user would have to start the
cpacfstats daemon by hand every time until now.

Acked-by: Steffen Eiden <seiden@linux.ibm.com>
Reviewed-by: Harald Freudenberger <freude@linux.ibm.com>
Signed-off-by: Finn Callies <fcallies@linux.ibm.com>
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
2025-01-28 17:25:19 +01:00
Joern Siglen e1730585e9 dbginfo.sh: Adding kdump info
Collect kdump.log to analyse kdump issues.

Suggested-by: Michael Storzer <MSTORZER@de.ibm.com>
Reviewed-by: Michael Storzer <MSTORZER@de.ibm.com>
Signed-off-by: Joern Siglen <siglen@de.ibm.com>
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
2025-01-28 17:25:19 +01:00
Joern Siglen ef55a5d677 dbginfo.sh: Add details on CPU-measurement
Adding lscpumf command to get details on the setup for CPU-measurement.

Suggested-by: Eberhard Pasch <epasch@de.ibm.com>
Reviewed-by: Michael Storzer <MSTORZER@de.ibm.com>
Signed-off-by: Joern Siglen <siglen@de.ibm.com>
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
2025-01-28 17:25:19 +01:00
Joern Siglen bc4b7475f7 Removing email references to linux390@de.ibm.com
The Email address linux390@de.ibm.com is about to be sunset.
Cleaning up the last references in man page/help function for:
- mon_tools
- dbginfo.sh

Reviewed-by: Michael Storzer <MSTORZER@de.ibm.com>
Signed-off-by: Joern Siglen <siglen@de.ibm.com>
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
2025-01-28 17:25:19 +01:00
Dan Horák abcb93aacb zipl: Update inline assembly for GCC 15
Properly escape % (as %%) for extended assembly in stage3.c in
start_kernel().

Fixes: https://github.com/ibm-s390-linux/s390-tools/issues/179
Closes: https://github.com/ibm-s390-linux/s390-tools/pull/180
Signed-off-by: Dan Horák <dan@danny.cz>
Reviewed-by: Stefan Haberland <sth@linux.ibm.com>
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
2025-01-28 17:25:19 +01:00
Eduard Shishkin b09a9b3490 zipl_helper.device-mapper: Fix logical device resolution
Reported issue:

\# lsblk
sdc                                8:32   0   20G  0 disk
|-sdc1                             8:33   0   20G  0 part
`-mpatha                         253:1    0   20G  0 mpath
  `-mpatha1                      253:17   0   20G  0 part
sdd                                8:48   0   20G  0 disk
|-sdd1                             8:49   0   20G  0 part
`-mpatha                         253:1    0   20G  0 mpath
  `-mpatha1                      253:17   0   20G  0 part

\# zipl -d /dev/mapper/mpatha1
Success
\# zgetdump -d /dev/mapper/mpatha
zgetdump: No dump tool found on "/dev/mapper/mpatha"

The root cause is in incorrect logical device resolution:

\# zipl_helper.device-mapper 253:17

Expected result:

targetbase=253:1
targettype=SCSI
targetblocksize=512
targetoffset=2048

Actual result:

targetbase=8:48
targettype=SCSI
targetblocksize=512
targetoffset=2048

Do not resolve dm device which provides access to boot sectors.

Fixes: 3db20bccef ("zipl_helper.device-mapper: Add missed step in logical device resolution")
Signed-off-by: Eduard Shishkin <edward6@linux.ibm.com>
Reported-by: Mikhail Zaslonko <zaslonko@linux.ibm.com>
Tested-by: Mikhail Zaslonko <zaslonko@linux.ibm.com>
Acked-by: Mikhail Zaslonko <zaslonko@linux.ibm.com>
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
2025-01-28 17:25:19 +01:00
Jakob Naucke caaf2b2116 rust/pvimg: Fix flag parsing for allowing dump
Parsing of pvimg flags did not pick up allowing dumping correctly.

Fixes: f4cf4ae6eb (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>
2025-01-28 17:25:19 +01:00
Joern Siglen 382ec368fc dbginfo.sh: Add new crypto command
s390tools has a new tool to view the CPACF data, now added
to our data collection

Suggested-by: Mario Held <mario.held@de.ibm.com>
Suggested-by: Ingo Franzki <ifranzki@linux.ibm.com>
Reviewed-by: Finn Callies <fcallies@linux.ibm.com>
Signed-off-by: Joern Siglen <siglen@de.ibm.com>
Signed-off-by: Steffen Eiden <seiden@linux.ibm.com>
2025-01-10 16:38:45 +01:00
Joern Siglen 9dd96144d4 dbginfo.sh: Rework network section
Define the network devices as global variable with closer selection
to avoid repeated calls with undefined interfaces.
Relable the OSA part of networks.

Reviewed-by: Mario Held <mario.held@de.ibm.com>
Signed-off-by: Joern Siglen <siglen@de.ibm.com>
Signed-off-by: Steffen Eiden <seiden@linux.ibm.com>
2025-01-10 16:38:45 +01:00
Joern Siglen efd41ca435 dbginfo.sh: Update copyright 2nd year
change second year of copyright to 2025

Reviewed-by: Mario Held <mario.held@de.ibm.com>
Signed-off-by: Joern Siglen <siglen@de.ibm.com>
Signed-off-by: Steffen Eiden <seiden@linux.ibm.com>
2025-01-10 16:38:45 +01:00
Marc Hartmayer 7bc12d0202 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: 5b6d7a467d ("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>
2025-01-10 16:38:45 +01:00
Marc Hartmayer b49cd13d16 cpumf/man: Fix whatis-entries in manpages
Fix whatis-entries in the lshwc, lspai, and pai manpages by inlining the
tool name. The following command line can be used to test a local
manpage:

$ lexgrog -w -m cpumf/man/lshwc.8
cpumf/man/lshwc.8: "lshwc - extract CPU Measurement Facilities counter sets"

Github-ID: https://github.com/ibm-s390-linux/s390-tools/issues/170
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>
2025-01-10 16:38:45 +01:00
Marc Hartmayer 56e32dfcbb rust/pvcore/uvdevice: Fix typo
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>
2025-01-10 16:38:45 +01:00
Ingo Franzki 833a8e7309 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>
2025-01-10 16:38:45 +01:00
Ingo Franzki a8eb2bd4e7 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>
2025-01-10 16:38:45 +01:00
Ingo Franzki 5276d408fd 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>
2025-01-10 16:38:45 +01:00
Ingo Franzki 95bf7eb285 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>
2025-01-10 16:38:45 +01:00
Ingo Franzki fdf66dc148 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>
2025-01-10 16:38:45 +01:00
Ingo Franzki 5ce79ea667 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>
2025-01-10 16:38:45 +01:00
Ingo Franzki 8c4b2872b8 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>
2025-01-10 16:38:45 +01:00
Marc Hartmayer 7be364aa84 rust/pvimg/man: Fix whatis-entries in manpages
Before this change:
$ lexgrog -w -m man/pvimg-create.1
man/pvimg-create.1: parse failed

After this change:

$ lexgrog -w -m man/pvimg-create.1
man/pvimg-create.1: "pvimg-create - Create an IBM Secure Execution image"

In addition, pvimg is highlighted in the manpage description section.

Github-ID: https://github.com/ibm-s390-linux/s390-tools/issues/170
Signed-off-by: Marc Hartmayer <mhartmay@linux.ibm.com>
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
2024-12-20 13:12:34 +01:00
Marc Hartmayer 43858063ca rust: Upgrade 'thiserror' to '2.0.6'
Since there have been breaking changes, let's fix them. Fortunately,
there was only one problem to fix.

Command line used:

  $ cargo upgrade --incompatible -p 'thiserror'

Acked-by: Jan Höppner <hoeppner@linux.ibm.com>
Signed-off-by: Marc Hartmayer <mhartmay@linux.ibm.com>
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
2024-12-20 13:01:15 +01:00
Marc Hartmayer 739761f0d2 rust: Update Rust depenencies
Command line used (https://crates.io/crates/cargo-edit):

 $ cargo upgrade

Acked-by: Jan Höppner <hoeppner@linux.ibm.com>
Signed-off-by: Marc Hartmayer <mhartmay@linux.ibm.com>
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
2024-12-20 13:01:15 +01:00
Steffen Eiden a8a3e7d49c rust/pvsecret: Update manuals and README
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>
2024-12-20 13:01:15 +01:00
Steffen Eiden ff04f76257 rust/pv*: Allow the use of non-hashes secret IDs
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>
2024-12-20 13:01:15 +01:00
Steffen Eiden 93216d916c rust/pv*: Support longer secret lists
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>
2024-12-20 13:01:15 +01:00
Steffen Eiden 256289a30a rust/pv_core: Refactor secret list
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>
2024-12-20 13:01:15 +01:00
Steffen Eiden 93da795520 rust/pvsecret: Add support for retrievable secrets
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>
2024-12-20 13:01:15 +01:00
Steffen Eiden a14f9d4edc rust/pvsecret: Improve CLI
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>
2024-12-20 13:01:15 +01:00
Steffen Eiden fd024387d7 rust/pv: Retrievable secrets support
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>
2024-12-20 13:01:15 +01:00
Steffen Eiden 4af137f4fa rust/pv_core: Support for listing Retrievable Secrets
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>
2024-12-20 13:01:15 +01:00
Steffen Eiden 01cd81ecf5 rust/pv_core: Retrieve Secret UVC
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>
2024-12-20 13:01:15 +01:00
Steffen Eiden 69eb06f39e rust/pv_core: Update ffi.rs to linux/uvdevice.h v6.13
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>
2024-12-20 13:01:15 +01:00
Steffen Eiden d1636168b2 rust/pv: Support for writing data in PEM format
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>
2024-12-20 13:01:15 +01:00
Steffen Eiden 1e44ace41d rust/pvsecret: Refactor writing secret
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>
2024-12-20 13:01:15 +01:00
Marc Hartmayer a9b8caa411 genprotimg/sample/check_hostkeydoc: Fix documentation link
Move the script to 'genprotimg/samples/check_hostkeydoc' and create a
symlink at 'rust/pvimg/tools/check_hostkeydoc' in order to keep
compatibility with existing documentation. The problem with the original
fix was that the github.com website does not follow symbolic links.

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>
2024-12-20 13:01:15 +01:00
Marc Hartmayer 6e48c5ebaa pvimg: info: Rename '--key' into '--hdr-key' and use '--key' as an alias
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>
2024-12-20 13:01:15 +01:00
Marc Hartmayer 944581eaef rust/pvimg: Add upper estimates for the Secure Execution header
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: f4cf4ae6eb ("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>
2024-12-20 13:01:15 +01:00
Marc Hartmayer 3f6572e901 rust/utils: mkdtemp: fix memory leak
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: e56acf4f14 ("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>
2024-12-19 21:45:37 +01:00
Peter Oberparleiter 6520070a46 chpstat: Fix invalid utilization data on older kernels
On kernels without support for the new "measurement_chars_full" CHPID
sysfs attribute, chpstat displays invalid utilization numbers (nan/inf).
This is due to an invalid buffer address calculation when reading the
old "measurement_chars" attribute.

Fix this by using the correct buffer address calculation.

Fixes: 026ecbafea ("chpstat: Add support for full CMCB")
Signed-off-by: Peter Oberparleiter <oberpar@linux.ibm.com>
Reviewed-by: Vineeth Vijayan <vneethv@linux.ibm.com>
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
2024-12-19 21:45:37 +01:00
Marc Hartmayer 3b8fdcc892 pvimg: Add '--hdr-key' command line option to 'pvimg create'
Add '--hdr-key <FILE>' as a command line option to the 'pvimg create'
command. This key can then be used later to decrypt the Secure Execution
header of a Secure Execution image, e.g. 'pvimg info --key <FILE>
--format json <SE_IMG>'. While updating the manpages, add missing hyphen
escapes in the manpages.

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>
2024-12-18 17:08:51 +01:00
Eduard Shishkin 352e2fe5d3 zipl/src: Fix incorrect installation of zipl_helper.md
Dereferencing zipl.helper.md by 'make install' causes "duplicate
BUILD-IDs" conflict for distro partners.

Install zipl_helper.device-mapper as a regular file and all other
helpers - as symlinks to the zipl_helper.device-mapper

Fixes: 3296d85e35 ("zipl/src: add support of md-mirrors")
Fixes: https://github.com/ibm-s390-linux/s390-tools/issues/178
Signed-off-by: Eduard Shishkin <edward6@linux.ibm.com>
Acked-by: Jan Höppner <hoeppner@linux.ibm.com>
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
2024-12-18 17:08:51 +01:00
Eduard Shishkin 37b7b68f1b zipl/src: Fix imprecise check that file locates on specified device
In case when target parameters are specified by user, the check
that a file locates on a specified device, compares a logical
device with a base disk, which is incorrect.

Fix the check to compare base disks (a specified one with the base
disk determined by disk_get_info() procedure called w/o any user
hints).

Fixes: c0f02d2f68 ("zipl/src: Fix problems when target parameters are specified by user")
Signed-off-by: Eduard Shishkin <edward6@linux.ibm.com>
Acked-by: Jan Höppner <hoeppner@linux.ibm.com>
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
2024-12-18 17:08:51 +01:00
Eduard Shishkin 3db20bccef zipl_helper.device-mapper: Add missed step in logical device resolution
Steps to reproduce the problem:

\# 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 problem is in a missed step which resolves the uppermost
logical dm-device to a physical device. Reproducible only for loop
devices.

Add missing step to correctly resolve to a physical device.

Fixes: 670bf3e870 ("zipl: refactor zipl_helper.device-mapper")
Signed-off-by: Eduard Shishkin <edward6@linux.ibm.com>
Acked-by: Jan Höppner <hoeppner@linux.ibm.com>
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
2024-12-18 17:08:51 +01:00
Marc Hartmayer 560b276f7e rust/pvimg: Fix possible 'range start index out of range for slice' error
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: f4cf4ae6eb ("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>
2024-12-18 17:08:50 +01:00
Marc Hartmayer a19c43f613 rust/Cargo.toml: Change to "thin local" LTO for faster release builds
"fat" LTO takes much longer to compile than with "thin local" LTO, so
use the Rust release build defaults, which is "thin local" LTO [1-2].

If someone wants to enable "fat" LTO, they can use one of the common
methods to adjust the Rust build process, for example:

  $ cargo build --config 'profile.release.lto=true' --release

or

  $ export CARGO_PROFILE_RELEASE_LTO=true
  $ cargo build --release

[1] https://doc.rust-lang.org/cargo/reference/profiles.html#release
[2] https://doc.rust-lang.org/cargo/reference/profiles.html#lto

Reviewed-by: Steffen Eiden <seiden@linux.ibm.com>
Acked-by: Jan Höppner <hoeppner@linux.ibm.com>
Signed-off-by: Marc Hartmayer <mhartmay@linux.ibm.com>
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
2024-12-18 17:08:50 +01:00
Marc Hartmayer 5b6d7a467d rust/pvimg: Add '--cck <FILE>' command line option and make '--comm-key' an alias
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>
2024-12-18 17:08:50 +01:00
Marc Hartmayer b1fdbac5f9 rust/pvimg/man: Document command line option aliases in the manpages
Document the visible command line option aliases in the manpages.

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>
2024-12-18 17:08:50 +01:00
Marc Hartmayer cf51ac7860 rust/pvimg: Add '--(enable|disable)-image-encryption' flags to 'pvimg create'
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>
2024-12-13 16:26:35 +01:00
Niklas Schnelle cf5560a100 libzpci: opticsmon: Refactor on_link_change() using new zpci_find_by_netdev()
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>
2024-12-13 16:26:35 +01:00
Niklas Schnelle dff965465c opticsmon: Fix runaway loop in on_link_change()
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: c34adb9cab ("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>
2024-12-13 16:26:35 +01:00
Marc Hartmayer 79de37bbc8 genprotimg/samples/check_hostkeydoc: Create symlink to the new location
Create a symbolic link to the new location of the 'check_hostkeydoc'
script in order to keep compatibility with existing documentation.

Reported-by: Stephan Hartig <hartig@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>
2024-12-13 16:26:35 +01:00
Fedor Loshakov e5c2fba0a8 ziomon/ziorep_utilization: introduce parameter for reports separation
The output of ziorep_utilization tool currently contains reports for both
the physical and the virtual adapter. As binding of physical and virtual
adapter reports together can be confusing for evaluation of results, introduce
--fcp-device tool parameter for reports separation. This parameter was
introduced to mark virtual adapter report as it represents utilization report
of FCP devices.

So, if --fcp-device was specified by the user, virtual adapter report is
printed. Otherwise physical adapter report is printed.

Parameter --fcp-device has no influence on CSV format report printing.

Refactor also print_reports() function for both reports for better
--fcp-device parameter handling.

Add clarification messages on how to use --fcp-device parameter.

Delete empty separator line between former two reports.

Add description of --fcp-device parameter to man pages.

Add examples for using of ziorep_utilization tool to man pages.

Signed-off-by: Fedor Loshakov <loshakov@linux.ibm.com>
Reviewed-by: Benjamin Block <bblock@linux.ibm.com>
Reviewed-by: Sakshi Singh <005c7w@linux.ibm.com>
Reviewed-by: M Nikhil <nikh1092@linux.ibm.com>
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
2024-12-13 16:26:35 +01:00
Fedor Loshakov 04267470b1 ziomon/ziorep_utilization: swap Bus-ID and CHPID columns in virt report
As virtual adapter report in ziorep_utilization represents utilization
values of FCP devices, swap Bus-ID and CHPID columns in virtual
adapter report to avoid confusion during evaluation. Also use
print_delimiter() function instead of slash for Bus-ID and CHPID fields
separation.

Before patch virtual adapter report looks like:
...
CHP Bus-ID  |qdio util.%|queu|fail|-thp in MB/s-|I/O reqs-|
 ID            max   avg full  erc     rd    wrt   rd  wrt
2020-05-14 14:00:41
 60/0.0.1906   2.3   0.8    0    0   0.0  329.8    30 192K
 61/0.0.1946   0.8   0.7    0    0   0.0  341.8    30 192K
...

After patch application virtual adapter report looks like:
...
Bus-ID  |CHP|qdio util.%|queu|fail|-thp in MB/s-|I/O reqs-|
          ID   max   avg full  erc     rd    wrt   rd  wrt
2020-05-14 14:00:41
0.0.1906  60   0.0   0.8    0    0   0.0  331.2     0 1.3K
0.0.1946  61   0.0   0.7    0    0   0.0  344.0     0 1.3K
...

Note, that this patch does not change CSV-format report.

Signed-off-by: Fedor Loshakov <loshakov@linux.ibm.com>
Reviewed-by: Steffen Maier <maier@linux.ibm.com>
Reviewed-by: Benjamin Block <bblock@linux.ibm.com>
Reviewed-by: Sakshi Singh <005c7w@linux.ibm.com>
Reviewed-by: M Nikhil <nikh1092@linux.ibm.com>
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
2024-12-13 16:26:35 +01:00
Fedor Loshakov cf7060ea08 ziomon/ziorep_utilization: add pchid column to physical adapter report
In ziorep_utilization tool physical adapter report represents PCHID
scope, rather than CHPID. PCHID column is added as a very first column of
physical adapter report for non-CSV format report.

Old version of physical adapter report with CHPID column only
looks like:
...
CHP|adapter in %-|--bus in %---|--cpu in %---|
 ID min max   avg min max   avg min max   avg
2020-05-14 14:00:41
 60   0   0   0.0   1   1   1.0   0   0   0.0
 61   0   0   0.0   1   1   1.0   0   0   0.0
...

New version of physical adapter report with additional PCHID column
looks like:
...
PCH |CHP|adapter in %-|--bus in %---|--cpu in %---|
 ID   ID min max   avg min max   avg min max   avg
2020-05-14 14:00:41
01c0  60   0   0   0.0   1   1   1.0   0   0   0.0
01c1  61   0   0   0.0   1   1   1.0   0   0   0.0
...

For CSV-format report PCHID column is appended to the end of each record line.

For older kernel releases, where PCHID sysfs entry is not available, replace
PCHID column value with "n/a".

Signed-off-by: Fedor Loshakov <loshakov@linux.ibm.com>
Reviewed-by: Benjamin Block <bblock@linux.ibm.com>
Reviewed-by: Sakshi Singh <005c7w@linux.ibm.com>
Reviewed-by: M Nikhil <nikh1092@linux.ibm.com>
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
2024-12-13 16:26:35 +01:00
Fedor Loshakov f340a96fdf ziomon/ziorep_cfgreader: extract PCHID from .config file
Add an ability for ziorep_config tool to extract PCHID additional adapter
parameter from .config file. With this patch device_info structure will have
pchid field, which can be used by different printers classes. Also add
function for querying PCHID by using of devno for providing reports. Translate
"n/a" field from .config file record into invalid PCHID number.

Signed-off-by: Fedor Loshakov <loshakov@linux.ibm.com>
Reviewed-by: Sakshi Singh <005c7w@linux.ibm.com>
Reviewed-by: M Nikhil <nikh1092@linux.ibm.com>
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
2024-12-13 16:26:35 +01:00
Fedor Loshakov 798bd752cf ziomon/ziorep_config: extract PCHID and add to internal report
The ziomon_fcpconf saves a lot of information about adapter into .cfg file.
With this patch ziorep_config tool will extract PCHID additional adapter
parameter from .cfg file and put it into .config file for future evaluation by
ziorep_* tools group.

This patch also adds notification for cases, when chid sysfs entry does not
exist for specified CHPID, for example for old kernels.

Before patch application typical line example from .config file looked like:
... 253:0 /dev/sde 8388672 8:64 Disk

After application of the patch typical line example from .config file
looks like:
... 253:0 /dev/sde 8388672 8:64 Disk 01c0

On older distros, which do not contain pchid sysfs entry, typical line
example from .config file looks like:
... 253:0 /dev/sde 8388672 8:64 Disk n/a

As PCHID value of FCP adapter can now be extracted from .config file,
add PCHID field to report of ziorep_config tool.

If there was no PCHID sysfs entry on the system, print following message into
ziorep_config command output:
...
PCHID:   there is no PCHID entry in data source
...

Signed-off-by: Fedor Loshakov <loshakov@linux.ibm.com>
Reviewed-by: Steffen Maier <maier@linux.ibm.com>
Reviewed-by: Benjamin Block <bblock@linux.ibm.com>
Reviewed-by: Sakshi Singh <005c7w@linux.ibm.com>
Reviewed-by: M Nikhil <nikh1092@linux.ibm.com>
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
2024-12-13 16:26:35 +01:00
Fedor Loshakov 638f0aa4f9 ziomon/ziorep_traffic: disambiguate paths with device bus-ID
Add device bus-ID to ziorep_traffic output for better distinguishing
of paths in setups, which have paths in the same pathgroup with same
WWPN and LUN.

Output without device bus-ID column:
$ ziorep_traffic log
       WWPN                LUN       ...
                                        ...
2019-10-21 11:44:38
0x50050763071bc5e3:0x4006404f00000000   ...
0x50050763071bc5e3:0x4006404f00000000   ...

Output with device bus-ID column:
$ ziorep_traffic log
DEVBUSID        WWPN                LUN       ...
                                                 ...
2019-10-21 11:44:38
0.0.1980:0x50050763071bc5e3:0x4006404f00000000   ...
0.0.19c0:0x50050763071bc5e3:0x4006404f00000000   ...

After patch application device bus-ID can also be seen in detailed
(with -D option) output and in CSV output format (with -x option).

Also adjust output headings correspondingly.

Signed-off-by: Fedor Loshakov <loshakov@linux.ibm.com>
Reviewed-by: Steffen Maier <maier@linux.ibm.com>
Reviewed-by: Benjamin Block <bblock@linux.ibm.com>
Reviewed-by: Sakshi Singh <005c7w@linux.ibm.com>
Reviewed-by: M Nikhil <nikh1092@linux.ibm.com>
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
2024-12-13 16:26:35 +01:00
Jan Höppner d3b7581669 Prepare for next release
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
2024-12-06 12:26:08 +01:00
107 changed files with 5793 additions and 1543 deletions
+1
View File
@@ -59,6 +59,7 @@ List of all individuals having contributed content to s390-tools
- Horst Hummel - Horst Hummel
- Ingo Franzki - Ingo Franzki
- Ingo Tuchscherer - Ingo Tuchscherer
- Jakob Naucke
- Jakub Čajka - Jakub Čajka
- Jan Glauber - Jan Glauber
- Jan Höppner - Jan Höppner
+32
View File
@@ -1,6 +1,38 @@
Release history for s390-tools (MIT version) Release history for s390-tools (MIT version)
-------------------------------------------- --------------------------------------------
* __v2.37.0 (2025-02-07)__
For Linux kernel version: 6.13
Changes of existing tools:
- dbginfo.sh: Add details on CPU-measurement
- dbginfo.sh: Add new crypto command
- dbginfo.sh: Add overview commands and crypto update
- dbginfo.sh: Adding kdump info
- dbginfo.sh: Removing outdated email references
- dbginfo.sh: Rework network section
- dbginfo.sh: Update copyright 2nd year
- pvimg: Add '--(enable|disable)-image-encryption' flags to 'pvimg create'
- pvimg: Add '--cck <FILE>' command line option and make '--comm-key' an alias
- pvimg: Add '--hdr-key' command line option to 'pvimg create'
- pvimg: Rename '--key' into '--hdr-key' and use '--key' as an alias (for 'pvimg info')
- pvsecret: Add support for retrievable secrets
- ziorep_config: Add PCHID field to adapter report
- ziorep_traffic: Add DEVBUSID column to traffic report
- ziorep_utilization: Add --fcp-device parameter to print virtual adapter report
- ziorep_utilization: Add PCHID column to physical adapter report
- ziorep_utilization: Now prints only physical adapter report by default
- ziorep_utilization: Swap Bus-ID and CHPID columns in virtual adapter report
- zipl/boot: Increase section size for eckd_mv dumper
- zkey: Add support for listing and importing protected virtualization secrets
Bug Fixes:
- chpstat: Fix invalid utilization data on older kernels
- opticsmon: Fix runaway loop in on_link_change()
- zipl: Update inline assembly for GCC 15
- zipl_helper.device-mapper: Add missed step in logical device resolution
* __v2.36.0 (2024-12-06)__ * __v2.36.0 (2024-12-06)__
For Linux kernel version: 6.12 For Linux kernel version: 6.12
+1 -1
View File
@@ -32,7 +32,7 @@ endif
# The variable "DISTRELEASE" should be overwritten in rpm spec files with: # The variable "DISTRELEASE" should be overwritten in rpm spec files with:
# "make DISTRELEASE=%{release}" and "make install DISTRELEASE=%{release}" # "make DISTRELEASE=%{release}" and "make install DISTRELEASE=%{release}"
VERSION := 2 VERSION := 2
RELEASE := 36 RELEASE := 37
PATCHLEVEL := 0 PATCHLEVEL := 0
DISTRELEASE := build-$(shell date +%Y%m%d) DISTRELEASE := build-$(shell date +%Y%m%d)
S390_TOOLS_RELEASE := $(VERSION).$(RELEASE).$(PATCHLEVEL)-$(DISTRELEASE) S390_TOOLS_RELEASE := $(VERSION).$(RELEASE).$(PATCHLEVEL)-$(DISTRELEASE)
+155 -117
View File
@@ -46,8 +46,8 @@ All counters are initially disabled and must be switched on to measure
CPACF activities of the system. There is a slight performance penalty with CPACF activities of the system. There is a slight performance penalty with
CPACF counters enabled. CPACF counters enabled.
CPACF activity counters come in two flavors: CPU-MF and PAI. CPU-MF CPACF activity counters come in two flavors: CPU-MF and PAI. CPU-MF
counters are only available on LPARs and have to be authorized. If counters are only available on LPARs and have to be authorized. If
they are available, the counters they are available, the counters
.B des, .B des,
.B aes, .B aes,
@@ -55,14 +55,131 @@ they are available, the counters
.B rng, .B rng,
and and
.B ecc .B ecc
are made available. These counters can individually be activated, are made available. These counters can individually be activated,
reset, printed, or deactivated. PAI counters are a lot more detailed. reset, printed, or deactivated. PAI counters are a lot more detailed.
The user interface only offers the counters The user interface only offers the counters
.B pai_user .B pai_user
and and
.B pai_kernel .B pai_kernel
to count CPACF usage in user-space or kernel-space. When printing to count CPACF usage in user-space or kernel-space. When printing
these counters, detailed counters are shown. The detailed names are: these counters, detailed counters are shown.
A complete list of counters can be found at the end of this manpage.
Note that the counters starting with PCKMO and Reserved are only
available in the pai_kernel set. Also note that the counters are
designed to count successful operations. In the case of KMA this
means only complete GCM operations including final hashing are
counted.
Note: CPU-MF based CPACF performance counters are available on LPARs
only. PAI counters are available on all hypervisors. For security
reasons only members of the group \fIcpacfstats\fR are allowed to run
the cpacfstats client application.
Example usage scenario:
.P
1. Start the cpacfstatsd daemon with root privileges.
.P
2. Check for successful startup by using the ps and syslog commands.
.P
3. Enable the CPACF counters of interest. For example, enable all counters
by issuing cpacfstats -e.
.P
4. Run your applications.
.P
5. Display counter values by using the cpacfstats command. Reset the
cryptographic counters as required. To reset, use, for example, cpacfstats
-r.
.P
6. Disable all the CPACF measurements, for example, by using cpacfstats
-d.
.P
7. Shutdown the cpacfstatsd daemon by using killall cpacfstatsd.
.SH OPTIONS
.TP
\fB\-h\fR or \fB\-\-help\fR
Display help information for the command.
.TP
\fB\-v\fR or \fB\-\-version\fR
Display version and copyright information for the command.
.TP
\fB\-e\fR or \fB\-\-enable\fR [counter]
Enable one or all CPACF performance counters. The optional counter
argument can be one of: \fBdes\fR, \fBaes\fR, \fBsha\fR, \fBprng\fR,
\fBecc\fR, or \fBall\fR. If the counter argument is omitted, all
performance counters are enabled. Enabling a counter does not reset
it. New events are added to the current counter value.
.TP
\fB\-d\fR or \fB\-\-disable\fR [counter]
Disable one or all CPACF performance counters. The optional counter
argument can be one of: \fBdes\fR, \fBaes\fR, \fBsha\fR, \fBprng\fR,
\fBecc\fR, or \fBall\fR. If the counter argument is omitted, all
performance counters are disabled. Disabling a counter does not reset
it. The counter value is preserved when a counter is disabled, and
counting will resume using the preserved value when the counter is
re-enabled.
.TP
\fB\-r\fR or \fB\-\-reset\fR [counter]
Reset one or all CPACF performance counters. The optional counter
argument can be one of: \fBdes\fR, \fBaes\fR, \fBsha\fR, \fBprng\fR,
\fBecc\fR, or \fBall\fR. If the counter argument is omitted, all
performance counters are reset to 0.
.TP
\fB\-p\fR or \fB\-\-print\fR [\fB\-n\fR or \fB\-\-nonzero\fR] [counter]
Display the value of one or all CPACF performance counters. The
optional counter argument can be one of: \fBdes\fR, \fBaes\fR,
\fBsha\fR, \fBprng\fR, \fBecc\fR, \fBpai_user\fR, \fBpai_kernel\fR, or
\fBall\fR. If the counter argument is omitted or if there is no
argument, all performance counters are displayed. If the optional
\fB\-n\fR or \fB\-\-nonzero\fR argument is given, then only PAI
counters that have a non-zero value are printed.
.TP
\fB\-j\fR or \fB\-\-json\fR
Display all activated counters in JSON format. The JSON contains an
array of counter objects. Each object contains the property
.B counter
specifying either a CPU-MF counter of one of the detailed PAI counter.
Additional properties include
.B error
an error number if the counter could not be read,
.B value
the counter value if the counter could be read,
.B space
for PAI counters to specify
.B user
or
.B kernel
space counter set, and
.B counterid
for PAI counters to specify the PAI counter number as specified in the
Principles of Operation.
.TP
The default command is --print all.
.
.SH FILES
.nf
/run/cpacfstatsd_socket
.fi
.
.SH RETURN VALUE
.IP 0
Successful program execution.
.IP 1
An error occurred, reasons include: invalid argument, cpacfstatsd could not
be reached (check that the daemon is running), insufficient access rights,
version mismatch between client and daemon, or the application is out of
memory. The application prints a message with the details of the error and
the errno value.
.
.SH NOTES
ECC counters are only available since z15. cpacfstats will show the
counters as \fIunsupported\fR if the hardware does not support ECC
counters.
.
.SH APPENDIX
The detailed pai counter names are:
.RS .RS
.IP \(bu .IP \(bu
KM DES, KM DES,
@@ -376,118 +493,39 @@ PCKMO ECC Ed448,
Reserved 1, and Reserved 1, and
.IP \(bu .IP \(bu
Reserved 2. Reserved 2.
.IP \(bu
KM AES-XTS (full) 128bit
.IP \(bu
KM AES-XTS (full) 256bit
.IP \(bu
KM AES-XTS (full) 128bit protected key
.IP \(bu
KM AES-XTS (full) 256bit protected key
.IP \(bu
KMAC HMAC SHA 224
.IP \(bu
KMAC HMAC SHA 256
.IP \(bu
KMAC HMAC SHA 384
.IP \(bu
KMAC HMAC SHA 512
.IP \(bu
KMAC HMAC SHA 224 protected key
.IP \(bu
KMAC HMAC SHA 256 protected key
.IP \(bu
KMAC HMAC SHA 384 protected key
.IP \(bu
KMAC HMAC SHA 512 protected key
.IP \(bu
PCKMO HMAC 512 protected key
.IP \(bu
PCKMO HMAC 1024 protected key
.IP \(bu
PCKMO AES-XTS 128bit double key protected key
.IP \(bu
PCKMO AES-XTS 256bit double key protected key
.RE .RE
Note that the counters starting with PCKMO and Reserved are only
available in the pai_kernel set. Also note that the counters are
designed to count successful operations. In the case of KMA this
means only complete GCM operations including final hashing are
counted.
Note: CPU-MF based CPACF performance counters are available on LPARs
only. PAI counters are available on all hypervisors. For security
reasons only members of the group \fIcpacfstats\fR are allowed to run
the cpacfstats client application.
Example usage scenario:
.P
1. Start the cpacfstatsd daemon with root privileges.
.P
2. Check for successful startup by using the ps and syslog commands.
.P
3. Enable the CPACF counters of interest. For example, enable all counters
by issuing cpacfstats -e.
.P
4. Run your applications.
.P
5. Display counter values by using the cpacfstats command. Reset the
cryptographic counters as required. To reset, use, for example, cpacfstats
-r.
.P
6. Disable all the CPACF measurements, for example, by using cpacfstats
-d.
.P
7. Shutdown the cpacfstatsd daemon by using killall cpacfstatsd.
.SH OPTIONS
.TP
\fB\-h\fR or \fB\-\-help\fR
Display help information for the command.
.TP
\fB\-v\fR or \fB\-\-version\fR
Display version and copyright information for the command.
.TP
\fB\-e\fR or \fB\-\-enable\fR [counter]
Enable one or all CPACF performance counters. The optional counter
argument can be one of: \fBdes\fR, \fBaes\fR, \fBsha\fR, \fBprng\fR,
\fBecc\fR, or \fBall\fR. If the counter argument is omitted, all
performance counters are enabled. Enabling a counter does not reset
it. New events are added to the current counter value.
.TP
\fB\-d\fR or \fB\-\-disable\fR [counter]
Disable one or all CPACF performance counters. The optional counter
argument can be one of: \fBdes\fR, \fBaes\fR, \fBsha\fR, \fBprng\fR,
\fBecc\fR, or \fBall\fR. If the counter argument is omitted, all
performance counters are disabled. Disabling a counter does not reset
it. The counter value is preserved when a counter is disabled, and
counting will resume using the preserved value when the counter is
re-enabled.
.TP
\fB\-r\fR or \fB\-\-reset\fR [counter]
Reset one or all CPACF performance counters. The optional counter
argument can be one of: \fBdes\fR, \fBaes\fR, \fBsha\fR, \fBprng\fR,
\fBecc\fR, or \fBall\fR. If the counter argument is omitted, all
performance counters are reset to 0.
.TP
\fB\-p\fR or \fB\-\-print\fR [\fB\-n\fR or \fB\-\-nonzero\fR] [counter]
Display the value of one or all CPACF performance counters. The
optional counter argument can be one of: \fBdes\fR, \fBaes\fR,
\fBsha\fR, \fBprng\fR, \fBecc\fR, \fBpai_user\fR, \fBpai_kernel\fR, or
\fBall\fR. If the counter argument is omitted or if there is no
argument, all performance counters are displayed. If the optional
\fB\-n\fR or \fB\-\-nonzero\fR argument is given, then only PAI
counters that have a non-zero value are printed.
.TP
\fB\-j\fR or \fB\-\-json\fR
Display all activated counters in JSON format. The JSON contains an
array of counter objects. Each object contains the property
.B counter
specifying either a CPU-MF counter of one of the detailed PAI counter.
Additional properties include
.B error
an error number if the counter could not be read,
.B value
the counter value if the counter could be read,
.B space
for PAI counters to specify
.B user
or
.B kernel
space counter set, and
.B counterid
for PAI counters to specify the PAI counter number as specified in the
Principles of Operation.
.TP
The default command is --print all.
.
.SH FILES
.nf
/run/cpacfstatsd_socket
.fi
.
.SH RETURN VALUE
.IP 0
Successful program execution.
.IP 1
An error occurred, reasons include: invalid argument, cpacfstatsd could not
be reached (check that the daemon is running), insufficient access rights,
version mismatch between client and daemon, or the application is out of
memory. The application prints a message with the details of the error and
the errno value.
.
.SH NOTES
ECC counters are only available since z15. cpacfstats will show the
counters as \fIunsupported\fR if the hardware does not support ECC
counters.
. .
.SH SEE ALSO .SH SEE ALSO
.BR cpacfstatsd (8) .BR cpacfstatsd (8)
+1 -1
View File
@@ -10,7 +10,7 @@
.TH \*c "8" "May 2022" "s390-tools" "CPU-MF management programs" .TH \*c "8" "May 2022" "s390-tools" "CPU-MF management programs"
. .
.SH NAME .SH NAME
\*c \- extract CPU Measurement Facilities counter sets lshwc \- extract CPU Measurement Facilities counter sets
. .
.SH SYNOPSIS .SH SYNOPSIS
\*c \*c
+1 -1
View File
@@ -10,7 +10,7 @@
.TH \*c "8" "August 2023" "s390-tools" "CPU-MF management programs" .TH \*c "8" "August 2023" "s390-tools" "CPU-MF management programs"
. .
.SH NAME .SH NAME
\*c \- list Processor Activity Instrumentation (PAI) counters lspai \- list Processor Activity Instrumentation (PAI) counters
. .
.SH SYNOPSIS .SH SYNOPSIS
\*c \*c
+1 -1
View File
@@ -10,7 +10,7 @@
.TH \*c "8" "May 2022" "s390-tools" "PAI Management Programs" .TH \*c "8" "May 2022" "s390-tools" "PAI Management Programs"
. .
.SH NAME .SH NAME
\*c \- record and report Processor Activity Instrumentation (PAI) counters pai \- record and report Processor Activity Instrumentation (PAI) counters
. .
.SH SYNOPSIS .SH SYNOPSIS
\*c \*c
+303
View File
@@ -0,0 +1,303 @@
#!/bin/sh
#
# check_hostkeydoc - Verify an IBM Secure Execution host key document
#
# Sample script to verify that a host key document is genuine by
# verifying the issuer, the validity date and the signature.
# Optionally verify the full trust chain using a CA certificate.
#
# Sample invocation:
#
# ./check_hostkeydoc HKD1234.crt ibm-z-host-key-signing.crt -c DigiCertCA.crt -r ibm-z-host-key.crl
#
# Copyright IBM Corp. 2020
#
# s390-tools is free software; you can redistribute it and/or modify
# it under the terms of the MIT license. See LICENSE for details.
# Allocate temporary files
ISSUER_PUBKEY_FILE=$(mktemp)
SIGNATURE_FILE=$(mktemp)
BODY_FILE=$(mktemp)
ISSUER_DN_FILE=$(mktemp)
SUBJECT_DN_FILE=$(mktemp)
DEF_ISSUER_ARMONK_DN_FILE=$(mktemp)
DEF_ISSUER_POUGHKEEPSIE_DN_FILE=$(mktemp)
CANONICAL_ISSUER_DN_FILE=$(mktemp)
CRL_SERIAL_FILE=$(mktemp)
# Cleanup on exit
cleanup()
{
rm -f "$ISSUER_PUBKEY_FILE" "$SIGNATURE_FILE" "$BODY_FILE" \
"$ISSUER_DN_FILE" "$SUBJECT_DN_FILE" "$DEF_ISSUER_ARMONK_DN_FILE" "$DEF_ISSUER_POUGHKEEPSIE_DN_FILE" \
"$CANONICAL_ISSUER_DN_FILE" "$CRL_SERIAL_FILE"
}
trap cleanup EXIT
# Enhanced error checking for bash
if [ -n "${BASH}" ]; then
# shellcheck disable=SC3040
set -o posix
# shellcheck disable=SC3040
set -o pipefail
# shellcheck disable=SC3040
set -o nounset
fi
set -e
# Usage
usage()
{
cat <<-EOF
Usage: $(basename "$1") [-d] [-c CA-cert] [-r CRL] host-key-doc signing-key-cert
Verify an IBM Secure Execution host key document against
a signing key.
Options:
-d disable default issuer check of host-key-doc
-c CA-cert trusted CA certificate
-r CRL list of revoked host-key-docs
Note that in order to have the full trust chain verified
it is necessary to provide the issuing CA's certificate.
The default issuer check may be disabled if a non-default
signing key certificate needs to be verified against the
CA certificate.
EOF
}
check_verify_chain()
{
# Verify certificate chain in case a CA certificate file/bundle
# was specified on the command line.
if [ -z "$2" ]; then
cat >&2 <<-EOF
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
No CA certificate specified! Skipping trust chain verification.
Make sure that '$1' is a valid certificate.
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
EOF
else
openssl verify -crl_download -crl_check "$2" &&
openssl verify -crl_download -crl_check -untrusted "$2" "$1" ||
exit 1
fi
}
extract_pubkey()
{
openssl x509 -in "$1" -pubkey -noout >"$2"
}
extract_signature()
{
# Assuming that the last field is the signature
SIGOFFSET=$(openssl asn1parse -in "$1" | tail -1 | cut -d : -f 1)
openssl asn1parse -in "$1" -out "$2" -strparse "$SIGOFFSET" -noout
}
extract_body()
{
# Assuming that the first field is the full cert body
SIGOFFSET=$(openssl asn1parse -in "$1" | head -2 | tail -1 | cut -d : -f 1)
openssl asn1parse -in "$1" -out "$2" -strparse "$SIGOFFSET" -noout
}
verify_signature()
{
# Assuming that the signature algorithm is SHA512 with RSA
openssl sha512 -verify "$1" -signature "$2" "$3"
}
canonical_dn()
{
OBJTYPE=$1
OBJ=$2
DNTYPE=$3
OUTPUT=$4
openssl "$OBJTYPE" -in "$OBJ" -"$DNTYPE" -noout -nameopt multiline |
LC_ALL=C sort | grep -v "$DNTYPE"= >"$OUTPUT"
}
default_issuer_armonk()
{
cat <<-EOF
commonName = International Business Machines Corporation
countryName = US
localityName = Armonk
organizationName = International Business Machines Corporation
organizationalUnitName = Key Signing Service
stateOrProvinceName = New York
EOF
}
default_issuer_pougkeepsie()
{
cat <<-EOF
commonName = International Business Machines Corporation
countryName = US
localityName = Poughkeepsie
organizationName = International Business Machines Corporation
organizationalUnitName = Key Signing Service
stateOrProvinceName = New York
EOF
}
# As organizationalUnitName can have an arbitrary prefix but must
# end with "Key Signing Service" let's normalize the OU name by
# stripping off the prefix
verify_default_issuer()
{
default_issuer_pougkeepsie >"$DEF_ISSUER_POUGHKEEPSIE_DN_FILE"
default_issuer_armonk >"$DEF_ISSUER_ARMONK_DN_FILE"
sed "s/\(^[ ]*organizationalUnitName[ ]*=[ ]*\).*\(Key Signing Service$\)/\1\2/" \
"$ISSUER_DN_FILE" >"$CANONICAL_ISSUER_DN_FILE"
if ! {
diff "$CANONICAL_ISSUER_DN_FILE" "$DEF_ISSUER_POUGHKEEPSIE_DN_FILE" ||
diff "$CANONICAL_ISSUER_DN_FILE" "$DEF_ISSUER_ARMONK_DN_FILE"
} >/dev/null 2>&1; then
echo Incorrect default issuer >&2 && exit 1
fi
}
verify_issuer_files()
{
if [ "$1" -eq 1 ]; then
verify_default_issuer
fi
}
cert_time()
{
DATE=$(openssl x509 -in "$1" -"$2" -noout | sed "s/^.*=//")
date -d "$DATE" +%s
}
crl_time()
{
DATE=$(openssl crl -in "$1" -"$2" -noout | sed "s/^.*=//")
date -d "$DATE" +%s
}
verify_dates()
{
START="$1"
END="$2"
MSG="${3:-Certificate}"
NOW=$(date +%s)
if [ "$START" -le "$NOW" ] && [ "$NOW" -le "$END" ]; then
echo "${MSG} dates are OK"
else
echo "${MSG} date verification failed" >&2 && exit 1
fi
}
crl_serials()
{
openssl crl -in "$1" -text -noout |
grep "Serial Number" >"$CRL_SERIAL_FILE"
}
check_serial()
{
CERT_SERIAL=$(openssl x509 -in "$1" -noout -serial | cut -d = -f 2)
grep -q "$CERT_SERIAL" "$CRL_SERIAL_FILE"
}
check_file()
{
[ -e "$1" ] ||
(echo "File '$1' not found" >&2 && exit 1)
}
# check args
CRL_FILE=
CA_FILE=
CHECK_DEFAULT_ISSUER=1
while getopts 'dr:c:h' opt; do
case $opt in
d) CHECK_DEFAULT_ISSUER=0 ;;
r) CRL_FILE=$OPTARG ;;
c) CA_FILE=$OPTARG ;;
h)
usage "$0"
exit 0
;;
?)
usage "$0"
exit 1
;;
esac
done
shift "$((OPTIND - 1))"
if [ $# -ne 2 ]; then
usage "$0" >&2
exit 1
fi
HKD_FILE=$1
HKSK_FILE=$2
# Check whether all specified files exist
check_file "$HKD_FILE"
check_file "$HKSK_FILE"
# CA and CRL are optional arguments
[ -n "$CA_FILE" ] && check_file "$CA_FILE"
[ -n "$CRL_FILE" ] && check_file "$CRL_FILE"
# Check trust chain
check_verify_chain "$HKSK_FILE" "$CA_FILE"
# Verify host key document signature
printf "Checking host key document signature: "
extract_pubkey "$HKSK_FILE" "$ISSUER_PUBKEY_FILE" &&
extract_signature "$HKD_FILE" "$SIGNATURE_FILE" &&
extract_body "$HKD_FILE" "$BODY_FILE" &&
verify_signature "$ISSUER_PUBKEY_FILE" "$SIGNATURE_FILE" "$BODY_FILE" ||
exit 1
# Verify the issuer
canonical_dn x509 "$HKD_FILE" issuer "$ISSUER_DN_FILE"
canonical_dn x509 "$HKSK_FILE" subject "$SUBJECT_DN_FILE"
verify_issuer_files $CHECK_DEFAULT_ISSUER
# Verify dates
verify_dates "$(cert_time "$HKD_FILE" startdate)" "$(cert_time "$HKD_FILE" enddate)"
# Check CRL if specified
if [ -n "$CRL_FILE" ]; then
printf "Checking CRL signature: "
extract_signature "$CRL_FILE" "$SIGNATURE_FILE" &&
extract_body "$CRL_FILE" "$BODY_FILE" &&
verify_signature "$ISSUER_PUBKEY_FILE" "$SIGNATURE_FILE" "$BODY_FILE" ||
exit 1
printf "CRL "
canonical_dn crl "$CRL_FILE" issuer "$ISSUER_DN_FILE"
canonical_dn x509 "$HKSK_FILE" subject "$SUBJECT_DN_FILE"
verify_issuer_files $CHECK_DEFAULT_ISSUER
verify_dates "$(crl_time "$CRL_FILE" lastupdate)" "$(crl_time "$CRL_FILE" nextupdate)" 'CRL'
crl_serials "$CRL_FILE"
check_serial "$HKD_FILE" &&
echo "Certificate is revoked, do not use it anymore!" >&2 &&
exit 1
fi
# We made it
echo All checks requested for \'"$HKD_FILE"\' were successful
+2 -2
View File
@@ -21,7 +21,7 @@
#define STAGE2_DESC _AC(0x78, UL) #define STAGE2_DESC _AC(0x78, UL)
#define STAGE2_ENTRY _AC(0x2018, UL) #define STAGE2_ENTRY _AC(0x2018, UL)
#define ECKD2DUMP_MV_TAIL_ADDRESS _AC(0x5000, UL) #define ECKD2DUMP_MV_TAIL_ADDRESS _AC(0x6000, UL)
#define STAGE2_HEAP_ADDRESS _AC(0x6000, UL) #define STAGE2_HEAP_ADDRESS _AC(0x6000, UL)
#define ECKD2DUMP_HEAP_ADDRESS _AC(0xb000, UL) #define ECKD2DUMP_HEAP_ADDRESS _AC(0xb000, UL)
#define STAGE2_HEAP_SIZE _AC(0x3000, UL) #define STAGE2_HEAP_SIZE _AC(0x3000, UL)
@@ -34,7 +34,7 @@
#define STAGE2_DUMPER_SIZE_V1 _AC(0x1000, UL) #define STAGE2_DUMPER_SIZE_V1 _AC(0x1000, UL)
#define STAGE2_DUMPER_SIZE_V2 _AC(0x2000, UL) #define STAGE2_DUMPER_SIZE_V2 _AC(0x2000, UL)
#define STAGE2_DUMPER_SIZE_V3 _AC(0x3000, UL) #define STAGE2_DUMPER_SIZE_V3 _AC(0x3000, UL)
#define STAGE2_DUMPER_SIZE_MV _AC(0x3000, UL) #define STAGE2_DUMPER_SIZE_MV _AC(0x4000, UL)
#define STAGE2_DUMPER_SIZE_ZLIB _AC(0x8000, UL) #define STAGE2_DUMPER_SIZE_ZLIB _AC(0x8000, UL)
#define STAGE3_ENTRY _AC(0xa000, UL) #define STAGE3_ENTRY _AC(0xa000, UL)
+3
View File
@@ -93,4 +93,7 @@ const char *zpci_pft_str(struct zpci_dev *zdev);
const char *zpci_operstate_str(operstate_t state); const char *zpci_operstate_str(operstate_t state);
operstate_t zpci_operstate_from_str(const char *oper_str); 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 */ #endif /* LIB_ZPCI_PCI_LIST_H */
+31
View File
@@ -356,3 +356,34 @@ void zpci_free_dev_list(struct util_list *zpci_list)
} }
util_list_free(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;
}
+1 -3
View File
@@ -171,8 +171,6 @@ static const char help_text[] =
"-h, --help Print this help, then exit\n" "-h, --help Print this help, then exit\n"
"-v, --version Print version information, then exit\n" "-v, --version Print version information, then exit\n"
"-a, --attach Run in foreground\n" "-a, --attach Run in foreground\n"
"-i, --interval=<seconds> Sample interval\n" "-i, --interval=<seconds> Sample interval\n";
"\n"
"Please report bugs to: linux390@de.ibm.com\n";
#endif #endif
+13 -18
View File
@@ -274,38 +274,33 @@ static int oneshot_mode(struct opticsmon_ctx *ctx)
void on_link_change(struct zpci_netdev *netdev, void *arg) void on_link_change(struct zpci_netdev *netdev, void *arg)
{ {
struct opticsmon_ctx *ctx = arg; struct opticsmon_ctx *ctx = arg;
struct zpci_dev *zdev; struct zpci_netdev *found_netdev;
int i, reloads = 1; struct zpci_dev *zdev = NULL;
int reloads = 1;
if (!ctx->zpci_list || util_list_is_empty(ctx->zpci_list)) do {
zpci_list_reload(&ctx->zpci_list); if (ctx->zpci_list) {
zdev = zpci_find_by_netdev(ctx->zpci_list, netdev->name, &found_netdev);
reload: if (zdev) {
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 /* Skip data collection if operational state is
* unchanged * unchanged
*/ */
if (zdev->netdevs[i].operstate == netdev->operstate) if (found_netdev->operstate == netdev->operstate)
continue; return;
/* Update operation state for VFs even though /* Update operation state for VFs even though
* they are skipped just for a consistent view * 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 */ /* Only collect optics data for PFs */
if (!zpci_is_vf(zdev)) if (!zpci_is_vf(zdev))
dump_adapter_data(ctx, zdev); dump_adapter_data(ctx, zdev);
return;
} }
} }
} /* Could be uninitalized list or a new device, retry after reload */
/* Might be a new device, reload list of devices and retry */
if (reloads) {
zpci_list_reload(&ctx->zpci_list); zpci_list_reload(&ctx->zpci_list);
reloads--; reloads--;
goto reload; } while (reloads > 0);
}
} }
#define MAX_EVENTS 8 #define MAX_EVENTS 8
+172 -321
View File
@@ -4,18 +4,18 @@ version = 3
[[package]] [[package]]
name = "aho-corasick" name = "aho-corasick"
version = "1.1.2" version = "1.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b2969dcb958b36655471fc61f7e416fa76033bdd4bfed0678d8fee1e2d07a1f0" checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916"
dependencies = [ dependencies = [
"memchr", "memchr",
] ]
[[package]] [[package]]
name = "anstream" name = "anstream"
version = "0.6.15" version = "0.6.18"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "64e15c1ab1f89faffbf04a634d5e1962e9074f2741eef6d97f3c4e322426d526" checksum = "8acc5369981196006228e28809f761875c0327210a891e941f4c683b3a99529b"
dependencies = [ dependencies = [
"anstyle", "anstyle",
"anstyle-parse", "anstyle-parse",
@@ -34,43 +34,43 @@ checksum = "55cc3b69f167a1ef2e161439aa98aed94e6028e5f9a59be9a6ffb47aef1651f9"
[[package]] [[package]]
name = "anstyle-parse" name = "anstyle-parse"
version = "0.2.0" version = "0.2.6"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e765fd216e48e067936442276d1d57399e37bce53c264d6fefbe298080cb57ee" checksum = "3b2d16507662817a6a20a9ea92df6652ee4f94f914589377d69f3b21bc5798a9"
dependencies = [ dependencies = [
"utf8parse", "utf8parse",
] ]
[[package]] [[package]]
name = "anstyle-query" name = "anstyle-query"
version = "1.0.0" version = "1.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5ca11d4be1bab0c8bc8734a9aa7bf4ee8316d462a08c6ac5052f888fef5b494b" checksum = "79947af37f4177cfead1110013d678905c37501914fba0efea834c3fe9a8d60c"
dependencies = [ dependencies = [
"windows-sys 0.48.0", "windows-sys 0.59.0",
] ]
[[package]] [[package]]
name = "anstyle-wincon" name = "anstyle-wincon"
version = "3.0.4" version = "3.0.6"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5bf74e1b6e971609db8ca7a9ce79fd5768ab6ae46441c572e46cf596f59e57f8" checksum = "2109dbce0e72be3ec00bed26e6a7479ca384ad226efdd66db8fa2e3a38c83125"
dependencies = [ dependencies = [
"anstyle", "anstyle",
"windows-sys 0.52.0", "windows-sys 0.59.0",
] ]
[[package]] [[package]]
name = "anyhow" name = "anyhow"
version = "1.0.71" version = "1.0.95"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9c7d0618f0e0b7e8ff11427422b64564d5fb0be1940354bfe2e0529b18a9d9b8" checksum = "34ac096ce696dc2fcabef30516bb13c0a68a11d30131d3df6f04711467681b04"
[[package]] [[package]]
name = "autocfg" name = "autocfg"
version = "1.1.0" version = "1.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26"
[[package]] [[package]]
name = "base64" name = "base64"
@@ -80,30 +80,24 @@ checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6"
[[package]] [[package]]
name = "bit-set" name = "bit-set"
version = "0.5.3" version = "0.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0700ddab506f33b20a03b13996eccd309a48e5ff77d0d95926aa0210fb4e95f1" checksum = "08807e080ed7f9d5433fa9b275196cfc35414f66a0c79d864dc51a0d825231a3"
dependencies = [ dependencies = [
"bit-vec", "bit-vec",
] ]
[[package]] [[package]]
name = "bit-vec" name = "bit-vec"
version = "0.6.3" version = "0.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "349f9b6a179ed607305526ca489b34ad0a41aed5f7980fa90eb03160b69598fb" checksum = "5e764a1d40d510daf35e07be9eb06e75770908c27d411ee6c92109c9840eaaf7"
[[package]] [[package]]
name = "bitflags" name = "bitflags"
version = "1.3.2" version = "2.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" checksum = "1be3f42a67d6d345ecd59f675f3f012d6974981560836e938c22b424b85ce1be"
[[package]]
name = "bitflags"
version = "2.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "327762f6e5a765692301e5bb513e0d9fef63be86bbc14528052b1cd3e6f03e07"
[[package]] [[package]]
name = "bitvec" name = "bitvec"
@@ -119,15 +113,18 @@ dependencies = [
[[package]] [[package]]
name = "byteorder" name = "byteorder"
version = "1.4.3" version = "1.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610" checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b"
[[package]] [[package]]
name = "cc" name = "cc"
version = "1.0.79" version = "1.2.9"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "50d30906286121d95be3d479533b458f87493b30a4b5f79a607db8f5d11aa91f" checksum = "c8293772165d9345bdaaa39b45b2109591e63fe5e6fbc23c6ff930a048aa310b"
dependencies = [
"shlex",
]
[[package]] [[package]]
name = "cfg-if" name = "cfg-if"
@@ -137,9 +134,9 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
[[package]] [[package]]
name = "clap" name = "clap"
version = "4.5.18" version = "4.5.23"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b0956a43b323ac1afaffc053ed5c4b7c1f1800bacd1683c353aabbb752515dd3" checksum = "3135e7ec2ef7b10c6ed8950f0f792ed96ee093fa088608f1c76e569722700c84"
dependencies = [ dependencies = [
"clap_builder", "clap_builder",
"clap_derive", "clap_derive",
@@ -147,9 +144,9 @@ dependencies = [
[[package]] [[package]]
name = "clap_builder" name = "clap_builder"
version = "4.5.18" version = "4.5.23"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4d72166dd41634086d5803a47eb71ae740e61d84709c36f3c34110173db3961b" checksum = "30582fc632330df2bd26877bde0c1f4470d57c582bbc070376afcd04d8cb4838"
dependencies = [ dependencies = [
"anstream", "anstream",
"anstyle", "anstyle",
@@ -160,9 +157,9 @@ dependencies = [
[[package]] [[package]]
name = "clap_complete" name = "clap_complete"
version = "4.5.34" version = "4.5.38"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e16bb32eaa759f7fe76c59793c4e00dad6d11705f74ddfe4887e62f459536f16" checksum = "d9647a559c112175f17cf724dc72d3645680a883c58481332779192b0d8e7a01"
dependencies = [ dependencies = [
"clap", "clap",
] ]
@@ -181,15 +178,15 @@ dependencies = [
[[package]] [[package]]
name = "clap_lex" name = "clap_lex"
version = "0.7.3" version = "0.7.4"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "afb84c814227b90d6895e01398aee0d8033c00e7466aca416fb6a8e0eb19d8a7" checksum = "f46ad14479a25103f283c0f10005961cf086d8dc42205bb44c46ac563475dca6"
[[package]] [[package]]
name = "colorchoice" name = "colorchoice"
version = "1.0.0" version = "1.0.3"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "acbf1af155f9b9ef647e42cdc158db4b64a1b61f743629225fde6f3e0be2a7c7" checksum = "5b63caa9aa9397e2d9480a9b13673856c78d8ac123288526c37d7839f2a86990"
[[package]] [[package]]
name = "cpacfinfo" name = "cpacfinfo"
@@ -208,9 +205,9 @@ dependencies = [
[[package]] [[package]]
name = "curl" name = "curl"
version = "0.4.44" version = "0.4.47"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "509bd11746c7ac09ebd19f0b17782eae80aadee26237658a6b4808afb5c11a22" checksum = "d9fb4d13a1be2b58f14d60adba57c9834b78c62fd86c3e76a148f732686e9265"
dependencies = [ dependencies = [
"curl-sys", "curl-sys",
"libc", "libc",
@@ -218,14 +215,14 @@ dependencies = [
"openssl-sys", "openssl-sys",
"schannel", "schannel",
"socket2", "socket2",
"winapi", "windows-sys 0.52.0",
] ]
[[package]] [[package]]
name = "curl-sys" name = "curl-sys"
version = "0.4.72+curl-8.6.0" version = "0.4.78+curl-8.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "29cbdc8314c447d11e8fd156dcdd031d9e02a7a976163e396b548c03153bc9ea" checksum = "8eec768341c5c7789611ae51cf6c459099f22e64a5d5d0ce4892434e33821eaf"
dependencies = [ dependencies = [
"cc", "cc",
"libc", "libc",
@@ -316,30 +313,19 @@ checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5"
[[package]] [[package]]
name = "errno" name = "errno"
version = "0.3.1" version = "0.3.10"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4bcfec3a70f97c962c307b2d2c56e358cf1d00b558d74262b5f929ee8cc7e73a" checksum = "33d852cb9b869c2a9b3df2f71a3074817f01e1844f839a144f5fcef059a4eb5d"
dependencies = [ dependencies = [
"errno-dragonfly",
"libc",
"windows-sys 0.48.0",
]
[[package]]
name = "errno-dragonfly"
version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "aa68f1b12764fab894d2755d2518754e71b4fd80ecfb822714a1206c2aab39bf"
dependencies = [
"cc",
"libc", "libc",
"windows-sys 0.59.0",
] ]
[[package]] [[package]]
name = "fastrand" name = "fastrand"
version = "2.2.0" version = "2.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "486f806e73c5707928240ddc295403b1b93c96a02038563881c4a2fd84b81ac4" checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be"
[[package]] [[package]]
name = "fnv" name = "fnv"
@@ -370,9 +356,9 @@ checksum = "e6d5a32815ae3f33302d95fdcb2ce17862f8c65363dcfd29360480ba1001fc9c"
[[package]] [[package]]
name = "getrandom" name = "getrandom"
version = "0.2.10" version = "0.2.15"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "be4136b2a15dd319360be1c07d9933517ccf0be8f16bf62a3bee4f0d618df427" checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7"
dependencies = [ dependencies = [
"cfg-if", "cfg-if",
"libc", "libc",
@@ -437,27 +423,21 @@ checksum = "453ad9f582a441959e5f0d088b02ce04cfe8d51a8eaf077f12ac6d3e94164ca6"
[[package]] [[package]]
name = "lazy_static" name = "lazy_static"
version = "1.4.0" version = "1.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe"
[[package]] [[package]]
name = "libc" name = "libc"
version = "0.2.146" version = "0.2.169"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f92be4933c13fd498862a9e02a3055f8a8d9c039ce33db97306fd5a6caa7f29b" checksum = "b5aba8db14291edd000dfcc4d620c7ebfb122c613afb886ca8803fa4e128a20a"
[[package]]
name = "libm"
version = "0.2.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8355be11b20d696c8f18f6cc018c4e372165b1fa8126cef092399c9951984ffa"
[[package]] [[package]]
name = "libz-sys" name = "libz-sys"
version = "1.1.9" version = "1.1.20"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "56ee889ecc9568871456d42f603d6a0ce59ff328d291063a45cbdf0036baf6db" checksum = "d2d16453e800a8cf6dd2fc3eb4bc99b786a9b90c663b8559a5b1a041bf89e472"
dependencies = [ dependencies = [
"cc", "cc",
"libc", "libc",
@@ -467,21 +447,21 @@ dependencies = [
[[package]] [[package]]
name = "linux-raw-sys" name = "linux-raw-sys"
version = "0.4.14" version = "0.4.15"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "78b3ae25bc7c8c38cec158d1f2757ee79e9b3740fbc7ccf0e59e4b08d793fa89" checksum = "d26c52dbd32dccf2d10cac7725f8eae5296885fb5703b261f7d0a0739ec807ab"
[[package]] [[package]]
name = "log" name = "log"
version = "0.4.19" version = "0.4.25"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b06a4cde4c0f271a446782e3eff8de789548ce57dbc8eca9292c27f4a42004b4" checksum = "04cbf5b083de1c7e0222a7a51dbfdba1cbe1c6ab0b15e29fff3f6c077fd9cd9f"
[[package]] [[package]]
name = "memchr" name = "memchr"
version = "2.6.4" version = "2.7.4"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f665ee40bc4a3c5590afb1e9677db74a508659dfd71e126420da8274909a0167" checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3"
[[package]] [[package]]
name = "no_std_io2" name = "no_std_io2"
@@ -499,22 +479,21 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841"
dependencies = [ dependencies = [
"autocfg", "autocfg",
"libm",
] ]
[[package]] [[package]]
name = "once_cell" name = "once_cell"
version = "1.19.0" version = "1.20.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" checksum = "1261fe7e33c73b354eab43b1273a57c8f967d0391e80353e51f764ac02cf6775"
[[package]] [[package]]
name = "openssl" name = "openssl"
version = "0.10.68" version = "0.10.70"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6174bc48f102d208783c2c84bf931bb75927a617866870de8a4ea85597f871f5" checksum = "61cfb4e166a8bb8c9b55c500bc2308550148ece889be90f609377e58140f42c6"
dependencies = [ dependencies = [
"bitflags 2.4.1", "bitflags",
"cfg-if", "cfg-if",
"foreign-types", "foreign-types",
"libc", "libc",
@@ -542,9 +521,9 @@ checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf"
[[package]] [[package]]
name = "openssl-sys" name = "openssl-sys"
version = "0.9.104" version = "0.9.105"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "45abf306cbf99debc8195b66b7346498d7b10c210de50418b5ccd7ceba08c741" checksum = "8b22d5b84be05a8d6947c7cb71f7c849aa0f112acd4bf51c2a7c1c988ac0a9dc"
dependencies = [ dependencies = [
"cc", "cc",
"libc", "libc",
@@ -554,15 +533,18 @@ dependencies = [
[[package]] [[package]]
name = "pkg-config" name = "pkg-config"
version = "0.3.27" version = "0.3.31"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "26072860ba924cbfa98ea39c8c19b4dd6a4a25423dbdf219c1eca91aa0cf6964" checksum = "953ec861398dccce10c670dfeaf3ec4911ca479e9c02154b3a215178c5f566f2"
[[package]] [[package]]
name = "ppv-lite86" name = "ppv-lite86"
version = "0.2.17" version = "0.2.20"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" checksum = "77957b295656769bb8ad2b6a6b09d897d94f05c41b069aede1fcdaa675eaea04"
dependencies = [
"zerocopy",
]
[[package]] [[package]]
name = "proc-macro-crate" name = "proc-macro-crate"
@@ -575,22 +557,22 @@ dependencies = [
[[package]] [[package]]
name = "proc-macro2" name = "proc-macro2"
version = "1.0.75" version = "1.0.93"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "907a61bd0f64c2f29cd1cf1dc34d05176426a3f504a78010f08416ddb7b13708" checksum = "60946a68e5f9d28b0dc1c21bb8a97ee7d018a8b322fa57838ba31cc878e22d99"
dependencies = [ dependencies = [
"unicode-ident", "unicode-ident",
] ]
[[package]] [[package]]
name = "proptest" name = "proptest"
version = "1.5.0" version = "1.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b4c2511913b88df1637da85cc8d96ec8e43a3f8bb8ccb71ee1ac240d6f3df58d" checksum = "14cae93065090804185d3b75f0bf93b8eeda30c7a9b4a33d3bdb3988d6229e50"
dependencies = [ dependencies = [
"bit-set", "bit-set",
"bit-vec", "bit-vec",
"bitflags 2.4.1", "bitflags",
"lazy_static", "lazy_static",
"num-traits", "num-traits",
"rand", "rand",
@@ -679,9 +661,9 @@ checksum = "a1d01941d82fa2ab50be1e79e6714289dd7cde78eba4c074bc5a4374f650dfe0"
[[package]] [[package]]
name = "quote" name = "quote"
version = "1.0.35" version = "1.0.38"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "291ec9ab5efd934aaf503a6466c5d5251535d108ee747472c3977cc5acc868ef" checksum = "0e4dccaaaf89514f546c693ddc140f729f958c247918a13380cccc6078391acc"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
] ]
@@ -731,20 +713,11 @@ dependencies = [
"rand_core", "rand_core",
] ]
[[package]]
name = "redox_syscall"
version = "0.3.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "567664f262709473930a4bf9e51bf2ebf3348f2e748ccc50dea20646858f8f29"
dependencies = [
"bitflags 1.3.2",
]
[[package]] [[package]]
name = "regex" name = "regex"
version = "1.10.2" version = "1.11.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "380b951a9c5e80ddfd6136919eef32310721aa4aacd4889a8d39124b026ab343" checksum = "b544ef1b4eac5dc2db33ea63606ae9ffcfac26c1416a2806ae0bf5f56b201191"
dependencies = [ dependencies = [
"aho-corasick", "aho-corasick",
"memchr", "memchr",
@@ -754,9 +727,9 @@ dependencies = [
[[package]] [[package]]
name = "regex-automata" name = "regex-automata"
version = "0.4.3" version = "0.4.9"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5f804c7828047e88b2d32e2d7fe5a105da8ee3264f01902f796c8e067dc2483f" checksum = "809e8dc61f6de73b46c85f4c96486310fe304c434cfa43669d7b40f711150908"
dependencies = [ dependencies = [
"aho-corasick", "aho-corasick",
"memchr", "memchr",
@@ -765,21 +738,21 @@ dependencies = [
[[package]] [[package]]
name = "regex-syntax" name = "regex-syntax"
version = "0.8.2" version = "0.8.5"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c08c74e62047bb2de4ff487b251e4a92e24f48745648451635cec7d591162d9f" checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c"
[[package]] [[package]]
name = "rustix" name = "rustix"
version = "0.38.3" version = "0.38.43"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ac5ffa1efe7548069688cd7028f32591853cd7b5b756d41bcffd2353e4fc75b4" checksum = "a78891ee6bf2340288408954ac787aa063d8e8817e9f53abb37c695c6d834ef6"
dependencies = [ dependencies = [
"bitflags 2.4.1", "bitflags",
"errno", "errno",
"libc", "libc",
"linux-raw-sys", "linux-raw-sys",
"windows-sys 0.48.0", "windows-sys 0.59.0",
] ]
[[package]] [[package]]
@@ -840,27 +813,27 @@ dependencies = [
[[package]] [[package]]
name = "schannel" name = "schannel"
version = "0.1.21" version = "0.1.27"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "713cfb06c7059f3588fb8044c0fad1d09e3c01d225e25b9220dbfdcf16dbb1b3" checksum = "1f29ebaa345f945cec9fbbc532eb307f0fdad8161f281b6369539c8d84876b3d"
dependencies = [ dependencies = [
"windows-sys 0.42.0", "windows-sys 0.59.0",
] ]
[[package]] [[package]]
name = "serde" name = "serde"
version = "1.0.164" version = "1.0.217"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9e8c8cf938e98f769bc164923b06dce91cea1751522f46f8466461af04c9027d" checksum = "02fc4265df13d6fa1d00ecff087228cc0a2b5f3c0e87e258d8b94a156e984c70"
dependencies = [ dependencies = [
"serde_derive", "serde_derive",
] ]
[[package]] [[package]]
name = "serde_derive" name = "serde_derive"
version = "1.0.164" version = "1.0.217"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d9735b638ccc51c28bf6914d90a2e9725b377144fc612c49a611fddd1b631d68" checksum = "5a9bf7cf98d04a2b28aead066b7496853d4779c9cc183c440dbac457641e19a0"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
@@ -880,9 +853,9 @@ dependencies = [
[[package]] [[package]]
name = "serde_test" name = "serde_test"
version = "1.0.176" version = "1.0.177"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5a2f49ace1498612d14f7e0b8245519584db8299541dfe31a06374a828d620ab" checksum = "7f901ee573cab6b3060453d2d5f0bae4e6d628c23c0a962ff9b5f1d7c8d4f1ed"
dependencies = [ dependencies = [
"serde", "serde",
] ]
@@ -901,13 +874,19 @@ dependencies = [
] ]
[[package]] [[package]]
name = "socket2" name = "shlex"
version = "0.4.9" version = "1.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "64a4a911eed85daf18834cfaa86a79b7d266ff93ff5ba14005426219480ed662" checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64"
[[package]]
name = "socket2"
version = "0.5.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c970269d99b64e60ec3bd6ad27270092a5394c4e309314b18ae3fe575695fbe8"
dependencies = [ dependencies = [
"libc", "libc",
"winapi", "windows-sys 0.52.0",
] ]
[[package]] [[package]]
@@ -918,9 +897,9 @@ checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f"
[[package]] [[package]]
name = "syn" name = "syn"
version = "2.0.47" version = "2.0.96"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1726efe18f42ae774cc644f330953a5e7b3c3003d3edcecf18850fe9d4dd9afb" checksum = "d5d0adab1ae378d7f53bdebc67a39f1f151407ef230f0ce2883572f5d8985c80"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
@@ -935,41 +914,42 @@ checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369"
[[package]] [[package]]
name = "tempfile" name = "tempfile"
version = "3.8.0" version = "3.15.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cb94d2f3cc536af71caac6b6fcebf65860b347e7ce0cc9ebe8f70d3e521054ef" checksum = "9a8a559c81686f576e8cd0290cd2a24a2a9ad80c98b3478856500fcbd7acd704"
dependencies = [ dependencies = [
"cfg-if", "cfg-if",
"fastrand", "fastrand",
"redox_syscall", "getrandom",
"once_cell",
"rustix", "rustix",
"windows-sys 0.48.0", "windows-sys 0.59.0",
] ]
[[package]] [[package]]
name = "terminal_size" name = "terminal_size"
version = "0.3.0" version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "21bebf2b7c9e0a515f6e0f8c51dc0f8e4696391e6f1ff30379559f8365fb0df7" checksum = "4f599bd7ca042cfdf8f4512b277c02ba102247820f9d9d4a9f521f496751a6ef"
dependencies = [ dependencies = [
"rustix", "rustix",
"windows-sys 0.48.0", "windows-sys 0.59.0",
] ]
[[package]] [[package]]
name = "thiserror" name = "thiserror"
version = "1.0.40" version = "2.0.11"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "978c9a314bd8dc99be594bc3c175faaa9794be04a5a5e153caba6915336cebac" checksum = "d452f284b73e6d76dd36758a0c8684b1d5be31f92b89d07fd5822175732206fc"
dependencies = [ dependencies = [
"thiserror-impl", "thiserror-impl",
] ]
[[package]] [[package]]
name = "thiserror-impl" name = "thiserror-impl"
version = "1.0.40" version = "2.0.11"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f9456a42c5b0d803c8cd86e73dd7cc9edd429499f37a3550d286d5e86720569f" checksum = "26afc1baea8a989337eeb52b6e72a039780ce45c3edfcc9c5b9d112feeb173c2"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
@@ -1001,9 +981,9 @@ checksum = "eaea85b334db583fe3274d12b4cd1880032beab409c0d774be044d4480ab9a94"
[[package]] [[package]]
name = "unicode-ident" name = "unicode-ident"
version = "1.0.9" version = "1.0.14"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b15811caf2415fb889178633e7724bad2509101cde276048e013b9def5e51fa0" checksum = "adb9e6ca4f869e1180728b7950e35922a7fc6397f7b641499e8f3ef06e50dc83"
[[package]] [[package]]
name = "unsafe-libyaml" name = "unsafe-libyaml"
@@ -1013,9 +993,9 @@ checksum = "1865806a559042e51ab5414598446a5871b561d21b6764f2eabb0dd481d880a6"
[[package]] [[package]]
name = "utf8parse" name = "utf8parse"
version = "0.2.1" version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a" checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821"
[[package]] [[package]]
name = "utils" name = "utils"
@@ -1049,216 +1029,87 @@ version = "0.11.0+wasi-snapshot-preview1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
[[package]]
name = "winapi"
version = "0.3.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419"
dependencies = [
"winapi-i686-pc-windows-gnu",
"winapi-x86_64-pc-windows-gnu",
]
[[package]]
name = "winapi-i686-pc-windows-gnu"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
[[package]]
name = "winapi-x86_64-pc-windows-gnu"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
[[package]]
name = "windows-sys"
version = "0.42.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5a3e1820f08b8513f676f7ab6c1f99ff312fb97b553d30ff4dd86f9f15728aa7"
dependencies = [
"windows_aarch64_gnullvm 0.42.2",
"windows_aarch64_msvc 0.42.2",
"windows_i686_gnu 0.42.2",
"windows_i686_msvc 0.42.2",
"windows_x86_64_gnu 0.42.2",
"windows_x86_64_gnullvm 0.42.2",
"windows_x86_64_msvc 0.42.2",
]
[[package]]
name = "windows-sys"
version = "0.48.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9"
dependencies = [
"windows-targets 0.48.0",
]
[[package]] [[package]]
name = "windows-sys" name = "windows-sys"
version = "0.52.0" version = "0.52.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d"
dependencies = [ dependencies = [
"windows-targets 0.52.4", "windows-targets",
]
[[package]]
name = "windows-sys"
version = "0.59.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b"
dependencies = [
"windows-targets",
] ]
[[package]] [[package]]
name = "windows-targets" name = "windows-targets"
version = "0.48.0" version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7b1eb6f0cd7c80c79759c929114ef071b87354ce476d9d94271031c0497adfd5" checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973"
dependencies = [ dependencies = [
"windows_aarch64_gnullvm 0.48.0", "windows_aarch64_gnullvm",
"windows_aarch64_msvc 0.48.0", "windows_aarch64_msvc",
"windows_i686_gnu 0.48.0", "windows_i686_gnu",
"windows_i686_msvc 0.48.0", "windows_i686_gnullvm",
"windows_x86_64_gnu 0.48.0", "windows_i686_msvc",
"windows_x86_64_gnullvm 0.48.0", "windows_x86_64_gnu",
"windows_x86_64_msvc 0.48.0", "windows_x86_64_gnullvm",
] "windows_x86_64_msvc",
[[package]]
name = "windows-targets"
version = "0.52.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7dd37b7e5ab9018759f893a1952c9420d060016fc19a472b4bb20d1bdd694d1b"
dependencies = [
"windows_aarch64_gnullvm 0.52.4",
"windows_aarch64_msvc 0.52.4",
"windows_i686_gnu 0.52.4",
"windows_i686_msvc 0.52.4",
"windows_x86_64_gnu 0.52.4",
"windows_x86_64_gnullvm 0.52.4",
"windows_x86_64_msvc 0.52.4",
] ]
[[package]] [[package]]
name = "windows_aarch64_gnullvm" name = "windows_aarch64_gnullvm"
version = "0.42.2" version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "597a5118570b68bc08d8d59125332c54f1ba9d9adeedeef5b99b02ba2b0698f8" checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3"
[[package]]
name = "windows_aarch64_gnullvm"
version = "0.48.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "91ae572e1b79dba883e0d315474df7305d12f569b400fcf90581b06062f7e1bc"
[[package]]
name = "windows_aarch64_gnullvm"
version = "0.52.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bcf46cf4c365c6f2d1cc93ce535f2c8b244591df96ceee75d8e83deb70a9cac9"
[[package]] [[package]]
name = "windows_aarch64_msvc" name = "windows_aarch64_msvc"
version = "0.42.2" version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e08e8864a60f06ef0d0ff4ba04124db8b0fb3be5776a5cd47641e942e58c4d43" checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469"
[[package]]
name = "windows_aarch64_msvc"
version = "0.48.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b2ef27e0d7bdfcfc7b868b317c1d32c641a6fe4629c171b8928c7b08d98d7cf3"
[[package]]
name = "windows_aarch64_msvc"
version = "0.52.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "da9f259dd3bcf6990b55bffd094c4f7235817ba4ceebde8e6d11cd0c5633b675"
[[package]] [[package]]
name = "windows_i686_gnu" name = "windows_i686_gnu"
version = "0.42.2" version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c61d927d8da41da96a81f029489353e68739737d3beca43145c8afec9a31a84f" checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b"
[[package]] [[package]]
name = "windows_i686_gnu" name = "windows_i686_gnullvm"
version = "0.48.0" version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "622a1962a7db830d6fd0a69683c80a18fda201879f0f447f065a3b7467daa241" checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66"
[[package]]
name = "windows_i686_gnu"
version = "0.52.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b474d8268f99e0995f25b9f095bc7434632601028cf86590aea5c8a5cb7801d3"
[[package]] [[package]]
name = "windows_i686_msvc" name = "windows_i686_msvc"
version = "0.42.2" version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "44d840b6ec649f480a41c8d80f9c65108b92d89345dd94027bfe06ac444d1060" checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66"
[[package]]
name = "windows_i686_msvc"
version = "0.48.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4542c6e364ce21bf45d69fdd2a8e455fa38d316158cfd43b3ac1c5b1b19f8e00"
[[package]]
name = "windows_i686_msvc"
version = "0.52.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1515e9a29e5bed743cb4415a9ecf5dfca648ce85ee42e15873c3cd8610ff8e02"
[[package]] [[package]]
name = "windows_x86_64_gnu" name = "windows_x86_64_gnu"
version = "0.42.2" version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8de912b8b8feb55c064867cf047dda097f92d51efad5b491dfb98f6bbb70cb36" checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78"
[[package]]
name = "windows_x86_64_gnu"
version = "0.48.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ca2b8a661f7628cbd23440e50b05d705db3686f894fc9580820623656af974b1"
[[package]]
name = "windows_x86_64_gnu"
version = "0.52.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5eee091590e89cc02ad514ffe3ead9eb6b660aedca2183455434b93546371a03"
[[package]] [[package]]
name = "windows_x86_64_gnullvm" name = "windows_x86_64_gnullvm"
version = "0.42.2" version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "26d41b46a36d453748aedef1486d5c7a85db22e56aff34643984ea85514e94a3" checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d"
[[package]]
name = "windows_x86_64_gnullvm"
version = "0.48.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7896dbc1f41e08872e9d5e8f8baa8fdd2677f29468c4e156210174edc7f7b953"
[[package]]
name = "windows_x86_64_gnullvm"
version = "0.52.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "77ca79f2451b49fa9e2af39f0747fe999fcda4f5e241b2898624dca97a1f2177"
[[package]] [[package]]
name = "windows_x86_64_msvc" name = "windows_x86_64_msvc"
version = "0.42.2" version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0" checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec"
[[package]]
name = "windows_x86_64_msvc"
version = "0.48.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1a515f5799fe4961cb532f983ce2b23082366b898e52ffbce459c86f67c8378a"
[[package]]
name = "windows_x86_64_msvc"
version = "0.52.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "32b752e52a2da0ddfbdbcc6fceadfeede4c939ed16d13e648833a61dfb611ed8"
[[package]] [[package]]
name = "winnow" name = "winnow"
@@ -1280,9 +1131,9 @@ dependencies = [
[[package]] [[package]]
name = "zerocopy" name = "zerocopy"
version = "0.7.32" version = "0.7.35"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "74d4d3961e53fa4c9a25a8637fc2bfaf2595b3d3ae34875568a5cf64787716be" checksum = "1b9b4fd18abc82b8136838da5d50bae7bdea537c574d8dc1a34ed098d6c166f0"
dependencies = [ dependencies = [
"byteorder", "byteorder",
"zerocopy-derive", "zerocopy-derive",
@@ -1290,9 +1141,9 @@ dependencies = [
[[package]] [[package]]
name = "zerocopy-derive" name = "zerocopy-derive"
version = "0.7.32" version = "0.7.35"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9ce1b18ccd8e73a9321186f97e46f9f04b778851177567b1975109d26a08d2a6" checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
-3
View File
@@ -26,6 +26,3 @@ unstable_features = "warn"
unused = "warn" unused = "warn"
unused_import_braces = "warn" unused_import_braces = "warn"
unused_qualifications = "warn" unused_qualifications = "warn"
[profile.release]
lto = true
+6 -6
View File
@@ -6,15 +6,15 @@ license.workspace = true
rust-version.workspace = true rust-version.workspace = true
[dependencies] [dependencies]
anyhow = "1.0.71" anyhow = "1.0.95"
clap = { version = "4.1", features = ["derive"] } clap = { version = "4.5", features = ["derive"] }
libc = "0.2" libc = "0.2"
serde = { version = "1.0.164", features = ["derive"] } serde = { version = "1.0.217", features = ["derive"] }
serde_json = "1.0" serde_json = "1.0"
utils = { path = "../utils" } utils = { path = "../utils" }
zerocopy = { version="0.7", features = ["derive"] } zerocopy = { version="0.7", features = ["derive"] }
[build-dependencies] [build-dependencies]
cc = "1.0" cc = "1.2"
clap = { version ="4.1", features = ["derive"]} clap = { version ="4.5", features = ["derive"]}
lazy_static = "1.1" lazy_static = "1.5"
+9 -9
View File
@@ -14,18 +14,18 @@ readme = "README.md"
workspace = true workspace = true
[dependencies] [dependencies]
byteorder = "1.3" byteorder = "1.5"
curl = "0.4.44" curl = "0.4.47"
enum_dispatch = "0.3.13" enum_dispatch = "0.3.13"
foreign-types = "0.3.1" foreign-types = "0.3.2"
log = { version = "0.4.6", features = ["std", "release_max_level_debug"] } log = { version = "0.4.25", features = ["std", "release_max_level_debug"] }
openssl = "0.10.66" openssl = "0.10.70"
openssl-sys = "0.9.92" openssl-sys = "0.9.105"
serde = { version = "1.0.139", features = ["derive"] } serde = { version = "1.0.217", features = ["derive"] }
thiserror = "1.0.33" thiserror = "2.0.11"
zerocopy = { version="0.7", features = ["derive"] } zerocopy = { version="0.7", features = ["derive"] }
pv_core = { path = "../pv_core", package = "s390_pv_core", version = "0.11.0" } pv_core = { path = "../pv_core", package = "s390_pv_core", version = "0.11.0" }
[dev-dependencies] [dev-dependencies]
serde_test = "1.0.139" serde_test = "1.0.177"
+2 -1
View File
@@ -29,7 +29,6 @@ pub type Aes256XtsKey = Confidential<[u8; SymKeyType::AES_256_XTS_KEY_LEN]>;
/// SHA-512 digest length (in bytes) /// SHA-512 digest length (in bytes)
pub const SHA_512_HASH_LEN: usize = 64; pub const SHA_512_HASH_LEN: usize = 64;
#[allow(dead_code)] #[allow(dead_code)]
pub(crate) const SHA_256_HASH_LEN: u32 = 32; pub(crate) const SHA_256_HASH_LEN: u32 = 32;
#[allow(dead_code)] #[allow(dead_code)]
@@ -60,6 +59,8 @@ impl SymKeyType {
pub const AES_256_XTS_KEY_LEN: usize = 64; pub const AES_256_XTS_KEY_LEN: usize = 64;
/// AES256-XTS tweak length (in bytes) /// AES256-XTS tweak length (in bytes)
pub const AES_256_XTS_TWEAK_LEN: usize = 16; 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 /// Returns the tag length of the [`SymKeyType`] if it is an AEAD key
pub const fn tag_len(&self) -> Option<usize> { pub const fn tag_len(&self) -> Option<usize> {
+15 -1
View File
@@ -55,7 +55,7 @@ pub enum Error {
#[error("Decryption failed. Probably due to a GCM tag mismatch.")] #[error("Decryption failed. Probably due to a GCM tag mismatch.")]
GcmTagMismatch, GcmTagMismatch,
#[error("Invalid {0} user-data for signing provided. Max {} bytes allowed", .0.max())] #[error("Invalid {0} user-data for signing provided. Max {max} bytes allowed", max=.0.max())]
AsrcbInvSgnUserData(UserDataType), AsrcbInvSgnUserData(UserDataType),
#[error("Unsupported user data signing key provided. Only EC(secp521r1) and RSA(2048 & 3072 bit) are supported")] #[error("Unsupported user data signing key provided. Only EC(secp521r1) and RSA(2048 & 3072 bit) are supported")]
@@ -106,6 +106,20 @@ pub enum Error {
)] )]
AddDataMissing(&'static str), AddDataMissing(&'static str),
#[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,
},
#[error("Invalid data from OpenSSL")]
InvalSslData,
// errors from other crates // errors from other crates
#[error(transparent)] #[error(transparent)]
PvCore(#[from] pv_core::Error), PvCore(#[from] pv_core::Error),
+13 -1
View File
@@ -37,6 +37,7 @@ mod brcb;
mod crypto; mod crypto;
mod error; mod error;
mod openssl_extensions; mod openssl_extensions;
mod pem_utils;
mod req; mod req;
mod utils; mod utils;
mod uvattest; 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 /// Miscellaneous functions and definitions
pub mod misc { pub mod misc {
pub use pv_core::misc::*; pub use pv_core::misc::*;
@@ -98,7 +104,12 @@ pub mod request {
/// Reexports some useful OpenSSL symbols /// Reexports some useful OpenSSL symbols
pub mod openssl { 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::*; pub use pv_core::request::*;
@@ -112,6 +123,7 @@ pub mod secret {
asrcb::{AddSecretFlags, AddSecretRequest, AddSecretVersion}, asrcb::{AddSecretFlags, AddSecretRequest, AddSecretVersion},
ext_secret::ExtSecret, ext_secret::ExtSecret,
guest_secret::GuestSecret, guest_secret::GuestSecret,
retr_secret::{IbmProtectedKey, RetrievedSecret},
user_data::verify_asrcb_and_get_user_data, user_data::verify_asrcb_and_get_user_data,
}; };
} }
+85
View File
@@ -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
}
}
+2
View File
@@ -6,8 +6,10 @@
/// Extensions to the rust-openssl crate /// Extensions to the rust-openssl crate
mod akid; mod akid;
mod bio;
mod crl; mod crl;
mod stackable_crl; mod stackable_crl;
pub use akid::*; pub use akid::*;
pub use bio::*;
pub use crl::*; pub use crl::*;
@@ -2,16 +2,14 @@
// //
// Copyright IBM Corp. 2023 // Copyright IBM Corp. 2023
use std::{marker::PhantomData, ptr}; use crate::openssl_extensions::bio::BioMemSlice;
use foreign_types::{ForeignType, ForeignTypeRef}; use foreign_types::{ForeignType, ForeignTypeRef};
use openssl::{ use openssl::{
error::ErrorStack, error::ErrorStack,
stack::Stackable, stack::Stackable,
x509::{X509Crl, X509CrlRef}, x509::{X509Crl, X509CrlRef},
}; };
use openssl_sys::BIO_new_mem_buf; use std::ptr;
use std::ffi::c_int;
#[derive(Debug)] #[derive(Debug)]
pub struct StackableX509Crl(*mut openssl_sys::X509_CRL); pub struct StackableX509Crl(*mut openssl_sys::X509_CRL);
@@ -62,44 +60,11 @@ impl Stackable for StackableX509Crl {
type StackType = openssl_sys::stack_st_X509_CRL; 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 { impl StackableX509Crl {
pub fn stack_from_pem(pem: &[u8]) -> Result<Vec<X509Crl>, ErrorStack> { pub fn stack_from_pem(pem: &[u8]) -> Result<Vec<X509Crl>, ErrorStack> {
unsafe { unsafe {
openssl_sys::init(); openssl_sys::init();
let bio = MemBioSlice::new(pem)?; let bio = BioMemSlice::new(pem)?;
let mut crls = vec![]; let mut crls = vec![];
loop { loop {
+222
View File
@@ -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
View File
@@ -10,4 +10,5 @@
pub mod asrcb; pub mod asrcb;
pub mod ext_secret; pub mod ext_secret;
pub mod guest_secret; pub mod guest_secret;
pub mod retr_secret;
pub mod user_data; pub mod user_data;
+423 -21
View File
@@ -4,20 +4,34 @@
#[allow(unused_imports)] // used for more convenient docstring #[allow(unused_imports)] // used for more convenient docstring
use super::asrcb::AddSecretRequest; use super::asrcb::AddSecretRequest;
use crate::assert_size;
use crate::{ use crate::{
crypto::{hash, random_array}, assert_size,
request::Confidential, crypto::{hash, random_array, SymKeyType},
Result, request::{
openssl::{NID_ED25519, NID_ED448},
Confidential,
},
uv::{
AesSizes, AesXtsSizes, EcCurves, HmacShaSizes, ListableSecretType, RetrievableSecret,
RetrieveCmd, SecretId,
},
Error, Result,
}; };
use byteorder::BigEndian; use byteorder::BigEndian;
use openssl::hash::MessageDigest; use openssl::{
use pv_core::uv::{ListableSecretType, SecretId}; hash::MessageDigest,
nid::Nid,
pkey::{Id, PKey, PKeyRef, Private},
};
use pv_core::static_assert;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use std::{convert::TryInto, fmt::Display}; use std::fmt::Display;
use zerocopy::{AsBytes, U16, U32}; use zerocopy::{AsBytes, U16, U32};
const ASSOC_SECRET_SIZE: usize = 32; 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`] /// A Secret to be added in [`AddSecretRequest`]
#[derive(Debug, Serialize, Deserialize, PartialEq, Eq)] #[derive(Debug, Serialize, Deserialize, PartialEq, Eq)]
@@ -36,13 +50,61 @@ pub enum GuestSecret {
#[serde(skip)] #[serde(skip)]
secret: Confidential<[u8; ASSOC_SECRET_SIZE]>, 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
#[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 { impl GuestSecret {
/// 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()
.unwrap();
Ok(id.into())
}
/// Create a new [`GuestSecret::Association`]. /// Create a new [`GuestSecret::Association`].
/// ///
/// * `name` - Name of the secret. Will be hashed into a 32 byte id /// * `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 /// # Errors
/// ///
@@ -51,10 +113,6 @@ impl GuestSecret {
where where
O: Into<Option<[u8; ASSOC_SECRET_SIZE]>>, 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() { let secret = match secret.into() {
Some(s) => s, Some(s) => s,
None => random_array()?, None => random_array()?,
@@ -62,16 +120,41 @@ impl GuestSecret {
Ok(Self::Association { Ok(Self::Association {
name: name.to_string(), name: name.to_string(),
id: id.into(), id: Self::name_to_id(name)?,
secret: secret.into(), 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);
/// 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 /// Reference to the confidential data
pub(crate) fn confidential(&self) -> &[u8] { pub fn confidential(&self) -> &[u8] {
match &self { match &self {
Self::Null => &[], Self::Null => &[],
Self::Association { secret, .. } => secret.value().as_slice(), Self::Association { secret, .. } => secret.value().as_slice(),
Self::Retrievable { secret, .. } => secret.value(),
} }
} }
@@ -79,7 +162,7 @@ impl GuestSecret {
pub(crate) fn auth(&self) -> SecretAuth { pub(crate) fn auth(&self) -> SecretAuth {
match &self { match &self {
Self::Null => SecretAuth::Null, 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 => { listable => {
SecretAuth::Listable(ListableSecretHdr::from_guest_secret(listable).unwrap()) SecretAuth::Listable(ListableSecretHdr::from_guest_secret(listable).unwrap())
} }
@@ -92,6 +175,7 @@ impl GuestSecret {
// Null is not listable, but the ListableSecretType provides the type constant (1) // Null is not listable, but the ListableSecretType provides the type constant (1)
Self::Null => ListableSecretType::NULL, Self::Null => ListableSecretType::NULL,
Self::Association { .. } => ListableSecretType::ASSOCIATION, Self::Association { .. } => ListableSecretType::ASSOCIATION,
Self::Retrievable { kind, .. } => kind.into(),
} }
} }
@@ -100,6 +184,7 @@ impl GuestSecret {
match self { match self {
Self::Null => 0, Self::Null => 0,
Self::Association { secret, .. } => secret.value().len() as u32, Self::Association { secret, .. } => secret.value().len() as u32,
Self::Retrievable { secret, .. } => secret.value().len() as u32,
} }
} }
@@ -107,18 +192,172 @@ impl GuestSecret {
fn id(&self) -> Option<SecretId> { fn id(&self) -> Option<SecretId> {
match self { match self {
Self::Null => None, 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> {
// reads & left-pads Edward EC keys
fn pad_ed_key(pkey: &PKeyRef<Private>, curve: &EcCurves) -> Result<Vec<u8>> {
let raw_key = pkey.raw_private_key()?;
match raw_key.len().cmp(&curve.exp_key_size()) {
std::cmp::Ordering::Less => {
let mut key = Vec::with_capacity(curve.exp_key_size());
key.extend_from_slice(&vec![0u8; curve.exp_key_size() - raw_key.len()]);
key.extend_from_slice(&raw_key);
Ok(key)
}
std::cmp::Ordering::Equal => Ok(raw_key),
std::cmp::Ordering::Greater => Err(Error::InvalSslData),
}
}
let nid = match key.id() {
Id::EC => key.ec_key()?.group().curve_name().unwrap_or(Nid::UNDEF),
id @ (Id::ED25519 | Id::ED448) => Nid::from_raw(id.as_raw()),
_ => 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 = match key.id() {
Id::EC => key
.ec_key()?
.private_key()
.to_vec_padded(kind.exp_key_size() as i32)?,
// ED keys are not handled via the EC struct in OpenSSL.
Id::ED25519 | Id::ED448 => pad_ed_key(&key, &kind)?,
_ => unreachable!(),
};
Ok((RetrievableSecret::Ec(kind), key.into()))
}
#[inline(always)]
const fn bitsize(bytesize: u32) -> u32 {
bytesize * 8
}
impl Display for GuestSecret { impl Display for GuestSecret {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self { match self {
Self::Null => write!(f, "Meta"), Self::Null => write!(f, "Meta"),
gs => { gs => {
let kind: U16<BigEndian> = gs.kind().into(); let kind: U16<BigEndian> = gs.kind().into();
let st: ListableSecretType = kind.into(); let st: ListableSecretType = kind.get().into();
write!(f, "{st}") write!(f, "{st}")
} }
} }
@@ -153,20 +392,24 @@ assert_size!(ListableSecretHdr, 0x30);
impl ListableSecretHdr { impl ListableSecretHdr {
fn from_guest_secret(gs: &GuestSecret) -> Option<Self> { fn from_guest_secret(gs: &GuestSecret) -> Option<Self> {
let id = gs.id()?;
Some(Self { Some(Self {
res0: 0, res0: 0,
kind: gs.kind().into(), kind: gs.kind().into(),
secret_len: gs.secret_len().into(), secret_len: gs.secret_len().into(),
res8: 0, res8: 0,
id, id: gs.id()?,
}) })
} }
} }
#[cfg(test)] #[cfg(test)]
mod test { mod test {
use super::HmacShaSizes as HmacSizes;
use super::RetrievableSecret::*;
use super::*; use super::*;
use openssl::ec::{EcGroup, EcKey};
use pv_core::uv::AesSizes;
use serde_test::{assert_tokens, Token}; use serde_test::{assert_tokens, Token};
#[test] #[test]
@@ -187,8 +430,117 @@ mod test {
assert_eq!(secret, exp); 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] #[test]
fn ap_asc_parse() { 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 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 gen_ec(nid: Nid) -> PKey<Private> {
let group = EcGroup::from_curve_name(nid).unwrap();
let key = EcKey::generate(&group).unwrap();
PKey::from_ec_key(key).unwrap()
}
#[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 => gen_ec(nid),
};
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 retr_ec_pad() {
let pkey = PKey::generate_ed448().unwrap();
let (_, key) = ec(pkey).unwrap();
assert_eq!(key.value()[..7], [0; 7]);
let pkey = gen_ec(Nid::SECP521R1);
let (_, key) = ec(pkey).unwrap();
assert_eq!(key.value()[..14], [0; 14]);
}
#[test]
fn asc_parse() {
let id = [ let id = [
0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef, 0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 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, 0xcd, 0xef, 0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef, 0x01, 0x23, 0x45, 0x67,
@@ -217,6 +569,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] #[test]
fn guest_secret_bin_null() { fn guest_secret_bin_null() {
let gs = GuestSecret::Null; let gs = GuestSecret::Null;
@@ -228,7 +613,7 @@ mod test {
} }
#[test] #[test]
fn guest_secret_bin_ap() { fn guest_secret_bin_asoc() {
let gs = GuestSecret::Association { let gs = GuestSecret::Association {
name: "test".to_string(), name: "test".to_string(),
id: [1; 32].into(), id: [1; 32].into(),
@@ -241,4 +626,21 @@ mod test {
assert_eq!(exp, gs_bytes_auth.get()); assert_eq!(exp, gs_bytes_auth.get());
assert_eq!(&[2; 32], gs.confidential()); 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());
}
} }
+234
View File
@@ -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);
}
}
+7 -7
View File
@@ -16,13 +16,13 @@ readme = "README.md"
workspace = true workspace = true
[dependencies] [dependencies]
libc = "0.2.49" libc = "0.2.169"
log = { version = "0.4.6", features = ["std", "release_max_level_debug"] } log = { version = "0.4.25", features = ["std", "release_max_level_debug"] }
thiserror = "1.0.33" thiserror = "2.0.11"
zerocopy = {version = "0.7", features = ["derive"]} zerocopy = {version = "0.7", features = ["derive"]}
serde = { version = "1.0.139", features = ["derive"]} serde = { version = "1.0.217", features = ["derive"]}
byteorder = "1.3" byteorder = "1.5"
[dev-dependencies] [dev-dependencies]
serde_test = "1.0.139" serde_test = "1.0.177"
lazy_static = "1.1" lazy_static = "1.5"
+7
View File
@@ -4,6 +4,8 @@
use std::path::PathBuf; use std::path::PathBuf;
use crate::uv::SecretId;
/// Result type for this crate /// Result type for this crate
pub type Result<T, E = Error> = std::result::Result<T, E>; 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.")] #[error("The attestation request does not specify a measurement size or measurement data.")]
BinArcbNoMeasurement, 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 // errors from other crates
#[error(transparent)] #[error(transparent)]
Io(#[from] std::io::Error), Io(#[from] std::io::Error),
+3 -1
View File
@@ -32,7 +32,9 @@ pub mod misc {
/// [`crate::uv::UvCmd`] /// [`crate::uv::UvCmd`]
pub mod uv { pub mod uv {
pub use crate::uvdevice::attest::AttestationCmd; pub use crate::uvdevice::attest::AttestationCmd;
pub use crate::uvdevice::secret::{AddCmd, ListCmd, LockCmd}; 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::secret_list::{ListableSecretType, SecretEntry, SecretId, SecretList};
pub use crate::uvdevice::{ConfigUid, UvCmd, UvDevice, UvDeviceInfo, UvFlags, UvcSuccess}; pub use crate::uvdevice::{ConfigUid, UvCmd, UvDevice, UvDeviceInfo, UvFlags, UvcSuccess};
} }
+16 -3
View File
@@ -25,6 +25,7 @@ mod info;
mod test; mod test;
pub(crate) use ffi::uv_ioctl; pub(crate) use ffi::uv_ioctl;
pub mod attest; pub mod attest;
pub mod retr_secret;
pub mod secret; pub mod secret;
pub mod secret_list; pub mod secret_list;
@@ -58,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()); 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:?}"); debug!("ioctl resulted with {cb:?}");
match rc { match rc {
0 => Ok(()), 0 => Ok(()),
// NOTE io::Error handles all errnos ioctl uses _ => Err(errno.into()),
_ => Err(std::io::Error::last_os_error().into()),
} }
} }
@@ -73,7 +76,7 @@ fn rc_fmt<C: UvCmd>(rc: u16, rrc: u16, cmd: &mut C) -> &'static str {
(0x0002, _) => Some("invalid UV command"), (0x0002, _) => Some("invalid UV command"),
(0x0005, _) => Some("request has an invalid size"), (0x0005, _) => Some("request has an invalid size"),
(0x0030, _) => Some("home address space control bit has R-bit set to one. This may be due to a corrupted host or a guest kernel bug. STOP using this guest"), (0x0030, _) => Some("home address space control bit has R-bit set to one. This may be due to a corrupted host or a guest kernel bug. STOP using this guest"),
(0x0031, _) => Some("adress translation exception. This may be due to a corrupted host or a guest kernel bug. STOP using this guest"), (0x0031, _) => Some("address translation exception. This may be due to a corrupted host or a guest kernel bug. STOP using this guest"),
(0x0032, _) => Some("request contains virtual address translating to an invalid address. This may be due to a corrupted host or a guest kernel bug. STOP using this guest"), (0x0032, _) => Some("request contains virtual address translating to an invalid address. This may be due to a corrupted host or a guest kernel bug. STOP using this guest"),
(UvDevice::RC_MORE_DATA, _) => unreachable!("This is no Error!!!!"), (UvDevice::RC_MORE_DATA, _) => unreachable!("This is no Error!!!!"),
(UvDevice::RC_SUCCESS, _) => unreachable!("This is no Error!!!!"), (UvDevice::RC_SUCCESS, _) => unreachable!("This is no Error!!!!"),
@@ -162,6 +165,16 @@ pub enum UvcSuccess {
RC_MORE_DATA = UvDevice::RC_MORE_DATA, 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 /// The `UvDevice` is a (virtual) device on s390 machines to send Ultravisor commands(UVCs) from
/// userspace. /// userspace.
/// ///
+9 -2
View File
@@ -2,6 +2,13 @@
// //
// Copyright IBM Corp. 2023 // 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 std::mem::size_of;
use crate::{assert_size, static_assert}; 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_MEASUREMENT_MAX_LEN: usize = 0x8000;
pub const UVIO_ATT_ADDITIONAL_MAX_LEN: usize = 0x8000; pub const UVIO_ATT_ADDITIONAL_MAX_LEN: usize = 0x8000;
pub const UVIO_ADD_SECRET_MAX_LEN: usize = 0x100000; 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_LIST_SECRETS_LEN: usize = 0x1000;
pub const UVIO_RETR_SECRET_MAX_LEN: usize = 0x2000;
// equal to ascii 'u' // equal to ascii 'u'
pub const UVIO_TYPE_UVC: u8 = 117u8; 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_ADD_SECRET_NR: u8 = 2;
pub const UVIO_IOCTL_LIST_SECRETS_NR: u8 = 3; pub const UVIO_IOCTL_LIST_SECRETS_NR: u8 = 3;
pub const UVIO_IOCTL_LOCK_SECRETS_NR: u8 = 4; pub const UVIO_IOCTL_LOCK_SECRETS_NR: u8 = 4;
pub const UVIO_IOCTL_RETR_SECRET_NR: u8 = 5;
/// Uvdevice IOCTL control block /// Uvdevice IOCTL control block
/// Programs can use this struct to communicate with the uvdevice via IOCTLs /// Programs can use this struct to communicate with the uvdevice via IOCTLs
+392
View File
@@ -0,0 +1,392 @@
// 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 {
/// Returns the expected key-byte-size for this curve.
pub const fn exp_key_size(&self) -> usize {
match self {
Self::Secp256R1 => 32,
Self::Secp384R1 => 48,
Self::Secp521R1 => 80,
Self::Ed25519 => 32,
Self::Ed448 => 64,
}
}
}
// 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()
);
}
}
+106 -2
View File
@@ -3,8 +3,15 @@
// Copyright IBM Corp. 2023 // Copyright IBM Corp. 2023
use super::ffi; use super::ffi;
use crate::{request::MagicValue, uv::UvCmd, uvsecret::AddSecretMagic, Error, Result, PAGESIZE}; use crate::{
use std::io::Read; 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. /// _List Secrets_ Ultravisor command.
/// ///
@@ -17,6 +24,17 @@ impl ListCmd {
Self(vec![0; size]) 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 /// Create a new list secrets command with a one page capacity
pub fn new() -> Self { pub fn new() -> Self {
Self::with_size(PAGESIZE) Self::with_size(PAGESIZE)
@@ -116,3 +134,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())
}
}
+360 -73
View File
@@ -2,13 +2,20 @@
// //
// Copyright IBM Corp. 2024 // Copyright IBM Corp. 2024
use crate::assert_size; use crate::{
use crate::{misc::to_u16, uv::ListCmd, uvdevice::UvCmd, Error, Result}; assert_size,
use byteorder::{BigEndian, ReadBytesExt, WriteBytesExt}; uv::{AesSizes, AesXtsSizes, EcCurves, HmacShaSizes, ListCmd, RetrievableSecret},
uvdevice::UvCmd,
Error, Result,
};
use byteorder::{BigEndian, ByteOrder};
use serde::{Deserialize, Serialize, Serializer}; use serde::{Deserialize, Serialize, Serializer};
use std::{ use std::{
fmt::Display, cmp::min,
ffi::CStr,
fmt::{Debug, Display, LowerHex, UpperHex},
io::{Cursor, Read, Seek, Write}, io::{Cursor, Read, Seek, Write},
mem::size_of,
slice::Iter, slice::Iter,
vec::IntoIter, vec::IntoIter,
}; };
@@ -18,7 +25,7 @@ use zerocopy::{AsBytes, FromBytes, FromZeroes, U16, U32};
/// ///
/// (de)serializes itself in/from a hex-string /// (de)serializes itself in/from a hex-string
#[repr(C)] #[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]); pub struct SecretId([u8; Self::ID_SIZE]);
assert_size!(SecretId, SecretId::ID_SIZE); assert_size!(SecretId, SecretId::ID_SIZE);
@@ -26,10 +33,37 @@ impl SecretId {
/// Size in bytes of the [`SecretId`] /// Size in bytes of the [`SecretId`]
pub const ID_SIZE: usize = 32; 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 { pub fn from(buf: [u8; Self::ID_SIZE]) -> Self {
buf.into() 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 { impl Serialize for SecretId {
@@ -37,8 +71,8 @@ impl Serialize for SecretId {
where where
S: Serializer, S: Serializer,
{ {
// calls Display at one point // calls LowerHex at one point
ser.serialize_str(&self.to_string()) ser.serialize_str(&format!("{self:#x}"))
} }
} }
@@ -51,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 { impl Display for SecretId {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let mut s = String::with_capacity(32 * 2 + 2); if let Some(s) = self.as_ascii() {
s.push_str("0x"); write!(f, "{s} | ")?;
let s = self.0.iter().fold(s, |acc, e| acc + &format!("{e:02x}")); }
write!(f, "{s}") write!(f, "{self:#x}")
} }
} }
@@ -74,7 +132,7 @@ impl AsRef<[u8]> for SecretId {
/// A secret in a [`SecretList`] /// A secret in a [`SecretList`]
#[repr(C)] #[repr(C)]
#[derive(Debug, PartialEq, Eq, AsBytes, FromZeroes, FromBytes, Serialize)] #[derive(Debug, Clone, PartialEq, Eq, AsBytes, FromZeroes, FromBytes, Serialize)]
pub struct SecretEntry { pub struct SecretEntry {
#[serde(serialize_with = "ser_u16")] #[serde(serialize_with = "ser_u16")]
index: U16<BigEndian>, index: U16<BigEndian>,
@@ -94,11 +152,11 @@ impl SecretEntry {
/// Create a new entry for a [`SecretList`]. /// Create a new entry for a [`SecretList`].
/// ///
/// The content of this entry will very likely not represent the status of the guest in the /// 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 { pub fn new(index: u16, stype: ListableSecretType, id: SecretId, secret_len: u32) -> Self {
Self { Self {
index: index.into(), index: index.into(),
stype: stype.into(), stype: U16::new(stype.into()),
len: secret_len.into(), len: secret_len.into(),
res_8: 0, res_8: 0,
id, id,
@@ -110,9 +168,14 @@ impl SecretEntry {
self.index.get() self.index.get()
} }
/// Returns the secret type of this [`SecretEntry`]. /// 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 { pub fn stype(&self) -> ListableSecretType {
self.stype.into() self.stype.get().into()
} }
/// Returns a reference to the id of this [`SecretEntry`]. /// Returns a reference to the id of this [`SecretEntry`].
@@ -127,26 +190,65 @@ impl SecretEntry {
pub fn id(&self) -> &[u8] { pub fn id(&self) -> &[u8] {
self.id.as_ref() 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 { impl Display for SecretEntry {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 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)?; writeln!(f, "{} {}:", self.index, stype)?;
write!(f, " ")?; write!(f, " {}", self.id)
for b in self.id.as_ref() {
write!(f, "{b:02x}")?;
}
Ok(())
} }
} }
#[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. /// List of secrets used to parse the [`crate::uv::ListCmd`] result.
/// ///
/// The list should not hold more than 0xffffffff elements /// The list should ONLY be created from an UV-Call result using either:
#[derive(Debug, PartialEq, Eq, Serialize)] /// - [`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 { pub struct SecretList {
total_num_secrets: usize, #[serde(flatten)]
hdr: SecretListHdr,
secrets: Vec<SecretEntry>, secrets: Vec<SecretEntry>,
} }
@@ -182,10 +284,14 @@ impl SecretList {
/// The content of this list will very likely not represent the status of the guest in the /// 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. /// Ultravisor. Use of [`SecretList::decode`] in any non-test environments is encuraged.
pub fn new(total_num_secrets: u16, secrets: Vec<SecretEntry>) -> Self { pub fn new(total_num_secrets: u16, secrets: Vec<SecretEntry>) -> Self {
Self { Self::new_with_hdr(
total_num_secrets: total_num_secrets as usize, SecretListHdr::new(total_num_secrets, total_num_secrets, 0),
secrets, secrets,
} )
}
fn new_with_hdr(hdr: SecretListHdr, secrets: Vec<SecretEntry>) -> Self {
Self { hdr, secrets }
} }
/// Returns an iterator over the slice. /// Returns an iterator over the slice.
@@ -209,19 +315,17 @@ impl SecretList {
/// ///
/// This number may be not equal to the provided number of [`SecretEntry`] /// This number may be not equal to the provided number of [`SecretEntry`]
pub fn total_num_secrets(&self) -> usize { pub fn total_num_secrets(&self) -> usize {
self.total_num_secrets 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 /// Encodes the list in the same binary format the UV would do
pub fn encode<T: Write>(&self, w: &mut T) -> Result<()> { pub fn encode<T: Write>(&self, w: &mut T) -> Result<()> {
let num_s = to_u16(self.secrets.len()).ok_or(Error::ManySecrets)?; w.write_all(self.hdr.as_bytes())?;
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])?;
for secret in &self.secrets { for secret in &self.secrets {
w.write_all(secret.as_bytes())?; w.write_all(secret.as_bytes())?;
} }
@@ -230,19 +334,20 @@ impl SecretList {
/// Decodes the list from the binary format of the UV into this internal representation /// 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> { pub fn decode<R: Read + Seek>(r: &mut R) -> std::io::Result<Self> {
let num_s = r.read_u16::<BigEndian>()?; let mut buf = [0u8; size_of::<SecretListHdr>()];
let total_num_secrets = r.read_u16::<BigEndian>()? as usize; r.read_exact(&mut buf)?;
let mut v: Vec<SecretEntry> = Vec::with_capacity(num_s as usize); let hdr = SecretListHdr::ref_from(&buf).unwrap();
r.seek(std::io::SeekFrom::Current(12))?; // skip reserved bytes
let mut buf = [0u8; SecretEntry::STRUCT_SIZE]; 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)?; r.read_exact(&mut buf)?;
// cannot fail. buffer has the same size as the secret entry // cannot fail. buffer has the same size as the secret entry
let secr = SecretEntry::read_from(buf.as_slice()).unwrap(); let secr = SecretEntry::read_from(buf.as_slice()).unwrap();
v.push(secr); v.push(secr);
} }
Ok(Self { Ok(Self {
total_num_secrets, hdr: hdr.clone(),
secrets: v, secrets: v,
}) })
} }
@@ -258,7 +363,7 @@ impl TryFrom<ListCmd> for SecretList {
impl Display for SecretList { impl Display for SecretList {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 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() { if !self.secrets.is_empty() {
writeln!(f)?; writeln!(f)?;
} }
@@ -283,51 +388,115 @@ fn ser_u16<S: Serializer>(v: &U16<BigEndian>, ser: S) -> Result<S::Ok, S::Error>
pub enum ListableSecretType { pub enum ListableSecretType {
/// Association Secret /// Association Secret
Association, Association,
/// Retrievable key
Retrievable(RetrievableSecret),
/// Invalid secret type, that should never appear in a list /// Invalid secret type, that should never appear in a list
/// ///
/// 0 is reserved /// 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), Invalid(u16),
/// Unknown secret type /// Unknown secret type
Unknown(u16), Unknown(u16),
} }
impl ListableSecretType { 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; 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 { impl Display for ListableSecretType {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self { match self {
Self::Association => write!(f, "Association"), Self::Association => write!(f, "Association"),
Self::Invalid(n) => write!(f, "Invalid({n})"), Self::Invalid(n) => write!(f, "Invalid(0x{n:04x})"),
Self::Unknown(n) => write!(f, "Unknown({n})"), Self::Unknown(n) => write!(f, "Unknown(0x{n:04x})"),
Self::Retrievable(r) => write!(f, "{r}"),
} }
} }
} }
impl From<U16<BigEndian>> for ListableSecretType { impl<O: ByteOrder> From<U16<O>> for ListableSecretType {
fn from(value: U16<BigEndian>) -> Self { fn from(value: U16<O>) -> Self {
match value.get() { value.get().into()
}
}
impl From<u16> for ListableSecretType {
fn from(value: u16) -> Self {
match value {
Self::RESERVED_0 => Self::Invalid(Self::RESERVED_0), Self::RESERVED_0 => Self::Invalid(Self::RESERVED_0),
Self::NULL => Self::Invalid(Self::NULL), Self::NULL => Self::Invalid(Self::NULL),
Self::ASSOCIATION => Self::Association, 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), 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 { fn from(value: ListableSecretType) -> Self {
match value { match value {
ListableSecretType::Association => ListableSecretType::ASSOCIATION, ListableSecretType::Association => ListableSecretType::ASSOCIATION,
ListableSecretType::Invalid(n) | ListableSecretType::Unknown(n) => n, ListableSecretType::Invalid(n) | ListableSecretType::Unknown(n) => n,
ListableSecretType::Retrievable(r) => (&r).into(),
} }
.into()
} }
} }
@@ -341,15 +510,18 @@ where
type Value = [u8; SecretId::ID_SIZE]; type Value = [u8; SecretId::ID_SIZE];
fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result { 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> fn visit_str<E>(self, s: &str) -> Result<Self::Value, E>
where where
E: serde::de::Error, E: serde::de::Error,
{ {
if s.len() != SecretId::ID_SIZE * 2 + 2 { if s.len() != SecretId::ID_SIZE * 2 + "0x".len() {
return Err(serde::de::Error::invalid_length(s.len(), &self)); return Err(serde::de::Error::invalid_length(
s.len().saturating_sub("0x".len()),
&self,
));
} }
let nb = s.strip_prefix("0x").ok_or_else(|| { let nb = s.strip_prefix("0x").ok_or_else(|| {
serde::de::Error::invalid_value(serde::de::Unexpected::Str(s), &self) serde::de::Error::invalid_value(serde::de::Unexpected::Str(s), &self)
@@ -370,7 +542,6 @@ mod test {
use super::*; use super::*;
use std::io::{BufReader, BufWriter, Cursor}; use std::io::{BufReader, BufWriter, Cursor};
#[test] #[test]
fn dump_secret_entry() { fn dump_secret_entry() {
const EXP: &[u8] = &[ const EXP: &[u8] = &[
@@ -398,8 +569,8 @@ mod test {
let buf = [ let buf = [
0x00u8, 0x01, // num secr stored 0x00u8, 0x01, // num secr stored
0x01, 0x12, // total num secrets 0x01, 0x12, // total num secrets
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, // next valid idx
0x00, // reserved 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // reserved
// secret // secret
0x00, 0x01, 0x00, 0x02, // idx + type 0x00, 0x01, 0x00, 0x02, // idx + type
0x00, 0x00, 0x00, 0x20, // len 0x00, 0x00, 0x00, 0x20, // len
@@ -410,16 +581,16 @@ mod test {
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
]; ];
let exp = SecretList { let exp = SecretList::new_with_hdr(
total_num_secrets: 0x112, SecretListHdr::new(0x001, 0x112, 0x101),
secrets: vec![SecretEntry { vec![SecretEntry {
index: 1.into(), index: 1.into(),
stype: 2.into(), stype: 2.into(),
len: 32.into(), len: 32.into(),
res_8: 0, res_8: 0,
id: SecretId::from([0; 32]), id: SecretId::from([0; 32]),
}], }],
}; );
let mut br = BufReader::new(Cursor::new(buf)); let mut br = BufReader::new(Cursor::new(buf));
let sl = SecretList::decode(&mut br).unwrap(); let sl = SecretList::decode(&mut br).unwrap();
@@ -431,8 +602,8 @@ mod test {
const EXP: &[u8] = &[ const EXP: &[u8] = &[
0x00, 0x01, // num secr stored 0x00, 0x01, // num secr stored
0x01, 0x12, // total num secrets 0x01, 0x12, // total num secrets
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, // next valid idx
0x00, // reserved 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // reserved
// secret // secret
0x00, 0x01, 0x00, 0x02, // idx + type 0x00, 0x01, 0x00, 0x02, // idx + type
0x00, 0x00, 0x00, 0x20, // len 0x00, 0x00, 0x00, 0x20, // len
@@ -443,16 +614,16 @@ mod test {
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
]; ];
let sl = SecretList { let sl = SecretList::new_with_hdr(
total_num_secrets: 0x112, SecretListHdr::new(0x001, 0x112, 0x101),
secrets: vec![SecretEntry { vec![SecretEntry {
index: 1.into(), index: 1.into(),
stype: 2.into(), stype: 2.into(),
len: 32.into(), len: 32.into(),
res_8: 0, res_8: 0,
id: SecretId::from([0; 32]), id: SecretId::from([0; 32]),
}], }],
}; );
let mut buf = [0u8; 0x40]; let mut buf = [0u8; 0x40];
{ {
@@ -501,4 +672,120 @@ mod test {
)], )],
) )
} }
#[test]
fn secret_list_ser() {
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::Map { len: None },
Token::String("total_num_secrets"),
Token::U16(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::MapEnd,
],
)
}
#[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"
);
}
} }
+8 -8
View File
@@ -8,17 +8,17 @@ license.workspace = true
rust-version.workspace = true rust-version.workspace = true
[dependencies] [dependencies]
clap = { version ="4.1", features = ["derive", "wrap_help"]} clap = { version ="4.5", features = ["derive", "wrap_help"]}
lazy_static = "1.1" lazy_static = "1.5"
openssl = { version = "0.10.66" } openssl = { version = "0.10.70" }
pv_core = { path = "../pv_core", package = "s390_pv_core"} pv_core = { path = "../pv_core", package = "s390_pv_core"}
rand = "0.8" rand = "0.8"
regex = "1.7" regex = "1.11"
serde = { version = "1.0.139", features = ["derive"] } serde = { version = "1.0.217", features = ["derive"] }
serde_yaml = "0.9" serde_yaml = "0.9"
utils = { path = "../utils" } utils = { path = "../utils" }
[build-dependencies] [build-dependencies]
clap = { version ="4.1", features = ["derive", "wrap_help"]} clap = { version ="4.5", features = ["derive", "wrap_help"]}
clap_complete = "4.1" clap_complete = "4.5"
lazy_static = "1.1" lazy_static = "1.5"
+9 -9
View File
@@ -9,14 +9,14 @@ rust-version.workspace = true
workspace = true workspace = true
[dependencies] [dependencies]
anyhow = { version = "1.0.70", features = ["std"] } anyhow = { version = "1.0.95", features = ["std"] }
base64 = "0.22.1" base64 = "0.22.1"
byteorder = "1.3" byteorder = "1.5"
clap = { version ="4.1", features = ["derive", "wrap_help"]} clap = { version ="4.5", features = ["derive", "wrap_help"]}
curl = "0.4.44" curl = "0.4.47"
log = { version = "0.4.6", features = ["std", "release_max_level_debug"] } log = { version = "0.4.25", features = ["std", "release_max_level_debug"] }
openssl = "0.10.66" openssl = "0.10.70"
serde = { version = "1.0.139", features = ["derive"]} serde = { version = "1.0.217", features = ["derive"]}
serde_json = "1.0" serde_json = "1.0"
serde_yaml = "0.9" serde_yaml = "0.9"
zerocopy = { version="0.7", features = ["derive"] } zerocopy = { version="0.7", features = ["derive"] }
@@ -25,8 +25,8 @@ pv = { path = "../pv", package = "s390_pv" }
utils = { path = "../utils" } utils = { path = "../utils" }
[build-dependencies] [build-dependencies]
clap = { version ="4.1", features = ["derive", "wrap_help"]} clap = { version ="4.5", features = ["derive", "wrap_help"]}
clap_complete = "4.1" clap_complete = "4.5"
log = { version = "0.4", features = ["std", "release_max_level_debug"] } log = { version = "0.4", features = ["std", "release_max_level_debug"] }
utils = { path = "../utils" } utils = { path = "../utils" }
+28
View File
@@ -0,0 +1,28 @@
-----BEGIN CERTIFICATE-----
MIIEvDCCAqSgAwIBAgIUaAbjRvw8jn+JnUpX8FvywTRln98wDQYJKoZIhvcNAQEN
BQAwgcwxCzAJBgNVBAYTAlVTMTQwMgYDVQQKDCtJbnRlcm5hdGlvbmFsIEJ1c2lu
ZXNzIE1hY2hpbmVzIENvcnBvcmF0aW9uMTQwMgYDVQQDDCtJbnRlcm5hdGlvbmFs
IEJ1c2luZXNzIE1hY2hpbmVzIENvcnBvcmF0aW9uMREwDwYDVQQIDAhOZXcgWW9y
azEVMBMGA1UEBwwMUG91Z2hrZWVwc2llMScwJQYDVQQLDB5JQk0gWiBIb3N0IEtl
eSBTaWduaW5nIFNlcnZpY2UwIBcNMjEwNjAxMDkxODA3WhgPMjI5NTAzMTcwOTE4
MDdaMIG2MQswCQYDVQQGEwJVUzE0MDIGA1UECgwrSW50ZXJuYXRpb25hbCBCdXNp
bmVzcyBNYWNoaW5lcyBDb3Jwb3JhdGlvbjE0MDIGA1UEAwwrSW50ZXJuYXRpb25h
bCBCdXNpbmVzcyBNYWNoaW5lcyBDb3Jwb3JhdGlvbjERMA8GA1UECAwITmV3IFlv
cmsxDzANBgNVBAcMBkFybW9uazEXMBUGA1UECwwOSUJNIFogSG9zdCBLZXkwgZsw
EAYHKoZIzj0CAQYFK4EEACMDgYYABAEm7h/+hliO9pRO+nuDpkzuvdAlcMaiO/B4
Fa7gDHP59/E5s86G9GDBr1dqGNeJeB0wX8FTxZI2YJPv+T9kJDxaJQGenakJOdGe
SkkVjcdf4+65leLN2CzUMl0/csUu8jTZNsdFIL2vVMuY9ruT2se6tPdR4YhuoQRj
ADli4nUOsj/wcqMwMC4wHAYDVR0fBBUwEzARoA+gDYYLaWJtLmNydC5wZW0wDgYD
VR0PAQH/BAQDAgMIMA0GCSqGSIb3DQEBDQUAA4ICAQBZNR9MypBMNgT44FsKKQDG
Cduozb/NbcQFVsY7EV5VaeOjMzYSpKYNHZhJekVT8FRzRNrqcsaIvJMixPyx773g
GlYbE23Y6ktFvjwgbPrllBqsepoowCk04e3kt4yGYoo8MKQhYmSizcfIa8rXIjon
3INJHysh50hlWQgGScgOP1IK6vweKn+O9UcBxHuO9xm67NKmWVyEJkABB048eGFd
P10becukiN7XjBacxGnT8haKkinB0hoj1AzXHORZYaG3JM8KF+G/tcBhJcxc9qJd
XpGsvEIiHXg5JvaKmk4wX4y36O3koFc5fzm1fNEFt4k5OaKxI5jglmws/Tf7Q8qq
QR2WBcQ3dfqToFeDL7PXH4bgLkiAz1PFZpJGqCxKwI1dFc57fhpwGqoVDynH36mE
QSFMHGxSzigZYWCAlk/vYTDF10IuKwxaseuuaEXSfGfIl8Nmw/k5w4kBhk7i3vh7
QasnhCaDir13QjCZ/uPbXEc5RlgQ+fZVmG9HoZv9rnKlxlLzYYxHR5idKku1D789
bn2YsN4J9yxBGdSsG1qWD+pUbtn9vn7LbD5eXFmIiKEn3aDgyUBBFTdB24OgKRVn
tbBNLGc+yMEBH5BCGVHM5Y7tHo4cgCCbmD+PStQ9w+0+WxBwYt1n7La2Sx22K7e8
FLwjv+dXJRmO54vY3laicQ==
-----END CERTIFICATE-----
+10 -10
View File
@@ -9,28 +9,28 @@ rust-version.workspace = true
workspace = true workspace = true
[dependencies] [dependencies]
anyhow = { version = "1.0.70", features = ["std"] } anyhow = { version = "1.0.95", features = ["std"] }
clap = { version ="4.1", features = ["derive", "wrap_help"]} clap = { version ="4.5", features = ["derive", "wrap_help"]}
deku = "0.18" deku = "0.18"
deku_derive = "0.18" deku_derive = "0.18"
enum_dispatch = "0.3.13" enum_dispatch = "0.3.13"
log = { version = "0.4.6", features = ["std", "release_max_level_debug"] } log = { version = "0.4.25", features = ["std", "release_max_level_debug"] }
openssl = "0.10.66" openssl = "0.10.70"
serde = { version = "1.0.139", features = ["derive"]} serde = { version = "1.0.217", features = ["derive"]}
serde_json = "1.0" serde_json = "1.0"
thiserror = "1.0.33" thiserror = "2.0.11"
pv = { path = "../pv", package = "s390_pv" } pv = { path = "../pv", package = "s390_pv" }
utils = { path = "../utils" } utils = { path = "../utils" }
[build-dependencies] [build-dependencies]
anyhow = { version = "1.0.70", features = ["std"] } anyhow = { version = "1.0.95", features = ["std"] }
clap = { version ="4.1", features = ["derive", "wrap_help"]} clap = { version ="4.5", features = ["derive", "wrap_help"]}
clap_complete = "4.5" clap_complete = "4.5"
log = { version = "0.4.6", features = ["std", "release_max_level_debug"] } log = { version = "0.4.25", features = ["std", "release_max_level_debug"] }
pv = { path = "../pv", package = "s390_pv" } pv = { path = "../pv", package = "s390_pv" }
utils = { path = "../utils" } utils = { path = "../utils" }
[dev-dependencies] [dev-dependencies]
proptest = "1.3" proptest = "1.6"
+38 -16
View File
@@ -3,12 +3,11 @@
.\" it under the terms of the MIT license. See LICENSE for details. .\" 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-19" "s390-tools" "Genprotimg Manual"
.nh .nh
.ad l .ad l
.SH NAME .SH NAME
\fBgenprotimg\fP - Create an IBM Secure Execution image genprotimg \- Create an IBM Secure Execution image
\fB
.SH SYNOPSIS .SH SYNOPSIS
.nf .nf
.fam C .fam C
@@ -37,7 +36,7 @@ QEMU.
.SH OPTIONS .SH OPTIONS
.PP .PP
\-i, \-\-kernel <FILE> \-i, \-\-kernel, \-\-image <FILE>
.RS 4 .RS 4
Use the content of FILE as a raw binary Linux kernel. The Linux kernel must be a Use the content of FILE as a raw binary Linux kernel. The Linux kernel must be a
raw binary s390x Linux kernel. The ELF format is not supported. raw binary s390x Linux kernel. The ELF format is not supported.
@@ -123,17 +122,25 @@ Overwrite an existing Secure Execution boot image.
.RE .RE
.RE .RE
.PP .PP
\-\-comm\-key <FILE> \-\-cck, \-\-comm\-key <FILE>
.RS 4 .RS 4
Use the content of FILE as the customer\-communication key (CCK). The file must Use the content of FILE as the customer\-communication key (CCK). The file must
contain exactly 32 bytes of data. contain exactly 32 bytes of data.
.RE .RE
.RE .RE
.PP .PP
\-\-hdr\-key <FILE>
.RS 4
Use the content of FILE as the Secure Execution header protection key. The file
must contain exactly 32 bytes of data. If the option is not specified, the
Secure Execution header protection key is a randomly generated key.
.RE
.RE
.PP
\-\-enable\-dump \-\-enable\-dump
.RS 4 .RS 4
Enable Secure Execution guest dump support. This option requires the Enable Secure Execution guest dump support. This option requires the
\fB\-\-comm\-key\fR option. \fB\-\-cck\fR option.
.RE .RE
.RE .RE
.PP .PP
@@ -146,8 +153,7 @@ Disable Secure Execution guest dump support (default).
\-\-enable\-cck\-extension\-secret \-\-enable\-cck\-extension\-secret
.RS 4 .RS 4
Add\-secret requests must provide an extension secret that matches the Add\-secret requests must provide an extension secret that matches the
CCK\-derived extension secret. This option requires the \fB\-\-comm\-key\fR CCK\-derived extension secret. This option requires the \fB\-\-cck\fR option.
option.
.RE .RE
.RE .RE
.PP .PP
@@ -196,6 +202,22 @@ Disable the support for backup target keys (default).
.RE .RE
.RE .RE
.PP .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 \-v, \-\-verbose
.RS 4 .RS 4
Provide more detailed output. Provide more detailed output.
@@ -222,37 +244,37 @@ Print help (see a summary with \fB\-h\fR).
.SH EXIT STATUS .SH EXIT STATUS
.TP 8 .TP 8
.B 0 - Program finished successfully .B 0 \- Program finished successfully
The command was executed successfully. The command was executed successfully.
.RE .RE
.TP 8 .TP 8
.B 1 - Generic error .B 1 \- Generic error
Something went wrong during the operation. Refer to the error Something went wrong during the operation. Refer to the error
message. message.
.RE .RE
.TP 8 .TP 8
.B 2 - Usage error .B 2 \- Usage error
The command was used incorrectly, for example: unsupported command The command was used incorrectly, for example: unsupported command
line flag, or wrong number of arguments. line flag, or wrong number of arguments.
.RE .RE
.SH EXAMPLES .SH EXAMPLES
These are examples of how to generate an IBM Secure Execution image in These are examples of how to generate an IBM Secure Execution image in
\fI\,/boot/secure-linux\/\fR, using the kernel file \fI\,/boot/vmlinuz\/\fR, the \fI\,/boot/secure\-linux\/\fR, using the kernel file \fI\,/boot/vmlinuz\/\fR, the
initrd in \fI\,/boot/initrd.img\/\fR, the kernel parameters contained in initrd in \fI\,/boot/initrd.img\/\fR, the kernel parameters contained in
\fI\,parmfile\/\fR, the intermediate CA in \fI\,DigiCertCA.crt\/\fR, the IBM Z \fI\,parmfile\/\fR, the intermediate CA in \fI\,DigiCertCA.crt\/\fR, the IBM Z
signing key in \fI\,ibm-z-host-key-signing.crt\/\fR, and the host-key document signing key in \fI\,ibm\-z\-host\-key\-signing.crt\/\fR, and the host-key document
in \fI\,host_key.crt\/\fR. An AES-256 GCM key is stored in \fI\,comm-key\/\fR, in \fI\,host_key.crt\/\fR. An AES-256 GCM key is stored in \fI\,comm\-key\/\fR,
which is used when creating a Secure Execution image with guest dump support which is used when creating a Secure Execution image with guest dump support
enabled in the second example. enabled in the second example.
Generate an IBM Secure Execution image: Generate an IBM Secure Execution image:
.PP .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 .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
Generate an IBM Secure Execution image with Secure Execution guest dump support: Generate an IBM Secure Execution image with Secure Execution guest dump support:
.PP .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 .SH NOTES
.IP "1." 4 .IP "1." 4
The \fBgenprotimg\fR(1) command is a symbolic link to the \fBpvimg-create\fR(1) command. The \fBgenprotimg\fR(1) command is a symbolic link to the \fBpvimg-create\fR(1) command.
+38 -16
View File
@@ -3,12 +3,11 @@
.\" it under the terms of the MIT license. See LICENSE for details. .\" 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-19" "s390-tools" "Pvimg Manual"
.nh .nh
.ad l .ad l
.SH NAME .SH NAME
\fBpvimg create\fP - Create an IBM Secure Execution image pvimg-create \- Create an IBM Secure Execution image
\fB
.SH SYNOPSIS .SH SYNOPSIS
.nf .nf
.fam C .fam C
@@ -36,7 +35,7 @@ QEMU.
.SH OPTIONS .SH OPTIONS
.PP .PP
\-i, \-\-kernel <FILE> \-i, \-\-kernel, \-\-image <FILE>
.RS 4 .RS 4
Use the content of FILE as a raw binary Linux kernel. The Linux kernel must be a Use the content of FILE as a raw binary Linux kernel. The Linux kernel must be a
raw binary s390x Linux kernel. The ELF format is not supported. raw binary s390x Linux kernel. The ELF format is not supported.
@@ -122,17 +121,25 @@ Overwrite an existing Secure Execution boot image.
.RE .RE
.RE .RE
.PP .PP
\-\-comm\-key <FILE> \-\-cck, \-\-comm\-key <FILE>
.RS 4 .RS 4
Use the content of FILE as the customer\-communication key (CCK). The file must Use the content of FILE as the customer\-communication key (CCK). The file must
contain exactly 32 bytes of data. contain exactly 32 bytes of data.
.RE .RE
.RE .RE
.PP .PP
\-\-hdr\-key <FILE>
.RS 4
Use the content of FILE as the Secure Execution header protection key. The file
must contain exactly 32 bytes of data. If the option is not specified, the
Secure Execution header protection key is a randomly generated key.
.RE
.RE
.PP
\-\-enable\-dump \-\-enable\-dump
.RS 4 .RS 4
Enable Secure Execution guest dump support. This option requires the Enable Secure Execution guest dump support. This option requires the
\fB\-\-comm\-key\fR option. \fB\-\-cck\fR option.
.RE .RE
.RE .RE
.PP .PP
@@ -145,8 +152,7 @@ Disable Secure Execution guest dump support (default).
\-\-enable\-cck\-extension\-secret \-\-enable\-cck\-extension\-secret
.RS 4 .RS 4
Add\-secret requests must provide an extension secret that matches the Add\-secret requests must provide an extension secret that matches the
CCK\-derived extension secret. This option requires the \fB\-\-comm\-key\fR CCK\-derived extension secret. This option requires the \fB\-\-cck\fR option.
option.
.RE .RE
.RE .RE
.PP .PP
@@ -195,6 +201,22 @@ Disable the support for backup target keys (default).
.RE .RE
.RE .RE
.PP .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 \-h, \-\-help
.RS 4 .RS 4
Print help (see a summary with \fB\-h\fR). Print help (see a summary with \fB\-h\fR).
@@ -203,37 +225,37 @@ Print help (see a summary with \fB\-h\fR).
.SH EXIT STATUS .SH EXIT STATUS
.TP 8 .TP 8
.B 0 - Program finished successfully .B 0 \- Program finished successfully
The command was executed successfully. The command was executed successfully.
.RE .RE
.TP 8 .TP 8
.B 1 - Generic error .B 1 \- Generic error
Something went wrong during the operation. Refer to the error Something went wrong during the operation. Refer to the error
message. message.
.RE .RE
.TP 8 .TP 8
.B 2 - Usage error .B 2 \- Usage error
The command was used incorrectly, for example: unsupported command The command was used incorrectly, for example: unsupported command
line flag, or wrong number of arguments. line flag, or wrong number of arguments.
.RE .RE
.SH EXAMPLES .SH EXAMPLES
These are examples of how to generate an IBM Secure Execution image in These are examples of how to generate an IBM Secure Execution image in
\fI\,/boot/secure-linux\/\fR, using the kernel file \fI\,/boot/vmlinuz\/\fR, the \fI\,/boot/secure\-linux\/\fR, using the kernel file \fI\,/boot/vmlinuz\/\fR, the
initrd in \fI\,/boot/initrd.img\/\fR, the kernel parameters contained in initrd in \fI\,/boot/initrd.img\/\fR, the kernel parameters contained in
\fI\,parmfile\/\fR, the intermediate CA in \fI\,DigiCertCA.crt\/\fR, the IBM Z \fI\,parmfile\/\fR, the intermediate CA in \fI\,DigiCertCA.crt\/\fR, the IBM Z
signing key in \fI\,ibm-z-host-key-signing.crt\/\fR, and the host-key document signing key in \fI\,ibm\-z\-host\-key\-signing.crt\/\fR, and the host-key document
in \fI\,host_key.crt\/\fR. An AES-256 GCM key is stored in \fI\,comm-key\/\fR, in \fI\,host_key.crt\/\fR. An AES-256 GCM key is stored in \fI\,comm\-key\/\fR,
which is used when creating a Secure Execution image with guest dump support which is used when creating a Secure Execution image with guest dump support
enabled in the second example. enabled in the second example.
Generate an IBM Secure Execution image: Generate an IBM Secure Execution image:
.PP .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 .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
Generate an IBM Secure Execution image with Secure Execution guest dump support: Generate an IBM Secure Execution image with Secure Execution guest dump support:
.PP .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 .SH NOTES
.IP "1." 4 .IP "1." 4
The \fBgenprotimg\fR(1) command is a symbolic link to the \fBpvimg-create\fR(1) command. The \fBgenprotimg\fR(1) command is a symbolic link to the \fBpvimg-create\fR(1) command.
+9 -8
View File
@@ -3,12 +3,11 @@
.\" it under the terms of the MIT license. See LICENSE for details. .\" 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-19" "s390-tools" "Pvimg Manual"
.nh .nh
.ad l .ad l
.SH NAME .SH NAME
\fBpvimg info\fP - Print information about the IBM Secure Execution image pvimg-info \- Print information about the IBM Secure Execution image
\fB
.SH SYNOPSIS .SH SYNOPSIS
.nf .nf
.fam C .fam C
@@ -37,9 +36,11 @@ Possible values:
.RE .RE
.RE .RE
.PP .PP
\-\-key <FILE> \-\-hdr\-key <FILE>
.RS 4 .RS 4
Use the key in FILE to decrypt the Secure Execution header. Use the key in FILE to decrypt the Secure Execution header. It is the key that
was specified with the command line option \fB\-\-hdr\-key\fR at the Secure
Execution image creation.
.RE .RE
.RE .RE
.PP .PP
@@ -51,16 +52,16 @@ Print help (see a summary with \fB\-h\fR).
.SH EXIT STATUS .SH EXIT STATUS
.TP 8 .TP 8
.B 0 - Program finished successfully .B 0 \- Program finished successfully
The command was executed successfully. The command was executed successfully.
.RE .RE
.TP 8 .TP 8
.B 1 - Generic error .B 1 \- Generic error
Something went wrong during the operation. Refer to the error Something went wrong during the operation. Refer to the error
message. message.
.RE .RE
.TP 8 .TP 8
.B 2 - Usage error .B 2 \- Usage error
The command was used incorrectly, for example: unsupported command The command was used incorrectly, for example: unsupported command
line flag, or wrong number of arguments. line flag, or wrong number of arguments.
.RE .RE
+5 -6
View File
@@ -3,12 +3,11 @@
.\" it under the terms of the MIT license. See LICENSE for details. .\" 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-19" "s390-tools" "Pvimg Manual"
.nh .nh
.ad l .ad l
.SH NAME .SH NAME
\fBpvimg test\fP - Test different aspects of an existing IBM Secure Execution image pvimg-test \- Test different aspects of an existing IBM Secure Execution image
\fB
.SH SYNOPSIS .SH SYNOPSIS
.nf .nf
.fam C .fam C
@@ -54,16 +53,16 @@ Print help (see a summary with \fB\-h\fR).
.SH EXIT STATUS .SH EXIT STATUS
.TP 8 .TP 8
.B 0 - Program finished successfully .B 0 \- Program finished successfully
The command was executed successfully. The command was executed successfully.
.RE .RE
.TP 8 .TP 8
.B 1 - Generic error .B 1 \- Generic error
Something went wrong during the operation. Refer to the error Something went wrong during the operation. Refer to the error
message. message.
.RE .RE
.TP 8 .TP 8
.B 2 - Usage error .B 2 \- Usage error
The command was used incorrectly, for example: unsupported command The command was used incorrectly, for example: unsupported command
line flag, or wrong number of arguments. line flag, or wrong number of arguments.
.RE .RE
+8 -9
View File
@@ -3,12 +3,11 @@
.\" it under the terms of the MIT license. See LICENSE for details. .\" 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-19" "s390-tools" "Pvimg Manual"
.nh .nh
.ad l .ad l
.SH NAME .SH NAME
\fBpvimg\fP - Create and inspect IBM Secure Execution images pvimg \- Create and inspect IBM Secure Execution images
\fB
.SH SYNOPSIS .SH SYNOPSIS
.nf .nf
.fam C .fam C
@@ -16,9 +15,9 @@ pvimg [OPTIONS] <COMMAND>
.fam C .fam C
.fi .fi
.SH DESCRIPTION .SH DESCRIPTION
Use pvimg to create an IBM Secure Execution image, which can be loaded using Use \fBpvimg\fP to create an IBM Secure Execution image, which can be loaded
zipl or QEMU. pvimg can also be used to inspect existing Secure Execution using zipl or QEMU. \fBpvimg\fP can also be used to inspect existing Secure
images. Execution images.
.SH "PVIMG COMMANDS" .SH "PVIMG COMMANDS"
.PP .PP
@@ -69,16 +68,16 @@ Print help (see a summary with \fB\-h\fR).
.SH EXIT STATUS .SH EXIT STATUS
.TP 8 .TP 8
.B 0 - Program finished successfully .B 0 \- Program finished successfully
The command was executed successfully. The command was executed successfully.
.RE .RE
.TP 8 .TP 8
.B 1 - Generic error .B 1 \- Generic error
Something went wrong during the operation. Refer to the error Something went wrong during the operation. Refer to the error
message. message.
.RE .RE
.TP 8 .TP 8
.B 2 - Usage error .B 2 \- Usage error
The command was used incorrectly, for example: unsupported command The command was used incorrectly, for example: unsupported command
line flag, or wrong number of arguments. line flag, or wrong number of arguments.
.RE .RE
+61 -15
View File
@@ -96,8 +96,8 @@ pub struct ComponentPaths {
#[command(group(ArgGroup::new("header-flags").multiple(true).conflicts_with_all(["x_pcf", "x_scf"])))] #[command(group(ArgGroup::new("header-flags").multiple(true).conflicts_with_all(["x_pcf", "x_scf"])))]
pub struct CreateBootImageLegacyFlags { pub struct CreateBootImageLegacyFlags {
/// Enable Secure Execution guest dump support. This option requires the /// Enable Secure Execution guest dump support. This option requires the
/// '--comm-key' option. /// '--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_dump: Option<bool>, pub enable_dump: Option<bool>,
/// Disable Secure Execution guest dump support (default). /// Disable Secure Execution guest dump support (default).
@@ -105,9 +105,9 @@ pub struct CreateBootImageLegacyFlags {
pub disable_dump: Option<bool>, pub disable_dump: Option<bool>,
/// Add-secret requests must provide an extension secret that matches the /// 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. /// 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>, pub enable_cck_extension_secret: Option<bool>,
/// Add-secret requests don't have to provide the CCK-derived extension /// Add-secret requests don't have to provide the CCK-derived extension
@@ -140,6 +140,20 @@ pub struct CreateBootImageLegacyFlags {
/// Disable the support for backup target keys (default). /// Disable the support for backup target keys (default).
#[arg(long, action = clap::ArgAction::SetTrue, conflicts_with="enable_backup_keys", group="header-flags")] #[arg(long, action = clap::ArgAction::SetTrue, conflicts_with="enable_backup_keys", group="header-flags")]
pub disable_backup_keys: Option<bool>, 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] #[non_exhaustive]
@@ -178,8 +192,11 @@ pub struct InfoArgs {
pub format: OutputFormat, pub format: OutputFormat,
/// Use the key in FILE to decrypt the Secure Execution header. /// Use the key in FILE to decrypt the Secure Execution header.
#[arg(long, value_name = "FILE", value_hint = ValueHint::FilePath,)] ///
pub key: Option<PathBuf>, /// It is the key that was specified with the command line option
/// '--hdr-key' at the Secure Execution image creation.
#[arg(long, value_name = "FILE", value_hint = ValueHint::FilePath, alias = "key")]
pub hdr_key: Option<PathBuf>,
} }
#[derive(Args, Debug)] #[derive(Args, Debug)]
@@ -313,9 +330,18 @@ pub struct CreateBootImageArgs {
/// Use the content of FILE as the customer-communication key (CCK). /// 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
#[arg(long, value_name = "FILE")] /// called '--comm-key' in previous versions.
pub comm_key: Option<PathBuf>, #[arg(long, value_name = "FILE", visible_alias = "comm-key")]
pub cck: Option<PathBuf>,
/// Use the content of FILE as the Secure Execution header protection key.
///
/// The file must contain exactly 32 bytes of data. If the option is not
/// specified, the Secure Execution header protection key is a randomly
/// generated key.
#[arg(long, value_name = "FILE", alias = "x-header-key")]
pub hdr_key: Option<PathBuf>,
#[clap(flatten)] #[clap(flatten)]
pub legacy_flags: CreateBootImageLegacyFlags, pub legacy_flags: CreateBootImageLegacyFlags,
@@ -338,11 +364,6 @@ pub struct CreateBootImageExperimentalArgs {
#[arg(long, value_name = "FILE", hide(true))] #[arg(long, value_name = "FILE", hide(true))]
pub x_comp_key: Option<PathBuf>, pub x_comp_key: Option<PathBuf>,
/// Manually set the Secure Execution header protection key (experimental option).
// Hidden in user documentation.
#[arg(long, value_name = "FILE", hide(true))]
pub x_header_key: Option<PathBuf>,
/// Manually set the PSW address used for the Secure Execution header (experimental option). /// Manually set the PSW address used for the Secure Execution header (experimental option).
// Hidden in user documentation. // Hidden in user documentation.
#[arg(long, value_name = "ADDRESS", hide(true))] #[arg(long, value_name = "ADDRESS", hide(true))]
@@ -467,6 +488,8 @@ mod test {
flat_map_collect(insert(mvca.clone(), vec![CliOption::new("parmfile", ["--parmfile", "/dev/null"])])), flat_map_collect(insert(mvca.clone(), vec![CliOption::new("parmfile", ["--parmfile", "/dev/null"])])),
flat_map_collect(insert(mvca.clone(), vec![CliOption::new("enable-dump", ["--enable-dump"]), flat_map_collect(insert(mvca.clone(), vec![CliOption::new("enable-dump", ["--enable-dump"]),
CliOption::new("comm-key", ["--comm-key", "/dev/null"])])), 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"]), flat_map_collect(insert(mvca.clone(), vec![CliOption::new("enable-dump", ["--enable-dump"]),
CliOption::new("comm-key", ["--comm-key", "/dev/null"])])), CliOption::new("comm-key", ["--comm-key", "/dev/null"])])),
flat_map_collect(insert(mvca.clone(), vec![CliOption::new("x-pcf", ["--x-pcf", "0x0"]), flat_map_collect(insert(mvca.clone(), vec![CliOption::new("x-pcf", ["--x-pcf", "0x0"]),
@@ -476,6 +499,10 @@ 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", ["--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-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("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"])])),
flat_map_collect(insert(mvca.clone(), vec![CliOption::new("x-header-key", ["--x-header-key", "/dev/null"]),])),
flat_map_collect(insert(mvca.clone(), vec![CliOption::new("x-header-key", ["--hdr-key", "/dev/null"]),])),
]; ];
let invalid_create_args = [ let invalid_create_args = [
flat_map_collect(remove(mvcanv.clone(), "no-verify")), flat_map_collect(remove(mvcanv.clone(), "no-verify")),
@@ -501,6 +528,9 @@ mod test {
CliOption::new("x-pcf2", ["--x-pcf", "0x0"])])), CliOption::new("x-pcf2", ["--x-pcf", "0x0"])])),
flat_map_collect(insert(mvca.clone(), vec![CliOption::new("enable-pckmo", ["--enable-pckmo"]), flat_map_collect(insert(mvca.clone(), vec![CliOption::new("enable-pckmo", ["--enable-pckmo"]),
CliOption::new("disable-pckmo", ["--disable-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"])])),
flat_map_collect(insert(mvca.clone(), vec![CliOption::new("x-header-key", ["--hdr-key"]),])),
]; ];
let mut genprotimg_valid_args = vec![ let mut genprotimg_valid_args = vec![
@@ -692,6 +722,22 @@ mod test {
CliOption::new("image", ["/dev/null"]), 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 // separation between keyword and positional args works
flat_map_collect(insert( flat_map_collect(insert(
args.clone(), args.clone(),
@@ -732,7 +778,7 @@ mod test {
// Test for invalid combinations // Test for invalid combinations
// Input is missing // 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 { for create_args in &valid_test_args {
pvimg_valid_args.push( pvimg_valid_args.push(
+2 -1
View File
@@ -25,6 +25,7 @@ pub struct UserProvidedKeys {
/// Reads all user provided keys. /// Reads all user provided keys.
pub fn read_user_provided_keys( pub fn read_user_provided_keys(
cck_path: Option<&Path>, cck_path: Option<&Path>,
hdr_key_path: Option<&Path>,
experimental_args: &CreateBootImageExperimentalArgs, experimental_args: &CreateBootImageExperimentalArgs,
) -> Result<UserProvidedKeys> { ) -> Result<UserProvidedKeys> {
let components_key = { let components_key = {
@@ -43,7 +44,7 @@ pub fn read_user_provided_keys(
} }
}; };
let aead_key = { let aead_key = {
match &experimental_args.x_header_key { match hdr_key_path {
Some(key_path) => { Some(key_path) => {
info!( info!(
"Use file '{}' as the Secure Execution header protection", "Use file '{}' as the Secure Execution header protection",
+16 -3
View File
@@ -55,7 +55,7 @@ fn parse_flags(
.and(Some(PcfV1::all_disabled([PcfV1::AllowDumping]))), .and(Some(PcfV1::all_disabled([PcfV1::AllowDumping]))),
lf.enable_dump lf.enable_dump
.filter(|x| *x) .filter(|x| *x)
.and(Some(PcfV1::all_disabled([PcfV1::AllowDumping]))), .and(Some(PcfV1::all_enabled([PcfV1::AllowDumping]))),
lf.disable_pckmo lf.disable_pckmo
.filter(|x| *x) .filter(|x| *x)
.and(Some(PcfV1::all_disabled([ .and(Some(PcfV1::all_disabled([
@@ -80,6 +80,12 @@ fn parse_flags(
lf.enable_backup_keys lf.enable_backup_keys
.filter(|x| *x) .filter(|x| *x)
.and(Some(PcfV1::all_enabled([PcfV1::BackupTargetKeys]))), .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() .into_iter()
.flatten() .flatten()
@@ -131,10 +137,17 @@ pub fn create(opt: &CreateBootImageArgs) -> Result<OwnExitCode> {
let verified_host_keys = opt let verified_host_keys = opt
.certificate_args .certificate_args
.get_verified_hkds("Secure Execution image")?; .get_verified_hkds("Secure Execution image")?;
let user_provided_keys = let user_provided_keys = read_user_provided_keys(
read_user_provided_keys(opt.comm_key.as_deref(), &opt.experimental_args)?; opt.cck.as_deref(),
opt.hdr_key.as_deref(),
&opt.experimental_args,
)?;
let (plaintext_flags, secret_flags) = parse_flags(opt)?; 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)?; let mut components = components(&opt.component_paths)?;
if opt.no_component_check { if opt.no_component_check {
warn!("The component check is turned off!"); warn!("The component check is turned off!");
+1 -1
View File
@@ -27,7 +27,7 @@ pub fn info(opt: &InfoArgs) -> Result<OwnExitCode> {
SeHdr::seek_sehdr(&mut input, None)?; SeHdr::seek_sehdr(&mut input, None)?;
let hdr = SeHdr::try_from_io(input)?; let hdr = SeHdr::try_from_io(input)?;
if let Some(key_path) = &opt.key { if let Some(key_path) = &opt.hdr_key {
let key = let key =
SymKey::try_from_data(hdr.key_type(), read_file(key_path, "Reading key")?.into())?; SymKey::try_from_data(hdr.key_type(), read_file(key_path, "Reading key")?.into())?;
serde_json::to_writer_pretty(&mut output, &hdr.decrypt(&key)?)?; serde_json::to_writer_pretty(&mut output, &hdr.decrypt(&key)?)?;
+3
View File
@@ -30,6 +30,9 @@ pub enum Error {
#[error("Invalid Secure Execution header")] #[error("Invalid Secure Execution header")]
InvalidSeHdr, InvalidSeHdr,
#[error("Secure Execution header size {given} is larger than the maximum of {maximum} bytes")]
InvalidSeHdrTooLarge { given: usize, maximum: usize },
#[error("Invalid component metadata.")] #[error("Invalid component metadata.")]
InvalidComponentMetadata, InvalidComponentMetadata,
+66 -7
View File
@@ -171,8 +171,8 @@ impl AeadCipherTrait for SeHdr {
} }
impl AeadDataTrait for SeHdr { impl AeadDataTrait for SeHdr {
fn aad(&self) -> Vec<u8> { fn aad(&self) -> Result<Vec<u8>> {
[serialize_to_bytes(&self.common).unwrap(), self.data.aad()].concat() Ok([serialize_to_bytes(&self.common)?, self.data.aad()?].concat())
} }
fn data(&self) -> Vec<u8> { fn data(&self) -> Vec<u8> {
@@ -259,9 +259,13 @@ impl SeHdr {
return Err(Error::InvalidSeHdr); return Err(Error::InvalidSeHdr);
} }
if sehs <= common_size {
return Err(Error::InvalidSeHdr);
}
data.resize(sehs, 0); data.resize(sehs, 0);
reader.read_exact(&mut data[common_size..])?; reader.read_exact(&mut data[common_size..])?;
Self::try_from_data(&data) Self::try_from_data(&data).map_err(|_| Error::InvalidSeHdr)
} }
} }
@@ -338,13 +342,13 @@ impl UvDataPlainTrait for SeHdrPlain {
} }
impl AeadPlainDataTrait for SeHdrPlain { impl AeadPlainDataTrait for SeHdrPlain {
fn aad(&self) -> Vec<u8> { fn aad(&self) -> Result<Vec<u8>> {
let data_aad = self.data.aad(); 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() self.data.data()
} }
@@ -366,3 +370,58 @@ impl AeadCipherTrait for SeHdrPlain {
self.data.aead_tag_size() 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)
));
// 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)
));
}
}
+8 -2
View File
@@ -230,8 +230,14 @@ mod tests {
let decrypted = bin.decrypt(&prot_key).expect("BUG"); let decrypted = bin.decrypt(&prot_key).expect("BUG");
assert_eq!(bin.common, decrypted.common); assert_eq!(bin.common, decrypted.common);
assert_eq!(bin.aad(), decrypted.aad()); assert_eq!(
assert_ne!(&bin.data(), decrypted.data().value()); 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"); let _decrypted_hdrv1: SeHdrDataV1 = decrypted.data.try_into().expect("BUG");
} }
+64 -7
View File
@@ -19,6 +19,7 @@ use serde::{Serialize, Serializer};
use super::keys::phkh_v1; use super::keys::phkh_v1;
use crate::{ use crate::{
error::Error, error::Error,
misc::PAGESIZE,
pv_utils::{ pv_utils::{
error::Result, error::Result,
se_hdr::{ se_hdr::{
@@ -51,11 +52,14 @@ struct HdrSizesV1 {
#[derive(Debug, Clone, PartialEq, Eq, DekuRead, DekuWrite, Serialize)] #[derive(Debug, Clone, PartialEq, Eq, DekuRead, DekuWrite, Serialize)]
#[deku(endian = "endian", ctx = "endian: Endian", ctx_default = "Endian::Big")] #[deku(endian = "endian", ctx = "endian: Endian", ctx_default = "Endian::Big")]
struct SeHdrAadV1 { struct SeHdrAadV1 {
#[deku(assert = "*sehs <= SeHdrDataV1::MAX_SIZE.try_into().unwrap()")]
sehs: u32, sehs: u32,
#[serde(serialize_with = "ser_hex")] #[serde(serialize_with = "ser_hex")]
iv: [u8; SymKeyType::AES_256_GCM_IV_LEN], iv: [u8; SymKeyType::AES_256_GCM_IV_LEN],
res1: u32, res1: u32,
#[deku(assert = "*nks <= (*sehs).into()", update = "self.keyslots.len()")]
nks: u64, nks: u64,
#[deku(assert = "*sea <= (*sehs).into()")]
sea: u64, sea: u64,
nep: u64, nep: u64,
#[serde(serialize_with = "ser_lower_hex")] #[serde(serialize_with = "ser_lower_hex")]
@@ -118,6 +122,7 @@ pub struct SeHdrConfV1 {
psw: PSW, psw: PSW,
#[serde(serialize_with = "ser_lower_hex")] #[serde(serialize_with = "ser_lower_hex")]
scf: u64, scf: u64,
#[deku(assert_eq = "0")]
noi: u32, noi: u32,
res2: u32, res2: u32,
#[deku(count = "noi")] #[deku(count = "noi")]
@@ -200,6 +205,7 @@ where
} }
impl SeHdrDataV1 { impl SeHdrDataV1 {
const MAX_SIZE: usize = 2 * PAGESIZE;
const PCF_DEFAULT: u64 = 0x0; const PCF_DEFAULT: u64 = 0x0;
const SCF_DEFAULT: u64 = 0x0; const SCF_DEFAULT: u64 = 0x0;
@@ -241,7 +247,14 @@ impl SeHdrDataV1 {
tag: SeHdrTagV1::default(), tag: SeHdrTagV1::default(),
}; };
let hdr_size = ret.size()?; 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; ret.aad.sea = hdr_size.sea;
Ok(ret) Ok(ret)
} }
@@ -494,8 +507,8 @@ impl KeyExchangeTrait for SeHdrBinV1 {
} }
impl AeadDataTrait for SeHdrBinV1 { impl AeadDataTrait for SeHdrBinV1 {
fn aad(&self) -> Vec<u8> { fn aad(&self) -> Result<Vec<u8>> {
serialize_to_bytes(&self.aad).unwrap() serialize_to_bytes(&self.aad)
} }
fn data(&self) -> Vec<u8> { fn data(&self) -> Vec<u8> {
@@ -508,12 +521,12 @@ impl AeadDataTrait for SeHdrBinV1 {
} }
impl AeadPlainDataTrait for SeHdrDataV1 { impl AeadPlainDataTrait for SeHdrDataV1 {
fn aad(&self) -> Vec<u8> { fn aad(&self) -> Result<Vec<u8>> {
serialize_to_bytes(&self.aad).unwrap() serialize_to_bytes(&self.aad)
} }
fn data(&self) -> Confidential<Vec<u8>> { fn data(&self) -> Result<Confidential<Vec<u8>>> {
serialize_to_bytes(self.data.value()).unwrap().into() Ok(serialize_to_bytes(self.data.value())?.into())
} }
fn tag(&self) -> Vec<u8> { fn tag(&self) -> Vec<u8> {
@@ -610,4 +623,48 @@ mod tests {
assert_eq!(psw, hdr_data_v1.data.value().psw); assert_eq!(psw, hdr_data_v1.data.value().psw);
assert_eq!(cck.value(), hdr_data_v1.data.value().cck.value()); 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)));
}
} }
+12 -6
View File
@@ -34,7 +34,7 @@ pub trait AeadCipherTrait {
#[enum_dispatch] #[enum_dispatch]
pub trait AeadDataTrait { pub trait AeadDataTrait {
/// Returns the authenticated associated data. /// Returns the authenticated associated data.
fn aad(&self) -> Vec<u8>; fn aad(&self) -> Result<Vec<u8>>;
/// Returns the encrypted data. /// Returns the encrypted data.
fn data(&self) -> Vec<u8>; fn data(&self) -> Vec<u8>;
@@ -47,10 +47,10 @@ pub trait AeadDataTrait {
#[enum_dispatch] #[enum_dispatch]
pub trait AeadPlainDataTrait { pub trait AeadPlainDataTrait {
/// Returns the authenticated associated data. /// Returns the authenticated associated data.
fn aad(&self) -> Vec<u8>; fn aad(&self) -> Result<Vec<u8>>;
/// Returns the unencrypted data. /// Returns the unencrypted data.
fn data(&self) -> Confidential<Vec<u8>>; fn data(&self) -> Result<Confidential<Vec<u8>>>;
/// Returns the tag data. /// Returns the tag data.
fn tag(&self) -> Vec<u8>; fn tag(&self) -> Vec<u8>;
@@ -124,8 +124,14 @@ pub trait UvDataPlainTrait:
expected: self.aead_key_type().to_string(), expected: self.aead_key_type().to_string(),
}); });
} }
let aad = self.aad(); let aad = self.aad().map_err(|err| match err {
let unecrypted_data = self.data(); 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 iv = self.iv();
let result = encrypt_aead(key, iv, &aad, unecrypted_data.value())?; let result = encrypt_aead(key, iv, &aad, unecrypted_data.value())?;
Self::C::try_from_data(&result.into_buf()) 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 tag_size = self.aead_tag_size();
let aad = self.aad(); let aad = self.aad()?;
let unecrypted_data = self.data(); let unecrypted_data = self.data();
let iv = self.iv(); let iv = self.iv();
let tag = self.tag(); let tag = self.tag();
-303
View File
@@ -1,303 +0,0 @@
#!/bin/sh
#
# check_hostkeydoc - Verify an IBM Secure Execution host key document
#
# Sample script to verify that a host key document is genuine by
# verifying the issuer, the validity date and the signature.
# Optionally verify the full trust chain using a CA certificate.
#
# Sample invocation:
#
# ./check_hostkeydoc HKD1234.crt ibm-z-host-key-signing.crt -c DigiCertCA.crt -r ibm-z-host-key.crl
#
# Copyright IBM Corp. 2020
#
# s390-tools is free software; you can redistribute it and/or modify
# it under the terms of the MIT license. See LICENSE for details.
# Allocate temporary files
ISSUER_PUBKEY_FILE=$(mktemp)
SIGNATURE_FILE=$(mktemp)
BODY_FILE=$(mktemp)
ISSUER_DN_FILE=$(mktemp)
SUBJECT_DN_FILE=$(mktemp)
DEF_ISSUER_ARMONK_DN_FILE=$(mktemp)
DEF_ISSUER_POUGHKEEPSIE_DN_FILE=$(mktemp)
CANONICAL_ISSUER_DN_FILE=$(mktemp)
CRL_SERIAL_FILE=$(mktemp)
# Cleanup on exit
cleanup()
{
rm -f "$ISSUER_PUBKEY_FILE" "$SIGNATURE_FILE" "$BODY_FILE" \
"$ISSUER_DN_FILE" "$SUBJECT_DN_FILE" "$DEF_ISSUER_ARMONK_DN_FILE" "$DEF_ISSUER_POUGHKEEPSIE_DN_FILE" \
"$CANONICAL_ISSUER_DN_FILE" "$CRL_SERIAL_FILE"
}
trap cleanup EXIT
# Enhanced error checking for bash
if [ -n "${BASH}" ]; then
# shellcheck disable=SC3040
set -o posix
# shellcheck disable=SC3040
set -o pipefail
# shellcheck disable=SC3040
set -o nounset
fi
set -e
# Usage
usage()
{
cat <<-EOF
Usage: $(basename "$1") [-d] [-c CA-cert] [-r CRL] host-key-doc signing-key-cert
Verify an IBM Secure Execution host key document against
a signing key.
Options:
-d disable default issuer check of host-key-doc
-c CA-cert trusted CA certificate
-r CRL list of revoked host-key-docs
Note that in order to have the full trust chain verified
it is necessary to provide the issuing CA's certificate.
The default issuer check may be disabled if a non-default
signing key certificate needs to be verified against the
CA certificate.
EOF
}
check_verify_chain()
{
# Verify certificate chain in case a CA certificate file/bundle
# was specified on the command line.
if [ -z "$2" ]; then
cat >&2 <<-EOF
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
No CA certificate specified! Skipping trust chain verification.
Make sure that '$1' is a valid certificate.
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
EOF
else
openssl verify -crl_download -crl_check "$2" &&
openssl verify -crl_download -crl_check -untrusted "$2" "$1" ||
exit 1
fi
}
extract_pubkey()
{
openssl x509 -in "$1" -pubkey -noout >"$2"
}
extract_signature()
{
# Assuming that the last field is the signature
SIGOFFSET=$(openssl asn1parse -in "$1" | tail -1 | cut -d : -f 1)
openssl asn1parse -in "$1" -out "$2" -strparse "$SIGOFFSET" -noout
}
extract_body()
{
# Assuming that the first field is the full cert body
SIGOFFSET=$(openssl asn1parse -in "$1" | head -2 | tail -1 | cut -d : -f 1)
openssl asn1parse -in "$1" -out "$2" -strparse "$SIGOFFSET" -noout
}
verify_signature()
{
# Assuming that the signature algorithm is SHA512 with RSA
openssl sha512 -verify "$1" -signature "$2" "$3"
}
canonical_dn()
{
OBJTYPE=$1
OBJ=$2
DNTYPE=$3
OUTPUT=$4
openssl "$OBJTYPE" -in "$OBJ" -"$DNTYPE" -noout -nameopt multiline |
LC_ALL=C sort | grep -v "$DNTYPE"= >"$OUTPUT"
}
default_issuer_armonk()
{
cat <<-EOF
commonName = International Business Machines Corporation
countryName = US
localityName = Armonk
organizationName = International Business Machines Corporation
organizationalUnitName = Key Signing Service
stateOrProvinceName = New York
EOF
}
default_issuer_pougkeepsie()
{
cat <<-EOF
commonName = International Business Machines Corporation
countryName = US
localityName = Poughkeepsie
organizationName = International Business Machines Corporation
organizationalUnitName = Key Signing Service
stateOrProvinceName = New York
EOF
}
# As organizationalUnitName can have an arbitrary prefix but must
# end with "Key Signing Service" let's normalize the OU name by
# stripping off the prefix
verify_default_issuer()
{
default_issuer_pougkeepsie >"$DEF_ISSUER_POUGHKEEPSIE_DN_FILE"
default_issuer_armonk >"$DEF_ISSUER_ARMONK_DN_FILE"
sed "s/\(^[ ]*organizationalUnitName[ ]*=[ ]*\).*\(Key Signing Service$\)/\1\2/" \
"$ISSUER_DN_FILE" >"$CANONICAL_ISSUER_DN_FILE"
if ! {
diff "$CANONICAL_ISSUER_DN_FILE" "$DEF_ISSUER_POUGHKEEPSIE_DN_FILE" ||
diff "$CANONICAL_ISSUER_DN_FILE" "$DEF_ISSUER_ARMONK_DN_FILE"
} >/dev/null 2>&1; then
echo Incorrect default issuer >&2 && exit 1
fi
}
verify_issuer_files()
{
if [ "$1" -eq 1 ]; then
verify_default_issuer
fi
}
cert_time()
{
DATE=$(openssl x509 -in "$1" -"$2" -noout | sed "s/^.*=//")
date -d "$DATE" +%s
}
crl_time()
{
DATE=$(openssl crl -in "$1" -"$2" -noout | sed "s/^.*=//")
date -d "$DATE" +%s
}
verify_dates()
{
START="$1"
END="$2"
MSG="${3:-Certificate}"
NOW=$(date +%s)
if [ "$START" -le "$NOW" ] && [ "$NOW" -le "$END" ]; then
echo "${MSG} dates are OK"
else
echo "${MSG} date verification failed" >&2 && exit 1
fi
}
crl_serials()
{
openssl crl -in "$1" -text -noout |
grep "Serial Number" >"$CRL_SERIAL_FILE"
}
check_serial()
{
CERT_SERIAL=$(openssl x509 -in "$1" -noout -serial | cut -d = -f 2)
grep -q "$CERT_SERIAL" "$CRL_SERIAL_FILE"
}
check_file()
{
[ -e "$1" ] ||
(echo "File '$1' not found" >&2 && exit 1)
}
# check args
CRL_FILE=
CA_FILE=
CHECK_DEFAULT_ISSUER=1
while getopts 'dr:c:h' opt; do
case $opt in
d) CHECK_DEFAULT_ISSUER=0 ;;
r) CRL_FILE=$OPTARG ;;
c) CA_FILE=$OPTARG ;;
h)
usage "$0"
exit 0
;;
?)
usage "$0"
exit 1
;;
esac
done
shift "$((OPTIND - 1))"
if [ $# -ne 2 ]; then
usage "$0" >&2
exit 1
fi
HKD_FILE=$1
HKSK_FILE=$2
# Check whether all specified files exist
check_file "$HKD_FILE"
check_file "$HKSK_FILE"
# CA and CRL are optional arguments
[ -n "$CA_FILE" ] && check_file "$CA_FILE"
[ -n "$CRL_FILE" ] && check_file "$CRL_FILE"
# Check trust chain
check_verify_chain "$HKSK_FILE" "$CA_FILE"
# Verify host key document signature
printf "Checking host key document signature: "
extract_pubkey "$HKSK_FILE" "$ISSUER_PUBKEY_FILE" &&
extract_signature "$HKD_FILE" "$SIGNATURE_FILE" &&
extract_body "$HKD_FILE" "$BODY_FILE" &&
verify_signature "$ISSUER_PUBKEY_FILE" "$SIGNATURE_FILE" "$BODY_FILE" ||
exit 1
# Verify the issuer
canonical_dn x509 "$HKD_FILE" issuer "$ISSUER_DN_FILE"
canonical_dn x509 "$HKSK_FILE" subject "$SUBJECT_DN_FILE"
verify_issuer_files $CHECK_DEFAULT_ISSUER
# Verify dates
verify_dates "$(cert_time "$HKD_FILE" startdate)" "$(cert_time "$HKD_FILE" enddate)"
# Check CRL if specified
if [ -n "$CRL_FILE" ]; then
printf "Checking CRL signature: "
extract_signature "$CRL_FILE" "$SIGNATURE_FILE" &&
extract_body "$CRL_FILE" "$BODY_FILE" &&
verify_signature "$ISSUER_PUBKEY_FILE" "$SIGNATURE_FILE" "$BODY_FILE" ||
exit 1
printf "CRL "
canonical_dn crl "$CRL_FILE" issuer "$ISSUER_DN_FILE"
canonical_dn x509 "$HKSK_FILE" subject "$SUBJECT_DN_FILE"
verify_issuer_files $CHECK_DEFAULT_ISSUER
verify_dates "$(crl_time "$CRL_FILE" lastupdate)" "$(crl_time "$CRL_FILE" nextupdate)" 'CRL'
crl_serials "$CRL_FILE"
check_serial "$HKD_FILE" &&
echo "Certificate is revoked, do not use it anymore!" >&2 &&
exit 1
fi
# We made it
echo All checks requested for \'"$HKD_FILE"\' were successful
+1
View File
@@ -0,0 +1 @@
../../../genprotimg/samples/check_hostkeydoc
+5 -5
View File
@@ -9,17 +9,17 @@ rust-version.workspace = true
workspace = true workspace = true
[dependencies] [dependencies]
anyhow = { version = "1.0.70", features = ["std"] } anyhow = { version = "1.0.95", features = ["std"] }
clap = { version ="4.1", features = ["derive", "wrap_help"]} clap = { version ="4.5", features = ["derive", "wrap_help"]}
log = { version = "0.4.6", features = ["std", "release_max_level_debug"] } log = { version = "0.4.25", features = ["std", "release_max_level_debug"] }
serde_yaml = "0.9" serde_yaml = "0.9"
pv = { path = "../pv" , package = "s390_pv" } pv = { path = "../pv" , package = "s390_pv" }
utils = { path = "../utils"} utils = { path = "../utils"}
[build-dependencies] [build-dependencies]
clap = { version ="4.1", features = ["derive", "wrap_help"]} clap = { version ="4.5", features = ["derive", "wrap_help"]}
clap_complete = "4.1" clap_complete = "4.5"
log = { version = "0.4", features = ["std", "release_max_level_debug"] } log = { version = "0.4", features = ["std", "release_max_level_debug"] }
utils = { path = "../utils" } utils = { path = "../utils" }
+175 -47
View File
@@ -32,7 +32,7 @@ Create a new add-secret request
- **add** - **add**
<ul> <ul>
Perform an add-secret request (s390x only) Submit an add-secret request to the Ultravisor (s390x only)
</ul> </ul>
- **lock** - **lock**
@@ -50,23 +50,34 @@ List all ultravisor secrets (s390x only)
Verify that an add-secret request is sane Verify that an add-secret request is sane
</ul> </ul>
- **retrieve**
<ul>
Retrieve a secret from the UV secret store (s390x only)
</ul>
## Options ## Options
`-v`, `--verbose` `-v`, `--verbose`
<ul> <ul>
Provide more detailed output Provide more detailed output.
</ul>
`-q`, `--quiet`
<ul>
Provide less output.
</ul> </ul>
`--version` `--version`
<ul> <ul>
Print version information and exit Print version information and exit.
</ul> </ul>
`-h`, `--help` `-h`, `--help`
<ul> <ul>
Print help Print help (see a summary with '-h').
</ul> </ul>
@@ -95,12 +106,17 @@ Create a meta secret
Create an association secret Create an association secret
</ul> </ul>
- **retrievable**
<ul>
Create a retrievable secret
</ul>
### Options ### Options
`-k`, `--host-key-document <FILE>` `-k`, `--host-key-document <FILE>`
<ul> <ul>
Use FILE as a host-key document. Can be specified multiple times and must be Use FILE as a host-key document. Can be specified multiple times and must be
used at least once. specified at least once.
</ul> </ul>
@@ -114,7 +130,7 @@ the host-key document beforehand.
`-C`, `--cert <FILE>` `-C`, `--cert <FILE>`
<ul> <ul>
Use FILE as a certificate to verify the host key or keys. The certificates are Use FILE as a certificate to verify the host-key or keys. The certificates are
used to establish a chain of trust for the verification of the host-key used to establish a chain of trust for the verification of the host-key
documents. Specify this option twice to specify the IBM Z signing key and the documents. Specify this option twice to specify the IBM Z signing key and the
intermediate CA certificate (signed by the root CA). intermediate CA certificate (signed by the root CA).
@@ -123,15 +139,15 @@ intermediate CA certificate (signed by the root CA).
`--crl <FILE>` `--crl <FILE>`
<ul> <ul>
Use FILE as a certificate revocation list. The list is used to check whether a Use FILE as a certificate revocation list (CRL). The list is used to check
certificate of the chain of trust is revoked. Specify this option multiple times whether a certificate of the chain of trust is revoked. Specify this option
to use multiple CRLs. multiple times to use multiple CRLs.
</ul> </ul>
`--offline` `--offline`
<ul> <ul>
Make no attempt to download CRLs Make no attempt to download CRLs.
</ul> </ul>
@@ -146,8 +162,7 @@ specified certificate.
`--hdr <FILE>` `--hdr <FILE>`
<ul> <ul>
Specifies the header of the guest image. Can be an IBM Secure Execution image 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 created by 'pvimg/genprotimg' or an extracted IBM Secure Execution header.
must start at a page boundary.
</ul> </ul>
@@ -162,7 +177,7 @@ behavior.
`-o`, `--output <FILE>` `-o`, `--output <FILE>`
<ul> <ul>
Write the generated request to FILE Write the generated request to FILE.
</ul> </ul>
@@ -209,15 +224,15 @@ the request.
`--flags <FLAGS>` `--flags <FLAGS>`
<ul> <ul>
Flags for the add-secret request Flags for the add-secret request.
Possible values: Possible values:
- **disable-dump**: Disables host-initiated dumping for the target guest instance - **disable-dump**: Disables host-initiated dumping for the target guest instance.
</ul> </ul>
`--user-data <FILE>` `--user-data <FILE>`
<ul> <ul>
Use the content of FILE as user-data. Passes user data defined in <FILE> through Use the content of FILE as user-data. Passes user data defined in FILE through
the add-secret request to the ultravisor. The user data can be up to 512 bytes the add-secret request to the ultravisor. The user data can be up to 512 bytes
of arbitrary data, and the maximum size depends on the size of the user-signing of arbitrary data, and the maximum size depends on the size of the user-signing
key: key:
@@ -236,19 +251,25 @@ Optional. No user-data by default.
`--user-sign-key <FILE>` `--user-sign-key <FILE>`
<ul> <ul>
Use the content of FILE as user signing key. Adds a signature calculated from Use the content of FILE as user signing key. Adds a signature calculated from
the key in <FILE> to the add-secret request. The file must be in DER or PEM the key in FILE to the add-secret request. The file must be in DER or PEM format
format containing a private key. Supported are RSA 2048 & 3072-bit and containing a private key. Supported are RSA 2048 & 3072-bit and EC(secp521r1)
EC(secp521r1) keys. The firmware ignores the content, but the request tag keys. The firmware ignores the content, but the request tag protects the
protects the signature. The user-signing key signs the request. The location of signature. The user-signing key signs the request. The location of the signature
the signature is filled with zeros during the signature calculation. The request is filled with zeros during the signature calculation. The request tag also
tag also secures the signature. See man pvsecret verify for more details. secures the signature. See man pvsecret verify for more details. Optional. No
Optional. No signature by default. signature by default.
</ul>
`--use-name`
<ul>
Do not hash the name, use it directly as secret ID. Ignored for meta-secrets.
</ul> </ul>
`-h`, `--help` `-h`, `--help`
<ul> <ul>
Print help Print help (see a summary with '-h').
</ul> </ul>
@@ -265,13 +286,62 @@ of secrets.
`pvsecret create association [OPTIONS] <NAME>` `pvsecret create association [OPTIONS] <NAME>`
#### Description #### Description
Create an association secret. Use an association secret to connect a trusted I/O Create an association secret. Use an association secret to connect a trusted I/O
device to a guest. The `pvapconfig` tool provides more information about device to a guest. The 'pvapconfig' tool provides more information about
association secrets. association secrets.
#### Arguments #### Arguments
`<NAME>` `<NAME>`
<ul> <ul>
String to identify the new secret. The actual secret is set with --input-secret. String that identifies the new secret. The actual secret is set with
'--input-secret'. The name is saved in `NAME.yaml` with white-spaces mapped to
`_`.
</ul>
#### Options
`--stdout`
<ul>
Print the hashed name to stdout. The hashed name is not written to `NAME.yaml`
</ul>
`--input-secret <SECRET-FILE>`
<ul>
Path from which to read the plaintext secret. Uses a random secret if not
specified.
</ul>
`--output-secret <SECRET-FILE>`
<ul>
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 anymore.
</ul>
`-h`, `--help`
<ul>
Print help (see a summary with '-h').
</ul>
### pvsecret create retrievable
#### Synopsis
`pvsecret create retrievable [OPTIONS] --secret <SECRET-FILE> --type <TYPE> <NAME>`
`pvsecret create retr [OPTIONS] --secret <SECRET-FILE> --type <TYPE> <NAME>`
#### Description
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.
#### Arguments
`<NAME>`
<ul>
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 `_`. The name is saved in `NAME.yaml` with white-spaces mapped to `_`.
</ul> </ul>
@@ -284,24 +354,28 @@ Print the hashed name to stdout. The hashed name is not written to `NAME.yaml`
</ul> </ul>
`--input-secret <FILE>` `--secret <SECRET-FILE>`
<ul> <ul>
Path from which to read the plaintext secret. Uses a random secret if not Use SECRET-FILE as retrievable secret.
specified
</ul> </ul>
`--output-secret <FILE>` `--type <TYPE>`
<ul> <ul>
Save the generated secret as plaintext in FILE. The generated secret can be used Specify the secret type. Limitations to the input data apply depending on the
to generate add-secret requests for a different guest with the same secret using secret type.
--input-secret. Destroy the secret when it is not used anymore. Possible values:
- **plain**: A plaintext secret. Can be any file up to 8190 bytes long.
- **aes**: An AES key. Must be a plain byte file 128, 192, or 256 bit long.
- **aes-xts**: An AES-XTS key. Must be a plain byte file 512, or 1024 bit long.
- **hmac-sha**: A HMAC-SHA key. Must be a plain byte file 512, or 1024 bit long.
- **ec**: An elliptic curve private key. Must be a PEM or DER file.
</ul> </ul>
`-h`, `--help` `-h`, `--help`
<ul> <ul>
Print help Print help (see a summary with '-h').
</ul> </ul>
@@ -309,13 +383,14 @@ Print help
### Synopsis ### Synopsis
`pvsecret add <FILE>` `pvsecret add <FILE>`
### Description ### Description
Perform an add-secret request (s390x only). Perform an add-secret request using Submit an add-secret request to the Ultravisor (s390x only). Perform an
a previously generated add-secret request. Only available on s390x. add-secret request using a previously generated add-secret request. Only
available on s390x.
### Arguments ### Arguments
`<FILE>` `<FILE>`
<ul> <ul>
Specify the request to be sent Specify the request to be sent.
</ul> </ul>
@@ -325,8 +400,8 @@ Specify the request to be sent
`pvsecret lock` `pvsecret lock`
### Description ### Description
Lock the secret-store (s390x only). Lock the secret store (s390x only). After Lock the secret-store (s390x only). Lock the secret store (s390x only). After
this command executed successfully, all add-secret requests will fail. Only this command executed successfully, all subsequent add-secret requests will
available on s390x. fail. Only available on s390x.
## pvsecret list ## pvsecret list
### Synopsis ### Synopsis
@@ -339,7 +414,7 @@ Execution guest. Only available on s390x.
`<FILE>` `<FILE>`
<ul> <ul>
Store the result in FILE Store the result in FILE.
Default value: '-' Default value: '-'
</ul> </ul>
@@ -348,18 +423,18 @@ Store the result in FILE
`--format <FORMAT>` `--format <FORMAT>`
<ul> <ul>
Define the output format of the list Define the output format of the list.
Default value: 'human' Default value: 'human'
Possible values: Possible values:
- **human**: Human-focused, non-parsable output format - **human**: Human-focused, non-parsable output format.
- **yaml**: Use yaml format - **yaml**: Use yaml format.
- **bin**: Use the format the ultravisor uses to pass the list - **bin**: Use the format the ultravisor uses to pass the list.
</ul> </ul>
`-h`, `--help` `-h`, `--help`
<ul> <ul>
Print help Print help (see a summary with '-h').
</ul> </ul>
@@ -407,7 +482,7 @@ The verification process works as follows:
`<FILE>` `<FILE>`
<ul> <ul>
Specify the request to be checked Specify the request to be checked.
</ul> </ul>
@@ -435,5 +510,58 @@ contains this user-data with padded zeros if available.
`-h`, `--help` `-h`, `--help`
<ul> <ul>
Print help Print help (see a summary with '-h').
</ul>
## pvsecret retrieve
### Synopsis
`pvsecret retrieve [OPTIONS] <ID>`
`pvsecret retr [OPTIONS] <ID>`
### Description
Retrieve a secret from the UV secret store (s390x only)
### Arguments
`<ID>`
<ul>
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.
</ul>
### Options
`-o`, `--output <FILE>`
<ul>
Specify the output path to place the secret value.
Default value: '-'
</ul>
`--inform <INFORM>`
<ul>
Define input type for the Secret ID.
Default value: 'yaml'
Possible values:
- **yaml**: Use a yaml file.
- **hex**: Use a hex string.
- **name**: Use a name-string. Will hash it if no secret with the name found.
</ul>
`--outform <OUTFORM>`
<ul>
Define the output format for the retrieved secret.
Default value: 'pem'
Possible values:
- **pem**: Write the secret as PEM.
- **bin**: Write the secret in binary.
</ul>
`-h`, `--help`
<ul>
Print help (see a summary with '-h').
</ul> </ul>
+4 -5
View File
@@ -3,12 +3,11 @@
.\" it under the terms of the MIT license. See LICENSE for details. .\" it under the terms of the MIT license. See LICENSE for details.
.\" .\"
.TH pvsecret-add 1 "2024-05-21" "s390-tools" "UV-Secret Manual" .TH "PVSECRET-ADD" "1" "2024-12-19" "s390-tools" "UV-Secret Manual"
.nh .nh
.ad l .ad l
.SH NAME .SH NAME
\fBpvsecret add\fP - Perform an add-secret request (s390x only) pvsecret-add \- Submit an add-secret request to the Ultravisor (s390x only)
\fB
.SH SYNOPSIS .SH SYNOPSIS
.nf .nf
.fam C .fam C
@@ -16,7 +15,7 @@ pvsecret add <FILE>
.fam C .fam C
.fi .fi
.SH DESCRIPTION .SH DESCRIPTION
Perform an add-secret request using a previously generated add-secret request. Perform an add\-secret request using a previously generated add\-secret request.
Only available on s390x. Only available on s390x.
.SH OPTIONS .SH OPTIONS
.PP .PP
@@ -29,7 +28,7 @@ Specify the request to be sent.
.PP .PP
\-h, \-\-help \-h, \-\-help
.RS 4 .RS 4
Print help. Print help (see a summary with \fB\-h\fR).
.RE .RE
.RE .RE
+13 -13
View File
@@ -3,12 +3,11 @@
.\" it under the terms of the MIT license. See LICENSE for details. .\" it under the terms of the MIT license. See LICENSE for details.
.\" .\"
.TH pvsecret-create-association 1 "2024-05-21" "s390-tools" "UV-Secret Manual" .TH "PVSECRET-CREATE-ASSOCIATION" "1" "2024-12-19" "s390-tools" "UV-Secret Manual"
.nh .nh
.ad l .ad l
.SH NAME .SH NAME
\fBpvsecret create association\fP - Create an association secret pvsecret-create-association \- Create an association secret
\fB
.SH SYNOPSIS .SH SYNOPSIS
.nf .nf
.fam C .fam C
@@ -17,14 +16,14 @@ pvsecret create association [OPTIONS] <NAME>
.fi .fi
.SH DESCRIPTION .SH DESCRIPTION
Use an association secret to connect a trusted I/O device to a guest. The Use an association secret to connect a trusted I/O device to a guest. The
`pvapconfig` tool provides more information about association secrets. \fBpvapconfig\fR tool provides more information about association secrets.
.SH OPTIONS .SH OPTIONS
.PP .PP
<NAME> <NAME>
.RS 4 .RS 4
String to identify the new secret. The actual secret is set with String that identifies the new secret. The actual secret is set with
\fB--input-secret\fR. The name is saved in `NAME.yaml` with white-spaces mapped \fB\-\-input\-secret\fR. The name is saved in `NAME.yaml` with white\-spaces
to `_`. mapped to `_`.
.RE .RE
.RE .RE
@@ -35,24 +34,25 @@ Print the hashed name to stdout. The hashed name is not written to `NAME.yaml`
.RE .RE
.RE .RE
.PP .PP
\-\-input-secret <FILE> \-\-input\-secret <SECRET-FILE>
.RS 4 .RS 4
Path from which to read the plaintext secret. Uses a random secret if not Path from which to read the plaintext secret. Uses a random secret if not
specified. specified.
.RE .RE
.RE .RE
.PP .PP
\-\-output-secret <FILE> \-\-output\-secret <SECRET-FILE>
.RS 4 .RS 4
Save the generated secret as plaintext in FILE. The generated secret can be used Save the generated secret as plaintext in SECRET\-FILE. The generated secret can
to generate add-secret requests for a different guest with the same secret using be used to generate add\-secret requests for a different guest with the same
\fB--input-secret\fR. Destroy the secret when it is not used anymore. secret using \fB\-\-input\-secret\fR. Destroy the secret when it is not used
anymore.
.RE .RE
.RE .RE
.PP .PP
\-h, \-\-help \-h, \-\-help
.RS 4 .RS 4
Print help. Print help (see a summary with \fB\-h\fR).
.RE .RE
.RE .RE
+3 -4
View File
@@ -1,14 +1,13 @@
.\" Copyright 2023 IBM Corp. .\" Copyright 2023, 2024 IBM Corp.
.\" s390-tools is free software; you can redistribute it and/or modify .\" s390-tools is free software; you can redistribute it and/or modify
.\" it under the terms of the MIT license. See LICENSE for details. .\" it under the terms of the MIT license. See LICENSE for details.
.\" .\"
.TH pvsecret-create-meta 1 "2024-01-30" "s390-tools" "UV-Secret Manual" .TH "PVSECRET-CREATE-META" "1" "2024-12-19" "s390-tools" "UV-Secret Manual"
.nh .nh
.ad l .ad l
.SH NAME .SH NAME
\fBpvsecret create meta\fP - Create a meta secret pvsecret-create-meta \- Create a meta secret
\fB
.SH SYNOPSIS .SH SYNOPSIS
.nf .nf
.fam C .fam C
@@ -0,0 +1,74 @@
.\" Copyright 2024 IBM Corp.
.\" 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 "PVSECRET-CREATE-RETRIEVABLE" "1" "2024-12-19" "s390-tools" "UV-Secret Manual"
.nh
.ad l
.SH NAME
pvsecret-create-retrievable \- Create a retrievable secret
.SH SYNOPSIS
.nf
.fam C
pvsecret create retrievable [OPTIONS] --secret <SECRET-FILE> --type <TYPE> <NAME>
pvsecret create retr [OPTIONS] --secret <SECRET-FILE> --type <TYPE> <NAME>
.fam C
.fi
.SH DESCRIPTION
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.
.SH OPTIONS
.PP
<NAME>
.RS 4
String that identifies the new secret. The actual secret is set with
\fB\-\-secret\fR. The name is saved in `NAME.yaml` with white\-spaces mapped to
`_`.
.RE
.RE
.PP
\-\-stdout
.RS 4
Print the hashed name to stdout. The hashed name is not written to `NAME.yaml`
.RE
.RE
.PP
\-\-secret <SECRET-FILE>
.RS 4
Use SECRET\-FILE as retrievable secret.
.RE
.RE
.PP
\-\-type <TYPE>
.RS 4
Specify the secret type. Limitations to the input data apply depending on the
secret type.
Possible values:
.RS 4
\- \fBplain\fP: A plaintext secret. Can be any file up to 8190 bytes long.
\- \fBaes\fP: An AES key. Must be a plain byte file 128, 192, or 256 bit long.
\- \fBaes-xts\fP: An AES-XTS key. Must be a plain byte file 512, or 1024 bit long.
\- \fBhmac-sha\fP: A HMAC-SHA key. Must be a plain byte file 512, or 1024 bit long.
\- \fBec\fP: An elliptic curve private key. Must be a PEM or DER file.
.RE
.RE
.PP
\-h, \-\-help
.RS 4
Print help (see a summary with \fB\-h\fR).
.RE
.RE
.SH "SEE ALSO"
.sp
\fBpvsecret\fR(1) \fBpvsecret-create\fR(1)
+61 -50
View File
@@ -3,12 +3,11 @@
.\" it under the terms of the MIT license. See LICENSE for details. .\" it under the terms of the MIT license. See LICENSE for details.
.\" .\"
.TH pvsecret-create 1 "2024-05-21" "s390-tools" "UV-Secret Manual" .TH "PVSECRET-CREATE" "1" "2024-12-19" "s390-tools" "UV-Secret Manual"
.nh .nh
.ad l .ad l
.SH NAME .SH NAME
\fBpvsecret create\fP - Create a new add-secret request pvsecret-create \- Create a new add-secret request
\fB
.SH SYNOPSIS .SH SYNOPSIS
.nf .nf
.fam C .fam C
@@ -29,39 +28,46 @@ bound to the Configuration Unique ID from \fBpvattest\fR using \fB--cuid\fR
.SH "PVSECRET CREATE COMMANDS" .SH "PVSECRET CREATE COMMANDS"
.PP .PP
\fBmeta\fR \fBpvsecret create-meta(1)\fR
.RS 4 .RS 4
Create a meta secret Create a meta secret
.RE .RE
.PP .PP
\fBassociation\fR \fBpvsecret create-association(1)\fR
.RS 4 .RS 4
Create an association secret Create an association secret
.RE .RE
.PP
\fBpvsecret create-retrievable(1)\fR
.RS 4
Create a retrievable secret
.RE
.SH OPTIONS .SH OPTIONS
.PP .PP
\-k, \-\-host-key-document <FILE> \-k, \-\-host\-key\-document <FILE>
.RS 4 .RS 4
Use FILE as a host-key document. Can be specified multiple times and must be Use FILE as a host\-key document. Can be specified multiple times and must be
used at least once. specified at least once.
.RE .RE
.RE .RE
.PP .PP
\-\-no-verify \-\-no\-verify
.RS 4 .RS 4
Disable the host-key document verification. Does not require the host-key Disable the host\-key document verification. Does not require the host\-key
documents to be valid. Do not use for a production request unless you verified documents to be valid. Do not use for a production request unless you verified
the host-key document beforehand. the host\-key document beforehand.
.RE .RE
.RE .RE
.PP .PP
\-C, \-\-cert <FILE> \-C, \-\-cert <FILE>
.RS 4 .RS 4
Use FILE as a certificate to verify the host key or keys. The certificates are Use FILE as a certificate to verify the host\-key or keys. The certificates are
used to establish a chain of trust for the verification of the host-key used to establish a chain of trust for the verification of the host\-key
documents. Specify this option twice to specify the IBM Z signing key and the documents. Specify this option twice to specify the IBM Z signing key and the
intermediate CA certificate (signed by the root CA). intermediate CA certificate (signed by the root CA).
.RE .RE
@@ -69,9 +75,9 @@ intermediate CA certificate (signed by the root CA).
.PP .PP
\-\-crl <FILE> \-\-crl <FILE>
.RS 4 .RS 4
Use FILE as a certificate revocation list. The list is used to check whether a Use FILE as a certificate revocation list (CRL). The list is used to check
certificate of the chain of trust is revoked. Specify this option multiple times whether a certificate of the chain of trust is revoked. Specify this option
to use multiple CRLs. multiple times to use multiple CRLs.
.RE .RE
.RE .RE
.PP .PP
@@ -81,27 +87,26 @@ Make no attempt to download CRLs.
.RE .RE
.RE .RE
.PP .PP
\-\-root-ca <ROOT_CA> \-\-root\-ca <ROOT_CA>
.RS 4 .RS 4
Use FILE as the root-CA certificate for the verification. If omitted, the system Use FILE as the root\-CA certificate for the verification. If omitted, the
wide-root CAs installed on the system are used. Use this only if you trust the system wide\-root CAs installed on the system are used. Use this only if you
specified certificate. trust the specified certificate.
.RE .RE
.RE .RE
.PP .PP
\-\-hdr <FILE> \-\-hdr <FILE>
.RS 4 .RS 4
Specifies the header of the guest image. Can be an IBM Secure Execution image 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 created by \fBpvimg/genprotimg\fR or an extracted IBM Secure Execution header.
must start at a page boundary.
.RE .RE
.RE .RE
.PP .PP
\-f, \-\-force \-f, \-\-force
.RS 4 .RS 4
Force the generation of add-secret requests on IBM Secure Execution guests. If Force the generation of add\-secret requests on IBM Secure Execution guests. If
the program detects that it is running on an IBM Secure Execution guest, it the program detects that it is running on an IBM Secure Execution guest, it
denies the generation of add-secret requests. The force flag overwrites this denies the generation of add\-secret requests. The force flag overwrites this
behavior. behavior.
.RE .RE
.RE .RE
@@ -112,7 +117,7 @@ Write the generated request to FILE.
.RE .RE
.RE .RE
.PP .PP
\-\-extension-secret <FILE> \-\-extension\-secret <FILE>
.RS 4 .RS 4
Use the content of FILE as an extension secret. The file must be exactly 32 Use the content of FILE as an extension secret. The file must be exactly 32
bytes long. If this request is the first, all subsequent requests must have the bytes long. If this request is the first, all subsequent requests must have the
@@ -124,7 +129,7 @@ request.
.PP .PP
\-\-cck <FILE> \-\-cck <FILE>
.RS 4 .RS 4
Use the content of FILE as the customer-communication key (CCK) to derive the Use the content of FILE as the customer\-communication key (CCK) to derive the
extension secret. The file must contain exactly 32 bytes of data. If the target extension secret. The file must contain exactly 32 bytes of data. If the target
guest was started with bit 1 of the secret control flag set, the ultravisor also guest was started with bit 1 of the secret control flag set, the ultravisor also
derives the secret from the CCK. Otherwise, the ultravisor interprets the derives the secret from the CCK. Otherwise, the ultravisor interprets the
@@ -133,13 +138,13 @@ all requests.
.RE .RE
.RE .RE
.PP .PP
\-\-cuid-hex <HEXSTRING> \-\-cuid\-hex <HEXSTRING>
.RS 4 .RS 4
Use HEXSTRING as the Configuration Unique ID. Must be a hex 128-bit unsigned big Use HEXSTRING as the Configuration Unique ID. Must be a hex 128\-bit unsigned
endian number string. Leading zeros must be provided. If specified, the value big endian number string. Leading zeros must be provided. If specified, the
must match with the Config-UID from the attestation result of that guest. If not value must match with the Config\-UID from the attestation result of that guest.
specified, the CUID will be ignored by the ultravisor during the verification of If not specified, the CUID will be ignored by the ultravisor during the
the request. verification of the request.
.RE .RE
.RE .RE
.PP .PP
@@ -147,7 +152,7 @@ the request.
.RS 4 .RS 4
Use the content of FILE as the Configuration Unique ID. The file must contain Use the content of FILE as the Configuration Unique ID. The file must contain
exactly 128 bit of data or a yaml with a `cuid` entry. If specified, the value exactly 128 bit of data or a yaml with a `cuid` entry. If specified, the value
must match the Config-UID from the attestation result of that guest. If not must match the Config\-UID from the attestation result of that guest. If not
specified, the CUID will be ignored by the Ultravisor during the verification of specified, the CUID will be ignored by the Ultravisor during the verification of
the request. the request.
.RE .RE
@@ -155,52 +160,58 @@ the request.
.PP .PP
\-\-flags <FLAGS> \-\-flags <FLAGS>
.RS 4 .RS 4
Flags for the add-secret request. Flags for the add\-secret request.
Possible values: Possible values:
.RS 4 .RS 4
- \fBdisable-dump\fP: Disables host-initiated dumping for the target guest instance. \- \fBdisable-dump\fP: Disables host-initiated dumping for the target guest instance.
.RE .RE
.RE .RE
.PP .PP
\-\-user-data <FILE> \-\-user\-data <FILE>
.RS 4 .RS 4
Use the content of FILE as user-data. Passes user data defined in <FILE> through Use the content of FILE as user\-data. Passes user data defined in FILE through
the add-secret request to the ultravisor. The user data can be up to 512 bytes the add\-secret request to the ultravisor. The user data can be up to 512 bytes
of arbitrary data, and the maximum size depends on the size of the user-signing of arbitrary data, and the maximum size depends on the size of the user\-signing
key: key:
- No key: user data can be 512 bytes. \- No key: user data can be 512 bytes.
- EC(secp521r1) or RSA 2048 keys: user data can be 256 bytes. \- EC(secp521r1) or RSA 2048 keys: user data can be 256 bytes.
- RSA 3072 key: user data can be 128 bytes. \- RSA 3072 key: user data can be 128 bytes.
The firmware ignores this data, but the request tag protects the user-data. The firmware ignores this data, but the request tag protects the user\-data.
Optional. No user-data by default. Optional. No user\-data by default.
.RE .RE
.RE .RE
.PP .PP
\-\-user-sign-key <FILE> \-\-user\-sign\-key <FILE>
.RS 4 .RS 4
Use the content of FILE as user signing key. Adds a signature calculated from Use the content of FILE as user signing key. Adds a signature calculated from
the key in <FILE> to the add-secret request. The file must be in DER or PEM the key in FILE to the add\-secret request. The file must be in DER or PEM
format containing a private key. Supported are RSA 2048 & 3072-bit and format containing a private key. Supported are RSA 2048 & 3072\-bit and
EC(secp521r1) keys. The firmware ignores the content, but the request tag EC(secp521r1) keys. The firmware ignores the content, but the request tag
protects the signature. The user-signing key signs the request. The location of protects the signature. The user\-signing key signs the request. The location of
the signature is filled with zeros during the signature calculation. The request the signature is filled with zeros during the signature calculation. The request
tag also secures the signature. See man pvsecret verify for more details. tag also secures the signature. See man pvsecret verify for more details.
Optional. No signature by default. Optional. No signature by default.
.RE .RE
.RE .RE
.PP .PP
\-\-use\-name
.RS 4
Do not hash the name, use it directly as secret ID. Ignored for meta\-secrets.
.RE
.RE
.PP
\-h, \-\-help \-h, \-\-help
.RS 4 .RS 4
Print help. Print help (see a summary with \fB\-h\fR).
.RE .RE
.RE .RE
.SH "SEE ALSO" .SH "SEE ALSO"
.sp .sp
\fBpvsecret\fR(1) \fBpvsecret-create-meta\fR(1) \fBpvsecret-create-association\fR(1) \fBpvsecret\fR(1) \fBpvsecret-create-meta\fR(1) \fBpvsecret-create-association\fR(1) \fBpvsecret-create-retrievable\fR(1)
+8 -9
View File
@@ -3,12 +3,11 @@
.\" it under the terms of the MIT license. See LICENSE for details. .\" it under the terms of the MIT license. See LICENSE for details.
.\" .\"
.TH pvsecret-list 1 "2024-05-21" "s390-tools" "UV-Secret Manual" .TH "PVSECRET-LIST" "1" "2024-12-19" "s390-tools" "UV-Secret Manual"
.nh .nh
.ad l .ad l
.SH NAME .SH NAME
\fBpvsecret list\fP - List all ultravisor secrets (s390x only) pvsecret-list \- List all ultravisor secrets (s390x only)
\fB
.SH SYNOPSIS .SH SYNOPSIS
.nf .nf
.fam C .fam C
@@ -16,8 +15,8 @@ pvsecret list [OPTIONS] [FILE]
.fam C .fam C
.fi .fi
.SH DESCRIPTION .SH DESCRIPTION
Lists the IDs of all non-null secrets currently stored in the ultravisor for the Lists the IDs of all non\-null secrets currently stored in the ultravisor for
currently running IBM Secure Execution guest. Only available on s390x. the currently running IBM Secure Execution guest. Only available on s390x.
.SH OPTIONS .SH OPTIONS
.PP .PP
<FILE> <FILE>
@@ -35,18 +34,18 @@ Define the output format of the list.
Possible values: Possible values:
.RS 4 .RS 4
- \fBhuman\fP: Human-focused, non-parsable output format. \- \fBhuman\fP: Human-focused, non-parsable output format.
- \fByaml\fP: Use yaml format. \- \fByaml\fP: Use yaml format.
- \fBbin\fP: Use the format the ultravisor uses to pass the list. \- \fBbin\fP: Use the format the ultravisor uses to pass the list.
.RE .RE
.RE .RE
.PP .PP
\-h, \-\-help \-h, \-\-help
.RS 4 .RS 4
Print help. Print help (see a summary with \fB\-h\fR).
.RE .RE
.RE .RE
+3 -4
View File
@@ -3,12 +3,11 @@
.\" it under the terms of the MIT license. See LICENSE for details. .\" it under the terms of the MIT license. See LICENSE for details.
.\" .\"
.TH pvsecret-lock 1 "2024-05-15" "s390-tools" "UV-Secret Manual" .TH "PVSECRET-LOCK" "1" "2024-12-19" "s390-tools" "UV-Secret Manual"
.nh .nh
.ad l .ad l
.SH NAME .SH NAME
\fBpvsecret lock\fP - Lock the secret-store (s390x only) pvsecret-lock \- Lock the secret-store (s390x only)
\fB
.SH SYNOPSIS .SH SYNOPSIS
.nf .nf
.fam C .fam C
@@ -17,7 +16,7 @@ pvsecret lock
.fi .fi
.SH DESCRIPTION .SH DESCRIPTION
Lock the secret store (s390x only). After this command executed successfully, Lock the secret store (s390x only). After this command executed successfully,
all add-secret requests will fail. Only available on s390x. all subsequent add\-secret requests will fail. Only available on s390x.
.SH "SEE ALSO" .SH "SEE ALSO"
.sp .sp
\fBpvsecret\fR(1) \fBpvsecret\fR(1)
+77
View File
@@ -0,0 +1,77 @@
.\" Copyright 2024 IBM Corp.
.\" 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 "PVSECRET-RETRIEVE" "1" "2024-12-19" "s390-tools" "UV-Secret Manual"
.nh
.ad l
.SH NAME
pvsecret-retrieve \- Retrieve a secret from the UV secret store (s390x only)
.SH SYNOPSIS
.nf
.fam C
pvsecret retrieve [OPTIONS] <ID>
pvsecret retr [OPTIONS] <ID>
.fam C
.fi
.SH DESCRIPTION
Retrieve a secret from the UV secret store (s390x only)
.SH OPTIONS
.PP
<ID>
.RS 4
Specify the secret ID to be retrieved. Input type depends on \fB\-\-inform\fR.
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.
.RE
.RE
.PP
\-o, \-\-output <FILE>
.RS 4
Specify the output path to place the secret value.
[default: '-']
.RE
.RE
.PP
\-\-inform <INFORM>
.RS 4
Define input type for the Secret ID.
[default: 'yaml']
Possible values:
.RS 4
\- \fByaml\fP: Use a yaml file.
\- \fBhex\fP: Use a hex string.
\- \fBname\fP: Use a name-string. Will hash it if no secret with the name found.
.RE
.RE
.PP
\-\-outform <OUTFORM>
.RS 4
Define the output format for the retrieved secret.
[default: 'pem']
Possible values:
.RS 4
\- \fBpem\fP: Write the secret as PEM.
\- \fBbin\fP: Write the secret in binary.
.RE
.RE
.PP
\-h, \-\-help
.RS 4
Print help (see a summary with \fB\-h\fR).
.RE
.RE
.SH "SEE ALSO"
.sp
\fBpvsecret\fR(1)
+8 -9
View File
@@ -3,12 +3,11 @@
.\" it under the terms of the MIT license. See LICENSE for details. .\" it under the terms of the MIT license. See LICENSE for details.
.\" .\"
.TH pvsecret-verify 1 "2024-05-21" "s390-tools" "UV-Secret Manual" .TH "PVSECRET-VERIFY" "1" "2024-12-19" "s390-tools" "UV-Secret Manual"
.nh .nh
.ad l .ad l
.SH NAME .SH NAME
\fBpvsecret verify\fP - Verify that an add-secret request is sane pvsecret-verify \- Verify that an add-secret request is sane
\fB
.SH SYNOPSIS .SH SYNOPSIS
.nf .nf
.fam C .fam C
@@ -89,12 +88,12 @@ Specify the request to be checked.
.RE .RE
.PP .PP
\-\-user-cert <FILE> \-\-user\-cert <FILE>
.RS 4 .RS 4
Certificate containing a public key used to verify the user data signature. Certificate containing a public key used to verify the user data signature.
Specifies a public key used to verify the user-data signature. The file must be Specifies a public key used to verify the user\-data signature. The file must be
a X509 certificate in DSA or PEM format. The certificate must hold the public a X509 certificate in DSA or PEM format. The certificate must hold the public
EC, RSA 2048, or RSA 3072 key corresponding to the private user-key used during EC, RSA 2048, or RSA 3072 key corresponding to the private user\-key used during
`create`. No chain of trust is established. Ensuring that the certificate can be `create`. No chain of trust is established. Ensuring that the certificate can be
trusted is the responsibility of the user. The EC key must use the NIST/SECG trusted is the responsibility of the user. The EC key must use the NIST/SECG
curve over a 521 bit prime field (secp521r1). curve over a 521 bit prime field (secp521r1).
@@ -103,15 +102,15 @@ curve over a 521 bit prime field (secp521r1).
.PP .PP
\-o, \-\-output <FILE> \-o, \-\-output <FILE>
.RS 4 .RS 4
Store the result in FILE If the request contained abirtary user-data the output Store the result in FILE If the request contained abirtary user\-data the output
contains this user-data with padded zeros if available. contains this user\-data with padded zeros if available.
[default: '-'] [default: '-']
.RE .RE
.RE .RE
.PP .PP
\-h, \-\-help \-h, \-\-help
.RS 4 .RS 4
Print help. Print help (see a summary with \fB\-h\fR).
.RE .RE
.RE .RE
+23 -11
View File
@@ -3,12 +3,11 @@
.\" it under the terms of the MIT license. See LICENSE for details. .\" it under the terms of the MIT license. See LICENSE for details.
.\" .\"
.TH pvsecret 1 "2024-05-21" "s390-tools" "UV-Secret Manual" .TH "PVSECRET" "1" "2024-12-19" "s390-tools" "UV-Secret Manual"
.nh .nh
.ad l .ad l
.SH NAME .SH NAME
\fBpvsecret\fP - Manage secrets for IBM Secure Execution guests pvsecret \- Manage secrets for IBM Secure Execution guests
\fB
.SH SYNOPSIS .SH SYNOPSIS
.nf .nf
.fam C .fam C
@@ -36,39 +35,46 @@ both the PEM and DER input formats are supported.
.SH "PVSECRET COMMANDS" .SH "PVSECRET COMMANDS"
.PP .PP
\fBcreate\fR \fBpvsecret-create(1)\fR
.RS 4 .RS 4
Create a new add-secret request Create a new add-secret request
.RE .RE
.PP .PP
\fBadd\fR \fBpvsecret-add(1)\fR
.RS 4 .RS 4
Perform an add-secret request (s390x only) Submit an add-secret request to the Ultravisor (s390x only)
.RE .RE
.PP .PP
\fBlock\fR \fBpvsecret-lock(1)\fR
.RS 4 .RS 4
Lock the secret-store (s390x only) Lock the secret-store (s390x only)
.RE .RE
.PP .PP
\fBlist\fR \fBpvsecret-list(1)\fR
.RS 4 .RS 4
List all ultravisor secrets (s390x only) List all ultravisor secrets (s390x only)
.RE .RE
.PP .PP
\fBverify\fR \fBpvsecret-verify(1)\fR
.RS 4 .RS 4
Verify that an add-secret request is sane Verify that an add-secret request is sane
.RE .RE
.PP
\fBpvsecret-retrieve(1)\fR
.RS 4
Retrieve a secret from the UV secret store (s390x only)
.RE
.SH OPTIONS .SH OPTIONS
.PP .PP
\-v, \-\-verbose \-v, \-\-verbose
@@ -77,6 +83,12 @@ Provide more detailed output.
.RE .RE
.RE .RE
.PP .PP
\-q, \-\-quiet
.RS 4
Provide less output.
.RE
.RE
.PP
\-\-version \-\-version
.RS 4 .RS 4
Print version information and exit. Print version information and exit.
@@ -85,7 +97,7 @@ Print version information and exit.
.PP .PP
\-h, \-\-help \-h, \-\-help
.RS 4 .RS 4
Print help. Print help (see a summary with \fB\-h\fR).
.RE .RE
.RE .RE
@@ -138,4 +150,4 @@ On the SE-guest, \fIlock\fP the secret store.
.fi .fi
.SH "SEE ALSO" .SH "SEE ALSO"
.sp .sp
\fBpvsecret-create\fR(1) \fBpvsecret-add\fR(1) \fBpvsecret-lock\fR(1) \fBpvsecret-list\fR(1) \fBpvsecret-verify\fR(1) \fBpvsecret-create\fR(1) \fBpvsecret-add\fR(1) \fBpvsecret-lock\fR(1) \fBpvsecret-list\fR(1) \fBpvsecret-verify\fR(1) \fBpvsecret-retrieve\fR(1)
+147 -16
View File
@@ -1,7 +1,10 @@
// SPDX-License-Identifier: MIT // 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 clap::{ArgGroup, Args, CommandFactory, Parser, Subcommand, ValueEnum, ValueHint};
use utils::{CertificateOptions, DeprecatedVerbosityOptions, STDOUT}; use utils::{CertificateOptions, DeprecatedVerbosityOptions, STDOUT};
@@ -37,8 +40,8 @@ pub struct CreateSecretOpt {
/// Specifies the header of the guest image. /// Specifies the header of the guest image.
/// ///
/// Can be an IBM Secure Execution image created by genprotimg or an extracted IBM Secure /// Can be an IBM Secure Execution image created by 'pvimg/genprotimg' or an
/// Execution header. The header must start at a page boundary. /// extracted IBM Secure Execution header.
#[arg(long, value_name = "FILE", value_hint = ValueHint::FilePath)] #[arg(long, value_name = "FILE", value_hint = ValueHint::FilePath)]
pub hdr: String, pub hdr: String,
@@ -138,6 +141,12 @@ pub struct CreateSecretOpt {
/// by default. /// by default.
#[arg(long, value_name = "FILE", value_hint = ValueHint::FilePath,)] #[arg(long, value_name = "FILE", value_hint = ValueHint::FilePath,)]
pub user_sign_key: Option<String>, 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)] #[derive(Subcommand, Debug)]
@@ -150,12 +159,12 @@ pub enum AddSecretType {
/// Create an association secret. /// 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. /// provides more information about association secrets.
Association { 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 `_`. /// white-spaces mapped to `_`.
name: String, name: String,
@@ -166,17 +175,83 @@ pub enum AddSecretType {
stdout: bool, stdout: bool,
/// Path from which to read the plaintext secret. Uses a random secret if not specified. /// 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>, 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 /// 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. /// anymore.
#[arg(long, value_name = "FILE", value_hint = ValueHint::FilePath,)] #[arg(long, value_name = "SECRET-FILE", value_hint = ValueHint::FilePath,)]
output_secret: Option<String>, 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 // all members s390x only
@@ -238,18 +313,70 @@ pub struct VerifyOpt {
pub output: String, 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,
/// 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)]
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)] #[derive(Subcommand, Debug)]
pub enum Command { pub enum Command {
/// Create a new add-secret request. /// Create a new add-secret request.
/// ///
/// Create add-secret requests for IBM Secure Execution guests. Only create these requests in a /// 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 /// 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. /// the use case depending on the secret type.
Create(Box<CreateSecretOpt>), 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 /// Perform an add-secret request using a previously generated add-secret request. Only
/// available on s390x. /// available on s390x.
@@ -258,7 +385,7 @@ pub enum Command {
/// Lock the secret-store (s390x only). /// Lock the secret-store (s390x only).
/// ///
/// Lock the secret store (s390x only). After this command executed successfully, all /// 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, Lock,
/// List all ultravisor secrets (s390x only). /// List all ultravisor secrets (s390x only).
@@ -274,6 +401,10 @@ pub enum Command {
/// provided key. Outputs the arbitrary user-data. /// provided key. Outputs the arbitrary user-data.
Verify(VerifyOpt), Verify(VerifyOpt),
/// Retrieve a secret from the UV secret store (s390x only).
#[command(visible_alias = "retr")]
Retrieve(RetrSecretOptions),
/// Print version information and exit. /// Print version information and exit.
#[command(aliases(["--version"]), hide(true))] #[command(aliases(["--version"]), hide(true))]
Version, Version,
@@ -294,13 +425,13 @@ pub fn validate_cli(cli: &CliOptions) -> Result<(), clap::Error> {
} }
if secret_out == &Some(format!("{name}.yaml")) { if secret_out == &Some(format!("{name}.yaml")) {
return Err(CliOptions::command().error( return Err(CliOptions::command().error(
clap::error::ErrorKind::ValueValidation, ValueValidation,
format!("Secret output file and the secret name '{name}.yaml' are the same."), format!("Secret output file and the secret name '{name}.yaml' are the same."),
)); ));
} }
if format!("{name}.yaml") == opt.output { if format!("{name}.yaml") == opt.output {
return Err(CliOptions::command().error( return Err(CliOptions::command().error(
clap::error::ErrorKind::ValueValidation, ValueValidation,
format!( format!(
"output file and the secret name '{}' are the same.", "output file and the secret name '{}' are the same.",
&opt.output &opt.output
+5 -1
View File
@@ -16,6 +16,8 @@ mod add;
mod list; mod list;
#[cfg(target_arch = "s390x")] #[cfg(target_arch = "s390x")]
mod lock; mod lock;
#[cfg(target_arch = "s390x")]
mod retr;
// Commands (directly) related to UVCs are only available on s389x // Commands (directly) related to UVCs are only available on s389x
#[cfg(target_arch = "s390x")] #[cfg(target_arch = "s390x")]
@@ -24,12 +26,13 @@ mod uv_cmd {
pub use add::add; pub use add::add;
pub use list::list; pub use list::list;
pub use lock::lock; pub use lock::lock;
pub use retr::retr;
pub const UV_CMD_FN: &[&str] = &["+add", "+lock", "+list"]; pub const UV_CMD_FN: &[&str] = &["+add", "+lock", "+list"];
} }
#[cfg(not(target_arch = "s390x"))] #[cfg(not(target_arch = "s390x"))]
mod uv_cmd { mod uv_cmd {
use crate::cli::{AddSecretOpt, ListSecretOpt}; use crate::cli::{AddSecretOpt, ListSecretOpt, RetrSecretOptions};
use anyhow::{bail, Result}; use anyhow::{bail, Result};
macro_rules! not_supp { macro_rules! not_supp {
($name: ident $( ,$opt: ty )?) => { ($name: ident $( ,$opt: ty )?) => {
@@ -40,6 +43,7 @@ mod uv_cmd {
} }
not_supp!(add, AddSecretOpt); not_supp!(add, AddSecretOpt);
not_supp!(list, ListSecretOpt); not_supp!(list, ListSecretOpt);
not_supp!(retr, RetrSecretOptions);
not_supp!(lock); not_supp!(lock);
pub const UV_CMD_FN: &[&str] = &[]; pub const UV_CMD_FN: &[&str] = &[];
} }
+78 -45
View File
@@ -4,7 +4,6 @@
use std::path::Path; use std::path::Path;
use crate::cli::{AddSecretType, CreateSecretFlags, CreateSecretOpt};
use anyhow::{anyhow, bail, Context, Error, Result}; use anyhow::{anyhow, bail, Context, Error, Result};
use log::{debug, info, trace, warn}; use log::{debug, info, trace, warn};
use pv::{ use pv::{
@@ -22,6 +21,8 @@ use pv::{
use serde_yaml::Value; use serde_yaml::Value;
use utils::get_writer_from_cli_file_arg; 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<()> fn write_out<P, D>(path: &P, data: D, ctx: &str) -> pv::Result<()>
where where
P: AsRef<Path>, P: AsRef<Path>,
@@ -32,6 +33,23 @@ where
Ok(()) 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 /// Prepare an add-secret request
pub fn create(opt: &CreateSecretOpt) -> Result<()> { pub fn create(opt: &CreateSecretOpt) -> Result<()> {
if pv_guest_bit_set() { if pv_guest_bit_set() {
@@ -62,7 +80,7 @@ pub fn create(opt: &CreateSecretOpt) -> Result<()> {
write_out(&opt.output, ser_asrbc, "add-secret request")?; write_out(&opt.output, ser_asrbc, "add-secret request")?;
info!("Successfully wrote the request to '{}'", &opt.output); 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. /// Read+parse the first key from the buffer.
@@ -76,7 +94,7 @@ fn read_private_key(buf: &[u8]) -> Result<PKey<Private>> {
fn build_asrcb(opt: &CreateSecretOpt) -> Result<AddSecretRequest> { fn build_asrcb(opt: &CreateSecretOpt) -> Result<AddSecretRequest> {
debug!("Build add-secret request"); debug!("Build add-secret request");
let secret = match &opt.secret { let mut secret = match &opt.secret {
AddSecretType::Meta => GuestSecret::Null, AddSecretType::Meta => GuestSecret::Null,
AddSecretType::Association { AddSecretType::Association {
name, name,
@@ -88,9 +106,14 @@ fn build_asrcb(opt: &CreateSecretOpt) -> Result<AddSecretRequest> {
input_secret: None, input_secret: None,
.. ..
} => GuestSecret::association(name, None)?, } => GuestSecret::association(name, None)?,
AddSecretType::Retrievable {
name, secret, kind, ..
} => retrievable(name, secret, kind)?,
}; };
trace!("AddSecret: {secret:x?}"); trace!("AddSecret: {secret:x?}");
opt.use_name.then(|| secret.no_hash_name());
let mut flags = match &opt.pcf { let mut flags = match &opt.pcf {
Some(v) => (&try_parse_u64(v, "pcf")?).into(), Some(v) => (&try_parse_u64(v, "pcf")?).into(),
None => AddSecretFlags::default(), None => AddSecretFlags::default(),
@@ -136,7 +159,9 @@ fn build_asrcb(opt: &CreateSecretOpt) -> Result<AddSecretRequest> {
.as_ref() .as_ref()
.map(|p| read_file(p, "User-signing key")) .map(|p| read_file(p, "User-signing key"))
.transpose()? .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()?; .transpose()?;
if user_data.is_some() || user_key.is_some() { if user_data.is_some() || user_key.is_some() {
@@ -206,54 +231,62 @@ fn read_cuid(asrcb: &mut AddSecretRequest, opt: &CreateSecretOpt) -> Result<()>
Ok(()) 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 /// Write the generated secret (if any) to the specified output stream
fn write_secret<P: AsRef<Path>>( fn write_secret<P: AsRef<Path>>(
secret: &AddSecretType, secret: &AddSecretType,
asrcb: &AddSecretRequest, guest_secret: &GuestSecret,
outp_path: P, outp_path: P,
) -> Result<()> { ) -> Result<()> {
if let AddSecretType::Association { match secret {
name, AddSecretType::Association {
stdout, name,
output_secret: secret_out, stdout,
.. output_secret,
} = secret ..
{ } => {
let gen_name: String = name write_yaml(name, guest_secret, stdout, outp_path)?;
.chars() if let Some(path) = output_secret {
.map(|c| if c.is_whitespace() { '_' } else { c }) write_out(path, guest_secret.confidential(), "Association secret")?
.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)!")
} }
info!("Successfully wrote generated association secret to '{path}'");
} }
AddSecretType::Retrievable { name, stdout, .. } => {
write_yaml(name, guest_secret, stdout, outp_path)?
}
_ => (),
}; };
Ok(()) Ok(())
} }
+28 -10
View File
@@ -2,22 +2,40 @@
// //
// Copyright IBM Corp. 2023 // Copyright IBM Corp. 2023
use std::io::ErrorKind;
use crate::cli::{ListSecretOpt, ListSecretOutputType}; use crate::cli::{ListSecretOpt, ListSecretOutputType};
use anyhow::{Context, Result}; use anyhow::{Context, Error, Result};
use log::warn; use log::{info, warn};
use pv::uv::{ListCmd, SecretList, UvDevice, UvcSuccess}; use pv::uv::{ListCmd, SecretList, UvDevice};
use utils::{get_writer_from_cli_file_arg, STDOUT}; use utils::{get_writer_from_cli_file_arg, STDOUT};
const SECRET_LIST_BUF_SIZE: usize = 4;
/// Do a List Secrets UVC /// Do a List Secrets UVC
pub fn list_uvc(uv: &UvDevice) -> Result<SecretList> {
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)
}
/// Do a List Secrets UVC and output the list in the requested format
pub fn list(opt: &ListSecretOpt) -> Result<()> { pub fn list(opt: &ListSecretOpt) -> Result<()> {
let uv = UvDevice::open()?; let uv = UvDevice::open()?;
let mut cmd = ListCmd::default(); let secret_list = list_uvc(&uv)?;
match uv.send_cmd(&mut cmd)? {
UvcSuccess::RC_SUCCESS => (),
UvcSuccess::RC_MORE_DATA => warn!("There is more data available than expected"),
};
let secret_list: SecretList = cmd.try_into()?;
let mut wr_out = get_writer_from_cli_file_arg(&opt.output)?; let mut wr_out = get_writer_from_cli_file_arg(&opt.output)?;
match &opt.format { match &opt.format {
+68
View File
@@ -0,0 +1,68 @@
// 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 = 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:?}");
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")?
}
RetrInpFmt::Name => SecretId::from_string(&opt.input),
};
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(())
}
+1
View File
@@ -45,6 +45,7 @@ fn main() -> ExitCode {
Command::Create(opt) => cmd::create(opt), Command::Create(opt) => cmd::create(opt),
Command::Version => Ok(print_version!("2024", log_level; FEATURES.concat())), Command::Version => Ok(print_version!("2024", log_level; FEATURES.concat())),
Command::Verify(opt) => cmd::verify(opt), Command::Verify(opt) => cmd::verify(opt),
Command::Retrieve(opt) => cmd::retr(opt),
}; };
match res { match res {
+4 -4
View File
@@ -5,8 +5,8 @@ edition.workspace = true
license.workspace = true license.workspace = true
[dependencies] [dependencies]
clap = { version ="4.1", features = ["derive", "wrap_help"] } clap = { version ="4.5", features = ["derive", "wrap_help"] }
libc = "0.2.49" libc = "0.2.169"
log = { version = "0.4.6", features = ["std", "release_max_level_debug"] } log = { version = "0.4.25", features = ["std", "release_max_level_debug"] }
pv = { path = "../pv", package = "s390_pv" } pv = { path = "../pv", package = "s390_pv" }
serde = { version = "1.0.139"} serde = { version = "1.0.217"}
+4 -3
View File
@@ -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 // SAFETY: template_raw is a valid CString because it was generated by
// the `CString::new`. // the `CString::new`.
let ret = libc::mkdtemp(template_raw); 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() { if ret.is_null() {
drop(path_cstr);
Err(std::io::Error::last_os_error()) Err(std::io::Error::last_os_error())
} else { } 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 = OsStr::from_bytes(path_cstr.as_bytes());
let path = std::path::PathBuf::from(path); let path = std::path::PathBuf::from(path);
+24 -26
View File
@@ -2,7 +2,7 @@
# #
# dbginfo.sh - Tool to collect runtime, configuration, and trace information # dbginfo.sh - Tool to collect runtime, configuration, and trace information
# #
# Copyright IBM Corp. 2002, 2024 # Copyright IBM Corp. 2002, 2025
# #
# s390-tools is free software; you can redistribute it and/or modify # s390-tools is free software; you can redistribute it and/or modify
# it under the terms of the MIT license. See LICENSE for details. # it under the terms of the MIT license. See LICENSE for details.
@@ -38,6 +38,8 @@ readonly LOG_FILE_SIZE_CHECK=50 # max logfile size in MB
readonly LOG_FILE_AGE_CHECK=7 # age in days to include for size checking readonly LOG_FILE_AGE_CHECK=7 # age in days to include for size checking
# Mount point of the debug file system # Mount point of the debug file system
readonly MOUNT_POINT_DEBUGFS="/sys/kernel/debug" readonly MOUNT_POINT_DEBUGFS="/sys/kernel/debug"
# Network devices
readonly NETWORK_DEVS=$(cd /sys/class/net; ls -d */ 2>/dev/null | sed 's/\///g')
# distro info # distro info
readonly OSPRETTY="$(cat /etc/os* 2>/dev/null | grep -m1 PRETTY_NAME | sed 's/\"//g')" readonly OSPRETTY="$(cat /etc/os* 2>/dev/null | grep -m1 PRETTY_NAME | sed 's/\"//g')"
readonly OS_NAME="${OSPRETTY##*=}" readonly OS_NAME="${OSPRETTY##*=}"
@@ -81,7 +83,7 @@ paramWORKDIR_BASE="/tmp" # initial default path
print_version() { print_version() {
cat <<EOF cat <<EOF
${SCRIPTNAME}: Debug information script version %S390_TOOLS_VERSION% ${SCRIPTNAME}: Debug information script version %S390_TOOLS_VERSION%
Copyright IBM Corp. 2002, 2024 Copyright IBM Corp. 2002, 2025
EOF EOF
} }
@@ -117,8 +119,6 @@ Options:
-v|--version print version information -v|--version print version information
-c|--check online base system information (no data collection) -c|--check online base system information (no data collection)
Please report bugs to: linux390@de.ibm.com
EOF EOF
} }
@@ -374,6 +374,7 @@ LOGFILES="\
/var/log/dracut.log*\ /var/log/dracut.log*\
/var/log/IBMtape.trace\ /var/log/IBMtape.trace\
/var/log/IBMtape.errorlog\ /var/log/IBMtape.errorlog\
/var/log/kdump.log\
/var/log/libvirt\ /var/log/libvirt\
/var/log/lin_tape.trace\ /var/log/lin_tape.trace\
/var/log/lin_tape.errorlog\ /var/log/lin_tape.errorlog\
@@ -475,6 +476,7 @@ CMDS="uname -a\
:last\ :last\
:lscpu -ae\ :lscpu -ae\
:lscpu -ye\ :lscpu -ye\
:lscpumf -i\
:lsmem\ :lsmem\
:lsmod\ :lsmod\
:lsshut\ :lsshut\
@@ -542,6 +544,8 @@ CMDS="${CMDS}\
# crypto specific commands # crypto specific commands
CMDS="${CMDS}\ CMDS="${CMDS}\
:cpacfinfo # for MSA 13 info\
:cpacfinfo -m -f -a -n\
:ep11info -D\ :ep11info -D\
:ep11info -H\ :ep11info -H\
:ep11info -M\ :ep11info -M\
@@ -1014,20 +1018,20 @@ collect_initrd_configfiles() {
######################################## ########################################
collect_osaoat() { collect_osaoat() {
local network_devices local osa_devices
local network_device local osa_device
network_devices=$(lsqeth 2>/dev/null | grep "Device name" \
| sed 's/D.*:[[:space:]]*\([^[:space:]]*\)[[:space:]]\+/\1/g' \
| sed 's/[()]//g' )
if type qethqoat >/dev/null; then if type qethqoat >/dev/null; then
if test -n "${network_devices}"; then osa_devices=$(lsqeth 2>/dev/null | grep "Device name" \
| sed 's/D.*:[[:space:]]*\([^[:space:]]*\)[[:space:]]\+/\1/g' \
| sed 's/[()]//g' )
if test -n "${osa_devices}"; then
pr_collect_output "osa oat" pr_collect_output "osa oat"
for network_device in ${network_devices}; do for osa_device in ${osa_devices}; do
call_run_command "qethqoat ${network_device}" \ call_run_command "qethqoat ${osa_device}" \
"${OUTPUT_FILE_OSAOAT}.out" && "${OUTPUT_FILE_OSAOAT}.out" &&
call_run_command "qethqoat -r ${network_device}" \ call_run_command "qethqoat -r ${osa_device}" \
"${OUTPUT_FILE_OSAOAT}_${network_device}.raw" "${OUTPUT_FILE_OSAOAT}_${osa_device}.raw"
done done
else else
pr_skip "osa oat: no devices" pr_skip "osa oat: no devices"
@@ -1039,14 +1043,12 @@ collect_osaoat() {
######################################## ########################################
collect_ethtool() { collect_ethtool() {
local network_devices
local network_device local network_device
network_devices=$(ls /sys/class/net 2>/dev/null)
if type ethtool >/dev/null; then if type ethtool >/dev/null; then
if test -n "${network_devices}"; then if test -n "${NETWORK_DEVS}"; then
pr_collect_output "ethtool" pr_collect_output "ethtool"
for network_device in ${network_devices}; do for network_device in ${NETWORK_DEVS}; do
call_run_command "ethtool ${network_device}" \ call_run_command "ethtool ${network_device}" \
"${OUTPUT_FILE_ETHTOOL}" "${OUTPUT_FILE_ETHTOOL}"
call_run_command "ethtool -k ${network_device}" \ call_run_command "ethtool -k ${network_device}" \
@@ -1082,14 +1084,12 @@ collect_ethtool() {
######################################## ########################################
collect_tc() { collect_tc() {
local network_devices
local network_device local network_device
network_devices=$(ls /sys/class/net 2>/dev/null)
if type tc >/dev/null; then if type tc >/dev/null; then
if test -n "${network_devices}"; then if test -n "${NETWORK_DEVS}"; then
pr_collect_output "Trafic Control" pr_collect_output "Trafic Control"
for network_device in ${network_devices}; do for network_device in ${NETWORK_DEVS}; do
call_run_command "tc -s qdisc show dev ${network_device}" \ call_run_command "tc -s qdisc show dev ${network_device}" \
"${OUTPUT_FILE_TC}" "${OUTPUT_FILE_TC}"
done done
@@ -1103,14 +1103,12 @@ collect_tc() {
######################################## ########################################
collect_bridge() { collect_bridge() {
local network_devices
local network_device local network_device
network_devices=$(ls /sys/class/net 2>/dev/null)
if type bridge >/dev/null; then if type bridge >/dev/null; then
if test -n "${network_devices}"; then if test -n "${NETWORK_DEVS}"; then
pr_collect_output "bridge" pr_collect_output "bridge"
for network_device in ${network_devices}; do for network_device in ${NETWORK_DEVS}; do
call_run_command "bridge -d link show dev ${network_device}" \ call_run_command "bridge -d link show dev ${network_device}" \
"${OUTPUT_FILE_BRIDGE}" "${OUTPUT_FILE_BRIDGE}"
call_run_command "bridge -s fdb show dev ${network_device}" \ call_run_command "bridge -s fdb show dev ${network_device}" \
-2
View File
@@ -125,5 +125,3 @@ Run the script with root authority.
.br .br
For Linux on z/VM, only z/VM guest virtual machines with class B privileges For Linux on z/VM, only z/VM guest virtual machines with class B privileges
yield the complete debug information. yield the complete debug information.
.SH AUTHOR
Linux on IBM Z development <linux390@de.ibm.com>
+2 -1
View File
@@ -10,7 +10,8 @@
[Unit] [Unit]
Description=CPACF statistics collection daemon process for Linux on System z Description=CPACF statistics collection daemon process for Linux on System z
Documentation=man:cpacfstatsd(8) Documentation=man:cpacfstatsd(8)
ConditionVirtualization=no ConditionVirtualization=|no
ConditionVirtualization=|vm
[Service] [Service]
Type=forking Type=forking
+1 -1
View File
@@ -719,7 +719,7 @@ static bool read_cmcb(int chpid, cmcb_t *cmcb)
free(path); free(path);
path = get_chpid_path(chpid, "measurement_chars"); path = get_chpid_path(chpid, "measurement_chars");
/* Partial CMCB contains data starting at word 3. */ /* Partial CMCB contains data starting at word 3. */
read_bin(path, &cmcb[PARTIAL_CMCB_OFFSET], read_bin(path, (char *)cmcb + PARTIAL_CMCB_OFFSET,
PARTIAL_CMCB_SIZE, false); PARTIAL_CMCB_SIZE, false);
} }
free(path); free(path);
+2 -2
View File
@@ -17,8 +17,8 @@
#include "column.h" #include "column.h"
#define CMCB_SIZE (8 * sizeof(u32)) #define CMCB_SIZE (8 * sizeof(u32))
#define PARTIAL_CMCB_OFFSET 3 #define PARTIAL_CMCB_OFFSET (3 * sizeof(u32))
#define PARTIAL_CMCB_SIZE (CMCB_SIZE - PARTIAL_CMCB_OFFSET * sizeof(u32)) #define PARTIAL_CMCB_SIZE (CMCB_SIZE - PARTIAL_CMCB_OFFSET)
#define CUE_SIZE (8 * sizeof(u32)) #define CUE_SIZE (8 * sizeof(u32))
#define EXT_CUE_SIZE (16 * sizeof(u32)) #define EXT_CUE_SIZE (16 * sizeof(u32))
#define METRICS_SIZE (18 * sizeof(double)) #define METRICS_SIZE (18 * sizeof(double))
+3
View File
@@ -165,6 +165,9 @@ void df_s390_dumper_read(struct zg_fh *fh, int blk_size,
if (strncmp(dumper->magic, DF_S390_DUMPER_MAGIC_EXT, if (strncmp(dumper->magic, DF_S390_DUMPER_MAGIC_EXT,
DF_S390_DUMPER_MAGIC_SIZE) == 0) DF_S390_DUMPER_MAGIC_SIZE) == 0)
dumper->size = STAGE2_DUMPER_SIZE_ZLIB; dumper->size = STAGE2_DUMPER_SIZE_ZLIB;
else if (strncmp(dumper->magic, DF_S390_DUMPER_MAGIC_MV_EXT,
DF_S390_DUMPER_MAGIC_SIZE) == 0)
dumper->size = STAGE2_DUMPER_SIZE_MV;
else else
dumper->size = STAGE2_DUMPER_SIZE_V2; dumper->size = STAGE2_DUMPER_SIZE_V2;
break; break;
+20 -6
View File
@@ -3,7 +3,7 @@
* *
* Utility classes to read and access FCP configuration information * Utility classes to read and access FCP configuration information
* *
* Copyright IBM Corp. 2008, 2017 * Copyright IBM Corp. 2008, 2024
* *
* s390-tools is free software; you can redistribute it and/or modify * s390-tools is free software; you can redistribute it and/or modify
* it under the terms of the MIT license. See LICENSE for details. * it under the terms of the MIT license. See LICENSE for details.
@@ -32,6 +32,7 @@ ConfigReader::ConfigReader(int *rc, const char *filename)
FILE *fp = NULL; FILE *fp = NULL;
struct device_info new_elem; struct device_info new_elem;
char *line = NULL; char *line = NULL;
char *cfg_pchid = NULL;
size_t line_len; size_t line_len;
int lrc; int lrc;
int line_idx = 1; int line_idx = 1;
@@ -67,7 +68,8 @@ ConfigReader::ConfigReader(int *rc, const char *filename)
new_elem.device = (char*)malloc(lrc + 1); new_elem.device = (char*)malloc(lrc + 1);
new_elem.type = (char*)malloc(lrc + 1); new_elem.type = (char*)malloc(lrc + 1);
new_elem.multipath_device = (char*)malloc(lrc + 1); new_elem.multipath_device = (char*)malloc(lrc + 1);
lrc = sscanf(line, "%x %u:%u:%u:%u %x.%x.%x:%x.%x.%x:%Lx:%Lx %s %u %u:%u %s %u %u:%u %s", cfg_pchid = (char*)malloc(lrc + 1);
lrc = sscanf(line, "%x %u:%u:%u:%u %x.%x.%x:%x.%x.%x:%Lx:%Lx %s %u %u:%u %s %u %u:%u %s %s",
&new_elem.chpid, &new_elem.chpid,
&new_elem.hctl_identifier.host, &new_elem.hctl_identifier.host,
&new_elem.hctl_identifier.channel, &new_elem.hctl_identifier.channel,
@@ -82,10 +84,13 @@ ConfigReader::ConfigReader(int *rc, const char *filename)
&new_elem.mp_minor, new_elem.device, &new_elem.mp_minor, new_elem.device,
&new_elem.mm_internal, &new_elem.mm_internal,
&new_elem.major, &new_elem.minor, &new_elem.major, &new_elem.minor,
new_elem.type); new_elem.type,
cfg_pchid);
*rc = parse_pchid_str(cfg_pchid, &new_elem.pchid);
free(cfg_pchid);
free(line); free(line);
line = NULL; line = NULL;
if (lrc != 22) { if (lrc != 23 || *rc != 0) {
fprintf(stderr, "%s: Could not parse line %d" fprintf(stderr, "%s: Could not parse line %d"
" - configuration file broken?\n", toolname, line_idx); " - configuration file broken?\n", toolname, line_idx);
*rc = -1; *rc = -1;
@@ -465,6 +470,14 @@ __u32 ConfigReader::get_chpid_by_host_id(__u32 host, int *rc) const
return 0; return 0;
} }
__u32 ConfigReader::get_pchid_by_host_id(__u32 host, int *rc) const
{
search_for(hctl_identifier.host, host, pchid);
host_id_not_found_error(host, rc);
return 0;
}
__u32 ConfigReader::get_chpid_by_devno(__u32 d, int *rc) const __u32 ConfigReader::get_chpid_by_devno(__u32 d, int *rc) const
{ {
@@ -861,7 +874,7 @@ void ConfigReader::dump(FILE *fp) const
fprintf(fp, "dumping cfg....\n"); fprintf(fp, "dumping cfg....\n");
for (list<struct device_info>::const_iterator i = m_devices.begin(); for (list<struct device_info>::const_iterator i = m_devices.begin();
i != m_devices.end(); ++i) { i != m_devices.end(); ++i) {
fprintf(fp, "%x %u %u:%u:%u:%u %x.%x.%04x:%x.%x.%04x:%016Lx:%016Lx %s %u:%u %s %u:%u %s\n", fprintf(fp, "%x %u %u:%u:%u:%u %x.%x.%04x:%x.%x.%04x:%016Lx:%016Lx %s %u:%u %s %u:%u %s %x\n",
(*i).chpid, (*i).mm_internal, (*i).chpid, (*i).mm_internal,
(*i).hctl_identifier.host, (*i).hctl_identifier.host,
(*i).hctl_identifier.channel, (*i).hctl_identifier.channel,
@@ -874,7 +887,8 @@ void ConfigReader::dump(FILE *fp) const
((*i).multipath_device ? (*i).multipath_device : "n/a"), (*i).mp_major, ((*i).multipath_device ? (*i).multipath_device : "n/a"), (*i).mp_major,
(*i).mp_minor, (*i).device, (*i).mp_minor, (*i).device,
(*i).major, (*i).minor, (*i).major, (*i).minor,
(*i).type); (*i).type,
(*i).pchid);
} }
} }
+5 -1
View File
@@ -3,7 +3,7 @@
* *
* Utility classes to read and access FCP configuration information * Utility classes to read and access FCP configuration information
* *
* Copyright IBM Corp. 2008, 2017 * Copyright IBM Corp. 2008, 2024
* *
* s390-tools is free software; you can redistribute it and/or modify * s390-tools is free software; you can redistribute it and/or modify
* it under the terms of the MIT license. See LICENSE for details. * it under the terms of the MIT license. See LICENSE for details.
@@ -39,6 +39,8 @@ public:
/// rc is only set on error! /// rc is only set on error!
__u32 get_chpid_by_host_id(__u32 h, int *rc) const; __u32 get_chpid_by_host_id(__u32 h, int *rc) const;
/// rc is only set on error! /// rc is only set on error!
__u32 get_pchid_by_host_id(__u32 h, int *rc) const;
/// rc is only set on error!
__u32 get_chpid_by_devno(__u32 devno, int *rc) const; __u32 get_chpid_by_devno(__u32 devno, int *rc) const;
/// rc is only set on error! /// rc is only set on error!
__u32 get_chpid_by_ident(const struct hctl_ident *ident, int *rc) const; __u32 get_chpid_by_ident(const struct hctl_ident *ident, int *rc) const;
@@ -153,6 +155,8 @@ private:
bool cached_config_exists(const char *fname); bool cached_config_exists(const char *fname);
struct device_info { struct device_info {
// pchid, e.g. 01c0
__u32 pchid;
// chpid, e.g. 43 (hex) // chpid, e.g. 43 (hex)
__u32 chpid; __u32 chpid;
+14
View File
@@ -70,6 +70,7 @@ sub get_sub_ch_data
$c_src = catdir($base_dir, S_DIR1, $host); $c_src = catdir($base_dir, S_DIR1, $host);
my $sub_ch; my $sub_ch;
my $adapter; my $adapter;
my $chp;
if (-l $c_src) { if (-l $c_src) {
$sub_ch = (split("/", readlink($c_src)))[-5]; $sub_ch = (split("/", readlink($c_src)))[-5];
$adapter = (split("/", readlink($c_src)))[-4]; $adapter = (split("/", readlink($c_src)))[-4];
@@ -90,6 +91,13 @@ sub get_sub_ch_data
"Offline"; "Offline";
$c_src = catdir($base_dir, S_DIR2, $sub_ch); $c_src = catdir($base_dir, S_DIR2, $sub_ch);
$sub_ch{$adapter}{chpid} = substr(get_line("chpids"), 0, 2); $sub_ch{$adapter}{chpid} = substr(get_line("chpids"), 0, 2);
$chp = "chp0." . $sub_ch{$adapter}{chpid};
$c_src = catdir($base_dir, S_DIR2, $chp);
if (-e catdir($base_dir, S_DIR2, $chp, "chid")) {
$sub_ch{$adapter}{pchid} = get_line("chid");
} else {
$sub_ch{$adapter}{pchid} = "n/a"
}
} }
} }
@@ -215,6 +223,7 @@ sub mapping_table
push @out_str, $devices{$line}{mm}; push @out_str, $devices{$line}{mm};
} }
push @out_str, $devices{$line}{type}; push @out_str, $devices{$line}{type};
push @out_str, $sub_ch{$devices{$line}{hba_id}}{pchid};
print "@out_str\n"; print "@out_str\n";
} }
} }
@@ -227,6 +236,11 @@ sub adapter_report
next if (@adapters && "@adapters" !~ /\b$a\b/); next if (@adapters && "@adapters" !~ /\b$a\b/);
my @out_str; my @out_str;
push @out_str, "Host: $sub_ch{$a}{host}\n"; push @out_str, "Host: $sub_ch{$a}{host}\n";
if ($sub_ch{$a}{pchid} eq "n/a") {
push @out_str, "PCHID: there is no PCHID entry in data source\n";
} else {
push @out_str, "PCHID: $sub_ch{$a}{pchid}\n";
}
push @out_str, "CHPID: $sub_ch{$a}{chpid}\n"; push @out_str, "CHPID: $sub_ch{$a}{chpid}\n";
push @out_str, "Adapter: $a\n"; push @out_str, "Adapter: $a\n";
push @out_str, "Sub-Ch.: $sub_ch{$a}{sub_ch}\n"; push @out_str, "Sub-Ch.: $sub_ch{$a}{sub_ch}\n";
+49 -16
View File
@@ -3,7 +3,7 @@
* *
* Utility classes to print framsets * Utility classes to print framsets
* *
* Copyright IBM Corp. 2008, 2021 * Copyright IBM Corp. 2008, 2024
* *
* s390-tools is free software; you can redistribute it and/or modify * s390-tools is free software; you can redistribute it and/or modify
* it under the terms of the MIT license. See LICENSE for details. * it under the terms of the MIT license. See LICENSE for details.
@@ -309,6 +309,22 @@ void PhysAdapterPrinter::print_phys_adpt(FILE *fp, __u32 host_id, int *rc)
fprintf(fp, "%3x", chpid); fprintf(fp, "%3x", chpid);
} }
void PhysAdapterPrinter::print_pchid(FILE *fp, __u32 host_id, int *rc)
{
__u32 pchid = m_cfg->get_pchid_by_host_id(host_id, rc);
if (m_csv)
if (pchid != ZIOREP_PCHID_NA)
fprintf(fp, "%x", pchid);
else
fprintf(fp, "%s", "n/a");
else
if (pchid != ZIOREP_PCHID_NA)
fprintf(fp, "%04x", pchid);
else
fprintf(fp, "%s", "n/a ");
}
void PhysAdapterPrinter::print_utilization(FILE *fp, void PhysAdapterPrinter::print_utilization(FILE *fp,
const struct abbrev_stat *stat, const struct abbrev_stat *stat,
@@ -361,10 +377,10 @@ void PhysAdapterPrinter::print_topline(FILE *fp)
if (m_csv) if (m_csv)
fprintf(fp, "timestamp,aggregated,CHPID,adapter min %%," fprintf(fp, "timestamp,aggregated,CHPID,adapter min %%,"
"adapter max %%,adapter avg %%,bus min %%,bus max %%," "adapter max %%,adapter avg %%,bus min %%,bus max %%,"
"bus avg %%,cpu min %%,cpu max %%,cpu avg %%\n"); "bus avg %%,cpu min %%,cpu max %%,cpu avg %%,PCHID\n");
else { else {
fprintf(fp, "CHP|adapter in %%-|--bus in %%---|--cpu in %%---|\n"); fprintf(fp, "PCH |CHP|adapter in %%-|--bus in %%---|--cpu in %%---|\n");
fprintf(fp, " ID min max avg min max avg min max avg\n"); fprintf(fp, " ID ID min max avg min max avg min max avg\n");
} }
} }
@@ -387,6 +403,12 @@ int PhysAdapterPrinter::print_frame(FILE *fp,
// print timestamp for every line in CSV mode // print timestamp for every line in CSV mode
timestamp_printed = true; timestamp_printed = true;
} }
if (!m_csv) {
print_pchid(fp, *i, &lrc);
if (lrc)
return -1;
print_delimiter(fp);
}
print_phys_adpt(fp, *i, &lrc); print_phys_adpt(fp, *i, &lrc);
if (lrc) if (lrc)
return -1; return -1;
@@ -402,6 +424,12 @@ int PhysAdapterPrinter::print_frame(FILE *fp,
print_utilization(fp, &util->stats.cpu, print_utilization(fp, &util->stats.cpu,
util->stats.count, util->stats.count,
util->valid); util->valid);
if (m_csv) {
print_delimiter(fp);
print_pchid(fp, *i, &lrc);
if (lrc)
return -1;
}
fputc('\n', fp); fputc('\n', fp);
} }
@@ -423,10 +451,11 @@ void VirtAdapterPrinter::print_virt_adpt(FILE *fp, __u32 devno,
fprintf(fp, "%x,%x.%x.%04x", fprintf(fp, "%x,%x.%x.%04x",
m_cfg->get_chpid_by_devno(devno, rc), m_cfg->get_chpid_by_devno(devno, rc),
ZIOREP_BUSID_UNPACKED(devno)); ZIOREP_BUSID_UNPACKED(devno));
else else {
fprintf(fp, "%3x/%x.%x.%04x", fprintf(fp, "%x.%x.%04x", ZIOREP_BUSID_UNPACKED(devno));
m_cfg->get_chpid_by_devno(devno, rc), print_delimiter(fp);
ZIOREP_BUSID_UNPACKED(devno)); fprintf(fp, "%3x", m_cfg->get_chpid_by_devno(devno, rc));
}
} }
void VirtAdapterPrinter::print_queue_fill(FILE *fp, void VirtAdapterPrinter::print_queue_fill(FILE *fp,
@@ -520,8 +549,8 @@ void VirtAdapterPrinter::print_topline(FILE *fp)
if (m_csv) if (m_csv)
fprintf(fp, "timestamp,aggregated,CHPID,Bus-ID,qdio utilization max %%,qdio utilization avg %%,queue full,fail erc,throughput read / MS/s,throughput write / MS/s,I/O requests read,I/O requests write\n"); fprintf(fp, "timestamp,aggregated,CHPID,Bus-ID,qdio utilization max %%,qdio utilization avg %%,queue full,fail erc,throughput read / MS/s,throughput write / MS/s,I/O requests read,I/O requests write\n");
else { else {
fprintf(fp, "CHP Bus-ID |qdio util.%%|queu|fail|-thp in MB/s-|I/O reqs-|\n"); fprintf(fp, "Bus-ID |CHP|qdio util.%%|queu|fail|-thp in MB/s-|I/O reqs-|\n");
fprintf(fp, " ID max avg full erc rd wrt rd wrt\n"); fprintf(fp, " ID max avg full erc rd wrt rd wrt\n");
} }
} }
@@ -638,9 +667,9 @@ void TrafficPrinter::print_topline_prefix1(FILE *fp)
switch (m_agg_crit) { switch (m_agg_crit) {
case none: case none:
if (m_csv) if (m_csv)
str = "WWPN,LUN"; str = "DEVBUSID,WWPN,LUN";
else else
str = " WWPN LUN "; str = "DEVBUSID WWPN LUN ";
break; break;
case chpid: case chpid:
if (m_csv) if (m_csv)
@@ -677,7 +706,7 @@ void TrafficPrinter::print_topline_prefix2(FILE *fp)
switch (m_agg_crit) { switch (m_agg_crit) {
case none: case none:
str = " "; str = " ";
break; break;
case chpid: case chpid:
str = " ID"; str = " ID";
@@ -705,7 +734,7 @@ void TrafficPrinter::print_topline_whitespace(FILE *fp)
switch (m_agg_crit) { switch (m_agg_crit) {
case none: case none:
str = " "; str = " ";
break; break;
case chpid: case chpid:
str = " "; str = " ";
@@ -788,12 +817,16 @@ void TrafficPrinter::print_device_mp_mm(FILE *fp, __u32 mp_mm,
void TrafficPrinter::print_device(FILE *fp, __u32 dev, void TrafficPrinter::print_device(FILE *fp, __u32 dev,
const ConfigReader &cfg, int *rc) const ConfigReader &cfg, int *rc)
{ {
__u32 devno = cfg.get_devno_by_mm_internal(dev, rc);
print_device_devno(fp, devno);
if (m_csv) if (m_csv)
fprintf(fp, "0x%016Lx,0x%016Lx", fprintf(fp, ",0x%016Lx,0x%016Lx",
(long long unsigned int)cfg.get_wwpn_by_mm_internal(dev, rc), (long long unsigned int)cfg.get_wwpn_by_mm_internal(dev, rc),
(long long unsigned int)cfg.get_lun_by_mm_internal(dev, rc)); (long long unsigned int)cfg.get_lun_by_mm_internal(dev, rc));
else else
fprintf(fp, "0x%016Lx:0x%016Lx", fprintf(fp, ":0x%016Lx:0x%016Lx",
(long long unsigned int)cfg.get_wwpn_by_mm_internal(dev, rc), (long long unsigned int)cfg.get_wwpn_by_mm_internal(dev, rc),
(long long unsigned int)cfg.get_lun_by_mm_internal(dev, rc)); (long long unsigned int)cfg.get_lun_by_mm_internal(dev, rc));
} }
+3 -1
View File
@@ -3,7 +3,7 @@
* *
* Utility classes to print framsets * Utility classes to print framsets
* *
* Copyright IBM Corp. 2008, 2021 * Copyright IBM Corp. 2008, 2024
* *
* s390-tools is free software; you can redistribute it and/or modify * s390-tools is free software; you can redistribute it and/or modify
* it under the terms of the MIT license. See LICENSE for details. * it under the terms of the MIT license. See LICENSE for details.
@@ -108,6 +108,8 @@ public:
private: private:
void print_phys_adpt(FILE *fp, __u32 host_id, void print_phys_adpt(FILE *fp, __u32 host_id,
int *rc); int *rc);
void print_pchid(FILE *fp, __u32 host_id,
int *rc);
void print_utilization(FILE *fp, const struct abbrev_stat *stat, void print_utilization(FILE *fp, const struct abbrev_stat *stat,
__u64 count, bool valid); __u64 count, bool valid);
+22 -4
View File
@@ -1,4 +1,4 @@
.\" Copyright 2017 IBM Corp. .\" Copyright 2024 IBM Corp.
.\" s390-tools is free software; you can redistribute it and/or modify .\" s390-tools is free software; you can redistribute it and/or modify
.\" it under the terms of the MIT license. See LICENSE for details. .\" it under the terms of the MIT license. See LICENSE for details.
.\" .\"
@@ -58,6 +58,15 @@ Print a summary of the data, then exit.
Only consider the specified physical adapter. Adapters must be specified in hex. Only consider the specified physical adapter. Adapters must be specified in hex.
If multiple adapters should be specified, specify each one separately. If multiple adapters should be specified, specify each one separately.
.TP
.BR "\-f" " or " "\-\-fcp-device"
Print a utilization report for an FCP device. If this option is specified, then
a virtual adapter report is printed, otherwise a physical adapter report with FCP
channel scope is printed. This parameter has no influence on CSV format report printing.
Using the --export-csv option causes both physical and virtual adapter utilization
reports to be printed into two separate CSV-formatted files independent of the
--fcp-device parameter.
.TP .TP
.BR "\-x" " or " "\-\-export-csv" .BR "\-x" " or " "\-\-export-csv"
Write data to file(s) in CSV format. Output filenames will be based on the data filename. Write data to file(s) in CSV format. Output filenames will be based on the data filename.
@@ -127,14 +136,23 @@ give the values for read and write requests respectively.
.SH EXAMPLES .SH EXAMPLES
Print a utilization report using Print a physical adapter utilization report only using
.IR sample.log .IR sample.log
, considering adapters 4e and 4f only. , considering adapters 4e and 4f only.
Only data between 8:57 April 5, 2008, and 17:09 June 21, 2008, should be considered. Only data between 8:57 April 5, 2024, and 17:09 June 21, 2024, should be considered.
Data should be aggregated to 60 second intervals. Data should be aggregated to 60 second intervals.
ziorep_utilization -c 4e -c 4f -i 60 -b "2008-04-05 08:57" -e "2008-06-21 17:09" ziorep_utilization sample.log -c 4e -c 4f -i 60 -b "2024-04-05 08:57" -e "2024-06-21 17:09"
Print virtual adapter report only using sample.log. Consider adapter 4f only. Data should be
aggregated to 3 seconds interval. Print topline after every 2nd frame.
ziorep_utilization sample.log -c 4f -i 3 -t 2 --fcp-device
Print both physical and virtual adapter reports using sample.log in two separate CSV-formatted
files with the default interval. Considering adapter 4f only. Print topline after every 2nd frame.
ziorep_utilization sample.log -c 4f -t 2 --export-csv
.SH "SEE ALSO" .SH "SEE ALSO"
.BR ziorep_config (8), .BR ziorep_config (8),
+52 -22
View File
@@ -3,7 +3,7 @@
* *
* Utilization report program * Utilization report program
* *
* Copyright IBM Corp. 2008, 2017 * Copyright IBM Corp. 2008, 2024
* *
* s390-tools is free software; you can redistribute it and/or modify * s390-tools is free software; you can redistribute it and/or modify
* it under the terms of the MIT license. See LICENSE for details. * it under the terms of the MIT license. See LICENSE for details.
@@ -49,6 +49,7 @@ struct options {
char* filename; char* filename;
bool print_summary; bool print_summary;
bool csv_export; bool csv_export;
bool fcp_device;
}; };
@@ -61,6 +62,7 @@ static void init_opts(struct options *opts)
opts->filename = NULL; opts->filename = NULL;
opts->print_summary = false; opts->print_summary = false;
opts->csv_export = false; opts->csv_export = false;
opts->fcp_device = false;
} }
@@ -85,6 +87,8 @@ static const char help_text[] =
"-s, --summary Show a summary of the data.\n" "-s, --summary Show a summary of the data.\n"
"-c, --chpid <chpid> Select physical adapter in hex.\n" "-c, --chpid <chpid> Select physical adapter in hex.\n"
" E.g. '-c 32a'\n" " E.g. '-c 32a'\n"
"-f, --fcp-device Print utilization report for FCP device scope.\n"
" Otherwise for FCP channel scope.\n"
"-x, --export-csv Export data to files in CSV format.\n" "-x, --export-csv Export data to files in CSV format.\n"
"-t, --topline <num> Repeat topline after every 'num' frames.\n" "-t, --topline <num> Repeat topline after every 'num' frames.\n"
" 0 for no repeat (default).\n"; " 0 for no repeat (default).\n";
@@ -99,7 +103,7 @@ static void print_help()
static void print_version() static void print_version()
{ {
printf("%s: Utilization report generator version %s\n" printf("%s: Utilization report generator version %s\n"
"Copyright IBM Corp. 2008, 2017\n", toolname, RELEASE_STRING); "Copyright IBM Corp. 2008, 2024\n", toolname, RELEASE_STRING);
} }
@@ -121,6 +125,7 @@ static int parse_params(int argc, char **argv, struct options *opts)
{ "chpid", required_argument, NULL, 'c'}, { "chpid", required_argument, NULL, 'c'},
{ "export-csv", no_argument, NULL, 'x'}, { "export-csv", no_argument, NULL, 'x'},
{ "topline", required_argument, NULL, 't'}, { "topline", required_argument, NULL, 't'},
{ "fcp-device", no_argument, NULL, 'f'},
{ 0, 0, 0, 0 } { 0, 0, 0, 0 }
}; };
@@ -130,7 +135,7 @@ static int parse_params(int argc, char **argv, struct options *opts)
} }
assert(sizeof(long long int) == sizeof(__u64)); assert(sizeof(long long int) == sizeof(__u64));
while ((c = getopt_long(argc, argv, "b:e:i:c:t:xshvV", while ((c = getopt_long(argc, argv, "b:e:i:c:t:xshvVf",
long_options, &index)) != EOF) { long_options, &index)) != EOF) {
switch (c) { switch (c) {
case 'V': case 'V':
@@ -183,6 +188,9 @@ static int parse_params(int argc, char **argv, struct options *opts)
if (parse_topline_arg(optarg, &opts->topline)) if (parse_topline_arg(optarg, &opts->topline))
return -1; return -1;
break; break;
case 'f':
opts->fcp_device = true;
break;
default: default:
fprintf(stderr, "%s: Try '%s --help' for" fprintf(stderr, "%s: Try '%s --help' for"
" more information.\n", toolname, toolname); " more information.\n", toolname, toolname);
@@ -297,43 +305,65 @@ static int print_reports(struct options *opts, ConfigReader &cfg)
type_flt.push_back(utilization); type_flt.push_back(utilization);
// physical adapter report
if (opts->csv_export) { if (opts->csv_export) {
fp = open_csv_output_file(opts->filename, fp = open_csv_output_file(opts->filename,
"_util_phys_adpt.csv", &rc); "_util_phys_adpt.csv", &rc);
if (!fp) if (!fp)
goto out; goto out;
} } else {
else
fp = stdout; fp = stdout;
if ( (rc = print_report(fp, opts->begin, opts->end,
opts->interval, opts->filename, opts->topline,
&type_flt, dev_filt, noop_col,
physPrnt)) < 0 ) {
rc = -3;
goto out1;
} }
if (rc == 0) if (!opts->fcp_device || opts->csv_export) {
fprintf(stderr, "%s: No eligible data found.\n", toolname); rc = print_report(fp, opts->begin, opts->end,
opts->interval, opts->filename, opts->topline,
&type_flt, dev_filt, noop_col, physPrnt);
if (rc < 0) {
rc = -3;
goto out1;
} else if (rc == 0) {
fprintf(stderr, "%s: No eligible data found.\n",
toolname);
}
if (!opts->csv_export)
fprintf(stderr,
"%s: The FCP channel utilization report was "
"printed. To print the FCP device utilization "
"report, use the -f/--fcp-device option.\n",
toolname);
}
// virtual adapter report
if (opts->csv_export) { if (opts->csv_export) {
fclose(fp); fclose(fp);
fp = open_csv_output_file(opts->filename, fp = open_csv_output_file(opts->filename,
"_util_virt_adpt.csv", &rc); "_util_virt_adpt.csv", &rc);
if (!fp) if (!fp)
goto out; goto out;
} } else {
else {
fp = stdout; fp = stdout;
fputc('\n', fp);
} }
if (print_report(fp, opts->begin, opts->end, opts->interval, if (opts->fcp_device || opts->csv_export) {
opts->filename, opts->topline, NULL, dev_filt, rc = print_report(fp, opts->begin, opts->end, opts->interval,
*col, virtPrnt)) { opts->filename, opts->topline, NULL, dev_filt,
rc = -4; *col, virtPrnt);
goto out1; if (rc < 0) {
rc = -4;
goto out1;
} else if (rc == 0) {
fprintf(stderr, "%s: No eligible data found.\n",
toolname);
}
if (!opts->csv_export)
fprintf(stderr,
"%s: The FCP device utilization report was "
"printed. To print the FCP channel utilization "
"report, omit the -f/--fcp-device option.\n",
toolname);
} }
out1: out1:
+31 -1
View File
@@ -3,7 +3,7 @@
* *
* Utility functions * Utility functions
* *
* Copyright IBM Corp. 2008, 2017 * Copyright IBM Corp. 2008, 2024
* *
* s390-tools is free software; you can redistribute it and/or modify * s390-tools is free software; you can redistribute it and/or modify
* it under the terms of the MIT license. See LICENSE for details. * it under the terms of the MIT license. See LICENSE for details.
@@ -552,6 +552,36 @@ FILE* open_csv_output_file(const char *filename, const char *extension,
return fp; return fp;
} }
/**
* parse_pchid_str - parse PCHID from string to 32-bit number
* @pchid_str: PCHID string
* @pchid: 32-bit PCHID number
*
* Reads PCHID string, checks for corner cases, converts to 32-bit
* PCHID number, checks for errors, writes resulted PCHID to specified
* location.
*/
int parse_pchid_str(const char *const pchid_str, __u32 *const pchid)
{
unsigned long parsed;
char *end;
if (strcmp(pchid_str, "n/a") == 0) {
parsed = ZIOREP_PCHID_NA;
goto out;
}
parsed = strtoul(pchid_str, &end, 16);
if (parsed > 0xffff) {
fprintf(stderr, "%s: PCHID %s exceeds maximum possible value for a PCHID.\n",
toolname, pchid_str);
return -1;
} else if (parsed == 0 && end == &pchid_str[0]) {
fprintf(stderr, "%s: PCHID %s could not be converted.\n", toolname, pchid_str);
return -1;
}
out:
*pchid = (__u32)parsed;
return 0;
}
+7 -1
View File
@@ -3,7 +3,7 @@
* *
* Utility functions * Utility functions
* *
* Copyright IBM Corp. 2008, 2017 * Copyright IBM Corp. 2008, 2024
* *
* s390-tools is free software; you can redistribute it and/or modify * s390-tools is free software; you can redistribute it and/or modify
* it under the terms of the MIT license. See LICENSE for details. * it under the terms of the MIT license. See LICENSE for details.
@@ -24,6 +24,8 @@ extern "C" {
#include <linux/types.h> #include <linux/types.h>
#define ZIOREP_PCHID_NA 65536
/** /**
* Parse date provided in 'str' and store as seconds since 1970 in 'tgt'. */ * Parse date provided in 'str' and store as seconds since 1970 in 'tgt'. */
int get_datetime_val(const char *str, __u64 *tgt); int get_datetime_val(const char *str, __u64 *tgt);
@@ -82,6 +84,10 @@ int parse_topline_arg(char *str, __u64 *arg);
FILE* open_csv_output_file(const char *filename, const char *extension, FILE* open_csv_output_file(const char *filename, const char *extension,
int *rc); int *rc);
/**
* Parse PCHID from string to 32-bit number
*/
int parse_pchid_str(const char *const pchid_str, __u32 *const pchid);
/** /**
* accessors for internal representation of device and subchannel bus-IDs * accessors for internal representation of device and subchannel bus-IDs
+1 -1
View File
@@ -57,7 +57,7 @@ eckd2dump_sv.lds: eckd2dump.lds.S
$(CPP) -Wp,-MD,.$@.d,-MT,$@ $(INCLUDE_PARMS) -D STAGE2DUMP_TAIL_ADDRESS=0x9ff0 \ $(CPP) -Wp,-MD,.$@.d,-MT,$@ $(INCLUDE_PARMS) -D STAGE2DUMP_TAIL_ADDRESS=0x9ff0 \
-P -C -o $@ $< -P -C -o $@ $<
eckd2dump_mv.lds: eckd2dump.lds.S eckd2dump_mv.lds: eckd2dump.lds.S
$(CPP) -Wp,-MD,.$@.d,-MT,$@ $(INCLUDE_PARMS) -D STAGE2DUMP_TAIL_ADDRESS=0x4ff0 \ $(CPP) -Wp,-MD,.$@.d,-MT,$@ $(INCLUDE_PARMS) -D STAGE2DUMP_TAIL_ADDRESS=0x5ff0 \
-D ECKD2DUMP_MV -P -C -o $@ $< -D ECKD2DUMP_MV -P -C -o $@ $<
stage2.lds: stage2.lds.S stage2.lds: stage2.lds.S
$(CPP) -Wp,-MD,.$@.d,-MT,$@ $(INCLUDE_PARMS) -D STAGE2DUMP_TAIL_ADDRESS=0x4ff0 \ $(CPP) -Wp,-MD,.$@.d,-MT,$@ $(INCLUDE_PARMS) -D STAGE2DUMP_TAIL_ADDRESS=0x4ff0 \
+2 -2
View File
@@ -17,8 +17,8 @@
* *
* 0x78 Stage 2 description parameters * 0x78 Stage 2 description parameters
* 0x2018 Stage 2 entry point * 0x2018 Stage 2 entry point
* 0x4ff0 Stage 2 multi-volume dump parameters (eckd2dump_mv) * 0x5ff0 Stage 2 multi-volume dump parameters (eckd2dump_mv)
* 0x5000 Multi-volume dump parameters table (eckd2dump_mv) * 0x6000 Multi-volume dump parameters table (eckd2dump_mv)
* 0x9ff0 Stage 2 single volume dump parameters (eckd2dump_sv) * 0x9ff0 Stage 2 single volume dump parameters (eckd2dump_sv)
*/ */
+1 -1
View File
@@ -22,7 +22,7 @@
/* /*
* Magic number at start of dump record * Magic number at start of dump record
*/ */
const uint64_t __section(.stage2.head) magic = 0x584d554c54363401ULL; /* XMULT64, version 1 */ const uint64_t __section(.stage2.head) magic = 0x584d554c54363402ULL; /* XMULT64, version 2 */
/* /*
* Parameter format for ECKD MV dumper (13 bytes): * Parameter format for ECKD MV dumper (13 bytes):
+3 -3
View File
@@ -48,9 +48,9 @@ static inline void __noreturn start_kernel(void)
" diag %[code],%[code],0x308\n" " diag %[code],%[code],0x308\n"
".no_diag308:\n" ".no_diag308:\n"
" sam31\n" " sam31\n"
" sr %r1,%r1\n" " sr %%r1,%%r1\n"
" sr %r2,%r2\n" " sr %%r2,%%r2\n"
" sigp %r1,%r2,%[order]\n" " sigp %%r1,%%r2,%[order]\n"
" lpsw 0\n" " lpsw 0\n"
: [addr] "=&d" (addr), : [addr] "=&d" (addr),
[code] "+&d" (code) [code] "+&d" (code)
+4 -3
View File
@@ -38,9 +38,10 @@ install: all
$(INSTALL) -d -m 755 $(DESTDIR)$(BINDIR) $(INSTALL) -d -m 755 $(DESTDIR)$(BINDIR)
$(INSTALL) -c zipl $(DESTDIR)$(BINDIR) $(INSTALL) -c zipl $(DESTDIR)$(BINDIR)
$(INSTALL) -c zipl-editenv $(DESTDIR)$(BINDIR) $(INSTALL) -c zipl-editenv $(DESTDIR)$(BINDIR)
$(INSTALL) -m 755 $(zipl_helpers) $(chreipl_helpers) \ $(INSTALL) -m 755 zipl_helper.device-mapper $(DESTDIR)$(TOOLS_LIBDIR)
$(DESTDIR)$(TOOLS_LIBDIR) ln -f -s zipl_helper.device-mapper $(DESTDIR)$(TOOLS_LIBDIR)/zipl_helper.md
$(CP) --no-dereference $(chreipl_helpers) $(DESTDIR)$(TOOLS_LIBDIR) ln -f -s zipl_helper.device-mapper $(DESTDIR)$(TOOLS_LIBDIR)/chreipl_helper.md
ln -f -s zipl_helper.device-mapper $(DESTDIR)$(TOOLS_LIBDIR)/chreipl_helper.device-mapper
clean: clean:
rm -f *.o $(zipl_helpers) $(chreipl_helpers) zipl zipl-editenv rm -f *.o $(zipl_helpers) $(chreipl_helpers) zipl zipl-editenv
+5 -4
View File
@@ -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 * Try to auto-detect parameters of the disk which the file locates on
* and compare found device-ID with DISK. * and compare found device-ID with DISK.
* Return 0, if auto-detection succeeded, and it is proven that the * Return 0, if auto-detection succeeded, and it is proven that the
* file does NOT locate on DISK. Otherwise, return 1. * 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 * 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"); "Warning: Preparing a logical device for boot might fail\n");
return 1; return 1;
} }
if (info->device != disk) { if (info->basedisks[0] != where->basedisks[0]) {
disk_free_info(info); disk_free_info(info);
return 0; return 0;
} }
@@ -378,7 +379,7 @@ static int add_component_file_range(struct install_set *bis,
return -1; return -1;
} }
} else { } 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"); error_reason("File is not on target device");
return -1; return -1;
} }
+9 -2
View File
@@ -1306,13 +1306,20 @@ static int complete_physical_device(struct physical_device *pd, dev_t *base_dev)
*base_dev = base_entry->dev.dev; *base_dev = base_entry->dev.dev;
} else { } else {
/* /*
* In this case base device is the uppermost logical * In this case base device is the uppermost physical
* device which provides access to boot sectors * device which provides access to boot sectors
*/ */
base_entry = find_base_entry(pd->dmpath, dc->bootsectors); base_entry = find_base_entry(pd->dmpath, dc->bootsectors);
if (!base_entry) if (!base_entry)
return -1; return -1;
*base_dev = base_entry->dev.dev; if (target_get_start(base_entry->target) == 0) {
/* base device is a dm device */
*base_dev = base_entry->dev.dev;
} else {
/* base device is a non-dm device */
*base_dev =
first_device_by_target_data(base_entry->target);
}
} }
/* Check for valid offset of filesystem */ /* Check for valid offset of filesystem */
if ((pd->offset % (dc->blocksize / SECTOR_SIZE)) != 0) { if ((pd->offset % (dc->blocksize / SECTOR_SIZE)) != 0) {
+3 -1
View File
@@ -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 \ zkey-cryptsetup.o: check-dep-zkey-cryptsetup zkey-cryptsetup.c pkey.h cca.h \
ep11.h misc.h utils.h ep11.h misc.h utils.h
kms.o: kms.c kms.h kms-plugin.h utils.h pkey.h kms.o: kms.c kms.h kms-plugin.h utils.h pkey.h
pvsecrets.o: pvsecrets.h
zkey: LDLIBS = -ldl -lcrypto 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 $@ $(LINK) $(ALL_LDFLAGS) $^ $(LDLIBS) -o $@
zkey-cryptsetup: LDLIBS = -ldl -lcryptsetup -ljson-c -lcrypto zkey-cryptsetup: LDLIBS = -ldl -lcryptsetup -ljson-c -lcrypto
+147 -60
View File
@@ -3,7 +3,7 @@
* *
* Keystore handling functions * 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 * s390-tools is free software; you can redistribute it and/or modify
* it under the terms of the MIT license. See LICENSE for details. * 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; return 1;
if (strcasecmp(key_type, KEY_TYPE_EP11_AES) == 0) if (strcasecmp(key_type, KEY_TYPE_EP11_AES) == 0)
return 1; return 1;
if (strcasecmp(key_type, KEY_TYPE_PVSECRET_AES) == 0)
return 1;
return 0; return 0;
} }
@@ -2007,6 +2009,12 @@ int keystore_generate_key(struct keystore *keystore, const char *name,
return -EINVAL; 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); rc = _keystore_get_key_filenames(keystore, name, &file_names);
if (rc != 0) if (rc != 0)
goto out_free_key_filenames; goto out_free_key_filenames;
@@ -2257,9 +2265,11 @@ out_free_key_filenames:
} }
/** /**
* 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] 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] name the name of the key
* @param[in] description textual description of the key (optional, can be NULL) * @param[in] description textual description of the key (optional, can be NULL)
* @param[in] volumes a comma separated list of volumes associated with this * @param[in] volumes a comma separated list of volumes associated with this
@@ -2272,7 +2282,6 @@ out_free_key_filenames:
* of two and in range 512 - 4096 bytes. 0 means that * of two and in range 512 - 4096 bytes. 0 means that
* the sector size is not specified and the system * the sector size is not specified and the system
* default is used. * 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] volume_type the type of volume
* @param[in] gen_passphrase if true, generate a (dummy) passphrase for LUKS2 * @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 * @param[in] passphrase_file the file name of a file containing a passphrase
@@ -2281,25 +2290,23 @@ out_free_key_filenames:
* *
* @returns 0 for success or a negative errno in case of an error * @returns 0 for success or a negative errno in case of an error
*/ */
int keystore_import_key(struct keystore *keystore, const char *name, int keystore_import(struct keystore *keystore, unsigned char *secure_key,
const char *description, const char *volumes, size_t secure_key_size, const char *name,
const char *apqns, bool noapqncheck, size_t sector_size, const char *description, const char *volumes,
const char *import_file, const char *volume_type, const char *apqns, bool noapqncheck, size_t sector_size,
bool gen_passphrase, const char *passphrase_file, const char *volume_type, bool gen_passphrase,
struct ext_lib *lib) const char *passphrase_file, struct ext_lib *lib)
{ {
struct key_filenames file_names = { 0 }; struct key_filenames file_names = { 0 };
struct properties *key_props = NULL; struct properties *key_props = NULL;
size_t secure_key_size;
const char *key_type; const char *key_type;
u8 mkvp[MKVP_LENGTH]; u8 mkvp[MKVP_LENGTH];
int selected = 1; int selected = 1;
u8 *secure_key;
int rc; int rc;
util_assert(keystore != NULL, "Internal error: keystore is NULL"); util_assert(keystore != NULL, "Internal error: keystore is NULL");
util_assert(name != NULL, "Internal error: name 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); rc = _keystore_get_key_filenames(keystore, name, &file_names);
if (rc != 0) if (rc != 0)
@@ -2309,27 +2316,29 @@ int keystore_import_key(struct keystore *keystore, const char *name,
if (rc != 0) if (rc != 0)
goto out_free_key_filenames; 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); key_type = get_key_type(secure_key, secure_key_size);
if (key_type == NULL) { if (key_type == NULL) {
warnx("Key '%s' is not a valid secure key", name); warnx("Key '%s' is not a valid secure key", name);
free(secure_key);
rc = -EINVAL; rc = -EINVAL;
goto out_free_key_filenames; 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, rc = get_master_key_verification_pattern(secure_key, secure_key_size,
mkvp, keystore->verbose); mkvp, keystore->verbose);
if (rc != 0) { if (rc != 0) {
warnx("Failed to get the master key verification pattern: %s", warnx("Failed to get the master key verification pattern: %s",
strerror(-rc)); strerror(-rc));
goto out_free_key; goto out_free_props;
} }
rc = cross_check_apqns(apqns, mkvp, rc = cross_check_apqns(apqns, mkvp,
@@ -2338,17 +2347,17 @@ int keystore_import_key(struct keystore *keystore, const char *name,
get_card_type_for_keytype(key_type), get_card_type_for_keytype(key_type),
true, keystore->verbose); true, keystore->verbose);
if (rc == -EINVAL) if (rc == -EINVAL)
goto out_free_key; goto out_free_props;
if (rc != 0 && rc != -ENOTSUP && noapqncheck == 0) { if (rc != 0 && rc != -ENOTSUP && noapqncheck == 0) {
warnx("Your master key setup is improper"); 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 (is_cca_aes_cipher_key(secure_key, secure_key_size)) {
if (lib->cca->lib_csulcca == NULL) { if (lib->cca->lib_csulcca == NULL) {
rc = load_cca_library(lib->cca, keystore->verbose); rc = load_cca_library(lib->cca, keystore->verbose);
if (rc != 0) if (rc != 0)
goto out_free_key; goto out_free_props;
} }
rc = select_cca_adapter_by_mkvp(lib->cca, mkvp, apqns, rc = select_cca_adapter_by_mkvp(lib->cca, mkvp, apqns,
@@ -2363,7 +2372,7 @@ int keystore_import_key(struct keystore *keystore, const char *name,
warnx("No APQN found that is suitable for " warnx("No APQN found that is suitable for "
"working with the secure AES key '%s'", name); "working with the secure AES key '%s'", name);
rc = 0; rc = 0;
goto out_free_key; goto out_free_props;
} }
rc = restrict_key_export(lib->cca, secure_key, secure_key_size, rc = restrict_key_export(lib->cca, secure_key, secure_key_size,
@@ -2373,7 +2382,7 @@ int keystore_import_key(struct keystore *keystore, const char *name,
"key: %s", strerror(-rc)); "key: %s", strerror(-rc));
if (!selected) if (!selected)
print_msg_for_cca_envvars("secure AES key"); 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); rc = check_aes_cipher_key(secure_key, secure_key_size);
@@ -2384,15 +2393,14 @@ int keystore_import_key(struct keystore *keystore, const char *name,
if (!prompt_for_yes(keystore->verbose)) { if (!prompt_for_yes(keystore->verbose)) {
warnx("Operation aborted"); warnx("Operation aborted");
rc = -ECANCELED; rc = -ECANCELED;
goto out_free_key; goto out_free_props;
} }
} }
} }
write_key:
rc = write_secure_key(file_names.skey_filename, secure_key, rc = write_secure_key(file_names.skey_filename, secure_key,
secure_key_size, keystore->verbose); secure_key_size, keystore->verbose);
free(secure_key);
secure_key = NULL;
if (rc != 0) if (rc != 0)
goto out_free_props; goto out_free_props;
@@ -2412,9 +2420,6 @@ int keystore_import_key(struct keystore *keystore, const char *name,
"Successfully imported a secure key in '%s' and key info in '%s'", "Successfully imported a secure key in '%s' and key info in '%s'",
file_names.skey_filename, file_names.info_filename); file_names.skey_filename, file_names.info_filename);
out_free_key:
if (secure_key != NULL)
free(secure_key);
out_free_props: out_free_props:
if (key_props != NULL) if (key_props != NULL)
properties_free(key_props); properties_free(key_props);
@@ -2429,6 +2434,59 @@ out_free_key_filenames:
return rc; 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. * Changes properties of a key in the keystore.
@@ -2483,9 +2541,9 @@ int keystore_change_key(struct keystore *keystore, const char *name,
const char *null_ptr = NULL; const char *null_ptr = NULL;
char *upd_volumes = NULL; char *upd_volumes = NULL;
size_t secure_key_size; size_t secure_key_size;
u8 *secure_key = NULL;
u8 mkvp[MKVP_LENGTH]; u8 mkvp[MKVP_LENGTH];
char sect_size[30]; char sect_size[30];
u8 *secure_key;
bool kms_bound; bool kms_bound;
int rc; int rc;
@@ -2537,13 +2595,6 @@ int keystore_change_key(struct keystore *keystore, const char *name,
goto out; 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 = read_secure_key(file_names.skey_filename,
&secure_key_size, &secure_key_size,
keystore->verbose); keystore->verbose);
@@ -2552,11 +2603,24 @@ int keystore_change_key(struct keystore *keystore, const char *name,
goto out; 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, rc = get_master_key_verification_pattern(secure_key,
secure_key_size, secure_key_size,
mkvp, mkvp,
keystore->verbose); keystore->verbose);
free(secure_key);
if (rc) if (rc)
goto out; goto out;
@@ -2690,6 +2754,8 @@ out:
free(upd_volumes); free(upd_volumes);
if (upd_volume_type != NULL) if (upd_volume_type != NULL)
free(upd_volume_type); free(upd_volume_type);
if (secure_key != NULL)
free(secure_key);
if (rc != 0) if (rc != 0)
pr_verbose(keystore, "Failed to change key '%s': %s", pr_verbose(keystore, "Failed to change key '%s': %s",
@@ -2989,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_XTS, is_xts ? "Yes" : "No");
util_rec_set(rec, REC_KEY_TYPE, key_type); util_rec_set(rec, REC_KEY_TYPE, key_type);
if (validation) { if (validation) {
if (valid) if (mkvp != NULL) {
util_rec_set(rec, REC_MASTERKEY, if (valid)
"%s master key (MKVP: %s)", util_rec_set(rec, REC_MASTERKEY,
is_old_mk ? "OLD" : "CURRENT", "%s master key (MKVP: %s)",
printable_mkvp( is_old_mk ? "OLD" : "CURRENT",
get_card_type_for_keytype(key_type), printable_mkvp(
mkvp)); get_card_type_for_keytype(
else key_type),
util_rec_set(rec, REC_MASTERKEY, mkvp));
"(unknown, MKVP: %s)", else
printable_mkvp( util_rec_set(rec, REC_MASTERKEY,
get_card_type_for_keytype(key_type), "(unknown, MKVP: %s)",
mkvp)); printable_mkvp(
get_card_type_for_keytype(
key_type),
mkvp));
} else {
util_rec_set(rec, REC_MASTERKEY, "(none)");
}
} }
if (volumes_argz != NULL) if (volumes_argz != NULL)
util_rec_set_argz(rec, REC_VOLUMES, volumes_argz, util_rec_set_argz(rec, REC_VOLUMES, volumes_argz,
@@ -3228,17 +3300,22 @@ static int _keystore_process_validate(struct keystore *keystore,
valid = 1; valid = 1;
} }
rc = get_master_key_verification_pattern(secure_key, secure_key_size, if (is_secure_key(secure_key, secure_key_size)) {
mkvp, keystore->verbose); rc = get_master_key_verification_pattern(secure_key,
if (rc != 0) secure_key_size,
goto out; mkvp,
keystore->verbose);
if (rc != 0)
goto out;
}
_keystore_print_record(info->rec, name, properties, 1, _keystore_print_record(info->rec, name, properties, 1,
file_names->skey_filename, secure_key_size, file_names->skey_filename, secure_key_size,
is_xts_key(secure_key, secure_key_size), is_xts_key(secure_key, secure_key_size),
clear_key_bitsize, valid, is_old_mk, clear_key_bitsize, valid, is_old_mk,
_keystore_reencipher_key_exists(file_names), _keystore_reencipher_key_exists(file_names),
mkvp, is_secure_key(secure_key, secure_key_size) ?
mkvp : NULL,
_keystore_passphrase_file_exists(file_names) ? _keystore_passphrase_file_exists(file_names) ?
file_names->pass_filename : NULL); file_names->pass_filename : NULL);
@@ -3250,7 +3327,8 @@ static int _keystore_process_validate(struct keystore *keystore,
"master key\n", 0); "master key\n", 0);
info->num_warnings++; 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, if (_keystore_display_apqn_status(keystore, properties,
mkvp) != 0) mkvp) != 0)
info->num_warnings++; info->num_warnings++;
@@ -3501,6 +3579,15 @@ static int _keystore_process_reencipher(struct keystore *keystore,
goto out; 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); apqns = properties_get(properties, PROP_NAME_APQNS);
if (apqns != NULL) if (apqns != NULL)
apqn_list = str_list_split(apqns); apqn_list = str_list_split(apqns);
+8 -1
View File
@@ -3,7 +3,7 @@
* *
* Keystore handling functions * 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 * s390-tools is free software; you can redistribute it and/or modify
* it under the terms of the MIT license. See LICENSE for details. * 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, struct kms_option *kms_options,
size_t num_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, int keystore_import_key(struct keystore *keystore, const char *name,
const char *description, const char *volumes, const char *description, const char *volumes,
const char *apqns, bool noapqncheck, size_t sector_size, const char *apqns, bool noapqncheck, size_t sector_size,

Some files were not shown because too many files have changed in this diff Show More