diff --git a/iocore/net/P_QUICNetVConnection_quiche.h b/iocore/net/P_QUICNetVConnection_quiche.h index 400d363a93d..16511fbf383 100644 --- a/iocore/net/P_QUICNetVConnection_quiche.h +++ b/iocore/net/P_QUICNetVConnection_quiche.h @@ -39,6 +39,9 @@ #include "P_UDPNet.h" #include "P_ALPNSupport.h" #include "TLSBasicSupport.h" +#include "TLSSessionResumptionSupport.h" +#include "TLSSNISupport.h" +#include "TLSCertSwitchSupport.h" #include "tscore/ink_apidefs.h" #include "tscore/List.h" @@ -57,6 +60,9 @@ class QUICNetVConnection : public UnixNetVConnection, public QUICConnection, public RefCountObj, public ALPNSupport, + public TLSSNISupport, + public TLSSessionResumptionSupport, + public TLSCertSwitchSupport, public TLSBasicSupport { using super = UnixNetVConnection; ///< Parent type. @@ -66,7 +72,7 @@ class QUICNetVConnection : public UnixNetVConnection, ~QUICNetVConnection(); void init(QUICVersion version, QUICConnectionId peer_cid, QUICConnectionId original_cid, UDPConnection *, QUICPacketHandler *); void init(QUICVersion version, QUICConnectionId peer_cid, QUICConnectionId original_cid, QUICConnectionId first_cid, - QUICConnectionId retry_cid, UDPConnection *, quiche_conn *, QUICPacketHandler *, QUICConnectionTable *ctable); + QUICConnectionId retry_cid, UDPConnection *, quiche_conn *, QUICPacketHandler *, QUICConnectionTable *ctable, SSL *); // Event handlers int acceptEvent(int event, Event *e); @@ -88,6 +94,7 @@ class QUICNetVConnection : public UnixNetVConnection, VIO *do_io_write(Continuation *c, int64_t nbytes, IOBufferReader *buf, bool owner = false) override; int connectUp(EThread *t, int fd) override; int64_t load_buffer_and_write(int64_t towrite, MIOBufferAccessor &buf, int64_t &total_written, int &needs) override; + bool getSSLHandShakeComplete() const override; // NetEvent virtual void net_read_io(NetHandler *nh, EThread *lthread) override; @@ -95,6 +102,8 @@ class QUICNetVConnection : public UnixNetVConnection, // NetVConnection int populate_protocol(std::string_view *results, int n) const override; const char *protocol_contains(std::string_view tag) const override; + const char *get_server_name() const override; + bool support_sni() const override; // QUICConnection QUICStreamManager *stream_manager() override; @@ -144,7 +153,19 @@ class QUICNetVConnection : public UnixNetVConnection, SSL *_get_ssl_object() const override; ssl_curve_id _get_tls_curve() const override; + // TLSSNISupport + void _fire_ssl_servername_event() override; + + // TLSSessionResumptionSupport + const IpEndpoint &_getLocalEndpoint() override; + + // TLSCertSwitchSupport + bool _isTryingRenegotiation() const override; + shared_SSL_CTX _lookupContextByName(const std::string &servername, SSLCertContextType ctxType) override; + shared_SSL_CTX _lookupContextByIP() override; + private: + SSL *_ssl; QUICConfig::scoped_config _quic_config; QUICConnectionId _peer_quic_connection_id; // dst cid in local @@ -159,6 +180,9 @@ class QUICNetVConnection : public UnixNetVConnection, quiche_conn *_quiche_con = nullptr; QUICConnectionTable *_ctable = nullptr; + void _bindSSLObject(); + void _unbindSSLObject(); + void _schedule_packet_write_ready(bool delay = false); void _unschedule_packet_write_ready(); void _close_packet_write_ready(Event *data); diff --git a/iocore/net/QUICMultiCertConfigLoader.cc b/iocore/net/QUICMultiCertConfigLoader.cc index f8c52fd6521..653aec4d1ce 100644 --- a/iocore/net/QUICMultiCertConfigLoader.cc +++ b/iocore/net/QUICMultiCertConfigLoader.cc @@ -109,77 +109,6 @@ QUICMultiCertConfigLoader::_set_npn_callback(SSL_CTX *ctx) return true; } -void -QUICMultiCertConfigLoader::_set_handshake_callbacks(SSL_CTX *ssl_ctx) -{ - SSL_CTX_set_cert_cb(ssl_ctx, QUICMultiCertConfigLoader::ssl_cert_cb, nullptr); - SSL_CTX_set_tlsext_servername_callback(ssl_ctx, QUICMultiCertConfigLoader::ssl_sni_cb); - - // Set client hello callback if needed - // SSL_CTX_set_client_hello_cb(ctx, QUIC::ssl_client_hello_cb, nullptr); -} - -int -QUICMultiCertConfigLoader::ssl_sni_cb(SSL *ssl, int * /*ad*/, void * /*arg*/) -{ - // XXX: add SNIConfig support ? - // XXX: add TRANSPORT_BLIND_TUNNEL support ? - return 1; -} - -int -QUICMultiCertConfigLoader::ssl_cert_cb(SSL *ssl, void * /*arg*/) -{ - shared_SSL_CTX ctx = nullptr; - SSLCertContext *cc = nullptr; - QUICCertConfig::scoped_config lookup; - const char *servername = SSL_get_servername(ssl, TLSEXT_NAMETYPE_host_name); - QUICConnection *qc = static_cast(SSL_get_ex_data(ssl, QUIC::ssl_quic_qc_index)); - - if (servername == nullptr) { - servername = ""; - } - QUICGlobalQCDebug(qc, "SNI=%s", servername); - - // The incoming SSL_CTX is either the one mapped from the inbound IP address or the default one. If we - // don't find a name-based match at this point, we *do not* want to mess with the context because we've - // already made a best effort to find the best match. - if (likely(servername)) { - cc = lookup->find(const_cast(servername)); - if (cc && cc->getCtx()) { - ctx = cc->getCtx(); - } - } - - // If there's no match on the server name, try to match on the peer address. - if (ctx == nullptr) { - QUICFiveTuple five_tuple = qc->five_tuple(); - IpEndpoint ip = five_tuple.destination(); - cc = lookup->find(ip); - - if (cc && cc->getCtx()) { - ctx = cc->getCtx(); - } - } - - bool found = true; - if (ctx != nullptr) { - SSL_set_SSL_CTX(ssl, ctx.get()); - } else { - found = false; - } - - SSL_CTX *verify_ctx = nullptr; - verify_ctx = SSL_get_SSL_CTX(ssl); - QUICGlobalQCDebug(qc, "%s SSL_CTX %p for requested name '%s'", found ? "found" : "using", verify_ctx, servername); - - if (verify_ctx == nullptr) { - return 0; - } - - return 1; -} - const char * QUICMultiCertConfigLoader::_debug_tag() const { diff --git a/iocore/net/QUICMultiCertConfigLoader.h b/iocore/net/QUICMultiCertConfigLoader.h index 1f97ee31aae..6ee7df7bc03 100644 --- a/iocore/net/QUICMultiCertConfigLoader.h +++ b/iocore/net/QUICMultiCertConfigLoader.h @@ -49,12 +49,8 @@ class QUICMultiCertConfigLoader : public SSLMultiCertConfigLoader private: const char *_debug_tag() const override; - virtual void _set_handshake_callbacks(SSL_CTX *ssl_ctx) override; virtual bool _setup_session_cache(SSL_CTX *ctx) override; virtual bool _set_cipher_suites_for_legacy_versions(SSL_CTX *ctx) override; virtual bool _set_info_callback(SSL_CTX *ctx) override; virtual bool _set_npn_callback(SSL_CTX *ctx) override; - - static int ssl_cert_cb(SSL *ssl, void *arg); - static int ssl_sni_cb(SSL *ssl, int *ad, void *arg); }; diff --git a/iocore/net/QUICNetProcessor_quiche.cc b/iocore/net/QUICNetProcessor_quiche.cc index b6882a7c706..2f680582ca9 100644 --- a/iocore/net/QUICNetProcessor_quiche.cc +++ b/iocore/net/QUICNetProcessor_quiche.cc @@ -75,19 +75,11 @@ QUICNetProcessor::start(int, size_t stacksize) // QUICInitializeLibrary(); QUICConfig::startup(); QUICCertConfig::startup(); - QUICCertConfig::scoped_config certs; QUICConfig::scoped_config params; - SSLCertContext *context = certs->get(0); quiche_enable_debug_logging(debug_log, NULL); this->_quiche_config = quiche_config_new(QUICHE_PROTOCOL_VERSION); quiche_config_set_application_protos(this->_quiche_config, (uint8_t *)"\02h3\x05h3-29\x05hq-29\x05h3-27\x05hq-27", 27); - if (context->userconfig->cert != nullptr) { - quiche_config_load_cert_chain_from_pem_file(this->_quiche_config, context->userconfig->cert); - } - if (context->userconfig->key != nullptr) { - quiche_config_load_priv_key_from_pem_file(this->_quiche_config, context->userconfig->key); - } quiche_config_set_max_idle_timeout(this->_quiche_config, params->no_activity_timeout_in()); quiche_config_set_max_recv_udp_payload_size(this->_quiche_config, params->get_max_recv_udp_payload_size_in()); diff --git a/iocore/net/QUICNetVConnection_quiche.cc b/iocore/net/QUICNetVConnection_quiche.cc index e7065cd4c9b..7d4329f5fa1 100644 --- a/iocore/net/QUICNetVConnection_quiche.cc +++ b/iocore/net/QUICNetVConnection_quiche.cc @@ -23,7 +23,9 @@ #include "P_QUICNetVConnection_quiche.h" #include "P_QUICPacketHandler_quiche.h" +#include "QUICMultiCertConfigLoader.h" #include "quic/QUICStream_quiche.h" +#include "quic/QUICGlobals.h" #include static constexpr ink_hrtime WRITE_READY_INTERVAL = HRTIME_MSECONDS(2); @@ -46,7 +48,7 @@ QUICNetVConnection::init(QUICVersion version, QUICConnectionId peer_cid, QUICCon void QUICNetVConnection::init(QUICVersion version, QUICConnectionId peer_cid, QUICConnectionId original_cid, QUICConnectionId first_cid, QUICConnectionId retry_cid, UDPConnection *udp_con, quiche_conn *quiche_con, - QUICPacketHandler *packet_handler, QUICConnectionTable *ctable) + QUICPacketHandler *packet_handler, QUICConnectionTable *ctable, SSL *ssl) { SET_HANDLER((NetVConnHandler)&QUICNetVConnection::acceptEvent); this->_udp_con = udp_con; @@ -61,6 +63,10 @@ QUICNetVConnection::init(QUICVersion version, QUICConnectionId peer_cid, QUICCon this->_ctable->insert(this->_quic_connection_id, this); this->_ctable->insert(this->_original_quic_connection_id, this); } + + this->_ssl = ssl; + SSL_set_ex_data(ssl, QUIC::ssl_quic_qc_index, static_cast(this)); + this->_bindSSLObject(); } void @@ -114,6 +120,7 @@ QUICNetVConnection::free(EThread *t) this->_context->trigger(QUICContext::CallbackEvent::CONNECTION_CLOSE); ALPNSupport::clear(); TLSBasicSupport::clear(); + TLSCertSwitchSupport::_clear(); this->_packet_handler->close_connection(this); this->_packet_handler = nullptr; @@ -489,6 +496,32 @@ QUICNetVConnection::load_buffer_and_write(int64_t towrite, MIOBufferAccessor &bu return 0; } +bool +QUICNetVConnection::getSSLHandShakeComplete() const +{ + return quiche_conn_is_established(this->_quiche_con); +} + +void +QUICNetVConnection::_bindSSLObject() +{ + TLSBasicSupport::bind(this->_ssl, this); + ALPNSupport::bind(this->_ssl, this); + TLSSessionResumptionSupport::bind(this->_ssl, this); + TLSSNISupport::bind(this->_ssl, this); + TLSCertSwitchSupport::bind(this->_ssl, this); +} + +void +QUICNetVConnection::_unbindSSLObject() +{ + TLSBasicSupport::unbind(this->_ssl); + ALPNSupport::unbind(this->_ssl); + TLSSessionResumptionSupport::unbind(this->_ssl); + TLSSNISupport::unbind(this->_ssl); + TLSCertSwitchSupport::unbind(this->_ssl); +} + void QUICNetVConnection::_schedule_packet_write_ready(bool delay) { @@ -632,14 +665,79 @@ QUICNetVConnection::protocol_contains(std::string_view tag) const return ""; } +const char * +QUICNetVConnection::get_server_name() const +{ + return get_sni_server_name(); +} + +bool +QUICNetVConnection::support_sni() const +{ + return true; +} + SSL * QUICNetVConnection::_get_ssl_object() const { - return nullptr; + return this->_ssl; } ssl_curve_id QUICNetVConnection::_get_tls_curve() const { - return 0; + if (getSSLSessionCacheHit()) { + return getSSLCurveNID(); + } else { + return SSLGetCurveNID(this->_ssl); + } +} + +void +QUICNetVConnection::_fire_ssl_servername_event() +{ +} + +const IpEndpoint & +QUICNetVConnection::_getLocalEndpoint() +{ + return this->local_addr; +} + +bool +QUICNetVConnection::_isTryingRenegotiation() const +{ + // Renegotiation is not allowed on QUIC (TLS 1.3) connections. + // If handshake is completed when this function is called, that should be unallowed attempt of renegotiation. + return this->getSSLHandShakeComplete(); +} + +shared_SSL_CTX +QUICNetVConnection::_lookupContextByName(const std::string &servername, SSLCertContextType ctxType) +{ + shared_SSL_CTX ctx = nullptr; + QUICCertConfig::scoped_config lookup; + SSLCertContext *cc = lookup->find(servername, ctxType); + + if (cc && cc->getCtx()) { + ctx = cc->getCtx(); + } + + return ctx; +} + +shared_SSL_CTX +QUICNetVConnection::_lookupContextByIP() +{ + shared_SSL_CTX ctx = nullptr; + QUICCertConfig::scoped_config lookup; + QUICFiveTuple five_tuple = this->five_tuple(); + IpEndpoint ip = five_tuple.destination(); + SSLCertContext *cc = lookup->find(ip); + + if (cc && cc->getCtx()) { + ctx = cc->getCtx(); + } + + return ctx; } diff --git a/iocore/net/QUICPacketHandler_quiche.cc b/iocore/net/QUICPacketHandler_quiche.cc index c6be82ea659..d256445c1f0 100644 --- a/iocore/net/QUICPacketHandler_quiche.cc +++ b/iocore/net/QUICPacketHandler_quiche.cc @@ -29,6 +29,7 @@ #include "P_QUICNetProcessor_quiche.h" #include "P_QUICClosedConCollector.h" #include "quic/QUICConnectionTable.h" +#include "QUICMultiCertConfigLoader.h" #include static constexpr char debug_tag[] = "quic_sec"; @@ -274,10 +275,14 @@ QUICPacketHandlerIn::_recv_packet(int event, UDPPacket *udp_packet) } QUICConnectionId new_cid; - quiche_conn *quiche_con = quiche_accept( + + QUICCertConfig::scoped_config server_cert; + SSL *ssl = SSL_new(server_cert->defaultContext()); + + quiche_conn *quiche_con = quiche_conn_new_with_tls( new_cid, new_cid.length(), retry_token.original_dcid(), retry_token.original_dcid().length(), &udp_packet->to.sa, udp_packet->to.isIp4() ? sizeof(udp_packet->to.sin) : sizeof(udp_packet->to.sin6), &udp_packet->from.sa, - udp_packet->from.isIp4() ? sizeof(udp_packet->from.sin) : sizeof(udp_packet->from.sin6), &this->_quiche_config); + udp_packet->from.isIp4() ? sizeof(udp_packet->from.sin) : sizeof(udp_packet->from.sin6), &this->_quiche_config, ssl, true); if (params->get_qlog_file_base_name() != nullptr) { char qlog_filepath[PATH_MAX]; @@ -296,7 +301,7 @@ QUICPacketHandlerIn::_recv_packet(int event, UDPPacket *udp_packet) // vc->init(version, peer_cid, original_cid, ocid_in_retry_token, rcid_in_retry_token, udp_packet->getConnection(), this, // &this->_ctable); vc->init(version, peer_cid, new_cid, QUICConnectionId::ZERO(), QUICConnectionId::ZERO(), udp_packet->getConnection(), - quiche_con, this, &this->_ctable); + quiche_con, this, &this->_ctable, ssl); vc->id = net_next_connection_number(); vc->con.move(con); vc->submit_time = Thread::get_hrtime(); diff --git a/tests/gold_tests/timeout/active_timeout.test.py b/tests/gold_tests/timeout/active_timeout.test.py index 8c8b8bc650d..80282efbedc 100644 --- a/tests/gold_tests/timeout/active_timeout.test.py +++ b/tests/gold_tests/timeout/active_timeout.test.py @@ -64,7 +64,7 @@ tr3.Processes.Default.Command = 'curl -k -i --http2 https://127.0.0.1:{0}/file'.format(ts.Variables.ssl_port) tr3.Processes.Default.Streams.stdout = Testers.ContainsExpression("Activity Timeout", "Request should fail with active timeout") -if Condition.HasATSFeature('TS_USE_QUIC') and Condition.HasCurlFeature('http3'): +if Condition.HasATSFeature('TS_HAS_QUICHE') and Condition.HasCurlFeature('http3'): tr4 = Test.AddTestRun("tr") tr4.Processes.Default.Command = 'curl -k -i --http3 https://127.0.0.1:{0}/file'.format(ts.Variables.ssl_port) tr4.Processes.Default.Streams.stdout = Testers.ContainsExpression("Activity Timeout", "Request should fail with active timeout")