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 some EOF-handling issues in TLS #44563

Closed
wants to merge 2 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
31 changes: 12 additions & 19 deletions src/crypto/crypto_tls.cc
Expand Up @@ -677,11 +677,6 @@ void TLSWrap::OnStreamAfterWrite(WriteWrap* req_wrap, int status) {
EncOut();
}

int TLSWrap::GetSSLError(int status) const {
// ssl_ might already be destroyed for reading EOF from a close notify alert.
return ssl_ != nullptr ? SSL_get_error(ssl_.get(), status) : 0;
}

void TLSWrap::ClearOut() {
Debug(this, "Trying to read cleartext output");
// Ignore cycling data if ClientHello wasn't yet parsed
Expand Down Expand Up @@ -735,25 +730,23 @@ void TLSWrap::ClearOut() {
}
}

int flags = SSL_get_shutdown(ssl_.get());
if (!eof_ && flags & SSL_RECEIVED_SHUTDOWN) {
eof_ = true;
EmitRead(UV_EOF);
}

// We need to check whether an error occurred or the connection was
// shutdown cleanly (SSL_ERROR_ZERO_RETURN) even when read == 0.
// See node#1642 and SSL_read(3SSL) for details.
// See node#1642 and SSL_read(3SSL) for details. SSL_get_error must be
// called immediately after SSL_read, without calling into JS, which may
// change OpenSSL's error queue, modify ssl_, or even destroy ssl_
// altogether.
if (read <= 0) {
HandleScope handle_scope(env()->isolate());
Local<Value> error;
int err = GetSSLError(read);
int err = SSL_get_error(ssl_.get(), read);
switch (err) {
case SSL_ERROR_ZERO_RETURN:
// Ignore ZERO_RETURN after EOF, it is basically not an error.
if (eof_) return;
error = env()->zero_return_string();
break;
if (!eof_) {
eof_ = true;
EmitRead(UV_EOF);
}
return;

case SSL_ERROR_SSL:
case SSL_ERROR_SYSCALL:
Expand Down Expand Up @@ -838,7 +831,7 @@ void TLSWrap::ClearIn() {
}

// Error or partial write
int err = GetSSLError(written);
int err = SSL_get_error(ssl_.get(), written);
if (err == SSL_ERROR_SSL || err == SSL_ERROR_SYSCALL) {
Debug(this, "Got SSL error (%d)", err);
write_callback_scheduled_ = true;
Expand Down Expand Up @@ -1014,7 +1007,7 @@ int TLSWrap::DoWrite(WriteWrap* w,

if (written == -1) {
// If we stopped writing because of an error, it's fatal, discard the data.
int err = GetSSLError(written);
int err = SSL_get_error(ssl_.get(), written);
if (err == SSL_ERROR_SSL || err == SSL_ERROR_SYSCALL) {
// TODO(@jasnell): What are we doing with the error?
Debug(this, "Got SSL error (%d), returning UV_EPROTO", err);
Expand Down
2 changes: 0 additions & 2 deletions src/crypto/crypto_tls.h
Expand Up @@ -166,8 +166,6 @@ class TLSWrap : public AsyncWrap,

int SetCACerts(SecureContext* sc);

int GetSSLError(int status) const;

static int SelectSNIContextCallback(SSL* s, int* ad, void* arg);

static void CertCbDone(const v8::FunctionCallbackInfo<v8::Value>& args);
Expand Down
3 changes: 1 addition & 2 deletions src/env_properties.h
Expand Up @@ -321,8 +321,7 @@
V(writable_string, "writable") \
V(write_host_object_string, "_writeHostObject") \
V(write_queue_size_string, "writeQueueSize") \
V(x_forwarded_string, "x-forwarded-for") \
V(zero_return_string, "ZERO_RETURN")
V(x_forwarded_string, "x-forwarded-for")

#define PER_ISOLATE_TEMPLATE_PROPERTIES(V) \
V(async_wrap_ctor_template, v8::FunctionTemplate) \
Expand Down
3 changes: 2 additions & 1 deletion test/parallel/test-tls-client-auth.js
Expand Up @@ -79,7 +79,8 @@ connect({
}, function(err, pair, cleanup) {
assert.strictEqual(pair.server.err.code,
'ERR_SSL_PEER_DID_NOT_RETURN_A_CERTIFICATE');
assert.strictEqual(pair.client.err.code, 'ECONNRESET');
assert.strictEqual(pair.client.err.code,
'ERR_SSL_SSLV3_ALERT_HANDSHAKE_FAILURE');
return cleanup();
});

Expand Down
2 changes: 1 addition & 1 deletion test/parallel/test-tls-empty-sni-context.js
Expand Up @@ -26,6 +26,6 @@ const server = tls.createServer(options, (c) => {
}, common.mustNotCall());

c.on('error', common.mustCall((err) => {
assert.match(err.message, /Client network socket disconnected/);
assert.strictEqual(err.code, 'ERR_SSL_SSLV3_ALERT_HANDSHAKE_FAILURE');
}));
}));
43 changes: 28 additions & 15 deletions test/parallel/test-tls-min-max-version.js
Expand Up @@ -120,23 +120,27 @@ test(U, U, 'TLS_method', U, U, 'TLSv1_2_method', 'TLSv1.2');
test(U, U, 'TLS_method', U, U, 'TLSv1_1_method', 'TLSv1.1');
test(U, U, 'TLS_method', U, U, 'TLSv1_method', 'TLSv1');

// OpenSSL 1.1.1 and 3.0 use a different error code and alert (sent to the
// client) when no protocols are enabled on the server.
const NO_PROTOCOLS_AVAILABLE_SERVER = common.hasOpenSSL3 ?
'ERR_SSL_NO_PROTOCOLS_AVAILABLE' : 'ERR_SSL_INTERNAL_ERROR';
const NO_PROTOCOLS_AVAILABLE_SERVER_ALERT = common.hasOpenSSL3 ?
'ERR_SSL_TLSV1_ALERT_PROTOCOL_VERSION' : 'ERR_SSL_TLSV1_ALERT_INTERNAL_ERROR';

// SSLv23 also means "any supported protocol" greater than the default
// minimum (which is configurable via command line).
if (DEFAULT_MIN_VERSION === 'TLSv1.3') {
test(U, U, 'TLSv1_2_method', U, U, 'SSLv23_method',
U, 'ECONNRESET', common.hasOpenSSL3 ?
'ERR_SSL_NO_PROTOCOLS_AVAILABLE' : 'ERR_SSL_INTERNAL_ERROR');
U, NO_PROTOCOLS_AVAILABLE_SERVER_ALERT, NO_PROTOCOLS_AVAILABLE_SERVER);
} else {
test(U, U, 'TLSv1_2_method', U, U, 'SSLv23_method', 'TLSv1.2');
}

if (DEFAULT_MIN_VERSION === 'TLSv1.3') {
test(U, U, 'TLSv1_1_method', U, U, 'SSLv23_method',
U, 'ECONNRESET', common.hasOpenSSL3 ?
'ERR_SSL_NO_PROTOCOLS_AVAILABLE' : 'ERR_SSL_INTERNAL_ERROR');
U, NO_PROTOCOLS_AVAILABLE_SERVER_ALERT, NO_PROTOCOLS_AVAILABLE_SERVER);
test(U, U, 'TLSv1_method', U, U, 'SSLv23_method',
U, 'ECONNRESET', common.hasOpenSSL3 ?
'ERR_SSL_NO_PROTOCOLS_AVAILABLE' : 'ERR_SSL_INTERNAL_ERROR');
U, NO_PROTOCOLS_AVAILABLE_SERVER_ALERT, NO_PROTOCOLS_AVAILABLE_SERVER);
test(U, U, 'SSLv23_method', U, U, 'TLSv1_1_method',
U, 'ERR_SSL_NO_PROTOCOLS_AVAILABLE', 'ERR_SSL_UNEXPECTED_MESSAGE');
test(U, U, 'SSLv23_method', U, U, 'TLSv1_method',
Expand All @@ -145,9 +149,11 @@ if (DEFAULT_MIN_VERSION === 'TLSv1.3') {

if (DEFAULT_MIN_VERSION === 'TLSv1.2') {
test(U, U, 'TLSv1_1_method', U, U, 'SSLv23_method',
U, 'ECONNRESET', 'ERR_SSL_UNSUPPORTED_PROTOCOL');
U, 'ERR_SSL_TLSV1_ALERT_PROTOCOL_VERSION',
'ERR_SSL_UNSUPPORTED_PROTOCOL');
test(U, U, 'TLSv1_method', U, U, 'SSLv23_method',
U, 'ECONNRESET', 'ERR_SSL_UNSUPPORTED_PROTOCOL');
U, 'ERR_SSL_TLSV1_ALERT_PROTOCOL_VERSION',
'ERR_SSL_UNSUPPORTED_PROTOCOL');
test(U, U, 'SSLv23_method', U, U, 'TLSv1_1_method',
U, 'ERR_SSL_UNSUPPORTED_PROTOCOL', 'ERR_SSL_WRONG_VERSION_NUMBER');
test(U, U, 'SSLv23_method', U, U, 'TLSv1_method',
Expand All @@ -157,7 +163,8 @@ if (DEFAULT_MIN_VERSION === 'TLSv1.2') {
if (DEFAULT_MIN_VERSION === 'TLSv1.1') {
test(U, U, 'TLSv1_1_method', U, U, 'SSLv23_method', 'TLSv1.1');
test(U, U, 'TLSv1_method', U, U, 'SSLv23_method',
U, 'ECONNRESET', 'ERR_SSL_UNSUPPORTED_PROTOCOL');
U, 'ERR_SSL_TLSV1_ALERT_PROTOCOL_VERSION',
'ERR_SSL_UNSUPPORTED_PROTOCOL');
test(U, U, 'SSLv23_method', U, U, 'TLSv1_1_method', 'TLSv1.1');
test(U, U, 'SSLv23_method', U, U, 'TLSv1_method',
U, 'ERR_SSL_UNSUPPORTED_PROTOCOL', 'ERR_SSL_WRONG_VERSION_NUMBER');
Expand All @@ -179,9 +186,11 @@ test(U, U, 'TLSv1_method', U, U, 'TLSv1_method', 'TLSv1');
// The default default.
if (DEFAULT_MIN_VERSION === 'TLSv1.2') {
test(U, U, 'TLSv1_1_method', U, U, U,
U, 'ECONNRESET', 'ERR_SSL_UNSUPPORTED_PROTOCOL');
U, 'ERR_SSL_TLSV1_ALERT_PROTOCOL_VERSION',
'ERR_SSL_UNSUPPORTED_PROTOCOL');
test(U, U, 'TLSv1_method', U, U, U,
U, 'ECONNRESET', 'ERR_SSL_UNSUPPORTED_PROTOCOL');
U, 'ERR_SSL_TLSV1_ALERT_PROTOCOL_VERSION',
'ERR_SSL_UNSUPPORTED_PROTOCOL');

if (DEFAULT_MAX_VERSION === 'TLSv1.2') {
test(U, U, U, U, U, 'TLSv1_1_method',
Expand All @@ -191,17 +200,20 @@ if (DEFAULT_MIN_VERSION === 'TLSv1.2') {
} else {
// TLS1.3 client hellos are are not understood by TLS1.1 or below.
test(U, U, U, U, U, 'TLSv1_1_method',
U, 'ECONNRESET', 'ERR_SSL_UNSUPPORTED_PROTOCOL');
U, 'ERR_SSL_TLSV1_ALERT_PROTOCOL_VERSION',
'ERR_SSL_UNSUPPORTED_PROTOCOL');
test(U, U, U, U, U, 'TLSv1_method',
U, 'ECONNRESET', 'ERR_SSL_UNSUPPORTED_PROTOCOL');
U, 'ERR_SSL_TLSV1_ALERT_PROTOCOL_VERSION',
'ERR_SSL_UNSUPPORTED_PROTOCOL');
}
}

// The default with --tls-v1.1.
if (DEFAULT_MIN_VERSION === 'TLSv1.1') {
test(U, U, 'TLSv1_1_method', U, U, U, 'TLSv1.1');
test(U, U, 'TLSv1_method', U, U, U,
U, 'ECONNRESET', 'ERR_SSL_UNSUPPORTED_PROTOCOL');
U, 'ERR_SSL_TLSV1_ALERT_PROTOCOL_VERSION',
'ERR_SSL_UNSUPPORTED_PROTOCOL');
test(U, U, U, U, U, 'TLSv1_1_method', 'TLSv1.1');

if (DEFAULT_MAX_VERSION === 'TLSv1.2') {
Expand All @@ -210,7 +222,8 @@ if (DEFAULT_MIN_VERSION === 'TLSv1.1') {
} else {
// TLS1.3 client hellos are are not understood by TLS1.1 or below.
test(U, U, U, U, U, 'TLSv1_method',
U, 'ECONNRESET', 'ERR_SSL_UNSUPPORTED_PROTOCOL');
U, 'ERR_SSL_TLSV1_ALERT_PROTOCOL_VERSION',
'ERR_SSL_UNSUPPORTED_PROTOCOL');
}
}

Expand Down
12 changes: 5 additions & 7 deletions test/parallel/test-tls-psk-circuit.js
Expand Up @@ -49,24 +49,22 @@ function test(secret, opts, error) {
} else {
const client = tls.connect(options, common.mustNotCall());
client.on('error', common.mustCall((err) => {
assert.strictEqual(err.message, error);
assert.strictEqual(err.code, error);
server.close();
}));
}
}));
}

