Added support for MeshCmd routing.

Ylian Saint-Hilaire committed Oct 31, 2017 at 16:19 UTC e75adafb05901cbc690369e74bde689cd9b6ad03
21 files changed +343 -70
agents/MeshConsole.exe
Binary files /dev/null and b/agents/MeshConsole.exe differ
agents/MeshConsole64.exe
Binary files /dev/null and b/agents/MeshConsole64.exe differ
agents/MeshService.exe
Binary files a/agents/MeshService.exe and b/agents/MeshService.exe differ
agents/MeshService64.exe
Binary files a/agents/MeshService64.exe and b/agents/MeshService64.exe differ
agents/meshagent_arm
Binary files a/agents/meshagent_arm and b/agents/meshagent_arm differ
agents/meshagent_pi
Binary files a/agents/meshagent_pi and b/agents/meshagent_pi differ
agents/meshagent_pogo
Binary files a/agents/meshagent_pogo and b/agents/meshagent_pogo differ
agents/meshagent_poky
Binary files a/agents/meshagent_poky and b/agents/meshagent_poky differ
agents/meshagent_poky64
Binary files a/agents/meshagent_poky64 and b/agents/meshagent_poky64 differ
agents/meshagent_x86
Binary files a/agents/meshagent_x86 and b/agents/meshagent_x86 differ
agents/meshagent_x86-64
Binary files a/agents/meshagent_x86-64 and b/agents/meshagent_x86-64 differ
agents/meshagent_x86-64_nokvm
Binary files a/agents/meshagent_x86-64_nokvm and b/agents/meshagent_x86-64_nokvm differ
agents/meshagent_x86_nokvm
Binary files a/agents/meshagent_x86_nokvm and b/agents/meshagent_x86_nokvm differ
agents/meshcmd.js new
+170
@@ -0,0 +1,170 @@
1 +var fs = require('fs');
2 +var os = require('os');
3 +var net = require('net');
4 +var http = require('http');
5 +var dgram = require('dgram');
6 +var httpHeaders = require('http-headers');
7 +var tcpserver = null;
8 +var broadcastSockets = {};
9 +var multicastSockets = {};
10 +var discoveryInterval = null;
11 +var membershipIPv4 = '239.255.255.235';
12 +var membershipIPv6 = 'FF02:0:0:0:0:0:0:FE';
13 +
14 +/*
15 +// Route Settings
16 +var settings = {
17 + action: 'route',
18 + localPort: 1234,
19 + remoteName: 'AmtMachine7',
20 + remoteNodeId: 'node//nmiPnDhT3vHKu$zg296YC5RjK53Trgh3Cimx3K8GVrFh$xch0UAAett2rbJpeddc',
21 + remotePort: 3389,
22 + username: 'a',
23 + password: 'a',
24 + serverUrl: 'wss://devbox.mesh.meshcentral.com:443/meshrelay.ashx',
25 + serverId: 'D99362D5ED8BAEA8BF9E743B34B242256370C460FD66CB62373C6CFCB204D6D707403E396CF0EF6DC2B3A42F735135FD', // SHA384 of server HTTPS public key
26 + serverHttpsHash: 'D9DE9E27A229B5355708A3672FB23237CC994A680B3570D242A91E36B4AE5BC9', // SHA256 of server HTTPS certificate
27 + debugLevel: 0
28 +}
29 +*/
30 +
31 +// Check the server certificate fingerprint
32 +function onVerifyServer(clientName, certs) {
33 + try { for (var i in certs) { if (certs[i].fingerprint.replace(/:/g, '') == settings.serverHttpsHash) { return; } } } catch (e) { }
34 + if (serverhash != null) { console.log('Error: Failed to verify server certificate.'); return false; }
35 +}
36 +
37 +// Print a debug message
38 +function debug(level, message) { if ((settings.debugLevel != null) && (settings.debugLevel >= level)) { console.log(message); } }
39 +
40 +// Start the router, start by listening to the local port
41 +function run(argv) {
42 + console.log('MeshCentral Command v1.0');
43 + var actionpath = 'meshaction.txt';
44 + if (argv.length >= 2) { actionpath = argv[1]; }
45 +
46 + // Load the action file
47 + var actionfile = null;
48 + try { actionfile = fs.readFileSync(actionpath); } catch (e) { }
49 + if (actionfile == null) { console.log('Unable to load \"' + actionpath + '\". Create this file or specify the location as the first argument.'); process.exit(1); }
50 + try { settings = JSON.parse(actionfile); } catch (e) { console.log(actionpath, e); process.exit(1); }
51 +
52 + // Validate meshaction.txt
53 + if (settings.action == null) { console.log('No \"action\" specified.'); process.exit(1); }
54 + settings.action = settings.action.toLowerCase();
55 + if (settings.action == 'route') {
56 + if ((settings.localPort == null) || (typeof settings.localPort != 'number') || (settings.localPort < 0) || (settings.localPort > 65535)) { console.log('No or invalid \"localPort\" specified.'); process.exit(1); }
57 + if ((settings.remoteNodeId == null) || (typeof settings.remoteNodeId != 'string')) { console.log('No or invalid \"remoteNodeId\" specified.'); process.exit(1); }
58 + if ((settings.username == null) || (typeof settings.username != 'string')) { console.log('No or invalid \"username\" specified.'); process.exit(1); }
59 + if ((settings.password == null) || (typeof settings.password != 'string') || (settings.password == '')) { console.log('No or invalid \"password\" specified.'); process.exit(1); }
60 + if ((settings.serverId == null) || (typeof settings.serverId != 'string') || (settings.serverId.length != 96)) { console.log('No or invalid \"serverId\" specified.'); process.exit(1); }
61 + if ((settings.serverHttpsHash == null) || (typeof settings.serverHttpsHash != 'string') || (settings.serverHttpsHash.length != 96)) { console.log('No or invalid \"serverHttpsHash\" specified.'); process.exit(1); }
62 + if ((settings.remotePort == null) || (typeof settings.remotePort != 'number') || (settings.remotePort < 0) || (settings.remotePort > 65535)) { console.log('No or invalid \"remotePort\" specified.'); process.exit(1); }
63 + } else {
64 + console.log('Invalid \"action\" specified.'); process.exit(1);
65 + }
66 +
67 + debug(1, "Settings: " + JSON.stringify(settings));
68 + if (settings.serverUrl != null) { startRouter(); } else { discoverMeshServer(); }
69 +}
70 +
71 +// Starts the router
72 +function startRouter() {
73 + tcpserver = net.createServer(OnTcpClientConnected);
74 + tcpserver.on('error', function (err) { console.log(err); process.exit(0); });
75 + tcpserver.listen(settings.localPort, function () {
76 + // We started listening.
77 + if (settings.remoteName == null) {
78 + console.log('Redirecting local port ' + settings.localPort + ' to remote port ' + settings.remotePort + '.');
79 + } else {
80 + console.log('Redirecting local port ' + settings.localPort + ' to ' + settings.remoteName + ':' + settings.remotePort + '.');
81 + }
82 + console.log('Press ctrl-c to terminal.');
83 +
84 + // If settings has a "cmd", run it now.
85 + //process.exec("notepad.exe");
86 + });
87 +}
88 +
89 +// Called when a TCP connect is received on the local port. Launch a tunnel.
90 +function OnTcpClientConnected(c) {
91 + try {
92 + // 'connection' listener
93 + debug(1, 'Client connected');
94 + c.on('end', function () { disconnectTunnel(this, this.websocket, 'Client closed'); });
95 + c.pause();
96 +
97 + try {
98 + options = http.parseUri(settings.serverUrl + '?user=' + settings.username + '&pass=' + settings.password + '&nodeid=' + settings.remoteNodeId + '&tcpport=' + settings.remotePort);
99 + } catch (e) { console.log('Unable to parse \"serverUrl\".'); process.exit(1); }
100 + options.checkServerIdentity = onVerifyServer;
101 + c.websocket = http.request(options);
102 + c.websocket.tcp = c;
103 + c.websocket.tunneling = false;
104 + c.websocket.upgrade = OnWebSocket;
105 + c.websocket.on('error', function (msg) { console.log(msg); });
106 + c.websocket.end();
107 + } catch (e) { debug(2, e); }
108 +}
109 +
110 +// Disconnect both TCP & WebSocket connections and display a message.
111 +function disconnectTunnel(tcp, ws, msg) {
112 + if (ws != null) { try { ws.end(); } catch (e) { debug(2, e); } }
113 + if (tcp != null) { try { tcp.end(); } catch (e) { debug(2, e); } }
114 + debug(1, 'Tunnel disconnected: ' + msg);
115 +}
116 +
117 +// Called when the web socket gets connected
118 +function OnWebSocket(msg, s, head) {
119 + debug(1, 'Websocket connected');
120 + s.on('data', function (msg) {
121 + if (this.parent.tunneling == false) {
122 + msg = msg.toString();
123 + if (msg == 'c') {
124 + this.parent.tunneling = true; this.pipe(this.parent.tcp); this.parent.tcp.pipe(this); debug(1, 'Tunnel active');
125 + } else if ((msg.length > 6) && (msg.substring(0, 6) == 'error:')) {
126 + console.log(msg.substring(6));
127 + disconnectTunnel(this.tcp, this, msg.substring(6));
128 + }
129 + }
130 + });
131 + s.on('error', function (msg) { disconnectTunnel(this.tcp, this, 'Websocket error'); });
132 + s.on('close', function (msg) { disconnectTunnel(this.tcp, this, 'Websocket closed'); });
133 + s.parent = this;
134 +}
135 +
136 +// Try to discover the location of the mesh server
137 +function discoverMeshServer() { console.log('Looking for server...'); discoveryInterval = setInterval(discoverMeshServerOnce, 5000); discoverMeshServerOnce(); }
138 +
139 +// Try to discover the location of the mesh server only once
140 +function discoverMeshServerOnce() {
141 + var interfaces = os.networkInterfaces();
142 + for (var adapter in interfaces) {
143 + if (interfaces.hasOwnProperty(adapter)) {
144 + for (var i = 0 ; i < interfaces[adapter].length; ++i) {
145 + var addr = interfaces[adapter][i];
146 + multicastSockets[i] = dgram.createSocket({ type: (addr.family == "IPv4" ? "udp4" : "udp6") });
147 + multicastSockets[i].bind({ address: addr.address, exclusive: false });
148 + if (addr.family == "IPv4") {
149 + multicastSockets[i].addMembership(membershipIPv4);
150 + //multicastSockets[i].setMulticastLoopback(true);
151 + multicastSockets[i].once('message', OnMulticastMessage);
152 + multicastSockets[i].send(settings.serverId, 16989, membershipIPv4);
153 + }
154 + }
155 + }
156 + }
157 +}
158 +
159 +// Called when a multicast packet is received
160 +function OnMulticastMessage(msg, rinfo) {
161 + var m = msg.toString().split('|');
162 + if ((m.length == 3) && (m[0] == 'MeshCentral2') && (m[1] == settings.serverId)) {
163 + settings.serverUrl = m[2].replace('%s', rinfo.address).replace('/agent.ashx', '/meshrelay.ashx');
164 + console.log('Found server at ' + settings.serverUrl + '.');
165 + if (discoveryInterval != null) { clearInterval(discoveryInterval); discoveryInterval = null; }
166 + startRouter();
167 + }
168 +}
169 +
170 +try { run(process.argv); } catch (e) { /*console.log(e);*/ }
agents/meshinstall-linux.sh
+3 -2
@@ -59,7 +59,7 @@ CheckInstallAgent() {
59 DownloadAgent $url $meshid $machineid
60 fi
61 else
62 - echo "MeshID is not correct, must be 64 HEX characters long."
62 + echo "MeshID is not correct, must be 64 characters long."
63 fi
64 else
65 echo "URI and/or MeshID have not been specified, must be passed in as arguments."
@@ -78,7 +78,7 @@ DownloadAgent() {
78 wget $url/meshagents?id=$machineid -q --no-check-certificate -O /usr/local/mesh/meshagent
79 if [ $? -eq 0 ]
80 then
81 - echo "Mesh agent download."
81 + echo "Mesh agent downloaded."
82 # TODO: We could check the meshagent sha256 hash, but best to authenticate the server.
83 chmod 755 /usr/local/mesh/meshagent
84 wget $url/meshsettings?id=$meshid -q --no-check-certificate -O /usr/local/mesh/meshagent.msh
@@ -97,6 +97,7 @@ DownloadAgent() {
97 ln -s /usr/local/mesh/meshagent /etc/rc3.d/S20mesh
98 ln -s /usr/local/mesh/meshagent /etc/rc5.d/S20mesh
99 fi
100 + echo "Mesh agent started."
101 else
102 echo "Unable to download mesh settings at: $url/meshsettings?id=$meshid."
103 fi
meshagent.js
+1 -1
@@ -131,7 +131,7 @@ module.exports.CreateMeshAgent = function (parent, db, ws, req, args, domain) {
131 if (len == agentUpdateBlockSize) { obj.ws.send(obj.agentUpdate.buf); } else { obj.ws.send(obj.agentUpdate.buf.slice(0, len + 4)); } // Command 14, mesh agent next data block
132
133 if (len < agentUpdateBlockSize) {
134 - console.log("Agent update sent");
134 + //console.log("Agent update sent");
135 obj.send(obj.common.ShortToStr(13) + obj.common.ShortToStr(0) + obj.common.hex2rstr(obj.agentExeInfo.hash)); // Command 13, end mesh agent download, send agent SHA384 hash
136 obj.fs.close(obj.agentUpdate.fd);
137 obj.agentUpdate = null;
meshrelay.js
+84 -59
@@ -4,7 +4,7 @@
4 * @version v0.0.1
5 */
6
7 -module.exports.CreateMeshRelay = function (parent, ws, req) {
7 +module.exports.CreateMeshRelay = function (parent, ws, req, domain) {
8 var obj = {};
9 obj.ws = ws;
10 obj.req = req;
@@ -12,6 +12,7 @@ module.exports.CreateMeshRelay = function (parent, ws, req) {
12 obj.parent = parent;
13 obj.id = req.query.id;
14 obj.remoteaddr = obj.ws._socket.remoteAddress;
15 + obj.domain = domain;
16 if (obj.remoteaddr.startsWith('::ffff:')) { obj.remoteaddr = obj.remoteaddr.substring(7); }
17
18 // Disconnect this agent
@@ -60,7 +61,27 @@ module.exports.CreateMeshRelay = function (parent, ws, req) {
61
62 if (req.query.auth == null) {
63 // Use ExpressJS session, check if this session is a logged in user, at least one of the two connections will need to be authenticated.
63 - try { if ((req.session) && (req.session.userid) || (req.session.domainid == getDomain(req).id)) { obj.authenticated = true; } } catch (e) { }
64 + try { if ((req.session) && (req.session.userid) || (req.session.domainid == obj.domain.id)) { obj.authenticated = true; } } catch (e) { }
65 + if ((obj.authenticated != true) && (req.query.user != null) && (req.query.pass != null)) {
66 + // Check user authentication
67 + obj.parent.authenticate(req.query.user, req.query.pass, obj.domain, function (err, userid, passhint) {
68 + if (userid != null) {
69 + obj.authenticated = true;
70 + // Check is we have agent routing instructions, process this here.
71 + if ((req.query.nodeid != null) && (req.query.tcpport != null)) {
72 + if (obj.id == undefined) { obj.id = ('' + Math.random()).substring(2); } // If there is no connection id, generate one.
73 + var command = { nodeid: req.query.nodeid, action: 'msg', type: 'tunnel', value: '*/meshrelay.ashx?id=' + obj.id, tcpport: req.query.tcpport, tcpaddr: ((req.query.tcpaddr == null) ? '127.0.0.1' : req.query.tcpaddr) };
74 + if (obj.sendAgentMessage(command, userid, obj.domain.id) == false) { obj.id = null; obj.parent.parent.debug(1, 'Relay: Unable to contact this agent (' + obj.remoteaddr + ')'); }
75 + }
76 + } else {
77 + obj.parent.parent.debug(1, 'Relay: User authentication failed (' + obj.remoteaddr + ')');
78 + obj.ws.send('error:Authentication failed');
79 + }
80 + performRelay();
81 + });
82 + } else {
83 + performRelay();
84 + }
85 } else {
86 // Get the session from the cookie
87 var cookie = obj.parent.parent.webserver.decodeCookie(req.query.auth);
@@ -76,74 +97,78 @@ module.exports.CreateMeshRelay = function (parent, ws, req) {
97 } else {
98 obj.id = null;
99 obj.parent.parent.debug(1, 'Relay: invalid cookie (' + obj.remoteaddr + ')');
100 + obj.ws.send('error:Invalid cookie');
101 }
102 + performRelay();
103 }
104
82 - if (obj.id == null) { try { obj.close(); } catch (e) { } return null; } // Attempt to connect without id, drop this.
83 - ws._socket.setKeepAlive(true, 240000); // Set TCP keep alive
105 + function performRelay() {
106 + if (obj.id == null) { try { obj.close(); } catch (e) { } return null; } // Attempt to connect without id, drop this.
107 + ws._socket.setKeepAlive(true, 240000); // Set TCP keep alive
108
85 - // Validate that the id is valid, we only need to do this on non-authenticated sessions.
86 - // TODO: Figure out when this needs to be done.
87 - /*
88 - if (!parent.args.notls) {
89 - // Check the identifier, if running without TLS, skip this.
90 - var ids = obj.id.split(':');
91 - if (ids.length != 3) { obj.ws.close(); obj.id = null; return null; } // Invalid ID, drop this.
92 - if (parent.crypto.createHmac('SHA384', parent.relayRandom).update(ids[0] + ':' + ids[1]).digest('hex') != ids[2]) { obj.ws.close(); obj.id = null; return null; } // Invalid HMAC, drop this.
93 - if ((Date.now() - parseInt(ids[1])) > 120000) { obj.ws.close(); obj.id = null; return null; } // Expired time, drop this.
94 - obj.id = ids[0];
95 - }
96 - */
109 + // Validate that the id is valid, we only need to do this on non-authenticated sessions.
110 + // TODO: Figure out when this needs to be done.
111 + /*
112 + if (!parent.args.notls) {
113 + // Check the identifier, if running without TLS, skip this.
114 + var ids = obj.id.split(':');
115 + if (ids.length != 3) { obj.ws.close(); obj.id = null; return null; } // Invalid ID, drop this.
116 + if (parent.crypto.createHmac('SHA384', parent.relayRandom).update(ids[0] + ':' + ids[1]).digest('hex') != ids[2]) { obj.ws.close(); obj.id = null; return null; } // Invalid HMAC, drop this.
117 + if ((Date.now() - parseInt(ids[1])) > 120000) { obj.ws.close(); obj.id = null; return null; } // Expired time, drop this.
118 + obj.id = ids[0];
119 + }
120 + */
121 +
122 + // Check the peer connection status
123 + {
124 + var relayinfo = parent.wsrelays[obj.id];
125 + if (relayinfo) {
126 + if (relayinfo.state == 1) {
127 + // Check that at least one connection is authenticated
128 + if ((obj.authenticated != true) && (relayinfo.peer1.authenticated != true)) {
129 + obj.id = null;
130 + obj.ws.close();
131 + obj.parent.parent.debug(1, 'Relay without-auth: ' + obj.id + ' (' + obj.remoteaddr + ')');
132 + return null;
133 + }
134
98 - // Check the peer connection status
99 - {
100 - var relayinfo = parent.wsrelays[obj.id];
101 - if (relayinfo) {
102 - if (relayinfo.state == 1) {
103 - // Check that at least one connection is authenticated
104 - if ((obj.authenticated != true) && (relayinfo.peer1.authenticated != true)) {
135 + // Connect to peer
136 + obj.peer = relayinfo.peer1;
137 + obj.peer.peer = obj;
138 + relayinfo.peer2 = obj;
139 + relayinfo.state = 2;
140 + obj.ws.send('c'); // Send connect to both peers
141 + relayinfo.peer1.ws.send('c');
142 + relayinfo.peer1.ws.resume(); // Release the traffic
143 +
144 + relayinfo.peer1.ws.peer = relayinfo.peer2.ws;
145 + relayinfo.peer2.ws.peer = relayinfo.peer1.ws;
146 +
147 + obj.parent.parent.debug(1, 'Relay connected: ' + obj.id + ' (' + obj.remoteaddr + ' --> ' + obj.peer.remoteaddr + ')');
148 + } else {
149 + // Connected already, drop (TODO: maybe we should re-connect?)
150 obj.id = null;
151 obj.ws.close();
107 - obj.parent.parent.debug(1, 'Relay without-auth: ' + obj.id + ' (' + obj.remoteaddr + ')');
152 + obj.parent.parent.debug(1, 'Relay duplicate: ' + obj.id + ' (' + obj.remoteaddr + ')');
153 return null;
154 }
110 -
111 - // Connect to peer
112 - obj.peer = relayinfo.peer1;
113 - obj.peer.peer = obj;
114 - relayinfo.peer2 = obj;
115 - relayinfo.state = 2;
116 - obj.ws.send('c'); // Send connect to both peers
117 - relayinfo.peer1.ws.send('c');
118 - relayinfo.peer1.ws.resume(); // Release the traffic
119 -
120 - relayinfo.peer1.ws.peer = relayinfo.peer2.ws;
121 - relayinfo.peer2.ws.peer = relayinfo.peer1.ws;
122 -
123 - obj.parent.parent.debug(1, 'Relay connected: ' + obj.id + ' (' + obj.remoteaddr + ' --> ' + obj.peer.remoteaddr + ')');
155 } else {
125 - // Connected already, drop (TODO: maybe we should re-connect?)
126 - obj.id = null;
127 - obj.ws.close();
128 - obj.parent.parent.debug(1, 'Relay duplicate: ' + obj.id + ' (' + obj.remoteaddr + ')');
129 - return null;
130 - }
131 - } else {
132 - // Wait for other relay connection
133 - ws.pause(); // Hold traffic until the other connection
134 - parent.wsrelays[obj.id] = { peer1: obj, state: 1 };
135 - obj.parent.parent.debug(1, 'Relay holding: ' + obj.id + ' (' + obj.remoteaddr + ')');
156 + // Wait for other relay connection
157 + ws.pause(); // Hold traffic until the other connection
158 + parent.wsrelays[obj.id] = { peer1: obj, state: 1 };
159 + obj.parent.parent.debug(1, 'Relay holding: ' + obj.id + ' (' + obj.remoteaddr + ')');
160
137 - // Check if a peer server has this connection
138 - if (parent.parent.multiServer != null) {
139 - var rsession = obj.parent.wsPeerRelays[obj.id];
140 - if ((rsession != null) && (rsession.serverId > obj.parent.parent.serverId)) {
141 - // We must initiate the connection to the peer
142 - parent.parent.multiServer.createPeerRelay(ws, req, rsession.serverId, req.session.userid);
143 - delete parent.wsrelays[obj.id];
144 - } else {
145 - // Send message to other peers that we have this connection
146 - parent.parent.multiServer.DispatchMessage(JSON.stringify({ action: 'relay', id: obj.id }));
161 + // Check if a peer server has this connection
162 + if (parent.parent.multiServer != null) {
163 + var rsession = obj.parent.wsPeerRelays[obj.id];
164 + if ((rsession != null) && (rsession.serverId > obj.parent.parent.serverId)) {
165 + // We must initiate the connection to the peer
166 + parent.parent.multiServer.createPeerRelay(ws, req, rsession.serverId, req.session.userid);
167 + delete parent.wsrelays[obj.id];
168 + } else {
169 + // Send message to other peers that we have this connection
170 + parent.parent.multiServer.DispatchMessage(JSON.stringify({ action: 'relay', id: obj.id }));
171 + }
172 }
173 }
174 }
meshuser.js
+2 -2
@@ -338,7 +338,7 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain) {
338 // TODO: Right now, we only create type 1 Agent-less Intel AMT mesh, or type 2 Agent mesh
339 if ((command.meshtype == 1) || (command.meshtype == 2)) {
340 // Create a type 1 agent-less Intel AMT mesh.
341 - obj.crypto.randomBytes(48, function (err, buf) {
341 + obj.parent.crypto.randomBytes(48, function (err, buf) {
342 var meshid = 'mesh/' + domain.id + '/' + buf.toString('base64').replace(/\+/g, '@').replace(/\//g, '$');;
343 var links = {}
344 links[user._id] = { name: user.name, rights: 0xFFFFFFFF };
@@ -492,7 +492,7 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain) {
492 if (mesh.links[user._id] == null || ((mesh.links[user._id].rights & 4) == 0)) return;
493
494 // Create a new nodeid
495 - obj.crypto.randomBytes(48, function (err, buf) {
495 + obj.parent.crypto.randomBytes(48, function (err, buf) {
496 // create the new node
497 var nodeid = 'node/' + domain.id + '/' + buf.toString('base64').replace(/\+/g, '@').replace(/\//g, '$');;
498 var device = { type: 'node', mtype: 1, _id: nodeid, meshid: command.meshid, name: command.devicename, host: command.hostname, domain: domain.id, intelamt: { user: command.amtusername, pass: command.amtpassword, tls: parseInt(command.amttls) } };
package.json
+1 -1
@@ -1,6 +1,6 @@
1 {
2 "name": "meshcentral",
3 - "version": "0.0.8-w",
3 + "version": "0.1.0-d",
4 "keywords": [
5 "Remote Management",
6 "Intel AMT",
views/default.handlebars
+37 -3
@@ -25,7 +25,6 @@
25 <script type="text/javascript" src="scripts/ol3-contextmenu.js"></script>
26 <title>MeshCentral</title>
27 </head>
28 -
28 <body onload="javascript:if (typeof(startup) !== 'undefined') startup();" oncontextmenu="handleContextMenu(event)">
29 <!-- right click menu -->
30 <div id="contextMenu" class="contextMenu" style="display: none">
@@ -1494,10 +1493,10 @@
1493 setDialogMode(2, "Add Mesh Agent", 1, null, x);
1494
1495 if (serverinfo.https == true) {
1497 - Q('agins_linux_area').value = "wget -q https://" + serverinfo.name + ":" + serverinfo.port + "/meshagents?script=1 --no-check-certificate -O ./meshinstall.sh && chmod 755 ./meshinstall.sh && sudo ./meshinstall.sh https://" + serverinfo.name + ":" + serverinfo.port + " " + meshid.split('/')[2] + "\r\n";
1496 + Q('agins_linux_area').value = "wget -q https://" + serverinfo.name + ":" + serverinfo.port + "/meshagents?script=1 --no-check-certificate -O ./meshinstall.sh && chmod 755 ./meshinstall.sh && sudo ./meshinstall.sh https://" + serverinfo.name + ":" + serverinfo.port + " " + meshid.split('/')[2].replace(/\$/g, '\\$') + "\r\n";
1497 Q('agins_linux_area_un').value = "wget -q https://" + serverinfo.name + ":" + serverinfo.port + "/meshagents?script=1 --no-check-certificate -O ./meshinstall.sh && chmod 755 ./meshinstall.sh && sudo ./meshinstall.sh uninstall\r\n";
1498 } else {
1500 - Q('agins_linux_area').value = "wget -q http://" + serverinfo.name + ":" + serverinfo.port + "/meshagents?script=1 -O ./meshinstall.sh && chmod 755 ./meshinstall.sh && sudo ./meshinstall.sh http://" + serverinfo.name + ":" + serverinfo.port + " " + meshid.split('/')[2] + "\r\n";
1499 + Q('agins_linux_area').value = "wget -q http://" + serverinfo.name + ":" + serverinfo.port + "/meshagents?script=1 -O ./meshinstall.sh && chmod 755 ./meshinstall.sh && sudo ./meshinstall.sh http://" + serverinfo.name + ":" + serverinfo.port + " " + meshid.split('/')[2].replace(/\$/g, '\\$') + "\r\n";
1500 Q('agins_linux_area_un').value = "wget -q http://" + serverinfo.name + ":" + serverinfo.port + "/meshagents?script=1 -O ./meshinstall.sh && chmod 755 ./meshinstall.sh && sudo ./meshinstall.sh uninstall\r\n";
1501 }
1502 }
@@ -2415,6 +2414,8 @@
2414 if (mesh.mtype == 2) x += '<a style=cursor:pointer onclick=p10showNodeNetInfoDialog("' + node._id + '") title="Show device network interface information">Interfaces</a>&nbsp;';
2415 if (xxmap != null) x += '<a style=cursor:pointer onclick=p10showNodeLocationDialog("' + node._id + '") title="Show device locations information">Location</a>&nbsp;';
2416
2417 + if (mesh.mtype == 2) x += '<a style=cursor:pointer onclick=p10showRouterDialog("' + node._id + '") title="Traffic router used to connect to a device thru this server.">Router</a>&nbsp;';
2418 +
2419 // RDP link, show this link only of the remote machine is Windows.
2420 if (((connectivity & 1) != 0) && (clickOnce == true) && (mesh.mtype == 2) && ((meshrights & 8) != 0)) {
2421 if ((node.agent.id > 0) && (node.agent.id < 5)) { x += '<a style=cursor:pointer onclick=p10clickOnce("' + node._id + '","RDP2",3389) title="Requires Microsoft ClickOnce support in your browser.">RDP</a>&nbsp;'; }
@@ -2689,6 +2690,39 @@
2690 meshserver.Send({ action: 'getnetworkinfo', nodeid: currentNode._id });
2691 }
2692
2693 + // Show router dialog
2694 + function p10showRouterDialog() {
2695 + if (xxdialogMode) return;
2696 + var y = "<select id=aginsSelect onclick=meshCmdOsClick() style=width:236px>";
2697 + y += "<option value=1>Windows (32bit)</option>";
2698 + y += "<option value=2>Windows (64bit)</option>";
2699 + y += "<option value=5>Linux x86 (32bit)</option>";
2700 + y += "<option value=6>Linux x86 (64bit)</option>";
2701 + y += "<option value=25>Linux ARM, Raspberry Pi (32bit)</option>";
2702 + y += "</select>";
2703 +
2704 + var x = "";
2705 + x += "<div>Download \"meshcmd\" with an action file to route traffic thru this server to this device. Make sure to edit meshaction.txt and add your account password or make any changes needed.<br /><br />";
2706 + x += addHtmlValue('Operating System', y);
2707 + x += addHtmlValue('Mesh Command', '<a id="meshcmddownloadid" href="meshagents?meshcmd=1" target="_blank"></a>');
2708 + x += addHtmlValue('Action File', '<a href="meshagents?meshaction=route&nodeid=' + currentNode._id + '" target="_blank">MeshAction (.txt)</a>');
2709 + x += "</div>";
2710 +
2711 + setDialogMode(2, "Network Router", 1, null, x, currentNode._id);
2712 + meshCmdOsClick();
2713 + }
2714 +
2715 + function meshCmdOsClick() {
2716 + var os = Q('aginsSelect').value, osn = '';
2717 + Q('meshcmddownloadid').href = "meshagents?meshcmd=" + os;
2718 + if (os == 1) { osn = 'MeshCmd (Win32 executable)'; }
2719 + if (os == 2) { osn = 'MeshCmd (Win64 executable)'; }
2720 + if (os == 5) { osn = 'MeshCmd (Linux x86, 32bit)'; }
2721 + if (os == 6) { osn = 'MeshCmd (Linux x86, 64bit)'; }
2722 + if (os == 25) { osn = 'MeshCmd (Linux ARM, 32bit)'; }
2723 + QH('meshcmddownloadid', osn);
2724 + }
2725 +
2726 function p10showiconselector() {
2727 if (xxdialogMode) return;
2728 var mesh = meshes[currentNode.meshid];
webserver.js
+45 -2
@@ -88,6 +88,7 @@ module.exports.CreateWebServer = function (parent, db, args, secret, certificate
88 // Perform hash on web certificate and agent certificate
89 obj.webCertificateHash = parent.certificateOperations.forge.pki.getPublicKeyFingerprint(parent.certificateOperations.forge.pki.certificateFromPem(obj.certificates.web.cert).publicKey, { md: parent.certificateOperations.forge.md.sha384.create(), encoding: 'binary' });
90 obj.webCertificateHashBase64 = new Buffer(parent.certificateOperations.forge.pki.getPublicKeyFingerprint(parent.certificateOperations.forge.pki.certificateFromPem(obj.certificates.web.cert).publicKey, { md: parent.certificateOperations.forge.md.sha384.create(), encoding: 'binary' }), 'binary').toString('base64').replace(/\+/g, '@').replace(/\//g, '$');
91 + obj.agentCertificateHashHex = parent.certificateOperations.forge.pki.getPublicKeyFingerprint(parent.certificateOperations.forge.pki.certificateFromPem(obj.certificates.agent.cert).publicKey, { md: parent.certificateOperations.forge.md.sha384.create(), encoding: 'hex' });
92 obj.agentCertificateHashBase64 = new Buffer(parent.certificateOperations.forge.pki.getPublicKeyFingerprint(parent.certificateOperations.forge.pki.certificateFromPem(obj.certificates.agent.cert).publicKey, { md: parent.certificateOperations.forge.md.sha384.create(), encoding: 'binary' }), 'binary').toString('base64').replace(/\+/g, '@').replace(/\//g, '$');
93 obj.agentCertificateAsn1 = parent.certificateOperations.forge.asn1.toDer(parent.certificateOperations.forge.pki.certificateToAsn1(parent.certificateOperations.forge.pki.certificateFromPem(parent.certificates.agent.cert))).getBytes();
94
@@ -1119,6 +1120,48 @@ module.exports.CreateWebServer = function (parent, db, args, secret, certificate
1120 if (scriptInfo == null) { res.sendStatus(404); return; }
1121 res.set({ 'Cache-Control': 'no-cache, no-store, must-revalidate', 'Pragma': 'no-cache', 'Expires': '0', 'Content-Type': 'text/plain', 'Content-Disposition': 'attachment; filename=' + scriptInfo.rname });
1122 res.sendFile(scriptInfo.path);
1123 + } else if (req.query.meshcmd != null) {
1124 + // Send meshcmd for a specific platform back
1125 + var argentInfo = obj.parent.meshAgentBinaries[req.query.meshcmd];
1126 + if (argentInfo == null) { res.sendStatus(404); return; }
1127 + // Load the agent
1128 + obj.fs.readFile(argentInfo.path, function (err, agentexe) {
1129 + if (err != null) { res.sendStatus(404); return; }
1130 + // Load meshcmd.js
1131 + obj.fs.readFile(obj.path.join(__dirname, 'agents', 'meshcmd.js'), function (err, meshcmdjs) {
1132 + if (err != null) { res.sendStatus(404); return; }
1133 + res.set({ 'Cache-Control': 'no-cache, no-store, must-revalidate', 'Pragma': 'no-cache', 'Expires': '0', 'Content-Type': 'application/octet-stream', 'Content-Disposition': 'attachment; filename=meshcmd' + ((req.query.meshcmd <= 4) ? '.exe' : '') });
1134 + var tail = new Buffer(8);
1135 + tail.writeInt32BE(meshcmdjs.length, 0);
1136 + tail.writeInt32BE(agentexe.length + meshcmdjs.length + 8, 4);
1137 + res.send(Buffer.concat([agentexe, meshcmdjs, tail]));
1138 + });
1139 + });
1140 + } else if (req.query.meshaction != null) {
1141 + var domain = checkUserIpAddress(req, res);
1142 + var user = obj.users[req.session.userid];
1143 + if (domain == null || req.query.nodeid == null) { res.sendStatus(404); return; }
1144 + obj.db.Get(req.query.nodeid, function (err, nodes) {
1145 + if (nodes.length != 1) { res.sendStatus(401); return; }
1146 + var node = nodes[0];
1147 + // Create the meshaction.txt file for meshcmd.exe
1148 + var meshaction = {
1149 + action: req.query.meshaction,
1150 + localPort: 1234,
1151 + remoteName: node.name,
1152 + remoteNodeId: node._id,
1153 + remotePort: 3389,
1154 + username: '',
1155 + password: '',
1156 + serverId: obj.agentCertificateHashHex.toUpperCase(), // SHA384 of server HTTPS public key
1157 + serverHttpsHash: new Buffer(obj.webCertificateHash, 'binary').toString('hex').toUpperCase(), // SHA384 of server HTTPS certificate
1158 + debugLevel: 0
1159 + }
1160 + if (user != null) { meshaction.username = user.name; }
1161 + if (obj.args.lanonly != true) { meshaction.serverUrl = ((obj.args.notls == true) ? 'ws://' : 'wss://') + obj.certificates.CommonName + ':' + obj.args.port + '/' + ((domain.id == '') ? '' : ('/' + domain.id)) + 'meshrelay.ashx'; }
1162 + res.set({ 'Cache-Control': 'no-cache, no-store, must-revalidate', 'Pragma': 'no-cache', 'Expires': '0', 'Content-Type': 'text/plain', 'Content-Disposition': 'attachment; filename=meshaction.txt' });
1163 + res.send(JSON.stringify(meshaction, null, ' '));
1164 + });
1165 } else {
1166 // Send a list of available mesh agents
1167 var response = '<html><head><title>Mesh Agents</title><style>table,th,td { border:1px solid black;border-collapse:collapse;padding:3px; }</style></head><body><table>';
@@ -1140,7 +1183,7 @@ module.exports.CreateWebServer = function (parent, db, args, secret, certificate
1183 if (domain == null) return;
1184 //if ((domain.id !== '') || (!req.session) || (req.session == null) || (!req.session.userid)) { res.sendStatus(401); return; }
1185
1143 - // Delete a mesh and all computers within it
1186 + // Query the meshid
1187 obj.db.Get('mesh/' + domain.id + '/' + req.query.id, function (err, meshes) {
1188 if (meshes.length != 1) { res.sendStatus(401); return; }
1189 var mesh = meshes[0];
@@ -1205,7 +1248,7 @@ module.exports.CreateWebServer = function (parent, db, args, secret, certificate
1248 obj.app.post(url + 'uploadmeshcorefile.ashx', handleUploadMeshCoreFile);
1249 obj.app.get(url + 'userfiles/*', handleDownloadUserFiles);
1250 obj.app.ws(url + 'echo.ashx', handleEchoWebSocket);
1208 - obj.app.ws(url + 'meshrelay.ashx', function (ws, req) { try { obj.meshRelayHandler.CreateMeshRelay(obj, ws, req); } catch (e) { console.log(e); } });
1251 + obj.app.ws(url + 'meshrelay.ashx', function (ws, req) { try { obj.meshRelayHandler.CreateMeshRelay(obj, ws, req, getDomain(req)); } catch (e) { console.log(e); } });
1252
1253 // Receive mesh agent connections
1254 obj.app.ws(url + 'agent.ashx', function (ws, req) { try { var domain = getDomain(req); obj.meshAgentHandler.CreateMeshAgent(obj, obj.db, ws, req, obj.args, domain); } catch (e) { console.log(e); } });