Fixed many TLS-SNI problems, updated agents
Ylian Saint-Hilaire committed
Nov 30, 2018 at 16:42 UTC
7f76495d7202b886ffc4d23907d38c06c667a3c2
23 files changed
+103
-36
agents/MeshCmd-signed.exe
Binary files a/agents/MeshCmd-signed.exe and b/agents/MeshCmd-signed.exe differ
agents/MeshCmd64-signed.exe
Binary files a/agents/MeshCmd64-signed.exe and b/agents/MeshCmd64-signed.exe differ
agents/MeshService-signed.exe
Binary files a/agents/MeshService-signed.exe and b/agents/MeshService-signed.exe differ
agents/MeshService.exe
Binary files a/agents/MeshService.exe and b/agents/MeshService.exe differ
agents/MeshService64-signed.exe
Binary files a/agents/MeshService64-signed.exe and b/agents/MeshService64-signed.exe differ
agents/MeshService64.exe
Binary files a/agents/MeshService64.exe and b/agents/MeshService64.exe differ
agents/meshagent_arm
Binary files a/agents/meshagent_arm and b/agents/meshagent_arm differ
agents/meshagent_arm-linaro
Binary files a/agents/meshagent_arm-linaro and b/agents/meshagent_arm-linaro differ
agents/meshagent_osx-x86-64
Binary files a/agents/meshagent_osx-x86-64 and b/agents/meshagent_osx-x86-64 differ
agents/meshagent_pi
Binary files a/agents/meshagent_pi and b/agents/meshagent_pi differ
agents/meshagent_pogo
Binary files a/agents/meshagent_pogo and b/agents/meshagent_pogo differ
agents/meshagent_poky
Binary files a/agents/meshagent_poky and b/agents/meshagent_poky differ
agents/meshagent_poky64
Binary files a/agents/meshagent_poky64 and b/agents/meshagent_poky64 differ
agents/meshagent_x86
Binary files a/agents/meshagent_x86 and b/agents/meshagent_x86 differ
agents/meshagent_x86-64
Binary files a/agents/meshagent_x86-64 and b/agents/meshagent_x86-64 differ
agents/meshagent_x86-64_nokvm
Binary files a/agents/meshagent_x86-64_nokvm and b/agents/meshagent_x86-64_nokvm differ
agents/meshagent_x86_nokvm
Binary files a/agents/meshagent_x86_nokvm and b/agents/meshagent_x86_nokvm differ
agents/meshcore.js
+1
-1
@@ -413,7 +413,7 @@ function createMeshCore(agent) {
413
if (xurl != null) {
414
var woptions = http.parseUri(xurl);
415
woptions.rejectUnauthorized = 0;
416
- //sendConsoleText(JSON.stringify(woptions));
416
+ sendConsoleText(JSON.stringify(woptions));
417
var tunnel = http.request(woptions);
418
tunnel.upgrade = onTunnelUpgrade;
419
tunnel.onerror = function (e) { sendConsoleText('ERROR: ' + JSON.stringify(e)); }
certoperations.js
+52
-3
@@ -28,14 +28,26 @@ module.exports.CertificateOperations = function () {
28
29
// Return the certificate of the remote HTTPS server
30
obj.loadCertificate = function (url, tag, func) {
31
- var u = require('url').parse(url);
31
+ const u = require('url').parse(url);
32
if (u.protocol == 'https:') {
33
- var tlssocket = obj.tls.connect((u.port ? u.port : 443), u.hostname, { rejectUnauthorized: false }, function () { this.xxcert = this.getPeerCertificate(); this.end(); });
33
+ // Read the certificate from HTTPS
34
+ const tlssocket = obj.tls.connect((u.port ? u.port : 443), u.hostname, { servername: u.hostname, rejectUnauthorized: false }, function () { this.xxcert = this.getPeerCertificate(); this.end(); });
35
tlssocket.xxurl = url;
36
tlssocket.xxfunc = func;
37
tlssocket.xxtag = tag;
37
- tlssocket.on('end', function () { this.xxfunc(this.xxurl, this.xxcert, this.xxtag); });
38
+ tlssocket.on('end', function () { this.xxfunc(this.xxurl, this.xxcert.raw.toString('binary'), this.xxtag); });
39
tlssocket.on('error', function () { this.xxfunc(this.xxurl, null, this.xxtag); });
40
+ } else if (u.protocol == 'file:') {
41
+ // Read the certificate from a file
42
+ obj.fs.readFile(url.substring(7), 'utf8', function (err, data) {
43
+ if (err) { func(url, null, tag); return; }
44
+ var x1 = data.indexOf('-----BEGIN CERTIFICATE-----'), x2 = data.indexOf('-----END CERTIFICATE-----');
45
+ if ((x1 >= 0) && (x2 > x1)) {
46
+ func(url, new Buffer(data.substring(x1 + 27, x2), 'base64').toString('binary'), tag);
47
+ } else {
48
+ func(url, data, tag);
49
+ }
50
+ });
51
} else { func(url, null, tag); }
52
};
53
@@ -45,6 +57,43 @@ module.exports.CertificateOperations = function () {
57
return obj.pki.getPublicKeyFingerprint(publickey, { encoding: "hex", md: obj.forge.md.sha384.create() });
58
};
59
60
+ // Return the SHA384 hash of the certificate, return hex
61
+ obj.getCertHash = function (cert) {
62
+ try {
63
+ var md = obj.forge.md.sha384.create();
64
+ md.update(obj.forge.asn1.toDer(obj.pki.certificateToAsn1(obj.pki.certificateFromPem(cert))).getBytes());
65
+ return md.digest().toHex();
66
+ } catch (ex) {
67
+ // If this is not an RSA certificate, hash the raw PKCS7 out of the PEM file
68
+ var x1 = cert.indexOf('-----BEGIN CERTIFICATE-----'), x2 = cert.indexOf('-----END CERTIFICATE-----');
69
+ if ((x1 >= 0) && (x2 > x1)) {
70
+ return obj.crypto.createHash('sha384').update(new Buffer(cert.substring(x1 + 27, x2), 'base64')).digest('hex');
71
+ } else { console.log('ERROR: Unable to decode certificate.'); return null; }
72
+ }
73
+ };
74
+
75
+ // Return the SHA384 hash of the certificate public key
76
+ obj.getPublicKeyHashBinary = function (cert) {
77
+ var publickey = obj.pki.certificateFromPem(cert).publicKey;
78
+ return obj.pki.getPublicKeyFingerprint(publickey, { encoding: "binary", md: obj.forge.md.sha384.create() });
79
+ };
80
+
81
+ // Return the SHA384 hash of the certificate, return binary
82
+ obj.getCertHashBinary = function (cert) {
83
+ try {
84
+ // If this is a RSA certificate, we can use Forge to hash the ASN1
85
+ var md = obj.forge.md.sha384.create();
86
+ md.update(obj.forge.asn1.toDer(obj.pki.certificateToAsn1(obj.pki.certificateFromPem(cert))).getBytes());
87
+ return md.digest().getBytes();
88
+ } catch (ex) {
89
+ // If this is not an RSA certificate, hash the raw PKCS7 out of the PEM file
90
+ var x1 = cert.indexOf('-----BEGIN CERTIFICATE-----'), x2 = cert.indexOf('-----END CERTIFICATE-----');
91
+ if ((x1 >= 0) && (x2 > x1)) {
92
+ return obj.crypto.createHash('sha384').update(new Buffer(cert.substring(x1 + 27, x2), 'base64')).digest('binary');
93
+ } else { console.log('ERROR: Unable to decode certificate.'); return null; }
94
+ }
95
+ };
96
+
97
// Create a self-signed certificate
98
obj.GenerateRootCertificate = function (addThumbPrintToName, commonName, country, organization, strong) {
99
var keys = obj.pki.rsa.generateKeyPair((strong == true) ? 3072 : 2048);
meshagent.js
+15
-4
@@ -198,7 +198,7 @@ module.exports.CreateMeshAgent = function (parent, db, ws, req, args, domain) {
198
obj.receivedCommands += 1; // Agent can't send the same command twice on the same connection ever. Block DOS attack path.
199
200
// Check that the server hash matches our own web certificate hash (SHA384)
201
- if (getWebCertHash(obj.domain) != msg.substring(2, 50)) { console.log('Agent connected with bad web certificate hash (' + (new Buffer(getWebCertHash(obj.domain), 'binary').toString('hex').substring(0, 10)) + ' != ' + (new Buffer(msg.substring(2, 50), 'binary').toString('hex').substring(0, 10)) + '), holding connection (' + obj.remoteaddrport + ').'); return; }
201
+ if ((getWebCertHash(obj.domain) != msg.substring(2, 50)) && (getWebCertFullHash(obj.domain) != msg.substring(2, 50))) { console.log('Agent connected with bad web certificate hash (Agent:' + (new Buffer(msg.substring(2, 50), 'binary').toString('hex').substring(0, 10)) + ' != Server:' + (new Buffer(getWebCertHash(obj.domain), 'binary').toString('hex').substring(0, 10)) + ' or ' + (new Buffer(getWebCertFullHash(obj.domain), 'binary').toString('hex').substring(0, 10)) + '), holding connection (' + obj.remoteaddrport + ').'); return; }
202
203
// Use our server private key to sign the ServerHash + AgentNonce + ServerNonce
204
obj.agentnonce = msg.substring(50, 98);
@@ -411,19 +411,30 @@ module.exports.CreateMeshAgent = function (parent, db, ws, req, args, domain) {
411
});
412
}
413
414
- // Get the web certificate hash for the speficied domain
414
+ // Get the web certificate private key hash for the specified domain
415
function getWebCertHash(domain) {
416
var hash = obj.parent.webCertificateHashs[domain.id];
417
if (hash != null) return hash;
418
return obj.parent.webCertificateHash;
419
}
420
421
+ // Get the web certificate hash for the specified domain
422
+ function getWebCertFullHash(domain) {
423
+ var hash = obj.parent.webCertificateFullHashs[domain.id];
424
+ if (hash != null) return hash;
425
+ return obj.parent.webCertificateFullHash;
426
+ }
427
+
428
// Verify the agent signature
429
function processAgentSignature(msg) {
430
// Verify the signature. This is the fast way, without using forge.
431
const verify = obj.parent.crypto.createVerify('SHA384');
425
- verify.end(new Buffer(getWebCertHash(obj.domain) + obj.nonce + obj.agentnonce, 'binary'));
426
- if (verify.verify(obj.unauth.nodeCertPem, new Buffer(msg, 'binary')) !== true) { return false; }
432
+ verify.end(new Buffer(getWebCertHash(obj.domain) + obj.nonce + obj.agentnonce, 'binary')); // Test using the private key hash
433
+ if (verify.verify(obj.unauth.nodeCertPem, new Buffer(msg, 'binary')) !== true) {
434
+ const verify2 = obj.parent.crypto.createVerify('SHA384');
435
+ verify2.end(new Buffer(getWebCertFullHash(obj.domain) + obj.nonce + obj.agentnonce, 'binary')); // Test using the full cert hash
436
+ if (verify2.verify(obj.unauth.nodeCertPem, new Buffer(msg, 'binary')) !== true) { return false; }
437
+ }
438
439
// Connection is a success, clean up
440
obj.nodeid = obj.unauth.nodeid;
meshcentral.js
+16
-15
@@ -417,22 +417,23 @@ function CreateMeshCentralServer(config, args) {
417
webCertLoadCount++;
418
obj.certificateOperations.loadCertificate(obj.config.domains[i].certurl, obj.config.domains[i], function (url, cert, xdomain) {
419
if (cert != null) {
420
- try {
421
- // Decode a RSA certificate and hash the public key
422
- var forgeCert = obj.certificateOperations.forge.pki.certificateFromAsn1(obj.certificateOperations.forge.asn1.fromDer(cert.raw.toString('binary')));
423
- var hash = obj.certificateOperations.forge.pki.getPublicKeyFingerprint(forgeCert.publicKey, { md: obj.certificateOperations.forge.md.sha384.create(), encoding: 'hex' });
424
- if (xdomain.certhash != hash) {
425
- xdomain.certhash = hash;
426
- console.log('Loaded RSA web certificate at ' + url + ', SHA384: ' + xdomain.certhash + '.');
427
- }
428
- } catch (ex) {
429
- // This may be a ECDSA certificate, hash the entire cert
430
- var hash = obj.crypto.createHash('sha384').update(cert.raw).digest('hex');
431
- if (xdomain.certhash != hash) {
432
- xdomain.certhash = hash;
433
- console.log('Loaded non-RSA web certificate at ' + url + ', SHA384: ' + xdomain.certhash + '.');
434
- }
420
+ // Hash the entire cert
421
+ var hash = obj.crypto.createHash('sha384').update(cert).digest('hex');
422
+ if (xdomain.certhash != hash) {
423
+ xdomain.certkeyhash = hash;
424
+ xdomain.certhash = hash;
425
}
426
+
427
+ try {
428
+ // Decode a RSA certificate and hash the public key, if this is not RSA, skip this.
429
+ var forgeCert = obj.certificateOperations.forge.pki.certificateFromAsn1(obj.certificateOperations.forge.asn1.fromDer(cert));
430
+ xdomain.certkeyhash = obj.certificateOperations.forge.pki.getPublicKeyFingerprint(forgeCert.publicKey, { md: obj.certificateOperations.forge.md.sha384.create(), encoding: 'hex' });
431
+ console.log('V1: ' + xdomain.certkeyhash);
432
+ } catch (ex) { }
433
+
434
+ console.log('Loaded web certificate from ' + url);
435
+ console.log(' SHA384 cert hash: ' + xdomain.certhash);
436
+ if (xdomain.certhash != xdomain.certkeyhash) { console.log(' SHA384 key hash: ' + xdomain.certkeyhash); }
437
} else {
438
console.log('Failed to load web certificate at: ' + url);
439
}
package.json
+1
-1
@@ -1,6 +1,6 @@
1
{
2
"name": "meshcentral",
3
- "version": "0.2.3-r",
3
+ "version": "0.2.3-v",
4
"keywords": [
5
"Remote Management",
6
"Intel AMT",
webserver.js
+18
-12
@@ -102,33 +102,39 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) {
102
}
103
104
// Perform hash on web certificate and agent certificate
105
- obj.webCertificateHash = parent.certificateOperations.forge.pki.getPublicKeyFingerprint(parent.certificateOperations.forge.pki.certificateFromPem(obj.certificates.web.cert).publicKey, { md: parent.certificateOperations.forge.md.sha384.create(), encoding: 'binary' });
105
+ obj.webCertificateHash = parent.certificateOperations.getPublicKeyHashBinary(obj.certificates.web.cert);
106
obj.webCertificateHashs = { '': obj.webCertificateHash };
107
- obj.webCertificateHashBase64 = new Buffer(parent.certificateOperations.forge.pki.getPublicKeyFingerprint(parent.certificateOperations.forge.pki.certificateFromPem(obj.certificates.web.cert).publicKey, { md: parent.certificateOperations.forge.md.sha384.create(), encoding: 'binary' }), 'binary').toString('base64').replace(/\+/g, '@').replace(/\//g, '$');
108
- obj.agentCertificateHashHex = parent.certificateOperations.forge.pki.getPublicKeyFingerprint(parent.certificateOperations.forge.pki.certificateFromPem(obj.certificates.agent.cert).publicKey, { md: parent.certificateOperations.forge.md.sha384.create(), encoding: 'hex' });
109
- obj.agentCertificateHashBase64 = new Buffer(parent.certificateOperations.forge.pki.getPublicKeyFingerprint(parent.certificateOperations.forge.pki.certificateFromPem(obj.certificates.agent.cert).publicKey, { md: parent.certificateOperations.forge.md.sha384.create(), encoding: 'binary' }), 'binary').toString('base64').replace(/\+/g, '@').replace(/\//g, '$');
107
+ obj.webCertificateHashBase64 = new Buffer(obj.webCertificateHash, 'binary').toString('base64').replace(/\+/g, '@').replace(/\//g, '$');
108
+ obj.webCertificateFullHash = parent.certificateOperations.getCertHashBinary(obj.certificates.web.cert);
109
+ obj.webCertificateFullHashs = { '': obj.webCertificateFullHash };
110
+ obj.webCertificateFullHashBase64 = new Buffer(obj.webCertificateFullHash, 'binary').toString('base64').replace(/\+/g, '@').replace(/\//g, '$');
111
+ obj.agentCertificateHashHex = parent.certificateOperations.getPublicKeyHash(obj.certificates.agent.cert);
112
+ obj.agentCertificateHashBase64 = new Buffer(obj.agentCertificateHashHex, 'hex').toString('base64').replace(/\+/g, '@').replace(/\//g, '$');
113
obj.agentCertificateAsn1 = parent.certificateOperations.forge.asn1.toDer(parent.certificateOperations.forge.pki.certificateToAsn1(parent.certificateOperations.forge.pki.certificateFromPem(parent.certificates.agent.cert))).getBytes();
114
115
// Compute the hash of all of the web certificates for each domain
116
for (var i in obj.parent.config.domains) {
117
if (obj.parent.config.domains[i].certhash != null) {
118
// If the web certificate hash is provided, use it.
116
- obj.webCertificateHashs[i] = new Buffer(obj.parent.config.domains[i].certhash, 'hex').toString('binary');
119
+ obj.webCertificateHashs[i] = obj.webCertificateFullHashs[i] = new Buffer(obj.parent.config.domains[i].certhash, 'hex').toString('binary');
120
+ if (obj.parent.config.domains[i].certkeyhash != null) { obj.webCertificateHashs[i] = new Buffer(obj.parent.config.domains[i].certkeyhash, 'hex').toString('binary'); }
121
} else if ((obj.parent.config.domains[i].dns != null) && (obj.parent.config.domains[i].certs != null)) {
122
// If the domain has a different DNS name, use a different certificate hash.
123
+ // Hash the full certificate
124
+ obj.webCertificateFullHashs[i] = parent.certificateOperations.getCertHashBinary(obj.parent.config.domains[i].certs.cert);
125
try {
120
- // Decode a RSA certificate and hash the public key
121
- obj.webCertificateHashs[i] = parent.certificateOperations.forge.pki.getPublicKeyFingerprint(parent.certificateOperations.forge.pki.certificateFromPem(obj.parent.config.domains[i].certs.cert).publicKey, { md: parent.certificateOperations.forge.md.sha384.create(), encoding: 'binary' });
126
+ // Decode a RSA certificate and hash the public key.
127
+ obj.webCertificateHashs[i] = parent.certificateOperations.getPublicKeyHashBinary(obj.parent.config.domains[i].certs.cert);
128
} catch (ex) {
123
- // This may be a ECDSA certificate, hash the entire cert
124
- var x1 = obj.parent.config.domains[i].certs.cert.indexOf('-----BEGIN CERTIFICATE-----'), x2 = obj.parent.config.domains[i].certs.cert.indexOf('-----END CERTIFICATE-----');
125
- if ((x1 >= 0) && (x2 > x1)) {
126
- obj.webCertificateHashs[i] = obj.crypto.createHash('sha384').update(new Buffer(obj.parent.config.domains[i].certs.cert.substring(x1 + 27, x2), 'base64')).digest('binary');
127
- } else { console.log('ERROR: Unable to decode certificate for domain "' + i + '".'); }
129
+ // This may be a ECDSA certificate, hash the entire cert.
130
+ obj.webCertificateHashs[i] = obj.webCertificateFullHashs[i];
131
}
132
}
133
}
134
135
+ //console.log(new Buffer(obj.webCertificateHashs['devtest'], 'binary').toString('hex'));
136
+ //console.log(new Buffer(obj.webCertificateFullHashs['devtest'], 'binary').toString('hex'));
137
+
138
// If we are running the legacy swarm server, compute the hash for that certificate
139
if (parent.certificates.swarmserver != null) {
140
obj.swarmCertificateAsn1 = parent.certificateOperations.forge.asn1.toDer(parent.certificateOperations.forge.pki.certificateToAsn1(parent.certificateOperations.forge.pki.certificateFromPem(parent.certificates.swarmserver.cert))).getBytes();