const DISCONNECT_MESSAGE =
'Client network socket disconnected before ' +
'secure TLS connection was established';

test({ psk: USERS.UserA, identity: 'UserA' });
test({ psk: USERS.UserA, identity: 'UserA' }, { maxVersion: 'TLSv1.2' });
test({ psk: USERS.UserA, identity: 'UserA' }, { minVersion: 'TLSv1.3' });
test({ psk: USERS.UserB, identity: 'UserB' });
test({ psk: USERS.UserB, identity: 'UserB' }, { minVersion: 'TLSv1.3' });
// Unrecognized user should fail handshake
test({ psk: USERS.UserB, identity: 'UserC' }, {}, DISCONNECT_MESSAGE);
test({ psk: USERS.UserB, identity: 'UserC' }, {},
'ERR_SSL_SSLV3_ALERT_HANDSHAKE_FAILURE');
// Recognized user but incorrect secret should fail handshake
test({ psk: USERS.UserA, identity: 'UserB' }, {}, DISCONNECT_MESSAGE);
test({ psk: USERS.UserA, identity: 'UserB' }, {},
'ERR_SSL_SSLV3_ALERT_ILLEGAL_PARAMETER');
test({ psk: USERS.UserB, identity: 'UserB' });
9 changes: 5 additions & 4 deletions test/parallel/test-tls-set-ciphers.js
Expand Up @@ -88,12 +88,13 @@ test('TLS_AES_256_GCM_SHA384', U, 'TLS_AES_256_GCM_SHA384');

