rust: Add tool to manage UV-secrets

Add `pvsecret` a tool to create, add, list, and delete Ultravisor
secrets. `pvsecret` uses the functionality from the pv-crate
to provide an command line tool to manage the secrets.

Add a new target group PV_TARGETS in rust/Makefile that additionally
requires openssl and libcurl as pv with the feature "request" uses
openssl and libcurl fearures.

Acked-by: Jan Höppner <hoeppner@linux.ibm.com>
Acked-by: Marc Hartmayer <mhartmay@linux.ibm.com>
Signed-off-by: Steffen Eiden <seiden@linux.ibm.com>
[hoeppner@linux.ibm.com: Adapt man pages and help output]
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
This commit is contained in:
Steffen Eiden
2023-05-04 14:28:01 +02:00
committed by Jan Höppner
parent c6f621d0dc
commit dd82c26f87
21 changed files with 2338 additions and 1 deletions

View File

@@ -9,6 +9,7 @@ Release history for s390-tools (MIT version)
Add new tools / libraries:
- rust/pv: Library for pv tools written in rust
- rust/pvsecret: Tool to manage UV-secrets
Changes of existing tools:
- genprotimg: add support for add-secret requests

View File

@@ -1,10 +1,14 @@
include ../common.mak
SHELL := /bin/bash
HAVE_CARGO ?= 1
HAVE_OPENSSL ?= 1
HAVE_LIBCURL ?= 1
INSTALL_TARGETS := skip-build
BUILD_TARGETS := skip-build
PV_BUILD_TARGETS := skip-pv-build
CARGO_TARGETS :=
PV_TARGETS :=
CARGO_TEST_TARGETS :=
ifneq (${HAVE_CARGO},0)
@@ -14,7 +18,17 @@ ifneq (${HAVE_CARGO},0)
INSTALL_TARGETS := install-rust-tools install-man
CARGO_TEST_TARGETS = $(addsuffix, _test, $(CARGO_TARGETS))
endif
ifneq (${HAVE_OPENSSL},0)
ifneq (${HAVE_LIBCURL},0)
PV_TARGETS := pvsecret
PV_BUILD_TARGETS = $(PV_TARGETS)
CARGO_TEST_TARGETS += $(addsuffix, _test, $(PV_TARGETS)) pv
endif #LIBCURL
endif #OPENSSL
endif #CARGO
BUILD_TARGETS += $(PV_BUILD_TARGETS)
# build release targets by default
ifeq ("${D}","0")
@@ -35,9 +49,16 @@ $(CARGO_TEST_TARGETS): .check-cargo .no-cross-compile
$(CARGO_TEST) --manifest-path=$@/Cargo.toml --all-features $(CARGOFLAGS)
.PHONY: $(CARGO_TEST_TARGETS)
$(PV_TARGETS): .check-cargo .no-cross-compile .check-dep-pvtools
$(CARGO_BUILD) --manifest-path=$@/Cargo.toml $(ALL_CARGOFLAGS)
.PHONY: $(PV_TARGETS)
skip-build:
echo " SKIP rust-tools due to unresolved dependencies"
skip-pv-build:
echo " SKIP rust-pv-tools due to unresolved dependencies"
all: $(BUILD_TARGETS)
install: $(INSTALL_TARGETS)
@@ -47,6 +68,8 @@ print-rust-targets:
clean:
$(foreach target,$(CARGO_TARGETS),\
$(CARGO_CLEAN) --manifest-path=$(target)/Cargo.toml ${ALL_CARGOFLAGS} ;)
$(foreach target,$(PV_TARGETS),\
$(CARGO_CLEAN) --manifest-path=$(target)/Cargo.toml ${CARGOFLAGS} ;)
$(RM) -- .check-dep-pvtools .detect-openssl.dep.c .check-cargo
rust-test: .check-cargo .no-cross-compile
@@ -57,10 +80,14 @@ install-rust-tools: $(BUILD_TARGETS)
$(INSTALL) -d -m 755 $(DESTDIR)$(USRBINDIR)
$(foreach target,$(CARGO_TARGETS),\
$(INSTALL) $(target)/target/release/$(target) $(DESTDIR)$(USRBINDIR);)
$(foreach target,$(PV_TARGETS),\
$(INSTALL) $(target)/target/release/$(target) $(DESTDIR)$(USRBINDIR);)
install-man:
$(foreach target,$(CARGO_TARGETS),\
$(INSTALL) -m 644 $(target)/man/*.1 -t $(DESTDIR)$(MANDIR)/man1;)
$(foreach target,$(PV_TARGETS),\
$(INSTALL) -m 644 $(target)/man/*.1 -t $(DESTDIR)$(MANDIR)/man1;)
.PHONY: all install clean skip-build install-rust-tools print-rust-targets install-man rust-test
@@ -79,3 +106,27 @@ ifneq ($(HOST_ARCH), $(BUILD_ARCH))
$(error Cross compiling is not supported for rust code. Specify HAVE_CARGO=0 to disable rust compilation)
endif
.PHONY: .no-cross-compile
.detect-openssl.dep.c:
echo "#include <openssl/evp.h>" > $@
echo "#if OPENSSL_VERSION_NUMBER < 0x10101000L" >> $@
echo " #error openssl version 1.1.1 is required" >> $@
echo "#endif" >> $@
echo "static void __attribute__((unused)) test(void) {" >> $@
echo " EVP_MD_CTX *ctx = EVP_MD_CTX_new();" >> $@
echo " EVP_MD_CTX_free(ctx);" >> $@
echo "}" >> $@
.check-dep-pvtools: .detect-openssl.dep.c
$(call check_dep, \
"$(BIN_PROGRAM)", \
$^, \
"openssl-devel / libssl-dev version >= 1.1.1", \
"HAVE_OPENSSL=0", \
"-I.")
$(call check_dep, \
"$(BIN_PROGRAM)", \
"curl/curl.h", \
"libcurl-devel", \
"HAVE_LIBCURL=0")
touch $@

1
rust/pvsecret/.gitignore vendored Normal file
View File

@@ -0,0 +1 @@
!Cargo.lock

728
rust/pvsecret/Cargo.lock generated Normal file
View File

@@ -0,0 +1,728 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
version = 3
[[package]]
name = "anstream"
version = "0.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0ca84f3628370c59db74ee214b3263d58f9aadd9b4fe7e711fd87dc452b7f163"
dependencies = [
"anstyle",
"anstyle-parse",
"anstyle-query",
"anstyle-wincon",
"colorchoice",
"is-terminal",
"utf8parse",
]
[[package]]
name = "anstyle"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "41ed9a86bf92ae6580e0a31281f65a1b1d867c0cc68d5346e2ae128dddfa6a7d"
[[package]]
name = "anstyle-parse"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e765fd216e48e067936442276d1d57399e37bce53c264d6fefbe298080cb57ee"
dependencies = [
"utf8parse",
]
[[package]]
name = "anstyle-query"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5ca11d4be1bab0c8bc8734a9aa7bf4ee8316d462a08c6ac5052f888fef5b494b"
dependencies = [
"windows-sys 0.48.0",
]
[[package]]
name = "anstyle-wincon"
version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "180abfa45703aebe0093f79badacc01b8fd4ea2e35118747e5811127f926e188"
dependencies = [
"anstyle",
"windows-sys 0.48.0",
]
[[package]]
name = "anyhow"
version = "1.0.71"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9c7d0618f0e0b7e8ff11427422b64564d5fb0be1940354bfe2e0529b18a9d9b8"
[[package]]
name = "autocfg"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa"
[[package]]
name = "bitflags"
version = "1.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
[[package]]
name = "byteorder"
version = "1.4.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610"
[[package]]
name = "cc"
version = "1.0.79"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "50d30906286121d95be3d479533b458f87493b30a4b5f79a607db8f5d11aa91f"
[[package]]
name = "cfg-if"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
[[package]]
name = "clap"
version = "4.3.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "80672091db20273a15cf9fdd4e47ed43b5091ec9841bf4c6145c9dfbbcae09ed"
dependencies = [
"clap_builder",
"clap_derive",
"once_cell",
]
[[package]]
name = "clap_builder"
version = "4.3.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c1458a1df40e1e2afebb7ab60ce55c1fa8f431146205aa5f4887e0b111c27636"
dependencies = [
"anstream",
"anstyle",
"bitflags",
"clap_lex",
"strsim",
"terminal_size",
]
[[package]]
name = "clap_derive"
version = "4.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b8cd2b2a819ad6eec39e8f1d6b53001af1e5469f8c177579cdaeb313115b825f"
dependencies = [
"heck",
"proc-macro2",
"quote",
"syn 2.0.18",
]
[[package]]
name = "clap_lex"
version = "0.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2da6da31387c7e4ef160ffab6d5e7f00c42626fe39aea70a7b0f1773f7dd6c1b"
[[package]]
name = "colorchoice"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "acbf1af155f9b9ef647e42cdc158db4b64a1b61f743629225fde6f3e0be2a7c7"
[[package]]
name = "curl"
version = "0.4.44"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "509bd11746c7ac09ebd19f0b17782eae80aadee26237658a6b4808afb5c11a22"
dependencies = [
"curl-sys",
"libc",
"openssl-probe",
"openssl-sys",
"schannel",
"socket2",
"winapi",
]
[[package]]
name = "curl-sys"
version = "0.4.63+curl-8.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "aeb0fef7046022a1e2ad67a004978f0e3cacb9e3123dc62ce768f92197b771dc"
dependencies = [
"cc",
"libc",
"libz-sys",
"openssl-sys",
"pkg-config",
"vcpkg",
"winapi",
]
[[package]]
name = "errno"
version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4bcfec3a70f97c962c307b2d2c56e358cf1d00b558d74262b5f929ee8cc7e73a"
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",
]
[[package]]
name = "foreign-types"
version = "0.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1"
dependencies = [
"foreign-types-shared",
]
[[package]]
name = "foreign-types-shared"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b"
[[package]]
name = "hashbrown"
version = "0.12.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888"
[[package]]
name = "heck"
version = "0.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8"
[[package]]
name = "hermit-abi"
version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fed44880c466736ef9a5c5b5facefb5ed0785676d0c02d612db14e54f0d84286"
[[package]]
name = "indexmap"
version = "1.9.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99"
dependencies = [
"autocfg",
"hashbrown",
]
[[package]]
name = "io-lifetimes"
version = "1.0.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "eae7b9aee968036d54dce06cebaefd919e4472e753296daccd6d344e3e2df0c2"
dependencies = [
"hermit-abi",
"libc",
"windows-sys 0.48.0",
]
[[package]]
name = "is-terminal"
version = "0.4.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "adcf93614601c8129ddf72e2d5633df827ba6551541c6d8c59520a371475be1f"
dependencies = [
"hermit-abi",
"io-lifetimes",
"rustix",
"windows-sys 0.48.0",
]
[[package]]
name = "itoa"
version = "1.0.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "453ad9f582a441959e5f0d088b02ce04cfe8d51a8eaf077f12ac6d3e94164ca6"
[[package]]
name = "libc"
version = "0.2.146"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f92be4933c13fd498862a9e02a3055f8a8d9c039ce33db97306fd5a6caa7f29b"
[[package]]
name = "libz-sys"
version = "1.1.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "56ee889ecc9568871456d42f603d6a0ce59ff328d291063a45cbdf0036baf6db"
dependencies = [
"cc",
"libc",
"pkg-config",
"vcpkg",
]
[[package]]
name = "linux-raw-sys"
version = "0.3.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ef53942eb7bf7ff43a617b3e2c1c4a5ecf5944a7c1bc12d7ee39bbb15e5c1519"
[[package]]
name = "log"
version = "0.4.19"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b06a4cde4c0f271a446782e3eff8de789548ce57dbc8eca9292c27f4a42004b4"
[[package]]
name = "once_cell"
version = "1.18.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dd8b5dd2ae5ed71462c540258bedcb51965123ad7e7ccf4b9a8cafaa4a63576d"
[[package]]
name = "openssl"
version = "0.10.54"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "69b3f656a17a6cbc115b5c7a40c616947d213ba182135b014d6051b73ab6f019"
dependencies = [
"bitflags",
"cfg-if",
"foreign-types",
"libc",
"once_cell",
"openssl-macros",
"openssl-sys",
]
[[package]]
name = "openssl-macros"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.18",
]
[[package]]
name = "openssl-probe"
version = "0.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf"
[[package]]
name = "openssl-sys"
version = "0.9.88"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c2ce0f250f34a308dcfdbb351f511359857d4ed2134ba715a4eadd46e1ffd617"
dependencies = [
"cc",
"libc",
"pkg-config",
"vcpkg",
]
[[package]]
name = "openssl_extensions"
version = "0.1.0"
dependencies = [
"foreign-types",
"libc",
"log",
"openssl",
"openssl-sys",
]
[[package]]
name = "pkg-config"
version = "0.3.27"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "26072860ba924cbfa98ea39c8c19b4dd6a4a25423dbdf219c1eca91aa0cf6964"
[[package]]
name = "proc-macro2"
version = "1.0.60"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dec2b086b7a862cf4de201096214fa870344cf922b2b30c167badb3af3195406"
dependencies = [
"unicode-ident",
]
[[package]]
name = "pv"
version = "0.9.0"
dependencies = [
"byteorder",
"cfg-if",
"clap",
"curl",
"libc",
"log",
"openssl",
"openssl_extensions",
"serde",
"thiserror",
"zerocopy",
]
[[package]]
name = "pvsecret"
version = "0.9.0"
dependencies = [
"anyhow",
"clap",
"log",
"pv",
"serde_yaml",
"utils",
]
[[package]]
name = "quote"
version = "1.0.28"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1b9ab9c7eadfd8df19006f1cf1a4aed13540ed5cbc047010ece5826e10825488"
dependencies = [
"proc-macro2",
]
[[package]]
name = "rustix"
version = "0.37.20"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b96e891d04aa506a6d1f318d2771bcb1c7dfda84e126660ace067c9b474bb2c0"
dependencies = [
"bitflags",
"errno",
"io-lifetimes",
"libc",
"linux-raw-sys",
"windows-sys 0.48.0",
]
[[package]]
name = "ryu"
version = "1.0.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f91339c0467de62360649f8d3e185ca8de4224ff281f66000de5eb2a77a79041"
[[package]]
name = "schannel"
version = "0.1.21"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "713cfb06c7059f3588fb8044c0fad1d09e3c01d225e25b9220dbfdcf16dbb1b3"
dependencies = [
"windows-sys 0.42.0",
]
[[package]]
name = "serde"
version = "1.0.164"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9e8c8cf938e98f769bc164923b06dce91cea1751522f46f8466461af04c9027d"
dependencies = [
"serde_derive",
]
[[package]]
name = "serde_derive"
version = "1.0.164"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d9735b638ccc51c28bf6914d90a2e9725b377144fc612c49a611fddd1b631d68"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.18",
]
[[package]]
name = "serde_yaml"
version = "0.9.21"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d9d684e3ec7de3bf5466b32bd75303ac16f0736426e5a4e0d6e489559ce1249c"
dependencies = [
"indexmap",
"itoa",
"ryu",
"serde",
"unsafe-libyaml",
]
[[package]]
name = "socket2"
version = "0.4.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "64a4a911eed85daf18834cfaa86a79b7d266ff93ff5ba14005426219480ed662"
dependencies = [
"libc",
"winapi",
]
[[package]]
name = "strsim"
version = "0.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623"
[[package]]
name = "syn"
version = "1.0.109"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237"
dependencies = [
"proc-macro2",
"quote",
"unicode-ident",
]
[[package]]
name = "syn"
version = "2.0.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "32d41677bcbe24c20c52e7c70b0d8db04134c5d1066bf98662e2871ad200ea3e"
dependencies = [
"proc-macro2",
"quote",
"unicode-ident",
]
[[package]]
name = "terminal_size"
version = "0.2.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8e6bf6f19e9f8ed8d4048dc22981458ebcf406d67e94cd422e5ecd73d63b3237"
dependencies = [
"rustix",
"windows-sys 0.48.0",
]
[[package]]
name = "thiserror"
version = "1.0.40"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "978c9a314bd8dc99be594bc3c175faaa9794be04a5a5e153caba6915336cebac"
dependencies = [
"thiserror-impl",
]
[[package]]
name = "thiserror-impl"
version = "1.0.40"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f9456a42c5b0d803c8cd86e73dd7cc9edd429499f37a3550d286d5e86720569f"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.18",
]
[[package]]
name = "unicode-ident"
version = "1.0.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b15811caf2415fb889178633e7724bad2509101cde276048e013b9def5e51fa0"
[[package]]
name = "unsafe-libyaml"
version = "0.2.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1865806a559042e51ab5414598446a5871b561d21b6764f2eabb0dd481d880a6"
[[package]]
name = "utf8parse"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a"
[[package]]
name = "utils"
version = "0.1.0"
[[package]]
name = "vcpkg"
version = "0.2.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426"
[[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",
]
[[package]]
name = "windows-targets"
version = "0.48.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7b1eb6f0cd7c80c79759c929114ef071b87354ce476d9d94271031c0497adfd5"
dependencies = [
"windows_aarch64_gnullvm 0.48.0",
"windows_aarch64_msvc 0.48.0",
"windows_i686_gnu 0.48.0",
"windows_i686_msvc 0.48.0",
"windows_x86_64_gnu 0.48.0",
"windows_x86_64_gnullvm 0.48.0",
"windows_x86_64_msvc 0.48.0",
]
[[package]]
name = "windows_aarch64_gnullvm"
version = "0.42.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "597a5118570b68bc08d8d59125332c54f1ba9d9adeedeef5b99b02ba2b0698f8"
[[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_msvc"
version = "0.42.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e08e8864a60f06ef0d0ff4ba04124db8b0fb3be5776a5cd47641e942e58c4d43"
[[package]]
name = "windows_aarch64_msvc"
version = "0.48.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b2ef27e0d7bdfcfc7b868b317c1d32c641a6fe4629c171b8928c7b08d98d7cf3"
[[package]]
name = "windows_i686_gnu"
version = "0.42.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c61d927d8da41da96a81f029489353e68739737d3beca43145c8afec9a31a84f"
[[package]]
name = "windows_i686_gnu"
version = "0.48.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "622a1962a7db830d6fd0a69683c80a18fda201879f0f447f065a3b7467daa241"
[[package]]
name = "windows_i686_msvc"
version = "0.42.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "44d840b6ec649f480a41c8d80f9c65108b92d89345dd94027bfe06ac444d1060"
[[package]]
name = "windows_i686_msvc"
version = "0.48.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4542c6e364ce21bf45d69fdd2a8e455fa38d316158cfd43b3ac1c5b1b19f8e00"
[[package]]
name = "windows_x86_64_gnu"
version = "0.42.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8de912b8b8feb55c064867cf047dda097f92d51efad5b491dfb98f6bbb70cb36"
[[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_gnullvm"
version = "0.42.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "26d41b46a36d453748aedef1486d5c7a85db22e56aff34643984ea85514e94a3"
[[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_msvc"
version = "0.42.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0"
[[package]]
name = "windows_x86_64_msvc"
version = "0.48.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1a515f5799fe4961cb532f983ce2b23082366b898e52ffbce459c86f67c8378a"
[[package]]
name = "zerocopy"
version = "0.6.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "332f188cc1bcf1fe1064b8c58d150f497e697f49774aa846f2dc949d9a25f236"
dependencies = [
"byteorder",
"zerocopy-derive",
]
[[package]]
name = "zerocopy-derive"
version = "0.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6505e6815af7de1746a08f69c69606bb45695a17149517680f3b2149713b19a3"
dependencies = [
"proc-macro2",
"quote",
"syn 1.0.109",
]

18
rust/pvsecret/Cargo.toml Normal file
View File

@@ -0,0 +1,18 @@
[package]
name = "pvsecret"
version = "0.9.0"
edition = "2021"
license = "MIT"
[dependencies]
anyhow = { version = "1", features = ["std"] }
clap = { version ="4", features = ["derive", "wrap_help"]}
log = { version = "0.4", features = ["std", "release_max_level_debug"] }
serde_yaml = "0.9"
pv = { path = "../pv", features = ["uvsecret", "request"] }
utils = { path = "../utils" }
[profile.release]
lto = true
panic = "abort" # release builds now do not clean up stack after panics. .1 Mb

284
rust/pvsecret/README.md Normal file
View File

@@ -0,0 +1,284 @@
<!--
Copyright 2023 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.
-->
# pvsecret
## Description
Use **pvsecret** to manage secrets for IBM Secure Execution guests. **pvsecret**
can **create** add-secret requests on any architecture. On s390x systems, use
**pvsecret** to **add** the secrets to the ultravisor secret store, **list** all
secrets in the secret store, or lock the secret store to prevent any
modifications in the future.
The ultravisor secret store stores secrets for the IBM Secure Execution guest.
The secret store is cleared on guest reboot.
Create requests only on trusted systems that are not the IBM Secure Execution
guest where you want to inject the secrets. This approach prevents the secrets
from being in cleartext on the guest. For extra safety, do an attestation with
**pvattest** of your guest beforehand, and include the configuration UID in the
secret request using **--cuid**. Refer to **pvsecret-add**(1) for more
information. For all certificates, revocation lists, and host-key documents,
both the PEM and DER input formats are supported.
## Synopsis
`pvsecret [OPTIONS] <COMMAND>`
## Commands Overview
- **create**
Create a new add-secret request
- **add**
Repeat an add-secret request (s390x only)
- **lock**
Lock the secret-store (s390x only)
- **list**
List all ultravisor secrets (s390x only)
## Options
`-v`, `--verbose`
<ul>
Provide more detailed output
</ul>
`--version`
<ul>
Print version information and exit
</ul>
## pvsecret create
### Description
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 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 the use case
depending on the secret type.
Such a request is bound to a specific IBM Secure Execution image specified with
**--hdr**. Optionally, the request can be bound to a specific instance when
bound to the Configuration Unique ID from **pvattest** using **--cuid**
### Synopsis
`pvsecret create [OPTIONS] --host-key-document <FILE> --hdr <FILE> --output <FILE> <--no-verify|--cert <FILE>> <COMMAND>`
### Commands Overview
- **meta**
Use a meta secret to carry flags to the ultravisor without having to provide
an actual secret value. Meta secrets do not appear in the list of secrets
- **association**
Use an association secret to connect a trusted I/O device to a guest. The
`pvapconfig` tool provides more information about association secrets
### Options
`-k`, `--host-key-document <FILE>`
<ul>
Use FILE as a host-key document. Can be specified multiple times and must be
used at least once.
</ul>
`--no-verify`
<ul>
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 the host-key document beforehand.
</ul>
`-C`, `--cert <FILE>`
<ul>
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
documents. Specify this option twice to specify the IBM Z signing key and
the intermediate CA certificate (signed by the root CA).
</ul>
`--crl <FILE>`
<ul>
Use FILE as a certificate revocation list. The list is used to check whether
a certificate of the chain of trust is revoked. Specify this option multiple
times to use multiple CRLs.
</ul>
`--offline`
<ul>
Make no attempt to download CRLs
</ul>
`--root-ca <ROOT_CA>`
<ul>
Use FILE as the root-CA certificate for the verification. If omitted, the
system-wide root CAs installed on the system are used. Use this only if you
trust the specified certificate.
</ul>
`--hdr <FILE>`
<ul>
Specifies the header of the guest image. Can be an IBM Secure Execution
image created by genprotimg or an extracted IBM Secure Execution header. The
header must start at a page boundary.
</ul>
`-f`, `--force`
<ul>
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 denies the generation of add-secret requests. The force flag overwrites
this behavior.
</ul>
`-o`, `--output <FILE>`
<ul>
Write the generated request to FILE
</ul>
`--extension-secret <FILE>`
<ul>
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 same extension secret. Only makes sense if bit 1 of the secret control
flags of the IBM Secure Execution header is 0. Otherwise the ultravisor
rejects the request.
</ul>
`--cck <FILE>`
<ul>
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 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 extension secret as a normal one. This still works if you
use the same CCK for all requests.
</ul>
`--cuid-hex <HEXSTRING>`
<ul>
Use HEXSTRING as the Configuration Unique ID. Must be a hex 128-bit unsigned
big endian number string. Leading zeros must be provided. If specified, the
value must match with 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 the request.
</ul>
`--cuid <FILE>`
<ul>
Use the content of FILE as the Configuration Unique ID. The file must
contain exactly 128 bit of data, a hex string, 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 specified, the CUID will be ignored
by the Ultravisor during the verification of the request.
</ul>
`--flags <FLAGS>`
<ul>
Flags for the add-secret request.
Possible values:
- disable-dump: Disables host-initiated dumping for the target guest
instance
</ul>
### pvsecret create meta
#### Description
Use a meta secret to carry flags to the ultravisor without having to provide an
actual secret value. Meta secrets do not appear in the list of secrets.
#### Synopsis
`pvsecret create meta`
### pvsecret create association
#### Description
Use an association secret to connect a trusted I/O device to a guest. The
`pvapconfig` tool provides more information about association secrets.
#### Synopsis
`pvsecret create association [OPTIONS] <NAME>`
#### Arguments
`<NAME>`
<ul>
String to identify 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 will not be written to
`NAME.yaml`
</ul>
`--input-secret <FILE>`
<ul>
Path from which to read the plaintext secret. Uses a random secret if not
specified.
</ul>
`--output-secret <FILE>`
<ul>
Save the generated secret as plaintext in 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>
## pvsecret add
### Description
Perform an add-secret request using a previously generated add-secret request.
Only available on s390x.
### Synopsis
`pvsecret add <FILE>`
### Arguments
`<FILE>`
<ul>
Specify the request to be sent
</ul>
## pvsecret lock
### Description
Lock the secret store (s390x only). After this command executed successfully,
all add-secret requests will fail. Only available on s390x.
### Synopsis
`pvsecret lock`
## pvsecret list
### Description
Lists the IDs of all non-null secrets currently stored in the ultravisor for the
currently running IBM Secure Execution guest. Only available on s390x.
### Synopsis
`pvsecret list [OPTIONS] [FILE]`
### Arguments
`<FILE>`
<ul>
Store the result in FILE. Default value: '-'
</ul>
### Options
`--format <FORMAT>`
Define the output format of the list. Default value: 'human'
Possible values:
- human: Human-focused, non-parsable output format
- yaml: Use yaml format
- bin: Use the format the ultravisor uses to pass the list

View File

@@ -0,0 +1,33 @@
.\" Copyright 2023 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-add 1 "2023-07-28" "s390-tools" "UV-Secret Manual"
.nh
.ad l
.SH NAME
\fBpvsecret add\fP - Repeat an add-secret request (s390x only)
\fB
.SH SYNOPSIS
.nf
.fam C
pvsecret add <FILE>
.fam C
.fi
.SH DESCRIPTION
Perform an add-secret request using a previously generated add-secret request.
Only available on s390x.
.SH OPTIONS
.PP
<FILE>
.RS 4
Specify the request to be sent.
.RE
.RE
.SH "SEE ALSO"
.sp
\fBpvsecret\fR(1)

View File

@@ -0,0 +1,56 @@
.\" Copyright 2023 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-association 1 "2023-07-28" "s390-tools" "UV-Secret Manual"
.nh
.ad l
.SH NAME
\fBpvsecret create association\fP - Use an association secret to connect an I/O device to a guest
\fB
.SH SYNOPSIS
.nf
.fam C
pvsecret create association [OPTIONS] <NAME>
.fam C
.fi
.SH DESCRIPTION
Use an association secret to connect a trusted I/O device to a guest. The
`pvapconfig` tool provides more information about association secrets.
.SH OPTIONS
.PP
<NAME>
.RS 4
String to identify the new secret. The actual secret is set with
\fB--input-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
\-\-input-secret <FILE>
.RS 4
Path from which to read the plaintext secret. Uses a random secret if not
specified.
.RE
.RE
.PP
\-\-output-secret <FILE>
.RS 4
Save the generated secret as plaintext in FILE. The generated secret can be used
to generate add-secret requests for a different guest with the same secret using
\fB--input-secret\fR. Destroy the secret when it is not used anymore.
.RE
.RE
.SH "SEE ALSO"
.sp
\fBpvsecret\fR(1) \fBpvsecret-create\fR(1)

View File

@@ -0,0 +1,24 @@
.\" Copyright 2023 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-meta 1 "2023-07-28" "s390-tools" "UV-Secret Manual"
.nh
.ad l
.SH NAME
\fBpvsecret create meta\fP - Use a meta secret to carry flags to the ultravisor
\fB
.SH SYNOPSIS
.nf
.fam C
pvsecret create meta
.fam C
.fi
.SH DESCRIPTION
Use a meta secret to carry flags to the ultravisor without having to provide an
actual secret value. Meta secrets do not appear in the list of secrets.
.SH "SEE ALSO"
.sp
\fBpvsecret\fR(1) \fBpvsecret-create\fR(1)

View File

@@ -0,0 +1,154 @@
.\" Copyright 2023 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 1 "2023-07-28" "s390-tools" "UV-Secret Manual"
.nh
.ad l
.SH NAME
\fBpvsecret create\fP - Create a new add-secret request
\fB
.SH SYNOPSIS
.nf
.fam C
pvsecret create [OPTIONS] --host-key-document <FILE> --hdr <FILE> --output <FILE> <--no-verify|--cert <FILE>> <COMMAND>
.fam C
.fi
.SH DESCRIPTION
Create add-secret requests for IBM Secure Execution guests. Only create these
requests in a trusted environment, such as your workstation. The \fBpvattest
create\fR command creates a randomly generated key to protect the request. The
generated requests can then be added on an IBM Secure Execution guest using
\fBpvsecret add\fR. The guest can then use the secrets with the use case
depending on the secret type.
Such a request is bound to a specific IBM Secure Execution image specified with
\fB--hdr\fR. Optionally, the request can be bound to a specific instance when
bound to the Configuration Unique ID from \fBpvattest\fR using \fB--cuid\fR
.SH OPTIONS
.PP
\-k, \-\-host-key-document <FILE>
.RS 4
Use FILE as a host-key document. Can be specified multiple times and must be
used at least once.
.RE
.RE
.PP
\-\-no-verify
.RS 4
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
the host-key document beforehand.
.RE
.RE
.PP
\-C, \-\-cert <FILE>
.RS 4
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
documents. Specify this option twice to specify the IBM Z signing key and the
intermediate CA certificate (signed by the root CA).
.RE
.RE
.PP
\-\-crl <FILE>
.RS 4
Use FILE as a certificate revocation list. The list is used to check whether a
certificate of the chain of trust is revoked. Specify this option multiple times
to use multiple CRLs.
.RE
.RE
.PP
\-\-offline
.RS 4
Make no attempt to download CRLs
.RE
.RE
.PP
\-\-root-ca <ROOT_CA>
.RS 4
Use FILE as the root-CA certificate for the verification. If omitted, the
system-wide root CAs installed on the system are used. Use this only if you
trust the specified certificate.
.RE
.RE
.PP
\-\-hdr <FILE>
.RS 4
Specifies the header of the guest image. Can be an IBM Secure Execution image
created by genprotimg or an extracted IBM Secure Execution header. The header
must start at a page boundary.
.RE
.RE
.PP
\-f, \-\-force
.RS 4
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
denies the generation of add-secret requests. The force flag overwrites this
behavior.
.RE
.RE
.PP
\-o, \-\-output <FILE>
.RS 4
Write the generated request to FILE
.RE
.RE
.PP
\-\-extension-secret <FILE>
.RS 4
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
same extension secret. Only makes sense if bit 1 of the secret control flags of
the IBM Secure Execution header is 0. Otherwise the ultravisor rejects the
request.
.RE
.RE
.PP
\-\-cck <FILE>
.RS 4
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
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
extension secret as a normal one. This still works if you use the same CCK for
all requests.
.RE
.RE
.PP
\-\-cuid-hex <HEXSTRING>
.RS 4
Use HEXSTRING as the Configuration Unique ID. Must be a hex 128-bit unsigned
big endian number string. Leading zeros must be provided. If specified, the
value must match with 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 the request.
.RE
.RE
.PP
\-\-cuid <FILE>
.RS 4
Use the content of FILE as the Configuration Unique ID. The file must contain
exactly 128 bit of data, a hex string, 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 specified, the CUID will be ignored by the Ultravisor during
the verification of the request.
.RE
.RE
.PP
\-\-flags <FLAGS>
.RS 4
Flags for the add-secret request.
Possible values:
.RS 4
- \fBdisable-dump\fP: Disables host-initiated dumping for the target guest instance.
.RE
.RE
.SH "SEE ALSO"
.sp
\fBpvsecret\fR(1) \fBpvsecret-create-meta\fR(1) \fBpvsecret-create-association\fR(1)

View File

@@ -0,0 +1,47 @@
.\" Copyright 2023 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-list 1 "2023-07-28" "s390-tools" "UV-Secret Manual"
.nh
.ad l
.SH NAME
\fBpvsecret list\fP - List all ultravisor secrets (s390x only)
\fB
.SH SYNOPSIS
.nf
.fam C
pvsecret list [OPTIONS] [FILE]
.fam C
.fi
.SH DESCRIPTION
Lists the IDs of all non-null secrets currently stored in the ultravisor for the
currently running IBM Secure Execution guest. Only available on s390x.
.SH OPTIONS
.PP
<FILE>
.RS 4
Store the result in FILE. Default value: '-'
.RE
.RE
.PP
\-\-format <FORMAT>
.RS 4
Define the output format of the list. Default value: 'human'
Possible values:
.RS 4
- \fBhuman\fP: Human-focused, non-parsable output format
- \fByaml\fP: Use yaml format
- \fBbin\fP: Use the format the ultravisor uses to pass the list
.RE
.RE
.SH "SEE ALSO"
.sp
\fBpvsecret\fR(1)

View File

@@ -0,0 +1,26 @@
.\" Copyright 2023 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-lock 1 "2023-07-28" "s390-tools" "UV-Secret Manual"
.nh
.ad l
.SH NAME
\fBpvsecret lock\fP - Lock the secret-store (s390x only)
\fB
.SH SYNOPSIS
.nf
.fam C
pvsecret lock
.fam C
.fi
.SH DESCRIPTION
Lock the secret store (s390x only). After this command executed successfully,
all add-secret requests will fail. Only available on s390x.
.SH OPTIONS
.SH "SEE ALSO"
.sp
\fBpvsecret\fR(1)

View File

@@ -0,0 +1,25 @@
.\" Copyright 2023 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-version 1 "2023-07-28" "s390-tools" "UV-Secret Manual"
.nh
.ad l
.SH NAME
\fBpvsecret version\fP- Print version information and exit
\fB
.SH SYNOPSIS
.nf
.fam C
pvsecret version
.fam C
.fi
.SH DESCRIPTION
Print version information and exit
.SH OPTIONS
.SH "SEE ALSO"
.sp
\fBpvsecret\fR(1)

View File

@@ -0,0 +1,103 @@
.\" Copyright 2023 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 1 "2023-07-28" "s390-tools" "UV-Secret Manual"
.nh
.ad l
.SH NAME
\fBpvsecret\fP - Manage secrets for IBM Secure Execution guests
\fB
.SH SYNOPSIS
.nf
.fam C
pvsecret [OPTIONS] <COMMAND>
.fam C
.fi
.SH DESCRIPTION
Use \fBpvsecret\fR to manage secrets for IBM Secure Execution guests.
\fBpvsecret\fR can \fIcreate\fR add-secret requests on any architecture. On
s390x systems, use \fBpvsecret\fR to \fIadd\fR the secrets to the ultravisor
secret store, \fIlist\fR all secrets in the secret store, or \fIlock\fR the
secret store to prevent any modifications in the future.
The ultravisor secret store stores secrets for the IBM Secure Execution guest.
The secret store is cleared on guest reboot.
Create requests only on trusted systems that are not the IBM Secure Execution
guest where you want to inject the secrets. This approach prevents the secrets
from being in cleartext on the guest. For extra safety, do an attestation with
\fBpvattest\fR of your guest beforehand, and include the configuration UID in
the secret request using \fB--cuid\fR. Refer to \fBpvsecret-add\fR(1) for more
information. For all certificates, revocation lists, and host-key documents,
both the PEM and DER input formats are supported.
.SH OPTIONS
.PP
\-v, \-\-verbose
.RS 4
Provide more detailed output.
.RE
.RE
.PP
\-\-version
.RS 4
Print version information and exit.
.RE
.RE
.SH EXAMPLES
.PP
Create the add-secret request on a trusted system. The program generates three
files. \fFaddsecreq.bin\fP contains the add-secret request. \fFTEST.yaml\fP
contains the non-confidential information about the generated secret. It
contains the name and ID of the secret. \fFTEST\fP contains the plaintext secret
that is encrypted in the request. It can be used to generate add-secret requests
for a different guest with the same secret. Destroy the secret when it is not
used anymore.
.PP
.nf
.fam C
trusted:~$ pvsecret create -k hkd.crt --cert CA.crt --cert ibmsk.crt --hdr pvimage -o addsecreq.bin association EXAMPLE
Successfully generated the request
Successfully wrote association info to 'EXAMPLE.yaml'
Successfully wrote generated association secret to 'EXAMPLE'
.fam T
.fi
On the SE-guest, \fIadd\fP the secret from request to the secret store.
.PP
.nf
.fam C
seguest:~$ pvsecret add addsecreq.bin
Successfully added the secret
.fam T
.fi
On the SE-guest, \fIlist\fP the secrets currently stored.
.PP
.nf
.fam C
seguest:~$ pvsecret list
Total number of secrets: 1
0 Association:
94ee059335e587e501cc4bf90613e0814f00a7b08bc7c648fd865a2af6a22cc2
.fam T
.fi
On the SE-guest, \fIlock\fP the secret store.
.PP
.nf
.fam C
seguest:~$ pvsecret lock
Successfully locked secret store
seguest:~$ pvsecret add addsecreq.bin
error: Ultravisor: 'secret store locked' (0x0102)
.fam T
.fi
.SH "SEE ALSO"
.sp
\fBpvsecret-create\fR(1) \fBpvsecret-add\fR(1) \fBpvsecret-lock\fR(1) \fBpvsecret-list\fR(1) \fBpvsecret-version\fR(1)

322
rust/pvsecret/src/cli.rs Normal file
View File

@@ -0,0 +1,322 @@
// SPDX-License-Identifier: MIT
//
// Copyright IBM Corp. 2023
use clap::{ArgGroup, Args, CommandFactory, Parser, Subcommand, ValueEnum, ValueHint};
use pv::misc::CertificateOptions;
#[cfg(target_arch = "s390x")]
use pv::misc::STDOUT;
/// Manage secrets for IBM Secure Execution guests.
///
/// Use to create and send add-secret requests, list the added secrets and lock the Secret Store.
#[derive(Parser, Debug)]
pub struct CliOptions {
/// Provide more detailed output.
#[arg(short='v', long, action = clap::ArgAction::Count, short_alias('V'))]
pub verbose: u8,
/// Print version information and exit.
#[arg(long)]
pub version: bool,
#[command(subcommand)]
pub cmd: Command,
}
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, ValueEnum, Debug)]
pub enum CreateSecretFlags {
/// Disables host-initiated dumping for the target guest instance.
DisableDump,
}
#[derive(Args, Debug)]
#[command(group(ArgGroup::new("as-ext").args(["cck", "extension_secret"])),)]
pub struct CreateSecretOpt {
#[command(flatten)]
pub certificate_args: CertificateOptions,
/// Specifies the header of the guest image.
///
/// Can be an IBM Secure Execution image created by genprotimg or an extracted IBM Secure
/// Execution header. The header must start at a page boundary.
#[arg(long, value_name = "FILE", value_hint = ValueHint::FilePath)]
pub hdr: String,
/// 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 denies the
/// generation of add-secret requests. The force flag overwrites this behavior.
#[arg(short, long)]
pub force: bool,
/// Write the generated request to FILE.
#[arg(short, long, value_name = "FILE", value_hint = ValueHint::FilePath,)]
pub output: String,
/// 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 same extension secret. Only makes sense if bit 1 of the secret
/// control flags of the IBM Secure Execution header is
/// 0. Otherwise the ultravisor rejects the request.
#[arg(long, value_name = "FILE", value_hint = ValueHint::FilePath,)]
pub extension_secret: Option<String>,
/// 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 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 extension secret as a normal one. This still
/// works if you use the same CCK for all requests.
#[arg(long, value_name = "FILE")]
pub cck: Option<String>,
/// Use HEXSTRING as the Configuration Unique ID.
///
/// Must be a hex 128-bit unsigned big endian number string. Leading zeros must be provided. If
/// specified, the value must match with 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 the request.
#[arg(long, value_name = "HEXSTRING")]
pub cuid_hex: Option<String>,
/// Use the content of FILE as the Configuration Unique ID.
///
/// The file must contain exactly 128 bit of data, a hex string, 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 specified, the CUID will be ignored by the Ultravisor during the verification
/// of the request.
#[arg(long, value_name = "FILE", conflicts_with("cuid_hex"), value_hint = ValueHint::FilePath,)]
pub cuid: Option<String>,
#[command(subcommand)]
pub secret: AddSecretType,
// FLAGS
// each flag must conflict with `flags`
// `flags` is hidden in the help menu
/// Manually set the Add Secret Request flags.
///
/// No validity checks made. Hidden in user documentation.
#[arg(long, hide(true))]
pub pcf: Option<String>,
/// Flags for the add-secret request.
#[arg(
long,
conflicts_with("pcf"),
value_enum,
value_parser,
use_value_delimiter = true,
value_delimiter = ','
)]
pub flags: Vec<CreateSecretFlags>,
}
#[derive(Subcommand, Debug)]
pub enum AddSecretType {
/// Use a meta secret to carry flags to the ultravisor without having to provide an actual
/// secret value. Meta secrets do not appear in the list of secrets.
Meta,
/// Use an association secret to connect a trusted I/O device to a guest. The `pvapconfig` tool
/// provides more information about association secrets.
Association {
/// String to identify the new secret.
///
/// The actual secret is set with --input-secret. The name is saved in `NAME.yaml` with
/// white-spaces mapped to `_`.
name: String,
///Print the hashed name to stdout.
///
///The hashed name will not be written to `NAME.yaml`
#[arg(long)]
stdout: bool,
/// Path from which to read the plaintext secret. Uses a random secret if not specified.
#[arg(long, value_name = "FILE", value_hint = ValueHint::FilePath, conflicts_with("output_secret"))]
input_secret: Option<String>,
/// Save the generated secret as plaintext in 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.
#[arg(long, value_name = "FILE", value_hint = ValueHint::FilePath,)]
output_secret: Option<String>,
},
}
// all members s390x only
#[derive(Args, Debug)]
pub struct AddSecretOpt {
/// Specify the request to be sent.
#[arg(value_name = "FILE", value_hint = ValueHint::FilePath,)]
#[cfg(target_arch = "s390x")]
pub input: String,
}
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, ValueEnum, Debug, Default)]
#[cfg(target_arch = "s390x")]
pub enum ListSecretOutputType {
/// Human-focused, non-parsable output format
#[default]
Human,
/// Use yaml format.
Yaml,
/// Use the format the ultravisor uses to pass the list.
Bin,
}
// all members s390x only
#[derive(Args, Debug)]
pub struct ListSecretOpt {
/// Store the result in FILE
#[arg(value_name = "FILE", default_value = STDOUT, value_hint = ValueHint::FilePath,)]
#[cfg(target_arch = "s390x")]
pub output: String,
/// Define the output format of the list.
#[arg(long, value_enum, default_value_t)]
#[cfg(target_arch = "s390x")]
pub format: ListSecretOutputType,
}
#[derive(Subcommand, Debug)]
pub enum Command {
/// Create a new add-secret request.
///
/// Create add-secret requests for IBM Secure Execution guests. Only create these requests in a
/// trusted environment, such as your workstation. The `pvattest create` command creates a
/// 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
/// the use case depending on the secret type.
Create(Box<CreateSecretOpt>),
/// Repeat an add-secret request (s390x only).
///
/// Perform an add-secret request using a previously generated add-secret request. Only
/// available on s390x.
Add(AddSecretOpt),
/// Lock the secret-store (s390x only).
///
/// Lock the secret store (s390x only). After this command executed successfully, all
/// add-secret requests will fail. Only available on s390x.
Lock,
/// List all ultravisor secrets (s390x only).
///
/// Lists the IDs of all non-null secrets currently stored in the ultravisor for the currently
/// running IBM Secure Execution guest. Only available on s390x.
List(ListSecretOpt),
/// Print version information and exit.
#[command(aliases(["--version"]), hide(true))]
Version,
}
/// Additional checks to assure, option integrity
pub fn validate_cli(cli: &CliOptions) -> Result<(), clap::Error> {
if let Command::Create(opt) = &cli.cmd {
if let AddSecretType::Association {
name,
stdout,
input_secret: _,
output_secret: secret_out,
} = &opt.secret
{
if *stdout {
return Ok(());
}
if secret_out == &Some(format!("{name}.yaml")) {
return Err(CliOptions::command().error(
clap::error::ErrorKind::ValueValidation,
format!("Secret output file and the secret name '{name}.yaml' are the same."),
));
}
if format!("{name}.yaml") == opt.output {
return Err(CliOptions::command().error(
clap::error::ErrorKind::ValueValidation,
format!(
"output file and the secret name '{}' are the same.",
&opt.output
),
));
}
}
}
Ok(())
}
#[cfg(test)]
mod test {
use super::*;
#[test]
#[rustfmt::skip]
fn cli_args() {
//Verify only that some arguments are optional, we do not want to test clap, only the
//configuration
let valid_args = [
vec!["pvsecret", "lock"],
vec!["pvsecret", "version"],
vec!["pvsecret", "list"],
#[cfg(target_arch = "s390x")]
vec!["pvsecret", "add", "abc"],
#[cfg(not(target_arch = "s390x"))]
vec!["pvsecret", "add"],
vec!["pvsecret", "create", "-k", "abc", "--hdr", "abc", "-o", "abc", "--no-verify", "meta"],
vec!["pvsecret", "create", "-k", "abc", "--hdr", "abc", "-o", "abc", "--no-verify", "association", "name" ],
// verify that arguments stay backwards compatible
vec!["pvsecret", "create", "-k", "abc,cdef", "--hdr", "abc", "-o", "abc", "-C", "uuu,ggg", "--crl", "yyy,hhh", "--root-ca", "tttt",
"--extension-secret", "fff", "--cuid", "cuid", "--flags", "disable-dump", "meta"],
vec!["pvsecret", "create", "--host-key-document", "abc", "-k", "y", "--hdr", "abc", "-o", "abc", "--cert", "uuu", "--crl", "yyy",
"--root-ca", "tttt", "--cck", "cck", "--cuid-hex", "0x11223344556677889900aabbccddeeff", "--pcf", "0x123", "association", "name", "--stdout",
"--output-secret", "secret"],
vec!["pvsecret", "create", "-k", "abc", "--hdr", "abc", "-o", "abc", "--no-verify", "association", "name", "--output-secret", "secret"],
#[cfg(target_arch = "s390x")]
vec!["pvsecret", "list", "--format", "human"],
#[cfg(target_arch = "s390x")]
vec!["pvsecret", "list", "--format", "yaml"],
#[cfg(target_arch = "s390x")]
vec!["pvsecret", "list", "--format", "bin"],
];
// Test for the minimal amount of flags to yield an invalid combination
let invalid_args = [
vec!["pvsecret"],
vec!["pvsecret", "list", "--yaml", "--bin"],
vec!["pvsecret", "create", "--hdr", "abc", "-o", "abc", "--no-verify" ,"null"],
vec!["pvsecret", "create", "-k", "abc", "-o", "abc", "--no-verify", "null"],
vec!["pvsecret", "create", "-k", "abc", "--hdr", "abc", "--no-verify", "null"],
vec!["pvsecret", "create", "-k", "abc", "--hdr", "abc", "-o", "abc", "null"],
vec!["pvsecret", "create", "-k", "abc", "--hdr", "abc", "-o", "abc", "--cck", "abc", "--extension_secret", "abc", "--no-verify", "null"],
vec!["pvsecret", "create", "-k", "abc", "--hdr", "abc", "-o", "abc", "--no-verify", "--flags", "disable-dump", "--pcf", "0", "null"],
vec!["pvsecret", "create", "-k", "abc", "--hdr", "abc", "-o", "abc", "--no-verify", "--cuid", "abc", "--cuid_hex", "9", "null"],
vec!["pvsecret", "create", "-k", "abc", "--hdr", "abc", "-o", "abc", "--no-verify", "association"],
vec!["pvsecret", "create", "-k", "abc", "--hdr", "abc", "-o", "abc", "--no-verify", "association", "name", "--output-secret", "secret", "--input-secret", "secret"],
];
for arg in valid_args {
let res = CliOptions::try_parse_from(&arg);
if let Err(e) = &res {
println!("arg: {arg:?}");
println!("{e}");
}
assert!(res.is_ok());
}
for arg in invalid_args {
let res = CliOptions::try_parse_from(&arg);
assert!(res.is_err());
}
}
#[test]
fn verify_cli() {
use clap::CommandFactory;
CliOptions::command().debug_assert()
}
}

22
rust/pvsecret/src/cmd.rs Normal file
View File

@@ -0,0 +1,22 @@
// SPDX-License-Identifier: MIT
//
// Copyright IBM Corp. 2023
mod create;
pub use create::create;
// Commands (directly) related to UVCs are only available on s389x
#[cfg(target_arch = "s390x")]
mod add;
#[cfg(target_arch = "s390x")]
pub use add::add;
#[cfg(target_arch = "s390x")]
mod list;
#[cfg(target_arch = "s390x")]
pub use list::list;
#[cfg(target_arch = "s390x")]
mod lock;
#[cfg(target_arch = "s390x")]
pub use lock::lock;

View File

@@ -0,0 +1,19 @@
// SPDX-License-Identifier: MIT
//
// Copyright IBM Corp. 2023
use crate::cli::AddSecretOpt;
use anyhow::{Context, Result};
use log::warn;
use pv::misc::get_reader_from_cli_file_arg;
use pv::uv::{AddCmd, UvDevice};
/// Do an Add Secret UVC
pub fn add(opt: &AddSecretOpt) -> Result<()> {
let mut rd_in = get_reader_from_cli_file_arg(&opt.input)?;
let mut cmd =
AddCmd::new(&mut rd_in).context(format!("Processing input file {}", opt.input))?;
UvDevice::open()?.send_cmd(&mut cmd)?;
warn!("Successfully added the secret");
Ok(())
}

View File

@@ -0,0 +1,240 @@
// SPDX-License-Identifier: MIT
//
// Copyright IBM Corp. 2023
use crate::cli::{AddSecretType, CreateSecretFlags, CreateSecretOpt};
use anyhow::{anyhow, bail, Context, Result};
use log::{debug, info, trace, warn};
use pv::{
misc::{
get_writer_from_cli_file_arg, parse_hex, pv_guest_bit_set, read_certs, read_exact_file,
read_file, try_parse_u128, try_parse_u64,
},
open_buffered_file,
request::{
openssl::pkey::{PKey, Public},
uvsecret::{AddSecretFlags, AddSecretRequest, AddSecretVersion, ExtSecret, GuestSecret},
BootHdrTags, HkdVerifier, ReqEncrCtx, Request, SymKeyType,
},
uv::ConfigUid,
};
use serde_yaml::Value;
fn write_out<D: AsRef<[u8]>>(path: &str, data: D, ctx: &str) -> pv::Result<()> {
let mut wr = match get_writer_from_cli_file_arg(path) {
Ok(it) => it,
Err(err) => return Err(err),
};
pv::misc::write(&mut wr, data, path, ctx)?;
Ok(())
}
/// Prepare an Add Secret Request
pub fn create(opt: &CreateSecretOpt) -> Result<()> {
if pv_guest_bit_set() {
warn!("The system seems to be a Secure Execution guest");
if !opt.force {
bail!("Do NOT generate Add-secret requests on a machine where you want to use the secret! Overwrite with '-f'");
} else {
warn!("WARNING: Enforcing of generating a request on a Secure Execution guest")
}
}
let mut asrcb = build_asrcb(opt)?;
debug!("Generated Add-secret request");
// Add host-key documents
let verifier = opt.certificate_args.verifier()?;
read_and_verify_hkds(&opt.certificate_args.host_key_documents, verifier)?
.into_iter()
.for_each(|k| asrcb.add_hostkey(k));
debug!("Added all host-keys");
// build + encrypt the request
let rq = ReqEncrCtx::random(SymKeyType::Aes256).context("Failed to generate random input")?;
let ser_asrbc = asrcb.encrypt(&rq)?;
warn!("Successfully generated the request");
write_out(&opt.output, ser_asrbc, "Add Secret Request")?;
info!("Successfully wrote the request to '{}'", &opt.output);
write_secret(&opt.secret, &asrcb)
}
/// Set-up the `Add Secret Request` from command-line arguments
fn build_asrcb(opt: &CreateSecretOpt) -> Result<AddSecretRequest> {
debug!("Build Add Secret Request");
let secret = match &opt.secret {
AddSecretType::Meta => GuestSecret::Null,
AddSecretType::Association {
name,
input_secret: Some(p),
..
} => GuestSecret::association(name, read_exact_file(p, "Association secret")?)?,
AddSecretType::Association {
name,
input_secret: None,
..
} => GuestSecret::association(name, None)?,
};
trace!("AddSecret: {secret:x?}");
let mut flags = match &opt.pcf {
Some(v) => (&try_parse_u64(v, "pcf")?).into(),
None => AddSecretFlags::default(),
};
opt.flags.iter().for_each(|v| match v {
CreateSecretFlags::DisableDump => flags.set_disable_dump(),
});
debug!("FLAGS: {flags:x?}");
let mut se_hdr = open_buffered_file!(&opt.hdr);
let mut asrcb = AddSecretRequest::new(
AddSecretVersion::One,
secret,
BootHdrTags::from_se_image(&mut se_hdr)
.with_context(|| format!("Provided SE-header in '{}' is malformed", &opt.hdr))?,
flags,
);
// Set CUID
read_cuid(&mut asrcb, opt)?;
// Set extension secret
if let Some(path) = &opt.extension_secret {
asrcb.set_ext_secret(ExtSecret::Simple(
read_exact_file(path, "extension secret")?.into(),
))?;
} else if let Some(path) = &opt.cck {
asrcb.set_ext_secret(ExtSecret::Derived(read_exact_file(path, "CCK")?.into()))?;
}
Ok(asrcb)
}
// Try to extract a Config-UId from a yaml structure
// The cuid field can be embedded in an abritray amount of Mappings
// The function takes the first cuid it founds (width search).
fn try_from_val(val: Value) -> anyhow::Result<ConfigUid> {
fn get_cuid_from_mapping(val: &Value, depth: u8) -> Option<String> {
if depth >= 8 {
return None;
}
match val {
Value::Mapping(m) if m.contains_key("cuid") => {
return m.get("cuid").and_then(|v| v.as_str()).map(|s| s.to_owned())
}
Value::Mapping(m) => {
for (_, v) in m {
if let Some(v) = get_cuid_from_mapping(v, depth + 1) {
return Some(v);
}
}
}
_ => return None,
};
None
}
let cuid = match &val {
Value::String(s) => Some(s.clone()),
Value::Mapping(_) => get_cuid_from_mapping(&val, 0),
_ => None,
}
.ok_or(anyhow!("No 'cuid' entry found"))?;
let cuid = cuid
.strip_prefix("0x")
.ok_or(anyhow!("Value starts not with 0x".to_string()))?
.to_owned();
if cuid.len() != ::std::mem::size_of::<ConfigUid>() * 2 {
return Err(anyhow!(format!("len invalid ({})", cuid.len())));
}
let cuid: ConfigUid = parse_hex(&cuid)
.try_into()
.map_err(|_| anyhow!("Cannot parse hex number".to_string()))?;
Ok(cuid)
}
fn read_cuid(asrcb: &mut AddSecretRequest, opt: &CreateSecretOpt) -> Result<()> {
if let Some(path) = &opt.cuid {
let cuid = match read_exact_file(path, "The CUID-file") {
Ok(v) => v,
Err(_) => {
let buf = read_file(path, "The CUID-file")?;
let val: Value = serde_yaml::from_slice(&buf).context(
"The CUID-file does not contain a 128bit value or a yaml with a 'cuid' field",
)?;
try_from_val(val)?
}
};
asrcb.set_cuid(cuid);
} else if let Some(v) = &opt.cuid_hex {
asrcb.set_cuid(try_parse_u128(v, "CUID")?);
}
Ok(())
}
/// reads HKDs into memory, verifies them with the provided HKD verifier.
/// returns list of public keys or Err
/// Aborts on first error
fn read_and_verify_hkds(
hkds: &Vec<String>,
verifier: Box<dyn HkdVerifier>,
) -> Result<Vec<PKey<Public>>> {
let mut res = Vec::with_capacity(hkds.len());
for hkd in hkds {
let hk = read_file(hkd, "host-key document")?;
let certs = read_certs(&hk).with_context(|| {
format!("The provided Host Key Document in '{hkd}' is not in PEM or DER format")
})?;
if certs.len() != 1 {
warn!("The host key document in '{hkd}' contains more than one certificate!")
}
// len is 1 -> unwrap will succeed
let c = certs.first().unwrap();
verifier.verify(c)?;
res.push(c.public_key()?);
info!("Use host-key document at '{hkd}'");
}
Ok(res)
}
/// Write the generated secret (if any) to the specified output stream
fn write_secret(secret: &AddSecretType, asrcb: &AddSecretRequest) -> Result<()> {
if let AddSecretType::Association {
name,
stdout,
output_secret: secret_out,
..
} = secret
{
let gen_path: String = name
.chars()
.map(|c| if c.is_whitespace() { '_' } else { c })
.collect();
//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 {
let secret_info_path = format!("{gen_path}.yaml");
write_out(&secret_info_path, secret_info, "association secret info")?;
debug!(
"Non-confidential secret information: {:x?}",
asrcb.guest_secret()
);
warn!("Successfully wrote association info to '{secret_info_path}'");
}
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}'");
}
};
Ok(())
}

View File

@@ -0,0 +1,44 @@
// SPDX-License-Identifier: MIT
//
// Copyright IBM Corp. 2023
use crate::cli::{ListSecretOpt, ListSecretOutputType};
use anyhow::{Context, Result};
use log::warn;
use pv::{
misc::{get_writer_from_cli_file_arg, STDOUT},
uv::{ListCmd, SecretList, UvDevice, UvcSuccess},
};
/// Do a List Secrets UVC
pub fn list(opt: &ListSecretOpt) -> Result<()> {
let uv = UvDevice::open()?;
let mut cmd = ListCmd::default();
match uv.send_cmd(&mut cmd)? {
UvcSuccess::RC_SUCCESS => (),
UvcSuccess::RC_MORE_DATA => warn!("There is more data available than expected"),
};
let secret_list: SecretList = cmd.try_into()?;
let mut wr_out = get_writer_from_cli_file_arg(&opt.output)?;
match &opt.format {
ListSecretOutputType::Human => {
write!(wr_out, "{secret_list}").context("Cannot generate output")?
}
ListSecretOutputType::Yaml => write!(wr_out, "{}", serde_yaml::to_string(&secret_list)?)
.context("Cannot generate yaml output")?,
ListSecretOutputType::Bin => secret_list
.encode(&mut wr_out)
.context("Cannot encode secret list")?,
}
wr_out.flush()?;
if opt.output != STDOUT {
warn!(
"Successfully wrote the list of secrets to '{}'",
&opt.output
);
}
Ok(())
}

View File

@@ -0,0 +1,14 @@
// SPDX-License-Identifier: MIT
//
// Copyright IBM Corp. 2023
use anyhow::Result;
use log::warn;
use pv::uv::{LockCmd, UvDevice};
/// Do a Lock Secret Store UVC
pub fn lock() -> Result<()> {
UvDevice::open()?.send_cmd(&mut LockCmd)?;
warn!("Successfully locked secret store");
Ok(())
}

125
rust/pvsecret/src/main.rs Normal file
View File

@@ -0,0 +1,125 @@
// SPDX-License-Identifier: MIT
//
// Copyright IBM Corp. 2023
mod cli;
mod cmd;
use clap::CommandFactory;
use clap::Parser;
use cli::{CliOptions, Command};
use log::trace;
use pv::misc::PvLogger;
use std::process::ExitCode;
use utils::release_string;
use crate::cli::validate_cli;
static LOGGER: PvLogger = PvLogger;
static EXIT_LOGGER: u8 = 3;
const FEATURES: &[&str] = &[
"+create",
#[cfg(target_arch = "s390x")]
"+add",
#[cfg(target_arch = "s390x")]
"+lock",
#[cfg(target_arch = "s390x")]
"+list",
];
fn print_error(e: anyhow::Error, verbosity: u8) -> ExitCode {
if verbosity > 0 {
// Debug formatter also prints the whole error stack
// So only print it when on verbose
eprintln!("error: {e:?}")
} else {
eprintln!("error: {e}")
};
ExitCode::FAILURE
}
fn print_cli_error(e: clap::Error) -> ExitCode {
let ret = if e.use_stderr() {
ExitCode::FAILURE
} else {
ExitCode::SUCCESS
};
//Ignore any errors during printing of the error
let _ = e.format(&mut CliOptions::command()).print();
ret
}
fn print_version(verbosity: u8) -> anyhow::Result<()> {
println!(
"{} version {}\nCopyright IBM Corp. 2023",
env!("CARGO_PKG_NAME"),
release_string!()
);
if verbosity > 0 {
FEATURES.iter().for_each(|f| print!("{f} "));
println!("(compiled)");
println!(
"\n{}-crate {}",
env!("CARGO_PKG_NAME"),
env!("CARGO_PKG_VERSION")
);
println!("{}", pv::crate_info());
}
Ok(())
}
#[cfg(not(target_arch = "s390x"))]
fn not_supported() -> anyhow::Result<()> {
use anyhow::bail;
bail!("Command only available on s390x")
}
fn main() -> ExitCode {
let cli: CliOptions = match CliOptions::try_parse() {
Ok(cli) => match validate_cli(&cli) {
Ok(_) => cli,
Err(e) => return print_cli_error(e),
},
Err(e) => return print_cli_error(e),
};
// set up logger/std(out,err)
if let Err(e) = LOGGER.start(cli.verbose) {
//should(TM) never happen
eprintln!("Logger error: {e:?}");
return EXIT_LOGGER.into();
}
// NOTE trace verbosity is disabled in release builds
trace!("Trace verbosity, may leak secrets to command-line");
trace!("Options {cli:?}");
if cli.version {
let _ = print_version(cli.verbose);
return ExitCode::SUCCESS;
}
// perform the command selected by the user
let res = match &cli.cmd {
#[cfg(target_arch = "s390x")]
Command::Add(opt) => cmd::add(opt),
#[cfg(target_arch = "s390x")]
Command::List(opt) => cmd::list(opt),
#[cfg(target_arch = "s390x")]
Command::Lock => cmd::lock(),
#[cfg(not(target_arch = "s390x"))]
Command::Add(_) => not_supported(),
#[cfg(not(target_arch = "s390x"))]
Command::List(_) => not_supported(),
#[cfg(not(target_arch = "s390x"))]
Command::Lock => not_supported(),
Command::Create(opt) => cmd::create(opt),
Command::Version => print_version(cli.verbose),
};
match res {
Ok(_) => ExitCode::SUCCESS,
Err(e) => print_error(e, cli.verbose),
}
}