Skip to content

Commit 1f4dd2b

Browse files
committed
Adds support for certificates with empty Subject
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.
1 parent db4fe87 commit 1f4dd2b

File tree

4 files changed

+82
-0
lines changed

4 files changed

+82
-0
lines changed

lib/initConf.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ var defaults = {
2020
ALLOW_PASSWORD_CHANGE_REQUIRED: false,
2121
OVERRIDE_CONFIG: true,
2222
CACHE_FILE: __dirname + '/../cache.db',
23+
SSL_ENABLE_EMPTY_SUBJECT: false,
2324
SSL_CA_FILE: '.+.(pem|crt|cer)$',
2425
SSL_CA_PATH: false,
2526
SSL_OPENSSLDIR_PATTERN: 'OPENSSLDIR\\s*\\:\\s*\\"([^\\"]*)\\"'

lib/ldap.js

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,16 @@ var client, binder;
55
var crypto = require('./crypto');
66
var cb = require('cb');
77
var https = require('https');
8+
var tls = require('./tls');
89

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

1520
var connection = ldap.createClient({

lib/tls.js

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
var tls = require('tls');
2+
3+
// Fixes the cert representation when subject is empty and altNames are present
4+
// See https://github.com/nodejs/node/issues/11771 for details
5+
function checkServerIdentity(host, cert) {
6+
// cert subject should be empty if altnames are defined
7+
if (cert && !cert.subject && /(IP|DNS|URL)/.test(cert.subjectaltname)) cert.subject = tls.parseCertString("");
8+
9+
return tls.checkServerIdentity(host, cert);
10+
}
11+
12+
module.exports = {
13+
checkServerIdentity: checkServerIdentity
14+
};

test/certs.js

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
const tls = require('tls');
2+
const tlsHelper = require('../lib/tls');
3+
const expect = require('chai').expect;
4+
5+
describe('Connection to server with empty subject', function () {
6+
7+
const PORT=19876;
8+
const cert = {
9+
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-----',
10+
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-----',
11+
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-----'
12+
};
13+
14+
var server;
15+
16+
before(function(done) {
17+
server=tls.createServer({
18+
key: cert.privateKey,
19+
cert: cert.publicKey
20+
});
21+
server.on('connection', function(){});
22+
server.on('secureConnection', function(){});
23+
server.listen(PORT, done);
24+
});
25+
26+
27+
after(function(done) {
28+
if (server) {
29+
server.close(done);
30+
} else {
31+
done();
32+
}
33+
});
34+
35+
// This test is mainly to keep track of the original Node.Js issue. When this test fails you can safely remove ../lib/tls.js
36+
it('should fail when using node provided server identity verification', function(done) {
37+
var socket = tls.connect({
38+
port: PORT,
39+
ca: cert.ca,
40+
servername: 'localhost',
41+
}, function() {
42+
socket.end();
43+
done(new Error('Certificate should not pass server Identity validation'));
44+
});
45+
socket.on('error', function(e) {
46+
expect(e).to.have.property('reason','Cert is empty');
47+
done();
48+
});
49+
});
50+
51+
it('should connect when using connector\'s server identity verification', function(done) {
52+
var socket = tls.connect(PORT, {
53+
ca: cert.ca,
54+
servername: 'localhost',
55+
checkServerIdentity: tlsHelper.checkServerIdentity
56+
}, function() {
57+
socket.end();
58+
done();
59+
});
60+
socket.on('error', done);
61+
});
62+
});

0 commit comments

Comments
 (0)