Fix lock during SslStream renegotiation request (#56470)
authorJan Jahoda <jajahoda@microsoft.com>
Thu, 5 Aug 2021 08:53:58 +0000 (10:53 +0200)
committerGitHub <noreply@github.com>
Thu, 5 Aug 2021 08:53:58 +0000 (10:53 +0200)
* Change lock and buffer test order

* revert _nestedAuth clearing

* Clear nested lock

* Remove ability to renegotiate again when fail

src/libraries/System.Net.Security/src/System/Net/Security/SslStream.Implementation.cs
src/libraries/System.Net.Security/tests/FunctionalTests/SslStreamNetworkStreamTest.cs

index 0cd8884..add3a7d 100644 (file)
@@ -268,16 +268,17 @@ namespace System.Net.Security
                 throw new NotSupportedException(SR.Format(SR.net_io_invalidnestedcall, nameof(WriteAsync), "write"));
             }
 
-            if (_decryptedBytesCount is not 0)
+            try
             {
-                throw new InvalidOperationException(SR.net_ssl_renegotiate_buffer);
-            }
+                if (_decryptedBytesCount is not 0)
+                {
+                    throw new InvalidOperationException(SR.net_ssl_renegotiate_buffer);
+                }
+
+                _sslAuthenticationOptions!.RemoteCertRequired = true;
+                _isRenego = true;
 
-            _sslAuthenticationOptions!.RemoteCertRequired = true;
-            _isRenego = true;
 
-            try
-            {
                 SecurityStatusPal status = _context!.Renegotiate(out byte[]? nextmsg);
 
                 if (nextmsg is {} && nextmsg.Length > 0)
index b3a5f5e..3428ba7 100644 (file)
@@ -369,6 +369,7 @@ namespace System.Net.Security.Tests
             using (server)
             {
                 using X509Certificate2 serverCertificate = Configuration.Certificates.GetServerCertificate();
+                using X509Certificate2 clientCertificate = Configuration.Certificates.GetClientCertificate();
 
                 SslClientAuthenticationOptions clientOptions = new SslClientAuthenticationOptions()
                 {
@@ -376,8 +377,12 @@ namespace System.Net.Security.Tests
                     EnabledSslProtocols = SslProtocols.Tls | SslProtocols.Tls11 | SslProtocols.Tls12,
                 };
                 clientOptions.RemoteCertificateValidationCallback = (sender, certificate, chain, sslPolicyErrors) => true;
+                clientOptions.LocalCertificateSelectionCallback = (sender, targetHost, localCertificates, remoteCertificate, acceptableIssuers) =>
+                {
+                    return clientCertificate;
+                };
                 SslServerAuthenticationOptions serverOptions = new SslServerAuthenticationOptions() { ServerCertificate = serverCertificate };
-
+                serverOptions.RemoteCertificateValidationCallback = (sender, certificate, chain, sslPolicyErrors) => true;
                 await TestConfiguration.WhenAllOrAnyFailedWithTimeout(
                                 client.AuthenticateAsClientAsync(clientOptions, cts.Token),
                                 server.AuthenticateAsServerAsync(serverOptions, cts.Token));
@@ -392,6 +397,12 @@ namespace System.Net.Security.Tests
                 await Assert.ThrowsAsync<InvalidOperationException>(()=>
                     server.NegotiateClientCertificateAsync(cts.Token)
                 );
+
+                // Drain client data.
+                await server.ReadAsync(new byte[499]);
+                // Verify that the session is usable even renego request failed.
+                await TestHelper.PingPong(client, server, cts.Token);
+                await TestHelper.PingPong(server, client, cts.Token);
             }
         }