Merge ~fheimes/ubuntu/+source/s390-tools:s390-tools-sru-lp1942908-focal into ubuntu/+source/s390-tools:ubuntu/focal-devel

Proposed by Frank Heimes
Status: Needs review
Proposed branch: ~fheimes/ubuntu/+source/s390-tools:s390-tools-sru-lp1942908-focal
Merge into: ubuntu/+source/s390-tools:ubuntu/focal-devel
Diff against target: 2556 lines (+2510/-1)
6 files modified
debian/changelog (+15/-0)
debian/control (+1/-1)
debian/patches/0001-genprotimg-add-host-key-document-verification.patch (+2379/-0)
debian/patches/0002-genprotimg-add-missing-return.patch (+42/-0)
debian/patches/0003-genprotimg-check-return-value-of-BIO_reset.patch (+70/-0)
debian/patches/series (+3/-0)
Reviewer Review Type Date Requested Status
Lukas Märdian (community) Approve
Review via email: mp+409308@code.launchpad.net

Description of the change

s390-tools-sru-lp1942908-focal
   * Fixing zKVM: Host Key Document Verification
     Fix of genprotimg allowing the tool to verify the validity of
     IBM Secure Execution host key documents.
     commit 074de1e required a backport, 7827a79 and d90344a are cherry-picks
     - 074de1e d/p/0001-genprotimg-add-host-key-document-verification.patch
     - 7827a79 d/p/0002-genprotimg-add-missing-return.patch
     - d90344a d/p/0003-genprotimg-check-return-value-of-BIO_reset.patch
     Added additional build dependency libcurl4-openssl-dev to debian/control,
     needed by d/p/0001-genprotimg-add-host-key-document-verification.patch.
     (LP: #1942908)

Test build is available here:
https://launchpad.net/~fheimes/+archive/ubuntu/lp1942908 (ppa4)
or
https://launchpad.net/~fheimes/+archive/ubuntu/lp1942908-new

To post a comment you must log in.
Revision history for this message
Lukas Märdian (slyon) wrote :

+1

review: Approve

Unmerged commits

bfd1027... by Frank Heimes

  * debian/changelog for
    d/p/0001-genprotimg-add-host-key-document-verification.patch
    d/p/0002-genprotimg-add-missing-return.patch
    d/p/0003-genprotimg-check-return-value-of-BIO_reset.patch
    d/c add libcurl4-openssl-dev build dependency req. by d/p/0001-genprotimg

cb94f7a... by Frank Heimes

  * debian/control for
    debian/patches/0001-genprotimg-add-host-key-document-verification.patch
    requires an additional build dependency 'libcurl4-openssl-dev'

de70998... by Frank Heimes

  * Fixing zKVM: Host Key Document Verification
    Fix of genprotimg allowing the tool to verify the validity of
    IBM Secure Execution host key documents.
    commit 074de1e required a backport, 7827a79 and d90344a are cherry-picks
    - 074de1e d/p/0001-genprotimg-add-host-key-document-verification.patch
    - 7827a79 d/p/0002-genprotimg-add-missing-return.patch
    - d90344a d/p/0003-genprotimg-check-return-value-of-BIO_reset.patch
    (LP: #1942908)

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1diff --git a/debian/changelog b/debian/changelog
2index dd6539d..a19e3a4 100644
3--- a/debian/changelog
4+++ b/debian/changelog
5@@ -1,3 +1,18 @@
6+s390-tools (2.12.0-0ubuntu3.4) focal; urgency=medium
7+
8+ * Fixing zKVM: Host Key Document Verification
9+ Fix of genprotimg allowing the tool to verify the validity of
10+ IBM Secure Execution host key documents.
11+ commit 074de1e required a backport, 7827a79 and d90344a are cherry-picks
12+ - 074de1e d/p/0001-genprotimg-add-host-key-document-verification.patch
13+ - 7827a79 d/p/0002-genprotimg-add-missing-return.patch
14+ - d90344a d/p/0003-genprotimg-check-return-value-of-BIO_reset.patch
15+ Added additional build dependency libcurl4-openssl-dev to debian/control,
16+ needed by d/p/0001-genprotimg-add-host-key-document-verification.patch.
17+ (LP: #1942908)
18+
19+ -- Frank Heimes <frank.heimes@canonical.com> Tue, 28 Sep 2021 16:37:28 +0200
20+
21 s390-tools (2.12.0-0ubuntu3.3) focal; urgency=medium
22
23 * debian/patches/0001-zdev-Add-build-option-to-update-initial-RAM-disk-by-default.patch
24diff --git a/debian/control b/debian/control
25index 1b1ce76..905c33b 100644
26--- a/debian/control
27+++ b/debian/control
28@@ -4,7 +4,7 @@ Priority: optional
29 Maintainer: Ubuntu Developers <ubuntu-devel-discuss@lists.ubuntu.com>
30 XSBC-Original-Maintainer: Debian S/390 Team <debian-s390@lists.debian.org>
31 Uploaders: Bastian Blank <waldi@debian.org>, Philipp Kern <pkern@debian.org>
32-Build-Depends: debhelper (>> 9), libz-dev, quilt, gcc-multilib, libfuse-dev, libncurses-dev, libpfm4-dev, libsnmp-dev, dh-systemd, libssl-dev, libcryptsetup-dev, libjson-c-dev, libglib2.0-dev
33+Build-Depends: debhelper (>> 9), libz-dev, quilt, gcc-multilib, libfuse-dev, libncurses-dev, libpfm4-dev, libsnmp-dev, dh-systemd, libssl-dev, libcurl4-openssl-dev, libcryptsetup-dev, libjson-c-dev, libglib2.0-dev
34 Standards-Version: 3.9.7
35 Homepage: http://www.ibm.com/developerworks/linux/linux390/s390-tools.html
36
37diff --git a/debian/patches/0001-genprotimg-add-host-key-document-verification.patch b/debian/patches/0001-genprotimg-add-host-key-document-verification.patch
38new file mode 100644
39index 0000000..f9c4d9e
40--- /dev/null
41+++ b/debian/patches/0001-genprotimg-add-host-key-document-verification.patch
42@@ -0,0 +1,2379 @@
43+genprotimg: add host-key document verification support
44+
45+Add host-key document verification support to genprotimg. This ensures
46+that a host-key document is genuine and provided by IBM. For this the
47+user must provide the IBM Z signing key, the intermediate CA
48+certificate (signed by the root CA used) so a chain of trust starting
49+from the host-key document and ending in the root CA can be
50+established.
51+
52+By default, genprotimg tries to download all revocation lists needed
53+by looking up in the corresponding certificate on how CRL information
54+can be obtained (see https://tools.ietf.org/html/rfc5280#section-4.2.1.13
55+for details).
56+
57+Acked-by: Patrick Steuer <patrick.steuer@de.ibm.com>
58+Signed-off-by: Marc Hartmayer <mhartmay@linux.ibm.com>
59+Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
60+
61+Author: Marc Hartmayer <mhartmay@linux.ibm.com>
62+Origin: upstream, https://github.com/ibm-s390-linux/s390-tools/commit/074de1e14ed785c18f55ecf9762ac3f5de3465b4
63+Bug-IBM: Bugzilla 194437
64+Bug-Ubuntu: https://bugs.launchpad.net/bugs/1942908
65+Applied-Upstream: 2.16.0
66+Reviewed-by: Frank Heimes <frank.heimes@canonical.com>
67+Last-Update: 2021-09-22
68+
69+---
70+This patch header follows DEP-3: http://dep.debian.net/deps/dep3/
71+--- a/genprotimg/man/genprotimg.8
72++++ b/genprotimg/man/genprotimg.8
73+@@ -2,7 +2,7 @@
74+ .\" s390-tools is free software; you can redistribute it and/or modify
75+ .\" it under the terms of the MIT license. See LICENSE for details.
76+ .\"
77+-.TH GENPROTIMG 8 "March 2020" "s390-tools"
78++.TH GENPROTIMG 8 "November 2020" "s390-tools"
79+ .SH NAME
80+ genprotimg \- Create a protected virtualization image
81+
82+@@ -10,6 +10,7 @@
83+ .SY
84+ .B genprotimg
85+ \fB\-k\fR \fIHOST_KEY_DOCUMENT\fR...
86++\fB\-C\fR \fICERTIFICATE\fR...
87+ \fB\-i\fR \fIVMLINUZ\fR
88+ [\fB\-r\fR \fIRAMDISK\fR]
89+ [\fB\-p\fR \fIPARMFILE\fR]
90+@@ -21,15 +22,19 @@
91+ .PP
92+ Use \fBgenprotimg\fR to generate a single bootable image file with
93+ encrypted and integrity-protected parts. The command requires a kernel
94+-image, a host-key document, and an output file name. Optionally,
95+-specify an initial RAM filesystem, and a file containing the kernel
96+-parameters. Should special circumstances require it, you can
97++image, a host-key document, certificates for the host-key document
98++verification, and an output file name. Optionally, specify an initial
99++RAM filesystem, and a file containing the kernel parameters. If the
100++command should be run offline, use the \fB\-\-offline\fR option and
101++specify the certificate revocation lists (CRLs) by using the
102++\fB\-\-crl\fR option. Should special circumstances require it, you can
103+ optionally specify your own keys for the encryption by using the
104+-experimental options. In the resulting image file, a plain text boot
105+-loader, the encrypted components for kernel, initial RAM disk, kernel
106+-parameters, and the encrypted and integrity-protected header are
107+-concatenated. The header contains metadata necessary for running the
108+-guest in protected mode.
109++experimental options. For all certificates, CRLs, and host-key
110++documents, both the PEM and DER input formats are supported. In the
111++resulting image file, a plain text boot loader, the encrypted
112++components for kernel, initial RAM disk, kernel parameters, and the
113++encrypted and integrity-protected header are concatenated. The header
114++contains metadata necessary for running the guest in protected mode.
115+ .PP
116+ Use this image file as a kernel image for zipl or for a direct kernel
117+ boot using QEMU.
118+@@ -53,6 +58,12 @@
119+ option multiple times to enable the image to run on more than one
120+ host.
121+ .TP
122++\fB\-C\fR, \fB\-\-cert\fR=\fI\,FILE\/\fR
123++Specifies the certificate that is used to establish a chain of trust
124++for the verification of the host-key documents. Specify this option
125++twice to specify the IBM Z signing key and the intermediate CA
126++certificate (signed by the root CA). Required.
127++.TP
128+ \fB\-o\fR, \fB\-\-output\fR=\fI\,OUTPUT_FILE\/\fR
129+ Specifies the output file. Required.
130+ .TP
131+@@ -65,6 +76,20 @@
132+ \fB\-p\fR, \fB\-\-parmfile\fR=\fI\,PARMFILE\/\fR
133+ Specifies the kernel command line stored in \fI\,PARMFILE\/\fR. Optional.
134+ .TP
135++\fB\-\-crl\fR=\fI\,FILE\/\fR
136++Specifies the revocation list that is used to check whether a
137++certificate of the chain of trust is revoked. Specify this option
138++multiple times to use multiple CRLs. Optional.
139++.TP
140++\fB\-\-offline\fR
141++Specifies offline mode, in which no attempt is made to download
142++CRLs. Optional.
143++.TP
144++\fB\-\-root\-ca\fR=\fI\,FILE\/\fR
145++Specifies the root CA certificate for the verification. If omitted,
146++the DigiCert root CA certificate installed on the system is used. Use
147++this only if you trust the specified certificate. Optional.
148++.TP
149+ \fB\-\-no-verify\fR
150+ Do not require the host-key documents to be valid. For testing
151+ purposes, do not use for a production image. Optional.
152+@@ -77,11 +102,13 @@
153+ Generate a protected virtualization image in
154+ \fI\,/boot/vmlinuz.pv\/\fR, using the kernel file \fI\,vmlinuz\/\fR,
155+ the initrd in \fI\,initramfs\/\fR, the kernel parameters contained in
156+-\fI\,parmfile\/\fR, and the host-key document in \fI\,host_key.crt\/\fR:
157++\fI\,parmfile\/\fR, the intermediate CA in \fI\,DigiCertCA.crt\/\fR,
158++the IBM Z signing key in \fI\,ibm-z-host-key-signing.crt\/\fR, and the
159++host-key document in \fI\,host_key.crt\/\fR:
160+ .PP
161+ .Vb 1
162+ .EX
163+-\& 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
164++\& 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
165+ .EE
166+ .Ve
167+ .PP
168+--- a/genprotimg/src/genprotimg.c
169++++ b/genprotimg/src/genprotimg.c
170+@@ -18,6 +18,8 @@
171+ #include "common.h"
172+ #include "pv/pv_args.h"
173+ #include "pv/pv_image.h"
174++#include "utils/crypto.h"
175++#include "utils/curl.h"
176+
177+ enum {
178+ LOG_LEVEL_CRITICAL = 0,
179+@@ -117,6 +119,8 @@
180+ signal(signals[i], SIG_DFL);
181+ }
182+
183++static void __attribute__((constructor)) __init(void);
184++static void __attribute__((destructor)) __cleanup(void);
185+ gint main(gint argc, gchar *argv[])
186+ {
187+ g_autoptr(PvArgs) args = pv_args_new();
188+@@ -179,3 +183,16 @@
189+ g_free(tmp_dir);
190+ exit(ret);
191+ }
192++
193++static void __init(void)
194++{
195++ pv_crypto_init();
196++ if (curl_init() != 0)
197++ g_abort();
198++}
199++
200++static void __cleanup(void)
201++{
202++ curl_cleanup();
203++ pv_crypto_cleanup();
204++}
205+--- a/genprotimg/src/include/pv_crypto_def.h
206++++ b/genprotimg/src/include/pv_crypto_def.h
207+@@ -14,6 +14,24 @@
208+
209+ #include "lib/zt_common.h"
210+
211++/* IBM signing key subject */
212++#define PV_IBM_Z_SUBJECT_COMMON_NAME "International Business Machines Corporation"
213++#define PV_IBM_Z_SUBJECT_COUNTRY_NAME "US"
214++#define PV_IBM_Z_SUBJECT_LOCALITY_NAME "Poughkeepsie"
215++#define PV_IBM_Z_SUBJECT_ORGANIZATIONONAL_UNIT_NAME_SUFFIX "Key Signing Service"
216++#define PV_IBM_Z_SUBJECT_ORGANIZATION_NAME "International Business Machines Corporation"
217++#define PV_IBM_Z_SUBJECT_STATE "New York"
218++#define PV_IMB_Z_SUBJECT_ENTRY_COUNT 6
219++
220++/* Minimum security level for the keys/certificates used to establish a chain of
221++ * trust (see https://www.openssl.org/docs/man1.1.1/man3/X509_VERIFY_PARAM_set_auth_level.html
222++ * for details).
223++ */
224++#define PV_CERTS_SECURITY_LEVEL 2
225++
226++/* SKID for DigiCert Assured ID Root CA */
227++#define DIGICERT_ASSURED_ID_ROOT_CA_SKID "45EBA2AFF492CB82312D518BA7A7219DF36DC80F"
228++
229+ union ecdh_pub_key {
230+ struct {
231+ uint8_t x[80];
232+--- a/genprotimg/src/Makefile
233++++ b/genprotimg/src/Makefile
234+@@ -23,16 +23,16 @@
235+ $(bin_PROGRAM)_SRCS := $(bin_PROGRAM).c pv/pv_stage3.c pv/pv_image.c \
236+ pv/pv_comp.c pv/pv_hdr.c pv/pv_ipib.c utils/crypto.c utils/file_utils.c \
237+ pv/pv_args.c utils/buffer.c pv/pv_comps.c pv/pv_error.c \
238+- pv/pv_opt_item.c \
239++ pv/pv_opt_item.c utils/curl.c \
240+ $(NULL)
241+ $(bin_PROGRAM)_OBJS := $($(bin_PROGRAM)_SRCS:.c=.o)
242+
243+ ALL_CFLAGS += -std=gnu11 -DPKGDATADIR=$(PKGDATADIR) \
244+- $(GLIB2_CFLAGS) $(LIBCRYPTO_CFLAGS) \
245++ $(GLIB2_CFLAGS) $(LIBCRYPTO_CFLAGS) $(LIBCURL_CFLAGS) \
246+ $(WARNINGS) \
247+ $(NULL)
248+ ALL_CPPFLAGS += $(INCLUDE_PARMS)
249+-LDLIBS += $(GLIB2_LIBS) $(LIBCRYPTO_LIBS)
250++LDLIBS += $(GLIB2_LIBS) $(LIBCRYPTO_LIBS) $(LIBCURL_LIBS)
251+
252+
253+ ifneq ($(shell sh -c 'command -v pkg-config'),)
254+@@ -40,21 +40,27 @@
255+ GLIB2_LIBS := $(shell pkg-config --silence-errors --libs glib-2.0)
256+ LIBCRYPTO_CFLAGS := $(shell pkg-config --silence-errors --cflags libcrypto)
257+ LIBCRYPTO_LIBS := $(shell pkg-config --silence-errors --libs libcrypto)
258++LIBCURL_CFLAGS := $(shell pkg-config --silence-errors --cflags libcurl)
259++LIBCURL_LIBS := $(shell pkg-config --silence-errors --libs libcurl)
260+ else
261+ GLIB2_CFLAGS := -I/usr/include/glib-2.0 -I/usr/lib64/glib-2.0/include
262+ GLIB2_LIBS := -lglib-2.0
263+ LIBCRYPTO_CFLAGS :=
264+ LIBCRYPTO_LIBS := -lcrypto
265++LIBCURL_CFLAGS :=
266++LIBCURL_LIBS := -lcurl
267+ endif
268+
269+ BUILD_TARGETS := skip-$(bin_PROGRAM)
270+ INSTALL_TARGETS := skip-$(bin_PROGRAM)
271+ ifneq (${HAVE_OPENSSL},0)
272+ ifneq (${HAVE_GLIB2},0)
273++ifneq (${HAVE_LIBCURL},0)
274+ BUILD_TARGETS := $(bin_PROGRAM)
275+ INSTALL_TARGETS := install-$(bin_PROGRAM)
276+ endif
277+ endif
278++endif
279+
280+ all: $(BUILD_TARGETS)
281+
282+@@ -98,4 +104,9 @@
283+ "openssl-devel / libssl-dev version >= 1.1.0", \
284+ "HAVE_OPENSSL=0", \
285+ "-I.")
286++ $(call check_dep, \
287++ "$(bin_PROGRAM)", \
288++ "curl/curl.h", \
289++ "libcurl-devel", \
290++ "HAVE_LIBCURL=0")
291+ touch $@
292+--- a/genprotimg/src/pv/pv_args.c
293++++ b/genprotimg/src/pv/pv_args.c
294+@@ -18,7 +18,9 @@
295+
296+ static gchar summary[] =
297+ "Use genprotimg to create a protected virtualization kernel image file,\n"
298+- "which can be loaded using zipl or QEMU.";
299++ "which can be loaded using zipl or QEMU. For all certificates, revocation\n"
300++ "lists, and host-key documents, both the PEM and DER input formats are\n"
301++ "supported.";
302+
303+ static gint pv_arg_compare(gconstpointer arg_1, gconstpointer arg_2)
304+ {
305+@@ -97,9 +99,14 @@
306+ return -1;
307+ }
308+
309+- if (!args->no_verify) {
310+- g_set_error(err, PV_PARSE_ERROR, PR_PARSE_ERROR_MISSING_ARGUMENT,
311+- _("Use the option '--no-verify' as the verification support is not available yet."));
312++ if (!args->no_verify &&
313++ (!args->untrusted_cert_paths ||
314++ g_strv_length(args->untrusted_cert_paths) == 0)) {
315++ g_set_error(
316++ err, PV_PARSE_ERROR, PR_PARSE_ERROR_MISSING_ARGUMENT,
317++ _("Either specify the IBM Z signing key and (DigiCert) intermediate CA certificate\n"
318++ "by using the '--cert' option, or use the '--no-verify' flag to disable the\n"
319++ "host-key document verification completely (at your own risk)."));
320+ return -1;
321+ }
322+
323+@@ -141,6 +148,8 @@
324+ {
325+ gchar **args_option = NULL;
326+
327++ if (g_str_equal(option, "--root-ca"))
328++ args_option = &args->root_ca_path;
329+ if (g_str_equal(option, "-o") || g_str_equal(option, "--output"))
330+ args_option = &args->output_path;
331+ if (g_str_equal(option, "--x-comp-key"))
332+@@ -211,6 +220,18 @@
333+ _("FILE specifies a host-key document. At least\n" INDENT
334+ "one is required."),
335+ .arg_description = _("FILE") },
336++ { .long_name = "cert",
337++ .short_name = 'C',
338++ .flags = G_OPTION_FLAG_NONE,
339++ .arg = G_OPTION_ARG_FILENAME_ARRAY,
340++ .arg_data = &args->untrusted_cert_paths,
341++ .description = _(
342++ "FILE contains a certificate that is used to\n" INDENT
343++ "establish a chain of trust for the verification\n" INDENT
344++ "of the host-key documents. The IBM Z signing\n" INDENT
345++ "key and intermediate CA certificate (signed\n" INDENT
346++ "by the root CA) are required."),
347++ .arg_description = _("FILE") },
348+ { .long_name = "output",
349+ .short_name = 'o',
350+ .flags = G_OPTION_FLAG_FILENAME,
351+@@ -241,6 +262,31 @@
352+ .description = _("Use the kernel parameters stored in PARMFILE\n" INDENT
353+ "(optional)."),
354+ .arg_description = _("PARMFILE") },
355++ { .long_name = "crl",
356++ .short_name = 0,
357++ .flags = G_OPTION_FLAG_NONE,
358++ .arg = G_OPTION_ARG_FILENAME_ARRAY,
359++ .arg_data = &args->crl_paths,
360++ .description = _(
361++ "FILE contains a certificate revocation list\n" INDENT
362++ "(optional)."),
363++ .arg_description = _("FILE") },
364++ { .long_name = "offline",
365++ .short_name = 0,
366++ .flags = G_OPTION_FLAG_NONE,
367++ .arg = G_OPTION_ARG_NONE,
368++ .arg_data = &args->offline,
369++ .description = _("Don't download CRLs (optional)."),
370++ .arg_description = NULL },
371++ { .long_name = "root-ca",
372++ .short_name = 0,
373++ .flags = G_OPTION_FLAG_FILENAME,
374++ .arg = G_OPTION_ARG_CALLBACK,
375++ .arg_data = cb_set_string_option,
376++ .description = _(
377++ "Set FILE as the trusted root CA and don't use the\n" INDENT
378++ "root CAs that are installed on the system (optional)."),
379++ .arg_description = _("FILE") },
380+ { .long_name = "no-verify",
381+ .short_name = 0,
382+ .flags = G_OPTION_FLAG_NONE,
383+@@ -378,6 +424,9 @@
384+ g_free(args->cust_root_key_path);
385+ g_free(args->cust_comm_key_path);
386+ g_free(args->gcm_iv_path);
387++ g_free(args->root_ca_path);
388++ g_strfreev(args->crl_paths);
389++ g_strfreev(args->untrusted_cert_paths);
390+ g_strfreev(args->host_keys);
391+ g_free(args->xts_key_path);
392+ g_slist_free_full(args->comps, (GDestroyNotify)pv_arg_free);
393+--- a/genprotimg/src/pv/pv_args.h
394++++ b/genprotimg/src/pv/pv_args.h
395+@@ -25,6 +25,7 @@
396+ typedef struct {
397+ gint log_level;
398+ gint no_verify;
399++ gboolean offline;
400+ gchar *pcf;
401+ gchar *scf;
402+ gchar *psw_addr; /* PSW address which will be used for the start of
403+@@ -34,6 +35,11 @@
404+ gchar *cust_comm_key_path;
405+ gchar *gcm_iv_path;
406+ gchar **host_keys;
407++ gchar *root_ca_path; /* Trusted root CA used for the verification of the
408++ * chain of trust (if specified).
409++ */
410++ gchar **untrusted_cert_paths;
411++ gchar **crl_paths;
412+ gchar *xts_key_path;
413+ GSList *comps;
414+ gchar *output_path;
415+--- a/genprotimg/src/pv/pv_error.h
416++++ b/genprotimg/src/pv/pv_error.h
417+@@ -28,6 +28,8 @@
418+ PV_ERROR_IPIB_SIZE,
419+ PV_ERROR_PV_HDR_SIZE,
420+ PV_ERROR_INTERNAL,
421++ PV_ERROR_CURL_INIT_FAILED,
422++ PV_ERROR_DOWNLOAD_FAILED,
423+ } PvErrors;
424+
425+ typedef enum {
426+@@ -57,6 +59,31 @@
427+ PV_CRYPTO_ERROR_RANDOMIZATION,
428+ PV_CRYPTO_ERROR_INVALID_PARM,
429+ PV_CRYPTO_ERROR_INVALID_KEY_SIZE,
430++ PV_CRYPTO_ERROR_INVALID_VALIDITY_PERIOD,
431++ PV_CRYPTO_ERROR_EXPIRED,
432++ PV_CRYPTO_ERROR_NOT_VALID_YET,
433++ PV_CRYPTO_ERROR_LOAD_CRL,
434++ PV_CRYPTO_ERROR_NO_PUBLIC_KEY,
435++ PV_CRYPTO_ERROR_INVALID_SIGNATURE_ALGORITHM,
436++ PV_CRYPTO_ERROR_SIGNATURE_ALGORITHM_MISMATCH,
437++ PV_CRYPTO_ERROR_INVALID_URI,
438++ PV_CRYPTO_ERROR_CRL_DOWNLOAD_FAILED,
439++ PV_CRYPTO_ERROR_CERT_SIGNATURE_INVALID,
440++ PV_CRYPTO_ERROR_CRL_SIGNATURE_INVALID,
441++ PV_CRYPTO_ERROR_CERT_SUBJECT_ISSUER_MISMATCH,
442++ PV_CRYPTO_ERROR_CRL_SUBJECT_ISSUER_MISMATCH,
443++ PV_CRYPTO_ERROR_NO_IBM_Z_SIGNING_KEY,
444++ PV_CRYPTO_ERROR_MALFORMED_CERTIFICATE,
445++ PV_CRYPTO_ERROR_NO_CRL,
446++ PV_CRYPTO_ERROR_LOAD_ROOT_CA,
447++ PV_CRYPTO_ERROR_LOAD_DEFAULT_CA,
448++ PV_CRYPTO_ERROR_MALFORMED_ROOT_CA,
449++ PV_CRYPTO_ERROR_WRONG_CA_USED,
450++ PV_CRYPTO_ERROR_SKID_AKID_MISMATCH,
451++ PV_CRYPTO_ERROR_NO_ISSUER_IBM_Z_FOUND,
452++ PV_CRYPTO_ERROR_FAILED_DOWNLOAD_CRL,
453++ PV_CRYPTO_ERROR_NO_CRLDP,
454++ PV_CRYPTO_ERROR_CERT_REVOKED,
455+ } PvCryptoErrors;
456+
457+ #endif
458+--- a/genprotimg/src/pv/pv_image.c
459++++ b/genprotimg/src/pv/pv_image.c
460+@@ -10,6 +10,7 @@
461+ #include <errno.h>
462+ #include <glib.h>
463+ #include <openssl/evp.h>
464++#include <openssl/x509.h>
465+ #include <stdio.h>
466+ #include <stdlib.h>
467+ #include <string.h>
468+@@ -138,22 +139,18 @@
469+ return generate_ec_key(nid, err);
470+ }
471+
472+-static HostKeyList *pv_img_get_host_keys(gchar **host_cert_paths,
473+- X509_STORE *store, gint nid,
474++static HostKeyList *pv_img_get_host_keys(GSList *host_keys_with_path, gint nid,
475+ GError **err)
476+ {
477+ g_autoslist(EVP_PKEY) ret = NULL;
478+
479+- g_assert(host_cert_paths);
480+-
481+- for (gchar **iterator = host_cert_paths; iterator != NULL && *iterator != NULL;
482+- iterator++) {
483++ for (GSList *iterator = host_keys_with_path; iterator;
484++ iterator = iterator->next) {
485++ x509_with_path *cert_with_path = iterator->data;
486+ g_autoptr(EVP_PKEY) host_key = NULL;
487+- const gchar *path = *iterator;
488+-
489+- g_assert(path);
490++ X509 *cert = cert_with_path->cert;
491+
492+- host_key = read_ec_pubkey_cert(store, nid, path, err);
493++ host_key = read_ec_pubkey_cert(cert, nid, err);
494+ if (!host_key)
495+ return NULL;
496+
497+@@ -253,10 +250,172 @@
498+ return 0;
499+ }
500+
501++static gint pv_img_hostkey_verify(GSList *host_key_certs,
502++ const gchar *root_ca_path,
503++ const gchar *const *crl_paths,
504++ const gchar *const *untrusted_cert_paths,
505++ gboolean offline, GError **err)
506++{
507++ g_autoslist(x509_with_path) untrusted_certs_with_path = NULL;
508++ g_autoptr(STACK_OF_X509) ibm_signing_certs = NULL;
509++ g_autoptr(STACK_OF_X509) untrusted_certs = NULL;
510++ g_autoslist(x509_pair) ibm_z_pairs = NULL;
511++ g_autoptr(X509_STORE) trusted = NULL;
512++ gint ibm_signing_certs_count;
513++
514++ /* Load trusted root CAs of the system if and only if @root_ca_path is
515++ * NULL, otherwise use the root CA specified by @root_ca_path.
516++ */
517++ trusted = store_setup(root_ca_path, crl_paths, err);
518++ if (!trusted)
519++ goto error;
520++
521++ if (!offline) {
522++ g_autoptr(STACK_OF_X509_CRL) downloaded_ibm_signing_crls = NULL;
523++
524++ /* Set up the download routine for the lookup of CRLs. */
525++ store_setup_crl_download(trusted);
526++
527++ /* Try to download the CRLs of the IBM Z signing certificates
528++ * specified in the host-key documents. Ignore download errors
529++ * as it's still possible that a CRL is specified via command
530++ * line.
531++ */
532++ downloaded_ibm_signing_crls = try_load_crls_by_certs(host_key_certs);
533++
534++ /* Add the downloaded CRLs to the store so they can be used for
535++ * the verification later.
536++ */
537++ for (int i = 0; i < sk_X509_CRL_num(downloaded_ibm_signing_crls); i++) {
538++ X509_CRL *crl = sk_X509_CRL_value(downloaded_ibm_signing_crls, i);
539++
540++ if (X509_STORE_add_crl(trusted, crl) != 1) {
541++ g_set_error(err, PV_CRYPTO_ERROR,
542++ PV_CRYPTO_ERROR_INTERNAL,
543++ _("failed to load CRL"));
544++ goto error;
545++ }
546++ }
547++ }
548++
549++ /* Load all untrusted certificates (e.g. IBM Z signing key and
550++ * DigiCert intermediate CA) that are required to establish a chain of
551++ * trust starting from the host-key document up to the root CA (if not
552++ * otherwise specified that's the DigiCert Assured ID Root CA).
553++ */
554++ untrusted_certs_with_path = load_certificates(untrusted_cert_paths, err);
555++ if (!untrusted_certs_with_path)
556++ goto error;
557++
558++ /* Convert to STACK_OF(X509) */
559++ untrusted_certs = get_x509_stack(untrusted_certs_with_path);
560++
561++ /* Find all IBM Z signing keys and remove them from the chain as we
562++ * have to verify that they're valid. The last step of the chain of
563++ * trust verification must be done manually, as the IBM Z signing keys
564++ * are not marked as (intermediate) CA and therefore the standard
565++ * `X509_verify_cert` function of OpenSSL cannot be used to verify the
566++ * actual host-key documents.
567++ */
568++ ibm_signing_certs = delete_ibm_signing_certs(untrusted_certs);
569++ ibm_signing_certs_count = sk_X509_num(ibm_signing_certs);
570++ if (ibm_signing_certs_count < 1) {
571++ g_set_error(err, PV_CRYPTO_ERROR, PV_CRYPTO_ERROR_NO_IBM_Z_SIGNING_KEY,
572++ _("please specify at least one IBM Z signing key"));
573++ goto error;
574++ } else if (ibm_signing_certs_count > 1) {
575++ g_set_error(err, PV_CRYPTO_ERROR, PV_CRYPTO_ERROR_NO_IBM_Z_SIGNING_KEY,
576++ _("please specify only one IBM Z signing key"));
577++ goto error;
578++ }
579++
580++ if (store_set_verify_param(trusted, err) < 0)
581++ goto error;
582++
583++ /* Verify that the IBM Z signing keys are trustable.
584++ * For this we must check:
585++ *
586++ * 1. Can a chain of trust be established ending in a root CA
587++ * 2. Is the correct root CA ued? It has either to be the
588++ * 'DigiCert Assured ID Root CA' or the root CA specified via
589++ * command line.
590++ */
591++ for (gint i = 0; i < sk_X509_num(ibm_signing_certs); ++i) {
592++ X509 *ibm_signing_cert = sk_X509_value(ibm_signing_certs, i);
593++ g_autoptr(STACK_OF_X509_CRL) ibm_signing_crls = NULL;
594++ g_autoptr(X509_STORE_CTX) ctx = NULL;
595++ x509_pair *pair = NULL;
596++
597++ g_assert(ibm_signing_cert);
598++
599++ /* Create the verification context and set the trusted
600++ * and chain parameters.
601++ */
602++ ctx = create_store_ctx(trusted, untrusted_certs, err);
603++ if (!ctx)
604++ goto error;
605++
606++ /* Verify the IBM Z signing key */
607++ if (verify_cert(ibm_signing_cert, ctx, err) < 0)
608++ goto error;
609++
610++ /* Verify the build chain of trust chain. If the user passes a
611++ * trusted root CA on the command line then the check for the
612++ * Subject Key Identifier (SKID) is skipped, otherwise let's
613++ * check if the SKID meets our expectation.
614++ */
615++ if (!root_ca_path &&
616++ check_chain_parameters(X509_STORE_CTX_get0_chain(ctx),
617++ get_digicert_assured_id_root_ca_skid(),
618++ err) < 0) {
619++ goto error;
620++ }
621++
622++ ibm_signing_crls = store_ctx_find_valid_crls(ctx, ibm_signing_cert, err);
623++ if (!ibm_signing_crls) {
624++ g_prefix_error(err, _("IBM Z signing key: "));
625++ goto error;
626++ }
627++
628++ /* Increment reference counter of @ibm_signing_cert as the
629++ * certificate will now also be owned by @ibm_z_pairs.
630++ */
631++ if (X509_up_ref(ibm_signing_cert) != 1)
632++ g_abort();
633++
634++ pair = x509_pair_new(&ibm_signing_cert, &ibm_signing_crls);
635++ ibm_z_pairs = g_slist_append(ibm_z_pairs, pair);
636++ g_assert(!ibm_signing_cert);
637++ g_assert(!ibm_signing_crls);
638++ }
639++
640++ /* Verify host-key documents by using the IBM Z signing
641++ * certificates and the corresponding certificate revocation
642++ * lists.
643++ */
644++ for (GSList *iterator = host_key_certs; iterator; iterator = iterator->next) {
645++ x509_with_path *host_key_with_path = iterator->data;
646++ const gchar *host_key_path = host_key_with_path->path;
647++ X509 *host_key = host_key_with_path->cert;
648++ gint flags = X509_V_FLAG_CRL_CHECK;
649++
650++ if (verify_host_key(host_key, ibm_z_pairs, flags,
651++ PV_CERTS_SECURITY_LEVEL, err) < 0) {
652++ g_prefix_error(err, "'%s': ", host_key_path);
653++ goto error;
654++ }
655++ }
656++
657++ return 0;
658++error:
659++ g_prefix_error(err, _("Failed to verify host-key document: "));
660++ return -1;
661++}
662++
663+ /* read in the keys or auto-generate them */
664+ static gint pv_img_set_keys(PvImage *img, const PvArgs *args, GError **err)
665+ {
666+- g_autoptr(X509_STORE) store = NULL;
667++ g_autoslist(x509_with_path) host_key_certs = NULL;
668+
669+ g_assert(img->xts_cipher);
670+ g_assert(img->cust_comm_cipher);
671+@@ -285,8 +444,25 @@
672+ if (!img->cust_pub_priv_key)
673+ return -1;
674+
675++ /* Load all host-key documents specified on the command line */
676++ host_key_certs = load_certificates((const gchar **)args->host_keys,
677++ err);
678++ if (!host_key_certs)
679++ return -1;
680++
681++ if (!args->no_verify &&
682++ pv_img_hostkey_verify(host_key_certs, args->root_ca_path,
683++ (const gchar * const *)args->crl_paths,
684++ (const gchar * const *)args->untrusted_cert_paths,
685++ args->offline, err) < 0) {
686++ return -1;
687++ }
688++
689++ /* Loads the public keys stored in the host-key documents and verify
690++ * that the correct elliptic curve is used.
691++ */
692+ img->host_pub_keys =
693+- pv_img_get_host_keys(args->host_keys, store, img->nid, err);
694++ pv_img_get_host_keys(host_key_certs, img->nid, err);
695+ if (!img->host_pub_keys)
696+ return -1;
697+
698+@@ -406,6 +582,9 @@
699+ if (args->no_verify)
700+ g_warning(_("host-key document verification is disabled. Your workload is not secured."));
701+
702++ if (args->root_ca_path)
703++ g_warning(_("A different root CA than the default DigiCert root CA is selected. Ensure that this root CA is trusted."));
704++
705+ ret->comps = pv_img_comps_new(EVP_sha512(), EVP_sha512(), EVP_sha512(), err);
706+ if (!ret->comps)
707+ return NULL;
708+--- a/genprotimg/src/utils/crypto.c
709++++ b/genprotimg/src/utils/crypto.c
710+@@ -16,6 +16,11 @@
711+ #include <openssl/evp.h>
712+ #include <openssl/pem.h>
713+ #include <openssl/rand.h>
714++#include <openssl/x509.h>
715++#include <openssl/x509v3.h>
716++#include <openssl/x509_vfy.h>
717++#include <openssl/err.h>
718++#include <stdio.h>
719+ #include <stdint.h>
720+ #include <string.h>
721+
722+@@ -25,8 +30,49 @@
723+ #include "pv/pv_error.h"
724+
725+ #include "buffer.h"
726++#include "curl.h"
727+ #include "crypto.h"
728+
729++#define DEFINE_GSLIST_MAP(t2, t1) \
730++ typedef t1 *(*g_slist_map_func_##t2##_##t1)(const t2 *x, \
731++ GError **err); \
732++ G_GNUC_UNUSED static GSList *g_slist_map_##t2##_##t1(const GSList *list, \
733++ g_slist_map_func_##t2##_##t1 func, \
734++ GError **err) \
735++ { \
736++ g_autoslist(t1) ret = NULL; \
737++ for (const GSList *iterator = list; iterator; \
738++ iterator = iterator->next) { \
739++ const t2 *value = iterator->data; \
740++ t1 *new_value = NULL; \
741++ g_assert(value); \
742++ new_value = func(value, err); \
743++ if (!new_value) \
744++ return NULL; \
745++ ret = g_slist_append(ret, g_steal_pointer(&new_value)); \
746++ } \
747++ return g_steal_pointer(&ret); \
748++ }
749++
750++#define DEFINE_GSLIST_TO_STACK(t1) \
751++ G_GNUC_UNUSED static STACK_OF(t1) *g_slist_to_stack_of_##t1(GSList **list) \
752++ { \
753++ g_assert(list); \
754++ g_autoptr(STACK_OF_##t1) ret = sk_##t1##_new_null(); \
755++ if (!ret) \
756++ g_abort(); \
757++ for (GSList *iterator = *list; iterator; \
758++ iterator = iterator->next) { \
759++ if (sk_##t1##_push(ret, g_steal_pointer(&iterator->data)) == 0) \
760++ g_abort(); \
761++ } \
762++ g_clear_pointer(list, g_slist_free); \
763++ return g_steal_pointer(&ret); \
764++ }
765++
766++DEFINE_GSLIST_MAP(x509_with_path, X509)
767++DEFINE_GSLIST_TO_STACK(X509)
768++
769+ EVP_MD_CTX *digest_ctx_new(const EVP_MD *md, GError **err)
770+ {
771+ g_autoptr(EVP_MD_CTX) ctx = EVP_MD_CTX_new();
772+@@ -359,79 +405,1340 @@
773+ return TRUE;
774+ }
775+
776+-static gboolean verify_certificate(X509_STORE *store, X509 *cert, GError **err)
777++/* Verify that the used public key algorithm matches the subject signature
778++ * algorithm
779++ */
780++static int check_signature_algo_match(const EVP_PKEY *pkey, const X509 *subject,
781++ GError **err)
782+ {
783+- g_autoptr(X509_STORE_CTX) csc = X509_STORE_CTX_new();
784+- if (!csc)
785++ gint pkey_nid;
786++
787++ if (!pkey) {
788++ g_set_error(err, PV_CRYPTO_ERROR, PV_CRYPTO_ERROR_NO_PUBLIC_KEY,
789++ _("no public key"));
790++ return -1;
791++ }
792++
793++ if (OBJ_find_sigid_algs(X509_get_signature_nid(subject), NULL,
794++ &pkey_nid) != 1) {
795++ g_set_error(err, PV_CRYPTO_ERROR,
796++ PV_CRYPTO_ERROR_INVALID_SIGNATURE_ALGORITHM,
797++ _("unsupported signature algorithm"));
798++ return -1;
799++ }
800++
801++ if (EVP_PKEY_type(pkey_nid) != EVP_PKEY_base_id(pkey)) {
802++ g_set_error(err, PV_CRYPTO_ERROR,
803++ PV_CRYPTO_ERROR_SIGNATURE_ALGORITHM_MISMATCH,
804++ _("signature algorithm mismatch"));
805++ return -1;
806++ }
807++
808++ return 0;
809++}
810++
811++static X509_CRL *load_crl_from_bio(BIO *bio)
812++{
813++ g_autoptr(X509_CRL) crl = PEM_read_bio_X509_CRL(bio, NULL, 0, NULL);
814++ if (crl)
815++ return g_steal_pointer(&crl);
816++ ERR_clear_error();
817++ BIO_reset(bio);
818++
819++ /* maybe the CRL is stored in DER format */
820++ crl = d2i_X509_CRL_bio(bio, NULL);
821++ if (crl)
822++ return g_steal_pointer(&crl);
823++ return NULL;
824++}
825++
826++static X509_CRL *GByteArray_to_X509_CRL(const GByteArray *data)
827++{
828++ g_autoptr(X509_CRL) ret = NULL;
829++ g_autoptr(BIO) bio = NULL;
830++
831++ g_assert(data);
832++
833++ if (data->len > INT_MAX)
834++ return NULL;
835++
836++ bio = BIO_new_mem_buf(data->data, (int)data->len);
837++ if (!bio)
838+ g_abort();
839+
840+- if (X509_STORE_CTX_init(csc, store, cert, NULL) != 1) {
841+- g_set_error(err, PV_CRYPTO_ERROR, PV_CRYPTO_ERROR_INIT,
842+- _("Failed to initialize X.509 store"));
843+- return FALSE;
844++ ret = load_crl_from_bio(bio);
845++ if (!ret)
846++ return NULL;
847++
848++ return g_steal_pointer(&ret);
849++}
850++
851++static gint load_crl_from_web(const gchar *url, X509_CRL **crl, GError **err)
852++{
853++ g_autoptr(X509_CRL) tmp_crl = NULL;
854++ g_autoptr(GByteArray) data = NULL;
855++ g_assert(crl);
856++
857++ data = curl_download(url, CRL_DOWNLOAD_TIMEOUT_MS,
858++ CRL_DOWNLOAD_MAX_SIZE, err);
859++ if (!data) {
860++ g_prefix_error(err, _("unable to download CRL: "));
861++ return -1;
862+ }
863++ tmp_crl = GByteArray_to_X509_CRL(data);
864++ if (!tmp_crl) {
865++ g_set_error(err, PV_CRYPTO_ERROR,
866++ PV_CRYPTO_ERROR_CRL_DOWNLOAD_FAILED,
867++ _("unable to load CRL from '%s'"), url);
868++ return -1;
869++ }
870++ *crl = g_steal_pointer(&tmp_crl);
871++ return 0;
872++}
873+
874+- if (X509_verify_cert(csc) != 1) {
875+- g_set_error(err, PV_CRYPTO_ERROR, PV_CRYPTO_ERROR_VERIFICATION,
876+- _("Failed to verify host-key document"));
877++static BIO *bio_read_from_file(const char *path)
878++{
879++ g_autoptr(BIO) bio = BIO_new_file(path, "r");
880++
881++ if (!bio)
882++ return NULL;
883++
884++ return g_steal_pointer(&bio);
885++}
886++
887++/* This function reads in only the first certificate and ignores all other. This
888++ * is only relevant for the PEM file format. For the host-key document and the
889++ * root CA this behavior is expected.
890++ */
891++X509 *load_cert_from_file(const char *path, GError **err)
892++{
893++ g_autoptr(BIO) bio = bio_read_from_file(path);
894++ g_autoptr(X509) cert = NULL;
895++
896++ if (!bio) {
897++ g_set_error(err, PV_CRYPTO_ERROR,
898++ PV_CRYPTO_ERROR_READ_CERTIFICATE,
899++ _("unable to read certificate: '%s'"), path);
900++ return NULL;
901++ }
902++
903++ cert = PEM_read_bio_X509(bio, NULL, NULL, NULL);
904++ if (cert)
905++ return g_steal_pointer(&cert);
906++ ERR_clear_error();
907++ BIO_reset(bio);
908++
909++ /* maybe the certificate is stored in DER format */
910++ cert = d2i_X509_bio(bio, NULL);
911++ if (cert)
912++ return g_steal_pointer(&cert);
913++
914++ g_set_error(err, PV_CRYPTO_ERROR, PV_CRYPTO_ERROR_READ_CERTIFICATE,
915++ _("unable to load certificate: '%s'"), path);
916++ return NULL;
917++}
918++
919++/* @crl_paths is allowed to be NULL */
920++static int load_crls_to_store(X509_STORE *store, const gchar *const *crl_paths,
921++ gboolean err_out_empty_crls, GError **err)
922++{
923++ for (const gchar *const *iterator = crl_paths;
924++ iterator != NULL && *iterator != NULL; iterator++) {
925++ const gchar *crl_path = *iterator;
926++ X509_LOOKUP *lookup = X509_STORE_add_lookup(store, X509_LOOKUP_file());
927++ int count;
928++
929++ g_assert(crl_path);
930++
931++ if (!lookup) {
932++ g_set_error(err, PV_CRYPTO_ERROR, PV_CRYPTO_ERROR_INTERNAL,
933++ _("X509 store initialization failed"));
934++ return -1;
935++ }
936++
937++ /* support *.pem files containing multiple CRLs */
938++ count = X509_load_crl_file(lookup, crl_path, X509_FILETYPE_PEM);
939++ if (count > 0)
940++ continue;
941++
942++ count = X509_load_crl_file(lookup, crl_path, X509_FILETYPE_ASN1);
943++ if (count == 1)
944++ continue;
945++
946++ if (err_out_empty_crls) {
947++ g_set_error(err, PV_CRYPTO_ERROR,
948++ PV_CRYPTO_ERROR_LOAD_CRL,
949++ _("unable to load CRL from: '%s'"), crl_path);
950++ return -1;
951++ }
952++ }
953++
954++ return 0;
955++}
956++
957++/* returns
958++ * 0 when the certificate is valid,
959++ * -1 when not yet valid,
960++ * 1 when expired
961++ */
962++static int check_validity_period(const ASN1_TIME *not_before, const ASN1_TIME *not_after)
963++{
964++ if (X509_cmp_current_time(not_before) != -1)
965++ return -1;
966++
967++ if (X509_cmp_current_time(not_after) != 1)
968++ return 1;
969++
970++ return 0;
971++}
972++
973++static gint x509_name_entry_get_data0(X509_NAME_ENTRY *entry, const guchar **data,
974++ gsize *data_len)
975++{
976++ const ASN1_STRING *asn1_str;
977++ gint tmp_data_len;
978++
979++ g_assert(data);
980++ g_assert(data_len);
981++
982++ asn1_str = X509_NAME_ENTRY_get_data(entry);
983++ if (!asn1_str)
984++ return -1;
985++
986++ tmp_data_len = ASN1_STRING_length(asn1_str);
987++ if (tmp_data_len < 0)
988++ return -1;
989++
990++ *data = ASN1_STRING_get0_data(asn1_str);
991++ *data_len = (gsize)tmp_data_len;
992++ return 0;
993++}
994++
995++/* The caller must not free *data! */
996++static gint x509_name_get_data0_by_NID(X509_NAME *name, gint nid,
997++ const guchar **data, gsize *data_len)
998++{
999++
1000++ X509_NAME_ENTRY *entry = NULL;
1001++ gint lastpos = -1;
1002++
1003++ lastpos = X509_NAME_get_index_by_NID(name, nid, lastpos);
1004++ if (lastpos == -1)
1005++ return -1;
1006++
1007++ entry = X509_NAME_get_entry(name, lastpos);
1008++ if (!entry)
1009++ return -1;
1010++
1011++ if (x509_name_entry_get_data0(entry, data, data_len) < 0)
1012++ return -1;
1013++
1014++ return 0;
1015++}
1016++
1017++/* @y must be a NULL-terminated string */
1018++static gboolean x509_name_data_by_nid_equal(X509_NAME *name, gint nid,
1019++ const gchar *y)
1020++{
1021++ const guchar *data = NULL;
1022++ gsize y_len = strlen(y);
1023++ gsize data_len;
1024++
1025++ if (x509_name_get_data0_by_NID(name, nid, &data, &data_len) < 0)
1026++ return FALSE;
1027++
1028++ if (data_len != y_len)
1029++ return FALSE;
1030++
1031++ return memcmp(data, y, data_len) == 0;
1032++}
1033++
1034++static gboolean own_X509_NAME_ENTRY_equal(const X509_NAME_ENTRY *x,
1035++ const X509_NAME_ENTRY *y)
1036++{
1037++ const ASN1_OBJECT *x_obj = X509_NAME_ENTRY_get_object(x);
1038++ const ASN1_STRING *x_data = X509_NAME_ENTRY_get_data(x);
1039++ const ASN1_OBJECT *y_obj = X509_NAME_ENTRY_get_object(y);
1040++ const ASN1_STRING *y_data = X509_NAME_ENTRY_get_data(y);
1041++ gint x_len = ASN1_STRING_length(x_data);
1042++ gint y_len = ASN1_STRING_length(y_data);
1043++
1044++ if (x_len < 0 || x_len != y_len)
1045+ return FALSE;
1046++
1047++ /* ASN1_STRING_cmp(x_data, y_data) == 0 doesn't work because it also
1048++ * compares the type, which is sometimes different.
1049++ */
1050++ return OBJ_cmp(x_obj, y_obj) == 0 &&
1051++ memcmp(ASN1_STRING_get0_data(x_data),
1052++ ASN1_STRING_get0_data(y_data),
1053++ (unsigned long)x_len) == 0;
1054++}
1055++
1056++static gboolean own_X509_NAME_equal(const X509_NAME *x, const X509_NAME *y)
1057++{
1058++ gint x_count = X509_NAME_entry_count(x);
1059++ gint y_count = X509_NAME_entry_count(y);
1060++
1061++ if (x != y && (!x || !y))
1062++ return FALSE;
1063++
1064++ if (x_count != y_count)
1065++ return FALSE;
1066++
1067++ for (gint i = 0; i < x_count; i++) {
1068++ const X509_NAME_ENTRY *entry_i = X509_NAME_get_entry(x, i);
1069++ gboolean entry_found = FALSE;
1070++
1071++ for (gint j = 0; j < y_count; j++) {
1072++ const X509_NAME_ENTRY *entry_j =
1073++ X509_NAME_get_entry(y, j);
1074++
1075++ if (own_X509_NAME_ENTRY_equal(entry_i, entry_j)) {
1076++ entry_found = TRUE;
1077++ break;
1078++ }
1079++ }
1080++
1081++ if (!entry_found)
1082++ return FALSE;
1083+ }
1084++ return TRUE;
1085++}
1086++
1087++/* Checks whether the subject of @cert is a IBM signing key subject. For this we
1088++ * must check that the subject is equal to: 'C = US, ST = New York, L =
1089++ * Poughkeepsie, O = International Business Machines Corporation, CN =
1090++ * International Business Machines Corporation' and the organization unit (OUT)
1091++ * must end with the suffix ' Key Signing Service'.
1092++ */
1093++static gboolean has_ibm_signing_subject(X509 *cert)
1094++{
1095++ X509_NAME *subject = X509_get_subject_name(cert);
1096++ /* X509_NAME_entry_count is safe to be used with NULL */
1097++ gint entry_count = X509_NAME_entry_count(subject);
1098++ g_autofree gchar *data_str = NULL;
1099++ const guchar *data;
1100++ gsize data_len;
1101++
1102++ if (entry_count != PV_IMB_Z_SUBJECT_ENTRY_COUNT)
1103++ return FALSE;
1104++
1105++ if (!x509_name_data_by_nid_equal(subject, NID_countryName,
1106++ PV_IBM_Z_SUBJECT_COUNTRY_NAME))
1107++ return FALSE;
1108++
1109++ if (!x509_name_data_by_nid_equal(subject, NID_stateOrProvinceName,
1110++ PV_IBM_Z_SUBJECT_STATE))
1111++ return FALSE;
1112++
1113++ if (!x509_name_data_by_nid_equal(subject, NID_localityName,
1114++ PV_IBM_Z_SUBJECT_LOCALITY_NAME))
1115++ return FALSE;
1116++
1117++ if (!x509_name_data_by_nid_equal(subject, NID_organizationName,
1118++ PV_IBM_Z_SUBJECT_ORGANIZATION_NAME))
1119++ return FALSE;
1120++
1121++ if (!x509_name_data_by_nid_equal(subject, NID_commonName,
1122++ PV_IBM_Z_SUBJECT_COMMON_NAME))
1123++ return FALSE;
1124++
1125++ if (x509_name_get_data0_by_NID(subject, NID_organizationalUnitName,
1126++ &data, &data_len) < 0)
1127++ return FALSE;
1128++
1129++ /* Make sure that data_str is null-terminated as in general it cannot be
1130++ * assumed that @data is null-terminated.
1131++ */
1132++ data_str = g_strndup((const gchar *)data, data_len);
1133++ if (!g_str_has_suffix(data_str,
1134++ PV_IBM_Z_SUBJECT_ORGANIZATIONONAL_UNIT_NAME_SUFFIX))
1135++ return FALSE;
1136+
1137+ return TRUE;
1138+ }
1139+
1140+-static X509 *load_certificate(const gchar *path, GError **err)
1141++static X509_NAME *x509_name_reorder_attributes(const X509_NAME *name, const gint nids[],
1142++ gsize nids_len)
1143+ {
1144+- g_autoptr(X509) ret = NULL;
1145+- g_autoptr(BIO) bio = BIO_new_file(path, "rb");
1146++ gint entry_count = X509_NAME_entry_count(name);
1147++ g_autoptr(X509_NAME) ret = NULL;
1148++
1149++ if (entry_count < 0)
1150++ return NULL;
1151++
1152++ if (nids_len != (gsize) entry_count)
1153++ return NULL;
1154++
1155++ ret = X509_NAME_new();
1156++ if (!ret)
1157++ g_abort();
1158++
1159++ for (gsize i = 0; i < nids_len; i++) {
1160++ const X509_NAME_ENTRY *entry = NULL;
1161++ gint nid = nids[i];
1162++ gint lastpos = -1;
1163++
1164++ lastpos = X509_NAME_get_index_by_NID((X509_NAME *)name, nid, lastpos);
1165++ if (lastpos == -1)
1166++ return NULL;
1167++
1168++ entry = X509_NAME_get_entry(name, lastpos);
1169++ if (!entry)
1170++ return NULL;
1171++
1172++ if (X509_NAME_add_entry(ret, entry, -1, 0) != 1)
1173++ return NULL;
1174++ }
1175++
1176++ return g_steal_pointer(&ret);
1177++}
1178++
1179++/* In RFC 5280 the attributes of a (subject/issuer) name is not mandatory
1180++ * ordered. The problem is that our certificates are not consistent in the order
1181++ * (see https://tools.ietf.org/html/rfc5280#section-4.1.2.4 for details).
1182++ *
1183++ * This function converts a correct X509_NAME into the broken one. The caller is
1184++ * responsible to free the returned value.
1185++ */
1186++X509_NAME *c2b_name(const X509_NAME *name)
1187++{
1188++ gint nids[] = { NID_countryName, NID_organizationName, NID_organizationalUnitName,
1189++ NID_localityName, NID_stateOrProvinceName, NID_commonName };
1190++ g_autoptr(X509_NAME) broken_name = NULL;
1191++
1192++ g_assert(name);
1193++
1194++ /* Try to reorder the attributes */
1195++ broken_name = x509_name_reorder_attributes(name, nids, G_N_ELEMENTS(nids));
1196++ if (broken_name)
1197++ return g_steal_pointer(&broken_name);
1198++ return X509_NAME_dup((X509_NAME *)name);
1199++}
1200++
1201++/* Verify that: subject(issuer) == issuer(crl) and SKID(issuer) == AKID(crl) */
1202++static gint check_crl_issuer(X509_CRL *crl, X509 *issuer, GError **err)
1203++{
1204++ const X509_NAME *crl_issuer = X509_CRL_get_issuer(crl);
1205++ const X509_NAME *issuer_subject = X509_get_subject_name(issuer);
1206++ AUTHORITY_KEYID *akid = NULL;
1207++
1208++ if (!own_X509_NAME_equal(issuer_subject, crl_issuer)) {
1209++ g_autofree char *issuer_subject_str = X509_NAME_oneline(issuer_subject,
1210++ NULL, 0);
1211++ g_autofree char *crl_issuer_str = X509_NAME_oneline(crl_issuer, NULL, 0);
1212+
1213+- if (!bio) {
1214+ g_set_error(err, PV_CRYPTO_ERROR,
1215+- PV_CRYPTO_ERROR_READ_CERTIFICATE,
1216+- _("Failed to read host-key document: '%s'"), path);
1217++ PV_CRYPTO_ERROR_CRL_SUBJECT_ISSUER_MISMATCH,
1218++ _("issuer mismatch:\n%s\n%s"),
1219++ issuer_subject_str, crl_issuer_str);
1220++ return -1;
1221++ }
1222++
1223++ /* If AKID(@crl) is specified it must match with SKID(@issuer) */
1224++ akid = X509_CRL_get_ext_d2i(crl, NID_authority_key_identifier, NULL, NULL);
1225++ if (akid && X509_check_akid(issuer, akid) != X509_V_OK) {
1226++ g_set_error(err, PV_CRYPTO_ERROR, PV_CRYPTO_ERROR_SKID_AKID_MISMATCH,
1227++ _("AKID mismatch"));
1228++ return -1;
1229++ }
1230++
1231++ return 0;
1232++}
1233++
1234++/* Verify whether a revocation list @crl is valid and is issued by @cert. For
1235++ * this multiple steps must be done:
1236++ *
1237++ * 1. verify issuer of the CRL matches with the suject name of @cert
1238++ * 2. verify the validity period of the CRL
1239++ * 3. verify the signature of the CRL
1240++ *
1241++ * Important: This function doesn't verify whether @cert is allowed to issue a
1242++ * CRL. Returns 0 if @crl is valid and issued by @cert, otherwise -1.
1243++ */
1244++gint check_crl_valid_for_cert(X509_CRL *crl, X509 *cert,
1245++ gint verify_flags, GError **err)
1246++{
1247++ EVP_PKEY *pkey = X509_get0_pubkey(cert);
1248++
1249++ g_assert(crl);
1250++
1251++ if (!pkey) {
1252++ g_set_error(err, PV_CRYPTO_ERROR, PV_CRYPTO_ERROR_INTERNAL,
1253++ _("failed to retrieve public key from the certificate"));
1254++ return -1;
1255++ }
1256++
1257++ /* check that the @crl issuer matches with the subject name of @cert*/
1258++ if (check_crl_issuer(crl, cert, err) < 0)
1259++ return -1;
1260++
1261++ /* verify the validity period of the CRL */
1262++ if (!(verify_flags & X509_V_FLAG_NO_CHECK_TIME)) {
1263++ const ASN1_TIME *last = X509_CRL_get0_lastUpdate(crl);
1264++ const ASN1_TIME *next = X509_CRL_get0_nextUpdate(crl);
1265++
1266++ if (!last || !next || check_validity_period(last, next)) {
1267++ g_set_error(err, PV_CRYPTO_ERROR,
1268++ PV_CRYPTO_ERROR_INVALID_VALIDITY_PERIOD,
1269++ _("validity period is not valid"));
1270++ return -1;
1271++ }
1272++ } else {
1273++ verify_flags &= ~X509_V_FLAG_NO_CHECK_TIME;
1274++ }
1275++
1276++ /* verify the signature */
1277++ if (X509_CRL_verify(crl, pkey) != 1) {
1278++ g_set_error(err, PV_CRYPTO_ERROR, PV_CRYPTO_ERROR_CRL_SIGNATURE_INVALID,
1279++ _("signature is not valid"));
1280++ return -1;
1281++ }
1282++ g_assert(verify_flags == 0);
1283++ return 0;
1284++}
1285++
1286++/* Given a certificate @cert try to find valid revocation lists in @ctx. If no
1287++ * valid CRL was found NULL is returned.
1288++ */
1289++STACK_OF_X509_CRL *store_ctx_find_valid_crls(X509_STORE_CTX *ctx, X509 *cert,
1290++ GError **err)
1291++{
1292++ g_autoptr(STACK_OF_X509_CRL) ret = NULL;
1293++ const gint verify_flags = 0;
1294++ X509_NAME *subject = NULL;
1295++
1296++ subject = X509_get_subject_name(cert);
1297++ if (!subject) {
1298++ g_set_error(err, PV_CRYPTO_ERROR, PV_CRYPTO_ERROR_MALFORMED_CERTIFICATE,
1299++ _("certificate is malformed"));
1300+ return NULL;
1301+ }
1302+
1303+- ret = PEM_read_bio_X509(bio, NULL, 0, NULL);
1304++ ret = X509_STORE_CTX_get1_crls(ctx, subject);
1305+ if (!ret) {
1306+- g_set_error(err, PV_CRYPTO_ERROR,
1307+- PV_CRYPTO_ERROR_READ_CERTIFICATE,
1308+- _("Failed to load host-key document: '%s'"), path);
1309++ /* Workaround to fix the mismatch between issuer name of the
1310++ * IBM Z signing CRLs and the IBM Z signing key subject name.
1311++ */
1312++ g_autoptr(X509_NAME) broken_subject = c2b_name(subject);
1313++
1314++ ret = X509_STORE_CTX_get1_crls(ctx, broken_subject);
1315++ if (!ret) {
1316++ g_set_error(err, PV_CRYPTO_ERROR, PV_CRYPTO_ERROR_NO_CRL,
1317++ _("no CRL found"));
1318++ return NULL;
1319++ }
1320++ }
1321++
1322++ /* Filter out non-valid CRLs for @cert */
1323++ for (gint i = 0; i < sk_X509_CRL_num(ret); i++) {
1324++ X509_CRL *crl = sk_X509_CRL_value(ret, i);
1325++
1326++ g_assert(crl);
1327++
1328++ /* If @crl is not valid remove it from the array and log a
1329++ * warning.
1330++ */
1331++ if (check_crl_valid_for_cert(crl, cert, verify_flags, err) < 0) {
1332++ g_assert(err);
1333++ g_warning(_("CRL is not valid: %s"), (*err)->message);
1334++ g_clear_error(err);
1335++
1336++ /* Remove this certificate from the list and change i-- as the
1337++ * array has changed - this is not beautfiul, but right now the
1338++ * easiest solution I came up with
1339++ */
1340++ if (sk_X509_CRL_delete(ret, i--) != crl)
1341++ g_abort();
1342++
1343++ g_clear_pointer(&crl, X509_CRL_free);
1344++ }
1345++ }
1346++
1347++ if (sk_X509_CRL_num(ret) < 1) {
1348++ g_set_error(err, PV_CRYPTO_ERROR, PV_CRYPTO_ERROR_NO_CRL,
1349++ _("no valid CRL found"));
1350+ return NULL;
1351+ }
1352++ return g_steal_pointer(&ret);
1353++}
1354++
1355++/* Return a list of all IBM Z signing key certificates in @certs and remove them
1356++ * from the chain. Return empty stack if no IBM Z signing key is found.
1357++ */
1358++STACK_OF_X509 *delete_ibm_signing_certs(STACK_OF_X509 *certs)
1359++{
1360++ g_autoptr(STACK_OF_X509) ret = sk_X509_new_null();
1361++
1362++ for (gint i = 0; i < sk_X509_num(certs); i++) {
1363++ X509 *cert = sk_X509_value(certs, i);
1364++
1365++ g_assert(cert);
1366++
1367++ if (!has_ibm_signing_subject(cert))
1368++ continue;
1369++
1370++ /* Remove this certificate from the list and change i-- as the
1371++ * array has changed - this is not beautfiul, but right now the
1372++ * easiest solution I came up with.
1373++ */
1374++ if (sk_X509_delete(certs, i--) != cert)
1375++ g_abort();
1376++
1377++ if (sk_X509_push(ret, g_steal_pointer(&cert)) == 0)
1378++ g_abort();
1379++ }
1380+
1381+ return g_steal_pointer(&ret);
1382+ }
1383+
1384+-EVP_PKEY *read_ec_pubkey_cert(X509_STORE *store, gint nid, const gchar *path,
1385+- GError **err)
1386++X509_STORE *store_setup(const gchar *root_ca_path, const gchar * const *crl_paths,
1387++ GError **err)
1388++{
1389++ g_autoptr(X509_STORE) store = X509_STORE_new();
1390++
1391++ g_assert(store);
1392++
1393++ /* if @root_ca_path != NULL use the specified root CA only, otherwise use the
1394++ * default root CAs found on the system
1395++ */
1396++ if (root_ca_path) {
1397++ X509_LOOKUP *lookup = X509_STORE_add_lookup(store, X509_LOOKUP_file());
1398++ int count;
1399++
1400++ if (!lookup) {
1401++ g_set_error(err, PV_CRYPTO_ERROR, PV_CRYPTO_ERROR_INTERNAL,
1402++ _("X509 store initialization failed"));
1403++ return NULL;
1404++ }
1405++
1406++ count = X509_load_cert_file(lookup, root_ca_path, X509_FILETYPE_PEM);
1407++ if (count > 1) {
1408++ g_set_error(err, PV_CRYPTO_ERROR,
1409++ PV_CRYPTO_ERROR_LOAD_ROOT_CA,
1410++ _("multiple certificates in one PEM file is not supported: '%s'"),
1411++ root_ca_path);
1412++ return NULL;
1413++ } else if (count < 1) {
1414++ count = X509_load_cert_file(lookup, root_ca_path,
1415++ X509_FILETYPE_ASN1);
1416++ if (count != 1) {
1417++ g_set_error(err, PV_CRYPTO_ERROR,
1418++ PV_CRYPTO_ERROR_LOAD_ROOT_CA,
1419++ _("failed to load root certificate from '%s'"),
1420++ root_ca_path);
1421++ return NULL;
1422++ }
1423++ }
1424++ } else {
1425++ /* Load certificates into @store from the hardcoded OpenSSL
1426++ * default paths
1427++ */
1428++ if (X509_STORE_set_default_paths(store) != 1) {
1429++ g_set_error(err, PV_CRYPTO_ERROR,
1430++ PV_CRYPTO_ERROR_LOAD_DEFAULT_CA,
1431++ _("failed to load system root certificates"));
1432++ return NULL;
1433++ }
1434++ }
1435++
1436++ /* Error out if a CRL file was provided that has not at least one CRL*/
1437++ if (load_crls_to_store(store, crl_paths, TRUE, err) < 0)
1438++ return NULL;
1439++
1440++ return g_steal_pointer(&store);
1441++}
1442++
1443++int store_set_verify_param(X509_STORE *store, GError **err)
1444++{
1445++ g_autoptr(X509_VERIFY_PARAM) param = NULL;
1446++ unsigned long flags = X509_V_FLAG_CRL_CHECK |
1447++ X509_V_FLAG_CRL_CHECK_ALL |
1448++ X509_V_FLAG_TRUSTED_FIRST |
1449++ X509_V_FLAG_CHECK_SS_SIGNATURE |
1450++ X509_V_FLAG_X509_STRICT |
1451++ X509_V_FLAG_POLICY_CHECK;
1452++
1453++ /* Create a X509_VERIFY_PARAM structure, which specifies which checks
1454++ * should be done by the certificate verification operation
1455++ */
1456++ param = X509_VERIFY_PARAM_new();
1457++ if (!param)
1458++ g_abort();
1459++
1460++ /* The maximum depth level of the chain of trust for the verification of
1461++ * the IBM Z signing key is 2, i.e. IBM Z signing key -> (DigiCert)
1462++ * intermediate CA -> (DigiCert) root CA
1463++ */
1464++ X509_VERIFY_PARAM_set_depth(param, 2);
1465++
1466++ /* Set minimum allowed security level to at least 112 bits. */
1467++ X509_VERIFY_PARAM_set_auth_level(param, PV_CERTS_SECURITY_LEVEL);
1468++
1469++ /* Set verification purpose to 'Any Purpose' and specify that the
1470++ * associated trust setting of the default purpose should be used.
1471++ */
1472++ if (X509_VERIFY_PARAM_set_purpose(param,
1473++ X509_PURPOSE_ANY | X509_TRUST_DEFAULT) != 1)
1474++ goto error;
1475++
1476++ /* Each certificate from the chain of trust must be checked against a
1477++ * CRL to see if it has been revoked. In addition, use trusted
1478++ * certificates first mode, check signature of the last certificate,
1479++ * strict mode, and verify the policies.
1480++ */
1481++ if (X509_VERIFY_PARAM_set_flags(param, flags) != 1)
1482++ goto error;
1483++
1484++ if (X509_STORE_set1_param(store, param) != 1)
1485++ goto error;
1486++
1487++ return 0;
1488++
1489++error:
1490++ g_set_error(err, PV_CRYPTO_ERROR, PV_CRYPTO_ERROR_INTERNAL,
1491++ _("X509 store initialization failed"));
1492++ return -1;
1493++}
1494++
1495++/* @cert_paths must contain at least one element, otherwise an error is
1496++ * reported.
1497++ */
1498++GSList *load_certificates(const gchar *const *cert_paths, GError **err)
1499++{
1500++ g_autoslist(x509_with_path) ret = NULL;
1501++
1502++ for (const gchar *const *iterator = cert_paths;
1503++ iterator != NULL && *iterator != NULL; iterator++) {
1504++ const gchar *cert_path = *iterator;
1505++ g_autoptr(X509) cert = NULL;
1506++
1507++ g_assert(cert_path);
1508++
1509++ cert = load_cert_from_file(cert_path, err);
1510++ if (!cert)
1511++ return NULL;
1512++
1513++ ret = g_slist_append(ret, x509_with_path_new(cert, cert_path));
1514++ }
1515++ if (!ret) {
1516++ g_set_error(err, PV_CRYPTO_ERROR, PV_CRYPTO_ERROR_READ_CERTIFICATE,
1517++ _("no certificates specified"));
1518++ return NULL;
1519++ }
1520++
1521++ return g_steal_pointer(&ret);
1522++}
1523++
1524++static X509 *get_cert(const x509_with_path *cert_with_path, G_GNUC_UNUSED GError **err)
1525+ {
1526+- g_autoptr(EVP_PKEY) ret = NULL;
1527+ g_autoptr(X509) cert = NULL;
1528+
1529+- cert = load_certificate(path, err);
1530++ g_assert(cert_with_path && cert_with_path->cert);
1531++
1532++ cert = cert_with_path->cert;
1533++ if (X509_up_ref(cert) != 1)
1534++ g_abort();
1535++ return g_steal_pointer(&cert);
1536++}
1537++
1538++STACK_OF_X509 *get_x509_stack(const GSList *x509_with_path_list)
1539++{
1540++ g_autoslist(X509) certs = NULL;
1541++ g_autoptr(GError) err = NULL;
1542++
1543++ certs = g_slist_map_x509_with_path_X509(x509_with_path_list,
1544++ get_cert, &err);
1545++ g_assert_null(err);
1546++ return g_slist_to_stack_of_X509(&certs);
1547++}
1548++
1549++x509_with_path *x509_with_path_new(X509 *cert, const gchar *path)
1550++{
1551++ g_autoptr(x509_with_path) ret = g_new(x509_with_path, 1);
1552++
1553++ g_assert(cert && path);
1554++
1555++ if (X509_up_ref(cert) != 1)
1556++ g_abort();
1557++ ret->cert = cert;
1558++ ret->path = g_strdup(path);
1559++ return g_steal_pointer(&ret);
1560++}
1561++
1562++void x509_with_path_free(x509_with_path *cert)
1563++{
1564+ if (!cert)
1565++ return;
1566++
1567++ X509_free(cert->cert);
1568++ g_free((gchar *)cert->path);
1569++ g_free(cert);
1570++}
1571++
1572++x509_pair *x509_pair_new(X509 **cert, STACK_OF_X509_CRL **crls)
1573++{
1574++ g_autoptr(x509_pair) ret = g_new0(x509_pair, 1);
1575++
1576++ g_assert(cert);
1577++ g_assert(crls);
1578++
1579++ ret->cert = g_steal_pointer(cert);
1580++ ret->crls = g_steal_pointer(crls);
1581++ return g_steal_pointer(&ret);
1582++}
1583++
1584++void x509_pair_free(x509_pair *pair)
1585++{
1586++ if (!pair)
1587++ return;
1588++
1589++ sk_X509_CRL_pop_free(pair->crls, X509_CRL_free);
1590++ X509_free(pair->cert);
1591++ g_free(pair);
1592++}
1593++
1594++X509_STORE_CTX *create_store_ctx(X509_STORE *trusted, STACK_OF_X509 *chain,
1595++ GError **err)
1596++{
1597++ g_autoptr(X509_STORE_CTX) ctx = X509_STORE_CTX_new();
1598++
1599++ if (!ctx || !X509_STORE_CTX_init(ctx, trusted, NULL, chain)) {
1600++ g_set_error(err, PV_CRYPTO_ERROR, PV_CRYPTO_ERROR_INTERNAL,
1601++ _("X509 store initialization failed: %s"),
1602++ X509_verify_cert_error_string(X509_STORE_CTX_get_error(ctx)));
1603+ return NULL;
1604++ }
1605+
1606+- if (store && !verify_certificate(store, cert, err)) {
1607+- g_prefix_error(err,
1608+- _("Failed to load host-key document: '%s': "),
1609+- path);
1610++ return g_steal_pointer(&ctx);
1611++}
1612++
1613++gint verify_cert(X509 *cert, X509_STORE_CTX *ctx, GError **err)
1614++{
1615++ gint rc;
1616++
1617++ X509_STORE_CTX_set_cert(ctx, cert);
1618++ rc = X509_verify_cert(ctx);
1619++ if (rc != 1) {
1620++ X509 *tmp_cert = NULL;
1621++
1622++ tmp_cert = X509_STORE_CTX_get_current_cert(ctx);
1623++ if (tmp_cert) {
1624++ g_autofree char *subj_name = X509_NAME_oneline(
1625++ X509_get_subject_name(tmp_cert), NULL, 0);
1626++ g_set_error(err, PV_CRYPTO_ERROR,
1627++ PV_CRYPTO_ERROR_INTERNAL,
1628++ _("failed to verify certificate '%s': %s"),
1629++ subj_name,
1630++ X509_verify_cert_error_string(
1631++ X509_STORE_CTX_get_error(ctx)));
1632++ } else {
1633++ g_set_error(err, PV_CRYPTO_ERROR,
1634++ PV_CRYPTO_ERROR_INTERNAL,
1635++ _("failed to verify certificate: %s"),
1636++ X509_verify_cert_error_string(
1637++ X509_STORE_CTX_get_error(ctx)));
1638++ }
1639++
1640++ return -1;
1641++ }
1642++
1643++ return 0;
1644++}
1645++
1646++static int security_level_to_bits(int level)
1647++{
1648++ static int security_bits[] = { 0, 80, 112, 128, 192, 256 };
1649++
1650++ g_assert(level > 0 && level < (int)G_N_ELEMENTS(security_bits));
1651++
1652++ return security_bits[level];
1653++}
1654++
1655++static ASN1_OCTET_STRING *digicert_assured_id_root_ca;
1656++
1657++const ASN1_OCTET_STRING *get_digicert_assured_id_root_ca_skid(void)
1658++{
1659++ pv_crypto_init();
1660++ return digicert_assured_id_root_ca;
1661++}
1662++
1663++/* Used for the caching of the downloaded CRLs */
1664++static GHashTable *cached_crls;
1665++
1666++void pv_crypto_init(void)
1667++{
1668++ if (digicert_assured_id_root_ca)
1669++ return;
1670++
1671++ cached_crls = g_hash_table_new_full(g_str_hash, g_str_equal, g_free,
1672++ (GDestroyNotify)X509_CRL_free);
1673++ digicert_assured_id_root_ca = s2i_ASN1_OCTET_STRING(
1674++ NULL, NULL, DIGICERT_ASSURED_ID_ROOT_CA_SKID);
1675++}
1676++
1677++void pv_crypto_cleanup(void)
1678++{
1679++ if (!digicert_assured_id_root_ca)
1680++ return;
1681++ g_clear_pointer(&cached_crls, g_hash_table_destroy);
1682++ g_clear_pointer(&digicert_assured_id_root_ca, ASN1_OCTET_STRING_free);
1683++}
1684++
1685++gint check_chain_parameters(const STACK_OF_X509 *chain,
1686++ const ASN1_OCTET_STRING *skid, GError **err)
1687++{
1688++ const ASN1_OCTET_STRING *ca_skid = NULL;
1689++ gint len = sk_X509_num(chain);
1690++ X509 *ca = NULL;
1691++
1692++ g_assert(skid);
1693++ /* at least one root and one leaf certificate must be defined */
1694++ g_assert(len >= 2);
1695++
1696++ /* get the root certificate of the chain of trust */
1697++ ca = sk_X509_value(chain, len - 1);
1698++ if (!ca) {
1699++ g_set_error(err, PV_CRYPTO_ERROR, PV_CRYPTO_ERROR_INTERNAL,
1700++ _("no root certificate found"));
1701++ return -1;
1702++ }
1703++
1704++ ca_skid = X509_get0_subject_key_id(ca);
1705++ if (!ca_skid) {
1706++ g_set_error(err, PV_CRYPTO_ERROR, PV_CRYPTO_ERROR_MALFORMED_ROOT_CA,
1707++ _("malformed root certificate"));
1708++ return -1;
1709++ }
1710++
1711++ if (ASN1_STRING_cmp(ca_skid, skid) != 0) {
1712++ g_set_error(err, PV_CRYPTO_ERROR, PV_CRYPTO_ERROR_WRONG_CA_USED,
1713++ _("expecting DigiCert root CA to be used"));
1714++ return -1;
1715++ }
1716++
1717++ return 0;
1718++}
1719++
1720++/* It's almost the same as X509_check_issed from OpenSSL does except that we
1721++ * don't check the key usage of the potential issuer. This means we check:
1722++ * 1. issuer_name(cert) == subject_name(issuer)
1723++ * 2. Check whether the akid(cert) (if available) matches the issuer skid
1724++ * 3. Check that the cert algrithm matches the subject algorithm
1725++ * 4. Verify the signature of certificate @cert is using the public key of
1726++ * @issuer.
1727++ */
1728++static gint check_host_key_issued(X509 *cert, X509 *issuer, GError **err)
1729++{
1730++ const X509_NAME *issuer_subject = X509_get_subject_name(issuer);
1731++ const X509_NAME *cert_issuer = X509_get_issuer_name(cert);
1732++ AUTHORITY_KEYID *akid = NULL;
1733++
1734++ /* We cannot use X509_NAME_cmp() because it considers the order of the
1735++ * X509_NAME_Entries.
1736++ */
1737++ if (!own_X509_NAME_equal(issuer_subject, cert_issuer)) {
1738++ g_autofree char *issuer_subject_str =
1739++ X509_NAME_oneline(issuer_subject, NULL, 0);
1740++ g_autofree char *cert_issuer_str =
1741++ X509_NAME_oneline(cert_issuer, NULL, 0);
1742++ g_set_error(err, PV_CRYPTO_ERROR,
1743++ PV_CRYPTO_ERROR_CERT_SUBJECT_ISSUER_MISMATCH,
1744++ _("Subject issuer mismatch:\n'%s'\n'%s'"),
1745++ issuer_subject_str, cert_issuer_str);
1746++ return -1;
1747++ }
1748++
1749++ akid = X509_get_ext_d2i(cert, NID_authority_key_identifier, NULL, NULL);
1750++ if (akid && X509_check_akid(issuer, akid) != X509_V_OK) {
1751++ g_set_error(err, PV_CRYPTO_ERROR, PV_CRYPTO_ERROR_SKID_AKID_MISMATCH,
1752++ _("AKID mismatch"));
1753++ return -1;
1754++ }
1755++
1756++ if (check_signature_algo_match(X509_get0_pubkey(issuer), cert, err) < 0)
1757++ return -1;
1758++
1759++ if (X509_verify(cert, X509_get0_pubkey(issuer)) != 1) {
1760++ g_set_error(err, PV_CRYPTO_ERROR,
1761++ PV_CRYPTO_ERROR_CERT_SIGNATURE_INVALID,
1762++ _("Signature verification failed"));
1763++ return -1;
1764++ }
1765++
1766++ return 0;
1767++}
1768++
1769++static gboolean is_cert_revoked(X509 *cert, X509_CRL *crl)
1770++{
1771++ X509_REVOKED *revoked = NULL;
1772++ gint rc;
1773++
1774++ if (!cert || !crl)
1775++ g_abort();
1776++
1777++ rc = X509_CRL_get0_by_serial(crl, &revoked,
1778++ (ASN1_INTEGER *)X509_get0_serialNumber(cert));
1779++ if (rc == 0)
1780++ return FALSE;
1781++
1782++ if (revoked)
1783++ return TRUE;
1784++
1785++ return FALSE;
1786++}
1787++
1788++/* Get the first http[s] URL from a DIST_POINT */
1789++static const char *get_first_dp_url(DIST_POINT *dp)
1790++{
1791++ GENERAL_NAMES *general_names;
1792++
1793++ g_assert(dp);
1794++
1795++ if (!dp->distpoint || dp->distpoint->type != 0)
1796++ return NULL;
1797++
1798++ general_names = dp->distpoint->name.fullname;
1799++ for (gint i = 0; i < sk_GENERAL_NAME_num(general_names); i++) {
1800++ GENERAL_NAME *name = sk_GENERAL_NAME_value(general_names, i);
1801++ g_autofree const gchar *uri_str = NULL;
1802++ ASN1_STRING *uri_asn1;
1803++ const gchar *uri_data;
1804++ gint uri_data_len;
1805++ gint type;
1806++
1807++ uri_asn1 = GENERAL_NAME_get0_value(name, &type);
1808++ if (type != GEN_URI)
1809++ continue;
1810++ uri_data_len = ASN1_STRING_length(uri_asn1);
1811++ if (uri_data_len < 0)
1812++ continue;
1813++ uri_data = (const gchar *)ASN1_STRING_get0_data(uri_asn1);
1814++ /* Make sure that uri_str is null-terminated as in general it
1815++ * cannot be assumed that @uri_data is null-terminated.
1816++ */
1817++ uri_str = g_strndup(uri_data,
1818++ (gsize)uri_data_len);
1819++ if (g_str_has_prefix(uri_str, "http://"))
1820++ return uri_data;
1821++ if (g_str_has_prefix(uri_str, "https://"))
1822++ return uri_data;
1823++ }
1824++ return NULL;
1825++}
1826++
1827++static gboolean insert_crl(X509_NAME *name, X509_CRL *crl)
1828++{
1829++ g_autofree gchar *key = NULL;
1830++
1831++ g_assert(name);
1832++
1833++ key = X509_NAME_oneline(name, NULL, 0);
1834++ if (!key)
1835++ g_abort();
1836++ if (X509_CRL_up_ref(crl) != 1)
1837++ g_abort();
1838++ return g_hash_table_insert(cached_crls, g_steal_pointer(&key), crl);
1839++}
1840++
1841++/* Caller is responsible for free'ing */
1842++static X509_CRL *lookup_crl(X509_NAME *name)
1843++{
1844++ g_autoptr(X509_CRL) crl = NULL;
1845++ g_autofree gchar *key = NULL;
1846++
1847++ g_assert(name);
1848++
1849++ key = X509_NAME_oneline(name, NULL, 0);
1850++ if (!key)
1851++ g_abort();
1852++ crl = g_hash_table_lookup(cached_crls, key);
1853++ if (crl) {
1854++ if (X509_CRL_up_ref(crl) != 1)
1855++ g_abort();
1856++ return g_steal_pointer(&crl);
1857++ }
1858++ return NULL;
1859++}
1860++
1861++/* Returns empty stack if no CRL downloaded. */
1862++static STACK_OF_X509_CRL *crls_download_cb(X509_STORE_CTX *ctx, X509_NAME *nm)
1863++{
1864++ g_autoptr(STACK_OF_X509_CRL) crls = NULL;
1865++ g_autoptr(X509_CRL) crl = NULL;
1866++ /* must not be free'd */
1867++ X509 *cert = NULL;
1868++
1869++ crls = sk_X509_CRL_new_null();
1870++ if (!crls)
1871++ g_abort();
1872++ cert = X509_STORE_CTX_get_current_cert(ctx);
1873++ if (!cert)
1874++ g_steal_pointer(&crls);
1875++ g_assert(X509_NAME_cmp(X509_get_issuer_name(cert), nm) == 0);
1876++ crl = lookup_crl(nm);
1877++ if (!crl) {
1878++ /* ignore error */
1879++ crl = load_crl_by_cert(cert, NULL);
1880++ if (!crl)
1881++ return g_steal_pointer(&crls);
1882++ g_assert_true(insert_crl(nm, crl));
1883++ }
1884++ if (sk_X509_CRL_push(crls, g_steal_pointer(&crl)) == 0)
1885++ g_abort();
1886++ return g_steal_pointer(&crls);
1887++}
1888++
1889++void STACK_OF_DIST_POINT_free(STACK_OF_DIST_POINT *stack)
1890++{
1891++ if (!stack)
1892++ return;
1893++
1894++ sk_DIST_POINT_pop_free(stack, DIST_POINT_free);
1895++}
1896++
1897++void STACK_OF_X509_free(STACK_OF_X509 *stack)
1898++{
1899++ if (!stack)
1900++ return;
1901++
1902++ sk_X509_pop_free(stack, X509_free);
1903++}
1904++
1905++void STACK_OF_X509_CRL_free(STACK_OF_X509_CRL *stack)
1906++{
1907++ if (!stack)
1908++ return;
1909++
1910++ sk_X509_CRL_pop_free(stack, X509_CRL_free);
1911++}
1912++
1913++/* Downloaded CRLs have a higher precedence than the CRLs specified on the
1914++ * command line.
1915++ */
1916++static STACK_OF_X509_CRL *crls_cb(X509_STORE_CTX *ctx, X509_NAME *nm)
1917++{
1918++ g_autoptr(STACK_OF_X509_CRL) crls = crls_download_cb(ctx, nm);
1919++
1920++ if (sk_X509_CRL_num(crls) > 0)
1921++ return g_steal_pointer(&crls);
1922++ return X509_STORE_CTX_get1_crls(ctx, nm);
1923++}
1924++
1925++/* Set up CRL lookup with download support */
1926++void store_setup_crl_download(X509_STORE *st)
1927++{
1928++ X509_STORE_set_lookup_crls(st, crls_cb);
1929++}
1930++
1931++/* Download a CRL using the URI specified in the distribution @crldp */
1932++static X509_CRL *load_crl_by_dist_point(DIST_POINT *crldp, GError **err)
1933++{
1934++ const gchar *uri = get_first_dp_url(crldp);
1935++ g_autoptr(X509_CRL) crl = NULL;
1936++
1937++ if (!uri) {
1938++ g_set_error(err, PV_CRYPTO_ERROR, PV_CRYPTO_ERROR_INTERNAL,
1939++ _("no valid URL specified in distribution point"));
1940+ return NULL;
1941+ }
1942+
1943++ if (load_crl_from_web(uri, &crl, err) < 0)
1944++ return NULL;
1945++
1946++ return g_steal_pointer(&crl);
1947++}
1948++
1949++/* This function returns the first X509_CRL found from the CRL distribution
1950++ * points specified in @cert. This function could be optimized by filtering
1951++ * duplicate certificates and/or filtering duplicated URIs.
1952++ */
1953++X509_CRL *load_crl_by_cert(X509 *cert, GError **err)
1954++{
1955++ g_autoptr(STACK_OF_DIST_POINT) crldps = NULL;
1956++ g_autoptr(X509_CRL) ret = NULL;
1957++
1958++ g_assert(cert);
1959++
1960++ crldps = X509_get_ext_d2i(cert, NID_crl_distribution_points, NULL, NULL);
1961++ if (!crldps || sk_DIST_POINT_num(crldps) == 0) {
1962++ g_set_error(err, PV_CRYPTO_ERROR, PV_CRYPTO_ERROR_NO_CRLDP,
1963++ _("no distribution point found"));
1964++ return NULL;
1965++ }
1966++
1967++ for (int i = 0; i < sk_DIST_POINT_num(crldps); i++) {
1968++ DIST_POINT *crldp = sk_DIST_POINT_value(crldps, i);
1969++
1970++ g_assert(crldp);
1971++
1972++ /* ignore error */
1973++ ret = load_crl_by_dist_point(crldp, NULL);
1974++ if (ret)
1975++ return g_steal_pointer(&ret);
1976++ }
1977++
1978++ g_set_error(err, PV_CRYPTO_ERROR, PV_CRYPTO_ERROR_FAILED_DOWNLOAD_CRL,
1979++ _("failed to download CRL"));
1980++ return NULL;
1981++}
1982++
1983++STACK_OF_X509_CRL *try_load_crls_by_certs(GSList *certs_with_path)
1984++{
1985++ g_autoptr(STACK_OF_X509_CRL) ret = sk_X509_CRL_new_null();
1986++ if (!ret)
1987++ g_abort();
1988++
1989++ for (GSList *iterator = certs_with_path; iterator;
1990++ iterator = iterator->next) {
1991++ x509_with_path *cert_with_path = iterator->data;
1992++ X509 *cert = cert_with_path->cert;
1993++ g_autoptr(X509_CRL) crl = NULL;
1994++
1995++ g_assert(cert);
1996++
1997++ /* ignore error */
1998++ crl = load_crl_by_cert(cert, NULL);
1999++ if (!crl)
2000++ continue;
2001++
2002++ if (sk_X509_CRL_push(ret, g_steal_pointer(&crl)) == 0)
2003++ g_abort();
2004++ }
2005++
2006++ return g_steal_pointer(&ret);
2007++}
2008++
2009++/* Assumptions are that the issuer_crt and issuer_crl is a trusted IBM Z
2010++ * signing certificate/revocation list. This function verifies a host-key
2011++ * document. To do so multiple steps are required:
2012++ *
2013++ * 1. issuer(host_key) == subject(issuer_crt)
2014++ * 2. Signature verification
2015++ * 3. @host_key must not be expired
2016++ * 4. @host_key must not be revoked
2017++ */
2018++gint verify_host_key(X509 *host_key, GSList *issuer_pairs,
2019++ gint verify_flags, int level, GError **err)
2020++{
2021++ g_assert(host_key);
2022++
2023++ const gint exp_security_bits = security_level_to_bits(level);
2024++ EVP_PKEY *pkey = X509_get0_pubkey(host_key);
2025++ gboolean successfully_checked = FALSE;
2026++ gint pkey_security_bits;
2027++
2028++ if (!pkey) {
2029++ g_set_error(err, PV_CRYPTO_ERROR, PV_CRYPTO_ERROR_INTERNAL,
2030++ _("failed to retrieve public key"));
2031++ return -1;
2032++ }
2033++
2034++ /* check key level, if necessary */
2035++ pkey_security_bits = EVP_PKEY_security_bits(pkey);
2036++ if (exp_security_bits > 0 && pkey_security_bits < exp_security_bits) {
2037++ g_set_error(err, PV_CRYPTO_ERROR, PV_CRYPTO_ERROR_VERIFICATION,
2038++ _("not enough bits of security (%d, %d expected)"),
2039++ pkey_security_bits, exp_security_bits);
2040++ return -1;
2041++ }
2042++
2043++ if (!(verify_flags & X509_V_FLAG_NO_CHECK_TIME)) {
2044++ const ASN1_TIME *last = X509_get_notBefore(host_key);
2045++ const ASN1_TIME *next = X509_get_notAfter(host_key);
2046++
2047++ if (!last || !next || check_validity_period(last, next)) {
2048++ g_set_error(err, PV_CRYPTO_ERROR,
2049++ PV_CRYPTO_ERROR_INVALID_VALIDITY_PERIOD,
2050++ _("validity period is not valid"));
2051++ return -1;
2052++ }
2053++ } else {
2054++ verify_flags &= ~X509_V_FLAG_NO_CHECK_TIME;
2055++ }
2056++
2057++ /* Verify that the host_key was issued by a certificate and that it
2058++ * wasn't revoked.
2059++ */
2060++ for (GSList *iterator = issuer_pairs; iterator;
2061++ iterator = iterator->next) {
2062++ const x509_pair *pair = iterator->data;
2063++ STACK_OF_X509_CRL *issuer_crls = NULL;
2064++ X509 *issuer_cert = NULL;
2065++
2066++ g_assert(pair);
2067++
2068++ issuer_cert = pair->cert;
2069++ issuer_crls = pair->crls;
2070++
2071++ g_assert(issuer_cert);
2072++
2073++ /* Verify that the issuer(host_key) == subject(issuer_cert) and
2074++ * that the signature is valid
2075++ */
2076++ if (check_host_key_issued(host_key, issuer_cert, NULL) < 0)
2077++ continue;
2078++
2079++ /* Check against CRL */
2080++ if (verify_flags & X509_V_FLAG_CRL_CHECK) {
2081++ gboolean crl_checked = FALSE;
2082++
2083++ verify_flags &= ~X509_V_FLAG_CRL_CHECK;
2084++ for (gint i = 0; i < sk_X509_CRL_num(issuer_crls); i++) {
2085++ X509_CRL *issuer_crl =
2086++ sk_X509_CRL_value(issuer_crls, i);
2087++
2088++ g_assert(issuer_crl);
2089++
2090++ if (is_cert_revoked(host_key, issuer_crl)) {
2091++ g_set_error(err, PV_CRYPTO_ERROR,
2092++ PV_CRYPTO_ERROR_CERT_REVOKED,
2093++ _("certificate revoked"));
2094++ return -1;
2095++ }
2096++
2097++ crl_checked = TRUE;
2098++ }
2099++
2100++ if (!crl_checked) {
2101++ g_set_error(err, PV_CRYPTO_ERROR,
2102++ PV_CRYPTO_ERROR_INTERNAL,
2103++ _("no valid CRL found"));
2104++ return -1;
2105++ }
2106++ successfully_checked = TRUE;
2107++ break;
2108++ }
2109++ }
2110++
2111++ if (!successfully_checked) {
2112++ g_set_error(err, PV_CRYPTO_ERROR,
2113++ PV_CRYPTO_ERROR_NO_ISSUER_IBM_Z_FOUND,
2114++ _("no IBM Z signing key that issued this host-key document found"));
2115++ return -1;
2116++ }
2117++
2118++ /* were some unsupported flags specified? */
2119++ g_assert(verify_flags == 0);
2120++ return 0;
2121++}
2122++
2123++EVP_PKEY *read_ec_pubkey_cert(X509 *cert, gint nid,
2124++ GError **err)
2125++{
2126++ g_autoptr(EVP_PKEY) ret = NULL;
2127++
2128+ ret = X509_get_pubkey(cert);
2129+ if (!ret) {
2130+ g_set_error(err, PV_CRYPTO_ERROR, PV_CRYPTO_ERROR_INVALID_PARM,
2131+- _("Failed to get public key from host-key document: '%s'"),
2132+- path);
2133++ _("Failed to get public key from host-key document"));
2134+ return NULL;
2135+ }
2136+
2137+ if (!certificate_uses_correct_curve(ret, nid, err)) {
2138+ g_prefix_error(err,
2139+- _("Failed to load host-key document: '%s': "),
2140+- path);
2141++ _("Host-key document doesn\'t use correct EC curve"));
2142+ return NULL;
2143+ }
2144+
2145+--- a/genprotimg/src/utils/crypto.h
2146++++ b/genprotimg/src/utils/crypto.h
2147+@@ -11,14 +11,18 @@
2148+ #define PV_UTILS_CRYPTO_H
2149+
2150+ #include <glib.h>
2151++#include <openssl/asn1.h>
2152+ #include <openssl/bio.h>
2153+ #include <openssl/bn.h>
2154+ #include <openssl/ec.h>
2155+ #include <openssl/ecdh.h>
2156+ #include <openssl/evp.h>
2157++#include <openssl/ossl_typ.h>
2158+ #include <openssl/rand.h>
2159++#include <openssl/safestack.h>
2160+ #include <openssl/sha.h>
2161+ #include <openssl/x509.h>
2162++#include <openssl/x509v3.h>
2163+ #include <stdint.h>
2164+
2165+ #include "common.h"
2166+@@ -33,6 +37,9 @@
2167+ #define AES_256_XTS_TWEAK_SIZE 16
2168+ #define AES_256_XTS_KEY_SIZE 64
2169+
2170++#define CRL_DOWNLOAD_TIMEOUT_MS 3000
2171++#define CRL_DOWNLOAD_MAX_SIZE (1024 * 1024) /* in bytes */
2172++
2173+ enum PvCryptoMode {
2174+ PV_ENCRYPT,
2175+ PV_DECRYPT,
2176+@@ -40,7 +47,34 @@
2177+
2178+ typedef GSList HostKeyList;
2179+
2180++/* play nice with g_autoptr */
2181++typedef STACK_OF(DIST_POINT) STACK_OF_DIST_POINT;
2182++typedef STACK_OF(X509) STACK_OF_X509;
2183++typedef STACK_OF(X509_CRL) STACK_OF_X509_CRL;
2184++
2185++void STACK_OF_DIST_POINT_free(STACK_OF_DIST_POINT *stack);
2186++void STACK_OF_X509_free(STACK_OF_X509 *stack);
2187++void STACK_OF_X509_CRL_free(STACK_OF_X509_CRL *stack);
2188++
2189++typedef struct {
2190++ X509 *cert;
2191++ const gchar *path;
2192++} x509_with_path;
2193++
2194++x509_with_path *x509_with_path_new(X509 *cert, const gchar *path);
2195++void x509_with_path_free(x509_with_path *cert);
2196++
2197++typedef struct {
2198++ X509 *cert;
2199++ STACK_OF_X509_CRL *crls;
2200++} x509_pair;
2201++
2202++x509_pair *x509_pair_new(X509 **cert, STACK_OF_X509_CRL **crls);
2203++void x509_pair_free(x509_pair *pair);
2204++
2205+ /* Register auto cleanup functions */
2206++WRAPPED_G_DEFINE_AUTOPTR_CLEANUP_FUNC(ASN1_INTEGER, ASN1_INTEGER_free)
2207++WRAPPED_G_DEFINE_AUTOPTR_CLEANUP_FUNC(ASN1_OCTET_STRING, ASN1_OCTET_STRING_free)
2208+ WRAPPED_G_DEFINE_AUTOPTR_CLEANUP_FUNC(BIGNUM, BN_free)
2209+ WRAPPED_G_DEFINE_AUTOPTR_CLEANUP_FUNC(BIO, BIO_free_all)
2210+ WRAPPED_G_DEFINE_AUTOPTR_CLEANUP_FUNC(BN_CTX, BN_CTX_free)
2211+@@ -51,10 +85,18 @@
2212+ WRAPPED_G_DEFINE_AUTOPTR_CLEANUP_FUNC(EVP_MD_CTX, EVP_MD_CTX_free)
2213+ WRAPPED_G_DEFINE_AUTOPTR_CLEANUP_FUNC(EVP_PKEY, EVP_PKEY_free)
2214+ WRAPPED_G_DEFINE_AUTOPTR_CLEANUP_FUNC(EVP_PKEY_CTX, EVP_PKEY_CTX_free)
2215++WRAPPED_G_DEFINE_AUTOPTR_CLEANUP_FUNC(STACK_OF_DIST_POINT, STACK_OF_DIST_POINT_free);
2216++WRAPPED_G_DEFINE_AUTOPTR_CLEANUP_FUNC(STACK_OF_X509, STACK_OF_X509_free);
2217++WRAPPED_G_DEFINE_AUTOPTR_CLEANUP_FUNC(STACK_OF_X509_CRL, STACK_OF_X509_CRL_free);
2218+ WRAPPED_G_DEFINE_AUTOPTR_CLEANUP_FUNC(X509, X509_free)
2219++WRAPPED_G_DEFINE_AUTOPTR_CLEANUP_FUNC(X509_CRL, X509_CRL_free)
2220+ WRAPPED_G_DEFINE_AUTOPTR_CLEANUP_FUNC(X509_LOOKUP, X509_LOOKUP_free)
2221++WRAPPED_G_DEFINE_AUTOPTR_CLEANUP_FUNC(X509_NAME, X509_NAME_free)
2222++WRAPPED_G_DEFINE_AUTOPTR_CLEANUP_FUNC(x509_pair, x509_pair_free)
2223+ WRAPPED_G_DEFINE_AUTOPTR_CLEANUP_FUNC(X509_STORE, X509_STORE_free)
2224+ WRAPPED_G_DEFINE_AUTOPTR_CLEANUP_FUNC(X509_STORE_CTX, X509_STORE_CTX_free)
2225++WRAPPED_G_DEFINE_AUTOPTR_CLEANUP_FUNC(X509_VERIFY_PARAM, X509_VERIFY_PARAM_free)
2226++WRAPPED_G_DEFINE_AUTOPTR_CLEANUP_FUNC(x509_with_path, x509_with_path_free)
2227+
2228+ union cmp_index {
2229+ struct {
2230+@@ -79,8 +121,37 @@
2231+ const Buffer *iv_or_tweak;
2232+ };
2233+
2234+-EVP_PKEY *read_ec_pubkey_cert(X509_STORE *store, gint nid, const gchar *path,
2235+- GError **err);
2236++int check_crl_valid_for_cert(X509_CRL *crl, X509 *cert,
2237++ gint verify_flags, GError **err);
2238++void pv_crypto_init(void);
2239++void pv_crypto_cleanup(void);
2240++const ASN1_OCTET_STRING *get_digicert_assured_id_root_ca_skid(void);
2241++gint verify_host_key(X509 *host_key, GSList *issuer_pairs,
2242++ gint verify_flags, int level, GError **err);
2243++X509 *load_cert_from_file(const char *path, GError **err);
2244++X509_CRL *load_crl_from_file(const gchar *path, GError **err);
2245++GSList *load_certificates(const gchar *const *cert_paths, GError **err);
2246++STACK_OF_X509 *get_x509_stack(const GSList *x509_with_path_list);
2247++X509_STORE *store_setup(const gchar *root_ca_path,
2248++ const gchar * const *crl_paths,
2249++ GError **err);
2250++int store_set_verify_param(X509_STORE *store, GError **err);
2251++X509_CRL *load_crl_by_cert(X509 *cert, GError **err);
2252++STACK_OF_X509_CRL *try_load_crls_by_certs(GSList *certs_with_path);
2253++gint check_chain_parameters(const STACK_OF_X509 *chain,
2254++ const ASN1_OCTET_STRING *skid, GError **err);
2255++X509_NAME *c2b_name(const X509_NAME *name);
2256++
2257++STACK_OF_X509 *delete_ibm_signing_certs(STACK_OF_X509 *certs);
2258++STACK_OF_X509_CRL *store_ctx_find_valid_crls(X509_STORE_CTX *ctx, X509 *cert,
2259++ GError **err);
2260++X509_STORE_CTX *create_store_ctx(X509_STORE *trusted, STACK_OF_X509 *chain,
2261++ GError **err);
2262++gint verify_cert(X509 *cert, X509_STORE_CTX *ctx, GError **err);
2263++X509_CRL *get_first_valid_crl(X509_STORE_CTX *ctx, X509 *cert, GError **err);
2264++void store_setup_crl_download(X509_STORE *st);
2265++EVP_PKEY *read_ec_pubkey_cert(X509 *cert, gint nid, GError **err);
2266++
2267+ Buffer *compute_exchange_key(EVP_PKEY *cust, EVP_PKEY *host, GError **err);
2268+ Buffer *generate_aes_key(guint size, GError **err);
2269+ Buffer *generate_aes_iv(guint size, GError **err);
2270+--- /dev/null
2271++++ b/genprotimg/src/utils/curl.c
2272+@@ -0,0 +1,121 @@
2273++/*
2274++ * Libcurl utils
2275++ *
2276++ * Copyright IBM Corp. 2020
2277++ *
2278++ * s390-tools is free software; you can redistribute it and/or modify
2279++ * it under the terms of the MIT license. See LICENSE for details.
2280++ */
2281++
2282++#include <stdio.h>
2283++#include <glib.h>
2284++#include <glib/gtypes.h>
2285++#include <curl/curl.h>
2286++
2287++#include "lib/zt_common.h"
2288++#include "pv/pv_error.h"
2289++
2290++#include "curl.h"
2291++
2292++struct UserData {
2293++ GByteArray *buffer;
2294++ guint max_size;
2295++};
2296++
2297++static size_t write_callback(char *ptr, size_t size, size_t nmemb, void *userdata)
2298++{
2299++ g_assert(userdata);
2300++ struct UserData *data = (struct UserData *)userdata;
2301++ GByteArray *buffer = data->buffer;
2302++ guint64 actual_size;
2303++ size_t err;
2304++
2305++ g_assert(buffer);
2306++
2307++ if (!g_uint64_checked_mul(&actual_size, size, nmemb))
2308++ g_abort();
2309++
2310++ /* Signal an error condition by returning a amount that differs
2311++ * from the amount passed to the callback. This results in a
2312++ * CURLE_WRITE_ERROR.
2313++ */
2314++ err = actual_size + 1;
2315++
2316++ if (actual_size > G_MAXUINT)
2317++ return err;
2318++
2319++ data->buffer = g_byte_array_append(buffer, (guchar *)ptr, (guint)actual_size);
2320++ if (data->buffer->len > data->max_size)
2321++ return err;
2322++
2323++ return actual_size;
2324++}
2325++
2326++gint curl_init(void)
2327++{
2328++ if (curl_global_init(CURL_GLOBAL_ALL) != 0)
2329++ return -1;
2330++ return 0;
2331++}
2332++
2333++void curl_cleanup(void)
2334++{
2335++ curl_global_cleanup();
2336++}
2337++
2338++GByteArray *curl_download(const gchar *url, long timeout_ms, guint max_size,
2339++ GError **err)
2340++{
2341++ g_autoptr(GByteArray) ret = NULL;
2342++ g_autoptr(CURL) handle = NULL;
2343++ g_autofree gchar *agent = NULL;
2344++ struct UserData userdata;
2345++ CURLcode rc;
2346++
2347++ /* set up curl session */
2348++ handle = curl_easy_init();
2349++ if (!handle)
2350++ g_abort();
2351++
2352++ /* follow redirection */
2353++ rc = curl_easy_setopt(handle, CURLOPT_FOLLOWLOCATION, 1l);
2354++ if (rc != CURLE_OK)
2355++ goto curl_err;
2356++ rc = curl_easy_setopt(handle, CURLOPT_TIMEOUT_MS, timeout_ms);
2357++ if (rc != CURLE_OK)
2358++ goto curl_err;
2359++ rc = curl_easy_setopt(handle, CURLOPT_NOSIGNAL, 1l);
2360++ if (rc != CURLE_OK)
2361++ goto curl_err;
2362++ agent = g_strdup_printf("%s/%s", tool_name, RELEASE_STRING);
2363++ rc = curl_easy_setopt(handle, CURLOPT_USERAGENT, agent);
2364++ if (rc != CURLE_OK)
2365++ goto curl_err;
2366++ rc = curl_easy_setopt(handle, CURLOPT_WRITEFUNCTION, write_callback);
2367++ if (rc != CURLE_OK)
2368++ goto curl_err;
2369++ ret = g_byte_array_new();
2370++ userdata.buffer = ret;
2371++ userdata.max_size = max_size;
2372++ rc = curl_easy_setopt(handle, CURLOPT_WRITEDATA, (void *)&userdata);
2373++ if (rc != CURLE_OK)
2374++ goto curl_err;
2375++ rc = curl_easy_setopt(handle, CURLOPT_URL, url);
2376++ if (rc != CURLE_OK)
2377++ goto curl_err;
2378++
2379++ rc = curl_easy_perform(handle);
2380++ if (rc != CURLE_OK) {
2381++ g_set_error(err, PV_ERROR, PV_ERROR_DOWNLOAD_FAILED,
2382++ _("download failed: %s"), curl_easy_strerror(rc));
2383++ return NULL;
2384++ }
2385++
2386++ return g_steal_pointer(&ret);
2387++curl_err:
2388++ g_set_error(err, PV_ERROR,
2389++ PV_ERROR_CURL_INIT_FAILED,
2390++ _("cURL initialization failed: %s"),
2391++ curl_easy_strerror(rc));
2392++ return NULL;
2393++}
2394+--- /dev/null
2395++++ b/genprotimg/src/utils/curl.h
2396+@@ -0,0 +1,25 @@
2397++/*
2398++ * Libcurl utils
2399++ *
2400++ * Copyright IBM Corp. 2020
2401++ *
2402++ * s390-tools is free software; you can redistribute it and/or modify
2403++ * it under the terms of the MIT license. See LICENSE for details.
2404++ */
2405++
2406++#ifndef PV_UTILS_LIBCURL_H
2407++#define PV_UTILS_LIBCURL_H
2408++
2409++#include <glib.h>
2410++#include <curl/curl.h>
2411++
2412++#include "common.h"
2413++
2414++WRAPPED_G_DEFINE_AUTOPTR_CLEANUP_FUNC(CURL, curl_easy_cleanup)
2415++
2416++GByteArray *curl_download(const gchar *url, long timeout_ms, guint max_size,
2417++ GError **err);
2418++gint curl_init(void);
2419++void curl_cleanup(void);
2420++
2421++#endif /* PV_UTILS_LIBCURL_H */
2422diff --git a/debian/patches/0002-genprotimg-add-missing-return.patch b/debian/patches/0002-genprotimg-add-missing-return.patch
2423new file mode 100644
2424index 0000000..1fb7a3c
2425--- /dev/null
2426+++ b/debian/patches/0002-genprotimg-add-missing-return.patch
2427@@ -0,0 +1,42 @@
2428+genprotimg: add missing return
2429+
2430+This should be no problem (if OpenSSL works correctly), because
2431+`crls_download_cb` is only called out of the verification context of
2432+OpenSSL. It's used to look up a CRL and therefore
2433+X509_STORE_CTX_get_current_cert should always return a certificate
2434+since X509_STORE_CTX_get_current_cert returns NULL only if an error
2435+has occurred during the verification (see
2436+https://www.openssl.org/docs/man1.1.0/man3/X509_STORE_CTX_get_current_cert.html).
2437+
2438+Fixes: 074de1e14ed7 ("genprotimg: add host-key document verification support")
2439+Signed-off-by: Marc Hartmayer <mhartmay@linux.ibm.com>
2440+Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
2441+
2442+Author: Marc Hartmayer <mhartmay@linux.ibm.com>
2443+Origin: upstream, https://github.com/ibm-s390-linux/s390-tools/commit/7827a791c98dbf14f7e5dfd1c9ea14365cac6272
2444+Bug-IBM: Bugzilla 194437
2445+Bug-Ubuntu: https://bugs.launchpad.net/bugs/1942908
2446+Applied-Upstream: 2.17.0
2447+Reviewed-by: Frank Heimes <frank.heimes@canonical.com>
2448+Last-Update: 2021-09-20
2449+
2450+---
2451+ genprotimg/src/utils/crypto.c | 2 +-
2452+ 1 file changed, 1 insertion(+), 1 deletion(-)
2453+
2454+diff --git a/genprotimg/src/utils/crypto.c b/genprotimg/src/utils/crypto.c
2455+index 0f774ebe..44facc28 100644
2456+--- a/genprotimg/src/utils/crypto.c
2457++++ b/genprotimg/src/utils/crypto.c
2458+@@ -1475,7 +1475,7 @@ static STACK_OF_X509_CRL *crls_download_cb(X509_STORE_CTX *ctx, X509_NAME *nm)
2459+ g_abort();
2460+ cert = X509_STORE_CTX_get_current_cert(ctx);
2461+ if (!cert)
2462+- g_steal_pointer(&crls);
2463++ return g_steal_pointer(&crls);
2464+ g_assert(X509_NAME_cmp(X509_get_issuer_name(cert), nm) == 0);
2465+ crl = lookup_crl(nm);
2466+ if (!crl) {
2467+--
2468+2.25.1
2469+
2470diff --git a/debian/patches/0003-genprotimg-check-return-value-of-BIO_reset.patch b/debian/patches/0003-genprotimg-check-return-value-of-BIO_reset.patch
2471new file mode 100644
2472index 0000000..7792491
2473--- /dev/null
2474+++ b/debian/patches/0003-genprotimg-check-return-value-of-BIO_reset.patch
2475@@ -0,0 +1,70 @@
2476+genprotimg: check return value of BIO_reset
2477+
2478+Add missing return value checks for BIO_reset. Unfortunately, the OpenSSL
2479+documentation says:
2480+
2481+"BIO_reset() normally returns 1 for success and 0 or -1 for failure. File BIOs
2482+are an exception, they return 0 for success and -1 for failure."
2483+
2484+Github-ID: https://github.com/ibm-s390-linux/s390-tools/issues/112
2485+Reviewed-by: Patrick Steuer <patrick.steuer@de.ibm.com>
2486+Signed-off-by: Marc Hartmayer <mhartmay@linux.ibm.com>
2487+Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
2488+
2489+Author: Marc Hartmayer <mhartmay@linux.ibm.com>
2490+Origin: upstream, https://github.com/ibm-s390-linux/s390-tools/commit/d90344a2d5ca3a0caacf7d0c12f981be86862d8c
2491+Bug-IBM: Bugzilla 194437
2492+Bug-Ubuntu: https://bugs.launchpad.net/bugs/1942908
2493+Applied-Upstream: 2.17.0
2494+Reviewed-by: Frank Heimes <frank.heimes@canonical.com>
2495+Last-Update: 2021-09-20
2496+
2497+---
2498+ genprotimg/src/utils/crypto.c | 14 ++++++++++++--
2499+ 1 file changed, 12 insertions(+), 2 deletions(-)
2500+
2501+diff --git a/genprotimg/src/utils/crypto.c b/genprotimg/src/utils/crypto.c
2502+index 81367b23..36379f06 100644
2503+--- a/genprotimg/src/utils/crypto.c
2504++++ b/genprotimg/src/utils/crypto.c
2505+@@ -440,10 +440,14 @@ static int check_signature_algo_match(const EVP_PKEY *pkey, const X509 *subject,
2506+ static X509_CRL *load_crl_from_bio(BIO *bio)
2507+ {
2508+ g_autoptr(X509_CRL) crl = PEM_read_bio_X509_CRL(bio, NULL, 0, NULL);
2509++ gint rc;
2510++
2511+ if (crl)
2512+ return g_steal_pointer(&crl);
2513+ ERR_clear_error();
2514+- BIO_reset(bio);
2515++ rc = BIO_reset(bio);
2516++ if (rc != 1 || (rc != 0 && BIO_method_type(bio) == BIO_TYPE_FILE))
2517++ return NULL;
2518+
2519+ /* maybe the CRL is stored in DER format */
2520+ crl = d2i_X509_CRL_bio(bio, NULL);
2521+@@ -514,6 +518,7 @@ X509 *load_cert_from_file(const char *path, GError **err)
2522+ {
2523+ g_autoptr(BIO) bio = bio_read_from_file(path);
2524+ g_autoptr(X509) cert = NULL;
2525++ gint rc;
2526+
2527+ if (!bio) {
2528+ g_set_error(err, PV_CRYPTO_ERROR,
2529+@@ -526,7 +531,12 @@ X509 *load_cert_from_file(const char *path, GError **err)
2530+ if (cert)
2531+ return g_steal_pointer(&cert);
2532+ ERR_clear_error();
2533+- BIO_reset(bio);
2534++ rc = BIO_reset(bio);
2535++ if (rc != 1 || (rc != 0 && BIO_method_type(bio) == BIO_TYPE_FILE)) {
2536++ g_set_error(err, PV_CRYPTO_ERROR, PV_CRYPTO_ERROR_READ_CERTIFICATE,
2537++ _("unable to load certificate: '%s'"), path);
2538++ return NULL;
2539++ }
2540+
2541+ /* maybe the certificate is stored in DER format */
2542+ cert = d2i_X509_bio(bio, NULL);
2543+--
2544+2.25.1
2545+
2546diff --git a/debian/patches/series b/debian/patches/series
2547index 7959b7f..f24af1a 100644
2548--- a/debian/patches/series
2549+++ b/debian/patches/series
2550@@ -114,3 +114,6 @@ s390-tools-sru-lp1903984-focal.patch
2551 s390-tools-sru-lp1908371-focal.patch
2552 s390-tools-sru-lp1902179-focal.patch
2553 0001-zdev-Add-build-option-to-update-initial-RAM-disk-by-default.patch
2554+0001-genprotimg-add-host-key-document-verification.patch
2555+0002-genprotimg-add-missing-return.patch
2556+0003-genprotimg-check-return-value-of-BIO_reset.patch

Subscribers

People subscribed via source and target branches