Skip to content
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
3 changes: 2 additions & 1 deletion StackExchange.Redis.sln.DotSettings
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
<wpf:ResourceDictionary xml:space="preserve" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:s="clr-namespace:System;assembly=mscorlib" xmlns:ss="urn:shemas-jetbrains-com:settings-storage-xaml" xmlns:wpf="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=OK/@EntryIndexedValue">OK</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=PONG/@EntryIndexedValue">PONG</s:String></wpf:ResourceDictionary>
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=PONG/@EntryIndexedValue">PONG</s:String>
<s:Boolean x:Key="/Default/UserDictionary/Words/=pubsub/@EntryIndexedValue">True</s:Boolean></wpf:ResourceDictionary>
14 changes: 12 additions & 2 deletions src/StackExchange.Redis/ConfigurationOptions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -390,8 +390,18 @@ private static bool CheckTrustedIssuer(X509Certificate2 certificateToValidate, X
try
{
// This only verifies that the chain is valid, but with AllowUnknownCertificateAuthority could trust
// self-signed or partial chained vertificates
var chainIsVerified = chain.Build(certificateToValidate);
// self-signed or partial chained certificates
bool chainIsVerified;
try
{
chainIsVerified = chain.Build(certificateToValidate);
}
catch (ArgumentException ex) when ((ex.ParamName ?? ex.Message) == "certificate" && Runtime.IsMono)
{
// work around Mono cert limitation; report as rejected rather than fault
// (note also the likely .ctor mixup re param-name vs message)
chainIsVerified = false;
}
if (chainIsVerified)
{
// Our method is "TrustIssuer", which means any intermediate cert we're being told to trust
Expand Down
9 changes: 9 additions & 0 deletions src/StackExchange.Redis/Runtime.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
using System;
using System.Runtime.InteropServices;

namespace StackExchange.Redis;

internal static class Runtime
{
public static readonly bool IsMono = RuntimeInformation.FrameworkDescription.StartsWith("Mono ", StringComparison.OrdinalIgnoreCase);
}
45 changes: 27 additions & 18 deletions tests/StackExchange.Redis.Tests/Certificates/CertValidationTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -16,30 +16,39 @@ public void CheckIssuerValidity()

// Trusting CA explicitly
var callback = ConfigurationOptions.TrustIssuerCallback(Path.Combine("Certificates", "ca.foo.com.pem"));
Assert.True(callback(this, endpointCert, null, SslPolicyErrors.None));
Assert.True(callback(this, endpointCert, null, SslPolicyErrors.RemoteCertificateChainErrors));
Assert.False(callback(this, endpointCert, null, SslPolicyErrors.RemoteCertificateNameMismatch));
Assert.False(callback(this, endpointCert, null, SslPolicyErrors.RemoteCertificateNotAvailable));
Assert.False(callback(this, endpointCert, null, SslPolicyErrors.RemoteCertificateChainErrors | SslPolicyErrors.RemoteCertificateNameMismatch));
Assert.False(callback(this, endpointCert, null, SslPolicyErrors.RemoteCertificateChainErrors | SslPolicyErrors.RemoteCertificateNotAvailable));
Assert.True(callback(this, endpointCert, null, SslPolicyErrors.None), "subtest 1a");
Assert.True(callback(this, endpointCert, null, SslPolicyErrors.RemoteCertificateChainErrors), "subtest 1b");
Assert.False(callback(this, endpointCert, null, SslPolicyErrors.RemoteCertificateNameMismatch), "subtest 1c");
Assert.False(callback(this, endpointCert, null, SslPolicyErrors.RemoteCertificateNotAvailable), "subtest 1d");
Assert.False(callback(this, endpointCert, null, SslPolicyErrors.RemoteCertificateChainErrors | SslPolicyErrors.RemoteCertificateNameMismatch), "subtest 1e");
Assert.False(callback(this, endpointCert, null, SslPolicyErrors.RemoteCertificateChainErrors | SslPolicyErrors.RemoteCertificateNotAvailable), "subtest 1f");

// Trusting the remote endpoint cert directly
callback = ConfigurationOptions.TrustIssuerCallback(Path.Combine("Certificates", "device01.foo.com.pem"));
Assert.True(callback(this, endpointCert, null, SslPolicyErrors.None));
Assert.True(callback(this, endpointCert, null, SslPolicyErrors.RemoteCertificateChainErrors));
Assert.False(callback(this, endpointCert, null, SslPolicyErrors.RemoteCertificateNameMismatch));
Assert.False(callback(this, endpointCert, null, SslPolicyErrors.RemoteCertificateNotAvailable));
Assert.False(callback(this, endpointCert, null, SslPolicyErrors.RemoteCertificateChainErrors | SslPolicyErrors.RemoteCertificateNameMismatch));
Assert.False(callback(this, endpointCert, null, SslPolicyErrors.RemoteCertificateChainErrors | SslPolicyErrors.RemoteCertificateNotAvailable));
Assert.True(callback(this, endpointCert, null, SslPolicyErrors.None), "subtest 2a");
if (Runtime.IsMono)
{
// Mono doesn't support this cert usage, reports as rejection (happy for someone to work around this, but isn't high priority)
Assert.False(callback(this, endpointCert, null, SslPolicyErrors.RemoteCertificateChainErrors), "subtest 2b");
}
else
{
Assert.True(callback(this, endpointCert, null, SslPolicyErrors.RemoteCertificateChainErrors), "subtest 2b");
}

Assert.False(callback(this, endpointCert, null, SslPolicyErrors.RemoteCertificateNameMismatch), "subtest 2c");
Assert.False(callback(this, endpointCert, null, SslPolicyErrors.RemoteCertificateNotAvailable), "subtest 2d");
Assert.False(callback(this, endpointCert, null, SslPolicyErrors.RemoteCertificateChainErrors | SslPolicyErrors.RemoteCertificateNameMismatch), "subtest 2e");
Assert.False(callback(this, endpointCert, null, SslPolicyErrors.RemoteCertificateChainErrors | SslPolicyErrors.RemoteCertificateNotAvailable), "subtest 2f");

// Attempting to trust another CA (mismatch)
callback = ConfigurationOptions.TrustIssuerCallback(Path.Combine("Certificates", "ca2.foo.com.pem"));
Assert.True(callback(this, endpointCert, null, SslPolicyErrors.None));
Assert.False(callback(this, endpointCert, null, SslPolicyErrors.RemoteCertificateChainErrors));
Assert.False(callback(this, endpointCert, null, SslPolicyErrors.RemoteCertificateNameMismatch));
Assert.False(callback(this, endpointCert, null, SslPolicyErrors.RemoteCertificateNotAvailable));
Assert.False(callback(this, endpointCert, null, SslPolicyErrors.RemoteCertificateChainErrors | SslPolicyErrors.RemoteCertificateNameMismatch));
Assert.False(callback(this, endpointCert, null, SslPolicyErrors.RemoteCertificateChainErrors | SslPolicyErrors.RemoteCertificateNotAvailable));
Assert.True(callback(this, endpointCert, null, SslPolicyErrors.None), "subtest 3a");
Assert.False(callback(this, endpointCert, null, SslPolicyErrors.RemoteCertificateChainErrors), "subtest 3b");
Assert.False(callback(this, endpointCert, null, SslPolicyErrors.RemoteCertificateNameMismatch), "subtest 3c");
Assert.False(callback(this, endpointCert, null, SslPolicyErrors.RemoteCertificateNotAvailable), "subtest 3d");
Assert.False(callback(this, endpointCert, null, SslPolicyErrors.RemoteCertificateChainErrors | SslPolicyErrors.RemoteCertificateNameMismatch), "subtest 3e");
Assert.False(callback(this, endpointCert, null, SslPolicyErrors.RemoteCertificateChainErrors | SslPolicyErrors.RemoteCertificateNotAvailable), "subtest 3f");
}

private static X509Certificate2 LoadCert(string certificatePath) => new X509Certificate2(File.ReadAllBytes(certificatePath));
Expand Down
5 changes: 4 additions & 1 deletion tests/StackExchange.Redis.Tests/FormatTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,10 @@ public void ParseEndPoint(string data, EndPoint expected, string? expectedFormat
[InlineData(CommandFlags.DemandReplica | CommandFlags.FireAndForget, "PreferMaster, FireAndForget, DemandReplica")] // 2-bit flag is hit-and-miss
#endif
public void CommandFlagsFormatting(CommandFlags value, string expected)
=> Assert.Equal(expected, value.ToString());
{
Assert.SkipWhen(Runtime.IsMono, "Mono has different enum flag behavior");
Assert.Equal(expected, value.ToString());
}

[Theory]
[InlineData(ClientType.Normal, "Normal")]
Expand Down
Loading