nghttpx: Verify OCSP response
At least we should make sure that the OCSP response is targeted to the expected certificate. This is important because we pass the file path to the external script, and if the file is replaced because of renewal, and nghttpx has not reloaded its configuration, the certificate nghttpx has loaded and the one included in the file differ. Verifying the OCSP response detects this, and avoids to send wrong OCSP response.
This commit is contained in:
parent
7f31278c4c
commit
1428a5e3ae
|
@ -620,8 +620,9 @@ void ConnectionHandler::handle_ocsp_complete() {
|
||||||
<< " finished successfully";
|
<< " finished successfully";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (tls::verify_ocsp_response(ssl_ctx, ocsp_.resp.data(),
|
||||||
|
ocsp_.resp.size()) == 0) {
|
||||||
#ifndef OPENSSL_IS_BORINGSSL
|
#ifndef OPENSSL_IS_BORINGSSL
|
||||||
{
|
|
||||||
#ifdef HAVE_ATOMIC_STD_SHARED_PTR
|
#ifdef HAVE_ATOMIC_STD_SHARED_PTR
|
||||||
std::atomic_store_explicit(
|
std::atomic_store_explicit(
|
||||||
&tls_ctx_data->ocsp_data,
|
&tls_ctx_data->ocsp_data,
|
||||||
|
@ -632,10 +633,10 @@ void ConnectionHandler::handle_ocsp_complete() {
|
||||||
tls_ctx_data->ocsp_data =
|
tls_ctx_data->ocsp_data =
|
||||||
std::make_shared<std::vector<uint8_t>>(std::move(ocsp_.resp));
|
std::make_shared<std::vector<uint8_t>>(std::move(ocsp_.resp));
|
||||||
#endif // !HAVE_ATOMIC_STD_SHARED_PTR
|
#endif // !HAVE_ATOMIC_STD_SHARED_PTR
|
||||||
}
|
|
||||||
#else // OPENSSL_IS_BORINGSSL
|
#else // OPENSSL_IS_BORINGSSL
|
||||||
SSL_CTX_set_ocsp_response(ssl_ctx, ocsp_.resp.data(), ocsp_.resp.size());
|
SSL_CTX_set_ocsp_response(ssl_ctx, ocsp_.resp.data(), ocsp_.resp.size());
|
||||||
#endif // OPENSSL_IS_BORINGSSL
|
#endif // OPENSSL_IS_BORINGSSL
|
||||||
|
}
|
||||||
|
|
||||||
++ocsp_.next;
|
++ocsp_.next;
|
||||||
proceed_next_cert_ocsp();
|
proceed_next_cert_ocsp();
|
||||||
|
|
|
@ -45,6 +45,7 @@
|
||||||
#include <openssl/x509v3.h>
|
#include <openssl/x509v3.h>
|
||||||
#include <openssl/rand.h>
|
#include <openssl/rand.h>
|
||||||
#include <openssl/dh.h>
|
#include <openssl/dh.h>
|
||||||
|
#include <openssl/ocsp.h>
|
||||||
|
|
||||||
#include <nghttp2/nghttp2.h>
|
#include <nghttp2/nghttp2.h>
|
||||||
|
|
||||||
|
@ -1818,6 +1819,84 @@ int proto_version_from_string(const StringRef &v) {
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int verify_ocsp_response(SSL_CTX *ssl_ctx, const uint8_t *ocsp_resp,
|
||||||
|
size_t ocsp_resplen) {
|
||||||
|
#if !defined(LIBRESSL_VERSION_NUMBER) && OPENSSL_VERSION_NUMBER >= 0x10002000L
|
||||||
|
int rv;
|
||||||
|
|
||||||
|
STACK_OF(X509) * chain_certs;
|
||||||
|
SSL_CTX_get0_chain_certs(ssl_ctx, &chain_certs);
|
||||||
|
|
||||||
|
auto resp = d2i_OCSP_RESPONSE(nullptr, &ocsp_resp, ocsp_resplen);
|
||||||
|
if (resp == nullptr) {
|
||||||
|
LOG(ERROR) << "d2i_OCSP_RESPONSE failed";
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
auto resp_deleter = defer(OCSP_RESPONSE_free, resp);
|
||||||
|
|
||||||
|
ERR_clear_error();
|
||||||
|
|
||||||
|
auto bs = OCSP_response_get1_basic(resp);
|
||||||
|
if (bs == nullptr) {
|
||||||
|
LOG(ERROR) << "OCSP_response_get1_basic failed: "
|
||||||
|
<< ERR_error_string(ERR_get_error(), nullptr);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
auto bs_deleter = defer(OCSP_BASICRESP_free, bs);
|
||||||
|
|
||||||
|
ERR_clear_error();
|
||||||
|
|
||||||
|
rv = OCSP_basic_verify(bs, chain_certs, nullptr, OCSP_TRUSTOTHER);
|
||||||
|
|
||||||
|
if (rv != 1) {
|
||||||
|
LOG(ERROR) << "OCSP_basic_verify failed: "
|
||||||
|
<< ERR_error_string(ERR_get_error(), nullptr);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto sresp = OCSP_resp_get0(bs, 0);
|
||||||
|
if (sresp == nullptr) {
|
||||||
|
LOG(ERROR) << "OCSP response verification failed: no single response";
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
#if OPENSSL_1_1_API
|
||||||
|
auto certid = OCSP_SINGLERESP_get0_id(sresp);
|
||||||
|
#else // !OPENSSL_1_1_API
|
||||||
|
auto certid = sresp->certId;
|
||||||
|
#endif // !OPENSSL_1_1_API
|
||||||
|
assert(certid != nullptr);
|
||||||
|
|
||||||
|
ASN1_INTEGER *serial;
|
||||||
|
rv = OCSP_id_get0_info(nullptr, nullptr, nullptr, &serial,
|
||||||
|
const_cast<OCSP_CERTID *>(certid));
|
||||||
|
if (rv != 1) {
|
||||||
|
LOG(ERROR) << "OCSP_id_get0_info failed";
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (serial == nullptr) {
|
||||||
|
LOG(ERROR) << "OCSP response does not contain serial number";
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto cert = SSL_CTX_get0_certificate(ssl_ctx);
|
||||||
|
auto cert_serial = X509_get_serialNumber(cert);
|
||||||
|
|
||||||
|
if (ASN1_INTEGER_cmp(cert_serial, serial)) {
|
||||||
|
LOG(ERROR) << "OCSP verification serial numbers do not match";
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (LOG_ENABLED(INFO)) {
|
||||||
|
LOG(INFO) << "OCSP verification succeeded";
|
||||||
|
}
|
||||||
|
#endif // !defined(LIBRESSL_VERSION_NUMBER) && OPENSSL_VERSION_NUMBER >=
|
||||||
|
// 0x10002000L
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace tls
|
} // namespace tls
|
||||||
|
|
||||||
} // namespace shrpx
|
} // namespace shrpx
|
||||||
|
|
|
@ -264,6 +264,11 @@ X509 *load_certificate(const char *filename);
|
||||||
// TLS version string.
|
// TLS version string.
|
||||||
int proto_version_from_string(const StringRef &v);
|
int proto_version_from_string(const StringRef &v);
|
||||||
|
|
||||||
|
// Verifies OCSP response |ocsp_resp| of length |ocsp_resplen|. This
|
||||||
|
// function returns 0 if it succeeds, or -1.
|
||||||
|
int verify_ocsp_response(SSL_CTX *ssl_ctx, const uint8_t *ocsp_resp,
|
||||||
|
size_t ocsp_resplen);
|
||||||
|
|
||||||
} // namespace tls
|
} // namespace tls
|
||||||
|
|
||||||
} // namespace shrpx
|
} // namespace shrpx
|
||||||
|
|
Loading…
Reference in New Issue