Added server-side IDER over CIRA support.

This commit is contained in:
Ylian Saint-Hilaire 2019-04-25 15:13:32 -07:00
parent 6f68741cb8
commit bfd7b8ba41
6 changed files with 922 additions and 923 deletions

View File

@ -108,6 +108,7 @@ module.exports.CreateAmtRemoteIder = function (webserver, meshcentral) {
obj.bytesFromAmt = 0;
obj.inSequence = 0;
obj.outSequence = 0;
g_readQueue = [];
// Send first command, OPEN_SESSION
obj.SendCommand(0x40, webserver.common.ShortToStrX(obj.rx_timeout) + webserver.common.ShortToStrX(obj.tx_timeout) + webserver.common.ShortToStrX(obj.heartbeat) + webserver.common.IntToStrX(obj.version));
@ -587,8 +588,8 @@ module.exports.CreateAmtRemoteIder = function (webserver, meshcentral) {
if (obj.sectorStats) { obj.sectorStats(1, (dev == 0xA0) ? 0 : 1, mediaBlocks, lba, len); }
if (dev == 0xA0) { lba <<= 9; len <<= 9; } else { lba <<= 11; len <<= 11; }
if (g_media !== null) {
console.log('IDERERROR: Read while performing read');
obj.Stop();
// Queue read operation
g_readQueue.push({ media: media, dev: dev, lba: lba, len: len, fr: featureRegister });
} else {
// obj.iderinfo.readbfr // TODO: MaxRead
g_media = media;
@ -600,7 +601,7 @@ module.exports.CreateAmtRemoteIder = function (webserver, meshcentral) {
}
}
var g_dev, g_lba, g_len, g_media = null, g_reset = false;
var g_readQueue = [], g_dev, g_lba, g_len, g_media = null, g_reset = false;
function sendDiskDataEx(featureRegister) {
var len = g_len, lba = g_lba;
if (g_len > obj.iderinfo.readbfr) { len = obj.iderinfo.readbfr; }
@ -613,7 +614,8 @@ module.exports.CreateAmtRemoteIder = function (webserver, meshcentral) {
sendDiskDataEx(featureRegister);
} else {
g_media = null;
if (g_reset) { obj.SendCommand(0x47); g_reset = false; } // Send ResetOccuredResponse
if (g_reset) { obj.SendCommand(0x47); g_readQueue = []; g_reset = false; } // Send ResetOccuredResponse
else if (g_readQueue.length > 0) { var op = g_readQueue.shift(); g_media = op.media; g_dev = op.dev; g_lba = op.lba; g_len = op.len; sendDiskDataEx(op.fr); } // Un-queue read operation
}
});
}

View File

@ -79,8 +79,6 @@ module.exports.CreateAmtIderSession = function (parent, db, ws, req, args, domai
if (results[i].toLowerCase().endsWith('.img')) { floppyImages.push(results[i].substring(userPath.length + 1)); }
else if (results[i].toLowerCase().endsWith('.iso')) { cdromImages.push(results[i].substring(userPath.length + 1)); }
}
//console.log(floppyImages, cdromImages);
var xx, sel = true, html = "<div style='margin:10px 5px 10px 5px'>Select disk images & start type.</div>";
// Floppy image selection

View File

