From 7315c2288a88b4844a1b18806374281b8237e652 Mon Sep 17 00:00:00 2001 From: Alba Mendez Date: Tue, 12 May 2020 14:53:12 +0200 Subject: [PATCH] tls: fix --tls-keylog option MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit There's a typo that causes only the first socket to be logged (i.e. when the warning is emitted). In addition, server sockets aren't logged because `keylog` events are not emitted on tls.Server, not the socket. This behaviour is counterintuitive and has caused more bugs in the past, so make all sockets (server or client) emit 'keylog'. tls.Server will just re-emit these events. Refs: https://github.com/nodejs/node/pull/30055 PR-URL: https://github.com/nodejs/node/pull/33366 Reviewed-By: Sam Roberts Reviewed-By: James M Snell Reviewed-By: Juan José Arboleda --- doc/api/tls.md | 2 +- lib/_tls_wrap.js | 69 ++++++++++----------- test/parallel/test-tls-enable-keylog-cli.js | 11 +++- 3 files changed, 41 insertions(+), 41 deletions(-) diff --git a/doc/api/tls.md b/doc/api/tls.md index 9528819b33c285..04f5e23d3902ae 100644 --- a/doc/api/tls.md +++ b/doc/api/tls.md @@ -697,7 +697,7 @@ added: v12.3.0 * `line` {Buffer} Line of ASCII text, in NSS `SSLKEYLOGFILE` format. -The `keylog` event is emitted on a client `tls.TLSSocket` when key material +The `keylog` event is emitted on a `tls.TLSSocket` when key material is generated or received by the socket. This keying material can be stored for debugging, as it allows captured TLS traffic to be decrypted. It may be emitted multiple times, before or after the handshake completes. diff --git a/lib/_tls_wrap.js b/lib/_tls_wrap.js index 7aa2c939aa1910..ff3deac0ae9f10 100644 --- a/lib/_tls_wrap.js +++ b/lib/_tls_wrap.js @@ -377,16 +377,9 @@ function onPskClientCallback(hint, maxPskLen, maxIdentityLen) { return { psk: ret.psk, identity: ret.identity }; } -function onkeylogclient(line) { - debug('client onkeylog'); - this[owner_symbol].emit('keylog', line); -} - function onkeylog(line) { - debug('server onkeylog'); - const owner = this[owner_symbol]; - if (owner.server) - owner.server.emit('keylog', line, owner); + debug('onkeylog'); + this[owner_symbol].emit('keylog', line); } function onocspresponse(resp) { @@ -678,13 +671,26 @@ TLSSocket.prototype._init = function(socket, wrap) { if (requestCert || rejectUnauthorized) ssl.setVerifyMode(requestCert, rejectUnauthorized); + // Only call .onkeylog if there is a keylog listener. + ssl.onkeylog = onkeylog; + this.on('newListener', keylogNewListener); + + function keylogNewListener(event) { + if (event !== 'keylog') + return; + + ssl.enableKeylogCallback(); + + // Remove this listener since it's no longer needed. + this.removeListener('newListener', keylogNewListener); + } + if (options.isServer) { ssl.onhandshakestart = onhandshakestart; ssl.onhandshakedone = onhandshakedone; ssl.onclienthello = loadSession; ssl.oncertcb = loadSNI; ssl.onnewsession = onnewsession; - ssl.onkeylog = onkeylog; ssl.lastHandshakeTime = 0; ssl.handshakes = 0; @@ -694,8 +700,6 @@ TLSSocket.prototype._init = function(socket, wrap) { // Also starts the client hello parser as a side effect. ssl.enableSessionCallbacks(); } - if (this.server.listenerCount('keylog') > 0) - ssl.enableKeylogCallback(); if (this.server.listenerCount('OCSPRequest') > 0) ssl.enableCertCb(); } @@ -724,21 +728,6 @@ TLSSocket.prototype._init = function(socket, wrap) { // Remove this listener since it's no longer needed. this.removeListener('newListener', newListener); } - - ssl.onkeylog = onkeylogclient; - - // Only call .onkeylog if there is a keylog listener. - this.on('newListener', keylogNewListener); - - function keylogNewListener(event) { - if (event !== 'keylog') - return; - - ssl.enableKeylogCallback(); - - // Remove this listener since it's no longer needed. - this.removeListener('newListener', keylogNewListener); - } } if (tlsKeylog) { @@ -746,17 +735,16 @@ TLSSocket.prototype._init = function(socket, wrap) { warnOnTlsKeylog = false; process.emitWarning('Using --tls-keylog makes TLS connections insecure ' + 'by writing secret key material to file ' + tlsKeylog); - ssl.enableKeylogCallback(); - this.on('keylog', (line) => { - appendFile(tlsKeylog, line, { mode: 0o600 }, (err) => { - if (err && warnOnTlsKeylogError) { - warnOnTlsKeylogError = false; - process.emitWarning('Failed to write TLS keylog (this warning ' + - 'will not be repeated): ' + err); - } - }); - }); } + this.on('keylog', (line) => { + appendFile(tlsKeylog, line, { mode: 0o600 }, (err) => { + if (err && warnOnTlsKeylogError) { + warnOnTlsKeylogError = false; + process.emitWarning('Failed to write TLS keylog (this warning ' + + 'will not be repeated): ' + err); + } + }); + }); } ssl.onerror = onerror; @@ -1059,6 +1047,10 @@ function onSocketTLSError(err) { } } +function onSocketKeylog(line) { + this._tlsOptions.server.emit('keylog', line, this); +} + function onSocketClose(err) { // Closed because of error - no need to emit it twice if (err) @@ -1091,6 +1083,9 @@ function tlsConnectionListener(rawSocket) { socket.on('secure', onServerSocketSecure); + if (this.listenerCount('keylog') > 0) + socket.on('keylog', onSocketKeylog); + socket[kErrorEmitted] = false; socket.on('close', onSocketClose); socket.on('_tlsError', onSocketTLSError); diff --git a/test/parallel/test-tls-enable-keylog-cli.js b/test/parallel/test-tls-enable-keylog-cli.js index 5d05069b15f87c..128d9a096a0532 100644 --- a/test/parallel/test-tls-enable-keylog-cli.js +++ b/test/parallel/test-tls-enable-keylog-cli.js @@ -24,8 +24,11 @@ const child = fork(__filename, ['test'], { child.on('close', common.mustCall((code, signal) => { assert.strictEqual(code, 0); assert.strictEqual(signal, null); - const log = fs.readFileSync(file, 'utf8'); - assert(/SECRET/.test(log)); + const log = fs.readFileSync(file, 'utf8').trim().split('\n'); + // Both client and server should log their secrets, + // so we should have two identical lines in the log + assert.strictEqual(log.length, 2); + assert.strictEqual(log[0], log[1]); })); function test() { @@ -40,7 +43,9 @@ function test() { }, server: { cert: keys.agent6.cert, - key: keys.agent6.key + key: keys.agent6.key, + // Number of keylog events is dependent on protocol version + maxVersion: 'TLSv1.2', }, }, common.mustCall((err, pair, cleanup) => { if (pair.server.err) {