2019-03-05 10:48:45 +03:00
/ *
Copyright 2018 - 2019 Intel Corporation
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 .
* /
/ * *
* @ fileoverview Intel ( r ) AMT Management
* @ author Ylian Saint - Hilaire
* @ version v0 . 1.0
* /
/ * *
* Construct a AmtStackCreateService object , this ia the main Intel AMT communication stack .
* @ constructor
* /
function AmtManager ( agent , db , isdebug ) {
var sendConsole = function ( msg ) { agent . SendCommand ( { "action" : "msg" , "type" : "console" , "value" : msg } ) ; }
2019-03-11 07:40:25 +03:00
var debug = function ( msg ) { if ( isdebug ) { sendConsole ( 'amt-manager: ' + msg + '<br />' ) ; } }
2019-03-05 10:48:45 +03:00
var amtMei = null , amtMeiState = 0 ;
var amtLms = null , amtLmsState = 0 ;
var amtGetVersionResult = null ;
var oswsstack = null ;
var osamtstack = null ;
var amtpolicy = null ;
var obj = this ;
2019-06-15 02:33:53 +03:00
var mestate ;
2019-06-20 03:16:50 +03:00
var trustedHashes = null ; ;
2019-03-05 10:48:45 +03:00
obj . state = 0 ;
obj . lmsstate = 0 ;
obj . onStateChange = null ;
obj . setDebug = function ( x ) { isdebug = x ; }
2019-03-11 07:40:25 +03:00
2019-03-05 10:48:45 +03:00
// Set current Intel AMT activation policy
2019-06-21 02:56:19 +03:00
obj . setPolicy = function ( policy , forceApply ) {
if ( forceApply || ( JSON . stringify ( amtpolicy ) != JSON . stringify ( policy ) ) ) {
2019-03-05 10:48:45 +03:00
amtpolicy = policy ;
2019-06-21 02:56:19 +03:00
if ( applyPolicyTimer == null ) { applyPolicyTimer = setTimeout ( obj . applyPolicy , 8000 ) ; }
2019-03-05 10:48:45 +03:00
}
}
// Try to load up the MEI module
var rebindToMeiRetrys = 0 ;
obj . reset = function ( ) {
++ rebindToMeiRetrys ;
amtMei = null , amtMeiState = 0 , amtLms = null , amtLmsState = 0 , obj . state = 0 , obj . lmsstate = 0 ;
//debug('Binding to MEI');
try {
var amtMeiLib = require ( 'amt-mei' ) ;
amtMei = new amtMeiLib ( ) ;
2019-06-21 02:56:19 +03:00
amtMei . on ( 'error' , function ( e ) { debug ( 'MEI error' ) ; amtMei = null ; amtMeiState = - 1 ; obj . state = - 1 ; if ( obj . onStateChange != null ) { obj . onStateChange ( amtMeiState ) ; } } ) ;
2019-03-05 10:48:45 +03:00
amtMei . getVersion ( function ( result ) {
if ( result == null ) {
amtMeiState = - 1 ;
obj . state = - 1 ;
if ( obj . onStateChange != null ) { obj . onStateChange ( amtMeiState ) ; }
if ( rebindToMeiRetrys < 10 ) { setTimeout ( obj . reset , 10000 ) ; }
} else {
amtGetVersionResult = result ;
amtMeiState = 2 ;
obj . state = 2 ;
rebindToMeiRetrys = 0 ;
if ( obj . onStateChange != null ) { obj . onStateChange ( amtMeiState ) ; }
//debug('MEI binded');
obj . lmsreset ( ) ;
}
} ) ;
} catch ( ex ) { debug ( 'MEI exception: ' + ex ) ; amtMei = null ; amtMeiState = - 1 ; obj . state = - 1 ; }
}
// Get Intel AMT information using MEI
var amtMeiTmpState = null ;
obj . getAmtInfo = function ( func ) {
if ( ( amtMei == null ) || ( amtMeiState < 2 ) ) { if ( func != null ) { func ( null ) ; } return ; }
try {
amtMeiTmpState = { Flags : 0 } ; // Flags: 1=EHBC, 2=CCM, 4=ACM
amtMei . getProtocolVersion ( function ( result ) { if ( result != null ) { amtMeiTmpState . MeiVersion = result ; } } ) ;
amtMei . getVersion ( function ( result ) { if ( result ) { amtMeiTmpState . Versions = { } ; for ( var version in result . Versions ) { amtMeiTmpState . Versions [ result . Versions [ version ] . Description ] = result . Versions [ version ] . Version ; } } } ) ;
amtMei . getProvisioningMode ( function ( result ) { if ( result ) { amtMeiTmpState . ProvisioningMode = result . mode ; } } ) ;
amtMei . getProvisioningState ( function ( result ) { if ( result ) { amtMeiTmpState . ProvisioningState = result . state ; } } ) ;
amtMei . getEHBCState ( function ( result ) { if ( ( result != null ) && ( result . EHBC == true ) ) { amtMeiTmpState . Flags += 1 ; } } ) ;
2019-03-10 21:47:03 +03:00
amtMei . getControlMode ( function ( result ) { if ( result != null ) { if ( result . controlMode == 1 ) { amtMeiTmpState . Flags += 2 ; } if ( result . controlMode == 2 ) { amtMeiTmpState . Flags += 4 ; } } } ) ; // Flag 2 = CCM, 4 = ACM
2019-03-05 10:48:45 +03:00
//amtMei.getMACAddresses(function (result) { if (result) { amtMeiTmpState.mac = result; } });
amtMei . getLanInterfaceSettings ( 0 , function ( result ) { if ( result ) { amtMeiTmpState . net0 = result ; } } ) ;
amtMei . getLanInterfaceSettings ( 1 , function ( result ) { if ( result ) { amtMeiTmpState . net1 = result ; } } ) ;
amtMei . getUuid ( function ( result ) { if ( ( result != null ) && ( result . uuid != null ) ) { amtMeiTmpState . UUID = result . uuid ; } } ) ;
2019-06-15 02:33:53 +03:00
amtMei . getDnsSuffix ( function ( result ) { if ( result != null ) { amtMeiTmpState . DNS = result ; } if ( func != null ) { func ( amtMeiTmpState ) ; } } ) ;
2019-03-05 10:48:45 +03:00
} catch ( e ) { if ( func != null ) { func ( null ) ; } return ; }
}
// Called on MicroLMS Intel AMT user notification
var handleAmtNotification = function ( notifyMsg ) {
if ( ( notifyMsg == null ) || ( notifyMsg . Body == null ) || ( notifyMsg . Body . MessageID == null ) || ( notifyMsg . Body . MessageArguments == null ) ) return null ;
var amtMessage = notifyMsg . Body . MessageID , amtMessageArg = notifyMsg . Body . MessageArguments [ 0 ] , notify = null ;
switch ( amtMessage ) {
case 'iAMT0050' : { if ( amtMessageArg == '48' ) { notify = 'Intel® AMT Serial-over-LAN connected' ; } else if ( amtMessageArg == '49' ) { notify = 'Intel® AMT Serial-over-LAN disconnected' ; } break ; } // SOL
case 'iAMT0052' : { if ( amtMessageArg == '1' ) { notify = 'Intel® AMT KVM connected' ; } else if ( amtMessageArg == '2' ) { notify = 'Intel® AMT KVM disconnected' ; } break ; } // KVM
default : { break ; }
}
// Sent to the entire group, no sessionid or userid specified.
2019-07-19 01:21:41 +03:00
if ( notify != null ) { agent . SendCommand ( { "action" : "msg" , "type" : "notify" , "value" : notify , "tag" : "general" , "amtMessage" : amtMessage } ) ; }
2019-03-05 10:48:45 +03:00
}
// Launch LMS
obj . lmsreset = function ( ) {
//debug('Binding to LMS');
var amtLms = null , amtLmsState = 0 ;
obj . lmsstate = 0 ;
try {
var lme _heci = require ( 'amt-lme' ) ;
amtLmsState = 1 ;
obj . lmsstate = 1 ;
amtLms = new lme _heci ( ) ;
amtLms . on ( 'error' , function ( e ) { amtLmsState = 0 ; obj . lmsstate = 0 ; amtLms = null ; debug ( 'LMS error' ) ; setupMeiOsAdmin ( 1 ) ; } ) ;
amtLms . on ( 'connect' , function ( ) { amtLmsState = 2 ; obj . lmsstate = 2 ; debug ( 'LMS connected' ) ; setupMeiOsAdmin ( 2 ) ; } ) ;
//amtLms.on('bind', function (map) { });
amtLms . on ( 'notify' , function ( data , options , str , code ) {
//debug('LMS notify');
if ( code == 'iAMT0052-3' ) {
kvmGetData ( ) ;
} else {
//if (str != null) { debug('Intel AMT LMS: ' + str); }
handleAmtNotification ( data ) ;
}
} ) ;
} catch ( e ) { amtLmsState = - 1 ; obj . lmsstate = - 1 ; amtLms = null ; }
}
//
// KVM Data Channel
//
var setupMeiOsAdmin = function ( state ) {
//debug('Setup MEI OS Admin');
if ( ( amtMei == null ) || ( amtMeiState < 2 ) || ( amtGetVersionResult == null ) ) { return ; } // If there is no MEI, don't bother with obj.
amtMei . getLocalSystemAccount ( function ( x ) {
if ( x == null ) return ;
//debug('getLocalSystemAccount ' + JSON.stringify(x));
var transport = require ( 'amt-wsman-duk' ) ;
var wsman = require ( 'amt-wsman' ) ;
var amt = require ( 'amt' ) ;
oswsstack = new wsman ( transport , '127.0.0.1' , 16992 , x . user , x . pass , false ) ;
osamtstack = new amt ( oswsstack ) ;
//if (func) { func(state); }
// We got the $$OsAdmin account setup.
amtMeiState = 3 ;
obj . state = 3 ;
if ( obj . onStateChange != null ) { obj . onStateChange ( amtMeiState ) ; }
2019-03-11 07:40:25 +03:00
if ( applyPolicyTimer == null ) { obj . applyPolicy ( ) ; }
2019-03-05 10:48:45 +03:00
//var AllWsman = "CIM_SoftwareIdentity,IPS_SecIOService,IPS_ScreenSettingData,IPS_ProvisioningRecordLog,IPS_HostBasedSetupService,IPS_HostIPSettings,IPS_IPv6PortSettings".split(',');
//osamtstack.BatchEnum(null, AllWsman, startLmsWsmanResponse, null, true);
//*************************************
// Setup KVM data channel if this is Intel AMT 12 or above
var amtver = null ;
try { for ( var i in amtGetVersionResult . Versions ) { if ( amtGetVersionResult . Versions [ i ] . Description == 'AMT' ) amtver = parseInt ( amtGetVersionResult . Versions [ i ] . Version . split ( '.' ) [ 0 ] ) ; } } catch ( e ) { }
if ( ( amtver != null ) && ( amtver >= 12 ) ) {
2019-06-15 02:33:53 +03:00
//debug('KVM data channel setup');
2019-03-05 10:48:45 +03:00
kvmGetData ( 'skip' ) ; // Clear any previous data, this is a dummy read to about handling old data.
obj . kvmTempTimer = setInterval ( function ( ) { kvmGetData ( ) ; } , 2000 ) ; // Start polling for KVM data.
kvmSetData ( JSON . stringify ( { action : 'restart' , ver : 1 } ) ) ; // Send a restart command to advise the console if present that MicroLMS just started.
}
} ) ;
}
var kvmGetData = function ( tag ) {
osamtstack . IPS _KVMRedirectionSettingData _DataChannelRead ( obj . kvmDataGetResponse , tag ) ;
}
var kvmDataGetResponse = function ( stack , name , response , status , tag ) {
if ( ( tag != 'skip' ) && ( status == 200 ) && ( response . Body . ReturnValue == 0 ) ) {
var val = null ;
try { val = Buffer . from ( response . Body . DataMessage , 'base64' ) . toString ( ) ; } catch ( e ) { return }
if ( val != null ) { obj . kvmProcessData ( response . Body . RealmsBitmap , response . Body . MessageId , val ) ; }
}
}
var webRtcDesktop = null ;
var kvmProcessData = function ( realms , messageId , val ) {
var data = null ;
try { data = JSON . parse ( val ) } catch ( e ) { }
if ( ( data != null ) && ( data . action ) ) {
if ( data . action == 'present' ) { kvmSetData ( JSON . stringify ( { action : 'present' , ver : 1 , platform : process . platform } ) ) ; }
if ( data . action == 'offer' ) {
webRtcDesktop = { } ;
var rtc = require ( 'ILibWebRTC' ) ;
webRtcDesktop . webrtc = rtc . createConnection ( ) ;
webRtcDesktop . webrtc . on ( 'connected' , function ( ) { } ) ;
webRtcDesktop . webrtc . on ( 'disconnected' , function ( ) { obj . webRtcCleanUp ( ) ; } ) ;
webRtcDesktop . webrtc . on ( 'dataChannel' , function ( rtcchannel ) {
webRtcDesktop . rtcchannel = rtcchannel ;
webRtcDesktop . kvm = mesh . getRemoteDesktopStream ( ) ;
webRtcDesktop . kvm . pipe ( webRtcDesktop . rtcchannel , { dataTypeSkip : 1 , end : false } ) ;
webRtcDesktop . rtcchannel . on ( 'end' , function ( ) { obj . webRtcCleanUp ( ) ; } ) ;
webRtcDesktop . rtcchannel . on ( 'data' , function ( x ) { obj . kvmCtrlData ( this , x ) ; } ) ;
webRtcDesktop . rtcchannel . pipe ( webRtcDesktop . kvm , { dataTypeSkip : 1 , end : false } ) ;
//webRtcDesktop.kvm.on('end', function () { debug('WebRTC DataChannel closed2'); obj.webRtcCleanUp(); });
//webRtcDesktop.rtcchannel.on('data', function (data) { debug('WebRTC data: ' + data); });
} ) ;
kvmSetData ( JSON . stringify ( { action : 'answer' , ver : 1 , sdp : webRtcDesktop . webrtc . setOffer ( data . sdp ) } ) ) ;
}
}
}
// Process KVM control channel data
var kvmCtrlData = function ( channel , cmd ) {
if ( cmd . length > 0 && cmd . charCodeAt ( 0 ) != 123 ) {
// This is upload data
if ( obj . fileupload != null ) {
cmd = Buffer . from ( cmd , 'base64' ) ;
var header = cmd . readUInt32BE ( 0 ) ;
if ( ( header == 0x01000000 ) || ( header == 0x01000001 ) ) {
fs . writeSync ( obj . fileupload . fp , cmd . slice ( 4 ) ) ;
channel . write ( { action : 'upload' , sub : 'ack' , reqid : obj . fileupload . reqid } ) ;
if ( header == 0x01000001 ) { fs . closeSync ( obj . fileupload . fp ) ; obj . fileupload = null ; } // Close the file
}
}
return ;
}
debug ( 'KVM Ctrl Data: ' + cmd ) ;
//sendConsoleText('KVM Ctrl Data: ' + cmd);
try { cmd = JSON . parse ( cmd ) ; } catch ( ex ) { debug ( 'Invalid JSON: ' + cmd ) ; return ; }
if ( ( cmd . path != null ) && ( process . platform != 'win32' ) && ( cmd . path [ 0 ] != '/' ) ) { cmd . path = '/' + cmd . path ; } // Add '/' to paths on non-windows
switch ( cmd . action ) {
case 'ping' : {
// This is a keep alive
channel . write ( { action : 'pong' } ) ;
break ;
}
case 'lock' : {
// Lock the current user out of the desktop
if ( process . platform == 'win32' ) { var child = require ( 'child_process' ) ; child . execFile ( process . env [ 'windir' ] + '\\system32\\cmd.exe' , [ '/c' , 'RunDll32.exe user32.dll,LockWorkStation' ] , { type : 1 } ) ; }
break ;
}
case 'ls' : {
/ *
// Close the watcher if required
var samepath = ( ( obj . httprequest . watcher != undefined ) && ( cmd . path == obj . httprequest . watcher . path ) ) ;
if ( ( obj . httprequest . watcher != undefined ) && ( samepath == false ) ) {
//console.log('Closing watcher: ' + obj.httprequest.watcher.path);
//obj.httprequest.watcher.close(); // TODO: This line causes the agent to crash!!!!
delete obj . httprequest . watcher ;
}
* /
// Send the folder content to the browser
var response = getDirectoryInfo ( cmd . path ) ;
if ( cmd . reqid != undefined ) { response . reqid = cmd . reqid ; }
channel . write ( response ) ;
/ *
// Start the directory watcher
if ( ( cmd . path != '' ) && ( samepath == false ) ) {
var watcher = fs . watch ( cmd . path , onFileWatcher ) ;
watcher . tunnel = obj . httprequest ;
watcher . path = cmd . path ;
obj . httprequest . watcher = watcher ;
//console.log('Starting watcher: ' + obj.httprequest.watcher.path);
}
* /
break ;
}
case 'mkdir' : {
// Create a new empty folder
fs . mkdirSync ( cmd . path ) ;
break ;
}
case 'rm' : {
// Remove many files or folders
for ( var i in cmd . delfiles ) {
var fullpath = path . join ( cmd . path , cmd . delfiles [ i ] ) ;
try { fs . unlinkSync ( fullpath ) ; } catch ( e ) { debug ( e ) ; }
}
break ;
}
case 'rename' : {
// Rename a file or folder
try { fs . renameSync ( path . join ( cmd . path , cmd . oldname ) , path . join ( cmd . path , cmd . newname ) ) ; } catch ( e ) { debug ( e ) ; }
break ;
}
case 'download' : {
// Download a file, to browser
var sendNextBlock = 0 ;
if ( cmd . sub == 'start' ) { // Setup the download
if ( obj . filedownload != null ) { channel . write ( { action : 'download' , sub : 'cancel' , id : obj . filedownload . id } ) ; delete obj . filedownload ; }
obj . filedownload = { id : cmd . id , path : cmd . path , ptr : 0 }
try { obj . filedownload . f = fs . openSync ( obj . filedownload . path , 'rbN' ) ; } catch ( e ) { channel . write ( { action : 'download' , sub : 'cancel' , id : obj . filedownload . id } ) ; delete obj . filedownload ; }
if ( obj . filedownload ) { channel . write ( { action : 'download' , sub : 'start' , id : cmd . id } ) ; }
} else if ( ( obj . filedownload != null ) && ( cmd . id == obj . filedownload . id ) ) { // Download commands
if ( cmd . sub == 'startack' ) { sendNextBlock = 8 ; } else if ( cmd . sub == 'stop' ) { delete obj . filedownload ; } else if ( cmd . sub == 'ack' ) { sendNextBlock = 1 ; }
}
// Send the next download block(s)
while ( sendNextBlock > 0 ) {
sendNextBlock -- ;
var buf = Buffer . alloc ( 4096 ) ;
var len = fs . readSync ( obj . filedownload . f , buf , 4 , 4092 , null ) ;
obj . filedownload . ptr += len ;
if ( len < 4092 ) { buf . writeInt32BE ( 0x01000001 , 0 ) ; fs . closeSync ( obj . filedownload . f ) ; delete obj . filedownload ; sendNextBlock = 0 ; } else { buf . writeInt32BE ( 0x01000000 , 0 ) ; }
channel . write ( buf . slice ( 0 , len + 4 ) . toString ( 'base64' ) ) ; // Write as Base64
}
break ;
}
case 'upload' : {
// Upload a file, from browser
if ( cmd . sub == 'start' ) { // Start the upload
if ( obj . fileupload != null ) { fs . closeSync ( obj . fileupload . fp ) ; }
if ( ! cmd . path || ! cmd . name ) break ;
obj . fileupload = { reqid : cmd . reqid } ;
var filepath = path . join ( cmd . path , cmd . name ) ;
try { obj . fileupload . fp = fs . openSync ( filepath , 'wbN' ) ; } catch ( e ) { }
if ( obj . fileupload . fp ) { channel . write ( { action : 'upload' , sub : 'start' , reqid : obj . fileupload . reqid } ) ; } else { obj . fileupload = null ; channel . write ( { action : 'upload' , sub : 'error' , reqid : obj . fileupload . reqid } ) ; }
}
else if ( cmd . sub == 'cancel' ) { // Stop the upload
if ( obj . fileupload != null ) { fs . closeSync ( obj . fileupload . fp ) ; obj . fileupload = null ; }
}
break ;
}
case 'copy' : {
// Copy a bunch of files from scpath to dspath
for ( var i in cmd . names ) {
var sc = path . join ( cmd . scpath , cmd . names [ i ] ) , ds = path . join ( cmd . dspath , cmd . names [ i ] ) ;
if ( sc != ds ) { try { fs . copyFileSync ( sc , ds ) ; } catch ( e ) { } }
}
break ;
}
case 'move' : {
// Move a bunch of files from scpath to dspath
for ( var i in cmd . names ) {
var sc = path . join ( cmd . scpath , cmd . names [ i ] ) , ds = path . join ( cmd . dspath , cmd . names [ i ] ) ;
if ( sc != ds ) { try { fs . copyFileSync ( sc , ds ) ; fs . unlinkSync ( sc ) ; } catch ( e ) { } }
}
break ;
}
default : {
debug ( 'Invalid KVM command: ' + cmd ) ;
break ;
}
}
}
var webRtcCleanUp = function ( ) {
debug ( 'webRtcCleanUp' ) ;
if ( webRtcDesktop == null ) return ;
if ( webRtcDesktop . rtcchannel ) {
try { webRtcDesktop . rtcchannel . close ( ) ; } catch ( e ) { }
try { webRtcDesktop . rtcchannel . removeAllListeners ( 'data' ) ; } catch ( e ) { }
try { webRtcDesktop . rtcchannel . removeAllListeners ( 'end' ) ; } catch ( e ) { }
delete webRtcDesktop . rtcchannel ;
}
if ( webRtcDesktop . webrtc ) {
try { webRtcDesktop . webrtc . close ( ) ; } catch ( e ) { }
try { webRtcDesktop . webrtc . removeAllListeners ( 'connected' ) ; } catch ( e ) { }
try { webRtcDesktop . webrtc . removeAllListeners ( 'disconnected' ) ; } catch ( e ) { }
try { webRtcDesktop . webrtc . removeAllListeners ( 'dataChannel' ) ; } catch ( e ) { }
delete webRtcDesktop . webrtc ;
}
if ( webRtcDesktop . kvm ) {
try { webRtcDesktop . kvm . end ( ) ; } catch ( e ) { }
delete webRtcDesktop . kvm ;
}
webRtcDesktop = null ;
}
var kvmSetData = function ( x ) {
osamtstack . IPS _KVMRedirectionSettingData _DataChannelWrite ( Buffer . from ( x ) . toString ( 'base64' ) , function ( ) { } ) ;
}
// Delete a directory with a files and directories within it
var deleteFolderRecursive = function ( path , rec ) {
if ( fs . existsSync ( path ) ) {
if ( rec == true ) {
fs . readdirSync ( obj . path . join ( path , '*' ) ) . forEach ( function ( file , index ) {
var curPath = obj . path . join ( path , file ) ;
if ( fs . statSync ( curPath ) . isDirectory ( ) ) { // recurse
deleteFolderRecursive ( curPath , true ) ;
} else { // delete file
fs . unlinkSync ( curPath ) ;
}
} ) ;
}
fs . unlinkSync ( path ) ;
}
} ;
// Polyfill path.join
var path = {
join : function ( ) {
var x = [ ] ;
for ( var i in arguments ) {
var w = arguments [ i ] ;
if ( w != null ) {
while ( w . endsWith ( '/' ) || w . endsWith ( '\\' ) ) { w = w . substring ( 0 , w . length - 1 ) ; }
if ( i != 0 ) { while ( w . startsWith ( '/' ) || w . startsWith ( '\\' ) ) { w = w . substring ( 1 ) ; } }
x . push ( w ) ;
}
}
if ( x . length == 0 ) return '/' ;
return x . join ( '/' ) ;
}
} ;
function md5hex ( str ) { return require ( 'MD5Stream' ) . create ( ) . syncHash ( str ) . toString ( 'hex' ) ; }
//
// Deactivate Intel AMT CCM
//
// When called, this will use MEI to deactivate Intel AMT when it's in CCM mode. Simply calls "unprovision" on MEI and checks the return code.
obj . deactivateCCM = function ( ) {
amtMei . unprovision ( 1 , function ( status ) {
if ( status == 0 ) {
debug ( 'Success deactivating Intel AMT CCM.' ) ;
agent . SendCommand ( { "action" : "coreinfo" , "intelamt" : { "state" : 0 , "flags" : 0 } } ) ;
applyPolicyTimer = setTimeout ( obj . applyPolicy , 8000 ) ;
} else {
debug ( 'Intel AMT CCM deactivation error: ' + status ) ;
}
} ) ;
}
2019-06-20 03:16:50 +03:00
//
// Get Intel AMT activation hashes
//
obj . getTrustedHashes = function ( func , tag ) {
if ( trustedHashes != null ) { func ( tag ) ; }
trustedHashes = [ ] ;
amtMei . getHashHandles ( function ( handles ) {
var exitOnCount = handles . length ;
for ( var i = 0 ; i < handles . length ; ++ i ) {
this . getCertHashEntry ( handles [ i ] , function ( result ) {
if ( result . isActive == 1 ) { trustedHashes . push ( result . certificateHash . toLowerCase ( ) ) ; }
if ( -- exitOnCount == 0 ) { func ( tag ) ; }
} ) ;
}
} ) ;
}
2019-06-15 02:33:53 +03:00
//
// Activate Intel AMT to ACM
//
obj . activeToACM = function ( mestate ) {
2019-06-20 03:16:50 +03:00
if ( ( mestate . ProvisioningState != 0 ) || ( amtpolicy == null ) || ( amtpolicy . match == null ) ) return ; // Can't activate unless in "PRE" activation mode & policy is present.
2019-06-15 02:33:53 +03:00
var trustedFqdn = null ;
if ( ( mestate . net0 == null ) && ( mestate . net0 . enabled != 0 ) ) return ; // Can't activate unless wired interface is active
if ( mestate . DNS ) { trustedFqdn = mestate . DNS ; } // If Intel AMT has a trusted DNS suffix set, use that one.
else {
// Look for the DNS suffix for the Intel AMT Ethernet interface
var interfaces = require ( 'os' ) . networkInterfaces ( ) ;
for ( var i in interfaces ) {
for ( var j in interfaces [ i ] ) {
2019-06-20 03:16:50 +03:00
if ( ( interfaces [ i ] [ j ] . mac == mestate . net0 . mac ) && ( interfaces [ i ] [ j ] . fqdn != null ) && ( interfaces [ i ] [ j ] . fqdn != '' ) ) { trustedFqdn = interfaces [ i ] [ j ] . fqdn . toLowerCase ( ) ; }
2019-06-15 02:33:53 +03:00
}
}
}
2019-06-20 03:59:03 +03:00
if ( trustedFqdn == null ) return ; // No trusted DNS suffix.
2019-06-20 03:16:50 +03:00
// Check if we have a ACM policy match
2019-06-20 03:59:03 +03:00
var hashMatch = null ;
2019-06-20 03:16:50 +03:00
for ( var i in amtpolicy . match ) { var m = amtpolicy . match [ i ] ; if ( m . cn == trustedFqdn ) { for ( var j in trustedHashes ) { if ( ( trustedHashes [ j ] == m . sha256 ) || ( trustedHashes [ j ] == m . sha1 ) ) { hashMatch = trustedHashes [ j ] ; } } } }
if ( hashMatch == null ) return ; // No certificate / FQDN match
2019-06-15 02:33:53 +03:00
// Fetch Intel AMT realm and activation nonce and get ready to ACM activation...
if ( osamtstack != null ) {
2019-06-21 00:27:57 +03:00
osamtstack . BatchEnum ( null , [ '*AMT_GeneralSettings' , '*IPS_HostBasedSetupService' ] , activeToACM2 , { fqdn : trustedFqdn , hash : hashMatch , uuid : mestate . UUID } ) ;
2019-06-15 02:33:53 +03:00
} else {
amtMei . getLocalSystemAccount ( function ( x ) {
if ( ( x != null ) && x . user && x . pass ) {
var transport = require ( 'amt-wsman-duk' ) ;
var wsman = require ( 'amt-wsman' ) ;
var amt = require ( 'amt' ) ;
oswsstack = new wsman ( transport , '127.0.0.1' , 16992 , x . user , x . pass , false ) ;
osamtstack = new amt ( oswsstack ) ;
2019-06-21 00:27:57 +03:00
osamtstack . BatchEnum ( null , [ '*AMT_GeneralSettings' , '*IPS_HostBasedSetupService' ] , activeToACM2 , { fqdn : trustedFqdn , hash : hashMatch , uuid : mestate . UUID } ) ;
2019-06-15 02:33:53 +03:00
}
} ) ;
}
}
2019-06-20 03:16:50 +03:00
function activeToACM2 ( stack , name , responses , status , tag ) {
2019-06-15 02:33:53 +03:00
if ( status != 200 ) return ;
var fwNonce = responses [ 'IPS_HostBasedSetupService' ] . response [ 'ConfigurationNonce' ] ;
var digestRealm = responses [ 'AMT_GeneralSettings' ] . response [ 'DigestRealm' ] ;
2019-06-21 00:27:57 +03:00
agent . SendCommand ( { "action" : "acmactivate" , "nonce" : fwNonce , "realm" : digestRealm , "fqdn" : tag . fqdn , "hash" : tag . hash , "uuid" : tag . uuid } ) ;
2019-06-20 03:16:50 +03:00
}
// Called when the server responds with a ACM activation signature.
2019-06-21 02:56:19 +03:00
obj . setAcmResponse = function ( acmdata ) { acmdata . index = 0 ; performAcmActivation ( acmdata ) ; }
2019-06-20 03:16:50 +03:00
// Recursive function to inject the provisioning certificates into AMT in the proper order and completes ACM activation
function performAcmActivation ( acmdata ) {
2019-06-20 03:59:03 +03:00
var leaf = ( acmdata . index == 0 ) , root = ( acmdata . index == ( acmdata . certs . length - 1 ) ) ;
if ( ( acmdata . index < acmdata . certs . length ) && ( acmdata . certs [ acmdata . index ] != null ) ) {
osamtstack . IPS _HostBasedSetupService _AddNextCertInChain ( acmdata . certs [ acmdata . index ] , leaf , root , function ( stack , name , responses , status ) {
if ( status !== 200 ) { debug ( 'AddNextCertInChain status=' + status ) ; return ; }
else if ( responses [ 'Body' ] [ 'ReturnValue' ] !== 0 ) { debug ( 'AddNextCertInChain error=' + responses [ 'Body' ] [ 'ReturnValue' ] ) ; return ; }
else { acmdata . index ++ ; performAcmActivation ( acmdata ) ; }
} ) ;
2019-06-20 03:16:50 +03:00
} else {
osamtstack . IPS _HostBasedSetupService _AdminSetup ( 2 , acmdata . password , acmdata . nonce , 2 , acmdata . signature ,
2019-06-20 03:59:03 +03:00
function ( stack , name , responses , status ) {
2019-06-21 02:56:19 +03:00
if ( ( status == 200 ) && ( responses [ 'Body' ] [ 'ReturnValue' ] == 0 ) ) {
// ACM activation success, force an update to the server so it can get our new state.
if ( obj . onStateChange != null ) { obj . onStateChange ( 2 ) ; }
}
2019-06-20 03:59:03 +03:00
}
2019-06-20 03:16:50 +03:00
) ;
}
2019-06-15 02:33:53 +03:00
}
2019-03-05 10:48:45 +03:00
//
// Activate Intel AMT to CCM
//
function makePass ( length ) {
var text = "" , possible = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789" ;
for ( var i = 0 ; i < length ; i ++ ) { text += possible . charAt ( Math . floor ( Math . random ( ) * possible . length ) ) ; }
return text ;
}
obj . activeToCCM = function ( adminpass ) {
if ( ( adminpass == null ) || ( adminpass == '' ) ) { adminpass = 'P@0s' + makePass ( 23 ) ; }
intelAmtAdminPass = adminpass ;
2019-06-15 02:33:53 +03:00
if ( osamtstack != null ) {
osamtstack . BatchEnum ( null , [ '*AMT_GeneralSettings' , '*IPS_HostBasedSetupService' ] , activeToCCMEx2 , adminpass ) ;
} else {
//debug('Trying to get local account info...');
amtMei . getLocalSystemAccount ( function ( x ) {
if ( ( x != null ) && x . user && x . pass ) {
//debug('Intel AMT local account info: User=' + x.user + ', Pass=' + x.pass + '.');
var transport = require ( 'amt-wsman-duk' ) ;
var wsman = require ( 'amt-wsman' ) ;
var amt = require ( 'amt' ) ;
oswsstack = new wsman ( transport , '127.0.0.1' , 16992 , x . user , x . pass , false ) ;
osamtstack = new amt ( oswsstack ) ;
//debug('Trying to get Intel AMT activation information...');
osamtstack . BatchEnum ( null , [ '*AMT_GeneralSettings' , '*IPS_HostBasedSetupService' ] , activeToCCMEx2 , adminpass ) ;
} else {
//debug('Unable to get $$OsAdmin password.');
}
} ) ;
}
2019-03-05 10:48:45 +03:00
}
var activeToCCMEx2 = function ( stack , name , responses , status , adminpass ) {
if ( status != 200 ) { debug ( 'Failed to fetch activation information, status ' + status ) ; }
else if ( responses [ 'IPS_HostBasedSetupService' ] . response [ 'AllowedControlModes' ] . length != 2 ) { debug ( 'Client control mode activation not allowed' ) ; }
else { stack . IPS _HostBasedSetupService _Setup ( 2 , md5hex ( 'admin:' + responses [ 'AMT_GeneralSettings' ] . response [ 'DigestRealm' ] + ':' + adminpass ) . substring ( 0 , 32 ) , null , null , null , null , activeToCCMEx3 ) ; }
}
var activeToCCMEx3 = function ( stack , name , responses , status ) {
if ( status != 200 ) { debug ( 'Failed to activate, status ' + status ) ; }
else if ( responses . Body . ReturnValue != 0 ) { debug ( 'Client control mode activation failed: ' + responses . Body . ReturnValueStr ) ; }
else {
debug ( 'Intel AMT CCM activation success.' ) ;
db . Put ( 'amtCCMPass' , intelAmtAdminPass ) ;
agent . SendCommand ( { "action" : "coreinfo" , "intelamt" : { "state" : 2 , "flags" : 2 , "user" : "admin" , "pass" : intelAmtAdminPass } } ) ;
}
applyPolicyTimer = setTimeout ( obj . applyPolicy , 8000 ) ;
}
obj . start = function ( ) {
// Try to load Intel AMT policy
var amtPolicy = null ;
try { amtPolicy = JSON . parse ( db . Get ( 'amtPolicy' ) ) ; } catch ( ex ) { debug ( 'Exception loading amtPolicy' ) ; }
//if (amtPolicy == null) { debug('no amtPolicy'); } else { debug('Loaded amtPolicy: ' + JSON.stringify(amtPolicy)); }
try { intelAmtAdminPass = db . Get ( 'amtCCMPass' ) ; } catch ( ex ) { }
if ( typeof intelAmtAdminPass != 'string' ) { intelAmtAdminPass = null ; }
obj . reset ( ) ;
}
// Apply Intel AMT policy
2019-03-11 07:40:25 +03:00
var intelAmtAdminPass , wsstack , amtstack , applyPolicyTimer , policyWsmanRetry = 0 ;
2019-03-05 10:48:45 +03:00
obj . applyPolicy = function ( ) {
applyPolicyTimer = null ;
2019-03-27 22:55:31 +03:00
if ( ( amtMeiState != 3 ) || ( amtpolicy == null ) || ( typeof amtpolicy != 'object' ) || ( typeof amtpolicy . type != 'number' ) || ( amtpolicy . type == 0 ) ) return ;
2019-03-05 10:48:45 +03:00
if ( ( amtpolicy . password != null ) && ( amtpolicy . password != '' ) ) { intelAmtAdminPass = amtpolicy . password ; }
obj . getAmtInfo ( function ( meinfo ) {
2019-03-10 21:47:03 +03:00
if ( ( amtpolicy . type == 1 ) && ( meinfo . ProvisioningState == 2 ) && ( ( meinfo . Flags & 2 ) != 0 ) ) {
2019-03-05 10:48:45 +03:00
// CCM Deactivation Policy.
2019-06-20 03:16:50 +03:00
wsstack = amtstack = null ;
2019-03-05 10:48:45 +03:00
obj . deactivateCCM ( ) ;
} else if ( ( amtpolicy . type == 2 ) && ( meinfo . ProvisioningState == 0 ) ) {
// CCM Activation Policy
2019-06-20 03:16:50 +03:00
wsstack = amtstack = null ;
2019-03-05 10:48:45 +03:00
if ( ( amtpolicy . password == null ) || ( amtpolicy . password == '' ) ) { intelAmtAdminPass = null ; }
obj . activeToCCM ( intelAmtAdminPass ) ;
2019-03-10 21:47:03 +03:00
} else if ( ( amtpolicy . type == 2 ) && ( meinfo . ProvisioningState == 2 ) && ( intelAmtAdminPass != null ) && ( ( meinfo . Flags & 2 ) != 0 ) ) {
2019-03-05 10:48:45 +03:00
// Perform password test
var transport = require ( 'amt-wsman-duk' ) ;
var wsman = require ( 'amt-wsman' ) ;
var amt = require ( 'amt' ) ;
wsstack = new wsman ( transport , '127.0.0.1' , 16992 , 'admin' , intelAmtAdminPass , false ) ;
amtstack = new amt ( wsstack ) ;
2019-03-11 07:40:25 +03:00
var wsmanQuery = [ '*AMT_GeneralSettings' , '*IPS_HostBasedSetupService' , '*AMT_RedirectionService' , '*CIM_KVMRedirectionSAP' , 'AMT_PublicKeyCertificate' , '*AMT_EnvironmentDetectionSettingData' ] ;
if ( amtpolicy . cirasetup == 2 ) { wsmanQuery . push ( "AMT_ManagementPresenceRemoteSAP" , "AMT_RemoteAccessCredentialContext" , "AMT_RemoteAccessPolicyAppliesToMPS" , "AMT_RemoteAccessPolicyRule" , "*AMT_UserInitiatedConnectionService" , "AMT_MPSUsernamePassword" ) ; }
try { amtstack . BatchEnum ( null , wsmanQuery , wsmanPassTestResponse ) ; } catch ( ex ) { debug ( ex ) ; }
2019-06-21 02:56:19 +03:00
} else if ( ( amtpolicy . type == 3 ) && ( meinfo . ProvisioningState == 0 ) && ( agent . isControlChannelConnected ) ) {
2019-06-14 02:39:21 +03:00
// ACM Activation Policy
2019-06-20 03:16:50 +03:00
obj . getTrustedHashes ( obj . activeToACM , meinfo ) ;
2019-03-05 10:48:45 +03:00
} else {
// Other possible cases...
}
} ) ;
}
2019-03-11 07:40:25 +03:00
function wsmanPassTestResponse ( stack , name , responses , status ) {
2019-03-05 10:48:45 +03:00
if ( status != 200 ) {
2019-03-11 07:40:25 +03:00
if ( status == 401 ) {
if ( amtpolicy . badpass == 1 ) { obj . deactivateCCM ( ) ; } // Incorrect password, reactivate
} else {
if ( ++ policyWsmanRetry < 20 ) {
if ( policyWsmanRetry == 10 ) { debug ( 'WSMAN fault, MEI Reset' ) ; obj . reset ( ) ; }
var wsmanQuery = [ '*AMT_GeneralSettings' , '*IPS_HostBasedSetupService' , '*AMT_RedirectionService' , '*CIM_KVMRedirectionSAP' , 'AMT_PublicKeyCertificate' , '*AMT_EnvironmentDetectionSettingData' ] ;
if ( amtpolicy . cirasetup == 2 ) { wsmanQuery . push ( "AMT_ManagementPresenceRemoteSAP" , "AMT_RemoteAccessCredentialContext" , "AMT_RemoteAccessPolicyAppliesToMPS" , "AMT_RemoteAccessPolicyRule" , "*AMT_UserInitiatedConnectionService" , "AMT_MPSUsernamePassword" ) ; }
try { amtstack . BatchEnum ( null , wsmanQuery , wsmanPassTestResponse ) ; } catch ( ex ) { debug ( ex ) ; }
} else {
debug ( 'WSMAN fault, status=' + status ) ;
policyWsmanRetry = 0 ;
}
}
2019-03-05 10:48:45 +03:00
} else {
2019-03-11 07:40:25 +03:00
policyWsmanRetry = 0 ;
var s = { } ;
s . redir = ( responses [ 'AMT_RedirectionService' ] . response [ "ListenerEnabled" ] == true ) ;
s . sol = ( ( responses [ 'AMT_RedirectionService' ] . response [ "EnabledState" ] & 2 ) != 0 ) ;
s . ider = ( ( responses [ 'AMT_RedirectionService' ] . response [ "EnabledState" ] & 1 ) != 0 ) ;
s . kvm = ( responses [ 'CIM_KVMRedirectionSAP' ] != null ) && ( ( responses [ 'CIM_KVMRedirectionSAP' ] . response [ "EnabledState" ] == 6 && responses [ 'CIM_KVMRedirectionSAP' ] . response [ "RequestedState" ] == 2 ) || responses [ 'CIM_KVMRedirectionSAP' ] . response [ "EnabledState" ] == 2 || responses [ 'CIM_KVMRedirectionSAP' ] . response [ "EnabledState" ] == 6 ) ;
// Enable Ping and RMCP if disabled
if ( ( responses [ 'AMT_GeneralSettings' ] . response [ 'PingResponseEnabled' ] != true ) || ( responses [ 'AMT_GeneralSettings' ] . response [ 'RmcpPingResponseEnabled' ] != true ) ) {
responses [ 'AMT_GeneralSettings' ] . response [ 'PingResponseEnabled' ] = true ;
responses [ 'AMT_GeneralSettings' ] . response [ 'RmcpPingResponseEnabled' ] = true ;
amtstack . Put ( 'AMT_GeneralSettings' , responses [ 'AMT_GeneralSettings' ] . response , function ( stack , name , response , status ) { if ( status != 200 ) { debug ( "Enable PING PUT Error " + status ) ; } } , 0 , 1 )
}
// Enable redirection port, SOL and IDER if needed
if ( ( s . redir == false ) || ( s . sol == false ) || ( s . ider == false ) ) {
var r = responses [ 'AMT_RedirectionService' ] . response ;
r [ "ListenerEnabled" ] = true ; // Turn on the redirection port
r [ "EnabledState" ] = 32768 + 1 + 2 ; // Turn on IDER (1) and SOL (2)
amtstack . AMT _RedirectionService _RequestStateChange ( r [ "EnabledState" ] , function ( stack , name , response , status ) { if ( status != 200 ) { debug ( "Enable Redirection EXEC Error " + status ) ; } } ) ;
}
// Enable KVM if needed
if ( ( responses [ 'CIM_KVMRedirectionSAP' ] != null ) && ( s . kvm == false ) ) {
amtstack . CIM _KVMRedirectionSAP _RequestStateChange ( 2 , 0 ,
function ( stack , name , response , status ) {
if ( status != 200 ) { messagebox ( "Error" , "KVMRedirectionSAP, RequestStateChange Error " + status ) ; return ; }
amtstack . Put ( "AMT_RedirectionService" , r , function ( stack , name , response , status ) { if ( status != 200 ) { debug ( "Enable KVM PUT Error " + status ) ; } } , 0 , 1 )
}
) ;
}
// Check if the MeshCentral root certificate is present
if ( typeof amtpolicy . rootcert == 'string' ) {
var rootFound = false , xxCertificates = responses [ "AMT_PublicKeyCertificate" ] . responses ;
for ( var i in xxCertificates ) { if ( ( xxCertificates [ i ] [ "X509Certificate" ] == amtpolicy . rootcert ) && ( xxCertificates [ i ] [ "TrustedRootCertficate" ] == true ) ) { rootFound = true ; } }
if ( rootFound == false ) { amtstack . AMT _PublicKeyManagementService _AddTrustedRootCertificate ( amtpolicy . rootcert , function ( stack , name , response , status ) { if ( status != 200 ) { debug ( "Add root cert EXEC Error " + status ) ; } } ) ; }
}
// If CIRA needs to be setup
if ( ( amtpolicy . cirasetup == 2 ) && ( amtpolicy . ciraserver != null ) ) {
var serverFound = false , xxCiraServers = responses [ "AMT_ManagementPresenceRemoteSAP" ] . responses ;
for ( var i in xxCiraServers ) { if ( ( xxCiraServers [ i ] . AccessInfo == amtpolicy . ciraserver . name ) && ( xxCiraServers [ i ] . Port == amtpolicy . ciraserver . port ) ) { serverFound = xxCiraServers [ i ] . Name ; } }
if ( serverFound == false ) {
// TODO: Remove all CIRA activation policies.
// amtstack.Delete('AMT_RemoteAccessPolicyRule', { 'PolicyRuleName': name }, editMpsPolicyOk2);
// TODO: Remove all other MPS servers.
// Add our MPS server
amtstack . AMT _RemoteAccessService _AddMpServer ( amtpolicy . ciraserver . name , 201 , amtpolicy . ciraserver . port , 2 , null , amtpolicy . ciraserver . user , amtpolicy . ciraserver . pass , null , function ( stack , name , response , status ) {
if ( status != 200 ) {
debug ( "Add MPS server EXEC Error " + status ) ;
} else {
serverFound = false ;
var x = response . Body . MpServer . ReferenceParameters . SelectorSet . Selector ;
for ( var i in x ) { if ( x [ i ] [ '@Name' ] == 'Name' ) { serverFound = x [ i ] [ 'Value' ] ; } }
if ( serverFound != false ) { checkCiraTriggerPolicy ( responses , serverFound ) ; }
}
} ) ;
} else {
checkCiraTriggerPolicy ( responses , serverFound ) ;
}
} else if ( amtpolicy . cirasetup == 1 ) {
// This call will clear environement detection if needed.
checkEnvironmentDetection ( responses ) ;
2019-03-10 21:47:03 +03:00
}
2019-03-11 07:40:25 +03:00
}
}
2019-03-10 21:47:03 +03:00
2019-03-11 07:40:25 +03:00
function checkCiraTriggerPolicy ( responses , serverInstanceName ) {
// Check CIRA activation policy
var server1 = '<Address xmlns="http://schemas.xmlsoap.org/ws/2004/08/addressing">http://schemas.xmlsoap.org/ws/2004/08/addressing/role/anonymous</Address><ReferenceParameters xmlns="http://schemas.xmlsoap.org/ws/2004/08/addressing"><ResourceURI xmlns="http://schemas.dmtf.org/wbem/wsman/1/wsman.xsd">http://intel.com/wbem/wscim/1/amt-schema/1/AMT_ManagementPresenceRemoteSAP</ResourceURI><SelectorSet xmlns="http://schemas.dmtf.org/wbem/wsman/1/wsman.xsd"><Selector Name="Name">' + serverInstanceName + '</Selector></SelectorSet></ReferenceParameters>' ;
amtstack . AMT _RemoteAccessService _AddRemoteAccessPolicyRule ( 2 , 0 , 'AAAAAAAAAAo=' , [ server1 ] , null , function ( stack , name , response , status ) {
if ( status != 200 ) {
debug ( "Add AddRemoteAccessPolicyRule Error " + status ) ;
} else {
//debug('AMT_RemoteAccessService_AddRemoteAccessPolicyRule Response:' + JSON.stringify(response));
checkEnvironmentDetection ( responses ) ;
}
} ) ;
}
// Check environement detection. This will set or clear the environement detection strings as needed.
function checkEnvironmentDetection ( responses ) {
var t2 = [ ] ;
if ( ( amtpolicy . ciraserver != null ) && ( amtpolicy . ciraserver . home != null ) ) { t2 = amtpolicy . ciraserver . home ; }
var t = responses [ "AMT_EnvironmentDetectionSettingData" ] . response ;
t [ 'DetectionStrings' ] = MakeToArray ( t [ 'DetectionStrings' ] ) ;
if ( CompareStrArrays ( t [ 'DetectionStrings' ] , t2 ) == false ) {
t [ 'DetectionStrings' ] = t2 ;
amtstack . Put ( 'AMT_EnvironmentDetectionSettingData' , t , function ( stack , name , response , status ) { if ( status != 200 ) { debug ( "Put AMT_EnvironmentDetectionSettingData Error " + status ) ; } } , 0 , 1 ) ;
2019-03-05 10:48:45 +03:00
}
}
2019-03-11 07:40:25 +03:00
// Imperfect compare of two string arrays.
function CompareStrArrays ( arr1 , arr2 ) {
if ( arr1 == arr2 ) return true ;
if ( arr1 == null ) { arr1 = [ ] ; }
if ( arr2 == null ) { arr2 = [ ] ; }
if ( arr1 . length != arr2 . length ) return false ;
for ( var i in arr1 ) { if ( arr2 . indexOf ( arr1 [ i ] ) == - 1 ) return false ; }
return true ;
}
function MakeToArray ( v ) { if ( ! v || v == null || typeof v == "object" ) return v ; return [ v ] ; } ;
2019-03-05 10:48:45 +03:00
}
module . exports = AmtManager ;