diff --git a/.gitignore b/.gitignore index f4293eb..1a0a2e7 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,3 @@ SOURCES/cmsfs-1.1.8c.tar.gz -SOURCES/s390-tools-2.15.1.tar.gz +SOURCES/s390-tools-2.16.0.tar.gz SOURCES/src_vipa-2.1.0.tar.gz diff --git a/.s390utils.metadata b/.s390utils.metadata index 73b210e..95db57d 100644 --- a/.s390utils.metadata +++ b/.s390utils.metadata @@ -1,3 +1,3 @@ 9c9a4e89bddb2b4e6e09ef6fc7c2e6f2ad6316de SOURCES/cmsfs-1.1.8c.tar.gz -9a169518bf1074f3c087f97a32fd85c35ac216ee SOURCES/s390-tools-2.15.1.tar.gz +faf04b6d0bca264a65107d063674b06c9b4c03bf SOURCES/s390-tools-2.16.0.tar.gz 8ed8592a0a9370ce8422df9231ccb17f6cf49bed SOURCES/src_vipa-2.1.0.tar.gz diff --git a/SOURCES/ccw_init b/SOURCES/ccw_init index 23e8ed9..3703eaf 100644 --- a/SOURCES/ccw_init +++ b/SOURCES/ccw_init @@ -35,6 +35,14 @@ get_config_by_subchannel () | LC_ALL=C sed -e "$__sed_discard_ignored_files" } +get_config_by_subchannel_nm () +{ + LANG=C grep -E -i -l \ + "^s390-subchannels=([0-9]\.[0-9]\.[a-f0-9]+;){0,2}${1};([0-9]\.[0-9]\.[a-f0-9]+;){0,2}$" \ + /etc/NetworkManager/system-connections/*.nmconnection \ + | LC_ALL=C sed -e "$__sed_discard_ignored_files" +} + CHANNEL=${DEVPATH##*/} if [ $MODE = "dracut" ]; then @@ -65,7 +73,14 @@ elif [ $MODE = "normal" ]; then if [ -n "$CONFIG_FILE" ]; then . $CONFIG_FILE else - exit 1 + CONFIG_FILE=$(get_config_by_subchannel_nm $CHANNEL) + if [ -n "$CONFIG_FILE" ]; then + NETTYPE=$(sed -nr "/^\[ethernet\]/ { :l /^s390-nettype[ ]*=/ { s/.*=[ ]*//; p; q;}; n; b l;}" $CONFIG_FILE) + SUBCHANNELS=$(sed -nr "/^\[ethernet\]/ { :l /^s390-subchannels[ ]*=/ { s/.*=[ ]*//; p; q;}; n; b l;}" $CONFIG_FILE | sed -e "s/;/,/g" -e "s/,$//") + LAYER2=$(sed -nr "/^\[ethernet-s390-options\]/ { :l /^layer2[ ]*=/ { s/.*=[ ]*//; p; q;}; n; b l;}" $CONFIG_FILE) + else + exit 1 + fi fi else echo "Unknown mode=$MODE" diff --git a/SOURCES/device_cio_free b/SOURCES/device_cio_free index cd2b21f..cfdced0 100644 --- a/SOURCES/device_cio_free +++ b/SOURCES/device_cio_free @@ -306,6 +306,14 @@ if [ $MODE_ZNET ]; then eval "$line" free_device $SUBCHANNELS done + for line in $(LANG=C grep -E -i -h \ + "^s390-subchannels=([0-9]\.[0-9]\.[a-f0-9]+;){2,3}$" \ + $( (ls /etc/NetworkManager/system-connections/*.nmconnection 2> /dev/null || echo "__no_config_file") | \ + LC_ALL=C sed -e "$__sed_discard_ignored_files") 2> /dev/null) + do + SUBCHANNELS="$(echo $line | sed -e "s/s390-subchannels=//" -e "s/;/,/g")" + free_device $SUBCHANNELS + done fi [ -z "$ALL_DEVICES" ] && exit 0 diff --git a/SOURCES/s390utils-2.15.1-rhel.patch b/SOURCES/s390utils-2.15.1-rhel.patch deleted file mode 100644 index 86f0a77..0000000 --- a/SOURCES/s390utils-2.15.1-rhel.patch +++ /dev/null @@ -1,3478 +0,0 @@ -From b67ac1d584e00c8c36c885a2fd36709a16809d86 Mon Sep 17 00:00:00 2001 -From: =?UTF-8?q?Dan=20Hor=C3=A1k?= -Date: Mon, 7 Dec 2020 14:26:26 +0100 -Subject: [PATCH 1/9] zdev/lsdasd: Add FC Endpoint Security information - (#1723844) - -Summary: zdev/lsdasd: Add FC Endpoint Security information -Description: Provide the status of the FC Endpoint Security information via the - long output of lsdasd for online Base and Alias devices. -Upstream-ID: d619b492e997cef0ddba8073f2a3073c7c2ecc5c -Upstream-ID: 79e4798061a89152b967a0761f88f59742722b06 ---- - zconf/lsdasd | 7 +++++-- - zdev/src/dasd.c | 17 +++++++++++++++++ - 2 files changed, 22 insertions(+), 2 deletions(-) - -diff --git a/zconf/lsdasd b/zconf/lsdasd -index 792efc0..795b33b 100755 ---- a/zconf/lsdasd -+++ b/zconf/lsdasd -@@ -387,6 +387,7 @@ function extended() - read EXTSZ 2> /dev/null < $DEVPATH/extent_pool/extent_size - read CAPACITY 2> /dev/null < $DEVPATH/capacity/logical_capacity - read ALLOCATED 2> /dev/null < $DEVPATH/capacity/space_allocated -+ read FC_SEC 2> /dev/null < $DEVPATH/fc_security - - # convert to hexadecimal values - PIM=0x$PIM -@@ -521,7 +522,7 @@ function extended() - elif [[ "$ALIAS" == 1 ]]; then - if [[ "$BASEONLY" == "false" ]]; then - ACTIVE="alias" -- printf "%s:%s:%s# status:\t\t\t\t%s# type: \t\t\t\t%s# use_diag:\t\t\t\t%s# readonly:\t\t\t\t%s# eer_enabled:\t\t\t\t%s# erplog:\t\t\t\t%s# hpf:\t\t\t\t\t%s # uid: \t\t\t\t%s# paths_installed: \t\t\t%s %s %s %s %s %s %s %s# paths_in_use: \t\t\t%s %s %s %s %s %s %s %s# paths_non_preferred: \t\t\t%s %s %s %s %s %s %s %s# paths_invalid_cabling: \t\t%s %s %s %s %s %s %s %s# paths_cuir_quiesced: \t\t\t%s %s %s %s %s %s %s %s# paths_invalid_hpf_characteristics: \t%s %s %s %s %s %s %s %s# paths_error_threshold_exceeded: \t%s %s %s %s %s %s %s %s#\n" \ -+ printf "%s:%s:%s# status:\t\t\t\t%s# type: \t\t\t\t%s# use_diag:\t\t\t\t%s# readonly:\t\t\t\t%s# eer_enabled:\t\t\t\t%s# erplog:\t\t\t\t%s# hpf:\t\t\t\t\t%s # uid: \t\t\t\t%s# fc_security: \t\t\t\t%s# paths_installed: \t\t\t%s %s %s %s %s %s %s %s# paths_in_use: \t\t\t%s %s %s %s %s %s %s %s# paths_non_preferred: \t\t\t%s %s %s %s %s %s %s %s# paths_invalid_cabling: \t\t%s %s %s %s %s %s %s %s# paths_cuir_quiesced: \t\t\t%s %s %s %s %s %s %s %s# paths_invalid_hpf_characteristics: \t%s %s %s %s %s %s %s %s# paths_error_threshold_exceeded: \t%s %s %s %s %s %s %s %s#\n" \ - "$SORTKEYLEN" "$SORTKEY" \ - "$BUSID" \ - "$ACTIVE" \ -@@ -532,6 +533,7 @@ function extended() - "$ERP" \ - "$HPF" \ - "$DEV_UID" \ -+ "$FC_SEC" \ - "${INSTALLED_PATHS[@]}" \ - "${USED_PATHS[@]}" \ - "${NP_PATHS[@]}" \ -@@ -563,7 +565,7 @@ function extended() - DISCIPLINE="${DISCIPLINE} (ESE)" - fi - -- printf "%s:%s:%s/%s/%s%s%s# status:\t\t\t\t%s# type: \t\t\t\t%s# blksz:\t\t\t\t%s# size: \t\t\t\t%s# blocks:\t\t\t\t%s# extent_size:\t\t\t\t%s# logical_capacity:\t\t\t%s# space_allocated:\t\t\t%s# use_diag:\t\t\t\t%s# readonly:\t\t\t\t%s# eer_enabled:\t\t\t\t%s# erplog:\t\t\t\t%s# hpf:\t\t\t\t\t%s# uid: \t\t\t\t%s# paths_installed: \t\t\t%s %s %s %s %s %s %s %s# paths_in_use: \t\t\t%s %s %s %s %s %s %s %s# paths_non_preferred: \t\t\t%s %s %s %s %s %s %s %s# paths_invalid_cabling: \t\t%s %s %s %s %s %s %s %s# paths_cuir_quiesced: \t\t\t%s %s %s %s %s %s %s %s# paths_invalid_hpf_characteristics: \t%s %s %s %s %s %s %s %s# paths_error_threshold_exceeded: \t%s %s %s %s %s %s %s %s#\n" \ -+ printf "%s:%s:%s/%s/%s%s%s# status:\t\t\t\t%s# type: \t\t\t\t%s# blksz:\t\t\t\t%s# size: \t\t\t\t%s# blocks:\t\t\t\t%s# extent_size:\t\t\t\t%s# logical_capacity:\t\t\t%s# space_allocated:\t\t\t%s# use_diag:\t\t\t\t%s# readonly:\t\t\t\t%s# eer_enabled:\t\t\t\t%s# erplog:\t\t\t\t%s# hpf:\t\t\t\t\t%s# uid: \t\t\t\t%s# fc_security: \t\t\t\t%s# paths_installed: \t\t\t%s %s %s %s %s %s %s %s# paths_in_use: \t\t\t%s %s %s %s %s %s %s %s# paths_non_preferred: \t\t\t%s %s %s %s %s %s %s %s# paths_invalid_cabling: \t\t%s %s %s %s %s %s %s %s# paths_cuir_quiesced: \t\t\t%s %s %s %s %s %s %s %s# paths_invalid_hpf_characteristics: \t%s %s %s %s %s %s %s %s# paths_error_threshold_exceeded: \t%s %s %s %s %s %s %s %s#\n" \ - "$SORTKEYLEN" "$SORTKEY" \ - "$BUSID" \ - "$BLOCKNAME" \ -@@ -584,6 +586,7 @@ function extended() - "$ERP" \ - "$HPF" \ - "$DEV_UID" \ -+ "$FC_SEC" \ - "${INSTALLED_PATHS[@]}" \ - "${USED_PATHS[@]}" \ - "${NP_PATHS[@]}" \ -diff --git a/zdev/src/dasd.c b/zdev/src/dasd.c -index 645e8b0..63b49e3 100644 ---- a/zdev/src/dasd.c -+++ b/zdev/src/dasd.c -@@ -313,6 +313,22 @@ static struct attrib dasd_attr_safe_offline = { - .writeonly = 1, - }; - -+static struct attrib dasd_attr_fc_security = { -+ .name = "fc_security", -+ .title = "Show FC Endpoint Security state of DASD device", -+ .desc = -+ "This read-only attribute shows the Fibre Channel Endpoint Security\n" -+ "status of the connection to the DASD device:\n" -+ " Unsupported : The DASD device does not support Fibre Channel\n" -+ " Endpoint Security\n" -+ " Inconsistent : The operational channel paths of the DASD device\n" -+ " report inconsistent Fibre Channel Endpoint\n" -+ " Security status\n" -+ " Authentication: The connection has been authenticated\n" -+ " Encryption : The connection is encrypted\n", -+ .readonly = 1, -+}; -+ - /* - * DASD subtype methods. - */ -@@ -617,6 +633,7 @@ struct subtype dasd_subtype_eckd = { - &dasd_attr_reservation_policy, - &dasd_attr_last_known_reservation_state, - &dasd_attr_safe_offline, -+ &dasd_attr_fc_security, - &internal_attr_early, - ), - .unknown_dev_attribs = 1, --- -2.26.2 - - -From 5f1a4aa22f2c294cd29d1e896c2629969c840dde Mon Sep 17 00:00:00 2001 -From: Marc Hartmayer -Date: Mon, 2 Nov 2020 14:30:58 +0100 -Subject: [PATCH 2/9] genprotimg: abort if one of the recursive targets is - failing (#1845925) -MIME-Version: 1.0 -Content-Type: text/plain; charset=UTF-8 -Content-Transfer-Encoding: 8bit - -Abort compilation as soon as one of the recursive targets is failing. - -Fixes: 65b9fc442c1a ("genprotimg: introduce new tool for the creation of PV images") -Signed-off-by: Marc Hartmayer -Signed-off-by: Jan Höppner -(cherry picked from commit 6db7fbe0187042f44a63a5c7dbeb9f116909d02e) ---- - genprotimg/Makefile | 2 +- - 1 file changed, 1 insertion(+), 1 deletion(-) - -diff --git a/genprotimg/Makefile b/genprotimg/Makefile -index 127bde2..4e81a63 100644 ---- a/genprotimg/Makefile -+++ b/genprotimg/Makefile -@@ -21,7 +21,7 @@ clean: clean-recursive - $(RECURSIVE_TARGETS): - @target=`echo $@ |sed s/-recursive//`; \ - for d in $(SUBDIRS); do \ -- $(MAKE) -C $$d $$target; \ -+ $(MAKE) -C $$d $$target || exit 1; \ - done - - .PHONY: all install clean $(RECURSIVE_TARGETS) --- -2.26.2 - - -From 9bbcbb300168b1e246b5b02605e359e5bbe8dceb Mon Sep 17 00:00:00 2001 -From: Marc Hartmayer -Date: Mon, 2 Nov 2020 15:55:46 +0100 -Subject: [PATCH 3/9] genprotimg: fix two memory leaks (#1845925) -MIME-Version: 1.0 -Content-Type: text/plain; charset=UTF-8 -Content-Transfer-Encoding: 8bit - -==1005844== HEAP SUMMARY: -==1005844== in use at exit: 18,907 bytes in 14 blocks -==1005844== total heap usage: 82 allocs, 68 frees, 32,529 bytes allocated -==1005844== -==1005844== 136 (104 direct, 32 indirect) bytes in 1 blocks are definitely lost in loss record 12 of 14 -==1005844== at 0x483885A: calloc (vg_replace_malloc.c:760) -==1005844== by 0x48C950D: g_malloc0 (gmem.c:132) -==1005844== by 0x100EC41: pv_args_new (pv_args.c:364) -==1005844== by 0x100587F: main (genprotimg.c:122) -==1005844== -==1005844== LEAK SUMMARY: -==1005844== definitely lost: 104 bytes in 1 blocks -==1005844== indirectly lost: 32 bytes in 1 blocks -==1005844== possibly lost: 0 bytes in 0 blocks -==1005844== still reachable: 18,771 bytes in 12 blocks -==1005844== suppressed: 0 bytes in 0 blocks -==1005844== Reachable blocks (those to which a pointer was found) are not shown. -==1005844== To see them, rerun with: --leak-check=full --show-leak-kinds=all -==1005844== -==1005844== For lists of detected and suppressed errors, rerun with: -s -==1005844== ERROR SUMMARY: 1 errors from 1 contexts (suppressed: 0 from 0) - -Signed-off-by: Marc Hartmayer -Signed-off-by: Jan Höppner -(cherry picked from commit db6f272607842a6279fee589fb101f3a1f6148f3) ---- - genprotimg/src/genprotimg.c | 2 ++ - 1 file changed, 2 insertions(+) - -diff --git a/genprotimg/src/genprotimg.c b/genprotimg/src/genprotimg.c -index 0d82550..c6de381 100644 ---- a/genprotimg/src/genprotimg.c -+++ b/genprotimg/src/genprotimg.c -@@ -177,5 +177,7 @@ error: - rmdir_recursive(tmp_dir, NULL); - remove_signal_handler(signals, G_N_ELEMENTS(signals)); - g_free(tmp_dir); -+ g_clear_pointer(&img, pv_img_free); -+ g_clear_pointer(&args, pv_args_free); - exit(ret); - } --- -2.26.2 - - -From 977d553fd81dbc5924f9b783231e1bacfe73ccd2 Mon Sep 17 00:00:00 2001 -From: Marc Hartmayer -Date: Wed, 11 Nov 2020 22:30:12 +0100 -Subject: [PATCH 4/9] genprotimg: require argument for 'ramdisk' and 'parmfile' - options (#1845925) -MIME-Version: 1.0 -Content-Type: text/plain; charset=UTF-8 -Content-Transfer-Encoding: 8bit - -A argument is required for the optional options 'ramdisk' and -'parmfile'. - -Fixes: 65b9fc442c1a ("genprotimg: introduce new tool for the creation of PV images") -Reviewed-by: Bjoern Walk -Signed-off-by: Marc Hartmayer -Signed-off-by: Jan Höppner -(cherry picked from commit 895a88b2f8d775e45ab1251f0b4bb275efd44a64) ---- - genprotimg/src/pv/pv_args.c | 4 ++-- - 1 file changed, 2 insertions(+), 2 deletions(-) - -diff --git a/genprotimg/src/pv/pv_args.c b/genprotimg/src/pv/pv_args.c -index 9fb7298..4bb78b5 100644 ---- a/genprotimg/src/pv/pv_args.c -+++ b/genprotimg/src/pv/pv_args.c -@@ -227,7 +227,7 @@ gint pv_args_parse_options(PvArgs *args, gint *argc, gchar **argv[], - .arg_description = _("IMAGE") }, - { .long_name = "ramdisk", - .short_name = 'r', -- .flags = G_OPTION_FLAG_OPTIONAL_ARG | G_OPTION_FLAG_FILENAME, -+ .flags = G_OPTION_FLAG_FILENAME, - .arg = G_OPTION_ARG_CALLBACK, - .arg_data = cb_add_component, - .description = _("Use RAMDISK as the initial RAM disk\n" INDENT -@@ -235,7 +235,7 @@ gint pv_args_parse_options(PvArgs *args, gint *argc, gchar **argv[], - .arg_description = _("RAMDISK") }, - { .long_name = "parmfile", - .short_name = 'p', -- .flags = G_OPTION_FLAG_OPTIONAL_ARG | G_OPTION_FLAG_FILENAME, -+ .flags = G_OPTION_FLAG_FILENAME, - .arg = G_OPTION_ARG_CALLBACK, - .arg_data = cb_add_component, - .description = _("Use the kernel parameters stored in PARMFILE\n" INDENT --- -2.26.2 - - -From c17cdcf28fabb373884f51c08b93502999910035 Mon Sep 17 00:00:00 2001 -From: Marc Hartmayer -Date: Mon, 2 Nov 2020 15:19:06 +0100 -Subject: [PATCH 5/9] genprotimg: add host-key document verification support - (#1845925) -MIME-Version: 1.0 -Content-Type: text/plain; charset=UTF-8 -Content-Transfer-Encoding: 8bit - -Add host-key document verification support to genprotimg. This ensures -that a host-key document is genuine and provided by IBM. For this the -user must provide the IBM Z signing key, the intermediate CA -certificate (signed by the root CA used) so a chain of trust starting -from the host-key document and ending in the root CA can be -established. - -By default, genprotimg tries to download all revocation lists needed -by looking up in the corresponding certificate on how CRL information -can be obtained (see https://tools.ietf.org/html/rfc5280#section-4.2.1.13 -for details). - -Acked-by: Patrick Steuer -Signed-off-by: Marc Hartmayer -Signed-off-by: Jan Höppner -(cherry picked from commit 074de1e14ed785c18f55ecf9762ac3f5de3465b4) ---- - README.md | 2 +- - genprotimg/man/genprotimg.8 | 49 +- - genprotimg/src/Makefile | 17 +- - genprotimg/src/genprotimg.c | 17 + - genprotimg/src/include/pv_crypto_def.h | 18 + - genprotimg/src/pv/pv_args.c | 57 +- - genprotimg/src/pv/pv_args.h | 6 + - genprotimg/src/pv/pv_error.h | 27 + - genprotimg/src/pv/pv_image.c | 203 +++- - genprotimg/src/utils/crypto.c | 1379 +++++++++++++++++++++++- - genprotimg/src/utils/crypto.h | 75 +- - genprotimg/src/utils/curl.c | 121 +++ - genprotimg/src/utils/curl.h | 25 + - 13 files changed, 1927 insertions(+), 69 deletions(-) - create mode 100644 genprotimg/src/utils/curl.c - create mode 100644 genprotimg/src/utils/curl.h - -diff --git a/README.md b/README.md -index aa2188a..6fb6831 100644 ---- a/README.md -+++ b/README.md -@@ -277,7 +277,7 @@ build options: - | cryptsetup | `HAVE_CRYPTSETUP2` | zkey-cryptsetup | - | json-c | `HAVE_JSONC` | zkey-cryptsetup, libekmfweb | - | glib2 | `HAVE_GLIB2` | genprotimg | --| libcurl | `HAVE_LIBCURL` | libekmfweb | -+| libcurl | `HAVE_LIBCURL` | genprotimg, libekmfweb | - - This table lists additional build or install options: - -diff --git a/genprotimg/man/genprotimg.8 b/genprotimg/man/genprotimg.8 -index 597106e..c08336b 100644 ---- a/genprotimg/man/genprotimg.8 -+++ b/genprotimg/man/genprotimg.8 -@@ -2,7 +2,7 @@ - .\" s390-tools is free software; you can redistribute it and/or modify - .\" it under the terms of the MIT license. See LICENSE for details. - .\" --.TH GENPROTIMG 8 "March 2020" "s390-tools" -+.TH GENPROTIMG 8 "November 2020" "s390-tools" - .SH NAME - genprotimg \- Create a protected virtualization image - -@@ -10,6 +10,7 @@ genprotimg \- Create a protected virtualization image - .SY - .B genprotimg - \fB\-k\fR \fIHOST_KEY_DOCUMENT\fR... -+\fB\-C\fR \fICERTIFICATE\fR... - \fB\-i\fR \fIVMLINUZ\fR - [\fB\-r\fR \fIRAMDISK\fR] - [\fB\-p\fR \fIPARMFILE\fR] -@@ -21,15 +22,19 @@ genprotimg \- Create a protected virtualization image - .PP - Use \fBgenprotimg\fR to generate a single bootable image file with - encrypted and integrity-protected parts. The command requires a kernel --image, a host-key document, and an output file name. Optionally, --specify an initial RAM filesystem, and a file containing the kernel --parameters. Should special circumstances require it, you can -+image, a host-key document, certificates for the host-key document -+verification, and an output file name. Optionally, specify an initial -+RAM filesystem, and a file containing the kernel parameters. If the -+command should be run offline, use the \fB\-\-offline\fR option and -+specify the certificate revocation lists (CRLs) by using the -+\fB\-\-crl\fR option. Should special circumstances require it, you can - optionally specify your own keys for the encryption by using the --experimental options. In the resulting image file, a plain text boot --loader, the encrypted components for kernel, initial RAM disk, kernel --parameters, and the encrypted and integrity-protected header are --concatenated. The header contains metadata necessary for running the --guest in protected mode. -+experimental options. For all certificates, CRLs, and host-key -+documents, both the PEM and DER input formats are supported. In the -+resulting image file, a plain text boot loader, the encrypted -+components for kernel, initial RAM disk, kernel parameters, and the -+encrypted and integrity-protected header are concatenated. The header -+contains metadata necessary for running the guest in protected mode. - .PP - Use this image file as a kernel image for zipl or for a direct kernel - boot using QEMU. -@@ -53,6 +58,12 @@ Specifies a host-key document. At least one is required. Specify this - option multiple times to enable the image to run on more than one - host. - .TP -+\fB\-C\fR, \fB\-\-cert\fR=\fI\,FILE\/\fR -+Specifies the certificate that is used to establish a chain of trust -+for the verification of the host-key documents. Specify this option -+twice to specify the IBM Z signing key and the intermediate CA -+certificate (signed by the root CA). Required. -+.TP - \fB\-o\fR, \fB\-\-output\fR=\fI\,OUTPUT_FILE\/\fR - Specifies the output file. Required. - .TP -@@ -65,6 +76,20 @@ Specifies the RAM disk image. Optional. - \fB\-p\fR, \fB\-\-parmfile\fR=\fI\,PARMFILE\/\fR - Specifies the kernel command line stored in \fI\,PARMFILE\/\fR. Optional. - .TP -+\fB\-\-crl\fR=\fI\,FILE\/\fR -+Specifies the revocation list that is used to check whether a -+certificate of the chain of trust is revoked. Specify this option -+multiple times to use multiple CRLs. Optional. -+.TP -+\fB\-\-offline\fR -+Specifies offline mode, in which no attempt is made to download -+CRLs. Optional. -+.TP -+\fB\-\-root\-ca\fR=\fI\,FILE\/\fR -+Specifies the root CA certificate for the verification. If omitted, -+the DigiCert root CA certificate installed on the system is used. Use -+this only if you trust the specified certificate. Optional. -+.TP - \fB\-\-no-verify\fR - Do not require the host-key documents to be valid. For testing - purposes, do not use for a production image. Optional. -@@ -77,11 +102,13 @@ Prints version information, then exits. - Generate a protected virtualization image in - \fI\,/boot/vmlinuz.pv\/\fR, using the kernel file \fI\,vmlinuz\/\fR, - the initrd in \fI\,initramfs\/\fR, the kernel parameters contained in --\fI\,parmfile\/\fR, and the host-key document in \fI\,host_key.crt\/\fR: -+\fI\,parmfile\/\fR, the intermediate CA in \fI\,DigiCertCA.crt\/\fR, -+the IBM Z signing key in \fI\,ibm-z-host-key-signing.crt\/\fR, and the -+host-key document in \fI\,host_key.crt\/\fR: - .PP - .Vb 1 - .EX --\& genprotimg \-i \fI\,vmlinuz\/\fR \-r \fI\,initramfs\/\fR \-p \fI\,parmfile\/\fR \-k \fI\,host_key.crt\/\fR \-o \fI\,/boot/vmlinuz.pv\/\fR -+\& genprotimg \-i \fI\,vmlinuz\/\fR \-r \fI\,initramfs\/\fR \-p \fI\,parmfile\/\fR \-k \fI\,host_key.crt\/\fR \-C \fI\,ibm-z-host-key-signing.crt\/\fR \-C \fI\,DigiCertCA.crt \-o \fI\,/boot/vmlinuz.pv\/\fR - .EE - .Ve - .PP -diff --git a/genprotimg/src/Makefile b/genprotimg/src/Makefile -index 1adeac3..2da15da 100644 ---- a/genprotimg/src/Makefile -+++ b/genprotimg/src/Makefile -@@ -23,16 +23,16 @@ WARNINGS := -Wall -Wextra -Wshadow \ - $(bin_PROGRAM)_SRCS := $(bin_PROGRAM).c pv/pv_stage3.c pv/pv_image.c \ - pv/pv_comp.c pv/pv_hdr.c pv/pv_ipib.c utils/crypto.c utils/file_utils.c \ - pv/pv_args.c utils/buffer.c pv/pv_comps.c pv/pv_error.c \ -- pv/pv_opt_item.c \ -+ pv/pv_opt_item.c utils/curl.c \ - $(NULL) - $(bin_PROGRAM)_OBJS := $($(bin_PROGRAM)_SRCS:.c=.o) - - ALL_CFLAGS += -std=gnu11 -DPKGDATADIR=$(PKGDATADIR) \ -- $(GLIB2_CFLAGS) $(LIBCRYPTO_CFLAGS) \ -+ $(GLIB2_CFLAGS) $(LIBCRYPTO_CFLAGS) $(LIBCURL_CFLAGS) \ - $(WARNINGS) \ - $(NULL) - ALL_CPPFLAGS += $(INCLUDE_PARMS) --LDLIBS += $(GLIB2_LIBS) $(LIBCRYPTO_LIBS) -+LDLIBS += $(GLIB2_LIBS) $(LIBCRYPTO_LIBS) $(LIBCURL_LIBS) - - - ifneq ($(shell sh -c 'command -v pkg-config'),) -@@ -40,21 +40,27 @@ GLIB2_CFLAGS := $(shell pkg-config --silence-errors --cflags glib-2.0) - GLIB2_LIBS := $(shell pkg-config --silence-errors --libs glib-2.0) - LIBCRYPTO_CFLAGS := $(shell pkg-config --silence-errors --cflags libcrypto) - LIBCRYPTO_LIBS := $(shell pkg-config --silence-errors --libs libcrypto) -+LIBCURL_CFLAGS := $(shell pkg-config --silence-errors --cflags libcurl) -+LIBCURL_LIBS := $(shell pkg-config --silence-errors --libs libcurl) - else - GLIB2_CFLAGS := -I/usr/include/glib-2.0 -I/usr/lib64/glib-2.0/include - GLIB2_LIBS := -lglib-2.0 - LIBCRYPTO_CFLAGS := - LIBCRYPTO_LIBS := -lcrypto -+LIBCURL_CFLAGS := -+LIBCURL_LIBS := -lcurl - endif - - BUILD_TARGETS := skip-$(bin_PROGRAM) - INSTALL_TARGETS := skip-$(bin_PROGRAM) - ifneq (${HAVE_OPENSSL},0) - ifneq (${HAVE_GLIB2},0) -+ifneq (${HAVE_LIBCURL},0) - BUILD_TARGETS := $(bin_PROGRAM) - INSTALL_TARGETS := install-$(bin_PROGRAM) - endif - endif -+endif - - all: $(BUILD_TARGETS) - -@@ -98,4 +104,9 @@ $($(bin_PROGRAM)_OBJS): .check-dep-$(bin_PROGRAM) - "openssl-devel / libssl-dev version >= 1.1.0", \ - "HAVE_OPENSSL=0", \ - "-I.") -+ $(call check_dep, \ -+ "$(bin_PROGRAM)", \ -+ "curl/curl.h", \ -+ "libcurl-devel", \ -+ "HAVE_LIBCURL=0") - touch $@ -diff --git a/genprotimg/src/genprotimg.c b/genprotimg/src/genprotimg.c -index c6de381..2041fa2 100644 ---- a/genprotimg/src/genprotimg.c -+++ b/genprotimg/src/genprotimg.c -@@ -18,6 +18,8 @@ - #include "common.h" - #include "pv/pv_args.h" - #include "pv/pv_image.h" -+#include "utils/crypto.h" -+#include "utils/curl.h" - - enum { - LOG_LEVEL_CRITICAL = 0, -@@ -117,6 +119,8 @@ static void remove_signal_handler(const gint *signals, const gsize signals_n) - signal(signals[i], SIG_DFL); - } - -+static void __attribute__((constructor)) __init(void); -+static void __attribute__((destructor)) __cleanup(void); - gint main(gint argc, gchar *argv[]) - { - g_autoptr(PvArgs) args = pv_args_new(); -@@ -181,3 +185,16 @@ error: - g_clear_pointer(&args, pv_args_free); - exit(ret); - } -+ -+static void __init(void) -+{ -+ pv_crypto_init(); -+ if (curl_init() != 0) -+ g_abort(); -+} -+ -+static void __cleanup(void) -+{ -+ curl_cleanup(); -+ pv_crypto_cleanup(); -+} -diff --git a/genprotimg/src/include/pv_crypto_def.h b/genprotimg/src/include/pv_crypto_def.h -index ddb8652..53984a3 100644 ---- a/genprotimg/src/include/pv_crypto_def.h -+++ b/genprotimg/src/include/pv_crypto_def.h -@@ -14,6 +14,24 @@ - - #include "lib/zt_common.h" - -+/* IBM signing key subject */ -+#define PV_IBM_Z_SUBJECT_COMMON_NAME "International Business Machines Corporation" -+#define PV_IBM_Z_SUBJECT_COUNTRY_NAME "US" -+#define PV_IBM_Z_SUBJECT_LOCALITY_NAME "Poughkeepsie" -+#define PV_IBM_Z_SUBJECT_ORGANIZATIONONAL_UNIT_NAME_SUFFIX "Key Signing Service" -+#define PV_IBM_Z_SUBJECT_ORGANIZATION_NAME "International Business Machines Corporation" -+#define PV_IBM_Z_SUBJECT_STATE "New York" -+#define PV_IMB_Z_SUBJECT_ENTRY_COUNT 6 -+ -+/* Minimum security level for the keys/certificates used to establish a chain of -+ * trust (see https://www.openssl.org/docs/man1.1.1/man3/X509_VERIFY_PARAM_set_auth_level.html -+ * for details). -+ */ -+#define PV_CERTS_SECURITY_LEVEL 2 -+ -+/* SKID for DigiCert Assured ID Root CA */ -+#define DIGICERT_ASSURED_ID_ROOT_CA_SKID "45EBA2AFF492CB82312D518BA7A7219DF36DC80F" -+ - union ecdh_pub_key { - struct { - uint8_t x[80]; -diff --git a/genprotimg/src/pv/pv_args.c b/genprotimg/src/pv/pv_args.c -index 4bb78b5..3ba1f94 100644 ---- a/genprotimg/src/pv/pv_args.c -+++ b/genprotimg/src/pv/pv_args.c -@@ -18,7 +18,9 @@ - - static gchar summary[] = - "Use genprotimg to create a protected virtualization kernel image file,\n" -- "which can be loaded using zipl or QEMU."; -+ "which can be loaded using zipl or QEMU. For all certificates, revocation\n" -+ "lists, and host-key documents, both the PEM and DER input formats are\n" -+ "supported."; - - static gint pv_arg_compare(gconstpointer arg_1, gconstpointer arg_2) - { -@@ -97,9 +99,14 @@ static gint pv_args_validate_options(PvArgs *args, GError **err) - return -1; - } - -- if (!args->no_verify) { -- g_set_error(err, PV_PARSE_ERROR, PR_PARSE_ERROR_MISSING_ARGUMENT, -- _("Use the option '--no-verify' as the verification support is not available yet.")); -+ if (!args->no_verify && -+ (!args->untrusted_cert_paths || -+ g_strv_length(args->untrusted_cert_paths) == 0)) { -+ g_set_error( -+ err, PV_PARSE_ERROR, PR_PARSE_ERROR_MISSING_ARGUMENT, -+ _("Either specify the IBM Z signing key and (DigiCert) intermediate CA certificate\n" -+ "by using the '--cert' option, or use the '--no-verify' flag to disable the\n" -+ "host-key document verification completely (at your own risk).")); - return -1; - } - -@@ -141,6 +148,8 @@ static gboolean cb_set_string_option(const gchar *option, const gchar *value, - { - gchar **args_option = NULL; - -+ if (g_str_equal(option, "--root-ca")) -+ args_option = &args->root_ca_path; - if (g_str_equal(option, "-o") || g_str_equal(option, "--output")) - args_option = &args->output_path; - if (g_str_equal(option, "--x-comp-key")) -@@ -211,6 +220,18 @@ gint pv_args_parse_options(PvArgs *args, gint *argc, gchar **argv[], - _("FILE specifies a host-key document. At least\n" INDENT - "one is required."), - .arg_description = _("FILE") }, -+ { .long_name = "cert", -+ .short_name = 'C', -+ .flags = G_OPTION_FLAG_NONE, -+ .arg = G_OPTION_ARG_FILENAME_ARRAY, -+ .arg_data = &args->untrusted_cert_paths, -+ .description = _( -+ "FILE contains a certificate that is used to\n" INDENT -+ "establish a chain of trust for the verification\n" INDENT -+ "of the host-key documents. The IBM Z signing\n" INDENT -+ "key and intermediate CA certificate (signed\n" INDENT -+ "by the root CA) are required."), -+ .arg_description = _("FILE") }, - { .long_name = "output", - .short_name = 'o', - .flags = G_OPTION_FLAG_FILENAME, -@@ -241,6 +262,31 @@ gint pv_args_parse_options(PvArgs *args, gint *argc, gchar **argv[], - .description = _("Use the kernel parameters stored in PARMFILE\n" INDENT - "(optional)."), - .arg_description = _("PARMFILE") }, -+ { .long_name = "crl", -+ .short_name = 0, -+ .flags = G_OPTION_FLAG_NONE, -+ .arg = G_OPTION_ARG_FILENAME_ARRAY, -+ .arg_data = &args->crl_paths, -+ .description = _( -+ "FILE contains a certificate revocation list\n" INDENT -+ "(optional)."), -+ .arg_description = _("FILE") }, -+ { .long_name = "offline", -+ .short_name = 0, -+ .flags = G_OPTION_FLAG_NONE, -+ .arg = G_OPTION_ARG_NONE, -+ .arg_data = &args->offline, -+ .description = _("Don't download CRLs (optional)."), -+ .arg_description = NULL }, -+ { .long_name = "root-ca", -+ .short_name = 0, -+ .flags = G_OPTION_FLAG_FILENAME, -+ .arg = G_OPTION_ARG_CALLBACK, -+ .arg_data = cb_set_string_option, -+ .description = _( -+ "Set FILE as the trusted root CA and don't use the\n" INDENT -+ "root CAs that are installed on the system (optional)."), -+ .arg_description = _("FILE") }, - { .long_name = "no-verify", - .short_name = 0, - .flags = G_OPTION_FLAG_NONE, -@@ -378,6 +424,9 @@ void pv_args_free(PvArgs *args) - g_free(args->cust_root_key_path); - g_free(args->cust_comm_key_path); - g_free(args->gcm_iv_path); -+ g_free(args->root_ca_path); -+ g_strfreev(args->crl_paths); -+ g_strfreev(args->untrusted_cert_paths); - g_strfreev(args->host_keys); - g_free(args->xts_key_path); - g_slist_free_full(args->comps, (GDestroyNotify)pv_arg_free); -diff --git a/genprotimg/src/pv/pv_args.h b/genprotimg/src/pv/pv_args.h -index f17e7b8..8939232 100644 ---- a/genprotimg/src/pv/pv_args.h -+++ b/genprotimg/src/pv/pv_args.h -@@ -25,6 +25,7 @@ void pv_arg_free(PvArg *arg); - typedef struct { - gint log_level; - gint no_verify; -+ gboolean offline; - gchar *pcf; - gchar *scf; - gchar *psw_addr; /* PSW address which will be used for the start of -@@ -34,6 +35,11 @@ typedef struct { - gchar *cust_comm_key_path; - gchar *gcm_iv_path; - gchar **host_keys; -+ gchar *root_ca_path; /* Trusted root CA used for the verification of the -+ * chain of trust (if specified). -+ */ -+ gchar **untrusted_cert_paths; -+ gchar **crl_paths; - gchar *xts_key_path; - GSList *comps; - gchar *output_path; -diff --git a/genprotimg/src/pv/pv_error.h b/genprotimg/src/pv/pv_error.h -index 1dd24fc..abe47ae 100644 ---- a/genprotimg/src/pv/pv_error.h -+++ b/genprotimg/src/pv/pv_error.h -@@ -28,6 +28,8 @@ typedef enum { - PV_ERROR_IPIB_SIZE, - PV_ERROR_PV_HDR_SIZE, - PV_ERROR_INTERNAL, -+ PV_ERROR_CURL_INIT_FAILED, -+ PV_ERROR_DOWNLOAD_FAILED, - } PvErrors; - - typedef enum { -@@ -57,6 +59,31 @@ typedef enum { - PV_CRYPTO_ERROR_RANDOMIZATION, - PV_CRYPTO_ERROR_INVALID_PARM, - PV_CRYPTO_ERROR_INVALID_KEY_SIZE, -+ PV_CRYPTO_ERROR_INVALID_VALIDITY_PERIOD, -+ PV_CRYPTO_ERROR_EXPIRED, -+ PV_CRYPTO_ERROR_NOT_VALID_YET, -+ PV_CRYPTO_ERROR_LOAD_CRL, -+ PV_CRYPTO_ERROR_NO_PUBLIC_KEY, -+ PV_CRYPTO_ERROR_INVALID_SIGNATURE_ALGORITHM, -+ PV_CRYPTO_ERROR_SIGNATURE_ALGORITHM_MISMATCH, -+ PV_CRYPTO_ERROR_INVALID_URI, -+ PV_CRYPTO_ERROR_CRL_DOWNLOAD_FAILED, -+ PV_CRYPTO_ERROR_CERT_SIGNATURE_INVALID, -+ PV_CRYPTO_ERROR_CRL_SIGNATURE_INVALID, -+ PV_CRYPTO_ERROR_CERT_SUBJECT_ISSUER_MISMATCH, -+ PV_CRYPTO_ERROR_CRL_SUBJECT_ISSUER_MISMATCH, -+ PV_CRYPTO_ERROR_NO_IBM_Z_SIGNING_KEY, -+ PV_CRYPTO_ERROR_MALFORMED_CERTIFICATE, -+ PV_CRYPTO_ERROR_NO_CRL, -+ PV_CRYPTO_ERROR_LOAD_ROOT_CA, -+ PV_CRYPTO_ERROR_LOAD_DEFAULT_CA, -+ PV_CRYPTO_ERROR_MALFORMED_ROOT_CA, -+ PV_CRYPTO_ERROR_WRONG_CA_USED, -+ PV_CRYPTO_ERROR_SKID_AKID_MISMATCH, -+ PV_CRYPTO_ERROR_NO_ISSUER_IBM_Z_FOUND, -+ PV_CRYPTO_ERROR_FAILED_DOWNLOAD_CRL, -+ PV_CRYPTO_ERROR_NO_CRLDP, -+ PV_CRYPTO_ERROR_CERT_REVOKED, - } PvCryptoErrors; - - #endif -diff --git a/genprotimg/src/pv/pv_image.c b/genprotimg/src/pv/pv_image.c -index 7ec5fe9..59eca5e 100644 ---- a/genprotimg/src/pv/pv_image.c -+++ b/genprotimg/src/pv/pv_image.c -@@ -10,6 +10,7 @@ - #include - #include - #include -+#include - #include - #include - #include -@@ -138,22 +139,18 @@ static EVP_PKEY *pv_img_get_cust_pub_priv_key(gint nid, GError **err) - return generate_ec_key(nid, err); - } - --static HostKeyList *pv_img_get_host_keys(gchar **host_cert_paths, -- X509_STORE *store, gint nid, -+static HostKeyList *pv_img_get_host_keys(GSList *host_keys_with_path, gint nid, - GError **err) - { - g_autoslist(EVP_PKEY) ret = NULL; - -- g_assert(host_cert_paths); -- -- for (gchar **iterator = host_cert_paths; iterator != NULL && *iterator != NULL; -- iterator++) { -+ for (GSList *iterator = host_keys_with_path; iterator; -+ iterator = iterator->next) { -+ x509_with_path *cert_with_path = iterator->data; - g_autoptr(EVP_PKEY) host_key = NULL; -- const gchar *path = *iterator; -- -- g_assert(path); -+ X509 *cert = cert_with_path->cert; - -- host_key = read_ec_pubkey_cert(store, nid, path, err); -+ host_key = read_ec_pubkey_cert(cert, nid, err); - if (!host_key) - return NULL; - -@@ -253,10 +250,172 @@ static gint pv_img_set_control_flags(PvImage *img, const gchar *pcf_s, - return 0; - } - -+static gint pv_img_hostkey_verify(GSList *host_key_certs, -+ const gchar *root_ca_path, -+ const gchar *const *crl_paths, -+ const gchar *const *untrusted_cert_paths, -+ gboolean offline, GError **err) -+{ -+ g_autoslist(x509_with_path) untrusted_certs_with_path = NULL; -+ g_autoptr(STACK_OF_X509) ibm_signing_certs = NULL; -+ g_autoptr(STACK_OF_X509) untrusted_certs = NULL; -+ g_autoslist(x509_pair) ibm_z_pairs = NULL; -+ g_autoptr(X509_STORE) trusted = NULL; -+ gint ibm_signing_certs_count; -+ -+ /* Load trusted root CAs of the system if and only if @root_ca_path is -+ * NULL, otherwise use the root CA specified by @root_ca_path. -+ */ -+ trusted = store_setup(root_ca_path, crl_paths, err); -+ if (!trusted) -+ goto error; -+ -+ if (!offline) { -+ g_autoptr(STACK_OF_X509_CRL) downloaded_ibm_signing_crls = NULL; -+ -+ /* Set up the download routine for the lookup of CRLs. */ -+ store_setup_crl_download(trusted); -+ -+ /* Try to download the CRLs of the IBM Z signing certificates -+ * specified in the host-key documents. Ignore download errors -+ * as it's still possible that a CRL is specified via command -+ * line. -+ */ -+ downloaded_ibm_signing_crls = try_load_crls_by_certs(host_key_certs); -+ -+ /* Add the downloaded CRLs to the store so they can be used for -+ * the verification later. -+ */ -+ for (int i = 0; i < sk_X509_CRL_num(downloaded_ibm_signing_crls); i++) { -+ X509_CRL *crl = sk_X509_CRL_value(downloaded_ibm_signing_crls, i); -+ -+ if (X509_STORE_add_crl(trusted, crl) != 1) { -+ g_set_error(err, PV_CRYPTO_ERROR, -+ PV_CRYPTO_ERROR_INTERNAL, -+ _("failed to load CRL")); -+ goto error; -+ } -+ } -+ } -+ -+ /* Load all untrusted certificates (e.g. IBM Z signing key and -+ * DigiCert intermediate CA) that are required to establish a chain of -+ * trust starting from the host-key document up to the root CA (if not -+ * otherwise specified that's the DigiCert Assured ID Root CA). -+ */ -+ untrusted_certs_with_path = load_certificates(untrusted_cert_paths, err); -+ if (!untrusted_certs_with_path) -+ goto error; -+ -+ /* Convert to STACK_OF(X509) */ -+ untrusted_certs = get_x509_stack(untrusted_certs_with_path); -+ -+ /* Find all IBM Z signing keys and remove them from the chain as we -+ * have to verify that they're valid. The last step of the chain of -+ * trust verification must be done manually, as the IBM Z signing keys -+ * are not marked as (intermediate) CA and therefore the standard -+ * `X509_verify_cert` function of OpenSSL cannot be used to verify the -+ * actual host-key documents. -+ */ -+ ibm_signing_certs = delete_ibm_signing_certs(untrusted_certs); -+ ibm_signing_certs_count = sk_X509_num(ibm_signing_certs); -+ if (ibm_signing_certs_count < 1) { -+ g_set_error(err, PV_CRYPTO_ERROR, PV_CRYPTO_ERROR_NO_IBM_Z_SIGNING_KEY, -+ _("please specify at least one IBM Z signing key")); -+ goto error; -+ } else if (ibm_signing_certs_count > 1) { -+ g_set_error(err, PV_CRYPTO_ERROR, PV_CRYPTO_ERROR_NO_IBM_Z_SIGNING_KEY, -+ _("please specify only one IBM Z signing key")); -+ goto error; -+ } -+ -+ if (store_set_verify_param(trusted, err) < 0) -+ goto error; -+ -+ /* Verify that the IBM Z signing keys are trustable. -+ * For this we must check: -+ * -+ * 1. Can a chain of trust be established ending in a root CA -+ * 2. Is the correct root CA ued? It has either to be the -+ * 'DigiCert Assured ID Root CA' or the root CA specified via -+ * command line. -+ */ -+ for (gint i = 0; i < sk_X509_num(ibm_signing_certs); ++i) { -+ X509 *ibm_signing_cert = sk_X509_value(ibm_signing_certs, i); -+ g_autoptr(STACK_OF_X509_CRL) ibm_signing_crls = NULL; -+ g_autoptr(X509_STORE_CTX) ctx = NULL; -+ x509_pair *pair = NULL; -+ -+ g_assert(ibm_signing_cert); -+ -+ /* Create the verification context and set the trusted -+ * and chain parameters. -+ */ -+ ctx = create_store_ctx(trusted, untrusted_certs, err); -+ if (!ctx) -+ goto error; -+ -+ /* Verify the IBM Z signing key */ -+ if (verify_cert(ibm_signing_cert, ctx, err) < 0) -+ goto error; -+ -+ /* Verify the build chain of trust chain. If the user passes a -+ * trusted root CA on the command line then the check for the -+ * Subject Key Identifier (SKID) is skipped, otherwise let's -+ * check if the SKID meets our expectation. -+ */ -+ if (!root_ca_path && -+ check_chain_parameters(X509_STORE_CTX_get0_chain(ctx), -+ get_digicert_assured_id_root_ca_skid(), -+ err) < 0) { -+ goto error; -+ } -+ -+ ibm_signing_crls = store_ctx_find_valid_crls(ctx, ibm_signing_cert, err); -+ if (!ibm_signing_crls) { -+ g_prefix_error(err, _("IBM Z signing key: ")); -+ goto error; -+ } -+ -+ /* Increment reference counter of @ibm_signing_cert as the -+ * certificate will now also be owned by @ibm_z_pairs. -+ */ -+ if (X509_up_ref(ibm_signing_cert) != 1) -+ g_abort(); -+ -+ pair = x509_pair_new(&ibm_signing_cert, &ibm_signing_crls); -+ ibm_z_pairs = g_slist_append(ibm_z_pairs, pair); -+ g_assert(!ibm_signing_cert); -+ g_assert(!ibm_signing_crls); -+ } -+ -+ /* Verify host-key documents by using the IBM Z signing -+ * certificates and the corresponding certificate revocation -+ * lists. -+ */ -+ for (GSList *iterator = host_key_certs; iterator; iterator = iterator->next) { -+ x509_with_path *host_key_with_path = iterator->data; -+ const gchar *host_key_path = host_key_with_path->path; -+ X509 *host_key = host_key_with_path->cert; -+ gint flags = X509_V_FLAG_CRL_CHECK; -+ -+ if (verify_host_key(host_key, ibm_z_pairs, flags, -+ PV_CERTS_SECURITY_LEVEL, err) < 0) { -+ g_prefix_error(err, "'%s': ", host_key_path); -+ goto error; -+ } -+ } -+ -+ return 0; -+error: -+ g_prefix_error(err, _("Failed to verify host-key document: ")); -+ return -1; -+} -+ - /* read in the keys or auto-generate them */ - static gint pv_img_set_keys(PvImage *img, const PvArgs *args, GError **err) - { -- g_autoptr(X509_STORE) store = NULL; -+ g_autoslist(x509_with_path) host_key_certs = NULL; - - g_assert(img->xts_cipher); - g_assert(img->cust_comm_cipher); -@@ -285,8 +444,25 @@ static gint pv_img_set_keys(PvImage *img, const PvArgs *args, GError **err) - if (!img->cust_pub_priv_key) - return -1; - -+ /* Load all host-key documents specified on the command line */ -+ host_key_certs = load_certificates((const gchar **)args->host_keys, -+ err); -+ if (!host_key_certs) -+ return -1; -+ -+ if (!args->no_verify && -+ pv_img_hostkey_verify(host_key_certs, args->root_ca_path, -+ (const gchar * const *)args->crl_paths, -+ (const gchar * const *)args->untrusted_cert_paths, -+ args->offline, err) < 0) { -+ return -1; -+ } -+ -+ /* Loads the public keys stored in the host-key documents and verify -+ * that the correct elliptic curve is used. -+ */ - img->host_pub_keys = -- pv_img_get_host_keys(args->host_keys, store, img->nid, err); -+ pv_img_get_host_keys(host_key_certs, img->nid, err); - if (!img->host_pub_keys) - return -1; - -@@ -406,6 +582,9 @@ PvImage *pv_img_new(PvArgs *args, const gchar *stage3a_path, GError **err) - if (args->no_verify) - g_warning(_("host-key document verification is disabled. Your workload is not secured.")); - -+ if (args->root_ca_path) -+ g_warning(_("A different root CA than the default DigiCert root CA is selected. Ensure that this root CA is trusted.")); -+ - ret->comps = pv_img_comps_new(EVP_sha512(), EVP_sha512(), EVP_sha512(), err); - if (!ret->comps) - return NULL; -diff --git a/genprotimg/src/utils/crypto.c b/genprotimg/src/utils/crypto.c -index b0c4aa9..0f774eb 100644 ---- a/genprotimg/src/utils/crypto.c -+++ b/genprotimg/src/utils/crypto.c -@@ -16,6 +16,11 @@ - #include - #include - #include -+#include -+#include -+#include -+#include -+#include - #include - #include - -@@ -25,8 +30,49 @@ - #include "pv/pv_error.h" - - #include "buffer.h" -+#include "curl.h" - #include "crypto.h" - -+#define DEFINE_GSLIST_MAP(t2, t1) \ -+ typedef t1 *(*g_slist_map_func_##t2##_##t1)(const t2 *x, \ -+ GError **err); \ -+ G_GNUC_UNUSED static GSList *g_slist_map_##t2##_##t1(const GSList *list, \ -+ g_slist_map_func_##t2##_##t1 func, \ -+ GError **err) \ -+ { \ -+ g_autoslist(t1) ret = NULL; \ -+ for (const GSList *iterator = list; iterator; \ -+ iterator = iterator->next) { \ -+ const t2 *value = iterator->data; \ -+ t1 *new_value = NULL; \ -+ g_assert(value); \ -+ new_value = func(value, err); \ -+ if (!new_value) \ -+ return NULL; \ -+ ret = g_slist_append(ret, g_steal_pointer(&new_value)); \ -+ } \ -+ return g_steal_pointer(&ret); \ -+ } -+ -+#define DEFINE_GSLIST_TO_STACK(t1) \ -+ G_GNUC_UNUSED static STACK_OF(t1) *g_slist_to_stack_of_##t1(GSList **list) \ -+ { \ -+ g_assert(list); \ -+ g_autoptr(STACK_OF_##t1) ret = sk_##t1##_new_null(); \ -+ if (!ret) \ -+ g_abort(); \ -+ for (GSList *iterator = *list; iterator; \ -+ iterator = iterator->next) { \ -+ if (sk_##t1##_push(ret, g_steal_pointer(&iterator->data)) == 0) \ -+ g_abort(); \ -+ } \ -+ g_clear_pointer(list, g_slist_free); \ -+ return g_steal_pointer(&ret); \ -+ } -+ -+DEFINE_GSLIST_MAP(x509_with_path, X509) -+DEFINE_GSLIST_TO_STACK(X509) -+ - EVP_MD_CTX *digest_ctx_new(const EVP_MD *md, GError **err) - { - g_autoptr(EVP_MD_CTX) ctx = EVP_MD_CTX_new(); -@@ -359,79 +405,1340 @@ static gboolean certificate_uses_correct_curve(EVP_PKEY *key, gint nid, - return TRUE; - } - --static gboolean verify_certificate(X509_STORE *store, X509 *cert, GError **err) -+/* Verify that the used public key algorithm matches the subject signature -+ * algorithm -+ */ -+static int check_signature_algo_match(const EVP_PKEY *pkey, const X509 *subject, -+ GError **err) - { -- g_autoptr(X509_STORE_CTX) csc = X509_STORE_CTX_new(); -- if (!csc) -- g_abort(); -+ gint pkey_nid; - -- if (X509_STORE_CTX_init(csc, store, cert, NULL) != 1) { -- g_set_error(err, PV_CRYPTO_ERROR, PV_CRYPTO_ERROR_INIT, -- _("Failed to initialize X.509 store")); -- return FALSE; -+ if (!pkey) { -+ g_set_error(err, PV_CRYPTO_ERROR, PV_CRYPTO_ERROR_NO_PUBLIC_KEY, -+ _("no public key")); -+ return -1; - } - -- if (X509_verify_cert(csc) != 1) { -- g_set_error(err, PV_CRYPTO_ERROR, PV_CRYPTO_ERROR_VERIFICATION, -- _("Failed to verify host-key document")); -- return FALSE; -+ if (OBJ_find_sigid_algs(X509_get_signature_nid(subject), NULL, -+ &pkey_nid) != 1) { -+ g_set_error(err, PV_CRYPTO_ERROR, -+ PV_CRYPTO_ERROR_INVALID_SIGNATURE_ALGORITHM, -+ _("unsupported signature algorithm")); -+ return -1; - } - -- return TRUE; -+ if (EVP_PKEY_type(pkey_nid) != EVP_PKEY_base_id(pkey)) { -+ g_set_error(err, PV_CRYPTO_ERROR, -+ PV_CRYPTO_ERROR_SIGNATURE_ALGORITHM_MISMATCH, -+ _("signature algorithm mismatch")); -+ return -1; -+ } -+ -+ return 0; -+} -+ -+static X509_CRL *load_crl_from_bio(BIO *bio) -+{ -+ g_autoptr(X509_CRL) crl = PEM_read_bio_X509_CRL(bio, NULL, 0, NULL); -+ if (crl) -+ return g_steal_pointer(&crl); -+ ERR_clear_error(); -+ BIO_reset(bio); -+ -+ /* maybe the CRL is stored in DER format */ -+ crl = d2i_X509_CRL_bio(bio, NULL); -+ if (crl) -+ return g_steal_pointer(&crl); -+ return NULL; -+} -+ -+static X509_CRL *GByteArray_to_X509_CRL(const GByteArray *data) -+{ -+ g_autoptr(X509_CRL) ret = NULL; -+ g_autoptr(BIO) bio = NULL; -+ -+ g_assert(data); -+ -+ if (data->len > INT_MAX) -+ return NULL; -+ -+ bio = BIO_new_mem_buf(data->data, (int)data->len); -+ if (!bio) -+ g_abort(); -+ -+ ret = load_crl_from_bio(bio); -+ if (!ret) -+ return NULL; -+ -+ return g_steal_pointer(&ret); -+} -+ -+static gint load_crl_from_web(const gchar *url, X509_CRL **crl, GError **err) -+{ -+ g_autoptr(X509_CRL) tmp_crl = NULL; -+ g_autoptr(GByteArray) data = NULL; -+ g_assert(crl); -+ -+ data = curl_download(url, CRL_DOWNLOAD_TIMEOUT_MS, -+ CRL_DOWNLOAD_MAX_SIZE, err); -+ if (!data) { -+ g_prefix_error(err, _("unable to download CRL: ")); -+ return -1; -+ } -+ tmp_crl = GByteArray_to_X509_CRL(data); -+ if (!tmp_crl) { -+ g_set_error(err, PV_CRYPTO_ERROR, -+ PV_CRYPTO_ERROR_CRL_DOWNLOAD_FAILED, -+ _("unable to load CRL from '%s'"), url); -+ return -1; -+ } -+ *crl = g_steal_pointer(&tmp_crl); -+ return 0; -+} -+ -+static BIO *bio_read_from_file(const char *path) -+{ -+ g_autoptr(BIO) bio = BIO_new_file(path, "r"); -+ -+ if (!bio) -+ return NULL; -+ -+ return g_steal_pointer(&bio); - } - --static X509 *load_certificate(const gchar *path, GError **err) -+/* This function reads in only the first certificate and ignores all other. This -+ * is only relevant for the PEM file format. For the host-key document and the -+ * root CA this behavior is expected. -+ */ -+X509 *load_cert_from_file(const char *path, GError **err) - { -- g_autoptr(X509) ret = NULL; -- g_autoptr(BIO) bio = BIO_new_file(path, "rb"); -+ g_autoptr(BIO) bio = bio_read_from_file(path); -+ g_autoptr(X509) cert = NULL; - - if (!bio) { - g_set_error(err, PV_CRYPTO_ERROR, - PV_CRYPTO_ERROR_READ_CERTIFICATE, -- _("Failed to read host-key document: '%s'"), path); -+ _("unable to read certificate: '%s'"), path); - return NULL; - } - -- ret = PEM_read_bio_X509(bio, NULL, 0, NULL); -- if (!ret) { -- g_set_error(err, PV_CRYPTO_ERROR, -- PV_CRYPTO_ERROR_READ_CERTIFICATE, -- _("Failed to load host-key document: '%s'"), path); -+ cert = PEM_read_bio_X509(bio, NULL, NULL, NULL); -+ if (cert) -+ return g_steal_pointer(&cert); -+ ERR_clear_error(); -+ BIO_reset(bio); -+ -+ /* maybe the certificate is stored in DER format */ -+ cert = d2i_X509_bio(bio, NULL); -+ if (cert) -+ return g_steal_pointer(&cert); -+ -+ g_set_error(err, PV_CRYPTO_ERROR, PV_CRYPTO_ERROR_READ_CERTIFICATE, -+ _("unable to load certificate: '%s'"), path); -+ return NULL; -+} -+ -+/* @crl_paths is allowed to be NULL */ -+static int load_crls_to_store(X509_STORE *store, const gchar *const *crl_paths, -+ gboolean err_out_empty_crls, GError **err) -+{ -+ for (const gchar *const *iterator = crl_paths; -+ iterator != NULL && *iterator != NULL; iterator++) { -+ const gchar *crl_path = *iterator; -+ X509_LOOKUP *lookup = X509_STORE_add_lookup(store, X509_LOOKUP_file()); -+ int count; -+ -+ g_assert(crl_path); -+ -+ if (!lookup) { -+ g_set_error(err, PV_CRYPTO_ERROR, PV_CRYPTO_ERROR_INTERNAL, -+ _("X509 store initialization failed")); -+ return -1; -+ } -+ -+ /* support *.pem files containing multiple CRLs */ -+ count = X509_load_crl_file(lookup, crl_path, X509_FILETYPE_PEM); -+ if (count > 0) -+ continue; -+ -+ count = X509_load_crl_file(lookup, crl_path, X509_FILETYPE_ASN1); -+ if (count == 1) -+ continue; -+ -+ if (err_out_empty_crls) { -+ g_set_error(err, PV_CRYPTO_ERROR, -+ PV_CRYPTO_ERROR_LOAD_CRL, -+ _("unable to load CRL from: '%s'"), crl_path); -+ return -1; -+ } -+ } -+ -+ return 0; -+} -+ -+/* returns -+ * 0 when the certificate is valid, -+ * -1 when not yet valid, -+ * 1 when expired -+ */ -+static int check_validity_period(const ASN1_TIME *not_before, const ASN1_TIME *not_after) -+{ -+ if (X509_cmp_current_time(not_before) != -1) -+ return -1; -+ -+ if (X509_cmp_current_time(not_after) != 1) -+ return 1; -+ -+ return 0; -+} -+ -+static gint x509_name_entry_get_data0(X509_NAME_ENTRY *entry, const guchar **data, -+ gsize *data_len) -+{ -+ const ASN1_STRING *asn1_str; -+ gint tmp_data_len; -+ -+ g_assert(data); -+ g_assert(data_len); -+ -+ asn1_str = X509_NAME_ENTRY_get_data(entry); -+ if (!asn1_str) -+ return -1; -+ -+ tmp_data_len = ASN1_STRING_length(asn1_str); -+ if (tmp_data_len < 0) -+ return -1; -+ -+ *data = ASN1_STRING_get0_data(asn1_str); -+ *data_len = (gsize)tmp_data_len; -+ return 0; -+} -+ -+/* The caller must not free *data! */ -+static gint x509_name_get_data0_by_NID(X509_NAME *name, gint nid, -+ const guchar **data, gsize *data_len) -+{ -+ -+ X509_NAME_ENTRY *entry = NULL; -+ gint lastpos = -1; -+ -+ lastpos = X509_NAME_get_index_by_NID(name, nid, lastpos); -+ if (lastpos == -1) -+ return -1; -+ -+ entry = X509_NAME_get_entry(name, lastpos); -+ if (!entry) -+ return -1; -+ -+ if (x509_name_entry_get_data0(entry, data, data_len) < 0) -+ return -1; -+ -+ return 0; -+} -+ -+/* @y must be a NULL-terminated string */ -+static gboolean x509_name_data_by_nid_equal(X509_NAME *name, gint nid, -+ const gchar *y) -+{ -+ const guchar *data = NULL; -+ gsize y_len = strlen(y); -+ gsize data_len; -+ -+ if (x509_name_get_data0_by_NID(name, nid, &data, &data_len) < 0) -+ return FALSE; -+ -+ if (data_len != y_len) -+ return FALSE; -+ -+ return memcmp(data, y, data_len) == 0; -+} -+ -+static gboolean own_X509_NAME_ENTRY_equal(const X509_NAME_ENTRY *x, -+ const X509_NAME_ENTRY *y) -+{ -+ const ASN1_OBJECT *x_obj = X509_NAME_ENTRY_get_object(x); -+ const ASN1_STRING *x_data = X509_NAME_ENTRY_get_data(x); -+ const ASN1_OBJECT *y_obj = X509_NAME_ENTRY_get_object(y); -+ const ASN1_STRING *y_data = X509_NAME_ENTRY_get_data(y); -+ gint x_len = ASN1_STRING_length(x_data); -+ gint y_len = ASN1_STRING_length(y_data); -+ -+ if (x_len < 0 || x_len != y_len) -+ return FALSE; -+ -+ /* ASN1_STRING_cmp(x_data, y_data) == 0 doesn't work because it also -+ * compares the type, which is sometimes different. -+ */ -+ return OBJ_cmp(x_obj, y_obj) == 0 && -+ memcmp(ASN1_STRING_get0_data(x_data), -+ ASN1_STRING_get0_data(y_data), -+ (unsigned long)x_len) == 0; -+} -+ -+static gboolean own_X509_NAME_equal(const X509_NAME *x, const X509_NAME *y) -+{ -+ gint x_count = X509_NAME_entry_count(x); -+ gint y_count = X509_NAME_entry_count(y); -+ -+ if (x != y && (!x || !y)) -+ return FALSE; -+ -+ if (x_count != y_count) -+ return FALSE; -+ -+ for (gint i = 0; i < x_count; i++) { -+ const X509_NAME_ENTRY *entry_i = X509_NAME_get_entry(x, i); -+ gboolean entry_found = FALSE; -+ -+ for (gint j = 0; j < y_count; j++) { -+ const X509_NAME_ENTRY *entry_j = -+ X509_NAME_get_entry(y, j); -+ -+ if (own_X509_NAME_ENTRY_equal(entry_i, entry_j)) { -+ entry_found = TRUE; -+ break; -+ } -+ } -+ -+ if (!entry_found) -+ return FALSE; -+ } -+ return TRUE; -+} -+ -+/* Checks whether the subject of @cert is a IBM signing key subject. For this we -+ * must check that the subject is equal to: 'C = US, ST = New York, L = -+ * Poughkeepsie, O = International Business Machines Corporation, CN = -+ * International Business Machines Corporation' and the organization unit (OUT) -+ * must end with the suffix ' Key Signing Service'. -+ */ -+static gboolean has_ibm_signing_subject(X509 *cert) -+{ -+ X509_NAME *subject = X509_get_subject_name(cert); -+ /* X509_NAME_entry_count is safe to be used with NULL */ -+ gint entry_count = X509_NAME_entry_count(subject); -+ g_autofree gchar *data_str = NULL; -+ const guchar *data; -+ gsize data_len; -+ -+ if (entry_count != PV_IMB_Z_SUBJECT_ENTRY_COUNT) -+ return FALSE; -+ -+ if (!x509_name_data_by_nid_equal(subject, NID_countryName, -+ PV_IBM_Z_SUBJECT_COUNTRY_NAME)) -+ return FALSE; -+ -+ if (!x509_name_data_by_nid_equal(subject, NID_stateOrProvinceName, -+ PV_IBM_Z_SUBJECT_STATE)) -+ return FALSE; -+ -+ if (!x509_name_data_by_nid_equal(subject, NID_localityName, -+ PV_IBM_Z_SUBJECT_LOCALITY_NAME)) -+ return FALSE; -+ -+ if (!x509_name_data_by_nid_equal(subject, NID_organizationName, -+ PV_IBM_Z_SUBJECT_ORGANIZATION_NAME)) -+ return FALSE; -+ -+ if (!x509_name_data_by_nid_equal(subject, NID_commonName, -+ PV_IBM_Z_SUBJECT_COMMON_NAME)) -+ return FALSE; -+ -+ if (x509_name_get_data0_by_NID(subject, NID_organizationalUnitName, -+ &data, &data_len) < 0) -+ return FALSE; -+ -+ /* Make sure that data_str is null-terminated as in general it cannot be -+ * assumed that @data is null-terminated. -+ */ -+ data_str = g_strndup((const gchar *)data, data_len); -+ if (!g_str_has_suffix(data_str, -+ PV_IBM_Z_SUBJECT_ORGANIZATIONONAL_UNIT_NAME_SUFFIX)) -+ return FALSE; -+ -+ return TRUE; -+} -+ -+static X509_NAME *x509_name_reorder_attributes(const X509_NAME *name, const gint nids[], -+ gsize nids_len) -+{ -+ gint entry_count = X509_NAME_entry_count(name); -+ g_autoptr(X509_NAME) ret = NULL; -+ -+ if (entry_count < 0) -+ return NULL; -+ -+ if (nids_len != (gsize) entry_count) - return NULL; -+ -+ ret = X509_NAME_new(); -+ if (!ret) -+ g_abort(); -+ -+ for (gsize i = 0; i < nids_len; i++) { -+ const X509_NAME_ENTRY *entry = NULL; -+ gint nid = nids[i]; -+ gint lastpos = -1; -+ -+ lastpos = X509_NAME_get_index_by_NID((X509_NAME *)name, nid, lastpos); -+ if (lastpos == -1) -+ return NULL; -+ -+ entry = X509_NAME_get_entry(name, lastpos); -+ if (!entry) -+ return NULL; -+ -+ if (X509_NAME_add_entry(ret, entry, -1, 0) != 1) -+ return NULL; - } - - return g_steal_pointer(&ret); - } - --EVP_PKEY *read_ec_pubkey_cert(X509_STORE *store, gint nid, const gchar *path, -- GError **err) -+/* In RFC 5280 the attributes of a (subject/issuer) name is not mandatory -+ * ordered. The problem is that our certificates are not consistent in the order -+ * (see https://tools.ietf.org/html/rfc5280#section-4.1.2.4 for details). -+ * -+ * This function converts a correct X509_NAME into the broken one. The caller is -+ * responsible to free the returned value. -+ */ -+X509_NAME *c2b_name(const X509_NAME *name) - { -- g_autoptr(EVP_PKEY) ret = NULL; -- g_autoptr(X509) cert = NULL; -+ gint nids[] = { NID_countryName, NID_organizationName, NID_organizationalUnitName, -+ NID_localityName, NID_stateOrProvinceName, NID_commonName }; -+ g_autoptr(X509_NAME) broken_name = NULL; - -- cert = load_certificate(path, err); -- if (!cert) -+ g_assert(name); -+ -+ /* Try to reorder the attributes */ -+ broken_name = x509_name_reorder_attributes(name, nids, G_N_ELEMENTS(nids)); -+ if (broken_name) -+ return g_steal_pointer(&broken_name); -+ return X509_NAME_dup((X509_NAME *)name); -+} -+ -+/* Verify that: subject(issuer) == issuer(crl) and SKID(issuer) == AKID(crl) */ -+static gint check_crl_issuer(X509_CRL *crl, X509 *issuer, GError **err) -+{ -+ const X509_NAME *crl_issuer = X509_CRL_get_issuer(crl); -+ const X509_NAME *issuer_subject = X509_get_subject_name(issuer); -+ AUTHORITY_KEYID *akid = NULL; -+ -+ if (!own_X509_NAME_equal(issuer_subject, crl_issuer)) { -+ g_autofree char *issuer_subject_str = X509_NAME_oneline(issuer_subject, -+ NULL, 0); -+ g_autofree char *crl_issuer_str = X509_NAME_oneline(crl_issuer, NULL, 0); -+ -+ g_set_error(err, PV_CRYPTO_ERROR, -+ PV_CRYPTO_ERROR_CRL_SUBJECT_ISSUER_MISMATCH, -+ _("issuer mismatch:\n%s\n%s"), -+ issuer_subject_str, crl_issuer_str); -+ return -1; -+ } -+ -+ /* If AKID(@crl) is specified it must match with SKID(@issuer) */ -+ akid = X509_CRL_get_ext_d2i(crl, NID_authority_key_identifier, NULL, NULL); -+ if (akid && X509_check_akid(issuer, akid) != X509_V_OK) { -+ g_set_error(err, PV_CRYPTO_ERROR, PV_CRYPTO_ERROR_SKID_AKID_MISMATCH, -+ _("AKID mismatch")); -+ return -1; -+ } -+ -+ return 0; -+} -+ -+/* Verify whether a revocation list @crl is valid and is issued by @cert. For -+ * this multiple steps must be done: -+ * -+ * 1. verify issuer of the CRL matches with the suject name of @cert -+ * 2. verify the validity period of the CRL -+ * 3. verify the signature of the CRL -+ * -+ * Important: This function doesn't verify whether @cert is allowed to issue a -+ * CRL. Returns 0 if @crl is valid and issued by @cert, otherwise -1. -+ */ -+gint check_crl_valid_for_cert(X509_CRL *crl, X509 *cert, -+ gint verify_flags, GError **err) -+{ -+ EVP_PKEY *pkey = X509_get0_pubkey(cert); -+ -+ g_assert(crl); -+ -+ if (!pkey) { -+ g_set_error(err, PV_CRYPTO_ERROR, PV_CRYPTO_ERROR_INTERNAL, -+ _("failed to retrieve public key from the certificate")); -+ return -1; -+ } -+ -+ /* check that the @crl issuer matches with the subject name of @cert*/ -+ if (check_crl_issuer(crl, cert, err) < 0) -+ return -1; -+ -+ /* verify the validity period of the CRL */ -+ if (!(verify_flags & X509_V_FLAG_NO_CHECK_TIME)) { -+ const ASN1_TIME *last = X509_CRL_get0_lastUpdate(crl); -+ const ASN1_TIME *next = X509_CRL_get0_nextUpdate(crl); -+ -+ if (!last || !next || check_validity_period(last, next)) { -+ g_set_error(err, PV_CRYPTO_ERROR, -+ PV_CRYPTO_ERROR_INVALID_VALIDITY_PERIOD, -+ _("validity period is not valid")); -+ return -1; -+ } -+ } else { -+ verify_flags &= ~X509_V_FLAG_NO_CHECK_TIME; -+ } -+ -+ /* verify the signature */ -+ if (X509_CRL_verify(crl, pkey) != 1) { -+ g_set_error(err, PV_CRYPTO_ERROR, PV_CRYPTO_ERROR_CRL_SIGNATURE_INVALID, -+ _("signature is not valid")); -+ return -1; -+ } -+ g_assert(verify_flags == 0); -+ return 0; -+} -+ -+/* Given a certificate @cert try to find valid revocation lists in @ctx. If no -+ * valid CRL was found NULL is returned. -+ */ -+STACK_OF_X509_CRL *store_ctx_find_valid_crls(X509_STORE_CTX *ctx, X509 *cert, -+ GError **err) -+{ -+ g_autoptr(STACK_OF_X509_CRL) ret = NULL; -+ const gint verify_flags = 0; -+ X509_NAME *subject = NULL; -+ -+ subject = X509_get_subject_name(cert); -+ if (!subject) { -+ g_set_error(err, PV_CRYPTO_ERROR, PV_CRYPTO_ERROR_MALFORMED_CERTIFICATE, -+ _("certificate is malformed")); - return NULL; -+ } - -- if (store && !verify_certificate(store, cert, err)) { -- g_prefix_error(err, -- _("Failed to load host-key document: '%s': "), -- path); -+ ret = X509_STORE_CTX_get1_crls(ctx, subject); -+ if (!ret) { -+ /* Workaround to fix the mismatch between issuer name of the -+ * IBM Z signing CRLs and the IBM Z signing key subject name. -+ */ -+ g_autoptr(X509_NAME) broken_subject = c2b_name(subject); -+ -+ ret = X509_STORE_CTX_get1_crls(ctx, broken_subject); -+ if (!ret) { -+ g_set_error(err, PV_CRYPTO_ERROR, PV_CRYPTO_ERROR_NO_CRL, -+ _("no CRL found")); -+ return NULL; -+ } -+ } -+ -+ /* Filter out non-valid CRLs for @cert */ -+ for (gint i = 0; i < sk_X509_CRL_num(ret); i++) { -+ X509_CRL *crl = sk_X509_CRL_value(ret, i); -+ -+ g_assert(crl); -+ -+ /* If @crl is not valid remove it from the array and log a -+ * warning. -+ */ -+ if (check_crl_valid_for_cert(crl, cert, verify_flags, err) < 0) { -+ g_assert(err); -+ g_warning(_("CRL is not valid: %s"), (*err)->message); -+ g_clear_error(err); -+ -+ /* Remove this certificate from the list and change i-- as the -+ * array has changed - this is not beautfiul, but right now the -+ * easiest solution I came up with -+ */ -+ if (sk_X509_CRL_delete(ret, i--) != crl) -+ g_abort(); -+ -+ g_clear_pointer(&crl, X509_CRL_free); -+ } -+ } -+ -+ if (sk_X509_CRL_num(ret) < 1) { -+ g_set_error(err, PV_CRYPTO_ERROR, PV_CRYPTO_ERROR_NO_CRL, -+ _("no valid CRL found")); - return NULL; - } -+ return g_steal_pointer(&ret); -+} -+ -+/* Return a list of all IBM Z signing key certificates in @certs and remove them -+ * from the chain. Return empty stack if no IBM Z signing key is found. -+ */ -+STACK_OF_X509 *delete_ibm_signing_certs(STACK_OF_X509 *certs) -+{ -+ g_autoptr(STACK_OF_X509) ret = sk_X509_new_null(); -+ -+ for (gint i = 0; i < sk_X509_num(certs); i++) { -+ X509 *cert = sk_X509_value(certs, i); -+ -+ g_assert(cert); -+ -+ if (!has_ibm_signing_subject(cert)) -+ continue; -+ -+ /* Remove this certificate from the list and change i-- as the -+ * array has changed - this is not beautfiul, but right now the -+ * easiest solution I came up with. -+ */ -+ if (sk_X509_delete(certs, i--) != cert) -+ g_abort(); -+ -+ if (sk_X509_push(ret, g_steal_pointer(&cert)) == 0) -+ g_abort(); -+ } -+ -+ return g_steal_pointer(&ret); -+} -+ -+X509_STORE *store_setup(const gchar *root_ca_path, const gchar * const *crl_paths, -+ GError **err) -+{ -+ g_autoptr(X509_STORE) store = X509_STORE_new(); -+ -+ g_assert(store); -+ -+ /* if @root_ca_path != NULL use the specified root CA only, otherwise use the -+ * default root CAs found on the system -+ */ -+ if (root_ca_path) { -+ X509_LOOKUP *lookup = X509_STORE_add_lookup(store, X509_LOOKUP_file()); -+ int count; -+ -+ if (!lookup) { -+ g_set_error(err, PV_CRYPTO_ERROR, PV_CRYPTO_ERROR_INTERNAL, -+ _("X509 store initialization failed")); -+ return NULL; -+ } -+ -+ count = X509_load_cert_file(lookup, root_ca_path, X509_FILETYPE_PEM); -+ if (count > 1) { -+ g_set_error(err, PV_CRYPTO_ERROR, -+ PV_CRYPTO_ERROR_LOAD_ROOT_CA, -+ _("multiple certificates in one PEM file is not supported: '%s'"), -+ root_ca_path); -+ return NULL; -+ } else if (count < 1) { -+ count = X509_load_cert_file(lookup, root_ca_path, -+ X509_FILETYPE_ASN1); -+ if (count != 1) { -+ g_set_error(err, PV_CRYPTO_ERROR, -+ PV_CRYPTO_ERROR_LOAD_ROOT_CA, -+ _("failed to load root certificate from '%s'"), -+ root_ca_path); -+ return NULL; -+ } -+ } -+ } else { -+ /* Load certificates into @store from the hardcoded OpenSSL -+ * default paths -+ */ -+ if (X509_STORE_set_default_paths(store) != 1) { -+ g_set_error(err, PV_CRYPTO_ERROR, -+ PV_CRYPTO_ERROR_LOAD_DEFAULT_CA, -+ _("failed to load system root certificates")); -+ return NULL; -+ } -+ } -+ -+ /* Error out if a CRL file was provided that has not at least one CRL*/ -+ if (load_crls_to_store(store, crl_paths, TRUE, err) < 0) -+ return NULL; -+ -+ return g_steal_pointer(&store); -+} -+ -+int store_set_verify_param(X509_STORE *store, GError **err) -+{ -+ g_autoptr(X509_VERIFY_PARAM) param = NULL; -+ unsigned long flags = X509_V_FLAG_CRL_CHECK | -+ X509_V_FLAG_CRL_CHECK_ALL | -+ X509_V_FLAG_TRUSTED_FIRST | -+ X509_V_FLAG_CHECK_SS_SIGNATURE | -+ X509_V_FLAG_X509_STRICT | -+ X509_V_FLAG_POLICY_CHECK; -+ -+ /* Create a X509_VERIFY_PARAM structure, which specifies which checks -+ * should be done by the certificate verification operation -+ */ -+ param = X509_VERIFY_PARAM_new(); -+ if (!param) -+ g_abort(); -+ -+ /* The maximum depth level of the chain of trust for the verification of -+ * the IBM Z signing key is 2, i.e. IBM Z signing key -> (DigiCert) -+ * intermediate CA -> (DigiCert) root CA -+ */ -+ X509_VERIFY_PARAM_set_depth(param, 2); -+ -+ /* Set minimum allowed security level to at least 112 bits. */ -+ X509_VERIFY_PARAM_set_auth_level(param, PV_CERTS_SECURITY_LEVEL); -+ -+ /* Set verification purpose to 'Any Purpose' and specify that the -+ * associated trust setting of the default purpose should be used. -+ */ -+ if (X509_VERIFY_PARAM_set_purpose(param, -+ X509_PURPOSE_ANY | X509_TRUST_DEFAULT) != 1) -+ goto error; -+ -+ /* Each certificate from the chain of trust must be checked against a -+ * CRL to see if it has been revoked. In addition, use trusted -+ * certificates first mode, check signature of the last certificate, -+ * strict mode, and verify the policies. -+ */ -+ if (X509_VERIFY_PARAM_set_flags(param, flags) != 1) -+ goto error; -+ -+ if (X509_STORE_set1_param(store, param) != 1) -+ goto error; -+ -+ return 0; -+ -+error: -+ g_set_error(err, PV_CRYPTO_ERROR, PV_CRYPTO_ERROR_INTERNAL, -+ _("X509 store initialization failed")); -+ return -1; -+} -+ -+/* @cert_paths must contain at least one element, otherwise an error is -+ * reported. -+ */ -+GSList *load_certificates(const gchar *const *cert_paths, GError **err) -+{ -+ g_autoslist(x509_with_path) ret = NULL; -+ -+ for (const gchar *const *iterator = cert_paths; -+ iterator != NULL && *iterator != NULL; iterator++) { -+ const gchar *cert_path = *iterator; -+ g_autoptr(X509) cert = NULL; -+ -+ g_assert(cert_path); -+ -+ cert = load_cert_from_file(cert_path, err); -+ if (!cert) -+ return NULL; -+ -+ ret = g_slist_append(ret, x509_with_path_new(cert, cert_path)); -+ } -+ if (!ret) { -+ g_set_error(err, PV_CRYPTO_ERROR, PV_CRYPTO_ERROR_READ_CERTIFICATE, -+ _("no certificates specified")); -+ return NULL; -+ } -+ -+ return g_steal_pointer(&ret); -+} -+ -+static X509 *get_cert(const x509_with_path *cert_with_path, G_GNUC_UNUSED GError **err) -+{ -+ g_autoptr(X509) cert = NULL; -+ -+ g_assert(cert_with_path && cert_with_path->cert); -+ -+ cert = cert_with_path->cert; -+ if (X509_up_ref(cert) != 1) -+ g_abort(); -+ return g_steal_pointer(&cert); -+} -+ -+STACK_OF_X509 *get_x509_stack(const GSList *x509_with_path_list) -+{ -+ g_autoslist(X509) certs = NULL; -+ g_autoptr(GError) err = NULL; -+ -+ certs = g_slist_map_x509_with_path_X509(x509_with_path_list, -+ get_cert, &err); -+ g_assert_null(err); -+ return g_slist_to_stack_of_X509(&certs); -+} -+ -+x509_with_path *x509_with_path_new(X509 *cert, const gchar *path) -+{ -+ g_autoptr(x509_with_path) ret = g_new(x509_with_path, 1); -+ -+ g_assert(cert && path); -+ -+ if (X509_up_ref(cert) != 1) -+ g_abort(); -+ ret->cert = cert; -+ ret->path = g_strdup(path); -+ return g_steal_pointer(&ret); -+} -+ -+void x509_with_path_free(x509_with_path *cert) -+{ -+ if (!cert) -+ return; -+ -+ X509_free(cert->cert); -+ g_free((gchar *)cert->path); -+ g_free(cert); -+} -+ -+x509_pair *x509_pair_new(X509 **cert, STACK_OF_X509_CRL **crls) -+{ -+ g_autoptr(x509_pair) ret = g_new0(x509_pair, 1); -+ -+ g_assert(cert); -+ g_assert(crls); -+ -+ ret->cert = g_steal_pointer(cert); -+ ret->crls = g_steal_pointer(crls); -+ return g_steal_pointer(&ret); -+} -+ -+void x509_pair_free(x509_pair *pair) -+{ -+ if (!pair) -+ return; -+ -+ sk_X509_CRL_pop_free(pair->crls, X509_CRL_free); -+ X509_free(pair->cert); -+ g_free(pair); -+} -+ -+X509_STORE_CTX *create_store_ctx(X509_STORE *trusted, STACK_OF_X509 *chain, -+ GError **err) -+{ -+ g_autoptr(X509_STORE_CTX) ctx = X509_STORE_CTX_new(); -+ -+ if (!ctx || !X509_STORE_CTX_init(ctx, trusted, NULL, chain)) { -+ g_set_error(err, PV_CRYPTO_ERROR, PV_CRYPTO_ERROR_INTERNAL, -+ _("X509 store initialization failed: %s"), -+ X509_verify_cert_error_string(X509_STORE_CTX_get_error(ctx))); -+ return NULL; -+ } -+ -+ return g_steal_pointer(&ctx); -+} -+ -+gint verify_cert(X509 *cert, X509_STORE_CTX *ctx, GError **err) -+{ -+ gint rc; -+ -+ X509_STORE_CTX_set_cert(ctx, cert); -+ rc = X509_verify_cert(ctx); -+ if (rc != 1) { -+ X509 *tmp_cert = NULL; -+ -+ tmp_cert = X509_STORE_CTX_get_current_cert(ctx); -+ if (tmp_cert) { -+ g_autofree char *subj_name = X509_NAME_oneline( -+ X509_get_subject_name(tmp_cert), NULL, 0); -+ g_set_error(err, PV_CRYPTO_ERROR, -+ PV_CRYPTO_ERROR_INTERNAL, -+ _("failed to verify certificate '%s': %s"), -+ subj_name, -+ X509_verify_cert_error_string( -+ X509_STORE_CTX_get_error(ctx))); -+ } else { -+ g_set_error(err, PV_CRYPTO_ERROR, -+ PV_CRYPTO_ERROR_INTERNAL, -+ _("failed to verify certificate: %s"), -+ X509_verify_cert_error_string( -+ X509_STORE_CTX_get_error(ctx))); -+ } -+ -+ return -1; -+ } -+ -+ return 0; -+} -+ -+static int security_level_to_bits(int level) -+{ -+ static int security_bits[] = { 0, 80, 112, 128, 192, 256 }; -+ -+ g_assert(level > 0 && level < (int)G_N_ELEMENTS(security_bits)); -+ -+ return security_bits[level]; -+} -+ -+static ASN1_OCTET_STRING *digicert_assured_id_root_ca; -+ -+const ASN1_OCTET_STRING *get_digicert_assured_id_root_ca_skid(void) -+{ -+ pv_crypto_init(); -+ return digicert_assured_id_root_ca; -+} -+ -+/* Used for the caching of the downloaded CRLs */ -+static GHashTable *cached_crls; -+ -+void pv_crypto_init(void) -+{ -+ if (digicert_assured_id_root_ca) -+ return; -+ -+ cached_crls = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, -+ (GDestroyNotify)X509_CRL_free); -+ digicert_assured_id_root_ca = s2i_ASN1_OCTET_STRING( -+ NULL, NULL, DIGICERT_ASSURED_ID_ROOT_CA_SKID); -+} -+ -+void pv_crypto_cleanup(void) -+{ -+ if (!digicert_assured_id_root_ca) -+ return; -+ g_clear_pointer(&cached_crls, g_hash_table_destroy); -+ g_clear_pointer(&digicert_assured_id_root_ca, ASN1_OCTET_STRING_free); -+} -+ -+gint check_chain_parameters(const STACK_OF_X509 *chain, -+ const ASN1_OCTET_STRING *skid, GError **err) -+{ -+ const ASN1_OCTET_STRING *ca_skid = NULL; -+ gint len = sk_X509_num(chain); -+ X509 *ca = NULL; -+ -+ g_assert(skid); -+ /* at least one root and one leaf certificate must be defined */ -+ g_assert(len >= 2); -+ -+ /* get the root certificate of the chain of trust */ -+ ca = sk_X509_value(chain, len - 1); -+ if (!ca) { -+ g_set_error(err, PV_CRYPTO_ERROR, PV_CRYPTO_ERROR_INTERNAL, -+ _("no root certificate found")); -+ return -1; -+ } -+ -+ ca_skid = X509_get0_subject_key_id(ca); -+ if (!ca_skid) { -+ g_set_error(err, PV_CRYPTO_ERROR, PV_CRYPTO_ERROR_MALFORMED_ROOT_CA, -+ _("malformed root certificate")); -+ return -1; -+ } -+ -+ if (ASN1_STRING_cmp(ca_skid, skid) != 0) { -+ g_set_error(err, PV_CRYPTO_ERROR, PV_CRYPTO_ERROR_WRONG_CA_USED, -+ _("expecting DigiCert root CA to be used")); -+ return -1; -+ } -+ -+ return 0; -+} -+ -+/* It's almost the same as X509_check_issed from OpenSSL does except that we -+ * don't check the key usage of the potential issuer. This means we check: -+ * 1. issuer_name(cert) == subject_name(issuer) -+ * 2. Check whether the akid(cert) (if available) matches the issuer skid -+ * 3. Check that the cert algrithm matches the subject algorithm -+ * 4. Verify the signature of certificate @cert is using the public key of -+ * @issuer. -+ */ -+static gint check_host_key_issued(X509 *cert, X509 *issuer, GError **err) -+{ -+ const X509_NAME *issuer_subject = X509_get_subject_name(issuer); -+ const X509_NAME *cert_issuer = X509_get_issuer_name(cert); -+ AUTHORITY_KEYID *akid = NULL; -+ -+ /* We cannot use X509_NAME_cmp() because it considers the order of the -+ * X509_NAME_Entries. -+ */ -+ if (!own_X509_NAME_equal(issuer_subject, cert_issuer)) { -+ g_autofree char *issuer_subject_str = -+ X509_NAME_oneline(issuer_subject, NULL, 0); -+ g_autofree char *cert_issuer_str = -+ X509_NAME_oneline(cert_issuer, NULL, 0); -+ g_set_error(err, PV_CRYPTO_ERROR, -+ PV_CRYPTO_ERROR_CERT_SUBJECT_ISSUER_MISMATCH, -+ _("Subject issuer mismatch:\n'%s'\n'%s'"), -+ issuer_subject_str, cert_issuer_str); -+ return -1; -+ } -+ -+ akid = X509_get_ext_d2i(cert, NID_authority_key_identifier, NULL, NULL); -+ if (akid && X509_check_akid(issuer, akid) != X509_V_OK) { -+ g_set_error(err, PV_CRYPTO_ERROR, PV_CRYPTO_ERROR_SKID_AKID_MISMATCH, -+ _("AKID mismatch")); -+ return -1; -+ } -+ -+ if (check_signature_algo_match(X509_get0_pubkey(issuer), cert, err) < 0) -+ return -1; -+ -+ if (X509_verify(cert, X509_get0_pubkey(issuer)) != 1) { -+ g_set_error(err, PV_CRYPTO_ERROR, -+ PV_CRYPTO_ERROR_CERT_SIGNATURE_INVALID, -+ _("Signature verification failed")); -+ return -1; -+ } -+ -+ return 0; -+} -+ -+static gboolean is_cert_revoked(X509 *cert, X509_CRL *crl) -+{ -+ X509_REVOKED *revoked = NULL; -+ gint rc; -+ -+ if (!cert || !crl) -+ g_abort(); -+ -+ rc = X509_CRL_get0_by_serial(crl, &revoked, -+ (ASN1_INTEGER *)X509_get0_serialNumber(cert)); -+ if (rc == 0) -+ return FALSE; -+ -+ if (revoked) -+ return TRUE; -+ -+ return FALSE; -+} -+ -+/* Get the first http[s] URL from a DIST_POINT */ -+static const char *get_first_dp_url(DIST_POINT *dp) -+{ -+ GENERAL_NAMES *general_names; -+ -+ g_assert(dp); -+ -+ if (!dp->distpoint || dp->distpoint->type != 0) -+ return NULL; -+ -+ general_names = dp->distpoint->name.fullname; -+ for (gint i = 0; i < sk_GENERAL_NAME_num(general_names); i++) { -+ GENERAL_NAME *name = sk_GENERAL_NAME_value(general_names, i); -+ g_autofree const gchar *uri_str = NULL; -+ ASN1_STRING *uri_asn1; -+ const gchar *uri_data; -+ gint uri_data_len; -+ gint type; -+ -+ uri_asn1 = GENERAL_NAME_get0_value(name, &type); -+ if (type != GEN_URI) -+ continue; -+ uri_data_len = ASN1_STRING_length(uri_asn1); -+ if (uri_data_len < 0) -+ continue; -+ uri_data = (const gchar *)ASN1_STRING_get0_data(uri_asn1); -+ /* Make sure that uri_str is null-terminated as in general it -+ * cannot be assumed that @uri_data is null-terminated. -+ */ -+ uri_str = g_strndup(uri_data, -+ (gsize)uri_data_len); -+ if (g_str_has_prefix(uri_str, "http://")) -+ return uri_data; -+ if (g_str_has_prefix(uri_str, "https://")) -+ return uri_data; -+ } -+ return NULL; -+} -+ -+static gboolean insert_crl(X509_NAME *name, X509_CRL *crl) -+{ -+ g_autofree gchar *key = NULL; -+ -+ g_assert(name); -+ -+ key = X509_NAME_oneline(name, NULL, 0); -+ if (!key) -+ g_abort(); -+ if (X509_CRL_up_ref(crl) != 1) -+ g_abort(); -+ return g_hash_table_insert(cached_crls, g_steal_pointer(&key), crl); -+} -+ -+/* Caller is responsible for free'ing */ -+static X509_CRL *lookup_crl(X509_NAME *name) -+{ -+ g_autoptr(X509_CRL) crl = NULL; -+ g_autofree gchar *key = NULL; -+ -+ g_assert(name); -+ -+ key = X509_NAME_oneline(name, NULL, 0); -+ if (!key) -+ g_abort(); -+ crl = g_hash_table_lookup(cached_crls, key); -+ if (crl) { -+ if (X509_CRL_up_ref(crl) != 1) -+ g_abort(); -+ return g_steal_pointer(&crl); -+ } -+ return NULL; -+} -+ -+/* Returns empty stack if no CRL downloaded. */ -+static STACK_OF_X509_CRL *crls_download_cb(X509_STORE_CTX *ctx, X509_NAME *nm) -+{ -+ g_autoptr(STACK_OF_X509_CRL) crls = NULL; -+ g_autoptr(X509_CRL) crl = NULL; -+ /* must not be free'd */ -+ X509 *cert = NULL; -+ -+ crls = sk_X509_CRL_new_null(); -+ if (!crls) -+ g_abort(); -+ cert = X509_STORE_CTX_get_current_cert(ctx); -+ if (!cert) -+ g_steal_pointer(&crls); -+ g_assert(X509_NAME_cmp(X509_get_issuer_name(cert), nm) == 0); -+ crl = lookup_crl(nm); -+ if (!crl) { -+ /* ignore error */ -+ crl = load_crl_by_cert(cert, NULL); -+ if (!crl) -+ return g_steal_pointer(&crls); -+ g_assert_true(insert_crl(nm, crl)); -+ } -+ if (sk_X509_CRL_push(crls, g_steal_pointer(&crl)) == 0) -+ g_abort(); -+ return g_steal_pointer(&crls); -+} -+ -+void STACK_OF_DIST_POINT_free(STACK_OF_DIST_POINT *stack) -+{ -+ if (!stack) -+ return; -+ -+ sk_DIST_POINT_pop_free(stack, DIST_POINT_free); -+} -+ -+void STACK_OF_X509_free(STACK_OF_X509 *stack) -+{ -+ if (!stack) -+ return; -+ -+ sk_X509_pop_free(stack, X509_free); -+} -+ -+void STACK_OF_X509_CRL_free(STACK_OF_X509_CRL *stack) -+{ -+ if (!stack) -+ return; -+ -+ sk_X509_CRL_pop_free(stack, X509_CRL_free); -+} -+ -+/* Downloaded CRLs have a higher precedence than the CRLs specified on the -+ * command line. -+ */ -+static STACK_OF_X509_CRL *crls_cb(X509_STORE_CTX *ctx, X509_NAME *nm) -+{ -+ g_autoptr(STACK_OF_X509_CRL) crls = crls_download_cb(ctx, nm); -+ -+ if (sk_X509_CRL_num(crls) > 0) -+ return g_steal_pointer(&crls); -+ return X509_STORE_CTX_get1_crls(ctx, nm); -+} -+ -+/* Set up CRL lookup with download support */ -+void store_setup_crl_download(X509_STORE *st) -+{ -+ X509_STORE_set_lookup_crls(st, crls_cb); -+} -+ -+/* Download a CRL using the URI specified in the distribution @crldp */ -+static X509_CRL *load_crl_by_dist_point(DIST_POINT *crldp, GError **err) -+{ -+ const gchar *uri = get_first_dp_url(crldp); -+ g_autoptr(X509_CRL) crl = NULL; -+ -+ if (!uri) { -+ g_set_error(err, PV_CRYPTO_ERROR, PV_CRYPTO_ERROR_INTERNAL, -+ _("no valid URL specified in distribution point")); -+ return NULL; -+ } -+ -+ if (load_crl_from_web(uri, &crl, err) < 0) -+ return NULL; -+ -+ return g_steal_pointer(&crl); -+} -+ -+/* This function returns the first X509_CRL found from the CRL distribution -+ * points specified in @cert. This function could be optimized by filtering -+ * duplicate certificates and/or filtering duplicated URIs. -+ */ -+X509_CRL *load_crl_by_cert(X509 *cert, GError **err) -+{ -+ g_autoptr(STACK_OF_DIST_POINT) crldps = NULL; -+ g_autoptr(X509_CRL) ret = NULL; -+ -+ g_assert(cert); -+ -+ crldps = X509_get_ext_d2i(cert, NID_crl_distribution_points, NULL, NULL); -+ if (!crldps || sk_DIST_POINT_num(crldps) == 0) { -+ g_set_error(err, PV_CRYPTO_ERROR, PV_CRYPTO_ERROR_NO_CRLDP, -+ _("no distribution point found")); -+ return NULL; -+ } -+ -+ for (int i = 0; i < sk_DIST_POINT_num(crldps); i++) { -+ DIST_POINT *crldp = sk_DIST_POINT_value(crldps, i); -+ -+ g_assert(crldp); -+ -+ /* ignore error */ -+ ret = load_crl_by_dist_point(crldp, NULL); -+ if (ret) -+ return g_steal_pointer(&ret); -+ } -+ -+ g_set_error(err, PV_CRYPTO_ERROR, PV_CRYPTO_ERROR_FAILED_DOWNLOAD_CRL, -+ _("failed to download CRL")); -+ return NULL; -+} -+ -+STACK_OF_X509_CRL *try_load_crls_by_certs(GSList *certs_with_path) -+{ -+ g_autoptr(STACK_OF_X509_CRL) ret = sk_X509_CRL_new_null(); -+ if (!ret) -+ g_abort(); -+ -+ for (GSList *iterator = certs_with_path; iterator; -+ iterator = iterator->next) { -+ x509_with_path *cert_with_path = iterator->data; -+ X509 *cert = cert_with_path->cert; -+ g_autoptr(X509_CRL) crl = NULL; -+ -+ g_assert(cert); -+ -+ /* ignore error */ -+ crl = load_crl_by_cert(cert, NULL); -+ if (!crl) -+ continue; -+ -+ if (sk_X509_CRL_push(ret, g_steal_pointer(&crl)) == 0) -+ g_abort(); -+ } -+ -+ return g_steal_pointer(&ret); -+} -+ -+/* Assumptions are that the issuer_crt and issuer_crl is a trusted IBM Z -+ * signing certificate/revocation list. This function verifies a host-key -+ * document. To do so multiple steps are required: -+ * -+ * 1. issuer(host_key) == subject(issuer_crt) -+ * 2. Signature verification -+ * 3. @host_key must not be expired -+ * 4. @host_key must not be revoked -+ */ -+gint verify_host_key(X509 *host_key, GSList *issuer_pairs, -+ gint verify_flags, int level, GError **err) -+{ -+ g_assert(host_key); -+ -+ const gint exp_security_bits = security_level_to_bits(level); -+ EVP_PKEY *pkey = X509_get0_pubkey(host_key); -+ gboolean successfully_checked = FALSE; -+ gint pkey_security_bits; -+ -+ if (!pkey) { -+ g_set_error(err, PV_CRYPTO_ERROR, PV_CRYPTO_ERROR_INTERNAL, -+ _("failed to retrieve public key")); -+ return -1; -+ } -+ -+ /* check key level, if necessary */ -+ pkey_security_bits = EVP_PKEY_security_bits(pkey); -+ if (exp_security_bits > 0 && pkey_security_bits < exp_security_bits) { -+ g_set_error(err, PV_CRYPTO_ERROR, PV_CRYPTO_ERROR_VERIFICATION, -+ _("not enough bits of security (%d, %d expected)"), -+ pkey_security_bits, exp_security_bits); -+ return -1; -+ } -+ -+ if (!(verify_flags & X509_V_FLAG_NO_CHECK_TIME)) { -+ const ASN1_TIME *last = X509_get_notBefore(host_key); -+ const ASN1_TIME *next = X509_get_notAfter(host_key); -+ -+ if (!last || !next || check_validity_period(last, next)) { -+ g_set_error(err, PV_CRYPTO_ERROR, -+ PV_CRYPTO_ERROR_INVALID_VALIDITY_PERIOD, -+ _("validity period is not valid")); -+ return -1; -+ } -+ } else { -+ verify_flags &= ~X509_V_FLAG_NO_CHECK_TIME; -+ } -+ -+ /* Verify that the host_key was issued by a certificate and that it -+ * wasn't revoked. -+ */ -+ for (GSList *iterator = issuer_pairs; iterator; -+ iterator = iterator->next) { -+ const x509_pair *pair = iterator->data; -+ STACK_OF_X509_CRL *issuer_crls = NULL; -+ X509 *issuer_cert = NULL; -+ -+ g_assert(pair); -+ -+ issuer_cert = pair->cert; -+ issuer_crls = pair->crls; -+ -+ g_assert(issuer_cert); -+ -+ /* Verify that the issuer(host_key) == subject(issuer_cert) and -+ * that the signature is valid -+ */ -+ if (check_host_key_issued(host_key, issuer_cert, NULL) < 0) -+ continue; -+ -+ /* Check against CRL */ -+ if (verify_flags & X509_V_FLAG_CRL_CHECK) { -+ gboolean crl_checked = FALSE; -+ -+ verify_flags &= ~X509_V_FLAG_CRL_CHECK; -+ for (gint i = 0; i < sk_X509_CRL_num(issuer_crls); i++) { -+ X509_CRL *issuer_crl = -+ sk_X509_CRL_value(issuer_crls, i); -+ -+ g_assert(issuer_crl); -+ -+ if (is_cert_revoked(host_key, issuer_crl)) { -+ g_set_error(err, PV_CRYPTO_ERROR, -+ PV_CRYPTO_ERROR_CERT_REVOKED, -+ _("certificate revoked")); -+ return -1; -+ } -+ -+ crl_checked = TRUE; -+ } -+ -+ if (!crl_checked) { -+ g_set_error(err, PV_CRYPTO_ERROR, -+ PV_CRYPTO_ERROR_INTERNAL, -+ _("no valid CRL found")); -+ return -1; -+ } -+ successfully_checked = TRUE; -+ break; -+ } -+ } -+ -+ if (!successfully_checked) { -+ g_set_error(err, PV_CRYPTO_ERROR, -+ PV_CRYPTO_ERROR_NO_ISSUER_IBM_Z_FOUND, -+ _("no IBM Z signing key that issued this host-key document found")); -+ return -1; -+ } -+ -+ /* were some unsupported flags specified? */ -+ g_assert(verify_flags == 0); -+ return 0; -+} -+ -+EVP_PKEY *read_ec_pubkey_cert(X509 *cert, gint nid, -+ GError **err) -+{ -+ g_autoptr(EVP_PKEY) ret = NULL; - - ret = X509_get_pubkey(cert); - if (!ret) { - g_set_error(err, PV_CRYPTO_ERROR, PV_CRYPTO_ERROR_INVALID_PARM, -- _("Failed to get public key from host-key document: '%s'"), -- path); -+ _("Failed to get public key from host-key document")); - return NULL; - } - - if (!certificate_uses_correct_curve(ret, nid, err)) { - g_prefix_error(err, -- _("Failed to load host-key document: '%s': "), -- path); -+ _("Host-key document doesn\'t use correct EC curve")); - return NULL; - } - -diff --git a/genprotimg/src/utils/crypto.h b/genprotimg/src/utils/crypto.h -index 34418ed..286cf45 100644 ---- a/genprotimg/src/utils/crypto.h -+++ b/genprotimg/src/utils/crypto.h -@@ -11,14 +11,18 @@ - #define PV_UTILS_CRYPTO_H - - #include -+#include - #include - #include - #include - #include - #include -+#include - #include -+#include - #include - #include -+#include - #include - - #include "common.h" -@@ -33,6 +37,9 @@ - #define AES_256_XTS_TWEAK_SIZE 16 - #define AES_256_XTS_KEY_SIZE 64 - -+#define CRL_DOWNLOAD_TIMEOUT_MS 3000 -+#define CRL_DOWNLOAD_MAX_SIZE (1024 * 1024) /* in bytes */ -+ - enum PvCryptoMode { - PV_ENCRYPT, - PV_DECRYPT, -@@ -40,7 +47,34 @@ enum PvCryptoMode { - - typedef GSList HostKeyList; - -+/* play nice with g_autoptr */ -+typedef STACK_OF(DIST_POINT) STACK_OF_DIST_POINT; -+typedef STACK_OF(X509) STACK_OF_X509; -+typedef STACK_OF(X509_CRL) STACK_OF_X509_CRL; -+ -+void STACK_OF_DIST_POINT_free(STACK_OF_DIST_POINT *stack); -+void STACK_OF_X509_free(STACK_OF_X509 *stack); -+void STACK_OF_X509_CRL_free(STACK_OF_X509_CRL *stack); -+ -+typedef struct { -+ X509 *cert; -+ const gchar *path; -+} x509_with_path; -+ -+x509_with_path *x509_with_path_new(X509 *cert, const gchar *path); -+void x509_with_path_free(x509_with_path *cert); -+ -+typedef struct { -+ X509 *cert; -+ STACK_OF_X509_CRL *crls; -+} x509_pair; -+ -+x509_pair *x509_pair_new(X509 **cert, STACK_OF_X509_CRL **crls); -+void x509_pair_free(x509_pair *pair); -+ - /* Register auto cleanup functions */ -+WRAPPED_G_DEFINE_AUTOPTR_CLEANUP_FUNC(ASN1_INTEGER, ASN1_INTEGER_free) -+WRAPPED_G_DEFINE_AUTOPTR_CLEANUP_FUNC(ASN1_OCTET_STRING, ASN1_OCTET_STRING_free) - WRAPPED_G_DEFINE_AUTOPTR_CLEANUP_FUNC(BIGNUM, BN_free) - WRAPPED_G_DEFINE_AUTOPTR_CLEANUP_FUNC(BIO, BIO_free_all) - WRAPPED_G_DEFINE_AUTOPTR_CLEANUP_FUNC(BN_CTX, BN_CTX_free) -@@ -51,10 +85,18 @@ WRAPPED_G_DEFINE_AUTOPTR_CLEANUP_FUNC(EVP_CIPHER_CTX, EVP_CIPHER_CTX_free) - WRAPPED_G_DEFINE_AUTOPTR_CLEANUP_FUNC(EVP_MD_CTX, EVP_MD_CTX_free) - WRAPPED_G_DEFINE_AUTOPTR_CLEANUP_FUNC(EVP_PKEY, EVP_PKEY_free) - WRAPPED_G_DEFINE_AUTOPTR_CLEANUP_FUNC(EVP_PKEY_CTX, EVP_PKEY_CTX_free) -+WRAPPED_G_DEFINE_AUTOPTR_CLEANUP_FUNC(STACK_OF_DIST_POINT, STACK_OF_DIST_POINT_free); -+WRAPPED_G_DEFINE_AUTOPTR_CLEANUP_FUNC(STACK_OF_X509, STACK_OF_X509_free); -+WRAPPED_G_DEFINE_AUTOPTR_CLEANUP_FUNC(STACK_OF_X509_CRL, STACK_OF_X509_CRL_free); - WRAPPED_G_DEFINE_AUTOPTR_CLEANUP_FUNC(X509, X509_free) -+WRAPPED_G_DEFINE_AUTOPTR_CLEANUP_FUNC(X509_CRL, X509_CRL_free) - WRAPPED_G_DEFINE_AUTOPTR_CLEANUP_FUNC(X509_LOOKUP, X509_LOOKUP_free) -+WRAPPED_G_DEFINE_AUTOPTR_CLEANUP_FUNC(X509_NAME, X509_NAME_free) -+WRAPPED_G_DEFINE_AUTOPTR_CLEANUP_FUNC(x509_pair, x509_pair_free) - WRAPPED_G_DEFINE_AUTOPTR_CLEANUP_FUNC(X509_STORE, X509_STORE_free) - WRAPPED_G_DEFINE_AUTOPTR_CLEANUP_FUNC(X509_STORE_CTX, X509_STORE_CTX_free) -+WRAPPED_G_DEFINE_AUTOPTR_CLEANUP_FUNC(X509_VERIFY_PARAM, X509_VERIFY_PARAM_free) -+WRAPPED_G_DEFINE_AUTOPTR_CLEANUP_FUNC(x509_with_path, x509_with_path_free) - - union cmp_index { - struct { -@@ -79,8 +121,37 @@ struct cipher_parms { - const Buffer *iv_or_tweak; - }; - --EVP_PKEY *read_ec_pubkey_cert(X509_STORE *store, gint nid, const gchar *path, -- GError **err); -+int check_crl_valid_for_cert(X509_CRL *crl, X509 *cert, -+ gint verify_flags, GError **err); -+void pv_crypto_init(void); -+void pv_crypto_cleanup(void); -+const ASN1_OCTET_STRING *get_digicert_assured_id_root_ca_skid(void); -+gint verify_host_key(X509 *host_key, GSList *issuer_pairs, -+ gint verify_flags, int level, GError **err); -+X509 *load_cert_from_file(const char *path, GError **err); -+X509_CRL *load_crl_from_file(const gchar *path, GError **err); -+GSList *load_certificates(const gchar *const *cert_paths, GError **err); -+STACK_OF_X509 *get_x509_stack(const GSList *x509_with_path_list); -+X509_STORE *store_setup(const gchar *root_ca_path, -+ const gchar * const *crl_paths, -+ GError **err); -+int store_set_verify_param(X509_STORE *store, GError **err); -+X509_CRL *load_crl_by_cert(X509 *cert, GError **err); -+STACK_OF_X509_CRL *try_load_crls_by_certs(GSList *certs_with_path); -+gint check_chain_parameters(const STACK_OF_X509 *chain, -+ const ASN1_OCTET_STRING *skid, GError **err); -+X509_NAME *c2b_name(const X509_NAME *name); -+ -+STACK_OF_X509 *delete_ibm_signing_certs(STACK_OF_X509 *certs); -+STACK_OF_X509_CRL *store_ctx_find_valid_crls(X509_STORE_CTX *ctx, X509 *cert, -+ GError **err); -+X509_STORE_CTX *create_store_ctx(X509_STORE *trusted, STACK_OF_X509 *chain, -+ GError **err); -+gint verify_cert(X509 *cert, X509_STORE_CTX *ctx, GError **err); -+X509_CRL *get_first_valid_crl(X509_STORE_CTX *ctx, X509 *cert, GError **err); -+void store_setup_crl_download(X509_STORE *st); -+EVP_PKEY *read_ec_pubkey_cert(X509 *cert, gint nid, GError **err); -+ - Buffer *compute_exchange_key(EVP_PKEY *cust, EVP_PKEY *host, GError **err); - Buffer *generate_aes_key(guint size, GError **err); - Buffer *generate_aes_iv(guint size, GError **err); -diff --git a/genprotimg/src/utils/curl.c b/genprotimg/src/utils/curl.c -new file mode 100644 -index 0000000..a8ef3f6 ---- /dev/null -+++ b/genprotimg/src/utils/curl.c -@@ -0,0 +1,121 @@ -+/* -+ * Libcurl utils -+ * -+ * Copyright IBM Corp. 2020 -+ * -+ * s390-tools is free software; you can redistribute it and/or modify -+ * it under the terms of the MIT license. See LICENSE for details. -+ */ -+ -+#include -+#include -+#include -+#include -+ -+#include "lib/zt_common.h" -+#include "pv/pv_error.h" -+ -+#include "curl.h" -+ -+struct UserData { -+ GByteArray *buffer; -+ guint max_size; -+}; -+ -+static size_t write_callback(char *ptr, size_t size, size_t nmemb, void *userdata) -+{ -+ g_assert(userdata); -+ struct UserData *data = (struct UserData *)userdata; -+ GByteArray *buffer = data->buffer; -+ guint64 actual_size; -+ size_t err; -+ -+ g_assert(buffer); -+ -+ if (!g_uint64_checked_mul(&actual_size, size, nmemb)) -+ g_abort(); -+ -+ /* Signal an error condition by returning a amount that differs -+ * from the amount passed to the callback. This results in a -+ * CURLE_WRITE_ERROR. -+ */ -+ err = actual_size + 1; -+ -+ if (actual_size > G_MAXUINT) -+ return err; -+ -+ data->buffer = g_byte_array_append(buffer, (guchar *)ptr, (guint)actual_size); -+ if (data->buffer->len > data->max_size) -+ return err; -+ -+ return actual_size; -+} -+ -+gint curl_init(void) -+{ -+ if (curl_global_init(CURL_GLOBAL_ALL) != 0) -+ return -1; -+ return 0; -+} -+ -+void curl_cleanup(void) -+{ -+ curl_global_cleanup(); -+} -+ -+GByteArray *curl_download(const gchar *url, long timeout_ms, guint max_size, -+ GError **err) -+{ -+ g_autoptr(GByteArray) ret = NULL; -+ g_autoptr(CURL) handle = NULL; -+ g_autofree gchar *agent = NULL; -+ struct UserData userdata; -+ CURLcode rc; -+ -+ /* set up curl session */ -+ handle = curl_easy_init(); -+ if (!handle) -+ g_abort(); -+ -+ /* follow redirection */ -+ rc = curl_easy_setopt(handle, CURLOPT_FOLLOWLOCATION, 1l); -+ if (rc != CURLE_OK) -+ goto curl_err; -+ rc = curl_easy_setopt(handle, CURLOPT_TIMEOUT_MS, timeout_ms); -+ if (rc != CURLE_OK) -+ goto curl_err; -+ rc = curl_easy_setopt(handle, CURLOPT_NOSIGNAL, 1l); -+ if (rc != CURLE_OK) -+ goto curl_err; -+ agent = g_strdup_printf("%s/%s", tool_name, RELEASE_STRING); -+ rc = curl_easy_setopt(handle, CURLOPT_USERAGENT, agent); -+ if (rc != CURLE_OK) -+ goto curl_err; -+ rc = curl_easy_setopt(handle, CURLOPT_WRITEFUNCTION, write_callback); -+ if (rc != CURLE_OK) -+ goto curl_err; -+ ret = g_byte_array_new(); -+ userdata.buffer = ret; -+ userdata.max_size = max_size; -+ rc = curl_easy_setopt(handle, CURLOPT_WRITEDATA, (void *)&userdata); -+ if (rc != CURLE_OK) -+ goto curl_err; -+ rc = curl_easy_setopt(handle, CURLOPT_URL, url); -+ if (rc != CURLE_OK) -+ goto curl_err; -+ -+ rc = curl_easy_perform(handle); -+ if (rc != CURLE_OK) { -+ g_set_error(err, PV_ERROR, PV_ERROR_DOWNLOAD_FAILED, -+ _("download failed: %s"), curl_easy_strerror(rc)); -+ return NULL; -+ } -+ -+ return g_steal_pointer(&ret); -+curl_err: -+ g_set_error(err, PV_ERROR, -+ PV_ERROR_CURL_INIT_FAILED, -+ _("cURL initialization failed: %s"), -+ curl_easy_strerror(rc)); -+ return NULL; -+} -diff --git a/genprotimg/src/utils/curl.h b/genprotimg/src/utils/curl.h -new file mode 100644 -index 0000000..4ec1c11 ---- /dev/null -+++ b/genprotimg/src/utils/curl.h -@@ -0,0 +1,25 @@ -+/* -+ * Libcurl utils -+ * -+ * Copyright IBM Corp. 2020 -+ * -+ * s390-tools is free software; you can redistribute it and/or modify -+ * it under the terms of the MIT license. See LICENSE for details. -+ */ -+ -+#ifndef PV_UTILS_LIBCURL_H -+#define PV_UTILS_LIBCURL_H -+ -+#include -+#include -+ -+#include "common.h" -+ -+WRAPPED_G_DEFINE_AUTOPTR_CLEANUP_FUNC(CURL, curl_easy_cleanup) -+ -+GByteArray *curl_download(const gchar *url, long timeout_ms, guint max_size, -+ GError **err); -+gint curl_init(void); -+void curl_cleanup(void); -+ -+#endif /* PV_UTILS_LIBCURL_H */ --- -2.26.2 - - -From 4964155a921e38d971bc857dbe95ce60aaffef45 Mon Sep 17 00:00:00 2001 -From: =?UTF-8?q?Dan=20Hor=C3=A1k?= -Date: Mon, 7 Dec 2020 14:34:51 +0100 -Subject: [PATCH 6/9] s390-tools: add hsci tool (#1847434) - -Summary: s390-tools: add hsci tool -Description: hsci is used to control and show HSCI (HiperSockets Converged - Interfaces) settings. A HiperSockets interface and an external - network interface are converged to an HSCI interface. -Upstream-ID: 0566a492aec764c5405a94185fd6117fcd520249 ---- - Makefile | 2 +- - README.md | 3 + - hsci/Makefile | 16 ++ - hsci/hsci | 441 ++++++++++++++++++++++++++++++++++++++++++++++++++ - hsci/hsci.8 | 100 ++++++++++++ - 5 files changed, 561 insertions(+), 1 deletion(-) - create mode 100644 hsci/Makefile - create mode 100644 hsci/hsci - create mode 100644 hsci/hsci.8 - -diff --git a/Makefile b/Makefile -index cc277b8..cfbbd95 100644 ---- a/Makefile -+++ b/Makefile -@@ -9,7 +9,7 @@ TOOL_DIRS = zipl zdump fdasd dasdfmt dasdview tunedasd \ - vmconvert vmcp man mon_tools dasdinfo vmur cpuplugd ipl_tools \ - ziomon iucvterm hyptop cmsfs-fuse qethqoat zfcpdump zdsfs cpumf \ - systemd hmcdrvfs cpacfstats zdev dump2tar zkey netboot etc zpcictl \ -- genprotimg lsstp -+ genprotimg lsstp hsci - - SUB_DIRS = $(LIB_DIRS) $(TOOL_DIRS) - -diff --git a/README.md b/README.md -index 6fb6831..5a2153c 100644 ---- a/README.md -+++ b/README.md -@@ -249,6 +249,9 @@ Package contents - Management Foundation - Web Edition, and is used to manage keys in an - enterprise. - -+ * hsci: -+ Manage HiperSockets Converged Interfaces (HSCI). -+ - For more information refer to the following publications: - - * "Device Drivers, Features, and Commands" chapter "Useful Linux commands" -diff --git a/hsci/Makefile b/hsci/Makefile -new file mode 100644 -index 0000000..6f7474c ---- /dev/null -+++ b/hsci/Makefile -@@ -0,0 +1,16 @@ -+include ../common.mak -+ -+all: -+ -+install: hsci -+ $(SED) -e 's/%S390_TOOLS_VERSION%/$(S390_TOOLS_RELEASE)/' \ -+ < hsci >$(DESTDIR)$(BINDIR)/hsci; \ -+ chown $(OWNER).$(GROUP) $(DESTDIR)$(BINDIR)/hsci; \ -+ chmod 755 $(DESTDIR)$(BINDIR)/hsci; \ -+ $(INSTALL) -d -m 755 $(DESTDIR)$(BINDIR) $(DESTDIR)$(MANDIR)/man8 -+ $(INSTALL) -g $(GROUP) -o $(OWNER) -m 644 hsci.8 \ -+ $(DESTDIR)$(MANDIR)/man8 -+ -+clean: -+ -+.PHONY: all install clean -diff --git a/hsci/hsci b/hsci/hsci -new file mode 100644 -index 0000000..9a56aa0 ---- /dev/null -+++ b/hsci/hsci -@@ -0,0 +1,441 @@ -+#!/bin/bash -+# -+# hsci - Tool to manage HiperSockets Converged Interfaces (HSCI) -+# -+# Copyright IBM Corp. 2020 -+# -+# s390-tools is free software; you can redistribute it and/or modify -+# it under the terms of the MIT license. See LICENSE for details. -+# -+ -+hsdev="" -+ndev="" -+hsci="" -+hsdev_mac="" -+hsif_pnetid="" -+netif_pnetid="" -+hsci_pnetid="" -+ -+function usage { -+cat <<-EOD -+Usage: hsci COMMAND [OPTION] -+ -+This tool is designed to control and show HSCI (HiperSockets Converged -+Interfaces) settings. A HiperSockets interface and an external network -+ -+COMMANDS -+ add HIPERSOCKETS_DEV NET_DEV Adds an HSCI interface -+ del HSCI_NAME Deletes an HSCI interface -+ show Lists the configured HSCI interfaces -+ -+OPTIONS: -+ -v, --version Prints the version number of the hsci tool and exits -+ -h, --help Displays the help information for the command -+EOD -+} -+ -+function prereqs_check { -+ if ! [ -x "$(command -v ip)" ]; then -+ echo "Error: No iproute2 installed on this system" >&2 -+ return 1 -+ fi -+} -+ -+function check_pnetids { -+ # get PNETID of the HS -+ local hsif_pnetids="" -+ local netif_pnetids="" -+ -+ if [ -e /sys/class/net/$hsdev/device/util_string ]; then -+ hsif_pnetids="$(cat /sys/class/net/$hsdev/device/util_string | tr -d '\000' | iconv -f IBM-1047 -t ASCII)" -+ else -+ if [ -e /sys/class/net/$hsdev/device/chpid ]; then -+ chpid="$(cat /sys/class/net/$hsdev/device/chpid | tr [:upper:] [:lower:])" -+ hsif_pnetids="$(cat /sys/devices/css0/chp0.$chpid/util_string | tr -d '\000' | iconv -f IBM-1047 -t ASCII)" -+ fi -+ fi -+ if [ "$hsif_pnetids" != "" ]; then -+ port_hsif="$(cat /sys/class/net/$hsdev/dev_port)" -+ (( idx=16*$port_hsif+1 )) -+ (( end=$idx+15 )) -+ hsif_pnetid="$(echo "$hsif_pnetids" | cut -c $idx-$end | tr -d ' ')" -+ fi -+ -+ # get PNETID of the NET_DEV -+ if [ -e /sys/class/net/$ndev/device/util_string ]; then -+ netif_pnetids="$(cat /sys/class/net/$ndev/device/util_string | tr -d '\000' | iconv -f IBM-1047 -t ASCII)" -+ else -+ if [ -e /sys/class/net/$ndev/device/chpid ]; then -+ chpid="$(cat /sys/class/net/$ndev/device/chpid | tr [:upper:] [:lower:])" -+ netif_pnetids="$(cat /sys/devices/css0/chp0.$chpid/util_string | tr -d '\000' | iconv -f IBM-1047 -t ASCII)" -+ fi -+ fi -+ if [ "$netif_pnetids" != "" ]; then -+ port_netif="$(cat /sys/class/net/$ndev/dev_port)" -+ (( idx=16*$port_netif+1 )) -+ (( end=$idx+15 )) -+ netif_pnetid="$(echo "$netif_pnetids" | cut -c $idx-$end | tr -d ' ')" -+ fi -+ -+ #Check PNETIDs -+ if [ "$hsif_pnetid" != "" ] && [ "$netif_pnetid" != "" ] && [ "$netif_pnetid" != "$hsif_pnetid" ]; then -+ echo "Error: $hsdev and $ndev have different PNETIDs! They are $hsif_pnetid and $netif_pnetid respectively" >&2 -+ return 1 -+ fi -+ -+ if [ "$hsif_pnetid" != "" ] && [ "$netif_pnetid" != "" ] && [ "$netif_pnetid" == "$hsif_pnetid" ]; then -+ hsci_pnetid=$hsif_pnetid -+ fi -+} -+ -+function verify_precon { -+ echo "Verifying net dev $ndev and HiperSockets dev $hsdev" -+ -+ if [ ! -e /sys/class/net/$hsdev ]; then -+ echo "Error: $hsdev does not exist" >&2 -+ return 1 -+ fi -+ if [ "$(cat /sys/class/net/$hsdev/device/card_type)" != "HiperSockets" ]; then -+ echo "Error: $hsdev is not a HiperSockets device" >&2 -+ return 1 -+ fi -+ if [ "$(cat /sys/class/net/$hsdev/device/layer2)" != "1" ]; then -+ echo "Error: $hsdev is not in layer 2 mode" >&2 -+ return 1 -+ fi -+ if [ ! -e /sys/class/net/$hsdev/device/vnicc/bridge_invisible ]; then -+ echo "Error: Missing vnic-characteristics support" >&2 -+ return 1 -+ fi -+ if [ "$(cat /sys/class/net/$hsdev/device/vnicc/bridge_invisible)" == "n/a" ]; then -+ echo "Error: $hsdev does not support vnicc" >&2 -+ return 1 -+ fi -+ if [ $(ip link show $hsdev | grep UP | wc -l) -eq 0 ]; then -+ echo "Error: $hsdev is not in state UP" >&2 -+ return 1 -+ fi -+ if [ $(bridge -d link show dev $hsdev self | grep learning_sync | wc -l) -eq 0 ]; then -+ echo "Error: $hsdev does not support attribute learning_sync" >&2 -+ return 1 -+ fi -+ if [ $(ip link show $hsdev | grep master | wc -l) -ne 0 ]; then -+ echo "Error: $hsdev is already a bridge port" >&2 -+ return 1 -+ fi -+ -+ #Pre-verify net_dev -+ if [ ! -e /sys/class/net/$ndev ]; then -+ echo "Error: $ndev does not exist" >&2 -+ return 1 -+ fi -+ if [ "$(cat /sys/class/net/$ndev/device/card_type)" == "HiperSockets" ]; then -+ echo "Error: $ndev is also a HiperSockets device" >&2 -+ return 1 -+ fi -+ if [ $(ip link show $ndev | grep UP | wc -l) -eq 0 ]; then -+ echo "Error: $ndev is not in state UP" >&2 -+ return 1 -+ fi -+ if [ $(ip link show $ndev | grep master | wc -l) -ne 0 ]; then -+ echo "Error: $ndev is already a bridge port" >&2 -+ return 1 -+ fi -+ -+ #Check PNETIDs -+ check_pnetids -+ if [ $? -ne 0 ]; then -+ return $? -+ fi -+ -+ return 0 -+} -+ -+function clean_up { -+ bridge link set dev $hsdev learning_sync off self >/dev/null 2>&1 -+ echo 0 > /sys/class/net/$hsdev/device/vnicc/bridge_invisible >/dev/null 2>&1 -+ bridge fdb del $hsdev_mac dev $ndev >/dev/null 2>&1 -+ ip link del $hsci >/dev/null 2>&1 -+} -+ -+############################################################################## -+## add a new HSCI interface -+############################################################################## -+function add_hsci { -+ -+ if [ $# != 2 ]; then -+ echo "hsci: Invalid parameters" >&2 -+ echo "Use 'hsci --help' for more information" >&2 -+ return 1 -+ fi -+ hsdev=$1 -+ ndev=$2 -+ -+ #### Verify preconditions -+ verify_precon -+ if [ $? -ne 0 ]; then -+ return $? -+ fi -+ -+ hsci_postfix="$(readlink /sys/class/net/$hsdev/device/cdev0 | tail -c5)" -+ hsci=hsci$hsci_postfix -+ -+ echo "Adding $hsci with a HiperSockets dev $hsdev and an external dev $ndev" -+ -+ #### Create bridge -+ ip link add name $hsci type bridge stp_state 0 >/dev/null 2>&1 -+ if [ $? -ne 0 ]; then -+ echo "Error: Could not create a bridge" >&2 -+ return 1 -+ fi -+ -+ #### Prepare hsdev -+ # Set VNICC of hsdev to invisible -+ #(mandatory for co-existence with HS-OSA bridges!) -+ echo 1 > /sys/class/net/$hsdev/device/vnicc/bridge_invisible -+ -+ #### Create bridge ports -+ ip link set dev $ndev master $hsci >/dev/null 2>&1 -+ if [ $? -ne 0 ]; then -+ echo "Error: Could not set master for $ndev" >&2 -+ clean_up -+ return 1 -+ fi -+ ip link set dev $hsdev master $hsci >/dev/null 2>&1 -+ if [ $? -ne 0 ]; then -+ echo "Error: Could not set master for $hsdev" >&2 -+ clean_up -+ return 1 -+ fi -+ -+ # no forwarding between ndev and hsdev -> isolated on -+ # ndev is default for outgoing unknown targets -> flood on -+ # no need to learn external LAN targets into fdb -> learning off -+ bridge link set dev $ndev isolated on learning off flood on mcast_flood on >/dev/null 2>&1 -+ if [ $? -ne 0 ]; then -+ echo "Error: Failed to set bridge attributes on $ndev" >&2 -+ clean_up -+ return 1 -+ fi -+ -+ # no forwarding between ndev and hsdev -> isolated on -+ # fdb will be populated by dev-to-bridge-notification, no need to learn -+ # -> learning off -+ # only send to hsdev, if listed in fdb -> flood off -+ # don't send MC/BC on hsdev -> mcast_flood off -+ bridge link set dev $hsdev isolated on learning off flood off mcast_flood off >/dev/null 2>&1 -+ if [ $? -ne 0 ]; then -+ echo "Error: Failed to set bridge attributes on $hsdev" >&2 -+ clean_up -+ return 1 -+ fi -+ -+ # NOTE: Although not required, BCs will be sent out on hsdev. -+ # NOTE: We need to receive BCs on hsdev, as z/OS HSCI does ARP requests on HS. -+ -+ hsdev_mac="$(cat /sys/class/net/$hsdev/address)" -+ echo "Set $hsdev MAC $hsdev_mac on $ndev and $hsci" -+ -+ # set HS MAC on OSA as secondary MAC -+ bridge fdb add $hsdev_mac dev $ndev >/dev/null 2>&1 -+ if [ $? -ne 0 ]; then -+ echo "Error: Failed to set HS MAC on OSA as secondary MAC" >&2 -+ clean_up -+ return 1 -+ fi -+ -+ # set HS MAC (common MAC) on HSCI as primary MAC -+ ip link set address $hsdev_mac dev $hsci >/dev/null 2>&1 -+ if [ $? -ne 0 ]; then -+ echo "Error: Failed to set HiperSockets MAC (common MAC) on HSCI as primary MAC" >&2 -+ clean_up -+ return 1 -+ fi -+ -+ # use hsdev MTU -+ if [ -e /sys/class/net/$hsdev/mtu ]; then -+ hs_mtu="$(cat /sys/class/net/$hsdev/mtu)" -+ ip link set dev $hsci mtu $hs_mtu >/dev/null 2>&1 -+ if [ $? -ne 0 ]; then -+ echo "Error: Failed to set MTU for $hsci " >&2 -+ clean_up -+ return 1 -+ fi -+ fi -+ ip link set dev $hsci up >/dev/null 2>&1 -+ if [ $? -ne 0 ]; then -+ echo "Error: Failed to set $hsci up" >&2 -+ clean_up -+ return 1 -+ fi -+ -+ # Turn on device for bridge notification -+ bridge link set dev $hsdev learning_sync on self >/dev/null 2>&1 -+ if [ $? -ne 0 ]; then -+ echo "Error: Failed to turn on device for bridge notification" >&2 -+ clean_up -+ return 1 -+ fi -+ echo "Successfully added HSCI interface $hsci" -+ return 0 -+} -+ -+############################################################################## -+## Delete HSCI -+############################################################################## -+ -+function del_hsci { -+ if [ $# != 1 ]; then -+ echo "hsci: invalid parameters" >&2 -+ echo "Use 'hsci --help' for more information" >&2 -+ return 1 -+ fi -+ hsci=$1 -+ if [ $(ip link show dev $hsci | wc -l) -eq 0 ]; then -+ echo "Error: $hsci does not exit" >&2 -+ return 1 -+ fi -+ if [ $(ip link show | grep "master $hsci" | wc -l) -eq 0 ]; then -+ echo "Error: $hsci is not an active HSCI interface" >&2 -+ return 1 -+ fi -+ -+ bports="$(ip link show | grep "master $hsci" | awk '{print $2}')" -+ for bport in $bports; do -+ if [ $(bridge -d link show dev $bport | grep "learning_sync on" | wc -l) -ne 0 ]; then -+ hsdev=${bport%:} -+ else -+ ndev=${bport%:} -+ fi -+ done -+ if [ "$hsdev" == "" ]; then -+ echo "Error: $hsci has no active HiperSockets port" >&2 -+ return 1 -+ fi -+ echo "Deleting HSCI interface $hsci with the HiperSockets $hsdev and the external $ndev" -+ -+ bridge link set dev $hsdev learning_sync off self >/dev/null 2>&1 -+ if [ $? -ne 0 ]; then -+ echo "Error: Failed to turn off learning_sync on $hsdev" >&2 -+ return 1 -+ fi -+ echo 0 > /sys/class/net/$hsdev/device/vnicc/bridge_invisible -+ -+ hsdev_mac="$(cat /sys/class/net/$hsdev/address)" -+ echo "Deleting $hsev MAC $hsdev_mac on $ndev" -+ bridge fdb del $hsdev_mac dev $ndev >/dev/null 2>&1 -+ if [ $? -ne 0 ]; then -+ echo "Error: Failed to delete $hsev MAC $hsdev_mac on $ndev" >&2 -+ return 1 -+ fi -+ -+ ip link del $hsci >/dev/null 2>&1 -+ if [ $? -ne 0 ]; then -+ echo "Error: Failed to delete $hsci" >&2 -+ return 1 -+ fi -+ echo "Successfully deleted device $hsci" -+ -+ return 0 -+} -+ -+############################################################################## -+## Show HSCI -+############################################################################## -+ -+function list_active { -+ hsdev=$1 -+ local ext="" -+ -+ hsci="$(ip link show dev $hsdev | awk '{for(x=1;x&2 -+ echo "Use 'hsci --help' for more information" >&2 -+ return 1 -+ fi -+ header=0 -+ -+ for hs_net_dev in $(ls -1 /sys/class/net/); do -+ list_one $hs_net_dev -+ done -+ -+ return 0 -+} -+ -+#============================================================================== -+ -+function print_version() -+{ -+ echo "hsci utility: version %S390_TOOLS_VERSION%" -+ echo "Copyright IBM Corp. 2020" -+} -+ -+############################################################################## -+##### Main -+############################################################################## -+prereqs_check -+ -+args="$(getopt -u -o hv -l help,version -- $*)" -+[ $? -ne 0 ] && exit 2 -+set -- $args -+while true; do -+ case $1 in -+ -v | --version) -+ print_version -+ exit 0 -+ ;; -+ -h | --help) -+ usage -+ exit 0 -+ ;; -+ --) -+ ;; -+ add) shift -+ add_hsci "$@" -+ exit $? -+ ;; -+ del) shift -+ del_hsci "$@" -+ exit $? -+ ;; -+ show) shift -+ show_hsci "$@" -+ exit $? -+ ;; -+ *) echo "hsci: Please specify a valid command or option" >&2 -+ echo "Use 'hsci --help' for more information" >&2 -+ exit 1 -+ esac -+ shift -+done -+ -diff --git a/hsci/hsci.8 b/hsci/hsci.8 -new file mode 100644 -index 0000000..fc17053 ---- /dev/null -+++ b/hsci/hsci.8 -@@ -0,0 +1,100 @@ -+.\" Copyright IBM Corp. 2020 -+ -+.TH HSCI 8 "November 2020" "s390-tools" "Linux Programmer's Manual" -+ -+ -+.SH NAME -+.B hsci -+\- control and show HSCI settings. -+ -+ -+.SH SYNOPSIS -+.B hsci add -+.I HSDEV -+.I NETDEV -+.br -+.B hsci del -+.I HSCINAME -+.br -+.B hsci show -+.br -+.B hsci [\-hv] -+ -+.SH DESCRIPTION -+.BI hsci -+is used to control and show HSCI (HiperSockets Converged Interfaces) settings. A HiperSockets interface and an external network interface are converged into an HSCI interface. -+ -+.SH COMMANDS -+.TP -+.B add \fIHSDEV\fR \fINETDEV\fR -+.RS .4i -+.PP -+Adds an HSCI interface -+.PP -+.I HSDEV -+is the interface name of the HiperSockets device to be converged into the HSCI interface. -+.PP -+.I NETDEV -+is the interface name of the external network device to be converged into the HSCI interface. -+.RE -+ -+.TP -+.B del \fIHSCINAME\fR -+.RS .4i -+.PP -+Deletes an HSCI interface -+.PP -+.I HSCINAME -+is the name of the HSCI interface for the HiperSockets device and the external network device. -+.RE -+ -+.TP -+.B show -+.RS .4i -+.PP -+Lists the configured HSCI interfaces. -+.RE -+ -+.SH OPTIONS -+.TP -+.BR \-v ", " \-\-version -+Prints the version number of hsci and exits. -+.TP -+.BR \-h ", " \-\-help -+Displays the help information for the command. -+ -+.SH EXIT CODES -+.TP -+.BR "0" -+The hsci command ran successfully. -+ -+.TP -+.BR "1" -+An error occurred. -+ -+.SH EXAMPLE -+.BR "hsci show" -+.TP -+.RB -+Lists the configured HSCI interfaces: -+.RS 1.2i -+ -+HSCI PNET_ID HiperSockets External -+.br -+----------------------------------------- -+.br -+hsci8410 NET1 enc8410 encb040 -+ -+.RE -+ -+.SH SEE ALSO -+.nf -+ip(8), bridge(8) -+.fi -+ -+.SH AUTHOR -+.nf -+Written by Alexandra Winter -+ Wenjia Zhang -+.fi -+ --- -2.26.2 - - -From 0b71c216d158a21c9905dad773a22c456d80b32d Mon Sep 17 00:00:00 2001 -From: =?UTF-8?q?Dan=20Hor=C3=A1k?= -Date: Mon, 7 Dec 2020 14:36:11 +0100 -Subject: [PATCH 7/9] zcryptstats: Fix handling of partial results with many - domains (#1901962) - -Description: zcryptstats: Fix handling of partial results with many domains -Symptom: Running zcryptstats when many domains are available per cryto - card does not produce any output, and is hanging in a loop. -Problem: When many domains per card are available, then the results of - the SCDMD CHSC call may not fit into the output area, and a - partial result is returned. The further results must be - retrieved with another CHSC call. Fix the code to pass the - correct next-domain to the subsequent CHSC call of a partial - response. Otherwise the same set of domains 1 to n are retrieved - again, resulting in an infinite loop, because this will always - produce a partial result. -Solution: Fix the code to pass the correct next-domain to the subsequent - CHSC call of a partial response. -Reproduction: Run zcryptstats on a system with many domains per card. -Upstream-ID: cf2311f1f1de17435b49ba8c8697be91705ba031 ---- - zconf/zcrypt/zcryptstats.c | 14 ++++++++------ - 1 file changed, 8 insertions(+), 6 deletions(-) - -diff --git a/zconf/zcrypt/zcryptstats.c b/zconf/zcrypt/zcryptstats.c -index 3bb2078..2456e5d 100644 ---- a/zconf/zcrypt/zcryptstats.c -+++ b/zconf/zcrypt/zcryptstats.c -@@ -1178,8 +1178,14 @@ static int get_apqn_measurement_data(uint8_t card) - scdmd_area.request.header.code = 0x102d; - scdmd_area.request.header.length = - sizeof(struct chsc_scdmd_request); -- scdmd_area.request.first_drid.ap_index = card; -- scdmd_area.request.first_drid.domain_index = g.min_domain; -+ if (scdmd_area.response.p) { -+ scdmd_area.request.first_drid = -+ scdmd_area.response.crid; -+ } else { -+ scdmd_area.request.first_drid.ap_index = card; -+ scdmd_area.request.first_drid.domain_index = -+ g.min_domain; -+ } - scdmd_area.request.last_drid.ap_index = card; - scdmd_area.request.last_drid.domain_index = g.max_domain; - scdmd_area.request.s = 1; -@@ -1217,10 +1223,6 @@ static int get_apqn_measurement_data(uint8_t card) - 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; --- -2.26.2 - - -From 42d4f61d02b268f6a4e04a18188905ffbd95c9bc Mon Sep 17 00:00:00 2001 -From: =?UTF-8?q?Dan=20Hor=C3=A1k?= -Date: Mon, 7 Dec 2020 14:37:26 +0100 -Subject: [PATCH 8/9] dasdfmt: Fix bad file descriptor error when running on - symlinks (#1901963) - -Description: dasdfmt: Fix bad file descriptor error when running on symlinks -Symptom: When calling dasdfmt on device node symlinks like - /dev/disk/by-id/ccw-0X9300, dasdfmt fails with "dasdfmt: the - ioctl to get the blocksize of the device failed (Bad file - descriptor)" -Problem: This is because before the actual formatting process starts, the - disk will be disabled calling the BIODASDDISABLE ioctl, - resulting in the removal of the symlink. Trying to open this - file later in the process to retrieve e.g. blocksize information - results in the mentioned error, as the file doesn't exist any - longer. -Solution: In order to fix this without modifying the behaviour of libdasd, - introduce the two global variables dev_node and dev_path. - dev_path is the original device path entered by the user. - dev_node on the other hand is the reliable device node under - /dev/block/ using the major and minor numbers and is determined - in get_device_name(). The dev_path is used for message output - only and the dev_node variable is used for the actual disk - operations. -Reproduction: Simply call dasdfmt on a device node symlink in /dev/disk/ such - as /dev/disk/by-id/ccw-0X9300 for example. -Upstream-ID: 148d3f9b64da599adf453baf65e7a8596e2e7d97 -Upstream-ID: cad450fdf9a9dd2562eec27157e4fd133a98813e ---- - dasdfmt/dasdfmt.c | 2 +- - dasdfmt/dasdfmt.h | 4 ++++ - 2 files changed, 5 insertions(+), 1 deletion(-) - -diff --git a/dasdfmt/dasdfmt.c b/dasdfmt/dasdfmt.c -index a424f3c..5665f64 100644 ---- a/dasdfmt/dasdfmt.c -+++ b/dasdfmt/dasdfmt.c -@@ -1637,7 +1637,7 @@ int main(int argc, char *argv[]) - /* End of options string - start of devices list */ - break; - default: -- error("Try '%s --help' for more information."); -+ error("Try '%s --help' for more information.", prog_name); - } - - if (rc == -1) -diff --git a/dasdfmt/dasdfmt.h b/dasdfmt/dasdfmt.h -index 9d35eed..4d6fbb5 100644 ---- a/dasdfmt/dasdfmt.h -+++ b/dasdfmt/dasdfmt.h -@@ -41,6 +41,10 @@ static const char mode_str[3][10] = { - "Full", "Quick", "Expand" - }; - -+/* Report error, free memory, and exit */ -+static void error(const char *format, ...) -+ __attribute__((__noreturn__, __format__(__printf__, 1, 2))); -+ - #define DASD_PARTN_BITS 2 - #define PARTN_MASK ((1 << DASD_PARTN_BITS) - 1) - --- -2.26.2 - - -From 2e86043c334aa5a48878eda656e292fb408dd912 Mon Sep 17 00:00:00 2001 -From: =?UTF-8?q?Dan=20Hor=C3=A1k?= -Date: Mon, 7 Dec 2020 14:38:35 +0100 -Subject: [PATCH 9/9] zkey: Fix KMS plugin configuration to store APQNs - correctly. (#1901968) - -Description: zkey: Fix KMS plugin configuration to store APQNs correctly. -Symptom: When a KMS plugin is configured with APQNs, the set of - APQNs is stored per card type, i.e. the set of CCA APQNs and - the set of EP11 APQNs is stored separately in the KMS - plugin configuration file. Unfortunately, the names of the - configuration properties are swapped, so that CCA APQNs are - stored as EP11 APQNs, and vice versa. - This does not cause any malfunction as of today, however - if this is fixed later, while a KMS plugin configuration - already exists, then the KMS plugin will fail to work once - the fix is applied. A KMS plugin reconfiguration would then - be needed to make the plugin work again. -Problem: The KMS configuration property names to store the CCA and - EP11 APQNs are incorrect, i.e. swapped. -Solution: Correct the KMS configuration property names. -Reproduction: Configure a KMS plugin with APQNs and check the KMS config - file. -Upstream-ID: 07d181e29b484108bce5ea07c1561ffb62a1b56e ---- - zkey/kms.c | 4 ++-- - 1 file changed, 2 insertions(+), 2 deletions(-) - -diff --git a/zkey/kms.c b/zkey/kms.c -index b2ce165..85e3ff2 100644 ---- a/zkey/kms.c -+++ b/zkey/kms.c -@@ -46,8 +46,8 @@ - #define KMS_CONFIG_PROP_KMS "kms" - #define KMS_CONFIG_PROP_KMS_CONFIG "config" - #define KMS_CONFIG_PROP_APQNS "apqns" --#define KMS_CONFIG_PROP_CCA_APQNS "ep11_apqns" --#define KMS_CONFIG_PROP_EP11_APQNS "cca_apqns" -+#define KMS_CONFIG_PROP_CCA_APQNS "cca_apqns" -+#define KMS_CONFIG_PROP_EP11_APQNS "ep11_apqns" - #define KMS_CONFIG_LOCAL "local" - - #define KMS_KEY_PROP_NAME "zkey-name" --- -2.26.2 - diff --git a/SOURCES/s390utils-2.16.0-rhel.patch b/SOURCES/s390utils-2.16.0-rhel.patch new file mode 100644 index 0000000..cf95fbe --- /dev/null +++ b/SOURCES/s390utils-2.16.0-rhel.patch @@ -0,0 +1,3522 @@ +From 02fb523c7cf1619cb53803a52ca73a56bd43f7e2 Mon Sep 17 00:00:00 2001 +From: Ingo Franzki +Date: Tue, 23 Feb 2021 08:52:26 +0100 +Subject: [PATCH 1/7] zkey: Fix build error when the compiler flags are + overridden +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +When the compiler flags are overridden, the build of zkey may fail with: + +kms.c:44:2: error: #error KMS_PLUGIN_LOCATION must be defined + 44 | #error KMS_PLUGIN_LOCATION must be defined + | ^~~~~ + +The Makefile uses CFLAGS variable for defining the KMS_PLUGIN_LOCATION, +but it should rather use ALL_CFLAGS. + +Also use ALL_CPPFLAGS for defining HAVE_LUKS2_SUPPORT. + +Fixes: https://github.com/ibm-s390-linux/s390-tools/issues/108 + +Signed-off-by: Ingo Franzki +Reviewed-by: Jan Hoeppner +Signed-off-by: Jan Höppner +(cherry picked from commit 3f3f063c98278f53ad3b34e68b70fca62eaea8fb) +--- + zkey/Makefile | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +diff --git a/zkey/Makefile b/zkey/Makefile +index 41129bc..f74e209 100644 +--- a/zkey/Makefile ++++ b/zkey/Makefile +@@ -18,7 +18,7 @@ ifneq (${HAVE_CRYPTSETUP2},0) + ifneq (${HAVE_OPENSSL},0) + BUILD_TARGETS += zkey-cryptsetup + INSTALL_TARGETS += install-zkey-cryptsetup +- CPPFLAGS += -DHAVE_LUKS2_SUPPORT ++ ALL_CPPFLAGS += -DHAVE_LUKS2_SUPPORT + else + BUILD_TARGETS += zkey-cryptsetup-skip-openssl + INSTALL_TARGETS += zkey-cryptsetup-skip-openssl +@@ -34,7 +34,7 @@ endif + + libs = $(rootdir)/libutil/libutil.a + +-CFLAGS += -DKMS_PLUGIN_LOCATION=\"$(ZKEYKMSPLUGINDIR)\" ++ALL_CFLAGS += -DKMS_PLUGIN_LOCATION=\"$(ZKEYKMSPLUGINDIR)\" + + detect-libcryptsetup.dep: + echo "#include " > detect-libcryptsetup.dep +-- +2.31.1 + + +From ea06d53a2d775bd1ad54482202ea021aaa93a1d8 Mon Sep 17 00:00:00 2001 +From: Marc Hartmayer +Date: Wed, 24 Feb 2021 15:04:11 +0000 +Subject: [PATCH 2/7] genprotimg: use `pv_` namespace for our Buffer + implementation +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Use `pv_` namespace for our Buffer implementation so a symbol clash with other +libraries is less likely. + +Fixes: https://github.com/ibm-s390-linux/s390-tools/issues/109 +Reviewed-by: Jan Hoeppner +Signed-off-by: Marc Hartmayer +Signed-off-by: Jan Höppner +(cherry picked from commit b6bdd7744aba06d82f30b0c84012f0b06ccb01de) +--- + genprotimg/src/pv/pv_comp.c | 26 +++++------ + genprotimg/src/pv/pv_comp.h | 4 +- + genprotimg/src/pv/pv_comps.c | 10 ++--- + genprotimg/src/pv/pv_comps.h | 4 +- + genprotimg/src/pv/pv_hdr.c | 24 +++++------ + genprotimg/src/pv/pv_hdr.h | 4 +- + genprotimg/src/pv/pv_image.c | 70 +++++++++++++++--------------- + genprotimg/src/pv/pv_image.h | 14 +++--- + genprotimg/src/pv/pv_ipib.c | 4 +- + genprotimg/src/pv/pv_ipib.h | 2 +- + genprotimg/src/pv/pv_stage3.c | 26 +++++------ + genprotimg/src/pv/pv_stage3.h | 10 ++--- + genprotimg/src/utils/buffer.c | 18 ++++---- + genprotimg/src/utils/buffer.h | 16 +++---- + genprotimg/src/utils/crypto.c | 72 +++++++++++++++---------------- + genprotimg/src/utils/crypto.h | 28 ++++++------ + genprotimg/src/utils/file_utils.c | 4 +- + genprotimg/src/utils/file_utils.h | 2 +- + 18 files changed, 169 insertions(+), 169 deletions(-) + +diff --git a/genprotimg/src/pv/pv_comp.c b/genprotimg/src/pv/pv_comp.c +index 1f64eea..21879ae 100644 +--- a/genprotimg/src/pv/pv_comp.c ++++ b/genprotimg/src/pv/pv_comp.c +@@ -73,12 +73,12 @@ PvComponent *pv_component_new_file(PvComponentType type, const gchar *path, + return pv_component_new(type, size, DATA_FILE, (void **)&file, err); + } + +-PvComponent *pv_component_new_buf(PvComponentType type, const Buffer *buf, ++PvComponent *pv_component_new_buf(PvComponentType type, const PvBuffer *buf, + GError **err) + { + g_assert(buf); + +- g_autoptr(Buffer) dup_buf = buffer_dup(buf, FALSE); ++ g_autoptr(PvBuffer) dup_buf = pv_buffer_dup(buf, FALSE); + return pv_component_new(type, buf->size, DATA_BUFFER, (void **)&dup_buf, + err); + } +@@ -90,7 +90,7 @@ void pv_component_free(PvComponent *component) + + switch ((PvComponentDataType)component->d_type) { + case DATA_BUFFER: +- buffer_clear(&component->buf); ++ pv_buffer_clear(&component->buf); + break; + case DATA_FILE: + comp_file_free(component->file); +@@ -162,21 +162,21 @@ gint pv_component_align_and_encrypt(PvComponent *component, const gchar *tmp_pat + + switch ((PvComponentDataType)component->d_type) { + case DATA_BUFFER: { +- g_autoptr(Buffer) enc_buf = NULL; ++ g_autoptr(PvBuffer) enc_buf = NULL; + + if (!(IS_PAGE_ALIGNED(pv_component_size(component)))) { +- g_autoptr(Buffer) new = NULL; ++ g_autoptr(PvBuffer) new = NULL; + + /* create a page aligned copy */ +- new = buffer_dup(component->buf, TRUE); +- buffer_clear(&component->buf); ++ new = pv_buffer_dup(component->buf, TRUE); ++ pv_buffer_clear(&component->buf); + component->buf = g_steal_pointer(&new); + } + enc_buf = encrypt_buf(parms, component->buf, err); + if (!enc_buf) + return -1; + +- buffer_clear(&component->buf); ++ pv_buffer_clear(&component->buf); + component->buf = g_steal_pointer(&enc_buf); + return 0; + } +@@ -220,10 +220,10 @@ gint pv_component_align(PvComponent *component, const gchar *tmp_path, + + switch (component->d_type) { + case DATA_BUFFER: { +- g_autoptr(Buffer) buf = NULL; ++ g_autoptr(PvBuffer) buf = NULL; + +- buf = buffer_dup(component->buf, TRUE); +- buffer_clear(&component->buf); ++ buf = pv_buffer_dup(component->buf, TRUE); ++ pv_buffer_clear(&component->buf); + component->buf = g_steal_pointer(&buf); + return 0; + } break; +@@ -301,7 +301,7 @@ int64_t pv_component_update_pld(const PvComponent *comp, EVP_MD_CTX *ctx, + + switch (comp->d_type) { + case DATA_BUFFER: { +- const Buffer *buf = comp->buf; ++ const PvBuffer *buf = comp->buf; + + g_assert(buf->size <= INT64_MAX); + g_assert(buf->size == size); +@@ -425,7 +425,7 @@ gint pv_component_write(const PvComponent *component, FILE *f, GError **err) + + switch (component->d_type) { + case DATA_BUFFER: { +- const Buffer *buf = component->buf; ++ const PvBuffer *buf = component->buf; + + if (seek_and_write_buffer(f, buf, offset, err) < 0) + return -1; +diff --git a/genprotimg/src/pv/pv_comp.h b/genprotimg/src/pv/pv_comp.h +index aa1b5ae..a4ffae8 100644 +--- a/genprotimg/src/pv/pv_comp.h ++++ b/genprotimg/src/pv/pv_comp.h +@@ -41,7 +41,7 @@ typedef struct { + gint d_type; /* PvComponentDataType */ + union { + struct comp_file *file; +- Buffer *buf; ++ PvBuffer *buf; + void *data; + }; + uint64_t src_addr; +@@ -51,7 +51,7 @@ typedef struct { + + PvComponent *pv_component_new_file(PvComponentType type, const gchar *path, + GError **err); +-PvComponent *pv_component_new_buf(PvComponentType type, const Buffer *buf, ++PvComponent *pv_component_new_buf(PvComponentType type, const PvBuffer *buf, + GError **err); + void pv_component_free(PvComponent *component); + gint pv_component_type(const PvComponent *component); +diff --git a/genprotimg/src/pv/pv_comps.c b/genprotimg/src/pv/pv_comps.c +index 15d32f0..2d364fd 100644 +--- a/genprotimg/src/pv/pv_comps.c ++++ b/genprotimg/src/pv/pv_comps.c +@@ -210,13 +210,13 @@ GSList *pv_img_comps_get_comps(const PvImgComps *comps) + return comps->comps; + } + +-gint pv_img_comps_finalize(PvImgComps *comps, Buffer **pld_digest, +- Buffer **ald_digest, Buffer **tld_digest, ++gint pv_img_comps_finalize(PvImgComps *comps, PvBuffer **pld_digest, ++ PvBuffer **ald_digest, PvBuffer **tld_digest, + uint64_t *nep, GError **err) + { +- g_autoptr(Buffer) tmp_pld_digest = NULL; +- g_autoptr(Buffer) tmp_ald_digest = NULL; +- g_autoptr(Buffer) tmp_tld_digest = NULL; ++ g_autoptr(PvBuffer) tmp_pld_digest = NULL; ++ g_autoptr(PvBuffer) tmp_ald_digest = NULL; ++ g_autoptr(PvBuffer) tmp_tld_digest = NULL; + + comps->finalized = TRUE; + for (GSList *iterator = comps->comps; iterator; iterator = iterator->next) { +diff --git a/genprotimg/src/pv/pv_comps.h b/genprotimg/src/pv/pv_comps.h +index d555e36..891ecdf 100644 +--- a/genprotimg/src/pv/pv_comps.h ++++ b/genprotimg/src/pv/pv_comps.h +@@ -32,8 +32,8 @@ gint pv_img_comps_add_component(PvImgComps *comps, PvComponent **comp, + GError **err); + PvComponent *pv_img_comps_get_nth_comp(PvImgComps *comps, guint n); + gint pv_img_comps_set_offset(PvImgComps *comps, gsize offset, GError **err); +-gint pv_img_comps_finalize(PvImgComps *comps, Buffer **pld_digest, +- Buffer **ald_digest, Buffer **tld_digest, ++gint pv_img_comps_finalize(PvImgComps *comps, PvBuffer **pld_digest, ++ PvBuffer **ald_digest, PvBuffer **tld_digest, + uint64_t *nep, GError **err); + void pv_img_comps_free(PvImgComps *comps); + +diff --git a/genprotimg/src/pv/pv_hdr.c b/genprotimg/src/pv/pv_hdr.c +index 45e721d..e46e176 100644 +--- a/genprotimg/src/pv/pv_hdr.c ++++ b/genprotimg/src/pv/pv_hdr.c +@@ -76,17 +76,17 @@ uint64_t pv_hdr_get_nks(const PvHdr *hdr) + } + + /* In-place modification of ``buf`` */ +-static gint pv_hdr_encrypt(const PvHdr *hdr, const PvImage *img, Buffer *buf, ++static gint pv_hdr_encrypt(const PvHdr *hdr, const PvImage *img, PvBuffer *buf, + GError **err) + { + uint32_t hdr_len = pv_hdr_size(hdr); + uint32_t aad_len = pv_hdr_aad_size(hdr); + guint tag_len = pv_hdr_tag_size(hdr); + uint32_t enc_len = pv_hdr_enc_size_casted(hdr); +- const Buffer aad_part = { .data = buf->data, .size = aad_len }; +- Buffer enc_part = { .data = (uint8_t *)buf->data + aad_len, ++ const PvBuffer aad_part = { .data = buf->data, .size = aad_len }; ++ PvBuffer enc_part = { .data = (uint8_t *)buf->data + aad_len, + .size = enc_len }; +- Buffer tag_part = { .data = (uint8_t *)buf->data + hdr_len - tag_len, ++ PvBuffer tag_part = { .data = (uint8_t *)buf->data + hdr_len - tag_len, + .size = tag_len }; + struct cipher_parms parms; + int64_t c_len; +@@ -119,9 +119,9 @@ static gint pv_hdr_aad_init(PvHdr *hdr, const PvImage *img, GError **err) + g_autofree union ecdh_pub_key *cust_pub_key = NULL; + struct pv_hdr_key_slot *hdr_slot = hdr->slots; + struct pv_hdr_head *head = &hdr->head; +- g_autoptr(Buffer) pld = NULL; +- g_autoptr(Buffer) ald = NULL; +- g_autoptr(Buffer) tld = NULL; ++ g_autoptr(PvBuffer) pld = NULL; ++ g_autoptr(PvBuffer) ald = NULL; ++ g_autoptr(PvBuffer) tld = NULL; + uint64_t nep = 0; + + g_assert(sizeof(head->iv) == img->gcm_iv->size); +@@ -250,7 +250,7 @@ PvHdr *pv_hdr_new(const PvImage *img, GError **err) + return g_steal_pointer(&ret); + } + +-static void pv_hdr_memcpy(const PvHdr *hdr, const Buffer *dst) ++static void pv_hdr_memcpy(const PvHdr *hdr, const PvBuffer *dst) + { + uint64_t nks = pv_hdr_get_nks(hdr); + uint8_t *data; +@@ -270,13 +270,13 @@ static void pv_hdr_memcpy(const PvHdr *hdr, const Buffer *dst) + } + } + +-Buffer *pv_hdr_serialize(const PvHdr *hdr, const PvImage *img, +- enum PvCryptoMode mode, GError **err) ++PvBuffer *pv_hdr_serialize(const PvHdr *hdr, const PvImage *img, ++ enum PvCryptoMode mode, GError **err) + { + uint32_t hdr_size = pv_hdr_size(hdr); +- g_autoptr(Buffer) ret = NULL; ++ g_autoptr(PvBuffer) ret = NULL; + +- ret = buffer_alloc(hdr_size); ++ ret = pv_buffer_alloc(hdr_size); + pv_hdr_memcpy(hdr, ret); + + if (mode == PV_ENCRYPT) { +diff --git a/genprotimg/src/pv/pv_hdr.h b/genprotimg/src/pv/pv_hdr.h +index 8df7a6f..fbcc9e9 100644 +--- a/genprotimg/src/pv/pv_hdr.h ++++ b/genprotimg/src/pv/pv_hdr.h +@@ -23,8 +23,8 @@ + PvHdr *pv_hdr_new(const PvImage *img, GError **err); + void pv_hdr_free(PvHdr *hdr); + G_GNUC_UNUSED gboolean pv_hdr_uses_encryption(const PvHdr *hdr); +-Buffer *pv_hdr_serialize(const PvHdr *hdr, const PvImage *img, +- enum PvCryptoMode mode, GError **err); ++PvBuffer *pv_hdr_serialize(const PvHdr *hdr, const PvImage *img, ++ enum PvCryptoMode mode, GError **err); + uint32_t pv_hdr_size(const PvHdr *hdr); + uint32_t pv_hdr_aad_size(const PvHdr *hdr); + uint64_t pv_hdr_enc_size(const PvHdr *hdr); +diff --git a/genprotimg/src/pv/pv_image.c b/genprotimg/src/pv/pv_image.c +index 59eca5e..375e40f 100644 +--- a/genprotimg/src/pv/pv_image.c ++++ b/genprotimg/src/pv/pv_image.c +@@ -56,7 +56,7 @@ static gint pv_img_prepare_component(const PvImage *img, PvComponent *comp, + GError **err) + { + struct cipher_parms parms = { 0 }; +- g_autoptr(Buffer) tweak = NULL; ++ g_autoptr(PvBuffer) tweak = NULL; + prepare_func func = NULL; + void *opaque = NULL; + gint rc; +@@ -76,7 +76,7 @@ static gint pv_img_prepare_component(const PvImage *img, PvComponent *comp, + EVP_CIPHER_iv_length(cipher)); + g_assert(img->xts_key->size <= UINT_MAX); + +- tweak = buffer_alloc(sizeof(comp->tweak.data)); ++ tweak = pv_buffer_alloc(sizeof(comp->tweak.data)); + memcpy(tweak->data, comp->tweak.data, tweak->size); + func = pv_component_align_and_encrypt; + parms.cipher = cipher; +@@ -93,11 +93,11 @@ static gint pv_img_prepare_component(const PvImage *img, PvComponent *comp, + return 0; + } + +-static Buffer *pv_img_read_key(const gchar *path, guint key_size, +- GError **err) ++static PvBuffer *pv_img_read_key(const gchar *path, guint key_size, ++ GError **err) + { +- g_autoptr(Buffer) tmp_ret = NULL; +- Buffer *ret = NULL; ++ g_autoptr(PvBuffer) tmp_ret = NULL; ++ PvBuffer *ret = NULL; + gsize bytes_read; + FILE *f = NULL; + gsize size; +@@ -116,7 +116,7 @@ static Buffer *pv_img_read_key(const gchar *path, guint key_size, + if (!f) + return NULL; + +- tmp_ret = buffer_alloc(size); ++ tmp_ret = pv_buffer_alloc(size); + if (file_read(f, tmp_ret->data, 1, tmp_ret->size, &bytes_read, err) < 0) + goto err; + +@@ -160,8 +160,8 @@ static HostKeyList *pv_img_get_host_keys(GSList *host_keys_with_path, gint nid, + return g_steal_pointer(&ret); + } + +-static Buffer *pv_img_get_key(const EVP_CIPHER *cipher, const gchar *path, +- GError **err) ++static PvBuffer *pv_img_get_key(const EVP_CIPHER *cipher, const gchar *path, ++ GError **err) + { + gint key_len = EVP_CIPHER_key_length(cipher); + +@@ -173,8 +173,8 @@ static Buffer *pv_img_get_key(const EVP_CIPHER *cipher, const gchar *path, + return generate_aes_key((guint)key_len, err); + } + +-static Buffer *pv_img_get_iv(const EVP_CIPHER *cipher, const gchar *path, +- GError **err) ++static PvBuffer *pv_img_get_iv(const EVP_CIPHER *cipher, const gchar *path, ++ GError **err) + { + gint iv_len = EVP_CIPHER_iv_length(cipher); + +@@ -485,23 +485,23 @@ static void pv_hdr_key_slot_free(PvHdrKeySlot *slot) + WRAPPED_G_DEFINE_AUTOPTR_CLEANUP_FUNC(PvHdrKeySlot, pv_hdr_key_slot_free) + + static PvHdrKeySlot *pv_hdr_key_slot_new(const EVP_CIPHER *gcm_cipher, +- const Buffer *cust_root_key, ++ const PvBuffer *cust_root_key, + EVP_PKEY *cust_key, EVP_PKEY *host_key, + GError **err) + { + g_autoptr(PvHdrKeySlot) ret = g_new0(PvHdrKeySlot, 1); + g_autofree union ecdh_pub_key *pub = NULL; +- g_autoptr(Buffer) exchange_key = NULL; +- g_autoptr(Buffer) digest_key = NULL; +- g_autoptr(Buffer) iv = NULL; +- Buffer pub_buf; ++ g_autoptr(PvBuffer) exchange_key = NULL; ++ g_autoptr(PvBuffer) digest_key = NULL; ++ g_autoptr(PvBuffer) iv = NULL; ++ PvBuffer pub_buf; + /* No AAD data is used */ +- Buffer aad = { .data = NULL, .size = 0 }; ++ PvBuffer aad = { .data = NULL, .size = 0 }; + /* Set the output buffers for the encrypted data and the + * generated GCM tag + */ +- Buffer enc = { .data = ret->wrapped_key, .size = sizeof(ret->wrapped_key) }; +- Buffer tag = { .data = ret->tag, .size = sizeof(ret->tag) }; ++ PvBuffer enc = { .data = ret->wrapped_key, .size = sizeof(ret->wrapped_key) }; ++ PvBuffer tag = { .data = ret->tag, .size = sizeof(ret->tag) }; + struct cipher_parms parms; + int64_t c_len = 0; + +@@ -530,7 +530,7 @@ static PvHdrKeySlot *pv_hdr_key_slot_new(const EVP_CIPHER *gcm_cipher, + g_assert(exchange_key->size == (guint)EVP_CIPHER_key_length(gcm_cipher)); + + /* create zero IV */ +- iv = buffer_alloc((guint)EVP_CIPHER_iv_length(gcm_cipher)); ++ iv = pv_buffer_alloc((guint)EVP_CIPHER_iv_length(gcm_cipher)); + parms.iv_or_tweak = iv; + parms.key = exchange_key; + parms.cipher = gcm_cipher; +@@ -637,13 +637,13 @@ void pv_img_free(PvImage *img) + g_slist_free_full(img->key_slots, (GDestroyNotify)pv_hdr_key_slot_free); + g_slist_free_full(img->host_pub_keys, (GDestroyNotify)EVP_PKEY_free); + EVP_PKEY_free(img->cust_pub_priv_key); +- buffer_clear(&img->stage3a); ++ pv_buffer_clear(&img->stage3a); + pv_img_comps_free(img->comps); + g_free(img->tmp_dir); +- buffer_free(img->xts_key); +- buffer_free(img->cust_root_key); +- buffer_free(img->gcm_iv); +- buffer_free(img->cust_comm_key); ++ pv_buffer_free(img->xts_key); ++ pv_buffer_free(img->cust_root_key); ++ pv_buffer_free(img->gcm_iv); ++ pv_buffer_free(img->cust_comm_key); + g_free(img); + } + +@@ -684,13 +684,13 @@ gint pv_img_add_component(PvImage *img, const PvArg *arg, GError **err) + return 0; + } + +-gint pv_img_calc_pld_ald_tld_nep(const PvImage *img, Buffer **pld, Buffer **ald, +- Buffer **tld, uint64_t *nep, GError **err) ++gint pv_img_calc_pld_ald_tld_nep(const PvImage *img, PvBuffer **pld, PvBuffer **ald, ++ PvBuffer **tld, uint64_t *nep, GError **err) + { + return pv_img_comps_finalize(img->comps, pld, ald, tld, nep, err); + } + +-static gint pv_img_build_stage3b(PvImage *img, Buffer *stage3b, GError **err) ++static gint pv_img_build_stage3b(PvImage *img, PvBuffer *stage3b, GError **err) + { + g_autofree struct stage3b_args *args = NULL; + +@@ -708,7 +708,7 @@ static gint pv_img_build_stage3b(PvImage *img, Buffer *stage3b, GError **err) + gint pv_img_add_stage3b_comp(PvImage *img, const gchar *path, GError **err) + { + g_autoptr(PvComponent) comp = NULL; +- g_autoptr(Buffer) stage3b = NULL; ++ g_autoptr(PvBuffer) stage3b = NULL; + + stage3b = stage3b_getblob(path, err); + if (!stage3b) +@@ -825,7 +825,7 @@ static gint get_stage3a_data_size(const PvImage *img, gsize *data_size, + + gint pv_img_load_and_set_stage3a(PvImage *img, const gchar *path, GError **err) + { +- g_autoptr(Buffer) stage3a = NULL; ++ g_autoptr(PvBuffer) stage3a = NULL; + gsize bin_size, data_size = 0; + + if (get_stage3a_data_size(img, &data_size, err) < 0) +@@ -845,8 +845,8 @@ gint pv_img_load_and_set_stage3a(PvImage *img, const gchar *path, GError **err) + } + + /* Creates the PV IPIB and sets the stage3a arguments */ +-static gint pv_img_build_stage3a(Buffer *stage3a, gsize stage3a_bin_size, +- GSList *comps, const Buffer *hdr, GError **err) ++static gint pv_img_build_stage3a(PvBuffer *stage3a, gsize stage3a_bin_size, ++ GSList *comps, const PvBuffer *hdr, GError **err) + { + g_autofree struct ipl_parameter_block *ipib = NULL; + +@@ -866,9 +866,9 @@ static gint pv_img_build_stage3a(Buffer *stage3a, gsize stage3a_bin_size, + } + + /* Creates the actual PV header (serialized and AES-GCM encrypted) */ +-static Buffer *pv_img_create_pv_hdr(PvImage *img, GError **err) ++static PvBuffer *pv_img_create_pv_hdr(PvImage *img, GError **err) + { +- g_autoptr(Buffer) hdr_buf = NULL; ++ g_autoptr(PvBuffer) hdr_buf = NULL; + g_autoptr(PvHdr) hdr = NULL; + + hdr = pv_hdr_new(img, err); +@@ -887,7 +887,7 @@ static Buffer *pv_img_create_pv_hdr(PvImage *img, GError **err) + */ + gint pv_img_finalize(PvImage *pv, const gchar *stage3b_path, GError **err) + { +- g_autoptr(Buffer) hdr = NULL; ++ g_autoptr(PvBuffer) hdr = NULL; + + /* load stage3b template into memory and add it to the list of + * components. This must be done before calling +diff --git a/genprotimg/src/pv/pv_image.h b/genprotimg/src/pv/pv_image.h +index 7c624e2..116fb1a 100644 +--- a/genprotimg/src/pv/pv_image.h ++++ b/genprotimg/src/pv/pv_image.h +@@ -25,7 +25,7 @@ + + typedef struct { + gchar *tmp_dir; /* directory used for temporary files */ +- Buffer *stage3a; /* stage3a containing IPIB and PV header */ ++ PvBuffer *stage3a; /* stage3a containing IPIB and PV header */ + gsize stage3a_bin_size; /* size of stage3a.bin */ + struct psw_t stage3a_psw; /* (short) PSW that is written to + * location 0 of the created image +@@ -35,15 +35,15 @@ typedef struct { + GSList *host_pub_keys; /* public host keys */ + gint nid; /* Elliptic Curve used for the key derivation */ + /* keys and cipher used for the AES-GCM encryption */ +- Buffer *cust_root_key; +- Buffer *gcm_iv; ++ PvBuffer *cust_root_key; ++ PvBuffer *gcm_iv; + const EVP_CIPHER *gcm_cipher; + /* Information for the IPIB and PV header */ + uint64_t pcf; + uint64_t scf; +- Buffer *cust_comm_key; ++ PvBuffer *cust_comm_key; + const EVP_CIPHER *cust_comm_cipher; +- Buffer *xts_key; ++ PvBuffer *xts_key; + const EVP_CIPHER *xts_cipher; + GSList *key_slots; + GSList *optional_items; +@@ -54,8 +54,8 @@ PvImage *pv_img_new(PvArgs *args, const gchar *stage3a_path, GError **err); + void pv_img_free(PvImage *img); + gint pv_img_add_component(PvImage *img, const PvArg *arg, GError **err); + gint pv_img_finalize(PvImage *img, const gchar *stage3b_path, GError **err); +-gint pv_img_calc_pld_ald_tld_nep(const PvImage *img, Buffer **pld, Buffer **ald, +- Buffer **tld, uint64_t *nep, GError **err); ++gint pv_img_calc_pld_ald_tld_nep(const PvImage *img, PvBuffer **pld, PvBuffer **ald, ++ PvBuffer **tld, uint64_t *nep, GError **err); + gint pv_img_load_and_set_stage3a(PvImage *img, const gchar *path, GError **err); + const PvComponent *pv_img_get_stage3b_comp(const PvImage *img, GError **err); + gint pv_img_add_stage3b_comp(PvImage *img, const gchar *path, GError **err); +diff --git a/genprotimg/src/pv/pv_ipib.c b/genprotimg/src/pv/pv_ipib.c +index 2517e54..59fe008 100644 +--- a/genprotimg/src/pv/pv_ipib.c ++++ b/genprotimg/src/pv/pv_ipib.c +@@ -35,7 +35,7 @@ uint64_t pv_ipib_get_size(uint32_t num_comp) + } + + static gint pv_ipib_init(IplParameterBlock *ipib, GSList *comps, +- const Buffer *hdr) ++ const PvBuffer *hdr) + { + g_assert(sizeof(struct ipl_pl_hdr) <= UINT32_MAX); + g_assert(sizeof(struct ipl_pb0_pv_comp) <= UINT32_MAX); +@@ -100,7 +100,7 @@ static gint pv_ipib_init(IplParameterBlock *ipib, GSList *comps, + return 0; + } + +-IplParameterBlock *pv_ipib_new(GSList *comps, const Buffer *hdr, GError **err) ++IplParameterBlock *pv_ipib_new(GSList *comps, const PvBuffer *hdr, GError **err) + { + uint64_t ipib_size = pv_ipib_get_size(g_slist_length(comps)); + g_autoptr(IplParameterBlock) ret = NULL; +diff --git a/genprotimg/src/pv/pv_ipib.h b/genprotimg/src/pv/pv_ipib.h +index 9331790..4b66643 100644 +--- a/genprotimg/src/pv/pv_ipib.h ++++ b/genprotimg/src/pv/pv_ipib.h +@@ -19,7 +19,7 @@ + typedef struct ipl_parameter_block IplParameterBlock; + + uint64_t pv_ipib_get_size(uint32_t num_comp); +-IplParameterBlock *pv_ipib_new(GSList *comps, const Buffer *hdr, GError **err); ++IplParameterBlock *pv_ipib_new(GSList *comps, const PvBuffer *hdr, GError **err); + void pv_ipib_free(IplParameterBlock *ipib); + + WRAPPED_G_DEFINE_AUTOPTR_CLEANUP_FUNC(IplParameterBlock, pv_ipib_free) +diff --git a/genprotimg/src/pv/pv_stage3.c b/genprotimg/src/pv/pv_stage3.c +index a1e5b16..bff9db7 100644 +--- a/genprotimg/src/pv/pv_stage3.c ++++ b/genprotimg/src/pv/pv_stage3.c +@@ -24,12 +24,12 @@ + ((struct stage3a_args *)((uint64_t)data_ptr + loader_size - \ + sizeof(struct stage3a_args))) + +-static Buffer *loader_getblob(const gchar *filename, gsize *loader_size, +- gsize args_size, gsize data_size, +- gboolean data_aligned, GError **err) ++static PvBuffer *loader_getblob(const gchar *filename, gsize *loader_size, ++ gsize args_size, gsize data_size, ++ gboolean data_aligned, GError **err) + { + g_autoptr(GMappedFile) mapped_file = NULL; +- g_autoptr(Buffer) ret = NULL; ++ g_autoptr(PvBuffer) ret = NULL; + gsize size, tmp_loader_size; + gchar *loader_data; + +@@ -60,7 +60,7 @@ static Buffer *loader_getblob(const gchar *filename, gsize *loader_size, + size = (data_aligned ? PAGE_ALIGN(tmp_loader_size) : tmp_loader_size) + + data_size; + +- ret = buffer_alloc(size); ++ ret = pv_buffer_alloc(size); + + /* copy the loader "template" */ + memcpy(ret->data, loader_data, tmp_loader_size); +@@ -71,8 +71,8 @@ static Buffer *loader_getblob(const gchar *filename, gsize *loader_size, + return g_steal_pointer(&ret); + } + +-Buffer *stage3a_getblob(const gchar *filename, gsize *loader_size, +- gsize data_size, GError **err) ++PvBuffer *stage3a_getblob(const gchar *filename, gsize *loader_size, ++ gsize data_size, GError **err) + { + return loader_getblob(filename, loader_size, + sizeof(struct stage3a_args), data_size, TRUE, +@@ -83,8 +83,8 @@ Buffer *stage3a_getblob(const gchar *filename, gsize *loader_size, + /* Set the right offsets and sizes in the stage3a template + add + * the IPIB block with the PV header + */ +-static gint stage3a_set_data(Buffer *loader, gsize loader_size, +- const Buffer *hdr, struct ipl_parameter_block *ipib, ++static gint stage3a_set_data(PvBuffer *loader, gsize loader_size, ++ const PvBuffer *hdr, struct ipl_parameter_block *ipib, + GError **err) + { + uint32_t ipib_size = GUINT32_FROM_BE(ipib->hdr.len); +@@ -126,15 +126,15 @@ static gint stage3a_set_data(Buffer *loader, gsize loader_size, + return 0; + } + +-gint build_stage3a(Buffer *loader, gsize loader_size, const Buffer *hdr, ++gint build_stage3a(PvBuffer *loader, gsize loader_size, const PvBuffer *hdr, + struct ipl_parameter_block *ipib, GError **err) + { + return stage3a_set_data(loader, loader_size, hdr, ipib, err); + } + +-Buffer *stage3b_getblob(const gchar *filename, GError **err) ++PvBuffer *stage3b_getblob(const gchar *filename, GError **err) + { +- g_autoptr(Buffer) ret = NULL; ++ g_autoptr(PvBuffer) ret = NULL; + gsize rb_size; + + ret = loader_getblob(filename, &rb_size, sizeof(struct stage3b_args), 0, +@@ -146,7 +146,7 @@ Buffer *stage3b_getblob(const gchar *filename, GError **err) + return g_steal_pointer(&ret); + } + +-void build_stage3b(Buffer *stage3b, const struct stage3b_args *args) ++void build_stage3b(PvBuffer *stage3b, const struct stage3b_args *args) + { + g_assert(stage3b->size > sizeof(*args)); + +diff --git a/genprotimg/src/pv/pv_stage3.h b/genprotimg/src/pv/pv_stage3.h +index baaf921..364408e 100644 +--- a/genprotimg/src/pv/pv_stage3.h ++++ b/genprotimg/src/pv/pv_stage3.h +@@ -19,12 +19,12 @@ + #include "boot/stage3b.h" + #include "utils/buffer.h" + +-Buffer *stage3a_getblob(const gchar *filename, gsize *loader_size, +- gsize data_size, GError **err); +-gint build_stage3a(Buffer *dc, gsize dc_size, const Buffer *hdr, ++PvBuffer *stage3a_getblob(const gchar *filename, gsize *loader_size, ++ gsize data_size, GError **err); ++gint build_stage3a(PvBuffer *dc, gsize dc_size, const PvBuffer *hdr, + struct ipl_parameter_block *ipib, GError **err); +-Buffer *stage3b_getblob(const gchar *filename, GError **err); +-void build_stage3b(Buffer *stage3b, const struct stage3b_args *args); ++PvBuffer *stage3b_getblob(const gchar *filename, GError **err); ++void build_stage3b(PvBuffer *stage3b, const struct stage3b_args *args); + void memblob_init(struct memblob *arg, uint64_t src, uint64_t size); + + #endif +diff --git a/genprotimg/src/utils/buffer.c b/genprotimg/src/utils/buffer.c +index 35aed74..509dc0d 100644 +--- a/genprotimg/src/utils/buffer.c ++++ b/genprotimg/src/utils/buffer.c +@@ -17,18 +17,18 @@ + #include "common.h" + #include "file_utils.h" + +-Buffer *buffer_alloc(gsize size) ++PvBuffer *pv_buffer_alloc(gsize size) + { +- Buffer *ret = g_new0(Buffer, 1); ++ PvBuffer *ret = g_new0(PvBuffer, 1); + + ret->data = g_malloc0(size); + ret->size = size; + return ret; + } + +-Buffer *buffer_dup(const Buffer *buf, gboolean page_aligned) ++PvBuffer *pv_buffer_dup(const PvBuffer *buf, gboolean page_aligned) + { +- Buffer *ret; ++ PvBuffer *ret; + gsize size; + + if (!buf) +@@ -38,19 +38,19 @@ Buffer *buffer_dup(const Buffer *buf, gboolean page_aligned) + if (page_aligned) + size = PAGE_ALIGN(size); + +- ret = buffer_alloc(size); ++ ret = pv_buffer_alloc(size); + + /* content will be 0-right-padded */ + memcpy(ret->data, buf->data, buf->size); + return ret; + } + +-gint buffer_write(const Buffer *buf, FILE *file, GError **err) ++gint pv_buffer_write(const PvBuffer *buf, FILE *file, GError **err) + { + return file_write(file, buf->data, buf->size, 1, NULL, err); + } + +-void buffer_free(Buffer *buf) ++void pv_buffer_free(PvBuffer *buf) + { + if (!buf) + return; +@@ -59,11 +59,11 @@ void buffer_free(Buffer *buf) + g_free(buf); + } + +-void buffer_clear(Buffer **buf) ++void pv_buffer_clear(PvBuffer **buf) + { + if (!buf || !*buf) + return; + +- buffer_free(*buf); ++ pv_buffer_free(*buf); + *buf = NULL; + } +diff --git a/genprotimg/src/utils/buffer.h b/genprotimg/src/utils/buffer.h +index 7239d5c..824b72c 100644 +--- a/genprotimg/src/utils/buffer.h ++++ b/genprotimg/src/utils/buffer.h +@@ -15,17 +15,17 @@ + + #include "common.h" + +-typedef struct Buffer { ++typedef struct PvBuffer { + void *data; + gsize size; /* in bytes */ +-} Buffer; ++} PvBuffer; + +-Buffer *buffer_alloc(gsize size); +-void buffer_free(Buffer *buf); +-void buffer_clear(Buffer **buf); +-gint buffer_write(const Buffer *buf, FILE *file, GError **err); +-Buffer *buffer_dup(const Buffer *buf, gboolean page_aligned); ++PvBuffer *pv_buffer_alloc(gsize size); ++void pv_buffer_free(PvBuffer *buf); ++void pv_buffer_clear(PvBuffer **buf); ++gint pv_buffer_write(const PvBuffer *buf, FILE *file, GError **err); ++PvBuffer *pv_buffer_dup(const PvBuffer *buf, gboolean page_aligned); + +-WRAPPED_G_DEFINE_AUTOPTR_CLEANUP_FUNC(Buffer, buffer_free) ++WRAPPED_G_DEFINE_AUTOPTR_CLEANUP_FUNC(PvBuffer, pv_buffer_free) + + #endif +diff --git a/genprotimg/src/utils/crypto.c b/genprotimg/src/utils/crypto.c +index 44facc2..05c3e83 100644 +--- a/genprotimg/src/utils/crypto.c ++++ b/genprotimg/src/utils/crypto.c +@@ -89,15 +89,15 @@ EVP_MD_CTX *digest_ctx_new(const EVP_MD *md, GError **err) + return g_steal_pointer(&ctx); + } + +-Buffer *digest_ctx_finalize(EVP_MD_CTX *ctx, GError **err) ++PvBuffer *digest_ctx_finalize(EVP_MD_CTX *ctx, GError **err) + { + gint md_size = EVP_MD_size(EVP_MD_CTX_md(ctx)); +- g_autoptr(Buffer) ret = NULL; ++ g_autoptr(PvBuffer) ret = NULL; + guint digest_size; + + g_assert(md_size > 0); + +- ret = buffer_alloc((guint)md_size); ++ ret = pv_buffer_alloc((guint)md_size); + if (EVP_DigestFinal_ex(ctx, ret->data, &digest_size) != 1) { + g_set_error(err, PV_CRYPTO_ERROR, PV_CRYPTO_ERROR_INTERNAL, + _("EVP_DigestFinal_ex failed")); +@@ -110,10 +110,10 @@ Buffer *digest_ctx_finalize(EVP_MD_CTX *ctx, GError **err) + } + + /* Returns the digest of @buf using the hash algorithm @md */ +-static Buffer *digest_buffer(const EVP_MD *md, const Buffer *buf, GError **err) ++static PvBuffer *digest_buffer(const EVP_MD *md, const PvBuffer *buf, GError **err) + { + g_autoptr(EVP_MD_CTX) md_ctx = NULL; +- g_autoptr(Buffer) ret = NULL; ++ g_autoptr(PvBuffer) ret = NULL; + g_assert(buf); + + md_ctx = digest_ctx_new(md, err); +@@ -134,9 +134,9 @@ static Buffer *digest_buffer(const EVP_MD *md, const Buffer *buf, GError **err) + } + + /* Returns the SHA256 digest of @buf */ +-Buffer *sha256_buffer(const Buffer *buf, GError **err) ++PvBuffer *sha256_buffer(const PvBuffer *buf, GError **err) + { +- g_autoptr(Buffer) ret = NULL; ++ g_autoptr(PvBuffer) ret = NULL; + + ret = digest_buffer(EVP_sha256(), buf, err); + if (!ret) +@@ -207,10 +207,10 @@ union ecdh_pub_key *evp_pkey_to_ecdh_pub_key(EVP_PKEY *key, GError **err) + return g_steal_pointer(&ret); + } + +-static Buffer *derive_key(EVP_PKEY *cust, EVP_PKEY *host, GError **err) ++static PvBuffer *derive_key(EVP_PKEY *cust, EVP_PKEY *host, GError **err) + { + g_autoptr(EVP_PKEY_CTX) ctx = NULL; +- g_autoptr(Buffer) ret = NULL; ++ g_autoptr(PvBuffer) ret = NULL; + gsize key_size; + + ctx = EVP_PKEY_CTX_new(cust, NULL); +@@ -236,7 +236,7 @@ static Buffer *derive_key(EVP_PKEY *cust, EVP_PKEY *host, GError **err) + return NULL; + } + +- ret = buffer_alloc(key_size); ++ ret = pv_buffer_alloc(key_size); + if (EVP_PKEY_derive(ctx, ret->data, &key_size) != 1) { + g_set_error(err, PV_CRYPTO_ERROR, PV_CRYPTO_ERROR_DERIVE, + _("Key derivation failed")); +@@ -247,11 +247,11 @@ static Buffer *derive_key(EVP_PKEY *cust, EVP_PKEY *host, GError **err) + return g_steal_pointer(&ret); + } + +-Buffer *compute_exchange_key(EVP_PKEY *cust, EVP_PKEY *host, GError **err) ++PvBuffer *compute_exchange_key(EVP_PKEY *cust, EVP_PKEY *host, GError **err) + { +- g_autoptr(Buffer) raw = buffer_alloc(70); +- g_autoptr(Buffer) ret = NULL; +- g_autoptr(Buffer) key = NULL; ++ g_autoptr(PvBuffer) raw = pv_buffer_alloc(70); ++ g_autoptr(PvBuffer) ret = NULL; ++ g_autoptr(PvBuffer) key = NULL; + guchar *data; + + key = derive_key(cust, host, err); +@@ -290,10 +290,10 @@ gint generate_tweak(union tweak *tweak, uint16_t i, GError **err) + return 0; + } + +-static Buffer *generate_rand_data(guint size, const gchar *err_msg, +- GError **err) ++static PvBuffer *generate_rand_data(guint size, const gchar *err_msg, ++ GError **err) + { +- g_autoptr(Buffer) buf = buffer_alloc(size); ++ g_autoptr(PvBuffer) buf = pv_buffer_alloc(size); + + g_assert(size <= INT_MAX); + +@@ -307,14 +307,14 @@ static Buffer *generate_rand_data(guint size, const gchar *err_msg, + return g_steal_pointer(&buf); + } + +-Buffer *generate_aes_iv(guint size, GError **err) ++PvBuffer *generate_aes_iv(guint size, GError **err) + { + return generate_rand_data(size, + _("Generating a IV failed because the required amount of random data is not available"), + err); + } + +-Buffer *generate_aes_key(guint size, GError **err) ++PvBuffer *generate_aes_key(guint size, GError **err) + { + return generate_rand_data(size, + _("Generating a key failed because the required amount of random data is not available"), +@@ -1756,8 +1756,8 @@ static gint __encrypt_decrypt_bio(const struct cipher_parms *parms, BIO *b_in, + gint cipher_block_size = EVP_CIPHER_block_size(cipher); + guchar in_buf[PAGE_SIZE], + out_buf[PAGE_SIZE + (guint)cipher_block_size]; +- const Buffer *key = parms->key; +- const Buffer *tweak = parms->iv_or_tweak; ++ const PvBuffer *key = parms->key; ++ const PvBuffer *tweak = parms->iv_or_tweak; + g_autofree guchar *tmp_tweak = NULL; + gint out_len, tweak_size; + gsize tmp_size_in = 0, tmp_size_out = 0; +@@ -1895,11 +1895,11 @@ static gint __encrypt_decrypt_bio(const struct cipher_parms *parms, BIO *b_in, + return 0; + } + +-static Buffer *__encrypt_decrypt_buffer(const struct cipher_parms *parms, +- const Buffer *in, gboolean encrypt, +- GError **err) ++static PvBuffer *__encrypt_decrypt_buffer(const struct cipher_parms *parms, ++ const PvBuffer *in, gboolean encrypt, ++ GError **err) + { +- g_autoptr(Buffer) ret = NULL; ++ g_autoptr(PvBuffer) ret = NULL; + g_autoptr(BIO) b_out = NULL; + g_autoptr(BIO) b_in = NULL; + gsize in_size, out_size; +@@ -1927,19 +1927,19 @@ static Buffer *__encrypt_decrypt_buffer(const struct cipher_parms *parms, + return NULL; + } + +- ret = buffer_alloc((unsigned long)data_size); ++ ret = pv_buffer_alloc((unsigned long)data_size); + memcpy(ret->data, data, ret->size); + return g_steal_pointer(&ret); + } + +-Buffer *encrypt_buf(const struct cipher_parms *parms, const Buffer *in, +- GError **err) ++PvBuffer *encrypt_buf(const struct cipher_parms *parms, const PvBuffer *in, ++ GError **err) + { + return __encrypt_decrypt_buffer(parms, in, TRUE, err); + } + +-Buffer *decrypt_buf(const struct cipher_parms *parms, const Buffer *in, +- GError **err) ++PvBuffer *decrypt_buf(const struct cipher_parms *parms, const PvBuffer *in, ++ GError **err) + { + return __encrypt_decrypt_buffer(parms, in, FALSE, err); + } +@@ -1993,16 +1993,16 @@ G_GNUC_UNUSED static gint decrypt_file(const struct cipher_parms *parms, + } + + /* GCM mode uses (zero-)padding */ +-static int64_t gcm_encrypt_decrypt(const Buffer *in, const Buffer *aad, ++static int64_t gcm_encrypt_decrypt(const PvBuffer *in, const PvBuffer *aad, + const struct cipher_parms *parms, +- Buffer *out, Buffer *tag, ++ PvBuffer *out, PvBuffer *tag, + enum PvCryptoMode mode, GError **err) + { + g_autoptr(EVP_CIPHER_CTX) ctx = NULL; + const EVP_CIPHER *cipher = parms->cipher; +- const Buffer *iv = parms->iv_or_tweak; ++ const PvBuffer *iv = parms->iv_or_tweak; + gboolean encrypt = mode == PV_ENCRYPT; +- const Buffer *key = parms->key; ++ const PvBuffer *key = parms->key; + int64_t ret = -1; + gint len = -1; + +@@ -2097,8 +2097,8 @@ static int64_t gcm_encrypt_decrypt(const Buffer *in, const Buffer *aad, + return ret; + } + +-int64_t gcm_encrypt(const Buffer *in, const Buffer *aad, +- const struct cipher_parms *parms, Buffer *out, Buffer *tag, ++int64_t gcm_encrypt(const PvBuffer *in, const PvBuffer *aad, ++ const struct cipher_parms *parms, PvBuffer *out, PvBuffer *tag, + GError **err) + { + return gcm_encrypt_decrypt(in, aad, parms, out, tag, PV_ENCRYPT, err); +diff --git a/genprotimg/src/utils/crypto.h b/genprotimg/src/utils/crypto.h +index 286cf45..3cda450 100644 +--- a/genprotimg/src/utils/crypto.h ++++ b/genprotimg/src/utils/crypto.h +@@ -117,8 +117,8 @@ union tweak { + + struct cipher_parms { + const EVP_CIPHER *cipher; +- const Buffer *key; +- const Buffer *iv_or_tweak; ++ const PvBuffer *key; ++ const PvBuffer *iv_or_tweak; + }; + + int check_crl_valid_for_cert(X509_CRL *crl, X509 *cert, +@@ -152,24 +152,24 @@ X509_CRL *get_first_valid_crl(X509_STORE_CTX *ctx, X509 *cert, GError **err); + void store_setup_crl_download(X509_STORE *st); + EVP_PKEY *read_ec_pubkey_cert(X509 *cert, gint nid, GError **err); + +-Buffer *compute_exchange_key(EVP_PKEY *cust, EVP_PKEY *host, GError **err); +-Buffer *generate_aes_key(guint size, GError **err); +-Buffer *generate_aes_iv(guint size, GError **err); ++PvBuffer *compute_exchange_key(EVP_PKEY *cust, EVP_PKEY *host, GError **err); ++PvBuffer *generate_aes_key(guint size, GError **err); ++PvBuffer *generate_aes_iv(guint size, GError **err); + EVP_PKEY *generate_ec_key(gint nid, GError **err); + gint generate_tweak(union tweak *tweak, uint16_t i, GError **err); + union ecdh_pub_key *evp_pkey_to_ecdh_pub_key(EVP_PKEY *key, GError **err); + EVP_MD_CTX *digest_ctx_new(const EVP_MD *md, GError **err); +-Buffer *digest_ctx_finalize(EVP_MD_CTX *ctx, GError **err); +-Buffer *sha256_buffer(const Buffer *buf, GError **err); +-int64_t gcm_encrypt(const Buffer *in, const Buffer *aad, +- const struct cipher_parms *parms, Buffer *out, +- Buffer *tag, GError **err); ++PvBuffer *digest_ctx_finalize(EVP_MD_CTX *ctx, GError **err); ++PvBuffer *sha256_buffer(const PvBuffer *buf, GError **err); ++int64_t gcm_encrypt(const PvBuffer *in, const PvBuffer *aad, ++ const struct cipher_parms *parms, PvBuffer *out, ++ PvBuffer *tag, GError **err); + gint encrypt_file(const struct cipher_parms *parms, const gchar *in_path, + const gchar *path_out, gsize *in_size, gsize *out_size, + GError **err); +-Buffer *encrypt_buf(const struct cipher_parms *parms, const Buffer *in, +- GError **err); +-G_GNUC_UNUSED Buffer *decrypt_buf(const struct cipher_parms *parms, +- const Buffer *in, GError **err); ++PvBuffer *encrypt_buf(const struct cipher_parms *parms, const PvBuffer *in, ++ GError **err); ++G_GNUC_UNUSED PvBuffer *decrypt_buf(const struct cipher_parms *parms, ++ const PvBuffer *in, GError **err); + + #endif +diff --git a/genprotimg/src/utils/file_utils.c b/genprotimg/src/utils/file_utils.c +index 1d6fc37..ba33400 100644 +--- a/genprotimg/src/utils/file_utils.c ++++ b/genprotimg/src/utils/file_utils.c +@@ -171,13 +171,13 @@ err: + return ret; + } + +-gint seek_and_write_buffer(FILE *o, const Buffer *buf, uint64_t offset, ++gint seek_and_write_buffer(FILE *o, const PvBuffer *buf, uint64_t offset, + GError **err) + { + if (file_seek(o, offset, err) < 0) + return -1; + +- if (buffer_write(buf, o, err) < 0) ++ if (pv_buffer_write(buf, o, err) < 0) + return -1; + + return 0; +diff --git a/genprotimg/src/utils/file_utils.h b/genprotimg/src/utils/file_utils.h +index 47df114..456e7ac 100644 +--- a/genprotimg/src/utils/file_utils.h ++++ b/genprotimg/src/utils/file_utils.h +@@ -26,7 +26,7 @@ gint file_write(FILE *out, const void *ptr, gsize size, gsize count, + gsize *count_written, GError **err); + gint pad_file_right(const gchar *path_out, const gchar *path_in, + gsize *size_out, guint padding, GError **err); +-gint seek_and_write_buffer(FILE *out, const Buffer *buf, uint64_t offset, ++gint seek_and_write_buffer(FILE *out, const PvBuffer *buf, uint64_t offset, + GError **err); + gint seek_and_write_file(FILE *o, const CompFile *ifile, uint64_t offset, + GError **err); +-- +2.31.1 + + +From 5997d2b96e05fd8d228bd05a8b56850822747944 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Jan=20H=C3=B6ppner?= +Date: Fri, 26 Feb 2021 16:47:23 +0100 +Subject: [PATCH 3/7] ttyrun-getty: Avoid conflicts with serial-getty@ + (#1907781) +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Starting ttyrun-getty@ will fail as it conflicts with the serial-getty@ +service. Add Conflicts= option to avoid any conflicts. + +Fixes: https://github.com/ibm-s390-linux/s390-tools/issues/105 +Suggested-by: Dan Horák +Signed-off-by: Jan Höppner +(cherry picked from commit d23558f1d1b8268f1ebcb4c05c692a89f2a433c0) +--- + systemd/ttyrun-getty@.service.in | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/systemd/ttyrun-getty@.service.in b/systemd/ttyrun-getty@.service.in +index eb4299b..37554b4 100644 +--- a/systemd/ttyrun-getty@.service.in ++++ b/systemd/ttyrun-getty@.service.in +@@ -16,7 +16,7 @@ After=dev-%i.device systemd-user-sessions.service plymouth-quit-wait.service + After=rc-local.service + Before=getty.target + IgnoreOnIsolate=yes +- ++Conflicts=serial-getty@%i.service + + [Service] + Environment=TERM=linux +-- +2.31.1 + + +From 3040c1e6c8962b1aca1291586de304ba3ee6d67f Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Dan=20Hor=C3=A1k?= +Date: Fri, 2 Jul 2021 11:46:33 +0200 +Subject: [PATCH 4/7] zdsfs: transparent dataset conversion (#1919238) + +Summary: zdsfs: transparent dataset conversion +Description: The zdsfs tool is used to access z/OS data sets from + Linux. Many text based data sets in z/OS are EBCDIC + encoded. To perform actual work in Linux those data sets + usually have to be converted to ASCII. This is currently + done by first copying the data set to Linux using zdsfs + and run a separate conversion tool afterwards. + This item aims to improve the user experience by allowing + the user to do the conversion while the data is + transferred using zdsfs. +Upstream-ID: 4b0403a9630dd64d93efcf443d2e5231458a812e +Upstream-ID: cdf716a7a92b33c6bb64e66da2a4914fc2bd80f9 +Upstream-ID: 7244785279ddcf66cf1da92f6167a824e2ee119c +Upstream-ID: 0fafbcf3bb44a49a7eea41011d60049f61063c00 +Upstream-ID: 2ece47ee1ac3d5b448680264d948f6c50a76af56 + +Problem-ID: IO1811 +--- + include/lib/libzds.h | 7 +- + libzds/libzds.c | 186 +++++++++++++++++++- + zdsfs/zdsfs.1 | 167 ++++++++++++------ + zdsfs/zdsfs.c | 399 ++++++++++++++++++++++++++++++++++++++++++- + 4 files changed, 697 insertions(+), 62 deletions(-) + +diff --git a/include/lib/libzds.h b/include/lib/libzds.h +index 9135485..2647107 100644 +--- a/include/lib/libzds.h ++++ b/include/lib/libzds.h +@@ -115,7 +115,7 @@ + #include "lib/util_base.h" + #include "lib/util_list.h" + #include "vtoc.h" +- ++#include + + + /** +@@ -834,6 +834,11 @@ void lzds_dshandle_get_errorlog(struct dshandle *dsh, struct errorlog **log); + int lzds_dshandle_set_seekbuffer(struct dshandle *dsh, + unsigned long long seek_buffer_size); + ++/** ++ * @brief Set iconv handle for codepage conversion. ++ */ ++int lzds_dshandle_set_iconv(struct dshandle *dsh, iconv_t *iconv); ++ + /** + * @brief Get the size of the data set in number of tracks (sum of all extents). + */ +diff --git a/libzds/libzds.c b/libzds/libzds.c +index a6c6912..dbf810d 100644 +--- a/libzds/libzds.c ++++ b/libzds/libzds.c +@@ -61,6 +61,9 @@ struct errorlog { + + #define BUSIDSIZE 8 + ++#define EBCDIC_SP 0x40 ++#define EBCDIC_LF 0x25 ++ + /** + * @brief An internal structure that represents an entry in the error log. + */ +@@ -307,6 +310,8 @@ struct dshandle { + struct errorlog *log; + + char *session_ref; ++ iconv_t *iconv; ++ char *convbuffer; + }; + + /** @endcond */ +@@ -2812,6 +2817,38 @@ int lzds_dshandle_set_keepRDW(struct dshandle *dsh, int keepRDW) + return 0; + } + ++/** ++ * @pre The dsh must not be open when this function is called. ++ * ++ * @param[in] dsh The dshandle we want to modify. ++ * @param[in] iconv_t The iconv handle for codepage conversion. ++ * ++ * @return 0 on success, otherwise one of the following error codes: ++ * - EBUSY The handle is already open. ++ */ ++int lzds_dshandle_set_iconv(struct dshandle *dsh, iconv_t *iconv) ++{ ++ errorlog_clear(dsh->log); ++ if (dsh->is_open) ++ return errorlog_add_message( ++ &dsh->log, NULL, EBUSY, ++ "dshandle: cannot set iconv while handle is open\n"); ++ ++ /* ++ * if conversion is enabled the returned data might in worst case ++ * be 4 times the size of the input buffer. So realloc the buffer. ++ * If for whatever very unlikely reason the converted size is still ++ * larger the conversion will fail. ++ */ ++ if (iconv) { ++ dsh->databufmax *= 4; ++ dsh->databuffer = util_realloc(dsh->databuffer, ++ dsh->databufmax); ++ } ++ dsh->iconv = iconv; ++ return 0; ++} ++ + /** + * @param[in] dsh The dshandle that we want to know the member of. + * @param[out] keepRDW Reference to a variable in which the previously +@@ -2946,6 +2983,8 @@ void lzds_dshandle_close(struct dshandle *dsh) + for (i = 0; i < MAXVOLUMESPERDS; ++i) + if (dsh->dasdhandle[i]) + lzds_dasdhandle_close(dsh->dasdhandle[i]); ++ free(dsh->convbuffer); ++ free(dsh->iconv); + dsh->is_open = 0; + } + +@@ -3280,10 +3319,130 @@ int lzds_dshandle_open(struct dshandle *dsh) + return rc; + } + } ++ if (dsh->iconv) ++ dsh->convbuffer = util_zalloc(dsh->databufmax); ++ + dsh->is_open = 1; + return 0; + } + ++/** ++ * @brief subroutine of parse_fixed_record for codepage conversion ++ * ++ * Converts the provided data from one codepage to another using iconv. ++ * Stores converted data directly in the target buffer. ++ * Adds a linebreak at the end of each record to end the line. ++ * Also remove trailing spaces. ++ * ++ * @param[in] dsh The dshandle that keeps track of the I/O operations. ++ * @param[in] rec Pointer to the record buffer. ++ * @param[in] targetdata Pointer to the data buffer. ++ * @return Number of copied data bytes on success, ++ * otherwise one of the following (negative) error codes: ++ * - -EPROTO The record is malformed. ++ */ ++static ssize_t convert_fixed_record(struct dshandle *dsh, ++ char *rec, char *targetdata) ++{ ++ struct eckd_count *ecount = (struct eckd_count *)rec; ++ size_t in_count, out_count, max_count; ++ int reclen, blocksize, reccount; ++ char *inbuf, *outbuf; ++ char *src, *target; ++ size_t rc; ++ int i; ++ ++ blocksize = ecount->dl; ++ reclen = dsh->ds->dsp[0]->f1->DS1LRECL; ++ reccount = blocksize / reclen; ++ ++ outbuf = targetdata; ++ out_count = max_count = ++ (unsigned long)dsh->databuffer + dsh->databufmax - ++ (unsigned long)targetdata; ++ in_count = 0; ++ inbuf = dsh->convbuffer; ++ ++ /* skip block header */ ++ src = (rec + sizeof(*ecount) + ecount->kl); ++ target = inbuf; ++ /* for each record aka line */ ++ for (i = 0; i < reccount; i++) { ++ /* remove trailing spaces */ ++ while (reclen && (*(src + reclen - 1) == EBCDIC_SP)) ++ reclen--; ++ /* move remaining data and add linebreak at end of record */ ++ memcpy(target, src, reclen); ++ target += reclen; ++ *target = EBCDIC_LF; ++ target++; ++ /* count how much chars remain after whitespace cleanup */ ++ in_count += reclen + 1; ++ ++ /* reset for next line */ ++ reclen = dsh->ds->dsp[0]->f1->DS1LRECL; ++ src += reclen; ++ } ++ /* convert directly into target buffer */ ++ rc = iconv(*(dsh->iconv), &inbuf, &in_count, &outbuf, &out_count); ++ if ((rc == (size_t) -1) || (in_count != 0)) ++ return -errorlog_add_message( ++ &dsh->log, NULL, EPROTO, ++ "fixed record parser: codepage conversion failed\n"); ++ /* return how much was written in the target buffer */ ++ return max_count - out_count; ++} ++ ++/** ++ * @brief subroutine of parse_variable_record for codepage conversion ++ * ++ * Converts the record data from one codepage to another using iconv. ++ * Stores converted data directly in the target buffer ++ * ++ * @param[in] dsh The dshandle that keeps track of the I/O operations. ++ * @param[in] reclen Length of the record. ++ * @param[in] rec Pointer to the record buffer. ++ * @param[in] targetdata Pointer to the data buffer. ++ * @return Number of copied data bytes on success, ++ * otherwise one of the following (negative) error codes: ++ * - -EPROTO The record is malformed. ++ */ ++static ssize_t convert_variable_record(struct dshandle *dsh, int reclen, ++ char *rec, char *targetdata) ++{ ++ size_t in_count, out_count, max_count; ++ char *inbuf, *outbuf; ++ size_t rc; ++ ++ inbuf = rec; ++ outbuf = targetdata; ++ in_count = reclen + 1; ++ out_count = max_count = ++ (unsigned long)dsh->databuffer + dsh->databufmax - ++ (unsigned long)targetdata; ++ /* ++ * we can not overwrite the track end marker since it is still used ++ * for this case we have to make a copy of the source data to add the ++ * linebreak ++ */ ++ if (inbuf[reclen] == 0xFF) { ++ inbuf = dsh->convbuffer; ++ memcpy(inbuf, rec, reclen); ++ } ++ ++ /* add linebreak */ ++ inbuf[reclen] = 0x25; ++ ++ rc = iconv(*(dsh->iconv), &inbuf, &in_count, &outbuf, &out_count); ++ if ((rc == (size_t) -1) || (in_count != 0)) ++ return -errorlog_add_message( ++ &dsh->log, NULL, EPROTO, ++ "variable record parser: codepage conversion failed\n"); ++ ++ /* return how much was written in the target buffer */ ++ return max_count - out_count; ++} ++ + /** + * @brief subroutine of dshandle_extract_data_from_trackbuffer + * +@@ -3298,6 +3457,7 @@ static ssize_t parse_fixed_record(struct dshandle *dsh, + char *rec, char *targetdata) + { + struct eckd_count *ecount; ++ int count; + + ecount = (struct eckd_count *)rec; + /* Make sure that we do not copy data beyond the end of +@@ -3308,8 +3468,13 @@ static ssize_t parse_fixed_record(struct dshandle *dsh, + return - errorlog_add_message( + &dsh->log, NULL, EPROTO, + "fixed record to long for target buffer\n"); +- memcpy(targetdata, (rec + sizeof(*ecount) + ecount->kl), ecount->dl); +- return ecount->dl; ++ if (dsh->iconv) { ++ count = convert_fixed_record(dsh, rec, targetdata); ++ } else { ++ memcpy(targetdata, (rec + sizeof(*ecount) + ecount->kl), ecount->dl); ++ count = ecount->dl; ++ } ++ return count; + } + + /** +@@ -3333,6 +3498,7 @@ static ssize_t parse_variable_record(struct dshandle *dsh, char *rec, + struct segment_header *blockhead; + struct segment_header *seghead; + size_t totaldatalength; ++ int count; + + /* We must not rely on the data in rec, as it was read from disk and + * may be broken. Wherever we interprete the data we must have sanity +@@ -3398,9 +3564,19 @@ static ssize_t parse_variable_record(struct dshandle *dsh, char *rec, + &dsh->log, NULL, EPROTO, + "variable record parser: " + "record to long for target buffer\n"); +- memcpy(targetdata, data, segmentlength); +- targetdata += segmentlength; +- totaldatalength += segmentlength; ++ if (dsh->iconv) { ++ count = convert_variable_record(dsh, segmentlength, ++ data, targetdata); ++ if (count < 0) ++ return count; ++ ++ totaldatalength += count; ++ targetdata += count; ++ } else { ++ memcpy(targetdata, data, segmentlength); ++ totaldatalength += segmentlength; ++ targetdata += segmentlength; ++ } + data += segmentlength; + } + return totaldatalength; +diff --git a/zdsfs/zdsfs.1 b/zdsfs/zdsfs.1 +index 677bea5..21acb70 100644 +--- a/zdsfs/zdsfs.1 ++++ b/zdsfs/zdsfs.1 +@@ -32,29 +32,23 @@ Only read access is supported. + + Data sets on tape devices are not supported. + +-To maintain data consistency, a DASD must not be modified while it is +-in use by zdsfs. This can be assured by varying the device offline +-in z/OS before setting it online in Linux or by using z/OSMF REST +-services to notify z/OS about access to data sets. +- +-Device access by Linux is not subject to RACF or other +-z/OS auditing mechanisms unless the z/OSMF REST services are used for +-a coordinated read access. +-The safety of the data on the device must be established by +-therespective Linux mechanisms. By default, zdsfs grants access to the +-files in the fuse file system only to the user who started the +-tool. This behavior can be changed by using the options `allow_other', +-`default_permissions', `umask', `uid', and `gid'. +- +-When using the z/OSMF REST services for coordinated read access, a +-connection is established for every opened file. The z/OSMF REST +-services confirm that the z/OS userid that is specified in the .netrc +-configuration file has the required access rights for the data set. +-Using this mechanism also an exclusive ENQ is obtained to mark the +-data set as in use to z/OS. The ENQ prevents z/OS applications from +-modifying the data set during zdsfs access. +-If the ENQ cannot be obtained, the access from Linux fails with an +-error. ++If the z/OSMF REST services are not used for a coordinated-read ++access, access to the device by Linux is not subject to RACF or any ++other z/OS auditing mechanism. Ensure the security of the data on the ++device by using the well-known Linux methods. By default, zdsfs grants ++access to the files in the fuse file system only to the user who ++started the tool. Configure file access behavior by using the ++‘allow_other’, ‘default_permissions’, ‘umask’, ‘uid’, and ‘gid’ ++options. ++ ++When using the z/OSMF REST services for coordinated read access, every ++file open establishes a connection to the z/OSMF REST server. This ++checks that the accessing z/OS user ID has the appropriate access ++rights for the data set. Using this mechanism, an exclusive ENQ is ++obtained that marks the data set as in-use to z/OS. An ENQ is a z/OS ++method to control a serially reusable resource. While the ENQ is held, ++no z/OS application can modify the data set. If the ENQ cannot be ++obtained, the access attempt from Linux fails. + + Only physical sequential (PS) and partitioned data sets (PDS) are + supported. Supported record formats are: V, F, U, B, S, A, and M. +@@ -171,20 +165,50 @@ instance. + + .TP + \fB\-c\fR \fI\fR +-zdsfs configuration file. The default is /etc/zdsfs.conf. ++Provide a configuration file for zdsfs. The default is /etc/zdsfs.conf. + + .TP + \fB\-o\fR restapi +-Make zdsfs use z/OSMF REST services for coordinated read access to +-data sets. The user credentials are taken from .netrc file in the +-user's home directory or where the NETRC environment variable points ++Use z/OSMF REST services for coordinated read-access to data sets. The ++user credentials are read from the .netrc file in the user's home ++directory, or from the location the NETRC environment variable points + to. + + .TP + \fB\-o\fR restserver= +-Specify up to 3 server URLs to z/OSMF REST services. +-For multiple specifications, the URLs are tried sequentially, and the +-first functioning URL is used. ++Specify up to three server URLs to z/OSMF REST services. If more than ++one server is specified, the first that responds is used. If a server ++does not respond any longer during operation all specified server are probed again. ++ ++.TP ++\fB\-x\fR \fI\fR ++Use a config file with code-page conversion options for ++specific data sets. ++ ++.TP ++\fB\-o\fR codepage_convert ++Enable code-page conversion for all data sets. Unless specified ++otherwise, the source code-page is EBCDIC CP1047 and the target ++code-page is UTF-8. ++ ++You can change the default for the source code-page with the -o ++codepage_from option and for the target code-page with the -o ++codepage_to option. To specify source and target code-pages for ++individual data sets use a data set config file. ++ ++.TP ++\fB\-o\fR codepage_from=\fI\fR ++Override EBCDIC CP1047 as the default code-page for the source. Must ++be combined with \fB\-o\fR codepage_to=\fI\fR. Overrides settings ++in a data set config file. Issue iconv -l for a list of valid ++specifications for . ++ ++.TP ++\fB\-o\fR codepage_to=\fI\fR ++Override UTF-8 as the default code-page for the target. Must be ++combined with \fB\-o\fR codepage_from=\fI\fR. Overrides settings ++in a data set configu file. Issue iconv -l for a list of valid ++specifications for . + + .SS "Applicable FUSE options (version 2.8):" + This is a selected subset of all FUSE options. Use the zdsfs +@@ -273,44 +297,81 @@ directory: + + \fBuser.dsorg\fR: The data set organization of a file. + +-.SH zdsfs configuration file +- +-The default search path is /etc/zdsfs.conf. +-Use the \fB\-c\fR \fI\fR option to specify other zdsfs +-configuration file locations. ++.SH zdsfs config file + ++The default path is /etc/zdsfs.conf. Specify a different zdsfs config ++file location with the \fB\-c\fR \fI\fR option. + .br +- +-The configuration file can contain the following options: ++The config file may contain the following options: + .PP + + .B restapi + = + .IR 0 / 1 + .IP +-Setting this option to 1 enables the z/OSMF REST services. +-The z/OSMF REST services require a valid URL specification for a REST +-server, and a .netrc file with a valid z/OS user ID and password. ++Determines whether the z/OSMF REST services should be used. If ++enabled, a valid REST server must be specified, as well as a .netrc ++file with a valid z/OS user ID and password. + .PP + + .B restserver + = + .IR URL + .IP +-Specifies the URL of the z/OSMF REST server that is +-used for coordinated read access. For failover, up to 3 different +-server addresses can be provided. ++Specifies the address of the z/OSMF REST server that is used for ++coordinating read-access. For failover scenarios, provide up to three ++different server addresses. These will be tried in the specified order ++when one of the servers cannot be reached during mount or operation. + .PP + + .B keepalive + = +-.I timeout +-(in seconds) ++.I timeout (in seconds) ++.IP ++Optionally change the keepalive timer for ENQs. By default the ++keepalive refreshes the access after 540 seconds (9 minutes).The 9 ++minutes are chosen to prevent a timeout by z/OS after 10 minutes. ++ ++.SH data-set config file ++Provides code-page conversion settings for individual data sets. ++The default config file is /etc/zdsfs-dataset.conf. Use the \fB\-x\fR ++\fI\fR option to specify a different file. ++.br ++Each config-file entry must contain the following options: ++.PP ++ ++.B ++DATASET.TITLE ++.IP ++Specifies the data-set title or a pattern of titles to which the entry ++applies. The title can have a trailing asterisk to match all titles ++that begin with the leading characters. ++.PP ++ ++.B conv ++= ++.IR 0 / 1 / , ++.IP ++\fI0\fR disables code-page conversion. ++.br ++\fI1\fR performs conversion with the default conversion table. ++.br ++An explicit specification of source and target code-page overrides the default conversion tables ++The code-page specifications must be separated by a comma. ++Issue iconv -l for a list of valid code-page specifications. ++ ++.PP ++ ++.B rdw ++= ++.I 0 / 1 + .IP +-Specifies the keepalive timer for ENQs. +-By default the timer is set to 540 seconds to prevent the ENQ from a +-timeout after 10 minutes in case access to the data set takes longer +-than this. ++\fI0\fR omits the record descriptor word from the data stream. ++.br ++\fI1\fR keeps the record descriptor word from the data stream. ++.br ++Code-page conversion can render data unreadable if the record descriptor word is kept. ++ + + .SH EXAMPLES + To mount the z/OS disk with the name dasde enter: +@@ -341,9 +402,15 @@ assuming the z/OS disk was mounted on /mnt: + + To mount the z/OS disk using the z/OSMF REST services for coordinated + read access: ++ ++ # ./zdsfs -o restapi -o restserver=zos1.server.tld/zosmf /dev/dasde /mnt/ ++ + .br ++To mount the z/OS disk and enable code-page conversion for all data ++sets using a custom source and target code page: ++ ++ # ./zdsfs -o codepage_from=CP037 -o codepage_to=ISO-8859-1 /dev/dasde /mnt/ + +- # ./zdsfs -o restapi -o restserver=zos1.server.tld/zosmf /dev/dasde /mnt/ + + .SH SEE ALSO + getfattr(1), fuse(8), z/OS DFSMS Using Data Sets, +diff --git a/zdsfs/zdsfs.c b/zdsfs/zdsfs.c +index 87c1bea..ceadd34 100644 +--- a/zdsfs/zdsfs.c ++++ b/zdsfs/zdsfs.c +@@ -25,6 +25,8 @@ + #include + #include + ++#include ++ + #ifdef HAVE_SETXATTR + #include + #endif +@@ -34,6 +36,7 @@ + #include "lib/zt_common.h" + + #define COMP "zdsfs: " ++ + #define METADATAFILE "metadata.txt" + + /* defaults for file and directory permissions (octal) */ +@@ -42,6 +45,10 @@ + /* default timer interval 9 minutes, enq times out after 10 minutes */ + #define DEFAULT_KEEPALIVE_SEC 540 + ++#define SECTION_ENTRIES 3 ++static char CODEPAGE_EDF[] = "CP1047"; ++static char CODEPAGE_LINUX[] = "UTF-8"; ++ + struct zdsfs_info { + int devcount; + int allow_inclomplete_multi_volume; +@@ -61,6 +68,12 @@ struct zdsfs_info { + int active_server; + char *server[MAX_SERVER]; + long keepalive; ++ int codepage_convert; ++ char *codepage_from; ++ char *codepage_to; ++ iconv_t iconv; ++ struct util_list *dsclist; ++ char *dsfile; + }; + + static struct zdsfs_info zdsfsinfo; +@@ -82,6 +95,18 @@ struct zdsfs_file_info { + size_t metaread; /* how many bytes have already been read */ + }; + ++struct dsconvert { ++ char *name; ++ char *codepage_from; ++ char *codepage_to; ++ bool keeprdw; ++}; ++ ++struct dsc_node { ++ struct util_list_node node; ++ struct dsconvert *dsc; ++}; ++ + /* Allocate and initialize a new list of struct dsh_node. */ + static struct util_list *dshlist_alloc(void) + { +@@ -144,6 +169,65 @@ void dshlist_remove(struct util_list *list, struct dshandle *dsh) + } + } + ++/* Allocate and initialize a new list of struct dsc_node. */ ++static struct util_list *dsclist_alloc(void) ++{ ++ struct util_list *list; ++ ++ list = util_malloc(sizeof(struct util_list)); ++ util_list_init(list, struct dsc_node, node); ++ ++ return list; ++} ++ ++/* free struct dsconvert. */ ++static void dsc_free(struct dsconvert *dsc) ++{ ++ free(dsc->name); ++ free(dsc->codepage_from); ++ free(dsc->codepage_to); ++ free(dsc); ++} ++ ++/* free list of struct dsc_node. */ ++static void dsclist_free(struct util_list *list) ++{ ++ struct dsc_node *s, *n; ++ ++ if (!list) ++ return; ++ ++ util_list_iterate_safe(list, s, n) { ++ util_list_remove(list, s); ++ dsc_free(s->dsc); ++ free(s); ++ } ++ ++ free(list); ++} ++ ++/* add dsc to list */ ++static void dsclist_add(struct util_list *list, struct dsconvert *dsc) ++{ ++ struct dsc_node *s; ++ ++ s = util_malloc(sizeof(struct dsc_node)); ++ s->dsc = dsc; ++ util_list_add_tail(list, s); ++} ++ ++/* Find a dsc_node by name. */ ++static struct dsc_node *dsclist_find_by_name(struct util_list *list, char *name) ++{ ++ struct dsc_node *s; ++ ++ util_list_iterate(list, s) { ++ if (strcmp(s->dsc->name, name) == 0) ++ return s; ++ } ++ ++ return NULL; ++} + + /* normalize the given path name to a dataset name + * so that we can compare it to the names in the vtoc. This means: +@@ -176,6 +260,16 @@ static void path_to_member_name(const char *path, char *normds, size_t size) + } + } + ++static int setup_iconv(iconv_t *conv, const char *from, const char *to) ++{ ++ *conv = iconv_open(to, from); ++ if (*conv == ((iconv_t) -1)) { ++ fprintf(stderr, "error when setting up iconv\n"); ++ return -1; ++ } ++ return 0; ++} ++ + static void setup_timer(long sec) + { + static struct itimerval timer; +@@ -531,6 +625,102 @@ static int zdsfs_test_restserver(void) + return 0; + } + ++/* ++ * check if a dsconvert entry exists that match the given DS name ++ * if the dsconvert entry ends in an asterisk only match the prefix ++ * otherwise look for an exact match ++ */ ++static struct dsconvert *zdsfs_get_matching_dsc(char *name) ++{ ++ struct dsconvert *dsc; ++ unsigned int length; ++ struct dsc_node *n; ++ char *match; ++ ++ util_list_iterate(zdsfsinfo.dsclist, n) { ++ dsc = n->dsc; ++ match = dsc->name; ++ length = strlen(match); ++ if (strcmp(&match[length - 1], "*") == 0) ++ length--; ++ else if (strlen(name) != length) ++ continue; ++ ++ if (strncmp(name, match, length) == 0) ++ return dsc; ++ } ++ return NULL; ++} ++ ++/* ++ * Setup iconv conversion for a given dataset. ++ * The codepage settings can be obtained from (in descending priority) ++ * - globally set codepage settings ++ * - a dsconvert entry matching the DS name ++ * - default codepage settings. ++ */ ++static int zdsfs_setup_conversion(struct dshandle *dsh, struct dataset *ds) ++{ ++ struct dsconvert *dsc; ++ const char *from, *to; ++ struct errorlog *log; ++ iconv_t *iconv; ++ char *dsname; ++ int rc; ++ ++ from = to = NULL; ++ lzds_dataset_get_name(ds, &dsname); ++ dsc = zdsfs_get_matching_dsc(dsname); ++ /* the DS matches a dsconvert entry */ ++ if (dsc) { ++ from = dsc->codepage_from; ++ to = dsc->codepage_to; ++ } ++ ++ /* globally set conversion overwriting possible config file settings */ ++ if (zdsfsinfo.codepage_from && zdsfsinfo.codepage_to) { ++ from = zdsfsinfo.codepage_from; ++ to = zdsfsinfo.codepage_to; ++ } ++ ++ /* ++ * globally set conversion using defaults ++ * if not specified otherwise already ++ */ ++ if (zdsfsinfo.codepage_convert) { ++ if (!from) ++ from = CODEPAGE_EDF; ++ if (!to) ++ to = CODEPAGE_LINUX; ++ } ++ ++ /* no conversion */ ++ if (!from || !to) ++ return 0; ++ ++ iconv = util_malloc(sizeof(*iconv)); ++ rc = setup_iconv(iconv, from, to); ++ if (rc) { ++ fprintf(stderr, "Error when preparing iconv setting:\n"); ++ lzds_dshandle_get_errorlog(dsh, &log); ++ lzds_errorlog_fprint(log, stderr); ++ rc = -rc; ++ goto out; ++ } ++ rc = lzds_dshandle_set_iconv(dsh, iconv); ++ if (rc) { ++ fprintf(stderr, "Error when setting iconv handle:\n"); ++ lzds_dshandle_get_errorlog(dsh, &log); ++ lzds_errorlog_fprint(log, stderr); ++ rc = -rc; ++ goto out; ++ } ++ return 0; ++out: ++ free(iconv); ++ return rc; ++} ++ + + static int zdsfs_open(const char *path, struct fuse_file_info *fi) + { +@@ -620,6 +810,7 @@ static int zdsfs_open(const char *path, struct fuse_file_info *fi) + rc = -rc; + goto error2; + } ++ zdsfs_setup_conversion(dsh, ds); + + retry: + if (zdsfsinfo.restapi && zdsfsinfo.active_server >= 0) { +@@ -660,7 +851,6 @@ error2: + error1: + free(zfi); + return rc; +- + } + + static int zdsfs_release(const char *UNUSED(path), struct fuse_file_info *fi) +@@ -1012,7 +1202,10 @@ enum { + KEY_TRACKS, + KEY_SEEKBUFFER, + KEY_CONFIG, ++ KEY_DSCONFIG, + KEY_SERVER, ++ KEY_CODE_FROM, ++ KEY_CODE_TO, + }; + + #define ZDSFS_OPT(t, p, v) { t, offsetof(struct zdsfs_info, p), v } +@@ -1026,11 +1219,15 @@ static const struct fuse_opt zdsfs_opts[] = { + FUSE_OPT_KEY("tracks=", KEY_TRACKS), + FUSE_OPT_KEY("seekbuffer=", KEY_SEEKBUFFER), + FUSE_OPT_KEY("-c %s", KEY_CONFIG), ++ FUSE_OPT_KEY("-x %s", KEY_DSCONFIG), + FUSE_OPT_KEY("restserver=", KEY_SERVER), ++ FUSE_OPT_KEY("codepage_from=", KEY_CODE_FROM), ++ FUSE_OPT_KEY("codepage_to=", KEY_CODE_TO), + ZDSFS_OPT("rdw", keepRDW, 1), + ZDSFS_OPT("ignore_incomplete", allow_inclomplete_multi_volume, 1), + ZDSFS_OPT("check_host_count", host_count, 1), + ZDSFS_OPT("restapi", restapi, 1), ++ ZDSFS_OPT("codepage_convert", codepage_convert, 1), + FUSE_OPT_END + }; + +@@ -1052,6 +1249,8 @@ static void usage(const char *progname) + " nodes\n" + " -c config_file Text file that contains configuration options\n" + " for zdsfs\n" ++" -x ds_config_file Text file that contains conversion options\n" ++" for specific datasets\n" + " -o rdw Keep record descriptor words in byte stream\n" + " -o ignore_incomplete Continue processing even if parts of a multi" + " volume\n" +@@ -1065,6 +1264,10 @@ static void usage(const char *progname) + " access to datasets\n" + " -o restserver=URL The URL of the z/OSMF REST server to be used for\n" + " coordinated access to datasets\n" ++" -o codepage_convert Enable codepage conversion using default codepages\n" ++" from 'CP1047' to 'UTF-8'\n" ++" -o codepage_from=from Set codepage for source. See 'iconv -l' for a list\n" ++" -o codepage_to=to Set codepage for target. See 'iconv -l' for a list\n" + , progname); + } + +@@ -1167,7 +1370,7 @@ static void zdsfs_process_device_file(const char *devfile) + void remove_whitespace(const char *s, char *t) + { + while (*s != '\0') { +- if (!isspace(*s)) { ++ if (!isblank(*s)) { + *t = *s; + t++; + } +@@ -1176,7 +1379,6 @@ void remove_whitespace(const char *s, char *t) + *t = '\0'; + } + +- + static void zdsfs_process_config_file(const char *config) + { + char line[MAX_LINE_LENGTH]; +@@ -1198,7 +1400,7 @@ static void zdsfs_process_config_file(const char *config) + continue; + + /* remove all whitespaces */ +- tmp = util_malloc(strlen(line)); ++ tmp = util_malloc(strlen(line) + 1); + remove_whitespace(line, tmp); + + key = strtok(tmp, delimiter); +@@ -1225,6 +1427,163 @@ static void zdsfs_process_config_file(const char *config) + fclose(fd); + } + ++static struct dsconvert *zdsfs_allocate_dsc(char *name, const char *config) ++{ ++ struct dsconvert *dsc; ++ ++ /* check for duplicate entries */ ++ if (dsclist_find_by_name(zdsfsinfo.dsclist, name)) { ++ fprintf(stderr, ++ "Error in config file %s. Duplicate entry found: %s\n", ++ config, name); ++ return NULL; ++ } ++ dsc = util_zalloc(sizeof(*dsc)); ++ dsc->name = util_strdup(name); ++ ++ return dsc; ++} ++ ++static int zdsfs_check_codepage_setting(char *from, char *to) ++{ ++ iconv_t iconv; ++ ++ /* no conversion is OK */ ++ if (!from && !to) ++ return 0; ++ ++ /* partial setup is not OK */ ++ if ((from && !to) || (to && !from)) ++ return 1; ++ ++ /* return if the codepages are valid */ ++ return setup_iconv(&iconv, from, to); ++} ++ ++/* ++ * process a dataset configuration file that specifies conversion on a per dataset basis ++ * ++ * expect a section title each 3 lines ++ * the section should contain a rdw= and conv= line ++ * valid values for rdw= are 0/1 ++ * valid values for conv= are 0/1 or a comma separated list ++ * of codepage_from and codepage_to arguments ++ */ ++static int zdsfs_process_dataset_conf(const char *config) ++{ ++ char line[MAX_LINE_LENGTH]; ++ char delimiter[] = " =#\n"; ++ char *tmp, *key, *value; ++ int linecount, in_section; ++ unsigned long enabled; ++ struct dsconvert *dsc; ++ int rc = 1; ++ FILE *fd; ++ ++ fd = fopen(config, "r"); ++ if (!fd) { ++ fprintf(stderr, "could not open file %s: %s\n", ++ config, strerror(errno)); ++ return 0; ++ } ++ in_section = 0; ++ linecount = 0; ++ dsc = NULL; ++ tmp = NULL; ++ while (fgets(line, sizeof(line), fd)) { ++ linecount++; ++ /* remove all whitespaces */ ++ tmp = util_malloc(strlen(line) + 1); ++ remove_whitespace(line, tmp); ++ /* skip empty lines */ ++ if (*tmp == '\n' || *tmp == '#') { ++ free(tmp); ++ tmp = NULL; ++ continue; ++ } ++ if (!in_section) { ++ /* the section title should not contain a '=' */ ++ if (strchr(line, '=') != NULL) { ++ fprintf(stderr, ++ "Error in config file %s line %d. Expected section title instead of %s\n", ++ config, linecount, line); ++ goto out; ++ } ++ } ++ key = strtok(tmp, delimiter); ++ if (!in_section) { ++ dsc = zdsfs_allocate_dsc(key, config); ++ if (!dsc) ++ goto out; ++ in_section = SECTION_ENTRIES; ++ } else if (strcmp(key, "rdw") == 0) { ++ value = strtok(NULL, delimiter); ++ enabled = strtoul(value, NULL, 0); ++ if (enabled == 1) ++ dsc->keeprdw = true; ++ else ++ dsc->keeprdw = false; ++ } else if (strcmp(key, "conv") == 0) { ++ value = strtok(NULL, delimiter); ++ if (strchr(value, ',') != NULL) { ++ /* use provided codepages */ ++ value = strtok(value, ","); ++ dsc->codepage_from = util_strdup(value); ++ value = strtok(NULL, delimiter); ++ dsc->codepage_to = util_strdup(value); ++ } else if (strcmp(value, "1") == 0) { ++ /* use default codepages */ ++ dsc->codepage_from = CODEPAGE_EDF; ++ dsc->codepage_to = CODEPAGE_LINUX; ++ } else if (strcmp(value, "0") == 0) { ++ /* disable conversion */ ++ dsc->codepage_from = NULL; ++ dsc->codepage_to = NULL; ++ } else { ++ fprintf(stderr, ++ "Error in config file %s line %d. Invalid 'conv' statement: %s.\n", ++ config, linecount, value); ++ goto out; ++ } ++ } else { ++ fprintf(stderr, ++ "Error in config file %s line %d. Missing 'rdw' or 'conv' statement.\n", ++ config, linecount); ++ goto out; ++ } ++ in_section--; ++ /* if the section was parsed completely, add the dsc to the list */ ++ if (!in_section) { ++ if (zdsfs_check_codepage_setting(dsc->codepage_from, ++ dsc->codepage_to)) { ++ fprintf(stderr, ++ "Error in config file %s. Invalid codepage setting: %s %s.\n", ++ config, dsc->codepage_from, ++ dsc->codepage_to); ++ goto out; ++ } ++ dsclist_add(zdsfsinfo.dsclist, dsc); ++ dsc = NULL; ++ } ++ free(tmp); ++ tmp = NULL; ++ } ++ /* find incomplete last section */ ++ if (in_section) ++ fprintf(stderr, ++ "Error in config file %s. Missing 'rdw' or 'conv' statement.\n", ++ config); ++ else ++ rc = 0; ++ ++out: ++ fclose(fd); ++ free(tmp); ++ dsc_free(dsc); ++ ++ return rc; ++} ++ + static int zdsfs_process_args(void *UNUSED(data), const char *arg, int key, + struct fuse_args *outargs) + { +@@ -1313,6 +1672,10 @@ static int zdsfs_process_args(void *UNUSED(data), const char *arg, int key, + /* note that arg starts with "-c" */ + zdsfsinfo.configfile = util_strdup(arg + 2); + return 0; ++ case KEY_DSCONFIG: ++ /* note that arg starts with "-x" */ ++ zdsfsinfo.dsfile = util_strdup(arg + 2); ++ return 0; + case KEY_SERVER: + if (zdsfsinfo.nr_server >= MAX_SERVER) + return 0; +@@ -1321,6 +1684,16 @@ static int zdsfs_process_args(void *UNUSED(data), const char *arg, int key, + util_strdup(value); + zdsfsinfo.nr_server++; + return 0; ++ case KEY_CODE_FROM: ++ value = arg + strlen("codepage_from="); ++ zdsfsinfo.codepage_from = ++ util_strdup(value); ++ return 0; ++ case KEY_CODE_TO: ++ value = arg + strlen("codepage_to="); ++ zdsfsinfo.codepage_to = ++ util_strdup(value); ++ return 0; + default: + fprintf(stderr, "Unknown argument key %x\n", key); + exit(1); +@@ -1340,11 +1713,14 @@ int main(int argc, char *argv[]) + zdsfsinfo.tracks_per_frame = 128; + zdsfsinfo.seek_buffer_size = 1048576; + zdsfsinfo.configfile = "/etc/zdsfs.conf"; ++ zdsfsinfo.dsfile = "/etc/zdsfs-dataset.conf"; + zdsfsinfo.keepalive = DEFAULT_KEEPALIVE_SEC; + zdsfsinfo.active_server = -1; + + rc = lzds_zdsroot_alloc(&zdsfsinfo.zdsroot); + open_dsh = dshlist_alloc(); ++ zdsfsinfo.dsclist = dsclist_alloc(); ++ + if (rc) { + fprintf(stderr, "Could not allocate internal structures\n"); + exit(1); +@@ -1354,8 +1730,19 @@ int main(int argc, char *argv[]) + fprintf(stderr, "Failed to parse option\n"); + exit(1); + } ++ if (zdsfs_check_codepage_setting(zdsfsinfo.codepage_from, ++ zdsfsinfo.codepage_to)) { ++ fprintf(stderr, "Ivalid codepage setting from '%s' to '%s'\n", ++ zdsfsinfo.codepage_from, ++ zdsfsinfo.codepage_to); ++ rc = -EINVAL; ++ goto cleanup; ++ } + zdsfs_process_config_file(zdsfsinfo.configfile); +- ++ if (zdsfs_process_dataset_conf(zdsfsinfo.dsfile)) { ++ rc = -EACCES; ++ goto cleanup; ++ } + if (!zdsfsinfo.devcount) { + fprintf(stderr, "Please specify a block device\n"); + fprintf(stderr, "Try '%s --help' for more information\n", +@@ -1390,12 +1777,12 @@ int main(int argc, char *argv[]) + goto cleanup; + } + } +- + rc = fuse_main(args.argc, args.argv, &rdf_oper, NULL); + + cleanup: + curl_global_cleanup(); + dshlist_free(open_dsh); ++ dsclist_free(zdsfsinfo.dsclist); + lzds_zdsroot_free(zdsfsinfo.zdsroot); + + fuse_opt_free_args(&args); +-- +2.31.1 + + +From 994e280ae9292821f77d49138f0f17827965e5a6 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Dan=20Hor=C3=A1k?= +Date: Fri, 2 Jul 2021 11:49:28 +0200 +Subject: [PATCH 5/7] dasd: change default scheduler to reduce CPU consumption + (#1972038) + +Description: dasd: change default scheduler to reduce CPU consumption +Symptom: CPU consumption up to 20% higher for mq-deadline + compared to none scheduler for DASD devices with no + difference in throughput. +Problem: Performance analysis showed that with recent DASD + device drivers using multi-queue block queuing the + throughput of mq-deadline and none scheduler is nearly + identical but the CPU consumption of mq-deadline + scheduler due to its optimizations is up to 20% higher + compared to none scheduler. +Solution: Set none scheduler as default in the DASD udev rule. +Reproduction: Use DASD devices with mq-deadline scheduler. +Upstream-ID: a65bc51cf4e5c1fe628bb182cc1a02ee83eb102d +Problem-ID: 192049 +--- + etc/udev/rules.d/59-dasd.rules | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +diff --git a/etc/udev/rules.d/59-dasd.rules b/etc/udev/rules.d/59-dasd.rules +index 98fbd18..06c1bf2 100644 +--- a/etc/udev/rules.d/59-dasd.rules ++++ b/etc/udev/rules.d/59-dasd.rules +@@ -26,10 +26,10 @@ KERNEL=="dasd*[0-9]", ENV{ID_XUID}=="?*", SYMLINK+="disk/by-id/$env{ID_BUS}-$env + + LABEL="dasd_symlinks_end" + +-# on device add set request queue scheduler to deadline ++# on device add set request queue scheduler to none + SUBSYSTEM!="block", GOTO="sched_end" + + ACTION!="change", GOTO="sched_end" +-KERNEL=="dasd*[!0-9]", TEST=="queue/scheduler", ATTR{queue/scheduler}="deadline" ++KERNEL=="dasd*[!0-9]", TEST=="queue/scheduler", ATTR{queue/scheduler}="none" + + LABEL="sched_end" +-- +2.31.1 + + +From 6141192f0487ce450106dfc32a8d6f3d7a89908b Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Dan=20Hor=C3=A1k?= +Date: Fri, 2 Jul 2021 11:50:35 +0200 +Subject: [PATCH 6/7] dbginfo.sh: Collect /proc/kallsyms, issue additional + commands (#1972041) + +Description: dbginfo.sh: Collect /proc/kallsyms, issue additional commands +Symptom: - Required data gets not collected during run of dbginfo.sh + - vmcp q cache command always fails +Problem: - some commands yet missing in dbginfo.sh script + - command vmcp q cache is missing a mandatory parameter +Solution: Stop to issue a vmcp q cache command (fails always) and add + - collect /proc/kallsyms as a tgz file (because of huge size) + - lscpu -ye + - vmcp q memassist + - vmcp q pcifunction + - vmcp q vmrelocate +Reproduction: Run the dbginfo.sh and check for results in DBGINFOxxx.tgz file + - /proc/kallsyms + - zvm_runtime.out + - runtime.out +Upstream-ID: eb1fd47a85c93ec247d89a4d02b0a5a2c5a8d444 +Problem-ID: 192022 +--- + scripts/dbginfo.sh | 32 +++++++++++++++++++++++++++----- + scripts/dbginfo.sh.1 | 8 ++++---- + 2 files changed, 31 insertions(+), 9 deletions(-) + +diff --git a/scripts/dbginfo.sh b/scripts/dbginfo.sh +index 705b5f0..405dcb3 100755 +--- a/scripts/dbginfo.sh ++++ b/scripts/dbginfo.sh +@@ -2,7 +2,7 @@ + # + # dbginfo.sh - Tool to collect runtime, configuration, and trace information + # +-# Copyright IBM Corp. 2002, 2020 ++# Copyright IBM Corp. 2002, 2021 + # + # s390-tools is free software; you can redistribute it and/or modify + # it under the terms of the MIT license. See LICENSE for details. +@@ -21,7 +21,7 @@ readonly SCRIPTNAME="${0##*/}" + print_version() { + cat </dev/null | while IFS= read -r file_name; do ++ tmp_file=${file_name}-`uname -r`.tgz ++ ch_dir="${WORKPATH}proc/" ++ orig_file="kallsyms" ++ ++ ++ echo " ${file_name}" ++ if ! test -e "${file_name}"; then ++ echo "${SCRIPTNAME}: Warning: Postprocessing failed on ${file_name}" ++ echo ++ fi ++ ++ tar -cvzf "${tmp_file}" -C "${ch_dir}" "${orig_file}" ++ rm -f "${file_name}" ++ ++ done + + pr_log_stdout " " + } +@@ -1124,6 +1144,8 @@ create_package() + pr_stdout "Finalizing: Creating archive with collected data" + cd "${WORKDIR_BASE}" + ++ touch "${WORKARCHIVE}" ++ chmod 0600 "${WORKARCHIVE}" + tar -czf "${WORKARCHIVE}" "${WORKDIR_CURRENT}" + rc_tar=$? + if [ $rc_tar -eq 0 ]; then +diff --git a/scripts/dbginfo.sh.1 b/scripts/dbginfo.sh.1 +index ef9fe89..a59e4c0 100644 +--- a/scripts/dbginfo.sh.1 ++++ b/scripts/dbginfo.sh.1 +@@ -2,7 +2,7 @@ + + .SH NAME + dbginfo.sh \- collect runtime, configuration and trace information +-for debugging Linux on System z ++for debugging Linux on IBM Z + + .SH SYNOPSIS + .br +@@ -12,7 +12,7 @@ for debugging Linux on System z + + .SH DESCRIPTION + This script collects runtime, configuration and trace information that can +-be used to debug a Linux on System z instance. ++be used to debug a Linux on IBM Z instance. + For Linux on z/VM, the script also traces information about the z/VM system. + The debug information is written to a file + /tmp/DBGINFO\-\-