Skip to content

QuicListener accepts an invalid connection after it was rejected due to client cert issues #57246

@Tratcher

Description

@Tratcher

Scenario:

  1. The listener sets ClientCertificateRequired and does not override RemoteCertificateValidationCallback
  2. The client connects using a client certificate that's invalid (self-signed)
  3. The listener accepts the connection even though validation failed
  4. Calling OpenUnidirectionalStream from the server fails with INVALID_STATE

Workaround: override RemoteCertificateValidationCallback to accept the certificate.

Expected: AcceptConnectionAsync should not return connections that have already failed due to client cert validation errors.

  Message: 
    System.Net.Quic.QuicException : Failed to open stream to peer. Error Code: INVALID_STATE

  Stack Trace: 
    QuicExceptionHelpers.ThrowIfFailed(UInt32 status, String message, Exception innerException)
    MsQuicStream.ctor(State connectionState, QUIC_STREAM_OPEN_FLAGS flags)
    MsQuicConnection.OpenUnidirectionalStream()
    QuicConnection.OpenUnidirectionalStream()
    QuicConnectionContextTests.ClientCertificate_ControlStream() line 775
    --- End of stack trace from previous location ---

  Standard Output: 
    | [0.007s] TestLifetime Information: Starting test ClientCertificate_ControlStream at 2021-08-11T23:36:26
    | [0.224s] Microsoft.AspNetCore.Server.Kestrel.Transport.Quic.Tests.QuicConnectionContextTests Error: Test threw an exception.
    | System.Net.Quic.QuicException: Failed to open stream to peer. Error Code: INVALID_STATE
    |    at System.Net.Quic.Implementations.MsQuic.Internal.QuicExceptionHelpers.ThrowIfFailed(UInt32 status, String message, Exception innerException)
    |    at System.Net.Quic.Implementations.MsQuic.MsQuicStream..ctor(State connectionState, QUIC_STREAM_OPEN_FLAGS flags)
    |    at System.Net.Quic.Implementations.MsQuic.MsQuicConnection.OpenUnidirectionalStream()
    |    at System.Net.Quic.QuicConnection.OpenUnidirectionalStream()
    |    at Microsoft.AspNetCore.Server.Kestrel.Transport.Quic.Tests.QuicConnectionContextTests.ClientCertificate_ControlStream() in D:\github\aspnetcore\src\Servers\Kestrel\Transport.Quic\test\QuicConnectionContextTests.cs:line 775
    |    at Xunit.Sdk.TestInvoker`1.<>c__DisplayClass48_1.<<InvokeTestMethodAsync>b__1>d.MoveNext() in C:\Dev\xunit\xunit\src\xunit.execution\Sdk\Frameworks\Runners\TestInvoker.cs:line 264
    | --- End of stack trace from previous location ---
    |    at Xunit.Sdk.ExecutionTimer.AggregateAsync(Func`1 asyncAction) in C:\Dev\xunit\xunit\src\xunit.execution\Sdk\Frameworks\ExecutionTimer.cs:line 48
    |    at Xunit.Sdk.ExceptionAggregator.RunAsync(Func`1 code) in C:\Dev\xunit\xunit\src\xunit.core\Sdk\ExceptionAggregator.cs:line 90
    | [0.245s] TestLifetime Information: Finished test ClientCertificate_ControlStream in 0.2425817s 
            var listenerOptions = new QuicListenerOptions()
            {
                MaxBidirectionalStreams = 100,
                MaxUnidirectionalStreams = 100,
                ServerAuthenticationOptions = new SslServerAuthenticationOptions()
                {
                    ServerCertificate = TestResources.GetTestCertificate(),
                    ApplicationProtocols = new List<SslApplicationProtocol>() { new SslApplicationProtocol("h3") },
                    ClientCertificateRequired = true,
                    // RemoteCertificateValidationCallback = RemoteCertificateValidationCallback,
                },
                ListenEndPoint = new IPEndPoint(IPAddress.Loopback, 0),
            };
            var listener = new QuicListener(listenerOptions);
            var acceptConnection = listener.AcceptConnectionAsync();

            var clientOptions = new QuicClientConnectionOptions
            {
                MaxBidirectionalStreams = 200,
                MaxUnidirectionalStreams = 200,
                RemoteEndPoint = listener.ListenEndPoint,
                ClientAuthenticationOptions = new SslClientAuthenticationOptions
                {
                    ApplicationProtocols = new List<SslApplicationProtocol>
                    {
                        new SslApplicationProtocol("h3")
                    },
                    RemoteCertificateValidationCallback = RemoteCertificateValidationCallback
                }
            };
            var testCert = TestResources.GetTestCertificate();
            clientOptions.ClientAuthenticationOptions.ClientCertificates = new X509CertificateCollection { testCert };

            using var clientConnection = new QuicConnection(clientOptions);
            await clientConnection.ConnectAsync().DefaultTimeout();

            var serverConnection = await acceptConnection;

            var clientStreamAccept = clientConnection.AcceptStreamAsync();

            var serverStream = serverConnection.OpenUnidirectionalStream();

            var clientStream = await clientStreamAccept;

            static bool RemoteCertificateValidationCallback(object sender, X509Certificate certificate, X509Chain chain, SslPolicyErrors sslPolicyErrors)
            {
                return true;
            }

Metadata

Metadata

Assignees

Type

No type

Projects

No projects

Milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions