Merge ~fheimes/ubuntu/+source/s390-tools:s390-tools-sru-lp1942908-focal into ubuntu/+source/s390-tools:ubuntu/focal-devel
- Git
- lp:~fheimes/ubuntu/+source/s390-tools
- s390-tools-sru-lp1942908-focal
- Merge into ubuntu/focal-devel
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) |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Lukas Märdian (community) | Approve | ||
Review via email: mp+409308@code.launchpad.net |
Commit message
Description of the change
s390-tools-
* 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-
- 7827a79 d/p/0002-
- d90344a d/p/0003-
Added additional build dependency libcurl4-
needed by d/p/0001-
(LP: #1942908)
Test build is available here:
https:/
or
https:/
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
1 | diff --git a/debian/changelog b/debian/changelog |
2 | index 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 |
24 | diff --git a/debian/control b/debian/control |
25 | index 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 | |
37 | diff --git a/debian/patches/0001-genprotimg-add-host-key-document-verification.patch b/debian/patches/0001-genprotimg-add-host-key-document-verification.patch |
38 | new file mode 100644 |
39 | index 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 */ |
2422 | diff --git a/debian/patches/0002-genprotimg-add-missing-return.patch b/debian/patches/0002-genprotimg-add-missing-return.patch |
2423 | new file mode 100644 |
2424 | index 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 | + |
2470 | diff --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 |
2471 | new file mode 100644 |
2472 | index 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 | + |
2546 | diff --git a/debian/patches/series b/debian/patches/series |
2547 | index 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 |
+1