Merge pull request #1 from Ylianst/master

updating before my new branch
This commit is contained in:
elastalink 2019-03-30 19:15:46 -04:00 committed by GitHub
commit fdc087dba9
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
32 changed files with 934 additions and 1501 deletions

View File

@ -32,7 +32,6 @@
<Compile Include="agents\modules_meshcmd\amt-wsman.js" /> <Compile Include="agents\modules_meshcmd\amt-wsman.js" />
<Compile Include="agents\modules_meshcmd\amt-xml.js" /> <Compile Include="agents\modules_meshcmd\amt-xml.js" />
<Compile Include="agents\modules_meshcmd\amt.js" /> <Compile Include="agents\modules_meshcmd\amt.js" />
<Compile Include="agents\modules_meshcmd\process-manager.js" />
<Compile Include="agents\modules_meshcmd\service-host.js" /> <Compile Include="agents\modules_meshcmd\service-host.js" />
<Compile Include="agents\modules_meshcmd\service-manager.js" /> <Compile Include="agents\modules_meshcmd\service-manager.js" />
<Compile Include="agents\modules_meshcmd\smbios.js" /> <Compile Include="agents\modules_meshcmd\smbios.js" />
@ -58,21 +57,14 @@
<Compile Include="agents\modules_meshcore\amt-wsman.js" /> <Compile Include="agents\modules_meshcore\amt-wsman.js" />
<Compile Include="agents\modules_meshcore\amt-xml.js" /> <Compile Include="agents\modules_meshcore\amt-xml.js" />
<Compile Include="agents\modules_meshcore\amt.js" /> <Compile Include="agents\modules_meshcore\amt.js" />
<Compile Include="agents\modules_meshcore\clipboard.js" />
<Compile Include="agents\modules_meshcore\linux-dbus.js" /> <Compile Include="agents\modules_meshcore\linux-dbus.js" />
<Compile Include="agents\modules_meshcore\monitor-border.js" /> <Compile Include="agents\modules_meshcore\monitor-border.js" />
<Compile Include="agents\modules_meshcore\monitor-info.js" />
<Compile Include="agents\modules_meshcore\power-monitor.js" /> <Compile Include="agents\modules_meshcore\power-monitor.js" />
<Compile Include="agents\modules_meshcore\process-manager.js" />
<Compile Include="agents\modules_meshcore\service-manager.js" />
<Compile Include="agents\modules_meshcore\smbios.js" /> <Compile Include="agents\modules_meshcore\smbios.js" />
<Compile Include="agents\modules_meshcore\toaster.js" /> <Compile Include="agents\modules_meshcore\toaster.js" />
<Compile Include="agents\modules_meshcore\user-sessions.js" />
<Compile Include="agents\modules_meshcore\wifi-scanner-windows.js" /> <Compile Include="agents\modules_meshcore\wifi-scanner-windows.js" />
<Compile Include="agents\modules_meshcore\wifi-scanner.js" /> <Compile Include="agents\modules_meshcore\wifi-scanner.js" />
<Compile Include="agents\modules_meshcore\win-console.js" /> <Compile Include="agents\modules_meshcore\win-console.js" />
<Compile Include="agents\modules_meshcore\win-message-pump.js" />
<Compile Include="agents\modules_meshcore\win-registry.js" />
<Compile Include="agents\modules_meshcore\win-terminal.js" /> <Compile Include="agents\modules_meshcore\win-terminal.js" />
<Compile Include="agents\modules_meshcore_min\amt-lme.min.js" /> <Compile Include="agents\modules_meshcore_min\amt-lme.min.js" />
<Compile Include="agents\modules_meshcore_min\amt-mei.min.js" /> <Compile Include="agents\modules_meshcore_min\amt-mei.min.js" />

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -54,12 +54,12 @@ var Small_IntelAmtWebApp = "H4sIAAAAAAAEAHq/e7+Noou/c0hkgCuA0+rcdhMHwq/CcXohAVwD
// Check the server certificate fingerprint // Check the server certificate fingerprint
function onVerifyServer(clientName, certs) { function onVerifyServer(clientName, certs) {
if (certs == null) { certs = clientName; } // Temporary thing until we fix duktape if (certs == null) { certs = clientName; } // Temporary thing until we fix duktape
try { for (var i in certs) { if (certs[i].fingerprint.replace(/:/g, '') == settings.serverHttpsHash) { return; } } } catch (e) { } try { for (var i in certs) { if (certs[i].fingerprint.replace(/:/g, '') == settings.serverhttpshash) { return; } } } catch (e) { }
if (serverhash != null) { console.log('Error: Failed to verify server certificate.'); throw 'Invalid server certificate'; } if (serverhash != null) { console.log('Error: Failed to verify server certificate.'); throw 'Invalid server certificate'; }
} }
// Various utility functions // Various utility functions
function debug(level, message) { if ((settings.debugLevel != null) && (settings.debugLevel >= level)) { console.log(message); } } function debug(level, message) { if ((settings.debuglevel != null) && (settings.debuglevel >= level)) { console.log(message); } }
function exit(status) { if (status == null) { status = 0; } try { process.exit(status); } catch (e) { } } function exit(status) { if (status == null) { status = 0; } try { process.exit(status); } catch (e) { } }
function getInstance(x, y) { for (var i in x) { if (x[i]["InstanceID"] == y) return x[i]; } return null; } function getInstance(x, y) { for (var i in x) { if (x[i]["InstanceID"] == y) return x[i]; } return null; }
function md5hex(str) { return require('MD5Stream').create().syncHash(str).toString('hex'); } function md5hex(str) { return require('MD5Stream').create().syncHash(str).toString('hex'); }
@ -102,25 +102,27 @@ function run(argv) {
if ((actionpath != 'meshaction.txt') && (actionfile == null)) { console.log('Unable to load \"' + actionpath + '\". Create this file or specify the location using --actionfile [filename].'); exit(1); return; } if ((actionpath != 'meshaction.txt') && (actionfile == null)) { console.log('Unable to load \"' + actionpath + '\". Create this file or specify the location using --actionfile [filename].'); exit(1); return; }
if (actionfile != null) { try { settings = JSON.parse(actionfile); } catch (e) { console.log(actionpath, e); exit(1); return; } } else { if (argv.length >= 2) { settings = { action: argv[1] } } } if (actionfile != null) { try { settings = JSON.parse(actionfile); } catch (e) { console.log(actionpath, e); exit(1); return; } } else { if (argv.length >= 2) { settings = { action: argv[1] } } }
if (settings == null) { settings = {}; } if (settings == null) { settings = {}; }
var settings2 = {}; for (var i in settings) { settings2[i.toLowerCase()] = settings[i]; } settings = settings2;
// Set the arguments // Set the arguments
if ((typeof args.action) == 'string') { settings.action = args.action; } if ((typeof args.action) == 'string') { settings.action = args.action; }
if ((typeof args.localport) == 'string') { settings.localport = parseInt(args.localport); } if ((typeof args.localport) == 'string') { settings.localport = parseInt(args.localport); }
if ((typeof args.remotenodeid) == 'string') { settings.remoteNodeId = args.remotenodeid; } if ((typeof args.remotenodeid) == 'string') { settings.remotenodeid = args.remotenodeid; }
if ((typeof args.username) == 'string') { settings.username = args.username; } if ((typeof args.username) == 'string') { settings.username = args.username; }
if ((typeof args.password) == 'string') { settings.password = args.password; } if ((typeof args.password) == 'string') { settings.password = args.password; }
if ((typeof args.user) == 'string') { settings.username = args.user; } if ((typeof args.user) == 'string') { settings.username = args.user; }
if ((typeof args.pass) == 'string') { settings.password = args.pass; } if ((typeof args.pass) == 'string') { settings.password = args.pass; }
if ((typeof args.host) == 'string') { settings.hostname = args.host; } if ((typeof args.host) == 'string') { settings.hostname = args.host; }
if ((typeof args.hostname) == 'string') { settings.hostname = args.hostname; } if ((typeof args.hostname) == 'string') { settings.hostname = args.hostname; }
if ((typeof args.serverid) == 'string') { settings.serverId = args.serverid; } if ((typeof args.serverid) == 'string') { settings.serverid = args.serverid; }
if ((typeof args.serverhttpshash) == 'string') { settings.serverHttpsHash = args.serverhttpshash; } if ((typeof args.serverhttpshash) == 'string') { settings.serverhttpshash = args.serverhttpshash; }
if ((typeof args.remoteport) == 'string') { settings.remotePort = parseInt(args.remoteport); } if ((typeof args.remoteport) == 'string') { settings.remoteport = parseInt(args.remoteport); }
if ((typeof args.out) == 'string') { settings.output = args.out; } if ((typeof args.out) == 'string') { settings.output = args.out; }
if ((typeof args.output) == 'string') { settings.output = args.output; } if ((typeof args.output) == 'string') { settings.output = args.output; }
if ((typeof args.debug) == 'string') { settings.debugLevel = parseInt(args.debug); } if ((typeof args.debug) == 'string') { settings.debuglevel = parseInt(args.debug); }
if ((typeof args.script) == 'string') { settings.script = args.script; } if ((typeof args.script) == 'string') { settings.script = args.script; }
if ((typeof args.agent) == 'string') { settings.agent = args.agent; } if ((typeof args.agent) == 'string') { settings.agent = args.agent; }
if ((typeof args.proxy) == 'string') { settings.proxy = args.proxy; }
if (args.debug) { try { waitForDebugger(); } catch (e) { } } if (args.debug) { try { waitForDebugger(); } catch (e) { } }
if (args.noconsole) { settings.noconsole = true; } if (args.noconsole) { settings.noconsole = true; }
if (args.nocommander) { settings.noconsole = true; } if (args.nocommander) { settings.noconsole = true; }
@ -263,6 +265,15 @@ function run(argv) {
} }
settings.action = settings.action.toLowerCase(); settings.action = settings.action.toLowerCase();
debug(1, "Settings: " + JSON.stringify(settings)); debug(1, "Settings: " + JSON.stringify(settings));
// Setup the proxy if needed
if ((typeof settings.proxy) == 'string') {
var proxy = settings.proxy.split(':'), proxyport = (proxy.length == 2) ? parseInt(proxy[1]) : 0;
if ((proxy.length != 2) || (proxy[0].length < 1) || (proxyport < 1) || (proxyport > 65535)) { console.log('Invalid \"proxy\" specified, use --proxy [hostname]:[port].'); exit(1); return; }
try { require('global-tunnel').initialize({ host: proxy[0], port: proxyport }); } catch (ex) { console.log(ex); exit(1); return; }
console.log('Proxy set to ' + proxy[0] + ':' + proxyport);
}
if (settings.action == 'smbios') { if (settings.action == 'smbios') {
// Display SM BIOS tables in raw form // Display SM BIOS tables in raw form
SMBiosTables = require('smbios'); SMBiosTables = require('smbios');
@ -283,14 +294,14 @@ function run(argv) {
}); });
} else if (settings.action == 'route') { } else if (settings.action == 'route') {
// MeshCentral Router, port map local TCP port to a remote computer // MeshCentral Router, port map local TCP port to a remote computer
if ((settings.localPort == null) || (typeof settings.localPort != 'number') || (settings.localPort < 0) || (settings.localPort > 65535)) { console.log('No or invalid \"localPort\" specified, use --localport [localport].'); exit(1); return; } if ((settings.localport == null) || (typeof settings.localport != 'number') || (settings.localport < 0) || (settings.localport > 65535)) { console.log('No or invalid \"localPort\" specified, use --localport [localport].'); exit(1); return; }
if ((settings.remoteNodeId == null) || (typeof settings.remoteNodeId != 'string')) { console.log('No or invalid \"remoteNodeId\" specified.'); exit(1); return; } if ((settings.remotenodeid == null) || (typeof settings.remotenodeid != 'string')) { console.log('No or invalid \"remoteNodeId\" specified.'); exit(1); return; }
if ((settings.username == null) || (typeof settings.username != 'string') || (settings.username == '')) { console.log('No or invalid \"username\" specified, use --username [username].'); exit(1); return; } if ((settings.username == null) || (typeof settings.username != 'string') || (settings.username == '')) { console.log('No or invalid \"username\" specified, use --username [username].'); exit(1); return; }
if ((settings.password == null) || (typeof settings.password != 'string') || (settings.password == '')) { console.log('No or invalid \"password\" specified, use --password [password].'); exit(1); return; } if ((settings.password == null) || (typeof settings.password != 'string') || (settings.password == '')) { console.log('No or invalid \"password\" specified, use --password [password].'); exit(1); return; }
if ((settings.serverId == null) || (typeof settings.serverId != 'string') || (settings.serverId.length != 96)) { console.log('No or invalid \"serverId\" specified.'); exit(1); return; } if ((settings.serverid == null) || (typeof settings.serverid != 'string') || (settings.serverid.length != 96)) { console.log('No or invalid \"serverId\" specified.'); exit(1); return; }
if ((settings.serverHttpsHash == null) || (typeof settings.serverHttpsHash != 'string') || (settings.serverHttpsHash.length != 96)) { console.log('No or invalid \"serverHttpsHash\" specified.'); exit(1); return; } if ((settings.serverhttpshash == null) || (typeof settings.serverhttpshash != 'string') || (settings.serverhttpshash.length != 96)) { console.log('No or invalid \"serverHttpsHash\" specified.'); exit(1); return; }
if ((settings.remotePort == null) || (typeof settings.remotePort != 'number') || (settings.remotePort < 0) || (settings.remotePort > 65535)) { console.log('No or invalid \"remotePort\" specified, use --remoteport [remoteport].'); exit(1); return; } if ((settings.remoteport == null) || (typeof settings.remoteport != 'number') || (settings.remoteport < 0) || (settings.remoteport > 65535)) { console.log('No or invalid \"remotePort\" specified, use --remoteport [remoteport].'); exit(1); return; }
if (settings.serverUrl != null) { startRouter(); } else { discoverMeshServer(); } // Start MeshCentral Router if (settings.serverurl != null) { startRouter(); } else { discoverMeshServer(); } // Start MeshCentral Router
} else if ((settings.action == 'amtloadwebapp') || (settings.action == 'amtloadsmallwebapp') || (settings.action == 'amtloadlargewebapp') || (settings.action == 'amtclearwebapp') || (settings.action == 'amtstoragestate')) { // Intel AMT Web Application Actions } else if ((settings.action == 'amtloadwebapp') || (settings.action == 'amtloadsmallwebapp') || (settings.action == 'amtloadlargewebapp') || (settings.action == 'amtclearwebapp') || (settings.action == 'amtstoragestate')) { // Intel AMT Web Application Actions
// Intel AMT 11.6+ Load MeshCommander into firmware // Intel AMT 11.6+ Load MeshCommander into firmware
if ((settings.password == null) || (typeof settings.password != 'string') || (settings.password == '')) { console.log('No or invalid \"password\" specified, use --password [password].'); exit(1); return; } if ((settings.password == null) || (typeof settings.password != 'string') || (settings.password == '')) { console.log('No or invalid \"password\" specified, use --password [password].'); exit(1); return; }
@ -417,7 +428,7 @@ function run(argv) {
if ((settings.password == null) || (typeof settings.password != 'string') || (settings.password == '')) { console.log('No or invalid \"password\" specified, use --password [password].'); exit(1); return; } if ((settings.password == null) || (typeof settings.password != 'string') || (settings.password == '')) { console.log('No or invalid \"password\" specified, use --password [password].'); exit(1); return; }
if ((settings.hostname == null) || (typeof settings.hostname != 'string') || (settings.hostname == '')) { settings.hostname = '127.0.0.1'; } if ((settings.hostname == null) || (typeof settings.hostname != 'string') || (settings.hostname == '')) { settings.hostname = '127.0.0.1'; }
if ((settings.username == null) || (typeof settings.username != 'string') || (settings.username == '')) { settings.username = 'admin'; } if ((settings.username == null) || (typeof settings.username != 'string') || (settings.username == '')) { settings.username = 'admin'; }
if ((settings.script == null) || (typeof settings.script != 'string') || (settings.script == '')) { if (mescriptJSON != '') { settings.scriptJSON = mescriptJSON; } else { console.log('No or invalid \"script\" file specified, use --script [filename].'); exit(1); return; } } if ((settings.script == null) || (typeof settings.script != 'string') || (settings.script == '')) { if (mescriptJSON != '') { settings.scriptjson = mescriptJSON; } else { console.log('No or invalid \"script\" file specified, use --script [filename].'); exit(1); return; } }
startMeScript(); startMeScript();
} else if (settings.action == 'amtuuid') { } else if (settings.action == 'amtuuid') {
// Start running // Start running
@ -751,7 +762,7 @@ function startMeScriptEx() {
if (settings.script != null) { if (settings.script != null) {
try { scriptData = fs.readFileSync(settings.script); } catch (e) { console.log('Unable to read script file (1): ' + settings.script + '.'); exit(1); return; } try { scriptData = fs.readFileSync(settings.script); } catch (e) { console.log('Unable to read script file (1): ' + settings.script + '.'); exit(1); return; }
} else { } else {
scriptData = settings.scriptJSON; scriptData = settings.scriptjson;
} }
if (scriptData == null) { console.log('Unable to read script file (2): ' + settings.script + '.'); exit(1); return; } if (scriptData == null) { console.log('Unable to read script file (2): ' + settings.script + '.'); exit(1); return; }
try { scriptData = JSON.parse(scriptData); } catch (e) { console.log('Unable to read script file (3): ' + settings.script + '.'); exit(1); return; } try { scriptData = JSON.parse(scriptData); } catch (e) { console.log('Unable to read script file (3): ' + settings.script + '.'); exit(1); return; }
@ -1362,12 +1373,12 @@ function processLmsControlData(data) {
function startRouter() { function startRouter() {
tcpserver = net.createServer(OnTcpClientConnected); tcpserver = net.createServer(OnTcpClientConnected);
tcpserver.on('error', function (e) { console.log('ERROR: ' + JSON.stringify(e)); exit(0); return; }); tcpserver.on('error', function (e) { console.log('ERROR: ' + JSON.stringify(e)); exit(0); return; });
tcpserver.listen(settings.localPort, function () { tcpserver.listen(settings.localport, function () {
// We started listening. // We started listening.
if (settings.remoteName == null) { if (settings.remotename == null) {
console.log('Redirecting local port ' + settings.localPort + ' to remote port ' + settings.remotePort + '.'); console.log('Redirecting local port ' + settings.localport + ' to remote port ' + settings.remoteport + '.');
} else { } else {
console.log('Redirecting local port ' + settings.localPort + ' to ' + settings.remoteName + ':' + settings.remotePort + '.'); console.log('Redirecting local port ' + settings.localport + ' to ' + settings.remotename + ':' + settings.remoteport + '.');
} }
console.log('Press ctrl-c to exit.'); console.log('Press ctrl-c to exit.');
@ -1384,7 +1395,7 @@ function OnTcpClientConnected(c) {
c.on('end', function () { disconnectTunnel(this, this.websocket, 'Client closed'); }); c.on('end', function () { disconnectTunnel(this, this.websocket, 'Client closed'); });
c.pause(); c.pause();
try { try {
options = http.parseUri(settings.serverUrl + '?user=' + settings.username + '&pass=' + settings.password + '&nodeid=' + settings.remoteNodeId + '&tcpport=' + settings.remotePort); options = http.parseUri(settings.serverurl + '?user=' + settings.username + '&pass=' + settings.password + '&nodeid=' + settings.remotenodeid + '&tcpport=' + settings.remoteport);
} catch (e) { console.log('Unable to parse \"serverUrl\".'); process.exit(1); return; } } catch (e) { console.log('Unable to parse \"serverUrl\".'); process.exit(1); return; }
options.checkServerIdentity = onVerifyServer; options.checkServerIdentity = onVerifyServer;
options.rejectUnauthorized = false; options.rejectUnauthorized = false;
@ -1440,7 +1451,7 @@ function discoverMeshServerOnce() {
multicastSockets[i].addMembership(membershipIPv4); multicastSockets[i].addMembership(membershipIPv4);
//multicastSockets[i].setMulticastLoopback(true); //multicastSockets[i].setMulticastLoopback(true);
multicastSockets[i].once('message', OnMulticastMessage); multicastSockets[i].once('message', OnMulticastMessage);
multicastSockets[i].send(settings.serverId, 16989, membershipIPv4); multicastSockets[i].send(settings.serverid, 16989, membershipIPv4);
} catch (e) { } } catch (e) { }
} }
} }
@ -1451,9 +1462,9 @@ function discoverMeshServerOnce() {
// Called when a multicast packet is received // Called when a multicast packet is received
function OnMulticastMessage(msg, rinfo) { function OnMulticastMessage(msg, rinfo) {
var m = msg.toString().split('|'); var m = msg.toString().split('|');
if ((m.length == 3) && (m[0] == 'MeshCentral2') && (m[1] == settings.serverId)) { if ((m.length == 3) && (m[0] == 'MeshCentral2') && (m[1] == settings.serverid)) {
settings.serverUrl = m[2].replace('%s', rinfo.address).replace('/agent.ashx', '/meshrelay.ashx'); settings.serverurl = m[2].replace('%s', rinfo.address).replace('/agent.ashx', '/meshrelay.ashx');
console.log('Found server at ' + settings.serverUrl + '.'); console.log('Found server at ' + settings.serverurl + '.');
if (discoveryInterval != null) { clearInterval(discoveryInterval); discoveryInterval = null; } if (discoveryInterval != null) { clearInterval(discoveryInterval); discoveryInterval = null; }
startRouter(); startRouter();
} }

File diff suppressed because one or more lines are too long

View File

@ -21,6 +21,7 @@ start() {
fi fi
echo 'Starting service…' >&2 echo 'Starting service…' >&2
local CMD="$SCRIPT &> \"$LOGFILE\" & echo \$!" local CMD="$SCRIPT &> \"$LOGFILE\" & echo \$!"
cd /usr/local/mesh
su -c "$CMD" $RUNAS > "$PIDFILE" su -c "$CMD" $RUNAS > "$PIDFILE"
echo 'Service started' >&2 echo 'Service started' >&2
} }

View File

@ -121,65 +121,68 @@ function _getChildElementsByTagName(name) { var ret = []; if (this.childNodes !=
function _getChildElementsByTagNameNS(ns, name) { var ret = []; if (this.childNodes != null) { for (var node in this.childNodes) { if (this.childNodes[node].localName == name && (ns == '*' || this.childNodes[node].namespace == ns)) { ret.push(this.childNodes[node]); } } } return (ret); } function _getChildElementsByTagNameNS(ns, name) { var ret = []; if (this.childNodes != null) { for (var node in this.childNodes) { if (this.childNodes[node].localName == name && (ns == '*' || this.childNodes[node].namespace == ns)) { ret.push(this.childNodes[node]); } } } return (ret); }
function _xmlTraverseAllRec(nodes, func) { for (var i in nodes) { func(nodes[i]); if (nodes[i].childNodes) { _xmlTraverseAllRec(nodes[i].childNodes, func); } } } function _xmlTraverseAllRec(nodes, func) { for (var i in nodes) { func(nodes[i]); if (nodes[i].childNodes) { _xmlTraverseAllRec(nodes[i].childNodes, func); } } }
function _turnToXmlRec(text) { function _turnToXmlRec(text) {
var elementStack = new _treeBuilder(), lastElement = null, x1 = text.split('<'), ret = [], element = null, currentElementName = null; try {
for (var i in x1) { if (text == null) return null;
var x2 = x1[i].split('>'), x3 = x2[0].split(' '), elementName = x3[0]; var elementStack = new _treeBuilder(), lastElement = null, x1 = text.split('<'), ret = [], element = null, currentElementName = null;
if ((elementName.length > 0) && (elementName[0] != '?')) { for (var i in x1) {
if (elementName[0] != '/') { var x2 = x1[i].split('>'), x3 = x2[0].split(' '), elementName = x3[0];
var attributes = [], localName, localname2 = elementName.split(' ')[0].split(':'), localName = (localname2.length > 1) ? localname2[1] : localname2[0]; if ((elementName.length > 0) && (elementName[0] != '?')) {
Object.defineProperty(attributes, "get", if (elementName[0] != '/') {
{ var attributes = [], localName, localname2 = elementName.split(' ')[0].split(':'), localName = (localname2.length > 1) ? localname2[1] : localname2[0];
value: function () { Object.defineProperty(attributes, "get",
if (arguments.length == 1) { {
for (var a in this) { if (this[a].name == arguments[0]) { return (this[a]); } } value: function () {
} if (arguments.length == 1) {
else if (arguments.length == 2) { for (var a in this) { if (this[a].name == arguments[0]) { return (this[a]); } }
for (var a in this) { if (this[a].name == arguments[1] && (arguments[0] == '*' || this[a].namespace == arguments[0])) { return (this[a]); } } }
} else if (arguments.length == 2) {
else { for (var a in this) { if (this[a].name == arguments[1] && (arguments[0] == '*' || this[a].namespace == arguments[0])) { return (this[a]); } }
throw ('attributes.get(): Invalid number of parameters'); }
} else {
} throw ('attributes.get(): Invalid number of parameters');
}); }
elementStack.push({ name: elementName, localName: localName, getChildElementsByTagName: _getChildElementsByTagName, getElementsByTagNameNS: _getElementsByTagNameNS, getChildElementsByTagNameNS: _getChildElementsByTagNameNS, attributes: attributes, childNodes: [], nsTable: {} }); }
// Parse Attributes });
if (x3.length > 0) { elementStack.push({ name: elementName, localName: localName, getChildElementsByTagName: _getChildElementsByTagName, getElementsByTagNameNS: _getElementsByTagNameNS, getChildElementsByTagNameNS: _getChildElementsByTagNameNS, attributes: attributes, childNodes: [], nsTable: {} });
var skip = false; // Parse Attributes
for (var j in x3) { if (x3.length > 0) {
if (x3[j] == '/') { var skip = false;
// This is an empty Element for (var j in x3) {
elementStack.peek().namespace = elementStack.peek().name == elementStack.peek().localName ? elementStack.getNamespace('*') : elementStack.getNamespace(elementStack.peek().name.substring(0, elementStack.peek().name.indexOf(':'))); if (x3[j] == '/') {
elementStack.peek().textContent = ''; // This is an empty Element
lastElement = elementStack.pop(); elementStack.peek().namespace = elementStack.peek().name == elementStack.peek().localName ? elementStack.getNamespace('*') : elementStack.getNamespace(elementStack.peek().name.substring(0, elementStack.peek().name.indexOf(':')));
skip = true; elementStack.peek().textContent = '';
break; lastElement = elementStack.pop();
} skip = true;
var k = x3[j].indexOf('='); break;
if (k > 0) { }
var attrName = x3[j].substring(0, k); var k = x3[j].indexOf('=');
var attrValue = x3[j].substring(k + 2, x3[j].length - 1); if (k > 0) {
var attrNS = elementStack.getNamespace('*'); var attrName = x3[j].substring(0, k);
var attrValue = x3[j].substring(k + 2, x3[j].length - 1);
if (attrName == 'xmlns') { var attrNS = elementStack.getNamespace('*');
elementStack.addNamespace('*', attrValue);
attrNS = attrValue; if (attrName == 'xmlns') {
} else if (attrName.startsWith('xmlns:')) { elementStack.addNamespace('*', attrValue);
elementStack.addNamespace(attrName.substring(6), attrValue); attrNS = attrValue;
} else { } else if (attrName.startsWith('xmlns:')) {
var ax = attrName.split(':'); elementStack.addNamespace(attrName.substring(6), attrValue);
if (ax.length == 2) { attrName = ax[1]; attrNS = elementStack.getNamespace(ax[0]); } } else {
var ax = attrName.split(':');
if (ax.length == 2) { attrName = ax[1]; attrNS = elementStack.getNamespace(ax[0]); }
}
var x = { name: attrName, value: attrValue }
if (attrNS != null) x.namespace = attrNS;
elementStack.peek().attributes.push(x);
} }
var x = { name: attrName, value: attrValue }
if (attrNS != null) x.namespace = attrNS;
elementStack.peek().attributes.push(x);
} }
if (skip) { continue; }
} }
if (skip) { continue; } elementStack.peek().namespace = elementStack.peek().name == elementStack.peek().localName ? elementStack.getNamespace('*') : elementStack.getNamespace(elementStack.peek().name.substring(0, elementStack.peek().name.indexOf(':')));
} if (x2[1]) { elementStack.peek().textContent = x2[1]; }
elementStack.peek().namespace = elementStack.peek().name == elementStack.peek().localName ? elementStack.getNamespace('*') : elementStack.getNamespace(elementStack.peek().name.substring(0, elementStack.peek().name.indexOf(':'))); } else { lastElement = elementStack.pop(); }
if (x2[1]) { elementStack.peek().textContent = x2[1]; } }
} else { lastElement = elementStack.pop(); }
} }
} } catch (ex) { return null; }
return lastElement; return lastElement;
} }

File diff suppressed because one or more lines are too long

View File

@ -492,7 +492,7 @@ function AmtManager(agent, db, isdebug) {
var intelAmtAdminPass, wsstack, amtstack, applyPolicyTimer, policyWsmanRetry = 0; var intelAmtAdminPass, wsstack, amtstack, applyPolicyTimer, policyWsmanRetry = 0;
obj.applyPolicy = function () { obj.applyPolicy = function () {
applyPolicyTimer = null; applyPolicyTimer = null;
if ((amtMeiState != 3) || (typeof amtpolicy != 'object') || (typeof amtpolicy.type != 'number') || (amtpolicy.type == 0)) return; if ((amtMeiState != 3) || (amtpolicy == null) || (typeof amtpolicy != 'object') || (typeof amtpolicy.type != 'number') || (amtpolicy.type == 0)) return;
if ((amtpolicy.password != null) && (amtpolicy.password != '')) { intelAmtAdminPass = amtpolicy.password; } if ((amtpolicy.password != null) && (amtpolicy.password != '')) { intelAmtAdminPass = amtpolicy.password; }
obj.getAmtInfo(function (meinfo) { obj.getAmtInfo(function (meinfo) {
if ((amtpolicy.type == 1) && (meinfo.ProvisioningState == 2) && ((meinfo.Flags & 2) != 0)) { if ((amtpolicy.type == 1) && (meinfo.ProvisioningState == 2) && ((meinfo.Flags & 2) != 0)) {

View File

@ -121,6 +121,7 @@ function _getChildElementsByTagName(name) { var ret = []; if (this.childNodes !=
function _getChildElementsByTagNameNS(ns, name) { var ret = []; if (this.childNodes != null) { for (var node in this.childNodes) { if (this.childNodes[node].localName == name && (ns == '*' || this.childNodes[node].namespace == ns)) { ret.push(this.childNodes[node]); } } } return (ret); } function _getChildElementsByTagNameNS(ns, name) { var ret = []; if (this.childNodes != null) { for (var node in this.childNodes) { if (this.childNodes[node].localName == name && (ns == '*' || this.childNodes[node].namespace == ns)) { ret.push(this.childNodes[node]); } } } return (ret); }
function _xmlTraverseAllRec(nodes, func) { for (var i in nodes) { func(nodes[i]); if (nodes[i].childNodes) { _xmlTraverseAllRec(nodes[i].childNodes, func); } } } function _xmlTraverseAllRec(nodes, func) { for (var i in nodes) { func(nodes[i]); if (nodes[i].childNodes) { _xmlTraverseAllRec(nodes[i].childNodes, func); } } }
function _turnToXmlRec(text) { function _turnToXmlRec(text) {
if (text == null) return null;
var elementStack = new _treeBuilder(), lastElement = null, x1 = text.split('<'), ret = [], element = null, currentElementName = null; var elementStack = new _treeBuilder(), lastElement = null, x1 = text.split('<'), ret = [], element = null, currentElementName = null;
for (var i in x1) { for (var i in x1) {
var x2 = x1[i].split('>'), x3 = x2[0].split(' '), elementName = x3[0]; var x2 = x1[i].split('>'), x3 = x2[0].split(' '), elementName = x3[0];

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

38
db.js
View File

@ -30,6 +30,7 @@ module.exports.CreateDB = function (parent) {
var Datastore = null; var Datastore = null;
var expireEventsSeconds = (60 * 60 * 24 * 20); // By default, expire events after 20 days. (Seconds * Minutes * Hours * Days) var expireEventsSeconds = (60 * 60 * 24 * 20); // By default, expire events after 20 days. (Seconds * Minutes * Hours * Days)
var expirePowerEventsSeconds = (60 * 60 * 24 * 10); // By default, expire power events after 10 days. (Seconds * Minutes * Hours * Days) var expirePowerEventsSeconds = (60 * 60 * 24 * 10); // By default, expire power events after 10 days. (Seconds * Minutes * Hours * Days)
var expireServerStatsSeconds = (60 * 60 * 24 * 30); // By default, expire power events after 30 days. (Seconds * Minutes * Hours * Days)
obj.path = require('path'); obj.path = require('path');
obj.parent = parent; obj.parent = parent;
obj.identifier = null; obj.identifier = null;
@ -39,6 +40,7 @@ module.exports.CreateDB = function (parent) {
if (typeof obj.parent.args.dbexpire == 'object') { if (typeof obj.parent.args.dbexpire == 'object') {
if (typeof obj.parent.args.dbexpire.events == 'number') { expireEventsSeconds = obj.parent.args.dbexpire.events; } if (typeof obj.parent.args.dbexpire.events == 'number') { expireEventsSeconds = obj.parent.args.dbexpire.events; }
if (typeof obj.parent.args.dbexpire.powerevents == 'number') { expirePowerEventsSeconds = obj.parent.args.dbexpire.powerevents; } if (typeof obj.parent.args.dbexpire.powerevents == 'number') { expirePowerEventsSeconds = obj.parent.args.dbexpire.powerevents; }
if (typeof obj.parent.args.dbexpire.statsevents == 'number') { expireServerStatsSeconds = obj.parent.args.dbexpire.statsevents; }
} }
if (obj.parent.args.mongodb) { if (obj.parent.args.mongodb) {
@ -115,6 +117,30 @@ module.exports.CreateDB = function (parent) {
// Setup MongoDB smbios collection, no indexes needed // Setup MongoDB smbios collection, no indexes needed
obj.smbiosfile = db.collection('smbios'); // Collection containing all smbios information obj.smbiosfile = db.collection('smbios'); // Collection containing all smbios information
// Setup MongoDB server stats collection
obj.serverstatsfile = db.collection('serverstats'); // Collection of server stats
obj.serverstatsfile.getIndexes(function (err, indexes) {
// Check if we need to reset indexes
var indexesByName = {}, indexCount = 0;
for (var i in indexes) { indexesByName[indexes[i].name] = indexes[i]; indexCount++; }
if ((indexCount != 3) || (indexesByName['ExpireTime1'] == null)) {
// Reset all indexes
console.log('Resetting server stats indexes...');
obj.serverstatsfile.dropIndexes(function (err) {
// Create all indexes
obj.serverstatsfile.createIndex({ "time": 1 }, { expireAfterSeconds: expireServerStatsSeconds, name: 'ExpireTime1' });
obj.serverstatsfile.createIndex({ "expire": 1 }, { expireAfterSeconds: 0, name: 'ExpireTime2' }); // Auto-expire events
});
} else if (indexesByName['ExpireTime1'].expireAfterSeconds != expireServerStatsSeconds) {
// Reset the timeout index
console.log('Resetting server stats expire index...');
obj.serverstatsfile.dropIndex("ExpireTime1", function (err) {
// Reset the expire server stats index
obj.serverstatsfile.createIndex({ "time": 1 }, { expireAfterSeconds: expireServerStatsSeconds, name: 'ExpireTime1' });
});
}
});
} else { } else {
// Use NeDB (The default) // Use NeDB (The default)
obj.databaseType = 1; obj.databaseType = 1;
@ -167,6 +193,12 @@ module.exports.CreateDB = function (parent) {
// Setup the SMBIOS collection // Setup the SMBIOS collection
obj.smbiosfile = new Datastore({ filename: obj.parent.getConfigFilePath('meshcentral-smbios.db'), autoload: true }); obj.smbiosfile = new Datastore({ filename: obj.parent.getConfigFilePath('meshcentral-smbios.db'), autoload: true });
// Setup the server stats collection and setup indexes
obj.serverstatsfile = new Datastore({ filename: obj.parent.getConfigFilePath('meshcentral-stats.db'), autoload: true });
obj.serverstatsfile.persistence.setAutocompactionInterval(36000);
obj.serverstatsfile.ensureIndex({ fieldName: 'time', expireAfterSeconds: 60 * 60 * 24 * 30 }); // Limit the server stats log to 30 days (Seconds * Minutes * Hours * Days)
obj.serverstatsfile.ensureIndex({ fieldName: 'expire', expireAfterSeconds: 0 }); // Auto-expire events
} }
obj.SetupDatabase = function (func) { obj.SetupDatabase = function (func) {
@ -305,6 +337,10 @@ module.exports.CreateDB = function (parent) {
obj.RemoveSMBIOS = function (id) { obj.smbiosfile.remove({ _id: id }); }; obj.RemoveSMBIOS = function (id) { obj.smbiosfile.remove({ _id: id }); };
obj.GetSMBIOS = function (id, func) { obj.smbiosfile.find({ _id: id }, func); }; obj.GetSMBIOS = function (id, func) { obj.smbiosfile.find({ _id: id }, func); };
// Database actions on the Server Stats collection
obj.SetServerStats = function (data, func) { obj.serverstatsfile.insert(data, func); };
obj.GetServerStats = function (hours, func) { var t = new Date(); t.setTime(t.getTime() - (60 * 60 * 1000 * hours)); obj.serverstatsfile.find({ time: { $gt: t } }, { _id: 0, cpu: 0 }, func); };
// Read a configuration file from the database // Read a configuration file from the database
obj.getConfigFile = function (path, func) { obj.Get('cfile/' + path, func); } obj.getConfigFile = function (path, func) { obj.Get('cfile/' + path, func); }
@ -376,7 +412,7 @@ module.exports.CreateDB = function (parent) {
obj.file.count({ type: 'mesh' }, function (err, meshCount) { obj.file.count({ type: 'mesh' }, function (err, meshCount) {
obj.file.count({ type: 'user' }, function (err, userCount) { obj.file.count({ type: 'user' }, function (err, userCount) {
obj.file.count({}, function (err, totalCount) { obj.file.count({}, function (err, totalCount) {
func({ nodes: nodeCount, meshes: meshCount, powerEvents: powerCount, users: userCount, nodeInterfaces: nodeInterfaceCount, notes: noteCount, connectEvent: nodeLastConnectCount, smbios: nodeSmbiosCount, total: totalCount }); func({ nodes: nodeCount, meshes: meshCount, users: userCount, total: totalCount });
}); });
}); });
}); });

View File

@ -60,6 +60,7 @@ function CreateMeshCentralServer(config, args) {
obj.serverKey = Buffer.from(obj.crypto.randomBytes(48), 'binary'); obj.serverKey = Buffer.from(obj.crypto.randomBytes(48), 'binary');
obj.loginCookieEncryptionKey = null; obj.loginCookieEncryptionKey = null;
obj.serverSelfWriteAllowed = true; obj.serverSelfWriteAllowed = true;
obj.serverStatsCounter = Math.floor(Math.random() * 1000);
obj.taskLimiter = obj.common.createTaskLimiterQueue(50, 20, 60); // (maxTasks, maxTaskTime, cleaningInterval) This is a task limiter queue to smooth out server work. obj.taskLimiter = obj.common.createTaskLimiterQueue(50, 20, 60); // (maxTasks, maxTaskTime, cleaningInterval) This is a task limiter queue to smooth out server work.
try { obj.currentVer = JSON.parse(obj.fs.readFileSync(obj.path.join(__dirname, 'package.json'), 'utf8')).version; } catch (e) { } // Fetch server version try { obj.currentVer = JSON.parse(obj.fs.readFileSync(obj.path.join(__dirname, 'package.json'), 'utf8')).version; } catch (e) { } // Fetch server version
@ -800,6 +801,35 @@ function CreateMeshCentralServer(config, args) {
}); });
} }
// Start collecting server stats every 5 minutes
setInterval(function () {
obj.serverStatsCounter++;
var hours = 720; // Start with all events lasting 30 days.
if (((obj.serverStatsCounter) % 2) == 1) { hours = 3; } // Half of the event get removed after 3 hours.
else if ((Math.floor(obj.serverStatsCounter / 2) % 2) == 1) { hours = 8; } // Another half of the event get removed after 8 hours.
else if ((Math.floor(obj.serverStatsCounter / 4) % 2) == 1) { hours = 24; } // Another half of the event get removed after 24 hours.
else if ((Math.floor(obj.serverStatsCounter / 8) % 2) == 1) { hours = 48; } // Another half of the event get removed after 48 hours.
else if ((Math.floor(obj.serverStatsCounter / 16) % 2) == 1) { hours = 72; } // Another half of the event get removed after 72 hours.
var expire = new Date();
expire.setTime(expire.getTime() + (60 * 60 * 1000 * hours));
var data = {
time: new Date(),
expire: expire,
mem: process.memoryUsage(),
//cpu: process.cpuUsage(),
conn: {
ca: Object.keys(obj.webserver.wsagents).length,
cu: Object.keys(obj.webserver.wssessions).length,
us: Object.keys(obj.webserver.wssessions2).length,
rs: obj.webserver.relaySessionCount
}
};
if (obj.mpsserver != null) { data.conn.am = Object.keys(obj.mpsserver.ciraConnections).length; }
obj.db.SetServerStats(data); // Save the stats to the database
obj.DispatchEvent(['*'], obj, { action: 'servertimelinestats', data: data }); // Event the server stats
}, 300000);
//obj.debug(1, 'Server started'); //obj.debug(1, 'Server started');
if (obj.args.nousers == true) { obj.updateServerState('nousers', '1'); } if (obj.args.nousers == true) { obj.updateServerState('nousers', '1'); }
obj.updateServerState('state', 'running'); obj.updateServerState('state', 'running');
@ -1598,31 +1628,34 @@ function getConfig(createSampleConfig) {
function InstallModules(modules, func) { function InstallModules(modules, func) {
var missingModules = []; var missingModules = [];
if (modules.length > 0) { if (modules.length > 0) {
for (var i in modules) { try { var xxmodule = require(modules[i]); } catch (e) { missingModules.push(modules[i]); } } for (var i in modules) {
if (missingModules.length > 0) { InstallModule(missingModules.join(' '), InstallModules, modules, func); } else { func(); } try {
var xxmodule = require(modules[i]);
} catch (e) {
if (previouslyInstalledModules[modules[i]] !== true) { previouslyInstalledModules[modules[i]] = true; missingModules.push(modules[i]); }
}
}
if (missingModules.length > 0) { InstallModule(missingModules.shift(), InstallModules, modules, func); } else { func(); }
} }
} }
// Check if a module is present and install it if missing // Check if a module is present and install it if missing
var InstallModuleChildProcess = null; var InstallModuleChildProcess = null;
function InstallModule(modulename, func, tag1, tag2) { function InstallModule(modulename, func, tag1, tag2) {
try { console.log('Installing ' + modulename + '...');
var module = require(modulename); var child_process = require('child_process');
} catch (e) {
console.log('Installing ' + modulename + '...');
var child_process = require('child_process');
// Looks like we need to keep a global reference to the child process object for this to work correctly. // Looks like we need to keep a global reference to the child process object for this to work correctly.
InstallModuleChildProcess = child_process.exec('npm install ' + modulename + ' --no-optional --save', { maxBuffer: 512000, timeout: 10000 }, function (error, stdout, stderr) { InstallModuleChildProcess = child_process.exec('npm install --no-optional --save ' + modulename, { maxBuffer: 512000, timeout: 10000 }, function (error, stdout, stderr) {
InstallModuleChildProcess = null; InstallModuleChildProcess = null;
if (error != null) { console.log('ERROR: Unable to install missing package \'' + modulename + '\', make sure npm is installed: ' + error); process.exit(); return; } if (error != null) {
func(tag1, tag2); console.log('ERROR: Unable to install required module "' + modulename + '". MeshCentral may not have access to npm, or npm may not have suffisent rights to load the new module. Try "npm install ' + modulename + '" to manualy install this module.\r\n');
process.exit();
return; return;
}); }
func(tag1, tag2);
return; return;
} });
func(tag1, tag2);
} }
// Detect CTRL-C on Linux and stop nicely // Detect CTRL-C on Linux and stop nicely
@ -1630,6 +1663,7 @@ process.on('SIGINT', function () { if (meshserver != null) { meshserver.Stop();
// Load the really basic modules // Load the really basic modules
var meshserver = null; var meshserver = null;
var previouslyInstalledModules = { };
function mainStart(args) { function mainStart(args) {
// Check the NodeJS is version 6 or better. // Check the NodeJS is version 6 or better.
if (Number(process.version.match(/^v(\d+\.\d+)/)[1]) < 6) { console.log("MeshCentral requires Node v6.x or above, current version is " + process.version + "."); return; } if (Number(process.version.match(/^v(\d+\.\d+)/)[1]) < 6) { console.log("MeshCentral requires Node v6.x or above, current version is " + process.version + "."); return; }
@ -1640,10 +1674,12 @@ function mainStart(args) {
var config = getConfig(false); var config = getConfig(false);
if (config == null) { process.exit(); } if (config == null) { process.exit(); }
// Check is Windows SSPI will be used // Check is Windows SSPI and YubiKey OTP will be used
var sspi = false; var sspi = false;
var allsspi = true; var allsspi = true;
if (require('os').platform() == 'win32') { for (var i in config.domains) { if (config.domains[i].auth == 'sspi') { sspi = true; } else { allsspi = 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; }
for (var i in config.domains) { if (config.domains[i].yubikey != null) { yubikey = 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'];
@ -1658,8 +1694,13 @@ function mainStart(args) {
// If running NodeJS < 8, install "util.promisify" // If running NodeJS < 8, install "util.promisify"
if (nodeVersion < 8) { modules.push('util.promisify'); } if (nodeVersion < 8) { modules.push('util.promisify'); }
// if running NodeJS 8 or higher, we can install WebAuthn/FIDO2 support // Setup 2nd factor authentication
if ((nodeVersion >= 8) && (allsspi == false)) { modules.push('@davedoesdev/fido2-lib'); } if (config.settings.no2factorauth !== true) {
// Setup YubiKey OTP if configured
if (yubikey == true) { modules.push('yubikeyotp'); } // Add YubiKey OTP support
// if not all SSPI, WebAuthn/FIDO2 or U2F support depending on the NodeJS version. FIDO2 does not work below NodeJS 8.x
if (allsspi == false) { modules.push('otplib'); if (nodeVersion >= 8) { modules.push('@davedoesdev/fido2-lib'); } else { modules.push('authdog'); } }
}
// Install any missing modules and launch the server // Install any missing modules and launch the server
InstallModules(modules, function () { meshserver = CreateMeshCentralServer(config, args); meshserver.Start(); }); InstallModules(modules, function () { meshserver = CreateMeshCentralServer(config, args); meshserver.Start(); });

View File

@ -261,17 +261,25 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use
try { ws.send(JSON.stringify({ action: 'authcookie', cookie: parent.parent.encodeCookie({ userid: user._id, domainid: domain.id }, parent.parent.loginCookieEncryptionKey) })); } catch (ex) { } try { ws.send(JSON.stringify({ action: 'authcookie', cookie: parent.parent.encodeCookie({ userid: user._id, domainid: domain.id }, parent.parent.loginCookieEncryptionKey) })); } catch (ex) { }
break; break;
} }
case 'servertimelinestats':
{
if ((user.siteadmin & 21) == 0) return; // Only site administrators with "site backup" or "site restore" or "site update" permissions can use this.
if (common.validateInt(command.hours, 0, 24 * 30) == false) return;
db.GetServerStats(command.hours, function (err, docs) {
if (err == null) { ws.send(JSON.stringify({ action: 'servertimelinestats', events: docs })); }
});
break;
}
case 'serverstats': case 'serverstats':
{ {
if ((user.siteadmin & 21) != 0) { // Only site administrators with "site backup" or "site restore" or "site update" permissions can use this. if ((user.siteadmin & 21) == 0) return; // Only site administrators with "site backup" or "site restore" or "site update" permissions can use this.
if (common.validateInt(command.interval, 1000, 1000000) == false) { if (common.validateInt(command.interval, 1000, 1000000) == false) {
// Clear the timer // Clear the timer
if (obj.serverStatsTimer != null) { clearInterval(obj.serverStatsTimer); delete obj.serverStatsTimer; } if (obj.serverStatsTimer != null) { clearInterval(obj.serverStatsTimer); delete obj.serverStatsTimer; }
} else { } else {
// Set the timer // Set the timer
obj.SendServerStats(); obj.SendServerStats();
obj.serverStatsTimer = setInterval(obj.SendServerStats, command.interval); obj.serverStatsTimer = setInterval(obj.SendServerStats, command.interval);
}
} }
break; break;
} }
@ -1773,7 +1781,9 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use
const twoStepLoginSupported = ((domain.auth != 'sspi') && (parent.parent.certificates.CommonName.indexOf('.') != -1) && (args.lanonly !== true) && (args.nousers !== true)); const twoStepLoginSupported = ((domain.auth != 'sspi') && (parent.parent.certificates.CommonName.indexOf('.') != -1) && (args.lanonly !== true) && (args.nousers !== true));
if (twoStepLoginSupported) { if (twoStepLoginSupported) {
// Request a one time password to be setup // Request a one time password to be setup
const otplib = require('otplib'); var otplib = null;
try { otplib = require('otplib'); } catch (ex) { }
if (otplib == null) { break; }
const secret = otplib.authenticator.generateSecret(); // TODO: Check the random source of this value. const secret = otplib.authenticator.generateSecret(); // TODO: Check the random source of this value.
ws.send(JSON.stringify({ action: 'otpauth-request', secret: secret, url: otplib.authenticator.keyuri(user.name, parent.certificates.CommonName, secret) })); ws.send(JSON.stringify({ action: 'otpauth-request', secret: secret, url: otplib.authenticator.keyuri(user.name, parent.certificates.CommonName, secret) }));
} }
@ -1785,7 +1795,9 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use
const twoStepLoginSupported = ((domain.auth != 'sspi') && (parent.parent.certificates.CommonName.indexOf('.') != -1) && (args.lanonly !== true) && (args.nousers !== true)); const twoStepLoginSupported = ((domain.auth != 'sspi') && (parent.parent.certificates.CommonName.indexOf('.') != -1) && (args.lanonly !== true) && (args.nousers !== true));
if (twoStepLoginSupported) { if (twoStepLoginSupported) {
// Perform the one time password setup // Perform the one time password setup
const otplib = require('otplib'); var otplib = null;
try { otplib = require('otplib'); } catch (ex) { }
if (otplib == null) { break; }
otplib.authenticator.options = { window: 2 }; // Set +/- 1 minute window otplib.authenticator.options = { window: 2 }; // Set +/- 1 minute window
if (otplib.authenticator.check(command.token, command.secret) === true) { if (otplib.authenticator.check(command.token, command.secret) === true) {
// Token is valid, activate 2-step login on this account. // Token is valid, activate 2-step login on this account.
@ -1853,8 +1865,6 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use
} }
case 'otp-hkey-get': case 'otp-hkey-get':
{ {
// Check is 2-step login is supported // Check is 2-step login is supported
const twoStepLoginSupported = ((domain.auth != 'sspi') && (parent.parent.certificates.CommonName.indexOf('.') != -1) && (args.lanonly !== true) && (args.nousers !== true)); const twoStepLoginSupported = ((domain.auth != 'sspi') && (parent.parent.certificates.CommonName.indexOf('.') != -1) && (args.lanonly !== true) && (args.nousers !== true));
if (twoStepLoginSupported == false) break; if (twoStepLoginSupported == false) break;
@ -1886,11 +1896,15 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use
} }
case 'otp-hkey-yubikey-add': case 'otp-hkey-yubikey-add':
{ {
if (parent.parent.config.settings.no2factorauth === true) return;
// Yubico API id and signature key can be requested from https://upgrade.yubico.com/getapikey/ // Yubico API id and signature key can be requested from https://upgrade.yubico.com/getapikey/
var yubikeyotp = null;
try { yubikeyotp = require('yubikeyotp'); } catch (ex) { }
// Check is 2-step login is supported // Check is 2-step login is supported
const twoStepLoginSupported = ((domain.auth != 'sspi') && (parent.parent.certificates.CommonName.indexOf('.') != -1) && (args.lanonly !== true) && (args.nousers !== true)); const twoStepLoginSupported = ((domain.auth != 'sspi') && (parent.parent.certificates.CommonName.indexOf('.') != -1) && (args.lanonly !== true) && (args.nousers !== true));
if ((twoStepLoginSupported == false) || (typeof command.otp != 'string')) { if ((yubikeyotp == null) || (twoStepLoginSupported == false) || (typeof command.otp != 'string')) {
ws.send(JSON.stringify({ action: 'otp-hkey-yubikey-add', result: false, name: command.name })); ws.send(JSON.stringify({ action: 'otp-hkey-yubikey-add', result: false, name: command.name }));
break; break;
} }
@ -1904,7 +1918,6 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use
// TODO: Check if command.otp is modhex encoded, reject if not. // TODO: Check if command.otp is modhex encoded, reject if not.
// Query the YubiKey server to validate the OTP // Query the YubiKey server to validate the OTP
var yubikeyotp = require('yubikeyotp');
var request = { otp: command.otp, id: domain.yubikey.id, key: domain.yubikey.secret, timestamp: true } var request = { otp: command.otp, id: domain.yubikey.id, key: domain.yubikey.secret, timestamp: true }
if (domain.yubikey.proxy) { request.requestParams = { proxy: domain.yubikey.proxy }; } if (domain.yubikey.proxy) { request.requestParams = { proxy: domain.yubikey.proxy }; }
yubikeyotp.verifyOTP(request, function (err, results) { yubikeyotp.verifyOTP(request, function (err, results) {
@ -1934,16 +1947,21 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use
} }
case 'otp-hkey-setup-request': case 'otp-hkey-setup-request':
{ {
if (parent.parent.config.settings.no2factorauth === true) return;
var authdoglib = null;
try { authdoglib = require('authdog'); } catch (ex) { }
// Check is 2-step login is supported // Check is 2-step login is supported
const twoStepLoginSupported = ((domain.auth != 'sspi') && (parent.parent.certificates.CommonName.indexOf('.') != -1) && (args.lanonly !== true) && (args.nousers !== true)); const twoStepLoginSupported = ((domain.auth != 'sspi') && (parent.parent.certificates.CommonName.indexOf('.') != -1) && (args.lanonly !== true) && (args.nousers !== true));
if (twoStepLoginSupported == false) break; if ((authdoglib == null) || (twoStepLoginSupported == false)) break;
// Build list of known keys // Build list of known keys
var knownKeys = []; var knownKeys = [];
if (user.otphkeys != null) { for (var i = 0; i < user.otphkeys.length; i++) { if (user.otphkeys[i].type == 1) { knownKeys.push(user.otphkeys[i]); } } } if (user.otphkeys != null) { for (var i = 0; i < user.otphkeys.length; i++) { if (user.otphkeys[i].type == 1) { knownKeys.push(user.otphkeys[i]); } } }
// Build a key registration request and send it over // Build a key registration request and send it over
require('authdog').startRegistration('https://' + parent.parent.certificates.CommonName, knownKeys, { requestId: 556, timeoutSeconds: 100 }).then(function (registrationRequest) { authdoglib.startRegistration('https://' + parent.parent.certificates.CommonName, knownKeys, { requestId: 556, timeoutSeconds: 100 }).then(function (registrationRequest) {
// Save registration request to session for later use // Save registration request to session for later use
obj.hardwareKeyRegistrationRequest = registrationRequest; obj.hardwareKeyRegistrationRequest = registrationRequest;
@ -1957,12 +1975,17 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use
} }
case 'otp-hkey-setup-response': case 'otp-hkey-setup-response':
{ {
if (parent.parent.config.settings.no2factorauth === true) return;
var authdoglib = null;
try { authdoglib = require('authdog'); } catch (ex) { }
// Check is 2-step login is supported // Check is 2-step login is supported
const twoStepLoginSupported = ((domain.auth != 'sspi') && (parent.parent.certificates.CommonName.indexOf('.') != -1) && (args.lanonly !== true) && (args.nousers !== true)); const twoStepLoginSupported = ((domain.auth != 'sspi') && (parent.parent.certificates.CommonName.indexOf('.') != -1) && (args.lanonly !== true) && (args.nousers !== true));
if ((twoStepLoginSupported == false) || (command.response == null) || (command.name == null) || (obj.hardwareKeyRegistrationRequest == null)) break; if ((authdoglib == null) || (twoStepLoginSupported == false) || (command.response == null) || (command.name == null) || (obj.hardwareKeyRegistrationRequest == null)) break;
// Check the key registration request // Check the key registration request
require('authdog').finishRegistration(obj.hardwareKeyRegistrationRequest, command.response).then(function (registrationStatus) { authdoglib.finishRegistration(obj.hardwareKeyRegistrationRequest, command.response).then(function (registrationStatus) {
var keyIndex = parent.crypto.randomBytes(4).readUInt32BE(0); var keyIndex = parent.crypto.randomBytes(4).readUInt32BE(0);
ws.send(JSON.stringify({ action: 'otp-hkey-setup-response', result: true, name: command.name, index: keyIndex })); ws.send(JSON.stringify({ action: 'otp-hkey-setup-response', result: true, name: command.name, index: keyIndex }));
if (user.otphkeys == null) { user.otphkeys = []; } if (user.otphkeys == null) { user.otphkeys = []; }
@ -1980,6 +2003,8 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use
} }
case 'webauthn-startregister': case 'webauthn-startregister':
{ {
if (parent.parent.config.settings.no2factorauth === true) return;
// Check is 2-step login is supported // Check is 2-step login is supported
const twoStepLoginSupported = ((domain.auth != 'sspi') && (parent.parent.certificates.CommonName.indexOf('.') != -1) && (args.lanonly !== true) && (args.nousers !== true)); const twoStepLoginSupported = ((domain.auth != 'sspi') && (parent.parent.certificates.CommonName.indexOf('.') != -1) && (args.lanonly !== true) && (args.nousers !== true));
if ((twoStepLoginSupported == false) || (command.name == null) || (parent.f2l == null)) break; if ((twoStepLoginSupported == false) || (command.name == null) || (parent.f2l == null)) break;
@ -2002,6 +2027,7 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use
} }
case 'webauthn-endregister': case 'webauthn-endregister':
{ {
if (parent.parent.config.settings.no2factorauth === true) return;
if ((obj.webAuthnReqistrationRequest == null) || (parent.f2l == null)) return; if ((obj.webAuthnReqistrationRequest == null) || (parent.f2l == null)) return;
// Figure out the origin // Figure out the origin
@ -2022,8 +2048,10 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use
clientAttestationResponse.response.clientDataJSON = new Uint8Array(Buffer.from(clientAttestationResponse.response.clientDataJSON, 'base64')).buffer; clientAttestationResponse.response.clientDataJSON = new Uint8Array(Buffer.from(clientAttestationResponse.response.clientDataJSON, 'base64')).buffer;
parent.f2l.attestationResult(clientAttestationResponse, attestationExpectations).then(function (regResult) { parent.f2l.attestationResult(clientAttestationResponse, attestationExpectations).then(function (regResult) {
// If we register a WebAuthn/FIDO2 key, remove all U2F keys. // Since we are registering a WebAuthn/FIDO2 key, remove all U2F keys (Type 1).
// TODO var otphkeys2 = [];
for (var i = 0; i < user.otphkeys.length; i++) { if (user.otphkeys[i].type != 1) { otphkeys2.push(user.otphkeys[i]); } }
user.otphkeys = otphkeys2;
// Add the new WebAuthn/FIDO2 keys // Add the new WebAuthn/FIDO2 keys
var keyIndex = parent.crypto.randomBytes(4).readUInt32BE(0); var keyIndex = parent.crypto.randomBytes(4).readUInt32BE(0);

784
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -1,6 +1,6 @@
{ {
"name": "meshcentral", "name": "meshcentral",
"version": "0.3.0-u", "version": "0.3.1-q",
"keywords": [ "keywords": [
"Remote Management", "Remote Management",
"Intel AMT", "Intel AMT",
@ -27,7 +27,6 @@
], ],
"dependencies": { "dependencies": {
"archiver": "^3.0.0", "archiver": "^3.0.0",
"authdog": "^0.1.1",
"body-parser": "^1.18.2", "body-parser": "^1.18.2",
"compression": "^1.7.3", "compression": "^1.7.3",
"connect-redis": "^3.4.0", "connect-redis": "^3.4.0",
@ -41,11 +40,9 @@
"multiparty": "^4.2.1", "multiparty": "^4.2.1",
"nedb": "^1.8.0", "nedb": "^1.8.0",
"node-forge": "^0.7.6", "node-forge": "^0.7.6",
"otplib": "^10.0.1",
"ws": "^6.1.2", "ws": "^6.1.2",
"xmldom": "^0.1.27", "xmldom": "^0.1.27",
"yauzl": "^2.10.0", "yauzl": "^2.10.0"
"yubikeyotp": "^0.2.0"
}, },
"devDependencies": {}, "devDependencies": {},
"repository": { "repository": {

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.5 KiB

After

Width:  |  Height:  |  Size: 11 KiB

1
reinstall-modules.bat Normal file
View File

@ -0,0 +1 @@
npm install archiver authdog body-parser compression connect-redis cookie-session express express-handlebars express-ws ipcheck minimist mongojs multiparty nedb node-forge otplib ws xmldom yauzl yubikeyotp

View File

@ -30,6 +30,7 @@
}, },
"_TlsOffload": true, "_TlsOffload": true,
"_MpsTlsOffload": true, "_MpsTlsOffload": true,
"_No2FactorAuth": true,
"_WebRtConfig": { "_WebRtConfig": {
"iceServers": [ "iceServers": [
{ "urls": "stun:stun.services.mozilla.com" }, { "urls": "stun:stun.services.mozilla.com" },

File diff suppressed because one or more lines are too long

View File

@ -25,7 +25,7 @@
<script type="text/javascript" src="scripts/agent-redir-rtc-0.1.0.js"></script> <script type="text/javascript" src="scripts/agent-redir-rtc-0.1.0.js"></script>
<script type="text/javascript" src="scripts/agent-desktop-0.0.2.js"></script> <script type="text/javascript" src="scripts/agent-desktop-0.0.2.js"></script>
<script type="text/javascript" src="scripts/qrcode.min.js"></script> <script type="text/javascript" src="scripts/qrcode.min.js"></script>
<script type="text/javascript" src="scripts/u2f-api.js"></script> <script keeplink=1 type="text/javascript" src="scripts/u2f-api.js"></script>
<script keeplink=1 type="text/javascript" src="scripts/charts.js"></script> <script keeplink=1 type="text/javascript" src="scripts/charts.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>
<script keeplink=1 type="text/javascript" src="scripts/ol.js"></script> <script keeplink=1 type="text/javascript" src="scripts/ol.js"></script>
@ -141,6 +141,7 @@
<table id=ServerSubMenu style=width:100%;height:22px cellpadding=0 cellspacing=0 class=style1> <table id=ServerSubMenu style=width:100%;height:22px cellpadding=0 cellspacing=0 class=style1>
<tr> <tr>
<td id=ServerGeneral style=width:100px;height:24px;cursor:pointer class=style3x onclick=go(6)>General</td> <td id=ServerGeneral style=width:100px;height:24px;cursor:pointer class=style3x onclick=go(6)>General</td>
<td id=ServerStats style=width:100px;height:24px;cursor:pointer class=style3x onclick=go(40)>Stats</td>
<td id=ServerConsole style=width:100px;height:24px;cursor:pointer class=style3x onclick=go(115)>Console</td> <td id=ServerConsole style=width:100px;height:24px;cursor:pointer class=style3x onclick=go(115)>Console</td>
<td class=style3 style=height:24px>&nbsp;</td> <td class=style3 style=height:24px>&nbsp;</td>
</tr> </tr>
@ -743,6 +744,30 @@
</div> </div>
<div id=p31events style="max-height:calc(100vh - 267px);overflow-y:scroll"></div> <div id=p31events style="max-height:calc(100vh - 267px);overflow-y:scroll"></div>
</div> </div>
<div id=p40 style="display:none;height:calc(100vh - 170px)">
<h1>My Server Stats</h1>
<div style=width:100%;height:24px;background-color:#d3d9d6;margin-bottom:4px>
<div style="float:right">
<select id=p40type onchange=updateServerTimelineStats()>
<option value=0>Connections</option>
<option value=1>Memory</option>
</select>&nbsp;
<select id=p40time onchange=updateServerTimelineHours()>
<option value=3>Last 3 hours</option>
<option value=8>Last 8 hours</option>
<option value=24>Last day</option>
<option value=168>Last week</option>
<option value=5040>Last 30 days</option>
</select>&nbsp;
<img src=images/link4.png height=10 width=10 title="Download data points (.csv)" style=cursor:pointer onclick=p40downloadEvents()>&nbsp;
</div>
<div>
&nbsp;<input value="Refresh" type="button" onclick="refreshServerTimelineStats()" />
&nbsp;<input id=p40log type="checkbox" onclick="updateServerTimelineHours()" />Log-X
</div>
</div>
<canvas id=serverMainStats style="height:calc(100vh - 250px);max-height:calc(100vh - 250px);width:100%"></canvas>
</div>
<br id="column_l_bottomgap" /> <br id="column_l_bottomgap" />
</div> </div>
<div id=footer class=noselect> <div id=footer class=noselect>
@ -1014,8 +1039,9 @@
for (var c = 1; c < 27; c++) x += "<option value='" + c + "'>Ctrl-" + String.fromCharCode(64 + c) + " (" + c + ")</option>"; for (var c = 1; c < 27; c++) x += "<option value='" + c + "'>Ctrl-" + String.fromCharCode(64 + c) + " (" + c + ")</option>";
QH('specialkeylist', x); QH('specialkeylist', x);
// Setup server stats panel // Setup server stats panels
setupServerStats(); setupGeneralServerStats();
setupServerTimelineStats();
} }
// Toggle the web page to full screen // Toggle the web page to full screen
@ -1209,7 +1235,11 @@
function onMessage(server, message) { function onMessage(server, message) {
switch (message.action) { switch (message.action) {
case 'serverstats': { case 'serverstats': {
updateServerStats(message); updateGeneralServerStats(message);
break;
}
case 'servertimelinestats': {
setServerTimelineStats(message.events);
break; break;
} }
case 'authcookie': { case 'authcookie': {
@ -1519,8 +1549,10 @@
meshserver.send({ action: 'otp-hkey-setup-response', response: registrationResponse, name: Q('dp1keyname').value }); meshserver.send({ action: 'otp-hkey-setup-response', response: registrationResponse, name: Q('dp1keyname').value });
setDialogMode(2, "Add Security Key", 0, null, '<br />Checking...<br /><br /><br />', 'otpauth-hardware-manage'); setDialogMode(2, "Add Security Key", 0, null, '<br />Checking...<br /><br /><br />', 'otpauth-hardware-manage');
} else { } else {
var errorCodes = ['', 'Unknown error', 'Bad request', 'Unsupported configuration', 'This key was already registered', 'Timeout']; if (xxdialogMode && (xxdialogTag != 'otpauth-hardware-manage')) {
setDialogMode(2, "Add Security Key", 1, null, '<br />' + errorCodes[registrationResponse.errorCode] + '.<br /><br />'); var errorCodes = ['', 'Unknown error', 'Bad request', 'Unsupported configuration', 'This key was already registered', 'Timeout'];
setDialogMode(2, "Add Security Key", 1, null, '<br />' + errorCodes[registrationResponse.errorCode] + '.<br /><br />');
}
} }
}, message.request.timeoutSeconds); }, message.request.timeoutSeconds);
break; break;
@ -1540,15 +1572,15 @@
setDialogMode(2, "Add Security Key", 2, null, x); setDialogMode(2, "Add Security Key", 2, null, x);
var publicKey = message.request; var publicKey = message.request;
message.request.challenge = Uint8Array.from(atob(message.request.challenge), c => c.charCodeAt(0)) message.request.challenge = Uint8Array.from(atob(message.request.challenge), function (c) { return c.charCodeAt(0) })
message.request.user.id = Uint8Array.from(atob(message.request.user.id), c => c.charCodeAt(0)) message.request.user.id = Uint8Array.from(atob(message.request.user.id), function (c) { return c.charCodeAt(0) })
navigator.credentials.create({ publicKey }) navigator.credentials.create({ publicKey: publicKey })
.then((newCredentialInfo) => { .then(function(newCredentialInfo) {
// Public key credential // Public key credential
var r = { rawId: btoa(String.fromCharCode.apply(null, new Uint8Array(newCredentialInfo.rawId))), response: { attestationObject: btoa(String.fromCharCode.apply(null, new Uint8Array(newCredentialInfo.response.attestationObject))), clientDataJSON: btoa(String.fromCharCode.apply(null, new Uint8Array(newCredentialInfo.response.clientDataJSON))) }, type: newCredentialInfo.type }; var r = { rawId: btoa(String.fromCharCode.apply(null, new Uint8Array(newCredentialInfo.rawId))), response: { attestationObject: btoa(String.fromCharCode.apply(null, new Uint8Array(newCredentialInfo.response.attestationObject))), clientDataJSON: btoa(String.fromCharCode.apply(null, new Uint8Array(newCredentialInfo.response.clientDataJSON))) }, type: newCredentialInfo.type };
meshserver.send({ action: 'webauthn-endregister', response: r }); meshserver.send({ action: 'webauthn-endregister', response: r });
setDialogMode(0); setDialogMode(0);
}).catch((error) => { }, function(error) {
// Error // Error
setDialogMode(2, "Add Security Key", 1, null, "ERROR: " + error); setDialogMode(2, "Add Security Key", 1, null, "ERROR: " + error);
}); });
@ -1562,6 +1594,7 @@
masterUpdate(32); masterUpdate(32);
} }
switch (message.event.action) { switch (message.event.action) {
case 'servertimelinestats': { addServerTimelineStats(message.event.data); break; }
case 'accountcreate': case 'accountcreate':
case 'accountchange': { case 'accountchange': {
// An account was created or changed // An account was created or changed
@ -6433,18 +6466,20 @@
var x = '', dateHeader = null; var x = '', dateHeader = null;
for (var i in events) { for (var i in events) {
var event = events[i], time = new Date(event.time); var event = events[i], time = new Date(event.time);
if (time.toLocaleDateString() != dateHeader) { if (event.msg) {
if (dateHeader != null) x += '</table>'; if (time.toLocaleDateString() != dateHeader) {
x += '<table style=width:100% cellpadding=0 cellspacing=0><tr><td colspan=4 class=DevSt>' + time.toLocaleDateString() + '</td></tr>'; if (dateHeader != null) x += '</table>';
dateHeader = time.toLocaleDateString(); x += '<table style=width:100% cellpadding=0 cellspacing=0><tr><td colspan=4 class=DevSt>' + time.toLocaleDateString() + '</td></tr>';
} dateHeader = time.toLocaleDateString();
var icon = 'si3'; }
if (event.etype == 'user') icon = 'm2'; var icon = 'si3';
if (event.etype == 'server') icon = 'si3'; if (event.etype == 'user') icon = 'm2';
if (event.etype == 'server') icon = 'si3';
var msg = event.msg.split('(R)').join('&reg;'); var msg = event.msg.split('(R)').join('&reg;');
if (event.username && event.username != userinfo.name) { msg += ': ' + event.username; } if (event.username && event.username != userinfo.name) { msg += ': ' + event.username; }
x += '<tr onmouseover=eventMouseHover(this,1) onmouseout=eventMouseHover(this,0) style=cursor:pointer><td style=width:18px><div class=' + icon + '></div></td><td class=g1 style=float:none>&nbsp;</td><td style=background-color:#C9C9C9>' + time.toLocaleTimeString() + ' - ' + msg + '</td><td class=g2 style=float:none>&nbsp;</td></tr><tr style=height:2px></tr>'; x += '<tr onmouseover=eventMouseHover(this,1) onmouseout=eventMouseHover(this,0) style=cursor:pointer><td style=width:18px><div class=' + icon + '></div></td><td class=g1 style=float:none>&nbsp;</td><td style=background-color:#C9C9C9>' + time.toLocaleTimeString() + ' - ' + msg + '</td><td class=g2 style=float:none>&nbsp;</td></tr><tr style=height:2px></tr>';
}
} }
if (dateHeader != null) x += '</table>'; if (dateHeader != null) x += '</table>';
if (x == '') x = "<br><i>No Events Found</i><br><br>"; if (x == '') x = "<br><i>No Events Found</i><br><br>";
@ -7169,10 +7204,10 @@
} }
// //
// Server Statistics // MyServer General
// //
function setupServerStats() { function setupGeneralServerStats() {
window.serverStatCpu = new Chart(document.getElementById('serverCpuChart').getContext('2d'), { window.serverStatCpu = new Chart(document.getElementById('serverCpuChart').getContext('2d'), {
type: 'doughnut', type: 'doughnut',
data: { datasets: [{ data: [0, 0], backgroundColor: ['#AAAAAA', '#00AA00'] }], labels: ['Used', 'Free'] }, data: { datasets: [{ data: [0, 0], backgroundColor: ['#AAAAAA', '#00AA00'] }], labels: ['Used', 'Free'] },
@ -7186,7 +7221,7 @@
} }
var lastServerStats = null; var lastServerStats = null;
function updateServerStats(message) { function updateGeneralServerStats(message) {
if (message != null) { lastServerStats = message; } else { message = lastServerStats; } if (message != null) { lastServerStats = message; } else { message = lastServerStats; }
if (message == null) return; if (message == null) return;
@ -7220,6 +7255,131 @@
QH('serverStatsTable', x); QH('serverStatsTable', x);
} }
//
// MyServer Stats
//
var serverTimelineStats = null;
var serverTimelineConfig = {
type: 'line',
data: { labels: [], datasets: [{ label: '', backgroundColor: 'rgba(255, 99, 132, .5)', borderColor: 'rgb(255, 99, 132)', data: [], fill: true }] },
options: {
responsive: true,
maintainAspectRatio: false,
scales: {
xAxes: [{ type: 'time', time: { tooltipFormat: 'll HH:mm' }, display: true, scaleLabel: { display: false, labelString: '' } }],
yAxes: [{ type: 'linear', display: true, scaleLabel: { display: true, labelString: '' } }]
}
}
};
function refreshServerTimelineStats(stats) { meshserver.send({ action: 'servertimelinestats', hours: 24 }); }
function pastDate(hours) { var t = new Date(); t.setTime(t.getTime() - (60 * 60 * 1000 * hours)); return t; }
function setServerTimelineStats(stats) { serverTimelineStats = stats; updateServerTimelineStats(); }
function addServerTimelineStats(stats) {
if (serverTimelineStats == null) return;
serverTimelineStats.push(stats);
var chartType = Q('p40type').value;
if (chartType == 0) {
serverTimelineConfig.data.datasets[0].data.push({ x: stats.time, y: stats.conn.ca });
serverTimelineConfig.data.datasets[1].data.push({ x: stats.time, y: stats.conn.cu });
serverTimelineConfig.data.datasets[2].data.push({ x: stats.time, y: stats.conn.us });
serverTimelineConfig.data.datasets[3].data.push({ x: stats.time, y: stats.conn.rs });
if (stats.conn.am != null) { serverTimelineConfig.data.datasets[4].data.push({ x: stats.time, y: stats.conn.am }); }
} else if (chartType == 1) {
serverTimelineConfig.data.datasets[0].data.push({ x: stats.time, y: stats.mem.external / (1024 * 1024) });
serverTimelineConfig.data.datasets[1].data.push({ x: stats.time, y: stats.mem.heapUsed / (1024 * 1024) });
serverTimelineConfig.data.datasets[2].data.push({ x: stats.time, y: stats.mem.heapTotal / (1024 * 1024) });
serverTimelineConfig.data.datasets[3].data.push({ x: stats.time, y: stats.mem.rss / (1024 * 1024) });
} /* else if (chartType == 2) {
serverTimelineConfig.data.datasets[0].data.push({ x: stats.time, y: stats.db.meshes });
serverTimelineConfig.data.datasets[1].data.push({ x: stats.time, y: stats.db.nodes });
serverTimelineConfig.data.datasets[2].data.push({ x: stats.time, y: stats.db.users });
serverTimelineConfig.data.datasets[3].data.push({ x: stats.time, y: stats.db.total });
} */
updateServerTimelineHours();
}
function updateServerTimelineHours() {
serverTimelineConfig.options.scales.yAxes[0].type = (Q('p40log').checked ? 'logarithmic' : 'linear');
serverTimelineConfig.options.scales.xAxes[0].time = { min: pastDate(Q('p40time').value) };
window.serverMainStats.update();
}
function setupServerTimelineStats() { window.serverMainStats = new Chart(document.getElementById('serverMainStats').getContext('2d'), serverTimelineConfig); }
function updateServerTimelineStats() {
var data, chartType = Q('p40type').value, timeAfter = pastDate(Q('p40time').value);
serverTimelineConfig.options.scales.xAxes[0].time = { min: timeAfter };
if (chartType == 0) { // Connections
serverTimelineConfig.options.scales.yAxes[0].scaleLabel.labelString = 'Connection Count';
data = {
labels: [pastDate(0), timeAfter],
datasets: [
{ label: 'Agents', data: [], backgroundColor: 'rgba(158, 151, 16, .1)', borderColor: 'rgb(158, 151, 16)', fill: true },
{ label: 'Users', data: [], backgroundColor: 'rgba(16, 84, 158, .1)', borderColor: 'rgb(16, 84, 158)', fill: true },
{ label: 'User Sessions', data: [], backgroundColor: 'rgba(255, 99, 132, .1)', borderColor: 'rgb(255, 99, 132)', fill: true },
{ label: 'Relay Sessions', data: [], backgroundColor: 'rgba(39, 158, 16, .1)', borderColor: 'rgb(39, 158, 16)', fill: true },
{ label: 'Intel AMT', data: [], backgroundColor: 'rgba(134, 16, 158, .1)', borderColor: 'rgb(134, 16, 158)', fill: true }
]
};
for (var i = 0; i < serverTimelineStats.length; i++) {
var t = new Date(serverTimelineStats[i].time);
if (serverTimelineStats[i].conn) {
data.datasets[0].data.push({ x: serverTimelineStats[i].time, y: serverTimelineStats[i].conn.ca });
data.datasets[1].data.push({ x: serverTimelineStats[i].time, y: serverTimelineStats[i].conn.cu });
data.datasets[2].data.push({ x: serverTimelineStats[i].time, y: serverTimelineStats[i].conn.us });
data.datasets[3].data.push({ x: serverTimelineStats[i].time, y: serverTimelineStats[i].conn.rs });
if (serverTimelineStats[i].conn.am != null) { data.datasets[4].data.push({ x: serverTimelineStats[i].time, y: serverTimelineStats[i].conn.am }); }
}
}
} else if (chartType == 1) { // Memory
serverTimelineConfig.options.scales.yAxes[0].scaleLabel.labelString = 'Megabytes';
data = {
labels: [pastDate(0), timeAfter],
datasets: [
{ label: 'External', data: [], backgroundColor: 'rgba(158, 151, 16, .1)', borderColor: 'rgb(158, 151, 16)', fill: true },
{ label: 'Heap Used', data: [], backgroundColor: 'rgba(16, 84, 158, .1)', borderColor: 'rgb(16, 84, 158)', fill: true },
{ label: 'Heap Total', data: [], backgroundColor: 'rgba(255, 99, 132, .1)', borderColor: 'rgb(255, 99, 132)', fill: true },
{ label: 'RSS', data: [], backgroundColor: 'rgba(39, 158, 16, .1)', borderColor: 'rgb(39, 158, 16)', fill: true }
]
};
for (var i = 0; i < serverTimelineStats.length; i++) {
data.datasets[0].data.push({ x: serverTimelineStats[i].time, y: serverTimelineStats[i].mem.external / (1024 * 1024) });
data.datasets[1].data.push({ x: serverTimelineStats[i].time, y: serverTimelineStats[i].mem.heapUsed / (1024 * 1024) });
data.datasets[2].data.push({ x: serverTimelineStats[i].time, y: serverTimelineStats[i].mem.heapTotal / (1024 * 1024) });
data.datasets[3].data.push({ x: serverTimelineStats[i].time, y: serverTimelineStats[i].mem.rss / (1024 * 1024) });
}
} /*else if (chartType == 2) { // Database
serverTimelineConfig.options.scales.yAxes[0].scaleLabel.labelString = 'Records';
data = {
labels: [pastDate(0), timeAfter],
datasets: [
{ label: 'Groups', data: [], backgroundColor: 'rgba(158, 151, 16, .1)', borderColor: 'rgb(158, 151, 16)', fill: true },
{ label: 'Devices', data: [], backgroundColor: 'rgba(16, 84, 158, .1)', borderColor: 'rgb(16, 84, 158)', fill: true },
{ label: 'Users', data: [], backgroundColor: 'rgba(255, 99, 132, .1)', borderColor: 'rgb(255, 99, 132)', fill: true },
{ label: 'Records', data: [], backgroundColor: 'rgba(39, 158, 16, .1)', borderColor: 'rgb(39, 158, 16)', fill: true }
]
};
for (var i = 0; i < serverTimelineStats.length; i++) {
data.datasets[0].data.push({ x: serverTimelineStats[i].time, y: serverTimelineStats[i].db.meshes });
data.datasets[1].data.push({ x: serverTimelineStats[i].time, y: serverTimelineStats[i].db.nodes });
data.datasets[2].data.push({ x: serverTimelineStats[i].time, y: serverTimelineStats[i].db.users });
data.datasets[3].data.push({ x: serverTimelineStats[i].time, y: serverTimelineStats[i].db.total });
}
}*/
serverTimelineConfig.data = data;
window.serverMainStats.update();
}
function p40downloadEvents() {
var csv = "time, conn.agent, conn.users, conn.usersessions, conn.relaysession, conn.intelamt, mem.external, mem.heapused, mem.heaptotal, mem.rss\r\n";
for (var i = 0; i < serverTimelineStats.length; i++) {
if (serverTimelineStats[i].conn && serverTimelineStats[i].mem) {
csv += new Date(serverTimelineStats[i].time) + ', ' + serverTimelineStats[i].conn.ca + ', ' + serverTimelineStats[i].conn.cu + ', ' + serverTimelineStats[i].conn.us + ', ' + serverTimelineStats[i].conn.rs + ', ' + (serverTimelineStats[i].conn.am ? serverTimelineStats[i].conn.am : '') + ', ' + serverTimelineStats[i].mem.external + ', ' + serverTimelineStats[i].mem.heapUsed + ', ' + serverTimelineStats[i].mem.heapTotal + ', ' + serverTimelineStats[i].mem.rss + '\r\n';
}
}
saveAs(new Blob([csv], { type: "application/octet-stream" }), "ServerStats.csv");
}
// //
// POPUP DIALOG // POPUP DIALOG
// //
@ -7274,7 +7434,7 @@
function go(x) { function go(x) {
if (xxdialogMode || xxcurrentView == x) return; if (xxdialogMode || xxcurrentView == x) return;
// Edit this line when adding a new screen // Edit this line when adding a new screen
for (var i = 0; i < 32; i++) { QV('p' + i, i == x); } for (var i = 0; i < 41; i++) { QV('p' + i, i == x); }
xxcurrentView = x; xxcurrentView = x;
// Remove left bar selection // Remove left bar selection
@ -7307,7 +7467,7 @@
// My Server // My Server
QS('MainMenuMyServer').backgroundColor = (((x == 6) || (x == 115)) ? "#003366" : "#808080"); QS('MainMenuMyServer').backgroundColor = (((x == 6) || (x == 115)) ? "#003366" : "#808080");
if (((x == 6) || (x == 115))) { Q('LeftMenuMyServer').classList.add('lbbuttonsel', 'lbbuttonsel2'); } if (((x == 6) || (x == 115) || (x == 40))) { Q('LeftMenuMyServer').classList.add('lbbuttonsel', 'lbbuttonsel2'); }
// column_l max-height // column_l max-height
if (webPageFullScreen) { if (webPageFullScreen) {
@ -7326,8 +7486,8 @@
QV('UserDummyMenuSpan', (x < 10) && (x != 6) && webPageFullScreen); QV('UserDummyMenuSpan', (x < 10) && (x != 6) && webPageFullScreen);
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); QV('ServerSubMenuSpan', x == 6 || x == 115 || x == 40);
var panels = { 10: 'MainDev', 11: 'MainDevDesktop', 12: 'MainDevTerminal', 13: 'MainDevFiles', 14: 'MainDevAmt', 15: 'MainDevConsole', 20: 'MeshGeneral', 30: 'UserGeneral', 31: 'UserEvents', 6: 'ServerGeneral', 115: 'ServerConsole' }; var panels = { 10: 'MainDev', 11: 'MainDevDesktop', 12: 'MainDevTerminal', 13: 'MainDevFiles', 14: 'MainDevAmt', 15: 'MainDevConsole', 20: 'MeshGeneral', 30: 'UserGeneral', 31: 'UserEvents', 6: 'ServerGeneral', 40: 'ServerStats', 115: 'ServerConsole' };
for (var i in panels) { for (var i in panels) {
Q(panels[i]).classList.remove('style3x'); Q(panels[i]).classList.remove('style3x');
Q(panels[i]).classList.remove('style3sel'); Q(panels[i]).classList.remove('style3sel');
@ -7342,6 +7502,9 @@
if (x == 1) masterUpdate(4); if (x == 1) masterUpdate(4);
// Fetch the server timeline stats if needed
if ((x == 40) && (serverTimelineStats == null)) { refreshServerTimelineStats(); }
// Update the web page title // Update the web page title
if ((currentNode) && (x >= 10) && (x < 20)) { document.title = 'MeshCentral - ' + currentNode.name; } else { document.title = 'MeshCentral'; } if ((currentNode) && (x >= 10) && (x < 20)) { document.title = 'MeshCentral - ' + currentNode.name; } else { document.title = 'MeshCentral'; }
} }

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -260,7 +260,7 @@
var passRequirements = "{{{passRequirements}}}"; var passRequirements = "{{{passRequirements}}}";
if (passRequirements != "") { passRequirements = JSON.parse(decodeURIComponent(passRequirements)); } else { passRequirements = {}; } if (passRequirements != "") { passRequirements = JSON.parse(decodeURIComponent(passRequirements)); } else { passRequirements = {}; }
var passRequirementsEx = ((passRequirements.min != null) || (passRequirements.max != null) || (passRequirements.upper != null) || (passRequirements.lower != null) || (passRequirements.numeric != null) || (passRequirements.nonalpha != null)); var passRequirementsEx = ((passRequirements.min != null) || (passRequirements.max != null) || (passRequirements.upper != null) || (passRequirements.lower != null) || (passRequirements.numeric != null) || (passRequirements.nonalpha != null));
var hardwareKeyChallenge = '{{{hkey}}}'; var hardwareKeyChallenge = decodeURIComponent('{{{hkey}}}');
var currentpanel = 0; var currentpanel = 0;
function startup() { function startup() {
@ -287,7 +287,34 @@
if ('{{loginmode}}' == '4') { if ('{{loginmode}}' == '4') {
try { if (hardwareKeyChallenge.length > 0) { hardwareKeyChallenge = JSON.parse(hardwareKeyChallenge); } else { hardwareKeyChallenge = null; } } catch (ex) { hardwareKeyChallenge = null } try { if (hardwareKeyChallenge.length > 0) { hardwareKeyChallenge = JSON.parse(hardwareKeyChallenge); } else { hardwareKeyChallenge = null; } } catch (ex) { hardwareKeyChallenge = null }
if ((hardwareKeyChallenge != null) && u2fSupported()) { if ((hardwareKeyChallenge != null) && (hardwareKeyChallenge.type == 'webAuthn')) {
hardwareKeyChallenge.challenge = Uint8Array.from(atob(hardwareKeyChallenge.challenge), function (c) { return c.charCodeAt(0) }).buffer;
var publicKeyCredentialRequestOptions = { challenge: hardwareKeyChallenge.challenge, allowCredentials: [], timeout: hardwareKeyChallenge.timeout }
for (var i = 0; i < hardwareKeyChallenge.keyIds.length; i++) {
publicKeyCredentialRequestOptions.allowCredentials.push(
{ id: Uint8Array.from(atob(hardwareKeyChallenge.keyIds[i]), function (c) { return c.charCodeAt(0) }), type: 'public-key', transports: ['usb', 'ble', 'nfc'], }
);
}
// New WebAuthn hardware keys
navigator.credentials.get({ publicKey: publicKeyCredentialRequestOptions }).then(
function (rawAssertion) {
var assertion = {
id: btoa(String.fromCharCode.apply(null, new Uint8Array(rawAssertion.rawId))),
clientDataJSON: btoa(String.fromCharCode.apply(null, new Uint8Array(rawAssertion.response.clientDataJSON))),
userHandle: btoa(String.fromCharCode.apply(null, new Uint8Array(rawAssertion.response.userHandle))),
signature: btoa(String.fromCharCode.apply(null, new Uint8Array(rawAssertion.response.signature))),
authenticatorData: btoa(String.fromCharCode.apply(null, new Uint8Array(rawAssertion.response.authenticatorData))),
};
Q('hwtokenInput').value = JSON.stringify(assertion);
QE('tokenOkButton', true);
Q('tokenOkButton').click();
},
function (error) { console.log('credentials-get error', error); }
);
} else if ((hardwareKeyChallenge != null) && u2fSupported()) {
// Old U2F hardware keys
window.u2f.sign(hardwareKeyChallenge.appId, hardwareKeyChallenge.challenge, hardwareKeyChallenge.registeredKeys, function (authResponse) { window.u2f.sign(hardwareKeyChallenge.appId, hardwareKeyChallenge.challenge, hardwareKeyChallenge.registeredKeys, function (authResponse) {
if ((currentpanel == 4) && authResponse.signatureData) { if ((currentpanel == 4) && authResponse.signatureData) {
Q('hwtokenInput').value = JSON.stringify(authResponse); Q('hwtokenInput').value = JSON.stringify(authResponse);
@ -300,7 +327,34 @@
if ('{{loginmode}}' == '5') { if ('{{loginmode}}' == '5') {
try { if (hardwareKeyChallenge.length > 0) { hardwareKeyChallenge = JSON.parse(hardwareKeyChallenge); } else { hardwareKeyChallenge = null; } } catch (ex) { hardwareKeyChallenge = null } try { if (hardwareKeyChallenge.length > 0) { hardwareKeyChallenge = JSON.parse(hardwareKeyChallenge); } else { hardwareKeyChallenge = null; } } catch (ex) { hardwareKeyChallenge = null }
if ((hardwareKeyChallenge != null) && u2fSupported()) { if ((hardwareKeyChallenge != null) && (hardwareKeyChallenge.type == 'webAuthn')) {
hardwareKeyChallenge.challenge = Uint8Array.from(atob(hardwareKeyChallenge.challenge), function (c) { return c.charCodeAt(0) }).buffer;
var publicKeyCredentialRequestOptions = { challenge: hardwareKeyChallenge.challenge, allowCredentials: [], timeout: hardwareKeyChallenge.timeout }
for (var i = 0; i < hardwareKeyChallenge.keyIds.length; i++) {
publicKeyCredentialRequestOptions.allowCredentials.push(
{ id: Uint8Array.from(atob(hardwareKeyChallenge.keyIds[i]), function (c) { return c.charCodeAt(0) }), type: 'public-key', transports: ['usb', 'ble', 'nfc'], }
);
}
// New WebAuthn hardware keys
navigator.credentials.get({ publicKey: publicKeyCredentialRequestOptions }).then(
function (rawAssertion) {
var assertion = {
id: btoa(String.fromCharCode.apply(null, new Uint8Array(rawAssertion.rawId))),
clientDataJSON: btoa(String.fromCharCode.apply(null, new Uint8Array(rawAssertion.response.clientDataJSON))),
userHandle: btoa(String.fromCharCode.apply(null, new Uint8Array(rawAssertion.response.userHandle))),
signature: btoa(String.fromCharCode.apply(null, new Uint8Array(rawAssertion.response.signature))),
authenticatorData: btoa(String.fromCharCode.apply(null, new Uint8Array(rawAssertion.response.authenticatorData))),
};
Q('resetHwtokenInput').value = JSON.stringify(assertion);
QE('resetTokenOkButton', true);
Q('resetTokenOkButton').click();
},
function (error) { console.log('credentials-get error', error); }
);
} else if ((hardwareKeyChallenge != null) && u2fSupported()) {
// Old U2F hardware keys
window.u2f.sign(hardwareKeyChallenge.appId, hardwareKeyChallenge.challenge, hardwareKeyChallenge.registeredKeys, function (authResponse) { window.u2f.sign(hardwareKeyChallenge.appId, hardwareKeyChallenge.challenge, hardwareKeyChallenge.registeredKeys, function (authResponse) {
if ((currentpanel == 5) && authResponse.signatureData) { if ((currentpanel == 5) && authResponse.signatureData) {
Q('resetHwtokenInput').value = JSON.stringify(authResponse); Q('resetHwtokenInput').value = JSON.stringify(authResponse);

View File

@ -87,7 +87,7 @@
} }
</style> </style>
<script type="text/javascript" src="scripts/common-0.0.1.js"></script> <script type="text/javascript" src="scripts/common-0.0.1.js"></script>
<script type="text/javascript" src="scripts/u2f-api.js"></script> <script keeplink=1 type="text/javascript" src="scripts/u2f-api.js"></script>
<title>MeshCentral - Login</title> <title>MeshCentral - Login</title>
</head> </head>
<body onload="if (typeof(startup) !== 'undefined') startup();"> <body onload="if (typeof(startup) !== 'undefined') startup();">
@ -110,7 +110,7 @@
<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=width:100%> <table id="centralTable" style=width:100%>
<tr> <tr>
<td id="welcomeimage" align="right"> <td id="welcomeimage" align="right" style="display:none">
<picture> <picture>
<img alt="" width=359 height=310 src=welcome.jpg /> <img alt="" width=359 height=310 src=welcome.jpg />
</picture> </picture>
@ -332,7 +332,7 @@
var newAccountPass = parseInt('{{{newAccountPass}}}'); var newAccountPass = parseInt('{{{newAccountPass}}}');
var emailCheck = ('{{{emailcheck}}}' == 'true'); var emailCheck = ('{{{emailcheck}}}' == 'true');
var passRequirements = "{{{passRequirements}}}"; var passRequirements = "{{{passRequirements}}}";
var hardwareKeyChallenge = '{{{hkey}}}'; var hardwareKeyChallenge = decodeURIComponent('{{{hkey}}}');
if (passRequirements != "") { passRequirements = JSON.parse(decodeURIComponent(passRequirements)); } else { passRequirements = {}; } if (passRequirements != "") { passRequirements = JSON.parse(decodeURIComponent(passRequirements)); } else { passRequirements = {}; }
var passRequirementsEx = ((passRequirements.min != null) || (passRequirements.max != null) || (passRequirements.upper != null) || (passRequirements.lower != null) || (passRequirements.numeric != null) || (passRequirements.nonalpha != null)); var passRequirementsEx = ((passRequirements.min != null) || (passRequirements.max != null) || (passRequirements.upper != null) || (passRequirements.lower != null) || (passRequirements.numeric != null) || (passRequirements.nonalpha != null));
var features = parseInt('{{{features}}}'); var features = parseInt('{{{features}}}');
@ -373,12 +373,12 @@
if ('{{loginmode}}' == '4') { if ('{{loginmode}}' == '4') {
try { if (hardwareKeyChallenge.length > 0) { hardwareKeyChallenge = JSON.parse(hardwareKeyChallenge); } else { hardwareKeyChallenge = null; } } catch (ex) { hardwareKeyChallenge = null } try { if (hardwareKeyChallenge.length > 0) { hardwareKeyChallenge = JSON.parse(hardwareKeyChallenge); } else { hardwareKeyChallenge = null; } } catch (ex) { hardwareKeyChallenge = null }
if ((hardwareKeyChallenge != null) && (hardwareKeyChallenge.type == 'webAuthn')) { if ((hardwareKeyChallenge != null) && (hardwareKeyChallenge.type == 'webAuthn')) {
hardwareKeyChallenge.challenge = Uint8Array.from(atob(hardwareKeyChallenge.challenge), c => c.charCodeAt(0)).buffer; hardwareKeyChallenge.challenge = Uint8Array.from(atob(hardwareKeyChallenge.challenge), function (c) { return c.charCodeAt(0) }).buffer;
const publicKeyCredentialRequestOptions = { challenge: hardwareKeyChallenge.challenge, allowCredentials: [], timeout: hardwareKeyChallenge.timeout } var publicKeyCredentialRequestOptions = { challenge: hardwareKeyChallenge.challenge, allowCredentials: [], timeout: hardwareKeyChallenge.timeout }
for (var i = 0; i < hardwareKeyChallenge.keyIds.length; i++) { for (var i = 0; i < hardwareKeyChallenge.keyIds.length; i++) {
publicKeyCredentialRequestOptions.allowCredentials.push( publicKeyCredentialRequestOptions.allowCredentials.push(
{ id: Uint8Array.from(atob(hardwareKeyChallenge.keyIds[i]), c => c.charCodeAt(0)), type: 'public-key', transports: ['usb', 'ble', 'nfc'], } { id: Uint8Array.from(atob(hardwareKeyChallenge.keyIds[i]), function (c) { return c.charCodeAt(0) }), type: 'public-key', transports: ['usb', 'ble', 'nfc'], }
); );
} }
@ -386,11 +386,11 @@
navigator.credentials.get({ publicKey: publicKeyCredentialRequestOptions }).then( navigator.credentials.get({ publicKey: publicKeyCredentialRequestOptions }).then(
function (rawAssertion) { function (rawAssertion) {
var assertion = { var assertion = {
id: btoa(String.fromCharCode.apply(null, new Uint8Array(rawAssertion.rawId))), //base64encode(rawAssertion.rawId), id: btoa(String.fromCharCode.apply(null, new Uint8Array(rawAssertion.rawId))),
clientDataJSON: btoa(String.fromCharCode.apply(null, new Uint8Array(rawAssertion.response.clientDataJSON))), //arrayBufferToString(rawAssertion.response.clientDataJSON), clientDataJSON: btoa(String.fromCharCode.apply(null, new Uint8Array(rawAssertion.response.clientDataJSON))),
userHandle: btoa(String.fromCharCode.apply(null, new Uint8Array(rawAssertion.response.userHandle))), //base64encode(rawAssertion.response.userHandle), userHandle: btoa(String.fromCharCode.apply(null, new Uint8Array(rawAssertion.response.userHandle))),
signature: btoa(String.fromCharCode.apply(null, new Uint8Array(rawAssertion.response.signature))), //base64encode(rawAssertion.response.signature), signature: btoa(String.fromCharCode.apply(null, new Uint8Array(rawAssertion.response.signature))),
authenticatorData: btoa(String.fromCharCode.apply(null, new Uint8Array(rawAssertion.response.authenticatorData))), //base64encode(rawAssertion.response.authenticatorData) authenticatorData: btoa(String.fromCharCode.apply(null, new Uint8Array(rawAssertion.response.authenticatorData))),
}; };
Q('hwtokenInput').value = JSON.stringify(assertion); Q('hwtokenInput').value = JSON.stringify(assertion);
QE('tokenOkButton', true); QE('tokenOkButton', true);
@ -412,7 +412,34 @@
if ('{{loginmode}}' == '5') { if ('{{loginmode}}' == '5') {
try { if (hardwareKeyChallenge.length > 0) { hardwareKeyChallenge = JSON.parse(hardwareKeyChallenge); } else { hardwareKeyChallenge = null; } } catch (ex) { hardwareKeyChallenge = null } try { if (hardwareKeyChallenge.length > 0) { hardwareKeyChallenge = JSON.parse(hardwareKeyChallenge); } else { hardwareKeyChallenge = null; } } catch (ex) { hardwareKeyChallenge = null }
if ((hardwareKeyChallenge != null) && u2fSupported()) { if ((hardwareKeyChallenge != null) && (hardwareKeyChallenge.type == 'webAuthn')) {
hardwareKeyChallenge.challenge = Uint8Array.from(atob(hardwareKeyChallenge.challenge), function (c) { return c.charCodeAt(0) }).buffer;
var publicKeyCredentialRequestOptions = { challenge: hardwareKeyChallenge.challenge, allowCredentials: [], timeout: hardwareKeyChallenge.timeout }
for (var i = 0; i < hardwareKeyChallenge.keyIds.length; i++) {
publicKeyCredentialRequestOptions.allowCredentials.push(
{ id: Uint8Array.from(atob(hardwareKeyChallenge.keyIds[i]), function (c) { return c.charCodeAt(0) }), type: 'public-key', transports: ['usb', 'ble', 'nfc'], }
);
}
// New WebAuthn hardware keys
navigator.credentials.get({ publicKey: publicKeyCredentialRequestOptions }).then(
function (rawAssertion) {
var assertion = {
id: btoa(String.fromCharCode.apply(null, new Uint8Array(rawAssertion.rawId))),
clientDataJSON: btoa(String.fromCharCode.apply(null, new Uint8Array(rawAssertion.response.clientDataJSON))),
userHandle: btoa(String.fromCharCode.apply(null, new Uint8Array(rawAssertion.response.userHandle))),
signature: btoa(String.fromCharCode.apply(null, new Uint8Array(rawAssertion.response.signature))),
authenticatorData: btoa(String.fromCharCode.apply(null, new Uint8Array(rawAssertion.response.authenticatorData))),
};
Q('resetHwtokenInput').value = JSON.stringify(assertion);
QE('resetTokenOkButton', true);
Q('resetTokenOkButton').click();
},
function (error) { console.log('credentials-get error', error); }
);
} else if ((hardwareKeyChallenge != null) && u2fSupported()) {
// Old U2F hardware keys
window.u2f.sign(hardwareKeyChallenge.appId, hardwareKeyChallenge.challenge, hardwareKeyChallenge.registeredKeys, function (authResponse) { window.u2f.sign(hardwareKeyChallenge.appId, hardwareKeyChallenge.challenge, hardwareKeyChallenge.registeredKeys, function (authResponse) {
if ((currentpanel == 5) && authResponse.signatureData) { if ((currentpanel == 5) && authResponse.signatureData) {
Q('resetHwtokenInput').value = JSON.stringify(authResponse); Q('resetHwtokenInput').value = JSON.stringify(authResponse);

File diff suppressed because one or more lines are too long

View File

@ -560,8 +560,7 @@
displayLocalVideo(true); displayLocalVideo(true);
} }
for (var i in tracks) { webrtc.addTrack(tracks[i], localStream); } for (var i in tracks) { webrtc.addTrack(tracks[i], localStream); }
}) }, function (err) {
.catch(function (err) {
displayControl(err.message + '.'); displayControl(err.message + '.');
hangUpButtonClick(1); hangUpButtonClick(1);
}); });

View File

@ -62,7 +62,7 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) {
const constants = (obj.crypto.constants ? obj.crypto.constants : require('constants')); // require('constants') is deprecated in Node 11.10, use require('crypto').constants instead. const constants = (obj.crypto.constants ? obj.crypto.constants : require('constants')); // require('constants') is deprecated in Node 11.10, use require('crypto').constants instead.
// Setup WebAuthn / FIDO2 // Setup WebAuthn / FIDO2
try { const { Fido2Lib } = require("@davedoesdev/fido2-lib"); obj.f2l = new Fido2Lib({ attestation: "none" }); } catch (ex) { console.log(ex); } try { const { Fido2Lib } = require("@davedoesdev/fido2-lib"); obj.f2l = new Fido2Lib({ attestation: "none" }); } catch (ex) { }
// Variables // Variables
obj.parent = parent; obj.parent = parent;
@ -328,12 +328,12 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) {
// Return true if this user has 2-step auth active // Return true if this user has 2-step auth active
function checkUserOneTimePasswordRequired(domain, user) { function checkUserOneTimePasswordRequired(domain, user) {
return ((user.otpsecret != null) || ((user.otphkeys != null) && (user.otphkeys.length > 0))); return ((parent.config.settings.no2factorauth !== true) && ((user.otpsecret != null) || ((user.otphkeys != null) && (user.otphkeys.length > 0))));
} }
// Check the 2-step auth token // Check the 2-step auth token
function checkUserOneTimePassword(req, domain, user, token, hwtoken, func) { function checkUserOneTimePassword(req, domain, user, token, hwtoken, func) {
const twoStepLoginSupported = ((domain.auth != 'sspi') && (obj.parent.certificates.CommonName.indexOf('.') != -1) && (obj.args.nousers !== true)); const twoStepLoginSupported = ((domain.auth != 'sspi') && (obj.parent.certificates.CommonName.indexOf('.') != -1) && (obj.args.nousers !== true) && (parent.config.settings.no2factorauth !== true));
if (twoStepLoginSupported == false) { func(true); return; }; if (twoStepLoginSupported == false) { func(true); return; };
// Check hardware key // Check hardware key
@ -396,7 +396,11 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) {
for (var i = 0; i < user.otphkeys.length; i++) { if (user.otphkeys[i].type == 1) { u2fKeys.push(user.otphkeys[i]); } } for (var i = 0; i < user.otphkeys.length; i++) { if (user.otphkeys[i].type == 1) { u2fKeys.push(user.otphkeys[i]); } }
if (u2fKeys.length > 0) { if (u2fKeys.length > 0) {
// Check authentication response // Check authentication response
require('authdog').finishAuthentication(req.session.u2fchallenge, authResponse, u2fKeys).then(function (authenticationStatus) { func(true); }, function (error) { func(false); }); var authdoglib = null;
try { authdoglib = require('authdog'); } catch (ex) { }
if (authdoglib == null) { func(false); } else {
authdoglib.finishAuthentication(req.session.u2fchallenge, authResponse, u2fKeys).then(function (authenticationStatus) { func(true); }, function (error) { console.log(error); func(false); });
}
return; return;
} }
} }
@ -458,24 +462,28 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) {
} }
} }
// Get all U2F keys var authdoglib = null;
var u2fKeys = []; try { authdoglib = require('authdog'); } catch (ex) { }
for (var i = 0; i < user.otphkeys.length; i++) { if (user.otphkeys[i].type == 1) { u2fKeys.push(user.otphkeys[i]); } } if (authdoglib != null) {
// Get all U2F keys
var u2fKeys = [];
for (var i = 0; i < user.otphkeys.length; i++) { if (user.otphkeys[i].type == 1) { u2fKeys.push(user.otphkeys[i]); } }
// Generate a U2F challenge // Generate a U2F challenge
if (u2fKeys.length > 0) { if (u2fKeys.length > 0) {
require('authdog').startAuthentication('https://' + obj.parent.certificates.CommonName, u2fKeys, { requestId: 0, timeoutSeconds: 60 }).then(function (registrationRequest) { authdoglib.startAuthentication('https://' + obj.parent.certificates.CommonName, u2fKeys, { requestId: 0, timeoutSeconds: 60 }).then(function (registrationRequest) {
// Save authentication request to session for later use // Save authentication request to session for later use
req.session.u2fchallenge = registrationRequest; req.session.u2fchallenge = registrationRequest;
// Send authentication request to client // Send authentication request to client
func(JSON.stringify(registrationRequest)); func(JSON.stringify(registrationRequest));
}, function (error) { }, function (error) {
// Handle authentication request error // Handle authentication request error
func('');
});
} else {
func(''); func('');
}); }
} else {
func('');
} }
} else { } else {
func(''); func('');
@ -1149,19 +1157,19 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) {
if (obj.args.nousers == true) { features += 0x00000004; } // Single user mode if (obj.args.nousers == true) { features += 0x00000004; } // Single user mode
if (domain.userQuota == -1) { features += 0x00000008; } // No server files mode if (domain.userQuota == -1) { features += 0x00000008; } // No server files mode
if (obj.args.mpstlsoffload) { features += 0x00000010; } // No mutual-auth CIRA if (obj.args.mpstlsoffload) { features += 0x00000010; } // No mutual-auth CIRA
if ((parent.config != null) && (parent.config.settings != null) && (parent.config.settings.allowframing == true)) { features += 0x00000020; } // Allow site within iframe if (parent.config.settings.allowframing == true) { features += 0x00000020; } // Allow site within iframe
if ((obj.parent.mailserver != null) && (obj.parent.certificates.CommonName != null) && (obj.parent.certificates.CommonName.indexOf('.') != -1) && (obj.args.lanonly != true)) { features += 0x00000040; } // Email invites if ((obj.parent.mailserver != null) && (obj.parent.certificates.CommonName != null) && (obj.parent.certificates.CommonName.indexOf('.') != -1) && (obj.args.lanonly != true)) { features += 0x00000040; } // Email invites
if (obj.args.webrtc == true) { features += 0x00000080; } // Enable WebRTC (Default false for now) if (obj.args.webrtc == true) { features += 0x00000080; } // Enable WebRTC (Default false for now)
if (obj.args.clickonce !== false) { features += 0x00000100; } // Enable ClickOnce (Default true) if (obj.args.clickonce !== false) { features += 0x00000100; } // Enable ClickOnce (Default true)
if (obj.args.allowhighqualitydesktop == true) { features += 0x00000200; } // Enable AllowHighQualityDesktop (Default false) if (obj.args.allowhighqualitydesktop == true) { features += 0x00000200; } // Enable AllowHighQualityDesktop (Default false)
if (obj.args.lanonly == true || obj.args.mpsport == 0) { features += 0x00000400; } // No CIRA if (obj.args.lanonly == true || obj.args.mpsport == 0) { features += 0x00000400; } // No CIRA
if ((obj.parent.serverSelfWriteAllowed == true) && (user != null) && (user.siteadmin == 0xFFFFFFFF)) { features += 0x00000800; } // Server can self-write (Allows self-update) if ((obj.parent.serverSelfWriteAllowed == true) && (user != null) && (user.siteadmin == 0xFFFFFFFF)) { features += 0x00000800; } // Server can self-write (Allows self-update)
if ((domain.auth != 'sspi') && (obj.parent.certificates.CommonName.indexOf('.') != -1) && (obj.args.nousers !== true)) { features += 0x00001000; } // 2-step login supported if ((parent.config.settings.no2factorauth !== true) && (domain.auth != 'sspi') && (obj.parent.certificates.CommonName.indexOf('.') != -1) && (obj.args.nousers !== true)) { features += 0x00001000; } // 2-step login supported
if (domain.agentnoproxy === true) { features += 0x00002000; } // Indicates that agents should be installed without using a HTTP proxy if (domain.agentnoproxy === true) { features += 0x00002000; } // Indicates that agents should be installed without using a HTTP proxy
if (domain.yubikey && domain.yubikey.id && domain.yubikey.secret) { features += 0x00004000; } // Indicates Yubikey support if ((parent.config.settings.no2factorauth !== true) && domain.yubikey && domain.yubikey.id && domain.yubikey.secret) { features += 0x00004000; } // Indicates Yubikey support
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 (obj.f2l != null) { features += 0x00020000; } // Enable WebAuthn/FIDO2 support if ((parent.config.settings.no2factorauth !== true) && (obj.f2l != null)) { features += 0x00020000; } // Enable WebAuthn/FIDO2 support
// 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);
@ -1187,9 +1195,9 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) {
// If this is a 2 factor auth request, look for a hardware key challenge. // If this is a 2 factor auth request, look for a hardware key challenge.
// Normal login 2 factor request // Normal login 2 factor request
if ((req.session.loginmode == '4') && (req.session.tokenusername)) { if ((req.session.loginmode == '4') && (req.session.tokenusername)) {
var user = obj.users['user/' + domain.id + '/' + req.session.tokenusername]; var user = obj.users['user/' + domain.id + '/' + req.session.tokenusername.toLowerCase()];
if (user != null) { if (user != null) {
getHardwareKeyChallenge(req, domain, user, function (u2fChallenge) { handleRootRequestLogin(req, res, domain, u2fChallenge, passRequirements); }); getHardwareKeyChallenge(req, domain, user, function (hwchallenge) { handleRootRequestLogin(req, res, domain, hwchallenge, passRequirements); });
return; return;
} }
} }
@ -1202,7 +1210,7 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) {
} else { } else {
var user = obj.users[docs[0]._id]; var user = obj.users[docs[0]._id];
if (user != null) { if (user != null) {
getHardwareKeyChallenge(req, domain, user, function (u2fChallenge) { handleRootRequestLogin(req, res, domain, u2fChallenge, passRequirements); }); getHardwareKeyChallenge(req, domain, user, function (hwchallenge) { handleRootRequestLogin(req, res, domain, hwchallenge, passRequirements); });
} else { } else {
req.session = null; req.session = null;
res.redirect(domain.url); res.redirect(domain.url);
@ -1240,14 +1248,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) ? 'login-mobile-min' : 'login-min'), { loginmode: loginmode, rootCertLink: getRootCertLink(), title: domain.title, title2: domain.title2, newAccount: domain.newaccounts, newAccountPass: (((domain.newaccountspass == null) || (domain.newaccountspass == '')) ? 0 : 1), serverDnsName: obj.getWebServerName(domain), serverPublicPort: httpsPort, emailcheck: emailcheck, features: features, sessiontime: args.sessiontime, passRequirements: passRequirements, footer: (domain.footer == null) ? '' : domain.footer, hkey: hardwareKeyChallenge, message: message, passhint: passhint, welcometext: domain.welcometext?encodeURIComponent(domain.welcometext):null }); res.render(obj.path.join(obj.parent.webViewsPath, isMobileBrowser(req) ? 'login-mobile-min' : 'login-min'), { loginmode: loginmode, rootCertLink: getRootCertLink(), title: domain.title, title2: domain.title2, newAccount: domain.newaccounts, newAccountPass: (((domain.newaccountspass == null) || (domain.newaccountspass == '')) ? 0 : 1), serverDnsName: obj.getWebServerName(domain), serverPublicPort: httpsPort, emailcheck: emailcheck, features: features, sessiontime: args.sessiontime, passRequirements: passRequirements, footer: (domain.footer == null) ? '' : domain.footer, hkey: encodeURIComponent(hardwareKeyChallenge), message: message, passhint: passhint, welcometext: domain.welcometext?encodeURIComponent(domain.welcometext):null });
} 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) ? 'login-mobile' : 'login'), { loginmode: loginmode, rootCertLink: getRootCertLink(), title: domain.title, title2: domain.title2, newAccount: domain.newaccounts, newAccountPass: (((domain.newaccountspass == null) || (domain.newaccountspass == '')) ? 0 : 1), serverDnsName: obj.getWebServerName(domain), serverPublicPort: httpsPort, emailcheck: emailcheck, features: features, sessiontime: args.sessiontime, passRequirements: passRequirements, footer: (domain.footer == null) ? '' : domain.footer, hkey: hardwareKeyChallenge, message: message, passhint: passhint, welcometext: domain.welcometext ? encodeURIComponent(domain.welcometext) : null }); res.render(obj.path.join(obj.parent.webViewsPath, isMobileBrowser(req) ? 'login-mobile' : 'login'), { loginmode: loginmode, rootCertLink: getRootCertLink(), title: domain.title, title2: domain.title2, newAccount: domain.newaccounts, newAccountPass: (((domain.newaccountspass == null) || (domain.newaccountspass == '')) ? 0 : 1), serverDnsName: obj.getWebServerName(domain), serverPublicPort: httpsPort, emailcheck: emailcheck, features: features, sessiontime: args.sessiontime, passRequirements: passRequirements, footer: (domain.footer == null) ? '' : domain.footer, hkey: encodeURIComponent(hardwareKeyChallenge), message: message, passhint: passhint, welcometext: domain.welcometext ? encodeURIComponent(domain.welcometext) : null });
} }
} 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) ? 'login-mobile' : 'login'), { loginmode: loginmode, rootCertLink: getRootCertLink(), title: domain.title, title2: domain.title2, newAccount: domain.newaccounts, newAccountPass: (((domain.newaccountspass == null) || (domain.newaccountspass == '')) ? 0 : 1), serverDnsName: obj.getWebServerName(domain), serverPublicPort: httpsPort, emailcheck: emailcheck, features: features, sessiontime: args.sessiontime, passRequirements: passRequirements, footer: (domain.footer == null) ? '' : domain.footer, hkey: hardwareKeyChallenge, message: message, passhint: passhint, welcometext: domain.welcometext ? encodeURIComponent(domain.welcometext) : null }); res.render(obj.path.join(obj.parent.webViewsPath, isMobileBrowser(req) ? 'login-mobile' : 'login'), { loginmode: loginmode, rootCertLink: getRootCertLink(), title: domain.title, title2: domain.title2, newAccount: domain.newaccounts, newAccountPass: (((domain.newaccountspass == null) || (domain.newaccountspass == '')) ? 0 : 1), serverDnsName: obj.getWebServerName(domain), serverPublicPort: httpsPort, emailcheck: emailcheck, features: features, sessiontime: args.sessiontime, passRequirements: passRequirements, footer: (domain.footer == null) ? '' : domain.footer, hkey: encodeURIComponent(hardwareKeyChallenge), message: message, passhint: passhint, welcometext: domain.welcometext ? encodeURIComponent(domain.welcometext) : null });
} }
/* /*