1 | /*
|
---|
2 | * Copyright 2015-2022 The OpenSSL Project Authors. All Rights Reserved.
|
---|
3 | *
|
---|
4 | * Licensed under the Apache License 2.0 (the "License"). You may not use
|
---|
5 | * this file except in compliance with the License. You can obtain a copy
|
---|
6 | * in the file LICENSE in the source distribution or at
|
---|
7 | * https://www.openssl.org/source/license.html
|
---|
8 | */
|
---|
9 |
|
---|
10 | #include <stdio.h>
|
---|
11 | #include <string.h>
|
---|
12 | #include <openssl/crypto.h>
|
---|
13 | #include <openssl/bio.h>
|
---|
14 | #include <openssl/x509.h>
|
---|
15 | #include <openssl/x509v3.h>
|
---|
16 | #include <openssl/pem.h>
|
---|
17 | #include <openssl/err.h>
|
---|
18 | #include "testutil.h"
|
---|
19 |
|
---|
20 | static const char *certs_dir;
|
---|
21 | static char *root_f = NULL;
|
---|
22 | static char *roots_f = NULL;
|
---|
23 | static char *untrusted_f = NULL;
|
---|
24 | static char *bad_f = NULL;
|
---|
25 | static char *req_f = NULL;
|
---|
26 | static char *sroot_cert = NULL;
|
---|
27 | static char *ca_cert = NULL;
|
---|
28 | static char *ee_cert = NULL;
|
---|
29 |
|
---|
30 | #define load_cert_from_file(file) load_cert_pem(file, NULL)
|
---|
31 |
|
---|
32 | /*-
|
---|
33 | * Test for CVE-2015-1793 (Alternate Chains Certificate Forgery)
|
---|
34 | *
|
---|
35 | * Chain is as follows:
|
---|
36 | *
|
---|
37 | * rootCA (self-signed)
|
---|
38 | * |
|
---|
39 | * interCA
|
---|
40 | * |
|
---|
41 | * subinterCA subinterCA (self-signed)
|
---|
42 | * | |
|
---|
43 | * leaf ------------------
|
---|
44 | * |
|
---|
45 | * bad
|
---|
46 | *
|
---|
47 | * rootCA, interCA, subinterCA, subinterCA (ss) all have CA=TRUE
|
---|
48 | * leaf and bad have CA=FALSE
|
---|
49 | *
|
---|
50 | * subinterCA and subinterCA (ss) have the same subject name and keys
|
---|
51 | *
|
---|
52 | * interCA (but not rootCA) and subinterCA (ss) are in the trusted store
|
---|
53 | * (roots.pem)
|
---|
54 | * leaf and subinterCA are in the untrusted list (untrusted.pem)
|
---|
55 | * bad is the certificate being verified (bad.pem)
|
---|
56 | *
|
---|
57 | * Versions vulnerable to CVE-2015-1793 will fail to detect that leaf has
|
---|
58 | * CA=FALSE, and will therefore incorrectly verify bad
|
---|
59 | *
|
---|
60 | */
|
---|
61 | static int test_alt_chains_cert_forgery(void)
|
---|
62 | {
|
---|
63 | int ret = 0;
|
---|
64 | int i;
|
---|
65 | X509 *x = NULL;
|
---|
66 | STACK_OF(X509) *untrusted = NULL;
|
---|
67 | X509_STORE_CTX *sctx = NULL;
|
---|
68 | X509_STORE *store = NULL;
|
---|
69 | X509_LOOKUP *lookup = NULL;
|
---|
70 |
|
---|
71 | store = X509_STORE_new();
|
---|
72 | if (store == NULL)
|
---|
73 | goto err;
|
---|
74 |
|
---|
75 | lookup = X509_STORE_add_lookup(store, X509_LOOKUP_file());
|
---|
76 | if (lookup == NULL)
|
---|
77 | goto err;
|
---|
78 | if (!X509_LOOKUP_load_file(lookup, roots_f, X509_FILETYPE_PEM))
|
---|
79 | goto err;
|
---|
80 |
|
---|
81 | untrusted = load_certs_pem(untrusted_f);
|
---|
82 |
|
---|
83 | if ((x = load_cert_from_file(bad_f)) == NULL)
|
---|
84 | goto err;
|
---|
85 |
|
---|
86 | sctx = X509_STORE_CTX_new();
|
---|
87 | if (sctx == NULL)
|
---|
88 | goto err;
|
---|
89 |
|
---|
90 | if (!X509_STORE_CTX_init(sctx, store, x, untrusted))
|
---|
91 | goto err;
|
---|
92 |
|
---|
93 | i = X509_verify_cert(sctx);
|
---|
94 |
|
---|
95 | if (i == 0 && X509_STORE_CTX_get_error(sctx) == X509_V_ERR_INVALID_CA) {
|
---|
96 | /* This is the result we were expecting: Test passed */
|
---|
97 | ret = 1;
|
---|
98 | }
|
---|
99 | err:
|
---|
100 | X509_STORE_CTX_free(sctx);
|
---|
101 | X509_free(x);
|
---|
102 | sk_X509_pop_free(untrusted, X509_free);
|
---|
103 | X509_STORE_free(store);
|
---|
104 | return ret;
|
---|
105 | }
|
---|
106 |
|
---|
107 | static int test_distinguishing_id(void)
|
---|
108 | {
|
---|
109 | X509 *x = NULL;
|
---|
110 | int ret = 0;
|
---|
111 | ASN1_OCTET_STRING *v = NULL, *v2 = NULL;
|
---|
112 | char *distid = "this is an ID";
|
---|
113 |
|
---|
114 | x = load_cert_from_file(bad_f);
|
---|
115 | if (x == NULL)
|
---|
116 | goto err;
|
---|
117 |
|
---|
118 | v = ASN1_OCTET_STRING_new();
|
---|
119 | if (v == NULL)
|
---|
120 | goto err;
|
---|
121 |
|
---|
122 | if (!ASN1_OCTET_STRING_set(v, (unsigned char *)distid,
|
---|
123 | (int)strlen(distid))) {
|
---|
124 | ASN1_OCTET_STRING_free(v);
|
---|
125 | goto err;
|
---|
126 | }
|
---|
127 |
|
---|
128 | X509_set0_distinguishing_id(x, v);
|
---|
129 |
|
---|
130 | v2 = X509_get0_distinguishing_id(x);
|
---|
131 | if (!TEST_ptr(v2)
|
---|
132 | || !TEST_int_eq(ASN1_OCTET_STRING_cmp(v, v2), 0))
|
---|
133 | goto err;
|
---|
134 |
|
---|
135 | ret = 1;
|
---|
136 | err:
|
---|
137 | X509_free(x);
|
---|
138 | return ret;
|
---|
139 | }
|
---|
140 |
|
---|
141 | static int test_req_distinguishing_id(void)
|
---|
142 | {
|
---|
143 | X509_REQ *x = NULL;
|
---|
144 | BIO *bio = NULL;
|
---|
145 | int ret = 0;
|
---|
146 | ASN1_OCTET_STRING *v = NULL, *v2 = NULL;
|
---|
147 | char *distid = "this is an ID";
|
---|
148 |
|
---|
149 | bio = BIO_new_file(req_f, "r");
|
---|
150 | if (bio == NULL)
|
---|
151 | goto err;
|
---|
152 |
|
---|
153 | x = PEM_read_bio_X509_REQ(bio, NULL, 0, NULL);
|
---|
154 | if (x == NULL)
|
---|
155 | goto err;
|
---|
156 |
|
---|
157 | v = ASN1_OCTET_STRING_new();
|
---|
158 | if (v == NULL)
|
---|
159 | goto err;
|
---|
160 |
|
---|
161 | if (!ASN1_OCTET_STRING_set(v, (unsigned char *)distid,
|
---|
162 | (int)strlen(distid))) {
|
---|
163 | ASN1_OCTET_STRING_free(v);
|
---|
164 | goto err;
|
---|
165 | }
|
---|
166 |
|
---|
167 | X509_REQ_set0_distinguishing_id(x, v);
|
---|
168 |
|
---|
169 | v2 = X509_REQ_get0_distinguishing_id(x);
|
---|
170 | if (!TEST_ptr(v2)
|
---|
171 | || !TEST_int_eq(ASN1_OCTET_STRING_cmp(v, v2), 0))
|
---|
172 | goto err;
|
---|
173 |
|
---|
174 | ret = 1;
|
---|
175 | err:
|
---|
176 | X509_REQ_free(x);
|
---|
177 | BIO_free(bio);
|
---|
178 | return ret;
|
---|
179 | }
|
---|
180 |
|
---|
181 | static int test_self_signed(const char *filename, int use_trusted, int expected)
|
---|
182 | {
|
---|
183 | X509 *cert = load_cert_from_file(filename); /* may result in NULL */
|
---|
184 | STACK_OF(X509) *trusted = sk_X509_new_null();
|
---|
185 | X509_STORE_CTX *ctx = X509_STORE_CTX_new();
|
---|
186 | int ret;
|
---|
187 |
|
---|
188 | ret = TEST_int_eq(X509_self_signed(cert, 1), expected);
|
---|
189 |
|
---|
190 | if (cert != NULL) {
|
---|
191 | if (use_trusted)
|
---|
192 | ret = ret && TEST_true(sk_X509_push(trusted, cert));
|
---|
193 | ret = ret && TEST_true(X509_STORE_CTX_init(ctx, NULL, cert, NULL));
|
---|
194 | X509_STORE_CTX_set0_trusted_stack(ctx, trusted);
|
---|
195 | ret = ret && TEST_int_eq(X509_verify_cert(ctx), expected);
|
---|
196 | }
|
---|
197 |
|
---|
198 | X509_STORE_CTX_free(ctx);
|
---|
199 | sk_X509_free(trusted);
|
---|
200 | X509_free(cert);
|
---|
201 | return ret;
|
---|
202 | }
|
---|
203 |
|
---|
204 | static int test_self_signed_good(void)
|
---|
205 | {
|
---|
206 | return test_self_signed(root_f, 1, 1);
|
---|
207 | }
|
---|
208 |
|
---|
209 | static int test_self_signed_bad(void)
|
---|
210 | {
|
---|
211 | return test_self_signed(bad_f, 1, 0);
|
---|
212 | }
|
---|
213 |
|
---|
214 | static int test_self_signed_error(void)
|
---|
215 | {
|
---|
216 | return test_self_signed("nonexistent file name", 1, -1);
|
---|
217 | }
|
---|
218 |
|
---|
219 | static int test_store_ctx(void)
|
---|
220 | {
|
---|
221 | /* Verifying a cert where we have no trusted certs should fail */
|
---|
222 | return test_self_signed(bad_f, 0, 0);
|
---|
223 | }
|
---|
224 |
|
---|
225 | static int do_test_purpose(int purpose, int expected)
|
---|
226 | {
|
---|
227 | X509 *eecert = load_cert_from_file(ee_cert); /* may result in NULL */
|
---|
228 | X509 *untrcert = load_cert_from_file(ca_cert);
|
---|
229 | X509 *trcert = load_cert_from_file(sroot_cert);
|
---|
230 | STACK_OF(X509) *trusted = sk_X509_new_null();
|
---|
231 | STACK_OF(X509) *untrusted = sk_X509_new_null();
|
---|
232 | X509_STORE_CTX *ctx = X509_STORE_CTX_new();
|
---|
233 | int testresult = 0;
|
---|
234 |
|
---|
235 | if (!TEST_ptr(eecert)
|
---|
236 | || !TEST_ptr(untrcert)
|
---|
237 | || !TEST_ptr(trcert)
|
---|
238 | || !TEST_ptr(trusted)
|
---|
239 | || !TEST_ptr(untrusted)
|
---|
240 | || !TEST_ptr(ctx))
|
---|
241 | goto err;
|
---|
242 |
|
---|
243 |
|
---|
244 | if (!TEST_true(sk_X509_push(trusted, trcert)))
|
---|
245 | goto err;
|
---|
246 | trcert = NULL;
|
---|
247 | if (!TEST_true(sk_X509_push(untrusted, untrcert)))
|
---|
248 | goto err;
|
---|
249 | untrcert = NULL;
|
---|
250 |
|
---|
251 | if (!TEST_true(X509_STORE_CTX_init(ctx, NULL, eecert, untrusted)))
|
---|
252 | goto err;
|
---|
253 |
|
---|
254 | if (!TEST_true(X509_STORE_CTX_set_purpose(ctx, purpose)))
|
---|
255 | goto err;
|
---|
256 |
|
---|
257 | /*
|
---|
258 | * X509_STORE_CTX_set0_trusted_stack() is bady named. Despite the set0 name
|
---|
259 | * we are still responsible for freeing trusted after we have finished with
|
---|
260 | * it.
|
---|
261 | */
|
---|
262 | X509_STORE_CTX_set0_trusted_stack(ctx, trusted);
|
---|
263 |
|
---|
264 | if (!TEST_int_eq(X509_verify_cert(ctx), expected))
|
---|
265 | goto err;
|
---|
266 |
|
---|
267 | testresult = 1;
|
---|
268 | err:
|
---|
269 | sk_X509_pop_free(trusted, X509_free);
|
---|
270 | sk_X509_pop_free(untrusted, X509_free);
|
---|
271 | X509_STORE_CTX_free(ctx);
|
---|
272 | X509_free(eecert);
|
---|
273 | X509_free(untrcert);
|
---|
274 | X509_free(trcert);
|
---|
275 | return testresult;
|
---|
276 | }
|
---|
277 |
|
---|
278 | static int test_purpose_ssl_client(void)
|
---|
279 | {
|
---|
280 | return do_test_purpose(X509_PURPOSE_SSL_CLIENT, 0);
|
---|
281 | }
|
---|
282 |
|
---|
283 | static int test_purpose_ssl_server(void)
|
---|
284 | {
|
---|
285 | return do_test_purpose(X509_PURPOSE_SSL_SERVER, 1);
|
---|
286 | }
|
---|
287 |
|
---|
288 | static int test_purpose_any(void)
|
---|
289 | {
|
---|
290 | return do_test_purpose(X509_PURPOSE_ANY, 1);
|
---|
291 | }
|
---|
292 |
|
---|
293 | OPT_TEST_DECLARE_USAGE("certs-dir\n")
|
---|
294 |
|
---|
295 | int setup_tests(void)
|
---|
296 | {
|
---|
297 | if (!test_skip_common_options()) {
|
---|
298 | TEST_error("Error parsing test options\n");
|
---|
299 | return 0;
|
---|
300 | }
|
---|
301 |
|
---|
302 | if (!TEST_ptr(certs_dir = test_get_argument(0)))
|
---|
303 | return 0;
|
---|
304 |
|
---|
305 | if (!TEST_ptr(root_f = test_mk_file_path(certs_dir, "rootCA.pem"))
|
---|
306 | || !TEST_ptr(roots_f = test_mk_file_path(certs_dir, "roots.pem"))
|
---|
307 | || !TEST_ptr(untrusted_f = test_mk_file_path(certs_dir, "untrusted.pem"))
|
---|
308 | || !TEST_ptr(bad_f = test_mk_file_path(certs_dir, "bad.pem"))
|
---|
309 | || !TEST_ptr(req_f = test_mk_file_path(certs_dir, "sm2-csr.pem"))
|
---|
310 | || !TEST_ptr(sroot_cert = test_mk_file_path(certs_dir, "sroot-cert.pem"))
|
---|
311 | || !TEST_ptr(ca_cert = test_mk_file_path(certs_dir, "ca-cert.pem"))
|
---|
312 | || !TEST_ptr(ee_cert = test_mk_file_path(certs_dir, "ee-cert.pem")))
|
---|
313 | goto err;
|
---|
314 |
|
---|
315 | ADD_TEST(test_alt_chains_cert_forgery);
|
---|
316 | ADD_TEST(test_store_ctx);
|
---|
317 | ADD_TEST(test_distinguishing_id);
|
---|
318 | ADD_TEST(test_req_distinguishing_id);
|
---|
319 | ADD_TEST(test_self_signed_good);
|
---|
320 | ADD_TEST(test_self_signed_bad);
|
---|
321 | ADD_TEST(test_self_signed_error);
|
---|
322 | ADD_TEST(test_purpose_ssl_client);
|
---|
323 | ADD_TEST(test_purpose_ssl_server);
|
---|
324 | ADD_TEST(test_purpose_any);
|
---|
325 | return 1;
|
---|
326 | err:
|
---|
327 | cleanup_tests();
|
---|
328 | return 0;
|
---|
329 | }
|
---|
330 |
|
---|
331 | void cleanup_tests(void)
|
---|
332 | {
|
---|
333 | OPENSSL_free(root_f);
|
---|
334 | OPENSSL_free(roots_f);
|
---|
335 | OPENSSL_free(untrusted_f);
|
---|
336 | OPENSSL_free(bad_f);
|
---|
337 | OPENSSL_free(req_f);
|
---|
338 | OPENSSL_free(sroot_cert);
|
---|
339 | OPENSSL_free(ca_cert);
|
---|
340 | OPENSSL_free(ee_cert);
|
---|
341 | }
|
---|