| 1 | /** |
| 2 | * @description MeshCentral Intel AMT manager |
| 3 | * @author Ylian Saint-Hilaire |
| 4 | * @copyright Intel Corporation 2018-2022 |
| 5 | * @license Apache-2.0 |
| 6 | * @version v0.0.1 |
| 7 | */ |
| 8 | |
| 9 | /*jslint node: true */ |
| 10 | /*jshint node: true */ |
| 11 | /*jshint strict:false */ |
| 12 | /*jshint -W097 */ |
| 13 | /*jshint esversion: 6 */ |
| 14 | 'use strict'; |
| 15 | |
| 16 | module.exports.CreateAmtManager = function (parent) { |
| 17 | var obj = {}; |
| 18 | obj.parent = parent; |
| 19 | obj.amtDevices = {}; // Nodeid --> [ dev ] |
| 20 | obj.activeLocalConnections = {}; // Host --> dev |
| 21 | obj.amtAdminAccounts = {}; // DomainId -> [ { user, pass } ] |
| 22 | obj.rootCertBase64 = obj.parent.certificates.root.cert.split('-----BEGIN CERTIFICATE-----').join('').split('-----END CERTIFICATE-----').join('').split('\r').join('').split('\n').join('') |
| 23 | obj.rootCertCN = obj.parent.certificateOperations.forge.pki.certificateFromPem(obj.parent.certificates.root.cert).subject.getField('CN').value; |
| 24 | |
| 25 | // 802.1x authentication protocols |
| 26 | 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']; |
| 27 | |
| 28 | // WSMAN stack |
| 29 | const CreateWsmanComm = require('./amt/amt-wsman-comm'); |
| 30 | const WsmanStackCreateService = require('./amt/amt-wsman'); |
| 31 | const AmtStackCreateService = require('./amt/amt'); |
| 32 | const ConnectionTypeStrings = { 0: "CIRA", 1: "Relay", 2: "LMS", 3: "Local" }; |
| 33 | |
| 34 | // Check that each domain configuration is correct because we are not going to be checking this later. |
| 35 | if (parent.config == null) parent.config = {}; |
| 36 | if (parent.config.domains == null) parent.config.domains = {}; |
| 37 | for (var domainid in parent.config.domains) { |
| 38 | var domain = parent.config.domains[domainid]; |
| 39 | if (typeof domain.amtmanager != 'object') { domain.amtmanager = {}; } |
| 40 | |
| 41 | // Load administrator accounts |
| 42 | if (Array.isArray(domain.amtmanager.adminaccounts) == true) { |
| 43 | for (var i = 0; i < domain.amtmanager.adminaccounts.length; i++) { |
| 44 | var c = domain.amtmanager.adminaccounts[i], c2 = {}; |
| 45 | if (typeof c.user == 'string') { c2.user = c.user; } else { c2.user = 'admin'; } |
| 46 | if (typeof c.pass == 'string') { |
| 47 | c2.pass = c.pass; |
| 48 | if (obj.amtAdminAccounts[domainid] == null) { obj.amtAdminAccounts[domainid] = []; } |
| 49 | obj.amtAdminAccounts[domainid].push(c2); |
| 50 | } |
| 51 | } |
| 52 | } else { |
| 53 | delete domain.amtmanager.adminaccounts; |
| 54 | } |
| 55 | |
| 56 | // Check environment detection |
| 57 | if (Array.isArray(domain.amtmanager.environmentdetection) == true) { |
| 58 | var envDetect = []; |
| 59 | for (var i = 0; i < domain.amtmanager.environmentdetection.length; i++) { |
| 60 | var x = domain.amtmanager.environmentdetection[i].toLowerCase(); |
| 61 | if ((typeof x == 'string') && (x != '') && (x.length < 64) && (envDetect.indexOf(x) == -1)) { envDetect.push(x); } |
| 62 | if (envDetect.length >= 4) break; // Maximum of 4 DNS suffix |
| 63 | } |
| 64 | if (envDetect.length > 0) { domain.amtmanager.environmentdetection = envDetect; } else { delete domain.amtmanager.environmentdetection; } |
| 65 | } else { |
| 66 | delete domain.amtmanager.environmentdetection; |
| 67 | } |
| 68 | |
| 69 | // Check 802.1x wired profile if present |
| 70 | if ((domain.amtmanager['802.1x'] != null) && (typeof domain.amtmanager['802.1x'] == 'object')) { |
| 71 | if (domain.amtmanager['802.1x'].satellitecredentials != null) { |
| 72 | if (typeof domain.amtmanager['802.1x'].satellitecredentials != 'string') { delete domain.amtmanager['802.1x']; } else { |
| 73 | const userSplit = domain.amtmanager['802.1x'].satellitecredentials.split('/'); |
| 74 | if (userSplit.length > 3) { delete domain.amtmanager['802.1x']; } |
| 75 | else if (userSplit.length == 2) { domain.amtmanager['802.1x'].satellitecredentials = 'user/' + domain.id + '/' + userSplit[1]; } |
| 76 | else if (userSplit.length == 1) { domain.amtmanager['802.1x'].satellitecredentials = 'user/' + domain.id + '/' + userSplit[0]; } |
| 77 | } |
| 78 | } |
| 79 | |
| 80 | if (typeof domain.amtmanager['802.1x'].servercertificatename != 'string') { |
| 81 | delete domain.amtmanager['802.1x'].servercertificatenamecomparison; |
| 82 | } else { |
| 83 | const serverCertCompareStrings = ['', '', 'fullname', 'domainsuffix']; |
| 84 | if (typeof domain.amtmanager['802.1x'].servercertificatenamecomparison == 'string') { |
| 85 | domain.amtmanager['802.1x'].servercertificatenamecomparison = serverCertCompareStrings.indexOf(domain.amtmanager['802.1x'].servercertificatenamecomparison.toLowerCase()); |
| 86 | if (domain.amtmanager['802.1x'].servercertificatenamecomparison == -1) { domain.amtmanager['802.1x'].servercertificatenamecomparison = 2; } // Default to full name compare |
| 87 | } |
| 88 | } |
| 89 | |
| 90 | if (typeof domain.amtmanager['802.1x'].authenticationprotocol == 'string') { |
| 91 | domain.amtmanager['802.1x'].authenticationprotocol = netAuthStrings.indexOf(domain.amtmanager['802.1x'].authenticationprotocol.toLowerCase()); |
| 92 | if (domain.amtmanager['802.1x'].authenticationprotocol == -1) { delete domain.amtmanager['802.1x']; } |
| 93 | } |
| 94 | } |
| 95 | |
| 96 | // Check WIFI profiles |
| 97 | //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" }; |
| 98 | //var wifiEncMethod = { 1: "Other", 2: "WEP", 3: "TKIP", 4: "CCMP", 5: "None" } |
| 99 | if (Array.isArray(domain.amtmanager.wifiprofiles) == true) { |
| 100 | var goodWifiProfiles = []; |
| 101 | for (var i = 0; i < domain.amtmanager.wifiprofiles.length; i++) { |
| 102 | var wifiProfile = domain.amtmanager.wifiprofiles[i]; |
| 103 | if ((typeof wifiProfile.ssid == 'string') && (wifiProfile.ssid != '')) { |
| 104 | if ((wifiProfile.name == null) || (wifiProfile.name == '')) { wifiProfile.name = wifiProfile.ssid; } |
| 105 | |
| 106 | // Authentication |
| 107 | if (typeof wifiProfile.authentication == 'string') { wifiProfile.authentication = wifiProfile.authentication.toLowerCase(); } |
| 108 | if (wifiProfile.authentication == 'wpa-psk') { wifiProfile.authentication = 4; } |
| 109 | if (wifiProfile.authentication == 'wpa2-psk') { wifiProfile.authentication = 6; } |
| 110 | if (wifiProfile.authentication == 'wpa-8021x') { wifiProfile.authentication = 5; } |
| 111 | if (wifiProfile.authentication == 'wpa2-802.1x') { wifiProfile.authentication = 7; } |
| 112 | if (wifiProfile.authentication == 'wpa3-sae-802.1x') { wifiProfile.authentication = 32768; } |
| 113 | if (wifiProfile.authentication == 'wpa3-owe-802.1x') { wifiProfile.authentication = 32769; } |
| 114 | if (typeof wifiProfile.authentication != 'number') { |
| 115 | if (wifiProfile['802.1x']) { wifiProfile.authentication = 7; } // Default to WPA2-802.1x |
| 116 | else { wifiProfile.authentication = 6; } // Default to WPA2-PSK |
| 117 | } |
| 118 | |
| 119 | // Encyption |
| 120 | if (typeof wifiProfile.encryption == 'string') { wifiProfile.encryption = wifiProfile.encryption.toLowerCase(); } |
| 121 | if ((wifiProfile.encryption == 'ccmp-aes') || (wifiProfile.encryption == 'ccmp')) { wifiProfile.encryption = 4; } |
| 122 | if ((wifiProfile.encryption == 'tkip-rc4') || (wifiProfile.encryption == 'tkip')) { wifiProfile.encryption = 3; } |
| 123 | if (typeof wifiProfile.encryption != 'number') { wifiProfile.encryption = 4; } // Default to CCMP-AES |
| 124 | |
| 125 | // Type |
| 126 | wifiProfile.type = 3; // Infrastructure |
| 127 | |
| 128 | // Check authentication |
| 129 | if ([4, 6].indexOf(wifiProfile.authentication) >= 0) { |
| 130 | // Password authentication |
| 131 | if ((typeof wifiProfile.password != 'string') || (wifiProfile.password.length < 8) || (wifiProfile.password.length > 63)) continue; |
| 132 | } else if ([5, 7, 32768, 32769].indexOf(wifiProfile.authentication) >= 0) { |
| 133 | // 802.1x authentication |
| 134 | if ((domain.amtmanager['802.1x'] == null) || (typeof domain.amtmanager['802.1x'] != 'object')) continue; |
| 135 | } |
| 136 | |
| 137 | goodWifiProfiles.push(wifiProfile); |
| 138 | } |
| 139 | } |
| 140 | |
| 141 | domain.amtmanager.wifiprofiles = goodWifiProfiles; |
| 142 | } else { |
| 143 | delete domain.amtmanager.wifiprofiles; |
| 144 | } |
| 145 | } |
| 146 | |
| 147 | // Check if an Intel AMT device is being managed |
| 148 | function isAmtDeviceValid(dev) { |
| 149 | var devices = obj.amtDevices[dev.nodeid]; |
| 150 | if (devices == null) return false; |
| 151 | return (devices.indexOf(dev) >= 0) |
| 152 | } |
| 153 | |
| 154 | // Add an Intel AMT managed device |
| 155 | function addAmtDevice(dev) { |
| 156 | var devices = obj.amtDevices[dev.nodeid]; |
| 157 | if (devices == null) { obj.amtDevices[dev.nodeid] = [dev]; return true; } |
| 158 | if (devices.indexOf(dev) >= 0) return false; // This device is already in the list |
| 159 | devices.push(dev); // Add the device to the list |
| 160 | return true; |
| 161 | } |
| 162 | |
| 163 | // Remove an Intel AMT managed device |
| 164 | function removeAmtDevice(dev, tag) { |
| 165 | parent.debug('amt', dev.name, "Remove device", dev.nodeid, dev.connType, tag); |
| 166 | |
| 167 | // Find the device in the list |
| 168 | var devices = obj.amtDevices[dev.nodeid]; |
| 169 | if (devices == null) return false; |
| 170 | var i = devices.indexOf(dev); |
| 171 | if (i == -1) return false; |
| 172 | |
| 173 | // Remove from task limiter if needed |
| 174 | if (dev.taskid != null) { obj.parent.taskLimiter.completed(dev.taskid); delete dev.taskLimiter; } |
| 175 | |
| 176 | // Clean up this device |
| 177 | if (dev.amtstack != null) { dev.amtstack.CancelAllQueries(999); if (dev.amtstack != null) { delete dev.amtstack.dev; delete dev.amtstack; } } |
| 178 | if (dev.polltimer != null) { clearInterval(dev.polltimer); delete dev.polltimer; } |
| 179 | |
| 180 | // Remove the device from the list |
| 181 | devices.splice(i, 1); |
| 182 | if (devices.length == 0) { delete obj.amtDevices[dev.nodeid]; } else { obj.amtDevices[dev.nodeid] = devices; } |
| 183 | |
| 184 | // Notify connection closure if this is a LMS connection |
| 185 | if (dev.connType == 2) { dev.controlMsg({ action: 'close' }); } |
| 186 | return true; |
| 187 | } |
| 188 | |
| 189 | // Remove all Intel AMT devices for a given nodeid |
| 190 | function removeDevice(nodeid) { |
| 191 | parent.debug('amt', "Remove nodeid", nodeid); |
| 192 | |
| 193 | // Find the devices in the list |
| 194 | var devices = obj.amtDevices[nodeid]; |
| 195 | if (devices == null) return false; |
| 196 | |
| 197 | for (var i in devices) { |
| 198 | var dev = devices[i]; |
| 199 | |
| 200 | // Remove from task limiter if needed |
| 201 | if (dev.taskid != null) { obj.parent.taskLimiter.completed(dev.taskid); delete dev.taskLimiter; } |
| 202 | |
| 203 | // Clean up this device |
| 204 | if (dev.amtstack != null) { dev.amtstack.wsman.comm.FailAllError = 999; delete dev.amtstack; } // Disconnect any active connections. |
| 205 | if (dev.polltimer != null) { clearInterval(dev.polltimer); delete dev.polltimer; } |
| 206 | |
| 207 | // Notify connection closure if this is a LMS connection |
| 208 | if (dev.connType == 2) { dev.controlMsg({ action: 'close' }); } |
| 209 | } |
| 210 | |
| 211 | // Remove all Intel AMT management sessions for this nodeid |
| 212 | delete obj.amtDevices[nodeid]; |
| 213 | |
| 214 | // If a 802.1x profile is active with MeshCentral Satellite, notify Satellite of the removal |
| 215 | if (domain.amtmanager['802.1x'] != null) { |
| 216 | var reqId = Buffer.from(parent.crypto.randomBytes(16), 'binary').toString('base64'); // Generate a crypto-secure request id. |
| 217 | 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 }); |
| 218 | } |
| 219 | |
| 220 | return true; |
| 221 | } |
| 222 | |
| 223 | // Start Intel AMT management |
| 224 | // connType: 0 = CIRA, 1 = CIRA-Relay, 2 = CIRA-LMS, 3 = LAN |
| 225 | obj.startAmtManagement = function (nodeid, connType, connection) { |
| 226 | //if (connType == 3) return; // DEBUG |
| 227 | var devices = obj.amtDevices[nodeid], dev = null; |
| 228 | if (devices != null) { for (var i in devices) { if ((devices[i].mpsConnection == connection) || (devices[i].host == connection)) { dev = devices[i]; } } } |
| 229 | if (dev != null) return false; // We are already managing this device on this connection |
| 230 | dev = { nodeid: nodeid, connType: connType, domainid: nodeid.split('/')[1] }; |
| 231 | if (typeof connection == 'string') { dev.host = connection; } |
| 232 | if (typeof connection == 'object') { dev.mpsConnection = connection; } |
| 233 | dev.consoleMsg = function deviceConsoleMsg(msg) { parent.debug('amt', deviceConsoleMsg.dev.name, msg); if (typeof deviceConsoleMsg.conn == 'object') { deviceConsoleMsg.conn.ControlMsg({ action: 'console', msg: msg }); } } |
| 234 | dev.consoleMsg.conn = connection; |
| 235 | dev.consoleMsg.dev = dev; |
| 236 | dev.controlMsg = function deviceControlMsg(msg) { if (typeof deviceControlMsg.conn == 'object') { deviceControlMsg.conn.ControlMsg(msg); } } |
| 237 | dev.controlMsg.conn = connection; |
| 238 | parent.debug('amt', "Start Management", nodeid, connType); |
| 239 | addAmtDevice(dev); |
| 240 | |
| 241 | // Start the device manager in the task limiter so not to flood the server. Low priority task |
| 242 | obj.parent.taskLimiter.launch(function (dev, taskid, taskLimiterQueue) { |
| 243 | if (isAmtDeviceValid(dev)) { |
| 244 | // Start managing this device |
| 245 | dev.taskid = taskid; |
| 246 | fetchIntelAmtInformation(dev); |
| 247 | } else { |
| 248 | // Device is not valid anymore, do nothing |
| 249 | obj.parent.taskLimiter.completed(taskid); |
| 250 | } |
| 251 | }, dev, 2); |
| 252 | } |
| 253 | |
| 254 | // Stop Intel AMT management |
| 255 | obj.stopAmtManagement = function (nodeid, connType, connection) { |
| 256 | var devices = obj.amtDevices[nodeid], dev = null; |
| 257 | if (devices != null) { for (var i in devices) { if ((devices[i].mpsConnection == connection) || (devices[i].host == connection)) { dev = devices[i]; } } } |
| 258 | if (dev == null) return false; // We are not managing this device on this connection |
| 259 | parent.debug('amt', dev.name, "Stop Management", nodeid, connType); |
| 260 | return removeAmtDevice(dev, 1); |
| 261 | } |
| 262 | |
| 263 | // Get a string status of the managed devices |
| 264 | obj.getStatusString = function () { |
| 265 | var r = ''; |
| 266 | for (var nodeid in obj.amtDevices) { |
| 267 | var devices = obj.amtDevices[nodeid]; |
| 268 | r += devices[0].nodeid + ', ' + devices[0].name + '\r\n'; |
| 269 | for (var i in devices) { |
| 270 | var dev = devices[i]; |
| 271 | var items = []; |
| 272 | if (dev.state == 1) { items.push('Connected'); } else { items.push('Trying'); } |
| 273 | items.push(ConnectionTypeStrings[dev.connType]); |
| 274 | if (dev.connType == 3) { items.push(dev.host); } |
| 275 | if (dev.polltimer != null) { items.push('Polling Power'); } |
| 276 | r += ' ' + items.join(', ') + '\r\n'; |
| 277 | } |
| 278 | } |
| 279 | if (r == '') { r = "No managed Intel AMT devices"; } |
| 280 | return r; |
| 281 | } |
| 282 | |
| 283 | // Receive a JSON control message from the MPS server |
| 284 | obj.mpsControlMessage = function (nodeid, conn, connType, jsondata) { |
| 285 | // Find the devices in the list |
| 286 | var dev = null; |
| 287 | var devices = obj.amtDevices[nodeid]; |
| 288 | if (devices == null) return; |
| 289 | for (var i in devices) { if (devices[i].mpsConnection === conn) { dev = devices[i]; } } |
| 290 | if (dev == null) return; |
| 291 | |
| 292 | // Process the message |
| 293 | switch (jsondata.action) { |
| 294 | case 'deactivate': |
| 295 | if ((dev.connType != 2) || (dev.deactivateCcmPending != 1)) break; // Only accept MEI state on CIRA-LMS connection |
| 296 | delete dev.deactivateCcmPending; |
| 297 | deactivateIntelAmtCCMEx(dev, jsondata.value); |
| 298 | break; |
| 299 | case 'meiState': |
| 300 | if (dev.acmactivate == 1) { |
| 301 | // Continue ACM activation |
| 302 | dev.consoleMsg("Got new Intel AMT MEI state. Holding 40 seconds prior to ACM activation..."); |
| 303 | delete dev.acmactivate; |
| 304 | var continueAcmFunc = function continueAcm() { if (isAmtDeviceValid(continueAcm.dev)) { activateIntelAmtAcmEx0(continueAcm.dev); } } |
| 305 | continueAcmFunc.dev = dev; |
| 306 | setTimeout(continueAcmFunc, 40000); |
| 307 | } else { |
| 308 | if (dev.pendingUpdatedMeiState != 1) break; |
| 309 | delete dev.pendingUpdatedMeiState; |
| 310 | attemptInitialContact(dev); |
| 311 | } |
| 312 | break; |
| 313 | case 'startTlsHostConfig': |
| 314 | if (dev.acmTlsInfo == null) break; |
| 315 | if ((typeof jsondata.value != 'object') || (typeof jsondata.value.status != 'number')) { |
| 316 | removeAmtDevice(dev, 2); // Invalid startTlsHostConfig response |
| 317 | } else { |
| 318 | activateIntelAmtTlsAcmEx(dev, jsondata.value); // Start TLS activation. |
| 319 | } |
| 320 | break; |
| 321 | case 'stopConfiguration': |
| 322 | if (dev.acmactivate != 1) break; |
| 323 | if (jsondata.value == 3) { delete dev.acmactivate; activateIntelAmtAcmEx0(dev); } // Intel AMT was already not in in-provisioning state, keep going right away. |
| 324 | else if (jsondata.value == 0) { |
| 325 | dev.consoleMsg("Cleared in-provisioning state. Holding 30 seconds prior to getting Intel AMT MEI state..."); |
| 326 | var askStateFunc = function askState() { if (isAmtDeviceValid(askState.dev)) { askState.dev.controlMsg({ action: 'mestate' }); } } |
| 327 | askStateFunc.dev = dev; |
| 328 | setTimeout(askStateFunc, 30000); |
| 329 | } |
| 330 | else { dev.consoleMsg("Unknown stopConfiguration() state of " + jsondata.value + ". Continuing with ACM activation..."); delete dev.acmactivate; activateIntelAmtAcmEx0(dev); } |
| 331 | break; |
| 332 | } |
| 333 | } |
| 334 | |
| 335 | // Subscribe to server events |
| 336 | parent.AddEventDispatch(['*'], obj); |
| 337 | |
| 338 | // Handle server events |
| 339 | // 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. |
| 340 | obj.HandleEvent = function (source, event, ids, id) { |
| 341 | switch (event.action) { |
| 342 | case 'removenode': { // React to node being removed |
| 343 | 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. |
| 344 | removeDevice(event.nodeid); |
| 345 | break; |
| 346 | } |
| 347 | case 'wakedevices': { // React to node wakeup command, perform Intel AMT wake if possible |
| 348 | 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. |
| 349 | if (Array.isArray(event.nodeids)) { for (var i in event.nodeids) { performPowerAction(event.nodeids[i], 2); } } |
| 350 | break; |
| 351 | } |
| 352 | case 'oneclickrecovery': { // React to Intel AMT One Click Recovery command |
| 353 | 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. |
| 354 | if (Array.isArray(event.nodeids)) { for (var i in event.nodeids) { performOneClickRecoveryAction(event.nodeids[i], event.file); } } |
| 355 | break; |
| 356 | } |
| 357 | case 'amtpoweraction': { |
| 358 | 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. |
| 359 | if (Array.isArray(event.nodeids)) { for (var i in event.nodeids) { performPowerAction(event.nodeids[i], event.actiontype); } } |
| 360 | break; |
| 361 | } |
| 362 | case 'changenode': { // React to changes in a device |
| 363 | var devices = obj.amtDevices[event.nodeid], rescan = false; |
| 364 | if (devices != null) { |
| 365 | for (var i in devices) { |
| 366 | var dev = devices[i]; |
| 367 | dev.name = event.node.name; |
| 368 | dev.icon = event.node.icon; |
| 369 | dev.rname = event.node.rname; |
| 370 | |
| 371 | // If there are any changes, apply them. |
| 372 | if (event.node.intelamt != null) { |
| 373 | if (dev.intelamt == null) { dev.intelamt = {}; } |
| 374 | if ((typeof event.node.intelamt.version == 'string') && (event.node.intelamt.version != dev.intelamt.ver)) { dev.intelamt.ver = event.node.intelamt.version; } |
| 375 | if ((typeof event.node.intelamt.user == 'string') && (event.node.intelamt.user != dev.intelamt.user)) { dev.intelamt.user = event.node.intelamt.user; } |
| 376 | if ((typeof event.node.intelamt.pass == 'string') && (event.node.intelamt.pass != dev.intelamt.pass)) { dev.intelamt.pass = event.node.intelamt.pass; } |
| 377 | if ((typeof event.node.intelamt.mpspass == 'string') && (event.node.intelamt.mpspass != dev.intelamt.mpspass)) { dev.intelamt.mpspass = event.node.intelamt.mpspass; } |
| 378 | if ((typeof event.node.intelamt.host == 'string') && (event.node.intelamt.host != dev.intelamt.host)) { dev.intelamt.host = event.node.intelamt.host; } |
| 379 | if ((typeof event.node.intelamt.realm == 'string') && (event.node.intelamt.realm != dev.intelamt.realm)) { dev.intelamt.realm = event.node.intelamt.realm; } |
| 380 | if ((typeof event.node.intelamt.hash == 'string') && (event.node.intelamt.hash != dev.intelamt.hash)) { dev.intelamt.hash = event.node.intelamt.hash; } |
| 381 | if ((typeof event.node.intelamt.tls == 'number') && (event.node.intelamt.tls != dev.intelamt.tls)) { dev.intelamt.tls = event.node.intelamt.tls; } |
| 382 | if ((typeof event.node.intelamt.state == 'number') && (event.node.intelamt.state != dev.intelamt.state)) { dev.intelamt.state = event.node.intelamt.state; } |
| 383 | } |
| 384 | |
| 385 | if ((dev.connType == 3) && (dev.host != event.node.host)) { |
| 386 | dev.host = event.node.host; // The host has changed, if we are connected to this device locally, we need to reset. |
| 387 | removeAmtDevice(dev, 3); // We are going to wait for the AMT scanned to find this device again. |
| 388 | rescan = true; |
| 389 | } |
| 390 | } |
| 391 | } else { |
| 392 | // 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. |
| 393 | if (event.amtchange == 1) { rescan = true; } |
| 394 | } |
| 395 | |
| 396 | // 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. |
| 397 | if (rescan && (parent.amtScanner != null)) { parent.amtScanner.performSpecificScan(event.node); } |
| 398 | break; |
| 399 | } |
| 400 | case 'meshchange': { |
| 401 | // TODO |
| 402 | |
| 403 | // 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. |
| 404 | |
| 405 | break; |
| 406 | } |
| 407 | case 'satelliteResponse': { |
| 408 | if ((typeof event.nodeid != 'string') || (typeof event.reqid != 'string') || (event.satelliteFlags != 2)) return; |
| 409 | var devices = obj.amtDevices[event.nodeid], devFound = null; |
| 410 | if (devices != null) { for (var i in devices) { if (devices[i].netAuthSatReqId == event.reqid) { devFound = devices[i]; } } } |
| 411 | if (devFound == null) return; // Unable to find a device for this 802.1x profile |
| 412 | switch (event.subaction) { |
| 413 | case '802.1x-KeyPair-Request': { |
| 414 | // 802.1x request for public/private key pair be generated |
| 415 | attempt8021xKeyGeneration(devFound); |
| 416 | break; |
| 417 | } |
| 418 | case '802.1x-CSR-Request': { |
| 419 | // 802.1x request for a Certificate Signing Request |
| 420 | attempt8021xCRSRequest(devFound, event); |
| 421 | break; |
| 422 | } |
| 423 | case '802.1x-Profile-Completed': { |
| 424 | // The 802.1x profile request is done, set it in Intel AMT. |
| 425 | if (devFound.netAuthSatReqTimer != null) { clearTimeout(devFound.netAuthSatReqTimer); delete devFound.netAuthSatReqTimer; } |
| 426 | |
| 427 | if ((event.response == null) || (typeof event.response != 'object')) { |
| 428 | // Unable to create a 802.1x profile |
| 429 | delete devFound.netAuthSatReqId; |
| 430 | if (isAmtDeviceValid(devFound) == false) return; // Device no longer exists, ignore this request. |
| 431 | delete devFound.netAuthSatReqData; |
| 432 | devFound.consoleMsg("MeshCentral Satellite could not create a 802.1x profile for this device."); |
| 433 | devTaskCompleted(devFound); |
| 434 | return; |
| 435 | } |
| 436 | |
| 437 | if (typeof event.response.authProtocol != 'number') { delete devFound.netAuthSatReqId; break; } |
| 438 | |
| 439 | // We got a new 802.1x profile |
| 440 | devFound.netAuthCredentials = event.response; |
| 441 | perform8021xRootCertCheck(devFound); |
| 442 | break; |
| 443 | } |
| 444 | } |
| 445 | break; |
| 446 | } |
| 447 | } |
| 448 | } |
| 449 | |
| 450 | |
| 451 | // |
| 452 | // Intel AMT Connection Setup |
| 453 | // |
| 454 | |
| 455 | // Update information about a device |
| 456 | function fetchIntelAmtInformation(dev) { |
| 457 | parent.db.Get(dev.nodeid, function (err, nodes) { |
| 458 | if ((nodes == null) || (nodes.length != 1)) { removeAmtDevice(dev, 4); return; } |
| 459 | const node = nodes[0]; |
| 460 | if ((node.intelamt == null) || (node.meshid == null)) { removeAmtDevice(dev, 5); return; } |
| 461 | const mesh = parent.webserver.meshes[node.meshid]; |
| 462 | if (mesh == null) { removeAmtDevice(dev, 6); return; } |
| 463 | if (dev == null) { return; } |
| 464 | |
| 465 | // Fetch Intel AMT setup policy |
| 466 | // mesh.amt.type: 0 = No Policy, 1 = Deactivate CCM, 2 = Manage in CCM, 3 = Manage in ACM |
| 467 | // mesh.amt.cirasetup: 0 = No Change, 1 = Remove CIRA, 2 = Setup CIRA |
| 468 | var amtPolicy = 0, ciraPolicy = 0, badPass = 0, password = null; |
| 469 | if (mesh.amt != null) { |
| 470 | if (mesh.amt.type) { amtPolicy = mesh.amt.type; } |
| 471 | if (mesh.amt.type == 4) { |
| 472 | // Fully automatic policy |
| 473 | ciraPolicy = 2; // CIRA will be setup |
| 474 | badPass = 1; // Automatically re-active CCM |
| 475 | password = null; // Randomize the password. |
| 476 | } else { |
| 477 | if (mesh.amt.cirasetup) { ciraPolicy = mesh.amt.cirasetup; } |
| 478 | if (mesh.amt.badpass) { badPass = mesh.amt.badpass; } |
| 479 | if ((typeof mesh.amt.password == 'string') && (mesh.amt.password != '')) { password = mesh.amt.password; } |
| 480 | } |
| 481 | } |
| 482 | if (amtPolicy == 0) { ciraPolicy = 0; } // If no policy, don't change CIRA state. |
| 483 | if (amtPolicy == 1) { ciraPolicy = 1; } // If deactivation policy, clear CIRA. |
| 484 | dev.policy = { amtPolicy: amtPolicy, ciraPolicy: ciraPolicy, badPass: badPass, password: password }; |
| 485 | |
| 486 | // Setup the monitored device |
| 487 | dev.name = node.name; |
| 488 | dev.rname = node.rname; |
| 489 | dev.icon = node.icon; |
| 490 | dev.meshid = node.meshid; |
| 491 | dev.intelamt = node.intelamt; |
| 492 | |
| 493 | // Check if the status of Intel AMT sent by the agents matched what we have in the database |
| 494 | if ((dev.connType == 2) && (dev.mpsConnection != null) && (dev.mpsConnection.tag != null) && (dev.mpsConnection.tag.meiState != null)) { |
| 495 | dev.aquired = {}; |
| 496 | if ((typeof dev.mpsConnection.tag.meiState.OsHostname == 'string') && (typeof dev.mpsConnection.tag.meiState.OsDnsSuffix == 'string')) { |
| 497 | dev.host = dev.aquired.host = dev.mpsConnection.tag.meiState.OsHostname + '.' + dev.mpsConnection.tag.meiState.OsDnsSuffix; |
| 498 | } |
| 499 | if (typeof dev.mpsConnection.tag.meiState['ProvisioningState'] == 'number') { |
| 500 | dev.intelamt.state = dev.aquired.state = dev.mpsConnection.tag.meiState['ProvisioningState']; |
| 501 | } |
| 502 | if ((typeof dev.mpsConnection.tag.meiState['Versions'] == 'object') && (typeof dev.mpsConnection.tag.meiState['Versions']['AMT'] == 'string')) { |
| 503 | dev.intelamt.ver = dev.aquired.version = dev.mpsConnection.tag.meiState['Versions']['AMT']; |
| 504 | } |
| 505 | if (typeof dev.mpsConnection.tag.meiState['Flags'] == 'number') { |
| 506 | const flags = dev.intelamt.flags = dev.mpsConnection.tag.meiState['Flags']; |
| 507 | if (flags & 2) { dev.aquired.controlMode = 1; } // CCM |
| 508 | if (flags & 4) { dev.aquired.controlMode = 2; } // ACM |
| 509 | } |
| 510 | UpdateDevice(dev); |
| 511 | } |
| 512 | |
| 513 | // If there is no Intel AMT policy for this device, stop here. |
| 514 | //if (amtPolicy == 0) { dev.consoleMsg("Done."); removeAmtDevice(dev, 7); return; } |
| 515 | |
| 516 | // Initiate the communication to Intel AMT |
| 517 | dev.consoleMsg("Checking Intel AMT state..."); |
| 518 | attemptInitialContact(dev); |
| 519 | }); |
| 520 | } |
| 521 | |
| 522 | // Attempt to perform initial contact with Intel AMT |
| 523 | function attemptInitialContact(dev) { |
| 524 | // If there is a WSMAN stack setup, clean it up now. |
| 525 | if (dev.amtstack != null) { |
| 526 | dev.amtstack.CancelAllQueries(999); |
| 527 | delete dev.amtstack.dev; |
| 528 | delete dev.amtstack; |
| 529 | } |
| 530 | |
| 531 | delete dev.amtstack; |
| 532 | parent.debug('amt', dev.name, "Attempt Initial Contact", ["CIRA", "CIRA-Relay", "CIRA-LMS", "Local"][dev.connType]); |
| 533 | |
| 534 | // Check Intel AMT policy when CIRA-LMS connection is in use. |
| 535 | if ((dev.connType == 2) && (dev.mpsConnection != null) && (dev.mpsConnection.tag != null) && (dev.mpsConnection.tag.meiState != null)) { |
| 536 | // Intel AMT activation policy |
| 537 | if ((dev.policy.amtPolicy > 1) && (dev.mpsConnection.tag.meiState.ProvisioningState !== 2)) { |
| 538 | // This Intel AMT device is not activated, we need to work on activating it. |
| 539 | activateIntelAmt(dev); |
| 540 | return; |
| 541 | } |
| 542 | // Check if we have an ACM activation policy, but the device is in CCM |
| 543 | if (((dev.policy.amtPolicy == 3) || (dev.policy.amtPolicy == 4)) && (dev.mpsConnection.tag.meiState.ProvisioningState == 2) && ((dev.mpsConnection.tag.meiState.Flags & 2) != 0)) { |
| 544 | // This device in is CCM, check if we can upgrade to ACM |
| 545 | if (activateIntelAmt(dev) == false) return; // If this return true, the platform is in CCM and can't go to ACM, keep going with management. |
| 546 | } |
| 547 | // Intel AMT CCM deactivation policy |
| 548 | if (dev.policy.amtPolicy == 1) { |
| 549 | if ((dev.mpsConnection.tag.meiState.ProvisioningState == 2) && ((dev.mpsConnection.tag.meiState.Flags & 2) != 0)) { |
| 550 | // Deactivate CCM. |
| 551 | deactivateIntelAmtCCM(dev); |
| 552 | return; |
| 553 | } |
| 554 | } |
| 555 | } |
| 556 | |
| 557 | // See what username/password we need to try |
| 558 | // We create an efficient strategy for trying different Intel AMT passwords. |
| 559 | if (dev.acctry == null) { |
| 560 | dev.acctry = []; |
| 561 | |
| 562 | // Add Intel AMT username and password provided by MeshCMD if available |
| 563 | 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 != '')) { |
| 564 | dev.acctry.push([dev.mpsConnection.tag.meiState.amtuser, dev.mpsConnection.tag.meiState.amtpass]); |
| 565 | } |
| 566 | |
| 567 | // Add the know Intel AMT password for this device if available |
| 568 | 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]); } |
| 569 | |
| 570 | // Add the policy password as an alternative |
| 571 | if ((typeof dev.policy.password == 'string') && (dev.policy.password != '')) { dev.acctry.push(['admin', dev.policy.password]); } |
| 572 | |
| 573 | // Add any configured admin account as alternatives |
| 574 | 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]); } } |
| 575 | |
| 576 | // Add any previous passwords for the device UUID as alternative |
| 577 | 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)) { |
| 578 | for (var i in parent.amtPasswords[dev.mpsConnection.tag.meiState.UUID]) { |
| 579 | dev.acctry.push(['admin', parent.amtPasswords[dev.mpsConnection.tag.meiState.UUID][i]]); |
| 580 | } |
| 581 | } |
| 582 | |
| 583 | // Remove any duplicates user/passwords |
| 584 | var acctry2 = []; |
| 585 | for (var i = 0; i < dev.acctry.length; i++) { |
| 586 | var found = false; |
| 587 | 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; } } |
| 588 | if (found == false) { acctry2.push(dev.acctry[i]); } |
| 589 | } |
| 590 | dev.acctry = acctry2; |
| 591 | |
| 592 | // If we have passwords to try, try the first one now. |
| 593 | if (dev.acctry.length == 0) { |
| 594 | dev.consoleMsg("No admin login passwords to try, stopping now."); |
| 595 | removeAmtDevice(dev, 8); |
| 596 | return; |
| 597 | } |
| 598 | } |
| 599 | |
| 600 | if ((dev.acctry == null) || (dev.acctry.length == 0)) { removeAmtDevice(dev, 9); return; } // No Intel AMT credentials to try |
| 601 | var user = dev.acctry[0][0], pass = dev.acctry[0][1]; // Try the first user/pass in the list |
| 602 | |
| 603 | switch (dev.connType) { |
| 604 | case 0: // CIRA |
| 605 | // Handle the case where the Intel AMT CIRA is connected (connType 0) |
| 606 | // In this connection type, we look at the port bindings to see if we need to do TLS or not. |
| 607 | |
| 608 | // Check to see if CIRA is connected on this server. |
| 609 | var ciraconn = dev.mpsConnection; |
| 610 | 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. |
| 611 | |
| 612 | // See if we need to perform TLS or not. We prefer not to do TLS within CIRA. |
| 613 | var dotls = -1; |
| 614 | if (ciraconn.tag.boundPorts.indexOf('16992')) { dotls = 0; } |
| 615 | else if (ciraconn.tag.boundPorts.indexOf('16993')) { dotls = 1; } |
| 616 | if (dotls == -1) { removeAmtDevice(dev, 10); return; } // The Intel AMT ports are not open, not a device we can deal with. |
| 617 | |
| 618 | // Connect now |
| 619 | parent.debug('amt', dev.name, 'CIRA-Connect', (dotls == 1) ? "TLS" : "NoTLS", user, pass); |
| 620 | var comm; |
| 621 | if (dotls == 1) { |
| 622 | comm = CreateWsmanComm(dev.nodeid, 16993, user, pass, 1, null, ciraconn); // Perform TLS |
| 623 | comm.xtlsFingerprint = 0; // Perform no certificate checking |
| 624 | } else { |
| 625 | comm = CreateWsmanComm(dev.nodeid, 16992, user, pass, 0, null, ciraconn); // No TLS |
| 626 | } |
| 627 | var wsstack = WsmanStackCreateService(comm); |
| 628 | dev.amtstack = AmtStackCreateService(wsstack); |
| 629 | dev.amtstack.dev = dev; |
| 630 | dev.amtstack.BatchEnum(null, ['*AMT_GeneralSettings', '*IPS_HostBasedSetupService'], attemptLocalConnectResponse); |
| 631 | break; |
| 632 | case 1: // CIRA-Relay |
| 633 | case 2: // CIRA-LMS |
| 634 | // Handle the case where the Intel AMT relay or LMS is connected (connType 1 or 2) |
| 635 | // Check to see if CIRA is connected on this server. |
| 636 | var ciraconn = dev.mpsConnection; |
| 637 | if ((ciraconn == null) || (ciraconn.tag == null) || (ciraconn.tag.boundPorts == null)) { removeAmtDevice(dev, 11); return; } // Relay connection not valid |
| 638 | |
| 639 | // Connect now |
| 640 | var comm; |
| 641 | if ((dev.tlsfail !== true) && (parent.config.domains[dev.domainid].amtmanager.tlsconnections !== false) && (dev.intelamt.tls == 1)) { |
| 642 | parent.debug('amt', dev.name, (dev.connType == 1) ? 'Relay-Connect' : 'LMS-Connect', "TLS", user); |
| 643 | comm = CreateWsmanComm(dev.nodeid, 16993, user, pass, 1, null, ciraconn); // Perform TLS |
| 644 | comm.xtlsFingerprint = 0; // Perform no certificate checking |
| 645 | } else { |
| 646 | parent.debug('amt', dev.name, (dev.connType == 1) ? 'Relay-Connect' : 'LMS-Connect', "NoTLS", user); |
| 647 | comm = CreateWsmanComm(dev.nodeid, 16992, user, pass, 0, null, ciraconn); // No TLS |
| 648 | } |
| 649 | var wsstack = WsmanStackCreateService(comm); |
| 650 | dev.amtstack = AmtStackCreateService(wsstack); |
| 651 | dev.amtstack.dev = dev; |
| 652 | dev.amtstack.BatchEnum(null, ['*AMT_GeneralSettings', '*IPS_HostBasedSetupService'], attemptLocalConnectResponse); |
| 653 | break; |
| 654 | case 3: // Local LAN |
| 655 | // Check if Intel AMT is activated. If not, stop here. |
| 656 | if ((dev.intelamt == null) || ((dev.intelamt.state != null) && (dev.intelamt.state != 2))) { removeAmtDevice(dev, 12); return; } |
| 657 | |
| 658 | // Handle the case where the Intel AMT local scanner found the device (connType 3) |
| 659 | parent.debug('amt', dev.name, "Attempt Initial Local Contact", dev.connType, dev.host); |
| 660 | if (typeof dev.host != 'string') { removeAmtDevice(dev, 13); return; } // Local connection not valid |
| 661 | |
| 662 | // Since we don't allow two or more connections to the same host, check if a pending connection is active. |
| 663 | if (obj.activeLocalConnections[dev.host] != null) { |
| 664 | // Active connection, hold and try later. |
| 665 | var tryAgainFunc = function tryAgainFunc() { if (obj.amtDevices[tryAgainFunc.dev.nodeid] != null) { attemptInitialContact(tryAgainFunc.dev); } } |
| 666 | tryAgainFunc.dev = dev; |
| 667 | setTimeout(tryAgainFunc, 5000); |
| 668 | } else { |
| 669 | // No active connections |
| 670 | |
| 671 | // Connect now |
| 672 | var comm; |
| 673 | if ((dev.tlsfail !== true) && (parent.config.domains[dev.domainid].amtmanager.tlsconnections !== false) && (dev.intelamt.tls == 1)) { |
| 674 | parent.debug('amt', dev.name, 'Direct-Connect', "TLS", dev.host, user); |
| 675 | comm = CreateWsmanComm(dev.host, 16993, user, pass, 1); // Always try with TLS first |
| 676 | comm.xtlsFingerprint = 0; // Perform no certificate checking |
| 677 | } else { |
| 678 | parent.debug('amt', dev.name, 'Direct-Connect', "NoTLS", dev.host, user); |
| 679 | comm = CreateWsmanComm(dev.host, 16992, user, pass, 0); // Try without TLS |
| 680 | } |
| 681 | var wsstack = WsmanStackCreateService(comm); |
| 682 | dev.amtstack = AmtStackCreateService(wsstack); |
| 683 | dev.amtstack.dev = dev; |
| 684 | obj.activeLocalConnections[dev.host] = dev; |
| 685 | dev.amtstack.BatchEnum(null, ['*AMT_GeneralSettings', '*IPS_HostBasedSetupService'], attemptLocalConnectResponse); |
| 686 | } |
| 687 | break; |
| 688 | } |
| 689 | } |
| 690 | |
| 691 | function attemptLocalConnectResponse(stack, name, responses, status) { |
| 692 | const dev = stack.dev; |
| 693 | parent.debug('amt', dev.name, "Initial Contact Response", status); |
| 694 | |
| 695 | // If this is a local connection device, release active connection to this host. |
| 696 | if (dev.connType == 3) { delete obj.activeLocalConnections[dev.host]; } |
| 697 | |
| 698 | // Check if the device still exists |
| 699 | if (isAmtDeviceValid(dev) == false) return; // Device no longer exists, ignore this request. |
| 700 | |
| 701 | // Check the response |
| 702 | 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)) { |
| 703 | // Everything looks good |
| 704 | dev.consoleMsg(stack.wsman.comm.xtls ? "Intel AMT connected with TLS." : "Intel AMT connected."); |
| 705 | dev.state = 1; |
| 706 | if (dev.aquired == null) { dev.aquired = {}; } |
| 707 | dev.aquired.controlMode = responses['IPS_HostBasedSetupService'].response.CurrentControlMode; // 1 = CCM, 2 = ACM |
| 708 | if (typeof stack.wsman.comm.amtVersion == 'string') { // Set the Intel AMT version using the HTTP header if present |
| 709 | var verSplit = stack.wsman.comm.amtVersion.split('.'); |
| 710 | if (verSplit.length >= 2) { |
| 711 | dev.aquired.version = verSplit[0] + '.' + verSplit[1]; |
| 712 | dev.aquired.majorver = parseInt(verSplit[0]); |
| 713 | dev.aquired.minorver = parseInt(verSplit[1]); |
| 714 | if (verSplit.length >= 3) { |
| 715 | dev.aquired.version = verSplit[0] + '.' + verSplit[1] + '.' + verSplit[2]; |
| 716 | dev.aquired.maintenancever = parseInt(verSplit[2]); |
| 717 | } |
| 718 | } |
| 719 | } |
| 720 | dev.aquired.realm = stack.wsman.comm.digestRealm; |
| 721 | dev.aquired.user = dev.intelamt.user = stack.wsman.comm.user; |
| 722 | dev.aquired.pass = dev.intelamt.pass = stack.wsman.comm.pass; |
| 723 | dev.aquired.lastContact = Date.now(); |
| 724 | dev.aquired.warn = 0; // Clear all warnings (TODO: Check Realm and TLS cert pinning) |
| 725 | 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. |
| 726 | if (stack.wsman.comm.xtls == 1) { dev.aquired.hash = stack.wsman.comm.xtlsCertificate.fingerprint.split(':').join('').toLowerCase(); } else { delete dev.aquired.hash; } |
| 727 | UpdateDevice(dev); |
| 728 | |
| 729 | // If this is the new first user/pass for the device UUID, update the activation log now. |
| 730 | 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)) { |
| 731 | 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 }); |
| 732 | } |
| 733 | |
| 734 | // Perform Intel AMT clock sync |
| 735 | attemptSyncClock(dev, function (dev) { |
| 736 | // Check Intel AMT TLS state |
| 737 | attemptTlsSync(dev, function (dev) { |
| 738 | // If we need to switch to TLS, do it now. |
| 739 | if (dev.switchToTls == 1) { delete dev.switchToTls; attemptInitialContact(dev); return; } |
| 740 | // Check Intel AMT 802.1x wired state and Intel AMT WIFI state (must be done both at once). |
| 741 | attemptWifiSync(dev, function (dev) { |
| 742 | // Check Intel AMT root certificate state |
| 743 | attemptRootCertSync(dev, function (dev) { |
| 744 | // Check Intel AMT CIRA settings |
| 745 | attemptCiraSync(dev, function (dev) { |
| 746 | // Check Intel AMT settings |
| 747 | attemptSettingsSync(dev, function (dev) { |
| 748 | // Clean unused certificates |
| 749 | attemptCleanCertsSync(dev, function (dev) { |
| 750 | // See if we need to get hardware inventory |
| 751 | attemptFetchHardwareInventory(dev, function (dev) { |
| 752 | dev.consoleMsg('Done.'); |
| 753 | |
| 754 | // Remove from task limiter if needed |
| 755 | if (dev.taskid != null) { obj.parent.taskLimiter.completed(dev.taskid); delete dev.taskLimiter; } |
| 756 | |
| 757 | if (dev.connType != 2) { |
| 758 | // Start power polling if not connected to LMS |
| 759 | var ppfunc = function powerPoleFunction() { fetchPowerState(powerPoleFunction.dev); } |
| 760 | ppfunc.dev = dev; |
| 761 | if(dev.polltimer){ clearInterval(dev.polltimer); delete dev.polltimer; } |
| 762 | dev.polltimer = new setInterval(ppfunc, 290000); // Poll for power state every 4 minutes 50 seconds. |
| 763 | fetchPowerState(dev); |
| 764 | } else { |
| 765 | // For LMS connections, close now. |
| 766 | dev.controlMsg({ action: 'close' }); |
| 767 | } |
| 768 | }); |
| 769 | }); |
| 770 | }); |
| 771 | }); |
| 772 | }); |
| 773 | }); |
| 774 | }); |
| 775 | }); |
| 776 | } else { |
| 777 | // We got a bad response |
| 778 | if ((dev.connType != 0) && (dev.tlsfail !== false) && (status == 408)) { // If not using CIRA and we get a 408 error while using non-TLS, try TLS. |
| 779 | // non-TLS error on a local connection, try again with TLS |
| 780 | dev.tlsfail = false; dev.intelamt.tls = 1; attemptInitialContact(dev); return; |
| 781 | } else 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. |
| 782 | // TLS error on a local connection, try again without TLS |
| 783 | dev.tlsfail = true; dev.intelamt.tls = 0; attemptInitialContact(dev); return; |
| 784 | } else if (status == 401) { |
| 785 | // Authentication error, see if we can use alternative credentials |
| 786 | if (dev.acctry != null) { |
| 787 | // Remove the first password from the trial list since it did not work. |
| 788 | if (dev.acctry.length > 0) { dev.acctry.shift(); } |
| 789 | |
| 790 | // We have another password to try, hold 20 second and try the next user/password. |
| 791 | if (dev.acctry.length > 0) { |
| 792 | dev.consoleMsg("Holding 20 seconds and trying again with different credentials..."); |
| 793 | setTimeout(function () { if (isAmtDeviceValid(dev)) { attemptInitialContact(dev); } }, 20000); return; |
| 794 | } |
| 795 | } |
| 796 | |
| 797 | // If this device is in CCM mode and we have a bad password reset policy, do it now. |
| 798 | 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)) { |
| 799 | deactivateIntelAmtCCM(dev); |
| 800 | return; |
| 801 | } |
| 802 | |
| 803 | // We are unable to authenticate to this device |
| 804 | dev.consoleMsg("Unable to connect."); |
| 805 | |
| 806 | // Set an error that we can't login to this device |
| 807 | if (dev.aquired == null) { dev.aquired = {}; } |
| 808 | dev.aquired.warn = 1; // Intel AMT Warning Flags: 1 = Unknown credentials, 2 = Realm Mismatch, 4 = TLS Cert Mismatch, 8 = Trying credentials |
| 809 | UpdateDevice(dev); |
| 810 | } |
| 811 | //console.log(dev.nodeid, dev.name, dev.host, status, 'Bad response'); |
| 812 | removeAmtDevice(dev, 14); |
| 813 | } |
| 814 | } |
| 815 | |
| 816 | |
| 817 | // |
| 818 | // Intel AMT Database Update |
| 819 | // |
| 820 | |
| 821 | // Change the current core information string and event it |
| 822 | function UpdateDevice(dev) { |
| 823 | // Check that the mesh exists |
| 824 | const mesh = parent.webserver.meshes[dev.meshid]; |
| 825 | if (mesh == null) { removeAmtDevice(dev, 15); return false; } |
| 826 | |
| 827 | // Get the node and change it if needed |
| 828 | parent.db.Get(dev.nodeid, function (err, nodes) { |
| 829 | if ((nodes == null) || (nodes.length != 1)) return false; |
| 830 | const device = nodes[0]; |
| 831 | var changes = [], change = 0, log = 0; |
| 832 | var domain = parent.config.domains[device.domain]; |
| 833 | if (domain == null) return false; |
| 834 | |
| 835 | // Check if anything changes |
| 836 | if (device.intelamt == null) { device.intelamt = {}; } |
| 837 | if ((typeof dev.aquired.version == 'string') && (dev.aquired.version != device.intelamt.ver)) { change = 1; log = 1; device.intelamt.ver = dev.aquired.version; changes.push('AMT version'); } |
| 838 | if ((typeof dev.aquired.user == 'string') && (dev.aquired.user != device.intelamt.user)) { change = 1; log = 1; device.intelamt.user = dev.aquired.user; changes.push('AMT user'); } |
| 839 | if ((typeof dev.aquired.pass == 'string') && (dev.aquired.pass != device.intelamt.pass)) { change = 1; log = 1; device.intelamt.pass = dev.aquired.pass; changes.push('AMT pass'); } |
| 840 | if ((typeof dev.aquired.mpspass == 'string') && (dev.aquired.mpspass != device.intelamt.mpspass)) { change = 1; log = 1; device.intelamt.mpspass = dev.aquired.mpspass; changes.push('AMT MPS pass'); } |
| 841 | if ((typeof dev.aquired.host == 'string') && (dev.aquired.host != device.intelamt.host)) { change = 1; log = 1; device.intelamt.host = dev.aquired.host; changes.push('AMT host'); } |
| 842 | if ((typeof dev.aquired.realm == 'string') && (dev.aquired.realm != device.intelamt.realm)) { change = 1; log = 1; device.intelamt.realm = dev.aquired.realm; changes.push('AMT realm'); } |
| 843 | if ((typeof dev.aquired.hash == 'string') && (dev.aquired.hash != device.intelamt.hash)) { change = 1; log = 1; device.intelamt.hash = dev.aquired.hash; changes.push('AMT hash'); } |
| 844 | if ((typeof dev.aquired.tls == 'number') && (dev.aquired.tls != device.intelamt.tls)) { change = 1; log = 1; device.intelamt.tls = dev.aquired.tls; /*changes.push('AMT TLS');*/ } |
| 845 | if ((typeof dev.aquired.state == 'number') && (dev.aquired.state != device.intelamt.state)) { change = 1; log = 1; device.intelamt.state = dev.aquired.state; changes.push('AMT state'); } |
| 846 | |
| 847 | // Intel AMT Warning Flags: 1 = Unknown credentials, 2 = Realm Mismatch, 4 = TLS Cert Mismatch, 8 = Trying credentials |
| 848 | if ((typeof dev.aquired.warn == 'number')) { |
| 849 | if ((dev.aquired.warn == 0) && (device.intelamt.warn != null)) { delete device.intelamt.warn; change = 1; } |
| 850 | else if ((dev.aquired.warn != 0) && (dev.aquired.warn != device.intelamt.warn)) { device.intelamt.warn = dev.aquired.warn; change = 1; } |
| 851 | } |
| 852 | |
| 853 | // Update Intel AMT flags if needed |
| 854 | // dev.aquired.controlMode // 1 = CCM, 2 = ACM |
| 855 | // (node.intelamt.flags & 2) == CCM, (node.intelamt.flags & 4) == ACM |
| 856 | var flags = 0; |
| 857 | if (typeof device.intelamt.flags == 'number') { flags = device.intelamt.flags; } |
| 858 | if (dev.aquired.controlMode == 1) { if ((flags & 4) != 0) { flags -= 4; } if ((flags & 2) == 0) { flags += 2; } } // CCM |
| 859 | if (dev.aquired.controlMode == 2) { if ((flags & 4) == 0) { flags += 4; } if ((flags & 2) != 0) { flags -= 2; } } // ACM |
| 860 | if (device.intelamt.flags != flags) { change = 1; log = 1; device.intelamt.flags = flags; changes.push('AMT flags'); } |
| 861 | |
| 862 | // If there are changes, event the new device |
| 863 | if (change == 1) { |
| 864 | // Save to the database |
| 865 | parent.db.Set(device); |
| 866 | |
| 867 | // Event the node change |
| 868 | var event = { etype: 'node', action: 'changenode', nodeid: device._id, domain: domain.id, node: parent.webserver.CloneSafeNode(device) }; |
| 869 | if (changes.length > 0) { event.msg = 'Changed device ' + device.name + ' from group ' + mesh.name + ': ' + changes.join(', '); } |
| 870 | if ((log == 0) || ((obj.agentInfo) && (obj.agentInfo.capabilities) && (obj.agentInfo.capabilities & 0x20)) || (changes.length == 0)) { event.nolog = 1; } // If this is a temporary device, don't log changes |
| 871 | if (parent.db.changeStream) { event.noact = 1; } // If DB change stream is active, don't use this event to change the node. Another event will come. |
| 872 | parent.DispatchEvent(parent.webserver.CreateMeshDispatchTargets(device.meshid, [device._id]), obj, event); |
| 873 | } |
| 874 | }); |
| 875 | } |
| 876 | |
| 877 | // Change the current core information string and event it |
| 878 | function ClearDeviceCredentials(dev) { |
| 879 | if (isAmtDeviceValid(dev) == false) return; // Device no longer exists, ignore this request. |
| 880 | |
| 881 | // Check that the mesh exists |
| 882 | const mesh = parent.webserver.meshes[dev.meshid]; |
| 883 | if (mesh == null) { removeAmtDevice(dev, 16); return; } |
| 884 | |
| 885 | // Get the node and change it if needed |
| 886 | parent.db.Get(dev.nodeid, function (err, nodes) { |
| 887 | if ((nodes == null) || (nodes.length != 1)) return; |
| 888 | const device = nodes[0]; |
| 889 | var changes = [], change = 0, log = 0; |
| 890 | var domain = parent.config.domains[device.domain]; |
| 891 | if (domain == null) return; |
| 892 | |
| 893 | // Check if anything changes |
| 894 | if (device.intelamt == null) return; |
| 895 | if (device.intelamt.user != null) { change = 1; log = 1; delete device.intelamt.user; changes.push('AMT user'); } |
| 896 | if (device.intelamt.pass != null) { change = 1; log = 1; delete device.intelamt.pass; changes.push('AMT pass'); } |
| 897 | |
| 898 | // If there are changes, event the new device |
| 899 | if (change == 1) { |
| 900 | // Save to the database |
| 901 | parent.db.Set(device); |
| 902 | |
| 903 | // Event the node change |
| 904 | var event = { etype: 'node', action: 'changenode', nodeid: device._id, domain: domain.id, node: parent.webserver.CloneSafeNode(device) }; |
| 905 | if (changes.length > 0) { event.msg = 'Changed device ' + device.name + ' from group ' + mesh.name + ': ' + changes.join(', '); } |
| 906 | if ((log == 0) || ((obj.agentInfo) && (obj.agentInfo.capabilities) && (obj.agentInfo.capabilities & 0x20)) || (changes.length == 0)) { event.nolog = 1; } // If this is a temporary device, don't log changes |
| 907 | if (parent.db.changeStream) { event.noact = 1; } // If DB change stream is active, don't use this event to change the node. Another event will come. |
| 908 | parent.DispatchEvent(parent.webserver.CreateMeshDispatchTargets(device.meshid, [device._id]), obj, event); |
| 909 | } |
| 910 | }); |
| 911 | } |
| 912 | |
| 913 | |
| 914 | // |
| 915 | // Intel AMT Power State |
| 916 | // |
| 917 | |
| 918 | // Get the current power state of a device |
| 919 | function fetchPowerState(dev) { |
| 920 | if (isAmtDeviceValid(dev) == false) return; |
| 921 | |
| 922 | // Check if the agent is connected |
| 923 | var constate = parent.GetConnectivityState(dev.nodeid); |
| 924 | if ((constate == null) || (constate.connectivity & 1)) return; // If there is no connectivity or the agent is connected, skip trying to poll power state. |
| 925 | |
| 926 | // Fetch the power state |
| 927 | dev.amtstack.BatchEnum(null, ['CIM_ServiceAvailableToElement'], function (stack, name, responses, status) { |
| 928 | const dev = stack.dev; |
| 929 | if (obj.amtDevices[dev.nodeid] == null) return; // Device no longer exists, ignore this response. |
| 930 | |
| 931 | if ((status != 200) || (responses['CIM_ServiceAvailableToElement'] == null) || (responses['CIM_ServiceAvailableToElement'].responses == null) || (responses['CIM_ServiceAvailableToElement'].responses.length < 1)) return; // If the polling fails, just skip it. |
| 932 | var powerstate = responses['CIM_ServiceAvailableToElement'].responses[0].PowerState; |
| 933 | if ((powerstate == 2) && (dev.aquired.majorver != null) && (dev.aquired.majorver > 9)) { |
| 934 | // Device is powered on and Intel AMT 10+, poll the OS power state. |
| 935 | dev.amtstack.Get('IPS_PowerManagementService', function (stack, name, response, status) { |
| 936 | const dev = stack.dev; |
| 937 | if (obj.amtDevices[dev.nodeid] == null) return; // Device no longer exists, ignore this response. |
| 938 | if (status != 200) return; |
| 939 | |
| 940 | // Convert the OS power state |
| 941 | var meshPowerState = -1; |
| 942 | if (response.Body.OSPowerSavingState == 2) { meshPowerState = 1; } // Fully powered (S0); |
| 943 | else if (response.Body.OSPowerSavingState == 3) { meshPowerState = 2; } // Modern standby (We are going to call this S1); |
| 944 | |
| 945 | // Set OS power state - connType: 0 = CIRA, 1 = CIRA-Relay, 2 = CIRA-LMS, 3 = LAN |
| 946 | if (meshPowerState >= 0) { parent.SetConnectivityState(dev.meshid, dev.nodeid, Date.now(), (dev.connType == 3 ? 4 : 2), meshPowerState, null, { name: dev.name }); } |
| 947 | }); |
| 948 | } else { |
| 949 | // Convert the power state |
| 950 | // AMT power: 1 = Other, 2 = On, 3 = Sleep-Light, 4 = Sleep-Deep, 5 = Power Cycle (Off-Soft), 6 = Off-Hard, 7 = Hibernate (Off-Soft), 8 = Off-Soft, 9 = Power Cycle (Off-Hard), 10 = Master Bus Reset, 11 = Diagnostic Interrupt (NMI), 12 = Off-Soft Graceful, 13 = Off-Hard Graceful, 14 = Master Bus Reset Graceful, 15 = Power Cycle (Off- oft Graceful), 16 = Power Cycle (Off - Hard Graceful), 17 = Diagnostic Interrupt (INIT) |
| 951 | // Mesh power: 0 = Unknown, 1 = S0 power on, 2 = S1 Sleep, 3 = S2 Sleep, 4 = S3 Sleep, 5 = S4 Hibernate, 6 = S5 Soft-Off, 7 = Present |
| 952 | var meshPowerState = -1, powerConversionTable = [-1, -1, 1, 2, 3, 6, 6, 5, 6]; |
| 953 | if (powerstate < powerConversionTable.length) { meshPowerState = powerConversionTable[powerstate]; } else { powerstate = 6; } |
| 954 | |
| 955 | // Set power state - connType: 0 = CIRA, 1 = CIRA-Relay, 2 = CIRA-LMS, 3 = LAN |
| 956 | if (meshPowerState >= 0) { parent.SetConnectivityState(dev.meshid, dev.nodeid, Date.now(), (dev.connType == 3 ? 4 : 2), meshPowerState, null, { name: dev.name }); } |
| 957 | } |
| 958 | }); |
| 959 | } |
| 960 | |
| 961 | // Perform a power action: 2 = Power up, 5 = Power cycle, 8 = Power down, 10 = Reset, 11 = Power on to BIOS, 12 = Reset to BIOS, 13 = Power on to BIOS with SOL, 14 = Reset to BIOS with SOL, 15 = Power on to PXE, 16 = Reset to PXE |
| 962 | function performPowerAction(nodeid, action) { |
| 963 | console.log('performPowerAction', nodeid, action); |
| 964 | var devices = obj.amtDevices[nodeid]; |
| 965 | if (devices == null) return; |
| 966 | for (var i in devices) { |
| 967 | var dev = devices[i]; |
| 968 | // If not LMS, has a AMT stack present and is in connected state, perform power operation. |
| 969 | if ((dev.connType != 2) && (dev.state == 1) && (dev.amtstack != null)) { |
| 970 | parent.debug('amt', dev.name, "performPowerAction", action); |
| 971 | dev.powerAction = action; |
| 972 | if (action <= 10) { |
| 973 | // Action: 2 = Power up, 5 = Power cycle, 8 = Power down, 10 = Reset |
| 974 | try { dev.amtstack.RequestPowerStateChange(action, performPowerActionResponse); } catch (ex) { } |
| 975 | } else { |
| 976 | // 11 = Power on to BIOS, 12 = Reset to BIOS, 13 = Power on to BIOS with SOL, 14 = Reset to BIOS with SOL, 15 = Power on to PXE, 16 = Reset to PXE |
| 977 | dev.amtstack.BatchEnum(null, ['*AMT_BootSettingData'], performAdvancedPowerActionResponse); |
| 978 | } |
| 979 | } |
| 980 | } |
| 981 | } |
| 982 | |
| 983 | // Response to Intel AMT advanced power action |
| 984 | function performAdvancedPowerActionResponse(stack, name, responses, status) { |
| 985 | const dev = stack.dev; |
| 986 | const action = dev.powerAction; |
| 987 | delete dev.powerAction; |
| 988 | if (obj.amtDevices[dev.nodeid] == null) return; // Device no longer exists, ignore this response. |
| 989 | if (status != 200) return; |
| 990 | if ((responses['AMT_BootSettingData'] == null) || (responses['AMT_BootSettingData'].response == null)) return; |
| 991 | var bootSettingData = responses['AMT_BootSettingData'].response; |
| 992 | |
| 993 | // Clean up parameters |
| 994 | bootSettingData['ConfigurationDataReset'] = false; |
| 995 | delete bootSettingData['WinREBootEnabled']; |
| 996 | delete bootSettingData['UEFILocalPBABootEnabled']; |
| 997 | delete bootSettingData['UEFIHTTPSBootEnabled']; |
| 998 | delete bootSettingData['SecureBootControlEnabled']; |
| 999 | delete bootSettingData['BootguardStatus']; |
| 1000 | delete bootSettingData['OptionsCleared']; |
| 1001 | delete bootSettingData['BIOSLastStatus']; |
| 1002 | delete bootSettingData['UefiBootParametersArray']; |
| 1003 | delete bootSettingData['RPEEnabled']; |
| 1004 | delete bootSettingData['RSEPassword'] |
| 1005 | |
| 1006 | // Ready boot parameters |
| 1007 | bootSettingData['BIOSSetup'] = ((action >= 11) && (action <= 14)); |
| 1008 | bootSettingData['UseSOL'] = ((action >= 13) && (action <= 14)); |
| 1009 | if ((action == 11) || (action == 13) || (action == 15)) { dev.powerAction = 2; } // Power on |
| 1010 | if ((action == 12) || (action == 14) || (action == 16)) { dev.powerAction = 10; } // Reset |
| 1011 | |
| 1012 | // Set boot parameters |
| 1013 | dev.amtstack.Put('AMT_BootSettingData', bootSettingData, function (stack, name, response, status, tag) { |
| 1014 | const dev = stack.dev; |
| 1015 | if ((obj.amtDevices[dev.nodeid] == null) || (status != 200)) return; // Device no longer exists or error |
| 1016 | // Set boot config |
| 1017 | dev.amtstack.SetBootConfigRole(1, function (stack, name, response, status, tag) { |
| 1018 | const dev = stack.dev; |
| 1019 | if ((obj.amtDevices[dev.nodeid] == null) || (status != 200)) return; // Device no longer exists or error |
| 1020 | // Set boot order |
| 1021 | var bootDevice = (action === 15 || action === 16) ? '<Address xmlns="http://schemas.xmlsoap.org/ws/2004/08/addressing">http://schemas.xmlsoap.org/ws/2004/08/addressing</Address><ReferenceParameters xmlns="http://schemas.xmlsoap.org/ws/2004/08/addressing"><ResourceURI xmlns="http://schemas.dmtf.org/wbem/wsman/1/wsman.xsd">http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2/CIM_BootSourceSetting</ResourceURI><SelectorSet xmlns="http://schemas.dmtf.org/wbem/wsman/1/wsman.xsd"><Selector Name="InstanceID">Intel(r) AMT: Force PXE Boot</Selector></SelectorSet></ReferenceParameters>' : null; |
| 1022 | dev.amtstack.CIM_BootConfigSetting_ChangeBootOrder(bootDevice, function (stack, name, response, status) { |
| 1023 | const dev = stack.dev; |
| 1024 | if ((obj.amtDevices[dev.nodeid] == null) || (status != 200)) return; // Device no longer exists or error |
| 1025 | // Perform power action |
| 1026 | try { dev.amtstack.RequestPowerStateChange(dev.powerAction, performPowerActionResponse); } catch (ex) { } |
| 1027 | }, 0, 1); |
| 1028 | }, 0, 1); |
| 1029 | }, 0, 1); |
| 1030 | } |
| 1031 | |
| 1032 | // Response to Intel AMT power action |
| 1033 | function performPowerActionResponse(stack, name, responses, status) { |
| 1034 | const dev = stack.dev; |
| 1035 | const action = dev.powerAction; |
| 1036 | delete dev.powerAction; |
| 1037 | if (obj.amtDevices[dev.nodeid] == null) return; // Device no longer exists, ignore this response. |
| 1038 | if (status != 200) return; |
| 1039 | |
| 1040 | // If this is Intel AMT 10 or higher and we are trying to wake the device, send an OS wake. |
| 1041 | // This will wake the device from "Modern Standby". |
| 1042 | if ((action == 2) && (dev.aquired.majorver > 9)) { |
| 1043 | try { dev.amtstack.RequestOSPowerStateChange(2, function (stack, name, response, status) { }); } catch (ex) { } |
| 1044 | } |
| 1045 | } |
| 1046 | |
| 1047 | |
| 1048 | // |
| 1049 | // Intel AMT One Click Recovery |
| 1050 | // |
| 1051 | |
| 1052 | // Perform Intel AMT One Click Recovery on a device |
| 1053 | function performOneClickRecoveryAction(nodeid, file) { |
| 1054 | var devices = obj.amtDevices[nodeid]; |
| 1055 | if (devices == null) return; |
| 1056 | for (var i in devices) { |
| 1057 | var dev = devices[i]; |
| 1058 | // If not LMS, has a AMT stack present and is in connected state, perform operation. |
| 1059 | if ((dev.connType != 2) && (dev.state == 1) && (dev.amtstack != null)) { |
| 1060 | // Make sure the MPS server root certificate is present. |
| 1061 | // Start by looking at existing certificates. |
| 1062 | dev.ocrfile = file; |
| 1063 | dev.amtstack.BatchEnum(null, ['AMT_PublicKeyCertificate', '*AMT_BootCapabilities'], performOneClickRecoveryActionEx); |
| 1064 | } |
| 1065 | } |
| 1066 | } |
| 1067 | |
| 1068 | // Response with list of certificates in Intel AMT |
| 1069 | function performOneClickRecoveryActionEx(stack, name, responses, status) { |
| 1070 | const dev = stack.dev; |
| 1071 | if (isAmtDeviceValid(dev) == false) return; // Device no longer exists, ignore this request. |
| 1072 | if (status != 200) { dev.consoleMsg("Failed to get security information (" + status + ")."); delete dev.ocrfile; return; } |
| 1073 | |
| 1074 | // Check if this Intel AMT device supports OCR |
| 1075 | if (responses['AMT_BootCapabilities'].response['ForceUEFIHTTPSBoot'] !== true) { |
| 1076 | dev.consoleMsg("This Intel AMT device does not support UEFI HTTPS boot (" + status + ")."); delete dev.ocrfile; return; |
| 1077 | } |
| 1078 | |
| 1079 | // Organize the certificates and add the MPS root cert if missing |
| 1080 | var xxCertificates = responses['AMT_PublicKeyCertificate'].responses; |
| 1081 | for (var i in xxCertificates) { |
| 1082 | xxCertificates[i].TrustedRootCertficate = (xxCertificates[i]['TrustedRootCertficate'] == true); |
| 1083 | xxCertificates[i].X509CertificateBin = Buffer.from(xxCertificates[i]['X509Certificate'], 'base64').toString('binary'); |
| 1084 | xxCertificates[i].XIssuer = parseCertName(xxCertificates[i]['Issuer']); |
| 1085 | xxCertificates[i].XSubject = parseCertName(xxCertificates[i]['Subject']); |
| 1086 | } |
| 1087 | dev.policy.certificates = xxCertificates; |
| 1088 | attemptRootCertSync(dev, performOneClickRecoveryActionEx2, true); |
| 1089 | } |
| 1090 | |
| 1091 | // MPS root certificate was added |
| 1092 | function performOneClickRecoveryActionEx2(dev) { |
| 1093 | // Ask for Boot Settings Data |
| 1094 | dev.amtstack.Get('AMT_BootSettingData', performOneClickRecoveryActionEx3, 0, 1); |
| 1095 | } |
| 1096 | |
| 1097 | // Getting Intel AMT Boot Settings Data |
| 1098 | function performOneClickRecoveryActionEx3(stack, name, response, status) { |
| 1099 | const dev = stack.dev; |
| 1100 | if (isAmtDeviceValid(dev) == false) return; // Device no longer exists, ignore this request. |
| 1101 | if (status != 200) { dev.consoleMsg("Failed to get boot settings data (" + status + ")."); delete dev.ocrfile; return; } |
| 1102 | |
| 1103 | // Generate the one-time URL. |
| 1104 | var cookie = obj.parent.encodeCookie({ a: 'f', f: dev.ocrfile }, obj.parent.loginCookieEncryptionKey) |
| 1105 | var url = 'https://' + parent.webserver.certificates.AmtMpsName + ':' + ((parent.args.mpsaliasport != null) ? parent.args.mpsaliasport : parent.args.mpsport) + '/c/' + cookie + '.efi'; |
| 1106 | delete dev.ocrfile; |
| 1107 | |
| 1108 | // Generate the boot data for OCR with URL |
| 1109 | var r = response.Body; |
| 1110 | r['BIOSPause'] = false; |
| 1111 | r['BIOSSetup'] = false; |
| 1112 | r['EnforceSecureBoot'] = false; |
| 1113 | r['UefiBootParametersArray'] = Buffer.from(makeUefiBootParam(1, url) + makeUefiBootParam(20, 1, 1) + makeUefiBootParam(30, 0, 2), 'binary').toString('base64'); |
| 1114 | r['UefiBootNumberOfParams'] = 3; |
| 1115 | r['BootMediaIndex'] = 0; // Do not use boot media index for One Click Recovery (OCR) |
| 1116 | |
| 1117 | // Set the boot order to null, this is needed for some Intel AMT versions that don't clear this automatically. |
| 1118 | dev.amtstack.CIM_BootConfigSetting_ChangeBootOrder(null, function (stack, name, response, status) { |
| 1119 | if (isAmtDeviceValid(dev) == false) return; // Device no longer exists, ignore this request. |
| 1120 | if (status != 200) { dev.consoleMsg("Failed to set boot order (" + status + ")."); return; } |
| 1121 | dev.amtstack.Put('AMT_BootSettingData', r, performOneClickRecoveryActionEx4, 0, 1); |
| 1122 | }, 0, 1); |
| 1123 | } |
| 1124 | |
| 1125 | // Intel AMT Put Boot Settings |
| 1126 | function performOneClickRecoveryActionEx4(stack, name, response, status) { |
| 1127 | const dev = stack.dev; |
| 1128 | if (isAmtDeviceValid(dev) == false) return; // Device no longer exists, ignore this request. |
| 1129 | if (status != 200) { dev.consoleMsg("Failed to set boot settings data (" + status + ")."); return; } |
| 1130 | dev.amtstack.SetBootConfigRole(1, function (stack, name, response, status) { |
| 1131 | if (isAmtDeviceValid(dev) == false) return; // Device no longer exists, ignore this request. |
| 1132 | if (status != 200) { dev.consoleMsg("Failed to set boot config role (" + status + ")."); return; } |
| 1133 | dev.amtstack.CIM_BootConfigSetting_ChangeBootOrder('<Address xmlns="http://schemas.xmlsoap.org/ws/2004/08/addressing">http://schemas.xmlsoap.org/ws/2004/08/addressing</Address><ReferenceParameters xmlns="http://schemas.xmlsoap.org/ws/2004/08/addressing"><ResourceURI xmlns="http://schemas.dmtf.org/wbem/wsman/1/wsman.xsd">http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2/CIM_BootSourceSetting</ResourceURI><SelectorSet xmlns="http://schemas.dmtf.org/wbem/wsman/1/wsman.xsd"><Selector Name="InstanceID">Intel(r) AMT: Force OCR UEFI HTTPS Boot</Selector></SelectorSet></ReferenceParameters>', function (stack, name, response, status) { |
| 1134 | if (isAmtDeviceValid(dev) == false) return; // Device no longer exists, ignore this request. |
| 1135 | if (status != 200) { dev.consoleMsg("Failed to set boot config (" + status + ")."); return; } |
| 1136 | dev.amtstack.RequestPowerStateChange(10, function (stack, name, response, status) { // 10 = Reset, 2 = Power Up |
| 1137 | if (isAmtDeviceValid(dev) == false) return; // Device no longer exists, ignore this request. |
| 1138 | if (status != 200) { dev.consoleMsg("Failed to perform power action (" + status + ")."); return; } |
| 1139 | console.log('One Click Recovery Completed.'); |
| 1140 | }); |
| 1141 | }); |
| 1142 | }, 0, 1); |
| 1143 | } |
| 1144 | |
| 1145 | |
| 1146 | // |
| 1147 | // Intel AMT Clock Syncronization |
| 1148 | // |
| 1149 | |
| 1150 | // Attempt to sync the Intel AMT clock if needed, call func back when done. |
| 1151 | // Care should be take not to have many pending WSMAN called when performing clock sync. |
| 1152 | function attemptSyncClock(dev, func) { |
| 1153 | if (isAmtDeviceValid(dev) == false) return; // Device no longer exists, ignore this request. |
| 1154 | if (dev.policy.amtPolicy == 0) { func(dev); return; } // If there is no Intel AMT policy, skip this operation. |
| 1155 | dev.taskCount = 1; |
| 1156 | dev.taskCompleted = func; |
| 1157 | dev.amtstack.AMT_TimeSynchronizationService_GetLowAccuracyTimeSynch(attemptSyncClockEx); |
| 1158 | } |
| 1159 | |
| 1160 | // Intel AMT clock query response |
| 1161 | function attemptSyncClockEx(stack, name, response, status) { |
| 1162 | const dev = stack.dev; |
| 1163 | if (isAmtDeviceValid(dev) == false) return; // Device no longer exists, ignore this request. |
| 1164 | if (status != 200) { dev.consoleMsg("Failed to get clock (" + status + ")."); removeAmtDevice(dev, 17); return; } |
| 1165 | |
| 1166 | // Compute how much drift between Intel AMT and our clock. |
| 1167 | var t = new Date(), now = new Date(); |
| 1168 | t.setTime(response.Body['Ta0'] * 1000); |
| 1169 | if (Math.abs(t - now) > 10000) { // If the Intel AMT clock is more than 10 seconds off, set it. |
| 1170 | dev.consoleMsg("Performing clock sync."); |
| 1171 | var Tm1 = Math.round(now.getTime() / 1000); |
| 1172 | dev.amtstack.AMT_TimeSynchronizationService_SetHighAccuracyTimeSynch(response.Body['Ta0'], Tm1, Tm1, attemptSyncClockSet); |
| 1173 | } else { |
| 1174 | // Clock is fine, we are done. |
| 1175 | devTaskCompleted(dev) |
| 1176 | } |
| 1177 | } |
| 1178 | |
| 1179 | // Intel AMT clock set response |
| 1180 | function attemptSyncClockSet(stack, name, responses, status) { |
| 1181 | const dev = stack.dev; |
| 1182 | if (isAmtDeviceValid(dev) == false) return; // Device no longer exists, ignore this request. |
| 1183 | if (status != 200) { dev.consoleMsg("Failed to sync clock (" + status + ")."); removeAmtDevice(dev, 18); } |
| 1184 | devTaskCompleted(dev) |
| 1185 | } |
| 1186 | |
| 1187 | |
| 1188 | // |
| 1189 | // Intel AMT TLS setup |
| 1190 | // |
| 1191 | |
| 1192 | // Check if Intel AMT TLS state is correct |
| 1193 | function attemptTlsSync(dev, func) { |
| 1194 | if (isAmtDeviceValid(dev) == false) return; // Device no longer exists, ignore this request. |
| 1195 | if (dev.policy.amtPolicy == 0) { func(dev); return; } // If there is no Intel AMT policy, skip this operation. |
| 1196 | dev.taskCount = 1; |
| 1197 | dev.taskCompleted = func; |
| 1198 | // TODO: We only deal with certificates starting with Intel AMT 6 and beyond |
| 1199 | dev.amtstack.BatchEnum(null, ['AMT_PublicKeyCertificate', 'AMT_PublicPrivateKeyPair', 'AMT_TLSSettingData', 'AMT_TLSCredentialContext'], attemptTlsSyncEx); |
| 1200 | } |
| 1201 | |
| 1202 | // Intel AMT is not always in a good spot to generate a key pair. This will retry at 10 second interval. |
| 1203 | function generateKeyPairWithRetry(dev, func) { |
| 1204 | if (isAmtDeviceValid(dev) == false) return; |
| 1205 | if (dev.keyPairAttempts == null) { dev.keyPairAttempts = 1; } else { dev.keyPairAttempts++; } |
| 1206 | dev.amtstack.AMT_PublicKeyManagementService_GenerateKeyPair(0, 2048, function (stack, name, responses, status) { |
| 1207 | if (isAmtDeviceValid(dev) == false) { delete dev.keyPairAttempts; return; } |
| 1208 | if ((status == 200) || (dev.keyPairAttempts > 19)) { |
| 1209 | delete dev.keyPairAttempts; |
| 1210 | func(stack, name, responses, status); |
| 1211 | } else { |
| 1212 | if ((responses.Body != null) && (responses.Body.ReturnValue != null) && (responses.Body.ReturnValueStr != null)) { |
| 1213 | dev.consoleMsg("Failed to generate a key pair (" + status + ", " + responses.Body.ReturnValue + ", \"" + responses.Body.ReturnValueStr + "\"), attempt " + dev.keyPairAttempts + ", trying again in 10 seconds..."); |
| 1214 | } else { |
| 1215 | dev.consoleMsg("Failed to generate a key pair (" + status + "), attempt " + dev.keyPairAttempts + ", trying again in 10 seconds..."); |
| 1216 | } |
| 1217 | |
| 1218 | // Wait 10 seconds before attempting again |
| 1219 | var f = function doManage() { generateKeyPairWithRetry(doManage.dev, doManage.func); } |
| 1220 | f.dev = dev; |
| 1221 | f.func = func; |
| 1222 | setTimeout(f, 10000); |
| 1223 | } |
| 1224 | }); |
| 1225 | } |
| 1226 | |
| 1227 | function attemptTlsSyncEx(stack, name, responses, status) { |
| 1228 | const dev = stack.dev; |
| 1229 | if (isAmtDeviceValid(dev) == false) return; // Device no longer exists, ignore this request. |
| 1230 | if (status != 200) { dev.consoleMsg("Failed to get security information (" + status + ")."); removeAmtDevice(dev, 19); return; } |
| 1231 | |
| 1232 | // Setup the certificates |
| 1233 | dev.policy.certPrivateKeys = responses['AMT_PublicPrivateKeyPair'].responses; |
| 1234 | dev.policy.tlsSettings = responses['AMT_TLSSettingData'].responses; |
| 1235 | dev.policy.tlsCredentialContext = responses['AMT_TLSCredentialContext'].responses; |
| 1236 | var xxCertificates = responses['AMT_PublicKeyCertificate'].responses; |
| 1237 | for (var i in xxCertificates) { |
| 1238 | xxCertificates[i].TrustedRootCertficate = (xxCertificates[i]['TrustedRootCertficate'] == true); |
| 1239 | xxCertificates[i].X509CertificateBin = Buffer.from(xxCertificates[i]['X509Certificate'], 'base64').toString('binary'); |
| 1240 | xxCertificates[i].XIssuer = parseCertName(xxCertificates[i]['Issuer']); |
| 1241 | xxCertificates[i].XSubject = parseCertName(xxCertificates[i]['Subject']); |
| 1242 | } |
| 1243 | amtcert_linkCertPrivateKey(xxCertificates, dev.policy.certPrivateKeys); |
| 1244 | dev.policy.certificates = xxCertificates; |
| 1245 | |
| 1246 | // Find the current TLS certificate & MeshCentral root certificate |
| 1247 | var xxTlsCurrentCert = null; |
| 1248 | if (dev.policy.tlsCredentialContext.length > 0) { |
| 1249 | var certInstanceId = dev.policy.tlsCredentialContext[0]['ElementInContext']['ReferenceParameters']['SelectorSet']['Selector']['Value']; |
| 1250 | for (var i in dev.policy.certificates) { if (dev.policy.certificates[i]['InstanceID'] == certInstanceId) { xxTlsCurrentCert = i; } } |
| 1251 | } |
| 1252 | |
| 1253 | // This is a managed device and TLS is not enabled, turn it on. |
| 1254 | if (xxTlsCurrentCert == null) { |
| 1255 | // Start by generating a key pair |
| 1256 | generateKeyPairWithRetry(dev, function (stack, name, responses, status) { |
| 1257 | const dev = stack.dev; |
| 1258 | if (isAmtDeviceValid(dev) == false) return; // Device no longer exists, ignore this request. |
| 1259 | if (status != 200) { dev.consoleMsg("Failed to generate a key pair (" + status + ")."); removeAmtDevice(dev, 20); return; } |
| 1260 | |
| 1261 | // Check that we get a key pair reference |
| 1262 | var x = null; |
| 1263 | try { x = responses.Body['KeyPair']['ReferenceParameters']['SelectorSet']['Selector']['Value']; } catch (ex) { } |
| 1264 | if (x == null) { dev.consoleMsg("Unable to get key pair reference."); removeAmtDevice(dev, 21); return; } |
| 1265 | |
| 1266 | // Get the new key pair |
| 1267 | dev.amtstack.Enum('AMT_PublicPrivateKeyPair', function (stack, name, responses, status, tag) { |
| 1268 | const dev = stack.dev; |
| 1269 | if (isAmtDeviceValid(dev) == false) return; // Device no longer exists, ignore this request. |
| 1270 | if (status != 200) { dev.consoleMsg("Failed to get a key pair list (" + status + ")."); removeAmtDevice(dev, 22); return; } |
| 1271 | |
| 1272 | // Get the new DER key |
| 1273 | var DERKey = null; |
| 1274 | for (var i in responses) { if (responses[i]['InstanceID'] == tag) { DERKey = responses[i]['DERKey']; } } |
| 1275 | |
| 1276 | // Get certificate values |
| 1277 | const commonName = 'IntelAMT-' + Buffer.from(parent.crypto.randomBytes(6), 'binary').toString('hex'); |
| 1278 | const domain = parent.config.domains[dev.domainid]; |
| 1279 | var serverName = 'MeshCentral'; |
| 1280 | if ((domain != null) && (domain.title != null)) { serverName = domain.title; } |
| 1281 | const certattributes = { 'CN': commonName, 'O': serverName, 'ST': 'MC', 'C': 'MC' }; |
| 1282 | |
| 1283 | // See what root certificate to use to sign the TLS cert |
| 1284 | var xxCaPrivateKey = obj.parent.certificates.root.key; // Use our own root by default |
| 1285 | var issuerattributes = { 'CN': obj.rootCertCN }; |
| 1286 | if (domain.amtmanager.tlsrootcert2 != null) { |
| 1287 | xxCaPrivateKey = domain.amtmanager.tlsrootcert2.key; |
| 1288 | issuerattributes = domain.amtmanager.tlsrootcert2.attributes; |
| 1289 | // TODO: We should change the start and end dates of our issued certificate to at least match the root. |
| 1290 | // TODO: We could do one better and auto-renew TLS certificates as needed. |
| 1291 | } |
| 1292 | |
| 1293 | // Set the extended key usages |
| 1294 | var extKeyUsage = { name: 'extKeyUsage', serverAuth: true, clientAuth: true } |
| 1295 | |
| 1296 | // Sign the key pair using the CA certifiate |
| 1297 | const cert = obj.amtcert_createCertificate(certattributes, xxCaPrivateKey, DERKey, issuerattributes, extKeyUsage); |
| 1298 | if (cert == null) { dev.consoleMsg("Failed to sign the TLS certificate."); removeAmtDevice(dev, 23); return; } |
| 1299 | |
| 1300 | // Place the resulting signed certificate back into AMT |
| 1301 | var pem = obj.parent.certificateOperations.forge.pki.certificateToPem(cert).replace(/(\r\n|\n|\r)/gm, ''); |
| 1302 | |
| 1303 | // Set the certificate finderprint (SHA1) |
| 1304 | var md = obj.parent.certificateOperations.forge.md.sha1.create(); |
| 1305 | md.update(obj.parent.certificateOperations.forge.asn1.toDer(obj.parent.certificateOperations.forge.pki.certificateToAsn1(cert)).getBytes()); |
| 1306 | dev.aquired.xhash = md.digest().toHex(); |
| 1307 | |
| 1308 | dev.amtstack.AMT_PublicKeyManagementService_AddCertificate(pem.substring(27, pem.length - 25), function (stack, name, responses, status) { |
| 1309 | const dev = stack.dev; |
| 1310 | if (isAmtDeviceValid(dev) == false) return; // Device no longer exists, ignore this request. |
| 1311 | if (status != 200) { dev.consoleMsg("Failed to add TLS certificate (" + status + ")."); removeAmtDevice(dev, 24); return; } |
| 1312 | var certInstanceId = null; |
| 1313 | try { certInstanceId = responses.Body['CreatedCertificate']['ReferenceParameters']['SelectorSet']['Selector']['Value']; } catch (ex) { } |
| 1314 | if (certInstanceId == null) { dev.consoleMsg("Failed to get TLS certificate identifier."); removeAmtDevice(dev, 25); return; } |
| 1315 | |
| 1316 | // Set the TLS certificate |
| 1317 | if (dev.hbacmtls == 1) { |
| 1318 | dev.setTlsSecurityPendingCalls = 2; // Set remote port only |
| 1319 | } else { |
| 1320 | dev.setTlsSecurityPendingCalls = 3; // Set local and remote port |
| 1321 | } |
| 1322 | if (dev.policy.tlsCredentialContext.length > 0) { |
| 1323 | // Modify the current context |
| 1324 | var newTLSCredentialContext = Clone(dev.policy.tlsCredentialContext[0]); |
| 1325 | newTLSCredentialContext['ElementInContext']['ReferenceParameters']['SelectorSet']['Selector']['Value'] = certInstanceId; |
| 1326 | dev.amtstack.Put('AMT_TLSCredentialContext', newTLSCredentialContext, amtSwitchToTls, 0, 1); |
| 1327 | } else { |
| 1328 | // Add a new security context |
| 1329 | dev.amtstack.Create('AMT_TLSCredentialContext', { |
| 1330 | 'ElementInContext': '<a:Address>/wsman</a:Address><a:ReferenceParameters><w:ResourceURI>' + dev.amtstack.CompleteName('AMT_PublicKeyCertificate') + '</w:ResourceURI><w:SelectorSet><w:Selector Name="InstanceID">' + certInstanceId + '</w:Selector></w:SelectorSet></a:ReferenceParameters>', |
| 1331 | 'ElementProvidingContext': '<a:Address>/wsman</a:Address><a:ReferenceParameters><w:ResourceURI>' + dev.amtstack.CompleteName('AMT_TLSProtocolEndpointCollection') + '</w:ResourceURI><w:SelectorSet><w:Selector Name="ElementName">TLSProtocolEndpointInstances Collection</w:Selector></w:SelectorSet></a:ReferenceParameters>' |
| 1332 | }, amtSwitchToTls); |
| 1333 | } |
| 1334 | |
| 1335 | // Figure out what index is local & remote |
| 1336 | var localNdx = ((dev.policy != null) && (dev.policy.tlsSettings != null) && (dev.policy.tlsSettings[0] != null) && (dev.policy.tlsSettings[0]['InstanceID'] == 'Intel(r) AMT LMS TLS Settings')) ? 0 : 1, remoteNdx = (1 - localNdx); |
| 1337 | |
| 1338 | // Remote TLS settings |
| 1339 | var xxTlsSettings2 = Clone(dev.policy.tlsSettings); |
| 1340 | xxTlsSettings2[remoteNdx]['Enabled'] = true; |
| 1341 | xxTlsSettings2[remoteNdx]['MutualAuthentication'] = false; |
| 1342 | xxTlsSettings2[remoteNdx]['AcceptNonSecureConnections'] = true; |
| 1343 | delete xxTlsSettings2[remoteNdx]['TrustedCN']; |
| 1344 | |
| 1345 | // Local TLS settings |
| 1346 | xxTlsSettings2[localNdx]['Enabled'] = true; |
| 1347 | delete xxTlsSettings2[localNdx]['TrustedCN']; |
| 1348 | |
| 1349 | if (dev.hbacmtls == 1) { |
| 1350 | // If we are doing Host-based TLS ACM activation, you need to only enable the remote port with TLS. |
| 1351 | // If you enable on local port, the commit will succeed but be ignored. |
| 1352 | dev.consoleMsg("Enabling TLS on remote port..."); |
| 1353 | if (remoteNdx == 0) { dev.amtstack.Put('AMT_TLSSettingData', xxTlsSettings2[0], amtSwitchToTls, 0, 1, xxTlsSettings2[0]); } |
| 1354 | else { dev.amtstack.Put('AMT_TLSSettingData', xxTlsSettings2[1], amtSwitchToTls, 0, 1, xxTlsSettings2[1]); } |
| 1355 | delete dev.hbacmtls; // Remove this indication |
| 1356 | } else { |
| 1357 | // Update TLS settings |
| 1358 | dev.amtstack.Put('AMT_TLSSettingData', xxTlsSettings2[0], amtSwitchToTls, 0, 1, xxTlsSettings2[0]); |
| 1359 | dev.amtstack.Put('AMT_TLSSettingData', xxTlsSettings2[1], amtSwitchToTls, 0, 1, xxTlsSettings2[1]); |
| 1360 | } |
| 1361 | }); |
| 1362 | |
| 1363 | }, responses.Body['KeyPair']['ReferenceParameters']['SelectorSet']['Selector']['Value']); |
| 1364 | }); |
| 1365 | } else { |
| 1366 | // Update device in the database |
| 1367 | dev.intelamt.tls = dev.aquired.tls = 1; |
| 1368 | UpdateDevice(dev); |
| 1369 | |
| 1370 | // TLS is setup |
| 1371 | devTaskCompleted(dev); |
| 1372 | } |
| 1373 | } |
| 1374 | |
| 1375 | function amtSwitchToTls(stack, name, responses, status) { |
| 1376 | const dev = stack.dev; |
| 1377 | if (isAmtDeviceValid(dev) == false) return; // Device no longer exists, ignore this request. |
| 1378 | if (status != 200) { dev.consoleMsg("Failed setup TLS (" + status + ")."); removeAmtDevice(dev, 26); return; } |
| 1379 | |
| 1380 | // Check if all the calls are done & perform a commit |
| 1381 | if ((--dev.setTlsSecurityPendingCalls) == 0) { |
| 1382 | dev.consoleMsg("Performing Commit..."); |
| 1383 | dev.amtstack.AMT_SetupAndConfigurationService_CommitChanges(null, function (stack, name, responses, status) { |
| 1384 | const dev = stack.dev; |
| 1385 | if (isAmtDeviceValid(dev) == false) return; // Device no longer exists, ignore this request. |
| 1386 | if (status != 200) { dev.consoleMsg("Failed perform commit (" + status + ")."); removeAmtDevice(dev, 27); return; } |
| 1387 | dev.consoleMsg("Enabled TLS, holding 10 seconds..."); |
| 1388 | |
| 1389 | // Update device in the database |
| 1390 | dev.intelamt.tls = dev.aquired.tls = 1; |
| 1391 | dev.intelamt.hash = dev.aquired.hash = dev.aquired.xhash; |
| 1392 | delete dev.aquired.xhash; |
| 1393 | UpdateDevice(dev); |
| 1394 | |
| 1395 | // Switch our communications to TLS (Restart our management of this node) |
| 1396 | dev.switchToTls = 1; |
| 1397 | delete dev.tlsfail; |
| 1398 | |
| 1399 | // Wait 5 seconds before attempting to manage this device some more |
| 1400 | var f = function doManage() { if (isAmtDeviceValid(dev)) { devTaskCompleted(doManage.dev); } } |
| 1401 | f.dev = dev; |
| 1402 | setTimeout(f, 10000); |
| 1403 | }); |
| 1404 | } |
| 1405 | } |
| 1406 | |
| 1407 | |
| 1408 | // |
| 1409 | // Intel AMT WIFI & Wired 802.1x |
| 1410 | // |
| 1411 | |
| 1412 | // Check which key pair matches the public key in the certificate |
| 1413 | function amtcert_linkCertPrivateKey(certs, keys) { |
| 1414 | for (var i in certs) { |
| 1415 | var cert = certs[i]; |
| 1416 | try { |
| 1417 | if (keys.length == 0) return; |
| 1418 | var publicKeyPEM = forge.pki.publicKeyToPem(forge.pki.certificateFromAsn1(forge.asn1.fromDer(cert.X509Certificate)).publicKey).substring(28 + 32).replace(/(\r\n|\n|\r)/gm, ""); |
| 1419 | for (var j = 0; j < keys.length; j++) { |
| 1420 | if (publicKeyPEM === (keys[j]['DERKey'] + '-----END PUBLIC KEY-----')) { |
| 1421 | keys[j].XCert = cert; // Link the key pair to the certificate |
| 1422 | cert.XPrivateKey = keys[j]; // Link the certificate to the key pair |
| 1423 | } |
| 1424 | } |
| 1425 | } catch (e) { console.log(e); } |
| 1426 | } |
| 1427 | } |
| 1428 | |
| 1429 | // This method will sync the WIFI profiles from the device and the server, but does not care about profile priority. |
| 1430 | // We also sync wired 802.1x at the same time since we only allow a single 802.1x profile per device shared between wired and wireless |
| 1431 | // We may want to work on an alternate version that does do priority if requested. |
| 1432 | function attemptWifiSync(dev, func) { |
| 1433 | if (isAmtDeviceValid(dev) == false) return; // Device no longer exists, ignore this request. |
| 1434 | if (dev.policy.amtPolicy == 0) { func(dev); return; } // If there is no Intel AMT policy, skip this operation. |
| 1435 | if (dev.connType != 2) { func(dev); return; } // Only configure wireless over a CIRA-LMS link |
| 1436 | //if (parent.config.domains[dev.domainid].amtmanager.wifiprofiles == null) { func(dev); return; } // No server WIFI profiles set, skip this. |
| 1437 | //if ((dev.mpsConnection.tag.meiState == null) || (dev.mpsConnection.tag.meiState.net1 == null)) { func(dev); return; } // No WIFI on this device, skip this. |
| 1438 | |
| 1439 | // Get the current list of WIFI profiles, wireless interface state and wired 802.1x profile |
| 1440 | dev.taskCount = 1; |
| 1441 | dev.taskCompleted = func; |
| 1442 | |
| 1443 | const objQuery = ['CIM_WiFiEndpointSettings', '*CIM_WiFiPort', '*AMT_WiFiPortConfigurationService', 'CIM_IEEE8021xSettings', 'AMT_PublicKeyCertificate', 'AMT_PublicPrivateKeyPair']; |
| 1444 | if (parent.config.domains[dev.domainid].amtmanager['802.1x'] != null) { objQuery.push('*AMT_8021XProfile'); } |
| 1445 | dev.amtstack.BatchEnum(null, objQuery, function (stack, name, responses, status) { |
| 1446 | const dev = stack.dev; |
| 1447 | if (isAmtDeviceValid(dev) == false) return; // Device no longer exists, ignore this request. |
| 1448 | const domain = parent.config.domains[dev.domainid]; |
| 1449 | if ((responses['AMT_PublicKeyCertificate'] == null) || (responses['AMT_PublicKeyCertificate'].status != 200) || (responses['AMT_PublicPrivateKeyPair'] == null) || (responses['AMT_PublicPrivateKeyPair'].status != 200)) { devTaskCompleted(dev); return; } // We can't get the certificate list, fail and carry on. |
| 1450 | |
| 1451 | // See if we need to perform wired or wireless 802.1x configuration |
| 1452 | var wiredConfig = ((parent.config.domains[dev.domainid].amtmanager['802.1x'] != null) && (responses['AMT_8021XProfile'] != null) && (responses['AMT_8021XProfile'].status == 200)); |
| 1453 | const wirelessConfig = ((responses['CIM_WiFiEndpointSettings'] != null) && (responses['CIM_WiFiEndpointSettings'].status == 200) && (responses['AMT_WiFiPortConfigurationService'] != null) && (responses['AMT_WiFiPortConfigurationService'].status == 200) && (responses['CIM_WiFiPort'] != null) && (responses['CIM_WiFiPort'].status == 200) && (responses['CIM_IEEE8021xSettings'] != null) && (responses['CIM_IEEE8021xSettings'].status == 200)); |
| 1454 | if (!wiredConfig && !wirelessConfig) { devTaskCompleted(dev); return; } // We can't get wired or wireless settings, ignore and carry on. |
| 1455 | |
| 1456 | // Sort out the certificates |
| 1457 | var xxCertificates = responses['AMT_PublicKeyCertificate'].responses; |
| 1458 | var xxCertPrivateKeys = responses['AMT_PublicPrivateKeyPair'].responses; |
| 1459 | for (var i in xxCertificates) { |
| 1460 | xxCertificates[i].TrustedRootCertficate = (xxCertificates[i]['TrustedRootCertficate'] == true); |
| 1461 | xxCertificates[i].X509CertificateBin = Buffer.from(xxCertificates[i]['X509Certificate'], 'base64').toString('binary'); |
| 1462 | xxCertificates[i].XIssuer = parseCertName(xxCertificates[i]['Issuer']); |
| 1463 | xxCertificates[i].XSubject = parseCertName(xxCertificates[i]['Subject']); |
| 1464 | } |
| 1465 | amtcert_linkCertPrivateKey(xxCertificates, xxCertPrivateKeys); // This links all certificates and private keys |
| 1466 | |
| 1467 | // Remove any unlinked private keys |
| 1468 | for (var i in xxCertPrivateKeys) { |
| 1469 | if (!xxCertPrivateKeys[i].XCert) { |
| 1470 | dev.amtstack.Delete('AMT_PublicPrivateKeyPair', { 'InstanceID': xxCertPrivateKeys[i]['InstanceID'] }, function (stack, name, response, status) { |
| 1471 | //if (status == 200) { dev.consoleMsg("Removed unassigned private key pair."); } |
| 1472 | }); |
| 1473 | } |
| 1474 | } |
| 1475 | |
| 1476 | // Check if wired 802.1x needs updating |
| 1477 | var newNetAuthProfileRequested = false; |
| 1478 | var srvNetAuthProfile = domain.amtmanager['802.1x']; |
| 1479 | var devNetAuthProfile = null; |
| 1480 | var netAuthClientCertInstanceId = null; |
| 1481 | |
| 1482 | if (wiredConfig) { |
| 1483 | var wiredMatch = 0; |
| 1484 | devNetAuthProfile = responses['AMT_8021XProfile'].response; |
| 1485 | if ((srvNetAuthProfile === false) && (devNetAuthProfile != null)) { |
| 1486 | // Remove the 802.1x profile |
| 1487 | wiredMatch = 1; |
| 1488 | } else if ((srvNetAuthProfile != null) && (devNetAuthProfile == null)) { |
| 1489 | // Device has no 802.1x, add it |
| 1490 | wiredMatch = 2; |
| 1491 | } else if ((typeof srvNetAuthProfile == 'object') && (devNetAuthProfile != null)) { |
| 1492 | // Check if the existing 802.1x profile look good |
| 1493 | if (devNetAuthProfile.AuthenticationProtocol != srvNetAuthProfile.authenticationprotocol) { wiredMatch = 2; } |
| 1494 | if (devNetAuthProfile.ServerCertificateName != srvNetAuthProfile.servercertificatename) { wiredMatch = 2; } |
| 1495 | if (devNetAuthProfile.ServerCertificateNameComparison != srvNetAuthProfile.servercertificatenamecomparison) { wiredMatch = 2; } |
| 1496 | if (devNetAuthProfile.ActiveInS0 != srvNetAuthProfile.availableins0) { wiredMatch = 2; } |
| 1497 | if (typeof srvNetAuthProfile.satellitecredentials != 'string') { |
| 1498 | // Credentials for this profile are in the config file |
| 1499 | if (devNetAuthProfile.RoamingIdentity != srvNetAuthProfile.roamingidentity) { wiredMatch = 2; } |
| 1500 | if (devNetAuthProfile.Username != srvNetAuthProfile.username) { wiredMatch = 2; } |
| 1501 | if (devNetAuthProfile.Domain != srvNetAuthProfile.domain) { wiredMatch = 2; } |
| 1502 | } |
| 1503 | // If the existing 802.1x profile has a certificate, remember the client certificate instance id for later checking |
| 1504 | if (devNetAuthProfile.ClientCertificate) { netAuthClientCertInstanceId = devNetAuthProfile.ClientCertificate.ReferenceParameters.SelectorSet.Selector.Value; } |
| 1505 | } |
| 1506 | if (wiredMatch == 2) { newNetAuthProfileRequested = true; } |
| 1507 | } |
| 1508 | |
| 1509 | if (wirelessConfig) { |
| 1510 | // If we have server WIFI profiles to sync, do this now. |
| 1511 | if (parent.config.domains[dev.domainid].amtmanager.wifiprofiles != null) { |
| 1512 | // The server and device WIFI profiles, find profiles to add and remove |
| 1513 | const sevProfiles = parent.config.domains[dev.domainid].amtmanager.wifiprofiles; |
| 1514 | const devProfiles = responses['CIM_WiFiEndpointSettings'].responses; |
| 1515 | const netAuthProfiles = responses['CIM_IEEE8021xSettings'].responses; |
| 1516 | var profilesToAdd = [], profilesToRemove = []; |
| 1517 | var profilesToAdd2 = [], profilesToRemove2 = []; |
| 1518 | |
| 1519 | // Look at the WIFI profiles in the device |
| 1520 | for (var i in sevProfiles) { |
| 1521 | var sevProfile = sevProfiles[i], wirelessMatch = false; |
| 1522 | for (var j in devProfiles) { |
| 1523 | var devProfile = devProfiles[j]; |
| 1524 | if ( |
| 1525 | (devProfile.ElementName == sevProfile.name) && |
| 1526 | (devProfile.SSID == sevProfile.ssid) && |
| 1527 | (devProfile.AuthenticationMethod == sevProfile.authentication) && |
| 1528 | (devProfile.EncryptionMethod == sevProfile.encryption) && |
| 1529 | (devProfile.BSSType == sevProfile.type) |
| 1530 | ) { |
| 1531 | if (([5, 7, 32768, 32769].indexOf(sevProfile.authentication)) >= 0) { |
| 1532 | // This is a 802.1x profile, do some extra matching. |
| 1533 | // Start by finding the 802.1x profile for this WIFI profile |
| 1534 | var netAuthProfile = null, netAuthMatch = false; |
| 1535 | for (var k in netAuthProfiles) { if (netAuthProfiles[k].ElementName == devProfile.ElementName) { netAuthProfile = netAuthProfiles[k]; } } |
| 1536 | if (netAuthProfile != null) { |
| 1537 | netAuthMatch = true; |
| 1538 | if (srvNetAuthProfile.authenticationprotocol != netAuthProfile['AuthenticationProtocol']) { netAuthMatch = false; } |
| 1539 | if (srvNetAuthProfile.roamingidentity != netAuthProfile['RoamingIdentity']) { netAuthMatch = false; } |
| 1540 | if (srvNetAuthProfile.servercertificatename != netAuthProfile['ServerCertificateName']) { netAuthMatch = false; } |
| 1541 | if (srvNetAuthProfile.servercertificatenamecomparison != netAuthProfile['ServerCertificateNameComparison']) { netAuthMatch = false; } |
| 1542 | if (typeof srvNetAuthProfile.satellitecredentials != 'string') { |
| 1543 | // Credentials for this profile are in the config file |
| 1544 | if (srvNetAuthProfile.username != netAuthProfile['Username']) { netAuthMatch = false; } |
| 1545 | if (srvNetAuthProfile.domain != netAuthProfile['Domain']) { netAuthMatch = false; } |
| 1546 | } |
| 1547 | } |
| 1548 | |
| 1549 | // TODO: If the existing 802.1x profile has a certificate, remember the client certificate instance id for later checking |
| 1550 | |
| 1551 | if (netAuthMatch == true) { |
| 1552 | // The 802.1x profile seems to match what we want, keep it. |
| 1553 | wirelessMatch = true; |
| 1554 | devProfile.match = true; |
| 1555 | } |
| 1556 | } else { |
| 1557 | // Not a 802.1x profile, looks fine, keep it. |
| 1558 | wirelessMatch = true; |
| 1559 | devProfile.match = true; |
| 1560 | } |
| 1561 | } |
| 1562 | } |
| 1563 | if (wirelessMatch == false) { profilesToAdd.push(sevProfile); } // Add non-matching profile |
| 1564 | if ((wirelessMatch == false) || (([5, 7, 32768, 32769].indexOf(sevProfile.authentication)) >= 0)) { profilesToAdd2.push(sevProfile); } // Add non-matching profile or 802.1x profile |
| 1565 | } |
| 1566 | for (var j in devProfiles) { |
| 1567 | var devProfile = devProfiles[j]; |
| 1568 | if (devProfile.InstanceID != null) { |
| 1569 | if (devProfile.match !== true) { profilesToRemove.push(devProfile); } // Missing profile to remove |
| 1570 | if ((devProfile.match !== true) || (([5, 7, 32768, 32769].indexOf(devProfile.AuthenticationMethod)) >= 0)) { profilesToRemove2.push(devProfile); } // Missing profile to remove or 802.1x profile |
| 1571 | } |
| 1572 | } |
| 1573 | |
| 1574 | // Compute what priorities are allowed |
| 1575 | var prioritiesInUse = []; |
| 1576 | for (var j in devProfiles) { if (devProfiles[j].match == true) { prioritiesInUse.push(devProfiles[j].Priority); } } |
| 1577 | |
| 1578 | // Check if any other WIFI profiles require a 802.1x request to MeshCentral Satellite |
| 1579 | if (dev.netAuthCredentials == null) { |
| 1580 | for (var i in profilesToAdd) { if (([5, 7, 32768, 32769].indexOf(profilesToAdd[i].authentication)) >= 0) { newNetAuthProfileRequested = true; } } |
| 1581 | } |
| 1582 | |
| 1583 | // If we need to request a new 802.1x profile, remove all existing 802.1x WIFI profiles and re-add later. |
| 1584 | if (newNetAuthProfileRequested) { |
| 1585 | profilesToAdd = profilesToAdd2; // Just use the second list we built for this purpose. |
| 1586 | profilesToRemove = profilesToRemove2; |
| 1587 | } |
| 1588 | |
| 1589 | // Notify of WIFI profile changes |
| 1590 | if ((profilesToAdd.length > 0) || (profilesToRemove.length > 0)) { dev.consoleMsg("Changing WIFI profiles, adding " + profilesToAdd.length + ", removing " + profilesToRemove.length + "."); } |
| 1591 | |
| 1592 | // Remove any extra WIFI profiles |
| 1593 | for (var i in profilesToRemove) { |
| 1594 | dev.amtstack.Delete('CIM_WiFiEndpointSettings', { InstanceID: 'Intel(r) AMT:WiFi Endpoint Settings ' + profilesToRemove[i].ElementName }, function (stack, name, responses, status) { }, 0, 1); |
| 1595 | } |
| 1596 | } |
| 1597 | } |
| 1598 | |
| 1599 | // Check the 802.1x client certificate expiration time |
| 1600 | // TODO: We are only getting the client cert from the wired 802.1x profile, need to get it for wireless too. |
| 1601 | var netAuthClientCert = null; |
| 1602 | if (netAuthClientCertInstanceId != null) { |
| 1603 | netAuthClientCert = getInstance(responses['AMT_PublicKeyCertificate'].responses, netAuthClientCertInstanceId); |
| 1604 | if (netAuthClientCert) { |
| 1605 | var cert = null; |
| 1606 | try { cert = obj.parent.certificateOperations.forge.pki.certificateFromAsn1(obj.parent.certificateOperations.forge.asn1.fromDer(obj.parent.certificateOperations.forge.util.decode64(netAuthClientCert.X509Certificate))); } catch (ex) { } |
| 1607 | if (cert != null) { |
| 1608 | const certStart = new Date(cert.validity.notBefore).getTime(); |
| 1609 | const certEnd = new Date(cert.validity.notAfter).getTime(); |
| 1610 | const certMidPoint = certStart + ((certEnd - certStart) / 2); |
| 1611 | if (Date.now() > certMidPoint) { newNetAuthProfileRequested = true; } // Past mid-point or expired, request a new 802.1x certificate & profile |
| 1612 | } |
| 1613 | } |
| 1614 | } |
| 1615 | |
| 1616 | // Figure out if there are no changes to 802.1x wired configuration |
| 1617 | if ((wiredMatch == 0) && (newNetAuthProfileRequested == false)) { wiredConfig = false; } |
| 1618 | |
| 1619 | // See if we need to ask MeshCentral Satellite for a new 802.1x profile |
| 1620 | if (newNetAuthProfileRequested && (typeof srvNetAuthProfile.satellitecredentials == 'string')) { |
| 1621 | // Credentials for this 802.1x profile are provided using MeshCentral Satellite |
| 1622 | // Send a message to Satellite requesting a 802.1x profile for this device |
| 1623 | dev.consoleMsg("Requesting 802.1x credentials for " + netAuthStrings[srvNetAuthProfile.authenticationprotocol] + " from MeshCentral Satellite..."); |
| 1624 | dev.netAuthSatReqId = Buffer.from(parent.crypto.randomBytes(16), 'binary').toString('base64'); // Generate a crypto-secure request id. |
| 1625 | dev.netAuthSatReqData = { domain: domain, wiredConfig: wiredConfig, wirelessConfig: wirelessConfig, devNetAuthProfile: devNetAuthProfile, srvNetAuthProfile: srvNetAuthProfile, profilesToAdd: profilesToAdd, prioritiesInUse: prioritiesInUse, responses: responses, xxCertificates: xxCertificates, xxCertPrivateKeys: xxCertPrivateKeys } |
| 1626 | const request = { action: 'satellite', subaction: '802.1x-ProFile-Request', satelliteFlags: 2, nodeid: dev.nodeid, icon: dev.icon, domain: dev.nodeid.split('/')[1], nolog: 1, reqid: dev.netAuthSatReqId, authProtocol: srvNetAuthProfile.authenticationprotocol, devname: dev.name, osname: dev.rname, ver: dev.intelamt.ver }; |
| 1627 | if (netAuthClientCert != null) { request.cert = netAuthClientCert.X509Certificate; request.certid = netAuthClientCertInstanceId; } |
| 1628 | parent.DispatchEvent([srvNetAuthProfile.satellitecredentials], obj, request); |
| 1629 | |
| 1630 | // Set a response timeout |
| 1631 | const netAuthTimeoutFunc = function netAuthTimeout() { |
| 1632 | if (isAmtDeviceValid(netAuthTimeout.dev) == false) return; // Device no longer exists, ignore this request. |
| 1633 | if (dev.netAuthSatReqId != null) { |
| 1634 | delete netAuthTimeout.dev.netAuthSatReqId; |
| 1635 | delete netAuthTimeout.dev.netAuthSatReqData; |
| 1636 | netAuthTimeout.dev.consoleMsg("MeshCentral Satellite did not respond in time, 802.1x profile will not be set."); |
| 1637 | devTaskCompleted(netAuthTimeout.dev); |
| 1638 | } |
| 1639 | } |
| 1640 | netAuthTimeoutFunc.dev = dev; |
| 1641 | dev.netAuthSatReqTimer = setTimeout(netAuthTimeoutFunc, 20000); |
| 1642 | return; |
| 1643 | } else { |
| 1644 | // No need to call MeshCentral Satellite for a 802.1x profile, so configure everything now. |
| 1645 | attempt8021xSyncEx(dev, { domain: domain, wiredConfig: wiredConfig, wirelessConfig: wirelessConfig, devNetAuthProfile: devNetAuthProfile, srvNetAuthProfile: srvNetAuthProfile, profilesToAdd: profilesToAdd, prioritiesInUse: prioritiesInUse, responses: responses, xxCertificates: xxCertificates, xxCertPrivateKeys: xxCertPrivateKeys }); |
| 1646 | } |
| 1647 | }); |
| 1648 | } |
| 1649 | |
| 1650 | |
| 1651 | // Check 802.1x root certificate |
| 1652 | function perform8021xRootCertCheck(dev) { |
| 1653 | if (isAmtDeviceValid(dev) == false) return; // Device no longer exists, ignore this request. |
| 1654 | |
| 1655 | // Check if there is a root certificate to add, if we already have it, get the instance id. |
| 1656 | if (dev.netAuthCredentials.rootcert) { |
| 1657 | var matchingRootCertId = null; |
| 1658 | for (var i in dev.netAuthSatReqData.xxCertificates) { |
| 1659 | if ((dev.netAuthSatReqData.xxCertificates[i].X509Certificate == dev.netAuthCredentials.rootcert) && (dev.netAuthSatReqData.xxCertificates[i].TrustedRootCertficate)) { |
| 1660 | matchingRootCertId = dev.netAuthSatReqData.xxCertificates[i].InstanceID; |
| 1661 | } |
| 1662 | } |
| 1663 | if (matchingRootCertId == null) { |
| 1664 | // Root certificate not found, add it |
| 1665 | dev.consoleMsg("Setting up new 802.1x root certificate..."); |
| 1666 | const f = function perform8021xRootCertCheckResponse(stack, name, response, status) { |
| 1667 | if ((status != 200) || (response.Body['ReturnValue'] != 0)) { |
| 1668 | // Failed to add the root certificate |
| 1669 | dev.consoleMsg("Failed to sign the certificate request."); |
| 1670 | } else { |
| 1671 | // Root certificate added, move on to client certificate checking |
| 1672 | perform8021xRootCertCheckResponse.dev.netAuthSatReqData.rootCertInstanceId = response.Body.CreatedCertificate.ReferenceParameters.SelectorSet.Selector.Value; |
| 1673 | perform8021xClientCertCheck(perform8021xRootCertCheckResponse.dev); |
| 1674 | } |
| 1675 | } |
| 1676 | f.dev = dev; |
| 1677 | dev.amtstack.AMT_PublicKeyManagementService_AddTrustedRootCertificate(dev.netAuthCredentials.rootcert, f); |
| 1678 | } else { |
| 1679 | // Root certificate already present, move on to client certificate checking |
| 1680 | dev.netAuthSatReqData.rootCertInstanceId = matchingRootCertId; |
| 1681 | perform8021xClientCertCheck(dev); |
| 1682 | } |
| 1683 | } else { |
| 1684 | // No root certificate to check, move on to client certificate checking |
| 1685 | perform8021xClientCertCheck(dev); |
| 1686 | } |
| 1687 | } |
| 1688 | |
| 1689 | // Check 802.1x client certificate |
| 1690 | function perform8021xClientCertCheck(dev) { |
| 1691 | if (isAmtDeviceValid(dev) == false) return; // Device no longer exists, ignore this request. |
| 1692 | |
| 1693 | if (dev.netAuthCredentials.certificate) { |
| 1694 | // The new 802.1x profile includes a new certificate, add it now before adding the 802.1x profiles |
| 1695 | // dev.netAuthCredentials.certificate must be in DER encoded format |
| 1696 | dev.consoleMsg("Setting up new 802.1x client certificate..."); |
| 1697 | const f = function AddCertificateResponse(stack, name, response, status) { |
| 1698 | if ((status != 200) || (response.Body['ReturnValue'] != 0)) { |
| 1699 | AddCertificateResponse.dev.consoleMsg("Unable to set 802.1x certificate."); |
| 1700 | } else { |
| 1701 | // Keep the certificate reference since we need it to add 802.1x profiles |
| 1702 | const certInstanceId = response.Body.CreatedCertificate.ReferenceParameters.SelectorSet.Selector.Value; |
| 1703 | |
| 1704 | // Set the 802.1x wired profile in the device |
| 1705 | AddCertificateResponse.dev.consoleMsg("Setting MeshCentral Satellite 802.1x profile..."); |
| 1706 | const netAuthSatReqData = AddCertificateResponse.dev.netAuthSatReqData; |
| 1707 | delete dev.netAuthSatReqData; |
| 1708 | netAuthSatReqData.certInstanceId = certInstanceId; |
| 1709 | attempt8021xSyncEx(AddCertificateResponse.dev, netAuthSatReqData); |
| 1710 | } |
| 1711 | } |
| 1712 | f.dev = dev; |
| 1713 | dev.amtstack.AMT_PublicKeyManagementService_AddCertificate(dev.netAuthCredentials.certificate, f); |
| 1714 | } else { |
| 1715 | // No 802.1x certificate, set the 802.1x wired profile in the device |
| 1716 | dev.consoleMsg("Setting MeshCentral Satellite 802.1x profile..."); |
| 1717 | const netAuthSatReqData = dev.netAuthSatReqData; |
| 1718 | delete dev.netAuthSatReqData; |
| 1719 | if (dev.netAuthCredentials.certid) { netAuthSatReqData.certInstanceId = dev.netAuthCredentials.certid; } // If we are reusing an existing certificate, set that now. |
| 1720 | attempt8021xSyncEx(dev, netAuthSatReqData); |
| 1721 | } |
| 1722 | } |
| 1723 | |
| 1724 | // Set the 802.1x wired profile |
| 1725 | function attempt8021xSyncEx(dev, devNetAuthData) { |
| 1726 | if (isAmtDeviceValid(dev) == false) return; // Device no longer exists, ignore this request. |
| 1727 | |
| 1728 | // Unpack |
| 1729 | const domain = devNetAuthData.domain; |
| 1730 | const devNetAuthProfile = devNetAuthData.devNetAuthProfile; |
| 1731 | const srvNetAuthProfile = devNetAuthData.srvNetAuthProfile; |
| 1732 | const profilesToAdd = devNetAuthData.profilesToAdd; |
| 1733 | const responses = devNetAuthData.responses; |
| 1734 | const wiredConfig = devNetAuthData.wiredConfig; |
| 1735 | const wirelessConfig = devNetAuthData.wirelessConfig; |
| 1736 | |
| 1737 | if (wiredConfig) { |
| 1738 | var netAuthProfile = Clone(devNetAuthProfile); |
| 1739 | netAuthProfile['Enabled'] = ((srvNetAuthProfile != null) && (typeof srvNetAuthProfile == 'object')); |
| 1740 | if (netAuthProfile['Enabled']) { |
| 1741 | netAuthProfile['ActiveInS0'] = (srvNetAuthProfile.availableins0 !== false); |
| 1742 | netAuthProfile['AuthenticationProtocol'] = srvNetAuthProfile.authenticationprotocol; |
| 1743 | if (srvNetAuthProfile.roamingidentity && (srvNetAuthProfile.roamingidentity != '')) { netAuthProfile['RoamingIdentity'] = srvNetAuthProfile.roamingidentity; } else { delete netAuthProfile['RoamingIdentity']; } |
| 1744 | if (srvNetAuthProfile.servercertificatename && (srvNetAuthProfile.servercertificatename != '')) { |
| 1745 | netAuthProfile['ServerCertificateName'] = srvNetAuthProfile.servercertificatename; |
| 1746 | netAuthProfile['ServerCertificateNameComparison'] = srvNetAuthProfile.servercertificatenamecomparison; |
| 1747 | } else { |
| 1748 | delete netAuthProfile['ServerCertificateName']; |
| 1749 | delete netAuthProfile['ServerCertificateNameComparison']; |
| 1750 | } |
| 1751 | if (srvNetAuthProfile.username && (srvNetAuthProfile.username != '')) { netAuthProfile['Username'] = srvNetAuthProfile.username; } else { delete netAuthProfile['Username']; } |
| 1752 | if (srvNetAuthProfile.password && (srvNetAuthProfile.password != '')) { netAuthProfile['Password'] = srvNetAuthProfile.password; } else { delete netAuthProfile['Password']; } |
| 1753 | if (srvNetAuthProfile.domain && (srvNetAuthProfile.domain != '')) { netAuthProfile['Domain'] = srvNetAuthProfile.domain; } else { delete netAuthProfile['Domain']; } |
| 1754 | if (srvNetAuthProfile.authenticationprotocol > 3) { |
| 1755 | netAuthProfile['ProtectedAccessCredential'] = srvNetAuthProfile.protectedaccesscredentialhex; |
| 1756 | netAuthProfile['PACPassword'] = srvNetAuthProfile.pacpassword; |
| 1757 | } else { |
| 1758 | delete netAuthProfile['ProtectedAccessCredential']; |
| 1759 | delete netAuthProfile['PACPassword']; |
| 1760 | } |
| 1761 | |
| 1762 | // Setup Client Certificate |
| 1763 | if (devNetAuthData.certInstanceId) { |
| 1764 | netAuthProfile['ClientCertificate'] = '<a:Address>/wsman</a:Address><a:ReferenceParameters><w:ResourceURI>' + dev.amtstack.CompleteName('AMT_PublicKeyCertificate') + '</w:ResourceURI><w:SelectorSet><w:Selector Name="InstanceID">' + devNetAuthData.certInstanceId + '</w:Selector></w:SelectorSet></a:ReferenceParameters>'; |
| 1765 | } else { |
| 1766 | delete netAuthProfile['ClientCertificate']; |
| 1767 | } |
| 1768 | |
| 1769 | // Setup Server Certificate |
| 1770 | if (devNetAuthData.rootCertInstanceId) { |
| 1771 | netAuthProfile['ServerCertificateIssuer'] = '<a:Address>/wsman</a:Address><a:ReferenceParameters><w:ResourceURI>' + dev.amtstack.CompleteName('AMT_PublicKeyCertificate') + '</w:ResourceURI><w:SelectorSet><w:Selector Name="InstanceID">' + devNetAuthData.rootCertInstanceId + '</w:Selector></w:SelectorSet></a:ReferenceParameters>'; |
| 1772 | } else { |
| 1773 | delete netAuthProfile['ServerCertificateIssuer']; |
| 1774 | } |
| 1775 | |
| 1776 | netAuthProfile['PxeTimeout'] = (typeof srvNetAuthProfile.pxetimeoutinseconds == 'number') ? srvNetAuthProfile.pxetimeoutinseconds : 120; |
| 1777 | |
| 1778 | // If we have a MeshCentral Satellite profile, use that |
| 1779 | if (dev.netAuthCredentials != null) { |
| 1780 | const srvNetAuthProfile2 = dev.netAuthCredentials; |
| 1781 | if (srvNetAuthProfile2.username && (srvNetAuthProfile2.username != '')) { netAuthProfile['Username'] = srvNetAuthProfile2.username; } |
| 1782 | if (srvNetAuthProfile2.password && (srvNetAuthProfile2.password != '')) { netAuthProfile['Password'] = srvNetAuthProfile2.password; } |
| 1783 | if (srvNetAuthProfile2.domain && (srvNetAuthProfile2.domain != '')) { netAuthProfile['Domain'] = srvNetAuthProfile2.domain; } |
| 1784 | } |
| 1785 | } |
| 1786 | |
| 1787 | dev.amtstack.Put('AMT_8021XProfile', netAuthProfile, function (stack, name, responses, status) { |
| 1788 | const dev = stack.dev; |
| 1789 | if (isAmtDeviceValid(dev) == false) return; // Device no longer exists, ignore this request. |
| 1790 | if (status == 200) { dev.consoleMsg("802.1x wired profile set."); } else { dev.consoleMsg("Unable to set 802.1x wired profile."); } |
| 1791 | attemptWifiSyncEx(dev, devNetAuthData); |
| 1792 | }); |
| 1793 | } else { |
| 1794 | // No wired interface, skip with WIFI config |
| 1795 | attemptWifiSyncEx(dev, devNetAuthData); |
| 1796 | } |
| 1797 | } |
| 1798 | |
| 1799 | function attemptWifiSyncEx(dev, devNetAuthData) { |
| 1800 | if (isAmtDeviceValid(dev) == false) return; // Device no longer exists, ignore this request. |
| 1801 | |
| 1802 | // Unpack |
| 1803 | const domain = devNetAuthData.domain; |
| 1804 | const devNetAuthProfile = devNetAuthData.devNetAuthProfile; |
| 1805 | const srvNetAuthProfile = devNetAuthData.srvNetAuthProfile; |
| 1806 | const profilesToAdd = devNetAuthData.profilesToAdd; |
| 1807 | const responses = devNetAuthData.responses; |
| 1808 | const prioritiesInUse = devNetAuthData.prioritiesInUse; |
| 1809 | const wiredConfig = devNetAuthData.wiredConfig; |
| 1810 | const wirelessConfig = devNetAuthData.wirelessConfig; |
| 1811 | |
| 1812 | var taskCounter = 0; |
| 1813 | if (wirelessConfig) { |
| 1814 | // Add missing WIFI profiles |
| 1815 | var nextPriority = 1; |
| 1816 | for (var i in profilesToAdd) { |
| 1817 | while (prioritiesInUse.indexOf(nextPriority) >= 0) { nextPriority++; } // Figure out the next available priority slot. |
| 1818 | var profileToAdd = profilesToAdd[i]; |
| 1819 | const wifiep = { |
| 1820 | __parameterType: 'reference', |
| 1821 | __resourceUri: 'http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2/CIM_WiFiEndpoint', |
| 1822 | Name: 'WiFi Endpoint 0' |
| 1823 | }; |
| 1824 | const wifiepsettinginput = { |
| 1825 | __parameterType: 'instance', |
| 1826 | __namespace: 'http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2/CIM_WiFiEndpointSettings', |
| 1827 | ElementName: profileToAdd.name, |
| 1828 | InstanceID: 'Intel(r) AMT:WiFi Endpoint Settings ' + profileToAdd.name, |
| 1829 | AuthenticationMethod: profileToAdd.authentication, |
| 1830 | EncryptionMethod: profileToAdd.encryption, |
| 1831 | SSID: profileToAdd.ssid, |
| 1832 | Priority: nextPriority, |
| 1833 | } |
| 1834 | var netAuthProfile, netAuthSettingsClientCert, netAuthSettingsServerCaCert; |
| 1835 | if (([4, 6].indexOf(profileToAdd.authentication)) >= 0) { wifiepsettinginput['PSKPassPhrase'] = profileToAdd.password; } |
| 1836 | if (([5, 7, 32768, 32769].indexOf(profileToAdd.authentication)) >= 0) { |
| 1837 | netAuthProfile = { |
| 1838 | '__parameterType': 'instance', |
| 1839 | '__namespace': dev.amtstack.CompleteName('CIM_IEEE8021xSettings'), |
| 1840 | 'ElementName': '8021x-' + profileToAdd.name, |
| 1841 | 'InstanceID': '8021x-' + profileToAdd.name, |
| 1842 | 'ActiveInS0': (domain.amtmanager['802.1x'].availableins0 !== false), |
| 1843 | 'AuthenticationProtocol': domain.amtmanager['802.1x'].authenticationprotocol |
| 1844 | }; |
| 1845 | if (domain.amtmanager['802.1x'].roamingidentity) { netAuthProfile['RoamingIdentity'] = domain.amtmanager['802.1x'].roamingidentity; } |
| 1846 | if (domain.amtmanager['802.1x'].servercertificatename) { netAuthProfile['ServerCertificateName'] = domain.amtmanager['802.1x'].servercertificatename; netAuthProfile['ServerCertificateNameComparison'] = profileToAdd['802.1x'].servercertificatenamecomparison; } |
| 1847 | if (domain.amtmanager['802.1x'].username) { netAuthProfile['Username'] = domain.amtmanager['802.1x'].username; } |
| 1848 | if (domain.amtmanager['802.1x'].password) { netAuthProfile['Password'] = domain.amtmanager['802.1x'].password; } |
| 1849 | if (domain.amtmanager['802.1x'].domain) { netAuthProfile['Domain'] = domain.amtmanager['802.1x'].domain; } |
| 1850 | if (domain.amtmanager['802.1x'].authenticationprotocol > 3) { domain.amtmanager['ProtectedAccessCredential'] = profileToAdd['802.1x'].protectedaccesscredentialhex; netAuthProfile['PACPassword'] = profileToAdd['802.1x'].pacpassword; } |
| 1851 | |
| 1852 | // Setup Client Certificate |
| 1853 | if (devNetAuthData.certInstanceId) { |
| 1854 | netAuthSettingsClientCert = '<a:Address>/wsman</a:Address><a:ReferenceParameters><w:ResourceURI>' + dev.amtstack.CompleteName('AMT_PublicKeyCertificate') + '</w:ResourceURI><w:SelectorSet><w:Selector Name="InstanceID">' + devNetAuthData.certInstanceId + '</w:Selector></w:SelectorSet></a:ReferenceParameters>'; |
| 1855 | } |
| 1856 | // Setup Server Certificate |
| 1857 | if (devNetAuthData.rootCertInstanceId) { |
| 1858 | netAuthSettingsServerCaCert = '<a:Address>/wsman</a:Address><a:ReferenceParameters><w:ResourceURI>' + dev.amtstack.CompleteName('AMT_PublicKeyCertificate') + '</w:ResourceURI><w:SelectorSet><w:Selector Name="InstanceID">' + devNetAuthData.rootCertInstanceId + '</w:Selector></w:SelectorSet></a:ReferenceParameters>'; |
| 1859 | } |
| 1860 | |
| 1861 | // If we have credentials from MeshCentral Satelite, use that |
| 1862 | if (dev.netAuthCredentials != null) { |
| 1863 | const srvNetAuthProfile2 = dev.netAuthCredentials; |
| 1864 | if (srvNetAuthProfile2.username && (srvNetAuthProfile2.username != '')) { netAuthProfile['Username'] = srvNetAuthProfile2.username; } |
| 1865 | if (srvNetAuthProfile2.password && (srvNetAuthProfile2.password != '')) { netAuthProfile['Password'] = srvNetAuthProfile2.password; } |
| 1866 | if (srvNetAuthProfile2.domain && (srvNetAuthProfile2.domain != '')) { netAuthProfile['Domain'] = srvNetAuthProfile2.domain; } |
| 1867 | } |
| 1868 | } |
| 1869 | prioritiesInUse.push(nextPriority); // Occupy the priority slot and add the WIFI profile. |
| 1870 | |
| 1871 | taskCounter++; |
| 1872 | dev.amtstack.AMT_WiFiPortConfigurationService_AddWiFiSettings(wifiep, wifiepsettinginput, netAuthProfile, netAuthSettingsClientCert, netAuthSettingsServerCaCert, function (stack, name, response, status) { |
| 1873 | if (status != 200) { dev.consoleMsg("Unable to set WIFI profile."); } |
| 1874 | if (--taskCounter == 0) { attemptWifiSyncEx2(dev, devNetAuthData); } // All done, complete WIFI configuration |
| 1875 | }); |
| 1876 | } |
| 1877 | } |
| 1878 | |
| 1879 | if (taskCounter == 0) { attemptWifiSyncEx2(dev, devNetAuthData); } // All done, complete WIFI configuration |
| 1880 | } |
| 1881 | |
| 1882 | function attemptWifiSyncEx2(dev, devNetAuthData) { |
| 1883 | if (isAmtDeviceValid(dev) == false) return; // Device no longer exists, ignore this request. |
| 1884 | const responses = devNetAuthData.responses; |
| 1885 | const wirelessConfig = devNetAuthData.wirelessConfig; |
| 1886 | |
| 1887 | if (wirelessConfig) { |
| 1888 | // Check if local WIFI profile sync is enabled, if not, enabled it. |
| 1889 | if ((responses['AMT_WiFiPortConfigurationService'] != null) && (responses['AMT_WiFiPortConfigurationService'].response != null) && (responses['AMT_WiFiPortConfigurationService'].response['localProfileSynchronizationEnabled'] == 0)) { |
| 1890 | responses['AMT_WiFiPortConfigurationService'].response['localProfileSynchronizationEnabled'] = 1; |
| 1891 | dev.amtstack.Put('AMT_WiFiPortConfigurationService', responses['AMT_WiFiPortConfigurationService'].response, function (stack, name, response, status) { |
| 1892 | if (status != 200) { dev.consoleMsg("Unable to enable local WIFI profile sync."); } else { dev.consoleMsg("Enabled local WIFI profile sync."); } |
| 1893 | }); |
| 1894 | } |
| 1895 | |
| 1896 | // Change the WIFI state if needed. Right now, we always enable it. |
| 1897 | // WifiState = { 3: "Disabled", 32768: "Enabled in S0", 32769: "Enabled in S0, Sx/AC" }; |
| 1898 | var wifiState = 32769; // For now, always enable WIFI |
| 1899 | if (responses['CIM_WiFiPort'].responses.Body.EnabledState != 32769) { |
| 1900 | if (wifiState == 3) { |
| 1901 | dev.amtstack.CIM_WiFiPort_RequestStateChange(wifiState, null, function (stack, name, responses, status) { |
| 1902 | const dev = stack.dev; |
| 1903 | if (isAmtDeviceValid(dev) == false) return; // Device no longer exists, ignore this request. |
| 1904 | if (status == 200) { dev.consoleMsg("Disabled WIFI."); } |
| 1905 | }); |
| 1906 | } else { |
| 1907 | dev.amtstack.CIM_WiFiPort_RequestStateChange(wifiState, null, function (stack, name, responses, status) { |
| 1908 | const dev = stack.dev; |
| 1909 | if (isAmtDeviceValid(dev) == false) return; // Device no longer exists, ignore this request. |
| 1910 | if (status == 200) { dev.consoleMsg("Enabled WIFI."); } |
| 1911 | }); |
| 1912 | } |
| 1913 | } |
| 1914 | } |
| 1915 | |
| 1916 | // Done |
| 1917 | devTaskCompleted(dev); |
| 1918 | } |
| 1919 | |
| 1920 | // Request for a RSA key pair generation. This will be used to generate the 802.1x certificate |
| 1921 | function attempt8021xKeyGeneration(dev) { |
| 1922 | if (isAmtDeviceValid(dev) == false) return; // Device no longer exists, ignore this request. |
| 1923 | |
| 1924 | dev.amtstack.AMT_PublicKeyManagementService_GenerateKeyPair(0, 2048, function (stack, name, response, status) { |
| 1925 | if ((status != 200) || (response.Body['ReturnValue'] != 0)) { |
| 1926 | // Failed to generate a key pair |
| 1927 | dev.consoleMsg("Failed to generate the requested RSA key pair."); |
| 1928 | } else { |
| 1929 | dev.amtstack.Enum('AMT_PublicPrivateKeyPair', function (stack, name, xresponse, status, keyInstanceId) { |
| 1930 | if (status != 200) { |
| 1931 | // Failed to get the generated key pair |
| 1932 | dev.consoleMsg("Failed to get the generated RSA key pair."); |
| 1933 | } else { |
| 1934 | // We got the key pair |
| 1935 | var DERKey = null; |
| 1936 | for (var i in xresponse) { |
| 1937 | if (xresponse[i]['InstanceID'] == keyInstanceId) { |
| 1938 | // We found our matching DER key |
| 1939 | DERKey = xresponse[i]['DERKey']; |
| 1940 | } |
| 1941 | } |
| 1942 | if (DERKey == null) { dev.consoleMsg("Failed to match the generated RSA key pair."); return; } |
| 1943 | dev.consoleMsg("Generated a RSA key pair."); |
| 1944 | var domain = parent.config.domains[dev.domainid]; |
| 1945 | parent.DispatchEvent([domain.amtmanager['802.1x'].satellitecredentials], obj, { action: 'satellite', subaction: '802.1x-KeyPair-Response', satelliteFlags: 2, nodeid: dev.nodeid, icon: dev.icon, domain: dev.nodeid.split('/')[1], nolog: 1, reqid: dev.netAuthSatReqId, authProtocol: domain.amtmanager['802.1x'].authenticationprotocol, devname: dev.name, osname: dev.rname, DERKey: DERKey, keyInstanceId: keyInstanceId, ver: dev.intelamt.ver }); |
| 1946 | } |
| 1947 | }, response.Body['KeyPair']['ReferenceParameters']['SelectorSet']['Selector']['Value']); |
| 1948 | } |
| 1949 | }); |
| 1950 | } |
| 1951 | |
| 1952 | // 802.1x request to process a Certificate Signing Request, we ask Intel AMT to sign the request |
| 1953 | function attempt8021xCRSRequest(dev, event) { |
| 1954 | if (isAmtDeviceValid(dev) == false) return; // Device no longer exists, ignore this request. |
| 1955 | |
| 1956 | if ((event.response == null) || (event.response.keyInstanceId == null)) return; |
| 1957 | var keyPair = '<a:Address>http://schemas.xmlsoap.org/ws/2004/08/addressing/role/anonymous</a:Address><a:ReferenceParameters><w:ResourceURI>http://intel.com/wbem/wscim/1/amt-schema/1/AMT_PublicPrivateKeyPair</w:ResourceURI><w:SelectorSet><w:Selector Name="InstanceID">' + event.response.keyInstanceId + '</w:Selector></w:SelectorSet></a:ReferenceParameters>'; // keyPair EPR Reference |
| 1958 | var signingAlgorithm = 1; // 0 = SHA1-RSA, 1 = SHA256-RSA |
| 1959 | var nullSignedCertificateRequest = event.response.csr; // DEREncodedRequest |
| 1960 | dev.amtstack.AMT_PublicKeyManagementService_GeneratePKCS10RequestEx(keyPair, signingAlgorithm, nullSignedCertificateRequest, function (stack, name, response, status) { |
| 1961 | if ((status != 200) || (response.Body['ReturnValue'] != 0)) { |
| 1962 | // Failed to get the generated key pair |
| 1963 | dev.consoleMsg("Failed to sign the certificate request."); |
| 1964 | } else { |
| 1965 | // We got a signed certificate request, return that to the server |
| 1966 | dev.consoleMsg("Generated a signed certificate request."); |
| 1967 | var domain = parent.config.domains[dev.domainid]; |
| 1968 | parent.DispatchEvent([domain.amtmanager['802.1x'].satellitecredentials], obj, { action: 'satellite', subaction: '802.1x-CSR-Response', satelliteFlags: 2, nodeid: dev.nodeid, icon: dev.icon, domain: dev.nodeid.split('/')[1], nolog: 1, reqid: dev.netAuthSatReqId, authProtocol: domain.amtmanager['802.1x'].authenticationprotocol, devname: dev.name, osname: dev.rname, signedcsr: response.Body['SignedCertificateRequest'], ver: dev.intelamt.ver }); |
| 1969 | } |
| 1970 | }); |
| 1971 | } |
| 1972 | |
| 1973 | |
| 1974 | // |
| 1975 | // Intel AMT Server Root Certificate |
| 1976 | // |
| 1977 | |
| 1978 | // Check if Intel AMT has the server root certificate |
| 1979 | function attemptRootCertSync(dev, func, forced) { |
| 1980 | if (isAmtDeviceValid(dev) == false) return; // Device no longer exists, ignore this request. |
| 1981 | if (dev.policy.amtPolicy == 0) { func(dev); return; } // If there is no Intel AMT policy, skip this operation. |
| 1982 | if (forced !== true) { if ((dev.connType != 2) || (dev.policy.ciraPolicy != 2)) { func(dev); return; } } // Server root certificate does not need to be present if CIRA is not needed and "forced" is false |
| 1983 | if (parent.mpsserver.server == null) { func(dev); return; } // Root cert not needed if MPS is not active. |
| 1984 | |
| 1985 | // Find the current TLS certificate & MeshCentral root certificate |
| 1986 | var xxMeshCentralRoot = null; |
| 1987 | if (dev.policy.tlsCredentialContext.length > 0) { |
| 1988 | for (var i in dev.policy.certificates) { if (dev.policy.certificates[i]['X509Certificate'] == obj.rootCertBase64) { xxMeshCentralRoot = i; } } |
| 1989 | } |
| 1990 | |
| 1991 | // If the server root certificate is not present and we need to configure CIRA, add it |
| 1992 | if (xxMeshCentralRoot == null) { |
| 1993 | dev.taskCount = 1; |
| 1994 | dev.taskCompleted = func; |
| 1995 | dev.amtstack.AMT_PublicKeyManagementService_AddTrustedRootCertificate(obj.rootCertBase64, function (stack, name, responses, status) { |
| 1996 | const dev = stack.dev; |
| 1997 | if (isAmtDeviceValid(dev) == false) return; // Device no longer exists, ignore this request. |
| 1998 | if (status != 200) { dev.consoleMsg("Failed to add server root certificate (" + status + ")."); removeAmtDevice(dev, 28); return; } |
| 1999 | dev.consoleMsg("Added server root certificate."); |
| 2000 | devTaskCompleted(dev); |
| 2001 | }); |
| 2002 | } else { func(dev); } |
| 2003 | } |
| 2004 | |
| 2005 | |
| 2006 | // |
| 2007 | // Intel AMT CIRA Setup |
| 2008 | // |
| 2009 | |
| 2010 | // Check if Intel AMT has the server root certificate |
| 2011 | // If deactivation policy is in effect, remove CIRA configuration |
| 2012 | function attemptCiraSync(dev, func) { |
| 2013 | if (isAmtDeviceValid(dev) == false) return; // Device no longer exists, ignore this request. |
| 2014 | if ((dev.connType != 2) || ((dev.policy.ciraPolicy != 1) && (dev.policy.ciraPolicy != 2))) { func(dev); return; } // Only setup CIRA when LMS connection is used and a CIRA policy is enabled. |
| 2015 | |
| 2016 | // Get current CIRA settings |
| 2017 | // TODO: We only deal with remote access starting with Intel AMT 6 and beyond |
| 2018 | dev.taskCount = 1; |
| 2019 | dev.taskCompleted = func; |
| 2020 | dev.tryCount = 0; |
| 2021 | var requests = ['*AMT_EnvironmentDetectionSettingData', 'AMT_ManagementPresenceRemoteSAP', 'AMT_RemoteAccessCredentialContext', 'AMT_RemoteAccessPolicyAppliesToMPS', 'AMT_RemoteAccessPolicyRule', '*AMT_UserInitiatedConnectionService', 'AMT_MPSUsernamePassword']; |
| 2022 | if ((dev.aquired.majorver != null) && (dev.aquired.majorver > 11)) { requests.push('*IPS_HTTPProxyService', 'IPS_HTTPProxyAccessPoint'); } |
| 2023 | dev.amtstack.BatchEnum(null, requests, attemptCiraSyncResponse); |
| 2024 | } |
| 2025 | |
| 2026 | function attemptCiraSyncResponse(stack, name, responses, status) { |
| 2027 | const dev = stack.dev; |
| 2028 | if (isAmtDeviceValid(dev) == false) return; // Device no longer exists, ignore this request. |
| 2029 | |
| 2030 | if ((dev.aquired.majorver != null) && (dev.aquired.majorver > 11) && (status == 400)) { |
| 2031 | // Check if only the HTTP proxy objects failed |
| 2032 | status = 200; |
| 2033 | if (responses['IPS_HTTPProxyAccessPoint'].status == 400) { delete responses['IPS_HTTPProxyAccessPoint']; } |
| 2034 | if (responses['IPS_HTTPProxyService'].status == 400) { delete responses['IPS_HTTPProxyService']; } |
| 2035 | for (var i in responses) { if (responses[i].status != 200) { status = responses[i].status; } } |
| 2036 | } |
| 2037 | |
| 2038 | // If batch enumeration was not succesful, try again. |
| 2039 | if (status != 200) { |
| 2040 | // If we failed to get the CIRA state, try again up to 5 times. |
| 2041 | if (dev.tryCount <= 5) { |
| 2042 | dev.tryCount++; |
| 2043 | var requests = ['*AMT_EnvironmentDetectionSettingData', 'AMT_ManagementPresenceRemoteSAP', 'AMT_RemoteAccessCredentialContext', 'AMT_RemoteAccessPolicyAppliesToMPS', 'AMT_RemoteAccessPolicyRule', '*AMT_UserInitiatedConnectionService', 'AMT_MPSUsernamePassword']; |
| 2044 | if ((dev.aquired.majorver != null) && (dev.aquired.majorver > 11)) { requests.push('*IPS_HTTPProxyService', 'IPS_HTTPProxyAccessPoint'); } |
| 2045 | dev.amtstack.BatchEnum(null, requests, attemptCiraSyncResponse); |
| 2046 | return; |
| 2047 | } |
| 2048 | |
| 2049 | // We tried 5 times, give up. |
| 2050 | dev.consoleMsg("Failed to get CIRA state (" + status + ")."); |
| 2051 | removeAmtDevice(dev, 29); |
| 2052 | return; |
| 2053 | } |
| 2054 | |
| 2055 | // Check if CIRA is supported |
| 2056 | if ((responses['AMT_UserInitiatedConnectionService'] == null) || (responses['AMT_UserInitiatedConnectionService'].response == null)) { |
| 2057 | dev.consoleMsg("This device does not support CIRA."); |
| 2058 | devTaskCompleted(dev); |
| 2059 | return; |
| 2060 | } |
| 2061 | |
| 2062 | dev.cira = {}; |
| 2063 | dev.cira.xxRemoteAccess = responses; |
| 2064 | dev.cira.xxEnvironementDetection = responses['AMT_EnvironmentDetectionSettingData'].response; |
| 2065 | dev.cira.xxEnvironementDetection['DetectionStrings'] = MakeToArray(dev.cira.xxEnvironementDetection['DetectionStrings']); |
| 2066 | dev.cira.xxCiraServers = responses['AMT_ManagementPresenceRemoteSAP'].responses; |
| 2067 | dev.cira.xxUserInitiatedCira = responses['AMT_UserInitiatedConnectionService'].response; |
| 2068 | dev.cira.xxRemoteAccessCredentiaLinks = responses['AMT_RemoteAccessCredentialContext'].responses; |
| 2069 | dev.cira.xxMPSUserPass = responses['AMT_MPSUsernamePassword'].responses; |
| 2070 | |
| 2071 | // Set CIRA initiation to BIOS & OS enabled |
| 2072 | if (dev.cira.xxUserInitiatedCira['EnabledState'] != 32771) { // 32768: "Disabled", 32769: "BIOS enabled", 32770: "OS enable", 32771: "BIOS & OS enabled" |
| 2073 | dev.amtstack.AMT_UserInitiatedConnectionService_RequestStateChange(32771, null, function (stack, name, responses, status) { }); // This is not a critical call. |
| 2074 | } |
| 2075 | |
| 2076 | // Figure out policies attached to servers. Create a policy type to server table. |
| 2077 | dev.cira.xxPolicies = { 'User': [], 'Alert': [], 'Periodic': [] }; |
| 2078 | for (var i in responses['AMT_RemoteAccessPolicyAppliesToMPS'].responses) { |
| 2079 | var policy = responses['AMT_RemoteAccessPolicyAppliesToMPS'].responses[i]; |
| 2080 | var server = Clone(getItem(dev.cira.xxCiraServers, 'Name', getItem(policy['ManagedElement']['ReferenceParameters']['SelectorSet']['Selector'], '@Name', 'Name')['Value'])); |
| 2081 | server.MpsType = policy['MpsType']; // MpsType was added in Intel AMT 11.6 |
| 2082 | var ptype = (getItem(policy['PolicySet']['ReferenceParameters']['SelectorSet']['Selector'], '@Name', 'PolicyRuleName')['Value']).split(' ')[0]; |
| 2083 | dev.cira.xxPolicies[ptype].push(server); |
| 2084 | } |
| 2085 | |
| 2086 | // Fetch the server's CIRA settings |
| 2087 | dev.cira.mpsPresent = null; |
| 2088 | dev.cira.mpsPolicy = false; |
| 2089 | if ((dev.policy.ciraPolicy == 2) && (parent.mpsserver.server != null)) { // parent.mpsserver.server is not null if the MPS server is listening for TCP/TLS connections |
| 2090 | dev.cira.meshidx = dev.meshid.split('/')[2].replace(/\@/g, 'X').replace(/\$/g, 'X').substring(0, 16); |
| 2091 | dev.cira.mpsName = parent.webserver.certificates.AmtMpsName; |
| 2092 | var serverNameSplit = dev.cira.mpsName.split('.'); |
| 2093 | dev.cira.mpsPort = ((parent.args.mpsaliasport != null) ? parent.args.mpsaliasport : parent.args.mpsport); |
| 2094 | dev.cira.mpsAddressFormat = 201; // 201 = FQDN, 3 = IPv4 |
| 2095 | dev.cira.mpsPass = getRandomAmtPassword(); |
| 2096 | if ((serverNameSplit.length == 4) && (parseInt(serverNameSplit[0]) == serverNameSplit[0]) && (parseInt(serverNameSplit[1]) == serverNameSplit[1]) && (parseInt(serverNameSplit[2]) == serverNameSplit[2]) && (parseInt(serverNameSplit[3]) == serverNameSplit[3])) { dev.cira.mpsAddressFormat = 3; } |
| 2097 | |
| 2098 | // Check if our server is already present |
| 2099 | if ((dev.cira.xxCiraServers != null) && (dev.cira.xxCiraServers.length > 0)) { |
| 2100 | for (var i = 0; i < dev.cira.xxCiraServers.length; i++) { |
| 2101 | var mpsServer = dev.cira.xxCiraServers[i]; |
| 2102 | if ((mpsServer.AccessInfo == dev.cira.mpsName) && (mpsServer.Port == dev.cira.mpsPort) && (mpsServer.InfoFormat == dev.cira.mpsAddressFormat)) { dev.cira.mpsPresent = mpsServer['Name']; } |
| 2103 | } |
| 2104 | } |
| 2105 | |
| 2106 | // Check if our server is already present |
| 2107 | if ((dev.cira.xxPolicies != null) && (dev.cira.xxPolicies['Periodic'].length > 0)) { |
| 2108 | var mpsServer = dev.cira.xxPolicies['Periodic'][0]; |
| 2109 | if ((mpsServer.AccessInfo == dev.cira.mpsName) && (mpsServer.Port == dev.cira.mpsPort) && (mpsServer.InfoFormat == dev.cira.mpsAddressFormat)) { dev.cira.mpsPolicy = true; } |
| 2110 | } |
| 2111 | } |
| 2112 | |
| 2113 | // Remove all MPS policies that are not ours |
| 2114 | if (dev.cira.xxPolicies != null) { |
| 2115 | if ((dev.cira.xxPolicies['User'] != null) && (dev.cira.xxPolicies['User'].length > 0)) { dev.consoleMsg("Removing CIRA user trigger."); dev.amtstack.Delete('AMT_RemoteAccessPolicyRule', { 'PolicyRuleName': 'User Initiated' }, function (stack, name, responses, status) { }); } |
| 2116 | if ((dev.cira.xxPolicies['Alert'] != null) && (dev.cira.xxPolicies['Alert'].length > 0)) { dev.consoleMsg("Removing CIRA alert trigger."); dev.amtstack.Delete('AMT_RemoteAccessPolicyRule', { 'PolicyRuleName': 'Alert' }, function (stack, name, responses, status) { }); } |
| 2117 | if ((dev.cira.xxPolicies['Periodic'] != null) && (dev.cira.xxPolicies['Periodic'].length > 0) && (dev.cira.mpsPolicy == false)) { dev.consoleMsg("Removing CIRA periodic trigger."); dev.amtstack.Delete('AMT_RemoteAccessPolicyRule', { 'PolicyRuleName': 'Periodic' }, function (stack, name, responses, status) { }); } |
| 2118 | } |
| 2119 | |
| 2120 | // Remove all MPS servers that are not ours |
| 2121 | if ((dev.cira.xxCiraServers != null) && (dev.cira.xxCiraServers.length > 0)) { |
| 2122 | for (var i = 0; i < dev.cira.xxCiraServers.length; i++) { |
| 2123 | var mpsServer = dev.cira.xxCiraServers[i]; |
| 2124 | if ((mpsServer.AccessInfo != dev.cira.mpsName) || (mpsServer.Port != dev.cira.mpsPort) || (mpsServer.InfoFormat != dev.cira.mpsAddressFormat)) { |
| 2125 | dev.consoleMsg("Removing MPS server."); |
| 2126 | dev.amtstack.Delete('AMT_ManagementPresenceRemoteSAP', { 'Name': mpsServer['Name'] }, function (stack, name, responses, status) { }); |
| 2127 | } |
| 2128 | } |
| 2129 | } |
| 2130 | |
| 2131 | // If we need to setup CIRA, start by checking the MPS server |
| 2132 | // parent.mpsserver.server is not null if the MPS server is listening for TCP/TLS connections |
| 2133 | if ((dev.policy.ciraPolicy == 2) && (parent.mpsserver.server != null)) { addMpsServer(dev); } else { checkEnvironmentDetection(dev); } |
| 2134 | } |
| 2135 | |
| 2136 | function addMpsServer(dev) { |
| 2137 | // Add the MPS server if not present |
| 2138 | if (dev.cira.mpsPresent == null) { |
| 2139 | dev.amtstack.AMT_RemoteAccessService_AddMpServer(dev.cira.mpsName, dev.cira.mpsAddressFormat, dev.cira.mpsPort, 2, null, dev.cira.meshidx, dev.cira.mpsPass, dev.cira.mpsName, function (stack, name, response, status) { |
| 2140 | const dev = stack.dev; |
| 2141 | if (isAmtDeviceValid(dev) == false) return; // Device no longer exists, ignore this request. |
| 2142 | if (status != 200) { dev.consoleMsg("Failed to create new MPS server (" + status + ")."); removeAmtDevice(dev, 31); return; } |
| 2143 | if ((response.Body.MpServer == null) || (response.Body.MpServer.ReferenceParameters == null) || (response.Body.MpServer.ReferenceParameters.SelectorSet == null) || (response.Body.MpServer.ReferenceParameters.SelectorSet.Selector == null)) { dev.consoleMsg("Create new MPS server invalid response."); removeAmtDevice(dev, 32); return; } |
| 2144 | dev.cira.mpsPresent = getItem(response.Body.MpServer.ReferenceParameters.SelectorSet.Selector, '@Name', 'Name').Value; |
| 2145 | dev.consoleMsg("Created new MPS server."); |
| 2146 | addMpsPolicy(dev); |
| 2147 | |
| 2148 | // Update the device with the MPS password |
| 2149 | dev.aquired.mpspass = dev.cira.mpsPass; |
| 2150 | UpdateDevice(dev); |
| 2151 | }); |
| 2152 | } else { |
| 2153 | // MPS server is present, check MPS trigger policy |
| 2154 | addMpsPolicy(dev); |
| 2155 | } |
| 2156 | } |
| 2157 | |
| 2158 | function addMpsPolicy(dev) { |
| 2159 | if (dev.cira.mpsPolicy == false) { |
| 2160 | var cilaSupport = ((dev.aquired.majorver != null) && (dev.aquired.minorver != null)) && ((dev.aquired.majorver > 11) || ((dev.aquired.majorver == 11) && (dev.aquired.minorver >= 6))); |
| 2161 | var trigger = 2; // 1 = Alert, 2 = Periodic |
| 2162 | |
| 2163 | // Setup extended data |
| 2164 | var extendedData = null; |
| 2165 | if (trigger == 2) { |
| 2166 | var timertype = 0; // 0 = Periodic, 1 = Time of day |
| 2167 | var exdata = IntToStr(10); // Interval trigger, 10 seconds |
| 2168 | extendedData = Buffer.from(IntToStr(timertype) + exdata, 'binary').toString('base64'); |
| 2169 | } |
| 2170 | |
| 2171 | // Create the MPS server references |
| 2172 | 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">' + dev.cira.mpsPresent + '</Selector></SelectorSet></ReferenceParameters>'; |
| 2173 | var server2 = null; |
| 2174 | |
| 2175 | // Put the CIRA/CILA servers in the right bins. |
| 2176 | var ciraServers = [], cilaServers = []; |
| 2177 | if (server1) { ciraServers.push(server1); if (server2) { ciraServers.push(server2); } } |
| 2178 | |
| 2179 | // Go ahead and create the new CIRA/CILA policy. |
| 2180 | dev.amtstack.AMT_RemoteAccessService_AddRemoteAccessPolicyRule(trigger, 0, extendedData, ciraServers, cilaServers, function (stack, name, responses, status) { |
| 2181 | const dev = stack.dev; |
| 2182 | if (isAmtDeviceValid(dev) == false) return; // Device no longer exists, ignore this request. |
| 2183 | if (status != 200) { dev.consoleMsg("Failed to create new MPS policy (" + status + ")."); removeAmtDevice(dev, 33); return; } |
| 2184 | dev.consoleMsg("Created new MPS policy."); |
| 2185 | checkEnvironmentDetection(dev); |
| 2186 | }); |
| 2187 | } else { |
| 2188 | checkEnvironmentDetection(dev); |
| 2189 | } |
| 2190 | } |
| 2191 | |
| 2192 | function checkEnvironmentDetection(dev) { |
| 2193 | var changes = false; |
| 2194 | var editEnvironmentDetectionTmp = []; |
| 2195 | var currentEnvDetect = dev.cira.xxEnvironementDetection['DetectionStrings']; |
| 2196 | if (currentEnvDetect == null) { currentEnvDetect = []; } |
| 2197 | |
| 2198 | if ((dev.policy.ciraPolicy == 2) && (parent.mpsserver.server != null)) { // ciraPolicy: 0 = Do Nothing, 1 = Clear, 2 = Set |
| 2199 | const newEnvDetect = parent.config.domains[dev.domainid].amtmanager.environmentdetection; |
| 2200 | if (newEnvDetect == null) { |
| 2201 | // If no environment detection is specified in the config.json, check that we have a random environment detection |
| 2202 | if (currentEnvDetect.length == 0) { editEnvironmentDetectionTmp = [ Buffer.from(parent.crypto.randomBytes(6), 'binary').toString('hex') ]; changes = true; } |
| 2203 | } else { |
| 2204 | // Check that we have exactly the correct environement detection suffixes |
| 2205 | var mismatch = false; |
| 2206 | if (currentEnvDetect.length != newEnvDetect.length) { |
| 2207 | mismatch = true; |
| 2208 | } else { |
| 2209 | // Check if everything matches |
| 2210 | for (var i in currentEnvDetect) { if (newEnvDetect.indexOf(currentEnvDetect[i]) == -1) { mismatch = true; } } |
| 2211 | for (var i in newEnvDetect) { if (currentEnvDetect.indexOf(newEnvDetect[i]) == -1) { mismatch = true; } } |
| 2212 | } |
| 2213 | // If not, we need to set the new ones |
| 2214 | if (mismatch == true) { editEnvironmentDetectionTmp = newEnvDetect; changes = true; } |
| 2215 | } |
| 2216 | |
| 2217 | } else if ((dev.policy.ciraPolicy == 1) || (parent.mpsserver.server == null)) { |
| 2218 | // Check environment detection is clear |
| 2219 | if (currentEnvDetect.length != 0) { editEnvironmentDetectionTmp = []; changes = true; } |
| 2220 | } |
| 2221 | |
| 2222 | // If we need to change the environment detection on the remote device, do it now. |
| 2223 | if (changes == true) { |
| 2224 | var t = Clone(dev.cira.xxEnvironementDetection); |
| 2225 | t['DetectionStrings'] = editEnvironmentDetectionTmp; |
| 2226 | dev.cira.envclear = (editEnvironmentDetectionTmp.length == 0); |
| 2227 | dev.amtstack.Put('AMT_EnvironmentDetectionSettingData', t, function (stack, name, responses, status) { |
| 2228 | const dev = stack.dev; |
| 2229 | if (isAmtDeviceValid(dev) == false) return; // Device no longer exists, ignore this request. |
| 2230 | if (status != 200) { dev.consoleMsg("Failed to set environement detection (" + status + ")."); removeAmtDevice(dev, 34); return; } |
| 2231 | if (dev.cira.envclear) { dev.consoleMsg("Environment detection cleared."); } else { dev.consoleMsg("Environment detection set."); } |
| 2232 | devTaskCompleted(dev); |
| 2233 | }, 0, 1); |
| 2234 | } else { |
| 2235 | devTaskCompleted(dev); |
| 2236 | } |
| 2237 | } |
| 2238 | |
| 2239 | |
| 2240 | // |
| 2241 | // Intel AMT Settings |
| 2242 | // |
| 2243 | |
| 2244 | function attemptSettingsSync(dev, func) { |
| 2245 | if (isAmtDeviceValid(dev) == false) return; // Device no longer exists, ignore this request. |
| 2246 | if (dev.policy.amtPolicy == 0) { func(dev); return; } // If there is no Intel AMT policy, skip this operation. |
| 2247 | dev.taskCount = 1; |
| 2248 | dev.taskCompleted = func; |
| 2249 | |
| 2250 | // Query the things we are going to be checking |
| 2251 | var query = ['*AMT_GeneralSettings', '*AMT_RedirectionService']; |
| 2252 | if ((dev.aquired.majorver != null) && (dev.aquired.majorver > 5)) { query.push('*CIM_KVMRedirectionSAP', '*IPS_OptInService'); } |
| 2253 | dev.amtstack.BatchEnum('', query, attemptSettingsSyncResponse); |
| 2254 | } |
| 2255 | |
| 2256 | function attemptSettingsSyncResponse(stack, name, responses, status) { |
| 2257 | const dev = stack.dev; |
| 2258 | if (isAmtDeviceValid(dev) == false) return; // Device no longer exists, ignore this request. |
| 2259 | if (status != 200) { devTaskCompleted(dev); return; } |
| 2260 | |
| 2261 | // If this device does not have KVM, ignore the response. This can happen for Intel Standard Manageability (Intel(R) SM). |
| 2262 | if ((responses['CIM_KVMRedirectionSAP'] == null) || (responses['CIM_KVMRedirectionSAP'].status == 400)) { responses['CIM_KVMRedirectionSAP'] = null; } |
| 2263 | |
| 2264 | // Set user consent requirement to match device group user consent |
| 2265 | const mesh = parent.webserver.meshes[dev.meshid]; |
| 2266 | if (mesh == null) { removeAmtDevice(dev, 35); return; } |
| 2267 | const userConsentRequirement = ((typeof mesh.consent == 'number') && ((mesh.consent & 8) != 0)) ? 1 : 0; // Enable user consent for KVM if device group desktop "Prompt for user consent" is enabled. |
| 2268 | |
| 2269 | // Check user consent requirements |
| 2270 | if ((responses['IPS_OptInService'] != null) && (responses['IPS_OptInService'].response['OptInRequired'] != userConsentRequirement)) { |
| 2271 | responses['IPS_OptInService'].response['OptInRequired'] = userConsentRequirement; // 0 = Not Required, 1 = Required for KVM only, 0xFFFFFFFF = Always Required |
| 2272 | dev.amtstack.Put('IPS_OptInService', responses['IPS_OptInService'].response, function (stack, name, responses, status) { |
| 2273 | const dev = stack.dev; |
| 2274 | if (isAmtDeviceValid(dev) == false) return; // Device no longer exists, ignore this request. |
| 2275 | if (status == 200) { |
| 2276 | if (userConsentRequirement == 0) { |
| 2277 | dev.consoleMsg("Cleared user consent requirements."); |
| 2278 | } else if (userConsentRequirement == 1) { |
| 2279 | dev.consoleMsg("Enabled KVM user consent requirement."); |
| 2280 | } else if (userConsentRequirement == 0xFFFFFFFF) { |
| 2281 | dev.consoleMsg("Enabled all user consent requirement."); |
| 2282 | } |
| 2283 | } |
| 2284 | }, 0, 1); |
| 2285 | } |
| 2286 | |
| 2287 | // Enable SOL & IDER |
| 2288 | if ((responses['AMT_RedirectionService'].response['EnabledState'] != 32771) || (responses['AMT_RedirectionService'].response['ListenerEnabled'] == false)) { |
| 2289 | dev.redirObj = responses['AMT_RedirectionService'].response; |
| 2290 | dev.redirObj['ListenerEnabled'] = true; |
| 2291 | dev.redirObj['EnabledState'] = 32771; |
| 2292 | dev.taskCount++; |
| 2293 | dev.amtstack.AMT_RedirectionService_RequestStateChange(32771, |
| 2294 | function (stack, name, response, status) { |
| 2295 | const dev = stack.dev; |
| 2296 | if (isAmtDeviceValid(dev) == false) return; // Device no longer exists, ignore this request. |
| 2297 | dev.amtstack.Put('AMT_RedirectionService', dev.redirObj, function (stack, name, response, status) { |
| 2298 | const dev = stack.dev; |
| 2299 | delete dev.redirObj; |
| 2300 | if (isAmtDeviceValid(dev) == false) return; // Device no longer exists, ignore this request. |
| 2301 | if (status == 200) { dev.consoleMsg("Enabled redirection features."); } |
| 2302 | devTaskCompleted(dev); |
| 2303 | }, 0, 1); |
| 2304 | } |
| 2305 | ); |
| 2306 | } |
| 2307 | |
| 2308 | // Check KVM state |
| 2309 | if ((dev.aquired.majorver != null) && (dev.aquired.majorver > 5) && (responses['CIM_KVMRedirectionSAP'] != null)) { |
| 2310 | const kvm = ((responses['CIM_KVMRedirectionSAP'].response['EnabledState'] == 2) || (responses['CIM_KVMRedirectionSAP'].response['EnabledState'] == 6)); |
| 2311 | if (kvm == false) { |
| 2312 | // Enable KVM |
| 2313 | dev.taskCount++; |
| 2314 | dev.amtstack.CIM_KVMRedirectionSAP_RequestStateChange(2, 0, |
| 2315 | function (stack, name, response, status) { |
| 2316 | const dev = stack.dev; |
| 2317 | if (isAmtDeviceValid(dev) == false) return; // Device no longer exists, ignore this request. |
| 2318 | if (status == 200) { dev.consoleMsg("Enabled KVM."); } |
| 2319 | devTaskCompleted(dev); |
| 2320 | } |
| 2321 | ); |
| 2322 | } |
| 2323 | } |
| 2324 | |
| 2325 | // Check device name and domain name |
| 2326 | if ((dev.connType == 2) && (dev.mpsConnection != null) && (dev.mpsConnection.tag != null) && (dev.mpsConnection.tag.meiState != null) && (typeof dev.mpsConnection.tag.meiState.OsHostname == 'string') && (typeof dev.mpsConnection.tag.meiState.OsDnsSuffix == 'string')) { |
| 2327 | const generalSettings = responses['AMT_GeneralSettings'].response; |
| 2328 | if ((generalSettings['HostName'] != dev.mpsConnection.tag.meiState.OsHostname) || (generalSettings['DomainName'] != dev.mpsConnection.tag.meiState.OsDnsSuffix)) { |
| 2329 | // Change the computer and domain name |
| 2330 | generalSettings['HostName'] = dev.mpsConnection.tag.meiState.OsHostname; |
| 2331 | generalSettings['DomainName'] = dev.mpsConnection.tag.meiState.OsDnsSuffix; |
| 2332 | dev.taskCount++; |
| 2333 | dev.xname = dev.mpsConnection.tag.meiState.OsHostname + '.' + dev.mpsConnection.tag.meiState.OsDnsSuffix; |
| 2334 | dev.amtstack.Put('AMT_GeneralSettings', generalSettings, function () { |
| 2335 | const dev = stack.dev; |
| 2336 | if (isAmtDeviceValid(dev) == false) return; // Device no longer exists, ignore this request. |
| 2337 | if (status == 200) { dev.consoleMsg("Changed device name: " + dev.xname); } |
| 2338 | delete dev.xname; |
| 2339 | devTaskCompleted(dev); |
| 2340 | }, 0, 1); |
| 2341 | } |
| 2342 | } |
| 2343 | |
| 2344 | // Done |
| 2345 | devTaskCompleted(dev); |
| 2346 | } |
| 2347 | |
| 2348 | |
| 2349 | // |
| 2350 | // Intel AMT Certificate cleanup |
| 2351 | // |
| 2352 | |
| 2353 | // Remove any unused, non-trusted certificates |
| 2354 | function attemptCleanCertsSync(dev, func) { |
| 2355 | if (isAmtDeviceValid(dev) == false) return; // Device no longer exists, ignore this request. |
| 2356 | dev.amtstack.BatchEnum(null, ['AMT_PublicKeyCertificate', 'AMT_PublicPrivateKeyPair'], function (stack, name, responses, status) { |
| 2357 | const dev = stack.dev; |
| 2358 | if (isAmtDeviceValid(dev) == false) return; // Device no longer exists, ignore this request. |
| 2359 | const domain = parent.config.domains[dev.domainid]; |
| 2360 | if ((responses['AMT_PublicKeyCertificate'].status != 200) || (responses['AMT_PublicPrivateKeyPair'].status != 200)) { func(dev); return; } // We can't get the certificate list, fail and carry on. |
| 2361 | if ((responses['AMT_PublicKeyCertificate'].responses.length == 0) || (responses['AMT_PublicPrivateKeyPair'].responses.length == 0)) { func(dev); return; } // Empty certificate list, fail and carry on. |
| 2362 | |
| 2363 | // Sort out the certificates |
| 2364 | var xxCertificates = responses['AMT_PublicKeyCertificate'].responses; |
| 2365 | var xxCertPrivateKeys = responses['AMT_PublicPrivateKeyPair'].responses; |
| 2366 | for (var i in xxCertificates) { |
| 2367 | xxCertificates[i].TrustedRootCertficate = (xxCertificates[i]['TrustedRootCertficate'] == true); |
| 2368 | xxCertificates[i].X509CertificateBin = Buffer.from(xxCertificates[i]['X509Certificate'], 'base64').toString('binary'); |
| 2369 | xxCertificates[i].XIssuer = parseCertName(xxCertificates[i]['Issuer']); |
| 2370 | xxCertificates[i].XSubject = parseCertName(xxCertificates[i]['Subject']); |
| 2371 | } |
| 2372 | amtcert_linkCertPrivateKey(xxCertificates, xxCertPrivateKeys); // This links all certificates and private keys |
| 2373 | dev.certDeleteTasks = 0; |
| 2374 | |
| 2375 | // Remove any unlinked private keys |
| 2376 | for (var i in xxCertPrivateKeys) { |
| 2377 | if (!xxCertPrivateKeys[i].XCert) { |
| 2378 | dev.certDeleteTasks++; |
| 2379 | dev.amtstack.Delete('AMT_PublicPrivateKeyPair', { 'InstanceID': xxCertPrivateKeys[i]['InstanceID'] }, function (stack, name, response, status) { |
| 2380 | //if (status == 200) { dev.consoleMsg("Removed unassigned private key pair."); } |
| 2381 | if (--dev.certDeleteTasks == 0) { delete dev.certDeleteTasks; func(dev); } |
| 2382 | }); |
| 2383 | } |
| 2384 | } |
| 2385 | |
| 2386 | // Try to remove all untrusted certificates |
| 2387 | for (var i in xxCertificates) { |
| 2388 | if (xxCertificates[i].TrustedRootCertficate == false) { |
| 2389 | var privateKeyInstanceId = null; |
| 2390 | if (xxCertificates[i].XPrivateKey) { privateKeyInstanceId = { 'InstanceID': xxCertificates[i].XPrivateKey['InstanceID'] }; } |
| 2391 | dev.certDeleteTasks++; |
| 2392 | dev.amtstack.Delete('AMT_PublicKeyCertificate', { 'InstanceID': xxCertificates[i]['InstanceID'] }, function (stack, name, response, status, tag) { |
| 2393 | if ((status == 200) && (tag != null)) { |
| 2394 | // If there is a private key, delete it. |
| 2395 | dev.amtstack.Delete('AMT_PublicPrivateKeyPair', tag, function () { |
| 2396 | if (--dev.certDeleteTasks == 0) { delete dev.certDeleteTasks; func(dev); } |
| 2397 | }, 0, 1); |
| 2398 | } else { |
| 2399 | if (--dev.certDeleteTasks == 0) { delete dev.certDeleteTasks; func(dev); } |
| 2400 | } |
| 2401 | }, privateKeyInstanceId); |
| 2402 | } |
| 2403 | } |
| 2404 | }); |
| 2405 | } |
| 2406 | |
| 2407 | |
| 2408 | // |
| 2409 | // Intel AMT Hardware Inventory and Networking |
| 2410 | // |
| 2411 | |
| 2412 | function attemptFetchHardwareInventory(dev, func) { |
| 2413 | if (isAmtDeviceValid(dev) == false) return; // Device no longer exists, ignore this request. |
| 2414 | const mesh = parent.webserver.meshes[dev.meshid]; |
| 2415 | if (mesh == null) { removeAmtDevice(dev, 35); return; } |
| 2416 | if (mesh.mtype == 1) { // If this is a Intel AMT only device group, pull the hardware inventory and network information for this device |
| 2417 | dev.consoleMsg("Fetching hardware inventory."); |
| 2418 | dev.taskCount = 2; |
| 2419 | dev.taskCompleted = func; |
| 2420 | dev.amtstack.BatchEnum('', ['*CIM_ComputerSystemPackage', 'CIM_SystemPackaging', '*CIM_Chassis', 'CIM_Chip', '*CIM_Card', '*CIM_BIOSElement', 'CIM_Processor', 'CIM_PhysicalMemory', 'CIM_MediaAccessDevice', 'CIM_PhysicalPackage'], attemptFetchHardwareInventoryResponse); |
| 2421 | dev.amtstack.BatchEnum('', ['AMT_EthernetPortSettings'], attemptFetchNetworkResponse); |
| 2422 | } else { |
| 2423 | if (func) { func(dev); } |
| 2424 | } |
| 2425 | } |
| 2426 | |
| 2427 | function attemptFetchNetworkResponse(stack, name, responses, status) { |
| 2428 | const dev = stack.dev; |
| 2429 | if (isAmtDeviceValid(dev) == false) return; // Device no longer exists, ignore this request. |
| 2430 | if (status != 200) { devTaskCompleted(dev); return; } |
| 2431 | |
| 2432 | //console.log(JSON.stringify(responses, null, 2)); |
| 2433 | if ((responses['AMT_EthernetPortSettings'] == null) || (responses['AMT_EthernetPortSettings'].responses == null)) { devTaskCompleted(dev); return; } |
| 2434 | |
| 2435 | // Find the wired and wireless interfaces |
| 2436 | var wired = null, wireless = null; |
| 2437 | for (var i in responses['AMT_EthernetPortSettings'].responses) { |
| 2438 | var netif = responses['AMT_EthernetPortSettings'].responses[i]; |
| 2439 | if ((netif.MACAddress != null) && (netif.MACAddress != '00-00-00-00-00-00')) { |
| 2440 | if (netif.WLANLinkProtectionLevel != null) { wireless = netif; } else { wired = netif; } |
| 2441 | } |
| 2442 | } |
| 2443 | if ((wired == null) && (wireless == null)) { devTaskCompleted(dev); return; } |
| 2444 | |
| 2445 | // Sent by the agent to update agent network interface information |
| 2446 | var net = { netif2: {} }; |
| 2447 | |
| 2448 | if (wired != null) { |
| 2449 | var x = {}; |
| 2450 | x.family = 'IPv4'; |
| 2451 | x.type = 'ethernet'; |
| 2452 | x.address = wired.IPAddress; |
| 2453 | x.netmask = wired.SubnetMask; |
| 2454 | x.mac = wired.MACAddress.split('-').join(':').toUpperCase(); |
| 2455 | x.gateway = wired.DefaultGateway; |
| 2456 | net.netif2['Ethernet'] = [x]; |
| 2457 | } |
| 2458 | |
| 2459 | if (wireless != null) { |
| 2460 | var x = {}; |
| 2461 | x.family = 'IPv4'; |
| 2462 | x.type = 'wireless'; |
| 2463 | x.address = wireless.IPAddress; |
| 2464 | x.netmask = wireless.SubnetMask; |
| 2465 | x.mac = wireless.MACAddress.split('-').join(':').toUpperCase(); |
| 2466 | x.gateway = wireless.DefaultGateway; |
| 2467 | net.netif2['Wireless'] = [x]; |
| 2468 | } |
| 2469 | |
| 2470 | net.updateTime = Date.now(); |
| 2471 | net._id = 'if' + dev.nodeid; |
| 2472 | net.type = 'ifinfo'; |
| 2473 | parent.db.Set(net); |
| 2474 | |
| 2475 | // Event the node interface information change |
| 2476 | parent.DispatchEvent(parent.webserver.CreateMeshDispatchTargets(dev.meshid, [dev.nodeid]), obj, { action: 'ifchange', nodeid: dev.nodeid, domain: dev.nodeid.split('/')[1], nolog: 1 }); |
| 2477 | |
| 2478 | devTaskCompleted(dev); |
| 2479 | } |
| 2480 | |
| 2481 | |
| 2482 | /* |
| 2483 | // http://www.dmtf.org/sites/default/files/standards/documents/DSP0134_2.7.1.pdf |
| 2484 | const DMTFCPUStatus = ["Unknown", "Enabled", "Disabled by User", "Disabled By BIOS (POST Error)", "Idle", "Other"]; |
| 2485 | const DMTFMemType = ["Unknown", "Other", "DRAM", "Synchronous DRAM", "Cache DRAM", "EDO", "EDRAM", "VRAM", "SRAM", "RAM", "ROM", "Flash", "EEPROM", "FEPROM", "EPROM", "CDRAM", "3DRAM", "SDRAM", "SGRAM", "RDRAM", "DDR", "DDR-2", "BRAM", "FB-DIMM", "DDR3", "FBD2", "DDR4", "LPDDR", "LPDDR2", "LPDDR3", "LPDDR4"]; |
| 2486 | const DMTFMemFormFactor = ['', "Other", "Unknown", "SIMM", "SIP", "Chip", "DIP", "ZIP", "Proprietary Card", "DIMM", "TSOP", "Row of chips", "RIMM", "SODIMM", "SRIMM", "FB-DIM"]; |
| 2487 | const DMTFProcFamilly = { // Page 46 of DMTF document |
| 2488 | 191: "Intel® Core™ 2 Duo Processor", |
| 2489 | 192: "Intel® Core™ 2 Solo processor", |
| 2490 | 193: "Intel® Core™ 2 Extreme processor", |
| 2491 | 194: "Intel® Core™ 2 Quad processor", |
| 2492 | 195: "Intel® Core™ 2 Extreme mobile processor", |
| 2493 | 196: "Intel® Core™ 2 Duo mobile processor", |
| 2494 | 197: "Intel® Core™ 2 Solo mobile processor", |
| 2495 | 198: "Intel® Core™ i7 processor", |
| 2496 | 199: "Dual-Core Intel® Celeron® processor" |
| 2497 | }; |
| 2498 | */ |
| 2499 | |
| 2500 | function attemptFetchHardwareInventoryResponse(stack, name, responses, status) { |
| 2501 | const dev = stack.dev; |
| 2502 | if (isAmtDeviceValid(dev) == false) return; // Device no longer exists, ignore this request. |
| 2503 | if (status != 200) { devTaskCompleted(dev); return; } |
| 2504 | |
| 2505 | // Extract basic data |
| 2506 | var hw = {} |
| 2507 | hw.PlatformGUID = responses['CIM_ComputerSystemPackage'].response.PlatformGUID; |
| 2508 | hw.Chassis = responses['CIM_Chassis'].response; |
| 2509 | hw.Chips = responses['CIM_Chip'].responses; |
| 2510 | hw.Card = responses['CIM_Card'].response; |
| 2511 | hw.Bios = responses['CIM_BIOSElement'].response; |
| 2512 | hw.Processors = responses['CIM_Processor'].responses; |
| 2513 | hw.PhysicalMemory = responses['CIM_PhysicalMemory'].responses; |
| 2514 | hw.MediaAccessDevice = responses['CIM_MediaAccessDevice'].responses; |
| 2515 | hw.PhysicalPackage = responses['CIM_PhysicalPackage'].responses; |
| 2516 | |
| 2517 | // Convert the hardware data into the same structure as we get from Windows |
| 2518 | var hw2 = { hardware: { windows: {}, identifiers: {} } }; |
| 2519 | hw2.hardware.identifiers.product_uuid = guidToStr(hw.PlatformGUID); |
| 2520 | if ((hw.PhysicalMemory != null) && (hw.PhysicalMemory.length > 0)) { |
| 2521 | var memory = []; |
| 2522 | for (var i in hw.PhysicalMemory) { |
| 2523 | var m2 = {}, m = hw.PhysicalMemory[i]; |
| 2524 | m2.BankLabel = m.BankLabel; |
| 2525 | m2.Capacity = m.Capacity; |
| 2526 | if (typeof m.PartNumber == 'string') { m2.PartNumber = m.PartNumber.trim(); } |
| 2527 | if (typeof m.PartNumber == 'number') { m2.PartNumber = m.PartNumber; } |
| 2528 | if (typeof m.SerialNumber == 'string') { m2.SerialNumber = m.SerialNumber.trim(); } |
| 2529 | if (typeof m.SerialNumber == 'number') { m2.SerialNumber = m.SerialNumber; } |
| 2530 | if (typeof m.Manufacturer == 'string') { m2.Manufacturer = m.Manufacturer.trim(); } |
| 2531 | if (typeof m.Manufacturer == 'number') { m2.Manufacturer = m.Manufacturer; } |
| 2532 | memory.push(m2); |
| 2533 | } |
| 2534 | hw2.hardware.windows.memory = memory; |
| 2535 | } |
| 2536 | if ((hw.MediaAccessDevice != null) && (hw.MediaAccessDevice.length > 0)) { |
| 2537 | var drives = []; |
| 2538 | for (var i in hw.MediaAccessDevice) { |
| 2539 | var m2 = {}, m = hw.MediaAccessDevice[i]; |
| 2540 | m2.Caption = m.DeviceID; |
| 2541 | if (m.MaxMediaSize) { m2.Size = (m.MaxMediaSize * 1000); } |
| 2542 | drives.push(m2); |
| 2543 | } |
| 2544 | hw2.hardware.identifiers.storage_devices = drives; |
| 2545 | } |
| 2546 | if (hw.Bios != null) { |
| 2547 | if (typeof hw.Bios.Manufacturer == 'string') { hw2.hardware.identifiers.bios_vendor = hw.Bios.Manufacturer.trim(); } |
| 2548 | if (typeof hw.Bios.Manufacturer == 'number') { hw2.hardware.identifiers.bios_vendor = hw.Bios.Manufacturer; } |
| 2549 | hw2.hardware.identifiers.bios_version = hw.Bios.Version; |
| 2550 | if (hw.Bios.ReleaseDate && hw.Bios.ReleaseDate.Datetime) { hw2.hardware.identifiers.bios_date = hw.Bios.ReleaseDate.Datetime; } |
| 2551 | } |
| 2552 | if (hw.PhysicalPackage != null) { |
| 2553 | if (typeof hw.Card.Model == 'string') { hw2.hardware.identifiers.board_name = hw.Card.Model.trim(); } |
| 2554 | if (typeof hw.Card.Model == 'number') { hw2.hardware.identifiers.board_name = hw.Card.Model; } |
| 2555 | if (typeof hw.Card.Manufacturer == 'string') { hw2.hardware.identifiers.board_vendor = hw.Card.Manufacturer.trim(); } |
| 2556 | if (typeof hw.Card.Manufacturer == 'number') { hw2.hardware.identifiers.board_vendor = hw.Card.Manufacturer; } |
| 2557 | if (typeof hw.Card.Version == 'string') { hw2.hardware.identifiers.board_version = hw.Card.Version.trim(); } |
| 2558 | if (typeof hw.Card.Version == 'number') { hw2.hardware.identifiers.board_version = hw.Card.Version; } |
| 2559 | if (typeof hw.Card.SerialNumber == 'string') { hw2.hardware.identifiers.board_serial = hw.Card.SerialNumber.trim(); } |
| 2560 | if (typeof hw.Card.SerialNumber == 'number') { hw2.hardware.identifiers.board_serial = hw.Card.SerialNumber; } |
| 2561 | } |
| 2562 | if ((hw.Chips != null) && (hw.Chips.length > 0)) { |
| 2563 | for (var i in hw.Chips) { |
| 2564 | if ((hw.Chips[i].ElementName == 'Managed System Processor Chip') && (hw.Chips[i].Version)) { |
| 2565 | hw2.hardware.identifiers.cpu_name = hw.Chips[i].Version; |
| 2566 | } |
| 2567 | } |
| 2568 | } |
| 2569 | |
| 2570 | // Compute the hash of the document |
| 2571 | hw2.hash = parent.crypto.createHash('sha384').update(JSON.stringify(hw2)).digest().toString('hex'); |
| 2572 | |
| 2573 | // Fetch system information |
| 2574 | parent.db.GetHash('si' + dev.nodeid, function (err, results) { |
| 2575 | var sysinfohash = null; |
| 2576 | if ((results != null) && (results.length == 1)) { sysinfohash = results[0].hash; } |
| 2577 | if (sysinfohash != hw2.hash) { |
| 2578 | // Hardware information has changed, update the database |
| 2579 | hw2._id = 'si' + dev.nodeid; |
| 2580 | hw2.domain = dev.nodeid.split('/')[1]; |
| 2581 | hw2.time = Date.now(); |
| 2582 | hw2.type = 'sysinfo'; |
| 2583 | parent.db.Set(hw2); |
| 2584 | |
| 2585 | // Event the new sysinfo hash, this will notify everyone that the sysinfo document was changed |
| 2586 | var event = { etype: 'node', action: 'sysinfohash', nodeid: dev.nodeid, domain: hw2.domain, hash: hw2.hash, nolog: 1 }; |
| 2587 | parent.DispatchEvent(parent.webserver.CreateMeshDispatchTargets(dev.meshid, [dev.nodeid]), obj, event); |
| 2588 | } |
| 2589 | }); |
| 2590 | |
| 2591 | devTaskCompleted(dev); |
| 2592 | } |
| 2593 | |
| 2594 | |
| 2595 | // |
| 2596 | // Intel AMT Activation |
| 2597 | // |
| 2598 | |
| 2599 | function activateIntelAmt(dev) { |
| 2600 | // Find the Intel AMT policy |
| 2601 | const mesh = parent.webserver.meshes[dev.meshid]; |
| 2602 | if (mesh == null) { dev.consoleMsg("Unable to find device group (" + dev.meshid + ")."); removeAmtDevice(dev, 36); return false; } |
| 2603 | var amtPolicy = 0; // 0 = Do nothing, 1 = Deactivate CCM, 2 = CCM, 3 = ACM |
| 2604 | var ccmPolicy = 0; // Only used when in ACM policy: 0 = Do nothing, 1 = Deactivate CCM, 2 = CCM is ACM fails |
| 2605 | if (mesh.amt != null) { if (typeof mesh.amt.type == 'number') { amtPolicy = mesh.amt.type; } if (typeof mesh.amt.ccm == 'number') { ccmPolicy = mesh.amt.ccm; } } |
| 2606 | if ((typeof dev.mpsConnection.tag.meiState.OsAdmin != 'object') || (typeof dev.mpsConnection.tag.meiState.OsAdmin.user != 'string') || (typeof dev.mpsConnection.tag.meiState.OsAdmin.pass != 'string')) { amtPolicy = 0; } |
| 2607 | if (amtPolicy == 0) { removeAmtDevice(dev, 37); return false; } // Do nothing, we should not have gotten this CIRA-LMS connection. |
| 2608 | if (amtPolicy == 2) { activateIntelAmtCcm(dev, mesh.amt.password); } // Activate to CCM policy |
| 2609 | if ((amtPolicy == 3) || (amtPolicy == 4)) { // Activate to ACM policy |
| 2610 | var acminfo = checkAcmActivation(dev); |
| 2611 | if ((acminfo == null) || (acminfo.err != null)) { |
| 2612 | // No opportunity to activate to ACM, check if we are in CCM |
| 2613 | if ((dev.mpsConnection.tag.meiState.Flags & 2) != 0) { |
| 2614 | if ((amtPolicy == 3) && (ccmPolicy == 1)) { deactivateIntelAmtCCM(dev); } // If we are in ACM policy and CCM is not allowed, deactivate it now. |
| 2615 | else { return true; } // We are in CCM, keep going |
| 2616 | } else { |
| 2617 | // We are not in CCM, go to CCM now |
| 2618 | if ((amtPolicy == 4) || ((amtPolicy == 3) && (ccmPolicy == 2))) { activateIntelAmtCcm(dev, mesh.amt.password); } // If we are in full automatic or ACM with CCM allowed, setup CCM. |
| 2619 | else { |
| 2620 | // Unable to find an activation match. |
| 2621 | if (acminfo == null) { dev.consoleMsg("No opportunity for ACM activation."); } else { dev.consoleMsg("No opportunity for ACM activation: " + acminfo.err); } |
| 2622 | removeAmtDevice(dev, 38); |
| 2623 | return false; // We are not in CCM and policy restricts use of CCM, so exit now. |
| 2624 | } |
| 2625 | } |
| 2626 | } else { |
| 2627 | // Found a certificate to activate to ACM. |
| 2628 | if ((dev.mpsConnection.tag.meiState.Flags & 2) != 0) { |
| 2629 | // We are in CCM, deactivate CCM first. |
| 2630 | deactivateIntelAmtCCM(dev); |
| 2631 | } else { |
| 2632 | // We are not activated now, go to ACM directly. |
| 2633 | // Check if we are allowed to perform TLS ACM activation |
| 2634 | var TlsAcmActivation = false; |
| 2635 | var domain = parent.config.domains[dev.domainid]; |
| 2636 | if (domain && domain.amtmanager && (domain.amtmanager.tlsacmactivation == true)) { TlsAcmActivation = true; } |
| 2637 | |
| 2638 | // Check Intel AMT version |
| 2639 | if (typeof dev.intelamt.ver == 'string') { |
| 2640 | var verSplit = dev.intelamt.ver.split('.'); |
| 2641 | if (verSplit.length >= 2) { |
| 2642 | dev.aquired.majorver = parseInt(verSplit[0]); |
| 2643 | dev.aquired.minorver = parseInt(verSplit[1]); |
| 2644 | if (verSplit.length >= 3) { dev.aquired.maintenancever = parseInt(verSplit[2]); } |
| 2645 | } |
| 2646 | } |
| 2647 | |
| 2648 | // If this is Intel AMT 14 or better and allowed, we are going to attempt a host-based end-to-end TLS activation. |
| 2649 | if (TlsAcmActivation && (dev.aquired.majorver >= 14)) { |
| 2650 | // Perform host-based TLS ACM activation |
| 2651 | activateIntelAmtTlsAcm(dev, mesh.amt.password, acminfo); |
| 2652 | } else { |
| 2653 | // Perform host-based ACM activation |
| 2654 | activateIntelAmtAcm(dev, mesh.amt.password, acminfo); |
| 2655 | } |
| 2656 | } |
| 2657 | } |
| 2658 | } |
| 2659 | return false; |
| 2660 | } |
| 2661 | |
| 2662 | function activateIntelAmtCcm(dev, password) { |
| 2663 | // Generate a random Intel AMT password if needed |
| 2664 | if ((password == null) || (password == '')) { password = getRandomAmtPassword(); } |
| 2665 | dev.temp = { pass: password }; |
| 2666 | |
| 2667 | // Setup the WSMAN stack, no TLS |
| 2668 | var comm = CreateWsmanComm(dev.nodeid, 16992, dev.mpsConnection.tag.meiState.OsAdmin.user, dev.mpsConnection.tag.meiState.OsAdmin.pass, 0, null, dev.mpsConnection); // No TLS |
| 2669 | var wsstack = WsmanStackCreateService(comm); |
| 2670 | dev.amtstack = AmtStackCreateService(wsstack); |
| 2671 | dev.amtstack.dev = dev; |
| 2672 | dev.amtstack.BatchEnum(null, ['*AMT_GeneralSettings', '*IPS_HostBasedSetupService'], activateIntelAmtCcmEx1); |
| 2673 | } |
| 2674 | |
| 2675 | function activateIntelAmtCcmEx1(stack, name, responses, status) { |
| 2676 | const dev = stack.dev; |
| 2677 | if (isAmtDeviceValid(dev) == false) return; // Device no longer exists, ignore this request. |
| 2678 | if (status != 200) { dev.consoleMsg("Failed to get Intel AMT state."); removeAmtDevice(dev, 39); return; } |
| 2679 | if (responses['IPS_HostBasedSetupService'].response['AllowedControlModes'].length != 2) { dev.consoleMsg("Client control mode activation not allowed."); removeAmtDevice(dev, 40); return; } |
| 2680 | |
| 2681 | // Log the activation request, logging is a required step for activation. |
| 2682 | var domain = parent.config.domains[dev.domainid]; |
| 2683 | if (domain == null) { dev.consoleMsg("Invalid domain."); removeAmtDevice(dev, 41); return; } |
| 2684 | if (parent.certificateOperations.logAmtActivation(domain, { time: new Date(), action: 'ccmactivate', domain: dev.domainid, amtUuid: dev.mpsConnection.tag.meiState.UUID, amtRealm: responses['AMT_GeneralSettings'].response['DigestRealm'], user: 'admin', password: dev.temp.pass, ipport: dev.mpsConnection.remoteAddr + ':' + dev.mpsConnection.remotePort, nodeid: dev.nodeid, meshid: dev.meshid, computerName: dev.name }) == false) { |
| 2685 | dev.consoleMsg("Unable to log operation."); removeAmtDevice(dev, 42); return; |
| 2686 | } |
| 2687 | |
| 2688 | // Perform CCM activation |
| 2689 | dev.amtstack.IPS_HostBasedSetupService_Setup(2, hex_md5('admin:' + responses['AMT_GeneralSettings'].response['DigestRealm'] + ':' + dev.temp.pass).substring(0, 32), null, null, null, null, activateIntelAmtCcmEx2); |
| 2690 | } |
| 2691 | |
| 2692 | function activateIntelAmtCcmEx2(stack, name, responses, status) { |
| 2693 | const dev = stack.dev; |
| 2694 | if (isAmtDeviceValid(dev) == false) return; // Device no longer exists, ignore this request. |
| 2695 | if (status != 200) { dev.consoleMsg("Failed to activate Intel AMT to CCM."); removeAmtDevice(dev, 43); return; } |
| 2696 | |
| 2697 | // Update the device |
| 2698 | dev.aquired = {}; |
| 2699 | dev.aquired.controlMode = 1; // 1 = CCM, 2 = ACM |
| 2700 | if (typeof dev.amtstack.wsman.comm.amtVersion == 'string') { |
| 2701 | var verSplit = dev.amtstack.wsman.comm.amtVersion.split('.'); |
| 2702 | if (verSplit.length >= 2) { |
| 2703 | dev.aquired.version = verSplit[0] + '.' + verSplit[1]; |
| 2704 | dev.aquired.majorver = parseInt(verSplit[0]); |
| 2705 | dev.aquired.minorver = parseInt(verSplit[1]); |
| 2706 | if (verSplit.length >= 3) { |
| 2707 | dev.aquired.version = verSplit[0] + '.' + verSplit[1] + '.' + verSplit[2]; |
| 2708 | dev.aquired.maintenancever = parseInt(verSplit[2]); |
| 2709 | } |
| 2710 | } |
| 2711 | } |
| 2712 | if ((typeof dev.mpsConnection.tag.meiState.OsHostname == 'string') && (typeof dev.mpsConnection.tag.meiState.OsDnsSuffix == 'string')) { |
| 2713 | dev.aquired.host = dev.mpsConnection.tag.meiState.OsHostname + '.' + dev.mpsConnection.tag.meiState.OsDnsSuffix; |
| 2714 | } |
| 2715 | dev.aquired.realm = dev.amtstack.wsman.comm.digestRealm; |
| 2716 | dev.intelamt.user = dev.aquired.user = 'admin'; |
| 2717 | dev.intelamt.pass = dev.aquired.pass = dev.temp.pass; |
| 2718 | dev.intelamt.tls = dev.aquired.tls = 0; |
| 2719 | dev.aquired.lastContact = Date.now(); |
| 2720 | dev.aquired.state = 2; // Activated |
| 2721 | dev.aquired.warn = 0; // Clear all warnings |
| 2722 | delete dev.acctry; |
| 2723 | delete dev.temp; |
| 2724 | UpdateDevice(dev); |
| 2725 | |
| 2726 | // Success, switch to managing this device |
| 2727 | obj.parent.mpsserver.SendJsonControl(dev.mpsConnection, { action: 'mestate' }); // Request an MEI state refresh |
| 2728 | dev.consoleMsg("Succesfully activated in CCM mode, holding 10 seconds..."); |
| 2729 | |
| 2730 | // Wait 10 seconds before attempting to manage this device in CCM |
| 2731 | var f = function doManage() { if (isAmtDeviceValid(dev)) { attemptInitialContact(doManage.dev); } } |
| 2732 | f.dev = dev; |
| 2733 | setTimeout(f, 10000); |
| 2734 | } |
| 2735 | |
| 2736 | // Check if this device has any way to be activated in ACM using our server certificates. |
| 2737 | function checkAcmActivation(dev) { |
| 2738 | var domain = parent.config.domains[dev.domainid]; |
| 2739 | if ((domain == null) || (domain.amtacmactivation == null) || (domain.amtacmactivation.certs == null) || (domain.amtacmactivation.certs.length == 0)) return { err: "Server does not have any ACM activation certificates." }; |
| 2740 | const activationCerts = domain.amtacmactivation.certs; |
| 2741 | if ((dev.mpsConnection.tag.meiState == null) || (dev.mpsConnection.tag.meiState.Hashes == null) || (dev.mpsConnection.tag.meiState.Hashes.length == 0)) return { err: "Intel AMT did not report any trusted hashes." }; |
| 2742 | const deviceHashes = dev.mpsConnection.tag.meiState.Hashes; |
| 2743 | |
| 2744 | // Get the trusted FQDN of the device |
| 2745 | var trustedFqdn = null; |
| 2746 | if (dev.mpsConnection.tag.meiState.OsDnsSuffix != null) { trustedFqdn = dev.mpsConnection.tag.meiState.OsDnsSuffix; } |
| 2747 | if (dev.mpsConnection.tag.meiState.DnsSuffix != null) { trustedFqdn = dev.mpsConnection.tag.meiState.DnsSuffix; } |
| 2748 | if (trustedFqdn == null) return { err: "No trusted DNS suffix reported" }; |
| 2749 | |
| 2750 | // Find a matching certificate |
| 2751 | var gotSuffixMatch = false; |
| 2752 | var devValidHash = false; |
| 2753 | for (var i in activationCerts) { |
| 2754 | var cert = activationCerts[i]; |
| 2755 | var certDnsMatch = checkAcmActivationCertName(cert.cn, trustedFqdn); |
| 2756 | if (certDnsMatch == true) { gotSuffixMatch = true; } |
| 2757 | if ((cert.cn == '*') || certDnsMatch) { |
| 2758 | for (var j in deviceHashes) { |
| 2759 | var hashInfo = deviceHashes[j]; |
| 2760 | if ((hashInfo != null) && (hashInfo.isActive == 1)) { |
| 2761 | devValidHash = true; |
| 2762 | if ((hashInfo.hashAlgorithmStr == 'SHA256') && (hashInfo.certificateHash.toLowerCase() == cert.sha256)) { return { cert: cert, fqdn: trustedFqdn, hash: cert.sha256 }; } // Found a match |
| 2763 | else if ((hashInfo.hashAlgorithmStr == 'SHA1') && (hashInfo.certificateHash.toLowerCase() == cert.sha1)) { return { cert: cert, fqdn: trustedFqdn, hash: cert.sha1 }; } // Found a match |
| 2764 | } |
| 2765 | } |
| 2766 | } |
| 2767 | } |
| 2768 | if (!devValidHash) { return { err: "Intel AMT has no trusted root hashes for \"" + trustedFqdn + "\"." }; } // Found no trusted root hashes |
| 2769 | if (gotSuffixMatch) { return { err: "Certificate root hash matching failed for \"" + trustedFqdn + "\"." }; } // Found a DNS suffix match, but root hash failed to match. |
| 2770 | return { err: "No matching ACM activation certificate for \"" + trustedFqdn + "\"." }; // Did not find a match |
| 2771 | } |
| 2772 | |
| 2773 | // Return true if the trusted FQDN matched the certificate common name |
| 2774 | function checkAcmActivationCertName(commonName, trustedFqdn) { |
| 2775 | commonName = commonName.toLowerCase(); |
| 2776 | trustedFqdn = trustedFqdn.toLowerCase(); |
| 2777 | if (commonName.startsWith('*.') && (commonName.length > 2)) { commonName = commonName.substring(2); } |
| 2778 | return ((commonName == trustedFqdn) || (trustedFqdn.endsWith('.' + commonName))); |
| 2779 | } |
| 2780 | |
| 2781 | // Attempt Intel AMT TLS ACM activation |
| 2782 | function activateIntelAmtTlsAcm(dev, password, acminfo) { |
| 2783 | // Check if MeshAgent/MeshCMD can support the startConfigurationhostB() call. |
| 2784 | if ((dev.mpsConnection != null) && (dev.mpsConnection.tag != null) && (dev.mpsConnection.tag.meiState != null) && (typeof dev.mpsConnection.tag.meiState['core-ver'] == 'number') && (dev.mpsConnection.tag.meiState['core-ver'] > 0)) { |
| 2785 | // Generate a random Intel AMT password if needed |
| 2786 | if ((password == null) || (password == '')) { password = getRandomAmtPassword(); } |
| 2787 | dev.temp = { pass: password, acminfo: acminfo }; |
| 2788 | |
| 2789 | // Get our ACM activation certificate chain |
| 2790 | var acmTlsInfo = parent.certificateOperations.getAcmCertChain(parent.config.domains[dev.domainid], dev.temp.acminfo.fqdn, dev.temp.acminfo.hash); |
| 2791 | if (acmTlsInfo.error == 1) { dev.consoleMsg(acmTlsInfo.errorText); removeAmtDevice(dev, 44); return; } |
| 2792 | dev.acmTlsInfo = acmTlsInfo; |
| 2793 | |
| 2794 | // Send the MEI command to enable TLS connections |
| 2795 | dev.consoleMsg("Performing TLS ACM activation..."); |
| 2796 | dev.controlMsg({ action: 'startTlsHostConfig', hash: acmTlsInfo.hash256, hostVpn: false, dnsSuffixList: null }); // TODO: Use SHA384 is possible. |
| 2797 | } else { |
| 2798 | // MeshCore or MeshCMD is to old |
| 2799 | dev.consoleMsg("This software is to old to support ACM activation, pleasse update and try again."); |
| 2800 | removeAmtDevice(dev); |
| 2801 | } |
| 2802 | } |
| 2803 | |
| 2804 | // Attempt Intel AMT TLS ACM activation after startConfiguration() is called on remote device |
| 2805 | function activateIntelAmtTlsAcmEx(dev, startConfigData) { |
| 2806 | if ((startConfigData == null) || (startConfigData.status != 0) || (typeof startConfigData.hash != 'string')) { |
| 2807 | // Unable to call startTlsHostConfig on remote host. |
| 2808 | dev.consoleMsg("Failed to startConfigurationHBased(), status = " + startConfigData.status); |
| 2809 | removeAmtDevice(dev); |
| 2810 | } else { |
| 2811 | // Setup the WSMAN stack, no TLS |
| 2812 | dev.consoleMsg("Attempting TLS connection..."); |
| 2813 | var comm = CreateWsmanComm(dev.nodeid, 16993, 'admin', '', 1, { cert: dev.acmTlsInfo.certs.join(''), key: dev.acmTlsInfo.signkey }, dev.mpsConnection); // TLS with client certificate chain and key. |
| 2814 | comm.xtlsFingerprint = startConfigData.hash.toLowerCase(); // Intel AMT leaf TLS cert need to match this hash (SHA256 or SHA384) |
| 2815 | var wsstack = WsmanStackCreateService(comm); |
| 2816 | dev.amtstack = AmtStackCreateService(wsstack); |
| 2817 | dev.amtstack.dev = dev; |
| 2818 | dev.amtstack.BatchEnum(null, ['*AMT_GeneralSettings', 'CIM_SoftwareIdentity', '*AMT_SetupAndConfigurationService'], activateIntelAmtTlsAcmEx1); |
| 2819 | } |
| 2820 | } |
| 2821 | |
| 2822 | function activateIntelAmtTlsAcmEx1(stack, name, responses, status) { |
| 2823 | const dev = stack.dev; |
| 2824 | if (isAmtDeviceValid(dev) == false) return; // Device no longer exists, ignore this request. |
| 2825 | |
| 2826 | // Check if we succesfully connected |
| 2827 | if (status != 200) { |
| 2828 | dev.consoleMsg("Failed to perform ACM TLS connection, status " + status + "."); |
| 2829 | //activateIntelAmtAcm(dev); // It's possible to fallback to legacy WSMAN ACM activation here if we needed to.. |
| 2830 | removeAmtDevice(dev); |
| 2831 | return; |
| 2832 | } |
| 2833 | |
| 2834 | // Fetch the Intel AMT version from WSMAN |
| 2835 | if ((responses != null) && (responses['CIM_SoftwareIdentity'] != null) && (responses['CIM_SoftwareIdentity'].responses != null)) { |
| 2836 | var amtlogicalelements = []; |
| 2837 | amtlogicalelements = responses['CIM_SoftwareIdentity'].responses; |
| 2838 | if (responses['AMT_SetupAndConfigurationService'] != null && responses['AMT_SetupAndConfigurationService'].response != null) { |
| 2839 | amtlogicalelements.push(responses['AMT_SetupAndConfigurationService'].response); |
| 2840 | } |
| 2841 | if (amtlogicalelements.length > 0) { |
| 2842 | var vs = getInstance(amtlogicalelements, 'AMT')['VersionString']; |
| 2843 | if (vs != null) { |
| 2844 | dev.aquired.version = vs; |
| 2845 | version = dev.aquired.version.split('.') |
| 2846 | dev.aquired.versionmajor = parseInt(version[0]); |
| 2847 | dev.aquired.versionminor = parseInt(version[1]); |
| 2848 | if (version.length > 2) { dev.aquired.versionmaintenance = parseInt(version[2]); } |
| 2849 | } |
| 2850 | } |
| 2851 | } |
| 2852 | |
| 2853 | // Fetch the Intel AMT version from HTTP stack |
| 2854 | if ((dev.amtversionstr == null) && (stack.wsman.comm.amtVersion != null)) { |
| 2855 | var s = stack.wsman.comm.amtVersion.split('.'); |
| 2856 | if (s.length >= 2) { |
| 2857 | dev.aquired.version = s[0] + '.' + s[1] + '.'; |
| 2858 | dev.aquired.versionmajor = parseInt(s[0]); |
| 2859 | dev.aquired.versionminor = parseInt(s[1]); |
| 2860 | if (s.length >= 3) { |
| 2861 | dev.aquired.version = s[0] + '.' + s[1] + '.' + s[2]; |
| 2862 | dev.aquired.versionmaintenance = parseInt(s[2]); |
| 2863 | } |
| 2864 | } |
| 2865 | } |
| 2866 | |
| 2867 | // If we can't get the Intel AMT version, stop here. |
| 2868 | if (dev.aquired.version == null) { dev.consoleMsg('Could not get Intel AMT version.'); removeAmtDevice(dev); return; } // Could not get Intel AMT version, disconnect(); |
| 2869 | |
| 2870 | // Get the digest realm |
| 2871 | if (responses['AMT_GeneralSettings'] && responses['AMT_GeneralSettings'].response && (typeof responses['AMT_GeneralSettings'].response['DigestRealm'] == 'string')) { |
| 2872 | // Set the realm in the stack since we are not doing HTTP digest and this will be checked later by different code. |
| 2873 | dev.aquired.realm = dev.amtstack.wsman.comm.digestRealm = responses['AMT_GeneralSettings'].response['DigestRealm']; |
| 2874 | } else { |
| 2875 | dev.consoleMsg('Could not get Intel AMT digest realm.'); removeAmtDevice(dev); return; |
| 2876 | } |
| 2877 | |
| 2878 | // Looks like we are doing well. |
| 2879 | dev.consoleMsg('Succesful TLS connection, Intel AMT v' + dev.aquired.version); |
| 2880 | |
| 2881 | // Log this activation event |
| 2882 | var event = { etype: 'node', action: 'amtactivate', nodeid: dev.nodeid, domain: dev.domainid, msgid: 111, msgArgs: [dev.temp.acminfo.fqdn], msg: 'Device requested Intel(R) AMT ACM TLS activation, FQDN: ' + dev.temp.acminfo.fqdn }; |
| 2883 | if (parent.db.changeStream) { event.noact = 1; } // If DB change stream is active, don't use this event to change the node. Another event will come. |
| 2884 | parent.DispatchEvent(parent.webserver.CreateMeshDispatchTargets(dev.meshid, [dev.nodeid]), obj, event); |
| 2885 | |
| 2886 | // Log the activation request, logging is a required step for activation. |
| 2887 | var domain = parent.config.domains[dev.domainid]; |
| 2888 | if (domain == null) { dev.consoleMsg("Invalid domain."); removeAmtDevice(dev, 41); return; } |
| 2889 | if (parent.certificateOperations.logAmtActivation(domain, { time: new Date(), action: 'acmactivate-tls', domain: dev.domainid, amtUuid: dev.mpsConnection.tag.meiState.UUID, amtRealm: dev.aquired.realm, user: 'admin', password: dev.temp.pass, ipport: dev.mpsConnection.remoteAddr + ':' + dev.mpsConnection.remotePort, nodeid: dev.nodeid, meshid: dev.meshid, computerName: dev.name }) == false) { |
| 2890 | dev.consoleMsg("Unable to log operation."); removeAmtDevice(dev, 42); return; |
| 2891 | } |
| 2892 | |
| 2893 | // See what admin password to use |
| 2894 | dev.aquired.user = 'admin'; |
| 2895 | dev.aquired.pass = dev.temp.pass; |
| 2896 | |
| 2897 | // Set the account password |
| 2898 | if (typeof dev.temp.mebxpass == 'string') { |
| 2899 | // Set the new MEBx password |
| 2900 | dev.consoleMsg('Setting MEBx password...'); |
| 2901 | dev.amtstack.AMT_SetupAndConfigurationService_SetMEBxPassword(dev.temp.mebxpass, activateIntelAmtTlsAcmEx2); |
| 2902 | } else { |
| 2903 | // Set the admin password |
| 2904 | dev.consoleMsg('Setting admin password...'); |
| 2905 | dev.amtstack.AMT_AuthorizationService_SetAdminAclEntryEx(dev.aquired.user, hex_md5(dev.aquired.user + ':' + dev.aquired.realm + ':' + dev.aquired.pass), activateIntelAmtTlsAcmEx3); |
| 2906 | } |
| 2907 | } |
| 2908 | |
| 2909 | // Response from setting MEBx password |
| 2910 | function activateIntelAmtTlsAcmEx2(stack, name, responses, status) { |
| 2911 | const dev = stack.dev; |
| 2912 | if (isAmtDeviceValid(dev) == false) return; // Device no longer exists, ignore this request. |
| 2913 | if (status != 200) { dev.consoleMsg('Failed to set MEBx password, status=' + status + '.'); destroyDevice(dev); return; } |
| 2914 | dev.consoleMsg('MEBx password set. Setting admin password...'); |
| 2915 | |
| 2916 | // Set the admin password |
| 2917 | dev.amtstack.AMT_AuthorizationService_SetAdminAclEntryEx(dev.aquired.user, hex_md5(dev.aquired.user + ':' + dev.aquired.realm + ':' + dev.aquired.pass), activateIntelAmtTlsAcmEx3); |
| 2918 | } |
| 2919 | |
| 2920 | // Response from setting admin password |
| 2921 | function activateIntelAmtTlsAcmEx3(stack, name, responses, status) { |
| 2922 | const dev = stack.dev; |
| 2923 | if (isAmtDeviceValid(dev) == false) return; // Device no longer exists, ignore this request. |
| 2924 | if (status != 200) { dev.consoleMsg('Failed to set admin password, status=' + status + '.'); removeAmtDevice(dev); return; } |
| 2925 | dev.consoleMsg('Admin password set.'); |
| 2926 | |
| 2927 | // Switch the state of Intel AMT. |
| 2928 | if ((dev.mpsConnection != null) && (dev.mpsConnection.tag != null) && (dev.mpsConnection.tag.meiState != null)) { dev.mpsConnection.tag.meiState.ProvisioningState = 2; } |
| 2929 | dev.aquired.controlMode = 2; // 1 = CCM, 2 = ACM |
| 2930 | dev.aquired.state = 2; // Activated |
| 2931 | dev.hbacmtls = 1; // Indicate that we are doing a Host |
| 2932 | |
| 2933 | // Proceed to going the normal Intel AMT sync. This will trigger a commit when the TLS cert is setup. |
| 2934 | dev.amtstack.BatchEnum(null, ['*AMT_GeneralSettings', '*IPS_HostBasedSetupService'], attemptLocalConnectResponse); |
| 2935 | } |
| 2936 | |
| 2937 | // Attempt Intel AMT ACM activation |
| 2938 | function activateIntelAmtAcm(dev, password, acminfo) { |
| 2939 | // Check if MeshAgent/MeshCMD can support the stopConfiguration() call. |
| 2940 | if ((dev.mpsConnection != null) && (dev.mpsConnection.tag != null) && (dev.mpsConnection.tag.meiState != null) && (typeof dev.mpsConnection.tag.meiState['core-ver'] == 'number') && (dev.mpsConnection.tag.meiState['core-ver'] > 0)) { |
| 2941 | // Generate a random Intel AMT password if needed |
| 2942 | if (acminfo != null) { |
| 2943 | if ((password == null) || (password == '')) { password = getRandomAmtPassword(); } |
| 2944 | dev.temp = { pass: password, acminfo: acminfo }; |
| 2945 | } |
| 2946 | dev.acmactivate = 1; |
| 2947 | |
| 2948 | // Send the MEI command to stop configuration. |
| 2949 | // If Intel AMT is "in-provisioning" mode, the WSMAN ACM activation will not work, so we need to do this first. |
| 2950 | dev.consoleMsg("Getting ready for ACM activation..."); |
| 2951 | dev.controlMsg({ action: 'stopConfiguration' }); |
| 2952 | } else { |
| 2953 | // MeshCore or MeshCMD is to old |
| 2954 | dev.consoleMsg("This software is to old to support ACM activation, pleasse update and try again."); |
| 2955 | removeAmtDevice(dev); |
| 2956 | } |
| 2957 | } |
| 2958 | |
| 2959 | function activateIntelAmtAcmEx0(dev) { |
| 2960 | if (isAmtDeviceValid(dev) == false) return; // Device no longer exists, ignore this request. |
| 2961 | |
| 2962 | // Setup the WSMAN stack, no TLS |
| 2963 | var comm = CreateWsmanComm(dev.nodeid, 16992, dev.mpsConnection.tag.meiState.OsAdmin.user, dev.mpsConnection.tag.meiState.OsAdmin.pass, 0, null, dev.mpsConnection); // No TLS |
| 2964 | var wsstack = WsmanStackCreateService(comm); |
| 2965 | dev.amtstack = AmtStackCreateService(wsstack); |
| 2966 | dev.amtstack.dev = dev; |
| 2967 | dev.amtstack.BatchEnum(null, ['*AMT_GeneralSettings', '*IPS_HostBasedSetupService'], activateIntelAmtAcmEx1); |
| 2968 | } |
| 2969 | |
| 2970 | function activateIntelAmtAcmEx1(stack, name, responses, status) { |
| 2971 | const dev = stack.dev; |
| 2972 | if (isAmtDeviceValid(dev) == false) return; // Device no longer exists, ignore this request. |
| 2973 | if (status != 200) { dev.consoleMsg("Failed to get Intel AMT state."); removeAmtDevice(dev, 46); return; } |
| 2974 | |
| 2975 | // Sign the Intel AMT ACM activation request |
| 2976 | var info = { nonce: responses['IPS_HostBasedSetupService'].response['ConfigurationNonce'], realm: responses['AMT_GeneralSettings'].response['DigestRealm'], fqdn: dev.temp.acminfo.fqdn, hash: dev.temp.acminfo.hash, uuid: dev.mpsConnection.tag.meiState.UUID }; |
| 2977 | var acmdata = parent.certificateOperations.signAcmRequest(parent.config.domains[dev.domainid], info, 'admin', dev.temp.pass, dev.mpsConnection.remoteAddr + ':' + dev.mpsConnection.remotePort, dev.nodeid, dev.meshid, dev.name, 0); |
| 2978 | if (acmdata == null) { dev.consoleMsg("Failed to sign ACM nonce."); removeAmtDevice(dev, 47); return; } |
| 2979 | if (acmdata.error != null) { dev.consoleMsg(acmdata.errorText); removeAmtDevice(dev, 48); return; } |
| 2980 | |
| 2981 | // Log this activation event |
| 2982 | var event = { etype: 'node', action: 'amtactivate', nodeid: dev.nodeid, domain: dev.domainid, msgid: 58, msgArgs: [ dev.temp.acminfo.fqdn ], msg: 'Device requested Intel(R) AMT ACM activation, FQDN: ' + dev.temp.acminfo.fqdn }; |
| 2983 | if (parent.db.changeStream) { event.noact = 1; } // If DB change stream is active, don't use this event to change the node. Another event will come. |
| 2984 | parent.DispatchEvent(parent.webserver.CreateMeshDispatchTargets(dev.meshid, [dev.nodeid]), obj, event); |
| 2985 | |
| 2986 | // Start the activation process |
| 2987 | dev.temp.acmdata = acmdata; |
| 2988 | dev.temp.acmdata.index = 0; |
| 2989 | dev.consoleMsg("Performing ACM activation..."); |
| 2990 | activateIntelAmtAcmEx2(dev); |
| 2991 | } |
| 2992 | |
| 2993 | // Recursive function to inject the provisioning certificates into AMT in the proper order and completes ACM activation |
| 2994 | function activateIntelAmtAcmEx2(dev) { |
| 2995 | var acmdata = dev.temp.acmdata; |
| 2996 | var leaf = (acmdata.index == 0), root = (acmdata.index == (acmdata.certs.length - 1)); |
| 2997 | if ((acmdata.index < acmdata.certs.length) && (acmdata.certs[acmdata.index] != null)) { |
| 2998 | dev.amtstack.IPS_HostBasedSetupService_AddNextCertInChain(acmdata.certs[acmdata.index], leaf, root, |
| 2999 | function (stack, name, responses, status) { |
| 3000 | const dev = stack.dev; |
| 3001 | if (isAmtDeviceValid(dev) == false) return; // Device no longer exists, ignore this request. |
| 3002 | if (status != 200) { dev.consoleMsg("Failed to set ACM certificate chain (" + status + ")."); removeAmtDevice(dev, 49); return; } |
| 3003 | if (responses['Body']['ReturnValue'] != 0) { dev.consoleMsg("Failed to set ACM certificate chain (ERR/" + responses['Body']['ReturnValue'] + ")."); removeAmtDevice(dev, 50); return; } |
| 3004 | |
| 3005 | // Move to the next activation operation |
| 3006 | dev.temp.acmdata.index++; |
| 3007 | activateIntelAmtAcmEx2(dev); |
| 3008 | } |
| 3009 | ); |
| 3010 | } else { |
| 3011 | dev.amtstack.IPS_HostBasedSetupService_AdminSetup(2, acmdata.password, acmdata.nonce, 2, acmdata.signature, |
| 3012 | function (stack, name, responses, status) { |
| 3013 | const dev = stack.dev; |
| 3014 | if (isAmtDeviceValid(dev) == false) return; // Device no longer exists, ignore this request. |
| 3015 | if (status != 200) { dev.consoleMsg("Failed to complete ACM activation (" + status + ")."); removeAmtDevice(dev, 51); return; } |
| 3016 | if (responses['Body']['ReturnValue'] != 0) { dev.consoleMsg("Failed to complete ACM activation (ERR/" + responses['Body']['ReturnValue'] + ")."); removeAmtDevice(dev, 52); return; } |
| 3017 | |
| 3018 | // Success, switch to managing this device |
| 3019 | obj.parent.mpsserver.SendJsonControl(dev.mpsConnection, { action: 'mestate' }); // Request an MEI state refresh |
| 3020 | dev.consoleMsg("Succesfully activated in ACM mode, holding 10 seconds..."); |
| 3021 | |
| 3022 | // Update the device |
| 3023 | dev.aquired = {}; |
| 3024 | dev.aquired.controlMode = 2; // 1 = CCM, 2 = ACM |
| 3025 | if (typeof dev.amtstack.wsman.comm.amtVersion == 'string') { |
| 3026 | var verSplit = dev.amtstack.wsman.comm.amtVersion.split('.'); |
| 3027 | if (verSplit.length >= 3) { dev.aquired.version = verSplit[0] + '.' + verSplit[1] + '.' + verSplit[2]; dev.aquired.majorver = parseInt(verSplit[0]); dev.aquired.minorver = parseInt(verSplit[1]); } |
| 3028 | } |
| 3029 | if ((typeof dev.mpsConnection.tag.meiState.OsHostname == 'string') && (typeof dev.mpsConnection.tag.meiState.OsDnsSuffix == 'string')) { |
| 3030 | dev.aquired.host = dev.mpsConnection.tag.meiState.OsHostname + '.' + dev.mpsConnection.tag.meiState.OsDnsSuffix; |
| 3031 | } |
| 3032 | dev.aquired.realm = dev.amtstack.wsman.comm.digestRealm; |
| 3033 | dev.intelamt.user = dev.aquired.user = 'admin'; |
| 3034 | dev.intelamt.pass = dev.aquired.pass = dev.temp.pass; |
| 3035 | dev.intelamt.tls = dev.aquired.tls = 0; |
| 3036 | dev.aquired.lastContact = Date.now(); |
| 3037 | dev.aquired.state = 2; // Activated |
| 3038 | delete dev.acctry; |
| 3039 | delete dev.temp; |
| 3040 | UpdateDevice(dev); |
| 3041 | |
| 3042 | // Wait 10 seconds before attempting to manage this device in ACM |
| 3043 | var f = function doManage() { if (isAmtDeviceValid(dev)) { attemptInitialContact(doManage.dev); } } |
| 3044 | f.dev = dev; |
| 3045 | setTimeout(f, 10000); |
| 3046 | } |
| 3047 | ); |
| 3048 | } |
| 3049 | } |
| 3050 | |
| 3051 | |
| 3052 | // |
| 3053 | // Intel AMT CCM deactivation |
| 3054 | // |
| 3055 | |
| 3056 | function deactivateIntelAmtCCM(dev) { |
| 3057 | dev.consoleMsg("Deactivating CCM..."); |
| 3058 | dev.deactivateCcmPending = 1; |
| 3059 | dev.controlMsg({ action: 'deactivate' }); |
| 3060 | } |
| 3061 | |
| 3062 | // This is called after the deactivation call |
| 3063 | function deactivateIntelAmtCCMEx(dev, state) { |
| 3064 | if (state != 0) { |
| 3065 | dev.consoleMsg("Failed to deactivate Intel AMT CCM."); |
| 3066 | removeAmtDevice(dev, 53); |
| 3067 | } else { |
| 3068 | // Update the device |
| 3069 | dev.aquired = {}; |
| 3070 | dev.aquired.controlMode = 0; // 1 = CCM, 2 = ACM |
| 3071 | dev.aquired.state = 0; // Not activated |
| 3072 | delete dev.acctry; |
| 3073 | delete dev.amtstack; |
| 3074 | UpdateDevice(dev); |
| 3075 | |
| 3076 | if (dev.policy.amtPolicy == 1) { // Deactivation policy, we are done. |
| 3077 | dev.consoleMsg("Deactivation successful."); |
| 3078 | dev.consoleMsg("Done."); |
| 3079 | removeAmtDevice(dev, 54); |
| 3080 | } else { |
| 3081 | // Wait 20 seconds before attempting any operation on this device |
| 3082 | dev.consoleMsg("Deactivation successful, holding for 1 minute..."); |
| 3083 | var f = function askMeiState() { |
| 3084 | askMeiState.dev.pendingUpdatedMeiState = 1; |
| 3085 | askMeiState.dev.controlMsg({ action: 'mestate' }); |
| 3086 | } |
| 3087 | f.dev = dev; |
| 3088 | setTimeout(f, 60000); |
| 3089 | } |
| 3090 | } |
| 3091 | } |
| 3092 | |
| 3093 | |
| 3094 | // |
| 3095 | // General Methods |
| 3096 | // |
| 3097 | |
| 3098 | // Called this when a task is completed, when all tasks are completed the call back function will be called. |
| 3099 | function devTaskCompleted(dev) { |
| 3100 | dev.taskCount--; |
| 3101 | if (dev.taskCount == 0) { var f = dev.taskCompleted; delete dev.taskCount; delete dev.taskCompleted; if (f != null) { f(dev); } } |
| 3102 | } |
| 3103 | |
| 3104 | function guidToStr(g) { return g.substring(6, 8) + g.substring(4, 6) + g.substring(2, 4) + g.substring(0, 2) + '-' + g.substring(10, 12) + g.substring(8, 10) + '-' + g.substring(14, 16) + g.substring(12, 14) + '-' + g.substring(16, 20) + '-' + g.substring(20); } |
| 3105 | |
| 3106 | // Base64 to string conversion utility functions |
| 3107 | function atob(x) { return Buffer.from(x, 'base64').toString('binary'); } |
| 3108 | function btoa(x) { return Buffer.from(x, 'binary').toString('base64'); } |
| 3109 | |
| 3110 | // Check which key pair matches the public key in the certificate |
| 3111 | function amtcert_linkCertPrivateKey(certs, keys) { |
| 3112 | if ((keys == null) || (keys.length == 0)) return; |
| 3113 | for (var i in certs) { |
| 3114 | var cert = certs[i]; |
| 3115 | try { |
| 3116 | var publicKeyPEM = obj.parent.certificateOperations.forge.pki.publicKeyToPem(obj.parent.certificateOperations.forge.pki.certificateFromAsn1(obj.parent.certificateOperations.forge.asn1.fromDer(cert.X509CertificateBin)).publicKey).substring(28 + 32).replace(/(\r\n|\n|\r)/gm, ""); |
| 3117 | publicKeyPEM = publicKeyPEM.substring(0, publicKeyPEM.length - 24); // Remove the PEM footer |
| 3118 | for (var j = 0; j < keys.length; j++) { |
| 3119 | if ((publicKeyPEM === (keys[j]['DERKey'])) || (publicKeyPEM == btoa(atob(keys[j]['DERKey']).substring(24)))) { // Match directly or, new version of Intel AMT put the key type OID in the private key, skip that and match. |
| 3120 | keys[j].XCert = cert; // Link the key pair to the certificate |
| 3121 | cert.XPrivateKey = keys[j]; // Link the certificate to the key pair |
| 3122 | } |
| 3123 | } |
| 3124 | } catch (ex) { console.log(ex); } |
| 3125 | } |
| 3126 | } |
| 3127 | |
| 3128 | // Generate a random Intel AMT password |
| 3129 | function checkAmtPassword(p) { return (p.length > 7) && (/\d/.test(p)) && (/[a-z]/.test(p)) && (/[A-Z]/.test(p)) && (/\W/.test(p)); } |
| 3130 | function getRandomAmtPassword() { var p; do { p = Buffer.from(parent.crypto.randomBytes(9), 'binary').toString('base64').split('/').join('@'); } while (checkAmtPassword(p) == false); return p; } |
| 3131 | function getRandomPassword() { return Buffer.from(parent.crypto.randomBytes(9), 'binary').toString('base64').split('/').join('@'); } |
| 3132 | function getRandomLowerCase(len) { var r = '', random = parent.crypto.randomBytes(len); for (var i = 0; i < len; i++) { r += String.fromCharCode(97 + (random[i] % 26)); } return r; } |
| 3133 | function getInstance(x, y) { for (var i in x) { if (x[i]['InstanceID'] == y) return x[i]; } return null; } |
| 3134 | |
| 3135 | function hex_md5(str) { return parent.crypto.createHash('md5').update(str).digest('hex'); } |
| 3136 | function Clone(v) { return JSON.parse(JSON.stringify(v)); } |
| 3137 | function MakeToArray(v) { if (!v || v == null || typeof v == 'object') return v; return [v]; } |
| 3138 | function getItem(x, y, z) { for (var i in x) { if (x[i][y] == z) return x[i]; } return null; } |
| 3139 | function IntToStr(v) { return String.fromCharCode((v >> 24) & 0xFF, (v >> 16) & 0xFF, (v >> 8) & 0xFF, v & 0xFF); } |
| 3140 | |
| 3141 | // Returns a UEFI boot parameter in binary |
| 3142 | function makeUefiBootParam(type, data, len) { |
| 3143 | if (typeof data == 'number') { if (len == 1) { data = String.fromCharCode(data & 0xFF); } if (len == 2) { data = parent.common.ShortToStrX(data); } if (len == 4) { data = parent.common.IntToStrX(data); } } |
| 3144 | return parent.common.ShortToStrX(0x8086) + parent.common.ShortToStrX(type) + parent.common.IntToStrX(data.length) + data; |
| 3145 | } |
| 3146 | |
| 3147 | function parseCertName(x) { |
| 3148 | var j, r = {}, xx = x.split(','); |
| 3149 | for (var i in xx) { j = xx[i].indexOf('='); r[xx[i].substring(0, j)] = xx[i].substring(j + 1); } |
| 3150 | return r; |
| 3151 | } |
| 3152 | |
| 3153 | /* |
| 3154 | function amtcert_signWithCaKey(DERKey, caPrivateKey, certAttributes, issuerAttributes, extKeyUsage) { |
| 3155 | return obj.amtcert_createCertificate(certAttributes, caPrivateKey, DERKey, issuerAttributes, extKeyUsage); |
| 3156 | } |
| 3157 | */ |
| 3158 | |
| 3159 | // --- Extended Key Usage OID's --- |
| 3160 | // 1.3.6.1.5.5.7.3.1 = TLS Server certificate |
| 3161 | // 1.3.6.1.5.5.7.3.2 = TLS Client certificate |
| 3162 | // 2.16.840.1.113741.1.2.1 = Intel AMT Remote Console |
| 3163 | // 2.16.840.1.113741.1.2.2 = Intel AMT Local Console |
| 3164 | // 2.16.840.1.113741.1.2.3 = Intel AMT Client Setup Certificate (Zero-Touch) |
| 3165 | |
| 3166 | // Generate a certificate with a set of attributes signed by a rootCert. If the rootCert is obmitted, the generated certificate is self-signed. |
| 3167 | obj.amtcert_createCertificate = function(certAttributes, caPrivateKey, DERKey, issuerAttributes, extKeyUsage) { |
| 3168 | // Generate a keypair and create an X.509v3 certificate |
| 3169 | var keys, cert = obj.parent.certificateOperations.forge.pki.createCertificate(); |
| 3170 | cert.publicKey = obj.parent.certificateOperations.forge.pki.publicKeyFromPem('-----BEGIN PUBLIC KEY-----' + DERKey + '-----END PUBLIC KEY-----'); |
| 3171 | cert.serialNumber = '' + Math.floor((Math.random() * 100000) + 1); |
| 3172 | cert.validity.notBefore = new Date(2018, 0, 1); |
| 3173 | //cert.validity.notBefore.setFullYear(cert.validity.notBefore.getFullYear() - 1); // Create a certificate that is valid one year before, to make sure out-of-sync clocks don't reject this cert. |
| 3174 | cert.validity.notAfter = new Date(2049, 11, 31); |
| 3175 | //cert.validity.notAfter.setFullYear(cert.validity.notAfter.getFullYear() + 20); |
| 3176 | var attrs = []; |
| 3177 | if (certAttributes['CN']) attrs.push({ name: 'commonName', value: certAttributes['CN'] }); |
| 3178 | if (certAttributes['C']) attrs.push({ name: 'countryName', value: certAttributes['C'] }); |
| 3179 | if (certAttributes['ST']) attrs.push({ shortName: 'ST', value: certAttributes['ST'] }); |
| 3180 | if (certAttributes['O']) attrs.push({ name: 'organizationName', value: certAttributes['O'] }); |
| 3181 | cert.setSubject(attrs); |
| 3182 | |
| 3183 | // Use root attributes |
| 3184 | var rootattrs = []; |
| 3185 | if (issuerAttributes['CN']) rootattrs.push({ name: 'commonName', value: issuerAttributes['CN'] }); |
| 3186 | if (issuerAttributes['C']) rootattrs.push({ name: 'countryName', value: issuerAttributes['C'] }); |
| 3187 | if (issuerAttributes['ST']) rootattrs.push({ shortName: 'ST', value: issuerAttributes['ST'] }); |
| 3188 | if (issuerAttributes['O']) rootattrs.push({ name: 'organizationName', value: issuerAttributes['O'] }); |
| 3189 | cert.setIssuer(rootattrs); |
| 3190 | |
| 3191 | if (extKeyUsage == null) { extKeyUsage = { name: 'extKeyUsage', serverAuth: true, } } else { extKeyUsage.name = 'extKeyUsage'; } |
| 3192 | |
| 3193 | /* |
| 3194 | { |
| 3195 | name: 'extKeyUsage', |
| 3196 | serverAuth: true, |
| 3197 | clientAuth: true, |
| 3198 | codeSigning: true, |
| 3199 | emailProtection: true, |
| 3200 | timeStamping: true, |
| 3201 | '2.16.840.1.113741.1.2.1': true |
| 3202 | } |
| 3203 | */ |
| 3204 | |
| 3205 | // Create a leaf certificate |
| 3206 | cert.setExtensions([{ |
| 3207 | name: 'basicConstraints' |
| 3208 | }, { |
| 3209 | name: 'keyUsage', |
| 3210 | keyCertSign: true, |
| 3211 | digitalSignature: true, |
| 3212 | nonRepudiation: true, |
| 3213 | keyEncipherment: true, |
| 3214 | dataEncipherment: true |
| 3215 | }, extKeyUsage, { |
| 3216 | name: 'nsCertType', |
| 3217 | client: true, |
| 3218 | server: true, |
| 3219 | email: true, |
| 3220 | objsign: true, |
| 3221 | }, { |
| 3222 | name: 'subjectKeyIdentifier' |
| 3223 | }]); |
| 3224 | |
| 3225 | // Self-sign certificate |
| 3226 | var privatekey = obj.parent.certificateOperations.forge.pki.privateKeyFromPem(caPrivateKey); |
| 3227 | cert.sign(privatekey, obj.parent.certificateOperations.forge.md.sha256.create()); |
| 3228 | return cert; |
| 3229 | } |
| 3230 | |
| 3231 | return obj; |
| 3232 | }; |