Add TLS ClientHello inspection API#47
Open
DeagleGross wants to merge 3 commits into
Open
Conversation
Surface the raw, unparsed ClientHello handshake bytes to managed callers so ASP.NET Core Kestrel can inspect them directly off the new DirectSsl transport. Native (System.Security.Cryptography.Native): - Register SSL_CTX_set_msg_callback (REQUIRED_FUNCTION + _ptr shim define) and light up SSL_CTX_set_client_hello_cb. - Capture the inbound ClientHello once into per-SSL ex_data via msg_callback, then suspend the handshake from client_hello_cb (SSL_CLIENT_HELLO_RETRY) so SSL_do_handshake returns SSL_ERROR_WANT_CLIENT_HELLO_CB exactly once. - Add CryptoNative_SslCtxSetClientHelloCallback / CryptoNative_SslGetClientHelloData and their entrypoints; free the capture on SSL_free. Managed (System.Net.Security): - Map SSL_ERROR_WANT_CLIENT_HELLO_CB to TlsOperationStatus.NeedsClientHello. - Expose zero-copy ReadOnlySpan<byte> TlsSession.GetClientHelloBytes() (valid until the session is disposed) and EnableClientHelloInspection wiring. - Arm the callback on the SSL_CTX when IsServer and inspection is enabled. Adds TlsSessionAspNetCoreCallbacksTests covering the fd-mode capture flow. Note: BIO-mode wiring is intentionally not included yet. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
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.
New API lets a server pause its handshake at the ClientHello and read the TLS client hello as was received by OpenSSL, before any certificate or server-option decision.
1.
TlsContext.EnableClientHelloInspectionnamespace System.Net.Security; public sealed partial class TlsContext : IDisposable { + public bool EnableClientHelloInspection { get; set; } }Opt-in flag, set on the server context before the first handshake. When
true, the handshake suspends once at ClientHello. Defaultfalse(no behavior change).2.
TlsOperationStatus.NeedsClientHellonamespace System.Net.Security; public enum TlsOperationStatus { Complete = 0, WantRead = 1, WantWrite = 2, Closed = 3, WantCredentials = 4, NeedsCertificateValidation = 5, NeedsServerOptions = 6, /// <summary> /// Server-side only. The handshake paused at ClientHello because raw ClientHello /// inspection was enabled on the <see cref="TlsContext"/>. Retrieve the raw bytes /// via <see cref="TlsSession.GetClientHelloBytes"/>, then call /// <see cref="TlsSession.Handshake"/> (fd mode) or /// <see cref="TlsSession.ProcessHandshake"/> again to resume the handshake. /// </summary> + NeedsClientHello = 7, }New suspension reason returned by
Handshake()/ProcessHandshake(). Means: the ClientHello has arrived and is available; callGetClientHelloBytes(), then callHandshake()/ProcessHandshake()again to resume.3.
TlsSession.GetClientHelloBytes()namespace System.Net.Security; public sealed partial class TlsSession { + public ReadOnlySpan<byte> GetClientHelloBytes(); }Returns the raw ClientHello handshake message (handshake type + 3-byte length + body; no 5-byte TLS record header). Zero-copy view over OpenSSL's own buffer; valid until the next
Handshake()/ProcessHandshake()call. Empty if inspection wasn't enabled or the ClientHello hasn't arrived yet.Under the hood
When
EnableClientHelloInspectionis set, the server'sSSL_CTXis armed with two OpenSSL callbacks:SSL_CTX_set_msg_callback(copies the inbound ClientHello bytes into per-SSLex_data) andSSL_CTX_set_client_hello_cb(returnsSSL_CLIENT_HELLO_RETRYexactly once). That makesSSL_do_handshakereturnSSL_ERROR_WANT_CLIENT_HELLO_CB, which the managed layer maps toTlsOperationStatus.NeedsClientHello.GetClientHelloBytes()just returns a span over the already-capturedex_databuffer — no parsing, no copy. The nextHandshake()/ProcessHandshake()re-enters the suspendedSSL_do_handshake, the callback now returns "success," and the handshake proceeds normally (emits ServerHello, etc.).How it maps to the two drive modes
Handshake(), OpenSSL owns the socket): suspends after OpenSSL reads the ClientHello off the fd itself.ProcessHandshake(input, output, ...), caller owns the socket): the ClientHello is consumed from theinputspan into OpenSSL's BIO, then it suspends. On resume the caller passes empty input (the bytes were already consumed) and OpenSSL writes the ServerHello to theoutputspan.Typical caller loop (fd-mode)