diff --git a/src/libraries/Common/src/Interop/OSX/Interop.CoreFoundation.cs b/src/libraries/Common/src/Interop/OSX/Interop.CoreFoundation.cs index 6233a4048d236..6f3b1a312460e 100644 --- a/src/libraries/Common/src/Interop/OSX/Interop.CoreFoundation.cs +++ b/src/libraries/Common/src/Interop/OSX/Interop.CoreFoundation.cs @@ -131,10 +131,9 @@ internal static unsafe SafeCreateHandle CFStringCreateFromSpan(ReadOnlySpanShould be IntPtr.Zero /// Returns a pointer to a CFArray on success; otherwise, returns IntPtr.Zero [GeneratedDllImport(Interop.Libraries.CoreFoundationLibrary)] - private static partial SafeCreateHandle CFArrayCreate( + private static unsafe partial SafeCreateHandle CFArrayCreate( IntPtr allocator, - [MarshalAs(UnmanagedType.LPArray)] - IntPtr[] values, + IntPtr* values, UIntPtr numValues, IntPtr callbacks); @@ -144,9 +143,25 @@ private static partial SafeCreateHandle CFArrayCreate( /// The values to put in the array /// The number of values in the array /// Returns a valid SafeCreateHandle to a CFArray on success; otherwise, returns an invalid SafeCreateHandle - internal static SafeCreateHandle CFArrayCreate(IntPtr[] values, UIntPtr numValues) + internal static unsafe SafeCreateHandle CFArrayCreate(IntPtr[] values, UIntPtr numValues) { - return CFArrayCreate(IntPtr.Zero, values, numValues, IntPtr.Zero); + fixed (IntPtr* pValues = values) + { + return CFArrayCreate(IntPtr.Zero, pValues, (UIntPtr)values.Length, IntPtr.Zero); + } + } + + /// + /// Creates a pointer to an unmanaged CFArray containing the input values. Follows the "Create Rule" where if you create it, you delete it. + /// + /// The values to put in the array + /// Returns a valid SafeCreateHandle to a CFArray on success; otherwise, returns an invalid SafeCreateHandle + internal static unsafe SafeCreateHandle CFArrayCreate(Span values) + { + fixed (IntPtr* pValues = &MemoryMarshal.GetReference(values)) + { + return CFArrayCreate(IntPtr.Zero, pValues, (UIntPtr)values.Length, IntPtr.Zero); + } } /// diff --git a/src/libraries/Common/src/Interop/OSX/System.Security.Cryptography.Native.Apple/Interop.Ssl.cs b/src/libraries/Common/src/Interop/OSX/System.Security.Cryptography.Native.Apple/Interop.Ssl.cs index c728f15917e86..c1cdd4b33dbe7 100644 --- a/src/libraries/Common/src/Interop/OSX/System.Security.Cryptography.Native.Apple/Interop.Ssl.cs +++ b/src/libraries/Common/src/Interop/OSX/System.Security.Cryptography.Native.Apple/Interop.Ssl.cs @@ -161,6 +161,22 @@ private static partial int AppleCryptoNative_SslIsHostnameMatch( [GeneratedDllImport(Interop.Libraries.AppleCryptoNative, EntryPoint = "AppleCryptoNative_SslSetEnabledCipherSuites")] internal static unsafe partial int SslSetEnabledCipherSuites(SafeSslHandle sslHandle, uint* cipherSuites, int numCipherSuites); + [GeneratedDllImport(Interop.Libraries.AppleCryptoNative, EntryPoint = "AppleCryptoNative_SslSetCertificateAuthorities")] + internal static partial int SslSetCertificateAuthorities(SafeSslHandle sslHandle, SafeCreateHandle certificateOrArray, int replaceExisting); + + internal static unsafe void SslSetCertificateAuthorities(SafeSslHandle sslHandle, Span certificates, bool replaceExisting) + { + using (SafeCreateHandle cfCertRefs = CoreFoundation.CFArrayCreate(certificates)) + { + int osStatus = SslSetCertificateAuthorities(sslHandle, cfCertRefs, replaceExisting ? 1 : 0); + + if (osStatus != 0) + { + throw CreateExceptionForOSStatus(osStatus); + } + } + } + internal static void SslSetAcceptClientCert(SafeSslHandle sslHandle) { int osStatus = AppleCryptoNative_SslSetAcceptClientCert(sslHandle); diff --git a/src/libraries/Common/src/Interop/Unix/System.Security.Cryptography.Native/Interop.OpenSsl.cs b/src/libraries/Common/src/Interop/Unix/System.Security.Cryptography.Native/Interop.OpenSsl.cs index ed807385c9b57..19a3596377f43 100644 --- a/src/libraries/Common/src/Interop/Unix/System.Security.Cryptography.Native/Interop.OpenSsl.cs +++ b/src/libraries/Common/src/Interop/Unix/System.Security.Cryptography.Native/Interop.OpenSsl.cs @@ -12,6 +12,7 @@ using System.Security.Authentication; using System.Security.Authentication.ExtendedProtection; using System.Security.Cryptography; +using System.Security.Cryptography.X509Certificates; using Microsoft.Win32.SafeHandles; internal static partial class Interop @@ -46,32 +47,32 @@ internal static partial class OpenSsl return bindingHandle; } - private static volatile int s_disableTlsResume = -1; - - private static bool DisableTlsResume - { - get - { - int disableTlsResume = s_disableTlsResume; - if (disableTlsResume != -1) - { - return disableTlsResume != 0; - } - - // First check for the AppContext switch, giving it priority over the environment variable. - if (AppContext.TryGetSwitch(DisableTlsResumeCtxSwitch, out bool value)) - { - s_disableTlsResume = value ? 1 : 0; - } - else - { - // AppContext switch wasn't used. Check the environment variable. + private static volatile int s_disableTlsResume = -1; + + private static bool DisableTlsResume + { + get + { + int disableTlsResume = s_disableTlsResume; + if (disableTlsResume != -1) + { + return disableTlsResume != 0; + } + + // First check for the AppContext switch, giving it priority over the environment variable. + if (AppContext.TryGetSwitch(DisableTlsResumeCtxSwitch, out bool value)) + { + s_disableTlsResume = value ? 1 : 0; + } + else + { + // AppContext switch wasn't used. Check the environment variable. s_disableTlsResume = Environment.GetEnvironmentVariable(DisableTlsResumeEnvironmentVariable) is string envVar && (envVar == "1" || envVar.Equals("true", StringComparison.OrdinalIgnoreCase)) ? 1 : 0; - } + } - return s_disableTlsResume != 0; + return s_disableTlsResume != 0; } } @@ -147,7 +148,7 @@ internal static SafeSslContextHandle AllocateSslContext(SafeFreeSslCredentials c // Sets policy and security level if (!Ssl.SetEncryptionPolicy(sslCtx, sslAuthenticationOptions.EncryptionPolicy)) { - throw new SslException( SR.Format(SR.net_ssl_encryptionpolicy_notsupported, sslAuthenticationOptions.EncryptionPolicy)); + throw new SslException(SR.Format(SR.net_ssl_encryptionpolicy_notsupported, sslAuthenticationOptions.EncryptionPolicy)); } } @@ -274,7 +275,7 @@ internal static SafeSslHandle AllocateSslHandle(SafeFreeSslCredentials credentia if (cacheSslContext) { - sslAuthenticationOptions.CertificateContext!.SslContexts!.TryGetValue(protocols | (SslProtocols)(hasAlpn ? 1 : 0), out sslCtxHandle); + sslAuthenticationOptions.CertificateContext!.SslContexts!.TryGetValue(protocols | (SslProtocols)(hasAlpn ? 1 : 0), out sslCtxHandle); } if (sslCtxHandle == null) @@ -339,10 +340,36 @@ internal static SafeSslHandle AllocateSslHandle(SafeFreeSslCredentials credentia // if server actually requests a certificate. Ssl.SslSetClientCertCallback(sslHandle, 1); } - - if (sslAuthenticationOptions.IsServer && sslAuthenticationOptions.RemoteCertRequired) + else // sslAuthenticationOptions.IsServer { - Ssl.SslSetVerifyPeer(sslHandle); + if (sslAuthenticationOptions.RemoteCertRequired) + { + Ssl.SslSetVerifyPeer(sslHandle); + } + + if (sslAuthenticationOptions.CertificateContext?.Trust?._sendTrustInHandshake == true) + { + SslCertificateTrust trust = sslAuthenticationOptions.CertificateContext!.Trust!; + X509Certificate2Collection certList = (trust._trustList ?? trust._store!.Certificates); + + Debug.Assert(certList != null, "certList != null"); + Span handles = certList.Count <= 256 + ? stackalloc IntPtr[256] + : new IntPtr[certList.Count]; + + for (int i = 0; i < certList.Count; i++) + { + handles[i] = certList[i].Handle; + } + + if (!Ssl.SslAddClientCAs(sslHandle, handles.Slice(0, certList.Count))) + { + // The method can fail only when the number of cert names exceeds the maximum capacity + // supported by STACK_OF(X509_NAME) structure, which should not happen under normal + // operation. + Debug.Fail("Failed to add issuer to trusted CA list."); + } + } } } catch @@ -576,7 +603,7 @@ private static unsafe int AlpnServerSelectCallback(IntPtr ssl, byte** outp, byte { *outp = null; *outlen = 0; - IntPtr sslData = Ssl.SslGetData(ssl); + IntPtr sslData = Ssl.SslGetData(ssl); // reset application data to avoid dangling pointer. Ssl.SslSetData(ssl, IntPtr.Zero); diff --git a/src/libraries/Common/src/Interop/Unix/System.Security.Cryptography.Native/Interop.Ssl.cs b/src/libraries/Common/src/Interop/Unix/System.Security.Cryptography.Native/Interop.Ssl.cs index 8755c4a46cc1c..3e63f7238978e 100644 --- a/src/libraries/Common/src/Interop/Unix/System.Security.Cryptography.Native/Interop.Ssl.cs +++ b/src/libraries/Common/src/Interop/Unix/System.Security.Cryptography.Native/Interop.Ssl.cs @@ -221,6 +221,17 @@ internal static unsafe int SslSetAlpnProtos(SafeSslHandle ssl, Span serial [GeneratedDllImport(Libraries.CryptoNative, EntryPoint = "CryptoNative_SslAddExtraChainCert")] internal static partial bool SslAddExtraChainCert(SafeSslHandle ssl, SafeX509Handle x509); + [GeneratedDllImport(Libraries.CryptoNative, EntryPoint = "CryptoNative_SslAddClientCAs")] + private static unsafe partial bool SslAddClientCAs(SafeSslHandle ssl, IntPtr* x509s, int count); + + internal static unsafe bool SslAddClientCAs(SafeSslHandle ssl, Span x509handles) + { + fixed (IntPtr* pHandles = &MemoryMarshal.GetReference(x509handles)) + { + return SslAddClientCAs(ssl, pHandles, x509handles.Length); + } + } + internal static bool AddExtraChainCertificates(SafeSslHandle ssl, X509Certificate2[] chain) { // send pre-computed list of intermediates. diff --git a/src/libraries/System.Net.Security/src/Resources/Strings.resx b/src/libraries/System.Net.Security/src/Resources/Strings.resx index dd3b86e0b682b..0aef32953c3b0 100644 --- a/src/libraries/System.Net.Security/src/Resources/Strings.resx +++ b/src/libraries/System.Net.Security/src/Resources/Strings.resx @@ -386,4 +386,7 @@ Sending trust from collection is not supported on Windows. + + Sending trust in handshake is not supported on this platform. + diff --git a/src/libraries/System.Net.Security/src/System/Net/Security/Pal.OSX/SafeDeleteSslContext.cs b/src/libraries/System.Net.Security/src/System/Net/Security/Pal.OSX/SafeDeleteSslContext.cs index 9e1a445d9dc75..660a55087d243 100644 --- a/src/libraries/System.Net.Security/src/System/Net/Security/Pal.OSX/SafeDeleteSslContext.cs +++ b/src/libraries/System.Net.Security/src/System/Net/Security/Pal.OSX/SafeDeleteSslContext.cs @@ -92,6 +92,44 @@ public SafeDeleteSslContext(SafeFreeSslCredentials credential, SslAuthentication Dispose(); throw; } + + if (!string.IsNullOrEmpty(sslAuthenticationOptions.TargetHost) && !sslAuthenticationOptions.IsServer) + { + Interop.AppleCrypto.SslSetTargetName(_sslContext, sslAuthenticationOptions.TargetHost); + } + + if (sslAuthenticationOptions.CertificateContext == null && sslAuthenticationOptions.CertSelectionDelegate != null) + { + // certificate was not provided but there is user callback. We can break handshake if server asks for certificate + // and we can try to get it based on remote certificate and trusted issuers. + Interop.AppleCrypto.SslBreakOnCertRequested(_sslContext, true); + } + + if (sslAuthenticationOptions.IsServer) + { + if (sslAuthenticationOptions.RemoteCertRequired) + { + Interop.AppleCrypto.SslSetAcceptClientCert(_sslContext); + } + + if (sslAuthenticationOptions.CertificateContext?.Trust?._sendTrustInHandshake == true) + { + SslCertificateTrust trust = sslAuthenticationOptions.CertificateContext!.Trust!; + X509Certificate2Collection certList = (trust._trustList ?? trust._store!.Certificates); + + Debug.Assert(certList != null, "certList != null"); + Span handles = certList.Count <= 256 + ? stackalloc IntPtr[256] + : new IntPtr[certList.Count]; + + for (int i = 0; i < certList.Count; i++) + { + handles[i] = certList[i].Handle; + } + + Interop.AppleCrypto.SslSetCertificateAuthorities(_sslContext, handles.Slice(0, certList.Count), true); + } + } } private static SafeSslHandle CreateSslContext(SafeFreeSslCredentials credential, bool isServer) diff --git a/src/libraries/System.Net.Security/src/System/Net/Security/SslCertificateTrust.cs b/src/libraries/System.Net.Security/src/System/Net/Security/SslCertificateTrust.cs index 982d206c85935..98fd52fad3c5b 100644 --- a/src/libraries/System.Net.Security/src/System/Net/Security/SslCertificateTrust.cs +++ b/src/libraries/System.Net.Security/src/System/Net/Security/SslCertificateTrust.cs @@ -21,10 +21,10 @@ public static SslCertificateTrust CreateForX509Store(X509Store store, bool sendT throw new PlatformNotSupportedException(SR.net_ssl_trust_store); } #else - if (sendTrustInHandshake) + if (sendTrustInHandshake && !System.OperatingSystem.IsLinux() && !System.OperatingSystem.IsMacOS()) { // to be removed when implemented. - throw new PlatformNotSupportedException("Not supported yet."); + throw new PlatformNotSupportedException(SR.net_ssl_trust_handshake); } #endif if (!store.IsOpen) @@ -41,10 +41,10 @@ public static SslCertificateTrust CreateForX509Store(X509Store store, bool sendT [UnsupportedOSPlatform("windows")] public static SslCertificateTrust CreateForX509Collection(X509Certificate2Collection trustList, bool sendTrustInHandshake = false) { - if (sendTrustInHandshake) + if (sendTrustInHandshake && !System.OperatingSystem.IsLinux() && !System.OperatingSystem.IsMacOS()) { // to be removed when implemented. - throw new PlatformNotSupportedException("Not supported yet."); + throw new PlatformNotSupportedException(SR.net_ssl_trust_handshake); } #if TARGET_WINDOWS diff --git a/src/libraries/System.Net.Security/src/System/Net/Security/SslStream.Implementation.cs b/src/libraries/System.Net.Security/src/System/Net/Security/SslStream.Implementation.cs index 9dc0538cfca29..67925bd9c6373 100644 --- a/src/libraries/System.Net.Security/src/System/Net/Security/SslStream.Implementation.cs +++ b/src/libraries/System.Net.Security/src/System/Net/Security/SslStream.Implementation.cs @@ -747,6 +747,11 @@ private async ValueTask EnsureFullTlsFrameAsync(CancellationTok return frameSize; } + if (frameSize < int.MaxValue) + { + _buffer.EnsureAvailableSpace(frameSize - _buffer.EncryptedLength); + } + while (_buffer.EncryptedLength < frameSize) { // there should be space left to read into diff --git a/src/libraries/System.Net.Security/src/System/Net/Security/SslStreamPal.OSX.cs b/src/libraries/System.Net.Security/src/System/Net/Security/SslStreamPal.OSX.cs index 6b1b13ca9f2a7..7fbea273f95ef 100644 --- a/src/libraries/System.Net.Security/src/System/Net/Security/SslStreamPal.OSX.cs +++ b/src/libraries/System.Net.Security/src/System/Net/Security/SslStreamPal.OSX.cs @@ -248,23 +248,6 @@ private static SecurityStatusPal HandshakeInternal( { sslContext = new SafeDeleteSslContext((credential as SafeFreeSslCredentials)!, sslAuthenticationOptions); context = sslContext; - - if (!string.IsNullOrEmpty(sslAuthenticationOptions.TargetHost) && !sslAuthenticationOptions.IsServer) - { - Interop.AppleCrypto.SslSetTargetName(sslContext.SslContext, sslAuthenticationOptions.TargetHost); - } - - if (sslAuthenticationOptions.CertificateContext == null && sslAuthenticationOptions.CertSelectionDelegate != null) - { - // certificate was not provided but there is user callback. We can break handshake if server asks for certificate - // and we can try to get it based on remote certificate and trusted issuers. - Interop.AppleCrypto.SslBreakOnCertRequested(sslContext.SslContext, true); - } - - if (sslAuthenticationOptions.IsServer && sslAuthenticationOptions.RemoteCertRequired) - { - Interop.AppleCrypto.SslSetAcceptClientCert(sslContext.SslContext); - } } if (inputBuffer.Length > 0) diff --git a/src/libraries/System.Net.Security/tests/FunctionalTests/SslStreamCertificateTrustTests.cs b/src/libraries/System.Net.Security/tests/FunctionalTests/SslStreamCertificateTrustTests.cs new file mode 100644 index 0000000000000..fe3a977d479b6 --- /dev/null +++ b/src/libraries/System.Net.Security/tests/FunctionalTests/SslStreamCertificateTrustTests.cs @@ -0,0 +1,93 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.IO; +using System.IO.Tests; +using System.Security.Authentication; +using System.Security.Cryptography.X509Certificates; +using System.Threading.Tasks; +using System.Linq; +using Xunit; + +namespace System.Net.Security.Tests +{ + using Configuration = System.Net.Test.Common.Configuration; + + public class SslStreamCertificateTrustTest + { + [Fact] + // not supported on Windows, not implemented elsewhere + [PlatformSpecific(TestPlatforms.Linux | TestPlatforms.OSX)] + public async Task SslStream_SendCertificateTrust_CertificateCollection() + { + (X509Certificate2 certificate, X509Certificate2Collection caCerts) = TestHelper.GenerateCertificates(nameof(SslStream_SendCertificateTrust_CertificateCollection)); + + SslCertificateTrust trust = SslCertificateTrust.CreateForX509Collection(caCerts, sendTrustInHandshake: true); + string[] acceptableIssuers = await ConnectAndGatherAcceptableIssuers(trust); + + Assert.Equal(caCerts.Count, acceptableIssuers.Length); + Assert.Equal(caCerts.Select(c => c.Subject), acceptableIssuers); + } + + [Fact] + [ActiveIssue("https://github.com/dotnet/runtime/issues/65515", TestPlatforms.Windows)] + [PlatformSpecific(TestPlatforms.Windows | TestPlatforms.Linux | TestPlatforms.OSX)] + public async Task SslStream_SendCertificateTrust_CertificateStore() + { + using X509Store store = new X509Store("Root", StoreLocation.LocalMachine); + + SslCertificateTrust trust = SslCertificateTrust.CreateForX509Store(store, sendTrustInHandshake: true); + string[] acceptableIssuers = await ConnectAndGatherAcceptableIssuers(trust); + + // don't assert individual ellements, just that some issuers were sent + // we use Root cert store which should always contain at least some certs + Assert.NotEmpty(acceptableIssuers); + } + + private async Task ConnectAndGatherAcceptableIssuers(SslCertificateTrust trust) + { + (SslStream client, SslStream server) = TestHelper.GetConnectedSslStreams(); + using (client) + using (server) + using (X509Certificate2 serverCertificate = Configuration.Certificates.GetServerCertificate()) + using (X509Certificate2 clientCertificate = Configuration.Certificates.GetClientCertificate()) + { + SslServerAuthenticationOptions serverOptions = new SslServerAuthenticationOptions + { + ServerCertificate = serverCertificate, + ClientCertificateRequired = true, + RemoteCertificateValidationCallback = (sender, certificate, chain, sslPolicyErrors) => true, + ServerCertificateContext = SslStreamCertificateContext.Create(serverCertificate, null, false, trust) + }; + + string[] acceptableIssuers = Array.Empty(); + SslClientAuthenticationOptions clientOptions = new SslClientAuthenticationOptions + { + TargetHost = "localhost", + // Force Tls 1.2 to avoid issues with certain OpenSSL versions and Tls 1.3 + // https://github.com/openssl/openssl/issues/7384 + EnabledSslProtocols = SslProtocols.Tls12, + RemoteCertificateValidationCallback = (sender, certificate, chain, sslPolicyErrors) => true, + LocalCertificateSelectionCallback = (sender, targetHost, localCertificates, remoteCertificate, issuers) => + { + if (remoteCertificate == null) + { + // ignore the first call that is called before handshake + return null; + } + + acceptableIssuers = issuers; + return clientCertificate; + }, + + }; + + await TestConfiguration.WhenAllOrAnyFailedWithTimeout( + client.AuthenticateAsClientAsync(clientOptions), + server.AuthenticateAsServerAsync(serverOptions)); + + return acceptableIssuers; + } + } + } +} \ No newline at end of file diff --git a/src/libraries/System.Net.Security/tests/FunctionalTests/System.Net.Security.Tests.csproj b/src/libraries/System.Net.Security/tests/FunctionalTests/System.Net.Security.Tests.csproj index 8f2d6f40e17ef..6fe027ca2d280 100644 --- a/src/libraries/System.Net.Security/tests/FunctionalTests/System.Net.Security.Tests.csproj +++ b/src/libraries/System.Net.Security/tests/FunctionalTests/System.Net.Security.Tests.csproj @@ -100,6 +100,7 @@ + diff --git a/src/native/libs/System.Security.Cryptography.Native.Apple/entrypoints.c b/src/native/libs/System.Security.Cryptography.Native.Apple/entrypoints.c index 875ce80316562..5bd8d16efc7fc 100644 --- a/src/native/libs/System.Security.Cryptography.Native.Apple/entrypoints.c +++ b/src/native/libs/System.Security.Cryptography.Native.Apple/entrypoints.c @@ -7,7 +7,9 @@ #include "pal_digest.h" #include "pal_ecc.h" #include "pal_hmac.h" +#include "pal_keyagree.h" #include "pal_keychain_macos.h" +#include "pal_keyderivation_macos.h" #include "pal_random.h" #include "pal_rsa.h" #include "pal_sec.h" @@ -20,8 +22,6 @@ #include "pal_x509.h" #include "pal_x509_macos.h" #include "pal_x509chain.h" -#include "pal_keyderivation_macos.h" -#include "pal_keyagree.h" static const Entry s_cryptoAppleNative[] = { @@ -84,6 +84,7 @@ static const Entry s_cryptoAppleNative[] = DllImportEntry(AppleCryptoNative_SslSetMinProtocolVersion) DllImportEntry(AppleCryptoNative_SslSetMaxProtocolVersion) DllImportEntry(AppleCryptoNative_SslSetCertificate) + DllImportEntry(AppleCryptoNative_SslSetCertificateAuthorities) DllImportEntry(AppleCryptoNative_SslSetTargetName) DllImportEntry(AppleCryptoNative_SSLSetALPNProtocols) DllImportEntry(AppleCryptoNative_SslGetAlpnSelected) diff --git a/src/native/libs/System.Security.Cryptography.Native.Apple/pal_ssl.c b/src/native/libs/System.Security.Cryptography.Native.Apple/pal_ssl.c index 7a8a0eebe1317..f0adc55eecab2 100644 --- a/src/native/libs/System.Security.Cryptography.Native.Apple/pal_ssl.c +++ b/src/native/libs/System.Security.Cryptography.Native.Apple/pal_ssl.c @@ -641,8 +641,28 @@ int32_t AppleCryptoNative_SslSetEnabledCipherSuites(SSLContextRef sslContext, co } } +// This API is present on macOS 10.5 and newer only +static OSStatus (*SSLSetCertificateAuthoritiesPtr)(SSLContextRef context, CFArrayRef certificates, int32_t replaceExisting) = NULL; + +PALEXPORT int32_t AppleCryptoNative_SslSetCertificateAuthorities(SSLContextRef sslContext, CFArrayRef certificates, int32_t replaceExisting) +{ +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated-declarations" + // The underlying call handles NULL inputs, so just pass it through + + if (!SSLSetCertificateAuthoritiesPtr) + { + // not available. + return 0; + } + + return SSLSetCertificateAuthoritiesPtr(sslContext, certificates, replaceExisting); +#pragma clang diagnostic pop +} + __attribute__((constructor)) static void InitializeAppleCryptoSslShim() { + SSLSetCertificateAuthoritiesPtr = (OSStatus(*)(SSLContextRef, CFArrayRef, int32_t))dlsym(RTLD_DEFAULT, "SSLSetCertificateAuthorities"); SSLSetALPNProtocolsPtr = (OSStatus(*)(SSLContextRef, CFArrayRef))dlsym(RTLD_DEFAULT, "SSLSetALPNProtocols"); SSLCopyALPNProtocolsPtr = (OSStatus(*)(SSLContextRef, CFArrayRef*))dlsym(RTLD_DEFAULT, "SSLCopyALPNProtocols"); -} +} \ No newline at end of file diff --git a/src/native/libs/System.Security.Cryptography.Native.Apple/pal_ssl.h b/src/native/libs/System.Security.Cryptography.Native.Apple/pal_ssl.h index 2c5961a72ac09..312dacb9e7c21 100644 --- a/src/native/libs/System.Security.Cryptography.Native.Apple/pal_ssl.h +++ b/src/native/libs/System.Security.Cryptography.Native.Apple/pal_ssl.h @@ -249,3 +249,10 @@ Sets enabled cipher suites for the current session. Returns the output of SSLSetEnabledCiphers. */ PALEXPORT int32_t AppleCryptoNative_SslSetEnabledCipherSuites(SSLContextRef sslContext, const uint32_t* cipherSuites, int32_t numCipherSuites); + +/* +Adds one or more certificates to a server's list of certification authorities (CAs) acceptable for client authentication. + +Returns the output of SSLSetCertificateAuthorities. +*/ +PALEXPORT int32_t AppleCryptoNative_SslSetCertificateAuthorities(SSLContextRef sslContext, CFArrayRef certificates, int32_t replaceExisting); diff --git a/src/native/libs/System.Security.Cryptography.Native/entrypoints.c b/src/native/libs/System.Security.Cryptography.Native/entrypoints.c index 0518fa7987b74..1951011467a0b 100644 --- a/src/native/libs/System.Security.Cryptography.Native/entrypoints.c +++ b/src/native/libs/System.Security.Cryptography.Native/entrypoints.c @@ -299,6 +299,7 @@ static const Entry s_cryptoNative[] = DllImportEntry(CryptoNative_SslCtxUseCertificate) DllImportEntry(CryptoNative_SslCtxUsePrivateKey) DllImportEntry(CryptoNative_SslAddExtraChainCert) + DllImportEntry(CryptoNative_SslAddClientCAs) DllImportEntry(CryptoNative_SslDestroy) DllImportEntry(CryptoNative_SslDoHandshake) DllImportEntry(CryptoNative_SslGetClientCAList) diff --git a/src/native/libs/System.Security.Cryptography.Native/opensslshim.h b/src/native/libs/System.Security.Cryptography.Native/opensslshim.h index f1b39f2f5020d..6f60a3c78900e 100644 --- a/src/native/libs/System.Security.Cryptography.Native/opensslshim.h +++ b/src/native/libs/System.Security.Cryptography.Native/opensslshim.h @@ -460,6 +460,7 @@ const EVP_CIPHER* EVP_chacha20_poly1305(void); LIGHTUP_FUNCTION(SSL_CIPHER_get_name) \ LIGHTUP_FUNCTION(SSL_CIPHER_get_version) \ REQUIRED_FUNCTION(SSL_ctrl) \ + REQUIRED_FUNCTION(SSL_add_client_CA) \ REQUIRED_FUNCTION(SSL_set_alpn_protos) \ REQUIRED_FUNCTION(SSL_set_quiet_shutdown) \ REQUIRED_FUNCTION(SSL_CTX_check_private_key) \ @@ -923,6 +924,7 @@ FOR_ALL_OPENSSL_FUNCTIONS #define SSL_CIPHER_get_name SSL_CIPHER_get_name_ptr #define SSL_CIPHER_get_version SSL_CIPHER_get_version_ptr #define SSL_ctrl SSL_ctrl_ptr +#define SSL_add_client_CA SSL_add_client_CA_ptr #define SSL_set_alpn_protos SSL_set_alpn_protos_ptr #define SSL_set_quiet_shutdown SSL_set_quiet_shutdown_ptr #define SSL_CTX_check_private_key SSL_CTX_check_private_key_ptr diff --git a/src/native/libs/System.Security.Cryptography.Native/pal_ssl.c b/src/native/libs/System.Security.Cryptography.Native/pal_ssl.c index 5ef0e681182d3..ead2acea2bb6a 100644 --- a/src/native/libs/System.Security.Cryptography.Native/pal_ssl.c +++ b/src/native/libs/System.Security.Cryptography.Native/pal_ssl.c @@ -772,6 +772,25 @@ int32_t CryptoNative_SslAddExtraChainCert(SSL* ssl, X509* x509) return 0; } +int32_t CryptoNative_SslAddClientCAs(SSL* ssl, X509** x509s, uint32_t count) +{ + if (!x509s || !ssl) + { + return 0; + } + + for (uint32_t i = 0; i < count; i++) + { + int res = SSL_add_client_CA(ssl, x509s[i]); + if (res != 1) + { + return res; + } + } + + return 1; +} + void CryptoNative_SslCtxSetAlpnSelectCb(SSL_CTX* ctx, SslCtxSetAlpnCallback cb, void* arg) { // void shim functions don't lead to exceptions, so skip the unconditional error clearing. diff --git a/src/native/libs/System.Security.Cryptography.Native/pal_ssl.h b/src/native/libs/System.Security.Cryptography.Native/pal_ssl.h index 5bb758eeff16e..522c170e7cf07 100644 --- a/src/native/libs/System.Security.Cryptography.Native/pal_ssl.h +++ b/src/native/libs/System.Security.Cryptography.Native/pal_ssl.h @@ -405,6 +405,15 @@ Returns 1 if success and 0 in case of failure */ PALEXPORT int32_t CryptoNative_SslAddExtraChainCert(SSL* ssl, X509* x509); +/* +Adds the names of the given certificates to the list of acceptable issuers sent to +client when requesting a client certificate. Shims the SSL_add_client_CA function. + +No transfer of ownership or refcount changes. +Returns 1 if success and 0 in case of failure +*/ +PALEXPORT int32_t CryptoNative_SslAddClientCAs(SSL* ssl, X509** x509s, uint32_t count); + /* Shims the ssl_ctx_set_alpn_select_cb method. */