Skip to content

Commit 15ffe76

Browse files
authored
Enables switching SSL certificates on QUIC with QUICHE (#9347)
* Enables switching SSL certificates on QUIC with QUICHE This enables using certificates loaded by QUICMultiCertConfigLoader (not only the default cert but also certs for specific servernames and IP addresses). This also makes other SNI stuff work (SNIAction, cssn log field) on QUIC. * Only run HTTP/3 autests if TS uses Quiche * Simplify the code
1 parent 4cf91f8 commit 15ffe76

7 files changed

Lines changed: 135 additions & 91 deletions

iocore/net/P_QUICNetVConnection_quiche.h

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,9 @@
3939
#include "P_UDPNet.h"
4040
#include "P_ALPNSupport.h"
4141
#include "TLSBasicSupport.h"
42+
#include "TLSSessionResumptionSupport.h"
43+
#include "TLSSNISupport.h"
44+
#include "TLSCertSwitchSupport.h"
4245
#include "tscore/ink_apidefs.h"
4346
#include "tscore/List.h"
4447

@@ -57,6 +60,9 @@ class QUICNetVConnection : public UnixNetVConnection,
5760
public QUICConnection,
5861
public RefCountObj,
5962
public ALPNSupport,
63+
public TLSSNISupport,
64+
public TLSSessionResumptionSupport,
65+
public TLSCertSwitchSupport,
6066
public TLSBasicSupport
6167
{
6268
using super = UnixNetVConnection; ///< Parent type.
@@ -66,7 +72,7 @@ class QUICNetVConnection : public UnixNetVConnection,
6672
~QUICNetVConnection();
6773
void init(QUICVersion version, QUICConnectionId peer_cid, QUICConnectionId original_cid, UDPConnection *, QUICPacketHandler *);
6874
void init(QUICVersion version, QUICConnectionId peer_cid, QUICConnectionId original_cid, QUICConnectionId first_cid,
69-
QUICConnectionId retry_cid, UDPConnection *, quiche_conn *, QUICPacketHandler *, QUICConnectionTable *ctable);
75+
QUICConnectionId retry_cid, UDPConnection *, quiche_conn *, QUICPacketHandler *, QUICConnectionTable *ctable, SSL *);
7076

7177
// Event handlers
7278
int acceptEvent(int event, Event *e);
@@ -88,13 +94,16 @@ class QUICNetVConnection : public UnixNetVConnection,
8894
VIO *do_io_write(Continuation *c, int64_t nbytes, IOBufferReader *buf, bool owner = false) override;
8995
int connectUp(EThread *t, int fd) override;
9096
int64_t load_buffer_and_write(int64_t towrite, MIOBufferAccessor &buf, int64_t &total_written, int &needs) override;
97+
bool getSSLHandShakeComplete() const override;
9198

9299
// NetEvent
93100
virtual void net_read_io(NetHandler *nh, EThread *lthread) override;
94101

95102
// NetVConnection
96103
int populate_protocol(std::string_view *results, int n) const override;
97104
const char *protocol_contains(std::string_view tag) const override;
105+
const char *get_server_name() const override;
106+
bool support_sni() const override;
98107

99108
// QUICConnection
100109
QUICStreamManager *stream_manager() override;
@@ -144,7 +153,19 @@ class QUICNetVConnection : public UnixNetVConnection,
144153
SSL *_get_ssl_object() const override;
145154
ssl_curve_id _get_tls_curve() const override;
146155

156+
// TLSSNISupport
157+
void _fire_ssl_servername_event() override;
158+
159+
// TLSSessionResumptionSupport
160+
const IpEndpoint &_getLocalEndpoint() override;
161+
162+
// TLSCertSwitchSupport
163+
bool _isTryingRenegotiation() const override;
164+
shared_SSL_CTX _lookupContextByName(const std::string &servername, SSLCertContextType ctxType) override;
165+
shared_SSL_CTX _lookupContextByIP() override;
166+
147167
private:
168+
SSL *_ssl;
148169
QUICConfig::scoped_config _quic_config;
149170

150171
QUICConnectionId _peer_quic_connection_id; // dst cid in local
@@ -159,6 +180,9 @@ class QUICNetVConnection : public UnixNetVConnection,
159180
quiche_conn *_quiche_con = nullptr;
160181
QUICConnectionTable *_ctable = nullptr;
161182

183+
void _bindSSLObject();
184+
void _unbindSSLObject();
185+
162186
void _schedule_packet_write_ready(bool delay = false);
163187
void _unschedule_packet_write_ready();
164188
void _close_packet_write_ready(Event *data);

iocore/net/QUICMultiCertConfigLoader.cc

Lines changed: 0 additions & 71 deletions
Original file line numberDiff line numberDiff line change
@@ -109,77 +109,6 @@ QUICMultiCertConfigLoader::_set_npn_callback(SSL_CTX *ctx)
109109
return true;
110110
}
111111

