More work on Intel AMT provisioning server.

Ylian Saint-Hilaire committed Mar 13, 2021 at 18:15 UTC f38f6460eb823496f65ca6610851dce63a64bfe2
3 files changed +98 -44
amtmanager.js
+1
@@ -167,6 +167,7 @@ module.exports.CreateAmtManager = function (parent) {
167 }
168
169 // Start Intel AMT management
170 + // connType: 0 = CIRA, 1 = CIRA-Relay, 2 = CIRA-LMS, 3 = LAN
171 obj.startAmtManagement = function (nodeid, connType, connection) {
172 //if (connType == 3) return; // DEBUG
173 var devices = obj.amtDevices[nodeid], dev = null;
amtprovisioningserver.js
+94 -39
@@ -38,7 +38,6 @@ module.exports.CreateAmtProvisioningServer = function (parent, config) {
38 socket.on('error', function (err) { })
39 socket.on('close', function () { if (this.data != null) { processHelloData(this.data, this.ra); } delete this.ra; this.removeAllListeners(); })
40 socket.on('data', function (data) {
41 - console.log('HELLO:', data.toString('HEX'));
41 if (this.data == null) { this.data = data; } else { Buffer.concat([this.data, data]); }
42 var str = this.data.toString();
43 if (str.startsWith('GET ') && (str.indexOf('\r\n\r\n') >= 0)) {
@@ -258,13 +257,23 @@ module.exports.CreateAmtProvisioningServer = function (parent, config) {
257 if (status != 200) { dev.consoleMsg('Failed to set admin password, status=' + status + '.'); destroyDevice(dev); return; }
258 dev.consoleMsg('Admin password set.');
259
261 - // Setup TLS and commit.
262 - attemptTlsSync(dev, function (dev) {
263 - dev.consoleMsg('Intel AMT ACM activation completed.');
264 - destroyDevice(dev)
260 + // Perform Intel AMT clock sync
261 + attemptSyncClock(dev, function (dev) {
262 + // Setup TLS and commit.
263 + attemptTlsSync(dev, function (dev) {
264 + dev.consoleMsg('Intel AMT ACM activation completed.');
265 + parent.SetConnectivityState(dev.meshid, dev.nodeid, Date.now(), 4, 7); // Report power state as "present" (7).
266 + if (obj.parent.amtManager != null) { obj.parent.amtManager.startAmtManagement(dev.nodeid, 3, dev.aquired.host); } // Request that Intel AMT manager take a look at this device.
267 + destroyDevice(dev); // We are done, clean up.
268 + });
269 });
270 }
271
272 +
273 + //
274 + // Intel AMT TLS setup
275 + //
276 +
277 // Check if Intel AMT TLS state is correct
278 function attemptTlsSync(dev, func) {
279 dev.taskCount = 1;
@@ -302,7 +311,6 @@ module.exports.CreateAmtProvisioningServer = function (parent, config) {
311 }
312
313 // This is a managed device and TLS is not enabled, turn it on.
305 - /*
314 if (xxTlsCurrentCert === null) {
315 // Start by generating a key pair
316 dev.consoleMsg("No TLS certificate. Generating key pair...");
@@ -358,9 +366,9 @@ module.exports.CreateAmtProvisioningServer = function (parent, config) {
366 // Set the certificate finderprint (SHA1)
367 var md = obj.parent.certificateOperations.forge.md.sha1.create();
368 md.update(obj.parent.certificateOperations.forge.asn1.toDer(obj.parent.certificateOperations.forge.pki.certificateToAsn1(cert)).getBytes());
361 - dev.aquired.xhash = md.digest().toHex();
369 + dev.aquired.hash = md.digest().toHex();
370
363 - dev.consoleMsg("Adding certificate...");
371 + dev.consoleMsg("Adding certificate, hash: " + dev.aquired.hash);
372 dev.amtstack.AMT_PublicKeyManagementService_AddCertificate(pem.substring(27, pem.length - 25), function (stack, name, responses, status) {
373 const dev = stack.dev;
374 if (isAmtDeviceValid(dev) == false) return; // Device no longer exists, ignore this request.
@@ -370,7 +378,7 @@ module.exports.CreateAmtProvisioningServer = function (parent, config) {
378 if (certInstanceId == null) { dev.consoleMsg("Failed to get TLS certificate identifier."); removeAmtDevice(dev, 25); return; }
379
380 // Set the TLS certificate
373 - dev.setTlsSecurityPendingCalls = 3;
381 + dev.setTlsSecurityPendingCalls = 2;
382 if (dev.policy.tlsCredentialContext.length > 0) {
383 // Modify the current context
384 var newTLSCredentialContext = Clone(dev.policy.tlsCredentialContext[0]);
@@ -394,34 +402,28 @@ module.exports.CreateAmtProvisioningServer = function (parent, config) {
402 xxTlsSettings2[remoteNdx]['AcceptNonSecureConnections'] = true;
403 delete xxTlsSettings2[remoteNdx]['TrustedCN'];
404
397 - // Local TLS settings
398 - xxTlsSettings2[localNdx]['Enabled'] = true;
399 - delete xxTlsSettings2[localNdx]['TrustedCN'];
400 -
401 - // Update TLS settings
402 - dev.consoleMsg("Enabling TLS...");
403 - dev.amtstack.Put('AMT_TLSSettingData', xxTlsSettings2[0], amtSwitchToTls, 0, 1, xxTlsSettings2[0]);
404 - dev.amtstack.Put('AMT_TLSSettingData', xxTlsSettings2[1], amtSwitchToTls, 0, 1, xxTlsSettings2[1]);
405 + // Update TLS settings. Enable on remote port only. If you enable on local port, the commit() will succeed but be ignored.
406 + dev.consoleMsg("Enabling TLS on remote port...");
407 + if (remoteNdx == 0) { dev.amtstack.Put('AMT_TLSSettingData', xxTlsSettings2[0], amtSwitchToTls, 0, 1, xxTlsSettings2[0]); }
408 + else { dev.amtstack.Put('AMT_TLSSettingData', xxTlsSettings2[1], amtSwitchToTls, 0, 1, xxTlsSettings2[1]); }
409 });
410
411 }, responses.Body['KeyPair']['ReferenceParameters']['SelectorSet']['Selector']['Value']);
412 });
413 } else {
410 - */
414 // TLS already enabled, update device in the database
415 dev.consoleMsg("Intel AMT has TLS already enabled.");
416
417 // Perform commit
418 dev.taskCount = 1;
419 amtPerformCommit(dev);
417 - //}
420 + }
421 }
422
423 function amtSwitchToTls(stack, name, responses, status) {
424 const dev = stack.dev;
425 if (isAmtDeviceValid(dev) == false) return; // Device no longer exists, ignore this request.
426 if (status != 200) { dev.consoleMsg("Failed setup TLS (" + status + ")."); removeAmtDevice(dev, 26); return; }
424 - dev.consoleMsg("Switched to TLS.");
427
428 // Check if all the calls are done & perform a commit
429 if ((--dev.setTlsSecurityPendingCalls) == 0) {
@@ -438,12 +440,16 @@ module.exports.CreateAmtProvisioningServer = function (parent, config) {
440 if (status != 200) { dev.consoleMsg("Failed perform commit (" + status + ")."); removeAmtDevice(dev, 27); return; }
441 dev.consoleMsg("Commited, holding 5 seconds...");
442
441 - // Update device in the database
443 + // Update the device state
444 dev.aquired.tls = 1;
443 - dev.aquired.hash = dev.aquired.xhash;
445 dev.aquired.state = 2; // Activated
446 dev.aquired.controlMode = 2; // Activated in ACM
446 - delete dev.aquired.xhash;
447 +
448 + // Save activation data to amtactivation.log
449 + var domain = parent.config.domains[dev.domainid];
450 + obj.logAmtActivation(domain, { time: new Date(), action: 'acmactivate-bare-metal', domain: dev.domainid, amtUuid: dev.guid, newmebx: config.newmebxpassword, mesh: dev.meshid, amtRealm: dev.aquired.realm, amtver: dev.aquired.version, host: dev.aquired.host, ip: dev.addr, user: dev.aquired.user, pass: dev.aquired.pass, tls: dev.aquired.tls, tlshash: dev.aquired.hash });
451 +
452 + // Update device in the database
453 if (UpdateDevice(dev) == false) return;
454
455 // Switch our communications to TLS (Restart our management of this node)
@@ -457,22 +463,57 @@ module.exports.CreateAmtProvisioningServer = function (parent, config) {
463 });
464 }
465
466 +
467 + //
468 + // Intel AMT Clock Syncronization
469 + //
470 +
471 + // Attempt to sync the Intel AMT clock if needed, call func back when done.
472 + // Care should be take not to have many pending WSMAN called when performing clock sync.
473 + function attemptSyncClock(dev, func) {
474 + if (isAmtDeviceValid(dev) == false) return; // Device no longer exists, ignore this request.
475 + dev.taskCount = 1;
476 + dev.taskCompleted = func;
477 + dev.amtstack.AMT_TimeSynchronizationService_GetLowAccuracyTimeSynch(attemptSyncClockEx);
478 + }
479 +
480 + // Intel AMT clock query response
481 + function attemptSyncClockEx(stack, name, response, status) {
482 + const dev = stack.dev;
483 + if (isAmtDeviceValid(dev) == false) return; // Device no longer exists, ignore this request.
484 + if (status != 200) { dev.consoleMsg("Failed to get clock (" + status + ")."); removeAmtDevice(dev, 17); return; }
485 +
486 + // Compute how much drift between Intel AMT and our clock.
487 + var t = new Date(), now = new Date();
488 + t.setTime(response.Body['Ta0'] * 1000);
489 + if (Math.abs(t - now) > 10000) { // If the Intel AMT clock is more than 10 seconds off, set it.
490 + dev.consoleMsg("Performing clock sync.");
491 + var Tm1 = Math.round(now.getTime() / 1000);
492 + dev.amtstack.AMT_TimeSynchronizationService_SetHighAccuracyTimeSynch(response.Body['Ta0'], Tm1, Tm1, attemptSyncClockSet);
493 + } else {
494 + // Clock is fine, we are done.
495 + devTaskCompleted(dev)
496 + }
497 + }
498 +
499 + // Intel AMT clock set response
500 + function attemptSyncClockSet(stack, name, responses, status) {
501 + const dev = stack.dev;
502 + if (isAmtDeviceValid(dev) == false) return; // Device no longer exists, ignore this request.
503 + if (status != 200) { dev.consoleMsg("Failed to sync clock (" + status + ")."); removeAmtDevice(dev, 18); }
504 + devTaskCompleted(dev)
505 + }
506 +
507 +
508 + //
509 + // Device Management Methods
510 + //
511 +
512 // Do aggressive cleanup on the device
513 function destroyDevice(dev) {
462 - delete obj.devices[dev.addr];
463 - if (dev.amtstack != null) { delete dev.amtstack.dev; delete dev.amtstack; }
464 - delete dev.guid;
465 - delete dev.mesh;
466 - delete dev.realm;
467 - delete dev.meshid;
468 - delete dev.aquired;
469 - delete dev.guidhex;
470 - delete dev.domainid;
471 - delete dev.certchain;
472 - delete dev.retryCount;
473 - delete dev.amtversion;
474 - delete dev.amtversionmin;
475 - delete dev.amtversionstr;
514 + delete obj.devices[dev.addr]; // Remove the device from the list of currently active devices.
515 + if (dev.amtstack != null) { delete dev.amtstack.dev; delete dev.amtstack; } // Clean up the AMT stack.
516 + for (var i in dev) { delete dev[i]; } // Aggressive cleanup or everything else.
517 }
518
519 // Update the device in the database and event any changes
@@ -495,11 +536,11 @@ module.exports.CreateAmtProvisioningServer = function (parent, config) {
536 // (node.intelamt.flags & 2) == CCM, (node.intelamt.flags & 4) == ACM
537 if (dev.aquired.controlMode == 1) { device.intelamt.flags = 2; } // CCM
538 if (dev.aquired.controlMode == 2) { device.intelamt.flags = 4; } // ACM
498 -
539 +
540 parent.db.Set(device);
541
542 // Event the new node
502 - parent.DispatchEvent(parent.webserver.CreateMeshDispatchTargets(dev.meshid, [dev.nodeid]), obj, { etype: 'node', action: 'addnode', node: parent.CloneSafeNode(device), msgid: 84, msgArgs: [devicename, mesh.name], msg: 'Added device ' + devicename + ' to device group ' + mesh.name, domain: domain.id });
543 + parent.DispatchEvent(parent.webserver.CreateMeshDispatchTargets(dev.meshid, [dev.nodeid]), obj, { etype: 'node', action: 'addnode', node: parent.webserver.CloneSafeNode(device), msgid: 84, msgArgs: [devicename, mesh.name], msg: 'Added device ' + devicename + ' to device group ' + mesh.name, domain: dev.domainid });
544 } else {
545 // Update an existing device
546 const device = nodes[0];
@@ -549,10 +590,24 @@ module.exports.CreateAmtProvisioningServer = function (parent, config) {
590 return true;
591 }
592
593 +
594 //
595 // General Methods
596 //
597
598 + // Log the Intel AMT activation operation in the domain log
599 + obj.logAmtActivation = function (domain, x) {
600 + if (x == null) return true;
601 + var logpath = null;
602 + if ((domain.amtacmactivation == null) || (domain.amtacmactivation.log == null) || (typeof domain.amtacmactivation.log != 'string')) {
603 + if (domain.id == '') { logpath = parent.path.join(obj.parent.datapath, 'amtactivation.log'); } else { logpath = parent.path.join(obj.parent.datapath, 'amtactivation-' + domain.id + '.log'); }
604 + } else {
605 + if ((domain.amtacmactivation.log.length >= 2) && ((domain.amtacmactivation.log[0] == '/') || (domain.amtacmactivation.log[1] == ':'))) { logpath = domain.amtacmactivation.log; } else { logpath = parent.path.join(obj.parent.datapath, domain.amtacmactivation.log); }
606 + }
607 + try { parent.fs.appendFileSync(logpath, JSON.stringify(x) + '\r\n'); } catch (ex) { console.log(ex); return false; }
608 + return true;
609 + }
610 +
611 // Called this when a task is completed, when all tasks are completed the call back function will be called.
612 function devTaskCompleted(dev) {
613 dev.taskCount--;
certoperations.js
+3 -5
@@ -338,18 +338,16 @@ module.exports.CertificateOperations = function (parent) {
338 v.value = trustedFQDN;
339 setupbin.records[0].variables.push(v);
340
341 - /*
341 // Create "ME Provision Halt Active" variable
342 v = {};
343 v.moduleid = 2;
344 v.varid = 28;
345 v.length = -1;
347 - v.value = 1;
346 + v.value = 0; // Stop
347 setupbin.records[0].variables.push(v);
349 - */
348
349 // Write to log file
352 - obj.logAmtActivation(domain, { time: new Date(), action: 'setupbin', domain: domain.id, userid: user._id, oldmebx: oldmebxpass, newmebx: newmebxpass, rootname: certRootName, hash: wildcardCertSha256, dns: 'rootcert.meshcentral.com' });
350 + obj.logAmtActivation(domain, { time: new Date(), action: 'setupbin', domain: domain.id, userid: user._id, oldmebx: oldmebxpass, newmebx: newmebxpass, rootname: certRootName, hash: wildcardCertSha256, dns: trustedFQDN });
351
352 // Encode the setup.bin file
353 return AmtSetupBinStack.AmtSetupBinEncode(setupbin);
@@ -456,7 +454,7 @@ module.exports.CertificateOperations = function (parent) {
454 setupbin.records[0].variables.push(v);
455
456 // Write to log file
459 - obj.logAmtActivation(domain, { time: new Date(), action: 'setupbin', domain: domain.id, userid: user._id, oldmebx: oldmebxpass, newmebx: newmebxpass, rootname: certRootName, hash: wildcardCertSha256, dns: 'rootcert.meshcentral.com' });
457 + obj.logAmtActivation(domain, { time: new Date(), action: 'setupbin-bare-metal', domain: domain.id, userid: user._id, oldmebx: oldmebxpass, newmebx: newmebxpass, rootname: certRootName, hash: wildcardCertSha256, dns: trustedFQDN, ip: ipaddr, port: port });
458
459 // Encode the setup.bin file
460 return AmtSetupBinStack.AmtSetupBinEncode(setupbin);