Skip to content

Commit

Permalink
Adds support for certificates with empty Subject
Browse files Browse the repository at this point in the history
Node.js default verification does not consider the possibility of an empty subjects ([issue](nodejs/node#11771)).

In somce cases Windows Active Directory will use certificates with empty subjects, see this [article](https://blogs.technet.microsoft.com/askds/2008/09/16/third-party-application-fails-using-ldap-over-ssl/) for details, making necessary that the connector supports them to use `ldaps`.
This change adds a new configuration option `SSL_ENABLE_EMPTY_SUBJECT` that enables using a patched verification for the server identity that allows using certificates with empty subject. We want to avoid modifying the default behaviour unless required so this flag defaults to `false` and can be enabled when required.
  • Loading branch information
CriGoT committed Mar 30, 2017
1 parent db4fe87 commit 1f4dd2b
Show file tree
Hide file tree
Showing 4 changed files with 82 additions and 0 deletions.
1 change: 1 addition & 0 deletions lib/initConf.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ var defaults = {
ALLOW_PASSWORD_CHANGE_REQUIRED: false,
OVERRIDE_CONFIG: true,
CACHE_FILE: __dirname + '/../cache.db',
SSL_ENABLE_EMPTY_SUBJECT: false,
SSL_CA_FILE: '.+.(pem|crt|cer)$',
SSL_CA_PATH: false,
SSL_OPENSSLDIR_PATTERN: 'OPENSSLDIR\\s*\\:\\s*\\"([^\\"]*)\\"'
Expand Down
5 changes: 5 additions & 0 deletions lib/ldap.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,16 @@ var client, binder;
var crypto = require('./crypto');
var cb = require('cb');
var https = require('https');
var tls = require('./tls');

function createConnection () {
var tlsOptions = null;
if (nconf.get('LDAP_URL').toLowerCase().substr(0, 5) === 'ldaps') {
tlsOptions = { ca: https.globalAgent.options.ca };
if (nconf.get('SSL_ENABLE_EMPTY_SUBJECT')) {
// When enabled use the connector own verification function that fixes Node.js issue described in https://github.com/nodejs/node/issues/11771 for details
tlsoptions.checkServerIdentity= tls.checkServerIdentity;
}
}

var connection = ldap.createClient({
Expand Down
14 changes: 14 additions & 0 deletions lib/tls.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
var tls = require('tls');

// Fixes the cert representation when subject is empty and altNames are present
// See https://github.com/nodejs/node/issues/11771 for details
function checkServerIdentity(host, cert) {
// cert subject should be empty if altnames are defined
if (cert && !cert.subject && /(IP|DNS|URL)/.test(cert.subjectaltname)) cert.subject = tls.parseCertString("");

return tls.checkServerIdentity(host, cert);
}

module.exports = {
checkServerIdentity: checkServerIdentity
};
62 changes: 62 additions & 0 deletions test/certs.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
const tls = require('tls');
const tlsHelper = require('../lib/tls');
const expect = require('chai').expect;

describe('Connection to server with empty subject', function () {

const PORT=19876;
const cert = {
publicKey: '-----BEGIN CERTIFICATE-----\r\nMIIDHjCCAgagAwIBAgIBATANBgkqhkiG9w0BAQUFADB4MQswCQYDVQQGEwJVUzET\r\nMBEGA1UECBMKV2FzaGluZ3RvbjERMA8GA1UEBxMIQmVsbGV2dWUxDjAMBgNVBAoT\r\nBUF1dGgwMR8wHQYDVQQLExZBRCBMREFQIENvbm5lY3RvciBUZXN0MRAwDgYDVQQD\r\nEwdUZXN0IENBMB4XDTE3MDMzMDIwNDgzOVoXDTI3MDMyODIwNDgzOVowADCCASIw\r\nDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAMIYhS96xvPNDaq9iV5ddYrtlySr\r\nwktrWyCklH2S6gtJLen8IAwzHlg\/Cf6NbnqQIvusU5nehT0lv5jgNES85CifcBlV\r\nZyUClP48S8+xex6m6XU41PHSaDqpmzL3VdEBibqjv3Xnv+h3Np7JQnersHRMmUNZ\r\nFqzZvGg3yCgcKxpdqQRelB6AZDBQKXPxQ22ZZA2bMJlTfmXr65wkIRb5z8K4isi9\r\nyz4b1lKKkIobPiYY\/N8aki3BopfXSPnmuNOQV8pCjrB8+Ttwi3ukzE83KjtpZi+O\r\nJKqeGb0lHa1KJlTTqD12P3efDNrjKBdjjO39bMNRknlmudkMKtxDs93FlTECAwEA\r\nAaMrMCkwCwYDVR0PBAQDAgXgMBoGA1UdEQQTMBGHBH8AAAGCCWxvY2FsaG9zdDAN\r\nBgkqhkiG9w0BAQUFAAOCAQEAKIVqF\/LDMTiJxz9BoRzbpUV3\/z+T3O8IpRFNc+D5\r\n3JKevdzJRY3ShGivrafNfbS+eeYICR7\/3otnkbx0S9L81pc58R+qCHQYVTD+B\/eY\r\nYpBd\/vpiUE9\/RBSYDE\/O1FgC5ecYznugnWVl+y3wlT7g9XkkXJnZb\/SKvJwjWU6L\r\nF5CI4FwjlTbFIjWjDFUMYmfZXq\/ggj8nkQMHsy7NpqfYMZ6+QocL1V45QL+BBe9C\r\n5cdPptr9XMf3oS7Gy\/cBnjkQd0u4zJ8X2CYYkaJlwDQo+vbES\/Y0sGCwDiqTGDcH\r\ncfoOH7SdUHHKSLmUcrVoopY21mFcd3gZ178OET6UsQMOZw==\r\n-----END CERTIFICATE-----',
privateKey: '-----BEGIN RSA PRIVATE KEY-----\r\nMIIEowIBAAKCAQEAwhiFL3rG880Nqr2JXl11iu2XJKvCS2tbIKSUfZLqC0kt6fwg\r\nDDMeWD8J\/o1uepAi+6xTmd6FPSW\/mOA0RLzkKJ9wGVVnJQKU\/jxLz7F7HqbpdTjU\r\n8dJoOqmbMvdV0QGJuqO\/dee\/6Hc2nslCd6uwdEyZQ1kWrNm8aDfIKBwrGl2pBF6U\r\nHoBkMFApc\/FDbZlkDZswmVN+ZevrnCQhFvnPwriKyL3LPhvWUoqQihs+Jhj83xqS\r\nLcGil9dI+ea405BXykKOsHz5O3CLe6TMTzcqO2lmL44kqp4ZvSUdrUomVNOoPXY\/\r\nd58M2uMoF2OM7f1sw1GSeWa52Qwq3EOz3cWVMQIDAQABAoIBABw7rtvqMxhxomRM\r\nr7evRpLP3qVx6pBH7HiCGCtv\/GVp3qjjiNHdebOCb\/S8I+7mGoCbX4nJSX5MiGM3\r\nccLx6wpRrt+wgZFrn7qfkLOEcJFT3C+19Zu7bHfkBfRS8AO4Ao3IlegTruGkvag5\r\nRFbd\/YvdPIoEYn0AKxzJyG61MjviVONIJL7PkMk4q2nq12QjtOHp3TM1oSY6rwBE\r\nR585i3TZeQilhVOlvm+i6by47Tw8ljyCc9rsN\/sajoxjs650296MiRMElbs321Dl\r\n13DIzF9pTy654ZIjKvQgQta2LrK1kp8a4BUj03POf8oCH23ufkTmEB1hDQHapL7I\r\nC3ue05UCgYEA7SKNAm7q3e36QHPw3QZAvSpV51W9zcY7JymvMWCj\/izG0h7g6Th1\r\npI4Gu9wWEvJdZSEnEc2d5E4E6EeJnA\/OQ1cZqmbNqed\/r3xU+FfhpE7sP6rWYr5R\r\nW9TOr3U+bV+ts+iToj0Jlq40OlPFRwxNVCyDr2QSPlTqAzkk6lGGIzcCgYEA0Ylw\r\n5MYNdzMfunkUbMTE349ioninKY0xSOttSTQPw9NqRcBupcipY0a\/drNECQoPXWKu\r\nt+ZT13EmCZxBjuPqMO5a\/BexD+dhf3b4Ksui1PMhX8U4ODAdBQlBRT2GAUdQgl8a\r\ncFQSIRV0sW+svE6AsXV4Kx73V5aC0D4Zz+vXDtcCgYEAxsmvAbovw4mKvtsysGZc\r\ngPdreflDmquxzNvB1JfaAepRZbWi\/39oB2FUPcl667knF+7ZzK\/cy5WnwXyu3BfX\r\n5lWu201A3UyGmnqU1Hb\/XfkXTSwOekpm85+LAEU95vxNJkMy989JKXqxp6+v8iZa\r\n8NQ8NBykuoH+hmMyEgfzdbMCgYA0sYyba5b9T\/T9ru9M\/xrHYcabNx5Km8A2J0Zf\r\nb2E7jNIf4mmw9UprteH2VtSYNVhx0pw\/kQOqnUDEj\/AIoBZH4dktpkOXzUc+h8uW\r\n74juZooRDIa70pWpq48ne3ZUofuEHaiHcQzyFvQ2nu\/glxlUB0eGCI6JD0esWMGj\r\nARsfFwKBgBWeWFjgjotC12QZfV0ZMMw9sOnQQtbjxtzEBnalLfL+GjPJsawd9E+z\r\n0R39qIs0tN9\/\/93fw0WGu9BjraIzbFptEPirjT\/oe+p+GzMPztRcoZZ8kvM16BD5\r\n5D2UYsL+GUyeYTeMbd8bsUnE+Ul+We8tIPWxmrePipde9Uts9Qy4\r\n-----END RSA PRIVATE KEY-----',
ca : '-----BEGIN CERTIFICATE-----\r\nMIIEUTCCAzmgAwIBAgIJAMlqt\/8UuGbMMA0GCSqGSIb3DQEBBQUAMHgxCzAJBgNV\r\nBAYTAlVTMRMwEQYDVQQIEwpXYXNoaW5ndG9uMREwDwYDVQQHEwhCZWxsZXZ1ZTEO\r\nMAwGA1UEChMFQXV0aDAxHzAdBgNVBAsTFkFEIExEQVAgQ29ubmVjdG9yIFRlc3Qx\r\nEDAOBgNVBAMTB1Rlc3QgQ0EwHhcNMTcwMzMwMjAzMjE3WhcNMjcwMzI4MjAzMjE3\r\nWjB4MQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjERMA8GA1UEBxMI\r\nQmVsbGV2dWUxDjAMBgNVBAoTBUF1dGgwMR8wHQYDVQQLExZBRCBMREFQIENvbm5l\r\nY3RvciBUZXN0MRAwDgYDVQQDEwdUZXN0IENBMIIBIjANBgkqhkiG9w0BAQEFAAOC\r\nAQ8AMIIBCgKCAQEAvAKizExxRjxEKxkboxsE7ErdiOA3YBOaiRXAxjZelANGuuzo\r\nXPYPiwtf5gaHGRzfiJLzJDmIxbJsiR61qPC1\/mcaEWn54OKhwQa05FKusDm6ej8D\r\nWs6\/QDFwZYOukdPbz\/ZpV0In2YPHfdHucx\/JhKE3V5gYFd04U8PcKwn2eH\/fSjIA\r\n1ghSpHBa9F0jIPrBrNe1zPe33AFaE1SKuT\/JcbNaYx1jh1C1KWM7OX5EQ4M\/HUP3\r\n9VWdXVxt48Yj7OoLPrwz4\/5d\/KqCWoznQkcJ2FyA7LJqN1GBP1EoLLAew7yKMGzI\r\n5GCbS8o3F5baLeWJ3jlvylCFE0LDAj0UoxPSUQIDAQABo4HdMIHaMB0GA1UdDgQW\r\nBBTBFNN+vNPh\/ilPbnXsAQa0kAnUuzCBqgYDVR0jBIGiMIGfgBTBFNN+vNPh\/ilP\r\nbnXsAQa0kAnUu6F8pHoweDELMAkGA1UEBhMCVVMxEzARBgNVBAgTCldhc2hpbmd0\r\nb24xETAPBgNVBAcTCEJlbGxldnVlMQ4wDAYDVQQKEwVBdXRoMDEfMB0GA1UECxMW\r\nQUQgTERBUCBDb25uZWN0b3IgVGVzdDEQMA4GA1UEAxMHVGVzdCBDQYIJAMlqt\/8U\r\nuGbMMAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQEFBQADggEBAFQWabxlItAUN+\/3\r\n2gwDgqSVXrPGJ9XyQfVA3RmrVxD+PZqxo9+preS8SVGDLGzt9bL4MFO8+d\/0IVNM\r\nn+j4CCXRd\/BgKHpg6Cj14PPsrJuoCIhFJv9x5XAYqOoTf8KlkZKTfPXuZ0gAStuh\r\nVpKwC7rYrNjRigj0UIIe1ghtzhs2av5BRbvYFQlBgrQI4uMQKhlm8STPsV9vr8iF\r\nWcwmf2jvSmVVcRNPSjxOoqraYxhaIwCAEpQKKPsyoPUwSvCPcvZqwOmFd2FWwST9\r\nIc+EpvwplAizLJmh8M8mEEXfz1RkeYVsiWgcRV22Edd20lvlbnptRkMSEVbE2XMB\r\n9c2aY1k=\r\n-----END CERTIFICATE-----'
};

var server;

before(function(done) {
server=tls.createServer({
key: cert.privateKey,
cert: cert.publicKey
});
server.on('connection', function(){});
server.on('secureConnection', function(){});
server.listen(PORT, done);
});


after(function(done) {
if (server) {
server.close(done);
} else {
done();
}
});

// This test is mainly to keep track of the original Node.Js issue. When this test fails you can safely remove ../lib/tls.js
it('should fail when using node provided server identity verification', function(done) {
var socket = tls.connect({
port: PORT,
ca: cert.ca,
servername: 'localhost',
}, function() {
socket.end();
done(new Error('Certificate should not pass server Identity validation'));
});
socket.on('error', function(e) {
expect(e).to.have.property('reason','Cert is empty');
done();
});
});

it('should connect when using connector\'s server identity verification', function(done) {
var socket = tls.connect(PORT, {
ca: cert.ca,
servername: 'localhost',
checkServerIdentity: tlsHelper.checkServerIdentity
}, function() {
socket.end();
done();
});
socket.on('error', done);
});
});

0 comments on commit 1f4dd2b

Please sign in to comment.