diff --git a/meshctrl.js b/meshctrl.js index 5eec08ab..458d711c 100644 --- a/meshctrl.js +++ b/meshctrl.js @@ -7,7 +7,7 @@ try { require('ws'); } catch (ex) { console.log('Missing module "ws", type "npm var settings = {}; const crypto = require('crypto'); const args = require('minimist')(process.argv.slice(2)); -const possibleCommands = ['listusers', 'listusersessions', 'listdevicegroups', 'listdevices', 'listusersofdevicegroup', 'serverinfo', 'userinfo', 'adduser', 'removeuser', 'adddevicegroup', 'removedevicegroup', 'broadcast', 'showevents', 'addusertodevicegroup', 'removeuserfromdevicegroup', 'addusertodevice', 'removeuserfromdevice', 'sendinviteemail', 'generateinvitelink', 'config', 'movetodevicegroup', 'deviceinfo', 'addusergroup', 'listusergroups', 'removeusergroup']; +const possibleCommands = ['listusers', 'listusersessions', 'listdevicegroups', 'listdevices', 'listusersofdevicegroup', 'serverinfo', 'userinfo', 'adduser', 'removeuser', 'adddevicegroup', 'removedevicegroup', 'broadcast', 'showevents', 'addusertodevicegroup', 'removeuserfromdevicegroup', 'addusertodevice', 'removeuserfromdevice', 'sendinviteemail', 'generateinvitelink', 'config', 'movetodevicegroup', 'deviceinfo', 'addusergroup', 'listusergroups', 'removeusergroup', 'runcommand']; 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) { @@ -41,6 +41,7 @@ if (args['_'].length == 0) { console.log(" GenerateInviteLink - Create an invitation link."); console.log(" Broadcast - Display a message to all online users."); console.log(" ShowEvents - Display real-time server events in JSON format."); + console.log(" RunCommand - Run a shell command on a remote device."); console.log("\r\nSupported login arguments:"); console.log(" --url [wss://server] - Server url, wss://localhost:443 is default."); console.log(" --loginuser [username] - Login username, admin is default."); @@ -158,6 +159,12 @@ if (args['_'].length == 0) { else { ok = true; } break; } + case 'runcommand': { + if (args.id == null) { console.log("Missing device id, use --id [deviceid]"); } + else if (args.run == null) { console.log("Missing run, use --run \"command\""); } + else { ok = true; } + break; + } case 'help': { if (args['_'].length < 2) { console.log("Get help on an action. Type:\r\n\r\n help [action]\r\n\r\nPossible actions are: " + possibleCommands.join(', ') + '.'); @@ -410,6 +417,17 @@ if (args['_'].length == 0) { console.log(" --json - Give results in JSON format."); break; } + case 'runcommand': { + console.log("Run a shell command on a remote device, Example usages:\r\n"); + console.log(" MeshCtrl RunCommand --id deviceid --run \"command\""); + console.log(" MeshCtrl RunCommand --id deviceid --run \"command\" --powershell"); + console.log("\r\nRequired arguments:\r\n"); + console.log(" --id [deviceid] - The device identifier."); + console.log(" --run \"[command]\" - Shell command to execute on the remote device."); + console.log("\r\nOptional arguments:\r\n"); + console.log(" --powershell - Run in Windows PowerShell."); + break; + } default: { console.log("Get help on an action. Type:\r\n\r\n help [action]\r\n\r\nPossible actions are: " + possibleCommands.join(', ') + '.'); } @@ -768,6 +786,10 @@ function serverConnect() { ws.send(JSON.stringify({ action: 'getsysinfo', nodeid: args.id, nodeinfo: true, responseid: 'meshctrl' })); break; } + case 'runcommand': { + ws.send(JSON.stringify({ action: 'runcommands', nodeids: [args.id], type: ((args.powershell) ? 2 : 0), cmds: args.run, responseid: 'meshctrl' })); + break; + } } }); @@ -847,6 +869,7 @@ function serverConnect() { case 'adddeviceuser': // case 'createusergroup': // case 'deleteusergroup': // + case 'runcommands': case 'userbroadcast': { // BROADCAST if (data.responseid == 'meshctrl') { if (data.meshid) { console.log(data.result, data.meshid); } diff --git a/meshuser.js b/meshuser.js index 8a900b4f..d4481b37 100644 --- a/meshuser.js +++ b/meshuser.js @@ -3370,10 +3370,32 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use if (typeof command.cmds != 'string') break; // Check commands for (i in command.nodeids) { + var nodeid = command.nodeids[i], err = null; + + // Argument validation + if (common.validateString(nodeid, 1, 1024) == false) { err = 'Invalid nodeid'; } // Check nodeid + else { + if (nodeid.indexOf('/') == -1) { nodeid = 'node/' + domain.id + '/' + nodeid; } + if ((nodeid.split('/').length != 3) || (nodeid.split('/')[1] != domain.id)) { err = 'Invalid domain'; } // Invalid domain, operation only valid for current domain + } + if (err != null) { + if (command.responseid != null) { try { ws.send(JSON.stringify({ action: 'runcommands', responseid: command.responseid, result: err })); } catch (ex) { } } + continue; + } + // Get the node and the rights for this node - parent.GetNodeWithRights(domain, user, command.nodeids[i], function (node, rights, visible) { + parent.GetNodeWithRights(domain, user, nodeid, function (node, rights, visible) { + // Check if this node was found + if (node == null) { + if (command.responseid != null) { try { ws.send(JSON.stringify({ action: 'runcommands', responseid: command.responseid, result: 'Invalid nodeid' })); } catch (ex) { } } + return; + } + // Check we have the rights to run commands on this device - if ((rights & MESHRIGHT_REMOTECONTROL) == 0) return; + if ((rights & MESHRIGHT_REMOTECONTROL) == 0) { + if (command.responseid != null) { try { ws.send(JSON.stringify({ action: 'runcommands', responseid: command.responseid, result: 'Access denied' })); } catch (ex) { } } + return; + } // Get the agent and run the commands var agent = parent.wsagents[node._id]; @@ -3384,13 +3406,21 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use if ((agent.agentInfo.agentId > 0) && (agent.agentInfo.agentId < 5)) { // Windows Agent if ((command.type == 1) || (command.type == 2)) { commandsOk = true; } + else if (command.type === 0) { command.type = 1; commandsOk = true; } // Set the default type of this agent } else { // Non-Windows Agent if (command.type == 3) { commandsOk = true; } + else if (command.type === 0) { command.type = 3; commandsOk = true; } // Set the default type of this agent } if (commandsOk == true) { + // Send the commands to the agent try { agent.send(JSON.stringify({ action: 'runcommands', type: command.type, cmds: command.cmds })); } catch (ex) { } + if (command.responseid != null) { try { ws.send(JSON.stringify({ action: 'runcommands', responseid: command.responseid, result: 'OK' })); } catch (ex) { } } + } else { + if (command.responseid != null) { try { ws.send(JSON.stringify({ action: 'runcommands', responseid: command.responseid, result: 'Invalid command type' })); } catch (ex) { } } } + } else { + if (command.responseid != null) { try { ws.send(JSON.stringify({ action: 'runcommands', responseid: command.responseid, result: 'Agent not connected' })); } catch (ex) { } } } }); }