diff --git a/package.json b/package.json index 9f960de5a..f356d0f27 100644 --- a/package.json +++ b/package.json @@ -44,6 +44,7 @@ "big-number": "0.3.1", "bl": "^1.2.0", "depd": "^1.1.2", + "duplexpair": "1.0.1", "iconv-lite": "^0.4.11", "readable-stream": "^2.2.6", "sprintf-js": "^1.1.1", diff --git a/src/message-io.js b/src/message-io.js index 6c2a15c32..b04e13f0f 100644 --- a/src/message-io.js +++ b/src/message-io.js @@ -1,5 +1,6 @@ const tls = require('tls'); const crypto = require('crypto'); +const DuplexPair = require('duplexpair'); const EventEmitter = require('events').EventEmitter; const Transform = require('readable-stream').Transform; @@ -7,6 +8,8 @@ const Packet = require('./packet').Packet; const TYPE = require('./packet').TYPE; const packetHeaderLength = require('./packet').HEADER_LENGTH; +const USE_LEGACY_SECUREPAIR = Number(process.versions.node[0]) <= 7; + class ReadablePacketStream extends Transform { constructor() { super({ objectMode: true }); @@ -84,25 +87,14 @@ module.exports = class MessageIO extends EventEmitter { startTls(credentialsDetails, hostname, trustServerCertificate) { const credentials = tls.createSecureContext ? tls.createSecureContext(credentialsDetails) : crypto.createCredentials(credentialsDetails); - this.securePair = tls.createSecurePair(credentials); + this.securePair = this.createSecurePair(credentials); this.tlsNegotiationComplete = false; - this.securePair.on('secure', () => { + this.getSecurePairSecureTarget().on('secure', () => { const cipher = this.securePair.cleartext.getCipher(); - if (!trustServerCertificate) { - let verifyError = this.securePair.ssl.verifyError(); - - // Verify that server's identity matches it's certificate's names - if (!verifyError) { - verifyError = tls.checkServerIdentity(hostname, this.securePair.cleartext.getPeerCertificate()); - } - - if (verifyError) { - this.securePair.destroy(); - this.socket.destroy(verifyError); - return; - } + if (!trustServerCertificate && !this.checkAuthorizationError(hostname)) { + return; } this.debug.log('TLS negotiated (' + cipher.name + ', ' + cipher.version + ')'); @@ -189,4 +181,48 @@ module.exports = class MessageIO extends EventEmitter { resume() { this.packetStream.resume(); } + + createSecurePair(credentials) { + if (USE_LEGACY_SECUREPAIR) { + return tls.createSecurePair(credentials); + } else { + const duplexpair = new DuplexPair(); + return { + cleartext: new tls.TLSSocket(duplexpair.socket1, { + secureContext: credentials, + rejectUnauthorized: false + }), + encrypted: duplexpair.socket2 + }; + } + } + + checkAuthorizationError(hostname) { + let verifyError = null; + if (USE_LEGACY_SECUREPAIR) { + verifyError = this.securePair.ssl.verifyError(); + // Verify that server's identity matches it's certificate's names + if (!verifyError) { + verifyError = tls.checkServerIdentity(hostname, this.securePair.cleartext.getPeerCertificate()); + } + if (verifyError) { + this.securePair.destroy(); + } + } else if (!this.securePair.cleartext.authorized) { + verifyError = this.securePair.cleartext.authorizationError; + } + if (verifyError) { + this.socket.destroy(verifyError); + return false; + } + return true; + } + + getSecurePairSecureTarget() { + if (USE_LEGACY_SECUREPAIR) { + return this.securePair; + } else { + return this.securePair.cleartext; + } + } }; diff --git a/test/integration/connection-retry-test.js b/test/integration/connection-retry-test.js index 860ab95f2..2d91f8a84 100644 --- a/test/integration/connection-retry-test.js +++ b/test/integration/connection-retry-test.js @@ -74,7 +74,7 @@ exports['connection retry tests'] = { const config = getConfig(); config.options.connectTimeout = config.options.connectionRetryInterval / 2; - const clock = this.sinon.useFakeTimers(); + const clock = this.sinon.useFakeTimers('setTimeout'); test.expect(1);