diff --git a/src/libraries/System.Net.Quic/src/System/Net/Quic/Internal/MsQuicApi.cs b/src/libraries/System.Net.Quic/src/System/Net/Quic/Internal/MsQuicApi.cs index c4b1e00ada347..2a6ceac8cacf1 100644 --- a/src/libraries/System.Net.Quic/src/System/Net/Quic/Internal/MsQuicApi.cs +++ b/src/libraries/System.Net.Quic/src/System/Net/Quic/Internal/MsQuicApi.cs @@ -51,68 +51,96 @@ private MsQuicApi(QUIC_API_TABLE* apiTable) 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] msQuicOpenVersion = (delegate* unmanaged[Cdecl])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] msQuicOpenVersion = (delegate* unmanaged[Cdecl])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])msQuicClose)(apiTable); } } + + } + finally + { + if (!IsQuicSupported) + { + NativeLibrary.Free(msQuicHandle); + } } } diff --git a/src/libraries/System.Net.Quic/src/System/Net/Quic/Internal/MsQuicConfiguration.cs b/src/libraries/System.Net.Quic/src/System/Net/Quic/Internal/MsQuicConfiguration.cs index ea3859ea03c49..b84cb6cce4267 100644 --- a/src/libraries/System.Net.Quic/src/System/Net/Quic/Internal/MsQuicConfiguration.cs +++ b/src/libraries/System.Net.Quic/src/System/Net/Quic/Internal/MsQuicConfiguration.cs @@ -23,7 +23,7 @@ public static MsQuicSafeHandle Create(QuicClientConnectionOptions options) 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 @@ private static unsafe MsQuicSafeHandle Create(QuicConnectionOptions options, QUI 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 @@ private static unsafe MsQuicSafeHandle Create(QuicConnectionOptions options, QUI 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; diff --git a/src/libraries/System.Net.Quic/src/System/Net/Quic/QuicConnection.SslConnectionOptions.cs b/src/libraries/System.Net.Quic/src/System/Net/Quic/QuicConnection.SslConnectionOptions.cs index 8255938b0e326..56aa8f2a9a16a 100644 --- a/src/libraries/System.Net.Quic/src/System/Net/Quic/QuicConnection.SslConnectionOptions.cs +++ b/src/libraries/System.Net.Quic/src/System/Net/Quic/QuicConnection.SslConnectionOptions.cs @@ -69,7 +69,7 @@ public unsafe int ValidateCertificate(QUIC_BUFFER* certificatePtr, QUIC_BUFFER* 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); } diff --git a/src/libraries/System.Net.Security/tests/FunctionalTests/TestHelper.cs b/src/libraries/System.Net.Security/tests/FunctionalTests/TestHelper.cs index 47e6b40c3dc4e..a8b4fba179d6a 100644 --- a/src/libraries/System.Net.Security/tests/FunctionalTests/TestHelper.cs +++ b/src/libraries/System.Net.Security/tests/FunctionalTests/TestHelper.cs @@ -206,7 +206,7 @@ internal static (X509Certificate2 certificate, X509Certificate2Collection) Gener 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(); }