diff --git a/meshctrl.js b/meshctrl.js index 32937d20..f5d455ce 100644 --- a/meshctrl.js +++ b/meshctrl.js @@ -16,7 +16,7 @@ var settings = {}; const crypto = require('crypto'); const args = require('minimist')(process.argv.slice(2)); const path = require('path'); -const possibleCommands = ['edituser', 'listusers', 'listusersessions', 'listdevicegroups', 'listdevices', 'listusersofdevicegroup', 'listevents', 'logintokens', 'serverinfo', 'userinfo', 'adduser', 'removeuser', 'adddevicegroup', 'removedevicegroup', 'editdevicegroup', 'broadcast', 'showevents', 'addusertodevicegroup', 'removeuserfromdevicegroup', 'addusertodevice', 'removeuserfromdevice', 'sendinviteemail', 'generateinvitelink', 'config', 'movetodevicegroup', 'deviceinfo', 'removedevice', 'editdevice', 'addusergroup', 'listusergroups', 'removeusergroup', 'runcommand', 'shell', 'upload', 'download', 'deviceopenurl', 'devicemessage', 'devicetoast', 'addtousergroup', 'removefromusergroup', 'removeallusersfromusergroup', 'devicesharing', 'devicepower', 'indexagenterrorlog', 'agentdownload', 'report', 'grouptoast', 'groupmessage']; +const possibleCommands = ['edituser', 'listusers', 'listusersessions', 'listdevicegroups', 'listdevices', 'listusersofdevicegroup', 'listevents', 'logintokens', 'serverinfo', 'userinfo', 'adduser', 'removeuser', 'adddevicegroup', 'removedevicegroup', 'editdevicegroup', 'broadcast', 'showevents', 'addusertodevicegroup', 'removeuserfromdevicegroup', 'addusertodevice', 'removeuserfromdevice', 'sendinviteemail', 'generateinvitelink', 'config', 'movetodevicegroup', 'deviceinfo', 'removedevice', 'editdevice', 'addlocaldevice', 'addamtdevice', 'addusergroup', 'listusergroups', 'removeusergroup', 'runcommand', 'shell', 'upload', 'download', 'deviceopenurl', 'devicemessage', 'devicetoast', 'addtousergroup', 'removefromusergroup', 'removeallusersfromusergroup', 'devicesharing', 'devicepower', 'indexagenterrorlog', 'agentdownload', 'report', 'grouptoast', 'groupmessage']; if (args.proxy != null) { try { require('https-proxy-agent'); } catch (ex) { console.log('Missing module "https-proxy-agent", type "npm install https-proxy-agent" to install it.'); return; } } if (args['_'].length == 0) { @@ -36,6 +36,8 @@ if (args['_'].length == 0) { console.log(" ListEvents - List server events."); console.log(" LoginTokens - List, create and remove login tokens."); console.log(" DeviceInfo - Show information about a device."); + console.log(" AddLocalDevice - Add a local device."); + console.log(" AddAmtDevice - Add a AMT device."); console.log(" EditDevice - Make changes to a device."); console.log(" RemoveDevice - Delete a device."); console.log(" Config - Perform operation on config.json file."); @@ -109,6 +111,22 @@ if (args['_'].length == 0) { else { ok = true; } break; } + case 'addlocaldevice': { + if (args.id == null) { console.log(winRemoveSingleQuotes("Missing device id, use --id '[deviceid]'")); } + else if (args.devicename == null) { console.log(winRemoveSingleQuotes("Missing devicename, use --devicename [devicename]")); } + else if (args.hostname == null) { console.log(winRemoveSingleQuotes("Missing hostname, use --hostname [hostname]")); } + else { ok = true; } + break; + } + case 'addamtdevice': { + if (args.id == null) { console.log(winRemoveSingleQuotes("Missing device id, use --id '[deviceid]'")); } + else if (args.devicename == null) { console.log(winRemoveSingleQuotes("Missing devicename, use --devicename [devicename]")); } + else if (args.hostname == null) { console.log(winRemoveSingleQuotes("Missing hostname, use --hostname [hostname]")); } + else if (args.user == null) { console.log(winRemoveSingleQuotes("Missing user, use --user [user]")); } + else if (args.pass == null) { console.log(winRemoveSingleQuotes("Missing pass, use --pass [pass]")); } + else { ok = true; } + break; + } case 'addusertodevicegroup': { if ((args.id == null) && (args.group == null)) { console.log(winRemoveSingleQuotes("Device group identifier missing, use --id '[groupid]' or --group [groupname]")); } else if (args.userid == null) { console.log("Add user to group missing useid, use --userid [userid]"); } @@ -788,6 +806,55 @@ if (args['_'].length == 0) { } break; } + case 'addlocaldevice': { + console.log("Add a Local Device, Example usages:\r\n"); + console.log(winRemoveSingleQuotes(" MeshCtrl AddLocalDevice --id 'meshid' --devicename 'devicename' --hostname 'hostname'")); + console.log(winRemoveSingleQuotes(" MeshCtrl AddLocalDevice --id 'meshid' --devicename 'devicename' --hostname 'hostname' --type 6")); + console.log("\r\nRequired arguments:\r\n"); + if (process.platform == 'win32') { + console.log(" --id [meshid] - The mesh identifier."); + console.log(" --devicename [devicename] - The device name."); + console.log(" --hostname [hostname] - The devices hostname or ip address."); + } else { + console.log(" --id '[meshid]' - The mesh identifier."); + console.log(" --devicename '[devicename]' - The device name."); + console.log(" --hostname '[hostname]' - The devices hostname or ip address."); + } + + console.log("\r\nOptional arguments:\r\n"); + console.log(" --type [TypeNumber] - With the following choices:"); + console.log(" type 4 - Default, Windows (RDP)"); + console.log(" type 6 - Linux (SSH/SCP/VNC)"); + console.log(" type 29 - macOS (SSH/SCP/VNC)"); + break; + } + case 'addamtdevice': { + console.log("Add an Intel AMT Device, Example usages:\r\n"); + console.log(winRemoveSingleQuotes(" MeshCtrl AddAmtDevice --id 'meshid' --devicename 'devicename' --hostname 'hostname --user 'admin' --pass 'admin'")); + console.log(winRemoveSingleQuotes(" MeshCtrl AddAmtDevice --id 'meshid' --devicename 'devicename' --hostname 'hostname --user 'admin' --pass 'admin' --notls")); + console.log("\r\nRequired arguments:\r\n"); + if (process.platform == 'win32') { + console.log(" --id [meshid] - The mesh identifier."); + console.log(" --devicename [devicename] - The device name."); + console.log(" --hostname [hostname] - The devices hostname or ip address."); + console.log(" --user [user] - The devices AMT username."); + console.log(" --pass [pass] - The devices AMT password."); + console.log("") + } else { + console.log(" --id '[meshid]' - The mesh identifier."); + console.log(" --devicename '[devicename]' - The device name."); + console.log(" --hostname '[hostname]' - The devices hostname or ip address."); + console.log(" --user '[user]' - The devices AMT username."); + console.log(" --pass '[pass]' - The devices AMT password."); + } + console.log("\r\nOptional arguments:\r\n"); + if (process.platform == 'win32') { + console.log(" --notls - Use No TLS Security."); + } else { + console.log(" --notls - Use No TLS Security."); + } + break; + } case 'editdevice': { console.log("Change information about a device, Example usages:\r\n"); console.log(winRemoveSingleQuotes(" MeshCtrl EditDevice --id 'deviceid' --name 'device1'")); @@ -1490,6 +1557,29 @@ function serverConnect() { ws.send(JSON.stringify(op)); break; } + case 'addamtdevice': { + var op = { action: 'addamtdevice', amttls: 1, responseid: 'meshctrl' }; + if (args.id) { op.meshid = args.id; } + if ((typeof args.devicename == 'string') && (args.devicename != '')) { op.devicename = args.devicename; } + if ((typeof args.hostname == 'string') && (args.hostname != '')) { op.hostname = args.hostname; } + if ((typeof args.user == 'string') && (args.user != '')) { op.amtusername = args.user; } + if ((typeof args.pass == 'string') && (args.pass != '')) { op.amtpassword = args.pass; } + if (args.notls) { op.amttls = 0; } + ws.send(JSON.stringify(op)); + break; + } + case 'addlocaldevice': { + var op = { action: 'addlocaldevice', type: 4, responseid: 'meshctrl' }; + if (args.id) { op.meshid = args.id; } + if ((typeof args.devicename == 'string') && (args.devicename != '')) { op.devicename = args.devicename; } + if ((typeof args.hostname == 'string') && (args.hostname != '')) { op.hostname = args.hostname; } + if (args.type) { + if ((typeof parseInt(args.type) != 'number') || isNaN(parseInt(args.type))) { console.log("Invalid type."); process.exit(1); return; } + op.type = args.type; + } + ws.send(JSON.stringify(op)); + break; + } case 'editdevicegroup': { var op = { action: 'editmesh', responseid: 'meshctrl' }; if (args.id) { op.meshid = args.id; } else if (args.group) { op.meshidname = args.group; } @@ -2084,6 +2174,8 @@ function serverConnect() { case 'toast': // TOAST case 'adduser': // ADDUSER case 'edituser': // EDITUSER + case 'addamtdevice': // ADDAMTDEVICE + case 'addlocaldevice': // ADDLOCALDEVICE case 'removedevices': // REMOVEDEVICE case 'changedevice': // EDITDEVICE case 'deleteuser': // REMOVEUSER diff --git a/meshuser.js b/meshuser.js index 04b987a5..862d4706 100644 --- a/meshuser.js +++ b/meshuser.js @@ -2555,77 +2555,98 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use } case 'addlocaldevice': { - if (common.validateString(command.meshid, 8, 134) == false) break; // Check meshid - if ((command.meshid.split('/').length != 3) || (command.meshid.split('/')[1] != domain.id)) return; // Invalid domain, operation only valid for current domain - if (common.validateString(command.devicename, 1, 256) == false) break; // Check device name - if (common.validateString(command.hostname, 1, 256) == false) break; // Check hostname - if (typeof command.type != 'number') break; // Type must be a number - if ((command.type != 4) && (command.type != 6) && (command.type != 29)) break; // Check device type - - // Get the mesh - mesh = parent.meshes[command.meshid]; - if (mesh) { - if (mesh.mtype != 3) return; // This operation is only allowed for mesh type 3, local device agentless mesh. - - // Check if this user has rights to do this - if ((parent.GetMeshRights(user, mesh) & MESHRIGHT_MANAGECOMPUTERS) == 0) return; - - // Create a new nodeid - parent.crypto.randomBytes(48, function (err, buf) { - // Create the new node - nodeid = 'node/' + domain.id + '/' + buf.toString('base64').replace(/\+/g, '@').replace(/\//g, '$'); - var device = { type: 'node', _id: nodeid, meshid: command.meshid, mtype: 3, icon: 1, name: command.devicename, host: command.hostname, domain: domain.id, agent: { id: command.type, caps: 0 } }; - db.Set(device); - - // Event the new node - parent.parent.DispatchEvent(parent.CreateMeshDispatchTargets(command.meshid, [nodeid]), obj, { etype: 'node', userid: user._id, username: user.name, action: 'addnode', node: parent.CloneSafeNode(device), msgid: 84, msgArgs: [command.devicename, mesh.name], msg: 'Added device ' + command.devicename + ' to device group ' + mesh.name, domain: domain.id }); - }); + var err = null; + // Perform input validation + try { + if (common.validateString(command.meshid, 8, 134) == false) { err = "Invalid device group id"; } // Check meshid + if (common.validateString(command.devicename, 1, 256) == false) { err = "Invalid devicename"; } // Check device name + if (common.validateString(command.hostname, 1, 256) == false) { err = "Invalid hostname"; } // Check hostname + if (typeof command.type != 'number') { err = "Invalid type"; } // Type must be a number + if ((command.type != 4) && (command.type != 6) && (command.type != 29)) { err = "Invalid type"; } // Check device type + else { + if (command.meshid.indexOf('/') == -1) { command.meshid = 'mesh/' + domain.id + '/' + command.meshid; } + mesh = parent.meshes[command.meshid]; + if (mesh == null) { err = "Unknown device group"; } + if (mesh.mtype != 3) { err = "Local device agentless mesh only allowed" } // This operation is only allowed for mesh type 3, local device agentless mesh. + else if ((parent.GetMeshRights(user, mesh) & MESHRIGHT_MANAGECOMPUTERS) == 0) { err = "Permission denied"; } + else if ((command.meshid.split('/').length != 3) || (command.meshid.split('/')[1] != domain.id)) { err = "Invalid domain"; } // Invalid domain, operation only valid for current domain + } + } catch (ex) { console.log(ex); err = "Validation exception: " + ex; } + // Handle any errors + if (err != null) { + if (command.responseid != null) { try { ws.send(JSON.stringify({ action: 'changeDeviceMesh', responseid: command.responseid, result: err })); } catch (ex) { } } + break; } + // Create a new nodeid + parent.crypto.randomBytes(48, function (err, buf) { + // Create the new node + nodeid = 'node/' + domain.id + '/' + buf.toString('base64').replace(/\+/g, '@').replace(/\//g, '$'); + var device = { type: 'node', _id: nodeid, meshid: command.meshid, mtype: 3, icon: 1, name: command.devicename, host: command.hostname, domain: domain.id, agent: { id: command.type, caps: 0 } }; + db.Set(device); + + // Event the new node + parent.parent.DispatchEvent(parent.CreateMeshDispatchTargets(command.meshid, [nodeid]), obj, { etype: 'node', userid: user._id, username: user.name, action: 'addnode', node: parent.CloneSafeNode(device), msgid: 84, msgArgs: [command.devicename, mesh.name], msg: 'Added device ' + command.devicename + ' to device group ' + mesh.name, domain: domain.id }); + // Send response if required + if (command.responseid != null) { try { ws.send(JSON.stringify({ action: 'addlocaldevice', responseid: command.responseid, result: 'ok' })); } catch (ex) { } } + }); break; } case 'addamtdevice': { if (args.wanonly == true) return; // This is a WAN-only server, local Intel AMT computers can't be added - if (common.validateString(command.meshid, 8, 134) == false) break; // Check meshid - if ((command.meshid.split('/').length != 3) || (command.meshid.split('/')[1] != domain.id)) return; // Invalid domain, operation only valid for current domain - if (common.validateString(command.devicename, 1, 256) == false) break; // Check device name - if (common.validateString(command.hostname, 1, 256) == false) break; // Check hostname - if (common.validateString(command.amtusername, 0, 16) == false) break; // Check username - if (common.validateString(command.amtpassword, 0, 16) == false) break; // Check password - if (command.amttls == '0') { command.amttls = 0; } else if (command.amttls == '1') { command.amttls = 1; } // Check TLS flag - if ((command.amttls != 1) && (command.amttls != 0)) break; + var err = null; + // Perform input validation + try { + if (common.validateString(command.meshid, 8, 134) == false) { err = "Invalid device group id"; } // Check meshid + if (common.validateString(command.devicename, 1, 256) == false) { err = "Invalid devicename"; } // Check device name + if (common.validateString(command.hostname, 1, 256) == false) { err = "Invalid hostname"; } // Check hostname + if (common.validateString(command.amtusername, 0, 16) == false) { err = "Invalid amtusername"; } // Check username + if (common.validateString(command.amtpassword, 0, 16) == false) { err = "Invalid amtpassword"; } // Check password + if (command.amttls == '0') { command.amttls = 0; } else if (command.amttls == '1') { command.amttls = 1; } // Check TLS flag + if ((command.amttls != 1) && (command.amttls != 0)) { err = "Invalid amttls"; } + else { + if (command.meshid.indexOf('/') == -1) { command.meshid = 'mesh/' + domain.id + '/' + command.meshid; } + // Get the mesh + mesh = parent.meshes[command.meshid]; + if (mesh == null) { err = "Unknown device group"; } + if (mesh.mtype != 1) { err = "Intel AMT agentless mesh only allowed"; } // This operation is only allowed for mesh type 1, Intel AMT agentless mesh. + // Check if this user has rights to do this + else if ((parent.GetMeshRights(user, mesh) & MESHRIGHT_MANAGECOMPUTERS) == 0) { err = "Permission denied"; } + else if ((command.meshid.split('/').length != 3) || (command.meshid.split('/')[1] != domain.id)) { err = "Invalid domain"; } // Invalid domain, operation only valid for current domain + } + } catch (ex) { console.log(ex); err = "Validation exception: " + ex; } + + // Handle any errors + if (err != null) { + if (command.responseid != null) { try { ws.send(JSON.stringify({ action: 'changeDeviceMesh', responseid: command.responseid, result: err })); } catch (ex) { } } + break; + } // If we are in WAN-only mode, hostname is not used if ((args.wanonly == true) && (command.hostname)) { delete command.hostname; } - // Get the mesh - mesh = parent.meshes[command.meshid]; - if (mesh) { - if (mesh.mtype != 1) return; // This operation is only allowed for mesh type 1, Intel AMT agentless mesh. + // Create a new nodeid + parent.crypto.randomBytes(48, function (err, buf) { + // Create the new node + nodeid = 'node/' + domain.id + '/' + buf.toString('base64').replace(/\+/g, '@').replace(/\//g, '$'); + var device = { type: 'node', _id: nodeid, meshid: command.meshid, mtype: 1, icon: 1, name: command.devicename, host: command.hostname, domain: domain.id, intelamt: { user: command.amtusername, pass: command.amtpassword, tls: command.amttls } }; - // Check if this user has rights to do this - if ((parent.GetMeshRights(user, mesh) & MESHRIGHT_MANAGECOMPUTERS) == 0) return; + // Add optional feilds + if (common.validateInt(command.state, 0, 3)) { device.intelamt.state = command.state; } + if (common.validateString(command.ver, 1, 16)) { device.intelamt.ver = command.ver; } + if (common.validateString(command.hash, 1, 256)) { device.intelamt.hash = command.hash; } + if (common.validateString(command.realm, 1, 256)) { device.intelamt.realm = command.realm; } - // Create a new nodeid - parent.crypto.randomBytes(48, function (err, buf) { - // Create the new node - nodeid = 'node/' + domain.id + '/' + buf.toString('base64').replace(/\+/g, '@').replace(/\//g, '$'); - var device = { type: 'node', _id: nodeid, meshid: command.meshid, mtype: 1, icon: 1, name: command.devicename, host: command.hostname, domain: domain.id, intelamt: { user: command.amtusername, pass: command.amtpassword, tls: command.amttls } }; + // Save the device to the database + db.Set(device); - // Add optional feilds - if (common.validateInt(command.state, 0, 3)) { device.intelamt.state = command.state; } - if (common.validateString(command.ver, 1, 16)) { device.intelamt.ver = command.ver; } - if (common.validateString(command.hash, 1, 256)) { device.intelamt.hash = command.hash; } - if (common.validateString(command.realm, 1, 256)) { device.intelamt.realm = command.realm; } + // Event the new node + parent.parent.DispatchEvent(parent.CreateMeshDispatchTargets(command.meshid, [nodeid]), obj, { etype: 'node', userid: user._id, username: user.name, action: 'addnode', node: parent.CloneSafeNode(device), msgid: 84, msgArgs: [command.devicename, mesh.name], msg: 'Added device ' + command.devicename + ' to device group ' + mesh.name, domain: domain.id }); + // Send response if required + if (command.responseid != null) { try { ws.send(JSON.stringify({ action: 'addamtdevice', responseid: command.responseid, result: 'ok' })); } catch (ex) { } } + }); - // Save the device to the database - db.Set(device); - - // Event the new node - parent.parent.DispatchEvent(parent.CreateMeshDispatchTargets(command.meshid, [nodeid]), obj, { etype: 'node', userid: user._id, username: user.name, action: 'addnode', node: parent.CloneSafeNode(device), msgid: 84, msgArgs: [command.devicename, mesh.name], msg: 'Added device ' + command.devicename + ' to device group ' + mesh.name, domain: domain.id }); - }); - } break; } case 'scanamtdevice':