From 939c57683fbd5ab9d1e0b38e53de35689d0df558 Mon Sep 17 00:00:00 2001 From: Shiba Rin Date: Sat, 15 Apr 2023 17:56:17 +0800 Subject: [PATCH] tls: accept SecureContext object in server.addContext() Do not call tls.createSecureContext() if the context provided is already an instance of tls.SecureContext. Fixes: https://github.com/nodejs/node/issues/47408 --- doc/api/tls.md | 7 +-- lib/_tls_wrap.js | 6 ++- test/parallel/test-tls-add-context.js | 74 +++++++++++++++++++++++++++ 3 files changed, 82 insertions(+), 5 deletions(-) create mode 100644 test/parallel/test-tls-add-context.js diff --git a/doc/api/tls.md b/doc/api/tls.md index b115b172306912..49ea58b1b294b8 100644 --- a/doc/api/tls.md +++ b/doc/api/tls.md @@ -728,9 +728,10 @@ added: v0.5.3 --> * `hostname` {string} A SNI host name or wildcard (e.g. `'*'`) -* `context` {Object} An object containing any of the possible properties - from the [`tls.createSecureContext()`][] `options` arguments (e.g. `key`, - `cert`, `ca`, etc). +* `context` {Object|tls.SecureContext} An object containing any of the possible + properties from the [`tls.createSecureContext()`][] `options` arguments + (e.g. `key`, `cert`, `ca`, etc), or a TLS context object created with + [`tls.createSecureContext()`][] itself. The `server.addContext()` method adds a secure context that will be used if the client request's SNI name matches the supplied `hostname` (or wildcard). diff --git a/lib/_tls_wrap.js b/lib/_tls_wrap.js index e8f926d4c4f7a4..d5db06c4404c98 100644 --- a/lib/_tls_wrap.js +++ b/lib/_tls_wrap.js @@ -1476,8 +1476,10 @@ Server.prototype.addContext = function(servername, context) { RegExpPrototypeSymbolReplace(/([.^$+?\-\\[\]{}])/g, servername, '\\$1'), '*', '[^.]*', ) + '$'); - ArrayPrototypePush(this._contexts, - [re, tls.createSecureContext(context).context]); + + const secureContext = + context instanceof common.SecureContext ? context : tls.createSecureContext(context); + ArrayPrototypePush(this._contexts, [re, secureContext.context]); }; Server.prototype[EE.captureRejectionSymbol] = function( diff --git a/test/parallel/test-tls-add-context.js b/test/parallel/test-tls-add-context.js new file mode 100644 index 00000000000000..8d8f7bfee8596e --- /dev/null +++ b/test/parallel/test-tls-add-context.js @@ -0,0 +1,74 @@ +'use strict'; +const common = require('../common'); + +if (!common.hasCrypto) + common.skip('missing crypto'); + +const fixtures = require('../common/fixtures'); +const assert = require('assert'); +const tls = require('tls'); + +function loadPEM(n) { + return fixtures.readKey(`${n}.pem`); +} + +const serverOptions = { + key: loadPEM('agent2-key'), + cert: loadPEM('agent2-cert'), + ca: [ loadPEM('ca2-cert') ], + requestCert: true, + rejectUnauthorized: false, +}; + +const server = tls.createServer(serverOptions, (c) => { + if (c.servername === 'unknowncontext') { + assert.strictEqual(c.authorized, false); + return; + } + assert.strictEqual(c.authorized, true); +}); + +const secureContext = { + key: loadPEM('agent1-key'), + cert: loadPEM('agent1-cert'), + ca: [ loadPEM('ca1-cert') ], +}; +server.addContext('context1', secureContext); +server.addContext('context2', tls.createSecureContext(secureContext)); + +const clientOptionsBase = { + key: loadPEM('agent1-key'), + cert: loadPEM('agent1-cert'), + ca: [ loadPEM('ca1-cert') ], + rejectUnauthorized: false, +}; + +server.listen(0, common.mustCall(() => { + const client1 = tls.connect({ + ...clientOptionsBase, + port: server.address().port, + servername: 'context1', + }, common.mustCall(() => { + client1.end(); + })); + + const client2 = tls.connect({ + ...clientOptionsBase, + port: server.address().port, + servername: 'context2', + }, common.mustCall(() => { + client2.end(); + })); + + const client3 = tls.connect({ + ...clientOptionsBase, + port: server.address().port, + servername: 'unknowncontext', + }, common.mustCall(() => { + client3.end(); + })); + + client3.on('close', common.mustCall(() => { + server.close(); + })); +}));