Merge branch 'master' into moving-styles

This commit is contained in:
elastalink 2019-04-08 01:19:02 -04:00 committed by GitHub
commit dc0d5df9c5
36 changed files with 1058 additions and 1673 deletions

View File

@ -32,7 +32,6 @@
<Compile Include="agents\modules_meshcmd\amt-wsman.js" />
<Compile Include="agents\modules_meshcmd\amt-xml.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-manager.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-xml.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\monitor-border.js" />
<Compile Include="agents\modules_meshcore\monitor-info.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\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.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_min\amt-lme.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
function onVerifyServer(clientName, certs) {
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'; }
}
// 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 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'); }
@ -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 (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 = {}; }
var settings2 = {}; for (var i in settings) { settings2[i.toLowerCase()] = settings[i]; } settings = settings2;
// Set the arguments
if ((typeof args.action) == 'string') { settings.action = args.action; }
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.password) == 'string') { settings.password = args.password; }
if ((typeof args.user) == 'string') { settings.username = args.user; }
if ((typeof args.pass) == 'string') { settings.password = args.pass; }
if ((typeof args.host) == 'string') { settings.hostname = args.host; }
if ((typeof args.hostname) == 'string') { settings.hostname = args.hostname; }
if ((typeof args.serverid) == 'string') { settings.serverId = args.serverid; }
if ((typeof args.serverhttpshash) == 'string') { settings.serverHttpsHash = args.serverhttpshash; }
if ((typeof args.remoteport) == 'string') { settings.remotePort = parseInt(args.remoteport); }
if ((typeof args.serverid) == 'string') { settings.serverid = args.serverid; }
if ((typeof args.serverhttpshash) == 'string') { settings.serverhttpshash = args.serverhttpshash; }
if ((typeof args.remoteport) == 'string') { settings.remoteport = parseInt(args.remoteport); }
if ((typeof args.out) == 'string') { settings.output = args.out; }
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.agent) == 'string') { settings.agent = args.agent; }
if ((typeof args.proxy) == 'string') { settings.proxy = args.proxy; }
if (args.debug) { try { waitForDebugger(); } catch (e) { } }
if (args.noconsole) { settings.noconsole = true; }
if (args.nocommander) { settings.noconsole = true; }
@ -263,6 +265,15 @@ function run(argv) {
}
settings.action = settings.action.toLowerCase();
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') {
// Display SM BIOS tables in raw form
SMBiosTables = require('smbios');
@ -283,14 +294,14 @@ function run(argv) {
});
} else if (settings.action == 'route') {
// 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.remoteNodeId == null) || (typeof settings.remoteNodeId != 'string')) { console.log('No or invalid \"remoteNodeId\" specified.'); 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.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.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.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.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.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
} 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
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.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.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();
} else if (settings.action == 'amtuuid') {
// Start running
@ -751,7 +762,7 @@ function startMeScriptEx() {
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; }
} else {
scriptData = settings.scriptJSON;
scriptData = settings.scriptjson;
}
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; }
@ -1362,12 +1373,12 @@ function processLmsControlData(data) {
function startRouter() {
tcpserver = net.createServer(OnTcpClientConnected);
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.
if (settings.remoteName == null) {
console.log('Redirecting local port ' + settings.localPort + ' to remote port ' + settings.remotePort + '.');
if (settings.remotename == null) {
console.log('Redirecting local port ' + settings.localport + ' to remote port ' + settings.remoteport + '.');
} 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.');
@ -1384,7 +1395,7 @@ function OnTcpClientConnected(c) {
c.on('end', function () { disconnectTunnel(this, this.websocket, 'Client closed'); });
c.pause();
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; }
options.checkServerIdentity = onVerifyServer;
options.rejectUnauthorized = false;
@ -1440,7 +1451,7 @@ function discoverMeshServerOnce() {
multicastSockets[i].addMembership(membershipIPv4);
//multicastSockets[i].setMulticastLoopback(true);
multicastSockets[i].once('message', OnMulticastMessage);
multicastSockets[i].send(settings.serverId, 16989, membershipIPv4);
multicastSockets[i].send(settings.serverid, 16989, membershipIPv4);
} catch (e) { }
}
}
@ -1451,9 +1462,9 @@ function discoverMeshServerOnce() {
// Called when a multicast packet is received
function OnMulticastMessage(msg, rinfo) {
var m = msg.toString().split('|');
if ((m.length == 3) && (m[0] == 'MeshCentral2') && (m[1] == settings.serverId)) {
settings.serverUrl = m[2].replace('%s', rinfo.address).replace('/agent.ashx', '/meshrelay.ashx');
console.log('Found server at ' + settings.serverUrl + '.');
if ((m.length == 3) && (m[0] == 'MeshCentral2') && (m[1] == settings.serverid)) {
settings.serverurl = m[2].replace('%s', rinfo.address).replace('/agent.ashx', '/meshrelay.ashx');
console.log('Found server at ' + settings.serverurl + '.');
if (discoveryInterval != null) { clearInterval(discoveryInterval); discoveryInterval = null; }
startRouter();
}

File diff suppressed because one or more lines are too long

View File

@ -21,6 +21,7 @@ start() {
fi
echo 'Starting service…' >&2
local CMD="$SCRIPT &> \"$LOGFILE\" & echo \$!"
cd /usr/local/mesh
su -c "$CMD" $RUNAS > "$PIDFILE"
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 _xmlTraverseAllRec(nodes, func) { for (var i in nodes) { func(nodes[i]); if (nodes[i].childNodes) { _xmlTraverseAllRec(nodes[i].childNodes, func); } } }
function _turnToXmlRec(text) {
var elementStack = new _treeBuilder(), lastElement = null, x1 = text.split('<'), ret = [], element = null, currentElementName = null;
for (var i in x1) {
var x2 = x1[i].split('>'), x3 = x2[0].split(' '), elementName = x3[0];
if ((elementName.length > 0) && (elementName[0] != '?')) {
if (elementName[0] != '/') {
var attributes = [], localName, localname2 = elementName.split(' ')[0].split(':'), localName = (localname2.length > 1) ? localname2[1] : localname2[0];
Object.defineProperty(attributes, "get",
{
value: function () {
if (arguments.length == 1) {
for (var a in this) { if (this[a].name == arguments[0]) { return (this[a]); } }
}
else if (arguments.length == 2) {
for (var a in this) { if (this[a].name == arguments[1] && (arguments[0] == '*' || this[a].namespace == arguments[0])) { return (this[a]); } }
}
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) {
var skip = false;
for (var j in x3) {
if (x3[j] == '/') {
// This is an empty Element
elementStack.peek().namespace = elementStack.peek().name == elementStack.peek().localName ? elementStack.getNamespace('*') : elementStack.getNamespace(elementStack.peek().name.substring(0, elementStack.peek().name.indexOf(':')));
elementStack.peek().textContent = '';
lastElement = elementStack.pop();
skip = true;
break;
}
var k = x3[j].indexOf('=');
if (k > 0) {
var attrName = x3[j].substring(0, k);
var attrValue = x3[j].substring(k + 2, x3[j].length - 1);
var attrNS = elementStack.getNamespace('*');
if (attrName == 'xmlns') {
elementStack.addNamespace('*', attrValue);
attrNS = attrValue;
} else if (attrName.startsWith('xmlns:')) {
elementStack.addNamespace(attrName.substring(6), attrValue);
} else {
var ax = attrName.split(':');
if (ax.length == 2) { attrName = ax[1]; attrNS = elementStack.getNamespace(ax[0]); }
try {
if (text == null) return null;
var elementStack = new _treeBuilder(), lastElement = null, x1 = text.split('<'), ret = [], element = null, currentElementName = null;
for (var i in x1) {
var x2 = x1[i].split('>'), x3 = x2[0].split(' '), elementName = x3[0];
if ((elementName.length > 0) && (elementName[0] != '?')) {
if (elementName[0] != '/') {
var attributes = [], localName, localname2 = elementName.split(' ')[0].split(':'), localName = (localname2.length > 1) ? localname2[1] : localname2[0];
Object.defineProperty(attributes, "get",
{
value: function () {
if (arguments.length == 1) {
for (var a in this) { if (this[a].name == arguments[0]) { return (this[a]); } }
}
else if (arguments.length == 2) {
for (var a in this) { if (this[a].name == arguments[1] && (arguments[0] == '*' || this[a].namespace == arguments[0])) { return (this[a]); } }
}
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) {
var skip = false;
for (var j in x3) {
if (x3[j] == '/') {
// This is an empty Element
elementStack.peek().namespace = elementStack.peek().name == elementStack.peek().localName ? elementStack.getNamespace('*') : elementStack.getNamespace(elementStack.peek().name.substring(0, elementStack.peek().name.indexOf(':')));
elementStack.peek().textContent = '';
lastElement = elementStack.pop();
skip = true;
break;
}
var k = x3[j].indexOf('=');
if (k > 0) {
var attrName = x3[j].substring(0, k);
var attrValue = x3[j].substring(k + 2, x3[j].length - 1);
var attrNS = elementStack.getNamespace('*');
if (attrName == 'xmlns') {
elementStack.addNamespace('*', attrValue);
attrNS = attrValue;
} else if (attrName.startsWith('xmlns:')) {
elementStack.addNamespace(attrName.substring(6), attrValue);
} 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]; }
} else { 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(':')));
if (x2[1]) { elementStack.peek().textContent = x2[1]; }
} else { lastElement = elementStack.pop(); }
}
}
}
} catch (ex) { return null; }
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;
obj.applyPolicy = function () {
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; }
obj.getAmtInfo(function (meinfo) {
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 _xmlTraverseAllRec(nodes, func) { for (var i in nodes) { func(nodes[i]); if (nodes[i].childNodes) { _xmlTraverseAllRec(nodes[i].childNodes, func); } } }
function _turnToXmlRec(text) {
if (text == null) return null;
var elementStack = new _treeBuilder(), lastElement = null, x1 = text.split('<'), ret = [], element = null, currentElementName = null;
for (var i in x1) {
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 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 expireServerStatsSeconds = (60 * 60 * 24 * 30); // By default, expire power events after 30 days. (Seconds * Minutes * Hours * Days)
obj.path = require('path');
obj.parent = parent;
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.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.statsevents == 'number') { expireServerStatsSeconds = obj.parent.args.dbexpire.statsevents; }
}
if (obj.parent.args.mongodb) {
@ -115,6 +117,30 @@ module.exports.CreateDB = function (parent) {
// Setup MongoDB smbios collection, no indexes needed
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 {
// Use NeDB (The default)
obj.databaseType = 1;
@ -167,6 +193,12 @@ module.exports.CreateDB = function (parent) {
// Setup the SMBIOS collection
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) {
@ -305,6 +337,10 @@ module.exports.CreateDB = function (parent) {
obj.RemoveSMBIOS = function (id) { obj.smbiosfile.remove({ _id: id }); };
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
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: 'user' }, function (err, userCount) {
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

@ -637,13 +637,15 @@ module.exports.CreateMeshAgent = function (parent, db, ws, req, args, domain) {
obj.connectTime = Date.now();
db.Set({ _id: 'lc' + obj.dbNodeKey, type: 'lastconnect', domain: domain.id, time: obj.connectTime, addr: obj.remoteaddrport });
// Device already exists, look if changes has occured
// Device already exists, look if changes have occured
var changes = [], change = 0, log = 0;
if (device.agent == null) { device.agent = { ver: obj.agentInfo.agentVersion, id: obj.agentInfo.agentId, caps: obj.agentInfo.capabilities }; change = 1; }
if (device.rname != obj.agentInfo.computerName) { device.rname = obj.agentInfo.computerName; change = 1; changes.push('computer name'); }
if (device.agent.ver != obj.agentInfo.agentVersion) { device.agent.ver = obj.agentInfo.agentVersion; change = 1; changes.push('agent version'); }
if (device.agent.id != obj.agentInfo.agentId) { device.agent.id = obj.agentInfo.agentId; change = 1; changes.push('agent type'); }
if ((device.agent.caps & 24) != (obj.agentInfo.capabilities & 24)) { device.agent.caps = obj.agentInfo.capabilities; change = 1; changes.push('agent capabilities'); } // If agent console or javascript support changes, update capabilities
if (mesh.flags && (mesh.flags & 2) && (device.name != obj.agentInfo.computerName)) { device.name = obj.agentInfo.computerName; change = 1; } // We want the server name to be sync'ed to the hostname
if (change == 1) {
db.Set(device);

View File

@ -60,16 +60,19 @@ function CreateMeshCentralServer(config, args) {
obj.serverKey = Buffer.from(obj.crypto.randomBytes(48), 'binary');
obj.loginCookieEncryptionKey = null;
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.
try { obj.currentVer = JSON.parse(obj.fs.readFileSync(obj.path.join(__dirname, 'package.json'), 'utf8')).version; } catch (e) { } // Fetch server version
// Setup the default configuration and files paths
if ((__dirname.endsWith('/node_modules/meshcentral')) || (__dirname.endsWith('\\node_modules\\meshcentral')) || (__dirname.endsWith('/node_modules/meshcentral/')) || (__dirname.endsWith('\\node_modules\\meshcentral\\'))) {
obj.parentpath = obj.path.join(__dirname, '../..');
obj.datapath = obj.path.join(__dirname, '../../meshcentral-data');
obj.filespath = obj.path.join(__dirname, '../../meshcentral-files');
if (obj.fs.existsSync(obj.path.join(__dirname, '../../meshcentral-web/views'))) { obj.webViewsPath = obj.path.join(__dirname, '../../meshcentral-web/views'); } else { obj.webViewsPath = obj.path.join(__dirname, 'views'); }
if (obj.fs.existsSync(obj.path.join(__dirname, '../../meshcentral-web/public'))) { obj.webPublicPath = obj.path.join(__dirname, '../../meshcentral-web/public'); } else { obj.webPublicPath = obj.path.join(__dirname, 'public'); }
} else {
obj.parentpath = __dirname;
obj.datapath = obj.path.join(__dirname, '../meshcentral-data');
obj.filespath = obj.path.join(__dirname, '../meshcentral-files');
if (obj.fs.existsSync(obj.path.join(__dirname, '../meshcentral-web/views'))) { obj.webViewsPath = obj.path.join(__dirname, '../meshcentral-web/views'); } else { obj.webViewsPath = obj.path.join(__dirname, 'views'); }
@ -162,7 +165,7 @@ function CreateMeshCentralServer(config, args) {
// Launch MeshCentral as a child server and monitor it.
obj.launchChildServer = function (startLine) {
var child_process = require('child_process');
var xprocess = child_process.exec(startLine + ' --launch', { maxBuffer: Infinity }, function (error, stdout, stderr) {
var xprocess = child_process.exec(startLine + ' --launch', { maxBuffer: Infinity, cwd: obj.parentpath }, function (error, stdout, stderr) {
if (xprocess.xrestart == 1) {
setTimeout(function () { obj.launchChildServer(startLine); }, 500); // This is an expected restart.
} else if (xprocess.xrestart == 2) {
@ -174,7 +177,7 @@ function CreateMeshCentralServer(config, args) {
if (typeof obj.args.selfupdate == 'string') { version = '@' + obj.args.selfupdate; }
var child_process = require('child_process');
var npmpath = ((typeof obj.args.npmpath == 'string') ? obj.args.npmpath : 'npm');
var xxprocess = child_process.exec(npmpath + ' install meshcentral' + version, { maxBuffer: Infinity, cwd: obj.path.join(__dirname, '../..') }, function (error, stdout, stderr) { });
var xxprocess = child_process.exec(npmpath + ' install meshcentral' + version, { maxBuffer: Infinity, cwd: obj.parentpath }, function (error, stdout, stderr) { });
xxprocess.data = '';
xxprocess.stdout.on('data', function (data) { xxprocess.data += data; });
xxprocess.stderr.on('data', function (data) { xxprocess.data += data; });
@ -204,7 +207,7 @@ function CreateMeshCentralServer(config, args) {
if (typeof obj.args.selfupdate == 'string') { callback(obj.currentVer, obj.args.selfupdate); return; } // If we are targetting a specific version, return that one as current.
var child_process = require('child_process');
var npmpath = ((typeof obj.args.npmpath == 'string') ? obj.args.npmpath : 'npm');
var xprocess = child_process.exec(npmpath + ' view meshcentral dist-tags.latest', { maxBuffer: 512000 }, function (error, stdout, stderr) { });
var xprocess = child_process.exec(npmpath + ' view meshcentral dist-tags.latest', { maxBuffer: 512000, cwd: obj.parentpath }, function (error, stdout, stderr) { });
xprocess.data = '';
xprocess.stdout.on('data', function (data) { xprocess.data += data; });
xprocess.stderr.on('data', function (data) { });
@ -800,6 +803,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');
if (obj.args.nousers == true) { obj.updateServerState('nousers', '1'); }
obj.updateServerState('state', 'running');
@ -1598,31 +1630,38 @@ function getConfig(createSampleConfig) {
function InstallModules(modules, func) {
var missingModules = [];
if (modules.length > 0) {
for (var i in modules) { try { var xxmodule = require(modules[i]); } catch (e) { missingModules.push(modules[i]); } }
if (missingModules.length > 0) { InstallModule(missingModules.join(' '), InstallModules, modules, func); } else { func(); }
for (var i in modules) {
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
var InstallModuleChildProcess = null;
function InstallModule(modulename, func, tag1, tag2) {
try {
var module = require(modulename);
} catch (e) {
console.log('Installing ' + modulename + '...');
var child_process = require('child_process');
console.log('Installing ' + modulename + '...');
var child_process = require('child_process');
var parentpath = __dirname;
// 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 = null;
if (error != null) { console.log('ERROR: Unable to install missing package \'' + modulename + '\', make sure npm is installed: ' + error); process.exit(); return; }
func(tag1, tag2);
// Get the working directory
if ((__dirname.endsWith('/node_modules/meshcentral')) || (__dirname.endsWith('\\node_modules\\meshcentral')) || (__dirname.endsWith('/node_modules/meshcentral/')) || (__dirname.endsWith('\\node_modules\\meshcentral\\'))) { parentpath = require('path').join(__dirname, '../..'); }
// 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 --no-optional --save ' + modulename, { maxBuffer: 512000, timeout: 10000, cwd: parentpath }, function (error, stdout, stderr) {
InstallModuleChildProcess = null;
if (error != null) {
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;
});
}
func(tag1, tag2);
return;
}
func(tag1, tag2);
});
}
// Detect CTRL-C on Linux and stop nicely
@ -1630,6 +1669,7 @@ process.on('SIGINT', function () { if (meshserver != null) { meshserver.Stop();
// Load the really basic modules
var meshserver = null;
var previouslyInstalledModules = { };
function mainStart(args) {
// 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; }
@ -1640,10 +1680,12 @@ function mainStart(args) {
var config = getConfig(false);
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 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
var modules = ['ws', 'nedb', 'https', 'yauzl', 'xmldom', 'express', 'archiver', 'multiparty', 'node-forge', 'express-ws', 'compression', 'body-parser', 'connect-redis', 'express-handlebars'];
@ -1658,9 +1700,14 @@ function mainStart(args) {
// If running NodeJS < 8, install "util.promisify"
if (nodeVersion < 8) { modules.push('util.promisify'); }
// if running NodeJS 8 or higher, we can install WebAuthn/FIDO2 support
if ((nodeVersion >= 8) && (allsspi == false)) { modules.push('@davedoesdev/fido2-lib'); }
// Setup 2nd factor authentication
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
InstallModules(modules, function () { meshserver = CreateMeshCentralServer(config, args); meshserver.Start(); });
});
@ -1670,4 +1717,4 @@ if (require.main === module) {
mainStart(require('minimist')(process.argv.slice(2))); // Called directly, launch normally.
} else {
module.exports.mainStart = mainStart; // Required as a module, useful for winservice.js
}
}

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) { }
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':
{
if ((user.siteadmin & 21) != 0) { // Only site administrators with "site backup" or "site restore" or "site update" permissions can use this.
if (common.validateInt(command.interval, 1000, 1000000) == false) {
// Clear the timer
if (obj.serverStatsTimer != null) { clearInterval(obj.serverStatsTimer); delete obj.serverStatsTimer; }
} else {
// Set the timer
obj.SendServerStats();
obj.serverStatsTimer = setInterval(obj.SendServerStats, command.interval);
}
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) {
// Clear the timer
if (obj.serverStatsTimer != null) { clearInterval(obj.serverStatsTimer); delete obj.serverStatsTimer; }
} else {
// Set the timer
obj.SendServerStats();
obj.serverStatsTimer = setInterval(obj.SendServerStats, command.interval);
}
break;
}
@ -1032,6 +1040,9 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use
}
case 'createmesh':
{
// Check if we have new group restriction
if ((user.siteadmin != 0xFFFFFFFF) && ((user.siteadmin & 64) != 0)) break;
// In some situations, we need a verified email address to create a device group.
if ((parent.parent.mailserver != null) && (domain.auth != 'sspi') && (user.emailVerified !== true) && (user.siteadmin != 0xFFFFFFFF)) return; // User must verify it's email first.
@ -1773,7 +1784,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));
if (twoStepLoginSupported) {
// 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.
ws.send(JSON.stringify({ action: 'otpauth-request', secret: secret, url: otplib.authenticator.keyuri(user.name, parent.certificates.CommonName, secret) }));
}
@ -1785,7 +1798,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));
if (twoStepLoginSupported) {
// 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
if (otplib.authenticator.check(command.token, command.secret) === true) {
// Token is valid, activate 2-step login on this account.
@ -1853,8 +1868,6 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use
}
case 'otp-hkey-get':
{
// Check is 2-step login is supported
const twoStepLoginSupported = ((domain.auth != 'sspi') && (parent.parent.certificates.CommonName.indexOf('.') != -1) && (args.lanonly !== true) && (args.nousers !== true));
if (twoStepLoginSupported == false) break;
@ -1886,11 +1899,15 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use
}
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/
var yubikeyotp = null;
try { yubikeyotp = require('yubikeyotp'); } catch (ex) { }
// Check is 2-step login is supported
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 }));
break;
}
@ -1904,7 +1921,6 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use
// TODO: Check if command.otp is modhex encoded, reject if not.
// 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 }
if (domain.yubikey.proxy) { request.requestParams = { proxy: domain.yubikey.proxy }; }
yubikeyotp.verifyOTP(request, function (err, results) {
@ -1934,16 +1950,21 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use
}
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
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
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]); } } }
// 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
obj.hardwareKeyRegistrationRequest = registrationRequest;
@ -1957,12 +1978,17 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use
}
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
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
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);
ws.send(JSON.stringify({ action: 'otp-hkey-setup-response', result: true, name: command.name, index: keyIndex }));
if (user.otphkeys == null) { user.otphkeys = []; }
@ -1980,6 +2006,8 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use
}
case 'webauthn-startregister':
{
if (parent.parent.config.settings.no2factorauth === true) return;
// Check is 2-step login is supported
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;
@ -2002,6 +2030,7 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use
}
case 'webauthn-endregister':
{
if (parent.parent.config.settings.no2factorauth === true) return;
if ((obj.webAuthnReqistrationRequest == null) || (parent.f2l == null)) return;
// Figure out the origin
@ -2022,8 +2051,10 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use
clientAttestationResponse.response.clientDataJSON = new Uint8Array(Buffer.from(clientAttestationResponse.response.clientDataJSON, 'base64')).buffer;
parent.f2l.attestationResult(clientAttestationResponse, attestationExpectations).then(function (regResult) {
// If we register a WebAuthn/FIDO2 key, remove all U2F keys.
// TODO
// Since we are registering a WebAuthn/FIDO2 key, remove all U2F keys (Type 1).
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
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",
"version": "0.3.0-u",
"version": "0.3.1-x",
"keywords": [
"Remote Management",
"Intel AMT",
@ -27,7 +27,6 @@
],
"dependencies": {
"archiver": "^3.0.0",
"authdog": "^0.1.1",
"body-parser": "^1.18.2",
"compression": "^1.7.3",
"connect-redis": "^3.4.0",
@ -41,11 +40,9 @@
"multiparty": "^4.2.1",
"nedb": "^1.8.0",
"node-forge": "^0.7.6",
"otplib": "^10.0.1",
"ws": "^6.1.2",
"xmldom": "^0.1.27",
"yauzl": "^2.10.0",
"yubikeyotp": "^0.2.0"
"yauzl": "^2.10.0"
},
"devDependencies": {},
"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

View File

@ -4,6 +4,8 @@
* @version v0.0.2c
*/
// https://invisible-island.net/xterm/ctlseqs/ctlseqs.html
// Construct a MeshServer object
var CreateAmtRemoteTerminal = function (divid) {
var obj = {};
@ -32,10 +34,15 @@ var CreateAmtRemoteTerminal = function (divid) {
var _termstate = 0;
var _escNumber = [];
var _escNumberPtr = 0;
var _escNumberMode = 0;
var _scratt = [];
var _tscreen = [];
var _VTUNDERLINE = 1;
var _VTREVERSE = 2;
var _backSpaceErase = false;
var _cursorVisible = true;
var _scrollRegion = [0, 24];
var _altKeypadMode = false;
obj.Start = function () { }
@ -72,6 +79,9 @@ var CreateAmtRemoteTerminal = function (divid) {
switch (c) {
case 27: // ESC
_termstate = 1;
_escNumber = [];
_escNumberPtr = 0;
_escNumberMode = 0;
break;
default:
// Process a single char
@ -82,8 +92,6 @@ var CreateAmtRemoteTerminal = function (divid) {
case 1:
switch (b) {
case '[':
_escNumberPtr = 0;
_escNumber = [];
_termstate = 2;
break;
case '(':
@ -92,6 +100,16 @@ var CreateAmtRemoteTerminal = function (divid) {
case ')':
_termstate = 5;
break;
case '=':
// Set alternate keypad mode
_altKeypadMode = true;
_termstate = 0;
break;
case '>':
// Set numeric keypad mode
_altKeypadMode = false;
_termstate = 0;
break;
default:
_termstate = 0;
break;
@ -100,23 +118,20 @@ var CreateAmtRemoteTerminal = function (divid) {
case 2:
if (b >= '0' && b <= '9') {
// This is a number
if (!_escNumber[_escNumberPtr]) {
_escNumber[_escNumberPtr] = (b - '0');
}
else {
_escNumber[_escNumberPtr] = ((_escNumber[_escNumberPtr] * 10) + (b - '0'));
}
if (!_escNumber[_escNumberPtr]) { _escNumber[_escNumberPtr] = (b - '0'); }
else { _escNumber[_escNumberPtr] = ((_escNumber[_escNumberPtr] * 10) + (b - '0')); }
break;
}
else if (b == ';') {
} else if (b == ';') {
// New number
_escNumberPtr++;
break;
}
else {
} else if (b == '?') {
_escNumberMode = 1;
break;
} else {
// Process Escape Sequence
if (!_escNumber[0]) _escNumber[0] = 0;
_ProcessEscapeHandler(b, _escNumber, _escNumberPtr + 1);
_ProcessEscapeHandler(b, _escNumber, _escNumberPtr + 1, _escNumberMode);
_termstate = 0;
}
break;
@ -129,151 +144,207 @@ var CreateAmtRemoteTerminal = function (divid) {
}
}
function _ProcessEscapeHandler(code, args, argslen) {
var i;
switch (code) {
case 'c': // ResetDevice
// Reset
obj.TermResetScreen();
break;
case 'A': // Move cursor up n lines
if (argslen == 1) {
_termy -= args[0];
if (_termy < 0) _termy = 0;
}
break;
case 'B': // Move cursor down n lines
if (argslen == 1) {
_termy += args[0];
if (_termy > obj.height) _termy = obj.height;
}
break;
case 'C': // Move cursor right n lines
if (argslen == 1) {
_termx += args[0];
if (_termx > obj.width) _termx = obj.width;
}
break;
case 'D': // Move cursor left n lines
if (argslen == 1) {
_termx -= args[0];
if (_termx < 0) _termx = 0;
}
break;
case 'd': // Set cursor to line n
if (argslen == 1) {
_termy = args[0] - 1;
if (_termy > obj.height) _termy = obj.height;
if (_termy < 0) _termy = 0;
}
break;
case 'G': // Set cursor to col n
if (argslen == 1) {
_termx = args[0] - 1;
if (_termx < 0) _termx = 0;
if (_termx > 79) _termx = 79;
}
break;
case 'J': // ClearScreen:
if (argslen == 1 && args[0] == 2) {
obj.TermClear((_TermCurrentBColor << 12) + (_TermCurrentFColor << 6)); // Erase entire screen
_termx = 0;
_termy = 0;
}
else if (argslen == 0 || argslen == 1 && args[0] == 0) // Erase cursor down
{
_EraseCursorToEol();
for (i = _termy + 1; i < obj.height; i++) _EraseLine(i);
}
else if (argslen == 1 && args[0] == 1) // Erase cursor up
{
_EraseCursorToEol();
for (i = 0; i < _termy - 1; i++) _EraseLine(i);
}
break;
case 'H': // MoveCursor:
if (argslen == 2) {
if (args[0] < 1) args[0] = 1;
if (args[1] < 1) args[1] = 1;
if (args[0] > obj.height) args[0] = obj.height;
if (args[1] > obj.width) args[1] = obj.width;
_termy = args[0] - 1;
_termx = args[1] - 1;
}
else {
_termy = 0;
_termx = 0;
}
break;
case 'm': // ScreenAttribs:
// Change attributes
for (i = 0; i < argslen; i++) {
if (!args[i] || args[i] == 0) {
// Reset Attributes
_TermCurrentBColor = 0;
_TermCurrentFColor = 7;
_TermCurrentReverse = 0;
function _ProcessEscapeHandler(code, args, argslen, mode) {
//console.log('process', code, args, mode);
if (mode == 1) {
switch (code) {
case 'l': // Hide the cursor
if (args[0] == 25) { _cursorVisible = false; }
break;
case 'h': // Show the cursor
if (args[0] == 25) { _cursorVisible = true; }
break;
}
} else if (mode == 0) {
var i;
switch (code) {
case 'c': // ResetDevice
// Reset
obj.TermResetScreen();
break;
case 'A': // Move cursor up n lines
if (argslen == 1) {
if (args[0] == 0) { _termy--; } else { _termy -= args[0]; }
if (_termy < 0) _termy = 0;
}
else if (args[i] == 1) {
// Bright
if (_TermCurrentFColor < 8) _TermCurrentFColor += 8;
break;
case 'B': // Move cursor down n lines
if (argslen == 1) {
if (args[0] == 0) { _termy++; } else { _termy += args[0]; }
if (_termy > obj.height) _termy = obj.height;
}
else if (args[i] == 2 || args[i] == 22) {
// Dim
if (_TermCurrentFColor >= 8) _TermCurrentFColor -= 8;
break;
case 'C': // Move cursor right n lines
if (argslen == 1) {
if (args[0] == 0) { _termx++; } else { _termx += args[0]; }
if (_termx > obj.width) _termx = obj.width;
}
else if (args[i] == 7) {
// Set Reverse attribute true
_TermCurrentReverse = 2;
break;
case 'D': // Move cursor left n lines
if (argslen == 1) {
if (args[0] == 0) { _termx--; } else { _termx -= args[0]; }
if (_termx < 0) _termx = 0;
}
else if (args[i] == 27) {
// Set Reverse attribute false
_TermCurrentReverse = 0;
break;
case 'd': // Set cursor to line n
if (argslen == 1) {
_termy = args[0] - 1;
if (_termy > obj.height) _termy = obj.height;
if (_termy < 0) _termy = 0;
}
else if (args[i] >= 30 && args[i] <= 37) {
// Set Foreground Color
var bright = (_TermCurrentFColor >= 8);
_TermCurrentFColor = (args[i] - 30);
if (bright && _TermCurrentFColor <= 8) _TermCurrentFColor += 8;
break;
case 'G': // Set cursor to col n
if (argslen == 1) {
_termx = args[0] - 1;
if (_termx < 0) _termx = 0;
if (_termx > 79) _termx = 79;
}
else if (args[i] >= 40 && args[i] <= 47) {
// Set Background Color
_TermCurrentBColor = (args[i] - 40);
break;
case 'P': // Delete X Character(s), default 1 char
var x = 1;
if (argslen == 1) { x = args[0]; }
for (i = _termx; i < (_termx + x) ; i++) { _tscreen[_termy][i] = ' '; _scratt[_termy][i] = (7 << 6); }
break;
case 'L': // Insert X Line(s), default 1 char
var linecount = 1;
if (argslen == 1) { linecount = args[0]; }
if (linecount == 0) { linecount = 1; }
for (y = _scrollRegion[1]; y >= _termy + linecount; y--) {
_tscreen[y] = _tscreen[y - linecount];
_scratt[y] = _scratt[y - linecount];
}
else if (args[i] >= 90 && args[i] <= 99) {
// Set Bright Foreground Color
_TermCurrentFColor = (args[i] - 82);
for (y = _termy; y < _termy + linecount; y++) {
_tscreen[y] = [];
_scratt[y] = [];
for (x = 0; x < obj.width; x++) { _tscreen[y][x] = ' '; _scratt[y][x] = (7 << 6); }
}
else if (args[i] >= 100 && args[i] <= 109) {
// Set Bright Background Color
_TermCurrentBColor = (args[i] - 92);
break;
case 'J': // ClearScreen:
if (argslen == 1 && args[0] == 2) {
obj.TermClear((_TermCurrentBColor << 12) + (_TermCurrentFColor << 6)); // Erase entire screen
_termx = 0;
_termy = 0;
}
}
break;
case 'K': // EraseLine:
if (argslen == 0 || (argslen == 1 && (!args[0] || args[0] == 0))) {
_EraseCursorToEol(); // Erase from the cursor to the end of the line
}
else if (argslen == 1) {
if (args[0] == 1) // Erase from the beginning of the line to the cursor
else if (argslen == 0 || argslen == 1 && args[0] == 0) // Erase cursor down
{
_EraseBolToCursor();
_EraseCursorToEol();
for (i = _termy + 1; i < obj.height; i++) _EraseLine(i);
}
else if (args[0] == 2) // Erase the line with the cursor
else if (argslen == 1 && args[0] == 1) // Erase cursor up
{
_EraseLine(_termy);
_EraseCursorToEol();
for (i = 0; i < _termy - 1; i++) _EraseLine(i);
}
}
break;
case 'h': // EnableLineWrap:
_TermLineWrap = true;
break;
case 'l': // DisableLineWrap:
_TermLineWrap = false;
break;
default:
//if (code != '@') alert(code);
break;
break;
case 'H': // MoveCursor:
if (argslen == 2) {
if (args[0] < 1) args[0] = 1;
if (args[1] < 1) args[1] = 1;
if (args[0] > obj.height) args[0] = obj.height;
if (args[1] > obj.width) args[1] = obj.width;
_termy = args[0] - 1;
_termx = args[1] - 1;
}
else {
_termy = 0;
_termx = 0;
}
break;
case 'm': // ScreenAttribs:
// Change attributes
for (i = 0; i < argslen; i++) {
if (!args[i] || args[i] == 0) {
// Reset Attributes
_TermCurrentBColor = 0;
_TermCurrentFColor = 7;
_TermCurrentReverse = 0;
}
else if (args[i] == 1) {
// Bright
if (_TermCurrentFColor < 8) _TermCurrentFColor += 8;
}
else if (args[i] == 2 || args[i] == 22) {
// Dim
if (_TermCurrentFColor >= 8) _TermCurrentFColor -= 8;
}
else if (args[i] == 7) {
// Set Reverse attribute true
_TermCurrentReverse = 2;
}
else if (args[i] == 27) {
// Set Reverse attribute false
_TermCurrentReverse = 0;
}
else if (args[i] >= 30 && args[i] <= 37) {
// Set Foreground Color
var bright = (_TermCurrentFColor >= 8);
_TermCurrentFColor = (args[i] - 30);
if (bright && _TermCurrentFColor <= 8) _TermCurrentFColor += 8;
}
else if (args[i] >= 40 && args[i] <= 47) {
// Set Background Color
_TermCurrentBColor = (args[i] - 40);
}
else if (args[i] >= 90 && args[i] <= 99) {
// Set Bright Foreground Color
_TermCurrentFColor = (args[i] - 82);
}
else if (args[i] >= 100 && args[i] <= 109) {
// Set Bright Background Color
_TermCurrentBColor = (args[i] - 92);
}
}
break;
case 'K': // EraseLine:
if (argslen == 0 || (argslen == 1 && (!args[0] || args[0] == 0))) {
_EraseCursorToEol(); // Erase from the cursor to the end of the line
} else if (argslen == 1) {
if (args[0] == 1) { // Erase from the beginning of the line to the cursor
_EraseBolToCursor();
} else if (args[0] == 2) { // Erase the line with the cursor
_EraseLine(_termy);
}
}
break;
case 'h': // EnableLineWrap:
_TermLineWrap = true;
break;
case 'l': // DisableLineWrap:
_TermLineWrap = false;
break;
case 'r': // Set the scroll region
if (argslen == 2) { _scrollRegion = [args[0] - 1, args[1] - 1]; }
if (_scrollRegion[0] < 0) { _scrollRegion[0] = 0; }
if (_scrollRegion[0] > 24) { _scrollRegion[0] = 24; }
if (_scrollRegion[1] < 0) { _scrollRegion[1] = 0; }
if (_scrollRegion[1] > 24) { _scrollRegion[1] = 24; }
if (_scrollRegion[0] > _scrollRegion[1]) { _scrollRegion[0] = _scrollRegion[1]; }
break;
case 'S': // Scroll up the scroll region X lines, default 1
var x = 1;
if (argslen == 1) { x = args[0] }
for (var y = _scrollRegion[0]; y <= _scrollRegion[1] - x; y++) {
for (var z = 0; z < obj.width; z++) { _tscreen[y][z] = _tscreen[y + x][z]; _scratt[y][z] = _scratt[y + x][z]; }
}
for (var y = _scrollRegion[1] - x + 1; y < _scrollRegion[1]; y++) {
for (var z = 0; z < obj.width; z++) { _tscreen[y][z] = ' '; _scratt[y][z] = (7 << 6); }
}
break;
case 'T': // Scroll down the scroll region X lines, default 1
var x = 1;
if (argslen == 1) { x = args[0] }
for (var y = _scrollRegion[1]; y > _scrollRegion[0] + x; y--) {
for (var z = 0; z < obj.width; z++) { _tscreen[y][z] = _tscreen[y - x][z]; _scratt[y][z] = _scratt[y - x][z]; }
}
for (var y = _scrollRegion[0] + x; y > _scrollRegion[0]; y--) {
for (var z = 0; z < obj.width; z++) { _tscreen[y][z] = ' '; _scratt[y][z] = (7 << 6); }
}
break;
default:
//if (code != '@') alert(code);
//console.log('unknown terminal code', code, args, mode);
break;
}
}
}
@ -366,6 +437,7 @@ var CreateAmtRemoteTerminal = function (divid) {
function _ProcessVt100Char(c) {
if (c == '\0' || c.charCodeAt() == 7) return; // Ignore null & bell
var ch = c.charCodeAt();
//console.log('_ProcessVt100Char', ch, c);
// ###BEGIN###{Terminal-Enumation-All}
// UTF8 Terminal
@ -401,8 +473,8 @@ var CreateAmtRemoteTerminal = function (divid) {
switch (c) {
case '\b': // Backspace
if (_termx > 0) {
_termx = _termx - 1;
_TermDrawChar(' ');
_termx--;
if (_backSpaceErase) { _TermDrawChar(' '); }
}
break;
case '\t': // tab
@ -411,12 +483,12 @@ var CreateAmtRemoteTerminal = function (divid) {
break;
case '\n': // Linefeed
_termy++;
if (_termy > (obj.height - 1)) {
if (_termy > _scrollRegion[1]) {
// Move everything up one line
_TermMoveUp(1);
_termy = (obj.height - 1);
_termy = _scrollRegion[1];
}
if (obj.lineFeed = '\n') { _termx = 0; } // *** If we are in Linux mode, \n will also return the cursor to the first col
if (obj.lineFeed = '\r') { _termx = 0; } // *** If we are in Linux mode, \n will also return the cursor to the first col
break;
case '\r': // Carriage Return
_termx = 0;
@ -452,14 +524,16 @@ var CreateAmtRemoteTerminal = function (divid) {
_TermCurrentReverse = 0;
_TermCurrentFColor = 7;
_TermCurrentBColor = 0;
_TermLineWrap = true;
_termx = 0;
_termy = 0;
_TermLineWrap = _cursorVisible = true;
_termx = _termy = 0;
_backSpaceErase = false;
_scrollRegion = [0, 24];
_altKeypadMode = false;
obj.TermClear(7 << 6);
}
function _EraseCursorToEol() {
var t = (_TermCurrentBColor << 12);
var t = (_TermCurrentFColor << 6) + (_TermCurrentBColor << 12) + _TermCurrentReverse;
for (var x = _termx; x < obj.width; x++) {
_tscreen[_termy][x] = ' ';
_scratt[_termy][x] = t;
@ -467,7 +541,7 @@ var CreateAmtRemoteTerminal = function (divid) {
}
function _EraseBolToCursor() {
var t = (_TermCurrentBColor << 12);
var t = (_TermCurrentFColor << 6) + (_TermCurrentBColor << 12) + _TermCurrentReverse;
for (var x = 0; x < _termx; x++) {
_tscreen[_termy][x] = ' ';
_scratt[_termy][x] = t;
@ -475,7 +549,7 @@ var CreateAmtRemoteTerminal = function (divid) {
}
function _EraseLine(line) {
var t = (_TermCurrentBColor << 12);
var t = (_TermCurrentFColor << 6) + (_TermCurrentBColor << 12) + _TermCurrentReverse;
for (var x = 0; x < obj.width; x++) {
_tscreen[line][x] = ' ';
_scratt[line][x] = t;
@ -487,11 +561,11 @@ var CreateAmtRemoteTerminal = function (divid) {
function _TermMoveUp(linecount) {
var x, y;
for (y = 0; y < obj.height - linecount; y++) {
for (y = _scrollRegion[0]; y <= _scrollRegion[1] - linecount; y++) {
_tscreen[y] = _tscreen[y + linecount];
_scratt[y] = _scratt[y + linecount];
}
for (y = obj.height - linecount; y < obj.height; y++) {
for (y = _scrollRegion[1] - linecount + 1; y <= _scrollRegion[1]; y++) {
_tscreen[y] = [];
_scratt[y] = [];
for (x = 0; x < obj.width; x++) {
@ -504,7 +578,7 @@ var CreateAmtRemoteTerminal = function (divid) {
obj.TermHandleKeys = function (e) {
if (!e.ctrlKey) {
if (e.which == 127) obj.TermSendKey(8);
else if (e.which == 13) obj.TermSendKeys(obj.lineFeed);
else if (e.which == 13) { obj.TermSendKeys(obj.lineFeed); }
else if (e.which != 0) obj.TermSendKey(e.which);
return false;
}
@ -527,10 +601,19 @@ var CreateAmtRemoteTerminal = function (divid) {
return;
}
if (e.which == 27) { obj.TermSendKeys(String.fromCharCode(27)); return true; }; // ESC
if (e.which == 37) { obj.TermSendKeys(String.fromCharCode(27, 91, 68)); return true; }; // Left
if (e.which == 38) { obj.TermSendKeys(String.fromCharCode(27, 91, 65)); return true; }; // Up
if (e.which == 39) { obj.TermSendKeys(String.fromCharCode(27, 91, 67)); return true; }; // Right
if (e.which == 40) { obj.TermSendKeys(String.fromCharCode(27, 91, 66)); return true; }; // Down
if (_altKeypadMode == true) {
if (e.which == 37) { obj.TermSendKeys(String.fromCharCode(27, 79, 68)); return true; }; // Left
if (e.which == 38) { obj.TermSendKeys(String.fromCharCode(27, 79, 65)); return true; }; // Up
if (e.which == 39) { obj.TermSendKeys(String.fromCharCode(27, 79, 67)); return true; }; // Right
if (e.which == 40) { obj.TermSendKeys(String.fromCharCode(27, 79, 66)); return true; }; // Down
} else {
if (e.which == 37) { obj.TermSendKeys(String.fromCharCode(27, 91, 68)); return true; }; // Left
if (e.which == 38) { obj.TermSendKeys(String.fromCharCode(27, 91, 65)); return true; }; // Up
if (e.which == 39) { obj.TermSendKeys(String.fromCharCode(27, 91, 67)); return true; }; // Right
if (e.which == 40) { obj.TermSendKeys(String.fromCharCode(27, 91, 66)); return true; }; // Down
}
if (e.which == 9) { obj.TermSendKeys("\t"); if (e.preventDefault) e.preventDefault(); if (e.stopPropagation) e.stopPropagation(); return true; }; // TAB
// F1 to F12 keys
@ -569,12 +652,12 @@ var CreateAmtRemoteTerminal = function (divid) {
for (var y = 0; y < obj.height; ++y) {
for (var x = 0; x < obj.width; ++x) {
newat = _scratt[y][x];
if (_termx == x && _termy == y) { newat |= _VTREVERSE; } // If this is the cursor location, reverse the color.
if (_termx == x && _termy == y && _cursorVisible) { newat |= _VTREVERSE; } // If this is the cursor location, reverse the color.
if (newat != oldat) {
buf += closetag;
closetag = '';
x1 = 6; x2 = 12;
if (newat & _VTREVERSE) { x1 = 12; x2 = 6;}
if (newat & _VTREVERSE) { x1 = 12; x2 = 6; }
buf += '<span style="color:#' + _TermColors[(newat >> x1) & 0x3F] + ';background-color:#' + _TermColors[(newat >> x2) & 0x3F];
if (newat & _VTUNDERLINE) buf += ';text-decoration:underline';
buf += ';">';
@ -584,17 +667,11 @@ var CreateAmtRemoteTerminal = function (divid) {
c = _tscreen[y][x];
switch (c) {
case '&':
buf += '&amp;'; break;
case '<':
buf += '&lt;'; break;
case '>':
buf += '&gt;'; break;
case ' ':
buf += '&nbsp;'; break;
default:
buf += c;
break;
case '&': buf += '&amp;'; break;
case '<': buf += '&lt;'; break;
case '>': buf += '&gt;'; break;
case ' ': buf += '&nbsp;'; break;
default: buf += c; break;
}
}
if (y != (obj.height - 1)) buf += '<br>';

View File

@ -180,7 +180,7 @@
margin: 0;
padding: 0 15px;
background-color: #fff;
max-height: calc(100vh - 111px);
/*max-height: calc(100vh - 111px);*/
min-width: unset;
}
.room4submenu {
@ -188,7 +188,7 @@
}
.fullscreen #column_l {
height: calc(100vh - 135px);
height: calc(100vh - 111px);
width: calc(100% - 30px);
overflow-y: auto;
}

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,
"_MpsTlsOffload": true,
"_No2FactorAuth": true,
"_WebRtConfig": {
"iceServers": [
{ "urls": "stun:stun.services.mozilla.com" },

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -1216,6 +1216,7 @@
QV('authAppSetupCheck', userinfo.otpsecret == 1);
QV('authKeySetupCheck', userinfo.otphkeys > 0);
QV('authCodesSetupCheck', userinfo.otpkeys > 0);
masterUpdate(4 + 128);
if (typeof userinfo.passchange == 'number') {
if (userinfo.passchange == -1) { QH('p2nextPasswordUpdateTime', ' - Reset on next login.'); }
@ -1655,6 +1656,7 @@
}
}
masterUpdate(4 + 128);
if (currentNode && (currentNode.meshid == message.event.meshid)) { refreshDevice(currentNode._id); }
//meshserver.send({ action: 'files' }); // TODO: Why do we need to do this??
// If we are looking at a mesh that is now deleted, move back to "My Account"
@ -1925,8 +1927,8 @@
var meshrights = mesh.links['user/' + domain + '/' + userinfo.name.toLowerCase()].rights;
var inputAllowed = ((meshrights == 0xFFFFFFFF) || (((meshrights & 8) != 0) && ((meshrights & 256) == 0)));
if (inputAllowed == false) return false;
var limitedInputAllowed = ((meshrights != 0xFFFFFFFF) || (((meshrights & 8) != 0) && ((meshrights & 256) == 0) && ((meshrights & 4096) != 0)));
if (limitedInputAllowed == true) { if ((e.altKey == true) || (e.ctrlKey == true) || (e.keyCode < 32) || (e.keyCode > 90)) return false; }
var limitedInputAllowed = ((meshrights != 0xFFFFFFFF) && (((meshrights & 8) != 0) && ((meshrights & 256) == 0) && ((meshrights & 4096) != 0)));
if (limitedInputAllowed == true) { if ((e.altKey == true) || (e.ctrlKey == true) || ((e.keyCode < 32) && (e.keyCode != 8) && (e.keyCode != 13)) || (e.keyCode > 90)) return false; }
}
return desktop.m.handleKeys(e);
}
@ -1984,8 +1986,8 @@
var meshrights = mesh.links['user/' + domain + '/' + userinfo.name.toLowerCase()].rights;
var inputAllowed = ((meshrights == 0xFFFFFFFF) || (((meshrights & 8) != 0) && ((meshrights & 256) == 0)));
if (inputAllowed == false) return false;
var limitedInputAllowed = ((meshrights != 0xFFFFFFFF) || (((meshrights & 8) != 0) && ((meshrights & 256) == 0) && ((meshrights & 4096) != 0)));
if (limitedInputAllowed == true) { if ((e.altKey == true) || (e.ctrlKey == true) || (e.keyCode < 32) || (e.keyCode > 90)) return false; }
var limitedInputAllowed = ((meshrights != 0xFFFFFFFF) && (((meshrights & 8) != 0) && ((meshrights & 256) == 0) && ((meshrights & 4096) != 0)));
if (limitedInputAllowed == true) { if ((e.altKey == true) || (e.ctrlKey == true) || ((e.keyCode < 32) && (e.keyCode != 8) && (e.keyCode != 13)) || (e.keyCode > 90)) return false; }
}
return desktop.m.handleKeyDown(e);
}
@ -2018,8 +2020,8 @@
var meshrights = mesh.links['user/' + domain + '/' + userinfo.name.toLowerCase()].rights;
var inputAllowed = ((meshrights == 0xFFFFFFFF) || (((meshrights & 8) != 0) && ((meshrights & 256) == 0)));
if (inputAllowed == false) return false;
var limitedInputAllowed = ((meshrights != 0xFFFFFFFF) || (((meshrights & 8) != 0) && ((meshrights & 256) == 0) && ((meshrights & 4096) != 0)));
if (limitedInputAllowed == true) { if ((e.altKey == true) || (e.ctrlKey == true) || (e.keyCode < 32) || (e.keyCode > 90)) return false; }
var limitedInputAllowed = ((meshrights != 0xFFFFFFFF) && (((meshrights & 8) != 0) && ((meshrights & 256) == 0) && ((meshrights & 4096) != 0)));
if (limitedInputAllowed == true) { if ((e.altKey == true) || (e.ctrlKey == true) || ((e.keyCode < 32) && (e.keyCode != 8) && (e.keyCode != 13)) || (e.keyCode > 90)) return false; }
}
return desktop.m.handleKeyUp(e);
}
@ -2259,8 +2261,8 @@
// Add a "Add Device Group" option
r += '<div style=border-top-style:solid;border-top-width:1px;border-top-color:#DDDDDD;cursor:pointer;font-size:10px>';
if ((view < 3) && (sort == 0) && (meshcount > 0)) { r += '<a onclick=account_createMesh() title="Create a new group of devices." style=cursor:pointer>Add Device Group</a>&nbsp'; }
r += '<a onclick=p10showMeshCmdDialog(0) style=cursor:pointer title="Download MeshCmd, a command line tool that performs many functions.">MeshCmd</a></div>';
if ((view < 3) && (sort == 0) && (meshcount > 0) && ((userinfo.siteadmin == 0xFFFFFFFF) || ((userinfo.siteadmin & 64) == 0))) { r += '<a onclick=account_createMesh() title="Create a new group of devices." style=cursor:pointer>Add Device Group</a>&nbsp'; }
if ((userinfo.siteadmin == 0xFFFFFFFF) || ((userinfo.siteadmin & 128) == 0)) { r += '<a onclick=p10showMeshCmdDialog(0) style=cursor:pointer title="Download MeshCmd, a command line tool that performs many functions.">MeshCmd</a></div>'; }
r += '</div><br/>';
QH('xdevices', r);
@ -3539,7 +3541,6 @@
//
// MY DEVICE
//
function refreshDevice(nodeid) {
if (!currentNode || currentNode._id != nodeid) return;
gotoDevice(nodeid, xxcurrentView, true);
@ -3577,7 +3578,7 @@
// Add node name
var nname = EscapeHtml(node.name);
if (nname.length == 0) { nname = '<i>None</i>'; }
if ((meshrights & 4) != 0) { nname = '<span title="Click here to edit the server-side device name" onclick=showEditNodeValueDialog(0) style=cursor:pointer>' + nname + ' <img class=hoverButton src="images/link5.png" /></span>'; }
if (((meshrights & 4) != 0) && ((!mesh.flags) || ((mesh.flags & 2) == 0))) { nname = '<span title="Click here to edit the server-side device name" onclick=showEditNodeValueDialog(0) style=cursor:pointer>' + nname + ' <img class=hoverButton src="images/link5.png" /></span>'; }
QH('p10deviceName', nname);
QH('p11deviceName', nname);
QH('p12deviceName', nname);
@ -4850,7 +4851,7 @@
terminal = CreateAgentRedirect(meshserver, CreateAmtRemoteTerminal('Term'), serverPublicNamePort, authCookie);
terminal.debugmode = debugmode;
terminal.m.debugmode = debugmode;
terminal.m.lineFeed = ([1,2,3,4,21,22].indexOf(currentNode.agent.id) >= 0)?'\r\n':'\n'; // On windows, send \r\n, on Linux only \n
terminal.m.lineFeed = ([1, 2, 3, 4, 21, 22].indexOf(currentNode.agent.id) >= 0) ? '\r\n' : '\r'; // On windows, send \r\n, on Linux only \r
terminal.attemptWebRTC = attemptWebRTC;
terminal.onStateChanged = onTerminalStateChange;
terminal.Start(terminalNode._id);
@ -5780,6 +5781,9 @@
function account_createMesh() {
if (xxdialogMode) return;
// Check if we are disallowed from creating a device group
if ((userinfo.siteadmin != 0xFFFFFFFF) && ((userinfo.siteadmin & 64) != 0)) { setDialogMode(2, "New Device Group", 1, null, "This account does not have the rights to create a new device group."); return; }
// Check if we are allowed to create a new device group
if ((userinfo.emailVerified !== true) && (serverinfo.emailcheck == true) && (userinfo.siteadmin != 0xFFFFFFFF)) { setDialogMode(2, "New Device Group", 1, null, "Unable to create a new device group until a email address is verified. This is required for password recovery. Go to the \"My Account\" tab to change and verify an email address."); return; }
@ -5934,7 +5938,10 @@
// Display features
var meshFeatures = [];
if (currentMesh.flags) { if (currentMesh.flags & 1) { meshFeatures.push('Auto-Remove'); } }
if (currentMesh.flags) {
if (currentMesh.flags & 1) { meshFeatures.push('Auto-Remove'); }
if (currentMesh.flags & 2) { meshFeatures.push('Hostname Sync'); }
}
meshFeatures = meshFeatures.join(', ');
if (meshFeatures == '') { meshFeatures = '<i>None</i>'; }
x += addHtmlValue('Features', addLinkConditional(meshFeatures, 'p20editmeshfeatures()', (meshrights & 1) != 0));
@ -6101,12 +6108,14 @@
if (xxdialogMode) return;
var flags = (currentMesh.flags)?currentMesh.flags:0;
var x = "<div><input type=checkbox id=d20flag1 " + ((flags & 1)?'checked':'') + ">Remove device on disconnect<br></div>";
x += "<div><input type=checkbox id=d20flag2 " + ((flags & 2) ? 'checked' : '') + ">Sync server device name to hostname<br></div>";
setDialogMode(2, "Edit Device Group Features", 3, p20editmeshfeaturesEx, x);
}
function p20editmeshfeaturesEx() {
var flags = 0;
if (Q('d20flag1').checked) { flags += 1; }
if (Q('d20flag2').checked) { flags += 2; }
meshserver.send({ action: 'editmesh', meshid: currentMesh._id, flags: flags });
}
@ -6594,7 +6603,7 @@
if (self) { permissions += "<a style=cursor:pointer onclick=showUserAdminDialog(event,\"" + encodeURIComponent(user._id) + "\")>"; }
if ((user.siteadmin != null) && ((user.siteadmin & 32) != 0) && (user.siteadmin != 0xFFFFFFFF)) { permissions += "Locked,&nbsp;"; }
permissions += "<span title='Server Permissions'>";
if ((user.siteadmin == null) || (user.siteadmin == 0) || (user.siteadmin == 32)) {
if ((user.siteadmin == null) || (user.siteadmin == 0) || ((user.siteadmin & (0xFFFFFFFF - 224)) == 0)) {
permissions += "User";
} else if (user.siteadmin == 8) {
permissions += "User + Files";
@ -6603,6 +6612,7 @@
} else {
permissions += "Partial";
}
if ((user.siteadmin != null) && (user.siteadmin != 0xFFFFFFFF) && ((user.siteadmin & (64 + 128)) != 0)) { permissions += "*"; }
permissions += "</span>";
//if ((user.quota != null) && ((user.siteadmin & 8) != 0)) { msg += ", " + (user.quota / 1024) + " k"; }
if (self) { permissions += "</a>"; }
@ -6714,6 +6724,8 @@
x += '<input type=checkbox onchange=showUserAdminDialogValidate() id=ua_serverupdate>Server Updates<br>';
x += '<input type=checkbox onchange=showUserAdminDialogValidate() id=ua_manageusers>Manage Users<br>';
x += '<hr/><input type=checkbox onchange=showUserAdminDialogValidate() id=ua_lockedaccount>Lock Account<br>';
x += '<input type=checkbox onchange=showUserAdminDialogValidate() id=ua_nonewgroups>No New Device Groups<br>';
x += '<input type=checkbox onchange=showUserAdminDialogValidate() id=ua_nomeshcmd>No MeshCmd<br>';
x += '</div>';
var user = users[userid.toLowerCase()];
setDialogMode(2, "Server Permissions", 3, showUserAdminDialogEx, x, user);
@ -6725,6 +6737,8 @@
Q('ua_fileaccess').checked = ((user.siteadmin != 0xFFFFFFFF) && ((user.siteadmin & 8) != 0)); // Server Files
Q('ua_serverupdate').checked = ((user.siteadmin != 0xFFFFFFFF) && ((user.siteadmin & 16) != 0)); // Server Update
Q('ua_lockedaccount').checked = ((user.siteadmin != 0xFFFFFFFF) && ((user.siteadmin & 32) != 0)); // Account locked
Q('ua_nonewgroups').checked = ((user.siteadmin != 0xFFFFFFFF) && ((user.siteadmin & 64) != 0)); // No New Groups
Q('ua_nomeshcmd').checked = ((user.siteadmin != 0xFFFFFFFF) && ((user.siteadmin & 128) != 0)); // No MeshCmd
}
QE('ua_fulladmin', userinfo.siteadmin == 0xFFFFFFFF);
QE('ua_serverbackup', userinfo.siteadmin == 0xFFFFFFFF);
@ -6732,6 +6746,9 @@
QE('ua_serverrestore', userinfo.siteadmin == 0xFFFFFFFF);
QE('ua_fileaccess', userinfo.siteadmin == 0xFFFFFFFF);
QE('ua_serverupdate', userinfo.siteadmin == 0xFFFFFFFF);
QE('ua_lockedaccount', userinfo.siteadmin == 0xFFFFFFFF);
QE('ua_nonewgroups', userinfo.siteadmin == 0xFFFFFFFF);
QE('ua_nomeshcmd', userinfo.siteadmin == 0xFFFFFFFF);
Q('ua_fileaccessquota').value = (user.quota != null)?(user.quota / 1024):'';
showUserAdminDialogValidate();
return false;
@ -6744,6 +6761,9 @@
QE('ua_serverrestore', !Q('ua_fulladmin').checked);
QE('ua_fileaccess', !Q('ua_fulladmin').checked);
QE('ua_serverupdate', !Q('ua_fulladmin').checked);
QE('ua_lockedaccount', !Q('ua_fulladmin').checked);
QE('ua_nonewgroups', !Q('ua_fulladmin').checked);
QE('ua_nomeshcmd', !Q('ua_fulladmin').checked);
QE('ua_fileaccessquota', Q('ua_fileaccess').checked && !Q('ua_fulladmin').checked);
}
}
@ -6757,6 +6777,8 @@
if (Q('ua_fileaccess').checked == true) siteadmin += 8;
if (Q('ua_serverupdate').checked == true) siteadmin += 16;
if (Q('ua_lockedaccount').checked == true) siteadmin += 32;
if (Q('ua_nonewgroups').checked == true) siteadmin += 64;
if (Q('ua_nomeshcmd').checked == true) siteadmin += 128;
}
var x = { action: 'edituser', name: user.name, siteadmin: siteadmin };
if (isNaN(quota) == false) { x.quota = (quota * 1024); }
@ -6784,16 +6806,17 @@
if (activeSessions == 0) { Q('MainUserImage').classList.add('gray'); }
// Server permissions
var msg = '', premsg = '';
if ((user.siteadmin != null) && ((user.siteadmin & 32) != 0) && (user.siteadmin != 0xFFFFFFFF)) { premsg = '<img src="images/padlock12.png" height=12 width=8 title="Account is locked" style="margin-top:2px" /> '; msg += 'Locked account, '; }
if ((user.siteadmin == null) || (user.siteadmin == 0) || (user.siteadmin == 32)) { msg += "No server rights"; } else if (user.siteadmin == 8) { msg += "Access to server files"; } else if (user.siteadmin == 0xFFFFFFFF) { msg += "Full administrator"; } else { msg += "Partial rights"; }
var msg = [], premsg = '';
if ((user.siteadmin != null) && ((user.siteadmin & 32) != 0) && (user.siteadmin != 0xFFFFFFFF)) { premsg = '<img src="images/padlock12.png" height=12 width=8 title="Account is locked" style="margin-top:2px" /> '; msg.push("Locked account"); }
if ((user.siteadmin == null) || ((user.siteadmin & (0xFFFFFFFF - 224)) == 0)) { msg.push("No server rights"); } else if (user.siteadmin == 8) { msg.push("Access to server files"); } else if (user.siteadmin == 0xFFFFFFFF) { msg.push("Full administrator"); } else { msg.push("Partial rights"); }
if ((user.siteadmin != null) && ((user.siteadmin & (64 + 128)) != 0)) { msg.push("Restrictions"); }
// Show user attributes
var x = '<div style=min-height:80px><table style=width:100%>';
var email = user.email?EscapeHtml(user.email):'<i>Not set</i>', everify = '';
if (serverinfo.emailcheck) { everify = ((user.emailVerified == true)?'<b style=color:green;cursor:pointer title="Email is verified">&#x1F5F8</b> ':'<b style=color:red;cursor:pointer title="Email not verified">&#x1F5F4</b> '); }
x += addDeviceAttribute('Email', everify + "<a style=cursor:pointer onclick=p30showUserEmailChangeDialog(event,\"" + userid + "\")>" + email + '</a> <a style=cursor:pointer onclick=doemail(event,\"' + user.email + '\")><img class=hoverButton src="images/link1.png" /></a>');
x += addDeviceAttribute('Server Rights', premsg + "<a style=cursor:pointer onclick=showUserAdminDialog(event,\"" + userid + "\")>" + msg + "</a>");
x += addDeviceAttribute('Server Rights', premsg + "<a style=cursor:pointer onclick=showUserAdminDialog(event,\"" + userid + "\")>" + msg.join(', ') + "</a>");
if (user.quota) x += addDeviceAttribute('Server Quota', EscapeHtml(parseInt(user.quota) / 1024) + ' k');
x += addDeviceAttribute('Creation', new Date(user.creation * 1000).toLocaleString());
if (user.login) x += addDeviceAttribute('Last Login', new Date(user.login * 1000).toLocaleString());
@ -7274,7 +7297,7 @@
}
};
function refreshServerTimelineStats(stats) { meshserver.send({ action: 'servertimelinestats', hours: 24 }); }
function refreshServerTimelineStats(stats) { meshserver.send({ action: 'servertimelinestats', hours: 24 * 30 }); }
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) {
@ -7479,7 +7502,7 @@
// My Server
if ((x == 6) || (x == 115)) QC('MainMenuMyServer').add(mainMenuActiveClass);
if ((x == 6) || (x == 115)) QC('LeftMenuMyServer').add(leftMenuActiveClass);
if ((x == 6) || (x == 115) || (x == 40)) QC('LeftMenuMyServer').add(leftMenuActiveClass);
// column_l max-height
if (!webPageFullScreen && x >= 10) {

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}}}";
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 hardwareKeyChallenge = '{{{hkey}}}';
var hardwareKeyChallenge = decodeURIComponent('{{{hkey}}}');
var currentpanel = 0;
function startup() {
@ -287,7 +287,34 @@
if ('{{loginmode}}' == '4') {
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) {
if ((currentpanel == 4) && authResponse.signatureData) {
Q('hwtokenInput').value = JSON.stringify(authResponse);
@ -300,7 +327,34 @@
if ('{{loginmode}}' == '5') {
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) {
if ((currentpanel == 5) && authResponse.signatureData) {
Q('resetHwtokenInput').value = JSON.stringify(authResponse);

View File

@ -8,7 +8,7 @@
<meta name="format-detection" content="telephone=no" />
<link type="text/css" href="styles/style.css" media="screen" rel="stylesheet" title="CSS" />
<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>{{{title}}} - Login</title>
</head>
<body id="body" onload="if (typeof(startup) !== 'undefined') startup();" class="arg_hide">
@ -243,7 +243,7 @@
var newAccountPass = parseInt('{{{newAccountPass}}}');
var emailCheck = ('{{{emailcheck}}}' == 'true');
var passRequirements = "{{{passRequirements}}}";
var hardwareKeyChallenge = '{{{hkey}}}';
var hardwareKeyChallenge = decodeURIComponent('{{{hkey}}}');
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 features = parseInt('{{{features}}}');
@ -284,12 +284,12 @@
if ('{{loginmode}}' == '4') {
try { if (hardwareKeyChallenge.length > 0) { hardwareKeyChallenge = JSON.parse(hardwareKeyChallenge); } else { hardwareKeyChallenge = null; } } catch (ex) { hardwareKeyChallenge = null }
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++) {
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'], }
);
}
@ -297,11 +297,11 @@
navigator.credentials.get({ publicKey: publicKeyCredentialRequestOptions }).then(
function (rawAssertion) {
var assertion = {
id: btoa(String.fromCharCode.apply(null, new Uint8Array(rawAssertion.rawId))), //base64encode(rawAssertion.rawId),
clientDataJSON: btoa(String.fromCharCode.apply(null, new Uint8Array(rawAssertion.response.clientDataJSON))), //arrayBufferToString(rawAssertion.response.clientDataJSON),
userHandle: btoa(String.fromCharCode.apply(null, new Uint8Array(rawAssertion.response.userHandle))), //base64encode(rawAssertion.response.userHandle),
signature: btoa(String.fromCharCode.apply(null, new Uint8Array(rawAssertion.response.signature))), //base64encode(rawAssertion.response.signature),
authenticatorData: btoa(String.fromCharCode.apply(null, new Uint8Array(rawAssertion.response.authenticatorData))), //base64encode(rawAssertion.response.authenticatorData)
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);
@ -323,7 +323,34 @@
if ('{{loginmode}}' == '5') {
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) {
if ((currentpanel == 5) && authResponse.signatureData) {
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);
}
for (var i in tracks) { webrtc.addTrack(tracks[i], localStream); }
})
.catch(function (err) {
}, function (err) {
displayControl(err.message + '.');
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.
// 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
obj.parent = parent;
@ -138,6 +138,10 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) {
// If this domain has a DNS and a matching DNS cert, use it. This case works for wildcard certs.
obj.webCertificateFullHashs[i] = parent.certificateOperations.getCertHashBinary(obj.certificates.dns[i].cert);
obj.webCertificateHashs[i] = parent.certificateOperations.getPublicKeyHashBinary(obj.certificates.dns[i].cert);
} else if (i != '') {
// For any other domain, use the default cert.
obj.webCertificateFullHashs[i] = obj.webCertificateFullHashs[''];
obj.webCertificateHashs[i] = obj.webCertificateHashs[''];
}
}
@ -328,12 +332,12 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) {
// Return true if this user has 2-step auth active
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
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; };
// Check hardware key
@ -396,7 +400,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]); } }
if (u2fKeys.length > 0) {
// 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;
}
}
@ -458,24 +466,28 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) {
}
}
// 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]); } }
var authdoglib = null;
try { authdoglib = require('authdog'); } catch (ex) { }
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
if (u2fKeys.length > 0) {
require('authdog').startAuthentication('https://' + obj.parent.certificates.CommonName, u2fKeys, { requestId: 0, timeoutSeconds: 60 }).then(function (registrationRequest) {
// Save authentication request to session for later use
req.session.u2fchallenge = registrationRequest;
// Generate a U2F challenge
if (u2fKeys.length > 0) {
authdoglib.startAuthentication('https://' + obj.parent.certificates.CommonName, u2fKeys, { requestId: 0, timeoutSeconds: 60 }).then(function (registrationRequest) {
// Save authentication request to session for later use
req.session.u2fchallenge = registrationRequest;
// Send authentication request to client
func(JSON.stringify(registrationRequest));
}, function (error) {
// Handle authentication request error
// Send authentication request to client
func(JSON.stringify(registrationRequest));
}, function (error) {
// Handle authentication request error
func('');
});
} else {
func('');
});
} else {
func('');
}
}
} else {
func('');
@ -1149,19 +1161,19 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) {
if (obj.args.nousers == true) { features += 0x00000004; } // Single user mode
if (domain.userQuota == -1) { features += 0x00000008; } // No server files mode
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.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.allowhighqualitydesktop == true) { features += 0x00000200; } // Enable AllowHighQualityDesktop (Default false)
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 ((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.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.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
const authCookie = obj.parent.encodeCookie({ userid: user._id, domainid: domain.id }, obj.parent.loginCookieEncryptionKey);
@ -1187,9 +1199,9 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) {
// If this is a 2 factor auth request, look for a hardware key challenge.
// Normal login 2 factor request
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) {
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;
}
}
@ -1202,7 +1214,7 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) {
} else {
var user = obj.users[docs[0]._id];
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 {
req.session = null;
res.redirect(domain.url);
@ -1240,14 +1252,14 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) {
if (obj.args.minify && !req.query.nominify) {
// Try to server the minified version if we can.
try {
res.render(obj.path.join(obj.parent.webViewsPath, isMobileBrowser(req) ? '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) {
// 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 {
// 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 });
}
/*