Session IP binding, MeshCtrl Invite Link

This commit is contained in:
Ylian Saint-Hilaire 2019-09-23 11:45:10 -07:00
parent b37113169b
commit 2f6e9093bf
7 changed files with 77 additions and 34 deletions

View File

@ -14,8 +14,8 @@ See the License for the specific language governing permissions and
limitations under the License. limitations under the License.
*/ */
const PDH_FMT_LONG = 0x00000100; var PDH_FMT_LONG = 0x00000100;
const PDH_FMT_DOUBLE = 0x00000200; var PDH_FMT_DOUBLE = 0x00000200;
var promise = require('promise'); var promise = require('promise');
if (process.platform == 'win32') if (process.platform == 'win32')

View File

@ -0,0 +1 @@
var PDH_FMT_LONG=256;var PDH_FMT_DOUBLE=512;var promise=require("promise");if(process.platform=="win32"){var GM=require("_GenericMarshal");GM.kernel32=GM.CreateNativeProxy("kernel32.dll");GM.kernel32.CreateMethod("GlobalMemoryStatusEx");GM.pdh=GM.CreateNativeProxy("pdh.dll");GM.pdh.CreateMethod("PdhAddEnglishCounterA");GM.pdh.CreateMethod("PdhCloseQuery");GM.pdh.CreateMethod("PdhCollectQueryData");GM.pdh.CreateMethod("PdhGetFormattedCounterValue");GM.pdh.CreateMethod("PdhGetFormattedCounterArrayA");GM.pdh.CreateMethod("PdhOpenQueryA");GM.pdh.CreateMethod("PdhRemoveCounter")}function windows_cpuUtilization(){var b=new promise(function(d,c){this._res=d;this._rej=c});b.counter=GM.CreateVariable(16);b.cpu=GM.CreatePointer();b.cpuTotal=GM.CreatePointer();var a=0;if((a=GM.pdh.PdhOpenQueryA(0,0,b.cpu).Val)!=0){b._rej(a);return}if((a=GM.pdh.PdhAddEnglishCounterA(b.cpu.Deref(),GM.CreateVariable("\\Processor(*)\\% Processor Time"),0,b.cpuTotal).Val)!=0){b._rej(a);return}if((a=GM.pdh.PdhCollectQueryData(b.cpu.Deref()).Val!=0)){b._rej(a);return}b._timeout=setTimeout(function(k){var m={cpus:[]};var d=GM.CreateVariable(4);var j=GM.CreateVariable(4);var c,l,h;var f;if((f=GM.pdh.PdhCollectQueryData(k.cpu.Deref()).Val!=0)){k._rej(f);return}if((f=GM.pdh.PdhGetFormattedCounterArrayA(k.cpuTotal.Deref(),PDH_FMT_DOUBLE,d,j,0).Val)==-2147481646){c=GM.CreateVariable(d.toBuffer().readUInt32LE())}else{k._rej(f);return}if((f=GM.pdh.PdhGetFormattedCounterArrayA(k.cpuTotal.Deref(),PDH_FMT_DOUBLE,d,j,c).Val)!=0){k._rej(f);return}for(var g=0;g<j.toBuffer().readUInt32LE();++g){h=c.Deref(g*24,24);l=h.Deref(0,GM.PointerSize).Deref();if(l.String=="_Total"){m.total=h.Deref(16,8).toBuffer().readDoubleLE().toFixed(2)}else{m.cpus[parseInt(l.String)]=h.Deref(16,8).toBuffer().readDoubleLE().toFixed(2)}}GM.pdh.PdhRemoveCounter(k.cpuTotal.Deref());GM.pdh.PdhCloseQuery(k.cpu.Deref());b._res(m)},100,b);return(b)}function windows_memUtilization(){var a=GM.CreateVariable(64);a.Deref(0,4).toBuffer().writeUInt32LE(64);GM.kernel32.GlobalMemoryStatusEx(a);var b={MemTotal:require("bignum").fromBuffer(a.Deref(8,8).toBuffer(),{endian:"little"}),MemFree:require("bignum").fromBuffer(a.Deref(16,8).toBuffer(),{endian:"little"})};b.percentFree=((b.MemFree.div(require("bignum")("1048576")).toNumber()/b.MemTotal.div(require("bignum")("1048576")).toNumber())*100).toFixed(2);b.percentConsumed=((b.MemTotal.sub(b.MemFree).div(require("bignum")("1048576")).toNumber()/b.MemTotal.div(require("bignum")("1048576")).toNumber())*100).toFixed(2);b.MemTotal=b.MemTotal.toString();b.MemFree=b.MemFree.toString();return(b)}function linux_cpuUtilization(){var g={cpus:[]};var d=require("fs").readFileSync("/proc/stat");var e=d.toString().split("\n");var a;var k,l;var h,c,j;for(var b in e){a=e[b].split(" ");if(!a[0].startsWith("cpu")){break}k=0,h=0;while(a[++k]==""){}for(l=k;l<a.length;++l){h+=parseInt(a[l])}c=parseInt(a[3+k]);j=(100-((c/h)*100)).toFixed(2);if(!g.total){g.total=j}else{g.cpus.push(j)}}var f=new promise(function(m,i){this._res=m;this._rej=i});f._res(g);return(f)}function linux_memUtilization(){var c={};var b=require("fs").readFileSync("/proc/meminfo").toString().split("\n");var d;for(var a in b){d=b[a].split(" ");switch(d[0]){case"MemTotal:":c.total=parseInt(d[d.length-2]);break;case"MemFree:":c.free=parseInt(d[d.length-2]);break}}c.percentFree=((c.free/c.total)*100).toFixed(2);c.percentConsumed=(((c.total-c.free)/c.total)*100).toFixed(2);return(c)}function macos_cpuUtilization(){var d=new promise(function(h,g){this._res=h;this._rej=g});var b=require("child_process").execFile("/bin/sh",["sh"]);b.stdout.str="";b.stdout.on("data",function(g){this.str+=g.toString()});b.stdin.write('top -l 1 | grep -E "^CPU"\nexit\n');b.waitExit();var c=b.stdout.str.split("\n");if(c[0].length>0){var f=c[0].split(":")[1];var a=f.split(",");var e=parseFloat(a[0].split("%")[0].trim())+parseFloat(a[1].split("%")[0].trim());d._res({total:e,cpus:[]})}else{d._rej("parse error")}return(d)}function macos_memUtilization(){var d={};var e=new promise(function(h,g){this._res=h;this._rej=g});var b=require("child_process").execFile("/bin/sh",["sh"]);b.stdout.str="";b.stdout.on("data",function(g){this.str+=g.toString()});b.stdin.write('top -l 1 | grep -E "^Phys"\nexit\n');b.waitExit();var c=b.stdout.str.split("\n");if(c[0].length>0){var f=c[0].split(":")[1];var a=f.split(",");d.MemTotal=parseInt(a[0].trim().split(" ")[0]);d.MemFree=parseInt(a[1].trim().split(" ")[0]);d.percentFree=((d.MemFree/d.MemTotal)*100).toFixed(2);d.percentConsumed=(((d.MemTotal-d.MemFree)/d.MemTotal)*100).toFixed(2);return(d)}else{throw ("Parse Error")}}switch(process.platform){case"linux":module.exports={cpuUtilization:linux_cpuUtilization,memUtilization:linux_memUtilization};break;case"win32":module.exports={cpuUtilization:windows_cpuUtilization,memUtilization:windows_memUtilization};break;case"darwin":module.exports={cpuUtilization:macos_cpuUtilization,memUtilization:macos_memUtilization};break};

