Merge branch 'master' into master

This commit is contained in:
elastalink 2019-04-15 21:04:54 -04:00 committed by GitHub
commit 47438294be
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
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.on('error', function (e) { sendConsoleText('ERROR: ' + JSON.stringify(e)); });
tunnel.sessionid = data.sessionid; tunnel.sessionid = data.sessionid;
tunnel.rights = data.rights; tunnel.rights = data.rights;
tunnel.consent = data.consent;
tunnel.username = data.username;
tunnel.state = 0; tunnel.state = 0;
tunnel.url = xurl; tunnel.url = xurl;
tunnel.protocol = 0; tunnel.protocol = 0;
@ -706,6 +708,11 @@ function createMeshCore(agent) {
return; 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 // Remote terminal using native pipes
if (process.platform == "win32") if (process.platform == "win32")
{ {
@ -759,14 +766,16 @@ function createMeshCore(agent) {
return; 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 // Remote desktop using native pipes
this.httprequest.desktop = { state: 0, kvm: mesh.getRemoteDesktopStream(), tunnel: this }; this.httprequest.desktop = { state: 0, kvm: mesh.getRemoteDesktopStream(), tunnel: this };
this.httprequest.desktop.kvm.parent = this.httprequest.desktop; this.httprequest.desktop.kvm.parent = this.httprequest.desktop;
this.desktop = this.httprequest.desktop; this.desktop = this.httprequest.desktop;
// Display a toast message
//require('toaster').Toast('MeshCentral', 'Remote Desktop Control Started.');
this.end = function () { this.end = function () {
--this.desktop.kvm.connectionCount; --this.desktop.kvm.connectionCount;
@ -811,6 +820,11 @@ function createMeshCore(agent) {
return; 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 // Setup files
// NOP // 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 then
# upstart # 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 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 'meshagent installed as upstart/init.d service.'
echo 'To start service: sudo initctl start meshagent' echo 'To start service: sudo initctl start meshagent'
echo 'To stop service: sudo initctl stop meshagent' echo 'To stop service: sudo initctl stop meshagent'
@ -193,7 +193,7 @@ UninstallAgent() {
rm -f /sbin/meshcmd /etc/init.d/meshagent rm -f /sbin/meshcmd /etc/init.d/meshagent
elif [ $starttype -eq 2 ]; then elif [ $starttype -eq 2 ]; then
# upstart # upstart
service meshagent stop initctl stop meshagent
rm -f /sbin/meshcmd rm -f /sbin/meshcmd
rm -f /etc/init/meshagent.conf rm -f /etc/init/meshagent.conf
rm -f /etc/rc2.d/S20mesh /etc/rc3.d/S20mesh /etc/rc5.d/S20mesh 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 argcount = ReadShort(obj.script, obj.ip + 4);
var argptr = obj.ip + 6; var argptr = obj.ip + 6;
var args = []; var args = [];
// Clear all temp variables (This is optional) // Clear all temp variables (This is optional)
for (var i in obj.variables) { if (i.startsWith('__')) { delete obj.variables[i]; } } 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 // Loop on each argument, moving forward by the argument length each time
for (var i = 0; i < argcount; i++) { for (var i = 0; i < argcount; i++) {
var arglen = ReadShort(obj.script, argptr); var arglen = ReadShort(obj.script, argptr);
@ -122,7 +122,7 @@ module.exports.setup = function(binary, startvars) {
if (argtyp < 2) { if (argtyp < 2) {
// Get the value and replace all {var} with variable values // Get the value and replace all {var} with variable values
argval = argval.toString(); 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. 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); args.push(argval);
} }
@ -132,7 +132,7 @@ module.exports.setup = function(binary, startvars) {
} }
argptr += (2 + arglen); argptr += (2 + arglen);
} }
// Move instruction pointer forward by command size // Move instruction pointer forward by command size
obj.ip += cmdlen; 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(); this.TrayIcon.remove();
handled = true; handled = true;
} }
if (!handled) { console.log(msg); } //if (!handled) { console.log(msg); }
} }
}); });
retVal.remove = function remove() 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]; }); }; 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 // 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) { for (var i in obj) {
if (i.toLowerCase() !== i) { obj[i.toLowerCase()] = obj[i]; delete obj[i]; } // LowerCase all key names 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; return obj;
}; };

23
db.js
View File

