diff --git a/agents/MeshCmd-signed.exe b/agents/MeshCmd-signed.exe index 089899d8..f50713c0 100644 Binary files a/agents/MeshCmd-signed.exe and b/agents/MeshCmd-signed.exe differ diff --git a/agents/MeshCmd64-signed.exe b/agents/MeshCmd64-signed.exe index ed23dd54..dec1862d 100644 Binary files a/agents/MeshCmd64-signed.exe and b/agents/MeshCmd64-signed.exe differ diff --git a/agents/MeshService-signed.exe b/agents/MeshService-signed.exe index 8eabf604..af1baae8 100644 Binary files a/agents/MeshService-signed.exe and b/agents/MeshService-signed.exe differ diff --git a/agents/MeshService.exe b/agents/MeshService.exe index d6d1c78e..87cf8766 100644 Binary files a/agents/MeshService.exe and b/agents/MeshService.exe differ diff --git a/agents/MeshService64-signed.exe b/agents/MeshService64-signed.exe index e0a7eedd..55bb6144 100644 Binary files a/agents/MeshService64-signed.exe and b/agents/MeshService64-signed.exe differ diff --git a/agents/MeshService64.exe b/agents/MeshService64.exe index 02956c4e..fb9b0f52 100644 Binary files a/agents/MeshService64.exe and b/agents/MeshService64.exe differ diff --git a/agents/meshagent_arm b/agents/meshagent_arm index f628a87a..a9db917a 100644 Binary files a/agents/meshagent_arm and b/agents/meshagent_arm differ diff --git a/agents/meshagent_arm-linaro b/agents/meshagent_arm-linaro index d5ad7cbf..9f3becca 100644 Binary files a/agents/meshagent_arm-linaro and b/agents/meshagent_arm-linaro differ diff --git a/agents/meshagent_osx-x86-64 b/agents/meshagent_osx-x86-64 index bc4220e8..2371adc8 100644 Binary files a/agents/meshagent_osx-x86-64 and b/agents/meshagent_osx-x86-64 differ diff --git a/agents/meshagent_pi b/agents/meshagent_pi index a6a5a368..28a70502 100644 Binary files a/agents/meshagent_pi and b/agents/meshagent_pi differ diff --git a/agents/meshagent_pogo b/agents/meshagent_pogo index abbfe034..4cbb733c 100644 Binary files a/agents/meshagent_pogo and b/agents/meshagent_pogo differ diff --git a/agents/meshagent_poky b/agents/meshagent_poky index dc8fc8a8..481c6a67 100644 Binary files a/agents/meshagent_poky and b/agents/meshagent_poky differ diff --git a/agents/meshagent_poky64 b/agents/meshagent_poky64 index 27da5369..bb2ff043 100644 Binary files a/agents/meshagent_poky64 and b/agents/meshagent_poky64 differ diff --git a/agents/meshagent_x86 b/agents/meshagent_x86 index 221a9a4c..181ecd8e 100644 Binary files a/agents/meshagent_x86 and b/agents/meshagent_x86 differ diff --git a/agents/meshagent_x86-64 b/agents/meshagent_x86-64 index c5bf74bf..0aaffff2 100644 Binary files a/agents/meshagent_x86-64 and b/agents/meshagent_x86-64 differ diff --git a/agents/meshagent_x86-64_nokvm b/agents/meshagent_x86-64_nokvm index 405192cc..0c2a0cfd 100644 Binary files a/agents/meshagent_x86-64_nokvm and b/agents/meshagent_x86-64_nokvm differ diff --git a/agents/meshagent_x86_nokvm b/agents/meshagent_x86_nokvm index 911454ae..c5870125 100644 Binary files a/agents/meshagent_x86_nokvm and b/agents/meshagent_x86_nokvm differ diff --git a/agents/meshcore.js b/agents/meshcore.js index 04701b77..9ae58167 100644 --- a/agents/meshcore.js +++ b/agents/meshcore.js @@ -413,7 +413,7 @@ function createMeshCore(agent) { if (xurl != null) { var woptions = http.parseUri(xurl); woptions.rejectUnauthorized = 0; - //sendConsoleText(JSON.stringify(woptions)); + sendConsoleText(JSON.stringify(woptions)); var tunnel = http.request(woptions); tunnel.upgrade = onTunnelUpgrade; tunnel.onerror = function (e) { sendConsoleText('ERROR: ' + JSON.stringify(e)); } diff --git a/certoperations.js b/certoperations.js index c680cbb6..8dbb830f 100644 --- a/certoperations.js +++ b/certoperations.js @@ -28,14 +28,26 @@ module.exports.CertificateOperations = function () { // Return the certificate of the remote HTTPS server obj.loadCertificate = function (url, tag, func) { - var u = require('url').parse(url); + const u = require('url').parse(url); if (u.protocol == 'https:') { - var tlssocket = obj.tls.connect((u.port ? u.port : 443), u.hostname, { rejectUnauthorized: false }, function () { this.xxcert = this.getPeerCertificate(); this.end(); }); + // Read the certificate from HTTPS + const tlssocket = obj.tls.connect((u.port ? u.port : 443), u.hostname, { servername: u.hostname, rejectUnauthorized: false }, function () { this.xxcert = this.getPeerCertificate(); this.end(); }); tlssocket.xxurl = url; tlssocket.xxfunc = func; tlssocket.xxtag = tag; - tlssocket.on('end', function () { this.xxfunc(this.xxurl, this.xxcert, this.xxtag); }); + tlssocket.on('end', function () { this.xxfunc(this.xxurl, this.xxcert.raw.toString('binary'), this.xxtag); }); tlssocket.on('error', function () { this.xxfunc(this.xxurl, null, this.xxtag); }); + } else if (u.protocol == 'file:') { + // Read the certificate from a file + obj.fs.readFile(url.substring(7), 'utf8', function (err, data) { + if (err) { func(url, null, tag); return; } + var x1 = data.indexOf('-----BEGIN CERTIFICATE-----'), x2 = data.indexOf('-----END CERTIFICATE-----'); + if ((x1 >= 0) && (x2 > x1)) { + func(url, new Buffer(data.substring(x1 + 27, x2), 'base64').toString('binary'), tag); + } else { + func(url, data, tag); + } + }); } else { func(url, null, tag); } }; @@ -45,6 +57,43 @@ module.exports.CertificateOperations = function () { return obj.pki.getPublicKeyFingerprint(publickey, { encoding: "hex", md: obj.forge.md.sha384.create() }); }; + // Return the SHA384 hash of the certificate, return hex + obj.getCertHash = function (cert) { + try { + var md = obj.forge.md.sha384.create(); + md.update(obj.forge.asn1.toDer(obj.pki.certificateToAsn1(obj.pki.certificateFromPem(cert))).getBytes()); + return md.digest().toHex(); + } catch (ex) { + // If this is not an RSA certificate, hash the raw PKCS7 out of the PEM file + var x1 = cert.indexOf('-----BEGIN CERTIFICATE-----'), x2 = cert.indexOf('-----END CERTIFICATE-----'); + if ((x1 >= 0) && (x2 > x1)) { + return obj.crypto.createHash('sha384').update(new Buffer(cert.substring(x1 + 27, x2), 'base64')).digest('hex'); + } else { console.log('ERROR: Unable to decode certificate.'); return null; } + } + }; + + // Return the SHA384 hash of the certificate public key + obj.getPublicKeyHashBinary = function (cert) { + var publickey = obj.pki.certificateFromPem(cert).publicKey; + return obj.pki.getPublicKeyFingerprint(publickey, { encoding: "binary", md: obj.forge.md.sha384.create() }); + }; + + // Return the SHA384 hash of the certificate, return binary + obj.getCertHashBinary = function (cert) { + try { + // If this is a RSA certificate, we can use Forge to hash the ASN1 + var md = obj.forge.md.sha384.create(); + md.update(obj.forge.asn1.toDer(obj.pki.certificateToAsn1(obj.pki.certificateFromPem(cert))).getBytes()); + return md.digest().getBytes(); + } catch (ex) { + // If this is not an RSA certificate, hash the raw PKCS7 out of the PEM file + var x1 = cert.indexOf('-----BEGIN CERTIFICATE-----'), x2 = cert.indexOf('-----END CERTIFICATE-----'); + if ((x1 >= 0) && (x2 > x1)) { + return obj.crypto.createHash('sha384').update(new Buffer(cert.substring(x1 + 27, x2), 'base64')).digest('binary'); + } else { console.log('ERROR: Unable to decode certificate.'); return null; } + } + }; + // Create a self-signed certificate obj.GenerateRootCertificate = function (addThumbPrintToName, commonName, country, organization, strong) { var keys = obj.pki.rsa.generateKeyPair((strong == true) ? 3072 : 2048); diff --git a/meshagent.js b/meshagent.js index f662a8d6..ca85b668 100644 --- a/meshagent.js +++ b/meshagent.js @@ -198,7 +198,7 @@ module.exports.CreateMeshAgent = function (parent, db, ws, req, args, domain) { obj.receivedCommands += 1; // Agent can't send the same command twice on the same connection ever. Block DOS attack path. // Check that the server hash matches our own web certificate hash (SHA384) - 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; } + 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; } // Use our server private key to sign the ServerHash + AgentNonce + ServerNonce obj.agentnonce = msg.substring(50, 98); @@ -411,19 +411,30 @@ module.exports.CreateMeshAgent = function (parent, db, ws, req, args, domain) { }); } - // Get the web certificate hash for the speficied domain + // Get the web certificate private key hash for the specified domain function getWebCertHash(domain) { var hash = obj.parent.webCertificateHashs[domain.id]; if (hash != null) return hash; return obj.parent.webCertificateHash; } + // Get the web certificate hash for the specified domain + function getWebCertFullHash(domain) { + var hash = obj.parent.webCertificateFullHashs[domain.id]; + if (hash != null) return hash; + return obj.parent.webCertificateFullHash; + } + // Verify the agent signature function processAgentSignature(msg) { // Verify the signature. This is the fast way, without using forge. const verify = obj.parent.crypto.createVerify('SHA384'); - verify.end(new Buffer(getWebCertHash(obj.domain) + obj.nonce + obj.agentnonce, 'binary')); - if (verify.verify(obj.unauth.nodeCertPem, new Buffer(msg, 'binary')) !== true) { return false; } + verify.end(new Buffer(getWebCertHash(obj.domain) + obj.nonce + obj.agentnonce, 'binary')); // Test using the private key hash + if (verify.verify(obj.unauth.nodeCertPem, new Buffer(msg, 'binary')) !== true) { + const verify2 = obj.parent.crypto.createVerify('SHA384'); + verify2.end(new Buffer(getWebCertFullHash(obj.domain) + obj.nonce + obj.agentnonce, 'binary')); // Test using the full cert hash + if (verify2.verify(obj.unauth.nodeCertPem, new Buffer(msg, 'binary')) !== true) { return false; } + } // Connection is a success, clean up obj.nodeid = obj.unauth.nodeid; diff --git a/meshcentral.js b/meshcentral.js index d72d71d2..13ae02a2 100644 --- a/meshcentral.js +++ b/meshcentral.js @@ -417,22 +417,23 @@ function CreateMeshCentralServer(config, args) { webCertLoadCount++; obj.certificateOperations.loadCertificate(obj.config.domains[i].certurl, obj.config.domains[i], function (url, cert, xdomain) { if (cert != null) { - try { - // Decode a RSA certificate and hash the public key - var forgeCert = obj.certificateOperations.forge.pki.certificateFromAsn1(obj.certificateOperations.forge.asn1.fromDer(cert.raw.toString('binary'))); - var hash = obj.certificateOperations.forge.pki.getPublicKeyFingerprint(forgeCert.publicKey, { md: obj.certificateOperations.forge.md.sha384.create(), encoding: 'hex' }); - if (xdomain.certhash != hash) { - xdomain.certhash = hash; - console.log('Loaded RSA web certificate at ' + url + ', SHA384: ' + xdomain.certhash + '.'); - } - } catch (ex) { - // This may be a ECDSA certificate, hash the entire cert - var hash = obj.crypto.createHash('sha384').update(cert.raw).digest('hex'); - if (xdomain.certhash != hash) { - xdomain.certhash = hash; - console.log('Loaded non-RSA web certificate at ' + url + ', SHA384: ' + xdomain.certhash + '.'); - } + // Hash the entire cert + var hash = obj.crypto.createHash('sha384').update(cert).digest('hex'); + if (xdomain.certhash != hash) { + xdomain.certkeyhash = hash; + xdomain.certhash = hash; } + + try { + // Decode a RSA certificate and hash the public key, if this is not RSA, skip this. + var forgeCert = obj.certificateOperations.forge.pki.certificateFromAsn1(obj.certificateOperations.forge.asn1.fromDer(cert)); + xdomain.certkeyhash = obj.certificateOperations.forge.pki.getPublicKeyFingerprint(forgeCert.publicKey, { md: obj.certificateOperations.forge.md.sha384.create(), encoding: 'hex' }); + console.log('V1: ' + xdomain.certkeyhash); + } catch (ex) { } + + console.log('Loaded web certificate from ' + url); + console.log(' SHA384 cert hash: ' + xdomain.certhash); + if (xdomain.certhash != xdomain.certkeyhash) { console.log(' SHA384 key hash: ' + xdomain.certkeyhash); } } else { console.log('Failed to load web certificate at: ' + url); } diff --git a/package.json b/package.json index 5a34ac1d..a6842aea 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "meshcentral", - "version": "0.2.3-r", + "version": "0.2.3-v", "keywords": [ "Remote Management", "Intel AMT", diff --git a/webserver.js b/webserver.js index 27c6e8b2..b77afb86 100644 --- a/webserver.js +++ b/webserver.js @@ -102,33 +102,39 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) { } // Perform hash on web certificate and agent certificate - 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' }); + obj.webCertificateHash = parent.certificateOperations.getPublicKeyHashBinary(obj.certificates.web.cert); obj.webCertificateHashs = { '': obj.webCertificateHash }; - 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, '$'); - 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' }); - 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, '$'); + obj.webCertificateHashBase64 = new Buffer(obj.webCertificateHash, 'binary').toString('base64').replace(/\+/g, '@').replace(/\//g, '$'); + obj.webCertificateFullHash = parent.certificateOperations.getCertHashBinary(obj.certificates.web.cert); + obj.webCertificateFullHashs = { '': obj.webCertificateFullHash }; + obj.webCertificateFullHashBase64 = new Buffer(obj.webCertificateFullHash, 'binary').toString('base64').replace(/\+/g, '@').replace(/\//g, '$'); + obj.agentCertificateHashHex = parent.certificateOperations.getPublicKeyHash(obj.certificates.agent.cert); + obj.agentCertificateHashBase64 = new Buffer(obj.agentCertificateHashHex, 'hex').toString('base64').replace(/\+/g, '@').replace(/\//g, '$'); obj.agentCertificateAsn1 = parent.certificateOperations.forge.asn1.toDer(parent.certificateOperations.forge.pki.certificateToAsn1(parent.certificateOperations.forge.pki.certificateFromPem(parent.certificates.agent.cert))).getBytes(); // Compute the hash of all of the web certificates for each domain for (var i in obj.parent.config.domains) { if (obj.parent.config.domains[i].certhash != null) { // If the web certificate hash is provided, use it. - obj.webCertificateHashs[i] = new Buffer(obj.parent.config.domains[i].certhash, 'hex').toString('binary'); + obj.webCertificateHashs[i] = obj.webCertificateFullHashs[i] = new Buffer(obj.parent.config.domains[i].certhash, 'hex').toString('binary'); + if (obj.parent.config.domains[i].certkeyhash != null) { obj.webCertificateHashs[i] = new Buffer(obj.parent.config.domains[i].certkeyhash, 'hex').toString('binary'); } } else if ((obj.parent.config.domains[i].dns != null) && (obj.parent.config.domains[i].certs != null)) { // If the domain has a different DNS name, use a different certificate hash. + // Hash the full certificate + obj.webCertificateFullHashs[i] = parent.certificateOperations.getCertHashBinary(obj.parent.config.domains[i].certs.cert); try { - // Decode a RSA certificate and hash the public key - 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' }); + // Decode a RSA certificate and hash the public key. + obj.webCertificateHashs[i] = parent.certificateOperations.getPublicKeyHashBinary(obj.parent.config.domains[i].certs.cert); } catch (ex) { - // This may be a ECDSA certificate, hash the entire cert - var x1 = obj.parent.config.domains[i].certs.cert.indexOf('-----BEGIN CERTIFICATE-----'), x2 = obj.parent.config.domains[i].certs.cert.indexOf('-----END CERTIFICATE-----'); - if ((x1 >= 0) && (x2 > x1)) { - obj.webCertificateHashs[i] = obj.crypto.createHash('sha384').update(new Buffer(obj.parent.config.domains[i].certs.cert.substring(x1 + 27, x2), 'base64')).digest('binary'); - } else { console.log('ERROR: Unable to decode certificate for domain "' + i + '".'); } + // This may be a ECDSA certificate, hash the entire cert. + obj.webCertificateHashs[i] = obj.webCertificateFullHashs[i]; } } } + //console.log(new Buffer(obj.webCertificateHashs['devtest'], 'binary').toString('hex')); + //console.log(new Buffer(obj.webCertificateFullHashs['devtest'], 'binary').toString('hex')); + // If we are running the legacy swarm server, compute the hash for that certificate if (parent.certificates.swarmserver != null) { obj.swarmCertificateAsn1 = parent.certificateOperations.forge.asn1.toDer(parent.certificateOperations.forge.pki.certificateToAsn1(parent.certificateOperations.forge.pki.certificateFromPem(parent.certificates.swarmserver.cert))).getBytes();