2017-08-28 19:27:45 +03:00
/ * *
2018-01-04 23:15:21 +03:00
* @ description MeshCentral MeshAgent communication module
2017-08-28 19:27:45 +03:00
* @ author Ylian Saint - Hilaire & Bryan Roe
2019-01-04 03:22:15 +03:00
* @ copyright Intel Corporation 2018 - 2019
2018-01-04 23:15:21 +03:00
* @ license Apache - 2.0
2017-08-28 19:27:45 +03:00
* @ version v0 . 0.1
* /
2018-08-30 03:40:30 +03:00
/*xjslint node: true */
/*xjslint plusplus: true */
/*xjslint maxlen: 256 */
/*jshint node: true */
/*jshint strict: false */
/*jshint esversion: 6 */
"use strict" ;
2018-08-27 22:24:15 +03:00
2017-08-28 19:27:45 +03:00
// Construct a MeshAgent object, called upon connection
module . exports . CreateMeshAgent = function ( parent , db , ws , req , args , domain ) {
2019-03-08 09:47:27 +03:00
const forge = parent . parent . certificateOperations . forge ;
const common = parent . parent . common ;
2019-03-10 01:28:08 +03:00
const agentUpdateBlockSize = 65531 ;
2019-05-02 01:02:03 +03:00
parent . agentStats . createMeshAgentCount ++ ;
2019-03-08 09:47:27 +03:00
2017-08-28 19:27:45 +03:00
var obj = { } ;
obj . domain = domain ;
2019-03-08 09:47:27 +03:00
obj . authenticated = 0 ;
2017-08-28 19:27:45 +03:00
obj . receivedCommands = 0 ;
obj . agentCoreCheck = 0 ;
2019-03-08 09:47:27 +03:00
obj . remoteaddr = ( req . ip . startsWith ( '::ffff:' ) ) ? ( req . ip . substring ( 7 ) ) : req . ip ;
obj . remoteaddrport = obj . remoteaddr + ':' + ws . _socket . remotePort ;
obj . nonce = parent . crypto . randomBytes ( 48 ) . toString ( 'binary' ) ;
2019-06-18 01:20:47 +03:00
//ws._socket.setKeepAlive(true, 240000); // Set TCP keep alive, 4 minutes
if ( args . agentidletimeout != 0 ) { ws . _socket . setTimeout ( args . agentidletimeout , function ( ) { obj . close ( 1 ) ; } ) ; } // Inactivity timeout of 2:30 minutes, by default agent will WebSocket ping every 2 minutes and server will pong back.
2019-03-08 09:47:27 +03:00
//obj.nodeid = null;
//obj.meshid = null;
//obj.dbNodeKey = null;
//obj.dbMeshKey = null;
//obj.connectTime = null;
//obj.agentInfo = null;
2017-08-28 19:27:45 +03:00
// Send a message to the mesh agent
2019-03-08 09:47:27 +03:00
obj . send = function ( data , func ) { try { if ( typeof data == 'string' ) { ws . send ( Buffer . from ( data , 'binary' ) , func ) ; } else { ws . send ( data , func ) ; } } catch ( e ) { } } ;
2017-08-28 19:27:45 +03:00
// Disconnect this agent
obj . close = function ( arg ) {
2019-02-26 04:30:19 +03:00
obj . authenticated = - 1 ;
2019-03-08 09:47:27 +03:00
if ( ( arg == 1 ) || ( arg == null ) ) { try { ws . close ( ) ; if ( obj . nodeid != null ) { parent . parent . debug ( 1 , 'Soft disconnect ' + obj . nodeid + ' (' + obj . remoteaddrport + ')' ) ; } } catch ( e ) { console . log ( e ) ; } } // Soft close, close the websocket
if ( arg == 2 ) { try { ws . _socket . _parent . end ( ) ; if ( obj . nodeid != null ) { parent . parent . debug ( 1 , 'Hard disconnect ' + obj . nodeid + ' (' + obj . remoteaddrport + ')' ) ; } } catch ( e ) { console . log ( e ) ; } } // Hard close, close the TCP socket
2019-02-26 04:30:19 +03:00
// If arg == 3, don't communicate with this agent anymore, but don't disconnect (Duplicate agent).
2019-03-10 01:28:08 +03:00
2019-04-10 20:41:10 +03:00
// Remove this agent from the webserver list
if ( parent . wsagents [ obj . dbNodeKey ] == obj ) {
delete parent . wsagents [ obj . dbNodeKey ] ;
parent . parent . ClearConnectivityState ( obj . dbMeshKey , obj . dbNodeKey , 1 ) ;
}
2018-09-27 00:58:55 +03:00
2019-04-10 20:41:10 +03:00
// Get the current mesh
const mesh = parent . meshes [ obj . dbMeshKey ] ;
// If this is a temporary or recovery agent, or all devices in this group are temporary, remove the agent (0x20 = Temporary, 0x40 = Recovery)
if ( ( ( obj . agentInfo ) && ( obj . agentInfo . capabilities ) && ( ( obj . agentInfo . capabilities & 0x20 ) || ( obj . agentInfo . capabilities & 0x40 ) ) ) || ( ( mesh ) && ( mesh . flags ) && ( mesh . flags & 1 ) ) ) {
// Delete this node including network interface information and events
db . Remove ( obj . dbNodeKey ) ; // Remove node with that id
db . Remove ( 'if' + obj . dbNodeKey ) ; // Remove interface information
db . Remove ( 'nt' + obj . dbNodeKey ) ; // Remove notes
db . Remove ( 'lc' + obj . dbNodeKey ) ; // Remove last connect time
2019-08-07 03:58:29 +03:00
db . Remove ( 'si' + obj . dbNodeKey ) ; // Remove system information
2019-04-10 20:41:10 +03:00
db . RemoveSMBIOS ( obj . dbNodeKey ) ; // Remove SMBios data
db . RemoveAllNodeEvents ( obj . dbNodeKey ) ; // Remove all events for this node
db . removeAllPowerEventsForNode ( obj . dbNodeKey ) ; // Remove all power events for this node
// Event node deletion
parent . parent . DispatchEvent ( [ '*' , obj . dbMeshKey ] , obj , { etype : 'node' , action : 'removenode' , nodeid : obj . dbNodeKey , domain : domain . id , nolog : 1 } ) ;
// Disconnect all connections if needed
const state = parent . parent . GetConnectivityState ( obj . dbNodeKey ) ;
if ( ( state != null ) && ( state . connectivity != null ) ) {
if ( ( state . connectivity & 1 ) != 0 ) { parent . wsagents [ obj . dbNodeKey ] . close ( ) ; } // Disconnect mesh agent
if ( ( state . connectivity & 2 ) != 0 ) { parent . parent . mpsserver . close ( parent . parent . mpsserver . ciraConnections [ obj . dbNodeKey ] ) ; } // Disconnect CIRA connection
2018-04-12 21:15:01 +03:00
}
2019-04-10 20:41:10 +03:00
} else {
// Update the last connect time
if ( obj . authenticated == 2 ) { db . Set ( { _id : 'lc' + obj . dbNodeKey , type : 'lastconnect' , domain : domain . id , time : obj . connectTime , addr : obj . remoteaddrport } ) ; }
2018-04-12 21:15:01 +03:00
}
2019-03-10 01:28:08 +03:00
// If we where updating the agent, clean that up.
if ( obj . agentUpdate != null ) {
if ( obj . agentUpdate . fd ) { try { parent . fs . close ( obj . agentUpdate . fd ) ; } catch ( ex ) { } }
parent . parent . taskLimiter . completed ( obj . agentUpdate . taskid ) ; // Indicate this task complete
2019-03-10 21:00:14 +03:00
delete obj . agentUpdate . buf ;
2019-03-10 01:28:08 +03:00
delete obj . agentUpdate ;
}
2019-05-28 20:58:46 +03:00
// Perform timer cleanup
if ( obj . pingtimer ) { clearInterval ( obj . pingtimer ) ; delete obj . pingtimer ; }
if ( obj . pongtimer ) { clearInterval ( obj . pongtimer ) ; delete obj . pongtimer ; }
2019-03-10 01:28:08 +03:00
// Perform aggressive cleanup
if ( obj . nonce ) { delete obj . nonce ; }
if ( obj . nodeid ) { delete obj . nodeid ; }
if ( obj . unauth ) { delete obj . unauth ; }
if ( obj . remoteaddr ) { delete obj . remoteaddr ; }
if ( obj . remoteaddrport ) { delete obj . remoteaddrport ; }
if ( obj . meshid ) { delete obj . meshid ; }
if ( obj . dbNodeKey ) { delete obj . dbNodeKey ; }
if ( obj . dbMeshKey ) { delete obj . dbMeshKey ; }
if ( obj . connectTime ) { delete obj . connectTime ; }
if ( obj . agentInfo ) { delete obj . agentInfo ; }
2019-03-10 02:04:43 +03:00
if ( obj . agentExeInfo ) { delete obj . agentExeInfo ; }
2019-03-10 01:28:08 +03:00
ws . removeAllListeners ( [ "message" , "close" , "error" ] ) ;
2018-08-30 03:40:30 +03:00
} ;
2017-08-28 19:27:45 +03:00
// When data is received from the mesh agent web socket
ws . on ( 'message' , function ( msg ) {
if ( msg . length < 2 ) return ;
2017-10-15 09:22:19 +03:00
if ( typeof msg == 'object' ) { msg = msg . toString ( 'binary' ) ; } // TODO: Could change this entire method to use Buffer instead of binary string
2017-08-28 19:27:45 +03:00
if ( obj . authenticated == 2 ) { // We are authenticated
2018-07-03 00:34:10 +03:00
if ( ( obj . agentUpdate == null ) && ( msg . charCodeAt ( 0 ) == 123 ) ) { processAgentData ( msg ) ; } // Only process JSON messages if meshagent update is not in progress
2017-08-28 19:27:45 +03:00
if ( msg . length < 2 ) return ;
2019-03-08 09:47:27 +03:00
const cmdid = common . ReadShort ( msg , 0 ) ;
2017-08-28 19:27:45 +03:00
if ( cmdid == 11 ) { // MeshCommand_CoreModuleHash
2018-09-22 02:34:35 +03:00
if ( msg . length == 4 ) { ChangeAgentCoreInfo ( { "caps" : 0 } ) ; } // If the agent indicated that no core is running, clear the core information string.
2017-08-28 19:27:45 +03:00
// Mesh core hash, sent by agent with the hash of the current mesh core.
2019-02-19 04:50:10 +03:00
// If we are using a custom core, don't try to update it.
if ( obj . agentCoreCheck == 1000 ) {
2019-03-08 09:47:27 +03:00
obj . send ( common . ShortToStr ( 16 ) + common . ShortToStr ( 0 ) ) ; // MeshCommand_CoreOk. Indicates to the agent that the core is ok. Start it if it's not already started.
2019-02-19 04:50:10 +03:00
agentCoreIsStable ( ) ;
return ;
}
2018-12-30 02:24:33 +03:00
// Get the current meshcore hash
const agentMeshCoreHash = ( msg . length == 52 ) ? msg . substring ( 4 , 52 ) : null ;
2019-03-08 09:47:27 +03:00
// If the agent indicates this is a custom core, we are done.
2019-03-10 01:28:08 +03:00
if ( ( agentMeshCoreHash != null ) && ( agentMeshCoreHash == '\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0' ) ) {
2019-02-19 04:50:10 +03:00
obj . agentCoreCheck = 0 ;
2019-03-08 09:47:27 +03:00
obj . send ( common . ShortToStr ( 16 ) + common . ShortToStr ( 0 ) ) ; // MeshCommand_CoreOk. Indicates to the agent that the core is ok. Start it if it's not already started.
2019-02-19 04:50:10 +03:00
agentCoreIsStable ( ) ;
return ;
}
2019-03-08 09:47:27 +03:00
// We need to check if the core is current. Figure out what core we need.
2019-06-18 01:37:13 +03:00
var corename = null ;
if ( parent . parent . meshAgentsArchitectureNumbers [ obj . agentInfo . agentId ] != null ) {
if ( obj . agentCoreCheck == 1001 ) {
// If the user asked, use the recovery core.
corename = parent . parent . meshAgentsArchitectureNumbers [ obj . agentInfo . agentId ] . rcore ;
} else if ( obj . agentInfo . capabilities & 0x40 ) {
// If this is a recovery agent, use the agent recovery core.
corename = parent . parent . meshAgentsArchitectureNumbers [ obj . agentInfo . agentId ] . arcore ;
} else {
// This is the normal core for this agent type.
corename = parent . parent . meshAgentsArchitectureNumbers [ obj . agentInfo . agentId ] . core ;
}
2019-03-08 09:47:27 +03:00
}
2019-02-26 01:35:08 +03:00
// If we have a core, use it.
2018-12-30 02:24:33 +03:00
if ( corename != null ) {
2019-03-08 09:47:27 +03:00
const meshcorehash = parent . parent . defaultMeshCoresHash [ corename ] ;
2018-12-30 02:24:33 +03:00
if ( agentMeshCoreHash != meshcorehash ) {
2019-01-22 01:05:50 +03:00
if ( ( obj . agentCoreCheck < 5 ) || ( obj . agentCoreCheck == 1001 ) ) {
2018-12-30 02:24:33 +03:00
if ( meshcorehash == null ) {
2019-01-18 02:07:34 +03:00
// Clear the core
2019-03-08 09:47:27 +03:00
obj . send ( common . ShortToStr ( 10 ) + common . ShortToStr ( 0 ) ) ; // MeshCommand_CoreModule, ask mesh agent to clear the core
2019-05-02 01:02:03 +03:00
parent . agentStats . clearingCoreCount ++ ;
2019-03-08 09:47:27 +03:00
parent . parent . debug ( 1 , 'Clearing core' ) ;
2018-12-30 02:24:33 +03:00
} else {
// Update new core
2019-03-08 09:47:27 +03:00
//obj.send(common.ShortToStr(10) + common.ShortToStr(0) + meshcorehash + parent.parent.defaultMeshCores[corename]); // MeshCommand_CoreModule, start core update
//parent.parent.debug(1, 'Updating code ' + corename);
2019-01-29 02:47:54 +03:00
2019-02-09 01:17:35 +03:00
// Update new core with task limiting so not to flood the server. This is a high priority task.
2019-02-26 03:07:27 +03:00
obj . agentCoreUpdatePending = true ;
2019-03-08 09:47:27 +03:00
parent . parent . taskLimiter . launch ( function ( argument , taskid , taskLimiterQueue ) {
2019-02-26 04:30:19 +03:00
if ( obj . authenticated == 2 ) {
// Send the updated code.
2019-03-08 09:47:27 +03:00
delete obj . agentCoreUpdatePending ;
obj . send ( common . ShortToStr ( 10 ) + common . ShortToStr ( 0 ) + argument . hash + argument . core , function ( ) { parent . parent . taskLimiter . completed ( taskid ) ; } ) ; // MeshCommand_CoreModule, start core update
2019-05-02 01:02:03 +03:00
parent . agentStats . updatingCoreCount ++ ;
parent . parent . debug ( 1 , 'Updating core ' + argument . name ) ;
2019-02-26 04:30:19 +03:00
agentCoreIsStable ( ) ;
} else {
// This agent is probably disconnected, nothing to do.
2019-03-08 09:47:27 +03:00
parent . parent . taskLimiter . completed ( taskid ) ;
2019-02-26 04:30:19 +03:00
}
2019-03-08 09:47:27 +03:00
} , { hash : meshcorehash , core : parent . parent . defaultMeshCores [ corename ] , name : corename } , 0 ) ;
2018-12-30 02:24:33 +03:00
}
obj . agentCoreCheck ++ ;
}
} else {
obj . agentCoreCheck = 0 ;
2019-03-08 09:47:27 +03:00
obj . send ( common . ShortToStr ( 16 ) + common . ShortToStr ( 0 ) ) ; // MeshCommand_CoreOk. Indicates to the agent that the core is ok. Start it if it's not already started.
2019-02-08 02:00:10 +03:00
agentCoreIsStable ( ) ; // No updates needed, agent is ready to go.
2018-12-30 02:24:33 +03:00
}
}
/ *
2017-08-28 19:27:45 +03:00
// TODO: Check if we have a mesh specific core. If so, use that.
var agentMeshCoreHash = null ;
2017-10-15 09:22:19 +03:00
if ( msg . length == 52 ) { agentMeshCoreHash = msg . substring ( 4 , 52 ) ; }
2019-03-08 09:47:27 +03:00
if ( ( agentMeshCoreHash != parent . parent . defaultMeshCoreHash ) && ( agentMeshCoreHash != parent . parent . defaultMeshCoreNoMeiHash ) ) {
2017-08-28 19:27:45 +03:00
if ( obj . agentCoreCheck < 5 ) { // This check is in place to avoid a looping core update.
2019-03-08 09:47:27 +03:00
if ( parent . parent . defaultMeshCoreHash == null ) {
2017-08-28 19:27:45 +03:00
// Update no core
2019-03-08 09:47:27 +03:00
obj . send ( common . ShortToStr ( 10 ) + common . ShortToStr ( 0 ) ) ; // Command 10, ask mesh agent to clear the core
2017-08-28 19:27:45 +03:00
} else {
// Update new core
2019-03-08 09:47:27 +03:00
if ( parent . parent . meshAgentsArchitectureNumbers [ obj . agentInfo . agentId ] . amt == true ) {
obj . send ( common . ShortToStr ( 10 ) + common . ShortToStr ( 0 ) + parent . parent . defaultMeshCoreHash + parent . parent . defaultMeshCore ) ; // Command 10, ask mesh agent to set the core (with MEI support)
2017-12-13 03:04:54 +03:00
} else {
2019-03-08 09:47:27 +03:00
obj . send ( common . ShortToStr ( 10 ) + common . ShortToStr ( 0 ) + parent . parent . defaultMeshCoreNoMeiHash + parent . parent . defaultMeshCoreNoMei ) ; // Command 10, ask mesh agent to set the core (No MEI)
2017-12-13 03:04:54 +03:00
}
2017-08-28 19:27:45 +03:00
}
obj . agentCoreCheck ++ ;
}
} else {
obj . agentCoreCheck = 0 ;
}
2018-12-30 02:24:33 +03:00
* /
2017-08-28 19:27:45 +03:00
}
else if ( cmdid == 12 ) { // MeshCommand_AgentHash
2017-10-15 09:22:19 +03:00
if ( ( msg . length == 52 ) && ( obj . agentExeInfo != null ) && ( obj . agentExeInfo . update == true ) ) {
2019-03-08 09:47:27 +03:00
const agenthash = msg . substring ( 4 ) ;
2019-03-10 01:28:08 +03:00
if ( ( agenthash != obj . agentExeInfo . hash ) && ( agenthash != '\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0' ) ) {
2019-02-09 01:17:35 +03:00
// Mesh agent update required, do it using task limiter so not to flood the network. Medium priority task.
2019-03-08 09:47:27 +03:00
parent . parent . taskLimiter . launch ( function ( argument , taskid , taskLimiterQueue ) {
if ( obj . authenticated != 2 ) { parent . parent . taskLimiter . completed ( taskid ) ; return ; } // If agent disconnection, complete and exit now.
if ( obj . nodeid != null ) { parent . parent . debug ( 1 , 'Agent update required, NodeID=0x' + obj . nodeid . substring ( 0 , 16 ) + ', ' + obj . agentExeInfo . desc ) ; }
2019-05-25 01:11:00 +03:00
parent . agentStats . agentBinaryUpdate ++ ;
2019-02-15 09:16:15 +03:00
if ( obj . agentExeInfo . data == null ) {
// Read the agent from disk
2019-03-08 09:47:27 +03:00
parent . fs . open ( obj . agentExeInfo . path , 'r' , function ( err , fd ) {
2019-03-10 02:04:43 +03:00
if ( obj . agentExeInfo == null ) return ; // Agent disconnected during this call.
2019-02-15 09:16:15 +03:00
if ( err ) { return console . error ( err ) ; }
2019-03-08 09:47:27 +03:00
obj . agentUpdate = { ptr : 0 , buf : Buffer . alloc ( agentUpdateBlockSize + 4 ) , fd : fd , taskid : taskid } ;
2019-02-15 09:16:15 +03:00
// MeshCommand_CoreModule, ask mesh agent to clear the core.
// The new core will only be sent after the agent updates.
2019-03-08 09:47:27 +03:00
obj . send ( common . ShortToStr ( 10 ) + common . ShortToStr ( 0 ) ) ;
2019-02-15 09:16:15 +03:00
// We got the agent file open on the server side, tell the agent we are sending an update starting with the SHA384 hash of the result
//console.log("Agent update file open.");
2019-03-08 09:47:27 +03:00
obj . send ( common . ShortToStr ( 13 ) + common . ShortToStr ( 0 ) ) ; // Command 13, start mesh agent download
2019-02-15 09:16:15 +03:00
// Send the first mesh agent update data block
obj . agentUpdate . buf [ 0 ] = 0 ;
obj . agentUpdate . buf [ 1 ] = 14 ;
obj . agentUpdate . buf [ 2 ] = 0 ;
obj . agentUpdate . buf [ 3 ] = 1 ;
2019-03-08 09:47:27 +03:00
parent . fs . read ( obj . agentUpdate . fd , obj . agentUpdate . buf , 4 , agentUpdateBlockSize , obj . agentUpdate . ptr , function ( err , bytesRead , buffer ) {
if ( ( err != null ) || ( bytesRead == 0 ) ) {
// Error reading the agent file, stop here.
try { parent . fs . close ( obj . agentUpdate . fd ) ; } catch ( ex ) { }
parent . parent . taskLimiter . completed ( obj . agentUpdate . taskid ) ; // Indicate this task complete
2019-03-10 21:00:14 +03:00
delete obj . agentUpdate . buf ;
2019-03-08 09:47:27 +03:00
delete obj . agentUpdate ;
} else {
// Send the first block to the agent
obj . agentUpdate . ptr += bytesRead ;
//console.log("Agent update send first block: " + bytesRead);
obj . send ( obj . agentUpdate . buf ) ; // Command 14, mesh agent first data block
}
} ) ;
2019-02-15 09:16:15 +03:00
} ) ;
} else {
// Send the agent from RAM
2019-03-08 09:47:27 +03:00
obj . agentUpdate = { ptr : 0 , buf : Buffer . alloc ( agentUpdateBlockSize + 4 ) , taskid : taskid } ;
2019-01-29 03:53:11 +03:00
// MeshCommand_CoreModule, ask mesh agent to clear the core.
// The new core will only be sent after the agent updates.
2019-03-08 09:47:27 +03:00
obj . send ( common . ShortToStr ( 10 ) + common . ShortToStr ( 0 ) ) ;
2019-01-29 03:53:11 +03:00
// We got the agent file open on the server side, tell the agent we are sending an update starting with the SHA384 hash of the result
2019-03-08 09:47:27 +03:00
obj . send ( common . ShortToStr ( 13 ) + common . ShortToStr ( 0 ) ) ; // Command 13, start mesh agent download
2019-01-29 03:53:11 +03:00
// Send the first mesh agent update data block
obj . agentUpdate . buf [ 0 ] = 0 ;
obj . agentUpdate . buf [ 1 ] = 14 ;
obj . agentUpdate . buf [ 2 ] = 0 ;
obj . agentUpdate . buf [ 3 ] = 1 ;
2019-02-15 09:16:15 +03:00
2019-03-08 09:47:27 +03:00
const len = Math . min ( agentUpdateBlockSize , obj . agentExeInfo . data . length - obj . agentUpdate . ptr ) ;
2019-02-15 09:16:15 +03:00
if ( len > 0 ) {
// Send the first block
obj . agentExeInfo . data . copy ( obj . agentUpdate . buf , 4 , obj . agentUpdate . ptr , obj . agentUpdate . ptr + len ) ;
2019-01-29 03:53:11 +03:00
obj . agentUpdate . ptr += len ;
obj . send ( obj . agentUpdate . buf ) ; // Command 14, mesh agent first data block
2019-02-15 09:16:15 +03:00
} else {
// Error
2019-03-08 09:47:27 +03:00
parent . parent . taskLimiter . completed ( obj . agentUpdate . taskid ) ; // Indicate this task complete
2019-03-10 21:00:14 +03:00
delete obj . agentUpdate . buf ;
2019-03-08 09:47:27 +03:00
delete obj . agentUpdate ;
2019-01-29 03:53:11 +03:00
}
2019-02-15 09:16:15 +03:00
}
2019-02-09 01:17:35 +03:00
} , null , 1 ) ;
2019-01-29 03:53:11 +03:00
2019-01-22 01:05:50 +03:00
} else {
// Check the mesh core, if the agent is capable of running one
2019-03-08 09:47:27 +03:00
if ( ( ( obj . agentInfo . capabilities & 16 ) != 0 ) && ( parent . parent . meshAgentsArchitectureNumbers [ obj . agentInfo . agentId ] . core != null ) ) {
obj . send ( common . ShortToStr ( 11 ) + common . ShortToStr ( 0 ) ) ; // Command 11, ask for mesh core hash.
2019-01-23 02:40:08 +03:00
}
2017-08-28 19:27:45 +03:00
}
}
}
else if ( cmdid == 14 ) { // MeshCommand_AgentBinaryBlock
if ( ( msg . length == 4 ) && ( obj . agentUpdate != null ) ) {
2019-03-08 09:47:27 +03:00
const status = common . ReadShort ( msg , 2 ) ;
2017-08-28 19:27:45 +03:00
if ( status == 1 ) {
2019-02-15 09:16:15 +03:00
if ( obj . agentExeInfo . data == null ) {
// Read the agent from disk
2019-03-08 09:47:27 +03:00
parent . fs . read ( obj . agentUpdate . fd , obj . agentUpdate . buf , 4 , agentUpdateBlockSize , obj . agentUpdate . ptr , function ( err , bytesRead , buffer ) {
2019-03-10 02:04:43 +03:00
if ( ( obj . agentExeInfo == null ) || ( obj . agentUpdate == null ) ) return ; // Agent disconnected during this async call.
2019-03-08 09:47:27 +03:00
if ( ( err != null ) || ( bytesRead < 0 ) ) {
// Error reading the agent file, stop here.
try { parent . fs . close ( obj . agentUpdate . fd ) ; } catch ( ex ) { }
parent . parent . taskLimiter . completed ( obj . agentUpdate . taskid ) ; // Indicate this task complete
2019-03-10 21:00:14 +03:00
delete obj . agentUpdate . buf ;
2019-03-08 09:47:27 +03:00
delete obj . agentUpdate ;
} else {
// Send the next block to the agent
obj . agentUpdate . ptr += bytesRead ;
//console.log("Agent update send next block", obj.agentUpdate.ptr, bytesRead);
if ( bytesRead == agentUpdateBlockSize ) { obj . send ( obj . agentUpdate . buf ) ; } else { obj . send ( obj . agentUpdate . buf . slice ( 0 , bytesRead + 4 ) ) ; } // Command 14, mesh agent next data block
if ( bytesRead < agentUpdateBlockSize ) {
//console.log("Agent update sent from disk.");
obj . send ( common . ShortToStr ( 13 ) + common . ShortToStr ( 0 ) + obj . agentExeInfo . hash ) ; // Command 13, end mesh agent download, send agent SHA384 hash
try { parent . fs . close ( obj . agentUpdate . fd ) ; } catch ( ex ) { }
parent . parent . taskLimiter . completed ( obj . agentUpdate . taskid ) ; // Indicate this task complete
2019-03-10 21:00:14 +03:00
delete obj . agentUpdate . buf ;
2019-03-08 09:47:27 +03:00
delete obj . agentUpdate ;
}
2019-02-15 09:16:15 +03:00
}
2019-03-08 09:47:27 +03:00
} ) ;
2017-08-28 19:27:45 +03:00
} else {
2019-02-15 09:16:15 +03:00
// Send the agent from RAM
2019-03-08 09:47:27 +03:00
const len = Math . min ( agentUpdateBlockSize , obj . agentExeInfo . data . length - obj . agentUpdate . ptr ) ;
2019-02-15 09:16:15 +03:00
if ( len > 0 ) {
obj . agentExeInfo . data . copy ( obj . agentUpdate . buf , 4 , obj . agentUpdate . ptr , obj . agentUpdate . ptr + len ) ;
if ( len == agentUpdateBlockSize ) { obj . send ( obj . agentUpdate . buf ) ; } else { obj . send ( obj . agentUpdate . buf . slice ( 0 , len + 4 ) ) ; } // Command 14, mesh agent next data block
obj . agentUpdate . ptr += len ;
}
2017-08-28 19:27:45 +03:00
2019-02-15 09:16:15 +03:00
if ( obj . agentUpdate . ptr == obj . agentExeInfo . data . length ) {
//console.log("Agent update sent from RAM.");
2019-03-08 09:47:27 +03:00
obj . send ( common . ShortToStr ( 13 ) + common . ShortToStr ( 0 ) + obj . agentExeInfo . hash ) ; // Command 13, end mesh agent download, send agent SHA384 hash
parent . parent . taskLimiter . completed ( obj . agentUpdate . taskid ) ; // Indicate this task complete
2019-03-10 21:00:14 +03:00
delete obj . agentUpdate . buf ;
2019-03-08 09:47:27 +03:00
delete obj . agentUpdate ;
2017-08-28 19:27:45 +03:00
}
}
}
}
}
else if ( cmdid == 15 ) { // MeshCommand_AgentTag
2018-01-24 01:15:59 +03:00
var tag = msg . substring ( 2 ) ;
while ( tag . charCodeAt ( tag . length - 1 ) == 0 ) { tag = tag . substring ( 0 , tag . length - 1 ) ; } // Remove end-of-line zeros.
ChangeAgentTag ( tag ) ;
2017-08-28 19:27:45 +03:00
}
2018-01-24 01:15:59 +03:00
} else if ( obj . authenticated < 2 ) { // We are not authenticated
2019-03-08 09:47:27 +03:00
const cmd = common . ReadShort ( msg , 0 ) ;
2017-08-28 19:27:45 +03:00
if ( cmd == 1 ) {
// Agent authentication request
2017-10-15 09:22:19 +03:00
if ( ( msg . length != 98 ) || ( ( obj . receivedCommands & 1 ) != 0 ) ) return ;
2017-08-28 19:27:45 +03:00
obj . receivedCommands += 1 ; // Agent can't send the same command twice on the same connection ever. Block DOS attack path.
2019-03-08 09:47:27 +03:00
if ( args . ignoreagenthashcheck === true ) {
2018-12-15 23:34:55 +03:00
// Send the agent web hash back to the agent
2019-03-08 09:47:27 +03:00
obj . send ( common . ShortToStr ( 1 ) + msg . substring ( 2 , 50 ) + obj . nonce ) ; // Command 1, hash + nonce. Use the web hash given by the agent.
2018-12-15 23:34:55 +03:00
} else {
// Check that the server hash matches our own web certificate hash (SHA384)
2019-03-08 09:47:27 +03:00
if ( ( getWebCertHash ( domain ) != msg . substring ( 2 , 50 ) ) && ( getWebCertFullHash ( domain ) != msg . substring ( 2 , 50 ) ) ) {
2019-05-02 01:02:03 +03:00
parent . agentStats . agentBadWebCertHashCount ++ ;
2019-03-08 09:47:27 +03:00
console . log ( 'Agent bad web cert hash (Agent:' + ( Buffer . from ( msg . substring ( 2 , 50 ) , 'binary' ) . toString ( 'hex' ) . substring ( 0 , 10 ) ) + ' != Server:' + ( Buffer . from ( getWebCertHash ( domain ) , 'binary' ) . toString ( 'hex' ) . substring ( 0 , 10 ) ) + ' or ' + ( new Buffer ( getWebCertFullHash ( domain ) , 'binary' ) . toString ( 'hex' ) . substring ( 0 , 10 ) ) + '), holding connection (' + obj . remoteaddrport + ').' ) ;
2019-02-15 01:44:38 +03:00
console . log ( 'Agent reported web cert hash:' + ( Buffer . from ( msg . substring ( 2 , 50 ) , 'binary' ) . toString ( 'hex' ) ) + '.' ) ;
return ;
}
2018-12-15 23:34:55 +03:00
}
2017-08-28 19:27:45 +03:00
// Use our server private key to sign the ServerHash + AgentNonce + ServerNonce
2018-11-01 02:03:09 +03:00
obj . agentnonce = msg . substring ( 50 , 98 ) ;
2018-08-22 01:08:15 +03:00
// Check if we got the agent auth confirmation
if ( ( obj . receivedCommands & 8 ) == 0 ) {
// If we did not get an indication that the agent already validated this server, send the server signature.
if ( obj . useSwarmCert == true ) {
// Perform the hash signature using older swarm server certificate
2019-03-08 09:47:27 +03:00
parent . parent . certificateOperations . acceleratorPerformSignature ( 1 , msg . substring ( 2 ) + obj . nonce , obj , function ( obj2 , signature ) {
2018-08-22 01:08:15 +03:00
// Send back our certificate + signature
2019-03-10 01:28:08 +03:00
obj2 . send ( common . ShortToStr ( 2 ) + common . ShortToStr ( parent . swarmCertificateAsn1 . length ) + parent . swarmCertificateAsn1 + signature ) ; // Command 2, certificate + signature
2018-08-22 01:08:15 +03:00
} ) ;
} else {
// Perform the hash signature using the server agent certificate
2019-03-08 09:47:27 +03:00
parent . parent . certificateOperations . acceleratorPerformSignature ( 0 , msg . substring ( 2 ) + obj . nonce , obj , function ( obj2 , signature ) {
2018-08-22 01:08:15 +03:00
// Send back our certificate + signature
2019-03-10 01:28:08 +03:00
obj2 . send ( common . ShortToStr ( 2 ) + common . ShortToStr ( parent . agentCertificateAsn1 . length ) + parent . agentCertificateAsn1 + signature ) ; // Command 2, certificate + signature
2018-08-22 01:08:15 +03:00
} ) ;
}
2017-11-08 04:05:22 +03:00
}
2017-08-28 19:27:45 +03:00
// Check the agent signature if we can
2017-09-15 21:45:06 +03:00
if ( obj . unauthsign != null ) {
2019-05-02 01:02:03 +03:00
if ( processAgentSignature ( obj . unauthsign ) == false ) {
parent . agentStats . agentBadSignature1Count ++ ;
console . log ( 'Agent connected with bad signature, holding connection (' + obj . remoteaddrport + ').' ) ; return ;
} else { completeAgentConnection ( ) ; }
2017-08-28 19:27:45 +03:00
}
}
else if ( cmd == 2 ) {
// Agent certificate
if ( ( msg . length < 4 ) || ( ( obj . receivedCommands & 2 ) != 0 ) ) return ;
obj . receivedCommands += 2 ; // Agent can't send the same command twice on the same connection ever. Block DOS attack path.
// Decode the certificate
2019-03-08 09:47:27 +03:00
const certlen = common . ReadShort ( msg , 2 ) ;
2017-08-28 19:27:45 +03:00
obj . unauth = { } ;
2019-03-08 09:47:27 +03:00
try { obj . unauth . nodeid = Buffer . from ( forge . pki . getPublicKeyFingerprint ( forge . pki . certificateFromAsn1 ( forge . asn1 . fromDer ( msg . substring ( 4 , 4 + certlen ) ) ) . publicKey , { md : forge . md . sha384 . create ( ) } ) . data , 'binary' ) . toString ( 'base64' ) . replace ( /\+/g , '@' ) . replace ( /\//g , '$' ) ; } catch ( ex ) { console . log ( ex ) ; return ; }
2019-01-03 05:03:34 +03:00
obj . unauth . nodeCertPem = '-----BEGIN CERTIFICATE-----\r\n' + Buffer . from ( msg . substring ( 4 , 4 + certlen ) , 'binary' ) . toString ( 'base64' ) + '\r\n-----END CERTIFICATE-----' ;
2017-08-28 19:27:45 +03:00
// Check the agent signature if we can
2019-05-02 01:02:03 +03:00
if ( obj . agentnonce == null ) { obj . unauthsign = msg . substring ( 4 + certlen ) ; } else {
if ( processAgentSignature ( msg . substring ( 4 + certlen ) ) == false ) {
parent . agentStats . agentBadSignature2Count ++ ;
console . log ( 'Agent connected with bad signature, holding connection (' + obj . remoteaddrport + ').' ) ; return ;
}
}
2017-08-28 19:27:45 +03:00
completeAgentConnection ( ) ;
}
else if ( cmd == 3 ) {
// Agent meshid
2017-10-15 09:22:19 +03:00
if ( ( msg . length < 72 ) || ( ( obj . receivedCommands & 4 ) != 0 ) ) return ;
2017-08-28 19:27:45 +03:00
obj . receivedCommands += 4 ; // Agent can't send the same command twice on the same connection ever. Block DOS attack path.
// Set the meshid
obj . agentInfo = { } ;
2019-03-08 09:47:27 +03:00
obj . agentInfo . infoVersion = common . ReadInt ( msg , 2 ) ;
obj . agentInfo . agentId = common . ReadInt ( msg , 6 ) ;
obj . agentInfo . agentVersion = common . ReadInt ( msg , 10 ) ;
obj . agentInfo . platformType = common . ReadInt ( msg , 14 ) ;
2017-10-17 06:11:03 +03:00
if ( obj . agentInfo . platformType > 6 || obj . agentInfo . platformType < 1 ) { obj . agentInfo . platformType = 1 ; }
2017-11-08 04:05:22 +03:00
if ( msg . substring ( 50 , 66 ) == '\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0' ) {
2019-01-03 05:03:34 +03:00
obj . meshid = Buffer . from ( msg . substring ( 18 , 50 ) , 'binary' ) . toString ( 'hex' ) ; // Older HEX MeshID
2017-11-08 04:05:22 +03:00
} else {
2019-01-03 05:03:34 +03:00
obj . meshid = Buffer . from ( msg . substring ( 18 , 66 ) , 'binary' ) . toString ( 'base64' ) . replace ( /\+/g , '@' ) . replace ( /\//g , '$' ) ; // New Base64 MeshID
2017-11-08 04:05:22 +03:00
}
//console.log('MeshID', obj.meshid);
2019-03-08 09:47:27 +03:00
obj . agentInfo . capabilities = common . ReadInt ( msg , 66 ) ;
const computerNameLen = common . ReadShort ( msg , 70 ) ;
2017-10-15 09:22:19 +03:00
obj . agentInfo . computerName = msg . substring ( 72 , 72 + computerNameLen ) ;
2019-03-08 09:47:27 +03:00
obj . dbMeshKey = 'mesh/' + domain . id + '/' + obj . meshid ;
2017-08-28 19:27:45 +03:00
completeAgentConnection ( ) ;
2018-08-22 01:08:15 +03:00
} else if ( cmd == 4 ) {
if ( ( msg . length < 2 ) || ( ( obj . receivedCommands & 8 ) != 0 ) ) return ;
obj . receivedCommands += 8 ; // Agent can't send the same command twice on the same connection ever. Block DOS attack path.
// Agent already authenticated the server, wants to skip the server signature - which is great for server performance.
2017-11-08 04:05:22 +03:00
} else if ( cmd == 5 ) {
// ServerID. Agent is telling us what serverid it expects. Useful if we have many server certificates.
2019-03-08 09:47:27 +03:00
if ( ( msg . substring ( 2 , 34 ) == parent . swarmCertificateHash256 ) || ( msg . substring ( 2 , 50 ) == parent . swarmCertificateHash384 ) ) { obj . useSwarmCert = true ; }
2017-08-28 19:27:45 +03:00
}
}
} ) ;
// If error, do nothing
2019-03-10 01:28:08 +03:00
ws . on ( 'error' , function ( err ) { console . log ( 'AGENT WSERR: ' + err ) ; obj . close ( 0 ) ; } ) ;
2017-08-28 19:27:45 +03:00
// If the mesh agent web socket is closed, clean up.
2019-01-23 04:47:51 +03:00
ws . on ( 'close' , function ( req ) {
2019-05-25 01:11:00 +03:00
parent . agentStats . agentClose ++ ;
2019-01-23 04:47:51 +03:00
if ( obj . nodeid != null ) {
2019-03-08 09:47:27 +03:00
const agentId = ( obj . agentInfo && obj . agentInfo . agentId ) ? obj . agentInfo . agentId : 'Unknown' ;
2019-02-13 00:37:01 +03:00
//console.log('Agent disconnect ' + obj.nodeid + ' (' + obj.remoteaddrport + ') id=' + agentId);
2019-03-08 09:47:27 +03:00
parent . parent . debug ( 1 , 'Agent disconnect ' + obj . nodeid + ' (' + obj . remoteaddrport + ') id=' + agentId ) ;
2019-01-23 06:34:55 +03:00
// Log the agent disconnection
2019-03-08 09:47:27 +03:00
if ( parent . wsagentsDisconnections [ obj . nodeid ] == null ) {
parent . wsagentsDisconnections [ obj . nodeid ] = 1 ;
2019-01-23 06:34:55 +03:00
} else {
2019-03-08 09:47:27 +03:00
parent . wsagentsDisconnections [ obj . nodeid ] = ++ parent . wsagentsDisconnections [ obj . nodeid ] ;
2019-01-23 06:34:55 +03:00
}
2019-01-23 04:47:51 +03:00
}
obj . close ( 0 ) ;
} ) ;
2017-08-28 19:27:45 +03:00
// Start authenticate the mesh agent by sending a auth nonce & server TLS cert hash.
2017-10-15 09:22:19 +03:00
// Send 384 bits SHA384 hash of TLS cert public key + 384 bits nonce
2019-03-08 09:47:27 +03:00
if ( args . ignoreagenthashcheck !== true ) {
obj . send ( common . ShortToStr ( 1 ) + getWebCertHash ( domain ) + obj . nonce ) ; // Command 1, hash + nonce
2018-12-15 23:34:55 +03:00
}
2017-08-28 19:27:45 +03:00
2019-02-15 01:44:38 +03:00
// Return the mesh for this device, in some cases, we may auto-create the mesh.
function getMeshAutoCreate ( ) {
2019-03-18 05:34:52 +03:00
var mesh = parent . meshes [ obj . dbMeshKey ] ;
2019-03-08 09:47:27 +03:00
if ( ( mesh == null ) && ( typeof domain . orphanagentuser == 'string' ) ) {
const adminUser = parent . users [ 'user/' + domain . id + '/' + domain . orphanagentuser . toLowerCase ( ) ] ;
2019-02-15 01:44:38 +03:00
if ( ( adminUser != null ) && ( adminUser . siteadmin == 0xFFFFFFFF ) ) {
// Mesh name is hex instead of base64
2019-03-08 09:47:27 +03:00
const meshname = obj . meshid . substring ( 0 , 18 ) ;
2019-02-15 01:44:38 +03:00
// Create a new mesh for this device
2019-03-08 09:47:27 +03:00
const links = { } ;
2019-02-15 01:44:38 +03:00
links [ adminUser . _id ] = { name : adminUser . name , rights : 0xFFFFFFFF } ;
mesh = { type : 'mesh' , _id : obj . dbMeshKey , name : meshname , mtype : 2 , desc : '' , domain : domain . id , links : links } ;
2019-03-08 09:47:27 +03:00
db . Set ( common . escapeLinksFieldName ( mesh ) ) ;
parent . meshes [ obj . dbMeshKey ] = mesh ;
2019-02-15 01:44:38 +03:00
2019-02-15 02:53:22 +03:00
if ( adminUser . links == null ) adminUser . links = { } ;
2019-02-15 01:44:38 +03:00
adminUser . links [ obj . dbMeshKey ] = { rights : 0xFFFFFFFF } ;
2019-03-08 09:47:27 +03:00
db . SetUser ( adminUser ) ;
parent . parent . DispatchEvent ( [ '*' , obj . dbMeshKey , adminUser . _id ] , obj , { etype : 'mesh' , username : adminUser . name , meshid : obj . dbMeshKey , name : meshname , mtype : 2 , desc : '' , action : 'createmesh' , links : links , msg : 'Mesh created: ' + obj . meshid , domain : domain . id } ) ;
2019-02-15 01:44:38 +03:00
}
2019-02-19 01:32:55 +03:00
} else {
2019-02-20 20:39:41 +03:00
if ( ( mesh != null ) && ( mesh . deleted != null ) && ( mesh . links ) ) {
2019-02-19 01:32:55 +03:00
// Must un-delete this mesh
var ids = [ '*' , mesh . _id ] ;
// See if users still exists, if so, add links to the mesh
for ( var userid in mesh . links ) {
2019-03-08 09:47:27 +03:00
const user = parent . users [ userid ] ;
2019-02-19 01:32:55 +03:00
if ( user ) {
if ( user . links == null ) { user . links = { } ; }
if ( user . links [ mesh . _id ] == null ) {
user . links [ mesh . _id ] = { rights : mesh . links [ userid ] . rights } ;
ids . push ( user . _id ) ;
2019-03-08 09:47:27 +03:00
db . SetUser ( user ) ;
2019-02-19 01:32:55 +03:00
}
}
}
// Send out an event indicating this mesh was "created"
2019-03-08 09:47:27 +03:00
parent . parent . DispatchEvent ( ids , obj , { etype : 'mesh' , meshid : mesh . _id , name : mesh . name , mtype : mesh . mtype , desc : mesh . desc , action : 'createmesh' , links : mesh . links , msg : 'Mesh undeleted: ' + mesh . _id , domain : domain . id } ) ;
2019-02-19 01:32:55 +03:00
// Mark the mesh as active
delete mesh . deleted ;
2019-03-08 09:47:27 +03:00
db . Set ( common . escapeLinksFieldName ( mesh ) ) ;
2019-02-19 01:32:55 +03:00
}
2019-02-15 01:44:38 +03:00
}
return mesh ;
}
2019-05-28 20:58:46 +03:00
// Send a PING/PONG message
function sendPing ( ) { obj . send ( '{"action":"ping"}' ) ; }
function sendPong ( ) { obj . send ( '{"action":"pong"}' ) ; }
2017-08-28 19:27:45 +03:00
// Once we get all the information about an agent, run this to hook everything up to the server
function completeAgentConnection ( ) {
2019-03-10 21:47:03 +03:00
if ( ( obj . authenticated != 1 ) || ( obj . meshid == null ) || obj . pendingCompleteAgentConnection || ( obj . agentInfo == null ) ) { return ; }
2018-08-21 21:02:35 +03:00
obj . pendingCompleteAgentConnection = true ;
2019-05-28 20:58:46 +03:00
// Setup the agent PING/PONG timers
if ( ( typeof args . agentping == 'number' ) && ( obj . pingtimer == null ) ) { obj . pingtimer = setInterval ( sendPing , args . agentping * 1000 ) ; }
else if ( ( typeof args . agentpong == 'number' ) && ( obj . pongtimer == null ) ) { obj . pongtimer = setInterval ( sendPong , args . agentpong * 1000 ) ; }
2019-04-10 20:41:10 +03:00
// If this is a recovery agent
2019-04-10 01:51:05 +03:00
if ( obj . agentInfo . capabilities & 0x40 ) {
2019-04-10 20:41:10 +03:00
// Inform mesh agent that it's authenticated.
delete obj . pendingCompleteAgentConnection ;
obj . authenticated = 2 ;
obj . send ( common . ShortToStr ( 4 ) ) ;
// Ask for mesh core hash.
obj . send ( common . ShortToStr ( 11 ) + common . ShortToStr ( 0 ) ) ;
2019-04-10 01:51:05 +03:00
return ;
}
2019-02-12 01:41:15 +03:00
// Check if we have too many agent sessions
if ( typeof domain . limits . maxagentsessions == 'number' ) {
// Count the number of agent sessions for this domain
var domainAgentSessionCount = 0 ;
2019-03-08 09:47:27 +03:00
for ( var i in parent . wsagents ) { if ( parent . wsagents [ i ] . domain . id == domain . id ) { domainAgentSessionCount ++ ; } }
2019-02-12 01:41:15 +03:00
// Check if we have too many user sessions
2019-05-02 01:02:03 +03:00
if ( domainAgentSessionCount >= domain . limits . maxagentsessions ) {
// Too many, hold the connection.
parent . agentStats . agentMaxSessionHoldCount ++ ;
return ;
}
2019-02-12 01:41:15 +03:00
}
2019-02-15 01:44:38 +03:00
/ *
2017-08-28 19:27:45 +03:00
// Check that the mesh exists
2019-03-08 09:47:27 +03:00
var mesh = parent . meshes [ obj . dbMeshKey ] ;
2019-02-15 01:44:38 +03:00
if ( mesh == null ) {
var holdConnection = true ;
2019-03-08 09:47:27 +03:00
if ( typeof domain . orphanagentuser == 'string' ) {
var adminUser = parent . users [ 'user/' + domain . id + '/' + args . orphanagentuser ] ;
2019-02-15 01:44:38 +03:00
if ( ( adminUser != null ) && ( adminUser . siteadmin == 0xFFFFFFFF ) ) {
// Create a new mesh for this device
holdConnection = false ;
var links = { } ;
links [ user . _id ] = { name : adminUser . name , rights : 0xFFFFFFFF } ;
mesh = { type : 'mesh' , _id : obj . dbMeshKey , name : obj . meshid , mtype : 2 , desc : '' , domain : domain . id , links : links } ;
2019-03-08 09:47:27 +03:00
db . Set ( common . escapeLinksFieldName ( mesh ) ) ;
parent . meshes [ obj . meshid ] = mesh ;
parent . parent . AddEventDispatch ( [ obj . meshid ] , ws ) ;
2019-02-15 01:44:38 +03:00
if ( adminUser . links == null ) user . links = { } ;
adminUser . links [ obj . meshid ] = { rights : 0xFFFFFFFF } ;
2019-03-08 09:47:27 +03:00
//adminUser.subscriptions = parent.subscribe(adminUser._id, ws);
db . SetUser ( user ) ;
parent . parent . DispatchEvent ( [ '*' , meshid , user . _id ] , obj , { etype : 'mesh' , username : user . name , meshid : obj . meshid , name : obj . meshid , mtype : 2 , desc : '' , action : 'createmesh' , links : links , msg : 'Mesh created: ' + obj . meshid , domain : domain . id } ) ;
2019-02-15 01:44:38 +03:00
}
}
if ( holdConnection == true ) {
// If we disconnect, the agent will just reconnect. We need to log this or tell agent to connect in a few hours.
console . log ( 'Agent connected with invalid domain/mesh, holding connection (' + obj . remoteaddrport + ', ' + obj . dbMeshKey + ').' ) ;
return ;
}
}
2018-11-02 01:01:21 +03:00
if ( mesh . mtype != 2 ) { console . log ( 'Agent connected with invalid mesh type, holding connection (' + obj . remoteaddrport + ').' ) ; return ; } // If we disconnect, the agnet will just reconnect. We need to log this or tell agent to connect in a few hours.
2019-02-15 01:44:38 +03:00
* /
2018-08-22 01:08:15 +03:00
// Check that the node exists
2019-03-08 09:47:27 +03:00
db . Get ( obj . dbNodeKey , function ( err , nodes ) {
2019-03-10 21:47:03 +03:00
if ( obj . agentInfo == null ) { return ; }
2019-05-22 00:19:32 +03:00
var device , mesh ;
2018-08-22 01:08:15 +03:00
2018-09-25 03:47:03 +03:00
// See if this node exists in the database
2019-05-21 04:03:14 +03:00
if ( ( nodes == null ) || ( nodes . length == 0 ) ) {
2019-02-15 01:44:38 +03:00
// This device does not exist, use the meshid given by the device
2019-06-06 01:24:07 +03:00
// Check if we already have too many devices for this domain
if ( domain . limits && ( typeof domain . limits . maxdevices == 'number' ) ) {
db . isMaxType ( domain . limits . maxdevices , 'node' , domain . id , function ( ismax , count ) {
if ( ismax == true ) {
// Too many devices in this domain.
parent . agentStats . maxDomainDevicesReached ++ ;
} else {
// We are under the limit, create the new device.
completeAgentConnection2 ( ) ;
}
} ) ;
2017-08-28 19:27:45 +03:00
} else {
2019-06-06 01:24:07 +03:00
completeAgentConnection2 ( ) ;
2017-08-28 19:27:45 +03:00
}
2019-06-06 01:24:07 +03:00
return ;
2018-08-22 01:08:15 +03:00
} else {
device = nodes [ 0 ] ;
2019-02-15 01:44:38 +03:00
// This device exists, meshid given by the device must be ignored, use the server side one.
if ( device . meshid != obj . dbMeshKey ) {
obj . dbMeshKey = device . meshid ;
obj . meshid = device . meshid . split ( '/' ) [ 2 ] ;
}
// See if this mesh exists, if it does not we may want to create it.
2019-05-22 00:19:32 +03:00
mesh = getMeshAutoCreate ( ) ;
2019-02-15 01:44:38 +03:00
// Check if the mesh exists
if ( mesh == null ) {
// If we disconnect, the agent will just reconnect. We need to log this or tell agent to connect in a few hours.
2019-05-02 01:02:03 +03:00
parent . agentStats . invalidDomainMesh2Count ++ ;
2019-02-15 01:44:38 +03:00
console . log ( 'Agent connected with invalid domain/mesh, holding connection (' + obj . remoteaddrport + ', ' + obj . dbMeshKey + ').' ) ;
return ;
}
// Check if the mesh is the right type
if ( mesh . mtype != 2 ) {
// If we disconnect, the agent will just reconnect. We need to log this or tell agent to connect in a few hours.
2019-05-02 01:02:03 +03:00
parent . agentStats . invalidMeshType2Count ++ ;
2019-02-15 01:44:38 +03:00
console . log ( 'Agent connected with invalid mesh type, holding connection (' + obj . remoteaddrport + ').' ) ;
return ;
}
// Mark when this device connected
obj . connectTime = Date . now ( ) ;
2019-03-08 09:47:27 +03:00
db . Set ( { _id : 'lc' + obj . dbNodeKey , type : 'lastconnect' , domain : domain . id , time : obj . connectTime , addr : obj . remoteaddrport } ) ;
2019-02-15 01:44:38 +03:00
2019-04-06 07:21:28 +03:00
// Device already exists, look if changes have occured
2018-08-30 03:40:30 +03:00
var changes = [ ] , change = 0 , log = 0 ;
if ( device . agent == null ) { device . agent = { ver : obj . agentInfo . agentVersion , id : obj . agentInfo . agentId , caps : obj . agentInfo . capabilities } ; change = 1 ; }
if ( device . rname != obj . agentInfo . computerName ) { device . rname = obj . agentInfo . computerName ; change = 1 ; changes . push ( 'computer name' ) ; }
if ( device . agent . ver != obj . agentInfo . agentVersion ) { device . agent . ver = obj . agentInfo . agentVersion ; change = 1 ; changes . push ( 'agent version' ) ; }
if ( device . agent . id != obj . agentInfo . agentId ) { device . agent . id = obj . agentInfo . agentId ; change = 1 ; changes . push ( 'agent type' ) ; }
if ( ( device . agent . caps & 24 ) != ( obj . agentInfo . capabilities & 24 ) ) { device . agent . caps = obj . agentInfo . capabilities ; change = 1 ; changes . push ( 'agent capabilities' ) ; } // If agent console or javascript support changes, update capabilities
2019-04-06 07:21:28 +03:00
if ( mesh . flags && ( mesh . flags & 2 ) && ( device . name != obj . agentInfo . computerName ) ) { device . name = obj . agentInfo . computerName ; change = 1 ; } // We want the server name to be sync'ed to the hostname
2018-08-30 03:40:30 +03:00
if ( change == 1 ) {
2019-04-10 20:41:10 +03:00
// Do some clean up if needed, these values should not be in the database.
if ( device . conn != null ) { delete device . conn ; }
if ( device . pwr != null ) { delete device . pwr ; }
if ( device . agct != null ) { delete device . agct ; }
if ( device . cict != null ) { delete device . cict ; }
// Save the updated device in the database
2019-03-08 09:47:27 +03:00
db . Set ( device ) ;
2018-08-30 03:40:30 +03:00
// If this is a temporary device, don't log changes
if ( obj . agentInfo . capabilities & 0x20 ) { log = 0 ; }
// Event the node change
var event = { etype : 'node' , action : 'changenode' , nodeid : obj . dbNodeKey , domain : domain . id } ;
2018-09-25 03:47:03 +03:00
if ( log == 0 ) { event . nolog = 1 ; } else { event . msg = 'Changed device ' + device . name + ' from group ' + mesh . name + ': ' + changes . join ( ', ' ) ; }
2019-03-08 09:47:27 +03:00
const device2 = common . Clone ( device ) ;
2018-08-30 03:40:30 +03:00
if ( device2 . intelamt && device2 . intelamt . pass ) delete device2 . intelamt . pass ; // Remove the Intel AMT password before eventing this.
event . node = device ;
2019-05-29 03:25:23 +03:00
if ( db . changeStream ) { event . noact = 1 ; } // If DB change stream is active, don't use this event to change the node. Another event will come.
2019-03-08 09:47:27 +03:00
parent . parent . DispatchEvent ( [ '*' , device . meshid ] , obj , event ) ;
2018-08-22 01:08:15 +03:00
}
}
2017-08-28 19:27:45 +03:00
2019-06-07 19:44:39 +03:00
completeAgentConnection3 ( device , mesh ) ;
2019-06-06 01:24:07 +03:00
} ) ;
}
2019-06-07 19:44:39 +03:00
function completeAgentConnection2 ( ) {
2019-06-06 01:24:07 +03:00
// See if this mesh exists, if it does not we may want to create it.
var mesh = getMeshAutoCreate ( ) ;
// Check if the mesh exists
if ( mesh == null ) {
// If we disconnect, the agent will just reconnect. We need to log this or tell agent to connect in a few hours.
parent . agentStats . invalidDomainMeshCount ++ ;
console . log ( 'Agent connected with invalid domain/mesh, holding connection (' + obj . remoteaddrport + ', ' + obj . dbMeshKey + ').' ) ;
return ;
}
// Check if the mesh is the right type
if ( mesh . mtype != 2 ) {
// If we disconnect, the agent will just reconnect. We need to log this or tell agent to connect in a few hours.
parent . agentStats . invalidMeshTypeCount ++ ;
console . log ( 'Agent connected with invalid mesh type, holding connection (' + obj . remoteaddrport + ').' ) ;
return ;
}
// Mark when this device connected
obj . connectTime = Date . now ( ) ;
db . Set ( { _id : 'lc' + obj . dbNodeKey , type : 'lastconnect' , domain : domain . id , time : obj . connectTime , addr : obj . remoteaddrport } ) ;
// This node does not exist, create it.
var device = { type : 'node' , mtype : mesh . mtype , _id : obj . dbNodeKey , icon : obj . agentInfo . platformType , meshid : obj . dbMeshKey , name : obj . agentInfo . computerName , rname : obj . agentInfo . computerName , domain : domain . id , agent : { ver : obj . agentInfo . agentVersion , id : obj . agentInfo . agentId , caps : obj . agentInfo . capabilities } , host : null } ;
db . Set ( device ) ;
// Event the new node
if ( obj . agentInfo . capabilities & 0x20 ) {
// This is a temporary agent, don't log.
parent . parent . DispatchEvent ( [ '*' , obj . dbMeshKey ] , obj , { etype : 'node' , action : 'addnode' , node : device , domain : domain . id , nolog : 1 } ) ;
} else {
parent . parent . DispatchEvent ( [ '*' , obj . dbMeshKey ] , obj , { etype : 'node' , action : 'addnode' , node : device , msg : ( 'Added device ' + obj . agentInfo . computerName + ' to mesh ' + mesh . name ) , domain : domain . id } ) ;
}
2019-06-07 19:44:39 +03:00
completeAgentConnection3 ( device , mesh ) ;
2019-06-06 01:24:07 +03:00
}
2019-06-07 19:44:39 +03:00
function completeAgentConnection3 ( device , mesh ) {
2019-06-06 01:24:07 +03:00
// Check if this agent is already connected
const dupAgent = parent . wsagents [ obj . dbNodeKey ] ;
parent . wsagents [ obj . dbNodeKey ] = obj ;
if ( dupAgent ) {
// Record duplicate agents
if ( parent . duplicateAgentsLog [ obj . dbNodeKey ] == null ) {
if ( dupAgent . remoteaddr == obj . remoteaddr ) {
parent . duplicateAgentsLog [ obj . dbNodeKey ] = { name : device . name , group : mesh . name , ip : [ obj . remoteaddr ] , count : 1 } ;
2019-05-22 00:19:32 +03:00
} else {
2019-06-06 01:24:07 +03:00
parent . duplicateAgentsLog [ obj . dbNodeKey ] = { name : device . name , group : mesh . name , ip : [ obj . remoteaddr , dupAgent . remoteaddr ] , count : 1 } ;
2019-05-22 00:19:32 +03:00
}
2018-08-22 01:08:15 +03:00
} else {
2019-06-06 01:24:07 +03:00
parent . duplicateAgentsLog [ obj . dbNodeKey ] . name = device . name ;
parent . duplicateAgentsLog [ obj . dbNodeKey ] . group = mesh . name ;
parent . duplicateAgentsLog [ obj . dbNodeKey ] . count ++ ;
if ( parent . duplicateAgentsLog [ obj . dbNodeKey ] . ip . indexOf ( obj . remoteaddr ) == - 1 ) { parent . duplicateAgentsLog [ obj . dbNodeKey ] . ip . push ( obj . remoteaddr ) ; }
2018-08-22 01:08:15 +03:00
}
2017-09-06 03:19:28 +03:00
2019-06-06 01:24:07 +03:00
// Close the duplicate agent
parent . agentStats . duplicateAgentCount ++ ;
if ( obj . nodeid != null ) { parent . parent . debug ( 1 , 'Duplicate agent ' + obj . nodeid + ' (' + obj . remoteaddrport + ')' ) ; }
dupAgent . close ( 3 ) ;
} else {
// Indicate the agent is connected
parent . parent . SetConnectivityState ( obj . dbMeshKey , obj . dbNodeKey , obj . connectTime , 1 , 1 ) ;
}
2018-08-22 01:08:15 +03:00
2019-06-06 01:24:07 +03:00
// We are done, ready to communicate with this agent
delete obj . pendingCompleteAgentConnection ;
obj . authenticated = 2 ;
2019-01-23 06:34:55 +03:00
2019-06-06 01:24:07 +03:00
// Check how many times this agent disconnected in the last few minutes.
const disconnectCount = parent . wsagentsDisconnections [ obj . nodeid ] ;
if ( disconnectCount > 6 ) {
console . log ( 'Agent in big trouble: NodeId=' + obj . nodeid + ', IP=' + obj . remoteaddrport + ', Agent=' + obj . agentInfo . agentId + '.' ) ;
// TODO: Log or do something to recover?
return ;
}
2018-08-22 01:08:15 +03:00
2019-06-06 01:24:07 +03:00
// Command 4, inform mesh agent that it's authenticated.
obj . send ( common . ShortToStr ( 4 ) ) ;
2019-01-23 06:34:55 +03:00
2019-06-06 01:24:07 +03:00
if ( disconnectCount > 4 ) {
// Too many disconnections, this agent has issues. Just clear the core.
obj . send ( common . ShortToStr ( 10 ) + common . ShortToStr ( 0 ) ) ;
//console.log('Agent in trouble: NodeId=' + obj.nodeid + ', IP=' + obj.remoteaddrport + ', Agent=' + obj.agentInfo.agentId + '.');
// TODO: Log or do something to recover?
return ;
}
2019-03-11 21:18:50 +03:00
2019-06-06 01:24:07 +03:00
// Not sure why, but in rare cases, obj.agentInfo is undefined here.
if ( ( obj . agentInfo == null ) || ( typeof obj . agentInfo . capabilities != 'number' ) ) { return ; } // This is an odd case.
2019-04-10 01:51:05 +03:00
2019-06-06 01:24:07 +03:00
// Check if we need to make an native update check
obj . agentExeInfo = parent . parent . meshAgentBinaries [ obj . agentInfo . agentId ] ;
2019-06-18 01:20:47 +03:00
var corename = null ;
if ( parent . parent . meshAgentsArchitectureNumbers [ obj . agentInfo . agentId ] != null ) {
corename = parent . parent . meshAgentsArchitectureNumbers [ obj . agentInfo . agentId ] . core ;
} else {
// MeshCommand_CoreModule, ask mesh agent to clear the core
obj . send ( common . ShortToStr ( 10 ) + common . ShortToStr ( 0 ) ) ;
}
2019-06-06 01:24:07 +03:00
if ( ( obj . agentExeInfo != null ) && ( obj . agentExeInfo . update == true ) ) {
// Ask the agent for it's executable binary hash
obj . send ( common . ShortToStr ( 12 ) + common . ShortToStr ( 0 ) ) ;
} else {
// Check the mesh core, if the agent is capable of running one
if ( ( ( obj . agentInfo . capabilities & 16 ) != 0 ) && ( corename != null ) ) {
obj . send ( common . ShortToStr ( 11 ) + common . ShortToStr ( 0 ) ) ; // Command 11, ask for mesh core hash.
2019-01-22 01:05:50 +03:00
} else {
2019-06-06 01:24:07 +03:00
agentCoreIsStable ( ) ; // No updates needed, agent is ready to go.
2019-01-22 01:05:50 +03:00
}
2019-06-06 01:24:07 +03:00
}
2019-02-08 02:00:10 +03:00
}
2019-01-22 01:05:50 +03:00
2019-03-11 07:40:25 +03:00
// Take a basic Intel AMT policy and add all server information to it, making it ready to send to this agent.
function completeIntelAmtPolicy ( amtPolicy ) {
2019-06-13 05:40:27 +03:00
var r = amtPolicy ;
2019-03-11 07:40:25 +03:00
if ( amtPolicy == null ) return null ;
if ( amtPolicy . type == 2 ) {
2019-06-13 05:40:27 +03:00
// CCM - Add server root certificate
2019-03-11 07:40:25 +03:00
if ( parent . parent . certificates . rootex == null ) { parent . parent . certificates . rootex = parent . parent . certificates . root . cert . split ( '-----BEGIN CERTIFICATE-----' ) . join ( '' ) . split ( '-----END CERTIFICATE-----' ) . join ( '' ) . split ( '\r' ) . join ( '' ) . split ( '\n' ) . join ( '' ) ; }
2019-06-13 05:40:27 +03:00
r . rootcert = parent . parent . certificates . rootex ;
2019-06-20 03:16:50 +03:00
if ( ( amtPolicy . cirasetup == 2 ) && ( parent . parent . mpsserver != null ) && ( parent . parent . certificates . AmtMpsName != null ) && ( args . lanonly != true ) && ( args . mpsport != 0 ) ) {
// Add server CIRA settings
r . ciraserver = {
name : parent . parent . certificates . AmtMpsName ,
port : ( typeof args . mpsaliasport == 'number' ? args . mpsaliasport : args . mpsport ) ,
user : obj . meshid . replace ( /\@/g , 'X' ) . replace ( /\$/g , 'X' ) . substring ( 0 , 16 ) ,
pass : args . mpspass ? args . mpspass : 'A@xew9rt' , // If the MPS password is not set, just use anything. TODO: Use the password as an agent identifier?
home : [ 'sdlwerulis3wpj95dfj' ] // Use a random FQDN to not have any home network.
} ;
if ( Array . isArray ( args . ciralocalfqdn ) ) { r . ciraserver . home = args . ciralocalfqdn ; }
}
} else if ( ( amtPolicy . type == 3 ) && ( domain . amtacmactivation . acmmatch ) ) {
2019-06-13 05:40:27 +03:00
// ACM - In this mode, don't send much to Intel AMT. Just indicate ACM policy and let the agent try activation when possible.
2019-06-20 03:16:50 +03:00
r = { type : 3 , match : domain . amtacmactivation . acmmatch } ;
2019-03-11 07:40:25 +03:00
}
2019-06-13 05:40:27 +03:00
return r ;
2019-03-11 07:40:25 +03:00
}
// Send Intel AMT policy
obj . sendUpdatedIntelAmtPolicy = function ( policy ) {
if ( obj . agentExeInfo && ( obj . agentExeInfo . amt == true ) ) { // Only send Intel AMT policy to agents what could have AMT.
if ( policy == null ) { var mesh = parent . meshes [ obj . dbMeshKey ] ; if ( mesh == null ) return ; policy = mesh . amt ; }
2019-03-11 21:18:50 +03:00
if ( policy != null ) { try { obj . send ( JSON . stringify ( { action : 'amtPolicy' , amtPolicy : completeIntelAmtPolicy ( common . Clone ( policy ) ) } ) ) ; } catch ( ex ) { } }
2019-03-08 06:56:24 +03:00
}
}
2019-04-10 01:51:05 +03:00
function recoveryAgentCoreIsStable ( mesh ) {
2019-05-02 01:02:03 +03:00
parent . agentStats . recoveryCoreIsStableCount ++ ;
2019-04-10 01:51:05 +03:00
// Recovery agent is doing ok, lets perform main agent checking.
//console.log('recoveryAgentCoreIsStable()');
// Fetch the the real agent nodeid
2019-04-12 21:28:57 +03:00
db . Get ( 'da' + obj . dbNodeKey , function ( err , nodes , self )
{
2019-05-21 04:03:14 +03:00
if ( ( nodes != null ) && ( nodes . length == 1 ) )
2019-04-12 21:28:57 +03:00
{
self . realNodeKey = nodes [ 0 ] . raid ;
2019-04-13 02:12:31 +03:00
// Get agent connection state
var agentConnected = false ;
var state = parent . parent . GetConnectivityState ( self . realNodeKey ) ;
if ( state ) { agentConnected = ( ( state . connectivity & 1 ) != 0 ) }
self . send ( JSON . stringify ( { action : 'diagnostic' , value : { command : 'query' , value : self . realNodeKey , agent : agentConnected } } ) ) ;
2019-04-12 21:28:57 +03:00
} else
{
self . send ( JSON . stringify ( { action : 'diagnostic' , value : { command : 'query' , value : null } } ) ) ;
2019-04-10 01:51:05 +03:00
}
2019-04-12 21:28:57 +03:00
} , obj ) ;
2019-04-10 01:51:05 +03:00
}
2019-02-08 02:00:10 +03:00
function agentCoreIsStable ( ) {
2019-05-02 01:02:03 +03:00
parent . agentStats . coreIsStableCount ++ ;
2019-02-08 02:00:10 +03:00
// Check that the mesh exists
2019-03-08 09:47:27 +03:00
const mesh = parent . meshes [ obj . dbMeshKey ] ;
2019-02-08 02:00:10 +03:00
if ( mesh == null ) {
2019-05-02 01:02:03 +03:00
parent . agentStats . meshDoesNotExistCount ++ ;
2019-02-08 02:00:10 +03:00
// TODO: Mark this agent as part of a mesh that does not exists.
return ; // Probably not worth doing anything else. Hold this agent.
}
2019-04-10 20:41:10 +03:00
// Check if this is a recovery agent
if ( obj . agentInfo . capabilities & 0x40 ) {
recoveryAgentCoreIsStable ( mesh ) ;
return ;
}
2019-05-02 01:02:03 +03:00
// Fetch the the diagnostic agent nodeid
2019-04-10 01:51:05 +03:00
db . Get ( 'ra' + obj . dbNodeKey , function ( err , nodes ) {
2019-05-21 04:03:14 +03:00
if ( ( nodes != null ) && ( nodes . length == 1 ) ) {
2019-04-10 01:51:05 +03:00
obj . diagnosticNodeKey = nodes [ 0 ] . daid ;
obj . send ( JSON . stringify ( { action : 'diagnostic' , value : { command : 'query' , value : obj . diagnosticNodeKey } } ) ) ;
}
} ) ;
2019-02-08 02:00:10 +03:00
// Send Intel AMT policy
2019-03-11 21:18:50 +03:00
if ( obj . agentExeInfo && ( obj . agentExeInfo . amt == true ) && ( mesh . amt != null ) ) { // Only send Intel AMT policy to agents what could have AMT.
try { obj . send ( JSON . stringify ( { action : 'amtPolicy' , amtPolicy : completeIntelAmtPolicy ( common . Clone ( mesh . amt ) ) } ) ) ; } catch ( ex ) { }
2019-03-11 07:40:25 +03:00
}
2019-02-08 02:00:10 +03:00
2019-08-07 03:58:29 +03:00
// Fetch system information
2019-08-07 21:08:36 +03:00
/ *
2019-08-07 03:58:29 +03:00
db . GetHash ( 'si' + obj . dbNodeKey , function ( err , results ) {
if ( ( results != null ) && ( results . length == 1 ) ) { obj . send ( JSON . stringify ( { action : 'sysinfo' , hash : results [ 0 ] . hash } ) ) ; } else { obj . send ( JSON . stringify ( { action : 'sysinfo' } ) ) ; }
} ) ;
2019-08-07 21:08:36 +03:00
* /
2019-08-07 03:58:29 +03:00
2019-02-08 02:00:10 +03:00
// Do this if IP location is enabled on this domain TODO: Set IP location per device group?
if ( domain . iplocation == true ) {
// Check if we already have IP location information for this node
2019-03-08 09:47:27 +03:00
db . Get ( 'iploc_' + obj . remoteaddr , function ( err , iplocs ) {
2019-05-21 04:03:14 +03:00
if ( ( iplocs != null ) && ( iplocs . length == 1 ) ) {
2019-02-08 02:00:10 +03:00
// We have a location in the database for this remote IP
2019-03-08 09:47:27 +03:00
const iploc = nodes [ 0 ] , x = { } ;
2019-02-08 02:00:10 +03:00
if ( ( iploc != null ) && ( iploc . ip != null ) && ( iploc . loc != null ) ) {
x . publicip = iploc . ip ;
x . iploc = iploc . loc + ',' + ( Math . floor ( ( new Date ( iploc . date ) ) / 1000 ) ) ;
ChangeAgentLocationInfo ( x ) ;
}
} else {
// Check if we need to ask for the IP location
var doIpLocation = 0 ;
if ( device . iploc == null ) {
doIpLocation = 1 ;
2017-09-06 03:19:28 +03:00
} else {
2019-03-08 09:47:27 +03:00
const loc = device . iploc . split ( ',' ) ;
2019-02-08 02:00:10 +03:00
if ( loc . length < 3 ) {
doIpLocation = 2 ;
2017-09-06 20:08:01 +03:00
} else {
2019-02-08 02:00:10 +03:00
var t = new Date ( ( parseFloat ( loc [ 2 ] ) * 1000 ) ) , now = Date . now ( ) ;
t . setDate ( t . getDate ( ) + 20 ) ;
if ( t < now ) { doIpLocation = 3 ; }
2017-09-06 20:08:01 +03:00
}
2019-02-08 02:00:10 +03:00
}
2017-09-06 03:19:28 +03:00
2019-02-08 02:00:10 +03:00
// If we need to ask for IP location, see if we have the quota to do it.
if ( doIpLocation > 0 ) {
2019-03-08 09:47:27 +03:00
db . getValueOfTheDay ( 'ipLocationRequestLimitor' , 10 , function ( ipLocationLimitor ) {
2019-05-21 04:03:14 +03:00
if ( ( ipLocationLimitor != null ) && ( ipLocationLimitor . value > 0 ) ) {
2019-02-08 02:00:10 +03:00
ipLocationLimitor . value -- ;
2019-03-08 09:47:27 +03:00
db . Set ( ipLocationLimitor ) ;
2019-02-08 02:00:10 +03:00
obj . send ( JSON . stringify ( { action : 'iplocation' } ) ) ;
}
} ) ;
2017-09-06 20:08:01 +03:00
}
2019-02-08 02:00:10 +03:00
}
} ) ;
}
2017-08-28 19:27:45 +03:00
}
2018-01-03 03:52:49 +03:00
2018-12-01 03:42:58 +03:00
// Get the web certificate private key hash for the specified domain
2018-01-03 03:52:49 +03:00
function getWebCertHash ( domain ) {
2019-03-08 09:47:27 +03:00
const hash = parent . webCertificateHashs [ domain . id ] ;
2018-01-12 22:41:26 +03:00
if ( hash != null ) return hash ;
2019-03-08 09:47:27 +03:00
return parent . webCertificateHash ;
2018-01-03 03:52:49 +03:00
}
2018-12-01 03:42:58 +03:00
// Get the web certificate hash for the specified domain
function getWebCertFullHash ( domain ) {
2019-03-08 09:47:27 +03:00
const hash = parent . webCertificateFullHashs [ domain . id ] ;
2018-12-01 03:42:58 +03:00
if ( hash != null ) return hash ;
2019-03-08 09:47:27 +03:00
return parent . webCertificateFullHash ;
2018-12-01 03:42:58 +03:00
}
2017-08-28 19:27:45 +03:00
// Verify the agent signature
function processAgentSignature ( msg ) {
2019-03-08 09:47:27 +03:00
if ( args . ignoreagenthashcheck !== true ) {
2019-01-11 21:02:54 +03:00
var verified = false ;
if ( msg . length != 384 ) {
// Verify a PKCS7 signature.
var msgDer = null ;
2019-03-08 09:47:27 +03:00
try { msgDer = forge . asn1 . fromDer ( forge . util . createBuffer ( msg , 'binary' ) ) ; } catch ( ex ) { }
2019-01-11 21:02:54 +03:00
if ( msgDer != null ) {
try {
2019-03-08 09:47:27 +03:00
var p7 = forge . pkcs7 . messageFromAsn1 ( msgDer ) ;
2019-01-11 21:02:54 +03:00
var sig = p7 . rawCapture . signature ;
// Verify with key hash
2019-03-08 09:47:27 +03:00
var buf = Buffer . from ( getWebCertHash ( domain ) + obj . nonce + obj . agentnonce , 'binary' ) ;
var verifier = parent . crypto . createVerify ( 'RSA-SHA384' ) ;
2019-01-11 21:02:54 +03:00
verifier . update ( buf ) ;
verified = verifier . verify ( obj . unauth . nodeCertPem , sig , 'binary' ) ;
if ( verified == false ) {
// Verify with full hash
2019-03-08 09:47:27 +03:00
buf = Buffer . from ( getWebCertFullHash ( domain ) + obj . nonce + obj . agentnonce , 'binary' ) ;
verifier = parent . crypto . createVerify ( 'RSA-SHA384' ) ;
2019-01-11 21:02:54 +03:00
verifier . update ( buf ) ;
verified = verifier . verify ( obj . unauth . nodeCertPem , sig , 'binary' ) ;
}
2019-05-02 01:02:03 +03:00
if ( verified == false ) {
// Not a valid signature
parent . agentStats . invalidPkcsSignatureCount ++ ;
return false ;
}
2019-01-11 21:02:54 +03:00
} catch ( ex ) { } ;
}
}
if ( verified == false ) {
// Verify the RSA signature. This is the fast way, without using forge.
2019-03-08 09:47:27 +03:00
const verify = parent . crypto . createVerify ( 'SHA384' ) ;
verify . end ( Buffer . from ( getWebCertHash ( domain ) + obj . nonce + obj . agentnonce , 'binary' ) ) ; // Test using the private key hash
2019-01-11 21:02:54 +03:00
if ( verify . verify ( obj . unauth . nodeCertPem , Buffer . from ( msg , 'binary' ) ) !== true ) {
2019-03-08 09:47:27 +03:00
const verify2 = parent . crypto . createVerify ( 'SHA384' ) ;
verify2 . end ( Buffer . from ( getWebCertFullHash ( domain ) + obj . nonce + obj . agentnonce , 'binary' ) ) ; // Test using the full cert hash
2019-05-02 01:02:03 +03:00
if ( verify2 . verify ( obj . unauth . nodeCertPem , Buffer . from ( msg , 'binary' ) ) !== true ) {
parent . agentStats . invalidRsaSignatureCount ++ ;
return false ;
}
2019-01-11 21:02:54 +03:00
}
2018-12-15 23:34:55 +03:00
}
2018-12-01 03:42:58 +03:00
}
2017-08-28 19:27:45 +03:00
// Connection is a success, clean up
2017-10-16 03:36:06 +03:00
obj . nodeid = obj . unauth . nodeid ;
2017-08-28 19:27:45 +03:00
obj . dbNodeKey = 'node/' + domain . id + '/' + obj . nodeid ;
delete obj . nonce ;
delete obj . agentnonce ;
delete obj . unauth ;
2019-03-08 09:47:27 +03:00
delete obj . receivedCommands ;
2017-08-28 19:27:45 +03:00
if ( obj . unauthsign ) delete obj . unauthsign ;
2019-05-02 01:02:03 +03:00
parent . agentStats . verifiedAgentConnectionCount ++ ;
2019-03-08 09:47:27 +03:00
parent . parent . debug ( 1 , 'Verified agent connection to ' + obj . nodeid + ' (' + obj . remoteaddrport + ').' ) ;
2017-08-28 19:27:45 +03:00
obj . authenticated = 1 ;
return true ;
}
// Process incoming agent JSON data
function processAgentData ( msg ) {
2019-03-08 09:47:27 +03:00
var i , str = msg . toString ( 'utf8' ) , command = null ;
2017-08-28 19:27:45 +03:00
if ( str [ 0 ] == '{' ) {
2019-05-02 01:02:03 +03:00
try { command = JSON . parse ( str ) ; } catch ( ex ) { parent . agentStats . invalidJsonCount ++ ; console . log ( 'Unable to parse agent JSON (' + obj . remoteaddrport + '): ' + str , ex ) ; return ; } // If the command can't be parsed, ignore it.
2018-08-27 22:24:15 +03:00
if ( typeof command != 'object' ) { return ; }
2017-08-28 19:27:45 +03:00
switch ( command . action ) {
case 'msg' :
{
// Route a message.
// If this command has a sessionid, that is the target.
2017-09-15 21:45:06 +03:00
if ( command . sessionid != null ) {
2017-10-24 00:09:58 +03:00
if ( typeof command . sessionid != 'string' ) break ;
2017-08-28 19:27:45 +03:00
var splitsessionid = command . sessionid . split ( '/' ) ;
// Check that we are in the same domain and the user has rights over this node.
if ( ( splitsessionid [ 0 ] == 'user' ) && ( splitsessionid [ 1 ] == domain . id ) ) {
// Check if this user has rights to get this message
2017-09-15 21:45:06 +03:00
//if (mesh.links[user._id] == null || ((mesh.links[user._id].rights & 16) == 0)) return; // TODO!!!!!!!!!!!!!!!!!!!!!
2018-08-30 03:40:30 +03:00
2017-09-15 21:45:06 +03:00
// See if the session is connected. If so, go ahead and send this message to the target node
2019-03-08 09:47:27 +03:00
var ws = parent . wssessions2 [ command . sessionid ] ;
2017-09-15 21:45:06 +03:00
if ( ws != null ) {
2017-08-28 19:27:45 +03:00
command . nodeid = obj . dbNodeKey ; // Set the nodeid, required for responses.
delete command . sessionid ; // Remove the sessionid, since we are sending to that sessionid, so it's implyed.
2018-09-25 03:47:03 +03:00
try { ws . send ( JSON . stringify ( command ) ) ; } catch ( ex ) { }
2019-03-08 09:47:27 +03:00
} else if ( parent . parent . multiServer != null ) {
2017-09-21 00:44:22 +03:00
// See if we can send this to a peer server
2019-03-08 09:47:27 +03:00
var serverid = parent . wsPeerSessions2 [ command . sessionid ] ;
2017-09-21 00:44:22 +03:00
if ( serverid != null ) {
command . fromNodeid = obj . dbNodeKey ;
2019-03-08 09:47:27 +03:00
parent . parent . multiServer . DispatchMessageSingleServer ( command , serverid ) ;
2017-09-21 00:44:22 +03:00
}
2017-08-28 19:27:45 +03:00
}
}
2017-09-15 21:45:06 +03:00
} else if ( command . userid != null ) { // If this command has a userid, that is the target.
2017-10-24 00:09:58 +03:00
if ( typeof command . userid != 'string' ) break ;
2017-08-28 19:27:45 +03:00
var splituserid = command . userid . split ( '/' ) ;
// Check that we are in the same domain and the user has rights over this node.
if ( ( splituserid [ 0 ] == 'user' ) && ( splituserid [ 1 ] == domain . id ) ) {
// Check if this user has rights to get this message
2017-09-15 21:45:06 +03:00
//if (mesh.links[user._id] == null || ((mesh.links[user._id].rights & 16) == 0)) return; // TODO!!!!!!!!!!!!!!!!!!!!!
2017-08-28 19:27:45 +03:00
// See if the session is connected
2019-03-08 09:47:27 +03:00
var sessions = parent . wssessions [ command . userid ] ;
2017-08-28 19:27:45 +03:00
// Go ahead and send this message to the target node
2017-09-15 21:45:06 +03:00
if ( sessions != null ) {
2017-08-28 19:27:45 +03:00
command . nodeid = obj . dbNodeKey ; // Set the nodeid, required for responses.
delete command . userid ; // Remove the userid, since we are sending to that userid, so it's implyed.
2018-08-30 03:40:30 +03:00
for ( i in sessions ) { sessions [ i ] . send ( JSON . stringify ( command ) ) ; }
2017-08-28 19:27:45 +03:00
}
2018-04-12 21:15:01 +03:00
2019-03-08 09:47:27 +03:00
if ( parent . parent . multiServer != null ) {
2018-04-12 21:15:01 +03:00
// TODO: Add multi-server support
}
2017-08-28 19:27:45 +03:00
}
} else { // Route this command to the mesh
2017-09-15 21:45:06 +03:00
command . nodeid = obj . dbNodeKey ;
var cmdstr = JSON . stringify ( command ) ;
2019-03-08 09:47:27 +03:00
for ( var userid in parent . wssessions ) { // Find all connected users for this mesh and send the message
var user = parent . users [ userid ] ;
2018-06-05 23:28:07 +03:00
if ( ( user != null ) && ( user . links != null ) ) {
2017-08-28 19:27:45 +03:00
var rights = user . links [ obj . dbMeshKey ] ;
2017-09-15 21:45:06 +03:00
if ( rights != null ) { // TODO: Look at what rights are needed for message routing
2019-03-08 09:47:27 +03:00
var xsessions = parent . wssessions [ userid ] ;
2017-09-15 21:45:06 +03:00
// Send the message to all users on this server
2018-08-30 03:40:30 +03:00
for ( i in xsessions ) { try { xsessions [ i ] . send ( cmdstr ) ; } catch ( e ) { } }
2017-08-28 19:27:45 +03:00
}
}
}
2017-09-15 21:45:06 +03:00
// Send the message to all users of other servers
2019-03-08 09:47:27 +03:00
if ( parent . parent . multiServer != null ) {
2017-09-15 21:45:06 +03:00
delete command . nodeid ;
command . fromNodeid = obj . dbNodeKey ;
command . meshid = obj . dbMeshKey ;
2019-03-08 09:47:27 +03:00
parent . parent . multiServer . DispatchMessage ( command ) ;
2017-09-15 21:45:06 +03:00
}
2017-08-28 19:27:45 +03:00
}
break ;
}
case 'coreinfo' :
{
// Sent by the agent to update agent information
ChangeAgentCoreInfo ( command ) ;
2018-09-28 02:17:05 +03:00
break ;
}
case 'smbios' :
{
2019-02-25 22:13:13 +03:00
// Store the RAW SMBios table of this computer
2019-02-26 00:14:04 +03:00
// We store SMBIOS information as a string because we don't want the MongoDB to attempt to store all of the sub-documents seperatly.
// If an agent sends an insanely large SMBIOS table, don't store it.
try {
var smbiosstr = JSON . stringify ( command . value ) ;
2019-03-08 09:47:27 +03:00
if ( smbiosstr . length < 65535 ) { db . SetSMBIOS ( { _id : obj . dbNodeKey , domain : domain . id , time : new Date ( ) , value : smbiosstr } ) ; }
2019-02-26 00:14:04 +03:00
} catch ( ex ) { }
2018-09-28 02:17:05 +03:00
// Event the node interface information change (This is a lot of traffic, probably don't need this).
2019-03-08 09:47:27 +03:00
//parent.parent.DispatchEvent(['*', obj.meshid], obj, { action: 'smBiosChange', nodeid: obj.dbNodeKey, domain: domain.id, smbios: command.value, nolog: 1 });
2018-09-28 02:17:05 +03:00
2017-08-28 19:27:45 +03:00
break ;
}
case 'netinfo' :
{
// Sent by the agent to update agent network interface information
delete command . action ;
command . updateTime = Date . now ( ) ;
command . _id = 'if' + obj . dbNodeKey ;
command . type = 'ifinfo' ;
2019-03-08 09:47:27 +03:00
db . Set ( command ) ;
2017-08-28 19:27:45 +03:00
// Event the node interface information change
2019-03-08 09:47:27 +03:00
parent . parent . DispatchEvent ( [ '*' , obj . meshid ] , obj , { action : 'ifchange' , nodeid : obj . dbNodeKey , domain : domain . id , nolog : 1 } ) ;
2017-08-28 19:27:45 +03:00
2017-08-28 22:48:53 +03:00
break ;
}
2017-09-06 03:19:28 +03:00
case 'iplocation' :
2017-08-28 22:48:53 +03:00
{
// Sent by the agent to update location information
2017-08-30 23:52:56 +03:00
if ( ( command . type == 'publicip' ) && ( command . value != null ) && ( typeof command . value == 'object' ) && ( command . value . ip ) && ( command . value . loc ) ) {
2017-08-29 01:06:29 +03:00
var x = { } ;
x . publicip = command . value . ip ;
2018-08-30 03:40:30 +03:00
x . iploc = command . value . loc + ',' + ( Math . floor ( Date . now ( ) / 1000 ) ) ;
2017-08-29 01:06:29 +03:00
ChangeAgentLocationInfo ( x ) ;
2017-09-06 20:08:01 +03:00
command . value . _id = 'iploc_' + command . value . ip ;
command . value . type = 'iploc' ;
command . value . date = Date . now ( ) ;
2019-03-08 09:47:27 +03:00
db . Set ( command . value ) ; // Store the IP to location data in the database
2017-09-06 20:08:01 +03:00
// Sample Value: { ip: '192.55.64.246', city: 'Hillsboro', region: 'Oregon', country: 'US', loc: '45.4443,-122.9663', org: 'AS4983 Intel Corporation', postal: '97123' }
2017-08-29 01:06:29 +03:00
}
2017-08-28 19:27:45 +03:00
break ;
}
2017-11-08 04:05:22 +03:00
case 'mc1migration' :
{
if ( command . oldnodeid . length != 64 ) break ;
2019-03-08 09:47:27 +03:00
const oldNodeKey = 'node//' + command . oldnodeid . toLowerCase ( ) ;
db . Get ( oldNodeKey , function ( err , nodes ) {
2019-05-21 04:03:14 +03:00
if ( ( nodes != null ) && ( nodes . length != 1 ) ) return ;
2019-03-08 09:47:27 +03:00
const node = nodes [ 0 ] ;
2017-11-08 04:05:22 +03:00
if ( node . meshid == obj . dbMeshKey ) {
// Update the device name & host
2019-03-08 09:47:27 +03:00
const newNode = { "name" : node . name } ;
2017-11-10 04:18:30 +03:00
if ( node . intelamt != null ) { newNode . intelamt = node . intelamt ; }
ChangeAgentCoreInfo ( newNode ) ;
2017-11-08 04:05:22 +03:00
// Delete this node including network interface information and events
2019-03-08 09:47:27 +03:00
db . Remove ( node . _id ) ;
db . Remove ( 'if' + node . _id ) ;
2017-11-08 04:05:22 +03:00
// Event node deletion
2019-03-08 09:47:27 +03:00
const change = 'Migrated device ' + node . name ;
parent . parent . DispatchEvent ( [ '*' , node . meshid ] , obj , { etype : 'node' , action : 'removenode' , nodeid : node . _id , msg : change , domain : node . domain } ) ;
2017-11-08 04:05:22 +03:00
}
} ) ;
break ;
}
2019-01-24 23:08:48 +03:00
case 'openUrl' :
{
// Sent by the agent to return the status of a open URL action.
// Nothing is done right now.
break ;
}
2019-07-30 02:35:48 +03:00
case 'log' :
{
// Log a value in the event log
if ( ( typeof command . msg == 'string' ) && ( command . msg . length < 4096 ) ) {
var event = { etype : 'node' , action : 'agentlog' , nodeid : obj . dbNodeKey , domain : domain . id , msg : command . msg } ;
if ( typeof command . userid == 'string' ) {
var loguser = parent . users [ command . userid ] ;
if ( loguser ) { event . userid = command . userid ; event . username = loguser . name ; }
}
if ( ( typeof command . sessionid == 'string' ) && ( command . sessionid . length < 500 ) ) { event . sessionid = command . sessionid ; }
parent . parent . DispatchEvent ( [ '*' , obj . dbMeshKey ] , obj , event ) ;
}
break ;
}
2019-05-28 20:58:46 +03:00
case 'ping' : { sendPong ( ) ; break ; }
case 'pong' : { break ; }
2019-02-08 07:06:01 +03:00
case 'getScript' :
{
// Used by the agent to get configuration scripts.
if ( command . type == 1 ) {
2019-03-08 09:47:27 +03:00
parent . getCiraConfigurationScript ( obj . dbMeshKey , function ( script ) {
2019-02-08 07:06:01 +03:00
obj . send ( JSON . stringify ( { action : 'getScript' , type : 1 , script : script . toString ( ) } ) ) ;
} ) ;
} else if ( command . type == 2 ) {
2019-03-08 09:47:27 +03:00
parent . getCiraCleanupScript ( function ( script ) {
2019-02-08 07:06:01 +03:00
obj . send ( JSON . stringify ( { action : 'getScript' , type : 2 , script : script . toString ( ) } ) ) ;
} ) ;
}
break ;
}
2019-06-15 02:33:53 +03:00
case 'acmactivate' :
{
2019-06-21 00:27:57 +03:00
if ( obj . agentInfo . capabilities & 0x20 ) return ; // If this is a temporary device, don't do ACM activation.
// Get the current Intel AMT policy
var mesh = parent . meshes [ obj . dbMeshKey ] ;
if ( ( mesh == null ) || ( mesh . amt == null ) || ( mesh . amt . type != 3 ) || ( domain . amtacmactivation == null ) || ( domain . amtacmactivation . acmmatch == null ) || ( mesh . amt . password == null ) ) break ; // If this is not the right policy, ignore this.
// Get the Intel AMT admin password, randomize if needed.
var amtpassword = ( ( mesh . amt . password == '' ) ? getRandomAmtPassword ( ) : mesh . amt . password ) ;
if ( checkAmtPassword ( amtpassword ) == false ) return ; // Invalid Intel AMT password, this should never happen.
2019-06-15 02:33:53 +03:00
// Agent is asking the server to sign an Intel AMT ACM activation request
2019-06-21 00:27:57 +03:00
var signResponse = parent . parent . certificateOperations . signAcmRequest ( domain , command , 'admin' , amtpassword , obj . remoteaddrport , obj . dbNodeKey , obj . dbMeshKey , obj . agentInfo . computerName , obj . agentInfo . agentId ) ; // TODO: Place account credentials!!!
2019-06-22 03:18:48 +03:00
if ( ( signResponse != null ) && ( signResponse . error == null ) ) {
2019-06-21 00:27:57 +03:00
// Log this activation event
var event = { etype : 'node' , action : 'amtactivate' , nodeid : obj . dbNodeKey , domain : domain . id , msg : 'Device requested Intel AMT ACM activation, FQDN: ' + command . fqdn , ip : obj . remoteaddrport } ;
if ( db . changeStream ) { event . noact = 1 ; } // If DB change stream is active, don't use this event to change the node. Another event will come.
parent . parent . DispatchEvent ( [ '*' , obj . dbMeshKey ] , obj , event ) ;
// Update the device Intel AMT information
ChangeAgentCoreInfo ( { "intelamt" : { user : 'admin' , pass : amtpassword , uuid : command . uuid , realm : command . realm } } ) ;
// Send the activation response
2019-06-25 04:56:14 +03:00
obj . send ( JSON . stringify ( signResponse ) ) ;
2019-06-21 00:27:57 +03:00
}
2019-06-15 02:33:53 +03:00
break ;
}
2019-04-10 01:51:05 +03:00
case 'diagnostic' :
{
if ( typeof command . value == 'object' ) {
switch ( command . value . command ) {
case 'register' : {
// Only main agent can do this
2019-04-12 21:28:57 +03:00
if ( ( ( obj . agentInfo . capabilities & 0x40 ) == 0 ) && ( typeof command . value . value == 'string' ) && ( command . value . value . length == 64 ) )
{
2019-04-10 01:51:05 +03:00
// Store links to diagnostic agent id
2019-04-12 21:28:57 +03:00
var daNodeKey = 'node/' + domain . id + '/' + db . escapeBase64 ( command . value . value ) ;
2019-04-10 01:51:05 +03:00
db . Set ( { _id : 'da' + daNodeKey , domain : domain . id , time : obj . connectTime , raid : obj . dbNodeKey } ) ; // DiagnosticAgent --> Agent
db . Set ( { _id : 'ra' + obj . dbNodeKey , domain : domain . id , time : obj . connectTime , daid : daNodeKey } ) ; // Agent --> DiagnosticAgent
}
break ;
}
case 'query' : {
// Only the diagnostic agent can do
if ( ( obj . agentInfo . capabilities & 0x40 ) != 0 ) {
// Return nodeid of main agent + connection status
db . Get ( 'da' + obj . dbNodeKey , function ( err , nodes ) {
if ( nodes . length == 1 ) {
obj . realNodeKey = nodes [ 0 ] . raid ;
2019-04-13 02:12:31 +03:00
// Get agent connection state
var agentConnected = false ;
var state = parent . parent . GetConnectivityState ( obj . realNodeKey ) ;
if ( state ) { agentConnected = ( ( state . connectivity & 1 ) != 0 ) }
obj . send ( JSON . stringify ( { action : 'diagnostic' , value : { command : 'query' , value : obj . realNodeKey , agent : agentConnected } } ) ) ;
2019-04-10 01:51:05 +03:00
} else {
obj . send ( JSON . stringify ( { action : 'diagnostic' , value : { command : 'query' , value : null } } ) ) ;
}
} ) ;
}
break ;
}
case 'log' : {
2019-07-30 02:35:48 +03:00
if ( ( ( obj . agentInfo . capabilities & 0x40 ) != 0 ) && ( typeof command . value . value == 'string' ) && ( command . value . value . length < 256 ) ) {
// If this is a diagnostic agent, log the event in the log of the main agent
2019-04-12 23:24:15 +03:00
var event = { etype : 'node' , action : 'diagnostic' , nodeid : obj . realNodeKey , domain : domain . id , msg : command . value . value } ;
2019-04-10 01:51:05 +03:00
parent . parent . DispatchEvent ( [ '*' , obj . dbMeshKey ] , obj , event ) ;
}
break ;
}
}
}
break ;
}
2019-08-07 21:08:36 +03:00
/ *
2019-08-07 03:58:29 +03:00
case 'sysinfo' : {
//console.log('sysinfo', obj.nodeid, JSON.stringify(command.data.hash));
command . data . _id = 'si' + obj . dbNodeKey ;
db . Set ( command . data ) ; // Update system information in the database.
break ;
}
case 'sysinfocheck' : {
// Check system information update
db . GetHash ( 'si' + obj . dbNodeKey , function ( err , results ) {
if ( ( results != null ) && ( results . length == 1 ) ) { obj . send ( JSON . stringify ( { action : 'sysinfo' , hash : results [ 0 ] . hash } ) ) ; } else { obj . send ( JSON . stringify ( { action : 'sysinfo' } ) ) ; }
} ) ;
break ;
}
2019-08-07 21:08:36 +03:00
* /
2019-01-05 04:59:13 +03:00
default : {
2019-05-02 01:02:03 +03:00
parent . agentStats . unknownAgentActionCount ++ ;
2019-01-05 04:59:13 +03:00
console . log ( 'Unknown agent action (' + obj . remoteaddrport + '): ' + command . action + '.' ) ;
break ;
}
2017-08-28 19:27:45 +03:00
}
}
}
// Change the current core information string and event it
function ChangeAgentCoreInfo ( command ) {
2019-04-10 01:51:05 +03:00
if ( obj . agentInfo . capabilities & 0x40 ) return ;
2017-09-15 21:45:06 +03:00
if ( ( command == null ) || ( command == null ) ) return ; // Safety, should never happen.
2017-08-28 19:27:45 +03:00
// Check that the mesh exists
2019-03-08 09:47:27 +03:00
const mesh = parent . meshes [ obj . dbMeshKey ] ;
2018-08-22 01:08:15 +03:00
if ( mesh == null ) return ;
// Get the node and change it if needed
2019-03-08 09:47:27 +03:00
db . Get ( obj . dbNodeKey , function ( err , nodes ) { // TODO: THIS IS A BIG RACE CONDITION HERE, WE NEED TO FIX THAT. If this call is made twice at the same time on the same device, data will be missed.
2019-05-21 04:03:14 +03:00
if ( ( nodes == null ) || ( nodes . length != 1 ) ) return ;
2019-03-08 09:47:27 +03:00
const device = nodes [ 0 ] ;
2018-08-22 01:08:15 +03:00
if ( device . agent ) {
2019-01-31 00:43:42 +03:00
var changes = [ ] , change = 0 , log = 0 ;
2018-08-22 01:08:15 +03:00
2018-09-22 02:34:35 +03:00
//if (command.users) { console.log(command.users); }
2018-08-22 01:08:15 +03:00
// Check if anything changes
2019-01-31 00:43:42 +03:00
if ( command . name && ( command . name != device . name ) ) { change = 1 ; log = 1 ; device . name = command . name ; changes . push ( 'name' ) ; }
if ( ( command . caps != null ) && ( device . agent . core != command . value ) ) { if ( ( command . value == null ) && ( device . agent . core != null ) ) { delete device . agent . core ; } else { device . agent . core = command . value ; } change = 1 ; } // Don't save this as an event to the db.
if ( ( command . caps != null ) && ( ( device . agent . caps & 0xFFFFFFE7 ) != ( command . caps & 0xFFFFFFE7 ) ) ) { device . agent . caps = ( ( device . agent . caps & 24 ) + ( command . caps & 0xFFFFFFE7 ) ) ; change = 1 ; } // Allow Javascript on the agent to change all capabilities except console and javascript support, Don't save this as an event to the db.
2019-02-26 01:35:08 +03:00
if ( ( command . osdesc != null ) && ( device . osdesc != command . osdesc ) ) { device . osdesc = command . osdesc ; change = 1 ; changes . push ( 'os desc' ) ; } // Don't save this as an event to the db.
2019-03-15 22:30:04 +03:00
if ( device . ip != obj . remoteaddr ) { device . ip = obj . remoteaddr ; change = 1 ; }
2018-08-22 01:08:15 +03:00
if ( command . intelamt ) {
if ( ! device . intelamt ) { device . intelamt = { } ; }
2019-01-31 00:43:42 +03:00
if ( ( command . intelamt . ver != null ) && ( device . intelamt . ver != command . intelamt . ver ) ) { changes . push ( 'AMT version' ) ; device . intelamt . ver = command . intelamt . ver ; change = 1 ; log = 1 ; }
if ( ( command . intelamt . state != null ) && ( device . intelamt . state != command . intelamt . state ) ) { changes . push ( 'AMT state' ) ; device . intelamt . state = command . intelamt . state ; change = 1 ; log = 1 ; }
2018-09-22 02:34:35 +03:00
if ( ( command . intelamt . flags != null ) && ( device . intelamt . flags != command . intelamt . flags ) ) {
if ( device . intelamt . flags ) { changes . push ( 'AMT flags (' + device . intelamt . flags + ' --> ' + command . intelamt . flags + ')' ) ; } else { changes . push ( 'AMT flags (' + command . intelamt . flags + ')' ) ; }
2019-01-31 00:43:42 +03:00
device . intelamt . flags = command . intelamt . flags ; change = 1 ; log = 1 ;
2018-09-22 02:34:35 +03:00
}
2019-06-21 00:27:57 +03:00
if ( ( command . intelamt . realm != null ) && ( device . intelamt . realm != command . intelamt . realm ) ) { changes . push ( 'AMT realm' ) ; device . intelamt . realm = command . intelamt . realm ; change = 1 ; log = 1 ; }
2019-01-31 00:43:42 +03:00
if ( ( command . intelamt . host != null ) && ( device . intelamt . host != command . intelamt . host ) ) { changes . push ( 'AMT host' ) ; device . intelamt . host = command . intelamt . host ; change = 1 ; log = 1 ; }
if ( ( command . intelamt . uuid != null ) && ( device . intelamt . uuid != command . intelamt . uuid ) ) { changes . push ( 'AMT uuid' ) ; device . intelamt . uuid = command . intelamt . uuid ; change = 1 ; log = 1 ; }
2019-03-05 10:48:45 +03:00
if ( ( command . intelamt . user != null ) && ( device . intelamt . user != command . intelamt . user ) ) { changes . push ( 'AMT user' ) ; device . intelamt . user = command . intelamt . user ; change = 1 ; log = 1 ; }
if ( ( command . intelamt . pass != null ) && ( device . intelamt . pass != command . intelamt . pass ) ) { changes . push ( 'AMT pass' ) ; device . intelamt . pass = command . intelamt . pass ; change = 1 ; log = 1 ; }
2018-08-22 01:08:15 +03:00
}
2019-08-02 21:35:59 +03:00
if ( command . av ) {
if ( ! device . av ) { device . av = [ ] ; }
if ( ( command . av != null ) && ( JSON . stringify ( device . av ) != JSON . stringify ( command . av ) ) ) { changes . push ( 'AV status' ) ; device . av = command . av ; change = 1 ; log = 1 ; }
}
2019-01-31 00:43:42 +03:00
if ( ( command . users != null ) && ( device . users != command . users ) ) { device . users = command . users ; change = 1 ; } // Don't save this to the db.
2019-03-08 09:47:27 +03:00
if ( ( mesh . mtype == 2 ) && ( ! args . wanonly ) ) {
2019-02-21 02:26:27 +03:00
// In WAN mode, the hostname of a computer is not important. Don't log hostname changes.
if ( device . host != obj . remoteaddr ) { device . host = obj . remoteaddr ; change = 1 ; changes . push ( 'host' ) ; }
2018-08-22 01:08:15 +03:00
// TODO: Check that the agent has an interface that is the same as the one we got this websocket connection on. Only set if we have a match.
}
2017-08-28 19:27:45 +03:00
2018-09-22 02:34:35 +03:00
// If there are changes, event the new device
2018-08-22 01:08:15 +03:00
if ( change == 1 ) {
2019-04-10 20:41:10 +03:00
// Do some clean up if needed, these values should not be in the database.
if ( device . conn != null ) { delete device . conn ; }
if ( device . pwr != null ) { delete device . pwr ; }
if ( device . agct != null ) { delete device . agct ; }
if ( device . cict != null ) { delete device . cict ; }
2018-09-22 02:34:35 +03:00
// Save to the database
2019-03-08 09:47:27 +03:00
db . Set ( device ) ;
2017-08-28 19:27:45 +03:00
2018-08-22 01:08:15 +03:00
// Event the node change
2018-09-22 02:34:35 +03:00
var event = { etype : 'node' , action : 'changenode' , nodeid : obj . dbNodeKey , domain : domain . id } ;
2018-09-25 03:47:03 +03:00
if ( changes . length > 0 ) { event . msg = 'Changed device ' + device . name + ' from group ' + mesh . name + ': ' + changes . join ( ', ' ) ; }
2019-07-15 19:48:40 +03:00
if ( ( log == 0 ) || ( ( obj . agentInfo ) && ( obj . agentInfo . capabilities ) && ( obj . agentInfo . capabilities & 0x20 ) ) || ( changes . length == 0 ) ) { event . nolog = 1 ; } // If this is a temporary device, don't log changes
2019-03-08 09:47:27 +03:00
var device2 = common . Clone ( device ) ;
2019-01-31 00:43:42 +03:00
if ( device2 . intelamt && device2 . intelamt . pass ) { delete device2 . intelamt . pass ; } // Remove the Intel AMT password before eventing this.
2018-08-22 01:08:15 +03:00
event . node = device ;
2019-05-29 03:25:23 +03:00
if ( db . changeStream ) { event . noact = 1 ; } // If DB change stream is active, don't use this event to change the node. Another event will come.
2019-03-08 09:47:27 +03:00
parent . parent . DispatchEvent ( [ '*' , device . meshid ] , obj , event ) ;
2017-08-28 19:27:45 +03:00
}
2018-08-22 01:08:15 +03:00
}
2017-08-28 19:27:45 +03:00
} ) ;
}
2017-08-29 01:06:29 +03:00
// Change the current core information string and event it
function ChangeAgentLocationInfo ( command ) {
2019-04-10 01:51:05 +03:00
if ( obj . agentInfo . capabilities & 0x40 ) return ;
2018-08-22 01:08:15 +03:00
if ( ( command == null ) || ( command == null ) ) { return ; } // Safety, should never happen.
2017-08-29 01:06:29 +03:00
// Check that the mesh exists
2019-03-08 09:47:27 +03:00
const mesh = parent . meshes [ obj . dbMeshKey ] ;
2018-08-22 01:08:15 +03:00
if ( mesh == null ) return ;
// Get the node and change it if needed
2019-03-08 09:47:27 +03:00
db . Get ( obj . dbNodeKey , function ( err , nodes ) {
2019-05-21 04:03:14 +03:00
if ( ( nodes == null ) || ( nodes . length != 1 ) ) { return ; }
2019-03-08 09:47:27 +03:00
const device = nodes [ 0 ] ;
2018-08-22 01:08:15 +03:00
if ( device . agent ) {
var changes = [ ] , change = 0 ;
// Check if anything changes
if ( ( command . publicip ) && ( device . publicip != command . publicip ) ) { device . publicip = command . publicip ; change = 1 ; changes . push ( 'public ip' ) ; }
if ( ( command . iploc ) && ( device . iploc != command . iploc ) ) { device . iploc = command . iploc ; change = 1 ; changes . push ( 'ip location' ) ; }
// If there are changes, save and event
if ( change == 1 ) {
2019-04-10 20:41:10 +03:00
// Do some clean up if needed, these values should not be in the database.
if ( device . conn != null ) { delete device . conn ; }
if ( device . pwr != null ) { delete device . pwr ; }
if ( device . agct != null ) { delete device . agct ; }
if ( device . cict != null ) { delete device . cict ; }
// Save the device
2019-03-08 09:47:27 +03:00
db . Set ( device ) ;
2017-08-29 01:06:29 +03:00
2018-08-22 01:08:15 +03:00
// Event the node change
2018-09-25 03:47:03 +03:00
var event = { etype : 'node' , action : 'changenode' , nodeid : obj . dbNodeKey , domain : domain . id , msg : 'Changed device ' + device . name + ' from group ' + mesh . name + ': ' + changes . join ( ', ' ) } ;
2018-08-22 01:08:15 +03:00
if ( obj . agentInfo . capabilities & 0x20 ) { event . nolog = 1 ; } // If this is a temporary device, don't log changes
2019-03-08 09:47:27 +03:00
var device2 = common . Clone ( device ) ;
2018-08-22 01:08:15 +03:00
if ( device2 . intelamt && device2 . intelamt . pass ) { delete device2 . intelamt . pass ; } // Remove the Intel AMT password before eventing this.
event . node = device ;
2019-05-29 03:25:23 +03:00
if ( db . changeStream ) { event . noact = 1 ; } // If DB change stream is active, don't use this event to change the node. Another event will come.
2019-03-08 09:47:27 +03:00
parent . parent . DispatchEvent ( [ '*' , device . meshid ] , obj , event ) ;
2017-08-29 01:06:29 +03:00
}
2018-08-22 01:08:15 +03:00
}
2017-08-29 01:06:29 +03:00
} ) ;
}
2017-08-28 19:27:45 +03:00
// Update the mesh agent tab in the database
function ChangeAgentTag ( tag ) {
2019-04-10 01:51:05 +03:00
if ( obj . agentInfo . capabilities & 0x40 ) return ;
2017-09-15 21:45:06 +03:00
if ( tag . length == 0 ) { tag = null ; }
2017-08-28 19:27:45 +03:00
// Get the node and change it if needed
2019-03-08 09:47:27 +03:00
db . Get ( obj . dbNodeKey , function ( err , nodes ) {
2019-05-21 04:03:14 +03:00
if ( ( nodes == null ) || ( nodes . length != 1 ) ) return ;
2019-03-08 09:47:27 +03:00
const device = nodes [ 0 ] ;
2017-08-28 19:27:45 +03:00
if ( device . agent ) {
if ( device . agent . tag != tag ) {
2019-04-10 20:41:10 +03:00
// Do some clean up if needed, these values should not be in the database.
if ( device . conn != null ) { delete device . conn ; }
if ( device . pwr != null ) { delete device . pwr ; }
if ( device . agct != null ) { delete device . agct ; }
if ( device . cict != null ) { delete device . cict ; }
// Set the new tag
2017-08-28 19:27:45 +03:00
device . agent . tag = tag ;
2019-03-08 09:47:27 +03:00
db . Set ( device ) ;
2017-08-28 19:27:45 +03:00
// Event the node change
var event = { etype : 'node' , action : 'changenode' , nodeid : obj . dbNodeKey , domain : domain . id , nolog : 1 } ;
2019-03-08 09:47:27 +03:00
var device2 = common . Clone ( device ) ;
2017-08-28 19:27:45 +03:00
if ( device2 . intelamt && device2 . intelamt . pass ) delete device2 . intelamt . pass ; // Remove the Intel AMT password before eventing this.
event . node = device ;
2019-05-29 03:25:23 +03:00
if ( db . changeStream ) { event . noact = 1 ; } // If DB change stream is active, don't use this event to change the node. Another event will come.
2019-03-08 09:47:27 +03:00
parent . parent . DispatchEvent ( [ '*' , device . meshid ] , obj , event ) ;
2017-08-28 19:27:45 +03:00
}
}
} ) ;
}
2019-06-21 00:27:57 +03:00
// Generate a random Intel AMT password
function checkAmtPassword ( p ) { return ( p . length > 7 ) && ( /\d/ . test ( p ) ) && ( /[a-z]/ . test ( p ) ) && ( /[A-Z]/ . test ( p ) ) && ( /\W/ . test ( p ) ) ; }
function getRandomAmtPassword ( ) { var p ; do { p = Buffer . from ( parent . crypto . randomBytes ( 9 ) , 'binary' ) . toString ( 'base64' ) . split ( '/' ) . join ( '@' ) ; } while ( checkAmtPassword ( p ) == false ) ; return p ; }
2017-08-28 19:27:45 +03:00
return obj ;
2018-08-30 03:40:30 +03:00
} ;