MeshCentral/agents/modules_meshcore/amt-manage.js
2019-06-19 18:11:00 -07:00

765 lines
42 KiB
JavaScript

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