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 } 342