From b54504c1d53796e02967e2c6c0a48416e64cf4e1 Mon Sep 17 00:00:00 2001 From: HinataKah0 <128208841+HinataKah0@users.noreply.github.com> Date: Wed, 26 Apr 2023 14:39:00 +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 PR-URL: https://github.com/nodejs/node/pull/47570 Reviewed-By: Anna Henningsen Reviewed-By: Yagiz Nizipli Reviewed-By: Luigi Pinca Reviewed-By: James M Snell --- doc/api/tls.md | 7 +-- lib/_tls_wrap.js | 6 ++- test/parallel/test-tls-add-context.js | 75 +++++++++++++++++++++++++++ 3 files changed, 83 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 5b44ba7d8c3f56..b0522452e7c5c0 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..8d02866ce51c5e --- /dev/null +++ b/test/parallel/test-tls-add-context.js @@ -0,0 +1,75 @@ +'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, +}; + +let connections = 0; + +const server = tls.createServer(serverOptions, (c) => { + if (++connections === 3) { + server.close(); + } + 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(); + })); +}));