Skip to content

Commit b370cc5

Browse files
committed
address mTLS certificate validation feedback
1 parent 8d433c6 commit b370cc5

1 file changed

Lines changed: 68 additions & 13 deletions

File tree

src/Apache.IoTDB/SessionPool.cs

Lines changed: 68 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,10 @@ public partial class SessionPool : IDisposable, IPoolDiagnosticReporter
5454
private readonly string _clientCertificatePath;
5555
private readonly string _clientCertificatePassword;
5656
private readonly string _rootCertificatePath;
57+
private X509Certificate2 _clientCertificate;
58+
private X509Certificate2Collection _rootCertificates;
59+
private RemoteCertificateValidationCallback _remoteCertificateValidationCallback;
60+
private LocalCertificateSelectionCallback _localCertificateSelectionCallback;
5761
private readonly int _fetchSize;
5862
/// <summary>
5963
/// _timeout is the amount of time a Session will wait for a send operation to complete successfully.
@@ -64,6 +68,9 @@ public partial class SessionPool : IDisposable, IPoolDiagnosticReporter
6468
private string _database;
6569
private readonly Utils _utilFunctions = new();
6670
private const int RetryNum = 3;
71+
private const string ServerAuthenticationOid = "1.3.6.1.5.5.7.3.1";
72+
private readonly object _tlsConfigurationLock = new();
73+
private bool _tlsConfigurationLoaded;
6774
private bool _debugMode;
6875
private bool _isClose = true;
6976
private ConcurrentClientQueue _clients;
@@ -287,7 +294,7 @@ public async Task Open(CancellationToken cancellationToken = default)
287294
{
288295
try
289296
{
290-
_clients.Add(await CreateAndOpen(_host, _port, _enableRpcCompression, _timeout, _useSsl, _clientCertificatePath, _clientCertificatePassword, _rootCertificatePath, _sqlDialect, _database, cancellationToken));
297+
_clients.Add(await CreateAndOpen(_host, _port, _enableRpcCompression, _timeout, _useSsl, _sqlDialect, _database, cancellationToken));
291298
}
292299
catch (Exception e)
293300
{
@@ -307,7 +314,7 @@ public async Task Open(CancellationToken cancellationToken = default)
307314
var endPoint = _endPoints[endPointIndex];
308315
try
309316
{
310-
var client = await CreateAndOpen(endPoint.Ip, endPoint.Port, _enableRpcCompression, _timeout, _useSsl, _clientCertificatePath, _clientCertificatePassword, _rootCertificatePath, _sqlDialect, _database, cancellationToken);
317+
var client = await CreateAndOpen(endPoint.Ip, endPoint.Port, _enableRpcCompression, _timeout, _useSsl, _sqlDialect, _database, cancellationToken);
311318
_clients.Add(client);
312319
isConnected = true;
313320
startIndex = (endPointIndex + 1) % _endPoints.Count;
@@ -343,7 +350,7 @@ public async Task<Client> Reconnect(Client originalClient = null, CancellationTo
343350
{
344351
try
345352
{
346-
var client = await CreateAndOpen(_host, _port, _enableRpcCompression, _timeout, _useSsl, _clientCertificatePath, _clientCertificatePassword, _rootCertificatePath, _sqlDialect, _database, cancellationToken);
353+
var client = await CreateAndOpen(_host, _port, _enableRpcCompression, _timeout, _useSsl, _sqlDialect, _database, cancellationToken);
347354
return client;
348355
}
349356
catch (Exception e)
@@ -367,7 +374,7 @@ public async Task<Client> Reconnect(Client originalClient = null, CancellationTo
367374
int j = (startIndex + i) % _endPoints.Count;
368375
try
369376
{
370-
var client = await CreateAndOpen(_endPoints[j].Ip, _endPoints[j].Port, _enableRpcCompression, _timeout, _useSsl, _clientCertificatePath, _clientCertificatePassword, _rootCertificatePath, _sqlDialect, _database, cancellationToken);
377+
var client = await CreateAndOpen(_endPoints[j].Ip, _endPoints[j].Port, _enableRpcCompression, _timeout, _useSsl, _sqlDialect, _database, cancellationToken);
371378
return client;
372379
}
373380
catch (Exception e)
@@ -458,19 +465,16 @@ public async Task<string> GetTimeZone()
458465
}
459466
}
460467

461-
private async Task<Client> CreateAndOpen(string host, int port, bool enableRpcCompression, int timeout, bool useSsl, string clientCertificatePath, string clientCertificatePassword, string rootCertificatePath, string sqlDialect, string database, CancellationToken cancellationToken = default)
468+
private async Task<Client> CreateAndOpen(string host, int port, bool enableRpcCompression, int timeout, bool useSsl, string sqlDialect, string database, CancellationToken cancellationToken = default)
462469
{
463470
TTransport socket;
464471

465472
if (useSsl)
466473
{
467-
var clientCertificate = LoadClientCertificate(clientCertificatePath, clientCertificatePassword);
468-
var rootCertificates = LoadRootCertificates(rootCertificatePath);
469-
var remoteCertificateValidationCallback = CreateRemoteCertificateValidationCallback(rootCertificates);
470-
var localCertificateSelectionCallback = CreateLocalCertificateSelectionCallback(clientCertificate);
474+
EnsureTlsConfigurationLoaded();
471475
socket = IPAddress.TryParse(host, out var ipAddress)
472-
? new TTlsSocketTransport(ipAddress, port, null, timeout, clientCertificate, remoteCertificateValidationCallback, localCertificateSelectionCallback)
473-
: new TTlsSocketTransport(host, port, null, timeout, clientCertificate, remoteCertificateValidationCallback, localCertificateSelectionCallback);
476+
? new TTlsSocketTransport(ipAddress, port, null, timeout, _clientCertificate, _remoteCertificateValidationCallback, _localCertificateSelectionCallback)
477+
: new TTlsSocketTransport(host, port, null, timeout, _clientCertificate, _remoteCertificateValidationCallback, _localCertificateSelectionCallback);
474478
}
475479
else
476480
{
@@ -538,14 +542,31 @@ private async Task<Client> CreateAndOpen(string host, int port, bool enableRpcCo
538542
}
539543
}
540544

545+
private void EnsureTlsConfigurationLoaded()
546+
{
547+
lock (_tlsConfigurationLock)
548+
{
549+
if (_tlsConfigurationLoaded)
550+
{
551+
return;
552+
}
553+
554+
_clientCertificate = LoadClientCertificate(_clientCertificatePath, _clientCertificatePassword);
555+
_rootCertificates = LoadRootCertificates(_rootCertificatePath);
556+
_remoteCertificateValidationCallback = CreateRemoteCertificateValidationCallback(_rootCertificates);
557+
_localCertificateSelectionCallback = CreateLocalCertificateSelectionCallback(_clientCertificate);
558+
_tlsConfigurationLoaded = true;
559+
}
560+
}
561+
541562
private static X509Certificate2 LoadClientCertificate(string clientCertificatePath, string clientCertificatePassword)
542563
{
543564
if (string.IsNullOrWhiteSpace(clientCertificatePath))
544565
{
545566
return null;
546567
}
547568

548-
return clientCertificatePassword == null
569+
return string.IsNullOrEmpty(clientCertificatePassword)
549570
? new X509Certificate2(clientCertificatePath)
550571
: new X509Certificate2(clientCertificatePath, clientCertificatePassword);
551572
}
@@ -615,6 +636,11 @@ private static RemoteCertificateValidationCallback CreateRemoteCertificateValida
615636
var serverCertificate = certificate as X509Certificate2 ?? new X509Certificate2(certificate);
616637
try
617638
{
639+
if (!HasServerAuthenticationEku(serverCertificate))
640+
{
641+
return false;
642+
}
643+
618644
using var customChain = new X509Chain();
619645
customChain.ChainPolicy.RevocationMode = X509RevocationMode.NoCheck;
620646
customChain.ChainPolicy.VerificationFlags = X509VerificationFlags.AllowUnknownCertificateAuthority;
@@ -647,6 +673,27 @@ private static RemoteCertificateValidationCallback CreateRemoteCertificateValida
647673
};
648674
}
649675

676+
private static bool HasServerAuthenticationEku(X509Certificate2 certificate)
677+
{
678+
var hasEnhancedKeyUsage = false;
679+
foreach (var extension in certificate.Extensions)
680+
{
681+
if (extension is X509EnhancedKeyUsageExtension enhancedKeyUsage)
682+
{
683+
hasEnhancedKeyUsage = true;
684+
foreach (var oid in enhancedKeyUsage.EnhancedKeyUsages)
685+
{
686+
if (string.Equals(oid.Value, ServerAuthenticationOid, StringComparison.Ordinal))
687+
{
688+
return true;
689+
}
690+
}
691+
}
692+
}
693+
694+
return !hasEnhancedKeyUsage;
695+
}
696+
650697
private static bool ChainEndsWithTrustedRoot(X509Chain chain, X509Certificate2Collection rootCertificates)
651698
{
652699
if (chain.ChainElements.Count == 0)
@@ -1977,8 +2024,16 @@ protected virtual void Dispose(bool disposing)
19772024
{
19782025
#if NET461_OR_GREATER || NETSTANDARD2_0
19792026
#else
1980-
_clients.ClientQueue.Clear();
2027+
_clients?.ClientQueue.Clear();
19812028
#endif
2029+
_clientCertificate?.Dispose();
2030+
if (_rootCertificates != null)
2031+
{
2032+
foreach (var rootCertificate in _rootCertificates)
2033+
{
2034+
rootCertificate.Dispose();
2035+
}
2036+
}
19822037
}
19832038
_clients = null;
19842039
disposedValue = true;

0 commit comments

Comments
 (0)