diff -Nru s390-tools-2.15.1/AUTHORS.md s390-tools-2.16.0/AUTHORS.md --- s390-tools-2.15.1/AUTHORS.md 2020-10-28 14:31:59.000000000 +0000 +++ s390-tools-2.16.0/AUTHORS.md 2021-02-19 14:46:37.000000000 +0000 @@ -9,12 +9,14 @@ - Arnd Bergmann - Axel Wirbser - Benjamin Block +- Brian C. Lane - Carsten Otte - Christian Borntraeger - Christian Ehrhardt - Christof Schmitt - Claudio Imbrenda - Clemens von Mann +- Colin Walters - Dan Horak - Despina Papadopoulou - Dimitri John Ledkov @@ -75,11 +77,13 @@ - Michael Mueller - Mijo Safradin - Mikhail Zaslonko +- Nikita Dubrovskii - Niklas Schnelle - Peter Oberparleiter - Peter Tiedemann - Philipp Kern - Philipp Rudo +- Prashanth Sundararaman - Rafael Fonseca - Raimund Schroeder - Ralph Wuerthner @@ -109,7 +113,10 @@ - Tuan Hoang - Ursula Braun - Utz Bacher +- Vance Morris - Vasily Gorbik - Viktor Mihajlovski +- Vineeth Vijayan - Volker Sameske +- Wenjia Zhang - Wolfgang Taphorn diff -Nru s390-tools-2.15.1/CHANGELOG.md s390-tools-2.16.0/CHANGELOG.md --- s390-tools-2.15.1/CHANGELOG.md 2020-10-28 14:31:59.000000000 +0000 +++ s390-tools-2.16.0/CHANGELOG.md 2021-02-19 14:46:37.000000000 +0000 @@ -1,5 +1,36 @@ Release history for s390-tools (MIT version) -------------------------------------------- +* __v2.16.0 (2021-02-19)__ + + For Linux kernel version: 5.10 / 5.11 + + Add new tool: + - hsci: New tool to manage HSCI (HiperSockets Converged Interfaces) + + Changes of existing tools: + - genprotimg: Add host-key document verification support + - genprotimg: boot: Make boot loader -march=z900 compatible + - libekmfweb: Make install directory for shared libraries configurable + - lsdasd: Add FC Endpoint Security information + - make: Add address sanitizer support + - netboot: Add version information to scripts + - netboot: Bump busybox version in pxelinux.0 build + - zdev: Add FC Endpoint Security information for DASD devices + - zdev: Add build option to update initial RAM-disk by default + - zkey-ekmfweb: Avoid sequence number clash when generating keys + - zkey/zkey-ekmfweb: Install KMS plugins into configurable location + - zkey: Add support to store LUKS2 dummy passphrase in key repository + + Bug Fixes: + - dasdfmt: Fix segfault when an incorrect option is specified + - genprotimg: Fix several build issues + - genprotimg: Require argument for 'ramdisk' and 'parmfile' options + - zcryptstats: Fix handling of partial results with many domains + - zfcpdbf: Deal with crash 7.2.9 change in caller name formatting + - zipl/boot: Fix memory use after free in stage2 + - zipl/boot: Fix potential heap overflow in stage2 + - zipl: Fix reading 4k disk's geometry + * __v2.15.1 (2020-10-28)__ For Linux kernel version: 5.9 diff -Nru s390-tools-2.15.1/cmsfs-fuse/cmsfs-fuse.c s390-tools-2.16.0/cmsfs-fuse/cmsfs-fuse.c --- s390-tools-2.15.1/cmsfs-fuse/cmsfs-fuse.c 2020-10-28 14:31:59.000000000 +0000 +++ s390-tools-2.16.0/cmsfs-fuse/cmsfs-fuse.c 2021-02-19 14:46:37.000000000 +0000 @@ -299,7 +299,7 @@ { unsigned long res; - asm volatile("cvb %0,%1" : "=d" (res) : "m" (num)); + asm volatile("cvb %0,%1" : "=d" (res) : "Q" (num)); return res & 0xffffffff; } @@ -307,7 +307,7 @@ { unsigned long long res; - asm volatile("cvd %1,%0" : "=m" (res) : "d" (num)); + asm volatile("cvd %1,%0" : "=Q" (res) : "d" (num)); return res & 0xffffffff; } @@ -2050,7 +2050,7 @@ if (blocks < 2) return 0; - while (blocks / (PTRS_PER_BLOCK + 1)) { + while (blocks / PTRS_PER_BLOCK) { levels++; blocks /= PTRS_PER_BLOCK; } @@ -3103,7 +3103,7 @@ return; } - while (blocks / (per_block + 1)) { + while (blocks / per_block) { levels++; blocks /= per_block; } diff -Nru s390-tools-2.15.1/common.mak s390-tools-2.16.0/common.mak --- s390-tools-2.15.1/common.mak 2020-10-28 14:31:59.000000000 +0000 +++ s390-tools-2.16.0/common.mak 2021-02-19 14:46:37.000000000 +0000 @@ -5,8 +5,8 @@ # The variable "DISTRELEASE" should be overwritten in rpm spec files with: # "make DISTRELEASE=%{release}" and "make install DISTRELEASE=%{release}" VERSION = 2 -RELEASE = 15 -PATCHLEVEL = 1 +RELEASE = 16 +PATCHLEVEL = 0 DISTRELEASE = build-$(shell date +%Y%m%d) S390_TOOLS_RELEASE = $(VERSION).$(RELEASE).$(PATCHLEVEL)-$(DISTRELEASE) export S390_TOOLS_RELEASE @@ -106,6 +106,11 @@ DEFAULT_CPPFLAGS = -D_GNU_SOURCE DEFAULT_LDFLAGS = -rdynamic +ifeq ("${ASAN}","1") + DEFAULT_CFLAGS += -fsanitize=address -fno-omit-frame-pointer + DEFAULT_LDFLAGS += -fsanitize=address +endif + # # Check for build dependency # @@ -174,12 +179,20 @@ # for SYSTEMDSYSTEMUNITDIR (e.g. /lib/systemd/system) SYSTEMDSYSTEMUNITDIR = USRINCLUDEDIR = $(INSTALLDIR)/usr/include +ZKEYKMSPLUGINDIR = $(USRLIB64DIR)/zkey + +ifeq ($(LIBDIR),$(INSTALLDIR)/lib) +SOINSTALLDIR = $(USRLIB64DIR) +else +SOINSTALLDIR = $(LIBDIR) +endif INSTDIRS = $(USRSBINDIR) $(USRBINDIR) $(BINDIR) $(LIBDIR) $(MANDIR) \ $(SYSCONFDIR) $(SYSCONFDIR)/sysconfig \ $(TOOLS_LIBDIR) $(TOOLS_DATADIR) \ $(ZFCPDUMP_DIR) $(SYSTEMDSYSTEMUNITDIR) \ - $(USRLIB64DIR) $(USRINCLUDEDIR) + $(USRLIB64DIR) $(USRINCLUDEDIR) $(ZKEYKMSPLUGINDIR) \ + $(SOINSTALLDIR) OWNER = $(shell id -un) GROUP = $(shell id -gn) export INSTALLDIR BINDIR LIBDIR USRLIB64DIR MANDIR OWNER GROUP @@ -264,6 +277,7 @@ @echo ' G=1 Build with gcov to collect code coverage data' @echo ' V=1 Generate verbose build output' @echo ' W=1 Build with higher warning level' + @echo ' ASAN=1 Build with address sanitizer' @echo '' @echo 'EXAMPLES' @echo ' # make clean all D=1 W=1 -j' @@ -366,9 +380,9 @@ clean_echo: $(call echocmd," CLEAN ") clean_gcov: - rm -f *.gcda *.gcno *.gcov + rm -f -- *.gcda *.gcno *.gcov clean_dep: - rm -f .*.o.d + rm -f -- .*.o.d clean: clean_echo clean_gcov clean_dep endif diff -Nru s390-tools-2.15.1/CONTRIBUTING.md s390-tools-2.16.0/CONTRIBUTING.md --- s390-tools-2.15.1/CONTRIBUTING.md 2020-10-28 14:31:59.000000000 +0000 +++ s390-tools-2.16.0/CONTRIBUTING.md 2021-02-19 14:46:37.000000000 +0000 @@ -72,7 +72,7 @@ ### Setup GitHub and local git 1. Create a fork of this repository by clicking the `Fork` button on the top - right of the [s390-tools](https://github.com/ibm-s390-tools/s390-tools) + right of the [s390-tools](https://github.com/ibm-s390-linux/s390-tools) main page 2. Clone your forked repository to your local development system @@ -84,7 +84,7 @@ s390-tools repository on GitHub ``` $ cd s390-tools - ~/s390-tools $ git remote add upstream https://github.com/ibm-s390-tools/s390-tools.git + ~/s390-tools $ git remote add upstream https://github.com/ibm-s390-linux/s390-tools.git ``` 4. Verify your remotes @@ -92,8 +92,8 @@ ~/s390-tools $ git remote -v origin https://github.com/random-developer/s390-tools.git (fetch) origin https://github.com/random-developer/s390-tools.git (push) - upstream https://github.com/ibm-s390-tools/s390-tools.git (fetch) - upstream https://github.com/ibm-s390-tools/s390-tools.git (push) + upstream https://github.com/ibm-s390-linux/s390-tools.git (fetch) + upstream https://github.com/ibm-s390-linux/s390-tools.git (push) ``` You now have two remotes: The "origin" remote points to your fork and the "upstream" remote to the official s390-tools repository. diff -Nru s390-tools-2.15.1/cpumf/chcpumf.c s390-tools-2.16.0/cpumf/chcpumf.c --- s390-tools-2.15.1/cpumf/chcpumf.c 2020-10-28 14:31:59.000000000 +0000 +++ s390-tools-2.16.0/cpumf/chcpumf.c 2021-02-19 14:46:37.000000000 +0000 @@ -91,13 +91,18 @@ int rc = EXIT_SUCCESS; FILE *fp; + if (geteuid()) { + fprintf(stderr, "Error: Must run as root\n"); + return EXIT_FAILURE; + } fp = fopen(PERF_SFB_SIZE, "r"); if (fp == NULL) { linux_error(PERF_SFB_SIZE); return EXIT_FAILURE; } if (fscanf(fp, "%ld,%ld", &cur_min_sdb, &cur_max_sdb) != 2) { - fprintf(stderr, "Error: Can not parse file " PERF_SFB_SIZE); + fprintf(stderr, "Error: Can not parse file " PERF_SFB_SIZE + "\n"); rc = EXIT_FAILURE; } else { if (*min == 0) diff -Nru s390-tools-2.15.1/cpumf/lscpumf.c s390-tools-2.16.0/cpumf/lscpumf.c --- s390-tools-2.15.1/cpumf/lscpumf.c 2020-10-28 14:31:59.000000000 +0000 +++ s390-tools-2.16.0/cpumf/lscpumf.c 2021-02-19 14:46:37.000000000 +0000 @@ -2572,6 +2572,7 @@ case 3906: return "IBM z14"; case 3907: return "IBM z14 ZR1"; case 8561: return "IBM z15"; + case 8562: return "IBM z15 Model T02"; } return "Unknown hardware model"; } @@ -3094,11 +3095,11 @@ util_prg_init(&prg); util_opt_init(opt_vec, NULL); - ret = read_info(); - if (ret == EXIT_FAILURE) - return ret; + ret = parse_args(argc, argv); + if (read_info() == EXIT_FAILURE) + return EXIT_FAILURE; - switch ((ret = parse_args(argc, argv))) { + switch (ret) { case ACTION_CNT: case ACTION_CNTALL: all = ret == ACTION_CNTALL; @@ -3114,6 +3115,7 @@ case ACTION_NONE: case ACTION_INFO: show_info(&cpumf, ret == ACTION_INFO); + ret = EXIT_SUCCESS; break; } return ret; diff -Nru s390-tools-2.15.1/dasdfmt/dasdfmt.c s390-tools-2.16.0/dasdfmt/dasdfmt.c --- s390-tools-2.15.1/dasdfmt/dasdfmt.c 2020-10-28 14:31:59.000000000 +0000 +++ s390-tools-2.16.0/dasdfmt/dasdfmt.c 2021-02-19 14:46:37.000000000 +0000 @@ -1637,7 +1637,7 @@ /* End of options string - start of devices list */ break; default: - error("Try '%s --help' for more information."); + error("Try '%s --help' for more information.", prog_name); } if (rc == -1) diff -Nru s390-tools-2.15.1/dasdfmt/dasdfmt.h s390-tools-2.16.0/dasdfmt/dasdfmt.h --- s390-tools-2.15.1/dasdfmt/dasdfmt.h 2020-10-28 14:31:59.000000000 +0000 +++ s390-tools-2.16.0/dasdfmt/dasdfmt.h 2021-02-19 14:46:37.000000000 +0000 @@ -41,6 +41,10 @@ "Full", "Quick", "Expand" }; +/* Report error, free memory, and exit */ +static void error(const char *format, ...) + __attribute__((__noreturn__, __format__(__printf__, 1, 2))); + #define DASD_PARTN_BITS 2 #define PARTN_MASK ((1 << DASD_PARTN_BITS) - 1) diff -Nru s390-tools-2.15.1/debian/changelog s390-tools-2.16.0/debian/changelog --- s390-tools-2.15.1/debian/changelog 2021-01-19 17:58:26.000000000 +0000 +++ s390-tools-2.16.0/debian/changelog 2021-02-24 05:49:54.000000000 +0000 @@ -1,3 +1,61 @@ +s390-tools (2.16.0-0ubuntu1) hirsute; urgency=medium + + * New upstream release. LP: #1914574 + * Drop patches applied upstream. + * Compile with ZDEV_ALWAYS_UPDATE_INITRD=1 option. This fixes calls to + chzdev to trigger update-initramfs -u. LP: #1892367 + * Set install path via make variables, instead of patching source + Makefiles. + * Update homepage to github.com. + + -- Dimitri John Ledkov Wed, 24 Feb 2021 05:49:54 +0000 + +s390-tools (2.15.1-1ubuntu3) hirsute; urgency=medium + + * No-change rebuild to drop the udeb package. + + -- Matthias Klose Mon, 22 Feb 2021 10:36:10 +0100 + +s390-tools (2.15.1-1ubuntu2) hirsute; urgency=medium + + * No change rebuild with fixed ownership. + + -- Dimitri John Ledkov Thu, 18 Feb 2021 00:03:00 +0000 + +s390-tools (2.15.1-1ubuntu1) hirsute; urgency=low + + * Update debian/watch for new ibm-s390-linux repo + * Update debian/control for [XC-]Package-Type + * Merge from Debian unstable. Remaining changes: + - keep providing zkey + - split some tools into individuall packages + - Drop udevadm-path.patch + - Drop ziomon package + - Linux on z Systems naming + - 0001-ziomon-Use-exit-code-0-for-version-and-help.patch + - 0001-zkey-add-initramfs-hook.patch + - 0001-zkey-on-Ubuntu-use-default-benchmarked-Argon2i-with-.patch + - 0001-dumpconf-Don-t-run-the-service-in-LXC.patch + - update-install-paths.patch + - 0010-no-pie-is-not-a-valid-option-for-ld.patch + - s390-tools-lp1903984-hirsute.patch + + -- Lukas Märdian Mon, 08 Feb 2021 12:46:12 +0100 + +s390-tools (2.15.1-2) unstable; urgency=medium + + * Remove myself as a maintainer. + + -- Philipp Kern Thu, 11 Feb 2021 07:18:40 +0100 + +s390-tools (2.15.1-1) unstable; urgency=medium + + * New upstream release + - Install new tool lsstp. + - Continue to skip out on zkey for now. + + -- Philipp Kern Sat, 05 Dec 2020 11:55:08 +0100 + s390-tools (2.15.1-0ubuntu6) hirsute; urgency=medium * debian/patches/s390-tools-lp1903984-hirsute.patch @@ -346,6 +404,15 @@ -- Dimitri John Ledkov Tue, 24 Jul 2018 17:00:03 +0100 +s390-tools (2.3.0-1) unstable; urgency=medium + + * New upstream release. + - Drop debian/patches/zipl-no-pie.patch: applied upstream. + * Add a watch file. + * Package ziomon/ziorep reporting tools. (Closes: #812588) + + -- Philipp Kern Sun, 04 Feb 2018 13:29:22 -0500 + s390-tools (2.3.0-0ubuntu4) cosmic; urgency=medium * No-change rebuild for ncurses soname changes. @@ -370,6 +437,15 @@ -- Dimitri John Ledkov Tue, 27 Feb 2018 10:49:21 +0000 +s390-tools (2.2.0-1) unstable; urgency=medium + + * New upstream release + - Drop debian/patches/systemd-bindto.patch: applied upstream. + - lsmem/chmem were moved to util-linux. + - Ship upstream's changelog. + + -- Philipp Kern Sun, 14 Jan 2018 15:36:29 +0100 + s390-tools (2.2.0-0ubuntu1) bionic; urgency=medium * New upstream release. @@ -406,6 +482,17 @@ -- Dimitri John Ledkov Wed, 23 Aug 2017 17:28:21 +0100 +s390-tools (1.37.1-1) unstable; urgency=medium + + * New upstream release + - Pass in the default CPPFLAGS/LDFLAGS from debian/rules. + * Drop the libsysfs-dev build-dependency; it is no longer used. + (Closes: #856062) + * Check for udevadm in /bin, not /sbin. (Closes: #852572) + * Replace BindTo with BindsTo in systemd unit file. (Closes: #857190) + + -- Philipp Kern Sun, 09 Jul 2017 10:07:15 +0200 + s390-tools (1.37.0-0ubuntu7) artful; urgency=medium * Drop chmem/lsmem utilities, moved to util-linux. @@ -1078,4 +1165,3 @@ * Composed zipl manpage from "zipl -h" output. -- Jochen Röhrig Mon, 23 Jul 2001 20:24:37 +0200 - diff -Nru s390-tools-2.15.1/debian/control s390-tools-2.16.0/debian/control --- s390-tools-2.15.1/debian/control 2021-01-07 11:05:49.000000000 +0000 +++ s390-tools-2.16.0/debian/control 2021-02-24 05:49:54.000000000 +0000 @@ -3,10 +3,10 @@ Priority: optional Maintainer: Ubuntu Developers XSBC-Original-Maintainer: Debian S/390 Team -Uploaders: Bastian Blank , Philipp Kern +Uploaders: Bastian Blank Build-Depends: debhelper-compat (= 13), libz-dev, quilt, gcc-multilib, libfuse-dev, libncurses-dev, libpfm4-dev, libssl-dev, libcurl4-openssl-dev, libcryptsetup-dev, libjson-c-dev, libsnmp-dev, libglib2.0-dev Standards-Version: 3.9.7 -Homepage: http://www.ibm.com/developerworks/linux/linux390/s390-tools.html +Homepage: https://github.com/ibm-s390-linux/s390-tools Package: s390-tools Architecture: s390 s390x @@ -79,7 +79,7 @@ This package contains the development library and headers. Package: s390-tools-udeb -XC-Package-Type: udeb +Package-Type: udeb Section: debian-installer Architecture: s390 s390x Depends: ${shlibs:Depends}, ${misc:Depends} @@ -89,7 +89,7 @@ fdasd. Package: s390-tools-zkey-udeb -XC-Package-Type: udeb +Package-Type: udeb Section: debian-installer Priority: standard Architecture: s390 s390x diff -Nru s390-tools-2.15.1/debian/patches/disable.patch.DEBIAN s390-tools-2.16.0/debian/patches/disable.patch.DEBIAN --- s390-tools-2.15.1/debian/patches/disable.patch.DEBIAN 1970-01-01 00:00:00.000000000 +0000 +++ s390-tools-2.16.0/debian/patches/disable.patch.DEBIAN 2020-12-05 10:55:08.000000000 +0000 @@ -0,0 +1,13 @@ +Index: s390-tools-2.15.1/Makefile +=================================================================== +--- s390-tools-2.15.1.orig/Makefile ++++ s390-tools-2.15.1/Makefile +@@ -5,7 +5,7 @@ include common.mak + + LIB_DIRS = libvtoc libutil libzds libdasd libvmdump libccw libvmcp libekmfweb + TOOL_DIRS = zipl zdump fdasd dasdfmt dasdview tunedasd \ +- tape390 osasnmpd qetharp ip_watcher qethconf scripts zconf \ ++ tape390 qetharp ip_watcher qethconf scripts zconf \ + vmconvert vmcp man mon_tools dasdinfo vmur cpuplugd ipl_tools \ + ziomon iucvterm hyptop cmsfs-fuse qethqoat zfcpdump zdsfs cpumf \ + systemd hmcdrvfs cpacfstats zdev dump2tar zkey netboot etc zpcictl \ diff -Nru s390-tools-2.15.1/debian/patches/install-iucvterm.patch s390-tools-2.16.0/debian/patches/install-iucvterm.patch --- s390-tools-2.15.1/debian/patches/install-iucvterm.patch 2021-01-07 11:31:47.000000000 +0000 +++ s390-tools-2.16.0/debian/patches/install-iucvterm.patch 2021-02-24 05:49:54.000000000 +0000 @@ -1,6 +1,8 @@ ---- a/iucvterm/src/Makefile -+++ b/iucvterm/src/Makefile -@@ -10,8 +10,8 @@ +Index: s390-tools-2.16.0/iucvterm/src/Makefile +=================================================================== +--- s390-tools-2.16.0.orig/iucvterm/src/Makefile ++++ s390-tools-2.16.0/iucvterm/src/Makefile +@@ -10,8 +10,8 @@ ALL_CPPFLAGS += -I../include ALL_CPPFLAGS += -DUSE_NLS -DGETTEXT_TEXTDOMAIN=\"$(GETTEXT_TEXTDOMAIN)\" #ALL_CPPFLAGS += -D__DEBUG__ @@ -11,8 +13,10 @@ all: $(PROGRAMS) $(SYSTOOLS) check: ---- a/iucvterm/doc/Makefile -+++ b/iucvterm/doc/Makefile +Index: s390-tools-2.16.0/iucvterm/doc/Makefile +=================================================================== +--- s390-tools-2.16.0.orig/iucvterm/doc/Makefile ++++ s390-tools-2.16.0/iucvterm/doc/Makefile @@ -2,7 +2,10 @@ include ../../common.mak @@ -25,16 +29,18 @@ all: -@@ -17,6 +20,7 @@ +@@ -17,6 +20,7 @@ install-man: $(MANS) done clean: -+ rm iucvtty.8 ++ rm -f iucvtty.8 pdf: $(MANS) for man in $(MANS); do \ ---- a/iucvterm/doc/iucvtty.1 -+++ b/iucvterm/doc/iucvtty.1 +Index: s390-tools-2.16.0/iucvterm/doc/iucvtty.1 +=================================================================== +--- s390-tools-2.16.0.orig/iucvterm/doc/iucvtty.1 ++++ s390-tools-2.16.0/iucvterm/doc/iucvtty.1 @@ -1,11 +1,11 @@ -.\" iucvtty.1 +.\" iucvtty.8 @@ -49,7 +55,7 @@ . .ds t \fBiucvtty\fP .ds i \fBiucvconn\fP -@@ -139,7 +139,7 @@ +@@ -139,7 +139,7 @@ with \fB/bin/login\fP could be: .ft CW .in +0.25in .nf @@ -58,7 +64,7 @@ .fi .in -0.25in .ft -@@ -150,7 +150,7 @@ +@@ -150,7 +150,7 @@ with \fB/sbin/sulogin\fP in single user .ft CW .in +0.25in .nf diff -Nru s390-tools-2.15.1/debian/patches/s390-tools-lp1903984-hirsute.patch s390-tools-2.16.0/debian/patches/s390-tools-lp1903984-hirsute.patch --- s390-tools-2.15.1/debian/patches/s390-tools-lp1903984-hirsute.patch 2021-01-19 17:58:26.000000000 +0000 +++ s390-tools-2.16.0/debian/patches/s390-tools-lp1903984-hirsute.patch 1970-01-01 00:00:00.000000000 +0000 @@ -1,49 +0,0 @@ -Description: zcryptstats fails - The zcryptstats tool reports data for only the last two domain IDs. - Further, it hangs if one collect data for a complete crypto card, - and then one has to kill the ssh session to get back. - Patch/commit is: - cf2311f cf2311f1f1de17435b49ba8c8697be91705ba031 - "zcryptstats: Fix handling of partial results with many domains" - - zconf/zcrypt/zcryptstats.c: code fix to pass the correct next-domain - to the subsequent CHSC call of a partial response -Author: Ingo Franzki -Origin: https://github.com/ibm-s390-tools/s390-tools/commit/cf2311f1f1de17435b49ba8c8697be91705ba031 -Bug-IBM: IBM Bugzilla 189183 -Bug-Ubuntu: https://bugs.launchpad.net/bugs/1903984 -Forwarded: not-needed -Applied-Upstream: upstream accepted with > v2.15.1 -Reviewed-by: Frank Heimes -Last-Update: 2021-01-19 ---- -This patch header follows DEP-3: http://dep.debian.net/deps/dep3/ ---- a/zconf/zcrypt/zcryptstats.c -+++ b/zconf/zcrypt/zcryptstats.c -@@ -1178,8 +1178,14 @@ - scdmd_area.request.header.code = 0x102d; - scdmd_area.request.header.length = - sizeof(struct chsc_scdmd_request); -- scdmd_area.request.first_drid.ap_index = card; -- scdmd_area.request.first_drid.domain_index = g.min_domain; -+ if (scdmd_area.response.p) { -+ scdmd_area.request.first_drid = -+ scdmd_area.response.crid; -+ } else { -+ scdmd_area.request.first_drid.ap_index = card; -+ scdmd_area.request.first_drid.domain_index = -+ g.min_domain; -+ } - scdmd_area.request.last_drid.ap_index = card; - scdmd_area.request.last_drid.domain_index = g.max_domain; - scdmd_area.request.s = 1; -@@ -1217,10 +1223,6 @@ - rc = process_apqn_measurement_data(&scdmd_area); - if (rc != 0) - break; -- -- if (scdmd_area.response.p) -- scdmd_area.request.first_drid = -- scdmd_area.response.crid; - } while (scdmd_area.response.p); - - return rc; diff -Nru s390-tools-2.15.1/debian/patches/series s390-tools-2.16.0/debian/patches/series --- s390-tools-2.15.1/debian/patches/series 2021-01-19 17:58:26.000000000 +0000 +++ s390-tools-2.16.0/debian/patches/series 2021-02-24 05:49:54.000000000 +0000 @@ -6,6 +6,4 @@ 0001-zkey-add-initramfs-hook.patch 0001-zkey-on-Ubuntu-use-default-benchmarked-Argon2i-with-.patch 0001-dumpconf-Don-t-run-the-service-in-LXC.patch -update-install-paths.patch 0010-no-pie-is-not-a-valid-option-for-ld.patch -s390-tools-lp1903984-hirsute.patch diff -Nru s390-tools-2.15.1/debian/patches/update-install-paths.patch s390-tools-2.16.0/debian/patches/update-install-paths.patch --- s390-tools-2.15.1/debian/patches/update-install-paths.patch 2021-01-07 11:31:47.000000000 +0000 +++ s390-tools-2.16.0/debian/patches/update-install-paths.patch 1970-01-01 00:00:00.000000000 +0000 @@ -1,44 +0,0 @@ -Description: update install paths -Author: Dimitri John Ledkov - ---- s390-tools-2.15.1.orig/common.mak -+++ s390-tools-2.15.1/common.mak -@@ -163,7 +163,7 @@ USRSBINDIR = $(INSTALLDIR)/usr/sbin - USRBINDIR = $(INSTALLDIR)/usr/bin - BINDIR = $(INSTALLDIR)/sbin - LIBDIR = $(INSTALLDIR)/lib --USRLIB64DIR = $(INSTALLDIR)/usr/lib64 -+USRLIB64DIR = $(INSTALLDIR)/usr/lib/s390x-linux-gnu - SYSCONFDIR = $(INSTALLDIR)/etc - MANDIR = $(INSTALLDIR)/usr/share/man - VARDIR = $(INSTALLDIR)/var ---- s390-tools-2.15.1.orig/zkey/ekmfweb/Makefile -+++ s390-tools-2.15.1/zkey/ekmfweb/Makefile -@@ -46,11 +46,11 @@ libekmfweb.dep: - install: all install-libekmfweb.dep zkey-ekmfweb.so - $(INSTALL) -d -m 755 $(DESTDIR)$(MANDIR)/man1 - $(INSTALL) -m 644 -c zkey-ekmfweb.1 $(DESTDIR)$(MANDIR)/man1 -- $(INSTALL) -d -m 755 $(DESTDIR)$(USRLIB64DIR) -- $(INSTALL) -d -m 755 $(DESTDIR)$(USRLIB64DIR)/zkey -- $(INSTALL) -g $(GROUP) -o $(OWNER) -m 755 -T zkey-ekmfweb.so $(DESTDIR)$(USRLIB64DIR)/zkey/zkey-ekmfweb.so -+ $(INSTALL) -d -m 755 $(DESTDIR)/usr/libexec -+ $(INSTALL) -d -m 755 $(DESTDIR)/usr/libexec/zkey -+ $(INSTALL) -g $(GROUP) -o $(OWNER) -m 755 -T zkey-ekmfweb.so $(DESTDIR)/usr/libexec/zkey/zkey-ekmfweb.so - - clean: - rm -f *.o zkey-ekmfweb.so install-libekmfweb.dep libekmfweb.dep - --.PHONY: all install clean -\ No newline at end of file -+.PHONY: all install clean ---- s390-tools-2.15.1.orig/zkey/kms.c -+++ s390-tools-2.15.1/zkey/kms.c -@@ -40,7 +40,7 @@ - - #define ENVVAR_ZKEY_KMS_PLUGINS "ZKEY_KMS_PLUGINS" - #define DEFAULT_KMS_PLUGINS "/etc/zkey/kms-plugins.conf" --#define KMS_PLUGIN_LOCATION "/usr/lib64/zkey" -+#define KMS_PLUGIN_LOCATION "/usr/libexec/zkey" - - #define KMS_CONFIG_FILE "kms.conf" - #define KMS_CONFIG_PROP_KMS "kms" diff -Nru s390-tools-2.15.1/debian/rules s390-tools-2.16.0/debian/rules --- s390-tools-2.15.1/debian/rules 2021-01-07 11:05:49.000000000 +0000 +++ s390-tools-2.16.0/debian/rules 2021-02-24 05:49:54.000000000 +0000 @@ -6,7 +6,7 @@ DPKG_EXPORT_BUILDFLAGS = 1 include /usr/share/dpkg/default.mk -options= V=1 HAVE_SNMP=1 SYSTEMDSYSTEMUNITDIR=/lib/systemd/system +options= V=1 HAVE_SNMP=1 SYSTEMDSYSTEMUNITDIR=/lib/systemd/system ZDEV_ALWAYS_UPDATE_INITRD=1 ZKEYKMSPLUGINDIR=/usr/libexec/zkey USRLIB64DIR=/usr/lib/s390x-linux-gnu # Enable signing in Launchpad Only for now SIGN_SIPL= diff -Nru s390-tools-2.15.1/debian/s390-tools.install s390-tools-2.16.0/debian/s390-tools.install --- s390-tools-2.15.1/debian/s390-tools.install 2021-01-07 11:05:49.000000000 +0000 +++ s390-tools-2.16.0/debian/s390-tools.install 2021-02-08 11:46:12.000000000 +0000 @@ -31,3 +31,11 @@ debian/s390-cpi-vars /lib/systemd/system-generators usr/share/initramfs-tools/ usr/lib/dracut/ + +# debugging +/sbin/zfcpdbf +/usr/share/man/man1/zfcpdbf.1 + +# lsstp +/sbin/lsstp +/usr/share/man/man8/lsstp.8 diff -Nru s390-tools-2.15.1/debian/watch s390-tools-2.16.0/debian/watch --- s390-tools-2.15.1/debian/watch 2021-01-07 11:05:49.000000000 +0000 +++ s390-tools-2.16.0/debian/watch 2021-02-08 11:46:12.000000000 +0000 @@ -1,4 +1,4 @@ version=4 opts="filenamemangle=s%(?:.*?)?v?(\d[\d.]*)\.tar\.gz%s390-tools-$1.tar.gz%" \ - https://github.com/ibm-s390-tools/s390-tools/tags \ + https://github.com/ibm-s390-linux/s390-tools/tags \ (?:.*?/)?v?(\d[\d.]*)\.tar\.gz debian uupdate diff -Nru s390-tools-2.15.1/fdasd/fdasd.c s390-tools-2.16.0/fdasd/fdasd.c --- s390-tools-2.15.1/fdasd/fdasd.c 2020-10-28 14:31:59.000000000 +0000 +++ s390-tools-2.16.0/fdasd/fdasd.c 2021-02-19 14:46:37.000000000 +0000 @@ -1232,7 +1232,7 @@ if (!anc->silent) printf("rereading partition table...\n"); - if (dasd_reread_partition_table(options.device, 1) != 0) { + if (dasd_reread_partition_table(options.device, 5) != 0) { fdasd_error(anc, unable_to_ioctl, "Error while rereading " "partition table.\nPlease reboot!"); } diff -Nru s390-tools-2.15.1/genprotimg/boot/head.S s390-tools-2.16.0/genprotimg/boot/head.S --- s390-tools-2.15.1/genprotimg/boot/head.S 2020-10-28 14:31:59.000000000 +0000 +++ s390-tools-2.16.0/genprotimg/boot/head.S 2021-02-19 14:46:37.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Entry code for stage 3a boot loader + * Entry code for stage 3a and stage 3b boot loader * * Copyright IBM Corp. 2020 * @@ -24,6 +24,8 @@ sam64 /* Initialize stack */ - lgfi %r15, STACK_ADDRESS + STACK_SIZE - STACK_FRAME_OVERHEAD + basr %r13, 0 +.Lbase: llgf %r15, .Lstack - .Lbase(%r13) brasl %r14, initialize +.Lstack: .long STACK_ADDRESS + STACK_SIZE - STACK_FRAME_OVERHEAD .previous diff -Nru s390-tools-2.15.1/genprotimg/boot/stage3a_init.S s390-tools-2.16.0/genprotimg/boot/stage3a_init.S --- s390-tools-2.15.1/genprotimg/boot/stage3a_init.S 2020-10-28 14:31:59.000000000 +0000 +++ s390-tools-2.16.0/genprotimg/boot/stage3a_init.S 2021-02-19 14:46:37.000000000 +0000 @@ -21,6 +21,8 @@ * kernel command line and the address and size of the * ramdisk. Simply ignore this by starting at 0x11000. */ - lgfi %r1, STAGE3A_ENTRY + basr %r13, 0 +.Lbase: llgf %r1, .Lstage3a_entry - .Lbase(%r13) br %r1 +.Lstage3a_entry: .long STAGE3A_ENTRY .previous diff -Nru s390-tools-2.15.1/genprotimg/boot/stage3b_reloc.S s390-tools-2.16.0/genprotimg/boot/stage3b_reloc.S --- s390-tools-2.15.1/genprotimg/boot/stage3b_reloc.S 2020-10-28 14:31:59.000000000 +0000 +++ s390-tools-2.16.0/genprotimg/boot/stage3b_reloc.S 2021-02-19 14:46:37.000000000 +0000 @@ -31,12 +31,12 @@ sigp %r1, %r0, SIGP_SET_ARCHITECTURE sam64 -.copy_stage3b: /* Location of stage3b in memory */ larl %r8, stage3b_start /* Destination for stage3b */ - lgfi %r9, STAGE3B_LOAD_ADDRESS + basr %r13, 0 +.Lbase: llgf %r9, .Lstage3b_load_address - .Lbase(%r13) /* Size of stage3b */ lghi %r11, stage3b_end - stage3b_start @@ -45,8 +45,10 @@ MEMCPY %r9, %r8, %r11 /* Branch to STAGE3B_ENTRY */ - lgfi %r9, STAGE3B_ENTRY + llgf %r9, .Lstage3b_entry - .Lbase(%r13) br %r9 +.Lstage3b_load_address: .long STAGE3B_LOAD_ADDRESS +.Lstage3b_entry: .long STAGE3B_ENTRY stage3b_start: .incbin "stage3b.bin" stage3b_end: diff -Nru s390-tools-2.15.1/genprotimg/Makefile s390-tools-2.16.0/genprotimg/Makefile --- s390-tools-2.15.1/genprotimg/Makefile 2020-10-28 14:31:59.000000000 +0000 +++ s390-tools-2.16.0/genprotimg/Makefile 2021-02-19 14:46:37.000000000 +0000 @@ -21,7 +21,7 @@ $(RECURSIVE_TARGETS): @target=`echo $@ |sed s/-recursive//`; \ for d in $(SUBDIRS); do \ - $(MAKE) -C $$d $$target; \ + $(MAKE) -C $$d $$target || exit 1; \ done .PHONY: all install clean $(RECURSIVE_TARGETS) diff -Nru s390-tools-2.15.1/genprotimg/man/genprotimg.8 s390-tools-2.16.0/genprotimg/man/genprotimg.8 --- s390-tools-2.15.1/genprotimg/man/genprotimg.8 2020-10-28 14:31:59.000000000 +0000 +++ s390-tools-2.16.0/genprotimg/man/genprotimg.8 2021-02-19 14:46:37.000000000 +0000 @@ -2,7 +2,7 @@ .\" 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 GENPROTIMG 8 "March 2020" "s390-tools" +.TH GENPROTIMG 8 "November 2020" "s390-tools" .SH NAME genprotimg \- Create a protected virtualization image @@ -10,6 +10,7 @@ .SY .B genprotimg \fB\-k\fR \fIHOST_KEY_DOCUMENT\fR... +\fB\-C\fR \fICERTIFICATE\fR... \fB\-i\fR \fIVMLINUZ\fR [\fB\-r\fR \fIRAMDISK\fR] [\fB\-p\fR \fIPARMFILE\fR] @@ -21,15 +22,19 @@ .PP Use \fBgenprotimg\fR to generate a single bootable image file with encrypted and integrity-protected parts. The command requires a kernel -image, a host-key document, and an output file name. Optionally, -specify an initial RAM filesystem, and a file containing the kernel -parameters. Should special circumstances require it, you can +image, a host-key document, certificates for the host-key document +verification, and an output file name. Optionally, specify an initial +RAM filesystem, and a file containing the kernel parameters. If the +command should be run offline, use the \fB\-\-offline\fR option and +specify the certificate revocation lists (CRLs) by using the +\fB\-\-crl\fR option. Should special circumstances require it, you can optionally specify your own keys for the encryption by using the -experimental options. In the resulting image file, a plain text boot -loader, the encrypted components for kernel, initial RAM disk, kernel -parameters, and the encrypted and integrity-protected header are -concatenated. The header contains metadata necessary for running the -guest in protected mode. +experimental options. For all certificates, CRLs, and host-key +documents, both the PEM and DER input formats are supported. In the +resulting image file, a plain text boot loader, the encrypted +components for kernel, initial RAM disk, kernel parameters, and the +encrypted and integrity-protected header are concatenated. The header +contains metadata necessary for running the guest in protected mode. .PP Use this image file as a kernel image for zipl or for a direct kernel boot using QEMU. @@ -53,6 +58,12 @@ option multiple times to enable the image to run on more than one host. .TP +\fB\-C\fR, \fB\-\-cert\fR=\fI\,FILE\/\fR +Specifies the certificate that is 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). Required. +.TP \fB\-o\fR, \fB\-\-output\fR=\fI\,OUTPUT_FILE\/\fR Specifies the output file. Required. .TP @@ -65,6 +76,20 @@ \fB\-p\fR, \fB\-\-parmfile\fR=\fI\,PARMFILE\/\fR Specifies the kernel command line stored in \fI\,PARMFILE\/\fR. Optional. .TP +\fB\-\-crl\fR=\fI\,FILE\/\fR +Specifies the revocation list that is used to check whether a +certificate of the chain of trust is revoked. Specify this option +multiple times to use multiple CRLs. Optional. +.TP +\fB\-\-offline\fR +Specifies offline mode, in which no attempt is made to download +CRLs. Optional. +.TP +\fB\-\-root\-ca\fR=\fI\,FILE\/\fR +Specifies the root CA certificate for the verification. If omitted, +the DigiCert root CA certificate installed on the system is used. Use +this only if you trust the specified certificate. Optional. +.TP \fB\-\-no-verify\fR Do not require the host-key documents to be valid. For testing purposes, do not use for a production image. Optional. @@ -77,11 +102,13 @@ Generate a protected virtualization image in \fI\,/boot/vmlinuz.pv\/\fR, using the kernel file \fI\,vmlinuz\/\fR, the initrd in \fI\,initramfs\/\fR, the kernel parameters contained in -\fI\,parmfile\/\fR, and the host-key document in \fI\,host_key.crt\/\fR: +\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 in \fI\,host_key.crt\/\fR: .PP .Vb 1 .EX -\& genprotimg \-i \fI\,vmlinuz\/\fR \-r \fI\,initramfs\/\fR \-p \fI\,parmfile\/\fR \-k \fI\,host_key.crt\/\fR \-o \fI\,/boot/vmlinuz.pv\/\fR +\& genprotimg \-i \fI\,vmlinuz\/\fR \-r \fI\,initramfs\/\fR \-p \fI\,parmfile\/\fR \-k \fI\,host_key.crt\/\fR \-C \fI\,ibm-z-host-key-signing.crt\/\fR \-C \fI\,DigiCertCA.crt \-o \fI\,/boot/vmlinuz.pv\/\fR .EE .Ve .PP diff -Nru s390-tools-2.15.1/genprotimg/src/genprotimg.c s390-tools-2.16.0/genprotimg/src/genprotimg.c --- s390-tools-2.15.1/genprotimg/src/genprotimg.c 2020-10-28 14:31:59.000000000 +0000 +++ s390-tools-2.16.0/genprotimg/src/genprotimg.c 2021-02-19 14:46:37.000000000 +0000 @@ -18,6 +18,8 @@ #include "common.h" #include "pv/pv_args.h" #include "pv/pv_image.h" +#include "utils/crypto.h" +#include "utils/curl.h" enum { LOG_LEVEL_CRITICAL = 0, @@ -117,6 +119,8 @@ signal(signals[i], SIG_DFL); } +static void __attribute__((constructor)) __init(void); +static void __attribute__((destructor)) __cleanup(void); gint main(gint argc, gchar *argv[]) { g_autoptr(PvArgs) args = pv_args_new(); @@ -177,5 +181,20 @@ rmdir_recursive(tmp_dir, NULL); remove_signal_handler(signals, G_N_ELEMENTS(signals)); g_free(tmp_dir); + g_clear_pointer(&img, pv_img_free); + g_clear_pointer(&args, pv_args_free); exit(ret); } + +static void __init(void) +{ + pv_crypto_init(); + if (curl_init() != 0) + g_abort(); +} + +static void __cleanup(void) +{ + curl_cleanup(); + pv_crypto_cleanup(); +} diff -Nru s390-tools-2.15.1/genprotimg/src/include/pv_crypto_def.h s390-tools-2.16.0/genprotimg/src/include/pv_crypto_def.h --- s390-tools-2.15.1/genprotimg/src/include/pv_crypto_def.h 2020-10-28 14:31:59.000000000 +0000 +++ s390-tools-2.16.0/genprotimg/src/include/pv_crypto_def.h 2021-02-19 14:46:37.000000000 +0000 @@ -14,6 +14,24 @@ #include "lib/zt_common.h" +/* IBM signing key subject */ +#define PV_IBM_Z_SUBJECT_COMMON_NAME "International Business Machines Corporation" +#define PV_IBM_Z_SUBJECT_COUNTRY_NAME "US" +#define PV_IBM_Z_SUBJECT_LOCALITY_NAME "Poughkeepsie" +#define PV_IBM_Z_SUBJECT_ORGANIZATIONONAL_UNIT_NAME_SUFFIX "Key Signing Service" +#define PV_IBM_Z_SUBJECT_ORGANIZATION_NAME "International Business Machines Corporation" +#define PV_IBM_Z_SUBJECT_STATE "New York" +#define PV_IMB_Z_SUBJECT_ENTRY_COUNT 6 + +/* Minimum security level for the keys/certificates used to establish a chain of + * trust (see https://www.openssl.org/docs/man1.1.1/man3/X509_VERIFY_PARAM_set_auth_level.html + * for details). + */ +#define PV_CERTS_SECURITY_LEVEL 2 + +/* SKID for DigiCert Assured ID Root CA */ +#define DIGICERT_ASSURED_ID_ROOT_CA_SKID "45EBA2AFF492CB82312D518BA7A7219DF36DC80F" + union ecdh_pub_key { struct { uint8_t x[80]; diff -Nru s390-tools-2.15.1/genprotimg/src/Makefile s390-tools-2.16.0/genprotimg/src/Makefile --- s390-tools-2.15.1/genprotimg/src/Makefile 2020-10-28 14:31:59.000000000 +0000 +++ s390-tools-2.16.0/genprotimg/src/Makefile 2021-02-19 14:46:37.000000000 +0000 @@ -15,24 +15,24 @@ WARNINGS := -Wall -Wextra -Wshadow \ -Wcast-align -Wwrite-strings -Wmissing-prototypes \ - -Wmissing-declarations -Wredundant-decls -Wnested-externs -Winline \ + -Wmissing-declarations -Wredundant-decls -Wnested-externs \ -Wno-long-long -Wuninitialized -Wconversion -Wstrict-prototypes \ - -Wpointer-arith -Werror \ + -Wpointer-arith -Werror -Wno-error=inline \ $(NULL) $(bin_PROGRAM)_SRCS := $(bin_PROGRAM).c pv/pv_stage3.c pv/pv_image.c \ pv/pv_comp.c pv/pv_hdr.c pv/pv_ipib.c utils/crypto.c utils/file_utils.c \ pv/pv_args.c utils/buffer.c pv/pv_comps.c pv/pv_error.c \ - pv/pv_opt_item.c \ + pv/pv_opt_item.c utils/curl.c \ $(NULL) $(bin_PROGRAM)_OBJS := $($(bin_PROGRAM)_SRCS:.c=.o) ALL_CFLAGS += -std=gnu11 -DPKGDATADIR=$(PKGDATADIR) \ - $(GLIB2_CFLAGS) $(LIBCRYPTO_CFLAGS) \ + $(GLIB2_CFLAGS) $(LIBCRYPTO_CFLAGS) $(LIBCURL_CFLAGS) \ $(WARNINGS) \ $(NULL) ALL_CPPFLAGS += $(INCLUDE_PARMS) -LDLIBS += $(GLIB2_LIBS) $(LIBCRYPTO_LIBS) +LDLIBS += $(GLIB2_LIBS) $(LIBCRYPTO_LIBS) $(LIBCURL_LIBS) ifneq ($(shell sh -c 'command -v pkg-config'),) @@ -40,21 +40,27 @@ GLIB2_LIBS := $(shell pkg-config --silence-errors --libs glib-2.0) LIBCRYPTO_CFLAGS := $(shell pkg-config --silence-errors --cflags libcrypto) LIBCRYPTO_LIBS := $(shell pkg-config --silence-errors --libs libcrypto) +LIBCURL_CFLAGS := $(shell pkg-config --silence-errors --cflags libcurl) +LIBCURL_LIBS := $(shell pkg-config --silence-errors --libs libcurl) else GLIB2_CFLAGS := -I/usr/include/glib-2.0 -I/usr/lib64/glib-2.0/include GLIB2_LIBS := -lglib-2.0 LIBCRYPTO_CFLAGS := LIBCRYPTO_LIBS := -lcrypto +LIBCURL_CFLAGS := +LIBCURL_LIBS := -lcurl endif BUILD_TARGETS := skip-$(bin_PROGRAM) INSTALL_TARGETS := skip-$(bin_PROGRAM) ifneq (${HAVE_OPENSSL},0) ifneq (${HAVE_GLIB2},0) +ifneq (${HAVE_LIBCURL},0) BUILD_TARGETS := $(bin_PROGRAM) INSTALL_TARGETS := install-$(bin_PROGRAM) endif endif +endif all: $(BUILD_TARGETS) @@ -98,4 +104,9 @@ "openssl-devel / libssl-dev version >= 1.1.0", \ "HAVE_OPENSSL=0", \ "-I.") + $(call check_dep, \ + "$(bin_PROGRAM)", \ + "curl/curl.h", \ + "libcurl-devel", \ + "HAVE_LIBCURL=0") touch $@ diff -Nru s390-tools-2.15.1/genprotimg/src/pv/pv_args.c s390-tools-2.16.0/genprotimg/src/pv/pv_args.c --- s390-tools-2.15.1/genprotimg/src/pv/pv_args.c 2020-10-28 14:31:59.000000000 +0000 +++ s390-tools-2.16.0/genprotimg/src/pv/pv_args.c 2021-02-19 14:46:37.000000000 +0000 @@ -18,7 +18,9 @@ static gchar summary[] = "Use genprotimg to create a protected virtualization kernel image file,\n" - "which can be loaded using zipl or QEMU."; + "which can be loaded using zipl or QEMU. For all certificates, revocation\n" + "lists, and host-key documents, both the PEM and DER input formats are\n" + "supported."; static gint pv_arg_compare(gconstpointer arg_1, gconstpointer arg_2) { @@ -97,9 +99,14 @@ return -1; } - if (!args->no_verify) { - g_set_error(err, PV_PARSE_ERROR, PR_PARSE_ERROR_MISSING_ARGUMENT, - _("Use the option '--no-verify' as the verification support is not available yet.")); + if (!args->no_verify && + (!args->untrusted_cert_paths || + g_strv_length(args->untrusted_cert_paths) == 0)) { + g_set_error( + err, PV_PARSE_ERROR, PR_PARSE_ERROR_MISSING_ARGUMENT, + _("Either specify the IBM Z signing key and (DigiCert) intermediate CA certificate\n" + "by using the '--cert' option, or use the '--no-verify' flag to disable the\n" + "host-key document verification completely (at your own risk).")); return -1; } @@ -141,6 +148,8 @@ { gchar **args_option = NULL; + if (g_str_equal(option, "--root-ca")) + args_option = &args->root_ca_path; if (g_str_equal(option, "-o") || g_str_equal(option, "--output")) args_option = &args->output_path; if (g_str_equal(option, "--x-comp-key")) @@ -211,6 +220,18 @@ _("FILE specifies a host-key document. At least\n" INDENT "one is required."), .arg_description = _("FILE") }, + { .long_name = "cert", + .short_name = 'C', + .flags = G_OPTION_FLAG_NONE, + .arg = G_OPTION_ARG_FILENAME_ARRAY, + .arg_data = &args->untrusted_cert_paths, + .description = _( + "FILE contains a certificate that is used to\n" INDENT + "establish a chain of trust for the verification\n" INDENT + "of the host-key documents. The IBM Z signing\n" INDENT + "key and intermediate CA certificate (signed\n" INDENT + "by the root CA) are required."), + .arg_description = _("FILE") }, { .long_name = "output", .short_name = 'o', .flags = G_OPTION_FLAG_FILENAME, @@ -227,7 +248,7 @@ .arg_description = _("IMAGE") }, { .long_name = "ramdisk", .short_name = 'r', - .flags = G_OPTION_FLAG_OPTIONAL_ARG | G_OPTION_FLAG_FILENAME, + .flags = G_OPTION_FLAG_FILENAME, .arg = G_OPTION_ARG_CALLBACK, .arg_data = cb_add_component, .description = _("Use RAMDISK as the initial RAM disk\n" INDENT @@ -235,12 +256,37 @@ .arg_description = _("RAMDISK") }, { .long_name = "parmfile", .short_name = 'p', - .flags = G_OPTION_FLAG_OPTIONAL_ARG | G_OPTION_FLAG_FILENAME, + .flags = G_OPTION_FLAG_FILENAME, .arg = G_OPTION_ARG_CALLBACK, .arg_data = cb_add_component, .description = _("Use the kernel parameters stored in PARMFILE\n" INDENT "(optional)."), .arg_description = _("PARMFILE") }, + { .long_name = "crl", + .short_name = 0, + .flags = G_OPTION_FLAG_NONE, + .arg = G_OPTION_ARG_FILENAME_ARRAY, + .arg_data = &args->crl_paths, + .description = _( + "FILE contains a certificate revocation list\n" INDENT + "(optional)."), + .arg_description = _("FILE") }, + { .long_name = "offline", + .short_name = 0, + .flags = G_OPTION_FLAG_NONE, + .arg = G_OPTION_ARG_NONE, + .arg_data = &args->offline, + .description = _("Don't download CRLs (optional)."), + .arg_description = NULL }, + { .long_name = "root-ca", + .short_name = 0, + .flags = G_OPTION_FLAG_FILENAME, + .arg = G_OPTION_ARG_CALLBACK, + .arg_data = cb_set_string_option, + .description = _( + "Set FILE as the trusted root CA and don't use the\n" INDENT + "root CAs that are installed on the system (optional)."), + .arg_description = _("FILE") }, { .long_name = "no-verify", .short_name = 0, .flags = G_OPTION_FLAG_NONE, @@ -378,6 +424,9 @@ g_free(args->cust_root_key_path); g_free(args->cust_comm_key_path); g_free(args->gcm_iv_path); + g_free(args->root_ca_path); + g_strfreev(args->crl_paths); + g_strfreev(args->untrusted_cert_paths); g_strfreev(args->host_keys); g_free(args->xts_key_path); g_slist_free_full(args->comps, (GDestroyNotify)pv_arg_free); diff -Nru s390-tools-2.15.1/genprotimg/src/pv/pv_args.h s390-tools-2.16.0/genprotimg/src/pv/pv_args.h --- s390-tools-2.15.1/genprotimg/src/pv/pv_args.h 2020-10-28 14:31:59.000000000 +0000 +++ s390-tools-2.16.0/genprotimg/src/pv/pv_args.h 2021-02-19 14:46:37.000000000 +0000 @@ -25,6 +25,7 @@ typedef struct { gint log_level; gint no_verify; + gboolean offline; gchar *pcf; gchar *scf; gchar *psw_addr; /* PSW address which will be used for the start of @@ -34,6 +35,11 @@ gchar *cust_comm_key_path; gchar *gcm_iv_path; gchar **host_keys; + gchar *root_ca_path; /* Trusted root CA used for the verification of the + * chain of trust (if specified). + */ + gchar **untrusted_cert_paths; + gchar **crl_paths; gchar *xts_key_path; GSList *comps; gchar *output_path; diff -Nru s390-tools-2.15.1/genprotimg/src/pv/pv_error.h s390-tools-2.16.0/genprotimg/src/pv/pv_error.h --- s390-tools-2.15.1/genprotimg/src/pv/pv_error.h 2020-10-28 14:31:59.000000000 +0000 +++ s390-tools-2.16.0/genprotimg/src/pv/pv_error.h 2021-02-19 14:46:37.000000000 +0000 @@ -28,6 +28,8 @@ PV_ERROR_IPIB_SIZE, PV_ERROR_PV_HDR_SIZE, PV_ERROR_INTERNAL, + PV_ERROR_CURL_INIT_FAILED, + PV_ERROR_DOWNLOAD_FAILED, } PvErrors; typedef enum { @@ -57,6 +59,31 @@ PV_CRYPTO_ERROR_RANDOMIZATION, PV_CRYPTO_ERROR_INVALID_PARM, PV_CRYPTO_ERROR_INVALID_KEY_SIZE, + PV_CRYPTO_ERROR_INVALID_VALIDITY_PERIOD, + PV_CRYPTO_ERROR_EXPIRED, + PV_CRYPTO_ERROR_NOT_VALID_YET, + PV_CRYPTO_ERROR_LOAD_CRL, + PV_CRYPTO_ERROR_NO_PUBLIC_KEY, + PV_CRYPTO_ERROR_INVALID_SIGNATURE_ALGORITHM, + PV_CRYPTO_ERROR_SIGNATURE_ALGORITHM_MISMATCH, + PV_CRYPTO_ERROR_INVALID_URI, + PV_CRYPTO_ERROR_CRL_DOWNLOAD_FAILED, + PV_CRYPTO_ERROR_CERT_SIGNATURE_INVALID, + PV_CRYPTO_ERROR_CRL_SIGNATURE_INVALID, + PV_CRYPTO_ERROR_CERT_SUBJECT_ISSUER_MISMATCH, + PV_CRYPTO_ERROR_CRL_SUBJECT_ISSUER_MISMATCH, + PV_CRYPTO_ERROR_NO_IBM_Z_SIGNING_KEY, + PV_CRYPTO_ERROR_MALFORMED_CERTIFICATE, + PV_CRYPTO_ERROR_NO_CRL, + PV_CRYPTO_ERROR_LOAD_ROOT_CA, + PV_CRYPTO_ERROR_LOAD_DEFAULT_CA, + PV_CRYPTO_ERROR_MALFORMED_ROOT_CA, + PV_CRYPTO_ERROR_WRONG_CA_USED, + PV_CRYPTO_ERROR_SKID_AKID_MISMATCH, + PV_CRYPTO_ERROR_NO_ISSUER_IBM_Z_FOUND, + PV_CRYPTO_ERROR_FAILED_DOWNLOAD_CRL, + PV_CRYPTO_ERROR_NO_CRLDP, + PV_CRYPTO_ERROR_CERT_REVOKED, } PvCryptoErrors; #endif diff -Nru s390-tools-2.15.1/genprotimg/src/pv/pv_image.c s390-tools-2.16.0/genprotimg/src/pv/pv_image.c --- s390-tools-2.15.1/genprotimg/src/pv/pv_image.c 2020-10-28 14:31:59.000000000 +0000 +++ s390-tools-2.16.0/genprotimg/src/pv/pv_image.c 2021-02-19 14:46:37.000000000 +0000 @@ -10,6 +10,7 @@ #include #include #include +#include #include #include #include @@ -138,22 +139,18 @@ return generate_ec_key(nid, err); } -static HostKeyList *pv_img_get_host_keys(gchar **host_cert_paths, - X509_STORE *store, gint nid, +static HostKeyList *pv_img_get_host_keys(GSList *host_keys_with_path, gint nid, GError **err) { g_autoslist(EVP_PKEY) ret = NULL; - g_assert(host_cert_paths); - - for (gchar **iterator = host_cert_paths; iterator != NULL && *iterator != NULL; - iterator++) { + for (GSList *iterator = host_keys_with_path; iterator; + iterator = iterator->next) { + x509_with_path *cert_with_path = iterator->data; g_autoptr(EVP_PKEY) host_key = NULL; - const gchar *path = *iterator; - - g_assert(path); + X509 *cert = cert_with_path->cert; - host_key = read_ec_pubkey_cert(store, nid, path, err); + host_key = read_ec_pubkey_cert(cert, nid, err); if (!host_key) return NULL; @@ -253,10 +250,172 @@ return 0; } +static gint pv_img_hostkey_verify(GSList *host_key_certs, + const gchar *root_ca_path, + const gchar *const *crl_paths, + const gchar *const *untrusted_cert_paths, + gboolean offline, GError **err) +{ + g_autoslist(x509_with_path) untrusted_certs_with_path = NULL; + g_autoptr(STACK_OF_X509) ibm_signing_certs = NULL; + g_autoptr(STACK_OF_X509) untrusted_certs = NULL; + g_autoslist(x509_pair) ibm_z_pairs = NULL; + g_autoptr(X509_STORE) trusted = NULL; + gint ibm_signing_certs_count; + + /* Load trusted root CAs of the system if and only if @root_ca_path is + * NULL, otherwise use the root CA specified by @root_ca_path. + */ + trusted = store_setup(root_ca_path, crl_paths, err); + if (!trusted) + goto error; + + if (!offline) { + g_autoptr(STACK_OF_X509_CRL) downloaded_ibm_signing_crls = NULL; + + /* Set up the download routine for the lookup of CRLs. */ + store_setup_crl_download(trusted); + + /* Try to download the CRLs of the IBM Z signing certificates + * specified in the host-key documents. Ignore download errors + * as it's still possible that a CRL is specified via command + * line. + */ + downloaded_ibm_signing_crls = try_load_crls_by_certs(host_key_certs); + + /* Add the downloaded CRLs to the store so they can be used for + * the verification later. + */ + for (int i = 0; i < sk_X509_CRL_num(downloaded_ibm_signing_crls); i++) { + X509_CRL *crl = sk_X509_CRL_value(downloaded_ibm_signing_crls, i); + + if (X509_STORE_add_crl(trusted, crl) != 1) { + g_set_error(err, PV_CRYPTO_ERROR, + PV_CRYPTO_ERROR_INTERNAL, + _("failed to load CRL")); + goto error; + } + } + } + + /* Load all untrusted certificates (e.g. IBM Z signing key and + * DigiCert intermediate CA) that are required to establish a chain of + * trust starting from the host-key document up to the root CA (if not + * otherwise specified that's the DigiCert Assured ID Root CA). + */ + untrusted_certs_with_path = load_certificates(untrusted_cert_paths, err); + if (!untrusted_certs_with_path) + goto error; + + /* Convert to STACK_OF(X509) */ + untrusted_certs = get_x509_stack(untrusted_certs_with_path); + + /* Find all IBM Z signing keys and remove them from the chain as we + * have to verify that they're valid. The last step of the chain of + * trust verification must be done manually, as the IBM Z signing keys + * are not marked as (intermediate) CA and therefore the standard + * `X509_verify_cert` function of OpenSSL cannot be used to verify the + * actual host-key documents. + */ + ibm_signing_certs = delete_ibm_signing_certs(untrusted_certs); + ibm_signing_certs_count = sk_X509_num(ibm_signing_certs); + if (ibm_signing_certs_count < 1) { + g_set_error(err, PV_CRYPTO_ERROR, PV_CRYPTO_ERROR_NO_IBM_Z_SIGNING_KEY, + _("please specify at least one IBM Z signing key")); + goto error; + } else if (ibm_signing_certs_count > 1) { + g_set_error(err, PV_CRYPTO_ERROR, PV_CRYPTO_ERROR_NO_IBM_Z_SIGNING_KEY, + _("please specify only one IBM Z signing key")); + goto error; + } + + if (store_set_verify_param(trusted, err) < 0) + goto error; + + /* Verify that the IBM Z signing keys are trustable. + * For this we must check: + * + * 1. Can a chain of trust be established ending in a root CA + * 2. Is the correct root CA ued? It has either to be the + * 'DigiCert Assured ID Root CA' or the root CA specified via + * command line. + */ + for (gint i = 0; i < sk_X509_num(ibm_signing_certs); ++i) { + X509 *ibm_signing_cert = sk_X509_value(ibm_signing_certs, i); + g_autoptr(STACK_OF_X509_CRL) ibm_signing_crls = NULL; + g_autoptr(X509_STORE_CTX) ctx = NULL; + x509_pair *pair = NULL; + + g_assert(ibm_signing_cert); + + /* Create the verification context and set the trusted + * and chain parameters. + */ + ctx = create_store_ctx(trusted, untrusted_certs, err); + if (!ctx) + goto error; + + /* Verify the IBM Z signing key */ + if (verify_cert(ibm_signing_cert, ctx, err) < 0) + goto error; + + /* Verify the build chain of trust chain. If the user passes a + * trusted root CA on the command line then the check for the + * Subject Key Identifier (SKID) is skipped, otherwise let's + * check if the SKID meets our expectation. + */ + if (!root_ca_path && + check_chain_parameters(X509_STORE_CTX_get0_chain(ctx), + get_digicert_assured_id_root_ca_skid(), + err) < 0) { + goto error; + } + + ibm_signing_crls = store_ctx_find_valid_crls(ctx, ibm_signing_cert, err); + if (!ibm_signing_crls) { + g_prefix_error(err, _("IBM Z signing key: ")); + goto error; + } + + /* Increment reference counter of @ibm_signing_cert as the + * certificate will now also be owned by @ibm_z_pairs. + */ + if (X509_up_ref(ibm_signing_cert) != 1) + g_abort(); + + pair = x509_pair_new(&ibm_signing_cert, &ibm_signing_crls); + ibm_z_pairs = g_slist_append(ibm_z_pairs, pair); + g_assert(!ibm_signing_cert); + g_assert(!ibm_signing_crls); + } + + /* Verify host-key documents by using the IBM Z signing + * certificates and the corresponding certificate revocation + * lists. + */ + for (GSList *iterator = host_key_certs; iterator; iterator = iterator->next) { + x509_with_path *host_key_with_path = iterator->data; + const gchar *host_key_path = host_key_with_path->path; + X509 *host_key = host_key_with_path->cert; + gint flags = X509_V_FLAG_CRL_CHECK; + + if (verify_host_key(host_key, ibm_z_pairs, flags, + PV_CERTS_SECURITY_LEVEL, err) < 0) { + g_prefix_error(err, "'%s': ", host_key_path); + goto error; + } + } + + return 0; +error: + g_prefix_error(err, _("Failed to verify host-key document: ")); + return -1; +} + /* read in the keys or auto-generate them */ static gint pv_img_set_keys(PvImage *img, const PvArgs *args, GError **err) { - g_autoptr(X509_STORE) store = NULL; + g_autoslist(x509_with_path) host_key_certs = NULL; g_assert(img->xts_cipher); g_assert(img->cust_comm_cipher); @@ -285,8 +444,25 @@ if (!img->cust_pub_priv_key) return -1; + /* Load all host-key documents specified on the command line */ + host_key_certs = load_certificates((const gchar **)args->host_keys, + err); + if (!host_key_certs) + return -1; + + if (!args->no_verify && + pv_img_hostkey_verify(host_key_certs, args->root_ca_path, + (const gchar * const *)args->crl_paths, + (const gchar * const *)args->untrusted_cert_paths, + args->offline, err) < 0) { + return -1; + } + + /* Loads the public keys stored in the host-key documents and verify + * that the correct elliptic curve is used. + */ img->host_pub_keys = - pv_img_get_host_keys(args->host_keys, store, img->nid, err); + pv_img_get_host_keys(host_key_certs, img->nid, err); if (!img->host_pub_keys) return -1; @@ -406,6 +582,9 @@ if (args->no_verify) g_warning(_("host-key document verification is disabled. Your workload is not secured.")); + if (args->root_ca_path) + g_warning(_("A different root CA than the default DigiCert root CA is selected. Ensure that this root CA is trusted.")); + ret->comps = pv_img_comps_new(EVP_sha512(), EVP_sha512(), EVP_sha512(), err); if (!ret->comps) return NULL; diff -Nru s390-tools-2.15.1/genprotimg/src/utils/crypto.c s390-tools-2.16.0/genprotimg/src/utils/crypto.c --- s390-tools-2.15.1/genprotimg/src/utils/crypto.c 2020-10-28 14:31:59.000000000 +0000 +++ s390-tools-2.16.0/genprotimg/src/utils/crypto.c 2021-02-19 14:46:37.000000000 +0000 @@ -16,6 +16,11 @@ #include #include #include +#include +#include +#include +#include +#include #include #include @@ -25,8 +30,49 @@ #include "pv/pv_error.h" #include "buffer.h" +#include "curl.h" #include "crypto.h" +#define DEFINE_GSLIST_MAP(t2, t1) \ + typedef t1 *(*g_slist_map_func_##t2##_##t1)(const t2 *x, \ + GError **err); \ + G_GNUC_UNUSED static GSList *g_slist_map_##t2##_##t1(const GSList *list, \ + g_slist_map_func_##t2##_##t1 func, \ + GError **err) \ + { \ + g_autoslist(t1) ret = NULL; \ + for (const GSList *iterator = list; iterator; \ + iterator = iterator->next) { \ + const t2 *value = iterator->data; \ + t1 *new_value = NULL; \ + g_assert(value); \ + new_value = func(value, err); \ + if (!new_value) \ + return NULL; \ + ret = g_slist_append(ret, g_steal_pointer(&new_value)); \ + } \ + return g_steal_pointer(&ret); \ + } + +#define DEFINE_GSLIST_TO_STACK(t1) \ + G_GNUC_UNUSED static STACK_OF(t1) *g_slist_to_stack_of_##t1(GSList **list) \ + { \ + g_assert(list); \ + g_autoptr(STACK_OF_##t1) ret = sk_##t1##_new_null(); \ + if (!ret) \ + g_abort(); \ + for (GSList *iterator = *list; iterator; \ + iterator = iterator->next) { \ + if (sk_##t1##_push(ret, g_steal_pointer(&iterator->data)) == 0) \ + g_abort(); \ + } \ + g_clear_pointer(list, g_slist_free); \ + return g_steal_pointer(&ret); \ + } + +DEFINE_GSLIST_MAP(x509_with_path, X509) +DEFINE_GSLIST_TO_STACK(X509) + EVP_MD_CTX *digest_ctx_new(const EVP_MD *md, GError **err) { g_autoptr(EVP_MD_CTX) ctx = EVP_MD_CTX_new(); @@ -359,79 +405,1340 @@ return TRUE; } -static gboolean verify_certificate(X509_STORE *store, X509 *cert, GError **err) +/* Verify that the used public key algorithm matches the subject signature + * algorithm + */ +static int check_signature_algo_match(const EVP_PKEY *pkey, const X509 *subject, + GError **err) { - g_autoptr(X509_STORE_CTX) csc = X509_STORE_CTX_new(); - if (!csc) + gint pkey_nid; + + if (!pkey) { + g_set_error(err, PV_CRYPTO_ERROR, PV_CRYPTO_ERROR_NO_PUBLIC_KEY, + _("no public key")); + return -1; + } + + if (OBJ_find_sigid_algs(X509_get_signature_nid(subject), NULL, + &pkey_nid) != 1) { + g_set_error(err, PV_CRYPTO_ERROR, + PV_CRYPTO_ERROR_INVALID_SIGNATURE_ALGORITHM, + _("unsupported signature algorithm")); + return -1; + } + + if (EVP_PKEY_type(pkey_nid) != EVP_PKEY_base_id(pkey)) { + g_set_error(err, PV_CRYPTO_ERROR, + PV_CRYPTO_ERROR_SIGNATURE_ALGORITHM_MISMATCH, + _("signature algorithm mismatch")); + return -1; + } + + return 0; +} + +static X509_CRL *load_crl_from_bio(BIO *bio) +{ + g_autoptr(X509_CRL) crl = PEM_read_bio_X509_CRL(bio, NULL, 0, NULL); + if (crl) + return g_steal_pointer(&crl); + ERR_clear_error(); + BIO_reset(bio); + + /* maybe the CRL is stored in DER format */ + crl = d2i_X509_CRL_bio(bio, NULL); + if (crl) + return g_steal_pointer(&crl); + return NULL; +} + +static X509_CRL *GByteArray_to_X509_CRL(const GByteArray *data) +{ + g_autoptr(X509_CRL) ret = NULL; + g_autoptr(BIO) bio = NULL; + + g_assert(data); + + if (data->len > INT_MAX) + return NULL; + + bio = BIO_new_mem_buf(data->data, (int)data->len); + if (!bio) g_abort(); - if (X509_STORE_CTX_init(csc, store, cert, NULL) != 1) { - g_set_error(err, PV_CRYPTO_ERROR, PV_CRYPTO_ERROR_INIT, - _("Failed to initialize X.509 store")); - return FALSE; + ret = load_crl_from_bio(bio); + if (!ret) + return NULL; + + return g_steal_pointer(&ret); +} + +static gint load_crl_from_web(const gchar *url, X509_CRL **crl, GError **err) +{ + g_autoptr(X509_CRL) tmp_crl = NULL; + g_autoptr(GByteArray) data = NULL; + g_assert(crl); + + data = curl_download(url, CRL_DOWNLOAD_TIMEOUT_MS, + CRL_DOWNLOAD_MAX_SIZE, err); + if (!data) { + g_prefix_error(err, _("unable to download CRL: ")); + return -1; } + tmp_crl = GByteArray_to_X509_CRL(data); + if (!tmp_crl) { + g_set_error(err, PV_CRYPTO_ERROR, + PV_CRYPTO_ERROR_CRL_DOWNLOAD_FAILED, + _("unable to load CRL from '%s'"), url); + return -1; + } + *crl = g_steal_pointer(&tmp_crl); + return 0; +} - if (X509_verify_cert(csc) != 1) { - g_set_error(err, PV_CRYPTO_ERROR, PV_CRYPTO_ERROR_VERIFICATION, - _("Failed to verify host-key document")); +static BIO *bio_read_from_file(const char *path) +{ + g_autoptr(BIO) bio = BIO_new_file(path, "r"); + + if (!bio) + return NULL; + + return g_steal_pointer(&bio); +} + +/* This function reads in only the first certificate and ignores all other. This + * is only relevant for the PEM file format. For the host-key document and the + * root CA this behavior is expected. + */ +X509 *load_cert_from_file(const char *path, GError **err) +{ + g_autoptr(BIO) bio = bio_read_from_file(path); + g_autoptr(X509) cert = NULL; + + if (!bio) { + g_set_error(err, PV_CRYPTO_ERROR, + PV_CRYPTO_ERROR_READ_CERTIFICATE, + _("unable to read certificate: '%s'"), path); + return NULL; + } + + cert = PEM_read_bio_X509(bio, NULL, NULL, NULL); + if (cert) + return g_steal_pointer(&cert); + ERR_clear_error(); + BIO_reset(bio); + + /* maybe the certificate is stored in DER format */ + cert = d2i_X509_bio(bio, NULL); + if (cert) + return g_steal_pointer(&cert); + + g_set_error(err, PV_CRYPTO_ERROR, PV_CRYPTO_ERROR_READ_CERTIFICATE, + _("unable to load certificate: '%s'"), path); + return NULL; +} + +/* @crl_paths is allowed to be NULL */ +static int load_crls_to_store(X509_STORE *store, const gchar *const *crl_paths, + gboolean err_out_empty_crls, GError **err) +{ + for (const gchar *const *iterator = crl_paths; + iterator != NULL && *iterator != NULL; iterator++) { + const gchar *crl_path = *iterator; + X509_LOOKUP *lookup = X509_STORE_add_lookup(store, X509_LOOKUP_file()); + int count; + + g_assert(crl_path); + + if (!lookup) { + g_set_error(err, PV_CRYPTO_ERROR, PV_CRYPTO_ERROR_INTERNAL, + _("X509 store initialization failed")); + return -1; + } + + /* support *.pem files containing multiple CRLs */ + count = X509_load_crl_file(lookup, crl_path, X509_FILETYPE_PEM); + if (count > 0) + continue; + + count = X509_load_crl_file(lookup, crl_path, X509_FILETYPE_ASN1); + if (count == 1) + continue; + + if (err_out_empty_crls) { + g_set_error(err, PV_CRYPTO_ERROR, + PV_CRYPTO_ERROR_LOAD_CRL, + _("unable to load CRL from: '%s'"), crl_path); + return -1; + } + } + + return 0; +} + +/* returns + * 0 when the certificate is valid, + * -1 when not yet valid, + * 1 when expired + */ +static int check_validity_period(const ASN1_TIME *not_before, const ASN1_TIME *not_after) +{ + if (X509_cmp_current_time(not_before) != -1) + return -1; + + if (X509_cmp_current_time(not_after) != 1) + return 1; + + return 0; +} + +static gint x509_name_entry_get_data0(X509_NAME_ENTRY *entry, const guchar **data, + gsize *data_len) +{ + const ASN1_STRING *asn1_str; + gint tmp_data_len; + + g_assert(data); + g_assert(data_len); + + asn1_str = X509_NAME_ENTRY_get_data(entry); + if (!asn1_str) + return -1; + + tmp_data_len = ASN1_STRING_length(asn1_str); + if (tmp_data_len < 0) + return -1; + + *data = ASN1_STRING_get0_data(asn1_str); + *data_len = (gsize)tmp_data_len; + return 0; +} + +/* The caller must not free *data! */ +static gint x509_name_get_data0_by_NID(X509_NAME *name, gint nid, + const guchar **data, gsize *data_len) +{ + + X509_NAME_ENTRY *entry = NULL; + gint lastpos = -1; + + lastpos = X509_NAME_get_index_by_NID(name, nid, lastpos); + if (lastpos == -1) + return -1; + + entry = X509_NAME_get_entry(name, lastpos); + if (!entry) + return -1; + + if (x509_name_entry_get_data0(entry, data, data_len) < 0) + return -1; + + return 0; +} + +/* @y must be a NULL-terminated string */ +static gboolean x509_name_data_by_nid_equal(X509_NAME *name, gint nid, + const gchar *y) +{ + const guchar *data = NULL; + gsize y_len = strlen(y); + gsize data_len; + + if (x509_name_get_data0_by_NID(name, nid, &data, &data_len) < 0) + return FALSE; + + if (data_len != y_len) + return FALSE; + + return memcmp(data, y, data_len) == 0; +} + +static gboolean own_X509_NAME_ENTRY_equal(const X509_NAME_ENTRY *x, + const X509_NAME_ENTRY *y) +{ + const ASN1_OBJECT *x_obj = X509_NAME_ENTRY_get_object(x); + const ASN1_STRING *x_data = X509_NAME_ENTRY_get_data(x); + const ASN1_OBJECT *y_obj = X509_NAME_ENTRY_get_object(y); + const ASN1_STRING *y_data = X509_NAME_ENTRY_get_data(y); + gint x_len = ASN1_STRING_length(x_data); + gint y_len = ASN1_STRING_length(y_data); + + if (x_len < 0 || x_len != y_len) return FALSE; + + /* ASN1_STRING_cmp(x_data, y_data) == 0 doesn't work because it also + * compares the type, which is sometimes different. + */ + return OBJ_cmp(x_obj, y_obj) == 0 && + memcmp(ASN1_STRING_get0_data(x_data), + ASN1_STRING_get0_data(y_data), + (unsigned long)x_len) == 0; +} + +static gboolean own_X509_NAME_equal(const X509_NAME *x, const X509_NAME *y) +{ + gint x_count = X509_NAME_entry_count(x); + gint y_count = X509_NAME_entry_count(y); + + if (x != y && (!x || !y)) + return FALSE; + + if (x_count != y_count) + return FALSE; + + for (gint i = 0; i < x_count; i++) { + const X509_NAME_ENTRY *entry_i = X509_NAME_get_entry(x, i); + gboolean entry_found = FALSE; + + for (gint j = 0; j < y_count; j++) { + const X509_NAME_ENTRY *entry_j = + X509_NAME_get_entry(y, j); + + if (own_X509_NAME_ENTRY_equal(entry_i, entry_j)) { + entry_found = TRUE; + break; + } + } + + if (!entry_found) + return FALSE; } + return TRUE; +} + +/* Checks whether the subject of @cert is a IBM signing key subject. For this we + * must check that the subject is equal to: 'C = US, ST = New York, L = + * Poughkeepsie, O = International Business Machines Corporation, CN = + * International Business Machines Corporation' and the organization unit (OUT) + * must end with the suffix ' Key Signing Service'. + */ +static gboolean has_ibm_signing_subject(X509 *cert) +{ + X509_NAME *subject = X509_get_subject_name(cert); + /* X509_NAME_entry_count is safe to be used with NULL */ + gint entry_count = X509_NAME_entry_count(subject); + g_autofree gchar *data_str = NULL; + const guchar *data; + gsize data_len; + + if (entry_count != PV_IMB_Z_SUBJECT_ENTRY_COUNT) + return FALSE; + + if (!x509_name_data_by_nid_equal(subject, NID_countryName, + PV_IBM_Z_SUBJECT_COUNTRY_NAME)) + return FALSE; + + if (!x509_name_data_by_nid_equal(subject, NID_stateOrProvinceName, + PV_IBM_Z_SUBJECT_STATE)) + return FALSE; + + if (!x509_name_data_by_nid_equal(subject, NID_localityName, + PV_IBM_Z_SUBJECT_LOCALITY_NAME)) + return FALSE; + + if (!x509_name_data_by_nid_equal(subject, NID_organizationName, + PV_IBM_Z_SUBJECT_ORGANIZATION_NAME)) + return FALSE; + + if (!x509_name_data_by_nid_equal(subject, NID_commonName, + PV_IBM_Z_SUBJECT_COMMON_NAME)) + return FALSE; + + if (x509_name_get_data0_by_NID(subject, NID_organizationalUnitName, + &data, &data_len) < 0) + return FALSE; + + /* Make sure that data_str is null-terminated as in general it cannot be + * assumed that @data is null-terminated. + */ + data_str = g_strndup((const gchar *)data, data_len); + if (!g_str_has_suffix(data_str, + PV_IBM_Z_SUBJECT_ORGANIZATIONONAL_UNIT_NAME_SUFFIX)) + return FALSE; return TRUE; } -static X509 *load_certificate(const gchar *path, GError **err) +static X509_NAME *x509_name_reorder_attributes(const X509_NAME *name, const gint nids[], + gsize nids_len) { - g_autoptr(X509) ret = NULL; - g_autoptr(BIO) bio = BIO_new_file(path, "rb"); + gint entry_count = X509_NAME_entry_count(name); + g_autoptr(X509_NAME) ret = NULL; + + if (entry_count < 0) + return NULL; + + if (nids_len != (gsize) entry_count) + return NULL; + + ret = X509_NAME_new(); + if (!ret) + g_abort(); + + for (gsize i = 0; i < nids_len; i++) { + const X509_NAME_ENTRY *entry = NULL; + gint nid = nids[i]; + gint lastpos = -1; + + lastpos = X509_NAME_get_index_by_NID((X509_NAME *)name, nid, lastpos); + if (lastpos == -1) + return NULL; + + entry = X509_NAME_get_entry(name, lastpos); + if (!entry) + return NULL; + + if (X509_NAME_add_entry(ret, entry, -1, 0) != 1) + return NULL; + } + + return g_steal_pointer(&ret); +} + +/* In RFC 5280 the attributes of a (subject/issuer) name is not mandatory + * ordered. The problem is that our certificates are not consistent in the order + * (see https://tools.ietf.org/html/rfc5280#section-4.1.2.4 for details). + * + * This function converts a correct X509_NAME into the broken one. The caller is + * responsible to free the returned value. + */ +X509_NAME *c2b_name(const X509_NAME *name) +{ + gint nids[] = { NID_countryName, NID_organizationName, NID_organizationalUnitName, + NID_localityName, NID_stateOrProvinceName, NID_commonName }; + g_autoptr(X509_NAME) broken_name = NULL; + + g_assert(name); + + /* Try to reorder the attributes */ + broken_name = x509_name_reorder_attributes(name, nids, G_N_ELEMENTS(nids)); + if (broken_name) + return g_steal_pointer(&broken_name); + return X509_NAME_dup((X509_NAME *)name); +} + +/* Verify that: subject(issuer) == issuer(crl) and SKID(issuer) == AKID(crl) */ +static gint check_crl_issuer(X509_CRL *crl, X509 *issuer, GError **err) +{ + const X509_NAME *crl_issuer = X509_CRL_get_issuer(crl); + const X509_NAME *issuer_subject = X509_get_subject_name(issuer); + AUTHORITY_KEYID *akid = NULL; + + if (!own_X509_NAME_equal(issuer_subject, crl_issuer)) { + g_autofree char *issuer_subject_str = X509_NAME_oneline(issuer_subject, + NULL, 0); + g_autofree char *crl_issuer_str = X509_NAME_oneline(crl_issuer, NULL, 0); - if (!bio) { g_set_error(err, PV_CRYPTO_ERROR, - PV_CRYPTO_ERROR_READ_CERTIFICATE, - _("Failed to read host-key document: '%s'"), path); + PV_CRYPTO_ERROR_CRL_SUBJECT_ISSUER_MISMATCH, + _("issuer mismatch:\n%s\n%s"), + issuer_subject_str, crl_issuer_str); + return -1; + } + + /* If AKID(@crl) is specified it must match with SKID(@issuer) */ + akid = X509_CRL_get_ext_d2i(crl, NID_authority_key_identifier, NULL, NULL); + if (akid && X509_check_akid(issuer, akid) != X509_V_OK) { + g_set_error(err, PV_CRYPTO_ERROR, PV_CRYPTO_ERROR_SKID_AKID_MISMATCH, + _("AKID mismatch")); + return -1; + } + + return 0; +} + +/* Verify whether a revocation list @crl is valid and is issued by @cert. For + * this multiple steps must be done: + * + * 1. verify issuer of the CRL matches with the suject name of @cert + * 2. verify the validity period of the CRL + * 3. verify the signature of the CRL + * + * Important: This function doesn't verify whether @cert is allowed to issue a + * CRL. Returns 0 if @crl is valid and issued by @cert, otherwise -1. + */ +gint check_crl_valid_for_cert(X509_CRL *crl, X509 *cert, + gint verify_flags, GError **err) +{ + EVP_PKEY *pkey = X509_get0_pubkey(cert); + + g_assert(crl); + + if (!pkey) { + g_set_error(err, PV_CRYPTO_ERROR, PV_CRYPTO_ERROR_INTERNAL, + _("failed to retrieve public key from the certificate")); + return -1; + } + + /* check that the @crl issuer matches with the subject name of @cert*/ + if (check_crl_issuer(crl, cert, err) < 0) + return -1; + + /* verify the validity period of the CRL */ + if (!(verify_flags & X509_V_FLAG_NO_CHECK_TIME)) { + const ASN1_TIME *last = X509_CRL_get0_lastUpdate(crl); + const ASN1_TIME *next = X509_CRL_get0_nextUpdate(crl); + + if (!last || !next || check_validity_period(last, next)) { + g_set_error(err, PV_CRYPTO_ERROR, + PV_CRYPTO_ERROR_INVALID_VALIDITY_PERIOD, + _("validity period is not valid")); + return -1; + } + } else { + verify_flags &= ~X509_V_FLAG_NO_CHECK_TIME; + } + + /* verify the signature */ + if (X509_CRL_verify(crl, pkey) != 1) { + g_set_error(err, PV_CRYPTO_ERROR, PV_CRYPTO_ERROR_CRL_SIGNATURE_INVALID, + _("signature is not valid")); + return -1; + } + g_assert(verify_flags == 0); + return 0; +} + +/* Given a certificate @cert try to find valid revocation lists in @ctx. If no + * valid CRL was found NULL is returned. + */ +STACK_OF_X509_CRL *store_ctx_find_valid_crls(X509_STORE_CTX *ctx, X509 *cert, + GError **err) +{ + g_autoptr(STACK_OF_X509_CRL) ret = NULL; + const gint verify_flags = 0; + X509_NAME *subject = NULL; + + subject = X509_get_subject_name(cert); + if (!subject) { + g_set_error(err, PV_CRYPTO_ERROR, PV_CRYPTO_ERROR_MALFORMED_CERTIFICATE, + _("certificate is malformed")); return NULL; } - ret = PEM_read_bio_X509(bio, NULL, 0, NULL); + ret = X509_STORE_CTX_get1_crls(ctx, subject); if (!ret) { - g_set_error(err, PV_CRYPTO_ERROR, - PV_CRYPTO_ERROR_READ_CERTIFICATE, - _("Failed to load host-key document: '%s'"), path); + /* Workaround to fix the mismatch between issuer name of the + * IBM Z signing CRLs and the IBM Z signing key subject name. + */ + g_autoptr(X509_NAME) broken_subject = c2b_name(subject); + + ret = X509_STORE_CTX_get1_crls(ctx, broken_subject); + if (!ret) { + g_set_error(err, PV_CRYPTO_ERROR, PV_CRYPTO_ERROR_NO_CRL, + _("no CRL found")); + return NULL; + } + } + + /* Filter out non-valid CRLs for @cert */ + for (gint i = 0; i < sk_X509_CRL_num(ret); i++) { + X509_CRL *crl = sk_X509_CRL_value(ret, i); + + g_assert(crl); + + /* If @crl is not valid remove it from the array and log a + * warning. + */ + if (check_crl_valid_for_cert(crl, cert, verify_flags, err) < 0) { + g_assert(err); + g_warning(_("CRL is not valid: %s"), (*err)->message); + g_clear_error(err); + + /* Remove this certificate from the list and change i-- as the + * array has changed - this is not beautfiul, but right now the + * easiest solution I came up with + */ + if (sk_X509_CRL_delete(ret, i--) != crl) + g_abort(); + + g_clear_pointer(&crl, X509_CRL_free); + } + } + + if (sk_X509_CRL_num(ret) < 1) { + g_set_error(err, PV_CRYPTO_ERROR, PV_CRYPTO_ERROR_NO_CRL, + _("no valid CRL found")); return NULL; } + return g_steal_pointer(&ret); +} + +/* Return a list of all IBM Z signing key certificates in @certs and remove them + * from the chain. Return empty stack if no IBM Z signing key is found. + */ +STACK_OF_X509 *delete_ibm_signing_certs(STACK_OF_X509 *certs) +{ + g_autoptr(STACK_OF_X509) ret = sk_X509_new_null(); + + for (gint i = 0; i < sk_X509_num(certs); i++) { + X509 *cert = sk_X509_value(certs, i); + + g_assert(cert); + + if (!has_ibm_signing_subject(cert)) + continue; + + /* Remove this certificate from the list and change i-- as the + * array has changed - this is not beautfiul, but right now the + * easiest solution I came up with. + */ + if (sk_X509_delete(certs, i--) != cert) + g_abort(); + + if (sk_X509_push(ret, g_steal_pointer(&cert)) == 0) + g_abort(); + } return g_steal_pointer(&ret); } -EVP_PKEY *read_ec_pubkey_cert(X509_STORE *store, gint nid, const gchar *path, - GError **err) +X509_STORE *store_setup(const gchar *root_ca_path, const gchar * const *crl_paths, + GError **err) +{ + g_autoptr(X509_STORE) store = X509_STORE_new(); + + g_assert(store); + + /* if @root_ca_path != NULL use the specified root CA only, otherwise use the + * default root CAs found on the system + */ + if (root_ca_path) { + X509_LOOKUP *lookup = X509_STORE_add_lookup(store, X509_LOOKUP_file()); + int count; + + if (!lookup) { + g_set_error(err, PV_CRYPTO_ERROR, PV_CRYPTO_ERROR_INTERNAL, + _("X509 store initialization failed")); + return NULL; + } + + count = X509_load_cert_file(lookup, root_ca_path, X509_FILETYPE_PEM); + if (count > 1) { + g_set_error(err, PV_CRYPTO_ERROR, + PV_CRYPTO_ERROR_LOAD_ROOT_CA, + _("multiple certificates in one PEM file is not supported: '%s'"), + root_ca_path); + return NULL; + } else if (count < 1) { + count = X509_load_cert_file(lookup, root_ca_path, + X509_FILETYPE_ASN1); + if (count != 1) { + g_set_error(err, PV_CRYPTO_ERROR, + PV_CRYPTO_ERROR_LOAD_ROOT_CA, + _("failed to load root certificate from '%s'"), + root_ca_path); + return NULL; + } + } + } else { + /* Load certificates into @store from the hardcoded OpenSSL + * default paths + */ + if (X509_STORE_set_default_paths(store) != 1) { + g_set_error(err, PV_CRYPTO_ERROR, + PV_CRYPTO_ERROR_LOAD_DEFAULT_CA, + _("failed to load system root certificates")); + return NULL; + } + } + + /* Error out if a CRL file was provided that has not at least one CRL*/ + if (load_crls_to_store(store, crl_paths, TRUE, err) < 0) + return NULL; + + return g_steal_pointer(&store); +} + +int store_set_verify_param(X509_STORE *store, GError **err) +{ + g_autoptr(X509_VERIFY_PARAM) param = NULL; + unsigned long flags = X509_V_FLAG_CRL_CHECK | + X509_V_FLAG_CRL_CHECK_ALL | + X509_V_FLAG_TRUSTED_FIRST | + X509_V_FLAG_CHECK_SS_SIGNATURE | + X509_V_FLAG_X509_STRICT | + X509_V_FLAG_POLICY_CHECK; + + /* Create a X509_VERIFY_PARAM structure, which specifies which checks + * should be done by the certificate verification operation + */ + param = X509_VERIFY_PARAM_new(); + if (!param) + g_abort(); + + /* The maximum depth level of the chain of trust for the verification of + * the IBM Z signing key is 2, i.e. IBM Z signing key -> (DigiCert) + * intermediate CA -> (DigiCert) root CA + */ + X509_VERIFY_PARAM_set_depth(param, 2); + + /* Set minimum allowed security level to at least 112 bits. */ + X509_VERIFY_PARAM_set_auth_level(param, PV_CERTS_SECURITY_LEVEL); + + /* Set verification purpose to 'Any Purpose' and specify that the + * associated trust setting of the default purpose should be used. + */ + if (X509_VERIFY_PARAM_set_purpose(param, + X509_PURPOSE_ANY | X509_TRUST_DEFAULT) != 1) + goto error; + + /* Each certificate from the chain of trust must be checked against a + * CRL to see if it has been revoked. In addition, use trusted + * certificates first mode, check signature of the last certificate, + * strict mode, and verify the policies. + */ + if (X509_VERIFY_PARAM_set_flags(param, flags) != 1) + goto error; + + if (X509_STORE_set1_param(store, param) != 1) + goto error; + + return 0; + +error: + g_set_error(err, PV_CRYPTO_ERROR, PV_CRYPTO_ERROR_INTERNAL, + _("X509 store initialization failed")); + return -1; +} + +/* @cert_paths must contain at least one element, otherwise an error is + * reported. + */ +GSList *load_certificates(const gchar *const *cert_paths, GError **err) +{ + g_autoslist(x509_with_path) ret = NULL; + + for (const gchar *const *iterator = cert_paths; + iterator != NULL && *iterator != NULL; iterator++) { + const gchar *cert_path = *iterator; + g_autoptr(X509) cert = NULL; + + g_assert(cert_path); + + cert = load_cert_from_file(cert_path, err); + if (!cert) + return NULL; + + ret = g_slist_append(ret, x509_with_path_new(cert, cert_path)); + } + if (!ret) { + g_set_error(err, PV_CRYPTO_ERROR, PV_CRYPTO_ERROR_READ_CERTIFICATE, + _("no certificates specified")); + return NULL; + } + + return g_steal_pointer(&ret); +} + +static X509 *get_cert(const x509_with_path *cert_with_path, G_GNUC_UNUSED GError **err) { - g_autoptr(EVP_PKEY) ret = NULL; g_autoptr(X509) cert = NULL; - cert = load_certificate(path, err); + g_assert(cert_with_path && cert_with_path->cert); + + cert = cert_with_path->cert; + if (X509_up_ref(cert) != 1) + g_abort(); + return g_steal_pointer(&cert); +} + +STACK_OF_X509 *get_x509_stack(const GSList *x509_with_path_list) +{ + g_autoslist(X509) certs = NULL; + g_autoptr(GError) err = NULL; + + certs = g_slist_map_x509_with_path_X509(x509_with_path_list, + get_cert, &err); + g_assert_null(err); + return g_slist_to_stack_of_X509(&certs); +} + +x509_with_path *x509_with_path_new(X509 *cert, const gchar *path) +{ + g_autoptr(x509_with_path) ret = g_new(x509_with_path, 1); + + g_assert(cert && path); + + if (X509_up_ref(cert) != 1) + g_abort(); + ret->cert = cert; + ret->path = g_strdup(path); + return g_steal_pointer(&ret); +} + +void x509_with_path_free(x509_with_path *cert) +{ if (!cert) + return; + + X509_free(cert->cert); + g_free((gchar *)cert->path); + g_free(cert); +} + +x509_pair *x509_pair_new(X509 **cert, STACK_OF_X509_CRL **crls) +{ + g_autoptr(x509_pair) ret = g_new0(x509_pair, 1); + + g_assert(cert); + g_assert(crls); + + ret->cert = g_steal_pointer(cert); + ret->crls = g_steal_pointer(crls); + return g_steal_pointer(&ret); +} + +void x509_pair_free(x509_pair *pair) +{ + if (!pair) + return; + + sk_X509_CRL_pop_free(pair->crls, X509_CRL_free); + X509_free(pair->cert); + g_free(pair); +} + +X509_STORE_CTX *create_store_ctx(X509_STORE *trusted, STACK_OF_X509 *chain, + GError **err) +{ + g_autoptr(X509_STORE_CTX) ctx = X509_STORE_CTX_new(); + + if (!ctx || !X509_STORE_CTX_init(ctx, trusted, NULL, chain)) { + g_set_error(err, PV_CRYPTO_ERROR, PV_CRYPTO_ERROR_INTERNAL, + _("X509 store initialization failed: %s"), + X509_verify_cert_error_string(X509_STORE_CTX_get_error(ctx))); return NULL; + } - if (store && !verify_certificate(store, cert, err)) { - g_prefix_error(err, - _("Failed to load host-key document: '%s': "), - path); + return g_steal_pointer(&ctx); +} + +gint verify_cert(X509 *cert, X509_STORE_CTX *ctx, GError **err) +{ + gint rc; + + X509_STORE_CTX_set_cert(ctx, cert); + rc = X509_verify_cert(ctx); + if (rc != 1) { + X509 *tmp_cert = NULL; + + tmp_cert = X509_STORE_CTX_get_current_cert(ctx); + if (tmp_cert) { + g_autofree char *subj_name = X509_NAME_oneline( + X509_get_subject_name(tmp_cert), NULL, 0); + g_set_error(err, PV_CRYPTO_ERROR, + PV_CRYPTO_ERROR_INTERNAL, + _("failed to verify certificate '%s': %s"), + subj_name, + X509_verify_cert_error_string( + X509_STORE_CTX_get_error(ctx))); + } else { + g_set_error(err, PV_CRYPTO_ERROR, + PV_CRYPTO_ERROR_INTERNAL, + _("failed to verify certificate: %s"), + X509_verify_cert_error_string( + X509_STORE_CTX_get_error(ctx))); + } + + return -1; + } + + return 0; +} + +static int security_level_to_bits(int level) +{ + static int security_bits[] = { 0, 80, 112, 128, 192, 256 }; + + g_assert(level > 0 && level < (int)G_N_ELEMENTS(security_bits)); + + return security_bits[level]; +} + +static ASN1_OCTET_STRING *digicert_assured_id_root_ca; + +const ASN1_OCTET_STRING *get_digicert_assured_id_root_ca_skid(void) +{ + pv_crypto_init(); + return digicert_assured_id_root_ca; +} + +/* Used for the caching of the downloaded CRLs */ +static GHashTable *cached_crls; + +void pv_crypto_init(void) +{ + if (digicert_assured_id_root_ca) + return; + + cached_crls = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, + (GDestroyNotify)X509_CRL_free); + digicert_assured_id_root_ca = s2i_ASN1_OCTET_STRING( + NULL, NULL, DIGICERT_ASSURED_ID_ROOT_CA_SKID); +} + +void pv_crypto_cleanup(void) +{ + if (!digicert_assured_id_root_ca) + return; + g_clear_pointer(&cached_crls, g_hash_table_destroy); + g_clear_pointer(&digicert_assured_id_root_ca, ASN1_OCTET_STRING_free); +} + +gint check_chain_parameters(const STACK_OF_X509 *chain, + const ASN1_OCTET_STRING *skid, GError **err) +{ + const ASN1_OCTET_STRING *ca_skid = NULL; + gint len = sk_X509_num(chain); + X509 *ca = NULL; + + g_assert(skid); + /* at least one root and one leaf certificate must be defined */ + g_assert(len >= 2); + + /* get the root certificate of the chain of trust */ + ca = sk_X509_value(chain, len - 1); + if (!ca) { + g_set_error(err, PV_CRYPTO_ERROR, PV_CRYPTO_ERROR_INTERNAL, + _("no root certificate found")); + return -1; + } + + ca_skid = X509_get0_subject_key_id(ca); + if (!ca_skid) { + g_set_error(err, PV_CRYPTO_ERROR, PV_CRYPTO_ERROR_MALFORMED_ROOT_CA, + _("malformed root certificate")); + return -1; + } + + if (ASN1_STRING_cmp(ca_skid, skid) != 0) { + g_set_error(err, PV_CRYPTO_ERROR, PV_CRYPTO_ERROR_WRONG_CA_USED, + _("expecting DigiCert root CA to be used")); + return -1; + } + + return 0; +} + +/* It's almost the same as X509_check_issed from OpenSSL does except that we + * don't check the key usage of the potential issuer. This means we check: + * 1. issuer_name(cert) == subject_name(issuer) + * 2. Check whether the akid(cert) (if available) matches the issuer skid + * 3. Check that the cert algrithm matches the subject algorithm + * 4. Verify the signature of certificate @cert is using the public key of + * @issuer. + */ +static gint check_host_key_issued(X509 *cert, X509 *issuer, GError **err) +{ + const X509_NAME *issuer_subject = X509_get_subject_name(issuer); + const X509_NAME *cert_issuer = X509_get_issuer_name(cert); + AUTHORITY_KEYID *akid = NULL; + + /* We cannot use X509_NAME_cmp() because it considers the order of the + * X509_NAME_Entries. + */ + if (!own_X509_NAME_equal(issuer_subject, cert_issuer)) { + g_autofree char *issuer_subject_str = + X509_NAME_oneline(issuer_subject, NULL, 0); + g_autofree char *cert_issuer_str = + X509_NAME_oneline(cert_issuer, NULL, 0); + g_set_error(err, PV_CRYPTO_ERROR, + PV_CRYPTO_ERROR_CERT_SUBJECT_ISSUER_MISMATCH, + _("Subject issuer mismatch:\n'%s'\n'%s'"), + issuer_subject_str, cert_issuer_str); + return -1; + } + + akid = X509_get_ext_d2i(cert, NID_authority_key_identifier, NULL, NULL); + if (akid && X509_check_akid(issuer, akid) != X509_V_OK) { + g_set_error(err, PV_CRYPTO_ERROR, PV_CRYPTO_ERROR_SKID_AKID_MISMATCH, + _("AKID mismatch")); + return -1; + } + + if (check_signature_algo_match(X509_get0_pubkey(issuer), cert, err) < 0) + return -1; + + if (X509_verify(cert, X509_get0_pubkey(issuer)) != 1) { + g_set_error(err, PV_CRYPTO_ERROR, + PV_CRYPTO_ERROR_CERT_SIGNATURE_INVALID, + _("Signature verification failed")); + return -1; + } + + return 0; +} + +static gboolean is_cert_revoked(X509 *cert, X509_CRL *crl) +{ + X509_REVOKED *revoked = NULL; + gint rc; + + if (!cert || !crl) + g_abort(); + + rc = X509_CRL_get0_by_serial(crl, &revoked, + (ASN1_INTEGER *)X509_get0_serialNumber(cert)); + if (rc == 0) + return FALSE; + + if (revoked) + return TRUE; + + return FALSE; +} + +/* Get the first http[s] URL from a DIST_POINT */ +static const char *get_first_dp_url(DIST_POINT *dp) +{ + GENERAL_NAMES *general_names; + + g_assert(dp); + + if (!dp->distpoint || dp->distpoint->type != 0) + return NULL; + + general_names = dp->distpoint->name.fullname; + for (gint i = 0; i < sk_GENERAL_NAME_num(general_names); i++) { + GENERAL_NAME *name = sk_GENERAL_NAME_value(general_names, i); + g_autofree const gchar *uri_str = NULL; + ASN1_STRING *uri_asn1; + const gchar *uri_data; + gint uri_data_len; + gint type; + + uri_asn1 = GENERAL_NAME_get0_value(name, &type); + if (type != GEN_URI) + continue; + uri_data_len = ASN1_STRING_length(uri_asn1); + if (uri_data_len < 0) + continue; + uri_data = (const gchar *)ASN1_STRING_get0_data(uri_asn1); + /* Make sure that uri_str is null-terminated as in general it + * cannot be assumed that @uri_data is null-terminated. + */ + uri_str = g_strndup(uri_data, + (gsize)uri_data_len); + if (g_str_has_prefix(uri_str, "http://")) + return uri_data; + if (g_str_has_prefix(uri_str, "https://")) + return uri_data; + } + return NULL; +} + +static gboolean insert_crl(X509_NAME *name, X509_CRL *crl) +{ + g_autofree gchar *key = NULL; + + g_assert(name); + + key = X509_NAME_oneline(name, NULL, 0); + if (!key) + g_abort(); + if (X509_CRL_up_ref(crl) != 1) + g_abort(); + return g_hash_table_insert(cached_crls, g_steal_pointer(&key), crl); +} + +/* Caller is responsible for free'ing */ +static X509_CRL *lookup_crl(X509_NAME *name) +{ + g_autoptr(X509_CRL) crl = NULL; + g_autofree gchar *key = NULL; + + g_assert(name); + + key = X509_NAME_oneline(name, NULL, 0); + if (!key) + g_abort(); + crl = g_hash_table_lookup(cached_crls, key); + if (crl) { + if (X509_CRL_up_ref(crl) != 1) + g_abort(); + return g_steal_pointer(&crl); + } + return NULL; +} + +/* Returns empty stack if no CRL downloaded. */ +static STACK_OF_X509_CRL *crls_download_cb(X509_STORE_CTX *ctx, X509_NAME *nm) +{ + g_autoptr(STACK_OF_X509_CRL) crls = NULL; + g_autoptr(X509_CRL) crl = NULL; + /* must not be free'd */ + X509 *cert = NULL; + + crls = sk_X509_CRL_new_null(); + if (!crls) + g_abort(); + cert = X509_STORE_CTX_get_current_cert(ctx); + if (!cert) + return g_steal_pointer(&crls); + g_assert(X509_NAME_cmp(X509_get_issuer_name(cert), nm) == 0); + crl = lookup_crl(nm); + if (!crl) { + /* ignore error */ + crl = load_crl_by_cert(cert, NULL); + if (!crl) + return g_steal_pointer(&crls); + g_assert_true(insert_crl(nm, crl)); + } + if (sk_X509_CRL_push(crls, g_steal_pointer(&crl)) == 0) + g_abort(); + return g_steal_pointer(&crls); +} + +void STACK_OF_DIST_POINT_free(STACK_OF_DIST_POINT *stack) +{ + if (!stack) + return; + + sk_DIST_POINT_pop_free(stack, DIST_POINT_free); +} + +void STACK_OF_X509_free(STACK_OF_X509 *stack) +{ + if (!stack) + return; + + sk_X509_pop_free(stack, X509_free); +} + +void STACK_OF_X509_CRL_free(STACK_OF_X509_CRL *stack) +{ + if (!stack) + return; + + sk_X509_CRL_pop_free(stack, X509_CRL_free); +} + +/* Downloaded CRLs have a higher precedence than the CRLs specified on the + * command line. + */ +static STACK_OF_X509_CRL *crls_cb(X509_STORE_CTX *ctx, X509_NAME *nm) +{ + g_autoptr(STACK_OF_X509_CRL) crls = crls_download_cb(ctx, nm); + + if (sk_X509_CRL_num(crls) > 0) + return g_steal_pointer(&crls); + return X509_STORE_CTX_get1_crls(ctx, nm); +} + +/* Set up CRL lookup with download support */ +void store_setup_crl_download(X509_STORE *st) +{ + X509_STORE_set_lookup_crls(st, crls_cb); +} + +/* Download a CRL using the URI specified in the distribution @crldp */ +static X509_CRL *load_crl_by_dist_point(DIST_POINT *crldp, GError **err) +{ + const gchar *uri = get_first_dp_url(crldp); + g_autoptr(X509_CRL) crl = NULL; + + if (!uri) { + g_set_error(err, PV_CRYPTO_ERROR, PV_CRYPTO_ERROR_INTERNAL, + _("no valid URL specified in distribution point")); return NULL; } + if (load_crl_from_web(uri, &crl, err) < 0) + return NULL; + + return g_steal_pointer(&crl); +} + +/* This function returns the first X509_CRL found from the CRL distribution + * points specified in @cert. This function could be optimized by filtering + * duplicate certificates and/or filtering duplicated URIs. + */ +X509_CRL *load_crl_by_cert(X509 *cert, GError **err) +{ + g_autoptr(STACK_OF_DIST_POINT) crldps = NULL; + g_autoptr(X509_CRL) ret = NULL; + + g_assert(cert); + + crldps = X509_get_ext_d2i(cert, NID_crl_distribution_points, NULL, NULL); + if (!crldps || sk_DIST_POINT_num(crldps) == 0) { + g_set_error(err, PV_CRYPTO_ERROR, PV_CRYPTO_ERROR_NO_CRLDP, + _("no distribution point found")); + return NULL; + } + + for (int i = 0; i < sk_DIST_POINT_num(crldps); i++) { + DIST_POINT *crldp = sk_DIST_POINT_value(crldps, i); + + g_assert(crldp); + + /* ignore error */ + ret = load_crl_by_dist_point(crldp, NULL); + if (ret) + return g_steal_pointer(&ret); + } + + g_set_error(err, PV_CRYPTO_ERROR, PV_CRYPTO_ERROR_FAILED_DOWNLOAD_CRL, + _("failed to download CRL")); + return NULL; +} + +STACK_OF_X509_CRL *try_load_crls_by_certs(GSList *certs_with_path) +{ + g_autoptr(STACK_OF_X509_CRL) ret = sk_X509_CRL_new_null(); + if (!ret) + g_abort(); + + for (GSList *iterator = certs_with_path; iterator; + iterator = iterator->next) { + x509_with_path *cert_with_path = iterator->data; + X509 *cert = cert_with_path->cert; + g_autoptr(X509_CRL) crl = NULL; + + g_assert(cert); + + /* ignore error */ + crl = load_crl_by_cert(cert, NULL); + if (!crl) + continue; + + if (sk_X509_CRL_push(ret, g_steal_pointer(&crl)) == 0) + g_abort(); + } + + return g_steal_pointer(&ret); +} + +/* Assumptions are that the issuer_crt and issuer_crl is a trusted IBM Z + * signing certificate/revocation list. This function verifies a host-key + * document. To do so multiple steps are required: + * + * 1. issuer(host_key) == subject(issuer_crt) + * 2. Signature verification + * 3. @host_key must not be expired + * 4. @host_key must not be revoked + */ +gint verify_host_key(X509 *host_key, GSList *issuer_pairs, + gint verify_flags, int level, GError **err) +{ + g_assert(host_key); + + const gint exp_security_bits = security_level_to_bits(level); + EVP_PKEY *pkey = X509_get0_pubkey(host_key); + gboolean successfully_checked = FALSE; + gint pkey_security_bits; + + if (!pkey) { + g_set_error(err, PV_CRYPTO_ERROR, PV_CRYPTO_ERROR_INTERNAL, + _("failed to retrieve public key")); + return -1; + } + + /* check key level, if necessary */ + pkey_security_bits = EVP_PKEY_security_bits(pkey); + if (exp_security_bits > 0 && pkey_security_bits < exp_security_bits) { + g_set_error(err, PV_CRYPTO_ERROR, PV_CRYPTO_ERROR_VERIFICATION, + _("not enough bits of security (%d, %d expected)"), + pkey_security_bits, exp_security_bits); + return -1; + } + + if (!(verify_flags & X509_V_FLAG_NO_CHECK_TIME)) { + const ASN1_TIME *last = X509_get_notBefore(host_key); + const ASN1_TIME *next = X509_get_notAfter(host_key); + + if (!last || !next || check_validity_period(last, next)) { + g_set_error(err, PV_CRYPTO_ERROR, + PV_CRYPTO_ERROR_INVALID_VALIDITY_PERIOD, + _("validity period is not valid")); + return -1; + } + } else { + verify_flags &= ~X509_V_FLAG_NO_CHECK_TIME; + } + + /* Verify that the host_key was issued by a certificate and that it + * wasn't revoked. + */ + for (GSList *iterator = issuer_pairs; iterator; + iterator = iterator->next) { + const x509_pair *pair = iterator->data; + STACK_OF_X509_CRL *issuer_crls = NULL; + X509 *issuer_cert = NULL; + + g_assert(pair); + + issuer_cert = pair->cert; + issuer_crls = pair->crls; + + g_assert(issuer_cert); + + /* Verify that the issuer(host_key) == subject(issuer_cert) and + * that the signature is valid + */ + if (check_host_key_issued(host_key, issuer_cert, NULL) < 0) + continue; + + /* Check against CRL */ + if (verify_flags & X509_V_FLAG_CRL_CHECK) { + gboolean crl_checked = FALSE; + + verify_flags &= ~X509_V_FLAG_CRL_CHECK; + for (gint i = 0; i < sk_X509_CRL_num(issuer_crls); i++) { + X509_CRL *issuer_crl = + sk_X509_CRL_value(issuer_crls, i); + + g_assert(issuer_crl); + + if (is_cert_revoked(host_key, issuer_crl)) { + g_set_error(err, PV_CRYPTO_ERROR, + PV_CRYPTO_ERROR_CERT_REVOKED, + _("certificate revoked")); + return -1; + } + + crl_checked = TRUE; + } + + if (!crl_checked) { + g_set_error(err, PV_CRYPTO_ERROR, + PV_CRYPTO_ERROR_INTERNAL, + _("no valid CRL found")); + return -1; + } + successfully_checked = TRUE; + break; + } + } + + if (!successfully_checked) { + g_set_error(err, PV_CRYPTO_ERROR, + PV_CRYPTO_ERROR_NO_ISSUER_IBM_Z_FOUND, + _("no IBM Z signing key that issued this host-key document found")); + return -1; + } + + /* were some unsupported flags specified? */ + g_assert(verify_flags == 0); + return 0; +} + +EVP_PKEY *read_ec_pubkey_cert(X509 *cert, gint nid, + GError **err) +{ + g_autoptr(EVP_PKEY) ret = NULL; + ret = X509_get_pubkey(cert); if (!ret) { g_set_error(err, PV_CRYPTO_ERROR, PV_CRYPTO_ERROR_INVALID_PARM, - _("Failed to get public key from host-key document: '%s'"), - path); + _("Failed to get public key from host-key document")); return NULL; } if (!certificate_uses_correct_curve(ret, nid, err)) { g_prefix_error(err, - _("Failed to load host-key document: '%s': "), - path); + _("Host-key document doesn\'t use correct EC curve")); return NULL; } diff -Nru s390-tools-2.15.1/genprotimg/src/utils/crypto.h s390-tools-2.16.0/genprotimg/src/utils/crypto.h --- s390-tools-2.15.1/genprotimg/src/utils/crypto.h 2020-10-28 14:31:59.000000000 +0000 +++ s390-tools-2.16.0/genprotimg/src/utils/crypto.h 2021-02-19 14:46:37.000000000 +0000 @@ -11,14 +11,18 @@ #define PV_UTILS_CRYPTO_H #include +#include #include #include #include #include #include +#include #include +#include #include #include +#include #include #include "common.h" @@ -33,6 +37,9 @@ #define AES_256_XTS_TWEAK_SIZE 16 #define AES_256_XTS_KEY_SIZE 64 +#define CRL_DOWNLOAD_TIMEOUT_MS 3000 +#define CRL_DOWNLOAD_MAX_SIZE (1024 * 1024) /* in bytes */ + enum PvCryptoMode { PV_ENCRYPT, PV_DECRYPT, @@ -40,7 +47,34 @@ typedef GSList HostKeyList; +/* play nice with g_autoptr */ +typedef STACK_OF(DIST_POINT) STACK_OF_DIST_POINT; +typedef STACK_OF(X509) STACK_OF_X509; +typedef STACK_OF(X509_CRL) STACK_OF_X509_CRL; + +void STACK_OF_DIST_POINT_free(STACK_OF_DIST_POINT *stack); +void STACK_OF_X509_free(STACK_OF_X509 *stack); +void STACK_OF_X509_CRL_free(STACK_OF_X509_CRL *stack); + +typedef struct { + X509 *cert; + const gchar *path; +} x509_with_path; + +x509_with_path *x509_with_path_new(X509 *cert, const gchar *path); +void x509_with_path_free(x509_with_path *cert); + +typedef struct { + X509 *cert; + STACK_OF_X509_CRL *crls; +} x509_pair; + +x509_pair *x509_pair_new(X509 **cert, STACK_OF_X509_CRL **crls); +void x509_pair_free(x509_pair *pair); + /* Register auto cleanup functions */ +WRAPPED_G_DEFINE_AUTOPTR_CLEANUP_FUNC(ASN1_INTEGER, ASN1_INTEGER_free) +WRAPPED_G_DEFINE_AUTOPTR_CLEANUP_FUNC(ASN1_OCTET_STRING, ASN1_OCTET_STRING_free) WRAPPED_G_DEFINE_AUTOPTR_CLEANUP_FUNC(BIGNUM, BN_free) WRAPPED_G_DEFINE_AUTOPTR_CLEANUP_FUNC(BIO, BIO_free_all) WRAPPED_G_DEFINE_AUTOPTR_CLEANUP_FUNC(BN_CTX, BN_CTX_free) @@ -51,10 +85,18 @@ WRAPPED_G_DEFINE_AUTOPTR_CLEANUP_FUNC(EVP_MD_CTX, EVP_MD_CTX_free) WRAPPED_G_DEFINE_AUTOPTR_CLEANUP_FUNC(EVP_PKEY, EVP_PKEY_free) WRAPPED_G_DEFINE_AUTOPTR_CLEANUP_FUNC(EVP_PKEY_CTX, EVP_PKEY_CTX_free) +WRAPPED_G_DEFINE_AUTOPTR_CLEANUP_FUNC(STACK_OF_DIST_POINT, STACK_OF_DIST_POINT_free); +WRAPPED_G_DEFINE_AUTOPTR_CLEANUP_FUNC(STACK_OF_X509, STACK_OF_X509_free); +WRAPPED_G_DEFINE_AUTOPTR_CLEANUP_FUNC(STACK_OF_X509_CRL, STACK_OF_X509_CRL_free); WRAPPED_G_DEFINE_AUTOPTR_CLEANUP_FUNC(X509, X509_free) +WRAPPED_G_DEFINE_AUTOPTR_CLEANUP_FUNC(X509_CRL, X509_CRL_free) WRAPPED_G_DEFINE_AUTOPTR_CLEANUP_FUNC(X509_LOOKUP, X509_LOOKUP_free) +WRAPPED_G_DEFINE_AUTOPTR_CLEANUP_FUNC(X509_NAME, X509_NAME_free) +WRAPPED_G_DEFINE_AUTOPTR_CLEANUP_FUNC(x509_pair, x509_pair_free) WRAPPED_G_DEFINE_AUTOPTR_CLEANUP_FUNC(X509_STORE, X509_STORE_free) WRAPPED_G_DEFINE_AUTOPTR_CLEANUP_FUNC(X509_STORE_CTX, X509_STORE_CTX_free) +WRAPPED_G_DEFINE_AUTOPTR_CLEANUP_FUNC(X509_VERIFY_PARAM, X509_VERIFY_PARAM_free) +WRAPPED_G_DEFINE_AUTOPTR_CLEANUP_FUNC(x509_with_path, x509_with_path_free) union cmp_index { struct { @@ -79,8 +121,37 @@ const Buffer *iv_or_tweak; }; -EVP_PKEY *read_ec_pubkey_cert(X509_STORE *store, gint nid, const gchar *path, - GError **err); +int check_crl_valid_for_cert(X509_CRL *crl, X509 *cert, + gint verify_flags, GError **err); +void pv_crypto_init(void); +void pv_crypto_cleanup(void); +const ASN1_OCTET_STRING *get_digicert_assured_id_root_ca_skid(void); +gint verify_host_key(X509 *host_key, GSList *issuer_pairs, + gint verify_flags, int level, GError **err); +X509 *load_cert_from_file(const char *path, GError **err); +X509_CRL *load_crl_from_file(const gchar *path, GError **err); +GSList *load_certificates(const gchar *const *cert_paths, GError **err); +STACK_OF_X509 *get_x509_stack(const GSList *x509_with_path_list); +X509_STORE *store_setup(const gchar *root_ca_path, + const gchar * const *crl_paths, + GError **err); +int store_set_verify_param(X509_STORE *store, GError **err); +X509_CRL *load_crl_by_cert(X509 *cert, GError **err); +STACK_OF_X509_CRL *try_load_crls_by_certs(GSList *certs_with_path); +gint check_chain_parameters(const STACK_OF_X509 *chain, + const ASN1_OCTET_STRING *skid, GError **err); +X509_NAME *c2b_name(const X509_NAME *name); + +STACK_OF_X509 *delete_ibm_signing_certs(STACK_OF_X509 *certs); +STACK_OF_X509_CRL *store_ctx_find_valid_crls(X509_STORE_CTX *ctx, X509 *cert, + GError **err); +X509_STORE_CTX *create_store_ctx(X509_STORE *trusted, STACK_OF_X509 *chain, + GError **err); +gint verify_cert(X509 *cert, X509_STORE_CTX *ctx, GError **err); +X509_CRL *get_first_valid_crl(X509_STORE_CTX *ctx, X509 *cert, GError **err); +void store_setup_crl_download(X509_STORE *st); +EVP_PKEY *read_ec_pubkey_cert(X509 *cert, gint nid, GError **err); + Buffer *compute_exchange_key(EVP_PKEY *cust, EVP_PKEY *host, GError **err); Buffer *generate_aes_key(guint size, GError **err); Buffer *generate_aes_iv(guint size, GError **err); diff -Nru s390-tools-2.15.1/genprotimg/src/utils/curl.c s390-tools-2.16.0/genprotimg/src/utils/curl.c --- s390-tools-2.15.1/genprotimg/src/utils/curl.c 1970-01-01 00:00:00.000000000 +0000 +++ s390-tools-2.16.0/genprotimg/src/utils/curl.c 2021-02-19 14:46:37.000000000 +0000 @@ -0,0 +1,121 @@ +/* + * Libcurl utils + * + * 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. + */ + +#include +#include +#include +#include + +#include "lib/zt_common.h" +#include "pv/pv_error.h" + +#include "curl.h" + +struct UserData { + GByteArray *buffer; + guint max_size; +}; + +static size_t write_callback(char *ptr, size_t size, size_t nmemb, void *userdata) +{ + g_assert(userdata); + struct UserData *data = (struct UserData *)userdata; + GByteArray *buffer = data->buffer; + guint64 actual_size; + size_t err; + + g_assert(buffer); + + if (!g_uint64_checked_mul(&actual_size, size, nmemb)) + g_abort(); + + /* Signal an error condition by returning a amount that differs + * from the amount passed to the callback. This results in a + * CURLE_WRITE_ERROR. + */ + err = actual_size + 1; + + if (actual_size > G_MAXUINT) + return err; + + data->buffer = g_byte_array_append(buffer, (guchar *)ptr, (guint)actual_size); + if (data->buffer->len > data->max_size) + return err; + + return actual_size; +} + +gint curl_init(void) +{ + if (curl_global_init(CURL_GLOBAL_ALL) != 0) + return -1; + return 0; +} + +void curl_cleanup(void) +{ + curl_global_cleanup(); +} + +GByteArray *curl_download(const gchar *url, long timeout_ms, guint max_size, + GError **err) +{ + g_autoptr(GByteArray) ret = NULL; + g_autoptr(CURL) handle = NULL; + g_autofree gchar *agent = NULL; + struct UserData userdata; + CURLcode rc; + + /* set up curl session */ + handle = curl_easy_init(); + if (!handle) + g_abort(); + + /* follow redirection */ + rc = curl_easy_setopt(handle, CURLOPT_FOLLOWLOCATION, 1l); + if (rc != CURLE_OK) + goto curl_err; + rc = curl_easy_setopt(handle, CURLOPT_TIMEOUT_MS, timeout_ms); + if (rc != CURLE_OK) + goto curl_err; + rc = curl_easy_setopt(handle, CURLOPT_NOSIGNAL, 1l); + if (rc != CURLE_OK) + goto curl_err; + agent = g_strdup_printf("%s/%s", tool_name, RELEASE_STRING); + rc = curl_easy_setopt(handle, CURLOPT_USERAGENT, agent); + if (rc != CURLE_OK) + goto curl_err; + rc = curl_easy_setopt(handle, CURLOPT_WRITEFUNCTION, write_callback); + if (rc != CURLE_OK) + goto curl_err; + ret = g_byte_array_new(); + userdata.buffer = ret; + userdata.max_size = max_size; + rc = curl_easy_setopt(handle, CURLOPT_WRITEDATA, (void *)&userdata); + if (rc != CURLE_OK) + goto curl_err; + rc = curl_easy_setopt(handle, CURLOPT_URL, url); + if (rc != CURLE_OK) + goto curl_err; + + rc = curl_easy_perform(handle); + if (rc != CURLE_OK) { + g_set_error(err, PV_ERROR, PV_ERROR_DOWNLOAD_FAILED, + _("download failed: %s"), curl_easy_strerror(rc)); + return NULL; + } + + return g_steal_pointer(&ret); +curl_err: + g_set_error(err, PV_ERROR, + PV_ERROR_CURL_INIT_FAILED, + _("cURL initialization failed: %s"), + curl_easy_strerror(rc)); + return NULL; +} diff -Nru s390-tools-2.15.1/genprotimg/src/utils/curl.h s390-tools-2.16.0/genprotimg/src/utils/curl.h --- s390-tools-2.15.1/genprotimg/src/utils/curl.h 1970-01-01 00:00:00.000000000 +0000 +++ s390-tools-2.16.0/genprotimg/src/utils/curl.h 2021-02-19 14:46:37.000000000 +0000 @@ -0,0 +1,25 @@ +/* + * Libcurl utils + * + * 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. + */ + +#ifndef PV_UTILS_LIBCURL_H +#define PV_UTILS_LIBCURL_H + +#include +#include + +#include "common.h" + +WRAPPED_G_DEFINE_AUTOPTR_CLEANUP_FUNC(CURL, curl_easy_cleanup) + +GByteArray *curl_download(const gchar *url, long timeout_ms, guint max_size, + GError **err); +gint curl_init(void); +void curl_cleanup(void); + +#endif /* PV_UTILS_LIBCURL_H */ diff -Nru s390-tools-2.15.1/hsci/hsci s390-tools-2.16.0/hsci/hsci --- s390-tools-2.15.1/hsci/hsci 1970-01-01 00:00:00.000000000 +0000 +++ s390-tools-2.16.0/hsci/hsci 2021-02-19 14:46:37.000000000 +0000 @@ -0,0 +1,436 @@ +#!/bin/bash +# +# hsci - Tool to manage HiperSockets Converged Interfaces (HSCI) +# +# 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. +# + +hsdev="" +ndev="" +hsci="" +hsdev_mac="" +hsif_pnetid="" +netif_pnetid="" +hsci_pnetid="" + +function usage { +cat <<-EOD +Usage: hsci COMMAND [OPTION] + +This tool is designed to control and show HSCI (HiperSockets Converged +Interfaces) settings. A HiperSockets interface and an external network +interface are converged into an HSCI interface. + +COMMANDS + add HIPERSOCKETS_DEV NET_DEV Adds an HSCI interface + del HSCI_NAME Deletes an HSCI interface + show Lists the configured HSCI interfaces + +OPTIONS: + -v, --version Prints the version number of the hsci tool and exits + -h, --help Displays the help information for the command +EOD +} + +function prereqs_check { + if ! [ -x "$(command -v ip)" ]; then + echo "Error: No iproute2 installed on this system" >&2 + return 1 + fi +} + +function check_pnetids { + # get PNETID of the HS + local hsif_pnetids="" + local netif_pnetids="" + + if [ -e /sys/class/net/$hsdev/device/util_string ]; then + hsif_pnetids="$(cat /sys/class/net/$hsdev/device/util_string | tr -d '\000' | iconv -f IBM-1047 -t ASCII)" + else + if [ -e /sys/class/net/$hsdev/device/chpid ]; then + chpid="$(cat /sys/class/net/$hsdev/device/chpid | tr [:upper:] [:lower:])" + hsif_pnetids="$(cat /sys/devices/css0/chp0.$chpid/util_string | tr -d '\000' | iconv -f IBM-1047 -t ASCII)" + fi + fi + if [ "$hsif_pnetids" != "" ]; then + port_hsif="$(cat /sys/class/net/$hsdev/dev_port)" + (( idx=16*$port_hsif+1 )) + (( end=$idx+15 )) + hsif_pnetid="$(echo "$hsif_pnetids" | cut -c $idx-$end | tr -d ' ')" + fi + + # get PNETID of the NET_DEV + if [ -e /sys/class/net/$ndev/device/util_string ]; then + netif_pnetids="$(cat /sys/class/net/$ndev/device/util_string | tr -d '\000' | iconv -f IBM-1047 -t ASCII)" + else + if [ -e /sys/class/net/$ndev/device/chpid ]; then + chpid="$(cat /sys/class/net/$ndev/device/chpid | tr [:upper:] [:lower:])" + netif_pnetids="$(cat /sys/devices/css0/chp0.$chpid/util_string | tr -d '\000' | iconv -f IBM-1047 -t ASCII)" + fi + fi + if [ "$netif_pnetids" != "" ]; then + port_netif="$(cat /sys/class/net/$ndev/dev_port)" + (( idx=16*$port_netif+1 )) + (( end=$idx+15 )) + netif_pnetid="$(echo "$netif_pnetids" | cut -c $idx-$end | tr -d ' ')" + fi + + #Check PNETIDs + if [ "$hsif_pnetid" != "" ] && [ "$netif_pnetid" != "" ] && [ "$netif_pnetid" != "$hsif_pnetid" ]; then + echo "Error: $hsdev and $ndev have different PNETIDs! They are $hsif_pnetid and $netif_pnetid respectively" >&2 + return 1 + fi + + if [ "$hsif_pnetid" != "" ] && [ "$netif_pnetid" != "" ] && [ "$netif_pnetid" == "$hsif_pnetid" ]; then + hsci_pnetid=$hsif_pnetid + fi +} + +function verify_precon { + echo "Verifying net dev $ndev and HiperSockets dev $hsdev" + + if [ ! -e /sys/class/net/$hsdev ]; then + echo "Error: $hsdev does not exist" >&2 + return 1 + fi + if [ "$(cat /sys/class/net/$hsdev/device/card_type)" != "HiperSockets" ]; then + echo "Error: $hsdev is not a HiperSockets device" >&2 + return 1 + fi + if [ "$(cat /sys/class/net/$hsdev/device/layer2)" != "1" ]; then + echo "Error: $hsdev is not in layer 2 mode" >&2 + return 1 + fi + if [ ! -e /sys/class/net/$hsdev/device/vnicc/bridge_invisible ]; then + echo "Error: Missing vnic-characteristics support" >&2 + return 1 + fi + if [ "$(cat /sys/class/net/$hsdev/device/vnicc/bridge_invisible)" == "n/a" ]; then + echo "Error: $hsdev does not support vnicc" >&2 + return 1 + fi + if [ $(ip link show $hsdev | grep UP | wc -l) -eq 0 ]; then + echo "Error: $hsdev is not in state UP" >&2 + return 1 + fi + if [ $(bridge -d link show dev $hsdev self | grep learning_sync | wc -l) -eq 0 ]; then + echo "Error: $hsdev does not support attribute learning_sync" >&2 + return 1 + fi + if [ $(ip link show $hsdev | grep master | wc -l) -ne 0 ]; then + echo "Error: $hsdev is already a bridge port" >&2 + return 1 + fi + + #Pre-verify net_dev + if [ ! -e /sys/class/net/$ndev ]; then + echo "Error: $ndev does not exist" >&2 + return 1 + fi + if [ "$(cat /sys/class/net/$ndev/device/card_type)" == "HiperSockets" ]; then + echo "Error: $ndev is also a HiperSockets device" >&2 + return 1 + fi + if [ $(ip link show $ndev | grep UP | wc -l) -eq 0 ]; then + echo "Error: $ndev is not in state UP" >&2 + return 1 + fi + if [ $(ip link show $ndev | grep master | wc -l) -ne 0 ]; then + echo "Error: $ndev is already a bridge port" >&2 + return 1 + fi + + #Check PNETIDs + check_pnetids + if [ $? -ne 0 ]; then + return 1 + fi + + return 0 +} + +function clean_up { + bridge link set dev $hsdev learning_sync off self >/dev/null 2>&1 + echo 0 > /sys/class/net/$hsdev/device/vnicc/bridge_invisible >/dev/null 2>&1 + bridge fdb del $hsdev_mac dev $ndev >/dev/null 2>&1 + ip link del $hsci >/dev/null 2>&1 +} + +############################################################################## +## add a new HSCI interface +############################################################################## +function add_hsci { + + if [ $# != 2 ]; then + echo "hsci: Invalid parameters" >&2 + echo "Use 'hsci --help' for more information" >&2 + return 1 + fi + hsdev=$1 + ndev=$2 + + #### Verify preconditions + verify_precon + if [ $? -ne 0 ]; then + return 1 + fi + + hsci_postfix="$(readlink /sys/class/net/$hsdev/device/cdev0 | tail -c5)" + hsci=hsci$hsci_postfix + + echo "Adding $hsci with a HiperSockets dev $hsdev and an external dev $ndev" + + #### Create bridge + ip link add name $hsci type bridge stp_state 0 >/dev/null 2>&1 + if [ $? -ne 0 ]; then + echo "Error: Could not create a bridge" >&2 + return 1 + fi + + #### Prepare hsdev + # Set VNICC of hsdev to invisible + #(mandatory for co-existence with HS-OSA bridges!) + echo 1 > /sys/class/net/$hsdev/device/vnicc/bridge_invisible + + #### Create bridge ports + ip link set dev $ndev master $hsci >/dev/null 2>&1 + if [ $? -ne 0 ]; then + echo "Error: Could not set master for $ndev" >&2 + clean_up + return 1 + fi + ip link set dev $hsdev master $hsci >/dev/null 2>&1 + if [ $? -ne 0 ]; then + echo "Error: Could not set master for $hsdev" >&2 + clean_up + return 1 + fi + + # no forwarding between ndev and hsdev -> isolated on + # ndev is default for outgoing unknown targets -> flood on + # no need to learn external LAN targets into fdb -> learning off + bridge link set dev $ndev isolated on learning off flood on mcast_flood on >/dev/null 2>&1 + if [ $? -ne 0 ]; then + echo "Error: Failed to set bridge attributes on $ndev" >&2 + clean_up + return 1 + fi + + # no forwarding between ndev and hsdev -> isolated on + # fdb will be populated by dev-to-bridge-notification, no need to learn + # -> learning off + # only send to hsdev, if listed in fdb -> flood off + # don't send MC/BC on hsdev -> mcast_flood off + bridge link set dev $hsdev isolated on learning off flood off mcast_flood off >/dev/null 2>&1 + if [ $? -ne 0 ]; then + echo "Error: Failed to set bridge attributes on $hsdev" >&2 + clean_up + return 1 + fi + + # NOTE: Although not required, BCs will be sent out on hsdev. + # NOTE: We need to receive BCs on hsdev, as z/OS HSCI does ARP requests on HS. + + hsdev_mac="$(cat /sys/class/net/$hsdev/address)" + echo "Set $hsdev MAC $hsdev_mac on $ndev and $hsci" + + # set HS MAC on OSA as secondary MAC + bridge fdb add $hsdev_mac dev $ndev >/dev/null 2>&1 + if [ $? -ne 0 ]; then + echo "Error: Failed to set HS MAC on OSA as secondary MAC" >&2 + clean_up + return 1 + fi + + # set HS MAC (common MAC) on HSCI as primary MAC + ip link set address $hsdev_mac dev $hsci >/dev/null 2>&1 + if [ $? -ne 0 ]; then + echo "Error: Failed to set HiperSockets MAC (common MAC) on HSCI as primary MAC" >&2 + clean_up + return 1 + fi + + ip link set dev $hsci up >/dev/null 2>&1 + if [ $? -ne 0 ]; then + echo "Error: Failed to set $hsci up" >&2 + clean_up + return 1 + fi + + # Turn on device for bridge notification + bridge link set dev $hsdev learning_sync on self >/dev/null 2>&1 + if [ $? -ne 0 ]; then + echo "Error: Failed to turn on device for bridge notification" >&2 + clean_up + return 1 + fi + echo "Successfully added HSCI interface $hsci" + return 0 +} + +############################################################################## +## Delete HSCI +############################################################################## + +function del_hsci { + if [ $# != 1 ]; then + echo "hsci: invalid parameters" >&2 + echo "Use 'hsci --help' for more information" >&2 + return 1 + fi + hsci=$1 + if [ $(ip link show dev $hsci | wc -l) -eq 0 ]; then + echo "Error: $hsci does not exit" >&2 + return 1 + fi + if [ $(ip link show | grep "master $hsci" | wc -l) -eq 0 ]; then + echo "Error: $hsci is not an active HSCI interface" >&2 + return 1 + fi + + bports="$(ip link show | grep "master $hsci" | awk '{print $2}')" + for bport in $bports; do + bport=${bport%:} + if [[ $bport == *@* ]]; then + bport=${bport%@*} + fi + if [ $(bridge -d link show dev $bport | grep "learning_sync on" | wc -l) -ne 0 ]; then + hsdev=$bport + else + ndev=$bport + fi + done + if [ "$hsdev" == "" ]; then + echo "Error: $hsci has no active HiperSockets port" >&2 + return 1 + fi + echo "Deleting HSCI interface $hsci with the HiperSockets $hsdev and the external $ndev" + + bridge link set dev $hsdev learning_sync off self >/dev/null 2>&1 + if [ $? -ne 0 ]; then + echo "Error: Failed to turn off learning_sync on $hsdev" >&2 + return 1 + fi + echo 0 > /sys/class/net/$hsdev/device/vnicc/bridge_invisible + + hsdev_mac="$(cat /sys/class/net/$hsdev/address)" + echo "Deleting $hsev MAC $hsdev_mac on $ndev" + bridge fdb del $hsdev_mac dev $ndev >/dev/null 2>&1 + if [ $? -ne 0 ]; then + echo "Error: Failed to delete $hsev MAC $hsdev_mac on $ndev" >&2 + return 1 + fi + + ip link del $hsci >/dev/null 2>&1 + if [ $? -ne 0 ]; then + echo "Error: Failed to delete $hsci" >&2 + return 1 + fi + echo "Successfully deleted device $hsci" + + return 0 +} + +############################################################################## +## Show HSCI +############################################################################## + +function list_active { + hsdev=$1 + local ext="" + + hsci="$(ip link show dev $hsdev | awk '{for(x=1;x&2 + echo "Use 'hsci --help' for more information" >&2 + return 1 + fi + header=0 + + for hs_net_dev in $(ls -1 /sys/class/net/); do + list_one $hs_net_dev + done + + return 0 +} + +#============================================================================== + +function print_version() +{ + echo "hsci utility: version %S390_TOOLS_VERSION%" + echo "Copyright IBM Corp. 2020" +} + +############################################################################## +##### Main +############################################################################## +prereqs_check + +args="$(getopt -u -o hv -l help,version -- $*)" +[ $? -ne 0 ] && exit 2 +set -- $args +while true; do + case $1 in + -v | --version) + print_version + exit 0 + ;; + -h | --help) + usage + exit 0 + ;; + --) + ;; + add) shift + add_hsci "$@" + exit $? + ;; + del) shift + del_hsci "$@" + exit $? + ;; + show) shift + show_hsci "$@" + exit $? + ;; + *) echo "hsci: Please specify a valid command or option" >&2 + echo "Use 'hsci --help' for more information" >&2 + exit 1 + esac + shift +done + diff -Nru s390-tools-2.15.1/hsci/hsci.8 s390-tools-2.16.0/hsci/hsci.8 --- s390-tools-2.15.1/hsci/hsci.8 1970-01-01 00:00:00.000000000 +0000 +++ s390-tools-2.16.0/hsci/hsci.8 2021-02-19 14:46:37.000000000 +0000 @@ -0,0 +1,100 @@ +.\" Copyright IBM Corp. 2020 + +.TH HSCI 8 "November 2020" "s390-tools" "Linux Programmer's Manual" + + +.SH NAME +.B hsci +\- control and show HSCI settings. + + +.SH SYNOPSIS +.B hsci add +.I HSDEV +.I NETDEV +.br +.B hsci del +.I HSCINAME +.br +.B hsci show +.br +.B hsci [\-hv] + +.SH DESCRIPTION +.BI hsci +is used to control and show HSCI (HiperSockets Converged Interfaces) settings. A HiperSockets interface and an external network interface are converged into an HSCI interface. + +.SH COMMANDS +.TP +.B add \fIHSDEV\fR \fINETDEV\fR +.RS .4i +.PP +Adds an HSCI interface +.PP +.I HSDEV +is the interface name of the HiperSockets device to be converged into the HSCI interface. +.PP +.I NETDEV +is the interface name of the external network device to be converged into the HSCI interface. +.RE + +.TP +.B del \fIHSCINAME\fR +.RS .4i +.PP +Deletes an HSCI interface +.PP +.I HSCINAME +is the name of the HSCI interface for the HiperSockets device and the external network device. +.RE + +.TP +.B show +.RS .4i +.PP +Lists the configured HSCI interfaces. +.RE + +.SH OPTIONS +.TP +.BR \-v ", " \-\-version +Prints the version number of hsci and exits. +.TP +.BR \-h ", " \-\-help +Displays the help information for the command. + +.SH EXIT CODES +.TP +.BR "0" +The hsci command ran successfully. + +.TP +.BR "1" +An error occurred. + +.SH EXAMPLE +.BR "hsci show" +.TP +.RB +Lists the configured HSCI interfaces: +.RS 1.2i + +HSCI PNET_ID HiperSockets External +.br +----------------------------------------- +.br +hsci8410 NET1 enc8410 encb040 + +.RE + +.SH SEE ALSO +.nf +ip(8), bridge(8) +.fi + +.SH AUTHOR +.nf +Written by Alexandra Winter + Wenjia Zhang +.fi + diff -Nru s390-tools-2.15.1/hsci/Makefile s390-tools-2.16.0/hsci/Makefile --- s390-tools-2.15.1/hsci/Makefile 1970-01-01 00:00:00.000000000 +0000 +++ s390-tools-2.16.0/hsci/Makefile 2021-02-19 14:46:37.000000000 +0000 @@ -0,0 +1,16 @@ +include ../common.mak + +all: + +install: hsci + $(SED) -e 's/%S390_TOOLS_VERSION%/$(S390_TOOLS_RELEASE)/' \ + < hsci >$(DESTDIR)$(BINDIR)/hsci; \ + chown $(OWNER).$(GROUP) $(DESTDIR)$(BINDIR)/hsci; \ + chmod 755 $(DESTDIR)$(BINDIR)/hsci; \ + $(INSTALL) -d -m 755 $(DESTDIR)$(BINDIR) $(DESTDIR)$(MANDIR)/man8 + $(INSTALL) -g $(GROUP) -o $(OWNER) -m 644 hsci.8 \ + $(DESTDIR)$(MANDIR)/man8 + +clean: + +.PHONY: all install clean diff -Nru s390-tools-2.15.1/iucvterm/Makefile s390-tools-2.16.0/iucvterm/Makefile --- s390-tools-2.15.1/iucvterm/Makefile 2020-10-28 14:31:59.000000000 +0000 +++ s390-tools-2.16.0/iucvterm/Makefile 2021-02-19 14:46:37.000000000 +0000 @@ -21,7 +21,7 @@ $(RECURSIVE_TARGETS): @target=`echo $@ |sed s/-recursive//`; \ for d in $(SUBDIRS); do \ - (cd $$d && $(MAKE) $$target) \ + (cd $$d && $(MAKE) $$target) || exit 1; \ done diff -Nru s390-tools-2.15.1/libekmfweb/cca.c s390-tools-2.16.0/libekmfweb/cca.c --- s390-tools-2.15.1/libekmfweb/cca.c 2020-10-28 14:31:59.000000000 +0000 +++ s390-tools-2.16.0/libekmfweb/cca.c 2021-02-19 14:46:37.000000000 +0000 @@ -1061,6 +1061,12 @@ pr_verbose(verbose, "Failed to get and decode x"); goto out; } + if (len != prime_len) { + /* RFC 7517: Must be full size of a coordinate */ + pr_verbose(verbose, "x coordinate length is wrong"); + rc = -EINVAL; + goto out; + } len = prime_len; rc = json_object_get_base64url(jwk, "y", &q[1 + prime_len], @@ -1069,6 +1075,12 @@ pr_verbose(verbose, "Failed to get and decode y"); goto out; } + if (len != prime_len) { + /* RFC 7517: Must be full size of a coordinate */ + pr_verbose(verbose, "y coordinate length is wrong"); + rc = -EINVAL; + goto out; + } rule_array_count = 1; memcpy(rule_array, "ECC-PUBL", CCA_KEYWORD_SIZE); diff -Nru s390-tools-2.15.1/libekmfweb/Makefile s390-tools-2.16.0/libekmfweb/Makefile --- s390-tools-2.15.1/libekmfweb/Makefile 2020-10-28 14:31:59.000000000 +0000 +++ s390-tools-2.16.0/libekmfweb/Makefile 2021-02-19 14:46:37.000000000 +0000 @@ -77,11 +77,11 @@ ln -srf libekmfweb.so.$(VERSION) libekmfweb.so install-libekmfweb.so.$(VERSION): libekmfweb.so.$(VERSION) - $(INSTALL) -g $(GROUP) -o $(OWNER) -m 755 -T libekmfweb.so.$(VERSION) $(DESTDIR)$(USRLIB64DIR)/libekmfweb.so.$(VERSION) - ln -srf $(DESTDIR)$(USRLIB64DIR)/libekmfweb.so.$(VERSION) $(DESTDIR)$(USRLIB64DIR)/libekmfweb.so.$(VERM) - ln -srf $(DESTDIR)$(USRLIB64DIR)/libekmfweb.so.$(VERSION) $(DESTDIR)$(USRLIB64DIR)/libekmfweb.so + $(INSTALL) -g $(GROUP) -o $(OWNER) -m 755 -T libekmfweb.so.$(VERSION) $(DESTDIR)$(SOINSTALLDIR)/libekmfweb.so.$(VERSION) + ln -srf $(DESTDIR)$(SOINSTALLDIR)/libekmfweb.so.$(VERSION) $(DESTDIR)$(SOINSTALLDIR)/libekmfweb.so.$(VERM) + ln -srf $(DESTDIR)$(SOINSTALLDIR)/libekmfweb.so.$(VERSION) $(DESTDIR)$(SOINSTALLDIR)/libekmfweb.so $(INSTALL) -d -m 770 $(DESTDIR)$(USRINCLUDEDIR)/ekmfweb - $(INSTALL) -g $(GROUP) -o $(OWNER) -m 755 $(rootdir)include/ekmfweb/ekmfweb.h $(DESTDIR)$(USRINCLUDEDIR)/ekmfweb + $(INSTALL) -g $(GROUP) -o $(OWNER) -m 644 $(rootdir)include/ekmfweb/ekmfweb.h $(DESTDIR)$(USRINCLUDEDIR)/ekmfweb install: all $(INSTALL_TARGETS) diff -Nru s390-tools-2.15.1/libekmfweb/utilities.c s390-tools-2.16.0/libekmfweb/utilities.c --- s390-tools-2.15.1/libekmfweb/utilities.c 2020-10-28 14:31:59.000000000 +0000 +++ s390-tools-2.16.0/libekmfweb/utilities.c 2021-02-19 14:46:37.000000000 +0000 @@ -2254,11 +2254,21 @@ rc = json_object_get_base64url(jwk, "x", x, &len); if (rc != 0) goto out; + if (len != prime_len) { + /* RFC 7517: Must be full size of a coordinate */ + rc = -EINVAL; + goto out; + } len = prime_len; rc = json_object_get_base64url(jwk, "y", y, &len); if (rc != 0) goto out; + if (len != prime_len) { + /* RFC 7517: Must be full size of a coordinate */ + rc = -EINVAL; + goto out; + } rc = ecc_pub_key_as_pkey(nid, prime_len, x, y, pkey); if (rc != 0) diff -Nru s390-tools-2.15.1/libutil/util_proc.c s390-tools-2.16.0/libutil/util_proc.c --- s390-tools-2.15.1/libutil/util_proc.c 2020-10-28 14:31:59.000000000 +0000 +++ s390-tools-2.16.0/libutil/util_proc.c 2021-02-19 14:46:37.000000000 +0000 @@ -460,7 +460,7 @@ rc = scan_mnt_entry(&file, entry); if (rc) goto out_free; - if (!strcmp(entry->spec, spec)) { + if (!strcmp(entry->vfstype, spec)) { rc = 0; goto out_free; } diff -Nru s390-tools-2.15.1/Makefile s390-tools-2.16.0/Makefile --- s390-tools-2.15.1/Makefile 2020-10-28 14:31:59.000000000 +0000 +++ s390-tools-2.16.0/Makefile 2021-02-19 14:46:37.000000000 +0000 @@ -9,7 +9,7 @@ vmconvert vmcp man mon_tools dasdinfo vmur cpuplugd ipl_tools \ ziomon iucvterm hyptop cmsfs-fuse qethqoat zfcpdump zdsfs cpumf \ systemd hmcdrvfs cpacfstats zdev dump2tar zkey netboot etc zpcictl \ - genprotimg lsstp + genprotimg lsstp hsci SUB_DIRS = $(LIB_DIRS) $(TOOL_DIRS) diff -Nru s390-tools-2.15.1/netboot/Makefile s390-tools-2.16.0/netboot/Makefile --- s390-tools-2.15.1/netboot/Makefile 2020-10-28 14:31:59.000000000 +0000 +++ s390-tools-2.16.0/netboot/Makefile 2021-02-19 14:46:37.000000000 +0000 @@ -2,15 +2,27 @@ include ../common.mak +SCRIPTS = mk-s390image mk-pxelinux-ramfs NETBOOT_SAMPLEDIR=$(TOOLS_DATADIR)/netboot all: -install: all - $(INSTALL) -d -m 755 $(DESTDIR)$(NETBOOT_SAMPLEDIR) - $(INSTALL) -m 755 mk-s390image mk-pxelinux-ramfs \ - $(DESTDIR)$(NETBOOT_SAMPLEDIR) - $(INSTALL) -m 644 Dockerfile Makefile.pxelinux.0 README.md \ +install: install-scripts + $(INSTALL) -g $(GROUP) -o $(OWNER) -m 644 \ + Dockerfile Makefile.pxelinux.0 README.md mk-s390image.1 \ $(DESTDIR)$(NETBOOT_SAMPLEDIR) -.PHONY: all install clean +install-scripts: $(SCRIPTS) + @if [ ! -d $(DESTDIR)$(NETBOOT_SAMPLEDIR) ]; then \ + mkdir -p $(DESTDIR)$(NETBOOT_SAMPLEDIR); \ + chown $(OWNER).$(GROUP) $(DESTDIR)$(NETBOOT_SAMPLEDIR); \ + chmod 755 $(DESTDIR)$(NETBOOT_SAMPLEDIR); \ + fi; \ + for i in $^; do \ + $(SED) -e 's/%S390_TOOLS_VERSION%/$(S390_TOOLS_RELEASE)/' \ + < $$i >$(DESTDIR)$(NETBOOT_SAMPLEDIR)/$$i; \ + chown $(OWNER).$(GROUP) $(DESTDIR)$(NETBOOT_SAMPLEDIR)/$$i; \ + chmod 755 $(DESTDIR)$(NETBOOT_SAMPLEDIR)/$$i; \ + done + +.PHONY: all install clean install-scripts diff -Nru s390-tools-2.15.1/netboot/Makefile.pxelinux.0 s390-tools-2.16.0/netboot/Makefile.pxelinux.0 --- s390-tools-2.15.1/netboot/Makefile.pxelinux.0 2020-10-28 14:31:59.000000000 +0000 +++ s390-tools-2.16.0/netboot/Makefile.pxelinux.0 2021-02-19 14:46:37.000000000 +0000 @@ -21,7 +21,7 @@ $(error Could not find a kernel image under /boot) endif -BUSYBOX=busybox-1.27.1 +BUSYBOX=busybox-1.32.0 BBINSTALL=$(BUSYBOX)/_install all: $(KERNEL_IMAGE) pxelinux.initramfs diff -Nru s390-tools-2.15.1/netboot/mk-pxelinux-ramfs s390-tools-2.16.0/netboot/mk-pxelinux-ramfs --- s390-tools-2.15.1/netboot/mk-pxelinux-ramfs 2020-10-28 14:31:59.000000000 +0000 +++ s390-tools-2.16.0/netboot/mk-pxelinux-ramfs 2021-02-19 14:46:37.000000000 +0000 @@ -54,9 +54,18 @@ -b Search installed busybox in directory BUSYBOX_DIR -k Use KERNEL_VERSION instead of currently running kernel -h Print this help, then exit +-v Print version information, then exit EOF } +printversion() +{ + cat <<-EOD + $cmd: version %S390_TOOLS_VERSION% + Copyright IBM Corp. 2017 + EOD +} + # Get shared objects for binary sharedobjs() { @@ -64,7 +73,7 @@ } # Check args -args=$(getopt b:k:h $*) +args=$(getopt b:k:hv $*) if [ $? = 0 ] then set -- $args @@ -74,6 +83,7 @@ -b) busyboxdir=$2; shift 2;; -k) kernelversion=$2; shift 2;; -h) usage; exit 0;; + -v) printversion; exit 0;; --) shift; break;; *) echo "$cmd: Unexpected argument $1, exiting..." >&2; exit 1;; esac diff -Nru s390-tools-2.15.1/netboot/mk-s390image s390-tools-2.16.0/netboot/mk-s390image --- s390-tools-2.15.1/netboot/mk-s390image 2020-10-28 14:31:59.000000000 +0000 +++ s390-tools-2.16.0/netboot/mk-s390image 2021-02-19 14:46:37.000000000 +0000 @@ -60,9 +60,18 @@ -p Use PARMFILE with kernel parameters in the image -r Include RAMDISK in the image -h Print this help, then exit +-v Print version information, then exit EOF } +printversion() +{ + cat <<-EOD + $cmd: version %S390_TOOLS_VERSION% + Copyright IBM Corp. 2017 + EOD +} + # Convert decimal number to big endian doubleword dec2be64() { @@ -129,6 +138,9 @@ parmfile_size=$(du -b $parmfile | cut -f1) if [ $parmfile_size -le $MAX_PARMFILE_SIZE ] then + # Clear any previous parameters + dd seek=$OFFS_COMMANDLINE_BYTES bs=1 count=$MAX_PARMFILE_SIZE \ + if=/dev/zero of=$image conv=notrunc status=none dd seek=$OFFS_COMMANDLINE_BYTES bs=1 if=$parmfile \ of=$image conv=notrunc status=none else @@ -142,7 +154,7 @@ } # check args and build -args=$(getopt "r:p:h" $*) +args=$(getopt "r:p:hv" $*) if [ $? = 0 ] then set -- $args @@ -152,6 +164,7 @@ -r) ramdisk=$2; shift 2;; -p) parmfile=$2; shift 2;; -h) usage; exit 0;; + -v) printversion; exit 0;; --) shift; break;; *) echo "$cmd: Unexpected argument $1, exiting..." >&2; exit 1;; esac diff -Nru s390-tools-2.15.1/netboot/mk-s390image.1 s390-tools-2.16.0/netboot/mk-s390image.1 --- s390-tools-2.15.1/netboot/mk-s390image.1 1970-01-01 00:00:00.000000000 +0000 +++ s390-tools-2.16.0/netboot/mk-s390image.1 2021-02-19 14:46:37.000000000 +0000 @@ -0,0 +1,17 @@ +.TH MK-S390IMAGE "1" "November 2020" "s390-tools" "User Commands" +.SH NAME +mk-s390image \- tool for creating bootable image +.SH SYNOPSIS +.B mk-s390image +\fI\,KERNEL BOOT_IMAGE \/\fR[\fI\,-r RAMDISK\/\fR] [\fI\,-p PARMFILE\/\fR] +.SH DESCRIPTION +Build an s390 image BOOT_IMAGE suitable for CD/tape/network boot or as a +KVM firmware image using a stripped Linux kernel file KERNEL. +.PP +.SH OPTIONS +.TP +\fB\-p\fR Use PARMFILE with kernel parameters in the image +.TP +\fB\-r\fR Include RAMDISK in the image +.TP +\fB\-h\fR Print usage message, then exit diff -Nru s390-tools-2.15.1/README.md s390-tools-2.16.0/README.md --- s390-tools-2.15.1/README.md 2020-10-28 14:31:59.000000000 +0000 +++ s390-tools-2.16.0/README.md 2021-02-19 14:46:37.000000000 +0000 @@ -249,6 +249,9 @@ Management Foundation - Web Edition, and is used to manage keys in an enterprise. + * hsci: + Manage HiperSockets Converged Interfaces (HSCI). + For more information refer to the following publications: * "Device Drivers, Features, and Commands" chapter "Useful Linux commands" @@ -277,14 +280,15 @@ | cryptsetup | `HAVE_CRYPTSETUP2` | zkey-cryptsetup | | json-c | `HAVE_JSONC` | zkey-cryptsetup, libekmfweb | | glib2 | `HAVE_GLIB2` | genprotimg | -| libcurl | `HAVE_LIBCURL` | libekmfweb | +| libcurl | `HAVE_LIBCURL` | genprotimg, libekmfweb | This table lists additional build or install options: -| __COMPONENT__ | __OPTION__ | __TOOLS__ | -|----------------|:----------------:|:-------------------------------:| -| dracut | `HAVE_DRACUT` | zdev | -| initramfs-tools| `HAVE_INITRAMFS` | zdev | +| __COMPONENT__ | __OPTION__ | __TOOLS__ | +|------------------|:----------------------------:|:--------------:| +| dracut | `HAVE_DRACUT` | zdev | +| initramfs-tools | `HAVE_INITRAMFS` | zdev | +| | `ZDEV_ALWAYS_UPDATE_INITRD` | zdev | The s390-tools build process uses "pkg-config" if available and hard-coded compiler and linker options otherwise. @@ -375,6 +379,17 @@ Distributors with different boot or RAM-disk mechanisms should provide a custom zdev-root-update helper script. + - `ZDEV_ALWAYS_UPDATE_INITRD=1` upon modification of any persistent device + configuration, chzdev updates the initial RAM-disk by default, without any + additional user interaction. + + For some distributions, all the configuration attributes must be copied to + the initial RAM-disk. Because the device configuration directives applied + in the initial RAM-disk takes precedence over those stored in the root file- + system. This copying is done usually by explicitly invoking a command. This + build option makes it user-friendly and does this copying without any manual + intervention. + Some functions of zdev require that the following programs are available: - modprobe (kmod) diff -Nru s390-tools-2.15.1/scripts/zfcpdbf s390-tools-2.16.0/scripts/zfcpdbf --- s390-tools-2.15.1/scripts/zfcpdbf 2020-10-28 14:31:59.000000000 +0000 +++ s390-tools-2.16.0/scripts/zfcpdbf 2021-02-19 14:46:37.000000000 +0000 @@ -227,6 +227,16 @@ # function name or hexadecimal caller address. # $line =~ s/<\s+/ ]+)[ \t]+/<\1/; @common = split(/\s+/, $line); # # Process symbolic kernel module name, if necessary. @@ -388,6 +398,17 @@ # caller address. # $line =~ s/<\s+/ ]+)[ \t]+/<\1/; my @raw_rec = split(/\s+/,($line =~ /[|]/) ? substr($line, 0, index($line, '|')): $line); diff -Nru s390-tools-2.15.1/scripts/zipl-switch-to-blscfg.1 s390-tools-2.16.0/scripts/zipl-switch-to-blscfg.1 --- s390-tools-2.15.1/scripts/zipl-switch-to-blscfg.1 2020-10-28 14:31:59.000000000 +0000 +++ s390-tools-2.16.0/scripts/zipl-switch-to-blscfg.1 2021-02-19 14:46:37.000000000 +0000 @@ -10,10 +10,11 @@ \fBzipl-switch-to-blscfg\fP {\-h|\-v} .SH DESCRIPTION -This script switches the zipl boot-loader configuration to use BootLoaderSpec files -to define IPL sections. For each Linux kernel defined in the zipl.conf config file, -a BLS fragment is generated in the BLS directory specified. Also, the zipl.conf is -modified it only contains global configurations, all IPL sections comes from BLS. +This script switches the zipl boot-loader configuration to use BootLoaderSpec +files to define IPL sections. For each Linux kernel defined in the zipl.conf +config file, a BLS fragment is generated in the BLS directory specified. Also, +zipl.conf is modified to only contain global configuration. All IPL sections +come from BLS files. .SH OPTIONS .TP @@ -30,7 +31,8 @@ .TP \fB\-\-bls-directory \fP -The DIRECTORY where the BLS fragments will be generated. The directory is created if it doesn't exists, by default /boot/loader/entries is used. +The DIRECTORY where the BLS fragments will be stored. The directory is +created if it does not exist. By default /boot/loader/entries is used. .TP \fB\-\-config-file \fP diff -Nru s390-tools-2.15.1/systemd/cpi.service.in s390-tools-2.16.0/systemd/cpi.service.in --- s390-tools-2.15.1/systemd/cpi.service.in 2020-10-28 14:31:59.000000000 +0000 +++ s390-tools-2.16.0/systemd/cpi.service.in 2021-02-19 14:46:37.000000000 +0000 @@ -10,11 +10,12 @@ Description=Apply Control Program Identification (CPI) DefaultDependencies=no Conflicts=shutdown.target -After=local-fs.target +After=sysinit.target ConditionPathIsReadWrite=/sys/firmware/cpi [Service] Type=oneshot +RemainAfterExit=yes # # Specify a file with the environment variables using the EnvironmentFile= # service property. diff -Nru s390-tools-2.15.1/zconf/lsdasd s390-tools-2.16.0/zconf/lsdasd --- s390-tools-2.15.1/zconf/lsdasd 2020-10-28 14:31:59.000000000 +0000 +++ s390-tools-2.16.0/zconf/lsdasd 2021-02-19 14:46:37.000000000 +0000 @@ -387,6 +387,7 @@ read EXTSZ 2> /dev/null < $DEVPATH/extent_pool/extent_size read CAPACITY 2> /dev/null < $DEVPATH/capacity/logical_capacity read ALLOCATED 2> /dev/null < $DEVPATH/capacity/space_allocated + read FC_SEC 2> /dev/null < $DEVPATH/fc_security # convert to hexadecimal values PIM=0x$PIM @@ -521,7 +522,7 @@ elif [[ "$ALIAS" == 1 ]]; then if [[ "$BASEONLY" == "false" ]]; then ACTIVE="alias" - printf "%s:%s:%s# status:\t\t\t\t%s# type: \t\t\t\t%s# use_diag:\t\t\t\t%s# readonly:\t\t\t\t%s# eer_enabled:\t\t\t\t%s# erplog:\t\t\t\t%s# hpf:\t\t\t\t\t%s # uid: \t\t\t\t%s# paths_installed: \t\t\t%s %s %s %s %s %s %s %s# paths_in_use: \t\t\t%s %s %s %s %s %s %s %s# paths_non_preferred: \t\t\t%s %s %s %s %s %s %s %s# paths_invalid_cabling: \t\t%s %s %s %s %s %s %s %s# paths_cuir_quiesced: \t\t\t%s %s %s %s %s %s %s %s# paths_invalid_hpf_characteristics: \t%s %s %s %s %s %s %s %s# paths_error_threshold_exceeded: \t%s %s %s %s %s %s %s %s#\n" \ + printf "%s:%s:%s# status:\t\t\t\t%s# type: \t\t\t\t%s# use_diag:\t\t\t\t%s# readonly:\t\t\t\t%s# eer_enabled:\t\t\t\t%s# erplog:\t\t\t\t%s# hpf:\t\t\t\t\t%s # uid: \t\t\t\t%s# fc_security: \t\t\t\t%s# paths_installed: \t\t\t%s %s %s %s %s %s %s %s# paths_in_use: \t\t\t%s %s %s %s %s %s %s %s# paths_non_preferred: \t\t\t%s %s %s %s %s %s %s %s# paths_invalid_cabling: \t\t%s %s %s %s %s %s %s %s# paths_cuir_quiesced: \t\t\t%s %s %s %s %s %s %s %s# paths_invalid_hpf_characteristics: \t%s %s %s %s %s %s %s %s# paths_error_threshold_exceeded: \t%s %s %s %s %s %s %s %s#\n" \ "$SORTKEYLEN" "$SORTKEY" \ "$BUSID" \ "$ACTIVE" \ @@ -532,6 +533,7 @@ "$ERP" \ "$HPF" \ "$DEV_UID" \ + "$FC_SEC" \ "${INSTALLED_PATHS[@]}" \ "${USED_PATHS[@]}" \ "${NP_PATHS[@]}" \ @@ -563,7 +565,7 @@ DISCIPLINE="${DISCIPLINE} (ESE)" fi - printf "%s:%s:%s/%s/%s%s%s# status:\t\t\t\t%s# type: \t\t\t\t%s# blksz:\t\t\t\t%s# size: \t\t\t\t%s# blocks:\t\t\t\t%s# extent_size:\t\t\t\t%s# logical_capacity:\t\t\t%s# space_allocated:\t\t\t%s# use_diag:\t\t\t\t%s# readonly:\t\t\t\t%s# eer_enabled:\t\t\t\t%s# erplog:\t\t\t\t%s# hpf:\t\t\t\t\t%s# uid: \t\t\t\t%s# paths_installed: \t\t\t%s %s %s %s %s %s %s %s# paths_in_use: \t\t\t%s %s %s %s %s %s %s %s# paths_non_preferred: \t\t\t%s %s %s %s %s %s %s %s# paths_invalid_cabling: \t\t%s %s %s %s %s %s %s %s# paths_cuir_quiesced: \t\t\t%s %s %s %s %s %s %s %s# paths_invalid_hpf_characteristics: \t%s %s %s %s %s %s %s %s# paths_error_threshold_exceeded: \t%s %s %s %s %s %s %s %s#\n" \ + printf "%s:%s:%s/%s/%s%s%s# status:\t\t\t\t%s# type: \t\t\t\t%s# blksz:\t\t\t\t%s# size: \t\t\t\t%s# blocks:\t\t\t\t%s# extent_size:\t\t\t\t%s# logical_capacity:\t\t\t%s# space_allocated:\t\t\t%s# use_diag:\t\t\t\t%s# readonly:\t\t\t\t%s# eer_enabled:\t\t\t\t%s# erplog:\t\t\t\t%s# hpf:\t\t\t\t\t%s# uid: \t\t\t\t%s# fc_security: \t\t\t\t%s# paths_installed: \t\t\t%s %s %s %s %s %s %s %s# paths_in_use: \t\t\t%s %s %s %s %s %s %s %s# paths_non_preferred: \t\t\t%s %s %s %s %s %s %s %s# paths_invalid_cabling: \t\t%s %s %s %s %s %s %s %s# paths_cuir_quiesced: \t\t\t%s %s %s %s %s %s %s %s# paths_invalid_hpf_characteristics: \t%s %s %s %s %s %s %s %s# paths_error_threshold_exceeded: \t%s %s %s %s %s %s %s %s#\n" \ "$SORTKEYLEN" "$SORTKEY" \ "$BUSID" \ "$BLOCKNAME" \ @@ -584,6 +586,7 @@ "$ERP" \ "$HPF" \ "$DEV_UID" \ + "$FC_SEC" \ "${INSTALLED_PATHS[@]}" \ "${USED_PATHS[@]}" \ "${NP_PATHS[@]}" \ diff -Nru s390-tools-2.15.1/zconf/zcrypt/zcryptstats.c s390-tools-2.16.0/zconf/zcrypt/zcryptstats.c --- s390-tools-2.15.1/zconf/zcrypt/zcryptstats.c 2020-10-28 14:31:59.000000000 +0000 +++ s390-tools-2.16.0/zconf/zcrypt/zcryptstats.c 2021-02-19 14:46:37.000000000 +0000 @@ -1178,8 +1178,14 @@ scdmd_area.request.header.code = 0x102d; scdmd_area.request.header.length = sizeof(struct chsc_scdmd_request); - scdmd_area.request.first_drid.ap_index = card; - scdmd_area.request.first_drid.domain_index = g.min_domain; + if (scdmd_area.response.p) { + scdmd_area.request.first_drid = + scdmd_area.response.crid; + } else { + scdmd_area.request.first_drid.ap_index = card; + scdmd_area.request.first_drid.domain_index = + g.min_domain; + } scdmd_area.request.last_drid.ap_index = card; scdmd_area.request.last_drid.domain_index = g.max_domain; scdmd_area.request.s = 1; @@ -1217,10 +1223,6 @@ rc = process_apqn_measurement_data(&scdmd_area); if (rc != 0) break; - - if (scdmd_area.response.p) - scdmd_area.request.first_drid = - scdmd_area.response.crid; } while (scdmd_area.response.p); return rc; diff -Nru s390-tools-2.15.1/zdev/include/root.h s390-tools-2.16.0/zdev/include/root.h --- s390-tools-2.15.1/zdev/include/root.h 2020-10-28 14:31:59.000000000 +0000 +++ s390-tools-2.16.0/zdev/include/root.h 2021-02-19 14:46:37.000000000 +0000 @@ -12,6 +12,6 @@ #include "exit_code.h" -exit_code_t root_check(void); +exit_code_t initrd_check(bool all_pers); #endif /* ROOT_H */ diff -Nru s390-tools-2.15.1/zdev/src/chzdev.c s390-tools-2.16.0/zdev/src/chzdev.c --- s390-tools-2.15.1/zdev/src/chzdev.c 2020-10-28 14:31:59.000000000 +0000 +++ s390-tools-2.16.0/zdev/src/chzdev.c 2021-02-19 14:46:37.000000000 +0000 @@ -3027,7 +3027,7 @@ !dryrun) { /* If the root device/device type or early devices have been * modified, additional work might be necessary. */ - rc = root_check(); + rc = initrd_check(ZDEV_ALWAYS_UPDATE_INITRD); if (rc && !drc) drc = rc; } diff -Nru s390-tools-2.15.1/zdev/src/dasd.c s390-tools-2.16.0/zdev/src/dasd.c --- s390-tools-2.15.1/zdev/src/dasd.c 2020-10-28 14:31:59.000000000 +0000 +++ s390-tools-2.16.0/zdev/src/dasd.c 2021-02-19 14:46:37.000000000 +0000 @@ -313,6 +313,22 @@ .writeonly = 1, }; +static struct attrib dasd_attr_fc_security = { + .name = "fc_security", + .title = "Show FC Endpoint Security state of DASD device", + .desc = + "This read-only attribute shows the Fibre Channel Endpoint Security\n" + "status of the connection to the DASD device:\n" + " Unsupported : The DASD device does not support Fibre Channel\n" + " Endpoint Security\n" + " Inconsistent : The operational channel paths of the DASD device\n" + " report inconsistent Fibre Channel Endpoint\n" + " Security status\n" + " Authentication: The connection has been authenticated\n" + " Encryption : The connection is encrypted\n", + .readonly = 1, +}; + /* * DASD subtype methods. */ @@ -617,6 +633,7 @@ &dasd_attr_reservation_policy, &dasd_attr_last_known_reservation_state, &dasd_attr_safe_offline, + &dasd_attr_fc_security, &internal_attr_early, ), .unknown_dev_attribs = 1, diff -Nru s390-tools-2.15.1/zdev/src/Makefile s390-tools-2.16.0/zdev/src/Makefile --- s390-tools-2.15.1/zdev/src/Makefile 2020-10-28 14:31:59.000000000 +0000 +++ s390-tools-2.16.0/zdev/src/Makefile 2021-02-19 14:46:37.000000000 +0000 @@ -4,6 +4,16 @@ ALL_CPPFLAGS += -I ../include -std=gnu99 -Wno-unused-parameter \ -Wno-missing-field-initializers +# Adding ZDEV_ALWAYS_UPDATE_INITRD=1 option will update the initial RAM-disk +# without the user interaction upon the modification of a persistent device +# configuration. + +ifeq ($(ZDEV_ALWAYS_UPDATE_INITRD),1) +ALL_CPPFLAGS += -DZDEV_ALWAYS_UPDATE_INITRD=true +else +ALL_CPPFLAGS += -DZDEV_ALWAYS_UPDATE_INITRD=false +endif + # Core chzdev_objects += attrib.o chzdev.o device.o devnode.o devtype.o exit_code.o \ export.o hash.o inuse.o misc.o namespace.o opts.o path.o \ diff -Nru s390-tools-2.15.1/zdev/src/root.c s390-tools-2.16.0/zdev/src/root.c --- s390-tools-2.15.1/zdev/src/root.c 2020-10-28 14:31:59.000000000 +0000 +++ s390-tools-2.16.0/zdev/src/root.c 2021-02-19 14:46:37.000000000 +0000 @@ -58,11 +58,50 @@ } } +static void add_pers_removed(struct util_list *strlist) +{ + int i, j; + struct devtype *dt; + struct subtype *st; + struct device *dev; + + for (i = 0; devtypes[i]; i++) { + dt = devtypes[i]; + for (j = 0; dt->subtypes[j]; j++) { + st = dt->subtypes[j]; + util_list_iterate(&st->devices->hash.list, dev) { + if (dev->persistent.deconfigured) { + strlist_add(strlist, "%s %s", + dev->subtype->devname, dev->id); + } + } + } + } +} + +static bool is_zdev_early_0(struct selected_dev_node *sel) +{ + struct setting *s; + struct device *dev; + + dev = device_list_find(sel->st->devices, sel->id, NULL); + if (!dev) + return false; + s = setting_list_find(dev->persistent.settings, + internal_attr_early.name); + if (!s) + return false; + if (s->specified && strcmp(s->value, "0") == 0) + return true; + + return false; +} + /* Determine if initial RAM-disk needs updating. If so, run the corresponding * scripts if available. */ -exit_code_t root_check(void) +exit_code_t initrd_check(bool all_pers) { - struct util_list *selected, *params, *mod = NULL; + struct util_list *selected, *params, *mod = strlist_new(); struct selected_dev_node *sel; struct device *dev; char *params_str; @@ -76,6 +115,20 @@ /* Get list of devices that provide the root device or require * early configuration. */ selected = selected_dev_list_new(); + + if (all_pers) { + /* Add all persistently configured devices. */ + select = select_opts_new(); + select->configured = 1; + select_devices(select, selected, 1, 0, 0, config_persistent, + scope_mandatory, err_ignore); + select_opts_free(select); + + /* Ensure that removed devices are considered. */ + add_pers_removed(mod); + goto check_mod; + } + /* First add devices that had zdev:early removed or changed to 0. * The subsequent call to select_devices() will filter out any * duplicates. */ @@ -95,8 +148,8 @@ err_ignore); select_opts_free(select); +check_mod: /* Determine if any of the devices or device types has been modified. */ - mod = strlist_new(); util_list_iterate(selected, sel) { dt = sel->st->devtype; @@ -127,17 +180,22 @@ goto out; } - /* Ask for confirmation. */ - if (!confirm("Update initial RAM-disk now?")) { - rc = EXIT_ABORTED; - goto out; + if (!all_pers) { + /* Ask for confirmation. */ + if (!confirm("Update initial RAM-disk now?")) { + rc = EXIT_ABORTED; + goto out; + } } /* Build the command line. */ params = strlist_new(); util_list_iterate(selected, sel) { - strlist_add(params, "%s", sel->st->name); - strlist_add(params, "%s", sel->id); + /* From the selected list, remove the devices with zdev:early=0 */ + if (!is_zdev_early_0(sel)) { + strlist_add(params, "%s", sel->st->name); + strlist_add(params, "%s", sel->id); + } } params_str = strlist_flatten(params, " "); strlist_free(params); diff -Nru s390-tools-2.15.1/zdump/dfi.c s390-tools-2.16.0/zdump/dfi.c --- s390-tools-2.15.1/zdump/dfi.c 2020-10-28 14:31:59.000000000 +0000 +++ s390-tools-2.16.0/zdump/dfi.c 2021-02-19 14:46:37.000000000 +0000 @@ -407,7 +407,7 @@ if (size == 0) return; mem_chunk_create(&l.mem_phys, start, size, data, read_fn, free_fn); - mem_chunk_create(&l.mem_virt, start, size, data, read_fn, free_fn); + mem_chunk_create(&l.mem_virt, start, size, data, read_fn, NULL); l.mem_virt.chunk_cache->volnr = volnr; } diff -Nru s390-tools-2.15.1/zdump/dfi_elf.c s390-tools-2.16.0/zdump/dfi_elf.c --- s390-tools-2.15.1/zdump/dfi_elf.c 2020-10-28 14:31:59.000000000 +0000 +++ s390-tools-2.16.0/zdump/dfi_elf.c 2021-02-19 14:46:37.000000000 +0000 @@ -42,6 +42,8 @@ STDERR("Dump file \"%s\" is a user space core dump\n", g.opts.device); } + if (phdr->p_offset + phdr->p_filesz > zg_size(g.fh)) + return -EINVAL; if (phdr->p_filesz == 0) { /* Add zero memory chunk */ dfi_mem_chunk_add(phdr->p_paddr, phdr->p_memsz, NULL, @@ -52,8 +54,6 @@ dfi_mem_chunk_add(phdr->p_paddr, phdr->p_memsz, off_ptr, dfi_elf_mem_chunk_read_fn, zg_free); } - if (phdr->p_offset + phdr->p_filesz > zg_size(g.fh)) - return -EINVAL; return 0; } diff -Nru s390-tools-2.15.1/zdump/zg.c s390-tools-2.16.0/zdump/zg.c --- s390-tools-2.15.1/zdump/zg.c 2020-10-28 14:31:59.000000000 +0000 +++ s390-tools-2.16.0/zdump/zg.c 2021-02-19 14:46:37.000000000 +0000 @@ -280,6 +280,9 @@ { off_t rc; + if (off >= zg_fh->sb.st_size) + ERR_EXIT("Trying to seek past file end \"%s\"", zg_fh->path); + rc = lseek(zg_fh->fh, off, SEEK_SET); if (rc == -1 && check != ZG_CHECK_NONE) ERR_EXIT_ERRNO("Could not seek \"%s\"", zg_fh->path); diff -Nru s390-tools-2.15.1/zfcpdump/Makefile s390-tools-2.16.0/zfcpdump/Makefile --- s390-tools-2.15.1/zfcpdump/Makefile 2020-10-28 14:31:59.000000000 +0000 +++ s390-tools-2.16.0/zfcpdump/Makefile 2021-02-19 14:46:37.000000000 +0000 @@ -3,6 +3,9 @@ CPIOINIT = $(call echocmd," CPIOINI ",/$@)./cpioinit INSTALL_SCRIPTS = 10-zfcpdump.install +ALL_CFLAGS += -fno-sanitize=all +ALL_LDFLAGS += -fno-sanitize=all + ifeq (${HAVE_LIBC_STATIC},0) all: diff -Nru s390-tools-2.15.1/zipl/boot/eckd2dump.c s390-tools-2.16.0/zipl/boot/eckd2dump.c --- s390-tools-2.15.1/zipl/boot/eckd2dump.c 2020-10-28 14:31:59.000000000 +0000 +++ s390-tools-2.16.0/zipl/boot/eckd2dump.c 2021-02-19 14:46:37.000000000 +0000 @@ -449,13 +449,14 @@ * block number */ unsigned long write_dump_segment(unsigned long blk, - struct df_s390_dump_segm_hdr *segm, - unsigned long zero_page) + struct df_s390_dump_segm_hdr *segm) { - unsigned long addr, start_blk, blk_count; + unsigned long addr, start_blk, blk_count, zero_page; /* Write the dump segment header itself (1 page) */ + zero_page = get_zeroed_page(); writeblock(blk, (unsigned long)segm, m2b(PAGE_SIZE), zero_page); + free_page(zero_page); blk += m2b(PAGE_SIZE); /* Write the dump segment */ addr = segm->start; @@ -464,7 +465,9 @@ /* Remaining blocks to write */ blk_count = m2b(segm->len) - (blk - start_blk); blk_count = MIN(blk_count, eckd_blk_max); + zero_page = get_zeroed_page(); writeblock(blk, addr, blk_count, zero_page); + free_page(zero_page); progress_print(addr); blk += blk_count; addr += b2m(blk_count); diff -Nru s390-tools-2.15.1/zipl/boot/eckd2dump.h s390-tools-2.16.0/zipl/boot/eckd2dump.h --- s390-tools-2.15.1/zipl/boot/eckd2dump.h 2020-10-28 14:31:59.000000000 +0000 +++ s390-tools-2.16.0/zipl/boot/eckd2dump.h 2021-02-19 14:46:37.000000000 +0000 @@ -48,7 +48,6 @@ unsigned long zero_page); void readblock(unsigned long blk, unsigned long addr, unsigned long blk_count); unsigned long write_dump_segment(unsigned long blk, - struct df_s390_dump_segm_hdr *segm, - unsigned long zero_page); + struct df_s390_dump_segm_hdr *segm); #endif /* ECKD2DUMP_H */ diff -Nru s390-tools-2.15.1/zipl/boot/eckd2dump_mv.c s390-tools-2.16.0/zipl/boot/eckd2dump_mv.c --- s390-tools-2.15.1/zipl/boot/eckd2dump_mv.c 2020-10-28 14:31:59.000000000 +0000 +++ s390-tools-2.16.0/zipl/boot/eckd2dump_mv.c 2021-02-19 14:46:37.000000000 +0000 @@ -192,10 +192,13 @@ * Check for the volume timestamp and validate the dump signature * before writing a dump. */ -static void check_volume(unsigned long page) +static void check_volume(void) { struct mvdump_parm_table *mvdump_table_new; struct df_s390_hdr *hdr_new; + unsigned long page; + + page = get_zeroed_page(); /* * Check whether parameter table on dump device has a valid @@ -222,6 +225,8 @@ if (dump_hdr->magic != hdr_new->mvdump_sign) panic(ENOSIGN, "Wrong signature"); } + + free_page(page); } /* @@ -229,10 +234,10 @@ * address to write for the next volume or memory size if the dump ended * on this volume */ -static unsigned long write_volume(unsigned long addr, unsigned long page, +static unsigned long write_volume(unsigned long addr, struct df_s390_dump_segm_hdr *dump_segm) { - unsigned long free_space, blk; + unsigned long free_space, blk, page; /* * Write dump header @@ -245,7 +250,7 @@ * header and the end marker) */ free_space = b2m(device.blk_end - blk + 1) - b2m(2); - memset((void *) page, 0, PAGE_SIZE); + /* * Write dump data */ @@ -257,7 +262,7 @@ addr = find_dump_segment(addr, dump_hdr->mem_size, ROUND_DOWN(free_space, MIB), dump_segm); - blk = write_dump_segment(blk, dump_segm, page); + blk = write_dump_segment(blk, dump_segm); /* Update free space left on vol */ free_space -= dump_segm->len; /* Reserve one block for the next segment header */ @@ -267,8 +272,10 @@ /* Check if no more dump segments follow */ if (dump_segm->stop_marker) { /* Write end marker */ + page = get_zeroed_page(); df_s390_em_page_init(page); writeblock(blk, page, 1, 0); + free_page(page); return dump_hdr->mem_size; } /* @@ -286,19 +293,18 @@ void dt_dump_mem(void) { struct df_s390_dump_segm_hdr *dump_segm; - unsigned long addr, page; + unsigned long addr; dump_hdr->mvdump_sign = DF_S390_MAGIC_EXT; dump_hdr->mvdump = 1; addr = 0; total_dump_size = 0; - page = get_zeroed_page(); dump_segm = (void *)get_zeroed_page(); while (1) { printf("Dumping to: 0.%x.%04x", device.sid.ssid, device.devno); - check_volume(page); - addr = write_volume(addr, page, dump_segm); + check_volume(); + addr = write_volume(addr, dump_segm); if (addr == dump_hdr->mem_size) break; /* @@ -312,7 +318,6 @@ set_device(device.sid, DISABLED); dt_device_enable(); } - progress_print(addr); - free_page(page); free_page(__pa(dump_segm)); + progress_print(addr); } diff -Nru s390-tools-2.15.1/zipl/boot/eckd2dump_sv.c s390-tools-2.16.0/zipl/boot/eckd2dump_sv.c --- s390-tools-2.15.1/zipl/boot/eckd2dump_sv.c 2020-10-28 14:31:59.000000000 +0000 +++ s390-tools-2.16.0/zipl/boot/eckd2dump_sv.c 2021-02-19 14:46:37.000000000 +0000 @@ -66,7 +66,6 @@ struct df_s390_dump_segm_hdr *dump_segm; blk = device.blk_start; - page = get_zeroed_page(); dump_segm = (void *)get_zeroed_page(); /* Write dump header */ @@ -79,18 +78,19 @@ end = dump_hdr->mem_size; while (addr < end) { addr = find_dump_segment(addr, end, 0, dump_segm); - blk = write_dump_segment(blk, dump_segm, page); + blk = write_dump_segment(blk, dump_segm); total_dump_size += dump_segm->len; if (dump_segm->stop_marker) { addr = end; break; } } + free_page(__pa(dump_segm)); progress_print(addr); /* Write end marker */ + page = get_zeroed_page(); df_s390_em_page_init(page); writeblock(blk, page, 1, 0); free_page(page); - free_page(__pa(dump_segm)); } diff -Nru s390-tools-2.15.1/zipl/boot/fba2dump.c s390-tools-2.16.0/zipl/boot/fba2dump.c --- s390-tools-2.15.1/zipl/boot/fba2dump.c 2020-10-28 14:31:59.000000000 +0000 +++ s390-tools-2.16.0/zipl/boot/fba2dump.c 2021-02-19 14:46:37.000000000 +0000 @@ -141,13 +141,14 @@ * block number */ unsigned long write_dump_segment_fba(unsigned long blk, - struct df_s390_dump_segm_hdr *dump_segm, - unsigned long zero_page) + struct df_s390_dump_segm_hdr *dump_segm) { - unsigned long addr, start_blk, blk_count; + unsigned long addr, start_blk, blk_count, zero_page; /* Write the dump segment header itself (1 page) */ + zero_page = get_zeroed_page(); writeblock_fba(blk, __pa(dump_segm), BLK_PER_PAGE, zero_page); + free_page(zero_page); blk += BLK_PER_PAGE; /* Write the dump segment */ addr = dump_segm->start; @@ -156,7 +157,9 @@ /* Remaining blocks to write */ blk_count = m2b(dump_segm->len) - (blk - start_blk); blk_count = MIN(blk_count, BLK_PWRT); + zero_page = get_zeroed_page(); writeblock_fba(blk, addr, blk_count, zero_page); + free_page(zero_page); progress_print(addr); blk += blk_count; addr += b2m(blk_count); @@ -198,7 +201,6 @@ ccw_program_init(); blk = device.blk_start; - page = get_zeroed_page(); dump_segm = (void *)get_zeroed_page(); /* Write dump header */ @@ -211,18 +213,19 @@ end = dump_hdr->mem_size; while (addr < end) { addr = find_dump_segment(addr, end, 0, dump_segm); - blk = write_dump_segment_fba(blk, dump_segm, page); + blk = write_dump_segment_fba(blk, dump_segm); total_dump_size += dump_segm->len; if (dump_segm->stop_marker) { addr = end; break; } } + free_page(__pa(dump_segm)); progress_print(addr); /* Write end marker */ + page = get_zeroed_page(); df_s390_em_page_init(page); writeblock_fba(blk, page, 1, 0); free_page(page); - free_page(__pa(dump_segm)); } diff -Nru s390-tools-2.15.1/zipl/boot/libc.c s390-tools-2.16.0/zipl/boot/libc.c --- s390-tools-2.15.1/zipl/boot/libc.c 2020-10-28 14:31:59.000000000 +0000 +++ s390-tools-2.16.0/zipl/boot/libc.c 2021-02-19 14:46:37.000000000 +0000 @@ -32,9 +32,10 @@ }; #define MEM_ALLOC_START ((unsigned long) __heap_start) -#define MEM_ALLOC_CNT 4 +#define MEM_ALLOC_END ((unsigned long) __heap_stop) +#define MEM_ALLOC_MAX 4 -static uint8_t mem_page_alloc_vec[MEM_ALLOC_CNT]; +static uint8_t mem_page_alloc_vec[MEM_ALLOC_MAX]; /* * Initialize memory with value @@ -417,10 +418,11 @@ */ unsigned long get_zeroed_page(void) { + const int page_count = MIN(MEM_ALLOC_MAX, (int)((MEM_ALLOC_END - MEM_ALLOC_START) / PAGE_SIZE)); unsigned long addr; int i; - for (i = 0; i < MEM_ALLOC_CNT; i++) { + for (i = 0; i < page_count; i++) { if (mem_page_alloc_vec[i] != 0) continue; addr = MEM_ALLOC_START + i * PAGE_SIZE; @@ -436,6 +438,9 @@ */ void free_page(unsigned long addr) { + if (addr < MEM_ALLOC_START || addr >= MEM_ALLOC_END) + libc_stop(EINTERNAL); + mem_page_alloc_vec[(addr - MEM_ALLOC_START) / PAGE_SIZE] = 0; } diff -Nru s390-tools-2.15.1/zipl/boot/Makefile s390-tools-2.16.0/zipl/boot/Makefile --- s390-tools-2.15.1/zipl/boot/Makefile 2020-10-28 14:31:59.000000000 +0000 +++ s390-tools-2.16.0/zipl/boot/Makefile 2021-02-19 14:46:37.000000000 +0000 @@ -10,7 +10,8 @@ -fno-delete-null-pointer-checks -fno-stack-protector \ -fexec-charset=IBM1047 -m64 -mpacked-stack \ -mstack-size=4096 -mstack-guard=128 -msoft-float \ - -W -Wall -Wformat-security + -W -Wall -Wformat-security -fno-sanitize=all +ALL_LDFLAGS += -fno-sanitize=all FILES = fba0.bin fba1b.bin fba2.bin \ eckd0_ldl.bin eckd0_cdl.bin \ diff -Nru s390-tools-2.15.1/zipl/boot/stage2.c s390-tools-2.16.0/zipl/boot/stage2.c --- s390-tools-2.15.1/zipl/boot/stage2.c 2020-10-28 14:31:59.000000000 +0000 +++ s390-tools-2.16.0/zipl/boot/stage2.c 2021-02-19 14:46:37.000000000 +0000 @@ -90,6 +90,7 @@ void *load_address; struct component_entry *entry; disk_blockptr_t *blockptr; + uint64_t load_psw; void *load_page; int config_nr; @@ -133,11 +134,13 @@ if (entry->type != COMPONENT_EXECUTE) panic(EWRONGTYPE, ""); + load_psw = entry->address.load_psw; + free_page((unsigned long)load_page); io_irq_disable(); set_device(subchannel_id, DISABLED); - execute(entry->address.load_psw); + execute(load_psw); } void panic_notify(unsigned long UNUSED(reason)) diff -Nru s390-tools-2.15.1/zipl/src/disk.c s390-tools-2.16.0/zipl/src/disk.c --- s390-tools-2.15.1/zipl/src/disk.c 2020-10-28 14:31:59.000000000 +0000 +++ s390-tools-2.16.0/zipl/src/disk.c 2021-02-19 14:46:37.000000000 +0000 @@ -444,6 +444,9 @@ } /* Convert device size to size in physical blocks */ data->phy_blocks = devsize / (data->phy_block_size / 512); + /* Adjust start on SCSI according to block_size. device-mapper devices are skipped */ + if (data->type == disk_type_scsi && target->targetbase == NULL) + data->geo.start = data->geo.start / (data->phy_block_size / 512); if (data->partnum != 0) data->partition = stats.st_rdev; /* Try to get device name */ diff -Nru s390-tools-2.15.1/zkey/ekmfweb/Makefile s390-tools-2.16.0/zkey/ekmfweb/Makefile --- s390-tools-2.15.1/zkey/ekmfweb/Makefile 2020-10-28 14:31:59.000000000 +0000 +++ s390-tools-2.16.0/zkey/ekmfweb/Makefile 2021-02-19 14:46:37.000000000 +0000 @@ -7,8 +7,6 @@ libs = $(rootdir)/libutil/libutil.a -export LIBRARY_PATH = $(rootdir)/libekmfweb:$LIBRARY_PATH - zkey-ekmfweb.o: zkey-ekmfweb.c zkey-ekmfweb.h ../kms-plugin.h \ ../cca.h ../utils.h ../pkey.h ../properties.h \ $(rootdir)include/ekmfweb/ekmfweb.h libekmfweb.dep @@ -29,7 +27,7 @@ $(CC) $(ALL_CPPFLAGS) $(ALL_CFLAGS) -fPIC -c $< -o $@ zkey-ekmfweb.so: ALL_CFLAGS += -fPIC -zkey-ekmfweb.so: LDLIBS = -lekmfweb -ldl -lcrypto +zkey-ekmfweb.so: LDLIBS = -L$(rootdir)/libekmfweb -lekmfweb -ldl -lcrypto zkey-ekmfweb.so: ALL_LDFLAGS += -shared -Wl,--version-script=zkey-ekmfweb.map \ -Wl,-z,defs,-Bsymbolic -Wl,-soname,zkey-ekmfweb.so.$(VERM) zkey-ekmfweb.so: zkey-ekmfweb.o properties.o pkey.o cca.o ep11.o utils.o $(libs) @@ -46,9 +44,8 @@ install: all install-libekmfweb.dep zkey-ekmfweb.so $(INSTALL) -d -m 755 $(DESTDIR)$(MANDIR)/man1 $(INSTALL) -m 644 -c zkey-ekmfweb.1 $(DESTDIR)$(MANDIR)/man1 - $(INSTALL) -d -m 755 $(DESTDIR)$(USRLIB64DIR) - $(INSTALL) -d -m 755 $(DESTDIR)$(USRLIB64DIR)/zkey - $(INSTALL) -g $(GROUP) -o $(OWNER) -m 755 -T zkey-ekmfweb.so $(DESTDIR)$(USRLIB64DIR)/zkey/zkey-ekmfweb.so + $(INSTALL) -d -m 755 $(DESTDIR)$(ZKEYKMSPLUGINDIR) + $(INSTALL) -g $(GROUP) -o $(OWNER) -m 755 -T zkey-ekmfweb.so $(DESTDIR)$(ZKEYKMSPLUGINDIR)/zkey-ekmfweb.so clean: rm -f *.o zkey-ekmfweb.so install-libekmfweb.dep libekmfweb.dep diff -Nru s390-tools-2.15.1/zkey/ekmfweb/zkey-ekmfweb.1 s390-tools-2.16.0/zkey/ekmfweb/zkey-ekmfweb.1 --- s390-tools-2.15.1/zkey/ekmfweb/zkey-ekmfweb.1 2020-10-28 14:31:59.000000000 +0000 +++ s390-tools-2.16.0/zkey/ekmfweb/zkey-ekmfweb.1 2021-02-19 14:46:37.000000000 +0000 @@ -321,7 +321,7 @@ or self signed certificate, in the form \fB=(;=)*[;]\fP with types recognized by OpenSSL. .TP -.BR \-s ", " \-\-cert\-extensions\~\fIextensions\fP +.BR \-e ", " \-\-cert\-extensions\~\fIextensions\fP Specifies the certificate extensions for generating a certificate signing request (CSR) or self signed certificate, in the form \fB=[critical,](;=[critical,])*[;]\fP @@ -416,7 +416,7 @@ .B zkey kms configure \-\-ekmfweb\-url https://my.ekmfweb.server \-\-tls\-pin\-server\-pubkey \-\-tls\-verify\-hostname Configures the connection to the EKMF Web server on 'my.ekmfweb.server' and pins the server's public key from the server's TSL certificate as well as -enables verification of the host nmae to match the server's Common Name in the +enables verification of the host name to match the server's Common Name in the certificate. .TP .B zkey kms configure \-\-gen\-csr csr.pem \-\-cert\-subject \(dqCN=my.zkey.client;OU=Example;C=US\(dq @@ -437,10 +437,10 @@ in file 'cert.pem'. .TP .B zkey kms configure \-\-register cert.pem -Registers the zkey client with EKMF Web using the certifcate in file 'cert.pem'. +Registers the zkey client with EKMF Web using the certificate in file 'cert.pem'. .TP .B zkey kms configure \-\-register cert.pem \-\-label\-tags \(dqENV=TEST,APP=LINUX\(dq -Registers the zkey client with EKMF Web using the certifcate in file 'cert.pem' +Registers the zkey client with EKMF Web using the certificate in file 'cert.pem' and the label tags 'ENV=TEST' and 'APP=LINUX' for the identity key. .TP .B zkey kms info @@ -480,7 +480,7 @@ .TP .B zkey kms list \-\-label \(dq*LUKS2*\(dq Displays eligible secure keys managed by EKMF Web where the label name in EKMF -Web contains the work 'LUKS2'. +Web contains the word 'LUKS2'. .TP .B zkey kms import \-\-name seckey Imports the secure key managed by EKMF Web with a zkey name of 'seckey'. diff -Nru s390-tools-2.15.1/zkey/ekmfweb/zkey-ekmfweb.c s390-tools-2.16.0/zkey/ekmfweb/zkey-ekmfweb.c --- s390-tools-2.15.1/zkey/ekmfweb/zkey-ekmfweb.c 2020-10-28 14:31:59.000000000 +0000 +++ s390-tools-2.16.0/zkey/ekmfweb/zkey-ekmfweb.c 2021-02-19 14:46:37.000000000 +0000 @@ -899,24 +899,24 @@ if (rc == 0) { switch (type) { case EVP_PKEY_EC: - printf(" EBMF Web public key: ECC (%s)\n", + printf(" EKMF Web public key: ECC (%s)\n", OBJ_nid2sn(curve)); break; case EVP_PKEY_RSA: - printf(" EBMF Web public key: RSA " + printf(" EKMF Web public key: RSA " "(%d bits)\n", mod_bits); break; default: - printf(" EBMF Web public key: " + printf(" EKMF Web public key: " "(unknown key type)\n"); break; } } else { - printf(" EBMF Web public key: (not available)\n"); + printf(" EKMF Web public key: (not available)\n"); } free(tmp); } else { - printf(" EBMF Web public key: (configuration required)\n"); + printf(" EKMF Web public key: (configuration required)\n"); } printf(" Key templates:\n"); @@ -2520,7 +2520,7 @@ "[y/N]? ", program_invocation_short_name); if (!prompt_for_yes(ph->verbose)) { - _set_error(ph, "Opertion aborted by user"); + _set_error(ph, "Operation aborted by user"); return -ECANCELED; } } @@ -2651,10 +2651,10 @@ goto out; } } - printf("%s: Is this the EKMF Web server you intent to work with " + printf("%s: Is this the EKMF Web server you intend to work with " "[y/N]? ", program_invocation_short_name); if (!prompt_for_yes(ph->verbose)) { - _set_error(ph, "Opertion aborted by user"); + _set_error(ph, "Operation aborted by user"); rc = -ECANCELED; goto out; } @@ -2948,7 +2948,7 @@ printf("%s: Re-generate the identity key [y/N]? ", program_invocation_short_name); if (!prompt_for_yes(ph->verbose)) { - _set_error(ph, "Opertion aborted by user"); + _set_error(ph, "Operation aborted by user"); return -ECANCELED; } } else { @@ -3348,45 +3348,6 @@ } /** - * Gets the next sequence number of the template - * - * @param ph the plugin handle - * @param template_uuid the template UUID - * - * @returns an allocated string, or NULL in case of an error. - */ -static char *_get_seqno(struct plugin_handle *ph, const char *template_uuid) -{ - unsigned int seqno = 0; - char *error_msg = NULL; - char *ret = NULL; - int rc; - - rc = ekmf_get_last_seq_no(&ph->ekmf_config, &ph->curl_handle, - template_uuid, &seqno, &error_msg, - ph->verbose); - if (rc != 0) { - _set_error(ph, "Failed to get last used sequence number for " - "template '%s': %s", - template_uuid, error_msg != NULL ? error_msg : - strerror(-rc)); - _remove_login_token_if_error(ph, rc); - goto out; - } - - seqno++; - util_asprintf(&ret, "%05u", seqno); - - pr_verbose(ph, "Seqno: '%s'", ret); - -out: - if (error_msg != NULL) - free(error_msg); - - return ret; -} - -/** * Parses the label tags passed in via option (=;=tags[i].value = util_strdup(value); + util_str_toupper((char *) + ekmf_tag_list->tags[i].value); } else { ekmf_tag_list->tags[i].value = - _get_seqno(ph, - template_info->uuid); - if (ekmf_tag_list->tags[i].value == - NULL) { - rc = -EIO; - goto out; - } + util_strdup(EKMFWEB_SEQNO_NEXT); } k++; } else { ekmf_tag_list->tags[i].value = - _get_seqno(ph, - template_info->uuid); - if (ekmf_tag_list->tags[i].value == NULL) { - rc = -EIO; - goto out; - } + util_strdup(EKMFWEB_SEQNO_NEXT); } } else { if (tag == NULL) { @@ -3504,11 +3456,10 @@ } ekmf_tag_list->tags[i].value = util_strdup(value); + util_str_toupper((char *)ekmf_tag_list->tags[i].value); k++; } - util_str_toupper((char *)ekmf_tag_list->tags[i].value); - pr_verbose(ph, "Tag: '%s', Value: '%s'", ekmf_tag_list->tags[i].name, ekmf_tag_list->tags[i].value); @@ -3649,7 +3600,7 @@ printf("%s: Re-register the zkey client [y/N]? ", program_invocation_short_name); if (!prompt_for_yes(ph->verbose)) { - _set_error(ph, "Opertion aborted by user"); + _set_error(ph, "Operation aborted by user"); return -ECANCELED; } } @@ -4566,22 +4517,23 @@ struct ekmf_tag_list *ekmf_tag_list, bool null_values_only) { - size_t i; + size_t i, k; ekmf_tag_list->num_tags = 0; ekmf_tag_list->tags = util_malloc( sizeof(struct ekmf_tag) * num_properties); - for (i = 0; i < num_properties; i++) { + for (i = 0, k = 0; i < num_properties; i++) { if (!null_values_only && properties[i].value == NULL) continue; if (null_values_only && properties[i].value != NULL) continue; - ekmf_tag_list->tags[i].name = util_strdup(properties[i].name); - ekmf_tag_list->tags[i].value = properties[i].value != NULL ? + ekmf_tag_list->tags[k].name = util_strdup(properties[i].name); + ekmf_tag_list->tags[k].value = properties[i].value != NULL ? util_strdup(properties[i].value) : NULL; ekmf_tag_list->num_tags++; + k++; } return 0; @@ -4981,10 +4933,10 @@ for (i = 0; i < num_properties; i++) { util_assert(properties[i].name != NULL, "Internal error: property name is NULL"); - util_assert(properties[i].value != NULL, - "Internal error: property value is NULL"); - pr_verbose(ph, " Property '%s': '%s", properties[i].name, - properties[i].value); + + pr_verbose(ph, " Property '%s': '%s'", properties[i].name, + properties[i].value != NULL ? properties[i].value : + "(null)"); } _clear_error(ph); diff -Nru s390-tools-2.15.1/zkey/ekmfweb/zkey-ekmfweb.h s390-tools-2.16.0/zkey/ekmfweb/zkey-ekmfweb.h --- s390-tools-2.15.1/zkey/ekmfweb/zkey-ekmfweb.h 2020-10-28 14:31:59.000000000 +0000 +++ s390-tools-2.16.0/zkey/ekmfweb/zkey-ekmfweb.h 2021-02-19 14:46:37.000000000 +0000 @@ -99,6 +99,7 @@ #define EKMFWEB_CURVE_PRIME "PRIME_CURVE" #define EKMFWEB_CURVE_BAINPOOL "BRAINPOOL_CURVE" #define EKMFWEB_SEQNO_TAG "seqno" +#define EKMFWEB_SEQNO_NEXT "next" #define DEFAULT_IDENTITY_KEY_PUBLIC_EXPONENT 65537 diff -Nru s390-tools-2.15.1/zkey/keystore.c s390-tools-2.16.0/zkey/keystore.c --- s390-tools-2.15.1/zkey/keystore.c 2020-10-28 14:31:59.000000000 +0000 +++ s390-tools-2.16.0/zkey/keystore.c 2021-02-19 14:46:37.000000000 +0000 @@ -40,12 +40,16 @@ char *skey_filename; char *info_filename; char *renc_filename; + char *pass_filename; }; #define FILE_EXTENSION_LEN 5 #define SKEY_FILE_EXTENSION ".skey" #define INFO_FILE_EXTENSION ".info" #define RENC_FILE_EXTENSION ".renc" +#define PASS_FILE_EXTENSION ".pass" + +#define DUMMY_PASSPHRASE_LEN 16 #define LOCK_FILE_NAME ".lock" @@ -76,6 +80,7 @@ #define REC_VOLUME_TYPE "Volume type" #define REC_KMS "KMS" #define REC_KMS_KEY_LABEL "KMS key label" +#define REC_PASSPHRASE_FILE "Dummy passphrase" #define pr_verbose(keystore, fmt...) do { \ if (keystore->verbose) \ @@ -110,6 +115,8 @@ name, INFO_FILE_EXTENSION); util_asprintf(&names->renc_filename, "%s/%s%s", keystore->directory, name, RENC_FILE_EXTENSION); + util_asprintf(&names->pass_filename, "%s/%s%s", keystore->directory, + name, PASS_FILE_EXTENSION); pr_verbose(keystore, "File names for key '%s': '%s' and '%s'", name, names->skey_filename, names->info_filename); @@ -129,6 +136,18 @@ } /** + * Checks if the .pass file exists. + * + * @param[in] file_names names of the files + * + * @returns 1 if the file exist, 0 if the file do not exist + */ +static int _keystore_passphrase_file_exists(struct key_filenames *file_names) +{ + return util_path_is_reg_file("%s", file_names->pass_filename); +} + +/** * Checks if both, the .skey and the .info (and .renc) files exist. * * @param[in] file_names names of the files @@ -146,7 +165,8 @@ if (rc_skey && rc_info) return 1; if (!rc_skey && !rc_info && - _keystore_reencipher_key_exists(file_names) == 0) + _keystore_reencipher_key_exists(file_names) == 0 && + _keystore_passphrase_file_exists(file_names) == 0) return 0; return -1; } @@ -220,6 +240,8 @@ free(names->info_filename); if (names->renc_filename) free(names->renc_filename); + if (names->pass_filename) + free(names->pass_filename); } /** @@ -985,7 +1007,7 @@ process_key_t process_func, void *process_private) { - struct key_filenames file_names = { NULL, NULL, NULL }; + struct key_filenames file_names = { 0 }; char **apqn_filter_list = NULL; char **vol_filter_list = NULL; struct properties *key_props; @@ -1594,6 +1616,129 @@ } /** + * Generate, Set or remove a dummy LUKS2 passphrase of a key. + * + * @param[in] keystore the key store + * @param[in] name the name of the key + * @param[in] file_names the file names of the key + * @param[in] properties the properties of the key + * @param[in] prompt if true, prompt for removal (if passphrase exists) + * + * @returns 0 on success, or a negative errno value on error + */ +static int _keystore_remove_passphrase(struct keystore *keystore, + const char *name, + const struct key_filenames *filenames, + struct properties *properties, + bool prompt) +{ + int rc; + + if (_keystore_passphrase_file_exists((struct key_filenames *)filenames) + && prompt) { + util_print_indented("ATTENTION: When you remove the LUKS2 " + "dummy passphrase of a key, you might no " + "longer be able to open the LUKS2 volumes " + "associated with the key, unless you still " + "know a passphrase of one of the key slots " + "of these volumes!", 0); + _keystore_msg_for_volumes("The following volumes are encrypted " + "with this key:", properties, NULL); + printf("%s: Remove passphrase for key '%s' [y/N]? ", + program_invocation_short_name, name); + if (!prompt_for_yes(keystore->verbose)) { + warnx("Operation aborted"); + return -ECANCELED; + } + } + + rc = remove(filenames->pass_filename); + if (rc != 0 && errno != ENOENT) { + rc = -errno; + warnx("Failed to remove file '%s': %s", + filenames->pass_filename, strerror(-rc)); + return rc; + } + + return 0; +} + +/** + * Generate, Set or remove a dummy LUKS2 passphrase of a key. + * + * @param[in] keystore the key store + * @param[in] name the name of the key + * @param[in] passphrase_file the file name of a file containing a passphrase + * for LUKS2. If NULKL, the passphrase is generated by + * random. + * @param[in] file_names the file names of the key + * @param[in] properties the properties of the key + * @param[in] prompt if true, prompt for change, if passphrase exists + * already + * + * @returns 0 on success, or a negative errno value on error + */ +static int _keystore_set_passphrase(struct keystore *keystore, + const char *name, + const char *passphrase_file, + const struct key_filenames *filenames, + struct properties *properties, + bool prompt) +{ + char *volume_type; + int rc; + + if (_keystore_passphrase_file_exists((struct key_filenames *)filenames) + && prompt) { + warnx("There is already a LUKS2 dummy passphrase associated " + "with key '%s'.", name); + util_print_indented("To change a dummy passphrase of a key, " + "first remove the currently associated " + "passphrase with command 'zkey change " + "--name --remove-dummy-passphrase' " + "and then set the new dummy passphrase for " + "the key.", 0); + return -EEXIST; + } + + volume_type = _keystore_get_volume_type(properties); + if (volume_type == NULL) { + pr_verbose(keystore, "No volume type available"); + return -EINVAL; + } + if (strcasecmp(volume_type, VOLUME_TYPE_LUKS2) != 0) { + warnx("The LUKS2 dummy passphrase can only be set for keys " + "with a volume type of LUKS2."); + free(volume_type); + return -EINVAL; + } + free(volume_type); + + if (passphrase_file != NULL) { + rc = copy_file(passphrase_file, filenames->pass_filename, 0); + if (rc != 0) { + warnx("Failed to copy the passphrase phase '%s': %s", + passphrase_file, strerror(-rc)); + return rc; + } + } else { + rc = copy_file("/dev/urandom", filenames->pass_filename, + DUMMY_PASSPHRASE_LEN); + if (rc != 0) { + warnx("Failed to generate the dummy passphrase: %s", + strerror(-rc)); + return rc; + } + } + + rc = _keystore_set_file_permission(keystore, filenames->pass_filename); + if (rc != 0) + return rc; + + return 0; +} + +/** * Creates the key properties for a key * * @param[in] keystore the key store @@ -1615,6 +1760,8 @@ * @param[in] key_type the type of the key * @param[in] kms the name of the KMS plugin, or NULL if no KMS is bound * @param[out] props the properties object is allocated and returned + * + * @returns 0 on success, or a negative errno value on error */ static int _keystore_create_info_props(struct keystore *keystore, const char *name, @@ -1721,7 +1868,7 @@ * * @param[in] keystore the key store * @param[in] name the name of the key - * @param[in] info_filename the file name of the key info file + * @param[in] filenames the file names of the key files * @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) @@ -1735,7 +1882,12 @@ * default is used. * @param[in] volume_type the type of volume * @param[in] key_type the type of the key + * @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] kms the name of the KMS plugin, or NULL if no KMS is bound + * + * @returns 0 on success, or a negative errno value on error */ static int _keystore_create_info_file(struct keystore *keystore, const char *name, @@ -1746,6 +1898,8 @@ size_t sector_size, const char *volume_type, const char *key_type, + bool gen_passphrase, + const char *passphrase_file, const char *kms) { struct properties *key_props = NULL; @@ -1758,12 +1912,24 @@ if (rc != 0) return rc; + if (gen_passphrase || passphrase_file != NULL) { + rc = _keystore_set_passphrase(keystore, name, gen_passphrase ? + NULL : passphrase_file, + filenames, key_props, true); + if (rc != 0) { + pr_verbose(keystore, "Failed to set the passphrase: %s", + strerror(-rc)); + goto out; + } + } + rc = _keystore_ensure_vp_exists(keystore, filenames, key_props); if (rc != 0) { warnx("Failed to generate the key verification pattern: %s", strerror(-rc)); warnx("Make sure that kernel module 'paes_s390' is loaded and " "that the 'paes' cipher is available"); + remove(filenames->pass_filename); goto out; } @@ -1772,12 +1938,14 @@ pr_verbose(keystore, "Key info file '%s' could not be written: %s", filenames->info_filename, strerror(-rc)); + remove(filenames->pass_filename); goto out; } rc = _keystore_set_file_permission(keystore, filenames->info_filename); if (rc != 0) { remove(filenames->info_filename); + remove(filenames->pass_filename); goto out; } @@ -1809,6 +1977,9 @@ * if NULL, the secure key is generated by random. * @param[in] volume_type the type of volume * @param[in] key_type the type of the key + * @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] pkey_fd the file descriptor of /dev/pkey * * @returns 0 for success or a negative errno in case of an error @@ -1818,9 +1989,10 @@ const char *apqns, bool noapqncheck, size_t sector_size, size_t keybits, bool xts, const char *clear_key_file, const char *volume_type, - const char *key_type, int pkey_fd) + const char *key_type, bool gen_passphrase, + const char *passphrase_file, int pkey_fd) { - struct key_filenames file_names = { NULL, NULL, NULL }; + struct key_filenames file_names = { 0 }; struct properties *key_props = NULL; char **apqn_list = NULL; int rc; @@ -1880,7 +2052,8 @@ rc = _keystore_create_info_file(keystore, name, &file_names, description, volumes, apqns, noapqncheck, sector_size, volume_type, - key_type, NULL); + key_type, gen_passphrase, + passphrase_file, NULL); if (rc != 0) goto out_free_props; @@ -1921,6 +2094,9 @@ * @param[in] xts if true, an XTS key is generated * @param[in] volume_type the type of volume * @param[in] key_type the type of the key (can be NULL) + * @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] kms_options an array of KMS options specified, or NULL if no * KMS options have been specified * @param[in] num_kms_options the number of options in above array @@ -1931,10 +2107,11 @@ const char *description, const char *volumes, size_t sector_size, size_t keybits, bool xts, const char *volume_type, const char *key_type, + bool gen_passphrase, const char *passphrase_file, struct kms_option *kms_options, size_t num_kms_options) { - struct key_filenames file_names = { NULL, NULL, NULL }; + struct key_filenames file_names = { 0 }; struct properties *key_props = NULL; struct kms_info *kms_info; char *apqns = NULL; @@ -2000,8 +2177,21 @@ if (rc != 0) goto out_free_key_filenames; + if (gen_passphrase || passphrase_file != NULL) { + rc = _keystore_set_passphrase(keystore, name, gen_passphrase ? + NULL : passphrase_file, + &file_names, key_props, true); + if (rc != 0) { + pr_verbose(keystore, "Failed to set the passphrase: %s", + strerror(-rc)); + goto out_free_key_filenames; + } + } + rc = generate_kms_key(kms_info, name, key_type, key_props, xts, keybits, file_names.skey_filename, + _keystore_passphrase_file_exists(&file_names) ? + file_names.pass_filename : NULL, kms_options, num_kms_options, keystore->verbose); if (rc != 0) { warnx("KMS plugin '%s' failed to generate key '%s': %s", @@ -2048,8 +2238,10 @@ out_free_props: if (key_props != NULL) properties_free(key_props); - if (rc != 0) + if (rc != 0) { remove(file_names.skey_filename); + remove(file_names.pass_filename); + } out_free_key_filenames: _keystore_free_key_filenames(&file_names); if (apqns != NULL) @@ -2081,6 +2273,9 @@ * 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 @@ -2089,9 +2284,10 @@ 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) { - struct key_filenames file_names = { NULL, NULL, NULL }; + struct key_filenames file_names = { 0 }; struct properties *key_props = NULL; size_t secure_key_size; const char *key_type; @@ -2206,7 +2402,8 @@ rc = _keystore_create_info_file(keystore, name, &file_names, description, volumes, apqns, noapqncheck, sector_size, volume_type, - key_type, NULL); + key_type, gen_passphrase, + passphrase_file, NULL); if (rc != 0) goto out_free_props; @@ -2256,23 +2453,33 @@ * not be changed. * @param[in] volume_type the type of volume. If NULL then the volume type is * not changed. - * * + * @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] remove_passphrase if true, remove the (dummy) passphrase + * @param[in] quiet if true no confirmation prompt is shown when removing + * a (dummy) passphrase + * * @returns 0 for success or a negative errno in case of an error * */ int keystore_change_key(struct keystore *keystore, const char *name, const char *description, const char *volumes, const char *apqns, bool noapqncheck, - long int sector_size, const char *volume_type) + long int sector_size, const char *volume_type, + bool gen_passphrase, const char *passphrase_file, + bool remove_passphrase, bool quiet) { struct volume_check vol_check = { .keystore = keystore, .name = name, .set = 0, .nocheck = 0 }; struct apqn_check apqn_check = { .noonlinecheck = noapqncheck, .nomsg = 0 }; - struct key_filenames file_names = { NULL, NULL, NULL }; + struct key_filenames file_names = { 0 }; struct properties *key_props = NULL; + const char **passphrase_upd = NULL; char *upd_volume_type = NULL; char *apqns_prop, *key_type; + const char *null_ptr = NULL; char *upd_volumes = NULL; size_t secure_key_size; u8 mkvp[MKVP_LENGTH]; @@ -2401,6 +2608,38 @@ upd_volume_type = properties_get(key_props, PROP_NAME_VOLUME_TYPE); + + /* Remove dummy passphrase if change to PLAIN volume type */ + if (strcasecmp(volume_type, VOLUME_TYPE_LUKS2) != 0 && + _keystore_passphrase_file_exists(&file_names)) { + rc = _keystore_remove_passphrase(keystore, name, + &file_names, key_props, + false); + if (rc != 0) + goto out; + + passphrase_upd = &null_ptr; + } + } + + if (gen_passphrase || passphrase_file != NULL) { + rc = _keystore_set_passphrase(keystore, name, gen_passphrase ? + NULL : passphrase_file, + &file_names, key_props, true); + if (rc != 0) + goto out; + + passphrase_upd = (const char **)&file_names.pass_filename; + } + + if (remove_passphrase) { + if (_keystore_passphrase_file_exists(&file_names)) + passphrase_upd = &null_ptr; + + rc = _keystore_remove_passphrase(keystore, name, &file_names, + key_props, !quiet); + if (rc != 0) + goto out; } if (kms_bound) { @@ -2412,7 +2651,7 @@ description, upd_volumes, upd_volume_type, sector_size >= 0 ? sect_size : NULL, - keystore->verbose); + passphrase_upd, keystore->verbose); if (rc != 0) { warnx("KMS plugin '%s' failed to set key properties " "for key '%s': %s", @@ -2441,6 +2680,8 @@ pr_verbose(keystore, "Successfully changed key '%s'", name); out: + if (rc != 0 && passphrase_upd != NULL && *passphrase_upd != NULL) + remove(*passphrase_upd); _keystore_free_key_filenames(&file_names); if (key_props != NULL) properties_free(key_props); @@ -2467,10 +2708,11 @@ int keystore_rename_key(struct keystore *keystore, const char *name, const char *newname) { - struct key_filenames file_names = { NULL, NULL, NULL }; - struct key_filenames new_names = { NULL, NULL, NULL }; + struct key_filenames file_names = { 0 }; + struct key_filenames new_names = { 0 }; struct properties *key_props = NULL; bool reenc_exists = false; + bool pass_exists = false; char *msg; int rc; @@ -2516,6 +2758,16 @@ goto out_rename_info; } } + if (_keystore_passphrase_file_exists(&file_names)) { + pass_exists = true; + if (rename(file_names.pass_filename, + new_names.pass_filename) != 0) { + rc = -errno; + pr_verbose(keystore, "Failed to rename '%s': %s", + file_names.pass_filename, strerror(-rc)); + goto out_rename_info; + } + } key_props = properties_new(); rc = properties_load(key_props, new_names.info_filename, 1); @@ -2531,7 +2783,7 @@ rc = set_kms_key_properties(keystore->kms_info, key_props, newname, NULL, NULL, NULL, NULL, - keystore->verbose); + NULL, keystore->verbose); if (rc != 0) { warnx("KMS plugin '%s' failed to set key properties " "for key '%s': %s", @@ -2549,6 +2801,16 @@ _keystore_msg_for_volumes(msg, key_props, VOLUME_TYPE_PLAIN); free(msg); + if (_keystore_passphrase_file_exists(&new_names)) { + util_asprintf(&msg, "The following volumes are associated with " + "the renamed key '%s'. You should adjust the " + "corresponding crypttab entries to use the new " + "dummy passphrase file name '%s'.", newname, + new_names.pass_filename); + _keystore_msg_for_volumes(msg, key_props, VOLUME_TYPE_LUKS2); + free(msg); + } + pr_verbose(keystore, "Successfully renamed key '%s' to '%s'", name, newname); @@ -2557,6 +2819,8 @@ out_rename_info: if (reenc_exists) rename(file_names.renc_filename, new_names.renc_filename); + if (pass_exists) + rename(file_names.pass_filename, new_names.pass_filename); rename(new_names.info_filename, file_names.info_filename); out_rename_skey: @@ -2613,6 +2877,8 @@ util_rec_def(rec, REC_KMS, UTIL_REC_ALIGN_LEFT, 54, REC_KMS); util_rec_def(rec, REC_KMS_KEY_LABEL, UTIL_REC_ALIGN_LEFT, 54, REC_KMS_KEY_LABEL); + util_rec_def(rec, REC_PASSPHRASE_FILE, UTIL_REC_ALIGN_LEFT, 54, + REC_PASSPHRASE_FILE); util_rec_def(rec, REC_CREATION_TIME, UTIL_REC_ALIGN_LEFT, 54, REC_CREATION_TIME); util_rec_def(rec, REC_CHANGE_TIME, UTIL_REC_ALIGN_LEFT, 54, @@ -2629,7 +2895,8 @@ bool validation, const char *skey_filename, size_t secure_key_size, bool is_xts, size_t clear_key_bitsize, bool valid, - bool is_old_mk, bool reenc_pending, u8 *mkvp) + bool is_old_mk, bool reenc_pending, u8 *mkvp, + const char *pass_filename) { char temp_vp[VERIFICATION_PATTERN_LEN + 2]; char *kms_xts_key1_label = NULL; @@ -2767,6 +3034,10 @@ label_argz_len); else util_rec_set(rec, REC_KMS_KEY_LABEL, "(local)"); + if (pass_filename != NULL) + util_rec_set(rec, REC_PASSPHRASE_FILE, pass_filename); + else + util_rec_set(rec, REC_PASSPHRASE_FILE, "(none)"); util_rec_set(rec, REC_CREATION_TIME, creation); util_rec_set(rec, REC_CHANGE_TIME, change != NULL ? change : "(never)"); @@ -2966,7 +3237,9 @@ is_xts_key(secure_key, secure_key_size), clear_key_bitsize, valid, is_old_mk, _keystore_reencipher_key_exists(file_names), - mkvp); + mkvp, + _keystore_passphrase_file_exists(file_names) ? + file_names->pass_filename : NULL); if (valid && is_old_mk) { util_print_indented("WARNING: The secure key is currently " @@ -3434,8 +3707,8 @@ { struct volume_check vol_check = { .keystore = keystore, .name = newname, .set = 0, .nocheck = 0 }; - struct key_filenames file_names = { NULL, NULL, NULL }; - struct key_filenames new_names = { NULL, NULL, NULL }; + struct key_filenames file_names = { 0 }; + struct key_filenames new_names = { 0 }; struct properties *key_prop = NULL; size_t secure_key_size; bool kms_bound = false; @@ -3536,7 +3809,6 @@ pr_verbose(keystore, "Key info file '%s' could not be written: %s", new_names.info_filename, strerror(-rc)); - remove(new_names.skey_filename); goto out; } @@ -3544,6 +3816,23 @@ if (rc != 0) goto out; + if (_keystore_passphrase_file_exists(&file_names)) { + rc = copy_file(file_names.skey_filename, + new_names.pass_filename, 0); + if (rc != 0) { + pr_verbose(keystore, + "Passphrase file '%s' could not be copied: " + "%s", new_names.pass_filename, + strerror(-rc)); + goto out; + } + + rc = _keystore_set_file_permission(keystore, + file_names.pass_filename); + if (rc != 0) + goto out; + } + pr_verbose(keystore, "Successfully copied key '%s' to '%s'", name, newname); @@ -3551,6 +3840,7 @@ if (rc != 0) { remove(new_names.skey_filename); remove(new_names.info_filename); + remove(new_names.pass_filename); } _keystore_free_key_filenames(&file_names); @@ -3576,7 +3866,7 @@ int keystore_export_key(struct keystore *keystore, const char *name, const char *export_file) { - struct key_filenames file_names = { NULL, NULL, NULL }; + struct key_filenames file_names = { 0 }; size_t secure_key_size; u8 *secure_key; int rc; @@ -3675,7 +3965,7 @@ bool quiet, struct kms_option *kms_options, size_t num_kms_options) { - struct key_filenames file_names = { NULL, NULL, NULL }; + struct key_filenames file_names = { 0 }; struct properties *key_props = NULL; int rc; @@ -3739,6 +4029,13 @@ file_names.renc_filename, strerror(-rc)); } } + if (_keystore_passphrase_file_exists(&file_names)) { + if (remove(file_names.pass_filename) != 0) { + rc = -errno; + pr_verbose(keystore, "Failed to remove '%s': %s", + file_names.pass_filename, strerror(-rc)); + } + } pr_verbose(keystore, "Successfully removed key '%s'", name); out: @@ -3794,7 +4091,9 @@ is_xts_key(secure_key, secure_key_size), clear_key_bitsize, 0, 0, _keystore_reencipher_key_exists(file_names), - NULL); + NULL, + _keystore_passphrase_file_exists(file_names) ? + file_names->pass_filename : NULL); out: free(secure_key); @@ -3910,6 +4209,7 @@ size_t key_file_size, size_t sector_size, const char *volume_type, + const char *passphrase_file, struct crypt_info *info); }; @@ -3925,6 +4225,7 @@ * @param[in] key_file_size the size of the key file in bytes * @param[in] sector_size the sector size in bytes or 0 if not specified * @param[in] volume_type the volume type + * @param[in] passphrase_file the passphrase file name (can be NULL) * @param[in] info processing info * * @returns 0 if successful, a negative errno value otherwise @@ -3937,6 +4238,7 @@ size_t key_file_size, size_t sector_size, const char *volume_type, + const char *passphrase_file, struct crypt_info *info) { char *keyfile_opt = NULL, *offset_opt = NULL; @@ -3957,6 +4259,9 @@ if (info->keyfile_size > 0) util_asprintf(&size_opt, "--keyfile-size %lu ", info->keyfile_size); + } else if (passphrase_file != NULL) { + util_asprintf(&keyfile_opt, "--key-file '%s' ", + passphrase_file); } if (info->tries > 0) util_asprintf(&tries_opt, "--tries %lu ", info->tries); @@ -4069,6 +4374,7 @@ * @param[in] key_file_size the size of the key file in bytes * @param[in] sector_size the sector size in bytes or 0 if not specified * @param[in] volume_type the volume type + * @param[in] passphrase_file the passphrase file name (can be NULL) * @param[in] info processing info (not used here) * * @returns 0 if successful, a negative errno value otherwise @@ -4082,37 +4388,30 @@ size_t key_file_size, size_t sector_size, const char *volume_type, + const char *passphrase_file, struct crypt_info *info) { char temp[1000]; if (strcasecmp(volume_type, VOLUME_TYPE_PLAIN) == 0) { - if (sector_size > 0) { - sprintf(temp, - "WARNING: volume '%s' is using a sector size " - "of %lu. At the time this utility was " - "developed, systemd's support of crypttab did " - "not support to specify a sector size with " - "plain dm-crypt devices. The generated " - "crypttab entry might or might not work, and " - "might need manual adoptions.", volume, - sector_size); - util_print_indented(temp, 0); - } - sprintf(temp, ",sector-size=%lu", sector_size); printf("%s\t%s\t%s\tplain,cipher=%s,size=%lu%s\n", dmname, volume, key_file_name, cipher_spec, key_file_size * 8, sector_size > 0 ? temp : ""); } else if (strcasecmp(volume_type, VOLUME_TYPE_LUKS2) == 0) { - printf("%s\t%s\t%s\tluks", dmname, volume, - info->keyfile != NULL ? info->keyfile : "none"); if (info->keyfile != NULL) { + printf("%s\t%s\t%s\tluks", dmname, volume, + info->keyfile); if (info->keyfile_offset > 0) printf(",keyfile-offset=%lu", info->keyfile_offset); if (info->keyfile_size > 0) printf(",keyfile-size=%lu", info->keyfile_size); + } else if (passphrase_file != NULL) { + printf("%s\t%s\t%s\tluks", dmname, volume, + passphrase_file); + } else { + printf("%s\t%s\tnone\tluks", dmname, volume); } if (info->tries > 0) printf(",tries=%lu", info->tries); @@ -4237,7 +4536,10 @@ rc = info->process_func(keystore, vol, dmname, cipher_spec, file_names->skey_filename, secure_key_size, sector_size, - volume_type, info); + volume_type, + _keystore_passphrase_file_exists(file_names) ? + file_names->pass_filename : NULL, + info); if (rc != 0) break; } @@ -4403,7 +4705,7 @@ const char *key_type, bool noapqncheck, bool quiet, int pkey_fd, struct ext_lib *lib) { - struct key_filenames file_names = { NULL, NULL, NULL }; + struct key_filenames file_names = { 0 }; u8 output_key[2 * MAX_SECURE_KEY_SIZE]; struct properties *properties = NULL; int rc, min_level, selected = 1; @@ -4920,6 +5222,7 @@ * @param[in] volumes the associated volumes of the key (can be NULL) * @param[in] volume_type the volume type of the volume (can be NULL) * @param[in] sector_size the sector size of the volume (0 means default) + * @param[in] passphrase the passphrase of the key (can be NULL) * @param[in] addl_info_argz an argz string containing additional KMS plugin * specific infos to be displayed, or NULL if none. * @param[in] addl_info_len length of the argz string in addl_info_argz @@ -4940,12 +5243,13 @@ const char *volumes, const char *volume_type, size_t sector_size, + const char *passphrase, const char *UNUSED(addl_info_argz), size_t UNUSED(addl_info_len), void *private_data) { - struct key_filenames file_names = { NULL, NULL, NULL }; struct kms_import *import_data = private_data; + struct key_filenames file_names = { 0 }; u8 secure_key[2 * MAX_SECURE_KEY_SIZE]; struct properties *key_props = NULL; char vp[VERIFICATION_PATTERN_LEN]; @@ -5042,6 +5346,23 @@ if (rc != 0) goto out; + if (passphrase != NULL && volume_type != NULL && + strcasecmp(volume_type, VOLUME_TYPE_LUKS2) == 0) { + rc = store_passphrase_from_base64(passphrase, + file_names.pass_filename, + keystore->verbose); + if (rc != 0) { + pr_verbose(keystore, "Failed to parse passphrase: %s", + strerror(-rc)); + goto out; + } + + rc = _keystore_set_file_permission(keystore, + file_names.pass_filename); + if (rc != 0) + goto out_remove; + } + rc = properties_set(key_props, xts ? PROP_NAME_KMS_XTS_KEY1_ID : PROP_NAME_KMS_KEY_ID, key1_id); if (rc != 0) { @@ -5267,7 +5588,9 @@ rc = refresh_kms_key(keystore->kms_info, properties, &description, &cipher, &iv_mode, &volumes, &volume_type, §or_size, - file_names->skey_filename, keystore->verbose); + file_names->skey_filename, + file_names->pass_filename, + keystore->verbose); if (rc != 0) { warnx("KMS plugin '%s' failed to refresh key '%s': %s", keystore->kms_info->plugin_name, name, strerror(-rc)); @@ -5277,6 +5600,17 @@ goto out; } + rc = _keystore_set_file_permission(keystore, file_names->skey_filename); + if (rc != 0) + goto out; + + if (_keystore_passphrase_file_exists(file_names)) { + rc = _keystore_set_file_permission(keystore, + file_names->pass_filename); + if (rc != 0) + goto out; + } + if (!refresh_data->refresh_properties) goto save_props; @@ -5345,6 +5679,10 @@ goto out; } + rc = _keystore_set_file_permission(keystore, file_names->info_filename); + if (rc != 0) + goto out; + out: if (rc == 0) { printf("Successfully refreshed key '%s'\n", name); diff -Nru s390-tools-2.15.1/zkey/keystore.h s390-tools-2.16.0/zkey/keystore.h --- s390-tools-2.15.1/zkey/keystore.h 2020-10-28 14:31:59.000000000 +0000 +++ s390-tools-2.16.0/zkey/keystore.h 2021-02-19 14:46:37.000000000 +0000 @@ -54,12 +54,14 @@ const char *apqns, bool noapqncheck, size_t sector_size, size_t keybits, bool xts, const char *clear_key_file, const char *volume_type, - const char *key_type, int pkey_fd); + const char *key_type, bool gen_passphrase, + const char *passphrase_file, int pkey_fd); int keystore_generate_key_kms(struct keystore *keystore, const char *name, const char *description, const char *volumes, size_t sector_size, size_t keybits, bool xts, const char *volume_type, const char *key_type, + bool gen_passphrase, const char *passphrase_file, struct kms_option *kms_options, size_t num_kms_options); @@ -67,12 +69,15 @@ const char *description, const char *volumes, const char *apqns, bool noapqncheck, size_t sector_size, const char *import_file, const char *volume_type, + bool gen_passphrase, const char *passphrase_file, struct ext_lib *lib); int keystore_change_key(struct keystore *keystore, const char *name, const char *description, const char *volumes, const char *apqns, bool noapqncheck, - long int sector_size, const char *volume_type); + long int sector_size, const char *volume_type, + bool gen_passphrase, const char *passphrase_file, + bool remove_passphrase, bool quiet); int keystore_rename_key(struct keystore *keystore, const char *name, const char *newname); diff -Nru s390-tools-2.15.1/zkey/kms.c s390-tools-2.16.0/zkey/kms.c --- s390-tools-2.15.1/zkey/kms.c 2020-10-28 14:31:59.000000000 +0000 +++ s390-tools-2.16.0/zkey/kms.c 2021-02-19 14:46:37.000000000 +0000 @@ -40,14 +40,16 @@ #define ENVVAR_ZKEY_KMS_PLUGINS "ZKEY_KMS_PLUGINS" #define DEFAULT_KMS_PLUGINS "/etc/zkey/kms-plugins.conf" -#define KMS_PLUGIN_LOCATION "/usr/lib64/zkey" +#ifndef KMS_PLUGIN_LOCATION +#error KMS_PLUGIN_LOCATION must be defined +#endif #define KMS_CONFIG_FILE "kms.conf" #define KMS_CONFIG_PROP_KMS "kms" #define KMS_CONFIG_PROP_KMS_CONFIG "config" #define KMS_CONFIG_PROP_APQNS "apqns" -#define KMS_CONFIG_PROP_CCA_APQNS "ep11_apqns" -#define KMS_CONFIG_PROP_EP11_APQNS "cca_apqns" +#define KMS_CONFIG_PROP_CCA_APQNS "cca_apqns" +#define KMS_CONFIG_PROP_EP11_APQNS "ep11_apqns" #define KMS_CONFIG_LOCAL "local" #define KMS_KEY_PROP_NAME "zkey-name" @@ -62,6 +64,7 @@ #define KMS_KEY_PROP_XTS_KEY2_ID "xts-key2-id" #define KMS_KEY_PROP_XTS_KEY1_LABEL "xts-key1-label" #define KMS_KEY_PROP_XTS_KEY2_LABEL "xts-key2-label" +#define KMS_KEY_PROP_PASSPHRASE "dummy-passphrase-base64" #define KMS_REC_LABEL "Key label" #define KMS_REC_NAME "Name" @@ -2118,6 +2121,8 @@ * @param[in] keybits the key bit size (e.g. 128, 196, 256, 0 to use the * plugin's default) * @param[in] filename the file name to store the key in + * @param[in] passphrase_file the file name containing the LUKS2 passphrase, + * or NULL if no passphrase is set * @param[in] kms_options an array of KMS options specified, or NULL if no * KMS options have been specified * @param[in] num_kms_options the number of options in above array @@ -2129,6 +2134,7 @@ int generate_kms_key(struct kms_info *kms_info, const char *name, const char *key_type, struct properties *key_props, bool xts, size_t keybits, const char *filename, + const char *passphrase_file, struct kms_option *kms_options, size_t num_kms_options, bool verbose) { @@ -2138,12 +2144,13 @@ char key2_label[KMS_KEY_LABEL_SIZE + 1] = { 0 }; char key1_id[KMS_KEY_ID_SIZE + 1] = { 0 }; char key2_id[KMS_KEY_ID_SIZE + 1] = { 0 }; - struct kms_property kms_props[12]; + struct kms_property kms_props[13]; int xts_mode_prop = -1, rc = 0; size_t key_size, key_blob_size; enum kms_key_mode key_mode; size_t num_kms_props = 0; char *sys_volumes = NULL; + char *passphrase = NULL; util_assert(kms_info != NULL, "Internal error: kms_info is NULL"); util_assert(name != NULL, "Internal error: name is NULL"); @@ -2207,6 +2214,19 @@ "XTS-KEY-1"); } + if (passphrase_file != NULL) { + passphrase = read_passphrase_as_base64(passphrase_file, + verbose); + if (passphrase == NULL) { + pr_verbose(verbose, "Failed to read passphrase from " + "file '%s'", passphrase_file); + goto out; + } + + ADD_KMS_PROPS(kms_props, num_kms_props, KMS_KEY_PROP_PASSPHRASE, + passphrase); + } + key_mode = xts ? KMS_KEY_MODE_XTS_1 : KMS_KEY_MODE_NON_XTS; key_blob_size = key_size; @@ -2331,6 +2351,8 @@ free(sector_size); if (sys_volumes != NULL) free(sys_volumes); + if (passphrase != NULL) + free(passphrase); return rc; } @@ -2346,20 +2368,28 @@ * @param[in] volumes the volumes of the key (can be NULL) * @param[in] vol_type the volume type of the key (can be NULL) * @param[in] sector_size the sector_size of the key (can be NULL) + * @param[in] passphrase_file an address to a character string containing the + * file name containing the LUKS2 passphrase, + * or NULL if no passphrase is set. If + * passphrase_file itself is NULL, then no change in + * the passphrase property. + * @param[in] verbose if true, verbose messages are printed * * @returns 0 for success or a negative errno in case of an error. */ - int set_kms_key_properties(struct kms_info *kms_info, struct properties *key_props, const char *name, const char *description, const char *volumes, const char *vol_type, - const char *sector_size, bool verbose) + const char *sector_size, + const char **passphrase_file, + bool verbose) { char *key1_id = NULL, *key2_id = NULL; - struct kms_property kms_props[10]; + struct kms_property kms_props[11]; size_t num_kms_props = 0; char *sys_volumes = NULL; + char *passphrase = NULL; char *sys_name = NULL; bool xts = false; @@ -2414,6 +2444,22 @@ if (sector_size != NULL) ADD_KMS_PROPS(kms_props, num_kms_props, KMS_KEY_PROP_SECTOR_SIZE, sector_size); + if (passphrase_file != NULL) { + /* *passphrase_file is NULL to remove the propoerty */ + if (*passphrase_file != NULL) { + passphrase = + read_passphrase_as_base64(*passphrase_file, + verbose); + if (passphrase == NULL) { + pr_verbose(verbose, "Failed to read passphrase" + " from file '%s'", *passphrase_file); + goto out; + } + } + + ADD_KMS_PROPS(kms_props, num_kms_props, + KMS_KEY_PROP_PASSPHRASE, passphrase); + } if (num_kms_props == 0) goto out; @@ -2448,6 +2494,8 @@ free(key1_id); if (key2_id != NULL) free(key2_id); + if (passphrase != NULL) + free(passphrase); return rc; } @@ -2640,6 +2688,7 @@ const char *name, *volumes, *cipher, *iv_mode, *description; const char *xts_key2_id = NULL, *xts_key2_label = NULL; const char *xts_key, *volume_type, *temp; + const char *passphrase; size_t sector_size = 0; bool xts = false; @@ -2695,6 +2744,8 @@ KMS_KEY_PROP_SECTOR_SIZE); if (temp != NULL) sscanf(temp, "%lu", §or_size); + passphrase = _find_property(properties, num_properties, + KMS_KEY_PROP_PASSPHRASE); if (process_data->label_filter != NULL) { if (fnmatch(process_data->label_filter, key_label, @@ -2728,6 +2779,7 @@ xts ? key_bits * 2 : key_bits, description, cipher, iv_mode, volumes, volume_type, sector_size, + passphrase, addl_info_argz, addl_info_len, process_data->private_data); } @@ -2865,6 +2917,7 @@ const char *UNUSED(cipher), const char *UNUSED(iv_mode), const char *volumes, const char *volume_type, size_t sector_size, + const char *UNUSED(passphrase), const char *addl_info_argz, size_t addl_info_len, void *private_data) { @@ -3139,6 +3192,7 @@ * @param[out] volume_type on return: the volume_type property * @param[out] sector_size on return: the sector_size property * @param[in] filename the file name to store the refreshed key blob in + * @param[in] passphrase_file the file name to store the dummy passphras in * @param[in] verbose if true, verbose messages are printed * * @returns 0 for success or a negative errno in case of an error. @@ -3146,7 +3200,8 @@ int refresh_kms_key(struct kms_info *kms_info, struct properties *key_props, char **description, char **cipher, char **iv_mode, char **volumes, char **volume_type, ssize_t *sector_size, - const char *filename, bool verbose) + const char *filename, const char *passphrase_file, + bool verbose) { struct kms_property *properties = NULL; u8 key_blob[2 * MAX_SECURE_KEY_SIZE]; @@ -3239,6 +3294,29 @@ if (str != NULL) sscanf(str, "%lu", (long unsigned int *)sector_size); } + if (passphrase_file != NULL && volume_type != NULL && + *volume_type != NULL && strcasecmp(*volume_type, "luks2") == 0) { + str = _find_property(properties, num_properties, + KMS_KEY_PROP_PASSPHRASE); + if (str != NULL) { + rc = store_passphrase_from_base64(str, passphrase_file, + verbose); + if (rc != 0) { + pr_verbose(verbose, + "Failed to parse passphrase: %s", + strerror(-rc)); + goto out; + } + } else { + rc = remove(passphrase_file); + if (rc != 0 && errno != ENOENT) { + pr_verbose(verbose, + "Failed to remove passphrase_file: " + "%s", strerror(errno)); + goto out; + } + } + } key_blob_size = sizeof(key_blob); memset(key_blob, 0, key_blob_size); diff -Nru s390-tools-2.15.1/zkey/kms.h s390-tools-2.16.0/zkey/kms.h --- s390-tools-2.15.1/zkey/kms.h 2020-10-28 14:31:59.000000000 +0000 +++ s390-tools-2.16.0/zkey/kms.h 2021-02-19 14:46:37.000000000 +0000 @@ -74,6 +74,7 @@ int generate_kms_key(struct kms_info *kms_info, const char *name, const char *key_type, struct properties *key_props, bool xts, size_t keybits, const char *filename, + const char *passphrase_file, struct kms_option *kms_options, size_t num_kms_options, bool verbose); @@ -81,7 +82,9 @@ struct properties *key_props, const char *name, const char *description, const char *volumes, const char *vol_type, - const char *sector_size, bool verbose); + const char *sector_size, + const char **passphrase_file, + bool verbose); int remove_kms_key(struct kms_info *kms_info, struct properties *key_props, struct kms_option *kms_options, size_t num_kms_options, @@ -94,6 +97,7 @@ const char *description, const char *cipher, const char *iv_mode, const char *volumes, const char *volume_type, size_t sector_size, + const char *passphrase, const char *addl_info_argz, size_t addl_info_len, void *private_data); @@ -118,6 +122,7 @@ int refresh_kms_key(struct kms_info *kms_info, struct properties *key_props, char **description, char **cipher, char **iv_mode, char **volumes, char **volume_type, ssize_t *sector_size, - const char *filename, bool verbose); + const char *filename, const char *passphrase_file, + bool verbose); #endif diff -Nru s390-tools-2.15.1/zkey/Makefile s390-tools-2.16.0/zkey/Makefile --- s390-tools-2.15.1/zkey/Makefile 2020-10-28 14:31:59.000000000 +0000 +++ s390-tools-2.16.0/zkey/Makefile 2021-02-19 14:46:37.000000000 +0000 @@ -34,6 +34,8 @@ libs = $(rootdir)/libutil/libutil.a +CFLAGS += -DKMS_PLUGIN_LOCATION=\"$(ZKEYKMSPLUGINDIR)\" + detect-libcryptsetup.dep: echo "#include " > detect-libcryptsetup.dep echo "#ifndef CRYPT_LUKS2" >> detect-libcryptsetup.dep diff -Nru s390-tools-2.15.1/zkey/pkey.c s390-tools-2.16.0/zkey/pkey.c --- s390-tools-2.15.1/zkey/pkey.c 2020-10-28 14:31:59.000000000 +0000 +++ s390-tools-2.16.0/zkey/pkey.c 2021-02-19 14:46:37.000000000 +0000 @@ -1337,7 +1337,7 @@ int generate_key_verification_pattern(const u8 *key, size_t key_size, char *vp, size_t vp_len, bool verbose) { - int tfmfd = -1, opfd = -1, rc = 0; + int tfmfd = -1, opfd = -1, rc = 0, retry_count = 0; char null_msg[ENC_ZERO_LEN]; char enc_zero[ENC_ZERO_LEN]; struct af_alg_iv *alg_iv; @@ -1385,12 +1385,30 @@ goto out; } +retry_setkey: if (setsockopt(tfmfd, SOL_ALG, ALG_SET_KEY, key, key_size) < 0) { rc = -errno; - pr_verbose(verbose, "Failed to set the key"); + pr_verbose(verbose, "Failed to set the key: %s", + strerror(-rc)); + + /* + * After a master key change, it can happen that the setkey + * operation returns EINVAL or EAGAIN, although the key is + * valid. This is a temporary situation and the operation will + * succeed, once the firmware has completed some internal + * processing related with the master key change. + * Delay 1 second and retry up to 10 times. + */ + if ((rc == -EINVAL || rc == -EAGAIN) && retry_count < 10) { + pr_verbose(verbose, "Retrying after 1 second..."); + retry_count++; + sleep(1); + goto retry_setkey; + } goto out; } + rc = 0; opfd = accept(tfmfd, NULL, NULL); if (opfd < 0) { diff -Nru s390-tools-2.15.1/zkey/utils.c s390-tools-2.16.0/zkey/utils.c --- s390-tools-2.15.1/zkey/utils.c 2020-10-28 14:31:59.000000000 +0000 +++ s390-tools-2.16.0/zkey/utils.c 2021-02-19 14:46:37.000000000 +0000 @@ -15,6 +15,7 @@ #include #include #include +#include #include #include @@ -25,7 +26,8 @@ #include "lib/util_rec.h" #include "lib/util_base.h" - #include +#include +#include #include "utils.h" #include "properties.h" @@ -1128,6 +1130,7 @@ { char str[20]; + fflush(stdout); if (fgets(str, sizeof(str), stdin) == NULL) return false; @@ -1174,3 +1177,199 @@ return mkvp_print_buf; } + +/* + * Copy the contents of one file into another file. If num_bytes is zero, + * then all content until EOF of the input file is copied. Otherwise only up to + * num_bytes is copied. + * + * @param[in] in_file_name the file name of the input file + * @param[in] out_file_name the file name of the output file + * @param[in] num_bytes the number of bytes to copy, or 0 to copy until + * EOF of the input file + * + * @returns zero for success, or a negative error in case of an error + */ +int copy_file(const char *in_file_name, const char *out_file_name, + size_t num_bytes) +{ + FILE *fp_in = NULL, *fp_out = NULL; + size_t num = 0, len; + char buff[1024]; + int rc = 0; + + fp_in = fopen(in_file_name, "r"); + if (fp_in == NULL) { + rc = -errno; + warnx("Failed to open '%s': %s", in_file_name, strerror(-rc)); + goto out; + } + + fp_out = fopen(out_file_name, "w"); + if (fp_out == NULL) { + rc = -errno; + warnx("Failed to open '%s': %s", out_file_name, strerror(-rc)); + goto out; + } + + while (!feof(fp_in) && (num_bytes == 0 || num < num_bytes)) { + len = fread(buff, 1, num_bytes == 0 ? sizeof(buff) : + MIN(num_bytes - num, sizeof(buff)), fp_in); + if (ferror(fp_in)) { + rc = -EIO; + warnx("Failed to read from '%s': %s", in_file_name, + strerror(-rc)); + break; + } + + if (len == 0) + break; + + if (fwrite(buff, len, 1, fp_out) != 1) { + rc = -errno; + warnx("Failed to write to '%s': %s", out_file_name, + strerror(-rc)); + break; + } + + num += len; + } + +out: + if (fp_in != NULL) + fclose(fp_in); + if (fp_out != NULL) + fclose(fp_out); + + return rc; +} + +/** + * Reads the passphrase from the specified file and returns the passphrase as + * an base64 encoded string. The returned string must be freed by the caller. + * + * @param[in] filename the file name of the file containing the passphrase + * @param[in] verbose if true, additional error messages are printed. + * + * @returns an allocated string + */ +char *read_passphrase_as_base64(const char *filename, bool verbose) +{ + unsigned char *ret = NULL, *buf = NULL; + int outlen, len; + struct stat sb; + FILE *fp; + + if (stat(filename, &sb) != 0) { + pr_verbose(verbose, "stat on file '%s' failed: %s", filename, + strerror(errno)); + return NULL; + } + + if (sb.st_size == 0) { + pr_verbose(verbose, "File '%s' is empty", filename); + return NULL; + } + + fp = fopen(filename, "r"); + if (fp == NULL) { + pr_verbose(verbose, "Open of file '%s' failed: %s", filename, + strerror(errno)); + return NULL; + } + + buf = malloc(sb.st_size); + if (buf == NULL) { + pr_verbose(verbose, "Malloc failed"); + goto out; + } + + if (fread(buf, sb.st_size, 1, fp) != 1) { + pr_verbose(verbose, "Reading file '%s' failed: %s", filename, + strerror(errno)); + goto out; + } + + outlen = (sb.st_size / 3) * 4; + if (sb.st_size % 3 > 0) + outlen += 4; + + ret = malloc(outlen + 1); + if (ret == NULL) { + pr_verbose(verbose, "Malloc failed"); + goto out; + } + + len = EVP_EncodeBlock(ret, buf, sb.st_size); + if (len != outlen) { + pr_verbose(verbose, "EVP_EncodeBlock failed"); + free(ret); + ret = NULL; + goto out; + } + + ret[outlen] = '\0'; + +out: + free(buf); + fclose(fp); + + return (char *)ret; +} + +/** + * Stores the passphrase into the specified file. Decodes the base64 string into + * bytes. + * + * @param[in] b64_string the passphrase as a base64 string + * @param[in] filename the file name of the file containing the passphrase + * @param[in] verbose if true, additional error messages are printed. + * + * @returns 0 for success or a negative errno in case of an error. + */ +int store_passphrase_from_base64(const char *b64_string, const char *filename, + bool verbose) +{ + size_t len, outlen, rawlen, i; + unsigned char *buf; + FILE *fp = NULL; + int rc = 0; + + len = strlen(b64_string); + rawlen = outlen = (len / 4) * 3; + for (i = len - 1; b64_string[i] == '='; i--, rawlen--) + ; + + buf = malloc(outlen); + if (buf == NULL) { + pr_verbose(verbose, "Malloc failed"); + return -ENOMEM; + } + + fp = fopen(filename, "w"); + if (fp == NULL) { + pr_verbose(verbose, "Open of file '%s' failed: %s", filename, + strerror(errno)); + rc = -EIO; + goto out; + } + + len = EVP_DecodeBlock(buf, (unsigned char *)b64_string, len); + if (len != outlen) { + pr_verbose(verbose, "EVP_DecodeBlock failed"); + goto out; + } + + if (fwrite(buf, rawlen, 1, fp) != 1) { + pr_verbose(verbose, "Writing file '%s' failed: %s", filename, + strerror(errno)); + goto out; + } + +out: + if (fp != NULL) + fclose(fp); + free(buf); + return rc; +} + diff -Nru s390-tools-2.15.1/zkey/utils.h s390-tools-2.16.0/zkey/utils.h --- s390-tools-2.15.1/zkey/utils.h 2020-10-28 14:31:59.000000000 +0000 +++ s390-tools-2.16.0/zkey/utils.h 2021-02-19 14:46:37.000000000 +0000 @@ -77,4 +77,11 @@ char *printable_mkvp(enum card_type cardtype, u8 *mkvp); +int copy_file(const char *in_file_name, const char *out_file_name, + size_t num_bytes); + +char *read_passphrase_as_base64(const char *filename, bool verbose); +int store_passphrase_from_base64(const char *hex_string, const char *filename, + bool verbose); + #endif diff -Nru s390-tools-2.15.1/zkey/zkey.1 s390-tools-2.16.0/zkey/zkey.1 --- s390-tools-2.15.1/zkey/zkey.1 2020-10-28 14:31:59.000000000 +0000 +++ s390-tools-2.16.0/zkey/zkey.1 2021-02-19 14:46:37.000000000 +0000 @@ -126,6 +126,9 @@ .RB [ \-\-key-type | \-K .IR type ] .RB [ \-\-local | \-L ] +.RB [ \-\-gen\-dummy\-passphrase ] +.RB [ \-\-set\-dummy\-passphrase +.IR passphrase\-file ] .RB [ KMS\-plugin\ specific\ options ] .RB [ \-\-verbose | \-V ] .PP @@ -379,6 +382,9 @@ .IR bytes ] .RB [ \-\-volume-type | \-t .IR type ] +.RB [ \-\-gen\-dummy\-passphrase ] +.RB [ \-\-set\-dummy\-passphrase +.IR passphrase\-file ] .RB [ \-\-verbose | \-V ] . .PP @@ -515,6 +521,11 @@ .IR bytes ] .RB [ \-\-volume-type | \-t .IR type ] +.RB [ \-\-gen\-dummy\-passphrase ] +.RB [ \-\-set\-dummy\-passphrase +.IR passphrase\-file ] +.RB [ \-\-remove\-dummy\-passphrase ] +.RB [ \-\-force | \-F ] .RB [ \-\-verbose | \-V ] . .PP @@ -661,7 +672,7 @@ For LUKS2 volumes, a passphrase is required. You are prompted for the passphrase during system startup when crypttab is evaluated, unless option .B \-\-key\-file -is specified. Option +is specified, or a dummy passphrase is associated with the secure key. Option .B \-\-tries specifies how often a passphrase can be re-entered. When option .B \-\-key\-file @@ -734,7 +745,7 @@ For LUKS2 volumes, a passphrase is required. You are prompted for the passphrase when running the generated commands, unless option .B \-\-key\-file -is specified. Option +is specified, or a dummy passphrase is associated with the secure key. Option .B \-\-tries specifies how often a passphrase can be re-entered. When option .B \-\-key\-file @@ -1236,6 +1247,26 @@ repository is bound to a key management system plugin, then keys are generated using the key management system by default. .TP +.BR \-\-gen\-dummy\-passphrase +Generate a dummy passphrase randomly and associate it with the secure AES key +used to encrypt LUKS2 volume(s). The LUKS2 passphrase is of less or no relevance +for the security of the volume(s), when an secure AES key is used to encrypt the +volume(s), and can therefore be stored insecurely inside the secure key +repository. If for a certain usage the passphrase is of relevance for +security, then do not use this option. This option can only be specified for +keys with a volume type of \fBluks2\fP. +This option is only used for secure keys contained in the secure key repository. +.TP +.BR \-\-set\-dummy\-passphrase\~\fIpassphrase\-file\fP +Set a dummy passphrase that is read from the specified file and associate it +with the secure AES key used to encrypt LUKS2 volume(s). The LUKS2 passphrase +is of less or no relevance for the security of the volume(s), when an secure +AES key is used to encrypt the volume(s), and can therefore be stored insecurely +inside the secure key repository. If for a certain usage the passphrase is of +relevance for security, then do not use this option. This option can only be +specified for keys with a volume type of \fBluks2\fP. +This option is only used for secure keys contained in the secure key repository. +.TP .B KMS-plugin specific options A key management system plugin may offer and even require plugin specific options that can be specified with the generate command when the secure key @@ -1372,6 +1403,26 @@ has been compiled with LUKS2 support enabled. If LUKS2 support is not enabled, the default volume type is \fBplain\fP. This option is only used for secure keys contained in the secure key repository. +.TP +.BR \-\-gen\-dummy\-passphrase +Generate a dummy passphrase randomly and associate it with the secure AES key +used to encrypt LUKS2 volume(s). The LUKS2 passphrase is of less or no relevance +for the security of the volume(s), when an secure AES key is used to encrypt the +volume(s), and can therefore be stored insecurely inside the secure key +repository. If for a certain usage the passphrase is of relevance for +security, then do not use this option. This option can only be specified for +keys with a volume type of \fBluks2\fP. +This option is only used for secure keys contained in the secure key repository. +.TP +.BR \-\-set\-dummy\-passphrase\~\fIpassphrase\-file\fP +Set a dummy passphrase that is read from the specified file and associate it +with the secure AES key used to encrypt LUKS2 volume(s). The LUKS2 passphrase +is of less or no relevance for the security of the volume(s), when an secure +AES key is used to encrypt the volume(s), and can therefore be stored insecurely +inside the secure key repository. If for a certain usage the passphrase is of +relevance for security, then do not use this option. This option can only be +specified for keys with a volume type of \fBluks2\fP. +This option is only used for secure keys contained in the secure key repository. . . . @@ -1523,6 +1574,47 @@ .B zkey has been compiled with LUKS2 support enabled. This option is only used for secure keys contained in the secure key repository. +.TP +.BR \-\-gen\-dummy\-passphrase +Generate a dummy passphrase randomly and associate it with the secure AES key +used to encrypt LUKS2 volume(s). The LUKS2 passphrase is of less or no relevance +for the security of the volume(s), when an secure AES key is used to encrypt the +volume(s), and can therefore be stored insecurely inside the secure key +repository. If for a certain usage the passphrase is of relevance for +security, then do not use this option. This option can only be specified for +keys with a volume type of \fBluks2\fP. When there is already a dummy +passphrase associated with the key, you muts first remove the dummy passphrase +with option \fB\-\-remove\-dummy\-passphrase\fP before you can associate a new +dummy passphrase. +This option is only used for secure keys contained in the secure key repository. +.TP +.BR \-\-set\-dummy\-passphrase\~\fIpassphrase\-file\fP +Set a dummy passphrase that is read from the specified file and associate it +with the secure AES key used to encrypt LUKS2 volume(s). The LUKS2 passphrase +is of less or no relevance for the security of the volume(s), when an secure +AES key is used to encrypt the volume(s), and can therefore be stored insecurely +inside the secure key repository. If for a certain usage the passphrase is of +relevance for security, then do not use this option. This option can only be +specified for keys with a volume type of \fBluks2\fP. When there is already a +dummy passphrase associated with the key, you muts first remove the dummy +passphrase with option \fB\-\-remove\-dummy\-passphrase\fP before you can +associate a new dummy passphrase. +This option is only used for secure keys contained in the secure key repository. +.TP +.BR \-\-remove\-dummy\-passphrase +Remove the associated dummy passphrase used with LUKS2 volume(s). +The user is prompted to confirm the removal of the associated dummy passphrase. +Use the \fB\-\-force\fP option to remove the associated dummy passphrase without +prompting for a confirmation. +This option is only used for secure keys contained in the secure key repository. +.TP +.BR \-F ", " \-\-force\fP +The user is prompted to confirm the removal of the associated dummy passphrase. +Use this option to remove the associated dummy passphrase without prompting for +a confirmation. +This option is only used for secure keys contained in the secure key repository, +and can only be specified together with option +\fB\-\-remove\-dummy\-passphrase\fP. . . . @@ -1590,8 +1682,9 @@ This option is only used for secure keys contained in the secure key repository. .TP .BR \-\-key\-file\~\fIfile\-name\fP -Reads the passphrase from the specified file. If this option is omitted, then -you are prompted to enter the passphrase interactively during system startup. +Reads the passphrase from the specified file. If this option is omitted, and +no dummy passphrase is associated with the secure key, then you are prompted to +enter the passphrase interactively during system startup. This option is passed to the generated crypttab entries for LUKS2 volumes, and is only available if .B zkey @@ -1677,10 +1770,11 @@ has been compiled with LUKS2 support enabled. .TP .BR \-\-key\-file\~\fIfile\-name\fP -Reads the passphrase from the specified file. If this option is omitted, -or if the file\-name is \fI-\fP (a dash), then you are prompted to enter the -passphrase interactively. This option is passed to the generated command(s) -for LUKS2 volumes, and is only available if +Reads the passphrase from the specified file. If this option is omitted, and +no dummy passphrase is associated with the secure key, or if the file\-name is +\fI-\fP (a dash), then you are prompted to enter the passphrase interactively. +This option is passed to the generated command(s) for LUKS2 volumes, and is +only available if .B zkey has been compiled with LUKS2 support enabled. .TP diff -Nru s390-tools-2.15.1/zkey/zkey.c s390-tools-2.16.0/zkey/zkey.c --- s390-tools-2.15.1/zkey/zkey.c 2020-10-28 14:31:59.000000000 +0000 +++ s390-tools-2.16.0/zkey/zkey.c 2021-02-19 14:46:37.000000000 +0000 @@ -79,6 +79,9 @@ char *key_type; char *label; bool local; + bool gen_passphrase; + char *passphrase_file; + bool remove_passphrase; bool kms_bound; bool run; bool batch_mode; @@ -156,6 +159,9 @@ #define OPT_NO_APQN_CHECK 262 #define OPT_NO_VOLUME_CHECK 263 #define OPT_REFRESH_PROPERTIES 264 +#define OPT_GEN_DUMMY_PASSPHRASE 265 +#define OPT_SET_DUMMY_PASSPHRASE 266 +#define OPT_REMOVE_DUMMY_PASSPHRASE 267 /* * Configuration of command line options @@ -271,6 +277,31 @@ "generated by the KMS per default.", .command = COMMAND_GENERATE, }, + { + .option = { "gen-dummy-passphrase", 0, NULL, + OPT_GEN_DUMMY_PASSPHRASE}, + .desc = "Generate a dummy passphrase and associate it with the " + "secure AES key used to encrypt LUKS2 volume(s). The " + "LUKS2 passphrase is of less or no relevance for the " + "security of the volume(s), when an secure AES key is " + "used to encrypt the volume(s), and can therefore be " + "stored insecurely inside the secure key repository.", + .flags = UTIL_OPT_FLAG_NOSHORT, + .command = COMMAND_GENERATE, + }, + { + .option = { "set-dummy-passphrase", required_argument, NULL, + OPT_SET_DUMMY_PASSPHRASE}, + .argument = "passphrase-file", + .desc = "Set a dummy passphrase to be associated with the " + "secure AES key used to encrypt LUKS2 volume(s). The " + "LUKS2 passphrase is of less or no relevance for the " + "security of the volume(s), when an secure AES key is " + "used to encrypt the volume(s), and can therefore be " + "stored insecurely inside the secure key repository.", + .flags = UTIL_OPT_FLAG_NOSHORT, + .command = COMMAND_GENERATE, + }, /***********************************************************/ { .flags = UTIL_OPT_FLAG_SECTION, @@ -429,6 +460,31 @@ .command = COMMAND_IMPORT, }, #endif + { + .option = { "gen-dummy-passphrase", 0, NULL, + OPT_GEN_DUMMY_PASSPHRASE}, + .desc = "Generate a dummy passphrase and associate it with the " + "secure AES key used to encrypt LUKS2 volume(s). The " + "LUKS2 passphrase is of less or no relevance for the " + "security of the volume(s), when an secure AES key is " + "used to encrypt the volume(s), and can therefore be " + "stored insecurely inside the secure key repository.", + .flags = UTIL_OPT_FLAG_NOSHORT, + .command = COMMAND_IMPORT, + }, + { + .option = { "set-dummy-passphrase", required_argument, NULL, + OPT_SET_DUMMY_PASSPHRASE}, + .argument = "passphrase-file", + .desc = "Set a dummy passphrase to be associated with the " + "secure AES key used to encrypt LUKS2 volume(s). The " + "LUKS2 passphrase is of less or no relevance for the " + "security of the volume(s), when an secure AES key is " + "used to encrypt the volume(s), and can therefore be " + "stored insecurely inside the secure key repository.", + .flags = UTIL_OPT_FLAG_NOSHORT, + .command = COMMAND_IMPORT, + }, /***********************************************************/ { .flags = UTIL_OPT_FLAG_SECTION, @@ -593,6 +649,45 @@ .command = COMMAND_CHANGE, }, #endif + { + .option = { "gen-dummy-passphrase", 0, NULL, + OPT_GEN_DUMMY_PASSPHRASE}, + .desc = "Generate a dummy passphrase and associate it with the " + "secure AES key used to encrypt LUKS2 volume(s). The " + "LUKS2 passphrase is of less or no relevance for the " + "security of the volume(s), when an secure AES key is " + "used to encrypt the volume(s), and can therefore be " + "stored insecurely inside the secure key repository.", + .flags = UTIL_OPT_FLAG_NOSHORT, + .command = COMMAND_CHANGE, + }, + { + .option = { "set-dummy-passphrase", required_argument, NULL, + OPT_SET_DUMMY_PASSPHRASE}, + .argument = "passphrase-file", + .desc = "Set a dummy passphrase to be associated with the " + "secure AES key used to encrypt LUKS2 volume(s). The " + "LUKS2 passphrase is of less or no relevance for the " + "security of the volume(s), when an secure AES key is " + "used to encrypt the volume(s), and can therefore be " + "stored insecurely inside the secure key repository.", + .flags = UTIL_OPT_FLAG_NOSHORT, + .command = COMMAND_CHANGE, + }, + { + .option = { "remove-dummy-passphrase", 0, NULL, + OPT_REMOVE_DUMMY_PASSPHRASE}, + .desc = "Remove an associated dummy passphrase used with LUKS2 " + "volume(s).", + .flags = UTIL_OPT_FLAG_NOSHORT, + .command = COMMAND_CHANGE, + }, + { + .option = {"force", 0, NULL, 'F'}, + .desc = "Do not prompt for a confirmation when removing an " + "associated dummy passphrase", + .command = COMMAND_CHANGE, + }, /***********************************************************/ { .flags = UTIL_OPT_FLAG_SECTION, @@ -1631,6 +1726,14 @@ if (g.sector_size < 0) g.sector_size = 0; + if (g.gen_passphrase && g.passphrase_file != NULL) { + warnx("Either '--gen-dummy-passphrase' or " + "'--set-dummy-passphrase' can be specified, but not " + "both"); + util_prg_print_parse_error(); + return EXIT_FAILURE; + } + if (g.kms_info.plugin_lib != NULL && !g.local) { if (g.apqns != NULL) { warnx("Option '--apqns|-a' is not valid for " @@ -1655,6 +1758,8 @@ g.description, g.volumes, g.sector_size, g.keybits, g.xts, g.volume_type, g.key_type, + g.gen_passphrase, + g.passphrase_file, g.kms_options, g.num_kms_options); goto out; @@ -1666,7 +1771,8 @@ rc = keystore_generate_key(g.keystore, g.name, g.description, g.volumes, g.apqns, g.noapqncheck, g.sector_size, g.keybits, g.xts, g.clearkeyfile, - g.volume_type, g.key_type, g.pkey_fd); + g.volume_type, g.key_type, g.gen_passphrase, + g.passphrase_file, g.pkey_fd); out: return rc != 0 ? EXIT_FAILURE : EXIT_SUCCESS; @@ -1729,6 +1835,18 @@ util_prg_print_parse_error(); return EXIT_FAILURE; } + if (g.gen_passphrase) { + warnx("Option '--gen-dummy-passphrase' is not valid " + "for generating a key outside of the repository"); + util_prg_print_parse_error(); + return EXIT_FAILURE; + } + if (g.passphrase_file != NULL) { + warnx("Option '--sen-dummy-passphrase' is not valid " + "for generating a key outside of the repository"); + util_prg_print_parse_error(); + return EXIT_FAILURE; + } rc = cross_check_apqns(NULL, NULL, get_min_card_level_for_keytype(g.key_type), @@ -2111,10 +2229,19 @@ util_prg_print_parse_error(); return EXIT_FAILURE; } + if (g.gen_passphrase && g.passphrase_file != NULL) { + warnx("Either '--gen-dummy-passphrase' or " + "'--set-dummy-passphrase' can be specified, but not " + "both"); + util_prg_print_parse_error(); + return EXIT_FAILURE; + } + rc = keystore_import_key(g.keystore, g.name, g.description, g.volumes, g.apqns, g.noapqncheck, g.sector_size, - g.pos_arg, g.volume_type, &g.lib); + g.pos_arg, g.volume_type, g.gen_passphrase, + g.passphrase_file, &g.lib); return rc != 0 ? EXIT_FAILURE : EXIT_SUCCESS; } @@ -2200,10 +2327,39 @@ util_prg_print_parse_error(); return EXIT_FAILURE; } + if (g.gen_passphrase && g.passphrase_file != NULL) { + warnx("Either '--gen-dummy-passphrase' or " + "'--set-dummy-passphrase' can be specified, but not " + "both"); + util_prg_print_parse_error(); + return EXIT_FAILURE; + } + if (g.gen_passphrase && g.remove_passphrase) { + warnx("Either '--gen-dummy-passphrase' or " + "'--remove-dummy-passphrase' can be specified, but not " + "both"); + util_prg_print_parse_error(); + return EXIT_FAILURE; + } + if (g.passphrase_file != NULL && g.remove_passphrase) { + warnx("Either '--set-dummy-passphrase' or " + "'--remove-dummy-passphrase' can be specified, but not " + "both"); + util_prg_print_parse_error(); + return EXIT_FAILURE; + } + if (g.force && !g.remove_passphrase) { + warnx("Option '--force|-F' is only valid together with " + "the '--remove-dummy-passphrase' option"); + util_prg_print_parse_error(); + return EXIT_FAILURE; + } rc = keystore_change_key(g.keystore, g.name, g.description, g.volumes, g.apqns, g.noapqncheck, g.sector_size, - g.volume_type); + g.volume_type, g.gen_passphrase, + g.passphrase_file, g.remove_passphrase, + g.force); return rc != 0 ? EXIT_FAILURE : EXIT_SUCCESS; } @@ -3056,6 +3212,15 @@ case 'P': g.refresh_properties = 1; break; + case OPT_GEN_DUMMY_PASSPHRASE: + g.gen_passphrase = 1; + break; + case OPT_SET_DUMMY_PASSPHRASE: + g.passphrase_file = optarg; + break; + case OPT_REMOVE_DUMMY_PASSPHRASE: + g.remove_passphrase = 1; + break; case 'h': print_help(command, sub_command); return EXIT_SUCCESS;