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

Implement part of SslCertificateTrust #55104

Merged
merged 16 commits into from
Jul 12, 2021
Merged
Show file tree
Hide file tree
Changes from 14 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 22 additions & 0 deletions src/libraries/Common/src/Interop/Windows/SspiCli/Interop.SSPI.cs
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@ internal enum ContextAttribute
SECPKG_ATTR_LOCAL_CERT_CONTEXT = 0x54, // returns PCCERT_CONTEXT
SECPKG_ATTR_ROOT_STORE = 0x55, // returns HCERTCONTEXT to the root store
SECPKG_ATTR_ISSUER_LIST_EX = 0x59, // returns SecPkgContext_IssuerListInfoEx
SECPKG_ATTR_CLIENT_CERT_POLICY = 0x60, // sets SecPkgCred_ClientCertCtlPolicy
SECPKG_ATTR_CONNECTION_INFO = 0x5A, // returns SecPkgContext_ConnectionInfo
SECPKG_ATTR_CIPHER_INFO = 0x64, // returns SecPkgContext_CipherInfo
SECPKG_ATTR_UI_INFO = 0x68, // sets SEcPkgContext_UiInfo
Expand Down Expand Up @@ -315,6 +316,20 @@ public SecBufferDesc(int count)
}
}

[StructLayout(LayoutKind.Sequential)]
internal unsafe struct SecPkgCred_ClientCertPolicy
{
public uint dwFlags;
public Guid guidPolicyId;
public uint dwCertFlags;
public uint dwUrlRetrievalTimeout;
public BOOL fCheckRevocationFreshnessTime;
public uint dwRevocationFreshnessTime;
public BOOL fOmitUsageCheck;
public char* pwszSslCtlStoreName;
public char* pwszSslCtlIdentifier;
}

[DllImport(Interop.Libraries.SspiCli, ExactSpelling = true, SetLastError = true)]
internal static extern int EncryptMessage(
ref CredHandle contextHandle,
Expand Down Expand Up @@ -472,5 +487,12 @@ internal static extern SECURITY_STATUS SspiEncodeStringsAsAuthIdentity(
[In] string domainName,
[In] string password,
[Out] out SafeSspiAuthDataHandle authData);

[DllImport(Interop.Libraries.SspiCli, ExactSpelling = true, CharSet = CharSet.Unicode, SetLastError = true)]
internal static extern SECURITY_STATUS SetCredentialsAttributesW(
[In] ref CredHandle handlePtr,
[In] long ulAttribute,
[In] ref SecPkgCred_ClientCertPolicy pBuffer,
[In] long cbBuffer);
}
}
4 changes: 3 additions & 1 deletion src/libraries/System.Net.Http/src/System.Net.Http.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -386,10 +386,12 @@
<Compile Include="System\Net\Http\SocketsHttpHandler\HttpNoProxy.cs" />
<Compile Include="System\Net\Http\SocketsHttpHandler\HttpWindowsProxy.cs" />
<Compile Include="System\Net\Http\SocketsHttpHandler\CurrentUserIdentityProvider.Windows.cs" />
<Compile Include="$(CommonPath)Interop\Windows\Interop.BOOL.cs"
Link="Common\Interop\Windows\Interop.BOOL.cs" />
<Compile Include="$(CommonPath)\Interop\Windows\Interop.Libraries.cs"
Link="Common\Interop\Windows\Interop.Libraries.cs" />
<Compile Include="$(CommonPath)Interop\Windows\Interop.UNICODE_STRING.cs"
Link="Common\Interop\Windows\Interop.UNICODE_STRING.cs" />
Link="Common\Interop\Windows\Interop.UNICODE_STRING.cs" />
<Compile Include="$(CommonPath)Interop\Windows\Crypt32\Interop.CERT_CONTEXT.cs"
Link="Common\Interop\Windows\Crypt32\Interop.CERT_CONTEXT.cs" />
<Compile Include="$(CommonPath)Interop\Windows\Crypt32\Interop.CERT_INFO.cs"
Expand Down
2 changes: 2 additions & 0 deletions src/libraries/System.Net.Mail/src/System.Net.Mail.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -198,6 +198,8 @@
Link="Common\Interop\Windows\Crypt32\Interop.DATA_BLOB.cs" />
<Compile Include="$(CommonPath)Interop\Windows\Crypt32\Interop.MsgEncodingType.cs"
Link="Common\Interop\Windows\Crypt32\Interop.Interop.MsgEncodingType.cs" />
<Compile Include="$(CommonPath)Interop\Windows\Interop.BOOL.cs"
Link="Common\Interop\Windows\Interop.BOOL.cs" />
<Compile Include="$(CommonPath)Interop\Windows\Interop.Libraries.cs"
Link="Common\Interop\Windows\Interop.Libraries.cs" />
<Compile Include="$(CommonPath)Interop\Windows\SspiCli\SecPkgContext_Bindings.cs"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -183,6 +183,8 @@
</ItemGroup>
<!-- Windows specific files -->
<ItemGroup Condition="'$(TargetsWindows)'=='true'">
<Compile Include="$(CommonPath)Interop\Windows\Interop.BOOL.cs"
Link="Common\Interop\Windows\Interop.BOOL.cs" />
<Compile Include="$(CommonPath)Interop\Windows\SChannel\Interop.SecPkgContext_ApplicationProtocol.cs"
Link="Common\Interop\Windows\SChannel\Interop.SecPkgContext_ApplicationProtocol.cs" />
<Compile Include="$(CommonPath)System\Net\Security\SecurityBuffer.Windows.cs"
Expand Down
16 changes: 15 additions & 1 deletion src/libraries/System.Net.Security/ref/System.Net.Security.cs
Original file line number Diff line number Diff line change
Expand Up @@ -130,10 +130,24 @@ public readonly struct SslClientHelloInfo
public static bool operator !=(System.Net.Security.SslApplicationProtocol left, System.Net.Security.SslApplicationProtocol right) { throw null; }
public override string ToString() { throw null; }
}