// Do not have shared ciphers.
test('TLS_AES_256_GCM_SHA384', 'TLS_CHACHA20_POLY1305_SHA256',
U, 'ECONNRESET', 'ERR_SSL_NO_SHARED_CIPHER');
U, 'ERR_SSL_SSLV3_ALERT_HANDSHAKE_FAILURE', 'ERR_SSL_NO_SHARED_CIPHER');

test('AES128-SHA', 'AES256-SHA', U, 'ECONNRESET', 'ERR_SSL_NO_SHARED_CIPHER');
test('AES128-SHA', 'AES256-SHA', U, 'ERR_SSL_SSLV3_ALERT_HANDSHAKE_FAILURE',
'ERR_SSL_NO_SHARED_CIPHER');
test('AES128-SHA:TLS_AES_256_GCM_SHA384',
'TLS_CHACHA20_POLY1305_SHA256:AES256-SHA',
U, 'ECONNRESET', 'ERR_SSL_NO_SHARED_CIPHER');
U, 'ERR_SSL_SSLV3_ALERT_HANDSHAKE_FAILURE', 'ERR_SSL_NO_SHARED_CIPHER');

// Cipher order ignored, TLS1.3 chosen before TLS1.2.
test('AES256-SHA:TLS_AES_256_GCM_SHA384', U, 'TLS_AES_256_GCM_SHA384');
Expand All @@ -109,7 +110,7 @@ test(U, 'AES256-SHA', 'TLS_AES_256_GCM_SHA384', U, U, { maxVersion: 'TLSv1.3' })
// TLS_AES_128_CCM_8_SHA256 & TLS_AES_128_CCM_SHA256 are not enabled by
// default, but work.
test('TLS_AES_128_CCM_8_SHA256', U,
U, 'ECONNRESET', 'ERR_SSL_NO_SHARED_CIPHER');
U, 'ERR_SSL_SSLV3_ALERT_HANDSHAKE_FAILURE', 'ERR_SSL_NO_SHARED_CIPHER');

test('TLS_AES_128_CCM_8_SHA256', 'TLS_AES_128_CCM_8_SHA256',
'TLS_AES_128_CCM_8_SHA256');
Expand Down