diff options
author | Tobias Nießen <tniessen@tnie.de> | 2021-12-07 02:14:49 +0000 |
---|---|---|
committer | Richard Lau <rlau@redhat.com> | 2022-01-10 22:38:05 +0000 |
commit | 50439b446f1e6bfc91f03d4b070edb5357b16b8b (patch) | |
tree | 7ef0b8b3806ee5052dbc616bc0caf84be9379602 /test | |
parent | 466e5415a2b7b3574ab5403acb87e89a94a980d1 (diff) | |
download | node-new-50439b446f1e6bfc91f03d4b070edb5357b16b8b.tar.gz |
tls: drop support for URI alternative names
Previously, Node.js incorrectly accepted uniformResourceIdentifier (URI)
subject alternative names in checkServerIdentity regardless of the
application protocol. This was incorrect even in the most common cases.
For example, RFC 2818 specifies (and RFC 6125 confirms) that HTTP over
TLS only uses dNSName and iPAddress subject alternative names, but not
uniformResourceIdentifier subject alternative names.
Additionally, name constrained certificate authorities might not be
constrained to specific URIs, allowing them to issue certificates for
URIs that specify hosts that they would not be allowed to issue dNSName
certificates for.
Even for application protocols that make use of URI subject alternative
names (such as SIP, see RFC 5922), Node.js did not implement the
required checks correctly, for example, because checkServerIdentity
ignores the URI scheme.
As a side effect, this also fixes an edge case. When a hostname is not
an IP address and no dNSName subject alternative name exists, the
subject's Common Name should be considered even when an iPAddress
subject alternative name exists.
It remains possible for users to pass a custom checkServerIdentity
function to the TLS implementation in order to implement custom identity
verification logic.
This addresses CVE-2021-44531.
CVE-ID: CVE-2021-44531
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 'test')
-rw-r--r-- | test/fixtures/keys/Makefile | 14 | ||||
-rw-r--r-- | test/fixtures/keys/irrelevant_san_correct_subject-cert.pem | 11 | ||||
-rw-r--r-- | test/fixtures/keys/irrelevant_san_correct_subject-key.pem | 5 | ||||
-rw-r--r-- | test/parallel/test-tls-check-server-identity.js | 14 | ||||
-rw-r--r-- | test/parallel/test-x509-escaping.js | 31 |
5 files changed, 64 insertions, 11 deletions
diff --git a/test/fixtures/keys/Makefile b/test/fixtures/keys/Makefile index e5dab9e046..71bc36aaa3 100644 --- a/test/fixtures/keys/Makefile +++ b/test/fixtures/keys/Makefile @@ -87,6 +87,8 @@ all: \ ec_secp256k1_public.pem \ incorrect_san_correct_subject-cert.pem \ incorrect_san_correct_subject-key.pem \ + irrelevant_san_correct_subject-cert.pem \ + irrelevant_san_correct_subject-key.pem \ # # Create Certificate Authority: ca1 @@ -795,6 +797,18 @@ incorrect_san_correct_subject-cert.pem: incorrect_san_correct_subject-key.pem incorrect_san_correct_subject-key.pem: openssl ecparam -name prime256v1 -genkey -noout -out incorrect_san_correct_subject-key.pem +irrelevant_san_correct_subject-cert.pem: irrelevant_san_correct_subject-key.pem + openssl req -x509 \ + -key irrelevant_san_correct_subject-key.pem \ + -out irrelevant_san_correct_subject-cert.pem \ + -sha256 \ + -days 3650 \ + -subj "/CN=good.example.com" \ + -addext "subjectAltName = IP:1.2.3.4" + +irrelevant_san_correct_subject-key.pem: + openssl ecparam -name prime256v1 -genkey -noout -out irrelevant_san_correct_subject-key.pem + clean: rm -f *.pfx *.pem *.srl ca2-database.txt ca2-serial fake-startcom-root-serial *.print *.old fake-startcom-root-issued-certs/*.pem @> fake-startcom-root-database.txt diff --git a/test/fixtures/keys/irrelevant_san_correct_subject-cert.pem b/test/fixtures/keys/irrelevant_san_correct_subject-cert.pem new file mode 100644 index 0000000000..cdb74b7de3 --- /dev/null +++ b/test/fixtures/keys/irrelevant_san_correct_subject-cert.pem @@ -0,0 +1,11 @@ +-----BEGIN CERTIFICATE----- +MIIBnTCCAUKgAwIBAgIUa28EJmmQ7yZOq3WWNP3SLiJnzcAwCgYIKoZIzj0EAwIw +GzEZMBcGA1UEAwwQZ29vZC5leGFtcGxlLmNvbTAeFw0yMTEyMTExNzE0NDVaFw0z +MTEyMDkxNzE0NDVaMBsxGTAXBgNVBAMMEGdvb2QuZXhhbXBsZS5jb20wWTATBgcq +hkjOPQIBBggqhkjOPQMBBwNCAATEKoJfDvKQ6dD+yvc4DaeH0ZlG8VuGJUVi6iIb +ugY3dKHdmXUIuwwUScgztLc6W8FfvbTxfTF2q90ZBJlr/Klvo2QwYjAdBgNVHQ4E +FgQUu55oRZI5tdQDmViwAvPEbzZuY2owHwYDVR0jBBgwFoAUu55oRZI5tdQDmViw +AvPEbzZuY2owDwYDVR0TAQH/BAUwAwEB/zAPBgNVHREECDAGhwQBAgMEMAoGCCqG +SM49BAMCA0kAMEYCIQDw8z8d7ToB14yxMJxEDF1dhUqMReJFFwPVnvzkr174igIh +AKJ9XL+02sGOE7xZd5C0KqUXeHoIE9shnejnhm3WBrB/ +-----END CERTIFICATE----- diff --git a/test/fixtures/keys/irrelevant_san_correct_subject-key.pem b/test/fixtures/keys/irrelevant_san_correct_subject-key.pem new file mode 100644 index 0000000000..b0a96659c6 --- /dev/null +++ b/test/fixtures/keys/irrelevant_san_correct_subject-key.pem @@ -0,0 +1,5 @@ +-----BEGIN EC PRIVATE KEY----- +MHcCAQEEIDsijdVlHMNTvJ4eqeUbpjMMnl72+HLtEIEcbauckCP6oAoGCCqGSM49 +AwEHoUQDQgAExCqCXw7ykOnQ/sr3OA2nh9GZRvFbhiVFYuoiG7oGN3Sh3Zl1CLsM +FEnIM7S3OlvBX7208X0xdqvdGQSZa/ypbw== +-----END EC PRIVATE KEY----- diff --git a/test/parallel/test-tls-check-server-identity.js b/test/parallel/test-tls-check-server-identity.js index ad79d93a3d..fe81fc5285 100644 --- a/test/parallel/test-tls-check-server-identity.js +++ b/test/parallel/test-tls-check-server-identity.js @@ -134,7 +134,7 @@ const tests = [ { host: 'a.com', cert: { }, - error: 'Cert is empty' + error: 'Cert does not contain a DNS name' }, // Empty Subject w/DNS name @@ -148,7 +148,8 @@ const tests = [ { host: 'a.b.a.com', cert: { subjectaltname: 'URI:http://a.b.a.com/', - } + }, + error: 'Cert does not contain a DNS name' }, // Multiple CN fields @@ -265,15 +266,15 @@ const tests = [ host: 'a.b.a.com', cert: { subjectaltname: 'URI:http://a.b.a.com/', subject: {} - } + }, + error: 'Cert does not contain a DNS name' }, { host: 'a.b.a.com', cert: { subjectaltname: 'URI:http://*.b.a.com/', subject: {} }, - error: 'Host: a.b.a.com. is not in the cert\'s altnames: ' + - 'URI:http://*.b.a.com/' + error: 'Cert does not contain a DNS name' }, // IP addresses { @@ -281,8 +282,7 @@ const tests = [ subjectaltname: 'IP Address:127.0.0.1', subject: {} }, - error: 'Host: a.b.a.com. is not in the cert\'s altnames: ' + - 'IP Address:127.0.0.1' + error: 'Cert does not contain a DNS name' }, { host: '127.0.0.1', cert: { diff --git a/test/parallel/test-x509-escaping.js b/test/parallel/test-x509-escaping.js index 5904e01e43..5970f24347 100644 --- a/test/parallel/test-x509-escaping.js +++ b/test/parallel/test-x509-escaping.js @@ -20,10 +20,6 @@ const { hasOpenSSL3 } = common; const numLeaves = 5; for (let i = 0; i < numLeaves; i++) { - // TODO(tniessen): this test case requires proper handling of URI SANs, - // which node currently does not implement. - if (i === 3) continue; - const name = `x509-escaping/google/leaf${i}.pem`; const leafPEM = fixtures.readSync(name, 'utf8'); @@ -336,3 +332,30 @@ const { hasOpenSSL3 } = common; })); })).unref(); } + +// The subject MUST NOT be ignored if no dNSName subject alternative name +// exists, even if other subject alternative names exist. +{ + const key = fixtures.readKey('irrelevant_san_correct_subject-key.pem'); + const cert = fixtures.readKey('irrelevant_san_correct_subject-cert.pem'); + + // The hostname is the CN, but there is no dNSName SAN entry. + const servername = 'good.example.com'; + const certX509 = new X509Certificate(cert); + assert.strictEqual(certX509.subject, `CN=${servername}`); + assert.strictEqual(certX509.subjectAltName, 'IP Address:1.2.3.4'); + + // Connect to a server that uses the self-signed certificate. + const server = tls.createServer({ key, cert }, common.mustCall((socket) => { + socket.destroy(); + server.close(); + })).listen(common.mustCall(() => { + const { port } = server.address(); + tls.connect(port, { + ca: cert, + servername, + }, common.mustCall(() => { + // Do nothing, the server will close the connection. + })); + })); +} |