112-
void
113-
QUICMultiCertConfigLoader::_set_handshake_callbacks(SSL_CTX *ssl_ctx)
114-
{
115-
SSL_CTX_set_cert_cb(ssl_ctx, QUICMultiCertConfigLoader::ssl_cert_cb, nullptr);
116-
SSL_CTX_set_tlsext_servername_callback(ssl_ctx, QUICMultiCertConfigLoader::ssl_sni_cb);
117-
118-
// Set client hello callback if needed
119-
// SSL_CTX_set_client_hello_cb(ctx, QUIC::ssl_client_hello_cb, nullptr);
120-
}
121-
122-
int
123-
QUICMultiCertConfigLoader::ssl_sni_cb(SSL *ssl, int * /*ad*/, void * /*arg*/)
124-
{
125-
// XXX: add SNIConfig support ?
126-
// XXX: add TRANSPORT_BLIND_TUNNEL support ?
127-
return 1;
128-
}
129-
130-
int
131-
QUICMultiCertConfigLoader::ssl_cert_cb(SSL *ssl, void * /*arg*/)
132-
{
133-
shared_SSL_CTX ctx = nullptr;
134-
SSLCertContext *cc = nullptr;
135-
QUICCertConfig::scoped_config lookup;
136-
const char *servername = SSL_get_servername(ssl, TLSEXT_NAMETYPE_host_name);
137-
QUICConnection *qc = static_cast<QUICConnection *>(SSL_get_ex_data(ssl, QUIC::ssl_quic_qc_index));
138-
139-
if (servername == nullptr) {
140-
servername = "";
141-
}
142-
QUICGlobalQCDebug(qc, "SNI=%s", servername);
143-
144-
// The incoming SSL_CTX is either the one mapped from the inbound IP address or the default one. If we
145-
// don't find a name-based match at this point, we *do not* want to mess with the context because we've
146-
// already made a best effort to find the best match.
147-
if (likely(servername)) {
148-
cc = lookup->find(const_cast<char *>(servername));
149-
if (cc && cc->getCtx()) {
150-
ctx = cc->getCtx();
151-
}
152-
}
153-
154-
// If there's no match on the server name, try to match on the peer address.
155-
if (ctx == nullptr) {
156-
QUICFiveTuple five_tuple = qc->five_tuple();
157-
IpEndpoint ip = five_tuple.destination();
158-
cc = lookup->find(ip);
159-
160-
if (cc && cc->getCtx()) {
161-
ctx = cc->getCtx();
162-
}
163-
}
164-
165-
bool found = true;
166-
if (ctx != nullptr) {
167-
SSL_set_SSL_CTX(ssl, ctx.get());
168-
} else {
169-
found = false;
170-
}
171-
172-
SSL_CTX *verify_ctx = nullptr;
173-
verify_ctx = SSL_get_SSL_CTX(ssl);
174-
QUICGlobalQCDebug(qc, "%s SSL_CTX %p for requested name '%s'", found ? "found" : "using", verify_ctx, servername);
175-
176-
if (verify_ctx == nullptr) {
177-
return 0;
178-
}
179-
180-
return 1;
181-
}
182-
183112
const char *
184113
QUICMultiCertConfigLoader::_debug_tag() const
185114
{

iocore/net/QUICMultiCertConfigLoader.h

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -49,12 +49,8 @@ class QUICMultiCertConfigLoader : public SSLMultiCertConfigLoader
4949

5050
private:
5151
const char *_debug_tag() const override;
52-
virtual void _set_handshake_callbacks(SSL_CTX *ssl_ctx) override;
5352
virtual bool _setup_session_cache(SSL_CTX *ctx) override;
5453
virtual bool _set_cipher_suites_for_legacy_versions(SSL_CTX *ctx) override;
5554
virtual bool _set_info_callback(SSL_CTX *ctx) override;
5655
virtual bool _set_npn_callback(SSL_CTX *ctx) override;
57-
58-
static int ssl_cert_cb(SSL *ssl, void *arg);
59-
static int ssl_sni_cb(SSL *ssl, int *ad, void *arg);
6056
};

