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

ignore name mismatch when IgnoreInvalidName is set #73745

Merged
merged 8 commits into from
Aug 13, 2022
Merged
Show file tree
Hide file tree
Changes from all 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
Original file line number Diff line number Diff line change
@@ -0,0 +1,176 @@
// 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.CompilerServices;
using System.Runtime.InteropServices;
using System.Security.Cryptography;
using System.Security.Cryptography.X509Certificates;
using System.Security.Cryptography.X509Certificates.Tests.Common;
using Test.Cryptography;

namespace System.Net.Test.Common
{
public static partial class Configuration
{
public static partial class Certificates
{
private static readonly X509KeyUsageExtension s_eeKeyUsage =
new X509KeyUsageExtension(
X509KeyUsageFlags.DigitalSignature | X509KeyUsageFlags.KeyEncipherment | X509KeyUsageFlags.DataEncipherment,
critical: false);

private static readonly X509EnhancedKeyUsageExtension s_tlsServerEku =
new X509EnhancedKeyUsageExtension(
new OidCollection
{
new Oid("1.3.6.1.5.5.7.3.1", null)
},
false);

private static readonly X509EnhancedKeyUsageExtension s_tlsClientEku =
new X509EnhancedKeyUsageExtension(
new OidCollection
{
new Oid("1.3.6.1.5.5.7.3.2", null)
},
false);

private static readonly X509BasicConstraintsExtension s_eeConstraints =
new X509BasicConstraintsExtension(false, false, 0, false);

private static X509Certificate2 s_dynamicServerCertificate;
private static X509Certificate2Collection s_dynamicCaCertificates;
private static object certLock = new object();


// These Get* methods make a copy of the certificates so that consumers own the lifetime of the
// certificates handed back. Consumers are expected to dispose of their certs when done with them.

public static X509Certificate2 GetDynamicServerCerttificate(X509Certificate2Collection? chainCertificates)
{
lock (certLock)
{
if (s_dynamicServerCertificate == null)
{
CleanupCertificates();
(s_dynamicServerCertificate, s_dynamicCaCertificates) = GenerateCertificates("localhost", nameof(Configuration) + nameof(Certificates));
}

chainCertificates?.AddRange(s_dynamicCaCertificates);
return new X509Certificate2(s_dynamicServerCertificate);
}
}

public static void CleanupCertificates([CallerMemberName] string? testName = null, StoreName storeName = StoreName.CertificateAuthority)
{
string caName = $"O={testName}";
try
{
using (X509Store store = new X509Store(storeName, StoreLocation.LocalMachine))
{
store.Open(OpenFlags.ReadWrite);
foreach (X509Certificate2 cert in store.Certificates)
{
if (cert.Subject.Contains(caName))
{
store.Remove(cert);
}
cert.Dispose();
}
}
}
catch { };

try
{
using (X509Store store = new X509Store(storeName, StoreLocation.CurrentUser))
{
store.Open(OpenFlags.ReadWrite);
foreach (X509Certificate2 cert in store.Certificates)
{
if (cert.Subject.Contains(caName))
{
store.Remove(cert);
}
cert.Dispose();
}
}
}
catch { };
}

private static X509ExtensionCollection BuildTlsServerCertExtensions(string serverName)
{
return BuildTlsCertExtensions(serverName, true);
}

private static X509ExtensionCollection BuildTlsCertExtensions(string targetName, bool serverCertificate)
{
X509ExtensionCollection extensions = new X509ExtensionCollection();

SubjectAlternativeNameBuilder builder = new SubjectAlternativeNameBuilder();
builder.AddDnsName(targetName);
builder.AddIpAddress(IPAddress.Loopback);
builder.AddIpAddress(IPAddress.IPv6Loopback);
extensions.Add(builder.Build());
extensions.Add(s_eeConstraints);
extensions.Add(s_eeKeyUsage);
extensions.Add(serverCertificate ? s_tlsServerEku : s_tlsClientEku);

return extensions;
}

public static (X509Certificate2 certificate, X509Certificate2Collection) GenerateCertificates(string targetName, [CallerMemberName] string? testName = null, bool longChain = false, bool serverCertificate = true)
{
const int keySize = 2048;
if (PlatformDetection.IsWindows && testName != null)
{
CleanupCertificates(testName);
}

X509Certificate2Collection chain = new X509Certificate2Collection();
X509ExtensionCollection extensions = BuildTlsCertExtensions(targetName, serverCertificate);

CertificateAuthority.BuildPrivatePki(
PkiOptions.IssuerRevocationViaCrl,
out RevocationResponder responder,
out CertificateAuthority root,
out CertificateAuthority[] intermediates,
out X509Certificate2 endEntity,
intermediateAuthorityCount: longChain ? 3 : 1,
subjectName: targetName,
testName: testName,
keySize: keySize,
extensions: extensions);

// Walk the intermediates backwards so we build the chain collection as
// Issuer3
// Issuer2
// Issuer1
// Root
for (int i = intermediates.Length - 1; i >= 0; i--)
{
CertificateAuthority authority = intermediates[i];

chain.Add(authority.CloneIssuerCert());
authority.Dispose();
}

chain.Add(root.CloneIssuerCert());

responder.Dispose();
root.Dispose();

if (PlatformDetection.IsWindows)
{
X509Certificate2 ephemeral = endEntity;
endEntity = new X509Certificate2(endEntity.Export(X509ContentType.Pfx));
ephemeral.Dispose();
}

return (endEntity, chain);
}

}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ public static async Task<Http2LoopbackConnection> CreateAsync(SocketWrapper sock
{
var sslStream = new SslStream(stream, false, delegate { return true; });

using (X509Certificate2 cert = Configuration.Certificates.GetServerCertificate())
using (X509Certificate2 cert = httpOptions.Certificate ?? Configuration.Certificates.GetServerCertificate())
{
#if !NETFRAMEWORK
SslServerAuthenticationOptions options = new SslServerAuthenticationOptions();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -221,6 +221,7 @@ private static Http2Options CreateOptions(GenericLoopbackOptions options)
{
http2Options.Address = options.Address;
http2Options.UseSsl = options.UseSsl;
http2Options.Certificate = options.Certificate;
http2Options.SslProtocols = options.SslProtocols;
http2Options.ListenBacklog = options.ListenBacklog;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ public Http3LoopbackServer(Http3Options options = null)
{
options ??= new Http3Options();

_cert = Configuration.Certificates.GetServerCertificate();
_cert = options.Certificate ?? Configuration.Certificates.GetServerCertificate();

var listenerOptions = new QuicListenerOptions()
{
Expand Down Expand Up @@ -130,6 +130,7 @@ private static Http3Options CreateOptions(GenericLoopbackOptions options)
{
http3Options.Address = options.Address;
http3Options.UseSsl = options.UseSsl;
http3Options.Certificate = options.Certificate;
http3Options.SslProtocols = options.SslProtocols;
http3Options.ListenBacklog = options.ListenBacklog;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -202,6 +202,7 @@ private static HttpAgnosticOptions CreateOptions(GenericLoopbackOptions options)
if (options != null)
{
httpOptions.Address = options.Address;
httpOptions.Certificate = options.Certificate;
httpOptions.UseSsl = options.UseSsl;
httpOptions.SslProtocols = options.SslProtocols;
httpOptions.ListenBacklog = options.ListenBacklog;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1121,6 +1121,7 @@ private static LoopbackServer.Options CreateOptions(GenericLoopbackOptions optio
{
newOptions.Address = options.Address;
newOptions.UseSsl = options.UseSsl;
newOptions.Certificate = options.Certificate;
newOptions.SslProtocols = options.SslProtocols;
newOptions.ListenBacklog = options.ListenBacklog;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@
<ItemGroup>
<Compile Include="$(CommonTestPath)System\Net\Capability.Security.cs" Link="Common\System\Net\Capability.Security.cs" />
<Compile Include="$(CommonTestPath)System\Net\Configuration.cs" Link="Common\System\Net\Configuration.cs" />
<Compile Include="$(CommonTestPath)System\Net\Configuration.Certificates.cs" Link="Common\System\Net\Configuration.Certificates.cs" />
<Compile Include="$(CommonTestPath)System\Net\Configuration.Http.cs" Link="Common\System\Net\Configuration.Http.cs" />
<Compile Include="$(CommonTestPath)System\Net\Configuration.Security.cs" Link="Common\System\Net\Configuration.Security.cs" />
<Compile Include="$(CommonTestPath)System\Net\Http\HttpMessageHandlerLoopbackServer.cs" Link="Common\System\Net\Http\HttpMessageHandlerLoopbackServer.cs" />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@

namespace System.Net.Http.Functional.Tests
{
using Configuration = System.Net.Test.Common.Configuration;

[ConditionalClass(typeof(PlatformDetection), nameof(PlatformDetection.IsNotBrowser))]
public sealed class SocketsHttpHandler_HttpClientHandler_Asynchrony_Test : HttpClientHandler_Asynchrony_Test
{
Expand Down Expand Up @@ -3728,6 +3730,137 @@ public SocketsHttpHandler_RequestContentLengthMismatchTest_Http3(ITestOutputHelp
protected override Version UseVersion => HttpVersion.Version30;
}

[ConditionalClass(typeof(PlatformDetection), nameof(PlatformDetection.IsNotBrowser))]
public abstract class SocketsHttpHandler_SecurityTest : HttpClientHandlerTestBase
{
public SocketsHttpHandler_SecurityTest(ITestOutputHelper output) : base(output) { }

[ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsNotWindows7))]
public async Task SslOptions_CustomTrust_Ok()
{
X509Certificate2Collection caCerts = new X509Certificate2Collection();
X509Certificate2 certificate = Configuration.Certificates.GetDynamicServerCerttificate(caCerts);

GenericLoopbackOptions options = new GenericLoopbackOptions() { UseSsl = true, Certificate = certificate };
await LoopbackServerFactory.CreateClientAndServerAsync(
async uri =>
{
using HttpClientHandler handler = CreateHttpClientHandler(allowAllCertificates: false);
var socketsHandler = (SocketsHttpHandler)GetUnderlyingSocketsHttpHandler(handler);

var policy = new X509ChainPolicy()
{
RevocationMode = X509RevocationMode.NoCheck,
TrustMode = X509ChainTrustMode.CustomRootTrust,
};

policy.ExtraStore.AddRange(caCerts);
policy.CustomTrustStore.Add(caCerts[caCerts.Count - 1]);
socketsHandler.SslOptions = new SslClientAuthenticationOptions() { CertificateChainPolicy = policy };
using HttpClient client = CreateHttpClient(handler);
client.DefaultVersionPolicy = HttpVersionPolicy.RequestVersionExact;
HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Get, uri) { Version = UseVersion, VersionPolicy = HttpVersionPolicy.RequestVersionExact };
// This will drive SNI and name verification
request.Headers.Host = "localhost";
HttpResponseMessage response = await client.SendAsync(request, HttpCompletionOption.ResponseContentRead);
Assert.Equal("foo", await response.Content.ReadAsStringAsync());
},
async server =>
{
await server.AcceptConnectionSendResponseAndCloseAsync(content: "foo");
}, options: options);
}

[Fact]
public async Task SslOptions_InvalidName_Throws()
{
X509Certificate2Collection caCerts = new X509Certificate2Collection();
using X509Certificate2 certificate = Configuration.Certificates.GetDynamicServerCerttificate(caCerts);

GenericLoopbackOptions options = new GenericLoopbackOptions() { UseSsl = true, Certificate = certificate };
await LoopbackServerFactory.CreateClientAndServerAsync(
async uri =>
{
using HttpClientHandler handler = CreateHttpClientHandler(allowAllCertificates: false);
var socketsHandler = (SocketsHttpHandler)GetUnderlyingSocketsHttpHandler(handler);
using HttpClient client = CreateHttpClient(handler);
client.DefaultVersionPolicy = HttpVersionPolicy.RequestVersionExact;
HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Get, uri) { Version = UseVersion, VersionPolicy = HttpVersionPolicy.RequestVersionExact };
// This will drive SNI and name verification
request.Headers.Host = Guid.NewGuid().ToString("N");

await Assert.ThrowsAsync<HttpRequestException>(() => client.SendAsync(request, HttpCompletionOption.ResponseContentRead));
},
async server =>
{
try
{
await server.AcceptConnectionSendResponseAndCloseAsync(content: "foo");
}
catch { };
}, options: options);
}

[Fact]
public async Task SslOptions_CustomPolicy_IgnoresNameMismatch()
{
X509Certificate2Collection caCerts = new X509Certificate2Collection();
X509Certificate2 certificate = Configuration.Certificates.GetDynamicServerCerttificate(caCerts);

GenericLoopbackOptions options = new GenericLoopbackOptions() { UseSsl = true, Certificate = certificate };
await LoopbackServerFactory.CreateClientAndServerAsync(
async uri =>
{
using HttpClientHandler handler = CreateHttpClientHandler(allowAllCertificates: false);
var socketsHandler = (SocketsHttpHandler)GetUnderlyingSocketsHttpHandler(handler);

var policy = new X509ChainPolicy()
{
RevocationMode = X509RevocationMode.NoCheck,
TrustMode = X509ChainTrustMode.CustomRootTrust,
VerificationFlags = X509VerificationFlags.IgnoreInvalidName,
};

policy.ExtraStore.AddRange(caCerts);
policy.CustomTrustStore.Add(caCerts[caCerts.Count -1]);
socketsHandler.SslOptions = new SslClientAuthenticationOptions() { CertificateChainPolicy = policy };

using HttpClient client = CreateHttpClient(handler);
client.DefaultVersionPolicy = HttpVersionPolicy.RequestVersionExact;
HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Get, uri) { Version = UseVersion, VersionPolicy = HttpVersionPolicy.RequestVersionExact };
// This will drive SNI and name verification
request.Headers.Host = Guid.NewGuid().ToString("N");

HttpResponseMessage response = await client.SendAsync(request, HttpCompletionOption.ResponseContentRead);
Assert.Equal("foo", await response.Content.ReadAsStringAsync());
},
async server =>
{
await server.AcceptConnectionSendResponseAndCloseAsync(content: "foo");
}, options: options);
}
}

public sealed class SocketsHttpHandler_SocketsHttpHandler_SecurityTest_Http11 : SocketsHttpHandler_SecurityTest
{
public SocketsHttpHandler_SocketsHttpHandler_SecurityTest_Http11(ITestOutputHelper output) : base(output) { }
protected override Version UseVersion => HttpVersion.Version11;
}

[ConditionalClass(typeof(PlatformDetection), nameof(PlatformDetection.SupportsAlpn))]
public sealed class SocketsHttpHandler_SocketsHttpHandler_SecurityTest_Http2 : SocketsHttpHandler_SecurityTest
{
public SocketsHttpHandler_SocketsHttpHandler_SecurityTest_Http2(ITestOutputHelper output) : base(output) { }
protected override Version UseVersion => HttpVersion.Version20;
}

[ConditionalClass(typeof(HttpClientHandlerTestBase), nameof(IsQuicSupported))]
public sealed class SocketsHttpHandler_SocketsHttpHandler_SecurityTest_Http3 : SocketsHttpHandler_SecurityTest
{
public SocketsHttpHandler_SocketsHttpHandler_SecurityTest_Http3(ITestOutputHelper output) : base(output) { }
protected override Version UseVersion => HttpVersion.Version30;
}

public class MySsl : SslStream
{
public MySsl(Stream stream) : base(stream)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,12 @@
Link="CommonTest\System\Security\Cryptography\PlatformSupport.cs" />
<Compile Include="$(CommonTestPath)System\Net\Configuration.Certificates.cs"
Link="Common\System\Net\Configuration.Certificates.cs" />
<Compile Include="$(CommonTestPath)System\Net\Configuration.Certificates.Dynamic.cs"
Link="Common\System\Net\Configuration.Certificates.Dynamic.cs" />
<Compile Include="$(CommonTestPath)System\Security\Cryptography\X509Certificates\CertificateAuthority.cs"
Link="CommonTest\System\Security\Cryptography\X509Certificates\CertificateAuthority.cs" />
<Compile Include="$(CommonTestPath)System\Security\Cryptography\X509Certificates\RevocationResponder.cs"
Link="CommonTest\System\Security\Cryptography\X509Certificates\RevocationResponder.cs" />
<Compile Include="$(CommonTestPath)System\Net\EventSourceTestLogging.cs"
Link="Common\System\Net\EventSourceTestLogging.cs" />
<Compile Include="$(CommonTestPath)System\Net\HttpsTestServer.cs"
Expand Down
Loading