Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/nodejs/node.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTobias Nießen <tniessen@tnie.de>2021-12-07 03:21:28 +0300
committerRichard Lau <rlau@redhat.com>2022-01-11 01:38:05 +0300
commit466e5415a2b7b3574ab5403acb87e89a94a980d1 (patch)
treec023cb0068a802e111c5bfe829871f76080c67d2 /lib/tls.js
parent26a5c583ab907d788e4d738091ef7046253946c5 (diff)
crypto,tls: implement safe x509 GeneralName format
This change introduces JSON-compatible escaping rules for strings that include X.509 GeneralName components (see RFC 5280). This non-standard format avoids ambiguities and prevents injection attacks that could previously lead to X.509 certificates being accepted even though they were not valid for the target hostname. These changes affect the format of subject alternative names and the format of authority information access. The checkServerIdentity function has been modified to safely handle the new format, eliminating the possibility of injecting subject alternative names into the verification logic. Because each subject alternative name is only encoded as a JSON string literal if necessary for security purposes, this change will only be visible in rare cases. This addresses CVE-2021-44532. CVE-ID: CVE-2021-44532 PR-URL: https://github.com/nodejs-private/node-private/pull/300 Reviewed-By: Michael Dawson <midawson@redhat.com> Reviewed-By: Rich Trott <rtrott@gmail.com>
Diffstat (limited to 'lib/tls.js')
-rw-r--r--lib/tls.js48
1 files changed, 47 insertions, 1 deletions
diff --git a/lib/tls.js b/lib/tls.js
index 683736460b1..eaba340d572 100644
--- a/lib/tls.js
+++ b/lib/tls.js
@@ -30,20 +30,25 @@ const {
ArrayPrototypePush,
ArrayPrototypeReduce,
ArrayPrototypeSome,
+ JSONParse,
ObjectDefineProperty,
ObjectFreeze,
+ RegExpPrototypeExec,
RegExpPrototypeTest,
StringFromCharCode,
StringPrototypeCharCodeAt,
StringPrototypeEndsWith,
StringPrototypeIncludes,
+ StringPrototypeIndexOf,
StringPrototypeReplace,
StringPrototypeSlice,
StringPrototypeSplit,
StringPrototypeStartsWith,
+ StringPrototypeSubstring,
} = primordials;
const {
+ ERR_TLS_CERT_ALTNAME_FORMAT,
ERR_TLS_CERT_ALTNAME_INVALID,
ERR_OUT_OF_RANGE
} = require('internal/errors').codes;
@@ -227,6 +232,45 @@ function check(hostParts, pattern, wildcards) {
return true;
}
+// This pattern is used to determine the length of escaped sequences within
+// the subject alt names string. It allows any valid JSON string literal.
+// This MUST match the JSON specification (ECMA-404 / RFC8259) exactly.
+const jsonStringPattern =
+ // eslint-disable-next-line no-control-regex
+ /^"(?:[^"\\\u0000-\u001f]|\\(?:["\\/bfnrt]|u[0-9a-fA-F]{4}))*"/;
+
+function splitEscapedAltNames(altNames) {
+ const result = [];
+ let currentToken = '';
+ let offset = 0;
+ while (offset !== altNames.length) {
+ const nextSep = StringPrototypeIndexOf(altNames, ', ', offset);
+ const nextQuote = StringPrototypeIndexOf(altNames, '"', offset);
+ if (nextQuote !== -1 && (nextSep === -1 || nextQuote < nextSep)) {
+ // There is a quote character and there is no separator before the quote.
+ currentToken += StringPrototypeSubstring(altNames, offset, nextQuote);
+ const match = RegExpPrototypeExec(
+ jsonStringPattern, StringPrototypeSubstring(altNames, nextQuote));
+ if (!match) {
+ throw new ERR_TLS_CERT_ALTNAME_FORMAT();
+ }
+ currentToken += JSONParse(match[0]);
+ offset = nextQuote + match[0].length;
+ } else if (nextSep !== -1) {
+ // There is a separator and no quote before it.
+ currentToken += StringPrototypeSubstring(altNames, offset, nextSep);
+ ArrayPrototypePush(result, currentToken);
+ currentToken = '';
+ offset = nextSep + 2;
+ } else {
+ currentToken += StringPrototypeSubstring(altNames, offset);
+ offset = altNames.length;
+ }
+ }
+ ArrayPrototypePush(result, currentToken);
+ return result;
+}
+
exports.checkServerIdentity = function checkServerIdentity(hostname, cert) {
const subject = cert.subject;
const altNames = cert.subjectaltname;
@@ -237,7 +281,9 @@ exports.checkServerIdentity = function checkServerIdentity(hostname, cert) {
hostname = '' + hostname;
if (altNames) {
- const splitAltNames = StringPrototypeSplit(altNames, ', ');
+ const splitAltNames = StringPrototypeIncludes(altNames, '"') ?
+ splitEscapedAltNames(altNames) :
+ StringPrototypeSplit(altNames, ', ');
ArrayPrototypeForEach(splitAltNames, (name) => {
if (StringPrototypeStartsWith(name, 'DNS:')) {
ArrayPrototypePush(dnsNames, StringPrototypeSlice(name, 4));