2021-03-10 22:01:10 -05:00
/ * *
* @ description MeshCentral Intel AMT Hello server
* @ author Ylian Saint - Hilaire
* @ copyright Intel Corporation 2018 - 2021
* @ license Apache - 2.0
* @ version v0 . 0.1
* /
/*xjslint node: true */
/*xjslint plusplus: true */
/*xjslint maxlen: 256 */
/*jshint node: true */
/*jshint strict: false */
/*jshint esversion: 6 */
"use strict" ;
// Construct the Intel AMT hello server. This is used for Intel AMT bare-metal activation on the local LAN.
// This server can receive a notification from Intel AMT and attempt activation.
2021-03-11 21:23:05 -05:00
// In Intel documentation, this is called the Setup and Configuration Application (SCA)
2021-03-10 22:01:10 -05:00
module . exports . CreateAmtHelloServer = function ( parent , config ) {
var obj = { } ;
2021-03-11 21:23:05 -05:00
// WSMAN stack
const CreateWsmanComm = require ( './amt/amt-wsman-comm' ) ;
const WsmanStackCreateService = require ( './amt/amt-wsman' ) ;
const AmtStackCreateService = require ( './amt/amt' ) ;
2021-03-11 04:12:58 -05:00
// Start the Intel AMT hello server
2021-03-10 22:01:10 -05:00
var port = 9971 ;
if ( typeof config . port == 'number' ) { port = config . port ; }
const net = require ( 'net' ) ;
obj . server = net . createServer ( function ( socket ) {
socket . ra = socket . remoteAddress ;
socket . data = null ;
socket . on ( 'error' , function ( err ) { } )
2021-03-11 04:12:58 -05:00
socket . on ( 'close' , function ( ) { if ( this . data != null ) { processHelloData ( this . data , this . ra ) ; } delete this . ra ; this . removeAllListeners ( ) ; } )
2021-03-10 22:01:10 -05:00
socket . on ( 'data' , function ( data ) {
2021-03-11 04:12:58 -05:00
console . log ( 'indata' , data . toString ( 'hex' ) ) ;
2021-03-10 22:01:10 -05:00
if ( this . data == null ) { this . data = data ; } else { Buffer . concat ( [ this . data , data ] ) ; }
var str = this . data . toString ( ) ;
if ( str . startsWith ( 'GET ' ) && ( str . indexOf ( '\r\n\r\n' ) >= 0 ) ) {
this . data = null ;
var content = "<!DOCTYPE html><html><head><meta charset=\"UTF-8\"><title>Intel® AMT Hello Server</title></head><body>Intel AMT hello server.<br />Intel® AMT devices should send notification to this port for activation.</body></html>" ;
try { socket . end ( 'HTTP/1.1 200 OK\r\nContent-Type: text/html\r\nContent-Length: ' + content . length + '\r\nConnection: close\r\n\r\n' + content ) ; } catch ( ex ) { }
} else if ( this . data . length > 16000 ) {
try { this . end ( ) ; } catch ( ex ) { } ;
}
} )
} ) ;
obj . server . listen ( port ) ;
2021-03-11 04:12:58 -05:00
console . log ( 'MeshCentral Intel(R) AMT provisioning server running on port ' + port + '.' ) ;
2021-03-10 22:01:10 -05:00
2021-03-11 04:12:58 -05:00
// Example hello data for testing
2021-03-11 23:17:23 -05:00
//setTimeout(function () { processHelloData(Buffer.from('01000300000000004b529b93d413181de4871c697a6b7a2b170220c3846bf24b9e93ca64274c0ec67c1ecc5e024ffcacd2d74019350e81fe546ae4022045140b3247eb9cc8c5b4f0d7b53091f73292089e6e5a63e2749dd3aca9198eda0220d7a7a0fb5d7e2731d771e9484ebcdef71d5f0c3e0a2948782bc83ee0ea699ef402201465fa205397b876faa6f0a9958e5590e40fcc7faa4fb7c2c8677521fb5fb65802202ce1cb0bf9d2f9e102993fbe215152c3b2dd0cabde1c68e5319b839154dbb7f502209acfab7e43c8d880d06b262a94deeee4b4659989c3d0caf19baf6405e41ab7df022016af57a9f676b0ab126095aa5ebadef22ab31119d644ac95cd4b93dbf3f26aeb0220960adf0063e96356750c2965dd0a0867da0b9cbd6e77714aeafb2349ab393da3022068ad50909b04363c605ef13581a939ff2c96372e3f12325b0a6861e1d59f660302206dc47172e01cbcb0bf62580d895fe2b8ac9ad4f873801e0c10b9c837d21eb177022073c176434f1bc6d5adf45b0e76e727287c8de57616c1e6e6141a2b2cbc7d8e4c022043df5774b03e7fef5fe40d931a7bedf1bb2e6b42738c4e6d3841103d3aa7f33902202399561127a57125de8cefea610ddf2fa078b5c8067f4e828290bfb860e84b3c022070a73f7f376b60074248904534b11482d5bf0e698ecc498df52577ebf2e93b9a02204348a0e9444c78cb265e058d5e8944b4d84f9662bd26db257f8934a443c701610220cb3ccbb76031e5e0138f8dd39a23f9de47ffc35e43c1144cea27d46a5ab1cb5f022031ad6648f8104138c738f39ea4320133393e3a18cc02296ef97c2ac9ef6731d00220552f7bdcf1a7af9e6ce672017f4f12abf77240c78e761ac203d1d9d20ac89988022067540a47aa5b9f34570a99723cfefa96a96ee3f0d9b8bf4def9440b8065d665d02207224395222cd588c4f2683716922addb41e39b581ac34fa87b39efa896fbb39e0220cbb522d7b7f127ad6a0113865bdf1cd4102e7d0759af635a7cf4720dc963c53b0220179fbc148a3dd00fd24ea13458cc43bfa7f59c8182d783a513f6ebec100c892402202cabeafe37d06ca22aba7391c0033d25982952c453647349763a3ab5ad6ccf69', 'hex'), '192.168.2.148'); }, 500);
setTimeout ( function ( ) { processHelloData ( Buffer . from ( '01000300000000004b529b93d413181de4871c697a6b7a2b180220c3846bf24b9e93ca64274c0ec67c1ecc5e024ffcacd2d74019350e81fe546ae4022045140b3247eb9cc8c5b4f0d7b53091f73292089e6e5a63e2749dd3aca9198eda0220d7a7a0fb5d7e2731d771e9484ebcdef71d5f0c3e0a2948782bc83ee0ea699ef402201465fa205397b876faa6f0a9958e5590e40fcc7faa4fb7c2c8677521fb5fb65802202ce1cb0bf9d2f9e102993fbe215152c3b2dd0cabde1c68e5319b839154dbb7f502209acfab7e43c8d880d06b262a94deeee4b4659989c3d0caf19baf6405e41ab7df022016af57a9f676b0ab126095aa5ebadef22ab31119d644ac95cd4b93dbf3f26aeb0220960adf0063e96356750c2965dd0a0867da0b9cbd6e77714aeafb2349ab393da3022068ad50909b04363c605ef13581a939ff2c96372e3f12325b0a6861e1d59f660302206dc47172e01cbcb0bf62580d895fe2b8ac9ad4f873801e0c10b9c837d21eb177022073c176434f1bc6d5adf45b0e76e727287c8de57616c1e6e6141a2b2cbc7d8e4c022043df5774b03e7fef5fe40d931a7bedf1bb2e6b42738c4e6d3841103d3aa7f33902202399561127a57125de8cefea610ddf2fa078b5c8067f4e828290bfb860e84b3c022070a73f7f376b60074248904534b11482d5bf0e698ecc498df52577ebf2e93b9a02204348a0e9444c78cb265e058d5e8944b4d84f9662bd26db257f8934a443c701610220cb3ccbb76031e5e0138f8dd39a23f9de47ffc35e43c1144cea27d46a5ab1cb5f022031ad6648f8104138c738f39ea4320133393e3a18cc02296ef97c2ac9ef6731d00220552f7bdcf1a7af9e6ce672017f4f12abf77240c78e761ac203d1d9d20ac89988022067540a47aa5b9f34570a99723cfefa96a96ee3f0d9b8bf4def9440b8065d665d0220a267c480b0b29056eb5e8aa7c93add804f5a7df516e969e77bcacafe8d45607902207224395222cd588c4f2683716922addb41e39b581ac34fa87b39efa896fbb39e0220cbb522d7b7f127ad6a0113865bdf1cd4102e7d0759af635a7cf4720dc963c53b0220179fbc148a3dd00fd24ea13458cc43bfa7f59c8182d783a513f6ebec100c892402202cabeafe37d06ca22aba7391c0033d25982952c453647349763a3ab5ad6ccf69' , 'hex' ) , '192.168.2.148' ) ; } , 500 ) ;
2021-03-10 22:01:10 -05:00
2021-03-11 04:12:58 -05:00
// Parse Intel AMT hello data
function parseHelloData ( data , addr ) {
try {
if ( addr . startsWith ( '::ffff:' ) ) { addr = addr . substring ( 7 ) ; }
var amtHello = { time : Date . now ( ) , addr : addr } ;
2021-03-10 22:01:10 -05:00
2021-03-11 04:12:58 -05:00
// Decode header
if ( data . length < 25 ) return ; // Invalid data
const firstBytes = data . readInt16LE ( 0 ) ;
if ( firstBytes > 1 ) return ; // Invalid data
amtHello . adminCredentialsSet = ( firstBytes != 0 ) ;
amtHello . version = data . readInt16LE ( 2 ) ;
if ( amtHello . version != 3 ) return null ; // One touch PID not supported, only version 3 supported.
amtHello . retryCount = data . readInt32LE ( 4 ) ;
amtHello . guidhex = data . slice ( 8 , 24 ) . toString ( 'hex' ) ;
amtHello . guid = guidToStr ( amtHello . guidhex ) ;
2021-03-10 22:01:10 -05:00
2021-03-11 04:12:58 -05:00
// Get the list of hashes
const hashCount = data [ 24 ] ;
amtHello . hashes = [ ] ;
var ptr = 25 ;
for ( var i = 0 ; i < hashCount ; i ++ )
2021-03-10 22:01:10 -05:00
{
2021-03-11 04:12:58 -05:00
const hashType = data [ ptr ] ; // 1=SHA1 (20 byte hash); 2 = SHA256 (32 byte hash); 3 = SHA384 (48 byte hash)
const hashSize = data [ ptr + 1 ] ;
if ( ( hashType < 1 ) || ( hashType > 3 ) ) return null ; // Unexpected hash type
if ( ( hashType == 1 ) && ( hashSize != 20 ) ) return null ; // Unexpected SHA1 hash size
if ( ( hashType == 2 ) && ( hashSize != 32 ) ) return null ; // Unexpected SHA256 hash size
if ( ( hashType == 3 ) && ( hashSize != 48 ) ) return null ; // Unexpected SHA384 hash size
const hash = data . slice ( ptr + 2 , ptr + 2 + hashSize ) ;
amtHello . hashes . push ( hash . toString ( 'hex' ) ) ;
ptr += ( hashSize + 2 ) ;
2021-03-10 22:01:10 -05:00
}
2021-03-11 04:12:58 -05:00
if ( amtHello . hashes . length != hashCount ) return null ; // Unexpected number of hashes
return amtHello ; // Everything looks good.
} catch ( ex ) { return null ; }
2021-03-10 22:01:10 -05:00
}
2021-03-11 04:12:58 -05:00
function guidToStr ( g ) { return g . substring ( 6 , 8 ) + g . substring ( 4 , 6 ) + g . substring ( 2 , 4 ) + g . substring ( 0 , 2 ) + "-" + g . substring ( 10 , 12 ) + g . substring ( 8 , 10 ) + "-" + g . substring ( 14 , 16 ) + g . substring ( 12 , 14 ) + "-" + g . substring ( 16 , 20 ) + "-" + g . substring ( 20 ) ; }
function strToGuid ( s ) { s = s . replace ( /-/g , '' ) ; var ret = s . substring ( 6 , 8 ) + s . substring ( 4 , 6 ) + s . substring ( 2 , 4 ) + s . substring ( 0 , 2 ) + s . substring ( 10 , 12 ) + s . substring ( 8 , 10 ) + s . substring ( 14 , 16 ) + s . substring ( 12 , 14 ) + s . substring ( 16 , 20 ) + s . substring ( 20 ) ; return ret ; }
2021-03-10 22:01:10 -05:00
2021-03-11 04:12:58 -05:00
// Process incoming Intel AMT hello data
function processHelloData ( data , addr ) {
// Check if we can parse the incoming data
2021-03-11 21:23:05 -05:00
const dev = parseHelloData ( data , addr ) ;
if ( dev == null ) { parent . debug ( 'amtsca' , addr , 'Got invalid hello from: ' + addr ) ; return ; } // Invalid Intel AMT hello
parent . debug ( 'amtsca' , 'Got hello from ' + addr ) ;
// Get assumed trusted FQDN and device group
dev . trustedFqdn = config . trustedfqdn ;
var mesh = parent . webserver . meshes [ config . devicegroup ] ;
if ( ( mesh == null ) || ( mesh . mtype !== 1 ) || ( typeof mesh . amt !== 'object' ) || ( typeof mesh . amt . type !== 'number' ) ) { parent . debug ( 'amtsca' , addr , 'Invalid device group for Intel AMT activation.' ) ; return ; }
if ( ( mesh . amt . type != 3 ) && ( mesh . amt . type != 4 ) ) { parent . debug ( 'amtsca' , addr , 'Device group does not have ACM activation policy.' ) ; return ; }
dev . mesh = mesh ;
dev . domainid = mesh . domain ;
// Compute the nodeid for this device using the device GUID
const g = dev . guid . split ( '-' ) . join ( '' ) ;
const id = Buffer . from ( g + g + g , 'hex' ) . toString ( 'base64' ) ;
dev . nodeid = 'node/' + mesh . domain + '/' + id ;
// Attempts reverse DNS loopup on the device IP address
const func = function dnsReverseLoopup ( err , hostnames ) {
var hostname = dnsReverseLoopup . addr ;
if ( ( err == null ) && ( hostnames != null ) && ( hostnames . length > 0 ) ) { hostname = hostnames [ 0 ] ; }
dnsReverseLoopup . dev . hostname = hostname ;
processHelloDataEx1 ( dnsReverseLoopup . dev ) ;
}
func . addr = addr ;
func . dev = dev ;
require ( 'dns' ) . reverse ( addr , func ) ;
}
// Check if this device has any way to be activated in ACM using our server certificates.
function checkAcmActivation ( hello ) {
var domain = parent . config . domains [ hello . domainid ] ;
if ( ( domain == null ) || ( domain . amtacmactivation == null ) || ( domain . amtacmactivation . certs == null ) || ( domain . amtacmactivation . certs . length == 0 ) ) return null ;
const activationCerts = domain . amtacmactivation . certs ;
// Get the trusted FQDN of the device
var trustedFqdn = hello . trustedFqdn ;
// Find a matching certificate
for ( var i in activationCerts ) {
var cert = activationCerts [ i ] ;
if ( ( cert . cn == '*' ) || ( cert . cn == trustedFqdn ) ) {
for ( var j in hello . hashes ) {
var hash = hello . hashes [ j ] ;
if ( hash == cert . sha256 ) { return { cert : cert , fqdn : trustedFqdn , hash : cert . sha256 } ; } // Found a match
else if ( hash == cert . sha1 ) { return { cert : cert , fqdn : trustedFqdn , hash : cert . sha1 } ; } // Found a match
}
}
}
return null ; // Did not find a match
}
function processHelloDataEx1 ( dev ) {
// Get an activation certificate chain
const certinfo = checkAcmActivation ( dev ) ;
if ( certinfo == null ) { parent . debug ( 'amtsca' , dev . hostname , 'Unable to find a matching ACM activation certificate.' ) ; destroyDevice ( dev ) ; return ; }
2021-03-11 23:17:23 -05:00
var certchain = parent . certificateOperations . getAcmCertChain ( parent . config . domains [ dev . domainid ] , dev . trustedFqdn , certinfo . cert . sha256 ) ;
if ( certchain == null ) { parent . debug ( 'amtsca' , dev . hostname , 'Unable to create TLS certificate chain.' ) ; destroyDevice ( dev ) ; return ; }
dev . certchain = certchain ;
2021-03-11 21:23:05 -05:00
// Setup a connection to the Intel AMT device
parent . debug ( 'amtsca' , dev . hostname , 'Launching TLS connection...' ) ;
2021-03-11 23:17:23 -05:00
var comm = CreateWsmanComm ( dev . hostname , 16993 , 'admin' , '' , 1 , { cert : dev . certchain . certs . reverse ( ) . join ( '' ) , key : dev . certchain . signkey } ) ; // Perform TLS connection
2021-03-11 21:23:05 -05:00
comm . xtlsFingerprint = 0 ; // No Intel AMT certificate checking.
var wsstack = WsmanStackCreateService ( comm ) ;
dev . amtstack = AmtStackCreateService ( wsstack ) ;
dev . amtstack . dev = dev ;
dev . amtstack . BatchEnum ( null , [ '*AMT_GeneralSettings' , '*IPS_HostBasedSetupService' ] , processHelloDataEx2 ) ;
}
function processHelloDataEx2 ( stack , name , responses , status ) {
const dev = stack . dev ;
if ( status != 200 ) { parent . debug ( 'amtsca' , dev . hostname , 'Failed TLS connection, status=' + status + '.' ) ; destroyDevice ( dev ) ; return ; }
parent . debug ( 'amtsca' , dev . hostname , 'Succesful TLS connection.' ) ;
2021-03-11 23:17:23 -05:00
//console.log(JSON.stringify(dev.certinfo, null, 2));
console . log ( 'processHelloDataEx2' , status , responses ) ;
// TODO: Perform ACM activation
2021-03-11 21:23:05 -05:00
}
// Do aggressive cleanup on the device
function destroyDevice ( dev ) {
if ( dev . amtstack != null ) { delete dev . amtstack . dev ; delete dev . amtstack ; }
delete dev . certinfo ;
2021-03-10 22:01:10 -05:00
}
2021-03-11 04:12:58 -05:00
return obj ;
} ;