@ -281,7 +281,27 @@ module.exports.CreateDB = function (parent) {
// Database actions on the main collection // Database actions on the main collection
obj.Set = function (data, func) { obj.file.update({ _id: data._id }, data, { upsert: true }, func); }; 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.GetAll = function (func) { obj.file.find({}, func); };
obj.GetAllTypeNoTypeField = function (type, domain, func) { obj.file.find({ type: type, domain: domain }, { type: 0 }, 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); }; 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. // 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.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)); } 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; } if ((obj.authenticated != 1) || (obj.meshid == null) || obj.pendingCompleteAgentConnection || (obj.agentInfo == null)) { return; }
obj.pendingCompleteAgentConnection = true; 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 // Check if we have too many agent sessions
if (typeof domain.limits.maxagentsessions == 'number') { if (typeof domain.limits.maxagentsessions == 'number') {
// Count the number of agent sessions for this domain // 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 (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) { 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); db.Set(device);
// If this is a temporary device, don't log changes // 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. // 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 == null) || (typeof obj.agentInfo.capabilities != 'number')) { return; } // This is an odd case.
if ((obj.agentInfo.capabilities & 64) != 0) { // Check if we need to make an native update check
// This is a recovery agent obj.agentExeInfo = parent.parent.meshAgentBinaries[obj.agentInfo.agentId];
obj.send(common.ShortToStr(11) + common.ShortToStr(0)); // Command 11, ask for mesh core hash. const corename = parent.parent.meshAgentsArchitectureNumbers[obj.agentInfo.agentId].core;
} else { 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)) { if ((obj.agentExeInfo != null) && (obj.agentExeInfo.update == true)) {
// Ask the agent for it's executable binary hash // Ask the agent for it's executable binary hash
obj.send(common.ShortToStr(12) + common.ShortToStr(0)); 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 { } else {
// Check the mesh core, if the agent is capable of running one agentCoreIsStable(); // No updates needed, agent is ready to go.
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.
}
} }
} }
}); });
} }
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. // Take a basic Intel AMT policy and add all server information to it, making it ready to send to this agent.
function completeIntelAmtPolicy(amtPolicy) { function completeIntelAmtPolicy(amtPolicy) {
if (amtPolicy == null) return null; 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() { function agentCoreIsStable() {
// Check that the mesh exists // Check that the mesh exists
const mesh = parent.meshes[obj.dbMeshKey]; 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. 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 // 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. 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) { } 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; 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: { default: {
console.log('Unknown agent action (' + obj.remoteaddrport + '): ' + command.action + '.'); console.log('Unknown agent action (' + obj.remoteaddrport + '): ' + command.action + '.');
break; break;
@ -1076,6 +1168,7 @@ module.exports.CreateMeshAgent = function (parent, db, ws, req, args, domain) {
// Change the current core information string and event it // Change the current core information string and event it
function ChangeAgentCoreInfo(command) { function ChangeAgentCoreInfo(command) {
if (obj.agentInfo.capabilities & 0x40) return;
if ((command == null) || (command == null)) return; // Safety, should never happen. if ((command == null) || (command == null)) return; // Safety, should never happen.
// Check that the mesh exists // 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 there are changes, event the new device
if (change == 1) { 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 // Save to the database
db.Set(device); 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 // Change the current core information string and event it
function ChangeAgentLocationInfo(command) { function ChangeAgentLocationInfo(command) {
if (obj.agentInfo.capabilities & 0x40) return;
if ((command == null) || (command == null)) { return; } // Safety, should never happen. if ((command == null) || (command == null)) { return; } // Safety, should never happen.
// Check that the mesh exists // 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 there are changes, save and event
if (change == 1) { 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); db.Set(device);
// Event the node change // 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 // Update the mesh agent tab in the database
function ChangeAgentTag(tag) { function ChangeAgentTag(tag) {
if (obj.agentInfo.capabilities & 0x40) return;
if (tag.length == 0) { tag = null; } if (tag.length == 0) { tag = null; }
// Get the node and change it if needed // Get the node and change it if needed
db.Get(obj.dbNodeKey, function (err, nodes) { 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]; const device = nodes[0];
if (device.agent) { if (device.agent) {
if (device.agent.tag != tag) { 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; device.agent.tag = tag;
db.Set(device); db.Set(device);

View File

@ -479,7 +479,7 @@ function CreateMeshCentralServer(config, args) {
// Lower case all keys in the config file // Lower case all keys in the config file
try { try {
require('./common.js').objKeysToLower(config2); require('./common.js').objKeysToLower(config2, ["ldapoptions"]);
} catch (ex) { } catch (ex) {
console.log('CRITICAL ERROR: Unable to access the file \"./common.js\".\r\nCheck folder & file permissions.'); console.log('CRITICAL ERROR: Unable to access the file \"./common.js\".\r\nCheck folder & file permissions.');
process.exit(); 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; } 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 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 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) { 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].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 = '/'; } 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; 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].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].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 (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 // Log passed arguments into Windows Service Log
@ -1197,8 +1204,8 @@ function CreateMeshCentralServer(config, args) {
// Read the agent recovery core if present // Read the agent recovery core if present
var meshAgentRecoveryCore = null; var meshAgentRecoveryCore = null;
if (obj.fs.existsSync(obj.path.join(__dirname, 'agents', 'agentrecoverycore.js')) == true) { if (obj.fs.existsSync(obj.path.join(__dirname, 'agents', 'meshcore_diagnostic.js')) == true) {
try { meshAgentRecoveryCore = obj.fs.readFileSync(obj.path.join(__dirname, 'agents', 'agentrecoverycore.js')).toString(); } catch (ex) { } try { meshAgentRecoveryCore = obj.fs.readFileSync(obj.path.join(__dirname, 'agents', 'meshcore_diagnostic.js')).toString(); } catch (ex) { }
if (meshAgentRecoveryCore != null) { if (meshAgentRecoveryCore != null) {
modulesAdd['windows-agentrecovery'] = ['var addedModules = [];\r\n']; modulesAdd['windows-agentrecovery'] = ['var addedModules = [];\r\n'];
modulesAdd['linux-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 // Lower case all keys in the config file
try { try {
require('./common.js').objKeysToLower(config); require('./common.js').objKeysToLower(config, ["ldapoptions"]);
} catch (ex) { } catch (ex) {
console.log('CRITICAL ERROR: Unable to access the file \"./common.js\".\r\nCheck folder & file permissions.'); console.log('CRITICAL ERROR: Unable to access the file \"./common.js\".\r\nCheck folder & file permissions.');
process.exit(); process.exit();
} }
return config; return config;
} }
@ -1680,16 +1688,24 @@ function mainStart(args) {
var config = getConfig(false); var config = getConfig(false);
if (config == null) { process.exit(); } 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 // Check is Windows SSPI and YubiKey OTP will be used
var sspi = false; var sspi = false;
var ldap = false;
var allsspi = true; var allsspi = true;
var yubikey = false; 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; } 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 // 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']; 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 (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.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.settings.mongodb != null) { modules.push('mongojs'); } // Add MongoDB
if (config.smtp != null) { modules.push('nodemailer'); } // Add SMTP support 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) { obj.sendAgentMessage = function (command, userid, domainid) {
var rights; var rights, mesh;
if (command.nodeid == null) return false; if (command.nodeid == null) return false;
var user = obj.parent.users[userid]; var user = obj.parent.users[userid];
if (user == null) return false; if (user == null) return false;
@ -66,9 +66,13 @@ module.exports.CreateMeshRelay = function (parent, ws, req, domain, user, cookie
if (agent != null) { if (agent != null) {
// Check if we have permission to send a message to that node // Check if we have permission to send a message to that node
rights = user.links[agent.dbMeshKey]; 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.sessionid = ws.sessionId; // Set the session id, required for responses.
command.rights = rights.rights; // Add user rights flags to the message 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. delete command.nodeid; // Remove the nodeid since it's implyed.
agent.send(JSON.stringify(command)); agent.send(JSON.stringify(command));
return true; return true;
@ -79,9 +83,13 @@ module.exports.CreateMeshRelay = function (parent, ws, req, domain, user, cookie
if (routing != null) { if (routing != null) {
// Check if we have permission to send a message to that node // Check if we have permission to send a message to that node
rights = user.links[routing.meshid]; 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 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.fromSessionid = ws.sessionId; // Set the session id, required for responses.
command.rights = rights.rights; // Add user rights flags to the message 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); obj.parent.parent.multiServer.DispatchMessageSingleServer(command, routing.serverid);
return true; return true;
} }

View File

@ -19,6 +19,24 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use
const path = require('path'); const path = require('path');
const common = parent.common; 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 = {}; var obj = {};
obj.user = user; obj.user = user;
obj.domain = domain; obj.domain = domain;
@ -107,10 +125,14 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use
if (agent != null) { if (agent != null) {
// Check if we have permission to send a message to that node // Check if we have permission to send a message to that node
var rights = user.links[agent.dbMeshKey]; 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 var mesh = parent.meshes[agent.dbMeshKey];
command.sessionid = ws.sessionId; // Set the session id, required for responses. 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 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) { } try { agent.send(JSON.stringify(command)); } catch (ex) { }
} }
} else { } else {
@ -119,9 +141,13 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use
if (routing != null) { if (routing != null) {
// Check if we have permission to send a message to that node // Check if we have permission to send a message to that node
var rights = user.links[routing.meshid]; var rights = user.links[routing.meshid];
if ((rights != null) && ((rights.rights & 8) || (rights.rights & 256))) { // 8 is remote control permission var mesh = parent.meshes[agent.dbMeshKey];
command.fromSessionid = ws.sessionId; // Set the session id, required for responses. 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.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); 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); var httpport = ((args.aliasport != null) ? args.aliasport : args.port);
// Build server information object // 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 (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 // Send server information
try { ws.send(JSON.stringify({ action: 'serverinfo', serverinfo: serverinfo })); } catch (ex) { } 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) { db.GetAllTypeNoTypeFieldMeshFiltered(links, domain.id, 'node', command.id, function (err, docs) {
var r = {}; var r = {};
for (i in docs) { 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 // Add the connection state
var state = parent.parent.GetConnectivityState(docs[i]._id); var state = parent.parent.GetConnectivityState(docs[i]._id);
if (state) { if (state) {
@ -690,7 +724,7 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use
case 'changeemail': case 'changeemail':
{ {
// Change the email address // 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 (common.validateEmail(command.email, 1, 256) == false) return;
if (parent.users[req.session.userid].email != command.email) { if (parent.users[req.session.userid].email != command.email) {
// Check if this email is already validated on a different account // 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); parent.parent.DispatchEvent(['*', 'server-users', user._id], obj, message);
// Send the verification email // 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': case 'verifyemail':
{ {
// Send a account email verification email // 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 (common.validateString(command.email, 3, 1024) == false) return;
if ((parent.parent.mailserver != null) && (parent.users[req.session.userid].email == command.email)) { if ((parent.parent.mailserver != null) && (parent.users[req.session.userid].email == command.email)) {
// Send the verification email // Send the verification email
@ -853,20 +887,43 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use
case 'edituser': case 'edituser':
{ {
// Edit a user account, may involve changing email or administrator permissions // Edit a user account, may involve changing email or administrator permissions
if (((user.siteadmin & 2) != 0) || (user.name == command.name)) { if (((user.siteadmin & 2) != 0) || (user._id == command.id)) {
var chguserid = 'user/' + domain.id + '/' + command.name.toLowerCase(), chguser = parent.users[chguserid]; var chguser = parent.users[command.id];
change = 0; change = 0;
if (chguser) { if (chguser) {
if (common.validateString(command.email, 1, 256) && (chguser.email != command.email)) { chguser.email = command.email; change = 1; } 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 ((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 ((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 ((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) { if (change == 1) {
// Update the user
db.SetUser(chguser); db.SetUser(chguser);
parent.parent.DispatchEvent([chguser._id], obj, 'resubscribe'); 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 ((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 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; if ((user.siteadmin != 0xFFFFFFFF) && ((user.siteadmin & 64) != 0)) break;
// In some situations, we need a verified email address to create a device group. // 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 // Create mesh
if (common.validateString(command.meshname, 1, 64) == false) break; // Meshname is between 1 and 64 characters 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 if (common.validateString(command.meshid, 1, 1024) == false) break; // Check the meshid
mesh = parent.meshes[command.meshid]; mesh = parent.meshes[command.meshid];
change = ''; change = '';
if (mesh) { if (mesh) {
// Check if this user has rights to do this // Check if this user has rights to do this
if (mesh.links[user._id] == null || ((mesh.links[user._id].rights & 1) == 0)) return; 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.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.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 ((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; break;
} }
@ -1161,7 +1220,7 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use
parent.parent.DispatchEvent([newuser._id], obj, 'resubscribe'); parent.parent.DispatchEvent([newuser._id], obj, 'resubscribe');
// Add a user to the mesh // 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)); db.Set(common.escapeLinksFieldName(mesh));
// Notify mesh change // Notify mesh change
@ -1224,7 +1283,7 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use
change = ''; change = '';
if (mesh) { if (mesh) {
// Check if this user has rights to do this // Check if this user has rights to do this
if ((mesh.links[user._id] == null) || (mesh.links[user._id].rights != 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 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 // 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.RemoveSMBIOS(node._id); // Remove SMBios data
db.RemoveAllNodeEvents(node._id); // Remove all events for this node db.RemoveAllNodeEvents(node._id); // Remove all events for this node
db.removeAllPowerEventsForNode(node._id); // Remove all power 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 // 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 }); 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.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 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.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; } } } 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); } }
if (ok == true) { groupTags.sort(function (a, b) { return a.toLowerCase().localeCompare(b.toLowerCase()); }); node.tags = groupTags; change = 1; } group2.sort();
if (node.tags != group2) { node.tags = group2; change = 1; }
} else if ((command.tags === '') && node.tags) { delete node.tags; change = 1; } } else if ((command.tags === '') && node.tags) { delete node.tags; change = 1; }
if (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 (???) 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) { if (nodes.length == 1) {
meshlinks = user.links[nodes[0].meshid]; 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 // Add a user authentication cookie to a url
var cookieContent = { userid: user._id, domainid: user.domain }; var cookieContent = { userid: user._id, domainid: user.domain };
if (command.nodeid) { cookieContent.nodeid = command.nodeid; } if (command.nodeid) { cookieContent.nodeid = command.nodeid; }

View File

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

View File

@ -43,7 +43,7 @@ var CreateAgentRedirect = function (meshserver, module, serverPublicNamePort, au
obj.socket.onclose = obj.xxOnSocketClosed; obj.socket.onclose = obj.xxOnSocketClosed;
obj.xxStateChange(1); 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: 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); //obj.debug("Agent Redir Start: " + url);
} }

View File

@ -31,6 +31,8 @@ var CreateAmtRemoteTerminal = function (divid) {
var _TermLineWrap = true; var _TermLineWrap = true;
var _termx = 0; var _termx = 0;
var _termy = 0; var _termy = 0;
var _termsavex = 0;
var _termsavey = 0;
var _termstate = 0; var _termstate = 0;
var _escNumber = []; var _escNumber = [];
var _escNumberPtr = 0; var _escNumberPtr = 0;
@ -110,7 +112,31 @@ var CreateAmtRemoteTerminal = function (divid) {
_altKeypadMode = false; _altKeypadMode = false;
_termstate = 0; _termstate = 0;
break; 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: default:
console.log('unknown terminal short code', b);
_termstate = 0; _termstate = 0;
break; break;
} }
@ -203,7 +229,8 @@ var CreateAmtRemoteTerminal = function (divid) {
case 'P': // Delete X Character(s), default 1 char case 'P': // Delete X Character(s), default 1 char
var x = 1; var x = 1;
if (argslen == 1) { x = args[0]; } 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; break;
case 'L': // Insert X Line(s), default 1 char case 'L': // Insert X Line(s), default 1 char
var linecount = 1; var linecount = 1;
@ -244,8 +271,7 @@ var CreateAmtRemoteTerminal = function (divid) {
if (args[1] > obj.width) args[1] = obj.width; if (args[1] > obj.width) args[1] = obj.width;
_termy = args[0] - 1; _termy = args[0] - 1;
_termx = args[1] - 1; _termx = args[1] - 1;
} } else {
else {
_termy = 0; _termy = 0;
_termx = 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); } for (var z = 0; z < obj.width; z++) { _tscreen[y][z] = ' '; _scratt[y][z] = (7 << 6); }
} }
break; 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 case 'T': // Scroll down the scroll region X lines, default 1
var x = 1; var x = 1;
if (argslen == 1) { x = args[0] } 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); } for (var z = 0; z < obj.width; z++) { _tscreen[y][z] = ' '; _scratt[y][z] = (7 << 6); }
} }
break; 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: default:
//if (code != '@') alert(code); //if (code != '@') alert(code);
//console.log('unknown terminal code', code, args, mode); console.log('unknown terminal code', code, args, mode);
break; 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 == 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 if (e.which == 9) { obj.TermSendKeys("\t"); if (e.preventDefault) e.preventDefault(); if (e.stopPropagation) e.stopPropagation(); return true; }; // TAB
// F1 to F12 keys // 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; 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 { #page_leftbar {
height: calc(100vh - 66px); height: calc(100vh - 66px);
width: 90px; width: 90px;

View File

@ -49,13 +49,15 @@
"_NewAccountEmailDomains": [ "sample.com" ], "_NewAccountEmailDomains": [ "sample.com" ],
"Footer": "<a href='https://twitter.com/mytwitter'>Twitter</a>", "Footer": "<a href='https://twitter.com/mytwitter'>Twitter</a>",
"_CertUrl": "https://192.168.2.106:443/", "_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, "_AgentNoProxy": true,
"_GeoLocation": true, "_GeoLocation": true,
"_UserAllowedIP": "127.0.0.1,192.168.1.0/24", "_UserAllowedIP": "127.0.0.1,192.168.1.0/24",
"_UserBlockedIP": "127.0.0.1,::1,192.168.0.100", "_UserBlockedIP": "127.0.0.1,::1,192.168.0.100",
"_AgentAllowedIP": "192.168.0.100/24", "_AgentAllowedIP": "192.168.0.100/24",
"_AgentBlockedIP": "127.0.0.1,::1", "_AgentBlockedIP": "127.0.0.1,::1",
"__UserConsentFlags__" : "Set to: 1 for desktop, 2 for terminal, 3 for files, 7 for all",
"_UserConsentFlags" : 7,
"_Limits": { "_Limits": {
"_MaxUserAccounts": 100, "_MaxUserAccounts": 100,
"_MaxUserSessions": 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-adler32.js"></script>
<script type="text/javascript" src="scripts/zlib-crc32.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> <script keeplink=1 type="text/javascript" src="scripts/filesaver.1.1.20151003.js"></script>
<title>MeshCentral</title> <title>{{{title}}}</title>
<style> <style>
a { a {
color: #036; color: #036;
@ -249,10 +249,10 @@
<br style=clear:both /> <br style=clear:both />
</div> </div>
<strong>Device Groups</strong> <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 /> <br /><br />
<div id=p3meshes></div> <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 /> <br style=clear:both />
</div> </div>
</div> </div>
@ -655,6 +655,10 @@
QV('manageAuthApp', features & 4096); QV('manageAuthApp', features & 4096);
QV('manageOtp', ((features & 4096) != 0) && ((userinfo.otpsecret == 1) || (userinfo.otphkeys > 0))); 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 (typeof userinfo.passchange == 'number') {
if (userinfo.passchange == -1) { QH('p2nextPasswordUpdateTime', ' - Reset on next login.'); } if (userinfo.passchange == -1) { QH('p2nextPasswordUpdateTime', ' - Reset on next login.'); }
else if ((passRequirements != null) && (typeof passRequirements.reset == 'number')) { else if ((passRequirements != null) && (typeof passRequirements.reset == 'number')) {
@ -809,7 +813,7 @@
} }
case 'createmesh': { case 'createmesh': {
// A new mesh was created // 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 }; 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(); updateMeshes();
updateDevices(); updateDevices();
@ -830,7 +834,7 @@
meshes[message.event.meshid].links = message.event.links; meshes[message.event.meshid].links = message.event.links;
// Check if we lost rights to this mesh in this change. // 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); if ((xxcurrentView == 20) && (currentMesh == meshes[message.event.meshid])) go(2);
delete meshes[message.event.meshid]; delete meshes[message.event.meshid];
@ -1165,8 +1169,14 @@
function account_createMesh() { function account_createMesh() {
if (xxdialogMode) return; if (xxdialogMode) return;
// Check if we are allowed to create a new device group // Check if we are disallowed from creating a 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; } 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 // 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() />'); var x = addHtmlValue('Name', '<input id=dp3meshname style=width:170px maxlength=64 onchange=account_validateMeshCreate() onkeyup=account_validateMeshCreate() />');
@ -1240,7 +1250,7 @@
count++; count++;
// Mesh rights // 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'; var rights = 'Partial Rights';
if (meshrights == 0xFFFFFFFF) rights = 'Full Administrator'; else if (meshrights == 0) rights = 'No 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 // Go thru the list of nodes and display them
for (var i in nodes) { for (var i in nodes) {
if (nodes[i].v == false) continue; 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; if (meshlinks == null) continue;
var meshrights = meshlinks.rights; 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. // Display all empty meshes, we need to do this because users can add devices to these at any time.
if (sort == 0) { if (sort == 0) {
for (var i in meshes) { 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) { if (meshlink != null) {
var meshrights = meshlink.rights; var meshrights = meshlink.rights;
if (displayedMeshes[mesh._id] == null) { if (displayedMeshes[mesh._id] == null) {
@ -1690,7 +1700,7 @@
function getNodeRights(nodeid) { function getNodeRights(nodeid) {
var node = getNodeFromId(nodeid), mesh = meshes[node.meshid]; 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; var currentDevicePanel = 0;
@ -1701,11 +1711,18 @@
var powerTimeline = null; var powerTimeline = null;
function getCurrentNode() { return currentNode; }; function getCurrentNode() { return currentNode; };
function gotoDevice(nodeid, panel, refresh) { 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); var node = getNodeFromId(nodeid);
if (node == null) { goBack(); return; } if (node == null) { goBack(); return; }
var mesh = meshes[node.meshid]; var mesh = meshes[node.meshid];
if (mesh == null) { goBack(); return; } 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) { if (!currentNode || currentNode._id != node._id || refresh == true) {
currentNode = node; currentNode = node;
@ -1865,7 +1882,7 @@
function setupDeviceMenu(op, obj) { function setupDeviceMenu(op, obj) {
var meshrights = 0; 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; } if (op != null) { currentDevicePanel = op; }
QV('p10general', currentDevicePanel == 0); QV('p10general', currentDevicePanel == 0);
QV('p10desktop', currentDevicePanel == 1); // Show if we have remote control rights or desktop view only rights QV('p10desktop', currentDevicePanel == 1); // Show if we have remote control rights or desktop view only rights
@ -1879,7 +1896,7 @@
function deviceActionFunction() { function deviceActionFunction() {
if (xxdialogMode) return; 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 x = "Select an operation to perform on this device.<br /><br />";
var y = '<select id=d2deviceop style=float:right;width:170px>'; var y = '<select id=d2deviceop style=float:right;width:170px>';
if ((meshrights & 64) != 0) { y += '<option value=100>Wake-up</option>'; } // Wake-up permission if ((meshrights & 64) != 0) { y += '<option value=100>Wake-up</option>'; } // Wake-up permission
@ -2022,7 +2039,7 @@
function p10showiconselector() { function p10showiconselector() {
if (xxdialogMode) return; if (xxdialogMode) return;
var mesh = meshes[currentNode.meshid]; 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; if ((meshrights & 4) == 0) return;
var x = '<table align=center><td>'; var x = '<table align=center><td>';
@ -2100,7 +2117,7 @@
var mesh = meshes[currentNode.meshid]; var mesh = meshes[currentNode.meshid];
var deskState = 0; var deskState = 0;
if (desktop != null) { deskState = desktop.State; } 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 // Show the right buttons
QV('disconnectbutton1', (deskState != 0)); QV('disconnectbutton1', (deskState != 0));
@ -2842,7 +2859,7 @@
if (currentMesh == null) return; if (currentMesh == null) return;
QH('p20meshName', EscapeHtml(currentMesh.name)); QH('p20meshName', EscapeHtml(currentMesh.name));
var meshtype = 'Unknown #' + currentMesh.mtype; 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 == 1) meshtype = 'Intel&reg; AMT group';
if (currentMesh.mtype == 2) meshtype = 'Software agent 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><input type=button value=Notes title="View notes about this mesh" onclick=showNotes(false,"' + encodeURIComponent(currentMesh._id) + '") />';
x += '<br style=clear:both><br>'; 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>'; } 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() { 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('idx_dlgOkButton', (Q('dp20username').value.length > 0));
QE('p20fulladmin', meshrights == 0xFFFFFFFF); QE('p20fulladmin', meshrights == 0xFFFFFFFF);
QE('p20editmesh', (!Q('p20fulladmin').checked) && (meshrights == 0xFFFFFFFF)); QE('p20editmesh', (!Q('p20fulladmin').checked) && (meshrights == 0xFFFFFFFF));
@ -3010,7 +3027,7 @@
function p20viewuser(userid) { function p20viewuser(userid) {
if (xxdialogMode) return; if (xxdialogMode) return;
userid = decodeURIComponent(userid); 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 == 0xFFFFFFFF) r = ', Full Administrator'; else {
if ((meshrights & 1) != 0) r += ', Edit Device Group'; if ((meshrights & 1) != 0) r += ', Edit Device Group';
if ((meshrights & 2) != 0) r += ', Manage Device Group Users'; if ((meshrights & 2) != 0) r += ', Manage Device Group Users';
@ -3029,7 +3046,7 @@
if (r == '') { r = 'No Rights'; } if (r == '') { r = 'No Rights'; }
var buttons = 1, x = addHtmlValue('User', EscapeHtml(decodeURIComponent(userid.split('/')[2]))); var buttons = 1, x = addHtmlValue('User', EscapeHtml(decodeURIComponent(userid.split('/')[2])));
x += addHtmlValue('Permissions', r); 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); setDialogMode(2, "Mesh User", buttons, p20viewuserEx, x, userid);
} }

View File

@ -3554,6 +3554,7 @@
// Remind the user to verify the email address // 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; } 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 // 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; } 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 // 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; } 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 // 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; } 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(', '); meshFeatures = meshFeatures.join(', ');
if (meshFeatures == '') { meshFeatures = '<i>None</i>'; } if (meshFeatures == '') { meshFeatures = '<i>None</i>'; }
x += addHtmlValue('Features', addLinkConditional(meshFeatures, 'p20editmeshfeatures()', meshrights & 1)); x += addHtmlValue('Features', addLinkConditional(meshFeatures, 'p20editmeshfeatures()', meshrights & 1));
// Display user consent // Display user consent
meshFeatures = []; meshFeatures = [];
var consent = 0; 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>'; } } 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)))); x += addDeviceAttribute('User Groups', addLinkConditional(userGroups, 'showUserGroupDialog(event,\"' + userid + '\")', ((userinfo.groups == null) && (userinfo.siteadmin & 2) && (userinfo._id != user._id))));
var multiFactor = 0; var multiFactor = 0;
if ((user.otpsecret > 0) || (user.otphkeys > 0)) { if ((user.otpsecret > 0) || (user.otphkeys > 0)) {
multiFactor = 1; multiFactor = 1;
@ -7586,8 +7588,8 @@
QV('MeshSubMenuSpan', x >= 20 && x < 30); QV('MeshSubMenuSpan', x >= 20 && x < 30);
QV('UserSubMenuSpan', x >= 30 && x < 40); QV('UserSubMenuSpan', x >= 30 && x < 40);
QV('ServerSubMenuSpan', x == 6 || x == 115 || 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) { for (var i in panels) {
QC(panels[i]).remove('style3x'); QC(panels[i]).remove('style3x');
QC(panels[i]).remove('style3sel'); 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=topbar class="noselect style3">
<div id=toggle title="Toggle full width" onclick="toggleFullScreen(1)">&harr;</div> <div id=toggle title="Toggle full width" onclick="toggleFullScreen(1)">&harr;</div>
</div> </div>
<div id=column_l> <div id=column_l style="height:calc(100vh - 111px)">
<h1>Welcome</h1> <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> <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=""> <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) { obj.authenticate = function (name, pass, domain, fn) {
if ((typeof (name) != 'string') || (typeof (pass) != 'string') || (typeof (domain) != 'object')) { fn(new Error('invalid fields')); return; } 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); 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 (domain.auth == 'ldap') {
if (!user) { fn(new Error('cannot find user')); return; } if (domain.ldapoptions.url == 'test') {
// Apply the same algorithm to the POSTed password, applying the hash against the pass / salt, if there is a match we found the user // Fake LDAP login
if (user.salt == null) { var xxuser = domain.ldapoptions[name.toLowerCase()];
fn(new Error('invalid password')); if (xxuser == null) {
} else { fn(new Error('invalid password'));
if (user.passtype != null) { return;
// IIS default clear or weak password hashing (SHA-1) } else {
require('./pass').iishash(user.passtype, pass, user.salt, function (err, hash) { var username = xxuser['displayName'];
if (err) return fn(err); if (domain.ldapusername) { username = xxuser[domain.ldapusername]; }
if (hash == user.hash) { var shortname = null;
// Update the password to the stronger format. if (domain.ldapuserbinarykey) {
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); }); // 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; } if ((user.siteadmin) && (user.siteadmin != 0xFFFFFFFF) && (user.siteadmin & 32) != 0) { fn('locked'); return; }
return fn(null, user._id); return fn(null, user._id);
} }
fn(new Error('invalid password'), null, user.passhint); }
});
} else { } else {
// Default strong password hashing (pbkdf2 SHA384) // LDAP login
require('./pass').hash(pass, user.salt, function (err, hash) { var LdapAuth = require('ldapauth-fork');
if (err) return fn(err); var ldap = new LdapAuth(domain.ldapoptions);
if (hash == user.hash) { 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; } if ((user.siteadmin) && (user.siteadmin != 0xFFFFFFFF) && (user.siteadmin & 32) != 0) { fn('locked'); return; }
return fn(null, user._id); 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) { function handleCreateAccountRequest(req, res) {
const domain = checkUserIpAddress(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; } 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); obj.db.SetUser(user);
// Send the verification email // 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 }); 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); const domain = checkUserIpAddress(req, res);
// Check everything is ok // 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.loginmode;
delete req.session.tokenusername; delete req.session.tokenusername;
delete req.session.tokenpassword; delete req.session.tokenpassword;
@ -776,7 +883,7 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) {
// Called to process an account reset request // Called to process an account reset request
function handleResetAccountRequest(req, res) { function handleResetAccountRequest(req, res) {
const domain = checkUserIpAddress(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. // Get the email from the body or session.
var email = req.body.email; 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 // Called to process a web based email verification request
function handleCheckMailRequest(req, res) { function handleCheckMailRequest(req, res) {
const domain = checkUserIpAddress(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) { if (req.query.c != null) {
var cookie = obj.parent.decodeCookie(req.query.c, obj.parent.mailserver.mailCookieEncryptionKey, 30); 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) { function handleDeleteAccountRequest(req, res) {
const domain = checkUserIpAddress(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 // 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; } 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 // Handle password changes
function handlePasswordChangeRequest(req, res) { function handlePasswordChangeRequest(req, res) {
const domain = checkUserIpAddress(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 // 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; } 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); } }); 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) { } 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. // 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(); obj.authenticate(req.query.user, req.query.pass, domain, function (err, userid) {
if (obj.users[userid] != null) { req.session.userid = userid;
obj.authenticate(req.query.user, req.query.pass, domain, function (err, userid) { req.session.domainid = domain.id;
req.session.userid = userid; req.session.currentNode = '';
req.session.domainid = domain.id; handleRootRequestEx(req, res, domain);
req.session.currentNode = ''; });
handleRootRequestEx(req, res, domain);
});
}
} else { } else {
// Login using a different system // Login using a different system
handleRootRequestEx(req, res, domain); 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.geolocation == true) { features += 0x00008000; } // Enable geo-location features
if ((domain.passwordrequirements != null) && (domain.passwordrequirements.hint === true)) { features += 0x00010000; } // Enable password hints 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 ((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 // Create a authentication cookie
const authCookie = obj.parent.encodeCookie({ userid: user._id, domainid: domain.id }, obj.parent.loginCookieEncryptionKey); 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) { if (obj.args.minify && !req.query.nominify) {
// Try to server the minified version if we can. // Try to server the minified version if we can.
try { 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) { } catch (ex) {
// In case of an exception, serve the non-minified version. // 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 { } else {
// Serve non-minified version of web pages. // 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 { } else {
// Send back the login application // 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) { obj.db.Get(req.query.nodeid, function (err, nodes) {
if (nodes.length != 1) { res.sendStatus(401); return; } if (nodes.length != 1) { res.sendStatus(401); return; }
var node = nodes[0]; var node = nodes[0];
// Create the meshaction.txt file for meshcmd.exe // Create the meshaction.txt file for meshcmd.exe
var meshaction = { var meshaction = {
action: req.query.meshaction, action: req.query.meshaction,
@ -2194,7 +2300,7 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) {
username: '', username: '',
password: '', password: '',
serverId: obj.agentCertificateHashHex.toUpperCase(), // SHA384 of server HTTPS public key 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 debugLevel: 0
}; };
if (user != null) { meshaction.username = user.name; } if (user != null) { meshaction.username = user.name; }
@ -2209,7 +2315,7 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) {
username: '', username: '',
password: '', password: '',
serverId: obj.agentCertificateHashHex.toUpperCase(), // SHA384 of server HTTPS public key 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 debugLevel: 0
}; };
if (user != null) { meshaction.username = user.name; } if (user != null) { meshaction.username = user.name; }