From 6f606838025c4e87701f360135f1ae72e968dc4c Mon Sep 17 00:00:00 2001 From: koichik Date: Fri, 26 Aug 2011 20:00:40 +0900 Subject: [PATCH] tls: x509 certificate subject parsing fail Fixes #1568. --- lib/tls.js | 8 ++-- src/node_crypto.cc | 44 +++++++++++------- test/fixtures/alice.crt | 24 ++++++++++ test/simple/test-tls-peer-certificate.js | 58 ++++++++++++++++++++++++ 4 files changed, 113 insertions(+), 21 deletions(-) create mode 100644 test/fixtures/alice.crt create mode 100644 test/simple/test-tls-peer-certificate.js diff --git a/lib/tls.js b/lib/tls.js index a03c4289afe..43f565d4617 100644 --- a/lib/tls.js +++ b/lib/tls.js @@ -153,12 +153,12 @@ CryptoStream.prototype.setEncoding = function(encoding) { }; -// EG '/C=US/ST=CA/L=SF/O=Joyent/OU=Node.js/CN=ca1/emailAddress=ry@clouds.org' +// Example: +// C=US\nST=CA\nL=SF\nO=Joyent\nOU=Node.js\nCN=ca1\nemailAddress=ry@clouds.org function parseCertString(s) { var out = {}; - var parts = s.split('/'); - // Note: can always skip the first one. - for (var i = 1; i < parts.length; i++) { + var parts = s.split('\n'); + for (var i = 0, len = parts.length; i < len; i++) { var sepIndex = parts[i].indexOf('='); if (sepIndex > 0) { var key = parts[i].slice(0, sepIndex); diff --git a/src/node_crypto.cc b/src/node_crypto.cc index a88d124d796..10461be4c85 100644 --- a/src/node_crypto.cc +++ b/src/node_crypto.cc @@ -50,6 +50,11 @@ static const char *PUBLIC_KEY_PFX = "-----BEGIN PUBLIC KEY-----"; static const int PUBLIC_KEY_PFX_LEN = strlen(PUBLIC_KEY_PFX); +static const int X509_NAME_FLAGS = ASN1_STRFLGS_ESC_CTRL + | ASN1_STRFLGS_ESC_MSB + | XN_FLAG_SEP_MULTILINE + | XN_FLAG_FN_SN; + namespace node { namespace crypto { @@ -1066,27 +1071,31 @@ Handle Connection::GetPeerCertificate(const Arguments& args) { Local info = Object::New(); X509* peer_cert = SSL_get_peer_certificate(ss->ssl_); if (peer_cert != NULL) { - char* subject = X509_NAME_oneline(X509_get_subject_name(peer_cert), 0, 0); - if (subject != NULL) { - info->Set(subject_symbol, String::New(subject)); - OPENSSL_free(subject); - } - char* issuer = X509_NAME_oneline(X509_get_issuer_name(peer_cert), 0, 0); - if (subject != NULL) { - info->Set(issuer_symbol, String::New(issuer)); - OPENSSL_free(issuer); - } - char buf[256]; BIO* bio = BIO_new(BIO_s_mem()); + BUF_MEM* mem; + if (X509_NAME_print_ex(bio, X509_get_subject_name(peer_cert), 0, + X509_NAME_FLAGS) > 0) { + BIO_get_mem_ptr(bio, &mem); + info->Set(subject_symbol, String::New(mem->data, mem->length)); + } + (void) BIO_reset(bio); + + if (X509_NAME_print_ex(bio, X509_get_issuer_name(peer_cert), 0, + X509_NAME_FLAGS) > 0) { + BIO_get_mem_ptr(bio, &mem); + info->Set(issuer_symbol, String::New(mem->data, mem->length)); + } + (void) BIO_reset(bio); + ASN1_TIME_print(bio, X509_get_notBefore(peer_cert)); - memset(buf, 0, sizeof(buf)); - BIO_read(bio, buf, sizeof(buf) - 1); - info->Set(valid_from_symbol, String::New(buf)); + BIO_get_mem_ptr(bio, &mem); + info->Set(valid_from_symbol, String::New(mem->data, mem->length)); + (void) BIO_reset(bio); + ASN1_TIME_print(bio, X509_get_notAfter(peer_cert)); - memset(buf, 0, sizeof(buf)); - BIO_read(bio, buf, sizeof(buf) - 1); + BIO_get_mem_ptr(bio, &mem); + info->Set(valid_to_symbol, String::New(mem->data, mem->length)); BIO_free(bio); - info->Set(valid_to_symbol, String::New(buf)); unsigned int md_size, i; unsigned char md[EVP_MAX_MD_SIZE]; @@ -1114,6 +1123,7 @@ Handle Connection::GetPeerCertificate(const Arguments& args) { peer_cert, NID_ext_key_usage, NULL, NULL); if (eku != NULL) { Local ext_key_usage = Array::New(); + char buf[256]; for (int i = 0; i < sk_ASN1_OBJECT_num(eku); i++) { memset(buf, 0, sizeof(buf)); diff --git a/test/fixtures/alice.crt b/test/fixtures/alice.crt new file mode 100644 index 00000000000..b4e986bc7bf --- /dev/null +++ b/test/fixtures/alice.crt @@ -0,0 +1,24 @@ +-----BEGIN CERTIFICATE----- +MIID8DCCAtigAwIBAgIJALmw0zKhqlY1MA0GCSqGSIb3DQEBBQUAMFgxDjAMBgNV +BAMTBWFsaWNlMUYwRAYDVR0RFD11bmlmb3JtUmVzb3VyY2VJZGVudGlmaWVyOmh0 +dHA6Ly9sb2NhbGhvc3Q6ODAwMC9hbGljZS5mb2FmI21lMB4XDTExMDgyNDA1NTUx +NFoXDTExMDkyMzA1NTUxNFowWDEOMAwGA1UEAxMFYWxpY2UxRjBEBgNVHREUPXVu +aWZvcm1SZXNvdXJjZUlkZW50aWZpZXI6aHR0cDovL2xvY2FsaG9zdDo4MDAwL2Fs +aWNlLmZvYWYjbWUwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDP4tdk +6NxAImrf5kpQVpuPvWij97H9ewFwWq0FOGONPD0JURXB89BCnhfC0+IHbTi+dhfB +DX9HY11NCoJm7juXv0uywv+7Zrlj37Q0RTedADmp237UUATRzmh7E+KZc6tHcZZ8 +rPs+ZnY7TXXsh4JRRc8blTy6aEN7/iYMXhk0mIpzjTha2Gq5OqBLustBkeFnoPQS +cac6TOWbEkxg80dI5jX/qK901RRwLztrA0QDeWB+HLbkjIErdAlz5pgo1Nu3vQt6 +vLdu7bBYsUa2Y2IaVBNLgmzEiZGwQJMjvbs5tLv8VYBCypb4I4vRJrkG4wWsUimM ++sR7SKHu9FFt2ZdHAgMBAAGjgbwwgbkwHQYDVR0OBBYEFA51eHepg7Tp5bi6Ao5F +B01/5+GoMIGJBgNVHSMEgYEwf4AUDnV4d6mDtOnluLoCjkUHTX/n4aihXKRaMFgx +DjAMBgNVBAMTBWFsaWNlMUYwRAYDVR0RFD11bmlmb3JtUmVzb3VyY2VJZGVudGlm +aWVyOmh0dHA6Ly9sb2NhbGhvc3Q6ODAwMC9hbGljZS5mb2FmI21lggkAubDTMqGq +VjUwDAYDVR0TBAUwAwEB/zANBgkqhkiG9w0BAQUFAAOCAQEAreudH4Y7R9Vl2GJo +2xRUEwZiMj/ogomZ7B+IZtcuMIR8X2mzQ30xmPKaCy/fjbueqBroIDdxFeQ4eWZf +MD3AK/q5lJXwSInDjnn7jE9gNgLdQeCnajV0/QH+eDxIe/Alvx+RuvrDiNOudEs4 +vhqv5zEaL6VEXoWVb4/cghDbynQucSpyOMmGGPYYw2zmg0nNXdQauYWDUZIaDwQ6 +tM/pi2ewYubHPZdwJv5jvxTN3Z7RuuGHM+aLAZSAqSgAi0ml8PYYd2eRzXMaEI0c +eajcEvVa405aYT6dxuF1qqRDYx14As/R7O5RKCpz7wsxD6ICD/Ynv3GCUGxANQim +bcCjpg== +-----END CERTIFICATE----- diff --git a/test/simple/test-tls-peer-certificate.js b/test/simple/test-tls-peer-certificate.js new file mode 100644 index 00000000000..6ef03c7a6f9 --- /dev/null +++ b/test/simple/test-tls-peer-certificate.js @@ -0,0 +1,58 @@ +// 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 common = require('../common'); +var assert = require('assert'); +var tls = require('tls'); +var fs = require('fs'); +var util = require('util'); +var join = require('path').join; +var spawn = require('child_process').spawn; + +var options = { + key: fs.readFileSync(join(common.fixturesDir, 'agent.key')), + cert: fs.readFileSync(join(common.fixturesDir, 'alice.crt')) +}; +var verified = false; + +var server = tls.createServer(options, function(cleartext) { + cleartext.end('World'); +}); +server.listen(common.PORT, function() { + var socket = tls.connect(common.PORT, function() { + var peerCert = socket.getPeerCertificate(); + common.debug(util.inspect(peerCert)); + assert.equal(peerCert.subject.subjectAltName, + 'uniformResourceIdentifier:http://localhost:8000/alice.foaf#me'); + verified = true; + server.close(); + }); + socket.end('Hello'); +}); + +process.on('exit', function() { + assert.ok(verified); +});