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 @@ -1194,7 +1194,9 @@ internal bool VerifyRemoteCertificate(
trust,
ref alertToken,
ref sslPolicyErrors,
out chainStatus);
out chainStatus,
peerCertificateChain: null,
cloneCertificateChainPolicy: false);
}

internal static bool VerifyRemoteCertificateCore(
Expand All @@ -1212,7 +1214,9 @@ internal static bool VerifyRemoteCertificateCore(
SslCertificateTrust? trust,
ref ProtocolToken alertToken,
ref SslPolicyErrors sslPolicyErrors,
out X509ChainStatusFlags chainStatus)
out X509ChainStatusFlags chainStatus,
X509Certificate2Collection? peerCertificateChain,
bool cloneCertificateChainPolicy)
{
chainStatus = X509ChainStatusFlags.NoError;

Expand Down Expand Up @@ -1248,7 +1252,9 @@ internal static bool VerifyRemoteCertificateCore(

if (sslAuthenticationOptions.CertificateChainPolicy != null)
{
chain.ChainPolicy = sslAuthenticationOptions.CertificateChainPolicy;
chain.ChainPolicy = cloneCertificateChainPolicy
? sslAuthenticationOptions.CertificateChainPolicy.Clone()
: sslAuthenticationOptions.CertificateChainPolicy;
}
else
{
Expand All @@ -1274,6 +1280,11 @@ internal static bool VerifyRemoteCertificateCore(
}
}

if (peerCertificateChain is { Count: > 0 })
{
chain.ChainPolicy.ExtraStore.AddRange(peerCertificateChain);
}

// set ApplicationPolicy unless already provided.
if (chain.ChainPolicy.ApplicationPolicy.Count == 0)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -360,14 +360,9 @@ public SslPolicyErrors AcceptWithDefaultValidation()
SR.Format(SR.net_tlssession_validation_not_pending, nameof(AcceptWithDefaultValidation)));
}

// Build a fresh X509Chain locally and seed it with the peer-sent intermediates.
// The chain instance is never exposed to TlsSession callers; once validation is
// recorded it is disposed in SetRemoteCertificateValidationResult below.
// Build a fresh X509Chain locally. VerifyRemoteCertificateCore applies the configured
// chain policy before adding the peer-sent intermediates captured by this session.
using X509Chain chain = new X509Chain();
if (_externalRemoteCertificates is { Count: > 0 } intermediates)
{
chain.ChainPolicy.ExtraStore.AddRange(intermediates);
}

ProtocolToken alertToken = default;
SslPolicyErrors sslPolicyErrors = SslPolicyErrors.None;
Expand All @@ -389,7 +384,9 @@ public SslPolicyErrors AcceptWithDefaultValidation()
trust: null,
ref alertToken,
ref sslPolicyErrors,
out _);
out _,
_externalRemoteCertificates,
cloneCertificateChainPolicy: true);
}
finally
{
Expand Down Expand Up @@ -1856,25 +1853,69 @@ private void OnHandshakeCompleted()
// when AcceptWithDefaultValidation runs.
private void CaptureRemoteCertificateForExternalValidation()
{
X509ChainPolicy? chainPolicy = _options.CertificateChainPolicy?.Clone();
int preexistingExtraCertsCount = chainPolicy?.ExtraStore.Count ?? 0;
X509Chain? chain = null;
_externalPendingCert = CertificateValidationPal.GetRemoteCertificate(
_securityContext, ref chain, _options.CertificateChainPolicy);
X509Certificate2Collection? intermediates = null;

// Snapshot the peer-sent intermediates into a flat collection and dispose the
// platform-built chain immediately. The chain instance never escapes the PAL
// boundary into TlsSession state or its public surface.
if (chain is not null)
try
{
if (chain.ChainElements.Count > 1)
_externalPendingCert = CertificateValidationPal.GetRemoteCertificate(
_securityContext, ref chain, chainPolicy);

if (chain is not null)
{
X509Certificate2Collection intermediates = new X509Certificate2Collection();
for (int i = 1; i < chain.ChainElements.Count; i++)
X509Certificate2Collection extraStore = chain.ChainPolicy.ExtraStore;
while (extraStore.Count > preexistingExtraCertsCount)
{
intermediates.Add(new X509Certificate2(chain.ChainElements[i].Certificate));
X509Certificate2 certificate = extraStore[preexistingExtraCertsCount];
extraStore.RemoveAt(preexistingExtraCertsCount);

bool transferred = false;
try
{
if (_externalPendingCert is null ||
!certificate.RawDataMemory.Span.SequenceEqual(_externalPendingCert.RawDataMemory.Span))
{
(intermediates ??= new X509Certificate2Collection()).Add(certificate);
transferred = true;
}
}
finally
{
if (!transferred)
{
certificate.Dispose();
}
}
}
_externalRemoteCertificates = intermediates;
}
chain.Dispose();

_externalRemoteCertificates = intermediates;
intermediates = null;
}
finally
{
if (intermediates is not null)
{
foreach (X509Certificate2 certificate in intermediates)
{
certificate.Dispose();
}
}

if (chain is not null)
{
X509Certificate2Collection extraStore = chain.ChainPolicy.ExtraStore;
while (extraStore.Count > preexistingExtraCertsCount)
{
X509Certificate2 certificate = extraStore[preexistingExtraCertsCount];
extraStore.RemoveAt(preexistingExtraCertsCount);
certificate.Dispose();
}

chain.Dispose();
}
}

_externalValidationPending = true;
Expand Down
Loading
Loading