@ -13,7 +13,7 @@ module.exports.CreateAmtRedirect = function (module, domain, user, webserver, me
obj.net = require('net');
obj.tls = require('tls');
obj.crypto = require('crypto');
obj.constants = require('constants');
const constants = require('constants');
obj.socket = null;
obj.amtuser = null;
obj.amtpass = null;
@ -21,6 +21,7 @@ module.exports.CreateAmtRedirect = function (module, domain, user, webserver, me
obj.protocol = module.protocol; // 1 = SOL, 2 = KVM, 3 = IDER
obj.xtlsoptions = null;
obj.redirTrace = false;
obj.tls1only = 0; // TODO
obj.amtaccumulator = "";
obj.amtsequence = 1;
@ -53,6 +54,17 @@ module.exports.CreateAmtRedirect = function (module, domain, user, webserver, me
var a = []; for (var i = 1; i < arguments.length; i++) { a.push(arguments[i]); } console.log(...a);
}
// Older NodeJS does not support the keyword "class", so we do without using this syntax
// TODO: Validate that it's the same as above and that it works.
function SerialTunnel(options) {
var obj = new require('stream').Duplex(options);
obj.forwardwrite = null;
obj.updateBuffer = function (chunk) { this.push(chunk); };
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
obj._read = function (size) { }; // Push nothing, anything to read should be pushed from updateBuffer()
return obj;
}
obj.Start = function (nodeid) {
//console.log('Amt-Redir-Start', nodeid);
obj.connectstate = 0;
@ -106,18 +118,15 @@ module.exports.CreateAmtRedirect = function (module, domain, user, webserver, me
var ciraconn = meshcentral.mpsserver.ciraConnections[nodeid];
/*
// Compute target port, look at the CIRA port mappings, if non-TLS is allowed, use that, if not use TLS
var port = 16993;
//if (node.intelamt.tls == 0) port = 16992; // DEBUG: Allow TLS flag to set TLS mode within CIRA
if (ciraconn.tag.boundPorts.indexOf(16992) >= 0) port = 16992; // RELEASE: Always use non-TLS mode if available within CIRA
if (req.query.p == 2) port += 2;
var port = 16995;
if (ciraconn.tag.boundPorts.indexOf(16994) >= 0) port = 16994; // RELEASE: Always use non-TLS mode if available within CIRA
// Setup a new CIRA channel
if ((port == 16993) || (port == 16995)) {
// 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 )
var ser = new SerialTunnel();
var chnl = parent.mpsserver.SetupCiraChannel(ciraconn, port);
var chnl = meshcentral.mpsserver.SetupCiraChannel(ciraconn, port);
// let's chain up the TLSSocket <-> SerialTunnel <-> CIRA APF (chnl)
// Anything that needs to be forwarded by SerialTunnel will be encapsulated by chnl write
@ -141,7 +150,7 @@ module.exports.CreateAmtRedirect = function (module, domain, user, webserver, me
// TLSSocket to encapsulate TLS communication, which then tunneled via SerialTunnel an then wrapped through CIRA APF
const TLSSocket = require('tls').TLSSocket;
const tlsoptions = { secureProtocol: ((req.query.tls1only == 1) ? 'TLSv1_method' : 'SSLv23_method'), 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, rejectUnauthorized: false };
const tlsoptions = { secureProtocol: ((obj.tls1only == 1) ? 'TLSv1_method' : 'SSLv23_method'), 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, rejectUnauthorized: false };
const tlsock = new TLSSocket(ser, tlsoptions);
tlsock.on('error', function (err) { Debug(1, "CIRA TLS Connection Error ", err); });
tlsock.on('secureConnect', function () { Debug(2, "CIRA Secure TLS Connection"); ws._socket.resume(); });
@ -151,73 +160,36 @@ module.exports.CreateAmtRedirect = function (module, domain, user, webserver, me
// AMT/TLS ---> WS
try {
data = data.toString('binary');
if (ws.interceptor) { data = ws.interceptor.processAmtData(data); } // Run data thru interceptor
//ws.send(Buffer.from(data, 'binary'));
ws.send(data);
} catch (e) { }
});
// If TLS is on, forward it through TLSSocket
ws.forwardclient = tlsock;
ws.forwardclient.xtls = 1;
obj.forwardclient = tlsock;
obj.forwardclient.xtls = 1;
} else {
// Without TLS
ws.forwardclient = parent.mpsserver.SetupCiraChannel(ciraconn, port);
ws.forwardclient.xtls = 0;
ws._socket.resume();
obj.forwardclient = meshcentral.mpsserver.SetupCiraChannel(ciraconn, port);
obj.forwardclient.xtls = 0;
}
// When data is received from the web socket, forward the data into the associated CIRA cahnnel.
// If the CIRA connection is pending, the CIRA channel has built-in buffering, so we are ok sending anyway.
ws.on('message', function (msg) {
// WS ---> AMT/TLS
msg = msg.toString('binary');
if (ws.interceptor) { msg = ws.interceptor.processBrowserData(msg); } // Run data thru interceptor
if (ws.forwardclient.xtls == 1) { ws.forwardclient.write(Buffer.from(msg, 'binary')); } else { ws.forwardclient.write(msg); }
});
// If error, close the associated TCP connection.
ws.on('error', function (err) {
console.log('CIRA server websocket error from ' + ws._socket.remoteAddress + ', ' + err.toString().split('\r')[0] + '.');
Debug(1, 'Websocket relay closed on error.');
if (ws.forwardclient && ws.forwardclient.close) { ws.forwardclient.close(); } // TODO: If TLS is used, we need to close the socket that is wrapped by TLS
});
// If the web socket is closed, close the associated TCP connection.
ws.on('close', function (req) {
Debug(1, 'Websocket relay closed.');
if (ws.forwardclient && ws.forwardclient.close) { ws.forwardclient.close(); } // TODO: If TLS is used, we need to close the socket that is wrapped by TLS
});
ws.forwardclient.onStateChange = function (ciraconn, state) {
Debug(2, 'Relay CIRA state change', state);
if (state == 0) { try { ws.close(); } catch (e) { } }
obj.forwardclient.onStateChange = function (ciraconn, state) {
Debug(2, 'Intel AMT CIRA relay state change', state);
if (state == 0) { try { obj.Stop(); } catch (e) { } }
else if (state == 2) { obj.xxOnSocketConnected(); }
};
ws.forwardclient.onData = function (ciraconn, data) {
Debug(4, 'Relay CIRA data', data.length);
if (ws.interceptor) { data = ws.interceptor.processAmtData(data); } // Run data thru interceptor
if (data.length > 0) { try { ws.send(Buffer.from(data, 'binary')); } catch (e) { } } // TODO: Add TLS support
obj.forwardclient.onData = function (ciraconn, data) {
Debug(4, 'Intel AMT CIRA data', data.length);
if (data.length > 0) { obj.xxOnSocketData(data); } // TODO: Add TLS support
};
ws.forwardclient.onSendOk = function (ciraconn) {
obj.forwardclient.onSendOk = function (ciraconn) {
// TODO: Flow control? (Dont' really need it with AMT, but would be nice)
//console.log('onSendOk');
Debug(4, 'Intel AMT CIRA sendok');
};
// Fetch Intel AMT credentials & Setup interceptor
if (req.query.p == 1) {
Debug(3, 'INTERCEPTOR1', { host: node.host, port: port, user: node.intelamt.user, pass: node.intelamt.pass });
ws.interceptor = obj.interceptor.CreateHttpInterceptor({ host: node.host, port: port, user: node.intelamt.user, pass: node.intelamt.pass });
ws.interceptor.blockAmtStorage = true;
}
else if (req.query.p == 2) {
Debug(3, 'INTERCEPTOR2', { user: node.intelamt.user, pass: node.intelamt.pass });
ws.interceptor = obj.interceptor.CreateRedirInterceptor({ user: node.intelamt.user, pass: node.intelamt.pass });
ws.interceptor.blockAmtStorage = true;
}
*/
return;
}
@ -225,32 +197,6 @@ module.exports.CreateAmtRedirect = function (module, domain, user, webserver, me
if ((conn & 4) != 0) { // We got a new web socket connection, initiate a TCP connection to the target Intel AMT host/port.
Debug(1, 'Opening Intel AMT transport connection to ' + nodeid + '.');
/*
// When data is received from the web socket, forward the data into the associated TCP connection.
ws.on('message', function (msg) {
if (obj.parent.debugLevel >= 1) { // DEBUG
Debug(1, 'TCP relay data to ' + node.host + ', ' + msg.length + ' bytes');
if (obj.parent.debugLevel >= 4) { Debug(4, ' ' + msg.toString('hex')); }
}
msg = msg.toString('binary');
if (ws.interceptor) { msg = ws.interceptor.processBrowserData(msg); } // Run data thru interceptor
ws.forwardclient.write(Buffer.from(msg, 'binary')); // Forward data to the associated TCP connection.
});
// If error, close the associated TCP connection.
ws.on('error', function (err) {
console.log('Error with relay web socket connection from ' + ws._socket.remoteAddress + ', ' + err.toString().split('\r')[0] + '.');
Debug(1, 'Error with relay web socket connection from ' + ws._socket.remoteAddress + '.');
if (ws.forwardclient) { try { ws.forwardclient.destroy(); } catch (e) { } }
});
// If the web socket is closed, close the associated TCP connection.
ws.on('close', function () {
Debug(1, 'Closing relay web socket connection to ' + nodeid + '.');
if (ws.forwardclient) { try { ws.forwardclient.destroy(); } catch (e) { } }
});
*/
// Compute target port
var port = 16994;
if (node.intelamt.tls > 0) port = 16995; // This is a direct connection, use TLS when possible
@ -261,7 +207,7 @@ module.exports.CreateAmtRedirect = function (module, domain, user, webserver, me
obj.forwardclient.setEncoding('binary');
} else {
// If TLS is going to be used, setup a TLS socket
var tlsoptions = { secureProtocol: ((req.query.tls1only == 1) ? 'TLSv1_method' : 'SSLv23_method'), 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, rejectUnauthorized: false };
var tlsoptions = { secureProtocol: ((obj.tls1only == 1) ? 'TLSv1_method' : 'SSLv23_method'), 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, rejectUnauthorized: false };
obj.forwardclient = obj.tls.connect(port, node.host, tlsoptions, function () {
// The TLS connection method is the same as TCP, but located a bit differently.
Debug(2, 'TLS Intel AMT transport connected to ' + node.host + ':' + port + '.');
@ -507,9 +453,9 @@ module.exports.CreateAmtRedirect = function (module, domain, user, webserver, me
}
obj.xxSend = function (x) {
if (obj.redirTrace) { console.log("REDIR-SEND(" + x.length + "): " + webserver.common.rstr2hex(x)); }
if (obj.redirTrace) { console.log("REDIR-SEND2(" + x.length + "): " + new Buffer(x, "binary").toString('hex')); }
//obj.Debug("Send(" + x.length + "): " + webserver.common.rstr2hex(x));
obj.forwardclient.write(new Buffer(x, "binary"));
obj.forwardclient.write(x);
}
obj.Send = function (x) {
@ -543,8 +489,8 @@ module.exports.CreateAmtRedirect = function (module, domain, user, webserver, me
obj.xxStateChange(0);
obj.connectstate = -1;
obj.amtaccumulator = "";
if (obj.forwardclient != null) { obj.forwardclient.destroy(); obj.forwardclient = null; }
if (obj.amtkeepalivetimer != null) { clearInterval(obj.amtkeepalivetimer); obj.amtkeepalivetimer = null; }
if (obj.forwardclient != null) { try { obj.forwardclient.close(); } catch (ex) { } delete obj.forwardclient; }
if (obj.amtkeepalivetimer != null) { clearInterval(obj.amtkeepalivetimer); delete obj.amtkeepalivetimer; }
}
obj.RedirectStartSol = String.fromCharCode(0x10, 0x00, 0x00, 0x00, 0x53, 0x4F, 0x4C, 0x20);

File diff suppressed because one or more lines are too long

View File

@ -4156,6 +4156,7 @@
y += "<option value=4>Windows (64bit)</option>";
y += "<option value=5>Linux x86 (32bit)</option>";
y += "<option value=6>Linux x86 (64bit)</option>";
y += "<option value=16>MacOS (64bit)</option>";
y += "<option value=25>Linux ARM, Raspberry Pi (32bit)</option>";
y += "</select>";
@ -4181,6 +4182,7 @@
if (os == 4) { osn = 'MeshCmd (Win64 executable)'; }
if (os == 5) { osn = 'MeshCmd (Linux x86, 32bit)'; }
if (os == 6) { osn = 'MeshCmd (Linux x86, 64bit)'; }
if (os == 16) { osn = 'MeshCmd (MacOS, 64bit)'; }
if (os == 25) { osn = 'MeshCmd (Linux ARM, 32bit)'; }
QH('meshcmddownloadid', osn);
}

View File

@ -1910,6 +1910,7 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) {
// WS ---> AMT/TLS
msg = msg.toString('binary');
if (ws.interceptor) { msg = ws.interceptor.processBrowserData(msg); } // Run data thru interceptor
//console.log('WS --> AMT', Buffer.from(msg, 'binary').toString('hex'));
if (ws.forwardclient.xtls == 1) { ws.forwardclient.write(Buffer.from(msg, 'binary')); } else { ws.forwardclient.write(msg); }
});
@ -1934,6 +1935,7 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) {
ws.forwardclient.onData = function (ciraconn, data) {
Debug(4, 'Relay CIRA data', data.length);
if (ws.interceptor) { data = ws.interceptor.processAmtData(data); } // Run data thru interceptor
//console.log('AMT --> WS', Buffer.from(data, 'binary').toString('hex'));
if (data.length > 0) { try { ws.send(Buffer.from(data, 'binary')); } catch (e) { } } // TODO: Add TLS support
};