nghttpx: Create QUIC SSL_CTX

We choose an easier route to duplicate SSL_CTX for QUIC.
This commit is contained in:
Tatsuhiro Tsujikawa 2021-08-15 22:57:26 +09:00
parent aeb0b0728d
commit ef53db201e
6 changed files with 528 additions and 1 deletions

View File

@ -156,6 +156,17 @@ ConnectionHandler::~ConnectionHandler() {
ev_timer_stop(loop_, &ocsp_timer_); ev_timer_stop(loop_, &ocsp_timer_);
ev_timer_stop(loop_, &disable_acceptor_timer_); ev_timer_stop(loop_, &disable_acceptor_timer_);
for (auto ssl_ctx : quic_all_ssl_ctx_) {
if (ssl_ctx == nullptr) {
continue;
}
auto tls_ctx_data =
static_cast<tls::TLSContextData *>(SSL_CTX_get_app_data(ssl_ctx));
delete tls_ctx_data;
SSL_CTX_free(ssl_ctx);
}
for (auto ssl_ctx : all_ssl_ctx_) { for (auto ssl_ctx : all_ssl_ctx_) {
auto tls_ctx_data = auto tls_ctx_data =
static_cast<tls::TLSContextData *>(SSL_CTX_get_app_data(ssl_ctx)); static_cast<tls::TLSContextData *>(SSL_CTX_get_app_data(ssl_ctx));
@ -209,6 +220,16 @@ int ConnectionHandler::create_single_worker() {
nb_ nb_
#endif // HAVE_NEVERBLEED #endif // HAVE_NEVERBLEED
); );
quic_cert_tree_ = tls::create_cert_lookup_tree();
tls::setup_quic_server_ssl_context(quic_all_ssl_ctx_, quic_indexed_ssl_ctx_,
quic_cert_tree_.get()
#ifdef HAVE_NEVERBLEED
,
nb_
#endif // HAVE_NEVERBLEED
);
auto cl_ssl_ctx = tls::setup_downstream_client_ssl_context( auto cl_ssl_ctx = tls::setup_downstream_client_ssl_context(
#ifdef HAVE_NEVERBLEED #ifdef HAVE_NEVERBLEED
nb_ nb_
@ -217,6 +238,7 @@ int ConnectionHandler::create_single_worker() {
if (cl_ssl_ctx) { if (cl_ssl_ctx) {
all_ssl_ctx_.push_back(cl_ssl_ctx); all_ssl_ctx_.push_back(cl_ssl_ctx);
quic_all_ssl_ctx_.push_back(nullptr);
} }
auto config = get_config(); auto config = get_config();
@ -233,6 +255,7 @@ int ConnectionHandler::create_single_worker() {
tlsconf.cacert, memcachedconf.cert_file, tlsconf.cacert, memcachedconf.cert_file,
memcachedconf.private_key_file, nullptr); memcachedconf.private_key_file, nullptr);
all_ssl_ctx_.push_back(session_cache_ssl_ctx); all_ssl_ctx_.push_back(session_cache_ssl_ctx);
quic_all_ssl_ctx_.push_back(nullptr);
} }
} }
@ -263,6 +286,16 @@ int ConnectionHandler::create_worker_thread(size_t num) {
nb_ nb_
# endif // HAVE_NEVERBLEED # endif // HAVE_NEVERBLEED
); );
quic_cert_tree_ = tls::create_cert_lookup_tree();
tls::setup_quic_server_ssl_context(quic_all_ssl_ctx_, quic_indexed_ssl_ctx_,
quic_cert_tree_.get()
# ifdef HAVE_NEVERBLEED
,
nb_
# endif // HAVE_NEVERBLEED
);
auto cl_ssl_ctx = tls::setup_downstream_client_ssl_context( auto cl_ssl_ctx = tls::setup_downstream_client_ssl_context(
# ifdef HAVE_NEVERBLEED # ifdef HAVE_NEVERBLEED
nb_ nb_
@ -271,6 +304,7 @@ int ConnectionHandler::create_worker_thread(size_t num) {
if (cl_ssl_ctx) { if (cl_ssl_ctx) {
all_ssl_ctx_.push_back(cl_ssl_ctx); all_ssl_ctx_.push_back(cl_ssl_ctx);
quic_all_ssl_ctx_.push_back(nullptr);
} }
auto config = get_config(); auto config = get_config();
@ -294,6 +328,7 @@ int ConnectionHandler::create_worker_thread(size_t num) {
tlsconf.cacert, memcachedconf.cert_file, tlsconf.cacert, memcachedconf.cert_file,
memcachedconf.private_key_file, nullptr); memcachedconf.private_key_file, nullptr);
all_ssl_ctx_.push_back(session_cache_ssl_ctx); all_ssl_ctx_.push_back(session_cache_ssl_ctx);
quic_all_ssl_ctx_.push_back(nullptr);
} }
} }
@ -608,6 +643,7 @@ void ConnectionHandler::handle_ocsp_complete() {
ev_child_stop(loop_, &ocsp_.chldev); ev_child_stop(loop_, &ocsp_.chldev);
assert(ocsp_.next < all_ssl_ctx_.size()); assert(ocsp_.next < all_ssl_ctx_.size());
assert(all_ssl_ctx_.size() == quic_all_ssl_ctx_.size());
auto ssl_ctx = all_ssl_ctx_[ocsp_.next]; auto ssl_ctx = all_ssl_ctx_[ocsp_.next];
auto tls_ctx_data = auto tls_ctx_data =
@ -635,6 +671,29 @@ void ConnectionHandler::handle_ocsp_complete() {
if (tlsconf.ocsp.no_verify || if (tlsconf.ocsp.no_verify ||
tls::verify_ocsp_response(ssl_ctx, ocsp_.resp.data(), tls::verify_ocsp_response(ssl_ctx, ocsp_.resp.data(),
ocsp_.resp.size()) == 0) { ocsp_.resp.size()) == 0) {
// We have list of SSL_CTX with the same certificate in
// quic_all_ssl_ctx_ as well. Some SSL_CTXs are missing there in
// that case we get nullptr.
auto quic_ssl_ctx = quic_all_ssl_ctx_[ocsp_.next];
if (quic_ssl_ctx) {
auto quic_tls_ctx_data = static_cast<tls::TLSContextData *>(
SSL_CTX_get_app_data(quic_ssl_ctx));
#ifndef OPENSSL_IS_BORINGSSL
# ifdef HAVE_ATOMIC_STD_SHARED_PTR
std::atomic_store_explicit(
&quic_tls_ctx_data->ocsp_data,
std::make_shared<std::vector<uint8_t>>(ocsp_.resp),
std::memory_order_release);
# else // !HAVE_ATOMIC_STD_SHARED_PTR
std::lock_guard<std::mutex> g(quic_tls_ctx_data->mu);
quic_tls_ctx_data->ocsp_data =
std::make_shared<std::vector<uint8_t>>(ocsp_.resp);
# endif // !HAVE_ATOMIC_STD_SHARED_PTR
#else // OPENSSL_IS_BORINGSSL
SSL_CTX_set_ocsp_response(ssl_ctx, ocsp_.resp.data(), ocsp_.resp.size());
#endif // OPENSSL_IS_BORINGSSL
}
#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(
@ -815,6 +874,7 @@ SSL_CTX *ConnectionHandler::create_tls_ticket_key_memcached_ssl_ctx() {
nullptr); nullptr);
all_ssl_ctx_.push_back(ssl_ctx); all_ssl_ctx_.push_back(ssl_ctx);
quic_all_ssl_ctx_.push_back(nullptr);
return ssl_ctx; return ssl_ctx;
} }
@ -877,6 +937,11 @@ ConnectionHandler::get_indexed_ssl_ctx(size_t idx) const {
return indexed_ssl_ctx_[idx]; return indexed_ssl_ctx_[idx];
} }
const std::vector<SSL_CTX *> &
ConnectionHandler::get_quic_indexed_ssl_ctx(size_t idx) const {
return quic_indexed_ssl_ctx_[idx];
}
void ConnectionHandler::set_enable_acceptor_on_ocsp_completion(bool f) { void ConnectionHandler::set_enable_acceptor_on_ocsp_completion(bool f) {
enable_acceptor_on_ocsp_completion_ = f; enable_acceptor_on_ocsp_completion_ = f;
} }

