-
Notifications
You must be signed in to change notification settings - Fork 4.9k
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
include more details in exception if remote certificate validation fails #40110
Conversation
Tagging subscribers to this area: @dotnet/ncl |
src/libraries/System.Net.Security/src/System/Net/Security/SecureChannel.cs
Outdated
Show resolved
Hide resolved
Looks generally reasonable to me, but I didn't pay a lot of attention to the structural flow changes. |
<value>The remote certificate is invalid according to the validation procedure: {0}</value> | ||
</data> | ||
<data name="net_ssl_io_cert_custom_validation" xml:space="preserve"> | ||
<value>The remote certificate was rejected by RemoteCertificateValidationCallback.</value> |
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.
Nit re wording: The RemoteCertificateValidationCallback is provided by the user, right? Can we change this to something like "The remote certificate was rejected by the provided RemoteCertificateValidationCallback" or similar, so it's clearer what's going on here?
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.
yes, this is user provided. Updated as suggested.
_sslAuthenticationOptions.CheckCertName, | ||
_sslAuthenticationOptions.IsServer, | ||
_sslAuthenticationOptions.TargetHost); | ||
} | ||
|
||
if (remoteCertValidationCallback != null) | ||
{ | ||
success = remoteCertValidationCallback(_sslAuthenticationOptions.TargetHost, remoteCertificateEx, chain, sslPolicyErrors); | ||
object? sender = _ssl; |
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.
What's this check needed for?
Also, it seems like the only place you use _ssl
is for this check. If you're just using it to determine whether the SecureChannel is disposed, could you just use bool _disposed
instead?
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 was worried about race conditions. Since there is no lock, grabbing global value and testing it seemed like safest.
If I use _disposed, it can be modified between check and the passing it to the callback, right?
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.
Ah, I see, you're passing it to the validation callback below. Ok, makes sense.
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.
Here's my suggestion: Don't store the SslStream in _ssl
. Just pass it into VerifyRemoteCertificate
-- that seems to be the only place it's used.
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 was thinking about it but I was not sure if it is OK to pass null
to that callback. I think we would not do that in the past.
It would certainly simplify this.
In the past SecureChannel did not have any real clue about SslStream - and that make tracing more difficult as we deal with objects caller has no visibility to. This wrapper business is yet another case. (I did not eliminated all the cases - just the one I cared about for the remote certificate as that is most common people hit)
I was thinking about merging them to one - but that is bigger change that we should do right now. (and it may have other issues)
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.
If you pass SslStream to this routine, then why would it be null? I'm not sure exactly where this is called from, but I guess I assumed it was from SslStream?
I was thinking about merging them to one - but that is bigger change that we should do right now. (and it may have other issues)
Yep, merging seems good, but not right now.
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.
it is called from within SslStream. But either with sync or async, somebody can get tired of waiting and call Dispose from other thread and move one. Maybe I'm just paranoid here and if somebody does, possible NRE inside of the callback would be acceptable. Our code really does not care AFAIK.
I was also thinking about just leaving _ssl
on Dispose but I wanted to cut the circular reference.
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.
If you pass the SslStream into this function, then they shouldn't get NRE, though they could get ODE. But I think that's fine, and unavoidable anyway since it's a race.
src/libraries/System.Net.Security/src/System/Net/Security/SecureChannel.cs
Show resolved
Hide resolved
else if (sslPolicyErrors == SslPolicyErrors.RemoteCertificateChainErrors && chainStatus != X509ChainStatusFlags.NoError) | ||
{ | ||
// We failed only because of chain and we have some insight. | ||
SendAuthResetSignal(alertToken, ExceptionDispatchInfo.Capture(new AuthenticationException(SR.Format(SR.net_ssl_io_cert_validation, chainStatus), null))); |
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.
Should this have a different exception message than the case below? We go to the trouble of distinguishing them, so it seems like adding a bit more text to explain things is worthwhile.
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.
We could but the enums do not have overlapping values so it would be always unique. If you think it is worth of changing, could you suggest come wording?
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.
The existing message seems fine for the case below (sslPolicyErrors).
For the chain one, you could say something like "The remote certificate is invalid because of errors in the certificate chain: {0}" or something like that.
Up to you whether you think this is worth it.
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.
Generally looks good, a couple issues above.
I resolved feedback as well as added little bit to fix #40402. Since I'm touching relevant area, doing separate PR would cause conflicts. The crux of the reported issue is that the recently added callback only updated values (like certificates and crypto) but ignored delegates. That is generally ok since there is no delegate to select certificate but in case of client cert authentication, one may still want to verify that vis custom code. (added two new tests for cases when verification callback is set via option and via SslStream constructor) fixes #40402. |
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.
LGTM
…ils (dotnet#40110) * include more details in exception if remote certificate validation fails * fix unit test linking * feedback from review * update exception message
This includes enum value in Authentication exception if SslStream does not like remote certificate.
Relevant parts are in ForceAuthenticationAsync() when CompleteHandshake() fails.
I originally only used sslPolicyErrors but that is pretty crude enum with three values.
So I decided to also use X509ChainStatusFlags is available.
While working on this, I noticed that we always allocated verification delegate and copy certificate there.
With that, null checkin In SecureChannel really did not make sense.
The wrapper had extra
string?
parameter but it was never used.So I did little bit shuffling to avoid unnecessary allocations and hopefully simplify the logic.
fixes #2359