public sealed partial class SslCertificateTrust
{
public static SslCertificateTrust CreateForX509Store(
System.Security.Cryptography.X509Certificates.X509Store store,
bool sendTrustInHandshake = false) { throw null; }
[System.Runtime.Versioning.UnsupportedOSPlatform("windows")]
public static SslCertificateTrust CreateForX509Collection(
System.Security.Cryptography.X509Certificates.X509Certificate2Collection trustList,
bool sendTrustInHandshake = false) { throw null; }
}

public sealed partial class SslStreamCertificateContext
{
internal SslStreamCertificateContext() { throw null; }
public static SslStreamCertificateContext Create(System.Security.Cryptography.X509Certificates.X509Certificate2 target, System.Security.Cryptography.X509Certificates.X509Certificate2Collection? additionalCertificates, bool offline = false) { throw null; }
[System.ComponentModel.EditorBrowsable(System.ComponentModel.EditorBrowsableState.Never)]
public static SslStreamCertificateContext Create(System.Security.Cryptography.X509Certificates.X509Certificate2 target, System.Security.Cryptography.X509Certificates.X509Certificate2Collection? additionalCertificates, bool offline) { throw null; }
public static SslStreamCertificateContext Create(System.Security.Cryptography.X509Certificates.X509Certificate2 target, System.Security.Cryptography.X509Certificates.X509Certificate2Collection? additionalCertificates, bool offline = false, SslCertificateTrust? trust = null) { throw null; }
}
public partial class SslClientAuthenticationOptions
{
Expand Down
6 changes: 6 additions & 0 deletions src/libraries/System.Net.Security/src/Resources/Strings.resx
Original file line number Diff line number Diff line change
Expand Up @@ -458,4 +458,10 @@
<data name="net_android_ssl_api_level_unsupported" xml:space="preserve">
<value>Setting an SNI hostname is not supported on this API level.</value>
</data>
<data name="net_ssl_trust_store" xml:space="preserve">
<value>Only LocalMachine stores are supported on Windows.</value>
</data>
<data name="net_ssl_trust_collection" xml:space="preserve">
<value>Sending trust from collection is not supported on Windows.</value>
</data>
</root>
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
<TargetFrameworks>$(NetCoreAppCurrent)-windows;$(NetCoreAppCurrent)-Unix;$(NetCoreAppCurrent)-Android;$(NetCoreAppCurrent)-OSX;$(NetCoreAppCurrent)-iOS;$(NetCoreAppCurrent)-tvOS;$(NetCoreAppCurrent)</TargetFrameworks>
<!-- This is needed so that code for TlsCipherSuite will have no namespace (causes compile errors) when used within T4 template -->
<DefineConstants>$(DefineConstants);PRODUCT</DefineConstants>
<DefineConstants Condition="'$(TargetsWindows)' == 'true'">$(DefineConstants);TARGET_WINDOWS</DefineConstants>
wfurt marked this conversation as resolved.
Show resolved Hide resolved
<Nullable>enable</Nullable>
</PropertyGroup>
<PropertyGroup>
Expand All @@ -29,6 +30,7 @@
<Compile Include="System\Net\Security\ProtectionLevel.cs" />
<Compile Include="System\Net\Security\SslApplicationProtocol.cs" />
<Compile Include="System\Net\Security\SslAuthenticationOptions.cs" />
<Compile Include="System\Net\Security\SslCertificateTrust.cs" />
<Compile Include="System\Net\Security\SslClientAuthenticationOptions.cs" />
<Compile Include="System\Net\Security\SslClientHelloInfo.cs" />
<Compile Include="System\Net\Security\SslServerAuthenticationOptions.cs" />
Expand Down Expand Up @@ -156,6 +158,8 @@
<Compile Include="$(CommonPath)System\Net\Security\SecurityContextTokenHandle.cs"
Link="Common\System\Net\Security\SecurityContextTokenHandle.cs" />
<!-- Interop -->
<Compile Include="$(CommonPath)Interop\Windows\Interop.BOOL.cs"
Link="Common\Interop\Windows\Interop.BOOL.cs" />
<Compile Include="$(CommonPath)Interop\Windows\Interop.Libraries.cs"
Link="Common\Interop\Windows\Interop.Libraries.cs" />
<Compile Include="$(CommonPath)Interop\Windows\Interop.UNICODE_STRING.cs"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -922,7 +922,7 @@ internal SecurityStatusPal Decrypt(Span<byte> buffer, out int outputOffset, out
--*/

//This method validates a remote certificate.
internal bool VerifyRemoteCertificate(RemoteCertificateValidationCallback? remoteCertValidationCallback, ref ProtocolToken? alertToken, out SslPolicyErrors sslPolicyErrors, out X509ChainStatusFlags chainStatus)
internal bool VerifyRemoteCertificate(RemoteCertificateValidationCallback? remoteCertValidationCallback, SslCertificateTrust? trust, ref ProtocolToken? alertToken, out SslPolicyErrors sslPolicyErrors, out X509ChainStatusFlags chainStatus)
{
sslPolicyErrors = SslPolicyErrors.None;
chainStatus = X509ChainStatusFlags.NoError;
Expand Down Expand Up @@ -965,6 +965,19 @@ internal bool VerifyRemoteCertificate(RemoteCertificateValidationCallback? remot
chain.ChainPolicy.ExtraStore.AddRange(remoteCertificateStore);
}

if (trust != null)
{
chain.ChainPolicy.TrustMode = X509ChainTrustMode.CustomRootTrust;
if (trust._store != null)
{
chain.ChainPolicy.CustomTrustStore.AddRange(trust._store.Certificates);
}
if (trust._trustList != null)
{
chain.ChainPolicy.CustomTrustStore.AddRange(trust._trustList);
}
}

sslPolicyErrors |= CertificateValidationPal.VerifyCertificateProperties(
_securityContext!,
chain,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using System.Runtime.Versioning;
using System.Security.Cryptography.X509Certificates;

namespace System.Net.Security
{
public sealed class SslCertificateTrust
{
internal X509Store? _store;
internal X509Certificate2Collection? _trustList;
internal bool _sendTrustInHandshake;

public static SslCertificateTrust CreateForX509Store(X509Store store, bool sendTrustInHandshake = false)
{

#if TARGET_WINDOWS
wfurt marked this conversation as resolved.
Show resolved Hide resolved
if (sendTrustInHandshake && store.Location != StoreLocation.LocalMachine)
{
throw new PlatformNotSupportedException(SR.net_ssl_trust_store);
}
#else
if (sendTrustInHandshake)
{
// to be removed when implemented.
throw new PlatformNotSupportedException("Not supported yet.");
}
#endif
if (!store.IsOpen)
{
store.Open(OpenFlags.ReadOnly | OpenFlags.OpenExistingOnly);
}

var trust = new SslCertificateTrust();
wfurt marked this conversation as resolved.
Show resolved Hide resolved
trust._store = store;
trust._sendTrustInHandshake = sendTrustInHandshake;
return trust;
}

[UnsupportedOSPlatform("windows")]
public static SslCertificateTrust CreateForX509Collection(X509Certificate2Collection trustList, bool sendTrustInHandshake = false)
{
if (sendTrustInHandshake)
{
// to be removed when implemented.
throw new PlatformNotSupportedException("Not supported yet.");
}

#if TARGET_WINDOWS
if (sendTrustInHandshake)
{
throw new PlatformNotSupportedException(SR.net_ssl_trust_collection);
}
#endif
var trust = new SslCertificateTrust();
trust._trustList = trustList;
trust._sendTrustInHandshake = sendTrustInHandshake;
return trust;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -664,7 +664,7 @@ private bool CompleteHandshake(ref ProtocolToken? alertToken, out SslPolicyError
return true;
}

if (!_context.VerifyRemoteCertificate(_sslAuthenticationOptions!.CertValidationDelegate, ref alertToken, out sslPolicyErrors, out chainStatus))
if (!_context.VerifyRemoteCertificate(_sslAuthenticationOptions!.CertValidationDelegate, _sslAuthenticationOptions!.CertificateContext?.Trust, ref alertToken, out sslPolicyErrors, out chainStatus))
{
_handshakeCompleted = false;
return false;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,11 @@ public partial class SslStreamCertificateContext
{
private const bool TrimRootCertificate = true;

private SslStreamCertificateContext(X509Certificate2 target, X509Certificate2[] intermediates)
private SslStreamCertificateContext(X509Certificate2 target, X509Certificate2[] intermediates, SslCertificateTrust? trust)
{
Certificate = target;
IntermediateCertificates = intermediates;
Trust = trust;
}

internal static SslStreamCertificateContext Create(X509Certificate2 target) => Create(target, null);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,16 +10,17 @@ public partial class SslStreamCertificateContext
// No leaf, no root.
private const bool TrimRootCertificate = true;

private SslStreamCertificateContext(X509Certificate2 target, X509Certificate2[] intermediates)
private SslStreamCertificateContext(X509Certificate2 target, X509Certificate2[] intermediates, SslCertificateTrust? trust)
{
Certificate = target;
IntermediateCertificates = intermediates;
Trust = trust;
}

internal static SslStreamCertificateContext Create(X509Certificate2 target)
{
// On OSX we do not need to build chain unless we are asked for it.
return new SslStreamCertificateContext(target, Array.Empty<X509Certificate2>());
return new SslStreamCertificateContext(target, Array.Empty<X509Certificate2>(), null);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ internal static SslStreamCertificateContext Create(X509Certificate2 target)
return new SslStreamCertificateContext(target, Array.Empty<X509Certificate2>());
}

private SslStreamCertificateContext(X509Certificate2 target, X509Certificate2[] intermediates)
private SslStreamCertificateContext(X509Certificate2 target, X509Certificate2[] intermediates, SslCertificateTrust? trust = null)
wfurt marked this conversation as resolved.
Show resolved Hide resolved
{
if (intermediates.Length > 0)
{
Expand Down Expand Up @@ -103,6 +103,7 @@ private SslStreamCertificateContext(X509Certificate2 target, X509Certificate2[]

Certificate = target;
IntermediateCertificates = intermediates;
Trust = trust;
}
}
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using System.ComponentModel;
using System.Security.Cryptography.X509Certificates;

namespace System.Net.Security
Expand All @@ -9,8 +10,15 @@ public partial class SslStreamCertificateContext
{
internal readonly X509Certificate2 Certificate;
internal readonly X509Certificate2[] IntermediateCertificates;
internal readonly SslCertificateTrust? Trust;

public static SslStreamCertificateContext Create(X509Certificate2 target, X509Certificate2Collection? additionalCertificates, bool offline = false)
[EditorBrowsable(EditorBrowsableState.Never)]
public static SslStreamCertificateContext Create(X509Certificate2 target, X509Certificate2Collection? additionalCertificates, bool offline)
{
return Create(target, additionalCertificates, offline, null);
}

public static SslStreamCertificateContext Create(X509Certificate2 target, X509Certificate2Collection? additionalCertificates, bool offline = false, SslCertificateTrust? trust = null)
{
if (!target.HasPrivateKey)
{
Expand Down Expand Up @@ -81,13 +89,12 @@ public static SslStreamCertificateContext Create(X509Certificate2 target, X509Ce
}
}

return new SslStreamCertificateContext(target, intermediates);
return new SslStreamCertificateContext(target, intermediates, trust);
}

internal SslStreamCertificateContext Duplicate()
{
return new SslStreamCertificateContext(new X509Certificate2(Certificate), IntermediateCertificates);

return new SslStreamCertificateContext(new X509Certificate2(Certificate), IntermediateCertificates, Trust);
}
}
}
Loading