amt-redir-mesh.js 30 KB

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