diff --git a/src/shrpx_connection_handler.cc b/src/shrpx_connection_handler.cc index f732f74a..db2ace7d 100644 --- a/src/shrpx_connection_handler.cc +++ b/src/shrpx_connection_handler.cc @@ -156,6 +156,17 @@ ConnectionHandler::~ConnectionHandler() { ev_timer_stop(loop_, &ocsp_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(SSL_CTX_get_app_data(ssl_ctx)); + delete tls_ctx_data; + SSL_CTX_free(ssl_ctx); + } + for (auto ssl_ctx : all_ssl_ctx_) { auto tls_ctx_data = static_cast(SSL_CTX_get_app_data(ssl_ctx)); @@ -209,6 +220,16 @@ int ConnectionHandler::create_single_worker() { nb_ #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( #ifdef HAVE_NEVERBLEED nb_ @@ -217,6 +238,7 @@ int ConnectionHandler::create_single_worker() { if (cl_ssl_ctx) { all_ssl_ctx_.push_back(cl_ssl_ctx); + quic_all_ssl_ctx_.push_back(nullptr); } auto config = get_config(); @@ -233,6 +255,7 @@ int ConnectionHandler::create_single_worker() { tlsconf.cacert, memcachedconf.cert_file, memcachedconf.private_key_file, nullptr); 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_ # 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( # ifdef HAVE_NEVERBLEED nb_ @@ -271,6 +304,7 @@ int ConnectionHandler::create_worker_thread(size_t num) { if (cl_ssl_ctx) { all_ssl_ctx_.push_back(cl_ssl_ctx); + quic_all_ssl_ctx_.push_back(nullptr); } auto config = get_config(); @@ -294,6 +328,7 @@ int ConnectionHandler::create_worker_thread(size_t num) { tlsconf.cacert, memcachedconf.cert_file, memcachedconf.private_key_file, nullptr); 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); 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 tls_ctx_data = @@ -635,6 +671,29 @@ void ConnectionHandler::handle_ocsp_complete() { if (tlsconf.ocsp.no_verify || tls::verify_ocsp_response(ssl_ctx, ocsp_.resp.data(), 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( + 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>(ocsp_.resp), + std::memory_order_release); +# else // !HAVE_ATOMIC_STD_SHARED_PTR + std::lock_guard g(quic_tls_ctx_data->mu); + quic_tls_ctx_data->ocsp_data = + std::make_shared>(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 # ifdef HAVE_ATOMIC_STD_SHARED_PTR std::atomic_store_explicit( @@ -815,6 +874,7 @@ SSL_CTX *ConnectionHandler::create_tls_ticket_key_memcached_ssl_ctx() { nullptr); all_ssl_ctx_.push_back(ssl_ctx); + quic_all_ssl_ctx_.push_back(nullptr); return ssl_ctx; } @@ -877,6 +937,11 @@ ConnectionHandler::get_indexed_ssl_ctx(size_t idx) const { return indexed_ssl_ctx_[idx]; } +const std::vector & +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) { enable_acceptor_on_ocsp_completion_ = f; } diff --git a/src/shrpx_connection_handler.h b/src/shrpx_connection_handler.h index 535bc2c3..eb86b855 100644 --- a/src/shrpx_connection_handler.h +++ b/src/shrpx_connection_handler.h @@ -159,6 +159,7 @@ public: SSL_CTX *get_ssl_ctx(size_t idx) const; const std::vector &get_indexed_ssl_ctx(size_t idx) const; + const std::vector &get_quic_indexed_ssl_ctx(size_t idx) const; #ifdef HAVE_NEVERBLEED void set_neverbleed(neverbleed_t *nb); @@ -187,6 +188,8 @@ private: // selection among them are performed by hostname presented by SNI, // and signature algorithm presented by client. std::vector> indexed_ssl_ctx_; + std::vector quic_all_ssl_ctx_; + std::vector> quic_indexed_ssl_ctx_; OCSPUpdateContext ocsp_; std::mt19937 &gen_; // ev_loop for each worker @@ -203,6 +206,7 @@ private: // Otherwise, nullptr and workers_ has instances of Worker instead. std::unique_ptr single_worker_; std::unique_ptr cert_tree_; + std::unique_ptr quic_cert_tree_; std::unique_ptr tls_ticket_key_memcached_dispatcher_; // Current TLS session ticket keys. Note that TLS connection does // not refer to this field directly. They use TicketKeys object in diff --git a/src/shrpx_http3_upstream.cc b/src/shrpx_http3_upstream.cc index 6c2c8541..b0da58c8 100644 --- a/src/shrpx_http3_upstream.cc +++ b/src/shrpx_http3_upstream.cc @@ -30,7 +30,8 @@ namespace shrpx { -Http3Upstream::Http3Upstream(ClientHandler *handler) : handler_{handler} {} +Http3Upstream::Http3Upstream(ClientHandler *handler) + : handler_{handler}, tls_alert_{0} {} Http3Upstream::~Http3Upstream() {} @@ -129,4 +130,21 @@ int Http3Upstream::on_read(const UpstreamAddr *faddr, 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 diff --git a/src/shrpx_http3_upstream.h b/src/shrpx_http3_upstream.h index 318d9cc0..d979fff7 100644 --- a/src/shrpx_http3_upstream.h +++ b/src/shrpx_http3_upstream.h @@ -26,6 +26,9 @@ #define SHRPX_HTTP3_UPSTREAM_H #include "shrpx.h" + +#include + #include "shrpx_upstream.h" #include "network.h" @@ -84,8 +87,19 @@ public: int on_read(const UpstreamAddr *faddr, const Address &remote_addr, 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: ClientHandler *handler_; + uint8_t tls_alert_; }; } // namespace shrpx diff --git a/src/shrpx_tls.cc b/src/shrpx_tls.cc index 3294a541..9ca883e8 100644 --- a/src/shrpx_tls.cc +++ b/src/shrpx_tls.cc @@ -51,6 +51,10 @@ #include +#include +#include +#include + #include "shrpx_log.h" #include "shrpx_client_handler.h" #include "shrpx_config.h" @@ -60,6 +64,7 @@ #include "shrpx_memcached_request.h" #include "shrpx_memcached_dispatcher.h" #include "shrpx_connection_handler.h" +#include "shrpx_http3_upstream.h" #include "util.h" #include "tls.h" #include "template.h" @@ -600,6 +605,32 @@ int alpn_select_proto_cb(SSL *ssl, const unsigned char **out, } // namespace #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(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 # 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; } +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(SSL_get_app_data(ssl)); + auto handler = static_cast(conn->data); + auto upstream = static_cast(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(SSL_get_app_data(ssl)); + auto handler = static_cast(conn->data); + auto upstream = static_cast(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(SSL_get_app_data(ssl)); + auto handler = static_cast(conn->data); + auto upstream = static_cast(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 &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 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::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 { int select_h2_next_proto_cb(SSL *ssl, unsigned char **out, unsigned char *outlen, const unsigned char *in, @@ -1747,6 +2094,10 @@ bool in_proto_list(const std::vector &protos, } bool upstream_tls_enabled(const ConnectionConfig &connconf) { + if (connconf.quic_listener.addrs.size()) { + return true; + } + const auto &faddrs = connconf.listener.addrs; return std::any_of(std::begin(faddrs), std::end(faddrs), [](const UpstreamAddr &faddr) { return faddr.tls; }); @@ -1826,6 +2177,61 @@ setup_server_ssl_context(std::vector &all_ssl_ctx, return ssl_ctx; } +SSL_CTX *setup_quic_server_ssl_context( + std::vector &all_ssl_ctx, + std::vector> &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( #ifdef HAVE_NEVERBLEED neverbleed_t *nb diff --git a/src/shrpx_tls.h b/src/shrpx_tls.h index 1ca20330..ebc4aa27 100644 --- a/src/shrpx_tls.h +++ b/src/shrpx_tls.h @@ -100,6 +100,16 @@ SSL_CTX *create_ssl_client_context( unsigned char *outlen, const unsigned char *in, 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, int addrlen, const UpstreamAddr *faddr); @@ -217,6 +227,16 @@ setup_server_ssl_context(std::vector &all_ssl_ctx, #endif // HAVE_NEVERBLEED ); +SSL_CTX *setup_quic_server_ssl_context( + std::vector &all_ssl_ctx, + std::vector> &indexed_ssl_ctx, + CertLookupTree *cert_tree +#ifdef HAVE_NEVERBLEED + , + neverbleed_t *nb +#endif // HAVE_NEVERBLEED +); + // Setups client side SSL_CTX. SSL_CTX *setup_downstream_client_ssl_context( #ifdef HAVE_NEVERBLEED