s390utils/SOURCES/s390-tools-rhel.patch

22896 lines
688 KiB
Diff
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

From ab2402c95f031ad97544a86e4418cb765313eee8 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Dan=20Hor=C3=A1k?= <dan@danny.cz>
Date: Fri, 31 Aug 2018 10:07:35 +0200
Subject: [PATCH 01/44] drop LOADLIBES variable
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Remove depreciated LOADLIBES variable from the Makefile rules, LDLIBS serves
the same purpose these days.
Signed-off-by: Dan Horák <dan@danny.cz>
---
common.mak | 2 +-
cpuplugd/Makefile | 2 +-
ipl_tools/Makefile | 2 +-
vmconvert/Makefile | 2 +-
vmur/Makefile | 2 +-
ziomon/Makefile | 10 +++++-----
6 files changed, 10 insertions(+), 10 deletions(-)
diff --git a/common.mak b/common.mak
index 571fb30..6f0990e 100644
--- a/common.mak
+++ b/common.mak
@@ -239,7 +239,7 @@ endif
$(CXX) $(ALL_CPPFLAGS) $(ALL_CXXFLAGS) -c $< -o $@
%: %.o
- $(LINK) $(ALL_LDFLAGS) $^ $(LOADLIBES) $(LDLIBS) -o $@
+ $(LINK) $(ALL_LDFLAGS) $^ $(LDLIBS) -o $@
%.a:
$(AR) rcs $@ $^
diff --git a/cpuplugd/Makefile b/cpuplugd/Makefile
index a9a49ab..916638d 100644
--- a/cpuplugd/Makefile
+++ b/cpuplugd/Makefile
@@ -7,7 +7,7 @@ LDLIBS += -lm
OBJECTS = daemon.o cpu.o info.o terms.o config.o main.o getopt.o mem.o
cpuplugd: $(OBJECTS)
- $(LINK) $(ALL_LDFLAGS) $^ $(LOADLIBES) $(LDLIBS) -o $@
+ $(LINK) $(ALL_LDFLAGS) $^ $(LDLIBS) -o $@
clean:
rm -f cpuplugd $(OBJECTS)
diff --git a/ipl_tools/Makefile b/ipl_tools/Makefile
index 128ec3e..506d5cd 100644
--- a/ipl_tools/Makefile
+++ b/ipl_tools/Makefile
@@ -6,7 +6,7 @@ objects = main.o ccw.o fcp.o system.o shutdown.o \
cmd_lsshut.o cmd_chshut.o cmd_lsreipl.o cmd_chreipl.o proc.o
chreipl: $(objects)
- $(LINK) $(ALL_LDFLAGS) $^ $(LOADLIBES) $(LDLIBS) -o $@
+ $(LINK) $(ALL_LDFLAGS) $^ $(LDLIBS) -o $@
lsreipl:
ln -sf chreipl lsreipl
diff --git a/vmconvert/Makefile b/vmconvert/Makefile
index 4d0216c..380eb19 100644
--- a/vmconvert/Makefile
+++ b/vmconvert/Makefile
@@ -9,7 +9,7 @@ libs = $(rootdir)/libvmdump/libvmdump.a
objects = vmconvert.o
vmconvert: $(objects) $(libs)
- $(LINKXX) $(ALL_LDFLAGS) $^ $(LOADLIBES) $(LDLIBS) -o $@
+ $(LINKXX) $(ALL_LDFLAGS) $^ $(LDLIBS) -o $@
install: all
$(INSTALL) -d -m 755 $(DESTDIR)$(USRBINDIR) $(DESTDIR)$(MANDIR)/man1
diff --git a/vmur/Makefile b/vmur/Makefile
index 1a6bddc..2c1c2d5 100644
--- a/vmur/Makefile
+++ b/vmur/Makefile
@@ -11,7 +11,7 @@ libs = $(rootdir)/libvmdump/libvmdump.a \
objects = vmur.o
vmur: $(objects) $(libs)
- $(LINKXX) $(ALL_LDFLAGS) $^ $(LOADLIBES) $(LDLIBS) -o $@
+ $(LINKXX) $(ALL_LDFLAGS) $^ $(LDLIBS) -o $@
install: all
$(INSTALL) -d -m 755 $(DESTDIR)$(USRSBINDIR) $(DESTDIR)$(MANDIR)/man8
diff --git a/ziomon/Makefile b/ziomon/Makefile
index 778401b..61c2399 100644
--- a/ziomon/Makefile
+++ b/ziomon/Makefile
@@ -12,33 +12,33 @@ ziomon_mgr_main.o: ziomon_mgr.c
ziomon_mgr: LDLIBS += -lm
ziomon_mgr: ziomon_dacc.o ziomon_util.o ziomon_mgr_main.o ziomon_tools.o \
ziomon_zfcpdd.o ziomon_msg_tools.o
- $(LINK) $(ALL_LDFLAGS) $^ $(LOADLIBES) $(LDLIBS) -o $@
+ $(LINK) $(ALL_LDFLAGS) $^ $(LDLIBS) -o $@
ziomon_util_main.o: ziomon_util.c ziomon_util.h
$(CC) -DWITH_MAIN $(ALL_CFLAGS) $(ALL_CPPFLAGS) -c $< -o $@
ziomon_util: LDLIBS += -lm
ziomon_util: ziomon_util_main.o ziomon_tools.o
- $(LINK) $(ALL_LDFLAGS) $^ $(LOADLIBES) $(LDLIBS) -o $@
+ $(LINK) $(ALL_LDFLAGS) $^ $(LDLIBS) -o $@
ziomon_zfcpdd_main.o: ziomon_zfcpdd.c ziomon_zfcpdd.h
$(CC) -DWITH_MAIN $(ALL_CFLAGS) $(ALL_CPPFLAGS) -c $< -o $@
ziomon_zfcpdd: LDLIBS += -lm -lrt -lpthread
ziomon_zfcpdd: ziomon_zfcpdd_main.o ziomon_tools.o
- $(LINK) $(ALL_LDFLAGS) $^ $(LOADLIBES) $(LDLIBS) -o $@
+ $(LINK) $(ALL_LDFLAGS) $^ $(LDLIBS) -o $@
ziorep_traffic: ziorep_traffic.o ziorep_framer.o ziorep_frameset.o \
ziorep_printers.o ziomon_dacc.o ziomon_util.o \
ziomon_msg_tools.o ziomon_tools.o ziomon_zfcpdd.o \
ziorep_cfgreader.o ziorep_collapser.o ziorep_utils.o \
ziorep_filters.o
- $(LINKXX) $(ALL_LDFLAGS) $^ $(LOADLIBES) $(LDLIBS) -o $@
+ $(LINKXX) $(ALL_LDFLAGS) $^ $(LDLIBS) -o $@
ziorep_utilization: ziorep_utilization.o ziorep_framer.o ziorep_frameset.o \
ziorep_printers.o ziomon_dacc.o ziomon_util.o \
ziomon_msg_tools.o ziomon_tools.o ziomon_zfcpdd.o \
ziorep_cfgreader.o ziorep_collapser.o ziorep_utils.o \
ziorep_filters.o
- $(LINKXX) $(ALL_LDFLAGS) $^ $(LOADLIBES) $(LDLIBS) -o $@
+ $(LINKXX) $(ALL_LDFLAGS) $^ $(LDLIBS) -o $@
install: all
$(SED) -e 's/%S390_TOOLS_VERSION%/$(S390_TOOLS_RELEASE)/' \
--
2.21.3
From ed7cf76fd149a9fed3ce9f728e072bccc44997cb Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Dan=20Hor=C3=A1k?= <dan@danny.cz>
Date: Fri, 31 Aug 2018 10:13:38 +0200
Subject: [PATCH 02/44] zkey: Drop redundant include
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Signed-off-by: Dan Horák <dan@danny.cz>
---
zkey/Makefile | 1 -
1 file changed, 1 deletion(-)
diff --git a/zkey/Makefile b/zkey/Makefile
index 68f35cf..725cb3b 100644
--- a/zkey/Makefile
+++ b/zkey/Makefile
@@ -22,7 +22,6 @@ else
INSTALL_TARGETS += zkey-cryptsetup-skip-cryptsetup2
endif
-CPPFLAGS += -I../include
LIBS = $(rootdir)/libutil/libutil.a
detect-libcryptsetup.h:
--
2.21.3
From 3a354d9d8e83a36edb9ce68fb85b3cc4afd19991 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Dan=20Hor=C3=A1k?= <dan@danny.cz>
Date: Fri, 31 Aug 2018 10:17:07 +0200
Subject: [PATCH 03/44] zkey: Be consistent when refering to libutil.a
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Signed-off-by: Dan Horák <dan@danny.cz>
---
zkey/Makefile | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/zkey/Makefile b/zkey/Makefile
index 725cb3b..7e2047a 100644
--- a/zkey/Makefile
+++ b/zkey/Makefile
@@ -22,7 +22,7 @@ else
INSTALL_TARGETS += zkey-cryptsetup-skip-cryptsetup2
endif
-LIBS = $(rootdir)/libutil/libutil.a
+libs = $(rootdir)/libutil/libutil.a
detect-libcryptsetup.h:
echo "#include <libcryptsetup.h>" > detect-libcryptsetup.h
@@ -69,10 +69,10 @@ keystore.o: keystore.c keystore.h properties.h
zkey-cryptsetup.o: check-dep-zkey-cryptsetup zkey-cryptsetup.c pkey.h misc.h
zkey: LDLIBS = -ldl -lcrypto
-zkey: zkey.o pkey.o properties.o keystore.o $(LIBS)
+zkey: zkey.o pkey.o properties.o keystore.o $(libs)
zkey-cryptsetup: LDLIBS = -ldl -lcryptsetup -ljson-c
-zkey-cryptsetup: zkey-cryptsetup.o pkey.o $(LIBS)
+zkey-cryptsetup: zkey-cryptsetup.o pkey.o $(libs)
install-common:
$(INSTALL) -d -m 755 $(DESTDIR)$(USRBINDIR)
--
2.21.3
From 913721b06be3b4662593c3f1a344256c46ce841a Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Dan=20Hor=C3=A1k?= <dan@danny.cz>
Date: Fri, 31 Aug 2018 04:29:39 -0400
Subject: [PATCH 04/44] zkey: Be explicit about linking the tools
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
I've met cases when the make's default rule for linking was used instead omitting
the ALL_LDFLAGS variable. The linking rule from common.mak is defined for linking
*.o files only, here we have libutil.a too.
Signed-off-by: Dan Horák <dan@danny.cz>
---
zkey/Makefile | 2 ++
1 file changed, 2 insertions(+)
diff --git a/zkey/Makefile b/zkey/Makefile
index 7e2047a..901ddd4 100644
--- a/zkey/Makefile
+++ b/zkey/Makefile
@@ -70,9 +70,11 @@ zkey-cryptsetup.o: check-dep-zkey-cryptsetup zkey-cryptsetup.c pkey.h misc.h
zkey: LDLIBS = -ldl -lcrypto
zkey: zkey.o pkey.o properties.o keystore.o $(libs)
+ $(LINK) $(ALL_LDFLAGS) $^ $(LDLIBS) -o $@
zkey-cryptsetup: LDLIBS = -ldl -lcryptsetup -ljson-c
zkey-cryptsetup: zkey-cryptsetup.o pkey.o $(libs)
+ $(LINK) $(ALL_LDFLAGS) $^ $(LDLIBS) -o $@
install-common:
$(INSTALL) -d -m 755 $(DESTDIR)$(USRBINDIR)
--
2.21.3
From 6a3da6ec34a0947bd14cd0a36ab08b607236a414 Mon Sep 17 00:00:00 2001
From: Ingo Franzki <ifranzki@linux.ibm.com>
Date: Wed, 17 Oct 2018 13:52:48 +0200
Subject: [PATCH 05/44] zkey: Makefile: Avoid relink of modules during 'make
install'
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Because targets check-dep-zkey and check-dep-zkey-cryptsetup
do not produce any file, any targets that have a pre-req on those
targets are rebuilt during 'make install'.
Also correct .PHONY targets.
Fixes: https://github.com/ibm-s390-tools/s390-tools/issues/46
Signed-off-by: Ingo Franzki <ifranzki@linux.ibm.com>
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
---
zkey/Makefile | 9 +++++++--
1 file changed, 7 insertions(+), 2 deletions(-)
diff --git a/zkey/Makefile b/zkey/Makefile
index 901ddd4..bc7bc33 100644
--- a/zkey/Makefile
+++ b/zkey/Makefile
@@ -37,6 +37,7 @@ check-dep-zkey:
"openssl/evp.h", \
"openssl-devel", \
"HAVE_OPENSSL=0")
+ touch check-dep-zkey
check-dep-zkey-cryptsetup: detect-libcryptsetup.h
$(call check_dep, \
@@ -50,6 +51,7 @@ check-dep-zkey-cryptsetup: detect-libcryptsetup.h
"json-c/json.h", \
"json-c-devel", \
"HAVE_JSONC=0")
+ touch check-dep-zkey-cryptsetup
zkey-skip:
echo " SKIP zkey due to HAVE_OPENSSL=0"
@@ -93,6 +95,9 @@ install-zkey-cryptsetup:
install: all install-common $(INSTALL_TARGETS)
clean:
- rm -f *.o zkey zkey-cryptsetup detect-libcryptsetup.h
+ rm -f *.o zkey zkey-cryptsetup detect-libcryptsetup.h \
+ check-dep-zkey check-dep-zkey-cryptsetup
-.PHONY: all install clean
+.PHONY: all install clean zkey-skip zkey-cryptsetup-skip-cryptsetup2 \
+ zkey-cryptsetup-skip-jsonc install-common install-zkey \
+ install-zkey-cryptsetup
--
2.21.3
From e7b59a9f7cc5d049ef68331855a881beb85a1347 Mon Sep 17 00:00:00 2001
From: Ingo Franzki <ifranzki@linux.ibm.com>
Date: Thu, 25 Oct 2018 12:57:29 +0200
Subject: [PATCH 06/44] zkey: Makefile: Don't rebuild .o.d files on 'make
install'
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
The .o.d make targets in common.mak do not expect that
header files are generated by a make target. When a new header
file is generated, the .o.d targets will be rebuilt on the
next make invocation, because that new header file is then
detected, and is then treated as a new dependency of all .o.d
targets.
Signed-off-by: Ingo Franzki <ifranzki@linux.ibm.com>
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
---
zkey/Makefile | 18 +++++++++---------
1 file changed, 9 insertions(+), 9 deletions(-)
diff --git a/zkey/Makefile b/zkey/Makefile
index bc7bc33..a44b14b 100644
--- a/zkey/Makefile
+++ b/zkey/Makefile
@@ -24,12 +24,12 @@ endif
libs = $(rootdir)/libutil/libutil.a
-detect-libcryptsetup.h:
- echo "#include <libcryptsetup.h>" > detect-libcryptsetup.h
- echo "#ifndef CRYPT_LUKS2" >> detect-libcryptsetup.h
- echo " #error libcryptsetup version 2.0.3 is required" >> detect-libcryptsetup.h
- echo "#endif" >> detect-libcryptsetup.h
- echo "int i = CRYPT_SLOT_UNBOUND;" >> detect-libcryptsetup.h
+detect-libcryptsetup.dep:
+ echo "#include <libcryptsetup.h>" > detect-libcryptsetup.dep
+ echo "#ifndef CRYPT_LUKS2" >> detect-libcryptsetup.dep
+ echo " #error libcryptsetup version 2.0.3 is required" >> detect-libcryptsetup.dep
+ echo "#endif" >> detect-libcryptsetup.dep
+ echo "int i = CRYPT_SLOT_UNBOUND;" >> detect-libcryptsetup.dep
check-dep-zkey:
$(call check_dep, \
@@ -39,10 +39,10 @@ check-dep-zkey:
"HAVE_OPENSSL=0")
touch check-dep-zkey
-check-dep-zkey-cryptsetup: detect-libcryptsetup.h
+check-dep-zkey-cryptsetup: detect-libcryptsetup.dep
$(call check_dep, \
"zkey-cryptsetup", \
- "detect-libcryptsetup.h", \
+ "detect-libcryptsetup.dep", \
"cryptsetup-devel version 2.0.3", \
"HAVE_CRYPTSETUP2=0", \
"-I.")
@@ -95,7 +95,7 @@ install-zkey-cryptsetup:
install: all install-common $(INSTALL_TARGETS)
clean:
- rm -f *.o zkey zkey-cryptsetup detect-libcryptsetup.h \
+ rm -f *.o zkey zkey-cryptsetup detect-libcryptsetup.dep \
check-dep-zkey check-dep-zkey-cryptsetup
.PHONY: all install clean zkey-skip zkey-cryptsetup-skip-cryptsetup2 \
--
2.21.3
From dc92c4ce7963dcf5018c3ef3672b6d217f29842f Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Dan=20Hor=C3=A1k?= <dan@danny.cz>
Date: Mon, 19 Nov 2018 11:26:40 +0100
Subject: [PATCH 07/44] zpcictl: Add tool to manage PCI devices (#1525409)
Summary: zpcictl: Add tool to manage PCI devices
Description: Use the zpcictl tool to manage PCI devices on the IBM Z
platform. Initial functions include generating firmware
error logs, resetting PCI devices, and preparing a device
for further repair actions.
---
.gitignore | 1 +
Makefile | 2 +-
zpcictl/Makefile | 18 +++
zpcictl/zpcictl.8 | 80 ++++++++++
zpcictl/zpcictl.c | 379 ++++++++++++++++++++++++++++++++++++++++++++++
zpcictl/zpcictl.h | 60 ++++++++
6 files changed, 539 insertions(+), 1 deletion(-)
create mode 100644 zpcictl/Makefile
create mode 100644 zpcictl/zpcictl.8
create mode 100644 zpcictl/zpcictl.c
create mode 100644 zpcictl/zpcictl.h
diff --git a/.gitignore b/.gitignore
index 41feaf6..042233f 100644
--- a/.gitignore
+++ b/.gitignore
@@ -89,3 +89,4 @@ zipl/src/chreipl_helper.device-mapper
zipl/src/zipl
zipl/src/zipl_helper.device-mapper
zkey/zkey
+zpcictl/zpcictl
diff --git a/Makefile b/Makefile
index adc92b6..bb2b900 100644
--- a/Makefile
+++ b/Makefile
@@ -8,7 +8,7 @@ TOOL_DIRS = zipl zdump fdasd dasdfmt dasdview tunedasd \
tape390 osasnmpd 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
+ systemd hmcdrvfs cpacfstats zdev dump2tar zkey netboot etc zpcictl
SUB_DIRS = $(LIB_DIRS) $(TOOL_DIRS)
all: $(TOOL_DIRS)
diff --git a/zpcictl/Makefile b/zpcictl/Makefile
new file mode 100644
index 0000000..2b315c5
--- /dev/null
+++ b/zpcictl/Makefile
@@ -0,0 +1,18 @@
+include ../common.mak
+
+all: zpcictl
+
+libs = $(rootdir)/libutil/libutil.a
+
+zpcictl: zpcictl.o $(libs)
+
+install: all
+ $(INSTALL) -d -m 755 $(DESTDIR)$(BINDIR) $(DESTDIR)$(MANDIR)/man8
+ $(INSTALL) -g $(GROUP) -o $(OWNER) -m 755 zpcictl $(DESTDIR)$(BINDIR)
+ $(INSTALL) -g $(GROUP) -o $(OWNER) -m 644 zpcictl.8 \
+ $(DESTDIR)$(MANDIR)/man8
+
+clean:
+ rm -f *.o *~ zpcictl core
+
+.PHONY: all install clean
diff --git a/zpcictl/zpcictl.8 b/zpcictl/zpcictl.8
new file mode 100644
index 0000000..4f8fcd8
--- /dev/null
+++ b/zpcictl/zpcictl.8
@@ -0,0 +1,80 @@
+.\" Copyright 2017 IBM Corp.
+.\" s390-tools is free software; you can redistribute it and/or modify
+.\" it under the terms of the MIT license. See LICENSE for details.
+.\"
+.\" Macro for inserting an option description prologue.
+.\" .OD <long> [<short>] [args]
+.de OD
+. ds args "
+. if !'\\$3'' .as args \fI\\$3\fP
+. if !'\\$4'' .as args \\$4
+. if !'\\$5'' .as args \fI\\$5\fP
+. if !'\\$6'' .as args \\$6
+. if !'\\$7'' .as args \fI\\$7\fP
+. PD 0
+. if !'\\$2'' .IP "\fB\-\\$2\fP \\*[args]" 4
+. if !'\\$1'' .IP "\fB\-\-\\$1\fP \\*[args]" 4
+. PD
+..
+.
+.TH zpcictl 8 "Oct 2018" s390-tools zpcictl
+.
+.SH NAME
+zpcictl - Manage PCI devices on z Systems
+.
+.
+.SH SYNOPSIS
+.B "zpcictl"
+.I "OPTIONS"
+.I "DEVICE"
+.
+.
+.SH DESCRIPTION
+.B zpcictl
+is a tool for managing PCI devices on the IBM z Systems platform. It is
+especially used for reporting errorneous PCI devices to the service element.
+
+.B Note:
+For NVMe devices additional data (such as S.M.A.R.T. data) is collected and sent
+with any error handling action. The smartmontools are required to be installed
+for this to work.
+.PP
+.
+.
+.SH DEVICE
+.B DEVICE
+can be either the PCI slot address (e.g. 0000:00:00.0) or the main device node
+of an NVMe device (e.g. /dev/nvme0).
+.
+.
+.SH OPTIONS
+.SS Error Handling
+.OD reset "" "DEVICE"
+Reset
+.I DEVICE
+and initiate a re-initialisation of the adapter.
+.PP
+.
+.OD deconfigure "" "DEVICE"
+De-configure
+.I DEVICE
+and prepare for any repair action. This action will move the
+PCI device from a configured to a reserved state.
+.PP
+.
+.OD report-error "" "DEVICE"
+Report any device error for
+.IR DEVICE .
+The
+.I DEVICE
+is marked as erroneous and no further action is initiated on it.
+.PP
+.
+.SS Misc
+.OD help "h" ""
+Print usage information, then exit.
+.PP
+.
+.OD version "v" ""
+Print version information, then exit.
+.PP
diff --git a/zpcictl/zpcictl.c b/zpcictl/zpcictl.c
new file mode 100644
index 0000000..5f63b17
--- /dev/null
+++ b/zpcictl/zpcictl.c
@@ -0,0 +1,379 @@
+/*
+ * zpcictl - Manage PCI devices on z Systems
+ *
+ * Copyright IBM Corp. 2018
+ *
+ * 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 <errno.h>
+#include <fcntl.h>
+#include <sys/stat.h>
+#include <sys/sysmacros.h>
+#include <time.h>
+
+#include "lib/util_base.h"
+#include "lib/util_libc.h"
+#include "lib/util_opt.h"
+#include "lib/util_path.h"
+#include "lib/util_prg.h"
+#include "lib/util_proc.h"
+#include "lib/util_rec.h"
+#include "lib/util_scandir.h"
+
+#include "zpcictl.h"
+
+#define SMARTCTL_CMDLINE "smartctl -x %s 2>/dev/null"
+
+static const struct util_prg prg = {
+ .desc = "Use zpcictl to manage PCI devices on s390\n"
+ "DEVICE is the slot id or node of the device (e.g. /dev/nvme0)",
+ .args = "DEVICE",
+ .copyright_vec = {
+ {
+ .owner = "IBM Corp.",
+ .pub_first = 2018,
+ .pub_last = 2018,
+ },
+ UTIL_PRG_COPYRIGHT_END
+ }
+};
+
+/* Defines for options with no short command */
+#define OPT_RESET 128
+#define OPT_DECONF 129
+#define OPT_REPORT_ERR 130
+
+static struct util_opt opt_vec[] = {
+ UTIL_OPT_SECTION("ERROR HANDLING"),
+ {
+ .option = { "reset", no_argument, NULL, OPT_RESET },
+ .desc = "Reset device",
+ .flags = UTIL_OPT_FLAG_NOSHORT,
+ },
+ {
+ .option = { "deconfigure", no_argument, NULL, OPT_DECONF },
+ .desc = "De-configure device and prepare for any repair action",
+ .flags = UTIL_OPT_FLAG_NOSHORT,
+ },
+ {
+ .option = { "report-error", no_argument, NULL, OPT_REPORT_ERR },
+ .desc = "Report device error to service element (SE)",
+ .flags = UTIL_OPT_FLAG_NOSHORT,
+ },
+ UTIL_OPT_SECTION("MISC"),
+ UTIL_OPT_HELP,
+ UTIL_OPT_VERSION,
+ UTIL_OPT_END
+};
+
+static int is_char_dev(const char *dev)
+{
+ struct stat s;
+
+ if (stat(dev, &s))
+ return 0;
+
+ return S_ISCHR(s.st_mode);
+}
+
+static int is_blk_dev(const char *dev)
+{
+ struct stat s;
+
+ if (stat(dev, &s))
+ return 0;
+
+ return S_ISBLK(s.st_mode);
+}
+
+static void fopen_err(char *path)
+{
+ warnx("Could not open file %s: %s", path, strerror(errno));
+ free(path);
+ exit(EXIT_FAILURE);
+}
+
+#define READ_CHUNK_SIZE 512
+
+static char *collect_smart_data(struct zpci_device *pdev)
+{
+ char *buffer = NULL;
+ size_t count = 0;
+ char *cmd;
+ FILE *fd;
+
+ util_asprintf(&cmd, SMARTCTL_CMDLINE, pdev->device);
+ fd = popen(cmd, "r");
+ if (!fd)
+ goto out;
+
+ while (!feof(fd)) {
+ buffer = realloc(buffer, count + READ_CHUNK_SIZE);
+ if (!buffer) {
+ warnx("Could not collect S.M.A.R.T. data");
+ goto out;
+ }
+ count += fread(&buffer[count], 1, READ_CHUNK_SIZE, fd);
+ if (ferror(fd)) {
+ free(buffer);
+ buffer = NULL;
+ goto out;
+ }
+ }
+
+ buffer = realloc(buffer, count);
+ if (!buffer && count > 0)
+ warnx("Could not collect S.M.A.R.T. data");
+ if (buffer)
+ buffer[count] = '\0';
+
+out:
+ pclose(fd);
+ free(cmd);
+
+ return buffer;
+}
+
+static unsigned int sysfs_read_value(struct zpci_device *pdev, const char *attr)
+{
+ unsigned int val;
+ char *path;
+ FILE *fp;
+
+ path = util_path_sysfs("bus/pci/devices/%s/%s", pdev->slot, attr);
+ fp = fopen(path, "r");
+ if (!fp)
+ fopen_err(path);
+ fscanf(fp, "%x", &val);
+ fclose(fp);
+ free(path);
+
+ return val;
+}
+
+static void sysfs_write_data(struct zpci_report_error *report, char *slot)
+{
+ char *path;
+ int fd, rc;
+
+ path = util_path_sysfs("bus/pci/devices/%s/report_error", slot);
+ fd = open(path, O_WRONLY);
+ if (!fd)
+ fopen_err(path);
+ rc = write(fd, report, sizeof(*report));
+ if (rc == -1)
+ warnx("Could not write to file: %s: %s", path, strerror(errno));
+ if (close(fd))
+ warnx("Could not close file: %s: %s", path, strerror(errno));
+ free(path);
+}
+
+static void sysfs_get_slot_addr(const char *dev, char *slot)
+{
+ unsigned int major, minor;
+ struct stat dev_stat;
+ char addr[13];
+ char *path;
+ FILE *fp;
+
+ if (stat(dev, &dev_stat) != 0) {
+ errx(EXIT_FAILURE, "Could not get stat information for %s: %s",
+ dev, strerror(errno));
+ }
+ major = major(dev_stat.st_rdev);
+ minor = minor(dev_stat.st_rdev);
+
+ path = util_path_sysfs("dev/char/%u:%u/address", major, minor);
+ fp = fopen(path, "r");
+ if (!fp)
+ fopen_err(path);
+ fscanf(fp, "%s", addr);
+ fclose(fp);
+ free(path);
+
+ strcpy(slot, addr);
+}
+
+static void get_device_node(struct zpci_device *pdev)
+{
+ struct dirent **de_vec;
+ char *path, *dev;
+ char slot[13];
+ int count, i;
+
+ path = util_path_sysfs("bus/pci/devices/%s/nvme", pdev->slot);
+ count = util_scandir(&de_vec, alphasort, path, "nvme*");
+ if (count == -1) {
+ warnx("Could not read directory %s: %s", path, strerror(errno));
+ free(path);
+ exit(EXIT_FAILURE);
+ }
+
+ for (i = 0; i < count; i++) {
+ util_asprintf(&dev, "/dev/%s", de_vec[i]->d_name);
+ sysfs_get_slot_addr(dev, slot);
+ if (strcmp(slot, pdev->slot) == 0) {
+ pdev->device = dev;
+ break;
+ }
+ }
+
+ util_scandir_free(de_vec, count);
+ free(path);
+}
+
+static int device_exists(char *dev)
+{
+ char *path;
+ int rc = 0;
+
+ path = util_path_sysfs("bus/pci/devices/%s", dev);
+ if (util_path_exists(path) || util_path_exists(dev))
+ rc = 1;
+ free(path);
+
+ return rc;
+}
+
+static void get_device_info(struct zpci_device *pdev, char *dev)
+{
+ if (!device_exists(dev))
+ errx(EXIT_FAILURE, "Device %s not found", dev);
+ if (is_blk_dev(dev))
+ errx(EXIT_FAILURE, "Unsupported device type %s", dev);
+ if (is_char_dev(dev)) {
+ sysfs_get_slot_addr(dev, pdev->slot);
+ pdev->device = dev;
+ } else {
+ strcpy(pdev->slot, dev);
+ }
+
+ pdev->class = sysfs_read_value(pdev, "class");
+ pdev->fid = sysfs_read_value(pdev, "function_id");
+ pdev->pchid = sysfs_read_value(pdev, "pchid");
+
+ /* In case a slot address was specified, we still need to figure out
+ * the device node for NVMe devices. Otherwise we won't be able to
+ * collect S.M.A.R.T. data at a later point.
+ */
+ if (!pdev->device && pdev->class == PCI_CLASS_NVME)
+ get_device_node(pdev);
+}
+
+/*
+ * Issue an SCLP Adapter Error Notification event with a specific action
+ * qualifier.
+ *
+ * Collect additional information when possible (e.g. S.M.A.R.T. data for NVMe
+ * devices).
+ */
+static void sclp_issue_action(struct zpci_device *pdev, int action)
+{
+ struct zpci_report_error report = {
+ .header = { 0 },
+ .data = { 0 }
+ };
+ char *sdata = NULL;
+
+ report.header.version = 1;
+ report.header.action = action;
+ report.header.length = sizeof(report.data);
+ report.data.timestamp = (__u64)time(NULL);
+ report.data.err_log_id = 0x4713;
+
+ if (pdev->class == PCI_CLASS_NVME)
+ sdata = collect_smart_data(pdev);
+ if (sdata) {
+ strncpy(report.data.log_data, sdata, sizeof(report.data.log_data));
+ free(sdata);
+ }
+ sysfs_write_data(&report, pdev->slot);
+}
+
+/*
+ * Reset the PCI device and initiate a re-initialization.
+ */
+static void sclp_reset_device(struct zpci_device *pdev)
+{
+ sclp_issue_action(pdev, SCLP_ERRNOTIFY_AQ_RESET);
+}
+
+/*
+ * De-Configure/repair PCI device. Moves the device from configured
+ * to reserved state.
+ */
+static void sclp_deconfigure(struct zpci_device *pdev)
+{
+ sclp_issue_action(pdev, SCLP_ERRNOTIFY_AQ_DECONF);
+}
+
+/*
+ * Report an error to the SE.
+ */
+static void sclp_report_error(struct zpci_device *pdev)
+{
+ sclp_issue_action(pdev, SCLP_ERRNOTIFY_AQ_REPORT_ERR);
+}
+
+static void parse_cmdline(int argc, char *argv[], struct options *opts)
+{
+ int cmd;
+
+ util_prg_init(&prg);
+ util_opt_init(opt_vec, NULL);
+
+ do {
+ cmd = util_opt_getopt_long(argc, argv);
+
+ switch (cmd) {
+ case OPT_RESET:
+ opts->reset = 1;
+ break;
+ case OPT_DECONF:
+ opts->deconfigure = 1;
+ break;
+ case OPT_REPORT_ERR:
+ opts->report = 1;
+ break;
+ case 'h':
+ util_prg_print_help();
+ util_opt_print_help();
+ exit(EXIT_SUCCESS);
+ case 'v':
+ util_prg_print_version();
+ exit(EXIT_SUCCESS);
+ case -1:
+ /* End of options string */
+ if (argc == 1) {
+ errx(EXIT_FAILURE,
+ "Use '%s --help' for more information",
+ argv[0]);
+ }
+ break;
+ }
+ } while (cmd != -1);
+}
+
+int main(int argc, char *argv[])
+{
+ struct zpci_device pdev = { 0 };
+ struct options opts = { 0 };
+
+ parse_cmdline(argc, argv, &opts);
+
+ if (optind >= argc)
+ errx(EXIT_FAILURE, "No device specified");
+
+ get_device_info(&pdev, argv[optind]);
+
+ if (opts.reset)
+ sclp_reset_device(&pdev);
+ else if (opts.deconfigure)
+ sclp_deconfigure(&pdev);
+ else if (opts.report)
+ sclp_report_error(&pdev);
+
+ return 0;
+}
diff --git a/zpcictl/zpcictl.h b/zpcictl/zpcictl.h
new file mode 100644
index 0000000..5187e7c
--- /dev/null
+++ b/zpcictl/zpcictl.h
@@ -0,0 +1,60 @@
+/*
+ * zpcictl - Manage PCI devices on z Systems
+ *
+ * Copyright IBM Corp. 2018
+ *
+ * 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 ZPCICTL_H
+#define ZPCICTL_H
+
+#include <linux/types.h>
+#include "lib/zt_common.h"
+
+#define SCLP_ERRNOTIFY_AQ_RESET 0
+#define SCLP_ERRNOTIFY_AQ_DECONF 1
+#define SCLP_ERRNOTIFY_AQ_REPORT_ERR 2
+
+#define PCI_CLASS_UNCLASSIFIED 0x000000U
+#define PCI_CLASS_NVME 0x010802U
+#define PCI_CLASS_NETWORK 0x020000U
+
+struct options {
+ unsigned int reset;
+ unsigned int deconfigure;
+ unsigned int report;
+};
+
+struct zpci_device {
+ u16 fid;
+ u16 pchid;
+ u32 class;
+ char slot[13];
+ char *device;
+};
+
+struct zpci_report_error_header {
+ __u8 version; /* Interface version byte */
+ __u8 action; /* Action qualifier byte
+ * 0: Adapter Reset Request
+ * 1: Deconfigure and repair action requested
+ * 2: Informational Report
+ */
+ __u16 length; /* Length of Subsequent Data (up to 4K SCLP header) */
+ __u8 data[0]; /* Subsequent Data passed verbatim to SCLP ET 24 */
+};
+
+struct zpci_report_error_data {
+ __u64 timestamp;
+ __u64 err_log_id;
+ char log_data[4054]; /* We cannot exceed a total of 4074 bytes (header + data) */
+};
+
+struct zpci_report_error {
+ struct zpci_report_error_header header;
+ struct zpci_report_error_data data;
+} __packed;
+
+#endif /* ZPCICTL_H */
--
2.21.3
From c17b203ee85a63c812654e937f0c5cb3da6229e3 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Dan=20Hor=C3=A1k?= <dan@danny.cz>
Date: Mon, 19 Nov 2018 11:35:09 +0100
Subject: [PATCH 08/44] zpcictl: Read device link to obtain device address
(#1639220)
Description: zpcictl: Read device link to obtain device address
Symptom: Issuing zpcictl --report-error /dev/nvme0 leads to:
zpcictl: Could not open file /sys/dev/char/249:0/address:
No such file or directory
Problem: The sysfs attribute 'address' in /sys/dev/char/x:x/ is not
present on all kernel versions.
Solution: Read the device link using readlink() to obtain the device
address instead.
Reproduction: For example, run zpcictl --report-error /dev/nvme0 before
kernel 4.8.
---
zpcictl/zpcictl.8 | 13 +++++----
zpcictl/zpcictl.c | 72 +++++++++++++++++++++++++++++++----------------
2 files changed, 55 insertions(+), 30 deletions(-)
diff --git a/zpcictl/zpcictl.8 b/zpcictl/zpcictl.8
index 4f8fcd8..41fab9a 100644
--- a/zpcictl/zpcictl.8
+++ b/zpcictl/zpcictl.8
@@ -1,4 +1,4 @@
-.\" Copyright 2017 IBM Corp.
+.\" Copyright IBM Corp. 2018
.\" s390-tools is free software; you can redistribute it and/or modify
.\" it under the terms of the MIT license. See LICENSE for details.
.\"
@@ -30,9 +30,10 @@ zpcictl - Manage PCI devices on z Systems
.
.
.SH DESCRIPTION
+With
.B zpcictl
-is a tool for managing PCI devices on the IBM z Systems platform. It is
-especially used for reporting errorneous PCI devices to the service element.
+, you can manage PCI devices on the IBM z Systems platform. It is especially
+used for reporting erroneous PCI devices to the service element.
.B Note:
For NVMe devices additional data (such as S.M.A.R.T. data) is collected and sent
@@ -44,7 +45,9 @@ for this to work.
.SH DEVICE
.B DEVICE
can be either the PCI slot address (e.g. 0000:00:00.0) or the main device node
-of an NVMe device (e.g. /dev/nvme0).
+of an NVMe device (e.g.
+.I /dev/nvme0
+).
.
.
.SH OPTIONS
@@ -52,7 +55,7 @@ of an NVMe device (e.g. /dev/nvme0).
.OD reset "" "DEVICE"
Reset
.I DEVICE
-and initiate a re-initialisation of the adapter.
+and initiate a re-initialization of the PCI device.
.PP
.
.OD deconfigure "" "DEVICE"
diff --git a/zpcictl/zpcictl.c b/zpcictl/zpcictl.c
index 5f63b17..a9e38fb 100644
--- a/zpcictl/zpcictl.c
+++ b/zpcictl/zpcictl.c
@@ -104,6 +104,9 @@ static char *collect_smart_data(struct zpci_device *pdev)
char *cmd;
FILE *fd;
+ if (!pdev->device)
+ return NULL;
+
util_asprintf(&cmd, SMARTCTL_CMDLINE, pdev->device);
fd = popen(cmd, "r");
if (!fd)
@@ -155,45 +158,60 @@ static unsigned int sysfs_read_value(struct zpci_device *pdev, const char *attr)
static void sysfs_write_data(struct zpci_report_error *report, char *slot)
{
+ size_t r_size;
char *path;
- int fd, rc;
+ FILE *fp;
+
+ r_size = sizeof(*report);
path = util_path_sysfs("bus/pci/devices/%s/report_error", slot);
- fd = open(path, O_WRONLY);
- if (!fd)
+ fp = fopen(path, "w");
+ if (!fp)
fopen_err(path);
- rc = write(fd, report, sizeof(*report));
- if (rc == -1)
+ if (fwrite(report, 1, r_size, fp) != r_size)
warnx("Could not write to file: %s: %s", path, strerror(errno));
- if (close(fd))
+ if (fclose(fp))
warnx("Could not close file: %s: %s", path, strerror(errno));
free(path);
}
-static void sysfs_get_slot_addr(const char *dev, char *slot)
+/* lstat() doesn't work for sysfs files, so we have to work with a fixed size */
+#define READLINK_SIZE 256
+
+static int sysfs_get_slot_addr(const char *dev, char *slot)
{
+ char device[READLINK_SIZE], *result;
unsigned int major, minor;
struct stat dev_stat;
- char addr[13];
+ ssize_t len;
char *path;
- FILE *fp;
if (stat(dev, &dev_stat) != 0) {
- errx(EXIT_FAILURE, "Could not get stat information for %s: %s",
- dev, strerror(errno));
+ warnx("Could not get stat information for %s: %s",
+ dev, strerror(errno));
+ return 0;
}
major = major(dev_stat.st_rdev);
minor = minor(dev_stat.st_rdev);
- path = util_path_sysfs("dev/char/%u:%u/address", major, minor);
- fp = fopen(path, "r");
- if (!fp)
- fopen_err(path);
- fscanf(fp, "%s", addr);
- fclose(fp);
+ path = util_path_sysfs("dev/char/%u:%u/device", major, minor);
+ len = readlink(path, device, READLINK_SIZE - 1);
free(path);
+ if (len != -1) {
+ device[len] = '\0';
+ } else {
+ warnx("Could not read device link for %s", dev);
+ return 0;
+ }
+
+ result = strrchr(device, '/');
+ if (result)
+ result++;
+ else
+ result = device;
+ strcpy(slot, result);
- strcpy(slot, addr);
+ return 1;
}
static void get_device_node(struct zpci_device *pdev)
@@ -208,12 +226,13 @@ static void get_device_node(struct zpci_device *pdev)
if (count == -1) {
warnx("Could not read directory %s: %s", path, strerror(errno));
free(path);
- exit(EXIT_FAILURE);
+ return;
}
for (i = 0; i < count; i++) {
util_asprintf(&dev, "/dev/%s", de_vec[i]->d_name);
- sysfs_get_slot_addr(dev, slot);
+ if (!sysfs_get_slot_addr(dev, slot))
+ continue;
if (strcmp(slot, pdev->slot) == 0) {
pdev->device = dev;
break;
@@ -240,11 +259,13 @@ static int device_exists(char *dev)
static void get_device_info(struct zpci_device *pdev, char *dev)
{
if (!device_exists(dev))
- errx(EXIT_FAILURE, "Device %s not found", dev);
+ errx(EXIT_FAILURE, "Could not find device %s", dev);
if (is_blk_dev(dev))
errx(EXIT_FAILURE, "Unsupported device type %s", dev);
if (is_char_dev(dev)) {
- sysfs_get_slot_addr(dev, pdev->slot);
+ if (!sysfs_get_slot_addr(dev, pdev->slot))
+ errx(EXIT_FAILURE,
+ "Could not determine slot address for %s", dev);
pdev->device = dev;
} else {
strcpy(pdev->slot, dev);
@@ -254,9 +275,10 @@ static void get_device_info(struct zpci_device *pdev, char *dev)
pdev->fid = sysfs_read_value(pdev, "function_id");
pdev->pchid = sysfs_read_value(pdev, "pchid");
- /* In case a slot address was specified, we still need to figure out
- * the device node for NVMe devices. Otherwise we won't be able to
- * collect S.M.A.R.T. data at a later point.
+ /*
+ * In case a slot address was specified, the device node for NVMe
+ * devices is still needed. Otherwise it won't be possible to collect
+ * S.M.A.R.T. data at a later point.
*/
if (!pdev->device && pdev->class == PCI_CLASS_NVME)
get_device_node(pdev);
--
2.21.3
From 8d8e0a970dbde0ca4486192aed806d837f080370 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Dan=20Hor=C3=A1k?= <dan@danny.cz>
Date: Mon, 19 Nov 2018 11:36:36 +0100
Subject: [PATCH 09/44] zpcictl: Change wording of man-page and help output
(#1643451)
Description: zpcictl: Change wording of man-page and help output
Symptom: -
Problem: Wording not clear enough and platform description is wrong.
Solution: Change wording and use the correct platform description.
Reproduction: -
Upstream-ID: aaaebb2030c80151ecac528f22cb9a52752b868c
---
zpcictl/zpcictl.8 | 38 +++++++++++++++-----------------------
zpcictl/zpcictl.c | 15 ++++++++-------
2 files changed, 23 insertions(+), 30 deletions(-)
diff --git a/zpcictl/zpcictl.8 b/zpcictl/zpcictl.8
index 41fab9a..38aa344 100644
--- a/zpcictl/zpcictl.8
+++ b/zpcictl/zpcictl.8
@@ -20,7 +20,7 @@
.TH zpcictl 8 "Oct 2018" s390-tools zpcictl
.
.SH NAME
-zpcictl - Manage PCI devices on z Systems
+zpcictl - Manage PCI devices on IBM Z
.
.
.SH SYNOPSIS
@@ -30,50 +30,42 @@ zpcictl - Manage PCI devices on z Systems
.
.
.SH DESCRIPTION
-With
+Use
.B zpcictl
-, you can manage PCI devices on the IBM z Systems platform. It is especially
-used for reporting erroneous PCI devices to the service element.
+to manage PCI devices on the IBM Z platform. In particular,
+use this command to report defective PCI devices to the service element.
.B Note:
For NVMe devices additional data (such as S.M.A.R.T. data) is collected and sent
-with any error handling action. The smartmontools are required to be installed
-for this to work.
+with any error handling action. For this extendend data collection, the
+smartmontools must be installed.
.PP
.
.
.SH DEVICE
-.B DEVICE
-can be either the PCI slot address (e.g. 0000:00:00.0) or the main device node
-of an NVMe device (e.g.
+A PCI slot address (e.g. 0000:00:00.0) or the main device node of an NVMe
+device (e.g.
.I /dev/nvme0
).
.
.
.SH OPTIONS
-.SS Error Handling
+.SS Error Handling Options
.OD reset "" "DEVICE"
-Reset
-.I DEVICE
-and initiate a re-initialization of the PCI device.
+Reset and re-initialize the PCI device.
.PP
.
.OD deconfigure "" "DEVICE"
-De-configure
-.I DEVICE
-and prepare for any repair action. This action will move the
-PCI device from a configured to a reserved state.
+Deconfigure the PCI device and prepare for any repair action. This action
+changes the status of the PCI device from configured to reserved.
.PP
.
.OD report-error "" "DEVICE"
-Report any device error for
-.IR DEVICE .
-The
-.I DEVICE
-is marked as erroneous and no further action is initiated on it.
+Report any device error for the PCI device.
+The device is marked as defective but no further action is taken.
.PP
.
-.SS Misc
+.SS General Options
.OD help "h" ""
Print usage information, then exit.
.PP
diff --git a/zpcictl/zpcictl.c b/zpcictl/zpcictl.c
index a9e38fb..7cfa0d3 100644
--- a/zpcictl/zpcictl.c
+++ b/zpcictl/zpcictl.c
@@ -27,8 +27,9 @@
#define SMARTCTL_CMDLINE "smartctl -x %s 2>/dev/null"
static const struct util_prg prg = {
- .desc = "Use zpcictl to manage PCI devices on s390\n"
- "DEVICE is the slot id or node of the device (e.g. /dev/nvme0)",
+ .desc = "Use zpcictl to manage PCI devices on IBM Z\n"
+ "DEVICE is the slot ID or node of the device "
+ "(e.g. 0000:00:00.0 or /dev/nvme0)",
.args = "DEVICE",
.copyright_vec = {
{
@@ -46,23 +47,23 @@ static const struct util_prg prg = {
#define OPT_REPORT_ERR 130
static struct util_opt opt_vec[] = {
- UTIL_OPT_SECTION("ERROR HANDLING"),
+ UTIL_OPT_SECTION("ERROR HANDLING OPTIONS"),
{
.option = { "reset", no_argument, NULL, OPT_RESET },
- .desc = "Reset device",
+ .desc = "Reset the device",
.flags = UTIL_OPT_FLAG_NOSHORT,
},
{
.option = { "deconfigure", no_argument, NULL, OPT_DECONF },
- .desc = "De-configure device and prepare for any repair action",
+ .desc = "Deconfigure the device to prepare for any repair action",
.flags = UTIL_OPT_FLAG_NOSHORT,
},
{
.option = { "report-error", no_argument, NULL, OPT_REPORT_ERR },
- .desc = "Report device error to service element (SE)",
+ .desc = "Report a device error to the service element (SE)",
.flags = UTIL_OPT_FLAG_NOSHORT,
},
- UTIL_OPT_SECTION("MISC"),
+ UTIL_OPT_SECTION("GENERAL OPTIONS"),
UTIL_OPT_HELP,
UTIL_OPT_VERSION,
UTIL_OPT_END
--
2.21.3
From 916022e17d4cb189c0d83028ed09d3f4600046dd Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Dan=20Hor=C3=A1k?= <dan@danny.cz>
Date: Mon, 19 Nov 2018 11:37:34 +0100
Subject: [PATCH 10/44] zdev: qeth BridgePort and VNICC attribute conflict
(#1643452)
Description: zdev: qeth BridgePort and VNICC attribute conflict
Symptom: chzdev cannot set VNICC attributes due to a conflict with
BridgePort attributes.
Problem: Existing conflict checking always assumes a BridgePort and a
VNICC attribute are active.
Solution: Introduce a function that determines if BridgePort or VNICC
attributes are active and use only active attributes for conflict
checking.
Reproduction: Set VNICC attribute with chzdev w/o active BridgePort attributes.
Upstream-ID: ffe91d1b3082730905caee75b6ec8b05e3cf46a3
---
zdev/src/qeth.c | 33 +++++++++++++++++++++++++++++++++
1 file changed, 33 insertions(+)
diff --git a/zdev/src/qeth.c b/zdev/src/qeth.c
index 46bc23d..6191ad1 100644
--- a/zdev/src/qeth.c
+++ b/zdev/src/qeth.c
@@ -1171,6 +1171,37 @@ static exit_code_t check_ineffective_settings(struct setting_list *list,
return rc;
}
+/* Check if a possibly conflicting setting is active in the configuration */
+static bool conflict_setting_active(struct setting *s)
+{
+ enum qeth_attr_group_type t;
+
+ t = get_attr_group_type(s);
+ if (t != group_bridge && t != group_vnicc) {
+ /* Check BridgePort and VNICC attributes only */
+ return false;
+ }
+ if (s->specified) {
+ /* Specified on the command line: We are strict here and do not
+ * allow to specify VNICC and BridgePort attributes in the same
+ * command to avoid issues when attributes are enabled/disabled
+ * in the wrong order. Example: disable VNICC and enable
+ * BridgePort in the same command would result in an error
+ * because BridgePort attributes are set first.
+ */
+ return true;
+ }
+ if (attrib_match_default(s->attrib, s->value)) {
+ /* Not active if set to default value */
+ return false;
+ }
+ if (s->actual_value && strncmp(s->actual_value, "n/a", 3) == 0) {
+ /* Not active if in n/a state (conflicting attribute set) */
+ return false;
+ }
+ return true;
+}
+
/* Check if there are conflicting attribute settings */
static exit_code_t check_conflicting_settings(struct setting_list *list)
{
@@ -1182,6 +1213,8 @@ static exit_code_t check_conflicting_settings(struct setting_list *list)
util_list_iterate(&list->list, s) {
if (s->removed)
continue;
+ if (!conflict_setting_active(s))
+ continue;
t = get_attr_group_type(s);
if (t == group_bridge && (!bridge || !bridge->specified))
bridge = s;
--
2.21.3
From e2916bdb493e9565d74b0334fa45f17c6f0f730c Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Dan=20Hor=C3=A1k?= <dan@danny.cz>
Date: Mon, 19 Nov 2018 11:38:30 +0100
Subject: [PATCH 11/44] qethqoat: add OSA-Express7S support (#1644384)
Description: qethqoat: add OSA-Express7S support
Symptom: qethqoat fails to report HW generation and link speed for
OSA-Express7S network card.
Problem: Missing identifiers for new values in the QUERY OAT data.
Solution: Add identifiers for card generation and link speed.
Reproduction: Run qethqoat on an OSA-Express7S, check the output for
'OSA Generation' and 'Port speed/mode'.
Upstream-ID: 20145b6d06debd47944bff0a471d17e5eba07010
---
qethqoat/qethqoat.c | 6 ++++++
qethqoat/qethqoat.h | 2 ++
2 files changed, 8 insertions(+)
diff --git a/qethqoat/qethqoat.c b/qethqoat/qethqoat.c
index 6a30d1d..7110c28 100644
--- a/qethqoat/qethqoat.c
+++ b/qethqoat/qethqoat.c
@@ -208,6 +208,9 @@ static void print_physical(struct qeth_qoat_physical *phdr)
case OAT_OSA_GEN_OSAE6S:
osagen = "OSA-Express6S";
break;
+ case OAT_OSA_GEN_OSAE7S:
+ osagen = "OSA-Express7S";
+ break;
default:
sprintf(tmp, "unknown (0x%x)", phdr->osa_gen);
osagen = tmp;
@@ -239,6 +242,9 @@ static void print_physical(struct qeth_qoat_physical *phdr)
case OAT_PORT_SPEED_10gbs_full:
speed = "10 Gb/s / full duplex";
break;
+ case OAT_PORT_SPEED_25gbs_full:
+ speed = "25 Gb/s / full duplex";
+ break;
case OAT_PORT_SPEED_UNKNOWN:
speed = "unknown / unknown";
break;
diff --git a/qethqoat/qethqoat.h b/qethqoat/qethqoat.h
index dd7e992..e692937 100644
--- a/qethqoat/qethqoat.h
+++ b/qethqoat/qethqoat.h
@@ -58,6 +58,7 @@ struct qeth_qoat_physical {
#define OAT_OSA_GEN_OSAE4S 0x02
#define OAT_OSA_GEN_OSAE5S 0x03
#define OAT_OSA_GEN_OSAE6S 0x04
+#define OAT_OSA_GEN_OSAE7S 0x05
__u8 osa_gen;
#define OAT_PORT_SPEED_UNKNOWN 0x00
#define OAT_PORT_SPEED_10mbs_half 0x01
@@ -68,6 +69,7 @@ struct qeth_qoat_physical {
#define OAT_PORT_SPEED_1000mbs_full 0x06
#define OAT_PORT_SPEED_NA 0x07
#define OAT_PORT_SPEED_10gbs_full 0x08
+#define OAT_PORT_SPEED_25gbs_full 0x0A
__u8 port_speed;
#define OAT_PORT_MEDIA_COPPER 0x01
#define OAT_PORT_MEDIA_MULTI_MODE 0x02
--
2.21.3
From ba7eb5cd9d858f2ff8bd29e0b610337896587b94 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Dan=20Hor=C3=A1k?= <dan@danny.cz>
Date: Mon, 19 Nov 2018 11:39:35 +0100
Subject: [PATCH 12/44] zcryptctl: add zcryptctl to manage multiple zcrypt
nodes (#1646354)
Summary: zcryptctl: add zcryptctl to manage multiple zcrypt nodes
Description: There is a new zcrypt kernel feature which provides
multiple customizable device nodes for the zcrypt
device driver. Here is the userspace part of this
which adds a new application zcryptctl for user
friendly management of this feature.
Upstream-ID: f05f7d656b13c3904f0c55e86ebe9e9b19fcd222
---
zconf/zcrypt/Makefile | 7 +-
zconf/zcrypt/zcryptctl.8 | 147 ++++++
zconf/zcrypt/zcryptctl.c | 1030 ++++++++++++++++++++++++++++++++++++++
3 files changed, 1182 insertions(+), 2 deletions(-)
create mode 100644 zconf/zcrypt/zcryptctl.8
create mode 100644 zconf/zcrypt/zcryptctl.c
diff --git a/zconf/zcrypt/Makefile b/zconf/zcrypt/Makefile
index 698e148..d075f34 100644
--- a/zconf/zcrypt/Makefile
+++ b/zconf/zcrypt/Makefile
@@ -1,21 +1,24 @@
include ../../common.mak
-all: chzcrypt lszcrypt
+all: chzcrypt lszcrypt zcryptctl
libs = $(rootdir)/libutil/libutil.a
chzcrypt: chzcrypt.o misc.o $(libs)
lszcrypt: lszcrypt.o misc.o $(libs)
+zcryptctl: zcryptctl.o misc.o $(libs)
install: all
$(INSTALL) -d -m 755 $(DESTDIR)$(BINDIR)
$(INSTALL) -g $(GROUP) -o $(OWNER) -m 755 chzcrypt $(DESTDIR)$(BINDIR)
$(INSTALL) -g $(GROUP) -o $(OWNER) -m 755 lszcrypt $(DESTDIR)$(BINDIR)
+ $(INSTALL) -g $(GROUP) -o $(OWNER) -m 755 zcryptctl $(DESTDIR)$(BINDIR)
$(INSTALL) -d -m 755 $(DESTDIR)$(MANDIR)/man8
$(INSTALL) -m 644 -c chzcrypt.8 $(DESTDIR)$(MANDIR)/man8
$(INSTALL) -m 644 -c lszcrypt.8 $(DESTDIR)$(MANDIR)/man8
+ $(INSTALL) -m 644 -c zcryptctl.8 $(DESTDIR)$(MANDIR)/man8
clean:
- rm -f *.o chzcrypt lszcrypt
+ rm -f *.o chzcrypt lszcrypt zcryptctl
.PHONY: all install clean
diff --git a/zconf/zcrypt/zcryptctl.8 b/zconf/zcrypt/zcryptctl.8
new file mode 100644
index 0000000..5d098da
--- /dev/null
+++ b/zconf/zcrypt/zcryptctl.8
@@ -0,0 +1,147 @@
+.\" zcryptctl.8
+.\"
+.\" Copyright 2018 IBM Corp.
+.\" s390-tools is free software; you can redistribute it and/or modify
+.\" it under the terms of the MIT license. See LICENSE for details.
+.\"
+.\" use
+.\" groff -man -Tutf8 zcryptctl.8
+.\" or
+.\" nroff -man zcryptctl.8
+.\" to process this source
+.\"
+.TH ZCRYPTCTL 8 "AUG 2018" "s390-tools"
+.SH NAME
+zcryptctl \- display information and administrate zcrypt multiple device nodes
+.SH SYNOPSIS
+.TP 8
+.B zcryptctl list
+.TP
+.B zcryptctl create
+.R [
+.I node-name
+.R ]
+.TP
+.B zcryptctl destroy
+.I node-name
+.TP
+.B zcryptctl addap
+.R |
+.B delap
+.I node-name adapter-nr
+.TP
+.B zcryptctl adddom
+.R |
+.B deldom
+.I node-name domain-nr
+.TP
+.B zcryptctl addioctl
+.R |
+.B delioctl
+.I node-name ioctl-term
+.TP
+.B zcryptctl config
+.I config-file
+.TP
+.B zcryptctl listconfig
+.SH DESCRIPTION
+The
+.B zcryptctl
+command displays information and maintains the multi device node
+extension for the zcrypt device driver.
+.P
+With the multi device node extension you can create and configure
+additional zcrypt device nodes which can be used as alternate device
+nodes to access the crypto hardware provided by the zcrypt device
+driver. Each zcrypt device node can be restricted in terms of crypto
+cards, domains, and available ioctls. Such a device node can be used
+as a base for container solutions like Docker to control and restrict
+the access to crypto resources.
+.SH COMMANDS
+.TP 8
+.B zcryptctl list
+Show all the additional device nodes that are currently active.
+.TP
+.B zcryptctl create
+.R [
+.I node-name
+.R ]
+Create a new zcrypt device node. The \fInode-name\fP might be given
+and needs to be unique and not in use. If there is no node name
+provided, the zcrypt device driver will create a new one with pattern
+zcrypt_\fIx\fP, with \fIx\fP being the next free number. Up to 256
+additional device nodes can be created. The newly created additional
+device node appears in /dev and has read and write permissions enabled
+only for root. By default all adapters, domains and ioctls are
+initially disabled on this new device node.
+.TP
+.B zcryptctl destroy
+.I node-name
+Destroy an additional zcrypt device node. The device node is only
+marked for disposal and destroyed when it is no longer used.
+.TP
+.B zcryptctl addap
+.R |
+.B delap
+.I node-name adapter-nr
+Update the filter for the specified zcrypt device node and add or
+delete a crypto adapter to be accessible via this node. The symbol
+\fBALL\fP can be used to enable or disable all adapters.
+.TP
+.B zcryptctl adddom
+.R |
+.B deldom
+.I node-name domain-nr
+Update the filter for the specified zcrypt device node and add or
+delete a domain to be accessible through this node. The symbol
+\fBALL\fP can be used to enable or disable all domains.
+.TP
+.B zcryptctl addioctl
+.R |
+.B delioctl
+.I node-name ioctl-term
+Update the filter for the specified zcrypt device node and add or
+delete an ioctl. The ioctl might be specified as symbolic string (one
+of \fBICARSAMODEXPO\fP, \fBICARSACRT\fP, \fBZSECSENDCPRB\fP,
+\fBZSENDEP11CPRB\fP, \fBZCRYPT_DEVICE_STATUS\fP,
+\fBZCRYPT_STATUS_MASK\fP, \fBZCRYPT_QDEPTH_MASK\fP,
+\fBZCRYPT_PERDEV_REQCNT\fP) or numeric value in the range 0-255 and
+the symbol \fBALL\fP can be used to include all ioctls.
+.TP
+.B zcryptctl config
+.I config-file
+Process a config file. The given configuration file is read line by
+line and the settings are applied. Syntax is simple:
+.RS
+.IP "node=<node-name>"
+.IP "aps=<list of ap numbers separated by space, tab or ','>"
+.IP "doms=<list of domain numbers separated by space, tab or ','>"
+.IP "ioctls=<list of ioctl as numeric or symbolic number separated by space, tab or ','>"
+.LP
+Empty lines are ignored and the '#' marks the rest of the
+line as comment.
+.LP
+The \fBnode=\fP line creates a new zcrypt device node, the \fBaps=\fP,
+\fBdoms=\fP and \fBioctls=\fP lines customize the previously created
+node. The symbol \fBALL\fP is also recognized for aps, doms, and
+ioctls.
+.LP
+Each action must fit into one line, spreading over multiple lines is
+not supported. But you can use more than one \fBaps=\fP, \fBdoms=\fP
+and \fBioctls=\fP lines to customize the very same node.
+.LP
+Processing stops when a line cannot be parsed or the current action
+fails. In this case the exit status is non zero but the successful
+actions until the failure occurs are not rolled back.
+.RE
+.TP
+.B zcryptctl listconfig
+List the current configuration in a form suitable for input to the
+\fBzcryptctl config\fP command.
+.LP
+.SH EXIT STATUS
+On successful completion of the command the exit status is 0. A non
+zero return code (and some kind of failure message) is emitted if the
+processing could not complete successful.
+.SH SEE ALSO
+\fBlszcrypt\fR(8)
diff --git a/zconf/zcrypt/zcryptctl.c b/zconf/zcrypt/zcryptctl.c
new file mode 100644
index 0000000..8326a08
--- /dev/null
+++ b/zconf/zcrypt/zcryptctl.c
@@ -0,0 +1,1030 @@
+/*
+ * zcryptctl - Maintain zcrypt multi device nodes.
+ *
+ * by Harald Freudenberger <freude@linux.ibm.com>
+ * Copyright IBM Corp. 2018
+ *
+ * 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 <ctype.h>
+#include <dirent.h>
+#include <err.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <limits.h>
+#include <pthread.h>
+#include <stdarg.h>
+#include <stdint.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/ioctl.h>
+#include <sys/stat.h>
+#include <sys/sysmacros.h>
+#include <unistd.h>
+
+#include "lib/util_base.h"
+#include "lib/util_file.h"
+#include "lib/util_opt.h"
+#include "lib/util_panic.h"
+#include "lib/util_path.h"
+#include "lib/util_prg.h"
+#include "lib/util_proc.h"
+#include "lib/util_rec.h"
+#include "lib/util_scandir.h"
+#include "lib/zt_common.h"
+
+#define MAX_ZDEV_IOCTLS 256
+#define ZCRYPT_NAME "zcrypt"
+#define MAX_ZDEV_CARDIDS_EXT 256
+#define MAX_ZDEV_DOMAINS_EXT 256
+#define ZCRYPTDEVICE "/dev/z90crypt"
+#define _UNUSED_ __attribute__((unused))
+
+/*
+ * Currently known commands
+ */
+#define CMD_LIST 0x0001
+#define CMD_CREATE 0x0002
+#define CMD_DESTROY 0x0003
+#define CMD_ADD_AP 0x0004
+#define CMD_DEL_AP 0x0005
+#define CMD_ADD_DOM 0x0006
+#define CMD_DEL_DOM 0x0007
+#define CMD_ADD_IOCTL 0x0008
+#define CMD_DEL_IOCTL 0x0009
+#define CMD_CONFIG 0x000A
+#define CMD_LISTCONFIG 0x000B
+
+/*
+ * Program configuration
+ */
+static const struct util_prg prg = {
+ .args = "",
+ .command_args = "COMMAND [COMMAND-PARAMS]",
+ .desc = "Display and administrate zcrypt multiple device nodes.",
+ .copyright_vec = {
+ {
+ .owner = "IBM Corp.",
+ .pub_first = 2018,
+ .pub_last = 2018,
+ },
+ UTIL_PRG_COPYRIGHT_END
+ }
+};
+
+static struct util_opt opt_vec[] = {
+ UTIL_OPT_HELP,
+ UTIL_OPT_VERSION,
+ UTIL_OPT_END
+};
+
+/*
+ * List of currently known and supported ioctls
+ */
+static struct zcryptctl_ioctls_s {
+ int nr;
+ const char *name;
+} zcryptctl_ioctls[] = {
+ {
+ .name = "ICARSAMODEXPO",
+ .nr = 0x05,
+ },
+ {
+ .name = "ICARSACRT",
+ .nr = 0x06,
+ },
+ {
+ .name = "ZSECSENDCPRB",
+ .nr = 0x81,
+ },
+ {
+ .name = "ZSENDEP11CPRB",
+ .nr = 0x04,
+ },
+ {
+ .name = "ZCRYPT_DEVICE_STATUS",
+ .nr = 0x5f,
+ },
+ {
+ .name = "ZCRYPT_STATUS_MASK",
+ .nr = 0x58,
+ },
+ {
+ .name = "ZCRYPT_QDEPTH_MASK",
+ .nr = 0x59,
+ },
+ {
+ .name = "ZCRYPT_PERDEV_REQCNT",
+ .nr = 0x5a,
+ },
+ {
+ .name = NULL,
+ .nr = 0,
+ },
+};
+
+static int ioctlstr2value(const char *str)
+{
+ int i;
+
+ for (i = 0; zcryptctl_ioctls[i].name; i++)
+ if (strcasecmp(str, zcryptctl_ioctls[i].name) == 0)
+ return zcryptctl_ioctls[i].nr;
+
+ return -1;
+}
+
+static const char *value2ioctlstr(int value)
+{
+ int i;
+
+ for (i = 0; zcryptctl_ioctls[i].name; i++)
+ if (value == zcryptctl_ioctls[i].nr)
+ return zcryptctl_ioctls[i].name;
+
+ return NULL;
+}
+
+static int check_nodename(const char *nodename)
+{
+ struct stat sb;
+ const char *node;
+ char pathname[PATH_MAX];
+
+ node = strrchr(nodename, '/');
+ node = node ? node + 1 : nodename;
+ snprintf(pathname, sizeof(pathname), "/dev/%s", node);
+ pathname[sizeof(pathname) - 1] = '\0';
+ if (stat(pathname, &sb) != 0)
+ return -1;
+ if (!S_ISCHR(sb.st_mode))
+ return -2;
+
+ return 0;
+}
+
+static int check_zcrypt_class_dir(void)
+{
+ int rc = 0;
+ char *afile;
+
+ afile = util_path_sysfs("class/%s", ZCRYPT_NAME);
+ if (!util_path_is_dir(afile))
+ rc = -1;
+
+ free(afile);
+ return rc;
+}
+
+static int fetch_major_minor(const char *nodename, int *major, int *minor)
+{
+ FILE *f;
+ int rc = 0;
+ char *afile;
+ const char *node;
+
+ node = strrchr(nodename, '/');
+ node = node ? node + 1 : nodename;
+ afile = util_path_sysfs("class/%s/%s/dev", ZCRYPT_NAME, node);
+ f = fopen(afile, "r");
+ if (!f) {
+ rc = -1;
+ goto out;
+ }
+ if (fscanf(f, "%i:%i", major, minor) != 2) {
+ fclose(f);
+ rc = -2;
+ goto out;
+ }
+ fclose(f);
+
+out:
+ free(afile);
+ return rc;
+}
+
+static int write_dn_attr(const char *nodename, const char *attr,
+ const char *value)
+{
+ FILE *f;
+ int rc = 0;
+ char *afile;
+ const char *node;
+
+ if (nodename) {
+ node = strrchr(nodename, '/');
+ node = node ? node + 1 : nodename;
+ afile = util_path_sysfs("class/%s/%s/%s",
+ ZCRYPT_NAME, node, attr);
+ } else
+ afile = util_path_sysfs("class/%s/%s", ZCRYPT_NAME, attr);
+ f = fopen(afile, "w");
+ if (!f) {
+ rc = -1;
+ goto out;
+ }
+ if (fprintf(f, "%s\n", value) < 0) {
+ fclose(f);
+ rc = -2;
+ goto out;
+ }
+ fflush(f);
+ if (ferror(f)) {
+ fclose(f);
+ rc = -2;
+ goto out;
+ }
+
+ fclose(f);
+
+out:
+ free(afile);
+ return rc;
+}
+
+static int read_dn_attr(const char *nodename, const char *attr,
+ char *value, int valuelen)
+{
+ int rc;
+ FILE *f;
+ char *afile;
+ const char *node;
+
+ node = strrchr(nodename, '/');
+ node = node ? node + 1 : nodename;
+ afile = util_path_sysfs("class/%s/%s/%s", ZCRYPT_NAME, node, attr);
+ f = fopen(afile, "r");
+ if (!f) {
+ rc = -1;
+ goto out;
+ }
+ value = fgets(value, valuelen, f);
+ fclose(f);
+ rc = value ? 0 : -2;
+
+out:
+ free(afile);
+ return rc;
+}
+
+static int test_bit(int n, const char *hexbytestr)
+{
+ char c;
+ int v, i = 0;
+
+ if (strncmp(hexbytestr, "0x", 2) == 0)
+ i += 2;
+ c = hexbytestr[i + n / 4];
+ if (c >= '0' && c <= '9')
+ v = c - '0';
+ else if (c >= 'a' && c <= 'f')
+ v = 10 + c - 'a';
+ else if (c >= 'A' && c <= 'F')
+ v = 10 + c - 'A';
+ else
+ errx(EXIT_FAILURE,
+ "Could not parse hex digit '%c'", c);
+
+ return v & (1 << (3 - (n % 4)));
+}
+
+static int cmd_list(int cmd,
+ const char *node _UNUSED_,
+ const char *arg _UNUSED_)
+{
+ DIR *dir;
+ char *dirname;
+ const char *p;
+ struct dirent *de;
+ int i, n, major, minor, count = 0;
+ char buf[80], tab = (cmd == CMD_LISTCONFIG ? ' ' : '\t');
+
+ dirname = util_path_sysfs("class/%s", ZCRYPT_NAME);
+ dir = opendir(dirname);
+ if (!dir)
+ errx(EXIT_FAILURE,
+ "Could not read directory '%s' errno=%d (%s)",
+ dirname, errno, strerror(errno));
+ while ((de = readdir(dir)) != NULL) {
+ if (de->d_name[0] == '.' || de->d_type == DT_REG)
+ continue;
+ if (fetch_major_minor(de->d_name, &major, &minor) != 0)
+ errx(EXIT_FAILURE,
+ "Could not fetch major/minor from sysfs for zcrypt node '%s'",
+ de->d_name);
+ if (cmd == CMD_LISTCONFIG) {
+ printf("node = %s\n", de->d_name);
+ printf(" aps =");
+ } else {
+ printf("zcrypt node name:\t%s\n", de->d_name);
+ printf(" device node:\t/dev/%s\n", de->d_name);
+ printf(" major:minor:\t%d:%d\n", major, minor);
+ printf(" adapter:");
+ }
+ if (read_dn_attr(de->d_name, "apmask", buf, sizeof(buf)) != 0)
+ errx(EXIT_FAILURE,
+ "Could not fetch apmask attribute from sysfs for zcrypt node '%s'",
+ de->d_name);
+ for (i = n = 0; i < MAX_ZDEV_CARDIDS_EXT; i++)
+ if (test_bit(i, buf))
+ printf("%c%d", n++ == 0 ? tab : ',', i);
+ putchar('\n');
+ if (cmd == CMD_LISTCONFIG)
+ printf(" doms =");
+ else
+ printf(" domains:");
+ if (read_dn_attr(de->d_name, "aqmask", buf, sizeof(buf)) != 0)
+ errx(EXIT_FAILURE,
+ "Could not fetch aqmask attribute from sysfs for zcrypt node '%s'",
+ de->d_name);
+ for (i = n = 0; i < MAX_ZDEV_DOMAINS_EXT; i++)
+ if (test_bit(i, buf))
+ printf("%c%d", n++ == 0 ? tab : ',', i);
+ putchar('\n');
+ if (cmd == CMD_LISTCONFIG)
+ printf(" ioctls =");
+ else
+ printf(" ioctls:");
+ if (read_dn_attr(de->d_name, "ioctlmask",
+ buf, sizeof(buf)) != 0)
+ errx(EXIT_FAILURE,
+ "Could not fetch ioctlmask attribute from sysfs for zcrypt node '%s'",
+ de->d_name);
+ for (i = n = 0; i < MAX_ZDEV_IOCTLS; i++) {
+ if (test_bit(i, buf)) {
+ p = value2ioctlstr(i);
+ if (p)
+ printf("%c%s",
+ n++ == 0 ? tab : ',', p);
+ else
+ printf("%c%d",
+ n++ == 0 ? tab : ',', i);
+ }
+ }
+ putchar('\n');
+ count++;
+ }
+ closedir(dir);
+
+ if (count == 0)
+ printf("No additional zcrypt device nodes defined\n");
+
+ return 0;
+}
+
+static int cmd_create(int cmd _UNUSED_,
+ const char *nodename,
+ const char *arg _UNUSED_)
+{
+ int rc;
+ const char *node;
+ char buf[PATH_MAX];
+
+ if (nodename) {
+ node = strrchr(nodename, '/');
+ node = node ? node + 1 : nodename;
+ strncpy(buf, node, sizeof(buf) - 1);
+ } else
+ strncpy(buf, "\n", sizeof(buf) - 1);
+ buf[sizeof(buf) - 1] = 0;
+
+ rc = write_dn_attr(NULL, "create", buf);
+ if (rc != 0)
+ errx(EXIT_FAILURE,
+ "Could not write into sysfs entry to create zdev node");
+
+ printf("Device node created\n");
+
+ return 0;
+}
+
+static int cmd_destroy(int cmd _UNUSED_,
+ const char *nodename,
+ const char *arg _UNUSED_)
+{
+ int rc;
+ struct stat sb;
+ const char *node;
+ char pathname[PATH_MAX];
+
+ node = strrchr(nodename, '/');
+ node = node ? node + 1 : nodename;
+ snprintf(pathname, sizeof(pathname), "/dev/%s", node);
+ pathname[sizeof(pathname) - 1] = '\0';
+ rc = stat(pathname, &sb);
+ if (rc != 0)
+ errx(EXIT_FAILURE,
+ "Could not check status for '%s'", pathname);
+ if (!S_ISCHR(sb.st_mode))
+ errx(EXIT_FAILURE,
+ "File '%s' is not a character device node", pathname);
+
+ rc = write_dn_attr(NULL, "destroy", node);
+ if (rc != 0)
+ errx(EXIT_FAILURE,
+ "Could not write into sysfs entry to destroy zdev node '%s'",
+ node);
+
+ printf("Device node '%s' marked for destruction\n", node);
+
+ return 0;
+}
+
+static void add_del_ap(int cmd, const char *node, int ap)
+{
+ int rc;
+ char buf[PATH_MAX];
+
+ if (cmd == CMD_ADD_AP)
+ sprintf(buf, "+%d", ap);
+ else
+ sprintf(buf, "-%d", ap);
+ rc = write_dn_attr(node, "apmask", buf);
+ if (rc != 0)
+ errx(EXIT_FAILURE,
+ "Could not write into sysfs entry to %s adapter %d for zdev node '%s'",
+ cmd == CMD_ADD_AP ? "add" : "remove", ap, node);
+}
+
+static int cmd_add_del_ap(int cmd, const char *node, const char *arg)
+{
+ int ap, all = 0;
+
+ if (strcasecmp(arg, "ALL") == 0) {
+ all = 1;
+ } else {
+ if (sscanf(arg, "%i", &ap) != 1)
+ errx(EXIT_FAILURE,
+ "Invalid adapter argument '%s'", arg);
+ if (ap < 0 || ap >= MAX_ZDEV_CARDIDS_EXT)
+ errx(EXIT_FAILURE,
+ "Adapter argument '%s' out of range [0..%d]",
+ arg, MAX_ZDEV_CARDIDS_EXT - 1);
+ }
+
+ if (!all) {
+ add_del_ap(cmd, node, ap);
+ printf("Adapter %d %s\n", ap,
+ (cmd == CMD_ADD_AP ? "added" : "removed"));
+ } else {
+ for (ap = 0; ap < MAX_ZDEV_CARDIDS_EXT; ap++)
+ add_del_ap(cmd, node, ap);
+ printf("All adapters %s\n",
+ (cmd == CMD_ADD_AP ? "added" : "removed"));
+ }
+
+ return 0;
+}
+
+static void add_del_dom(int cmd, const char *node, int dom)
+{
+ int rc;
+ char buf[PATH_MAX];
+
+ if (cmd == CMD_ADD_DOM)
+ sprintf(buf, "+%d", dom);
+ else
+ sprintf(buf, "-%d", dom);
+ rc = write_dn_attr(node, "aqmask", buf);
+ if (rc != 0)
+ errx(EXIT_FAILURE,
+ "Could not write into sysfs entry to %s domain %d for zdev node '%s'",
+ cmd == CMD_ADD_DOM ? "add" : "remove", dom, node);
+}
+
+static int cmd_add_del_dom(int cmd, const char *node, const char *arg)
+{
+ int dom, all = 0;
+
+ if (strcasecmp(arg, "ALL") == 0) {
+ all = 1;
+ } else {
+ if (sscanf(arg, "%i", &dom) != 1)
+ errx(EXIT_FAILURE,
+ "Invalid domain argument '%s'", arg);
+ if (dom < 0 || dom >= MAX_ZDEV_DOMAINS_EXT)
+ errx(EXIT_FAILURE,
+ "Domain argument '%s' out of range [0..%d]",
+ arg, MAX_ZDEV_DOMAINS_EXT - 1);
+ }
+
+ if (!all) {
+ add_del_dom(cmd, node, dom);
+ printf("Domain %d %s\n", dom,
+ (cmd == CMD_ADD_DOM ? "added" : "removed"));
+ } else {
+ for (dom = 0; dom < MAX_ZDEV_DOMAINS_EXT; dom++)
+ add_del_dom(cmd, node, dom);
+ printf("All domains %s\n",
+ (cmd == CMD_ADD_DOM ? "added" : "removed"));
+ }
+
+ return 0;
+}
+
+static void add_del_ioctl(int cmd, const char *node, int ioctlnr)
+{
+ int rc;
+ char buf[PATH_MAX];
+
+ if (cmd == CMD_ADD_IOCTL)
+ sprintf(buf, "+%d", ioctlnr);
+ else
+ sprintf(buf, "-%d", ioctlnr);
+ rc = write_dn_attr(node, "ioctlmask", buf);
+ if (rc != 0)
+ errx(EXIT_FAILURE,
+ "Could not write into sysfs entry to %s ioctl %d for zdev node '%s'",
+ cmd == CMD_ADD_IOCTL ? "add" : "remove", ioctlnr, node);
+}
+
+static int cmd_add_del_ioctl(int cmd, const char *node, const char *arg)
+{
+ int ioctlnr, all = 0;
+
+ if (strcasecmp(arg, "ALL") == 0) {
+ all = 1;
+ } else {
+ ioctlnr = ioctlstr2value(arg);
+ if (ioctlnr < 0)
+ if (sscanf(arg, "%i", &ioctlnr) != 1)
+ errx(EXIT_FAILURE,
+ "Invalid ioctl argument '%s'", arg);
+ if (ioctlnr < 0 || ioctlnr >= MAX_ZDEV_IOCTLS)
+ errx(EXIT_FAILURE,
+ "Ioctl argument '%s' out of range [0..%d]",
+ arg, MAX_ZDEV_IOCTLS - 1);
+ }
+
+ if (!all) {
+ add_del_ioctl(cmd, node, ioctlnr);
+ printf("Ioctl %s %s\n", arg,
+ (cmd == CMD_ADD_IOCTL ? "added" : "removed"));
+ } else {
+ for (ioctlnr = 0; ioctlnr < MAX_ZDEV_IOCTLS; ioctlnr++)
+ add_del_ioctl(cmd, node, ioctlnr);
+ printf("All Ioctls %s\n",
+ (cmd == CMD_ADD_IOCTL ? "added" : "removed"));
+ }
+
+ return 0;
+}
+
+static int _match_keyword(char **p, const char *keyword)
+{
+ int n = strlen(keyword);
+
+ if (strncmp(*p, keyword, n) == 0) {
+ *p += n;
+ return n;
+ }
+
+ return 0;
+}
+
+static int _match_character(char **p, char c)
+{
+ char *q = *p;
+
+ while (isblank(*q))
+ q++;
+ if (*q != c)
+ return 0;
+ q++;
+ while (isblank(*q))
+ q++;
+ *p = q;
+
+ return 1;
+}
+
+static int _match_string(char **p, char *buf)
+{
+ int n = 0;
+ char *q = *p;
+
+ while (isblank(*q))
+ q++;
+ while (*q && *q != ',' && !isspace(*q)) {
+ buf[n++] = *q;
+ q++;
+ }
+ while (isblank(*q))
+ q++;
+
+ if (n > 0) {
+ buf[n] = '\0';
+ *p = q;
+ }
+
+ return n;
+}
+
+static int cmd_config(int cmd _UNUSED_,
+ const char *nodename _UNUSED_,
+ const char *arg)
+{
+ ssize_t n;
+ size_t linesize = 0;
+ int nr = 0, havenode = 0;
+ FILE *f = fopen(arg, "r");
+ char *p, *line = NULL, node[128], buf[128];
+
+ if (!f)
+ errx(EXIT_FAILURE,
+ "Could not open file '%s'", arg);
+
+ while ((n = getline(&line, &linesize, f)) != -1) {
+ nr++;
+ p = line;
+ while (isspace(*p))
+ p++;
+ if (*p == '\0' || *p == '#')
+ continue;
+ if (_match_keyword(&p, "node")) {
+ if (!_match_character(&p, '='))
+ errx(EXIT_FAILURE,
+ "Missing '=' at '%-8.8s...' in line %d '%s'",
+ p, nr, line);
+ if (!_match_string(&p, node))
+ errx(EXIT_FAILURE,
+ "Missing node name at '%-8.8s...' in line %d '%s'",
+ p, nr, line);
+ cmd_create(CMD_CREATE, node, NULL);
+ havenode = 1;
+ } else if (_match_keyword(&p, "aps")) {
+ if (!havenode)
+ errx(EXIT_FAILURE,
+ "Missing node=... before processing any aps=... statements in line %d '%s'",
+ nr, line);
+ if (!_match_character(&p, '='))
+ errx(EXIT_FAILURE,
+ "Missing '=' at '%-8.8s...' in line %d '%s'",
+ p, nr, line);
+ while (1) {
+ while (isspace(*p))
+ p++;
+ if (*p == '\0' || *p == '#')
+ break;
+ if (!_match_string(&p, buf))
+ errx(EXIT_FAILURE,
+ "Missing argument(s) for aps=... at '%-8.8s...' in line %d '%s'",
+ p, nr, line);
+ cmd_add_del_ap(CMD_ADD_AP, node, buf);
+ while (isblank(*p) || *p == ',')
+ p++;
+ }
+ } else if (_match_keyword(&p, "doms")) {
+ if (!havenode)
+ errx(EXIT_FAILURE,
+ "Missing node=... before processing any doms=... statements in line %d '%s'",
+ nr, line);
+ if (!_match_character(&p, '='))
+ errx(EXIT_FAILURE,
+ "Missing '=' at '%-8.8s...' in line %d '%s'",
+ p, nr, line);
+ while (1) {
+ while (isspace(*p))
+ p++;
+ if (*p == '\0' || *p == '#')
+ break;
+ if (!_match_string(&p, buf))
+ errx(EXIT_FAILURE,
+ "Missing argument(s) for aps=... at '%-8.8s...' in line %d '%s'",
+ p, nr, line);
+ cmd_add_del_dom(CMD_ADD_DOM, node, buf);
+ while (isblank(*p) || *p == ',')
+ p++;
+ }
+ } else if (_match_keyword(&p, "ioctls")) {
+ if (!havenode)
+ errx(EXIT_FAILURE,
+ "Missing node=... before processing any ioctls=... statements in line %d '%s'",
+ nr, line);
+ if (!_match_character(&p, '='))
+ errx(EXIT_FAILURE,
+ "Missing '=' at '%-8.8s...' in line %d '%s'",
+ p, nr, line);
+ while (1) {
+ while (isspace(*p))
+ p++;
+ if (*p == '\0' || *p == '#')
+ break;
+ if (!_match_string(&p, buf))
+ errx(EXIT_FAILURE,
+ "Missing argument(s) for aps=... at '%-8.8s...' in line %d '%s'",
+ p, nr, line);
+ cmd_add_del_ioctl(CMD_ADD_IOCTL, node, buf);
+ while (isblank(*p) || *p == ',')
+ p++;
+ }
+ } else
+ errx(EXIT_FAILURE,
+ "Unknown keyword '%-8.8s...' in line %d '%s'",
+ p, nr, line);
+ }
+
+ free(line);
+ fclose(f);
+
+ return 0;
+}
+
+static struct zcryptctl_cmds_s {
+ int cmd;
+ const char *usage;
+ const char *command;
+ const char *description;
+ int (*function)(int cmd, const char *node, const char *arg);
+} zcryptctl_cmds[] = {
+ {
+ .cmd = CMD_LIST,
+ .command = "list",
+ .function = cmd_list,
+ .usage = "zcryptctl list",
+ .description =
+ "List all currently known additional zcrypt device nodes.",
+ },
+ {
+ .cmd = CMD_CREATE,
+ .command = "create",
+ .function = cmd_create,
+ .usage = "zcryptctl create [nodename]",
+ .description =
+ "Create a new zcrypt device node.\n"
+ "The node-name might be given and needs to be unique and not\n"
+ "in use. If there is no node name provided, the zcrypt device\n"
+ "driver will create a new one with pattern zcrypt_<x>\n"
+ "with <x> being the next free number. By default all\n"
+ "adapters, domains and ioctls are initially disabled on this\n"
+ "new device node."
+ },
+ {
+ .cmd = CMD_DESTROY,
+ .command = "destroy",
+ .function = cmd_destroy,
+ .usage = "zcryptctl destroy <nodename>",
+ .description =
+ "Destroy an additional zcrypt device node.\n"
+ "Mark the given zcrypt device node as disposable. The removal\n"
+ "will take place when it is no longer used.",
+ },
+ {
+ .cmd = CMD_ADD_AP,
+ .command = "addap",
+ .function = cmd_add_del_ap,
+ .usage = "zcryptctl addap <adapter>",
+ .description =
+ "Update the filter for the specified zcrypt device node and\n"
+ "add an crypto adapter to be accessible via this node. The\n"
+ "adapter argument may be a number in the range 0-255 or the\n"
+ "symbol ALL.",
+ },
+ {
+ .cmd = CMD_DEL_AP,
+ .command = "delap",
+ .function = cmd_add_del_ap,
+ .usage = "zcryptctl delap <adapter>",
+ .description =
+ "Update the filter for the specified zcrypt device node and\n"
+ "remove a crypto adapter from the allowed adapters list. The\n"
+ "adapter argument may be a number in the range 0-255 or the\n"
+ "symbol ALL.",
+ },
+ {
+ .cmd = CMD_ADD_DOM,
+ .command = "adddom",
+ .function = cmd_add_del_dom,
+ .usage = "zcryptctl adddom <domain>",
+ .description =
+ "Update the filter for the specified zcrypt device node and\n"
+ "add a crypto domain to be accessible via this node. The\n"
+ "domain argument may be a number in the range 0-255 or the\n"
+ "symbol ALL.",
+ },
+ {
+ .cmd = CMD_DEL_DOM,
+ .command = "deldom",
+ .function = cmd_add_del_dom,
+ .usage = "zcryptctl deldom <adapter>",
+ .description =
+ "Update the filter for the specified zcrypt device node and\n"
+ "remove a crypto domain from the allowed domains list. The\n"
+ "domain argument may be a number in the range 0-255 or the\n"
+ "symbol ALL.",
+ },
+ {
+ .cmd = CMD_ADD_IOCTL,
+ .command = "addioctl",
+ .function = cmd_add_del_ioctl,
+ .usage = "zcryptctl addioctl <ioctlexp>",
+ .description =
+ "Update the filter for the specified zcrypt device node and\n"
+ "add an ioctl number to be accessible via this node. The\n"
+ "ioctlexp argument may be one of symbols ICARSAMODEXPO,\n"
+ "ICARSACRT, ZSECSENDCPRB, ZSENDEP11CPRB, ZCRYPT_DEVICE_STATUS\n"
+ "ZCRYPT_STATUS_MASK, ZCRYPT_QDEPTH_MASK, ZCRYPT_PERDEV_REQCNT\n"
+ "or a number in the range 0-255 or the symbol ALL.",
+ },
+ {
+ .cmd = CMD_DEL_IOCTL,
+ .command = "delioctl",
+ .function = cmd_add_del_ioctl,
+ .usage = "zcryptctl delioctl <ioctlexp>",
+ .description =
+ "Update the filter for the specified zcrypt device node and\n"
+ "remove an ioctl number from the allowed ioctls list. The\n"
+ "ioctlexp argument may be one of symbols ICARSAMODEXPO,\n"
+ "ICARSACRT, ZSECSENDCPRB, ZSENDEP11CPRB, ZCRYPT_DEVICE_STATUS\n"
+ "ZCRYPT_STATUS_MASK, ZCRYPT_QDEPTH_MASK, ZCRYPT_PERDEV_REQCNT\n"
+ "or a number in the range 0-255 or the symbol ALL.",
+ },
+ {
+ .cmd = CMD_CONFIG,
+ .command = "config",
+ .function = cmd_config,
+ .usage = "zcryptctl config <configfile>",
+ .description =
+ "Process a config file. The given config file is read line by\n"
+ "line and the settings are applied. Syntax is simple:\n"
+ " node=<node_name>\n"
+ " aps=<list of ap numbers separated by space, tab or ','>\n"
+ " doms=<list of domains separated by space, tab or ','>\n"
+ " ioctls=<list of ioctl as number or symbolic number\n"
+ " separated by space, tab or ','>\n"
+ "Empty lines are ignored and the '#' marks the rest of the\n"
+ "line as comment.\n"
+ "The node= line creates a new zcrypt device node, the\n"
+ "aps=, doms= and ioctls= lines customize the previously\n"
+ "created node. The symbol ALL is also recognized for aps,\n"
+ "doms, and ioctls.\n"
+ "Each action must fit into one line, spreading over multiple\n"
+ "lines is not supported. But you can use more than one\n"
+ "aps=, doms= and ioctls= lines to customize the very same\n"
+ "node.\n"
+ "Processing stops when a line cannot be parsed or the\n"
+ "current action fails. When the config file has been\n"
+ "processed successful, the zcryptctl return code is 0. A non\n"
+ "zero return code (and some kind of failure message) is\n"
+ "emitted on partial completion.",
+ },
+ {
+ .cmd = CMD_LISTCONFIG,
+ .command = "listconfig",
+ .function = cmd_list,
+ .usage = "zcryptctl listconfig",
+ .description =
+ "List all currently known additional zcrypt device nodes\n"
+ "in a format suitable for the 'config' command.",
+ },
+ {
+ .command = NULL,
+ .cmd = 0,
+ }
+};
+
+static int get_command_index(const char *cmdstr)
+{
+ int i;
+
+ for (i = 0; zcryptctl_cmds[i].command; i++)
+ if (!strcmp(zcryptctl_cmds[i].command, cmdstr))
+ return i;
+
+ return -1;
+}
+
+static void commands_print_help(void)
+{
+ int i;
+
+ for (i = 0; zcryptctl_cmds[i].command; i++)
+ if (zcryptctl_cmds[i].usage)
+ printf(" %s\n", zcryptctl_cmds[i].usage);
+}
+
+int main(int argc, char *argv[])
+{
+ int c, cmdindex = -1;
+ int rc = EXIT_SUCCESS;
+
+ util_prg_init(&prg);
+ util_opt_init(opt_vec, NULL);
+
+ for (c = 1; c < argc; c++) {
+ cmdindex = get_command_index(argv[c]);
+ if (cmdindex >= 0)
+ break;
+ }
+
+ while (1) {
+ c = util_opt_getopt_long(argc, argv);
+ if (c == -1)
+ break;
+ switch (c) {
+ case 'h':
+ if (cmdindex < 0) {
+ util_prg_print_help();
+ commands_print_help();
+ util_opt_print_help();
+ } else {
+ printf("Usage: %s\n",
+ zcryptctl_cmds[cmdindex].usage);
+ printf("%s\n",
+ zcryptctl_cmds[cmdindex].description);
+ }
+ return EXIT_SUCCESS;
+ case 'v':
+ util_prg_print_version();
+ return EXIT_SUCCESS;
+ default:
+ util_opt_print_parse_error(c, argv);
+ return EXIT_FAILURE;
+ }
+ }
+
+ if (cmdindex < 0)
+ errx(EXIT_FAILURE, "Missing or invalid command argument");
+
+ if (check_zcrypt_class_dir() != 0)
+ errx(EXIT_FAILURE,
+ "Directory class/%s is missing in sysfs.\n"
+ "Multiple zcrypt node support is not available",
+ ZCRYPT_NAME);
+
+ c = zcryptctl_cmds[cmdindex].cmd;
+ switch (c) {
+ case CMD_LIST:
+ case CMD_LISTCONFIG:
+ rc = zcryptctl_cmds[cmdindex].function(c, NULL, NULL);
+ break;
+ case CMD_CREATE:
+ rc = zcryptctl_cmds[cmdindex].function(c,
+ optind + 1 < argc ?
+ argv[optind + 1] : NULL,
+ NULL);
+ break;
+ case CMD_DESTROY:
+ if (optind + 1 >= argc)
+ errx(EXIT_FAILURE, "Missing node name argument");
+ if (check_nodename(argv[optind + 1]) != 0)
+ errx(EXIT_FAILURE, "Invalid or unknown nodename '%s'",
+ argv[optind + 1]);
+ rc = zcryptctl_cmds[cmdindex].function(c,
+ argv[optind + 1], NULL);
+ break;
+ case CMD_ADD_AP:
+ case CMD_DEL_AP:
+ if (optind + 1 >= argc)
+ errx(EXIT_FAILURE, "Missing node name argument");
+ if (optind + 2 >= argc)
+ errx(EXIT_FAILURE, "Missing adapter argument");
+ if (check_nodename(argv[optind + 1]) != 0)
+ errx(EXIT_FAILURE, "Invalid or unknown nodename '%s'",
+ argv[optind + 1]);
+ rc = zcryptctl_cmds[cmdindex].function(c,
+ argv[optind + 1],
+ argv[optind + 2]);
+ break;
+ case CMD_ADD_DOM:
+ case CMD_DEL_DOM:
+ if (optind + 1 >= argc)
+ errx(EXIT_FAILURE, "Missing node name argument");
+ if (optind + 2 >= argc)
+ errx(EXIT_FAILURE, "Missing domain argument");
+ if (check_nodename(argv[optind + 1]) != 0)
+ errx(EXIT_FAILURE, "Invalid or unknown nodename '%s'",
+ argv[optind + 1]);
+ rc = zcryptctl_cmds[cmdindex].function(c,
+ argv[optind + 1],
+ argv[optind + 2]);
+ break;
+ case CMD_ADD_IOCTL:
+ case CMD_DEL_IOCTL:
+ if (optind + 1 >= argc)
+ errx(EXIT_FAILURE, "Missing node name argument");
+ if (optind + 2 >= argc)
+ errx(EXIT_FAILURE, "Missing ioctl argument");
+ if (check_nodename(argv[optind + 1]) != 0)
+ errx(EXIT_FAILURE, "Invalid or unknown nodename '%s'",
+ argv[optind + 1]);
+ rc = zcryptctl_cmds[cmdindex].function(c,
+ argv[optind + 1],
+ argv[optind + 2]);
+ break;
+ case CMD_CONFIG:
+ if (optind + 1 >= argc)
+ errx(EXIT_FAILURE, "Missing filename argument");
+ rc = zcryptctl_cmds[cmdindex].function(c, NULL,
+ argv[optind + 1]);
+ break;
+ default:
+ errx(EXIT_FAILURE, "Unknown command %d", c);
+ }
+
+ return rc;
+}
--
2.21.3
From d5aab30ab6dd79a47d821b39e9f803d4afe8ed7b Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Dan=20Hor=C3=A1k?= <dan@danny.cz>
Date: Mon, 19 Nov 2018 11:40:35 +0100
Subject: [PATCH 13/44] lszcrypt: support for alternate zcrypt device drivers
(#1646355)
Summary: lszcrypt: support for alternate zcrypt device drivers
Description: With kernel 4.19 there comes an extension to the
existing AP bus which supports alternate zcrypt
drivers. For details about this see kernel patch
"s390/zcrypt: AP bus support for alternate
driver(s)". So now lszcrypt displays the driver name
in verbose mode. As some of the information
displayed by lszcrypt was based on sysfs attributes,
which are only available when the default zcrypt
driver is bound to the device, this also needed some
rework. If a sysfs attribute is not available
because of an alternate driver binding (or no
driver) a question mark is printed into the field.
Upstream-ID: 0a0b4c382693cded5652404e8fa2c0e483aa33df
---
zconf/zcrypt/lszcrypt.8 | 4 +-
zconf/zcrypt/lszcrypt.c | 163 +++++++++++++++++++++++++++-------------
2 files changed, 112 insertions(+), 55 deletions(-)
diff --git a/zconf/zcrypt/lszcrypt.8 b/zconf/zcrypt/lszcrypt.8
index 7196806..826e109 100644
--- a/zconf/zcrypt/lszcrypt.8
+++ b/zconf/zcrypt/lszcrypt.8
@@ -54,8 +54,8 @@ status.
.B -V, --verbose
The verbose level for cryptographic device information.
With this verbose level additional information like hardware card type,
-hardware queue depth, pending request queue count, outstanding
-request queue count, and installed function facilities are displayed.
+hardware queue depth, pending requests count, installed function
+facilities and driver binding is displayed.
.TP 8
.B <device-id>
Specifies a cryptographic device to display. A cryptographic device can be
diff --git a/zconf/zcrypt/lszcrypt.c b/zconf/zcrypt/lszcrypt.c
index eb3cd6e..580407f 100644
--- a/zconf/zcrypt/lszcrypt.c
+++ b/zconf/zcrypt/lszcrypt.c
@@ -1,7 +1,7 @@
/**
* lszcrypt - Display zcrypt devices and configuration settings
*
- * Copyright IBM Corp. 2008, 2017
+ * Copyright IBM Corp. 2008, 2018
*
* s390-tools is free software; you can redistribute it and/or modify
* it under the terms of the MIT license. See LICENSE for details.
@@ -56,6 +56,25 @@ struct lszcrypt_l *lszcrypt_l = &l;
#define MASK_CLASS_STATELESS 0x00400000
#define CLASS_STATELESS "restricted function set"
+/*
+ * facility bits
+ */
+#define MAX_FAC_BITS 9
+static struct fac_bits_s {
+ int mask;
+ char c;
+} fac_bits[MAX_FAC_BITS] = {
+ { 0x80000000, 'S' },
+ { 0x40000000, 'M' },
+ { 0x20000000, 'C' },
+ { 0x10000000, 'D' },
+ { 0x08000000, 'A' },
+ { 0x04000000, 'X' },
+ { 0x02000000, 'N' },
+ { 0x00800000, 'F' },
+ { 0x00400000, 'R' },
+};
+
/*
* Program configuration
*/
@@ -66,7 +85,7 @@ const struct util_prg prg = {
{
.owner = "IBM Corp.",
.pub_first = 2008,
- .pub_last = 2017,
+ .pub_last = 2018,
},
UTIL_PRG_COPYRIGHT_END
}
@@ -255,7 +274,8 @@ static void show_capability(const char *id_str)
/* Skip devices, which are not supported by zcrypt layer */
if (!util_path_is_readable("%s/type", dev) ||
!util_path_is_readable("%s/online", dev)) {
- printf("Detailed capability information for %s (hardware type %ld) is not available.\n", card, hwtype);
+ printf("Detailed capability information for %s (hardware type %ld) is not available.\n",
+ card, hwtype);
return;
}
cbuf[0] = '\0';
@@ -299,11 +319,13 @@ static void show_capability(const char *id_str)
} else if (func_val & MASK_EP11) {
printf("%s", CAP_EP11);
} else {
- printf("Detailed capability information for %s (hardware type %ld) is not available.", card, hwtype);
+ printf("Detailed capability information for %s (hardware type %ld) is not available.",
+ card, hwtype);
}
break;
default:
- printf("Detailed capability information for %s (hardware type %ld) is not available.", card, hwtype);
+ printf("Detailed capability information for %s (hardware type %ld) is not available.",
+ card, hwtype);
break;
}
printf("\n");
@@ -315,17 +337,22 @@ static void show_capability(const char *id_str)
static void read_subdev_rec_default(struct util_rec *rec, const char *grp_dev,
const char *sub_dev)
{
- unsigned long facility;
char buf[256];
+ unsigned long facility;
- util_file_read_line(buf, sizeof(buf), "%s/type", grp_dev);
- util_rec_set(rec, "type", buf);
+ if (util_file_read_line(buf, sizeof(buf), "%s/type", grp_dev))
+ util_rec_set(rec, "type", "-");
+ else
+ util_rec_set(rec, "type", buf);
- util_file_read_line(buf, sizeof(buf), "%s/%s/online", grp_dev, sub_dev);
- if (strcmp(buf, "0") == 0)
- util_rec_set(rec, "online", "offline");
+ if (util_file_read_line(buf, sizeof(buf), "%s/%s/online",
+ grp_dev, sub_dev))
+ util_rec_set(rec, "online", "-");
else
- util_rec_set(rec, "online", "online");
+ if (strcmp(buf, "0") == 0)
+ util_rec_set(rec, "online", "offline");
+ else
+ util_rec_set(rec, "online", "online");
util_file_read_ul(&facility, 16, "%s/ap_functions", grp_dev);
if (facility & MASK_COPRO)
@@ -339,7 +366,7 @@ static void read_subdev_rec_default(struct util_rec *rec, const char *grp_dev,
util_file_read_line(buf, sizeof(buf), "%s/%s/request_count",
grp_dev, sub_dev);
- util_rec_set(rec, "request_count", buf);
+ util_rec_set(rec, "requests", buf);
}
/*
@@ -348,20 +375,19 @@ static void read_subdev_rec_default(struct util_rec *rec, const char *grp_dev,
static void read_subdev_rec_verbose(struct util_rec *rec, const char *grp_dev,
const char *sub_dev)
{
+ int i;
unsigned long facility;
- char buf[256];
- long depth;
+ char buf[256], afile[PATH_MAX];
+ long depth, pending1, pending2;
if (l.verbose == 0)
return;
- util_file_read_line(buf, sizeof(buf), "%s/%s/pendingq_count",
- grp_dev, sub_dev);
- util_rec_set(rec, "pendingq_count", buf);
-
- util_file_read_line(buf, sizeof(buf), "%s/%s/requestq_count",
- grp_dev, sub_dev);
- util_rec_set(rec, "requestq_count", buf);
+ util_file_read_l(&pending1, 10, "%s/%s/pendingq_count",
+ grp_dev, sub_dev);
+ util_file_read_l(&pending2, 10, "%s/%s/requestq_count",
+ grp_dev, sub_dev);
+ util_rec_set(rec, "pending", "%ld", pending1 + pending2);
util_file_read_line(buf, sizeof(buf), "%s/hwtype", grp_dev);
util_rec_set(rec, "hwtype", buf);
@@ -370,7 +396,18 @@ static void read_subdev_rec_verbose(struct util_rec *rec, const char *grp_dev,
util_rec_set(rec, "depth", "%02d", depth + 1);
util_file_read_ul(&facility, 16, "%s/ap_functions", grp_dev);
- util_rec_set(rec, "facility", "0x%08x", facility);
+ for (i = 0; i < MAX_FAC_BITS; i++)
+ buf[i] = facility & fac_bits[i].mask ? fac_bits[i].c : '-';
+ buf[i] = '\0';
+ util_rec_set(rec, "facility", buf);
+
+ snprintf(afile, sizeof(afile), "%s/%s/driver", grp_dev, sub_dev);
+ afile[sizeof(afile) - 1] = '\0';
+ memset(buf, 0, sizeof(buf));
+ if (readlink(afile, buf, sizeof(buf)) > 0)
+ util_rec_set(rec, "driver", strrchr(buf, '/') + 1);
+ else
+ util_rec_set(rec, "driver", "-no-driver-");
}
/*
@@ -382,9 +419,13 @@ static void show_subdevice(struct util_rec *rec, const char *grp_dev,
if (!util_path_is_dir("%s/%s", grp_dev, sub_dev))
errx(EXIT_FAILURE, "Error - cryptographic device %s/%s does not exist.", grp_dev, sub_dev);
- /* Skip devices, which are not supported by zcrypt layer */
- if (!util_path_is_readable("%s/type", grp_dev) ||
- !util_path_is_readable("%s/%s/online", grp_dev, sub_dev))
+ /*
+ * If not verbose mode, skip devices which are not supported
+ * by the zcrypt layer.
+ */
+ if (l.verbose == 0 &&
+ (!util_path_is_readable("%s/type", grp_dev) ||
+ !util_path_is_readable("%s/%s/online", grp_dev, sub_dev)))
return;
util_rec_set(rec, "card", sub_dev);
@@ -414,11 +455,13 @@ static void show_subdevices(struct util_rec *rec, const char *grp_dev)
*/
static void read_rec_default(struct util_rec *rec, const char *grp_dev)
{
- unsigned long facility;
char buf[256];
+ unsigned long facility;
- util_file_read_line(buf, sizeof(buf), "%s/type", grp_dev);
- util_rec_set(rec, "type", buf);
+ if (util_file_read_line(buf, sizeof(buf), "%s/type", grp_dev))
+ util_rec_set(rec, "type", "-");
+ else
+ util_rec_set(rec, "type", buf);
util_file_read_ul(&facility, 16, "%s/ap_functions", grp_dev);
if (facility & MASK_COPRO)
@@ -430,14 +473,16 @@ static void read_rec_default(struct util_rec *rec, const char *grp_dev)
else
util_rec_set(rec, "mode", "Unknown");
- util_file_read_line(buf, sizeof(buf), "%s/online", grp_dev);
- if (strcmp(buf, "0") == 0)
- util_rec_set(rec, "online", "offline");
+ if (util_file_read_line(buf, sizeof(buf), "%s/online", grp_dev))
+ util_rec_set(rec, "online", "-");
else
- util_rec_set(rec, "online", "online");
+ if (strcmp(buf, "0") == 0)
+ util_rec_set(rec, "online", "offline");
+ else
+ util_rec_set(rec, "online", "online");
util_file_read_line(buf, sizeof(buf), "%s/request_count", grp_dev);
- util_rec_set(rec, "request_count", buf);
+ util_rec_set(rec, "requests", buf);
}
/*
@@ -445,18 +490,17 @@ static void read_rec_default(struct util_rec *rec, const char *grp_dev)
*/
static void read_rec_verbose(struct util_rec *rec, const char *grp_dev)
{
+ int i;
unsigned long facility;
- char buf[256];
- long depth;
+ char buf[256], afile[PATH_MAX];
+ long depth, pending1, pending2;
if (l.verbose == 0)
return;
- util_file_read_line(buf, sizeof(buf), "%s/pendingq_count", grp_dev);
- util_rec_set(rec, "pendingq_count", buf);
-
- util_file_read_line(buf, sizeof(buf), "%s/requestq_count", grp_dev);
- util_rec_set(rec, "requestq_count", buf);
+ util_file_read_l(&pending1, 10, "%s/pendingq_count", grp_dev);
+ util_file_read_l(&pending2, 10, "%s/requestq_count", grp_dev);
+ util_rec_set(rec, "pending", "%ld", pending1 + pending2);
util_file_read_line(buf, sizeof(buf), "%s/hwtype", grp_dev);
util_rec_set(rec, "hwtype", buf);
@@ -465,7 +509,18 @@ static void read_rec_verbose(struct util_rec *rec, const char *grp_dev)
util_rec_set(rec, "depth", "%02d", depth + 1);
util_file_read_ul(&facility, 16, "%s/ap_functions", grp_dev);
- util_rec_set(rec, "facility", "0x%08x", facility);
+ for (i = 0; i < MAX_FAC_BITS; i++)
+ buf[i] = facility & fac_bits[i].mask ? fac_bits[i].c : '-';
+ buf[i] = '\0';
+ util_rec_set(rec, "facility", buf);
+
+ snprintf(afile, sizeof(afile), "%s/driver", grp_dev);
+ afile[sizeof(afile) - 1] = '\0';
+ memset(buf, 0, sizeof(buf));
+ if (readlink(afile, buf, sizeof(buf)) > 0)
+ util_rec_set(rec, "driver", strrchr(buf, '/') + 1);
+ else
+ util_rec_set(rec, "driver", "-no-driver-");
}
/*
@@ -481,9 +536,14 @@ static void show_device(struct util_rec *rec, const char *device)
grp_dev = util_path_sysfs("devices/ap/%s", device);
if (!util_path_is_dir(grp_dev))
errx(EXIT_FAILURE, "Error - cryptographic device %s does not exist.", device);
- /* Skip devices, which are not supported by zcrypt layer */
- if (!util_path_is_readable("%s/type", grp_dev) ||
- !util_path_is_readable("%s/online", grp_dev)) {
+
+ /*
+ * If not verbose mode, skip devices which are not supported
+ * by the zcrypt layer.
+ */
+ if (l.verbose == 0 &&
+ (!util_path_is_readable("%s/type", grp_dev) ||
+ !util_path_is_readable("%s/online", grp_dev))) {
goto out_free;
}
util_rec_set(rec, "card", card);
@@ -506,8 +566,7 @@ static void define_rec_default(struct util_rec *rec)
util_rec_def(rec, "type", UTIL_REC_ALIGN_LEFT, 5, "TYPE");
util_rec_def(rec, "mode", UTIL_REC_ALIGN_LEFT, 11, "MODE");
util_rec_def(rec, "online", UTIL_REC_ALIGN_LEFT, 7, "STATUS");
- util_rec_def(rec, "request_count", UTIL_REC_ALIGN_RIGHT, 11,
- "REQUEST_CNT");
+ util_rec_def(rec, "requests", UTIL_REC_ALIGN_RIGHT, 8, "REQUESTS");
}
/*
@@ -517,13 +576,11 @@ static void define_rec_verbose(struct util_rec *rec)
{
if (l.verbose == 0)
return;
- util_rec_def(rec, "pendingq_count", UTIL_REC_ALIGN_RIGHT, 12,
- "PENDINGQ_CNT");
- util_rec_def(rec, "requestq_count", UTIL_REC_ALIGN_RIGHT, 12,
- "REQUESTQ_CNT");
- util_rec_def(rec, "hwtype", UTIL_REC_ALIGN_RIGHT, 7, "HW_TYPE");
- util_rec_def(rec, "depth", UTIL_REC_ALIGN_RIGHT, 7, "Q_DEPTH");
+ util_rec_def(rec, "pending", UTIL_REC_ALIGN_RIGHT, 8, "PENDING");
+ util_rec_def(rec, "hwtype", UTIL_REC_ALIGN_RIGHT, 6, "HWTYPE");
+ util_rec_def(rec, "depth", UTIL_REC_ALIGN_RIGHT, 6, "QDEPTH");
util_rec_def(rec, "facility", UTIL_REC_ALIGN_LEFT, 10, "FUNCTIONS");
+ util_rec_def(rec, "driver", UTIL_REC_ALIGN_LEFT, 11, "DRIVER");
}
/*
--
2.21.3
From 47e15616b0c81f85918794a0af2073aac6455826 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Dan=20Hor=C3=A1k?= <dan@danny.cz>
Date: Tue, 11 Dec 2018 09:46:40 +0100
Subject: [PATCH 14/44] zkey: Fails to run commands generated by 'zkey
cryptsetup' (#1650628)
Description: zkey: Fails to run commands generated by 'zkey cryptsetup'
Symptom: Fails to run commands generated by 'zkey cryptsetup'.
Problem: When using 'zkey cryptsetup' with --run option the
execution of the generated commands may fail, when
the executable to be run is located in '/sbin'.
Solution: Include /sbin into PATH when executing commands.
Reproduction: Use 'zkey cryptsetup' with option --run on a distribution
where 'cryptsetup' is located in '/sbin'.
Upstream-ID: 9100327092c470c2e5b5819087c8094822a1c751
---
zkey/keystore.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/zkey/keystore.c b/zkey/keystore.c
index 11f555c..a4ad634 100644
--- a/zkey/keystore.c
+++ b/zkey/keystore.c
@@ -3235,7 +3235,7 @@ static int _keystore_execute_cmd(const char *cmd,
{
int rc;
- rc = setenv("PATH", "/bin:/usr/bin:/usr/sbin", 1);
+ rc = setenv("PATH", "/bin:/sbin:/usr/bin:/usr/sbin", 1);
if (rc < 0)
return rc;
--
2.21.3
From 3328fadb3147a24bae10495259fa7fd00b973eec Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Dan=20Hor=C3=A1k?= <dan@danny.cz>
Date: Tue, 11 Dec 2018 10:14:05 +0100
Subject: [PATCH 15/44] zkey: Enhance error message about missing CCA library
(#1655134)
Description: zkey: Enhance error message about missing CCA library.
Symptom: "zkey-cryptsetup reencipher" fails with missing
library and confusing error message.
Problem: The "zkey reencipher" command as well as the "zkey-cryptsetup
reencipher" command requires the IBM CCA Host Libraries and
Tools package to be installed. This is a closed source
library that is not distributed by the distributions, but
must be downloaded separately from an IBM web page.
Solution: Enhance the error message to point to the web page where
the package can be downloaded.
Reproduction: Run the "zkey-cryptsetup reencipher" or "zkey reencipher"
command without having installed the IBM CCA Host Libraries
and Tools package.
---
zkey/pkey.c | 13 +++++++++----
zkey/zkey-cryptsetup.1 | 3 ++-
zkey/zkey.1 | 3 ++-
3 files changed, 13 insertions(+), 6 deletions(-)
diff --git a/zkey/pkey.c b/zkey/pkey.c
index fe43d02..15e606a 100644
--- a/zkey/pkey.c
+++ b/zkey/pkey.c
@@ -48,6 +48,7 @@
* Definitions for the CCA library
*/
#define CCA_LIBRARY_NAME "libcsulcca.so"
+#define CCA_WEB_PAGE "http://www.ibm.com/security/cryptocards"
#define DEFAULT_KEYBITS 256
@@ -71,16 +72,20 @@ int load_cca_library(void **lib_csulcca, t_CSNBKTC *dll_CSNBKTC, bool verbose)
/* Load the CCA library */
*lib_csulcca = dlopen(CCA_LIBRARY_NAME, RTLD_GLOBAL | RTLD_NOW);
if (*lib_csulcca == NULL) {
- warnx("%s\nEnsure that the IBM CCA Host Libraries and "
- "Tools are installed properly", dlerror());
+ pr_verbose(verbose, "%s", dlerror());
+ warnx("The command requires the IBM CCA Host Libraries and "
+ "Tools.\nFor the supported environments and downloads, "
+ "see:\n%s", CCA_WEB_PAGE);
return -ELIBACC;
}
/* Get the Key Token Change function */
*dll_CSNBKTC = (t_CSNBKTC)dlsym(*lib_csulcca, "CSNBKTC");
if (*dll_CSNBKTC == NULL) {
- warnx("%s\nEnsure that the IBM CCA Host Libraries and "
- "Tools are installed properly", dlerror());
+ pr_verbose(verbose, "%s", dlerror());
+ warnx("The command requires the IBM CCA Host Libraries and "
+ "Tools.\nFor the supported environments and downloads, "
+ "see:\n%s", CCA_WEB_PAGE);
dlclose(*lib_csulcca);
*lib_csulcca = NULL;
return -ELIBACC;
diff --git a/zkey/zkey-cryptsetup.1 b/zkey/zkey-cryptsetup.1
index bf92e21..988ef76 100644
--- a/zkey/zkey-cryptsetup.1
+++ b/zkey/zkey-cryptsetup.1
@@ -182,7 +182,8 @@ behave in the same way as with \fBcryptsetup\fP.
.PP
.B Note:
The \fBreencipher\fP command requires the CCA host library (libcsulcca.so)
-to be installed.
+to be installed. For the supported environments and downloads, see:
+\fIhttp://www.ibm.com/security/cryptocards\fP
.
.
.
diff --git a/zkey/zkey.1 b/zkey/zkey.1
index c9f7906..0837b27 100644
--- a/zkey/zkey.1
+++ b/zkey/zkey.1
@@ -282,7 +282,8 @@ a staged re-enciphering for the \fBOLD\fP to \fBCURRENT\fP case.
.PP
.B Note:
The \fBreencipher\fP command requires the CCA host library (libcsulcca.so)
-to be installed.
+to be installed. For the supported environments and downloads, see:
+\fIhttp://www.ibm.com/security/cryptocards\fP
.
.SS "Import existing AES secure keys into the secure key repository"
.
--
2.21.3
From ba7d612c055c887d5cbb596a369d32f00c47711d Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Dan=20Hor=C3=A1k?= <dan@danny.cz>
Date: Tue, 29 Jan 2019 13:06:21 +0100
Subject: [PATCH 16/44] zfcpdump: add install script for zfcpdump kernel
(#1600480)
Summary: zfcpdump: add install script for zfcpdump kernel
Description: zipl expects to find the zfcpdump kernel at a specific location
(usually /usr/lib/s390-tools/zfcpdump/zfcpdump-image). During
kernel installation however, the kernels are installed to /boot.
In order for zipl to find the image add a script that manages a
link to the latest installed zfcpdump kernel at said location.
---
.gitignore | 3 +-
common.mak | 7 +-
zfcpdump/10-zfcpdump.install.in | 114 ++++++++++++++++++++++++++++++++
zfcpdump/Makefile | 22 ++++--
zipl/include/zipl.h | 7 --
zipl/src/Makefile | 7 +-
zipl/src/job.c | 10 +--
7 files changed, 143 insertions(+), 27 deletions(-)
create mode 100644 zfcpdump/10-zfcpdump.install.in
diff --git a/.gitignore b/.gitignore
index 042233f..6cbec8c 100644
--- a/.gitignore
+++ b/.gitignore
@@ -76,7 +76,8 @@ zdsfs/zdsfs
zdump/zgetdump
zfcpdump/cpioinit
zfcpdump/zfcpdump_part
-zfcpdump/zfcpdump_part.rd
+zfcpdump/zfcpdump-initrd
+zfcpdump/10-zfcpdump.install
ziomon/ziomon_mgr
ziomon/ziomon_util
ziomon/ziomon_zfcpdd
diff --git a/common.mak b/common.mak
index 6f0990e..6029687 100644
--- a/common.mak
+++ b/common.mak
@@ -182,9 +182,10 @@ GROUP = $(shell id -gn)
export INSTALLDIR BINDIR LIBDIR MANDIR OWNER GROUP
# Special defines for zfcpdump
-ZFCPDUMP_PART_IMAGE = zfcpdump_part.image
-ZFCPDUMP_PART_RD = zfcpdump_part.rd
-export ZFCPDUMP_DIR ZFCPDUMP_PART_IMAGE ZFCPDUMP_PART_RD
+ZFCPDUMP_IMAGE = zfcpdump-image
+ZFCPDUMP_INITRD = zfcpdump-initrd
+ZFCPDUMP_FLAVOR = zfcpdump
+export ZFCPDUMP_DIR ZFCPDUMP_IMAGE ZFCPDUMP_INITRD ZFCPDUMP_FLAVOR
CFLAGS ?= $(DEFAULT_CFLAGS) $(OPT_FLAGS)
HOSTCFLAGS ?= $(DEFAULT_CFLAGS) $(OPT_FLAGS)
diff --git a/zfcpdump/10-zfcpdump.install.in b/zfcpdump/10-zfcpdump.install.in
new file mode 100644
index 0000000..37fde3b
--- /dev/null
+++ b/zfcpdump/10-zfcpdump.install.in
@@ -0,0 +1,114 @@
+#!/bin/bash
+#
+# 10-zfcpdump.install - Installation script to handle zfcpdump kernels
+#
+# Copyright IBM Corp. 2018
+#
+# s390-tools is free software; you can redistribute it and/or modify
+# it under the terms of the MIT license. See LICENSE for details.
+#
+#
+# This script supports two modes:
+#
+# 1) Installing the images to /boot/<machine-id>/<kernel-version>
+# subdirectories, i.e. BOOT_DIR_ABS, as recommended by the BLS.
+# In this case file names are taken over from the original files.
+#
+# 2) Installing the images directly to /boot. In this case the files are
+# renamed to <original-name>-<kernel-version>.
+#
+# The existence of BOOT_DIR_ABS is taken as trigger to switch between both
+# modes.
+#
+# The KERNEL_VERSION is assumed to contain '@flavor@' to identify the image
+# as a zfcpdump kernel.
+
+COMMAND="$1"
+KERNEL_VERSION="$2"
+BOOT_DIR_ABS="$3"
+KERNEL_IMAGE="$4"
+
+# Location zipl looks for the zfcpdump kernel
+ZFCPDUMP_IMAGE='@zfcpdump_image@'
+
+# Only handle zfcpdump kernels
+echo "$KERNEL_VERSION" | grep -q '@flavor@' || exit 0
+
+case "$COMMAND" in
+ add)
+ KERNEL_DIR="$(dirname $KERNEL_IMAGE)"
+ KERNEL_NAME="$(basename $KERNEL_IMAGE)"
+
+ for f in \
+ "$KERNEL_IMAGE" \
+ "$KERNEL_DIR"/System.map \
+ "$KERNEL_DIR"/config \
+ "$KERNEL_DIR"/zImage.stub
+ do
+ test -e "$f" || continue
+ test -d "$BOOT_DIR_ABS" \
+ && DEST="$BOOT_DIR_ABS/$(basename $f)" \
+ || DEST="/boot/$(basename $f)-$KERNEL_VERSION"
+
+ cp -aT "$f" "$DEST"
+ test $(command -v restorecon) && restorecon -R "$DEST"
+ done
+
+ # hmac file need special treatment
+ f="$KERNEL_DIR/.$KERNEL_NAME.hmac"
+ if [ -e "$f" ]; then
+ test -d "$BOOT_DIR_ABS" \
+ && DEST="$BOOT_DIR_ABS/$(basename $f)" \
+ || DEST="/boot/.$KERNEL_NAME-$KERNEL_VERSION.hmac"
+
+ cp -aT "$f" "$DEST"
+ test $(command -v restorecon) && restorecon -R "$DEST"
+ fi
+
+ # Set link so zipl finds the kernel
+ test -d "$BOOT_DIR_ABS" \
+ && TARGET="$BOOT_DIR_ABS/$KERNEL_NAME" \
+ || TARGET="/boot/$KERNEL_NAME-$KERNEL_VERSION"
+ ln -sf "$TARGET" "$ZFCPDUMP_IMAGE"
+ ;;
+
+ remove)
+ # On removal
+ # $KERNEL_IMAGE is empty -> $KERNEL_NAME is empty -> rebuild it
+ KERNEL_NAME="$(basename $(readlink $ZFCPDUMP_IMAGE))"
+ if [ -d "$BOOT_DIR_ABS" ]; then
+ INSTALL_DIR="$(dirname $BOOT_DIR_ABS)"
+ else
+ INSTALL_DIR="/boot/"
+ KERNEL_NAME="$(echo $KERNEL_NAME \
+ | sed -e "s#\(.*\)-$KERNEL_VERSION#\1#")"
+ fi
+
+ for f in $(find "$INSTALL_DIR" -name "*$KERNEL_VERSION*"); do
+ rm -rf "$f"
+ done
+
+ # Update link to latest remaining zfcpdump kernel.
+ if [ $(readlink "$ZFCPDUMP_IMAGE" | grep "$KERNEL_VERSION") ]
+ then
+ NEXT_IMAGE=$( \
+ find "$INSTALL_DIR" -type f \
+ | grep '@flavor@' \
+ | grep "$KERNEL_NAME" \
+ | grep -v "hmac" \
+ | sort -V \
+ | tail -n1 )
+
+ test $NEXT_IMAGE \
+ && ln -sf "$NEXT_IMAGE" "$ZFCPDUMP_IMAGE" \
+ || rm -f "$ZFCPDUMP_IMAGE"
+ fi
+ ;;
+ *)
+ ;;
+esac
+
+# Prevent execution of all other scripts.
+# The zfcpdump kernel is stripped down to the bare minimum needed for
+# dumping. It is not supposed to be used for any other purpose.
+exit 77
diff --git a/zfcpdump/Makefile b/zfcpdump/Makefile
index 5fcb073..cb2fd41 100644
--- a/zfcpdump/Makefile
+++ b/zfcpdump/Makefile
@@ -1,6 +1,7 @@
include ../common.mak
CPIOINIT = $(call echocmd," CPIOINI ",/$@)./cpioinit
+INSTALL_SCRIPTS = 10-zfcpdump.install
ifeq (${HAVE_LIBC_STATIC},0)
@@ -20,7 +21,7 @@ check_dep:
"HAVE_LIBC_STATIC=0", \
"-static")
-all: check_dep $(ZFCPDUMP_PART_RD)
+all: check_dep $(ZFCPDUMP_INITRD) scripts
cpioinit: cpioinit.c
$(HOSTCC) $(HOSTCFLAGS) -o $@ $^
@@ -29,17 +30,26 @@ zfcpdump_part: zfcpdump.o zfcpdump_part.o
$(LINK) $(ALL_LDFLAGS) $^ -static -o $@
$(STRIP) -s $@
-$(ZFCPDUMP_PART_RD): cpioinit zfcpdump_part
+$(ZFCPDUMP_INITRD): cpioinit zfcpdump_part
$(CPIOINIT) zfcpdump_part > $@.tmp
$(GZIP) -f $@.tmp
- $(MV) $@.tmp.gz $(ZFCPDUMP_PART_RD)
+ $(MV) $@.tmp.gz $(ZFCPDUMP_INITRD)
+
+scripts: $(INSTALL_SCRIPTS)
+ chmod +x $(INSTALL_SCRIPTS)
install: all
- $(INSTALL) -m 611 $(ZFCPDUMP_PART_RD) $(DESTDIR)$(ZFCPDUMP_DIR)
+ $(INSTALL) -m 611 $(ZFCPDUMP_INITRD) $(DESTDIR)$(ZFCPDUMP_DIR)
+%: %.in
+ zfcpdump_image=$(ZFCPDUMP_DIR)/$(ZFCPDUMP_IMAGE); \
+ $(SED) -e "s#@zfcpdump_image@#$$zfcpdump_image#g" \
+ -e "s#@flavor@#$(ZFCPDUMP_FLAVOR)#g" \
+ < $< > $@
endif
clean:
- rm -f *.o *.gz *.tmp *~ zfcpdump_part cpioinit $(ZFCPDUMP_PART_RD)
+ rm -f *.o *.gz *.tmp *~ zfcpdump_part cpioinit $(ZFCPDUMP_INITRD) \
+ $(INSTALL_SCRIPTS)
-.PHONY: all clean install check_dep
+.PHONY: all clean install check_dep scripts
diff --git a/zipl/include/zipl.h b/zipl/include/zipl.h
index 2aed54f..770801e 100644
--- a/zipl/include/zipl.h
+++ b/zipl/include/zipl.h
@@ -48,13 +48,6 @@
#define MENU_DEFAULT_PROMPT 0
#define MENU_DEFAULT_TIMEOUT 0
-#define FSDUMP_IMAGE STRINGIFY(ZFCPDUMP_DIR) "/" STRINGIFY(ZFCPDUMP_FS_IMAGE)
-#define FSDUMP_RAMDISK STRINGIFY(ZFCPDUMP_DIR) "/" STRINGIFY(ZFCPDUMP_FS_RD)
-#define FSDUMP_PART_IMAGE STRINGIFY(ZFCPDUMP_DIR) "/" \
- STRINGIFY(ZFCPDUMP_PART_IMAGE)
-#define FSDUMP_PART_RAMDISK STRINGIFY(ZFCPDUMP_DIR) "/" \
- STRINGIFY(ZFCPDUMP_PART_RD)
-
#define MAX_DUMP_VOLUMES 32
/* Internal component load address type */
diff --git a/zipl/src/Makefile b/zipl/src/Makefile
index 1634c0d..be14fce 100644
--- a/zipl/src/Makefile
+++ b/zipl/src/Makefile
@@ -2,11 +2,8 @@
include ../../common.mak
ALL_CPPFLAGS += -I../include -I../boot \
- -DZFCPDUMP_DIR=$(ZFCPDUMP_DIR) \
- -DZFCPDUMP_FS_IMAGE=$(ZFCPDUMP_FS_IMAGE) \
- -DZFCPDUMP_FS_RD=$(ZFCPDUMP_FS_RD) \
- -DZFCPDUMP_PART_IMAGE=$(ZFCPDUMP_PART_IMAGE) \
- -DZFCPDUMP_PART_RD=$(ZFCPDUMP_PART_RD) \
+ -DZFCPDUMP_IMAGE="STRINGIFY($(ZFCPDUMP_DIR)/$(ZFCPDUMP_IMAGE))" \
+ -DZFCPDUMP_INITRD="STRINGIFY($(ZFCPDUMP_DIR)/$(ZFCPDUMP_INITRD))" \
-D_FILE_OFFSET_BITS=64 $(NO_PIE_CFLAGS)
ALL_LDFLAGS += -Wl,-z,noexecstack $(NO_PIE_LDFLAGS)
diff --git a/zipl/src/job.c b/zipl/src/job.c
index e6d2981..22d2549 100644
--- a/zipl/src/job.c
+++ b/zipl/src/job.c
@@ -874,22 +874,22 @@ check_job_dump_images(struct job_dump_data* dump, char* name)
{
int rc;
/* Add data needed to convert fs dump job to IPL job */
- rc = misc_check_readable_file(FSDUMP_PART_IMAGE);
+ rc = misc_check_readable_file(ZFCPDUMP_IMAGE);
if (rc) {
error_text("Need external file '%s' for partition dump",
- FSDUMP_PART_IMAGE);
+ ZFCPDUMP_IMAGE);
return rc;
}
- dump->image = misc_strdup(FSDUMP_PART_IMAGE);
+ dump->image = misc_strdup(ZFCPDUMP_IMAGE);
if (dump->image == NULL)
return -1;
dump->image_addr = DEFAULT_IMAGE_ADDRESS;
/* Ramdisk is no longer required with new initramfs dump system */
- if (misc_check_readable_file(FSDUMP_PART_RAMDISK))
+ if (misc_check_readable_file(ZFCPDUMP_INITRD))
dump->ramdisk = NULL;
else {
- dump->ramdisk = misc_strdup(FSDUMP_PART_RAMDISK);
+ dump->ramdisk = misc_strdup(ZFCPDUMP_INITRD);
if (dump->ramdisk == NULL)
return -1;
dump->ramdisk_addr = UNSPECIFIED_ADDRESS;
--
2.21.3
From eef1d70b8bd0ec96aa90f5dad9795196c85382ce Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Dan=20Hor=C3=A1k?= <dan@danny.cz>
Date: Wed, 9 Jan 2019 13:58:10 +0100
Subject: [PATCH 17/44] pkey: Support autoloading kernel pkey module (#1664632)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
The module is loaded automatically based on CPU features, but it's still
too late in some use cases. Thus allow distros to use explicit loading.
See also: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=63c19be095d0f8eb8992674991e44b4228bd4179
Closes: https://github.com/ibm-s390-tools/s390-tools/pull/51
Signed-off-by: Dan Horák <dan@danny.cz>
Reviewed-by: Ingo Franzki <ifranzki@linux.ibm.com>
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
(cherry picked from commit dffd41943e5c01be2f343da7726edabf9d2ec05e)
---
etc/modules-load.d/s390-pkey.conf | 2 ++
1 file changed, 2 insertions(+)
create mode 100644 etc/modules-load.d/s390-pkey.conf
diff --git a/etc/modules-load.d/s390-pkey.conf b/etc/modules-load.d/s390-pkey.conf
new file mode 100644
index 0000000..972a099
--- /dev/null
+++ b/etc/modules-load.d/s390-pkey.conf
@@ -0,0 +1,2 @@
+# Load protected key support module on s390 early at boot
+pkey
--
2.21.3
From 7a3ba6fb39f015da0ee10e1dc0e14ae9fcf6347f Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Dan=20Hor=C3=A1k?= <dan@danny.cz>
Date: Tue, 21 May 2019 13:39:08 +0200
Subject: [PATCH 18/44] zipl: Secure Boot support for SCSI IPL (#1659401)
Description: The Secure Boot firmware feature ensures that only signed
and verified code is executed during IPL.
This feature adds support for secure boot to the zipl
tool and internal loader.
Upstream-ID: e764f460c457ab2a6000acb5f2eb7169866ce192
Upstream-ID: 6825645a21a3221629e7d5aa9d5cfbf6e3bc4188
Upstream-ID: 0c1a63ce5ec6059374d9f8dd55bffbb42ef9bd45
Upstream-ID: 7c7e10ed8fb048efc4e0cd91b0f6fa704fba128e
Upstream-ID: 58a7462f65d85726168303bd58e7472bd4c82731
Upstream-ID: 75d4317f208da1e25afd520fe43a1786333388ad
Upstream-ID: dc2e4395506956a99819484d8938e877c4c2cd88
Upstream-ID: 7e7a77675d2b88b3d516d60aa64f2b1264b25117
Upstream-ID: 331b54d573c38c7eed6617eef69cf296c2e86d6d
Upstream-ID: 6ea645b3453264a0f1a60955c4476dab54035f88
---
zipl/boot/Makefile | 26 ++-
zipl/boot/error.h | 3 +
zipl/boot/libc.c | 20 ++
zipl/boot/libc.h | 1 +
zipl/boot/s390.h | 5 +
zipl/boot/stage2.c | 9 +-
zipl/boot/stage2.h | 3 +-
zipl/boot/stage2.lds | 5 +-
zipl/boot/stage3.c | 114 ++++++++---
zipl/boot/stage3.h | 142 ++++++++++++++
zipl/boot/stage3.lds | 38 ++--
zipl/include/boot.h | 11 +-
zipl/include/bootmap.h | 31 +++
zipl/include/job.h | 1 +
zipl/include/misc.h | 1 +
zipl/include/scan.h | 3 +-
zipl/include/zipl.h | 15 +-
zipl/man/zipl.8 | 15 ++
zipl/man/zipl.conf.5 | 27 +++
zipl/src/Makefile | 7 +-
zipl/src/boot.c | 33 ++--
zipl/src/bootmap.c | 417 +++++++++++++++++++++++++++++++++--------
zipl/src/job.c | 44 ++++-
zipl/src/misc.c | 1 -
zipl/src/scan.c | 34 ++--
25 files changed, 837 insertions(+), 169 deletions(-)
diff --git a/zipl/boot/Makefile b/zipl/boot/Makefile
index 52b3a23..da7e95f 100644
--- a/zipl/boot/Makefile
+++ b/zipl/boot/Makefile
@@ -13,10 +13,9 @@ FILES = fba0.bin fba1b.bin fba2.bin \
eckd0_ldl.bin eckd0_cdl.bin \
eckd1.bin eckd1b.bin eckd2.bin \
tape0.bin \
- eckd2dump_sv.bin tape2dump.bin fba2dump.bin eckd2dump_mv.bin \
- stage3.bin
+ eckd2dump_sv.bin tape2dump.bin fba2dump.bin eckd2dump_mv.bin
-all: data.o data.h tape0.bin
+all: data.o data.h tape0.bin stage3.bin
# Prevent make from using some default rules...
%: %.S
@@ -44,7 +43,7 @@ eckd2.exec: head.o stage2.o cio.o eckd2.o libc.o menu.o sclp.o \
fba2.exec: head.o stage2.o cio.o fba2.o libc.o menu.o sclp.o \
kdump2.o kdump.o entry.o
stage3.exec: head.o stage3.o kdump3.o libc.o sclp.o sclp_stage3.o \
- kdump.o entry.o
+ kdump.o entry.o stage3.lds
%.exec: %.o
@STAGE=$$( \
@@ -77,6 +76,22 @@ stage3.exec: head.o stage3.o kdump3.o libc.o sclp.o sclp_stage3.o \
--only-section=.fixup \
$< $@
+stage3.bin: stage3.exec
+ $(OBJCOPY) -O binary \
+ --only-section=.stage2.head \
+ --only-section=.text.dummy \
+ --only-section=.text.start \
+ --only-section=.text \
+ --only-section=.ex_table \
+ --only-section=.data \
+ --only-section=.rodata.str1.2 \
+ --only-section=.rodata \
+ --only-section=.stage2dump.tail \
+ --only-section=.eckd2dump_mv.tail \
+ --only-section=.fixup \
+ --pad-to=0xf000 \
+ $< $@
+
data.o: $(FILES)
$(LD) $(NO_PIE_LDFLAGS) -r -b binary -o data.o $(FILES)
@@ -86,6 +101,7 @@ data.h: data.o
echo "extern char $$SYMBOL;" >>data.h; done
clean:
- rm -f *.o *.exec *.bin $(FILES) data.o data.h tape0.bin *.xxx *.yyy
+ rm -f *.o *.exec *.bin $(FILES) data.o data.h tape0.bin *.xxx *.yyy \
+ stage3.bin
.PHONY: all clean
diff --git a/zipl/boot/error.h b/zipl/boot/error.h
index 93fd748..715973e 100644
--- a/zipl/boot/error.h
+++ b/zipl/boot/error.h
@@ -31,6 +31,9 @@
/* Internal error */
#define EINTERNAL 0x00004511
+/* Secure IPL error */
+#define ESECUREBOOT 0x00004512
+
/* kdump: No operating system information was found */
#define EOS_INFO_MISSING 0x00004520
diff --git a/zipl/boot/libc.c b/zipl/boot/libc.c
index f86f62a..5944c4e 100644
--- a/zipl/boot/libc.c
+++ b/zipl/boot/libc.c
@@ -58,6 +58,26 @@ void *memcpy(void *dest, const void *src, unsigned long n)
return dest;
}
+/*
+ * Move @n bytes of memory from @src to @dest. The memory regions may overlap.
+ */
+void *memmove(void *dest, const void *src, unsigned long n)
+{
+ const char *s = src;
+ char *d = dest;
+
+ if (s < d) {
+ d += n;
+ s += n;
+ while (n--)
+ *--d = *--s;
+ } else {
+ while (n--)
+ *d++ = *s++;
+ }
+ return dest;
+}
+
/*
* Copy string
*/
diff --git a/zipl/boot/libc.h b/zipl/boot/libc.h
index b05a116..44097fa 100644
--- a/zipl/boot/libc.h
+++ b/zipl/boot/libc.h
@@ -49,6 +49,7 @@ typedef unsigned char uint8_t;
void printf(const char *, ...);
void sprintf(char *, const char *, ...);
void *memcpy(void *, const void *, unsigned long);
+void *memmove(void *, const void *, unsigned long);
void *memset(void *, int c, unsigned long);
char *strcat(char *, const char *);
int strncmp(const char *, const char *, unsigned long);
diff --git a/zipl/boot/s390.h b/zipl/boot/s390.h
index 279d16b..6e5a7ae 100644
--- a/zipl/boot/s390.h
+++ b/zipl/boot/s390.h
@@ -35,6 +35,11 @@ struct psw_t {
uint64_t addr;
} __aligned(8);
+struct psw32_t {
+ uint32_t mask;
+ uint32_t addr;
+} __aligned(8);
+
void load_wait_psw(uint64_t, struct psw_t *);
struct _lowcore {
diff --git a/zipl/boot/stage2.c b/zipl/boot/stage2.c
index 02d644e..99e1bd1 100644
--- a/zipl/boot/stage2.c
+++ b/zipl/boot/stage2.c
@@ -115,8 +115,13 @@ void start(void)
/* skip header */
entry = (struct component_entry *)
(load_address + sizeof(struct component_header));
-
- while (entry->type == COMPONENT_LOAD) {
+ while (entry->type == COMPONENT_LOAD ||
+ entry->type == COMPONENT_SIGNATURE) {
+ if (entry->type == COMPONENT_SIGNATURE) {
+ /* Skip unhandled signature components */
+ entry++;
+ continue;
+ }
load_address = (void *)(unsigned long)
entry->address.load_address[1];
load_blocklist(entry, subchannel_id, load_address);
diff --git a/zipl/boot/stage2.h b/zipl/boot/stage2.h
index b79c798..8fd5bd3 100644
--- a/zipl/boot/stage2.h
+++ b/zipl/boot/stage2.h
@@ -61,7 +61,8 @@ struct component_entry {
typedef enum {
COMPONENT_EXECUTE = 0x01,
- COMPONENT_LOAD = 0x02
+ COMPONENT_LOAD = 0x02,
+ COMPONENT_SIGNATURE = 0x03
} component_type;
struct stage2_descr {
diff --git a/zipl/boot/stage2.lds b/zipl/boot/stage2.lds
index befe8e2..41efa79 100644
--- a/zipl/boot/stage2.lds
+++ b/zipl/boot/stage2.lds
@@ -9,7 +9,8 @@
* 0x2000-0x4fff Sections (load): head, text, data, rodata, rodata.str
* 0x5000-0x51ff eckd2dump_mv parameter block (426 bytes)
* 0x5200-0x5fff Sections: bss
- * 0x6000-0x9fff Memory allocation (heap)
+ * 0x6000-0x8fff Memory allocation (heap)
+ * 0x9000-0x9fff Memory to load stage3 parameter to
* 0xa000-0xdfff Memory to load stage3 to
* 0xe000-0xffff Stack
*
@@ -52,7 +53,7 @@ SECTIONS
. = 0x6000;
__heap_start = .;
- . = 0xa000;
+ . = 0x9000;
__heap_stop = .;
. = 0xf000;
diff --git a/zipl/boot/stage3.c b/zipl/boot/stage3.c
index eb02627..f7a9597 100644
--- a/zipl/boot/stage3.c
+++ b/zipl/boot/stage3.c
@@ -12,35 +12,15 @@
#include "libc.h"
#include "s390.h"
#include "stage3.h"
+#include "error.h"
-/*
- * 48 Byte dummy space for external symbols
- * _parm_addr; address of parmline
- * _initrd_addr; address of initrd
- * _initrd_len; length of initrd
- * _load_psw; load psw of kernel
- * _extra_parm; use extra parm line mechanism?
- * stage3_flags; flags (e.g. STAGE3_FLAG_KDUMP)
- *
- * needed to blow up the binary and leave room
- */
-__attribute__ ((section(".text.dummy"))) void _dummy(void)
-{
- asm volatile(
- ".long 0x00000000\n"
- ".long 0x00000000\n"
- ".long 0x00000000\n"
- ".long 0x00000000\n"
- ".long 0x00000000\n"
- ".long 0x00000000\n"
- ".long 0x00000000\n"
- ".long 0x00000000\n"
- ".long 0x00000000\n"
- ".long 0x00000000\n"
- ".long 0x00000000\n"
- ".long 0x00000000\n"
- );
-}
+#define for_each_rb_entry(entry, rb) \
+ for (entry = rb->entries; \
+ (void *) entry + sizeof(*entry) <= (void *) rb + rb->len; \
+ entry++)
+
+static const char *msg_sipl_inval = "Secure boot failure: invalid load address";
+static const char *msg_sipl_unverified = "Secure boot failure: unverified load address";
static unsigned char ebc_037[256] = {
/* 0x00 NUL SOH STX ETX *SEL HT *RNL DEL */
@@ -221,6 +201,64 @@ start_kernel(void)
: [psw] "a" (psw) );
}
+unsigned int
+is_verified_address(unsigned long image_addr)
+{
+ struct ipl_rb_component_entry *comp;
+ struct ipl_rb_components *comps;
+ struct ipl_pl_hdr *pl_hdr;
+ struct ipl_rl_hdr *rl_hdr;
+ struct ipl_rb_hdr *rb_hdr;
+ unsigned long tmp;
+ void *rl_end;
+
+ /*
+ * There is an IPL report, to find it load the pointer to the
+ * IPL parameter information block from lowcore and skip past
+ * the IPL parameter list, then align the address to a double
+ * word boundary.
+ */
+ tmp = (unsigned long) S390_lowcore.ipl_parmblock_ptr;
+ pl_hdr = (struct ipl_pl_hdr *) tmp;
+ tmp = (tmp + pl_hdr->len + 7) & -8UL;
+ rl_hdr = (struct ipl_rl_hdr *) tmp;
+ /* Walk through the IPL report blocks in the IPL Report list */
+ comps = NULL;
+ rl_end = (void *) rl_hdr + rl_hdr->len;
+ rb_hdr = (void *) rl_hdr + sizeof(*rl_hdr);
+ while ((void *) rb_hdr + sizeof(*rb_hdr) < rl_end &&
+ (void *) rb_hdr + rb_hdr->len <= rl_end) {
+ switch (rb_hdr->rbt) {
+ case IPL_RBT_COMPONENTS:
+ comps = (struct ipl_rb_components *) rb_hdr;
+ break;
+ default:
+ break;
+ }
+
+ rb_hdr = (void *) rb_hdr + rb_hdr->len;
+ }
+ for_each_rb_entry(comp, comps) {
+ if (image_addr == comp->addr &&
+ comp->flags & IPL_RB_COMPONENT_FLAG_SIGNED &&
+ comp->flags & IPL_RB_COMPONENT_FLAG_VERIFIED)
+ return 1;
+ }
+ return 0;
+}
+
+unsigned int
+secure_boot_enabled()
+{
+ struct ipl_pl_hdr *pl_hdr;
+ unsigned long tmp;
+
+ tmp = (unsigned long) S390_lowcore.ipl_parmblock_ptr;
+ pl_hdr = (struct ipl_pl_hdr *) tmp;
+
+ return pl_hdr->flags & IPL_FLAG_SECURE;
+}
+
void start(void)
{
unsigned int subchannel_id;
@@ -228,6 +266,26 @@ void start(void)
unsigned char *command_line = (unsigned char *)COMMAND_LINE;
unsigned int begin = 0, end = 0, length = 0;
+ /*
+ * IPL process is secure we have to use default IPL values and
+ * check if the psw jump address is within at the start of a
+ * verified component. If it is not IPL is aborted.
+ */
+ if (secure_boot_enabled()) {
+ if (_image_addr != DEFAULT_IMAGE_ADDR ||
+ _load_psw != DEFAULT_PSW_LOAD)
+ panic(ESECUREBOOT, "%s", msg_sipl_inval);
+
+ if (!is_verified_address(_load_psw & PSW_ADDR_MASK))
+ panic(ESECUREBOOT, "%s", msg_sipl_unverified);
+ }
+ /*
+ * cut the kernel header
+ */
+ memmove((void *)_image_addr,
+ (void *)_image_addr + KERNEL_HEADER_SIZE,
+ _image_len - KERNEL_HEADER_SIZE);
+
/* store subchannel ID into low core and into new kernel space */
subchannel_id = S390_lowcore.subchannel_id;
*(unsigned int *)__LC_IPLDEV = subchannel_id;
diff --git a/zipl/boot/stage3.h b/zipl/boot/stage3.h
index cdc0d77..91477b1 100644
--- a/zipl/boot/stage3.h
+++ b/zipl/boot/stage3.h
@@ -27,14 +27,156 @@
#define STAGE3_FLAG_SCSI 0x0001000000000000ULL
#define STAGE3_FLAG_KDUMP 0x0002000000000000ULL
+#define IPL_FLAG_SECURE 0x40
+
+#define DEFAULT_IMAGE_ADDR 0x10000
+#define DEFAULT_PSW_LOAD 0x0008000080010000L
+#define PSW_ADDR_MASK 0x000000007FFFFFFFL
+#define KERNEL_HEADER_SIZE 65536
+
#define UNSPECIFIED_ADDRESS -1ULL
+
+/* IPL Parameter List header */
+struct ipl_pl_hdr {
+ uint32_t len;
+ uint8_t flags;
+ uint8_t reserved1[2];
+ uint8_t version;
+} __packed;
+
+/* IPL Parameter Block header */
+struct ipl_pb_hdr {
+ uint32_t len;
+ uint8_t pbt;
+} __packed;
+
+/* IPL Parameter Block 0 with common fields */
+struct ipl_pb0_common {
+ uint32_t len;
+ uint8_t pbt;
+ uint8_t flags;
+ uint8_t reserved1[2];
+ uint8_t loadparm[8];
+ uint8_t reserved2[84];
+} __packed;
+
+/* IPL Parameter Block 0 for FCP */
+struct ipl_pb0_fcp {
+ uint32_t len;
+ uint8_t pbt;
+ uint8_t reserved1[3];
+ uint8_t loadparm[8];
+ uint8_t reserved2[304];
+ uint8_t opt;
+ uint8_t reserved3[3];
+ uint8_t cssid;
+ uint8_t reserved4[1];
+ uint8_t devno;
+ uint8_t reserved5[4];
+ uint64_t wwpn;
+ uint64_t lun;
+ uint32_t bootprog;
+ uint8_t reserved6[12];
+ uint64_t br_lba;
+ uint32_t scp_data_len;
+ uint8_t reserved7[260];
+ uint8_t scp_data[];
+} __packed;
+
+/* IPL Parameter Block 0 for CCW */
+struct ipl_pb0_ccw {
+ uint32_t len;
+ uint8_t pbt;
+ uint8_t flags;
+ uint8_t reserved1[2];
+ uint8_t loadparm[8];
+ uint8_t reserved2[84];
+ uint16_t reserved3 : 13;
+ uint8_t ssid : 3;
+ uint16_t devno;
+ uint8_t vm_flags;
+ uint8_t reserved4[3];
+ uint32_t vm_parm_len;
+ uint8_t nss_name[8];
+ uint8_t vm_parm[64];
+ uint8_t reserved5[8];
+} __packed;
+
+struct ipl_parameter_block {
+ struct ipl_pl_hdr hdr;
+ union {
+ struct ipl_pb_hdr pb0_hdr;
+ struct ipl_pb0_common common;
+ struct ipl_pb0_fcp fcp;
+ struct ipl_pb0_ccw ccw;
+ char raw[PAGE_SIZE - sizeof(struct ipl_pl_hdr)];
+ };
+} __packed __aligned(PAGE_SIZE);
+
+/* IPL Report List header */
+struct ipl_rl_hdr {
+ uint32_t len;
+ uint8_t flags;
+ uint8_t reserved1[2];
+ uint8_t version;
+ uint8_t reserved2[8];
+} __packed;
+
+/* IPL Report Block header */
+struct ipl_rb_hdr {
+ uint32_t len;
+ uint8_t rbt;
+ uint8_t reserved1[11];
+} __packed;
+
+/* IPL Report Block types */
+enum ipl_rbt {
+ IPL_RBT_CERTIFICATES = 1,
+ IPL_RBT_COMPONENTS = 2,
+};
+
+/* IPL Report Block for the certificate list */
+struct ipl_rb_certificate_entry {
+ uint64_t addr;
+ uint64_t len;
+} __packed;
+
+struct ipl_rb_certificates {
+ uint32_t len;
+ uint8_t rbt;
+ uint8_t reserved1[11];
+ struct ipl_rb_certificate_entry entries[];
+} __packed;
+
+/* IPL Report Block for the component list */
+struct ipl_rb_component_entry {
+ uint64_t addr;
+ uint64_t len;
+ uint8_t flags;
+ uint8_t reserved1[5];
+ uint16_t certificate_index;
+ uint8_t reserved2[8];
+};
+
+#define IPL_RB_COMPONENT_FLAG_SIGNED 0x80
+#define IPL_RB_COMPONENT_FLAG_VERIFIED 0x40
+
+struct ipl_rb_components {
+ uint32_t len;
+ uint8_t rbt;
+ uint8_t reserved1[11];
+ struct ipl_rb_component_entry entries[];
+} __packed;
+
extern unsigned long long _parm_addr; /* address of parmline */
extern unsigned long long _initrd_addr; /* address of initrd */
extern unsigned long long _initrd_len; /* length of initrd */
extern unsigned long long _load_psw; /* load psw of kernel */
extern unsigned long long _extra_parm; /* use extra parm line mechanism? */
extern unsigned long long stage3_flags; /* flags (e.g. STAGE3_FLAG_KDUMP) */
+extern unsigned long long _image_len; /* length of kernel */
+extern unsigned long long _image_addr; /* target address of kernel */
extern void kdump_stage3();
#endif /* STAGE3_H */
diff --git a/zipl/boot/stage3.lds b/zipl/boot/stage3.lds
index 638fb25..c67bc32 100644
--- a/zipl/boot/stage3.lds
+++ b/zipl/boot/stage3.lds
@@ -1,33 +1,47 @@
/*
* Memory layout for stage 3
+ * =========================
+ *
+ * General memory layout
+ * ---------------------
+ *
+ * 0x0000-0x1fff Lowcore
+ * 0x2000-0x5fff Memory allocation (heap)
+ * 0x6000-0x8fff free
+ * 0x9000-0x9fff Stage3 parameter
+ * 0xa000-0xdfff Stage3 code + data
+ * 0xe000-0xffff Stack
*/
SECTIONS
{
. = 0x0;
- . = 0x7000;
+ . = 0x2000;
__heap_start = .;
- . = 0xa000;
+ . = 0x6000;
__heap_stop = .;
- . = 0xa000;
+
+ /* stage 3 parameter */
+ . = 0x9000;
_parm_addr = .;
- . = 0xa008;
+ . = 0x9008;
_initrd_addr = .;
- . = 0xa010;
+ . = 0x9010;
_initrd_len = .;
- . = 0xa018;
+ . = 0x9018;
_load_psw = .;
- . = 0xa020;
+ . = 0x9020;
_extra_parm = .;
- . = 0xa028;
+ . = 0x9028;
stage3_flags =.;
+ . = 0x9030;
+ _image_len = .;
+ . = 0x9038;
+ _image_addr = .;
. = 0xa000;
- .text.dummy : { *(.text.dummy) }
-
- . = 0xa050;
.text.start : { *(.text.start) }
.text : { *(.text) }
__ex_table_start = .;
@@ -35,11 +49,9 @@ SECTIONS
__ex_table_stop = .;
.eh_frame : { *(.eh_frame) }
- . = 0xc000;
__bss_start = .;
.bss : { *(.bss) }
__bss_stop = .;
.rodata : {*(.rodata) }
.data : { *(.data) }
-
}
diff --git a/zipl/include/boot.h b/zipl/include/boot.h
index f21ab59..6bd2a9b 100644
--- a/zipl/include/boot.h
+++ b/zipl/include/boot.h
@@ -248,6 +248,9 @@ struct boot_stage3_params {
uint64_t load_psw;
uint64_t extra_parm;
uint16_t flags;
+ uint16_t reserved[3];
+ uint64_t image_len;
+ uint64_t image_addr;
} __attribute__ ((packed));
#define STAGE3_FLAG_SCSI 0x0001
@@ -305,10 +308,10 @@ int boot_init_fba_stage1b(struct boot_fba_stage1b *stage1b,
disk_blockptr_t *stage2_list,
blocknum_t stage2_count);
int boot_get_eckd_stage2(void** data, size_t* size, struct job_data* job);
-size_t get_stage3_size();
-int boot_get_stage3(void** buffer, size_t* bytecount, address_t parm_addr,
- address_t initrd_addr, size_t initrd_len,
- address_t image_addr, int extra_parm, uint16_t flags);
+int boot_get_stage3_parms(void **buffer, size_t *bytecount, address_t parm_addr,
+ address_t initrd_addr, size_t initrd_len,
+ address_t load_addr, int extra_parm, uint16_t flags,
+ size_t image_len);
int boot_get_tape_ipl(void** data, size_t* size, address_t parm_addr,
address_t initrd_addr, address_t image_addr);
int boot_get_tape_dump(void** data, size_t* size, uint64_t mem);
diff --git a/zipl/include/bootmap.h b/zipl/include/bootmap.h
index c03c041..4a0bd04 100644
--- a/zipl/include/bootmap.h
+++ b/zipl/include/bootmap.h
@@ -16,6 +16,37 @@
#include "job.h"
#include "zipl.h"
+#define SIGNATURE_MAGIC "~Module signature appended~\n"
+#define PKCS7_FORMAT 0x01
+
+struct signature_header {
+ uint8_t format;
+ uint8_t reserved[3];
+ uint32_t length;
+} __attribute((packed));
+
+typedef union {
+ uint64_t load_address;
+ uint64_t load_psw;
+ struct signature_header sig_head;
+} component_data;
+
+/*
+ * The file_signature structure and the PKEY_ID definition
+ * are based on linux/scripts/sign-file.c
+ */
+struct file_signature {
+ u8 algorithm;
+ u8 hash;
+ u8 id_type;
+ u8 signer_len;
+ u8 key_id_len;
+ u8 __pad[3];
+ u32 sig_len;
+ char magic[28];
+};
+
+#define PKEY_ID_PKCS7 0x02
int bootmap_create(struct job_data* job, disk_blockptr_t* program_table,
disk_blockptr_t *scsi_dump_sb_blockptr,
diff --git a/zipl/include/job.h b/zipl/include/job.h
index 4c9253f..fce811c 100644
--- a/zipl/include/job.h
+++ b/zipl/include/job.h
@@ -121,6 +121,7 @@ struct job_data {
int add_files;
int dry_run;
int command_line;
+ int is_secure;
};
diff --git a/zipl/include/misc.h b/zipl/include/misc.h
index 37a8a87..5a349a7 100644
--- a/zipl/include/misc.h
+++ b/zipl/include/misc.h
@@ -51,6 +51,7 @@ int misc_check_readable_file(const char* filename);
int misc_check_writable_device(const char* devno, int blockdev, int chardev);
void misc_ebcdic_to_ascii(unsigned char *from, unsigned char *to);
void misc_ascii_to_ebcdic(unsigned char *from, unsigned char *to);
+unsigned int misc_check_secure_boot(void);
#define ROUNDUP(x, y) ((((x) + ((y) - 1)) / (y)) * (y))
#define DIV_ROUND_UP(n, d) (((n) + (d) - 1) / (d))
diff --git a/zipl/include/scan.h b/zipl/include/scan.h
index 0fe415c..4dcee0b 100644
--- a/zipl/include/scan.h
+++ b/zipl/include/scan.h
@@ -16,7 +16,7 @@
#define SCAN_SECTION_NUM 9
-#define SCAN_KEYWORD_NUM 21
+#define SCAN_KEYWORD_NUM 22
#define SCAN_KEYWORD_ONLY_NUM 1
#define SCAN_AUTOMENU_NAME "zipl-automatic-menu"
@@ -51,6 +51,7 @@ enum scan_keyword_id {
scan_keyword_targetoffset = 18,
scan_keyword_defaultauto = 19,
scan_keyword_kdump = 20,
+ scan_keyword_secure = 21,
};
enum scan_section_type {
diff --git a/zipl/include/zipl.h b/zipl/include/zipl.h
index 770801e..6f2d115 100644
--- a/zipl/include/zipl.h
+++ b/zipl/include/zipl.h
@@ -20,10 +20,11 @@
#define DISK_LAYOUT_ID 0x00000001
#define ZIPL_STAGE2_LOAD_ADDRESS 0x2000
-#define ZIPL_STAGE3_ENTRY_ADDRESS 0xa050LL
+#define ZIPL_STAGE3_ENTRY_ADDRESS 0xa000LL
#define DEFAULT_IMAGE_ADDRESS 0x10000LL
#define KDUMP_IMAGE_ADDRESS 0x10010LL
#define DEFAULT_STAGE3_ADDRESS 0xa000LL
+#define DEFAULT_STAGE3_PARAMS_ADDRESS 0x9000LL
#define MINIMUM_ADDRESS 0x10000LL
#define ADDRESS_LIMIT 0x80000000LL
#define ADDRESS_LIMIT_KDUMP 0x2000000UL /* HSA size: 32 MiB */
@@ -31,11 +32,15 @@
#define MAXIMUM_PARMLINE_SIZE 0x380
#define MAXIMUM_PHYSICAL_BLOCKSIZE 0x1000
+#define STAGE3_HEAP_SIZE 0x4000
+#define STAGE3_HEAP_ADDRESS 0x2000
+#define STAGE3_STACK_SIZE 0x1000
+#define STAGE3_STACK_ADDRESS 0xF000
+
#define PSW_ADDRESS_MASK 0x000000007fffffffLL
#define PSW_LOAD 0x0008000080000000LL
#define PSW_DISABLED_WAIT 0x000a000000000000LL
-#define KERNEL_HEADER_SIZE 65536
#define BOOTMAP_FILENAME "bootmap"
#define BOOTMAP_TEMPLATE_FILENAME "bootmap_temp.XXXXXX"
@@ -44,12 +49,18 @@
#define ZIPL_CONF_VAR "ZIPLCONF"
#define ZIPL_DEFAULT_CONF "/etc/zipl.conf"
#define ZIPL_DEFAULT_BLSDIR "/boot/loader/entries"
+#define ZIPL_STAGE3_PATH TOOLS_LIBDIR "/stage3.bin"
+#define ZIPL_SIPL_PATH "/sys/firmware/ipl/has_secure"
#define MENU_DEFAULT_PROMPT 0
#define MENU_DEFAULT_TIMEOUT 0
#define MAX_DUMP_VOLUMES 32
+#define SECURE_BOOT_DISABLED 0
+#define SECURE_BOOT_ENABLED 1
+#define SECURE_BOOT_AUTO 2
+
/* Internal component load address type */
typedef uint64_t address_t;
diff --git a/zipl/man/zipl.8 b/zipl/man/zipl.8
index 9891975..2f873f3 100644
--- a/zipl/man/zipl.8
+++ b/zipl/man/zipl.8
@@ -352,6 +352,21 @@ whether they contain a dump signature or not.
This option can only be used together with
.BR \-\-mvdump .
+.TP
+.BR "\-S <SWITCH>" " or " "\-\-secure <SWITCH>"
+Control the zIPL secure boot support.
+<SWITCH> can take one of three values:
+
+ auto (default)
+ Write signatures if available and supported by the system.
+ 1
+ Signatures are written independent of support indicated by the local
+ system. Also missing signatures for stage 3 and kernel IPL files
+ will result in an error.
+ 0
+ No signatures will be written.
+
+
.SH EXAMPLE
1. Scenario: prepare disk for booting a Linux kernel image using the
following parameters:
diff --git a/zipl/man/zipl.conf.5 b/zipl/man/zipl.conf.5
index d4877d8..71d1252 100644
--- a/zipl/man/zipl.conf.5
+++ b/zipl/man/zipl.conf.5
@@ -82,6 +82,8 @@ below).
.br
defaultmenu = menu1
.br
+secure = auto
+.br
[linux]
.br
@@ -517,6 +519,31 @@ An optional hexadecimal address may be provided to load the kernel to a
non-default memory location.
.PP
+.B secure
+=
+.IR auto / 1 / 0
+(configuration only)
+.IP
+.B Configuration section:
+.br
+Control the zIPL secure boot support.
+Set this option to one of the following:
+.IP " - " 12
+.BR auto:
+Write signatures if available and supported by the system.
+.IP " - " 12
+.BR 1:
+Signatures are written independent of support indicated by the local system.
+Also missing signatures for stage 3 and kernel IPL files will result in an error.
+.IP " - " 12
+.BR 0:
+No signatures will be written.
+
+The default value for
+.B 'secure'
+is auto.
+.PP
+
.B segment
=
.IR segment\-file , address
diff --git a/zipl/src/Makefile b/zipl/src/Makefile
index be14fce..bed970c 100644
--- a/zipl/src/Makefile
+++ b/zipl/src/Makefile
@@ -15,8 +15,9 @@ objects = misc.o error.o scan.o job.o boot.o bootmap.o disk.o \
zipl_helpers = $(basename $(wildcard zipl_helper.*.c))
chreipl_helpers = $(subst zipl_,chreipl_, $(zipl_helpers))
+zipl_stage3 = ../boot/stage3.bin
-all: zipl $(chreipl_helpers)
+all: zipl $(chreipl_helpers) $(zipl_stage3)
zipl: $(objects) $(libs)
@@ -33,6 +34,7 @@ install: all
$(INSTALL) -m 755 $(zipl_helpers) $(chreipl_helpers) \
$(DESTDIR)$(TOOLS_LIBDIR)
$(CP) --no-dereference $(chreipl_helpers) $(DESTDIR)$(TOOLS_LIBDIR)
+ $(CP) --no-dereference $(zipl_stage3) $(DESTDIR)$(TOOLS_LIBDIR)
clean:
rm -f *.o $(zipl_helpers) $(chreipl_helpers) zipl
@@ -46,3 +48,6 @@ clean:
../boot/data.o:
make -C ../boot data.o
+
+../boot/stage3.bin:
+ make -C ../boot stage3.bin
diff --git a/zipl/src/boot.c b/zipl/src/boot.c
index 279d246..b13bcb7 100644
--- a/zipl/src/boot.c
+++ b/zipl/src/boot.c
@@ -14,6 +14,8 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
+#include <fcntl.h>
+#include <sys/stat.h>
#include "../boot/data.h"
#include "boot.h"
@@ -68,20 +70,17 @@ boot_check_data(void)
return 0;
}
-/* Export stage 3 size for partition dump with dump kernel */
-size_t
-get_stage3_size()
-{
- return DATA_SIZE(stage3);
-}
-
-/* Create a stage 3 loader in memory.
+/*
+ * Create a stage 3 parameter block in memory.
* Upon success, return 0 and set BUFFER to point to the data buffer and set
- * BYTECOUNT to contain the loader size in bytes. Return non-zero otherwise. */
+ * BYTECOUNT to contain the parameter block size in bytes.
+ * Return non-zero otherwise.
+ */
int
-boot_get_stage3(void** buffer, size_t* bytecount, address_t parm_addr,
- address_t initrd_addr, size_t initrd_len, address_t image_addr,
- int extra_parm, uint16_t flags)
+boot_get_stage3_parms(void **buffer, size_t *bytecount, address_t parm_addr,
+ address_t initrd_addr, size_t initrd_len,
+ address_t image_addr, int extra_parm, uint16_t flags,
+ size_t image_len)
{
struct boot_stage3_params params;
void* data;
@@ -92,9 +91,10 @@ boot_get_stage3(void** buffer, size_t* bytecount, address_t parm_addr,
return -1;
}
/* Get memory */
- data = misc_malloc(DATA_SIZE(stage3));
+ data = misc_malloc(sizeof(params));
if (data == NULL)
return -1;
+ memset(data, 0, sizeof(params));
/* Prepare params section */
params.parm_addr = (uint64_t) parm_addr;
params.initrd_addr = (uint64_t) initrd_addr;
@@ -102,11 +102,12 @@ boot_get_stage3(void** buffer, size_t* bytecount, address_t parm_addr,
params.load_psw = (uint64_t)(image_addr | PSW_LOAD);
params.extra_parm = (uint64_t) extra_parm;
params.flags = flags;
+ params.image_len = (uint64_t) image_len;
+ params.image_addr = (uint64_t) image_addr;
/* Initialize buffer */
- memcpy(data, DATA_ADDR(stage3), DATA_SIZE(stage3));
- memcpy(data, &params, sizeof(struct boot_stage3_params));
+ memcpy(data, &params, sizeof(params));
*buffer = data;
- *bytecount = DATA_SIZE(stage3);
+ *bytecount = sizeof(params);
return 0;
}
diff --git a/zipl/src/bootmap.c b/zipl/src/bootmap.c
index 1b71b03..456c2ef 100644
--- a/zipl/src/bootmap.c
+++ b/zipl/src/bootmap.c
@@ -19,6 +19,7 @@
#include <sys/types.h>
#include "lib/util_part.h"
+#include "lib/util_path.h"
#include "boot.h"
#include "bootmap.h"
@@ -117,6 +118,22 @@ check_menu_positions(struct job_menu_data* menu, char* name,
return 0;
}
+static bool
+check_secure_boot_support(void)
+{
+ unsigned int val;
+ FILE *fp;
+
+ fp = fopen(ZIPL_SIPL_PATH, "r");
+ if (!fp)
+ return false;
+
+ fscanf(fp, "%d", &val);
+ fclose(fp);
+
+ return val ? true : false;
+}
+
/* Write COUNT elements of the blocklist specified by LIST as a linked list
* of segment table blocks to the file identified by file descriptor FD. Upon
@@ -199,24 +216,21 @@ add_program_table(int fd, disk_blockptr_t* table, int entries,
return rc;
}
-
struct component_entry {
uint8_t data[23];
uint8_t type;
- union {
- uint64_t load_address;
- uint64_t load_psw;
- } address;
+ component_data compdat;
} __attribute((packed));
typedef enum {
component_execute = 0x01,
- component_load = 0x02
+ component_load = 0x02,
+ component_signature = 0x03
} component_type;
static void
create_component_entry(void* buffer, disk_blockptr_t* pointer,
- component_type type, uint64_t address,
+ component_type type, component_data data,
struct disk_info* info)
{
struct component_entry* entry;
@@ -228,10 +242,15 @@ create_component_entry(void* buffer, disk_blockptr_t* pointer,
case component_load:
bootmap_store_blockptr(&entry->data, pointer,
info);
- entry->address.load_address = address;
+ entry->compdat.load_address = data.load_address;
break;
case component_execute:
- entry->address.load_psw = address;
+ entry->compdat.load_psw = data.load_psw;
+ break;
+ case component_signature:
+ bootmap_store_blockptr(&entry->data, pointer,
+ info);
+ entry->compdat.sig_head = data.sig_head;
break;
}
}
@@ -267,7 +286,7 @@ struct component_loc {
static int
add_component_file(int fd, const char* filename, address_t load_address,
- off_t offset, void* component, int add_files,
+ size_t trailer, void *component, int add_files,
struct disk_info* info, struct job_target_data* target,
struct component_loc *location)
{
@@ -279,8 +298,6 @@ add_component_file(int fd, const char* filename, address_t load_address,
size_t size;
blocknum_t count;
int rc;
- int from;
- unsigned int to;
if (add_files) {
/* Read file to buffer */
@@ -289,17 +306,10 @@ add_component_file(int fd, const char* filename, address_t load_address,
error_text("Could not read file '%s'", filename);
return rc;
}
- /* Ensure minimum size */
- if (size <= (size_t) offset) {
- error_reason("File '%s' is too small (has to be "
- "greater than %ld bytes)", filename,
- (long) offset);
- free(buffer);
- return -1;
- }
+ size -= trailer;
/* Write buffer */
- count = disk_write_block_buffer(fd, 0, buffer + offset,
- size - offset, &list, info);
+ count = disk_write_block_buffer(fd, 0, buffer,
+ size, &list, info);
free(buffer);
if (count == 0) {
error_text("Could not write to bootmap file");
@@ -321,20 +331,7 @@ add_component_file(int fd, const char* filename, address_t load_address,
disk_free_info(file_info);
if (count == 0)
return -1;
- if (count * info->phy_block_size <= (size_t) offset) {
- error_reason("File '%s' is too small (has to be "
- "greater than %ld bytes)", filename,
- (long) offset);
- free(list);
- return -1;
- }
- if (offset > 0) {
- /* Shorten list by offset */
- from = offset / info->phy_block_size;
- count -= from;
- for (to=0; to < count; to++, from++)
- list[to] = list[from];
- }
+ count -= DIV_ROUND_UP(trailer, info->phy_block_size);
}
/* Fill in component location */
loc.addr = load_address;
@@ -346,7 +343,7 @@ add_component_file(int fd, const char* filename, address_t load_address,
free(list);
if (rc == 0) {
create_component_entry(component, &segment, component_load,
- load_address, info);
+ (component_data) load_address, info);
/* Return location if requested */
if (location != NULL)
*location = loc;
@@ -354,11 +351,10 @@ add_component_file(int fd, const char* filename, address_t load_address,
return rc;
}
-
static int
-add_component_buffer(int fd, void* buffer, size_t size, address_t load_address,
+add_component_buffer(int fd, void* buffer, size_t size, component_data data,
void* component, struct disk_info* info,
- struct component_loc *location)
+ struct component_loc *location, int type)
{
struct component_loc loc;
disk_blockptr_t segment;
@@ -372,17 +368,21 @@ add_component_buffer(int fd, void* buffer, size_t size, address_t load_address,
error_text("Could not write to bootmap file");
return -1;
}
- /* Fill in component location */
- loc.addr = load_address;
- loc.size = count * info->phy_block_size;
+ if (type == component_load) {
+ /* Fill in component location */
+ loc.addr = data.load_address;
+ loc.size = count * info->phy_block_size;
+ } else {
+ loc.addr = 0;
+ loc.size = 0;
+ }
/* Try to compact list */
count = disk_compact_blocklist(list, count, info);
/* Write segment table */
rc = add_segment_table(fd, list, count, &segment, info);
free(list);
if (rc == 0) {
- create_component_entry(component, &segment, component_load,
- load_address, info);
+ create_component_entry(component, &segment, type, data, info);
/* Return location if requested */
if (location != NULL)
*location = loc;
@@ -391,6 +391,30 @@ add_component_buffer(int fd, void* buffer, size_t size, address_t load_address,
}
+static int
+add_dummy_buffer(int fd, size_t size, address_t addr, void *component,
+ struct disk_info *info, struct component_loc *comp_loc)
+{
+ char *buffer;
+ int rc;
+
+ buffer = misc_malloc(size);
+ if (buffer == NULL)
+ return -1;
+
+ memset(buffer, 0, size);
+ rc = add_component_buffer(fd, buffer, size,
+ (component_data) (uint64_t) addr,
+ component, info, comp_loc, component_load);
+ if (rc) {
+ free(buffer);
+ return rc;
+ }
+ free(buffer);
+ return 0;
+}
+
+
static void
print_components(const char *name[], struct component_loc *loc, int num)
{
@@ -409,23 +433,86 @@ print_components(const char *name[], struct component_loc *loc, int num)
}
}
+static int
+extract_signature(char *filename, void **ret_signature,
+ struct signature_header *sig_head)
+{
+ struct file_signature *file_sig;
+ size_t signature_size = 0;
+ void *signature;
+ char *buffer;
+ size_t size;
+
+ if (misc_read_file(filename, &buffer, &size, 0))
+ return 0;
+
+ file_sig = (void *) buffer + size - sizeof(*file_sig);
+ if (memcmp(file_sig->magic, SIGNATURE_MAGIC, sizeof(file_sig->magic))
+ != 0)
+ goto out;
+
+ signature = misc_malloc(file_sig->sig_len);
+ if (signature == NULL)
+ goto out;
+ signature_size = file_sig->sig_len;
+
+ memcpy(signature, buffer + size - signature_size - sizeof(*file_sig),
+ signature_size);
+
+ *ret_signature = signature;
+ sig_head->length = signature_size;
+
+ switch (file_sig->id_type) {
+ case PKEY_ID_PKCS7:
+ sig_head->format = PKCS7_FORMAT;
+ break;
+ default:
+ error_text("Unsupported signature type %02x",
+ file_sig->id_type);
+ signature_size = 0;
+ goto out;
+ }
+ /* return size of signature and corresponding header */
+ signature_size += sizeof(*file_sig);
+out:
+ free(buffer);
+ return signature_size;
+}
+
+static void
+check_remaining_filesize(size_t filesize, size_t signature_size,
+ struct disk_info *info, char *filename)
+{
+ if ((filesize - signature_size) % info->phy_block_size) {
+ fprintf(stderr,
+ "Warning: Size of signed file %s is not a multiple of the disk block size\n",
+ filename);
+ }
+}
static int
add_ipl_program(int fd, struct job_ipl_data* ipl, disk_blockptr_t* program,
int verbose, int add_files, component_header_type type,
- struct disk_info* info, struct job_target_data* target)
+ struct disk_info* info, struct job_target_data* target,
+ int is_secure)
{
+ struct component_loc comp_loc[10];
+ struct signature_header sig_head;
+ size_t ramdisk_size, image_size;
+ bool secure_boot_supported;
+ size_t stage3_params_size;
+ const char *comp_name[10];
+ size_t signature_size;
+ int offset, flags = 0;
+ void *stage3_params;
struct stat stats;
- void* table;
- void* stage3;
- size_t stage3_size;
- const char *comp_name[4] = {"kernel image", "parmline",
- "initial ramdisk", "internal loader"};
- struct component_loc comp_loc[4];
+ void *signature;
+ int comp_nr = 0;
+ void *table;
int rc;
- int offset, flags = 0;
memset(comp_loc, 0, sizeof(comp_loc));
+ memset(&sig_head, 0, sizeof(sig_head));
table = misc_malloc(info->phy_block_size);
if (table == NULL)
return -1;
@@ -456,55 +543,189 @@ add_ipl_program(int fd, struct job_ipl_data* ipl, disk_blockptr_t* program,
return -1;
}
}
- if (info->type == disk_type_scsi)
+ ramdisk_size = stats.st_size;
+ if (info->type == disk_type_scsi) {
flags |= STAGE3_FLAG_SCSI;
+ /*
+ * Add dummy components for stage 3 heap and stack to block the
+ * associated memory areas against firmware use.
+ */
+ rc = add_dummy_buffer(fd, STAGE3_HEAP_SIZE, STAGE3_HEAP_ADDRESS,
+ VOID_ADD(table, offset), info,
+ &comp_loc[comp_nr]);
+ if (rc) {
+ error_text("Could not add stage3 HEAP dummy");
+ free(table);
+ return rc;
+ }
+ comp_name[comp_nr] = "heap area";
+ offset += sizeof(struct component_entry);
+ comp_nr++;
+ rc = add_dummy_buffer(fd, STAGE3_STACK_SIZE,
+ STAGE3_STACK_ADDRESS,
+ VOID_ADD(table, offset), info,
+ &comp_loc[comp_nr]);
+ if (rc) {
+ error_text("Could not add stage3 STACK dummy");
+ free(table);
+ return rc;
+ }
+ comp_name[comp_nr] = "stack area";
+ offset += sizeof(struct component_entry);
+ comp_nr++;
+ }
if (ipl->is_kdump)
flags |= STAGE3_FLAG_KDUMP;
+ /* Get kernel file size */
+ if (stat(ipl->image, &stats)) {
+ error_reason(strerror(errno));
+ error_text("Could not get information for file '%s'",
+ ipl->image);
+ free(table);
+ return -1;
+ }
+ image_size = stats.st_size;
+ secure_boot_supported = check_secure_boot_support();
+ signature_size = extract_signature(ZIPL_STAGE3_PATH, &signature,
+ &sig_head);
+ if (signature_size &&
+ (is_secure == SECURE_BOOT_ENABLED ||
+ (is_secure == SECURE_BOOT_AUTO && secure_boot_supported))) {
+ if (verbose)
+ printf(" signature for.....: %s\n", ZIPL_STAGE3_PATH);
+
+ rc = add_component_buffer(fd, signature, sig_head.length,
+ (component_data)sig_head,
+ VOID_ADD(table, offset), info,
+ &comp_loc[comp_nr],
+ component_signature);
+ if (rc) {
+ error_text("Could not add stage3 signature");
+ free(table);
+ return rc;
+ }
+ comp_name[comp_nr] = "loader signature";
+ offset += sizeof(struct component_entry);
+ comp_nr++;
+ free(signature);
+ } else if (is_secure == SECURE_BOOT_ENABLED) {
+ /*
+ * If secure boot is forced and we have failed to extract a
+ * signature for the stage 3 loader zipl will abort with an
+ * error message
+ */
+ error_text("Could not install Secure Boot IPL records");
+ error_reason("Missing signature in internal loader file %s",
+ ZIPL_STAGE3_PATH);
+ free(table);
+ return -1;
+ }
+
/* Add stage 3 loader to bootmap */
- rc = boot_get_stage3(&stage3, &stage3_size, ipl->parm_addr,
- ipl->ramdisk_addr, (size_t) stats.st_size,
- ipl->is_kdump ? ipl->image_addr + 0x10 :
- ipl->image_addr,
- (info->type == disk_type_scsi) ? 0 : 1,
- flags);
+ rc = add_component_file(fd, ZIPL_STAGE3_PATH, DEFAULT_STAGE3_ADDRESS,
+ signature_size, VOID_ADD(table, offset), 1,
+ info, target, &comp_loc[comp_nr]);
if (rc) {
+ error_text("Could not add internal loader file '%s'",
+ ZIPL_STAGE3_PATH);
free(table);
return rc;
}
- rc = add_component_buffer(fd, stage3, stage3_size,
- DEFAULT_STAGE3_ADDRESS,
- VOID_ADD(table, offset), info, &comp_loc[3]);
- free(stage3);
+ offset += sizeof(struct component_entry);
+ comp_name[comp_nr] = "internal loader";
+ comp_nr++;
+
+ /* Add stage 3 parameter to bootmap */
+ rc = boot_get_stage3_parms(&stage3_params, &stage3_params_size,
+ ipl->parm_addr, ipl->ramdisk_addr,
+ ramdisk_size,
+ ipl->is_kdump ? ipl->image_addr + 0x10 :
+ ipl->image_addr,
+ (info->type == disk_type_scsi) ? 0 : 1,
+ flags, image_size);
if (rc) {
- error_text("Could not add stage 3 boot loader");
+ free(table);
+ return rc;
+ }
+ rc = add_component_buffer(fd, stage3_params, stage3_params_size,
+ (component_data) (uint64_t)
+ DEFAULT_STAGE3_PARAMS_ADDRESS,
+ VOID_ADD(table, offset), info,
+ &comp_loc[comp_nr], component_load);
+ free(stage3_params);
+ if (rc) {
+ error_text("Could not add parameters");
free(table);
return -1;
}
offset += sizeof(struct component_entry);
+ comp_name[comp_nr] = "parameters";
+ comp_nr++;
+
/* Add kernel image */
if (verbose) {
printf(" kernel image......: %s\n", ipl->image);
}
+ signature_size = extract_signature(ipl->image, &signature, &sig_head);
+ if (signature_size &&
+ (is_secure == SECURE_BOOT_ENABLED ||
+ (is_secure == SECURE_BOOT_AUTO && secure_boot_supported))) {
+ if (verbose)
+ printf(" signature for.....: %s\n", ipl->image);
+
+ rc = add_component_buffer(fd, signature, sig_head.length,
+ (component_data)sig_head,
+ VOID_ADD(table, offset), info,
+ &comp_loc[comp_nr],
+ component_signature);
+ if (rc) {
+ error_text("Could not add image signature");
+ free(table);
+ return rc;
+ }
+ comp_name[comp_nr] = "image signature";
+ offset += sizeof(struct component_entry);
+ comp_nr++;
+ free(signature);
+ check_remaining_filesize(image_size, signature_size, info,
+ ipl->image);
+ } else if (is_secure == SECURE_BOOT_ENABLED) {
+ /*
+ * If secure boot is forced and we have failed to extract a
+ * signature for the kernel image zipl will abort with an
+ * error message
+ */
+ error_text("Could not install Secure Boot IPL records");
+ error_reason("Missing signature in image file %s",
+ ipl->image);
+ free(table);
+ return -1;
+ }
+
rc = add_component_file(fd, ipl->image, ipl->image_addr,
- KERNEL_HEADER_SIZE, VOID_ADD(table, offset),
- add_files, info, target, &comp_loc[0]);
+ signature_size, VOID_ADD(table, offset),
+ add_files, info, target, &comp_loc[comp_nr]);
if (rc) {
error_text("Could not add image file '%s'", ipl->image);
free(table);
return rc;
}
offset += sizeof(struct component_entry);
+ comp_name[comp_nr] = "kernel image";
+ comp_nr++;
+
+ /* Add kernel parmline */
if (ipl->parmline != NULL) {
- /* Add kernel parmline */
if (verbose) {
printf(" kernel parmline...: '%s'\n", ipl->parmline);
}
rc = add_component_buffer(fd, ipl->parmline,
strlen(ipl->parmline) + 1,
- ipl->parm_addr,
+ (component_data) ipl->parm_addr,
VOID_ADD(table, offset),
- info, &comp_loc[1]);
+ info, &comp_loc[comp_nr],
+ component_load);
if (rc) {
error_text("Could not add parmline '%s'",
ipl->parmline);
@@ -512,14 +733,45 @@ add_ipl_program(int fd, struct job_ipl_data* ipl, disk_blockptr_t* program,
return -1;
}
offset += sizeof(struct component_entry);
+ comp_name[comp_nr] = "parmline";
+ comp_nr++;
}
/* finally add ramdisk */
if (ipl->ramdisk != NULL) {
+ signature_size = extract_signature(ipl->ramdisk, &signature,
+ &sig_head);
+ if (signature_size &&
+ (is_secure == SECURE_BOOT_ENABLED ||
+ (is_secure == SECURE_BOOT_AUTO &&
+ secure_boot_supported))) {
+ if (verbose) {
+ printf(" signature for.....: %s\n",
+ ipl->ramdisk);
+ }
+ rc = add_component_buffer(fd, signature,
+ sig_head.length,
+ (component_data)sig_head,
+ VOID_ADD(table, offset), info,
+ &comp_loc[comp_nr],
+ component_signature);
+ if (rc) {
+ error_text("Could not add ramdisk signature");
+ free(table);
+ return rc;
+ }
+ comp_name[comp_nr] = "ramdisk signature";
+ offset += sizeof(struct component_entry);
+ comp_nr++;
+ free(signature);
+ check_remaining_filesize(ramdisk_size, signature_size,
+ info, ipl->ramdisk);
+ }
rc = add_component_file(fd, ipl->ramdisk,
- ipl->ramdisk_addr, 0,
+ ipl->ramdisk_addr, signature_size,
VOID_ADD(table, offset),
- add_files, info, target, &comp_loc[2]);
+ add_files, info, target,
+ &comp_loc[comp_nr]);
if (rc) {
error_text("Could not add ramdisk '%s'",
ipl->ramdisk);
@@ -527,13 +779,16 @@ add_ipl_program(int fd, struct job_ipl_data* ipl, disk_blockptr_t* program,
return -1;
}
offset += sizeof(struct component_entry);
+ comp_name[comp_nr] = "initial ramdisk";
+ comp_nr++;
}
if (verbose)
- print_components(comp_name, comp_loc, 4);
+ print_components(comp_name, comp_loc, comp_nr);
/* Terminate component table */
create_component_entry(VOID_ADD(table, offset), NULL,
component_execute,
- ZIPL_STAGE3_ENTRY_ADDRESS | PSW_LOAD,
+ (component_data) (uint64_t)
+ (ZIPL_STAGE3_ENTRY_ADDRESS | PSW_LOAD),
info);
/* Write component table */
rc = disk_write_block_aligned(fd, table, info->phy_block_size,
@@ -584,7 +839,8 @@ if (rc) {
print_components(comp_name, comp_loc, 1);
/* Terminate component table */
create_component_entry(VOID_ADD(table, offset), NULL,
- component_execute, PSW_DISABLED_WAIT, info);
+ component_execute, (component_data) (uint64_t)
+ PSW_DISABLED_WAIT, info);
/* Write component table */
rc = disk_write_block_aligned(fd, table, info->phy_block_size,
program, info);
@@ -674,7 +930,7 @@ add_dump_program(int fd, struct job_dump_data* dump,
return rc;
ipl.parm_addr = dump->parm_addr;
return add_ipl_program(fd, &ipl, program, verbose, 1,
- type, info, target);
+ type, info, target, SECURE_BOOT_DISABLED);
}
@@ -711,7 +967,7 @@ build_program_table(int fd, struct job_data* job, disk_blockptr_t* pointer,
rc = add_ipl_program(fd, &job->data.ipl, &table[0],
verbose || job->command_line,
job->add_files, component_header,
- info, &job->target);
+ info, &job->target, job->is_secure);
break;
case job_segment:
if (job->command_line)
@@ -762,7 +1018,8 @@ build_program_table(int fd, struct job_data* job, disk_blockptr_t* pointer,
&table[job->data.menu.entry[i].pos],
verbose || job->command_line,
job->add_files, component_header,
- info, &job->target);
+ info, &job->target,
+ job->is_secure);
break;
case job_print_usage:
case job_print_version:
@@ -913,7 +1170,9 @@ bootmap_create(struct job_data *job, disk_blockptr_t *program_table,
ulong size;
ulong unused_size;
- size = DIV_ROUND_UP(get_stage3_size(), info->phy_block_size);
+ /* Use approximated stage 3 size as starting point */
+ size = MINIMUM_ADDRESS;
+
/* Ramdisk */
if (job->data.dump.ramdisk != NULL) {
if (stat(job->data.dump.ramdisk, &st))
diff --git a/zipl/src/job.c b/zipl/src/job.c
index 22d2549..2d8de8f 100644
--- a/zipl/src/job.c
+++ b/zipl/src/job.c
@@ -52,11 +52,12 @@ static struct option options[] = {
{ "dry-run", no_argument, NULL, '0'},
{ "force", no_argument, NULL, 'f'},
{ "kdump", required_argument, NULL, 'k'},
+ { "secure", required_argument, NULL, 'S'},
{ NULL, 0, NULL, 0 }
};
/* Command line option abbreviations */
-static const char option_string[] = "-c:b:t:i:r:p:P:d:D:M:s:m:hHnVvaT:fk:";
+static const char option_string[] = "-c:b:t:i:r:p:P:d:D:M:s:S:m:hHnVvaT:fk:";
struct command_line {
char* data[SCAN_KEYWORD_NUM];
@@ -215,6 +216,11 @@ get_command_line(int argc, char* argv[], struct command_line* line)
} else
cmdline.menu = optarg;
break;
+ case 'S':
+ is_keyword = 1;
+ rc = store_option(&cmdline, scan_keyword_secure,
+ optarg);
+ break;
case 'h':
cmdline.help = 1;
break;
@@ -511,7 +517,7 @@ get_ipl_components(struct job_ipl_data *ipl, struct component_loc **clp,
/* Fill in component data */
num = 0;
rc = set_cl_element(&cl[num++], "kernel image", ipl->image,
- &ipl->image_addr, 0, 0x10000,
+ &ipl->image_addr, 0, 0,
MAXIMUM_PHYSICAL_BLOCKSIZE);
if (rc)
goto error;
@@ -1263,6 +1269,26 @@ type_from_target(char *target, disk_type_t *type)
}
}
+static int
+set_secure_ipl(char *keyword, struct job_data *job)
+{
+ if (strcmp(keyword, "auto") == 0) {
+ job->is_secure = SECURE_BOOT_AUTO;
+ } else if (strcmp(keyword, "0") == 0) {
+ job->is_secure = SECURE_BOOT_DISABLED;
+ } else if (strcmp(keyword, "1") == 0) {
+ if (job->target.targettype != disk_type_scsi) {
+ error_reason("Secure boot forced for non-SCSI disk type");
+ return -1;
+ }
+ job->is_secure = SECURE_BOOT_ENABLED;
+ } else {
+ error_reason("Invalid secure boot setting '%s'",
+ keyword);
+ return -1;
+ }
+ return 0;
+}
static int
get_job_from_section_data(char* data[], struct job_data* job, char* section)
@@ -1345,6 +1371,13 @@ get_job_from_section_data(char* data[], struct job_data* job, char* section)
return -1;
}
}
+ /* Fill in secure boot */
+ if (data[(int) scan_keyword_secure] != NULL) {
+ rc = set_secure_ipl(data[(int) scan_keyword_secure],
+ job);
+ if (rc)
+ return rc;
+ }
break;
case section_ipl_tape:
/* Tape IPL job */
@@ -1502,6 +1535,13 @@ get_menu_job(struct scan_token* scan, char* menu, struct job_data* job)
job->data.menu.timeout =
atol(scan[i].content.keyword.value);
break;
+ case scan_keyword_secure:
+ rc = set_secure_ipl(
+ scan[i].content.keyword.value,
+ job);
+ if (rc)
+ return rc;
+ break;
case scan_keyword_target:
job->target.bootmap_dir = misc_strdup(
scan[i].content.keyword.value);
diff --git a/zipl/src/misc.c b/zipl/src/misc.c
index fd16810..057c9a0 100644
--- a/zipl/src/misc.c
+++ b/zipl/src/misc.c
@@ -22,7 +22,6 @@
#include "error.h"
#include "misc.h"
-
/* Allocate SIZE bytes of memory. Upon success, return pointer to memory.
* Return NULL otherwise. */
void *
diff --git a/zipl/src/scan.c b/zipl/src/scan.c
index fe72e9a..e57361e 100644
--- a/zipl/src/scan.c
+++ b/zipl/src/scan.c
@@ -45,45 +45,45 @@ enum scan_key_state scan_key_table[SCAN_SECTION_NUM][SCAN_KEYWORD_NUM] = {
* ult to tofs e mete file isk ent et pt out ultm dump
* rs enu
*
- * targ targ targ targ targ defa kdum
- * etba etty etge etbl etof ulta p
+ * targ targ targ targ targ defa kdum secu
+ * etba etty etge etbl etof ulta p re
* se pe omet ocks fset uto
* ry ize
*/
/* default auto */
{opt, inv, inv, inv, inv, inv, inv, inv, req, opt, opt, inv, inv, inv,
- opt, opt, opt, opt, opt, opt, inv},
+ opt, opt, opt, opt, opt, opt, inv, opt},
/* default menu */
{inv, inv, inv, inv, inv, inv, inv, inv, inv, inv, inv, req, inv, inv,
- inv, inv, inv, inv, inv, inv, inv},
+ inv, inv, inv, inv, inv, inv, inv, opt},
/* default section */
{req, inv, inv, inv, inv, inv, inv, inv, inv, inv, inv, inv, inv, inv,
- inv, inv, inv, inv, inv, inv, inv},
+ inv, inv, inv, inv, inv, inv, inv, opt},
/* ipl */
{inv, inv, inv, req, opt, opt, opt, inv, req, inv, inv, inv, inv, inv,
- opt, opt, opt, opt, opt, inv, opt},
+ opt, opt, opt, opt, opt, inv, opt, opt},
/* segment load */
{inv, inv, inv, inv, inv, inv, inv, req, req, inv, inv, inv, inv, inv,
- inv, inv, inv, inv, inv, inv, inv},
+ inv, inv, inv, inv, inv, inv, inv, inv},
/* part dump */
{inv, req, inv, inv, inv, inv, inv, inv, opt, inv, inv, inv, inv, inv,
- inv, inv, inv, inv, inv, inv, inv},
+ inv, inv, inv, inv, inv, inv, inv, inv},
/* fs dump */
{inv, inv, req, inv, opt, opt, inv, inv, req, inv, inv, inv, inv, inv,
- inv, inv, inv, inv, inv, inv, inv},
+ inv, inv, inv, inv, inv, inv, inv, inv},
/* ipl tape */
{inv, inv, inv, req, opt, opt, opt, inv, inv, inv, inv, inv, req, inv,
- inv, inv, inv, inv, inv, inv, inv},
+ inv, inv, inv, inv, inv, inv, inv, inv},
/* multi volume dump */
{inv, inv, inv, inv, inv, inv, inv, inv, inv, inv, inv, inv, inv, req,
- inv, inv, inv, inv, inv, inv, inv}
+ inv, inv, inv, inv, inv, inv, inv, inv}
};
/* Determines which keyword may be present in a menu section */
enum scan_key_state scan_menu_key_table[SCAN_KEYWORD_NUM] = {
/* menu section */
opt, inv, inv, inv, inv, inv, inv, inv, req, opt, opt, inv, inv, inv,
- opt, opt, opt, opt, opt, inv, inv
+ opt, opt, opt, opt, opt, inv, inv, opt
};
/* Mapping of keyword IDs to strings */
@@ -111,6 +111,7 @@ static const struct {
{ "timeout", scan_keyword_timeout},
{ "tape", scan_keyword_tape},
{ "kdump", scan_keyword_kdump},
+ { "secure", scan_keyword_secure},
};
/* List of keywords that are used without an assignment */
@@ -1863,6 +1864,7 @@ scan_build_automenu(struct scan_token* scan)
/* defaultmenu */ 1 +
/* menu heading */ 1 +
/* keyword default,prompt,timeout */ 3 +
+ /* keyword secure */ 1 +
/* target keywords*/ num_targets +
/* missing target definitions */ num_sections * num_targets +
/* number assigment */ num_sections;
@@ -1978,6 +1980,14 @@ scan_build_automenu(struct scan_token* scan)
db_keyword[i]))
goto err;
}
+ /* secure= */
+ i = (int) scan_keyword_secure;
+ if (db_keyword[i]) {
+ if (scan_append_keyword_assignment(new_scan, &i_new,
+ scan_keyword_secure,
+ db_keyword[i]))
+ goto err;
+ }
/* target= */
/* targetbase= */
/* targetgeometry= */
--
2.21.3
From 100c89a273fb4bcc1deb93aaf77ac9264d9ac3ee Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Dan=20Hor=C3=A1k?= <dan@danny.cz>
Date: Thu, 2 May 2019 15:46:39 +0200
Subject: [PATCH 19/44] zipl: update stage3 objcopy command for gcc9 (#1659401)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
The objcopy command for stage3.bin needs to take the new rodata section into account.
See also PR #60.
Signed-off-by: Dan Horák <dan@danny.cz>
---
zipl/boot/Makefile | 1 +
1 file changed, 1 insertion(+)
diff --git a/zipl/boot/Makefile b/zipl/boot/Makefile
index da7e95f..a049797 100644
--- a/zipl/boot/Makefile
+++ b/zipl/boot/Makefile
@@ -85,6 +85,7 @@ stage3.bin: stage3.exec
--only-section=.ex_table \
--only-section=.data \
--only-section=.rodata.str1.2 \
+ --only-section=.rodata.cst8 \
--only-section=.rodata \
--only-section=.stage2dump.tail \
--only-section=.eckd2dump_mv.tail \
--
2.21.3
From 78f0479055e025ba849766aa2d60c22a7bcdbfba Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Dan=20Hor=C3=A1k?= <dan@danny.cz>
Date: Tue, 21 May 2019 13:49:09 +0200
Subject: [PATCH 20/44] s390-tools: Add zcryptstats tool (#1658756)
Description: The zcryptstats tool displays usage statistics of
IBM Crypto Express adapters. It obtains cryptographic
performance measurement data periodically and displays
the data for each cryptographic device for each
interval. A cryptographic device can be either a card
device or queue device (APQN).
Upstream-ID: 5f2ddad6a83e04f6a64ea39619148c80e56c0814
Upstream-ID: b165500b6987f3fcbbb6daee6d84d66af7fbd41f
Upstream-ID: 58189b878697fef3bf405fdd864bf40f2149ccc0
Upstream-ID: 7b4a05e5a36fd36ba1866047f6b4814f127ec846
Upstream-ID: 8be43a1f16923da037d5f5bffca73c433ca1231b
Upstream-ID: 43fcb694bfd2e4515ce131ea3aba2f81e8437757
---
README.md | 1 +
include/lib/util_rec.h | 3 +
libutil/util_rec.c | 89 +-
libutil/util_rec_example.c | 7 +
zconf/zcrypt/Makefile | 7 +-
zconf/zcrypt/zcryptstats.8 | 247 ++++
zconf/zcrypt/zcryptstats.c | 2418 ++++++++++++++++++++++++++++++++++++
7 files changed, 2768 insertions(+), 4 deletions(-)
create mode 100644 zconf/zcrypt/zcryptstats.8
create mode 100644 zconf/zcrypt/zcryptstats.c
diff --git a/README.md b/README.md
index 08c27d7..af1024e 100644
--- a/README.md
+++ b/README.md
@@ -129,6 +129,7 @@ Package contents
or show encryption state of attached LUNs.
- lszcrypt: Show Information about zcrypt devices and configuration.
- chzcrypt: Modify the zcrypt configuration.
+ - zcryptstats: Display usage statistics of IBM Crypto Express adapters.
- znetconf: List and configure network devices for s390 network adapters.
- cio_ignore: Query and modify the contents of the CIO device driver
blacklist.
diff --git a/include/lib/util_rec.h b/include/lib/util_rec.h
index a44461a..7a0c2f6 100644
--- a/include/lib/util_rec.h
+++ b/include/lib/util_rec.h
@@ -68,5 +68,8 @@ const char *util_rec_get(struct util_rec *rec, const char *key);
void util_rec_print_hdr(struct util_rec *rec);
void util_rec_print(struct util_rec *rec);
+void util_rec_print_separator(struct util_rec *rec);
+
+void util_rec_set_indent(struct util_rec *rec, int indent);
#endif /** LIB_UTIL_REC_H @} */
diff --git a/libutil/util_rec.c b/libutil/util_rec.c
index 621bebc..c216cf9 100644
--- a/libutil/util_rec.c
+++ b/libutil/util_rec.c
@@ -64,6 +64,7 @@ struct rec_fmt {
int argz_sep;
} csv_p;
} d;
+ int indent;
};
/*
@@ -122,9 +123,50 @@ struct util_rec *util_rec_new_wide(const char *hdr_sep)
rec->fmt.type = REC_FMT_WIDE;
rec->fmt.d.wide_p.hdr_sep = util_strdup(hdr_sep);
rec->fmt.d.wide_p.argz_sep = ',';
+ rec->fmt.indent = 0;
return rec;
}
+/*
+ * Print the indentation characters
+ */
+static inline void rec_print_indention(int indent)
+{
+ if (indent <= 0)
+ return;
+
+ printf("%*s", indent, "");
+}
+
+/*
+ * Print record separator in "wide" output format
+ */
+static void rec_print_wide_separator(struct util_rec *rec)
+{
+ const char *hdr_sep = rec->fmt.d.wide_p.hdr_sep;
+ int size = 0, field_count = 0;
+ struct util_rec_fld *fld;
+ char *buf;
+
+ if (!hdr_sep)
+ return;
+
+ util_list_iterate(rec->list, fld) {
+ if (fld->hdr) {
+ size += fld->width;
+ field_count++;
+ }
+ }
+
+ size += field_count - 1;
+ buf = util_malloc(size + 1);
+ memset(buf, (int)hdr_sep[0], size);
+ buf[size] = 0;
+ rec_print_indention(rec->fmt.indent);
+ printf("%s\n", buf);
+ free(buf);
+}
+
/*
* Print record header in "wide" output format
*/
@@ -135,6 +177,7 @@ static void rec_print_wide_hdr(struct util_rec *rec)
struct util_rec_fld *fld;
char *buf;
+ rec_print_indention(rec->fmt.indent);
util_list_iterate(rec->list, fld) {
if (col_nr)
printf(" ");
@@ -156,6 +199,7 @@ static void rec_print_wide_hdr(struct util_rec *rec)
buf = util_malloc(size + 1);
memset(buf, (int)hdr_sep[0], size);
buf[size] = 0;
+ rec_print_indention(rec->fmt.indent);
printf("%s\n", buf);
free(buf);
}
@@ -172,6 +216,7 @@ void rec_print_wide(struct util_rec *rec)
int fld_count = 0;
char *entry;
+ rec_print_indention(rec->fmt.indent);
util_list_iterate(rec->list, fld) {
if (!fld->hdr)
continue;
@@ -225,6 +270,7 @@ struct util_rec *util_rec_new_long(const char *hdr_sep, const char *col_sep,
rec->fmt.d.long_p.key_size = key_size;
rec->fmt.d.long_p.val_size = val_size;
rec->fmt.d.long_p.argz_sep = ' ';
+ rec->fmt.indent = 0;
return rec;
}
@@ -241,6 +287,7 @@ static void rec_print_long_hdr(struct util_rec *rec)
fld = rec_get_fld(rec, p->key);
util_assert(fld != NULL, "Record not found\n");
util_assert(fld->hdr != NULL, "Header for field not found\n");
+ rec_print_indention(rec->fmt.indent);
if (p->col_sep) {
printf("%-*s %s %-*s\n", p->key_size, fld->hdr,
p->col_sep, fld->width, fld->val);
@@ -255,6 +302,7 @@ static void rec_print_long_hdr(struct util_rec *rec)
buf = util_malloc(len + 1);
memset(buf, p->hdr_sep[0], len);
buf[len] = 0;
+ rec_print_indention(rec->fmt.indent);
printf("%s\n", buf);
free(buf);
}
@@ -277,19 +325,24 @@ static void rec_print_long(struct util_rec *rec)
continue;
if (!fld->val)
continue;
+ rec_print_indention(rec->fmt.indent);
item = argz_next(fld->val, fld->len, item);
if (p->col_sep) {
printf(" %-*s %s %s\n",
p->key_size - 8, fld->hdr, p->col_sep, item);
- while ((item = argz_next(fld->val, fld->len, item)))
+ while ((item = argz_next(fld->val, fld->len, item))) {
+ rec_print_indention(rec->fmt.indent);
printf(" %-*s %c %s\n",
p->key_size - 8, "", p->argz_sep, item);
+ }
} else {
printf(" %-*s %s\n",
p->key_size - 8, fld->hdr, fld->val);
- while ((item = argz_next(fld->val, fld->len, item)))
+ while ((item = argz_next(fld->val, fld->len, item))) {
+ rec_print_indention(rec->fmt.indent);
printf(" %-*s %s\n",
p->key_size - 8, "", item);
+ }
}
}
printf("\n");
@@ -320,6 +373,7 @@ struct util_rec *util_rec_new_csv(const char *col_sep)
rec->fmt.type = REC_FMT_CSV;
rec->fmt.d.csv_p.col_sep = util_strdup(col_sep);
rec->fmt.d.csv_p.argz_sep = ' ';
+ rec->fmt.indent = 0;
return rec;
}
@@ -332,6 +386,7 @@ void rec_print_csv_hdr(struct util_rec *rec)
struct util_rec_fld *fld;
int fld_count = 0;
+ rec_print_indention(rec->fmt.indent);
util_list_iterate(rec->list, fld) {
if (fld_count)
printf("%c", *col_sep);
@@ -354,6 +409,7 @@ void rec_print_csv(struct util_rec *rec)
int fld_count = 0;
char *item = NULL;
+ rec_print_indention(rec->fmt.indent);
util_list_iterate(rec->list, fld) {
item = argz_next(fld->val, fld->len, item);
if (fld_count)
@@ -470,6 +526,24 @@ void util_rec_print_hdr(struct util_rec *rec)
}
}
+/**
+ * Print record separator according to output format
+ *
+ * @param[in] rec Record pointer
+ */
+void util_rec_print_separator(struct util_rec *rec)
+{
+ switch (rec->fmt.type) {
+ case REC_FMT_WIDE:
+ rec_print_wide_separator(rec);
+ break;
+ case REC_FMT_LONG:
+ break;
+ case REC_FMT_CSV:
+ break;
+ }
+}
+
/**
* Set a field value to an argz vector
*
@@ -537,3 +611,14 @@ const char *util_rec_get(struct util_rec *rec, const char *key)
return (fld != NULL) ? fld->val : NULL;
}
+
+/**
+ * Sets the indentation of the record
+ *
+ * @param[in] rec Record pointer
+ * @param[in] indent Number of characters to indent
+ */
+void util_rec_set_indent(struct util_rec *rec, int indent)
+{
+ rec->fmt.indent = indent;
+}
diff --git a/libutil/util_rec_example.c b/libutil/util_rec_example.c
index bc38bb4..801f909 100644
--- a/libutil/util_rec_example.c
+++ b/libutil/util_rec_example.c
@@ -42,6 +42,8 @@ static void print_records(const char *format, struct util_rec *rec)
/* Print the record */
util_rec_print(rec);
}
+ /* Print a separator line (is a nop for long and csv format) */
+ util_rec_print_separator(rec);
printf("\n");
}
@@ -72,6 +74,11 @@ int main(void)
print_records("Wide format", rec);
util_rec_free(rec);
+ rec = util_rec_new_wide("-");
+ util_rec_set_indent(rec, 4);
+ print_records("Wide format with indentation", rec);
+ util_rec_free(rec);
+
rec = util_rec_new_long("-", ":", "number", 30, 20);
print_records("Long format", rec);
util_rec_free(rec);
diff --git a/zconf/zcrypt/Makefile b/zconf/zcrypt/Makefile
index d075f34..f679775 100644
--- a/zconf/zcrypt/Makefile
+++ b/zconf/zcrypt/Makefile
@@ -1,24 +1,27 @@
include ../../common.mak
-all: chzcrypt lszcrypt zcryptctl
+all: chzcrypt lszcrypt zcryptctl zcryptstats
libs = $(rootdir)/libutil/libutil.a
chzcrypt: chzcrypt.o misc.o $(libs)
lszcrypt: lszcrypt.o misc.o $(libs)
zcryptctl: zcryptctl.o misc.o $(libs)
+zcryptstats: zcryptstats.o $(libs)
install: all
$(INSTALL) -d -m 755 $(DESTDIR)$(BINDIR)
$(INSTALL) -g $(GROUP) -o $(OWNER) -m 755 chzcrypt $(DESTDIR)$(BINDIR)
$(INSTALL) -g $(GROUP) -o $(OWNER) -m 755 lszcrypt $(DESTDIR)$(BINDIR)
$(INSTALL) -g $(GROUP) -o $(OWNER) -m 755 zcryptctl $(DESTDIR)$(BINDIR)
+ $(INSTALL) -g $(GROUP) -o $(OWNER) -m 755 zcryptstats $(DESTDIR)$(BINDIR)
$(INSTALL) -d -m 755 $(DESTDIR)$(MANDIR)/man8
$(INSTALL) -m 644 -c chzcrypt.8 $(DESTDIR)$(MANDIR)/man8
$(INSTALL) -m 644 -c lszcrypt.8 $(DESTDIR)$(MANDIR)/man8
$(INSTALL) -m 644 -c zcryptctl.8 $(DESTDIR)$(MANDIR)/man8
+ $(INSTALL) -m 644 -c zcryptstats.8 $(DESTDIR)$(MANDIR)/man8
clean:
- rm -f *.o chzcrypt lszcrypt zcryptctl
+ rm -f *.o chzcrypt lszcrypt zcryptctl zcryptstats
.PHONY: all install clean
diff --git a/zconf/zcrypt/zcryptstats.8 b/zconf/zcrypt/zcryptstats.8
new file mode 100644
index 0000000..8c31c69
--- /dev/null
+++ b/zconf/zcrypt/zcryptstats.8
@@ -0,0 +1,247 @@
+.\" Copyright IBM Corp. 2019
+.\" 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 ZCRYPTSTATS 1 "January 2019" "s390-tools"
+.SH NAME
+zcryptstats \- Display usage statistics of IBM Crypto Express adapters
+.
+.
+.SH SYNOPSIS
+.B zcryptstats
+.RI [ OPTIONS ]
+.RI [ DEVICE_ID
+[...] ]
+.
+.PP
+.B zcryptstats
+.BR \-\-help | \-h
+.br
+.B zcryptstats
+.BR \-\-version | \-v
+.
+.
+.
+.SH DESCRIPTION
+.
+Use \fBzcryptstats\fP to display usage statistics of IBM Crypto Express
+adapters.
+.PP
+\fBzcryptstats\fP obtains cryptographic performance measurement data
+periodically and displays the data for each cryptographic device for each
+interval.
+A cryptographic device can be either a card device or a queue device (APQN).
+\fBzcryptstats\fP runs forever unless you limit the number of intervals with
+the \fB\-\-count\fP option. The default interval time is 10 seconds.
+Use the \fB\-\-interval\fP option to specify a different interval time.
+.PP
+By default, all available cryptographic devices are monitored.
+You can optionally specify the device IDs of the devices to be monitored.
+The card device representation and the queue device are both in hexadecimal
+notation.
+.PP
+Use the \fB\-\-no-apqn\fP option to omit the performance measurement data of
+the queues. If the system does not support obtaining cryptographic
+performance measurement data on the queue devices, only the card devices
+are monitored.
+.PP
+For each device, a set of counters is displayed. The amount and meaning of the
+counters are dependent on the device type and mode, see the COUNTERS section.
+For each counter and interval, the following values are displayed:
+.RS 2
+.IP "\(bu" 2
+Number of measured operations.
+.IP "\(bu" 2
+Rate of the measured operation in operations per second.
+.IP "\(bu" 2
+Utilization of the device in percent.
+.IP "\(bu" 2
+Average duration of the operations.
+.RE
+.PP
+The sum of all operations is displayed in a separate \fBtotals\fP line.
+Use the \fB\-\-only-totals\fP option to omit the individual counters and
+display the totals only. Use the \fB\-\-no\-totals\fP option to omit the
+totals.
+.PP
+
+.B Note:
+The utilization value of a counter can exceed 100%. This value is caused by
+the parallel execution of cryptographic operations.
+.PP
+Cryptographic performance measurement data might not be available when Linux
+is running as guest under z/VM or under KVM. \fBzcryptstats\fP then displays an
+error message and exits.
+.
+.
+.
+.SH OPTIONS
+.
+.TP
+.BR DEVICE_ID
+Specifies a cryptographic device for which statistics are displayed.
+A device ID can either be a card device ID
+(\fI<card-id>\fP) or a queue device (APQN) ID (\fI<card-id>.<domain-id>\fP).
+To filter all devices by domain, provide \fI.<domain-id>\fP.
+If no IDs are given, statistics are displayed for all available devices.
+.
+.TP
+.BR \-i ", " \-\-interval\~\fIINTERVAL\fP
+Specifies the interval time in seconds. If this option is omitted, then the
+default interval time of 10 seconds is used.
+.
+.TP
+.BR \-c ", " \-\-count\~\fICOUNT\fP
+Specifies the number of reports that are generated at \fIINTERVAL\fP seconds
+apart. If this option is omitted, the \fBzcryptstats\fP command generates
+reports continuously, until it is stopped with control-C.
+.
+.TP
+.BR \-o ", " \-\-output\~\fIJSON\fP|\fITABLE\fP|\fICSV\fP
+Displays the statistics in the specified format. If this option is omitted, a
+comprehensive report is displayed. Supported output formats are:
+.RS 8
+.IP "\(bu" 2
+\fBJSON:\fP Displays the statistics in Javascript Object Notation (JSON) format.
+JSON output field order is undefined, and new fields might be added in the
+future.
+.IP "\(bu" 2
+\fBTABLE:\fP Displays the statistics in a human readable simple table format.
+The individual counters are omitted, and only the totals are displayed.
+This output format implies option \fB\-\-only-totals\fP.
+.IP "\(bu" 2
+\fBCSV:\fP Displays the statistics in comma-separated values format. The values
+are separated with a semicolon. The individual counters are omitted, and only
+the totals are displayed. This output format implies option
+\fB\-\-only-totals\fP.
+.RE
+.
+.TP
+.BR \-t ", " \-\-no\-totals
+Excludes the totals of all counters of a card device or queue device
+(APQN). This option cannot be specified together with option
+\fB\-\-only\-totals\fP or option \fB\-\-output\fP \fITABLE\fP|\fICSV\fP.
+.
+.TP
+.BR \-T ", " \-\-only\-totals
+Displays only the totals of all counters of a card device or a queue device
+(APQN), but not the individual counters. This option is implied with
+option \fB\-\-output\fP \fITABLE\fP|\fICSV\fP.
+.
+.TP
+.BR \-a ", " \-\-no\-apqn
+Displays only the counters of the card device, but omits the counters of the
+queue device (APQN). If the system does not support obtaining cryptographic
+performance measurement data on the queue devices, this option is implied.
+.
+.TP
+.BR \-M ", " \-\-map\-type\~\fIMAPPING\fP
+Maps unknown cryptographic device types and modes to known types and modes.
+This option should only be used when new, so far unknown cryptographic devices
+are found. You can then map them to known devices and modes, provided that the
+new cryptographic devices report the same counters as the known cryptographic
+device to which it is mapped.
+The mapping specification consists of a comma-separated list of
+\fIFROM\-TYPE\fP:\fIFROM\-MODE\fP=\fITO\-TYPE\fP:\fITO\-MODE\fP specifications.
+The type and mode values must be specified in decimal notation.
+.
+.TP
+.BR \-A ", " \-\-all
+Displays all cards devices and queue devices (APQNs), not only those that are
+available to the Linux instance. Using this option additional cryptographic
+devices that are available in the CEC, but not available to the Linux system
+are also monitored.
+This option cannot be specified together with option \fB\-\-only-online\fP.
+.
+.TP
+.BR \-O ", " \-\-only\-online
+Displays only online cards devices and queue devices (APQNs). This option
+cannot be specified together with option \fB\-\-all\fP.
+.
+.TP
+.BR \-V ", " \-\-verbose
+Displays additional information messages during processing.
+.TP
+.BR \-h ", " \-\-help
+Displays help text and exits.
+.TP
+.BR \-v ", " \-\-version
+Displays version information and exits.
+.
+.
+.
+.SH COUNTERS
+.
+.PP
+.B IBM Crypto Express adapter in accelerator mode:
+.RS 4
+.TP
+.B All
+All operations on the adapter
+.TP
+.B RSA Key-gen
+RSA-key-generation operations (also included in \fBAll\fP).
+.RE
+.PP
+.B IBM Crypto Express adapter in CCA co-processor mode:
+.RS 4
+.TP
+.B RSA 1024 ME
+1024-bit ME-format RSA operations.
+.TP
+.B RSA 2048 ME
+2048-bit ME-format RSA operations.
+.TP
+.B RSA 1024 CRT
+1024-bit CRT-format RSA operations.
+.TP
+.B RSA 2048 CRT
+2048-bit CRT-format RSA operations.
+.TP
+.B RSA 4096 ME
+4096-bit ME-format RSA operations.
+.TP
+.B RSA 4096 CTR
+4096-bit CRT-format RSA operations.
+.RE
+.PP
+.B IBM Crypto Express adapter in EP11 co-processor mode:
+.RS 4
+.TP
+.B Asym. Slow
+Slow asymmetric-key functions.
+.TP
+.B Asym. Fast
+Fast asymmetric-key functions.
+.TP
+.B Symm. Partial
+Symmetric-key functions that return partial or incremental results.
+.TP
+.B Symm. Complete
+Symmetric-key functions that return a complete or final result.
+.TP
+.B Asym. Key-gen
+asymmetric-key generation function.
+.RE
+.PP
+.
+.
+.
+.SH EXAMPLES
+.TP
+.B zcryptstats 02
+Display statistics for all cryptographic devices with card ID \fB02\fP.
+.TP
+.B zcryptstats 02.0005 --interval 5
+Display statistics for cryptographic devices with card ID \fB02\fP and domain
+ID \fB0005\fP in a 5 second interval.
+.TP
+.B zcryptstats .0005 --count 10
+Display statistics for cryptographic devices with domain ID \fB0005\fP with the
+default interval time of 10 seconds, for 10 intervals.
+.TP
+.B zcryptstats 02 --output JSON
+Display statistics for all cryptographic devices with card ID \fB02\fP in
+\fBJSON\fP output format.
+.TP
+
diff --git a/zconf/zcrypt/zcryptstats.c b/zconf/zcrypt/zcryptstats.c
new file mode 100644
index 0000000..136d5ba
--- /dev/null
+++ b/zconf/zcrypt/zcryptstats.c
@@ -0,0 +1,2418 @@
+/*
+ * zcryptstats - Show usage statistics of IBM Crypto Express adapters
+ *
+ * Copyright IBM Corp. 2019
+ *
+ * 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 <ctype.h>
+#include <err.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <locale.h>
+#include <stdbool.h>
+#include <stddef.h>
+#include <stdint.h>
+#include <signal.h>
+#include <time.h>
+#include <asm/chsc.h>
+#include <sys/ioctl.h>
+#include <sys/types.h>
+#include <sys/time.h>
+#include <sys/utsname.h>
+
+#include "lib/util_base.h"
+#include "lib/util_file.h"
+#include "lib/util_libc.h"
+#include "lib/util_opt.h"
+#include "lib/util_path.h"
+#include "lib/util_prg.h"
+#include "lib/util_rec.h"
+#include "lib/util_scandir.h"
+#include "lib/zt_common.h"
+
+#ifndef offsetof
+ #define offsetof(type, member) ((size_t) &((type *)0)->member)
+#endif
+#ifndef offsetofend
+ #define offsetofend(type, member) \
+ (offsetof(type, member) + sizeof(((type *)0)->member))
+#endif
+
+#define SYSFS_DEVICES_AP_PATH "devices/ap"
+#define SYSFS_DEVICES_CARD "devices/ap/card%02x"
+#define SYSFS_DEVICES_APQN "devices/ap/card%02x/%02x.%04x"
+#define SYSFS_DEVICES_CARD_ONLINE "devices/ap/card%02x/online"
+#define SYSFS_DEVICES_APQN_ONLINE "devices/ap/card%02x/%02x.%04x/online"
+#define CHSC_DEVICE "/dev/chsc"
+#define NUM_CARDS_OLD 64
+#define NUM_CARDS 256
+#define NUM_DOMAINS 256
+
+#define MASK_WORD_BITS (sizeof(uint32_t) * 8)
+#define MASK_WORD_NO(n) ((n) / MASK_WORD_BITS)
+#define MASK_BIT_IN_WORD(n) ((n) % MASK_WORD_BITS)
+#define MASK_BIT(n) (0x80000000 >> MASK_BIT_IN_WORD(n))
+
+struct chsc_apdn {
+ uint8_t ap_index;
+ uint8_t domain_index;
+} __packed;
+
+struct chsc_scdmd_request {
+ struct chsc_header header;
+ struct chsc_apdn first_drid;
+ struct chsc_apdn last_drid;
+ uint32_t s:1;
+ uint32_t reserved1:31;
+ uint32_t reserved2;
+ uint32_t apsm[8];
+ uint32_t dsm[8];
+} __packed;
+
+struct chsc_scdmd_response {
+ struct chsc_header header;
+ uint32_t reserved1;
+ uint16_t p:1;
+ uint16_t reserved2:15;
+ struct chsc_apdn crid;
+ uint32_t reserved3;
+} __packed;
+
+struct chsc_scdmd_area {
+ struct chsc_scdmd_request request;
+ struct chsc_scdmd_response response;
+ uint8_t response_data[CHSC_SIZE - sizeof(struct chsc_scdmd_request) -
+ sizeof(struct chsc_scdmd_response)];
+} __packed;
+
+struct chsc_scmd_request {
+ struct chsc_header header;
+ uint8_t reserved1;
+ uint8_t zeros1:6;
+ uint8_t one:1;
+ uint8_t zero:1;
+ uint8_t fcs;
+ uint8_t lcs;
+ uint32_t reserved2;
+ uint32_t reserved3;
+} __packed;
+
+struct chsc_scmd_response {
+ struct chsc_header header;
+ uint32_t reserved1;
+ uint32_t p:1;
+ uint32_t reserved2:31;
+ uint32_t reserved3;
+} __packed;
+
+struct chsc_scmd_area {
+ struct chsc_scmd_request request;
+ struct chsc_scmd_response response;
+ uint8_t response_data[CHSC_SIZE - sizeof(struct chsc_scmd_request) -
+ sizeof(struct chsc_scmd_response)];
+} __packed;
+
+struct chsc_cmb_header {
+ uint8_t reserved1;
+ uint8_t ct; /* AP_DEVICE_TYPE_xxx values */
+ uint8_t format;
+ uint8_t ax;
+ float s;
+ uint32_t v;
+ uint8_t dx;
+ uint8_t mt;
+ uint16_t l4;
+} __packed;
+
+struct chsc_cmb_entry {
+ u64 t;
+ u64 c;
+} __packed;
+
+struct chsc_cmb_area {
+ struct chsc_cmb_header header;
+ struct chsc_cmb_entry entries[32];
+} __packed;
+
+#define CRYPTO_TYPE_PCICC 3
+#define CRYPTO_TYPE_PCICA 4
+#define CRYPTO_TYPE_PCIXCC 5
+#define CRYPTO_TYPE_CEX2A 6
+#define CRYPTO_TYPE_CEX2C 7
+#define CRYPTO_TYPE_CEX3A 8
+#define CRYPTO_TYPE_CEX3C 9
+#define CRYPTO_TYPE_CEX4S 10
+#define CRYPTO_TYPE_CEX5S 11
+#define CRYPTO_TYPE_CEX6S 12
+
+#define CRYPTO_TYPE_TOLERATION CRYPTO_TYPE_CEX6S
+
+struct crypto_counter {
+ const char *name;
+ bool is_totals;
+};
+
+struct crypto_mode {
+ const char *name;
+ char indicatior_char;
+ unsigned int num_counters;
+ const struct crypto_counter *counters;
+};
+
+struct crypto_type {
+ const char *name;
+ unsigned int num_modes;
+ const struct crypto_mode *modes;
+};
+
+#define NUM_COPROC_COUNTERS 2
+const struct crypto_counter counter_coproc[NUM_COPROC_COUNTERS] = {
+ { .name = "All", .is_totals = true },
+ { .name = "RSA Key-gen" },
+};
+
+#define NUM_ACCEL_COUNTERS 6
+const struct crypto_counter counter_accel[NUM_ACCEL_COUNTERS] = {
+ { .name = "RSA 1024 ME" },
+ { .name = "RSA 2048 ME" },
+ { .name = "RSA 1024 CRT" },
+ { .name = "RSA 2048 CRT" },
+ { .name = "RSA 4096 ME" },
+ { .name = "RSA 4096 CTR" },
+};
+
+#define NUM_EP11_COUNTERS 5
+const struct crypto_counter counter_ep11[NUM_EP11_COUNTERS] = {
+ { .name = "Asym. Slow" },
+ { .name = "Asym. Fast" },
+ { .name = "Symm. Partial" },
+ { .name = "Symm. Complete" },
+ { .name = "Asym. Key-gen" },
+};
+
+#define NUM_PCICA_COUNTERS 20
+const struct crypto_counter counter_pcica[NUM_PCICA_COUNTERS] = {
+ { .name = "RSA 1024 ME (E0)" },
+ { .name = "RSA 2048 ME (E0)" },
+ { .name = "RSA 1024 CRT (E0)" },
+ { .name = "RSA 2048 CRT (E0)" },
+ { .name = "RSA 1024 ME (E1)" },
+ { .name = "RSA 2048 ME (E1)" },
+ { .name = "RSA 1024 CRT (E1)" },
+ { .name = "RSA 2048 CRT (E1)" },
+ { .name = "RSA 1024 ME (E2)" },
+ { .name = "RSA 2048 ME (E2)" },
+ { .name = "RSA 1024 CRT (E2)" },
+ { .name = "RSA 2048 CRT (E2)" },
+ { .name = "RSA 1024 ME (E3)" },
+ { .name = "RSA 2048 ME (E3)" },
+ { .name = "RSA 1024 CRT (E3)" },
+ { .name = "RSA 2048 CRT (E3)" },
+ { .name = "RSA 1024 ME (E4)" },
+ { .name = "RSA 2048 ME (E4)" },
+ { .name = "RSA 1024 CRT (E4)" },
+ { .name = "RSA 2048 CRT (E4)" },
+};
+
+#define NUM_COPROC_MODES 1
+const struct crypto_mode mode_coproc[1] = {
+ { .num_counters = NUM_COPROC_COUNTERS,
+ .counters = counter_coproc},
+};
+
+#define NUM_ACCEL_MODES 1
+const struct crypto_mode mode_accel[1] = {
+ { .num_counters = NUM_ACCEL_COUNTERS,
+ .counters = counter_accel },
+};
+
+#define NUM_PCICA_MODES 1
+const struct crypto_mode mode_pcica[1] = {
+ { .num_counters = NUM_PCICA_COUNTERS,
+ .counters = counter_pcica },
+};
+
+#define NUM_CEX456_MODES 11
+const struct crypto_mode mode_cex456[NUM_CEX456_MODES] = {
+ { 0 },
+ { 0 },
+ { 0 },
+ { 0 },
+ { 0 },
+ { 0 },
+ { 0 },
+ { 0 },
+ { .name = "Accelerator", .indicatior_char = 'A',
+ .num_counters = NUM_ACCEL_COUNTERS,
+ .counters = counter_accel },
+ { .name = "CCA co-processor", .indicatior_char = 'C',
+ .num_counters = NUM_COPROC_COUNTERS,
+ .counters = counter_coproc},
+ { .name = "EP11 co-processor", .indicatior_char = 'P',
+ .num_counters = NUM_EP11_COUNTERS,
+ .counters = counter_ep11 },
+};
+
+#define NUM_CRYPTO_TYPES 13
+const struct crypto_type crypto_types[NUM_CRYPTO_TYPES] = {
+ { 0 },
+ { 0 },
+ { 0 },
+ { .name = "PCICC", .num_modes = NUM_COPROC_MODES,
+ .modes = mode_coproc },
+ { .name = "PCICA", .num_modes = NUM_PCICA_MODES,
+ .modes = mode_pcica },
+ { .name = "PCIXCC", .num_modes = NUM_COPROC_MODES,
+ .modes = mode_coproc},
+ { .name = "CEX2A", .num_modes = NUM_ACCEL_MODES,
+ .modes = mode_accel },
+ { .name = "CEX2C", .num_modes = NUM_COPROC_MODES,
+ .modes = mode_coproc },
+ { .name = "CEX3A", .num_modes = NUM_ACCEL_MODES,
+ .modes = mode_accel },
+ { .name = "CEX3C", .num_modes = NUM_COPROC_MODES,
+ .modes = mode_coproc },
+ { .name = "CEX4", .num_modes = NUM_CEX456_MODES,
+ .modes = mode_cex456 },
+ { .name = "CEX5", .num_modes = NUM_CEX456_MODES,
+ .modes = mode_cex456 },
+ { .name = "CEX6", .num_modes = NUM_CEX456_MODES,
+ .modes = mode_cex456 },
+};
+
+
+struct type_mapping {
+ uint8_t from_type;
+ uint8_t from_mode;
+ uint8_t to_type;
+ uint8_t to_mode;
+ struct type_mapping *next;
+};
+
+struct device_selection {
+ int card;
+ int domain; /* -1 if not specified */
+ struct device_selection *next;
+};
+
+struct interval_data {
+ bool current_valid;
+ bool previous_valid;
+ struct chsc_cmb_area current;
+ struct chsc_cmb_area previous;
+};
+
+struct card_data {
+ struct interval_data data;
+ struct interval_data *domains[NUM_DOMAINS];
+};
+
+struct interval_values {
+ u64 count;
+ double rate;
+ double utilization;
+ double duration;
+};
+
+struct print_func {
+ int (*print_initialize)(void);
+ int (*print_terminate)(void);
+ int (*print_header)(void);
+ int (*print_footer)(void);
+ int (*print_interval_header)(unsigned long interval_count,
+ const char *timestamp);
+ int (*print_interval_footer)(void);
+ int (*print_device_header)(bool is_apqn, uint8_t card, uint8_t domain,
+ const char *type, const char *timestamp);
+ int (*print_device_footer)(void);
+ int (*print_counter_data)(bool is_apqn, uint8_t card, uint8_t domain,
+ const char *type, const char *timestamp,
+ const char *name,
+ struct interval_values *vals);
+ int (*print_counter_separator)(void);
+};
+
+#define pr_call(func) g.print_funcs->func == NULL ? 0 : g.print_funcs->func
+
+static int default_print_initialize(void);
+static int default_print_terminate(void);
+static int default_print_header(void);
+static int default_print_footer(void);
+static int default_print_interval_header(unsigned long interval_count,
+ const char *timestamp);
+static int default_print_device_header(bool is_apqn, uint8_t card,
+ uint8_t domain, const char *type,
+ const char *timestamp);
+static int default_print_device_footer(void);
+static int default_print_counter_data(bool is_apqn, uint8_t card,
+ uint8_t domain, const char *type,
+ const char *timestamp, const char *name,
+ struct interval_values *vals);
+static int default_print_counter_separator(void);
+
+static const struct print_func default_print = {
+ .print_initialize = default_print_initialize,
+ .print_terminate = default_print_terminate,
+ .print_header = default_print_header,
+ .print_footer = default_print_footer,
+ .print_interval_header = default_print_interval_header,
+ .print_device_header = default_print_device_header,
+ .print_device_footer = default_print_device_footer,
+ .print_counter_data = default_print_counter_data,
+ .print_counter_separator = default_print_counter_separator,
+};
+
+static int json_print_initialize(void);
+static int json_print_header(void);
+static int json_print_footer(void);
+static int json_print_interval_header(unsigned long interval_count,
+ const char *timestamp);
+static int json_print_interval_footer(void);
+static int json_print_device_header(bool is_apqn, uint8_t card,
+ uint8_t domain, const char *type,
+ const char *timestamp);
+static int json_print_device_footer(void);
+static int json_print_counter_data(bool is_apqn, uint8_t card, uint8_t domain,
+ const char *type, const char *timestamp,
+ const char *name,
+ struct interval_values *vals);
+
+static const struct print_func json_print = {
+ .print_initialize = json_print_initialize,
+ .print_header = json_print_header,
+ .print_footer = json_print_footer,
+ .print_interval_header = json_print_interval_header,
+ .print_interval_footer = json_print_interval_footer,
+ .print_device_header = json_print_device_header,
+ .print_device_footer = json_print_device_footer,
+ .print_counter_data = json_print_counter_data,
+};
+
+
+static int table_print_initialize(void);
+static int table_print_terminate(void);
+static int table_print_header(void);
+static int table_print_interval_footer(void);
+static int table_print_counter_data(bool is_apqn, uint8_t card, uint8_t domain,
+ const char *type, const char *timestamp,
+ const char *name,
+ struct interval_values *vals);
+
+static const struct print_func table_print = {
+ .print_initialize = table_print_initialize,
+ .print_terminate = table_print_terminate,
+ .print_header = table_print_header,
+ .print_interval_footer = table_print_interval_footer,
+ .print_counter_data = table_print_counter_data,
+};
+
+static int csv_print_initialize(void);
+static int csv_print_terminate(void);
+static int csv_print_header(void);
+static int csv_print_counter_data(bool is_apqn, uint8_t card, uint8_t domain,
+ const char *type, const char *timestamp,
+ const char *name,
+ struct interval_values *vals);
+
+static const struct print_func csv_print = {
+ .print_initialize = csv_print_initialize,
+ .print_terminate = csv_print_terminate,
+ .print_header = csv_print_header,
+ .print_counter_data = csv_print_counter_data,
+};
+
+/*
+ * Program configuration
+ */
+const struct util_prg prg = {
+ .desc = "Display usage statistics of IBM Crypto Express adapters",
+ .args = "[DEVICE_IDS]",
+ .copyright_vec = {
+ {
+ .owner = "IBM Corp.",
+ .pub_first = 2019,
+ .pub_last = 2019,
+ },
+ UTIL_PRG_COPYRIGHT_END
+ }
+};
+
+/*
+ * Global variables for program options
+ */
+static struct zcryptstats_globals {
+ long interval;
+ unsigned long count;
+ bool no_totals;
+ bool only_totals;
+ bool no_apqn;
+ char *map_type;
+ char **device_ids;
+ bool all;
+ bool only_online;
+ bool verbose;
+ int chsc_fd;
+ uint8_t max_card_used;
+ uint32_t card_mask[8];
+ uint8_t min_card;
+ uint8_t max_card;
+ uint32_t domain_mask[8];
+ uint8_t min_domain;
+ uint8_t max_domain;
+ struct device_selection *dev_selection;
+ struct type_mapping *type_mapping;
+ struct card_data *cards[NUM_CARDS];
+ const struct print_func *print_funcs;
+ struct util_rec *device_rec;
+ struct util_rec *counter_rec;
+ bool first_device;
+ bool first_counter;
+} g = {
+ .interval = 10,
+ .chsc_fd = -1,
+ .print_funcs = &default_print,
+};
+
+
+static volatile bool quit;
+
+/*
+ * Configuration of command line options
+ */
+static struct util_opt opt_vec[] = {
+ /***********************************************************/
+ {
+ .flags = UTIL_OPT_FLAG_SECTION,
+ .desc = "OPTIONS",
+ },
+ {
+ .option = {"interval", required_argument, NULL, 'i'},
+ .argument = "INTERVAL",
+ .desc = "Specifies the interval time in seconds. If omitted, a "
+ "default interval of 10 seconds is used",
+ },
+ {
+ .option = {"count", required_argument, NULL, 'c'},
+ .argument = "COUNT",
+ .desc = "Specifies the number of reports that are generated "
+ "at INTERVAL seconds apart. If omitted, reports are "
+ "generated continuously, until stopped with control-C",
+ },
+ {
+ .option = {"output", required_argument, NULL, 'o'},
+ .argument = "JSON|TABLE|CSV",
+ .desc = "Displays the statistics in the specified format. If "
+ "this option is omitted, a comprehensive report is "
+ "displayed. Supported output formats are: JSON, TABLE, "
+ "CSV. With TABLE and CSV the display of the individual "
+ "counters are omitted, and only the totals are "
+ "displayed. CSV and TABLE output formats imply option "
+ "--only-totals",
+ },
+ {
+ .option = {"no-totals", 0, NULL, 't'},
+ .desc = "Excludes the totals of all counters of a card "
+ "device or queue device (APQN). It can not be "
+ "specified together with option --only-totals or "
+ "option --output TABLE|CSV",
+ },
+ {
+ .option = {"only-totals", 0, NULL, 'T'},
+ .desc = "Displays only the totals of all counters of a card "
+ "device or a queue device (APQN), but not the "
+ "individual counters. This option is implied with "
+ "option --output TABLE|CSV",
+ },
+ {
+ .option = {"no-apqn", 0, NULL, 'a'},
+ .desc = "Displays only the counters of the card device, but "
+ "omits the counters of the queue device (APQN). If the "
+ "system does not support obtaining cryptographic "
+ "performance measurement data on the queue devices, "
+ "then this option is implied",
+ },
+ {
+ .option = {"map-type", required_argument, NULL, 'M'},
+ .argument = "MAPPING",
+ .desc = "Maps unknown cryptographic device types and modes to "
+ "known types and modes. This option should only be "
+ "used when new, so far unknown cryptographic devices "
+ "are found. You can then map them to known devices and "
+ "modes, provided that the new cryptographic devices "
+ "report the same counters as the known cryptographic "
+ "device to which it is mapped. The mapping "
+ "specification consists of a comma-separated list of "
+ "FROM-TYPE:FROM-MODE=TO-TYPE:TO-MODE specifications. "
+ "The type and mode values must be specified in decimal "
+ "notation",
+ },
+ {
+ .option = {"all", 0, NULL, 'A'},
+ .desc = "Displays all cards devices and queue devices (APQNs), "
+ "not only those that are available to the Linux "
+ "system. Using this option additional cryptographic "
+ "devices that are available in the CEC, but not "
+ "available to the Linux system are also monitored. "
+ "This option can not be specified together with option "
+ "--only-online",
+ },
+ {
+ .option = {"only-online", 0, NULL, 'O'},
+ .desc = "Displays only online cards devices and queue devices "
+ "(APQNs). This option can not be specified together "
+ "with option --all"
+ },
+ {
+ .option = {"verbose", 0, NULL, 'V'},
+ .desc = "Prints additional information messages during "
+ "processing",
+ },
+ UTIL_OPT_HELP,
+ UTIL_OPT_VERSION,
+ UTIL_OPT_END
+};
+
+#define pr_verbose(fmt...) do { \
+ if (g.verbose) \
+ warnx(fmt); \
+ } while (0)
+
+/*
+ * Describe adapter ids
+ */
+static void print_adapter_id_help(void)
+{
+ printf("\n");
+ printf("DEVICE_IDS\n");
+ util_print_indented(" List of cryptographic device IDs separated by "
+ "blanks for which statistics are displayed. "
+ "DEVICE_ID can either be a card device ID "
+ "('<card-id>') or a queue device ID (<card-id>."
+ "<domain-id>'). To filter all devices by domain, "
+ "provide '.<domain-id>'. If no IDs are given, "
+ "statistics are displayed for all available "
+ "devices.", 2);
+ printf("\n");
+ printf("EXAMPLE:\n");
+ util_print_indented(" Display statistics for all cryptographic "
+ "devices with card ID '02.", 2);
+ printf(" # zcryptstats 02\n");
+ printf("\n");
+ util_print_indented(" Display statistics for cryptographic devices "
+ "with card ID '02' and domain ID '0005'.", 2);
+ printf(" # zcryptstats 02.0005\n");
+ printf("\n");
+}
+
+static struct type_mapping *find_type_mapping(uint8_t from_type,
+ uint8_t from_mode)
+{
+ struct type_mapping *map = g.type_mapping;
+
+ while (map != NULL) {
+ if (map->from_type == from_type && map->from_mode == from_mode)
+ return map;
+ map = map->next;
+ }
+ return NULL;
+}
+
+/*
+ * Get the name of the card for a crypto type and mode.
+ * Note: This function might return the address of a static string variable.
+ * It is only valid until this function is called again.
+ */
+static const char *get_card_name(uint8_t type, uint8_t mode)
+{
+ const struct crypto_type *ct;
+ const struct crypto_mode *m;
+ static char temp_name[250];
+ struct type_mapping *map;
+
+ map = find_type_mapping(type, mode);
+ if (map != NULL) {
+ type = map->to_type;
+ mode = map->to_mode;
+ } else if (type >= NUM_CRYPTO_TYPES) {
+ type = CRYPTO_TYPE_TOLERATION;
+ }
+
+ if (type >= NUM_CRYPTO_TYPES)
+ return "UNKNOWN ADAPTER TYPE";
+
+ ct = &crypto_types[type];
+ if (ct->name == NULL || ct->modes == NULL || ct->num_modes == 0)
+ return "UNKNOWN ADAPTER TYPE";
+
+ if (mode >= ct->num_modes)
+ return ct->name;
+
+ m = &ct->modes[mode];
+ snprintf(temp_name, sizeof(temp_name) - 1, "%s%c (%s)", ct->name,
+ m->indicatior_char, m->name != NULL ? m->name : "");
+ return temp_name;
+}
+
+/*
+ * Get the name of a counter for a crypto type, mode and index.
+ * Note: This function might return the address of a static string variable.
+ * It is only valid until this function is called again.
+ */
+static const char *get_counter_name(uint8_t type, uint8_t mode, uint8_t index)
+{
+ const struct crypto_type *ct;
+ const struct crypto_mode *m;
+ static char temp_name[250];
+ struct type_mapping *map;
+
+ map = find_type_mapping(type, mode);
+ if (map != NULL) {
+ type = map->to_type;
+ mode = map->to_mode;
+ } else if (type >= NUM_CRYPTO_TYPES) {
+ type = CRYPTO_TYPE_TOLERATION;
+ }
+
+ if (type >= NUM_CRYPTO_TYPES)
+ goto generic;
+
+ ct = &crypto_types[type];
+ if (ct->name == NULL || ct->modes == NULL || ct->num_modes == 0)
+ goto generic;
+
+ if (mode >= ct->num_modes)
+ goto generic;
+
+ m = &ct->modes[mode];
+ if (m->counters == NULL || m->num_counters == 0)
+ goto generic;
+
+ if (index >= m->num_counters)
+ goto generic;
+
+ return m->counters[index].name;
+
+generic:
+ snprintf(temp_name, sizeof(temp_name) - 1, "COUNTER %u", index);
+ return temp_name;
+}
+
+/*
+ * Returns true if a counter for a crypto type, mode and index represents the
+ * total number of operations.
+ */
+static bool is_counter_totals(uint8_t type, uint8_t mode, uint8_t index)
+{
+ const struct crypto_type *ct;
+ const struct crypto_mode *m;
+ struct type_mapping *map;
+
+ map = find_type_mapping(type, mode);
+ if (map != NULL) {
+ type = map->to_type;
+ mode = map->to_mode;
+ } else if (type >= NUM_CRYPTO_TYPES) {
+ type = CRYPTO_TYPE_TOLERATION;
+ }
+
+ if (type >= NUM_CRYPTO_TYPES)
+ return false;
+
+ ct = &crypto_types[type];
+ if (ct->name == NULL || ct->modes == NULL || ct->num_modes == 0)
+ return false;
+
+ if (mode >= ct->num_modes)
+ return false;
+
+ m = &ct->modes[mode];
+ if (m->counters == NULL || m->num_counters == 0)
+ return false;
+
+ if (index >= m->num_counters)
+ return false;
+
+ return m->counters[index].is_totals;
+}
+
+/*
+ * Returns true if the card is available
+ */
+static bool is_card_available(uint8_t card)
+{
+ char *path;
+ bool ret;
+
+ path = util_path_sysfs(SYSFS_DEVICES_CARD, card);
+ ret = util_path_is_dir(path);
+ free(path);
+
+ return ret;
+}
+
+/*
+ * Returns true if the APQN is available
+ */
+static bool is_apqn_available(uint8_t card, uint8_t domain)
+{
+ char *path;
+ bool ret;
+
+ path = util_path_sysfs(SYSFS_DEVICES_APQN, card, card, domain);
+ ret = util_path_is_dir(path);
+ free(path);
+
+ return ret;
+}
+
+/*
+ * Returns true if the card is online
+ */
+static bool is_card_online(uint8_t card)
+{
+ unsigned long online;
+ char *path;
+ int rc;
+
+ path = util_path_sysfs(SYSFS_DEVICES_CARD_ONLINE, card);
+ rc = util_file_read_ul(&online, 10, path);
+ free(path);
+
+ return rc == 0 && online != 0;
+}
+
+/*
+ * Returns true if the APQN is online
+ */
+static bool is_apqn_online(uint8_t card, uint8_t domain)
+{
+ unsigned long online;
+ char *path;
+ int rc;
+
+ path = util_path_sysfs(SYSFS_DEVICES_APQN_ONLINE, card, card, domain);
+ rc = util_file_read_ul(&online, 10, path);
+ free(path);
+
+ if (rc != 0)
+ return false;
+
+ return online != 0;
+}
+
+/*
+ * Updates the APQNs data with data for the current interval.
+ */
+static void update_apqn_data(uint8_t card, uint8_t domain,
+ struct chsc_cmb_area *cmb, size_t cmb_len)
+{
+ struct card_data *cd = g.cards[card];
+ struct interval_data *dd;
+
+ if (cd == NULL) {
+ cd = util_malloc(sizeof(struct card_data));
+ memset(cd, 0, sizeof(struct card_data));
+ g.cards[card] = cd;
+
+ pr_verbose("Card %02x added", card);
+ }
+
+ dd = cd->domains[domain];
+ if (dd == NULL) {
+ dd = util_malloc(sizeof(struct interval_data));
+ memset(dd, 0, sizeof(struct interval_data));
+ cd->domains[domain] = dd;
+
+ pr_verbose("APQN %02x.%04x added", card, domain);
+ } else {
+ if (!dd->current_valid) {
+ dd->previous = dd->current;
+ dd->previous_valid = true;
+ }
+ }
+
+ memset(&dd->current, 0, sizeof(struct chsc_cmb_area));
+ memcpy(&dd->current, cmb, cmb_len);
+ dd->current_valid = true;
+}
+
+/*
+ * Updates the card's data with data for the current interval.
+ */
+static void update_card_data(uint8_t card, struct chsc_cmb_area *cmb,
+ size_t cmb_len)
+{
+ struct card_data *cd = g.cards[card];
+
+ if (cd == NULL) {
+ cd = util_malloc(sizeof(struct card_data));
+ memset(cd, 0, sizeof(struct card_data));
+ g.cards[card] = cd;
+
+ pr_verbose("Card %02x added", card);
+ } else {
+ if (!cd->data.current_valid) {
+ cd->data.previous = cd->data.current;
+ cd->data.previous_valid = true;
+ }
+ };
+
+ memset(&cd->data.current, 0, sizeof(struct chsc_cmb_area));
+ memcpy(&cd->data.current, cmb, cmb_len);
+ cd->data.current_valid = true;
+}
+
+/*
+ * Frees the interval data of a card
+ */
+static void free_card_data(struct card_data *cd)
+{
+ struct interval_data *dd;
+ int domain;
+
+ if (cd == NULL)
+ return;
+
+ for (domain = 0; domain < NUM_DOMAINS; domain++) {
+ dd = cd->domains[domain];
+ if (dd == NULL)
+ continue;
+
+ free(dd);
+ cd->domains[domain] = NULL;
+ }
+
+ free(cd);
+}
+
+/*
+ * Frees the interval data
+ */
+static void free_interval_data(void)
+{
+ struct card_data *cd;
+ int card;
+
+ for (card = 0; card < NUM_CARDS; card++) {
+ cd = g.cards[card];
+ if (cd == NULL)
+ continue;
+
+ free_card_data(cd);
+ g.cards[card] = NULL;
+ }
+}
+
+/*
+ * Returns the highest card index used by the system.
+ * If there is a card with an index > 0x3f, then the returned number is 0xff,
+ * else 0x3f is returned.
+ */
+static int get_max_card_index(uint8_t *max_index)
+{
+ struct dirent **dev_vec = NULL;
+ int i, count, card, rc = 0;
+ char *path;
+
+ path = util_path_sysfs(SYSFS_DEVICES_AP_PATH);
+ if (!util_path_is_dir(path)) {
+ warnx("Crypto device driver is not available");
+ rc = -ENODEV;
+ goto out;
+ }
+
+ count = util_scandir(&dev_vec, NULL, path, "card[0-9a-fA-F]+");
+ if (count < 1) {
+ warnx("No crypto card devices found");
+ rc = -ENODEV;
+ goto out;
+ }
+
+ *max_index = NUM_CARDS_OLD - 1;
+ for (i = 0; i < count; i++) {
+ if (sscanf(dev_vec[i]->d_name, "card%x", &card) != 1)
+ continue;
+ if (card >= NUM_CARDS_OLD)
+ *max_index = NUM_CARDS - 1;
+ }
+
+ pr_verbose("Max card index used: %u", *max_index);
+
+out:
+ free(path);
+ if (dev_vec != NULL)
+ free(dev_vec);
+ return rc;
+}
+
+/*
+ * Returns the size of the CMB. The size is either contained in l4 field
+ * of the cmb, or a fix length dependent on the crypto type, if l4 is zero.
+ */
+static size_t get_cmb_length(struct chsc_cmb_area *cmb)
+{
+ size_t len = cmb->header.l4;
+
+ if (len != 0)
+ return len;
+
+ switch (cmb->header.ct) {
+ case CRYPTO_TYPE_PCICC:
+ case CRYPTO_TYPE_PCIXCC:
+ case CRYPTO_TYPE_CEX2C:
+ case CRYPTO_TYPE_CEX3C:
+ return 64;
+ case CRYPTO_TYPE_PCICA:
+ return 336;
+ case CRYPTO_TYPE_CEX2A:
+ case CRYPTO_TYPE_CEX3A:
+ return 80;
+ default:
+ warnx("Zero length value in CMB");
+ return 0;
+ }
+}
+
+/*
+ * Return true if the device is in the device selection list and mask
+ */
+static bool filter_device(uint8_t card, uint8_t domain, bool is_apqn)
+{
+ struct device_selection *dev;
+ bool found;
+
+ /* Check for selection mask */
+ if ((g.card_mask[MASK_WORD_NO(card)] &
+ MASK_BIT(card)) == 0) {
+ pr_verbose("Skipping card %02x (mask)", card);
+ return false;
+ }
+ if (is_apqn) {
+ if ((g.domain_mask[MASK_WORD_NO(domain)] &
+ MASK_BIT(domain)) == 0) {
+ pr_verbose("Skipping APQN %02x.%04x (mask)", card,
+ domain);
+ return false;
+ }
+ }
+
+ /* Check for device selection list */
+ if (g.dev_selection != NULL) {
+ dev = g.dev_selection;
+ found = false;
+ while (dev != NULL) {
+ if (is_apqn == false) {
+ /* Its a card */
+ if (card == (dev->card >= 0 ? dev->card :
+ card)) {
+ found = true;
+ break;
+ }
+ } else {
+ /* Its an APQN */
+ if (card == (dev->card >= 0 ? dev->card :
+ card) &&
+ domain == (dev->domain >= 0 ? dev->domain :
+ domain)) {
+ found = true;
+ break;
+ }
+ }
+
+ dev = dev->next;
+ }
+
+ if (!found) {
+ if (is_apqn)
+ pr_verbose("Skipping APQN %02x.%04x "
+ "(selection)", card, domain);
+ else
+ pr_verbose("Skipping card %02x (selection)",
+ card);
+ return false;
+ }
+ }
+
+ if (g.all)
+ return true;
+
+ /* Check if card/APQN is available in the system (SYSFS) */
+ if (!is_card_available(card)) {
+ pr_verbose("Skipping card %02x (not available)", card);
+ return false;
+ }
+ if (is_apqn && !is_apqn_available(card, domain)) {
+ pr_verbose("Skipping APQN %02x.%04x (not available)",
+ card, domain);
+ return false;
+ }
+
+ if (g.only_online) {
+ /* Check if card/APQN is online */
+ if (!is_card_online(card)) {
+ pr_verbose("Skipping card %02x (not online)", card);
+ return false;
+ }
+ if (is_apqn && !is_apqn_online(card, domain)) {
+ pr_verbose("Skipping APQN %02x.%04x (not online)",
+ card, domain);
+ return false;
+ }
+ }
+
+ return true;
+}
+
+/*
+ * Process a crypto measurement block.
+ * Passes back the actual length of the CMB processed and its card number.
+ * Returns -ENODEV when the CMB is skipped.
+ */
+static int process_cmb(struct chsc_cmb_area *cmb, size_t size, size_t *length,
+ uint8_t *card)
+{
+ size_t len;
+
+ len = get_cmb_length(cmb);
+ if (len == 0)
+ return -EINVAL;
+
+ if (len > size) {
+ warnx("Length value in CMB exceeds size of CMB");
+ return -EINVAL;
+ }
+
+ if (card != NULL)
+ *card = cmb->header.ax;
+ if (length != NULL)
+ *length = len;
+
+ if (filter_device(cmb->header.ax, cmb->header.dx,
+ cmb->header.format == 1) == false)
+ return -ENODEV;
+
+ if (cmb->header.format == 1)
+ update_apqn_data(cmb->header.ax, cmb->header.dx, cmb, len);
+ else
+ update_card_data(cmb->header.ax, cmb, len);
+
+ return 0;
+}
+
+/*
+ * Translate the CHSC response code to an error (0 or negative errno)
+ */
+static int chsc_error_from_response(int response)
+{
+ if (response != 0x0001)
+ pr_verbose("CHSC Response code: %04x", response);
+
+ switch (response) {
+ case 0x0001:
+ return 0;
+ case 0x0002:
+ return -EOPNOTSUPP;
+ case 0x0003:
+ case 0x0006:
+ case 0x0007:
+ case 0x0008:
+ case 0x000a:
+ case 0x0103:
+ case 0x0104:
+ return -EINVAL;
+ case 0x0004:
+ return -EOPNOTSUPP;
+ case 0x000b:
+ return -EBUSY;
+ case 0x0102:
+ return -ENOMEM;
+ case 0x0105:
+ return -EACCES;
+ case 0x0100:
+ case 0x0107:
+ return -ENODEV;
+ default:
+ return -EIO;
+ }
+}
+
+/*
+ * Process the APQN measurement data and extract the CMBs
+ */
+static int process_apqn_measurement_data(struct chsc_scdmd_area *scdmd_area)
+{
+ size_t size = scdmd_area->response.header.length -
+ sizeof(struct chsc_scdmd_response);
+ size_t len, ofs = 0;
+ int rc;
+
+ while (ofs < size) {
+ rc = process_cmb((struct chsc_cmb_area *)
+ &scdmd_area->response_data[ofs],
+ size - ofs, &len, NULL);
+ if (rc != 0 && rc != -ENODEV)
+ return rc;
+ ofs += len;
+
+ }
+
+ return 0;
+}
+
+/*
+ * Get Crypto Measurement data on the APQN level
+ */
+static int get_apqn_measurement_data(uint8_t card)
+{
+ struct chsc_scdmd_area scdmd_area;
+ int rc;
+
+ memset(&scdmd_area, 0, sizeof(scdmd_area));
+ do {
+ 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;
+ scdmd_area.request.last_drid.ap_index = card;
+ scdmd_area.request.last_drid.domain_index = g.max_domain;
+ scdmd_area.request.s = 1;
+ scdmd_area.request.apsm[MASK_WORD_NO(card)] |= MASK_BIT(card);
+ memcpy(scdmd_area.request.dsm, g.domain_mask,
+ sizeof(scdmd_area.request.dsm));
+
+ rc = ioctl(g.chsc_fd, CHSC_START_SYNC, &scdmd_area);
+ if (rc != 0) {
+ rc = -errno;
+ warnx("Failed to get APQN measurement data for card "
+ "%02x: %s", card, strerror(errno));
+ break;
+ }
+
+ rc = chsc_error_from_response(scdmd_area.response.header.code);
+ if (rc != 0) {
+ if (rc != -EOPNOTSUPP && rc != -ENODEV) {
+ warnx("Failed to get APQN crypto measurement "
+ "data for card %02x: %s", card,
+ strerror(-rc));
+ } else {
+ pr_verbose("Failed to get APQN crypto "
+ "measurement data for card %02x: %s",
+ card, strerror(-rc));
+ /*
+ * ignore return code other than -EOPNOTSUPP
+ * and -ENODEV
+ */
+ rc = 0;
+ }
+ break;
+ }
+
+ 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;
+}
+
+/*
+ * Process the card measurement data and extract the CMBs
+ */
+static int process_card_measurement_data(struct chsc_scmd_area *scmd_area,
+ uint8_t *last_card)
+{
+ size_t size = scmd_area->response.header.length -
+ sizeof(struct chsc_scmd_response);
+ size_t len, ofs = 0;
+ int rc;
+
+ while (ofs < size) {
+ rc = process_cmb((struct chsc_cmb_area *)
+ &scmd_area->response_data[ofs],
+ size - ofs, &len, last_card);
+ if (rc != 0 && rc != -ENODEV)
+ return rc;
+ ofs += len;
+
+ if (rc == -ENODEV)
+ continue;
+
+ if (!g.no_apqn) {
+ rc = get_apqn_measurement_data(*last_card);
+ if (rc != 0)
+ return rc;
+ }
+ }
+
+ return 0;
+}
+
+/*
+ * Get Crypto Measurement data on the card level
+ */
+static int get_card_measurement_data(void)
+{
+ struct chsc_scmd_area scmd_area;
+ uint8_t last_card = 0;
+ int rc;
+
+ memset(&scmd_area, 0, sizeof(scmd_area));
+ do {
+ scmd_area.request.header.code = 0x102e;
+ scmd_area.request.header.length =
+ sizeof(struct chsc_scmd_request);
+ scmd_area.request.one = 1;
+ scmd_area.request.fcs = g.min_card;
+ scmd_area.request.lcs = g.max_card;
+
+ rc = ioctl(g.chsc_fd, CHSC_START_SYNC, &scmd_area);
+ if (rc != 0) {
+ rc = -errno;
+ warnx("Failed to get card measurement data: %s",
+ strerror(errno));
+ break;
+ }
+
+ rc = chsc_error_from_response(scmd_area.response.header.code);
+ if (rc != 0) {
+ warnx("Failed to get card crypto measurement data: %s",
+ strerror(-rc));
+ break;
+ }
+
+ rc = process_card_measurement_data(&scmd_area, &last_card);
+ if (rc != 0)
+ break;
+
+ if (scmd_area.response.p)
+ scmd_area.request.fcs = last_card + 1;
+ } while (scmd_area.response.p && last_card < g.max_card);
+
+ return rc;
+}
+
+/*
+ * Signal handler for SIGALRM
+ */
+static void alarm_handler(int UNUSED(sig))
+{
+ if (!quit)
+ alarm(g.interval);
+}
+
+/*
+ * Signal handler for SIGINT and SIGTERM
+ */
+static void int_handler(int UNUSED(sig))
+{
+ quit = true;
+ raise(SIGALRM);
+}
+
+/*
+ * Calculates the time difference between tv1 and tv2 in seconds
+ */
+static float time_diff(struct timeval *tv1, struct timeval *tv2)
+{
+ struct timeval tv_diff;
+
+ tv_diff.tv_sec = tv2->tv_sec - tv1->tv_sec;
+ tv_diff.tv_usec = tv2->tv_usec - tv1->tv_usec;
+ if (tv_diff.tv_usec < 0) {
+ tv_diff.tv_sec--;
+ tv_diff.tv_usec += 1000000;
+ }
+
+ return (float)tv_diff.tv_sec + (float)(0.000001 * tv_diff.tv_usec);
+}
+
+/*
+ * Initialize the default print format
+ */
+static int default_print_initialize(void)
+{
+ g.device_rec = util_rec_new_wide("-");
+ util_rec_def(g.device_rec, "device", UTIL_REC_ALIGN_LEFT, 7,
+ "DEVICE");
+ util_rec_def(g.device_rec, "kind", UTIL_REC_ALIGN_LEFT, 5, "");
+ util_rec_def(g.device_rec, "type", UTIL_REC_ALIGN_LEFT, 33,
+ "TYPE");
+ util_rec_def(g.device_rec, "time", UTIL_REC_ALIGN_LEFT, 20,
+ "TIMESTAMP");
+
+ g.counter_rec = util_rec_new_wide("-");
+ util_rec_def(g.counter_rec, "name", UTIL_REC_ALIGN_LEFT, 18,
+ "COUNTER");
+ util_rec_def(g.counter_rec, "ops", UTIL_REC_ALIGN_RIGHT, 10,
+ "OPS");
+ util_rec_def(g.counter_rec, "rate", UTIL_REC_ALIGN_RIGHT, 12,
+ "RATE");
+ util_rec_def(g.counter_rec, "utilization", UTIL_REC_ALIGN_RIGHT,
+ 12, "UTILIZATION");
+ util_rec_def(g.counter_rec, "duration", UTIL_REC_ALIGN_RIGHT,
+ 15, "AVG.DURATION");
+
+ return 0;
+}
+
+/*
+ * Terminate the default print format
+ */
+static int default_print_terminate(void)
+{
+ util_rec_free(g.counter_rec);
+ util_rec_free(g.device_rec);
+
+ return 0;
+}
+
+/*
+ * Print the header lines for the default print format
+ */
+static int default_print_header(void)
+{
+ char timestamp[64];
+ struct utsname un;
+ struct tm *tm;
+ time_t t;
+
+ time(&t);
+ tm = localtime(&t);
+ strftime(timestamp, sizeof(timestamp), "%x", tm);
+
+ if (uname(&un) != 0)
+ return -errno;
+
+ printf("%s %s (%s) \t%s\t%s\n\n", un.sysname, un.release,
+ un.nodename, timestamp, un.machine);
+
+ return 0;
+}
+
+/*
+ * Print the footer lines for the default print format
+ */
+static int default_print_footer(void)
+{
+ printf("\n");
+ return 0;
+}
+
+/*
+ * Print the interval header lines for the default print format
+ */
+static int default_print_interval_header(unsigned long interval_count,
+ const char *timestamp)
+{
+
+ printf("*****************************************************"
+ "***************\n");
+ printf("TIME: %s\t\tINTERVAL: %lu\n\n", timestamp, interval_count);
+ return 0;
+}
+
+/*
+ * Prints the separator lines in front of a device for the default print format
+ */
+static int default_print_device_header(bool is_apqn, uint8_t card,
+ uint8_t domain, const char *type,
+ const char *timestamp)
+{
+ if (is_apqn)
+ util_rec_set(g.device_rec, "device", "%02x.%04x", card,
+ domain);
+ else
+ util_rec_set(g.device_rec, "device", "%02x", card);
+ util_rec_set(g.device_rec, "kind", "%s",
+ is_apqn ? "APQN" : "CARD");
+ util_rec_set(g.device_rec, "type", "%s", type);
+ util_rec_set(g.device_rec, "time", "%s", timestamp);
+
+ util_rec_print_hdr(g.device_rec);
+ util_rec_print(g.device_rec);
+ printf("\n");
+
+ util_rec_set_indent(g.counter_rec, is_apqn ? 8 : 4);
+ util_rec_print_hdr(g.counter_rec);
+
+ return 0;
+}
+
+/*
+ * Prints the separator lines after a device for the default print format
+ */
+static int default_print_device_footer(void)
+{
+
+ printf("\n");
+ return 0;
+}
+
+
+/**
+ * Prints the counter data for the default print format
+ */
+static int default_print_counter_data(bool UNUSED(is_apqn),
+ uint8_t UNUSED(card),
+ uint8_t UNUSED(domain),
+ const char *UNUSED(type),
+ const char *UNUSED(timestamp),
+ const char *name,
+ struct interval_values *vals)
+{
+ util_rec_set(g.counter_rec, "name", "%s", name);
+ util_rec_set(g.counter_rec, "ops", "%llu", vals->count);
+ util_rec_set(g.counter_rec, "rate", "%.2f", vals->rate);
+ util_rec_set(g.counter_rec, "utilization", "%.2f %%",
+ vals->utilization * 100);
+ if (vals->duration >= 1)
+ util_rec_set(g.counter_rec, "duration", "%.3f sec ",
+ vals->duration);
+ else if (vals->duration >= 0.001)
+ util_rec_set(g.counter_rec, "duration", "%.3f msec",
+ vals->duration * 1000);
+ else
+ util_rec_set(g.counter_rec, "duration", "%.3f usec",
+ vals->duration * 1000000);
+ util_rec_print(g.counter_rec);
+
+ return 0;
+}
+
+/*
+ * Prints a separator between the counter lines and the totals line for the
+ * default print format
+ */
+static int default_print_counter_separator(void)
+{
+ util_rec_print_separator(g.counter_rec);
+ return 0;
+}
+
+/*
+ * Initialize the JSON print format
+ */
+static int json_print_initialize(void)
+{
+ /* Use a decimal point to make JSON code compliant with RFC7159 */
+ setlocale(LC_NUMERIC, "C");
+ return 0;
+}
+
+/*
+ * Print the header lines for the JSON print format
+ */
+static int json_print_header(void)
+{
+ char timestamp[64];
+ struct utsname un;
+ struct tm *tm;
+ time_t t;
+
+ time(&t);
+ tm = localtime(&t);
+ strftime(timestamp, sizeof(timestamp), "%x", tm);
+
+ if (uname(&un) != 0)
+ return -errno;
+
+ printf("{\"zcryptstats\": {\n");
+ printf("\t\"host\": {\n");
+ printf("\t\t\"nodename\": \"%s\",\n", un.nodename);
+ printf("\t\t\"sysname\": \"%s\",\n", un.sysname);
+ printf("\t\t\"release\": \"%s\",\n", un.release);
+ printf("\t\t\"machine\": \"%s\",\n", un.machine);
+ printf("\t\t\"date\": \"%s\",\n", timestamp);
+ printf("\t\t\"statistics\": [\n");
+
+ return 0;
+}
+
+/*
+ * Print the footer lines for the JSON print format
+ */
+static int json_print_footer(void)
+{
+ printf("\n\t\t]\n");
+ printf("\t}\n");
+ printf("}}\n");
+
+ return 0;
+}
+
+/*
+ * Print the interval header lines for the JSON print format
+ */
+static int json_print_interval_header(unsigned long interval_count,
+ const char *timestamp)
+{
+ if (interval_count > 1)
+ printf(",\n");
+ printf("\t\t\t{\n");
+ printf("\t\t\t\t\"interval\": %lu, \"timestamp\": \"%s\","
+ " \"devices\": [\n", interval_count, timestamp);
+
+ return 0;
+}
+
+/*
+ * Print the interval footer lines for the JSON print format
+ */
+static int json_print_interval_footer(void)
+{
+ if (!g.first_device)
+ printf("\n");
+ printf("\t\t\t\t]\n");
+ printf("\t\t\t}");
+
+ return 0;
+}
+
+/*
+ * Prints the separator lines in front of a device for the JSON print format
+ */
+static int json_print_device_header(bool is_apqn, uint8_t card,
+ uint8_t domain, const char *type,
+ const char *UNUSED(timestamp))
+{
+ if (!g.first_device)
+ printf(",\n");
+ printf("\t\t\t\t\t{");
+ if (is_apqn)
+ printf("\"device\": \"%02x.%04x\"", card,
+ domain);
+ else
+ printf("\"device\": \"%02x\"", card);
+ printf(", \"type\": \"%s\",\n", type);
+ printf("\t\t\t\t\t \"counters\": [\n");
+
+ return 0;
+}
+
+/*
+ * Prints the separator lines after a device for the JSON print format
+ */
+static int json_print_device_footer(void)
+{
+ if (!g.first_counter)
+ printf("\n");
+ printf("\t\t\t\t\t ]}");
+
+ return 0;
+}
+
+
+/**
+ * Prints the counter data for the JSON print format
+ */
+static int json_print_counter_data(bool UNUSED(is_apqn),
+ uint8_t UNUSED(card),
+ uint8_t UNUSED(domain),
+ const char *UNUSED(type),
+ const char *UNUSED(timestamp),
+ const char *name,
+ struct interval_values *vals)
+{
+ if (!g.first_counter)
+ printf(",\n");
+ printf("\t\t\t\t\t\t{\"counter\": \"%s\", \"ops\": %llu, "
+ "\"rate\": %.2f, \"utilization\": %.2f, \"duration\": %.9f}",
+ name, vals->count, vals->rate, vals->utilization * 100,
+ vals->duration);
+
+ return 0;
+}
+
+static int table_print_initialize(void)
+{
+ g.counter_rec = util_rec_new_wide("-");
+ util_rec_def(g.counter_rec, "time", UTIL_REC_ALIGN_LEFT, 20,
+ "TIMESTAMP");
+ util_rec_def(g.counter_rec, "device", UTIL_REC_ALIGN_LEFT, 7,
+ "DEVICE");
+ util_rec_def(g.counter_rec, "ops", UTIL_REC_ALIGN_RIGHT, 10,
+ "OPS");
+ util_rec_def(g.counter_rec, "rate", UTIL_REC_ALIGN_RIGHT, 12,
+ "RATE");
+ util_rec_def(g.counter_rec, "utilization", UTIL_REC_ALIGN_RIGHT,
+ 12, "UTILIZATION");
+ util_rec_def(g.counter_rec, "duration", UTIL_REC_ALIGN_RIGHT,
+ 15, "AVG.DURATION");
+ return 0;
+}
+
+static int table_print_terminate(void)
+{
+ util_rec_free(g.counter_rec);
+ return 0;
+}
+
+static int table_print_header(void)
+{
+ int rc;
+
+ rc = default_print_header();
+ if (rc != 0)
+ return rc;
+
+ util_rec_print_hdr(g.counter_rec);
+ return 0;
+}
+
+static int table_print_interval_footer(void)
+{
+ util_rec_print_separator(g.counter_rec);
+ return 0;
+}
+
+static int table_print_counter_data(bool is_apqn, uint8_t card, uint8_t domain,
+ const char *UNUSED(type),
+ const char *timestamp,
+ const char *UNUSED(name),
+ struct interval_values *vals)
+{
+ if (is_apqn)
+ util_rec_set(g.counter_rec, "device", "%02x.%04x", card,
+ domain);
+ else
+ util_rec_set(g.counter_rec, "device", "%02x", card);
+ util_rec_set(g.counter_rec, "time", "%s", timestamp);
+
+ util_rec_set(g.counter_rec, "ops", "%llu", vals->count);
+ util_rec_set(g.counter_rec, "rate", "%.2f", vals->rate);
+ util_rec_set(g.counter_rec, "utilization", "%.2f %%",
+ vals->utilization * 100);
+ if (vals->duration >= 1)
+ util_rec_set(g.counter_rec, "duration", "%.3f sec ",
+ vals->duration);
+ else if (vals->duration >= 0.001)
+ util_rec_set(g.counter_rec, "duration", "%.3f msec",
+ vals->duration * 1000);
+ else
+ util_rec_set(g.counter_rec, "duration", "%.3f usec",
+ vals->duration * 1000000);
+
+ util_rec_print(g.counter_rec);
+
+ return 0;
+}
+
+static int csv_print_initialize(void)
+{
+ /* Use a decimal point to not conflict with the colon separator char */
+ setlocale(LC_NUMERIC, "C");
+
+ g.counter_rec = util_rec_new_csv(",");
+ util_rec_def(g.counter_rec, "time", UTIL_REC_ALIGN_LEFT, 20,
+ "TIMESTAMP");
+ util_rec_def(g.counter_rec, "device", UTIL_REC_ALIGN_LEFT, 7,
+ "DEVICE");
+ util_rec_def(g.counter_rec, "ops", UTIL_REC_ALIGN_RIGHT, 10,
+ "OPS");
+ util_rec_def(g.counter_rec, "rate", UTIL_REC_ALIGN_RIGHT, 12,
+ "RATE");
+ util_rec_def(g.counter_rec, "utilization", UTIL_REC_ALIGN_RIGHT,
+ 12, "UTILIZATION");
+ util_rec_def(g.counter_rec, "duration", UTIL_REC_ALIGN_RIGHT,
+ 15, "AVG.DURATION");
+ return 0;
+}
+
+static int csv_print_terminate(void)
+{
+ util_rec_free(g.counter_rec);
+ return 0;
+}
+
+static int csv_print_header(void)
+{
+ util_rec_print_hdr(g.counter_rec);
+ return 0;
+}
+
+
+static int csv_print_counter_data(bool is_apqn, uint8_t card, uint8_t domain,
+ const char *UNUSED(type),
+ const char *timestamp,
+ const char *UNUSED(name),
+ struct interval_values *vals)
+{
+ if (is_apqn)
+ util_rec_set(g.counter_rec, "device", "%02x.%04x", card,
+ domain);
+ else
+ util_rec_set(g.counter_rec, "device", "%02x", card);
+ util_rec_set(g.counter_rec, "time", "%s", timestamp);
+ util_rec_set(g.counter_rec, "ops", "%llu", vals->count);
+ util_rec_set(g.counter_rec, "rate", "%.2f", vals->rate);
+ util_rec_set(g.counter_rec, "utilization", "%.2f %%",
+ vals->utilization * 100);
+ util_rec_set(g.counter_rec, "duration", "%.9f", vals->duration);
+
+ util_rec_print(g.counter_rec);
+
+ return 0;
+}
+
+/*
+ * Calculates number of ops, utilization, duration and rate of an
+ * interval from the timer values, scale and interval time.
+ */
+static void calc_interval_values(struct chsc_cmb_entry *current,
+ struct chsc_cmb_entry *previous,
+ float scale,
+ float interval_time,
+ struct interval_values *result)
+{
+ u64 tdiff;
+
+ tdiff = current->t - previous->t;
+ result->count = current->c - previous->c;
+ result->utilization = (double)(tdiff) * scale / interval_time;
+ if (result->count > 0)
+ result->duration = (double)(tdiff) * scale / result->count;
+ else
+ result->duration = 0;
+ result->rate = (double)result->count / interval_time;
+}
+
+/*
+ * Print the measurement data of an interval
+ */
+static int print_interval_data(struct interval_data *data,
+ const char *timestamp, float interval_time)
+{
+ struct chsc_cmb_entry total_current;
+ struct chsc_cmb_entry total_previous;
+ struct interval_values vals;
+ const char *type, *counter;
+ uint32_t mask = 0x80000000;
+ bool totals_found = false;
+ size_t len;
+ int i, rc;
+
+ len = get_cmb_length(&data->current);
+ type = get_card_name(data->current.header.ct, data->current.header.mt);
+
+ rc = pr_call(print_device_header)(data->current.header.format == 1,
+ data->current.header.ax,
+ data->current.header.dx, type,
+ timestamp);
+ if (rc != 0)
+ return rc;
+
+ memset(&total_current, 0, sizeof(total_current));
+ memset(&total_previous, 0, sizeof(total_previous));
+
+ g.first_counter = true;
+
+ for (i = 0; i < 32 &&
+ offsetofend(struct chsc_cmb_area, entries[i]) <= len; i++) {
+ if (data->current.header.v & mask) {
+ if (is_counter_totals(data->current.header.ct,
+ data->current.header.mt, i)) {
+ total_current.t = data->current.entries[i].t;
+ total_current.c = data->current.entries[i].c;
+ total_previous.t = data->previous.entries[i].t;
+ total_previous.c = data->previous.entries[i].c;
+ totals_found = true;
+ } else if (!totals_found) {
+ total_current.t += data->current.entries[i].t;
+ total_current.c += data->current.entries[i].c;
+ total_previous.t += data->previous.entries[i].t;
+ total_previous.c += data->previous.entries[i].c;
+ }
+
+ if (g.only_totals)
+ continue;
+
+ calc_interval_values(&data->current.entries[i],
+ &data->previous.entries[i],
+ data->current.header.s,
+ interval_time,
+ &vals);
+
+ counter = get_counter_name(data->current.header.ct,
+ data->current.header.mt, i);
+
+ rc = pr_call(print_counter_data)(
+ data->current.header.format == 1,
+ data->current.header.ax,
+ data->current.header.dx, type,
+ timestamp, counter, &vals);
+ if (rc != 0)
+ break;
+
+ g.first_counter = false;
+ }
+ mask >>= 1;
+ }
+
+ if (!g.no_totals) {
+ rc = pr_call(print_counter_separator)();
+ if (rc != 0)
+ return rc;
+
+ calc_interval_values(&total_current, &total_previous,
+ data->current.header.s, interval_time,
+ &vals);
+
+ rc = pr_call(print_counter_data)(
+ data->current.header.format == 1,
+ data->current.header.ax,
+ data->current.header.dx, type,
+ timestamp, "Total", &vals);
+ if (rc != 0)
+ return rc;
+ }
+
+ rc = pr_call(print_device_footer)();
+ if (rc != 0)
+ return rc;
+
+ return 0;
+}
+
+/*
+ * Print the measured data
+ */
+static int print_measurement_data(unsigned long interval_count,
+ float interval_time, const char *timestamp)
+{
+ bool header_printed = false;
+ struct interval_data *dd;
+ struct card_data *cd;
+ int card, domain, rc;
+
+ g.first_device = true;
+ for (card = 0; card < NUM_CARDS; card++) {
+ cd = g.cards[card];
+ if (cd == NULL)
+ continue;
+
+ if (!cd->data.current_valid) {
+ /* Not update in last interval -> free it */
+ free_card_data(cd);
+ g.cards[card] = NULL;
+
+ pr_verbose("Card %02x removed", card);
+ continue;
+ }
+
+ if (cd->data.previous_valid) {
+ if (memcmp(&cd->data.current.header,
+ &cd->data.previous.header,
+ sizeof(struct chsc_cmb_header)) != 0) {
+ free_card_data(cd);
+ g.cards[card] = NULL;
+
+ pr_verbose("CMB header mismatch, card %02x "
+ "removed", card);
+ continue;
+ }
+
+ if (!header_printed) {
+ rc = pr_call(print_interval_header)(
+ interval_count, timestamp);
+ if (rc != 0)
+ return rc;
+ header_printed = true;
+ }
+
+ rc = print_interval_data(&cd->data, timestamp,
+ interval_time);
+ if (rc != 0)
+ return rc;
+
+ g.first_device = false;
+ }
+
+ cd->data.current_valid = false;
+
+ for (domain = 0; domain < NUM_DOMAINS; domain++) {
+ dd = cd->domains[domain];
+ if (dd == NULL)
+ continue;
+
+ if (!dd->current_valid) {
+ /* Not update in last interval -> free it */
+ free(dd);
+ cd->domains[domain] = NULL;
+
+ pr_verbose("APQN %02x.%04x removed", card,
+ domain);
+ }
+
+ if (dd->previous_valid) {
+ if (memcmp(&dd->current.header,
+ &dd->previous.header,
+ sizeof(struct chsc_cmb_header))) {
+ free(dd);
+ cd->domains[domain] = NULL;
+
+ pr_verbose("CMB header mismatch, APQN "
+ "%02x.%04x removed", card,
+ domain);
+ continue;
+ }
+
+ rc = print_interval_data(dd, timestamp,
+ interval_time);
+ if (rc != 0)
+ return rc;
+ }
+
+ dd->current_valid = false;
+ }
+ }
+
+ if (header_printed) {
+ rc = pr_call(print_interval_footer)();
+ if (rc != 0)
+ return rc;
+ } else if (interval_count > 0) {
+ pr_verbose("No data was reported in this interval");
+ warnx("Failed to get card crypto measurement data: %s",
+ strerror(ENODEV));
+ return -ENODEV;
+ }
+
+ return 0;
+}
+
+/*
+ * Perform the measurement in intervals
+ */
+static int perform_measurement(void)
+{
+ struct timeval tv_current, tv_previous;
+ struct sigaction alrm_act, int_act;
+ unsigned long interval_count = 0;
+ float interval_time;
+ char timestamp[64];
+ struct tm *tm;
+ int rc;
+
+ /* Set a handler for SIGINT/SIGTERM */
+ memset(&int_act, 0, sizeof(int_act));
+ int_act.sa_handler = int_handler;
+ sigaction(SIGINT, &int_act, NULL);
+ sigaction(SIGTERM, &int_act, NULL);
+
+ /* Set a handler for SIGALRM */
+ memset(&alrm_act, 0, sizeof(alrm_act));
+ alrm_act.sa_handler = alarm_handler;
+ sigaction(SIGALRM, &alrm_act, NULL);
+
+ rc = pr_call(print_initialize)();
+ if (rc != 0)
+ return rc;
+
+ rc = pr_call(print_header)();
+ if (rc != 0)
+ return 0;
+
+ alarm(g.interval);
+
+ memset(&tv_current, 0, sizeof(tv_current));
+ while (!quit) {
+ pr_verbose("Interval %lu", interval_count);
+
+ tv_previous = tv_current;
+ rc = gettimeofday(&tv_current, NULL);
+ if (rc != 0)
+ break;
+
+ tm = localtime(&tv_current.tv_sec);
+ if (tm == NULL)
+ break;
+ strftime(timestamp, sizeof(timestamp), "%x %X", tm);
+ interval_time = time_diff(&tv_previous, &tv_current);
+
+ rc = get_card_measurement_data();
+ if (rc != 0)
+ break;
+
+ rc = print_measurement_data(interval_count, interval_time,
+ timestamp);
+ if (rc != 0)
+ break;
+
+ if (g.count > 0 && interval_count >= g.count) {
+ pr_verbose("Interval limit reached");
+ break;
+ }
+ interval_count++;
+
+ if (quit)
+ break;
+
+ pause();
+ }
+
+ if (quit)
+ pr_verbose("Measurement stopped by user");
+
+ alarm(0);
+ memset(&alrm_act, 0, sizeof(alrm_act));
+ alrm_act.sa_handler = SIG_DFL;
+ sigaction(SIGALRM, &alrm_act, NULL);
+
+ rc = pr_call(print_footer)();
+ if (rc != 0)
+ return 0;
+
+ rc = pr_call(print_terminate)();
+ if (rc != 0)
+ return rc;
+
+ return 0;
+}
+
+/*
+ * Parse the type mapping specification:
+ * TYPE:MODE=TYPE:MODE[,TYPE:MODE=TYPE:MODE[,...]]
+ */
+static int parse_type_mapping(char *mapping)
+{
+ unsigned int from_type, to_type, from_mode, to_mode;
+ struct type_mapping *map;
+ char *tok;
+
+ tok = strtok(mapping, ",");
+ while (tok != NULL) {
+ if (sscanf(tok, "%u:%u=%u:%u", &from_type, &from_mode,
+ &to_type, &to_mode) != 4) {
+ warnx("Invalid type mapping: %s", tok);
+ return -EINVAL;
+ }
+
+ pr_verbose("from_type: %u from_mode: %u to_type: %u "
+ "to_mode: %u", from_type, from_mode, to_type,
+ to_mode);
+
+ if (from_type < NUM_CRYPTO_TYPES &&
+ crypto_types[from_type].name != NULL &&
+ from_mode < crypto_types[from_type].num_modes &&
+ crypto_types[from_type].modes[from_mode].counters != NULL) {
+ warnx("Cannot map a known type/mode to another "
+ "type/mode: %s", tok);
+ return -EINVAL;
+ }
+
+ if (to_type >= NUM_CRYPTO_TYPES ||
+ crypto_types[to_type].name == NULL ||
+ to_mode >= crypto_types[to_type].num_modes ||
+ crypto_types[to_type].modes[to_mode].counters == NULL) {
+ warnx("Cannot map a type/mode to an unknown "
+ "type/mode: %s", tok);
+ return -EINVAL;
+ }
+
+ map = util_malloc(sizeof(struct type_mapping));
+ map->from_type = from_type;
+ map->from_mode = from_mode;
+ map->to_type = to_type;
+ map->to_mode = to_mode;
+
+ map->next = g.type_mapping;
+ g.type_mapping = map;
+
+ tok = strtok(NULL, ",");
+ }
+
+ return 0;
+}
+
+static void free_type_mapping(void)
+{
+ struct type_mapping *map = g.type_mapping;
+ struct type_mapping *next;
+
+ while (map != NULL) {
+ next = map->next;
+ free(map);
+ map = next;
+ }
+}
+
+static int add_device_selection(const char *device_id)
+{
+ int card = -1, domain = -1;
+ struct device_selection *dev;
+
+ pr_verbose("device_id: '%s'", device_id);
+
+ /* check for 'card[.domain]' specification */
+ if (sscanf(device_id, "%x.%x", &card, &domain) >= 1) {
+ pr_verbose("card: %d domain: %d", card, domain);
+ if (card < 0 || card > g.max_card_used) {
+ warnx("Invalid card specified: %s", device_id);
+ return -EINVAL;
+ }
+ g.card_mask[MASK_WORD_NO(card)] |= MASK_BIT(card);
+ g.min_card = MIN(g.min_card, card);
+ g.max_card = MAX(g.max_card, card);
+
+ if (domain >= 0) {
+ if (domain > NUM_DOMAINS) {
+ warnx("Invalid domain specified: %s",
+ device_id);
+ return -EINVAL;
+ }
+ g.domain_mask[MASK_WORD_NO(domain)] |=
+ MASK_BIT(domain);
+ g.min_domain = MIN(g.max_domain, domain);
+ g.max_domain = MAX(g.max_domain, domain);
+ } else {
+ memset(g.domain_mask, 0xff, sizeof(g.domain_mask));
+ g.min_domain = 0;
+ g.max_domain = NUM_DOMAINS - 1;
+ }
+
+ dev = util_malloc(sizeof(struct device_selection));
+ dev->card = card;
+ dev->domain = domain;
+ dev->next = g.dev_selection;
+ g.dev_selection = dev;
+
+ return 0;
+ }
+ /* check for '.domain' specification */
+ if (device_id[0] == '.' &&
+ sscanf(device_id + 1, "%x", &domain) == 1) {
+ pr_verbose("domain: %d", domain);
+ if (domain < 0 || domain > NUM_DOMAINS) {
+ warnx("Invalid domain specified: %s", device_id);
+ return -EINVAL;
+ }
+
+ g.domain_mask[MASK_WORD_NO(domain)] |= MASK_BIT(domain);
+ g.min_domain = MIN(g.max_domain, domain);
+ g.max_domain = MAX(g.max_domain, domain);
+ memset(g.card_mask, 0xff, sizeof(g.card_mask));
+ g.min_card = 0;
+ g.max_card = g.max_card_used;
+
+ dev = util_malloc(sizeof(struct device_selection));
+ dev->card = -1;
+ dev->domain = domain;
+ dev->next = g.dev_selection;
+ g.dev_selection = dev;
+
+ return 0;
+ }
+
+ warnx("Invalid device ID specified: %s", device_id);
+ return -EINVAL;
+}
+
+/*
+ * Frees the device selection list
+ */
+static void free_device_selection(void)
+{
+ struct device_selection *dev = g.dev_selection;
+ struct device_selection *next;
+
+ while (dev != NULL) {
+ next = dev->next;
+ free(dev);
+
+ dev = next;
+ }
+}
+
+/*
+ * Build the AP and domain selection mask from the specified device ID's.
+ */
+static int parse_device_selection(void)
+{
+ int i, rc;
+
+ g.min_card = NUM_CARDS - 1;
+ g.max_card = 0;
+ g.min_domain = NUM_DOMAINS - 1;
+ g.max_domain = 0;
+
+ for (i = 0; g.device_ids[i] != NULL; i++) {
+ rc = add_device_selection(g.device_ids[i]);
+ if (rc != 0)
+ return rc;
+ }
+
+ if (i == 0) {
+ /* No device-IDs specified */
+ memset(g.card_mask, 0xff, sizeof(g.card_mask));
+ g.min_card = 0;
+ g.max_card = g.max_card_used;
+ memset(g.domain_mask, 0xff, sizeof(g.domain_mask));
+ g.min_domain = 0;
+ g.max_domain = NUM_DOMAINS - 1;
+ }
+
+ pr_verbose("Min card: %u, max card: %u", g.min_card, g.max_card);
+ pr_verbose("Min domain: %u, max domain: %u", g.min_domain,
+ g.max_domain);
+
+ return 0;
+}
+
+/*
+ * Entry point
+ */
+int main(int argc, char *argv[])
+{
+ char *endp;
+ int c, rc;
+
+ util_prg_init(&prg);
+ util_opt_init(opt_vec, NULL);
+
+ while (1) {
+ c = util_opt_getopt_long(argc, argv);
+ if (c == -1)
+ break;
+ switch (c) {
+ case 'i':
+ g.interval = strtoll(optarg, &endp, 0);
+ if (*optarg == '\0' || *endp != '\0' ||
+ g.interval <= 0 ||
+ (g.interval == LLONG_MAX && errno == ERANGE)) {
+ warnx("Invalid value for '--interval'|"
+ "'-i': '%s'", optarg);
+ util_prg_print_parse_error();
+ return EXIT_FAILURE;
+ }
+ break;
+ case 'c':
+ g.count = strtoull(optarg, &endp, 0);
+ if (*optarg == '\0' || *endp != '\0' || g.count == 0 ||
+ (g.count == LLONG_MAX && errno == ERANGE)) {
+ warnx("Invalid value for '--count'|"
+ "'-c': '%s'", optarg);
+ util_prg_print_parse_error();
+ return EXIT_FAILURE;
+ }
+ break;
+ case 'o':
+ if (strcasecmp(optarg, "JSON") == 0) {
+ g.print_funcs = &json_print;
+ } else if (strcasecmp(optarg, "TABLE") == 0) {
+ g.only_totals = true;
+ g.print_funcs = &table_print;
+ } else if (strcasecmp(optarg, "CSV") == 0) {
+ g.only_totals = true;
+ g.print_funcs = &csv_print;
+ } else {
+ warnx("Invalid value for '--output'|"
+ "'-o': '%s'", optarg);
+ util_prg_print_parse_error();
+ return EXIT_FAILURE;
+ }
+ break;
+ case 't':
+ g.no_totals = true;
+ break;
+ case 'T':
+ g.only_totals = true;
+ break;
+ case 'a':
+ g.no_apqn = true;
+ break;
+ case 'M':
+ g.map_type = optarg;
+ break;
+ case 'A':
+ g.all = true;
+ break;
+ case 'O':
+ g.only_online = true;
+ break;
+ case 'V':
+ g.verbose = true;
+ break;
+ case 'h':
+ util_prg_print_help();
+ util_opt_print_help();
+ print_adapter_id_help();
+ return EXIT_SUCCESS;
+ case 'v':
+ util_prg_print_version();
+ return EXIT_SUCCESS;
+ default:
+ util_opt_print_parse_error(c, argv);
+ return EXIT_FAILURE;
+ }
+ }
+
+ /* remaining positional args are device IDs */
+ g.device_ids = &argv[optind];
+
+ if (g.only_totals && g.no_totals) {
+ warnx("Either --no-totals or --only-totals can be specified, "
+ "but not both");
+ return EXIT_FAILURE;
+ }
+
+ if (g.only_online && g.all) {
+ warnx("Either --only-online or --all can be specified, "
+ "but not both");
+ return EXIT_FAILURE;
+ }
+
+ pr_verbose("Interval: %ld Count: %ld", g.interval, g.count);
+
+ rc = get_max_card_index(&g.max_card_used);
+ if (rc != 0) {
+ rc = EXIT_FAILURE;
+ goto out;
+ }
+
+ rc = parse_device_selection();
+ if (rc != 0) {
+ rc = EXIT_FAILURE;
+ goto out;
+ }
+
+ if (g.map_type != NULL) {
+ rc = parse_type_mapping(g.map_type);
+ if (rc != 0) {
+ rc = EXIT_FAILURE;
+ goto out;
+ }
+ }
+
+ g.chsc_fd = open(CHSC_DEVICE, O_RDWR);
+ if (g.chsc_fd < 0) {
+ warnx("File '%s:' %s", CHSC_DEVICE, strerror(errno));
+ return EXIT_FAILURE;
+ }
+ pr_verbose("Device '%s' has been opened successfully", CHSC_DEVICE);
+
+ /* Don't buffer data if redirected to a pipe */
+ setbuf(stdout, NULL);
+
+ rc = perform_measurement();
+ if (rc != 0) {
+ rc = EXIT_FAILURE;
+ goto out;
+ }
+out:
+ if (g.chsc_fd >= 0)
+ close(g.chsc_fd);
+ free_device_selection();
+ free_type_mapping();
+ free_interval_data();
+
+ return rc;
+}
+
--
2.21.3
From c0670b62ccd7a9f1f1b3a8a49114c2e3c673ecd9 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Jan=20H=C3=B6ppner?= <hoeppner@linux.ibm.com>
Date: Mon, 1 Apr 2019 09:53:06 +0200
Subject: [PATCH 21/44] zpcictl: Check for regular directory (#1695001)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
In case a regular directory was specified, rather than a device node,
the check if the device exists will pass. The following code paths then
assume a slot id was specified. This in turn may lead to a buffer
overflow when the device data is copied to to the zpci_device struct.
Check if the specified path is a regular directory and prevent a
possible later buffer overflow and copying wrong data respectively.
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
(cherry picked from commit c7a255fcd9433bba6dd516f6b28a139bbc5351d3)
---
zpcictl/zpcictl.c | 6 +++++-
1 file changed, 5 insertions(+), 1 deletion(-)
diff --git a/zpcictl/zpcictl.c b/zpcictl/zpcictl.c
index 7cfa0d3..f2a55ad 100644
--- a/zpcictl/zpcictl.c
+++ b/zpcictl/zpcictl.c
@@ -249,8 +249,12 @@ static int device_exists(char *dev)
char *path;
int rc = 0;
+ /* In case a device node is specified, this will be sufficiant */
+ if (util_path_exists(dev) && !util_path_is_dir(dev))
+ return 1;
+
path = util_path_sysfs("bus/pci/devices/%s", dev);
- if (util_path_exists(path) || util_path_exists(dev))
+ if (util_path_exists(path))
rc = 1;
free(path);
--
2.21.3
From 9f0fe2e3d5bcc079577700d778e3f1a58fced510 Mon Sep 17 00:00:00 2001
From: Thomas Richter <tmricht@linux.ibm.com>
Date: Wed, 10 Jul 2019 13:01:09 +0200
Subject: [PATCH 22/44] cpumf: Add support for CPU-Measurement Facility
counters SVN 6 (#1683276)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Add support for CPU-Measurement facility counter second version
number 6. This adds some more counters to the crypto counter set.
Extended counter set is the same as for z14.
Signed-off-by: Thomas Richter <tmricht@linux.ibm.com>
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
(cherry picked from commit f110a77b68d46f10cab8847cf73fe92686ae7d11)
---
cpumf/Makefile | 2 +-
cpumf/bin/cpumf_helper.in | 7 +++-
...svn-generic.ctr => cpum-cf-csvn-12345.ctr} | 0
cpumf/data/cpum-cf-csvn-6.ctr | 33 +++++++++++++++++++
cpumf/data/cpum-cf-hw-counter.map | 6 +++-
5 files changed, 45 insertions(+), 3 deletions(-)
rename cpumf/data/{cpum-cf-csvn-generic.ctr => cpum-cf-csvn-12345.ctr} (100%)
create mode 100644 cpumf/data/cpum-cf-csvn-6.ctr
diff --git a/cpumf/Makefile b/cpumf/Makefile
index 3fc4c0f..2e11810 100644
--- a/cpumf/Makefile
+++ b/cpumf/Makefile
@@ -6,7 +6,7 @@ include ../common.mak
CPUMF_DATADIR = $(TOOLS_DATADIR)/cpumf
DATA_FILES = cpum-cf-hw-counter.map \
cpum-cf-cfvn-1.ctr cpum-cf-cfvn-3.ctr \
- cpum-cf-csvn-generic.ctr \
+ cpum-cf-csvn-12345.ctr cpum-cf-csvn-6.ctr \
cpum-cf-extended-z10.ctr cpum-cf-extended-z196.ctr \
cpum-cf-extended-zEC12.ctr cpum-sf-modes.ctr \
cpum-cf-extended-z13.ctr cpum-cf-extended-z14.ctr
diff --git a/cpumf/bin/cpumf_helper.in b/cpumf/bin/cpumf_helper.in
index 4757725..e1da017 100755
--- a/cpumf/bin/cpumf_helper.in
+++ b/cpumf/bin/cpumf_helper.in
@@ -266,7 +266,12 @@ sub cpumf_load_ctrdef($;$)
# List of "generic" counter sets
my @def = ();
push @def, "cfvn-" . $version->{cfvn};
- push @def, "csvn-generic";
+ if ($version->{csvn} >= 1 && $version->{csvn} <= 6) {
+ push @def, "csvn-12345";
+ }
+ if ($version->{csvn} == 6) {
+ push @def, "csvn-6";
+ }
my $h = {};
# Load counter set definition
diff --git a/cpumf/data/cpum-cf-csvn-generic.ctr b/cpumf/data/cpum-cf-csvn-12345.ctr
similarity index 100%
rename from cpumf/data/cpum-cf-csvn-generic.ctr
rename to cpumf/data/cpum-cf-csvn-12345.ctr
diff --git a/cpumf/data/cpum-cf-csvn-6.ctr b/cpumf/data/cpum-cf-csvn-6.ctr
new file mode 100644
index 0000000..4ea3b44
--- /dev/null
+++ b/cpumf/data/cpum-cf-csvn-6.ctr
@@ -0,0 +1,33 @@
+Counter: 80 Name:ECC_FUNCTION_COUNT
+Short-Description:ECC Function Count
+Description:
+This counter counts the
+total number of the elliptic-curve cryptography (ECC)
+functions issued by the CPU.
+.
+Counter: 81 Name:ECC_CYCLES_COUNT
+Short-Description:ECC Cycles Count
+Description:
+This counter counts the total
+number of CPU cycles when the ECC coprocessor is
+busy performing the elliptic-curve cryptography
+(ECC) functions issued by the CPU.
+.
+Counter: 82 Name:ECC_BLOCKED_FUNCTION_COUNT
+Short-Description:Ecc Blocked Function Count
+Description:
+This counter
+counts the total number of the elliptic-curve
+cryptography (ECC) functions that are issued by the CPU
+and are blocked because the ECC coprocessor is
+busy performing a function issued by another CPU.
+.
+Counter: 83 Name:ECC_BLOCKED_CYCLES_COUNT
+Short-Description:ECC Blocked Cycles Count
+Description:
+This counter counts
+the total number of CPU cycles blocked for the elliptic-curve
+cryptography (ECC) functions issued by the
+CPU because the ECC coprocessor is busy perform-
+ing a function issued by another CPU.
+.
diff --git a/cpumf/data/cpum-cf-hw-counter.map b/cpumf/data/cpum-cf-hw-counter.map
index a8a2846..5729b95 100644
--- a/cpumf/data/cpum-cf-hw-counter.map
+++ b/cpumf/data/cpum-cf-hw-counter.map
@@ -14,7 +14,8 @@
'cfvn-3' => 'cpum-cf-cfvn-3.ctr',
# CSVN
- 'csvn-generic' => 'cpum-cf-csvn-generic.ctr',
+ 'csvn-12345' => 'cpum-cf-csvn-12345.ctr',
+ 'csvn-6' => 'cpum-cf-csvn-6.ctr',
# Extended counters
2097 => 'cpum-cf-extended-z10.ctr',
@@ -27,4 +28,7 @@
2965 => 'cpum-cf-extended-z13.ctr',
3906 => 'cpum-cf-extended-z14.ctr',
3907 => 'cpum-cf-extended-z14.ctr',
+ # Identical with z14
+ 8561 => 'cpum-cf-extended-z14.ctr',
+ 8562 => 'cpum-cf-extended-z14.ctr',
};
--
2.21.3
From f58ee5580cd6e357dce488d9cd3d999e7ad61f16 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Dan=20Hor=C3=A1k?= <dan@danny.cz>
Date: Fri, 19 Jul 2019 09:41:08 +0200
Subject: [PATCH 23/44] ziomon: fix utilization recording with multi-digit scsi
hosts (#1731203)
Description: ziomon: fix utilization recording with multi-digit scsi hosts
Symptom: During running of ziomon script, user receives ziomon
error: "ziomon: Number of LUNs does not match number of
devices: 2 devices and 1 LUNs". Also user receives
ziomon_util error: "ziomon_util: Path does not exist:
/sys/class/scsi_host/host0/utilization - correct kernel
version?". After collection of records, user receives
error during using of ziorep_utilization or
ziorep_traffic: "ziorep_traffic: Could not retrieve
initial data - data files corrupted or broken, or the
.agg file is missing."
Problem: s390-tools-1.9.0 introduced a new way of recognizing of
multipath device paths with using of sed command
invocation in ziomon script. With this new way of
recognizing, if there are paths, related to multipath
device, with SCSI host ID longer, than one digit,
ziomon incorrectly parses the multipath -l command
output. It erroneously cuts off all but the least
significant digit of the SCSI host ID (H) of paths in
H:B:T:L format (Host:Bus:Target:Lun). This leads to
passing of hosts (-a) and paths (-l) with wrong
SCSI host ID to ziomon_util. In turn ziomon_util cannot
recognize hosts with non-existing SCSI host ID and
issues an error. Also, wrong sed command invocation
could lead to receiving of duplicate LUNs by ziomon
after parsing of multipath -l command output. Then
ziomon excludes duplicates from WRP_LUNS, which leads
to mismatch between number of LUNs and number of
detected block devices and issues ziomon script error,
without starting ziomon_util and without writing to
specified log file.
Solution: The regular expression to match a path in H:B:T:L
format started with a greedy ".*", which erroneously
consumed parts of the SCSI host ID (H). The solution
is to replace the greedy ".*" by "[^0-9]*", so that sed
command does not consume parts of the SCSI host ID any
more.
Reproduction: Create such multipath device, that its related path
contains LUN with SCSI host ID longer, than one digit.
Example for wrong SCSI host ID:
$ multipath -l
mpathc (36005076307ffc5e300000000000083f5) dm-2 IBM ...
size=20G features='1 queue_if_no_path' hwhandler='0'...
`-+- policy='service-time 0' prio=0 status=active
|- 10:0:0:1089814659 sdb 8:16 active undef running
`- 11:0:0:1089814659 sdf 8:80 active undef running
Example for duplicate SCSI host ID:
$ multipath -l
mpathd (36005076307ffc5e300000000000083f4) dm-3 IBM ...
size=20G features='1 queue_if_no_path' hwhandler='0'...
`-+- policy='service-time 0' prio=0 status=active
|- 10:0:0:1089749123 sda 8:0 active undef running
`- 0:0:0:1089749123 sdd 8:48 active undef running
Use ziomon tool with one of the described multipath
device as input.
Upstream-ID: f2dee9f542439cf07e00df4296b05a47b81e2469
---
ziomon/ziomon | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/ziomon/ziomon b/ziomon/ziomon
index 87c4bc4..26fa269 100755
--- a/ziomon/ziomon
+++ b/ziomon/ziomon
@@ -514,7 +514,7 @@ function check_for_multipath_devices() {
(( i+=2 ));
while [[ `echo "${mp_arr[$i]:0:1}" | grep -ve "[0-9a-zA-Z]"` ]] && [ $i -lt ${#mp_arr[@]} ]; do
if [ `echo ${mp_arr[$i]} | grep -e "[0-9]\{1,\}:[0-9]\{1,\}:[0-9]\{1,\}:[0-9]\{1,\}" | wc -l` -ne 0 ]; then
- line="`echo ${mp_arr[$i]} | sed 's/.*\([0-9]\{1,\}:[0-9]\{1,\}:[0-9]\{1,\}:[0-9]\{1,\}\)/\1/'`";
+ line="`echo ${mp_arr[$i]} | sed 's/[^0-9]*\([0-9]\{1,\}:[0-9]\{1,\}:[0-9]\{1,\}:[0-9]\{1,\}\)/\1/'`";
checked_devs[${#checked_devs[@]}]=`echo $line | awk '{print "/dev/"$2}'`;
ddebug " adding ${checked_devs[${#checked_devs[@]}-1]}";
WRP_HOST_ADAPTERS[${#WRP_HOST_ADAPTERS[@]}]="host${line%%:*}";
--
2.21.3
From ef93298c3f0a8d318c0568f3bec10b76279bb15b Mon Sep 17 00:00:00 2001
From: Peter Oberparleiter <oberpar@linux.ibm.com>
Date: Tue, 12 Mar 2019 14:05:17 +0100
Subject: [PATCH 24/44] zdev: Do not export inacceptable attribute values
(#1731960)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
For some device driver SysFS attributes, values read may not be
acceptable input values for that attribute.
An example would be the group of qeth VNICC attributes that return "n/a"
when VNICC setup is not supported, but only accept "0" and "1" as valid
values that can be written to it.
This leads to errors such as the following when data for such attributes
is imported:
# chzdev f500 --import test.conf
Importing configuration data from test.conf
QETH device 0.0.f500:0.0.f501:0.0.f502 configure failed
Error: Invalid value for qeth attribute: vnicc/flooding=n/a (*)
Acceptable values:
- Integers in the range 0 - 1
Use 'chzdev qeth --help-attribute vnicc/flooding' for more information
Note: You can use --force to override safety checks (*)
To fix this, change chzdev's --export function to skip any attribute
value that is not acceptable for that attribute.
Fixes: e831269e7433 ("zdev: Add support for VNIC Characteristics")
Signed-off-by: Peter Oberparleiter <oberpar@linux.ibm.com>
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
(cherry picked from commit 4ff6519961965deb278093c83ea0f46fb7c7c205)
---
zdev/src/export.c | 4 ++++
1 file changed, 4 insertions(+)
diff --git a/zdev/src/export.c b/zdev/src/export.c
index f1acb8a..a66db0b 100644
--- a/zdev/src/export.c
+++ b/zdev/src/export.c
@@ -78,6 +78,10 @@ static bool is_exportable(struct setting *s, config_t config)
/* Skip values that cannot be determined. */
return false;
}
+ if (!attrib_check_value(a, s->value)) {
+ /* Skip values that are not acceptable input values. */
+ return false;
+ }
if (!attrib_match_default(s->attrib, s->value)) {
/* All non-default values should be exported. */
return true;
--
2.21.3
From de7f4178f2178268ed65292bf5e1f42894832d9a Mon Sep 17 00:00:00 2001
From: Stefan Haberland <sth@linux.ibm.com>
Date: Wed, 17 Jul 2019 17:26:40 +0200
Subject: [PATCH 25/44] zipl: do not overwrite BOOT_IMAGE entry (#1728677)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
The zipl internal loader adds a variable BOOT_IMAGE= to the commandline
so that it is visible in the operating system which menu entry has been
chosen.
This entry was overwritten by the stage3 parameter page.
Fix by re-arranging the internal memory layout and putting the command
line extra param, which contains the BOOT_IMAGE entry, at 0xe000.
This location is available because less than one page is used for the
stack.
Fixes: https://github.com/ibm-s390-tools/s390-tools/issues/67
Signed-off-by: Stefan Haberland <sth@linux.ibm.com>
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
(cherry picked from commit 8bf5f8d0e293b294c9b7aaebea80e92634c7564d)
---
zipl/boot/Makefile | 4 ++--
zipl/boot/menu.h | 2 +-
zipl/boot/stage2.lds | 3 ++-
zipl/boot/stage3.h | 2 +-
4 files changed, 6 insertions(+), 5 deletions(-)
diff --git a/zipl/boot/Makefile b/zipl/boot/Makefile
index a049797..0faf3ec 100644
--- a/zipl/boot/Makefile
+++ b/zipl/boot/Makefile
@@ -6,7 +6,7 @@ CFLAGS_BOOT = $(NO_PIE_CFLAGS) -Os -g -I../include -D__ASSEMBLY__ \
-fno-builtin -ffreestanding -fno-asynchronous-unwind-tables \
-fno-delete-null-pointer-checks \
-fexec-charset=IBM1047 -m64 -mpacked-stack \
- -mstack-size=8192 -mstack-guard=128 -msoft-float \
+ -mstack-size=4096 -mstack-guard=128 -msoft-float \
-W -Wall -Wformat-security
FILES = fba0.bin fba1b.bin fba2.bin \
@@ -90,7 +90,7 @@ stage3.bin: stage3.exec
--only-section=.stage2dump.tail \
--only-section=.eckd2dump_mv.tail \
--only-section=.fixup \
- --pad-to=0xf000 \
+ --pad-to=0xe000 \
$< $@
data.o: $(FILES)
diff --git a/zipl/boot/menu.h b/zipl/boot/menu.h
index 83338a8..1b103a8 100644
--- a/zipl/boot/menu.h
+++ b/zipl/boot/menu.h
@@ -15,7 +15,7 @@
#include "stage2.h"
/* address of extra command line */
-#define COMMAND_LINE_EXTRA (0xA000-0x400)
+#define COMMAND_LINE_EXTRA 0xE000
/* max command line length */
#define COMMAND_LINE_SIZE 896
diff --git a/zipl/boot/stage2.lds b/zipl/boot/stage2.lds
index 41efa79..34ed014 100644
--- a/zipl/boot/stage2.lds
+++ b/zipl/boot/stage2.lds
@@ -12,7 +12,8 @@
* 0x6000-0x8fff Memory allocation (heap)
* 0x9000-0x9fff Memory to load stage3 parameter to
* 0xa000-0xdfff Memory to load stage3 to
- * 0xe000-0xffff Stack
+ * 0xe000-0xe3ff command line extra
+ * 0xe400-0xffff Stack
*
* Special memory locations
* ------------------------
diff --git a/zipl/boot/stage3.h b/zipl/boot/stage3.h
index 91477b1..3a02001 100644
--- a/zipl/boot/stage3.h
+++ b/zipl/boot/stage3.h
@@ -22,7 +22,7 @@
#define OLDMEM_SIZE 0x10420
#define COMMAND_LINE 0x10480
#define COMMAND_LINE_SIZE 896
-#define COMMAND_LINE_EXTRA (0xA000-0x400)
+#define COMMAND_LINE_EXTRA 0xE000
#define STAGE3_FLAG_SCSI 0x0001000000000000ULL
#define STAGE3_FLAG_KDUMP 0x0002000000000000ULL
--
2.21.3
From d25f635ebdb0f2faa8ca018859c3223c3dac4d0f Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Jan=20H=C3=B6ppner?= <hoeppner@linux.ibm.com>
Date: Wed, 31 Jul 2019 15:25:00 +0200
Subject: [PATCH 26/44] fdasd: Fix exit status in error cases (#1734816)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
In error cases, fdasd returns -1 which results in the return value 255.
This is due to the fact that only the low-order 8 bits are used for the
status value. See 2.13 Status Information [1] in the POSIX standard and
the exit() POSIX man page [2] for more details.
Instead of returning -1, use the EXIT_FAILURE constant to indicate
unsuccessful termination properly.
[1]: https://pubs.opengroup.org/onlinepubs/9699919799/functions/V2_chap02.html
[2]: https://pubs.opengroup.org/onlinepubs/9699919799/functions/exit.html
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
(cherry picked from commit 80443ab629a234e2af55dd9bcceb6fe150c6c79e)
---
fdasd/fdasd.c | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/fdasd/fdasd.c b/fdasd/fdasd.c
index 085d2be..5de7955 100644
--- a/fdasd/fdasd.c
+++ b/fdasd/fdasd.c
@@ -397,7 +397,7 @@ static void fdasd_error(fdasd_anchor_t *anc, enum fdasd_failure why, char *str)
fputc('\n', stderr);
fputs(err_str, stderr);
- fdasd_exit(anc, -1);
+ fdasd_exit(anc, EXIT_FAILURE);
}
/*
@@ -3040,5 +3040,5 @@ int main(int argc, char *argv[])
}
}
- return -1;
+ return EXIT_FAILURE;
}
--
2.21.3
From 89ed5688c2b351f487e75e3035948aadcab73a8f Mon Sep 17 00:00:00 2001
From: Stefan Haberland <sth@linux.ibm.com>
Date: Wed, 7 Aug 2019 16:59:20 +0200
Subject: [PATCH 27/44] zipl: fix zfcp dump image location (#1730707)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
The zfcp dumper fails with the following error:
MLOPDM003I: Machine loader finished, moving data to final storage
location.
uncompression error
--- System halted
HCPGIR450W CP entered; disabled wait
PSW 00020001 80000000 00000000 DEADBEEF
The zipl command shows overlapping components when installing the dumper
to a zfcp disk:
zipl -d /dev/sda1
Building bootmap directly on partition '/dev/sda1'
Adding dump section
kernel image......: /lib/s390-tools/zfcpdump/zfcpdump-image
kernel parmline...: 'root=/dev/ram0 dump_mem=1 possible_cpus=1
cgroup_disable=memory '
component address:
heap area.......: 0x00002000-0x00005fff
stack area......: 0x0000f000-0x0000ffff
internal loader.: 0x0000a000-0x0000dfff
parameters......: 0x00009000-0x000091ff
kernel image....: 0x00010000-0x005761ff
^^^^^^
parmline........: 0x00567000-0x005671ff
^^^^^^
Preparing boot device: sda.
Done.
With the secure IPL patchset the offset of the kernel image has been
removed for the normal IPL case but it has not been removed for the dump
image which leads to the overlap of 0x10000.
Fix by removing the offset for the dump case.
Signed-off-by: Stefan Haberland <sth@linux.ibm.com>
Reviewd-by: Mikhail Zaslonko <zaslonko@linux.ibm.com>
Tested-by: Mikhail Zaslonko <zaslonko@linux.ibm.com>
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
(cherry picked from commit a9f4ae2db3fa513941244956e323cb6ce214ca5f)
---
zipl/src/job.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/zipl/src/job.c b/zipl/src/job.c
index 2d8de8f..e68d1b7 100644
--- a/zipl/src/job.c
+++ b/zipl/src/job.c
@@ -559,7 +559,7 @@ get_dump_components(struct job_dump_data *dump,
/* Fill in component data */
num = 0;
rc = set_cl_element(&cl[num++], "kernel image", dump->image,
- &dump->image_addr, 0, 0x10000,
+ &dump->image_addr, 0, 0x0,
MAXIMUM_PHYSICAL_BLOCKSIZE);
if (rc)
goto error;
--
2.21.3
From 9e5da1e34622725dd9fb7a681f99552bcdabe414 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Dan=20Hor=C3=A1k?= <dan@danny.cz>
Date: Thu, 7 Nov 2019 12:23:26 +0100
Subject: [PATCH 28/44] dasdfmt/lsdasd: Add Thin provisioning base support
(#1651733)
Summary: dasdfmt/lsdasd: Add Thin provisioning base support
Description: Make dasdfmt and lsdasd aware of thinly provisioned (Extent Space
Efficient (ESE)) DASD volumes.
If an ESE volume is recognised a QUICK format is performed,
formatting only the first two tracks. The mode can always be
overwritten by --mode.
Previously allocated space is always released before formatting,
if not specified otherwise. The option --no-discard (-D) is
provided to omit the space release.
The ESE information is added to the discipline output of lsdasd
and the extended output additionally lists information about
logical capacity, allocated space, and the extent size.
---
Makefile | 2 +-
common.mak | 4 -
dasdfmt/Makefile | 1 -
dasdfmt/dasdfmt.8 | 6 +
dasdfmt/dasdfmt.c | 60 +++++++-
dasdfmt/dasdfmt.h | 48 +-----
dasdinfo/Makefile | 3 +-
dasdview/Makefile | 1 -
dasdview/dasdview.c | 4 +-
dasdview/dasdview.h | 3 +-
fdasd/Makefile | 1 -
fdasd/fdasd.c | 2 +-
include/lib/dasd_base.h | 36 +++++
include/lib/dasd_sys.h | 3 +-
include/lib/u2s.h | 21 ---
include/lib/util_sys.h | 17 ++
libdasd/dasd_ioctl.c | 20 +++
libdasd/dasd_sys.c | 114 ++++++++++++--
libu2s/Makefile | 14 --
libu2s/misc.c | 27 ----
libu2s/misc.h | 15 --
libu2s/u2s.c | 334 ----------------------------------------
libutil/Makefile | 3 +-
libutil/util_sys.c | 78 ++++++++++
libzds/libzds.c | 4 +-
tunedasd/src/Makefile | 1 -
zconf/lsdasd | 30 +++-
zdsfs/Makefile | 5 +-
zipl/src/Makefile | 3 +-
zipl/src/install.c | 4 +-
30 files changed, 354 insertions(+), 510 deletions(-)
delete mode 100644 include/lib/u2s.h
create mode 100644 include/lib/util_sys.h
delete mode 100644 libu2s/Makefile
delete mode 100644 libu2s/misc.c
delete mode 100644 libu2s/misc.h
delete mode 100644 libu2s/u2s.c
create mode 100644 libutil/util_sys.c
diff --git a/Makefile b/Makefile
index bb2b900..8a07a4d 100644
--- a/Makefile
+++ b/Makefile
@@ -3,7 +3,7 @@ ARCH := $(shell uname -m | sed -e s/i.86/i386/ -e s/sun4u/sparc64/ -e s/arm.*/ar
# Include common definitions
include common.mak
-LIB_DIRS = libvtoc libu2s libutil libzds libdasd libvmdump libccw libvmcp
+LIB_DIRS = libvtoc libutil libzds libdasd libvmdump libccw libvmcp
TOOL_DIRS = zipl zdump fdasd dasdfmt dasdview tunedasd \
tape390 osasnmpd qetharp ip_watcher qethconf scripts zconf \
vmconvert vmcp man mon_tools dasdinfo vmur cpuplugd ipl_tools \
diff --git a/common.mak b/common.mak
index 6029687..6f6acbb 100644
--- a/common.mak
+++ b/common.mak
@@ -331,10 +331,6 @@ $(rootdir)/libzds/libzds.a: $(rootdir)/libzds
$(MAKE) -C $(rootdir)/libzds/ libzds.a
.PHONY: $(rootdir)/libzds
-$(rootdir)/libu2s/libu2s.a: $(rootdir)/libu2s
- $(MAKE) -C $(rootdir)/libu2s/ libu2s.a
-.PHONY: $(rootdir)/libu2s
-
$(rootdir)/libvmdump/libvmdump.a: $(rootdir)/libvmdump
$(MAKE) -C $(rootdir)/libvmdump/ libvmdump.a
.PHONY: $(rootdir)/libvmdump
diff --git a/dasdfmt/Makefile b/dasdfmt/Makefile
index 03877b3..98d9d3e 100644
--- a/dasdfmt/Makefile
+++ b/dasdfmt/Makefile
@@ -4,7 +4,6 @@ all: dasdfmt
libs = $(rootdir)/libdasd/libdasd.a \
$(rootdir)/libvtoc/libvtoc.a \
- $(rootdir)/libu2s/libu2s.a \
$(rootdir)/libutil/libutil.a
dasdfmt: dasdfmt.o $(libs)
diff --git a/dasdfmt/dasdfmt.8 b/dasdfmt/dasdfmt.8
index 99da9ed..c3e98fa 100644
--- a/dasdfmt/dasdfmt.8
+++ b/dasdfmt/dasdfmt.8
@@ -122,6 +122,8 @@ Format the first two tracks and write label and partition information. Only use
this option if you are sure that the target DASD already contains a regular
format with the specified blocksize. A blocksize can optionally be specified
using \fB-b\fR (\fB--blocksize\fR).
+.br
+For thin-provisioned DASD ESE volumes this is the default mode.
.IP expand
Format all unformatted tracks at the end of the target DASD. This mode assumes
that tracks at the beginning of the DASD volume have already been correctly
@@ -136,6 +138,10 @@ using \fB-b\fR (\fB--blocksize\fR).
Perform a complete format check on a DASD volume. A blocksize can be specified
with \fB-b\fR (\fB--blocksize\fR).
+.TP
+\fB--no-discard\fR
+Omit a full space release when formatting a thin-provisioned DASD ESE volume.
+
.TP
\fB-r\fR \fIcylindercount\fR or \fB--requestsize\fR=\fIcylindercount\fR
Number of cylinders to be processed in one formatting step.
diff --git a/dasdfmt/dasdfmt.c b/dasdfmt/dasdfmt.c
index a34d79b..c78080c 100644
--- a/dasdfmt/dasdfmt.c
+++ b/dasdfmt/dasdfmt.c
@@ -53,6 +53,7 @@ static const struct util_prg prg = {
/* Defines for options with no short command */
#define OPT_CHECK 128
#define OPT_NOZERO 129
+#define OPT_NODISCARD 130
static struct util_opt opt_vec[] = {
UTIL_OPT_SECTION("FORMAT ACTIONS"),
@@ -105,6 +106,11 @@ static struct util_opt opt_vec[] = {
.desc = "Prevent storage server from modifying record 0",
.flags = UTIL_OPT_FLAG_NOSHORT,
},
+ {
+ .option = { "no-discard", no_argument, NULL, OPT_NODISCARD },
+ .desc = "Do not discard space before formatting",
+ .flags = UTIL_OPT_FLAG_NOSHORT,
+ },
{
.option = { NULL, no_argument, NULL, 'y' },
.desc = "Start formatting without further user-confirmation",
@@ -921,6 +927,8 @@ static void dasdfmt_print_info(dasdfmt_info_t *info, char *devname,
printf("Drive Geometry: %d Cylinders * %d Heads = %d Tracks\n",
cylinders, heads, (cylinders * heads));
+ printf("Device Type: %s Provisioned\n",
+ info->ese ? "Thinly" : "Fully");
printf("\nI am going to format the device ");
printf("%s in the following way:\n", devname);
printf(" Device number of device : 0x%x\n", info->dasd_info.devno);
@@ -939,7 +947,10 @@ static void dasdfmt_print_info(dasdfmt_info_t *info, char *devname,
(p->intensity & DASD_FMT_INT_COMPAT) ? "yes" : "no");
printf(" Blocksize : %d\n", p->blksize);
printf(" Mode : %s\n", mode_str[mode]);
-
+ if (info->ese) {
+ printf(" Full Space Release : %s\n",
+ (info->no_discard || mode == FULL) ? "no" : "yes");
+ }
if (info->testmode)
printf("Test mode active, omitting ioctl.\n");
}
@@ -1234,6 +1245,26 @@ static void dasdfmt_format(dasdfmt_info_t *info, unsigned int cylinders,
process_tracks(info, cylinders, heads, format_params);
}
+static void dasdfmt_release_space(dasdfmt_info_t *info)
+{
+ format_data_t r = {
+ .start_unit = 0,
+ .stop_unit = 0,
+ .intensity = DASD_FMT_INT_ESE_FULL,
+ };
+ int err = 0;
+
+ if (!info->ese || info->no_discard)
+ return;
+
+ printf("Releasing space for the entire device...\n");
+ err = dasd_release_space(dev_filename, &r);
+ if (err) {
+ ERRMSG_EXIT(EXIT_FAILURE, "%s: Could not release space (%s)\n",
+ prog_name, strerror(err));
+ }
+}
+
static void dasdfmt_prepare_and_format(dasdfmt_info_t *info,
unsigned int cylinders,
unsigned int heads, format_data_t *p)
@@ -1329,6 +1360,8 @@ static void dasdfmt_quick_format(dasdfmt_info_t *info, unsigned int cylinders,
if (info->force) {
printf("Skipping format check due to --force.\n");
+ } else if (info->ese) {
+ printf("Skipping format check due to thin-provisioned device.\n");
} else {
check_blocksize(info, p->blksize);
@@ -1394,7 +1427,7 @@ static void do_format_dasd(dasdfmt_info_t *info, char *devname,
if ((info->verbosity > 0) || !info->withoutprompt || info->testmode)
dasdfmt_print_info(info, devname, vlabel, cylinders, heads, p);
- count = u2s_get_host_access_count(devname);
+ count = dasd_get_host_access_count(devname);
if (info->force_host) {
if (count > 1) {
ERRMSG_EXIT(EXIT_FAILURE,
@@ -1438,6 +1471,7 @@ static void do_format_dasd(dasdfmt_info_t *info, char *devname,
dasdfmt_prepare_and_format(info, cylinders, heads, p);
break;
case QUICK:
+ dasdfmt_release_space(info);
dasdfmt_quick_format(info, cylinders, heads, p);
break;
case EXPAND:
@@ -1461,6 +1495,19 @@ static void do_format_dasd(dasdfmt_info_t *info, char *devname,
}
}
+static void eval_format_mode(dasdfmt_info_t *info)
+{
+ if (!info->force && info->mode_specified && info->ese && mode == EXPAND) {
+ ERRMSG_EXIT(EXIT_FAILURE,
+ "WARNING: The specified device is thin-provisioned\n"
+ "Format mode 'expand' is not feasible.\n"
+ "Use --mode=full or --mode=quick to perform a clean format\n");
+ }
+
+ if (!info->mode_specified)
+ mode = info->ese ? QUICK : FULL;
+}
+
int main(int argc, char *argv[])
{
dasdfmt_info_t info = {
@@ -1496,8 +1543,6 @@ int main(int argc, char *argv[])
format_params.blksize = DEFAULT_BLOCKSIZE;
format_params.intensity = DASD_FMT_INT_COMPAT;
- mode = FULL;
-
/*************** parse parameters **********************/
while (1) {
@@ -1601,6 +1646,10 @@ int main(int argc, char *argv[])
"invalid. Consult the man page for "
"more information.\n",
prog_name, optarg);
+ info.mode_specified = 1;
+ break;
+ case OPT_NODISCARD:
+ info.no_discard = 1;
break;
case OPT_CHECK:
info.check = 1;
@@ -1645,6 +1694,9 @@ int main(int argc, char *argv[])
"device information failed (%s).\n",
prog_name, strerror(rc));
+ info.ese = dasd_sys_ese(dev_filename);
+ eval_format_mode(&info);
+
/* Either let the user specify the blksize or get it from the kernel */
if (!info.blksize_specified) {
if (!(mode == FULL ||
diff --git a/dasdfmt/dasdfmt.h b/dasdfmt/dasdfmt.h
index 5470c90..2f53a0d 100644
--- a/dasdfmt/dasdfmt.h
+++ b/dasdfmt/dasdfmt.h
@@ -27,10 +27,6 @@
#include <sys/types.h>
#include <unistd.h>
-/****************************************************************************
- * SECTION: Definition needed for DASD-API (see dasd.h) *
- ****************************************************************************/
-
/*
* Represents possible format modes that can be specified when formatting
* a DASD.
@@ -45,57 +41,16 @@ static const char mode_str[3][10] = {
"Full", "Quick", "Expand"
};
-/*
- * values to be used for format_data_t.intensity
- * 0/8: normal format
- * 1/9: also write record zero
- * 3/11: also write home address
- * 4/12: invalidate track
- */
-#define DASD_FMT_INT_FMT_R0 1 /* write record zero */
-#define DASD_FMT_INT_FMT_HA 2 /* write home address, also set FMT_R0 ! */
-#define DASD_FMT_INT_INVAL 4 /* invalidate tracks */
-#define DASD_FMT_INT_COMPAT 8 /* use OS/390 compatible disk layout */
-#define DASD_FMT_INT_FMT_NOR0 16 /* remove permission to write record zero */
-
-/*
- * values to be used in format_check_t for indicating
- * possible format errors
- */
-#define DASD_FMT_ERR_TOO_FEW_RECORDS 1
-#define DASD_FMT_ERR_TOO_MANY_RECORDS 2
-#define DASD_FMT_ERR_BLKSIZE 3
-#define DASD_FMT_ERR_RECORD_ID 4
-#define DASD_FMT_ERR_KEY_LENGTH 5
-
-/*
- * values to be used for dasd_information2_t.format
- * 0x00: NOT formatted
- * 0x01: Linux disc layout
- * 0x02: Common disc layout
- */
-#define DASD_FORMAT_NONE 0
-#define DASD_FORMAT_LDL 1
-#define DASD_FORMAT_CDL 2
-
-/****************************************************************************
- * SECTION: DASDFMT internal types *
- ****************************************************************************/
-
#define DASD_PARTN_BITS 2
#define PARTN_MASK ((1 << DASD_PARTN_BITS) - 1)
#define EXIT_MISUSE 1
#define EXIT_BUSY 2
-#define LABEL_LENGTH 14
-#define VLABEL_CHARS 84
-#define LINE_LENGTH 80
#define ERR_LENGTH 90
#define DEFAULT_BLOCKSIZE 4096
/* requestsize - number of cylinders in one format step */
#define DEFAULT_REQUESTSIZE 10
-#define USABLE_PARTITIONS ((1 << DASD_PARTN_BITS) - 1)
#define ERRMSG(x...) {fflush(stdout);fprintf(stderr,x);}
#define ERRMSG_EXIT(ec,x...) {fflush(stdout);fprintf(stderr,x);exit(ec);}
@@ -137,6 +92,9 @@ typedef struct dasdfmt_info {
int force_host;
int layout_specified;
int check;
+ int mode_specified;
+ int ese;
+ int no_discard;
} dasdfmt_info_t;
diff --git a/dasdinfo/Makefile b/dasdinfo/Makefile
index ea45bf1..ed81c3f 100644
--- a/dasdinfo/Makefile
+++ b/dasdinfo/Makefile
@@ -1,7 +1,6 @@
include ../common.mak
-libs = $(rootdir)/libu2s/libu2s.a \
- $(rootdir)/libutil/libutil.a \
+libs = $(rootdir)/libutil/libutil.a \
$(rootdir)/libdasd/libdasd.a
all: dasdinfo
diff --git a/dasdview/Makefile b/dasdview/Makefile
index b7bdba3..59fda17 100644
--- a/dasdview/Makefile
+++ b/dasdview/Makefile
@@ -7,7 +7,6 @@ all: dasdview
libs = $(rootdir)/libdasd/libdasd.a \
$(rootdir)/libzds/libzds.a \
$(rootdir)/libvtoc/libvtoc.a \
- $(rootdir)/libu2s/libu2s.a \
$(rootdir)/libutil/libutil.a
dasdview: dasdview.o $(libs)
diff --git a/dasdview/dasdview.c b/dasdview/dasdview.c
index d773b02..3cf5629 100644
--- a/dasdview/dasdview.c
+++ b/dasdview/dasdview.c
@@ -28,10 +28,10 @@
#include "lib/dasd_base.h"
#include "lib/dasd_sys.h"
#include "lib/libzds.h"
-#include "lib/u2s.h"
#include "lib/util_base.h"
#include "lib/util_opt.h"
#include "lib/util_prg.h"
+#include "lib/util_sys.h"
#include "lib/vtoc.h"
#include "lib/zt_common.h"
@@ -183,7 +183,7 @@ dasdview_get_info(dasdview_info_t *info)
else
info->hw_cylinders = characteristics->no_cyl;
- if (u2s_getbusid(info->device, info->busid) == -1)
+ if (util_sys_get_dev_addr(info->device, info->busid) != 0)
info->busid_valid = 0;
else
info->busid_valid = 1;
diff --git a/dasdview/dasdview.h b/dasdview/dasdview.h
index 50dd3fe..35f2c54 100644
--- a/dasdview/dasdview.h
+++ b/dasdview/dasdview.h
@@ -11,7 +11,6 @@
#define DASDVIEW_H
#include <limits.h>
-#include "lib/u2s.h"
/********************************************************************************
* SECTION: Definitions needed for DASD-API (see dasd.h)
@@ -109,7 +108,7 @@ typedef struct dasdview_info
int f8c;
int f9c;
- char busid[U2S_BUS_ID_SIZE];
+ char busid[DASD_BUS_ID_SIZE];
int busid_valid;
int raw_track_access;
struct zdsroot *zdsroot;
diff --git a/fdasd/Makefile b/fdasd/Makefile
index 360d81f..facd89e 100644
--- a/fdasd/Makefile
+++ b/fdasd/Makefile
@@ -3,7 +3,6 @@ include ../common.mak
libs = $(rootdir)/libvtoc/libvtoc.a \
$(rootdir)/libzds/libzds.a \
$(rootdir)/libdasd/libdasd.a \
- $(rootdir)/libu2s/libu2s.a \
$(rootdir)/libutil/libutil.a
all: fdasd
diff --git a/fdasd/fdasd.c b/fdasd/fdasd.c
index 5de7955..569cc78 100644
--- a/fdasd/fdasd.c
+++ b/fdasd/fdasd.c
@@ -935,7 +935,7 @@ static void fdasd_verify_device(fdasd_anchor_t *anc, char *name)
fdasd_error(anc, device_verification_failed, err_str);
}
- count = u2s_get_host_access_count(name);
+ count = dasd_get_host_access_count(name);
if (anc->force_host) {
if (count > 1) {
snprintf(err_str, ERROR_STRING_SIZE,
diff --git a/include/lib/dasd_base.h b/include/lib/dasd_base.h
index cf7be2e..abd17f5 100644
--- a/include/lib/dasd_base.h
+++ b/include/lib/dasd_base.h
@@ -20,6 +20,9 @@
#include <stdio.h>
#include <sys/ioctl.h>
+/* A bus id of a DASD is 8 characters long. E.g. 0.0.4711 */
+#define DASD_BUS_ID_SIZE 9
+
typedef struct dasd_information2_t {
unsigned int devno; /* S/390 devno */
unsigned int real_devno; /* for aliases */
@@ -51,6 +54,16 @@ typedef struct dasd_information2_t {
unsigned int reserved7; /* reserved for further use ,... */
} dasd_information2_t;
+/*
+ * values to be used for dasd_information2_t.format
+ * 0x00: NOT formatted
+ * 0x01: Linux disc layout
+ * 0x02: Common disc layout
+ */
+#define DASD_FORMAT_NONE 0
+#define DASD_FORMAT_LDL 1
+#define DASD_FORMAT_CDL 2
+
struct dasd_eckd_characteristics {
unsigned short cu_type;
struct {
@@ -136,6 +149,16 @@ typedef struct format_data_t {
unsigned int intensity;
} format_data_t;
+/*
+ * values to be used for format_data_t.intensity
+ */
+#define DASD_FMT_INT_FMT_R0 1 /* write record zero */
+#define DASD_FMT_INT_FMT_HA 2 /* write home address, also set FMT_R0 ! */
+#define DASD_FMT_INT_INVAL 4 /* invalidate tracks */
+#define DASD_FMT_INT_COMPAT 8 /* use OS/390 compatible disk layout */
+#define DASD_FMT_INT_FMT_NOR0 16 /* remove permission to write record zero */
+#define DASD_FMT_INT_ESE_FULL 32 /* release space for entire volume */
+
/*
* struct format_check_t
* represents all data necessary to evaluate the format of
@@ -154,6 +177,16 @@ typedef struct format_check_t {
unsigned int key_length; /* Key length of first record in error */
} format_check_t;
+/*
+ * values to be used in format_check_t for indicating
+ * possible format errors
+ */
+#define DASD_FMT_ERR_TOO_FEW_RECORDS 1
+#define DASD_FMT_ERR_TOO_MANY_RECORDS 2
+#define DASD_FMT_ERR_BLKSIZE 3
+#define DASD_FMT_ERR_RECORD_ID 4
+#define DASD_FMT_ERR_KEY_LENGTH 5
+
#ifndef __linux__
/* definition from hdreg.h */
struct hd_geometry {
@@ -174,6 +207,8 @@ struct hd_geometry {
#define BIODASDINFO2 _IOR(DASD_IOCTL_LETTER, 3, dasd_information2_t)
/* #define BIODASDFORMAT _IOW(IOCTL_LETTER,0,format_data_t) , deprecated */
#define BIODASDFMT _IOW(DASD_IOCTL_LETTER, 1, format_data_t)
+/* Release Allocated Space */
+#define BIODASDRAS _IOW(DASD_IOCTL_LETTER, 3, format_data_t)
/* Check device format according to format_data_t */
#define BIODASDCHECKFMT _IOWR(DASD_IOCTL_LETTER, 2, format_check_t)
@@ -197,6 +232,7 @@ int dasd_check_format(const char *device, format_check_t *p);
int dasd_format_disk(int fd, format_data_t *p);
int dasd_disk_disable(const char *device, int *fd);
int dasd_disk_enable(int fd);
+int dasd_release_space(const char *device, format_data_t *r);
int dasd_get_blocksize(const char *device, unsigned int *blksize);
int dasd_get_blocksize_in_bytes(const char *device, unsigned long long *blksize);
int dasd_get_geo(const char *device, struct hd_geometry *geo);
diff --git a/include/lib/dasd_sys.h b/include/lib/dasd_sys.h
index 5438172..75b186e 100644
--- a/include/lib/dasd_sys.h
+++ b/include/lib/dasd_sys.h
@@ -13,9 +13,10 @@
#define LIB_DASD_SYS_H
#include <stdio.h>
-#include "u2s.h"
int dasd_sys_raw_track_access(char *);
+int dasd_sys_ese(char *);
int dasd_reset_chpid(char *, char *);
+int dasd_get_host_access_count(char *device);
#endif /* LIB_DASD_SYS_H */
diff --git a/include/lib/u2s.h b/include/lib/u2s.h
deleted file mode 100644
index fe5d673..0000000
--- a/include/lib/u2s.h
+++ /dev/null
@@ -1,21 +0,0 @@
-/*
- *
- * Copyright IBM Corp. 2004, 2017
- *
- * s390-tools is free software; you can redistribute it and/or modify
- * it under the terms of the MIT license. See LICENSE for details.
- *
- * History of changes (starts July 2004)
- * 2004-07-02 initial
- */
-
-#ifndef LIB_U2S_H
-#define LIB_U2S_H
-
-#define U2S_BUS_ID_SIZE 32
-
-int u2s_getbusid(char *, char *);
-int u2s_read_attribute(char *, char *, char *, size_t);
-int u2s_get_host_access_count(char *);
-
-#endif /* LIB_U2S_H */
diff --git a/include/lib/util_sys.h b/include/lib/util_sys.h
new file mode 100644
index 0000000..e0f078b
--- /dev/null
+++ b/include/lib/util_sys.h
@@ -0,0 +1,17 @@
+/*
+ * @defgroup util_sys_h util_sys: SysFS interface
+ * @{
+ * @brief Work with SysFS
+ *
+ * Copyright IBM Corp. 2019
+ *
+ * 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 LIB_UTIL_SYS_H
+#define LIB_UTIL_SYS_H
+
+int util_sys_get_dev_addr(const char *dev, char *addr);
+
+#endif /** LIB_UTIL_SYS_H @} */
diff --git a/libdasd/dasd_ioctl.c b/libdasd/dasd_ioctl.c
index bbfb384..ef1605e 100644
--- a/libdasd/dasd_ioctl.c
+++ b/libdasd/dasd_ioctl.c
@@ -228,6 +228,26 @@ int dasd_check_format(const char *device, format_check_t *p)
return 0;
}
+/*
+ * Release Allocated Space
+ *
+ * @param[in] fd device node's file descriptor
+ * @param[in] r format options
+ *
+ * @retval 0 in case of success
+ * @retval errno in case of failure
+ */
+int dasd_release_space(const char *device, format_data_t *r)
+{
+ int fd;
+
+ fd = dasd_open_device(device, O_RDONLY);
+ RUN_IOCTL(fd, BIODASDRAS, r);
+ dasd_close_device(fd);
+
+ return 0;
+}
+
/*
* Reread partition table
*
diff --git a/libdasd/dasd_sys.c b/libdasd/dasd_sys.c
index f2fd826..94c2d12 100644
--- a/libdasd/dasd_sys.c
+++ b/libdasd/dasd_sys.c
@@ -12,7 +12,11 @@
#include <errno.h>
#include <stdlib.h>
+#include "lib/dasd_base.h"
#include "lib/dasd_sys.h"
+#include "lib/util_file.h"
+#include "lib/util_path.h"
+#include "lib/util_sys.h"
/**
* Get raw-track access mode status
@@ -31,39 +35,80 @@
*/
int dasd_sys_raw_track_access(char *devnode)
{
- char busid[9];
- char path[47];
+ char busid[DASD_BUS_ID_SIZE];
+ char *path;
FILE *fp;
int rc;
- if (u2s_getbusid(devnode, busid))
+ if (util_sys_get_dev_addr(devnode, busid) != 0)
return 0;
- sprintf(path, "/sys/bus/ccw/devices/%s/raw_track_access", busid);
-
+ path = util_path_sysfs("bus/ccw/devices/%s/raw_track_access", busid);
fp = fopen(path, "r");
- if (!fp)
+ if (!fp) {
+ free(path);
return 0;
+ }
rc = fgetc(fp) - '0';
fclose(fp);
+ free(path);
return (rc == 1) ? 1 : 0;
}
+/**
+ * Is volume extent space efficient a.k.a. thin-provisioned
+ *
+ * The "devnode" parameter can be any valid relative or absolute path to
+ * a DASD device node, for example:
+ *
+ * - /dev/dasda
+ * - /dev/disk/by-path/ccw-0.0.bf20
+ *
+ * @param[in] devnode Device node of interest
+ *
+ * @retval 1 Volume is extent space efficient
+ * @retval 0 Volume is not extent space efficient or
+ * cannot be determined
+ */
+int dasd_sys_ese(char *devnode)
+{
+ char busid[DASD_BUS_ID_SIZE];
+ char *path;
+ FILE *fp;
+ int rc;
+
+ if (util_sys_get_dev_addr(devnode, busid) != 0)
+ return 0;
+
+ path = util_path_sysfs("bus/ccw/devices/%s/ese", busid);
+ fp = fopen(path, "r");
+ if (!fp) {
+ free(path);
+ return 0;
+ }
+
+ rc = fgetc(fp) - '0';
+ fclose(fp);
+
+ return (rc == 1) ? 1 : 0;
+}
int dasd_get_pm_from_chpid(char *busid, unsigned int chpid, int *mask)
{
unsigned int val;
- char path[40];
int count, i;
+ char *path;
FILE *fp;
- sprintf(path, "/sys/bus/ccw/devices/%s/../chpids", busid);
+ path = util_path_sysfs("bus/ccw/devices/%s/../chpids", busid);
*mask = 0;
fp = fopen(path, "r");
- if (!fp)
+ if (!fp) {
+ free(path);
return ENODEV;
+ }
for (i = 0; i < 8; i++) {
count = fscanf(fp, " %x", &val);
@@ -75,6 +120,7 @@ int dasd_get_pm_from_chpid(char *busid, unsigned int chpid, int *mask)
*mask = 0x80 >> i;
}
fclose(fp);
+ free(path);
return 0;
}
@@ -102,22 +148,25 @@ int dasd_get_pm_from_chpid(char *busid, unsigned int chpid, int *mask)
int dasd_reset_chpid(char *devnode, char *chpid_char)
{
unsigned int chpid;
- char path[41];
- char busid[9];
+ char busid[DASD_BUS_ID_SIZE];
int mask, rc;
char *endptr;
+ char *path;
FILE *fp;
- if (u2s_getbusid(devnode, busid))
+ if (util_sys_get_dev_addr(devnode, busid) != 0)
return ENODEV;
if (!chpid_char) {
- sprintf(path, "/sys/bus/ccw/devices/%s/path_reset", busid);
+ path = util_path_sysfs("bus/ccw/devices/%s/path_reset", busid);
fp = fopen(path, "w");
- if (!fp)
+ if (!fp) {
+ free(path);
return ENODEV;
+ }
fprintf(fp, "%s", "all\n");
fclose(fp);
+ free(path);
return 0;
}
@@ -132,12 +181,45 @@ int dasd_reset_chpid(char *devnode, char *chpid_char)
if (!mask)
return ENOENT;
- sprintf(path, "/sys/bus/ccw/devices/%s/path_reset", busid);
+ path = util_path_sysfs("bus/ccw/devices/%s/path_reset", busid);
fp = fopen(path, "w");
- if (!fp)
+ if (!fp) {
+ free(path);
return ENODEV;
+ }
fprintf(fp, "%02x", mask);
fclose(fp);
+ free(path);
return 0;
}
+
+/**
+ * Read amount of host with access to \p device
+ *
+ * The \p device can be any valid relative or absolute path to a DASD device
+ * node, for example:
+ *
+ * - /dev/dasda
+ * - /dev/disk/by-path/ccw-0.0.bf20
+ *
+ * @param[in] device Device node of interest
+ *
+ * @retval n Number of hosts with access to \p device
+ * @retval 0 Value could not be determined
+ */
+int dasd_get_host_access_count(char *device)
+{
+ char busid[9];
+ char *path;
+ long value;
+
+ if (!util_sys_get_dev_addr(device, busid))
+ return 0;
+
+ path = util_path_sysfs("bus/ccw/devices/%s/host_access_count", busid);
+ util_file_read_l(&value, 10, path);
+ free(path);
+
+ return value;
+}
diff --git a/libu2s/Makefile b/libu2s/Makefile
deleted file mode 100644
index 523282b..0000000
--- a/libu2s/Makefile
+++ /dev/null
@@ -1,14 +0,0 @@
-include ../common.mak
-
-lib = libu2s.a
-
-all: $(lib)
-
-objects = u2s.o misc.o
-
-$(lib): $(objects)
-
-install: all
-
-clean:
- rm -f *.o $(lib)
diff --git a/libu2s/misc.c b/libu2s/misc.c
deleted file mode 100644
index d29784c..0000000
--- a/libu2s/misc.c
+++ /dev/null
@@ -1,27 +0,0 @@
-/*
- * Misc - Local helper functions
- *
- * Copyright IBM Corp. 2016, 2017
- *
- * 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 <string.h>
-#include "lib/util_base.h"
-
-/*
- * Helper function that copies a string safely
- */
-size_t misc_strlcpy(char *dest, const char *src, size_t size)
-{
- size_t str_len = strlen(src);
- size_t len;
-
- if (size) {
- len = MIN(size - 1, str_len);
- memcpy(dest, src, len);
- dest[len] = '\0';
- }
- return str_len;
-}
diff --git a/libu2s/misc.h b/libu2s/misc.h
deleted file mode 100644
index 53b7356..0000000
--- a/libu2s/misc.h
+++ /dev/null
@@ -1,15 +0,0 @@
-/*
- * Misc - Local helper functions
- *
- * Copyright IBM Corp. 2016, 2017
- *
- * 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 MISC_H
-#define MISC_H
-
-size_t misc_strlcpy(char *, const char *, size_t);
-
-#endif /* MISC_H */
diff --git a/libu2s/u2s.c b/libu2s/u2s.c
deleted file mode 100644
index 9ffafa7..0000000
--- a/libu2s/u2s.c
+++ /dev/null
@@ -1,334 +0,0 @@
-/*
- *
- * Copyright IBM Corp. 2004, 2017
- *
- * 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 <dirent.h>
-#include <errno.h>
-#include <fcntl.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <sys/stat.h>
-#include <sys/sysmacros.h>
-#include <sys/types.h>
-#include <unistd.h>
-#include <wait.h>
-
-#include "lib/u2s.h"
-#include "misc.h"
-
-#define DEV_BUFFER_LENGTH 20
-#define PATH_BUFFER_LENGTH 256
-#define BUSIDSIZE 9
-
-#define BLOCKPATH "/sys/block/"
-#define DEVICE_LINK "device"
-#define DEV_ATTRIBUTE "dev"
-
-
-/*
- * Helper function that expects a file name and returns 1 if this
- * is a directory or 0 otherwise.
- */
-static int isdir(char *name) {
-
- struct stat statbuf;
-
- if (stat(name, &statbuf) < 0)
- return 0;
- return S_ISDIR(statbuf.st_mode);
-}
-
-/*
- * Helper function that expects a directory name in sysfs of the form
- * /sys/block/<devname>/ or /sys/block/<devname>/<partname>/.
- * It will try to read the file "dev" in this directory and compare
- * it's contents with the given dev string of the form <major>:<minor>.
- * Trailing white space (newline) is ignored.
- * The buffer name is expected to be long enough to hold the additional "dev".
- * Returns 1 if the directory matches dev, 0 otherwise.
- */
-static int check_directory(char *name, char *dev) {
-
- char buffer[DEV_BUFFER_LENGTH];
- char *end;
- int fd;
- ssize_t count;
- int dev_attr_len, dev_parm_len;
- unsigned int namelen;
-
- namelen = strlen(name);
- if ((PATH_BUFFER_LENGTH - namelen) < sizeof(DEV_ATTRIBUTE))
- return 0;
- end = name + namelen;
- strcpy(end, DEV_ATTRIBUTE);
- fd = open(name, O_RDONLY);
- *end = 0;
- if (fd < 0)
- return 0;
- count = read(fd, buffer, DEV_BUFFER_LENGTH);
- close(fd);
- if (count < 0)
- return 0;
- dev_attr_len = strspn(buffer, "1234567890:");
- dev_parm_len = strlen(dev);
- if (dev_attr_len != dev_parm_len )
- return 0;
- return (strncmp(dev, buffer, dev_parm_len) == 0);
-}
-
-/*
- * Helper function that expects a directory name in sysfs of the form
- * /sys/block/<devname>/. It will try to read a link "device"
- * in this directory and extract the busid, which is the last part
- * of that link. The buffer name is expected to be long enough
- * to hold the additional "device".
- * name: block device path in sysfs.
- * busid: buffer in which the busid string will be returned
- * returns 0 for successful operation and -1 in case of an error.
- */
-static int extract_busid(char *name, char *busid) {
-
- int count;
- unsigned int namelen;
- char linkbuffer[PATH_BUFFER_LENGTH];
- char *start, *end;
- size_t len;
-
- namelen = strlen(name);
- if ((PATH_BUFFER_LENGTH - namelen) < sizeof(DEVICE_LINK))
- return 0;
- end = name + namelen;
- strcpy(end, DEVICE_LINK);
- count = readlink(name, linkbuffer, PATH_BUFFER_LENGTH - 1);
- if (count < 0)
- return -1;
- linkbuffer[count] = 0;
- start = strrchr(linkbuffer, '/');
- if (!start)
- return -1;
- start++;
- len = misc_strlcpy(busid, start, BUSIDSIZE);
- if (len >= BUSIDSIZE)
- return -1;
-
- return 0;
-};
-
-/*
- * Helper function that makes some basic checks on a directory entry.
- * The function checks if there is still enough space left in the buffer
- * for the new string, excludes '.' and '..', and verifies that the entry
- * is actually a directory.
- * buffer: the beginning of the name buffer
- * oldend: the current end of the string in the name buffer
- * dir: the dirent in question
- * returns: a pointer to the new end of the string in buffer or NULL if
- * one of the checks failed
- */
-
-static char *append_if_directory(char *buffer, char *oldend, struct dirent *dir) {
-
- char *newend;
- int oldlength, dirlength;
-
- if (strcmp(dir->d_name, ".") == 0 ||
- strcmp(dir->d_name, "..") == 0)
- return NULL;
- oldlength = strlen(buffer);
- dirlength = strlen(dir->d_name);
- if (PATH_BUFFER_LENGTH < oldlength + dirlength + 2)
- return NULL;
- strcpy(oldend, dir->d_name);
- if (!isdir(buffer)) {
- *oldend = 0;
- return NULL;
- }
- newend = oldend + dirlength;
- strcpy(newend, "/");
- newend++;
-
- return newend;
-}
-
-/*
- * helper function that searches for a specific block device and returns
- * it's busid
- * dev: <major>:<minor> of the device
- * busid: buffer in which the busid string will be returned
- * returns 0 for successful operation and -1 in case of an error.
- */
-static int find_busid_in_sysfs(char *dev, char *busid) {
-
- DIR *blockdir, *diskdir;
- struct dirent *blockde, *diskde;
- int found = 0;
- char namebuffer[PATH_BUFFER_LENGTH];
- char *blockend, *diskend = NULL, *partend;
-
- /* everything, including the other helper functions, works on the
- * same buffer area 'namebuffer'. The pointers blockend, diskend
- * and partend point to the end of the various names.
- * Example:
- * "/sys/block/dasda/dasda1/"
- * ^ blockend
- * ^ diskend
- * ^ partend
- */
-
- strcpy(namebuffer,BLOCKPATH);
- blockdir = opendir(namebuffer);
- if (!blockdir)
- return -1;
- blockend = namebuffer + strlen(namebuffer);
- /* check each entry in /sys/block */
- while ((blockde = readdir(blockdir))) {
- diskend = append_if_directory(namebuffer, blockend, blockde);
- if (!diskend)
- continue;
- found = check_directory(namebuffer, dev);
- if (found)
- break;
- diskdir = opendir(namebuffer);
- if (!diskdir)
- continue;
- /* check each entry in /sys/block/<disk name> */
- while ((diskde = readdir(diskdir))) {
- partend = append_if_directory(
- namebuffer, diskend, diskde);
- if (!partend)
- continue;
- found = check_directory(namebuffer, dev);
- if (found)
- break;
- }
- closedir(diskdir);
- if (found)
- break;
- }
- closedir(blockdir);
- if (found) {
- *diskend = 0; /* remove partition directory from name */
- return extract_busid(namebuffer, busid);
- } else
- return -1;
-}
-
-/*
- * helper function that searches for a specific block device in
- * /proc/dasd/devices and returns it's bus-ID
- * maja, mina: <major>, <minor> of the device
- * busid: buffer in which the bus-ID string will be returned
- * returns 0 for successful operation and -1 in case of an error
- * e.g. /proc/dasd/devices does not exist.
- *
- * An entry looks like:
- * 0.0.XXXX(DISCIPLINE) at ( MAJ: MIN) is dasdX :
- * active at blocksize: BLOCKSIZE, BLOCKS blocks, SIZE MB
- */
-static int find_busid_in_proc(int maja, int mina, char *busid)
-{
- FILE *filp;
- char bus[BUSIDSIZE];
- int majb, minb, rc;
- size_t len;
-
- rc = -1;
-
- filp = fopen("/proc/dasd/devices", "r");
- if (!filp)
- return rc;
- while (fscanf(filp, "%[^(] %*[^)] ) at ( %d : %d %*[^\n]\n",
- bus, &majb, &minb) != EOF) {
- if ((maja == majb) && (mina == minb)) {
- len = misc_strlcpy(busid, bus, BUSIDSIZE);
- if (len < BUSIDSIZE)
- rc = 0;
- break;
- }
- }
-
- fclose(filp);
- return rc;
-}
-
-/*
- * Return the busid of a given device node.
- * Works only for block devices.
- * devicenode: path to the device node
- * busid: buffer in which the busid string will be returned
- * returns 0 for successful operation and -1 in case of an error.
- */
-int u2s_getbusid(char *devicenode, char *busid)
-{
- int maj, min, rc;
- struct stat stat_buf;
- char dev_string[DEV_BUFFER_LENGTH];
-
- /*
- * Get major and minor information of the device special file
- * and combine them to a <maj>:<min> string, as returned by
- * the dev attributes in sysfs
- */
- if (stat(devicenode, &stat_buf))
- return -1;
- if (!S_ISBLK(stat_buf.st_mode))
- return -1;
- maj = major(stat_buf.st_rdev);
- min = minor(stat_buf.st_rdev);
-
- rc = find_busid_in_proc(maj, min, busid);
- if (rc) {
- snprintf(dev_string, DEV_BUFFER_LENGTH, "%u:%u", maj, min);
- rc = find_busid_in_sysfs(dev_string, busid);
- }
-
- return rc;
-}
-
-/*
- * Attempts to find the sysfs entry for the given busid and reads
- * the contents of a specified attribute to the buffer
- */
-int u2s_read_attribute(char *busid, char *attribute, char *buffer,
- size_t count)
-{
- char path[100];
- int rc, fd;
- ssize_t rcount;
-
- rc = 0;
- snprintf(path, sizeof(path), "/sys/bus/ccw/devices/%s/%s",
- busid, attribute);
- fd = open(path, O_RDONLY);
- if (fd < 0)
- return errno;
- rcount = read(fd, buffer, count);
- if (rcount < 0)
- rc = errno;
- close(fd);
- return rc;
-}
-
-int u2s_get_host_access_count(char *devicenode)
-{
- char busid[BUSIDSIZE];
- unsigned long value;
- char buffer[10];
- char *endp;
-
- u2s_getbusid(devicenode, busid);
- u2s_read_attribute(busid, "host_access_count", buffer, sizeof(buffer));
-
- value = strtoul(buffer, &endp, 0);
-
- if (endp == buffer)
- return -EINVAL;
-
- return value;
-}
diff --git a/libutil/Makefile b/libutil/Makefile
index fd5e213..8a11db2 100644
--- a/libutil/Makefile
+++ b/libutil/Makefile
@@ -26,7 +26,8 @@ objects = util_base.o \
util_part.o \
util_prg.o \
util_proc.o \
- util_rec.o
+ util_rec.o \
+ util_sys.o
util_base_example: util_base_example.o $(lib)
util_panic_example: util_panic_example.o $(lib)
diff --git a/libutil/util_sys.c b/libutil/util_sys.c
new file mode 100644
index 0000000..05e3653
--- /dev/null
+++ b/libutil/util_sys.c
@@ -0,0 +1,78 @@
+/*
+ * util - Utility function library
+ *
+ * SysFS helper functions
+ *
+ * Copyright IBM Corp. 2019
+ *
+ * s390-tools is free software; you can redistribute it and/or modify
+ * it under the terms of the MIT license. See LICENSE for details.
+ */
+
+#include <err.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <sys/sysmacros.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include "lib/util_path.h"
+#include "lib/util_sys.h"
+
+/* lstat() doesn't work for sysfs files, a fixed size is therefore inevitable */
+#define READLINK_SIZE 256
+
+/**
+ * Identify device address
+ *
+ * Identifying the device address with this function works for almost any
+ * character and block device (e.g. NVMe, SCSI, DASD, etc).
+ * The user must provide a buffer that is large enough for the desired device
+ * address to be read into \p addr.
+ *
+ * @param[in] dev Device node of interest
+ * @param[out] addr Identified device address
+ *
+ * @retval 0 Success
+ * @retval -1 Error while reading device information or
+ * constructed path
+ */
+int util_sys_get_dev_addr(const char *dev, char *addr)
+{
+ char device[READLINK_SIZE], *result;
+ unsigned int maj, min;
+ struct stat s;
+ ssize_t len;
+ char *path;
+
+ if (stat(dev, &s) != 0)
+ return -1;
+
+ maj = major(s.st_rdev);
+ min = minor(s.st_rdev);
+
+ if (S_ISBLK(s.st_mode))
+ path = util_path_sysfs("dev/block/%u:%u/device", maj, min);
+ else if (S_ISCHR(s.st_mode))
+ path = util_path_sysfs("dev/char/%u:%u/device", maj, min);
+ else
+ return -1;
+
+ len = readlink(path, device, READLINK_SIZE - 1);
+ free(path);
+ if (len != -1)
+ device[len] = '\0';
+ else
+ return -1;
+
+ result = strrchr(device, '/');
+ if (result)
+ result++;
+ else
+ result = device;
+ strcpy(addr, result);
+
+ return 0;
+}
diff --git a/libzds/libzds.c b/libzds/libzds.c
index 2430962..affb30c 100644
--- a/libzds/libzds.c
+++ b/libzds/libzds.c
@@ -20,8 +20,8 @@
#include <stdlib.h>
#include "lib/dasd_base.h"
+#include "lib/dasd_sys.h"
#include "lib/libzds.h"
-#include "lib/u2s.h"
#include "lib/util_base.h"
#include "lib/util_list.h"
#include "lib/vtoc.h"
@@ -3727,7 +3727,7 @@ int lzds_analyse_open_count(struct zdsroot *root, int warn)
int rc = 0;
util_list_iterate(root->dasdlist, dasd) {
- value = u2s_get_host_access_count(dasd->device);
+ value = dasd_get_host_access_count(dasd->device);
if (value < 0) {
fprintf(stderr,
diff --git a/tunedasd/src/Makefile b/tunedasd/src/Makefile
index 959b8eb..c79a235 100644
--- a/tunedasd/src/Makefile
+++ b/tunedasd/src/Makefile
@@ -3,7 +3,6 @@ include ../../common.mak
ALL_CPPFLAGS += -I../include -I../boot
libs = $(rootdir)/libdasd/libdasd.a \
- $(rootdir)/libu2s/libu2s.a \
$(rootdir)/libutil/libutil.a
all: tunedasd
diff --git a/zconf/lsdasd b/zconf/lsdasd
index a0af6ea..792efc0 100755
--- a/zconf/lsdasd
+++ b/zconf/lsdasd
@@ -164,6 +164,7 @@ function gatherDeviceData() {
read DEV_UID 2> /dev/null < $DEVPATH/uid || continue
read READONLY 2> /dev/null < $DEVPATH/readonly || continue
read DISCIPLINE 2> /dev/null < $DEVPATH/discipline || continue
+ read ESE 2> /dev/null < $DEVPATH/ese
# Block device specific information is only available for
# devices that are online and not a PAV alias
@@ -244,7 +245,7 @@ function newoutput()
#-------------------------------------------#
if [[ "$ONLINE" == 0 ]]; then
- printf "%s:%s:%-8s offline\n" \
+ printf "%s:%s:%-8s offline\n" \
"$SORTKEYLEN" "$SORTKEY" \
"$BUSID" ;
return
@@ -252,7 +253,7 @@ function newoutput()
if [[ "$ALIAS" == 1 ]]; then
if [[ "$BASEONLY" == "false" ]]; then
- printf "%s:%s:%-8s alias %28s\n" \
+ printf "%s:%s:%-8s alias %26s\n" \
"$SORTKEYLEN" "$SORTKEY" \
"$BUSID" \
"$DISCIPLINE"
@@ -286,7 +287,11 @@ function newoutput()
ACTIVE="active"
fi
- printf "%s:%s:%-8s %-6s%-4s %-8s %-2s:%-2s %-4s %-4s %-8s %s\n" \
+ if [[ "$ESE" == 1 ]]; then
+ DISCIPLINE="${DISCIPLINE} (ESE)"
+ fi
+
+ printf "%s:%s:%-8s %-6s%-2s %-8s %-2s:%-2s %-11s %-4s %-8s %s\n" \
"$SORTKEYLEN" "$SORTKEY" \
"$BUSID" \
"$ACTIVE" \
@@ -378,6 +383,10 @@ function extended()
read OPM NPPM CABLEPM CUIRPM HPFPM IFCCPM 2> /dev/null < $DEVPATH/path_masks
read -a C 2> /dev/null < $DEVPATH/../chpids
read PIM PAM POM 2> /dev/null < $DEVPATH/../pimpampom
+ read ESE 2> /dev/null < $DEVPATH/ese
+ 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
# convert to hexadecimal values
PIM=0x$PIM
@@ -550,7 +559,11 @@ function extended()
COLON=":"
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# 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" \
+ if [[ "$ESE" == 1 ]]; then
+ 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" \
"$SORTKEYLEN" "$SORTKEY" \
"$BUSID" \
"$BLOCKNAME" \
@@ -562,6 +575,9 @@ function extended()
"$SSIZE" \
"$MBSIZE" \
"$BLOCKCOUNT" \
+ "$EXTSZ" \
+ "$CAPACITY" \
+ "$ALLOCATED" \
"$DIAG" \
"$READONLY" \
"$EER" \
@@ -787,10 +803,10 @@ fi
if [[ "$PRINTUID" == "true" ]] && [[ "$OUTPUT" != "old" ]]; then
printf "Bus-ID Name UID\n"
- printf "==============================================================================\n"
+ printf "================================================================================\n"
elif [[ "$OUTPUT" == "new" ]]; then
- printf "Bus-ID Status Name Device Type BlkSz Size Blocks\n"
- printf "==============================================================================\n"
+ printf "Bus-ID Status Name Device Type BlkSz Size Blocks\n"
+ printf "================================================================================\n"
elif [[ "$OUTPUT" == "extended" ]]; then
PROCESSING=" $PROCESSING | sed 's/#/\n/g' "
fi
diff --git a/zdsfs/Makefile b/zdsfs/Makefile
index 579ce4b..07d7658 100644
--- a/zdsfs/Makefile
+++ b/zdsfs/Makefile
@@ -2,9 +2,8 @@ include ../common.mak
libs = $(rootdir)/libzds/libzds.a \
$(rootdir)/libvtoc/libvtoc.a \
- $(rootdir)/libu2s/libu2s.a \
- $(rootdir)/libutil/libutil.a \
- $(rootdir)/libdasd/libdasd.a
+ $(rootdir)/libdasd/libdasd.a \
+ $(rootdir)/libutil/libutil.a
ifeq (${HAVE_FUSE},0)
diff --git a/zipl/src/Makefile b/zipl/src/Makefile
index bed970c..fd77670 100644
--- a/zipl/src/Makefile
+++ b/zipl/src/Makefile
@@ -7,8 +7,7 @@ ALL_CPPFLAGS += -I../include -I../boot \
-D_FILE_OFFSET_BITS=64 $(NO_PIE_CFLAGS)
ALL_LDFLAGS += -Wl,-z,noexecstack $(NO_PIE_LDFLAGS)
-libs = $(rootdir)/libutil/libutil.a \
- $(rootdir)/libu2s/libu2s.a
+libs = $(rootdir)/libutil/libutil.a
objects = misc.o error.o scan.o job.o boot.o bootmap.o disk.o \
install.o zipl.o $(rootdir)/zipl/boot/data.o
diff --git a/zipl/src/install.c b/zipl/src/install.c
index a6c4333..20b53f5 100644
--- a/zipl/src/install.c
+++ b/zipl/src/install.c
@@ -23,7 +23,7 @@
#include <syslog.h>
#include <unistd.h>
-#include "lib/u2s.h"
+#include "lib/util_sys.h"
#include "boot.h"
#include "bootmap.h"
@@ -1132,7 +1132,7 @@ install_mvdump(char* const device[], struct job_target_data* target, int count,
parm.param[i].num_heads = info[i]->geo.heads;
parm.param[i].blocksize = info[i]->phy_block_size >> 8;
parm.param[i].devno = info[i]->devno;
- if (u2s_getbusid(device[i], busid)) {
+ if (util_sys_get_dev_addr(device[i], busid) != 0) {
error_text("Could not find bus-ID for '%s'", device[i]);
rc = -1;
goto out;
--
2.21.3
From 836d4b9f0f4387a3e35125be8ee50563501d6ab7 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Dan=20Hor=C3=A1k?= <dan@danny.cz>
Date: Thu, 7 Nov 2019 12:29:42 +0100
Subject: [PATCH 29/44] zdsfs: add online vtoc refresh (#1685536)
Description: Enable zdsfs to access datasets that were created after zdsfs was
mounted without the need to remount zdsfs.
This is done by re-reading the VTOC with every readdir system
call.
To ensure a consistent VTOC state the DASD device is reserved for
every VTOC read and released afterwards.
Upstream-ID: bc053a975af6bd8eecd31269656f305d4c744fbe
---
dasdview/dasdview.c | 2 +-
include/lib/dasd_base.h | 6 ++
include/lib/libzds.h | 83 ++++++++++++++++--
libdasd/dasd_ioctl.c | 39 +++++++++
libzds/libzds.c | 184 +++++++++++++++++-----------------------
zdsfs/zdsfs.c | 97 +++++++++++++++++----
6 files changed, 280 insertions(+), 131 deletions(-)
diff --git a/dasdview/dasdview.c b/dasdview/dasdview.c
index 3cf5629..3dc5890 100644
--- a/dasdview/dasdview.c
+++ b/dasdview/dasdview.c
@@ -1741,7 +1741,7 @@ static void dasdview_print_vtoc_raw(dasdview_info_t *info)
" rc=%d\n", rc);
exit(-1);
}
- rc = lzds_dasd_read_rawvtoc(info->dasd);
+ rc = lzds_dasd_alloc_rawvtoc(info->dasd);
if (rc == EINVAL) {
zt_error_print("dasdview: Cannot read VTOC because disk does"
" not contain valid VOL1 label.\n",
diff --git a/include/lib/dasd_base.h b/include/lib/dasd_base.h
index abd17f5..b12e1f9 100644
--- a/include/lib/dasd_base.h
+++ b/include/lib/dasd_base.h
@@ -203,6 +203,10 @@ struct hd_geometry {
#define BIODASDDISABLE _IO(DASD_IOCTL_LETTER, 0)
/* Enable the volume (for Linux) */
#define BIODASDENABLE _IO(DASD_IOCTL_LETTER, 1)
+/* Reserve the device for the current LPAR */
+#define BIODASDRSRV _IO(DASD_IOCTL_LETTER, 2)
+/* Release the device for the current LPAR */
+#define BIODASDRLSE _IO(DASD_IOCTL_LETTER, 3)
/* Get information on a dasd device (enhanced) */
#define BIODASDINFO2 _IOR(DASD_IOCTL_LETTER, 3, dasd_information2_t)
/* #define BIODASDFORMAT _IOW(IOCTL_LETTER,0,format_data_t) , deprecated */
@@ -239,5 +243,7 @@ int dasd_get_geo(const char *device, struct hd_geometry *geo);
int dasd_get_info(const char *device, dasd_information2_t *info);
int dasd_is_ro(const char *device, bool *ro);
int dasd_reread_partition_table(const char *device, int ntries);
+int dasd_disk_reserve(const char *device);
+int dasd_disk_release(const char *device);
#endif /* LIB_DASD_BASE_H */
diff --git a/include/lib/libzds.h b/include/lib/libzds.h
index 355bf77..6ae767b 100644
--- a/include/lib/libzds.h
+++ b/include/lib/libzds.h
@@ -112,6 +112,8 @@
*/
#define LIB_LIBZDS_H
+#include "lib/util_base.h"
+#include "lib/util_list.h"
#include "vtoc.h"
@@ -328,11 +330,74 @@ struct pds_member_entry {
*/
struct zdsroot;
+/**
+ * @struct raw_vtoc
+ * @brief The VTOC is a directory of data sets on one DASD
+ *
+ * As the VTOC is the data area on the DASD that describes all data sets,
+ * this library will often have to refer to the various records in the VTOC.
+ * To make this more efficient, we will read the whole VTOC once and identify
+ * all elements (DSCBs). The raw data of the VTOC tracks and the index to the
+ * DSCBs is stored.
+ */
+struct raw_vtoc {
+ /** @brief The raw track data */
+ char *rawdata;
+ /** @brief This size of the raw track data in bytes */
+ unsigned long long rawdatasize;
+ /** @brief An array with pointers to the various DSCBs in the rawdata */
+ char **vtocindex;
+ /** @brief Number of entries in the index */
+ unsigned int vtocindexcount;
+ /** @brief Number of records per VTOC track
+ *
+ * @note While the DS4DEVDT field in the format 4 DSCB names the number
+ * if DSCBs per VTOC track, we count the records, which is DS4DEVDT + 1
+ * for record 0.
+ */
+ unsigned int vtoc_rec_per_track;
+ /** @brief The track number at which the vtoc begins on the DASD */
+ unsigned int vtoctrackoffset;
+ /** @brief Start record of VTOC.
+ *
+ * The rawdata contains full tracks. This is the number of the first
+ * record that actually belongs to the VTOC
+ */
+ unsigned int vtocrecno;
+ /** @brief The DASD this vtoc was read from */
+ struct dasd *dasd;
+ /** @brief Detailed error messages in case of a problem */
+ struct errorlog *log;
+};
+
/**
* @struct dasd
* @brief Represents one physical device, may have a vtoc
*/
-struct dasd;
+struct dasd {
+ /** @brief List head used to store a list of DASDs in struct zdsroot */
+ struct util_list_node list;
+ /** @brief Name of the block device, e.g. /dev/dasde */
+ char *device;
+ /** @brief File descriptor for the block device.
+ *
+ * The device is kept open for as along as the library uses it.
+ * This lets the system know that the device is still in use.
+ */
+ int inusefd;
+ /* @brief where to find the volume label */
+ unsigned int label_block;
+ /** @brief Device geometry. How many cylinders does the DASD have. */
+ unsigned int cylinders;
+ /** @brief Device geometry. How many heads does the DASD have. */
+ unsigned int heads;
+ /** @brief The VTOC data that has been read from this device */
+ struct raw_vtoc *rawvtoc;
+ /** @brief The volume label that has been read from this device */
+ volume_label_t *vlabel;
+ /** @brief Detailed error messages in case of a problem */
+ struct errorlog *log;
+};
/**
* @struct dasditerator
@@ -350,12 +415,6 @@ struct dasditerator;
*/
struct dasdhandle;
-/**
- * @struct raw_vtoc
- * @brief The VTOC is a directory of data sets on one dasd
- */
-struct raw_vtoc;
-
/**
* @struct dscbiterator
* @brief allows to iterate over all DSCBs in a vtoc
@@ -572,7 +631,13 @@ int lzds_dasd_get_vlabel(struct dasd *dasd, struct volume_label **vlabel);
* @brief Read the vtoc data from device. The data as stored as part
* of the struct dasd.
*/
-int lzds_dasd_read_rawvtoc(struct dasd *dasd);
+int lzds_dasd_read_rawvtoc(struct dasd *dasd, struct raw_vtoc *vtoc);
+
+/**
+ * @brief Read the vtoc data from device. The data as stored as part
+ * of the struct dasd.
+ */
+int lzds_dasd_alloc_rawvtoc(struct dasd *dasd);
/**
* @brief Get the previously read raw_vtoc data.
@@ -787,6 +852,8 @@ int lzds_zdsroot_extract_datasets_from_dasd(struct zdsroot *root,
struct dasd *dasd);
+void lzds_dslist_free(struct zdsroot *root);
+
/** @} */ /* end of group libzds_functions_high */
diff --git a/libdasd/dasd_ioctl.c b/libdasd/dasd_ioctl.c
index ef1605e..22e6d21 100644
--- a/libdasd/dasd_ioctl.c
+++ b/libdasd/dasd_ioctl.c
@@ -281,3 +281,42 @@ int dasd_reread_partition_table(const char *device, int ntries)
return err;
}
+
+/*
+ * Reserve DASD disk.
+ *
+ * @param[in] device node device node's name
+ *
+ * @retval 0 in case of success
+ * @retval errno in case of failure
+ *
+ */
+int dasd_disk_reserve(const char *device)
+{
+ int fd;
+
+ fd = dasd_open_device(device, O_RDONLY);
+ RUN_IOCTL(fd, BIODASDRSRV, NULL);
+ dasd_close_device(fd);
+
+ return 0;
+}
+
+/*
+ * Release DASD disk
+ *
+ * @param[in] device node device node's name
+ *
+ * @retval 0 in case of success
+ * @retval errno in case of failure
+ */
+int dasd_disk_release(const char *device)
+{
+ int fd;
+
+ fd = dasd_open_device(device, O_RDONLY);
+ RUN_IOCTL(fd, BIODASDRLSE, NULL);
+ dasd_close_device(fd);
+
+ return 0;
+}
diff --git a/libzds/libzds.c b/libzds/libzds.c
index affb30c..c444769 100644
--- a/libzds/libzds.c
+++ b/libzds/libzds.c
@@ -22,8 +22,6 @@
#include "lib/dasd_base.h"
#include "lib/dasd_sys.h"
#include "lib/libzds.h"
-#include "lib/util_base.h"
-#include "lib/util_list.h"
#include "lib/vtoc.h"
/** @cond PRIVATE */
@@ -70,42 +68,6 @@ struct errormsg {
char text[ERRORMSG];
};
-/**
- * As the VTOC is the data area on the DASD that describes all data sets,
- * this library will often have to refer to the various records in the VTOC.
- * To make this more efficiant, we will read the whole VTOC once and identify
- * all elements (DSCBs). The raw data of the VTOC tracks and the index to the
- * DSCBs is stored.
- */
-struct raw_vtoc {
- /** @brief The raw track data */
- char *rawdata;
- /** @brief This size of the raw track data in bytes */
- unsigned long long rawdatasize;
- /** @brief An array with pointers to the various DSCBs in the rawdata */
- char **vtocindex;
- /** @brief Number of entries in the index */
- unsigned int vtocindexcount;
- /** @brief Number of records per VTOC track
- *
- * @note While the DS4DEVDT field in the format 4 DSCB names the number
- * if DSCBs per VTOC track, we count the records, which is DS4DEVDT + 1
- * for record 0.
- */
- unsigned int vtoc_rec_per_track;
- /** @brief The track number in which the vtoc begins on the DASD */
- unsigned int vtoctrackoffset;
- /** @brief Start record of VTOC.
- *
- * The rawdata contains full tracks. This is the number of the first
- * record that actually belongs to the VTOC */
- unsigned int vtocrecno;
- /** @brief The DASD this vtoc was read from */
- struct dasd *dasd;
- /** @brief Detailed error messages in case of a problem */
- struct errorlog *log;
-};
-
struct dscbiterator {
/** @brief The raw_vtoc this iterator refers to */
struct raw_vtoc *rawvtoc;
@@ -113,31 +75,6 @@ struct dscbiterator {
unsigned int i;
};
-struct dasd {
- /** @brief List head used to store a list of DASDs in struct zdsroot */
- struct util_list_node list;
- /** @brief Name of the block device, e.g. /dev/dasde */
- char *device;
- /** @brief File descriptor for the block device.
- *
- * The device is kept open for as along as the library uses it.
- * This lets the system know that the device is still in use.
- */
- int inusefd;
- /* @brief where to find the volume label */
- unsigned int label_block;
- /** @brief Device geometry. How many cylinders does the DASD have. */
- unsigned int cylinders;
- /** @brief Device geometry. How many heads does the DASD have. */
- unsigned int heads;
- /** @brief The VTOC data that has been read from this device */
- struct raw_vtoc *rawvtoc;
- /** @brief The volume label that has been read from this device */
- volume_label_t *vlabel;
- /** @brief Detailed error messages in case of a problem */
- struct errorlog *log;
-};
-
struct dasdhandle {
/** @brief The struct dasd this context relates to */
struct dasd *dasd;
@@ -412,6 +349,7 @@ int lzds_zdsroot_alloc(struct zdsroot **root)
return 0;
}
+
/**
* It should be noted that this frees all structures that are owned by the
* root structure as well. For example, a pointer to a struct dasd that
@@ -419,21 +357,11 @@ int lzds_zdsroot_alloc(struct zdsroot **root)
*
* @param[in] root Reference to the zdsroot structure that is to be freed.
*/
-void lzds_zdsroot_free(struct zdsroot *root)
+void lzds_dslist_free(struct zdsroot *root)
{
- struct dasd *dasd, *nextdasd;
struct dataset *ds, *nextds;
int i;
- if (!root)
- return;
-
- util_list_iterate_safe(root->dasdlist, dasd, nextdasd) {
- util_list_remove(root->dasdlist, dasd);
- dasd_free(dasd);
- }
- util_list_free(root->dasdlist);
-
util_list_iterate_safe(root->datasetlist, ds, nextds) {
util_list_remove(root->datasetlist, ds);
dataset_free_memberlist(ds);
@@ -442,6 +370,28 @@ void lzds_zdsroot_free(struct zdsroot *root)
errorlog_free(ds->log);
free(ds);
}
+}
+
+/**
+ * It should be noted that this frees all structures that are owned by the
+ * root structure as well. For example, a pointer to a struct dasd that
+ * has been returned by lzds_zdsroot_add_device is not valid anymore.
+ *
+ * @param[in] root Reference to the zdsroot structure that is to be freed.
+ */
+void lzds_zdsroot_free(struct zdsroot *root)
+{
+ struct dasd *dasd, *nextdasd;
+
+ if (!root)
+ return;
+
+ util_list_iterate_safe(root->dasdlist, dasd, nextdasd) {
+ util_list_remove(root->dasdlist, dasd);
+ dasd_free(dasd);
+ }
+ util_list_free(root->dasdlist);
+ lzds_dslist_free(root);
util_list_free(root->datasetlist);
errorlog_free(root->log);
free(root);
@@ -1443,7 +1393,7 @@ int lzds_raw_vtoc_get_dscb_from_cchhb(struct raw_vtoc *rv, cchhb_t *p,
* - EPROTO The VTOC data is not in a valid format.
* - EIO Other I/O error
*/
-int lzds_dasd_read_rawvtoc(struct dasd *dasd)
+int lzds_dasd_read_rawvtoc(struct dasd *dasd, struct raw_vtoc *rawvtoc)
{
unsigned long long vtoctrckno, vtocrecno;
unsigned int vtoctrack_start, vtoctrack_end, vtocindexsize;
@@ -1455,26 +1405,11 @@ int lzds_dasd_read_rawvtoc(struct dasd *dasd)
format4_label_t *f4;
unsigned long long rawvtocsize;
- struct raw_vtoc *rawvtoc = NULL;
volume_label_t *vlabel = NULL;
char *trackdata = NULL;
char vol1[] = {0xe5, 0xd6, 0xd3, 0xf1, 0x00}; /* "VOL1" in EBCDIC */
errorlog_clear(dasd->log);
- /* cleanup the old rawvtoc structures before we read new ones */
- rawvtoc = dasd->rawvtoc;
- dasd->rawvtoc = NULL;
- if (rawvtoc) {
- free(rawvtoc->rawdata);
- free(rawvtoc->vtocindex);
- free(rawvtoc);
- }
-
- rawvtoc = malloc(sizeof(*rawvtoc));
- if (!rawvtoc)
- return ENOMEM;
- memset(rawvtoc, 0, sizeof(*rawvtoc));
- rawvtoc->dasd = dasd;
rc = lzds_dasd_get_vlabel(dasd, &vlabel);
if (rc) {
@@ -1611,13 +1546,49 @@ int lzds_dasd_read_rawvtoc(struct dasd *dasd)
++i;
}
- dasd->rawvtoc = rawvtoc;
return 0;
cleanup:
- free(rawvtoc->vtocindex);
free(trackdata);
- free(rawvtoc);
+ return rc;
+}
+
+/**
+ * @param[in] dasd The struct dasd that represents the device we want to read
+ * the VTOC from.
+ * @return 0 on success, otherwise one of the following error codes:
+ * - ENOMEM Could not allocate internal structure due to lack of memory.
+ * - EINVAL The volume label has not yet been read or it is not valid.
+ * - EPROTO The VTOC data is not in a valid format.
+ * - EIO Other I/O error
+ */
+int lzds_dasd_alloc_rawvtoc(struct dasd *dasd)
+{
+ struct raw_vtoc *rawvtoc = NULL;
+ int rc;
+
+ /* cleanup the old rawvtoc structures before we read new ones */
+ rawvtoc = dasd->rawvtoc;
+ dasd->rawvtoc = NULL;
+ if (rawvtoc) {
+ free(rawvtoc->rawdata);
+ free(rawvtoc->vtocindex);
+ free(rawvtoc);
+ }
+
+ rawvtoc = malloc(sizeof(*rawvtoc));
+ if (!rawvtoc)
+ return ENOMEM;
+ memset(rawvtoc, 0, sizeof(*rawvtoc));
+ rawvtoc->dasd = dasd;
+
+ rc = lzds_dasd_read_rawvtoc(dasd, rawvtoc);
+ if (rc) {
+ free(rawvtoc->vtocindex);
+ free(rawvtoc);
+ } else {
+ dasd->rawvtoc = rawvtoc;
+ }
return rc;
}
@@ -2261,6 +2232,7 @@ out1:
static int dataset_merge_dataset(struct dataset *baseds, struct dataset *newds)
{
int k, l, dspcount;
+
for (k = 0; k < MAXVOLUMESPERDS; ++k) {
/* if both datasets have a part in position k,
* then something is wrong */
@@ -2280,18 +2252,20 @@ static int dataset_merge_dataset(struct dataset *baseds, struct dataset *newds)
* Since dsp[0] may not be set yet, we loop over the
* base dsp array until we find an entry.
*/
- for (l = 0; l < MAXVOLUMESPERDS; ++l)
- if (baseds->dsp[l]) {
- if (memcmp(baseds->dsp[l]->f1->DS1DSSN,
- newds->dsp[k]->f1->DS1DSSN,
- MAXVOLSER))
- return errorlog_add_message(
- &baseds->log, NULL, EPROTO,
- "merge dataset: part %d has incompatible"
- " base volume serial\n", k);
- else
- break;
- }
+ for (l = 0; l < MAXVOLUMESPERDS; ++l) {
+ if (!baseds->dsp[l])
+ continue;
+ if (memcmp(baseds->dsp[l]->f1->DS1DSSN,
+ newds->dsp[k]->f1->DS1DSSN,
+ MAXVOLSER))
+ return errorlog_add_message(
+ &baseds->log, NULL, EPROTO,
+ "merge dataset: part %d has incompatible base volume serial\n",
+ k);
+ else
+ break;
+ }
+
baseds->dsp[k] = newds->dsp[k];
baseds->dspcount++;
diff --git a/zdsfs/zdsfs.c b/zdsfs/zdsfs.c
index 462fda7..d7a83ee 100644
--- a/zdsfs/zdsfs.c
+++ b/zdsfs/zdsfs.c
@@ -52,6 +52,8 @@ struct zdsfs_info {
};
static struct zdsfs_info zdsfsinfo;
+static int zdsfs_create_meta_data_buffer(struct zdsfs_info *);
+static int zdsfs_verify_datasets(void);
struct zdsfs_file_info {
struct dshandle *dsh;
@@ -194,6 +196,48 @@ static int zdsfs_getattr(const char *path, struct stat *stbuf)
return 0;
}
+static void zdsfs_read_device(struct dasd *newdasd, const char *device)
+{
+ struct errorlog *log;
+ int rc;
+
+ rc = dasd_disk_reserve(device);
+ if (rc) {
+ fprintf(stderr, "error when reserving device %s: %s\n",
+ device, strerror(rc));
+ lzds_dasd_get_errorlog(newdasd, &log);
+ lzds_errorlog_fprint(log, stderr);
+ exit(1);
+ }
+ rc = lzds_dasd_alloc_rawvtoc(newdasd);
+ if (rc) {
+ fprintf(stderr, "error when reading VTOC from device %s: %s\n",
+ device, strerror(rc));
+ lzds_dasd_get_errorlog(newdasd, &log);
+ lzds_errorlog_fprint(log, stderr);
+ exit(1);
+ }
+ rc = lzds_zdsroot_extract_datasets_from_dasd(zdsfsinfo.zdsroot,
+ newdasd);
+ if (rc) {
+ fprintf(stderr,
+ "error when extracting data sets from dasd %s: %s\n",
+ device, strerror(rc));
+ lzds_zdsroot_get_errorlog(zdsfsinfo.zdsroot, &log);
+ lzds_errorlog_fprint(log, stderr);
+ exit(1);
+ }
+ rc = dasd_disk_release(device);
+ if (rc) {
+ fprintf(stderr, "error when releasing device %s: %s\n",
+ device, strerror(rc));
+ lzds_dasd_get_errorlog(newdasd, &log);
+ lzds_errorlog_fprint(log, stderr);
+ exit(1);
+ }
+}
+
+
static int zdsfs_statfs(const char *UNUSED(path), struct statvfs *statvfs)
{
struct dasditerator *dasdit;
@@ -240,6 +284,33 @@ static int zdsfs_statfs(const char *UNUSED(path), struct statvfs *statvfs)
return 0;
}
+
+static int zdsfs_update_vtoc(void)
+{
+ struct dasditerator *dasdit;
+ struct dasd *dasd;
+ int rc;
+
+ lzds_dslist_free(zdsfsinfo.zdsroot);
+ rc = lzds_zdsroot_alloc_dasditerator(zdsfsinfo.zdsroot, &dasdit);
+ if (rc)
+ return -ENOMEM;
+
+ while (!lzds_dasditerator_get_next_dasd(dasdit, &dasd))
+ zdsfs_read_device(dasd, dasd->device);
+
+ lzds_dasditerator_free(dasdit);
+ rc = zdsfs_verify_datasets();
+ if (rc)
+ return rc;
+
+ rc = zdsfs_create_meta_data_buffer(&zdsfsinfo);
+ if (rc)
+ return rc;
+
+ return 0;
+}
+
static int zdsfs_readdir(const char *path, void *buf, fuse_fill_dir_t filler,
off_t UNUSED(offset), struct fuse_file_info *UNUSED(fi))
{
@@ -253,6 +324,10 @@ static int zdsfs_readdir(const char *path, void *buf, fuse_fill_dir_t filler,
int rc;
int ispds, issupported;
+ rc = zdsfs_update_vtoc();
+ if (rc)
+ return rc;
+
/* we have two type of directories
* type one: the root directory contains all data sets
*/
@@ -329,6 +404,9 @@ static int zdsfs_open(const char *path, struct fuse_file_info *fi)
goto error1;
if (strcmp(path, "/"METADATAFILE) == 0) {
+ rc = zdsfs_update_vtoc();
+ if (rc)
+ return rc;
zfi->dsh = NULL;
zfi->is_metadata_file = 1;
zfi->metaread = 0;
@@ -596,6 +674,7 @@ static int zdsfs_verify_datasets(void)
if (rc)
return ENOMEM;
while (!lzds_dsiterator_get_next_dataset(dsit, &ds)) {
+ lzds_dataset_get_name(ds, &dsname);
lzds_dataset_get_is_complete(ds, &iscomplete);
if (!iscomplete) {
lzds_dataset_get_name(ds, &dsname);
@@ -822,23 +901,7 @@ static void zdsfs_process_device(const char *device)
lzds_errorlog_fprint(log, stderr);
exit(1);
}
- rc = lzds_dasd_read_rawvtoc(newdasd);
- if (rc) {
- fprintf(stderr, "error when reading VTOC from device %s:"
- " %s\n", device, strerror(rc));
- lzds_dasd_get_errorlog(newdasd, &log);
- lzds_errorlog_fprint(log, stderr);
- exit(1);
- }
- rc = lzds_zdsroot_extract_datasets_from_dasd(zdsfsinfo.zdsroot,
- newdasd);
- if (rc) {
- fprintf(stderr, "error when extracting data sets from dasd %s:"
- " %s\n", device, strerror(rc));
- lzds_zdsroot_get_errorlog(zdsfsinfo.zdsroot, &log);
- lzds_errorlog_fprint(log, stderr);
- exit(1);
- }
+ zdsfs_read_device(newdasd, device);
}
static void zdsfs_process_device_file(const char *devfile)
--
2.21.3
From db8ece16f1c2b4e514d39505397bc4d70052617b Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Dan=20Hor=C3=A1k?= <dan@danny.cz>
Date: Thu, 7 Nov 2019 12:31:54 +0100
Subject: [PATCH 30/44] zdev: add zfcp dix parameter handling (#1723852)
Description: The zfcp kernel module was changed to introduce separate
parameters for selecting DIF and DIF&DIX. This patch
implements the corresponding changes in chzdev and
lszdev.
Upstream-ID: 2c4c210ca8c641ad6e051eea65493202a16a5f4a
---
zdev/src/zfcp.c | 20 +++++++++++++++++---
1 file changed, 17 insertions(+), 3 deletions(-)
diff --git a/zdev/src/zfcp.c b/zdev/src/zfcp.c
index 03aa252..82dac57 100644
--- a/zdev/src/zfcp.c
+++ b/zdev/src/zfcp.c
@@ -75,16 +75,28 @@ static struct attrib zfcp_tattr_allow_lun_scan = {
static struct attrib zfcp_tattr_dif = {
.name = "dif",
- .title = "Enable DIF/DIX data consistency checking",
+ .title = "Enable DIF data consistency checking",
.desc =
- "Control the use of the end-to-end data consistency checking\n"
- "mechanism (DIF/DIX):\n"
+ "Control the use of the DIF data consistency checking\n"
+ "mechanism:\n"
" 0: DIF is disabled\n"
" 1: DIF is enabled when supported by the FCP device hardware\n",
.defval = "0",
.accept = ACCEPT_ARRAY(ACCEPT_RANGE(0, 1)),
};
+static struct attrib zfcp_tattr_dix = {
+ .name = "dix",
+ .title = "Enable DIF&DIX data consistency checking",
+ .desc =
+ "Control the use of the end-to-end data consistency checking\n"
+ "mechanism (DIF&DIX):\n"
+ " 0: DIF&DIX is disabled\n"
+ " 1: DIF&DIX is enabled when supported by the FCP device hardware\n",
+ .defval = "0",
+ .accept = ACCEPT_ARRAY(ACCEPT_RANGE(0, 1)),
+};
+
static struct attrib zfcp_tattr_datarouter = {
.name = "datarouter",
.title = "Enable hardware data routing",
@@ -160,6 +172,7 @@ static void all_bools_to_num(struct setting_list *list)
util_list_iterate(&list->list, s) {
if (s->attrib == &zfcp_tattr_allow_lun_scan ||
s->attrib == &zfcp_tattr_dif ||
+ s->attrib == &zfcp_tattr_dix ||
s->attrib == &zfcp_tattr_datarouter ||
s->attrib == &zfcp_tattr_no_auto_port_rescan) {
/* Convert Y to 1 and N to 0. */
@@ -295,6 +308,7 @@ struct devtype zfcp_devtype = {
&zfcp_tattr_queue_depth,
&zfcp_tattr_allow_lun_scan,
&zfcp_tattr_dif,
+ &zfcp_tattr_dix,
&zfcp_tattr_datarouter,
&zfcp_tattr_no_auto_port_rescan,
&zfcp_tattr_port_scan_ratelimit,
--
2.21.3
From 41bb1d87abfe5f4b856ba885109749d42959a812 Mon Sep 17 00:00:00 2001
From: Philipp Rudo <prudo@linux.ibm.com>
Date: Wed, 21 Aug 2019 12:34:43 +0200
Subject: [PATCH 31/44] zipl: Fix error message printed with --dumptofs
(#1750307)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
The option --dump does not exist. Adjust it to --dumpto.
Signed-off-by: Philipp Rudo <prudo@linux.ibm.com>
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
---
zipl/src/job.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/zipl/src/job.c b/zipl/src/job.c
index e68d1b7..5dcebaf 100644
--- a/zipl/src/job.c
+++ b/zipl/src/job.c
@@ -114,7 +114,7 @@ get_command_line(int argc, char* argv[], struct command_line* line)
break;
case 'D':
error_reason("dumptofs has been deprecated, use "
- "--dump instead");
+ "--dumpto instead");
rc = -1;
break;
case 'M':
--
2.21.3
From 47f3a82078811bd4dfad2726ed3ba38cc0fe2e3f Mon Sep 17 00:00:00 2001
From: Stefan Haberland <sth@linux.ibm.com>
Date: Wed, 4 Sep 2019 13:01:53 +0200
Subject: [PATCH 32/44] zipl: set correct secure IPL default value (#1750326)
Set secure IPL to auto as default value to match documented behavior.
(cherry picked from commit 367598b18738d26e92302812deb5fdbdc2617ba6)
---
zipl/src/job.c | 1 +
1 file changed, 1 insertion(+)
diff --git a/zipl/src/job.c b/zipl/src/job.c
index 5dcebaf..1178a7d 100644
--- a/zipl/src/job.c
+++ b/zipl/src/job.c
@@ -1880,6 +1880,7 @@ job_get(int argc, char* argv[], struct job_data** data)
job->add_files = cmdline.add_files;
job->data.mvdump.force = cmdline.force;
job->dry_run = cmdline.dry_run;
+ job->is_secure = SECURE_BOOT_AUTO;
/* Get job data from user input */
if (cmdline.help) {
job->command_line = 1;
--
2.21.3
From d64cb3431d7cd51a4b4c24ed3e0a2e50a024b50b Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Dan=20Hor=C3=A1k?= <dan@danny.cz>
Date: Thu, 7 Nov 2019 12:49:06 +0100
Subject: [PATCH 33/44] zkey: various enhancements (#1725881)
Description: Enhancements to zkey in response to first (customer) experiences
Upstream-ID: 9561a0b98385180441b2f5aa338fb583a94d4c40
Upstream-ID: a648dbb0149513885eba4ced84ca9e1e65c6caca
Upstream-ID: d95dc6d698b5a666920201e39f3ccd0b1afb918c
Upstream-ID: b26dbfe8320c9ddb72b86efbe532b616243e0e3f
Upstream-ID: a69470d7e02af2d7d6a4c781eef57727c0672fc0
Upstream-ID: 79ce12055369009be3d5d0a170e8d6edde0e96f2
Upstream-ID: ed6e3b727059e0533b5ccf7bb697a7fe1a5f151b
Upstream-ID: 951bd1ae54a074cf59a4719909d6f6122f5c0448
Upstream-ID: ec9c67189e1b841a922a627a9066d17222b51a01
Upstream-ID: 90dc65659fa868ab446db07cac82235fa8a184a5
Upstream-ID: b0c7965234c80af83b0419dc3781aff323cd04d4
Upstream-ID: 5a0c93443c6a1d31d238444c798272979f0c5bf3
Upstream-ID: 3ed8ab4e2a071180cfdce1a673d5101b7a09e171
Upstream-ID: 11bfa1d3c8bdec60c5d0b04e93431e55c264d5ef
Upstream-ID: e693173eef75b9c67ad5516c2e1bc3d5ded5bbee
Upstream-ID: e4cd42900f1922b25d99af1134e2695799d14248
Upstream-ID: f97d048643ef105e2c4dcdcf656d4dee5b2d7a15
---
zkey/keystore.c | 315 ++++++++++++++++++++++++++++++-----------
zkey/keystore.h | 24 ++--
zkey/pkey.c | 10 +-
zkey/zkey-cryptsetup.1 | 36 +++++
zkey/zkey-cryptsetup.c | 81 ++++++++++-
zkey/zkey.1 | 244 +++++++++++++++++++++++++++++--
zkey/zkey.c | 282 ++++++++++++++++++++++++++++++++++--
7 files changed, 866 insertions(+), 126 deletions(-)
diff --git a/zkey/keystore.c b/zkey/keystore.c
index a4ad634..33c8f93 100644
--- a/zkey/keystore.c
+++ b/zkey/keystore.c
@@ -259,13 +259,13 @@ static int _keystore_set_file_permission(struct keystore *keystore,
if (chmod(filename, keystore->mode) != 0) {
rc = -errno;
- warnx("chmod faild on file '%s': %s", filename, strerror(-rc));
+ warnx("chmod failed on file '%s': %s", filename, strerror(-rc));
return rc;
}
if (chown(filename, geteuid(), keystore->owner) != 0) {
rc = -errno;
- warnx("chown faild on file '%s': %s", filename, strerror(-rc));
+ warnx("chown failed on file '%s': %s", filename, strerror(-rc));
return rc;
}
@@ -369,7 +369,7 @@ out:
return 0;
}
-typedef int (*check_association_t)(const char *value, bool remove,
+typedef int (*check_association_t)(const char *value, bool remove, bool set,
char **normalized, void *private);
/**
@@ -407,7 +407,7 @@ static int _keystore_set_association(struct properties *key_props,
for (i = 0; newvals[i] != NULL; i++) {
if (check_func != NULL) {
- rc = check_func(newvals[i], 0, &normalized,
+ rc = check_func(newvals[i], 0, 1, &normalized,
check_private);
if (rc != 0)
goto out;
@@ -488,7 +488,7 @@ static int _keystore_add_association(struct properties *key_props,
for (i = 0; newvals[i] != NULL; i++) {
if (check_func != NULL) {
- rc = check_func(newvals[i], 0, &normalized,
+ rc = check_func(newvals[i], 0, 0, &normalized,
check_private);
if (rc != 0)
goto out;
@@ -567,7 +567,7 @@ static int _keystore_remove_association(struct properties *key_props,
for (i = 0; delvals[i] != NULL; i++) {
if (check_func != NULL) {
- rc = check_func(delvals[i], 1, &normalized,
+ rc = check_func(delvals[i], 1, 0, &normalized,
check_private);
if (rc != 0)
goto out;
@@ -1085,20 +1085,27 @@ out:
return rc;
}
+struct apqn_check {
+ bool noonlinecheck;
+ bool nomsg;
+};
+
/**
* Checks an APQN value for its syntax. This is a callback function for
* function _keystore_change_association().
*
* @param[in] apqn the APQN value to check
* @param[in] remove if true the apqn is removed
+ * @param[in] set if true the apqn is set (not used here)
* @param[out] normalized normalized value on return or NULL if no change
- * @param[in] private private data (not used here)
+ * @param[in] private private data (struct apqn_check)
*
* @returns 0 if successful, a negative errno value otherwise
*/
-static int _keystore_apqn_check(const char *apqn, bool remove,
- char **normalized, void *UNUSED(private))
+static int _keystore_apqn_check(const char *apqn, bool remove, bool UNUSED(set),
+ char **normalized, void *private)
{
+ struct apqn_check *info = (struct apqn_check *)private;
int rc, card, domain;
regmatch_t pmatch[1];
regex_t reg_buf;
@@ -1124,15 +1131,16 @@ static int _keystore_apqn_check(const char *apqn, bool remove,
util_asprintf(normalized, "%02x.%04x", card, domain);
- if (remove) {
+ if (remove || info->noonlinecheck) {
rc = 0;
goto out;
}
rc = _keystore_is_apqn_online(card, domain);
if (rc != 1) {
- warnx("The APQN %02x.%04x is %s", card, domain,
- rc == -1 ? "not a CCA card" : "not online");
+ if (info->nomsg == 0)
+ warnx("The APQN %02x.%04x is %s", card, domain,
+ rc == -1 ? "not a CCA card" : "not online");
rc = -EIO;
goto out;
} else {
@@ -1149,6 +1157,7 @@ struct volume_check {
struct keystore *keystore;
const char *name;
const char *volume;
+ bool set;
};
/**
@@ -1173,6 +1182,11 @@ static int _keystore_volume_check_process(struct keystore *UNUSED(keystore),
{
struct volume_check *info = (struct volume_check *)private;
+ if (info->set) {
+ if (strcmp(name, info->name) == 0)
+ return 0;
+ }
+
warnx("Key '%s' is already associated with volume '%s'", name,
info->volume);
return -EINVAL;
@@ -1204,12 +1218,13 @@ static int _keystore_is_block_device(const char *volume)
*
* @param[in] volume the Volume value to check
* @param[in] remove if true the volume is removed
+ * @param[in] set if true the volume is set
* @param[out] normalized normalized value on return or NULL if no change
* @param[in] private private data: struct volume_check
*
* @returns 0 if successful, a negative errno value otherwise
*/
-static int _keystore_volume_check(const char *volume, bool remove,
+static int _keystore_volume_check(const char *volume, bool remove, bool set,
char **normalized, void *private)
{
struct volume_check *info = (struct volume_check *)private;
@@ -1250,6 +1265,7 @@ static int _keystore_volume_check(const char *volume, bool remove,
goto out;
}
+ info->set = set;
rc = _keystore_process_filtered(info->keystore, NULL, info->volume,
NULL, NULL,
_keystore_volume_check_process, info);
@@ -1359,7 +1375,7 @@ struct keystore *keystore_new(const char *directory, bool verbose)
util_assert(directory != NULL, "Internal error: directory is NULL");
if (stat(directory, &sb) != 0) {
- warnx("'%s' does not exist", directory);
+ warnx("Can not access '%s': %s", directory, strerror(errno));
return NULL;
}
if (!(sb.st_mode & S_IFDIR)) {
@@ -1543,6 +1559,8 @@ static int _keystore_set_default_properties(struct properties *key_props)
* key (optional, can be NULL)
* @param[in] apqns a comma separated list of APQNs associated with this
* key (optional, can be NULL)
+ * @param[in] noapqncheck if true, the specified APQN(s) are not checked for
+ * existence and type.
* @param[in] sector_size the sector size to use with dm-crypt. It must be power
* of two and in range 512 - 4096 bytes. 0 means that
* the sector size is not specified and the system
@@ -1554,10 +1572,14 @@ static int _keystore_create_info_file(struct keystore *keystore,
const struct key_filenames *filenames,
const char *description,
const char *volumes, const char *apqns,
+ bool noapqncheck,
size_t sector_size,
const char *volume_type)
{
- struct volume_check vol_check = { .keystore = keystore, .name = name };
+ struct volume_check vol_check = { .keystore = keystore, .name = name,
+ .set = 0 };
+ struct apqn_check apqn_check = { .noonlinecheck = noapqncheck,
+ .nomsg = 0 };
struct properties *key_props;
char temp[10];
int rc;
@@ -1583,7 +1605,8 @@ static int _keystore_create_info_file(struct keystore *keystore,
rc = _keystore_change_association(key_props, PROP_NAME_APQNS,
apqns != NULL ? apqns : "",
- "APQN", _keystore_apqn_check, NULL);
+ "APQN", _keystore_apqn_check,
+ &apqn_check);
if (rc != 0)
goto out;
@@ -1642,15 +1665,18 @@ out:
}
/**
- * Extracts a card/domain pair from the specified APQns, or uses AUTOSELECT
- * if no APQNs are specified.
+ * Extracts an online card/domain pair from the specified APQns. If none of the
+ * specified APQNs are online, then -ENODEV is returned.
+ * If no APQNs are specified at all, then it uses AUTOSELECT and returns zero.
*/
static int _keystore_get_card_domain(const char *apqns, unsigned int *card,
unsigned int *domain)
{
+ struct apqn_check apqn_check = { .noonlinecheck = 0, .nomsg = 1 };
char **apqn_list;
char *normalized = NULL;
int rc = 0;
+ int i;
*card = AUTOSELECT;
*domain = AUTOSELECT;
@@ -1662,17 +1688,23 @@ static int _keystore_get_card_domain(const char *apqns, unsigned int *card,
if (apqn_list[0] == NULL)
goto out;
- rc = _keystore_apqn_check(apqn_list[0], 0, &normalized, NULL);
- if (normalized != NULL)
- free(normalized);
- if (rc != 0)
- goto out;
+ for (i = 0; apqn_list[i] != NULL; i++) {
+ rc = _keystore_apqn_check(apqn_list[i], 0, 0, &normalized,
+ &apqn_check);
+ if (normalized != NULL)
+ free(normalized);
+ if (rc == -EINVAL)
+ goto out;
+ if (rc != 0)
+ continue;
- if (sscanf(apqn_list[0], "%x.%x", card, domain) != 2) {
- rc = -EINVAL;
- goto out;
+ if (sscanf(apqn_list[i], "%x.%x", card, domain) == 2)
+ goto found;
}
+ warnx("None of the specified APQNs is online or of type CCA");
+ rc = -ENODEV;
+found:
out:
str_list_free_string_array(apqn_list);
return rc;
@@ -1688,6 +1720,8 @@ out:
* key (optional, can be NULL)
* @param[in] apqns a comma separated list of APQNs associated with this
* key (optional, can be NULL)
+ * @param[in] noapqncheck if true, the specified APQN(s) are not checked for
+ * existence and type.
* @param[in] sector_size the sector size to use with dm-crypt. It must be power
* of two and in range 512 - 4096 bytes. 0 means that
* the sector size is not specified and the system
@@ -1704,9 +1738,10 @@ out:
*/
int keystore_generate_key(struct keystore *keystore, const char *name,
const char *description, const char *volumes,
- const char *apqns, size_t sector_size,
- size_t keybits, bool xts, const char *clear_key_file,
- const char *volume_type, int pkey_fd)
+ const char *apqns, bool noapqncheck,
+ size_t sector_size, size_t keybits, bool xts,
+ const char *clear_key_file, const char *volume_type,
+ int pkey_fd)
{
struct key_filenames file_names = { NULL, NULL, NULL };
struct properties *key_props = NULL;
@@ -1748,7 +1783,7 @@ int keystore_generate_key(struct keystore *keystore, const char *name,
rc = _keystore_create_info_file(keystore, name, &file_names,
description, volumes, apqns,
- sector_size, volume_type);
+ noapqncheck, sector_size, volume_type);
if (rc != 0)
goto out_free_props;
@@ -1781,6 +1816,8 @@ out_free_key_filenames:
* key (optional, can be NULL)
* @param[in] apqns a comma separated list of APQNs associated with this
* key (optional, can be NULL)
+ * @param[in] noapqncheck if true, the specified APQN(s) are not checked for
+ * existence and type.
* @param[in] sector_size the sector size to use with dm-crypt. It must be power
* of two and in range 512 - 4096 bytes. 0 means that
* the sector size is not specified and the system
@@ -1792,7 +1829,7 @@ out_free_key_filenames:
*/
int keystore_import_key(struct keystore *keystore, const char *name,
const char *description, const char *volumes,
- const char *apqns, size_t sector_size,
+ const char *apqns, bool noapqncheck, size_t sector_size,
const char *import_file, const char *volume_type)
{
struct key_filenames file_names = { NULL, NULL, NULL };
@@ -1832,7 +1869,7 @@ int keystore_import_key(struct keystore *keystore, const char *name,
rc = _keystore_create_info_file(keystore, name, &file_names,
description, volumes, apqns,
- sector_size, volume_type);
+ noapqncheck, sector_size, volume_type);
if (rc != 0)
goto out_free_props;
@@ -1870,6 +1907,8 @@ out_free_key_filenames:
* key, or an APQN prefixed with '+' or '-' to add or
* remove that APQN respectively. If NULL then the APQNs
* are not changed.
+ * @param[in] noapqncheck if true, the specified APQN(s) are not checked for
+ * existence and type.
* @param[in] sector_size the sector size to use with dm-crypt. It must be power
* of two and in range 512 - 4096 bytes. 0 means that
* the sector size is not specified and the system
@@ -1883,10 +1922,13 @@ out_free_key_filenames:
*/
int keystore_change_key(struct keystore *keystore, const char *name,
const char *description, const char *volumes,
- const char *apqns, long int sector_size,
- const char *volume_type)
+ const char *apqns, bool noapqncheck,
+ long int sector_size, const char *volume_type)
{
- struct volume_check vol_check = { .keystore = keystore, .name = name };
+ struct volume_check vol_check = { .keystore = keystore, .name = name,
+ .set = 0 };
+ struct apqn_check apqn_check = { .noonlinecheck = noapqncheck,
+ .nomsg = 0 };
struct key_filenames file_names = { NULL, NULL, NULL };
struct properties *key_props = NULL;
char temp[30];
@@ -1931,7 +1973,8 @@ int keystore_change_key(struct keystore *keystore, const char *name,
if (apqns != NULL) {
rc = _keystore_change_association(key_props, PROP_NAME_APQNS,
apqns, "APQN",
- _keystore_apqn_check, NULL);
+ _keystore_apqn_check,
+ &apqn_check);
if (rc != 0)
goto out;
}
@@ -1982,10 +2025,6 @@ int keystore_change_key(struct keystore *keystore, const char *name,
goto out;
}
- rc = _keystore_set_file_permission(keystore, file_names.info_filename);
- if (rc != 0)
- goto out;
-
pr_verbose(keystore, "Successfully changed key '%s'", name);
out:
@@ -2269,6 +2308,7 @@ static void _keystore_print_record(struct util_rec *rec,
struct validate_info {
struct util_rec *rec;
int pkey_fd;
+ bool noapqncheck;
unsigned long int num_valid;
unsigned long int num_invalid;
unsigned long int num_warnings;
@@ -2422,8 +2462,9 @@ static int _keystore_process_validate(struct keystore *keystore,
"master key\n", 0);
info->num_warnings++;
}
- if (_keystore_display_apqn_status(properties, name) != 0)
- info->num_warnings++;
+ if (info->noapqncheck == 0)
+ if (_keystore_display_apqn_status(properties, name) != 0)
+ info->num_warnings++;
if (_keystore_display_volume_status(properties, name) != 0)
info->num_warnings++;
@@ -2439,11 +2480,16 @@ out:
*
* @param[in] keystore the key store
* @param[in] name_filter the name filter to select the key (can be NULL)
+ * @param[in] apqn_filter the APQN filter to select the key (can be NULL)
+ * @param[in] noapqncheck if true, the specified APQN(s) are not checked for
+ * existence and type.
+ * @param[in] pkey_fd the file descriptor of /dev/pkey
*
* @returns 0 for success or a negative errno in case of an error
*/
int keystore_validate_key(struct keystore *keystore, const char *name_filter,
- const char *apqn_filter, int pkey_fd)
+ const char *apqn_filter, bool noapqncheck,
+ int pkey_fd)
{
struct validate_info info;
struct util_rec *rec;
@@ -2454,6 +2500,7 @@ int keystore_validate_key(struct keystore *keystore, const char *name_filter,
rec = _keystore_setup_record(1);
info.pkey_fd = pkey_fd;
+ info.noapqncheck = noapqncheck;
info.rec = rec;
info.num_valid = 0;
info.num_invalid = 0;
@@ -2683,10 +2730,6 @@ static int _keystore_process_reencipher(struct keystore *keystore,
if (rc != 0)
goto out;
- rc = _keystore_set_file_permission(keystore, out_file);
- if (rc != 0)
- goto out;
-
if (params.complete || params.inplace == 1) {
rc = _keystore_set_timestamp_property(properties,
PROP_NAME_REENC_TIME);
@@ -2712,11 +2755,6 @@ static int _keystore_process_reencipher(struct keystore *keystore,
goto out;
}
- rc = _keystore_set_file_permission(keystore,
- file_names->info_filename);
- if (rc != 0)
- goto out;
-
util_asprintf(&temp, "The following LUKS2 volumes are "
"encrypted with key '%s'. You should also "
"re-encipher the volume key of those volumes "
@@ -2846,7 +2884,7 @@ int keystore_copy_key(struct keystore *keystore, const char *name,
const char *newname, const char *volumes)
{
struct volume_check vol_check = { .keystore = keystore,
- .name = newname };
+ .name = newname, .set = 0 };
struct key_filenames file_names = { NULL, NULL, NULL };
struct key_filenames new_names = { NULL, NULL, NULL };
struct properties *key_prop = NULL;
@@ -3255,6 +3293,13 @@ static int _keystore_execute_cmd(const char *cmd,
struct crypt_info {
bool execute;
+ bool batch_mode;
+ const char *keyfile;
+ size_t keyfile_offset;
+ size_t keyfile_size;
+ size_t tries;
+ bool open;
+ bool format;
char **volume_filter;
int (*process_func)(struct keystore *keystore,
const char *volume,
@@ -3293,16 +3338,46 @@ static int _keystore_process_cryptsetup(struct keystore *keystore,
const char *volume_type,
struct crypt_info *info)
{
+ char *keyfile_opt = NULL, *offset_opt = NULL;
+ char *size_opt = NULL, *tries_opt = NULL;
+ char *common_passphrase_options;
+ size_t common_len;
char temp[100];
int rc = 0;
char *cmd;
sprintf(temp, "--sector-size %lu ", sector_size);
+ if (info->keyfile) {
+ util_asprintf(&keyfile_opt, "--key-file '%s' ", info->keyfile);
+ if (info->keyfile_offset > 0)
+ util_asprintf(&offset_opt, "--keyfile-offset %lu ",
+ info->keyfile_offset);
+ if (info->keyfile_size > 0)
+ util_asprintf(&size_opt, "--keyfile-size %lu ",
+ info->keyfile_size);
+ }
+ if (info->tries > 0)
+ util_asprintf(&tries_opt, "--tries %lu ", info->tries);
+ util_asprintf(&common_passphrase_options, "%s%s%s%s",
+ keyfile_opt != NULL ? keyfile_opt : "",
+ offset_opt != NULL ? offset_opt : "",
+ size_opt != NULL ? size_opt : "",
+ tries_opt != NULL ? tries_opt : "");
+ common_len = strlen(common_passphrase_options);
+ free(keyfile_opt);
+ free(offset_opt);
+ free(size_opt);
+ free(tries_opt);
+
if (strcasecmp(volume_type, VOLUME_TYPE_PLAIN) == 0) {
+ if (info->format)
+ return 0;
+
util_asprintf(&cmd,
- "cryptsetup plainOpen %s--key-file '%s' "
+ "cryptsetup plainOpen %s%s--key-file '%s' "
"--key-size %lu --cipher %s %s%s %s",
+ info->batch_mode ? "-q " : "",
keystore->verbose ? "-v " : "", key_file_name,
key_file_size * 8, cipher_spec,
sector_size > 0 ? temp : "", volume, dmname);
@@ -3314,39 +3389,69 @@ static int _keystore_process_cryptsetup(struct keystore *keystore,
printf("%s\n", cmd);
}
} else if (strcasecmp(volume_type, VOLUME_TYPE_LUKS2) == 0) {
- util_asprintf(&cmd,
- "cryptsetup luksFormat %s--type luks2 "
- "--master-key-file '%s' --key-size %lu "
- "--cipher %s %s%s",
- keystore->verbose ? "-v " : "", key_file_name,
- key_file_size * 8, cipher_spec,
- sector_size > 0 ? temp : "", volume);
-
- if (info->execute) {
- printf("Executing: %s\n", cmd);
- rc = _keystore_execute_cmd(cmd, "cryptsetup");
+ if (info->open) {
+ util_asprintf(&cmd,
+ "cryptsetup luksOpen %s%s%s%s %s",
+ info->batch_mode ? "-q " : "",
+ keystore->verbose ? "-v " : "",
+ common_len > 0 ?
+ common_passphrase_options : "",
+ volume, dmname);
+
+ if (info->execute) {
+ printf("Executing: %s\n", cmd);
+ rc = _keystore_execute_cmd(cmd, "cryptsetup");
+ } else {
+ printf("%s\n", cmd);
+ }
} else {
- printf("%s\n", cmd);
- }
-
- free(cmd);
- if (rc != 0)
- return rc;
-
- util_asprintf(&cmd,
- "zkey-cryptsetup setvp %s%s", volume,
- keystore->verbose ? " -V " : "");
+ /*
+ * Use PBKDF2 as key derivation function for LUKS2
+ * volumes. LUKS2 uses Argon2i as default, but this
+ * might cause out-of-memory errors when multiple LUKS2
+ * volumes are opened automatically via /etc/crypttab
+ */
+ util_asprintf(&cmd,
+ "cryptsetup luksFormat %s%s--type luks2 "
+ "--master-key-file '%s' --key-size %lu "
+ "--cipher %s --pbkdf pbkdf2 %s%s%s",
+ info->batch_mode ? "-q " : "",
+ keystore->verbose ? "-v " : "",
+ key_file_name, key_file_size * 8,
+ cipher_spec, common_len > 0 ?
+ common_passphrase_options : "",
+ sector_size > 0 ? temp : "", volume);
+
+ if (info->execute) {
+ printf("Executing: %s\n", cmd);
+ rc = _keystore_execute_cmd(cmd, "cryptsetup");
+ } else {
+ printf("%s\n", cmd);
+ }
- if (info->execute) {
- printf("Executing: %s\n", cmd);
- rc = _keystore_execute_cmd(cmd, "zkey-cryptsetup");
- } else {
- printf("%s\n", cmd);
+ free(cmd);
+ if (rc != 0)
+ return rc;
+
+ util_asprintf(&cmd,
+ "zkey-cryptsetup setvp %s %s%s", volume,
+ common_len > 0 ?
+ common_passphrase_options : "",
+ keystore->verbose ? "-V" : "");
+
+ if (info->execute) {
+ printf("Executing: %s\n", cmd);
+ rc = _keystore_execute_cmd(cmd,
+ "zkey-cryptsetup");
+ } else {
+ printf("%s\n", cmd);
+ }
}
} else {
return -EINVAL;
}
+ free(common_passphrase_options);
free(cmd);
return rc;
}
@@ -3376,7 +3481,7 @@ static int _keystore_process_crypttab(struct keystore *UNUSED(keystore),
size_t key_file_size,
size_t sector_size,
const char *volume_type,
- struct crypt_info *UNUSED(info))
+ struct crypt_info *info)
{
char temp[1000];
@@ -3395,11 +3500,22 @@ static int _keystore_process_crypttab(struct keystore *UNUSED(keystore),
}
sprintf(temp, ",sector-size=%lu", sector_size);
- printf("%s\t%s\t%s\tplain,cipher=%s,size=%lu,hash=plain%s\n",
+ 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\n", dmname, volume);
+ printf("%s\t%s\t%s\tluks", dmname, volume,
+ info->keyfile != NULL ? info->keyfile : "none");
+ if (info->keyfile != NULL) {
+ if (info->keyfile_offset > 0)
+ printf(",keyfile-offset=%lu",
+ info->keyfile_offset);
+ if (info->keyfile_size > 0)
+ printf(",keyfile-size=%lu", info->keyfile_size);
+ }
+ if (info->tries > 0)
+ printf(",tries=%lu", info->tries);
+ printf("\n");
} else {
return -EINVAL;
}
@@ -3584,11 +3700,21 @@ out:
* @param[in] execute If TRUE the cryptsetup command is executed,
* otherwise it is printed to stdout
* @param[in] volume_type the type of volume to generate cryptsetup cmds for
- * *
+ * @param[in] keyfile If non-NULL, specifies the name of the file to
+ * read the passphrase from.
+ * @param[in] keyfile_offset the offset in bytes for reading from keyfile
+ * @param[in] keyfile_size the size in bytes for reading from keyfile
+ * @param[in] tries the number of tries for passphrase entry
+ * @param[in] batch_mode If TRUE, suppress cryptsetup confirmation questions
+ * @param[in] open If TRUE, generate luksOpen/plainOpen commands
+ * @param[in] format If TRUE, generate luksFormat commands
* @returns 0 for success or a negative errno in case of an error
*/
int keystore_cryptsetup(struct keystore *keystore, const char *volume_filter,
- bool execute, const char *volume_type)
+ bool execute, const char *volume_type,
+ const char *keyfile, size_t keyfile_offset,
+ size_t keyfile_size, size_t tries, bool batch_mode,
+ bool open, bool format)
{
struct crypt_info info = { 0 };
int rc;
@@ -3605,6 +3731,13 @@ int keystore_cryptsetup(struct keystore *keystore, const char *volume_filter,
}
info.execute = execute;
+ info.open = open;
+ info.format = format;
+ info.batch_mode = batch_mode;
+ info.keyfile = keyfile;
+ info.keyfile_offset = keyfile_offset;
+ info.keyfile_size = keyfile_size;
+ info.tries = tries;
info.volume_filter = str_list_split(volume_filter);
info.process_func = _keystore_process_cryptsetup;
@@ -3636,11 +3769,17 @@ int keystore_cryptsetup(struct keystore *keystore, const char *volume_filter,
* for the volume filter. If not specified, the filter
* checks the volume part only.
* @param[in] volume_type the type of volume to generate crypttab entries for
+ * @param[in] keyfile If non-NULL, specifies the name of the file to
+ * read the passphrase from.
+ * @param[in] keyfile_offset the offset in bytes for reading from keyfile
+ * @param[in] keyfile_size the size in bytes for reading from keyfile
+ * @param[in] tries the number of tries for passphrase entry
*
* @returns 0 for success or a negative errno in case of an error
*/
int keystore_crypttab(struct keystore *keystore, const char *volume_filter,
- const char *volume_type)
+ const char *volume_type, const char *keyfile,
+ size_t keyfile_offset, size_t keyfile_size, size_t tries)
{
struct crypt_info info = { 0 };
int rc;
@@ -3656,6 +3795,10 @@ int keystore_crypttab(struct keystore *keystore, const char *volume_filter,
return -EINVAL;
}
+ info.keyfile = keyfile;
+ info.keyfile_offset = keyfile_offset;
+ info.keyfile_size = keyfile_size;
+ info.tries = tries;
info.volume_filter = str_list_split(volume_filter);
info.process_func = _keystore_process_crypttab;
diff --git a/zkey/keystore.h b/zkey/keystore.h
index 11a2103..16ecc62 100644
--- a/zkey/keystore.h
+++ b/zkey/keystore.h
@@ -28,25 +28,27 @@ struct keystore *keystore_new(const char *directory, bool verbose);
int keystore_generate_key(struct keystore *keystore, const char *name,
const char *description, const char *volumes,
- const char *apqns, size_t sector_size,
- size_t keybits, bool xts, const char *clear_key_file,
- const char *volume_type, int pkey_fd);
+ const char *apqns, bool noapqncheck,
+ size_t sector_size, size_t keybits, bool xts,
+ const char *clear_key_file, const char *volume_type,
+ int pkey_fd);
int keystore_import_key(struct keystore *keystore, const char *name,
const char *description, const char *volumes,
- const char *apqns, size_t sector_size,
+ const char *apqns, bool noapqncheck, size_t sector_size,
const char *import_file, const char *volume_type);
int keystore_change_key(struct keystore *keystore, const char *name,
const char *description, const char *volumes,
- const char *apqns, long int sector_size,
- const char *volume_type);
+ const char *apqns, bool noapqncheck,
+ long int sector_size, const char *volume_type);
int keystore_rename_key(struct keystore *keystore, const char *name,
const char *newname);
int keystore_validate_key(struct keystore *keystore, const char *name_filter,
- const char *apqn_filter, int pkey_fd);
+ const char *apqn_filter, bool noapqncheck,
+ int pkey_fd);
int keystore_reencipher_key(struct keystore *keystore, const char *name_filter,
const char *apqn_filter,
@@ -68,10 +70,14 @@ int keystore_list_keys(struct keystore *keystore, const char *name_filter,
const char *volume_type);
int keystore_cryptsetup(struct keystore *keystore, const char *volume_filter,
- bool execute, const char *volume_type);
+ bool execute, const char *volume_type,
+ const char *keyfile, size_t keyfile_offset,
+ size_t keyfile_size, size_t tries, bool batch_mode,
+ bool open, bool format);
int keystore_crypttab(struct keystore *keystore, const char *volume_filter,
- const char *volume_type);
+ const char *volume_type, const char *keyfile,
+ size_t keyfile_offset, size_t keyfile_size, size_t tries);
void keystore_free(struct keystore *keystore);
diff --git a/zkey/pkey.c b/zkey/pkey.c
index 15e606a..a88c4e9 100644
--- a/zkey/pkey.c
+++ b/zkey/pkey.c
@@ -163,8 +163,8 @@ u8 *read_secure_key(const char *keyfile, size_t *secure_key_size,
buf = util_malloc(size);
count = fread(buf, 1, size, fp);
- if (count <= 0) {
- msg = feof(fp) ? "File is too small" : strerror(errno);
+ if (count != size) {
+ msg = ferror(fp) ? strerror(errno) : "File is too small";
warnx("File '%s': %s", keyfile, msg);
free(buf);
buf = NULL;
@@ -211,7 +211,7 @@ int write_secure_key(const char *keyfile, const u8 *secure_key,
}
count = fwrite(secure_key, 1, secure_key_size, fp);
- if (count <= 0) {
+ if (count != secure_key_size) {
warnx("File '%s': %s", keyfile, strerror(errno));
fclose(fp);
return -EIO;
@@ -301,8 +301,8 @@ static u8 *read_clear_key(const char *keyfile, size_t keybits, bool xts,
buf = util_malloc(size);
count = fread(buf, 1, size, fp);
- if (count <= 0) {
- msg = feof(fp) ? "File is too small" : strerror(errno);
+ if (count != size) {
+ msg = ferror(fp) ? strerror(errno) : "File is too small";
warnx("File '%s': %s", keyfile, msg);
free(buf);
buf = NULL;
diff --git a/zkey/zkey-cryptsetup.1 b/zkey/zkey-cryptsetup.1
index 988ef76..e93b5f0 100644
--- a/zkey/zkey-cryptsetup.1
+++ b/zkey/zkey-cryptsetup.1
@@ -102,6 +102,7 @@ behave in the same way as with \fBcryptsetup\fP.
.IR bytes ]
.RB [ \-\-tries | \-T
.IR number ]
+.RB [ \-\-batch\-mode | \-q ]
.RB [ \-\-verbose | \-V ]
.RB [ \-\-debug | \-D ]
.PP
@@ -180,6 +181,20 @@ and
to control which part of the key file is used as passphrase. These options
behave in the same way as with \fBcryptsetup\fP.
.PP
+The
+.B reencipher
+command creates a new key slot with the re-enciphered secure AES volume key.
+The new key slot uses
+.B PBKDF2
+as password based key derivation function. LUKS2 volumes typically default to
+.B Argon2i
+as password based key derivation function,
+but this might cause out-of-memory errors when multiple encrypted volumes are
+unlocked automatically at boot through /etc/crypttab. Because PAES
+uses secure AES keys as volume keys, the security of the key derivation
+function used to encrypt the volume key in the LUKS key slots is of less
+relevance.
+.PP
.B Note:
The \fBreencipher\fP command requires the CCA host library (libcsulcca.so)
to be installed. For the supported environments and downloads, see:
@@ -247,6 +262,7 @@ behave in the same way as with \fBcryptsetup\fP.
.IR bytes ]
.RB [ \-\-tries | \-T
.IR number ]
+.RB [ \-\-batch\-mode | \-q ]
.RB [ \-\-verbose | \-V ]
.RB [ \-\-debug | \-D ]
.PP
@@ -288,6 +304,20 @@ and
.B \-\-keyfile\-size
to control which part of the key file is used as passphrase. These options
behave in the same way the same as with \fBcryptsetup\fP.
+.PP
+The
+.B setkey
+command creates a new key slot with the re-enciphered secure AES volume key.
+The new key slot uses
+.B PBKDF2
+as password based key derivation function. LUKS2 volumes typically default to
+.B Argon2i
+as password based key derivation function,
+but this might cause out-of-memory errors when multiple encrypted volumes are
+unlocked automatically at boot through /etc/crypttab. Because PAES
+uses secure AES keys as volume keys, the security of the key derivation
+function used to encrypt the volume key in the LUKS key slots is of less
+relevance.
.
.
.
@@ -319,6 +349,9 @@ has been set (made active). When completing the staged re-enciphering, the
(unbound) key slot containing the re-enciphered secure volume key becomes
the active key slot and, optionally, all key slots containing the old secure
volume key are removed.
+.TP
+.BR \-q ", " \-\-batch\-mode
+Suppresses all confirmation questions. Use with care!
.
.
.
@@ -327,6 +360,9 @@ volume key are removed.
.BR \-m ", " \-\-master\-key\-file\~\fIfile\-name\fP
Specifies the name of a file containing the secure AES key that is set as the
new volume key.
+.TP
+.BR \-q ", " \-\-batch\-mode
+Suppresses all confirmation questions. Use with care!
.
.
.
diff --git a/zkey/zkey-cryptsetup.c b/zkey/zkey-cryptsetup.c
index f7098f8..8180b4a 100644
--- a/zkey/zkey-cryptsetup.c
+++ b/zkey/zkey-cryptsetup.c
@@ -35,6 +35,11 @@
#include "misc.h"
#include "pkey.h"
+/* Detect if cryptsetup 2.1 or later is available */
+#ifdef CRYPT_LOG_DEBUG_JSON
+#define HAVE_CRYPT_KEYSLOT_GET_PBKDF
+#endif
+
#define MAX_KEY_SIZE (8 * 1024 * 1024)
#define MAX_PASSWORD_SIZE 512
#define KEYFILE_BUFLEN 4096
@@ -93,6 +98,7 @@ static struct zkey_cryptsetup_globals {
bool inplace;
bool staged;
char *master_key_file;
+ bool batch_mode;
bool debug;
bool verbose;
void *lib_csulcca;
@@ -176,6 +182,11 @@ static struct util_opt opt_vec[] = {
.command = COMMAND_REENCIPHER,
},
OPT_PASSPHRASE_ENTRY(COMMAND_REENCIPHER),
+ {
+ .option = {"batch-mode", 0, NULL, 'q'},
+ .desc = "Suppresses all confirmation questions. Use with care!",
+ .command = COMMAND_REENCIPHER,
+ },
/***********************************************************/
{
.flags = UTIL_OPT_FLAG_SECTION,
@@ -204,6 +215,11 @@ static struct util_opt opt_vec[] = {
.command = COMMAND_SETKEY,
},
OPT_PASSPHRASE_ENTRY(COMMAND_SETKEY),
+ {
+ .option = {"batch-mode", 0, NULL, 'q'},
+ .desc = "Suppresses all confirmation questions. Use with care!",
+ .command = COMMAND_SETKEY,
+ },
/***********************************************************/
{
.flags = UTIL_OPT_FLAG_SECTION,
@@ -414,8 +430,13 @@ static void cryptsetup_log(int level, const char *msg,
fprintf(stderr, "%s: %s", program_invocation_short_name, msg);
break;
case CRYPT_LOG_DEBUG:
- fprintf(stderr, "%s: # %s", program_invocation_short_name, msg);
+ fprintf(stderr, "%s: # %s\n", program_invocation_short_name, msg);
+ break;
+#ifdef CRYPT_DEBUG_JSON
+ case CRYPT_DEBUG_JSON:
+ fprintf(stderr, "%s\n", msg);
break;
+#endif
default:
warnx("Internal error on logging class for msg: %s", msg);
break;
@@ -1096,7 +1117,11 @@ static int put_vp_token(struct crypt_device *cd, int token,
*/
static int open_device(const char *device, struct crypt_device **cd)
{
- const struct crypt_pbkdf_type *pbkdf;
+ const struct crypt_pbkdf_type pbkdf2 = {
+ .type = CRYPT_KDF_PBKDF2,
+ .hash = "sha256",
+ .time_ms = 2000,
+ };
struct crypt_device *cdev = NULL;
int rc;
@@ -1128,10 +1153,14 @@ static int open_device(const char *device, struct crypt_device **cd)
goto out;
}
- pbkdf = crypt_get_pbkdf_type(cdev);
- rc = crypt_set_pbkdf_type(cdev, pbkdf);
+ /*
+ * Set PBKDF2 as default key derivation function. LUKS2 uses
+ * Argon2i as default, but this might cause out-of-memory errors when
+ * multiple LUKS2 volumes are opened automatically via /etc/crypttab
+ */
+ rc = crypt_set_pbkdf_type(cdev, &pbkdf2);
if (rc != 0) {
- warnx("Failed to set the PBKDF-type for device '%s': %s",
+ warnx("Failed to set the PBKDF for device '%s': %s",
device, strerror(-rc));
goto out;
}
@@ -1155,6 +1184,12 @@ static bool prompt_for_yes(void)
{
char str[20];
+ if (g.batch_mode) {
+ printf("(yes implied because '--batch-mode' | '-q' option is "
+ "specified)\n");
+ return true;
+ }
+
if (fgets(str, sizeof(str), stdin) == NULL)
return false;
@@ -1311,6 +1346,9 @@ static int open_keyslot(int keyslot, char **key, size_t *keysize,
char **password, size_t *password_len,
const char *prompt)
{
+#ifdef HAVE_CRYPT_KEYSLOT_GET_PBKDF
+ struct crypt_pbkdf_type pbkdf;
+#endif
char *vkey = NULL;
char *pw = NULL;
long long tries;
@@ -1362,6 +1400,30 @@ static int open_keyslot(int keyslot, char **key, size_t *keysize,
keyslot = rc;
pr_verbose("Volume key obtained from key slot %d", keyslot);
+#ifdef HAVE_CRYPT_KEYSLOT_GET_PBKDF
+ /*
+ * Get PBKDF of the key slot that was opened, and use its PBKDF for
+ * new key slots.
+ */
+ memset(&pbkdf, 0, sizeof(pbkdf));
+ rc = crypt_keyslot_get_pbkdf(g.cd, keyslot, &pbkdf);
+ if (rc != 0) {
+ warnx("Failed to get the PBKDF for key slot %d: %s",
+ keyslot, strerror(-rc));
+ goto out;
+ }
+
+ /* Reuse already benchmarked number of iterations */
+ pbkdf.flags |= CRYPT_PBKDF_NO_BENCHMARK;
+
+ rc = crypt_set_pbkdf_type(g.cd, &pbkdf);
+ if (rc != 0) {
+ warnx("Failed to set the PBKDF for new key slots: %s",
+ strerror(-rc));
+ goto out;
+ }
+#endif
+
if (key != NULL)
*key = vkey;
else
@@ -2191,6 +2253,9 @@ int main(int argc, char *argv[])
case 'm':
g.master_key_file = optarg;
break;
+ case 'q':
+ g.batch_mode = true;
+ break;
case 'D':
g.debug = true;
g.verbose = true;
@@ -2238,7 +2303,11 @@ int main(int argc, char *argv[])
crypt_set_log_callback(NULL, cryptsetup_log, NULL);
if (g.debug)
- crypt_set_debug_level(-1);
+#ifdef CRYPT_DEBUG_JSON
+ crypt_set_debug_level(CRYPT_DEBUG_JSON);
+#else
+ crypt_set_debug_level(CRYPT_DEBUG_ALL);
+#endif
if (command->open_device) {
if (g.pos_arg == NULL) {
diff --git a/zkey/zkey.1 b/zkey/zkey.1
index 0837b27..c7cea83 100644
--- a/zkey/zkey.1
+++ b/zkey/zkey.1
@@ -92,6 +92,7 @@ key repository.
.IR volume1:dmname1[,volume2:dmname2[,...]] ]
.RB [ \-\-apqns | \-a
.IR card1.domain1[,card2.domain2[,...]] ]
+.RB [ \-\-no\-apqn\-check ]
.RB [ \-\-sector-size | \-S
.IR bytes ]
.RB [ \-\-volume-type | \-t
@@ -141,6 +142,9 @@ options.
.BR validate | val
.RB [ \-\-name | \-N
.IR key-name ]
+.RB [ \-\-apqns | \-a
+.IR card1.domain1[,card2.domain2[,...]] ]
+.RB [ \-\-no\-apqn\-check ]
.RB [ \-\-verbose | \-V ]
.PP
Use the
@@ -160,8 +164,21 @@ contained in the secure key repository, specify the name of the key
or a pattern containing wildcards using the
.B \-\-name
option. When wildcards are used you must quote the value.
-If neither option \fIsecure\-key\-file\fP nor option
+You can also specify the
+.B \-\-apqns
+option to validate those secure keys which are associated with the specified
+cryptographic adapters (APQNs). You can use wildcards for the APQN
+specification. When wildcards are used you must quote the value.
+If both option
+.B \-\-name
+and option
+.B \-\-apqns
+are specified then all secure keys contained in the key repository that match
+both patterns are validated.
+If neither option \fIsecure\-key\-file\fP nor options
.B \-\-name
+or
+.B \-\-apqns
are specified, then all secure keys contained in the key repository
are validated.
.
@@ -259,7 +276,7 @@ and option
.B \-\-apqns
are specified then all secure keys
contained in the key repository that match both patterns are re-enciphered.
-If all both options are omitted, then all secure keys contained in the key
+If both options are omitted, then all secure keys contained in the key
repository are re-enciphered.
.PP
Re-enciphering a secure key contained in the secure key repository can be
@@ -298,6 +315,7 @@ to be installed. For the supported environments and downloads, see:
.IR volume1:dmname1[,volume2:dmname2[,...]] ]
.RB [ \-\-apqns | \-a
.IR card1.domain1[,card2.domain2[,...]] ]
+.RB [ \-\-no\-apqn\-check ]
.RB [ \-\-sector-size | \-S
.IR bytes ]
.RB [ \-\-volume-type | \-t
@@ -409,6 +427,7 @@ secure key.
.IR [+|-]volume1:dmname1[,volume2:dmname2[,...]] ]
.RB [ \-\-apqns | \-a
.IR [+|-]card1.domain1[,card2.domain2[,...]] ]
+.RB [ \-\-no\-apqn\-check ]
.RB [ \-\-sector-size | \-S
.IR bytes ]
.RB [ \-\-volume-type | \-t
@@ -477,8 +496,8 @@ option. You cannot use wildcards.
.B copy | co
.RB \-\-name | \-N
.IR key-name
-.B \-\-new-key-name | \-w
-.IR new-name
+.B \-\-new\-name | \-w
+.IR new-key-name
.RB [ \-\-volumes | \-l
.IR volume1:dmname1[,volume2:dmname2[,...]] ]
.RB [ \-\-verbose | \-V ]
@@ -509,6 +528,14 @@ volumes afterwards.
.IR volume1[:dmname1][,volume2[:dmname2][,...]] ]
.RB [ \-\-volume-type | \-t
.IR type ]
+.RB [ \-\-key\-file
+.IR file-name ]
+.RB [ \-\-keyfile\-offset
+.IR bytes ]
+.RB [ \-\-keyfile\-size
+.IR bytes ]
+.RB [ \-\-tries
+.IR number ]
.RB [ \-\-verbose | \-V ]
.
.PP
@@ -527,6 +554,23 @@ name are selected.
Specify the
.B \-\-volume-type
option to generate crypttab entries for the specified volume type only.
+.P
+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
+.B \-\-tries
+specifies how often a passphrase can be re-entered. When option
+.B \-\-key\-file
+is specified, the passphrase is read from the specified file. You can specify
+options
+.B \-\-keyfile\-offset
+and
+.B \-\-keyfile\-size
+to control which part of the key file is used as passphrase. These options are
+passed to the generated crypttab entries and are only available if
+.B zkey
+has been compiled with LUKS2 support enabled.
.
.SS "Generate cryptsetup commands for volumes associated with secure AES keys"
.
@@ -537,14 +581,24 @@ option to generate crypttab entries for the specified volume type only.
.RB [ \-\-volume-type | \-t
.IR type ]
.RB [ \-\-run | \-r ]
+.RB [ \-\-open ]
+.RB [ \-\-format ]
+.RB [ \-\-key\-file
+.IR file-name ]
+.RB [ \-\-keyfile\-offset
+.IR bytes ]
+.RB [ \-\-keyfile\-size
+.IR bytes ]
+.RB [ \-\-tries
+.IR number ]
.RB [ \-\-verbose | \-V ]
.
.PP
Use the
.B cryptsetup
-command to generate \fBcryptsetup plainOpen\fP or \fBcryptsetup luksFormat\fP
-commands for volumes that are associated with secure keys contained in the
-secure key repository. Specify the
+command to generate \fBcryptsetup plainOpen\fP, \fBcryptsetup luksOpen\fP, or
+\fBcryptsetup luksFormat\fP commands for volumes that are associated with
+secure keys contained in the secure key repository. Specify the
.B \-\-volumes
option to limit the list
of volumes where cryptsetup commands are generated for. You can use wildcards.
@@ -556,7 +610,44 @@ name are selected. Specify the
option to generate cryptsetup commands for the specified volume type only.
Specify the
.B \-\-run
-option to run the generated cryptsetup commands.
+option to run the generated cryptsetup commands. Specify the
+.B \-\-open
+to generate \fBcryptsetup plainOpen\fP or \fBcryptsetup luksOpen\fP commands.
+For the plain volume type, this is the default. Specify the
+.B \-\-format
+option to generate \fBcryptsetup luksFormat\fP commands. For the LUKS2 volume
+type, this is the default. If specified for the plain volume type, then no
+command is generated.
+.P
+For LUKS2 volumes, the generated \fBcryptsetup luksFormat\fP contains
+option \fB\-\-pbkdf pbkdf2\fP to set \fBPBKDF2\fP as password based key
+derivation function. LUKS2 volumes typically default to \fBArgon2i\fP as
+password based key derivation function, but this might cause out-of-memory
+errors when multiple encrypted volumes are unlocked automatically at boot
+through /etc/crypttab. Because PAES uses secure AES keys as volume keys, the
+security of the key derivation function used to encrypt the volume key in the
+LUKS key slots is of less relevance.
+.P
+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
+.B \-\-tries
+specifies how often a passphrase can be re-entered. When option
+.B \-\-key\-file
+is specified, the passphrase is read from the specified file. You can specify
+options
+.B \-\-keyfile\-offset
+and
+.B \-\-keyfile\-size
+to control which part of the key file is used as passphrase. These options are
+only available if
+.B zkey
+has been compiled with LUKS2 support enabled. To avoid cryptsetup confirmation
+questions, you can specify the
+.B \-\-batch\-mode
+option. These options are passed to the generated command(s) and behave in the
+same way as with \fBcryptsetup\fP.
.
.
.
@@ -603,8 +694,14 @@ Specifies a comma-separated list of cryptographic adapters in CCA
coprocessor mode (APQN) which are associated with the secure AES key in the
repository. Each APQN association specifies a card and domain number separated
by a period (like lszcrypt displays it). When at least one APQN is specified,
-then the first one is used to generate the key. If no APQNs are specified,
-then an APQN is selected automatically. All specified APQNs must be online.
+then the first online APQN is used to generate the key. If no APQNs are
+specified, then an APQN is selected automatically. All specified APQNs must be
+online, unless the \fB\-\-no\-apqn\-check\fP option is specified.
+This option is only used for secure keys contained in the secure key repository.
+.TP
+.BR \-\-no\-apqn\-check
+Do not check if the specified APQNs are available. Use this option to
+associate APQNs with a secure AES key that are currently not available.
This option is only used for secure keys contained in the secure key repository.
.TP
.BR \-S ", " \-\-sector-size\~\fIbytes\fP
@@ -631,6 +728,19 @@ Specifies the name of the secure key in the secure key repository. You can
use wildcards to select multiple secure keys in the secure key repository.
When wildcards are used you must quote the value.
This option is only used for secure keys contained in the secure key repository.
+.TP
+.BR \-a ", " \-\-apqns\~\fIcard1.domain1[,card2.domain2[,...]]\fP
+Specifies a comma-separated list of cryptographic adapters in CCA
+coprocessor mode (APQNs). You can use wildcards in the APQN specification.
+All secure keys contained in the secure key repository
+which are associated with the specified APQNs are validated.
+Each APQN specifies a card and domain number separated by a period (like
+lszcrypt displays it).
+This option is only used for secure keys contained in the secure key repository.
+.TP
+.BR \-\-no\-apqn\-check
+Do not check if the associated APQNs are available.
+This option is only used for secure keys contained in the secure key repository.
.
.
.
@@ -714,7 +824,13 @@ This option is only used for secure keys contained in the secure key repository.
Specifies a comma-separated list of cryptographic adapters in CCA
coprocessor mode (APQN) which are associated with the secure AES key in the
repository. Each APQN association specifies a card and domain number separated
-by a period (like lszcrypt displays it). All specified APQNs must be online.
+by a period (like lszcrypt displays it). All specified APQNs must be online,
+unless option \fB\-\-no\-apqn\-check\fP is specified.
+This option is only used for secure keys contained in the secure key repository.
+.TP
+.BR \-\-no\-apqn\-check
+Do not check if the specified APQNs are available. Use this option to
+associate APQNs with a secure AES key that are currently not available.
This option is only used for secure keys contained in the secure key repository.
.TP
.BR \-S ", " \-\-sector-size\~\fIbytes\fP
@@ -833,7 +949,13 @@ To remove an APQN from the associated APQNs, prefix the APQN with a \fI-\fP.
To set (replace) the APQN association do not specify a prefix.
You cannot mix \fI+\fP and \fI-\fP in one specification. You can either add or
remove (or set) the associations with one command.
-All APQNs being added or set (replaced) must be online.
+All APQNs being added or set (replaced) must be online, unless option
+\fB\-\-no\-apqn\-check\fP is specified.
+This option is only used for secure keys contained in the secure key repository.
+.TP
+.BR \-\-no\-apqn\-check
+Do not check if the specified APQNs are available. Use this option to
+associate APQNs with a secure AES key that are currently not available.
This option is only used for secure keys contained in the secure key repository.
.TP
.BR \-S ", " \-\-sector-size\~\fIbytes\fP
@@ -908,6 +1030,46 @@ This option is only available if
.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 \-\-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.
+This option is passed to the generated crypttab entries for LUKS2 volumes, and
+is only available if
+.B zkey
+has been compiled with LUKS2 support enabled.
+.TP
+.BR \-\-keyfile\-offset\~\fIbytes\fP
+Specifies the number of bytes to skip before starting to read in the file
+specified with option \fB\-\-key\-file\fP. If omitted, the file is read
+from the beginning. When option \fB\-\-key\-file\fP is not specified, this
+option is ignored. This option is passed to the generated crypttab entries
+for LUKS2 volumes, and is only available if
+.B zkey
+has been compiled with LUKS2 support enabled. Not all distributions support the
+.B keyfile-offset
+option in crypttab entries.
+.TP
+.BR \-\-keyfile\-size\~\fIbytes\fP
+Specifies the number of bytes to be read from the beginning of the file
+specified with option \fB\-\-key\-file\fP. If omitted, the file is read
+until the end. When \fB\-\-keyfile\-offset\fP is also specified, reading starts
+at the offset. When option \fB\-\-key\-file\fP is not specified, this option is
+ignored. This option is passed to the generated crypttab entries for LUKS2
+volumes, and is only available if
+.B zkey
+has been compiled with LUKS2 support enabled. Not all distributions support the
+.B keyfile-size
+option in crypttab entries.
+.TP
+.BR \-\-tries\~\fInumber\fP
+Specifies how often the interactive input of the passphrase can be re-entered
+during system startup. The default is 3 times. When option \fB\-\-key\-file\fP
+is specified, this option is ignored, and the passphrase is read only once from
+the file. This option is passed to the generated crypttab entries for LUKS2
+volumes, and is only available if
+.B zkey
+has been compiled with LUKS2 support enabled.
.
.
.
@@ -937,6 +1099,64 @@ This option is only used for secure keys contained in the secure key repository.
Runs the generated cryptsetup commands. When one of the cryptsetup command fail,
no further cryptsetup commands are run, and zkey ends with an error.
This option is only used for secure keys contained in the secure key repository.
+.TP
+.BR \-\-open
+Generates \fBcryptsetup luksOpen\fP or \fBcryptsetup plainOpen\fP commands.
+For a plain volume type, this is the default. This option can not be specified
+together with the
+.BR \-\-format
+option, and is only available if
+.B zkey
+has been compiled with LUKS2 support enabled.
+.TP
+.BR \-\-format
+Generates \fBcryptsetup luksFormat\fP commands. For a LUKS2 volume type, this
+is the default. If specified for a plain volume type, then no command is
+generated. This option can not be specified together with the
+.BR \-\-open
+option, and is only available if
+.B zkey
+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
+.B zkey
+has been compiled with LUKS2 support enabled.
+.TP
+.BR \-\-keyfile\-offset\~\fIbytes\fP
+Specifies the number of bytes to skip before starting to read in the file
+specified with option \fB\-\-key\-file\fP. If omitted, the file is read
+from the beginning. When option \fB\-\-key\-file\fP is not specified, this
+option is ignored. 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
+.BR \-\-keyfile\-size\~\fIbytes\fP
+Specifies the number of bytes to be read from the beginning of the file
+specified with option \fB\-\-key\-file\fP. If omitted, the file is read
+until the end. When \fB\-\-keyfile\-offset\fP is also specified, reading starts
+at the offset. When option \fB\-\-key\-file\fP is not specified, this option is
+ignored. 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
+.BR \-\-tries\~\fInumber\fP
+Specifies how often the interactive input of the passphrase can be re-entered.
+The default is 3 times. When option \fB\-\-key\-file\fP is specified, this
+option is ignored, and the passphrase is read only once from the file.
+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
+.BR \-q ", " \-\-batch\-mode
+Suppress cryptsetup confirmation questions. This option is passed to the generated
+cryptsetup command(s).
.
.
.
diff --git a/zkey/zkey.c b/zkey/zkey.c
index 3a8909a..2ecbb90 100644
--- a/zkey/zkey.c
+++ b/zkey/zkey.c
@@ -67,11 +67,19 @@ static struct zkey_globals {
char *description;
char *volumes;
char *apqns;
+ bool noapqncheck;
long int sector_size;
char *volume_type;
char *newname;
bool run;
+ bool batch_mode;
+ char *keyfile;
+ long long keyfile_offset;
+ long long keyfile_size;
+ long long tries;
bool force;
+ bool open;
+ bool format;
void *lib_csulcca;
t_CSNBKTC dll_CSNBKTC;
int pkey_fd;
@@ -102,6 +110,14 @@ static struct zkey_globals {
#define ENVVAR_ZKEY_REPOSITORY "ZKEY_REPOSITORY"
#define DEFAULT_KEYSTORE "/etc/zkey/repository"
+#define OPT_CRYPTSETUP_KEYFILE 256
+#define OPT_CRYPTSETUP_KEYFILE_OFFSET 257
+#define OPT_CRYPTSETUP_KEYFILE_SIZE 258
+#define OPT_CRYPTSETUP_TRIES 259
+#define OPT_CRYPTSETUP_OPEN 260
+#define OPT_CRYPTSETUP_FORMAT 261
+#define OPT_NO_APQN_CHECK 262
+
/*
* Configuration of command line options
*/
@@ -172,6 +188,14 @@ static struct util_opt opt_vec[] = {
"repository",
.command = COMMAND_GENERATE,
},
+ {
+ .option = {"no-apqn-check", 0, NULL, OPT_NO_APQN_CHECK},
+ .desc = "Do not check if the specified APQN(s) are available. "
+ "Use this option to associate APQN(s) with a secure "
+ "AES key that are currently not available.",
+ .command = COMMAND_GENERATE,
+ .flags = UTIL_OPT_FLAG_NOSHORT,
+ },
{
.option = { "sector-size", required_argument, NULL, 'S'},
.argument = "bytes",
@@ -281,6 +305,12 @@ static struct util_opt opt_vec[] = {
"associated with specific crypto cards",
.command = COMMAND_VALIDATE,
},
+ {
+ .option = {"no-apqn-check", 0, NULL, OPT_NO_APQN_CHECK},
+ .desc = "Do not check if the associated APQN(s) are available",
+ .command = COMMAND_VALIDATE,
+ .flags = UTIL_OPT_FLAG_NOSHORT,
+ },
/***********************************************************/
{
.flags = UTIL_OPT_FLAG_SECTION,
@@ -316,6 +346,14 @@ static struct util_opt opt_vec[] = {
"repository",
.command = COMMAND_IMPORT,
},
+ {
+ .option = {"no-apqn-check", 0, NULL, OPT_NO_APQN_CHECK},
+ .desc = "Do not check if the specified APQN(s) are available. "
+ "Use this option to associate APQN(s) with a secure "
+ "AES key that are currently not available.",
+ .command = COMMAND_IMPORT,
+ .flags = UTIL_OPT_FLAG_NOSHORT,
+ },
{
.option = { "sector-size", required_argument, NULL, 'S'},
.argument = "512|4096",
@@ -453,6 +491,14 @@ static struct util_opt opt_vec[] = {
"specify '-CARD.DOMAIN[,...]'",
.command = COMMAND_CHANGE,
},
+ {
+ .option = {"no-apqn-check", 0, NULL, OPT_NO_APQN_CHECK},
+ .desc = "Do not check if the specified APQN(s) are available. "
+ "Use this option to associate APQN(s) with a secure "
+ "AES key that are currently not available.",
+ .command = COMMAND_CHANGE,
+ .flags = UTIL_OPT_FLAG_NOSHORT,
+ },
{
.option = { "sector-size", required_argument, NULL, 'S'},
.argument = "0|512|4096",
@@ -547,6 +593,53 @@ static struct util_opt opt_vec[] = {
"entry is to be generated",
.command = COMMAND_CRYPTTAB,
},
+ {
+ .option = {"key-file", required_argument, NULL,
+ OPT_CRYPTSETUP_KEYFILE},
+ .argument = "FILE-NAME",
+ .desc = "Read the passphrase from the specified file. "
+ "The specified file is passed to the generated "
+ "crypttab entry for LUKS2 volumes",
+ .command = COMMAND_CRYPTTAB,
+ .flags = UTIL_OPT_FLAG_NOSHORT,
+ },
+ {
+ .option = {"keyfile-offset", required_argument, NULL,
+ OPT_CRYPTSETUP_KEYFILE_OFFSET},
+ .argument = "BYTES",
+ .desc = "Specifies the number of bytes to skip in the file "
+ "specified with option '--key-file'. "
+ "The specified offset is passed to the generated "
+ "crypttab entry for LUKS2 volumes. Not all "
+ "distributions support the 'keyfile-offset' option in "
+ "crypttab entries",
+ .command = COMMAND_CRYPTTAB,
+ .flags = UTIL_OPT_FLAG_NOSHORT,
+ },
+ {
+ .option = {"keyfile-size", required_argument, NULL,
+ OPT_CRYPTSETUP_KEYFILE_SIZE},
+ .argument = "BYTES",
+ .desc = "Specifies the number of bytes to read from the file "
+ "specified with option '--key-file'. "
+ "The specified size is passed to the generated "
+ "crypttab entry for LUKS2 volumes. Not all "
+ "distributions support the 'keyfile-size' option in "
+ "crypttab entries",
+ .command = COMMAND_CRYPTTAB,
+ .flags = UTIL_OPT_FLAG_NOSHORT,
+ },
+ {
+ .option = {"tries", required_argument, NULL,
+ OPT_CRYPTSETUP_TRIES},
+ .argument = "NUMBER",
+ .desc = "Specifies how often the interactive input of the "
+ "passphrase can be retried. "
+ "The specified number is passed to the generated "
+ "crypttab entry for LUKS2 volumes",
+ .command = COMMAND_CRYPTTAB,
+ .flags = UTIL_OPT_FLAG_NOSHORT,
+ },
#endif
/***********************************************************/
{
@@ -582,6 +675,75 @@ static struct util_opt opt_vec[] = {
.desc = "Runs the generated cryptsetup command",
.command = COMMAND_CRYPTSETUP,
},
+#ifdef HAVE_LUKS2_SUPPORT
+ {
+ .option = {"key-file", required_argument, NULL,
+ OPT_CRYPTSETUP_KEYFILE},
+ .argument = "FILE-NAME",
+ .desc = "Read the passphrase from the specified file. "
+ "This option is passed to the generated command(s) for "
+ "LUKS2 volumes",
+ .command = COMMAND_CRYPTSETUP,
+ .flags = UTIL_OPT_FLAG_NOSHORT,
+ },
+ {
+ .option = {"keyfile-offset", required_argument, NULL,
+ OPT_CRYPTSETUP_KEYFILE_OFFSET},
+ .argument = "BYTES",
+ .desc = "Specifies the number of bytes to skip in the file "
+ "specified with option '--key-file'. "
+ "This option is passed to the generated command(s) for "
+ "LUKS2 volumes",
+ .command = COMMAND_CRYPTSETUP,
+ .flags = UTIL_OPT_FLAG_NOSHORT,
+ },
+ {
+ .option = {"keyfile-size", required_argument, NULL,
+ OPT_CRYPTSETUP_KEYFILE_SIZE},
+ .argument = "BYTES",
+ .desc = "Specifies the number of bytes to read from the file "
+ "specified with option '--key-file'. "
+ "This option is passed to the generated command(s) for "
+ "LUKS2 volumes",
+ .command = COMMAND_CRYPTSETUP,
+ .flags = UTIL_OPT_FLAG_NOSHORT,
+ },
+ {
+ .option = {"tries", required_argument, NULL,
+ OPT_CRYPTSETUP_TRIES},
+ .argument = "NUMBER",
+ .desc = "Specifies how often the interactive input of the "
+ "passphrase can be retried. "
+ "This option is passed to the generated command(s) for "
+ "LUKS2 volumes",
+ .command = COMMAND_CRYPTSETUP,
+ .flags = UTIL_OPT_FLAG_NOSHORT,
+ },
+#endif
+ {
+ .option = {"batch-mode", 0, NULL, 'q'},
+ .desc = "Suppresses cryptsetup confirmation questions. "
+ "This option is passed to the generated cryptsetup "
+ "command(s)",
+ .command = COMMAND_CRYPTSETUP,
+ },
+#ifdef HAVE_LUKS2_SUPPORT
+ {
+ .option = {"open", 0, NULL, OPT_CRYPTSETUP_OPEN},
+ .desc = "Generates luksOpen or plainOpen commands. For the "
+ "plain volume type, this is the default",
+ .command = COMMAND_CRYPTSETUP,
+ .flags = UTIL_OPT_FLAG_NOSHORT,
+ },
+ {
+ .option = {"format", 0, NULL, OPT_CRYPTSETUP_FORMAT},
+ .desc = "Generates luksFormat commands. For the LUKS2 volume "
+ "type, this is the default. If specified for the "
+ "plain volume type, then no command is generated",
+ .command = COMMAND_CRYPTSETUP,
+ .flags = UTIL_OPT_FLAG_NOSHORT,
+ },
+#endif
/***********************************************************/
{
.flags = UTIL_OPT_FLAG_SECTION,
@@ -883,8 +1045,9 @@ static int command_generate_repository(void)
g.sector_size = 0;
rc = keystore_generate_key(g.keystore, g.name, g.description, g.volumes,
- g.apqns, g.sector_size, g.keybits, g.xts,
- g.clearkeyfile, g.volume_type, g.pkey_fd);
+ g.apqns, g.noapqncheck, g.sector_size,
+ g.keybits, g.xts, g.clearkeyfile,
+ g.volume_type, g.pkey_fd);
return rc != 0 ? EXIT_FAILURE : EXIT_SUCCESS;
}
@@ -903,6 +1066,12 @@ static int command_generate(void)
util_prg_print_parse_error();
return EXIT_FAILURE;
}
+ if (g.apqns == NULL && g.noapqncheck) {
+ warnx("Option '--noapqncheck' is only valid together with "
+ "the '--apqns|-a' option");
+ util_prg_print_parse_error();
+ return EXIT_FAILURE;
+ }
if (g.name != NULL)
return command_generate_repository();
if (g.pos_arg != NULL) {
@@ -918,6 +1087,12 @@ static int command_generate(void)
util_prg_print_parse_error();
return EXIT_FAILURE;
}
+ if (g.noapqncheck) {
+ warnx("Option '--noapqncheck' is not valid for "
+ "generating a key outside of the repository");
+ util_prg_print_parse_error();
+ return EXIT_FAILURE;
+ }
if (g.description != NULL) {
warnx("Option '--description|-d' is not valid for "
"generating a key outside of the repository");
@@ -1141,6 +1316,12 @@ static int command_validate_file(void)
util_prg_print_parse_error();
return EXIT_FAILURE;
}
+ if (g.noapqncheck) {
+ warnx("Option '--noapqncheck' is not valid for "
+ "validating a key outside of the repository");
+ util_prg_print_parse_error();
+ return EXIT_FAILURE;
+ }
/* Read the secure key to be re-enciphered */
secure_key = read_secure_key(g.pos_arg, &secure_key_size, g.verbose);
@@ -1194,7 +1375,15 @@ static int command_validate_repository(void)
{
int rc;
- rc = keystore_validate_key(g.keystore, g.name, g.apqns, g.pkey_fd);
+ if (g.apqns == NULL && g.noapqncheck) {
+ warnx("Option '--noapqncheck' is only valid together with "
+ "the '--apqns|-a' option");
+ util_prg_print_parse_error();
+ return EXIT_FAILURE;
+ }
+
+ rc = keystore_validate_key(g.keystore, g.name, g.apqns, g.noapqncheck,
+ g.pkey_fd);
return rc != 0 ? EXIT_FAILURE : EXIT_SUCCESS;
}
@@ -1231,9 +1420,16 @@ static int command_import(void)
if (g.sector_size < 0)
g.sector_size = 0;
+ if (g.apqns == NULL && g.noapqncheck) {
+ warnx("Option '--noapqncheck' is only valid together with "
+ "the '--apqns|-a' option");
+ util_prg_print_parse_error();
+ return EXIT_FAILURE;
+ }
+
rc = keystore_import_key(g.keystore, g.name, g.description, g.volumes,
- g.apqns, g.sector_size, g.pos_arg,
- g.volume_type);
+ g.apqns, g.noapqncheck, g.sector_size,
+ g.pos_arg, g.volume_type);
return rc != 0 ? EXIT_FAILURE : EXIT_SUCCESS;
}
@@ -1304,9 +1500,16 @@ static int command_change(void)
misc_print_required_parm("--name/-N");
return EXIT_FAILURE;
}
+ if (g.apqns == NULL && g.noapqncheck) {
+ warnx("Option '--noapqncheck' is only valid together with "
+ "the '--apqns|-a' option");
+ util_prg_print_parse_error();
+ return EXIT_FAILURE;
+ }
rc = keystore_change_key(g.keystore, g.name, g.description, g.volumes,
- g.apqns, g.sector_size, g.volume_type);
+ g.apqns, g.noapqncheck, g.sector_size,
+ g.volume_type);
return rc != 0 ? EXIT_FAILURE : EXIT_SUCCESS;
}
@@ -1366,7 +1569,8 @@ static int command_crypttab(void)
{
int rc;
- rc = keystore_crypttab(g.keystore, g.volumes, g.volume_type);
+ rc = keystore_crypttab(g.keystore, g.volumes, g.volume_type, g.keyfile,
+ g.keyfile_offset, g.keyfile_size, g.tries);
return rc != 0 ? EXIT_FAILURE : EXIT_SUCCESS;
}
@@ -1380,7 +1584,16 @@ static int command_cryptsetup(void)
{
int rc;
- rc = keystore_cryptsetup(g.keystore, g.volumes, g.run, g.volume_type);
+ if (g.open && g.format) {
+ warnx("Either '--open' or '--format' can be specified, but "
+ "not both");
+ util_prg_print_parse_error();
+ return EXIT_FAILURE;
+ }
+
+ rc = keystore_cryptsetup(g.keystore, g.volumes, g.run, g.volume_type,
+ g.keyfile, g.keyfile_offset, g.keyfile_size,
+ g.tries, g.batch_mode, g.open, g.format);
return rc != 0 ? EXIT_FAILURE : EXIT_SUCCESS;
}
@@ -1546,6 +1759,9 @@ int main(int argc, char *argv[])
case 'a':
g.apqns = optarg;
break;
+ case OPT_NO_APQN_CHECK:
+ g.noapqncheck = 1;
+ break;
case 'S':
g.sector_size = strtol(optarg, &endp, 0);
if (*optarg == '\0' || *endp != '\0' ||
@@ -1574,6 +1790,56 @@ int main(int argc, char *argv[])
case 'V':
g.verbose = 1;
break;
+#ifdef HAVE_LUKS2_SUPPORT
+ case OPT_CRYPTSETUP_KEYFILE:
+ g.keyfile = optarg;
+ break;
+ case OPT_CRYPTSETUP_KEYFILE_OFFSET:
+ g.keyfile_offset = strtoll(optarg, &endp, 0);
+ if (*optarg == '\0' || *endp != '\0' ||
+ g.keyfile_offset < 0 ||
+ (g.keyfile_offset == LLONG_MAX &&
+ errno == ERANGE)) {
+ warnx("Invalid value for '--keyfile-offset': "
+ "'%s'", optarg);
+ util_prg_print_parse_error();
+ return EXIT_FAILURE;
+ }
+ break;
+ case OPT_CRYPTSETUP_KEYFILE_SIZE:
+ g.keyfile_size = strtoll(optarg, &endp, 0);
+ if (*optarg == '\0' || *endp != '\0' ||
+ g.keyfile_size <= 0 ||
+ (g.keyfile_size == LLONG_MAX && errno == ERANGE)) {
+ warnx("Invalid value for '--keyfile-size': "
+ "'%s'", optarg);
+ util_prg_print_parse_error();
+ return EXIT_FAILURE;
+ }
+ break;
+ case OPT_CRYPTSETUP_TRIES:
+ g.tries = strtoll(optarg, &endp, 0);
+ if (*optarg == '\0' || *endp != '\0' ||
+ g.tries <= 0 ||
+ (g.tries == LLONG_MAX && errno == ERANGE)) {
+ warnx("Invalid value for '--tries': '%s'",
+ optarg);
+ util_prg_print_parse_error();
+ return EXIT_FAILURE;
+ }
+ break;
+#endif
+ case 'q':
+ g.batch_mode = 1;
+ break;
+#ifdef HAVE_LUKS2_SUPPORT
+ case OPT_CRYPTSETUP_OPEN:
+ g.open = 1;
+ break;
+ case OPT_CRYPTSETUP_FORMAT:
+ g.format = 1;
+ break;
+#endif
case 'h':
print_help(command);
return EXIT_SUCCESS;
--
2.21.3
From 86c3031204f6e52455bba1dfc89d619ce26debe9 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Dan=20Hor=C3=A1k?= <dan@danny.cz>
Date: Thu, 7 Nov 2019 13:16:36 +0100
Subject: [PATCH 34/44] zkey: check master key consistency (#1753153)
Description: Enhances the zkey tool to perform a cross check whether the
APQNs associated with a secure key have the same master key.
Display the master key verification pattern of a secure key
during the zkey validate command. This helps to better identify
which master key is the correct one, in case of master key
inconsistencies.
Select an appropriate APQN when re-enciphering a secure key.
Re-enciphering is done using the CCA host library. Special
handling is required to select an appropriate APQN for use with
the CCA host library.
Upstream-ID: a6507b330d05a3033837768a54ce9959c2a84870
Upstream-ID: 8d1a7fecd04e620138a3828d58900d35a396c33a
Upstream-ID: 7e6d782699adf681eaaf63fd43d65e6c0627ee6e
Upstream-ID: 819513ab385fa37306cff3f9ac32e3be7f225793
Upstream-ID: 5dbe73403f455cb880aa256598b9527a465beb39
Upstream-ID: 99ab2db6ad3f79c4e3a7287f4c724d1c635ebd2f
Upstream-ID: 7d4c8c27813c792237f92db5038b9dc4bea2fa7c
Upstream-ID: 94227b44d5e5538b221b7f33ae6e627b86d07593
Upstream-ID: 139dff3525e7140a1d2e6aefdc1d35930d05c6d2
Upstream-ID: 95c7258ea783c5bd6aa12fc0e3d5fbe65647af03
Upstream-ID: 696e8458f0c117e3a084e1a083de89ec19baaff9
Upstream-ID: a84d1c5d58fa4a0c9e087357eec009803ea06ef2
Upstream-ID: bf8872e94a2dc4810df388d1539560b00b1acf6e
Upstream-ID: 625b81130ab2c9d184aaede2749f1fd776f51062
Upstream-ID: bfc3dd018c4f0cc17f8463d8bd6be16aab8de4a4
Upstream-ID: b32c0314746ddee69e59f892f105acd720d06452
Upstream-ID: ea7cc9ea606dd879e4cdfae06a6f13d8fa3afff4
Upstream-ID: c2244a57950f4eb35e3209151dcf48de66828df1
Upstream-ID: a5b58038a0dbf1c3eb202a6933265f0d2e57e130
Upstream-ID: 7f8e31e8619b32297b432a4882d78af79de37a58
Upstream-ID: d854aed4b8154e7420def8749db2106a049dd80a
Upstream-ID: 0b4cbf00412f27456d28ff7f86ec5335a39e3416
Upstream-ID: 016a0a56fcb3dd0bf8bed693e5d64873f6288995
Upstream-ID: 1091b0bf65328aff94055a2e333aff2c737b6744
Upstream-ID: 552a915465301b768268cddc7ccb65a6d167e432
Upstream-ID: a0ed6709cf3c62b1fc9dfa28358e70215c1da55a
---
zkey/Makefile | 12 +-
zkey/cca.c | 628 +++++++++++++++++++++++++++++++++++
zkey/cca.h | 95 ++++++
zkey/keystore.c | 306 +++++++++--------
zkey/keystore.h | 3 +-
zkey/pkey.c | 165 ++--------
zkey/pkey.h | 21 +-
zkey/properties.c | 28 +-
zkey/properties.h | 3 +
zkey/utils.c | 725 +++++++++++++++++++++++++++++++++++++++++
zkey/utils.h | 54 +++
zkey/zkey-cryptsetup.1 | 49 ++-
zkey/zkey-cryptsetup.c | 198 +++++++++--
zkey/zkey.c | 122 +++++--
14 files changed, 2034 insertions(+), 375 deletions(-)
create mode 100644 zkey/cca.c
create mode 100644 zkey/cca.h
create mode 100644 zkey/utils.c
create mode 100644 zkey/utils.h
diff --git a/zkey/Makefile b/zkey/Makefile
index a44b14b..f92de4f 100644
--- a/zkey/Makefile
+++ b/zkey/Makefile
@@ -64,18 +64,20 @@ zkey-cryptsetup-skip-jsonc:
all: $(BUILD_TARGETS)
-zkey.o: zkey.c pkey.h misc.h
+zkey.o: zkey.c pkey.h cca.h misc.h
pkey.o: pkey.c pkey.h
+cca.o: cca.c cca.h pkey.h utils.h
+utils.o: utils.h
properties.o: check-dep-zkey properties.c properties.h
-keystore.o: keystore.c keystore.h properties.h
-zkey-cryptsetup.o: check-dep-zkey-cryptsetup zkey-cryptsetup.c pkey.h misc.h
+keystore.o: keystore.c keystore.h properties.h pkey.h cca.h utils.h
+zkey-cryptsetup.o: check-dep-zkey-cryptsetup zkey-cryptsetup.c pkey.h cca.h misc.h
zkey: LDLIBS = -ldl -lcrypto
-zkey: zkey.o pkey.o properties.o keystore.o $(libs)
+zkey: zkey.o pkey.o cca.o properties.o keystore.o utils.o $(libs)
$(LINK) $(ALL_LDFLAGS) $^ $(LDLIBS) -o $@
zkey-cryptsetup: LDLIBS = -ldl -lcryptsetup -ljson-c
-zkey-cryptsetup: zkey-cryptsetup.o pkey.o $(libs)
+zkey-cryptsetup: zkey-cryptsetup.o pkey.o cca.o utils.o $(libs)
$(LINK) $(ALL_LDFLAGS) $^ $(LDLIBS) -o $@
install-common:
diff --git a/zkey/cca.c b/zkey/cca.c
new file mode 100644
index 0000000..2669c75
--- /dev/null
+++ b/zkey/cca.c
@@ -0,0 +1,628 @@
+/*
+ * zkey - Generate, re-encipher, and validate secure keys
+ *
+ * Copyright IBM Corp. 2019
+ *
+ * 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 <dlfcn.h>
+#include <err.h>
+#include <errno.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdint.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include "lib/util_base.h"
+#include "lib/util_libc.h"
+#include "lib/util_panic.h"
+
+#include "cca.h"
+#include "pkey.h"
+#include "utils.h"
+
+#define pr_verbose(verbose, fmt...) do { \
+ if (verbose) \
+ warnx(fmt); \
+ } while (0)
+
+/*
+ * Definitions for the CCA library
+ */
+#define CCA_LIBRARY_NAME "libcsulcca.so"
+#define CCA_WEB_PAGE "http://www.ibm.com/security/cryptocards"
+#define CCA_DOMAIN_ENVAR "CSU_DEFAULT_DOMAIN"
+#define CCA_ADAPTER_ENVAR "CSU_DEFAULT_ADAPTER"
+
+/**
+ * Prints CCA return and reason code information for certain known CCA
+ * error situations.
+ *
+ * @param return_code the CCA return code
+ * @param reason_code the CCA reason code
+ */
+static void print_CCA_error(int return_code, int reason_code)
+{
+ switch (return_code) {
+ case 8:
+ switch (reason_code) {
+ case 48:
+ warnx("The secure key has a CCA master key "
+ "verification pattern that is not valid");
+ break;
+ }
+ break;
+ case 12:
+ switch (reason_code) {
+ case 764:
+ warnx("The CCA master key is not loaded and "
+ "therefore a secure key cannot be enciphered");
+ break;
+ }
+ break;
+ }
+}
+
+/**
+ * Returns the version, release and modification number of the used CCA library.
+ *
+ * @param[in] cca the CCA library structure
+ * @param[in] verbose if true, verbose messages are printed
+ *
+ * @returns 0 on success, a negative errno in case of an error
+ */
+static int get_cca_version(struct cca_lib *cca, bool verbose)
+{
+ unsigned char exit_data[4] = { 0, };
+ unsigned char version_data[20];
+ long return_code, reason_code;
+ long version_data_length;
+ long exit_data_len = 0;
+ char date[20];
+
+ util_assert(cca != NULL, "Internal error: cca is NULL");
+
+ memset(version_data, 0, sizeof(version_data));
+ version_data_length = sizeof(version_data);
+ cca->dll_CSUACFV(&return_code, &reason_code,
+ &exit_data_len, exit_data,
+ &version_data_length, version_data);
+ pr_verbose(verbose, "CSUACFV (Cryptographic Facility Version) "
+ "returned: return_code: %ld, reason_code: %ld", return_code,
+ reason_code);
+ if (return_code != 0) {
+ print_CCA_error(return_code, reason_code);
+ return -EIO;
+ }
+
+ version_data[sizeof(version_data) - 1] = '\0';
+ pr_verbose(verbose, "CCA Version string: %s", version_data);
+
+ if (sscanf((char *)version_data, "%u.%u.%uz%s", &cca->version.ver,
+ &cca->version.rel, &cca->version.mod, date) != 4) {
+ warnx("CCA library version is invalid: %s", version_data);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+/**
+ * Loads the CCA library and provides the entry point of the CSNBKTC function.
+ *
+ * @param[out] cca on return this contains the address of the CCA
+ * library and certain CCA symbols. dlclose() should
+ * be used to free the library when no longer needed.
+ * @param verbose if true, verbose messages are printed
+ *
+ * @returns 0 on success, -ELIBACC in case of library load errors
+ */
+int load_cca_library(struct cca_lib *cca, bool verbose)
+{
+ util_assert(cca != NULL, "Internal error: caa is NULL");
+
+ /* Load the CCA library */
+ cca->lib_csulcca = dlopen(CCA_LIBRARY_NAME, RTLD_GLOBAL | RTLD_NOW);
+ if (cca->lib_csulcca == NULL) {
+ pr_verbose(verbose, "%s", dlerror());
+ warnx("The command requires the IBM CCA Host Libraries and "
+ "Tools.\nFor the supported environments and downloads, "
+ "see:\n%s", CCA_WEB_PAGE);
+ return -ELIBACC;
+ }
+
+ /* Get the Cryptographic Facility Version function */
+ cca->dll_CSUACFV = (t_CSUACFV)dlsym(cca->lib_csulcca, "CSUACFV");
+
+ /* Get the Key Token Change function */
+ cca->dll_CSNBKTC = (t_CSNBKTC)dlsym(cca->lib_csulcca, "CSNBKTC");
+
+ /* Get the Cryptographic Facility Query function */
+ cca->dll_CSUACFQ = (t_CSUACFQ)dlsym(cca->lib_csulcca, "CSUACFQ");
+
+ /* Get the Cryptographic Resource Allocate function */
+ cca->dll_CSUACRA = (t_CSUACRA)dlsym(cca->lib_csulcca, "CSUACRA");
+
+ /* Cryptographic Resource Deallocate function */
+ cca->dll_CSUACRD = (t_CSUACRD)dlsym(cca->lib_csulcca, "CSUACRD");
+
+ if (cca->dll_CSUACFV == NULL ||
+ cca->dll_CSNBKTC == NULL ||
+ cca->dll_CSUACFQ == NULL ||
+ cca->dll_CSUACRA == NULL ||
+ cca->dll_CSUACRD == NULL) {
+ pr_verbose(verbose, "%s", dlerror());
+ warnx("The command requires the IBM CCA Host Libraries and "
+ "Tools.\nFor the supported environments and downloads, "
+ "see:\n%s", CCA_WEB_PAGE);
+ dlclose(cca->lib_csulcca);
+ cca->lib_csulcca = NULL;
+ return -ELIBACC;
+ }
+
+ pr_verbose(verbose, "CCA library '%s' has been loaded successfully",
+ CCA_LIBRARY_NAME);
+
+ return get_cca_version(cca, verbose);
+}
+
+/**
+ * Re-enciphers a secure key.
+ *
+ * @param[in] cca the CCA libraray structure
+ * @param[in] secure_key a buffer containing the secure key
+ * @param[in] secure_key_size the size of the secure key
+ * @param[in] method the re-enciphering method. METHOD_OLD_TO_CURRENT
+ * or METHOD_CURRENT_TO_NEW.
+ * @param[in] verbose if true, verbose messages are printed
+ *
+ * @returns 0 on success, -EIO in case of an error
+ */
+int key_token_change(struct cca_lib *cca,
+ u8 *secure_key, unsigned int secure_key_size,
+ char *method, bool verbose)
+{
+ long exit_data_len = 0, rule_array_count;
+ unsigned char rule_array[2 * 8] = { 0, };
+ unsigned char exit_data[4] = { 0, };
+ long return_code, reason_code;
+
+ util_assert(cca != NULL, "Internal error: cca is NULL");
+ util_assert(secure_key != NULL, "Internal error: secure_key is NULL");
+ util_assert(secure_key_size > 0,
+ "Internal error: secure_key_size is 0");
+ util_assert(method != NULL, "Internal error: method is NULL");
+
+ memcpy(rule_array, method, 8);
+ memcpy(rule_array + 8, "AES ", 8);
+ rule_array_count = 2;
+
+ cca->dll_CSNBKTC(&return_code, &reason_code,
+ &exit_data_len, exit_data,
+ &rule_array_count, rule_array,
+ secure_key);
+
+ pr_verbose(verbose, "CSNBKTC (Key Token Change) with '%s' returned: "
+ "return_code: %ld, reason_code: %ld", method, return_code,
+ reason_code);
+ if (return_code != 0) {
+ print_CCA_error(return_code, reason_code);
+ return -EIO;
+ }
+
+ if (secure_key_size == 2 * SECURE_KEY_SIZE) {
+ cca->dll_CSNBKTC(&return_code, &reason_code,
+ &exit_data_len, exit_data,
+ &rule_array_count, rule_array,
+ secure_key + SECURE_KEY_SIZE);
+
+ pr_verbose(verbose, "CSNBKTC (Key Token Change) with '%s' "
+ "returned: return_code: %ld, reason_code: %ld",
+ method, return_code, reason_code);
+ if (return_code != 0) {
+ print_CCA_error(return_code, reason_code);
+ return -EIO;
+ }
+ }
+ return 0;
+}
+
+/**
+ * Queries the number of adapters known by the CCA host library
+ *
+ * @param[in] cca the CCA library structure
+ * @param[out] adapters the number of adapters
+ * @param[in] verbose if true, verbose messages are printed
+ *
+ * @returns 0 on success, a negative errno in case of an error.
+ */
+static int get_number_of_cca_adapters(struct cca_lib *cca,
+ unsigned int *adapters, bool verbose)
+{
+ long exit_data_len = 0, rule_array_count, verb_data_length = 0;
+ unsigned char rule_array[16 * 8] = { 0, };
+ unsigned char exit_data[4] = { 0, };
+ long return_code, reason_code;
+
+ util_assert(cca != NULL, "Internal error: cca is NULL");
+ util_assert(adapters != NULL, "Internal error: adapters is NULL");
+
+ memset(rule_array, 0, sizeof(rule_array));
+ memcpy(rule_array, "STATCRD2", 8);
+ rule_array_count = 1;
+
+ cca->dll_CSUACFQ(&return_code, &reason_code,
+ &exit_data_len, exit_data,
+ &rule_array_count, rule_array,
+ &verb_data_length, NULL);
+
+ pr_verbose(verbose, "CSUACFQ (Cryptographic Facility Query) returned: "
+ "return_code: %ld, reason_code: %ld", return_code,
+ reason_code);
+ if (return_code != 0) {
+ print_CCA_error(return_code, reason_code);
+ return -EIO;
+ }
+
+ rule_array[8] = '\0';
+ if (sscanf((char *)rule_array, "%u", adapters) != 1) {
+ pr_verbose(verbose, "Unparsable output: %s", rule_array);
+ return -EIO;
+ }
+
+ pr_verbose(verbose, "Number of CCA adapters: %u", *adapters);
+ return 0;
+}
+
+/**
+ * Allocate a specific CCA adapter.
+ *
+ * @param[in] cca the CCA library structure
+ * @param[in] adapter the adapter number, starting at 1. If 0 is
+ * specified, then the AUTOSELECT option is
+ * enabled.
+ * @param[in] verbose if true, verbose messages are printed
+ *
+ * @returns 0 on success, a negative errno in case of an error. -ENODEV is
+ * returned if the adapter is not available.
+ */
+static int allocate_cca_adapter(struct cca_lib *cca, unsigned int adapter,
+ bool verbose)
+{
+ long exit_data_len = 0, rule_array_count;
+ unsigned char rule_array[8] = { 0, };
+ unsigned char exit_data[4] = { 0, };
+ long return_code, reason_code;
+ char res_name[9];
+ long res_name_len;
+
+ util_assert(cca != NULL, "Internal error: cca is NULL");
+
+ if (adapter > 0)
+ memcpy(rule_array, "DEVICE ", 8);
+ else
+ memcpy(rule_array, "DEV-ANY ", 8);
+ rule_array_count = 1;
+
+ sprintf(res_name, "CRP%02d", adapter);
+ res_name_len = strlen(res_name);
+
+ cca->dll_CSUACRA(&return_code, &reason_code,
+ &exit_data_len, exit_data,
+ &rule_array_count, rule_array,
+ &res_name_len, (unsigned char *)res_name);
+
+ pr_verbose(verbose, "CSUACRA (Cryptographic Resource Allocate) "
+ "returned: return_code: %ld, reason_code: %ld", return_code,
+ reason_code);
+ if (return_code != 0) {
+ print_CCA_error(return_code, reason_code);
+ return -ENODEV;
+ }
+
+ pr_verbose(verbose, "Adapter %u (%s) allocated", adapter, res_name);
+ return 0;
+}
+
+/**
+ * Deallocate a specific CCA adapter.
+ *
+ * @param[in] cca the CCA library structure
+ * @param[in] adapter the adapter number, starting at 1. If 0 is
+ * specified, then the AUTOSELECT option is
+ * disabled.
+ * @param[in] verbose if true, verbose messages are printed
+ *
+ * @returns 0 on success, a negative errno in case of an error. -ENODEV is
+ * returned if the adapter is not available.
+ */
+static int deallocate_cca_adapter(struct cca_lib *cca, unsigned int adapter,
+ bool verbose)
+{
+ long exit_data_len = 0, rule_array_count;
+ unsigned char rule_array[8] = { 0, };
+ unsigned char exit_data[4] = { 0, };
+ long return_code, reason_code;
+ char res_name[9];
+ long res_name_len;
+
+ util_assert(cca != NULL, "Internal error: cca is NULL");
+
+ if (adapter > 0)
+ memcpy(rule_array, "DEVICE ", 8);
+ else
+ memcpy(rule_array, "DEV-ANY ", 8);
+ rule_array_count = 1;
+
+ sprintf(res_name, "CRP%02d", adapter);
+ res_name_len = strlen(res_name);
+
+ cca->dll_CSUACRD(&return_code, &reason_code,
+ &exit_data_len, exit_data,
+ &rule_array_count, rule_array,
+ &res_name_len, (unsigned char *)res_name);
+
+ pr_verbose(verbose, "CSUACRD (Cryptographic Resource Deallocate) "
+ "returned: return_code: %ld, reason_code: %ld", return_code,
+ reason_code);
+ if (return_code != 0) {
+ print_CCA_error(return_code, reason_code);
+ return -ENODEV;
+ }
+
+ pr_verbose(verbose, "Adapter %u (%s) deallocated", adapter, res_name);
+ return 0;
+}
+
+/**
+ * Queries the serial number of the current CCA adapter
+ *
+ * @param[in] cca the CCA library structure
+ * @param[out] serialnr the buffer where the serial number is returned
+ * @param[in] verbose if true, verbose messages are printed
+ *
+ * @returns 0 on success, a negative errno in case of an error.
+ */
+static int get_cca_adapter_serialnr(struct cca_lib *cca, char serialnr[9],
+ bool verbose)
+{
+ long exit_data_len = 0, rule_array_count, verb_data_length = 0;
+ unsigned char rule_array[16 * 8] = { 0, };
+ unsigned char exit_data[4] = { 0, };
+ long return_code, reason_code;
+
+ util_assert(cca != NULL, "Internal error: cca is NULL");
+
+ memset(rule_array, 0, sizeof(rule_array));
+ memcpy(rule_array, "STATCRD2", 8);
+ rule_array_count = 1;
+
+ cca->dll_CSUACFQ(&return_code, &reason_code,
+ &exit_data_len, exit_data,
+ &rule_array_count, rule_array,
+ &verb_data_length, NULL);
+
+ pr_verbose(verbose, "CSUACFQ (Cryptographic Facility Query) returned: "
+ "return_code: %ld, reason_code: %ld", return_code,
+ reason_code);
+ if (return_code != 0) {
+ print_CCA_error(return_code, reason_code);
+ return -EIO;
+ }
+
+ memcpy(serialnr, rule_array+14*8, 8);
+ serialnr[8] = '\0';
+
+ pr_verbose(verbose, "Serial number of CCA adapter: %s", serialnr);
+ return 0;
+}
+
+/**
+ * Selects the specified APQN to be used for the CCA host library.
+ *
+ * @param[in] cca the CCA library structure
+ * @param[in] card the card number
+ * @param[in] domain the domain number
+ * @param[in] verbose if true, verbose messages are printed
+ *
+ * @returns 0 on success, a negative errno in case of an error. -ENOTSUP is
+ * returned when the serialnr sysfs attribute is not available,
+ * because the zcrypt kernel module is on an older level. -ENODEV is
+ * returned if the APQN is not available.
+ */
+int select_cca_adapter(struct cca_lib *cca, int card, int domain, bool verbose)
+{
+ unsigned int adapters, adapter;
+ char adapter_serialnr[9];
+ char apqn_serialnr[9];
+ char temp[10];
+ int rc, found = 0;
+
+ util_assert(cca != NULL, "Internal error: cca is NULL");
+
+ pr_verbose(verbose, "Select %02x.%04x for the CCA host library", card,
+ domain);
+
+ rc = sysfs_get_serialnr(card, apqn_serialnr, verbose);
+ if (rc != 0) {
+ pr_verbose(verbose, "Failed to get the serial number: %s",
+ strerror(-rc));
+ return rc;
+ }
+
+ sprintf(temp, "%u", domain);
+ if (setenv(CCA_DOMAIN_ENVAR, temp, 1) != 0) {
+ rc = -errno;
+ pr_verbose(verbose, "Failed to set the %s environment variable:"
+ " %s", CCA_DOMAIN_ENVAR, strerror(-rc));
+ return rc;
+ }
+ unsetenv(CCA_ADAPTER_ENVAR);
+
+ /*
+ * Unload and reload the CCA host library so that it recognizes the
+ * changed CSU_DEFAULT_DOMAIN environment variable value.
+ */
+ if (cca->lib_csulcca != NULL)
+ dlclose(cca->lib_csulcca);
+ memset(cca, 0, sizeof(struct cca_lib));
+
+ rc = load_cca_library(cca, verbose);
+ if (rc != 0)
+ return rc;
+
+ rc = get_number_of_cca_adapters(cca, &adapters, verbose);
+ if (rc != 0)
+ return rc;
+
+ /* Disable the AUTOSELECT option */
+ rc = deallocate_cca_adapter(cca, 0, verbose);
+ if (rc != 0)
+ return rc;
+
+ for (adapter = 1; adapter <= adapters; adapter++) {
+ rc = allocate_cca_adapter(cca, adapter, verbose);
+ if (rc != 0)
+ return rc;
+
+ rc = get_cca_adapter_serialnr(cca, adapter_serialnr, verbose);
+ if (rc == 0) {
+ if (memcmp(apqn_serialnr, adapter_serialnr, 8) == 0) {
+ found = 1;
+ break;
+ }
+ }
+
+ rc = deallocate_cca_adapter(cca, adapter, verbose);
+ if (rc != 0)
+ return rc;
+ }
+
+ if (!found)
+ return -ENODEV;
+
+ pr_verbose(verbose, "Selected adapter %u (CRP%02d)", adapter, adapter);
+ return 0;
+}
+
+struct find_mkvp_info {
+ u64 mkvp;
+ unsigned int flags;
+ bool found;
+ int card;
+ int domain;
+ bool verbose;
+};
+
+static int find_mkvp(int card, int domain, void *handler_data)
+{
+ struct find_mkvp_info *info = (struct find_mkvp_info *)handler_data;
+ struct mk_info mk_info;
+ bool found = false;
+ int rc;
+
+ rc = sysfs_get_mkvps(card, domain, &mk_info, info->verbose);
+ if (rc == -ENODEV)
+ return 0;
+ if (rc != 0)
+ return rc;
+
+ if (info->flags & FLAG_SEL_CCA_MATCH_CUR_MKVP)
+ if (mk_info.cur_mk.mk_state == MK_STATE_VALID &&
+ mk_info.cur_mk.mkvp == info->mkvp)
+ found = true;
+
+ if (info->flags & FLAG_SEL_CCA_MATCH_OLD_MKVP)
+ if (mk_info.old_mk.mk_state == MK_STATE_VALID &&
+ mk_info.old_mk.mkvp == info->mkvp)
+ found = true;
+
+ if (info->flags & FLAG_SEL_CCA_NEW_MUST_BE_SET)
+ if (mk_info.new_mk.mk_state != MK_STATE_FULL)
+ found = false;
+
+
+ if (found) {
+ info->card = card;
+ info->domain = domain;
+ info->found = true;
+
+ pr_verbose(info->verbose, "%02x.%04x has the desired mkvp%s",
+ card, domain,
+ info->flags & FLAG_SEL_CCA_NEW_MUST_BE_SET ?
+ " and NEW MK set" : "");
+
+ return 1;
+ }
+
+ return 0;
+}
+
+/**
+ * Selects an APQN to be used for the CCA host library that has the specified
+ * master key verification pattern
+ *
+ * @param[in] cca the CCA library structure
+ * @param[in] mkvp the master key verification pattern to search for
+ * @param[in] apqns a comma separated list of APQNs. If NULL is specified,
+ * or an empty string, then all online CCA APQNs are
+ * checked.
+ * @param[in] flags Flags that control the MKVM matching and NEW register
+ * checking. Multiple flags can be combined.
+ * @param[in] verbose if true, verbose messages are printed
+ *
+ * @returns 0 on success, a negative errno in case of an error. -ENOTSUP is
+ * returned when the serialnr sysfs attribute is not available,
+ * because the zcrypt kernel module is on an older level. -ENODEV is
+ * returned if no APQN is available with the desired mkvp.
+ */
+int select_cca_adapter_by_mkvp(struct cca_lib *cca, u64 mkvp, const char *apqns,
+ unsigned int flags, bool verbose)
+{
+ struct find_mkvp_info info;
+ int rc;
+
+ util_assert(cca != NULL, "Internal error: cca is NULL");
+
+ pr_verbose(verbose, "Select mkvp %016llx in APQNs %s for the CCA host "
+ "library", mkvp, apqns == 0 ? "ANY" : apqns);
+
+ info.mkvp = mkvp;
+ info.flags = flags;
+ info.found = false;
+ info.card = 0;
+ info.domain = 0;
+ info.verbose = verbose;
+
+ rc = handle_apqns(apqns, find_mkvp, &info, verbose);
+ if (rc < 0)
+ return rc;
+
+ if (!info.found)
+ return -ENODEV;
+
+ rc = select_cca_adapter(cca, info.card, info.domain, verbose);
+ return rc;
+}
+
+void print_msg_for_cca_envvars(const char *key_name)
+{
+ char *msg;
+
+ util_asprintf(&msg, "WARNING: You must set environment variables "
+ "%s and %s to the desired card and domain that is "
+ "set up with the AES master key used by this %s. "
+ "%s specifies the domain as decimal number. %s "
+ "specifies the adapter number as 'CRPnn', where "
+ "'nn' is the adapter number. See the CCA "
+ "documentation for more details.\n",
+ CCA_DOMAIN_ENVAR, CCA_ADAPTER_ENVAR, key_name,
+ CCA_DOMAIN_ENVAR, CCA_ADAPTER_ENVAR);
+ util_print_indented(msg, 0);
+ free(msg);
+}
diff --git a/zkey/cca.h b/zkey/cca.h
new file mode 100644
index 0000000..a79e2ee
--- /dev/null
+++ b/zkey/cca.h
@@ -0,0 +1,95 @@
+/*
+ * zkey - Generate, re-encipher, and validate secure keys
+ *
+ * This header file defines the interface to the CCA host library.
+ *
+ * Copyright IBM Corp. 2019
+ *
+ * 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 CCA_H
+#define CCA_H
+
+#include "lib/zt_common.h"
+
+#define METHOD_OLD_TO_CURRENT "RTCMK "
+#define METHOD_CURRENT_TO_NEW "RTNMK "
+
+typedef void (*t_CSNBKTC)(long *return_code,
+ long *reason_code,
+ long *exit_data_length,
+ unsigned char *exit_data,
+ long *rule_array_count,
+ unsigned char *rule_array,
+ unsigned char *key_identifier);
+
+typedef void (*t_CSUACFV)(long *return_code,
+ long *reason_code,
+ long *exit_data_length,
+ unsigned char *exit_data,
+ long *version_data_length,
+ unsigned char *version_data);
+
+typedef void (*t_CSUACFQ)(long *return_code,
+ long *reason_code,
+ long *exit_data_length,
+ unsigned char *exit_data,
+ long *rule_array_count,
+ unsigned char *rule_array,
+ long *verb_data_length,
+ unsigned char *verb_data);
+
+typedef void (*t_CSUACRA)(long *return_code,
+ long *reason_code,
+ long *exit_data_length,
+ unsigned char *exit_data,
+ long *rule_array_count,
+ unsigned char *rule_array,
+ long *ressource_name_length,
+ unsigned char *ressource_name);
+
+typedef void (*t_CSUACRD)(long *return_code,
+ long *reason_code,
+ long *exit_data_length,
+ unsigned char *exit_data,
+ long *rule_array_count,
+ unsigned char *rule_array,
+ long *ressource_name_length,
+ unsigned char *ressource_name);
+
+struct cca_version {
+ unsigned int ver;
+ unsigned int rel;
+ unsigned int mod;
+};
+
+struct cca_lib {
+ void *lib_csulcca;
+ t_CSNBKTC dll_CSNBKTC;
+ t_CSUACFV dll_CSUACFV;
+ t_CSUACFQ dll_CSUACFQ;
+ t_CSUACRA dll_CSUACRA;
+ t_CSUACRD dll_CSUACRD;
+ struct cca_version version;
+};
+
+int load_cca_library(struct cca_lib *cca, bool verbose);
+
+int key_token_change(struct cca_lib *cca,
+ u8 *secure_key, unsigned int secure_key_size,
+ char *method, bool verbose);
+
+int select_cca_adapter(struct cca_lib *cca, int card, int domain, bool verbose);
+
+#define FLAG_SEL_CCA_MATCH_CUR_MKVP 0x01
+#define FLAG_SEL_CCA_MATCH_OLD_MKVP 0x02
+#define FLAG_SEL_CCA_NEW_MUST_BE_SET 0x80
+
+int select_cca_adapter_by_mkvp(struct cca_lib *cca, u64 mkvp, const char *apqns,
+ unsigned int flags, bool verbose);
+
+void print_msg_for_cca_envvars(const char *key_name);
+
+#endif
diff --git a/zkey/keystore.c b/zkey/keystore.c
index 33c8f93..957ebb0 100644
--- a/zkey/keystore.c
+++ b/zkey/keystore.c
@@ -3,7 +3,7 @@
*
* Keystore handling functions
*
- * Copyright IBM Corp. 2018
+ * Copyright IBM Corp. 2018, 2019
*
* s390-tools is free software; you can redistribute it and/or modify
* it under the terms of the MIT license. See LICENSE for details.
@@ -25,7 +25,6 @@
#include <sys/types.h>
#include "lib/util_base.h"
-#include "lib/util_file.h"
#include "lib/util_libc.h"
#include "lib/util_panic.h"
#include "lib/util_path.h"
@@ -33,7 +32,9 @@
#include "keystore.h"
#include "pkey.h"
+#include "cca.h"
#include "properties.h"
+#include "utils.h"
struct key_filenames {
char *skey_filename;
@@ -133,14 +134,7 @@ static int _keystore_get_key_filenames(struct keystore *keystore,
*/
static int _keystore_reencipher_key_exists(struct key_filenames *file_names)
{
- struct stat sb;
- int rc;
-
- rc = stat(file_names->renc_filename, &sb);
- if (rc == 0 && !S_ISREG(sb.st_mode))
- rc = 1;
-
- return !rc;
+ return util_path_is_reg_file("%s", file_names->renc_filename);
}
/**
@@ -153,20 +147,14 @@ static int _keystore_reencipher_key_exists(struct key_filenames *file_names)
*/
static int _keystore_exists_keyfiles(struct key_filenames *file_names)
{
- struct stat sb_skey, sb_info;
- int rc_skey, rc_info;
-
- rc_skey = stat(file_names->skey_filename, &sb_skey);
- if (rc_skey == 0 && !S_ISREG(sb_skey.st_mode))
- rc_skey = 1;
+ bool rc_skey, rc_info;
- rc_info = stat(file_names->info_filename, &sb_info);
- if (rc_info == 0 && !S_ISREG(sb_info.st_mode))
- rc_info = 1;
+ rc_skey = util_path_is_reg_file("%s", file_names->skey_filename);
+ rc_info = util_path_is_reg_file("%s", file_names->info_filename);
- if (rc_skey == 0 && rc_info == 0)
+ if (rc_skey && rc_info)
return 1;
- if (rc_skey != 0 && rc_info != 0 &&
+ if (!rc_skey && !rc_info &&
_keystore_reencipher_key_exists(file_names) == 0)
return 0;
return -1;
@@ -1022,69 +1010,6 @@ free:
return rc;
}
-/**
- * Checks if the specified APQN is of type CCA and is online
- *
- * @param[in] card card number
- * @param[in] domain the domain
- *
- * @returns 1 if its a CCA card and is online, 0 if offline and -1 if its
- * not a CCA card.
- */
-static int _keystore_is_apqn_online(int card, int domain)
-{
- long int online;
- char *dev_path;
- char type[20];
- int rc = 1;
-
- dev_path = util_path_sysfs("bus/ap/devices/card%02x", card);
- if (!util_path_is_dir(dev_path)) {
- rc = 0;
- goto out;
- }
- if (util_file_read_l(&online, 10, "%s/online", dev_path) != 0) {
- rc = 0;
- goto out;
- }
- if (online == 0) {
- rc = 0;
- goto out;
- }
- if (util_file_read_line(type, sizeof(type), "%s/type", dev_path) != 0) {
- rc = 0;
- goto out;
- }
- if (strncmp(type, "CEX", 3) != 0 || strlen(type) < 5) {
- rc = 0;
- goto out;
- }
- if (type[4] != 'C') {
- rc = -1;
- goto out;
- }
- free(dev_path);
-
- dev_path = util_path_sysfs("bus/ap/devices/card%02x/%02x.%04x", card,
- card, domain);
- if (!util_path_is_dir(dev_path)) {
- rc = 0;
- goto out;
- }
- if (util_file_read_l(&online, 10, "%s/online", dev_path) != 0) {
- rc = 0;
- goto out;
- }
- if (online == 0) {
- rc = 0;
- goto out;
- }
-
-out:
- free(dev_path);
- return rc;
-}
-
struct apqn_check {
bool noonlinecheck;
bool nomsg;
@@ -1136,7 +1061,7 @@ static int _keystore_apqn_check(const char *apqn, bool remove, bool UNUSED(set),
goto out;
}
- rc = _keystore_is_apqn_online(card, domain);
+ rc = sysfs_is_apqn_online(card, domain);
if (rc != 1) {
if (info->nomsg == 0)
warnx("The APQN %02x.%04x is %s", card, domain,
@@ -1378,7 +1303,7 @@ struct keystore *keystore_new(const char *directory, bool verbose)
warnx("Can not access '%s': %s", directory, strerror(errno));
return NULL;
}
- if (!(sb.st_mode & S_IFDIR)) {
+ if (!S_ISDIR(sb.st_mode)) {
warnx("'%s' is not a directory", directory);
return NULL;
}
@@ -1630,7 +1555,8 @@ static int _keystore_create_info_file(struct keystore *keystore,
rc = -EINVAL;
goto out;
}
- rc = properties_set(key_props, PROP_NAME_VOLUME_TYPE, volume_type);
+ rc = properties_set2(key_props, PROP_NAME_VOLUME_TYPE, volume_type,
+ true);
if (rc != 0) {
warnx("Invalid characters in volume-type");
goto out;
@@ -1759,6 +1685,14 @@ int keystore_generate_key(struct keystore *keystore, const char *name,
if (rc != 0)
goto out_free_key_filenames;
+ rc = cross_check_apqns(apqns, 0, true, keystore->verbose);
+ if (rc == -EINVAL)
+ goto out_free_key_filenames;
+ if (rc != 0 && rc != -ENOTSUP && noapqncheck == 0) {
+ warnx("Your master key setup is improper");
+ goto out_free_key_filenames;
+ }
+
rc = _keystore_get_card_domain(apqns, &card, &domain);
if (rc != 0)
goto out_free_key_filenames;
@@ -1836,6 +1770,7 @@ int keystore_import_key(struct keystore *keystore, const char *name,
struct properties *key_props = NULL;
size_t secure_key_size;
u8 *secure_key;
+ u64 mkvp;
int rc;
util_assert(keystore != NULL, "Internal error: keystore is NULL");
@@ -1857,9 +1792,26 @@ int keystore_import_key(struct keystore *keystore, const char *name,
goto out_free_key_filenames;
}
+ rc = get_master_key_verification_pattern(secure_key, secure_key_size,
+ &mkvp, keystore->verbose);
+ if (rc != 0) {
+ warnx("Failed to get the master key verification pattern: %s",
+ strerror(-rc));
+ goto out_free_key;
+ }
+
+ rc = cross_check_apqns(apqns, mkvp, true, keystore->verbose);
+ if (rc == -EINVAL)
+ goto out_free_key;
+ if (rc != 0 && rc != -ENOTSUP && noapqncheck == 0) {
+ warnx("Your master key setup is improper");
+ goto out_free_key;
+ }
+
rc = write_secure_key(file_names.skey_filename, secure_key,
secure_key_size, keystore->verbose);
free(secure_key);
+ secure_key = NULL;
if (rc != 0)
goto out_free_props;
@@ -1877,6 +1829,9 @@ int keystore_import_key(struct keystore *keystore, const char *name,
"Successfully imported a secure key in '%s' and key info in '%s'",
file_names.skey_filename, file_names.info_filename);
+out_free_key:
+ if (secure_key != NULL)
+ free(secure_key);
out_free_props:
if (key_props != NULL)
properties_free(key_props);
@@ -1931,7 +1886,11 @@ int keystore_change_key(struct keystore *keystore, const char *name,
.nomsg = 0 };
struct key_filenames file_names = { NULL, NULL, NULL };
struct properties *key_props = NULL;
+ size_t secure_key_size;
+ char *apqns_prop;
+ u8 *secure_key;
char temp[30];
+ u64 mkvp;
int rc;
util_assert(keystore != NULL, "Internal error: keystore is NULL");
@@ -1977,6 +1936,33 @@ int keystore_change_key(struct keystore *keystore, const char *name,
&apqn_check);
if (rc != 0)
goto out;
+
+ secure_key = read_secure_key(file_names.skey_filename,
+ &secure_key_size,
+ keystore->verbose);
+ if (secure_key == NULL) {
+ rc = -ENOENT;
+ goto out;
+ }
+
+ rc = get_master_key_verification_pattern(secure_key,
+ secure_key_size,
+ &mkvp,
+ keystore->verbose);
+ free(secure_key);
+ if (rc)
+ goto out;
+
+ apqns_prop = properties_get(key_props, PROP_NAME_APQNS);
+ rc = cross_check_apqns(apqns_prop, mkvp, true,
+ keystore->verbose);
+ free(apqns_prop);
+ if (rc == -ENOTSUP)
+ rc = 0;
+ if (rc != 0 && noapqncheck == 0) {
+ warnx("Your master key setup is improper");
+ goto out;
+ }
}
if (sector_size >= 0) {
@@ -2002,8 +1988,8 @@ int keystore_change_key(struct keystore *keystore, const char *name,
goto out;
}
- rc = properties_set(key_props, PROP_NAME_VOLUME_TYPE,
- volume_type);
+ rc = properties_set2(key_props, PROP_NAME_VOLUME_TYPE,
+ volume_type, true);
if (rc != 0) {
warnx("Invalid characters in volume-type");
goto out;
@@ -2181,7 +2167,7 @@ static void _keystore_print_record(struct util_rec *rec,
bool validation, const char *skey_filename,
size_t secure_key_size,
size_t clear_key_bitsize, bool valid,
- bool is_old_mk, bool reenc_pending)
+ bool is_old_mk, bool reenc_pending, u64 mkvp)
{
char temp_vp[VERIFICATION_PATTERN_LEN + 2];
char *volumes_argz = NULL;
@@ -2243,10 +2229,11 @@ static void _keystore_print_record(struct util_rec *rec,
if (validation) {
if (valid)
util_rec_set(rec, REC_MASTERKEY,
- is_old_mk ? "OLD CCA master key" :
- "CURRENT CCA master key");
+ "%s CCA master key (MKVP: %016llx)",
+ is_old_mk ? "OLD" : "CURRENT", mkvp);
else
- util_rec_set(rec, REC_MASTERKEY, "(unknown)");
+ util_rec_set(rec, REC_MASTERKEY,
+ "(unknown, MKVP: %016llx)", mkvp);
}
if (volumes_argz != NULL)
util_rec_set_argz(rec, REC_VOLUMES, volumes_argz,
@@ -2317,43 +2304,32 @@ struct validate_info {
/**
* Displays the status of the associated APQNs.
*
+ * @param[in] keystore the key store
* @param[in] properties the properties of the key
- * @param[in] name the name of the key
+ * @param[in] mkvp the master key verification pattern of the key
*
* @returns 0 in case of success, 1 if at least one of the APQNs is not
- * available
+ * available or has a master key mismatch
*/
-static int _keystore_display_apqn_status(struct properties *properties,
- const char *name)
+static int _keystore_display_apqn_status(struct keystore *keystore,
+ struct properties *properties,
+ u64 mkvp)
{
- int i, rc, card, domain, warning = 0;
- char **apqn_list;
+ int rc, warning = 0;
char *apqns;
apqns = properties_get(properties, PROP_NAME_APQNS);
if (apqns == NULL)
return 0;
- apqn_list = str_list_split(apqns);
- for (i = 0; apqn_list[i] != NULL; i++) {
-
- if (sscanf(apqn_list[i], "%x.%x", &card, &domain) != 2)
- continue;
-
- rc = _keystore_is_apqn_online(card, domain);
- if (rc != 1) {
- printf("WARNING: The APQN %02x.%04x associated with "
- "key '%s' is %s\n", card, domain, name,
- rc == -1 ? "not a CCA card" : "not online");
- warning = 1;
- }
- }
+ rc = cross_check_apqns(apqns, mkvp, true, keystore->verbose);
+ if (rc != 0 && rc != -ENOTSUP)
+ warning = 1;
if (warning)
printf("\n");
free(apqns);
- str_list_free_string_array(apqn_list);
return warning;
}
/**
@@ -2424,6 +2400,7 @@ static int _keystore_process_validate(struct keystore *keystore,
u8 *secure_key;
int is_old_mk;
int rc, valid;
+ u64 mkvp;
rc = _keystore_ensure_keyfiles_exist(file_names, name);
if (rc != 0)
@@ -2447,12 +2424,18 @@ static int _keystore_process_validate(struct keystore *keystore,
info->num_valid++;
valid = 1;
}
+
+ rc = get_master_key_verification_pattern(secure_key, secure_key_size,
+ &mkvp, keystore->verbose);
free(secure_key);
+ if (rc)
+ goto out;
_keystore_print_record(info->rec, name, properties, 1,
file_names->skey_filename, secure_key_size,
clear_key_bitsize, valid, is_old_mk,
- _keystore_reencipher_key_exists(file_names));
+ _keystore_reencipher_key_exists(file_names),
+ mkvp);
if (valid && is_old_mk) {
util_print_indented("WARNING: The secure key is currently "
@@ -2463,7 +2446,8 @@ static int _keystore_process_validate(struct keystore *keystore,
info->num_warnings++;
}
if (info->noapqncheck == 0)
- if (_keystore_display_apqn_status(properties, name) != 0)
+ if (_keystore_display_apqn_status(keystore, properties,
+ mkvp) != 0)
info->num_warnings++;
if (_keystore_display_volume_status(properties, name) != 0)
info->num_warnings++;
@@ -2534,7 +2518,7 @@ struct reencipher_params {
struct reencipher_info {
struct reencipher_params params;
int pkey_fd;
- t_CSNBKTC dll_CSNBKTC;
+ struct cca_lib *cca;
unsigned long num_reenciphered;
unsigned long num_failed;
unsigned long num_skipped;
@@ -2545,23 +2529,33 @@ struct reencipher_info {
*
* @param[in] keystore the keystore
* @param[in] name the name of the key
- * @param[in] dll_CSNBKTC the CCA key token change function
+ * @param[in] cca the CCA library struct
* @param[in] params reenciphering parameters
* @param[in] secure_key a buffer containing the secure key
* @param[in] secure_key_size the size of the secure key
* @param[in] is_old_mk if true the key is currently re-enciphered with the
* OLD master key
+ * @param[in] apqns the associated APQNs (or NULL if none)
* @returns 0 if the re-enciphering is successful, a negative errno value
* otherwise, 1 if it was skipped
*/
static int _keystore_perform_reencipher(struct keystore *keystore,
const char *name,
- t_CSNBKTC dll_CSNBKTC,
+ struct cca_lib *cca,
struct reencipher_params *params,
u8 *secure_key, size_t secure_key_size,
- bool is_old_mk)
+ bool is_old_mk, const char *apqns)
{
- int rc;
+ int rc, selected = 1;
+ u64 mkvp;
+
+ rc = get_master_key_verification_pattern(secure_key, secure_key_size,
+ &mkvp, keystore->verbose);
+ if (rc != 0) {
+ warnx("Failed to get the master key verification pattern: %s",
+ strerror(-rc));
+ return rc;
+ }
if (!params->from_old && !params->to_new) {
/* Autodetect reencipher mode */
@@ -2583,12 +2577,6 @@ static int _keystore_perform_reencipher(struct keystore *keystore,
}
if (params->from_old) {
- if (!is_old_mk) {
- printf("The secure key '%s' is already enciphered "
- "with the CURRENT CCA master key\n", name);
- return 1;
- }
-
if (params->inplace == -1)
params->inplace = 1;
@@ -2596,13 +2584,27 @@ static int _keystore_perform_reencipher(struct keystore *keystore,
"Secure key '%s' will be re-enciphered from OLD "
"to the CURRENT CCA master key", name);
- rc = key_token_change(dll_CSNBKTC,
- secure_key, secure_key_size,
+ rc = select_cca_adapter_by_mkvp(cca, mkvp, apqns,
+ FLAG_SEL_CCA_MATCH_OLD_MKVP,
+ keystore->verbose);
+ if (rc == -ENOTSUP) {
+ rc = 0;
+ selected = 0;
+ }
+ if (rc != 0) {
+ warnx("No APQN found that is suitable for "
+ "re-enciphering this secure AES key");
+ return rc;
+ }
+
+ rc = key_token_change(cca, secure_key, secure_key_size,
METHOD_OLD_TO_CURRENT,
keystore->verbose);
if (rc != 0) {
warnx("Failed to re-encipher '%s' from OLD to "
"CURRENT CCA master key", name);
+ if (!selected)
+ print_msg_for_cca_envvars("secure AES key");
return rc;
}
}
@@ -2614,13 +2616,30 @@ static int _keystore_perform_reencipher(struct keystore *keystore,
if (params->inplace == -1)
params->inplace = 0;
- rc = key_token_change(dll_CSNBKTC,
- secure_key, secure_key_size,
+ rc = select_cca_adapter_by_mkvp(cca, mkvp, apqns,
+ FLAG_SEL_CCA_MATCH_CUR_MKVP |
+ FLAG_SEL_CCA_NEW_MUST_BE_SET,
+ keystore->verbose);
+ if (rc == -ENOTSUP) {
+ rc = 0;
+ selected = 0;
+ }
+ if (rc != 0) {
+ util_print_indented("No APQN found that is suitable "
+ "for re-enciphering this secure "
+ "AES key and has the NEW master "
+ "key loaded", 0);
+ return rc;
+ }
+
+ rc = key_token_change(cca, secure_key, secure_key_size,
METHOD_CURRENT_TO_NEW,
keystore->verbose);
if (rc != 0) {
warnx("Failed to re-encipher '%s' from CURRENT to "
"NEW CCA master key", name);
+ if (!selected)
+ print_msg_for_cca_envvars("secure AES key");
return rc;
}
}
@@ -2708,10 +2727,11 @@ static int _keystore_process_reencipher(struct keystore *keystore,
if (!params.complete) {
printf("Re-enciphering key '%s'\n", name);
- rc = _keystore_perform_reencipher(keystore, name,
- info->dll_CSNBKTC, &params,
- secure_key, secure_key_size,
- is_old_mk);
+ rc = _keystore_perform_reencipher(keystore, name, info->cca,
+ &params, secure_key,
+ secure_key_size, is_old_mk,
+ properties_get(properties,
+ PROP_NAME_APQNS));
if (rc < 0)
goto out;
if (rc > 0) {
@@ -2814,6 +2834,8 @@ out:
* @param[in] inplace if true, the key will be re-enciphere in-place
* @param[in] staged if true, the key will be re-enciphere not in-place
* @param[in] complete if true, a pending re-encipherment is completed
+ * @param[in] pkey_fd the file descriptor of /dev/pkey
+ * @param[in] cca the CCA library struct
* Note: if both from Old and toNew are FALSE, then the reencipherement mode is
* detected automatically. If both are TRUE then the key is reenciphered
* from the OLD to the NEW CCA master key.
@@ -2826,7 +2848,7 @@ int keystore_reencipher_key(struct keystore *keystore, const char *name_filter,
const char *apqn_filter,
bool from_old, bool to_new, bool inplace,
bool staged, bool complete, int pkey_fd,
- t_CSNBKTC dll_CSNBKTC)
+ struct cca_lib *cca)
{
struct reencipher_info info;
int rc;
@@ -2842,7 +2864,7 @@ int keystore_reencipher_key(struct keystore *keystore, const char *name_filter,
info.params.inplace = 0;
info.params.complete = complete;
info.pkey_fd = pkey_fd;
- info.dll_CSNBKTC = dll_CSNBKTC;
+ info.cca = cca;
info.num_failed = 0;
info.num_reenciphered = 0;
info.num_skipped = 0;
@@ -3063,7 +3085,7 @@ out:
* @returnd 0 if the user confirmed the deletion, a negative errno value
* otherwise
*/
-static int _keystore_propmp_for_remove(struct keystore *keystore,
+static int _keystore_prompt_for_remove(struct keystore *keystore,
const char *name,
struct key_filenames *file_names)
{
@@ -3084,7 +3106,8 @@ static int _keystore_propmp_for_remove(struct keystore *keystore,
_keystore_msg_for_volumes(msg, key_prop, VOLUME_TYPE_PLAIN);
free(msg);
- printf("%s: Remove key '%s'? ", program_invocation_short_name, name);
+ printf("%s: Remove key '%s' [y/N]? ", program_invocation_short_name,
+ name);
if (fgets(str, sizeof(str), stdin) == NULL) {
rc = -EIO;
goto out;
@@ -3093,6 +3116,7 @@ static int _keystore_propmp_for_remove(struct keystore *keystore,
str[strlen(str) - 1] = '\0';
pr_verbose(keystore, "Prompt reply: '%s'", str);
if (strcasecmp(str, "y") != 0 && strcasecmp(str, "yes") != 0) {
+ warnx("Operation aborted");
rc = -ECANCELED;
goto out;
}
@@ -3129,7 +3153,7 @@ int keystore_remove_key(struct keystore *keystore, const char *name,
goto out;
if (!quiet) {
- if (_keystore_propmp_for_remove(keystore, name,
+ if (_keystore_prompt_for_remove(keystore, name,
&file_names) != 0)
goto out;
}
@@ -3204,7 +3228,7 @@ static int _keystore_display_key(struct keystore *keystore,
IS_XTS(secure_key_size) ? secure_key->bitsize * 2
: secure_key->bitsize,
0, 0,
- _keystore_reencipher_key_exists(file_names));
+ _keystore_reencipher_key_exists(file_names), 0);
out:
free(secure_key);
diff --git a/zkey/keystore.h b/zkey/keystore.h
index 16ecc62..8e888d1 100644
--- a/zkey/keystore.h
+++ b/zkey/keystore.h
@@ -14,6 +14,7 @@
#include <stdbool.h>
+#include "cca.h"
#include "pkey.h"
struct keystore {
@@ -54,7 +55,7 @@ int keystore_reencipher_key(struct keystore *keystore, const char *name_filter,
const char *apqn_filter,
bool from_old, bool to_new, bool inplace,
bool staged, bool complete, int pkey_fd,
- t_CSNBKTC dll_CSNBKTC);
+ struct cca_lib *cca);
int keystore_copy_key(struct keystore *keystore, const char *name,
const char *newname, const char *volumes);
diff --git a/zkey/pkey.c b/zkey/pkey.c
index a88c4e9..8471f3d 100644
--- a/zkey/pkey.c
+++ b/zkey/pkey.c
@@ -44,57 +44,7 @@
#define MAX_CIPHER_LEN 32
-/*
- * Definitions for the CCA library
- */
-#define CCA_LIBRARY_NAME "libcsulcca.so"
-#define CCA_WEB_PAGE "http://www.ibm.com/security/cryptocards"
-
-#define DEFAULT_KEYBITS 256
-
-/**
- * Loads the CCA library and provides the entry point of the CSNBKTC function.
- *
- * @param[out] lib_csulcca on return this contains the address of the CCA
- * library. dlclose() should be used to free this
- * when no longer needed.
- * @param[out] dll_CSNBKTC on return this contains the address of the
- * CSNBKTC function.
- * @param verbose if true, verbose messages are printed
- *
- * @returns 0 on success, -ELIBACC in case of library load errors
- */
-int load_cca_library(void **lib_csulcca, t_CSNBKTC *dll_CSNBKTC, bool verbose)
-{
- util_assert(lib_csulcca != NULL, "Internal error: lib_csulcca is NULL");
- util_assert(dll_CSNBKTC != NULL, "Internal error: dll_CSNBKTC is NULL");
-
- /* Load the CCA library */
- *lib_csulcca = dlopen(CCA_LIBRARY_NAME, RTLD_GLOBAL | RTLD_NOW);
- if (*lib_csulcca == NULL) {
- pr_verbose(verbose, "%s", dlerror());
- warnx("The command requires the IBM CCA Host Libraries and "
- "Tools.\nFor the supported environments and downloads, "
- "see:\n%s", CCA_WEB_PAGE);
- return -ELIBACC;
- }
-
- /* Get the Key Token Change function */
- *dll_CSNBKTC = (t_CSNBKTC)dlsym(*lib_csulcca, "CSNBKTC");
- if (*dll_CSNBKTC == NULL) {
- pr_verbose(verbose, "%s", dlerror());
- warnx("The command requires the IBM CCA Host Libraries and "
- "Tools.\nFor the supported environments and downloads, "
- "see:\n%s", CCA_WEB_PAGE);
- dlclose(*lib_csulcca);
- *lib_csulcca = NULL;
- return -ELIBACC;
- }
-
- pr_verbose(verbose, "CCA library '%s' has been loaded successfully",
- CCA_LIBRARY_NAME);
- return 0;
-}
+#define DEFAULT_KEYBITS 256
/**
* Opens the pkey device and returns its file descriptor.
@@ -267,7 +217,7 @@ static u8 *read_clear_key(const char *keyfile, size_t keybits, bool xts,
return NULL;
}
} else {
- keybits = DOUBLE_KEYSIZE_FOR_XTS(size * 8, xts);
+ keybits = HALF_KEYSIZE_FOR_XTS(size * 8, xts);
}
switch (keybits) {
@@ -522,96 +472,6 @@ out:
return rc;
}
-/**
- * Prints CCA return and reason code information for certain known CCA
- * error situations.
- *
- * @param return_code the CCA return code
- * @param reason_code the CCA reason code
- */
-static void print_CCA_error(int return_code, int reason_code)
-{
- switch (return_code) {
- case 8:
- switch (reason_code) {
- case 48:
- warnx("The secure key has a CCA master key "
- "verification pattern that is not valid");
- break;
- }
- break;
- case 12:
- switch (reason_code) {
- case 764:
- warnx("The CCA master key is not loaded and "
- "therefore a secure key cannot be enciphered");
- break;
- }
- break;
- }
-}
-
-/**
- * Re-enciphers a secure key.
- *
- * @param[in] dll_CSNBKTC the address of the CCA CSNBKTC function
- * @param[in] secure_key a buffer containing the secure key
- * @param[in] secure_key_size the size of the secure key
- * @param[in] method the re-enciphering method. METHOD_OLD_TO_CURRENT
- * or METHOD_CURRENT_TO_NEW.
- * @param[in] verbose if true, verbose messages are printed
- *
- * @returns 0 on success, -EIO in case of an error
- */
-int key_token_change(t_CSNBKTC dll_CSNBKTC,
- u8 *secure_key, unsigned int secure_key_size,
- char *method, bool verbose)
-{
- long exit_data_len = 0, rule_array_count;
- unsigned char rule_array[2 * 80] = { 0, };
- unsigned char exit_data[4] = { 0, };
- long return_code, reason_code;
-
- util_assert(dll_CSNBKTC != NULL, "Internal error: dll_CSNBKTC is NULL");
- util_assert(secure_key != NULL, "Internal error: secure_key is NULL");
- util_assert(secure_key_size > 0,
- "Internal error: secure_key_size is 0");
- util_assert(method != NULL, "Internal error: method is NULL");
-
- memcpy(rule_array, method, 8);
- memcpy(rule_array + 8, "AES ", 8);
- rule_array_count = 2;
-
- dll_CSNBKTC(&return_code, &reason_code,
- &exit_data_len, exit_data,
- &rule_array_count, rule_array,
- secure_key);
-
- pr_verbose(verbose, "CSNBKTC (Key Token Change) with '%s' returned: "
- "return_code: %ld, reason_code: %ld", method, return_code,
- reason_code);
- if (return_code != 0) {
- print_CCA_error(return_code, reason_code);
- return -EIO;
- }
-
- if (secure_key_size == 2 * SECURE_KEY_SIZE) {
- dll_CSNBKTC(&return_code, &reason_code,
- &exit_data_len, exit_data,
- &rule_array_count, rule_array,
- secure_key + SECURE_KEY_SIZE);
-
- pr_verbose(verbose, "CSNBKTC (Key Token Change) with '%s' "
- "returned: return_code: %ld, reason_code: %ld",
- method, return_code, reason_code);
- if (return_code != 0) {
- print_CCA_error(return_code, reason_code);
- return -EIO;
- }
- }
- return 0;
-}
-
/**
* Validates an XTS secure key (the second part)
*
@@ -909,3 +769,24 @@ out:
return rc;
}
+
+int get_master_key_verification_pattern(const u8 *secure_key,
+ size_t secure_key_size, u64 *mkvp,
+ bool verbose)
+{
+ struct secaeskeytoken *token = (struct secaeskeytoken *)secure_key;
+
+ util_assert(secure_key != NULL, "Internal error: secure_key is NULL");
+ util_assert(mkvp != NULL, "Internal error: mkvp is NULL");
+
+ if (secure_key_size < SECURE_KEY_SIZE) {
+ pr_verbose(verbose, "Size of secure key is too small: "
+ "%lu expected %lu", secure_key_size,
+ SECURE_KEY_SIZE);
+ return -EINVAL;
+ }
+
+ *mkvp = token->mkvp;
+
+ return 0;
+}
diff --git a/zkey/pkey.h b/zkey/pkey.h
index 3c524cd..c0aac2e 100644
--- a/zkey/pkey.h
+++ b/zkey/pkey.h
@@ -82,23 +82,10 @@ struct pkey_verifykey {
#define PKEY_VERIFYKEY _IOWR(PKEY_IOCTL_MAGIC, 0x07, struct pkey_verifykey)
-#define METHOD_OLD_TO_CURRENT "RTCMK "
-#define METHOD_CURRENT_TO_NEW "RTNMK "
-
-typedef void (*t_CSNBKTC)(long *return_code,
- long *reason_code,
- long *exit_data_length,
- unsigned char *exit_data,
- long *rule_array_count,
- unsigned char *rule_array,
- unsigned char *key_identifier);
-
#define PAES_BLOCK_SIZE 16
#define ENC_ZERO_LEN (2 * PAES_BLOCK_SIZE)
#define VERIFICATION_PATTERN_LEN (2 * ENC_ZERO_LEN + 1)
-int load_cca_library(void **lib_csulcca, t_CSNBKTC *dll_CSNBKTC, bool verbose);
-
int open_pkey_device(bool verbose);
int generate_secure_key_random(int pkey_fd, const char *keyfile,
@@ -122,11 +109,11 @@ int validate_secure_key(int pkey_fd,
size_t *clear_key_bitsize, int *is_old_mk,
bool verbose);
-int key_token_change(t_CSNBKTC dll_CSNBKTC,
- u8 *secure_key, unsigned int secure_key_size,
- char *method, bool verbose);
-
int generate_key_verification_pattern(const char *key, size_t key_size,
char *vp, size_t vp_len, bool verbose);
+int get_master_key_verification_pattern(const u8 *secure_key,
+ size_t secure_key_size, u64 *mkvp,
+ bool verbose);
+
#endif
diff --git a/zkey/properties.c b/zkey/properties.c
index c20e51b..147bcc5 100644
--- a/zkey/properties.c
+++ b/zkey/properties.c
@@ -9,6 +9,7 @@
* it under the terms of the MIT license. See LICENSE for details.
*/
+#include <ctype.h>
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
@@ -183,14 +184,16 @@ static struct property *properties_find(struct properties *properties,
* @param[in] properties the properties object
* @param[in] name the name of the property
* @param[in] value the value of the property
+ * @param[in] uppercase if true the value is set all uppercase
*
* @returns 0 on success,
* -EINVAL if the name or value contains invalid characters
*/
-int properties_set(struct properties *properties,
- const char *name, const char *value)
+int properties_set2(struct properties *properties,
+ const char *name, const char *value, bool uppercase)
{
struct property *property;
+ int i;
util_assert(properties != NULL, "Internal error: properties is NULL");
util_assert(name != NULL, "Internal error: name is NULL");
@@ -211,9 +214,30 @@ int properties_set(struct properties *properties,
property->value = util_strdup(value);
util_list_add_tail(&properties->list, property);
}
+ if (uppercase) {
+ for (i = 0; property->value[i] != '\0'; i++)
+ property->value[i] = toupper(property->value[i]);
+ }
+
return 0;
}
+/**
+ * Adds or updates a property
+ *
+ * @param[in] properties the properties object
+ * @param[in] name the name of the property
+ * @param[in] value the value of the property
+ *
+ * @returns 0 on success,
+ * -EINVAL if the name or value contains invalid characters
+ */
+int properties_set(struct properties *properties,
+ const char *name, const char *value)
+{
+ return properties_set2(properties, name, value, false);
+}
+
/**
* Gets a property
*
diff --git a/zkey/properties.h b/zkey/properties.h
index 234948e..f2c8a15 100644
--- a/zkey/properties.h
+++ b/zkey/properties.h
@@ -23,6 +23,9 @@ void properties_free(struct properties *properties);
int properties_set(struct properties *properties,
const char *name, const char *value);
+int properties_set2(struct properties *properties,
+ const char *name, const char *value, bool uppercase);
+
char *properties_get(struct properties *properties, const char *name);
int properties_remove(struct properties *properties, const char *name);
diff --git a/zkey/utils.c b/zkey/utils.c
new file mode 100644
index 0000000..9dc4af7
--- /dev/null
+++ b/zkey/utils.c
@@ -0,0 +1,725 @@
+/*
+ * zkey - Generate, re-encipher, and validate secure keys
+ *
+ * Copyright IBM Corp. 2019
+ *
+ * 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 <dirent.h>
+#include <err.h>
+#include <errno.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdint.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include "lib/util_path.h"
+#include "lib/util_file.h"
+#include "lib/util_scandir.h"
+#include "lib/util_libc.h"
+#include "lib/util_rec.h"
+#include "lib/util_base.h"
+
+#include "utils.h"
+#include "properties.h"
+
+#define pr_verbose(verbose, fmt...) do { \
+ if (verbose) \
+ warnx(fmt); \
+ } while (0)
+
+/**
+ * Checks if the specified card is of type CCA and is online
+ *
+ * @param[in] card card number
+ *
+ * @returns 1 if its a CCA card and is online, 0 if offline and -1 if its
+ * not a CCA card.
+ */
+int sysfs_is_card_online(int card)
+{
+ long int online;
+ char *dev_path;
+ char type[20];
+ int rc = 1;
+
+ dev_path = util_path_sysfs("bus/ap/devices/card%02x", card);
+ if (!util_path_is_dir(dev_path)) {
+ rc = 0;
+ goto out;
+ }
+ if (util_file_read_l(&online, 10, "%s/online", dev_path) != 0) {
+ rc = 0;
+ goto out;
+ }
+ if (online == 0) {
+ rc = 0;
+ goto out;
+ }
+ if (util_file_read_line(type, sizeof(type), "%s/type", dev_path) != 0) {
+ rc = 0;
+ goto out;
+ }
+ if (strncmp(type, "CEX", 3) != 0 || strlen(type) < 5) {
+ rc = 0;
+ goto out;
+ }
+ if (type[4] != 'C') {
+ rc = -1;
+ goto out;
+ }
+
+out:
+ free(dev_path);
+ return rc;
+}
+
+/**
+ * Checks if the specified APQN is of type CCA and is online
+ *
+ * @param[in] card card number
+ * @param[in] domain the domain
+ *
+ * @returns 1 if its a CCA card and is online, 0 if offline and -1 if its
+ * not a CCA card.
+ */
+int sysfs_is_apqn_online(int card, int domain)
+{
+ long int online;
+ char *dev_path;
+ int rc = 1;
+
+ rc = sysfs_is_card_online(card);
+ if (rc != 1)
+ return rc;
+
+ dev_path = util_path_sysfs("bus/ap/devices/card%02x/%02x.%04x", card,
+ card, domain);
+ if (!util_path_is_dir(dev_path)) {
+ rc = 0;
+ goto out;
+ }
+ if (util_file_read_l(&online, 10, "%s/online", dev_path) != 0) {
+ rc = 0;
+ goto out;
+ }
+ if (online == 0) {
+ rc = 0;
+ goto out;
+ }
+
+out:
+ free(dev_path);
+ return rc;
+}
+
+/**
+ * Gets the 8 character ASCII serial number string of an card from the sysfs.
+ *
+ * @param[in] card card number
+ * @param[out] serialnr Result buffer
+ * @param[in] verbose if true, verbose messages are printed
+ *
+ * @returns 0 if the serial number was returned. -ENODEV if the APQN is not
+ * available, or is not a CCA card. -ENOTSUP if the serialnr sysfs
+ * attribute is not available, because the zcrypt kernel module is
+ * on an older level.
+ */
+int sysfs_get_serialnr(int card, char serialnr[9], bool verbose)
+{
+ char *dev_path;
+ int rc = 0;
+
+ if (serialnr == NULL)
+ return -EINVAL;
+
+ if (sysfs_is_card_online(card) != 1)
+ return -ENODEV;
+
+ dev_path = util_path_sysfs("bus/ap/devices/card%02x", card);
+ if (!util_path_is_dir(dev_path)) {
+ rc = -ENODEV;
+ goto out;
+ }
+ if (util_file_read_line(serialnr, 9, "%s/serialnr", dev_path) != 0) {
+ rc = -ENOTSUP;
+ goto out;
+ }
+
+ if (strlen(serialnr) == 0) {
+ rc = -ENODEV;
+ goto out;
+ }
+
+ pr_verbose(verbose, "Serial number of %02x: %s", card, serialnr);
+out:
+ if (rc != 0)
+ pr_verbose(verbose, "Failed to get serial number for "
+ "%02x: %s", card, strerror(-rc));
+
+ free(dev_path);
+ return rc;
+}
+
+static int parse_mk_info(char *line, struct mk_info *mk_info)
+{
+ struct mk_info_reg *mk_reg;
+ char *save;
+ char *tok;
+
+ tok = strtok_r(line, " ", &save);
+ if (tok == NULL)
+ return -EIO;
+
+ if (strcasecmp(tok, "AES") != 0)
+ return 0;
+
+ tok = strtok_r(NULL, " ", &save);
+ if (tok == NULL)
+ return -EIO;
+
+ if (strcasecmp(tok, "NEW:") == 0)
+ mk_reg = &mk_info->new_mk;
+ else if (strcasecmp(tok, "CUR:") == 0)
+ mk_reg = &mk_info->cur_mk;
+ else if (strcasecmp(tok, "OLD:") == 0)
+ mk_reg = &mk_info->old_mk;
+ else
+ return -EIO;
+
+ tok = strtok_r(NULL, " ", &save);
+ if (tok == NULL)
+ return -EIO;
+
+ if (strcasecmp(tok, "empty") == 0)
+ mk_reg->mk_state = MK_STATE_EMPTY;
+ else if (strcasecmp(tok, "partial") == 0)
+ mk_reg->mk_state = MK_STATE_PARTIAL;
+ else if (strcasecmp(tok, "full") == 0)
+ mk_reg->mk_state = MK_STATE_FULL;
+ else if (strcasecmp(tok, "valid") == 0)
+ mk_reg->mk_state = MK_STATE_VALID;
+ else if (strcasecmp(tok, "invalid") == 0)
+ mk_reg->mk_state = MK_STATE_INVALID;
+ else
+ mk_reg->mk_state = MK_STATE_UNKNOWN;
+
+ tok = strtok_r(NULL, " ", &save);
+ if (tok == NULL)
+ return -EIO;
+
+ if (sscanf(tok, "%llx", &mk_reg->mkvp) != 1)
+ return -EIO;
+
+ return 0;
+}
+
+/**
+ * Gets the master key states and verification patterns of an APQN from the
+ * sysfs.
+ *
+ * @param[in] card card number
+ * @param[in] domain the domain
+ * @param[out] mk_info structure is filled on return with master key infos
+ * @param[in] verbose if true, verbose messages are printed
+ *
+ * @returns 0 if the master key info was returned. -ENODEV if the APQN is not
+ * available, or is not a CCA card. -ENOTSUP if the mkvps sysfs
+ * attribute is not available, because the zcrypt kernel module is
+ * on an older level.
+ */
+int sysfs_get_mkvps(int card, int domain, struct mk_info *mk_info, bool verbose)
+{
+ char *dev_path;
+ char *p, *end;
+ char buf[100];
+ int rc = 0;
+ FILE *fp;
+
+ if (mk_info == NULL)
+ return -EINVAL;
+
+ memset(mk_info, 0, sizeof(struct mk_info));
+ mk_info->new_mk.mk_state = MK_STATE_UNKNOWN;
+ mk_info->cur_mk.mk_state = MK_STATE_UNKNOWN;
+ mk_info->old_mk.mk_state = MK_STATE_UNKNOWN;
+
+ if (sysfs_is_apqn_online(card, domain) != 1)
+ return -ENODEV;
+
+ dev_path = util_path_sysfs("bus/ap/devices/card%02x/%02x.%04x/mkvps",
+ card, card, domain);
+ if (!util_path_is_reg_file(dev_path)) {
+ rc = -ENOTSUP;
+ goto out;
+ }
+
+ fp = fopen(dev_path, "r");
+ if (fp == NULL) {
+ rc = -ENOTSUP;
+ goto out;
+ }
+
+ /*
+ * Expected contents:
+ * AES NEW: <new_mk_state> <new_mk_mkvp>
+ * AES CUR: <cur_mk_state> <cur_mk_mkvp>
+ * AES OLD: <old_mk_state> <old_mk_mkvp>
+ * with
+ * <new_mk_state>: 'empty' or 'partial' or 'full'
+ * <cur_mk_state>, <old_mk_state>: 'valid' or 'invalid'
+ * <new_mk_mkvp>, <cur_mk_mkvp>, <old_mk_mkvp:
+ * 8 byte hex string with leading 0x
+ */
+ while ((p = fgets(buf, sizeof(buf), fp)) != NULL) {
+ end = memchr(buf, '\n', sizeof(buf));
+ if (end)
+ *end = 0;
+ else
+ buf[sizeof(buf) - 1] = 0;
+
+ pr_verbose(verbose, "mkvp for %02x.%04x: %s", card, domain,
+ buf);
+
+ rc = parse_mk_info(buf, mk_info);
+ if (rc != 0)
+ break;
+ }
+
+ fclose(fp);
+
+ if (mk_info->new_mk.mk_state == MK_STATE_UNKNOWN &&
+ mk_info->cur_mk.mk_state == MK_STATE_UNKNOWN &&
+ mk_info->old_mk.mk_state == MK_STATE_UNKNOWN)
+ rc = -EIO;
+out:
+ if (rc != 0)
+ pr_verbose(verbose, "Failed to get mkvps for %02x.%04x: %s",
+ card, domain, strerror(-rc));
+
+ free(dev_path);
+ return rc;
+}
+
+static int scan_for_domains(int card, apqn_handler_t handler,
+ void *handler_data, bool verbose)
+{
+ struct dirent **namelist;
+ char fname[290];
+ int i, n, domain, rc = 0;
+
+ sprintf(fname, "/sys/devices/ap/card%02x/", card);
+ n = util_scandir(&namelist, alphasort, fname,
+ "[0-9a-fA-F]+\\.[0-9a-fA-F]+");
+
+ if (n < 0)
+ return -EIO;
+
+ for (i = 0; i < n; i++) {
+ if (sscanf(namelist[i]->d_name, "%x.%x", &card, &domain) != 2)
+ continue;
+
+ pr_verbose(verbose, "Found %02x.%04x", card, domain);
+
+ if (sysfs_is_apqn_online(card, domain) != 1) {
+ pr_verbose(verbose, "APQN %02x.%04x is offline or not "
+ "CCA", card, domain);
+ continue;
+ }
+
+ rc = handler(card, domain, handler_data);
+ if (rc != 0)
+ break;
+ }
+
+ util_scandir_free(namelist, n);
+ return rc;
+}
+
+
+static int scan_for_apqns(apqn_handler_t handler, void *handler_data,
+ bool verbose)
+{
+ struct dirent **namelist;
+ int i, n, card, rc = 0;
+
+ if (handler == NULL)
+ return -EINVAL;
+
+ n = util_scandir(&namelist, alphasort, "/sys/devices/ap/",
+ "card[0-9a-fA-F]+");
+ if (n < 0)
+ return -EIO;
+
+ for (i = 0; i < n; i++) {
+ if (sscanf(namelist[i]->d_name, "card%x", &card) != 1)
+ continue;
+
+ pr_verbose(verbose, "Found card %02x", card);
+
+ if (sysfs_is_card_online(card) != 1) {
+ pr_verbose(verbose, "Card %02x is offline or not CCA",
+ card);
+ continue;
+ }
+
+ rc = scan_for_domains(card, handler, handler_data, verbose);
+ if (rc != 0)
+ break;
+ }
+
+ util_scandir_free(namelist, n);
+ return rc;
+}
+
+/**
+ * Calls the handler for all APQNs specified in the apqns parameter, or of this
+ * is NULL, for all online CCA APQNs found in sysfs. In case sysfs is inspected,
+ * the cards and domains are processed in alphabetical order.
+ *
+ * @param[in] apqns a comma separated list of APQNs. If NULL is specified,
+ * or an empty string, then all online CCA APQNs are
+ * handled.
+ * @param[in] handler a handler function that is called for each APQN
+ * @param[in] handler_data private data that is passed to the handler
+ * @param[in] verbose if true, verbose messages are printed
+ *
+ * @returns 0 for success or a negative errno in case of an error
+ */
+int handle_apqns(const char *apqns, apqn_handler_t handler, void *handler_data,
+ bool verbose)
+{
+ int card, domain;
+ char *copy, *tok;
+ char *save;
+ int rc = 0;
+
+ if (apqns == NULL || (apqns != NULL && strlen(apqns) == 0)) {
+ rc = scan_for_apqns(handler, handler_data, verbose);
+ } else {
+ copy = util_strdup(apqns);
+ tok = strtok_r(copy, ",", &save);
+ while (tok != NULL) {
+
+ if (sscanf(tok, "%x.%x", &card, &domain) != 2) {
+ warnx("the APQN '%s' is not valid",
+ tok);
+ rc = -EINVAL;
+ break;
+ }
+
+ pr_verbose(verbose, "Specified: %02x.%04x", card,
+ domain);
+ rc = handler(card, domain, handler_data);
+ if (rc != 0)
+ break;
+
+ tok = strtok_r(NULL, ",", &save);
+ }
+ free(copy);
+ }
+
+ return rc;
+}
+
+struct print_apqn_info {
+ struct util_rec *rec;
+ bool verbose;
+};
+
+static int print_apqn_mk_info(int card, int domain, void *handler_data)
+{
+ struct print_apqn_info *info = (struct print_apqn_info *)handler_data;
+ struct mk_info mk_info;
+ int rc;
+
+ rc = sysfs_get_mkvps(card, domain, &mk_info, info->verbose);
+ if (rc == -ENOTSUP)
+ return rc;
+
+ util_rec_set(info->rec, "APQN", "%02x.%04x", card, domain);
+
+ if (rc == 0) {
+ if (mk_info.new_mk.mk_state == MK_STATE_FULL)
+ util_rec_set(info->rec, "NEW", "%016llx",
+ mk_info.new_mk.mkvp);
+ else if (mk_info.new_mk.mk_state == MK_STATE_PARTIAL)
+ util_rec_set(info->rec, "NEW", "partially loaded");
+ else
+ util_rec_set(info->rec, "NEW", "-");
+
+ if (mk_info.cur_mk.mk_state == MK_STATE_VALID)
+ util_rec_set(info->rec, "CUR", "%016llx",
+ mk_info.cur_mk.mkvp);
+ else
+ util_rec_set(info->rec, "CUR", "-");
+
+ if (mk_info.old_mk.mk_state == MK_STATE_VALID)
+ util_rec_set(info->rec, "OLD", "%016llx",
+ mk_info.old_mk.mkvp);
+ else
+ util_rec_set(info->rec, "OLD", "-");
+ } else {
+ util_rec_set(info->rec, "NEW", "?");
+ util_rec_set(info->rec, "CUR", "?");
+ util_rec_set(info->rec, "OLD", "?");
+ }
+
+ util_rec_print(info->rec);
+
+ return 0;
+}
+
+/**
+ * Prints master key information for all specified APQNs
+ *
+ * @param[in] apqns a comma separated list of APQNs. If NULL is specified,
+ * or an empty string, then all online CCA APQNs are
+ * printed.
+ * @param[in] verbose if true, verbose messages are printed
+ *
+ * @returns 0 for success or a negative errno in case of an error. -ENOTSUP is
+ * returned when the mkvps sysfs attribute is not available, because
+ * the zcrypt kernel module is on an older level.
+ */
+int print_mk_info(const char *apqns, bool verbose)
+{
+ struct print_apqn_info info;
+ int rc;
+
+ info.verbose = verbose;
+ info.rec = util_rec_new_wide("-");
+
+ util_rec_def(info.rec, "APQN", UTIL_REC_ALIGN_LEFT, 11, "CARD.DOMAIN");
+ util_rec_def(info.rec, "NEW", UTIL_REC_ALIGN_LEFT, 16, "NEW MK");
+ util_rec_def(info.rec, "CUR", UTIL_REC_ALIGN_LEFT, 16, "CURRENT MK");
+ util_rec_def(info.rec, "OLD", UTIL_REC_ALIGN_LEFT, 16, "OLD MK");
+ util_rec_print_hdr(info.rec);
+
+ rc = handle_apqns(apqns, print_apqn_mk_info, &info, verbose);
+
+ util_rec_free(info.rec);
+ return rc;
+}
+
+struct cross_check_info {
+ u64 mkvp;
+ u64 new_mkvp;
+ bool key_mkvp;
+ u32 num_cur_match;
+ u32 num_old_match;
+ u32 num_new_match;
+ bool mismatch;
+ bool print_mks;
+ int num_checked;
+ bool verbose;
+};
+
+static int cross_check_mk_info(int card, int domain, void *handler_data)
+{
+ struct cross_check_info *info = (struct cross_check_info *)handler_data;
+ struct mk_info mk_info;
+ char temp[200];
+ int rc;
+
+ rc = sysfs_get_mkvps(card, domain, &mk_info, info->verbose);
+ if (rc == -ENODEV) {
+ info->print_mks = 1;
+ printf("WARNING: APQN %02x.%04x: Not available or not of "
+ "type CCA\n", card, domain);
+ return 0;
+ }
+ if (rc != 0)
+ return rc;
+
+ info->num_checked++;
+
+ if (mk_info.new_mk.mk_state == MK_STATE_PARTIAL) {
+ info->print_mks = 1;
+ sprintf(temp, "INFO: APQN %02x.%04x: The NEW master key "
+ "register is only partially loaded.", card, domain);
+ util_print_indented(temp, 0);
+ }
+
+ if (info->new_mkvp == 0 &&
+ mk_info.new_mk.mk_state == MK_STATE_FULL)
+ info->new_mkvp = mk_info.new_mk.mkvp;
+
+ if (mk_info.new_mk.mk_state == MK_STATE_FULL &&
+ mk_info.new_mk.mkvp != info->new_mkvp) {
+ info->print_mks = 1;
+ sprintf(temp, "WARNING: APQN %02x.%04x: The NEW master key "
+ "register contains a different master key than "
+ "the NEW register of other APQNs.", card,
+ domain);
+ util_print_indented(temp, 0);
+ }
+
+ if (mk_info.cur_mk.mk_state != MK_STATE_VALID) {
+ info->print_mks = 1;
+ info->mismatch = 1;
+ printf("WARNING: APQN %02x.%04x: No master key is set.\n", card,
+ domain);
+ return 0;
+ }
+
+ if (mk_info.old_mk.mk_state == MK_STATE_VALID &&
+ mk_info.old_mk.mkvp == mk_info.cur_mk.mkvp) {
+ info->print_mks = 1;
+ sprintf(temp, "INFO: APQN %02x.%04x: The OLD master key "
+ "register contains the same master key as the CURRENT "
+ "master key register.", card, domain);
+ util_print_indented(temp, 0);
+ }
+ if (mk_info.new_mk.mk_state == MK_STATE_FULL &&
+ mk_info.new_mk.mkvp == mk_info.cur_mk.mkvp) {
+ info->print_mks = 1;
+ sprintf(temp, "INFO: APQN %02x.%04x: The NEW master key "
+ "register contains the same master key as the CURRENT "
+ "master key register.", card, domain);
+ util_print_indented(temp, 0);
+ }
+ if (mk_info.new_mk.mk_state == MK_STATE_FULL &&
+ mk_info.old_mk.mk_state == MK_STATE_VALID &&
+ mk_info.new_mk.mkvp == mk_info.old_mk.mkvp) {
+ info->print_mks = 1;
+ sprintf(temp, "INFO: APQN %02x.%04x: The NEW master key "
+ "register contains the same master key as the OLD "
+ "master key register.", card, domain);
+ util_print_indented(temp, 0);
+ }
+
+ if (info->mkvp == 0)
+ info->mkvp = mk_info.cur_mk.mkvp;
+
+ if (info->key_mkvp) {
+ if (mk_info.cur_mk.mk_state == MK_STATE_VALID &&
+ mk_info.cur_mk.mkvp == info->mkvp)
+ info->num_cur_match++;
+
+ if (mk_info.old_mk.mk_state == MK_STATE_VALID &&
+ mk_info.old_mk.mkvp == info->mkvp)
+ info->num_old_match++;
+
+ if (mk_info.new_mk.mk_state == MK_STATE_FULL &&
+ mk_info.new_mk.mkvp == info->mkvp)
+ info->num_new_match++;
+ }
+
+ if (mk_info.cur_mk.mkvp != info->mkvp) {
+
+ if (info->key_mkvp) {
+ if (mk_info.old_mk.mk_state == MK_STATE_VALID &&
+ mk_info.old_mk.mkvp == info->mkvp) {
+ info->print_mks = 1;
+ sprintf(temp, "INFO: APQN %02x.%04x: The master"
+ " key has been changed to a new "
+ "master key, but the secure key has "
+ "not yet been re-enciphered.", card,
+ domain);
+ util_print_indented(temp, 0);
+ } else if (mk_info.new_mk.mk_state == MK_STATE_FULL &&
+ mk_info.new_mk.mkvp == info->mkvp) {
+ info->print_mks = 1;
+ sprintf(temp, "INFO: APQN %02x.%04x: The master"
+ " key has been changed but is not "
+ "yet been set (made active).", card,
+ domain);
+ util_print_indented(temp, 0);
+ } else {
+ info->print_mks = 1;
+ info->mismatch = 1;
+ sprintf(temp, "WARNING: APQN %02x.%04x: The "
+ "CURRENT master key register contains "
+ "a master key that is different from "
+ "the one used by the secure key.", card,
+ domain);
+ util_print_indented(temp, 0);
+ }
+ } else {
+ info->print_mks = 1;
+ info->mismatch = 1;
+ }
+ }
+
+ return 0;
+}
+
+/**
+ * Cross checks the master key information for all specified APQNs. It checks
+ * if all specified APQNs have the same current master key, and if it matches
+ * the master key specified by the mkvp parameter (optional). If not, it prints
+ * out an information message about the APQNs that have a different master key.
+ *
+ * @param[in] apqns a comma separated list of APQNs. If NULL is specified,
+ * or an empty string, then all online CCA APQNs are
+ * checked.
+ * @param[in] mkvp The master key verification pattern of a secure key.
+ * If this is all zero, then the master keys are not
+ * matched against it.
+ * @param[in] print_mks if true, then a the full master key info of all
+ * specified APQns is printed, in case of a mismatch.
+ * @param[in] verbose if true, verbose messages are printed
+ *
+ * @returns 0 for success or a negative errno in case of an error. -ENODEV is
+ * returned if at least one APQN has a mismatching master key.
+ * -ENOTSUP is returned when the mkvps sysfs attribute is not
+ * available, because the zcrypt kernel module is on an older level.
+ */
+int cross_check_apqns(const char *apqns, u64 mkvp, bool print_mks, bool verbose)
+{
+ struct cross_check_info info;
+ char temp[200];
+ int rc;
+
+ memset(&info, 0, sizeof(info));
+ info.key_mkvp = mkvp != 0;
+ info.mkvp = mkvp;
+ info.verbose = verbose;
+
+ pr_verbose(verbose, "Cross checking APQNs with mkvp 0x%016llx: %s",
+ mkvp, apqns != NULL ? apqns : "ANY");
+
+ rc = handle_apqns(apqns, cross_check_mk_info, &info, verbose);
+ if (rc != 0)
+ return rc;
+
+ if (info.mismatch) {
+ if (info.key_mkvp)
+ printf("WARNING: Not all APQNs have the correct master "
+ "key (%016llx).\n", mkvp);
+ else
+ printf("WARNING: Not all APQNs have the same master "
+ "key.\n");
+
+ rc = -ENODEV;
+ }
+ if (info.num_checked == 0) {
+ printf("WARNING: None of the APQNs is available or of "
+ "type CCA\n");
+ rc = -ENODEV;
+ }
+ if (info.num_old_match > 0 && info.num_new_match > 0) {
+ sprintf(temp, "WARNING: On %u APQNs the OLD master key "
+ "register contains the master key use by the secure "
+ "key, and on %u APQNs the NEW master key register "
+ "contains the master key use by the secure key.",
+ info.num_old_match, info.num_new_match);
+ util_print_indented(temp, 0);
+ info.print_mks = 1;
+ rc = -ENODEV;
+ }
+
+ if (print_mks && info.print_mks) {
+ printf("\n");
+ print_mk_info(apqns, verbose);
+ printf("\n");
+ }
+
+ return rc;
+}
diff --git a/zkey/utils.h b/zkey/utils.h
new file mode 100644
index 0000000..c4dfb2b
--- /dev/null
+++ b/zkey/utils.h
@@ -0,0 +1,54 @@
+/*
+ * zkey - Generate, re-encipher, and validate secure keys
+ *
+ * This header file defines the interface to the CCA host library.
+ *
+ * Copyright IBM Corp. 2019
+ *
+ * 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 UTILS_H
+#define UTILS_H
+
+#include "lib/zt_common.h"
+
+int sysfs_is_card_online(int card);
+
+int sysfs_is_apqn_online(int card, int domain);
+
+int sysfs_get_serialnr(int card, char serialnr[9], bool verbose);
+
+#define MK_STATE_EMPTY 0
+#define MK_STATE_PARTIAL 1
+#define MK_STATE_FULL 2
+#define MK_STATE_VALID 3
+#define MK_STATE_INVALID 4
+#define MK_STATE_UNKNOWN -1
+
+struct mk_info_reg {
+ int mk_state;
+ u64 mkvp;
+};
+
+struct mk_info {
+ struct mk_info_reg new_mk;
+ struct mk_info_reg cur_mk;
+ struct mk_info_reg old_mk;
+};
+
+int sysfs_get_mkvps(int card, int domain, struct mk_info *mk_info,
+ bool verbose);
+
+typedef int(*apqn_handler_t) (int card, int domain, void *handler_data);
+
+int handle_apqns(const char *apqns, apqn_handler_t handler, void *handler_data,
+ bool verbose);
+
+int print_mk_info(const char *apqns, bool verbose);
+
+int cross_check_apqns(const char *apqns, u64 mkvp, bool print_mks,
+ bool verbose);
+
+#endif
diff --git a/zkey/zkey-cryptsetup.1 b/zkey/zkey-cryptsetup.1
index e93b5f0..35e3591 100644
--- a/zkey/zkey-cryptsetup.1
+++ b/zkey/zkey-cryptsetup.1
@@ -91,6 +91,8 @@ behave in the same way as with \fBcryptsetup\fP.
.B zkey\-cryptsetup
.BR reencipher | re
.I device
+.RB [ \-\-to\-new | \-N ]
+.RB [ \-\-from\-old | \-O ]
.RB [ \-\-staged | \-s ]
.RB [ \-\-in\-place | \-i ]
.RB [ \-\-complete | \-c ]
@@ -128,17 +130,36 @@ register can still be used until the master key is changed again.
The \fBNEW\fP register contains the new master key to be set.
The master key in the \fBNEW\fP register cannot be used until it is made
the current master key. You can pro-actively re-encipher a secure key with the
-\fBNEW\fP master key before this key is made the \fBCURRENT\fP key.
+\fBNEW\fP master key before this key is made the \fBCURRENT\fP key. Use the
+.B \-\-to-new
+option to do this.
.RE
.PP
-\fBzkey\-cryptsetup\fP automatically detects whether the secure volume key
-is currently enciphered with the master key in the \fBOLD\fP register or with
-the master key in the \fBCURRENT\fP register. If currently enciphered with the
-master key in the \fBOLD\fP register, it is re-enciphered with the master key
-in the \fBCURRENT\fP register. If it is currently enciphered with the master
-key in the \fBCURRENT\fP register, it is re-enciphered with the master key in
-the \fBNEW\fP register. If for this case the \fBNEW\fP register does not
-contain a valid master key, then the re-encipher operation fails.
+Use the
+.B \-\-from\-old
+option to re-encipher a secure volume key that is currently enciphered with
+the master key in the \fBOLD\fP register with the master key in the
+\fBCURRENT\fP register.
+.PP
+.PP
+If both the
+.B \-\-from-old
+and
+.B \-\-to-new
+options are specified, a secure volume key that is currently enciphered
+with the master key in the \fBOLD\fP register is re-enciphered with the
+master key in the \fBNEW\fP register.
+.RE
+.PP
+If both options are omitted, \fBzkey-cryptsetup\fP automatically detects whether
+the secure volume key is currently enciphered with the master key in the
+\fBOLD\fP register or with the master key in the \fBCURRENT\fP register.
+If currently enciphered with the master key in the \fBOLD\fP register,
+it is re-enciphered with the master key in the \fBCURRENT\fP register.
+If it is currently enciphered with the master key in the \fBCURRENT\fP
+register, it is re-enciphered with the master key in the \fBNEW\fP register.
+If for this case the \fBNEW\fP register does not contain a valid master key,
+then the re-encipher operation fails.
.PP
Re-enciphering a secure volume key of a volume encrypted with
\fBLUKS2\fP and the \fBpaes\fP cipher can be performed \fBin-place\fP, or in
@@ -326,6 +347,16 @@ relevance.
.
.SS "Options for the reencipher command"
.TP
+.BR \-N ", " \-\-to\-new
+Re-enciphers a secure volume key in the LUKS2 header that is currently
+enciphered with the master key in the CURRENT register with the master key in
+the NEW register.
+.TP
+.BR \-O ", " \-\-from\-old
+Re-enciphers a secure volume key in the LUKS2 header that is currently
+enciphered with the master key in the OLD register with the master key in the
+CURRENT register.
+.TP
.BR \-i ", " \-\-in-place
Forces an in-place re-enciphering of a secure volume key in the LUKS2
header. This option immediately replaces the secure volume key in the LUKS2
diff --git a/zkey/zkey-cryptsetup.c b/zkey/zkey-cryptsetup.c
index 8180b4a..c683fd4 100644
--- a/zkey/zkey-cryptsetup.c
+++ b/zkey/zkey-cryptsetup.c
@@ -34,6 +34,7 @@
#include "misc.h"
#include "pkey.h"
+#include "cca.h"
/* Detect if cryptsetup 2.1 or later is available */
#ifdef CRYPT_LOG_DEBUG_JSON
@@ -94,6 +95,8 @@ static struct zkey_cryptsetup_globals {
long long keyfile_offset;
long long keyfile_size;
long long tries;
+ bool tonew;
+ bool fromold;
bool complete;
bool inplace;
bool staged;
@@ -101,8 +104,7 @@ static struct zkey_cryptsetup_globals {
bool batch_mode;
bool debug;
bool verbose;
- void *lib_csulcca;
- t_CSNBKTC dll_CSNBKTC;
+ struct cca_lib cca;
int pkey_fd;
struct crypt_device *cd;
} g = {
@@ -162,6 +164,22 @@ static struct util_opt opt_vec[] = {
.desc = "OPTIONS",
.command = COMMAND_REENCIPHER,
},
+ {
+ .option = {"to-new", 0, NULL, 'N'},
+ .desc = "Re-enciphers a secure volume key in the LUKS2 header "
+ "that is currently enciphered with the master key in "
+ "the CURRENT register with the master key in the NEW "
+ "register",
+ .command = COMMAND_REENCIPHER,
+ },
+ {
+ .option = {"from-old", 0, NULL, 'O'},
+ .desc = "Re-enciphers a secure volume key in the LUKS2 header "
+ "that is currently enciphered with the master key in "
+ "the OLD register with the master key in the CURRENT "
+ "register",
+ .command = COMMAND_REENCIPHER,
+ },
{
.option = {"staged", 0, NULL, 's'},
.desc = "Forces that the re-enciphering of a secure volume "
@@ -1275,7 +1293,7 @@ static int activate_unbound_keyslot(int token, int keyslot, const char *key,
util_print_indented(complete_msg, 0);
util_print_indented("All key slots containing the old volume key are "
"now in unbound state. Do you want to remove "
- "these key slots?", 0);
+ "these key slots [y/N]?", 0);
if (!prompt_for_yes())
return 0;
@@ -1514,17 +1532,19 @@ static int reencipher_prepare(int token)
char *password = NULL;
size_t password_len;
char *key = NULL;
+ int selected = 1;
size_t keysize;
int is_old_mk;
char *prompt;
char *msg;
+ u64 mkvp;
int rc;
if (token >= 0) {
util_asprintf(&msg, "Staged volume key re-enciphering is "
"already initiated for device '%s'. Do you want to "
"cancel the pending re-enciphering and start a "
- "new re-enciphering process?", g.pos_arg);
+ "new re-enciphering process [y/N]?", g.pos_arg);
util_print_indented(msg, 0);
free(msg);
@@ -1570,25 +1590,97 @@ static int reencipher_prepare(int token)
if (rc < 0)
goto out;
- util_asprintf(&msg, "The secure volume key of device '%s' is "
- "enciphered with the %s CCA master key and is being "
- "re-enciphered with the %s CCA master key.",
- g.pos_arg, is_old_mk ? "OLD" : "CURRENT",
- is_old_mk ? "CURRENT" : "NEW");
- util_print_indented(msg, 0);
- free(msg);
+ if (!g.fromold && !g.tonew) {
+ /* Autodetect reencipher mode */
+ if (is_old_mk) {
+ g.fromold = 1;
+ util_asprintf(&msg, "The secure volume key of device "
+ "'%s' is enciphered with the OLD CCA "
+ "master key and is being re-enciphered "
+ "with the CURRENT CCA master key.",
+ g.pos_arg);
+ util_print_indented(msg, 0);
+ free(msg);
+ } else {
+ g.tonew = 1;
+ util_asprintf(&msg, "The secure volume key of device "
+ "'%s' is enciphered with the CURRENT CCA "
+ "master key and is being re-enciphered "
+ "with the NEW CCA master key.",
+ g.pos_arg);
+ util_print_indented(msg, 0);
+ free(msg);
+ }
+ }
- rc = key_token_change(g.dll_CSNBKTC, (u8 *)key, keysize,
- is_old_mk ? METHOD_OLD_TO_CURRENT :
- METHOD_CURRENT_TO_NEW,
- g.verbose);
+ rc = get_master_key_verification_pattern((u8 *)key, keysize, &mkvp,
+ g.verbose);
if (rc != 0) {
- warnx("Failed to re-encipher the secure volume key of device "
- "'%s'", g.pos_arg);
- rc = -EINVAL;
+ warnx("Failed to get the master key verification pattern: %s",
+ strerror(-rc));
goto out;
}
+ if (g.fromold) {
+ rc = select_cca_adapter_by_mkvp(&g.cca, mkvp, NULL,
+ FLAG_SEL_CCA_MATCH_OLD_MKVP,
+ g.verbose);
+ if (rc == -ENOTSUP) {
+ rc = 0;
+ selected = 0;
+ }
+ if (rc != 0) {
+ util_print_indented("No APQN found that is suitable "
+ "for re-enciphering the secure AES "
+ "volume key from the OLD to the "
+ "CURRENT CCA master key.", 0);
+ goto out;
+ }
+
+ rc = key_token_change(&g.cca, (u8 *)key, keysize,
+ METHOD_OLD_TO_CURRENT, g.verbose);
+ if (rc != 0) {
+ warnx("Failed to re-encipher the secure volume key of "
+ "device '%s'\n", g.pos_arg);
+ if (!selected)
+ print_msg_for_cca_envvars(
+ "secure AES volume key");
+ rc = -EINVAL;
+ goto out;
+ }
+ }
+
+ if (g.tonew) {
+ rc = select_cca_adapter_by_mkvp(&g.cca, mkvp, NULL,
+ FLAG_SEL_CCA_MATCH_CUR_MKVP |
+ FLAG_SEL_CCA_NEW_MUST_BE_SET,
+ g.verbose);
+ if (rc == -ENOTSUP) {
+ rc = 0;
+ selected = 0;
+ }
+ if (rc != 0) {
+ util_print_indented("No APQN found that is suitable "
+ "for re-enciphering the secure AES "
+ "volume key from the CURRENT to "
+ "the NEW CCA master key.", 0);
+ goto out;
+ }
+
+ rc = key_token_change(&g.cca, (u8 *)key, keysize,
+ METHOD_CURRENT_TO_NEW,
+ g.verbose);
+ if (rc != 0) {
+ warnx("Failed to re-encipher the secure volume key of "
+ "device '%s'\n", g.pos_arg);
+ if (!selected)
+ print_msg_for_cca_envvars(
+ "secure AES volume key");
+ rc = -EINVAL;
+ goto out;
+ }
+ }
+
rc = crypt_keyslot_add_by_key(g.cd, CRYPT_ANY_SLOT, key, keysize,
password, password_len,
CRYPT_VOLUME_KEY_NO_SEGMENT);
@@ -1651,10 +1743,12 @@ static int reencipher_complete(int token)
char *password = NULL;
size_t password_len;
char *key = NULL;
+ int selected = 1;
size_t keysize;
int is_old_mk;
char *prompt;
char *msg;
+ u64 mkvp;
int rc;
rc = get_reencipher_token(g.cd, token, &tok, true);
@@ -1690,7 +1784,7 @@ static int reencipher_complete(int token)
"was completed.\n"
"Do you want to re-encipher the secure key with "
"the CCA master key in the CURRENT master key "
- "register?", g.pos_arg);
+ "register [y/N]?", g.pos_arg);
util_print_indented(msg, 0);
free(msg);
@@ -1700,11 +1794,38 @@ static int reencipher_complete(int token)
goto out;
}
- rc = key_token_change(g.dll_CSNBKTC, (u8 *)key, keysize,
+ rc = get_master_key_verification_pattern((u8 *)key, keysize,
+ &mkvp, g.verbose);
+ if (rc != 0) {
+ warnx("Failed to get the master key verification "
+ "pattern: %s",
+ strerror(-rc));
+ goto out;
+ }
+
+ rc = select_cca_adapter_by_mkvp(&g.cca, mkvp, NULL,
+ FLAG_SEL_CCA_MATCH_OLD_MKVP,
+ g.verbose);
+ if (rc == -ENOTSUP) {
+ rc = 0;
+ selected = 0;
+ }
+ if (rc != 0) {
+ util_print_indented("No APQN found that is suitable "
+ "for re-enciphering the secure AES "
+ "volume key from the OLD to the "
+ "CURRENT CCA master key.", 0);
+ goto out;
+ }
+
+ rc = key_token_change(&g.cca, (u8 *)key, keysize,
METHOD_OLD_TO_CURRENT, g.verbose);
if (rc != 0) {
warnx("Failed to re-encipher the secure volume key for "
- "device '%s'", g.pos_arg);
+ "device '%s'\n", g.pos_arg);
+ if (!selected)
+ print_msg_for_cca_envvars(
+ "secure AES volume key");
rc = -EINVAL;
goto out;
}
@@ -1834,6 +1955,7 @@ static int command_validate(void)
char *prompt;
char *msg;
int token;
+ u64 mkvp;
int rc;
util_asprintf(&prompt, "Enter passphrase for '%s': ", g.pos_arg);
@@ -1864,6 +1986,14 @@ static int command_validate(void)
vp_tok_avail = 1;
}
+ rc = get_master_key_verification_pattern((u8 *)key, keysize,
+ &mkvp, g.verbose);
+ if (rc != 0) {
+ warnx("Failed to get the master key verification pattern: %s",
+ strerror(-rc));
+ goto out;
+ }
+
printf("Validation of secure volume key of device '%s':\n", g.pos_arg);
printf(" Status: %s\n", is_valid ? "Valid" : "Invalid");
printf(" Secure key size: %lu bytes\n", keysize);
@@ -1871,11 +2001,12 @@ static int command_validate(void)
keysize > SECURE_KEY_SIZE ? "Yes" : "No");
if (is_valid) {
printf(" Clear key size: %lu bits\n", clear_keysize);
- printf(" Enciphered with: %s CCA master key\n",
- is_old_mk ? "OLD" : "CURRENT");
+ printf(" Enciphered with: %s CCA master key (MKVP: "
+ "%016llx)\n", is_old_mk ? "OLD" : "CURRENT", mkvp);
} else {
printf(" Clear key size: (unknown)\n");
- printf(" Enciphered with: (unknown)\n");
+ printf(" Enciphered with: (unknown, MKVP: %016llx)\n",
+ mkvp);
}
if (vp_tok_avail)
print_verification_pattern(vp_tok.verification_pattern);
@@ -2014,12 +2145,13 @@ static int command_setkey(void)
util_asprintf(&msg, "The secure key in file '%s' is "
"enciphered with the CCA master key in the OLD "
"master key register. Do you want to set this "
- "key as the new volume key anyway?",
+ "key as the new volume key anyway [y/N]?",
g.master_key_file);
util_print_indented(msg, 0);
free(msg);
if (!prompt_for_yes()) {
+ warnx("Device '%s' is left unchanged", g.pos_arg);
rc = -EINVAL;
goto out;
}
@@ -2078,12 +2210,13 @@ static int command_setkey(void)
"be correct. You will lose all data on the "
"volume if you set the wrong volume key!\n"
"Are you sure that the key in file '%s' is the "
- "correct volume key for volume '%s'?",
+ "correct volume key for volume '%s' [y/N]?",
g.master_key_file, g.pos_arg);
util_print_indented(msg, 0);
free(msg);
if (!prompt_for_yes()) {
+ warnx("Device '%s' is left unchanged", g.pos_arg);
rc = -EINVAL;
goto out;
}
@@ -2204,6 +2337,12 @@ int main(int argc, char *argv[])
if (c == -1)
break;
switch (c) {
+ case 'N':
+ g.tonew = 1;
+ break;
+ case 'O':
+ g.fromold = 1;
+ break;
case 'c':
g.complete = 1;
break;
@@ -2286,8 +2425,7 @@ int main(int argc, char *argv[])
}
if (command->need_cca_library) {
- rc = load_cca_library(&g.lib_csulcca, &g.dll_CSNBKTC,
- g.verbose);
+ rc = load_cca_library(&g.cca, g.verbose);
if (rc != 0) {
rc = EXIT_FAILURE;
goto out;
@@ -2329,8 +2467,8 @@ int main(int argc, char *argv[])
rc = command->function();
out:
- if (g.lib_csulcca)
- dlclose(g.lib_csulcca);
+ if (g.cca.lib_csulcca)
+ dlclose(g.cca.lib_csulcca);
if (g.pkey_fd >= 0)
close(g.pkey_fd);
if (g.cd)
diff --git a/zkey/zkey.c b/zkey/zkey.c
index 2ecbb90..a0dbc0b 100644
--- a/zkey/zkey.c
+++ b/zkey/zkey.c
@@ -27,9 +27,11 @@
#include "lib/util_prg.h"
#include "lib/zt_common.h"
+#include "cca.h"
#include "keystore.h"
#include "misc.h"
#include "pkey.h"
+#include "utils.h"
/*
* Program configuration
@@ -80,8 +82,7 @@ static struct zkey_globals {
bool force;
bool open;
bool format;
- void *lib_csulcca;
- t_CSNBKTC dll_CSNBKTC;
+ struct cca_lib cca;
int pkey_fd;
struct keystore *keystore;
} g = {
@@ -832,7 +833,7 @@ static struct zkey_command zkey_commands[] = {
.need_pkey_device = 1,
.short_desc = "Validate an existing secure AES key",
.long_desc = "Validate an existing secure AES key that is "
- "either contained in SECURE-KEY-FILE or is stored"
+ "either contained in SECURE-KEY-FILE or is stored "
"in the repository and print information about "
"the key",
.has_options = 1,
@@ -1060,6 +1061,8 @@ static int command_generate_repository(void)
*/
static int command_generate(void)
{
+ int rc;
+
if (g.pos_arg != NULL && g.name != NULL) {
warnx(" Option '--name|-N' is not valid for generating a key "
"outside of the repository");
@@ -1067,7 +1070,7 @@ static int command_generate(void)
return EXIT_FAILURE;
}
if (g.apqns == NULL && g.noapqncheck) {
- warnx("Option '--noapqncheck' is only valid together with "
+ warnx("Option '--no-apqn-check' is only valid together with "
"the '--apqns|-a' option");
util_prg_print_parse_error();
return EXIT_FAILURE;
@@ -1088,7 +1091,7 @@ static int command_generate(void)
return EXIT_FAILURE;
}
if (g.noapqncheck) {
- warnx("Option '--noapqncheck' is not valid for "
+ warnx("Option '--no-apqn-check' is not valid for "
"generating a key outside of the repository");
util_prg_print_parse_error();
return EXIT_FAILURE;
@@ -1100,6 +1103,14 @@ static int command_generate(void)
return EXIT_FAILURE;
}
+ rc = cross_check_apqns(NULL, 0, true, g.verbose);
+ if (rc == -EINVAL)
+ return EXIT_FAILURE;
+ if (rc != 0 && rc != -ENOTSUP) {
+ warnx("Your master key setup is improper");
+ return EXIT_FAILURE;
+ }
+
return g.clearkeyfile ? command_generate_clear()
: command_generate_random();
}
@@ -1117,7 +1128,9 @@ static int command_reencipher_file(void)
{
size_t secure_key_size;
int rc, is_old_mk;
+ int selected = 1;
u8 *secure_key;
+ u64 mkvp;
if (g.name != NULL) {
warnx("Option '--name|-N' is not valid for "
@@ -1163,6 +1176,15 @@ static int command_reencipher_file(void)
goto out;
}
+ rc = get_master_key_verification_pattern(secure_key, secure_key_size,
+ &mkvp, g.verbose);
+ if (rc != 0) {
+ warnx("Failed to get the master key verification pattern: %s",
+ strerror(-rc));
+ rc = EXIT_FAILURE;
+ goto out;
+ }
+
if (!g.fromold && !g.tonew) {
/* Autodetect reencipher option */
if (is_old_mk) {
@@ -1194,13 +1216,28 @@ static int command_reencipher_file(void)
pr_verbose("Secure key will be re-enciphered from OLD to the "
"CURRENT CCA master key");
- rc = key_token_change(g.dll_CSNBKTC,
- secure_key, secure_key_size,
+ rc = select_cca_adapter_by_mkvp(&g.cca, mkvp, NULL,
+ FLAG_SEL_CCA_MATCH_OLD_MKVP,
+ g.verbose);
+ if (rc == -ENOTSUP) {
+ rc = 0;
+ selected = 0;
+ }
+ if (rc != 0) {
+ warnx("No APQN found that is suitable for "
+ "re-enciphering the secure AES volume key");
+ rc = EXIT_FAILURE;
+ goto out;
+ }
+
+ rc = key_token_change(&g.cca, secure_key, secure_key_size,
METHOD_OLD_TO_CURRENT,
g.verbose);
if (rc != 0) {
warnx("Re-encipher from OLD to CURRENT CCA "
- "master key has failed");
+ "master key has failed\n");
+ if (!selected)
+ print_msg_for_cca_envvars("secure AES key");
rc = EXIT_FAILURE;
goto out;
}
@@ -1209,12 +1246,30 @@ static int command_reencipher_file(void)
pr_verbose("Secure key will be re-enciphered from CURRENT "
"to the NEW CCA master key");
- rc = key_token_change(g.dll_CSNBKTC,
- secure_key, secure_key_size,
+ rc = select_cca_adapter_by_mkvp(&g.cca, mkvp, NULL,
+ FLAG_SEL_CCA_MATCH_CUR_MKVP |
+ FLAG_SEL_CCA_NEW_MUST_BE_SET,
+ g.verbose);
+ if (rc == -ENOTSUP) {
+ rc = 0;
+ selected = 0;
+ }
+ if (rc != 0) {
+ util_print_indented("No APQN found that is suitable "
+ "for re-enciphering this secure "
+ "AES key and has the NEW master "
+ "key loaded", 0);
+ rc = EXIT_FAILURE;
+ goto out;
+ }
+
+ rc = key_token_change(&g.cca, secure_key, secure_key_size,
METHOD_CURRENT_TO_NEW, g.verbose);
if (rc != 0) {
warnx("Re-encipher from CURRENT to NEW CCA "
- "master key has failed");
+ "master key has failed\n");
+ if (!selected)
+ print_msg_for_cca_envvars("secure AES key");
rc = EXIT_FAILURE;
goto out;
}
@@ -1270,7 +1325,7 @@ static int command_reencipher_repository(void)
rc = keystore_reencipher_key(g.keystore, g.name, g.apqns, g.fromold,
g.tonew, g.inplace, g.staged, g.complete,
- g.pkey_fd, g.dll_CSNBKTC);
+ g.pkey_fd, &g.cca);
return rc != 0 ? EXIT_FAILURE : EXIT_SUCCESS;
}
@@ -1302,6 +1357,7 @@ static int command_validate_file(void)
size_t clear_key_size;
u8 *secure_key;
int is_old_mk;
+ u64 mkvp;
int rc;
if (g.name != NULL) {
@@ -1317,7 +1373,7 @@ static int command_validate_file(void)
return EXIT_FAILURE;
}
if (g.noapqncheck) {
- warnx("Option '--noapqncheck' is not valid for "
+ warnx("Option '--no-apqn-check' is not valid for "
"validating a key outside of the repository");
util_prg_print_parse_error();
return EXIT_FAILURE;
@@ -1348,19 +1404,37 @@ static int command_validate_file(void)
goto out;
}
+ rc = get_master_key_verification_pattern(secure_key, secure_key_size,
+ &mkvp, g.verbose);
+ if (rc != 0) {
+ warnx("Failed to get the master key verification pattern: %s",
+ strerror(-rc));
+ rc = EXIT_FAILURE;
+ goto out;
+ }
+
printf("Validation of secure key in file '%s':\n", g.pos_arg);
printf(" Status: Valid\n");
printf(" Secure key size: %lu bytes\n", secure_key_size);
printf(" Clear key size: %lu bits\n", clear_key_size);
printf(" XTS type key: %s\n",
secure_key_size > SECURE_KEY_SIZE ? "Yes" : "No");
- printf(" Enciphered with: %s CCA master key\n",
- is_old_mk ? "OLD" : "CURRENT");
+ printf(" Enciphered with: %s CCA master key (MKVP: %016llx)\n",
+ is_old_mk ? "OLD" : "CURRENT", mkvp);
printf(" Verification pattern: %.*s\n", VERIFICATION_PATTERN_LEN / 2,
vp);
printf(" %.*s\n", VERIFICATION_PATTERN_LEN / 2,
&vp[VERIFICATION_PATTERN_LEN / 2]);
+ rc = cross_check_apqns(NULL, mkvp, true, g.verbose);
+ if (rc == -EINVAL)
+ return EXIT_FAILURE;
+ if (rc != 0 && rc != -ENOTSUP) {
+ warnx("Your master key setup is improper");
+ rc = EXIT_FAILURE;
+ goto out;
+ }
+
out:
free(secure_key);
return rc;
@@ -1375,13 +1449,6 @@ static int command_validate_repository(void)
{
int rc;
- if (g.apqns == NULL && g.noapqncheck) {
- warnx("Option '--noapqncheck' is only valid together with "
- "the '--apqns|-a' option");
- util_prg_print_parse_error();
- return EXIT_FAILURE;
- }
-
rc = keystore_validate_key(g.keystore, g.name, g.apqns, g.noapqncheck,
g.pkey_fd);
@@ -1421,7 +1488,7 @@ static int command_import(void)
g.sector_size = 0;
if (g.apqns == NULL && g.noapqncheck) {
- warnx("Option '--noapqncheck' is only valid together with "
+ warnx("Option '--no-apqn-check' is only valid together with "
"the '--apqns|-a' option");
util_prg_print_parse_error();
return EXIT_FAILURE;
@@ -1501,7 +1568,7 @@ static int command_change(void)
return EXIT_FAILURE;
}
if (g.apqns == NULL && g.noapqncheck) {
- warnx("Option '--noapqncheck' is only valid together with "
+ warnx("Option '--no-apqn-check' is only valid together with "
"the '--apqns|-a' option");
util_prg_print_parse_error();
return EXIT_FAILURE;
@@ -1874,8 +1941,7 @@ int main(int argc, char *argv[])
}
if (command->need_cca_library) {
- rc = load_cca_library(&g.lib_csulcca, &g.dll_CSNBKTC,
- g.verbose);
+ rc = load_cca_library(&g.cca, g.verbose);
if (rc != 0) {
rc = EXIT_FAILURE;
goto out;
@@ -1894,8 +1960,8 @@ int main(int argc, char *argv[])
rc = command->function();
out:
- if (g.lib_csulcca)
- dlclose(g.lib_csulcca);
+ if (g.cca.lib_csulcca)
+ dlclose(g.cca.lib_csulcca);
if (g.pkey_fd >= 0)
close(g.pkey_fd);
if (g.keystore)
--
2.21.3
From c5e94c1cf3b03d290752227d21540b8786df8131 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Dan=20Hor=C3=A1k?= <dan@danny.cz>
Date: Thu, 7 Nov 2019 13:23:55 +0100
Subject: [PATCH 35/44] zkey: Add support for CCA AES CIPHER keys (#1719623)
Description: With CCA 5 there is a new secure key type, the so called
variable length symmetric cipher key token. This token format
can hold AES keys with size 128, 192 and 256 bits together
with additional attributes cryptographic bound to the key
token. The attributes may limit the usage of the key, for
example restrict export or usability scope. So this key type
is considered to be even more secure than the traditional
secure key token. This key token type is also called "CCA
AES CIPHER key", where the formerly used key token is called
"CCA AES DATA key".
The zkey as well as the zkey-cryptsetup tools are enhanced
to support AES CIPHER keys. That is, zkey can manage AES DATA
keys, as well as AES CIPHER keys. The key type must be specified
at key generation time, the default is to generate AED DATA
keys.
Upstream-ID: 9de85f42951e0b1a3d083363d7000b1950aebcd7
Upstream-ID: 91c35543ca7fd25691487c61ec2e308f2903a6b8
Upstream-ID: b47007b8ac8b446eb94b06e7ed3050b3df3e80e8
Upstream-ID: 298fab68fee86cb9b1862d60ca274971d4c39638
Upstream-ID: ddde3f354f3506521877a4e2a6082c4d597629cb
Upstream-ID: d4027e6506963fbf995992e32490d56a6f7ea587
Upstream-ID: 663d362ff3b1036476bfce9e2563272bab087013
Upstream-ID: b56c74fe7b6100b9a2ef17b8847cd850309cf487
Upstream-ID: 0fab6bdf2aa01e093f8a4f3d86c9183889a587fe
Upstream-ID: 560b672bfad3c7bb6631ed4e1676a8b2c836e030
Upstream-ID: b7bb90c552f9b62c0b4ddc1295e76769149ee6bb
Upstream-ID: b0cc0e47378de9cd82b0cd14228b26be4d615ffc
Upstream-ID: 7d4b1e18b6195f48414f42b4655f900872fed1e7
Upstream-ID: e7d79d5c5c0928c1bdbd6b669a6e70b8fd3352a5
Upstream-ID: 7fede7021ece58b9960532e17d963f844fe0de02
Upstream-ID: 0d9e42264db9935e28f663802c5b95795af79160
Upstream-ID: a86e41a51827b524c5f88db5e24282166df9b3c8
Upstream-ID: bc987c8d18ddeb6fec46113a7fe7588555b592e7
Upstream-ID: 9894e391ef26fd31e2995f19965b7ce5bf006caa
---
zkey/cca.c | 385 ++++++++++-
zkey/cca.h | 51 ++
zkey/keystore.c | 605 +++++++++++++-----
zkey/keystore.h | 11 +-
zkey/pkey.c | 1385 ++++++++++++++++++++++++++++++++++------
zkey/pkey.h | 193 +++++-
zkey/utils.c | 104 ++-
zkey/utils.h | 8 +-
zkey/zkey-cryptsetup.c | 44 +-
zkey/zkey.1 | 142 +++-
zkey/zkey.c | 308 ++++++++-
11 files changed, 2809 insertions(+), 427 deletions(-)
diff --git a/zkey/cca.c b/zkey/cca.c
index 2669c75..01f7bfd 100644
--- a/zkey/cca.c
+++ b/zkey/cca.c
@@ -55,6 +55,14 @@ static void print_CCA_error(int return_code, int reason_code)
warnx("The secure key has a CCA master key "
"verification pattern that is not valid");
break;
+ case 90:
+ warnx("The operation has been rejected due to access "
+ "control checking");
+ break;
+ case 2143:
+ warnx("The operation has been rejected due to key "
+ "export restrictions of the secure key");
+ break;
}
break;
case 12:
@@ -142,6 +150,9 @@ int load_cca_library(struct cca_lib *cca, bool verbose)
/* Get the Key Token Change function */
cca->dll_CSNBKTC = (t_CSNBKTC)dlsym(cca->lib_csulcca, "CSNBKTC");
+ /* Get the Key Token Change 2 function */
+ cca->dll_CSNBKTC2 = (t_CSNBKTC2)dlsym(cca->lib_csulcca, "CSNBKTC2");
+
/* Get the Cryptographic Facility Query function */
cca->dll_CSUACFQ = (t_CSUACFQ)dlsym(cca->lib_csulcca, "CSUACFQ");
@@ -151,11 +162,20 @@ int load_cca_library(struct cca_lib *cca, bool verbose)
/* Cryptographic Resource Deallocate function */
cca->dll_CSUACRD = (t_CSUACRD)dlsym(cca->lib_csulcca, "CSUACRD");
+ /* Get the Key Translate 2 function */
+ cca->dll_CSNBKTR2 = (t_CSNBKTR2)dlsym(cca->lib_csulcca, "CSNBKTR2");
+
+ /* Get the Restrict Key Attribute function */
+ cca->dll_CSNBRKA = (t_CSNBRKA)dlsym(cca->lib_csulcca, "CSNBRKA");
+
if (cca->dll_CSUACFV == NULL ||
cca->dll_CSNBKTC == NULL ||
+ cca->dll_CSNBKTC2 == NULL ||
cca->dll_CSUACFQ == NULL ||
cca->dll_CSUACRA == NULL ||
- cca->dll_CSUACRD == NULL) {
+ cca->dll_CSUACRD == NULL ||
+ cca->dll_CSNBKTR2 == NULL ||
+ cca->dll_CSNBRKA == NULL) {
pr_verbose(verbose, "%s", dlerror());
warnx("The command requires the IBM CCA Host Libraries and "
"Tools.\nFor the supported environments and downloads, "
@@ -187,10 +207,13 @@ int key_token_change(struct cca_lib *cca,
u8 *secure_key, unsigned int secure_key_size,
char *method, bool verbose)
{
+ struct aescipherkeytoken *cipherkey =
+ (struct aescipherkeytoken *)secure_key;
long exit_data_len = 0, rule_array_count;
unsigned char rule_array[2 * 8] = { 0, };
unsigned char exit_data[4] = { 0, };
long return_code, reason_code;
+ long key_token_length;
util_assert(cca != NULL, "Internal error: cca is NULL");
util_assert(secure_key != NULL, "Internal error: secure_key is NULL");
@@ -202,33 +225,77 @@ int key_token_change(struct cca_lib *cca,
memcpy(rule_array + 8, "AES ", 8);
rule_array_count = 2;
- cca->dll_CSNBKTC(&return_code, &reason_code,
- &exit_data_len, exit_data,
- &rule_array_count, rule_array,
- secure_key);
-
- pr_verbose(verbose, "CSNBKTC (Key Token Change) with '%s' returned: "
- "return_code: %ld, reason_code: %ld", method, return_code,
- reason_code);
- if (return_code != 0) {
- print_CCA_error(return_code, reason_code);
- return -EIO;
- }
-
- if (secure_key_size == 2 * SECURE_KEY_SIZE) {
+ if (is_cca_aes_data_key(secure_key, secure_key_size)) {
cca->dll_CSNBKTC(&return_code, &reason_code,
&exit_data_len, exit_data,
&rule_array_count, rule_array,
- secure_key + SECURE_KEY_SIZE);
+ secure_key);
pr_verbose(verbose, "CSNBKTC (Key Token Change) with '%s' "
"returned: return_code: %ld, reason_code: %ld",
method, return_code, reason_code);
+ } else if (is_cca_aes_cipher_key(secure_key, secure_key_size)) {
+ key_token_length = cipherkey->length;
+ cca->dll_CSNBKTC2(&return_code, &reason_code,
+ &exit_data_len, exit_data,
+ &rule_array_count, rule_array,
+ &key_token_length,
+ (unsigned char *)cipherkey);
+
+ pr_verbose(verbose, "CSNBKTC2 (Key Token Change2) with '%s' "
+ "returned: return_code: %ld, reason_code: %ld",
+ method, return_code, reason_code);
+
+ pr_verbose(verbose, "key_token_length: %lu", key_token_length);
+ } else {
+ warnx("Invalid key type specified");
+ return -EINVAL;
+ }
+
+ if (return_code != 0) {
+ print_CCA_error(return_code, reason_code);
+ return -EIO;
+ }
+
+ if (is_xts_key(secure_key, secure_key_size)) {
+ if (is_cca_aes_data_key(secure_key, secure_key_size)) {
+ cca->dll_CSNBKTC(&return_code, &reason_code,
+ &exit_data_len, exit_data,
+ &rule_array_count, rule_array,
+ secure_key + AESDATA_KEY_SIZE);
+
+ pr_verbose(verbose, "CSNBKTC (Key Token Change) with "
+ "'%s' returned: return_code: %ld, "
+ "reason_code: %ld", method, return_code,
+ reason_code);
+ } else if (is_cca_aes_cipher_key(secure_key, secure_key_size)) {
+ cipherkey = (struct aescipherkeytoken *)(secure_key +
+ AESCIPHER_KEY_SIZE);
+ key_token_length = cipherkey->length;
+ cca->dll_CSNBKTC2(&return_code, &reason_code,
+ &exit_data_len, exit_data,
+ &rule_array_count, rule_array,
+ &key_token_length,
+ (unsigned char *)cipherkey);
+
+ pr_verbose(verbose, "CSNBKTC2 (Key Token Change2) with "
+ "'%s' returned: return_code: %ld, "
+ "reason_code: %ld", method, return_code,
+ reason_code);
+
+ pr_verbose(verbose, "key_token_length: %lu",
+ key_token_length);
+ } else {
+ warnx("Invalid key type specified");
+ return -EINVAL;
+ }
+
if (return_code != 0) {
print_CCA_error(return_code, reason_code);
return -EIO;
}
}
+
return 0;
}
@@ -422,6 +489,58 @@ static int get_cca_adapter_serialnr(struct cca_lib *cca, char serialnr[9],
return 0;
}
+/**
+ * Queries the firmware version of the current CCA adapter
+ *
+ * @param[in] cca the CCA library structure
+ * @param[out] version the struct where the version is returned
+ * @param[in] verbose if true, verbose messages are printed
+ *
+ * @returns 0 on success, a negative errno in case of an error.
+ */
+static int get_cca_adapter_version(struct cca_lib *cca,
+ struct cca_version *version,
+ bool verbose)
+{
+ long exit_data_len = 0, rule_array_count, verb_data_length = 0;
+ unsigned char rule_array[6 * 8] = { 0, };
+ unsigned char exit_data[4] = { 0, };
+ long return_code, reason_code;
+ char version_data[9];
+
+ util_assert(cca != NULL, "Internal error: cca is NULL");
+
+ memset(rule_array, 0, sizeof(rule_array));
+ memcpy(rule_array, "STATCCA ", 8);
+ rule_array_count = 1;
+
+ cca->dll_CSUACFQ(&return_code, &reason_code,
+ &exit_data_len, exit_data,
+ &rule_array_count, rule_array,
+ &verb_data_length, NULL);
+
+ pr_verbose(verbose, "CSUACFQ (Cryptographic Facility Query) returned: "
+ "return_code: %ld, reason_code: %ld", return_code,
+ reason_code);
+ if (return_code != 0) {
+ print_CCA_error(return_code, reason_code);
+ return -EIO;
+ }
+
+ memcpy(version_data, rule_array+3*8, 8);
+ version_data[8] = '\0';
+
+ pr_verbose(verbose, "CCA firmware version string: %s", version_data);
+
+ if (sscanf((char *)version_data, "%u.%u.%uz", &version->ver,
+ &version->rel, &version->mod) != 3) {
+ warnx("CCA formware version is invalid: %s", version_data);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
/**
* Selects the specified APQN to be used for the CCA host library.
*
@@ -626,3 +745,237 @@ void print_msg_for_cca_envvars(const char *key_name)
util_print_indented(msg, 0);
free(msg);
}
+
+/*
+ * Convert a secure key of type CCA-AESDATA into a secure key of type
+ * CCA-AESCIPHER.
+ *
+ * @param[in] cca the CCA library structure
+ * @param[in] input_key the secure key to convert
+ * @param[in] input_key_size the size of the secure key to convert
+ * @param[in] output_key buffer for the converted secure key
+ * @param[in/out] output_key_size on input: size of the output buffer.
+ * on exit: size of the converted secure key
+ * @param[in] verbose if true, verbose messages are printed
+ *
+ * @returns 0 on success, a negative errno in case of an error.
+ */
+int convert_aes_data_to_cipher_key(struct cca_lib *cca,
+ u8 *input_key, unsigned int input_key_size,
+ u8 *output_key,
+ unsigned int *output_key_size,
+ bool verbose)
+{
+ long input_token_size, output_token_size, zero = 0;
+ long exit_data_len = 0, rule_array_count = 0;
+ unsigned char *input_token, *output_token;
+ unsigned char rule_array[8 * 2] = { 0, };
+ unsigned char null_token[64] = { 0, };
+ long null_token_len = sizeof(null_token);
+ unsigned char exit_data[4] = { 0, };
+ struct aescipherkeytoken *cipherkey;
+ long return_code, reason_code;
+ struct cca_version version;
+ unsigned char buffer[800];
+ int rc;
+
+ util_assert(cca != NULL, "Internal error: cca is NULL");
+ util_assert(input_key != NULL, "Internal error: input_key is NULL");
+ util_assert(output_key != NULL, "Internal error: output_key is NULL");
+ util_assert(output_key_size != NULL,
+ "Internal error: output_key_size is NULL");
+
+ if (is_cca_aes_cipher_key(input_key, input_key_size)) {
+ warnx("Invalid key-type specified");
+ return -EINVAL;
+ }
+
+ if (*output_key_size < (is_xts_key(input_key, input_key_size) ?
+ 2 * AESCIPHER_KEY_SIZE : AESCIPHER_KEY_SIZE))
+ return -EINVAL;
+
+ /*
+ * We need a CCA firmware version 6.3.27 or later to support
+ * conversion of secure keys that are exportable to CPACF protected keys
+ */
+ rc = get_cca_adapter_version(cca, &version, verbose);
+ if (rc != 0)
+ return rc;
+ if (version.ver < 6 ||
+ (version.ver == 6 && version.rel < 3) ||
+ (version.ver == 6 && version.rel < 3 && version.mod < 27)) {
+ util_print_indented("The used CCA firmware version does not "
+ "support converting a secure key that can "
+ "be used with the PAES cipher. The "
+ "required CCA firmware version is 6.3.27 "
+ "or later. For the supported environments "
+ "and updates, see: " CCA_WEB_PAGE, 0);
+ return -ENOTSUP;
+ }
+
+ input_token = input_key;
+ input_token_size = AESDATA_KEY_SIZE;
+ output_token = buffer;
+ output_token_size = sizeof(buffer);
+ memset(buffer, 0, sizeof(buffer));
+
+ memcpy(rule_array, "AES ", 8);
+ memcpy(rule_array + 8, "REFORMAT", 8);
+ rule_array_count = 2;
+
+ cca->dll_CSNBKTR2(&return_code, &reason_code,
+ &exit_data_len, exit_data,
+ &rule_array_count, rule_array,
+ &input_token_size, input_token,
+ &null_token_len, null_token,
+ &zero, NULL,
+ &output_token_size, output_token);
+
+ pr_verbose(verbose, "CSNBKTR2 (Key Translate2) "
+ "returned: return_code: %ld, reason_code: %ld", return_code,
+ reason_code);
+ if (return_code != 0) {
+ print_CCA_error(return_code, reason_code);
+ return -EIO;
+ }
+
+ pr_verbose(verbose, "output_token_size: %lu", output_token_size);
+ if (output_token_size > (long)AESCIPHER_KEY_SIZE) {
+ pr_verbose(verbose, "Output key token too large");
+ return -EINVAL;
+ }
+
+ /*
+ * Check if the converted key allows export to CPACF protected key.
+ * If not, then the CCA host library or firmware code level is too low.
+ */
+ cipherkey = (struct aescipherkeytoken *)buffer;
+ if ((cipherkey->kmf1 & 0x0800) == 0) {
+ util_print_indented("The used CCA firmware version does not "
+ "support converting a secure key that can "
+ "be used with the PAES cipher. The "
+ "required CCA firmware version is 6.3.27 "
+ "or later. For the supported environments "
+ "and updates, see: " CCA_WEB_PAGE, 0);
+ return -ENOTSUP;
+ }
+
+ memset(output_key, 0, *output_key_size);
+ memcpy(output_key, buffer, output_token_size);
+ *output_key_size = AESCIPHER_KEY_SIZE;
+
+ if (is_xts_key(input_key, input_key_size)) {
+ input_token = input_key + AESDATA_KEY_SIZE;
+ input_token_size = AESDATA_KEY_SIZE;
+ output_token = buffer;
+ output_token_size = sizeof(buffer);
+ memset(buffer, 0, sizeof(buffer));
+
+ cca->dll_CSNBKTR2(&return_code, &reason_code,
+ &exit_data_len, exit_data,
+ &rule_array_count, rule_array,
+ &input_token_size, input_token,
+ &null_token_len, null_token,
+ &zero, NULL,
+ &output_token_size, output_token);
+
+ pr_verbose(verbose, "CSNBKTR2 (Key Translate2) "
+ "returned: return_code: %ld, reason_code: %ld",
+ return_code, reason_code);
+ if (return_code != 0) {
+ print_CCA_error(return_code, reason_code);
+ return -EIO;
+ }
+
+ pr_verbose(verbose, "output_token_size: %lu",
+ output_token_size);
+ if (output_token_size > (long)AESCIPHER_KEY_SIZE) {
+ pr_verbose(verbose, "Output key token too large");
+ return -EINVAL;
+ }
+
+ memcpy(output_key + AESCIPHER_KEY_SIZE, buffer,
+ output_token_size);
+ *output_key_size += AESCIPHER_KEY_SIZE;
+ }
+
+ return 0;
+}
+
+/*
+ * Restrict the exportability of an AES CIPHER key. It restricts export by means
+ * of NOEX-AES, NOEX-DES, NOEX-RSA, NOEX-SYM, NOEXUASY, NOEXAASY, NOEX-RAW
+ * keywords.
+ * When this function is called with an AES DATA key, it does nothing and
+ * returns 0. AES DATA keys can not be export restricted.
+ *
+ * @param[in] cca the CCA library structure
+ * @param[in] secure_key the secure key to restrict
+ * @param[in] secure_key_size the size of the secure key to restrict
+ * @param[in] verbose if true, verbose messages are printed
+ *
+ * @returns 0 on success, a negative errno in case of an error.
+ */
+int restrict_key_export(struct cca_lib *cca, u8 *secure_key,
+ unsigned int secure_key_size, bool verbose)
+{
+ struct aescipherkeytoken *cipherkey =
+ (struct aescipherkeytoken *)secure_key;
+ long exit_data_len = 0, rule_array_count = 0;
+ unsigned char rule_array[8 * 8] = { 0, };
+ unsigned char exit_data[4] = { 0, };
+ long return_code, reason_code;
+ long token_length, zero = 0;
+
+ util_assert(cca != NULL, "Internal error: cca is NULL");
+ util_assert(secure_key != NULL, "Internal error: secure_key is NULL");
+
+ if (!is_cca_aes_cipher_key(secure_key, secure_key_size))
+ return 0;
+
+ memcpy(rule_array, "AES ", 8);
+ memcpy(rule_array + 8, "NOEX-AES", 8);
+ memcpy(rule_array + 16, "NOEX-DES", 8);
+ memcpy(rule_array + 24, "NOEX-RSA", 8);
+ memcpy(rule_array + 32, "NOEX-SYM", 8);
+ memcpy(rule_array + 40, "NOEXUASY", 8);
+ memcpy(rule_array + 48, "NOEXAASY", 8);
+ memcpy(rule_array + 56, "NOEX-RAW", 8);
+ rule_array_count = 8;
+
+ token_length = cipherkey->length;
+ cca->dll_CSNBRKA(&return_code, &reason_code,
+ &exit_data_len, exit_data,
+ &rule_array_count, rule_array,
+ &token_length, (unsigned char *)secure_key,
+ &zero, NULL, &zero, NULL, &zero, NULL);
+
+ pr_verbose(verbose, "CSNBRKA (Restrict Key Attribute) "
+ "returned: return_code: %ld, reason_code: %ld", return_code,
+ reason_code);
+ if (return_code != 0) {
+ print_CCA_error(return_code, reason_code);
+ return -EIO;
+ }
+
+ if (is_xts_key(secure_key, secure_key_size)) {
+ cipherkey = (struct aescipherkeytoken *)(secure_key +
+ AESCIPHER_KEY_SIZE);
+ token_length = cipherkey->length;
+ cca->dll_CSNBRKA(&return_code, &reason_code,
+ &exit_data_len, exit_data,
+ &rule_array_count, rule_array,
+ &token_length, (unsigned char *)cipherkey,
+ &zero, NULL, &zero, NULL, &zero, NULL);
+
+ pr_verbose(verbose, "CSNBRKA (Restrict Key Attribute) "
+ "returned: return_code: %ld, reason_code: %ld",
+ return_code, reason_code);
+ if (return_code != 0) {
+ print_CCA_error(return_code, reason_code);
+ return -EIO;
+ }
+ }
+
+ return 0;
+}
diff --git a/zkey/cca.h b/zkey/cca.h
index a79e2ee..2b248ec 100644
--- a/zkey/cca.h
+++ b/zkey/cca.h
@@ -25,6 +25,15 @@ typedef void (*t_CSNBKTC)(long *return_code,
unsigned char *rule_array,
unsigned char *key_identifier);
+typedef void (*t_CSNBKTC2)(long *return_code,
+ long *reason_code,
+ long *exit_data_length,
+ unsigned char *exit_data,
+ long *rule_array_count,
+ unsigned char *rule_array,
+ long *key_identifier_length,
+ unsigned char *key_identifier);
+
typedef void (*t_CSUACFV)(long *return_code,
long *reason_code,
long *exit_data_length,
@@ -59,6 +68,36 @@ typedef void (*t_CSUACRD)(long *return_code,
long *ressource_name_length,
unsigned char *ressource_name);
+typedef void (*t_CSNBKTR2)(long *return_code,
+ long *reason_code,
+ long *exit_data_length,
+ unsigned char *exit_data,
+ long *rule_array_count,
+ unsigned char *rule_array,
+ long *input_key_token_length,
+ unsigned char *input_key_token,
+ long *input_KEK_key_identifier_length,
+ unsigned char *input_KEK_key_identifier,
+ long *output_KEK_key_identifier_length,
+ unsigned char *output_KEK_key_identifier,
+ long *output_key_token_length,
+ unsigned char *output_key_token);
+
+typedef void (*t_CSNBRKA)(long *return_code,
+ long *reason_code,
+ long *exit_data_length,
+ unsigned char *exit_data,
+ long *rule_array_count,
+ unsigned char *rule_array,
+ long *key_identifier_length,
+ unsigned char *key_identifier,
+ long *ey_encrypting_key_identifier_length,
+ unsigned char *ey_encrypting_key_identifier,
+ long *opt_parameter1_length,
+ unsigned char *opt_parameter1,
+ long *opt_parameter2_length,
+ unsigned char *opt_parameter2);
+
struct cca_version {
unsigned int ver;
unsigned int rel;
@@ -68,10 +107,13 @@ struct cca_version {
struct cca_lib {
void *lib_csulcca;
t_CSNBKTC dll_CSNBKTC;
+ t_CSNBKTC2 dll_CSNBKTC2;
t_CSUACFV dll_CSUACFV;
t_CSUACFQ dll_CSUACFQ;
t_CSUACRA dll_CSUACRA;
t_CSUACRD dll_CSUACRD;
+ t_CSNBKTR2 dll_CSNBKTR2;
+ t_CSNBRKA dll_CSNBRKA;
struct cca_version version;
};
@@ -92,4 +134,13 @@ int select_cca_adapter_by_mkvp(struct cca_lib *cca, u64 mkvp, const char *apqns,
void print_msg_for_cca_envvars(const char *key_name);
+int convert_aes_data_to_cipher_key(struct cca_lib *cca,
+ u8 *input_key, unsigned int input_key_size,
+ u8 *output_key,
+ unsigned int *output_key_size,
+ bool verbose);
+
+int restrict_key_export(struct cca_lib *cca, u8 *secure_key,
+ unsigned int secure_key_size, bool verbose);
+
#endif
diff --git a/zkey/keystore.c b/zkey/keystore.c
index 957ebb0..af67721 100644
--- a/zkey/keystore.c
+++ b/zkey/keystore.c
@@ -70,13 +70,12 @@ struct key_filenames {
#define DEFAULT_VOLUME_TYPE VOLUME_TYPE_PLAIN
#endif
-#define IS_XTS(secure_key_size) (secure_key_size > SECURE_KEY_SIZE ? 1 : 0)
-
#define REC_KEY "Key"
#define REC_DESCRIPTION "Description"
#define REC_SEC_KEY_SIZE "Secure key size"
#define REC_CLR_KEY_SIZE "Clear key size"
#define REC_XTS "XTS type key"
+#define REC_KEY_TYPE "Key type"
#define REC_VOLUMES "Volumes"
#define REC_APQNS "APQNs"
#define REC_KEY_FILE "Key file name"
@@ -313,6 +312,41 @@ static char *_keystore_get_volume_type(struct properties *properties)
return type;
}
+/**
+ * Returns the key type contained in the properties. If no key type
+ * property is contained, then 'CCA-AESDATA' is assumed (for backward
+ * compatibility).
+ *
+ * @returns a string containing the key type. Must be freed by the caller.
+ */
+static char *_keystore_get_key_type(struct properties *properties)
+{
+ char *type;
+
+ type = properties_get(properties, PROP_NAME_KEY_TYPE);
+ if (type == NULL)
+ type = util_strdup(KEY_TYPE_CCA_AESDATA);
+
+ return type;
+}
+
+/**
+ * Checks if the key type is supported.
+ *
+ * @param[in] key_type the key type
+ *
+ * @returns 1 if the key type is valid, 0 otherwise
+ */
+static int _keystore_valid_key_type(const char *key_type)
+{
+ if (strcasecmp(key_type, KEY_TYPE_CCA_AESDATA) == 0)
+ return 1;
+ if (strcasecmp(key_type, KEY_TYPE_CCA_AESCIPHER) == 0)
+ return 1;
+
+ return 0;
+}
+
/**
* Prints a message followed by a list of associated volumes, if volumes are
* associated and the volume-type matches (if specified)
@@ -816,6 +850,33 @@ static int _keystore_match_volume_type_property(struct properties *properties,
return rc;
}
+/**
+ * Checks if the key type property matches the specified key type.
+ * If the properties do not contain a key type property, then the default
+ * key type is assumed.
+ *
+ * @param[in] properties a properties object
+ * @param[in] key_type the key type to match. Can be NULL. In this case
+ * it always matches.
+ *
+ * @returns 1 for a match, 0 for not matched
+ */
+static int _keystore_match_key_type_property(struct properties *properties,
+ const char *key_type)
+{
+ char *type;
+ int rc = 0;
+
+ if (key_type == NULL)
+ return 1;
+
+ type = _keystore_get_key_type(properties);
+ if (strcasecmp(type, key_type) == 0)
+ rc = 1;
+
+ free(type);
+ return rc;
+}
/**
* Checks if a key name matches a name filter
@@ -881,6 +942,7 @@ typedef int (*process_key_t)(struct keystore *keystore,
* mutliple APQN filters separated by commas.
* NULL means no APQN filter.
* @param[in] volume_type If not NULL, specifies the volume type.
+ * @param[in] key_type The key type. NULL means no key type filter.
* @param[in] process_func the callback function called for a matching key
* @param[in/out] process_private private data passed to the process_func
*
@@ -893,6 +955,7 @@ static int _keystore_process_filtered(struct keystore *keystore,
const char *volume_filter,
const char *apqn_filter,
const char *volume_type,
+ const char *key_type,
process_key_t process_func,
void *process_private)
{
@@ -984,6 +1047,15 @@ static int _keystore_process_filtered(struct keystore *keystore,
goto free_prop;
}
+ rc = _keystore_match_key_type_property(key_props,
+ key_type);
+ if (rc == 0) {
+ pr_verbose(keystore,
+ "Key '%s' filtered out due to key type",
+ name);
+ goto free_prop;
+ }
+
rc = process_func(keystore, name, key_props, &file_names,
process_private);
if (rc != 0) {
@@ -1192,7 +1264,7 @@ static int _keystore_volume_check(const char *volume, bool remove, bool set,
info->set = set;
rc = _keystore_process_filtered(info->keystore, NULL, info->volume,
- NULL, NULL,
+ NULL, NULL, NULL,
_keystore_volume_check_process, info);
out:
free((void *)info->volume);
@@ -1368,7 +1440,7 @@ static int _keystore_generate_verification_pattern(struct keystore *keystore,
if (key == NULL)
return -EIO;
- rc = generate_key_verification_pattern((const char *)key, key_size,
+ rc = generate_key_verification_pattern(key, key_size,
vp, vp_len, keystore->verbose);
free(key);
@@ -1453,10 +1525,6 @@ static int _keystore_set_default_properties(struct properties *key_props)
{
int rc;
- rc = properties_set(key_props, PROP_NAME_KEY_TYPE, "CCA-AESDATA");
- if (rc != 0)
- return rc;
-
rc = properties_set(key_props, PROP_NAME_CIPHER, "paes");
if (rc != 0)
return rc;
@@ -1491,6 +1559,7 @@ static int _keystore_set_default_properties(struct properties *key_props)
* the sector size is not specified and the system
* default is used.
* @param[in] volume_type the type of volume
+ * @param[in] key_type the type of the key
*/
static int _keystore_create_info_file(struct keystore *keystore,
const char *name,
@@ -1499,7 +1568,8 @@ static int _keystore_create_info_file(struct keystore *keystore,
const char *volumes, const char *apqns,
bool noapqncheck,
size_t sector_size,
- const char *volume_type)
+ const char *volume_type,
+ const char *key_type)
{
struct volume_check vol_check = { .keystore = keystore, .name = name,
.set = 0 };
@@ -1521,6 +1591,12 @@ static int _keystore_create_info_file(struct keystore *keystore,
goto out;
}
+ rc = properties_set2(key_props, PROP_NAME_KEY_TYPE, key_type, true);
+ if (rc != 0) {
+ warnx("Invalid characters in key-type");
+ goto out;
+ }
+
rc = _keystore_change_association(key_props, PROP_NAME_VOLUMES,
volumes != NULL ? volumes : "",
"volume", _keystore_volume_check,
@@ -1590,52 +1666,6 @@ out:
return rc;
}
-/**
- * Extracts an online card/domain pair from the specified APQns. If none of the
- * specified APQNs are online, then -ENODEV is returned.
- * If no APQNs are specified at all, then it uses AUTOSELECT and returns zero.
- */
-static int _keystore_get_card_domain(const char *apqns, unsigned int *card,
- unsigned int *domain)
-{
- struct apqn_check apqn_check = { .noonlinecheck = 0, .nomsg = 1 };
- char **apqn_list;
- char *normalized = NULL;
- int rc = 0;
- int i;
-
- *card = AUTOSELECT;
- *domain = AUTOSELECT;
-
- if (apqns == NULL)
- return 0;
-
- apqn_list = str_list_split(apqns);
- if (apqn_list[0] == NULL)
- goto out;
-
- for (i = 0; apqn_list[i] != NULL; i++) {
- rc = _keystore_apqn_check(apqn_list[i], 0, 0, &normalized,
- &apqn_check);
- if (normalized != NULL)
- free(normalized);
- if (rc == -EINVAL)
- goto out;
- if (rc != 0)
- continue;
-
- if (sscanf(apqn_list[i], "%x.%x", card, domain) == 2)
- goto found;
- }
-
- warnx("None of the specified APQNs is online or of type CCA");
- rc = -ENODEV;
-found:
-out:
- str_list_free_string_array(apqn_list);
- return rc;
-}
-
/**
* Generates a secure key by random and adds it to the key store
*
@@ -1658,6 +1688,7 @@ out:
* clear key contained in the file denoted here.
* 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] pkey_fd the file descriptor of /dev/pkey
*
* @returns 0 for success or a negative errno in case of an error
@@ -1667,15 +1698,21 @@ int keystore_generate_key(struct keystore *keystore, const char *name,
const char *apqns, bool noapqncheck,
size_t sector_size, size_t keybits, bool xts,
const char *clear_key_file, const char *volume_type,
- int pkey_fd)
+ const char *key_type, int pkey_fd)
{
struct key_filenames file_names = { NULL, NULL, NULL };
struct properties *key_props = NULL;
- unsigned int card, domain;
+ char **apqn_list = NULL;
int rc;
util_assert(keystore != NULL, "Internal error: keystore is NULL");
util_assert(name != NULL, "Internal error: name is NULL");
+ util_assert(key_type != NULL, "Internal error: key_type is NULL");
+
+ if (!_keystore_valid_key_type(key_type)) {
+ warnx("Invalid key-type specified");
+ return -EINVAL;
+ }
rc = _keystore_get_key_filenames(keystore, name, &file_names);
if (rc != 0)
@@ -1685,7 +1722,9 @@ int keystore_generate_key(struct keystore *keystore, const char *name,
if (rc != 0)
goto out_free_key_filenames;
- rc = cross_check_apqns(apqns, 0, true, keystore->verbose);
+ rc = cross_check_apqns(apqns, 0,
+ get_min_card_level_for_keytype(key_type), true,
+ keystore->verbose);
if (rc == -EINVAL)
goto out_free_key_filenames;
if (rc != 0 && rc != -ENOTSUP && noapqncheck == 0) {
@@ -1693,20 +1732,21 @@ int keystore_generate_key(struct keystore *keystore, const char *name,
goto out_free_key_filenames;
}
- rc = _keystore_get_card_domain(apqns, &card, &domain);
- if (rc != 0)
- goto out_free_key_filenames;
+ if (apqns != NULL)
+ apqn_list = str_list_split(apqns);
if (clear_key_file == NULL)
rc = generate_secure_key_random(pkey_fd,
file_names.skey_filename,
- keybits, xts, card, domain,
+ keybits, xts, key_type,
+ (const char **)apqn_list,
keystore->verbose);
else
rc = generate_secure_key_clear(pkey_fd,
file_names.skey_filename,
keybits, xts, clear_key_file,
- card, domain,
+ key_type,
+ (const char **)apqn_list,
keystore->verbose);
if (rc != 0)
goto out_free_props;
@@ -1717,7 +1757,8 @@ int keystore_generate_key(struct keystore *keystore, const char *name,
rc = _keystore_create_info_file(keystore, name, &file_names,
description, volumes, apqns,
- noapqncheck, sector_size, volume_type);
+ noapqncheck, sector_size, volume_type,
+ key_type);
if (rc != 0)
goto out_free_props;
@@ -1727,6 +1768,8 @@ int keystore_generate_key(struct keystore *keystore, const char *name,
file_names.info_filename);
out_free_props:
+ if (apqn_list != NULL)
+ str_list_free_string_array(apqn_list);
if (key_props != NULL)
properties_free(key_props);
if (rc != 0)
@@ -1758,17 +1801,21 @@ out_free_key_filenames:
* 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] cca the CCA library struct
*
* @returns 0 for success or a negative errno in case of an error
*/
int keystore_import_key(struct keystore *keystore, const char *name,
const char *description, const char *volumes,
const char *apqns, bool noapqncheck, size_t sector_size,
- const char *import_file, const char *volume_type)
+ const char *import_file, const char *volume_type,
+ struct cca_lib *cca)
{
struct key_filenames file_names = { NULL, NULL, NULL };
struct properties *key_props = NULL;
size_t secure_key_size;
+ const char *key_type;
+ int selected = 1;
u8 *secure_key;
u64 mkvp;
int rc;
@@ -1792,6 +1839,14 @@ int keystore_import_key(struct keystore *keystore, const char *name,
goto out_free_key_filenames;
}
+ key_type = get_key_type(secure_key, secure_key_size);
+ if (key_type == NULL) {
+ warnx("Key '%s' is not a valid secure key", name);
+ free(secure_key);
+ rc = -EINVAL;
+ goto out_free_key_filenames;
+ }
+
rc = get_master_key_verification_pattern(secure_key, secure_key_size,
&mkvp, keystore->verbose);
if (rc != 0) {
@@ -1800,7 +1855,9 @@ int keystore_import_key(struct keystore *keystore, const char *name,
goto out_free_key;
}
- rc = cross_check_apqns(apqns, mkvp, true, keystore->verbose);
+ rc = cross_check_apqns(apqns, mkvp,
+ get_min_card_level_for_keytype(key_type), true,
+ keystore->verbose);
if (rc == -EINVAL)
goto out_free_key;
if (rc != 0 && rc != -ENOTSUP && noapqncheck == 0) {
@@ -1808,6 +1865,51 @@ int keystore_import_key(struct keystore *keystore, const char *name,
goto out_free_key;
}
+ if (is_cca_aes_cipher_key(secure_key, secure_key_size)) {
+ if (cca->lib_csulcca == NULL) {
+ rc = load_cca_library(cca, keystore->verbose);
+ if (rc != 0)
+ goto out_free_key;
+ }
+
+ rc = select_cca_adapter_by_mkvp(cca, mkvp, apqns,
+ FLAG_SEL_CCA_MATCH_CUR_MKVP |
+ FLAG_SEL_CCA_MATCH_OLD_MKVP,
+ keystore->verbose);
+ if (rc == -ENOTSUP) {
+ rc = 0;
+ selected = 0;
+ }
+ if (rc != 0) {
+ warnx("No APQN found that is suitable for "
+ "working with the secure AES key '%s'", name);
+ rc = 0;
+ goto out_free_key;
+ }
+
+ rc = restrict_key_export(cca, secure_key, secure_key_size,
+ keystore->verbose);
+ if (rc != 0) {
+ warnx("Failed to export-restrict the imported secure "
+ "key: %s", strerror(-rc));
+ if (!selected)
+ print_msg_for_cca_envvars("secure AES key");
+ goto out_free_key;
+ }
+
+ rc = check_aes_cipher_key(secure_key, secure_key_size);
+ if (rc != 0) {
+ warnx("The secure key to import might not be secure");
+ printf("%s: Do you want to import it anyway [y/N]? ",
+ program_invocation_short_name);
+ if (!prompt_for_yes(keystore->verbose)) {
+ warnx("Operation aborted");
+ rc = -ECANCELED;
+ goto out_free_key;
+ }
+ }
+ }
+
rc = write_secure_key(file_names.skey_filename, secure_key,
secure_key_size, keystore->verbose);
free(secure_key);
@@ -1821,7 +1923,8 @@ int keystore_import_key(struct keystore *keystore, const char *name,
rc = _keystore_create_info_file(keystore, name, &file_names,
description, volumes, apqns,
- noapqncheck, sector_size, volume_type);
+ noapqncheck, sector_size, volume_type,
+ key_type);
if (rc != 0)
goto out_free_props;
@@ -1886,8 +1989,8 @@ int keystore_change_key(struct keystore *keystore, const char *name,
.nomsg = 0 };
struct key_filenames file_names = { NULL, NULL, NULL };
struct properties *key_props = NULL;
+ char *apqns_prop, *key_type;
size_t secure_key_size;
- char *apqns_prop;
u8 *secure_key;
char temp[30];
u64 mkvp;
@@ -1954,9 +2057,12 @@ int keystore_change_key(struct keystore *keystore, const char *name,
goto out;
apqns_prop = properties_get(key_props, PROP_NAME_APQNS);
- rc = cross_check_apqns(apqns_prop, mkvp, true,
- keystore->verbose);
+ key_type = properties_get(key_props, PROP_NAME_KEY_TYPE);
+ rc = cross_check_apqns(apqns_prop, mkvp,
+ get_min_card_level_for_keytype(key_type),
+ true, keystore->verbose);
free(apqns_prop);
+ free(key_type);
if (rc == -ENOTSUP)
rc = 0;
if (rc != 0 && noapqncheck == 0) {
@@ -2140,6 +2246,7 @@ static struct util_rec *_keystore_setup_record(bool validation)
util_rec_def(rec, REC_CLR_KEY_SIZE, UTIL_REC_ALIGN_LEFT, 20,
REC_CLR_KEY_SIZE);
util_rec_def(rec, REC_XTS, UTIL_REC_ALIGN_LEFT, 3, REC_XTS);
+ util_rec_def(rec, REC_KEY_TYPE, UTIL_REC_ALIGN_LEFT, 54, REC_KEY_TYPE);
if (validation)
util_rec_def(rec, REC_MASTERKEY, UTIL_REC_ALIGN_LEFT, 54,
REC_MASTERKEY);
@@ -2165,7 +2272,7 @@ static void _keystore_print_record(struct util_rec *rec,
const char *name,
struct properties *properties,
bool validation, const char *skey_filename,
- size_t secure_key_size,
+ size_t secure_key_size, bool is_xts,
size_t clear_key_bitsize, bool valid,
bool is_old_mk, bool reenc_pending, u64 mkvp)
{
@@ -2178,6 +2285,7 @@ static void _keystore_print_record(struct util_rec *rec,
char *description;
char *volume_type;
char *reencipher;
+ char *key_type;
char *creation;
char *volumes;
char *change;
@@ -2212,6 +2320,7 @@ static void _keystore_print_record(struct util_rec *rec,
reencipher = properties_get(properties, PROP_NAME_REENC_TIME);
vp = properties_get(properties, PROP_NAME_KEY_VP);
volume_type = _keystore_get_volume_type(properties);
+ key_type = properties_get(properties, PROP_NAME_KEY_TYPE);
util_rec_set(rec, REC_KEY, name);
if (validation)
@@ -2219,13 +2328,13 @@ static void _keystore_print_record(struct util_rec *rec,
util_rec_set(rec, REC_DESCRIPTION,
description != NULL ? description : "");
util_rec_set(rec, REC_SEC_KEY_SIZE, "%lu bytes", secure_key_size);
- if (!validation || valid)
+ if ((!validation || valid) && clear_key_bitsize != 0)
util_rec_set(rec, REC_CLR_KEY_SIZE, "%lu bits",
clear_key_bitsize);
else
util_rec_set(rec, REC_CLR_KEY_SIZE, "(unknown)");
- util_rec_set(rec, REC_XTS,
- IS_XTS(secure_key_size) ? "Yes" : "No");
+ util_rec_set(rec, REC_XTS, is_xts ? "Yes" : "No");
+ util_rec_set(rec, REC_KEY_TYPE, key_type);
if (validation) {
if (valid)
util_rec_set(rec, REC_MASTERKEY,
@@ -2290,6 +2399,8 @@ static void _keystore_print_record(struct util_rec *rec,
free(vp);
if (volume_type != NULL)
free(volume_type);
+ if (key_type != NULL)
+ free(key_type);
}
struct validate_info {
@@ -2317,12 +2428,17 @@ static int _keystore_display_apqn_status(struct keystore *keystore,
{
int rc, warning = 0;
char *apqns;
+ char *key_type;
apqns = properties_get(properties, PROP_NAME_APQNS);
if (apqns == NULL)
return 0;
- rc = cross_check_apqns(apqns, mkvp, true, keystore->verbose);
+ apqns = properties_get(properties, PROP_NAME_APQNS);
+ key_type = properties_get(properties, PROP_NAME_KEY_TYPE);
+ rc = cross_check_apqns(apqns, mkvp,
+ get_min_card_level_for_keytype(key_type), true,
+ keystore->verbose);
if (rc != 0 && rc != -ENOTSUP)
warning = 1;
@@ -2330,6 +2446,7 @@ static int _keystore_display_apqn_status(struct keystore *keystore,
printf("\n");
free(apqns);
+ free(key_type);
return warning;
}
/**
@@ -2395,8 +2512,10 @@ static int _keystore_process_validate(struct keystore *keystore,
void *private)
{
struct validate_info *info = (struct validate_info *)private;
+ char **apqn_list = NULL;
size_t clear_key_bitsize;
size_t secure_key_size;
+ char *apqns = NULL;
u8 *secure_key;
int is_old_mk;
int rc, valid;
@@ -2413,9 +2532,13 @@ static int _keystore_process_validate(struct keystore *keystore,
goto out;
}
+ apqns = properties_get(properties, PROP_NAME_APQNS);
+ if (apqns != NULL)
+ apqn_list = str_list_split(apqns);
+
rc = validate_secure_key(info->pkey_fd, secure_key, secure_key_size,
&clear_key_bitsize, &is_old_mk,
- keystore->verbose);
+ (const char **)apqn_list, keystore->verbose);
if (rc != 0) {
valid = 0;
info->num_invalid++;
@@ -2433,6 +2556,7 @@ static int _keystore_process_validate(struct keystore *keystore,
_keystore_print_record(info->rec, name, properties, 1,
file_names->skey_filename, secure_key_size,
+ is_xts_key(secure_key, secure_key_size),
clear_key_bitsize, valid, is_old_mk,
_keystore_reencipher_key_exists(file_names),
mkvp);
@@ -2453,6 +2577,10 @@ static int _keystore_process_validate(struct keystore *keystore,
info->num_warnings++;
out:
+ if (apqns != NULL)
+ free(apqns);
+ if (apqn_list != NULL)
+ str_list_free_string_array(apqn_list);
if (rc != 0)
pr_verbose(keystore, "Failed to validate key '%s': %s",
name, strerror(-rc));
@@ -2491,7 +2619,7 @@ int keystore_validate_key(struct keystore *keystore, const char *name_filter,
info.num_warnings = 0;
rc = _keystore_process_filtered(keystore, name_filter, NULL,
- apqn_filter, NULL,
+ apqn_filter, NULL, NULL,
_keystore_process_validate, &info);
util_rec_free(rec);
@@ -2669,7 +2797,9 @@ static int _keystore_process_reencipher(struct keystore *keystore,
struct reencipher_params params = info->params;
size_t clear_key_bitsize;
size_t secure_key_size;
+ char **apqn_list = NULL;
u8 *secure_key = NULL;
+ char *apqns = NULL;
char *out_file;
int is_old_mk;
char *temp;
@@ -2706,9 +2836,13 @@ static int _keystore_process_reencipher(struct keystore *keystore,
goto out;
}
+ apqns = properties_get(properties, PROP_NAME_APQNS);
+ if (apqns != NULL)
+ apqn_list = str_list_split(apqns);
+
rc = validate_secure_key(info->pkey_fd, secure_key, secure_key_size,
&clear_key_bitsize, &is_old_mk,
- keystore->verbose);
+ (const char **)apqn_list, keystore->verbose);
if (rc != 0) {
if (params.complete) {
warnx("Key '%s' is not valid, re-enciphering is not "
@@ -2807,6 +2941,10 @@ static int _keystore_process_reencipher(struct keystore *keystore,
info->num_reenciphered++;
out:
+ if (apqns != NULL)
+ free(apqns);
+ if (apqn_list != NULL)
+ str_list_free_string_array(apqn_list);
if (secure_key != NULL)
free(secure_key);
@@ -2870,7 +3008,7 @@ int keystore_reencipher_key(struct keystore *keystore, const char *name_filter,
info.num_skipped = 0;
rc = _keystore_process_filtered(keystore, name_filter, NULL,
- apqn_filter, NULL,
+ apqn_filter, NULL, NULL,
_keystore_process_reencipher, &info);
if (rc != 0) {
@@ -3090,7 +3228,6 @@ static int _keystore_prompt_for_remove(struct keystore *keystore,
struct key_filenames *file_names)
{
struct properties *key_prop;
- char str[20];
char *msg;
int rc;
@@ -3108,14 +3245,7 @@ static int _keystore_prompt_for_remove(struct keystore *keystore,
printf("%s: Remove key '%s' [y/N]? ", program_invocation_short_name,
name);
- if (fgets(str, sizeof(str), stdin) == NULL) {
- rc = -EIO;
- goto out;
- }
- if (str[strlen(str) - 1] == '\n')
- str[strlen(str) - 1] = '\0';
- pr_verbose(keystore, "Prompt reply: '%s'", str);
- if (strcasecmp(str, "y") != 0 && strcasecmp(str, "yes") != 0) {
+ if (!prompt_for_yes(keystore->verbose)) {
warnx("Operation aborted");
rc = -ECANCELED;
goto out;
@@ -3205,29 +3335,29 @@ static int _keystore_display_key(struct keystore *keystore,
void *private)
{
struct util_rec *rec = (struct util_rec *)private;
- struct secaeskeytoken *secure_key;
- size_t secure_key_size;
+ u8 *secure_key;
+ size_t secure_key_size, clear_key_bitsize = 0;
int rc = 0;
- secure_key = (struct secaeskeytoken *)
- read_secure_key(file_names->skey_filename,
+ secure_key = read_secure_key(file_names->skey_filename,
&secure_key_size, keystore->verbose);
if (secure_key == NULL)
return -EIO;
- if (secure_key_size < SECURE_KEY_SIZE) {
+ if (secure_key_size < MIN_SECURE_KEY_SIZE) {
pr_verbose(keystore,
"Size of secure key is too small: %lu expected %lu",
- secure_key_size, SECURE_KEY_SIZE);
+ secure_key_size, MIN_SECURE_KEY_SIZE);
rc = -EIO;
goto out;
}
+ get_key_bit_size(secure_key, secure_key_size, &clear_key_bitsize);
+
_keystore_print_record(rec, name, properties, 0,
file_names->skey_filename, secure_key_size,
- IS_XTS(secure_key_size) ? secure_key->bitsize * 2
- : secure_key->bitsize,
- 0, 0,
+ is_xts_key(secure_key, secure_key_size),
+ clear_key_bitsize, 0, 0,
_keystore_reencipher_key_exists(file_names), 0);
out:
@@ -3251,12 +3381,13 @@ out:
* mutliple APQN filters separated by commas.
* NULL means no APQN filter.
* @param[in] volume_type The volume type. NULL means no volume type filter.
+ * @param[in] key_type The key type. NULL means no key type filter.
*
* @returns 0 for success or a negative errno in case of an error
*/
int keystore_list_keys(struct keystore *keystore, const char *name_filter,
const char *volume_filter, const char *apqn_filter,
- const char *volume_type)
+ const char *volume_type, const char *key_type)
{
struct util_rec *rec;
int rc;
@@ -3269,10 +3400,16 @@ int keystore_list_keys(struct keystore *keystore, const char *name_filter,
return -EINVAL;
}
+ if (key_type != NULL &&
+ !_keystore_valid_key_type(key_type)) {
+ warnx("Invalid key-type specified");
+ return -EINVAL;
+ }
+
rec = _keystore_setup_record(0);
rc = _keystore_process_filtered(keystore, name_filter, volume_filter,
- apqn_filter, volume_type,
+ apqn_filter, volume_type, key_type,
_keystore_display_key, rec);
util_rec_free(rec);
@@ -3582,37 +3719,6 @@ out:
return cipher_spec;
}
-/**
- * Returns the size of the secure key file
- *
- * @param[in] keystore the keystore
- * @param[in] skey_filename the file name of the secure key
- *
- * @returns the size of the secure key, or -1 in case of an error
- */
-static size_t _keystore_get_key_file_size(struct keystore *keystore,
- const char *skey_filename)
-{
- size_t secure_key_size;
- struct stat sb;
-
- if (stat(skey_filename, &sb)) {
- pr_verbose(keystore, "Key file '%s': %s",
- skey_filename, strerror(errno));
- return -1;
- }
-
- secure_key_size = sb.st_size;
- if (secure_key_size < SECURE_KEY_SIZE) {
- pr_verbose(keystore,
- "Size of secure key is too small: %lu expected %lu",
- secure_key_size, SECURE_KEY_SIZE);
- return -1;
- }
-
- return secure_key_size;
-}
-
/**
* Processing function for the cryptsetup and crypttab functions.
* Extracts the required information and calls the secondary processing function
@@ -3639,6 +3745,7 @@ static int _keystore_process_crypt(struct keystore *keystore,
size_t secure_key_size;
size_t sector_size = 0;
char *volumes = NULL;
+ u8 *secure_key = NULL;
char *dmname;
char *temp;
int rc = 0;
@@ -3646,18 +3753,14 @@ static int _keystore_process_crypt(struct keystore *keystore,
char *ch;
int i;
- secure_key_size = _keystore_get_key_file_size(keystore,
- file_names->skey_filename);
- if (secure_key_size < SECURE_KEY_SIZE) {
- pr_verbose(keystore,
- "Size of secure key is too small: %lu expected %lu",
- secure_key_size, SECURE_KEY_SIZE);
- rc = -EIO;
- goto out;
- }
+ secure_key = read_secure_key(file_names->skey_filename,
+ &secure_key_size, keystore->verbose);
+ if (secure_key == NULL)
+ return -EIO;
cipher_spec = _keystore_build_cipher_spec(properties,
- IS_XTS(secure_key_size));
+ is_xts_key(secure_key,
+ secure_key_size));
if (cipher_spec == NULL) {
rc = -EINVAL;
goto out;
@@ -3709,6 +3812,8 @@ out:
free(cipher_spec);
if (volume_type != NULL)
free(volume_type);
+ if (secure_key != NULL)
+ free(secure_key);
return rc;
}
@@ -3766,8 +3871,8 @@ int keystore_cryptsetup(struct keystore *keystore, const char *volume_filter,
info.process_func = _keystore_process_cryptsetup;
rc = _keystore_process_filtered(keystore, NULL, volume_filter, NULL,
- volume_type, _keystore_process_crypt,
- &info);
+ volume_type, NULL,
+ _keystore_process_crypt, &info);
str_list_free_string_array(info.volume_filter);
@@ -3827,8 +3932,8 @@ int keystore_crypttab(struct keystore *keystore, const char *volume_filter,
info.process_func = _keystore_process_crypttab;
rc = _keystore_process_filtered(keystore, NULL, volume_filter, NULL,
- volume_type, _keystore_process_crypt,
- &info);
+ volume_type, NULL,
+ _keystore_process_crypt, &info);
str_list_free_string_array(info.volume_filter);
@@ -3841,6 +3946,218 @@ int keystore_crypttab(struct keystore *keystore, const char *volume_filter,
return rc;
}
+/**
+ * Converts a secure keys in the keystore
+ *
+ * @param[in] keystore the key store
+ * @param[in] name the name of the key to convert
+ * @param[in] key_type the type of the key to convert it to
+ * @param[in] noapqncheck if true, the specified APQN(s) are not checked for
+ * existence and type.
+ * @param[in] pkey_fd the file descriptor of /dev/pkey
+ * @param[in] cca the CCA library struct
+ *
+ * @returns 0 for success or a negative errno in case of an error
+ */
+int keystore_convert_key(struct keystore *keystore, const char *name,
+ const char *key_type, bool noapqncheck, bool quiet,
+ int pkey_fd, struct cca_lib *cca)
+{
+ struct key_filenames file_names = { NULL, NULL, NULL };
+ u8 output_key[2 * MAX_SECURE_KEY_SIZE];
+ struct properties *properties = NULL;
+ int rc, min_level, selected = 1;
+ unsigned int output_key_size;
+ char *cur_key_type = NULL;
+ char **apqn_list = NULL;
+ size_t secure_key_size;
+ u8 *secure_key = NULL;
+ char *apqns = NULL;
+ char *temp;
+ u64 mkvp;
+
+ util_assert(keystore != NULL, "Internal error: keystore is NULL");
+ util_assert(name != NULL, "Internal error: name is NULL");
+
+ rc = _keystore_get_key_filenames(keystore, name, &file_names);
+ if (rc != 0)
+ goto out;
+
+ rc = _keystore_ensure_keyfiles_exist(&file_names, name);
+ if (rc != 0)
+ goto out;
+
+ properties = properties_new();
+ rc = properties_load(properties, file_names.info_filename, 1);
+ if (rc != 0) {
+ warnx("Key '%s' does not exist or is invalid", name);
+ goto out;
+ }
+
+ cur_key_type = _keystore_get_key_type(properties);
+ if (strcasecmp(cur_key_type, key_type) == 0) {
+ warnx("The secure key '%s' is already of type %s", name,
+ cur_key_type);
+ rc = 0;
+ goto out;
+ }
+ if (strcasecmp(cur_key_type, KEY_TYPE_CCA_AESDATA) != 0) {
+ warnx("Only secure keys of type %s can "
+ "be converted. The secure key '%s' is of type %s",
+ KEY_TYPE_CCA_AESDATA, name, cur_key_type);
+ rc = 0;
+ goto out;
+ }
+
+ secure_key = read_secure_key(file_names.skey_filename,
+ &secure_key_size, keystore->verbose);
+ if (secure_key == NULL) {
+ rc = -ENOENT;
+ goto out;
+ }
+
+ min_level = get_min_card_level_for_keytype(key_type);
+ if (min_level < 0) {
+ warnx("Invalid key-type specified: %s", key_type);
+ rc = -EINVAL;
+ goto out;
+ }
+
+ apqns = properties_get(properties, PROP_NAME_APQNS);
+ if (apqns != NULL)
+ apqn_list = str_list_split(apqns);
+
+ rc = cross_check_apqns(apqns, 0, min_level, true, keystore->verbose);
+ if (rc == -EINVAL)
+ goto out;
+ if (rc != 0 && rc != -ENOTSUP && !noapqncheck) {
+ warnx("Your master key setup is improper for converting key "
+ "'%s'", name);
+ goto out;
+ }
+
+ rc = validate_secure_key(pkey_fd, secure_key, secure_key_size,
+ NULL, NULL, (const char **)apqn_list,
+ keystore->verbose);
+ if (rc != 0)
+ goto out;
+
+ rc = get_master_key_verification_pattern(secure_key, secure_key_size,
+ &mkvp, keystore->verbose);
+ if (rc)
+ goto out;
+
+ rc = select_cca_adapter_by_mkvp(cca, mkvp, NULL,
+ FLAG_SEL_CCA_MATCH_CUR_MKVP,
+ keystore->verbose);
+ if (rc == -ENOTSUP) {
+ rc = 0;
+ selected = 0;
+ }
+ if (rc != 0) {
+ warnx("No APQN found that is suitable for "
+ "converting the secure AES key '%s'", name);
+ goto out;
+ }
+
+ if (!quiet) {
+ util_print_indented("ATTENTION: Converting a secure key is "
+ "irreversible, and might have an effect "
+ "on the volumes encrypted with it!", 0);
+ _keystore_msg_for_volumes("The following volumes are encrypted "
+ "with this key:", properties, NULL);
+ printf("%s: Convert key '%s [y/N]'? ",
+ program_invocation_short_name, name);
+ if (!prompt_for_yes(keystore->verbose)) {
+ warnx("Operation aborted");
+ rc = -ECANCELED;
+ goto out;
+ }
+ }
+
+ memset(output_key, 0, sizeof(output_key));
+ output_key_size = sizeof(output_key);
+ rc = convert_aes_data_to_cipher_key(cca, secure_key,
+ secure_key_size, output_key,
+ &output_key_size,
+ keystore->verbose);
+ if (rc != 0) {
+ warnx("Converting the secure key '%s' from %s to %s has failed",
+ name, KEY_TYPE_CCA_AESDATA, key_type);
+ if (!selected)
+ print_msg_for_cca_envvars("secure AES key");
+ goto out;
+ }
+
+ rc = restrict_key_export(cca, output_key, output_key_size,
+ keystore->verbose);
+ if (rc != 0) {
+ warnx("Export restricting the converted secure key '%s' has "
+ "failed", name);
+ if (!selected)
+ print_msg_for_cca_envvars("secure AES key");
+ goto out;
+ }
+
+ rc = properties_set2(properties, PROP_NAME_KEY_TYPE, key_type, true);
+ if (rc != 0) {
+ warnx("Invalid characters in key-type");
+ goto out;
+ }
+
+ rc = properties_save(properties, file_names.info_filename, 1);
+ if (rc != 0) {
+ pr_verbose(keystore,
+ "Failed to write key info file '%s': %s",
+ file_names.info_filename, strerror(-rc));
+ goto out;
+ }
+
+ rc = write_secure_key(file_names.skey_filename, output_key,
+ output_key_size, keystore->verbose);
+ if (rc != 0)
+ goto out;
+
+ pr_verbose(keystore, "Secure key '%s' was converted successfully",
+ name);
+
+ util_asprintf(&temp, "The following LUKS2 volumes are "
+ "encrypted with key '%s'. These volumes still contain "
+ "the secure AES volume key of type CCA-AESDATA. To "
+ "change the secure AES volume key in the LUKS2 header, "
+ "run command 'zkey-cryptsetup setkey <device> "
+ "--master-key-file %s':", name,
+ file_names.skey_filename);
+ _keystore_msg_for_volumes(temp, properties, VOLUME_TYPE_LUKS2);
+ free(temp);
+ util_asprintf(&temp, "The following plain mode volumes are "
+ "encrypted with key '%s'. You must adapt the crypttab "
+ "entries for this volumes and change the key size "
+ "parameter to 'size=%u' or run command 'zkey crypttab "
+ "--volumes <device>' for each volume to re-generate the "
+ "crypttab entries:", name, output_key_size * 8, name);
+ _keystore_msg_for_volumes(temp, properties, VOLUME_TYPE_PLAIN);
+ free(temp);
+
+out:
+ _keystore_free_key_filenames(&file_names);
+ if (properties != NULL)
+ properties_free(properties);
+ if (secure_key != NULL)
+ free(secure_key);
+ if (apqns != NULL)
+ free(apqns);
+ if (apqn_list != NULL)
+ str_list_free_string_array(apqn_list);
+ if (cur_key_type != NULL)
+ free(cur_key_type);
+
+ if (rc != 0)
+ pr_verbose(keystore, "Failed to convert key '%s': %s",
+ name, strerror(-rc));
+ return rc;
+}
+
/**
* Frees a keystore object
*
diff --git a/zkey/keystore.h b/zkey/keystore.h
index 8e888d1..b17a575 100644
--- a/zkey/keystore.h
+++ b/zkey/keystore.h
@@ -32,12 +32,13 @@ int keystore_generate_key(struct keystore *keystore, const char *name,
const char *apqns, bool noapqncheck,
size_t sector_size, size_t keybits, bool xts,
const char *clear_key_file, const char *volume_type,
- int pkey_fd);
+ const char *key_type, int pkey_fd);
int keystore_import_key(struct keystore *keystore, const char *name,
const char *description, const char *volumes,
const char *apqns, bool noapqncheck, size_t sector_size,
- const char *import_file, const char *volume_type);
+ const char *import_file, const char *volume_type,
+ struct cca_lib *cca);
int keystore_change_key(struct keystore *keystore, const char *name,
const char *description, const char *volumes,
@@ -68,7 +69,7 @@ int keystore_remove_key(struct keystore *keystore, const char *name,
int keystore_list_keys(struct keystore *keystore, const char *name_filter,
const char *volume_filter, const char *apqn_filter,
- const char *volume_type);
+ const char *volume_type, const char *key_type);
int keystore_cryptsetup(struct keystore *keystore, const char *volume_filter,
bool execute, const char *volume_type,
@@ -80,6 +81,10 @@ int keystore_crypttab(struct keystore *keystore, const char *volume_filter,
const char *volume_type, const char *keyfile,
size_t keyfile_offset, size_t keyfile_size, size_t tries);
+int keystore_convert_key(struct keystore *keystore, const char *name,
+ const char *key_type, bool noapqncheck, bool quiet,
+ int pkey_fd, struct cca_lib *cca);
+
void keystore_free(struct keystore *keystore);
diff --git a/zkey/pkey.c b/zkey/pkey.c
index 8471f3d..462f9fe 100644
--- a/zkey/pkey.c
+++ b/zkey/pkey.c
@@ -46,6 +46,8 @@
#define DEFAULT_KEYBITS 256
+#define INITIAL_APQN_ENTRIES 16
+
/**
* Opens the pkey device and returns its file descriptor.
*
@@ -98,10 +100,8 @@ u8 *read_secure_key(const char *keyfile, size_t *secure_key_size,
}
size = sb.st_size;
- if (size != SECURE_KEY_SIZE && size != 2*SECURE_KEY_SIZE) {
- warnx("File '%s' has an invalid size, %lu or %lu bytes "
- "expected", keyfile, SECURE_KEY_SIZE,
- 2 * SECURE_KEY_SIZE);
+ if (size < MIN_SECURE_KEY_SIZE || size > 2 * MAX_SECURE_KEY_SIZE) {
+ warnx("File '%s' has an invalid size: %lu", keyfile, size);
return NULL;
}
@@ -271,6 +271,615 @@ out:
return buf;
}
+/**
+ * Returns the PKEY_KEYTYPE_xxx value for the specified key size.
+ *
+ * @param[in] keysize the key size in bits
+ *
+ * @returns the PKEY_KEYTYPE_xxx value or 0 for an unknown key size
+ */
+static u32 keysize_to_keytype(enum pkey_key_size keysize)
+{
+ switch (keysize) {
+ case PKEY_SIZE_AES_128:
+ return PKEY_KEYTYPE_AES_128;
+ case PKEY_SIZE_AES_192:
+ return PKEY_KEYTYPE_AES_192;
+ case PKEY_SIZE_AES_256:
+ return PKEY_KEYTYPE_AES_256;
+ default:
+ return 0;
+ }
+}
+
+/**
+ * Returns the PKEY_SIZE_xxx value for the specified keybits.
+ *
+ * @param[in] keybits the key size in bits
+ *
+ * @returns thePKEY_SIZE_xxx value or 0 for an unknown key size
+ */
+static enum pkey_key_size keybits_to_keysize(u32 keybits)
+{
+ switch (keybits) {
+ case 128:
+ return PKEY_SIZE_AES_128;
+ case 192:
+ return PKEY_SIZE_AES_192;
+ case 256:
+ return PKEY_SIZE_AES_256;
+ default:
+ return PKEY_SIZE_UNKNOWN;
+ }
+}
+
+/*
+ * Wrapper for the PKEY_GENSECK/PKEY_GENSECK2 IOCTL to generate a secure
+ * key of any type by random. If the newer PKEY_GENSECK2 IOCTL is not supported
+ * by the pkey device, then it falls back to the older PKEY_GENSECK IOCTL
+ *
+ * @param[in] pkey_fd the pkey file descriptor
+ * @param[in/out] genseck info about key to generate
+ * @param[in] verbose if true, verbose messages are printed
+ *
+ * @returns 0 on success, a negative errno in case of an error
+ */
+static int pkey_genseck2(int pkey_fd, struct pkey_genseck2 *genseck2,
+ bool verbose)
+{
+ struct pkey_genseck genseck;
+ int rc;
+ u32 i;
+
+ util_assert(pkey_fd != -1, "Internal error: pkey_fd is -1");
+ util_assert(genseck2 != NULL, "Internal error: genseck2 is NULL");
+
+ rc = ioctl(pkey_fd, PKEY_GENSECK2, genseck2);
+ if (rc != 0 && errno != ENOTTY)
+ return -errno;
+ if (rc == 0)
+ return 0;
+
+ /* New IOCTL is not available, fall back to old one */
+ pr_verbose(verbose, "ioctl PKEY_GENSECK2 not supported, fall back to "
+ "PKEY_GENSECK");
+
+ if (genseck2->type != PKEY_TYPE_CCA_DATA) {
+ warnx("Key-type is not supported");
+ return -ENOTSUP;
+ }
+
+ if (genseck2->keylen < AESDATA_KEY_SIZE)
+ return -EINVAL;
+
+ memset(&genseck, 0, sizeof(genseck));
+
+ genseck.keytype = keysize_to_keytype(genseck2->size);
+ if (genseck.keytype == 0)
+ return -EINVAL;
+
+ for (i = 0; i < genseck2->apqn_entries; i++) {
+ genseck.cardnr = genseck2->apqns[i].card;
+ genseck.domain = genseck2->apqns[i].domain;
+
+ rc = ioctl(pkey_fd, PKEY_GENSECK, &genseck);
+ if (rc != 0)
+ continue;
+
+ memcpy(genseck2->key, &genseck.seckey.seckey, AESDATA_KEY_SIZE);
+ genseck2->keylen = AESDATA_KEY_SIZE;
+ return 0;
+ }
+
+ return -errno;
+}
+
+/*
+ * Wrapper for the PKEY_CLR2SECK/PKEY_CLR2SECK2 IOCTL to generate a secure
+ * key of any type from a clear key. If the newer PKEY_CLR2SECK2 IOCTL is not
+ * supported by the pkey device, then it falls back to the older PKEY_CLR2SECK
+ * IOCTL
+ *
+ * @param[in] pkey_fd the pkey file descriptor
+ * @param[in/out] clr2seck2 info about key to generate
+ * @param[in] verbose if true, verbose messages are printed
+ *
+ * @returns 0 on success, a negative errno in case of an error
+ */
+static int pkey_clr2seck2(int pkey_fd, struct pkey_clr2seck2 *clr2seck2,
+ bool verbose)
+{
+ struct pkey_clr2seck clr2seck;
+ int rc;
+ u32 i;
+
+ util_assert(pkey_fd != -1, "Internal error: pkey_fd is -1");
+ util_assert(clr2seck2 != NULL, "Internal error: clr2seck2 is NULL");
+
+ rc = ioctl(pkey_fd, PKEY_CLR2SECK2, clr2seck2);
+ if (rc != 0 && errno != ENOTTY)
+ return -errno;
+ if (rc == 0)
+ return 0;
+
+ /* New IOCTL is not available, fall back to old one */
+ pr_verbose(verbose, "ioctl PKEY_CLR2SECK2 not supported, fall back to "
+ "PKEY_CLR2SECK");
+
+ if (clr2seck2->type != PKEY_TYPE_CCA_DATA) {
+ warnx("Key-type is not supported");
+ return -ENOTSUP;
+ }
+
+ if (clr2seck2->keylen < AESDATA_KEY_SIZE)
+ return -EINVAL;
+
+ memset(&clr2seck, 0, sizeof(clr2seck));
+ clr2seck.clrkey = clr2seck2->clrkey;
+
+ clr2seck.keytype = keysize_to_keytype(clr2seck2->size);
+ if (clr2seck.keytype == 0)
+ return -EINVAL;
+
+ for (i = 0; i < clr2seck2->apqn_entries; i++) {
+ clr2seck.cardnr = clr2seck2->apqns[i].card;
+ clr2seck.domain = clr2seck2->apqns[i].domain;
+
+ rc = ioctl(pkey_fd, PKEY_CLR2SECK, &clr2seck);
+ if (rc != 0)
+ continue;
+
+ memcpy(clr2seck2->key, &clr2seck.seckey.seckey,
+ AESDATA_KEY_SIZE);
+ clr2seck2->keylen = AESDATA_KEY_SIZE;
+ return 0;
+ }
+
+ return -errno;
+}
+
+/*
+ * Wrapper for the PKEY_VERIFYKEY/PKEY_VERIFYKEY2 IOCTL to verify a secure
+ * key of any type. If the newer PKEY_VERIFYKEY2 IOCTL is not supported
+ * by the pkey device, then it falls back to the older PKEY_VERIFYKEY IOCTL
+ *
+ * @param[in] pkey_fd the pkey file descriptor
+ * @param[in/out] verifykey2 info about key to verify
+ * @param[in] verbose if true, verbose messages are printed
+ *
+ * @returns 0 on success, a negative errno in case of an error
+ */
+static int pkey_verifyseck2(int pkey_fd, struct pkey_verifykey2 *verifykey2,
+ bool verbose)
+{
+ struct pkey_verifykey verifykey;
+ int rc;
+
+ util_assert(pkey_fd != -1, "Internal error: pkey_fd is -1");
+ util_assert(verifykey2 != NULL, "Internal error: verifyseck2 is NULL");
+
+ rc = ioctl(pkey_fd, PKEY_VERIFYKEY2, verifykey2);
+ if (rc != 0 && errno != ENOTTY)
+ return -errno;
+ if (rc == 0)
+ return 0;
+
+ /* New IOCTL is not available, fall back to old one */
+ pr_verbose(verbose, "ioctl PKEY_VERIFYKEY2 not supported, fall back to "
+ "PKEY_VERIFYKEY");
+
+ if (!is_cca_aes_data_key(verifykey2->key, verifykey2->keylen))
+ return -ENODEV;
+
+ memset(&verifykey, 0, sizeof(verifykey));
+ memcpy(&verifykey.seckey, verifykey2->key, sizeof(verifykey.seckey));
+
+ /*
+ * Note: the old IOCTL does not support to check a specific card and
+ * domain. If falling back to the old IOCTL, this input is silently
+ * ignored, and all APQNs currently available in the system are used.
+ */
+ rc = ioctl(pkey_fd, PKEY_VERIFYKEY, &verifykey);
+ if (rc != 0)
+ return -errno;
+
+ if ((verifykey.attributes & PKEY_VERIFY_ATTR_AES) == 0)
+ return -ENODEV;
+
+ verifykey2->type = PKEY_TYPE_CCA_DATA;
+ verifykey2->cardnr = verifykey.cardnr;
+ verifykey2->domain = verifykey.domain;
+ verifykey2->size = keybits_to_keysize(verifykey.keysize);
+
+ if (verifykey.attributes & PKEY_VERIFY_ATTR_OLD_MKVP)
+ verifykey2->flags = PKEY_FLAGS_MATCH_ALT_MKVP;
+ else
+ verifykey2->flags = PKEY_FLAGS_MATCH_CUR_MKVP;
+
+ return 0;
+}
+
+/**
+ * Print a list of APQNs if verbose is set
+ */
+static void pr_verbose_apqn_list(bool verbose, struct pkey_apqn *list, u32 num)
+{
+ u32 i;
+
+ if (!verbose)
+ return;
+
+ for (i = 0; i < num ; i++)
+ warnx(" APQN: %02x.%04x", list[i].card, list[i].domain);
+}
+
+/**
+ * Filter a n array list of APQNs (struct pkey_apqn) by a list of APQN strings.
+ *
+ * @param[in] apqn_list a zero terminated array of pointers to C-strings
+ * @param[in/out] apqns A list of APQNs as array of struct pkey_apqn to
+ * filter. The list is modified during filtering.
+ * @param[in/out] apqn_entries Number of entries in the list of APQNs. The
+ * number is modified during filtering.
+ *
+ * @returns 0 on success, a negative errno in case of an error
+ */
+static int filter_apqn_list(const char **apqn_list, struct pkey_apqn **apqns,
+ u32 *apqn_entries)
+{
+ unsigned int count, i, k, card, domain;
+ struct pkey_apqn *list = *apqns;
+ bool found;
+
+ if (apqn_list == NULL)
+ return 0;
+
+ for (count = 0; apqn_list[count] != NULL; count++)
+ ;
+ if (count == 0)
+ return 0;
+
+ for (i = 0; i < *apqn_entries; i++) {
+ found = false;
+ for (k = 0; apqn_list[k] != NULL; k++) {
+ if (sscanf(apqn_list[k], "%x.%x", &card, &domain) != 2)
+ return -EINVAL;
+
+ if (list[i].card == card && list[i].domain == domain) {
+ found = true;
+ break;
+ }
+ }
+
+ if (!found) {
+ if (i < *apqn_entries - 1)
+ memmove(&list[i], &list[i+1],
+ (*apqn_entries - i - 1) *
+ sizeof(struct pkey_apqn));
+ (*apqn_entries)--;
+ i--;
+ }
+ }
+
+ return 0;
+}
+
+/**
+ * Build a list of APQNs in the form accepted by the pkey IOCTLs from the
+ * List of APQNs as zero terminated array of pointers to C-strings that
+ * are usable for the CCA-AESDATA key type.
+ *
+ * @param[in] apqn_list a zero terminated array of pointers to C-strings
+ * @param[out] apqns A list of APQNs as array of struct pkey_apqn. The
+ * list must be freed by the caller using free().
+ * @param[out] apqn_entries Number of entries in the list of APQNs
+ * @param[in] verbose if true, verbose messages are printed
+ *
+ * @returns 0 on success, a negative errno in case of an error
+ */
+static int build_apqn_list_for_aes_data(const char **apqn_list,
+ struct pkey_apqn **apqns,
+ u32 *apqn_entries, bool verbose)
+{
+ unsigned int card, domain, count = 0;
+ struct pkey_apqn *list = NULL;
+ u32 list_entries = 0;
+ int i;
+
+ pr_verbose(verbose, "Build a list of APQNs for CCA-AESDATA");
+
+ if (apqn_list != NULL)
+ for (count = 0; apqn_list[count] != NULL; count++)
+ ;
+
+ if (count > 0) {
+ list = util_malloc(count * sizeof(struct pkey_apqn));
+ list_entries = count;
+
+ for (i = 0; apqn_list[i] != NULL; i++) {
+ if (sscanf(apqn_list[i], "%x.%x", &card, &domain) != 2)
+ return -EINVAL;
+
+ list[i].card = card;
+ list[i].domain = domain;
+ }
+
+ } else {
+ /*
+ * Although the new pkey IOCTLs do not support APQN entries
+ * with ANY indication, build an ANY-list here. If we get here,
+ * then the new IOCTLs are not available, and it will fall back
+ * to the old IOCTL which do support ANY specifications.
+ */
+ list = util_malloc(sizeof(struct pkey_apqn));
+ list_entries = 1;
+
+ list[0].card = AUTOSELECT;
+ list[0].domain = AUTOSELECT;
+ }
+
+ *apqns = list;
+ *apqn_entries = list_entries;
+
+ pr_verbose(verbose, "%u APQNs found", list_entries);
+ pr_verbose_apqn_list(verbose, list, list_entries);
+ return 0;
+}
+
+/**
+ * Build a list of APQNs in the form accepted by the pkey IOCTLs from the
+ * List of APQNs as zero terminated array of pointers to C-strings that
+ * are usable for the specified key type.
+ *
+ * @param[in] pkey_fd the pkey file descriptor
+ * @param[in] type the key type
+ * @param[in] apqn_list a zero terminated array of pointers to C-strings
+ * @param[out] apqns A list of APQNs as array of struct pkey_apqn. The
+ * list must be freed by the caller using free().
+ * @param[out] apqn_entries Number of entries in the list of APQNs
+ * @param[in] verbose if true, verbose messages are printed
+ *
+ * @returns 0 on success, a negative errno in case of an error
+ */
+static int build_apqn_list_for_key_type(int pkey_fd, enum pkey_key_type type,
+ const char **apqn_list,
+ struct pkey_apqn **apqns,
+ u32 *apqn_entries, bool verbose)
+{
+ struct pkey_apqns4keytype apqns4keytype;
+ int rc;
+
+ util_assert(pkey_fd != -1, "Internal error: pkey_fd is -1");
+ util_assert(apqns != NULL, "Internal error: apqns is NULL");
+ util_assert(apqn_entries != NULL,
+ "Internal error: apqn_entries is NULL");
+
+ pr_verbose(verbose, "Build a list of APQNs for key type %d", type);
+
+ memset(&apqns4keytype, 0, sizeof(apqns4keytype));
+ apqns4keytype.type = type;
+ apqns4keytype.apqn_entries = INITIAL_APQN_ENTRIES;
+ apqns4keytype.apqns = (struct pkey_apqn *)util_malloc(
+ apqns4keytype.apqn_entries * sizeof(struct pkey_apqn));
+
+ do {
+ rc = ioctl(pkey_fd, PKEY_APQNS4KT, &apqns4keytype);
+ if (rc == 0)
+ break;
+ rc = -errno;
+ pr_verbose(verbose, "ioctl PKEY_APQNS4KT rc: %s",
+ strerror(-rc));
+
+ switch (rc) {
+ case -ENOSPC:
+ free(apqns4keytype.apqns);
+ apqns4keytype.apqns = (struct pkey_apqn *)
+ util_malloc(apqns4keytype.apqn_entries *
+ sizeof(struct pkey_apqn));
+ continue;
+ case -ENOTTY:
+ /*
+ * New IOCTL is not available: build the list
+ * manually (Key type CCA-AESDATA only)
+ */
+ free(apqns4keytype.apqns);
+
+ if (type != PKEY_TYPE_CCA_DATA)
+ return -ENOTSUP;
+
+ rc = build_apqn_list_for_aes_data(apqn_list, apqns,
+ apqn_entries,
+ verbose);
+ return rc;
+ default:
+ goto out;
+ }
+ } while (rc != 0);
+
+ if (apqns4keytype.apqn_entries == 0) {
+ pr_verbose(verbose, "No APQN available for key type %d", type);
+ rc = -ENODEV;
+ goto out;
+ }
+
+ rc = filter_apqn_list(apqn_list, &apqns4keytype.apqns,
+ &apqns4keytype.apqn_entries);
+ if (rc != 0)
+ goto out;
+
+ if (apqns4keytype.apqn_entries == 0) {
+ pr_verbose(verbose, "No APQN available for key type %d", type);
+ rc = -ENODEV;
+ goto out;
+ }
+
+ pr_verbose(verbose, "%u APQNs found", apqns4keytype.apqn_entries);
+ pr_verbose_apqn_list(verbose, apqns4keytype.apqns,
+ apqns4keytype.apqn_entries);
+
+out:
+ if (rc == 0) {
+ *apqns = apqns4keytype.apqns;
+ *apqn_entries = apqns4keytype.apqn_entries;
+ } else {
+ *apqns = NULL;
+ *apqn_entries = 0;
+ free(apqns4keytype.apqns);
+ }
+
+ return rc;
+}
+
+/**
+ * Build a list of APQNs in the form accepted by the pkey IOCTLs from the
+ * List of APQNs as zero terminated array of pointers to C-strings that are
+ * usable for the specufied key.
+ *
+ * @param[in] pkey_fd the pkey file descriptor
+ * @param[in] key the key
+ * @param[in] keylen the length of the key
+ * @param[in] flags PKEY_FLAGS_MATCH_xxx flags
+ * @param[in] apqn_list a zero terminated array of pointers to C-strings
+ * @param[out] apqns A list of APQNs as array of struct pkey_apqn. The
+ * list must be freed by the caller using free().
+ * @param[out] apqn_entries Number of entries in the list of APQNs
+ * @param[in] verbose if true, verbose messages are printed
+ *
+ * @returns 0 on success, a negative errno in case of an error
+ */
+static int build_apqn_list_for_key(int pkey_fd, u8 *key, u32 keylen, u32 flags,
+ const char **apqn_list,
+ struct pkey_apqn **apqns,
+ u32 *apqn_entries, bool verbose)
+{
+ struct pkey_apqns4key apqns4key;
+ u64 mkvp;
+ int rc;
+
+ util_assert(pkey_fd != -1, "Internal error: pkey_fd is -1");
+ util_assert(key != NULL, "Internal error: key is NULL");
+ util_assert(apqns != NULL, "Internal error: apqns is NULL");
+ util_assert(apqn_entries != NULL,
+ "Internal error: apqn_entries is NULL");
+
+ pr_verbose(verbose, "Build a list of APQNs for the key");
+
+ memset(&apqns4key, 0, sizeof(apqns4key));
+ apqns4key.key = key;
+ apqns4key.keylen = keylen;
+ apqns4key.flags = flags;
+ apqns4key.apqn_entries = INITIAL_APQN_ENTRIES;
+ apqns4key.apqns = (struct pkey_apqn *)util_malloc(
+ apqns4key.apqn_entries * sizeof(struct pkey_apqn));
+
+ do {
+ rc = ioctl(pkey_fd, PKEY_APQNS4K, &apqns4key);
+ if (rc == 0)
+ break;
+ rc = -errno;
+ pr_verbose(verbose, "ioctl PKEY_APQNS4K rc: %s", strerror(-rc));
+
+ switch (rc) {
+ case -ENOSPC:
+ free(apqns4key.apqns);
+ apqns4key.apqns = (struct pkey_apqn *)
+ util_malloc(apqns4key.apqn_entries *
+ sizeof(struct pkey_apqn));
+ continue;
+ case -ENOTTY:
+ /*
+ * New IOCTL is not available: build the list manually
+ * (Key type CCA-AESDATA only)
+ */
+ free(apqns4key.apqns);
+
+ if (!is_cca_aes_data_key(key, keylen))
+ return -ENOTSUP;
+
+ rc = get_master_key_verification_pattern(key, keylen,
+ &mkvp,
+ verbose);
+ if (rc != 0)
+ return rc;
+
+ rc = build_apqn_list_for_aes_data(apqn_list, apqns,
+ apqn_entries,
+ verbose);
+ return rc;
+ default:
+ goto out;
+ }
+ } while (rc != 0);
+
+ if (apqns4key.apqn_entries == 0) {
+ pr_verbose(verbose, "No APQN available for the key");
+ rc = -ENODEV;
+ goto out;
+ }
+
+ rc = filter_apqn_list(apqn_list, &apqns4key.apqns,
+ &apqns4key.apqn_entries);
+ if (rc != 0)
+ goto out;
+
+ if (apqns4key.apqn_entries == 0) {
+ pr_verbose(verbose, "No APQN available for the key");
+ rc = -ENODEV;
+ goto out;
+ }
+
+ pr_verbose(verbose, "%u APQNs found", apqns4key.apqn_entries);
+ pr_verbose_apqn_list(verbose, apqns4key.apqns, apqns4key.apqn_entries);
+
+out:
+ if (rc == 0) {
+ *apqns = apqns4key.apqns;
+ *apqn_entries = apqns4key.apqn_entries;
+ } else {
+ *apqns = NULL;
+ *apqn_entries = 0;
+ free(apqns4key.apqns);
+ }
+
+ return rc;
+}
+
+/**
+ * Convert the key type string into the pkey enumeration
+ *
+ * @param[in] key_type the type of the key
+ *
+ * @returns the pkey key type or 0 for an u known key type
+ */
+static enum pkey_key_type key_type_to_pkey_type(const char *key_type)
+{
+ if (strcasecmp(key_type, KEY_TYPE_CCA_AESDATA) == 0)
+ return PKEY_TYPE_CCA_DATA;
+ if (strcasecmp(key_type, KEY_TYPE_CCA_AESCIPHER) == 0)
+ return PKEY_TYPE_CCA_CIPHER;
+
+ return 0;
+}
+
+/**
+ * Return the size of a key blob for a specific type
+ *
+ * @param[in] type the type of the key
+ *
+ * @returns the size of the key or 0 for an invalid key type
+ */
+static size_t key_size_for_type(enum pkey_key_type type)
+{
+ switch (type) {
+ case PKEY_TYPE_CCA_DATA:
+ return AESDATA_KEY_SIZE;
+ case PKEY_TYPE_CCA_CIPHER:
+ return AESCIPHER_KEY_SIZE;
+ default:
+ return 0;
+ }
+}
+
/**
* Generate a secure key by random
*
@@ -278,80 +887,110 @@ out:
* @param[in] keyfile the file name of the secure key to generate
* @param[in] keybits the cryptographic size of the key in bits
* @param[in] xts if true an XTS key is generated
- * @param[in] card the card number to use (or AUTOSELECT)
- * @param[in] domain the domain number to use (or AUTOSELECT)
+ * @param[in] key_type the type of the key
+ * @param[in] apqns a zero terminated array of pointers to APQN-strings,
+ * or NULL for AUTOSELECT
* @param[in] verbose if true, verbose messages are printed
*
* @returns 0 on success, a negative errno in case of an error
*/
int generate_secure_key_random(int pkey_fd, const char *keyfile,
- size_t keybits, bool xts, u16 card, u16 domain,
- bool verbose)
+ size_t keybits, bool xts, const char *key_type,
+ const char **apqns, bool verbose)
{
- struct pkey_genseck gensec;
- size_t secure_key_size;
- u8 *secure_key;
+ struct pkey_genseck2 genseck2;
+ size_t secure_key_size, size;
+ u8 *secure_key = NULL;
int rc;
util_assert(pkey_fd != -1, "Internal error: pkey_fd is -1");
util_assert(keyfile != NULL, "Internal error: keyfile is NULL");
+ util_assert(key_type != NULL, "Internal error: key_type is NULL");
if (keybits == 0)
keybits = DEFAULT_KEYBITS;
- secure_key_size = DOUBLE_KEYSIZE_FOR_XTS(SECURE_KEY_SIZE, xts);
- secure_key = util_malloc(secure_key_size);
+ pr_verbose(verbose, "Generate secure key by random");
- pr_verbose(verbose, "Generate key on card %02x.%04x", card, domain);
+ memset(&genseck2, 0, sizeof(genseck2));
- gensec.cardnr = card;
- gensec.domain = domain;
- switch (keybits) {
- case 128:
- gensec.keytype = PKEY_KEYTYPE_AES_128;
- break;
- case 192:
- if (xts) {
- warnx("Invalid value for '--keybits'|'-c' "
- "for XTS: '%lu'", keybits);
- rc = -EINVAL;
- goto out;
- }
- gensec.keytype = PKEY_KEYTYPE_AES_192;
- break;
- case 256:
- gensec.keytype = PKEY_KEYTYPE_AES_256;
- break;
- default:
+ genseck2.type = key_type_to_pkey_type(key_type);
+ if (genseck2.type == 0) {
+ warnx("Key-type not supported; %s", key_type);
+ return -ENOTSUP;
+ }
+
+ genseck2.size = keybits_to_keysize(keybits);
+ if (genseck2.size == 0) {
warnx("Invalid value for '--keybits'/'-c': '%lu'", keybits);
- rc = -EINVAL;
- goto out;
+ return -EINVAL;
+ }
+ if (keybits == 192 && xts) {
+ warnx("Invalid value for '--keybits'|'-c' "
+ "for XTS: '%lu'", keybits);
+ return -EINVAL;
}
- rc = ioctl(pkey_fd, PKEY_GENSECK, &gensec);
- if (rc < 0) {
- rc = -errno;
- warnx("Failed to generate a secure key: %s", strerror(errno));
- warnx("Make sure that all available CCA crypto adapters are "
- "setup with the same master key");
- goto out;
+ rc = build_apqn_list_for_key_type(pkey_fd, genseck2.type, apqns,
+ &genseck2.apqns,
+ &genseck2.apqn_entries, verbose);
+ if (rc != 0) {
+ if (rc == -ENODEV || rc == -ENOTSUP)
+ warnx("No APQN is available that can generate a secure "
+ "key of type %s", key_type);
+ else
+ warnx("Failed to build a list of APQNs that can "
+ "generate a secure key of type %s: %s", key_type,
+ strerror(-rc));
+ return rc;
}
- memcpy(secure_key, &gensec.seckey, SECURE_KEY_SIZE);
+ size = key_size_for_type(genseck2.type);
+ secure_key_size = DOUBLE_KEYSIZE_FOR_XTS(size, xts);
+ secure_key = util_zalloc(secure_key_size);
+
+ genseck2.key = secure_key;
+ genseck2.keylen = size;
+
+ rc = pkey_genseck2(pkey_fd, &genseck2, verbose);
+ if (rc != 0) {
+ warnx("Failed to generate a secure key: %s", strerror(-rc));
+ goto out;
+ }
if (xts) {
- rc = ioctl(pkey_fd, PKEY_GENSECK, &gensec);
- if (rc < 0) {
- rc = -errno;
- warnx("Failed to generate a secure key: %s",
- strerror(errno));
- warnx("Make sure that all available CCA crypto "
- "adapters are setup with the same master key");
+ free(genseck2.apqns);
+ genseck2.apqns = NULL;
+ genseck2.apqn_entries = 0;
+
+ /*
+ * Ensure to generate 2nd key with an APQN that has the same
+ * master key that is used by the 1st key.
+ */
+ rc = build_apqn_list_for_key(pkey_fd, secure_key, size,
+ PKEY_FLAGS_MATCH_CUR_MKVP, apqns,
+ &genseck2.apqns,
+ &genseck2.apqn_entries, verbose);
+ if (rc != 0) {
+ if (rc == -ENODEV || rc == -ENOTSUP)
+ warnx("No APQN is available that can generate "
+ "a secure key of type %s", key_type);
+ else
+ warnx("Failed to build a list of APQNs that "
+ "can generate a secure key of type %s: "
+ "%s", key_type, strerror(-rc));
goto out;
}
- memcpy(secure_key + SECURE_KEY_SIZE, &gensec.seckey,
- SECURE_KEY_SIZE);
+ genseck2.key = secure_key + size;
+ genseck2.keylen = size;
+
+ rc = pkey_genseck2(pkey_fd, &genseck2, verbose);
+ if (rc != 0) {
+ warnx("Failed to generate a secure key: %s",
+ strerror(-rc));
+ goto out;
+ }
}
pr_verbose(verbose, "Successfully generated a secure key");
@@ -359,6 +998,7 @@ int generate_secure_key_random(int pkey_fd, const char *keyfile,
rc = write_secure_key(keyfile, secure_key, secure_key_size, verbose);
out:
+ free(genseck2.apqns);
free(secure_key);
return rc;
}
@@ -374,89 +1014,125 @@ out:
* determines the keybits.
* @param[in] xts if true an XTS key is generated
* @param[in] clearkeyfile the file name of the clear key to read
- * @param[in] card the card number to use (or AUTOSELECT)
- * @param[in] domain the domain number to use (or AUTOSELECT)
+ * @param[in] key_type the type of the key
+ * @param[in] apqns a zero terminated array of pointers to APQN-strings,
+ * or NULL for AUTOSELECT
* @param[in] verbose if true, verbose messages are printed
*
* @returns 0 on success, a negative errno in case of an error
*/
int generate_secure_key_clear(int pkey_fd, const char *keyfile,
size_t keybits, bool xts,
- const char *clearkeyfile,
- u16 card, u16 domain,
- bool verbose)
+ const char *clearkeyfile, const char *key_type,
+ const char **apqns, bool verbose)
{
- struct pkey_clr2seck clr2sec;
+ struct pkey_clr2seck2 clr2seck2;
size_t secure_key_size;
size_t clear_key_size;
u8 *secure_key;
u8 *clear_key;
+ size_t size;
int rc;
util_assert(pkey_fd != -1, "Internal error: pkey_fd is -1");
util_assert(keyfile != NULL, "Internal error: keyfile is NULL");
util_assert(clearkeyfile != NULL,
"Internal error: clearkeyfile is NULL");
+ util_assert(key_type != NULL, "Internal error: key_type is NULL");
- secure_key_size = DOUBLE_KEYSIZE_FOR_XTS(SECURE_KEY_SIZE, xts);
- secure_key = util_malloc(secure_key_size);
+ pr_verbose(verbose, "Generate secure key from a clear key");
clear_key = read_clear_key(clearkeyfile, keybits, xts, &clear_key_size,
verbose);
if (clear_key == NULL)
return -EINVAL;
- pr_verbose(verbose, "Generate key on card %02x.%04x", card, domain);
+ memset(&clr2seck2, 0, sizeof(clr2seck2));
- clr2sec.cardnr = card;
- clr2sec.domain = domain;
- switch (HALF_KEYSIZE_FOR_XTS(clear_key_size * 8, xts)) {
- case 128:
- clr2sec.keytype = PKEY_KEYTYPE_AES_128;
- break;
- case 192:
- clr2sec.keytype = PKEY_KEYTYPE_AES_192;
- break;
- case 256:
- clr2sec.keytype = PKEY_KEYTYPE_AES_256;
- break;
- default:
+ memcpy(&clr2seck2.clrkey, clear_key,
+ HALF_KEYSIZE_FOR_XTS(clear_key_size, xts));
+
+ clr2seck2.type = key_type_to_pkey_type(key_type);
+ if (clr2seck2.type == 0) {
+ warnx("Key-type not supported; %s", key_type);
+ return -ENOTSUP;
+ }
+
+ clr2seck2.size = keybits_to_keysize(HALF_KEYSIZE_FOR_XTS(
+ clear_key_size * 8, xts));
+ if (clr2seck2.size == 0) {
warnx("Invalid clear key size: '%lu' bytes", clear_key_size);
- rc = -EINVAL;
- goto out;
+ return -EINVAL;
+ }
+ if (keybits == 192 && xts) {
+ warnx("Invalid clear key size for XTS: '%lu' bytes",
+ clear_key_size);
+ return -EINVAL;
}
- memcpy(&clr2sec.clrkey, clear_key,
- HALF_KEYSIZE_FOR_XTS(clear_key_size, xts));
+ rc = build_apqn_list_for_key_type(pkey_fd, clr2seck2.type, apqns,
+ &clr2seck2.apqns,
+ &clr2seck2.apqn_entries, verbose);
+ if (rc != 0) {
+ if (rc == -ENODEV || rc == -ENOTSUP)
+ warnx("No APQN is available that can generate a secure "
+ "key of type %s", key_type);
+ else
+ warnx("Failed to build a list of APQNs that can "
+ "generate a secure key of type %s: %s", key_type,
+ strerror(-rc));
+ return rc;
+ }
- rc = ioctl(pkey_fd, PKEY_CLR2SECK, &clr2sec);
- if (rc < 0) {
- rc = -errno;
- warnx("Failed to generate a secure key from a "
- "clear key: %s", strerror(errno));
- warnx("Make sure that all available CCA crypto adapters are "
- "setup with the same master key");
+ size = key_size_for_type(clr2seck2.type);
+ secure_key_size = DOUBLE_KEYSIZE_FOR_XTS(size, xts);
+ secure_key = util_zalloc(secure_key_size);
+
+ clr2seck2.key = secure_key;
+ clr2seck2.keylen = size;
+
+ rc = pkey_clr2seck2(pkey_fd, &clr2seck2, verbose);
+ if (rc != 0) {
+ warnx("Failed to generate a secure key: %s", strerror(-rc));
goto out;
}
- memcpy(secure_key, &clr2sec.seckey, SECURE_KEY_SIZE);
-
if (xts) {
- memcpy(&clr2sec.clrkey, clear_key + clear_key_size / 2,
+ free(clr2seck2.apqns);
+ clr2seck2.apqns = NULL;
+ clr2seck2.apqn_entries = 0;
+
+ memcpy(&clr2seck2.clrkey, clear_key + clear_key_size / 2,
clear_key_size / 2);
- rc = ioctl(pkey_fd, PKEY_CLR2SECK, &clr2sec);
- if (rc < 0) {
- rc = -errno;
- warnx("Failed to generate a secure key from "
- "a clear key: %s", strerror(errno));
- warnx("Make sure that all available CCA crypto "
- "adapters are setup with the same master key");
+ /*
+ * Ensure to generate 2nd key with an APQN that has the same
+ * master key that is used by the 1st key.
+ */
+ rc = build_apqn_list_for_key(pkey_fd, secure_key, size,
+ PKEY_FLAGS_MATCH_CUR_MKVP, apqns,
+ &clr2seck2.apqns,
+ &clr2seck2.apqn_entries, verbose);
+ if (rc != 0) {
+ if (rc == -ENODEV || rc == -ENOTSUP)
+ warnx("No APQN is available that can generate "
+ "a secure key of type %s", key_type);
+ else
+ warnx("Failed to build a list of APQNs that "
+ "can generate a secure key of type %s: "
+ "%s", key_type, strerror(-rc));
goto out;
}
- memcpy(secure_key+SECURE_KEY_SIZE, &clr2sec.seckey,
- SECURE_KEY_SIZE);
+ clr2seck2.key = secure_key + size;
+ clr2seck2.keylen = size;
+
+ rc = pkey_clr2seck2(pkey_fd, &clr2seck2, verbose);
+ if (rc != 0) {
+ warnx("Failed to generate a secure key: %s",
+ strerror(-rc));
+ goto out;
+ }
}
pr_verbose(verbose,
@@ -465,10 +1141,11 @@ int generate_secure_key_clear(int pkey_fd, const char *keyfile,
rc = write_secure_key(keyfile, secure_key, secure_key_size, verbose);
out:
- memset(&clr2sec, 0, sizeof(clr2sec));
+ memset(&clr2seck2, 0, sizeof(clr2seck2));
memset(clear_key, 0, clear_key_size);
free(clear_key);
free(secure_key);
+ free(clr2seck2.apqns);
return rc;
}
@@ -476,84 +1153,58 @@ out:
* Validates an XTS secure key (the second part)
*
* @param[in] pkey_fd the pkey file descriptor
+ * @param[in] apqn the APQN to verify the key with
* @param[in] secure_key a buffer containing the secure key
* @param[in] secure_key_size the secure key size
* @param[in] part1_keysize the key size of the first key part
- * @param[in] part1_attributes the attributes of the first key part
+ * @param[in] part1_flags the flags of the first key part
* @param[out] clear_key_bitsize on return , the cryptographic size of the
* clear key
* @param[in] verbose if true, verbose messages are printed
*
* @returns 0 on success, a negative errno in case of an error
*/
-static int validate_secure_xts_key(int pkey_fd,
+static int validate_secure_xts_key(int pkey_fd, struct pkey_apqn *apqn,
u8 *secure_key, size_t secure_key_size,
- u16 part1_keysize, u32 part1_attributes,
- size_t *clear_key_bitsize, bool verbose)
+ enum pkey_key_size part1_keysize,
+ u32 part1_flags, size_t *clear_key_bitsize,
+ bool verbose)
{
- struct secaeskeytoken *token = (struct secaeskeytoken *)secure_key;
- struct pkey_verifykey verifykey;
- struct secaeskeytoken *token2;
+ struct pkey_verifykey2 verifykey2;
int rc;
util_assert(pkey_fd != -1, "Internal error: pkey_fd is -1");
util_assert(secure_key != NULL, "Internal error: secure_key is NULL");
+ util_assert(apqn != NULL, "Internal error: apqn is NULL");
- /* XTS uses 2 secure key tokens concatenated to each other */
- token2 = (struct secaeskeytoken *)(secure_key + SECURE_KEY_SIZE);
-
- if (secure_key_size != 2 * SECURE_KEY_SIZE) {
- pr_verbose(verbose, "Size of secure key is too small: "
- "%lu expected %lu", secure_key_size,
- 2 * SECURE_KEY_SIZE);
- return -EINVAL;
- }
-
- if (token->bitsize != token2->bitsize) {
- pr_verbose(verbose, "XTS secure key contains 2 clear keys of "
- "different sizes");
- return -EINVAL;
- }
- if (token->keysize != token2->keysize) {
- pr_verbose(verbose, "XTS secure key contains 2 keys of "
- "different sizes");
- return -EINVAL;
- }
- if (memcmp(&token->mkvp, &token2->mkvp, sizeof(token->mkvp)) != 0) {
- pr_verbose(verbose, "XTS secure key contains 2 keys using "
- "different CCA master keys");
- return -EINVAL;
- }
-
- memcpy(&verifykey.seckey, token2, sizeof(verifykey.seckey));
+ memset(&verifykey2, 0, sizeof(verifykey2));
+ verifykey2.key = secure_key + (secure_key_size / 2);
+ verifykey2.keylen = secure_key_size / 2;
+ verifykey2.cardnr = apqn->card;
+ verifykey2.domain = apqn->domain;
- rc = ioctl(pkey_fd, PKEY_VERIFYKEY, &verifykey);
+ rc = pkey_verifyseck2(pkey_fd, &verifykey2, verbose);
if (rc < 0) {
- rc = -errno;
- pr_verbose(verbose, "Failed to validate a secure key: %s",
- strerror(-rc));
+ pr_verbose(verbose, "Failed to validate the 2nd part of the "
+ "XTS secure key on APQN %02x.%04x: %s", apqn->card,
+ apqn->domain, strerror(-rc));
return rc;
}
- if ((verifykey.attributes & PKEY_VERIFY_ATTR_AES) == 0) {
- pr_verbose(verbose, "Secure key is not an AES key");
- return -EINVAL;
- }
-
- if (verifykey.keysize != part1_keysize) {
+ if (verifykey2.size != part1_keysize) {
pr_verbose(verbose, "XTS secure key contains 2 keys using "
"different key sizes");
return -EINVAL;
}
- if (verifykey.attributes != part1_attributes) {
+ if (verifykey2.flags != part1_flags) {
pr_verbose(verbose, "XTS secure key contains 2 keys using "
- "different attributes");
+ "different master keys");
return -EINVAL;
}
- if (clear_key_bitsize)
- *clear_key_bitsize += verifykey.keysize;
+ if (clear_key_bitsize && verifykey2.size != PKEY_SIZE_UNKNOWN)
+ *clear_key_bitsize += verifykey2.size;
return 0;
}
@@ -568,6 +1219,8 @@ static int validate_secure_xts_key(int pkey_fd,
* clear key
* @param[out] is_old_mk in return set to 1 to indicate if the secure key
* is currently enciphered by the OLD CCA master key
+ * @param[in] apqns a zero terminated array of pointers to APQN-strings,
+ * or NULL for AUTOSELECT
* @param[in] verbose if true, verbose messages are printed
*
* @returns 0 on success, a negative errno in case of an error
@@ -575,59 +1228,89 @@ static int validate_secure_xts_key(int pkey_fd,
int validate_secure_key(int pkey_fd,
u8 *secure_key, size_t secure_key_size,
size_t *clear_key_bitsize, int *is_old_mk,
- bool verbose)
+ const char **apqns, bool verbose)
{
- struct secaeskeytoken *token = (struct secaeskeytoken *)secure_key;
- struct pkey_verifykey verifykey;
+ struct pkey_verifykey2 verifykey2;
+ struct pkey_apqn *list = NULL;
+ u32 i, list_entries = 0;
+ bool xts, valid;
int rc;
util_assert(pkey_fd != -1, "Internal error: pkey_fd is -1");
util_assert(secure_key != NULL, "Internal error: secure_key is NULL");
- if (secure_key_size < SECURE_KEY_SIZE) {
- pr_verbose(verbose, "Size of secure key is too small: "
- "%lu expected %lu", secure_key_size,
- SECURE_KEY_SIZE);
- return -EINVAL;
- }
-
- memcpy(&verifykey.seckey, token, sizeof(verifykey.seckey));
+ xts = is_xts_key(secure_key, secure_key_size);
- rc = ioctl(pkey_fd, PKEY_VERIFYKEY, &verifykey);
- if (rc < 0) {
- rc = -errno;
- pr_verbose(verbose, "Failed to validate a secure key: %s",
- strerror(-rc));
+ rc = build_apqn_list_for_key(pkey_fd, secure_key,
+ HALF_KEYSIZE_FOR_XTS(secure_key_size, xts),
+ PKEY_FLAGS_MATCH_CUR_MKVP |
+ PKEY_FLAGS_MATCH_ALT_MKVP,
+ apqns, &list, &list_entries, verbose);
+ if (rc != 0) {
+ pr_verbose(verbose, "Failed to build a list of APQNs that can "
+ "validate this secure key: %s", strerror(-rc));
return rc;
}
- if ((verifykey.attributes & PKEY_VERIFY_ATTR_AES) == 0) {
- pr_verbose(verbose, "Secure key is not an AES key");
- return -EINVAL;
- }
+ if (is_old_mk != NULL)
+ *is_old_mk = true;
+ if (clear_key_bitsize != NULL)
+ *clear_key_bitsize = 0;
- if (clear_key_bitsize)
- *clear_key_bitsize = verifykey.keysize;
+ valid = false;
+ for (i = 0; i < list_entries; i++) {
+ memset(&verifykey2, 0, sizeof(verifykey2));
+ verifykey2.key = secure_key;
+ verifykey2.keylen = HALF_KEYSIZE_FOR_XTS(secure_key_size, xts);
+ verifykey2.cardnr = list[i].card;
+ verifykey2.domain = list[i].domain;
- /* XTS uses 2 secure key tokens concatenated to each other */
- if (secure_key_size > SECURE_KEY_SIZE) {
- rc = validate_secure_xts_key(pkey_fd,
- secure_key, secure_key_size,
- verifykey.keysize,
- verifykey.attributes,
- clear_key_bitsize,
- verbose);
- if (rc != 0)
- return rc;
+ rc = pkey_verifyseck2(pkey_fd, &verifykey2, verbose);
+ if (rc < 0) {
+ pr_verbose(verbose, "Failed to validate the secure key "
+ "on APQN %02x.%04x: %s", list[i].card,
+ list[i].domain, strerror(-rc));
+ continue;
+ }
+
+ if (is_xts_key(secure_key, secure_key_size)) {
+ rc = validate_secure_xts_key(pkey_fd, &list[i],
+ secure_key,
+ secure_key_size,
+ verifykey2.size,
+ verifykey2.flags,
+ clear_key_bitsize,
+ verbose);
+ if (rc != 0)
+ continue;
+
+ }
+
+ valid = true;
+
+ if (clear_key_bitsize) {
+ if (verifykey2.size != PKEY_SIZE_UNKNOWN)
+ *clear_key_bitsize += verifykey2.size;
+ clear_key_bitsize = NULL; /* Set it only once */
+ }
+
+ /*
+ * If at least one of the APQNs have a matching current MK,
+ * then don't report OLD, even if some match the old MK.
+ */
+ if (is_old_mk &&
+ (verifykey2.flags & PKEY_FLAGS_MATCH_CUR_MKVP))
+ *is_old_mk = false;
}
- if (is_old_mk)
- *is_old_mk = (verifykey.attributes &
- PKEY_VERIFY_ATTR_OLD_MKVP) != 0;
+ if (!valid)
+ return -ENODEV;
pr_verbose(verbose, "Secure key validation completed successfully");
- return 0;
+ if (list != NULL)
+ free(list);
+ return rc;
}
/**
@@ -642,7 +1325,7 @@ int validate_secure_key(int pkey_fd,
*
* @returns 0 on success, a negative errno in case of an error
*/
-int generate_key_verification_pattern(const char *key, size_t key_size,
+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;
@@ -677,7 +1360,7 @@ int generate_key_verification_pattern(const char *key, size_t key_size,
}
snprintf((char *)sa.salg_name, sizeof(sa.salg_name), "%s(paes)",
- key_size > SECURE_KEY_SIZE ? "xts" : "cbc");
+ is_xts_key(key, key_size) ? "xts" : "cbc");
tfmfd = socket(AF_ALG, SOCK_SEQPACKET, 0);
if (tfmfd < 0) {
@@ -770,23 +1453,337 @@ out:
return rc;
}
-int get_master_key_verification_pattern(const u8 *secure_key,
- size_t secure_key_size, u64 *mkvp,
- bool verbose)
+int get_master_key_verification_pattern(const u8 *key, size_t key_size,
+ u64 *mkvp, bool UNUSED(verbose))
{
- struct secaeskeytoken *token = (struct secaeskeytoken *)secure_key;
+ struct aesdatakeytoken *datakey = (struct aesdatakeytoken *)key;
+ struct aescipherkeytoken *cipherkey = (struct aescipherkeytoken *)key;
- util_assert(secure_key != NULL, "Internal error: secure_key is NULL");
+ util_assert(key != NULL, "Internal error: secure_key is NULL");
util_assert(mkvp != NULL, "Internal error: mkvp is NULL");
- if (secure_key_size < SECURE_KEY_SIZE) {
- pr_verbose(verbose, "Size of secure key is too small: "
- "%lu expected %lu", secure_key_size,
- SECURE_KEY_SIZE);
+ if (is_cca_aes_data_key(key, key_size))
+ *mkvp = datakey->mkvp;
+ else if (is_cca_aes_cipher_key(key, key_size))
+ memcpy(mkvp, cipherkey->kvp, sizeof(*mkvp));
+ else
return -EINVAL;
+
+ return 0;
+}
+
+/**
+ * Check if the specified key is a CCA AESDATA key token.
+ *
+ * @param[in] key the secure key token
+ * @param[in] key_size the size of the secure key
+ *
+ * @returns true if the key is an CCA AESDATA token type
+ */
+bool is_cca_aes_data_key(const u8 *key, size_t key_size)
+{
+ struct tokenheader *hdr = (struct tokenheader *)key;
+
+ if (key == NULL || key_size < AESDATA_KEY_SIZE)
+ return false;
+
+ if (hdr->type != TOKEN_TYPE_CCA_INTERNAL)
+ return false;
+ if (hdr->version != TOKEN_VERSION_AESDATA)
+ return false;
+
+ return true;
+}
+
+/**
+ * Check if the specified key is a CCA AESCIPHER key token.
+ *
+ * @param[in] key the secure key token
+ * @param[in] key_size the size of the secure key
+ *
+ * @returns true if the key is an CCA AESCIPHER token type
+ */
+bool is_cca_aes_cipher_key(const u8 *key, size_t key_size)
+{
+ struct aescipherkeytoken *cipherkey = (struct aescipherkeytoken *)key;
+
+ if (key == NULL || key_size < AESCIPHER_KEY_SIZE)
+ return false;
+
+ if (cipherkey->type != TOKEN_TYPE_CCA_INTERNAL)
+ return false;
+ if (cipherkey->version != TOKEN_VERSION_AESCIPHER)
+ return false;
+ if (cipherkey->length > key_size)
+ return false;
+
+ if (cipherkey->kms != 0x03) /* key wrapped by master key */
+ return false;
+ if (cipherkey->kwm != 0x02) /* key wrapped using AESKW */
+ return false;
+ if (cipherkey->pfv != 0x00 && cipherkey->pfv != 0x01) /* V0 or V1 */
+ return false;
+ if (cipherkey->adv != 0x01) /* Should have ass. data sect. version 1 */
+ return false;
+ if (cipherkey->at != 0x02) /* Algorithm: AES */
+ return false;
+ if (cipherkey->kt != 0x0001) /* Key type: CIPHER */
+ return false;
+ if (cipherkey->adl != 26) /* Ass. data section length should be 26 */
+ return false;
+ if (cipherkey->kll != 0) /* Should have no key label */
+ return false;
+ if (cipherkey->eadl != 0) /* Should have no ext associated data */
+ return false;
+ if (cipherkey->uadl != 0) /* Should have no user associated data */
+ return false;
+ if (cipherkey->kufc != 2) /* Should have 2 KUFs */
+ return false;
+ if (cipherkey->kmfc != 3) /* Should have 3 KMFs */
+ return false;
+
+ return true;
+}
+
+/**
+ * Check if the specified key is an XTS type key
+ *
+ * @param[in] key the secure key token
+ * @param[in] key_size the size of the secure key
+ *
+ * @returns true if the key is an XTS key type
+ */
+bool is_xts_key(const u8 *key, size_t key_size)
+{
+ if (is_cca_aes_data_key(key, key_size)) {
+ if (key_size == 2 * AESDATA_KEY_SIZE &&
+ is_cca_aes_data_key(key + AESDATA_KEY_SIZE,
+ key_size - AESDATA_KEY_SIZE))
+ return true;
+ } else if (is_cca_aes_cipher_key(key, key_size)) {
+ if (key_size == 2 * AESCIPHER_KEY_SIZE &&
+ is_cca_aes_cipher_key(key + AESCIPHER_KEY_SIZE,
+ key_size - AESCIPHER_KEY_SIZE))
+ return true;
}
- *mkvp = token->mkvp;
+ return false;
+}
+
+/**
+ * Gets the size in bits of the effective key of the specified secure key
+ *
+ * @param[in] key the secure key token
+ * @param[in] key_size the size of the secure key
+ * @param[out] bitsize On return, contains the size in bits of the key.
+ * If the key size can not be determined, then 0 is
+ * passed back as bitsize.
+ *
+ * @returns 0 on success, a negative errno in case of an error
+ */
+int get_key_bit_size(const u8 *key, size_t key_size, size_t *bitsize)
+{
+ struct aesdatakeytoken *datakey = (struct aesdatakeytoken *)key;
+ struct aescipherkeytoken *cipherkey = (struct aescipherkeytoken *)key;
+
+ util_assert(bitsize != NULL, "Internal error: bitsize is NULL");
+
+ if (is_cca_aes_data_key(key, key_size)) {
+ *bitsize = datakey->bitsize;
+ if (key_size == 2 * AESDATA_KEY_SIZE) {
+ datakey = (struct aesdatakeytoken *)key +
+ AESDATA_KEY_SIZE;
+ *bitsize += datakey->bitsize;
+ }
+ } else if (is_cca_aes_cipher_key(key, key_size)) {
+ if (cipherkey->pfv == 0x00) /* V0 payload */
+ *bitsize = cipherkey->pl - 384;
+ else
+ *bitsize = 0; /* Unknown */
+ if (key_size > cipherkey->length) {
+ cipherkey = (struct aescipherkeytoken *)key +
+ cipherkey->length;
+ if (cipherkey->pfv == 0x00) /* V0 payload */
+ *bitsize += cipherkey->pl - 384;
+ }
+ } else {
+ return -EINVAL;
+ }
return 0;
}
+
+/**
+ * Returns the type of the key
+ *
+ * @param[in] key the secure key token
+ * @param[in] key_size the size of the secure key
+ *
+ * @returns a static string on success, NULL in case of an error
+ */
+const char *get_key_type(const u8 *key, size_t key_size)
+{
+ if (is_cca_aes_data_key(key, key_size))
+ return KEY_TYPE_CCA_AESDATA;
+ if (is_cca_aes_cipher_key(key, key_size))
+ return KEY_TYPE_CCA_AESCIPHER;
+
+ return NULL;
+}
+
+/**
+ * Returns the minimum card level for a specific key type
+ *
+ * @param[in] key_type the type of the key
+ *
+ * @returns the minimum card level, or -1 for unknown key types
+ */
+int get_min_card_level_for_keytype(const char *key_type)
+{
+ if (key_type == NULL)
+ return -1;
+
+ if (strcasecmp(key_type, KEY_TYPE_CCA_AESDATA) == 0)
+ return 3;
+ if (strcasecmp(key_type, KEY_TYPE_CCA_AESCIPHER) == 0)
+ return 6;
+
+ return -1;
+}
+
+/**
+ * Performs extended checks on an AES CIPHER key. It checks the key usage
+ * fields (KUFs) and key management fields (KMFs) of the key. The function
+ * returns -EINVAL and issues warning messages if a mismatch is detected.
+ *
+ * @param[in] key the secure key token
+ * @param[in] key_size the size of the secure key
+ *
+ * @returns 0 on success, a negative errno in case of an error
+ */
+int check_aes_cipher_key(const u8 *key, size_t key_size)
+{
+ struct aescipherkeytoken *cipherkey = (struct aescipherkeytoken *)key;
+ bool mismatch = false;
+
+ if (!is_cca_aes_cipher_key(key, key_size)) {
+ warnx("The key is not of type '"KEY_TYPE_CCA_AESCIPHER"'");
+ return -EINVAL;
+ }
+
+ if ((cipherkey->kuf1 & 0x8000) == 0) {
+ printf("WARNING: The secure key can not be used for "
+ "encryption\n");
+ mismatch = true;
+ }
+ if ((cipherkey->kuf1 & 0x4000) == 0) {
+ printf("WARNING: The secure key can not be used for "
+ "decryption\n");
+ mismatch = true;
+ }
+ if (cipherkey->kuf1 & 0x2000) {
+ printf("INFO: The secure key can be used for data translate\n");
+ mismatch = true;
+ }
+ if (cipherkey->kuf1 & 0x1000) {
+ printf("WARNING: The secure key can only be used in UDXs\n");
+ mismatch = true;
+ }
+
+ if (cipherkey->kmf1 & 0x8000) {
+ printf("WARNING: The secure key can be exported using a "
+ "symmetric key\n");
+ mismatch = true;
+ }
+ if (cipherkey->kmf1 & 0x4000) {
+ printf("WARNING: The secure key can be exported using an "
+ "unauthenticated asymmetric key\n");
+ mismatch = true;
+ }
+ if (cipherkey->kmf1 & 0x2000) {
+ printf("WARNING: The secure key can be exported using an "
+ "authenticated asymmetric key\n");
+ mismatch = true;
+ }
+ if (cipherkey->kmf1 & 0x1000) {
+ printf("WARNING: The secure key can be exported using a RAW "
+ "key\n");
+ mismatch = true;
+ }
+ if ((cipherkey->kmf1 & 0x0800) == 0) {
+ printf("WARNING: The secure key can not be transformed into a "
+ "CPACF protected key\n");
+ mismatch = true;
+ }
+ if ((cipherkey->kmf1 & 0x0080) == 0) {
+ printf("WARNING: The secure key can be exported using a DES "
+ "key\n");
+ mismatch = true;
+ }
+ if ((cipherkey->kmf1 & 0x0040) == 0) {
+ printf("WARNING: The secure key can be exported using an AES "
+ "key\n");
+ mismatch = true;
+ }
+ if ((cipherkey->kmf1 & 0x0008) == 0) {
+ printf("WARNING: The secure key can be exported using an RSA "
+ "key\n");
+ mismatch = true;
+ }
+
+ if (cipherkey->kmf2 & 0xC000) {
+ printf("WARNING: The secure key is incomplete\n");
+ mismatch = true;
+ }
+ if (cipherkey->kmf2 & 0x0010) {
+ printf("WARNING: The secure key was previously encrypted with "
+ "an untrusted KEK\n");
+ mismatch = true;
+ }
+ if (cipherkey->kmf2 & 0x0008) {
+ printf("WARNING: The secure key was previously in a format "
+ "without type or usage attributes\n");
+ mismatch = true;
+ }
+ if (cipherkey->kmf2 & 0x0004) {
+ printf("WARNING: The secure key was previously encrypted with "
+ "a key weaker than itself\n");
+ mismatch = true;
+ }
+ if (cipherkey->kmf2 & 0x0002) {
+ printf("WARNING: The secure key was previously in a non-CCA "
+ "format\n");
+ mismatch = true;
+ }
+ if (cipherkey->kmf2 & 0x0001) {
+ printf("WARNING: The secure key was previously encrypted in "
+ "ECB mode\n");
+ mismatch = true;
+ }
+
+ if ((cipherkey->kmf3 & 0xFF00) == 0x0000 ||
+ (cipherkey->kmf3 & 0x00FF) == 0x0000) {
+ printf("WARNING: The secure key was created by an unknown "
+ "method\n");
+ mismatch = true;
+ }
+ if ((cipherkey->kmf3 & 0xFF00) == 0x0400 ||
+ (cipherkey->kmf3 & 0x00FF) == 0x0004) {
+ printf("WARNING: The secure key was created from cleartext key "
+ "components\n");
+ mismatch = true;
+ }
+ if ((cipherkey->kmf3 & 0xFF00) == 0x0500 ||
+ (cipherkey->kmf3 & 0x00FF) == 0x0005) {
+ printf("WARNING: The secure key was entered as a cleartext key "
+ "value\n");
+ mismatch = true;
+ }
+ if ((cipherkey->kmf3 & 0x00FF) == 0x0012) {
+ printf("WARNING: The secure key was converted from a CCA "
+ "key-token that had no export control attributes\n");
+ mismatch = true;
+ }
+
+ return mismatch ? -EINVAL : 0;
+}
diff --git a/zkey/pkey.h b/zkey/pkey.h
index c0aac2e..3dfd588 100644
--- a/zkey/pkey.h
+++ b/zkey/pkey.h
@@ -18,10 +18,23 @@
/*
* Definitions for the /dev/pkey kernel module interface
*/
-struct secaeskeytoken {
- u8 type; /* 0x01 for internal key token */
+struct tokenheader {
+ u8 type;
u8 res0[3];
- u8 version; /* should be 0x04 */
+ u8 version;
+ u8 res1[3];
+} __packed;
+
+#define TOKEN_TYPE_NON_CCA 0x00
+#define TOKEN_TYPE_CCA_INTERNAL 0x01
+
+#define TOKEN_VERSION_AESDATA 0x04
+#define TOKEN_VERSION_AESCIPHER 0x05
+
+struct aesdatakeytoken {
+ u8 type; /* TOKEN_TYPE_INTERNAL (0x01) for internal key token */
+ u8 res0[3];
+ u8 version; /* should be TOKEN_VERSION_AESDATA (0x04) */
u8 res1[1];
u8 flag; /* key flags */
u8 res2[1];
@@ -33,22 +46,60 @@ struct secaeskeytoken {
u8 tvv[4]; /* token validation value */
} __packed;
-#define SECURE_KEY_SIZE sizeof(struct secaeskeytoken)
+struct aescipherkeytoken {
+ u8 type; /* TOKEN_TYPE_INTERNAL (0x01) for internal key token */
+ u8 res0;
+ u16 length; /* length of token */
+ u8 version; /* should be TOKEN_VERSION_CIPHER (0x05) */
+ u8 res1[3];
+ u8 kms; /* key material state, should be 0x03 */
+ u8 kvptype; /* key verification pattern type */
+ u8 kvp[16]; /* key verification pattern */
+ u8 kwm; /* key wrapping method, should be 0x02 */
+ u8 kwh; /* key wrapping hash algorithm */
+ u8 pfv; /* payload format version, should be 0x00*/
+ u8 res2;
+ u8 adv; /* associated data section version */
+ u8 res3;
+ u16 adl; /* associated data length */
+ u8 kll; /* length of optional key label */
+ u8 eadl; /* extended associated data length */
+ u8 uadl; /* user associated data length */
+ u8 res4;
+ u16 pl; /* payload bit length */
+ u8 res5;
+ u8 at; /* algorithm type, should be 0x02 (AES) */
+ u16 kt; /* key type, should be 0x001 (CIPHER) */
+ u8 kufc; /* key usage field count */
+ u16 kuf1; /* key usage field 1 */
+ u16 kuf2; /* key usage field 2 */
+ u8 kmfc; /* key management field count */
+ u16 kmf1; /* key management field 1 */
+ u16 kmf2; /* key management field 2 */
+ u16 kmf3; /* key management field 3 */
+ u8 varpart[80]; /* variable part */
+} __packed;
+
+#define AESDATA_KEY_SIZE sizeof(struct aesdatakeytoken)
+#define AESCIPHER_KEY_SIZE sizeof(struct aescipherkeytoken)
+
+#define MAX_SECURE_KEY_SIZE MAX(AESDATA_KEY_SIZE, AESCIPHER_KEY_SIZE)
+#define MIN_SECURE_KEY_SIZE MIN(AESDATA_KEY_SIZE, AESCIPHER_KEY_SIZE)
struct pkey_seckey {
- u8 seckey[SECURE_KEY_SIZE]; /* the secure key blob */
+ u8 seckey[AESDATA_KEY_SIZE]; /* the secure key blob */
};
struct pkey_clrkey {
u8 clrkey[32]; /* 16, 24, or 32 byte clear key value */
};
-#define PKEY_IOCTL_MAGIC 'p'
-#define AUTOSELECT 0xFFFF
-#define PKEYDEVICE "/dev/pkey"
-#define PKEY_KEYTYPE_AES_128 1
-#define PKEY_KEYTYPE_AES_192 2
-#define PKEY_KEYTYPE_AES_256 3
+#define PKEY_IOCTL_MAGIC 'p'
+#define AUTOSELECT 0xFFFF
+#define PKEYDEVICE "/dev/pkey"
+#define PKEY_KEYTYPE_AES_128 1
+#define PKEY_KEYTYPE_AES_192 2
+#define PKEY_KEYTYPE_AES_256 3
struct pkey_genseck {
u16 cardnr; /* in: card to use or FFFF for any */
@@ -82,6 +133,100 @@ struct pkey_verifykey {
#define PKEY_VERIFYKEY _IOWR(PKEY_IOCTL_MAGIC, 0x07, struct pkey_verifykey)
+enum pkey_key_type {
+ PKEY_TYPE_CCA_DATA = (u32) 1,
+ PKEY_TYPE_CCA_CIPHER = (u32) 2,
+};
+
+enum pkey_key_size {
+ PKEY_SIZE_AES_128 = (u32) 128,
+ PKEY_SIZE_AES_192 = (u32) 192,
+ PKEY_SIZE_AES_256 = (u32) 256,
+ PKEY_SIZE_UNKNOWN = (u32) 0xFFFFFFFF,
+};
+
+#define PKEY_FLAGS_MATCH_CUR_MKVP 0x00000002
+#define PKEY_FLAGS_MATCH_ALT_MKVP 0x00000004
+
+#define PKEY_KEYGEN_XPRT_SYM 0x00008000
+#define PKEY_KEYGEN_XPRT_UASY 0x00004000
+#define PKEY_KEYGEN_XPRT_AASY 0x00002000
+#define PKEY_KEYGEN_XPRT_RAW 0x00001000
+#define PKEY_KEYGEN_XPRT_CPAC 0x00000800
+#define PKEY_KEYGEN_XPRT_DES 0x00000080
+#define PKEY_KEYGEN_XPRT_AES 0x00000040
+#define PKEY_KEYGEN_XPRT_RSA 0x00000008
+
+struct pkey_apqn {
+ u16 card;
+ u16 domain;
+};
+
+struct pkey_genseck2 {
+ struct pkey_apqn *apqns; /* in: ptr to list of apqn targets */
+ u32 apqn_entries; /* in: # of apqn target list entries */
+ enum pkey_key_type type; /* in: key type to generate */
+ enum pkey_key_size size; /* in: key size to generate */
+ u32 keygenflags; /* in: key generation flags */
+ u8 *key; /* in: pointer to key blob buffer */
+ u32 keylen; /* in: available key blob buffer size */
+ /* out: actual key blob size */
+};
+
+#define PKEY_GENSECK2 _IOWR(PKEY_IOCTL_MAGIC, 0x11, struct pkey_genseck2)
+
+struct pkey_clr2seck2 {
+ struct pkey_apqn *apqns; /* in: ptr to list of apqn targets */
+ u32 apqn_entries; /* in: # of apqn target list entries */
+ enum pkey_key_type type; /* in: key type to generate */
+ enum pkey_key_size size; /* in: key size to generate */
+ u32 keygenflags; /* in: key generation flags */
+ struct pkey_clrkey clrkey; /* in: the clear key value */
+ u8 *key; /* in: pointer to key blob buffer */
+ u32 keylen; /* in: available key blob buffer size */
+ /* out: actual key blob size */
+};
+
+#define PKEY_CLR2SECK2 _IOWR(PKEY_IOCTL_MAGIC, 0x12, struct pkey_clr2seck2)
+
+struct pkey_verifykey2 {
+ u8 *key; /* in: pointer to key blob */
+ u32 keylen; /* in: key blob size */
+ u16 cardnr; /* in/out: card number */
+ u16 domain; /* in/out: domain number */
+ enum pkey_key_type type; /* out: the key type */
+ enum pkey_key_size size; /* out: the key size */
+ u32 flags; /* out: additional key info flags */
+};
+
+#define PKEY_VERIFYKEY2 _IOWR(PKEY_IOCTL_MAGIC, 0x17, struct pkey_verifykey2)
+
+struct pkey_apqns4key {
+ u8 *key; /* in: pointer to key blob */
+ u32 keylen; /* in: key blob size */
+ u32 flags; /* in: match controlling flags */
+ struct pkey_apqn *apqns; /* in/out: ptr to list of apqn targets*/
+ u32 apqn_entries; /* in: max # of apqn entries in list */
+ /* out: # apqns stored into the list */
+};
+
+#define PKEY_APQNS4K _IOWR(PKEY_IOCTL_MAGIC, 0x1B, struct pkey_apqns4key)
+
+struct pkey_apqns4keytype {
+ enum pkey_key_type type; /* in: key type */
+ u8 cur_mkvp[32]; /* in: current mkvp */
+ u8 alt_mkvp[32]; /* in: alternate mkvp */
+ u32 flags; /* in: match controlling flags */
+ struct pkey_apqn *apqns; /* in/out: ptr to list of apqn targets*/
+ u32 apqn_entries; /* in: max # of apqn entries in list */
+ /* out: # apqns stored into the list */
+};
+
+#define PKEY_APQNS4KT _IOWR(PKEY_IOCTL_MAGIC, 0x1C, struct pkey_apqns4keytype)
+
+#define KEY_TYPE_CCA_AESDATA "CCA-AESDATA"
+#define KEY_TYPE_CCA_AESCIPHER "CCA-AESCIPHER"
+
#define PAES_BLOCK_SIZE 16
#define ENC_ZERO_LEN (2 * PAES_BLOCK_SIZE)
#define VERIFICATION_PATTERN_LEN (2 * ENC_ZERO_LEN + 1)
@@ -89,14 +234,13 @@ struct pkey_verifykey {
int open_pkey_device(bool verbose);
int generate_secure_key_random(int pkey_fd, const char *keyfile,
- size_t keybits, bool xts, u16 card, u16 domain,
- bool verbose);
+ size_t keybits, bool xts, const char *key_type,
+ const char **apqns, bool verbose);
int generate_secure_key_clear(int pkey_fd, const char *keyfile,
size_t keybits, bool xts,
- const char *clearkeyfile,
- u16 card, u16 domain,
- bool verbose);
+ const char *clearkeyfile, const char *key_type,
+ const char **apqns, bool verbose);
u8 *read_secure_key(const char *keyfile, size_t *secure_key_size,
bool verbose);
@@ -107,13 +251,20 @@ int write_secure_key(const char *keyfile, const u8 *secure_key,
int validate_secure_key(int pkey_fd,
u8 *secure_key, size_t secure_key_size,
size_t *clear_key_bitsize, int *is_old_mk,
- bool verbose);
+ const char **apqns, bool verbose);
-int generate_key_verification_pattern(const char *key, size_t key_size,
+int generate_key_verification_pattern(const u8 *key, size_t key_size,
char *vp, size_t vp_len, bool verbose);
-int get_master_key_verification_pattern(const u8 *secure_key,
- size_t secure_key_size, u64 *mkvp,
- bool verbose);
+int get_master_key_verification_pattern(const u8 *key, size_t key_size,
+ u64 *mkvp, bool verbose);
+
+bool is_cca_aes_data_key(const u8 *key, size_t key_size);
+bool is_cca_aes_cipher_key(const u8 *key, size_t key_size);
+bool is_xts_key(const u8 *key, size_t key_size);
+int get_key_bit_size(const u8 *key, size_t key_size, size_t *bitsize);
+const char *get_key_type(const u8 *key, size_t key_size);
+int get_min_card_level_for_keytype(const char *key_type);
+int check_aes_cipher_key(const u8 *key, size_t key_size);
#endif
diff --git a/zkey/utils.c b/zkey/utils.c
index 9dc4af7..e70ebcd 100644
--- a/zkey/utils.c
+++ b/zkey/utils.c
@@ -118,6 +118,49 @@ out:
return rc;
}
+/**
+ * Returns the level of the card. For a CEX3C 3 is returned, for a CEX4C 4,
+ * and so on.
+ *
+ * @param[in] card card number
+ *
+ * @returns The card level, or -1 of the level can not be determined.
+ */
+int sysfs_get_card_level(int card)
+{
+ char *dev_path;
+ char type[20];
+ int rc;
+
+ dev_path = util_path_sysfs("bus/ap/devices/card%02x", card);
+ if (!util_path_is_dir(dev_path)) {
+ rc = -1;
+ goto out;
+ }
+ if (util_file_read_line(type, sizeof(type), "%s/type", dev_path) != 0) {
+ rc = -1;
+ goto out;
+ }
+ if (strncmp(type, "CEX", 3) != 0 || strlen(type) < 5) {
+ rc = -1;
+ goto out;
+ }
+ if (type[4] != 'C') {
+ rc = -1;
+ goto out;
+ }
+ if (type[3] < '1' || type[3] > '9') {
+ rc = -1;
+ goto out;
+ }
+
+ rc = type[3] - '0';
+
+out:
+ free(dev_path);
+ return rc;
+}
+
/**
* Gets the 8 character ASCII serial number string of an card from the sysfs.
*
@@ -436,12 +479,14 @@ static int print_apqn_mk_info(int card, int domain, void *handler_data)
{
struct print_apqn_info *info = (struct print_apqn_info *)handler_data;
struct mk_info mk_info;
- int rc;
+ int rc, level;
rc = sysfs_get_mkvps(card, domain, &mk_info, info->verbose);
if (rc == -ENOTSUP)
return rc;
+ level = sysfs_get_card_level(card);
+
util_rec_set(info->rec, "APQN", "%02x.%04x", card, domain);
if (rc == 0) {
@@ -470,6 +515,11 @@ static int print_apqn_mk_info(int card, int domain, void *handler_data)
util_rec_set(info->rec, "OLD", "?");
}
+ if (level > 0)
+ util_rec_set(info->rec, "TYPE", "CEX%dC", level);
+ else
+ util_rec_set(info->rec, "TYPE", "?");
+
util_rec_print(info->rec);
return 0;
@@ -499,6 +549,7 @@ int print_mk_info(const char *apqns, bool verbose)
util_rec_def(info.rec, "NEW", UTIL_REC_ALIGN_LEFT, 16, "NEW MK");
util_rec_def(info.rec, "CUR", UTIL_REC_ALIGN_LEFT, 16, "CURRENT MK");
util_rec_def(info.rec, "OLD", UTIL_REC_ALIGN_LEFT, 16, "OLD MK");
+ util_rec_def(info.rec, "TYPE", UTIL_REC_ALIGN_LEFT, 6, "TYPE");
util_rec_print_hdr(info.rec);
rc = handle_apqns(apqns, print_apqn_mk_info, &info, verbose);
@@ -511,6 +562,7 @@ struct cross_check_info {
u64 mkvp;
u64 new_mkvp;
bool key_mkvp;
+ int min_level;
u32 num_cur_match;
u32 num_old_match;
u32 num_new_match;
@@ -525,7 +577,7 @@ static int cross_check_mk_info(int card, int domain, void *handler_data)
struct cross_check_info *info = (struct cross_check_info *)handler_data;
struct mk_info mk_info;
char temp[200];
- int rc;
+ int rc, level;
rc = sysfs_get_mkvps(card, domain, &mk_info, info->verbose);
if (rc == -ENODEV) {
@@ -539,6 +591,19 @@ static int cross_check_mk_info(int card, int domain, void *handler_data)
info->num_checked++;
+ if (info->min_level >= 0) {
+ level = sysfs_get_card_level(card);
+
+ if (level < info->min_level) {
+ info->print_mks = 1;
+ info->mismatch = 1;
+ sprintf(temp, "WARNING: APQN %02x.%04x: The card level "
+ "is less than CEX%dC.", card, domain,
+ info->min_level);
+ util_print_indented(temp, 0);
+ }
+ }
+
if (mk_info.new_mk.mk_state == MK_STATE_PARTIAL) {
info->print_mks = 1;
sprintf(temp, "INFO: APQN %02x.%04x: The NEW master key "
@@ -662,6 +727,8 @@ static int cross_check_mk_info(int card, int domain, void *handler_data)
* @param[in] mkvp The master key verification pattern of a secure key.
* If this is all zero, then the master keys are not
* matched against it.
+ * @param[in] min_level The minimum card level required. If min_level is -1 then
+ * the card level is not checked.
* @param[in] print_mks if true, then a the full master key info of all
* specified APQns is printed, in case of a mismatch.
* @param[in] verbose if true, verbose messages are printed
@@ -671,7 +738,8 @@ static int cross_check_mk_info(int card, int domain, void *handler_data)
* -ENOTSUP is returned when the mkvps sysfs attribute is not
* available, because the zcrypt kernel module is on an older level.
*/
-int cross_check_apqns(const char *apqns, u64 mkvp, bool print_mks, bool verbose)
+int cross_check_apqns(const char *apqns, u64 mkvp, int min_level,
+ bool print_mks, bool verbose)
{
struct cross_check_info info;
char temp[200];
@@ -680,10 +748,12 @@ int cross_check_apqns(const char *apqns, u64 mkvp, bool print_mks, bool verbose)
memset(&info, 0, sizeof(info));
info.key_mkvp = mkvp != 0;
info.mkvp = mkvp;
+ info.min_level = min_level;
info.verbose = verbose;
- pr_verbose(verbose, "Cross checking APQNs with mkvp 0x%016llx: %s",
- mkvp, apqns != NULL ? apqns : "ANY");
+ pr_verbose(verbose, "Cross checking APQNs with mkvp 0x%016llx and "
+ "min-level %d: %s", mkvp, min_level,
+ apqns != NULL ? apqns : "ANY");
rc = handle_apqns(apqns, cross_check_mk_info, &info, verbose);
if (rc != 0)
@@ -723,3 +793,27 @@ int cross_check_apqns(const char *apqns, u64 mkvp, bool print_mks, bool verbose)
return rc;
}
+
+/*
+ * Prompts for yes or no. Returns true if 'y' or 'yes' was entered.
+ *
+ * @param[in] verbose if true, verbose messages are printed
+ *
+ * @returns true if 'y' or 'yes' was entered (case insensitive). Returns false
+ * otherwise.
+ */
+bool prompt_for_yes(bool verbose)
+{
+ char str[20];
+
+ if (fgets(str, sizeof(str), stdin) == NULL)
+ return false;
+
+ if (str[strlen(str) - 1] == '\n')
+ str[strlen(str) - 1] = '\0';
+ pr_verbose(verbose, "Prompt reply: '%s'", str);
+ if (strcasecmp(str, "y") == 0 || strcasecmp(str, "yes") == 0)
+ return true;
+
+ return false;
+}
diff --git a/zkey/utils.h b/zkey/utils.h
index c4dfb2b..2a915eb 100644
--- a/zkey/utils.h
+++ b/zkey/utils.h
@@ -18,6 +18,8 @@ int sysfs_is_card_online(int card);
int sysfs_is_apqn_online(int card, int domain);
+int sysfs_get_card_level(int card);
+
int sysfs_get_serialnr(int card, char serialnr[9], bool verbose);
#define MK_STATE_EMPTY 0
@@ -48,7 +50,9 @@ int handle_apqns(const char *apqns, apqn_handler_t handler, void *handler_data,
int print_mk_info(const char *apqns, bool verbose);
-int cross_check_apqns(const char *apqns, u64 mkvp, bool print_mks,
- bool verbose);
+int cross_check_apqns(const char *apqns, u64 mkvp, int min_level,
+ bool print_mks, bool verbose);
+
+bool prompt_for_yes(bool verbose);
#endif
diff --git a/zkey/zkey-cryptsetup.c b/zkey/zkey-cryptsetup.c
index c683fd4..7ba2842 100644
--- a/zkey/zkey-cryptsetup.c
+++ b/zkey/zkey-cryptsetup.c
@@ -1329,22 +1329,25 @@ static int activate_unbound_keyslot(int token, int keyslot, const char *key,
return rc;
}
-static int check_keysize_and_cipher_mode(size_t keysize)
+static int check_keysize_and_cipher_mode(const u8 *key, size_t keysize)
{
- if (keysize == 0) {
+ if (keysize < MIN_SECURE_KEY_SIZE ||
+ keysize > 2 * MAX_SECURE_KEY_SIZE) {
warnx("Invalid volume key size");
return -EINVAL;
}
if (strncmp(crypt_get_cipher_mode(g.cd), "xts", 3) == 0) {
- if (keysize != 2 * SECURE_KEY_SIZE) {
+ if (keysize < 2 * MIN_SECURE_KEY_SIZE ||
+ (key != NULL && !is_xts_key(key, keysize))) {
warnx("The volume key size %lu is not valid for the "
"cipher mode '%s'", keysize,
crypt_get_cipher_mode(g.cd));
return -EINVAL;
}
} else {
- if (keysize != SECURE_KEY_SIZE) {
+ if (keysize > MAX_SECURE_KEY_SIZE ||
+ (key != NULL && is_xts_key(key, keysize))) {
warnx("The volume key size %lu is not valid for the "
"cipher mode '%s'", keysize,
crypt_get_cipher_mode(g.cd));
@@ -1377,7 +1380,7 @@ static int open_keyslot(int keyslot, char **key, size_t *keysize,
vkeysize = crypt_get_volume_key_size(g.cd);
pr_verbose("Volume key size: %lu", vkeysize);
- rc = check_keysize_and_cipher_mode(vkeysize);
+ rc = check_keysize_and_cipher_mode(NULL, vkeysize);
if (rc != 0)
return rc;
@@ -1489,7 +1492,7 @@ static int validate_keyslot(int keyslot, char **key, size_t *keysize,
keyslot = rc;
rc = validate_secure_key(g.pkey_fd, (u8 *)vkey, vkeysize, clear_keysize,
- &is_old, g.verbose);
+ &is_old, NULL, g.verbose);
if (rc != 0) {
if (invalid_msg != NULL)
warnx("%s", invalid_msg);
@@ -1571,7 +1574,7 @@ static int reencipher_prepare(int token)
if (rc != 0)
goto out;
- rc = generate_key_verification_pattern(key, keysize,
+ rc = generate_key_verification_pattern((u8 *)key, keysize,
reenc_tok.verification_pattern,
sizeof(reenc_tok.verification_pattern),
g.verbose);
@@ -1851,8 +1854,8 @@ static int reencipher_complete(int token)
}
- rc = generate_key_verification_pattern(key, keysize, vp, sizeof(vp),
- g.verbose);
+ rc = generate_key_verification_pattern((u8 *)key, keysize, vp,
+ sizeof(vp), g.verbose);
if (rc != 0) {
warnx("Failed to generate the verification pattern: %s",
strerror(-rc));
@@ -1969,7 +1972,7 @@ static int command_validate(void)
goto out;
rc = validate_secure_key(g.pkey_fd, (u8 *)key, keysize, &clear_keysize,
- &is_old_mk, g.verbose);
+ &is_old_mk, NULL, g.verbose);
is_valid = (rc == 0);
token = find_token(g.cd, PAES_REENC_TOKEN_NAME);
@@ -1998,7 +2001,9 @@ static int command_validate(void)
printf(" Status: %s\n", is_valid ? "Valid" : "Invalid");
printf(" Secure key size: %lu bytes\n", keysize);
printf(" XTS type key: %s\n",
- keysize > SECURE_KEY_SIZE ? "Yes" : "No");
+ is_xts_key((u8 *)key, keysize) ? "Yes" : "No");
+ printf(" Key type: %s\n",
+ get_key_type((u8 *)key, keysize));
if (is_valid) {
printf(" Clear key size: %lu bits\n", clear_keysize);
printf(" Enciphered with: %s CCA master key (MKVP: "
@@ -2074,7 +2079,7 @@ static int command_setvp(void)
token = find_token(g.cd, PAES_VP_TOKEN_NAME);
- rc = generate_key_verification_pattern(key, keysize,
+ rc = generate_key_verification_pattern((const u8 *)key, keysize,
vp_tok.verification_pattern,
sizeof(vp_tok.verification_pattern),
g.verbose);
@@ -2129,12 +2134,12 @@ static int command_setkey(void)
if (newkey == NULL)
return EXIT_FAILURE;
- rc = check_keysize_and_cipher_mode(newkey_size);
+ rc = check_keysize_and_cipher_mode(newkey, newkey_size);
if (rc != 0)
goto out;
rc = validate_secure_key(g.pkey_fd, newkey, newkey_size, NULL,
- &is_old_mk, g.verbose);
+ &is_old_mk, NULL, g.verbose);
if (rc != 0) {
warnx("The secure key in file '%s' is not valid",
g.master_key_file);
@@ -2164,21 +2169,14 @@ static int command_setkey(void)
if (rc < 0)
goto out;
- if (keysize != newkey_size) {
- warnx("The secure key in file '%s' has an invalid size",
- g.master_key_file);
- rc = -EINVAL;
- goto out;
- }
-
- if (memcmp(newkey, key, keysize) == 0) {
+ if (keysize == newkey_size && memcmp(newkey, key, keysize) == 0) {
warnx("The secure key in file '%s' is equal to the current "
"volume key, setkey is ignored", g.master_key_file);
rc = 0;
goto out;
}
- rc = generate_key_verification_pattern((char *)newkey, newkey_size, vp,
+ rc = generate_key_verification_pattern(newkey, newkey_size, vp,
sizeof(vp), g.verbose);
if (rc != 0) {
warnx("Failed to generate the verification pattern: %s",
diff --git a/zkey/zkey.1 b/zkey/zkey.1
index c7cea83..31fff43 100644
--- a/zkey/zkey.1
+++ b/zkey/zkey.1
@@ -62,7 +62,8 @@ in size.
The \fBzkey\fP tool can operate in two modes. When argument
.I secure\-key\-file
is specified then it operates on the secure key contained in the specified file.
-This applies to commands \fBgenerate\fP, \fBvalidate\fP, and \fBreencipher\fP.
+This applies to commands \fBgenerate\fP, \fBvalidate\fP, \fBreencipher\fP, and
+\fBconvert\fP.
When the
.B \-\-name
option is specified then it operates on a secure key contained in the secure
@@ -79,6 +80,8 @@ key repository.
.RB [ \-\-xts | \-x ]
.RB [ \-\-clearkey | \-c
.IR clear\-key\-file ]
+.RB [ \-\-key-type | \-K
+.IR type ]
.RB [ \-\-verbose | \-V ]
.
.PP
@@ -102,6 +105,8 @@ key repository.
.RB [ \-\-xts | \-x ]
.RB [ \-\-clearkey | \-c
.IR clear\-key\-file ]
+.RB [ \-\-key-type | \-K
+.IR type ]
.RB [ \-\-verbose | \-V ]
.PP
Use the
@@ -129,6 +134,15 @@ additional information can be associated with a secure key using the
, or the
.B \-\-sector-size
options.
+.PP
+You can generate different types of secure keys: \fBCCA-AESDATA\fP keys, and
+\fBCCA-AESCIPHER\fP keys. Specify the type of the secure key using the
+.B \-\-key\-type
+option. The default key type is CCA-AESDATA.
+.PP
+.B Note:
+Secure keys of type \fBCCA-AESCIPHER\fP require an IBM cryptographic
+adapter in CCA coprocessor mode of version 6 or later, e.g. a CEX6C.
.
.SS "Validating secure AES keys"
.
@@ -336,6 +350,12 @@ additional information can be associated with a secure key using the
, or the
.B \-\-sector-size
options.
+.PP
+.B Note:
+The \fBimport\fP command requires the CCA host library (libcsulcca.so)
+to be installed when secure keys of type \fBCCA-AESCIPHER\fP are imported.
+For the supported environments and downloads, see:
+\fIhttp://www.ibm.com/security/cryptocards\fP
.
.SS "Export AES secure keys from the secure key repository"
.
@@ -354,7 +374,6 @@ to a file in the file system. Specify the name of the key that is to be exported
using the
.B \-\-name
option. You cannot use wildcards.
-When wildcards are used you must quote the value.
The exported secure key also remains in the secure key repository.
.
.SS "List AES secure keys contained in the secure key repository"
@@ -369,6 +388,8 @@ The exported secure key also remains in the secure key repository.
.IR card1.domain1[,card2.domain2[,...]] ]
.RB [ \-\-volume-type | \-t
.IR type ]
+.RB [ \-\-key-type | \-K
+.IR type ]
.RB [ \-\-verbose | \-V ]
.
.PP
@@ -383,7 +404,7 @@ listed that are associated with the specified volume and device-mapper name.
.PP
The
.B list
-command displays the attributes of the secure keys, such as key sizes,
+command displays the attributes of the secure keys, such as key sizes, key type,
whether it is a secure key that can be used for the XTS cipher mode, the textual
description, associated cryptographic adapters (APQNs) and volumes, the
sector size, the key verification pattern, and timestamps for key creation, last
@@ -411,9 +432,11 @@ option.
.PP
.B Note:
When removing a secure key that is associated with one or multiple volumes,
+and the key's volume type is \fBplain\fP,
a message informs you about the associated volumes. When the secure key is
removed, these volumes can no longer be used, unless you have a backup of the
-secure key.
+secure key. For keys with volume type \fBluks2\fP no such message is issued,
+because the secure key is contained in the LUKS2 header.
.
.SS "Change existing AES secure keys contained the secure key repository"
.
@@ -490,6 +513,15 @@ option and the new name using the
.B \-\-new-name
option. You cannot use wildcards.
.
+.B Note:
+When renaming a secure key that is associated with one or multiple volumes and
+the key's volume type is \fBplain\fP, a message informs you about the
+associated volumes. When the secure key is renamed, these volumes can no
+longer be used, unless you change the name of the secure key in the 'cryptsetup
+plainOpen' commands and in the '/etc/crypttab' entries.
+For keys with volume type \fBluks2\fP no such message is issued, because the
+secure key is contained in the LUKS2 header.
+.
.SS "Copy (duplicate) existing AES secure keys in the secure key repository"
.
.B zkey
@@ -649,6 +681,72 @@ questions, you can specify the
option. These options are passed to the generated command(s) and behave in the
same way as with \fBcryptsetup\fP.
.
+.SS "Convert existing AES secure keys from one key type to another type"
+.
+.B zkey
+.BR convert | con
+.I secure\-key\-file
+.RB \-\-key-type | \-K
+.IR type
+.RB [ \-\-no\-apqn\-check ]
+.RB [ \-\-force | \-F ]
+.RB [ \-\-verbose | \-V ]
+.
+.PP
+.B zkey
+.BR convert | con
+.B \-\-name | \-N
+.IR key-name
+.RB \-\-key-type | \-K
+.IR type
+.RB [ \-\-no\-apqn\-check ]
+.RB [ \-\-force | \-F ]
+.RB [ \-\-verbose | \-V ]
+.
+.PP
+Use the
+.B convert
+command to convert an existing secure key from one key type to another type.
+You can convert secure keys of type CCA-AESDATA to type CCA-AESCIPHER only.
+
+.B Note:
+Secure keys converted to type \fBCCA-AESCIPHER\fP require an IBM cryptographic
+adapter in CCA coprocessor mode of version 6 or later, e.g. a CEX6C.
+
+The secure key can either be contained in a file in the file system, or in a
+secure key repository. To convert a secure key contained in a file, specify
+the file name with option \fIsecure\-key\-file\fP. To convert a secure key
+contained in the secure key repository, specify the name of the key
+that is to be converted using the
+.B \-\-name
+option. You cannot use wildcards. The convert command prompts for
+a confirmation, unless you specify the
+.B \-\-force
+option.
+.PP
+.B Note:
+Converting a secure key is irreversible!
+When converting a secure key that is associated with one or multiple volumes,
+a message informs you about the associated volumes. When the secure key is
+converted, this might have an effect on these volumes.
+.P
+For volumes with volume type \fBplain\fP, you must adapt the crypttab entries
+and change the key size parameter to \fBsize=<new-key-size-in-bits>\fP or run
+command \fBzkey crypttab --volumes <device>\fP for each associated volume to
+re-generate the crypttab entries.
+.P
+Associated volumes of type \fLUKS2\fP still contain the secure AES volume key of
+the original type. To change the secure AES volume key in the LUKS2 header,
+run command \fBzkey-cryptsetup setkey <device> --master-key-file
+<converted-key>\fP for each associated volume.
+.
+.P
+.B Note:
+The \fBconvert\fP command requires the CCA host library (libcsulcca.so)
+to be installed. The required CCA IBM cryptographic adapter firmware version
+is 6.3.27 or later. For the supported environments and downloads, see:
+\fIhttp://www.ibm.com/security/cryptocards\fP
+.
.
.
.
@@ -718,6 +816,13 @@ This option is only available if
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 \-K ", " \-\-key-type\~\fItype\fP
+Specifies the key type of the secure key. Possible values are \fBCCA-AESDATA\fP
+and \fBCCA-AESCIPHER\fP. If this option is omitted, then a secure key of type
+CCA-AESDATA is generated. Secure keys of type \fBCCA-AESCIPHER\fP require an
+IBM cryptographic adapter in CCA coprocessor mode of version 6 or later, e.g.
+a CEX6C.
.
.
.
@@ -897,6 +1002,11 @@ This option is only available if
.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 \-K ", " \-\-key-type\~\fItype\fP
+Specifies the key type of the secure key. Possible values are \fBCCA-AESDATA\fP
+and \fBCCA-AESCIPHER\fP. Only keys with the specified key type are listed.
+This option is only used for secure keys contained in the secure key repository.
.
.
.
@@ -1160,6 +1270,30 @@ cryptsetup command(s).
.
.
.
+.SS "Options for the convert command"
+.TP
+.BR \-N ", " \-\-name\~\fIkey-name\fP
+Specifies the name of the secure key in the secure key repository. You cannot
+use wildcards.
+This option is only used for secure keys contained in the secure key repository.
+.TP
+.BR \-K ", " \-\-key-type\~\fItype\fP
+Specifies the key type to which the secure key shall be converted to.
+Possible values are \fBCCA-AESCIPHER\fP. Secure keys of type \fBCCA-AESCIPHER\fP
+require an IBM cryptographic adapter in CCA coprocessor mode of version 6 or
+later, e.g. a CEX6C.
+.TP
+.BR \-\-no\-apqn\-check
+Do not check if the associated APQNs are available and capable of converting
+the secure key to type CCA-AESCIPHER.
+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 convertion of a secure key. Use this option
+to convert a secure key without prompting for a confirmation.
+.
+.
+.
.SS "General options"
.TP
.BR \-V ", " \-\-verbose
diff --git a/zkey/zkey.c b/zkey/zkey.c
index a0dbc0b..3ae9a9b 100644
--- a/zkey/zkey.c
+++ b/zkey/zkey.c
@@ -73,6 +73,7 @@ static struct zkey_globals {
long int sector_size;
char *volume_type;
char *newname;
+ char *key_type;
bool run;
bool batch_mode;
char *keyfile;
@@ -105,6 +106,7 @@ static struct zkey_globals {
#define COMMAND_COPY "copy "
#define COMMAND_CRYPTTAB "crypttab"
#define COMMAND_CRYPTSETUP "cryptsetup"
+#define COMMAND_CONVERT "convert"
#define ZKEY_COMMAND_MAX_LEN 10
@@ -216,6 +218,15 @@ static struct util_opt opt_vec[] = {
.command = COMMAND_GENERATE,
},
#endif
+ {
+ .option = { "key-type", required_argument, NULL, 'K'},
+ .argument = "type",
+ .desc = "The type of the key. Possible values are '"
+ KEY_TYPE_CCA_AESDATA"' and '"KEY_TYPE_CCA_AESCIPHER"'. "
+ "When this option is omitted, the default is '"
+ KEY_TYPE_CCA_AESDATA"'",
+ .command = COMMAND_GENERATE,
+ },
/***********************************************************/
{
.flags = UTIL_OPT_FLAG_SECTION,
@@ -432,6 +443,15 @@ static struct util_opt opt_vec[] = {
.command = COMMAND_LIST,
},
#endif
+ {
+ .option = { "key-type", required_argument, NULL, 'K'},
+ .argument = "type",
+ .desc = "The type of the key. Possible values are '"
+ KEY_TYPE_CCA_AESDATA"' and '"KEY_TYPE_CCA_AESCIPHER"'. "
+ "Use this option to list all keys with the specified "
+ "key type.",
+ .command = COMMAND_LIST,
+ },
/***********************************************************/
{
.flags = UTIL_OPT_FLAG_SECTION,
@@ -745,6 +765,38 @@ static struct util_opt opt_vec[] = {
.flags = UTIL_OPT_FLAG_NOSHORT,
},
#endif
+ /***********************************************************/
+ {
+ .flags = UTIL_OPT_FLAG_SECTION,
+ .desc = "OPTIONS",
+ .command = COMMAND_CONVERT,
+ },
+ {
+ .option = { "name", required_argument, NULL, 'N'},
+ .argument = "NAME",
+ .desc = "Name of the secure AES key in the repository that is "
+ "to be converted",
+ .command = COMMAND_CONVERT,
+ },
+ {
+ .option = { "key-type", required_argument, NULL, 'K'},
+ .argument = "type",
+ .desc = "The type of the key to convert the secure key to. "
+ "Possible values are '"KEY_TYPE_CCA_AESCIPHER"'. ",
+ .command = COMMAND_CONVERT,
+ },
+ {
+ .option = {"no-apqn-check", 0, NULL, OPT_NO_APQN_CHECK},
+ .desc = "Do not check if the associated APQN(s) are available",
+ .command = COMMAND_CONVERT,
+ .flags = UTIL_OPT_FLAG_NOSHORT,
+ },
+ {
+ .option = {"force", 0, NULL, 'F'},
+ .desc = "Do not prompt for a confirmation when converting a "
+ "key",
+ .command = COMMAND_CONVERT,
+ },
/***********************************************************/
{
.flags = UTIL_OPT_FLAG_SECTION,
@@ -793,6 +845,7 @@ static int command_rename(void);
static int command_copy(void);
static int command_crypttab(void);
static int command_cryptsetup(void);
+static int command_convert(void);
static struct zkey_command zkey_commands[] = {
{
@@ -927,6 +980,21 @@ static struct zkey_command zkey_commands[] = {
.has_options = 1,
.need_keystore = 1,
},
+ {
+ .command = COMMAND_CONVERT,
+ .abbrev_len = 3,
+ .function = command_convert,
+ .need_cca_library = 1,
+ .need_pkey_device = 1,
+ .short_desc = "Convert a secure AES key",
+ .long_desc = "Convert an existing secure AES key that is "
+ "either contained in SECURE-KEY-FILE or is stored "
+ "in the repository from one key type to another "
+ "type.",
+ .has_options = 1,
+ .pos_arg = "[SECURE-KEY-FILE]",
+ .pos_arg_optional = 1,
+ },
{ .command = NULL }
};
@@ -1009,9 +1077,8 @@ static int command_generate_clear(void)
rc = generate_secure_key_clear(g.pkey_fd, g.pos_arg,
g.keybits, g.xts,
- g.clearkeyfile,
- AUTOSELECT, AUTOSELECT,
- g.verbose);
+ g.clearkeyfile, g.key_type,
+ NULL, g.verbose);
return rc != 0 ? EXIT_FAILURE : EXIT_SUCCESS;
}
@@ -1026,9 +1093,8 @@ static int command_generate_random(void)
int rc;
rc = generate_secure_key_random(g.pkey_fd, g.pos_arg,
- g.keybits, g.xts,
- AUTOSELECT, AUTOSELECT,
- g.verbose);
+ g.keybits, g.xts, g.key_type,
+ NULL, g.verbose);
return rc != 0 ? EXIT_FAILURE : EXIT_SUCCESS;
}
@@ -1048,7 +1114,7 @@ static int command_generate_repository(void)
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.pkey_fd);
+ g.volume_type, g.key_type, g.pkey_fd);
return rc != 0 ? EXIT_FAILURE : EXIT_SUCCESS;
}
@@ -1075,6 +1141,8 @@ static int command_generate(void)
util_prg_print_parse_error();
return EXIT_FAILURE;
}
+ if (g.key_type == NULL)
+ g.key_type = KEY_TYPE_CCA_AESDATA;
if (g.name != NULL)
return command_generate_repository();
if (g.pos_arg != NULL) {
@@ -1103,7 +1171,9 @@ static int command_generate(void)
return EXIT_FAILURE;
}
- rc = cross_check_apqns(NULL, 0, true, g.verbose);
+ rc = cross_check_apqns(NULL, 0,
+ get_min_card_level_for_keytype(g.key_type),
+ true, g.verbose);
if (rc == -EINVAL)
return EXIT_FAILURE;
if (rc != 0 && rc != -ENOTSUP) {
@@ -1169,7 +1239,7 @@ static int command_reencipher_file(void)
return EXIT_FAILURE;
rc = validate_secure_key(g.pkey_fd, secure_key, secure_key_size, NULL,
- &is_old_mk, g.verbose);
+ &is_old_mk, NULL, g.verbose);
if (rc != 0) {
warnx("The secure key in file '%s' is not valid", g.pos_arg);
rc = EXIT_FAILURE;
@@ -1385,14 +1455,14 @@ static int command_validate_file(void)
return EXIT_FAILURE;
rc = validate_secure_key(g.pkey_fd, secure_key, secure_key_size,
- &clear_key_size, &is_old_mk, g.verbose);
+ &clear_key_size, &is_old_mk, NULL, g.verbose);
if (rc != 0) {
warnx("The secure key in file '%s' is not valid", g.pos_arg);
rc = EXIT_FAILURE;
goto out;
}
- rc = generate_key_verification_pattern((char *)secure_key,
+ rc = generate_key_verification_pattern(secure_key,
secure_key_size, vp, sizeof(vp),
g.verbose);
if (rc != 0) {
@@ -1416,9 +1486,11 @@ static int command_validate_file(void)
printf("Validation of secure key in file '%s':\n", g.pos_arg);
printf(" Status: Valid\n");
printf(" Secure key size: %lu bytes\n", secure_key_size);
+ printf(" Key type: %s\n",
+ get_key_type(secure_key, secure_key_size));
printf(" Clear key size: %lu bits\n", clear_key_size);
printf(" XTS type key: %s\n",
- secure_key_size > SECURE_KEY_SIZE ? "Yes" : "No");
+ is_xts_key(secure_key, secure_key_size) ? "Yes" : "No");
printf(" Enciphered with: %s CCA master key (MKVP: %016llx)\n",
is_old_mk ? "OLD" : "CURRENT", mkvp);
printf(" Verification pattern: %.*s\n", VERIFICATION_PATTERN_LEN / 2,
@@ -1426,7 +1498,10 @@ static int command_validate_file(void)
printf(" %.*s\n", VERIFICATION_PATTERN_LEN / 2,
&vp[VERIFICATION_PATTERN_LEN / 2]);
- rc = cross_check_apqns(NULL, mkvp, true, g.verbose);
+ rc = cross_check_apqns(NULL, mkvp,
+ get_min_card_level_for_keytype(
+ get_key_type(secure_key, secure_key_size)),
+ true, g.verbose);
if (rc == -EINVAL)
return EXIT_FAILURE;
if (rc != 0 && rc != -ENOTSUP) {
@@ -1496,7 +1571,7 @@ static int command_import(void)
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.pos_arg, g.volume_type, &g.cca);
return rc != 0 ? EXIT_FAILURE : EXIT_SUCCESS;
}
@@ -1530,7 +1605,7 @@ static int command_list(void)
int rc;
rc = keystore_list_keys(g.keystore, g.name, g.volumes, g.apqns,
- g.volume_type);
+ g.volume_type, g.key_type);
return rc != 0 ? EXIT_FAILURE : EXIT_SUCCESS;
}
@@ -1665,6 +1740,206 @@ static int command_cryptsetup(void)
return rc != 0 ? EXIT_FAILURE : EXIT_SUCCESS;
}
+/*
+ * Command handler for 'convert'.
+ *
+ * Converts secure keys from one key type to another
+ */
+static int command_convert_file(void)
+{
+ u8 output_key[2 * MAX_SECURE_KEY_SIZE];
+ unsigned int output_key_size;
+ size_t secure_key_size;
+ int rc, is_old_mk;
+ int selected = 1;
+ u8 *secure_key;
+ int min_level;
+ u64 mkvp;
+
+ if (g.name != NULL) {
+ warnx("Option '--name|-N' is not valid for "
+ "re-enciphering a key outside of the repository");
+ util_prg_print_parse_error();
+ return EXIT_FAILURE;
+ }
+ if (g.noapqncheck) {
+ warnx("Option '--no-apqn-check' is not valid for "
+ "converting a key outside of the repository");
+ util_prg_print_parse_error();
+ return EXIT_FAILURE;
+ }
+
+ min_level = get_min_card_level_for_keytype(g.key_type);
+ if (min_level < 0) {
+ warnx("Invalid key-type specified: %s", g.key_type);
+ return EXIT_FAILURE;
+ }
+
+ rc = cross_check_apqns(NULL, 0, min_level, true, g.verbose);
+ if (rc == -EINVAL)
+ return EXIT_FAILURE;
+ if (rc != 0 && rc != -ENOTSUP) {
+ warnx("Your master key setup is improper");
+ return EXIT_FAILURE;
+ }
+
+ /* Read the secure key to be re-enciphered */
+ secure_key = read_secure_key(g.pos_arg, &secure_key_size, g.verbose);
+ if (secure_key == NULL)
+ return EXIT_FAILURE;
+
+ rc = validate_secure_key(g.pkey_fd, secure_key, secure_key_size, NULL,
+ &is_old_mk, NULL, g.verbose);
+ if (rc != 0) {
+ warnx("The secure key in file '%s' is not valid", g.pos_arg);
+ rc = EXIT_FAILURE;
+ goto out;
+ }
+
+ rc = get_master_key_verification_pattern(secure_key, secure_key_size,
+ &mkvp, g.verbose);
+ if (rc != 0) {
+ warnx("Failed to get the master key verification pattern: %s",
+ strerror(-rc));
+ rc = EXIT_FAILURE;
+ goto out;
+ }
+
+ if (strcasecmp(get_key_type(secure_key, secure_key_size),
+ g.key_type) == 0) {
+ warnx("The secure key in file '%s' is already of type %s",
+ g.pos_arg, get_key_type(secure_key, secure_key_size));
+ rc = EXIT_FAILURE;
+ goto out;
+ }
+
+ if (is_cca_aes_data_key(secure_key, secure_key_size)) {
+ if (strcasecmp(g.key_type, KEY_TYPE_CCA_AESCIPHER) != 0) {
+ warnx("The secure key in file '%s' can not be "
+ "converted into type %s", g.pos_arg, g.key_type);
+ rc = EXIT_FAILURE;
+ goto out;
+ }
+ } else if (is_cca_aes_cipher_key(secure_key, secure_key_size)) {
+ warnx("The secure key in file '%s' is already of type %s",
+ g.pos_arg, KEY_TYPE_CCA_AESCIPHER);
+ rc = EXIT_FAILURE;
+ goto out;
+ } else {
+ warnx("The secure key in file '%s' has an unsupported key type",
+ g.pos_arg);
+ rc = EXIT_FAILURE;
+ goto out;
+ }
+
+ rc = select_cca_adapter_by_mkvp(&g.cca, mkvp, NULL,
+ FLAG_SEL_CCA_MATCH_CUR_MKVP,
+ g.verbose);
+ if (rc == -ENOTSUP) {
+ rc = 0;
+ selected = 0;
+ }
+ if (rc != 0) {
+ warnx("No APQN found that is suitable for "
+ "converting the secure AES key in file '%s'", g.pos_arg);
+ rc = EXIT_FAILURE;
+ goto out;
+ }
+
+ if (!g.force) {
+ util_print_indented("ATTENTION: Converting a secure key is "
+ "irreversible, and might have an effect "
+ "on the volumes encrypted with it!", 0);
+ printf("%s: Convert key in file '%s' [y/N]? ",
+ program_invocation_short_name, g.pos_arg);
+ if (!prompt_for_yes(g.verbose)) {
+ warnx("Operation aborted");
+ rc = EXIT_FAILURE;
+ goto out;
+ }
+ }
+
+ memset(output_key, 0, sizeof(output_key));
+ output_key_size = sizeof(output_key);
+ rc = convert_aes_data_to_cipher_key(&g.cca, secure_key, secure_key_size,
+ output_key, &output_key_size,
+ g.verbose);
+ if (rc != 0) {
+ warnx("Converting the secure key from %s to %s has failed",
+ get_key_type(secure_key, secure_key_size), g.key_type);
+ if (!selected)
+ print_msg_for_cca_envvars("secure AES key");
+ rc = EXIT_FAILURE;
+ goto out;
+ }
+
+ rc = restrict_key_export(&g.cca, output_key, output_key_size,
+ g.verbose);
+ if (rc != 0) {
+ warnx("Export restricting the converted secure key has failed");
+ if (!selected)
+ print_msg_for_cca_envvars("secure AES key");
+ rc = EXIT_FAILURE;
+ goto out;
+ }
+
+ pr_verbose("Secure key was converted successfully");
+
+ /* Write the converted secure key */
+ rc = write_secure_key(g.outputfile ? g.outputfile : g.pos_arg,
+ output_key, output_key_size, g.verbose);
+ if (rc != 0)
+ rc = EXIT_FAILURE;
+out:
+ free(secure_key);
+ return rc;
+}
+
+/*
+ * Command handler for 'convert in repository'.
+ *
+ * Converts secure keys from one key type to another
+ */
+static int command_convert_repository(void)
+{
+ int rc;
+
+ if (g.name == NULL) {
+ misc_print_required_parm("--name/-N");
+ return EXIT_FAILURE;
+ }
+
+ rc = keystore_convert_key(g.keystore, g.name, g.key_type, g.noapqncheck,
+ g.force, g.pkey_fd, &g.cca);
+
+ return rc != 0 ? EXIT_FAILURE : EXIT_SUCCESS;
+}
+
+/*
+ * Command handler for 'convert'.
+ *
+ * Converts secure keys from one key type to another
+ */
+static int command_convert(void)
+{
+ if (g.key_type == NULL) {
+ misc_print_required_parm("--key-type/-K");
+ return EXIT_FAILURE;
+ }
+ if (strcasecmp(g.key_type, KEY_TYPE_CCA_AESCIPHER) != 0) {
+ warnx("Secure keys can only be converted into key type %s",
+ KEY_TYPE_CCA_AESCIPHER);
+ return EXIT_FAILURE;
+ }
+
+ if (g.pos_arg != NULL)
+ return command_convert_file();
+ else
+ return command_convert_repository();
+
+ return EXIT_SUCCESS;
+}
+
/**
* Opens the keystore. The keystore directory is either the
* default directory or as specified in an environment variable
@@ -1851,6 +2126,9 @@ int main(int argc, char *argv[])
case 'r':
g.run = 1;
break;
+ case 'K':
+ g.key_type = optarg;
+ break;
case 'F':
g.force = 1;
break;
--
2.21.3
From a9fe5d1477161d658bcade0f85309eefe9fcb0bc Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Dan=20Hor=C3=A1k?= <dan@danny.cz>
Date: Thu, 7 Nov 2019 13:29:28 +0100
Subject: [PATCH 36/44] zcrypt: CEX7S exploitation support (#1723837)
Description: CEX7S exploitation support to lszcrypt, chzcrypt
and zcryptstats.
Upstream-ID: b631c74df57e9ea52f54b4d6be79b63bc89e5eee
Upstream-ID: e9c030f2026b1b8e0399679600845c298aeb508d
Upstream-ID: 784ac7d190080a3cf230995eedd3743bddfa4b5c
Upstream-ID: 4fc0c3cfefb8fb23a83ef629ac3f4a967fc0e77f
Upstream-ID: e15e8a1bfa15e2179f30c6fe2e937ddd1a5e53c1
---
zconf/zcrypt/chzcrypt.8 | 4 +--
zconf/zcrypt/chzcrypt.c | 4 +--
zconf/zcrypt/lszcrypt.8 | 66 ++++++++++++++++++++++++++++++++++++--
zconf/zcrypt/lszcrypt.c | 20 +++++++-----
zconf/zcrypt/zcryptstats.8 | 5 +++
zconf/zcrypt/zcryptstats.c | 27 ++++++++++------
6 files changed, 102 insertions(+), 24 deletions(-)
diff --git a/zconf/zcrypt/chzcrypt.8 b/zconf/zcrypt/chzcrypt.8
index 2021645..2697c96 100644
--- a/zconf/zcrypt/chzcrypt.8
+++ b/zconf/zcrypt/chzcrypt.8
@@ -1,8 +1,8 @@
-.\" Copyright 2017 IBM Corp.
+.\" Copyright 2019 IBM Corp.
.\" s390-tools is free software; you can redistribute it and/or modify
.\" it under the terms of the MIT license. See LICENSE for details.
.\"
-.TH CHZCRYPT 8 "OCT 2017" "s390-tools"
+.TH CHZCRYPT 8 "AUG 2019" "s390-tools"
.SH NAME
chzcrypt \- modify zcrypt configuration
.SH SYNOPSIS
diff --git a/zconf/zcrypt/chzcrypt.c b/zconf/zcrypt/chzcrypt.c
index 6521056..2fc8441 100644
--- a/zconf/zcrypt/chzcrypt.c
+++ b/zconf/zcrypt/chzcrypt.c
@@ -1,7 +1,7 @@
/*
* chzcrypt - Tool to modify zcrypt configuration
*
- * Copyright IBM Corp. 2008, 2017
+ * Copyright IBM Corp. 2008, 2019
*
* s390-tools is free software; you can redistribute it and/or modify
* it under the terms of the MIT license. See LICENSE for details.
@@ -47,7 +47,7 @@ const struct util_prg prg = {
{
.owner = "IBM Corp.",
.pub_first = 2008,
- .pub_last = 2017,
+ .pub_last = 2019,
},
UTIL_PRG_COPYRIGHT_END
}
diff --git a/zconf/zcrypt/lszcrypt.8 b/zconf/zcrypt/lszcrypt.8
index 826e109..f9ce4a5 100644
--- a/zconf/zcrypt/lszcrypt.8
+++ b/zconf/zcrypt/lszcrypt.8
@@ -1,6 +1,6 @@
.\" lszcrypt.8
.\"
-.\" Copyright 2017 IBM Corp.
+.\" Copyright 2019 IBM Corp.
.\" s390-tools is free software; you can redistribute it and/or modify
.\" it under the terms of the MIT license. See LICENSE for details.
.\"
@@ -10,7 +10,7 @@
.\" nroff -man lszcrypt.8
.\" to process this source
.\"
-.TH LSZCRYPT 8 "OCT 2017" "s390-tools"
+.TH LSZCRYPT 8 "AUG 2019" "s390-tools"
.SH NAME
lszcrypt \- display zcrypt device and configuration information
.SH SYNOPSIS
@@ -111,6 +111,68 @@ Displays help text and exits.
.TP 8
.B -v, --version
Displays version information and exits.
+.SH VERBOSE LISTING DETAILS
+Some of the columns showing up in verbose listing mode may need some
+explanation:
+.TP
+.B TYPE and HWTYPE
+The HWTYPE is a numeric value showing which type of hardware the zcrypt
+device driver presumes that this crypto card is. The currently known values
+are 7=CEX3C, 8=CEX3A, 10=CEX4, 11=CEX5, 12=CEX6 and 13=CEX7.
+.br
+The TYPE is a human readable value showing the hardware type and the basic
+function type (A=Accelerator, C=CCA Coprocessor, P=EP11 Coprocessor). So
+for example CEX6P means a CEX6 card in EP11 Coprocessor mode.
+.TP
+.B REQUESTS
+This is the counter value of successful processed requests on card or queue
+level. Successful here means the request was processed without any failure
+in the whole processing chain.
+.TP
+.B PENDING
+The underlying firmware and hardware layer usually provide some queuing
+space for requests. When this queue is already filled up, the zcrypt device
+driver maintains a software queue of pending requests. The sum of these
+both values is displayed here and shows the amount of requests waiting for
+processing on card or queue level.
+.TP
+.B FUNCTIONS
+This column shows firmware and hardware function details:
+.br
+S - APSC available: card/queue can handle requests with the special bit
+enabled.
+.br
+M - Accelerator card/queue with support for RSA ME with up to 4k key size.
+.br
+C - Accelerator card/queue with support for RSA CRT with up to 4k key size.
+.br
+D - Card/queue is providing CCA functions (this is the CCA Coprocessor mode).
+.br
+A - Card/queue is providing Accelerator functions (this is the Accelerator mode).
+.br
+X - Card/queue is providing EP11 functions (this is the EP11 Coprocessor mode).
+.br
+N - APXA available (ability to address more than 16 crypto cards and domains).
+.br
+F - Full function support (opposed to restricted function support, see below).
+.br
+R - Restricted function support. The F and R flag both reflect if a
+hypervisor is somehow restricting this crypto resource in a virtual
+environment. Dependent on the hypervisor configuration the crypto requests
+may be filtered by the hypervisor to allow only a subset of functions
+within the virtual runtime environment. For example a shared CCA
+Coprocessor may be restricted by the hypervisor to allow only clear key
+operations within the guests.
+.TP
+.B DRIVER
+.br
+Shows which card or queue device driver currently handles this crypto
+resource. Currently known drivers are cex4card/cex4queue (CEX4-CEX7
+hardware), cex2card/cex2cqueue (CEX2C and CEX3C hardware),
+cex2acard/cex2aqueue (CEX2A and CEX3A hardware) and vfio_ap (queue reserved
+for use by kvm hypervisor for kvm guests and not accessible to host
+applications). It is also valid to have no driver handling a queue which is
+shown as a -no-driver- entry.
.SH EXAMPLES
.TP
.B lszcrypt
diff --git a/zconf/zcrypt/lszcrypt.c b/zconf/zcrypt/lszcrypt.c
index 580407f..1b2befe 100644
--- a/zconf/zcrypt/lszcrypt.c
+++ b/zconf/zcrypt/lszcrypt.c
@@ -1,7 +1,7 @@
/**
* lszcrypt - Display zcrypt devices and configuration settings
*
- * Copyright IBM Corp. 2008, 2018
+ * Copyright IBM Corp. 2008, 2019
*
* s390-tools is free software; you can redistribute it and/or modify
* it under the terms of the MIT license. See LICENSE for details.
@@ -12,6 +12,7 @@
#include "lib/util_base.h"
#include "lib/util_file.h"
+#include "lib/util_libc.h"
#include "lib/util_opt.h"
#include "lib/util_panic.h"
#include "lib/util_path.h"
@@ -85,7 +86,7 @@ const struct util_prg prg = {
{
.owner = "IBM Corp.",
.pub_first = 2008,
- .pub_last = 2018,
+ .pub_last = 2019,
},
UTIL_PRG_COPYRIGHT_END
}
@@ -301,9 +302,10 @@ static void show_capability(const char *id_str)
printf("%s\n", CAP_CCA);
printf("%s", CAP_RNG);
break;
- case 10:
- case 11:
- case 12:
+ case 10: /* CEX4S */
+ case 11: /* CEX5S */
+ case 12: /* CEX6S */
+ case 13: /* CEX7S */
if (func_val & MASK_ACCEL) {
if (func_val & MASK_RSA4K)
printf("%s", CAP_RSA4K);
@@ -620,7 +622,7 @@ static void show_devices_argv(char *argv[])
{
struct util_rec *rec = util_rec_new_wide("-");
struct dirent **dev_vec, **subdev_vec;
- char *ap, *grp_dev, *path, card[16], sub_dev[16];
+ char *ap, *grp_dev, *path, *card, *sub_dev;
int id, dom, i, n, dev_cnt, sub_cnt;
/* check if ap driver is available */
@@ -639,14 +641,16 @@ static void show_devices_argv(char *argv[])
if (sscanf(argv[i], "%x.%x", &id, &dom) >= 1) {
/* at least the id field was valid */
if (id >= 0 && dom >= 0) { /* single subdevice */
- sprintf(sub_dev, "%02x.%04x", id, dom);
+ util_asprintf(&sub_dev, "%02x.%04x", id, dom);
grp_dev = util_path_sysfs("devices/ap/card%02x",
id);
show_subdevice(rec, grp_dev, sub_dev);
free(grp_dev);
+ free(sub_dev);
} else { /* group device */
- sprintf(card, "card%02x", id);
+ util_asprintf(&card, "card%02x", id);
show_device(rec, card);
+ free(card);
}
return;
}
diff --git a/zconf/zcrypt/zcryptstats.8 b/zconf/zcrypt/zcryptstats.8
index 8c31c69..7d000d2 100644
--- a/zconf/zcrypt/zcryptstats.8
+++ b/zconf/zcrypt/zcryptstats.8
@@ -72,6 +72,11 @@ the parallel execution of cryptographic operations.
Cryptographic performance measurement data might not be available when Linux
is running as guest under z/VM or under KVM. \fBzcryptstats\fP then displays an
error message and exits.
+.PP
+.B Note:
+\fBzcryptstats\fP utilizes the device node \fB/dev/chsc\fP. When this device
+node is not available, you might have to load kernel module \fBchsc_sch\fP using
+\fBmodprobe chsc_sch\fP to make it available.
.
.
.
diff --git a/zconf/zcrypt/zcryptstats.c b/zconf/zcrypt/zcryptstats.c
index 136d5ba..3bb2078 100644
--- a/zconf/zcrypt/zcryptstats.c
+++ b/zconf/zcrypt/zcryptstats.c
@@ -147,8 +147,9 @@ struct chsc_cmb_area {
#define CRYPTO_TYPE_CEX4S 10
#define CRYPTO_TYPE_CEX5S 11
#define CRYPTO_TYPE_CEX6S 12
+#define CRYPTO_TYPE_CEX7S 13
-#define CRYPTO_TYPE_TOLERATION CRYPTO_TYPE_CEX6S
+#define CRYPTO_TYPE_TOLERATION CRYPTO_TYPE_CEX7S
struct crypto_counter {
const char *name;
@@ -235,8 +236,8 @@ const struct crypto_mode mode_pcica[1] = {
.counters = counter_pcica },
};
-#define NUM_CEX456_MODES 11
-const struct crypto_mode mode_cex456[NUM_CEX456_MODES] = {
+#define NUM_CEX4567_MODES 11
+const struct crypto_mode mode_cex4567[NUM_CEX4567_MODES] = {
{ 0 },
{ 0 },
{ 0 },
@@ -256,7 +257,7 @@ const struct crypto_mode mode_cex456[NUM_CEX456_MODES] = {
.counters = counter_ep11 },
};
-#define NUM_CRYPTO_TYPES 13
+#define NUM_CRYPTO_TYPES 14
const struct crypto_type crypto_types[NUM_CRYPTO_TYPES] = {
{ 0 },
{ 0 },
@@ -275,12 +276,14 @@ const struct crypto_type crypto_types[NUM_CRYPTO_TYPES] = {
.modes = mode_accel },
{ .name = "CEX3C", .num_modes = NUM_COPROC_MODES,
.modes = mode_coproc },
- { .name = "CEX4", .num_modes = NUM_CEX456_MODES,
- .modes = mode_cex456 },
- { .name = "CEX5", .num_modes = NUM_CEX456_MODES,
- .modes = mode_cex456 },
- { .name = "CEX6", .num_modes = NUM_CEX456_MODES,
- .modes = mode_cex456 },
+ { .name = "CEX4", .num_modes = NUM_CEX4567_MODES,
+ .modes = mode_cex4567 },
+ { .name = "CEX5", .num_modes = NUM_CEX4567_MODES,
+ .modes = mode_cex4567 },
+ { .name = "CEX6", .num_modes = NUM_CEX4567_MODES,
+ .modes = mode_cex4567 },
+ { .name = "CEX7", .num_modes = NUM_CEX4567_MODES,
+ .modes = mode_cex4567 },
};
@@ -2393,7 +2396,11 @@ int main(int argc, char *argv[])
g.chsc_fd = open(CHSC_DEVICE, O_RDWR);
if (g.chsc_fd < 0) {
+ rc = errno;
warnx("File '%s:' %s", CHSC_DEVICE, strerror(errno));
+ if (rc == ENOENT)
+ warnx("You might have to load kernel module 'chsc_sch' "
+ "using 'modprobe chsc_sch'");
return EXIT_FAILURE;
}
pr_verbose("Device '%s' has been opened successfully", CHSC_DEVICE);
--
2.21.3
From afcf268f8af2a6e925c44c057cb04c2dc38c9b10 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Dan=20Hor=C3=A1k?= <dan@danny.cz>
Date: Thu, 7 Nov 2019 13:48:23 +0100
Subject: [PATCH 37/44] lstape, lsluns: handle non-zfcp; lin_tape multiple
paths (#1766569)
Symptom: lstape shows unexpected additional Device suffix numbers in
excess output columns for each additional path of the same
tape/changer driven by the IBM lin_tape device driver
(independent of actual path failover enablement in lin_tape).
It also shows a wrong number of found devices in the header
(without --scsi-only).
lstape prints error about "Unexpected extra argument:" and the
usage for "sg_inq" along with wrong tabular output.
lsluns prints ENOENT error text for "cat" on SCSI device sysfs
attributes hba_id, wwpn, and fcp_lun.
lstape with --verbose option prints ENOENT error text for
"cat" on SCSI device sysfs attributes hba_id and wwpn.
lstape man page: Description of --type and <devbusid> filter
for channel tapes is incomplete. SCSI output description is
incomplete.
lstape shows "N/A" instead of the HBA device bus-ID with
virtio-scsi-ccw.
Problem: s390-tools-1.8.0 before the first upstream commit b627b8d8e1ab
("Initial s390-tools-2.0.0 import") introduced SCSI
tape/changer output for lstape. It used the SCSI device serial
number as lookup key to find a match in IBM lin_tape device
driver proc-fs output for a given SCSI device name. Multiple
paths to the same tape/changer have the same serial number.
Multiple matches cause excess arguments to printf. Explaining
the resulting output, the bash man page says: "The format is
reused as necessary to consume all of the arguments." This
also causes a wrong number of found devices.
The default bash settings have nullglob disabled so if
$SCSI_DEV/scsi_generic* aka
/sys/bus/scsi/devices/*:*:*:*/scsi_generic* does not match
anything, it leaves the glob pattern unmodified and
SG_DEV=$(basename $SG_DEV/*) results in the literal "*". If
$SG_INQ exists, it invoked sg_inq with more than the one
allowed positional argument for a SCSI generic device node
"sg_inq /dev/*". Causing error messages and the usage of
sg_inq to land in $TAPE_SERIAL.
lsluns iterates SCSI generic devices and unconditionally
reads zfcp-specific SCSI device sysfs attributes hba_id, wwpn,
and fcp_lun.
lstape --verbose unconditionally reads zfcp-specific SCSI
device sysfs attributes hba_id and wwpn.
<devbusid> filter missing from synopsis. <device-type> example
at wrong place with <devbusid> filter. <devbusid> filter
option description is a duplicate of <device-type> filter
option description. SCSI output description misses fields.
Lstape only used the zfcp-specific sysfs attribute hba_id.
Solution: Prefer sysfs to find lin_tape device name for SCSI device.
Fallback: The lin_tape proc-fs output format has changed over
the years. The HBA device driver string can contain whitespace
(e.g. "Virtio SCSI HBA") and breaks the field numbers with
tokenized parsing. Grep for the SCSI device name as word (to
skip names with same substring, such as 0:0:1:1 also matching
0:0:1:10) and cut the first field 'Number' (lin_tape device
name suffix). If there is no SCSI column at all [lin_tape
before v2.2.0] (and no SCSI LLDD or other column with a name
accidentally matching an existing SCSI device name), we get no
match and better bail out with the initialized "N/A" for the
lstape column "Device".
To not have to rely on the nullglob setting, explicitly check
for the existence of $SCSI_DEV/scsi_generic before evaluating
SG_DEV=$(basename $SG_DEV/*). Also handle availability of
sg_inq but absence of scsi_generic individually to provide the
user with a hint if only sg is missing.
Simply skip non-zfcp SCSI devices, such as iSCSI or
virtio-scsi-ccw, to not erroneously access absent attributes.
Assume "N/A" for HBA and WWPN of non-zfcp SCSI devices, such
as iSCSI or virtio-scsi-ccw, to not erroneously access absent
zfcp-specific sysfs attributes.
Add <devbusid> filter to synopsis. Move <device-type> example
to <device-type> option. Replace <devbusid> filter option
description. Move existing SCSI output description to a new
subsection and add description of missing fields.
Also search sysfs for an ancestor with subsystem ccw.
Reproduction: Attach more than one path to the same SCSI tape or changer and
load the IBM lin_tape device driver.
Unload sg kernel module.
Attach non-zfcp SCSI devices such as iSCSI or virtio-scsi-ccw.
Attach non-zfcp SCSI devices such as iSCSI or virtio-scsi-ccw.
man lstape
Attach SCSI tape or changer with virtio-scsi-ccw to a KVM
guest and run "lstape --verbose".
Upstream-ID: ef4dc7a45b1b80c58076a0a317245569d84b2754
Upstream-ID: cdc787db1ba17dd2c0ed841fd7443bc32be63b65
Upstream-ID: 80e0c41b896e1eeffc594416b4c2787ed29363b6
Upstream-ID: eba744a25edcc7202068c1d46fd99d4c98c1acf2
Upstream-ID: 4cf8f5f46c0f80b49a2a70854372289c3fda932b
Upstream-ID: b9b3d2d23069e021693a251a3aadd4eefc30e6ea
Upstream-ID: 34260f17360f1034c562c941b0f879c2204e40b7
Upstream-ID: d5291eed1c46e1c97831e771b2975575ba268992
---
zconf/lsluns | 14 ++++++---
zconf/lsluns.8 | 5 ++-
zconf/lstape | 69 ++++++++++++++++++++++++++++++++++-------
zconf/lstape.8 | 84 +++++++++++++++++++++++++++++++++++++++-----------
4 files changed, 135 insertions(+), 37 deletions(-)
diff --git a/zconf/lsluns b/zconf/lsluns
index f88aa0f..bfe48f1 100755
--- a/zconf/lsluns
+++ b/zconf/lsluns
@@ -2,7 +2,7 @@
#
# lsluns - list LUNs discovered in the FC SAN, or show encryption state of attached LUNs
#
-# Copyright IBM Corp. 2008, 2017
+# Copyright IBM Corp. 2008, 2018
#
# s390-tools is free software; you can redistribute it and/or modify
# it under the terms of the MIT license. See LICENSE for details.
@@ -152,6 +152,11 @@ sub get_lun_hash
my %lun_hash;
foreach my $device (</$sg_dir/sg*>) {
+ # skip non-zfcp SCSI devices and avoid file access error messages
+ next unless -r "$device/device/fcp_lun";
+ next unless -r "$device/device/wwpn";
+ next unless -r "$device/device/hba_id";
+
my $l = `cat $device/device/fcp_lun`;
my $p = `cat $device/device/wwpn`;
my $a = `cat $device/device/hba_id`;
@@ -170,9 +175,8 @@ sub get_lun_hash
sub lsluns_usage {
print <<EOD;
Usage:
-This tool is designed for environments where all SCSI devices are attached
-through the zfcp device driver. Expect error messages in mixed environments
-such as with iSCSI.
+This tool is designed for environments with SCSI devices attached
+through the zfcp device driver.
$PROGRAM_NAME [-c <busid>] ... [-p <wwpn>] ... [-h] [-v]
@@ -220,7 +224,7 @@ EOD
sub lsluns_version {
print "$PROGRAM_NAME: version %S390_TOOLS_VERSION%\n";
- print "Copyright IBM Corp. 2008, 2017\n";
+ print "Copyright IBM Corp. 2008, 2018\n";
}
sub lsluns_invalid_usage {
diff --git a/zconf/lsluns.8 b/zconf/lsluns.8
index cca649a..a4a1849 100644
--- a/zconf/lsluns.8
+++ b/zconf/lsluns.8
@@ -28,9 +28,8 @@ zfcp-attached LUNs
.SH DESCRIPTION
.PP
-This tool is designed for environments where all SCSI devices are attached
-through the zfcp device driver. Expect error messages in mixed environments
-such as with iSCSI.
+This tool is designed for environments with SCSI devices attached
+through the zfcp device driver.
.B lsluns
lists all logical unit numbers (LUNs) discovered in the
diff --git a/zconf/lstape b/zconf/lstape
index 3090451..e2410e1 100755
--- a/zconf/lstape
+++ b/zconf/lstape
@@ -2,7 +2,7 @@
#
# lstape - Tool to show information about tape devices
#
-# Copyright IBM Corp. 2003, 2017
+# Copyright IBM Corp. 2003, 2018
#
# s390-tools is free software; you can redistribute it and/or modify
# it under the terms of the MIT license. See LICENSE for details.
@@ -48,6 +48,9 @@ function PrintUsage() {
: -v|--version
: Display the version of the tools package and
: the lstape command.
+ :
+ :$(basename $0) without the --ccw-only option causes extra SAN traffic
+ :for each SCSI tape or changer device by invoking the sg_inq command.
EOD
}
@@ -55,7 +58,7 @@ function PrintVersion()
{
cat <<-EOD
$CMD: version %S390_TOOLS_VERSION%
- Copyright IBM Corp. 2003, 2017
+ Copyright IBM Corp. 2003, 2018
EOD
}
@@ -220,6 +223,24 @@ function SysfsCreateListCCW() {
' | sort
}
+# handle SCSI device not necessarily zfcp-attached, e.g. virtio-scsi-ccw
+function SCSISearchCCWBusid()
+{
+ local SCSI_DEV=$1
+ local SDEVCAN=$(readlink -e $SCSI_DEV)
+ while [ -n "$SDEVCAN" ]; do
+ # ascend to parent: strip last path part
+ SDEVCAN=${SDEVCAN%/*}
+ [ -h $SDEVCAN/subsystem ] || continue
+ local SUBSYSTEM=$(readlink -e $SDEVCAN/subsystem)
+ if [ "${SUBSYSTEM##*/}" = "ccw" ]; then
+ echo ${SDEVCAN##*/}
+ return
+ fi
+ done
+ echo "N/A"
+}
+
function SysfsCreateListSCSI()
{
for SCSI_DEV in $1/bus/scsi/devices/*:*:*:*; do
@@ -249,11 +270,16 @@ function SysfsCreateListSCSI()
if [ -h $SG_DEV ]; then
# deprecated sysfs layout
SG_DEV=$(echo $SG_DEV | awk -F: '{print $NF}')
- else
+ elif [ -d $SCSI_DEV/scsi_generic ]; then
SG_DEV=$(basename $SG_DEV/*)
+ else
+ SG_DEV=""
fi
- if [ "$SG_INQ" != "" ]; then
+ if [ -z "$SG_DEV" ]; then
+ SG_DEV="N/A"
+ TAPE_SERIAL="NO/SG"
+ elif [ "$SG_INQ" != "" ]; then
TAPE_SERIAL=$(
sg_inq /dev/$SG_DEV |
awk '/serial/{print $NF}'
@@ -291,17 +317,31 @@ function SysfsCreateListSCSI()
if [ "$CHG_IDX" != "" ]; then
TAPE_DEV=$CHG_IDX
fi
+ elif [ "$(echo "$SCSI_LIST"|grep lin_tape)" != "" ]; then
+ # bash glob sorts so IBMtape0 comes before IBMtape0n
+ local IBM_PATH=$(
+ ls -1d $SCSI_DEV/lin_tape/$DEV_NAME[0-9]* |
+ head -n 1)
+ if [ -d "$IBM_PATH" ]; then
+ IBM_IDX=${IBM_PATH##*/}
+ else
+ # deprecated sysfs layout
+ IBM_IDX=$(
+ echo "$SCSI_LIST" |
+ awk -F: '/lin_tape\:'"$DEV_NAME"'[0-9]+$/{print $NF}'
+ )
+ fi
+ if [ "$IBM_IDX" != "" ]; then
+ TAPE_DEV=$IBM_IDX
+ fi
elif [ -r /proc/scsi/$DEV_NAME ]; then
- if [ "$TAPE_SERIAL" != "NO/INQ" ]; then
IBM_IDX=$(
- awk '$3 == "'$TAPE_SERIAL'"{
- print $1
- }' /proc/scsi/$DEV_NAME
+ grep -wF "$SCSI_ID" /proc/scsi/$DEV_NAME |
+ cut -d ' ' -f 1
)
if [ "$IBM_IDX" != "" ]; then
TAPE_DEV=$DEV_NAME$IBM_IDX
fi
- fi
fi
printf "$SCSIFORMAT" \
@@ -313,9 +353,16 @@ function SysfsCreateListSCSI()
$STATE
if $VERBOSE; then
+ if [ -r $SCSI_DEV/hba_id ]; then
+ HBA_ID=$(cat $SCSI_DEV/hba_id)
+ else
+ HBA_ID=$(SCSISearchCCWBusid $SCSI_DEV)
+ fi
+ WWPN="N/A"
+ [ -r $SCSI_DEV/wwpn ] && WWPN=$(cat $SCSI_DEV/wwpn)
printf "$SCSIVFORMAT" \
- $(cat $SCSI_DEV/hba_id) \
- $(cat $SCSI_DEV/wwpn) \
+ "$HBA_ID" \
+ "$WWPN" \
$TAPE_SERIAL
fi
done
diff --git a/zconf/lstape.8 b/zconf/lstape.8
index 544cb08..019b6e5 100644
--- a/zconf/lstape.8
+++ b/zconf/lstape.8
@@ -1,8 +1,8 @@
-.\" Copyright 2017 IBM Corp.
+.\" Copyright 2017, 2018 IBM Corp.
.\" s390-tools is free software; you can redistribute it and/or modify
.\" it under the terms of the MIT license. See LICENSE for details.
.\"
-.TH LSTAPE 8 "Jul 2007" "s390-tools"
+.TH LSTAPE 8 "Jun 2018" "s390-tools"
.SH NAME
lstape \- list tape devices.
@@ -20,6 +20,8 @@ lstape \- list tape devices.
.br
.RB [ -t
.IR <device-type> [, <device-type> ] "" ...]
+.br
+.RI [ <device-bus-ID> ...]
.SH DESCRIPTION
The lstape command lists all available tape devices on the current host. For
@@ -27,19 +29,8 @@ channel attached tape devices this output is the same as the contents of
/proc/tapedevices (which is obsolete) but also includes offline devices. By
default all tape devices are displayed.
-Since SCSI tape devices are accessed differently to channel attached tape
-devices they are only visible if they are known to the SCSI layer. There
-are at least two possible drivers that can claim a SCSI tape device and the
-lstape command tries to find out which one this is. For the generic tape
-and changer driver the device names start with "st" or "sch", while for the
-IBM tape driver this would be "IBMtape" or "IBMchanger". If "N/A" is shown,
-the correct driver could not be obtained.
-This happens for example if there is no sg_inq command installed which is
-required to read the drive's serial number which in turn is used to find out
-the device number of the IBM tape driver.
-
-The serial number of a SCSI tape can be displayed with the --verbose option. If
-there is no sg_inq command available "NO/INQ" is shown as the tape's serial.
+The lstape command without the --ccw-only option causes extra SAN traffic
+for each SCSI tape or changer device by invoking the sg_inq command.
.SH OPTIONS
.TP 8
@@ -75,12 +66,64 @@ on the output of SCSI devices.
.TP
.BR -t | --type " \fI<device-type>\fR"
-Limit output to given device types (currently only applies to channel attached
+Limit output to given device types, for example 3490
+(currently only applies to channel-attached
tape devices).
.TP
-\fB<device-type>\fR =
-Device type of devices that should be displayed (e.g. 3490).
+.I <device-bus-ID>
+Limits the output to information about the specified tape device or
+devices only. For CCW-attached devices only.
+
+.SH OUTPUT FIELDS FOR SCSI TAPE/CHANGER DEVICES
+.TP
+.B Generic
+SCSI generic device file for the tape drive, for example /dev/sg0.
+"N/A" if the SCSI generic (sg) kernel functionality is not available.
+.TP
+.B Device
+Main character device node file for accessing the tape drive or medium changer.
+SCSI tape devices are only visible if they are known to the SCSI layer. There
+are at least two possible drivers that can claim a SCSI tape device. The
+lstape command tries to determine the device driver. For the generic tape
+and changer driver the device names start with "st" or "sch", while for the
+IBM tape driver this would be "IBMtape" or "IBMchanger". If "N/A" is shown,
+the device driver could not be determined.
+.TP
+.B Target
+Linux SCSI device name in H:C:T:L format.
+.TP
+.B Vendor
+The vendor field from the SCSI device.
+.TP
+.B Model
+The model field from the SCSI device.
+.TP
+.B Type
+"tapedrv" for a tape drive or "changer" for a medium changer.
+.TP
+.B State
+The state of the SCSI device object in the kernel.
+Any state other than "running" can indicate problems.
+
+.PP
+
+For SCSI devices, the --verbose option additionally displays:
+.TP
+.B HBA
+The device bus-ID of the FCP device
+or of the virtio-scsi-ccw virtual HBA
+through which the tape drive is attached.
+"N/A" if the device does not have a sysfs ancestor with subsystem ccw.
+.TP
+.B WWPN
+The WWPN (worldwide port name) of the tape drive in the SAN.
+"N/A" if device is not attached through zfcp.
+.TP
+.B Serial
+The serial number.
+"NO/INQ" if there is no sg_inq command available.
+"NO/SG" if no SCSI generic (sg) kernel support is available.
.SH EXAMPLES
\fBlstape\fR
@@ -92,3 +135,8 @@ List all tape devices that are available
.RS
Show all 3490 CCW devices that are online.
.RE
+
+\fBlstape --scsi-only --verbose\fR
+.RS
+Show all SCSI tape or changer devices with maximum information.
+.RE
--
2.21.3
From 164d283c73cc17b8a5927a449ed59671545f7066 Mon Sep 17 00:00:00 2001
From: Javier Martinez Canillas <javierm@redhat.com>
Date: Tue, 1 Oct 2019 15:33:59 +0200
Subject: [PATCH 38/44] zipl: fix the scanned tokens array size calculation
(#1751587)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
The zipl config file (zipl.conf) and the BootLoaderSpec (BLS) fragments
in /boot/loader/entries define a set of tokens that are parsed by zipl.
These are stored in an array of tokens whose size is calculated to make
sure that there is enough memory allocated for all the scanned tokens.
But the size calculation logic was wrong, since it was checking if the
current size was enough to store a single token per BLS fragment, while
up to 4 tokens can be defined in a BLS file: a section heading and the
image, ramdisk and parameter keywords.
This led to zipl being killed by a SIGABRT signal when trying to parse
more tokens than the ones that could fit in the scanned tokens array:
Using config file '/etc/zipl.conf'
Using BLS config file '/boot/loader/entries/vmlinuz-9.conf'
Using BLS config file '/boot/loader/entries/vmlinuz-8.conf'
Using BLS config file '/boot/loader/entries/vmlinuz-7.conf'
Using BLS config file '/boot/loader/entries/vmlinuz-6.conf'
Using BLS config file '/boot/loader/entries/vmlinuz-5.conf'
Using BLS config file '/boot/loader/entries/vmlinuz-4.conf'
Using BLS config file '/boot/loader/entries/vmlinuz-3.conf'
Using BLS config file '/boot/loader/entries/vmlinuz-2.conf'
Using BLS config file '/boot/loader/entries/vmlinuz-1.conf'
Using BLS config file '/boot/loader/entries/vmlinuz-0.conf'
double free or corruption (out)
Aborted (core dumped)
Fixes: https://github.com/ibm-s390-tools/s390-tools/issues/68
Closes: https://github.com/ibm-s390-tools/s390-tools/pull/73
Signed-off-by: Javier Martinez Canillas <javierm@redhat.com>
Signed-off-by: Stefan Haberland <sth@linux.ibm.com>
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
(cherry picked from commit 8bfe18e674e17a20682dbd1fa0e4813c789e22e5)
---
zipl/src/scan.c | 10 ++++++++--
1 file changed, 8 insertions(+), 2 deletions(-)
diff --git a/zipl/src/scan.c b/zipl/src/scan.c
index e57361e..ee04251 100644
--- a/zipl/src/scan.c
+++ b/zipl/src/scan.c
@@ -751,8 +751,14 @@ scan_bls(const char* blsdir, struct scan_token** token, int scan_size)
remaining = scan_size - count;
- if (remaining < n) {
- size = scan_size - remaining + n;
+ /* The array of scanned tokens is allocated when the zipl config file is
+ * parsed. Its size is a multiple of INITIAL_ARRAY_LENGTH so it may have
+ * enough space to scan all the tokens that are defined in the BLS files.
+ * Calculate if is enough assuming that a BLS fragment can contain up to
+ * 4 tokens: a section heading and 3 keywords (image, ramdisk, parameter).
+ */
+ if (remaining < n * 4) {
+ size = scan_size - remaining + (n * 4);
buffer = (struct scan_token *)misc_malloc(size * sizeof(struct scan_token));
if (!buffer)
goto err;
--
2.21.3
From ba8021cb17f74ea5130d7c5acdc267b6fb0a433c Mon Sep 17 00:00:00 2001
From: Eric Sandeen <sandeen@sandeen.net>
Date: Wed, 12 Sep 2018 09:40:22 -0500
Subject: [PATCH 39/44] zipl: use FIEMAP mapping ioctl if it exists (moved from
Patch101)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
zipl currently uses the FIBMAP ioctl to map blocks for the bootloader;
on XFS, if FIBMAP is requested on a reflinked file, it will fail -
and FIBMAP returns 0 in this case, which is indistinguishable from a
hole. This causes boot to fail because the file is not mapped.
We can use the FIEMAP ioctl instead, which is able to map reflinked
files. While FIEMAP is able to map entire extents at once, here we
simply use it to obtain the mapping block-by-block so that it fits
in with the current FIBMAP calls.
Fixes: https://github.com/ibm-s390-tools/s390-tools/issues/34
Closes: https://github.com/ibm-s390-tools/s390-tools/pull/36
Signed-off-by: Eric Sandeen <sandeen@redhat.com>
Reviewed-by: Peter Oberparleiter <oberpar@linux.ibm.com>
Reviewed-by: Stefan Haberland <sth@linux.ibm.com>
Tested-by: Stefan Haberland <sth@linux.ibm.com>
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
(cherry picked from commit 07e30951f8f31cdb5ea3cbd020254239be522e6a)
---
zipl/src/disk.c | 57 +++++++++++++++++++++++++++++++++++++++++++++----
1 file changed, 53 insertions(+), 4 deletions(-)
diff --git a/zipl/src/disk.c b/zipl/src/disk.c
index 0d8e779..43092bf 100644
--- a/zipl/src/disk.c
+++ b/zipl/src/disk.c
@@ -21,6 +21,8 @@
#include <sys/sysmacros.h>
#include <sys/vfs.h>
#include <unistd.h>
+#include <linux/fs.h>
+#include <linux/fiemap.h>
#include "lib/util_proc.h"
@@ -550,8 +552,12 @@ disk_get_blocknum(int fd, int fd_is_basedisk, blocknum_t logical,
{
struct statfs buf;
blocknum_t phy_per_fs;
- int mapped;
+ blocknum_t mapped;
+ int block;
int subblock;
+ int fiemap_size;
+ int map_offset;
+ struct fiemap *fiemap;
/* No file system: partition or raw disk */
if (info->fs_block_size == -1) {
@@ -576,12 +582,55 @@ disk_get_blocknum(int fd, int fd_is_basedisk, blocknum_t logical,
}
/* Get mapping in file system blocks */
phy_per_fs = info->fs_block_size / info->phy_block_size;
- mapped = logical / phy_per_fs;
subblock = logical % phy_per_fs;
- if (ioctl(fd, FIBMAP, &mapped)) {
- error_reason("Could not get file mapping");
+
+ /* First try FIEMAP, more complicated to set up */
+ fiemap_size = sizeof(struct fiemap) + sizeof(struct fiemap_extent);
+
+ fiemap = misc_malloc(fiemap_size);
+ if (!fiemap)
return -1;
+ memset(fiemap, 0, fiemap_size);
+
+ fiemap->fm_extent_count = 1;
+ fiemap->fm_flags = FIEMAP_FLAG_SYNC;
+ /* fm_start, fm_length in bytes; logical is in physical block units */
+ fiemap->fm_start = logical * info->phy_block_size;
+ fiemap->fm_length = info->phy_block_size;
+
+ if (ioctl(fd, FS_IOC_FIEMAP, (unsigned long)fiemap)) {
+ /* FIEMAP failed, fall back to FIBMAP */
+ block = logical / phy_per_fs;
+ if (ioctl(fd, FIBMAP, &block)) {
+ error_reason("Could not get file mapping");
+ free(fiemap);
+ return -1;
+ }
+ mapped = block;
+ } else {
+ if (fiemap->fm_mapped_extents) {
+ if (fiemap->fm_extents[0].fe_flags &
+ FIEMAP_EXTENT_ENCODED) {
+ error_reason("File mapping is encoded");
+ free(fiemap);
+ return -1;
+ }
+ /*
+ * returned extent may start prior to our request
+ */
+ map_offset = fiemap->fm_start -
+ fiemap->fm_extents[0].fe_logical;
+ mapped = fiemap->fm_extents[0].fe_physical +
+ map_offset;
+ /* set mapped to fs block units */
+ mapped = mapped / info->fs_block_size;
+ } else {
+ mapped = 0;
+ }
}
+
+ free(fiemap);
+
if (mapped == 0) {
/* This is a hole in the file */
*physical = 0;
--
2.21.3
From 658eef5bc3ea1ec9b917c35a36dcbe1219bd0b08 Mon Sep 17 00:00:00 2001
From: Javier Martinez Canillas <javierm@redhat.com>
Date: Tue, 6 Nov 2018 13:29:32 +0100
Subject: [PATCH 40/44] zipl: use the BLS "title" field as the IPL section name
(moved from Patch103)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Most bootloaders use the BootLoaderSpec "title" field to name the entries
in their boot menu. The zipl bootloader used the "version" field instead,
since it was wrongly assumed that the zipl boot menu didn't support names
that contained spaces, which are usually present in a BLS "title" field.
But this is not the case, names with space characters are supported by the
IPL and is just a constraint of the section heading in the zipl.conf file.
So to be consistent with all the other bootloaders, use the "title" field
also on zipl when populating the boot menu entries from BLS files.
Closes: https://github.com/ibm-s390-tools/s390-tools/pull/47
Signed-off-by: Javier Martinez Canillas <javierm@redhat.com>
Reviewed-by: Peter Oberparleiter <oberpar@linux.ibm.com>
Reviewed-by: Stefan Haberland sth@linux.ibm.com
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
(cherry picked from commit 49510442133e63bfd650a7b7e27e388d38baba81)
---
scripts/zipl-switch-to-blscfg | 1 +
zipl/src/scan.c | 2 +-
2 files changed, 2 insertions(+), 1 deletion(-)
diff --git a/scripts/zipl-switch-to-blscfg b/scripts/zipl-switch-to-blscfg
index 5272033..5141b1c 100755
--- a/scripts/zipl-switch-to-blscfg
+++ b/scripts/zipl-switch-to-blscfg
@@ -150,6 +150,7 @@ while IFS='= ' read key val; do
if [ -f "${OUTPUT}" ]; then
print_error "BLS file ${OUTPUT} already exists"
fi
+ echo "title $section" >> ${OUTPUT}
fi
elif [[ $val ]]; then
val="$(echo $val | sed -e 's/^[ \t"]*//;s/[ \t"]*$//')"
diff --git a/zipl/src/scan.c b/zipl/src/scan.c
index ee04251..b8fea13 100644
--- a/zipl/src/scan.c
+++ b/zipl/src/scan.c
@@ -702,7 +702,7 @@ scan_bls_field(struct misc_file_buffer *file, struct scan_token* scan,
file->buffer[key_end] = '\0';
file->buffer[val_end] = '\0';
- if (strncmp("version", &file->buffer[key_start], key_end - key_start) == 0) {
+ if (strncmp("title", &file->buffer[key_start], key_end - key_start) == 0) {
scan_append_section_heading(scan, index, &file->buffer[val_start]);
}
--
2.21.3
From 2515287113812346ef8a3bb00a80632b698ab542 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Dan=20Hor=C3=A1k?= <dan@danny.cz>
Date: Thu, 7 Nov 2019 14:37:36 +0100
Subject: [PATCH 41/44] zipl: config file handling improvements for CoreOS
(#1764706)
Use TOOLS_SYSCONFDIR for the default zipl.conf path - c01bcfa73a6d795f33df639ba36304a704d13561
zipl: ship a minimal zipl.conf - 19259aeb656df5cbc7bda89599442dd51d80a7a8
zipl: allow check for other locations of zipl.conf - 275105fe3d64d214f982a5c0eb113d806853919b
zipl: add value of target= as search path for BLS case - d71628326d80e623fc9f008fe4ea93edb5592b2e
---
zipl/Makefile | 1 +
zipl/doc/zipl.conf.minimal | 10 ++++
zipl/include/scan.h | 1 +
zipl/include/zipl.h | 4 +-
zipl/man/Makefile | 10 +++-
zipl/man/{zipl.8 => zipl.8.in} | 2 +-
zipl/man/{zipl.conf.5 => zipl.conf.5.in} | 17 +++++--
zipl/src/job.c | 23 +++++++++-
zipl/src/scan.c | 58 ++++++++++++++++++++++++
9 files changed, 117 insertions(+), 9 deletions(-)
create mode 100644 zipl/doc/zipl.conf.minimal
rename zipl/man/{zipl.8 => zipl.8.in} (99%)
rename zipl/man/{zipl.conf.5 => zipl.conf.5.in} (96%)
diff --git a/zipl/Makefile b/zipl/Makefile
index a02e997..931b113 100644
--- a/zipl/Makefile
+++ b/zipl/Makefile
@@ -8,6 +8,7 @@ all:
install: all
$(MAKE) -C src install
$(MAKE) -C man install
+ $(INSTALL) -g $(GROUP) -o $(OWNER) -m 644 doc/zipl.conf.minimal $(DESTDIR)$(TOOLS_LIBDIR)/zipl.conf
clean:
$(MAKE) -C src clean
diff --git a/zipl/doc/zipl.conf.minimal b/zipl/doc/zipl.conf.minimal
new file mode 100644
index 0000000..9d6fa5e
--- /dev/null
+++ b/zipl/doc/zipl.conf.minimal
@@ -0,0 +1,10 @@
+# This is an example of a minimal zipl.conf file that can be used when the
+# sections are defined in BootLoaderSpec fragments files.
+#
+# See the zipl and zipl.conf man page for more details.
+[defaultboot]
+defaultauto
+prompt=1
+timeout=5
+secure=auto
+target=/boot
diff --git a/zipl/include/scan.h b/zipl/include/scan.h
index 4dcee0b..f39e96e 100644
--- a/zipl/include/scan.h
+++ b/zipl/include/scan.h
@@ -126,6 +126,7 @@ char* scan_keyword_name(enum scan_keyword_id id);
int scan_check_defaultboot(struct scan_token* scan);
struct scan_token* scan_build_automenu(struct scan_token* scan);
int scan_check(struct scan_token* scan);
+int scan_check_bls(struct scan_token *scan);
int scan_find_section(struct scan_token* scan, char* name, enum scan_id type,
int offset);
int scan_check_section_data(char* keyword[], int* line, char* name,
diff --git a/zipl/include/zipl.h b/zipl/include/zipl.h
index 6f2d115..0ef06e4 100644
--- a/zipl/include/zipl.h
+++ b/zipl/include/zipl.h
@@ -47,7 +47,9 @@
#define DEFAULTBOOT_SECTION "defaultboot"
#define ZIPL_CONF_VAR "ZIPLCONF"
-#define ZIPL_DEFAULT_CONF "/etc/zipl.conf"
+#define ZIPL_RUNTIME_CONF "/run/zipl/zipl.conf"
+#define ZIPL_DEFAULT_CONF TOOLS_SYSCONFDIR "/zipl.conf"
+#define ZIPL_MINIMAL_CONF TOOLS_LIBDIR "/zipl.conf"
#define ZIPL_DEFAULT_BLSDIR "/boot/loader/entries"
#define ZIPL_STAGE3_PATH TOOLS_LIBDIR "/stage3.bin"
#define ZIPL_SIPL_PATH "/sys/firmware/ipl/has_secure"
diff --git a/zipl/man/Makefile b/zipl/man/Makefile
index 6439205..1d8dbe3 100644
--- a/zipl/man/Makefile
+++ b/zipl/man/Makefile
@@ -6,7 +6,13 @@ all:
install:
$(INSTALL) -d -m 755 $(DESTDIR)$(MANDIR)/man5
$(INSTALL) -d -m 755 $(DESTDIR)$(MANDIR)/man8
- $(INSTALL) -m 644 -c zipl.8 $(DESTDIR)$(MANDIR)/man8
- $(INSTALL) -m 644 -c zipl.conf.5 $(DESTDIR)$(MANDIR)/man5
+ sed -e 's@%SYSCONFDIR%@$(SYSCONFDIR)@' \
+ -e 's@%TOOLS_LIBDIR%@$(TOOLS_LIBDIR)@' zipl.8.in \
+ > $(DESTDIR)$(MANDIR)/man8/zipl.8
+ sed -e 's@%SYSCONFDIR%@$(SYSCONFDIR)@' \
+ -e 's@%TOOLS_LIBDIR%@$(TOOLS_LIBDIR)@' zipl.conf.5.in \
+ > $(DESTDIR)$(MANDIR)/man5/zipl.conf.5
+ chmod 644 $(DESTDIR)$(MANDIR)/man8/zipl.8 \
+ $(DESTDIR)$(MANDIR)/man5/zipl.conf.5
clean:
diff --git a/zipl/man/zipl.8 b/zipl/man/zipl.8.in
similarity index 99%
rename from zipl/man/zipl.8
rename to zipl/man/zipl.8.in
index 2f873f3..f12e27b 100644
--- a/zipl/man/zipl.8
+++ b/zipl/man/zipl.8.in
@@ -120,7 +120,7 @@ Print version information, then exit.
.TP
.BR "\-c <CONFIG FILE>" " or " "\-\-config=<CONFIG FILE>"
Use the specified <CONFIG FILE>. If none is supplied, the environment
-variable ZIPLCONF is evaluated if set, otherwise /etc/zipl.conf is used.
+variable ZIPLCONF is evaluated if set, otherwise %SYSCONFDIR%/zipl.conf is used.
.TP
.BR "\-b <BLS DIRECTORY>" " or " "\-\-blsdir=<BLS DIRECTORY>"
diff --git a/zipl/man/zipl.conf.5 b/zipl/man/zipl.conf.5.in
similarity index 96%
rename from zipl/man/zipl.conf.5
rename to zipl/man/zipl.conf.5.in
index 71d1252..ef40287 100644
--- a/zipl/man/zipl.conf.5
+++ b/zipl/man/zipl.conf.5.in
@@ -17,8 +17,13 @@ boot loader tool
.BR zipl (8)).
.br
-By default this configuration file is located at /etc/zipl.conf. A different
-location may be specified either using the '\-\-config' option of
+By default
+.B zipl
+checks for
+.I zipl.conf
+at /run/zipl/zipl.conf, %SYSCONFDIR%/zipl.conf, %TOOLS_LIBDIR%/zipl.conf in that
+order - whichever is found first will be used. Users can specifically choose a
+location using the '\-\-config' option of
.B zipl
or by setting the ZIPLCONF shell environment variable.
.br
@@ -138,7 +143,13 @@ initrd /initramfs-4.15.9
options root=/dev/dasda1 console=ttyS0
.PP
-The location of the linux and initrd has to be specified relative to the boot partition. The BLS config files are only used to specify the IPL sections, a zipl.conf configuration files is still needed for global parameters.
+The location of the linux and initrd has to be specified relative to the boot
+partition. The BLS config files are only used to specify the IPL sections, a
+zipl.conf configuration file is still needed for global parameters. For this
+purpose, a minimal zipl.conf configuration file is shipped at
+%TOOLS_LIBDIR%/zipl.conf which would help when used with BLS config files, by
+not requiring users to create the traditional configuration file at
+%SYSCONFDIR%/zipl.conf.
.B Boot menu
diff --git a/zipl/src/job.c b/zipl/src/job.c
index 1178a7d..7d61c84 100644
--- a/zipl/src/job.c
+++ b/zipl/src/job.c
@@ -59,6 +59,14 @@ static struct option options[] = {
/* Command line option abbreviations */
static const char option_string[] = "-c:b:t:i:r:p:P:d:D:M:s:S:m:hHnVvaT:fk:";
+/* Locations of zipl.conf configuration file */
+static const char *zipl_conf[] = {
+ ZIPL_RUNTIME_CONF,
+ ZIPL_DEFAULT_CONF,
+ ZIPL_MINIMAL_CONF,
+ NULL
+};
+
struct command_line {
char* data[SCAN_KEYWORD_NUM];
char* config;
@@ -1783,7 +1791,7 @@ get_job_from_config_file(struct command_line* cmdline, struct job_data* job)
char* filename;
char *blsdir;
char* source;
- int rc, scan_size;
+ int i, rc, scan_size;
/* Read configuration file */
if (cmdline->config != NULL) {
@@ -1797,7 +1805,12 @@ get_job_from_config_file(struct command_line* cmdline, struct job_data* job)
ZIPL_CONF_VAR ")";
} else {
/* Use default config file */
- filename = ZIPL_DEFAULT_CONF;
+ for (i = 0; zipl_conf[i]; i++) {
+ if (misc_check_readable_file(zipl_conf[i]) == 0) {
+ filename = zipl_conf[i];
+ break;
+ }
+ }
source = "";
}
printf("Using config file '%s'%s\n", filename, source);
@@ -1840,6 +1853,12 @@ get_job_from_config_file(struct command_line* cmdline, struct job_data* job)
scan_free(scan);
return rc;
}
+ rc = scan_check_bls(scan);
+ if (rc) {
+ error_text("BLS parsing '%s'", blsdir);
+ scan_free(scan);
+ return rc;
+ }
/* Get job from config file data */
if (cmdline->menu != NULL)
rc = get_menu_job(scan, cmdline->menu, job);
diff --git a/zipl/src/scan.c b/zipl/src/scan.c
index b8fea13..f4228d3 100644
--- a/zipl/src/scan.c
+++ b/zipl/src/scan.c
@@ -1559,6 +1559,64 @@ scan_check(struct scan_token* scan)
return 0;
}
+/*
+ * Check if kernel and initrd image paths provided by BLS files are readable.
+ * If not, add value of 'scan_keyword_target' into search path and silently
+ * update scan list.
+ */
+int
+scan_check_bls(struct scan_token *scan)
+{
+ int i, rc;
+ char *target_value = NULL;
+ char *img_value = NULL;
+ char *buffer = NULL;
+ /*
+ * In the BLS case, each BLS section heading inherits a keyword
+ * assignment target= from zipl.conf, and they are all the same.
+ *
+ */
+ for (i = 0 ; scan[i].id != scan_id_empty; i++) {
+ if (scan[i].id == scan_id_keyword_assignment &&
+ scan[i].content.keyword.keyword == scan_keyword_target) {
+ target_value = scan[i].content.keyword.value;
+ break;
+ }
+ }
+ if (!target_value)
+ return -1;
+ for (i = 0 ; scan[i].id != scan_id_empty; i++) {
+ if (scan[i].id != scan_id_keyword_assignment)
+ continue;
+ if (scan[i].content.keyword.keyword == scan_keyword_image ||
+ scan[i].content.keyword.keyword == scan_keyword_ramdisk) {
+
+ rc = misc_check_readable_file(
+ scan[i].content.keyword.value);
+ if (rc) {
+ misc_asprintf(&img_value, "%s%s",
+ target_value,
+ scan[i].content.keyword.value);
+ rc = misc_check_readable_file(img_value);
+ if (rc) {
+ error_text(
+ "Image file '%s' is not accessible",
+ scan[i].content.keyword.value);
+ return rc;
+ }
+ buffer = (char *)
+ misc_malloc(strlen(img_value) + 1);
+ if (buffer == NULL)
+ return -1;
+ memcpy(buffer, img_value, strlen(img_value));
+ buffer[strlen(img_value)] = 0;
+ free(scan[i].content.keyword.value);
+ scan[i].content.keyword.value = buffer;
+ }
+ }
+ }
+ return 0;
+}
static int
scan_get_defaultboot_type(char* keyword[], int line[], int section_line,
--
2.21.3
From 1ca71226d392c59855432ee992e263be680644d3 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Dan=20Hor=C3=A1k?= <dan@danny.cz>
Date: Mon, 27 Jan 2020 11:22:42 +0100
Subject: [PATCH 42/44] zkey: Fix display of clear key size for XTS keys
(#1794375)
Description: zkey: Fix display of clear key size for XTS keys
Symptom: The 'zkey list' command shows bogus values for the
keys 'Clear key size' for XTS keys of type CCA-AESDATA
or CCA-AESCIPHER.
Problem: XTS keys consist of 2 keys concatenated to each other.
To calculate the clear key size, the clear key size of
both keys must be added. The code does not address the
second key correctly, and thus reads the clear key size
of the second key from an invalid memory location. This
results in bogus values reported as clear key size.
This bug has been introduced with feature SEC1717 "Cipher
key support" with commit 298fab68fee8 "zkey: Preparations
for introducing a new key type".
Solution: Correct the addressing of the second key.
Reproduction: Generate an XTS key of type CCA-AESDATA or CCA-AESCIPHER
and then run 'zkey list'.
Upstream-ID: e7f446432b92b293e758099842843cfb1f18fa97
---
zkey/pkey.c | 8 ++++----
1 file changed, 4 insertions(+), 4 deletions(-)
diff --git a/zkey/pkey.c b/zkey/pkey.c
index 462f9fe..640ff86 100644
--- a/zkey/pkey.c
+++ b/zkey/pkey.c
@@ -1591,8 +1591,8 @@ int get_key_bit_size(const u8 *key, size_t key_size, size_t *bitsize)
if (is_cca_aes_data_key(key, key_size)) {
*bitsize = datakey->bitsize;
if (key_size == 2 * AESDATA_KEY_SIZE) {
- datakey = (struct aesdatakeytoken *)key +
- AESDATA_KEY_SIZE;
+ datakey = (struct aesdatakeytoken *)(key +
+ AESDATA_KEY_SIZE);
*bitsize += datakey->bitsize;
}
} else if (is_cca_aes_cipher_key(key, key_size)) {
@@ -1601,8 +1601,8 @@ int get_key_bit_size(const u8 *key, size_t key_size, size_t *bitsize)
else
*bitsize = 0; /* Unknown */
if (key_size > cipherkey->length) {
- cipherkey = (struct aescipherkeytoken *)key +
- cipherkey->length;
+ cipherkey = (struct aescipherkeytoken *)(key +
+ cipherkey->length);
if (cipherkey->pfv == 0x00) /* V0 payload */
*bitsize += cipherkey->pl - 384;
}
--
2.21.3
From 2beeedb4f5eca1cf2faf34d1a0db176ea887854f Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Dan=20Hor=C3=A1k?= <dan@danny.cz>
Date: Mon, 27 Jan 2020 11:39:24 +0100
Subject: [PATCH 43/44] zkey: Fix listing of keys on file systems reporting
DT_UNKNOWN (#1792957)
Description: zkey: Fix listing of keys on file systems reporting DT_UNKNOWN.
Symptom: When the zkey key repository is located in a file system that
does not have full support for report the file type, such as
XFS, the 'zkey list' command does not show any keys, although
keys exist in the repository.
Problem: The zkey list function uses scandir() to look for files in the
zkey key repository directory. It checks the dirent.d_type field
to consider only regular files, but skips all others. File
systems that do not have full support for returning the file
type in d_type will return DT_UNKNOWN instead. zkey skips
those directory entries and thus does not show any keys.
Solution: Also consider directory entries with d_type = DT_UNKNOWN.
Reproduction: Use zkey with a zkey repository directory located in a file
system that does not have full support for returning the file
type, such as XFS. Generate a key in the repository and then
list the key s with 'zkey list'.
Note: Newly created XFS file systems usually support returning
the file type, but existing XFS file systems might not. To
create an XFS file system that does not support returning the
file type, use 'mkfs.xfs -f -m crc=0 -n ftype=0' to create
the file system.
Upstream-ID: 0de533aef9def920fed751c6025e4f19c4cba763
---
zkey/keystore.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/zkey/keystore.c b/zkey/keystore.c
index af67721..e6be3a4 100644
--- a/zkey/keystore.c
+++ b/zkey/keystore.c
@@ -906,7 +906,7 @@ static int _keystore_info_file_filter(const struct dirent *dirent)
{
size_t len;
- if (dirent->d_type != DT_REG)
+ if (dirent->d_type != DT_REG && dirent->d_type != DT_UNKNOWN)
return 0;
len = strlen(dirent->d_name);
--
2.21.3
From 4d0488330d69aa634d3c5bc68c8036b2ddc97144 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Dan=20Hor=C3=A1k?= <dan@danny.cz>
Date: Wed, 27 May 2020 10:29:10 +0200
Subject: [PATCH 44/44] zipl/libc: Fix potential buffer overflow in printf
(#1847536)
Description: zipl/libc: Fix potential buffer overflow in printf
Symptom: Crash of the zipl boot loader during boot.
Problem: The zipl boot loaders have their own minimalistic libc
implementation. In it printf and sprintf use vsprintf for string
formatting. Per definition vsprintf assumes that the buffer it
writes to is large enough to contain the formatted string and
performs no size checks. This is problematic for the boot
loaders because the buffer they use are often allocated on the
stack. Thus even small changes to the string format can
potentially cause buffer overflows on the stack.
Solution: Implement vsnprintf and make use of it.
Reproduction: Use printf to print a string with >81 characters (exact number
depends on the stack layout/compiler used).
Upstream-ID: 6fe9e6c55c69c14971dca55551009f5060418aae
Upstream-ID: 8874b908254c47c8a6fd7a1aca2c7371c11035c4
Upstream-ID: f7430027b41d5ad6220e962a179c2a5213330a44
Upstream-ID: 36fed0e6c6590631c4ce1707c8fe3c3397bcce4d
---
zipl/boot/libc.c | 360 ++++++++++++++++++++++++++++--------------
zipl/boot/libc.h | 4 +-
zipl/boot/menu.c | 2 +-
zipl/boot/menu.h | 1 -
zipl/boot/tape2dump.c | 2 +-
5 files changed, 248 insertions(+), 121 deletions(-)
diff --git a/zipl/boot/libc.c b/zipl/boot/libc.c
index 5944c4e..bd88d15 100644
--- a/zipl/boot/libc.c
+++ b/zipl/boot/libc.c
@@ -125,81 +125,6 @@ int strncmp(const char *s1, const char *s2, unsigned long count)
return 0;
}
-/*
- * Convert number to string
- *
- * Parameters:
- *
- * - buf: Output buffer
- * - base: Base used for formatting (e.g. 10 or 16)
- * - val: Number to format
- * - zero: If > 0, fill with leading zeros, otherwise use blanks
- * - count: Minimum number of characters used for output string
- */
-static int num_to_str(char *buf, int base, unsigned long val, int zero,
- unsigned long count)
-{
- static const char conv_vec[] = {'0', '1', '2', '3', '4', '5', '6', '7',
- '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'};
- unsigned long num = 0, val_work = val, in_number = 1;
- int i;
-
- /* Count number of characters needed for number */
- do {
- num++;
- val_work /= base;
- } while (val_work);
- /* Real character number overwrites count */
- if (count < num)
- count = num;
- /* Format number */
- for (i = count - 1; i >= 0; i--) {
- if (in_number) {
- buf[i] = conv_vec[val % base];
- val /= base;
- in_number = val ? 1 : 0;
- } else {
- buf[i] = zero ? '0' : ' ';
- }
- }
- buf[count] = 0;
- return count;
-}
-
-/*
- * Convert string to string with indentation
- */
-static int str_to_str(char *buf, const char *str, unsigned long count)
-{
- unsigned long size;
-
- size = strlen(str);
- if (count < size)
- count = size;
- else
- memset(buf, ' ', count - size);
- strcpy(buf + (count - size), str);
- return count;
-}
-
-/*
- * Convert string to number with given base
- */
-unsigned long strtoul(const char *nptr, char **endptr, int base)
-{
- unsigned long val = 0;
-
- while (isdigit(*nptr)) {
- if (val != 0)
- val *= base;
- val += *nptr - '0';
- nptr++;
- }
- if (endptr)
- *endptr = (char *) nptr;
- return val;
-}
-
/*
* Convert ebcdic string to number with given base
*/
@@ -218,79 +143,282 @@ unsigned long ebcstrtoul(char *nptr, char **endptr, int base)
return val;
}
-/*
- * Convert string to number with given base
- */
-static int sprintf_fmt(char type, char *buf, unsigned long val, int zero,
- int count)
+static int skip_atoi(const char **c)
{
- switch (type) {
+ int i = 0;
+
+ do {
+ i = i*10 + *((*c)++) - '0';
+ } while (isdigit(**c));
+
+ return i;
+}
+
+enum format_type {
+ FORMAT_TYPE_NONE,
+ FORMAT_TYPE_STR,
+ FORMAT_TYPE_ULONG,
+};
+
+struct printf_spec {
+ unsigned int type:8; /* format_type enum */
+ signed int field_width:24; /* width of output field */
+ unsigned int zeropad:1; /* pad numbers with zero */
+ unsigned int base:8; /* number base, 8, 10 or 16 only */
+ signed int precision:16; /* # of digits/chars */
+};
+
+#define FIELD_WIDTH_MAX ((1 << 23) - 1)
+
+static int format_decode(const char *fmt, struct printf_spec *spec)
+{
+ const char *start = fmt;
+
+ spec->type = FORMAT_TYPE_NONE;
+ while (*fmt) {
+ if (*fmt == '%')
+ break;
+ fmt++;
+ }
+
+ /* return current non-format string */
+ if (fmt != start || !*fmt)
+ return fmt - start;
+
+ /* first char is '%', skip it */
+ fmt++;
+ if (*fmt == '0') {
+ spec->zeropad = 1;
+ fmt++;
+ }
+
+ spec->field_width = -1;
+ if (isdigit(*fmt))
+ spec->field_width = skip_atoi(&fmt);
+
+ spec->precision = -1;
+ if (*fmt == '.') {
+ fmt++;
+ if (isdigit(*fmt))
+ spec->precision = skip_atoi(&fmt);
+ }
+
+ /* always use long form, i.e. ignore long qualifier */
+ if (*fmt == 'l')
+ fmt++;
+
+ switch (*fmt) {
case 's':
- return str_to_str(buf, (const char *) val, count);
- case 'x':
- return num_to_str(buf, 16, val, zero, count);
+ spec->type = FORMAT_TYPE_STR;
+ break;
+
+ case 'o':
+ spec->base = 8;
+ spec->type = FORMAT_TYPE_ULONG;
+ break;
+
case 'u':
- return num_to_str(buf, 10, val, zero, count);
+ spec->base = 10;
+ spec->type = FORMAT_TYPE_ULONG;
+ break;
+
+ case 'x':
+ spec->base = 16;
+ spec->type = FORMAT_TYPE_ULONG;
+ break;
+
default:
libc_stop(EINTERNAL);
}
- return 0;
+
+ return ++fmt - start;
+}
+
+static char *string(char *buf, char *end, const char *s,
+ struct printf_spec *spec)
+{
+ int limit = spec->precision;
+ int len = 0;
+ int spaces;
+
+ /* Copy string to buffer */
+ while (limit--) {
+ char c = *s++;
+ if (!c)
+ break;
+ if (buf < end)
+ *buf = c;
+ buf++;
+ len++;
+ }
+
+ /* right align if necessary */
+ if (len < spec->field_width && buf < end) {
+ spaces = spec->field_width - len;
+ if (spaces >= end - buf)
+ spaces = end - buf;
+ memmove(buf + spaces, buf, len);
+ memset(buf, ' ', spaces);
+ buf += spaces;
+ }
+
+ return buf;
+}
+
+static char *number(char *buf, char *end, unsigned long val,
+ struct printf_spec *spec)
+{
+ /* temporary buffer to prepare the string.
+ * Worst case: base = 8 -> 3 bits per char -> 2.67 chars per byte */
+ char tmp[3 * sizeof(val)];
+ static const char vec[] = {'0', '1', '2', '3', '4', '5', '6', '7',
+ '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'};
+ int field_width = spec->field_width;
+ int precision = spec->precision;
+ int len;
+
+ /* prepare string in reverse order */
+ len = 0;
+ while (val) {
+ tmp[len++] = vec[val % spec->base];
+ val /= spec->base;
+ }
+
+ if (len > precision)
+ precision = len;
+
+ field_width -= precision;
+ while (field_width-- > 0) {
+ char c = spec->zeropad ? '0' : ' ';
+ if (buf < end)
+ *buf = c;
+ buf++;
+ }
+
+ /* needed if no field width but a precision is given */
+ while (len < precision--) {
+ if (buf < end)
+ *buf = '0';
+ buf++;
+ }
+
+ while (len-- > 0) {
+ if (buf < end)
+ *buf = tmp[len];
+ buf++;
+ }
+
+ return buf;
}
/*
- * Print formated string (va version)
+ * vsnprintf - Format string and place in a buffer
+ *
+ * This funcion only supports a subset of format options defined in the
+ * C standard, i.e.
+ * specifiers:
+ * * %s (strings)
+ * * %o (unsigned int octal)
+ * * %u (unsigned int decimal)
+ * * %x (unsigned int hexadecimal)
+ *
+ * length modifier:
+ * * 'l' (ignored, see below)
+ *
+ * flag:
+ * * '0' (zero padding for integers)
+ *
+ * precision and field width as integers, i.e. _not_ by asterix '*'.
+ *
+ * The integer specifiers (o, u and, x) always use the long form, i.e.
+ * assume the argument to be of type 'unsigned long int'.
+ *
+ * Returns the number of characters the function would have generated for
+ * the given input (excluding the trailing '\0'. If the return value is
+ * greater than or equal @size the resulting string is trunctuated.
*/
-static void vsprintf(char *str, const char *fmt, va_list va)
+static int vsnprintf(char *buf, unsigned long size, const char *fmt,
+ va_list args)
{
- unsigned long val, zero, count;
- char *fmt_next;
+ struct printf_spec spec = {0};
+ char *str, *end;
- do {
- if (*fmt == '%') {
- fmt++;
- if (*fmt == '0') {
- zero = 1;
- fmt++;
- } else {
- zero = 0;
+ str = buf;
+ end = buf + size;
+
+ /* use negative (large positive) buffer sizes as indication for
+ * unknown/unlimited buffer sizes. */
+ if (end < buf) {
+ end = ((void *)-1);
+ size = end - buf;
+ }
+
+ while (*fmt) {
+ const char *old_fmt = fmt;
+ int read = format_decode(fmt, &spec);
+ int copy;
+
+ fmt += read;
+
+ switch (spec.type) {
+ case FORMAT_TYPE_NONE:
+ copy = read;
+ if (str < end) {
+ if (copy > end - str)
+ copy = end - str;
+ memcpy(str, old_fmt, copy);
}
- /* No number found by strtoul: count=0 fmt_next=fmt */
- count = strtoul(fmt, &fmt_next, 10);
- fmt = fmt_next;
- if (*fmt == 'l')
- fmt++;
- val = va_arg(va, unsigned long);
- str += sprintf_fmt(*fmt, str, val, zero, count);
- fmt++;
- } else {
- *str++ = *fmt++;
+ str += read;
+ break;
+
+ case FORMAT_TYPE_STR:
+ str = string(str, end, va_arg(args, char *), &spec);
+ break;
+
+ case FORMAT_TYPE_ULONG:
+ str = number(str, end, va_arg(args, unsigned long),
+ &spec);
+ break;
}
- } while (*fmt);
- *str = 0;
+ }
+
+ if (size) {
+ if (str < end)
+ *str = '\0';
+ else
+ end[-1] = '\0';
+ }
+ return str - buf;
}
/*
- * Write formated string to string
+ * Write formatted string to buffer
*/
-void sprintf(char *str, const char *fmt, ...)
+void snprintf(char *buf, unsigned long size, const char *fmt, ...)
{
va_list va;
va_start(va, fmt);
- vsprintf(str, fmt, va);
+ vsnprintf(buf, size, fmt, va);
va_end(va);
}
/*
- * Print formated string
+ * Print formatted string to console
*/
void printf(const char *fmt, ...)
{
- char buf[81];
+ char buf[LINE_LENGTH + 1];
+ int len;
va_list va;
va_start(va, fmt);
- vsprintf(buf, fmt, va);
+ len = vsnprintf(buf, sizeof(buf), fmt, va);
+ if (len > LINE_LENGTH) {
+ buf[LINE_LENGTH - 1] = '.';
+ buf[LINE_LENGTH - 2] = '.';
+ buf[LINE_LENGTH - 3] = '.';
+ }
sclp_print(buf);
va_end(va);
}
diff --git a/zipl/boot/libc.h b/zipl/boot/libc.h
index 44097fa..68ecd00 100644
--- a/zipl/boot/libc.h
+++ b/zipl/boot/libc.h
@@ -40,6 +40,7 @@
#define ENOTTY 25 /* Not a typewriter */
#define MIB (1024ULL * 1024)
+#define LINE_LENGTH 80 /* max line length printed by printf */
typedef unsigned long long uint64_t;
typedef unsigned int uint32_t;
@@ -47,13 +48,12 @@ typedef unsigned short uint16_t;
typedef unsigned char uint8_t;
void printf(const char *, ...);
-void sprintf(char *, const char *, ...);
+void snprintf(char *buf, unsigned long size, const char *fmt, ...);
void *memcpy(void *, const void *, unsigned long);
void *memmove(void *, const void *, unsigned long);
void *memset(void *, int c, unsigned long);
char *strcat(char *, const char *);
int strncmp(const char *, const char *, unsigned long);
-unsigned long strtoul(const char *, char **, int);
unsigned long ebcstrtoul(char *, char **, int);
int strlen(const char *);
char *strcpy(char *, const char *);
diff --git a/zipl/boot/menu.c b/zipl/boot/menu.c
index 3f68620..35ac4de 100644
--- a/zipl/boot/menu.c
+++ b/zipl/boot/menu.c
@@ -186,7 +186,7 @@ boot:
(void *)&__stage2_params + TEXT_OFFSET));
/* append 'BOOT_IMAGE=<num>' to parmline */
- sprintf(endstring, " BOOT_IMAGE=%u", value);
+ snprintf(endstring, sizeof(endstring), " BOOT_IMAGE=%u", value);
if ((strlen(cmd_line_extra) + strlen(endstring)) < COMMAND_LINE_SIZE)
strcat(cmd_line_extra, endstring);
diff --git a/zipl/boot/menu.h b/zipl/boot/menu.h
index 1b103a8..ee0f2c5 100644
--- a/zipl/boot/menu.h
+++ b/zipl/boot/menu.h
@@ -20,7 +20,6 @@
/* max command line length */
#define COMMAND_LINE_SIZE 896
#define BOOT_MENU_ENTRIES 63
-#define LINE_LENGTH 80
#define PARAM_SIZE 8
#define TEXT_OFFSET 4
diff --git a/zipl/boot/tape2dump.c b/zipl/boot/tape2dump.c
index 1da3ce1..800b943 100644
--- a/zipl/boot/tape2dump.c
+++ b/zipl/boot/tape2dump.c
@@ -186,7 +186,7 @@ static void progress_print_disp(unsigned long addr)
if (addr % (1024 * 1024 * 16) != 0)
return;
- sprintf(msg, "%08u", addr >> 20);
+ snprintf(msg, sizeof(msg), "%08u", addr >> 20);
ccw_load_display(msg);
}
--
2.21.3