View File

@ -159,6 +159,7 @@ public:
SSL_CTX *get_ssl_ctx(size_t idx) const; SSL_CTX *get_ssl_ctx(size_t idx) const;
const std::vector<SSL_CTX *> &get_indexed_ssl_ctx(size_t idx) const; const std::vector<SSL_CTX *> &get_indexed_ssl_ctx(size_t idx) const;
const std::vector<SSL_CTX *> &get_quic_indexed_ssl_ctx(size_t idx) const;
#ifdef HAVE_NEVERBLEED #ifdef HAVE_NEVERBLEED
void set_neverbleed(neverbleed_t *nb); void set_neverbleed(neverbleed_t *nb);
@ -187,6 +188,8 @@ private:
// selection among them are performed by hostname presented by SNI, // selection among them are performed by hostname presented by SNI,
// and signature algorithm presented by client. // and signature algorithm presented by client.
std::vector<std::vector<SSL_CTX *>> indexed_ssl_ctx_; std::vector<std::vector<SSL_CTX *>> indexed_ssl_ctx_;
std::vector<SSL_CTX *> quic_all_ssl_ctx_;
std::vector<std::vector<SSL_CTX *>> quic_indexed_ssl_ctx_;
OCSPUpdateContext ocsp_; OCSPUpdateContext ocsp_;
std::mt19937 &gen_; std::mt19937 &gen_;
// ev_loop for each worker // ev_loop for each worker
@ -203,6 +206,7 @@ private:
// Otherwise, nullptr and workers_ has instances of Worker instead. // Otherwise, nullptr and workers_ has instances of Worker instead.
std::unique_ptr<Worker> single_worker_; std::unique_ptr<Worker> single_worker_;
std::unique_ptr<tls::CertLookupTree> cert_tree_; std::unique_ptr<tls::CertLookupTree> cert_tree_;
std::unique_ptr<tls::CertLookupTree> quic_cert_tree_;
std::unique_ptr<MemcachedDispatcher> tls_ticket_key_memcached_dispatcher_; std::unique_ptr<MemcachedDispatcher> tls_ticket_key_memcached_dispatcher_;
// Current TLS session ticket keys. Note that TLS connection does // Current TLS session ticket keys. Note that TLS connection does
// not refer to this field directly. They use TicketKeys object in // not refer to this field directly. They use TicketKeys object in

