Repository navigation
tlshd: Verify the server certificate on every client handshake - #61
Merged
Merged
Conversation
When the kernel requests an anonymous client handshake (HANDSHAKE_AUTH_UNAUTH) on a QUIC socket, tlshd_quic_client_set_x509_session() loads the client trust store but installs no verify callback. GnuTLS does not verify a peer certificate on its own, so the handshake completes with any server certificate: one from an unknown CA, a self-signed one, or one issued for another name. The peer name is used only for SNI, and the kernel consumer is told the handshake succeeded. The TCP anonymous path authenticates the server with tlshd_client_verify_peer(), and Documentation/networking/ tls-handshake.rst states that an anonymous client handshake still performs server authentication. Install the same check on the QUIC path, through one verify callback that selects the anonymous or mutual-auth check by the kernel's auth mode. The QUIC client has marked the anonymous path by storing a sentinel in conn->cert_req, a two-bit field that otherwise carries the kernel's quic_config.certificate_request verbatim. The auth mode is the request the kernel made, and parms is reachable from the verify callback, so dispatch on it directly and retire the sentinel. A resumed session carries no Certificate message, so the verify callback does not run on it, and a ticket does not record whether the handshake that issued it authenticated the server. GnuTLS stores the server's chain in the resumption data. After a resumed anonymous handshake, verify that chain before reporting success to the kernel. With verification in place, the verify callback records the PKIX or DANE result, so the anonymous QUIC path no longer marks the handshake unauthenticated for the DANE audit. That mark has no other user; retire it. The anonymous credential carries no client key, so GNUTLS_CERTIFICATE_SKIP_KEY_CERT_MATCH has nothing to skip there; drop it too. Fixes: 43a15fe ("tlshd: add support for quic handshake") Signed-off-by: Andre Sieber Arens <andre@sieberarens.com> Reviewed-by: Xin Long <lucien.xin@gmail.com> Signed-off-by: Chuck Lever <cel@kernel.org>
The TCP and QUIC anonymous client handshakes set GNUTLS_VERIFY_ALLOW_SIGN_RSA_MD2 and GNUTLS_VERIFY_ALLOW_SIGN_RSA_MD5 on their credentials and set GNUTLS_CERTIFICATE_SKIP_OCSP_RESPONSE_CHECK as well, while the mutual-auth client handshakes set none of the three. An anonymous client therefore accepts a server chain that a mutual-auth client to the same server rejects, although the server is authenticated the same way on both paths. The verify flags date to the initial commit of ktls-utils, under a comment describing them as allowing self-signed server certificates. They never did that. GnuTLS has refused MD2 and MD5 chain signatures since release 1.2.9 in 2005 and offers these flags only for applications that must interoperate with such chains. A self-signed certificate is accepted when it is in the trust store, irrespective of its signature algorithm, and rejected otherwise, with or without the flags. The only chain the flags admit is one in which a trusted CA signed the server certificate or an intermediate with MD2 or MD5. No issue, pull request, or list thread has ever asked for that. The OCSP skip dates to the same commit and carries no rationale. With it set, gnutls_certificate_verify_peers3() ignores the OCSP response a server staples, so an anonymous client completes the handshake with a server whose stapled response marks its own certificate revoked. On the mutual-auth path the same response fails verification with GNUTLS_CERT_REVOKED. Drop the verify flags and the OCSP skip from both anonymous paths so every client handshake verifies the server chain under the GnuTLS default policy. After the change, a server chain carrying an MD2 or MD5 signature, or a stapled OCSP response that is invalid or reports revocation, fails the anonymous handshake as it already fails the mutual-auth handshake. Signed-off-by: Chuck Lever <cel@kernel.org>
GNUTLS_CERTIFICATE_SKIP_KEY_CERT_MATCH tells GnuTLS not to check that a private key loaded into a credential matches its certificate. The anonymous TCP client handshake loads no key or certificate into xcred, so the flag has nothing to act on there. It has sat in the credential flags since the initial commit and suggests a client identity that this path never carries. Drop the flag. The anonymous TCP path then sets no credential flags, as the anonymous QUIC client path already does. Signed-off-by: Chuck Lever <cel@kernel.org>
chucklever
force-pushed
the
quic-anon-verify
branch
2 times, most recently
from
October 20, 2026 18:19
d1fa053 to
f74eb75
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
An anonymous client handshake (HANDSHAKE_AUTH_UNAUTH) is one where
the client presents no certificate. Documentation/networking/
tls-handshake.rst states that tlshd still authenticates the server
on that path. As the code stands, the QUIC anonymous client path
does not verify the server certificate at all, and the TCP anonymous
path verifies it under a weaker policy than the mutual-auth paths:
MD2 and MD5 chain signatures are allowed and a stapled OCSP response
is ignored.
After this series every client handshake, TCP or QUIC, anonymous or
mutual-auth, verifies the server chain under the GnuTLS default
policy. One verify callback serves both QUIC client modes and
selects the anonymous or mutual-auth check from the auth mode the
kernel requested (patch 1). A resumed QUIC session carries no
Certificate message, so the callback does not run on it. The chain
GnuTLS stored in the resumption data is verified instead before the
handshake is reported to the kernel.
This is a behavioral change for anonymous clients. A server chain
signed with MD2 or MD5, or a stapled OCSP response that is invalid
or reports revocation, now fails the anonymous handshake as it
already fails the mutual-auth handshake.