Fixed many TLS-SNI problems, updated agents
This commit is contained in:
parent
d30b5f348e
commit
7f76495d72
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
@ -413,7 +413,7 @@ function createMeshCore(agent) {
|
||||||
if (xurl != null) {
|
if (xurl != null) {
|
||||||
var woptions = http.parseUri(xurl);
|
var woptions = http.parseUri(xurl);
|
||||||
woptions.rejectUnauthorized = 0;
|
woptions.rejectUnauthorized = 0;
|
||||||
//sendConsoleText(JSON.stringify(woptions));
|
sendConsoleText(JSON.stringify(woptions));
|
||||||
var tunnel = http.request(woptions);
|
var tunnel = http.request(woptions);
|
||||||
tunnel.upgrade = onTunnelUpgrade;
|
tunnel.upgrade = onTunnelUpgrade;
|
||||||
tunnel.onerror = function (e) { sendConsoleText('ERROR: ' + JSON.stringify(e)); }
|
tunnel.onerror = function (e) { sendConsoleText('ERROR: ' + JSON.stringify(e)); }
|
||||||
|
|
|
@ -28,14 +28,26 @@ module.exports.CertificateOperations = function () {
|
||||||
|
|
||||||
// Return the certificate of the remote HTTPS server
|
// Return the certificate of the remote HTTPS server
|
||||||
obj.loadCertificate = function (url, tag, func) {
|
obj.loadCertificate = function (url, tag, func) {
|
||||||
var u = require('url').parse(url);
|
const u = require('url').parse(url);
|
||||||
if (u.protocol == 'https:') {
|
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.xxurl = url;
|
||||||
tlssocket.xxfunc = func;
|
tlssocket.xxfunc = func;
|
||||||
tlssocket.xxtag = tag;
|
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); });
|
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); }
|
} 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 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
|
// Create a self-signed certificate
|
||||||
obj.GenerateRootCertificate = function (addThumbPrintToName, commonName, country, organization, strong) {
|
obj.GenerateRootCertificate = function (addThumbPrintToName, commonName, country, organization, strong) {
|
||||||
var keys = obj.pki.rsa.generateKeyPair((strong == true) ? 3072 : 2048);
|
var keys = obj.pki.rsa.generateKeyPair((strong == true) ? 3072 : 2048);
|
||||||
|
|
19
meshagent.js
19
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.
|
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)
|
// 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
|
// Use our server private key to sign the ServerHash + AgentNonce + ServerNonce
|
||||||
obj.agentnonce = msg.substring(50, 98);
|
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) {
|
function getWebCertHash(domain) {
|
||||||
var hash = obj.parent.webCertificateHashs[domain.id];
|
var hash = obj.parent.webCertificateHashs[domain.id];
|
||||||
if (hash != null) return hash;
|
if (hash != null) return hash;
|
||||||
return obj.parent.webCertificateHash;
|
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
|
// Verify the agent signature
|
||||||
function processAgentSignature(msg) {
|
function processAgentSignature(msg) {
|
||||||
// Verify the signature. This is the fast way, without using forge.
|
// Verify the signature. This is the fast way, without using forge.
|
||||||
const verify = obj.parent.crypto.createVerify('SHA384');
|
const verify = obj.parent.crypto.createVerify('SHA384');
|
||||||
verify.end(new Buffer(getWebCertHash(obj.domain) + obj.nonce + obj.agentnonce, 'binary'));
|
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) { return false; }
|
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
|
// Connection is a success, clean up
|
||||||
obj.nodeid = obj.unauth.nodeid;
|
obj.nodeid = obj.unauth.nodeid;
|
||||||
|
|
|
@ -417,22 +417,23 @@ function CreateMeshCentralServer(config, args) {
|
||||||
webCertLoadCount++;
|
webCertLoadCount++;
|
||||||
obj.certificateOperations.loadCertificate(obj.config.domains[i].certurl, obj.config.domains[i], function (url, cert, xdomain) {
|
obj.certificateOperations.loadCertificate(obj.config.domains[i].certurl, obj.config.domains[i], function (url, cert, xdomain) {
|
||||||
if (cert != null) {
|
if (cert != null) {
|
||||||
try {
|
// Hash the entire cert
|
||||||
// Decode a RSA certificate and hash the public key
|
var hash = obj.crypto.createHash('sha384').update(cert).digest('hex');
|
||||||
var forgeCert = obj.certificateOperations.forge.pki.certificateFromAsn1(obj.certificateOperations.forge.asn1.fromDer(cert.raw.toString('binary')));
|
if (xdomain.certhash != hash) {
|
||||||
var hash = obj.certificateOperations.forge.pki.getPublicKeyFingerprint(forgeCert.publicKey, { md: obj.certificateOperations.forge.md.sha384.create(), encoding: 'hex' });
|
xdomain.certkeyhash = hash;
|
||||||
if (xdomain.certhash != hash) {
|
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 + '.');
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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 {
|
} else {
|
||||||
console.log('Failed to load web certificate at: ' + url);
|
console.log('Failed to load web certificate at: ' + url);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "meshcentral",
|
"name": "meshcentral",
|
||||||
"version": "0.2.3-r",
|
"version": "0.2.3-v",
|
||||||
"keywords": [
|
"keywords": [
|
||||||
"Remote Management",
|
"Remote Management",
|
||||||
"Intel AMT",
|
"Intel AMT",
|
||||||
|
|
30
webserver.js
30
webserver.js
|
@ -102,33 +102,39 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Perform hash on web certificate and agent certificate
|
// 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.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.webCertificateHashBase64 = new Buffer(obj.webCertificateHash, '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.webCertificateFullHash = parent.certificateOperations.getCertHashBinary(obj.certificates.web.cert);
|
||||||
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.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();
|
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
|
// Compute the hash of all of the web certificates for each domain
|
||||||
for (var i in obj.parent.config.domains) {
|
for (var i in obj.parent.config.domains) {
|
||||||
if (obj.parent.config.domains[i].certhash != null) {
|
if (obj.parent.config.domains[i].certhash != null) {
|
||||||
// If the web certificate hash is provided, use it.
|
// 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)) {
|
} 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.
|
// 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 {
|
try {
|
||||||
// Decode a RSA certificate and hash the public key
|
// 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' });
|
obj.webCertificateHashs[i] = parent.certificateOperations.getPublicKeyHashBinary(obj.parent.config.domains[i].certs.cert);
|
||||||
} catch (ex) {
|
} catch (ex) {
|
||||||
// This may be a ECDSA certificate, hash the entire cert
|
// 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-----');
|
obj.webCertificateHashs[i] = obj.webCertificateFullHashs[i];
|
||||||
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 + '".'); }
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//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 we are running the legacy swarm server, compute the hash for that certificate
|
||||||
if (parent.certificates.swarmserver != null) {
|
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();
|
obj.swarmCertificateAsn1 = parent.certificateOperations.forge.asn1.toDer(parent.certificateOperations.forge.pki.certificateToAsn1(parent.certificateOperations.forge.pki.certificateFromPem(parent.certificates.swarmserver.cert))).getBytes();
|
||||||
|
|
Loading…
Reference in New Issue