iocore/net/QUICNetProcessor_quiche.cc

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -75,19 +75,11 @@ QUICNetProcessor::start(int, size_t stacksize)
7575
// QUICInitializeLibrary();
7676
QUICConfig::startup();
7777
QUICCertConfig::startup();
78-
QUICCertConfig::scoped_config certs;
7978
QUICConfig::scoped_config params;
80-
SSLCertContext *context = certs->get(0);
8179

8280
quiche_enable_debug_logging(debug_log, NULL);
8381
this->_quiche_config = quiche_config_new(QUICHE_PROTOCOL_VERSION);
8482
quiche_config_set_application_protos(this->_quiche_config, (uint8_t *)"\02h3\x05h3-29\x05hq-29\x05h3-27\x05hq-27", 27);
85-
if (context->userconfig->cert != nullptr) {
86-
quiche_config_load_cert_chain_from_pem_file(this->_quiche_config, context->userconfig->cert);
87-
}
88-
if (context->userconfig->key != nullptr) {
89-
quiche_config_load_priv_key_from_pem_file(this->_quiche_config, context->userconfig->key);
90-
}
9183

9284
quiche_config_set_max_idle_timeout(this->_quiche_config, params->no_activity_timeout_in());
9385
quiche_config_set_max_recv_udp_payload_size(this->_quiche_config, params->get_max_recv_udp_payload_size_in());

iocore/net/QUICNetVConnection_quiche.cc

Lines changed: 101 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,9 @@
2323

2424
#include "P_QUICNetVConnection_quiche.h"
2525
#include "P_QUICPacketHandler_quiche.h"
26+
#include "QUICMultiCertConfigLoader.h"
2627
#include "quic/QUICStream_quiche.h"
28+
#include "quic/QUICGlobals.h"
2729
#include <quiche.h>
2830

2931
static constexpr ink_hrtime WRITE_READY_INTERVAL = HRTIME_MSECONDS(2);
@@ -46,7 +48,7 @@ QUICNetVConnection::init(QUICVersion version, QUICConnectionId peer_cid, QUICCon
4648
void
4749
QUICNetVConnection::init(QUICVersion version, QUICConnectionId peer_cid, QUICConnectionId original_cid, QUICConnectionId first_cid,
4850
QUICConnectionId retry_cid, UDPConnection *udp_con, quiche_conn *quiche_con,
49-
QUICPacketHandler *packet_handler, QUICConnectionTable *ctable)
51+
QUICPacketHandler *packet_handler, QUICConnectionTable *ctable, SSL *ssl)
5052
{
5153
SET_HANDLER((NetVConnHandler)&QUICNetVConnection::acceptEvent);
5254
this->_udp_con = udp_con;
@@ -61,6 +63,10 @@ QUICNetVConnection::init(QUICVersion version, QUICConnectionId peer_cid, QUICCon
6163
this->_ctable->insert(this->_quic_connection_id, this);
6264
this->_ctable->insert(this->_original_quic_connection_id, this);
6365
}
66+
67+
this->_ssl = ssl;
68+
SSL_set_ex_data(ssl, QUIC::ssl_quic_qc_index, static_cast<QUICConnection *>(this));
69+
this->_bindSSLObject();
6470
}
6571

