Faster RSA signatures.

Ylian Saint-Hilaire committed Jan 9, 2018 at 20:13 UTC c53d51175a904dde26882d37c6af1a98acb4942a
16 files changed +417 -58
MeshCentralServer.njsproj
+2
@@ -190,6 +190,7 @@
190 <Folder Include="typings\globals\express-handlebars\" />
191 <Folder Include="typings\globals\express-session\" />
192 <Folder Include="typings\globals\node-forge\" />
193 + <Folder Include="typings\globals\nodemailer\" />
194 <Folder Include="typings\globals\node\" />
195 <Folder Include="views\" />
196 </ItemGroup>
@@ -198,6 +199,7 @@
199 <TypeScriptCompile Include="typings\globals\express-handlebars\index.d.ts" />
200 <TypeScriptCompile Include="typings\globals\express-session\index.d.ts" />
201 <TypeScriptCompile Include="typings\globals\node-forge\index.d.ts" />
202 + <TypeScriptCompile Include="typings\globals\nodemailer\index.d.ts" />
203 <TypeScriptCompile Include="typings\globals\node\index.d.ts" />
204 <TypeScriptCompile Include="typings\index.d.ts" />
205 </ItemGroup>
agents/meshcore.js
+56 -2
@@ -36,7 +36,24 @@ function createMeshCore(agent) {
36 var wifiScannerLib = null;
37 var wifiScanner = null;
38 var networkMonitor = null;
39 -
39 + var amtscanner = null;
40 +
41 + /*
42 + var AMTScanner = require("AMTScanner");
43 + var scan = new AMTScanner();
44 +
45 + scan.on("found", function (data) {
46 + if (typeof data === 'string') {
47 + console.log(data);
48 + } else {
49 + console.log(JSON.stringify(data, null, " "));
50 + }
51 + });
52 + scan.scan("10.2.55.140", 1000);
53 + scan.scan("10.2.55.139-10.2.55.145", 1000);
54 + scan.scan("10.2.55.128/25", 2000);
55 + */
56 +
57 // Try to load up the network monitor
58 try {
59 networkMonitor = require('NetworkMonitor');
@@ -44,6 +61,13 @@ function createMeshCore(agent) {
61 networkMonitor.on('add', function (addr) { sendNetworkUpdateNagle(); });
62 networkMonitor.on('remove', function (addr) { sendNetworkUpdateNagle(); });
63 } catch (e) { networkMonitor = null; }
64 +
65 + // Try to load up the Intel AMT scanner
66 + try {
67 + var AMTScannerModule = require('amt-scanner');
68 + amtscanner = new AMTScannerModule();
69 + //amtscanner.on('found', function (data) { if (typeof data != 'string') { data = JSON.stringify(data, null, " "); } sendConsoleText(data); });
70 + } catch (e) { amtscanner = null; }
71
72 // Try to load up the MEI module
73 try {
@@ -693,7 +717,7 @@ function createMeshCore(agent) {
717 var response = null;
718 switch (cmd) {
719 case 'help': { // Displays available commands
696 - response = 'Available commands: help, info, args, print, type, dbget, dbset, dbcompact, parseuri, httpget, wslist, wsconnect, wssend, wsclose, notify, ls, amt, netinfo, location, power, wakeonlan, scanwifi.';
720 + response = 'Available commands: help, info, args, print, type, dbget, dbset, dbcompact, parseuri, httpget, wslist,\r\nwsconnect, wssend, wsclose, notify, ls, amt, netinfo, location, power, wakeonlan, scanwifi, scanamt.';
721 break;
722 }
723 case 'notify': { // Send a notification message to the mesh
@@ -947,6 +971,36 @@ function createMeshCore(agent) {
971 } else { response = "Wifi module not present."; }
972 break;
973 }
974 + case 'scanamt': {
975 + if (amtscanner != null) {
976 + if (args['_'].length != 1) {
977 + response = 'Usage examples:\r\n scanamt 1.2.3.4\r\n scanamt 1.2.3.0-1.2.3.255\r\n scanamt 1.2.3.0/24\r\n'; // Display correct command usage
978 + } else {
979 + response = 'Scanning: ' + args['_'][0] + '...';
980 + amtscanner.scan(args['_'][0], 2000, function (data) {
981 + if (data.length > 0) {
982 + var r = '', pstates = ['NotActivated', 'InActivation', 'Activated'];
983 + for (var i in data) {
984 + var x = data[i];
985 + if (r != '') { r += '\r\n'; }
986 + r += x.address + ' - Intel AMT v' + x.majorVersion + '.' + x.minorVersion;
987 + if (x.provisioningState < 3) { r += (', ' + pstates[x.provisioningState]); }
988 + if (x.provisioningState == 2) { r += (', ' + x.openPorts.join(', ')); }
989 + r += '.';
990 + }
991 + } else {
992 + r = 'No Intel AMT found.';
993 + }
994 + sendConsoleText(r);
995 + });
996 + }
997 + } else { response = "Intel AMT scanner module not present."; }
998 + break;
999 + }
1000 + case 'modules': {
1001 + response = JSON.stringify(addedModules);
1002 + break;
1003 + }
1004 default: { // This is an unknown command, return an error message
1005 response = 'Unknown command \"' + cmd + '\", type \"help\" for list of avaialble commands.';
1006 break;
agents/modules_meshcmd/amt-scanner.js new
+89
@@ -0,0 +1,89 @@
1 +/**
2 +* @description Meshcentral Intel AMT Local Scanner
3 +* @author Ylian Saint-Hilaire & Joko Sastriawan
4 +* @version v0.0.1
5 +*/
6 +
7 +// Construct a Intel AMT Scanner object
8 +
9 +function AMTScanner() {
10 + var emitterUtils = require('events').inherits(this);
11 + emitterUtils.createEvent('found');
12 +
13 + this.dgram = require('dgram');
14 +
15 + this.buildRmcpPing = function (tag) {
16 + var packet = Buffer.from('06000006000011BE80000000', 'hex');
17 + packet[9] = tag;
18 + return packet;
19 + };
20 +
21 + this.parseRmcpPacket = function (server, data, rinfo, func) {
22 + if (data == null || data.length < 20) return;
23 + var res = {};
24 + if (((data[12] == 0) || (data[13] != 0) || (data[14] != 1) || (data[15] != 0x57)) && (data[21] & 32)) {
25 + res.servertag = data[9];
26 + res.minorVersion = data[18] & 0x0F;
27 + res.majorVersion = (data[18] >> 4) & 0x0F;
28 + res.provisioningState = data[19] & 0x03; // Pre = 0, In = 1, Post = 2
29 +
30 + var openPort = (data[16] * 256) + data[17];
31 + var dualPorts = ((data[19] & 0x04) != 0) ? true : false;
32 + res.openPorts = [openPort];
33 + res.address = rinfo.address;
34 + if (dualPorts == true) { res.openPorts = [16992, 16993]; }
35 + if (func !== undefined) {
36 + func(server, res);
37 + }
38 + }
39 + }
40 +
41 + this.parseIPv4Range = function (range) {
42 + if (range == undefined || range == null) return null;
43 + var x = range.split('-');
44 + if (x.length == 2) { return { min: this.parseIpv4Addr(x[0]), max: this.parseIpv4Addr(x[1]) }; }
45 + x = range.split('/');
46 + if (x.length == 2) {
47 + var ip = this.parseIpv4Addr(x[0]), masknum = parseInt(x[1]), mask = 0;
48 + if (masknum <= 16 || masknum > 32) return null;
49 + masknum = 32 - masknum;
50 + for (var i = 0; i < masknum; i++) { mask = (mask << 1); mask++; }
51 + return { min: ip & (0xFFFFFFFF - mask), max: (ip & (0xFFFFFFFF - mask)) + mask };
52 + }
53 + x = this.parseIpv4Addr(range);
54 + if (x == null) return null;
55 + return { min: x, max: x };
56 + };
57 +
58 + // Parse IP address. Takes a
59 + this.parseIpv4Addr = function (addr) {
60 + var x = addr.split('.');
61 + if (x.length == 4) { return (parseInt(x[0]) << 24) + (parseInt(x[1]) << 16) + (parseInt(x[2]) << 8) + (parseInt(x[3]) << 0); }
62 + return null;
63 + }
64 +
65 + // IP address number to string
66 + this.IPv4NumToStr = function (num) {
67 + return ((num >> 24) & 0xFF) + '.' + ((num >> 16) & 0xFF) + '.' + ((num >> 8) & 0xFF) + '.' + (num & 0xFF);
68 + }
69 +
70 + this.scan = function (rangestr, timeout) {
71 + var iprange = this.parseIPv4Range(rangestr);
72 + var rmcp = this.buildRmcpPing(0);
73 + var server = this.dgram.createSocket({ type: 'udp4' });
74 + server.parent = this;
75 + server.scanResults = [];
76 + server.on('error', function (err) { console.log('Error:' + err); });
77 + server.on('message', function (msg, rinfo) { if (rinfo.size > 4) { this.parent.parseRmcpPacket(this, msg, rinfo, function (s, res) { s.scanResults.push(res); }) }; });
78 + server.on('listening', function () { for (var i = iprange.min; i <= iprange.max; i++) { server.send(rmcp, 623, server.parent.IPv4NumToStr(i)); } });
79 + server.bind({ address: '0.0.0.0', port: 0, exclusive: true });
80 + var tmout = setTimeout(function cb() {
81 + //console.log("Server closed");
82 + //server.close();
83 + server.parent.emit('found', server.scanResults);
84 + delete server;
85 + }, timeout);
86 + };
87 +}
88 +
89 +module.exports = AMTScanner;
agents/modules_meshcore/amt-scanner.js new
+90
@@ -0,0 +1,90 @@
1 +/**
2 +* @description Meshcentral Intel AMT Local Scanner
3 +* @author Ylian Saint-Hilaire & Joko Sastriawan
4 +* @version v0.0.1
5 +*/
6 +
7 +// Construct a Intel AMT Scanner object
8 +
9 +function AMTScanner() {
10 + var emitterUtils = require('events').inherits(this);
11 + emitterUtils.createEvent('found');
12 +
13 + this.dgram = require('dgram');
14 +
15 + this.buildRmcpPing = function (tag) {
16 + var packet = Buffer.from('06000006000011BE80000000', 'hex');
17 + packet[9] = tag;
18 + return packet;
19 + };
20 +
21 + this.parseRmcpPacket = function (server, data, rinfo, func) {
22 + if (data == null || data.length < 20) return;
23 + var res = {};
24 + if (((data[12] == 0) || (data[13] != 0) || (data[14] != 1) || (data[15] != 0x57)) && (data[21] & 32)) {
25 + res.servertag = data[9];
26 + res.minorVersion = data[18] & 0x0F;
27 + res.majorVersion = (data[18] >> 4) & 0x0F;
28 + res.provisioningState = data[19] & 0x03; // Pre = 0, In = 1, Post = 2
29 +
30 + var openPort = (data[16] * 256) + data[17];
31 + var dualPorts = ((data[19] & 0x04) != 0) ? true : false;
32 + res.openPorts = [openPort];
33 + res.address = rinfo.address;
34 + if (dualPorts == true) { res.openPorts = [16992, 16993]; }
35 + if (func !== undefined) {
36 + func(server, res);
37 + }
38 + }
39 + }
40 +
41 + this.parseIPv4Range = function (range) {
42 + if (range == undefined || range == null) return null;
43 + var x = range.split('-');
44 + if (x.length == 2) { return { min: this.parseIpv4Addr(x[0]), max: this.parseIpv4Addr(x[1]) }; }
45 + x = range.split('/');
46 + if (x.length == 2) {
47 + var ip = this.parseIpv4Addr(x[0]), masknum = parseInt(x[1]), mask = 0;
48 + if (masknum <= 16 || masknum > 32) return null;
49 + masknum = 32 - masknum;
50 + for (var i = 0; i < masknum; i++) { mask = (mask << 1); mask++; }
51 + return { min: ip & (0xFFFFFFFF - mask), max: (ip & (0xFFFFFFFF - mask)) + mask };
52 + }
53 + x = this.parseIpv4Addr(range);
54 + if (x == null) return null;
55 + return { min: x, max: x };
56 + };
57 +
58 + // Parse IP address. Takes a
59 + this.parseIpv4Addr = function (addr) {
60 + var x = addr.split('.');
61 + if (x.length == 4) { return (parseInt(x[0]) << 24) + (parseInt(x[1]) << 16) + (parseInt(x[2]) << 8) + (parseInt(x[3]) << 0); }
62 + return null;
63 + }
64 +
65 + // IP address number to string
66 + this.IPv4NumToStr = function (num) {
67 + return ((num >> 24) & 0xFF) + '.' + ((num >> 16) & 0xFF) + '.' + ((num >> 8) & 0xFF) + '.' + (num & 0xFF);
68 + }
69 +
70 + this.scan = function (rangestr, timeout, func) {
71 + var iprange = this.parseIPv4Range(rangestr);
72 + var rmcp = this.buildRmcpPing(0);
73 + var server = this.dgram.createSocket({ type: 'udp4' });
74 + server.parent = this;
75 + server.scanResults = [];
76 + server.on('error', function (err) { console.log('Error:' + err); });
77 + server.on('message', function (msg, rinfo) { if (rinfo.size > 4) { this.parent.parseRmcpPacket(this, msg, rinfo, function (s, res) { s.scanResults.push(res); }) }; });
78 + server.on('listening', function () { for (var i = iprange.min; i <= iprange.max; i++) { server.send(rmcp, 623, server.parent.IPv4NumToStr(i)); } });
79 + server.bind({ address: '0.0.0.0', port: 0, exclusive: true });
80 + var tmout = setTimeout(function cb() {
81 + //console.log("Server closed");
82 + //server.close();
83 + server.parent.emit('found', server.scanResults);
84 + if (func != null) { func(server.scanResults); }
85 + delete server;
86 + }, timeout);
87 + };
88 +}
89 +
90 +module.exports = AMTScanner;
certoperations.js
+47
@@ -412,5 +412,52 @@ module.exports.CertificateOperations = function () {
412 return r;
413 }
414
415 + // Start accelerators
416 + const fork = require('child_process').fork;
417 + const program = require('path').resolve('meshaccelerator.js');
418 + const acceleratorCreateCount = require('os').cpus().length;
419 + var freeAccelerators = [];
420 +
421 + // Create a new accelerator module
422 + obj.getAccelerator = function() {
423 + if (freeAccelerators.length > 0) { return freeAccelerators.pop(); }
424 + if (acceleratorCreateCount > 0) {
425 + var accelerator = fork(program, [], { stdio: ['pipe', 'pipe', 'pipe', 'ipc'] });
426 + accelerator.on('message', function (message) { this.func(message); freeAccelerators.push(this); });
427 + if (obj.acceleratorCertStore != null) { accelerator.send({ action: 'setState', certs: obj.acceleratorCertStore }); }
428 + return accelerator;
429 + }
430 + return null;
431 + }
432 +
433 + // Set the state of the accelerators. This way, we don't have to send certificate & keys to them each time.
434 + obj.acceleratorCertStore = null;
435 + obj.acceleratorPerformSetState = function (certificates) {
436 + obj.acceleratorCertStore = [{ cert: certificates.agent.cert, key: certificates.agent.key }];
437 + if (certificates.swarmserver != null) { obj.acceleratorCertStore.push({ cert: certificates.swarmserver.cert, key: certificates.swarmserver.key }); }
438 + }
439 +
440 + // Perform any RSA signature, just pass in the private key and data.
441 + obj.acceleratorPerformSignature = function (privatekey, data, func) {
442 + var acc = obj.getAccelerator();
443 + if (acc == null) {
444 + // No accelerators available
445 + if (typeof privatekey == 'number') { privatekey = obj.acceleratorCertStore[privatekey].key; }
446 + const sign = crypto.createSign('SHA384');
447 + sign.end(new Buffer(data, 'binary'));
448 + func(sign.sign(privatekey).toString('binary'));
449 + } else {
450 + // Use the accelerator
451 + acc.func = func;
452 + acc.send({ action: 'sign', key: privatekey, data: data });
453 + }
454 + }
455 +
456 + // Perform a RSA signature. This is time consuming
457 + obj.acceleratorPerformVerify = function (publickey, data, msg, func) {
458 + console.log('Performing verification...');
459 + func(publickey.verify(data, msg));
460 + }
461 +
462 return obj;
463 };
meshaccelerator.js new
+28
@@ -0,0 +1,28 @@
1 +/**
2 +* @description MeshCentral accelerator
3 +* @author Ylian Saint-Hilaire
4 +* @copyright Intel Corporation 2018
5 +* @license Apache-2.0
6 +* @version v0.0.1
7 +*/
8 +
9 +const crypto = require('crypto');
10 +var certStore = null;
11 +
12 +process.on('message', function (message) {
13 + switch (message.action) {
14 + case 'sign': {
15 + if (typeof message.key == 'number') { message.key = certStore[message.key].key; }
16 + try {
17 + const sign = crypto.createSign('SHA384');
18 + sign.end(new Buffer(message.data, 'binary'));
19 + process.send(sign.sign(message.key).toString('binary'));
20 + } catch (e) { process.send(null); }
21 + break;
22 + }
23 + case 'setState': {
24 + certStore = message.certs;
25 + break;
26 + }
27 + }
28 +});
\ No newline at end of file
meshagent.js
+12 -15
@@ -162,23 +162,20 @@ module.exports.CreateMeshAgent = function (parent, db, ws, req, args, domain) {
162 if (getWebCertHash(obj.domain) != msg.substring(2, 50)) { console.log('Agent connected with bad web certificate hash, holding connection (' + obj.remoteaddr + ').'); return; }
163
164 // Use our server private key to sign the ServerHash + AgentNonce + ServerNonce
165 - var privateKey, certasn1;
165 + obj.agentnonce = msg.substring(50);
166 if (obj.useSwarmCert == true) {
167 - // Use older SwarmServer certificate of MC1
168 - certasn1 = obj.parent.swarmCertificateAsn1;
169 - privateKey = obj.forge.pki.privateKeyFromPem(obj.parent.certificates.swarmserver.key);
167 + // Perform the hash signature using older swarm server certificate
168 + obj.parent.parent.certificateOperations.acceleratorPerformSignature(1, msg.substring(2) + obj.nonce, function (signature) {
169 + // Send back our certificate + signature
170 + obj.send(obj.common.ShortToStr(2) + obj.common.ShortToStr(obj.parent.swarmCertificateAsn1.length) + obj.parent.swarmCertificateAsn1 + signature); // Command 2, certificate + signature
171 + });
172 } else {
171 - // Use new MC2 certificate
172 - certasn1 = obj.parent.agentCertificateAsn1;
173 - privateKey = obj.forge.pki.privateKeyFromPem(obj.parent.certificates.agent.key);
173 + // Perform the hash signature using new server agent certificate
174 + obj.parent.parent.certificateOperations.acceleratorPerformSignature(0, msg.substring(2) + obj.nonce, function (signature) {
175 + // Send back our certificate + signature
176 + obj.send(obj.common.ShortToStr(2) + obj.common.ShortToStr(obj.parent.agentCertificateAsn1.length) + obj.parent.agentCertificateAsn1 + signature); // Command 2, certificate + signature
177 + });
178 }
175 - var md = obj.forge.md.sha384.create();
176 - md.update(msg.substring(2), 'binary');
177 - md.update(obj.nonce, 'binary');
178 - obj.agentnonce = msg.substring(50);
179 -
180 - // Send back our certificate + signature
181 - obj.send(obj.common.ShortToStr(2) + obj.common.ShortToStr(certasn1.length) + certasn1 + privateKey.sign(md)); // Command 2, certificate + signature
179
180 // Check the agent signature if we can
181 if (obj.unauthsign != null) {
@@ -371,7 +368,7 @@ module.exports.CreateMeshAgent = function (parent, db, ws, req, args, domain) {
368 md.update(getWebCertHash(obj.domain), 'binary');
369 md.update(obj.nonce, 'binary');
370 md.update(obj.agentnonce, 'binary');
374 - if (obj.unauth.nodeCert.publicKey.verify(md.digest().bytes(), msg) == false) { return false; }
371 + if (obj.unauth.nodeCert.publicKey.verify(md.digest().bytes(), msg) == false) { return false; } // TODO: Check if this is slow or not. May n
372
373 // Connection is a success, clean up
374 obj.nodeid = obj.unauth.nodeid;
meshcentral.js
+7 -2
@@ -6,6 +6,9 @@
6 * @version v0.0.1
7 */
8
9 +// If app metrics is available
10 +if (process.argv[2] == '--launch') { try { require('appmetrics-dash').monitor({ url: '/', title: 'MeshCentral', port: 88, host: '127.0.0.1' }); } catch (e) { } }
11 +
12 function CreateMeshCentralServer() {
13 var obj = {};
14 obj.db;
@@ -30,7 +33,7 @@ function CreateMeshCentralServer() {
33 obj.debugLevel = 0;
34 obj.config = {}; // Configuration file
35 obj.dbconfig = {}; // Persistance values, loaded from database
33 - obj.certificateOperations = require('./certoperations.js').CertificateOperations();
36 + obj.certificateOperations = null;
37 obj.defaultMeshCmd = null;
38 obj.defaultMeshCore = null;
39 obj.defaultMeshCoreHash = null;
@@ -308,8 +311,10 @@ function CreateMeshCentralServer() {
311 obj.updateMeshCmd();
312
313 // Load server certificates
314 + obj.certificateOperations = require('./certoperations.js').CertificateOperations()
315 obj.certificateOperations.GetMeshServerCertificate(obj.datapath, obj.args, obj.config, function (certs) {
316 obj.certificates = certs;
317 + obj.certificateOperations.acceleratorPerformSetState(certs); // Set the state of the accelerators
318
319 // If the certificate is un-configured, force LAN-only mode
320 if (obj.certificates.CommonName == 'un-configured') { console.log('Server name not configured, running in LAN-only mode.'); obj.args.lanonly = true; }
@@ -719,7 +724,7 @@ function CreateMeshCentralServer() {
724 var moduleName = modulesDir[i].substring(0, modulesDir[i].length - 3);
725 var moduleDataB64 = obj.fs.readFileSync(obj.path.join(meshcorePath, 'modules_meshcore', modulesDir[i])).toString('base64');
726 moduleAdditions += 'try { addModule("' + moduleName + '", Buffer.from("' + moduleDataB64 + '", "base64")); addedModules.push("' + moduleName + '"); } catch (e) { }\r\n';
722 - if ((moduleName != 'amt_heci') && (moduleName != 'lme_heci')) {
727 + if ((moduleName != 'amt_heci') && (moduleName != 'lme_heci') && (moduleName != 'amt-0.2.0.js') && (moduleName != 'amt-script-0.2.0.js') && (moduleName != 'amt-wsman-0.2.0.js') && (moduleName != 'amt-wsman-duk-0.2.0.js')) {
728 moduleAdditionsNoMei += 'try { addModule("' + moduleName + '", Buffer.from("' + moduleDataB64 + '", "base64")); addedModules.push("' + moduleName + '"); } catch (e) { }\r\n';
729 }
730 }
multiserver.js
+3 -5
@@ -95,14 +95,13 @@ module.exports.CreateMultiServer = function (parent, args) {
95 obj.servernonce = msg.substring(50);
96
97 // Use our agent certificate root private key to sign the ServerHash + ServerNonce + PeerNonce
98 - var privateKey = obj.forge.pki.privateKeyFromPem(obj.certificates.agent.key);
98 var md = obj.forge.md.sha384.create();
99 md.update(msg.substring(2), 'binary');
100 md.update(obj.nonce, 'binary');
101
102 // Send back our certificate + signature
104 - agentRootCertificateAsn1 = obj.forge.asn1.toDer(obj.forge.pki.certificateToAsn1(obj.forge.pki.certificateFromPem(obj.certificates.agent.cert))).getBytes();
105 - obj.ws.send(obj.common.ShortToStr(2) + obj.common.ShortToStr(agentRootCertificateAsn1.length) + agentRootCertificatAsn1 + privateKey.sign(md)); // Command 3, signature
103 + agentRootCertificateAsn1 = obj.forge.asn1.toDer(obj.forge.pki.certificateToAsn1(obj.certificates.agent.fcert)).getBytes();
104 + obj.ws.send(obj.common.ShortToStr(2) + obj.common.ShortToStr(agentRootCertificateAsn1.length) + agentRootCertificatAsn1 + obj.certificates.agent.fkey.sign(md)); // Command 3, signature
105 break;
106 }
107 case 2: {
@@ -261,14 +260,13 @@ module.exports.CreateMultiServer = function (parent, args) {
260 if (obj.webCertificateHash != msg.substring(2, 50)) { obj.close(); return; }
261
262 // Use our server private key to sign the ServerHash + PeerNonce + ServerNonce
264 - var privateKey = obj.forge.pki.privateKeyFromPem(obj.parent.parent.certificates.agent.key);
263 var md = obj.forge.md.sha384.create();
264 md.update(msg.substring(2), 'binary');
265 md.update(obj.nonce, 'binary');
266 obj.peernonce = msg.substring(50);
267
268 // Send back our certificate + signature
271 - obj.send(obj.common.ShortToStr(2) + obj.common.ShortToStr(obj.agentCertificateAsn1.length) + obj.agentCertificateAsn1 + privateKey.sign(md)); // Command 2, certificate + signature
269 + obj.send(obj.common.ShortToStr(2) + obj.common.ShortToStr(obj.agentCertificateAsn1.length) + obj.agentCertificateAsn1 + obj.parent.parent.certificates.agent.fkey.sign(md)); // Command 2, certificate + signature
270
271 // Check the peer server signature if we can
272 if (obj.unauthsign != null) {
package.json
+1 -1
@@ -1,6 +1,6 @@
1 {
2 "name": "meshcentral",
3 - "version": "0.1.1-v",
3 + "version": "0.1.2-b",
4 "keywords": [
5 "Remote Management",
6 "Intel AMT",
public/scripts/agent-desktop-0.0.2.js
+4 -1
@@ -28,6 +28,7 @@ var CreateAgentRemoteDesktop = function (canvasid, scrolldiv) {
28 obj.connectioncount = 0;
29 obj.rotation = 0;
30 obj.protocol = 2; // KVM
31 + obj.debugmode = 0;
32
33 obj.sessionid = 0;
34 obj.username;
@@ -169,13 +170,15 @@ var CreateAgentRemoteDesktop = function (canvasid, scrolldiv) {
170 if (str.length < 4) return;
171 var cmdmsg = null, X = 0, Y = 0, command = ReadShort(str, 0), cmdsize = ReadShort(str, 2);
172 if (command >= 18) { console.error("Invalid KVM command " + command + " of size " + cmdsize); obj.parent.Stop(); return; }
172 - if (cmdsize > str.length) return;
173 + if (cmdsize > str.length) { console.error("KVM invalid command size", cmdsize, str.length); return; }
174 //meshOnDebug("KVM Command: " + command + " Len:" + cmdsize);
175 + if (obj.debugmode == 1) { console.log("KVM Command: " + command + " Len:" + cmdsize); }
176
177 if (command == 3 || command == 4 || command == 7) {
178 cmdmsg = str.substring(4, cmdsize);
179 X = ((cmdmsg.charCodeAt(0) & 0xFF) << 8) + (cmdmsg.charCodeAt(1) & 0xFF);
180 Y = ((cmdmsg.charCodeAt(2) & 0xFF) << 8) + (cmdmsg.charCodeAt(3) & 0xFF);
181 + //if (obj.debugmode == 1) { console.log("X=" + X + " Y=" + Y); }
182 }
183
184 switch (command) {
public/scripts/agent-redir-ws-0.1.0.js
+18 -5
@@ -20,6 +20,7 @@ var CreateAgentRedirect = function (meshserver, module, serverPublicNamePort) {
20 obj.webrtc = null;
21 obj.webchannel = null;
22 obj.onStateChanged = null;
23 + obj.debugmode = 0;
24
25 // Private method
26 //obj.debug = function (msg) { console.log(msg); }
@@ -41,6 +42,7 @@ var CreateAgentRedirect = function (meshserver, module, serverPublicNamePort) {
42 }
43
44 obj.xxOnSocketConnected = function () {
45 + if (obj.debugmode == 1) { console.log('onSocketConnected'); }
46 //obj.debug("Agent Redir Socket Connected");
47 obj.xxStateChange(2);
48 }
@@ -63,6 +65,7 @@ var CreateAgentRedirect = function (meshserver, module, serverPublicNamePort) {
65 }
66
67 obj.xxOnMessage = function (e) {
68 + if (obj.debugmode == 1) { console.log('Recv', e.data); }
69 if (obj.State < 3) {
70 if (e.data == 'c') {
71 obj.socket.send(obj.protocol);
@@ -156,10 +159,18 @@ var CreateAgentRedirect = function (meshserver, module, serverPublicNamePort) {
159 //obj.debug("Agent Redir Send(" + x.length + "): " + rstr2hex(x));
160 if (obj.socket != null && obj.socket.readyState == WebSocket.OPEN) {
161 if (typeof x == 'string') {
159 - var b = new Uint8Array(x.length);
160 - for (var i = 0; i < x.length; ++i) { b[i] = x.charCodeAt(i); }
161 - obj.socket.send(b.buffer);
162 + if (obj.debugmode == 1) {
163 + var b = new Uint8Array(x.length), c = [];
164 + for (var i = 0; i < x.length; ++i) { b[i] = x.charCodeAt(i); c.push(x.charCodeAt(i)); }
165 + obj.socket.send(b.buffer);
166 + console.log('Send', c);
167 + } else {
168 + var b = new Uint8Array(x.length);
169 + for (var i = 0; i < x.length; ++i) { b[i] = x.charCodeAt(i); }
170 + obj.socket.send(b.buffer);
171 + }
172 } else {
173 + if (obj.debugmode == 1) { console.log('Send', x); }
174 obj.socket.send(x);
175 }
176 }
@@ -167,7 +178,8 @@ var CreateAgentRedirect = function (meshserver, module, serverPublicNamePort) {
178
179 obj.xxOnSocketClosed = function () {
180 //obj.debug("Agent Redir Socket Closed");
170 - obj.Stop();
181 + if (obj.debugmode == 1) { console.log('onSocketClosed'); }
182 + obj.Stop(1);
183 }
184
185 obj.xxStateChange = function(newstate) {
@@ -177,7 +189,8 @@ var CreateAgentRedirect = function (meshserver, module, serverPublicNamePort) {
189 if (obj.onStateChanged != null) obj.onStateChanged(obj, obj.State);
190 }
191
180 - obj.Stop = function () {
192 + obj.Stop = function (x) {
193 + if (obj.debugmode == 1) { console.log('stop', x); }
194 //obj.debug("Agent Redir Socket Stopped");
195 obj.xxStateChange(0);
196 obj.connectstate = -1;
public/scripts/amt-redir-ws-0.1.0.js
+12 -6
@@ -22,6 +22,7 @@ var CreateAmtRedirect = function (module) {
22 // ###END###{!Mode-Firmware}
23 obj.connectstate = 0;
24 obj.protocol = module.protocol; // 1 = SOL, 2 = KVM, 3 = IDER
25 + obj.debugmode = 0;
26
27 obj.amtaccumulator = "";
28 obj.amtsequence = 1;
@@ -48,6 +49,7 @@ var CreateAmtRedirect = function (module) {
49
50 obj.xxOnSocketConnected = function () {
51 //obj.Debug("Redir Socket Connected");
52 + if (obj.debugmode == 1) { console.log('onSocketConnected'); }
53 obj.xxStateChange(2);
54 if (obj.protocol == 1) obj.xxSend(obj.RedirectStartSol); // TODO: Put these strings in higher level module to tighten code
55 if (obj.protocol == 2) obj.xxSend(obj.RedirectStartKvm); // Don't need these is the feature is not compiled-in.
@@ -55,6 +57,7 @@ var CreateAmtRedirect = function (module) {
57 }
58
59 obj.xxOnMessage = function (e) {
60 + if (obj.debugmode == 1) { console.log('Recv', e.data); }
61 obj.inDataCount++;
62 if (typeof e.data == 'object') {
63 var f = new FileReader();
@@ -113,7 +116,7 @@ var CreateAmtRedirect = function (module) {
116 cmdsize = (13 + oemlen);
117 break;
118 default:
116 - obj.Stop();
119 + obj.Stop(1);
120 break;
121 }
122 break;
@@ -141,7 +144,7 @@ var CreateAmtRedirect = function (module) {
144 // Basic Auth (Probably a good idea to not support this unless this is an old version of Intel AMT)
145 obj.xxSend(String.fromCharCode(0x13, 0x00, 0x00, 0x00, 0x01) + IntToStrX(obj.user.length + obj.pass.length + 2) + String.fromCharCode(obj.user.length) + obj.user + String.fromCharCode(obj.pass.length) + obj.pass);
146 }
144 - else obj.Stop();
147 + else obj.Stop(2);
148 }
149 else if ((authType == 3 || authType == 4) && status == 1) {
150 var curptr = 0;
@@ -197,7 +200,7 @@ var CreateAmtRedirect = function (module) {
200 obj.connectstate = 1;
201 obj.xxStateChange(3);
202 }
200 - } else obj.Stop();
203 + } else obj.Stop(3);
204 break;
205 case 0x21: // Response to settings (33)
206 if (obj.amtaccumulator.length < 23) break;
@@ -232,7 +235,7 @@ var CreateAmtRedirect = function (module) {
235 break;
236 default:
237 console.log("Unknown Intel AMT command: " + obj.amtaccumulator.charCodeAt(0) + " acclen=" + obj.amtaccumulator.length);
235 - obj.Stop();
238 + obj.Stop(4);
239 return;
240 }
241 if (cmdsize == 0) return;
@@ -243,6 +246,7 @@ var CreateAmtRedirect = function (module) {
246 obj.xxSend = function (x) {
247 //obj.Debug("Redir Send(" + x.length + "): " + rstr2hex(x));
248 if (obj.socket != null && obj.socket.readyState == WebSocket.OPEN) {
249 + if (obj.debugmode == 1) { console.log('Send', x); }
250 var b = new Uint8Array(x.length);
251 for (var i = 0; i < x.length; ++i) { b[i] = x.charCodeAt(i); }
252 obj.socket.send(b.buffer);
@@ -267,6 +271,7 @@ var CreateAmtRedirect = function (module) {
271 }
272
273 obj.xxOnSocketClosed = function () {
274 + if (obj.debugmode == 1) { console.log('onSocketClosed'); }
275 //obj.Debug("Redir Socket Closed");
276 if ((obj.inDataCount == 0) && (obj.tlsv1only == 0)) {
277 obj.tlsv1only = 1;
@@ -275,7 +280,7 @@ var CreateAmtRedirect = function (module) {
280 obj.socket.onmessage = obj.xxOnMessage;
281 obj.socket.onclose = obj.xxOnSocketClosed;
282 } else {
278 - obj.Stop();
283 + obj.Stop(5);
284 }
285 }
286
@@ -286,7 +291,8 @@ var CreateAmtRedirect = function (module) {
291 if (obj.onStateChanged != null) obj.onStateChanged(obj, obj.State);
292 }
293
289 - obj.Stop = function () {
294 + obj.Stop = function (x) {
295 + if (obj.debugmode == 1) { console.log('onSocketStop', x); }
296 //obj.Debug("Redir Socket Stopped");
297 obj.xxStateChange(0);
298 obj.connectstate = -1;
views/default.handlebars
+17 -8
@@ -482,7 +482,7 @@
482 </tr>
483 <tr>
484 <td style="background:black;text-align:center;height:500px;position:relative">
485 - <div id="p15agentConsole" style="background:black;margin:0;padding:0;color:lightgray;width:100%;max-width:930px;height:100%;text-align:left;overflow-y:scroll"></div>
485 + <div id=p15agentConsole style="background:black;margin:0;padding:0;color:lightgray;width:100%;max-width:930px;height:100%;text-align:left;overflow-y:scroll"><pre id=p15agentConsoleText></pre></div>
486 </td>
487 </tr>
488 <tr>
@@ -509,7 +509,9 @@
509 <div id=footer class=noselect>
510 <table cellpadding=0 cellspacing=10 style="width:100%">
511 <tr>
512 - <td style="text-align:left"></td>
512 + <td style="text-align:left;color:white">
513 + {{{footer}}}
514 + </td>
515 <td style="text-align:right">
516 <a id="verifyEmailId2" style="color:yellow;margin-left:3px;cursor:pointer;display:none" onclick="account_showVerifyEmail()">Verify Email</a>
517 <a style="margin-left:3px" href="terms">Terms &amp; Privacy</a>
@@ -2919,6 +2921,7 @@
2921 // Setup the Intel AMT remote desktop
2922 if ((desktopNode.intelamt.user == null) || (desktopNode.intelamt.user == '')) { editDeviceAmtSettings(desktopNode._id, connectDesktop); return; }
2923 desktop = CreateAmtRedirect(CreateAmtRemoteDesktop('Desk'));
2924 + desktop.debugmode = debugmode;
2925 desktop.onStateChanged = onDesktopStateChange;
2926 desktop.m.bpp = (desktopsettings.encoding == 1 || desktopsettings.encoding == 3) ? 1 : 2;
2927 desktop.m.useZRLE = (desktopsettings.encoding < 3);
@@ -2929,7 +2932,9 @@
2932 } else {
2933 // Setup the Mesh Agent remote desktop
2934 desktop = CreateAgentRedirect(meshserver, CreateAgentRemoteDesktop('Desk'), serverPublicNamePort);
2932 - desktop.attemptWebRTC = debugmode;
2935 + desktop.debugmode = debugmode;
2936 + desktop.m.debugmode = debugmode;
2937 + //desktop.attemptWebRTC = debugmode;
2938 desktop.onStateChanged = onDesktopStateChange;
2939 desktop.m.CompressionLevel = desktopsettings.quality; // Number from 1 to 100. 50 or less is best.
2940 desktop.m.ScalingLevel = desktopsettings.scaling;
@@ -3165,13 +3170,16 @@
3170 // Setup the Intel AMT terminal
3171 if ((terminalNode.intelamt.user == null) || (terminalNode.intelamt.user == '')) { editDeviceAmtSettings(terminalNode._id, connectTerminal); return; }
3172 terminal = CreateAmtRedirect(CreateAmtRemoteTerminal('Term'));
3173 + terminal.debugmode = debugmode;
3174 terminal.onStateChanged = onTerminalStateChange;
3175 terminal.Start(terminalNode._id, 16994, '*', '*', 0);
3176 terminal.contype = 2;
3177 } else {
3178 // Setup a mesh agent terminal
3179 terminal = CreateAgentRedirect(meshserver, CreateAmtRemoteTerminal('Term'), serverPublicNamePort);
3174 - terminal.attemptWebRTC = debugmode;
3180 + terminal.debugmode = debugmode;
3181 + terminal.m.debugmode = debugmode;
3182 + //terminal.attemptWebRTC = debugmode;
3183 terminal.onStateChanged = onTerminalStateChange;
3184 terminal.Start(terminalNode._id);
3185 terminal.contype = 1;
@@ -3649,6 +3657,7 @@
3657 if ((e.keyCode == 38) && ((consoleHistory.length - 1) > hindex)) { box.value = consoleHistory[hindex + 1]; }
3658 else if ((e.keyCode == 40) && (hindex > 0)) { box.value = consoleHistory[hindex - 1]; }
3659 else if ((e.keyCode == 40) && (hindex == 0)) { box.value = ''; }
3660 + processed = 1;
3661 }
3662 } else {
3663 if (e.charCode != 0 && consoleFocus == 0) { box.value = ((box.value + String.fromCharCode(e.charCode))); processed = 1; }
@@ -3667,7 +3676,7 @@
3676 if ((meshrights & 16) != 0) {
3677 if (consoleNode.consoleText == null) { consoleNode.consoleText = ''; }
3678 if (samenode == false) {
3670 - QH('p15agentConsole', consoleNode.consoleText);
3679 + QH('p15agentConsoleText', consoleNode.consoleText);
3680 Q('p15agentConsole').scrollTop = Q('p15agentConsole').scrollHeight;
3681 }
3682 var online = ((consoleNode.conn & 1) != 0)?true:false;
@@ -3683,7 +3692,7 @@
3692
3693 // Clear the console for this node
3694 function p15consoleClear() {
3686 - QH('p15agentConsole', '');
3695 + QH('p15agentConsoleText', '');
3696 Q('id_p15consoleClear').blur();
3697 consoleNode.consoleText = '';
3698 }
@@ -3693,7 +3702,7 @@
3702 function p15consoleSend(e) {
3703 if (e && e.keyCode != 13) return;
3704 var v = Q('p15consoleText').value, t = '<div style=color:green>&gt; ' + EscapeHtml(Q('p15consoleText').value) + '<br/></div>';
3696 - Q('p15agentConsole').innerHTML += t;
3705 + Q('p15agentConsoleText').innerHTML += t;
3706 consoleNode.consoleText += t;
3707 Q('p15agentConsole').scrollTop = Q('p15agentConsole').scrollHeight;
3708 Q('p15consoleText').value = '';
@@ -3716,7 +3725,7 @@
3725 data = '<div>' + EscapeHtmlBreaks(data) + '</div>'
3726 if (node.consoleText == null) { node.consoleText = data; } else { node.consoleText += data; }
3727 if (consoleNode == node) {
3719 - Q('p15agentConsole').innerHTML += data;
3728 + Q('p15agentConsoleText').innerHTML += data;
3729 Q('p15agentConsole').scrollTop = Q('p15agentConsole').scrollHeight;
3730 }
3731 }
views/login.handlebars
+3 -1
@@ -136,7 +136,9 @@
136 <div id=footer>
137 <table cellpadding=0 cellspacing=10 style=width:100%>
138 <tr>
139 - <td style=text-align:left></td>
139 + <td style=text-align:left;color:white>
140 + {{{footer}}}
141 + </td>
142 <td style=text-align:right>
143 {{{rootCertLink}}}
144 &nbsp;<a href=terms>Terms &amp; Privacy</a>
webserver.js
+28 -12
@@ -39,7 +39,7 @@ if (!String.prototype.endsWith) { String.prototype.endsWith = function (searchSt
39 module.exports.CreateWebServer = function (parent, db, args, secret, certificates) {
40 var obj = {};
41
42 - // Modules
42 + // Modules
43 obj.fs = require('fs');
44 obj.net = require('net');
45 obj.tls = require('tls');
@@ -270,6 +270,7 @@ module.exports.CreateWebServer = function (parent, db, args, secret, certificate
270
271 // Return the current domain of the request
272 function getDomain(req) {
273 + if (req.xdomain != null) { return req.xdomain; } // Domain already set for this request, return it.
274 if (req.headers.host != null) { var d = obj.dnsDomains[req.headers.host.toLowerCase()]; if (d != null) return d; } // If this is a DNS name domain, return it here.
275 var x = req.url.split('/');
276 if (x.length < 2) return parent.config.domains[''];
@@ -682,14 +683,14 @@ module.exports.CreateWebServer = function (parent, db, args, secret, certificate
683 if (obj.args.tlsoffload == true) { features += 16; } // No mutual-auth CIRA
684 if ((parent.config != null) && (parent.config.settings != null) && (parent.config.settings.allowframing == true)) { features += 32; } // Allow site within iframe
685 if ((!obj.args.user) && (obj.args.nousers != true) && (nologout == false)) { logoutcontrol += ' <a href=' + domain.url + 'logout?' + Math.random() + ' style=color:white>Logout</a>'; } // If a default user is in use or no user mode, don't display the logout button
685 - res.render(obj.path.join(__dirname, 'views/default'), { viewmode: viewmode, currentNode: currentNode, logoutControl: logoutcontrol, title: domain.title, title2: domain.title2, domainurl: domain.url, domain: domain.id, debuglevel: parent.debugLevel, serverDnsName: getWebServerName(domain), serverRedirPort: args.redirport, serverPublicPort: args.port, noServerBackup: (args.noserverbackup == 1 ? 1 : 0), features: features, mpspass: args.mpspass, webcerthash: obj.webCertificateHashBase64 });
686 + res.render(obj.path.join(__dirname, 'views/default'), { viewmode: viewmode, currentNode: currentNode, logoutControl: logoutcontrol, title: domain.title, title2: domain.title2, domainurl: domain.url, domain: domain.id, debuglevel: parent.debugLevel, serverDnsName: getWebServerName(domain), serverRedirPort: args.redirport, serverPublicPort: args.port, noServerBackup: (args.noserverbackup == 1 ? 1 : 0), features: features, mpspass: args.mpspass, webcerthash: obj.webCertificateHashBase64, footer: (domain.footer == null) ? '' : domain.footer });
687 } else {
688 // Send back the login application
689 var loginmode = req.session.loginmode;
690 delete req.session.loginmode; // Clear this state, if the user hits refresh, we want to go back to the login page.
691 var features = 0;
692 if ((parent.config != null) && (parent.config.settings != null) && (parent.config.settings.allowframing == true)) { features += 32; } // Allow site within iframe
692 - res.render(obj.path.join(__dirname, 'views/login'), { loginmode: loginmode, rootCertLink: getRootCertLink(), title: domain.title, title2: domain.title2, newAccount: domain.newaccounts, newAccountPass: (((domain.newaccountspass == null) || (domain.newaccountspass == '')) ? 0 : 1), serverDnsName: getWebServerName(domain), serverPublicPort: obj.args.port, emailcheck: obj.parent.mailserver != null, features: features });
693 + res.render(obj.path.join(__dirname, 'views/login'), { loginmode: loginmode, rootCertLink: getRootCertLink(), title: domain.title, title2: domain.title2, newAccount: domain.newaccounts, newAccountPass: (((domain.newaccountspass == null) || (domain.newaccountspass == '')) ? 0 : 1), serverDnsName: getWebServerName(domain), serverPublicPort: obj.args.port, emailcheck: obj.parent.mailserver != null, features: features, footer: (domain.footer == null) ? '' : domain.footer });
694 }
695 }
696
@@ -1518,18 +1519,33 @@ module.exports.CreateWebServer = function (parent, db, args, secret, certificate
1519
1520 // Add HTTP security headers to all responses
1521 obj.app.use(function (req, res, next) {
1521 - // Two more headers to take a look at:
1522 - // 'Public-Key-Pins': 'pin-sha256="X3pGTSOuJeEVw989IJ/cEtXUEmy52zs1TZQrU06KUKg="; max-age=10'
1523 - // 'strict-transport-security': 'max-age=31536000; includeSubDomains'
1522 res.removeHeader("X-Powered-By");
1525 - if (obj.args.notls) {
1526 - // Default headers if no TLS is used
1527 - res.set({ 'Referrer-Policy': 'no-referrer', 'x-frame-options': 'SAMEORIGIN', 'X-XSS-Protection': '1; mode=block', 'X-Content-Type-Options': 'nosniff', 'Content-Security-Policy': "default-src http: ws: data: 'self';script-src http: 'unsafe-inline';style-src http: 'unsafe-inline'" });
1523 + var domain = req.xdomain = getDomain(req);
1524 +
1525 + // Detect if this is a file sharing domain, if so, just share files.
1526 + if ((domain != null) && (domain.share != null)) {
1527 + var rpath;
1528 + if (domain.dns == null) { rpath = req.url.split('/'); rpath.splice(1, 1); rpath = rpath.join('/'); } else { rpath = req.url; }
1529 + if ((res.headers != null) && (res.headers.upgrade)) {
1530 + // If this is a websocket, stop here.
1531 + res.sendStatus(404);
1532 + } else {
1533 + // Check if the file exists, if so, serve it.
1534 + obj.fs.exists(obj.path.join(domain.share, rpath), function (exists) { if (exists == true) { res.sendfile(rpath, { root: domain.share }); } else { res.sendStatus(404); } });
1535 + }
1536 } else {
1529 - // Default headers if TLS is used
1530 - res.set({ 'Referrer-Policy': 'no-referrer', 'x-frame-options': 'SAMEORIGIN', 'X-XSS-Protection': '1; mode=block', 'X-Content-Type-Options': 'nosniff', 'Content-Security-Policy': "default-src https: wss: data: 'self';script-src https: 'unsafe-inline';style-src https: 'unsafe-inline'" });
1537 + // Two more headers to take a look at:
1538 + // 'Public-Key-Pins': 'pin-sha256="X3pGTSOuJeEVw989IJ/cEtXUEmy52zs1TZQrU06KUKg="; max-age=10'
1539 + // 'strict-transport-security': 'max-age=31536000; includeSubDomains'
1540 + if (obj.args.notls) {
1541 + // Default headers if no TLS is used
1542 + res.set({ 'Referrer-Policy': 'no-referrer', 'x-frame-options': 'SAMEORIGIN', 'X-XSS-Protection': '1; mode=block', 'X-Content-Type-Options': 'nosniff', 'Content-Security-Policy': "default-src http: ws: data: 'self';script-src http: 'unsafe-inline';style-src http: 'unsafe-inline'" });
1543 + } else {
1544 + // Default headers if TLS is used
1545 + res.set({ 'Referrer-Policy': 'no-referrer', 'x-frame-options': 'SAMEORIGIN', 'X-XSS-Protection': '1; mode=block', 'X-Content-Type-Options': 'nosniff', 'Content-Security-Policy': "default-src https: wss: data: 'self';script-src https: 'unsafe-inline';style-src https: 'unsafe-inline'" });
1546 + }
1547 + return next();
1548 }
1532 - return next();
1549 });
1550
1551 // Setup all HTTP handlers