2020-10-14 22:01:26 +03:00
/ *
2021-01-10 01:31:09 +03:00
Copyright 2020 - 2021 Intel Corporation
2020-10-14 22:01:26 +03:00
Licensed under the Apache License , Version 2.0 ( the "License" ) ;
you may not use this file except in compliance with the License .
You may obtain a copy of the License at
http : //www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing , software
distributed under the License is distributed on an "AS IS" BASIS ,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND , either express or implied .
See the License for the specific language governing permissions and
limitations under the License .
@ description Intel AMT redirection stack
@ author Ylian Saint - Hilaire
@ version v0 . 3.0
2019-04-24 05:17:33 +03:00
* /
2020-10-14 22:01:26 +03:00
/*jslint node: true */
/*jshint node: true */
/*jshint strict:false */
/*jshint -W097 */
/*jshint esversion: 6 */
"use strict" ;
2019-04-24 05:17:33 +03:00
// Construct a MeshServer object
module . exports . CreateAmtRedirect = function ( module , domain , user , webserver , meshcentral ) {
var obj = { } ;
obj . m = module ; // This is the inner module (Terminal or Desktop)
module . parent = obj ;
obj . State = 0 ;
obj . net = require ( 'net' ) ;
obj . tls = require ( 'tls' ) ;
obj . crypto = require ( 'crypto' ) ;
2019-04-26 01:13:32 +03:00
const constants = require ( 'constants' ) ;
2019-04-24 05:17:33 +03:00
obj . socket = null ;
obj . amtuser = null ;
obj . amtpass = null ;
obj . connectstate = 0 ;
obj . protocol = module . protocol ; // 1 = SOL, 2 = KVM, 3 = IDER
obj . xtlsoptions = null ;
2019-04-24 21:38:28 +03:00
obj . redirTrace = false ;
2019-04-26 01:13:32 +03:00
obj . tls1only = 0 ; // TODO
2019-04-24 05:17:33 +03:00
obj . amtaccumulator = "" ;
obj . amtsequence = 1 ;
obj . amtkeepalivetimer = null ;
obj . authuri = "/RedirectionService" ;
obj . onStateChanged = null ;
obj . forwardclient = null ;
// Mesh Rights
const MESHRIGHT _EDITMESH = 1 ;
const MESHRIGHT _MANAGEUSERS = 2 ;
const MESHRIGHT _MANAGECOMPUTERS = 4 ;
const MESHRIGHT _REMOTECONTROL = 8 ;
const MESHRIGHT _AGENTCONSOLE = 16 ;
const MESHRIGHT _SERVERFILES = 32 ;
const MESHRIGHT _WAKEDEVICE = 64 ;
const MESHRIGHT _SETNOTES = 128 ;
// Site rights
const SITERIGHT _SERVERBACKUP = 1 ;
const SITERIGHT _MANAGEUSERS = 2 ;
const SITERIGHT _SERVERRESTORE = 4 ;
const SITERIGHT _FILEACCESS = 8 ;
const SITERIGHT _SERVERUPDATE = 16 ;
const SITERIGHT _LOCKED = 32 ;
function Debug ( lvl ) {
2019-04-24 21:38:28 +03:00
if ( ( arguments . length < 2 ) || ( lvl > meshcentral . debugLevel ) ) return ;
2019-04-24 05:17:33 +03:00
var a = [ ] ; for ( var i = 1 ; i < arguments . length ; i ++ ) { a . push ( arguments [ i ] ) ; } console . log ( ... a ) ;
}
2019-04-26 01:13:32 +03:00
// 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 ;
}
2019-04-24 21:38:28 +03:00
obj . Start = function ( nodeid ) {
//console.log('Amt-Redir-Start', nodeid);
2019-04-24 05:17:33 +03:00
obj . connectstate = 0 ;
2019-04-24 21:38:28 +03:00
Debug ( 1 , 'AMT redir for ' + user . name + ' to ' + nodeid + '.' ) ;
2019-04-24 05:17:33 +03:00
obj . xxStateChange ( 1 ) ;
// Fetch information about the target
2019-04-24 21:38:28 +03:00
meshcentral . db . Get ( nodeid , function ( err , docs ) {
if ( docs . length == 0 ) { console . log ( 'ERR: Node not found' ) ; obj . Stop ( ) ; return ; }
2019-04-24 05:17:33 +03:00
var node = docs [ 0 ] ;
2019-04-24 21:38:28 +03:00
if ( ! node . intelamt ) { console . log ( 'ERR: Not AMT node' ) ; obj . Stop ( ) ; return ; }
2019-04-24 05:17:33 +03:00
obj . amtuser = node . intelamt . user ;
obj . amtpass = node . intelamt . pass ;
// Check if this user has permission to manage this computer
var meshlinks = user . links [ node . meshid ] ;
2019-04-24 21:38:28 +03:00
if ( ( ! meshlinks ) || ( ! meshlinks . rights ) || ( ( meshlinks . rights & MESHRIGHT _REMOTECONTROL ) == 0 ) ) { console . log ( 'ERR: Access denied (2)' ) ; obj . Stop ( ) ; return ; }
2019-04-24 05:17:33 +03:00
// Check what connectivity is available for this node
2019-04-24 21:38:28 +03:00
var state = meshcentral . GetConnectivityState ( nodeid ) ;
2019-04-24 05:17:33 +03:00
var conn = 0 ;
2019-04-24 21:38:28 +03:00
if ( ! state || state . connectivity == 0 ) { Debug ( 1 , 'ERR: No routing possible (1)' ) ; obj . Stop ( ) ; return ; } else { conn = state . connectivity ; }
2019-04-24 05:17:33 +03:00
/ *
// Check what server needs to handle this connection
if ( ( meshcentral . multiServer != null ) && ( cookie == null ) ) { // If a cookie is provided, don't allow the connection to jump again to a different server
2019-04-24 21:38:28 +03:00
var server = obj . parent . GetRoutingServerId ( nodeid , 2 ) ; // Check for Intel CIRA connection
2019-04-24 05:17:33 +03:00
if ( server != null ) {
if ( server . serverid != obj . parent . serverId ) {
// Do local Intel CIRA routing using a different server
Debug ( 1 , 'Route Intel AMT CIRA connection to peer server: ' + server . serverid ) ;
obj . parent . multiServer . createPeerRelay ( ws , req , server . serverid , user ) ;
return ;
}
} else {
2019-04-24 21:38:28 +03:00
server = obj . parent . GetRoutingServerId ( nodeid , 4 ) ; // Check for local Intel AMT connection
2019-04-24 05:17:33 +03:00
if ( ( server != null ) && ( server . serverid != obj . parent . serverId ) ) {
// Do local Intel AMT routing using a different server
Debug ( 1 , 'Route Intel AMT direct connection to peer server: ' + server . serverid ) ;
obj . parent . multiServer . createPeerRelay ( ws , req , server . serverid , user ) ;
return ;
}
}
}
* /
// If Intel AMT CIRA connection is available, use it
2020-10-10 01:44:09 +03:00
var ciraconn = meshcentral . mpsserver . GetConnectionToNode ( nodeid , null , true ) ; // Request an OOB connection
if ( ciraconn != null ) {
2019-04-24 21:38:28 +03:00
Debug ( 1 , 'Opening Intel AMT CIRA transport connection to ' + nodeid + '.' ) ;
2019-04-24 05:17:33 +03:00
// Compute target port, look at the CIRA port mappings, if non-TLS is allowed, use that, if not use TLS
2019-04-26 01:13:32 +03:00
var port = 16995 ;
if ( ciraconn . tag . boundPorts . indexOf ( 16994 ) >= 0 ) port = 16994 ; // RELEASE: Always use non-TLS mode if available within CIRA
2019-04-24 05:17:33 +03:00
// 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 ( ) ;
2020-10-08 22:50:27 +03:00
var chnl = meshcentral . mpsserver . SetupChannel ( ciraconn , port ) ;
2019-04-24 05:17:33 +03:00
// let's chain up the TLSSocket <-> SerialTunnel <-> CIRA APF (chnl)
// Anything that needs to be forwarded by SerialTunnel will be encapsulated by chnl write
ser . forwardwrite = function ( msg ) {
// TLS ---> CIRA
chnl . write ( msg . toString ( 'binary' ) ) ;
} ;
// When APF tunnel return something, update SerialTunnel buffer
chnl . onData = function ( ciraconn , data ) {
// CIRA ---> TLS
Debug ( 3 , 'Relay TLS CIRA data' , data . length ) ;
if ( data . length > 0 ) { try { ser . updateBuffer ( Buffer . from ( data , 'binary' ) ) ; } catch ( e ) { } }
} ;
// Handle CIRA tunnel state change
chnl . onStateChange = function ( ciraconn , state ) {
Debug ( 2 , 'Relay TLS CIRA state change' , state ) ;
if ( state == 0 ) { try { ws . close ( ) ; } catch ( e ) { } }
} ;
// TLSSocket to encapsulate TLS communication, which then tunneled via SerialTunnel an then wrapped through CIRA APF
const TLSSocket = require ( 'tls' ) . TLSSocket ;
2020-03-31 05:29:46 +03:00
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 } ;
if ( obj . tls1only == 1 ) { tlsoptions . secureProtocol = 'TLSv1_method' ; }
2019-04-24 05:17:33 +03:00
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 ( ) ; } ) ;
// Decrypted tunnel from TLS communcation to be forwarded to websocket
tlsock . on ( 'data' , function ( data ) {
// AMT/TLS ---> WS
try {
data = data . toString ( 'binary' ) ;
//ws.send(Buffer.from(data, 'binary'));
ws . send ( data ) ;
} catch ( e ) { }
} ) ;
// If TLS is on, forward it through TLSSocket
2019-04-26 01:13:32 +03:00
obj . forwardclient = tlsock ;
obj . forwardclient . xtls = 1 ;
2019-04-24 05:17:33 +03:00
} else {
// Without TLS
2020-10-08 22:50:27 +03:00
obj . forwardclient = meshcentral . mpsserver . SetupChannel ( ciraconn , port ) ;
2019-04-26 01:13:32 +03:00
obj . forwardclient . xtls = 0 ;
2019-04-24 05:17:33 +03:00
}
2019-04-26 01:13:32 +03:00
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 ( ) ; }
2019-04-24 05:17:33 +03:00
} ;
2019-04-26 01:13:32 +03:00
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
2019-04-24 05:17:33 +03:00
} ;
2019-04-26 01:13:32 +03:00
obj . forwardclient . onSendOk = function ( ciraconn ) {
2019-04-24 05:17:33 +03:00
// TODO: Flow control? (Dont' really need it with AMT, but would be nice)
2019-04-26 01:13:32 +03:00
Debug ( 4 , 'Intel AMT CIRA sendok' ) ;
2019-04-24 05:17:33 +03:00
} ;
return ;
}
// If Intel AMT direct connection is possible, option a direct socket
if ( ( conn & 4 ) != 0 ) { // We got a new web socket connection, initiate a TCP connection to the target Intel AMT host/port.
2019-04-24 21:38:28 +03:00
Debug ( 1 , 'Opening Intel AMT transport connection to ' + nodeid + '.' ) ;
2019-04-24 05:17:33 +03:00
2019-04-24 21:38:28 +03:00
// Compute target port
var port = 16994 ;
if ( node . intelamt . tls > 0 ) port = 16995 ; // This is a direct connection, use TLS when possible
if ( node . intelamt . tls != 1 ) {
2019-04-24 05:17:33 +03:00
// If this is TCP (without TLS) set a normal TCP socket
obj . forwardclient = new obj . net . Socket ( ) ;
obj . forwardclient . setEncoding ( 'binary' ) ;
} else {
// If TLS is going to be used, setup a TLS socket
2020-03-31 05:29:46 +03:00
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 , rejectUnauthorized : false } ;
if ( obj . tls1only == 1 ) { tlsoptions . secureProtocol = 'TLSv1_method' ; }
2019-04-24 05:17:33 +03:00
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 + '.' ) ;
obj . xxOnSocketConnected ( ) ;
} ) ;
obj . forwardclient . setEncoding ( 'binary' ) ;
}
// When we receive data on the TCP connection, forward it back into the web socket connection.
obj . forwardclient . on ( 'data' , function ( data ) {
//if (obj.parent.debugLevel >= 1) { // DEBUG
Debug ( 1 , 'Intel AMT transport data from ' + node . host + ', ' + data . length + ' bytes.' ) ;
2019-04-26 01:59:10 +03:00
Debug ( 4 , ' ' + Buffer . from ( data , 'binary' ) . toString ( 'hex' ) ) ;
2019-04-24 05:17:33 +03:00
//if (obj.parent.debugLevel >= 4) { Debug(4, ' ' + Buffer.from(data, 'binary').toString('hex')); }
//}
obj . xxOnSocketData ( data ) ;
} ) ;
// If the TCP connection closes, disconnect the associated web socket.
obj . forwardclient . on ( 'close' , function ( ) {
Debug ( 1 , 'Intel AMT transport relay disconnected from ' + node . host + '.' ) ;
2019-04-24 21:38:28 +03:00
obj . Stop ( ) ;
2019-04-24 05:17:33 +03:00
} ) ;
// If the TCP connection causes an error, disconnect the associated web socket.
obj . forwardclient . on ( 'error' , function ( err ) {
Debug ( 1 , 'Intel AMT transport relay error from ' + node . host + ': ' + err . errno ) ;
2019-04-24 21:38:28 +03:00
obj . Stop ( ) ;
2019-04-24 05:17:33 +03:00
} ) ;
if ( node . intelamt . tls == 0 ) {
// A TCP connection to Intel AMT just connected, start forwarding.
obj . forwardclient . connect ( port , node . host , function ( ) {
Debug ( 1 , 'Intel AMT transport connected to ' + node . host + ':' + port + '.' ) ;
obj . xxOnSocketConnected ( ) ;
} ) ;
}
return ;
}
} ) ;
}
// Get the certificate of Intel AMT
obj . getPeerCertificate = function ( ) { if ( obj . xtls == true ) { return obj . socket . getPeerCertificate ( ) ; } return null ; }
obj . xxOnSocketConnected = function ( ) {
2019-04-24 21:38:28 +03:00
//console.log('xxOnSocketConnected');
2019-04-24 05:17:33 +03:00
if ( ! obj . xtlsoptions || ! obj . xtlsoptions . meshServerConnect ) {
if ( obj . xtls == true ) {
obj . xtlsCertificate = obj . socket . getPeerCertificate ( ) ;
if ( ( obj . xtlsFingerprint != 0 ) && ( obj . xtlsCertificate . fingerprint . split ( ':' ) . join ( '' ) . toLowerCase ( ) != obj . xtlsFingerprint ) ) { obj . Stop ( ) ; return ; }
}
}
if ( obj . redirTrace ) { console . log ( "REDIR-CONNECTED" ) ; }
//obj.Debug("Socket Connected");
obj . xxStateChange ( 2 ) ;
if ( obj . protocol == 1 ) obj . xxSend ( obj . RedirectStartSol ) ; // TODO: Put these strings in higher level module to tighten code
if ( obj . protocol == 2 ) obj . xxSend ( obj . RedirectStartKvm ) ; // Don't need these is the feature if not compiled-in.
if ( obj . protocol == 3 ) obj . xxSend ( obj . RedirectStartIder ) ;
}
obj . xxOnSocketData = function ( data ) {
if ( ! data || obj . connectstate == - 1 ) return ;
if ( obj . redirTrace ) { console . log ( "REDIR-RECV(" + data . length + "): " + webserver . common . rstr2hex ( data ) ) ; }
//obj.Debug("Recv(" + data.length + "): " + webserver.common.rstr2hex(data));
2019-04-24 21:38:28 +03:00
if ( ( obj . protocol > 1 ) && ( obj . connectstate == 1 ) ) { return obj . m . ProcessData ( data ) ; } // KVM traffic, forward it directly.
2019-04-24 05:17:33 +03:00
obj . amtaccumulator += data ;
//obj.Debug("Recv(" + obj.amtaccumulator.length + "): " + webserver.common.rstr2hex(obj.amtaccumulator));
while ( obj . amtaccumulator . length >= 1 ) {
var cmdsize = 0 ;
switch ( obj . amtaccumulator . charCodeAt ( 0 ) ) {
case 0x11 : // StartRedirectionSessionReply (17)
if ( obj . amtaccumulator . length < 4 ) return ;
var statuscode = obj . amtaccumulator . charCodeAt ( 1 ) ;
switch ( statuscode ) {
case 0 : // STATUS_SUCCESS
if ( obj . amtaccumulator . length < 13 ) return ;
var oemlen = obj . amtaccumulator . charCodeAt ( 12 ) ;
if ( obj . amtaccumulator . length < 13 + oemlen ) return ;
obj . xxSend ( String . fromCharCode ( 0x13 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 ) ) ; // Query authentication support
cmdsize = ( 13 + oemlen ) ;
break ;
default :
obj . Stop ( ) ;
break ;
}
break ;
case 0x14 : // AuthenticateSessionReply (20)
if ( obj . amtaccumulator . length < 9 ) return ;
var authDataLen = webserver . common . ReadIntX ( obj . amtaccumulator , 5 ) ;
if ( obj . amtaccumulator . length < 9 + authDataLen ) return ;
var status = obj . amtaccumulator . charCodeAt ( 1 ) ;
var authType = obj . amtaccumulator . charCodeAt ( 4 ) ;
var authData = [ ] ;
2021-06-22 06:36:27 +03:00
for ( var i = 0 ; i < authDataLen ; i ++ ) { authData . push ( obj . amtaccumulator . charCodeAt ( 9 + i ) ) ; }
2019-04-24 05:17:33 +03:00
var authDataBuf = obj . amtaccumulator . substring ( 9 , 9 + authDataLen ) ;
cmdsize = 9 + authDataLen ;
if ( authType == 0 ) {
2019-04-24 21:38:28 +03:00
/ *
// This is Kerberos code, not supported in MeshCentral.
2019-04-24 05:17:33 +03:00
if ( obj . amtuser == '*' ) {
if ( authData . indexOf ( 2 ) >= 0 ) {
// Kerberos Auth
var ticket ;
if ( kerberos && kerberos != null ) {
var ticketReturn = kerberos . getTicket ( 'HTTP' + ( ( obj . tls == 1 ) ? 'S' : '' ) + '/' + ( ( obj . amtpass == '' ) ? ( obj . host + ':' + obj . port ) : obj . amtpass ) ) ;
if ( ticketReturn . returnCode == 0 || ticketReturn . returnCode == 0x90312 ) {
ticket = ticketReturn . ticket ;
if ( process . platform . indexOf ( 'win' ) >= 0 ) {
// Clear kerberos tickets on both 32 and 64bit Windows platforms
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 ) ; }
}
} else {
console . error ( 'Unexpected Kerberos error code: ' + ticketReturn . returnCode ) ;
}
}
if ( ticket ) {
obj . xxSend ( String . fromCharCode ( 0x13 , 0x00 , 0x00 , 0x00 , 0x02 ) + webserver . common . IntToStrX ( ticket . length ) + ticket ) ;
} else {
obj . Stop ( ) ;
}
}
else obj . Stop ( ) ;
} else {
2019-04-24 21:38:28 +03:00
* /
2019-04-24 05:17:33 +03:00
// Query
if ( authData . indexOf ( 4 ) >= 0 ) {
// Good Digest Auth (With cnonce and all)
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 ) ) ;
}
else if ( authData . indexOf ( 3 ) >= 0 ) {
// Bad Digest Auth (Not sure why this is supported, cnonce is not used!)
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 ) ) ;
}
else if ( authData . indexOf ( 1 ) >= 0 ) {
// Basic Auth (Probably a good idea to not support this unless this is an old version of Intel AMT)
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 ) ;
}
else obj . Stop ( ) ;
2019-04-24 21:38:28 +03:00
/ *
2019-04-24 05:17:33 +03:00
}
2019-04-24 21:38:28 +03:00
* /
2019-04-24 05:17:33 +03:00
}
else if ( ( authType == 3 || authType == 4 ) && status == 1 ) {
var curptr = 0 ;
// Realm
var realmlen = authDataBuf . charCodeAt ( curptr ) ;
var realm = authDataBuf . substring ( curptr + 1 , curptr + 1 + realmlen ) ;
curptr += ( realmlen + 1 ) ;
// Nonce
var noncelen = authDataBuf . charCodeAt ( curptr ) ;
var nonce = authDataBuf . substring ( curptr + 1 , curptr + 1 + noncelen ) ;
curptr += ( noncelen + 1 ) ;
// QOP
var qoplen = 0 ;
var qop = null ;
var cnonce = obj . xxRandomValueHex ( 32 ) ;
var snc = '00000002' ;
var extra = '' ;
if ( authType == 4 ) {
qoplen = authDataBuf . charCodeAt ( curptr ) ;
qop = authDataBuf . substring ( curptr + 1 , curptr + 1 + qoplen ) ;
curptr += ( qoplen + 1 ) ;
extra = snc + ":" + cnonce + ":" + qop + ":" ;
}
var digest = hex _md5 ( hex _md5 ( obj . amtuser + ":" + realm + ":" + obj . amtpass ) + ":" + nonce + ":" + extra + hex _md5 ( "POST:" + obj . authuri ) ) ;
var totallen = obj . amtuser . length + realm . length + nonce . length + obj . authuri . length + cnonce . length + snc . length + digest . length + 7 ;
if ( authType == 4 ) totallen += ( qop . length + 1 ) ;
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 ;
if ( authType == 4 ) buf += ( String . fromCharCode ( qop . length ) + qop ) ;
obj . xxSend ( buf ) ;
}
else if ( status == 0 ) { // Success
2019-04-24 21:38:28 +03:00
/ *
2019-04-24 05:17:33 +03:00
if ( obj . protocol == 1 ) {
// Serial-over-LAN: Send Intel AMT serial settings...
var MaxTxBuffer = 10000 ;
var TxTimeout = 100 ;
var TxOverflowTimeout = 0 ;
var RxTimeout = 10000 ;
var RxFlushTimeout = 100 ;
var Heartbeat = 0 ; //5000;
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 ) ) ;
}
if ( obj . protocol == 2 ) {
// Remote Desktop: Send traffic directly...
obj . xxSend ( String . fromCharCode ( 0x40 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 ) ) ;
}
2019-04-24 21:38:28 +03:00
* /
if ( obj . protocol == 3 ) { // IDE-R
obj . connectstate = 1 ;
obj . m . Start ( ) ;
if ( obj . amtaccumulator . length > cmdsize ) { obj . m . ProcessData ( obj . amtaccumulator . substring ( cmdsize ) ) ; }
cmdsize = obj . amtaccumulator . length ;
}
2019-04-24 05:17:33 +03:00
} else obj . Stop ( ) ;
break ;
case 0x21 : // Response to settings (33)
if ( obj . amtaccumulator . length < 23 ) break ;
cmdsize = 23 ;
obj . xxSend ( String . fromCharCode ( 0x27 , 0x00 , 0x00 , 0x00 ) + ToIntStr ( obj . amtsequence ++ ) + String . fromCharCode ( 0x00 , 0x00 , 0x1B , 0x00 , 0x00 , 0x00 ) ) ;
if ( obj . protocol == 1 ) { obj . amtkeepalivetimer = setInterval ( obj . xxSendAmtKeepAlive , 2000 ) ; }
obj . connectstate = 1 ;
obj . xxStateChange ( 3 ) ;
break ;
case 0x29 : // Serial Settings (41)
if ( obj . amtaccumulator . length < 10 ) break ;
cmdsize = 10 ;
break ;
case 0x2A : // Incoming display data (42)
if ( obj . amtaccumulator . length < 10 ) break ;
var cs = ( 10 + ( ( obj . amtaccumulator . charCodeAt ( 9 ) & 0xFF ) << 8 ) + ( obj . amtaccumulator . charCodeAt ( 8 ) & 0xFF ) ) ;
if ( obj . amtaccumulator . length < cs ) break ;
obj . m . ProcessData ( obj . amtaccumulator . substring ( 10 , cs ) ) ;
cmdsize = cs ;
break ;
case 0x2B : // Keep alive message (43)
if ( obj . amtaccumulator . length < 8 ) break ;
cmdsize = 8 ;
break ;
case 0x41 :
if ( obj . amtaccumulator . length < 8 ) break ;
obj . connectstate = 1 ;
obj . m . Start ( ) ;
// KVM traffic, forward rest of accumulator directly.
if ( obj . amtaccumulator . length > 8 ) { obj . m . ProcessData ( obj . amtaccumulator . substring ( 8 ) ) ; }
cmdsize = obj . amtaccumulator . length ;
break ;
default :
console . log ( "Unknown Intel AMT command: " + obj . amtaccumulator . charCodeAt ( 0 ) + " acclen=" + obj . amtaccumulator . length ) ;
obj . Stop ( ) ;
return ;
}
if ( cmdsize == 0 ) return ;
obj . amtaccumulator = obj . amtaccumulator . substring ( cmdsize ) ;
}
}
obj . xxSend = function ( x ) {
2020-08-06 23:23:17 +03:00
if ( obj . redirTrace ) { console . log ( "REDIR-SEND(" + x . length + "): " + Buffer . from ( x , "binary" ) . toString ( 'hex' ) , typeof x ) ; }
2019-04-24 05:17:33 +03:00
//obj.Debug("Send(" + x.length + "): " + webserver.common.rstr2hex(x));
2019-04-26 03:25:06 +03:00
//obj.forwardclient.write(x); // FIXES CIRA
2020-08-06 23:23:17 +03:00
obj . forwardclient . write ( Buffer . from ( x , "binary" ) ) ;
2019-04-24 05:17:33 +03:00
}
obj . Send = function ( x ) {
if ( obj . forwardclient == null || obj . connectstate != 1 ) return ;
if ( obj . protocol == 1 ) { obj . xxSend ( String . fromCharCode ( 0x28 , 0x00 , 0x00 , 0x00 ) + ToIntStr ( obj . amtsequence ++ ) + ToShortStr ( x . length ) + x ) ; } else { obj . xxSend ( x ) ; }
}
obj . xxSendAmtKeepAlive = function ( ) {
if ( obj . forwardclient == null ) return ;
obj . xxSend ( String . fromCharCode ( 0x2B , 0x00 , 0x00 , 0x00 ) + ToIntStr ( obj . amtsequence ++ ) ) ;
}
obj . xxRandomValueHex = function ( len ) { return obj . crypto . randomBytes ( Math . ceil ( len / 2 ) ) . toString ( 'hex' ) . slice ( 0 , len ) ; }
obj . xxOnSocketClosed = function ( ) {
if ( obj . redirTrace ) { console . log ( "REDIR-CLOSED" ) ; }
//obj.Debug("Socket Closed");
obj . Stop ( ) ;
}
obj . xxStateChange = function ( newstate ) {
if ( obj . State == newstate ) return ;
obj . State = newstate ;
obj . m . xxStateChange ( obj . State ) ;
if ( obj . onStateChanged != null ) obj . onStateChanged ( obj , obj . State ) ;
}
obj . Stop = function ( ) {
if ( obj . redirTrace ) { console . log ( "REDIR-CLOSED" ) ; }
//obj.Debug("Socket Stopped");
obj . xxStateChange ( 0 ) ;
obj . connectstate = - 1 ;
obj . amtaccumulator = "" ;
2019-04-26 01:13:32 +03:00
if ( obj . forwardclient != null ) { try { obj . forwardclient . close ( ) ; } catch ( ex ) { } delete obj . forwardclient ; }
if ( obj . amtkeepalivetimer != null ) { clearInterval ( obj . amtkeepalivetimer ) ; delete obj . amtkeepalivetimer ; }
2019-04-24 05:17:33 +03:00
}
obj . RedirectStartSol = String . fromCharCode ( 0x10 , 0x00 , 0x00 , 0x00 , 0x53 , 0x4F , 0x4C , 0x20 ) ;
obj . RedirectStartKvm = String . fromCharCode ( 0x10 , 0x01 , 0x00 , 0x00 , 0x4b , 0x56 , 0x4d , 0x52 ) ;
obj . RedirectStartIder = String . fromCharCode ( 0x10 , 0x00 , 0x00 , 0x00 , 0x49 , 0x44 , 0x45 , 0x52 ) ;
function hex _md5 ( str ) { return meshcentral . certificateOperations . forge . md . md5 . create ( ) . update ( str ) . digest ( ) . toHex ( ) ; }
return obj ;
}
function ToIntStr ( v ) { return String . fromCharCode ( ( v & 0xFF ) , ( ( v >> 8 ) & 0xFF ) , ( ( v >> 16 ) & 0xFF ) , ( ( v >> 24 ) & 0xFF ) ) ; }
function ToShortStr ( v ) { return String . fromCharCode ( ( v & 0xFF ) , ( ( v >> 8 ) & 0xFF ) ) ; }