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;