From f36866b8fd6f0d278513a136b2f814d48d3e0952 Mon Sep 17 00:00:00 2001 From: Ylian Saint-Hilaire Date: Thu, 12 Nov 2020 15:21:38 -0800 Subject: [PATCH] Server peering fixes. --- multiserver.js | 33 +++++++++++++++++++++------------ webserver.js | 2 +- 2 files changed, 22 insertions(+), 13 deletions(-) diff --git a/multiserver.js b/multiserver.js index 9aa653ed..49afc763 100644 --- a/multiserver.js +++ b/multiserver.js @@ -60,7 +60,7 @@ module.exports.CreateMultiServer = function (parent, args) { obj.connectionState = 1; // Get the web socket setup - obj.ws = new WebSocket(obj.url + 'meshserver.ashx', { rejectUnauthorized: false, cert: obj.certificates.agent.cert, key: obj.certificates.agent.key }); + obj.ws = new WebSocket(obj.url + 'meshserver.ashx', { rejectUnauthorized: false, servername: obj.certificates.CommonName, cert: obj.certificates.agent.cert, key: obj.certificates.agent.key }); obj.parent.parent.debug('peer', 'OutPeer ' + obj.serverid + ': Connecting to: ' + url + 'meshserver.ashx'); // Register the connection failed event @@ -75,8 +75,14 @@ module.exports.CreateMultiServer = function (parent, args) { // Get the peer server's certificate and compute the server public key hash if (obj.ws._socket == null) return; - var serverCert = obj.forge.pki.certificateFromAsn1(obj.forge.asn1.fromDer(obj.ws._socket.getPeerCertificate().raw.toString('binary'))); - obj.serverCertHash = obj.forge.pki.getPublicKeyFingerprint(serverCert.publicKey, { encoding: 'binary', md: obj.forge.md.sha384.create() }); + if (obj.url.toLowerCase().startsWith('wss://')) { + // We are using TLS, use the certificate hash + var serverCert = obj.forge.pki.certificateFromAsn1(obj.forge.asn1.fromDer(obj.ws._socket.getPeerCertificate().raw.toString('binary'))); + obj.serverCertHash = obj.forge.pki.getPublicKeyFingerprint(serverCert.publicKey, { encoding: 'binary', md: obj.forge.md.sha384.create() }); + } else { + // We are not using TLS, blank out the TLS certificate hash + obj.serverCertHash = Buffer.alloc(48).toString('binary'); + } // Start authenticate the peer server by sending a auth nonce & server TLS cert hash. // Send 384 bits SHA384 hash of TLS cert public key + 384 bits nonce @@ -98,13 +104,13 @@ module.exports.CreateMultiServer = function (parent, args) { if (msg.length != 98) { obj.parent.parent.debug('peer', 'OutPeer: BAD MESSAGE(A1)'); return; } // Check that the server hash matches the TLS server certificate public key hash - if (obj.serverCertHash != msg.substring(2, 50)) { obj.parent.parent.debug('peer', 'OutPeer: Server hash mismatch.'); disconnect(); return; } + if (obj.url.toLowerCase().startsWith('wss://') && (obj.serverCertHash != msg.substring(2, 50))) { obj.parent.parent.debug('peer', 'OutPeer: Server hash mismatch.'); disconnect(); return; } obj.servernonce = msg.substring(50); // Perform the hash signature using the server agent certificate obj.parent.parent.certificateOperations.acceleratorPerformSignature(0, msg.substring(2) + obj.nonce, null, function (tag, signature) { // Send back our certificate + signature - obj.ws.send(obj.common.ShortToStr(2) + obj.common.ShortToStr(obj.agentCertificateAsn1.length) + obj.agentCertificateAsn1 + signature); // Command 2, certificate + signature + if (obj.ws != null) { obj.ws.send(obj.common.ShortToStr(2) + obj.common.ShortToStr(obj.agentCertificateAsn1.length) + obj.agentCertificateAsn1 + signature); } // Command 2, certificate + signature }); break; @@ -196,7 +202,7 @@ module.exports.CreateMultiServer = function (parent, args) { if ((command.serverid != null) && (command.dbid != null)) { if (command.serverid == obj.parent.serverid) { console.log('ERROR: Same server ID, trying to peer with self. (' + obj.url + ', ' + command.serverid + ').'); return; } if (command.dbid != obj.parent.parent.db.identifier) { console.log('ERROR: Database ID mismatch. Trying to peer to a server with the wrong database. (' + obj.url + ', ' + command.serverid + ').'); return; } - if (obj.serverCertHash != command.serverCertHash) { console.log('ERROR: Outer certificate hash mismatch (2). (' + obj.url + ', ' + command.serverid + ').'); return; } + if (obj.url.toLowerCase().startsWith('wss://') && (obj.serverCertHash != command.serverCertHash)) { console.log('ERROR: Outer certificate hash mismatch (2). (' + obj.url + ', ' + command.serverid + ').'); return; } obj.peerServerId = command.serverid; obj.peerServerKey = Buffer.from(command.key, 'hex'); obj.authenticated = 3; @@ -215,9 +221,10 @@ module.exports.CreateMultiServer = function (parent, args) { }; // Create a mesh server module that received a connection to another server - obj.CreatePeerInServer = function (parent, ws, req) { + obj.CreatePeerInServer = function (parent, ws, req, tls) { var obj = {}; obj.ws = ws; + obj.tls = tls; obj.parent = parent; obj.common = require('./common.js'); obj.forge = require('node-forge'); @@ -267,7 +274,7 @@ module.exports.CreateMultiServer = function (parent, args) { obj.receivedCommands += 1; // Peer server can't send the same command twice on the same connection ever. Block DOS attack path. // Check that the server hash matches out own web certificate hash - if (obj.webCertificateHash != msg.substring(2, 50)) { obj.close(); return; } + if ((obj.tls == true) && (obj.webCertificateHash != msg.substring(2, 50))) { obj.close(); return; } obj.peernonce = msg.substring(50); // Perform the hash signature using the server agent certificate @@ -424,7 +431,8 @@ module.exports.CreateMultiServer = function (parent, args) { // We connected to a peer server, setup everything obj.SetupPeerServer = function (server, peerServerId) { - console.log('Connected to peer server ' + peerServerId + '.'); + obj.parent.debug('peer', 'Connected to peer server ' + peerServerId + '.'); + //console.log('Connected to peer server ' + peerServerId + '.'); obj.peerServers[peerServerId] = server; // Send the list of connections to the peer @@ -436,7 +444,8 @@ module.exports.CreateMultiServer = function (parent, args) { // We disconnected to a peer server, clean up everything obj.ClearPeerServer = function (server, peerServerId) { - console.log('Disconnected from peer server ' + peerServerId + '.'); + obj.parent.debug('peer', 'Disconnected from peer server ' + peerServerId + '.'); + //console.log('Disconnected from peer server ' + peerServerId + '.'); // Clean up the connectivity state delete obj.peerServers[peerServerId]; @@ -600,7 +609,7 @@ module.exports.CreateMultiServer = function (parent, args) { peerTunnel.connect = function () { // Get the web socket setup peerTunnel.parent.parent.debug('peer', 'FTunnel ' + peerTunnel.serverid + ': Start connect to ' + peerTunnel.url); - peerTunnel.ws2 = new WebSocket(peerTunnel.url, { rejectUnauthorized: false }); + peerTunnel.ws2 = new WebSocket(peerTunnel.url, { rejectUnauthorized: false, servername: this.parent.parent.certificates.CommonName, cert: this.parent.parent.certificates.agent.cert, key: this.parent.parent.certificates.agent.key }); // Register the connection failed event peerTunnel.ws2.on('error', function (error) { peerTunnel.parent.parent.debug('peer', 'FTunnel ' + obj.serverid + ': Connection error'); peerTunnel.close(); }); @@ -620,7 +629,7 @@ module.exports.CreateMultiServer = function (parent, args) { var serverCertHashHex = Buffer.from(obj.forge.pki.getPublicKeyFingerprint(serverCert.publicKey, { encoding: 'binary', md: obj.forge.md.sha384.create() }), 'binary').toString('base64').replace(/\+/g, '@').replace(/\//g, '$'); // Check if the peer certificate is the expected one for this serverid - if (obj.peerServers[serverid] == null || obj.peerServers[serverid].serverCertHash != serverCertHashHex) { console.log('ERROR: Outer certificate hash mismatch (1). (' + peerTunnel.url + ', ' + peerTunnel.serverid + ').'); peerTunnel.close(); return; } + if ((obj.peerServers[serverid] == null) || (obj.peerServers[serverid].serverCertHash != serverCertHashHex)) { console.log('ERROR: Outer certificate hash mismatch (1). (' + peerTunnel.url + ', ' + peerTunnel.serverid + ').'); peerTunnel.close(); return; } // Connection accepted, resume the web socket to start the data flow peerTunnel.ws1._socket.resume(); diff --git a/webserver.js b/webserver.js index 3a6c9a8e..c5465a09 100644 --- a/webserver.js +++ b/webserver.js @@ -4875,7 +4875,7 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) { } // Setup all HTTP handlers - if (parent.multiServer != null) { obj.app.ws('/meshserver.ashx', function (ws, req) { parent.multiServer.CreatePeerInServer(parent.multiServer, ws, req); }); } + if (parent.multiServer != null) { obj.app.ws('/meshserver.ashx', function (ws, req) { parent.multiServer.CreatePeerInServer(parent.multiServer, ws, req, obj.args.tlsoffload == null); }); } for (var i in parent.config.domains) { if (parent.config.domains[i].dns != null) { continue; } // This is a subdomain with a DNS name, no added HTTP bindings needed. var domain = parent.config.domains[i];