diff --git a/MeshCentralServer.njsproj b/MeshCentralServer.njsproj index 50b7b4e6..f3f74002 100644 --- a/MeshCentralServer.njsproj +++ b/MeshCentralServer.njsproj @@ -28,6 +28,8 @@ + + diff --git a/agents/MeshService.exe b/agents/MeshService.exe index 955c1a83..dc5b99ae 100644 Binary files a/agents/MeshService.exe and b/agents/MeshService.exe differ diff --git a/agents/MeshService64.exe b/agents/MeshService64.exe index 904ce57e..24c4032f 100644 Binary files a/agents/MeshService64.exe and b/agents/MeshService64.exe differ diff --git a/agents/meshagent_pi b/agents/meshagent_pi index 09f24c14..76df96d4 100644 Binary files a/agents/meshagent_pi and b/agents/meshagent_pi differ diff --git a/agents/meshcore.js b/agents/meshcore.js index 92113ee2..20f5b67c 100644 --- a/agents/meshcore.js +++ b/agents/meshcore.js @@ -472,7 +472,6 @@ function createMeshCore(agent) { function onTunnelClosed() { sendConsoleText("Tunnel #" + this.httprequest.index + " closed.", this.httprequest.sessionid); - if (this.httprequest.protocol == 1) { this.httprequest.process.end(); delete this.httprequest.process; } delete tunnels[this.httprequest.index]; /* @@ -561,7 +560,8 @@ function createMeshCore(agent) { this.httprequest.process.on('exit', function (ecode, sig) { this.tunnel.end(); }); this.httprequest.process.stderr.on('data', function (chunk) { this.parent.tunnel.write(chunk); }); this.httprequest.process.stdout.pipe(this, { dataTypeSkip: 1 }); // 0 = Binary, 1 = Text. - this.pipe(this.httprequest.process.stdin, { dataTypeSkip: 1 }); // 0 = Binary, 1 = Text. + this.pipe(this.httprequest.process.stdin, { dataTypeSkip: 1, end: false }); // 0 = Binary, 1 = Text. + this.prependListener('end', function () { this.httprequest.process.kill(); }); } } if (this.httprequest.protocol == 2) { @@ -717,7 +717,15 @@ function createMeshCore(agent) { var response = null; switch (cmd) { case 'help': { // Displays available commands - 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.'; + response = 'Available commands: help, info, args, print, type, dbget, dbset, dbcompact, eval, parseuri, httpget,\r\nwslist, wsconnect, wssend, wsclose, notify, ls, amt, netinfo, location, power, wakeonlan, scanwifi, scanamt.'; + break; + } + case 'eval': { // Eval JavaScript + if (args['_'].length < 1) { + response = 'Proper usage: eval "JavaScript code"'; // Display correct command usage + } else { + response = JSON.stringify(mesh.eval(args['_'][0])); + } break; } case 'notify': { // Send a notification message to the mesh @@ -735,6 +743,7 @@ function createMeshCore(agent) { response = 'Current Core: ' + obj.meshCoreInfo + '.\r\nAgent Time: ' + Date() + '.\r\nUser Rights: 0x' + rights.toString(16) + '.\r\nPlatform Info: ' + process.platform + '.\r\nCapabilities: ' + obj.meshCoreCapabilities + '.\r\nNative Pipes: ' + obj.useNativePipes + '.\r\nServer URL: ' + mesh.ServerUrl + '.'; if (amtLmsState >= 0) { response += '\r\nBuilt -in LMS: ' + ['Disabled', 'Connecting..', 'Connected'][amtLmsState] + '.'; } response += '\r\nModules: ' + JSON.stringify(addedModules) + ''; + response += '\r\nServerConnected: ' + mesh.isControlChannelConnected + ''; var oldNodeId = db.Get('OldNodeId'); if (oldNodeId != null) { response += '\r\nOldNodeID: ' + oldNodeId + '.'; } response += '\r\ServerState: ' + meshServerConnectionState + '.'; @@ -1136,16 +1145,11 @@ function createMeshCore(agent) { // Setup the mesh agent event handlers mesh.AddCommandHandler(handleServerCommand); mesh.AddConnectHandler(handleServerConnection); - //mesh.lmsNotification = handleAmtNotification; // TODO - sendPeriodicServerUpdate(true); // TODO: Check if connected before sending - + // Parse input arguments //var args = parseArgs(process.argv); //console.log(args); - //console.log('Stopping.'); - //process.exit(); - // Launch LMS try { var lme_heci = require('lme_heci'); @@ -1154,6 +1158,14 @@ function createMeshCore(agent) { amtLms.on('error', function (e) { amtLmsState = 0; amtLms = null; }); amtLms.on('connect', function () { amtLmsState = 2; }); } catch (e) { amtLmsState = -1; amtLms = null; } + + // Check if the control channel is connected + if (mesh.isControlChannelConnected) { + sendPeriodicServerUpdate(true); // Send the server update + } + + //console.log('Stopping.'); + //process.exit(); } obj.stop = function () { diff --git a/agents/modules_meshcmd/amt-scanner.js b/agents/modules_meshcmd/amt-scanner.js index 91d7dae4..d6f99fa1 100644 --- a/agents/modules_meshcmd/amt-scanner.js +++ b/agents/modules_meshcmd/amt-scanner.js @@ -79,7 +79,7 @@ function AMTScanner() { server.bind({ address: '0.0.0.0', port: 0, exclusive: true }); var tmout = setTimeout(function cb() { //console.log("Server closed"); - //server.close(); + server.close(); server.parent.emit('found', server.scanResults); delete server; }, timeout); diff --git a/agents/modules_meshcmd/lme_heci.js b/agents/modules_meshcmd/lme_heci.js index 2ecb0ec5..8b619e72 100644 --- a/agents/modules_meshcmd/lme_heci.js +++ b/agents/modules_meshcmd/lme_heci.js @@ -21,8 +21,7 @@ var APF_CHANNEL_CLOSE = 97; var APF_PROTOCOLVERSION = 192; -function lme_object() -{ +function lme_object() { this.ourId = ++lme_id; this.amtId = -1; this.LME_CHANNEL_STATUS = 'LME_CS_FREE'; @@ -32,12 +31,11 @@ function lme_object() this.errorCount = 0; } -function stream_bufferedWrite() -{ +function stream_bufferedWrite() { var emitterUtils = require('events').inherits(this); this.buffer = []; this._readCheckImmediate = undefined; - + // Writable Events emitterUtils.createEvent('close'); emitterUtils.createEvent('drain'); @@ -45,21 +43,17 @@ function stream_bufferedWrite() emitterUtils.createEvent('finish'); emitterUtils.createEvent('pipe'); emitterUtils.createEvent('unpipe'); - + // Readable Events emitterUtils.createEvent('readable'); - this.isEmpty = function () - { + this.isEmpty = function () { return (this.buffer.length == 0); }; - this.isWaiting = function () - { + this.isWaiting = function () { return (this._readCheckImmediate == undefined); }; - this.write = function (chunk) - { - for (var args in arguments) - { + this.write = function (chunk) { + for (var args in arguments) { if (typeof (arguments[args]) == 'function') { this.once('drain', arguments[args]); break; } } var tmp = Buffer.alloc(chunk.length); @@ -68,41 +62,34 @@ function stream_bufferedWrite() this.emit('readable'); return (this.buffer.length == 0 ? true : false); }; - this.read = function () - { + this.read = function () { var size = arguments.length == 0 ? undefined : arguments[0]; var bytesRead = 0; var list = []; - while((size == undefined || bytesRead < size) && this.buffer.length > 0) - { + while ((size == undefined || bytesRead < size) && this.buffer.length > 0) { var len = this.buffer[0].data.length - this.buffer[0].offset; var offset = this.buffer[0].offset; - - if(len > (size - bytesRead)) - { + + if (len > (size - bytesRead)) { // Only reading a subset list.push(this.buffer[0].data.slice(offset, offset + size - bytesRead)); this.buffer[0].offset += (size - bytesRead); bytesRead += (size - bytesRead); } - else - { + else { // Reading the entire thing list.push(this.buffer[0].data.slice(offset)); bytesRead += len; this.buffer.shift(); } } - this._readCheckImmediate = setImmediate(function (buffered) - { + this._readCheckImmediate = setImmediate(function (buffered) { buffered._readCheckImmediate = undefined; - if(buffered.buffer.length == 0) - { + if (buffered.buffer.length == 0) { // drained buffered.emit('drain'); } - else - { + else { // not drained buffered.emit('readable'); } @@ -112,38 +99,33 @@ function stream_bufferedWrite() } -function lme_heci() -{ +function lme_heci() { var emitterUtils = require('events').inherits(this); emitterUtils.createEvent('error'); emitterUtils.createEvent('connect'); - + var heci = require('heci'); this.INITIAL_RXWINDOW_SIZE = 4096; - + this._LME = heci.create(); this._LME.LMS = this; - this._LME.on('error', function (e) { this.Parent.emit('error', e); }); - this._LME.on('connect', function () - { + this._LME.on('error', function (e) { this.LMS.emit('error', e); }); + this._LME.on('connect', function () { this.LMS.emit('connect'); - this.on('data', function (chunk) - { + this.on('data', function (chunk) { // this = HECI var cmd = chunk.readUInt8(0); - - switch(cmd) - { + + switch (cmd) { default: //console.log('Received ' + chunk.length + ' bytes of data for LMS'); //console.log('Command = ' + cmd); break; - case APF_SERVICE_REQUEST: + case APF_SERVICE_REQUEST: var nameLen = chunk.readUInt32BE(1); var name = chunk.slice(5, nameLen + 5); //console.log("Service Request for: " + name); - if (name == 'pfwd@amt.intel.com' || name == 'auth@amt.intel.com') - { + if (name == 'pfwd@amt.intel.com' || name == 'auth@amt.intel.com') { var outBuffer = Buffer.alloc(5 + nameLen); outBuffer.writeUInt8(6, 0); outBuffer.writeUInt32BE(nameLen, 1); @@ -151,30 +133,26 @@ function lme_heci() this.write(outBuffer); //console.log('Answering APF_SERVICE_REQUEST'); } - else - { + else { //console.log('UNKNOWN APF_SERVICE_REQUEST'); } break; - case APF_GLOBAL_REQUEST: + case APF_GLOBAL_REQUEST: var nameLen = chunk.readUInt32BE(1); var name = chunk.slice(5, nameLen + 5).toString(); - switch(name) - { + switch (name) { case 'tcpip-forward': var len = chunk.readUInt32BE(nameLen + 6); var port = chunk.readUInt32BE(nameLen + 10 + len); //console.log("[" + chunk.length + "/" + len + "] APF_GLOBAL_REQUEST for: " + name + " on port " + port); - if (this[name] == undefined) - { + if (this[name] == undefined) { this[name] = {}; } this[name][port] = require('net').createServer(); this[name][port].HECI = this; this[name][port].listen({ port: port }); - this[name][port].on('connection', function (socket) - { + this[name][port].on('connection', function (socket) { //console.log('New [' + socket.remoteFamily + '] TCP Connection on: ' + socket.remoteAddress + ' :' + socket.localPort); this.HECI.LMS.bindDuplexStream(socket, socket.remoteFamily, socket.localPort); }); @@ -192,54 +170,48 @@ function lme_heci() break; } break; - case APF_CHANNEL_OPEN_CONFIRMATION: - var rChannel = chunk.readUInt32BE(1); - var sChannel = chunk.readUInt32BE(5); - var wSize = chunk.readUInt32BE(9); - //console.log('rChannel/' + rChannel + ', sChannel/' + sChannel + ', wSize/' + wSize); - if (this.sockets[rChannel] != undefined) - { - this.sockets[rChannel].lme.amtId = sChannel; - this.sockets[rChannel].lme.rxWindow = wSize; - this.sockets[rChannel].lme.txWindow = wSize; - this.sockets[rChannel].lme.LME_CHANNEL_STATUS = 'LME_CS_CONNECTED'; - //console.log('LME_CS_CONNECTED'); - this.sockets[rChannel].bufferedStream = new stream_bufferedWrite(); - this.sockets[rChannel].bufferedStream.socket = this.sockets[rChannel]; - this.sockets[rChannel].bufferedStream.on('readable', function () - { - if(this.socket.lme.txWindow > 0) - { - var buffer = this.read(this.socket.lme.txWindow); - var packet = Buffer.alloc(9 + buffer.length); - packet.writeUInt8(APF_CHANNEL_DATA, 0); - packet.writeUInt32BE(this.socket.lme.amtId, 1); - packet.writeUInt32BE(buffer.length, 5); - buffer.copy(packet, 9); - this.socket.lme.txWindow -= buffer.length; - this.socket.HECI.write(packet); - } - }); - this.sockets[rChannel].bufferedStream.on('drain', function () - { - this.socket.resume(); - }); - this.sockets[rChannel].on('data', function (chunk) - { - if (!this.bufferedStream.write(chunk)) { this.pause(); } - }); - this.sockets[rChannel].on('end', function () - { - var outBuffer = Buffer.alloc(5); - outBuffer.writeUInt8(APF_CHANNEL_CLOSE, 0); - outBuffer.writeUInt32BE(this.lme.amtId, 1); - this.HECI.write(outBuffer); - }); - this.sockets[rChannel].resume(); - } - - break; - case APF_PROTOCOLVERSION: + case APF_CHANNEL_OPEN_CONFIRMATION: + var rChannel = chunk.readUInt32BE(1); + var sChannel = chunk.readUInt32BE(5); + var wSize = chunk.readUInt32BE(9); + //console.log('rChannel/' + rChannel + ', sChannel/' + sChannel + ', wSize/' + wSize); + if (this.sockets[rChannel] != undefined) { + this.sockets[rChannel].lme.amtId = sChannel; + this.sockets[rChannel].lme.rxWindow = wSize; + this.sockets[rChannel].lme.txWindow = wSize; + this.sockets[rChannel].lme.LME_CHANNEL_STATUS = 'LME_CS_CONNECTED'; + //console.log('LME_CS_CONNECTED'); + this.sockets[rChannel].bufferedStream = new stream_bufferedWrite(); + this.sockets[rChannel].bufferedStream.socket = this.sockets[rChannel]; + this.sockets[rChannel].bufferedStream.on('readable', function () { + if (this.socket.lme.txWindow > 0) { + var buffer = this.read(this.socket.lme.txWindow); + var packet = Buffer.alloc(9 + buffer.length); + packet.writeUInt8(APF_CHANNEL_DATA, 0); + packet.writeUInt32BE(this.socket.lme.amtId, 1); + packet.writeUInt32BE(buffer.length, 5); + buffer.copy(packet, 9); + this.socket.lme.txWindow -= buffer.length; + this.socket.HECI.write(packet); + } + }); + this.sockets[rChannel].bufferedStream.on('drain', function () { + this.socket.resume(); + }); + this.sockets[rChannel].on('data', function (chunk) { + if (!this.bufferedStream.write(chunk)) { this.pause(); } + }); + this.sockets[rChannel].on('end', function () { + var outBuffer = Buffer.alloc(5); + outBuffer.writeUInt8(APF_CHANNEL_CLOSE, 0); + outBuffer.writeUInt32BE(this.lme.amtId, 1); + this.HECI.write(outBuffer); + }); + this.sockets[rChannel].resume(); + } + + break; + case APF_PROTOCOLVERSION: var major = chunk.readUInt32BE(1); var minor = chunk.readUInt32BE(5); var reason = chunk.readUInt32BE(9); @@ -254,16 +226,13 @@ function lme_heci() case APF_CHANNEL_WINDOW_ADJUST: var rChannelId = chunk.readUInt32BE(1); var bytesToAdd = chunk.readUInt32BE(5); - if (this.sockets[rChannelId] != undefined) - { + if (this.sockets[rChannelId] != undefined) { this.sockets[rChannelId].lme.txWindow += bytesToAdd; - if (!this.sockets[rChannelId].bufferedStream.isEmpty() && this.sockets[rChannelId].bufferedStream.isWaiting()) - { + if (!this.sockets[rChannelId].bufferedStream.isEmpty() && this.sockets[rChannelId].bufferedStream.isWaiting()) { this.sockets[rChannelId].bufferedStream.emit('readable'); } } - else - { + else { //console.log('Unknown Recipient ID/' + rChannelId + ' for APF_CHANNEL_WINDOW_ADJUST'); } break; @@ -271,11 +240,9 @@ function lme_heci() var rChannelId = chunk.readUInt32BE(1); var dataLen = chunk.readUInt32BE(5); var data = chunk.slice(9, 9 + dataLen); - if (this.sockets[rChannelId] != undefined) - { + if (this.sockets[rChannelId] != undefined) { this.sockets[rChannelId].pendingBytes.push(data.length); - this.sockets[rChannelId].write(data, function () - { + this.sockets[rChannelId].write(data, function () { var written = this.pendingBytes.shift(); var outBuffer = Buffer.alloc(9); outBuffer.writeUInt8(APF_CHANNEL_WINDOW_ADJUST, 0); @@ -284,35 +251,31 @@ function lme_heci() this.HECI.write(outBuffer); }); } - else - { + else { //console.log('Unknown Recipient ID/' + rChannelId + ' for APF_CHANNEL_DATA'); } break; case APF_CHANNEL_CLOSE: var rChannelId = chunk.readUInt32BE(1); - if (this.sockets[rChannelId] != undefined) - { - this.sockets[rChannelId].end(); + if (this.sockets[rChannelId] != undefined) { + this.sockets[rChannelId].end(); var amtId = this.sockets[rChannelId].lme.amtId; var buffer = Buffer.alloc(5); delete this.sockets[rChannelId]; - + buffer.writeUInt8(APF_CHANNEL_CLOSE, 0); buffer.writeUInt32BE(amtId, 1); this.write(buffer); } - else - { + else { //console.log('Unknown Recipient ID/' + rChannelId + ' for APF_CHANNEL_CLOSE'); } break; } }); }); - - this.bindDuplexStream = function (duplexStream, remoteFamily, localPort) - { + + this.bindDuplexStream = function (duplexStream, remoteFamily, localPort) { var socket = duplexStream; //console.log('New [' + remoteFamily + '] Virtual Connection/' + socket.localPort); socket.pendingBytes = []; @@ -327,19 +290,16 @@ function lme_heci() buffer.writeUInt32BE(socket.lme.ourId); buffer.writeUInt32BE(this.INITIAL_RXWINDOW_SIZE); buffer.writeUInt32BE(0xFFFFFFFF); - for (var i = 0; i < 2; ++i) - { - if (remoteFamily == 'IPv6') - { + for (var i = 0; i < 2; ++i) { + if (remoteFamily == 'IPv6') { buffer.writeUInt32BE(3); buffer.write('::1'); } - else - { + else { buffer.writeUInt32BE(9); buffer.write('127.0.0.1'); } - + buffer.writeUInt32BE(localPort); } this._LME.write(buffer.buffer); @@ -347,7 +307,7 @@ function lme_heci() this._LME.sockets[socket.lme.ourId] = socket; socket.pause(); }; - + this._LME.connect(heci.GUIDS.LME, { noPipeline: 0 }); } diff --git a/agents/modules_meshcore/amt-scanner.js b/agents/modules_meshcore/amt-scanner.js index f8c34278..9474dfb0 100644 --- a/agents/modules_meshcore/amt-scanner.js +++ b/agents/modules_meshcore/amt-scanner.js @@ -79,7 +79,7 @@ function AMTScanner() { server.bind({ address: '0.0.0.0', port: 0, exclusive: true }); var tmout = setTimeout(function cb() { //console.log("Server closed"); - //server.close(); + server.close(); server.parent.emit('found', server.scanResults); if (func != null) { func(server.scanResults); } delete server; diff --git a/agents/modules_meshcore/lme_heci.js b/agents/modules_meshcore/lme_heci.js index 2ecb0ec5..2ad039b4 100644 --- a/agents/modules_meshcore/lme_heci.js +++ b/agents/modules_meshcore/lme_heci.js @@ -123,7 +123,7 @@ function lme_heci() this._LME = heci.create(); this._LME.LMS = this; - this._LME.on('error', function (e) { this.Parent.emit('error', e); }); + this._LME.on('error', function (e) { this.LMS.emit('error', e); }); this._LME.on('connect', function () { this.LMS.emit('connect'); diff --git a/certoperations.js b/certoperations.js index d0625ff3..0fd71d71 100644 --- a/certoperations.js +++ b/certoperations.js @@ -11,6 +11,7 @@ module.exports.CertificateOperations = function () { obj.fs = require('fs'); obj.forge = require('node-forge'); + obj.crypto = require('crypto'); obj.pki = obj.forge.pki; obj.dirExists = function (filePath) { try { return obj.fs.statSync(filePath).isDirectory(); } catch (err) { return false; } } obj.getFilesizeInBytes = function(filename) { try { return obj.fs.statSync(filename)["size"]; } catch (err) { return -1; } } @@ -412,52 +413,56 @@ module.exports.CertificateOperations = function () { return r; } - // Start accelerators + // Accelerators, used to dispatch work to other processes const fork = require('child_process').fork; - const program = require('path').resolve('meshaccelerator.js'); - const acceleratorCreateCount = require('os').cpus().length; + const program = require('path').join(__dirname, 'meshaccelerator.js'); + const acceleratorTotalCount = require('os').cpus().length; + var acceleratorCreateCount = acceleratorTotalCount; var freeAccelerators = []; + var pendingAccelerator = []; + obj.acceleratorCertStore = null; // Create a new accelerator module - obj.getAccelerator = function() { + obj.getAccelerator = function () { + if (obj.acceleratorCertStore == null) { return null; } if (freeAccelerators.length > 0) { return freeAccelerators.pop(); } if (acceleratorCreateCount > 0) { + acceleratorCreateCount--; var accelerator = fork(program, [], { stdio: ['pipe', 'pipe', 'pipe', 'ipc'] }); - accelerator.on('message', function (message) { this.func(message); freeAccelerators.push(this); }); - if (obj.acceleratorCertStore != null) { accelerator.send({ action: 'setState', certs: obj.acceleratorCertStore }); } + accelerator.on('message', function (message) { this.func(message); if (pendingAccelerator.length > 0) { accelerator.send(pendingAccelerator.shift()); } else { freeAccelerators.push(this); } }); + accelerator.send({ action: 'setState', certs: obj.acceleratorCertStore }); return accelerator; } return null; } // Set the state of the accelerators. This way, we don't have to send certificate & keys to them each time. - obj.acceleratorCertStore = null; - obj.acceleratorPerformSetState = function (certificates) { + obj.acceleratorStart = function (certificates) { + if (obj.acceleratorCertStore != null) { console.error('ERROR: Accelerators can only be started once.'); return; } obj.acceleratorCertStore = [{ cert: certificates.agent.cert, key: certificates.agent.key }]; if (certificates.swarmserver != null) { obj.acceleratorCertStore.push({ cert: certificates.swarmserver.cert, key: certificates.swarmserver.key }); } } // Perform any RSA signature, just pass in the private key and data. obj.acceleratorPerformSignature = function (privatekey, data, func) { - var acc = obj.getAccelerator(); - if (acc == null) { + if (acceleratorTotalCount <= 1) { // No accelerators available if (typeof privatekey == 'number') { privatekey = obj.acceleratorCertStore[privatekey].key; } - const sign = crypto.createSign('SHA384'); + const sign = obj.crypto.createSign('SHA384'); sign.end(new Buffer(data, 'binary')); func(sign.sign(privatekey).toString('binary')); } else { - // Use the accelerator - acc.func = func; - acc.send({ action: 'sign', key: privatekey, data: data }); + var acc = obj.getAccelerator(); + if (acc == null) { + // Add to pending accelerator workload + pendingAccelerator.push({ action: 'sign', key: privatekey, data: data }); + } else { + // Send to accelerator now + acc.func = func; + acc.send({ action: 'sign', key: privatekey, data: data }); + } } } - // Perform a RSA signature. This is time consuming - obj.acceleratorPerformVerify = function (publickey, data, msg, func) { - console.log('Performing verification...'); - func(publickey.verify(data, msg)); - } - return obj; }; diff --git a/letsEncrypt.js b/letsEncrypt.js new file mode 100644 index 00000000..5d0145ab --- /dev/null +++ b/letsEncrypt.js @@ -0,0 +1,81 @@ +/** +* @description MeshCentral letsEncrypt module +* @author Ylian Saint-Hilaire +* @copyright Intel Corporation 2018 +* @license Apache-2.0 +* @version v0.0.1 +*/ + +module.exports.CreateLetsEncrypt = function (parent) { + var obj = {}; + obj.parent = parent; + obj.webrootPath = obj.parent.path.join(obj.parent.datapath, 'acme-challenges'); + obj.workPath = obj.parent.path.join(obj.parent.datapath, 'acme-challenges', 'work'); + obj.logsPath = obj.parent.path.join(obj.parent.datapath, 'acme-challenges', 'logs'); + + try { obj.parent.fs.mkdirSync(obj.webrootPath); } catch (e) { } + try { obj.parent.fs.mkdirSync(obj.workPath); } catch (e) { } + try { obj.parent.fs.mkdirSync(obj.logsPath); } catch (e) { } + + console.log('CreateLetsEncrypt-1', obj.webrootPath); + console.log('CreateLetsEncrypt-1', obj.workPath); + console.log('CreateLetsEncrypt-1', obj.logsPath); + + obj.lex = require('greenlock-express').create({ + // Set to https://acme-v01.api.letsencrypt.org/directory in production + server: 'staging' + + // If you wish to replace the default plugins, you may do so here + , challenges: { + 'http-01': require('le-challenge-fs').create({ webrootPath: obj.webrootPath }) + } + , store: require('le-store-certbot').create({ + //configDir: '/etc/letsencrypt', + //privkeyPath: ':configDir/live/:hostname/privkey.pem', + //fullchainPath: ':configDir/live/:hostname/fullchain.pem', + //certPath: ':configDir/live/:hostname/cert.pem', + //chainPath: ':configDir/live/:hostname/chain.pem', + workDir: obj.workPath, + logsDir: obj.logsPath, + webrootPath: obj.webrootPath, + debug: false + }) + , approveDomains: approveDomains + }); + + console.log('CreateLetsEncrypt-2'); + function approveDomains(opts, certs, func) { + console.log('approveDomains', opts, certs); + + // This is where you check your database and associated + // email addresses with domains and agreements and such + + + // The domains being approved for the first time are listed in opts.domains + // Certs being renewed are listed in certs.altnames + if (certs) { + opts.domains = ['example.com', 'yourdomain.com'] + } else { + opts.email = 'john.doe@example.com'; + opts.agreeTos = true; + } + + // NOTE: you can also change other options such as `challengeType` and `challenge` + // opts.challengeType = 'http-01'; + // opts.challenge = require('le-challenge-fs').create({}); + + func(null, { options: opts, certs: certs }); + } + + // Handles acme-challenge and redirects to https + require('http').createServer(obj.lex.middleware(require('redirect-https')())).listen(81, function () { console.log("Listening for ACME http-01 challenges on", this.address()); }); + + var app = require('express')(); + app.use('/', function (req, res) { res.end('Hello, World!'); }); + + // Handles your app + require('https').createServer(obj.lex.httpsOptions, obj.lex.middleware(app)).listen(443, function () { console.log("Listening for ACME tls-sni-01 challenges and serve app on", this.address()); }); + + console.log('CreateLetsEncrypt-3'); + return obj; +} \ No newline at end of file diff --git a/meshagent.js b/meshagent.js index 009bdba4..b4ec105b 100644 --- a/meshagent.js +++ b/meshagent.js @@ -170,7 +170,7 @@ module.exports.CreateMeshAgent = function (parent, db, ws, req, args, domain) { obj.send(obj.common.ShortToStr(2) + obj.common.ShortToStr(obj.parent.swarmCertificateAsn1.length) + obj.parent.swarmCertificateAsn1 + signature); // Command 2, certificate + signature }); } else { - // Perform the hash signature using new server agent certificate + // Perform the hash signature using the server agent certificate obj.parent.parent.certificateOperations.acceleratorPerformSignature(0, msg.substring(2) + obj.nonce, function (signature) { // Send back our certificate + signature obj.send(obj.common.ShortToStr(2) + obj.common.ShortToStr(obj.parent.agentCertificateAsn1.length) + obj.parent.agentCertificateAsn1 + signature); // Command 2, certificate + signature @@ -190,9 +190,8 @@ module.exports.CreateMeshAgent = function (parent, db, ws, req, args, domain) { // Decode the certificate var certlen = obj.common.ReadShort(msg, 2); obj.unauth = {}; - obj.unauth.nodeCert = null; - try { obj.unauth.nodeCert = obj.forge.pki.certificateFromAsn1(obj.forge.asn1.fromDer(msg.substring(4, 4 + certlen))); } catch (e) { return; } - obj.unauth.nodeid = new Buffer(obj.forge.pki.getPublicKeyFingerprint(obj.unauth.nodeCert.publicKey, { md: obj.forge.md.sha384.create() }).data, 'binary').toString('base64').replace(/\+/g, '@').replace(/\//g, '$'); + try { obj.unauth.nodeid = new Buffer(obj.forge.pki.getPublicKeyFingerprint(obj.forge.pki.certificateFromAsn1(obj.forge.asn1.fromDer(msg.substring(4, 4 + certlen))).publicKey, { md: obj.forge.md.sha384.create() }).data, 'binary').toString('base64').replace(/\+/g, '@').replace(/\//g, '$'); } catch (e) { return; } + obj.unauth.nodeCertPem = '-----BEGIN CERTIFICATE-----\r\n' + new Buffer(msg.substring(4, 4 + certlen), 'binary').toString('base64') + '\r\n-----END CERTIFICATE-----'; // Check the agent signature if we can if (obj.agentnonce == null) { obj.unauthsign = msg.substring(4 + certlen); } else { if (processAgentSignature(msg.substring(4 + certlen)) == false) { console.log('Agent connected with bad signature, holding connection (' + obj.remoteaddr + ').'); return; } } @@ -237,7 +236,7 @@ module.exports.CreateMeshAgent = function (parent, db, ws, req, args, domain) { // Start authenticate the mesh agent by sending a auth nonce & server TLS cert hash. // Send 384 bits SHA384 hash of TLS cert public key + 384 bits nonce - obj.nonce = obj.forge.random.getBytesSync(48); + obj.nonce = obj.parent.crypto.randomBytes(48).toString('binary'); obj.send(obj.common.ShortToStr(1) + getWebCertHash(obj.domain) + obj.nonce); // Command 1, hash + nonce // Once we get all the information about an agent, run this to hook everything up to the server @@ -357,18 +356,17 @@ module.exports.CreateMeshAgent = function (parent, db, ws, req, args, domain) { // Get the web certificate hash for the speficied domain function getWebCertHash(domain) { - //var hash = obj.parent.webCertificateHashs[domain.id]; - //if (hash == null) return obj.parent.webCertificateHash; else return hash; + var hash = obj.parent.webCertificateHashs[domain.id]; + if (hash != null) return hash; return obj.parent.webCertificateHash; } // Verify the agent signature function processAgentSignature(msg) { - var md = obj.forge.md.sha384.create(); // TODO: Switch this to SHA384 on node instead of forge. - md.update(getWebCertHash(obj.domain), 'binary'); - md.update(obj.nonce, 'binary'); - md.update(obj.agentnonce, 'binary'); - if (obj.unauth.nodeCert.publicKey.verify(md.digest().bytes(), msg) == false) { return false; } // TODO: Check if this is slow or not. May n + // Verify the signature. This is the fast way, without using forge. + const verify = obj.parent.crypto.createVerify('SHA384'); + verify.end(new Buffer(getWebCertHash(obj.domain) + obj.nonce + obj.agentnonce, 'binary')); + if (verify.verify(obj.unauth.nodeCertPem, new Buffer(msg, 'binary')) !== true) { return false; } // Connection is a success, clean up obj.nodeid = obj.unauth.nodeid; diff --git a/meshcentral.js b/meshcentral.js index 351fd41e..a536bb4d 100644 --- a/meshcentral.js +++ b/meshcentral.js @@ -314,7 +314,7 @@ function CreateMeshCentralServer() { obj.certificateOperations = require('./certoperations.js').CertificateOperations() obj.certificateOperations.GetMeshServerCertificate(obj.datapath, obj.args, obj.config, function (certs) { obj.certificates = certs; - obj.certificateOperations.acceleratorPerformSetState(certs); // Set the state of the accelerators + obj.certificateOperations.acceleratorStart(certs); // Set the state of the accelerators // If the certificate is un-configured, force LAN-only mode if (obj.certificates.CommonName == 'un-configured') { console.log('Server name not configured, running in LAN-only mode.'); obj.args.lanonly = true; } diff --git a/multiserver.js b/multiserver.js index 59501978..f701a8c5 100644 --- a/multiserver.js +++ b/multiserver.js @@ -29,7 +29,6 @@ module.exports.CreateMultiServer = function (parent, args) { obj.common = require('./common.js'); obj.forge = require('node-forge'); obj.crypto = require('crypto'); - obj.pki = obj.forge.pki; obj.connectionState = 0; obj.retryTimer = null; obj.retryBackoff = 0; @@ -64,7 +63,7 @@ module.exports.CreateMultiServer = function (parent, args) { obj.ws.on('open', function () { obj.parent.parent.debug(1, 'OutPeer ' + obj.serverid + ': Connected'); obj.connectionState |= 2; - obj.nonce = obj.forge.random.getBytesSync(48); + obj.nonce = obj.crypto.randomBytes(48).toString('binary'); // Get the peer server's certificate and compute the server public key hash if (obj.ws._socket == null) return; @@ -94,30 +93,27 @@ module.exports.CreateMultiServer = function (parent, args) { if (obj.serverCertHash != msg.substring(2, 50)) { obj.parent.parent.debug(1, 'OutPeer: Server hash mismatch.'); disconnect(); return; } obj.servernonce = msg.substring(50); - // Use our agent certificate root private key to sign the ServerHash + ServerNonce + PeerNonce - var md = obj.forge.md.sha384.create(); - md.update(msg.substring(2), 'binary'); - md.update(obj.nonce, 'binary'); + // Perform the hash signature using the server agent certificate + obj.parent.parent.certificateOperations.acceleratorPerformSignature(0, msg.substring(2) + obj.nonce, function (signature) { + // Send back our certificate + signature + obj.ws.send(obj.common.ShortToStr(2) + obj.common.ShortToStr(obj.agentCertificateAsn1.length) + obj.agentCertificateAsn1 + signature); // Command 2, certificate + signature + }); - // Send back our certificate + signature - agentRootCertificateAsn1 = obj.forge.asn1.toDer(obj.forge.pki.certificateToAsn1(obj.certificates.agent.fcert)).getBytes(); - obj.ws.send(obj.common.ShortToStr(2) + obj.common.ShortToStr(agentRootCertificateAsn1.length) + agentRootCertificatAsn1 + obj.certificates.agent.fkey.sign(md)); // Command 3, signature break; } case 2: { // Server certificate var certlen = obj.common.ReadShort(msg, 2), serverCert = null; + var serverCertPem = '-----BEGIN CERTIFICATE-----\r\n' + new Buffer(msg.substring(4, 4 + certlen), 'binary').toString('base64') + '\r\n-----END CERTIFICATE-----'; try { serverCert = obj.forge.pki.certificateFromAsn1(obj.forge.asn1.fromDer(msg.substring(4, 4 + certlen))); } catch (e) { } if (serverCert == null) { obj.parent.parent.debug(1, 'OutPeer: Invalid server certificate.'); disconnect(); return; } var serverid = new Buffer(obj.forge.pki.getPublicKeyFingerprint(serverCert.publicKey, { encoding: 'binary', md: obj.forge.md.sha384.create() }), 'binary').toString('base64').replace(/\+/g, '@').replace(/\//g, '$'); if (serverid !== obj.agentCertificateHashBase64) { obj.parent.parent.debug(1, 'OutPeer: Server hash mismatch.'); disconnect(); return; } - // Server signature, verify it - var md = obj.forge.md.sha384.create(); - md.update(obj.serverCertHash, 'binary'); - md.update(obj.nonce, 'binary'); - md.update(obj.servernonce, 'binary'); - if (serverCert.publicKey.verify(md.digest().bytes(), msg.substring(4 + certlen)) == false) { obj.parent.parent.debug(1, 'OutPeer: Server sign check failed.'); disconnect(); return; } + // Server signature, verify it. This is the fast way, without using forge. (TODO: Use accelerator for this?) + const verify = obj.parent.crypto.createVerify('SHA384'); + verify.end(new Buffer(obj.serverCertHash + obj.nonce + obj.servernonce, 'binary')); + if (verify.verify(serverCertPem, new Buffer(msg.substring(4 + certlen), 'binary')) !== true) { obj.parent.parent.debug(1, 'OutPeer: Server sign check failed.'); disconnect(); return; } // Connection is a success, clean up delete obj.nonce; @@ -128,14 +124,14 @@ module.exports.CreateMultiServer = function (parent, args) { obj.parent.parent.debug(1, 'OutPeer ' + obj.serverid + ': Verified peer connection to ' + obj.url); // Send information about our server to the peer - if (obj.connectionState == 15) { obj.ws.send(JSON.stringify({ action: 'info', serverid: obj.parent.serverid, dbid: obj.parent.parent.db.identifier, key: obj.parent.parent.webserver.serverKey.toString('hex'), serverCertHash: obj.parent.parent.webserver.webCertificateHashBase64 })); } + if (obj.connectionState == 15) { obj.ws.send(JSON.stringify({ action: 'info', serverid: obj.parent.serverid, dbid: obj.parent.parent.db.identifier, key: obj.parent.parent.serverKey.toString('hex'), serverCertHash: obj.parent.parent.webserver.webCertificateHashBase64 })); } //if ((obj.connectionState == 15) && (obj.connectHandler != null)) { obj.connectHandler(1); } break; } case 4: { // Server confirmed authentication, we are allowed to send commands to the server obj.connectionState |= 8; - if (obj.connectionState == 15) { obj.ws.send(JSON.stringify({ action: 'info', serverid: obj.parent.serverid, dbid: obj.parent.parent.db.identifier, key: obj.parent.parent.webserver.serverKey.toString('hex'), serverCertHash: obj.parent.parent.webserver.webCertificateHashBase64 })); } + if (obj.connectionState == 15) { obj.ws.send(JSON.stringify({ action: 'info', serverid: obj.parent.serverid, dbid: obj.parent.parent.db.identifier, key: obj.parent.parent.serverKey.toString('hex'), serverCertHash: obj.parent.parent.webserver.webCertificateHashBase64 })); } //if ((obj.connectionState == 15) && (obj.connectHandler != null)) { obj.connectHandler(1); } break; } @@ -258,15 +254,13 @@ module.exports.CreateMultiServer = function (parent, args) { // Check that the server hash matches out own web certificate hash if (obj.webCertificateHash != msg.substring(2, 50)) { obj.close(); return; } - - // Use our server private key to sign the ServerHash + PeerNonce + ServerNonce - var md = obj.forge.md.sha384.create(); - md.update(msg.substring(2), 'binary'); - md.update(obj.nonce, 'binary'); obj.peernonce = msg.substring(50); - // Send back our certificate + signature - 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 + // Perform the hash signature using the server agent certificate + obj.parent.parent.certificateOperations.acceleratorPerformSignature(0, msg.substring(2) + obj.nonce, function (signature) { + // Send back our certificate + signature + obj.send(obj.common.ShortToStr(2) + obj.common.ShortToStr(obj.agentCertificateAsn1.length) + obj.agentCertificateAsn1 + signature); // Command 2, certificate + signature + }); // Check the peer server signature if we can if (obj.unauthsign != null) { @@ -275,22 +269,25 @@ module.exports.CreateMultiServer = function (parent, args) { } else if (cmd == 2) { // Peer server certificate - if ((msg.length < 4) || ((obj.receivedCommands & 2) != 0)) return; + if ((msg.length < 4) || ((obj.receivedCommands & 2) != 0)) { obj.parent.parent.debug(1, 'InPeer: Invalid command 2.'); return; } obj.receivedCommands += 2; // Peer server can't send the same command twice on the same connection ever. Block DOS attack path. // Decode the certificate var certlen = obj.common.ReadShort(msg, 2); obj.unauth = {}; - obj.unauth.nodeCert = null; - try { obj.unauth.nodeCert = obj.forge.pki.certificateFromAsn1(obj.forge.asn1.fromDer(msg.substring(4, 4 + certlen))); } catch (e) { return; } - obj.unauth.nodeid = new Buffer(obj.forge.pki.getPublicKeyFingerprint(obj.unauth.nodeCert.publicKey, { encoding: 'binary', md: obj.forge.md.sha384.create() }), 'binary').toString('base64').replace(/\+/g, '@').replace(/\//g, '$'); + try { obj.unauth.nodeid = new Buffer(obj.forge.pki.getPublicKeyFingerprint(obj.forge.pki.certificateFromAsn1(obj.forge.asn1.fromDer(msg.substring(4, 4 + certlen))).publicKey, { encoding: 'binary', md: obj.forge.md.sha384.create() }), 'binary').toString('base64').replace(/\+/g, '@').replace(/\//g, '$'); } catch (e) { console.log(e); return; } + obj.unauth.nodeCertPem = '-----BEGIN CERTIFICATE-----\r\n' + new Buffer(msg.substring(4, 4 + certlen), 'binary').toString('base64') + '\r\n-----END CERTIFICATE-----'; // Check the peer server signature if we can - if (obj.peernonce == null) { obj.unauthsign = msg.substring(4 + certlen); } else { if (processPeerSignature(msg.substring(4 + certlen)) == false) { obj.close(); return; } } + if (obj.peernonce == null) { + obj.unauthsign = msg.substring(4 + certlen); + } else { + if (processPeerSignature(msg.substring(4 + certlen)) == false) { obj.parent.parent.debug(1, 'InPeer: Invalid signature.'); obj.close(); return; } + } completePeerServerConnection(); } else if (cmd == 3) { - if ((msg.length < 56) || ((obj.receivedCommands & 4) != 0)) return; + if ((msg.length < 56) || ((obj.receivedCommands & 4) != 0)) { obj.parent.parent.debug(1, 'InPeer: Invalid command 3.'); return; } obj.receivedCommands += 4; // Peer server can't send the same command twice on the same connection ever. Block DOS attack path. completePeerServerConnection(); } @@ -306,25 +303,24 @@ module.exports.CreateMultiServer = function (parent, args) { // Start authenticate the peer server by sending a auth nonce & server TLS cert hash. // Send 384 bits SHA382 hash of TLS cert public key + 384 bits nonce - obj.nonce = obj.forge.random.getBytesSync(48); + obj.nonce = obj.crypto.randomBytes(48).toString('binary'); obj.send(obj.common.ShortToStr(1) + obj.webCertificateHash + obj.nonce); // Command 1, hash + nonce // Once we get all the information about an peer server, run this to hook everything up to the server function completePeerServerConnection() { if (obj.authenticated != 1) return; obj.send(obj.common.ShortToStr(4)); - obj.send(JSON.stringify({ action: 'info', serverid: obj.parent.serverid, dbid: obj.parent.parent.db.identifier, key: obj.parent.parent.webserver.serverKey.toString('hex'), serverCertHash: obj.parent.parent.webserver.webCertificateHashBase64 })); + obj.send(JSON.stringify({ action: 'info', serverid: obj.parent.serverid, dbid: obj.parent.parent.db.identifier, key: obj.parent.parent.serverKey.toString('hex'), serverCertHash: obj.parent.parent.webserver.webCertificateHashBase64 })); obj.authenticated = 2; } // Verify the peer server signature function processPeerSignature(msg) { - var md = obj.forge.md.sha384.create(); // TODO: Switch this to SHA384 on node instead of forge. - md.update(obj.parent.parent.webserver.webCertificateHash, 'binary'); - md.update(obj.nonce, 'binary'); - md.update(obj.peernonce, 'binary'); - if (obj.unauth.nodeCert.publicKey.verify(md.digest().bytes(), msg) == false) { return false; } - if (obj.unauth.nodeid !== obj.agentCertificateHashBase64) { return false; } + // Verify the signature. This is the fast way, without using forge. + const verify = obj.parent.crypto.createVerify('SHA384'); + verify.end(new Buffer(obj.parent.parent.webserver.webCertificateHash + obj.nonce + obj.peernonce, 'binary')); + if (verify.verify(obj.unauth.nodeCertPem, new Buffer(msg, 'binary')) !== true) { console.log('Peer sign fail 1'); return false; } + if (obj.unauth.nodeid !== obj.agentCertificateHashBase64) { console.log('Peer sign fail 2'); return false; } // Connection is a success, clean up obj.nodeid = obj.unauth.nodeid; @@ -333,6 +329,7 @@ module.exports.CreateMultiServer = function (parent, args) { delete obj.unauth; if (obj.unauthsign) delete obj.unauthsign; obj.authenticated = 1; + return true; } diff --git a/package.json b/package.json index 76e84f35..b5446ca0 100644 --- a/package.json +++ b/package.json @@ -1,4 +1,5 @@ { +<<<<<<< HEAD "_from": "meshcentral", "_id": "meshcentral@0.1.2-b", "_inBundle": false, @@ -19,6 +20,15 @@ "#USER", "/", "/meshcentral" +======= + "name": "meshcentral", + "version": "0.1.2-h", + "keywords": [ + "Remote Management", + "Intel AMT", + "Active Management", + "Remote Desktop" +>>>>>>> upstream/master ], "_resolved": "https://registry.npmjs.org/meshcentral/-/meshcentral-0.1.2-b.tgz", "_shasum": "d64ca266607487ee9628c3e5b9ba554864c9d550",