quic: support hostname verification · nodejs/node@5989f4a · GitHub
Skip to content

Commit 5989f4a

Browse files
jasnelladuh95
authored andcommitted
quic: support hostname verification
Signed-off-by: James M Snell <jasnell@gmail.com> PR-URL: #63483 Reviewed-By: Stephen Belanger <admin@stephenbelanger.com> Reviewed-By: Matteo Collina <matteo.collina@gmail.com>
1 parent b4d30e7 commit 5989f4a

6 files changed

Lines changed: 48 additions & 15 deletions

File tree

lib/internal/quic/quic.js

Lines changed: 5 additions & 0 deletions

src/quic/bindingdata.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -169,6 +169,7 @@ class SessionManager;
169169
V(unacknowledged_packet_threshold, "unacknowledgedPacketThreshold") \
170170
V(validate_address, "validateAddress") \
171171
V(verify_client, "verifyClient") \
172+
V(verify_hostname, "verifyHostname") \
172173
V(verify_peer_strict, "verifyPeerStrict") \
173174
V(verify_private_key, "verifyPrivateKey") \
174175
V(version, "version")

src/quic/session.cc

Lines changed: 6 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -618,8 +618,7 @@ Maybe<Session::Options> Session::Options::From(Environment* env,
618618
!SET(keep_alive_timeout) || !SET(max_stream_window) || !SET(max_window) ||
619619
!SET(max_payload_size) || !SET(unacknowledged_packet_threshold) ||
620620
!SET(cc_algorithm) || !SET(draining_period_multiplier) ||
621-
!SET(max_datagram_send_attempts) ||
622-
!SET(stream_idle_timeout)) {
621+
!SET(max_datagram_send_attempts) || !SET(stream_idle_timeout)) {
623622
return Nothing<Options>();
624623
}
625624

@@ -2831,8 +2830,8 @@ void Session::ShutdownStream(stream_id id, QuicError error) {
28312830

28322831
void Session::ShutdownStreamWrite(stream_id id, QuicError error) {
28332832
DCHECK(!is_destroyed());
2834-
Debug(this, "Shutting down stream %" PRIi64 " write with error %s",
2835-
id, error);
2833+
Debug(
2834+
this, "Shutting down stream %" PRIi64 " write with error %s", id, error);
28362835
SendPendingDataScope send_scope(this);
28372836
error_code code;
28382837
if (error.type() == QuicError::Type::APPLICATION) {
@@ -3058,17 +3057,15 @@ void Session::CheckStreamIdleTimeout(uint64_t now) {
30583057

30593058
uint64_t last_activity = stream->last_activity_timestamp();
30603059
if (last_activity > 0 && (now - last_activity) > timeout_ns) {
3061-
Debug(this,
3062-
"Stream %" PRId64 " idle timeout exceeded, destroying",
3063-
id);
3060+
Debug(this, "Stream %" PRId64 " idle timeout exceeded, destroying", id);
30643061
// Notify the peer before destroying. ShutdownStream sends both
30653062
// STOP_SENDING and RESET_STREAM as appropriate, using the
30663063
// application's no-error code for non-APPLICATION errors (since
30673064
// these frames carry application-level error codes per RFC 9000).
30683065
// Without this, the peer's stream sits orphaned until the
30693066
// session closes.
3070-
auto error = QuicError::ForTransport(NGTCP2_ERR_PROTO,
3071-
"stream idle timeout");
3067+
auto error =
3068+
QuicError::ForTransport(NGTCP2_ERR_PROTO, "stream idle timeout");
30723069
ShutdownStream(id, error);
30733070
stream->Destroy(error);
30743071
STAT_INCREMENT(Stats, streams_idle_timed_out);

src/quic/streams.cc

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1891,6 +1891,7 @@ void Stream::EmitClose(const QuicError& error) {
18911891
}
18921892

18931893
void Stream::EmitHeaders() {
1894+
STAT_RECORD_TIMESTAMP(Stats, received_at);
18941895
// state()->wants_headers will be set from the javascript side if the
18951896
// stream object has a handler for the headers event.
18961897
if (!env()->can_call_into_js() || !state()->wants_headers) {

src/quic/tlscontext.cc

Lines changed: 27 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -261,6 +261,18 @@ bool OSSLContext::set_hostname(std::string_view hostname) const {
261261
const_cast<char*>(name.c_str())) == 1;
262262
}
263263

264+
bool OSSLContext::set_verify_hostname(std::string_view hostname) const {
265+
// SSL_set1_host tells OpenSSL to verify the peer certificate's
266+
// subject name (SAN/CN) matches this hostname. This is separate
267+
// from SSL_set_tlsext_host_name which only sets the SNI extension.
268+
static const char* kDefaultHostname = "localhost";
269+
if (hostname.empty()) {
270+
return SSL_set1_host(*this, kDefaultHostname) == 1;
271+
} else {
272+
return SSL_set1_host(*this, hostname.data()) == 1;
273+
}
274+
}
275+
264276
bool OSSLContext::set_early_data_enabled() const {
265277
return SSL_set_quic_tls_early_data_enabled(*this, 1) == 1;
266278
}
@@ -714,12 +726,13 @@ Maybe<TLSContext::Options> TLSContext::Options::From(Environment* env,
714726
env, &options, params, state.name##_string())
715727

716728
if (!SET(verify_client) || !SET(reject_unauthorized) ||
717-
!SET(verify_peer_strict) || !SET(enable_early_data) ||
718-
!SET(enable_tls_trace) || !SET(alpn) || !SET(servername) ||
719-
!SET(ciphers) || !SET(groups) || !SET(verify_private_key) ||
720-
!SET(keylog) || !SET(port) || !SET(authoritative) ||
721-
!SET_VECTOR(crypto::KeyObjectData, keys) || !SET_VECTOR(Store, certs) ||
722-
!SET_VECTOR(Store, ca) || !SET_VECTOR(Store, crl)) {
729+
!SET(verify_hostname) || !SET(verify_peer_strict) ||
730+
!SET(enable_early_data) || !SET(enable_tls_trace) || !SET(alpn) ||
731+
!SET(servername) || !SET(ciphers) || !SET(groups) ||
732+
!SET(verify_private_key) || !SET(keylog) || !SET(port) ||
733+
!SET(authoritative) || !SET_VECTOR(crypto::KeyObjectData, keys) ||
734+
!SET_VECTOR(Store, certs) || !SET_VECTOR(Store, ca) ||
735+
!SET_VECTOR(Store, crl)) {
723736
return Nothing<Options>();
724737
}
725738

@@ -854,6 +867,14 @@ void TLSSession::Initialize(
854867
return;
855868
}
856869

870+
if (options.verify_hostname) {
871+
if (!ossl_context_.set_verify_hostname(options.servername)) {
872+
validation_error_ = "Failed to set verify hostname";
873+
ossl_context_.reset();
874+
return;
875+
}
876+
}
877+
857878
if (maybeSessionTicket.has_value()) {
858879
const auto& sessionTicket = *maybeSessionTicket;
859880
uv_buf_t buf = sessionTicket.ticket();

src/quic/tlscontext.h

Lines changed: 8 additions & 0 deletions

0 commit comments

Comments
 (0)