Much improved Windows MeshAgent stability

Ylian Saint-Hilaire committed Jan 12, 2018 at 11:41 UTC f6ef228de6f05986bc4d7de4378bb01bbf3e55e8
15 files changed +269 -214
MeshCentralServer.njsproj
+2
@@ -28,6 +28,8 @@
28 <Compile Include="amtevents.js" />
29 <Compile Include="amtscanner.js" />
30 <Compile Include="amtscript.js" />
31 + <Compile Include="letsEncrypt.js" />
32 + <Compile Include="meshaccelerator.js" />
33 <Compile Include="meshmail.js" />
34 <Compile Include="meshscanner.js" />
35 <Compile Include="certoperations.js" />
agents/MeshService.exe
Binary files a/agents/MeshService.exe and b/agents/MeshService.exe differ
agents/MeshService64.exe
Binary files a/agents/MeshService64.exe and b/agents/MeshService64.exe differ
agents/meshagent_pi
Binary files a/agents/meshagent_pi and b/agents/meshagent_pi differ
agents/meshcore.js
+21 -9
@@ -472,7 +472,6 @@ function createMeshCore(agent) {
472
473 function onTunnelClosed() {
474 sendConsoleText("Tunnel #" + this.httprequest.index + " closed.", this.httprequest.sessionid);
475 - if (this.httprequest.protocol == 1) { this.httprequest.process.end(); delete this.httprequest.process; }
475 delete tunnels[this.httprequest.index];
476
477 /*
@@ -561,7 +560,8 @@ function createMeshCore(agent) {
560 this.httprequest.process.on('exit', function (ecode, sig) { this.tunnel.end(); });
561 this.httprequest.process.stderr.on('data', function (chunk) { this.parent.tunnel.write(chunk); });
562 this.httprequest.process.stdout.pipe(this, { dataTypeSkip: 1 }); // 0 = Binary, 1 = Text.
564 - this.pipe(this.httprequest.process.stdin, { dataTypeSkip: 1 }); // 0 = Binary, 1 = Text.
563 + this.pipe(this.httprequest.process.stdin, { dataTypeSkip: 1, end: false }); // 0 = Binary, 1 = Text.
564 + this.prependListener('end', function () { this.httprequest.process.kill(); });
565 }
566 }
567 if (this.httprequest.protocol == 2) {
@@ -717,7 +717,15 @@ function createMeshCore(agent) {
717 var response = null;
718 switch (cmd) {
719 case 'help': { // Displays available commands
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.';
720 + 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.';
721 + break;
722 + }
723 + case 'eval': { // Eval JavaScript
724 + if (args['_'].length < 1) {
725 + response = 'Proper usage: eval "JavaScript code"'; // Display correct command usage
726 + } else {
727 + response = JSON.stringify(mesh.eval(args['_'][0]));
728 + }
729 break;
730 }
731 case 'notify': { // Send a notification message to the mesh
@@ -735,6 +743,7 @@ function createMeshCore(agent) {
743 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 + '.';
744 if (amtLmsState >= 0) { response += '\r\nBuilt -in LMS: ' + ['Disabled', 'Connecting..', 'Connected'][amtLmsState] + '.'; }
745 response += '\r\nModules: ' + JSON.stringify(addedModules) + '';
746 + response += '\r\nServerConnected: ' + mesh.isControlChannelConnected + '';
747 var oldNodeId = db.Get('OldNodeId');
748 if (oldNodeId != null) { response += '\r\nOldNodeID: ' + oldNodeId + '.'; }
749 response += '\r\ServerState: ' + meshServerConnectionState + '.';
@@ -1136,16 +1145,11 @@ function createMeshCore(agent) {
1145 // Setup the mesh agent event handlers
1146 mesh.AddCommandHandler(handleServerCommand);
1147 mesh.AddConnectHandler(handleServerConnection);
1139 - //mesh.lmsNotification = handleAmtNotification; // TODO
1140 - sendPeriodicServerUpdate(true); // TODO: Check if connected before sending
1141 -
1148 +
1149 // Parse input arguments
1150 //var args = parseArgs(process.argv);
1151 //console.log(args);
1152
1146 - //console.log('Stopping.');
1147 - //process.exit();
1148 -
1153 // Launch LMS
1154 try {
1155 var lme_heci = require('lme_heci');
@@ -1154,6 +1158,14 @@ function createMeshCore(agent) {
1158 amtLms.on('error', function (e) { amtLmsState = 0; amtLms = null; });
1159 amtLms.on('connect', function () { amtLmsState = 2; });
1160 } catch (e) { amtLmsState = -1; amtLms = null; }
1161 +
1162 + // Check if the control channel is connected
1163 + if (mesh.isControlChannelConnected) {
1164 + sendPeriodicServerUpdate(true); // Send the server update
1165 + }
1166 +
1167 + //console.log('Stopping.');
1168 + //process.exit();
1169 }
1170
1171 obj.stop = function () {
agents/modules_meshcmd/amt-scanner.js
+1 -1
@@ -79,7 +79,7 @@ function AMTScanner() {
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();
82 + server.close();
83 server.parent.emit('found', server.scanResults);
84 delete server;
85 }, timeout);
agents/modules_meshcmd/lme_heci.js
+90 -130
@@ -21,8 +21,7 @@ var APF_CHANNEL_CLOSE = 97;
21 var APF_PROTOCOLVERSION = 192;
22
23
24 -function lme_object()
25 -{
24 +function lme_object() {
25 this.ourId = ++lme_id;
26 this.amtId = -1;
27 this.LME_CHANNEL_STATUS = 'LME_CS_FREE';
@@ -32,12 +31,11 @@ function lme_object()
31 this.errorCount = 0;
32 }
33
35 -function stream_bufferedWrite()
36 -{
34 +function stream_bufferedWrite() {
35 var emitterUtils = require('events').inherits(this);
36 this.buffer = [];
37 this._readCheckImmediate = undefined;
40 -
38 +
39 // Writable Events
40 emitterUtils.createEvent('close');
41 emitterUtils.createEvent('drain');
@@ -45,21 +43,17 @@ function stream_bufferedWrite()
43 emitterUtils.createEvent('finish');
44 emitterUtils.createEvent('pipe');
45 emitterUtils.createEvent('unpipe');
48 -
46 +
47 // Readable Events
48 emitterUtils.createEvent('readable');
51 - this.isEmpty = function ()
52 - {
49 + this.isEmpty = function () {
50 return (this.buffer.length == 0);
51 };
55 - this.isWaiting = function ()
56 - {
52 + this.isWaiting = function () {
53 return (this._readCheckImmediate == undefined);
54 };
59 - this.write = function (chunk)
60 - {
61 - for (var args in arguments)
62 - {
55 + this.write = function (chunk) {
56 + for (var args in arguments) {
57 if (typeof (arguments[args]) == 'function') { this.once('drain', arguments[args]); break; }
58 }
59 var tmp = Buffer.alloc(chunk.length);
@@ -68,41 +62,34 @@ function stream_bufferedWrite()
62 this.emit('readable');
63 return (this.buffer.length == 0 ? true : false);
64 };
71 - this.read = function ()
72 - {
65 + this.read = function () {
66 var size = arguments.length == 0 ? undefined : arguments[0];
67 var bytesRead = 0;
68 var list = [];
76 - while((size == undefined || bytesRead < size) && this.buffer.length > 0)
77 - {
69 + while ((size == undefined || bytesRead < size) && this.buffer.length > 0) {
70 var len = this.buffer[0].data.length - this.buffer[0].offset;
71 var offset = this.buffer[0].offset;
80 -
81 - if(len > (size - bytesRead))
82 - {
72 +
73 + if (len > (size - bytesRead)) {
74 // Only reading a subset
75 list.push(this.buffer[0].data.slice(offset, offset + size - bytesRead));
76 this.buffer[0].offset += (size - bytesRead);
77 bytesRead += (size - bytesRead);
78 }
88 - else
89 - {
79 + else {
80 // Reading the entire thing
81 list.push(this.buffer[0].data.slice(offset));
82 bytesRead += len;
83 this.buffer.shift();
84 }
85 }
96 - this._readCheckImmediate = setImmediate(function (buffered)
97 - {
86 + this._readCheckImmediate = setImmediate(function (buffered) {
87 buffered._readCheckImmediate = undefined;
99 - if(buffered.buffer.length == 0)
100 - {
88 + if (buffered.buffer.length == 0) {
89 // drained
90 buffered.emit('drain');
91 }
104 - else
105 - {
92 + else {
93 // not drained
94 buffered.emit('readable');
95 }
@@ -112,38 +99,33 @@ function stream_bufferedWrite()
99 }
100
101
115 -function lme_heci()
116 -{
102 +function lme_heci() {
103 var emitterUtils = require('events').inherits(this);
104 emitterUtils.createEvent('error');
105 emitterUtils.createEvent('connect');
120 -
106 +
107 var heci = require('heci');
108 this.INITIAL_RXWINDOW_SIZE = 4096;
123 -
109 +
110 this._LME = heci.create();
111 this._LME.LMS = this;
126 - this._LME.on('error', function (e) { this.Parent.emit('error', e); });
127 - this._LME.on('connect', function ()
128 - {
112 + this._LME.on('error', function (e) { this.LMS.emit('error', e); });
113 + this._LME.on('connect', function () {
114 this.LMS.emit('connect');
130 - this.on('data', function (chunk)
131 - {
115 + this.on('data', function (chunk) {
116 // this = HECI
117 var cmd = chunk.readUInt8(0);
134 -
135 - switch(cmd)
136 - {
118 +
119 + switch (cmd) {
120 default:
121 //console.log('Received ' + chunk.length + ' bytes of data for LMS');
122 //console.log('Command = ' + cmd);
123 break;
141 - case APF_SERVICE_REQUEST:
124 + case APF_SERVICE_REQUEST:
125 var nameLen = chunk.readUInt32BE(1);
126 var name = chunk.slice(5, nameLen + 5);
127 //console.log("Service Request for: " + name);
145 - if (name == 'pfwd@amt.intel.com' || name == 'auth@amt.intel.com')
146 - {
128 + if (name == 'pfwd@amt.intel.com' || name == 'auth@amt.intel.com') {
129 var outBuffer = Buffer.alloc(5 + nameLen);
130 outBuffer.writeUInt8(6, 0);
131 outBuffer.writeUInt32BE(nameLen, 1);
@@ -151,30 +133,26 @@ function lme_heci()
133 this.write(outBuffer);
134 //console.log('Answering APF_SERVICE_REQUEST');
135 }
154 - else
155 - {
136 + else {
137 //console.log('UNKNOWN APF_SERVICE_REQUEST');
138 }
139 break;
159 - case APF_GLOBAL_REQUEST:
140 + case APF_GLOBAL_REQUEST:
141 var nameLen = chunk.readUInt32BE(1);
142 var name = chunk.slice(5, nameLen + 5).toString();
143
163 - switch(name)
164 - {
144 + switch (name) {
145 case 'tcpip-forward':
146 var len = chunk.readUInt32BE(nameLen + 6);
147 var port = chunk.readUInt32BE(nameLen + 10 + len);
148 //console.log("[" + chunk.length + "/" + len + "] APF_GLOBAL_REQUEST for: " + name + " on port " + port);
169 - if (this[name] == undefined)
170 - {
149 + if (this[name] == undefined) {
150 this[name] = {};
151 }
152 this[name][port] = require('net').createServer();
153 this[name][port].HECI = this;
154 this[name][port].listen({ port: port });
176 - this[name][port].on('connection', function (socket)
177 - {
155 + this[name][port].on('connection', function (socket) {
156 //console.log('New [' + socket.remoteFamily + '] TCP Connection on: ' + socket.remoteAddress + ' :' + socket.localPort);
157 this.HECI.LMS.bindDuplexStream(socket, socket.remoteFamily, socket.localPort);
158 });
@@ -192,54 +170,48 @@ function lme_heci()
170 break;
171 }
172 break;
195 - case APF_CHANNEL_OPEN_CONFIRMATION:
196 - var rChannel = chunk.readUInt32BE(1);
197 - var sChannel = chunk.readUInt32BE(5);
198 - var wSize = chunk.readUInt32BE(9);
199 - //console.log('rChannel/' + rChannel + ', sChannel/' + sChannel + ', wSize/' + wSize);
200 - if (this.sockets[rChannel] != undefined)
201 - {
202 - this.sockets[rChannel].lme.amtId = sChannel;
203 - this.sockets[rChannel].lme.rxWindow = wSize;
204 - this.sockets[rChannel].lme.txWindow = wSize;
205 - this.sockets[rChannel].lme.LME_CHANNEL_STATUS = 'LME_CS_CONNECTED';
206 - //console.log('LME_CS_CONNECTED');
207 - this.sockets[rChannel].bufferedStream = new stream_bufferedWrite();
208 - this.sockets[rChannel].bufferedStream.socket = this.sockets[rChannel];
209 - this.sockets[rChannel].bufferedStream.on('readable', function ()
210 - {
211 - if(this.socket.lme.txWindow > 0)
212 - {
213 - var buffer = this.read(this.socket.lme.txWindow);
214 - var packet = Buffer.alloc(9 + buffer.length);
215 - packet.writeUInt8(APF_CHANNEL_DATA, 0);
216 - packet.writeUInt32BE(this.socket.lme.amtId, 1);
217 - packet.writeUInt32BE(buffer.length, 5);
218 - buffer.copy(packet, 9);
219 - this.socket.lme.txWindow -= buffer.length;
220 - this.socket.HECI.write(packet);
221 - }
222 - });
223 - this.sockets[rChannel].bufferedStream.on('drain', function ()
224 - {
225 - this.socket.resume();
226 - });
227 - this.sockets[rChannel].on('data', function (chunk)
228 - {
229 - if (!this.bufferedStream.write(chunk)) { this.pause(); }
230 - });
231 - this.sockets[rChannel].on('end', function ()
232 - {
233 - var outBuffer = Buffer.alloc(5);
234 - outBuffer.writeUInt8(APF_CHANNEL_CLOSE, 0);
235 - outBuffer.writeUInt32BE(this.lme.amtId, 1);
236 - this.HECI.write(outBuffer);
237 - });
238 - this.sockets[rChannel].resume();
239 - }
240 -
241 - break;
242 - case APF_PROTOCOLVERSION:
173 + case APF_CHANNEL_OPEN_CONFIRMATION:
174 + var rChannel = chunk.readUInt32BE(1);
175 + var sChannel = chunk.readUInt32BE(5);
176 + var wSize = chunk.readUInt32BE(9);
177 + //console.log('rChannel/' + rChannel + ', sChannel/' + sChannel + ', wSize/' + wSize);
178 + if (this.sockets[rChannel] != undefined) {
179 + this.sockets[rChannel].lme.amtId = sChannel;
180 + this.sockets[rChannel].lme.rxWindow = wSize;
181 + this.sockets[rChannel].lme.txWindow = wSize;
182 + this.sockets[rChannel].lme.LME_CHANNEL_STATUS = 'LME_CS_CONNECTED';
183 + //console.log('LME_CS_CONNECTED');
184 + this.sockets[rChannel].bufferedStream = new stream_bufferedWrite();
185 + this.sockets[rChannel].bufferedStream.socket = this.sockets[rChannel];
186 + this.sockets[rChannel].bufferedStream.on('readable', function () {
187 + if (this.socket.lme.txWindow > 0) {
188 + var buffer = this.read(this.socket.lme.txWindow);
189 + var packet = Buffer.alloc(9 + buffer.length);
190 + packet.writeUInt8(APF_CHANNEL_DATA, 0);
191 + packet.writeUInt32BE(this.socket.lme.amtId, 1);
192 + packet.writeUInt32BE(buffer.length, 5);
193 + buffer.copy(packet, 9);
194 + this.socket.lme.txWindow -= buffer.length;
195 + this.socket.HECI.write(packet);
196 + }
197 + });
198 + this.sockets[rChannel].bufferedStream.on('drain', function () {
199 + this.socket.resume();
200 + });
201 + this.sockets[rChannel].on('data', function (chunk) {
202 + if (!this.bufferedStream.write(chunk)) { this.pause(); }
203 + });
204 + this.sockets[rChannel].on('end', function () {
205 + var outBuffer = Buffer.alloc(5);
206 + outBuffer.writeUInt8(APF_CHANNEL_CLOSE, 0);
207 + outBuffer.writeUInt32BE(this.lme.amtId, 1);
208 + this.HECI.write(outBuffer);
209 + });
210 + this.sockets[rChannel].resume();
211 + }
212 +
213 + break;
214 + case APF_PROTOCOLVERSION:
215 var major = chunk.readUInt32BE(1);
216 var minor = chunk.readUInt32BE(5);
217 var reason = chunk.readUInt32BE(9);
@@ -254,16 +226,13 @@ function lme_heci()
226 case APF_CHANNEL_WINDOW_ADJUST:
227 var rChannelId = chunk.readUInt32BE(1);
228 var bytesToAdd = chunk.readUInt32BE(5);
257 - if (this.sockets[rChannelId] != undefined)
258 - {
229 + if (this.sockets[rChannelId] != undefined) {
230 this.sockets[rChannelId].lme.txWindow += bytesToAdd;
260 - if (!this.sockets[rChannelId].bufferedStream.isEmpty() && this.sockets[rChannelId].bufferedStream.isWaiting())
261 - {
231 + if (!this.sockets[rChannelId].bufferedStream.isEmpty() && this.sockets[rChannelId].bufferedStream.isWaiting()) {
232 this.sockets[rChannelId].bufferedStream.emit('readable');
233 }
234 }
265 - else
266 - {
235 + else {
236 //console.log('Unknown Recipient ID/' + rChannelId + ' for APF_CHANNEL_WINDOW_ADJUST');
237 }
238 break;
@@ -271,11 +240,9 @@ function lme_heci()
240 var rChannelId = chunk.readUInt32BE(1);
241 var dataLen = chunk.readUInt32BE(5);
242 var data = chunk.slice(9, 9 + dataLen);
274 - if (this.sockets[rChannelId] != undefined)
275 - {
243 + if (this.sockets[rChannelId] != undefined) {
244 this.sockets[rChannelId].pendingBytes.push(data.length);
277 - this.sockets[rChannelId].write(data, function ()
278 - {
245 + this.sockets[rChannelId].write(data, function () {
246 var written = this.pendingBytes.shift();
247 var outBuffer = Buffer.alloc(9);
248 outBuffer.writeUInt8(APF_CHANNEL_WINDOW_ADJUST, 0);
@@ -284,35 +251,31 @@ function lme_heci()
251 this.HECI.write(outBuffer);
252 });
253 }
287 - else
288 - {
254 + else {
255 //console.log('Unknown Recipient ID/' + rChannelId + ' for APF_CHANNEL_DATA');
256 }
257 break;
258 case APF_CHANNEL_CLOSE:
259 var rChannelId = chunk.readUInt32BE(1);
294 - if (this.sockets[rChannelId] != undefined)
295 - {
296 - this.sockets[rChannelId].end();
260 + if (this.sockets[rChannelId] != undefined) {
261 + this.sockets[rChannelId].end();
262 var amtId = this.sockets[rChannelId].lme.amtId;
263 var buffer = Buffer.alloc(5);
264 delete this.sockets[rChannelId];
300 -
265 +
266 buffer.writeUInt8(APF_CHANNEL_CLOSE, 0);
267 buffer.writeUInt32BE(amtId, 1);
268 this.write(buffer);
269 }
305 - else
306 - {
270 + else {
271 //console.log('Unknown Recipient ID/' + rChannelId + ' for APF_CHANNEL_CLOSE');
272 }
273 break;
274 }
275 });
276 });
313 -
314 - this.bindDuplexStream = function (duplexStream, remoteFamily, localPort)
315 - {
277 +
278 + this.bindDuplexStream = function (duplexStream, remoteFamily, localPort) {
279 var socket = duplexStream;
280 //console.log('New [' + remoteFamily + '] Virtual Connection/' + socket.localPort);
281 socket.pendingBytes = [];
@@ -327,19 +290,16 @@ function lme_heci()
290 buffer.writeUInt32BE(socket.lme.ourId);
291 buffer.writeUInt32BE(this.INITIAL_RXWINDOW_SIZE);
292 buffer.writeUInt32BE(0xFFFFFFFF);
330 - for (var i = 0; i < 2; ++i)
331 - {
332 - if (remoteFamily == 'IPv6')
333 - {
293 + for (var i = 0; i < 2; ++i) {
294 + if (remoteFamily == 'IPv6') {
295 buffer.writeUInt32BE(3);
296 buffer.write('::1');
297 }
337 - else
338 - {
298 + else {
299 buffer.writeUInt32BE(9);
300 buffer.write('127.0.0.1');
301 }
342 -
302 +
303 buffer.writeUInt32BE(localPort);
304 }
305 this._LME.write(buffer.buffer);
@@ -347,7 +307,7 @@ function lme_heci()
307 this._LME.sockets[socket.lme.ourId] = socket;
308 socket.pause();
309 };
350 -
310 +
311 this._LME.connect(heci.GUIDS.LME, { noPipeline: 0 });
312 }
313
agents/modules_meshcore/amt-scanner.js
+1 -1
@@ -79,7 +79,7 @@ function AMTScanner() {
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();
82 + server.close();
83 server.parent.emit('found', server.scanResults);
84 if (func != null) { func(server.scanResults); }
85 delete server;
agents/modules_meshcore/lme_heci.js
+1 -1
@@ -123,7 +123,7 @@ function lme_heci()
123
124 this._LME = heci.create();
125 this._LME.LMS = this;
126 - this._LME.on('error', function (e) { this.Parent.emit('error', e); });
126 + this._LME.on('error', function (e) { this.LMS.emit('error', e); });
127 this._LME.on('connect', function ()
128 {
129 this.LMS.emit('connect');
certoperations.js
+25 -20
@@ -11,6 +11,7 @@ module.exports.CertificateOperations = function () {
11
12 obj.fs = require('fs');
13 obj.forge = require('node-forge');
14 + obj.crypto = require('crypto');
15 obj.pki = obj.forge.pki;
16 obj.dirExists = function (filePath) { try { return obj.fs.statSync(filePath).isDirectory(); } catch (err) { return false; } }
17 obj.getFilesizeInBytes = function(filename) { try { return obj.fs.statSync(filename)["size"]; } catch (err) { return -1; } }
@@ -412,52 +413,56 @@ module.exports.CertificateOperations = function () {
413 return r;
414 }
415
415 - // Start accelerators
416 + // Accelerators, used to dispatch work to other processes
417 const fork = require('child_process').fork;
417 - const program = require('path').resolve('meshaccelerator.js');
418 - const acceleratorCreateCount = require('os').cpus().length;
418 + const program = require('path').join(__dirname, 'meshaccelerator.js');
419 + const acceleratorTotalCount = require('os').cpus().length;
420 + var acceleratorCreateCount = acceleratorTotalCount;
421 var freeAccelerators = [];
422 + var pendingAccelerator = [];
423 + obj.acceleratorCertStore = null;
424
425 // Create a new accelerator module
422 - obj.getAccelerator = function() {
426 + obj.getAccelerator = function () {
427 + if (obj.acceleratorCertStore == null) { return null; }
428 if (freeAccelerators.length > 0) { return freeAccelerators.pop(); }
429 if (acceleratorCreateCount > 0) {
430 + acceleratorCreateCount--;
431 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 }); }
432 + accelerator.on('message', function (message) { this.func(message); if (pendingAccelerator.length > 0) { accelerator.send(pendingAccelerator.shift()); } else { freeAccelerators.push(this); } });
433 + accelerator.send({ action: 'setState', certs: obj.acceleratorCertStore });
434 return accelerator;
435 }
436 return null;
437 }
438
439 // 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) {
440 + obj.acceleratorStart = function (certificates) {
441 + if (obj.acceleratorCertStore != null) { console.error('ERROR: Accelerators can only be started once.'); return; }
442 obj.acceleratorCertStore = [{ cert: certificates.agent.cert, key: certificates.agent.key }];
443 if (certificates.swarmserver != null) { obj.acceleratorCertStore.push({ cert: certificates.swarmserver.cert, key: certificates.swarmserver.key }); }
444 }
445
446 // Perform any RSA signature, just pass in the private key and data.
447 obj.acceleratorPerformSignature = function (privatekey, data, func) {
442 - var acc = obj.getAccelerator();
443 - if (acc == null) {
448 + if (acceleratorTotalCount <= 1) {
449 // No accelerators available
450 if (typeof privatekey == 'number') { privatekey = obj.acceleratorCertStore[privatekey].key; }
446 - const sign = crypto.createSign('SHA384');
451 + const sign = obj.crypto.createSign('SHA384');
452 sign.end(new Buffer(data, 'binary'));
453 func(sign.sign(privatekey).toString('binary'));
454 } else {
450 - // Use the accelerator
451 - acc.func = func;
452 - acc.send({ action: 'sign', key: privatekey, data: data });
455 + var acc = obj.getAccelerator();
456 + if (acc == null) {
457 + // Add to pending accelerator workload
458 + pendingAccelerator.push({ action: 'sign', key: privatekey, data: data });
459 + } else {
460 + // Send to accelerator now
461 + acc.func = func;
462 + acc.send({ action: 'sign', key: privatekey, data: data });
463 + }
464 }
465 }
466
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 -
467 return obj;
468 };
letsEncrypt.js new
+81
@@ -0,0 +1,81 @@
1 +/**
2 +* @description MeshCentral letsEncrypt module
3 +* @author Ylian Saint-Hilaire
4 +* @copyright Intel Corporation 2018
5 +* @license Apache-2.0
6 +* @version v0.0.1
7 +*/
8 +
9 +module.exports.CreateLetsEncrypt = function (parent) {
10 + var obj = {};
11 + obj.parent = parent;
12 + obj.webrootPath = obj.parent.path.join(obj.parent.datapath, 'acme-challenges');
13 + obj.workPath = obj.parent.path.join(obj.parent.datapath, 'acme-challenges', 'work');
14 + obj.logsPath = obj.parent.path.join(obj.parent.datapath, 'acme-challenges', 'logs');
15 +
16 + try { obj.parent.fs.mkdirSync(obj.webrootPath); } catch (e) { }
17 + try { obj.parent.fs.mkdirSync(obj.workPath); } catch (e) { }
18 + try { obj.parent.fs.mkdirSync(obj.logsPath); } catch (e) { }
19 +
20 + console.log('CreateLetsEncrypt-1', obj.webrootPath);
21 + console.log('CreateLetsEncrypt-1', obj.workPath);
22 + console.log('CreateLetsEncrypt-1', obj.logsPath);
23 +
24 + obj.lex = require('greenlock-express').create({
25 + // Set to https://acme-v01.api.letsencrypt.org/directory in production
26 + server: 'staging'
27 +
28 + // If you wish to replace the default plugins, you may do so here
29 + , challenges: {
30 + 'http-01': require('le-challenge-fs').create({ webrootPath: obj.webrootPath })
31 + }
32 + , store: require('le-store-certbot').create({
33 + //configDir: '/etc/letsencrypt',
34 + //privkeyPath: ':configDir/live/:hostname/privkey.pem',
35 + //fullchainPath: ':configDir/live/:hostname/fullchain.pem',
36 + //certPath: ':configDir/live/:hostname/cert.pem',
37 + //chainPath: ':configDir/live/:hostname/chain.pem',
38 + workDir: obj.workPath,
39 + logsDir: obj.logsPath,
40 + webrootPath: obj.webrootPath,
41 + debug: false
42 + })
43 + , approveDomains: approveDomains
44 + });
45 +
46 + console.log('CreateLetsEncrypt-2');
47 + function approveDomains(opts, certs, func) {
48 + console.log('approveDomains', opts, certs);
49 +
50 + // This is where you check your database and associated
51 + // email addresses with domains and agreements and such
52 +
53 +
54 + // The domains being approved for the first time are listed in opts.domains
55 + // Certs being renewed are listed in certs.altnames
56 + if (certs) {
57 + opts.domains = ['example.com', 'yourdomain.com']
58 + } else {
59 + opts.email = 'john.doe@example.com';
60 + opts.agreeTos = true;
61 + }
62 +
63 + // NOTE: you can also change other options such as `challengeType` and `challenge`
64 + // opts.challengeType = 'http-01';
65 + // opts.challenge = require('le-challenge-fs').create({});
66 +
67 + func(null, { options: opts, certs: certs });
68 + }
69 +
70 + // Handles acme-challenge and redirects to https
71 + require('http').createServer(obj.lex.middleware(require('redirect-https')())).listen(81, function () { console.log("Listening for ACME http-01 challenges on", this.address()); });
72 +
73 + var app = require('express')();
74 + app.use('/', function (req, res) { res.end('Hello, World!'); });
75 +
76 + // Handles your app
77 + 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()); });
78 +
79 + console.log('CreateLetsEncrypt-3');
80 + return obj;
81 +}
\ No newline at end of file
meshagent.js
+10 -12
@@ -170,7 +170,7 @@ module.exports.CreateMeshAgent = function (parent, db, ws, req, args, domain) {
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 {
173 - // Perform the hash signature using new server agent certificate
173 + // Perform the hash signature using the 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
@@ -190,9 +190,8 @@ module.exports.CreateMeshAgent = function (parent, db, ws, req, args, domain) {
190 // Decode the certificate
191 var certlen = obj.common.ReadShort(msg, 2);
192 obj.unauth = {};
193 - obj.unauth.nodeCert = null;
194 - try { obj.unauth.nodeCert = obj.forge.pki.certificateFromAsn1(obj.forge.asn1.fromDer(msg.substring(4, 4 + certlen))); } catch (e) { return; }
195 - 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, '$');
193 + 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; }
194 + obj.unauth.nodeCertPem = '-----BEGIN CERTIFICATE-----\r\n' + new Buffer(msg.substring(4, 4 + certlen), 'binary').toString('base64') + '\r\n-----END CERTIFICATE-----';
195
196 // Check the agent signature if we can
197 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) {
236
237 // Start authenticate the mesh agent by sending a auth nonce & server TLS cert hash.
238 // Send 384 bits SHA384 hash of TLS cert public key + 384 bits nonce
240 - obj.nonce = obj.forge.random.getBytesSync(48);
239 + obj.nonce = obj.parent.crypto.randomBytes(48).toString('binary');
240 obj.send(obj.common.ShortToStr(1) + getWebCertHash(obj.domain) + obj.nonce); // Command 1, hash + nonce
241
242 // 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) {
356
357 // Get the web certificate hash for the speficied domain
358 function getWebCertHash(domain) {
360 - //var hash = obj.parent.webCertificateHashs[domain.id];
361 - //if (hash == null) return obj.parent.webCertificateHash; else return hash;
359 + var hash = obj.parent.webCertificateHashs[domain.id];
360 + if (hash != null) return hash;
361 return obj.parent.webCertificateHash;
362 }
363
364 // Verify the agent signature
365 function processAgentSignature(msg) {
367 - var md = obj.forge.md.sha384.create(); // TODO: Switch this to SHA384 on node instead of forge.
368 - md.update(getWebCertHash(obj.domain), 'binary');
369 - md.update(obj.nonce, 'binary');
370 - md.update(obj.agentnonce, 'binary');
371 - if (obj.unauth.nodeCert.publicKey.verify(md.digest().bytes(), msg) == false) { return false; } // TODO: Check if this is slow or not. May n
366 + // Verify the signature. This is the fast way, without using forge.
367 + const verify = obj.parent.crypto.createVerify('SHA384');
368 + verify.end(new Buffer(getWebCertHash(obj.domain) + obj.nonce + obj.agentnonce, 'binary'));
369 + if (verify.verify(obj.unauth.nodeCertPem, new Buffer(msg, 'binary')) !== true) { return false; }
370
371 // Connection is a success, clean up
372 obj.nodeid = obj.unauth.nodeid;
meshcentral.js
+1 -1
@@ -314,7 +314,7 @@ function CreateMeshCentralServer() {
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
317 + obj.certificateOperations.acceleratorStart(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; }
multiserver.js
+35 -38
@@ -29,7 +29,6 @@ module.exports.CreateMultiServer = function (parent, args) {
29 obj.common = require('./common.js');
30 obj.forge = require('node-forge');
31 obj.crypto = require('crypto');
32 - obj.pki = obj.forge.pki;
32 obj.connectionState = 0;
33 obj.retryTimer = null;
34 obj.retryBackoff = 0;
@@ -64,7 +63,7 @@ module.exports.CreateMultiServer = function (parent, args) {
63 obj.ws.on('open', function () {
64 obj.parent.parent.debug(1, 'OutPeer ' + obj.serverid + ': Connected');
65 obj.connectionState |= 2;
67 - obj.nonce = obj.forge.random.getBytesSync(48);
66 + obj.nonce = obj.crypto.randomBytes(48).toString('binary');
67
68 // Get the peer server's certificate and compute the server public key hash
69 if (obj.ws._socket == null) return;
@@ -94,30 +93,27 @@ module.exports.CreateMultiServer = function (parent, args) {
93 if (obj.serverCertHash != msg.substring(2, 50)) { obj.parent.parent.debug(1, 'OutPeer: Server hash mismatch.'); disconnect(); return; }
94 obj.servernonce = msg.substring(50);
95
97 - // Use our agent certificate root private key to sign the ServerHash + ServerNonce + PeerNonce
98 - var md = obj.forge.md.sha384.create();
99 - md.update(msg.substring(2), 'binary');
100 - md.update(obj.nonce, 'binary');
96 + // Perform the hash signature using the server agent certificate
97 + obj.parent.parent.certificateOperations.acceleratorPerformSignature(0, msg.substring(2) + obj.nonce, function (signature) {
98 + // Send back our certificate + signature
99 + obj.ws.send(obj.common.ShortToStr(2) + obj.common.ShortToStr(obj.agentCertificateAsn1.length) + obj.agentCertificateAsn1 + signature); // Command 2, certificate + signature
100 + });
101
102 - // Send back our certificate + 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
102 break;
103 }
104 case 2: {
105 // Server certificate
106 var certlen = obj.common.ReadShort(msg, 2), serverCert = null;
107 + var serverCertPem = '-----BEGIN CERTIFICATE-----\r\n' + new Buffer(msg.substring(4, 4 + certlen), 'binary').toString('base64') + '\r\n-----END CERTIFICATE-----';
108 try { serverCert = obj.forge.pki.certificateFromAsn1(obj.forge.asn1.fromDer(msg.substring(4, 4 + certlen))); } catch (e) { }
109 if (serverCert == null) { obj.parent.parent.debug(1, 'OutPeer: Invalid server certificate.'); disconnect(); return; }
110 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, '$');
111 if (serverid !== obj.agentCertificateHashBase64) { obj.parent.parent.debug(1, 'OutPeer: Server hash mismatch.'); disconnect(); return; }
112
115 - // Server signature, verify it
116 - var md = obj.forge.md.sha384.create();
117 - md.update(obj.serverCertHash, 'binary');
118 - md.update(obj.nonce, 'binary');
119 - md.update(obj.servernonce, 'binary');
120 - if (serverCert.publicKey.verify(md.digest().bytes(), msg.substring(4 + certlen)) == false) { obj.parent.parent.debug(1, 'OutPeer: Server sign check failed.'); disconnect(); return; }
113 + // Server signature, verify it. This is the fast way, without using forge. (TODO: Use accelerator for this?)
114 + const verify = obj.parent.crypto.createVerify('SHA384');
115 + verify.end(new Buffer(obj.serverCertHash + obj.nonce + obj.servernonce, 'binary'));
116 + if (verify.verify(serverCertPem, new Buffer(msg.substring(4 + certlen), 'binary')) !== true) { obj.parent.parent.debug(1, 'OutPeer: Server sign check failed.'); disconnect(); return; }
117
118 // Connection is a success, clean up
119 delete obj.nonce;
@@ -128,14 +124,14 @@ module.exports.CreateMultiServer = function (parent, args) {
124 obj.parent.parent.debug(1, 'OutPeer ' + obj.serverid + ': Verified peer connection to ' + obj.url);
125
126 // Send information about our server to the peer
131 - 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 })); }
127 + 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 })); }
128 //if ((obj.connectionState == 15) && (obj.connectHandler != null)) { obj.connectHandler(1); }
129 break;
130 }
131 case 4: {
132 // Server confirmed authentication, we are allowed to send commands to the server
133 obj.connectionState |= 8;
138 - 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 })); }
134 + 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 })); }
135 //if ((obj.connectionState == 15) && (obj.connectHandler != null)) { obj.connectHandler(1); }
136 break;
137 }
@@ -258,15 +254,13 @@ module.exports.CreateMultiServer = function (parent, args) {
254
255 // Check that the server hash matches out own web certificate hash
256 if (obj.webCertificateHash != msg.substring(2, 50)) { obj.close(); return; }
261 -
262 - // Use our server private key to sign the ServerHash + PeerNonce + ServerNonce
263 - var md = obj.forge.md.sha384.create();
264 - md.update(msg.substring(2), 'binary');
265 - md.update(obj.nonce, 'binary');
257 obj.peernonce = msg.substring(50);
258
268 - // Send back our 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
259 + // Perform the hash signature using the server agent certificate
260 + obj.parent.parent.certificateOperations.acceleratorPerformSignature(0, msg.substring(2) + obj.nonce, function (signature) {
261 + // Send back our certificate + signature
262 + obj.send(obj.common.ShortToStr(2) + obj.common.ShortToStr(obj.agentCertificateAsn1.length) + obj.agentCertificateAsn1 + signature); // Command 2, certificate + signature
263 + });
264
265 // Check the peer server signature if we can
266 if (obj.unauthsign != null) {
@@ -275,22 +269,25 @@ module.exports.CreateMultiServer = function (parent, args) {
269 }
270 else if (cmd == 2) {
271 // Peer server certificate
278 - if ((msg.length < 4) || ((obj.receivedCommands & 2) != 0)) return;
272 + if ((msg.length < 4) || ((obj.receivedCommands & 2) != 0)) { obj.parent.parent.debug(1, 'InPeer: Invalid command 2.'); return; }
273 obj.receivedCommands += 2; // Peer server can't send the same command twice on the same connection ever. Block DOS attack path.
274
275 // Decode the certificate
276 var certlen = obj.common.ReadShort(msg, 2);
277 obj.unauth = {};
284 - obj.unauth.nodeCert = null;
285 - try { obj.unauth.nodeCert = obj.forge.pki.certificateFromAsn1(obj.forge.asn1.fromDer(msg.substring(4, 4 + certlen))); } catch (e) { return; }
286 - 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, '$');
278 + 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; }
279 + obj.unauth.nodeCertPem = '-----BEGIN CERTIFICATE-----\r\n' + new Buffer(msg.substring(4, 4 + certlen), 'binary').toString('base64') + '\r\n-----END CERTIFICATE-----';
280
281 // Check the peer server signature if we can
289 - if (obj.peernonce == null) { obj.unauthsign = msg.substring(4 + certlen); } else { if (processPeerSignature(msg.substring(4 + certlen)) == false) { obj.close(); return; } }
282 + if (obj.peernonce == null) {
283 + obj.unauthsign = msg.substring(4 + certlen);
284 + } else {
285 + if (processPeerSignature(msg.substring(4 + certlen)) == false) { obj.parent.parent.debug(1, 'InPeer: Invalid signature.'); obj.close(); return; }
286 + }
287 completePeerServerConnection();
288 }
289 else if (cmd == 3) {
293 - if ((msg.length < 56) || ((obj.receivedCommands & 4) != 0)) return;
290 + if ((msg.length < 56) || ((obj.receivedCommands & 4) != 0)) { obj.parent.parent.debug(1, 'InPeer: Invalid command 3.'); return; }
291 obj.receivedCommands += 4; // Peer server can't send the same command twice on the same connection ever. Block DOS attack path.
292 completePeerServerConnection();
293 }
@@ -306,25 +303,24 @@ module.exports.CreateMultiServer = function (parent, args) {
303
304 // Start authenticate the peer server by sending a auth nonce & server TLS cert hash.
305 // Send 384 bits SHA382 hash of TLS cert public key + 384 bits nonce
309 - obj.nonce = obj.forge.random.getBytesSync(48);
306 + obj.nonce = obj.crypto.randomBytes(48).toString('binary');
307 obj.send(obj.common.ShortToStr(1) + obj.webCertificateHash + obj.nonce); // Command 1, hash + nonce
308
309 // Once we get all the information about an peer server, run this to hook everything up to the server
310 function completePeerServerConnection() {
311 if (obj.authenticated != 1) return;
312 obj.send(obj.common.ShortToStr(4));
316 - 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 }));
313 + 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 }));
314 obj.authenticated = 2;
315 }
316
317 // Verify the peer server signature
318 function processPeerSignature(msg) {
322 - var md = obj.forge.md.sha384.create(); // TODO: Switch this to SHA384 on node instead of forge.
323 - md.update(obj.parent.parent.webserver.webCertificateHash, 'binary');
324 - md.update(obj.nonce, 'binary');
325 - md.update(obj.peernonce, 'binary');
326 - if (obj.unauth.nodeCert.publicKey.verify(md.digest().bytes(), msg) == false) { return false; }
327 - if (obj.unauth.nodeid !== obj.agentCertificateHashBase64) { return false; }
319 + // Verify the signature. This is the fast way, without using forge.
320 + const verify = obj.parent.crypto.createVerify('SHA384');
321 + verify.end(new Buffer(obj.parent.parent.webserver.webCertificateHash + obj.nonce + obj.peernonce, 'binary'));
322 + if (verify.verify(obj.unauth.nodeCertPem, new Buffer(msg, 'binary')) !== true) { console.log('Peer sign fail 1'); return false; }
323 + if (obj.unauth.nodeid !== obj.agentCertificateHashBase64) { console.log('Peer sign fail 2'); return false; }
324
325 // Connection is a success, clean up
326 obj.nodeid = obj.unauth.nodeid;
@@ -333,6 +329,7 @@ module.exports.CreateMultiServer = function (parent, args) {
329 delete obj.unauth;
330 if (obj.unauthsign) delete obj.unauthsign;
331 obj.authenticated = 1;
332 +
333 return true;
334 }
335
package.json
+1 -1
@@ -1,6 +1,6 @@
1 {
2 "name": "meshcentral",
3 - "version": "0.1.2-b",
3 + "version": "0.1.2-h",
4 "keywords": [
5 "Remote Management",
6 "Intel AMT",