nghttpx: Create QUIC SSL_CTX
We choose an easier route to duplicate SSL_CTX for QUIC.
This commit is contained in:
parent
aeb0b0728d
commit
ef53db201e
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
406
src/shrpx_tls.cc
406
src/shrpx_tls.cc
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
Loading…
Reference in New Issue