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();