// VerifyChain verifies the certificate chain in chain // according to the verification options given as opts. bool X509VerifierPrivate::VerifyChain(std::vector<X509Certificate> chain, const X509VerifierOptions &opts) { bool status = false; X509_STORE_CTX *ctx = X509_STORE_CTX_new(); STACK_OF(X509) *untrusted = sk_X509_new_null(); // Ensure that we have a chain to check on. if (chain.empty()) { goto out; } // If we've been passed a DNS name in opts, // we should check whether the leaf certificate // matches that before doing the more expensive // checks. if (!opts.dns_name.empty()) { if (!X509HostnameVerifier::VerifyHostname(chain.at(0), opts.dns_name)) { std::cerr << "X509VerifierPrivate - hostname verification failed" << std::endl; goto out; } } // Extract our chain into the format that OpenSSL // expects for verification. for (X509Certificate &cert : chain) { X509 *cur = cert.dptr_->AsOpenSSLX509(); sk_X509_push(untrusted, cur); } // Set up the X509_STORE_CTX to verify according to opts. X509_STORE_CTX_init(ctx, store_, sk_X509_value(untrusted, 0), untrusted); // If a time is not specified in opts, use the current system time. if (opts.time == 0) { X509_STORE_CTX_set_time(ctx, 0, std::time(nullptr)); } else { X509_STORE_CTX_set_time(ctx, 0, opts.time); } // If a dns_name is specified in opts, use the SSL server policy. if (!opts.dns_name.empty()) { X509_STORE_CTX_set_purpose(ctx, X509_PURPOSE_SSL_SERVER); X509_STORE_CTX_set_trust(ctx, X509_TRUST_SSL_SERVER); } if (X509_verify_cert(ctx) == 1) { status = true; } else { std::cerr << "X509VerifierPrivate - verification error: " << X509_verify_cert_error_string(ctx->error) << std::endl; } out: sk_X509_pop_free(untrusted, X509_free); X509_STORE_CTX_free(ctx); return status; }
/* * call-seq: * storectx.time = time => time */ static VALUE ossl_x509stctx_set_time(VALUE self, VALUE time) { X509_STORE_CTX *store; long t; t = NUM2LONG(rb_Integer(time)); GetX509StCtx(self, store); X509_STORE_CTX_set_time(store, 0, t); return time; }
/** * xmlSecOpenSSLX509StoreVerify: * @store: the pointer to X509 key data store klass. * @certs: the untrusted certificates stack. * @crls: the crls stack. * @keyInfoCtx: the pointer to <dsig:KeyInfo/> element processing context. * * Verifies @certs list. * * Returns: pointer to the first verified certificate from @certs. */ X509* xmlSecOpenSSLX509StoreVerify(xmlSecKeyDataStorePtr store, XMLSEC_STACK_OF_X509* certs, XMLSEC_STACK_OF_X509_CRL* crls, xmlSecKeyInfoCtx* keyInfoCtx) { xmlSecOpenSSLX509StoreCtxPtr ctx; STACK_OF(X509)* certs2 = NULL; STACK_OF(X509_CRL)* crls2 = NULL; X509 * res = NULL; X509 * cert; X509 * err_cert = NULL; X509_STORE_CTX *xsc; char buf[256]; int err = 0; int i; int ret; xmlSecAssert2(xmlSecKeyDataStoreCheckId(store, xmlSecOpenSSLX509StoreId), NULL); xmlSecAssert2(certs != NULL, NULL); xmlSecAssert2(keyInfoCtx != NULL, NULL); xsc = X509_STORE_CTX_new(); if(xsc == NULL) { xmlSecOpenSSLError(xmlSecKeyDataStoreGetName(store), "X509_STORE_CTX_new"); goto done; } ctx = xmlSecOpenSSLX509StoreGetCtx(store); xmlSecAssert2(ctx != NULL, NULL); xmlSecAssert2(ctx->xst != NULL, NULL); /* dup certs */ certs2 = sk_X509_dup(certs); if(certs2 == NULL) { xmlSecOpenSSLError(xmlSecKeyDataStoreGetName(store), "sk_X509_dup"); goto done; } /* add untrusted certs from the store */ if(ctx->untrusted != NULL) { for(i = 0; i < sk_X509_num(ctx->untrusted); ++i) { ret = sk_X509_push(certs2, sk_X509_value(ctx->untrusted, i)); if(ret < 1) { xmlSecOpenSSLError(xmlSecKeyDataStoreGetName(store), "sk_X509_push"); goto done; } } } /* dup crls but remove all non-verified */ if(crls != NULL) { crls2 = sk_X509_CRL_dup(crls); if(crls2 == NULL) { xmlSecOpenSSLError(xmlSecKeyDataStoreGetName(store), "sk_X509_CRL_dup"); goto done; } for(i = 0; i < sk_X509_CRL_num(crls2); ) { ret = xmlSecOpenSSLX509VerifyCRL(ctx->xst, sk_X509_CRL_value(crls2, i)); if(ret == 1) { ++i; } else if(ret == 0) { (void)sk_X509_CRL_delete(crls2, i); } else { xmlSecError(XMLSEC_ERRORS_HERE, xmlSecErrorsSafeString(xmlSecKeyDataStoreGetName(store)), "xmlSecOpenSSLX509VerifyCRL", XMLSEC_ERRORS_R_XMLSEC_FAILED, XMLSEC_ERRORS_NO_MESSAGE); goto done; } } } /* remove all revoked certs */ for(i = 0; i < sk_X509_num(certs2);) { cert = sk_X509_value(certs2, i); if(crls2 != NULL) { ret = xmlSecOpenSSLX509VerifyCertAgainstCrls(crls2, cert); if(ret == 0) { (void)sk_X509_delete(certs2, i); continue; } else if(ret != 1) { xmlSecError(XMLSEC_ERRORS_HERE, xmlSecErrorsSafeString(xmlSecKeyDataStoreGetName(store)), "xmlSecOpenSSLX509VerifyCertAgainstCrls", XMLSEC_ERRORS_R_XMLSEC_FAILED, XMLSEC_ERRORS_NO_MESSAGE); goto done; } } if(ctx->crls != NULL) { ret = xmlSecOpenSSLX509VerifyCertAgainstCrls(ctx->crls, cert); if(ret == 0) { (void)sk_X509_delete(certs2, i); continue; } else if(ret != 1) { xmlSecError(XMLSEC_ERRORS_HERE, xmlSecErrorsSafeString(xmlSecKeyDataStoreGetName(store)), "xmlSecOpenSSLX509VerifyCertAgainstCrls", XMLSEC_ERRORS_R_XMLSEC_FAILED, XMLSEC_ERRORS_NO_MESSAGE); goto done; } } ++i; } /* get one cert after another and try to verify */ for(i = 0; i < sk_X509_num(certs2); ++i) { cert = sk_X509_value(certs2, i); if(xmlSecOpenSSLX509FindNextChainCert(certs2, cert) == NULL) { ret = X509_STORE_CTX_init(xsc, ctx->xst, cert, certs2); if(ret != 1) { xmlSecOpenSSLError(xmlSecKeyDataStoreGetName(store), "X509_STORE_CTX_init"); goto done; } if(keyInfoCtx->certsVerificationTime > 0) { X509_STORE_CTX_set_time(xsc, 0, keyInfoCtx->certsVerificationTime); } { X509_VERIFY_PARAM * vpm = NULL; unsigned long vpm_flags = 0; vpm = X509_VERIFY_PARAM_new(); if(vpm == NULL) { xmlSecOpenSSLError(xmlSecKeyDataStoreGetName(store), "X509_VERIFY_PARAM_new"); goto done; } vpm_flags = X509_VERIFY_PARAM_get_flags(vpm); vpm_flags &= (~X509_V_FLAG_CRL_CHECK); if(keyInfoCtx->certsVerificationTime > 0) { vpm_flags |= X509_V_FLAG_USE_CHECK_TIME; X509_VERIFY_PARAM_set_time(vpm, keyInfoCtx->certsVerificationTime); } X509_VERIFY_PARAM_set_depth(vpm, keyInfoCtx->certsVerificationDepth); X509_VERIFY_PARAM_set_flags(vpm, vpm_flags); X509_STORE_CTX_set0_param(xsc, vpm); } ret = X509_verify_cert(xsc); err_cert = X509_STORE_CTX_get_current_cert(xsc); err = X509_STORE_CTX_get_error(xsc); X509_STORE_CTX_cleanup (xsc); if(ret == 1) { res = cert; goto done; } else if(ret < 0) { const char* err_msg; buf[0] = '\0'; X509_NAME_oneline(X509_get_subject_name(err_cert), buf, sizeof buf); err_msg = X509_verify_cert_error_string(err); xmlSecError(XMLSEC_ERRORS_HERE, xmlSecErrorsSafeString(xmlSecKeyDataStoreGetName(store)), "X509_verify_cert", XMLSEC_ERRORS_R_CRYPTO_FAILED, "subj=%s;err=%d;msg=%s", xmlSecErrorsSafeString(buf), err, xmlSecErrorsSafeString(err_msg)); goto done; } else if(ret == 0) { const char* err_msg; buf[0] = '\0'; X509_NAME_oneline(X509_get_subject_name(err_cert), buf, sizeof buf); err_msg = X509_verify_cert_error_string(err); xmlSecError(XMLSEC_ERRORS_HERE, xmlSecErrorsSafeString(xmlSecKeyDataStoreGetName(store)), "X509_verify_cert", XMLSEC_ERRORS_R_CRYPTO_FAILED, "subj=%s;err=%d;msg=%s", xmlSecErrorsSafeString(buf), err, xmlSecErrorsSafeString(err_msg)); } } } /* if we came here then we found nothing. do we have any error? */ if((err != 0) && (err_cert != NULL)) { const char* err_msg; err_msg = X509_verify_cert_error_string(err); switch (err) { case X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT: X509_NAME_oneline(X509_get_issuer_name(err_cert), buf, sizeof buf); xmlSecError(XMLSEC_ERRORS_HERE, xmlSecErrorsSafeString(xmlSecKeyDataStoreGetName(store)), NULL, XMLSEC_ERRORS_R_CERT_ISSUER_FAILED, "err=%d;msg=%s;issuer=%s", err, xmlSecErrorsSafeString(err_msg), xmlSecErrorsSafeString(buf)); break; case X509_V_ERR_CERT_NOT_YET_VALID: case X509_V_ERR_ERROR_IN_CERT_NOT_BEFORE_FIELD: xmlSecError(XMLSEC_ERRORS_HERE, xmlSecErrorsSafeString(xmlSecKeyDataStoreGetName(store)), NULL, XMLSEC_ERRORS_R_CERT_NOT_YET_VALID, "err=%d;msg=%s", err, xmlSecErrorsSafeString(err_msg)); break; case X509_V_ERR_CERT_HAS_EXPIRED: case X509_V_ERR_ERROR_IN_CERT_NOT_AFTER_FIELD: xmlSecError(XMLSEC_ERRORS_HERE, xmlSecErrorsSafeString(xmlSecKeyDataStoreGetName(store)), NULL, XMLSEC_ERRORS_R_CERT_HAS_EXPIRED, "err=%d;msg=%s", err, xmlSecErrorsSafeString(err_msg)); break; default: xmlSecError(XMLSEC_ERRORS_HERE, xmlSecErrorsSafeString(xmlSecKeyDataStoreGetName(store)), NULL, XMLSEC_ERRORS_R_CERT_VERIFY_FAILED, "err=%d;msg=%s", err, xmlSecErrorsSafeString(err_msg)); } } done: if(certs2 != NULL) { sk_X509_free(certs2); } if(crls2 != NULL) { sk_X509_CRL_free(crls2); } if(xsc != NULL) { X509_STORE_CTX_free(xsc); } return(res); }
s2n_cert_validation_code s2n_x509_validator_validate_cert_chain(struct s2n_x509_validator *validator, struct s2n_connection *conn, uint8_t *cert_chain_in, uint32_t cert_chain_len, s2n_cert_type *cert_type, struct s2n_pkey *public_key_out) { if (!validator->skip_cert_validation && !s2n_x509_trust_store_has_certs(validator->trust_store)) { return S2N_CERT_ERR_UNTRUSTED; } DEFER_CLEANUP(X509_STORE_CTX *ctx = NULL, X509_STORE_CTX_free_pointer); struct s2n_blob cert_chain_blob = {.data = cert_chain_in, .size = cert_chain_len}; DEFER_CLEANUP(struct s2n_stuffer cert_chain_in_stuffer = {{0}}, s2n_stuffer_free); if (s2n_stuffer_init(&cert_chain_in_stuffer, &cert_chain_blob) < 0) { return S2N_CERT_ERR_INVALID; } if (s2n_stuffer_write(&cert_chain_in_stuffer, &cert_chain_blob) < 0) { return S2N_CERT_ERR_INVALID; } uint32_t certificate_count = 0; X509 *server_cert = NULL; DEFER_CLEANUP(struct s2n_pkey public_key = {{{0}}}, s2n_pkey_free); s2n_pkey_zero_init(&public_key); while (s2n_stuffer_data_available(&cert_chain_in_stuffer) && certificate_count < validator->max_chain_depth) { uint32_t certificate_size = 0; if (s2n_stuffer_read_uint24(&cert_chain_in_stuffer, &certificate_size) < 0) { return S2N_CERT_ERR_INVALID; } if (certificate_size == 0 || certificate_size > s2n_stuffer_data_available(&cert_chain_in_stuffer)) { return S2N_CERT_ERR_INVALID; } struct s2n_blob asn1cert = {0}; asn1cert.data = s2n_stuffer_raw_read(&cert_chain_in_stuffer, certificate_size); asn1cert.size = certificate_size; if (asn1cert.data == NULL) { return S2N_CERT_ERR_INVALID; } const uint8_t *data = asn1cert.data; if (!validator->skip_cert_validation) { /* the cert is der encoded, just convert it. */ server_cert = d2i_X509(NULL, &data, asn1cert.size); if (!server_cert) { return S2N_CERT_ERR_INVALID; } /* add the cert to the chain. */ if (!sk_X509_push(validator->cert_chain, server_cert)) { X509_free(server_cert); return S2N_CERT_ERR_INVALID; } } /* Pull the public key from the first certificate */ if (certificate_count == 0) { if (s2n_asn1der_to_public_key_and_type(&public_key, cert_type, &asn1cert) < 0) { return S2N_CERT_ERR_INVALID; } } certificate_count++; } /* if this occurred we exceeded validator->max_chain_depth */ if (!validator->skip_cert_validation && s2n_stuffer_data_available(&cert_chain_in_stuffer)) { return S2N_CERT_ERR_MAX_CHAIN_DEPTH_EXCEEDED; } if (certificate_count < 1) { return S2N_CERT_ERR_INVALID; } if (!validator->skip_cert_validation) { X509 *leaf = sk_X509_value(validator->cert_chain, 0); if (!leaf) { return S2N_CERT_ERR_INVALID; } if (conn->verify_host_fn && !s2n_verify_host_information(validator, conn, leaf)) { return S2N_CERT_ERR_UNTRUSTED; } /* now that we have a chain, get the store and check against it. */ ctx = X509_STORE_CTX_new(); int op_code = X509_STORE_CTX_init(ctx, validator->trust_store->trust_store, leaf, validator->cert_chain); if (op_code <= 0) { return S2N_CERT_ERR_INVALID; } X509_VERIFY_PARAM *param = X509_STORE_CTX_get0_param(ctx); X509_VERIFY_PARAM_set_depth(param, validator->max_chain_depth); uint64_t current_sys_time = 0; conn->config->wall_clock(conn->config->sys_clock_ctx, ¤t_sys_time); /* this wants seconds not nanoseconds */ time_t current_time = (time_t)(current_sys_time / 1000000000); X509_STORE_CTX_set_time(ctx, 0, current_time); op_code = X509_verify_cert(ctx); if (op_code <= 0) { return S2N_CERT_ERR_UNTRUSTED; } } *public_key_out = public_key; /* Reset the old struct, so we don't clean up public_key_out */ s2n_pkey_zero_init(&public_key); return S2N_CERT_OK; }