meshcentral
Version:
Web based remote computer management server
803 lines (713 loc) • 210 kB
JavaScript
/**
* @description MeshCentral Intel AMT manager
* @author Ylian Saint-Hilaire
* @copyright Intel Corporation 2018-2022
* @license Apache-2.0
* @version v0.0.1
*/
/*jslint node: true */
/*jshint node: true */
/*jshint strict:false */
/*jshint -W097 */
/*jshint esversion: 6 */
'use strict';
module.exports.CreateAmtManager = function (parent) {
var obj = {};
obj.parent = parent;
obj.amtDevices = {}; // Nodeid --> [ dev ]
obj.activeLocalConnections = {}; // Host --> dev
obj.amtAdminAccounts = {}; // DomainId -> [ { user, pass } ]
obj.rootCertBase64 = obj.parent.certificates.root.cert.split('-----BEGIN CERTIFICATE-----').join('').split('-----END CERTIFICATE-----').join('').split('\r').join('').split('\n').join('')
obj.rootCertCN = obj.parent.certificateOperations.forge.pki.certificateFromPem(obj.parent.certificates.root.cert).subject.getField('CN').value;
// 802.1x authentication protocols
const netAuthStrings = ['eap-tls', 'eap-ttls/mschapv2', 'peapv0/eap-mschapv2', 'peapv1/eap-gtc', 'eap-fast/mschapv2', 'eap-fast/gtc', 'eap-md5', 'eap-psk', 'eap-sim', 'eap-aka', 'eap-fast/tls'];
// WSMAN stack
const CreateWsmanComm = require('./amt/amt-wsman-comm');
const WsmanStackCreateService = require('./amt/amt-wsman');
const AmtStackCreateService = require('./amt/amt');
const ConnectionTypeStrings = { 0: "CIRA", 1: "Relay", 2: "LMS", 3: "Local" };
// Check that each domain configuration is correct because we are not going to be checking this later.
if (parent.config == null) parent.config = {};
if (parent.config.domains == null) parent.config.domains = {};
for (var domainid in parent.config.domains) {
var domain = parent.config.domains[domainid];
if (typeof domain.amtmanager != 'object') { domain.amtmanager = {}; }
// Load administrator accounts
if (Array.isArray(domain.amtmanager.adminaccounts) == true) {
for (var i = 0; i < domain.amtmanager.adminaccounts.length; i++) {
var c = domain.amtmanager.adminaccounts[i], c2 = {};
if (typeof c.user == 'string') { c2.user = c.user; } else { c2.user = 'admin'; }
if (typeof c.pass == 'string') {
c2.pass = c.pass;
if (obj.amtAdminAccounts[domainid] == null) { obj.amtAdminAccounts[domainid] = []; }
obj.amtAdminAccounts[domainid].push(c2);
}
}
} else {
delete domain.amtmanager.adminaccounts;
}
// Check environment detection
if (Array.isArray(domain.amtmanager.environmentdetection) == true) {
var envDetect = [];
for (var i = 0; i < domain.amtmanager.environmentdetection.length; i++) {
var x = domain.amtmanager.environmentdetection[i].toLowerCase();
if ((typeof x == 'string') && (x != '') && (x.length < 64) && (envDetect.indexOf(x) == -1)) { envDetect.push(x); }
if (envDetect.length >= 4) break; // Maximum of 4 DNS suffix
}
if (envDetect.length > 0) { domain.amtmanager.environmentdetection = envDetect; } else { delete domain.amtmanager.environmentdetection; }
} else {
delete domain.amtmanager.environmentdetection;
}
// Check 802.1x wired profile if present
if ((domain.amtmanager['802.1x'] != null) && (typeof domain.amtmanager['802.1x'] == 'object')) {
if (domain.amtmanager['802.1x'].satellitecredentials != null) {
if (typeof domain.amtmanager['802.1x'].satellitecredentials != 'string') { delete domain.amtmanager['802.1x']; } else {
const userSplit = domain.amtmanager['802.1x'].satellitecredentials.split('/');
if (userSplit.length > 3) { delete domain.amtmanager['802.1x']; }
else if (userSplit.length == 2) { domain.amtmanager['802.1x'].satellitecredentials = 'user/' + domain.id + '/' + userSplit[1]; }
else if (userSplit.length == 1) { domain.amtmanager['802.1x'].satellitecredentials = 'user/' + domain.id + '/' + userSplit[0]; }
}
}
if (typeof domain.amtmanager['802.1x'].servercertificatename != 'string') {
delete domain.amtmanager['802.1x'].servercertificatenamecomparison;
} else {
const serverCertCompareStrings = ['', '', 'fullname', 'domainsuffix'];
if (typeof domain.amtmanager['802.1x'].servercertificatenamecomparison == 'string') {
domain.amtmanager['802.1x'].servercertificatenamecomparison = serverCertCompareStrings.indexOf(domain.amtmanager['802.1x'].servercertificatenamecomparison.toLowerCase());
if (domain.amtmanager['802.1x'].servercertificatenamecomparison == -1) { domain.amtmanager['802.1x'].servercertificatenamecomparison = 2; } // Default to full name compare
}
}
if (typeof domain.amtmanager['802.1x'].authenticationprotocol == 'string') {
domain.amtmanager['802.1x'].authenticationprotocol = netAuthStrings.indexOf(domain.amtmanager['802.1x'].authenticationprotocol.toLowerCase());
if (domain.amtmanager['802.1x'].authenticationprotocol == -1) { delete domain.amtmanager['802.1x']; }
}
}
// Check WIFI profiles
//var wifiAuthMethod = { 1: "Other", 2: "Open", 3: "Shared Key", 4: "WPA PSK", 5: "WPA 802.1x", 6: "WPA2 PSK", 7: "WPA2 802.1x", 32768: "WPA3 SAE IEEE 802.1x", 32769: "WPA3 OWE IEEE 802.1x" };
//var wifiEncMethod = { 1: "Other", 2: "WEP", 3: "TKIP", 4: "CCMP", 5: "None" }
if (Array.isArray(domain.amtmanager.wifiprofiles) == true) {
var goodWifiProfiles = [];
for (var i = 0; i < domain.amtmanager.wifiprofiles.length; i++) {
var wifiProfile = domain.amtmanager.wifiprofiles[i];
if ((typeof wifiProfile.ssid == 'string') && (wifiProfile.ssid != '')) {
if ((wifiProfile.name == null) || (wifiProfile.name == '')) { wifiProfile.name = wifiProfile.ssid; }
// Authentication
if (typeof wifiProfile.authentication == 'string') { wifiProfile.authentication = wifiProfile.authentication.toLowerCase(); }
if (wifiProfile.authentication == 'wpa-psk') { wifiProfile.authentication = 4; }
if (wifiProfile.authentication == 'wpa2-psk') { wifiProfile.authentication = 6; }
if (wifiProfile.authentication == 'wpa-8021x') { wifiProfile.authentication = 5; }
if (wifiProfile.authentication == 'wpa2-802.1x') { wifiProfile.authentication = 7; }
if (wifiProfile.authentication == 'wpa3-sae-802.1x') { wifiProfile.authentication = 32768; }
if (wifiProfile.authentication == 'wpa3-owe-802.1x') { wifiProfile.authentication = 32769; }
if (typeof wifiProfile.authentication != 'number') {
if (wifiProfile['802.1x']) { wifiProfile.authentication = 7; } // Default to WPA2-802.1x
else { wifiProfile.authentication = 6; } // Default to WPA2-PSK
}
// Encyption
if (typeof wifiProfile.encryption == 'string') { wifiProfile.encryption = wifiProfile.encryption.toLowerCase(); }
if ((wifiProfile.encryption == 'ccmp-aes') || (wifiProfile.encryption == 'ccmp')) { wifiProfile.encryption = 4; }
if ((wifiProfile.encryption == 'tkip-rc4') || (wifiProfile.encryption == 'tkip')) { wifiProfile.encryption = 3; }
if (typeof wifiProfile.encryption != 'number') { wifiProfile.encryption = 4; } // Default to CCMP-AES
// Type
wifiProfile.type = 3; // Infrastructure
// Check authentication
if ([4, 6].indexOf(wifiProfile.authentication) >= 0) {
// Password authentication
if ((typeof wifiProfile.password != 'string') || (wifiProfile.password.length < 8) || (wifiProfile.password.length > 63)) continue;
} else if ([5, 7, 32768, 32769].indexOf(wifiProfile.authentication) >= 0) {
// 802.1x authentication
if ((domain.amtmanager['802.1x'] == null) || (typeof domain.amtmanager['802.1x'] != 'object')) continue;
}
goodWifiProfiles.push(wifiProfile);
}
}
domain.amtmanager.wifiprofiles = goodWifiProfiles;
} else {
delete domain.amtmanager.wifiprofiles;
}
}
// Check if an Intel AMT device is being managed
function isAmtDeviceValid(dev) {
var devices = obj.amtDevices[dev.nodeid];
if (devices == null) return false;
return (devices.indexOf(dev) >= 0)
}
// Add an Intel AMT managed device
function addAmtDevice(dev) {
var devices = obj.amtDevices[dev.nodeid];
if (devices == null) { obj.amtDevices[dev.nodeid] = [dev]; return true; }
if (devices.indexOf(dev) >= 0) return false; // This device is already in the list
devices.push(dev); // Add the device to the list
return true;
}
// Remove an Intel AMT managed device
function removeAmtDevice(dev, tag) {
parent.debug('amt', dev.name, "Remove device", dev.nodeid, dev.connType, tag);
// Find the device in the list
var devices = obj.amtDevices[dev.nodeid];
if (devices == null) return false;
var i = devices.indexOf(dev);
if (i == -1) return false;
// Remove from task limiter if needed
if (dev.taskid != null) { obj.parent.taskLimiter.completed(dev.taskid); delete dev.taskLimiter; }
// Clean up this device
if (dev.amtstack != null) { dev.amtstack.CancelAllQueries(999); if (dev.amtstack != null) { delete dev.amtstack.dev; delete dev.amtstack; } }
if (dev.polltimer != null) { clearInterval(dev.polltimer); delete dev.polltimer; }
// Remove the device from the list
devices.splice(i, 1);
if (devices.length == 0) { delete obj.amtDevices[dev.nodeid]; } else { obj.amtDevices[dev.nodeid] = devices; }
// Notify connection closure if this is a LMS connection
if (dev.connType == 2) { dev.controlMsg({ action: 'close' }); }
return true;
}
// Remove all Intel AMT devices for a given nodeid
function removeDevice(nodeid) {
parent.debug('amt', "Remove nodeid", nodeid);
// Find the devices in the list
var devices = obj.amtDevices[nodeid];
if (devices == null) return false;
for (var i in devices) {
var dev = devices[i];
// Remove from task limiter if needed
if (dev.taskid != null) { obj.parent.taskLimiter.completed(dev.taskid); delete dev.taskLimiter; }
// Clean up this device
if (dev.amtstack != null) { dev.amtstack.wsman.comm.FailAllError = 999; delete dev.amtstack; } // Disconnect any active connections.
if (dev.polltimer != null) { clearInterval(dev.polltimer); delete dev.polltimer; }
// Notify connection closure if this is a LMS connection
if (dev.connType == 2) { dev.controlMsg({ action: 'close' }); }
}
// Remove all Intel AMT management sessions for this nodeid
delete obj.amtDevices[nodeid];
// If a 802.1x profile is active with MeshCentral Satellite, notify Satellite of the removal
if (domain.amtmanager['802.1x'] != null) {
var reqId = Buffer.from(parent.crypto.randomBytes(16), 'binary').toString('base64'); // Generate a crypto-secure request id.
parent.DispatchEvent([domain.amtmanager['802.1x'].satellitecredentials], obj, { action: 'satellite', subaction: '802.1x-Profile-Remove', satelliteFlags: 2, nodeid: nodeid, domain: nodeid.split('/')[1], nolog: 1, ver: dev.intelamt.ver });
}
return true;
}
// Start Intel AMT management
// connType: 0 = CIRA, 1 = CIRA-Relay, 2 = CIRA-LMS, 3 = LAN
obj.startAmtManagement = function (nodeid, connType, connection) {
//if (connType == 3) return; // DEBUG
var devices = obj.amtDevices[nodeid], dev = null;
if (devices != null) { for (var i in devices) { if ((devices[i].mpsConnection == connection) || (devices[i].host == connection)) { dev = devices[i]; } } }
if (dev != null) return false; // We are already managing this device on this connection
dev = { nodeid: nodeid, connType: connType, domainid: nodeid.split('/')[1] };
if (typeof connection == 'string') { dev.host = connection; }
if (typeof connection == 'object') { dev.mpsConnection = connection; }
dev.consoleMsg = function deviceConsoleMsg(msg) { parent.debug('amt', deviceConsoleMsg.dev.name, msg); if (typeof deviceConsoleMsg.conn == 'object') { deviceConsoleMsg.conn.ControlMsg({ action: 'console', msg: msg }); } }
dev.consoleMsg.conn = connection;
dev.consoleMsg.dev = dev;
dev.controlMsg = function deviceControlMsg(msg) { if (typeof deviceControlMsg.conn == 'object') { deviceControlMsg.conn.ControlMsg(msg); } }
dev.controlMsg.conn = connection;
parent.debug('amt', "Start Management", nodeid, connType);
addAmtDevice(dev);
// Start the device manager in the task limiter so not to flood the server. Low priority task
obj.parent.taskLimiter.launch(function (dev, taskid, taskLimiterQueue) {
if (isAmtDeviceValid(dev)) {
// Start managing this device
dev.taskid = taskid;
fetchIntelAmtInformation(dev);
} else {
// Device is not valid anymore, do nothing
obj.parent.taskLimiter.completed(taskid);
}
}, dev, 2);
}
// Stop Intel AMT management
obj.stopAmtManagement = function (nodeid, connType, connection) {
var devices = obj.amtDevices[nodeid], dev = null;
if (devices != null) { for (var i in devices) { if ((devices[i].mpsConnection == connection) || (devices[i].host == connection)) { dev = devices[i]; } } }
if (dev == null) return false; // We are not managing this device on this connection
parent.debug('amt', dev.name, "Stop Management", nodeid, connType);
return removeAmtDevice(dev, 1);
}
// Get a string status of the managed devices
obj.getStatusString = function () {
var r = '';
for (var nodeid in obj.amtDevices) {
var devices = obj.amtDevices[nodeid];
r += devices[0].nodeid + ', ' + devices[0].name + '\r\n';
for (var i in devices) {
var dev = devices[i];
var items = [];
if (dev.state == 1) { items.push('Connected'); } else { items.push('Trying'); }
items.push(ConnectionTypeStrings[dev.connType]);
if (dev.connType == 3) { items.push(dev.host); }
if (dev.polltimer != null) { items.push('Polling Power'); }
r += ' ' + items.join(', ') + '\r\n';
}
}
if (r == '') { r = "No managed Intel AMT devices"; }
return r;
}
// Receive a JSON control message from the MPS server
obj.mpsControlMessage = function (nodeid, conn, connType, jsondata) {
// Find the devices in the list
var dev = null;
var devices = obj.amtDevices[nodeid];
if (devices == null) return;
for (var i in devices) { if (devices[i].mpsConnection === conn) { dev = devices[i]; } }
if (dev == null) return;
// Process the message
switch (jsondata.action) {
case 'deactivate':
if ((dev.connType != 2) || (dev.deactivateCcmPending != 1)) break; // Only accept MEI state on CIRA-LMS connection
delete dev.deactivateCcmPending;
deactivateIntelAmtCCMEx(dev, jsondata.value);
break;
case 'meiState':
if (dev.acmactivate == 1) {
// Continue ACM activation
dev.consoleMsg("Got new Intel AMT MEI state. Holding 40 seconds prior to ACM activation...");
delete dev.acmactivate;
var continueAcmFunc = function continueAcm() { if (isAmtDeviceValid(continueAcm.dev)) { activateIntelAmtAcmEx0(continueAcm.dev); } }
continueAcmFunc.dev = dev;
setTimeout(continueAcmFunc, 40000);
} else {
if (dev.pendingUpdatedMeiState != 1) break;
delete dev.pendingUpdatedMeiState;
attemptInitialContact(dev);
}
break;
case 'startTlsHostConfig':
if (dev.acmTlsInfo == null) break;
if ((typeof jsondata.value != 'object') || (typeof jsondata.value.status != 'number')) {
removeAmtDevice(dev, 2); // Invalid startTlsHostConfig response
} else {
activateIntelAmtTlsAcmEx(dev, jsondata.value); // Start TLS activation.
}
break;
case 'stopConfiguration':
if (dev.acmactivate != 1) break;
if (jsondata.value == 3) { delete dev.acmactivate; activateIntelAmtAcmEx0(dev); } // Intel AMT was already not in in-provisioning state, keep going right away.
else if (jsondata.value == 0) {
dev.consoleMsg("Cleared in-provisioning state. Holding 30 seconds prior to getting Intel AMT MEI state...");
var askStateFunc = function askState() { if (isAmtDeviceValid(askState.dev)) { askState.dev.controlMsg({ action: 'mestate' }); } }
askStateFunc.dev = dev;
setTimeout(askStateFunc, 30000);
}
else { dev.consoleMsg("Unknown stopConfiguration() state of " + jsondata.value + ". Continuing with ACM activation..."); delete dev.acmactivate; activateIntelAmtAcmEx0(dev); }
break;
}
}
// Subscribe to server events
parent.AddEventDispatch(['*'], obj);
// Handle server events
// Make sure to only manage devices with connections to this server. In a multi-server setup, we don't want multiple managers talking to the same device.
obj.HandleEvent = function (source, event, ids, id) {
switch (event.action) {
case 'removenode': { // React to node being removed
if (event.noact == 1) return; // Take no action on these events. We are likely in peering mode and need to only act when the database signals the change in state.
removeDevice(event.nodeid);
break;
}
case 'wakedevices': { // React to node wakeup command, perform Intel AMT wake if possible
if (event.noact == 1) return; // Take no action on these events. We are likely in peering mode and need to only act when the database signals the change in state.
if (Array.isArray(event.nodeids)) { for (var i in event.nodeids) { performPowerAction(event.nodeids[i], 2); } }
break;
}
case 'oneclickrecovery': { // React to Intel AMT One Click Recovery command
if (event.noact == 1) return; // Take no action on these events. We are likely in peering mode and need to only act when the database signals the change in state.
if (Array.isArray(event.nodeids)) { for (var i in event.nodeids) { performOneClickRecoveryAction(event.nodeids[i], event.file); } }
break;
}
case 'amtpoweraction': {
if (event.noact == 1) return; // Take no action on these events. We are likely in peering mode and need to only act when the database signals the change in state.
if (Array.isArray(event.nodeids)) { for (var i in event.nodeids) { performPowerAction(event.nodeids[i], event.actiontype); } }
break;
}
case 'changenode': { // React to changes in a device
var devices = obj.amtDevices[event.nodeid], rescan = false;
if (devices != null) {
for (var i in devices) {
var dev = devices[i];
dev.name = event.node.name;
dev.icon = event.node.icon;
dev.rname = event.node.rname;
// If there are any changes, apply them.
if (event.node.intelamt != null) {
if (dev.intelamt == null) { dev.intelamt = {}; }
if ((typeof event.node.intelamt.version == 'string') && (event.node.intelamt.version != dev.intelamt.ver)) { dev.intelamt.ver = event.node.intelamt.version; }
if ((typeof event.node.intelamt.user == 'string') && (event.node.intelamt.user != dev.intelamt.user)) { dev.intelamt.user = event.node.intelamt.user; }
if ((typeof event.node.intelamt.pass == 'string') && (event.node.intelamt.pass != dev.intelamt.pass)) { dev.intelamt.pass = event.node.intelamt.pass; }
if ((typeof event.node.intelamt.mpspass == 'string') && (event.node.intelamt.mpspass != dev.intelamt.mpspass)) { dev.intelamt.mpspass = event.node.intelamt.mpspass; }
if ((typeof event.node.intelamt.host == 'string') && (event.node.intelamt.host != dev.intelamt.host)) { dev.intelamt.host = event.node.intelamt.host; }
if ((typeof event.node.intelamt.realm == 'string') && (event.node.intelamt.realm != dev.intelamt.realm)) { dev.intelamt.realm = event.node.intelamt.realm; }
if ((typeof event.node.intelamt.hash == 'string') && (event.node.intelamt.hash != dev.intelamt.hash)) { dev.intelamt.hash = event.node.intelamt.hash; }
if ((typeof event.node.intelamt.tls == 'number') && (event.node.intelamt.tls != dev.intelamt.tls)) { dev.intelamt.tls = event.node.intelamt.tls; }
if ((typeof event.node.intelamt.state == 'number') && (event.node.intelamt.state != dev.intelamt.state)) { dev.intelamt.state = event.node.intelamt.state; }
}
if ((dev.connType == 3) && (dev.host != event.node.host)) {
dev.host = event.node.host; // The host has changed, if we are connected to this device locally, we need to reset.
removeAmtDevice(dev, 3); // We are going to wait for the AMT scanned to find this device again.
rescan = true;
}
}
} else {
// If this event provides a hint that something changed with AMT and we are not managing this device, let's rescan the local network now.
if (event.amtchange == 1) { rescan = true; }
}
// If there is a significant change to the device AMT settings and this server manages local devices, perform a re-scan of the device now.
if (rescan && (parent.amtScanner != null)) { parent.amtScanner.performSpecificScan(event.node); }
break;
}
case 'meshchange': {
// TODO
// TODO: If a device changes to a device group that does not have a 802.1x policy, we may need to tell MeshCentral Satellite to remove the 802.1x profile.
break;
}
case 'satelliteResponse': {
if ((typeof event.nodeid != 'string') || (typeof event.reqid != 'string') || (event.satelliteFlags != 2)) return;
var devices = obj.amtDevices[event.nodeid], devFound = null;
if (devices != null) { for (var i in devices) { if (devices[i].netAuthSatReqId == event.reqid) { devFound = devices[i]; } } }
if (devFound == null) return; // Unable to find a device for this 802.1x profile
switch (event.subaction) {
case '802.1x-KeyPair-Request': {
// 802.1x request for public/private key pair be generated
attempt8021xKeyGeneration(devFound);
break;
}
case '802.1x-CSR-Request': {
// 802.1x request for a Certificate Signing Request
attempt8021xCRSRequest(devFound, event);
break;
}
case '802.1x-Profile-Completed': {
// The 802.1x profile request is done, set it in Intel AMT.
if (devFound.netAuthSatReqTimer != null) { clearTimeout(devFound.netAuthSatReqTimer); delete devFound.netAuthSatReqTimer; }
if ((event.response == null) || (typeof event.response != 'object')) {
// Unable to create a 802.1x profile
delete devFound.netAuthSatReqId;
if (isAmtDeviceValid(devFound) == false) return; // Device no longer exists, ignore this request.
delete devFound.netAuthSatReqData;
devFound.consoleMsg("MeshCentral Satellite could not create a 802.1x profile for this device.");
devTaskCompleted(devFound);
return;
}
if (typeof event.response.authProtocol != 'number') { delete devFound.netAuthSatReqId; break; }
// We got a new 802.1x profile
devFound.netAuthCredentials = event.response;
perform8021xRootCertCheck(devFound);
break;
}
}
break;
}
}
}
//
// Intel AMT Connection Setup
//
// Update information about a device
function fetchIntelAmtInformation(dev) {
parent.db.Get(dev.nodeid, function (err, nodes) {
if ((nodes == null) || (nodes.length != 1)) { removeAmtDevice(dev, 4); return; }
const node = nodes[0];
if ((node.intelamt == null) || (node.meshid == null)) { removeAmtDevice(dev, 5); return; }
const mesh = parent.webserver.meshes[node.meshid];
if (mesh == null) { removeAmtDevice(dev, 6); return; }
if (dev == null) { return; }
// Fetch Intel AMT setup policy
// mesh.amt.type: 0 = No Policy, 1 = Deactivate CCM, 2 = Manage in CCM, 3 = Manage in ACM
// mesh.amt.cirasetup: 0 = No Change, 1 = Remove CIRA, 2 = Setup CIRA
var amtPolicy = 0, ciraPolicy = 0, badPass = 0, password = null;
if (mesh.amt != null) {
if (mesh.amt.type) { amtPolicy = mesh.amt.type; }
if (mesh.amt.type == 4) {
// Fully automatic policy
ciraPolicy = 2; // CIRA will be setup
badPass = 1; // Automatically re-active CCM
password = null; // Randomize the password.
} else {
if (mesh.amt.cirasetup) { ciraPolicy = mesh.amt.cirasetup; }
if (mesh.amt.badpass) { badPass = mesh.amt.badpass; }
if ((typeof mesh.amt.password == 'string') && (mesh.amt.password != '')) { password = mesh.amt.password; }
}
}
if (amtPolicy == 0) { ciraPolicy = 0; } // If no policy, don't change CIRA state.
if (amtPolicy == 1) { ciraPolicy = 1; } // If deactivation policy, clear CIRA.
dev.policy = { amtPolicy: amtPolicy, ciraPolicy: ciraPolicy, badPass: badPass, password: password };
// Setup the monitored device
dev.name = node.name;
dev.rname = node.rname;
dev.icon = node.icon;
dev.meshid = node.meshid;
dev.intelamt = node.intelamt;
// Check if the status of Intel AMT sent by the agents matched what we have in the database
if ((dev.connType == 2) && (dev.mpsConnection != null) && (dev.mpsConnection.tag != null) && (dev.mpsConnection.tag.meiState != null)) {
dev.aquired = {};
if ((typeof dev.mpsConnection.tag.meiState.OsHostname == 'string') && (typeof dev.mpsConnection.tag.meiState.OsDnsSuffix == 'string')) {
dev.host = dev.aquired.host = dev.mpsConnection.tag.meiState.OsHostname + '.' + dev.mpsConnection.tag.meiState.OsDnsSuffix;
}
if (typeof dev.mpsConnection.tag.meiState['ProvisioningState'] == 'number') {
dev.intelamt.state = dev.aquired.state = dev.mpsConnection.tag.meiState['ProvisioningState'];
}
if ((typeof dev.mpsConnection.tag.meiState['Versions'] == 'object') && (typeof dev.mpsConnection.tag.meiState['Versions']['AMT'] == 'string')) {
dev.intelamt.ver = dev.aquired.version = dev.mpsConnection.tag.meiState['Versions']['AMT'];
}
if (typeof dev.mpsConnection.tag.meiState['Flags'] == 'number') {
const flags = dev.intelamt.flags = dev.mpsConnection.tag.meiState['Flags'];
if (flags & 2) { dev.aquired.controlMode = 1; } // CCM
if (flags & 4) { dev.aquired.controlMode = 2; } // ACM
}
UpdateDevice(dev);
}
// If there is no Intel AMT policy for this device, stop here.
//if (amtPolicy == 0) { dev.consoleMsg("Done."); removeAmtDevice(dev, 7); return; }
// Initiate the communication to Intel AMT
dev.consoleMsg("Checking Intel AMT state...");
attemptInitialContact(dev);
});
}
// Attempt to perform initial contact with Intel AMT
function attemptInitialContact(dev) {
// If there is a WSMAN stack setup, clean it up now.
if (dev.amtstack != null) {
dev.amtstack.CancelAllQueries(999);
delete dev.amtstack.dev;
delete dev.amtstack;
}
delete dev.amtstack;
parent.debug('amt', dev.name, "Attempt Initial Contact", ["CIRA", "CIRA-Relay", "CIRA-LMS", "Local"][dev.connType]);
// Check Intel AMT policy when CIRA-LMS connection is in use.
if ((dev.connType == 2) && (dev.mpsConnection != null) && (dev.mpsConnection.tag != null) && (dev.mpsConnection.tag.meiState != null)) {
// Intel AMT activation policy
if ((dev.policy.amtPolicy > 1) && (dev.mpsConnection.tag.meiState.ProvisioningState !== 2)) {
// This Intel AMT device is not activated, we need to work on activating it.
activateIntelAmt(dev);
return;
}
// Check if we have an ACM activation policy, but the device is in CCM
if (((dev.policy.amtPolicy == 3) || (dev.policy.amtPolicy == 4)) && (dev.mpsConnection.tag.meiState.ProvisioningState == 2) && ((dev.mpsConnection.tag.meiState.Flags & 2) != 0)) {
// This device in is CCM, check if we can upgrade to ACM
if (activateIntelAmt(dev) == false) return; // If this return true, the platform is in CCM and can't go to ACM, keep going with management.
}
// Intel AMT CCM deactivation policy
if (dev.policy.amtPolicy == 1) {
if ((dev.mpsConnection.tag.meiState.ProvisioningState == 2) && ((dev.mpsConnection.tag.meiState.Flags & 2) != 0)) {
// Deactivate CCM.
deactivateIntelAmtCCM(dev);
return;
}
}
}
// See what username/password we need to try
// We create an efficient strategy for trying different Intel AMT passwords.
if (dev.acctry == null) {
dev.acctry = [];
// Add Intel AMT username and password provided by MeshCMD if available
if ((dev.mpsConnection != null) && (dev.mpsConnection.tag != null) && (dev.mpsConnection.tag.meiState != null) && (typeof dev.mpsConnection.tag.meiState.amtuser == 'string') && (typeof dev.mpsConnection.tag.meiState.amtpass == 'string') && (dev.mpsConnection.tag.meiState.amtuser != '') && (dev.mpsConnection.tag.meiState.amtpass != '')) {
dev.acctry.push([dev.mpsConnection.tag.meiState.amtuser, dev.mpsConnection.tag.meiState.amtpass]);
}
// Add the know Intel AMT password for this device if available
if ((typeof dev.intelamt.user == 'string') && (typeof dev.intelamt.pass == 'string') && (dev.intelamt.user != '') && (dev.intelamt.pass != '')) { dev.acctry.push([dev.intelamt.user, dev.intelamt.pass]); }
// Add the policy password as an alternative
if ((typeof dev.policy.password == 'string') && (dev.policy.password != '')) { dev.acctry.push(['admin', dev.policy.password]); }
// Add any configured admin account as alternatives
if (obj.amtAdminAccounts[dev.domainid] != null) { for (var i in obj.amtAdminAccounts[dev.domainid]) { dev.acctry.push([obj.amtAdminAccounts[dev.domainid][i].user, obj.amtAdminAccounts[dev.domainid][i].pass]); } }
// Add any previous passwords for the device UUID as alternative
if ((parent.amtPasswords != null) && (dev.mpsConnection != null) && (dev.mpsConnection.tag != null) && (dev.mpsConnection.tag.meiState != null) && (dev.mpsConnection.tag.meiState.UUID != null) && (parent.amtPasswords[dev.mpsConnection.tag.meiState.UUID] != null)) {
for (var i in parent.amtPasswords[dev.mpsConnection.tag.meiState.UUID]) {
dev.acctry.push(['admin', parent.amtPasswords[dev.mpsConnection.tag.meiState.UUID][i]]);
}
}
// Remove any duplicates user/passwords
var acctry2 = [];
for (var i = 0; i < dev.acctry.length; i++) {
var found = false;
for (var j = 0; j < acctry2.length; j++) { if ((dev.acctry[i][0] == acctry2[j][0]) && (dev.acctry[i][1] == acctry2[j][1])) { found = true; } }
if (found == false) { acctry2.push(dev.acctry[i]); }
}
dev.acctry = acctry2;
// If we have passwords to try, try the first one now.
if (dev.acctry.length == 0) {
dev.consoleMsg("No admin login passwords to try, stopping now.");
removeAmtDevice(dev, 8);
return;
}
}
if ((dev.acctry == null) || (dev.acctry.length == 0)) { removeAmtDevice(dev, 9); return; } // No Intel AMT credentials to try
var user = dev.acctry[0][0], pass = dev.acctry[0][1]; // Try the first user/pass in the list
switch (dev.connType) {
case 0: // CIRA
// Handle the case where the Intel AMT CIRA is connected (connType 0)
// In this connection type, we look at the port bindings to see if we need to do TLS or not.
// Check to see if CIRA is connected on this server.
var ciraconn = dev.mpsConnection;
if ((ciraconn == null) || (ciraconn.tag == null) || (ciraconn.tag.boundPorts == null)) { removeAmtDevice(dev, 9); return; } // CIRA connection is not on this server, no need to deal with this device anymore.
// See if we need to perform TLS or not. We prefer not to do TLS within CIRA.
var dotls = -1;
if (ciraconn.tag.boundPorts.indexOf('16992')) { dotls = 0; }
else if (ciraconn.tag.boundPorts.indexOf('16993')) { dotls = 1; }
if (dotls == -1) { removeAmtDevice(dev, 10); return; } // The Intel AMT ports are not open, not a device we can deal with.
// Connect now
parent.debug('amt', dev.name, 'CIRA-Connect', (dotls == 1) ? "TLS" : "NoTLS", user, pass);
var comm;
if (dotls == 1) {
comm = CreateWsmanComm(dev.nodeid, 16993, user, pass, 1, null, ciraconn); // Perform TLS
comm.xtlsFingerprint = 0; // Perform no certificate checking
} else {
comm = CreateWsmanComm(dev.nodeid, 16992, user, pass, 0, null, ciraconn); // No TLS
}
var wsstack = WsmanStackCreateService(comm);
dev.amtstack = AmtStackCreateService(wsstack);
dev.amtstack.dev = dev;
dev.amtstack.BatchEnum(null, ['*AMT_GeneralSettings', '*IPS_HostBasedSetupService'], attemptLocalConnectResponse);
break;
case 1: // CIRA-Relay
case 2: // CIRA-LMS
// Handle the case where the Intel AMT relay or LMS is connected (connType 1 or 2)
// Check to see if CIRA is connected on this server.
var ciraconn = dev.mpsConnection;
if ((ciraconn == null) || (ciraconn.tag == null) || (ciraconn.tag.boundPorts == null)) { removeAmtDevice(dev, 11); return; } // Relay connection not valid
// Connect now
var comm;
if ((dev.tlsfail !== true) && (parent.config.domains[dev.domainid].amtmanager.tlsconnections !== false) && (dev.intelamt.tls == 1)) {
parent.debug('amt', dev.name, (dev.connType == 1) ? 'Relay-Connect' : 'LMS-Connect', "TLS", user);
comm = CreateWsmanComm(dev.nodeid, 16993, user, pass, 1, null, ciraconn); // Perform TLS
comm.xtlsFingerprint = 0; // Perform no certificate checking
} else {
parent.debug('amt', dev.name, (dev.connType == 1) ? 'Relay-Connect' : 'LMS-Connect', "NoTLS", user);
comm = CreateWsmanComm(dev.nodeid, 16992, user, pass, 0, null, ciraconn); // No TLS
}
var wsstack = WsmanStackCreateService(comm);
dev.amtstack = AmtStackCreateService(wsstack);
dev.amtstack.dev = dev;
dev.amtstack.BatchEnum(null, ['*AMT_GeneralSettings', '*IPS_HostBasedSetupService'], attemptLocalConnectResponse);
break;
case 3: // Local LAN
// Check if Intel AMT is activated. If not, stop here.
if ((dev.intelamt == null) || ((dev.intelamt.state != null) && (dev.intelamt.state != 2))) { removeAmtDevice(dev, 12); return; }
// Handle the case where the Intel AMT local scanner found the device (connType 3)
parent.debug('amt', dev.name, "Attempt Initial Local Contact", dev.connType, dev.host);
if (typeof dev.host != 'string') { removeAmtDevice(dev, 13); return; } // Local connection not valid
// Since we don't allow two or more connections to the same host, check if a pending connection is active.
if (obj.activeLocalConnections[dev.host] != null) {
// Active connection, hold and try later.
var tryAgainFunc = function tryAgainFunc() { if (obj.amtDevices[tryAgainFunc.dev.nodeid] != null) { attemptInitialContact(tryAgainFunc.dev); } }
tryAgainFunc.dev = dev;
setTimeout(tryAgainFunc, 5000);
} else {
// No active connections
// Connect now
var comm;
if ((dev.tlsfail !== true) && (parent.config.domains[dev.domainid].amtmanager.tlsconnections !== false) && (dev.intelamt.tls == 1)) {
parent.debug('amt', dev.name, 'Direct-Connect', "TLS", dev.host, user);
comm = CreateWsmanComm(dev.host, 16993, user, pass, 1); // Always try with TLS first
comm.xtlsFingerprint = 0; // Perform no certificate checking
} else {
parent.debug('amt', dev.name, 'Direct-Connect', "NoTLS", dev.host, user);
comm = CreateWsmanComm(dev.host, 16992, user, pass, 0); // Try without TLS
}
var wsstack = WsmanStackCreateService(comm);
dev.amtstack = AmtStackCreateService(wsstack);
dev.amtstack.dev = dev;
obj.activeLocalConnections[dev.host] = dev;
dev.amtstack.BatchEnum(null, ['*AMT_GeneralSettings', '*IPS_HostBasedSetupService'], attemptLocalConnectResponse);
}
break;
}
}
function attemptLocalConnectResponse(stack, name, responses, status) {
const dev = stack.dev;
parent.debug('amt', dev.name, "Initial Contact Response", status);
// If this is a local connection device, release active connection to this host.
if (dev.connType == 3) { delete obj.activeLocalConnections[dev.host]; }
// Check if the device still exists
if (isAmtDeviceValid(dev) == false) return; // Device no longer exists, ignore this request.
// Check the response
if ((status == 200) && (responses['AMT_GeneralSettings'] != null) && (responses['IPS_HostBasedSetupService'] != null) && (responses['IPS_HostBasedSetupService'].response != null) && (responses['IPS_HostBasedSetupService'].response != null) && (stack.wsman.comm.digestRealm == responses['AMT_GeneralSettings'].response.DigestRealm)) {
// Everything looks good
dev.consoleMsg(stack.wsman.comm.xtls ? "Intel AMT connected with TLS." : "Intel AMT connected.");
dev.state = 1;
if (dev.aquired == null) { dev.aquired = {}; }
dev.aquired.controlMode = responses['IPS_HostBasedSetupService'].response.CurrentControlMode; // 1 = CCM, 2 = ACM
if (typeof stack.wsman.comm.amtVersion == 'string') { // Set the Intel AMT version using the HTTP header if present
var verSplit = stack.wsman.comm.amtVersion.split('.');
if (verSplit.length >= 2) {
dev.aquired.version = verSplit[0] + '.' + verSplit[1];
dev.aquired.majorver = parseInt(verSplit[0]);
dev.aquired.minorver = parseInt(verSplit[1]);
if (verSplit.length >= 3) {
dev.aquired.version = verSplit[0] + '.' + verSplit[1] + '.' + verSplit[2];
dev.aquired.maintenancever = parseInt(verSplit[2]);
}
}
}
dev.aquired.realm = stack.wsman.comm.digestRealm;
dev.aquired.user = dev.intelamt.user = stack.wsman.comm.user;
dev.aquired.pass = dev.intelamt.pass = stack.wsman.comm.pass;
dev.aquired.lastContact = Date.now();
dev.aquired.warn = 0; // Clear all warnings (TODO: Check Realm and TLS cert pinning)
if ((dev.connType == 1) || (dev.connType == 3)) { dev.aquired.tls = stack.wsman.comm.xtls; } // Only set the TLS state if in relay or local mode. When using CIRA, this is auto-detected.
if (stack.wsman.comm.xtls == 1) { dev.aquired.hash = stack.wsman.comm.xtlsCertificate.fingerprint.split(':').join('').toLowerCase(); } else { delete dev.aquired.hash; }
UpdateDevice(dev);
// If this is the new first user/pass for the device UUID, update the activation log now.
if ((parent.amtPasswords != null) && (dev.mpsConnection != null) && (dev.mpsConnection.tag != null) && (dev.mpsConnection.tag.meiState != null) && (dev.mpsConnection.tag.meiState.UUID != null) && (parent.amtPasswords[dev.mpsConnection.tag.meiState.UUID] != null) && (parent.amtPasswords[dev.mpsConnection.tag.meiState.UUID][0] != dev.aquired.pass)) {
parent.certificateOperations.logAmtActivation(parent.config.domains[dev.domainid], { time: new Date(), action: 'amtpassword', domain: dev.domainid, amtUuid: dev.mpsConnection.tag.meiState.UUID, amtRealm: dev.aquired.realm, user: dev.aquired.user, password: dev.aquired.pass, computerName: dev.name });
}
// Perform Intel AMT clock sync
attemptSyncClock(dev, function (dev) {
// Check Intel AMT TLS state
attemptTlsSync(dev, function (dev) {
// If we need to switch to TLS, do it now.
if (dev.switchToTls == 1) { delete dev.switchToTls; attemptInitialContact(dev); return; }
// Check Intel AMT 802.1x wired state and Intel AMT WIFI state (must be done both at once).
attemptWifiSync(dev, function (dev) {
// Check Intel AMT root certificate state
attemptRootCertSync(dev, function (dev) {
// Check Intel AMT CIRA settings
attemptCiraSync(dev, function (dev) {
// Check Intel AMT settings
attemptSettingsSync(dev, function (dev) {
// Clean unused certificates
attemptCleanCertsSync(dev, function (dev) {
// See if we need to get hardware inventory
attemptFetchHardwareInventory(dev, function (dev) {
dev.consoleMsg('Done.');
// Remove from task limiter if needed
if (dev.taskid != null) { obj.parent.taskLimiter.completed(dev.taskid); delete dev.taskLimiter; }
if (dev.connType != 2) {
// Start power polling if not connected to LMS
var ppfunc = function powerPoleFunction() { fetchPowerState(powerPoleFunction.dev); }
ppfunc.dev = dev;
if(dev.polltimer){ clearInterval(dev.polltimer); delete dev.polltimer; }
dev.polltimer = new setInterval(ppfunc, 290000); // Poll for power state every 4 minutes 50 seconds.
fetchPowerState(dev);
} else {
// For LMS connections, close now.
dev.controlMsg({ action: 'close' });
}
});
});
});
});
});
});
});
});
} else {
// We got a bad response
if ((dev.conntype != 0) && (dev.tlsfail !== true) && (status == 408)) { // If not using CIRA and we get a 408 error while using TLS, try non-TLS.
// TLS error on a local connection, try again without TLS
dev.tlsfail = true; attemptInitialContact(dev); return;
} else if (status == 401) {
// Authentication error, see if we can use alternative credentials
if (dev.acctry != null) {
// Remove the first password from the trial list since it did not work.
if (dev.acctry.length > 0) { dev.acctry.shift(); }
// We have another password to try, hold 20 second and try the next user/password.
if (dev.acctry.length > 0) {
dev.consoleMsg("Holding 20 seconds and trying again with different credentials...");
setTimeout(function () { if (isAmtDeviceValid(dev)) { attemptInitialContact(dev); } }, 20000); return;
}
}
// If this device is in CCM mode and we have a bad password reset policy, do it now.
if ((dev.connType == 2) && (dev.policy.badPass == 1) && (dev.mpsConnection != null) && (dev.mpsConnection.tag != null) && (dev.mpsConnection.tag.meiState != null) && (dev.mpsConnection.tag.meiState.Flags != null) && ((dev.mpsConnection.tag.meiState.Flags & 2) != 0)) {
deactivateIntelAmtCCM(dev);
return;
}
// We are unable to authenticate to this device
dev.consoleMsg("Unable to connect.");
// Set an