Fixed WebRelay when using CIRA with TLS.

Ylian Saint-Hilaire committed Oct 14, 2020 at 13:13 UTC 04c2e36bff22ad25662cfba2d8416932221f1154
1 file changed +90 -79
webserver.js
+90 -79
@@ -3379,7 +3379,7 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) {
3379 if (ciraconn != null) {
3380 parent.debug('web', 'Opening relay CIRA channel connection to ' + req.query.host + '.');
3381
3382 - // TODO: If ciraconn is a relay connection, we can't detect the TLS state like this.
3382 + // TODO: If the CIRA connection is a relay or LMS connection, we can't detect the TLS state like this.
3383 // Compute target port, look at the CIRA port mappings, if non-TLS is allowed, use that, if not use TLS
3384 var port = 16993;
3385 //if (node.intelamt.tls == 0) port = 16992; // DEBUG: Allow TLS flag to set TLS mode within CIRA
@@ -3394,69 +3394,110 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) {
3394
3395 // Let's chain up the TLSSocket <-> SerialTunnel <-> CIRA APF (chnl)
3396 // Anything that needs to be forwarded by SerialTunnel will be encapsulated by chnl write
3397 - ser.forwardwrite = function (msg) {
3398 - // TLS ---> CIRA
3399 - chnl.write(msg.toString('binary'));
3400 - };
3397 + ser.forwardwrite = function (data) { if (data.length > 0) { chnl.write(data); } }; // TLS ---> CIRA
3398
3399 // When APF tunnel return something, update SerialTunnel buffer
3403 - chnl.onData = function (ciraconn, data) {
3404 - // CIRA ---> TLS
3405 - parent.debug('webrelay', 'Relay TLS CIRA data', data.length);
3406 - if (data.length > 0) { try { ser.updateBuffer(Buffer.from(data, 'binary')); } catch (ex) { console.log(ex); } }
3407 - };
3400 + chnl.onData = function (ciraconn, data) { if (data.length > 0) { try { ser.updateBuffer(data); } catch (ex) { console.log(ex); } } }; // CIRA ---> TLS
3401
3402 // Handle CIRA tunnel state change
3403 chnl.onStateChange = function (ciraconn, state) {
3404 parent.debug('webrelay', 'Relay TLS CIRA state change', state);
3405 if (state == 0) { try { ws.close(); } catch (e) { } }
3413 - };
3406 + if (state == 2) {
3407 + // TLSSocket to encapsulate TLS communication, which then tunneled via SerialTunnel an then wrapped through CIRA APF
3408 + const tlsoptions = { socket: ser, 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 };
3409 + if (req.query.tls1only == 1) { tlsoptions.secureProtocol = 'TLSv1_method'; }
3410 + var tlsock = obj.tls.connect(tlsoptions, function () { parent.debug('webrelay', "CIRA Secure TLS Connection"); ws._socket.resume(); });
3411 + tlsock.chnl = chnl;
3412 + tlsock.setEncoding('binary');
3413 + tlsock.on('error', function (err) { parent.debug('webrelay', "CIRA TLS Connection Error", err); });
3414 +
3415 + // Decrypted tunnel from TLS communcation to be forwarded to websocket
3416 + tlsock.on('data', function (data) {
3417 + // AMT/TLS ---> WS
3418 + if (ws.interceptor) { data = Buffer.from(ws.interceptor.processAmtData(data.toString('binary')), 'binary'); } // Run data thru interceptor
3419 + try { ws.send(data); } catch (ex) { }
3420 + });
3421
3415 - // TLSSocket to encapsulate TLS communication, which then tunneled via SerialTunnel an then wrapped through CIRA APF
3416 - const TLSSocket = require('tls').TLSSocket;
3417 - 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, rejectUnauthorized: false };
3418 - if (req.query.tls1only == 1) { tlsoptions.secureProtocol = 'TLSv1_method'; }
3419 - const tlsock = new TLSSocket(ser, tlsoptions);
3420 - tlsock.on('error', function (err) { parent.debug('webrelay', "CIRA TLS Connection Error ", err); });
3421 - tlsock.on('secureConnect', function () { parent.debug('webrelay', "CIRA Secure TLS Connection"); ws._socket.resume(); });
3422 -
3423 - // Decrypted tunnel from TLS communcation to be forwarded to websocket
3424 - tlsock.on('data', function (data) {
3425 - // AMT/TLS ---> WS
3426 - try {
3427 - data = data.toString('binary');
3428 - if (ws.interceptor) { data = ws.interceptor.processAmtData(data); } // Run data thru interceptor
3429 - //ws.send(Buffer.from(data, 'binary'));
3430 - ws.send(data);
3431 - } catch (e) { }
3432 - });
3422 + // If TLS is on, forward it through TLSSocket
3423 + ws.forwardclient = tlsock;
3424 + ws.forwardclient.xtls = 1;
3425 +
3426 + ws.forwardclient.onStateChange = function (ciraconn, state) {
3427 + parent.debug('webrelay', 'Relay CIRA state change', state);
3428 + if (state == 0) { try { ws.close(); } catch (e) { } }
3429 + };
3430
3434 - // If TLS is on, forward it through TLSSocket
3435 - ws.forwardclient = tlsock;
3436 - ws.forwardclient.xtls = 1;
3431 + ws.forwardclient.onData = function (ciraconn, data) {
3432 + // Run data thru interceptor
3433 + if (ws.interceptor) { data = Buffer.from(ws.interceptor.processAmtData(data.toString('binary')), 'binary'); }
3434 +
3435 + if (data.length > 0) {
3436 + if (ws.logfile == null) {
3437 + try { ws.send(data); } catch (e) { } // TODO: Add TLS support
3438 + } else {
3439 + // Log to recording file
3440 + recordingEntry(ws.logfile.fd, 2, 0, data, function () { try { ws.send(data); } catch (ex) { console.log(ex); } }); // TODO: Add TLS support
3441 + }
3442 + }
3443 + };
3444 +
3445 + ws.forwardclient.onSendOk = function (ciraconn) {
3446 + // TODO: Flow control? (Dont' really need it with AMT, but would be nice)
3447 + //console.log('onSendOk');
3448 + };
3449 + }
3450 + };
3451 } else {
3452 // Without TLS
3453 ws.forwardclient = parent.mpsserver.SetupChannel(ciraconn, port);
3454 ws.forwardclient.xtls = 0;
3455 ws._socket.resume();
3456 +
3457 + ws.forwardclient.onStateChange = function (ciraconn, state) {
3458 + parent.debug('webrelay', 'Relay CIRA state change', state);
3459 + if (state == 0) { try { ws.close(); } catch (e) { } }
3460 + };
3461 +
3462 + ws.forwardclient.onData = function (ciraconn, data) {
3463 + //parent.debug('webrelaydata', 'Relay CIRA data to WS', data.length);
3464 +
3465 + // Run data thru interceptor
3466 + if (ws.interceptor) { data = Buffer.from(ws.interceptor.processAmtData(data.toString('binary')), 'binary'); }
3467 +
3468 + //console.log('AMT --> WS', Buffer.from(data, 'binary').toString('hex'));
3469 + if (data.length > 0) {
3470 + if (ws.logfile == null) {
3471 + try { ws.send(data); } catch (e) { } // TODO: Add TLS support
3472 + } else {
3473 + // Log to recording file
3474 + recordingEntry(ws.logfile.fd, 2, 0, data, function () { try { ws.send(data); } catch (ex) { console.log(ex); } }); // TODO: Add TLS support
3475 + }
3476 + }
3477 + };
3478 +
3479 + ws.forwardclient.onSendOk = function (ciraconn) {
3480 + // TODO: Flow control? (Dont' really need it with AMT, but would be nice)
3481 + //console.log('onSendOk');
3482 + };
3483 }
3484
3485 // When data is received from the web socket, forward the data into the associated CIRA cahnnel.
3486 // If the CIRA connection is pending, the CIRA channel has built-in buffering, so we are ok sending anyway.
3446 - ws.on('message', function (msg) {
3487 + ws.on('message', function (data) {
3488 + //parent.debug('webrelaydata', 'Relay WS data to CIRA', data.length);
3489 + if (typeof data == 'string') { data = Buffer.from(data, 'binary'); }
3490 +
3491 // WS ---> AMT/TLS
3448 - msg = msg.toString('binary');
3449 - if (ws.interceptor) { msg = ws.interceptor.processBrowserData(msg); } // Run data thru interceptor
3450 - //console.log('WS --> AMT', Buffer.from(msg, 'binary').toString('hex'));
3492 + if (ws.interceptor) { data = Buffer.from(ws.interceptor.processBrowserData(data.toString('binary')), 'binary'); } // Run data thru interceptor
3493
3494 // Log to recording file
3495 if (ws.logfile == null) {
3496 // Forward data to the associated TCP connection.
3455 - if (ws.forwardclient.xtls == 1) { ws.forwardclient.write(Buffer.from(msg, 'binary')); } else { ws.forwardclient.write(msg); }
3497 + ws.forwardclient.write(data);
3498 } else {
3499 // Log to recording file
3458 - var msg2 = Buffer.from(msg, 'binary');
3459 - recordingEntry(ws.logfile.fd, 2, 2, msg2, function () { try { if (ws.forwardclient.xtls == 1) { ws.forwardclient.write(msg2); } else { ws.forwardclient.write(msg); } } catch (ex) { } });
3500 + recordingEntry(ws.logfile.fd, 2, 2, data, function () { try { ws.forwardclient.write(data); } catch (ex) { } });
3501 }
3502 });
3503
@@ -3467,60 +3508,30 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) {
3508 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
3509
3510 // Close the recording file
3470 - if (ws.logfile != null) {
3471 - recordingEntry(ws.logfile.fd, 3, 0, 'MeshCentralMCREC', function (fd, ws) {
3472 - obj.fs.close(fd);
3473 - ws.logfile = null;
3474 - }, ws);
3475 - }
3511 + if (ws.logfile != null) { recordingEntry(ws.logfile.fd, 3, 0, 'MeshCentralMCREC', function (fd, ws) { obj.fs.close(fd); ws.logfile = null; }, ws); }
3512 });
3513
3514 // If the web socket is closed, close the associated TCP connection.
3515 ws.on('close', function (req) {
3516 parent.debug('webrelay', 'Websocket relay closed.');
3481 - 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
3482 -
3483 - // Close the recording file
3484 - if (ws.logfile != null) {
3485 - recordingEntry(ws.logfile.fd, 3, 0, 'MeshCentralMCREC', function (fd, ws) {
3486 - obj.fs.close(fd);
3487 - ws.logfile = null;
3488 - }, ws);
3489 - }
3490 - });
3517
3492 - ws.forwardclient.onStateChange = function (ciraconn, state) {
3493 - parent.debug('webrelay', 'Relay CIRA state change', state);
3494 - if (state == 0) { try { ws.close(); } catch (e) { } }
3495 - };
3496 -
3497 - ws.forwardclient.onData = function (ciraconn, data) {
3498 - parent.debug('webrelaydata', 'Relay CIRA data', data.length);
3499 - if (ws.interceptor) { data = ws.interceptor.processAmtData(data); } // Run data thru interceptor
3500 - //console.log('AMT --> WS', Buffer.from(data, 'binary').toString('hex'));
3501 - if (data.length > 0) {
3502 - if (ws.logfile == null) {
3503 - try { ws.send(Buffer.from(data, 'binary')); } catch (e) { } // TODO: Add TLS support
3504 - } else {
3505 - // Log to recording file
3506 - data = Buffer.from(data, 'binary');
3507 - recordingEntry(ws.logfile.fd, 2, 0, data, function () { try { ws.send(data); } catch (ex) { console.log(ex); } }); // TODO: Add TLS support
3508 - }
3518 + // Websocket closed, close the CIRA channel and TLS session.
3519 + if (ws.forwardclient) {
3520 + if (ws.forwardclient.close) { ws.forwardclient.close(); } // NonTLS, close the CIRA channel
3521 + if (ws.forwardclient.end) { ws.forwardclient.end(); } // TLS, close the TLS session
3522 + if (ws.forwardclient.chnl) { ws.forwardclient.chnl.close(); } // TLS, close the CIRA channel
3523 }
3510 - };
3524
3512 - ws.forwardclient.onSendOk = function (ciraconn) {
3513 - // TODO: Flow control? (Dont' really need it with AMT, but would be nice)
3514 - //console.log('onSendOk');
3515 - };
3525 + // Close the recording file
3526 + if (ws.logfile != null) { recordingEntry(ws.logfile.fd, 3, 0, 'MeshCentralMCREC', function (fd, ws) { obj.fs.close(fd); ws.logfile = null; }, ws); }
3527 + });
3528
3529 // Fetch Intel AMT credentials & Setup interceptor
3530 if (req.query.p == 1) {
3531 parent.debug('webrelaydata', 'INTERCEPTOR1', { host: node.host, port: port, user: node.intelamt.user, pass: node.intelamt.pass });
3532 ws.interceptor = obj.interceptor.CreateHttpInterceptor({ host: node.host, port: port, user: node.intelamt.user, pass: node.intelamt.pass });
3533 ws.interceptor.blockAmtStorage = true;
3522 - }
3523 - else if (req.query.p == 2) {
3534 + } else if (req.query.p == 2) {
3535 parent.debug('webrelaydata', 'INTERCEPTOR2', { user: node.intelamt.user, pass: node.intelamt.pass });
3536 ws.interceptor = obj.interceptor.CreateRedirInterceptor({ user: node.intelamt.user, pass: node.intelamt.pass });
3537 ws.interceptor.blockAmtStorage = true;