diff --git a/db.js b/db.js index ecd3ecd6..106e32fd 100644 --- a/db.js +++ b/db.js @@ -660,7 +660,7 @@ module.exports.CreateDB = function (parent, func) { // If a DB encryption key is provided, perform database encryption if ((typeof parent.args.dbencryptkey == 'string') && (parent.args.dbencryptkey.length != 0)) { // Hash the database password into a AES256 key and setup encryption and decryption. - obj.dbKey = parent.crypto.createHash('sha384').update(parent.args.dbencryptkey).digest("raw").slice(0, 32); + obj.dbKey = parent.crypto.createHash('sha384').update(parent.args.dbencryptkey).digest('raw').slice(0, 32); datastoreOptions.afterSerialization = function (plaintext) { const iv = parent.crypto.randomBytes(16); const aes = parent.crypto.createCipheriv('aes-256-cbc', obj.dbKey, iv); diff --git a/meshcentral-config-schema.json b/meshcentral-config-schema.json index 6955b110..3ed7a3da 100644 --- a/meshcentral-config-schema.json +++ b/meshcentral-config-schema.json @@ -18,7 +18,6 @@ "sessionKey": { "type": "string" }, "sessionSameSite": { "type": "string" }, "dbEncryptKey": { "type": "string" }, - "multicastKey": { "type": "string", "description": "When set, encrypts all multicast LAN traffic to agents using this key. This is only useful in LAN mode when agents and tools will multicast to find the server." }, "dbRecordsEncryptKey": { "type": "string" }, "dbRecordsDecryptKey": { "type": "string" }, "dbExpire": { @@ -75,7 +74,8 @@ "additionalProperties": false, "properties": { "name": { "type": "string" }, - "info": { "type": "string" } + "info": { "type": "string" }, + "key": { "type": "string", "description": "When set, encrypts all LAN discovery traffic to agents and tools using this key. This is only useful in LAN/Hybrid mode when agents and tools user multicast to find the server." } }, "required": [ "name", "info" ] }, diff --git a/meshscanner.js b/meshscanner.js index d1948eab..d46adf5d 100644 --- a/meshscanner.js +++ b/meshscanner.js @@ -29,6 +29,31 @@ module.exports.CreateMeshScanner = function (parent) { obj.agentCertificateHashHex = parent.certificateOperations.forge.pki.getPublicKeyFingerprint(parent.certificateOperations.forge.pki.certificateFromPem(parent.certificates.agent.cert).publicKey, { md: parent.certificateOperations.forge.md.sha384.create(), encoding: 'hex' }).toUpperCase(); obj.error = 0; + // Setup the multicast key if present + if ((typeof obj.parent.args.localdiscovery == 'object') && (typeof obj.parent.args.localdiscovery.key == 'string') && (obj.parent.args.localdiscovery.key.length > 0)) { + obj.multicastKey = parent.crypto.createHash('sha384').update(obj.parent.args.localdiscovery.key).digest('raw').slice(0, 32); + } + + // Encrypt UDP packet + function encryptPacket(plainPacket) { + if (obj.multicastKey == null) { return plainPacket; } + const iv = parent.crypto.randomBytes(16), aes = parent.crypto.createCipheriv('aes-256-cbc', obj.multicastKey, iv); + var ciphertext = aes.update(plainPacket); + return Buffer.concat([iv, ciphertext, aes.final()]); + } + + // Decrypt UDP packet + function decryptPacket(packet) { + if (obj.multicastKey == null) { return packet; } + if (packet.length < 17) { return null; } + try { + const iv = packet.slice(0, 16), data = packet.slice(16); + const aes = parent.crypto.createDecipheriv('aes-256-cbc', obj.multicastKey, iv); + var plaintextBytes = Buffer.from(aes.update(data)); + return Buffer.concat([plaintextBytes, aes.final()]); + } catch (ex) { return null; } + } + // Get a list of IPv4 and IPv6 interface addresses function getInterfaceList() { var i; @@ -131,11 +156,20 @@ module.exports.CreateMeshScanner = function (parent) { // Setup the local discovery values var name = 'MeshCentral'; - try { name = obj.parent.config.domains[''].title; } catch (ex) { } - try { name = obj.parent.args.localdiscovery.name; } catch (ex) { } - var info = 'Beta2'; - try { info = obj.parent.config.domains[''].title2; } catch (ex) { } - try { info = obj.parent.args.localdiscovery.info; } catch (ex) { } + var info = ''; + try { + if ((typeof obj.parent.config.domains[''].title == 'string') && (obj.parent.config.domains[''].title.length > 0)) { + name = obj.parent.config.domains[''].title; info = ''; + try { if ((typeof obj.parent.config.domains[''].title2 == 'string') && (obj.parent.config.domains[''].title2.length > 0)) { info = obj.parent.config.domains[''].title2; } } catch (ex) { } + } + } catch (ex) { } + try { + if ((typeof obj.parent.args.localdiscovery.name == 'string') && (obj.parent.args.localdiscovery.name.length > 0)) { + name = obj.parent.args.localdiscovery.name; info = ''; + try { if ((typeof obj.parent.args.localdiscovery.info == 'string') && (obj.parent.args.localdiscovery.info.length > 0)) { info = obj.parent.args.localdiscovery.info; } } catch (ex) { } + } + } catch (ex) { } + if (info == '') { info = parent.certificates.CommonName; } // Figure out the correct websocket port var port = (parent.args.aliasport)?parent.args.aliasport:parent.args.port; @@ -167,26 +201,29 @@ module.exports.CreateMeshScanner = function (parent) { obj.performScan = function (server) { var i; if (server != null) { - if (server.xxtype == 4) { try { server.send(obj.multicastPacket4, 0, obj.multicastPacket4.length, 16990, membershipIPv4); } catch (e) { } } - if (server.xxtype == 6) { try { server.send(obj.multicastPacket6, 0, obj.multicastPacket6.length, 16990, membershipIPv6); } catch (e) { } } - if ((server.xxtype == 4) && (server.xxlocal == '*')) { try { server.send(obj.multicastPacket4, 0, obj.multicastPacket4.length, 16990, '127.0.0.1'); } catch (e) { } try { server.send(obj.multicastPacket4, 0, obj.multicastPacket4.length, 16990, '255.255.255.255'); } catch (e) { } } - if ((server.xxtype == 6) && (server.xxlocal == '*')) { try { server.send(obj.multicastPacket6, 0, obj.multicastPacket6.length, 16990, '::1'); } catch (e) { } } + if (server.xxtype == 4) { var p = encryptPacket(obj.multicastPacket4); try { server.send(p, 0, p.length, 16990, membershipIPv4); } catch (e) { } } + if (server.xxtype == 6) { var p = encryptPacket(obj.multicastPacket6); try { server.send(p, 0, p.length, 16990, membershipIPv6); } catch (e) { } } + if ((server.xxtype == 4) && (server.xxlocal == '*')) { var p = encryptPacket(obj.multicastPacket4); try { server.send(p, 0, p.length, 16990, '127.0.0.1'); } catch (e) { } try { server.send(p, 0, p.length, 16990, '255.255.255.255'); } catch (e) { } } + if ((server.xxtype == 6) && (server.xxlocal == '*')) { var p = encryptPacket(obj.multicastPacket6); try { server.send(p, 0, p.length, 16990, '::1'); } catch (e) { } } } else { - for (i in obj.servers4) { try { obj.servers4[i].send(obj.multicastPacket4, 0, obj.multicastPacket4.length, 16990, membershipIPv4); } catch (e) { } } - for (i in obj.servers6) { try { obj.servers6[i].send(obj.multicastPacket6, 0, obj.multicastPacket6.length, 16990, membershipIPv6); } catch (e) { } } + for (i in obj.servers4) { var p = encryptPacket(obj.multicastPacket4); try { obj.servers4[i].send(p, 0, p.length, 16990, membershipIPv4); } catch (e) { } } + for (i in obj.servers6) { var p = encryptPacket(obj.multicastPacket6); try { obj.servers6[i].send(p, 0, p.length, 16990, membershipIPv6); } catch (e) { } } setupServers(); // Check if any network interfaces where added or removed } }; // Called when a UDP packet is received from an agent. function onUdpPacket(msg, info, server) { + // Decrypt the packet if needed + if ((msg = decryptPacket(msg)) == null) return; + //console.log('Received ' + msg.length + ' bytes from ' + info.address + ':' + info.port + ', on interface: ' + server.xxlocal + '.'); if ((msg.length == 96) && (msg.toString('ascii') == obj.agentCertificateHashHex)) { - if (server.xxtype == 4) { try { server.send(obj.multicastPacket4, 0, obj.multicastPacket4.length, info.port, info.address); } catch (e) { } } - if (server.xxtype == 6) { try { server.send(obj.multicastPacket6, 0, obj.multicastPacket6.length, info.port, info.address); } catch (e) { } } + if (server.xxtype == 4) { var p = encryptPacket(obj.multicastPacket4); try { server.send(p, 0, p, info.port, info.address); } catch (e) { } } + if (server.xxtype == 6) { var p = encryptPacket(obj.multicastPacket6); try { server.send(p, 0, p, info.port, info.address); } catch (e) { } } } else if (msg.toString('ascii') == 'MeshServerScan') { - if (server.xxtype == 4) { try { server.send(obj.multicastPacket4x, 0, obj.multicastPacket4x.length, info.port, info.address); } catch (e) { } } - if (server.xxtype == 6) { try { server.send(obj.multicastPacket6x, 0, obj.multicastPacket6x.length, info.port, info.address); } catch (e) { } } + if (server.xxtype == 4) { var p = encryptPacket(obj.multicastPacket4x); try { server.send(p, 0, p.length, info.port, info.address); } catch (e) { } } + if (server.xxtype == 6) { var p = encryptPacket(obj.multicastPacket6x); try { server.send(p, 0, p.length, info.port, info.address); } catch (e) { } } } }