Added multi-pipe meshcore.js

This commit is contained in:
Ylian Saint-Hilaire 2020-10-09 18:35:59 -07:00
parent 05185df99d
commit fe2496e75b
2 changed files with 3898 additions and 205 deletions

3789
agents/meshcore-multipipe.js Normal file

File diff suppressed because it is too large Load Diff

View File

@ -153,7 +153,7 @@ function createMeshCore(agent) {
this.end(); // TODO: Must call end() on self to close the named pipe correctly. this.end(); // TODO: Must call end() on self to close the named pipe correctly.
this.parent._daipc = null; this.parent._daipc = null;
if (this._registered != null) { try { mesh.SendCommand({ action: 'sessions', type: 'app', value: {} }); } catch (e) { } } if (this._registered != null) { try { mesh.SendCommand({ action: 'sessions', type: 'app', value: {} }); } catch (e) { } }
}); });
c.on('data', function (chunk) { c.on('data', function (chunk) {
if (chunk.length < 4) { this.unshift(chunk); return; } if (chunk.length < 4) { this.unshift(chunk); return; }
var len = chunk.readUInt32LE(0); var len = chunk.readUInt32LE(0);
@ -195,8 +195,7 @@ function createMeshCore(agent) {
this._send(data); this._send(data);
break; break;
case 'descriptors': case 'descriptors':
require('ChainViewer').getSnapshot().then(function (f) require('ChainViewer').getSnapshot().then(function (f) {
{
this.tag.payload.result = f; this.tag.payload.result = f;
this.tag.ipc._send(this.tag.payload); this.tag.ipc._send(this.tag.payload);
}).parentPromise.tag = { ipc: this, payload: data }; }).parentPromise.tag = { ipc: this, payload: data };
@ -406,16 +405,12 @@ function createMeshCore(agent) {
childProcess = require('child_process'); childProcess = require('child_process');
if (mesh.hasKVM == 1) { // if the agent is compiled with KVM support if (mesh.hasKVM == 1) { // if the agent is compiled with KVM support
// Check if this computer supports a desktop // Check if this computer supports a desktop
try try {
{ if ((process.platform == 'win32') || (process.platform == 'darwin') || (require('monitor-info').kvm_x11_support)) {
if ((process.platform == 'win32') || (process.platform == 'darwin') || (require('monitor-info').kvm_x11_support))
{
meshCoreObj.caps |= 1; meshCoreObj.caps |= 1;
} }
else if(process.platform == 'linux' || process.platform == 'freebsd') else if (process.platform == 'linux' || process.platform == 'freebsd') {
{ require('monitor-info').on('kvmSupportDetected', function (value) {
require('monitor-info').on('kvmSupportDetected', function (value)
{
meshCoreObj.caps |= 1; meshCoreObj.caps |= 1;
mesh.SendCommand(meshCoreObj); mesh.SendCommand(meshCoreObj);
}); });
@ -478,20 +473,16 @@ function createMeshCore(agent) {
if (mesh.isControlChannelConnected) { mesh.SendCommand({ action: 'smbios', value: SMBiosTablesRaw }); } if (mesh.isControlChannelConnected) { mesh.SendCommand({ action: 'smbios', value: SMBiosTablesRaw }); }
// If SMBios tables say that Intel AMT is present, try to connect MEI // If SMBios tables say that Intel AMT is present, try to connect MEI
if (SMBiosTables.amtInfo && (SMBiosTables.amtInfo.AMT == true)) if (SMBiosTables.amtInfo && (SMBiosTables.amtInfo.AMT == true)) {
{
var amtmodule = require('amt-manage'); var amtmodule = require('amt-manage');
amt = new amtmodule(mesh, db, false); amt = new amtmodule(mesh, db, false);
amt.on('portBinding_LMS', function (map) amt.on('portBinding_LMS', function (map) {
{
var j = { action: 'lmsinfo', value: { ports: map.keys() } }; var j = { action: 'lmsinfo', value: { ports: map.keys() } };
mesh.SendCommand(j); mesh.SendCommand(j);
}); });
amt.on('stateChange_LMS', function (v) amt.on('stateChange_LMS', function (v) {
{
if (!meshCoreObj.intelamt) { meshCoreObj.intelamt = {}; } if (!meshCoreObj.intelamt) { meshCoreObj.intelamt = {}; }
switch(v) switch (v) {
{
case 0: case 0:
meshCoreObj.intelamt.microlms = 'DISABLED'; meshCoreObj.intelamt.microlms = 'DISABLED';
break; break;
@ -1198,11 +1189,9 @@ function createMeshCore(agent) {
s.tunnel = this; s.tunnel = this;
s.descriptorMetadata = "MeshAgent_relayTunnel"; s.descriptorMetadata = "MeshAgent_relayTunnel";
if (require('MeshAgent').idleTimeout != null) if (require('MeshAgent').idleTimeout != null) {
{
s.setTimeout(require('MeshAgent').idleTimeout * 1000); s.setTimeout(require('MeshAgent').idleTimeout * 1000);
s.on('timeout', function () s.on('timeout', function () {
{
this.ping(); this.ping();
this.setTimeout(require('MeshAgent').idleTimeout * 1000); this.setTimeout(require('MeshAgent').idleTimeout * 1000);
}); });
@ -1272,7 +1261,7 @@ function createMeshCore(agent) {
// Called when we get data from the server for a TCP relay (We have to skip the first received 'c' and pipe the rest) // Called when we get data from the server for a TCP relay (We have to skip the first received 'c' and pipe the rest)
function onTcpRelayServerTunnelData(data) { function onTcpRelayServerTunnelData(data) {
if (this.first == true) { if (this.first == true) {
this.first = false; this.first = false;
this.pipe(this.tcprelay, { dataTypeSkip: 1 }); // Pipe Server --> Target (don't pipe text type websocket frames) this.pipe(this.tcprelay, { dataTypeSkip: 1 }); // Pipe Server --> Target (don't pipe text type websocket frames)
} }
} }
@ -1362,8 +1351,7 @@ function createMeshCore(agent) {
// Check if this is a relay connection // Check if this is a relay connection
if ((data == 'c') || (data == 'cr')) { this.httprequest.state = 1; /*sendConsoleText("Tunnel #" + this.httprequest.index + " now active", this.httprequest.sessionid);*/ } if ((data == 'c') || (data == 'cr')) { this.httprequest.state = 1; /*sendConsoleText("Tunnel #" + this.httprequest.index + " now active", this.httprequest.sessionid);*/ }
} }
else else {
{
// Handle tunnel data // Handle tunnel data
if (this.httprequest.protocol == 0) { // 1 = Terminal (admin), 2 = Desktop, 5 = Files, 6 = PowerShell (admin), 7 = Plugin Data Exchange, 8 = Terminal (user), 9 = PowerShell (user), 10 = FileTransfer if (this.httprequest.protocol == 0) { // 1 = Terminal (admin), 2 = Desktop, 5 = Files, 6 = PowerShell (admin), 7 = Plugin Data Exchange, 8 = Terminal (user), 9 = PowerShell (user), 10 = FileTransfer
// Take a look at the protocol // Take a look at the protocol
@ -1388,8 +1376,7 @@ function createMeshCore(agent) {
this.write(JSON.stringify({ op: 'cancel' })); this.write(JSON.stringify({ op: 'cancel' }));
} }
} }
else if ((this.httprequest.protocol == 1) || (this.httprequest.protocol == 6) || (this.httprequest.protocol == 8) || (this.httprequest.protocol == 9)) else if ((this.httprequest.protocol == 1) || (this.httprequest.protocol == 6) || (this.httprequest.protocol == 8) || (this.httprequest.protocol == 9)) {
{
// //
// Remote Terminal // Remote Terminal
// //
@ -1405,10 +1392,8 @@ function createMeshCore(agent) {
this.descriptorMetadata = "Remote Terminal"; this.descriptorMetadata = "Remote Terminal";
if (process.platform == 'win32') if (process.platform == 'win32') {
{ if (!require('win-terminal').PowerShellCapable() && (this.httprequest.protocol == 6 || this.httprequest.protocol == 9)) {
if (!require('win-terminal').PowerShellCapable() && (this.httprequest.protocol == 6 || this.httprequest.protocol == 9))
{
this.httprequest.write(JSON.stringify({ ctrlChannel: '102938', type: 'console', msg: 'PowerShell is not supported on this version of windows', msgid: 1 })); this.httprequest.write(JSON.stringify({ ctrlChannel: '102938', type: 'console', msg: 'PowerShell is not supported on this version of windows', msgid: 1 }));
this.httprequest.s.end(); this.httprequest.s.end();
return; return;
@ -1420,27 +1405,23 @@ function createMeshCore(agent) {
this.httprequest.tpromise.that = this; this.httprequest.tpromise.that = this;
this.httprequest.tpromise.httprequest = this.httprequest; this.httprequest.tpromise.httprequest = this.httprequest;
this.end = function () this.end = function () {
{
if (this.httprequest.tpromise._consent) { this.httprequest.tpromise._consent.close(); } if (this.httprequest.tpromise._consent) { this.httprequest.tpromise._consent.close(); }
if (this.httprequest.connectionPromise) { this.httprequest.connectionPromise._rej('Closed'); } if (this.httprequest.connectionPromise) { this.httprequest.connectionPromise._rej('Closed'); }
// Remove the terminal session to the count to update the server // Remove the terminal session to the count to update the server
if (this.httprequest.userid != null) if (this.httprequest.userid != null) {
{
if (tunnelUserCount.terminal[this.httprequest.userid] != null) { tunnelUserCount.terminal[this.httprequest.userid]--; if (tunnelUserCount.terminal[this.httprequest.userid] <= 0) { delete tunnelUserCount.terminal[this.httprequest.userid]; } } if (tunnelUserCount.terminal[this.httprequest.userid] != null) { tunnelUserCount.terminal[this.httprequest.userid]--; if (tunnelUserCount.terminal[this.httprequest.userid] <= 0) { delete tunnelUserCount.terminal[this.httprequest.userid]; } }
try { mesh.SendCommand({ action: 'sessions', type: 'terminal', value: tunnelUserCount.terminal }); } catch (e) { } try { mesh.SendCommand({ action: 'sessions', type: 'terminal', value: tunnelUserCount.terminal }); } catch (e) { }
} }
if (process.platform == 'win32') if (process.platform == 'win32') {
{
// Unpipe the web socket // Unpipe the web socket
this.unpipe(this.httprequest._term); this.unpipe(this.httprequest._term);
if (this.httprequest._term) { this.httprequest._term.unpipe(this); } if (this.httprequest._term) { this.httprequest._term.unpipe(this); }
// Unpipe the WebRTC channel if needed (This will also be done when the WebRTC channel ends). // Unpipe the WebRTC channel if needed (This will also be done when the WebRTC channel ends).
if (this.rtcchannel) if (this.rtcchannel) {
{
this.rtcchannel.unpipe(this.httprequest._term); this.rtcchannel.unpipe(this.httprequest._term);
if (this.httprequest._term) { this.httprequest._term.unpipe(this.rtcchannel); } if (this.httprequest._term) { this.httprequest._term.unpipe(this.rtcchannel); }
} }
@ -1452,8 +1433,7 @@ function createMeshCore(agent) {
}; };
// Perform User-Consent if needed. // Perform User-Consent if needed.
if (this.httprequest.consent && (this.httprequest.consent & 16)) if (this.httprequest.consent && (this.httprequest.consent & 16)) {
{
this.write(JSON.stringify({ ctrlChannel: '102938', type: 'console', msg: "Waiting for user to grant access...", msgid: 1 })); this.write(JSON.stringify({ ctrlChannel: '102938', type: 'console', msg: "Waiting for user to grant access...", msgid: 1 }));
var consentMessage = this.httprequest.username + " requesting remote terminal access. Grant access?", consentTitle = 'MeshCentral'; var consentMessage = this.httprequest.username + " requesting remote terminal access. Grant access?", consentTitle = 'MeshCentral';
if (this.httprequest.soptions != null) { if (this.httprequest.soptions != null) {
@ -1463,106 +1443,85 @@ function createMeshCore(agent) {
this.httprequest.tpromise._consent = require('message-box').create(consentTitle, consentMessage, 30); this.httprequest.tpromise._consent = require('message-box').create(consentTitle, consentMessage, 30);
this.httprequest.tpromise._consent.retPromise = this.httprequest.tpromise; this.httprequest.tpromise._consent.retPromise = this.httprequest.tpromise;
this.httprequest.tpromise._consent.then( this.httprequest.tpromise._consent.then(
function () function () {
{
// Success // Success
MeshServerLogEx(27, null, "Local user accepted remote terminal request (" + this.retPromise.httprequest.remoteaddr + ")", this.retPromise.that.httprequest); MeshServerLogEx(27, null, "Local user accepted remote terminal request (" + this.retPromise.httprequest.remoteaddr + ")", this.retPromise.that.httprequest);
this.retPromise.that.write(JSON.stringify({ ctrlChannel: '102938', type: 'console', msg: null, msgid: 0 })); this.retPromise.that.write(JSON.stringify({ ctrlChannel: '102938', type: 'console', msg: null, msgid: 0 }));
this.retPromise._consent = null; this.retPromise._consent = null;
this.retPromise._res(); this.retPromise._res();
}, },
function (e) function (e) {
{
// Denied // Denied
MeshServerLogEx(28, null, "Local user rejected remote terminal request (" + this.retPromise.that.httprequest.remoteaddr + ")", this.retPromise.that.httprequest); MeshServerLogEx(28, null, "Local user rejected remote terminal request (" + this.retPromise.that.httprequest.remoteaddr + ")", this.retPromise.that.httprequest);
this.retPromise.that.write(JSON.stringify({ ctrlChannel: '102938', type: 'console', msg: e.toString(), msgid: 2 })); this.retPromise.that.write(JSON.stringify({ ctrlChannel: '102938', type: 'console', msg: e.toString(), msgid: 2 }));
this.retPromise._rej(e.toString()); this.retPromise._rej(e.toString());
}); });
} }
else else {
{
// User-Consent is not required, so just resolve this promise // User-Consent is not required, so just resolve this promise
this.httprequest.tpromise._res(); this.httprequest.tpromise._res();
} }
this.httprequest.tpromise.then( this.httprequest.tpromise.then(
function () function () {
{
this.httprequest.connectionPromise = new prom(function (res, rej) { this._res = res; this._rej = rej; }); this.httprequest.connectionPromise = new prom(function (res, rej) { this._res = res; this._rej = rej; });
this.httprequest.connectionPromise.ws = this.that; this.httprequest.connectionPromise.ws = this.that;
// Start Terminal // Start Terminal
if(process.platform == 'win32') if (process.platform == 'win32') {
{ try {
try
{
var cols = 80, rows = 25; var cols = 80, rows = 25;
if (this.httprequest.xoptions) if (this.httprequest.xoptions) {
{
if (this.httprequest.xoptions.rows) { rows = this.httprequest.xoptions.rows; } if (this.httprequest.xoptions.rows) { rows = this.httprequest.xoptions.rows; }
if (this.httprequest.xoptions.cols) { cols = this.httprequest.xoptions.cols; } if (this.httprequest.xoptions.cols) { cols = this.httprequest.xoptions.cols; }
} }
if ((this.httprequest.protocol == 1) || (this.httprequest.protocol == 6)) if ((this.httprequest.protocol == 1) || (this.httprequest.protocol == 6)) {
{
// Admin Terminal // Admin Terminal
if (require('win-virtual-terminal').supported) if (require('win-virtual-terminal').supported) {
{
// ConPTY PseudoTerminal // ConPTY PseudoTerminal
// this.httprequest._term = require('win-virtual-terminal')[this.httprequest.protocol == 6 ? 'StartPowerShell' : 'Start'](80, 25); // this.httprequest._term = require('win-virtual-terminal')[this.httprequest.protocol == 6 ? 'StartPowerShell' : 'Start'](80, 25);
// The above line is commented out, because there is a bug with ClosePseudoConsole() API, so this is the workaround // The above line is commented out, because there is a bug with ClosePseudoConsole() API, so this is the workaround
this.httprequest._dispatcher = require('win-dispatcher').dispatch({ modules: [{ name: 'win-virtual-terminal', script: getJSModule('win-virtual-terminal') }], launch: { module: 'win-virtual-terminal', method: (this.httprequest.protocol == 6 ? 'StartPowerShell' : 'Start'), args: [cols, rows] } }); this.httprequest._dispatcher = require('win-dispatcher').dispatch({ modules: [{ name: 'win-virtual-terminal', script: getJSModule('win-virtual-terminal') }], launch: { module: 'win-virtual-terminal', method: (this.httprequest.protocol == 6 ? 'StartPowerShell' : 'Start'), args: [cols, rows] } });
this.httprequest._dispatcher.httprequest = this.httprequest; this.httprequest._dispatcher.httprequest = this.httprequest;
this.httprequest._dispatcher.on('connection', function (c) this.httprequest._dispatcher.on('connection', function (c) {
{ if (this.httprequest.connectionPromise.completed) {
if (this.httprequest.connectionPromise.completed)
{
c.end(); c.end();
} }
else else {
{
this.httprequest.connectionPromise._res(c); this.httprequest.connectionPromise._res(c);
} }
}); });
} }
else else {
{
// Legacy Terminal // Legacy Terminal
this.httprequest.connectionPromise._res(require('win-terminal')[this.httprequest.protocol == 6 ? 'StartPowerShell' : 'Start'](cols, rows)); this.httprequest.connectionPromise._res(require('win-terminal')[this.httprequest.protocol == 6 ? 'StartPowerShell' : 'Start'](cols, rows));
} }
} }
else else {
{
// Logged in user // Logged in user
var userPromise = require('user-sessions').enumerateUsers(); var userPromise = require('user-sessions').enumerateUsers();
userPromise.that = this; userPromise.that = this;
userPromise.then(function (u) userPromise.then(function (u) {
{
var that = this.that; var that = this.that;
if (u.Active.length > 0) if (u.Active.length > 0) {
{
var username = u.Active[0].Username; var username = u.Active[0].Username;
if (require('win-virtual-terminal').supported) if (require('win-virtual-terminal').supported) {
{
// ConPTY PseudoTerminal // ConPTY PseudoTerminal
that.httprequest._dispatcher = require('win-dispatcher').dispatch({ user: username, modules: [{ name: 'win-virtual-terminal', script: getJSModule('win-virtual-terminal') }], launch: { module: 'win-virtual-terminal', method: (that.httprequest.protocol == 9 ? 'StartPowerShell' : 'Start'), args: [cols, rows] } }); that.httprequest._dispatcher = require('win-dispatcher').dispatch({ user: username, modules: [{ name: 'win-virtual-terminal', script: getJSModule('win-virtual-terminal') }], launch: { module: 'win-virtual-terminal', method: (that.httprequest.protocol == 9 ? 'StartPowerShell' : 'Start'), args: [cols, rows] } });
} }
else else {
{
// Legacy Terminal // Legacy Terminal
that.httprequest._dispatcher = require('win-dispatcher').dispatch({ user: username, modules: [{ name: 'win-terminal', script: getJSModule('win-terminal') }], launch: { module: 'win-terminal', method: (that.httprequest.protocol == 9 ? 'StartPowerShell' : 'Start'), args: [cols, rows] } }); that.httprequest._dispatcher = require('win-dispatcher').dispatch({ user: username, modules: [{ name: 'win-terminal', script: getJSModule('win-terminal') }], launch: { module: 'win-terminal', method: (that.httprequest.protocol == 9 ? 'StartPowerShell' : 'Start'), args: [cols, rows] } });
} }
that.httprequest._dispatcher.ws = that; that.httprequest._dispatcher.ws = that;
that.httprequest._dispatcher.on('connection', function (c) that.httprequest._dispatcher.on('connection', function (c) {
{ if (this.ws.httprequest.connectionPromise.completed) {
if (this.ws.httprequest.connectionPromise.completed)
{
c.end(); c.end();
} }
else else {
{
this.ws.httprequest.connectionPromise._res(c); this.ws.httprequest.connectionPromise._res(c);
} }
}); });
@ -1570,70 +1529,58 @@ function createMeshCore(agent) {
}); });
} }
} }
catch (e) catch (e) {
{
this.httprequest.connectionPromise._rej('Failed to start remote terminal session, ' + e.toString()); this.httprequest.connectionPromise._rej('Failed to start remote terminal session, ' + e.toString());
} }
} }
else else {
{ try {
try
{
var bash = fs.existsSync('/bin/bash') ? '/bin/bash' : false; var bash = fs.existsSync('/bin/bash') ? '/bin/bash' : false;
var sh = fs.existsSync('/bin/sh') ? '/bin/sh' : false; var sh = fs.existsSync('/bin/sh') ? '/bin/sh' : false;
var login = process.platform == 'linux' ? '/bin/login' : '/usr/bin/login'; var login = process.platform == 'linux' ? '/bin/login' : '/usr/bin/login';
var env = { HISTCONTROL: 'ignoreboth' }; var env = { HISTCONTROL: 'ignoreboth' };
if (this.httprequest.xoptions) if (this.httprequest.xoptions) {
{
if (this.httprequest.xoptions.rows) { env.LINES = ('' + this.httprequest.xoptions.rows); } if (this.httprequest.xoptions.rows) { env.LINES = ('' + this.httprequest.xoptions.rows); }
if (this.httprequest.xoptions.cols) { env.COLUMNS = ('' + this.httprequest.xoptions.cols); } if (this.httprequest.xoptions.cols) { env.COLUMNS = ('' + this.httprequest.xoptions.cols); }
} }
var options = { type: childProcess.SpawnTypes.TERM, uid: (this.httprequest.protocol == 8) ? require('user-sessions').consoleUid() : null, env: env }; var options = { type: childProcess.SpawnTypes.TERM, uid: (this.httprequest.protocol == 8) ? require('user-sessions').consoleUid() : null, env: env };
if (this.httprequest.xoptions && this.httprequest.xoptions.requireLogin) if (this.httprequest.xoptions && this.httprequest.xoptions.requireLogin) {
{
if (!require('fs').existsSync(login)) { throw ('Unable to spawn login process'); } if (!require('fs').existsSync(login)) { throw ('Unable to spawn login process'); }
this.httprequest.connectionPromise._res(childProcess.execFile(login, ['login'], options)); // Start login shell this.httprequest.connectionPromise._res(childProcess.execFile(login, ['login'], options)); // Start login shell
} }
else if (bash) else if (bash) {
{
var p = childProcess.execFile(bash, ['bash'], options); // Start bash var p = childProcess.execFile(bash, ['bash'], options); // Start bash
// Spaces at the beginning of lines are needed to hide commands from the command history // Spaces at the beginning of lines are needed to hide commands from the command history
if (process.platform == 'linux') { p.stdin.write(' alias ls=\'ls --color=auto\';clear\n'); } if (process.platform == 'linux') { p.stdin.write(' alias ls=\'ls --color=auto\';clear\n'); }
this.httprequest.connectionPromise._res(p); this.httprequest.connectionPromise._res(p);
} }
else if (sh) else if (sh) {
{
var p = childProcess.execFile(sh, ['sh'], options); // Start sh var p = childProcess.execFile(sh, ['sh'], options); // Start sh
// Spaces at the beginning of lines are needed to hide commands from the command history // Spaces at the beginning of lines are needed to hide commands from the command history
if (process.platform == 'linux') { p.stdin.write(' alias ls=\'ls --color=auto\';clear\n'); } if (process.platform == 'linux') { p.stdin.write(' alias ls=\'ls --color=auto\';clear\n'); }
this.httprequest.connectionPromise._res(p); this.httprequest.connectionPromise._res(p);
} }
else else {
{
this.httprequest.connectionPromise._rej('Failed to start remote terminal session, no shell found'); this.httprequest.connectionPromise._rej('Failed to start remote terminal session, no shell found');
} }
} }
catch (e) catch (e) {
{
this.httprequest.connectionPromise._rej('Failed to start remote terminal session, ' + e.toString()); this.httprequest.connectionPromise._rej('Failed to start remote terminal session, ' + e.toString());
} }
} }
this.httprequest.connectionPromise.then( this.httprequest.connectionPromise.then(
function (term) function (term) {
{
// SUCCESS // SUCCESS
var stdoutstream; var stdoutstream;
var stdinstream; var stdinstream;
if (process.platform == 'win32') if (process.platform == 'win32') {
{
this.ws.httprequest._term = term; this.ws.httprequest._term = term;
this.ws.httprequest._term.tunnel = this.ws; this.ws.httprequest._term.tunnel = this.ws;
stdoutstream = stdinstream = term; stdoutstream = stdinstream = term;
} }
else else {
{
term.descriptorMetadata = 'Remote Terminal'; term.descriptorMetadata = 'Remote Terminal';
this.ws.httprequest.process = term; this.ws.httprequest.process = term;
this.ws.httprequest.process.tunnel = this.ws; this.ws.httprequest.process.tunnel = this.ws;
@ -1652,15 +1599,13 @@ function createMeshCore(agent) {
this.ws.pipe(stdinstream, { dataTypeSkip: 1, end: false }); // 0 = Binary, 1 = Text. this.ws.pipe(stdinstream, { dataTypeSkip: 1, end: false }); // 0 = Binary, 1 = Text.
// Add the terminal session to the count to update the server // Add the terminal session to the count to update the server
if (this.ws.httprequest.userid != null) if (this.ws.httprequest.userid != null) {
{
if (tunnelUserCount.terminal[this.ws.httprequest.userid] == null) { tunnelUserCount.terminal[this.ws.httprequest.userid] = 1; } else { tunnelUserCount.terminal[this.ws.httprequest.userid]++; } if (tunnelUserCount.terminal[this.ws.httprequest.userid] == null) { tunnelUserCount.terminal[this.ws.httprequest.userid] = 1; } else { tunnelUserCount.terminal[this.ws.httprequest.userid]++; }
try { mesh.SendCommand({ action: 'sessions', type: 'terminal', value: tunnelUserCount.terminal }); } catch (e) { } try { mesh.SendCommand({ action: 'sessions', type: 'terminal', value: tunnelUserCount.terminal }); } catch (e) { }
} }
// Toast Notification, if required // Toast Notification, if required
if (this.ws.httprequest.consent && (this.ws.httprequest.consent & 2)) if (this.ws.httprequest.consent && (this.ws.httprequest.consent & 2)) {
{
// User Notifications is required // User Notifications is required
var notifyMessage = this.ws.httprequest.username + " started a remote terminal session.", notifyTitle = "MeshCentral"; var notifyMessage = this.ws.httprequest.username + " started a remote terminal session.", notifyTitle = "MeshCentral";
if (this.ws.httprequest.soptions != null) { if (this.ws.httprequest.soptions != null) {
@ -1670,22 +1615,19 @@ function createMeshCore(agent) {
try { require('toaster').Toast(notifyTitle, notifyMessage); } catch (e) { } try { require('toaster').Toast(notifyTitle, notifyMessage); } catch (e) { }
} }
}, },
function (e) function (e) {
{
// FAILED to connect terminal // FAILED to connect terminal
this.ws.write(JSON.stringify({ ctrlChannel: '102938', type: 'console', msg: e.toString(), msgid: 2 })); this.ws.write(JSON.stringify({ ctrlChannel: '102938', type: 'console', msg: e.toString(), msgid: 2 }));
this.ws.end(); this.ws.end();
}); });
}, },
function (e) function (e) {
{
// DO NOT start terminal // DO NOT start terminal
this.that.write(JSON.stringify({ ctrlChannel: '102938', type: 'console', msg: e.toString(), msgid: 2 })); this.that.write(JSON.stringify({ ctrlChannel: '102938', type: 'console', msg: e.toString(), msgid: 2 }));
this.that.end(); this.that.end();
}); });
} }
else if (this.httprequest.protocol == 2) else if (this.httprequest.protocol == 2) {
{
// //
// Remote KVM // Remote KVM
// //
@ -1739,22 +1681,19 @@ function createMeshCore(agent) {
} }
// Unpipe the web socket // Unpipe the web socket
try try {
{
this.unpipe(this.httprequest.desktop.kvm); this.unpipe(this.httprequest.desktop.kvm);
this.httprequest.desktop.kvm.unpipe(this); this.httprequest.desktop.kvm.unpipe(this);
} }
catch(e) { } catch (e) { }
// Unpipe the WebRTC channel if needed (This will also be done when the WebRTC channel ends). // Unpipe the WebRTC channel if needed (This will also be done when the WebRTC channel ends).
if (this.rtcchannel) if (this.rtcchannel) {
{ try {
try
{
this.rtcchannel.unpipe(this.httprequest.desktop.kvm); this.rtcchannel.unpipe(this.httprequest.desktop.kvm);
this.httprequest.desktop.kvm.unpipe(this.rtcchannel); this.httprequest.desktop.kvm.unpipe(this.rtcchannel);
} }
catch(e) { } catch (e) { }
} }
// Place wallpaper back if needed // Place wallpaper back if needed
@ -1813,8 +1752,7 @@ function createMeshCore(agent) {
} }
// Perform notification if needed. Toast messages may not be supported on all platforms. // Perform notification if needed. Toast messages may not be supported on all platforms.
if (this.httprequest.consent && (this.httprequest.consent & 8)) if (this.httprequest.consent && (this.httprequest.consent & 8)) {
{
// User Consent Prompt is required // User Consent Prompt is required
// Send a console message back using the console channel, "\n" is supported. // Send a console message back using the console channel, "\n" is supported.
this.write(JSON.stringify({ ctrlChannel: '102938', type: 'console', msg: "Waiting for user to grant access...", msgid: 1 })); this.write(JSON.stringify({ ctrlChannel: '102938', type: 'console', msg: "Waiting for user to grant access...", msgid: 1 }));
@ -1827,10 +1765,9 @@ function createMeshCore(agent) {
pr.ws = this; pr.ws = this;
this.pause(); this.pause();
this._consentpromise = pr; this._consentpromise = pr;
this.prependOnceListener('end', function () { if (this._consentpromise && this._consentpromise.close) { this._consentpromise.close(); }}); this.prependOnceListener('end', function () { if (this._consentpromise && this._consentpromise.close) { this._consentpromise.close(); } });
pr.then( pr.then(
function () function () {
{
// Success // Success
this.ws._consentpromise = null; this.ws._consentpromise = null;
MeshServerLogEx(30, null, "Starting remote desktop after local user accepted (" + this.ws.httprequest.remoteaddr + ")", this.ws.httprequest); MeshServerLogEx(30, null, "Starting remote desktop after local user accepted (" + this.ws.httprequest.remoteaddr + ")", this.ws.httprequest);
@ -1873,8 +1810,7 @@ function createMeshCore(agent) {
this.ws.httprequest.desktop.kvm.pipe(this.ws, { dataTypeSkip: 1 }); this.ws.httprequest.desktop.kvm.pipe(this.ws, { dataTypeSkip: 1 });
this.ws.resume(); this.ws.resume();
}, },
function (e) function (e) {
{
// User Consent Denied/Failed // User Consent Denied/Failed
this.ws._consentpromise = null; this.ws._consentpromise = null;
MeshServerLogEx(34, null, "Failed to start remote desktop after local user rejected (" + this.ws.httprequest.remoteaddr + ")", this.ws.httprequest); MeshServerLogEx(34, null, "Failed to start remote desktop after local user rejected (" + this.ws.httprequest.remoteaddr + ")", this.ws.httprequest);
@ -1971,8 +1907,7 @@ function createMeshCore(agent) {
this._consentpromise = pr; this._consentpromise = pr;
this.prependOnceListener('end', function () { if (this._consentpromise && this._consentpromise.close) { this._consentpromise.close(); } }); this.prependOnceListener('end', function () { if (this._consentpromise && this._consentpromise.close) { this._consentpromise.close(); } });
pr.then( pr.then(
function () function () {
{
// Success // Success
this.ws._consentpromise = null; this.ws._consentpromise = null;
MeshServerLogEx(40, null, "Starting remote files after local user accepted (" + this.ws.httprequest.remoteaddr + ")", this.ws.httprequest); MeshServerLogEx(40, null, "Starting remote files after local user accepted (" + this.ws.httprequest.remoteaddr + ")", this.ws.httprequest);
@ -1988,8 +1923,7 @@ function createMeshCore(agent) {
} }
this.ws.resume(); this.ws.resume();
}, },
function (e) function (e) {
{
// User Consent Denied/Failed // User Consent Denied/Failed
this.ws._consentpromise = null; this.ws._consentpromise = null;
MeshServerLogEx(41, null, "Failed to start remote files after local user rejected (" + this.ws.httprequest.remoteaddr + ")", this.ws.httprequest); MeshServerLogEx(41, null, "Failed to start remote files after local user rejected (" + this.ws.httprequest.remoteaddr + ")", this.ws.httprequest);
@ -2355,13 +2289,11 @@ function createMeshCore(agent) {
} }
case 'termsize': { case 'termsize': {
// Indicates a change in terminal size // Indicates a change in terminal size
if (process.platform == 'win32') if (process.platform == 'win32') {
{
if (ws.httprequest._dispatcher == null) return; if (ws.httprequest._dispatcher == null) return;
//sendConsoleText('Win32-TermSize: ' + obj.cols + 'x' + obj.rows); //sendConsoleText('Win32-TermSize: ' + obj.cols + 'x' + obj.rows);
if (ws.httprequest._dispatcher.invoke) { ws.httprequest._dispatcher.invoke('resizeTerminal', [obj.cols, obj.rows]); } if (ws.httprequest._dispatcher.invoke) { ws.httprequest._dispatcher.invoke('resizeTerminal', [obj.cols, obj.rows]); }
} else } else {
{
if (ws.httprequest.process == null || ws.httprequest.process.pty == 0) return; if (ws.httprequest.process == null || ws.httprequest.process.pty == 0) return;
//sendConsoleText('Linux Resize: ' + obj.cols + 'x' + obj.rows); //sendConsoleText('Linux Resize: ' + obj.cols + 'x' + obj.rows);
@ -2442,10 +2374,8 @@ function createMeshCore(agent) {
this.websocket.rtcchannel.on('end', function () { this.websocket.rtcchannel.on('end', function () {
// The WebRTC channel closed, unpipe the KVM now. This is also done when the web socket closes. // The WebRTC channel closed, unpipe the KVM now. This is also done when the web socket closes.
//sendConsoleText('Tunnel #' + this.websocket.tunnel.index + ' WebRTC data channel closed'); //sendConsoleText('Tunnel #' + this.websocket.tunnel.index + ' WebRTC data channel closed');
if (this.websocket.desktop && this.websocket.desktop.kvm) if (this.websocket.desktop && this.websocket.desktop.kvm) {
{ try {
try
{
this.unpipe(this.websocket.desktop.kvm); this.unpipe(this.websocket.desktop.kvm);
this.websocket.httprequest.desktop.kvm.unpipe(this); this.websocket.httprequest.desktop.kvm.unpipe(this);
} }
@ -2522,7 +2452,7 @@ function createMeshCore(agent) {
case 'help': { // Displays available commands case 'help': { // Displays available commands
var fin = '', f = '', availcommands = 'coredump,service,fdsnapshot,fdcount,startupoptions,alert,agentsize,versions,help,info,osinfo,args,print,type,dbkeys,dbget,dbset,dbcompact,eval,parseuri,httpget,nwslist,plugin,wsconnect,wssend,wsclose,notify,ls,ps,kill,amt,netinfo,location,power,wakeonlan,setdebug,smbios,rawsmbios,toast,lock,users,sendcaps,openurl,amtreset,amtccm,amtacm,amtdeactivate,amtpolicy,getscript,getclip,setclip,log,av,cpuinfo,sysinfo,apf,scanwifi,scanamt,wallpaper,agentmsg'; var fin = '', f = '', availcommands = 'coredump,service,fdsnapshot,fdcount,startupoptions,alert,agentsize,versions,help,info,osinfo,args,print,type,dbkeys,dbget,dbset,dbcompact,eval,parseuri,httpget,nwslist,plugin,wsconnect,wssend,wsclose,notify,ls,ps,kill,amt,netinfo,location,power,wakeonlan,setdebug,smbios,rawsmbios,toast,lock,users,sendcaps,openurl,amtreset,amtccm,amtacm,amtdeactivate,amtpolicy,getscript,getclip,setclip,log,av,cpuinfo,sysinfo,apf,scanwifi,scanamt,wallpaper,agentmsg';
if (process.platform == 'win32') { availcommands += ',safemode,wpfhwacceleration,uac'; } if (process.platform == 'win32') { availcommands += ',safemode,wpfhwacceleration,uac'; }
if (process.platform != 'freebsd') { availcommands += ',vm';} if (process.platform != 'freebsd') { availcommands += ',vm'; }
if (require('MeshAgent').maxKvmTileSize != null) { availcommands += ',kvmmode'; } if (require('MeshAgent').maxKvmTileSize != null) { availcommands += ',kvmmode'; }
try { require('zip-reader'); availcommands += ',zip,unzip'; } catch (e) { } try { require('zip-reader'); availcommands += ',zip,unzip'; } catch (e) { }
@ -2566,8 +2496,7 @@ function createMeshCore(agent) {
if (args['_'].length != 1) { if (args['_'].length != 1) {
response = "Proper usage: coredump on|off|status|clear"; // Display usage response = "Proper usage: coredump on|off|status|clear"; // Display usage
} else { } else {
switch (args['_'][0].toLowerCase()) switch (args['_'][0].toLowerCase()) {
{
case 'on': case 'on':
process.coreDumpLocation = (process.platform == 'win32') ? (process.execPath.replace('.exe', '.dmp')) : (process.execPath + '.dmp'); process.coreDumpLocation = (process.platform == 'win32') ? (process.execPath.replace('.exe', '.dmp')) : (process.execPath + '.dmp');
response = 'coredump is now on'; response = 'coredump is now on';
@ -2597,25 +2526,20 @@ function createMeshCore(agent) {
} }
break; break;
case 'service': case 'service':
if (args['_'].length != 1) if (args['_'].length != 1) {
{
response = "Proper usage: service status|restart"; // Display usage response = "Proper usage: service status|restart"; // Display usage
} }
else else {
{
var s = require('service-manager').manager.getService(process.platform == 'win32' ? 'Mesh Agent' : 'meshagent'); var s = require('service-manager').manager.getService(process.platform == 'win32' ? 'Mesh Agent' : 'meshagent');
switch(args['_'][0].toLowerCase()) switch (args['_'][0].toLowerCase()) {
{
case 'status': case 'status':
response = 'Service ' + (s.isRunning() ? (s.isMe() ? '[SELF]' : '[RUNNING]') : ('[NOT RUNNING]')); response = 'Service ' + (s.isRunning() ? (s.isMe() ? '[SELF]' : '[RUNNING]') : ('[NOT RUNNING]'));
break; break;
case 'restart': case 'restart':
if (s.isMe()) if (s.isMe()) {
{
s.restart(); s.restart();
} }
else else {
{
response = 'Restarting another agent instance is not allowed'; response = 'Restarting another agent instance is not allowed';
} }
break; break;
@ -2672,51 +2596,41 @@ function createMeshCore(agent) {
break; break;
case 'fdcount': case 'fdcount':
require('DescriptorEvents').getDescriptorCount().then( require('DescriptorEvents').getDescriptorCount().then(
function (c) function (c) {
{
sendConsoleText('Descriptor Count: ' + c, this.sessionid); sendConsoleText('Descriptor Count: ' + c, this.sessionid);
}, function (e) }, function (e) {
{
sendConsoleText('Error fetching descriptor count: ' + e, this.sessionid); sendConsoleText('Error fetching descriptor count: ' + e, this.sessionid);
}).parentPromise.sessionid = sessionid; }).parentPromise.sessionid = sessionid;
break; break;
case 'uac': case 'uac':
if (process.platform != 'win32') if (process.platform != 'win32') {
{
response = 'Unknown command "uac", type "help" for list of avaialble commands.'; response = 'Unknown command "uac", type "help" for list of avaialble commands.';
break; break;
} }
if (args['_'].length != 1) if (args['_'].length != 1) {
{
response = 'Proper usage: uac [get|interactive|secure]'; response = 'Proper usage: uac [get|interactive|secure]';
} }
else else {
{ switch (args['_'][0].toUpperCase()) {
switch(args['_'][0].toUpperCase())
{
case 'GET': case 'GET':
var secd = require('win-registry').QueryKey(require('win-registry').HKEY.LocalMachine, 'Software\\Microsoft\\Windows\\CurrentVersion\\Policies\\System', 'PromptOnSecureDesktop'); var secd = require('win-registry').QueryKey(require('win-registry').HKEY.LocalMachine, 'Software\\Microsoft\\Windows\\CurrentVersion\\Policies\\System', 'PromptOnSecureDesktop');
response = "UAC mode: " + (secd == 0 ? "Interactive Desktop" : "Secure Desktop"); response = "UAC mode: " + (secd == 0 ? "Interactive Desktop" : "Secure Desktop");
break; break;
case 'INTERACTIVE': case 'INTERACTIVE':
try try {
{
require('win-registry').WriteKey(require('win-registry').HKEY.LocalMachine, 'Software\\Microsoft\\Windows\\CurrentVersion\\Policies\\System', 'PromptOnSecureDesktop', 0); require('win-registry').WriteKey(require('win-registry').HKEY.LocalMachine, 'Software\\Microsoft\\Windows\\CurrentVersion\\Policies\\System', 'PromptOnSecureDesktop', 0);
response = 'UAC mode changed to: Interactive Desktop'; response = 'UAC mode changed to: Interactive Desktop';
} }
catch (e) catch (e) {
{
response = "Unable to change UAC Mode"; response = "Unable to change UAC Mode";
} }
break; break;
case 'SECURE': case 'SECURE':
try try {
{
require('win-registry').WriteKey(require('win-registry').HKEY.LocalMachine, 'Software\\Microsoft\\Windows\\CurrentVersion\\Policies\\System', 'PromptOnSecureDesktop', 1); require('win-registry').WriteKey(require('win-registry').HKEY.LocalMachine, 'Software\\Microsoft\\Windows\\CurrentVersion\\Policies\\System', 'PromptOnSecureDesktop', 1);
response = 'UAC mode changed to: Secure Desktop'; response = 'UAC mode changed to: Secure Desktop';
} }
catch(e) catch (e) {
{
response = "Unable to change UAC Mode"; response = "Unable to change UAC Mode";
} }
break; break;
@ -2726,45 +2640,37 @@ function createMeshCore(agent) {
} }
} }
break; break;
case 'vm': case 'vm':
response = 'Virtual Machine = ' + require('identifiers').isVM(); response = 'Virtual Machine = ' + require('identifiers').isVM();
break; break;
case 'startupoptions': case 'startupoptions':
response = JSON.stringify(require('MeshAgent').getStartupOptions()); response = JSON.stringify(require('MeshAgent').getStartupOptions());
break; break;
case 'kvmmode': case 'kvmmode':
if (require('MeshAgent').maxKvmTileSize == null) if (require('MeshAgent').maxKvmTileSize == null) {
{
response = "Unknown command \"kvmmode\", type \"help\" for list of avaialble commands."; response = "Unknown command \"kvmmode\", type \"help\" for list of avaialble commands.";
} }
else else {
{ if (require('MeshAgent').maxKvmTileSize == 0) {
if(require('MeshAgent').maxKvmTileSize == 0)
{
response = 'KVM Mode: Full JUMBO'; response = 'KVM Mode: Full JUMBO';
} }
else else {
{
response = 'KVM Mode: ' + (require('MeshAgent').maxKvmTileSize <= 65500 ? 'NO JUMBO' : 'Partial JUMBO'); response = 'KVM Mode: ' + (require('MeshAgent').maxKvmTileSize <= 65500 ? 'NO JUMBO' : 'Partial JUMBO');
response += (', TileLimit: ' + (require('MeshAgent').maxKvmTileSize < 1024 ? (require('MeshAgent').maxKvmTileSize + ' bytes') : (Math.round(require('MeshAgent').maxKvmTileSize/1024) + ' Kbytes'))); response += (', TileLimit: ' + (require('MeshAgent').maxKvmTileSize < 1024 ? (require('MeshAgent').maxKvmTileSize + ' bytes') : (Math.round(require('MeshAgent').maxKvmTileSize / 1024) + ' Kbytes')));
} }
} }
break; break;
case 'alert': case 'alert':
if (args['_'].length == 0) if (args['_'].length == 0) {
{
response = "Proper usage: alert TITLE, CAPTION [, TIMEOUT]"; // Display usage response = "Proper usage: alert TITLE, CAPTION [, TIMEOUT]"; // Display usage
} }
else else {
{
var p = args['_'].join(' ').split(','); var p = args['_'].join(' ').split(',');
if(p.length<2) if (p.length < 2) {
{
response = "Proper usage: alert TITLE, CAPTION [, TIMEOUT]"; // Display usage response = "Proper usage: alert TITLE, CAPTION [, TIMEOUT]"; // Display usage
} }
else else {
{ this._alert = require('message-box').create(p[0], p[1], p.length == 3 ? parseInt(p[2]) : 9999, 1);
this._alert = require('message-box').create(p[0], p[1], p.length==3?parseInt(p[2]):9999,1);
} }
} }
break; break;
@ -3137,7 +3043,7 @@ function createMeshCore(agent) {
break; break;
} }
case 'info': { // Return information about the agent and agent core module case 'info': { // Return information about the agent and agent core module
response = 'Current Core: ' + meshCoreObj.value + '\r\nAgent Time: ' + Date() + '.\r\nUser Rights: 0x' + rights.toString(16) + '.\r\nPlatform: ' + process.platform + '.\r\nCapabilities: ' + meshCoreObj.caps + '.\r\nServer URL: ' + mesh.ServerUrl + '.'; response = 'Current Core: ' + meshCoreObj.value + '\r\nAgent Time: ' + Date() + '.\r\nUser Rights: 0x' + rights.toString(16) + '.\r\nPlatform: ' + process.platform + '.\r\nCapabilities: ' + meshCoreObj.caps + '.\r\nServer URL: ' + mesh.ServerUrl + '.';
if (amt != null) { response += '\r\nBuilt-in LMS: ' + ['Disabled', 'Connecting..', 'Connected'][amt.lmsstate] + '.'; } if (amt != null) { response += '\r\nBuilt-in LMS: ' + ['Disabled', 'Connecting..', 'Connected'][amt.lmsstate] + '.'; }
if (meshCoreObj.osdesc) { response += '\r\nOS: ' + meshCoreObj.osdesc + '.'; } if (meshCoreObj.osdesc) { response += '\r\nOS: ' + meshCoreObj.osdesc + '.'; }
response += '\r\nModules: ' + addedModules.join(', ') + '.'; response += '\r\nModules: ' + addedModules.join(', ') + '.';
@ -3641,10 +3547,8 @@ function createMeshCore(agent) {
try { try {
if (meinfo == null) return; if (meinfo == null) return;
var intelamt = {}; var intelamt = {};
if (amt != null) if (amt != null) {
{ switch (amt.lmsstate) {
switch(amt.lmsstate)
{
case 0: intelamt.microlms = 'DISABLED'; break; case 0: intelamt.microlms = 'DISABLED'; break;
case 1: intelamt.microlms = 'CONNECTING'; break; case 1: intelamt.microlms = 'CONNECTING'; break;
case 2: intelamt.microlms = 'CONNECTED'; break; case 2: intelamt.microlms = 'CONNECTED'; break;