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
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
using System;
using System.Collections.Generic;
using System.Net;
using System.Net.Security;
using System.Security.Cryptography.X509Certificates;
using System.Threading;
using System.Threading.Tasks;
Expand Down Expand Up @@ -115,7 +116,7 @@ protected override TTransport CreateTransport()
? new X509Certificate2(TlsOptions.TrustedCertificatePath!)
: null;

var certValidator = HiveServer2TlsImpl.GetCertificateValidator(TlsOptions);
RemoteCertificateValidationCallback certValidator = (sender, cert, chain, errors) => HiveServer2TlsImpl.ValidateCertificate(cert, errors, TlsOptions);

if (IPAddress.TryParse(hostName!, out var ipAddress))
{
Expand Down
96 changes: 54 additions & 42 deletions csharp/src/Drivers/Apache/Hive2/HiveServer2TlsImpl.cs
Original file line number Diff line number Diff line change
Expand Up @@ -65,11 +65,8 @@ static internal TlsProperties GetHttpTlsOptions(IReadOnlyDictionary<string, stri
tlsProperties.DisableServerCertificateValidation = false;
tlsProperties.AllowHostnameMismatch = properties.TryGetValue(HttpTlsOptions.AllowHostnameMismatch, out string? allowHostnameMismatch) && bool.TryParse(allowHostnameMismatch, out bool allowHostnameMismatchBool) && allowHostnameMismatchBool;
tlsProperties.AllowSelfSigned = properties.TryGetValue(HttpTlsOptions.AllowSelfSigned, out string? allowSelfSigned) && bool.TryParse(allowSelfSigned, out bool allowSelfSignedBool) && allowSelfSignedBool;
if (tlsProperties.AllowSelfSigned)
{
if (!properties.TryGetValue(HttpTlsOptions.TrustedCertificatePath, out string? trustedCertificatePath)) return tlsProperties;
tlsProperties.TrustedCertificatePath = trustedCertificatePath != "" && File.Exists(trustedCertificatePath) ? trustedCertificatePath : throw new FileNotFoundException("Trusted certificate path is invalid or file does not exist.");
}
if (!properties.TryGetValue(HttpTlsOptions.TrustedCertificatePath, out string? trustedCertificatePath)) return tlsProperties;
tlsProperties.TrustedCertificatePath = trustedCertificatePath != "" && File.Exists(trustedCertificatePath) ? trustedCertificatePath : throw new FileNotFoundException("Trusted certificate path is invalid or file does not exist.");
return tlsProperties;
}

Expand All @@ -78,33 +75,39 @@ static internal HttpClientHandler NewHttpClientHandler(TlsProperties tlsProperti
HttpClientHandler httpClientHandler = new();
if (tlsProperties.IsTlsEnabled)
{
httpClientHandler.ServerCertificateCustomValidationCallback = (request, certificate, chain, policyErrors) =>
{
if (policyErrors == SslPolicyErrors.None || tlsProperties.DisableServerCertificateValidation) return true;
if (string.IsNullOrEmpty(tlsProperties.TrustedCertificatePath))
{
return
(!policyErrors.HasFlag(SslPolicyErrors.RemoteCertificateChainErrors) || tlsProperties.AllowSelfSigned)
&& (!policyErrors.HasFlag(SslPolicyErrors.RemoteCertificateNameMismatch) || tlsProperties.AllowHostnameMismatch);
}
if (certificate == null)
return false;
X509Certificate2 customCertificate = new X509Certificate2(tlsProperties.TrustedCertificatePath);
X509Chain chain2 = new X509Chain();
chain2.ChainPolicy.ExtraStore.Add(customCertificate);

// "tell the X509Chain class that I do trust this root certs and it should check just the certs in the chain and nothing else"
chain2.ChainPolicy.VerificationFlags = X509VerificationFlags.AllowUnknownCertificateAuthority;

// Build the chain and verify
return chain2.Build(certificate);
};
httpClientHandler.ServerCertificateCustomValidationCallback = (request, cert, chain, errors) => ValidateCertificate(cert, errors, tlsProperties);
}
proxyConfigurator.ConfigureProxy(httpClientHandler);
httpClientHandler.AutomaticDecompression = DecompressionMethods.GZip | DecompressionMethods.Deflate;
return httpClientHandler;
}

static private bool IsSelfSigned(X509Certificate2 cert)
{
return cert.Subject == cert.Issuer && IsSignedBy(cert, cert);
}

static private bool IsSignedBy(X509Certificate2 cert, X509Certificate2 issuer)
{
try
{
using (var chain = new X509Chain())
{
chain.ChainPolicy.ExtraStore.Add(issuer);
chain.ChainPolicy.VerificationFlags = X509VerificationFlags.AllowUnknownCertificateAuthority;
chain.ChainPolicy.RevocationMode = X509RevocationMode.Online;

return chain.Build(cert)
&& chain.ChainElements.Count == 1
&& chain.ChainElements[0].Certificate.Thumbprint == issuer.Thumbprint;
}
}
catch
{
return false;
}
}

static internal TlsProperties GetStandardTlsOptions(IReadOnlyDictionary<string, string> properties)
{
TlsProperties tlsProperties = new();
Expand All @@ -130,28 +133,37 @@ static internal TlsProperties GetStandardTlsOptions(IReadOnlyDictionary<string,
tlsProperties.DisableServerCertificateValidation = false;
tlsProperties.AllowHostnameMismatch = properties.TryGetValue(StandardTlsOptions.AllowHostnameMismatch, out string? allowHostnameMismatch) && bool.TryParse(allowHostnameMismatch, out bool allowHostnameMismatchBool) && allowHostnameMismatchBool;
tlsProperties.AllowSelfSigned = properties.TryGetValue(StandardTlsOptions.AllowSelfSigned, out string? allowSelfSigned) && bool.TryParse(allowSelfSigned, out bool allowSelfSignedBool) && allowSelfSignedBool;
if (tlsProperties.AllowSelfSigned)
{
if (!properties.TryGetValue(StandardTlsOptions.TrustedCertificatePath, out string? trustedCertificatePath)) return tlsProperties;
tlsProperties.TrustedCertificatePath = trustedCertificatePath != "" && File.Exists(trustedCertificatePath) ? trustedCertificatePath : throw new FileNotFoundException("Trusted certificate path is invalid or file does not exist.");
}
if (!properties.TryGetValue(StandardTlsOptions.TrustedCertificatePath, out string? trustedCertificatePath)) return tlsProperties;
tlsProperties.TrustedCertificatePath = trustedCertificatePath != "" && File.Exists(trustedCertificatePath) ? trustedCertificatePath : throw new FileNotFoundException("Trusted certificate path is invalid or file does not exist.");
return tlsProperties;
}

static internal RemoteCertificateValidationCallback GetCertificateValidator(TlsProperties tlsProperties)
static internal bool ValidateCertificate(X509Certificate? cert, SslPolicyErrors policyErrors, TlsProperties tlsProperties)
{
return (object sender, X509Certificate? certificate, X509Chain? chain, SslPolicyErrors policyErrors) =>
{
if (policyErrors == SslPolicyErrors.None || tlsProperties.DisableServerCertificateValidation) return true;
if (string.IsNullOrEmpty(tlsProperties.TrustedCertificatePath))
{
return
(!policyErrors.HasFlag(SslPolicyErrors.RemoteCertificateChainErrors) || tlsProperties.AllowSelfSigned)
&& (!policyErrors.HasFlag(SslPolicyErrors.RemoteCertificateNameMismatch) || tlsProperties.AllowHostnameMismatch);
}
if (policyErrors == SslPolicyErrors.None || tlsProperties.DisableServerCertificateValidation)
return true;

if (cert == null || !(cert is X509Certificate2 cert2))
return false;
};

bool isNameMismatchError = policyErrors.HasFlag(SslPolicyErrors.RemoteCertificateNameMismatch) && !tlsProperties.AllowHostnameMismatch;

if (isNameMismatchError) return false;

if (string.IsNullOrEmpty(tlsProperties.TrustedCertificatePath))
{
return !policyErrors.HasFlag(SslPolicyErrors.RemoteCertificateChainErrors) || (tlsProperties.AllowSelfSigned && IsSelfSigned(cert2));
}

X509Certificate2 trustedRoot = new X509Certificate2(tlsProperties.TrustedCertificatePath);
X509Chain customChain = new();
customChain.ChainPolicy.ExtraStore.Add(trustedRoot);
// "tell the X509Chain class that I do trust this root certs and it should check just the certs in the chain and nothing else"
customChain.ChainPolicy.VerificationFlags = X509VerificationFlags.AllowUnknownCertificateAuthority;
customChain.ChainPolicy.RevocationMode = X509RevocationMode.Online;

bool chainValid = customChain.Build(cert2);
return chainValid || (tlsProperties.AllowSelfSigned && IsSelfSigned(cert2));
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
using System;
using System.Collections.Generic;
using System.Net;
using System.Net.Security;
using System.Security.Cryptography.X509Certificates;
using System.Threading;
using System.Threading.Tasks;
Expand Down Expand Up @@ -115,13 +116,14 @@ protected override TTransport CreateTransport()
TTransport transport;
if (TlsOptions.IsTlsEnabled)
{
RemoteCertificateValidationCallback certValidator = (sender, cert, chain, errors) => HiveServer2TlsImpl.ValidateCertificate(cert, errors, TlsOptions);
if (IPAddress.TryParse(hostName!, out var address))
{
transport = new TTlsSocketTransport(address!, int.Parse(port!), config: new(), 0, !string.IsNullOrEmpty(TlsOptions.TrustedCertificatePath) ? new X509Certificate2(TlsOptions.TrustedCertificatePath!) : null, certValidator: HiveServer2TlsImpl.GetCertificateValidator(TlsOptions));
transport = new TTlsSocketTransport(address!, int.Parse(port!), config: new(), 0, null, certValidator: certValidator);
}
else
{
transport = new TTlsSocketTransport(hostName!, int.Parse(port!), config: new(), 0, !string.IsNullOrEmpty(TlsOptions.TrustedCertificatePath) ? new X509Certificate2(TlsOptions.TrustedCertificatePath!) : null, certValidator: HiveServer2TlsImpl.GetCertificateValidator(TlsOptions));
transport = new TTlsSocketTransport(hostName!, int.Parse(port!), config: new(), 0, null, certValidator: certValidator);
}
}
else
Expand Down
4 changes: 0 additions & 4 deletions csharp/test/Drivers/Apache/Hive2/HiveServer2TlsImplTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -64,8 +64,6 @@ internal void TestValidateStandardTlsOptions(Dictionary<string, string>? dataTyp
yield return new object?[] { new Dictionary<string, string> { { HttpTlsOptions.IsTlsEnabled, "True" } }, new TlsProperties { IsTlsEnabled = true, DisableServerCertificateValidation = false, AllowSelfSigned = false, AllowHostnameMismatch = false } };
yield return new object?[] { new Dictionary<string, string> { { HttpTlsOptions.IsTlsEnabled, "tRUe" }, { HttpTlsOptions.AllowSelfSigned, "true" } }, new TlsProperties { IsTlsEnabled = true, DisableServerCertificateValidation = false, AllowSelfSigned = true, AllowHostnameMismatch = false } };
yield return new object?[] { new Dictionary<string, string> { { HttpTlsOptions.IsTlsEnabled, "TruE" }, { HttpTlsOptions.AllowSelfSigned, "True" }, { HttpTlsOptions.AllowHostnameMismatch, "True" } }, new TlsProperties { IsTlsEnabled = true, DisableServerCertificateValidation = false, AllowSelfSigned = true, AllowHostnameMismatch = true } };
// certificate path is ignored if self signed is not allowed
yield return new object?[] { new Dictionary<string, string> { { HttpTlsOptions.IsTlsEnabled, "True" }, { HttpTlsOptions.AllowSelfSigned, "False" }, { HttpTlsOptions.AllowHostnameMismatch, "True" }, { HttpTlsOptions.TrustedCertificatePath, "" } }, new TlsProperties { IsTlsEnabled = true, DisableServerCertificateValidation = false, AllowSelfSigned = false, AllowHostnameMismatch = true } };
// invalid certificate path
yield return new object?[] { new Dictionary<string, string> { { HttpTlsOptions.IsTlsEnabled, "True" }, { HttpTlsOptions.AllowSelfSigned, "True" }, { HttpTlsOptions.AllowHostnameMismatch, "True" }, { HttpTlsOptions.TrustedCertificatePath, "" } }, null, typeof(FileNotFoundException) };
}
Expand All @@ -86,8 +84,6 @@ internal void TestValidateStandardTlsOptions(Dictionary<string, string>? dataTyp
yield return new object?[] { new Dictionary<string, string> { { StandardTlsOptions.IsTlsEnabled, "True" } }, new TlsProperties { IsTlsEnabled = true, DisableServerCertificateValidation = false, AllowSelfSigned = false, AllowHostnameMismatch = false } };
yield return new object?[] { new Dictionary<string, string> { { StandardTlsOptions.IsTlsEnabled, "tRUe" }, { StandardTlsOptions.AllowSelfSigned, "true" } }, new TlsProperties { IsTlsEnabled = true, DisableServerCertificateValidation = false, AllowSelfSigned = true, AllowHostnameMismatch = false } };
yield return new object?[] { new Dictionary<string, string> { { StandardTlsOptions.IsTlsEnabled, "TruE" }, { StandardTlsOptions.AllowSelfSigned, "True" }, { StandardTlsOptions.AllowHostnameMismatch, "True" } }, new TlsProperties { IsTlsEnabled = true, DisableServerCertificateValidation = false, AllowSelfSigned = true, AllowHostnameMismatch = true } };
// certificate path is ignored if self signed is not allowed
yield return new object?[] { new Dictionary<string, string> { { StandardTlsOptions.IsTlsEnabled, "True" }, { StandardTlsOptions.AllowSelfSigned, "False" }, { StandardTlsOptions.AllowHostnameMismatch, "True" }, { StandardTlsOptions.TrustedCertificatePath, "" } }, new TlsProperties { IsTlsEnabled = true, DisableServerCertificateValidation = false, AllowSelfSigned = false, AllowHostnameMismatch = true } };
// invalid certificate path
yield return new object?[] { new Dictionary<string, string> { { StandardTlsOptions.IsTlsEnabled, "True" }, { StandardTlsOptions.AllowSelfSigned, "True" }, { StandardTlsOptions.AllowHostnameMismatch, "True" }, { StandardTlsOptions.TrustedCertificatePath, "" } }, null, typeof(FileNotFoundException) };
}
Expand Down
Loading