Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[QUIC] Support for OpenSSL build of MsQuic on Windows (Attempt 2) #72609

Original file line number Diff line number Diff line change
Expand Up @@ -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]<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 SChanel is being used on windows and query for the actual provider from the library
rzikm marked this conversation as resolved.
Show resolved Hide resolved
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))
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I guess we had the same issue before your change as well, we just never hit the right set of conditions for it to manifest and with your original change it happened on every Windows non-TLS1.3 machine.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, we did not hit it before because the OS version check was before we opened the MsQuic library. I had to move the OS check later once we knew which TLS backend MsQuic is using.

We could have still hit the issue when opening an older MsQuic version on sufficiently new Windows OS

{
NativeLibrary.Free(msQuicHandle);
// Gracefully close the API table
((delegate* unmanaged[Cdecl]<QUIC_API_TABLE*, void>)msQuicClose)(apiTable);
}
}

}
finally
{
if (!IsQuicSupported)
{
NativeLibrary.Free(msQuicHandle);
}
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
Expand Down Expand Up @@ -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)
{
Expand All @@ -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;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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();
}

Expand Down