UNPKG

meshcentral

Version:

Web based remote computer management server

998 lines (911 loc) • 338 kB
/* Copyright 2018-2022 Intel Corporation Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ process.on('uncaughtException', function (ex) { require('MeshAgent').SendCommand({ action: 'msg', type: 'console', value: "uncaughtException1: " + ex }); }); if (process.platform == 'win32' && require('user-sessions').getDomain == null) { require('user-sessions').getDomain = function getDomain(uid) { return (this.getSessionAttribute(uid, this.InfoClass.WTSDomainName)); }; } var promise = require('promise'); // Mesh Rights var MNG_ERROR = 65; var MESHRIGHT_EDITMESH = 1; var MESHRIGHT_MANAGEUSERS = 2; var MESHRIGHT_MANAGECOMPUTERS = 4; var MESHRIGHT_REMOTECONTROL = 8; var MESHRIGHT_AGENTCONSOLE = 16; var MESHRIGHT_SERVERFILES = 32; var MESHRIGHT_WAKEDEVICE = 64; var MESHRIGHT_SETNOTES = 128; var MESHRIGHT_REMOTEVIEW = 256; // Remote View Only var MESHRIGHT_NOTERMINAL = 512; var MESHRIGHT_NOFILES = 1024; var MESHRIGHT_NOAMT = 2048; var MESHRIGHT_LIMITEDINPUT = 4096; var MESHRIGHT_LIMITEVENTS = 8192; var MESHRIGHT_CHATNOTIFY = 16384; var MESHRIGHT_UNINSTALL = 32768; var MESHRIGHT_NODESKTOP = 65536; var pendingSetClip = false; // This is a temporary hack to prevent multiple setclips at the same time to stop the agent from crashing. // // This is a helper function used by the 32 bit Windows Agent, when running on 64 bit windows. It will check if the agent is already patched for this // and will use this helper if it is not. This helper will inject 'sysnative' into the results when calling readdirSync() on %windir%. // function __readdirSync_fix(path) { var sysnative = false; pathstr = require('fs')._fixwinpath(path); if (pathstr.split('\\*').join('').toLowerCase() == process.env['windir'].toLowerCase()) { sysnative = true; } var ret = require('fs').__readdirSync_old(path); if (sysnative) { ret.push('sysnative'); } return (ret); } if (process.platform == 'win32' && require('_GenericMarshal').PointerSize == 4 && require('os').arch() == 'x64') { if (require('fs').readdirSync.version == null) { // // 32 Bit Windows Agent on 64 bit Windows has not been patched for sysnative issue, so lets use our own solution // require('fs').__readdirSync_old = require('fs').readdirSync; require('fs').readdirSync = __readdirSync_fix; } } function bcdOK() { if (process.platform != 'win32') { return (false); } if (require('os').arch() == 'x64') { return (require('_GenericMarshal').PointerSize == 8); } return (true); } function getDomainInfo() { var hostname = require('os').hostname(); var ret = { Name: hostname, Domain: "" }; switch (process.platform) { case 'win32': try { ret = require('win-wmi').query('ROOT\\CIMV2', 'SELECT * FROM Win32_ComputerSystem', ['Name', 'Domain'])[0]; } catch (x) { } break; case 'linux': var hasrealm = false; try { hasrealm = require('lib-finder').hasBinary('realm'); } catch (x) { } if (hasrealm) { var child = require('child_process').execFile('/bin/sh', ['sh']); child.stdout.str = ''; child.stdout.on('data', function (c) { this.str += c.toString(); }); child.stdin.write("realm list | grep domain-name: | tr '\\n' '`' | "); child.stdin.write("awk -F'`' '{ "); child.stdin.write(' printf("[");'); child.stdin.write(' ST="";'); child.stdin.write(' for(i=1;i<NF;++i)'); child.stdin.write(' {'); child.stdin.write(' match($i,/domain-name: /);'); child.stdin.write(' printf("%s\\"%s\\"", ST, substr($i, RSTART+RLENGTH));'); child.stdin.write(' ST=",";'); child.stdin.write(' }'); child.stdin.write(' printf("]");'); child.stdin.write(" }'"); child.stdin.write('\nexit\n'); child.waitExit(); var names = []; try { names = JSON.parse(child.stdout.str); } catch (e) { } while (names.length > 0) { if (hostname.endsWith('.' + names.peek())) { ret = { Name: hostname.substring(0, hostname.length - names.peek().length - 1), Domain: names.peek() }; break; } names.pop(); } } break; } return (ret); } try { Object.defineProperty(Array.prototype, 'findIndex', { value: function (func) { var i = 0; for (i = 0; i < this.length; ++i) { if (func(this[i], i, this)) { return (i); } } return (-1); } }); } catch (ex) { } if (require('MeshAgent').ARCHID == null) { var id = null; switch (process.platform) { case 'win32': id = require('_GenericMarshal').PointerSize == 4 ? 3 : 4; break; case 'freebsd': id = require('_GenericMarshal').PointerSize == 4 ? 31 : 30; break; case 'darwin': try { id = require('os').arch() == 'x64' ? 16 : 29; } catch (ex) { id = 16; } break; } if (id != null) { Object.defineProperty(require('MeshAgent'), 'ARCHID', { value: id }); } } function setDefaultCoreTranslation(obj, field, value) { if (obj[field] == null || obj[field] == '') { obj[field] = value; } } function getCoreTranslation() { var ret = {}; if (global.coretranslations != null) { try { var lang = require('util-language').current; if (coretranslations[lang] == null) { lang = lang.split('-')[0]; } if (coretranslations[lang] == null) { lang = 'en'; } if (coretranslations[lang] != null) { ret = coretranslations[lang]; } } catch (ex) { } } setDefaultCoreTranslation(ret, 'allow', 'Allow'); setDefaultCoreTranslation(ret, 'deny', 'Deny'); setDefaultCoreTranslation(ret, 'autoAllowForFive', 'Auto accept all connections for next 5 minutes'); setDefaultCoreTranslation(ret, 'terminalConsent', '{0} requesting remote terminal access. Grant access?'); setDefaultCoreTranslation(ret, 'desktopConsent', '{0} requesting remote desktop access. Grant access?'); setDefaultCoreTranslation(ret, 'fileConsent', '{0} requesting remote file Access. Grant access?'); setDefaultCoreTranslation(ret, 'terminalNotify', '{0} started a remote terminal session.'); setDefaultCoreTranslation(ret, 'desktopNotify', '{0} started a remote desktop session.'); setDefaultCoreTranslation(ret, 'fileNotify', '{0} started a remote file session.'); setDefaultCoreTranslation(ret, 'privacyBar', 'Sharing desktop with: {0}'); return (ret); } var currentTranslation = getCoreTranslation(); try { require('kvm-helper'); } catch (e) { var j = { users: function () { var r = {}; require('user-sessions').Current(function (c) { r = c; }); if (process.platform != 'win32') { for (var i in r) { r[i].SessionId = r[i].uid; } } return (r); } }; addModuleObject('kvm-helper', j); } function lockDesktop(uid) { switch (process.platform) { case 'linux': if (uid != null) { var name = require('user-sessions').getUsername(uid); var child = require('child_process').execFile('/bin/sh', ['sh']); child.stdout.str = ''; child.stdout.on('data', function (chunk) { this.str += chunk.toString(); }); child.stderr.str = ''; child.stderr.on('data', function (chunk) { this.str += chunk.toString(); }); child.stdin.write('loginctl show-user -p Sessions ' + name + " | awk '{"); child.stdin.write('gsub(/^Sessions=/,"",$0);'); child.stdin.write('cmd = sprintf("loginctl lock-session %s",$0);'); child.stdin.write('system(cmd);'); child.stdin.write("}'\nexit\n"); child.waitExit(); } else { var child = require('child_process').execFile('/bin/sh', ['sh']); child.stdout.str = ''; child.stdout.on('data', function (chunk) { this.str += chunk.toString(); }); child.stderr.str = ''; child.stderr.on('data', function (chunk) { this.str += chunk.toString(); }); child.stdin.write('loginctl lock-sessions\nexit\n'); child.waitExit(); } break; case 'win32': { var options = { type: 1, uid: uid }; var child = require('child_process').execFile(process.env['windir'] + '\\system32\\cmd.exe', ['/c', 'RunDll32.exe user32.dll,LockWorkStation'], options); child.waitExit(); } break; default: break; } } var writable = require('stream').Writable; function destopLockHelper_pipe(httprequest) { if (process.platform != 'linux' && process.platform != 'freebsd') { return; } if (httprequest.unlockerHelper == null && httprequest.desktop != null && httprequest.desktop.kvm != null) { httprequest.unlockerHelper = new writable( { 'write': function (chunk, flush) { if (chunk.readUInt16BE(0) == 65) { delete this.request.autolock; } flush(); return (true); }, 'final': function (flush) { flush(); } }); httprequest.unlockerHelper.request = httprequest; httprequest.desktop.kvm.pipe(httprequest.unlockerHelper); } } var obj = { serverInfo: {} }; var agentFileHttpRequests = {}; // Currently active agent HTTPS GET requests from the server. var agentFileHttpPendingRequests = []; // Pending HTTPS GET requests from the server. var debugConsole = (global._MSH && (_MSH().debugConsole == 1)); var color_options = { background: (global._MSH != null) ? global._MSH().background : '0,54,105', foreground: (global._MSH != null) ? global._MSH().foreground : '255,255,255' }; if (process.platform == 'win32' && require('user-sessions').isRoot()) { // Check the Agent Uninstall MetaData for correctness, as the installer may have written an incorrect value try { var writtenSize = 0, actualSize = Math.floor(require('fs').statSync(process.execPath).size / 1024); var serviceName = (_MSH().serviceName ? _MSH().serviceName : (require('_agentNodeId').serviceName() ? require('_agentNodeId').serviceName() : 'Mesh Agent')); try { writtenSize = require('win-registry').QueryKey(require('win-registry').HKEY.LocalMachine, 'Software\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\' + serviceName, 'EstimatedSize'); } catch (ex) { } if (writtenSize != actualSize) { try { require('win-registry').WriteKey(require('win-registry').HKEY.LocalMachine, 'Software\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\' + serviceName, 'EstimatedSize', actualSize); } catch (ex) { } } } catch (ex) { } // Check to see if we are the Installed Mesh Agent Service, if we are, make sure we can run in Safe Mode var svcname = process.platform == 'win32' ? 'Mesh Agent' : 'meshagent'; try { svcname = require('MeshAgent').serviceName; } catch (ex) { } try { var meshCheck = false; try { meshCheck = require('service-manager').manager.getService(svcname).isMe(); } catch (ex) { } if (meshCheck && require('win-bcd').isSafeModeService && !require('win-bcd').isSafeModeService(svcname)) { require('win-bcd').enableSafeModeService(svcname); } } catch (ex) { } // Check the Agent Uninstall MetaData for DisplayVersion and update if not the same and only on windows if (process.platform == 'win32') { try { var writtenDisplayVersion = 0, actualDisplayVersion = process.versions.commitDate.toString(); var serviceName = (_MSH().serviceName ? _MSH().serviceName : (require('_agentNodeId').serviceName() ? require('_agentNodeId').serviceName() : 'Mesh Agent')); try { writtenDisplayVersion = require('win-registry').QueryKey(require('win-registry').HKEY.LocalMachine, 'Software\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\' + serviceName, 'DisplayVersion'); } catch (ex) { } if (writtenDisplayVersion != actualDisplayVersion) { try { require('win-registry').WriteKey(require('win-registry').HKEY.LocalMachine, 'Software\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\' + serviceName, 'DisplayVersion', actualDisplayVersion); } catch (ex) { } } } catch (ex) { } } } if (process.platform != 'win32') { var ch = require('child_process'); ch._execFile = ch.execFile; ch.execFile = function execFile(path, args, options) { if (options && options.type && options.type == ch.SpawnTypes.TERM && options.env) { options.env['TERM'] = 'xterm-256color'; } return (this._execFile(path, args, options)); }; } if (process.platform == 'darwin' && !process.versions) { // This is an older MacOS Agent, so we'll need to check the service definition so that Auto-Update will function correctly var child = require('child_process').execFile('/bin/sh', ['sh']); child.stdout.str = ''; child.stdout.on('data', function (chunk) { this.str += chunk.toString(); }); child.stdin.write("cat /Library/LaunchDaemons/meshagent_osx64_LaunchDaemon.plist | tr '\n' '\.' | awk '{split($0, a, \"<key>KeepAlive</key>\"); split(a[2], b, \"<\"); split(b[2], c, \">\"); "); child.stdin.write(" if(c[1]==\"dict\"){ split(a[2], d, \"</dict>\"); if(split(d[1], truval, \"<true/>\")>1) { split(truval[1], kn1, \"<key>\"); split(kn1[2], kn2, \"</key>\"); print kn2[1]; } }"); child.stdin.write(" else { split(c[1], ka, \"/\"); if(ka[1]==\"true\") {print \"ALWAYS\";} } }'\nexit\n"); child.waitExit(); if (child.stdout.str.trim() == 'Crashed') { child = require('child_process').execFile('/bin/sh', ['sh']); child.stdout.str = ''; child.stdout.on('data', function (chunk) { this.str += chunk.toString(); }); child.stdin.write("launchctl list | grep 'meshagent' | awk '{ if($3==\"meshagent\"){print $1;}}'\nexit\n"); child.waitExit(); if (parseInt(child.stdout.str.trim()) == process.pid) { // The currently running MeshAgent is us, so we can continue with the update var plist = require('fs').readFileSync('/Library/LaunchDaemons/meshagent_osx64_LaunchDaemon.plist').toString(); var tokens = plist.split('<key>KeepAlive</key>'); if (tokens[1].split('>')[0].split('<')[1] == 'dict') { var tmp = tokens[1].split('</dict>'); tmp.shift(); tokens[1] = '\n <true/>' + tmp.join('</dict>'); tokens = tokens.join('<key>KeepAlive</key>'); require('fs').writeFileSync('/Library/LaunchDaemons/meshagent_osx64_LaunchDaemon.plist', tokens); var fix = ''; fix += ("function macosRepair()\n"); fix += ("{\n"); fix += (" var child = require('child_process').execFile('/bin/sh', ['sh']);\n"); fix += (" child.stdout.str = '';\n"); fix += (" child.stdout.on('data', function (chunk) { this.str += chunk.toString(); });\n"); fix += (" child.stderr.on('data', function (chunk) { });\n"); fix += (" child.stdin.write('launchctl unload /Library/LaunchDaemons/meshagent_osx64_LaunchDaemon.plist\\n');\n"); fix += (" child.stdin.write('launchctl load /Library/LaunchDaemons/meshagent_osx64_LaunchDaemon.plist\\n');\n"); fix += (" child.stdin.write('rm /Library/LaunchDaemons/meshagentRepair.plist\\n');\n"); fix += (" child.stdin.write('rm " + process.cwd() + "/macosRepair.js\\n');\n"); fix += (" child.stdin.write('launchctl stop meshagentRepair\\nexit\\n');\n"); fix += (" child.waitExit();\n"); fix += ("}\n"); fix += ("macosRepair();\n"); fix += ("process.exit();\n"); require('fs').writeFileSync(process.cwd() + '/macosRepair.js', fix); var plist = '<?xml version="1.0" encoding="UTF-8"?>\n'; plist += '<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">\n'; plist += '<plist version="1.0">\n'; plist += ' <dict>\n'; plist += ' <key>Label</key>\n'; plist += (' <string>meshagentRepair</string>\n'); plist += ' <key>ProgramArguments</key>\n'; plist += ' <array>\n'; plist += (' <string>' + process.execPath + '</string>\n'); plist += ' <string>macosRepair.js</string>\n'; plist += ' </array>\n'; plist += ' <key>WorkingDirectory</key>\n'; plist += (' <string>' + process.cwd() + '</string>\n'); plist += ' <key>RunAtLoad</key>\n'; plist += ' <true/>\n'; plist += ' </dict>\n'; plist += '</plist>'; require('fs').writeFileSync('/Library/LaunchDaemons/meshagentRepair.plist', plist); child = require('child_process').execFile('/bin/sh', ['sh']); child.stdout.str = ''; child.stdout.on('data', function (chunk) { this.str += chunk.toString(); }); child.stdin.write("launchctl load /Library/LaunchDaemons/meshagentRepair.plist\nexit\n"); child.waitExit(); } } } } // Add an Intel AMT event to the log function addAmtEvent(msg) { if (obj.amtevents == null) { obj.amtevents = []; } var d = new Date(), e = zeroPad(d.getHours(), 2) + ':' + zeroPad(d.getMinutes(), 2) + ':' + zeroPad(d.getSeconds(), 2) + ', ' + msg; obj.amtevents.push(e); if (obj.amtevents.length > 100) { obj.amtevents.splice(0, obj.amtevents.length - 100); } if (obj.showamtevent) { require('MeshAgent').SendCommand({ action: 'msg', type: 'console', value: e }); } } function zeroPad(num, size) { var s = '000000000' + num; return s.substr(s.length - size); } // Create Secure IPC for Diagnostic Agent Communications obj.DAIPC = require('net').createServer(); if (process.platform != 'win32') { try { require('fs').unlinkSync(process.cwd() + '/DAIPC'); } catch (ex) { } } obj.DAIPC.IPCPATH = process.platform == 'win32' ? ('\\\\.\\pipe\\' + require('_agentNodeId')() + '-DAIPC') : (process.cwd() + '/DAIPC'); try { obj.DAIPC.listen({ path: obj.DAIPC.IPCPATH, writableAll: true, maxConnections: 5 }); } catch (ex) { } obj.DAIPC._daipc = []; obj.DAIPC.on('connection', function (c) { c._send = function (j) { var data = JSON.stringify(j); var packet = Buffer.alloc(data.length + 4); packet.writeUInt32LE(data.length + 4, 0); Buffer.from(data).copy(packet, 4); this.write(packet); }; this._daipc.push(c); c.parent = this; c.on('end', function () { removeRegisteredApp(this); }); c.on('data', function (chunk) { if (chunk.length < 4) { this.unshift(chunk); return; } var len = chunk.readUInt32LE(0); if (len > 8192) { removeRegisteredApp(this); this.end(); return; } if (chunk.length < len) { this.unshift(chunk); return; } var data = chunk.slice(4, len); try { data = JSON.parse(data.toString()); } catch (ex) { } if ((data == null) || (typeof data.cmd != 'string')) return; try { switch (data.cmd) { case 'requesthelp': if (this._registered == null) return; sendConsoleText('Request Help (' + this._registered + '): ' + data.value); var help = {}; help[this._registered] = data.value; try { mesh.SendCommand({ action: 'sessions', type: 'help', value: help }); } catch (ex) { } MeshServerLogEx(98, [this._registered, data.value], "Help Requested, user: " + this._registered + ", details: " + data.value, null); break; case 'cancelhelp': if (this._registered == null) return; sendConsoleText('Cancel Help (' + this._registered + ')'); try { mesh.SendCommand({ action: 'sessions', type: 'help', value: {} }); } catch (ex) { } break; case 'register': if (typeof data.value == 'string') { this._registered = data.value; var apps = {}; apps[data.value] = 1; try { mesh.SendCommand({ action: 'sessions', type: 'app', value: apps }); } catch (ex) { } this._send({ cmd: 'serverstate', value: meshServerConnectionState, url: require('MeshAgent').ConnectedServer, amt: (amt != null) }); } break; case 'query': switch (data.value) { case 'connection': data.result = require('MeshAgent').ConnectedServer; this._send(data); break; case 'descriptors': require('ChainViewer').getSnapshot().then(function (f) { this.tag.payload.result = f; this.tag.ipc._send(this.tag.payload); }).parentPromise.tag = { ipc: this, payload: data }; break; case 'timerinfo': data.result = require('ChainViewer').getTimerInfo(); this._send(data); break; } break; case 'amtstate': if (amt == null) return; var func = function amtStateFunc(state) { if (state != null) { amtStateFunc.pipe._send({ cmd: 'amtstate', value: state }); } } func.pipe = this; amt.getMeiState(11, func); break; case 'sessions': this._send({ cmd: 'sessions', sessions: tunnelUserCount }); break; case 'meshToolInfo': try { mesh.SendCommand({ action: 'meshToolInfo', name: data.name, hash: data.hash, cookie: data.cookie ? true : false, pipe: true }); } catch (ex) { } break; case 'getUserImage': try { mesh.SendCommand({ action: 'getUserImage', userid: data.userid, pipe: true }); } catch (ex) { } break; case 'console': if (debugConsole) { var args = splitArgs(data.value); processConsoleCommand(args[0].toLowerCase(), parseArgs(args), 0, 'pipe'); } break; } } catch (ex) { removeRegisteredApp(this); this.end(); return; } }); }); // Send current sessions to registered apps function broadcastSessionsToRegisteredApps(x) { var p = {}, i; for (i = 0; sendAgentMessage.messages != null && i < sendAgentMessage.messages.length; ++i) { p[i] = sendAgentMessage.messages[i]; } tunnelUserCount.msg = p; broadcastToRegisteredApps({ cmd: 'sessions', sessions: tunnelUserCount }); tunnelUserCount.msg = {}; } // Send this object to all registered local applications function broadcastToRegisteredApps(x) { if ((obj.DAIPC == null) || (obj.DAIPC._daipc == null)) return; for (var i in obj.DAIPC._daipc) { if (obj.DAIPC._daipc[i]._registered != null) { obj.DAIPC._daipc[i]._send(x); } } } // Send this object to a specific registered local applications function sendToRegisteredApp(appid, x) { if ((obj.DAIPC == null) || (obj.DAIPC._daipc == null)) return; for (var i in obj.DAIPC._daipc) { if (obj.DAIPC._daipc[i]._registered == appid) { obj.DAIPC._daipc[i]._send(x); } } } // Send list of registered apps to the server function updateRegisteredAppsToServer() { if ((obj.DAIPC == null) || (obj.DAIPC._daipc == null)) return; var apps = {}; for (var i in obj.DAIPC._daipc) { if (apps[obj.DAIPC._daipc[i]._registered] == null) { apps[obj.DAIPC._daipc[i]._registered] = 1; } else { apps[obj.DAIPC._daipc[i]._registered]++; } } try { mesh.SendCommand({ action: 'sessions', type: 'app', value: apps }); } catch (ex) { } } // Remove a registered app function removeRegisteredApp(pipe) { for (var i = obj.DAIPC._daipc.length - 1; i >= 0; i--) { if (obj.DAIPC._daipc[i] === pipe) { obj.DAIPC._daipc.splice(i, 1); } } if (pipe._registered != null) updateRegisteredAppsToServer(); } function diagnosticAgent_uninstall() { require('service-manager').manager.uninstallService('meshagentDiagnostic'); require('task-scheduler').delete('meshagentDiagnostic/periodicStart'); // TODO: Using "delete" here breaks the minifier since this is a reserved keyword } function diagnosticAgent_installCheck(install) { try { var diag = require('service-manager').manager.getService('meshagentDiagnostic'); return (diag); } catch (ex) { } if (!install) { return null; } var svc = null; try { require('service-manager').manager.installService( { name: 'meshagentDiagnostic', displayName: "Mesh Agent Diagnostic Service", description: "Mesh Agent Diagnostic Service", servicePath: process.execPath, parameters: ['-recovery'] //files: [{ newName: 'diagnostic.js', _buffer: Buffer.from('LyoNCkNvcHlyaWdodCAyMDE5IEludGVsIENvcnBvcmF0aW9uDQoNCkxpY2Vuc2VkIHVuZGVyIHRoZSBBcGFjaGUgTGljZW5zZSwgVmVyc2lvbiAyLjAgKHRoZSAiTGljZW5zZSIpOw0KeW91IG1heSBub3QgdXNlIHRoaXMgZmlsZSBleGNlcHQgaW4gY29tcGxpYW5jZSB3aXRoIHRoZSBMaWNlbnNlLg0KWW91IG1heSBvYnRhaW4gYSBjb3B5IG9mIHRoZSBMaWNlbnNlIGF0DQoNCiAgICBodHRwOi8vd3d3LmFwYWNoZS5vcmcvbGljZW5zZXMvTElDRU5TRS0yLjANCg0KVW5sZXNzIHJlcXVpcmVkIGJ5IGFwcGxpY2FibGUgbGF3IG9yIGFncmVlZCB0byBpbiB3cml0aW5nLCBzb2Z0d2FyZQ0KZGlzdHJpYnV0ZWQgdW5kZXIgdGhlIExpY2Vuc2UgaXMgZGlzdHJpYnV0ZWQgb24gYW4gIkFTIElTIiBCQVNJUywNCldJVEhPVVQgV0FSUkFOVElFUyBPUiBDT05ESVRJT05TIE9GIEFOWSBLSU5ELCBlaXRoZXIgZXhwcmVzcyBvciBpbXBsaWVkLg0KU2VlIHRoZSBMaWNlbnNlIGZvciB0aGUgc3BlY2lmaWMgbGFuZ3VhZ2UgZ292ZXJuaW5nIHBlcm1pc3Npb25zIGFuZA0KbGltaXRhdGlvbnMgdW5kZXIgdGhlIExpY2Vuc2UuDQoqLw0KDQp2YXIgaG9zdCA9IHJlcXVpcmUoJ3NlcnZpY2UtaG9zdCcpLmNyZWF0ZSgnbWVzaGFnZW50RGlhZ25vc3RpYycpOw0KdmFyIFJlY292ZXJ5QWdlbnQgPSByZXF1aXJlKCdNZXNoQWdlbnQnKTsNCg0KaG9zdC5vbignc2VydmljZVN0YXJ0JywgZnVuY3Rpb24gKCkNCnsNCiAgICBjb25zb2xlLnNldERlc3RpbmF0aW9uKGNvbnNvbGUuRGVzdGluYXRpb25zLkxPR0ZJTEUpOw0KICAgIGhvc3Quc3RvcCA9IGZ1bmN0aW9uKCkNCiAgICB7DQogICAgICAgIHJlcXVpcmUoJ3NlcnZpY2UtbWFuYWdlcicpLm1hbmFnZXIuZ2V0U2VydmljZSgnbWVzaGFnZW50RGlhZ25vc3RpYycpLnN0b3AoKTsNCiAgICB9DQogICAgUmVjb3ZlcnlBZ2VudC5vbignQ29ubmVjdGVkJywgZnVuY3Rpb24gKHN0YXR1cykNCiAgICB7DQogICAgICAgIGlmIChzdGF0dXMgPT0gMCkNCiAgICAgICAgew0KICAgICAgICAgICAgY29uc29sZS5sb2coJ0RpYWdub3N0aWMgQWdlbnQ6IFNlcnZlciBjb25uZWN0aW9uIGxvc3QuLi4nKTsNCiAgICAgICAgICAgIHJldHVybjsNCiAgICAgICAgfQ0KICAgICAgICBjb25zb2xlLmxvZygnRGlhZ25vc3RpYyBBZ2VudDogQ29ubmVjdGlvbiBFc3RhYmxpc2hlZCB3aXRoIFNlcnZlcicpOw0KICAgICAgICBzdGFydCgpOw0KICAgIH0pOw0KfSk7DQpob3N0Lm9uKCdub3JtYWxTdGFydCcsIGZ1bmN0aW9uICgpDQp7DQogICAgaG9zdC5zdG9wID0gZnVuY3Rpb24gKCkNCiAgICB7DQogICAgICAgIHByb2Nlc3MuZXhpdCgpOw0KICAgIH0NCiAgICBjb25zb2xlLmxvZygnTm9uIFNlcnZpY2UgTW9kZScpOw0KICAgIFJlY292ZXJ5QWdlbnQub24oJ0Nvbm5lY3RlZCcsIGZ1bmN0aW9uIChzdGF0dXMpDQogICAgew0KICAgICAgICBpZiAoc3RhdHVzID09IDApDQogICAgICAgIHsNCiAgICAgICAgICAgIGNvbnNvbGUubG9nKCdEaWFnbm9zdGljIEFnZW50OiBTZXJ2ZXIgY29ubmVjdGlvbiBsb3N0Li4uJyk7DQogICAgICAgICAgICByZXR1cm47DQogICAgICAgIH0NCiAgICAgICAgY29uc29sZS5sb2coJ0RpYWdub3N0aWMgQWdlbnQ6IENvbm5lY3Rpb24gRXN0YWJsaXNoZWQgd2l0aCBTZXJ2ZXInKTsNCiAgICAgICAgc3RhcnQoKTsNCiAgICB9KTsNCn0pOw0KaG9zdC5vbignc2VydmljZVN0b3AnLCBmdW5jdGlvbiAoKSB7IHByb2Nlc3MuZXhpdCgpOyB9KTsNCmhvc3QucnVuKCk7DQoNCg0KZnVuY3Rpb24gc3RhcnQoKQ0Kew0KDQp9Ow0K', 'base64') }] }); svc = require('service-manager').manager.getService('meshagentDiagnostic'); } catch (ex) { return null; } var proxyConfig = require('global-tunnel').proxyConfig; var cert = require('MeshAgent').GenerateAgentCertificate('CN=MeshNodeDiagnosticCertificate'); var nodeid = require('tls').loadCertificate(cert.root).getKeyHash().toString('base64'); ddb = require('SimpleDataStore').Create(svc.appWorkingDirectory().replace('\\', '/') + '/meshagentDiagnostic.db'); ddb.Put('disableUpdate', '1'); ddb.Put('MeshID', Buffer.from(require('MeshAgent').ServerInfo.MeshID, 'hex')); ddb.Put('ServerID', require('MeshAgent').ServerInfo.ServerID); ddb.Put('MeshServer', require('MeshAgent').ServerInfo.ServerUri); if (cert.root.pfx) { ddb.Put('SelfNodeCert', cert.root.pfx); } if (cert.tls) { ddb.Put('SelfNodeTlsCert', cert.tls.pfx); } if (proxyConfig) { ddb.Put('WebProxy', proxyConfig.host + ':' + proxyConfig.port); } else { ddb.Put('ignoreProxyFile', '1'); } require('MeshAgent').SendCommand({ action: 'diagnostic', value: { command: 'register', value: nodeid } }); require('MeshAgent').SendCommand({ action: 'msg', type: 'console', value: "Diagnostic Agent Registered [" + nodeid.length + "/" + nodeid + "]" }); delete ddb; // Set a recurrent task, to run the Diagnostic Agent every 2 days require('task-scheduler').create({ name: 'meshagentDiagnostic/periodicStart', daily: 2, time: require('tls').generateRandomInteger('0', '23') + ':' + require('tls').generateRandomInteger('0', '59').padStart(2, '0'), service: 'meshagentDiagnostic' }); //require('task-scheduler').create({ name: 'meshagentDiagnostic/periodicStart', daily: '1', time: '17:16', service: 'meshagentDiagnostic' }); return (svc); } // Monitor the file 'batterystate.txt' in the agent's folder and sends battery update when this file is changed. if ((require('fs').existsSync(process.cwd() + 'batterystate.txt')) && (require('fs').watch != null)) { // Setup manual battery monitoring require('MeshAgent')._batteryFileWatcher = require('fs').watch(process.cwd(), function () { if (require('MeshAgent')._batteryFileTimer != null) return; require('MeshAgent')._batteryFileTimer = setTimeout(function () { try { require('MeshAgent')._batteryFileTimer = null; var data = null; try { data = require('fs').readFileSync(process.cwd() + 'batterystate.txt').toString(); } catch (ex) { } if ((data != null) && (data.length < 10)) { data = data.split(','); if ((data.length == 2) && ((data[0] == 'ac') || (data[0] == 'dc'))) { var level = parseInt(data[1]); if ((level >= 0) && (level <= 100)) { require('MeshAgent').SendCommand({ action: 'battery', state: data[0], level: level }); } } } } catch (ex) { } }, 1000); }); } else { try { // Setup normal battery monitoring if (require('computer-identifiers').isBatteryPowered && require('computer-identifiers').isBatteryPowered()) { require('MeshAgent')._battLevelChanged = function _battLevelChanged(val) { _battLevelChanged.self._currentBatteryLevel = val; _battLevelChanged.self.SendCommand({ action: 'battery', state: _battLevelChanged.self._currentPowerState, level: val }); }; require('MeshAgent')._battLevelChanged.self = require('MeshAgent'); require('MeshAgent')._powerChanged = function _powerChanged(val) { _powerChanged.self._currentPowerState = (val == 'AC' ? 'ac' : 'dc'); _powerChanged.self.SendCommand({ action: 'battery', state: (val == 'AC' ? 'ac' : 'dc'), level: _powerChanged.self._currentBatteryLevel }); }; require('MeshAgent')._powerChanged.self = require('MeshAgent'); require('MeshAgent').on('Connected', function (status) { if (status == 0) { require('power-monitor').removeListener('acdc', this._powerChanged); require('power-monitor').removeListener('batteryLevel', this._battLevelChanged); } else { require('power-monitor').on('acdc', this._powerChanged); require('power-monitor').on('batteryLevel', this._battLevelChanged); } }); } } catch (ex) { } } // MeshAgent JavaScript Core Module. This code is sent to and running on the mesh agent. var meshCoreObj = { action: 'coreinfo', value: (require('MeshAgent').coreHash ? ((process.versions.compileTime ? process.versions.compileTime : '').split(', ')[1].replace(' ', ' ') + ', ' + crc32c(require('MeshAgent').coreHash)) : ('MeshCore v6')), caps: 14, root: require('user-sessions').isRoot() }; // Capability bitmask: 1 = Desktop, 2 = Terminal, 4 = Files, 8 = Console, 16 = JavaScript, 32 = Temporary Agent, 64 = Recovery Agent // Get the operating system description string try { require('os').name().then(function (v) { meshCoreObj.osdesc = v; meshCoreObjChanged(); }); } catch (ex) { } // Setup logged in user monitoring (THIS IS BROKEN IN WIN7) function onUserSessionChanged(user, locked) { userSession.enumerateUsers().then(function (users) { if (process.platform == 'linux') { if (userSession._startTime == null) { userSession._startTime = Date.now(); userSession._count = users.length; } else if (Date.now() - userSession._startTime < 10000 && users.length == userSession._count) { userSession.removeAllListeners('changed'); return; } } var u = [], a = users.Active; if(meshCoreObj.lusers == null) { meshCoreObj.lusers = []; } for (var i = 0; i < a.length; i++) { var un = a[i].Domain ? (a[i].Domain + '\\' + a[i].Username) : (a[i].Username); if (user && locked && (JSON.stringify(a[i]) === JSON.stringify(user))) { if (meshCoreObj.lusers.indexOf(un) == -1) { meshCoreObj.lusers.push(un); } } else if (user && !locked && (JSON.stringify(a[i]) === JSON.stringify(user))) { meshCoreObj.lusers.splice(meshCoreObj.lusers.indexOf(un), 1); } if (u.indexOf(un) == -1) { u.push(un); } // Only push users in the list once. } meshCoreObj.lusers = meshCoreObj.lusers; meshCoreObj.users = u; meshCoreObjChanged(); }); } try { var userSession = require('user-sessions'); userSession.on('changed', function () { onUserSessionChanged(null, false); }); userSession.emit('changed'); userSession.on('locked', function (user) { if(user != undefined && user != null) { onUserSessionChanged(user, true); } }); userSession.on('unlocked', function (user) { if(user != undefined && user != null) { onUserSessionChanged(user, false); } }); } catch (ex) { } var meshServerConnectionState = 0; var tunnels = {}; var lastNetworkInfo = null; var lastPublicLocationInfo = null; var selfInfoUpdateTimer = null; var http = require('http'); var net = require('net'); var fs = require('fs'); var rtc = require('ILibWebRTC'); var amt = null; var processManager = require('process-manager'); var wifiScannerLib = null; var wifiScanner = null; var networkMonitor = null; var nextTunnelIndex = 1; var apftunnel = null; var tunnelUserCount = { terminal: {}, files: {}, tcp: {}, udp: {}, msg: {} }; // List of userid->count sessions for terminal, files and TCP/UDP routing // Add to the server event log function MeshServerLog(msg, state) { if (typeof msg == 'string') { msg = { action: 'log', msg: msg }; } else { msg.action = 'log'; } if (state) { if (state.userid) { msg.userid = state.userid; } if (state.username) { msg.username = state.username; } if (state.sessionid) { msg.sessionid = state.sessionid; } if (state.remoteaddr) { msg.remoteaddr = state.remoteaddr; } if (state.guestname) { msg.guestname = state.guestname; } } mesh.SendCommand(msg); } // Add to the server event log, use internationalized events function MeshServerLogEx(id, args, msg, state) { var msg = { action: 'log', msgid: id, msgArgs: args, msg: msg }; if (state) { if (state.userid) { msg.userid = state.userid; } if (state.xuserid) { msg.xuserid = state.xuserid; } if (state.username) { msg.username = state.username; } if (state.sessionid) { msg.sessionid = state.sessionid; } if (state.remoteaddr) { msg.remoteaddr = state.remoteaddr; } if (state.guestname) { msg.guestname = state.guestname; } } mesh.SendCommand(msg); } // Import libraries db = require('SimpleDataStore').Shared(); sha = require('SHA256Stream'); mesh = require('MeshAgent'); childProcess = require('child_process'); if (mesh.hasKVM == 1) { // if the agent is compiled with KVM support // Check if this computer supports a desktop try { if ((process.platform == 'win32') || (process.platform == 'darwin') || (require('monitor-info').kvm_x11_support)) { meshCoreObj.caps |= 1; meshCoreObjChanged(); } else if (process.platform == 'linux' || process.platform == 'freebsd') { require('monitor-info').on('kvmSupportDetected', function (value) { meshCoreObj.caps |= 1; meshCoreObjChanged(); }); } } catch (ex) { } } mesh.DAIPC = obj.DAIPC; /* // Try to load up the network monitor try { networkMonitor = require('NetworkMonitor'); networkMonitor.on('change', function () { sendNetworkUpdateNagle(); }); networkMonitor.on('add', function (addr) { sendNetworkUpdateNagle(); }); networkMonitor.on('remove', function (addr) { sendNetworkUpdateNagle(); }); } catch (ex) { networkMonitor = null; } */ // Fetch the SMBios Tables var SMBiosTables = null; var SMBiosTablesRaw = null; try { var SMBiosModule = null; try { SMBiosModule = require('smbios'); } catch (ex) { } if (SMBiosModule != null) { SMBiosModule.get(function (data) { if (data != null) { SMBiosTablesRaw = data; SMBiosTables = require('smbios').parse(data) if (mesh.isControlChannelConnected) { mesh.SendCommand({ action: 'smbios', value: SMBiosTablesRaw }); } // If SMBios tables say that Intel AMT is present, try to connect MEI if (SMBiosTables.amtInfo && (SMBiosTables.amtInfo.AMT == true)) { var amtmodule = require('amt-manage'); amt = new amtmodule(mesh, db, false); amt.on('portBinding_LMS', function (map) { mesh.SendCommand({ action: 'lmsinfo', value: { ports: map.keys() } }); }); amt.on('stateChange_LMS', function (v) { if (!meshCoreObj.intelamt) { meshCoreObj.intelamt = {}; } meshCoreObj.intelamt.microlms = v; meshCoreObjChanged(); }); // 0 = Disabled, 1 = Connecting, 2 = Connected amt.onStateChange = function (state) { if (state == 2) { sendPeriodicServerUpdate(1); } } // MEI State amt.reset(); } } }); } } catch (ex) { sendConsoleText("ex1: " + ex); } // Try to load up the WIFI scanner try { var wifiScannerLib = require('wifi-scanner'); wifiScanner = new wifiScannerLib(); wifiScanner.on('accessPoint', function (data) { sendConsoleText("wifiScanner: " + data); }); } catch (ex) { wifiScannerLib = null; wifiScanner = null; } // Get our location (lat/long) using our public IP address var getIpLocationDataExInProgress = false; var getIpLocationDataExCounts = [0, 0]; function getIpLocationDataEx(func) { if (getIpLocationDataExInProgress == true) { return false; } try { getIpLocationDataExInProgress = true; getIpLocationDataExCounts[0]++; var options = http.parseUri("http://ipinfo.io/json"); options.method = 'GET'; http.request(options, function (resp) { if (resp.statusCode == 200) { var geoData = ''; resp.data = function (geoipdata) { geoData += geoipdata; }; resp.end = function () { var location = null; try { if (typeof geoData == 'string') { var result = JSON.parse(geoData); if (result.ip && result.loc) { location = result; } } } catch (ex) { } if (func) { getIpLocationDataExCounts[1]++; func(location); } } } else { func(null); } getIpLocationDataExInProgress = false; }).end(); return true; } catch (ex) { return false; } } // Remove all Gateway MAC addresses for interface list. This is useful because the gateway MAC is not always populated reliably. function clearGatewayMac(str) { if (typeof str != 'string') return null; var x = JSON.parse(str); for (var i in x.netif) { try { if (x.netif[i].gatewaymac) { delete x.netif[i].gatewaymac } } catch (ex) { } } return JSON.stringify(x); } function getIpLocationData(func) { // Get the location information for the cache if possible var publicLocationInfo = db.Get('publicLocationInfo'); if (publicLocationInfo != null) { publicLocationInfo = JSON.parse(publicLocationInfo); } if (publicLocationInfo == null) { // Nothing in the cache, fetch the data getIpLocationDataEx(function (locationData) { if (locationData != null) { publicLocationInfo = {}; publicLocationInfo.netInfoStr = lastNetworkInfo; publicLocationInfo.locationData = locationData; var x = db.Put('publicLocationInfo', JSON.stringify(publicLocationInfo)); // Save to database if (func) func(locationData); // Report the new location } else { if (func) func(null); // Report no location } }); } else { // Check the cache if (clearGatewayMac(publicLocationInfo.netInfoStr) == clearGatewayMac(lastNetworkInfo)) { // Cache match if (func) func(publicLocationInfo.locationData); } else { // Cache mismatch getIpLocationDataEx(function (locationData) { if (locationData != null) { publicLocationInfo = {}; publicLocationInfo.netInfoStr = lastNetworkInfo; publicLocationInfo.locationData = locationData; var x = db.Put('publicLocationInfo', JSON.stringify(publicLocationInfo)); // Save to database if (func) func(locationData); // Report the new location } else { if (func) func(publicLocationInfo.locationData); // Can't get new location, report the old location } }); } } } // Polyfill String.endsWith if (!String.prototype.endsWith) { String.prototype.endsWith = function (searchString, position) { var subjectString = this.toString(); if (typeof position !== 'number' || !isFinite(position) || Math.floor(position) !== position || position > subjectString.length) { position = subjectString.length; } position -= searchString.length; var lastIndex = subjectString.lastIndexOf(searchString, position); return lastIndex !== -1 && lastIndex === position; }; } // Polyfill path.join obj.path = { join: function () { var x = []; for (var i in arguments) { var w = arguments[i]; if (w != null) { while (w.endsWith('/') || w.endsWith('\\')) { w = w.substring(0, w.length - 1); } if (i != 0) { while (w.startsWith('/') || w.startsWith('\\')) { w = w.substring(1); } } x.push(w); } } if (x.length == 0) return '/'; return x.join('/'); } }; // Replace a string with a number if the string is an exact number function toNumberIfNumber(x) { if ((typeof x == 'string') && (+parseInt(x) === x)) { x = parseInt(x); } return x; } // Convert decimal to hex function char2hex(i) { return (i + 0x100).toString(16).substr(-2).toUpperCase(); } // Convert a raw string to a hex string function rstr2hex(input) { var r = '', i; for (i = 0; i < input.length; i++) { r += char2hex(input.charCodeAt(i)); } return r; } // Convert a buffer into a string function buf2rstr(buf) { var r = ''; for (var i = 0; i < buf.length; i++) { r += String.fromCharCode(buf[i]); } return r; } // Convert a hex string to a raw string // TODO: Do this using Buffer(), will be MUCH faster function hex2rstr(d) { if (typeof d != "string" || d.length == 0) return ''; var r = '', m = ('' + d).match(/../g), t; while (t = m.shift()) r += String.fromCharCode('0x' + t); return r } // Convert an object to string with all functions function objToString(x, p, pad, ret) { if (ret == undefined) ret = ''; if (p == undefined) p = 0; if (x == null) { return '[null]'; } if (p > 8) { return '[...]'; } if (x == undefined) { return '[undefined]'; } if (typeof x == 'string') { if (p == 0) return x; return '"' + x + '"'; } if (typeof x == 'buffer') { return '[buffer]'; } if (typeof x != 'object') { return x; } var r = '{' + (ret ? '\r\n' : ' '); for (var i in x) { if (i != '_ObjectID') { r += (addPad(p + 2, pad) + i + ': ' + objToString(x[i], p + 2, pad, ret) + (ret ? '\r\n' : ' ')); } } return r + addPad(p, pad) + '}'; } // Return p number of spaces function addPad(p, ret) { var r = ''; for (var i = 0; i < p; i++) { r += ret; } return r; } // Split a string taking into account the quoats. Used for command line parsing function splitArgs(str) { var myArray = [], myRegexp = /[^\s"]+|"([^"]*)"/gi; do { var match = myRegexp.exec(str); if (match != null) { myArray.push(match[1] ? match[1] : match[0]); } } while (match != null); return myArray; } // Parse arguments string array into an object function parseArgs(argv) { var results = { '_': [] }, current = null; for (var i = 1, len = argv.length; i < len; i++) { var x = argv[i]; if (x.length > 2 && x[0] == '-' && x[1] == '-') { if (current != null) { results[current] = true; } current = x.substring(2); } else { if (current != null) { results[current] = toNumberIfNumber(x); current = null; } else { results['_'].push(toNumberIfNumber(x)); } } } if (current != null) { results[current] = true; } return results; } // Get server target url with a custom path function getServerTargetUrl(path) { var x = mesh.ServerUrl; //sendConsoleText("mesh.ServerUrl: " + mesh.ServerUrl); if (x == null) { return null; } if (path == null) { path = ''; } x = http.parseUri(x); if (x == null) return null; return x.protocol + '//' + x.host + ':' + x.port + '/' + path; } //