diff --git a/csharp/src/Drivers/Apache/Hive2/HiveServer2StandardConnection.cs b/csharp/src/Drivers/Apache/Hive2/HiveServer2StandardConnection.cs index 66f175a147..4f89a39047 100644 --- a/csharp/src/Drivers/Apache/Hive2/HiveServer2StandardConnection.cs +++ b/csharp/src/Drivers/Apache/Hive2/HiveServer2StandardConnection.cs @@ -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; @@ -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)) { diff --git a/csharp/src/Drivers/Apache/Hive2/HiveServer2TlsImpl.cs b/csharp/src/Drivers/Apache/Hive2/HiveServer2TlsImpl.cs index 345974d77b..9a9311d7af 100644 --- a/csharp/src/Drivers/Apache/Hive2/HiveServer2TlsImpl.cs +++ b/csharp/src/Drivers/Apache/Hive2/HiveServer2TlsImpl.cs @@ -65,11 +65,8 @@ static internal TlsProperties GetHttpTlsOptions(IReadOnlyDictionary - { - 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 properties) { TlsProperties tlsProperties = new(); @@ -130,28 +133,37 @@ static internal TlsProperties GetStandardTlsOptions(IReadOnlyDictionary - { - 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)); } } } diff --git a/csharp/src/Drivers/Apache/Impala/ImpalaStandardConnection.cs b/csharp/src/Drivers/Apache/Impala/ImpalaStandardConnection.cs index 6cbef924a8..a87a7973b3 100644 --- a/csharp/src/Drivers/Apache/Impala/ImpalaStandardConnection.cs +++ b/csharp/src/Drivers/Apache/Impala/ImpalaStandardConnection.cs @@ -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; @@ -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 diff --git a/csharp/test/Drivers/Apache/Hive2/HiveServer2TlsImplTest.cs b/csharp/test/Drivers/Apache/Hive2/HiveServer2TlsImplTest.cs index f707b992f5..7ae6a207f3 100644 --- a/csharp/test/Drivers/Apache/Hive2/HiveServer2TlsImplTest.cs +++ b/csharp/test/Drivers/Apache/Hive2/HiveServer2TlsImplTest.cs @@ -64,8 +64,6 @@ internal void TestValidateStandardTlsOptions(Dictionary? dataTyp yield return new object?[] { new Dictionary { { HttpTlsOptions.IsTlsEnabled, "True" } }, new TlsProperties { IsTlsEnabled = true, DisableServerCertificateValidation = false, AllowSelfSigned = false, AllowHostnameMismatch = false } }; yield return new object?[] { new Dictionary { { HttpTlsOptions.IsTlsEnabled, "tRUe" }, { HttpTlsOptions.AllowSelfSigned, "true" } }, new TlsProperties { IsTlsEnabled = true, DisableServerCertificateValidation = false, AllowSelfSigned = true, AllowHostnameMismatch = false } }; yield return new object?[] { new Dictionary { { 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 { { 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 { { HttpTlsOptions.IsTlsEnabled, "True" }, { HttpTlsOptions.AllowSelfSigned, "True" }, { HttpTlsOptions.AllowHostnameMismatch, "True" }, { HttpTlsOptions.TrustedCertificatePath, "" } }, null, typeof(FileNotFoundException) }; } @@ -86,8 +84,6 @@ internal void TestValidateStandardTlsOptions(Dictionary? dataTyp yield return new object?[] { new Dictionary { { StandardTlsOptions.IsTlsEnabled, "True" } }, new TlsProperties { IsTlsEnabled = true, DisableServerCertificateValidation = false, AllowSelfSigned = false, AllowHostnameMismatch = false } }; yield return new object?[] { new Dictionary { { StandardTlsOptions.IsTlsEnabled, "tRUe" }, { StandardTlsOptions.AllowSelfSigned, "true" } }, new TlsProperties { IsTlsEnabled = true, DisableServerCertificateValidation = false, AllowSelfSigned = true, AllowHostnameMismatch = false } }; yield return new object?[] { new Dictionary { { 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 { { 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 { { StandardTlsOptions.IsTlsEnabled, "True" }, { StandardTlsOptions.AllowSelfSigned, "True" }, { StandardTlsOptions.AllowHostnameMismatch, "True" }, { StandardTlsOptions.TrustedCertificatePath, "" } }, null, typeof(FileNotFoundException) }; }