Skip to content
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

Fix to handle tls error properly #1661

Closed
wants to merge 2 commits into from
Closed

Conversation

shigeki
Copy link
Contributor

@shigeki shigeki commented May 8, 2015

This is a fix of the error handling of tls and tls_wrap. Especially it fixes the error handling when the server receiving a fatal TLS alert just after TLS handshake completed. Testing is very hard to implement within node and openssl s_client so that we take this fix to resolve the issue of #1642 from the comments from reporters.

CI results are https://jenkins-iojs.nodesource.com/job/iojs+any-pr+multi/655/ where the test failures on Windows are related to child process not related this.

Fix: #1642

R= @indutny

In ssl.onerror event, `this` refers `ssl` so that
`this._secureEstablished` is always undefined. Fix it to refer
TLSSocket.
if (read == -1) {
// We need to check whether an error occurred or the connection was
// shutdown cleanly (SSL_ERROR_ZERO_RETURN) even when read == 0
// see iojs#1642 and SSL_read(3SSL) for details.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Femto-style nit: can you capitalize 'see' and insert a link to the issue?

@bnoordhuis
Copy link
Member

LGTM with suggestions. A regression test would be really nice though.

@brendanashworth brendanashworth added the tls Issues and PRs related to the tls subsystem. label May 8, 2015
@indutny
Copy link
Member

indutny commented May 8, 2015

Oh, right. How did I forget about it in a first place. @shigeki please add a test indeed.

@shigeki
Copy link
Contributor Author

shigeki commented May 9, 2015

I've just update the comments.

I tried to implement a test but it is very hard to simulate Firefox for sending encrypted TLS alert just after handshake completed. Could you give me any idea?

@indutny
Copy link
Member

indutny commented May 9, 2015

I guess you could use the raw socket to send the TLS alert frame (in a Buffer). And for ciphers you could try using 'NULL-SHA256' to make sure that you can send the non-encrypted alert.

The frame could be generated either by hand or with https://github.com/indutny/tls.js . Please let me know if you need any help with any of these.

@shigeki
Copy link
Contributor Author

shigeki commented May 9, 2015

Using NULL-SHA256! I never think of that. Okay I try it.

SSL_read() returns 0 when fatal TLS Alert is received.
Fix to invoke ssl error callback in this case.
@shigeki
Copy link
Contributor Author

shigeki commented May 12, 2015

@indutny Using NULL-SHA256 needs mac_write_key so that we have to obtain master_secret and client/server random. I think it is not the best way to implement TLS client in the test.
I found that this issue can also be reproduced by sending a bad TLS record from a client to a server. Both endpoints send TLS alerts and the server close the normal connection. It is more simple way to test this fix and added its test to this PR. PTAL.

@indutny
Copy link
Member

indutny commented May 16, 2015

LGTM, landing.

shigeki pushed a commit that referenced this pull request May 16, 2015
In ssl.onerror event, `this` refers `ssl` so that
`this._secureEstablished` is always undefined. Fix it to refer
TLSSocket.

PR-URL: #1661
Reviewed-By: Fedor Indutny <fedor@indutny.com>
shigeki pushed a commit that referenced this pull request May 16, 2015
SSL_read() returns 0 when fatal TLS Alert is received.
Fix to invoke ssl error callback in this case.

PR-URL: #1661
Reviewed-By: Fedor Indutny <fedor@indutny.com>
@indutny
Copy link
Member

indutny commented May 16, 2015

Landed in e008e8f and 7c52e1c

@indutny indutny closed this May 16, 2015
@indutny
Copy link
Member

indutny commented May 16, 2015

Thank you, @shigeki

Fishrock123 pushed a commit to Fishrock123/node that referenced this pull request May 19, 2015
In ssl.onerror event, `this` refers `ssl` so that
`this._secureEstablished` is always undefined. Fix it to refer
TLSSocket.

PR-URL: nodejs#1661
Reviewed-By: Fedor Indutny <fedor@indutny.com>
Fishrock123 pushed a commit to Fishrock123/node that referenced this pull request May 19, 2015
SSL_read() returns 0 when fatal TLS Alert is received.
Fix to invoke ssl error callback in this case.

PR-URL: nodejs#1661
Reviewed-By: Fedor Indutny <fedor@indutny.com>
andrewdeandrade pushed a commit to andrewdeandrade/node that referenced this pull request Jun 3, 2015
In ssl.onerror event, `this` refers `ssl` so that
`this._secureEstablished` is always undefined. Fix it to refer
TLSSocket.

PR-URL: nodejs/node#1661
Reviewed-By: Fedor Indutny <fedor@indutny.com>
andrewdeandrade pushed a commit to andrewdeandrade/node that referenced this pull request Jun 3, 2015
SSL_read() returns 0 when fatal TLS Alert is received.
Fix to invoke ssl error callback in this case.

PR-URL: nodejs/node#1661
Reviewed-By: Fedor Indutny <fedor@indutny.com>
davidben added a commit to davidben/node that referenced this pull request Sep 7, 2022
OpenSSL's API requires SSL_get_error be called immediately after the
failing operation, otherwise the SSL object may have changed state and
no longer report information about the failing error.

TLSWrap almost heeds this rule, except in TLSWrap::ClearOut. If SSL_read
picks up a close_notify (detected by checking SSL_get_shutdown), Node
calls out to JS with EmitRead(UV_EOF) and only afterwards proceeds to
dispatch on the error. But, by this point, Node has already re-entered
JS and indeed JS seems to sometimes call back into TLSWrap::DoShutdown,
calling SSL_shutdown. (I think this comes from onStreamRead in
stream_base_commons.js?)

Instead, SSL_get_error and the error queue should be sampled earlier.
This avoids the issue worked around by nodejs#1661, where GetSSLError needed
to check if ssl_ was destroyed before calling SSL_get_error. We can now
remove that wrapper and just call SSL_get_error directly. (Any case
where ssl_ may be destroyed first is a case where ssl_ may change state,
so it's a bug either way.)

This is the first of two fixes in error-handling here. The
EmitRead(UV_EOF) seems to additionally swallow fatal alerts from the
peer. Some of the ECONNRESET expectations in the tests aren't actually
correct. The next commit will fix this as well.
davidben added a commit to davidben/node that referenced this pull request Sep 7, 2022
Like errno, OpenSSL's API requires SSL_get_error and error queue be
checked immediately after the failing operation, otherwise the error
queue or SSL object may have changed state and no longer report
information about the operation the caller wanted.

TLSWrap almost heeds this rule, except in TLSWrap::ClearOut. If SSL_read
picks up a closing alert (detected by checking SSL_get_shutdown), Node
calls out to JS with EmitRead(UV_EOF) and only afterwards proceeds to
dispatch on the error. But, by this point, Node has already re-entered
JS, which may change the error.

In particular, I've observed that, on close_notify, JS seems to
sometimes call back into TLSWrap::DoShutdown, calling SSL_shutdown. (I
think this comes from onStreamRead in stream_base_commons.js?)

Instead, SSL_get_error and the error queue should be sampled earlier.
Back in nodejs#1661, Node needed to account for GetSSLError being called after
ssl_ was destroyed. This was the real cause. With this fixed, there's no
need to account for this. (Any case where ssl_ may be destroyed before
SSL_get_error is a case where ssl_ or the error queue could change
state, so it's a bug either way.)

This is the first of two fixes in error-handling here. The
EmitRead(UV_EOF) seems to additionally swallow fatal alerts from the
peer. Some of the ECONNRESET expectations in the tests aren't actually
correct. The next commit will fix this as well.
nodejs-github-bot pushed a commit that referenced this pull request Dec 12, 2022
Like errno, OpenSSL's API requires SSL_get_error and error queue be
checked immediately after the failing operation, otherwise the error
queue or SSL object may have changed state and no longer report
information about the operation the caller wanted.

TLSWrap almost heeds this rule, except in TLSWrap::ClearOut. If SSL_read
picks up a closing alert (detected by checking SSL_get_shutdown), Node
calls out to JS with EmitRead(UV_EOF) and only afterwards proceeds to
dispatch on the error. But, by this point, Node has already re-entered
JS, which may change the error.

In particular, I've observed that, on close_notify, JS seems to
sometimes call back into TLSWrap::DoShutdown, calling SSL_shutdown. (I
think this comes from onStreamRead in stream_base_commons.js?)

Instead, SSL_get_error and the error queue should be sampled earlier.
Back in #1661, Node needed to account for GetSSLError being called after
ssl_ was destroyed. This was the real cause. With this fixed, there's no
need to account for this. (Any case where ssl_ may be destroyed before
SSL_get_error is a case where ssl_ or the error queue could change
state, so it's a bug either way.)

This is the first of two fixes in error-handling here. The
EmitRead(UV_EOF) seems to additionally swallow fatal alerts from the
peer. Some of the ECONNRESET expectations in the tests aren't actually
correct. The next commit will fix this as well.

PR-URL: #44563
Reviewed-By: Luigi Pinca <luigipinca@gmail.com>
targos pushed a commit that referenced this pull request Jan 1, 2023
Like errno, OpenSSL's API requires SSL_get_error and error queue be
checked immediately after the failing operation, otherwise the error
queue or SSL object may have changed state and no longer report
information about the operation the caller wanted.

TLSWrap almost heeds this rule, except in TLSWrap::ClearOut. If SSL_read
picks up a closing alert (detected by checking SSL_get_shutdown), Node
calls out to JS with EmitRead(UV_EOF) and only afterwards proceeds to
dispatch on the error. But, by this point, Node has already re-entered
JS, which may change the error.

In particular, I've observed that, on close_notify, JS seems to
sometimes call back into TLSWrap::DoShutdown, calling SSL_shutdown. (I
think this comes from onStreamRead in stream_base_commons.js?)

Instead, SSL_get_error and the error queue should be sampled earlier.
Back in #1661, Node needed to account for GetSSLError being called after
ssl_ was destroyed. This was the real cause. With this fixed, there's no
need to account for this. (Any case where ssl_ may be destroyed before
SSL_get_error is a case where ssl_ or the error queue could change
state, so it's a bug either way.)

This is the first of two fixes in error-handling here. The
EmitRead(UV_EOF) seems to additionally swallow fatal alerts from the
peer. Some of the ECONNRESET expectations in the tests aren't actually
correct. The next commit will fix this as well.

PR-URL: #44563
Reviewed-By: Luigi Pinca <luigipinca@gmail.com>
juanarbol pushed a commit that referenced this pull request Jan 26, 2023
Like errno, OpenSSL's API requires SSL_get_error and error queue be
checked immediately after the failing operation, otherwise the error
queue or SSL object may have changed state and no longer report
information about the operation the caller wanted.

TLSWrap almost heeds this rule, except in TLSWrap::ClearOut. If SSL_read
picks up a closing alert (detected by checking SSL_get_shutdown), Node
calls out to JS with EmitRead(UV_EOF) and only afterwards proceeds to
dispatch on the error. But, by this point, Node has already re-entered
JS, which may change the error.

In particular, I've observed that, on close_notify, JS seems to
sometimes call back into TLSWrap::DoShutdown, calling SSL_shutdown. (I
think this comes from onStreamRead in stream_base_commons.js?)

Instead, SSL_get_error and the error queue should be sampled earlier.
Back in #1661, Node needed to account for GetSSLError being called after
ssl_ was destroyed. This was the real cause. With this fixed, there's no
need to account for this. (Any case where ssl_ may be destroyed before
SSL_get_error is a case where ssl_ or the error queue could change
state, so it's a bug either way.)

This is the first of two fixes in error-handling here. The
EmitRead(UV_EOF) seems to additionally swallow fatal alerts from the
peer. Some of the ECONNRESET expectations in the tests aren't actually
correct. The next commit will fix this as well.

PR-URL: #44563
Reviewed-By: Luigi Pinca <luigipinca@gmail.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
tls Issues and PRs related to the tls subsystem.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

clientError emitted for wrong socket - SSL alert number 48
4 participants