Completed legacy agent upload system.
Ylian Saint-Hilaire committed
Nov 6, 2017 at 17:54 UTC
2d8d6cff516d1205d48ce852db6bccfc9359df42
2 files changed
+143
-7
package.json
+1
-1
@@ -1,6 +1,6 @@
1
{
2
"name": "meshcentral",
3
- "version": "0.1.0-g",
3
+ "version": "0.1.0-h",
4
"keywords": [
5
"Remote Management",
6
"Intel AMT",
swarmserver.js
+142
-6
@@ -11,10 +11,12 @@ module.exports.CreateSwarmServer = function (parent, db, args, certificates) {
11
obj.db = db;
12
obj.args = args;
13
obj.certificates = certificates;
14
- //obj.legacyAgentConnections = {};
14
+ obj.legacyAgentConnections = {};
15
+ obj.migrationAgents = {};
16
var common = require('./common.js');
17
var net = require('net');
18
var tls = require('tls');
19
+ var forge = require('node-forge');
20
21
var LegacyMeshProtocol = {
22
NODEPUSH: 1, // Used to send a node block to another peer.
@@ -113,10 +115,32 @@ module.exports.CreateSwarmServer = function (parent, db, args, certificates) {
115
116
obj.server = tls.createServer({ key: certificates.swarmserver.key, cert: certificates.swarmserver.cert, requestCert: true }, onConnection);
117
obj.server.listen(args.swarmport, function () { console.log('MeshCentral Legacy Swarm Server running on ' + certificates.CommonName + ':' + args.swarmport + '.'); }).on('error', function (err) { console.error('ERROR: MeshCentral Swarm Server server port ' + args.swarmport + ' is not available.'); if (args.exactports) { process.exit(); } });
116
-
118
+ loadMigrationAgents();
119
+
120
+ // Load all migration agents along with full executable in memory
121
+ function loadMigrationAgents() {
122
+ var migrationAgentsDir = null, migrationAgentsPath = obj.parent.path.join(obj.parent.datapath, 'migrationagents');
123
+ try { migrationAgentsDir = obj.parent.fs.readdirSync(migrationAgentsPath); } catch (e) { }
124
+ if (migrationAgentsDir != null) {
125
+ for (var i in migrationAgentsDir) {
126
+ if (migrationAgentsDir[i].toLowerCase().startsWith('meshagent-')) {
127
+ var migrationAgentName = obj.parent.path.join(migrationAgentsPath, migrationAgentsDir[i]);
128
+ var agentInfo = migrationAgentsDir[i].substring(10).split('.');
129
+ var agentVersion = parseInt(agentInfo[0]);
130
+ var agentArch = parseInt(agentInfo[1]);
131
+ var agentBinary = obj.parent.fs.readFileSync(migrationAgentName);
132
+ if (obj.migrationAgents[agentArch] == null) { obj.migrationAgents[agentArch] = {}; }
133
+ if (obj.migrationAgents[agentArch][agentVersion] == null) { obj.migrationAgents[agentArch][agentVersion] = { arch: agentArch, ver: agentVersion, path: migrationAgentName, binary: agentBinary }; }
134
+ }
135
+ }
136
+ }
137
+ }
138
+
139
+ // Called when a legacy agent connects to this server
140
function onConnection(socket) {
141
socket.tag = { first: true, clientCert: socket.getPeerCertificate(true), accumulator: "", socket: socket };
142
socket.setEncoding('binary');
143
+ socket.pingTimer = setInterval(function () { obj.SendCommand(socket, LegacyMeshProtocol.PING); }, 20000);
144
Debug(1, 'SWARM:New legacy agent connection');
145
146
socket.addListener("data", function (data) {
@@ -149,12 +173,65 @@ module.exports.CreateSwarmServer = function (parent, db, args, certificates) {
173
var cmd = common.ReadShort(socket.tag.accumulator, 0);
174
var len = common.ReadShort(socket.tag.accumulator, 2);
175
if (len > socket.tag.accumulator.length) return 0;
152
-
153
- console.log('Swarm: Cmd=' + cmd + ', Len=' + len + '.');
176
+ var data = socket.tag.accumulator.substring(4, len);
177
+ //console.log('Swarm: Cmd=' + cmd + ', Len=' + len + '.');
178
179
switch (cmd) {
180
case LegacyMeshProtocol.NODEPUSH: {
181
Debug(3, 'Swarm:NODEPUSH');
182
+ var nodeblock = obj.decodeNodeBlock(data);
183
+ if ((nodeblock != null) && (nodeblock.agenttype != null) && (nodeblock.agentversion != null)) {
184
+ Debug(3, 'Swarm:NODEPUSH:' + JSON.stringify(nodeblock));
185
+
186
+ // Figure out what is the next agent version we need.
187
+ var nextAgentVersion = 200; // TODO
188
+
189
+ // See if we need to start the agent update
190
+ if ((obj.migrationAgents[nodeblock.agenttype] != null) && (obj.migrationAgents[nodeblock.agenttype][nextAgentVersion] != null)) {
191
+ // Start the update
192
+ socket.tag.update = obj.migrationAgents[nodeblock.agenttype][nextAgentVersion];
193
+ socket.tag.updatePtr = 0;
194
+ console.log('Performing legacy agent update from ' + nodeblock.agentversion + '.' + nodeblock.agenttype + ' to ' + socket.tag.update.ver + '.' + socket.tag.update.arch + ' on ' + nodeblock.agentname + '.');
195
+ obj.SendCommand(socket, LegacyMeshProtocol.GETSTATE, common.IntToStr(5) + common.IntToStr(0)); // agent.SendQuery(5, 0); // Start the agent download
196
+ }
197
+ }
198
+ break;
199
+ }
200
+ case LegacyMeshProtocol.AMTPROVISIONING: {
201
+ Debug(3, 'Swarm:AMTPROVISIONING');
202
+ obj.SendCommand(socket, LegacyMeshProtocol.AMTPROVISIONING, common.ShortToStr(1));
203
+ break;
204
+ }
205
+ case LegacyMeshProtocol.GETSTATE: {
206
+ Debug(3, 'Swarm:GETSTATE');
207
+ if (len < 12) break;
208
+ var statecmd = common.ReadInt(data, 0);
209
+ var statesync = common.ReadInt(data, 4);
210
+ switch (statecmd) {
211
+ case 6: { // Ask for agent block
212
+ if (socket.tag.update != null) {
213
+ // Send an agent block
214
+ var l = Math.min(socket.tag.update.binary.length - socket.tag.updatePtr, 16384);
215
+ obj.SendCommand(socket, LegacyMeshProtocol.GETSTATE, common.IntToStr(6) + common.IntToStr(socket.tag.updatePtr) + socket.tag.update.binary.toString('binary', socket.tag.updatePtr, socket.tag.updatePtr + l)); // agent.SendQuery(6, AgentFileLen + AgentBlock);
216
+ Debug(3, 'Swarm:Sending agent block, ptr = ' + socket.tag.updatePtr + ', len = ' + l);
217
+
218
+ socket.tag.updatePtr += l;
219
+ if (socket.tag.updatePtr >= socket.tag.update.binary.length) {
220
+ // Send end-of-transfer
221
+ obj.SendCommand(socket, LegacyMeshProtocol.GETSTATE, common.IntToStr(7) + common.IntToStr(socket.tag.update.binary.length)); //agent.SendQuery(7, AgentFileLen);
222
+ Debug(3, 'Swarm:Sending end of agent, ptr = ' + socket.tag.updatePtr);
223
+ delete socket.tag.update;
224
+ delete socket.tag.updatePtr;
225
+ }
226
+ }
227
+ break;
228
+ }
229
+ }
230
+ break;
231
+ }
232
+ case LegacyMeshProtocol.APPSUBSCRIBERS: {
233
+ Debug(3, 'Swarm:APPSUBSCRIBERS');
234
+ break;
235
}
236
default: {
237
Debug(1, 'Swarm:Unknown command: ' + cmd + ' of len ' + len + '.');
@@ -167,13 +244,67 @@ module.exports.CreateSwarmServer = function (parent, db, args, certificates) {
244
Debug(1, 'Swarm:Connection closed');
245
try { delete obj.ciraConnections[socket.tag.nodeid]; } catch (e) { }
246
obj.parent.ClearConnectivityState(socket.tag.meshid, socket.tag.nodeid, 2);
247
+ if (socket.pingTimer != null) { clearInterval(socket.pingTimer); delete socket.pingTimer; }
248
});
249
250
socket.addListener("error", function () {
251
//console.log("Swarm Error: " + socket.remoteAddress);
252
});
253
}
176
-
254
+
255
+ function getTagClass(data, tagClass, type) {
256
+ if ((data == null) || (data.value == null)) return;
257
+ for (var i in data.value) {
258
+ //console.log(JSON.stringify(data.value[i]));
259
+ if ((data.value[i].tagClass == tagClass) && (data.value[i].type == type)) {
260
+ return data.value[i];
261
+ }
262
+ }
263
+ }
264
+
265
+ // Decode a node push block
266
+ obj.decodeNodeBlock = function (data) {
267
+ try {
268
+ // Traverse the DER to get the raw data (Not sure if this works all the time)
269
+ var info = {}, ptr = 68, der = forge.asn1.fromDer(forge.util.createBuffer(data, 'binary'));
270
+ der = getTagClass(der, 128, 0);
271
+ der = getTagClass(der, 0, 16);
272
+ der = getTagClass(der, 0, 16);
273
+ der = getTagClass(der, 128, 0);
274
+ der = getTagClass(der, 0, 4);
275
+ var binarydata = der.value;
276
+
277
+ // Get the basic header values
278
+ info.certhashhex = common.rstr2hex(binarydata.substring(0, 32)); // Hash of the complete mesh agent certificate
279
+ info.nodeidhex = common.rstr2hex(binarydata.substring(32, 64)); // Old mesh agent nodeid
280
+ info.serialNumber = common.ReadIntX(binarydata, 64); // Block serial number
281
+
282
+ // Got thru the sub-blocks
283
+ while (ptr < binarydata.length) {
284
+ var btyp = common.ReadShort(binarydata, ptr), blen = common.ReadShort(binarydata, ptr + 2), bdata = binarydata.substring(ptr + 4, ptr + 4 + blen);
285
+ switch (btyp) {
286
+ case 1: { // PBST_COMPUTERINFO
287
+ info.agenttype = common.ReadShortX(bdata, 0);
288
+ info.agentbuild = common.ReadShortX(bdata, 2);
289
+ info.agentversion = common.ReadIntX(bdata, 4);
290
+ info.agentname = bdata.substring(8, 64 + 8);
291
+ var xx = info.agentname.indexOf('\u0000');
292
+ if (xx >= 0) { info.agentname = info.agentname.substring(0, xx); }
293
+ info.agentosdesc = bdata.substring(64 + 8, 64 + 64 + 8);
294
+ xx = info.agentosdesc.indexOf('\u0000');
295
+ if (xx >= 0) { info.agentosdesc = info.agentosdesc.substring(0, xx); }
296
+ return info;
297
+ }
298
+ }
299
+ ptr += blen;
300
+ }
301
+ return info;
302
+ } catch (e) {
303
+ console.log(e);
304
+ }
305
+ return null;
306
+ }
307
+
308
// Disconnect legacy agent connection
309
obj.close = function (socket) {
310
try { socket.close(); } catch (e) { }
@@ -181,11 +312,16 @@ module.exports.CreateSwarmServer = function (parent, db, args, certificates) {
312
obj.parent.ClearConnectivityState(socket.tag.meshid, socket.tag.nodeid, 2);
313
}
314
315
+ obj.SendCommand = function(socket, cmdid, data) {
316
+ if (data == null) { data = ''; }
317
+ Write(socket, common.ShortToStr(cmdid) + common.ShortToStr(data.length + 4) + data);
318
+ }
319
+
320
function Write(socket, data) {
321
if (args.swarmdebug) {
322
// Print out sent bytes
323
var buf = new Buffer(data, "binary");
188
- console.log('Swarm --> (' + buf.length + '):' + buf.toString('hex'));
324
+ console.log('SWARM --> (' + buf.length + '):' + buf.toString('hex'));
325
socket.write(buf);
326
} else {
327
socket.write(new Buffer(data, "binary"));