tls: add tlsSocket.disableRenegotiation()
Allows TLS renegotiation to be disabled per `TLSSocket` instance. Per HTTP/2, TLS renegotiation is forbidden after the initial connection prefix is exchanged. PR-URL: https://github.com/nodejs/node/pull/14239 Reviewed-By: Anna Henningsen <anna@addaleax.net> Reviewed-By: Colin Ihrig <cjihrig@gmail.com> Reviewed-By: Matteo Collina <matteo.collina@gmail.com>
This commit is contained in:
parent
9ea363ee99
commit
b1909d3a70
@ -552,6 +552,14 @@ added: v0.11.4
|
||||
Returns `true` if the peer certificate was signed by one of the CAs specified
|
||||
when creating the `tls.TLSSocket` instance, otherwise `false`.
|
||||
|
||||
### tlsSocket.disableRenegotiation()
|
||||
<!-- YAML
|
||||
added: REPLACEME
|
||||
-->
|
||||
|
||||
Disables TLS renegotiation for this `TLSSocket` instance. Once called, attempts
|
||||
to renegotiate will trigger an `'error'` event on the `TLSSocket`.
|
||||
|
||||
### tlsSocket.encrypted
|
||||
<!-- YAML
|
||||
added: v0.11.4
|
||||
|
@ -37,6 +37,7 @@ const tls_wrap = process.binding('tls_wrap');
|
||||
const TCP = process.binding('tcp_wrap').TCP;
|
||||
const Pipe = process.binding('pipe_wrap').Pipe;
|
||||
const errors = require('internal/errors');
|
||||
const kDisableRenegotiation = Symbol('disable-renegotiation');
|
||||
|
||||
function onhandshakestart() {
|
||||
debug('onhandshakestart');
|
||||
@ -64,6 +65,11 @@ function onhandshakestart() {
|
||||
self._emitTLSError(err);
|
||||
});
|
||||
}
|
||||
|
||||
if (this[kDisableRenegotiation] && ssl.handshakes > 0) {
|
||||
const err = new Error('TLS session renegotiation disabled for this socket');
|
||||
self._emitTLSError(err);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -358,6 +364,10 @@ tls_wrap.TLSWrap.prototype.close = function close(cb) {
|
||||
return this._parent.close(done);
|
||||
};
|
||||
|
||||
TLSSocket.prototype.disableRenegotiation = function disableRenegotiation() {
|
||||
this[kDisableRenegotiation] = true;
|
||||
};
|
||||
|
||||
TLSSocket.prototype._wrapHandle = function(wrap) {
|
||||
var res;
|
||||
var handle;
|
||||
|
67
test/parallel/test-tls-disable-renegotiation.js
Executable file
67
test/parallel/test-tls-disable-renegotiation.js
Executable file
@ -0,0 +1,67 @@
|
||||
'use strict';
|
||||
const common = require('../common');
|
||||
const assert = require('assert');
|
||||
const fs = require('fs');
|
||||
|
||||
// Tests that calling disableRenegotiation on a TLSSocket stops renegotiation.
|
||||
|
||||
if (!common.hasCrypto) {
|
||||
common.skip('missing crypto');
|
||||
return;
|
||||
}
|
||||
const tls = require('tls');
|
||||
|
||||
const options = {
|
||||
key: fs.readFileSync(`${common.fixturesDir}/keys/agent1-key.pem`),
|
||||
cert: fs.readFileSync(`${common.fixturesDir}/keys/agent1-cert.pem`)
|
||||
};
|
||||
|
||||
const server = tls.Server(options, common.mustCall((socket) => {
|
||||
socket.on('error', common.mustCall((err) => {
|
||||
assert.strictEqual(
|
||||
err.message,
|
||||
'TLS session renegotiation disabled for this socket');
|
||||
socket.destroy();
|
||||
server.close();
|
||||
}));
|
||||
// Disable renegotiation after the first chunk of data received.
|
||||
// Demonstrates that renegotiation works successfully up until
|
||||
// disableRenegotiation is called.
|
||||
socket.on('data', common.mustCall((chunk) => {
|
||||
socket.write(chunk);
|
||||
socket.disableRenegotiation();
|
||||
}));
|
||||
socket.on('secure', common.mustCall(() => {
|
||||
assert(socket._handle.handshakes < 2,
|
||||
`Too many handshakes [${socket._handle.handshakes}]`);
|
||||
}));
|
||||
}));
|
||||
|
||||
|
||||
server.listen(0, common.mustCall(() => {
|
||||
const port = server.address().port;
|
||||
const client =
|
||||
tls.connect({rejectUnauthorized: false, port: port}, common.mustCall(() => {
|
||||
client.write('');
|
||||
// Negotiation is still permitted for this first
|
||||
// attempt. This should succeed.
|
||||
client.renegotiate(
|
||||
{rejectUnauthorized: false},
|
||||
common.mustCall(() => {
|
||||
// Once renegotiation completes, we write some
|
||||
// data to the socket, which triggers the on
|
||||
// data event on the server. After that data
|
||||
// is received, disableRenegotiation is called.
|
||||
client.write('data', common.mustCall(() => {
|
||||
client.write('');
|
||||
// This second renegotiation attempt should fail
|
||||
// and the callback should never be invoked. The
|
||||
// server will simply drop the connection after
|
||||
// emitting the error.
|
||||
client.renegotiate(
|
||||
{rejectUnauthorized: false},
|
||||
common.mustNotCall());
|
||||
}));
|
||||
}));
|
||||
}));
|
||||
}));
|
Loading…
x
Reference in New Issue
Block a user