6672
void
@@ -114,6 +120,7 @@ QUICNetVConnection::free(EThread *t)
114120
this->_context->trigger(QUICContext::CallbackEvent::CONNECTION_CLOSE);
115121
ALPNSupport::clear();
116122
TLSBasicSupport::clear();
123+
TLSCertSwitchSupport::_clear();
117124

118125
this->_packet_handler->close_connection(this);
119126
this->_packet_handler = nullptr;
@@ -489,6 +496,32 @@ QUICNetVConnection::load_buffer_and_write(int64_t towrite, MIOBufferAccessor &bu
489496
return 0;
490497
}
491498

499+
bool
500+
QUICNetVConnection::getSSLHandShakeComplete() const
501+
{
502+
return quiche_conn_is_established(this->_quiche_con);
503+
}
504+
505+
void
506+
QUICNetVConnection::_bindSSLObject()
507+
{
508+
TLSBasicSupport::bind(this->_ssl, this);
509+
ALPNSupport::bind(this->_ssl, this);
510+
TLSSessionResumptionSupport::bind(this->_ssl, this);
511+
TLSSNISupport::bind(this->_ssl, this);
512+
TLSCertSwitchSupport::bind(this->_ssl, this);
513+
}
514+
515+
void
516+
QUICNetVConnection::_unbindSSLObject()
517+
{
518+
TLSBasicSupport::unbind(this->_ssl);
519+
ALPNSupport::unbind(this->_ssl);
520+
TLSSessionResumptionSupport::unbind(this->_ssl);
521+
TLSSNISupport::unbind(this->_ssl);
522+
TLSCertSwitchSupport::unbind(this->_ssl);
523+
}
524+
492525
void
493526
QUICNetVConnection::_schedule_packet_write_ready(bool delay)
494527
{
@@ -632,14 +665,79 @@ QUICNetVConnection::protocol_contains(std::string_view tag) const
632665
return "";
633666
}
634667

668+
const char *
669+
QUICNetVConnection::get_server_name() const
670+
{
671+
return get_sni_server_name();
672+
}
673+
674+
bool
675+
QUICNetVConnection::support_sni() const
676+
{
677+
return true;
678+
}
679+
635680
SSL *
636681
QUICNetVConnection::_get_ssl_object() const
637682
{
638-
return nullptr;
683+
return this->_ssl;
639684
}
640685

641686
ssl_curve_id
642687
QUICNetVConnection::_get_tls_curve() const
643688
{
644-
return 0;
689+
if (getSSLSessionCacheHit()) {
690+
return getSSLCurveNID();
691+
} else {
692+
return SSLGetCurveNID(this->_ssl);
693+
}
694+
}
695+
696+
void
697+
QUICNetVConnection::_fire_ssl_servername_event()
698+
{
699+
}
700+
701+
const IpEndpoint &
702+
QUICNetVConnection::_getLocalEndpoint()
703+
{
704+
return this->local_addr;
705+
}
706+
707+
bool
708+
QUICNetVConnection::_isTryingRenegotiation() const
709+
{
710+
// Renegotiation is not allowed on QUIC (TLS 1.3) connections.
711+
// If handshake is completed when this function is called, that should be unallowed attempt of renegotiation.
712+
return this->getSSLHandShakeComplete();
713+
}
714+
715+
shared_SSL_CTX
716+
QUICNetVConnection::_lookupContextByName(const std::string &servername, SSLCertContextType ctxType)
717+
{
718+
shared_SSL_CTX ctx = nullptr;
719+
QUICCertConfig::scoped_config lookup;
720+
SSLCertContext *cc = lookup->find(servername, ctxType);
721+
722+
if (cc && cc->getCtx()) {
723+
ctx = cc->getCtx();
724+
}
725+
726+
return ctx;
727+
}
728+
729+
shared_SSL_CTX
730+
QUICNetVConnection::_lookupContextByIP()
731+
{
732+
shared_SSL_CTX ctx = nullptr;
733+
QUICCertConfig::scoped_config lookup;
734+
QUICFiveTuple five_tuple = this->five_tuple();
735+
IpEndpoint ip = five_tuple.destination();
736+
SSLCertContext *cc = lookup->find(ip);
737+
738+
if (cc && cc->getCtx()) {
739+
ctx = cc->getCtx();
740+
}
741+
742+
return ctx;
645743
}