File diff suppressed because one or more lines are too long

View File

@ -3,7 +3,7 @@
const crypto = require('crypto'); const crypto = require('crypto');
var settings = {}; var settings = {};
const args = require('minimist')(process.argv.slice(2)); const args = require('minimist')(process.argv.slice(2));
const possibleCommands = ['listusers', 'listdevicegroups', 'listdevices', 'listusersofdevicegroup', 'serverinfo', 'userinfo', 'adduser', 'removeuser', 'adddevicegroup', 'removedevicegroup', 'broadcast', 'addusertodevicegroup', 'removeuserfromdevicegroup', 'sendinviteemail']; const possibleCommands = ['listusers', 'listdevicegroups', 'listdevices', 'listusersofdevicegroup', 'serverinfo', 'userinfo', 'adduser', 'removeuser', 'adddevicegroup', 'removedevicegroup', 'broadcast', 'addusertodevicegroup', 'removeuserfromdevicegroup', 'sendinviteemail', 'generateinvitelink'];
//console.log(args); //console.log(args);
if (args['_'].length == 0) { if (args['_'].length == 0) {
@ -25,6 +25,7 @@ if (args['_'].length == 0) {
console.log(" AddUserToDeviceGroup - Add a user to a device group."); console.log(" AddUserToDeviceGroup - Add a user to a device group.");
console.log(" RemoveUserFromDeviceGroup - Remove a user from a device group."); console.log(" RemoveUserFromDeviceGroup - Remove a user from a device group.");
console.log(" SendInviteEmail - Send an agent install invitation email."); console.log(" SendInviteEmail - Send an agent install invitation email.");
console.log(" GenerateInviteLink - Create an invitation link.");
console.log(" Broadcast - Display a message to all online users."); console.log(" Broadcast - Display a message to all online users.");
console.log("\r\nSupported login arguments:"); console.log("\r\nSupported login arguments:");
console.log(" --url [wss://server] - Server url, wss://localhost:443 is default."); console.log(" --url [wss://server] - Server url, wss://localhost:443 is default.");
@ -96,6 +97,12 @@ if (args['_'].length == 0) {
else { ok = true; } else { ok = true; }
break; break;
} }
case 'generateinvitelink': {
if (args.id == null) { console.log("Device group identifier id missing, use --id [groupid]"); }
else if (args.hours == null) { console.log("Invitation validity period missing, use --hours [hours]"); }
else { ok = true; }
break;
}
case 'help': { case 'help': {
if (args['_'].length < 2) { 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(', ') + '.'); console.log("Get help on an action. Type:\r\n\r\n help [action]\r\n\r\nPossible actions are: " + possibleCommands.join(', ') + '.');
@ -109,6 +116,15 @@ if (args['_'].length == 0) {
console.log(" --email [email] - Email address."); console.log(" --email [email] - Email address.");
break; break;
} }
case 'generateinvitelink': {
console.log("Generate a agent invitation URL for a given group. Example usage:\r\n");
console.log(" MeshCtrl GenerateInviteLink --id devicegroupid --hours 24");
console.log(" MeshCtrl GenerateInviteLink --id devicegroupid --hours 0");
console.log("\r\nRequired arguments:\r\n");
console.log(" --id [groupid] - Device group identifier.");
console.log(" --hours [hours] - Validity period in hours or 0 for infinit.");
break;
}
case 'serverinfo': { case 'serverinfo': {
console.log("Get information on the MeshCentral server, Example usages:\r\n"); console.log("Get information on the MeshCentral server, Example usages:\r\n");
console.log(" MeshCtrl ServerInfo --loginuser myaccountname --loginpass mypassword"); console.log(" MeshCtrl ServerInfo --loginuser myaccountname --loginpass mypassword");
@ -395,6 +411,11 @@ function serverConnect() {
ws.send(JSON.stringify(op)); ws.send(JSON.stringify(op));
break; break;
} }
case 'generateinvitelink': {
var op = { action: "createInviteLink", meshid: args.id, expire: args.hours, flags: 0, responseid: 'meshctrl' }
ws.send(JSON.stringify(op));
break;
}
case 'broadcast': { case 'broadcast': {
var op = { action: 'userbroadcast', msg: args.msg, responseid: 'meshctrl' }; var op = { action: 'userbroadcast', msg: args.msg, responseid: 'meshctrl' };
ws.send(JSON.stringify(op)); ws.send(JSON.stringify(op));
@ -439,7 +460,7 @@ function serverConnect() {
case 'deletemesh': // REMOVEDEVICEGROUP case 'deletemesh': // REMOVEDEVICEGROUP
case 'addmeshuser': // case 'addmeshuser': //
case 'removemeshuser': // case 'removemeshuser': //
case 'inviteAgent': case 'inviteAgent': //
case 'userbroadcast': { // BROADCAST case 'userbroadcast': { // BROADCAST
if (data.responseid == 'meshctrl') { if (data.responseid == 'meshctrl') {
if (data.meshid) { console.log(data.result, data.meshid); } if (data.meshid) { console.log(data.result, data.meshid); }
@ -449,6 +470,13 @@ function serverConnect() {
} }
break; break;
} }
case 'createInviteLink':
if (data.responseid == 'meshctrl') {
if (data.url) { console.log(data.url); }
else console.log(data.result);
process.exit();
}
break;
case 'users': { // LISTUSERS case 'users': { // LISTUSERS
if (args.json) { if (args.json) {
console.log(JSON.stringify(data.users, ' ', 2)); console.log(JSON.stringify(data.users, ' ', 2));

View File

@ -48,10 +48,10 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use
obj.user = user; obj.user = user;
obj.domain = domain; obj.domain = domain;
// Server side amt stack // Server side Intel AMT stack
var WsmanComm = require('./amt/amt-wsman-comm.js'); const WsmanComm = require('./amt/amt-wsman-comm.js');
var Wsman = require('./amt/amt-wsman.js'); const Wsman = require('./amt/amt-wsman.js');
var Amt = require('./amt/amt.js'); const Amt = require('./amt/amt.js');
// Send a message to the user // Send a message to the user
//obj.send = function (data) { try { if (typeof data == 'string') { ws.send(Buffer.from(data, 'binary')); } else { ws.send(data); } } catch (e) { } } //obj.send = function (data) { try { if (typeof data == 'string') { ws.send(Buffer.from(data, 'binary')); } else { ws.send(data); } } catch (e) { } }
@ -2772,14 +2772,32 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use
break; break;
} }
case 'createInviteLink': { case 'createInviteLink': {
if (common.validateString(command.meshid, 8, 128) == false) break; // Check the meshid var err = null;
if (common.validateInt(command.expire, 0, 99999) == false) break; // Check the expire time in hours if (common.validateString(command.meshid, 8, 128) == false) { err = 'Invalid group id'; } // Check the meshid
if (common.validateInt(command.flags, 0, 256) == false) break; // Check the flags else if (common.validateInt(command.expire, 0, 99999) == false) { err = 'Invalid expire time'; } // Check the expire time in hours
else if (common.validateInt(command.flags, 0, 256) == false) { err = 'Invalid flags'; }; // Check the flags
if (command.meshid.split('/').length == 1) { command.meshid = 'mesh/' + domain.id + '/' + command.meshid; }
var smesh = command.meshid.split('/');
if ((smesh.length != 3) || (smesh[0] != 'mesh') || (smesh[1] != domain.id)) { err = 'Invalid group id'; }
// Handle any errors
if (err != null) {
if (command.responseid != null) { try { ws.send(JSON.stringify({ action: 'createInviteLink', responseid: command.responseid, result: err })); } catch (ex) { } }
break;
}
mesh = parent.meshes[command.meshid]; mesh = parent.meshes[command.meshid];
if (mesh == null) break; if (mesh == null) { if (command.responseid != null) { try { ws.send(JSON.stringify({ action: 'createInviteLink', responseid: command.responseid, result: 'Unable to find this device group id' })); } catch (ex) { } } break; }
const inviteCookie = parent.parent.encodeCookie({ a: 4, mid: command.meshid, f: command.flags, expire: command.expire * 60 }, parent.parent.invitationLinkEncryptionKey); const inviteCookie = parent.parent.encodeCookie({ a: 4, mid: command.meshid, f: command.flags, expire: command.expire * 60 }, parent.parent.invitationLinkEncryptionKey);
if (inviteCookie == null) break; if (inviteCookie == null) { if (command.responseid != null) { try { ws.send(JSON.stringify({ action: 'createInviteLink', responseid: command.responseid, result: 'Unable to generate invitation cookie' })); } catch (ex) { } } break; }
ws.send(JSON.stringify({ action: 'createInviteLink', meshid: command.meshid, expire: command.expire, cookie: inviteCookie }));
// Create the server url
var httpsPort = ((args.aliasport == null) ? args.port : args.aliasport); // Use HTTPS alias port is specified
var xdomain = (domain.dns == null) ? domain.id : '';
if (xdomain != '') xdomain += "/";
var url = "http" + (args.notls ? '' : 's') + "://" + parent.getWebServerName(domain) + ":" + httpsPort + "/" + xdomain + "agentinvite?c=" + inviteCookie;
ws.send(JSON.stringify({ action: 'createInviteLink', meshid: command.meshid, url: url, expire: command.expire, cookie: inviteCookie, responseid: command.responseid, tag: command.tag }));
break; break;
} }
case 'traceinfo': { case 'traceinfo': {
@ -2792,9 +2810,9 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use
case 'amt': { case 'amt': {
if (common.validateString(command.nodeid, 1, 1024) == false) break; // Check nodeid if (common.validateString(command.nodeid, 1, 1024) == false) break; // Check nodeid
if (common.validateInt(command.mode, 0, 3) == false) break; // Check connection mode if (common.validateInt(command.mode, 0, 3) == false) break; // Check connection mode
// validate if communication mode is possible // Validate if communication mode is possible
if (command.mode == null || command.mode==0) { if (command.mode == null || command.mode==0) {
break;//unsupported break; //unsupported
} else if (command.mode == 1) { } else if (command.mode == 1) {
var state = parent.parent.GetConnectivityState(command.nodeid); var state = parent.parent.GetConnectivityState(command.nodeid);
if ( (state == null) || (state.connectivity & 4)==0 ) break; if ( (state == null) || (state.connectivity & 4)==0 ) break;
@ -2811,7 +2829,7 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use
var node = nodes[0]; var node = nodes[0];
// Get the mesh for this device // Get the mesh for this device
mesh = parent.meshes[node.meshid]; var mesh = parent.meshes[node.meshid];
if (mesh) { if (mesh) {
// Check if this user has rights to do this // Check if this user has rights to do this
if (mesh.links[user._id] != null && ((mesh.links[user._id].rights & 8) != 0)) { // "Remote Control permission" if (mesh.links[user._id] != null && ((mesh.links[user._id].rights & 8) != 0)) { // "Remote Control permission"
@ -2951,29 +2969,27 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use
function getRandomPassword() { return Buffer.from(parent.crypto.randomBytes(9), 'binary').toString('base64').split('/').join('@'); } function getRandomPassword() { return Buffer.from(parent.crypto.randomBytes(9), 'binary').toString('base64').split('/').join('@'); }
function handleAmtCommand(cmd, node) { function handleAmtCommand(cmd, node) {
if (cmd==null) return; if (cmd == null) return;
var host = cmd.nodeid; var host = cmd.nodeid;
if (cmd.mode==1) { if (cmd.mode == 1) { host = node.host; }
host = node.host;
}
var tlsoptions = null; var tlsoptions = null;
var wsman = new Wsman(WsmanComm, host, node.intelamt.tls? 16993: 16992, node.intelamt.user, node.intelamt.pass, var wsman = new Wsman(WsmanComm, host, node.intelamt.tls ? 16993 : 16992, node.intelamt.user, node.intelamt.pass,
node.intelamt.tls,tlsoptions, parent.parent, cmd.mode); node.intelamt.tls, tlsoptions, parent.parent, cmd.mode);
var amt = new Amt(wsman); var amt = new Amt(wsman);
switch (cmd.command) { switch (cmd.command) {
case "Get-GeneralSettings": { case "Get-GeneralSettings": {
amt.Get("AMT_GeneralSettings", function(obj, name, response, status) { amt.Get("AMT_GeneralSettings", function (obj, name, response, status) {
if (status==200) { if (status == 200) {
var resp = { action: 'amt', nodeid: cmd.nodeid, command: 'Get-GeneralSettings', value: response.Body} var resp = { action: 'amt', nodeid: cmd.nodeid, command: 'Get-GeneralSettings', value: response.Body }
ws.send(JSON.stringify(resp)); ws.send(JSON.stringify(resp));
} else { } else {
ws.send(JSON.stringify({"error": error})); ws.send(JSON.stringify({ "error": error }));
} }
}); });
break; break;
} }
default: { default: {
// do nothing // Do nothing
} }
} }
} }

View File

@ -1,6 +1,6 @@
{ {
"name": "meshcentral", "name": "meshcentral",
"version": "0.4.1-b", "version": "0.4.1-c",
"keywords": [ "keywords": [
"Remote Management", "Remote Management",
"Intel AMT", "Intel AMT",

View File

@ -3173,9 +3173,7 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) {
// { 'Referrer-Policy': 'no-referrer', 'x-frame-options': 'SAMEORIGIN', 'X-XSS-Protection': '1; mode=block', 'X-Content-Type-Options': 'nosniff', 'Content-Security-Policy': "default-src http: ws: data: 'self';script-src http: 'unsafe-inline';style-src http: 'unsafe-inline'" }; // { 'Referrer-Policy': 'no-referrer', 'x-frame-options': 'SAMEORIGIN', 'X-XSS-Protection': '1; mode=block', 'X-Content-Type-Options': 'nosniff', 'Content-Security-Policy': "default-src http: ws: data: 'self';script-src http: 'unsafe-inline';style-src http: 'unsafe-inline'" };
if ((domain != null) && (domain.httpheaders != null) && (typeof domain.httpheaders == 'object')) { if ((domain != null) && (domain.httpheaders != null) && (typeof domain.httpheaders == 'object')) {
res.set(domain.httpheaders); res.set(domain.httpheaders);
} } else {
/*
else {
// Use default security headers // Use default security headers
res.set({ res.set({
"X-Frame-Options": "sameorigin", "X-Frame-Options": "sameorigin",
@ -3185,10 +3183,9 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) {
"Content-Security-Policy": "default-src 'none'; script-src 'self' 'unsafe-inline'; connect-src 'self'; img-src 'self' data:; style-src 'self' 'unsafe-inline'; frame-src 'self'; media-src 'self'" "Content-Security-Policy": "default-src 'none'; script-src 'self' 'unsafe-inline'; connect-src 'self'; img-src 'self' data:; style-src 'self' 'unsafe-inline'; frame-src 'self'; media-src 'self'"
}); });
} }
*/
// Check the session if bound to the external IP address // Check the session if bound to the external IP address
//if ((req.session.ip != null) && (req.ip != null) && (req.session.ip != req.ip)) { req.session = {}; } if ((req.session.ip != null) && (req.ip != null) && (req.session.ip != req.ip)) { req.session = {}; }
// Detect if this is a file sharing domain, if so, just share files. // Detect if this is a file sharing domain, if so, just share files.
if ((domain != null) && (domain.share != null)) { if ((domain != null) && (domain.share != null)) {