[QUIC] Support for OpenSSL build of MsQuic on Windows (Attempt 2) (#72609)
authorRadek Zikmund <32671551+rzikm@users.noreply.github.com>
Fri, 22 Jul 2022 12:40:38 +0000 (14:40 +0200)
committerGitHub <noreply@github.com>
Fri, 22 Jul 2022 12:40:38 +0000 (14:40 +0200)
* Initial OpenSSL MsQuic support

* Don't check Windows version when OpenSSL is used

* Update src/libraries/System.Net.Quic/src/System/Net/Quic/Internal/MsQuicApi.cs

Co-authored-by: Marie Píchová <11718369+ManickaP@users.noreply.github.com>
* Gracefully close the API handle before unloading the library

* Minor formatting change

* Update src/libraries/System.Net.Quic/src/System/Net/Quic/Internal/MsQuicApi.cs

Co-authored-by: Marie Píchová <11718369+ManickaP@users.noreply.github.com>
Co-authored-by: Marie Píchová <11718369+ManickaP@users.noreply.github.com>
src/libraries/System.Net.Quic/src/System/Net/Quic/Internal/MsQuicApi.cs
src/libraries/System.Net.Quic/src/System/Net/Quic/Internal/MsQuicConfiguration.cs
src/libraries/System.Net.Quic/src/System/Net/Quic/QuicConnection.SslConnectionOptions.cs
src/libraries/System.Net.Security/tests/FunctionalTests/TestHelper.cs

index c4b1e00..2a6ceac 100644 (file)
@@ -51,68 +51,96 @@ internal sealed unsafe class MsQuicApi
 
     internal static bool IsQuicSupported { get; }
 
+    internal static bool UsesSChannelBackend { get; }
+
     internal static bool Tls13ServerMayBeDisabled { get; }
     internal static bool Tls13ClientMayBeDisabled { get; }
 
     static MsQuicApi()
     {
-        if (OperatingSystem.IsWindows())
+        IntPtr msQuicHandle;
+        if (!NativeLibrary.TryLoad($"{Interop.Libraries.MsQuic}.{MsQuicVersion.Major}", typeof(MsQuicApi).Assembly, DllImportSearchPath.AssemblyDirectory, out msQuicHandle) &&
+            !NativeLibrary.TryLoad(Interop.Libraries.MsQuic, typeof(MsQuicApi).Assembly, DllImportSearchPath.AssemblyDirectory, out msQuicHandle))
         {
-            if (!IsWindowsVersionSupported())
-            {
-                if (NetEventSource.Log.IsEnabled())
-                {
-                    NetEventSource.Info(null, $"Current Windows version ({Environment.OSVersion}) is not supported by QUIC. Minimal supported version is {MinWindowsVersion}");
-                }
+            return;
+        }
 
+        try
+        {
+            if (!NativeLibrary.TryGetExport(msQuicHandle, "MsQuicOpenVersion", out IntPtr msQuicOpenVersionAddress))
+            {
                 return;
             }
 
-            Tls13ServerMayBeDisabled = IsTls13Disabled(true);
-            Tls13ClientMayBeDisabled = IsTls13Disabled(false);
-        }
+            QUIC_API_TABLE* apiTable = null;
+            delegate* unmanaged[Cdecl]<uint, QUIC_API_TABLE**, int> msQuicOpenVersion = (delegate* unmanaged[Cdecl]<uint, QUIC_API_TABLE**, int>)msQuicOpenVersionAddress;
+            if (StatusFailed(msQuicOpenVersion((uint)MsQuicVersion.Major, &apiTable)))
+            {
+                return;
+            }
 
-        IntPtr msQuicHandle;
-        if (NativeLibrary.TryLoad($"{Interop.Libraries.MsQuic}.{MsQuicVersion.Major}", typeof(MsQuicApi).Assembly, DllImportSearchPath.AssemblyDirectory, out msQuicHandle) ||
-            NativeLibrary.TryLoad(Interop.Libraries.MsQuic, typeof(MsQuicApi).Assembly, DllImportSearchPath.AssemblyDirectory, out msQuicHandle))
-        {
             try
             {
-                if (NativeLibrary.TryGetExport(msQuicHandle, "MsQuicOpenVersion", out IntPtr msQuicOpenVersionAddress))
+                int arraySize = 4;
+                uint* libVersion = stackalloc uint[arraySize];
+                uint size = (uint)arraySize * sizeof(uint);
+                if (StatusFailed(apiTable->GetParam(null, QUIC_PARAM_GLOBAL_LIBRARY_VERSION, &size, libVersion)))
                 {
-                    QUIC_API_TABLE* apiTable;
-                    delegate* unmanaged[Cdecl]<uint, QUIC_API_TABLE**, int> msQuicOpenVersion = (delegate* unmanaged[Cdecl]<uint, QUIC_API_TABLE**, int>)msQuicOpenVersionAddress;
-                    if (StatusSucceeded(msQuicOpenVersion((uint)MsQuicVersion.Major, &apiTable)))
+                    return;
+                }
+
+                var version = new Version((int)libVersion[0], (int)libVersion[1], (int)libVersion[2], (int)libVersion[3]);
+                if (version < MsQuicVersion)
+                {
+                    if (NetEventSource.Log.IsEnabled())
+                    {
+                        NetEventSource.Info(null, $"Incompatible MsQuic library version '{version}', expecting '{MsQuicVersion}'");
+                    }
+                    return;
+                }
+
+                // Assume SChannel is being used on windows and query for the actual provider from the library
+                QUIC_TLS_PROVIDER provider = OperatingSystem.IsWindows() ? QUIC_TLS_PROVIDER.SCHANNEL : QUIC_TLS_PROVIDER.OPENSSL;
+                size = sizeof(QUIC_TLS_PROVIDER);
+                apiTable->GetParam(null, QUIC_PARAM_GLOBAL_TLS_PROVIDER, &size, &provider);
+                UsesSChannelBackend = provider == QUIC_TLS_PROVIDER.SCHANNEL;
+
+                if (UsesSChannelBackend)
+                {
+                    // Implies windows platform, check TLS1.3 availability
+                    if (!IsWindowsVersionSupported())
                     {
-                        int arraySize = 4;
-                        uint* libVersion = stackalloc uint[arraySize];
-                        uint size = (uint)arraySize * sizeof(uint);
-                        if (StatusSucceeded(apiTable->GetParam(null, QUIC_PARAM_GLOBAL_LIBRARY_VERSION, &size, libVersion)))
+                        if (NetEventSource.Log.IsEnabled())
                         {
-                            var version = new Version((int)libVersion[0], (int)libVersion[1], (int)libVersion[2], (int)libVersion[3]);
-                            if (version >= MsQuicVersion)
-                            {
-                                Api = new MsQuicApi(apiTable);
-                                IsQuicSupported = true;
-                            }
-                            else
-                            {
-                                if (NetEventSource.Log.IsEnabled())
-                                {
-                                    NetEventSource.Info(null, $"Incompatible MsQuic library version '{version}', expecting '{MsQuicVersion}'");
-                                }
-                            }
+                            NetEventSource.Info(null, $"Current Windows version ({Environment.OSVersion}) is not supported by QUIC. Minimal supported version is {MinWindowsVersion}");
                         }
+
+                        return;
                     }
+
+                    Tls13ServerMayBeDisabled = IsTls13Disabled(isServer: true);
+                    Tls13ClientMayBeDisabled = IsTls13Disabled(isServer: false);
                 }
+
+                Api = new MsQuicApi(apiTable);
+                IsQuicSupported = true;
             }
             finally
             {
-                if (!IsQuicSupported)
+                if (!IsQuicSupported && NativeLibrary.TryGetExport(msQuicHandle, "MsQuicClose", out IntPtr msQuicClose))
                 {
-                    NativeLibrary.Free(msQuicHandle);
+                    // Gracefully close the API table
+                    ((delegate* unmanaged[Cdecl]<QUIC_API_TABLE*, void>)msQuicClose)(apiTable);
                 }
             }
+
+        }
+        finally
+        {
+            if (!IsQuicSupported)
+            {
+                NativeLibrary.Free(msQuicHandle);
+            }
         }
     }
 
index ea3859e..b84cb6c 100644 (file)
@@ -23,7 +23,7 @@ internal static class MsQuicConfiguration
         flags |= QUIC_CREDENTIAL_FLAGS.CLIENT;
         flags |= QUIC_CREDENTIAL_FLAGS.INDICATE_CERTIFICATE_RECEIVED;
         flags |= QUIC_CREDENTIAL_FLAGS.NO_CERTIFICATE_VALIDATION;
-        if (OperatingSystem.IsWindows())
+        if (MsQuicApi.UsesSChannelBackend)
         {
             flags |= QUIC_CREDENTIAL_FLAGS.USE_SUPPLIED_CREDENTIALS;
         }
@@ -145,7 +145,7 @@ internal static class MsQuicConfiguration
         try
         {
             QUIC_CREDENTIAL_CONFIG config = new QUIC_CREDENTIAL_CONFIG { Flags = flags };
-            config.Flags |= (OperatingSystem.IsWindows() ? QUIC_CREDENTIAL_FLAGS.NONE : QUIC_CREDENTIAL_FLAGS.USE_PORTABLE_CERTIFICATES);
+            config.Flags |= (MsQuicApi.UsesSChannelBackend ? QUIC_CREDENTIAL_FLAGS.NONE : QUIC_CREDENTIAL_FLAGS.USE_PORTABLE_CERTIFICATES);
 
             if (cipherSuitesPolicy != null)
             {
@@ -159,7 +159,7 @@ internal static class MsQuicConfiguration
                 config.Type = QUIC_CREDENTIAL_TYPE.NONE;
                 status = MsQuicApi.Api.ApiTable->ConfigurationLoadCredential(configurationHandle.QuicHandle, &config);
             }
-            else if (OperatingSystem.IsWindows())
+            else if (MsQuicApi.UsesSChannelBackend)
             {
                 config.Type = QUIC_CREDENTIAL_TYPE.CERTIFICATE_CONTEXT;
                 config.CertificateContext = (void*)certificate.Handle;
index 8255938..56aa8f2 100644 (file)
@@ -69,7 +69,7 @@ public partial class QuicConnection
                     chain.ChainPolicy.RevocationFlag = X509RevocationFlag.ExcludeRoot;
                     chain.ChainPolicy.ApplicationPolicy.Add(_isClient ? s_serverAuthOid : s_clientAuthOid);
 
-                    if (OperatingSystem.IsWindows())
+                    if (MsQuicApi.UsesSChannelBackend)
                     {
                         result = new X509Certificate2((IntPtr)certificatePtr);
                     }
index 47e6b40..a8b4fba 100644 (file)
@@ -206,7 +206,7 @@ namespace System.Net.Security.Tests
             if (PlatformDetection.IsWindows)
             {
                 X509Certificate2 ephemeral = endEntity;
-                endEntity = new X509Certificate2(endEntity.Export(X509ContentType.Pfx));
+                endEntity = new X509Certificate2(endEntity.Export(X509ContentType.Pfx), (string?)null, X509KeyStorageFlags.Exportable);
                 ephemeral.Dispose();
             }