From 0c6e52352b5f91960b33584ca9d3068266bee68b Mon Sep 17 00:00:00 2001 From: Brian White Date: Sun, 31 Jan 2016 01:26:41 -0500 Subject: [PATCH 1/2] tls: add getProtocol() to TLS sockets This commit adds a new method for TLS sockets that returns the negotiated protocol version. --- doc/api/tls.markdown | 18 +++++++++++ lib/_tls_wrap.js | 7 +++++ src/node_crypto.cc | 10 +++++++ src/node_crypto.h | 1 + test/parallel/test-tls-getprotocol.js | 43 +++++++++++++++++++++++++++ 5 files changed, 79 insertions(+) create mode 100644 test/parallel/test-tls-getprotocol.js diff --git a/doc/api/tls.markdown b/doc/api/tls.markdown index 1de7593d18c673..a3502339a306b5 100644 --- a/doc/api/tls.markdown +++ b/doc/api/tls.markdown @@ -493,6 +493,24 @@ Example: If the peer does not provide a certificate, it returns `null` or an empty object. +### tlsSocket.getProtocol() + +Returns a string containing the negotiated SSL/TLS protocol version of the +current connection. `'unknown'` will be returned for connected sockets that have +not completed the handshaking process. `null` will be returned for server +sockets or disconnected client sockets. + +Examples: +``` +'SSLv3' +'TLSv1' +'TLSv1.1' +'TLSv1.2' +'unknown' +``` + +See https://www.openssl.org/docs/manmaster/ssl/SSL_get_version.html for more +information. ### tlsSocket.getSession() diff --git a/lib/_tls_wrap.js b/lib/_tls_wrap.js index dd293121cbc782..a888dc1554a495 100644 --- a/lib/_tls_wrap.js +++ b/lib/_tls_wrap.js @@ -649,6 +649,13 @@ TLSSocket.prototype.getEphemeralKeyInfo = function() { return null; }; +TLSSocket.prototype.getProtocol = function() { + if (this._handle) + return this._handle.getProtocol(); + + return null; +}; + // TODO: support anonymous (nocert) and PSK diff --git a/src/node_crypto.cc b/src/node_crypto.cc index 6cb79aa4b978bd..315637215c25d4 100644 --- a/src/node_crypto.cc +++ b/src/node_crypto.cc @@ -1195,6 +1195,7 @@ void SSLWrap::AddMethods(Environment* env, Local t) { env->SetProtoMethod(t, "setOCSPResponse", SetOCSPResponse); env->SetProtoMethod(t, "requestOCSP", RequestOCSP); env->SetProtoMethod(t, "getEphemeralKeyInfo", GetEphemeralKeyInfo); + env->SetProtoMethod(t, "getProtocol", GetProtocol); #ifdef SSL_set_max_send_fragment env->SetProtoMethod(t, "setMaxSendFragment", SetMaxSendFragment); @@ -1957,6 +1958,15 @@ void SSLWrap::GetCurrentCipher(const FunctionCallbackInfo& args) { } +template +void SSLWrap::GetProtocol(const FunctionCallbackInfo& args) { + Base* w = Unwrap(args.Holder()); + + const char* tls_version = SSL_get_version(w->ssl_); + args.GetReturnValue().Set(OneByteString(args.GetIsolate(), tls_version)); +} + + #ifdef OPENSSL_NPN_NEGOTIATED template int SSLWrap::AdvertiseNextProtoCallback(SSL* s, diff --git a/src/node_crypto.h b/src/node_crypto.h index aaadc904dd3980..8a6c66703720b0 100644 --- a/src/node_crypto.h +++ b/src/node_crypto.h @@ -248,6 +248,7 @@ class SSLWrap { static void RequestOCSP(const v8::FunctionCallbackInfo& args); static void GetEphemeralKeyInfo( const v8::FunctionCallbackInfo& args); + static void GetProtocol(const v8::FunctionCallbackInfo& args); #ifdef SSL_set_max_send_fragment static void SetMaxSendFragment( diff --git a/test/parallel/test-tls-getprotocol.js b/test/parallel/test-tls-getprotocol.js new file mode 100644 index 00000000000000..67592143ee4875 --- /dev/null +++ b/test/parallel/test-tls-getprotocol.js @@ -0,0 +1,43 @@ +'use strict'; +const common = require('../common'); +const assert = require('assert'); + +if (!common.hasCrypto) { + console.log('1..0 # Skipped: missing crypto'); + return; +} + +const tls = require('tls'); +const fs = require('fs'); + +const clientConfigs = [ + { secureProtocol: 'TLSv1_method', version: 'TLSv1' }, + { secureProtocol: 'TLSv1_1_method', version: 'TLSv1.1' }, + { secureProtocol: 'TLSv1_2_method', version: 'TLSv1.2' } +]; + +const serverConfig = { + key: fs.readFileSync(common.fixturesDir + '/keys/agent2-key.pem'), + cert: fs.readFileSync(common.fixturesDir + '/keys/agent2-cert.pem') +}; + +const server = tls.createServer(serverConfig, common.mustCall(function() { + +}, clientConfigs.length)).listen(common.PORT, common.localhostIPv4, function() { + let connected = 0; + clientConfigs.forEach(function(v) { + tls.connect({ + host: common.localhostIPv4, + port: common.PORT, + rejectUnauthorized: false, + secureProtocol: v.secureProtocol + }, common.mustCall(function() { + assert.strictEqual(this.getProtocol(), v.version); + this.on('end', common.mustCall(function() { + assert.strictEqual(this.getProtocol(), null); + })).end(); + if (++connected === clientConfigs.length) + server.close(); + })); + }); +}); From 94f9b2cbf9367a25f6140c38c72d8ab30a3fcd74 Mon Sep 17 00:00:00 2001 From: Brian White Date: Sun, 31 Jan 2016 01:28:41 -0500 Subject: [PATCH 2/2] doc: correct tlsSocket.getCipher() description getCipher() actually includes the protocol version that the cipher was first supported and *not* the negotiated protocol of the current connection. --- doc/api/tls.markdown | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/doc/api/tls.markdown b/doc/api/tls.markdown index a3502339a306b5..63f562d659a446 100644 --- a/doc/api/tls.markdown +++ b/doc/api/tls.markdown @@ -434,14 +434,15 @@ Static boolean value, always `true`. May be used to distinguish TLS sockets from regular ones. ### tlsSocket.getCipher() -Returns an object representing the cipher name and the SSL/TLS -protocol version of the current connection. + +Returns an object representing the cipher name and the SSL/TLS protocol version +that first defined the cipher. Example: { name: 'AES256-SHA', version: 'TLSv1/SSLv3' } See SSL_CIPHER_get_name() and SSL_CIPHER_get_version() in -https://www.openssl.org/docs/ssl/ssl.html#DEALING_WITH_CIPHERS for more +https://www.openssl.org/docs/manmaster/ssl/SSL_CIPHER_get_name.html for more information. ### tlsSocket.getEphemeralKeyInfo()