View File

@ -30,7 +30,8 @@
namespace shrpx { namespace shrpx {
Http3Upstream::Http3Upstream(ClientHandler *handler) : handler_{handler} {} Http3Upstream::Http3Upstream(ClientHandler *handler)
: handler_{handler}, tls_alert_{0} {}
Http3Upstream::~Http3Upstream() {} Http3Upstream::~Http3Upstream() {}
@ -129,4 +130,21 @@ int Http3Upstream::on_read(const UpstreamAddr *faddr,
return 0; return 0;
} }
int Http3Upstream::on_rx_secret(ngtcp2_crypto_level level,
const uint8_t *secret, size_t secretlen) {
return 0;
}
int Http3Upstream::on_tx_secret(ngtcp2_crypto_level level,
const uint8_t *secret, size_t secretlen) {
return 0;
}
int Http3Upstream::add_crypto_data(ngtcp2_crypto_level level,
const uint8_t *data, size_t datalen) {
return 0;
}
void Http3Upstream::set_tls_alert(uint8_t alert) { tls_alert_ = alert; }
} // namespace shrpx } // namespace shrpx

View File

@ -26,6 +26,9 @@
#define SHRPX_HTTP3_UPSTREAM_H #define SHRPX_HTTP3_UPSTREAM_H
#include "shrpx.h" #include "shrpx.h"
#include <ngtcp2/ngtcp2.h>
#include "shrpx_upstream.h" #include "shrpx_upstream.h"
#include "network.h" #include "network.h"
@ -84,8 +87,19 @@ public:
int on_read(const UpstreamAddr *faddr, const Address &remote_addr, int on_read(const UpstreamAddr *faddr, const Address &remote_addr,
const Address &local_addr, const uint8_t *data, size_t datalen); const Address &local_addr, const uint8_t *data, size_t datalen);
int on_rx_secret(ngtcp2_crypto_level level, const uint8_t *secret,
size_t secretlen);
int on_tx_secret(ngtcp2_crypto_level level, const uint8_t *secret,
size_t secretlen);
int add_crypto_data(ngtcp2_crypto_level level, const uint8_t *data,
size_t datalen);
void set_tls_alert(uint8_t alert);
private: private:
ClientHandler *handler_; ClientHandler *handler_;
uint8_t tls_alert_;
}; };
} // namespace shrpx } // namespace shrpx

View File

