Merge branch 'master' into master

This commit is contained in:
elastalink 2019-04-15 21:04:54 -04:00 committed by GitHub
commit 27c34b9540
32 changed files with 2391 additions and 149 deletions

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -423,6 +423,8 @@ function createMeshCore(agent) {
tunnel.on('error', function (e) { sendConsoleText('ERROR: ' + JSON.stringify(e)); });
tunnel.sessionid = data.sessionid;
tunnel.rights = data.rights;
tunnel.consent = data.consent;
tunnel.username = data.username;
tunnel.state = 0;
tunnel.url = xurl;
tunnel.protocol = 0;
@ -706,6 +708,11 @@ function createMeshCore(agent) {
return;
}
// Perform notification if needed
if (this.httprequest.consent && (this.httprequest.consent & 2)) {
require('toaster').Toast('MeshCentral', this.httprequest.username + ' started a remote terminal session.');
}
// Remote terminal using native pipes
if (process.platform == "win32")
{
@ -759,14 +766,16 @@ function createMeshCore(agent) {
return;
}
// Perform notification if needed
if (this.httprequest.consent && (this.httprequest.consent & 1)) {
require('toaster').Toast('MeshCentral', this.httprequest.username + ' started a remote desktop session.');
}
// Remote desktop using native pipes
this.httprequest.desktop = { state: 0, kvm: mesh.getRemoteDesktopStream(), tunnel: this };
this.httprequest.desktop.kvm.parent = this.httprequest.desktop;
this.desktop = this.httprequest.desktop;
// Display a toast message
//require('toaster').Toast('MeshCentral', 'Remote Desktop Control Started.');
this.end = function () {
--this.desktop.kvm.connectionCount;
@ -811,6 +820,11 @@ function createMeshCore(agent) {
return;
}
// Perform notification if needed
if (this.httprequest.consent && (this.httprequest.consent & 4)) {
require('toaster').Toast('MeshCentral', this.httprequest.username + ' started a remote file access.');
}
// Setup files
// NOP
}

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,206 @@
/*
Copyright 2019 Intel Corporation
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
require('MeshAgent').on('Connected', function (status)
{
if (status == 0)
{
return;
}
this.timeout = setTimeout(start, 10000);
});
function sendServerLog(msg)
{
require('MeshAgent').SendCommand({ action: 'diagnostic', value: { command: 'log', value: msg } });
}
function getMeshAgentService()
{
try
{
var ret = require('service-manager').manager.getService(process.platform == 'win32' ? 'mesh agent' : 'meshagent');
return(ret);
}
catch(e)
{
return (null);
}
}
function getARCHID() {
var ret = 0;
switch (process.platform) {
case 'linux':
// Need to detect Architecture ID
var child = require('child_process').execFile('/bin/sh', ['sh']);
child.stdout.str = '';
child.stdout.on('data', function (chunk) { this.str += chunk.toString(); });
child.stdin.write("uname -m\nexit\n");
child.waitExit();
switch (child.stdout.str.trim()) {
case 'x86_64':
case 'amd64':
ret = 6;
break;
case 'x86':
case 'i686':
case 'i586':
case 'i386':
ret = 5;
break;
case 'armv6l':
case 'armv7l':
ret = 25;
break;
default:
break;
}
break;
case 'darwin':
ret = 16;
break;
case 'win32':
ret = process.arch == 'x64' ? 4 : 3;
break;
}
return (ret);
}
function DownloadAgentBinary(path, ID)
{
var options = require('http').parseUri(require('MeshAgent').ServerInfo.ServerUri);
var downloadUri = 'https://' + options.host + ':' + options.port + '/meshagents?id=' + (ID != null ? ID : getARCHID());
sendServerLog('Diagnostic: Attempting to downlod agent from: ' + downloadUri);
return (wget(downloadUri, path, { rejectUnauthorized: false }));
}
function giveup()
{
sendServerLog('Diagnostic: Unable to diagnose Mesh Agent');
finished();
}
function finished()
{
sendServerLog('Diagnostic: End');
require('service-manager').manager.getService('meshagentDiagnostic').stop();
}
function ConfigureAgent(agent)
{
sendServerLog('...Configuring Agent...');
var info = require('MeshAgent').ServerInfo;
var msh = 'MeshID=0x' + info.MeshID + '\n' + 'ServerID=' + info.ServerID + '\n' + 'MeshServer=' + info.ServerUri + '\n';
var cfg = require('global-tunnel').proxyConfig;
if(cfg == null)
{
msh += 'ignoreProxyFile=1\n';
}
else
{
msh += ('WebProxy=' + cfg.host + ':' + cfg.port + '\n');
}
if(process.platform == 'win32')
{
require('fs').writeFileSync(agent.appLocation().replace('.exe', '.msh'), msh);
}
else
{
require('fs').writeFileSync(agent.appLocation() + '.msh', msh);
}
}
function start()
{
sendServerLog('Diagnostic: Start');
var id = getARCHID();
var s = getMeshAgentService();
if (s == null)
{
DownloadAgentBinary('agent_temporary.bin').then(function ()
{
// SUCCESS
try
{
var agent = require('service-manager').manager.installService(
{
name: process.platform == 'win32' ? 'Mesh Agent' : 'meshagent',
target: 'meshagent',
description: 'Mesh Central Agent v2 Background Service',
displayName: 'Mesh Agent v2 Background Service',
servicePath: 'agent_temporary.bin',
startType: 'DEMAND_START'
});
require('fs').unlinkSync('agent_temporary.bin');
ConfigureAgent(agent);
}
catch(e)
{
giveup();
}
},
function ()
{
// FAILURE
giveup();
});
}
if(s!=null)
{
// Mesh Agent Installation Found
sendServerLog('Diagnostic: Mesh Agent Service => ' + (s.isRunning() ? 'RUNNING' : 'NOT-RUNNING'));
if(s.isRunning())
{
finished();
}
else
{
sendServerLog('Diagnostic: Attempting to start Mesh Agent');
s.start();
sendServerLog('Diagnostic: ' + (s.isRunning() ? '(SUCCESS)' : '(FAILED)'));
if (s.isRunning())
{
finished();
return;
}
else
{
DownloadAgentBinary(s.appLocation()).then(
function () {
sendServerLog('Diagnostic: Downloaded Successfully');
sendServerLog('Diagnostic: Attempting to start Mesh Agent');
s.start();
sendServerLog('Diagnostic: ' + (s.isRunning() ? '(SUCCESS)' : '(FAILED)'));
if (s.isRunning()) {
finished();
return;
}
else {
giveup();
}
},
function () {
sendServerLog('Diagnostic: Download Failed');
giveup();
});
}
}
}
};

View File

@ -152,7 +152,7 @@ DownloadAgent() {
then
# upstart
echo -e "start on runlevel [2345]\nstop on runlevel [016]\n\nrespawn\n\nchdir /usr/local/mesh\nexec /usr/local/mesh/meshagent\n\n" > /etc/init/meshagent.conf
service meshagent start
initctl start meshagent
echo 'meshagent installed as upstart/init.d service.'
echo 'To start service: sudo initctl start meshagent'
echo 'To stop service: sudo initctl stop meshagent'
@ -193,7 +193,7 @@ UninstallAgent() {
rm -f /sbin/meshcmd /etc/init.d/meshagent
elif [ $starttype -eq 2 ]; then
# upstart
service meshagent stop
initctl stop meshagent
rm -f /sbin/meshcmd
rm -f /etc/init/meshagent.conf
rm -f /etc/rc2.d/S20mesh /etc/rc3.d/S20mesh /etc/rc5.d/S20mesh

View File

@ -109,10 +109,10 @@ module.exports.setup = function(binary, startvars) {
var argcount = ReadShort(obj.script, obj.ip + 4);
var argptr = obj.ip + 6;
var args = [];
// Clear all temp variables (This is optional)
for (var i in obj.variables) { if (i.startsWith('__')) { delete obj.variables[i]; } }
// Loop on each argument, moving forward by the argument length each time
for (var i = 0; i < argcount; i++) {
var arglen = ReadShort(obj.script, argptr);
@ -122,7 +122,7 @@ module.exports.setup = function(binary, startvars) {
if (argtyp < 2) {
// Get the value and replace all {var} with variable values
argval = argval.toString();
while (argval.split("{").length > 1) { var t = argval.split("{").pop().split("}").shift(); argval = argval.replace('{' + t + '}', obj.getVar(t)); }
if (argval != null) { while (argval.split("{").length > 1) { var t = argval.split("{").pop().split("}").shift(); argval = argval.replace('{' + t + '}', obj.getVar(t)); } }
if (argtyp == 1) { obj.variables['__' + i] = decodeURI(argval); argval = '__' + i; } // If argtyp is 1, this is a literal. Store in temp variable.
args.push(argval);
}
@ -132,7 +132,7 @@ module.exports.setup = function(binary, startvars) {
}
argptr += (2 + arglen);
}
// Move instruction pointer forward by command size
obj.ip += cmdlen;

File diff suppressed because one or more lines are too long

View File

@ -145,7 +145,7 @@ function WindowsConsole()
this.TrayIcon.remove();
handled = true;
}
if (!handled) { console.log(msg); }
//if (!handled) { console.log(msg); }
}
});
retVal.remove = function remove()

View File

@ -1 +1 @@
var TrayIconFlags={NIF_MESSAGE:1,NIF_ICON:2,NIF_TIP:4,NIF_STATE:8,NIF_INFO:16,NIF_GUID:32,NIF_REALTIME:64,NIF_SHOWTIP:128,NIM_ADD:0,NIM_MODIFY:1,NIM_DELETE:2,NIM_SETFOCUS:3,NIM_SETVERSION:4};var NOTIFYICON_VERSION_4=4;var MessageTypes={WM_APP:32768,WM_USER:1024};function WindowsConsole(){if(process.platform=="win32"){this._ObjectID="win-console";this._Marshal=require("_GenericMarshal");this._kernel32=this._Marshal.CreateNativeProxy("kernel32.dll");this._user32=this._Marshal.CreateNativeProxy("user32.dll");this._kernel32.CreateMethod("GetConsoleWindow");this._kernel32.CreateMethod("GetCurrentThread");this._user32.CreateMethod("ShowWindow");this._user32.CreateMethod("LoadImageA");this._user32.CreateMethod({method:"GetMessageA",threadDispatch:1});this._shell32=this._Marshal.CreateNativeProxy("Shell32.dll");this._shell32.CreateMethod("Shell_NotifyIconA");this._handle=this._kernel32.GetConsoleWindow();this.minimize=function(){this._user32.ShowWindow(this._handle,6)};this.restore=function(){this._user32.ShowWindow(this._handle,9)};this.hide=function(){this._user32.ShowWindow(this._handle,0)};this.show=function(){this._user32.ShowWindow(this._handle,5)};this._loadicon=function(c){var b=this._user32.LoadImageA(0,this._Marshal.CreateVariable(c),1,0,0,16|32768|64);return(b)};this.SetTrayIcon=function a(h){var b=this._Marshal.CreateVariable(this._Marshal.PointerSize==4?508:528);b.toBuffer().writeUInt32LE(b._size,0);var n=TrayIconFlags.NIF_TIP|TrayIconFlags.NIF_MESSAGE;h.filter=MessageTypes.WM_APP+1;b.Deref(this._Marshal.PointerSize==4?16:24,4).toBuffer().writeUInt32LE(h.filter);if(!h.noBalloon){n|=TrayIconFlags.NIF_INFO}if(h.icon){n|=TrayIconFlags.NIF_ICON;var c=b.Deref(this._Marshal.PointerSize==4?20:32,this._Marshal.PointerSize);h.icon.pointerBuffer().copy(c.toBuffer())}b.Deref(this._Marshal.PointerSize*2,4).toBuffer().writeUInt32LE(1);b.Deref(this._Marshal.PointerSize==4?12:20,4).toBuffer().writeUInt32LE(n);b.Deref(this._Marshal.PointerSize==4?416:432,4).toBuffer().writeUInt32LE(NOTIFYICON_VERSION_4);var m=b.Deref(this._Marshal.PointerSize==4?24:40,128);var k=b.Deref(this._Marshal.PointerSize==4?160:176,256);var l=b.Deref(this._Marshal.PointerSize==4?420:436,64);if(h.szTip){Buffer.from(h.szTip).copy(m.toBuffer())}if(h.szInfo){Buffer.from(h.szInfo).copy(k.toBuffer())}if(h.szInfoTitle){Buffer.from(h.szInfoTitle).copy(l.toBuffer())}var d=require("win-message-pump");retVal={_ObjectID:"WindowsConsole.TrayIcon",MessagePump:new d(h)};var j=require("events").inherits(retVal);j.createEvent("ToastClicked");j.createEvent("IconHover");j.createEvent("ToastDismissed");retVal.Options=h;retVal.MessagePump.TrayIcon=retVal;retVal.MessagePump.NotifyData=b;retVal.MessagePump.WindowsConsole=this;retVal.MessagePump.on("exit",function e(o){console.log("Pump Exited");if(this.TrayIcon){this.TrayIcon.remove()}});retVal.MessagePump.on("hwnd",function f(o){h.hwnd=o;o.pointerBuffer().copy(this.NotifyData.Deref(this.WindowsConsole._Marshal.PointerSize,this.WindowsConsole._Marshal.PointerSize).toBuffer());if(this.WindowsConsole._shell32.Shell_NotifyIconA(TrayIconFlags.NIM_ADD,this.NotifyData).Val==0){}});retVal.MessagePump.on("message",function g(p){if(p.message==this.TrayIcon.Options.filter){var o=false;if(p.wparam==1&&p.lparam==1029){this.TrayIcon.emit("ToastClicked");o=true}if(p.wparam==1&&p.lparam==512){this.TrayIcon.emit("IconHover");o=true}if(this.TrayIcon.Options.balloonOnly&&p.wparam==1&&(p.lparam==1028||p.lparam==1029)){this.TrayIcon.emit("ToastDismissed");this.TrayIcon.remove();o=true}if(!o){console.log(p)}}});retVal.remove=function i(){this.MessagePump.WindowsConsole._shell32.Shell_NotifyIconA(TrayIconFlags.NIM_DELETE,this.MessagePump.NotifyData);this.MessagePump.stop();delete this.MessagePump.TrayIcon;delete this.MessagePump};return(retVal)}}}module.exports=new WindowsConsole();
var TrayIconFlags={NIF_MESSAGE:1,NIF_ICON:2,NIF_TIP:4,NIF_STATE:8,NIF_INFO:16,NIF_GUID:32,NIF_REALTIME:64,NIF_SHOWTIP:128,NIM_ADD:0,NIM_MODIFY:1,NIM_DELETE:2,NIM_SETFOCUS:3,NIM_SETVERSION:4};var NOTIFYICON_VERSION_4=4;var MessageTypes={WM_APP:32768,WM_USER:1024};function WindowsConsole(){if(process.platform=="win32"){this._ObjectID="win-console";this._Marshal=require("_GenericMarshal");this._kernel32=this._Marshal.CreateNativeProxy("kernel32.dll");this._user32=this._Marshal.CreateNativeProxy("user32.dll");this._kernel32.CreateMethod("GetConsoleWindow");this._kernel32.CreateMethod("GetCurrentThread");this._user32.CreateMethod("ShowWindow");this._user32.CreateMethod("LoadImageA");this._user32.CreateMethod({method:"GetMessageA",threadDispatch:1});this._shell32=this._Marshal.CreateNativeProxy("Shell32.dll");this._shell32.CreateMethod("Shell_NotifyIconA");this._handle=this._kernel32.GetConsoleWindow();this.minimize=function(){this._user32.ShowWindow(this._handle,6)};this.restore=function(){this._user32.ShowWindow(this._handle,9)};this.hide=function(){this._user32.ShowWindow(this._handle,0)};this.show=function(){this._user32.ShowWindow(this._handle,5)};this._loadicon=function(c){var b=this._user32.LoadImageA(0,this._Marshal.CreateVariable(c),1,0,0,16|32768|64);return(b)};this.SetTrayIcon=function a(h){var b=this._Marshal.CreateVariable(this._Marshal.PointerSize==4?508:528);b.toBuffer().writeUInt32LE(b._size,0);var n=TrayIconFlags.NIF_TIP|TrayIconFlags.NIF_MESSAGE;h.filter=MessageTypes.WM_APP+1;b.Deref(this._Marshal.PointerSize==4?16:24,4).toBuffer().writeUInt32LE(h.filter);if(!h.noBalloon){n|=TrayIconFlags.NIF_INFO}if(h.icon){n|=TrayIconFlags.NIF_ICON;var c=b.Deref(this._Marshal.PointerSize==4?20:32,this._Marshal.PointerSize);h.icon.pointerBuffer().copy(c.toBuffer())}b.Deref(this._Marshal.PointerSize*2,4).toBuffer().writeUInt32LE(1);b.Deref(this._Marshal.PointerSize==4?12:20,4).toBuffer().writeUInt32LE(n);b.Deref(this._Marshal.PointerSize==4?416:432,4).toBuffer().writeUInt32LE(NOTIFYICON_VERSION_4);var m=b.Deref(this._Marshal.PointerSize==4?24:40,128);var k=b.Deref(this._Marshal.PointerSize==4?160:176,256);var l=b.Deref(this._Marshal.PointerSize==4?420:436,64);if(h.szTip){Buffer.from(h.szTip).copy(m.toBuffer())}if(h.szInfo){Buffer.from(h.szInfo).copy(k.toBuffer())}if(h.szInfoTitle){Buffer.from(h.szInfoTitle).copy(l.toBuffer())}var d=require("win-message-pump");retVal={_ObjectID:"WindowsConsole.TrayIcon",MessagePump:new d(h)};var j=require("events").inherits(retVal);j.createEvent("ToastClicked");j.createEvent("IconHover");j.createEvent("ToastDismissed");retVal.Options=h;retVal.MessagePump.TrayIcon=retVal;retVal.MessagePump.NotifyData=b;retVal.MessagePump.WindowsConsole=this;retVal.MessagePump.on("exit",function e(o){console.log("Pump Exited");if(this.TrayIcon){this.TrayIcon.remove()}});retVal.MessagePump.on("hwnd",function f(o){h.hwnd=o;o.pointerBuffer().copy(this.NotifyData.Deref(this.WindowsConsole._Marshal.PointerSize,this.WindowsConsole._Marshal.PointerSize).toBuffer());if(this.WindowsConsole._shell32.Shell_NotifyIconA(TrayIconFlags.NIM_ADD,this.NotifyData).Val==0){}});retVal.MessagePump.on("message",function g(p){if(p.message==this.TrayIcon.Options.filter){var o=false;if(p.wparam==1&&p.lparam==1029){this.TrayIcon.emit("ToastClicked");o=true}if(p.wparam==1&&p.lparam==512){this.TrayIcon.emit("IconHover");o=true}if(this.TrayIcon.Options.balloonOnly&&p.wparam==1&&(p.lparam==1028||p.lparam==1029)){this.TrayIcon.emit("ToastDismissed");this.TrayIcon.remove();o=true}}});retVal.remove=function i(){this.MessagePump.WindowsConsole._shell32.Shell_NotifyIconA(TrayIconFlags.NIM_DELETE,this.MessagePump.NotifyData);this.MessagePump.stop();delete this.MessagePump.TrayIcon;delete this.MessagePump};return(retVal)}}}module.exports=new WindowsConsole();

View File

@ -128,10 +128,11 @@ module.exports.escapeHtml = function (string) { return String(string).replace(/[
module.exports.escapeHtmlBreaks = function (string) { return String(string).replace(/[&<>"'`=\/]/g, function (s) { return { '&': '&amp;', '<': '&lt;', '>': '&gt;', '"': '&quot;', "'": '&#39;', '/': '&#x2F;', '`': '&#x60;', '=': '&#x3D;', '\r': '<br />', '\n': '' }[s]; }); };
// Lowercase all the names in a object recursively
module.exports.objKeysToLower = function (obj) {
// Allow for exception keys, child of exceptions will not get lower-cased.
module.exports.objKeysToLower = function (obj, exceptions) {
for (var i in obj) {
if (i.toLowerCase() !== i) { obj[i.toLowerCase()] = obj[i]; delete obj[i]; } // LowerCase all key names
if (typeof obj[i] == 'object') { module.exports.objKeysToLower(obj[i]); } // LowerCase all key names in the child object
if ((typeof obj[i] == 'object') && ((exceptions == null) || (exceptions.indexOf(i.toLowerCase()) == -1))) { module.exports.objKeysToLower(obj[i], exceptions); } // LowerCase all key names in the child object
}
return obj;
};

23
db.js
View File

@ -281,7 +281,27 @@ module.exports.CreateDB = function (parent) {
// Database actions on the main collection
obj.Set = function (data, func) { obj.file.update({ _id: data._id }, data, { upsert: true }, func); };
obj.Get = function (id, func) { obj.file.find({ _id: id }, func); };
obj.Get = function (id, func)
{
if (arguments.length > 2)
{
var parms = [func];
for (var parmx = 2; parmx < arguments.length; ++parmx) { parms.push(arguments[parmx]); }
var func2 = function _func2(arg1, arg2)
{
var userCallback = _func2.userArgs.shift();
_func2.userArgs.unshift(arg2);
_func2.userArgs.unshift(arg1);
userCallback.apply(obj, _func2.userArgs);
};
func2.userArgs = parms;
obj.file.find({ _id: id }, func2);
}
else
{
obj.file.find({ _id: id }, func);
}
};
obj.GetAll = function (func) { obj.file.find({}, func); };
obj.GetAllTypeNoTypeField = function (type, domain, func) { obj.file.find({ type: type, domain: domain }, { type: 0 }, func); };
obj.GetAllTypeNoTypeFieldMeshFiltered = function (meshes, domain, type, id, func) { var x = { type: type, domain: domain, meshid: { $in: meshes } }; if (id) { x._id = id; } obj.file.find(x, { type: 0 }, func); };
@ -422,6 +442,7 @@ module.exports.CreateDB = function (parent) {
// This is used to rate limit a number of operation per day. Returns a startValue each new days, but you can substract it and save the value in the db.
obj.getValueOfTheDay = function (id, startValue, func) { obj.Get(id, function (err, docs) { var date = new Date(), t = date.toLocaleDateString(); if (docs.length == 1) { var r = docs[0]; if (r.day == t) { func({ _id: id, value: r.value, day: t }); return; } } func({ _id: id, value: startValue, day: t }); }); };
obj.escapeBase64 = function escapeBase64(val) { return (val.replace(/\+/g, '@').replace(/\//g, '$')); }
function Clone(v) { return JSON.parse(JSON.stringify(v)); }

View File

@ -522,6 +522,18 @@ module.exports.CreateMeshAgent = function (parent, db, ws, req, args, domain) {
if ((obj.authenticated != 1) || (obj.meshid == null) || obj.pendingCompleteAgentConnection || (obj.agentInfo == null)) { return; }
obj.pendingCompleteAgentConnection = true;
// If this is a recovery agent
if (obj.agentInfo.capabilities & 0x40) {
// Inform mesh agent that it's authenticated.
delete obj.pendingCompleteAgentConnection;
obj.authenticated = 2;
obj.send(common.ShortToStr(4));
// Ask for mesh core hash.
obj.send(common.ShortToStr(11) + common.ShortToStr(0));
return;
}
// Check if we have too many agent sessions
if (typeof domain.limits.maxagentsessions == 'number') {
// Count the number of agent sessions for this domain
@ -647,6 +659,13 @@ module.exports.CreateMeshAgent = function (parent, db, ws, req, args, domain) {
if (mesh.flags && (mesh.flags & 2) && (device.name != obj.agentInfo.computerName)) { device.name = obj.agentInfo.computerName; change = 1; } // We want the server name to be sync'ed to the hostname
if (change == 1) {
// Do some clean up if needed, these values should not be in the database.
if (device.conn != null) { delete device.conn; }
if (device.pwr != null) { delete device.pwr; }
if (device.agct != null) { delete device.agct; }
if (device.cict != null) { delete device.cict; }
// Save the updated device in the database
db.Set(device);
// If this is a temporary device, don't log changes
@ -700,40 +719,25 @@ module.exports.CreateMeshAgent = function (parent, db, ws, req, args, domain) {
// Not sure why, but in rare cases, obj.agentInfo is undefined here.
if ((obj.agentInfo == null) || (typeof obj.agentInfo.capabilities != 'number')) { return; } // This is an odd case.
if ((obj.agentInfo.capabilities & 64) != 0) {
// This is a recovery agent
obj.send(common.ShortToStr(11) + common.ShortToStr(0)); // Command 11, ask for mesh core hash.
} else {
// Check if we need to make an native update check
obj.agentExeInfo = parent.parent.meshAgentBinaries[obj.agentInfo.agentId];
const corename = parent.parent.meshAgentsArchitectureNumbers[obj.agentInfo.agentId].core;
if (corename == null) { obj.send(common.ShortToStr(10) + common.ShortToStr(0)); } // MeshCommand_CoreModule, ask mesh agent to clear the core
// Check if we need to make an native update check
obj.agentExeInfo = parent.parent.meshAgentBinaries[obj.agentInfo.agentId];
const corename = parent.parent.meshAgentsArchitectureNumbers[obj.agentInfo.agentId].core;
if (corename == null) { obj.send(common.ShortToStr(10) + common.ShortToStr(0)); } // MeshCommand_CoreModule, ask mesh agent to clear the core
if ((obj.agentExeInfo != null) && (obj.agentExeInfo.update == true)) {
// Ask the agent for it's executable binary hash
obj.send(common.ShortToStr(12) + common.ShortToStr(0));
if ((obj.agentExeInfo != null) && (obj.agentExeInfo.update == true)) {
// Ask the agent for it's executable binary hash
obj.send(common.ShortToStr(12) + common.ShortToStr(0));
} else {
// Check the mesh core, if the agent is capable of running one
if (((obj.agentInfo.capabilities & 16) != 0) && (corename != null)) {
obj.send(common.ShortToStr(11) + common.ShortToStr(0)); // Command 11, ask for mesh core hash.
} else {
// Check the mesh core, if the agent is capable of running one
if (((obj.agentInfo.capabilities & 16) != 0) && (corename != null)) {
obj.send(common.ShortToStr(11) + common.ShortToStr(0)); // Command 11, ask for mesh core hash.
} else {
agentCoreIsStable(); // No updates needed, agent is ready to go.
}
agentCoreIsStable(); // No updates needed, agent is ready to go.
}
}
});
}
function recoveryAgentCoreIsStable() {
// Recovery agent is doing ok, lets perform main agent checking.
// TODO
console.log('recoveryAgentCoreIsStable()');
// Close the recovery agent connection when done.
//obj.close(1);
}
// Take a basic Intel AMT policy and add all server information to it, making it ready to send to this agent.
function completeIntelAmtPolicy(amtPolicy) {
if (amtPolicy == null) return null;
@ -764,6 +768,30 @@ module.exports.CreateMeshAgent = function (parent, db, ws, req, args, domain) {
}
}
function recoveryAgentCoreIsStable(mesh) {
// Recovery agent is doing ok, lets perform main agent checking.
//console.log('recoveryAgentCoreIsStable()');
// Fetch the the real agent nodeid
db.Get('da' + obj.dbNodeKey, function (err, nodes, self)
{
if (nodes.length == 1)
{
self.realNodeKey = nodes[0].raid;
// Get agent connection state
var agentConnected = false;
var state = parent.parent.GetConnectivityState(self.realNodeKey);
if (state) { agentConnected = ((state.connectivity & 1) != 0) }
self.send(JSON.stringify({ action: 'diagnostic', value: { command: 'query', value: self.realNodeKey, agent: agentConnected } }));
} else
{
self.send(JSON.stringify({ action: 'diagnostic', value: { command: 'query', value: null } }));
}
}, obj);
}
function agentCoreIsStable() {
// Check that the mesh exists
const mesh = parent.meshes[obj.dbMeshKey];
@ -772,6 +800,20 @@ module.exports.CreateMeshAgent = function (parent, db, ws, req, args, domain) {
return; // Probably not worth doing anything else. Hold this agent.
}
// Check if this is a recovery agent
if (obj.agentInfo.capabilities & 0x40) {
recoveryAgentCoreIsStable(mesh);
return;
}
// Fetch the the real agent nodeid
db.Get('ra' + obj.dbNodeKey, function (err, nodes) {
if (nodes.length == 1) {
obj.diagnosticNodeKey = nodes[0].daid;
obj.send(JSON.stringify({ action: 'diagnostic', value: { command: 'query', value: obj.diagnosticNodeKey } }));
}
});
// Send Intel AMT policy
if (obj.agentExeInfo && (obj.agentExeInfo.amt == true) && (mesh.amt != null)) { // Only send Intel AMT policy to agents what could have AMT.
try { obj.send(JSON.stringify({ action: 'amtPolicy', amtPolicy: completeIntelAmtPolicy(common.Clone(mesh.amt)) })); } catch (ex) { }
@ -1066,6 +1108,56 @@ module.exports.CreateMeshAgent = function (parent, db, ws, req, args, domain) {
}
break;
}
case 'diagnostic':
{
if (typeof command.value == 'object') {
switch (command.value.command) {
case 'register': {
// Only main agent can do this
if (((obj.agentInfo.capabilities & 0x40) == 0) && (typeof command.value.value == 'string') && (command.value.value.length == 64))
{
// Store links to diagnostic agent id
var daNodeKey = 'node/' + domain.id + '/' + db.escapeBase64(command.value.value);
db.Set({ _id: 'da' + daNodeKey, domain: domain.id, time: obj.connectTime, raid: obj.dbNodeKey }); // DiagnosticAgent --> Agent
db.Set({ _id: 'ra' + obj.dbNodeKey, domain: domain.id, time: obj.connectTime, daid: daNodeKey }); // Agent --> DiagnosticAgent
}
break;
}
case 'query': {
// Only the diagnostic agent can do
if ((obj.agentInfo.capabilities & 0x40) != 0) {
// Return nodeid of main agent + connection status
db.Get('da' + obj.dbNodeKey, function (err, nodes) {
if (nodes.length == 1) {
obj.realNodeKey = nodes[0].raid;
// Get agent connection state
var agentConnected = false;
var state = parent.parent.GetConnectivityState(obj.realNodeKey);
if (state) { agentConnected = ((state.connectivity & 1) != 0) }
obj.send(JSON.stringify({ action: 'diagnostic', value: { command: 'query', value: obj.realNodeKey, agent: agentConnected } }));
} else {
obj.send(JSON.stringify({ action: 'diagnostic', value: { command: 'query', value: null } }));
}
});
}
break;
}
case 'log': {
// Only the diagnostic agent can do
if (((obj.agentInfo.capabilities & 0x40) != 0) && (typeof command.value.value == 'string') && (command.value.value.length < 256))
{
// Log a value in the event log of the main again
var event = { etype: 'node', action: 'diagnostic', nodeid: obj.realNodeKey, domain: domain.id, msg: command.value.value };
parent.parent.DispatchEvent(['*', obj.dbMeshKey], obj, event);
}
break;
}
}
}
break;
}
default: {
console.log('Unknown agent action (' + obj.remoteaddrport + '): ' + command.action + '.');
break;
@ -1076,6 +1168,7 @@ module.exports.CreateMeshAgent = function (parent, db, ws, req, args, domain) {
// Change the current core information string and event it
function ChangeAgentCoreInfo(command) {
if (obj.agentInfo.capabilities & 0x40) return;
if ((command == null) || (command == null)) return; // Safety, should never happen.
// Check that the mesh exists
@ -1119,6 +1212,12 @@ module.exports.CreateMeshAgent = function (parent, db, ws, req, args, domain) {
// If there are changes, event the new device
if (change == 1) {
// Do some clean up if needed, these values should not be in the database.
if (device.conn != null) { delete device.conn; }
if (device.pwr != null) { delete device.pwr; }
if (device.agct != null) { delete device.agct; }
if (device.cict != null) { delete device.cict; }
// Save to the database
db.Set(device);
@ -1137,6 +1236,7 @@ module.exports.CreateMeshAgent = function (parent, db, ws, req, args, domain) {
// Change the current core information string and event it
function ChangeAgentLocationInfo(command) {
if (obj.agentInfo.capabilities & 0x40) return;
if ((command == null) || (command == null)) { return; } // Safety, should never happen.
// Check that the mesh exists
@ -1156,6 +1256,13 @@ module.exports.CreateMeshAgent = function (parent, db, ws, req, args, domain) {
// If there are changes, save and event
if (change == 1) {
// Do some clean up if needed, these values should not be in the database.
if (device.conn != null) { delete device.conn; }
if (device.pwr != null) { delete device.pwr; }
if (device.agct != null) { delete device.agct; }
if (device.cict != null) { delete device.cict; }
// Save the device
db.Set(device);
// Event the node change
@ -1172,6 +1279,7 @@ module.exports.CreateMeshAgent = function (parent, db, ws, req, args, domain) {
// Update the mesh agent tab in the database
function ChangeAgentTag(tag) {
if (obj.agentInfo.capabilities & 0x40) return;
if (tag.length == 0) { tag = null; }
// Get the node and change it if needed
db.Get(obj.dbNodeKey, function (err, nodes) {
@ -1179,6 +1287,13 @@ module.exports.CreateMeshAgent = function (parent, db, ws, req, args, domain) {
const device = nodes[0];
if (device.agent) {
if (device.agent.tag != tag) {
// Do some clean up if needed, these values should not be in the database.
if (device.conn != null) { delete device.conn; }
if (device.pwr != null) { delete device.pwr; }
if (device.agct != null) { delete device.agct; }
if (device.cict != null) { delete device.cict; }
// Set the new tag
device.agent.tag = tag;
db.Set(device);

View File

@ -479,7 +479,7 @@ function CreateMeshCentralServer(config, args) {
// Lower case all keys in the config file
try {
require('./common.js').objKeysToLower(config2);
require('./common.js').objKeysToLower(config2, ["ldapoptions"]);
} catch (ex) {
console.log('CRITICAL ERROR: Unable to access the file \"./common.js\".\r\nCheck folder & file permissions.');
process.exit();
@ -543,8 +543,9 @@ function CreateMeshCentralServer(config, args) {
if (obj.config.domains[''].dns != null) { console.log("ERROR: Default domain can't have a DNS name."); return; }
var xdomains = {}; for (i in obj.config.domains) { if (obj.config.domains[i].title == null) { obj.config.domains[i].title = 'MeshCentral'; } if (obj.config.domains[i].title2 == null) { obj.config.domains[i].title2 = '2.0 Beta 2'; } xdomains[i.toLowerCase()] = obj.config.domains[i]; } obj.config.domains = xdomains;
var bannedDomains = ['public', 'private', 'images', 'scripts', 'styles', 'views']; // List of banned domains
for (i in obj.config.domains) { for (var j in bannedDomains) { if (i == bannedDomains[j]) { console.log("ERROR: Domain '" + i + "' is not allowed domain name in ./data/config.json."); return; } } }
for (i in obj.config.domains) { for (var j in bannedDomains) { if (i == bannedDomains[j]) { console.log("ERROR: Domain '" + i + "' is not allowed domain name in config.json."); return; } } }
for (i in obj.config.domains) {
if (typeof config.domains[i].auth == 'string') { config.domains[i].auth = config.domains[i].auth.toLowerCase(); }
if (obj.config.domains[i].limits == null) { obj.config.domains[i].limits = {}; }
if (obj.config.domains[i].dns == null) { obj.config.domains[i].url = (i == '') ? '/' : ('/' + i + '/'); } else { obj.config.domains[i].url = '/'; }
obj.config.domains[i].id = i;
@ -552,6 +553,12 @@ function CreateMeshCentralServer(config, args) {
if (typeof obj.config.domains[i].userblockedip == 'string') { if (obj.config.domains[i].userblockedip == '') { obj.config.domains[i].userblockedip = null; } else { obj.config.domains[i].userblockedip = obj.config.domains[i].userallowedip.split(','); } }
if (typeof obj.config.domains[i].agentallowedip == 'string') { if (obj.config.domains[i].agentallowedip == '') { obj.config.domains[i].agentallowedip = null; } else { obj.config.domains[i].agentallowedip = obj.config.domains[i].agentallowedip.split(','); } }
if (typeof obj.config.domains[i].agentblockedip == 'string') { if (obj.config.domains[i].agentblockedip == '') { obj.config.domains[i].agentblockedip = null; } else { obj.config.domains[i].agentblockedip = obj.config.domains[i].agentblockedip.split(','); } }
if ((obj.config.domains[i].auth == 'ldap') && (typeof obj.config.domains[i].ldapoptions != 'object')) {
if (i == '') { console.log("ERROR: Default domain is LDAP, but is missing LDAPOptions."); } else { console.log("ERROR: Domain '" + i + "' is LDAP, but is missing LDAPOptions."); }
process.exit();
return;
}
if ((obj.config.domains[i].auth == 'ldap') || (obj.config.domains[i].auth == 'sspi')) { obj.config.domains[i].newaccounts = 0; } // No new accounts allowed in SSPI/LDAP authentication modes.
}
// Log passed arguments into Windows Service Log
@ -1197,8 +1204,8 @@ function CreateMeshCentralServer(config, args) {
// Read the agent recovery core if present
var meshAgentRecoveryCore = null;
if (obj.fs.existsSync(obj.path.join(__dirname, 'agents', 'agentrecoverycore.js')) == true) {
try { meshAgentRecoveryCore = obj.fs.readFileSync(obj.path.join(__dirname, 'agents', 'agentrecoverycore.js')).toString(); } catch (ex) { }
if (obj.fs.existsSync(obj.path.join(__dirname, 'agents', 'meshcore_diagnostic.js')) == true) {
try { meshAgentRecoveryCore = obj.fs.readFileSync(obj.path.join(__dirname, 'agents', 'meshcore_diagnostic.js')).toString(); } catch (ex) { }
if (meshAgentRecoveryCore != null) {
modulesAdd['windows-agentrecovery'] = ['var addedModules = [];\r\n'];
modulesAdd['linux-agentrecovery'] = ['var addedModules = [];\r\n'];
@ -1618,11 +1625,12 @@ function getConfig(createSampleConfig) {
// Lower case all keys in the config file
try {
require('./common.js').objKeysToLower(config);
require('./common.js').objKeysToLower(config, ["ldapoptions"]);
} catch (ex) {
console.log('CRITICAL ERROR: Unable to access the file \"./common.js\".\r\nCheck folder & file permissions.');
process.exit();
}
return config;
}
@ -1680,16 +1688,24 @@ function mainStart(args) {
var config = getConfig(false);
if (config == null) { process.exit(); }
// Lowercase the auth value is present
for (var i in config.domains) { if (typeof config.domains[i].auth == 'string') { config.domains[i].auth = config.domains[i].auth.toLowerCase(); } }
// Check is Windows SSPI and YubiKey OTP will be used
var sspi = false;
var ldap = false;
var allsspi = true;
var yubikey = false;
if (require('os').platform() == 'win32') { for (var i in config.domains) { if (config.domains[i].auth == 'sspi') { sspi = true; } else { allsspi = false; } } } else { allsspi = false; }
for (var i in config.domains) { if (config.domains[i].yubikey != null) { yubikey = true; } }
for (var i in config.domains) {
if (config.domains[i].yubikey != null) { yubikey = true; }
if (config.domains[i].auth == 'ldap') { ldap = true; }
}
// Build the list of required modules
var modules = ['ws', 'nedb', 'https', 'yauzl', 'xmldom', 'express', 'archiver', 'multiparty', 'node-forge', 'express-ws', 'compression', 'body-parser', 'connect-redis', 'express-handlebars'];
if (require('os').platform() == 'win32') { modules.push('node-windows'); if (sspi == true) { modules.push('node-sspi'); } } // Add Windows modules
if (ldap == true) { modules.push('ldapauth-fork'); }
if (config.letsencrypt != null) { modules.push('greenlock'); modules.push('le-store-certbot'); modules.push('le-challenge-fs'); modules.push('le-acme-core'); } // Add Greenlock Modules
if (config.settings.mongodb != null) { modules.push('mongojs'); } // Add MongoDB
if (config.smtp != null) { modules.push('nodemailer'); } // Add SMTP support

View File

@ -53,7 +53,7 @@ module.exports.CreateMeshRelay = function (parent, ws, req, domain, user, cookie
};
obj.sendAgentMessage = function (command, userid, domainid) {
var rights;
var rights, mesh;
if (command.nodeid == null) return false;
var user = obj.parent.users[userid];
if (user == null) return false;
@ -66,9 +66,13 @@ module.exports.CreateMeshRelay = function (parent, ws, req, domain, user, cookie
if (agent != null) {
// Check if we have permission to send a message to that node
rights = user.links[agent.dbMeshKey];
if (rights != null || ((rights & 16) != 0)) { // TODO: 16 is console permission, may need more gradular permission checking
mesh = parent.meshes[agent.dbMeshKey];
if ((rights != null) && (mesh != null) || ((rights & 16) != 0)) { // TODO: 16 is console permission, may need more gradular permission checking
command.sessionid = ws.sessionId; // Set the session id, required for responses.
command.rights = rights.rights; // Add user rights flags to the message
command.consent = mesh.consent; // Add user consent
if (typeof domain.userconsentflags == 'number') { command.consent |= domain.userconsentflags; } // Add server required consent flags
command.username = user.name; // Add user name
delete command.nodeid; // Remove the nodeid since it's implyed.
agent.send(JSON.stringify(command));
return true;
@ -79,9 +83,13 @@ module.exports.CreateMeshRelay = function (parent, ws, req, domain, user, cookie
if (routing != null) {
// Check if we have permission to send a message to that node
rights = user.links[routing.meshid];
mesh = parent.meshes[routing.meshid];
if (rights != null || ((rights & 16) != 0)) { // TODO: 16 is console permission, may need more gradular permission checking
command.fromSessionid = ws.sessionId; // Set the session id, required for responses.
command.rights = rights.rights; // Add user rights flags to the message
command.consent = mesh.consent; // Add user consent
if (typeof domain.userconsentflags == 'number') { command.consent |= domain.userconsentflags; } // Add server required consent flags
command.username = user.name; // Add user name
obj.parent.parent.multiServer.DispatchMessageSingleServer(command, routing.serverid);
return true;
}

View File

@ -19,6 +19,24 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use
const path = require('path');
const common = parent.common;
// Mesh Rights
const MESHRIGHT_EDITMESH = 1;
const MESHRIGHT_MANAGEUSERS = 2;
const MESHRIGHT_MANAGECOMPUTERS = 4;
const MESHRIGHT_REMOTECONTROL = 8;
const MESHRIGHT_AGENTCONSOLE = 16;
const MESHRIGHT_SERVERFILES = 32;
const MESHRIGHT_WAKEDEVICE = 64;
const MESHRIGHT_SETNOTES = 128;
// Site rights
const SITERIGHT_SERVERBACKUP = 1;
const SITERIGHT_MANAGEUSERS = 2;
const SITERIGHT_SERVERRESTORE = 4;
const SITERIGHT_FILEACCESS = 8;
const SITERIGHT_SERVERUPDATE = 16;
const SITERIGHT_LOCKED = 32;
var obj = {};
obj.user = user;
obj.domain = domain;
@ -107,10 +125,14 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use
if (agent != null) {
// Check if we have permission to send a message to that node
var rights = user.links[agent.dbMeshKey];
if ((rights != null) && ((rights.rights & 8) || (rights.rights & 256))) { // 8 is remote control permission, 256 is desktop read only
command.sessionid = ws.sessionId; // Set the session id, required for responses.
var mesh = parent.meshes[agent.dbMeshKey];
if ((rights != null) && (mesh != null) && ((rights.rights & 8) || (rights.rights & 256))) { // 8 is remote control permission, 256 is desktop read only
command.sessionid = ws.sessionId; // Set the session id, required for responses
command.rights = rights.rights; // Add user rights flags to the message
delete command.nodeid; // Remove the nodeid since it's implyed.
command.consent = mesh.consent; // Add user consent
if (typeof domain.userconsentflags == 'number') { command.consent |= domain.userconsentflags; } // Add server required consent flags
command.username = user.name; // Add user name
delete command.nodeid; // Remove the nodeid since it's implied
try { agent.send(JSON.stringify(command)); } catch (ex) { }
}
} else {
@ -119,9 +141,13 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use
if (routing != null) {
// Check if we have permission to send a message to that node
var rights = user.links[routing.meshid];
if ((rights != null) && ((rights.rights & 8) || (rights.rights & 256))) { // 8 is remote control permission
command.fromSessionid = ws.sessionId; // Set the session id, required for responses.
var mesh = parent.meshes[agent.dbMeshKey];
if ((rights != null) && (mesh != null) && ((rights.rights & 8) || (rights.rights & 256))) { // 8 is remote control permission
command.fromSessionid = ws.sessionId; // Set the session id, required for responses
command.rights = rights.rights; // Add user rights flags to the message
command.consent = mesh.consent; // Add user consent
if (typeof domain.userconsentflags == 'number') { command.consent |= domain.userconsentflags; } // Add server required consent flags
command.username = user.name; // Add user name
parent.parent.multiServer.DispatchMessageSingleServer(command, routing.serverid);
}
}
@ -233,8 +259,9 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use
var httpport = ((args.aliasport != null) ? args.aliasport : args.port);
// Build server information object
var serverinfo = { name: parent.certificates.CommonName, mpsname: parent.certificates.AmtMpsName, mpsport: mpsport, mpspass: args.mpspass, port: httpport, emailcheck: ((parent.parent.mailserver != null) && (domain.auth != 'sspi')), domainauth: (domain.auth == 'sspi') };
var serverinfo = { name: parent.certificates.CommonName, mpsname: parent.certificates.AmtMpsName, mpsport: mpsport, mpspass: args.mpspass, port: httpport, emailcheck: ((parent.parent.mailserver != null) && (domain.auth != 'sspi') && (domain.auth != 'ldap')), domainauth: ((domain.auth == 'sspi') || (domain.auth == 'ldap')) };
if (args.notls == true) { serverinfo.https = false; } else { serverinfo.https = true; serverinfo.redirport = args.redirport; }
if (typeof domain.userconsentflags == 'number') { serverinfo.consent = domain.userconsentflags; }
// Send server information
try { ws.send(JSON.stringify({ action: 'serverinfo', serverinfo: serverinfo })); } catch (ex) { }
@ -309,6 +336,13 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use
db.GetAllTypeNoTypeFieldMeshFiltered(links, domain.id, 'node', command.id, function (err, docs) {
var r = {};
for (i in docs) {
// Remove any connectivity and power state information, that should not be in the database anyway.
// TODO: Find why these are sometimes saves in the db.
if (docs[i].conn != null) { delete docs[i].conn; }
if (docs[i].pwr != null) { delete docs[i].pwr; }
if (docs[i].agct != null) { delete docs[i].agct; }
if (docs[i].cict != null) { delete docs[i].cict; }
// Add the connection state
var state = parent.parent.GetConnectivityState(docs[i]._id);
if (state) {
@ -690,7 +724,7 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use
case 'changeemail':
{
// Change the email address
if (domain.auth == 'sspi') return;
if ((domain.auth == 'sspi') || (domain.auth == 'ldap')) return;
if (common.validateEmail(command.email, 1, 256) == false) return;
if (parent.users[req.session.userid].email != command.email) {
// Check if this email is already validated on a different account
@ -715,7 +749,7 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use
parent.parent.DispatchEvent(['*', 'server-users', user._id], obj, message);
// Send the verification email
if ((parent.parent.mailserver != null) && (domain.auth != 'sspi')) { parent.parent.mailserver.sendAccountCheckMail(domain, user.name, user.email); }
if (parent.parent.mailserver != null) { parent.parent.mailserver.sendAccountCheckMail(domain, user.name, user.email); }
}
});
}
@ -724,7 +758,7 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use
case 'verifyemail':
{
// Send a account email verification email
if (domain.auth == 'sspi') return;
if ((domain.auth == 'sspi') || (domain.auth == 'ldap')) return;
if (common.validateString(command.email, 3, 1024) == false) return;
if ((parent.parent.mailserver != null) && (parent.users[req.session.userid].email == command.email)) {
// Send the verification email
@ -853,20 +887,43 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use
case 'edituser':
{
// Edit a user account, may involve changing email or administrator permissions
if (((user.siteadmin & 2) != 0) || (user.name == command.name)) {
var chguserid = 'user/' + domain.id + '/' + command.name.toLowerCase(), chguser = parent.users[chguserid];
if (((user.siteadmin & 2) != 0) || (user._id == command.id)) {
var chguser = parent.users[command.id];
change = 0;
if (chguser) {
if (common.validateString(command.email, 1, 256) && (chguser.email != command.email)) { chguser.email = command.email; change = 1; }
if ((command.emailVerified === true || command.emailVerified === false) && (chguser.emailVerified != command.emailVerified)) { chguser.emailVerified = command.emailVerified; change = 1; }
if ((common.validateInt(command.quota, 0) || command.quota == null) && (command.quota != chguser.quota)) { chguser.quota = command.quota; if (chguser.quota == null) { delete chguser.quota; } change = 1; }
if ((user.siteadmin == 0xFFFFFFFF) && common.validateInt(command.siteadmin) && (chguser.siteadmin != command.siteadmin)) { chguser.siteadmin = command.siteadmin; change = 1; }
if ((Array.isArray(command.groups)) && (user._id != command.id)) {
if (command.groups.length == 0) {
// Remove the user groups
if (chguser.groups != null) { delete chguser.groups; change = 1; }
} else {
// Arrange the user groups
var groups2 = [];
for (var i in command.groups) {
if (typeof command.groups[i] == 'string') {
var gname = command.groups[i].trim().toLowerCase();
if ((gname.length > 0) && (gname.length <= 64) && (groups2.indexOf(gname) == -1)) { groups2.push(gname); }
}
}
groups2.sort();
// Set the user groups
if (chguser.groups != groups2) { chguser.groups = groups2; change = 1; }
}
}
if (change == 1) {
// Update the user
db.SetUser(chguser);
parent.parent.DispatchEvent([chguser._id], obj, 'resubscribe');
parent.parent.DispatchEvent(['*', 'server-users', user._id, chguser._id], obj, { etype: 'user', username: user.name, account: parent.CloneSafeUser(chguser), action: 'accountchange', msg: 'Account changed: ' + command.name, domain: domain.id });
parent.parent.DispatchEvent(['*', 'server-users', user._id, chguser._id], obj, { etype: 'user', username: user.name, account: parent.CloneSafeUser(chguser), action: 'accountchange', msg: 'Account changed: ' + chguser.name, domain: domain.id });
}
if ((chguser.siteadmin) && (chguser.siteadmin != 0xFFFFFFFF) && (chguser.siteadmin & 32)) {
// If the user is locked out of this account, disconnect now
parent.parent.DispatchEvent([chguser._id], obj, 'close'); // Disconnect all this user's sessions
}
}
@ -1044,7 +1101,7 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use
if ((user.siteadmin != 0xFFFFFFFF) && ((user.siteadmin & 64) != 0)) break;
// In some situations, we need a verified email address to create a device group.
if ((parent.parent.mailserver != null) && (domain.auth != 'sspi') && (user.emailVerified !== true) && (user.siteadmin != 0xFFFFFFFF)) return; // User must verify it's email first.
if ((parent.parent.mailserver != null) && (domain.auth != 'sspi') && (domain.auth != 'ldap') && (user.emailVerified !== true) && (user.siteadmin != 0xFFFFFFFF)) return; // User must verify it's email first.
// Create mesh
if (common.validateString(command.meshname, 1, 64) == false) break; // Meshname is between 1 and 64 characters
@ -1122,6 +1179,7 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use
if (common.validateString(command.meshid, 1, 1024) == false) break; // Check the meshid
mesh = parent.meshes[command.meshid];
change = '';
if (mesh) {
// Check if this user has rights to do this
if (mesh.links[user._id] == null || ((mesh.links[user._id].rights & 1) == 0)) return;
@ -1130,7 +1188,8 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use
if ((common.validateString(command.meshname, 1, 64) == true) && (command.meshname != mesh.name)) { change = 'Group name changed from "' + mesh.name + '" to "' + command.meshname + '"'; mesh.name = command.meshname; }
if ((common.validateString(command.desc, 0, 1024) == true) && (command.desc != mesh.desc)) { if (change != '') change += ' and description changed'; else change += 'Group "' + mesh.name + '" description changed'; mesh.desc = command.desc; }
if ((common.validateInt(command.flags) == true) && (command.flags != mesh.flags)) { if (change != '') change += ' and flags changed'; else change += 'Group "' + mesh.name + '" flags changed'; mesh.flags = command.flags; }
if (change != '') { db.Set(common.escapeLinksFieldName(mesh)); parent.parent.DispatchEvent(['*', mesh._id, user._id], obj, { etype: 'mesh', username: user.name, meshid: mesh._id, name: mesh.name, mtype: mesh.mtype, desc: mesh.desc, flags: mesh.flags, action: 'meshchange', links: mesh.links, msg: change, domain: domain.id }); }
if ((common.validateInt(command.consent) == true) && (command.consent != mesh.consent)) { if (change != '') change += ' and consent changed'; else change += 'Group "' + mesh.name + '" consent changed'; mesh.consent = command.consent; }
if (change != '') { db.Set(common.escapeLinksFieldName(mesh)); parent.parent.DispatchEvent(['*', mesh._id, user._id], obj, { etype: 'mesh', username: user.name, meshid: mesh._id, name: mesh.name, mtype: mesh.mtype, desc: mesh.desc, flags: mesh.flags, consent: mesh.consent, action: 'meshchange', links: mesh.links, msg: change, domain: domain.id }); }
}
break;
}
@ -1161,7 +1220,7 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use
parent.parent.DispatchEvent([newuser._id], obj, 'resubscribe');
// Add a user to the mesh
mesh.links[newuserid] = { name: newuser.name, rights: command.meshadmin };
mesh.links[newuserid] = { userid: newuser.id, name: newuser.name, rights: command.meshadmin };
db.Set(common.escapeLinksFieldName(mesh));
// Notify mesh change
@ -1224,7 +1283,7 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use
change = '';
if (mesh) {
// Check if this user has rights to do this
if ((mesh.links[user._id] == null) || (mesh.links[user._id].rights != 0xFFFFFFFF)) return;
if ((mesh.links[user._id] == null) || ((mesh.links[user._id].rights & 1) == 0)) return;
if ((command.meshid.split('/').length != 3) || (command.meshid.split('/')[1] != domain.id)) return; // Invalid domain, operation only valid for current domain
// TODO: Check if this is a change from the existing policy
@ -1378,6 +1437,10 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use
db.RemoveSMBIOS(node._id); // Remove SMBios data
db.RemoveAllNodeEvents(node._id); // Remove all events for this node
db.removeAllPowerEventsForNode(node._id); // Remove all power events for this node
db.Get('ra' + obj.dbNodeKey, function (err, nodes) {
if (nodes.length == 1) { db.Remove('da' + nodes[0].daid); } // Remove diagnostic agent to real agent link
db.Remove('ra' + node._id); // Remove real agent to diagnostic agent link
});
// Event node deletion
parent.parent.DispatchEvent(['*', node.meshid], obj, { etype: 'node', username: user.name, action: 'removenode', nodeid: node._id, msg: 'Removed device ' + node.name + ' from group ' + mesh.name, domain: domain.id });
@ -1598,10 +1661,11 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use
if (command.intelamt.tls && (command.intelamt.tls != node.intelamt.tls)) { change = 1; node.intelamt.tls = command.intelamt.tls; changes.push('Intel AMT TLS'); }
}
if (command.tags) { // Node grouping tag, this is a array of strings that can't be empty and can't contain a comma
var ok = true;
var ok = true, group2 = [];
if (common.validateString(command.tags, 0, 4096) == true) { command.tags = command.tags.split(','); }
if (common.validateStrArray(command.tags, 1, 256) == true) { var groupTags = command.tags; for (var i in groupTags) { groupTags[i] = groupTags[i].trim(); if ((groupTags[i] == '') || (groupTags[i].indexOf(',') >= 0)) { ok = false; } } }
if (ok == true) { groupTags.sort(function (a, b) { return a.toLowerCase().localeCompare(b.toLowerCase()); }); node.tags = groupTags; change = 1; }
for (var i in command.tags) { var tname = command.tags[i].trim(); if ((tname.length > 0) && (tname.length < 64) && (group2.indexOf(tname) == -1)) { group2.push(tname); } }
group2.sort();
if (node.tags != group2) { node.tags = group2; change = 1; }
} else if ((command.tags === '') && node.tags) { delete node.tags; change = 1; }
if (change == 1) {
@ -1696,7 +1760,7 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use
db.Get(command.nodeid, function (err, nodes) { // TODO: Make a NodeRights(user) method that also does not do a db call if agent is connected (???)
if (nodes.length == 1) {
meshlinks = user.links[nodes[0].meshid];
if ((meshlinks) && (meshlinks.rights) && (meshlinks.rights & parent.MESHRIGHT_REMOTECONTROL != 0)) {
if ((meshlinks) && (meshlinks.rights) && ((meshlinks.rights & MESHRIGHT_REMOTECONTROL) != 0)) {
// Add a user authentication cookie to a url
var cookieContent = { userid: user._id, domainid: user.domain };
if (command.nodeid) { cookieContent.nodeid = command.nodeid; }

View File

@ -1,6 +1,6 @@
{
"name": "meshcentral",
"version": "0.3.1-x",
"version": "0.3.2-m",
"keywords": [
"Remote Management",
"Intel AMT",
@ -35,6 +35,7 @@
"express-handlebars": "^3.0.0",
"express-ws": "^4.0.0",
"ipcheck": "^0.1.0",
"ldapauth-fork": "^4.2.0",
"meshcentral": "*",
"minimist": "^1.2.0",
"multiparty": "^4.2.1",

View File

@ -43,7 +43,7 @@ var CreateAgentRedirect = function (meshserver, module, serverPublicNamePort, au
obj.socket.onclose = obj.xxOnSocketClosed;
obj.xxStateChange(1);
//obj.meshserver.send({ action: 'msg', type: 'tunnel', nodeid: obj.nodeid, value: url2 });
obj.meshserver.send({ action: 'msg', type: 'tunnel', nodeid: obj.nodeid, value: "*/meshrelay.ashx?id=" + obj.tunnelid });
obj.meshserver.send({ action: 'msg', type: 'tunnel', nodeid: obj.nodeid, value: "*/meshrelay.ashx?id=" + obj.tunnelid, usage: obj.protocol });
//obj.debug("Agent Redir Start: " + url);
}

View File

@ -31,6 +31,8 @@ var CreateAmtRemoteTerminal = function (divid) {
var _TermLineWrap = true;
var _termx = 0;
var _termy = 0;
var _termsavex = 0;
var _termsavey = 0;
var _termstate = 0;
var _escNumber = [];
var _escNumberPtr = 0;
@ -110,7 +112,31 @@ var CreateAmtRemoteTerminal = function (divid) {
_altKeypadMode = false;
_termstate = 0;
break;
case '7':
// Save Cursor
_termsavex = _termx;
_termsavey = _termy;
_termstate = 0;
break;
case '8':
// Restore Cursor
_termx = _termsavex;
_termy = _termsavey;
_termstate = 0;
break;
case 'M':
// Scroll down one
var x = 1;
for (var y = _scrollRegion[1]; y >= _scrollRegion[0] + x; y--) {
for (var z = 0; z < obj.width; z++) { _tscreen[y][z] = _tscreen[y - x][z]; _scratt[y][z] = _scratt[y - x][z]; }
}
for (var y = _scrollRegion[0] + x - 1; y > _scrollRegion[0] - 1; y--) {
for (var z = 0; z < obj.width; z++) { _tscreen[y][z] = ' '; _scratt[y][z] = (7 << 6); }
}
_termstate = 0;
break;
default:
console.log('unknown terminal short code', b);
_termstate = 0;
break;
}
@ -203,7 +229,8 @@ var CreateAmtRemoteTerminal = function (divid) {
case 'P': // Delete X Character(s), default 1 char
var x = 1;
if (argslen == 1) { x = args[0]; }
for (i = _termx; i < (_termx + x) ; i++) { _tscreen[_termy][i] = ' '; _scratt[_termy][i] = (7 << 6); }
for (i = _termx; i < 80 - x; i++) { _tscreen[_termy][i] = _tscreen[_termy][i + x]; _scratt[_termy][i] = _scratt[_termy][i + x]; }
for (i = (80 - x); i < 80; i++) { _tscreen[_termy][i] = ' '; _scratt[_termy][i] = (7 << 6); }
break;
case 'L': // Insert X Line(s), default 1 char
var linecount = 1;
@ -244,8 +271,7 @@ var CreateAmtRemoteTerminal = function (divid) {
if (args[1] > obj.width) args[1] = obj.width;
_termy = args[0] - 1;
_termx = args[1] - 1;
}
else {
} else {
_termy = 0;
_termx = 0;
}
@ -330,6 +356,16 @@ var CreateAmtRemoteTerminal = function (divid) {
for (var z = 0; z < obj.width; z++) { _tscreen[y][z] = ' '; _scratt[y][z] = (7 << 6); }
}
break;
case 'M': // Delete X lines, default 1
var x = 1;
if (argslen == 1) { x = args[0] }
for (var y = _termy; y <= _scrollRegion[1] - x; y++) {
for (var z = 0; z < obj.width; z++) { _tscreen[y][z] = _tscreen[y + x][z]; _scratt[y][z] = _scratt[y + x][z]; }
}
for (var y = _scrollRegion[1] - x + 1; y < _scrollRegion[1]; y++) {
for (var z = 0; z < obj.width; z++) { _tscreen[y][z] = ' '; _scratt[y][z] = (7 << 6); }
}
break;
case 'T': // Scroll down the scroll region X lines, default 1
var x = 1;
if (argslen == 1) { x = args[0] }
@ -340,9 +376,14 @@ var CreateAmtRemoteTerminal = function (divid) {
for (var z = 0; z < obj.width; z++) { _tscreen[y][z] = ' '; _scratt[y][z] = (7 << 6); }
}
break;
case 'X': // Erase X characters, default 1 (untested)
var x = 1;
if (argslen == 1) { x = args[0] }
while ((x > 0) && (_termx > 0)) { _tscreen[_termy][_termx] = ' '; _termx--; x--; }
break;
default:
//if (code != '@') alert(code);
//console.log('unknown terminal code', code, args, mode);
console.log('unknown terminal code', code, args, mode);
break;
}
}
@ -614,6 +655,13 @@ var CreateAmtRemoteTerminal = function (divid) {
if (e.which == 40) { obj.TermSendKeys(String.fromCharCode(27, 91, 66)); return true; }; // Down
}
if (e.which == 33) { obj.TermSendKeys(String.fromCharCode(27, 91, 53, 126)); return true; }; // PageUp
if (e.which == 34) { obj.TermSendKeys(String.fromCharCode(27, 91, 54, 126)); return true; }; // PageDown
if (e.which == 35) { obj.TermSendKeys(String.fromCharCode(27, 91, 70)); return true; }; // End
if (e.which == 36) { obj.TermSendKeys(String.fromCharCode(27, 91, 72)); return true; }; // Home
if (e.which == 45) { obj.TermSendKeys(String.fromCharCode(27, 91, 50, 126)); return true; }; // Insert
if (e.which == 46) { obj.TermSendKeys(String.fromCharCode(27, 91, 51, 126)); return true; }; // Delete
if (e.which == 9) { obj.TermSendKeys("\t"); if (e.preventDefault) e.preventDefault(); if (e.stopPropagation) e.stopPropagation(); return true; }; // TAB
// F1 to F12 keys

1600
public/styles/style-old.css Normal file

File diff suppressed because it is too large Load Diff

View File

@ -124,6 +124,27 @@ body {
font-weight: bold;
}
#page_content {
/*max-height: calc(100vh - 108px);*/
}
.fullscreen #page_content {
position: absolute;
top: 66px;
left: 90px;
right: 0px;
bottom: 0px;
}
.arg_hide #page_content {
left: 0px;
}
.fulldesk #page_content {
position: static;
top: 0;
left: 0;
right: 0;
bottom: 0;
}
#page_leftbar {
height: calc(100vh - 66px);
width: 90px;

