From 1bb0aeb8f2214702da18a172a0f26d6118e7cc82 Mon Sep 17 00:00:00 2001 From: Fedor Indutny Date: Tue, 4 Nov 2014 22:24:19 -0500 Subject: [PATCH] crypto: escape DNS altname Vulnerability credit goes to: Calvin Liang conradjliang@hotmail.com Reviewed-By: Ben Noordhuis PR-URL: ...private --- src/node_crypto.cc | 48 +++++++++++++++++++++- test/fixtures/keys/0-dns-cert.pem | 18 +++++++++ test/fixtures/keys/0-dns-key.pem | 27 +++++++++++++ test/simple/test-tls-0-dns-altname.js | 57 +++++++++++++++++++++++++++ 4 files changed, 148 insertions(+), 2 deletions(-) create mode 100644 test/fixtures/keys/0-dns-cert.pem create mode 100644 test/fixtures/keys/0-dns-key.pem create mode 100644 test/simple/test-tls-0-dns-altname.js diff --git a/src/node_crypto.cc b/src/node_crypto.cc index e4674a33be962c..dc6e56d4f6cdbd 100644 --- a/src/node_crypto.cc +++ b/src/node_crypto.cc @@ -1104,6 +1104,48 @@ void SSLWrap::OnClientHello(void* arg, } +static bool SafeX509ExtPrint(BIO* out, X509_EXTENSION* ext) { + // Only alt_name is escaped at the moment + if (OBJ_obj2nid(ext->object) != NID_subject_alt_name) + return false; + + const X509V3_EXT_METHOD* method = X509V3_EXT_get(ext); + if (method == NULL || method->it == NULL) + return false; + + if (method->i2v != reinterpret_cast(i2v_GENERAL_NAMES)) + return false; + + const unsigned char* p = ext->value->data; + GENERAL_NAMES* names = reinterpret_cast(ASN1_item_d2i( + NULL, + &p, + ext->value->length, + ASN1_ITEM_ptr(method->it))); + if (names == NULL) + return false; + + for (int i = 0; i < sk_GENERAL_NAME_num(names); i++) { + GENERAL_NAME* gen = sk_GENERAL_NAME_value(names, i); + + if (gen->type == GEN_DNS) { + ASN1_IA5STRING* name = gen->d.dNSName; + + BIO_write(out, "DNS:", 4); + BIO_write(out, name->data, name->length); + } else { + STACK_OF(CONF_VALUE)* nval = i2v_GENERAL_NAME( + const_cast(method), gen, NULL); + if (nval == NULL) + return false; + X509V3_EXT_val_prn(out, nval, 0, 0); + } + } + + return true; +} + + static Local X509ToObject(Environment* env, X509* cert) { EscapableHandleScope scope(env->isolate()); @@ -1146,8 +1188,10 @@ static Local X509ToObject(Environment* env, X509* cert) { ext = X509_get_ext(cert, index); CHECK_NE(ext, nullptr); - rv = X509V3_EXT_print(bio, ext, 0, 0); - CHECK_EQ(rv, 1); + if (!SafeX509ExtPrint(bio, ext)) { + rv = X509V3_EXT_print(bio, ext, 0, 0); + CHECK_EQ(rv, 1); + } BIO_get_mem_ptr(bio, &mem); info->Set(keys[i], diff --git a/test/fixtures/keys/0-dns-cert.pem b/test/fixtures/keys/0-dns-cert.pem new file mode 100644 index 00000000000000..70171dc5d17163 --- /dev/null +++ b/test/fixtures/keys/0-dns-cert.pem @@ -0,0 +1,18 @@ +-----BEGIN CERTIFICATE----- +MIIC2jCCAcSgAwIBAgICJxEwCwYJKoZIhvcNAQEFMBUxEzARBgNVBAMWCm9oLm15 +Lmdvc2gwHxcNMTQxMTA1MDMyMDUyWhcOMzQwMTAzMTAzMjA1MlowEzERMA8GA1UE +AxYIZXZpbC5jb20wggEfMAsGCSqGSIb3DQEBAQOCAQ4AMIIBCQKCAQCsFwwf1dsr +PdxyTHBreymbFGACLQtaOihGsSkYtIzUEF1aT90YDMzNdoLr4wkwWig5FPRMnjmX +7pXY9RVbWmwG/M2eku9S62LekUFkeY1W/QftV9LYgAg7wVDA+v3+zk/EMEqADYm6 +W735tzDIKtvx+/3Dd9puQ0TLFNHBxAmTz7YNaJdIUqzs3DWT4zeZQj0RCOyWCjQL +NfqQ80I7NYFYb4IJqiUY8iOTL5kPi7b5szem5EakQbhufDWun4xGTZk/URZHgYgp +REbOLTYs2hqbK76biW/Yvwd1l7RsptIvJvkuQ1R/dO1WPv6PLKLTuS1EOHM3YqNH +o7wDSplOJe5rAgMBAAGhCQMHADEyMzQ1NqIJAwcANzg5YWJjoyYwJDAiBgNVHREB +AQAEGDAWghRnb29nbGUuY29tAC5ldmlsLmNvbTALBgkqhkiG9w0BAQEDggEBAHuf +1kxr49w51fC4nou96xj3IjcrJjOy5Aywn755enmaQ5Wh6AuVMHKqheITSbtoDT42 +jlIFJ3x+XmfenzV5ac8tawGNzJ+vy4+EYwL4QC11nZJ0FSLZ6KZgPI3lpShMy6Gs +bWFHDKrz6oivsitpUpeCK7aH1a7MVmr/G004vpVFe3OHggfyn9mHK3pCp0WIQuRl +PLiRgZSvryvOaf9cbVLvaUqcL480gcDVd4RGicBU52CeStocYeIHmPat1T+IOLFc +uv0VYQ4dzUfZ5c5YwwMPpHCVprxVR+grpZtd0su0bHDL5wETKBXEz8u2bmSORHgB +x2H+/2UOb6jab+IuQ08= +-----END CERTIFICATE----- diff --git a/test/fixtures/keys/0-dns-key.pem b/test/fixtures/keys/0-dns-key.pem new file mode 100644 index 00000000000000..d292789554d798 --- /dev/null +++ b/test/fixtures/keys/0-dns-key.pem @@ -0,0 +1,27 @@ +-----BEGIN RSA PRIVATE KEY----- +MIIEowIBAAKCAQEArBcMH9XbKz3cckxwa3spmxRgAi0LWjooRrEpGLSM1BBdWk/d +GAzMzXaC6+MJMFooORT0TJ45l+6V2PUVW1psBvzNnpLvUuti3pFBZHmNVv0H7VfS +2IAIO8FQwPr9/s5PxDBKgA2Julu9+bcwyCrb8fv9w3fabkNEyxTRwcQJk8+2DWiX +SFKs7Nw1k+M3mUI9EQjslgo0CzX6kPNCOzWBWG+CCaolGPIjky+ZD4u2+bM3puRG +pEG4bnw1rp+MRk2ZP1EWR4GIKURGzi02LNoamyu+m4lv2L8HdZe0bKbSLyb5LkNU +f3TtVj7+jyyi07ktRDhzN2KjR6O8A0qZTiXuawIDAQABAoIBABUlW9sJlz3QAyeU +VvgOEUW6EjYSPOPgY5SULl2XyfpA7IetapiK8huJJXtA0Z88ZNbmyUIk6yTNL2KS +cwZfrQiKxeVnXrsMq4B3ztY+zWxT+UZj1Ue/K8PT9E1SSiWmSkzsNitX/oWEwmpN +5VOjWJV6hmsfbhrAb1KZA1FQ+nBMEQrkEFpmFD1nJE8dH5rWNo4YbM/boR/kC93G +CHOwd2TKNrBa8ZeMOjcyUK9fg15CMkj7uTzfIGkjCM/mXOxvsvTuZ0np7PL7aF+o +GfSHP/l+B5rxT1GTYjZtpSEgAoqYEFJnnZELklo7KRWB7p2rgyHPElSjQN3xIn5Z +apNPrBECgYEA26gZGBP+j1Hqrh3nAhOq/t6PMj+V8yz/i2TrraJ1z7GKRGoBUOX9 +ruJGJExfACzgrKl1hL4XRfLdHuooScUqrIxLX7eKHE2nBSd0M40zEKbgIMRhaMsf +lAFOkxJRHMT7edaVu3MkSfDgFXRbhr+jcdxspzhunHMJVUnC5LgAKHMCgYEAyJAw +6GF80Uud5oDHo2tGY9uYgMIUN9rmrrFjqstkVB6QMFlyyeI3MHUhiU7qH53yaRCi +FxuHU6usQFmduwZAKInoPMRhYTYbexe4CYB+C96trwoV7ltDE+a7ZTsEj5kSYvCO +KLcVTn4mcU0TSpE0MU1XQKP0Ev/mdZ5aYEopvCkCgYEAlkVa3YkYNq5g8btNRbN0 +4SYbKtIrYJChRpjFTyV8mZkpMYKf4dtmANWWDNEekP0iu5y25BgzzcvHkJW6+DTl +6+OS0Sm8V36cS79hFL99dt/jJyeSSGHl+ZgnTCBU02zDaefuya2M3vTmKGdREk9a +ntOglYnayjc85Fcw+M4UdZcCgYAFw/9j7smDysSzR6h1jjPr0vhDW1Dxeh1/kCHp +Wwd7U5WZjji6jQJBJlzccaRRXF0HoC7Is0Xkpd7BytG5+qgFglFmzc5u2PtZQolL +3KHC/ZfInGWdAIqhG9TvSA8Ngb0BkyDDEuBN7Vp1j12qmxoBANQtS4lMsoaRgwfe +FMO2YQKBgGv6Ndv+eHWSkqGFOSXU6dXAjOuAji3K1yRlxUg/RS/DCMK+8XQbuh47 ++p998LwvI70JIr4v2PAkO3/HaRILOTRLLvq8O/yqHwrVf+P7AQ8kPm7uUf7kTXat +DYcKIAp5ddZweyFCgwVm+JMd1E+cpL97RbHCbu7Ct6OD9uLGXCUh +-----END RSA PRIVATE KEY----- diff --git a/test/simple/test-tls-0-dns-altname.js b/test/simple/test-tls-0-dns-altname.js new file mode 100644 index 00000000000000..29ee7fcb4b615b --- /dev/null +++ b/test/simple/test-tls-0-dns-altname.js @@ -0,0 +1,57 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +if (!process.versions.openssl) { + console.error('Skipping because node compiled without OpenSSL.'); + process.exit(0); +} + +var assert = require('assert'); +var fs = require('fs'); +var net = require('net'); +var tls = require('tls'); + +var common = require('../common'); + +var requests = 0; + +var server = tls.createServer({ + key: fs.readFileSync(common.fixturesDir + '/keys/0-dns-key.pem'), + cert: fs.readFileSync(common.fixturesDir + '/keys/0-dns-cert.pem') +}, function(c) { + c.once('data', function() { + c.destroy(); + server.close(); + }); +}).listen(common.PORT, function() { + var c = tls.connect(common.PORT, { + rejectUnauthorized: false + }, function() { + requests++; + var cert = c.getPeerCertificate(); + assert.equal(cert.subjectaltname, 'DNS:google.com\0.evil.com'); + c.write('ok'); + }); +}); + +process.on('exit', function() { + assert.equal(requests, 1); +});