iocore/net/QUICPacketHandler_quiche.cc

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929
#include "P_QUICNetProcessor_quiche.h"
3030
#include "P_QUICClosedConCollector.h"
3131
#include "quic/QUICConnectionTable.h"
32+
#include "QUICMultiCertConfigLoader.h"
3233
#include <quiche.h>
3334

3435
static constexpr char debug_tag[] = "quic_sec";
@@ -274,10 +275,14 @@ QUICPacketHandlerIn::_recv_packet(int event, UDPPacket *udp_packet)
274275
}
275276

276277
QUICConnectionId new_cid;
277-
quiche_conn *quiche_con = quiche_accept(
278+
279+
QUICCertConfig::scoped_config server_cert;
280+
SSL *ssl = SSL_new(server_cert->defaultContext());
281+
282+
quiche_conn *quiche_con = quiche_conn_new_with_tls(
278283
new_cid, new_cid.length(), retry_token.original_dcid(), retry_token.original_dcid().length(), &udp_packet->to.sa,
279284
udp_packet->to.isIp4() ? sizeof(udp_packet->to.sin) : sizeof(udp_packet->to.sin6), &udp_packet->from.sa,
280-
udp_packet->from.isIp4() ? sizeof(udp_packet->from.sin) : sizeof(udp_packet->from.sin6), &this->_quiche_config);
285+
udp_packet->from.isIp4() ? sizeof(udp_packet->from.sin) : sizeof(udp_packet->from.sin6), &this->_quiche_config, ssl, true);
281286

282287
if (params->get_qlog_file_base_name() != nullptr) {
283288
char qlog_filepath[PATH_MAX];
@@ -296,7 +301,7 @@ QUICPacketHandlerIn::_recv_packet(int event, UDPPacket *udp_packet)
296301
// vc->init(version, peer_cid, original_cid, ocid_in_retry_token, rcid_in_retry_token, udp_packet->getConnection(), this,
297302
// &this->_ctable);
298303
vc->init(version, peer_cid, new_cid, QUICConnectionId::ZERO(), QUICConnectionId::ZERO(), udp_packet->getConnection(),
299-
quiche_con, this, &this->_ctable);
304+
quiche_con, this, &this->_ctable, ssl);
300305
vc->id = net_next_connection_number();
301306
vc->con.move(con);
302307
vc->submit_time = Thread::get_hrtime();

tests/gold_tests/timeout/active_timeout.test.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,7 @@
6464
tr3.Processes.Default.Command = 'curl -k -i --http2 https://127.0.0.1:{0}/file'.format(ts.Variables.ssl_port)
6565
tr3.Processes.Default.Streams.stdout = Testers.ContainsExpression("Activity Timeout", "Request should fail with active timeout")
6666

67-
if Condition.HasATSFeature('TS_USE_QUIC') and Condition.HasCurlFeature('http3'):
67+
if Condition.HasATSFeature('TS_HAS_QUICHE') and Condition.HasCurlFeature('http3'):
6868
tr4 = Test.AddTestRun("tr")
6969
tr4.Processes.Default.Command = 'curl -k -i --http3 https://127.0.0.1:{0}/file'.format(ts.Variables.ssl_port)
7070
tr4.Processes.Default.Streams.stdout = Testers.ContainsExpression("Activity Timeout", "Request should fail with active timeout")

0 commit comments

Comments
 (0)