diff --git a/agents/MeshCmd-signed.exe b/agents/MeshCmd-signed.exe index db912e6d..539c87a8 100644 Binary files a/agents/MeshCmd-signed.exe and b/agents/MeshCmd-signed.exe differ diff --git a/agents/MeshCmd64-signed.exe b/agents/MeshCmd64-signed.exe index 602dc320..e15d5a3c 100644 Binary files a/agents/MeshCmd64-signed.exe and b/agents/MeshCmd64-signed.exe differ diff --git a/agents/meshcmd.js b/agents/meshcmd.js index a6133d99..0227f84d 100644 --- a/agents/meshcmd.js +++ b/agents/meshcmd.js @@ -198,6 +198,7 @@ function run(argv) { console.log(' AmtScan - Search local network for Intel AMT devices.'); console.log(' AmtWifi - Intel AMT Wifi interface settings.'); console.log(' AmtWake - Intel AMT Wake Alarms.'); + console.log(' AmtRPE - Intel AMT Remote Platform Erase.'); console.log('\r\nHelp on a specific action using:\r\n'); console.log(' meshcmd help [action]'); exit(1); return; @@ -387,6 +388,21 @@ function run(argv) { console.log(' --interval (dd-hh-mm) Optional alarm interval in days-hours-minutes format, default is alarm once.'); console.log(' --deletewhendone Indicates alarm is removed once triggered, default is to no remove.'); console.log(' --del [alarm-name] Remove a wake alarm'); + } else if (action == 'amtrpe') { + console.log('AmtRPE is used to erase some elements of a remote Intel AMT platform. Example usage:\r\n\r\n meshcmd amtrpe --host 1.2.3.4 --user admin --pass mypassword'); + console.log('\r\nRequired arguments:\r\n'); + console.log(' --host [hostname] The IP address or DNS name of Intel AMT, 127.0.0.1 is default.'); + console.log(' --pass [password] The Intel AMT login password.'); + console.log('\r\nOptional arguments:\r\n'); + console.log(' --user [username] The Intel AMT login username, admin is default.'); + console.log(' --tls Specifies that TLS must be used.'); + console.log(' --reset / --poweron Power action to perform on Intel AMT device.'); + console.log(' --pyrite [PSID] Perform pyrite revert.'); + console.log(' --ssd [Password] Perform secure erase all SSDs.'); + console.log(' --tpm Perform TPM Clear.'); + console.log(' --nvm Perform clear BIOS NVM variables.'); + console.log(' --bios Perform BIOS reload of golden configuration.'); + console.log(' --csme Perform CSME unconfigure.'); } else { actions.shift(); console.log('Invalid action, usage:\r\n\r\n meshcmd help [action]\r\n\r\nValid actions are: ' + actions.join(', ') + '.'); @@ -718,6 +734,11 @@ function run(argv) { if (((typeof args.date != 'string') || args.data == '')) { console.log("Wake alarm date is required (--date [yyyy-mm-dd])."); exit(1); return; } } performAmtWakeConfig(args); + } else if (settings.action == 'amtrpe') { // Perform Intel AMT remote platform erase operations + if (settings.hostname == null) { settings.hostname = '127.0.0.1'; } + 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.username == null) || (typeof settings.username != 'string') || (settings.username == '')) { settings.username = 'admin'; } + performAmtPlatformErase(args); } else if (settings.action == 'amtfeatures') { // Perform remote Intel AMT feature configuration operation if (settings.hostname == null) { settings.hostname = '127.0.0.1'; } if ((settings.password == null) || (typeof settings.password != 'string') || (settings.password == '')) { console.log('No or invalid \"password\" specified, use --password [password].'); exit(1); return; } @@ -2534,6 +2555,138 @@ function performAmtWakeConfig1(stack, name, response, status, args) { } } + +// +// Intel AMT Remote Platform Erase +// + +function performAmtPlatformErase(args) { + var transport = require('amt-wsman-duk'); + var wsman = require('amt-wsman'); + var amt = require('amt'); + wsstack = new wsman(transport, settings.hostname, settings.tls ? 16993 : 16992, settings.username, settings.password, settings.tls); + amtstack = new amt(wsstack); + amtstack.BatchEnum(null, ['*CIM_BootService', '*AMT_BootCapabilities'], performAmtPlatformErase1, args); +} + +function performAmtPlatformErase1(stack, name, response, status, args) { + debug(0, "performAmtPlatformErase1(" + status + "): " + JSON.stringify(response, null, 2)); + if (status == 200) { + // See that RPE featues are supported + var platfromEraseSupport = response['AMT_BootCapabilities'].response['PlatformErase']; + if (platfromEraseSupport == null) { console.log("Remote Platfrom Erase (RPE) is not supported on this platform"); process.exit(1); return; } + var supportedRpeFeatures = []; + if (platfromEraseSupport & (1 << 1)) { supportedRpeFeatures.push("Pyrite Revert"); } + if (platfromEraseSupport & (1 << 2)) { supportedRpeFeatures.push("Secure Erase All SSDs"); } + if (platfromEraseSupport & (1 << 6)) { supportedRpeFeatures.push("TPM Clear"); } + if (platfromEraseSupport & (1 << 25)) { supportedRpeFeatures.push("Clear BIOS NVM Variables"); } + if (platfromEraseSupport & (1 << 26)) { supportedRpeFeatures.push("BIOS Reload of Golden Configuration"); } + if (platfromEraseSupport & (1 << 31)) { supportedRpeFeatures.push("CSME Unconfigure"); } + console.log("RPE Supported Features: " + supportedRpeFeatures.join(", ")); + + // Compute requested operations flags + var rpeflags = 0; + if (args.pyrite) { rpeflags += (1 << 1); } + if (args.ssd) { rpeflags += (1 << 2); } + if (args.tpm) { rpeflags += (1 << 6); } + if (args.nvm) { rpeflags += (1 << 25); } + if (args.bios) { rpeflags += (1 << 26); } + if (args.csme) { rpeflags += (1 << 31); } + if (rpeflags == 0) { process.exit(1); return; } + if ((rpeflags | platfromEraseSupport) != platfromEraseSupport) { console.log("Unable to perform unsupported RPE operation."); process.exit(1); return; } + settings.rpeflags = rpeflags; + settings.powerAction = 0; + if (args.reset) { settings.powerAction = 10; } else if (args.poweron) { settings.powerAction = 2; } + if (settings.powerAction == 0) { console.log("--reset or --poweron is required to perform RPE action."); process.exit(1); return; } + + // See if OCR and RPE are enabled + var enabledState = response['CIM_BootService'].response['EnabledState']; + var enabledBootStateStr = { 0: "Unknown", 1: "Other", 2: "Enabled", 3: "Disabled", 4: "Shutting Down", 5: "Not Applicable", 6: "Enabled but Offline", 7: "In Test", 8: "Deferred", 9: "Quiesce", 10: "Starting", 32768: "RPE Disabled", 32769: "All Enabled", 32770: "RPE & OCR Disabled" }; + var t = enabledBootStateStr[enabledState] ? enabledBootStateStr[enabledState] : ("Unknown, #" + enabledState); + console.log("BootService Enabled State: " + t); + + if (enabledState != 32769) { + // Enabled OCR and RPE + console.log("Enabling OCR and RPE features..."); + amtstack.CIM_BootService_RequestStateChange(32769, null, performAmtPlatformErase2); + } else { + performAmtPlatformErase3(args); + } + } else { console.log("Error, status " + status + "."); process.exit(1); } +} + +function performAmtPlatformErase2(stack, name, response, status, args) { + debug(0, "performAmtPlatformErase2(" + status + "): " + JSON.stringify(response, null, 2)); + if (status == 200) { + if (response.Body['ReturnValueStr'] != 'SUCCESS') { console.log("Error, " + response.Body['ReturnValueStr'] + "."); process.exit(1); } + else { performAmtPlatformErase3(args); } + process.exit(0); + } else { console.log("Error, status " + status + "."); process.exit(1); } +} + +function performAmtPlatformErase3(args) { + var tlv = makeUefiBootParam(1, settings.rpeflags, 4), tlvlen = 1; + if ((settings.rpeflags & 2) && (typeof args.pyrite == 'string')) { tlv += makeUefiBootParam(10, args.pyrite); tlvlen++; } + if ((settings.rpeflags & 4) && (typeof args.ssd == 'string')) { tlv += makeUefiBootParam(20, args.ssd); tlvlen++; } + settings.platfromEraseTLV = { tlv: Buffer.from(tlv, 'binary').toString('base64'), tlvlen: tlvlen }; + debug(0, "platfromEraseTLV: " + JSON.stringify(r, null, 2)); + console.log("Fetching boot information..."); + amtstack.Get('AMT_BootSettingData', performAmtPlatformErase4, 0, 1); +} + +function performAmtPlatformErase4(stack, name, response, status, args) { + debug(0, "performAmtPlatformErase4(" + status + "): " + JSON.stringify(response, null, 2)); + if (status == 200) { + var r = response['Body']; + r['PlatformErase'] = true; + r['UefiBootParametersArray'] = settings.platfromEraseTLV.tlv; + r['UefiBootNumberOfParams'] = settings.platfromEraseTLV.tlvlen; + debug(0, "BootConfig: " + JSON.stringify(r, null, 2)); + console.log("Setting Boot Order..."); + amtstack.CIM_BootConfigSetting_ChangeBootOrder(null, function (stack, name, response, status) { + if (status != 200) { console.log("PUT CIM_BootConfigSetting_ChangeBootOrder, Error #" + status + ((response.Header && response.Header.WsmanError) ? (', ' + response.Header.WsmanError) : '')); process.exit(1); return; } + if (response.Body['ReturnValue'] != 0) { messagebox("Error, Change Boot Order returns " + response.Body.ReturnValueStr); process.exit(1); return; } + amtstack.Put('AMT_BootSettingData', r, performAmtPlatformErase5, 0, 1); + }, 0, 1); + } else { console.log("Error, status " + status + "."); process.exit(1); } +} + +function performAmtPlatformErase5(stack, name, response, status, args) { + debug(0, "performAmtPlatformErase5(" + status + "): " + JSON.stringify(response, null, 2)); + if (status == 200) { + console.log("Setting Boot Configuration Role..."); + amtstack.SetBootConfigRole(1, performAmtPlatformErase6, 0, 1); + } else { console.log("Error, status " + status + "."); process.exit(1); } +} + +function performAmtPlatformErase6(stack, name, response, status, args) { + debug(0, "performAmtPlatformErase6(" + status + "): " + JSON.stringify(response, null, 2)); + if (status == 200) { + if (response.Body['ReturnValueStr'] != 'SUCCESS') { console.log("Error, " + response.Body['ReturnValueStr'] + "."); process.exit(1); } + else { + console.log('Performing power state change...'); + amtstack.RequestPowerStateChange(settings.powerAction, performAmtPlatformErase7); // 2 = Power Up, 10 = Reset + } + } else { console.log("Error, status " + status + "."); process.exit(1); } +} + +function performAmtPlatformErase7(stack, name, response, status, args) { + debug(0, "performAmtPlatformErase7(" + status + "): " + JSON.stringify(response, null, 2)); + if (status == 200) { + if (response.Body['ReturnValueStr'] != 'SUCCESS') { console.log("Error, " + response.Body['ReturnValueStr'] + "."); process.exit(1); } else { console.log('Done.'); } + process.exit(0); + } else { console.log("Error, status " + status + "."); process.exit(1); } +} + + +// Returns a UEFI boot parameter in binary +function makeUefiBootParam(type, data, len) { + if (typeof data == 'number') { if (len == 1) { data = String.fromCharCode(data & 0xFF); } if (len == 2) { data = ShortToStrX(data); } if (len == 4) { data = IntToStrX(data); } } + return ShortToStrX(0x8086) + ShortToStrX(type) + IntToStrX(data.length) + data; +} +function IntToStrX(v) { return String.fromCharCode(v & 0xFF, (v >> 8) & 0xFF, (v >> 16) & 0xFF, (v >> 24) & 0xFF); } +function ShortToStrX(v) { return String.fromCharCode(v & 0xFF, (v >> 8) & 0xFF); } + // // Intel AMT feature configuration action // diff --git a/agents/modules_meshcmd/amt.js b/agents/modules_meshcmd/amt.js index 22124358..e99e54a6 100644 --- a/agents/modules_meshcmd/amt.js +++ b/agents/modules_meshcmd/amt.js @@ -300,6 +300,7 @@ function AmtStackCreateService(wsmanStack) { obj.CIM_AccountManagementService_CreateAccount = function (System, AccountTemplate, callback_func) { obj.Exec("CIM_AccountManagementService", "CreateAccount", { "System": System, "AccountTemplate": AccountTemplate }, callback_func); } obj.CIM_BootConfigSetting_ChangeBootOrder = function (Source, callback_func) { obj.Exec("CIM_BootConfigSetting", "ChangeBootOrder", { "Source": Source }, callback_func); } obj.CIM_BootService_SetBootConfigRole = function (BootConfigSetting, Role, callback_func) { obj.Exec("CIM_BootService", "SetBootConfigRole", { "BootConfigSetting": BootConfigSetting, "Role": Role }, callback_func, 0, 1); } + obj.CIM_BootService_RequestStateChange = function (RequestedState, TimeoutPeriod, callback_func) { obj.Exec('CIM_BootService', 'RequestStateChange', { 'RequestedState': RequestedState, 'TimeoutPeriod': TimeoutPeriod }, callback_func, 0, 1); } obj.CIM_Card_ConnectorPower = function (Connector, PoweredOn, callback_func) { obj.Exec("CIM_Card", "ConnectorPower", { "Connector": Connector, "PoweredOn": PoweredOn }, callback_func); } obj.CIM_Card_IsCompatible = function (ElementToCheck, callback_func) { obj.Exec("CIM_Card", "IsCompatible", { "ElementToCheck": ElementToCheck }, callback_func); } obj.CIM_Chassis_IsCompatible = function (ElementToCheck, callback_func) { obj.Exec("CIM_Chassis", "IsCompatible", { "ElementToCheck": ElementToCheck }, callback_func); } diff --git a/meshuser.js b/meshuser.js index efe88e93..3081417b 100644 --- a/meshuser.js +++ b/meshuser.js @@ -6142,6 +6142,650 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use } } + const serverUserCommand = { + 'acceleratorsstats': [serverUserCommandAcceleratorsStats, "Show data on work being offloaded to other CPU's"], + 'agentissues': [serverUserCommandAgentIssues, ""], + 'agentstats': [serverUserCommandAgentStats, ""], + 'amtacm': [serverUserCommandAmtAcm, ""], + 'amtmanager': [serverUserCommandAmtManager, ""], + 'amtpasswords': [serverUserCommandAmtPasswords, ""], + 'args': [serverUserCommandArgs, ""], + 'autobackup': [serverUserCommandAutoBackup, ""], + 'backupconfig': [serverUserCommandBackupConfig, ""], + 'badlogins': [serverUserCommandBadLogins, ""], + 'certexpire': [serverUserCommandCertExpire, ""], + 'certhashes': [serverUserCommandCertHashes, ""], + 'closeusersessions': [serverUserCommandCloseUserSessions, "Disconnects all sessions for a specified user."], + 'cores': [serverUserCommandCores, ""], + 'dbcounters': [serverUserCommandDbCounters, ""], + 'dbstats': [serverUserCommandDbStats, ""], + 'dispatchtable': [serverUserCommandDispatchTable, ""], + 'dupagents': [serverUserCommandDupAgents, ""], + 'email': [serverUserCommandEmail, ""], + 'firebase': [serverUserCommandFirebase, ""], + 'heapdump': [serverUserCommandHeapDump, ""], + 'heapdump2': [serverUserCommandHeapDump2, ""], + 'help': [serverUserCommandHelp, ""], + 'info': [serverUserCommandInfo, "Returns the most immidiatly useful information about this server, including MeshCentral and NodeJS versions. This is often information required to file a bug."], + 'le': [serverUserCommandLe, ""], + 'lecheck': [serverUserCommandLeCheck, ""], + 'leevents': [serverUserCommandLeEvents, ""], + 'maintenance': [serverUserCommandMaintenance, ""], + 'migrationagents': [serverUserCommandMigrationAgents, ""], + 'mps': [serverUserCommandMps, ""], + 'mpsstats': [serverUserCommandMpsStats, ""], + 'nodeconfig': [serverUserCommandNodeConfig, ""], + 'print': [serverUserCommandPrint, ""], + 'relays': [serverUserCommandRelays, ""], + 'resetserver': [serverUserCommandResetServer, "Causes the server to reset, this is sometimes useful is the config.json file was changed."], + 'serverupdate': [serverUserCommandServerUpdate, "Updates server to latest version. Optional version argument to install specific version. Example: serverupdate 0.8.49"], + 'setmaxtasks': [serverUserCommandSetMaxTasks, ""], + 'showpaths': [serverUserCommandShowPaths, ""], + 'sms': [serverUserCommandSMS, ""], + 'swarmstats': [serverUserCommandSwarmStats, ""], + 'tasklimiter': [serverUserCommandTaslkLimiter, "Returns the internal status of the tasklimiter. This is a system used to smooth out work done by the server. It's used by, for example, agent updates so that not all agents are updated at the same time."], + 'trafficdelta': [serverUserCommandTrafficDelta, ""], + 'trafficstats': [serverUserCommandTrafficStats, ""], + 'updatecheck': [serverUserCommandUpdateCheck, ""], + 'usersessions': [serverUserCommandUserSessions, "Returns a list of active sessions grouped by user."], + 'versions': [serverUserCommandVersions, "Returns all internal versions for NodeJS running this server."], + 'watchdog': [serverUserCommandWatchdog, ""], + 'webpush': [serverUserCommandWebPush, ""], + 'webstats': [serverUserCommandWebStats, ""] + } + + function serverUserCommandHelp(cmdargs) { + var r, fin = '', f = '', availcommands = []; + for (var i in serverUserCommand) { availcommands.push(i); } + availcommands = availcommands.sort(); + while (availcommands.length > 0) { if (f.length > 80) { fin += (f + ',\r\n'); f = ''; } f += (((f != '') ? ', ' : ' ') + availcommands.shift()); } + if (f != '') { fin += f; } + if (cmdargs['_'].length == 0) { + r = 'Available commands: \r\n' + fin + '\r\nType help for details.'; + } else { + var cmd2 = cmdargs['_'][0].toLowerCase(); + var cmddata = serverUserCommand[cmd2]; + if (cmddata) { if (cmddata[1] == '') { r = "No help available for this command."; } else { r = cmddata[1]; } } else { r = "This command does not exist."; } + } + return r; + } + + function serverUserCommandCertExpire(cmdargs) { + var r = ''; + const now = Date.now(); + for (var i in parent.webCertificateExpire) { + const domainName = (i == '') ? '[Default]' : i; + r += domainName + ', expires in ' + Math.floor((parent.webCertificateExpire[i] - now) / 86400000) + ' day(s)\r\n'; + } + return r; + } + + function serverUserCommandWebPush(cmdargs) { + if (parent.parent.webpush == null) { + r = "Web push not supported."; + } else { + if (cmdargs['_'].length != 1) { + r = "Usage: WebPush \"Message\""; + } else { + const pushSubscription = { "endpoint": "https://updates.push.services.mozilla.com/wpush/v2/gAAAAABgIkO9hjXHWhMPiuk-ppNRw7r_pUZitddwCEK4ykdzeIxOIjFnYhIt_nr-qUca2mpZziwQsSEhYTUCiuYrhWnVDRweMtiUj16yJJq8V5jneaEaUYjEIe5jp3DOMNpoTm1aHgX74gCR8uTXSITcM97bNi-hRxcQ4f6Ie4WSAmoXpd89B_g", "keys": { "auth": "UB2sbLVK7ALnSHw5P1dahg", "p256dh": "BIoRbcNSxBuTjN39CCCUCHo1f4NxBJ1YDdu_k4MbPW_q3NK1_RufnydUzLPDp8ibBVItSI72-s48QJvOjQ_S8Ok" } } + parent.parent.webpush.sendNotification(pushSubscription, cmdargs['_'][0]).then( + function (value) { try { ws.send(JSON.stringify({ action: 'OK', value: r, tag: command.tag })); } catch (ex) { } }, + function (error) { try { ws.send(JSON.stringify({ action: 'Error', value: r, tag: command.tag })); } catch (ex) { } } + ); + } + } + return r; + } + + function serverUserCommandAmtManager(cmdargs) { + if (parent.parent.amtManager == null) { return 'Intel AMT Manager not active.'; } + return parent.parent.amtManager.getStatusString(); + } + + function serverUserCommandCertHashes(cmdargs) { + var r = 'AgentCertHash: ' + parent.agentCertificateHashHex; + for (var i in parent.webCertificateHashs) { r += '\r\nwebCertificateHash (' + i + '): ' + common.rstr2hex(parent.webCertificateHashs[i]); } + for (var i in parent.webCertificateFullHashs) { r += '\r\nwebCertificateFullHash (' + i + '): ' + common.rstr2hex(parent.webCertificateFullHashs[i]); } + r += '\r\ndefaultWebCertificateHash: ' + common.rstr2hex(parent.defaultWebCertificateHash); + r += '\r\ndefaultWebCertificateFullHash: ' + common.rstr2hex(parent.defaultWebCertificateFullHash); + return r; + } + + function serverUserCommandAmtAcm(cmdargs) { + var r = ''; + if ((domain.amtacmactivation == null) || (domain.amtacmactivation.acmmatch == null) || (domain.amtacmactivation.acmmatch.length == 0)) { + r = 'No Intel AMT activation certificates.'; + } else { + if (domain.amtacmactivation.log != null) { r += '--- Activation Log ---\r\nFile : ' + domain.amtacmactivation.log + '\r\n'; } + for (var i in domain.amtacmactivation.acmmatch) { + var acmcert = domain.amtacmactivation.acmmatch[i]; + r += '--- Activation Certificate ' + (parseInt(i) + 1) + ' ---\r\nName : ' + acmcert.cn + '\r\nSHA1 : ' + acmcert.sha1 + '\r\nSHA256: ' + acmcert.sha256 + '\r\n'; + } + } + return r; + } + + function serverUserCommandHeapDump(cmdargs) { + var r = ''; + // Heapdump support, see example at: + // https://www.arbazsiddiqui.me/a-practical-guide-to-memory-leaks-in-nodejs/ + if (parent.parent.config.settings.heapdump === true) { + var dumpFileName = parent.path.join(parent.parent.datapath, `heapDump-${Date.now()}.heapsnapshot`); + try { ws.send(JSON.stringify({ action: 'serverconsole', value: "Generating dump file at: " + dumpFileName, tag: command.tag })); } catch (ex) { } + require('heapdump').writeSnapshot(dumpFileName, (err, filename) => { + try { ws.send(JSON.stringify({ action: 'serverconsole', value: "Done.", tag: command.tag })); } catch (ex) { } + }); + } else { + r = "Heapdump not supported, add \"heapdump\":true to settings section of config.json."; + } + return r; + } + + function serverUserCommandHeapDump2(cmdargs) { + var r = ''; + var heapdump = null; + try { heapdump = require('heapdump'); } catch (ex) { } + if (heapdump == null) { + r = 'Heapdump module not installed, run "npm install heapdump".'; + } else { + heapdump.writeSnapshot(function (err, filename) { + if (err != null) { + try { ws.send(JSON.stringify({ action: 'serverconsole', value: 'Unable to write heapdump: ' + err })); } catch (ex) { } + } else { + try { ws.send(JSON.stringify({ action: 'serverconsole', value: 'Wrote heapdump at ' + filename })); } catch (ex) { } + } + }); + } + break; + } + + function serverUserCommandSMS(cmdargs) { + var r = ''; + if (parent.parent.smsserver == null) { + r = "No SMS gateway in use."; + } else { + if (cmdargs['_'].length != 2) { + r = "Usage: SMS \"PhoneNumber\" \"Message\"."; + } else { + parent.parent.smsserver.sendSMS(cmdargs['_'][0], cmdargs['_'][1], function (status, msg) { + if (typeof msg == 'string') { + try { ws.send(JSON.stringify({ action: 'serverconsole', value: status ? ('Success: ' + msg) : ('Failed: ' + msg), tag: command.tag })); } catch (ex) { } + } else { + try { ws.send(JSON.stringify({ action: 'serverconsole', value: status ? 'Success' : 'Failed', tag: command.tag })); } catch (ex) { } + } + }); + } + } + return r; + } + + function serverUserCommandEmail(cmdargs) { + var r = ''; + if (domain.mailserver == null) { + r = "No email service enabled."; + } else { + if (cmdargs['_'].length != 3) { + r = "Usage: email \"user@sample.com\" \"Subject\" \"Message\"."; + } else { + domain.mailserver.sendMail(cmdargs['_'][0], cmdargs['_'][1], cmdargs['_'][2]); + r = "Done."; + } + } + return r; + } + + function serverUserCommandLe(cmdargs) { + if (parent.parent.letsencrypt == null) { return "Let's Encrypt not in use."; } + return JSON.stringify(parent.parent.letsencrypt.getStats(), null, 4); + } + + function serverUserCommandLeCheck(cmdargs) { + if (parent.parent.letsencrypt == null) { return "Let's Encrypt not in use."; } + return ["CertOK", "Request:NoCert", "Request:Expire", "Request:MissingNames"][parent.parent.letsencrypt.checkRenewCertificate()]; + } + + function serverUserCommandLeEvents(cmdargs) { + if (parent.parent.letsencrypt == null) { return "Let's Encrypt not in use."; } + return parent.parent.letsencrypt.events.join('\r\n'); + } + + function serverUserCommandBadLogins(cmdargs) { + var r = ''; + if (parent.parent.config.settings.maxinvalidlogin == false) { + r = 'Bad login filter is disabled.'; + } else { + if (cmdargs['_'] == 'reset') { + // Reset bad login table + parent.badLoginTable = {}; + parent.badLoginTableLastClean = 0; + r = 'Done.' + } else if (cmdargs['_'] == '') { + // Show current bad login table + if (typeof parent.parent.config.settings.maxinvalidlogin.coolofftime == 'number') { + r = "Max is " + parent.parent.config.settings.maxinvalidlogin.count + " bad login(s) in " + parent.parent.config.settings.maxinvalidlogin.time + " minute(s), " + parent.parent.config.settings.maxinvalidlogin.coolofftime + " minute(s) cooloff.\r\n"; + } else { + r = "Max is " + parent.parent.config.settings.maxinvalidlogin.count + " bad login(s) in " + parent.parent.config.settings.maxinvalidlogin.time + " minute(s).\r\n"; + } + var badLoginCount = 0; + parent.cleanBadLoginTable(); + for (var i in parent.badLoginTable) { + badLoginCount++; + if (typeof parent.badLoginTable[i] == 'number') { + r += "Cooloff for " + Math.floor((parent.badLoginTable[i] - Date.now()) / 60000) + " minute(s)\r\n"; + } else { + if (parent.badLoginTable[i].length > 1) { + r += (i + ' - ' + parent.badLoginTable[i].length + " records\r\n"); + } else { + r += (i + ' - ' + parent.badLoginTable[i].length + " record\r\n"); + } + } + } + if (badLoginCount == 0) { r += 'No bad logins.'; } + } else { + r = 'Usage: badlogin [reset]'; + } + } + return r; + } + + function serverUserCommandDispatchTable(cmdargs) { + var r = ''; + for (var i in parent.parent.eventsDispatch) { r += (i + ', ' + parent.parent.eventsDispatch[i].length + '\r\n'); } + return r; + } + + function serverUserCommandDupAgents(cmdargs) { + var r = ''; + for (var i in parent.duplicateAgentsLog) { r += JSON.stringify(parent.duplicateAgentsLog[i]) + '\r\n'; } + if (r == '') { r = 'No duplicate agents in log.'; } + return r; + } + + function serverUserCommandAgentStats(cmdargs) { + var r = '', stats = parent.getAgentStats(); + for (var i in stats) { + if (typeof stats[i] == 'object') { r += (i + ': ' + JSON.stringify(stats[i]) + '\r\n'); } else { r += (i + ': ' + stats[i] + '\r\n'); } + } + return r; + } + + function serverUserCommandAgentIssues(cmdargs) { + var r = '', stats = parent.getAgentIssues(); + if (stats.length == 0) { + r = "No agent issues."; + } else { + for (var i in stats) { r += stats[i].join(', ') + '\r\n'; } + } + return r; + } + + function serverUserCommandWebStats(cmdargs) { + var r = '', stats = parent.getStats(); + for (var i in stats) { + if (typeof stats[i] == 'object') { r += (i + ': ' + JSON.stringify(stats[i]) + '\r\n'); } else { r += (i + ': ' + stats[i] + '\r\n'); } + } + return r; + } + + function serverUserCommandTrafficStats(cmdargs) { + var r = ''; + var stats = parent.getTrafficStats(); + for (var i in stats) { + if (typeof stats[i] == 'object') { r += (i + ': ' + JSON.stringify(stats[i]) + '\r\n'); } else { r += (i + ': ' + stats[i] + '\r\n'); } + } + return r; + } + + function serverUserCommandTrafficDelta(cmdargs) { + var r = ''; + const stats = parent.getTrafficDelta(obj.trafficStats); + obj.trafficStats = stats.current; + for (var i in stats.delta) { + if (typeof stats.delta[i] == 'object') { r += (i + ': ' + JSON.stringify(stats.delta[i]) + '\r\n'); } else { r += (i + ': ' + stats.delta[i] + '\r\n'); } + } + return r; + } + + function serverUserCommandWatchdog(cmdargs) { + var r = ''; + if (parent.parent.watchdog == null) { + r = 'Server watchdog not active.'; + } else { + r = 'Server watchdog active.\r\n'; + if (parent.parent.watchdogmaxtime != null) { r += 'Largest timeout was ' + parent.parent.watchdogmax + 'ms on ' + parent.parent.watchdogmaxtime + '\r\n'; } + for (var i in parent.parent.watchdogtable) { r += parent.parent.watchdogtable[i] + '\r\n'; } + } + return r; + } + + function serverUserCommandAcceleratorsStats(cmdargs) { + var r = '', stats = parent.parent.certificateOperations.getAcceleratorStats(); + for (var i in stats) { + if (typeof stats[i] == 'object') { r += (i + ': ' + JSON.stringify(stats[i]) + '\r\n'); } else { r += (i + ': ' + stats[i] + '\r\n'); } + } + return r; + } + + function serverUserCommandMpsStats(cmdargs) { + var r = ''; + if (parent.parent.mpsserver == null) { + r = 'MPS not enabled.'; + } else { + var stats = parent.parent.mpsserver.getStats(); + for (var i in stats) { + if (typeof stats[i] == 'object') { r += (i + ': ' + JSON.stringify(stats[i]) + '\r\n'); } else { r += (i + ': ' + stats[i] + '\r\n'); } + } + } + return r; + } + + // List all MPS connections and types. + function serverUserCommandMps(cmdargs) { + var r = ''; + if (parent.parent.mpsserver == null) { + r = 'MPS not enabled.'; + } else { + const connectionTypes = ['CIRA', 'Relay', 'LMS']; + for (var nodeid in parent.parent.mpsserver.ciraConnections) { + r += nodeid; + var connections = parent.parent.mpsserver.ciraConnections[nodeid]; + for (var i in connections) { r += ', ' + connectionTypes[connections[i].tag.connType]; } + r += '\r\n'; + } + if (r == '') { r = 'MPS has not connections.'; } + } + return r; + } + + function serverUserCommandDbStats(cmdargs) { + parent.parent.db.getStats(function (stats) { + var r2 = ''; + for (var i in stats) { r2 += (i + ': ' + stats[i] + '\r\n'); } + try { ws.send(JSON.stringify({ action: 'serverconsole', value: r2, tag: command.tag })); } catch (ex) { } + }); + } + + function serverUserCommandDbCounters(cmdargs) { + try { ws.send(JSON.stringify({ action: 'serverconsole', value: JSON.stringify(parent.parent.db.dbCounters, null, 2), tag: command.tag })); } catch (ex) { } + } + + function serverUserCommandServerUpdate(cmdargs) { + var r = 'Performing server update...'; + var version = null; + if (cmdargs['_'].length > 0) { + version = cmdargs['_'][0]; + + // This call is SLOW. We only want to validate version if we have to + if (version != 'stable' && version != 'latest') { + parent.parent.getServerVersions((data) => { + var versions = JSON.parse(data); + + if (versions.includes(version)) { + if (parent.parent.performServerUpdate(version) == false) { + try { + ws.send(JSON.stringify({ + action: 'serverconsole', + value: 'Server self-update not possible.' + })); + } catch (ex) { } + } + } else { + try { + ws.send(JSON.stringify({ + action: 'serverconsole', + value: 'Invalid version. Aborting update' + })); + } catch (ex) { } + } + }); + } else { + if (parent.parent.performServerUpdate(version) == false) { + r = 'Server self-update not possible.'; + } + } + } else { + if (parent.parent.performServerUpdate(version) == false) { + r = 'Server self-update not possible.'; + } + } + return r; + } + + function serverUserCommandPrint(cmdargs) { + console.log(cmdargs['_'][0]); + } + + function serverUserCommandAmtPasswords(cmdargs) { + var r = ''; + if (parent.parent.amtPasswords == null) { + r = "No Intel AMT password table." + } else { + for (var i in parent.parent.amtPasswords) { r += (i + ' - ' + parent.parent.amtPasswords[i].join(', ') + '\r\n'); } + } + return r; + } + + function serverUserCommandUpdateCheck(cmdargs) { + parent.parent.getServerTags(function (tags, error) { + var r2 = ''; + if (error != null) { r2 += 'Exception: ' + error + '\r\n'; } + else { for (var i in tags) { r2 += i + ': ' + tags[i] + '\r\n'; } } + try { ws.send(JSON.stringify({ action: 'serverconsole', value: r2, tag: command.tag })); } catch (ex) { } + }); + return "Checking server update..."; + } + + function serverUserCommandMaintenance(cmdargs) { + var arg = null, changed = false, r = ''; + if ((cmdargs['_'] != null) && (cmdargs['_'][0] != null)) { arg = cmdargs['_'][0].toLowerCase(); } + if (arg == 'enabled') { parent.parent.config.settings.maintenancemode = 1; changed = true; } + else if (arg == 'disabled') { delete parent.parent.config.settings.maintenancemode; changed = true; } + r = 'Maintenance mode: ' + ((parent.parent.config.settings.maintenancemode == null) ? 'Disabled' : 'Enabled'); + if (changed == false) { r += '\r\nTo change type: maintenance [enabled|disabled]'; } + return r; + } + + function serverUserCommandInfo(cmdargs) { + var info = process.memoryUsage(); + info.dbType = ['None', 'NeDB', 'MongoJS', 'MongoDB'][parent.db.databaseType]; + try { if (parent.parent.multiServer != null) { info.serverId = parent.parent.multiServer.serverid; } } catch (ex) { } + if (parent.db.databaseType == 3) { info.dbChangeStream = parent.db.changeStream; } + if (parent.parent.pluginHandler != null) { info.plugins = []; for (var i in parent.parent.pluginHandler.plugins) { info.plugins.push(i); } } + try { info.nodeVersion = process.version; } catch (ex) { } + try { info.meshVersion = parent.parent.currentVer; } catch (ex) { } + try { info.platform = process.platform; } catch (ex) { } + try { info.arch = process.arch; } catch (ex) { } + try { info.pid = process.pid; } catch (ex) { } + try { info.uptime = process.uptime(); } catch (ex) { } + try { info.cpuUsage = process.cpuUsage(); } catch (ex) { } + try { info.warnings = parent.parent.getServerWarnings(); } catch (ex) { } + try { info.database = ["Unknown", "NeDB", "MongoJS", "MongoDB", "MariaDB", "MySQL"][parent.parent.db.databaseType]; } catch (ex) { } + try { info.productionMode = ((process.env.NODE_ENV != null) && (process.env.NODE_ENV == 'production')); } catch (ex) { } + try { info.allDevGroupManagers = parent.parent.config.settings.managealldevicegroups; } catch (ex) { } + return JSON.stringify(info, null, 4); + } + + function serverUserCommandNodeConfig(cmdargs) { + return JSON.stringify(process.config, null, 4); + } + + function serverUserCommandVersions(cmdargs) { + return JSON.stringify(process.versions, null, 4); + } + + function serverUserCommandArgs(cmdargs) { + return cmd + ': ' + JSON.stringify(cmdargs); + } + + function serverUserCommandUserSessions(cmdargs) { + var r = ''; + var userSessionCount = 0; + var filter = null; + var arg = cmdargs['_'][0]; + if (typeof arg == 'string') { if (arg.indexOf('/') >= 0) { filter = arg; } else { filter = ('user/' + domain.id + '/' + arg); } } + for (var i in parent.wssessions) { + if ((filter == null) || (filter == i)) { + userSessionCount++; + r += (i + ', ' + parent.wssessions[i].length + ' session' + ((parent.wssessions[i].length > 1) ? 's' : '') + '.\r\n'); + for (var j in parent.wssessions[i]) { + r += ' ' + parent.wssessions[i][j].clientIp + ' --> ' + parent.wssessions[i][j].sessionId + '\r\n'; + } + } + } + if (userSessionCount == 0) { r = 'None.'; } + break; + } + + function serverUserCommandCloseUserSessions(cmdargs) { + var r = ''; + var userSessionCount = 0; + var filter = null; + var arg = cmdargs['_'][0]; + if (typeof arg == 'string') { if (arg.indexOf('/') >= 0) { filter = arg; } else { filter = ('user/' + domain.id + '/' + arg); } } + if (filter == null) { + r += "Usage: closeusersessions "; + } else { + r += "Closing user sessions for: " + filter + '\r\n'; + for (var i in parent.wssessions) { + if (filter == i) { + userSessionCount++; + for (var j in parent.wssessions[i]) { + parent.wssessions[i][j].send(JSON.stringify({ action: 'stopped', msg: "Administrator forced disconnection" })); + parent.wssessions[i][j].close(); + } + } + } + if (userSessionCount < 2) { r += 'Disconnected ' + userSessionCount + ' session.'; } else { r += 'Disconnected ' + userSessionCount + ' sessions.'; }; + } + break; + } + + function serverUserCommandResetServer(cmdargs) { + var r = ''; + console.log("Server restart..."); + process.exit(0); + break; + } + + function serverUserCommandTaslkLimiter(cmdargs) { + var r = ''; + if (parent.parent.taskLimiter != null) { + //var obj = { maxTasks: maxTasks, maxTaskTime: (maxTaskTime * 1000), nextTaskId: 0, currentCount: 0, current: {}, pending: [[], [], []], timer: null }; + const tl = parent.parent.taskLimiter; + r += 'MaxTasks: ' + tl.maxTasks + ', NextTaskId: ' + tl.nextTaskId + '\r\n'; + r += 'MaxTaskTime: ' + (tl.maxTaskTime / 1000) + ' seconds, Timer: ' + (tl.timer != null) + '\r\n'; + var c = []; + for (var i in tl.current) { c.push(i); } + r += 'Current (' + tl.currentCount + '): [' + c.join(', ') + ']\r\n'; + r += 'Pending (High/Med/Low): ' + tl.pending[0].length + ', ' + tl.pending[1].length + ', ' + tl.pending[2].length + '\r\n'; + } + break; + } + + function serverUserCommandSetMaxTasks(cmdargs) { + var r = ''; + if ((cmdargs["_"].length != 1) || (parseInt(cmdargs["_"][0]) < 1) || (parseInt(cmdargs["_"][0]) > 1000)) { + r = 'Usage: setmaxtasks [1 to 1000]'; + } else { + parent.parent.taskLimiter.maxTasks = parseInt(cmdargs["_"][0]); + r = 'MaxTasks set to ' + parent.parent.taskLimiter.maxTasks + '.'; + } + break; + } + + function serverUserCommandCores(cmdargs) { + var r = ''; + if (parent.parent.defaultMeshCores != null) { for (var i in parent.parent.defaultMeshCores) { r += i + ': ' + parent.parent.defaultMeshCores[i].length + ' bytes\r\n'; } } + break; + } + + function serverUserCommandShowPaths(cmdargs) { + var r = ''; + r = 'Parent: ' + parent.parent.parentpath + '\r\n'; + r += 'Data: ' + parent.parent.datapath + '\r\n'; + r += 'Files: ' + parent.parent.filespath + '\r\n'; + r += 'Backup: ' + parent.parent.backuppath + '\r\n'; + r += 'Record: ' + parent.parent.recordpath + '\r\n'; + r += 'WebPublic: ' + parent.parent.webPublicPath + '\r\n'; + r += 'WebViews: ' + parent.parent.webViewsPath + '\r\n'; + if (parent.parent.webViewsOverridePath) { r += 'XWebPublic: ' + parent.parent.webViewsOverridePath + '\r\n'; } + if (parent.parent.webViewsOverridePath) { r += 'XWebViews: ' + parent.parent.webPublicOverridePath + '\r\n'; } + break; + } + + function serverUserCommandMigrationAgents(cmdargs) { + var r = ''; + if (parent.parent.swarmserver == null) { + r = 'Swarm server not running.'; + } else { + for (var i in parent.parent.swarmserver.migrationAgents) { + var arch = parent.parent.swarmserver.migrationAgents[i]; + for (var j in arch) { var agent = arch[j]; r += 'Arch ' + agent.arch + ', Ver ' + agent.ver + ', Size ' + ((agent.binary == null) ? 0 : agent.binary.length) + '
'; } + } + } + break; + } + + function serverUserCommandSwarmStats(cmdargs) { + var r = ''; + if (parent.parent.swarmserver == null) { + r = 'Swarm server not running.'; + } else { + for (var i in parent.parent.swarmserver.stats) { + if (typeof parent.parent.swarmserver.stats[i] == 'object') { + r += i + ': ' + JSON.stringify(parent.parent.swarmserver.stats[i]) + '\r\n'; + } else { + r += i + ': ' + parent.parent.swarmserver.stats[i] + '\r\n'; + } + } + } + break; + } + + function serverUserCommandRelays(cmdargs) { + var r = ''; + for (var i in parent.wsrelays) { + r += 'id: ' + i + ', ' + ((parent.wsrelays[i].state == 2) ? 'connected' : 'pending'); + if (parent.wsrelays[i].peer1 != null) { + r += ', ' + cleanRemoteAddr(parent.wsrelays[i].peer1.req.clientIp); + if (parent.wsrelays[i].peer1.user) { r += ' (User:' + parent.wsrelays[i].peer1.user.name + ')' } + } + if (parent.wsrelays[i].peer2 != null) { + r += ' to ' + cleanRemoteAddr(parent.wsrelays[i].peer2.req.clientIp); + if (parent.wsrelays[i].peer2.user) { r += ' (User:' + parent.wsrelays[i].peer2.user.name + ')' } + } + r += '\r\n'; + } + if (r == '') { r = 'No relays.'; } + break; + } + + function serverUserCommandAutoBackup(cmdargs) { + var r = ''; + var backupResult = parent.db.performBackup(function (msg) { + try { ws.send(JSON.stringify({ action: 'serverconsole', value: msg, tag: command.tag })); } catch (ex) { } + }); + if (backupResult == 0) { r = 'Starting auto-backup...'; } else { r = 'Backup alreay in progress.'; } + break; + } + + function serverUserCommandBackupConfig(cmdargs) { + var r = ''; + r = parent.db.getBackupConfig(); + break; + } + + function serverUserCommandFirebase(cmdargs) { + var r = ''; + if (parent.parent.firebase == null) { + r = "Firebase push messaging not supported"; + } else { + r = JSON.stringify(parent.parent.firebase.stats, null, 2); + } + break; + } + + function csvClean(s) { return '\"' + s.split('\"').join('').split(',').join('').split('\r').join('').split('\n').join('') + '\"'; } // Return detailed information about an array of nodeid's