Skip to content

fix(server): downcast to MaybeTls in client_certificates to avoid panic#446

Merged
sunng87 merged 1 commit into
sunng87:masterfrom
vladikbr:fix/client-certificates-maybetls-downcast
Jun 29, 2026
Merged

fix(server): downcast to MaybeTls in client_certificates to avoid panic#446
sunng87 merged 1 commit into
sunng87:masterfrom
vladikbr:fix/client-certificates-maybetls-downcast

Conversation

@vladikbr

Copy link
Copy Markdown
Contributor

Fixes #445.

Problem

ClientInfo::client_certificates() panics on every TLS connection driven by process_socket. negotiate_tls wraps the negotiated stream in the MaybeTls enum, but client_certificates() downcast it to a bare TlsStream<TcpStream>:

let socket =
    <dyn std::any::Any>::downcast_ref::<TlsStream<TcpStream>>(self.get_ref()).unwrap();

The runtime type is MaybeTls, so downcast_ref returns None and .unwrap() panics. This makes peer certificates (and thus mTLS client-cert auth) unreachable from a StartupHandler.

Fix

Downcast to MaybeTls and match the Tls arm; non-TLS variants return None. The Tls arm already implies a secure connection, so the separate is_secure() guard is no longer needed.

let any = self.get_ref() as &dyn std::any::Any;
match any.downcast_ref::<MaybeTls>() {
    Some(MaybeTls::Tls(tls)) => {
        let (_, tls_session) = tls.get_ref();
        tls_session.peer_certificates()
    }
    _ => None,
}

Tests

  • New client_certificates_does_not_panic_under_maybe_tls: reconstructs the exact MaybeTls-over-TcpStream wrapper that process_socket produces (over a real loopback TCP + TLS handshake) and asserts client_certificates() returns None (the client presents no cert) instead of panicking. Verified it fails on the old code with the original panic and passes with the fix.
  • Made the existing server_name_metadata_is_set_from_tls_sni_in_memory test tolerant of an already-installed process-global crypto provider (install_default is install-once), so both TLS tests can run in the same test binary without one's unwrap() failing.

Suite passes under both server-api-ring and server-api-aws-lc-rs.

client_certificates() downcast the connection to TlsStream<TcpStream>,
but process_socket wraps the negotiated stream in the MaybeTls enum, so
the downcast always returned None and unwrap() panicked on every TLS
connection -- making client-certificate auth unreachable from a
StartupHandler.

Downcast to MaybeTls and match the Tls arm instead; non-TLS variants
return None (the Tls arm already implies a secure connection, so the
is_secure() guard is no longer needed).

Add a test that reconstructs the real MaybeTls-over-TcpStream wrapper
process_socket produces and asserts client_certificates() returns None
without panicking. Make the existing SNI test tolerant of an
already-installed process-global crypto provider so both TLS tests can
run in the same test binary.

Fixes sunng87#445

@sunng87 sunng87 left a comment

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM as a backward-compatible fix.

In next minor release, I'm going to introduce a trait for the T so we can avoid downcast here.

@sunng87

sunng87 commented Jun 29, 2026

Copy link
Copy Markdown
Owner

Thank you @vladikbr for reporting and a quick fix!

@sunng87 sunng87 merged commit 44d63a0 into sunng87:master Jun 29, 2026
10 checks passed
@sunng87

sunng87 commented Jun 29, 2026

Copy link
Copy Markdown
Owner

Released in 0.40.4

@vladikbr

Copy link
Copy Markdown
Contributor Author

Thanks!

@vladikbr vladikbr deleted the fix/client-certificates-maybetls-downcast branch June 29, 2026 13:45
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

ClientInfo::client_certificates() panics under process_socket on every TLS connection (downcasts to TlsStream instead of MaybeTls)

2 participants