-
Notifications
You must be signed in to change notification settings - Fork 5.5k
Add timestamp-based expiration to cached SafeFreeCredentials #66334
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -171,8 +171,8 @@ internal void Close() | |
| { | ||
| if (!_remoteCertificateExposed) | ||
| { | ||
| _remoteCertificate?.Dispose(); | ||
| _remoteCertificate = null; | ||
| _remoteCertificate?.Dispose(); | ||
| _remoteCertificate = null; | ||
| } | ||
|
|
||
| _securityContext?.Dispose(); | ||
|
|
@@ -607,9 +607,7 @@ private bool AcquireClientCredentials(ref byte[]? thumbPrint) | |
| _sslAuthenticationOptions.CertificateContext = SslStreamCertificateContext.Create(selectedCert!); | ||
| } | ||
|
|
||
| _credentialsHandle = SslStreamPal.AcquireCredentialsHandle(_sslAuthenticationOptions.CertificateContext, | ||
| _sslAuthenticationOptions.EnabledSslProtocols, _sslAuthenticationOptions.EncryptionPolicy, _sslAuthenticationOptions.IsServer); | ||
|
|
||
| _credentialsHandle = AcquireCredentialsHandle(_sslAuthenticationOptions); | ||
| thumbPrint = guessedThumbPrint; // Delay until here in case something above threw. | ||
| } | ||
| } | ||
|
|
@@ -713,14 +711,65 @@ private bool AcquireServerCredentials(ref byte[]? thumbPrint) | |
| } | ||
| else | ||
| { | ||
| _credentialsHandle = SslStreamPal.AcquireCredentialsHandle(_sslAuthenticationOptions.CertificateContext, _sslAuthenticationOptions.EnabledSslProtocols, | ||
| _sslAuthenticationOptions.EncryptionPolicy, _sslAuthenticationOptions.IsServer); | ||
| _credentialsHandle = AcquireCredentialsHandle(_sslAuthenticationOptions); | ||
| thumbPrint = guessedThumbPrint; | ||
| } | ||
|
|
||
| return cachedCred; | ||
| } | ||
|
|
||
| private static SafeFreeCredentials AcquireCredentialsHandle(SslAuthenticationOptions sslAuthenticationOptions) | ||
| { | ||
| SafeFreeCredentials cred = SslStreamPal.AcquireCredentialsHandle(sslAuthenticationOptions.CertificateContext, sslAuthenticationOptions.EnabledSslProtocols, | ||
| sslAuthenticationOptions.EncryptionPolicy, sslAuthenticationOptions.IsServer); | ||
|
|
||
| if (sslAuthenticationOptions.CertificateContext != null) | ||
| { | ||
| // | ||
| // Since the SafeFreeCredentials can be cached and reused, it may happen on long running processes that some cert on | ||
| // the chain expires and all subsequent connections would send expired intermediate certificates. Find the earliest | ||
| // NotAfter timestamp on the chain and use it as expiration timestamp for the credentials. | ||
| // This provides an opportunity to recreate the credentials with an alternative (and still valid) | ||
| // certificate chain. | ||
| // | ||
| SslStreamCertificateContext certificateContext = sslAuthenticationOptions.CertificateContext; | ||
| cred._expiry = GetExpiryTimestamp(certificateContext); | ||
|
|
||
| if (cred._expiry < DateTime.UtcNow) | ||
| { | ||
| // | ||
| // The CertificateContext from auth options is recreated just before creating the SafeFreeCredentials. However, in case when | ||
| // it was provided by the user code, it may still contain the (now expired) certificate chain. Such expiration timestamp would | ||
| // effectively disable caching as it would lead to creating new credentials for each connection. We attempt to recover by creating | ||
| // a temporary certificate context (which builds a new chain with hopefully more recent chain). | ||
| // | ||
| certificateContext = SslStreamCertificateContext.Create( | ||
| certificateContext.Certificate, | ||
| new X509Certificate2Collection(certificateContext.IntermediateCertificates), | ||
| trust: certificateContext.Trust); | ||
|
|
||
| cred._expiry = GetExpiryTimestamp(certificateContext); | ||
| } | ||
|
|
||
| static DateTime GetExpiryTimestamp(SslStreamCertificateContext certificateContext) | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We can possibly also add it to SslStreamCertificateContext itself. It may be used multiple times but I guess the difference would be probably very small.
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I considered it, but I couldn't think of other uses elsewhere. We can always move it later when we need it. |
||
| { | ||
| DateTime expiry = certificateContext.Certificate.NotAfter; | ||
|
|
||
| foreach (X509Certificate2 cert in certificateContext.IntermediateCertificates) | ||
| { | ||
| if (cert.NotAfter < expiry) | ||
| { | ||
| expiry = cert.NotAfter; | ||
| } | ||
| } | ||
|
|
||
| return expiry.ToUniversalTime(); | ||
| } | ||
| } | ||
|
|
||
| return cred; | ||
| } | ||
|
|
||
| // | ||
| internal ProtocolToken NextMessage(ReadOnlySpan<byte> incomingBuffer) | ||
| { | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I know this is tricky but AFAIK Unix does not have the same issue as Windows. I'm wondering if we should (need to?) replicate the logic.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I wasn't sure how to do this so that the code compiles for both platforms, since the code for
SslSessionsCachewhere I access the Expiry property is not platform specific. However, it would be possible to always leave MaxValue on non-windows platforms and set the expiry in SslStreamPal.Windows.cs