@ -51,6 +51,10 @@
#include <nghttp2/nghttp2.h> #include <nghttp2/nghttp2.h>
#include <ngtcp2/ngtcp2.h>
#include <ngtcp2/ngtcp2_crypto.h>
#include <ngtcp2/ngtcp2_crypto_openssl.h>
#include "shrpx_log.h" #include "shrpx_log.h"
#include "shrpx_client_handler.h" #include "shrpx_client_handler.h"
#include "shrpx_config.h" #include "shrpx_config.h"
@ -60,6 +64,7 @@
#include "shrpx_memcached_request.h" #include "shrpx_memcached_request.h"
#include "shrpx_memcached_dispatcher.h" #include "shrpx_memcached_dispatcher.h"
#include "shrpx_connection_handler.h" #include "shrpx_connection_handler.h"
#include "shrpx_http3_upstream.h"
#include "util.h" #include "util.h"
#include "tls.h" #include "tls.h"
#include "template.h" #include "template.h"
@ -600,6 +605,32 @@ int alpn_select_proto_cb(SSL *ssl, const unsigned char **out,
} // namespace } // namespace
#endif // OPENSSL_VERSION_NUMBER >= 0x10002000L #endif // OPENSSL_VERSION_NUMBER >= 0x10002000L
#if OPENSSL_VERSION_NUMBER >= 0x10002000L
namespace {
int quic_alpn_select_proto_cb(SSL *ssl, const unsigned char **out,
unsigned char *outlen, const unsigned char *in,
unsigned int inlen, void *arg) {
for (auto p = in, end = in + inlen; p < end;) {
auto proto_id = p + 1;
auto proto_len = *p;
if (proto_id + proto_len <= end &&
util::streq_l("h3", StringRef{proto_id, proto_len})) {
*out = reinterpret_cast<const unsigned char *>(proto_id);
*outlen = proto_len;
return SSL_TLSEXT_ERR_OK;
}
p += 1 + proto_len;
}
return SSL_TLSEXT_ERR_NOACK;
}
} // namespace
#endif // OPENSSL_VERSION_NUMBER >= 0x10002000L
#if !LIBRESSL_IN_USE && OPENSSL_VERSION_NUMBER >= 0x10002000L #if !LIBRESSL_IN_USE && OPENSSL_VERSION_NUMBER >= 0x10002000L
# ifndef TLSEXT_TYPE_signed_certificate_timestamp # ifndef TLSEXT_TYPE_signed_certificate_timestamp
@ -1042,6 +1073,322 @@ SSL_CTX *create_ssl_context(const char *private_key_file, const char *cert_file,
return ssl_ctx; return ssl_ctx;
} }
namespace {
int quic_set_encryption_secrets(SSL *ssl, OSSL_ENCRYPTION_LEVEL ossl_level,
const uint8_t *rx_secret,
const uint8_t *tx_secret, size_t secretlen) {
auto conn = static_cast<Connection *>(SSL_get_app_data(ssl));
auto handler = static_cast<ClientHandler *>(conn->data);
auto upstream = static_cast<Http3Upstream *>(handler->get_upstream());
auto level = ngtcp2_crypto_openssl_from_ossl_encryption_level(ossl_level);
if (upstream->on_rx_secret(level, rx_secret, secretlen) != 0) {
return 0;
}
if (tx_secret && upstream->on_tx_secret(level, tx_secret, secretlen) != 0) {
return 0;
}
return 1;
}
} // namespace
namespace {
int quic_add_handshake_data(SSL *ssl, OSSL_ENCRYPTION_LEVEL ossl_level,
const uint8_t *data, size_t len) {
auto conn = static_cast<Connection *>(SSL_get_app_data(ssl));
auto handler = static_cast<ClientHandler *>(conn->data);
auto upstream = static_cast<Http3Upstream *>(handler->get_upstream());
auto level = ngtcp2_crypto_openssl_from_ossl_encryption_level(ossl_level);
upstream->add_crypto_data(level, data, len);
return 1;
}
} // namespace
namespace {
int quic_flush_flight(SSL *ssl) { return 1; }
} // namespace
namespace {
int quic_send_alert(SSL *ssl, OSSL_ENCRYPTION_LEVEL ossl_level, uint8_t alert) {
auto conn = static_cast<Connection *>(SSL_get_app_data(ssl));
auto handler = static_cast<ClientHandler *>(conn->data);
auto upstream = static_cast<Http3Upstream *>(handler->get_upstream());
upstream->set_tls_alert(alert);
return 1;
}
} // namespace
namespace {
auto quic_method = SSL_QUIC_METHOD{
quic_set_encryption_secrets,
quic_add_handshake_data,
quic_flush_flight,
quic_send_alert,
};
} // namespace
SSL_CTX *create_quic_ssl_context(const char *private_key_file,
const char *cert_file,
const std::vector<uint8_t> &sct_data
#ifdef HAVE_NEVERBLEED
,
neverbleed_t *nb
#endif // HAVE_NEVERBLEED
) {
auto ssl_ctx = SSL_CTX_new(TLS_server_method());
if (!ssl_ctx) {
LOG(FATAL) << ERR_error_string(ERR_get_error(), nullptr);
DIE();
}
constexpr auto ssl_opts =
(SSL_OP_ALL & ~SSL_OP_DONT_INSERT_EMPTY_FRAGMENTS) |
SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION | SSL_OP_SINGLE_ECDH_USE |
SSL_OP_SINGLE_DH_USE |
SSL_OP_CIPHER_SERVER_PREFERENCE
#if OPENSSL_1_1_1_API
// The reason for disabling built-in anti-replay in OpenSSL is
// that it only works if client gets back to the same server.
// The freshness check described in
// https://tools.ietf.org/html/rfc8446#section-8.3 is still
// performed.
| SSL_OP_NO_ANTI_REPLAY
#endif // OPENSSL_1_1_1_API
;
auto config = mod_config();
auto &tlsconf = config->tls;
SSL_CTX_set_options(ssl_ctx, ssl_opts);
SSL_CTX_set_min_proto_version(ssl_ctx, TLS1_3_VERSION);
SSL_CTX_set_max_proto_version(ssl_ctx, TLS1_3_VERSION);
const unsigned char sid_ctx[] = "shrpx";
SSL_CTX_set_session_id_context(ssl_ctx, sid_ctx, sizeof(sid_ctx) - 1);
SSL_CTX_set_session_cache_mode(ssl_ctx, SSL_SESS_CACHE_SERVER);
if (!tlsconf.session_cache.memcached.host.empty()) {
SSL_CTX_sess_set_new_cb(ssl_ctx, tls_session_new_cb);
SSL_CTX_sess_set_get_cb(ssl_ctx, tls_session_get_cb);
}
SSL_CTX_set_timeout(ssl_ctx, tlsconf.session_timeout.count());
if (SSL_CTX_set_cipher_list(ssl_ctx, tlsconf.ciphers.c_str()) == 0) {
LOG(FATAL) << "SSL_CTX_set_cipher_list " << tlsconf.ciphers
<< " failed: " << ERR_error_string(ERR_get_error(), nullptr);
DIE();
}
#if OPENSSL_1_1_1_API
if (SSL_CTX_set_ciphersuites(ssl_ctx, tlsconf.tls13_ciphers.c_str()) == 0) {
LOG(FATAL) << "SSL_CTX_set_ciphersuites " << tlsconf.tls13_ciphers
<< " failed: " << ERR_error_string(ERR_get_error(), nullptr);
DIE();
}
#endif // OPENSSL_1_1_1_API
#ifndef OPENSSL_NO_EC
# if !LIBRESSL_LEGACY_API && OPENSSL_VERSION_NUMBER >= 0x10002000L
if (SSL_CTX_set1_curves_list(ssl_ctx, tlsconf.ecdh_curves.c_str()) != 1) {
LOG(FATAL) << "SSL_CTX_set1_curves_list " << tlsconf.ecdh_curves
<< " failed";
DIE();
}
# if !defined(OPENSSL_IS_BORINGSSL) && !OPENSSL_1_1_API
// It looks like we need this function call for OpenSSL 1.0.2. This
// function was deprecated in OpenSSL 1.1.0 and BoringSSL.
SSL_CTX_set_ecdh_auto(ssl_ctx, 1);
# endif // !defined(OPENSSL_IS_BORINGSSL) && !OPENSSL_1_1_API
# else // LIBRESSL_LEGACY_API || OPENSSL_VERSION_NUBMER < 0x10002000L
// Use P-256, which is sufficiently secure at the time of this
// writing.
auto ecdh = EC_KEY_new_by_curve_name(NID_X9_62_prime256v1);
if (ecdh == nullptr) {
LOG(FATAL) << "EC_KEY_new_by_curv_name failed: "
<< ERR_error_string(ERR_get_error(), nullptr);
DIE();
}
SSL_CTX_set_tmp_ecdh(ssl_ctx, ecdh);
EC_KEY_free(ecdh);
# endif // LIBRESSL_LEGACY_API || OPENSSL_VERSION_NUBMER < 0x10002000L
#endif // OPENSSL_NO_EC
if (!tlsconf.dh_param_file.empty()) {
// Read DH parameters from file
auto bio = BIO_new_file(tlsconf.dh_param_file.c_str(), "r");
if (bio == nullptr) {
LOG(FATAL) << "BIO_new_file() failed: "
<< ERR_error_string(ERR_get_error(), nullptr);
DIE();
}
auto dh = PEM_read_bio_DHparams(bio, nullptr, nullptr, nullptr);
if (dh == nullptr) {
LOG(FATAL) << "PEM_read_bio_DHparams() failed: "
<< ERR_error_string(ERR_get_error(), nullptr);
DIE();
}
SSL_CTX_set_tmp_dh(ssl_ctx, dh);
DH_free(dh);
BIO_free(bio);
}
SSL_CTX_set_mode(ssl_ctx, SSL_MODE_RELEASE_BUFFERS);
if (SSL_CTX_set_default_verify_paths(ssl_ctx) != 1) {
LOG(WARN) << "Could not load system trusted ca certificates: "
<< ERR_error_string(ERR_get_error(), nullptr);
}
if (!tlsconf.cacert.empty()) {
if (SSL_CTX_load_verify_locations(ssl_ctx, tlsconf.cacert.c_str(),
nullptr) != 1) {
LOG(FATAL) << "Could not load trusted ca certificates from "
<< tlsconf.cacert << ": "
<< ERR_error_string(ERR_get_error(), nullptr);
DIE();
}
}
if (!tlsconf.private_key_passwd.empty()) {
SSL_CTX_set_default_passwd_cb(ssl_ctx, ssl_pem_passwd_cb);
SSL_CTX_set_default_passwd_cb_userdata(ssl_ctx, config);
}
#ifndef HAVE_NEVERBLEED
if (SSL_CTX_use_PrivateKey_file(ssl_ctx, private_key_file,
SSL_FILETYPE_PEM) != 1) {
LOG(FATAL) << "SSL_CTX_use_PrivateKey_file failed: "
<< ERR_error_string(ERR_get_error(), nullptr);
}
#else // HAVE_NEVERBLEED
std::array<char, NEVERBLEED_ERRBUF_SIZE> errbuf;
if (neverbleed_load_private_key_file(nb, ssl_ctx, private_key_file,
errbuf.data()) != 1) {
LOG(FATAL) << "neverbleed_load_private_key_file failed: " << errbuf.data();
DIE();
}
#endif // HAVE_NEVERBLEED
if (SSL_CTX_use_certificate_chain_file(ssl_ctx, cert_file) != 1) {
LOG(FATAL) << "SSL_CTX_use_certificate_file failed: "
<< ERR_error_string(ERR_get_error(), nullptr);
DIE();
}
if (SSL_CTX_check_private_key(ssl_ctx) != 1) {
LOG(FATAL) << "SSL_CTX_check_private_key failed: "
<< ERR_error_string(ERR_get_error(), nullptr);
DIE();
}
if (tlsconf.client_verify.enabled) {
if (!tlsconf.client_verify.cacert.empty()) {
if (SSL_CTX_load_verify_locations(
ssl_ctx, tlsconf.client_verify.cacert.c_str(), nullptr) != 1) {
LOG(FATAL) << "Could not load trusted ca certificates from "
<< tlsconf.client_verify.cacert << ": "
<< ERR_error_string(ERR_get_error(), nullptr);
DIE();
}
// It is heard that SSL_CTX_load_verify_locations() may leave
// error even though it returns success. See
// http://forum.nginx.org/read.php?29,242540
ERR_clear_error();
auto list = SSL_load_client_CA_file(tlsconf.client_verify.cacert.c_str());
if (!list) {
LOG(FATAL) << "Could not load ca certificates from "
<< tlsconf.client_verify.cacert << ": "
<< ERR_error_string(ERR_get_error(), nullptr);
DIE();
}
SSL_CTX_set_client_CA_list(ssl_ctx, list);
}
SSL_CTX_set_verify(ssl_ctx,
SSL_VERIFY_PEER | SSL_VERIFY_CLIENT_ONCE |
SSL_VERIFY_FAIL_IF_NO_PEER_CERT,
verify_callback);
}
SSL_CTX_set_tlsext_servername_callback(ssl_ctx, servername_callback);
SSL_CTX_set_tlsext_ticket_key_cb(ssl_ctx, ticket_key_cb);
#ifndef OPENSSL_IS_BORINGSSL
SSL_CTX_set_tlsext_status_cb(ssl_ctx, ocsp_resp_cb);
#endif // OPENSSL_IS_BORINGSSL
#ifdef OPENSSL_IS_BORINGSSL
SSL_CTX_set_early_data_enabled(ssl_ctx, 1);
#endif // OPENSSL_IS_BORINGSSL
#if OPENSSL_VERSION_NUMBER >= 0x10002000L
// ALPN selection callback
SSL_CTX_set_alpn_select_cb(ssl_ctx, quic_alpn_select_proto_cb, nullptr);
#endif // OPENSSL_VERSION_NUMBER >= 0x10002000L
#if !LIBRESSL_IN_USE && OPENSSL_VERSION_NUMBER >= 0x10002000L && \
!defined(OPENSSL_IS_BORINGSSL)
// SSL_extension_supported(TLSEXT_TYPE_signed_certificate_timestamp)
// returns 1, which means OpenSSL internally handles it. But
// OpenSSL handles signed_certificate_timestamp extension specially,
// and it lets custom handler to process the extension.
if (!sct_data.empty()) {
# if OPENSSL_1_1_1_API
// It is not entirely clear to me that SSL_EXT_CLIENT_HELLO is
// required here. sct_parse_cb is called without
// SSL_EXT_CLIENT_HELLO being set. But the passed context value
// is SSL_EXT_CLIENT_HELLO.
if (SSL_CTX_add_custom_ext(
ssl_ctx, TLSEXT_TYPE_signed_certificate_timestamp,
SSL_EXT_CLIENT_HELLO | SSL_EXT_TLS1_2_SERVER_HELLO |
SSL_EXT_TLS1_3_CERTIFICATE | SSL_EXT_IGNORE_ON_RESUMPTION,
sct_add_cb, sct_free_cb, nullptr, sct_parse_cb, nullptr) != 1) {
LOG(FATAL) << "SSL_CTX_add_custom_ext failed: "
<< ERR_error_string(ERR_get_error(), nullptr);
DIE();
}
# else // !OPENSSL_1_1_1_API
if (SSL_CTX_add_server_custom_ext(
ssl_ctx, TLSEXT_TYPE_signed_certificate_timestamp,
legacy_sct_add_cb, legacy_sct_free_cb, nullptr, legacy_sct_parse_cb,
nullptr) != 1) {
LOG(FATAL) << "SSL_CTX_add_server_custom_ext failed: "
<< ERR_error_string(ERR_get_error(), nullptr);
DIE();
}
# endif // !OPENSSL_1_1_1_API
}
#endif // !LIBRESSL_IN_USE && OPENSSL_VERSION_NUMBER >= 0x10002000L &&
// !defined(OPENSSL_IS_BORINGSSL)
#if OPENSSL_1_1_1_API
if (SSL_CTX_set_max_early_data(ssl_ctx,
std::numeric_limits<uint32_t>::max()) != 1) {
LOG(FATAL) << "SSL_CTX_set_max_early_data failed: "
<< ERR_error_string(ERR_get_error(), nullptr);
DIE();
}
#endif // OPENSSL_1_1_1_API
#ifndef OPENSSL_NO_PSK
SSL_CTX_set_psk_server_callback(ssl_ctx, psk_server_cb);
#endif // !LIBRESSL_NO_PSK
SSL_CTX_set_quic_method(ssl_ctx, &quic_method);
auto tls_ctx_data = new TLSContextData();
tls_ctx_data->cert_file = cert_file;
tls_ctx_data->sct_data = sct_data;
SSL_CTX_set_app_data(ssl_ctx, tls_ctx_data);
return ssl_ctx;
}
namespace { namespace {
int select_h2_next_proto_cb(SSL *ssl, unsigned char **out, int select_h2_next_proto_cb(SSL *ssl, unsigned char **out,
unsigned char *outlen, const unsigned char *in, unsigned char *outlen, const unsigned char *in,
@ -1747,6 +2094,10 @@ bool in_proto_list(const std::vector<StringRef> &protos,
} }
bool upstream_tls_enabled(const ConnectionConfig &connconf) { bool upstream_tls_enabled(const ConnectionConfig &connconf) {
if (connconf.quic_listener.addrs.size()) {
return true;
}
const auto &faddrs = connconf.listener.addrs; const auto &faddrs = connconf.listener.addrs;
return std::any_of(std::begin(faddrs), std::end(faddrs), return std::any_of(std::begin(faddrs), std::end(faddrs),
[](const UpstreamAddr &faddr) { return faddr.tls; }); [](const UpstreamAddr &faddr) { return faddr.tls; });
@ -1826,6 +2177,61 @@ setup_server_ssl_context(std::vector<SSL_CTX *> &all_ssl_ctx,
return ssl_ctx; return ssl_ctx;
} }
SSL_CTX *setup_quic_server_ssl_context(
std::vector<SSL_CTX *> &all_ssl_ctx,
std::vector<std::vector<SSL_CTX *>> &indexed_ssl_ctx,
CertLookupTree *cert_tree
#ifdef HAVE_NEVERBLEED
,
neverbleed_t *nb
#endif // HAVE_NEVERBLEED
) {
auto config = get_config();
if (!upstream_tls_enabled(config->conn)) {
return nullptr;
}
auto &tlsconf = config->tls;
auto ssl_ctx =
create_quic_ssl_context(tlsconf.private_key_file.c_str(),
tlsconf.cert_file.c_str(), tlsconf.sct_data
#ifdef HAVE_NEVERBLEED
,
nb
#endif // HAVE_NEVERBLEED
);
all_ssl_ctx.push_back(ssl_ctx);
assert(cert_tree);
if (cert_lookup_tree_add_ssl_ctx(cert_tree, indexed_ssl_ctx, ssl_ctx) == -1) {
LOG(FATAL) << "Failed to add default certificate.";
DIE();
}
for (auto &c : tlsconf.subcerts) {
auto ssl_ctx = create_quic_ssl_context(c.private_key_file.c_str(),
c.cert_file.c_str(), c.sct_data
#ifdef HAVE_NEVERBLEED
,
nb
#endif // HAVE_NEVERBLEED
);
all_ssl_ctx.push_back(ssl_ctx);
if (cert_lookup_tree_add_ssl_ctx(cert_tree, indexed_ssl_ctx, ssl_ctx) ==
-1) {
LOG(FATAL) << "Failed to add sub certificate.";
DIE();
}
}
return ssl_ctx;
}
SSL_CTX *setup_downstream_client_ssl_context( SSL_CTX *setup_downstream_client_ssl_context(
#ifdef HAVE_NEVERBLEED #ifdef HAVE_NEVERBLEED
neverbleed_t *nb neverbleed_t *nb

View File

@ -100,6 +100,16 @@ SSL_CTX *create_ssl_client_context(
unsigned char *outlen, const unsigned char *in, unsigned char *outlen, const unsigned char *in,
unsigned int inlen, void *arg)); unsigned int inlen, void *arg));
SSL_CTX *create_quic_ssl_client_context(
#ifdef HAVE_NEVERBLEED
neverbleed_t *nb,
#endif // HAVE_NEVERBLEED
const StringRef &cacert, const StringRef &cert_file,
const StringRef &private_key_file,
int (*next_proto_select_cb)(SSL *s, unsigned char **out,
unsigned char *outlen, const unsigned char *in,
unsigned int inlen, void *arg));
ClientHandler *accept_connection(Worker *worker, int fd, sockaddr *addr, ClientHandler *accept_connection(Worker *worker, int fd, sockaddr *addr,
int addrlen, const UpstreamAddr *faddr); int addrlen, const UpstreamAddr *faddr);
@ -217,6 +227,16 @@ setup_server_ssl_context(std::vector<SSL_CTX *> &all_ssl_ctx,
#endif // HAVE_NEVERBLEED #endif // HAVE_NEVERBLEED
); );
SSL_CTX *setup_quic_server_ssl_context(
std::vector<SSL_CTX *> &all_ssl_ctx,
std::vector<std::vector<SSL_CTX *>> &indexed_ssl_ctx,
CertLookupTree *cert_tree
#ifdef HAVE_NEVERBLEED
,
neverbleed_t *nb
#endif // HAVE_NEVERBLEED
);
// Setups client side SSL_CTX. // Setups client side SSL_CTX.
SSL_CTX *setup_downstream_client_ssl_context( SSL_CTX *setup_downstream_client_ssl_context(
#ifdef HAVE_NEVERBLEED #ifdef HAVE_NEVERBLEED