From 2d3b1fe68abc5cca9bb539356061104e576abe90 Mon Sep 17 00:00:00 2001 From: Ylian Saint-Hilaire Date: Wed, 1 Dec 2021 01:46:41 -0800 Subject: [PATCH] If needed, auto-create missing device in agent group type when CIRA connects. --- mpsserver.js | 94 +++++++++++++++++++++++++++++++++++++--- package.json | 18 +++++++- views/default.handlebars | 1 + 3 files changed, 104 insertions(+), 9 deletions(-) diff --git a/mpsserver.js b/mpsserver.js index 3b2de02d..121648fd 100644 --- a/mpsserver.js +++ b/mpsserver.js @@ -658,9 +658,10 @@ module.exports.CreateMpsServer = function (parent, db, args, certificates) { // If this is a agent-less mesh, use the device guid 3 times as ID. if (initialMesh.mtype == 1) { // Intel AMT GUID (socket.tag.SystemId) will be used as NodeID - var systemid = socket.tag.SystemId.split('-').join(''); - var nodeid = Buffer.from(systemid + systemid + systemid, 'hex').toString('base64').replace(/\+/g, '@').replace(/\//g, '$'); - var domain = obj.parent.config.domains[initialMesh.domain]; + const systemid = socket.tag.SystemId.split('-').join(''); + const nodeid = Buffer.from(systemid + systemid + systemid, 'hex').toString('base64').replace(/\+/g, '@').replace(/\//g, '$'); + const domain = obj.parent.config.domains[initialMesh.domain]; + if (domain == null) return; socket.tag.domain = domain; socket.tag.domainid = initialMesh.domain; if (socket.tag.name == null) { socket.tag.name = ''; } @@ -694,7 +695,7 @@ module.exports.CreateMpsServer = function (parent, db, args, certificates) { // Event the new node addedDeviceCount++; - var change = 'CIRA added device ' + socket.tag.name + ' to group ' + initialMesh.name; + var change = 'Added CIRA device ' + socket.tag.name + ' to group ' + initialMesh.name; obj.parent.DispatchEvent(['*', socket.tag.meshid], obj, { etype: 'node', action: 'addnode', node: parent.webserver.CloneSafeNode(device), msg: change, domain: initialMesh.domain }); // Add the connection to the MPS connection list @@ -720,7 +721,7 @@ module.exports.CreateMpsServer = function (parent, db, args, certificates) { // Event the new node addedDeviceCount++; - var change = 'CIRA added device ' + socket.tag.name + ' to group ' + initialMesh.name; + var change = 'Added CIRA device ' + socket.tag.name + ' to group ' + initialMesh.name; obj.parent.DispatchEvent(['*', socket.tag.meshid], obj, { etype: 'node', action: 'addnode', node: parent.webserver.CloneSafeNode(device), msg: change, domain: initialMesh.domain }); }); } @@ -740,10 +741,89 @@ module.exports.CreateMpsServer = function (parent, db, args, certificates) { // Intel AMT GUID (socket.tag.SystemId) will be used to search the node obj.db.getAmtUuidMeshNode(initialMesh.domain, initialMesh.mtype, socket.tag.SystemId, function (err, nodes) { // TODO: Need to optimize this request with indexes if ((nodes == null) || (nodes.length === 0) || (obj.parent.webserver.meshes == null)) { - // New CIRA connection for unknown node, disconnect. + // New CIRA connection for unknown node, create a new device. unknownNodeCount++; console.log('CIRA connection for unknown node. groupid: ' + initialMesh._id + ', uuid: ' + socket.tag.SystemId); - obj.close(socket); + //obj.close(socket); + //return; + var domain = obj.parent.config.domains[initialMesh.domain]; + if (domain == null) return; + + // Check if we already have too many devices for this domain + if (domain.limits && (typeof domain.limits.maxdevices == 'number')) { + db.isMaxType(domain.limits.maxdevices, 'node', initialMesh.domain, function (ismax, count) { + if (ismax == true) { + // Too many devices in this domain. + maxDomainDevicesReached++; + console.log('Too many devices on this domain to accept the CIRA connection. meshid: ' + socket.tag.meshid); + obj.close(socket); + } else { + // Attempts reverse DNS loopup on the device IP address + require('dns').reverse(socket.remoteAddr, function (err, hostnames) { + var hostname = socket.remoteAddr; + if ((err == null) && (hostnames != null) && (hostnames.length > 0)) { hostname = hostnames[0]; } + + // Set the device group + socket.tag.meshid = initialMesh._id; + + const systemid = socket.tag.SystemId.split('-').join(''); + const nodeid = Buffer.from(systemid + systemid + systemid, 'hex').toString('base64').replace(/\+/g, '@').replace(/\//g, '$'); + socket.tag.domain = domain; + socket.tag.domainid = initialMesh.domain; + socket.tag.name = hostname; + socket.tag.nodeid = 'node/' + initialMesh.domain + '/' + nodeid; // Turn 16bit systemid guid into 48bit nodeid that is base64 encoded + socket.tag.connectTime = Date.now(); + + // Node is not in the database, add it. Credentials will be empty until added by the user. + var device = { type: 'node', mtype: 2, _id: socket.tag.nodeid, meshid: socket.tag.meshid, name: hostname, icon: (socket.tag.meiState && socket.tag.meiState.isBatteryPowered) ? 2 : 1, host: hostname, domain: initialMesh.domain, intelamt: { user: ((socket.tag.meiState) && (typeof socket.tag.meiState.amtuser == 'string')) ? socket.tag.meiState.amtuser : '', pass: ((socket.tag.meiState) && (typeof socket.tag.meiState.amtpass == 'string')) ? socket.tag.meiState.amtpass : '', tls: 0, state: 2, agent: { id: 0, caps: 0 } } }; + if ((socket.tag.meiState != null) && (typeof socket.tag.meiState.desc == 'string') && (socket.tag.meiState.desc.length > 0) && (socket.tag.meiState.desc.length < 1024)) { device.desc = socket.tag.meiState.desc; } + obj.db.Set(device); + + // Event the new node + addedDeviceCount++; + var change = 'Added CIRA device ' + socket.tag.name + ' to group ' + initialMesh.name; + obj.parent.DispatchEvent(['*', socket.tag.meshid], obj, { etype: 'node', action: 'addnode', node: parent.webserver.CloneSafeNode(device), msg: change, domain: initialMesh.domain }); + + // Add the connection to the MPS connection list + addCiraConnection(socket); + SendUserAuthSuccess(socket); // Notify the auth success on the CIRA connection + }); + } + }); + return; + } else { + // Attempts reverse DNS loopup on the device IP address + require('dns').reverse(socket.remoteAddr, function (err, hostnames) { + var hostname = socket.remoteAddr; + if ((err == null) && (hostnames != null) && (hostnames.length > 0)) { hostname = hostnames[0]; } + + // Set the device group + socket.tag.meshid = initialMesh._id; + + const systemid = socket.tag.SystemId.split('-').join(''); + const nodeid = Buffer.from(systemid + systemid + systemid, 'hex').toString('base64').replace(/\+/g, '@').replace(/\//g, '$'); + socket.tag.domain = domain; + socket.tag.domainid = initialMesh.domain; + socket.tag.name = hostname; + socket.tag.nodeid = 'node/' + initialMesh.domain + '/' + nodeid; // Turn 16bit systemid guid into 48bit nodeid that is base64 encoded + socket.tag.connectTime = Date.now(); + + // Node is not in the database, add it. Credentials will be empty until added by the user. + var device = { type: 'node', mtype: 2, _id: socket.tag.nodeid, meshid: socket.tag.meshid, name: hostname, icon: (socket.tag.meiState && socket.tag.meiState.isBatteryPowered) ? 2 : 1, host: hostname, domain: initialMesh.domain, agent: { ver: 0, id: 0, caps: 0 }, intelamt: { uuid: socket.tag.SystemId, user: ((socket.tag.meiState) && (typeof socket.tag.meiState.amtuser == 'string')) ? socket.tag.meiState.amtuser : '', pass: ((socket.tag.meiState) && (typeof socket.tag.meiState.amtpass == 'string')) ? socket.tag.meiState.amtpass : '', tls: 0, state: 2 } }; + if ((socket.tag.meiState != null) && (typeof socket.tag.meiState.desc == 'string') && (socket.tag.meiState.desc.length > 0) && (socket.tag.meiState.desc.length < 1024)) { device.desc = socket.tag.meiState.desc; } + obj.db.Set(device); + console.log('ADDED', device); + + // Event the new node + addedDeviceCount++; + var change = 'Added CIRA device ' + socket.tag.name + ' to group ' + initialMesh.name; + obj.parent.DispatchEvent(['*', socket.tag.meshid], obj, { etype: 'node', action: 'addnode', node: parent.webserver.CloneSafeNode(device), msg: change, domain: initialMesh.domain }); + + // Add the connection to the MPS connection list + addCiraConnection(socket); + SendUserAuthSuccess(socket); // Notify the auth success on the CIRA connection + }); + } return; } diff --git a/package.json b/package.json index 3ee14695..eb0b5ce4 100644 --- a/package.json +++ b/package.json @@ -36,6 +36,8 @@ "sample-config-advanced.json" ], "dependencies": { + "@yetzt/nedb": "^1.8.0", + "archiver": "^4.0.2", "body-parser": "^1.19.0", "cbor": "~5.2.0", "compression": "^1.7.4", @@ -43,13 +45,25 @@ "express": "^4.17.0", "express-handlebars": "^3.1.0", "express-ws": "^4.0.0", + "image-size": "^1.0.0", "ipcheck": "^0.1.0", + "loadavg-windows": "^1.1.1", "minimist": "^1.2.5", + "mongodb": "^4.1.0", "multiparty": "^4.2.1", - "@yetzt/nedb": "^1.8.0", "node-forge": "^0.10.0", + "node-rdpjs-2": "^0.3.5", + "node-windows": "^0.1.4", + "nodemailer": "^6.7.2", + "otplib": "^10.2.3", + "pg": "^8.7.1", + "pgtools": "^0.3.2", + "saslprep": "^1.0.3", + "ssh2": "^1.5.0", + "web-push": "^3.4.5", "ws": "^5.2.3", - "yauzl": "^2.10.0" + "yauzl": "^2.10.0", + "yubikeyotp": "^0.2.0" }, "engines": { "node": ">=10.0.0" diff --git a/views/default.handlebars b/views/default.handlebars index 5792bc0f..7e91da25 100644 --- a/views/default.handlebars +++ b/views/default.handlebars @@ -2322,6 +2322,7 @@ for (var m in message.nodes) { for (var n in message.nodes[m]) { if (message.nodes[m][n]._id == null) { console.log('Invalid node (' + n + '): ' + JSON.stringify(message.nodes)); continue; } + if (message.nodes[m][n].name == null) { message.nodes[m][n].name = '???'; } message.nodes[m][n].namel = message.nodes[m][n].name.toLowerCase(); if (message.nodes[m][n].rname) { message.nodes[m][n].rnamel = message.nodes[m][n].rname.toLowerCase(); } else { message.nodes[m][n].rnamel = message.nodes[m][n].namel; } message.nodes[m][n].meshnamel = meshes[m]?meshes[m].name.toLowerCase():'*';