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> ';
2415
if (xxmap != null) x += '<a style=cursor:pointer onclick=p10showNodeLocationDialog("' + node._id + '") title="Show device locations information">Location</a> ';
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> ';
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> '; }
@@ -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); } });