EverydayTech Platform - Developer Reference
Complete Source Code Documentation - All Applications
Loading...
Searching...
No Matches
amt-redir-mesh.js
Go to the documentation of this file.
1/*
2Copyright 2020-2021 Intel Corporation
3
4Licensed under the Apache License, Version 2.0 (the "License");
5you may not use this file except in compliance with the License.
6You may obtain a copy of the License at
7
8 http://www.apache.org/licenses/LICENSE-2.0
9
10Unless required by applicable law or agreed to in writing, software
11distributed under the License is distributed on an "AS IS" BASIS,
12WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13See the License for the specific language governing permissions and
14limitations under the License.
15
16@description Intel AMT redirection stack
17@author Ylian Saint-Hilaire
18@version v0.3.0
19*/
20
21/*jslint node: true */
22/*jshint node: true */
23/*jshint strict:false */
24/*jshint -W097 */
25/*jshint esversion: 6 */
26"use strict";
27
28// Construct a MeshServer object
29module.exports.CreateAmtRedirect = function (module, domain, user, webserver, meshcentral) {
30 var obj = {};
31 obj.m = module; // This is the inner module (Terminal or Desktop)
32 module.parent = obj;
33 obj.State = 0;
34 obj.net = require('net');
35 obj.tls = require('tls');
36 obj.crypto = require('crypto');
37 const constants = require('constants');
38 obj.socket = null;
39 obj.amtuser = null;
40 obj.amtpass = null;
41 obj.connectstate = 0;
42 obj.protocol = module.protocol; // 1 = SOL, 2 = KVM, 3 = IDER
43 obj.xtlsoptions = null;
44 obj.redirTrace = false;
45 obj.tls1only = 0; // TODO
46
47 obj.amtaccumulator = '';
48 obj.amtsequence = 1;
49 obj.amtkeepalivetimer = null;
50 obj.authuri = '/RedirectionService';
51
52 obj.onStateChanged = null;
53 obj.forwardclient = null;
54
55 // Mesh Rights
56 const MESHRIGHT_EDITMESH = 1;
57 const MESHRIGHT_MANAGEUSERS = 2;
58 const MESHRIGHT_MANAGECOMPUTERS = 4;
59 const MESHRIGHT_REMOTECONTROL = 8;
60 const MESHRIGHT_AGENTCONSOLE = 16;
61 const MESHRIGHT_SERVERFILES = 32;
62 const MESHRIGHT_WAKEDEVICE = 64;
63 const MESHRIGHT_SETNOTES = 128;
64
65 // Site rights
66 const SITERIGHT_SERVERBACKUP = 1;
67 const SITERIGHT_MANAGEUSERS = 2;
68 const SITERIGHT_SERVERRESTORE = 4;
69 const SITERIGHT_FILEACCESS = 8;
70 const SITERIGHT_SERVERUPDATE = 16;
71 const SITERIGHT_LOCKED = 32;
72
73 function Debug(lvl) {
74 if ((arguments.length < 2) || (meshcentral.debugLevel == null) || (lvl > meshcentral.debugLevel)) return;
75 var a = []; for (var i = 1; i < arguments.length; i++) { a.push(arguments[i]); } console.log(...a);
76 }
77
78 // Older NodeJS does not support the keyword "class", so we do without using this syntax
79 // TODO: Validate that it's the same as above and that it works.
80 function SerialTunnel(options) {
81 var obj = new require('stream').Duplex(options);
82 obj.forwardwrite = null;
83 obj.updateBuffer = function (chunk) { this.push(chunk); };
84 obj._write = function (chunk, encoding, callback) { if (obj.forwardwrite != null) { obj.forwardwrite(chunk); } else { console.err('Failed to fwd _write.'); } if (callback) callback(); }; // Pass data written to forward
85 obj._read = function (size) { }; // Push nothing, anything to read should be pushed from updateBuffer()
86 return obj;
87 }
88
89 obj.Start = function (nodeid) {
90 //console.log('Amt-Redir-Start', nodeid);
91 obj.connectstate = 0;
92 Debug(1, 'AMT redir for ' + user.name + ' to ' + nodeid + '.');
93 obj.xxStateChange(1);
94
95 // Fetch information about the target
96 meshcentral.db.Get(nodeid, function (err, docs) {
97 if (docs.length == 0) { console.log('ERR: Node not found'); obj.Stop(); return; }
98 var node = docs[0];
99 if (!node.intelamt) { console.log('ERR: Not AMT node'); obj.Stop(); return; }
100
101 obj.amtuser = node.intelamt.user;
102 obj.amtpass = node.intelamt.pass;
103
104 // Check if this user has permission to manage this computer
105 var meshlinks = user.links[node.meshid];
106 if ((!meshlinks) || (!meshlinks.rights) || ((meshlinks.rights & MESHRIGHT_REMOTECONTROL) == 0)) { console.log('ERR: Access denied (2)'); obj.Stop(); return; }
107
108 // Check what connectivity is available for this node
109 var state = meshcentral.GetConnectivityState(nodeid);
110 var conn = 0;
111 if (!state || state.connectivity == 0) { Debug(1, 'ERR: No routing possible (1)'); obj.Stop(); return; } else { conn = state.connectivity; }
112
113 /*
114 // Check what server needs to handle this connection
115 if ((meshcentral.multiServer != null) && (cookie == null)) { // If a cookie is provided, don't allow the connection to jump again to a different server
116 var server = obj.parent.GetRoutingServerId(nodeid, 2); // Check for Intel CIRA connection
117 if (server != null) {
118 if (server.serverid != obj.parent.serverId) {
119 // Do local Intel CIRA routing using a different server
120 Debug(1, 'Route Intel AMT CIRA connection to peer server: ' + server.serverid);
121 obj.parent.multiServer.createPeerRelay(ws, req, server.serverid, user);
122 return;
123 }
124 } else {
125 server = obj.parent.GetRoutingServerId(nodeid, 4); // Check for local Intel AMT connection
126 if ((server != null) && (server.serverid != obj.parent.serverId)) {
127 // Do local Intel AMT routing using a different server
128 Debug(1, 'Route Intel AMT direct connection to peer server: ' + server.serverid);
129 obj.parent.multiServer.createPeerRelay(ws, req, server.serverid, user);
130 return;
131 }
132 }
133 }
134 */
135
136 // If Intel AMT CIRA connection is available, use it
137 var ciraconn = meshcentral.mpsserver.GetConnectionToNode(nodeid, null, true); // Request an OOB connection
138 if (ciraconn != null) {
139 Debug(1, 'Opening Intel AMT CIRA transport connection to ' + nodeid + '.');
140
141 // Compute target port, look at the CIRA port mappings, if non-TLS is allowed, use that, if not use TLS
142 var port = 16995;
143 if (ciraconn.tag.boundPorts.indexOf(16994) >= 0) port = 16994; // RELEASE: Always use non-TLS mode if available within CIRA
144
145 // Setup a new CIRA channel
146 if ((port == 16993) || (port == 16995)) {
147 // Perform TLS - ( TODO: THIS IS BROKEN on Intel AMT v7 but works on v10, Not sure why. Well, could be broken TLS 1.0 in firmware )
148 var ser = new SerialTunnel();
149 var chnl = meshcentral.mpsserver.SetupChannel(ciraconn, port);
150
151 // let's chain up the TLSSocket <-> SerialTunnel <-> CIRA APF (chnl)
152 // Anything that needs to be forwarded by SerialTunnel will be encapsulated by chnl write
153 ser.forwardwrite = function (msg) {
154 // TLS ---> CIRA
155 chnl.write(msg.toString('binary'));
156 };
157
158 // When APF tunnel return something, update SerialTunnel buffer
159 chnl.onData = function (ciraconn, data) {
160 // CIRA ---> TLS
161 Debug(3, 'Relay TLS CIRA data', data.length);
162 if (data.length > 0) { try { ser.updateBuffer(Buffer.from(data, 'binary')); } catch (e) { } }
163 };
164
165 // Handle CIRA tunnel state change
166 chnl.onStateChange = function (ciraconn, state) {
167 Debug(2, 'Relay TLS CIRA state change', state);
168 if (state == 0) { try { ws.close(); } catch (e) { } }
169 };
170
171 // TLSSocket to encapsulate TLS communication, which then tunneled via SerialTunnel an then wrapped through CIRA APF
172 const TLSSocket = require('tls').TLSSocket;
173 const tlsoptions = { ciphers: 'RSA+AES:!aNULL:!MD5:!DSS', secureOptions: constants.SSL_OP_NO_SSLv2 | constants.SSL_OP_NO_SSLv3 | constants.SSL_OP_NO_COMPRESSION | constants.SSL_OP_CIPHER_SERVER_PREFERENCE | constants.SSL_OP_ALLOW_UNSAFE_LEGACY_RENEGOTIATION, rejectUnauthorized: false };
174 if (obj.tls1only == 1) {
175 tlsoptions.secureProtocol = 'TLSv1_method';
176 } else {
177 tlsoptions.minVersion = 'TLSv1';
178 }
179 const tlsock = new TLSSocket(ser, tlsoptions);
180 tlsock.on('error', function (err) { Debug(1, "CIRA TLS Connection Error ", err); });
181 tlsock.on('secureConnect', function () { Debug(2, "CIRA Secure TLS Connection"); ws._socket.resume(); });
182
183 // Decrypted tunnel from TLS communcation to be forwarded to websocket
184 tlsock.on('data', function (data) {
185 // AMT/TLS ---> WS
186 try {
187 data = data.toString('binary');
188 //ws.send(Buffer.from(data, 'binary'));
189 ws.send(data);
190 } catch (e) { }
191 });
192
193 // If TLS is on, forward it through TLSSocket
194 obj.forwardclient = tlsock;
195 obj.forwardclient.xtls = 1;
196 } else {
197 // Without TLS
198 obj.forwardclient = meshcentral.mpsserver.SetupChannel(ciraconn, port);
199 obj.forwardclient.xtls = 0;
200 }
201
202 obj.forwardclient.onStateChange = function (ciraconn, state) {
203 Debug(2, 'Intel AMT CIRA relay state change', state);
204 if (state == 0) { try { obj.Stop(); } catch (e) { } }
205 else if (state == 2) { obj.xxOnSocketConnected(); }
206 };
207
208 obj.forwardclient.onData = function (ciraconn, data) {
209 Debug(4, 'Intel AMT CIRA data', data.length);
210 if (data.length > 0) { obj.xxOnSocketData(data); } // TODO: Add TLS support
211 };
212
213 obj.forwardclient.onSendOk = function (ciraconn) {
214 // TODO: Flow control? (Dont' really need it with AMT, but would be nice)
215 Debug(4, 'Intel AMT CIRA sendok');
216 };
217
218 return;
219 }
220
221 // If Intel AMT direct connection is possible, option a direct socket
222 if ((conn & 4) != 0) { // We got a new web socket connection, initiate a TCP connection to the target Intel AMT host/port.
223 Debug(1, 'Opening Intel AMT transport connection to ' + nodeid + '.');
224
225 // Compute target port
226 var port = 16994;
227 if (node.intelamt.tls > 0) port = 16995; // This is a direct connection, use TLS when possible
228
229 if (node.intelamt.tls != 1) {
230 // If this is TCP (without TLS) set a normal TCP socket
231 obj.forwardclient = new obj.net.Socket();
232 obj.forwardclient.setEncoding('binary');
233 } else {
234 // If TLS is going to be used, setup a TLS socket
235 var tlsoptions = { ciphers: 'RSA+AES:!aNULL:!MD5:!DSS', secureOptions: constants.SSL_OP_NO_SSLv2 | constants.SSL_OP_NO_SSLv3 | constants.SSL_OP_NO_COMPRESSION | constants.SSL_OP_CIPHER_SERVER_PREFERENCE | constants.SSL_OP_ALLOW_UNSAFE_LEGACY_RENEGOTIATION, rejectUnauthorized: false };
236 if (obj.tls1only == 1) {
237 tlsoptions.secureProtocol = 'TLSv1_method';
238 } else {
239 tlsoptions.minVersion = 'TLSv1';
240 }
241 obj.forwardclient = obj.tls.connect(port, node.host, tlsoptions, function () {
242 // The TLS connection method is the same as TCP, but located a bit differently.
243 Debug(2, 'TLS Intel AMT transport connected to ' + node.host + ':' + port + '.');
244 obj.xxOnSocketConnected();
245 });
246 obj.forwardclient.setEncoding('binary');
247 }
248
249 // When we receive data on the TCP connection, forward it back into the web socket connection.
250 obj.forwardclient.on('data', function (data) {
251 //if (obj.parent.debugLevel >= 1) { // DEBUG
252 Debug(1, 'Intel AMT transport data from ' + node.host + ', ' + data.length + ' bytes.');
253 Debug(4, ' ' + Buffer.from(data, 'binary').toString('hex'));
254 //if (obj.parent.debugLevel >= 4) { Debug(4, ' ' + Buffer.from(data, 'binary').toString('hex')); }
255 //}
256 obj.xxOnSocketData(data);
257 });
258
259 // If the TCP connection closes, disconnect the associated web socket.
260 obj.forwardclient.on('close', function () {
261 Debug(1, 'Intel AMT transport relay disconnected from ' + node.host + '.');
262 obj.Stop();
263 });
264
265 // If the TCP connection causes an error, disconnect the associated web socket.
266 obj.forwardclient.on('error', function (err) {
267 Debug(1, 'Intel AMT transport relay error from ' + node.host + ': ' + err.errno);
268 obj.Stop();
269 });
270
271 if (node.intelamt.tls == 0) {
272 // A TCP connection to Intel AMT just connected, start forwarding.
273 obj.forwardclient.connect(port, node.host, function () {
274 Debug(1, 'Intel AMT transport connected to ' + node.host + ':' + port + '.');
275 obj.xxOnSocketConnected();
276 });
277 }
278
279 return;
280 }
281
282 });
283 }
284
285 // Get the certificate of Intel AMT
286 obj.getPeerCertificate = function () { if (obj.xtls == true) { return obj.socket.getPeerCertificate(); } return null; }
287
288 obj.xxOnSocketConnected = function () {
289 //console.log('xxOnSocketConnected');
290 if (!obj.xtlsoptions || !obj.xtlsoptions.meshServerConnect) {
291 if (obj.xtls == true) {
292 obj.xtlsCertificate = obj.socket.getPeerCertificate();
293 if ((obj.xtlsFingerprint != 0) && (obj.xtlsCertificate.fingerprint.split(':').join('').toLowerCase() != obj.xtlsFingerprint)) { obj.Stop(); return; }
294 }
295 }
296
297 if (obj.redirTrace) { console.log("REDIR-CONNECTED"); }
298 //obj.Debug("Socket Connected");
299 obj.xxStateChange(2);
300 if (obj.protocol == 1) obj.xxSend(obj.RedirectStartSol); // TODO: Put these strings in higher level module to tighten code
301 if (obj.protocol == 2) obj.xxSend(obj.RedirectStartKvm); // Don't need these is the feature if not compiled-in.
302 if (obj.protocol == 3) obj.xxSend(obj.RedirectStartIder);
303 }
304
305 obj.xxOnSocketData = function (data) {
306 if (!data || obj.connectstate == -1) return;
307 if (obj.redirTrace) { console.log("REDIR-RECV(" + data.length + "): " + webserver.common.rstr2hex(data)); }
308 //obj.Debug("Recv(" + data.length + "): " + webserver.common.rstr2hex(data));
309 if ((obj.protocol > 1) && (obj.connectstate == 1)) { return obj.m.ProcessData(data); } // KVM traffic, forward it directly.
310 obj.amtaccumulator += data;
311 //obj.Debug("Recv(" + obj.amtaccumulator.length + "): " + webserver.common.rstr2hex(obj.amtaccumulator));
312 while (obj.amtaccumulator.length >= 1) {
313 var cmdsize = 0;
314 switch (obj.amtaccumulator.charCodeAt(0)) {
315 case 0x11: // StartRedirectionSessionReply (17)
316 if (obj.amtaccumulator.length < 4) return;
317 var statuscode = obj.amtaccumulator.charCodeAt(1);
318 switch (statuscode) {
319 case 0: // STATUS_SUCCESS
320 if (obj.amtaccumulator.length < 13) return;
321 var oemlen = obj.amtaccumulator.charCodeAt(12);
322 if (obj.amtaccumulator.length < 13 + oemlen) return;
323 obj.xxSend(String.fromCharCode(0x13, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00)); // Query authentication support
324 cmdsize = (13 + oemlen);
325 break;
326 default:
327 obj.Stop();
328 break;
329 }
330 break;
331 case 0x14: // AuthenticateSessionReply (20)
332 if (obj.amtaccumulator.length < 9) return;
333 var authDataLen = webserver.common.ReadIntX(obj.amtaccumulator, 5);
334 if (obj.amtaccumulator.length < 9 + authDataLen) return;
335 var status = obj.amtaccumulator.charCodeAt(1);
336 var authType = obj.amtaccumulator.charCodeAt(4);
337 var authData = [];
338 for (var i = 0; i < authDataLen; i++) { authData.push(obj.amtaccumulator.charCodeAt(9 + i)); }
339 var authDataBuf = obj.amtaccumulator.substring(9, 9 + authDataLen);
340 cmdsize = 9 + authDataLen;
341 if (authType == 0) {
342 /*
343 // This is Kerberos code, not supported in MeshCentral.
344 if (obj.amtuser == '*') {
345 if (authData.indexOf(2) >= 0) {
346 // Kerberos Auth
347 var ticket;
348 if (kerberos && kerberos != null) {
349 var ticketReturn = kerberos.getTicket('HTTP' + ((obj.tls == 1)?'S':'') + '/' + ((obj.amtpass == '') ? (obj.host + ':' + obj.port) : obj.amtpass));
350 if (ticketReturn.returnCode == 0 || ticketReturn.returnCode == 0x90312) {
351 ticket = ticketReturn.ticket;
352 if (process.platform.indexOf('win') >= 0) {
353 // Clear kerberos tickets on both 32 and 64bit Windows platforms
354 try { require('child_process').exec('%windir%\\system32\\klist purge', function (error, stdout, stderr) { if (error) { require('child_process').exec('%windir%\\sysnative\\klist purge', function (error, stdout, stderr) { if (error) { console.error('Unable to purge kerberos tickets'); } }); } }); } catch (e) { console.log(e); }
355 }
356 } else {
357 console.error('Unexpected Kerberos error code: ' + ticketReturn.returnCode);
358 }
359 }
360 if (ticket) {
361 obj.xxSend(String.fromCharCode(0x13, 0x00, 0x00, 0x00, 0x02) + webserver.common.IntToStrX(ticket.length) + ticket);
362 } else {
363 obj.Stop();
364 }
365 }
366 else obj.Stop();
367 } else {
368 */
369 // Query
370 if (authData.indexOf(4) >= 0) {
371 // Good Digest Auth (With cnonce and all)
372 obj.xxSend(String.fromCharCode(0x13, 0x00, 0x00, 0x00, 0x04) + webserver.common.IntToStrX(obj.amtuser.length + obj.authuri.length + 8) + String.fromCharCode(obj.amtuser.length) + obj.amtuser + String.fromCharCode(0x00, 0x00) + String.fromCharCode(obj.authuri.length) + obj.authuri + String.fromCharCode(0x00, 0x00, 0x00, 0x00));
373 }
374 /*
375 else if (authData.indexOf(3) >= 0) {
376 // Bad Digest Auth (Not sure why this is supported, cnonce is not used!)
377 obj.xxSend(String.fromCharCode(0x13, 0x00, 0x00, 0x00, 0x03) + webserver.common.IntToStrX(obj.amtuser.length + obj.authuri.length + 7) + String.fromCharCode(obj.amtuser.length) + obj.amtuser + String.fromCharCode(0x00, 0x00) + String.fromCharCode(obj.authuri.length) + obj.authuri + String.fromCharCode(0x00, 0x00, 0x00));
378 }
379 else if (authData.indexOf(1) >= 0) {
380 // Basic Auth (Probably a good idea to not support this unless this is an old version of Intel AMT)
381 obj.xxSend(String.fromCharCode(0x13, 0x00, 0x00, 0x00, 0x01) + webserver.common.IntToStrX(obj.amtuser.length + obj.amtpass.length + 2) + String.fromCharCode(obj.amtuser.length) + obj.amtuser + String.fromCharCode(obj.amtpass.length) + obj.amtpass);
382 }
383 */
384 else obj.Stop();
385 /*
386 }
387 */
388 }
389 else if ((authType == 3 || authType == 4) && status == 1) {
390 var curptr = 0;
391
392 // Realm
393 var realmlen = authDataBuf.charCodeAt(curptr);
394 var realm = authDataBuf.substring(curptr + 1, curptr + 1 + realmlen);
395 curptr += (realmlen + 1);
396
397 // Nonce
398 var noncelen = authDataBuf.charCodeAt(curptr);
399 var nonce = authDataBuf.substring(curptr + 1, curptr + 1 + noncelen);
400 curptr += (noncelen + 1);
401
402 // QOP
403 var qoplen = 0;
404 var qop = null;
405 var cnonce = obj.xxRandomValueHex(32);
406 var snc = '00000002';
407 var extra = '';
408 if (authType == 4) {
409 qoplen = authDataBuf.charCodeAt(curptr);
410 qop = authDataBuf.substring(curptr + 1, curptr + 1 + qoplen);
411 curptr += (qoplen + 1);
412 extra = snc + ":" + cnonce + ":" + qop + ":";
413 }
414 var digest = hex_md5(hex_md5(obj.amtuser + ":" + realm + ":" + obj.amtpass) + ":" + nonce + ":" + extra + hex_md5("POST:" + obj.authuri));
415
416 var totallen = obj.amtuser.length + realm.length + nonce.length + obj.authuri.length + cnonce.length + snc.length + digest.length + 7;
417 if (authType == 4) totallen += (qop.length + 1);
418 var buf = String.fromCharCode(0x13, 0x00, 0x00, 0x00, authType) + webserver.common.IntToStrX(totallen) + String.fromCharCode(obj.amtuser.length) + obj.amtuser + String.fromCharCode(realm.length) + realm + String.fromCharCode(nonce.length) + nonce + String.fromCharCode(obj.authuri.length) + obj.authuri + String.fromCharCode(cnonce.length) + cnonce + String.fromCharCode(snc.length) + snc + String.fromCharCode(digest.length) + digest;
419 if (authType == 4) buf += (String.fromCharCode(qop.length) + qop);
420 obj.xxSend(buf);
421 }
422 else if (status == 0) { // Success
423 /*
424 if (obj.protocol == 1) {
425 // Serial-over-LAN: Send Intel AMT serial settings...
426 var MaxTxBuffer = 10000;
427 var TxTimeout = 100;
428 var TxOverflowTimeout = 0;
429 var RxTimeout = 10000;
430 var RxFlushTimeout = 100;
431 var Heartbeat = 0;//5000;
432 obj.xxSend(String.fromCharCode(0x20, 0x00, 0x00, 0x00) + ToIntStr(obj.amtsequence++) + ToShortStr(MaxTxBuffer) + ToShortStr(TxTimeout) + ToShortStr(TxOverflowTimeout) + ToShortStr(RxTimeout) + ToShortStr(RxFlushTimeout) + ToShortStr(Heartbeat) + ToIntStr(0));
433 }
434 if (obj.protocol == 2) {
435 // Remote Desktop: Send traffic directly...
436 obj.xxSend(String.fromCharCode(0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00));
437 }
438 */
439 if (obj.protocol == 3) { // IDE-R
440 obj.connectstate = 1;
441 obj.m.Start();
442 if (obj.amtaccumulator.length > cmdsize) { obj.m.ProcessData(obj.amtaccumulator.substring(cmdsize)); }
443 cmdsize = obj.amtaccumulator.length;
444 }
445 } else obj.Stop();
446 break;
447 case 0x21: // Response to settings (33)
448 if (obj.amtaccumulator.length < 23) break;
449 cmdsize = 23;
450 obj.xxSend(String.fromCharCode(0x27, 0x00, 0x00, 0x00) + ToIntStr(obj.amtsequence++) + String.fromCharCode(0x00, 0x00, 0x1B, 0x00, 0x00, 0x00));
451 if (obj.protocol == 1) { obj.amtkeepalivetimer = setInterval(obj.xxSendAmtKeepAlive, 2000); }
452 obj.connectstate = 1;
453 obj.xxStateChange(3);
454 break;
455 case 0x29: // Serial Settings (41)
456 if (obj.amtaccumulator.length < 10) break;
457 cmdsize = 10;
458 break;
459 case 0x2A: // Incoming display data (42)
460 if (obj.amtaccumulator.length < 10) break;
461 var cs = (10 + ((obj.amtaccumulator.charCodeAt(9) & 0xFF) << 8) + (obj.amtaccumulator.charCodeAt(8) & 0xFF));
462 if (obj.amtaccumulator.length < cs) break;
463 obj.m.ProcessData(obj.amtaccumulator.substring(10, cs));
464 cmdsize = cs;
465 break;
466 case 0x2B: // Keep alive message (43)
467 if (obj.amtaccumulator.length < 8) break;
468 cmdsize = 8;
469 break;
470 case 0x41:
471 if (obj.amtaccumulator.length < 8) break;
472 obj.connectstate = 1;
473 obj.m.Start();
474 // KVM traffic, forward rest of accumulator directly.
475 if (obj.amtaccumulator.length > 8) { obj.m.ProcessData(obj.amtaccumulator.substring(8)); }
476 cmdsize = obj.amtaccumulator.length;
477 break;
478 default:
479 console.log("Unknown Intel AMT command: " + obj.amtaccumulator.charCodeAt(0) + " acclen=" + obj.amtaccumulator.length);
480 obj.Stop();
481 return;
482 }
483 if (cmdsize == 0) return;
484 obj.amtaccumulator = obj.amtaccumulator.substring(cmdsize);
485 }
486 }
487
488 obj.xxSend = function (x) {
489 if (typeof x == 'string') {
490 if (obj.redirTrace) { console.log("REDIR-SEND(" + x.length + "): " + Buffer.from(x, 'binary').toString('hex'), typeof x); }
491 //obj.Debug("Send(" + x.length + "): " + webserver.common.rstr2hex(x));
492 //obj.forwardclient.write(x); // FIXES CIRA
493 obj.forwardclient.write(Buffer.from(x, 'binary'));
494 } else {
495 if (obj.redirTrace) { console.log("REDIR-SEND(" + x.length + "): " + x.toString('hex'), typeof x); }
496 //obj.Debug("Send(" + x.length + "): " + webserver.common.rstr2hex(x));
497 //obj.forwardclient.write(x); // FIXES CIRA
498 obj.forwardclient.write(x);
499 }
500 }
501
502 obj.Send = function (x) {
503 if (obj.forwardclient == null || obj.connectstate != 1) return;
504 if (obj.protocol == 1) { obj.xxSend(String.fromCharCode(0x28, 0x00, 0x00, 0x00) + ToIntStr(obj.amtsequence++) + ToShortStr(x.length) + x); } else { obj.xxSend(x); }
505 }
506
507 obj.xxSendAmtKeepAlive = function () {
508 if (obj.forwardclient == null) return;
509 obj.xxSend(String.fromCharCode(0x2B, 0x00, 0x00, 0x00) + ToIntStr(obj.amtsequence++));
510 }
511
512 obj.xxRandomValueHex = function(len) { return obj.crypto.randomBytes(Math.ceil(len / 2)).toString('hex').slice(0, len); }
513
514 obj.xxOnSocketClosed = function () {
515 if (obj.redirTrace) { console.log('REDIR-CLOSED'); }
516 //obj.Debug("Socket Closed");
517 obj.Stop();
518 }
519
520 obj.xxStateChange = function(newstate) {
521 if (obj.State == newstate) return;
522 obj.State = newstate;
523 obj.m.xxStateChange(obj.State);
524 if (obj.onStateChanged != null) obj.onStateChanged(obj, obj.State);
525 }
526
527 obj.Stop = function () {
528 if (obj.redirTrace) { console.log('REDIR-CLOSED'); }
529 //obj.Debug("Socket Stopped");
530 obj.xxStateChange(0);
531 obj.connectstate = -1;
532 obj.amtaccumulator = '';
533 if (obj.forwardclient != null) { try { obj.forwardclient.destroy(); } catch (ex) { } delete obj.forwardclient; }
534 if (obj.amtkeepalivetimer != null) { clearInterval(obj.amtkeepalivetimer); delete obj.amtkeepalivetimer; }
535 }
536
537 obj.RedirectStartSol = String.fromCharCode(0x10, 0x00, 0x00, 0x00, 0x53, 0x4F, 0x4C, 0x20);
538 obj.RedirectStartKvm = String.fromCharCode(0x10, 0x01, 0x00, 0x00, 0x4b, 0x56, 0x4d, 0x52);
539 obj.RedirectStartIder = String.fromCharCode(0x10, 0x00, 0x00, 0x00, 0x49, 0x44, 0x45, 0x52);
540
541 function hex_md5(str) { return meshcentral.certificateOperations.forge.md.md5.create().update(str).digest().toHex(); }
542
543 return obj;
544}
545
546function ToIntStr(v) { return String.fromCharCode((v & 0xFF), ((v >> 8) & 0xFF), ((v >> 16) & 0xFF), ((v >> 24) & 0xFF)); }
547function ToShortStr(v) { return String.fromCharCode((v & 0xFF), ((v >> 8) & 0xFF)); }