1 /* 2 * Copyright 2015-2021 The OpenSSL Project Authors. All Rights Reserved. 3 * 4 * Licensed under the OpenSSL license (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 <openssl/crypto.h> 12 #include <openssl/bio.h> 13 #include <openssl/x509.h> 14 #include <openssl/pem.h> 15 #include <openssl/err.h> 16 #include "testutil.h" 17 18 static const char *roots_f; 19 static const char *untrusted_f; 20 static const char *bad_f; 21 static const char *good_f; 22 23 static X509 *load_cert_pem(const char *file) 24 { 25 X509 *cert = NULL; 26 BIO *bio = NULL; 27 28 if (!TEST_ptr(bio = BIO_new(BIO_s_file()))) 29 return NULL; 30 if (TEST_int_gt(BIO_read_filename(bio, file), 0)) 31 (void)TEST_ptr(cert = PEM_read_bio_X509(bio, NULL, NULL, NULL)); 32 33 BIO_free(bio); 34 return cert; 35 } 36 37 static STACK_OF(X509) *load_certs_from_file(const char *filename) 38 { 39 STACK_OF(X509) *certs; 40 BIO *bio; 41 X509 *x; 42 43 bio = BIO_new_file(filename, "r"); 44 45 if (bio == NULL) { 46 return NULL; 47 } 48 49 certs = sk_X509_new_null(); 50 if (certs == NULL) { 51 BIO_free(bio); 52 return NULL; 53 } 54 55 ERR_set_mark(); 56 do { 57 x = PEM_read_bio_X509(bio, NULL, 0, NULL); 58 if (x != NULL && !sk_X509_push(certs, x)) { 59 sk_X509_pop_free(certs, X509_free); 60 BIO_free(bio); 61 return NULL; 62 } else if (x == NULL) { 63 /* 64 * We probably just ran out of certs, so ignore any errors 65 * generated 66 */ 67 ERR_pop_to_mark(); 68 } 69 } while (x != NULL); 70 71 BIO_free(bio); 72 73 return certs; 74 } 75 76 /*- 77 * Test for CVE-2015-1793 (Alternate Chains Certificate Forgery) 78 * 79 * Chain is as follows: 80 * 81 * rootCA (self-signed) 82 * | 83 * interCA 84 * | 85 * subinterCA subinterCA (self-signed) 86 * | | 87 * leaf ------------------ 88 * | 89 * bad 90 * 91 * rootCA, interCA, subinterCA, subinterCA (ss) all have CA=TRUE 92 * leaf and bad have CA=FALSE 93 * 94 * subinterCA and subinterCA (ss) have the same subject name and keys 95 * 96 * interCA (but not rootCA) and subinterCA (ss) are in the trusted store 97 * (roots.pem) 98 * leaf and subinterCA are in the untrusted list (untrusted.pem) 99 * bad is the certificate being verified (bad.pem) 100 * 101 * Versions vulnerable to CVE-2015-1793 will fail to detect that leaf has 102 * CA=FALSE, and will therefore incorrectly verify bad 103 * 104 */ 105 static int test_alt_chains_cert_forgery(void) 106 { 107 int ret = 0; 108 int i; 109 X509 *x = NULL; 110 STACK_OF(X509) *untrusted = NULL; 111 BIO *bio = NULL; 112 X509_STORE_CTX *sctx = NULL; 113 X509_STORE *store = NULL; 114 X509_LOOKUP *lookup = NULL; 115 116 store = X509_STORE_new(); 117 if (store == NULL) 118 goto err; 119 120 lookup = X509_STORE_add_lookup(store, X509_LOOKUP_file()); 121 if (lookup == NULL) 122 goto err; 123 if (!X509_LOOKUP_load_file(lookup, roots_f, X509_FILETYPE_PEM)) 124 goto err; 125 126 untrusted = load_certs_from_file(untrusted_f); 127 128 if ((bio = BIO_new_file(bad_f, "r")) == NULL) 129 goto err; 130 131 if ((x = PEM_read_bio_X509(bio, NULL, 0, NULL)) == NULL) 132 goto err; 133 134 sctx = X509_STORE_CTX_new(); 135 if (sctx == NULL) 136 goto err; 137 138 if (!X509_STORE_CTX_init(sctx, store, x, untrusted)) 139 goto err; 140 141 i = X509_verify_cert(sctx); 142 143 if (i != 0 || X509_STORE_CTX_get_error(sctx) != X509_V_ERR_INVALID_CA) 144 goto err; 145 146 /* repeat with X509_V_FLAG_X509_STRICT */ 147 X509_STORE_CTX_cleanup(sctx); 148 X509_STORE_set_flags(store, X509_V_FLAG_X509_STRICT); 149 150 if (!X509_STORE_CTX_init(sctx, store, x, untrusted)) 151 goto err; 152 153 i = X509_verify_cert(sctx); 154 155 if (i == 0 && X509_STORE_CTX_get_error(sctx) == X509_V_ERR_INVALID_CA) 156 /* This is the result we were expecting: Test passed */ 157 ret = 1; 158 159 err: 160 X509_STORE_CTX_free(sctx); 161 X509_free(x); 162 BIO_free(bio); 163 sk_X509_pop_free(untrusted, X509_free); 164 X509_STORE_free(store); 165 return ret; 166 } 167 168 static int test_store_ctx(void) 169 { 170 X509_STORE_CTX *sctx = NULL; 171 X509 *x = NULL; 172 BIO *bio = NULL; 173 int testresult = 0, ret; 174 175 bio = BIO_new_file(bad_f, "r"); 176 if (bio == NULL) 177 goto err; 178 179 x = PEM_read_bio_X509(bio, NULL, 0, NULL); 180 if (x == NULL) 181 goto err; 182 183 sctx = X509_STORE_CTX_new(); 184 if (sctx == NULL) 185 goto err; 186 187 if (!X509_STORE_CTX_init(sctx, NULL, x, NULL)) 188 goto err; 189 190 /* Verifying a cert where we have no trusted certs should fail */ 191 ret = X509_verify_cert(sctx); 192 193 if (ret == 0) { 194 /* This is the result we were expecting: Test passed */ 195 testresult = 1; 196 } 197 198 err: 199 X509_STORE_CTX_free(sctx); 200 X509_free(x); 201 BIO_free(bio); 202 return testresult; 203 } 204 205 static int test_self_signed(const char *filename, int expected) 206 { 207 X509 *cert = load_cert_pem(filename); 208 STACK_OF(X509) *trusted = sk_X509_new_null(); 209 X509_STORE_CTX *ctx = X509_STORE_CTX_new(); 210 int ret; 211 212 ret = TEST_ptr(cert) 213 && TEST_true(sk_X509_push(trusted, cert)) 214 && TEST_true(X509_STORE_CTX_init(ctx, NULL, cert, NULL)); 215 X509_STORE_CTX_set0_trusted_stack(ctx, trusted); 216 ret = ret && TEST_int_eq(X509_verify_cert(ctx), expected); 217 218 X509_STORE_CTX_free(ctx); 219 sk_X509_free(trusted); 220 X509_free(cert); 221 return ret; 222 } 223 224 static int test_self_signed_good(void) 225 { 226 return test_self_signed(good_f, 1); 227 } 228 229 static int test_self_signed_bad(void) 230 { 231 return test_self_signed(bad_f, 0); 232 } 233 234 int setup_tests(void) 235 { 236 if (!TEST_ptr(roots_f = test_get_argument(0)) 237 || !TEST_ptr(untrusted_f = test_get_argument(1)) 238 || !TEST_ptr(bad_f = test_get_argument(2)) 239 || !TEST_ptr(good_f = test_get_argument(3))) { 240 TEST_error("usage: verify_extra_test roots.pem untrusted.pem bad.pem good.pem\n"); 241 return 0; 242 } 243 244 ADD_TEST(test_alt_chains_cert_forgery); 245 ADD_TEST(test_store_ctx); 246 ADD_TEST(test_self_signed_good); 247 ADD_TEST(test_self_signed_bad); 248 return 1; 249 } 250