| 1 | /* |
| 2 | Copyright 2018-2021 Intel Corporation |
| 3 | |
| 4 | Licensed under the Apache License, Version 2.0 (the "License"); |
| 5 | you may not use this file except in compliance with the License. |
| 6 | You may obtain a copy of the License at |
| 7 | |
| 8 | http://www.apache.org/licenses/LICENSE-2.0 |
| 9 | |
| 10 | Unless required by applicable law or agreed to in writing, software |
| 11 | distributed under the License is distributed on an "AS IS" BASIS, |
| 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| 13 | See the License for the specific language governing permissions and |
| 14 | limitations under the License. |
| 15 | */ |
| 16 | |
| 17 | /** |
| 18 | * @fileoverview Intel(r) AMT Management |
| 19 | * @author Ylian Saint-Hilaire |
| 20 | * @version v0.1.0 |
| 21 | */ |
| 22 | |
| 23 | /** |
| 24 | * Construct a AmtStackCreateService object, this ia the main Intel AMT communication stack. |
| 25 | * @constructor |
| 26 | */ |
| 27 | function AmtManager(agent, db, isdebug) { |
| 28 | var sendConsole = function (msg) { agent.SendCommand({ 'action': 'msg', 'type': 'console', 'value': msg }); } |
| 29 | var debug = function (msg) { if (isdebug) { sendConsole('amt-manager: ' + msg + '<br />'); } } |
| 30 | var amtMei = null, amtMeiState = 0; |
| 31 | var amtLms = null, amtLmsState = 0; |
| 32 | var amtGetVersionResult = null; |
| 33 | var obj = this; |
| 34 | var mestate; |
| 35 | var trustedHashes = null;; |
| 36 | |
| 37 | require('events').EventEmitter.call(obj, true) |
| 38 | .createEvent('stateChange_LMS') |
| 39 | .createEvent('portBinding_LMS'); |
| 40 | obj._lmsstate = 0; |
| 41 | obj._mapping = []; |
| 42 | |
| 43 | obj.on('newListener', function (name, callback) { |
| 44 | if (name == 'portBinding_LMS') { callback.call(this, this._mapping); } |
| 45 | }); |
| 46 | |
| 47 | Object.defineProperty(obj, 'lmsstate', |
| 48 | { |
| 49 | get: function () { return (this._lmsstate); }, |
| 50 | set: function (value) { if (this._lmsstate != value) { this._lmsstate = value; this.emit('stateChange_LMS', value); } } |
| 51 | }); |
| 52 | |
| 53 | obj.state = 0; |
| 54 | obj.onStateChange = null; |
| 55 | obj.setDebug = function (x) { isdebug = x; } |
| 56 | |
| 57 | // Try to load up the MEI module |
| 58 | var rebindToMeiRetrys = 0; |
| 59 | obj.reset = function () { |
| 60 | ++rebindToMeiRetrys; |
| 61 | obj.amtMei = null, amtMei = null, amtMeiState = 0, amtLms = null, amtLmsState = 0, obj.state = 0, obj.lmsstate = 0; |
| 62 | //debug('Binding to MEI'); |
| 63 | try { |
| 64 | var amtMeiLib = require('amt-mei'); |
| 65 | obj.amtMei = amtMei = new amtMeiLib(); |
| 66 | amtMei.on('error', function (e) { debug('MEI error'); amtMei = null; amtMeiState = -1; obj.state = -1; if (obj.onStateChange != null) { obj.onStateChange(amtMeiState); } }); |
| 67 | amtMei.getVersion(function (result) { |
| 68 | if (result == null) { |
| 69 | obj.state = amtMeiState = -1; |
| 70 | if (obj.onStateChange != null) { obj.onStateChange(amtMeiState); } |
| 71 | if (rebindToMeiRetrys < 10) { setTimeout(obj.reset, 10000); } |
| 72 | } else { |
| 73 | amtGetVersionResult = result; |
| 74 | obj.state = amtMeiState = 2; |
| 75 | rebindToMeiRetrys = 0; |
| 76 | if (obj.onStateChange != null) { obj.onStateChange(amtMeiState); } |
| 77 | //debug('MEI binded'); |
| 78 | obj.lmsreset(); |
| 79 | } |
| 80 | }); |
| 81 | } catch (ex) { debug("MEI exception: " + ex); amtMei = null; amtMeiState = -1; obj.state = -1; } |
| 82 | } |
| 83 | |
| 84 | // Get Intel MEI State in a flexible way |
| 85 | // Flags: 1 = Versions, 2 = OsAdmin, 4 = Hashes, 8 = Network |
| 86 | var getMeiStateCache = {}; // Some MEI calls will only be made once and cached here. |
| 87 | obj.getMeiState = function(flags, func) { |
| 88 | if ((amtMei == null) || (amtMeiState < 2)) { if (func != null) { func(null); } return; } |
| 89 | try { |
| 90 | var amtMeiTmpState = { 'core-ver': 1, OsHostname: require('os').hostname(), Flags: 0 }; // Flags: 1=EHBC, 2=CCM, 4=ACM |
| 91 | if (getMeiStateCache.MeiVersion != null) { amtMeiTmpState.MeiVersion = getMeiStateCache.MeiVersion; } else { amtMei.getProtocolVersion(function (result) { if (result != null) { getMeiStateCache.MeiVersion = amtMeiTmpState.MeiVersion = result; } }); } |
| 92 | if ((flags & 1) != 0) { |
| 93 | if (getMeiStateCache.Versions != null) { |
| 94 | amtMeiTmpState.Versions = getMeiStateCache.Versions; |
| 95 | } else { |
| 96 | amtMei.getVersion(function (result) { if (result) { getMeiStateCache.Versions = amtMeiTmpState.Versions = {}; for (var version in result.Versions) { amtMeiTmpState.Versions[result.Versions[version].Description] = result.Versions[version].Version; } } }); |
| 97 | } |
| 98 | } |
| 99 | amtMei.getProvisioningMode(function (result) { if (result) { amtMeiTmpState.ProvisioningMode = result.mode; } }); |
| 100 | amtMei.getProvisioningState(function (result) { if (result) { amtMeiTmpState.ProvisioningState = result.state; if (result.state != 2) { amtMei.stopConfiguration(function () { }); } } }); // 0: "Not Activated (Pre)", 1: "Not Activated (In)", 2: "Activated". Make sure to stop remote configuration if needed. |
| 101 | amtMei.getEHBCState(function (result) { if ((result != null) && (result.EHBC == true)) { amtMeiTmpState.Flags += 1; } }); |
| 102 | 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 |
| 103 | //amtMei.getMACAddresses(function (result) { if (result) { amtMeiTmpState.mac = result; } }); |
| 104 | if ((flags & 8) != 0) { |
| 105 | amtMei.getLanInterfaceSettings(0, function (result) { |
| 106 | if (result) { |
| 107 | amtMeiTmpState.net0 = result; |
| 108 | var fqdn = null, interfaces = require('os').networkInterfaces(); // Look for the DNS suffix for the Intel AMT Ethernet interface |
| 109 | for (var i in interfaces) { for (var j in interfaces[i]) { if ((interfaces[i][j].mac == result.mac) && (interfaces[i][j].fqdn != null) && (interfaces[i][j].fqdn != '')) { amtMeiTmpState.OsDnsSuffix = interfaces[i][j].fqdn; } } } |
| 110 | } |
| 111 | }); |
| 112 | amtMei.getLanInterfaceSettings(1, function (result) { if (result) { amtMeiTmpState.net1 = result; } }); |
| 113 | } |
| 114 | if (getMeiStateCache.UUID != null) { amtMeiTmpState.UUID = getMeiStateCache.UUID; } else { amtMei.getUuid(function (result) { if ((result != null) && (result.uuid != null)) { getMeiStateCache.UUID = amtMeiTmpState.UUID = result.uuid; } }); } |
| 115 | if ((flags & 2) != 0) { amtMei.getLocalSystemAccount(function (x) { if ((x != null) && x.user && x.pass) { amtMeiTmpState.OsAdmin = { user: x.user, pass: x.pass }; } }); } |
| 116 | amtMei.getDnsSuffix(function (result) { if (result != null) { amtMeiTmpState.DnsSuffix = result; } if ((flags & 4) == 0) { if (func != null) { func(amtMeiTmpState); } } }); |
| 117 | if ((flags & 4) != 0) { |
| 118 | amtMei.getHashHandles(function (handles) { |
| 119 | if ((handles != null) && (handles.length > 0)) { amtMeiTmpState.Hashes = []; } else { func(amtMeiTmpState); } |
| 120 | var exitOnCount = handles.length; |
| 121 | for (var i = 0; i < handles.length; ++i) { this.getCertHashEntry(handles[i], function (hashresult) { amtMeiTmpState.Hashes.push(hashresult); if (--exitOnCount == 0) { if (func != null) { func(amtMeiTmpState); } } }); } |
| 122 | }); |
| 123 | } |
| 124 | } catch (e) { if (func != null) { func(null); } return; } |
| 125 | } |
| 126 | |
| 127 | // Called on MicroLMS Intel AMT user notification |
| 128 | var handleAmtNotification = function (notifyMsg) { |
| 129 | if ((notifyMsg == null) || (notifyMsg.Body == null) || (notifyMsg.Body.MessageID == null) || (notifyMsg.Body.MessageArguments == null)) return null; |
| 130 | var amtMessage = notifyMsg.Body.MessageID, amtMessageArg = notifyMsg.Body.MessageArguments[0], notify = null; |
| 131 | |
| 132 | switch (amtMessage) { |
| 133 | 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 |
| 134 | case 'iAMT0052': { if (amtMessageArg == '1') { notify = "Intel® AMT KVM connected"; } else if (amtMessageArg == '2') { notify = "Intel® AMT KVM disconnected"; } break; } // KVM |
| 135 | default: { break; } |
| 136 | } |
| 137 | |
| 138 | // Sent to the entire group, no sessionid or userid specified. |
| 139 | if (notify != null) { agent.SendCommand({ 'action': 'msg', 'type': 'notify', 'value': notify, 'tag': 'general', 'amtMessage': amtMessage }); } |
| 140 | } |
| 141 | |
| 142 | // Launch LMS |
| 143 | obj.lmsreset = function () { |
| 144 | //debug('Binding to LMS'); |
| 145 | obj.lmsstate = 0; |
| 146 | try { |
| 147 | var lme_heci = require('amt-lme'); |
| 148 | obj.lmsstate = amtLmsState = 1; |
| 149 | amtLms = new lme_heci(); |
| 150 | amtLms.on('error', function (e) { amtLmsState = 0; obj.lmsstate = 0; amtLms = null; debug("LMS error: " + e); }); |
| 151 | amtLms.on('connect', function () { amtLmsState = 2; obj.lmsstate = 2; debug("LMS connected"); }); |
| 152 | amtLms.on('bind', function (map) { obj._mapping = map; obj.emit('portBinding_LMS', map); }); |
| 153 | amtLms.on('notify', function (data, options, code) { handleAmtNotification(data); }); |
| 154 | } catch (ex) { |
| 155 | require('MeshAgent').SendCommand({ action: 'msg', type: 'console', value: "ex: " + ex }); |
| 156 | amtLmsState = -1; obj.lmsstate = -1; amtLms = null; |
| 157 | } |
| 158 | } |
| 159 | |
| 160 | // Start host based ACM activation with TLS |
| 161 | obj.startConfigurationHBased = function startConfigurationHBased(certHash, hostVpn, dnsSuffixList, func) { |
| 162 | if ((amtMei == null) || (amtMeiState < 2)) { if (func != null) { func({ status: -100 }); } return; } |
| 163 | amtMei.startConfigurationHBased(certHash, hostVpn, dnsSuffixList, func); |
| 164 | } |
| 165 | |
| 166 | } |
| 167 | |
| 168 | module.exports = AmtManager; |