View File

@ -49,13 +49,15 @@
"_NewAccountEmailDomains": [ "sample.com" ],
"Footer": "<a href='https://twitter.com/mytwitter'>Twitter</a>",
"_CertUrl": "https://192.168.2.106:443/",
"_PasswordRequirements": { "min": 8, "max": 128, "upper": 1, "lower": 1, "numeric": 1, "nonalpha": 1, "reset": 90 },
"_PasswordRequirements": { "min": 8, "max": 128, "upper": 1, "lower": 1, "numeric": 1, "nonalpha": 1, "reset": 90, "force2factor": true },
"_AgentNoProxy": true,
"_GeoLocation": true,
"_UserAllowedIP": "127.0.0.1,192.168.1.0/24",
"_UserBlockedIP": "127.0.0.1,::1,192.168.0.100",
"_AgentAllowedIP": "192.168.0.100/24",
"_AgentBlockedIP": "127.0.0.1,::1",
"__UserConsentFlags__" : "Set to: 1 for desktop, 2 for terminal, 3 for files, 7 for all",
"_UserConsentFlags" : 7,
"_Limits": {
"_MaxUserAccounts": 100,
"_MaxUserSessions": 100,

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -18,7 +18,7 @@
<script type="text/javascript" src="scripts/zlib-adler32.js"></script>
<script type="text/javascript" src="scripts/zlib-crc32.js"></script>
<script keeplink=1 type="text/javascript" src="scripts/filesaver.1.1.20151003.js"></script>
<title>MeshCentral</title>
<title>{{{title}}}</title>
<style>
a {
color: #036;
@ -249,10 +249,10 @@
<br style=clear:both />
</div>
<strong>Device Groups</strong>
( <a onclick=account_createMesh() style=cursor:pointer><img src="images/icon-addnew.png" width=12 height=12 border=0 /> New</a> )
<span id="p3createMeshLink1">( <a onclick=account_createMesh() style=cursor:pointer><img src="images/icon-addnew.png" width=12 height=12 border=0 /> New</a> )</span>
<br /><br />
<div id=p3meshes></div>
<div id=p3noMeshFound style=margin-left:9px;display:none>No device groups. <a onclick=account_createMesh() style=cursor:pointer><strong>Get started here!</strong></a></div>
<div id=p3noMeshFound style=margin-left:9px;display:none>No device groups.<span id="p3createMeshLink2"> <a onclick=account_createMesh() style=cursor:pointer><strong>Get started here!</strong></a></span></div>
<br style=clear:both />
</div>
</div>
@ -655,6 +655,10 @@
QV('manageAuthApp', features & 4096);
QV('manageOtp', ((features & 4096) != 0) && ((userinfo.otpsecret == 1) || (userinfo.otphkeys > 0)));
// On the mobile app, don't allow group creation (for now).
QV('p3createMeshLink1', false);
QV('p3createMeshLink2', false);
if (typeof userinfo.passchange == 'number') {
if (userinfo.passchange == -1) { QH('p2nextPasswordUpdateTime', ' - Reset on next login.'); }
else if ((passRequirements != null) && (typeof passRequirements.reset == 'number')) {
@ -809,7 +813,7 @@
}
case 'createmesh': {
// A new mesh was created
if (message.event.links['user/' + domain + '/' + userinfo.name.toLowerCase()] != null) { // Check if this is a mesh create for a mesh we own. If site administrator, we get all messages so need to ignore some.
if (message.event.links[userinfo._id] != null) { // Check if this is a mesh create for a mesh we own. If site administrator, we get all messages so need to ignore some.
meshes[message.event.meshid] = { _id: message.event.meshid, name: message.event.name, mtype: message.event.mtype, desc: message.event.desc, links: message.event.links };
updateMeshes();
updateDevices();
@ -830,7 +834,7 @@
meshes[message.event.meshid].links = message.event.links;
// Check if we lost rights to this mesh in this change.
if (meshes[message.event.meshid].links['user/' + domain + '/' + userinfo.name.toLowerCase()] == null) {
if (meshes[message.event.meshid].links[userinfo._id] == null) {
if ((xxcurrentView == 20) && (currentMesh == meshes[message.event.meshid])) go(2);
delete meshes[message.event.meshid];
@ -1165,8 +1169,14 @@
function account_createMesh() {
if (xxdialogMode) return;
// Check if we are allowed to create a new device group
if ((userinfo.emailVerified !== true) && (userinfo.email != null) && (serverinfo.emailcheck == true) && (userinfo.siteadmin != 0xFFFFFFFF)) { setDialogMode(2, "New Device Group", 1, null, "Unable to create a new device group until the email address is verified. Go to the \"My Account\" menu option to change and verify an email address."); return; }
// Check if we are disallowed from creating a device group
if ((userinfo.siteadmin != 0xFFFFFFFF) && ((userinfo.siteadmin & 64) != 0)) { setDialogMode(2, "New Device Group", 1, null, "This account does not have the rights to create a new device group."); return; }
// Remind the user to verify the email address
if ((userinfo.emailVerified !== true) && (serverinfo.emailcheck == true) && (userinfo.siteadmin != 0xFFFFFFFF)) { setDialogMode(2, "Account Security", 1, null, "Unable to access a device until a email address is verified. This is required for password recovery. Go to the \"My Account\" to change and verify an email address."); return; }
// Remind the user to add two factor authentication
if ((features & 0x00040000) && !((userinfo.otpsecret == 1) || (userinfo.otphkeys > 0) || (userinfo.otpkeys > 0))) { setDialogMode(2, "Account Security", 1, null, "Unable to access a device until two-factor authentication is enabled. This is required for extra security. Go to the \"My Account\" and look at the \"Account Security\" section."); return; }
// We are allowed, let's prompt to information
var x = addHtmlValue('Name', '<input id=dp3meshname style=width:170px maxlength=64 onchange=account_validateMeshCreate() onkeyup=account_validateMeshCreate() />');
@ -1240,7 +1250,7 @@
count++;
// Mesh rights
var meshrights = meshes[i].links['user/' + domain + '/' + userinfo.name.toLowerCase()].rights;
var meshrights = meshes[i].links[userinfo._id].rights;
var rights = 'Partial Rights';
if (meshrights == 0xFFFFFFFF) rights = 'Full Administrator'; else if (meshrights == 0) rights = 'No Rights';
@ -1531,7 +1541,7 @@
// Go thru the list of nodes and display them
for (var i in nodes) {
if (nodes[i].v == false) continue;
var mesh2 = meshes[nodes[i].meshid], meshlinks = mesh2.links['user/' + domain + '/' + userinfo.name.toLowerCase()];
var mesh2 = meshes[nodes[i].meshid], meshlinks = mesh2.links[userinfo._id];
if (meshlinks == null) continue;
var meshrights = meshlinks.rights;
@ -1602,7 +1612,7 @@
// Display all empty meshes, we need to do this because users can add devices to these at any time.
if (sort == 0) {
for (var i in meshes) {
var mesh = meshes[i], meshlink = mesh.links['user/' + domain + '/' + userinfo.name.toLowerCase()];
var mesh = meshes[i], meshlink = mesh.links[userinfo._id];
if (meshlink != null) {
var meshrights = meshlink.rights;
if (displayedMeshes[mesh._id] == null) {
@ -1690,7 +1700,7 @@
function getNodeRights(nodeid) {
var node = getNodeFromId(nodeid), mesh = meshes[node.meshid];
return mesh.links['user/' + domain + '/' + userinfo.name.toLowerCase()].rights;
return mesh.links[userinfo._id].rights;
}
var currentDevicePanel = 0;
@ -1701,11 +1711,18 @@
var powerTimeline = null;
function getCurrentNode() { return currentNode; };
function gotoDevice(nodeid, panel, refresh) {
// Remind the user to verify the email address
if ((userinfo.emailVerified !== true) && (serverinfo.emailcheck == true) && (userinfo.siteadmin != 0xFFFFFFFF)) { setDialogMode(2, "Account Security", 1, null, "Unable to access a device until a email address is verified. This is required for password recovery. Go to the \"My Account\" to change and verify an email address."); return; }
// Remind the user to add two factor authentication
if ((features & 0x00040000) && !((userinfo.otpsecret == 1) || (userinfo.otphkeys > 0) || (userinfo.otpkeys > 0))) { setDialogMode(2, "Account Security", 1, null, "Unable to access a device until two-factor authentication is enabled. This is required for extra security. Go to the \"My Account\" and look at the \"Account Security\" section."); return; }
var node = getNodeFromId(nodeid);
if (node == null) { goBack(); return; }
var mesh = meshes[node.meshid];
if (mesh == null) { goBack(); return; }
var meshrights = mesh.links['user/' + domain + '/' + userinfo.name.toLowerCase()].rights;
var meshrights = mesh.links[userinfo._id].rights;
if (!currentNode || currentNode._id != node._id || refresh == true) {
currentNode = node;
@ -1865,7 +1882,7 @@
function setupDeviceMenu(op, obj) {
var meshrights = 0;
if (currentNode) { meshrights = meshes[currentNode.meshid].links['user/' + domain + '/' + userinfo.name.toLowerCase()].rights; }
if (currentNode) { meshrights = meshes[currentNode.meshid].links[userinfo._id].rights; }
if (op != null) { currentDevicePanel = op; }
QV('p10general', currentDevicePanel == 0);
QV('p10desktop', currentDevicePanel == 1); // Show if we have remote control rights or desktop view only rights
@ -1879,7 +1896,7 @@
function deviceActionFunction() {
if (xxdialogMode) return;
var meshrights = meshes[currentNode.meshid].links['user/' + domain + '/' + userinfo.name.toLowerCase()].rights;
var meshrights = meshes[currentNode.meshid].links[userinfo._id].rights;
var x = "Select an operation to perform on this device.<br /><br />";
var y = '<select id=d2deviceop style=float:right;width:170px>';
if ((meshrights & 64) != 0) { y += '<option value=100>Wake-up</option>'; } // Wake-up permission
@ -2022,7 +2039,7 @@
function p10showiconselector() {
if (xxdialogMode) return;
var mesh = meshes[currentNode.meshid];
var meshrights = mesh.links['user/' + domain + '/' + userinfo.name.toLowerCase()].rights;
var meshrights = mesh.links[userinfo._id].rights;
if ((meshrights & 4) == 0) return;
var x = '<table align=center><td>';
@ -2100,7 +2117,7 @@
var mesh = meshes[currentNode.meshid];
var deskState = 0;
if (desktop != null) { deskState = desktop.State; }
var meshrights = mesh.links['user/' + domain + '/' + userinfo.name.toLowerCase()].rights;
var meshrights = mesh.links[userinfo._id].rights;
// Show the right buttons
QV('disconnectbutton1', (deskState != 0));
@ -2842,7 +2859,7 @@
if (currentMesh == null) return;
QH('p20meshName', EscapeHtml(currentMesh.name));
var meshtype = 'Unknown #' + currentMesh.mtype;
var meshrights = currentMesh.links['user/' + domain + '/' + userinfo.name.toLowerCase()].rights;
var meshrights = currentMesh.links[userinfo._id].rights;
if (currentMesh.mtype == 1) meshtype = 'Intel&reg; AMT group';
if (currentMesh.mtype == 2) meshtype = 'Software agent group';
@ -2855,7 +2872,7 @@
//x += '<br><input type=button value=Notes title="View notes about this mesh" onclick=showNotes(false,"' + encodeURIComponent(currentMesh._id) + '") />';
x += '<br style=clear:both><br>';
var currentMeshLinks = currentMesh.links['user/' + domain + '/' + userinfo.name.toLowerCase()];
var currentMeshLinks = currentMesh.links[userinfo._id];
if (currentMeshLinks && ((currentMeshLinks.rights & 2) != 0)) { x += '<div style=margin-bottom:6px><a onclick=p20showAddMeshUserDialog() style=cursor:pointer><img src=images/icon-addnew.png border=0 height=12 width=12> Add User</a></div>'; }
/*
@ -2971,7 +2988,7 @@
}
function p20validateAddMeshUserDialog() {
var meshrights = currentMesh.links['user/' + domain + '/' + userinfo.name.toLowerCase()].rights;
var meshrights = currentMesh.links[userinfo._id].rights;
QE('idx_dlgOkButton', (Q('dp20username').value.length > 0));
QE('p20fulladmin', meshrights == 0xFFFFFFFF);
QE('p20editmesh', (!Q('p20fulladmin').checked) && (meshrights == 0xFFFFFFFF));
@ -3010,7 +3027,7 @@
function p20viewuser(userid) {
if (xxdialogMode) return;
userid = decodeURIComponent(userid);
var r = '', cmeshrights = currentMesh.links['user/' + domain + '/' + userinfo.name.toLowerCase()].rights, meshrights = currentMesh.links[userid].rights;
var r = '', cmeshrights = currentMesh.links[userinfo._id].rights, meshrights = currentMesh.links[userid].rights;
if (meshrights == 0xFFFFFFFF) r = ', Full Administrator'; else {
if ((meshrights & 1) != 0) r += ', Edit Device Group';
if ((meshrights & 2) != 0) r += ', Manage Device Group Users';
@ -3029,7 +3046,7 @@
if (r == '') { r = 'No Rights'; }
var buttons = 1, x = addHtmlValue('User', EscapeHtml(decodeURIComponent(userid.split('/')[2])));
x += addHtmlValue('Permissions', r);
if ((('user/' + domain + '/' + userinfo.name.toLowerCase()) != userid) && (cmeshrights == 0xFFFFFFFF || (((cmeshrights & 2) != 0) && (meshrights != 0xFFFFFFFF)))) buttons += 4;
if (((userinfo._id) != userid) && (cmeshrights == 0xFFFFFFFF || (((cmeshrights & 2) != 0) && (meshrights != 0xFFFFFFFF)))) buttons += 4;
setDialogMode(2, "Mesh User", buttons, p20viewuserEx, x, userid);
}

View File

@ -3554,6 +3554,7 @@
// Remind the user to verify the email address
if ((userinfo.emailVerified !== true) && (serverinfo.emailcheck == true) && (userinfo.siteadmin != 0xFFFFFFFF)) { setDialogMode(2, "Account Security", 1, null, "Unable to access a device until a email address is verified. This is required for password recovery. Go to the \"My Account\" tab to change and verify an email address."); return; }
// Remind the user to add two factor authentication
if ((features & 0x00040000) && !((userinfo.otpsecret == 1) || (userinfo.otphkeys > 0) || (userinfo.otpkeys > 0))) { setDialogMode(2, "Account Security", 1, null, "Unable to access a device until two-factor authentication is enabled. This is required for extra security. Go to the \"My Account\" tab and look at the \"Account Security\" section."); return; }
@ -5754,6 +5755,7 @@
// Remind the user to verify the email address
if ((userinfo.emailVerified !== true) && (serverinfo.emailcheck == true) && (userinfo.siteadmin != 0xFFFFFFFF)) { setDialogMode(2, "Account Security", 1, null, "Unable to access a device until a email address is verified. This is required for password recovery. Go to the \"My Account\" tab to change and verify an email address."); return; }
// Remind the user to add two factor authentication
if ((features & 0x00040000) && !((userinfo.otpsecret == 1) || (userinfo.otphkeys > 0) || (userinfo.otpkeys > 0))) { setDialogMode(2, "Account Security", 1, null, "Unable to access a device until two-factor authentication is enabled. This is required for extra security. Go to the \"My Account\" tab and look at the \"Account Security\" section."); return; }
@ -5919,6 +5921,7 @@
meshFeatures = meshFeatures.join(', ');
if (meshFeatures == '') { meshFeatures = '<i>None</i>'; }
x += addHtmlValue('Features', addLinkConditional(meshFeatures, 'p20editmeshfeatures()', meshrights & 1));
// Display user consent
meshFeatures = [];
var consent = 0;
@ -6893,7 +6896,6 @@
if (user.groups) { userGroups = ''; for (var i in user.groups) { userGroups += '<span class="tagSpan">' + user.groups[i] + '</span>'; } }
x += addDeviceAttribute('User Groups', addLinkConditional(userGroups, 'showUserGroupDialog(event,\"' + userid + '\")', ((userinfo.groups == null) && (userinfo.siteadmin & 2) && (userinfo._id != user._id))));
var multiFactor = 0;
if ((user.otpsecret > 0) || (user.otphkeys > 0)) {
multiFactor = 1;
@ -7586,8 +7588,8 @@
QV('MeshSubMenuSpan', x >= 20 && x < 30);
QV('UserSubMenuSpan', x >= 30 && x < 40);
QV('ServerSubMenuSpan', x == 6 || x == 115 || x == 40);
var panels = { 10: 'MainDev', 11: 'MainDevDesktop', 12: 'MainDevTerminal', 13: 'MainDevFiles', 14: 'MainDevAmt', 15: 'MainDevConsole', 16: 'MainDevEvents', 20: 'MeshGeneral', 30: 'UserGeneral', 31: 'UserEvents', 6: 'ServerGeneral', 40: 'ServerStats', 115: 'ServerConsole' };
var panels = { 10: 'MainDev', 11: 'MainDevDesktop', 12: 'MainDevTerminal', 13: 'MainDevFiles', 14: 'MainDevAmt', 15: 'MainDevConsole', 16: 'MainDevEvents', 20: 'MeshGeneral', 30: 'UserGeneral', 31: 'UserEvents', 6: 'ServerGeneral', 40: 'ServerStats', 115: 'ServerConsole' };
for (var i in panels) {
QC(panels[i]).remove('style3x');
QC(panels[i]).remove('style3sel');

File diff suppressed because one or more lines are too long

View File

@ -22,7 +22,7 @@
<div id=topbar class="noselect style3">
<div id=toggle title="Toggle full width" onclick="toggleFullScreen(1)">&harr;</div>
</div>
<div id=column_l>
<div id=column_l style="height:calc(100vh - 111px)">
<h1>Welcome</h1>
<div id="welcomeText" style="display:none">Connect to your home or office devices from anywhere in the world using <a href="http://www.meshcommander.com/meshcentral2">MeshCentral</a>, the real time, open source remote monitoring and management web site. You will need to download and install a management agent on your computers. Once installed, computers will show up in the &quot;My Devices&quot; section of this web site and you will be able to monitor them and take control of them.</div>
<table id="centralTable" style="">

File diff suppressed because one or more lines are too long

View File

@ -217,36 +217,144 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) {
obj.authenticate = function (name, pass, domain, fn) {
if ((typeof (name) != 'string') || (typeof (pass) != 'string') || (typeof (domain) != 'object')) { fn(new Error('invalid fields')); return; }
if (!module.parent) console.log('authenticating %s:%s:%s', domain.id, name, pass);
var user = obj.users['user/' + domain.id + '/' + name.toLowerCase()];
// Query the db for the given username
if (!user) { fn(new Error('cannot find user')); return; }
// Apply the same algorithm to the POSTed password, applying the hash against the pass / salt, if there is a match we found the user
if (user.salt == null) {
fn(new Error('invalid password'));
} else {
if (user.passtype != null) {
// IIS default clear or weak password hashing (SHA-1)
require('./pass').iishash(user.passtype, pass, user.salt, function (err, hash) {
if (err) return fn(err);
if (hash == user.hash) {
// Update the password to the stronger format.
require('./pass').hash(pass, function (err, salt, hash) { if (err) throw err; user.salt = salt; user.hash = hash; delete user.passtype; obj.db.SetUser(user); });
if (domain.auth == 'ldap') {
if (domain.ldapoptions.url == 'test') {
// Fake LDAP login
var xxuser = domain.ldapoptions[name.toLowerCase()];
if (xxuser == null) {
fn(new Error('invalid password'));
return;
} else {
var username = xxuser['displayName'];
if (domain.ldapusername) { username = xxuser[domain.ldapusername]; }
var shortname = null;
if (domain.ldapuserbinarykey) {
// Use a binary key as the userid
if (xxuser[domain.ldapuserbinarykey]) { shortname = Buffer.from(xxuser[domain.ldapuserbinarykey], 'binary').toString('hex'); }
} else if (domain.ldapuserkey) {
// Use a string key as the userid
if (xxuser[domain.ldapuserkey]) { shortname = xxuser[domain.ldapuserkey]; }
} else {
// Use the default key as the userid
if (xxuser.objectSid) { shortname = Buffer.from(xxuser.objectSid, 'binary').toString('hex').toLowerCase(); }
else if (xxuser.objectGUID) { shortname = Buffer.from(xxuser.objectGUID, 'binary').toString('hex').toLowerCase(); }
else if (xxuser.name) { shortname = xxuser.name; }
else if (xxuser.cn) { shortname = xxuser.cn; }
}
if (username == null) { fn(new Error('no user name')); return; }
if (shortname == null) { fn(new Error('no user identifier')); return; }
var userid = 'user/' + domain.id + '/' + shortname;
var user = obj.users[userid];
if (user == null) {
// Create a new user
var user = { type: 'user', _id: userid, name: username, creation: Math.floor(Date.now() / 1000), login: Math.floor(Date.now() / 1000), domain: domain.id };
var usercount = 0;
for (var i in obj.users) { if (obj.users[i].domain == domain.id) { usercount++; } }
if (usercount == 0) { user.siteadmin = 0xFFFFFFFF; if (domain.newaccounts === 2) { domain.newaccounts = 0; } } // If this is the first user, give the account site admin.
obj.users[user._id] = user;
obj.db.SetUser(user);
obj.parent.DispatchEvent(['*', 'server-users'], obj, { etype: 'user', userid: userid, username: username, account: obj.CloneSafeUser(user), action: 'accountcreate', msg: 'Account created, name is ' + name, domain: domain.id });
return fn(null, user._id);
} else {
// This is an existing user
// If the display username has changes, update it.
if (user.name != username) {
user.name = username;
db.SetUser(user);
parent.DispatchEvent(['*', 'server-users', user._id], obj, { etype: 'user', username: user.name, account: obj.CloneSafeUser(user), action: 'accountchange', msg: 'Changed account display name to ' + username, domain: domain.id });
}
// If user is locker out, block here.
if ((user.siteadmin) && (user.siteadmin != 0xFFFFFFFF) && (user.siteadmin & 32) != 0) { fn('locked'); return; }
return fn(null, user._id);
}
fn(new Error('invalid password'), null, user.passhint);
});
}
} else {
// Default strong password hashing (pbkdf2 SHA384)
require('./pass').hash(pass, user.salt, function (err, hash) {
if (err) return fn(err);
if (hash == user.hash) {
// LDAP login
var LdapAuth = require('ldapauth-fork');
var ldap = new LdapAuth(domain.ldapoptions);
ldap.authenticate(name, pass, function (err, xxuser) {
try { ldap.close(); } catch (ex) { console.log(ex); } // Close the LDAP object
if (err) { fn(new Error('invalid password')); return; }
var shortname = null;
var username = xxuser['displayName'];
if (domain.ldapusername) { username = xxuser[domain.ldapusername]; }
if (domain.ldapuserbinarykey) {
// Use a binary key as the userid
if (xxuser[domain.ldapuserbinarykey]) { shortname = Buffer.from(xxuser[domain.ldapuserbinarykey], 'binary').toString('hex').toLowerCase(); }
} else if (domain.ldapuserkey) {
// Use a string key as the userid
if (xxuser[domain.ldapuserkey]) { shortname = xxuser[domain.ldapuserkey]; }
} else {
// Use the default key as the userid
if (xxuser.objectSid) { shortname = Buffer.from(xxuser.objectSid, 'binary').toString('hex').toLowerCase(); }
else if (xxuser.objectGUID) { shortname = Buffer.from(xxuser.objectGUID, 'binary').toString('hex').toLowerCase(); }
else if (xxuser.name) { shortname = xxuser.name; }
else if (xxuser.cn) { shortname = xxuser.cn; }
}
if (username == null) { fn(new Error('no user name')); return; }
if (shortname == null) { fn(new Error('no user identifier')); return; }
var userid = 'user/' + domain.id + '/' + shortname;
var user = obj.users[userid];
if (user == null) {
// This user does not exist, create a new account.
var user = { type: 'user', _id: userid, name: shortname, creation: Math.floor(Date.now() / 1000), login: Math.floor(Date.now() / 1000), domain: domain.id };
var usercount = 0;
for (var i in obj.users) { if (obj.users[i].domain == domain.id) { usercount++; } }
if (usercount == 0) { user.siteadmin = 0xFFFFFFFF; if (domain.newaccounts === 2) { domain.newaccounts = 0; } } // If this is the first user, give the account site admin.
obj.users[user._id] = user;
obj.db.SetUser(user);
obj.parent.DispatchEvent(['*', 'server-users'], obj, { etype: 'user', username: user.name, account: obj.CloneSafeUser(user), action: 'accountcreate', msg: 'Account created, name is ' + name, domain: domain.id });
return fn(null, user._id);
} else {
// This is an existing user
// If the display username has changes, update it.
if (user.name != username) {
user.name = username;
db.SetUser(user);
parent.DispatchEvent(['*', 'server-users', user._id], obj, { etype: 'user', username: user.name, account: obj.CloneSafeUser(user), action: 'accountchange', msg: 'Changed account display name to ' + username, domain: domain.id });
}
// If user is locker out, block here.
if ((user.siteadmin) && (user.siteadmin != 0xFFFFFFFF) && (user.siteadmin & 32) != 0) { fn('locked'); return; }
return fn(null, user._id);
}
fn(new Error('invalid password'), null, user.passhint);
});
}
} else {
// Regular login
var user = obj.users['user/' + domain.id + '/' + name.toLowerCase()];
// Query the db for the given username
if (!user) { fn(new Error('cannot find user')); return; }
// Apply the same algorithm to the POSTed password, applying the hash against the pass / salt, if there is a match we found the user
if (user.salt == null) {
fn(new Error('invalid password'));
} else {
if (user.passtype != null) {
// IIS default clear or weak password hashing (SHA-1)
require('./pass').iishash(user.passtype, pass, user.salt, function (err, hash) {
if (err) return fn(err);
if (hash == user.hash) {
// Update the password to the stronger format.
require('./pass').hash(pass, function (err, salt, hash) { if (err) throw err; user.salt = salt; user.hash = hash; delete user.passtype; obj.db.SetUser(user); });
if ((user.siteadmin) && (user.siteadmin != 0xFFFFFFFF) && (user.siteadmin & 32) != 0) { fn('locked'); return; }
return fn(null, user._id);
}
fn(new Error('invalid password'), null, user.passhint);
});
} else {
// Default strong password hashing (pbkdf2 SHA384)
require('./pass').hash(pass, user.salt, function (err, hash) {
if (err) return fn(err);
if (hash == user.hash) {
if ((user.siteadmin) && (user.siteadmin != 0xFFFFFFFF) && (user.siteadmin & 32) != 0) { fn('locked'); return; }
return fn(null, user._id);
}
fn(new Error('invalid password'), null, user.passhint);
});
}
}
}
};
@ -611,7 +719,7 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) {
function handleCreateAccountRequest(req, res) {
const domain = checkUserIpAddress(req, res);
if ((domain == null) || (domain.auth == 'sspi')) return;
if ((domain == null) || (domain.auth == 'sspi') || (domain.auth == 'ldap')) { res.sendStatus(401); return; }
if ((domain.newaccounts === 0) || (domain.newaccounts === false)) { res.sendStatus(401); return; }
@ -684,8 +792,7 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) {
obj.db.SetUser(user);
// Send the verification email
if ((obj.parent.mailserver != null) && (domain.auth != 'sspi') && (obj.common.validateEmail(user.email, 1, 256) == true)) { obj.parent.mailserver.sendAccountCheckMail(domain, user.name, user.email); }
if ((obj.parent.mailserver != null) && (domain.auth != 'sspi') && (domain.auth != 'ldap') && (obj.common.validateEmail(user.email, 1, 256) == true)) { obj.parent.mailserver.sendAccountCheckMail(domain, user.name, user.email); }
});
obj.parent.DispatchEvent(['*', 'server-users'], obj, { etype: 'user', username: user.name, account: obj.CloneSafeUser(user), action: 'accountcreate', msg: 'Account created, email is ' + req.body.email, domain: domain.id });
}
@ -702,7 +809,7 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) {
const domain = checkUserIpAddress(req, res);
// Check everything is ok
if ((domain == null) || (domain.auth == 'sspi') || (typeof req.body.rpassword1 != 'string') || (typeof req.body.rpassword2 != 'string') || (req.body.rpassword1 != req.body.rpassword2) || (typeof req.body.rpasswordhint != 'string') || (req.session == null) || (typeof req.session.resettokenusername != 'string') || (typeof req.session.resettokenpassword != 'string')) {
if ((domain == null) || (domain.auth == 'sspi') || (domain.auth == 'ldap') || (typeof req.body.rpassword1 != 'string') || (typeof req.body.rpassword2 != 'string') || (req.body.rpassword1 != req.body.rpassword2) || (typeof req.body.rpasswordhint != 'string') || (req.session == null) || (typeof req.session.resettokenusername != 'string') || (typeof req.session.resettokenpassword != 'string')) {
delete req.session.loginmode;
delete req.session.tokenusername;
delete req.session.tokenpassword;
@ -776,7 +883,7 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) {
// Called to process an account reset request
function handleResetAccountRequest(req, res) {
const domain = checkUserIpAddress(req, res);
if ((domain == null) || (domain.auth == 'sspi')) return;
if ((domain == null) || (domain.auth == 'sspi') || (domain.auth == 'ldap')) { res.sendStatus(401); return; }
// Get the email from the body or session.
var email = req.body.email;
@ -840,7 +947,7 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) {
// Called to process a web based email verification request
function handleCheckMailRequest(req, res) {
const domain = checkUserIpAddress(req, res);
if ((domain == null) || (domain.auth == 'sspi')) return;
if ((domain == null) || (domain.auth == 'sspi') || (domain.auth == 'ldap')) { res.sendStatus(401); return; }
if (req.query.c != null) {
var cookie = obj.parent.decodeCookie(req.query.c, obj.parent.mailserver.mailCookieEncryptionKey, 30);
@ -927,7 +1034,7 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) {
function handleDeleteAccountRequest(req, res) {
const domain = checkUserIpAddress(req, res);
if ((domain == null) || (domain.auth == 'sspi')) return;
if ((domain == null) || (domain.auth == 'sspi') || (domain.auth == 'ldap')) { res.sendStatus(401); return; }
// Check if the user is logged and we have all required parameters
if (!req.session || !req.session.userid || !req.body.apassword1 || (req.body.apassword1 != req.body.apassword2) || (req.session.domainid != domain.id)) { res.redirect(domain.url); return; }
@ -998,7 +1105,7 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) {
// Handle password changes
function handlePasswordChangeRequest(req, res) {
const domain = checkUserIpAddress(req, res);
if ((domain == null) || (domain.auth == 'sspi')) return;
if ((domain == null) || (domain.auth == 'sspi') || (domain.auth == 'ldap')) { res.sendStatus(401); return; }
// Check if the user is logged and we have all required parameters
if (!req.session || !req.session.userid || !req.body.apassword0 || !req.body.apassword1 || (req.body.apassword1 != req.body.apassword2) || (req.session.domainid != domain.id)) { res.redirect(domain.url); return; }
@ -1038,15 +1145,12 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) {
domain.sspi.authenticate(req, res, function (err) { if ((err != null) || (req.connection.user == null)) { res.end('Authentication Required...'); } else { handleRootRequestEx(req, res, domain); } });
} else if (req.query.user && req.query.pass) {
// User credentials are being passed in the URL. WARNING: Putting credentials in a URL is not good security... but people are requesting this option.
var userid = 'user/' + domain.id + '/' + req.query.user.toLowerCase();
if (obj.users[userid] != null) {
obj.authenticate(req.query.user, req.query.pass, domain, function (err, userid) {
req.session.userid = userid;
req.session.domainid = domain.id;
req.session.currentNode = '';
handleRootRequestEx(req, res, domain);
});
}
obj.authenticate(req.query.user, req.query.pass, domain, function (err, userid) {
req.session.userid = userid;
req.session.domainid = domain.id;
req.session.currentNode = '';
handleRootRequestEx(req, res, domain);
});
} else {
// Login using a different system
handleRootRequestEx(req, res, domain);
@ -1174,6 +1278,7 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) {
if (domain.geolocation == true) { features += 0x00008000; } // Enable geo-location features
if ((domain.passwordrequirements != null) && (domain.passwordrequirements.hint === true)) { features += 0x00010000; } // Enable password hints
if ((parent.config.settings.no2factorauth !== true) && (obj.f2l != null)) { features += 0x00020000; } // Enable WebAuthn/FIDO2 support
if ((obj.args.nousers != true) && (domain.passwordrequirements != null) && (domain.passwordrequirements.force2factor === true)) { features += 0x00040000; } // Force 2-factor auth
// Create a authentication cookie
const authCookie = obj.parent.encodeCookie({ userid: user._id, domainid: domain.id }, obj.parent.loginCookieEncryptionKey);
@ -1185,14 +1290,14 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) {
if (obj.args.minify && !req.query.nominify) {
// Try to server the minified version if we can.
try {
res.render(obj.path.join(obj.parent.webViewsPath, isMobileBrowser(req) ? 'default-mobile-min' : 'default-min'), { authCookie: authCookie, viewmode: viewmode, currentNode: currentNode, logoutControl: logoutcontrol, title: domain.title, title2: domain.title2, domainurl: domain.url, domain: domain.id, debuglevel: parent.debugLevel, serverDnsName: obj.getWebServerName(domain), serverRedirPort: args.redirport, serverPublicPort: httpsPort, noServerBackup: (args.noserverbackup == 1 ? 1 : 0), features: features, sessiontime: args.sessiontime, mpspass: args.mpspass, passRequirements: passRequirements, webcerthash: Buffer.from(obj.webCertificateFullHashs[domain.id], 'binary').toString('base64').replace(/\+/g, '@').replace(/\//g, '$'), footer: (domain.footer == null) ? '' : domain.footer });
res.render(obj.path.join(obj.parent.webViewsPath, isMobileBrowser(req) ? 'default-mobile-min' : 'default-min'), { authCookie: authCookie, viewmode: viewmode, currentNode: currentNode, logoutControl: logoutcontrol, title: domain.title, title2: domain.title2, extitle: encodeURIComponent(domain.title), extitle2: encodeURIComponent(domain.title2), domainurl: domain.url, domain: domain.id, debuglevel: parent.debugLevel, serverDnsName: obj.getWebServerName(domain), serverRedirPort: args.redirport, serverPublicPort: httpsPort, noServerBackup: (args.noserverbackup == 1 ? 1 : 0), features: features, sessiontime: args.sessiontime, mpspass: args.mpspass, passRequirements: passRequirements, webcerthash: Buffer.from(obj.webCertificateFullHashs[domain.id], 'binary').toString('base64').replace(/\+/g, '@').replace(/\//g, '$'), footer: (domain.footer == null) ? '' : domain.footer });
} catch (ex) {
// In case of an exception, serve the non-minified version.
res.render(obj.path.join(obj.parent.webViewsPath, isMobileBrowser(req) ? 'default-mobile' : 'default'), { authCookie: authCookie, viewmode: viewmode, currentNode: currentNode, logoutControl: logoutcontrol, title: domain.title, title2: domain.title2, domainurl: domain.url, domain: domain.id, debuglevel: parent.debugLevel, serverDnsName: obj.getWebServerName(domain), serverRedirPort: args.redirport, serverPublicPort: httpsPort, noServerBackup: (args.noserverbackup == 1 ? 1 : 0), features: features, sessiontime: args.sessiontime, mpspass: args.mpspass, passRequirements: passRequirements, webcerthash: Buffer.from(obj.webCertificateFullHashs[domain.id], 'binary').toString('base64').replace(/\+/g, '@').replace(/\//g, '$'), footer: (domain.footer == null) ? '' : domain.footer });
res.render(obj.path.join(obj.parent.webViewsPath, isMobileBrowser(req) ? 'default-mobile' : 'default'), { authCookie: authCookie, viewmode: viewmode, currentNode: currentNode, logoutControl: logoutcontrol, title: domain.title, title2: domain.title2, extitle: encodeURIComponent(domain.title), extitle2: encodeURIComponent(domain.title2), domainurl: domain.url, domain: domain.id, debuglevel: parent.debugLevel, serverDnsName: obj.getWebServerName(domain), serverRedirPort: args.redirport, serverPublicPort: httpsPort, noServerBackup: (args.noserverbackup == 1 ? 1 : 0), features: features, sessiontime: args.sessiontime, mpspass: args.mpspass, passRequirements: passRequirements, webcerthash: Buffer.from(obj.webCertificateFullHashs[domain.id], 'binary').toString('base64').replace(/\+/g, '@').replace(/\//g, '$'), footer: (domain.footer == null) ? '' : domain.footer });
}
} else {
// Serve non-minified version of web pages.
res.render(obj.path.join(obj.parent.webViewsPath, isMobileBrowser(req) ? 'default-mobile' : 'default'), { authCookie: authCookie, viewmode: viewmode, currentNode: currentNode, logoutControl: logoutcontrol, title: domain.title, title2: domain.title2, domainurl: domain.url, domain: domain.id, debuglevel: parent.debugLevel, serverDnsName: obj.getWebServerName(domain), serverRedirPort: args.redirport, serverPublicPort: httpsPort, noServerBackup: (args.noserverbackup == 1 ? 1 : 0), features: features, sessiontime: args.sessiontime, mpspass: args.mpspass, passRequirements: passRequirements, webcerthash: Buffer.from(obj.webCertificateFullHashs[domain.id], 'binary').toString('base64').replace(/\+/g, '@').replace(/\//g, '$'), footer: (domain.footer == null) ? '' : domain.footer });
res.render(obj.path.join(obj.parent.webViewsPath, isMobileBrowser(req) ? 'default-mobile' : 'default'), { authCookie: authCookie, viewmode: viewmode, currentNode: currentNode, logoutControl: logoutcontrol, title: domain.title, title2: domain.title2, extitle: encodeURIComponent(domain.title), extitle2: encodeURIComponent(domain.title2), domainurl: domain.url, domain: domain.id, debuglevel: parent.debugLevel, serverDnsName: obj.getWebServerName(domain), serverRedirPort: args.redirport, serverPublicPort: httpsPort, noServerBackup: (args.noserverbackup == 1 ? 1 : 0), features: features, sessiontime: args.sessiontime, mpspass: args.mpspass, passRequirements: passRequirements, webcerthash: Buffer.from(obj.webCertificateFullHashs[domain.id], 'binary').toString('base64').replace(/\+/g, '@').replace(/\//g, '$'), footer: (domain.footer == null) ? '' : domain.footer });
}
} else {
// Send back the login application
@ -2184,6 +2289,7 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) {
obj.db.Get(req.query.nodeid, function (err, nodes) {
if (nodes.length != 1) { res.sendStatus(401); return; }
var node = nodes[0];
// Create the meshaction.txt file for meshcmd.exe
var meshaction = {
action: req.query.meshaction,
@ -2194,7 +2300,7 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) {
username: '',
password: '',
serverId: obj.agentCertificateHashHex.toUpperCase(), // SHA384 of server HTTPS public key
serverHttpsHash: Buffer.from(obj.webCertificateHash, 'binary').toString('hex').toUpperCase(), // SHA384 of server HTTPS certificate
serverHttpsHash: Buffer.from(obj.webCertificateHashs[domain.id], 'binary').toString('hex').toUpperCase(), // SHA384 of server HTTPS certificate
debugLevel: 0
};
if (user != null) { meshaction.username = user.name; }
@ -2209,7 +2315,7 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) {
username: '',
password: '',
serverId: obj.agentCertificateHashHex.toUpperCase(), // SHA384 of server HTTPS public key
serverHttpsHash: Buffer.from(obj.webCertificateHash, 'binary').toString('hex').toUpperCase(), // SHA384 of server HTTPS certificate
serverHttpsHash: Buffer.from(obj.webCertificateHashs[domain.id], 'binary').toString('hex').toUpperCase(), // SHA384 of server HTTPS certificate
debugLevel: 0
};
if (user != null) { meshaction.username = user.name; }