mirror of
https://github.com/Ylianst/MeshCentral.git
synced 2024-12-18 11:21:38 +03:00
781 lines
42 KiB
JavaScript
781 lines
42 KiB
JavaScript
/*
|
|
Copyright 2018-2020 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;;
|
|
|
|
require('events').EventEmitter.call(obj, true)
|
|
.createEvent('stateChange_LMS')
|
|
.createEvent('portBinding_LMS');
|
|
obj._lmsstate = 0;
|
|
obj._mapping = [];
|
|
|
|
obj.on('newListener', function (name, callback)
|
|
{
|
|
if(name == 'portBinding_LMS')
|
|
{
|
|
callback.call(this, this._mapping);
|
|
}
|
|
});
|
|
|
|
Object.defineProperty(obj, 'lmsstate',
|
|
{
|
|
get: function ()
|
|
{
|
|
return (this._lmsstate);
|
|
},
|
|
set: function (value)
|
|
{
|
|
if (this._lmsstate != value)
|
|
{
|
|
this._lmsstate = value;
|
|
this.emit('stateChange_LMS', value);
|
|
}
|
|
}
|
|
});
|
|
|
|
obj.state = 0;
|
|
obj.onStateChange = null;
|
|
obj.setDebug = function (x) { isdebug = x; }
|
|
|
|
// Set current Intel AMT activation policy
|
|
obj.setPolicy = function (policy, forceApply) {
|
|
if (forceApply || (JSON.stringify(amtpolicy) != JSON.stringify(policy))) {
|
|
amtpolicy = policy;
|
|
if (applyPolicyTimer == null) { applyPolicyTimer = setTimeout(obj.applyPolicy, 8000); }
|
|
}
|
|
}
|
|
|
|
// 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; if (obj.onStateChange != null) { 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® AMT Serial-over-LAN connected"; } else if (amtMessageArg == '49') { notify = "Intel® AMT Serial-over-LAN disconnected"; } break; } // SOL
|
|
case 'iAMT0052': { if (amtMessageArg == '1') { notify = "Intel® AMT KVM connected"; } else if (amtMessageArg == '2') { notify = "Intel® AMT KVM disconnected"; } break; } // KVM
|
|
default: { break; }
|
|
}
|
|
|
|
// Sent to the entire group, no sessionid or userid specified.
|
|
if (notify != null) { agent.SendCommand({ 'action': 'msg', 'type': 'notify', 'value': notify, 'tag': 'general', 'amtMessage': amtMessage }); }
|
|
}
|
|
|
|
// Launch LMS
|
|
obj.lmsreset = function () {
|
|
//debug('Binding to LMS');
|
|
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: " + e); setupMeiOsAdmin(1); });
|
|
amtLms.on('connect', function () { amtLmsState = 2; obj.lmsstate = 2; debug("LMS connected"); setupMeiOsAdmin(2); });
|
|
amtLms.on('bind', function (map)
|
|
{
|
|
obj._mapping = map;
|
|
obj.emit('portBinding_LMS', 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) {
|
|
if ((mestate.ProvisioningState != 0) || (amtpolicy == null) || (amtpolicy.match == null)) return; // Can't activate unless in "PRE" activation mode & policy is present.
|
|
var trustedFqdn = null;
|
|
if ((mestate.net0 == null) && (mestate.net0.enabled != 0)) return; // Can't activate unless wired interface is active
|
|
if (mestate.DNS) { trustedFqdn = mestate.DNS; } // If Intel AMT has a trusted DNS suffix set, use that one.
|
|
else {
|
|
// Look for the DNS suffix for the Intel AMT Ethernet interface
|
|
var interfaces = require('os').networkInterfaces();
|
|
for (var i in interfaces) {
|
|
for (var j in interfaces[i]) {
|
|
if ((interfaces[i][j].mac == mestate.net0.mac) && (interfaces[i][j].fqdn != null) && (interfaces[i][j].fqdn != '')) { trustedFqdn = interfaces[i][j].fqdn.toLowerCase(); }
|
|
}
|
|
}
|
|
}
|
|
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
|
|
|
|
// Fetch Intel AMT realm and activation nonce and get ready to ACM activation...
|
|
if (osamtstack != null) {
|
|
osamtstack.BatchEnum(null, ['*AMT_GeneralSettings', '*IPS_HostBasedSetupService'], activeToACM2, { fqdn: trustedFqdn, hash: hashMatch, uuid: mestate.UUID });
|
|
} else {
|
|
amtMei.getLocalSystemAccount(function (x) {
|
|
if ((x != null) && x.user && x.pass) {
|
|
var transport = require('amt-wsman-duk');
|
|
var wsman = require('amt-wsman');
|
|
var amt = require('amt');
|
|
oswsstack = new wsman(transport, '127.0.0.1', 16992, x.user, x.pass, false);
|
|
osamtstack = new amt(oswsstack);
|
|
osamtstack.BatchEnum(null, ['*AMT_GeneralSettings', '*IPS_HostBasedSetupService'], activeToACM2, { fqdn: trustedFqdn, hash: hashMatch, uuid: mestate.UUID });
|
|
}
|
|
});
|
|
}
|
|
}
|
|
|
|
function activeToACM2(stack, name, responses, status, tag) {
|
|
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, "uuid": tag.uuid });
|
|
}
|
|
|
|
// Called when the server responds with a ACM activation signature.
|
|
obj.setAcmResponse = function (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) {
|
|
var leaf = (acmdata.index == 0), root = (acmdata.index == (acmdata.certs.length - 1));
|
|
if ((acmdata.index < acmdata.certs.length) && (acmdata.certs[acmdata.index] != null)) {
|
|
osamtstack.IPS_HostBasedSetupService_AddNextCertInChain(acmdata.certs[acmdata.index], leaf, root, function (stack, name, responses, status) {
|
|
if (status !== 200) { debug('AddNextCertInChain status=' + status); return; }
|
|
else if (responses['Body']['ReturnValue'] !== 0) { debug('AddNextCertInChain error=' + responses['Body']['ReturnValue']); return; }
|
|
else { acmdata.index++; performAcmActivation(acmdata); }
|
|
});
|
|
} else {
|
|
osamtstack.IPS_HostBasedSetupService_AdminSetup(2, acmdata.password, acmdata.nonce, 2, acmdata.signature,
|
|
function (stack, name, responses, status) {
|
|
if ((status == 200) && (responses['Body']['ReturnValue'] == 0)) {
|
|
// ACM activation success, force an update to the server so it can get our new state.
|
|
if (obj.onStateChange != null) { obj.onStateChange(2); }
|
|
}
|
|
}
|
|
);
|
|
}
|
|
}
|
|
|
|
//
|
|
// Activate Intel AMT to CCM
|
|
//
|
|
|
|
obj.makePass = function(length) {
|
|
var buf = Buffer.alloc(length), text = "", possible = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
|
|
buf.randomFill(); // Fills buffer with secure random from OpenSSL.
|
|
for (var i = 0; i < length; i++) { text += possible.charAt(buf[i] % possible.length); }
|
|
return text;
|
|
}
|
|
|
|
obj.activeToCCM = function (adminpass) {
|
|
if ((adminpass == null) || (adminpass == '')) { adminpass = 'P@0s' + obj.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) && (agent.isControlChannelConnected)) {
|
|
// 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;
|