Added Intel AMT ACM activation using CIRA-LMS.
Ylian Saint-Hilaire committed
Oct 21, 2020 at 19:13 UTC
921e442dc8708ec489d1b8c3f2f2022fb3058ef6
1 file changed
+148
-16
amtmanager.js
+148
-16
@@ -13,7 +13,7 @@
13
/*jshint esversion: 6 */
14
'use strict';
15
16
-module.exports.CreateAmtManager = function(parent) {
16
+module.exports.CreateAmtManager = function (parent) {
17
var obj = {};
18
obj.parent = parent;
19
obj.amtDevices = {}; // Nodeid --> [ dev ]
@@ -43,7 +43,7 @@ module.exports.CreateAmtManager = function(parent) {
43
}
44
}
45
}
46
-
46
+
47
}
48
}
49
@@ -51,7 +51,7 @@ module.exports.CreateAmtManager = function(parent) {
51
function isAmtDeviceValid(dev) {
52
var devices = obj.amtDevices[dev.nodeid];
53
if (devices == null) return false;
54
- return (devices.indexOf(dev) >= 0)
54
+ return (devices.indexOf(dev) >= 0)
55
}
56
57
// Add an Intel AMT managed device
@@ -529,15 +529,15 @@ module.exports.CreateAmtManager = function(parent) {
529
function UpdateDevice(dev) {
530
// Check that the mesh exists
531
const mesh = parent.webserver.meshes[dev.meshid];
532
- if (mesh == null) { removeAmtDevice(dev); console.log('y3'); return false; }
532
+ if (mesh == null) { removeAmtDevice(dev); return false; }
533
534
// Get the node and change it if needed
535
parent.db.Get(dev.nodeid, function (err, nodes) {
536
- if ((nodes == null) || (nodes.length != 1)) { console.log('y1'); return false; }
536
+ if ((nodes == null) || (nodes.length != 1)) return false;
537
const device = nodes[0];
538
var changes = [], change = 0, log = 0;
539
var domain = parent.config.domains[device.domain];
540
- if (domain == null) { console.log('y2'); return false; }
540
+ if (domain == null) return false;
541
542
// Check if anything changes
543
if (device.intelamt == null) { device.intelamt = {}; }
@@ -557,10 +557,7 @@ module.exports.CreateAmtManager = function(parent) {
557
if (typeof device.intelamt.flags == 'number') { flags = device.intelamt.flags; }
558
if (dev.aquired.controlMode == 1) { if ((flags & 4) != 0) { flags -= 4; } if ((flags & 2) == 0) { flags += 2; } } // CCM
559
if (dev.aquired.controlMode == 2) { if ((flags & 4) == 0) { flags += 4; } if ((flags & 2) != 0) { flags -= 2; } } // ACM
560
- if (device.intelamt.flags != flags) {
561
- console.log('ChangeFlags', flags);
562
- change = 1; log = 1; device.intelamt.flags = flags; changes.push('AMT flags');
563
- }
560
+ if (device.intelamt.flags != flags) { change = 1; log = 1; device.intelamt.flags = flags; changes.push('AMT flags'); }
561
562
// If there are changes, event the new device
563
if (change == 1) {
@@ -866,7 +863,7 @@ module.exports.CreateAmtManager = function(parent) {
863
const dev = stack.dev;
864
if (isAmtDeviceValid(dev) == false) return; // Device no longer exists, ignore this request.
865
if (status != 200) { dev.consoleMsg("Failed perform commit (" + status + ")."); removeAmtDevice(dev); return; }
869
- dev.consoleMsg("Enabled TLS.");
866
+ dev.consoleMsg("Enabled TLS, holding 5 seconds...");
867
868
// Update device in the database
869
dev.aquired.tls = 1;
@@ -877,7 +874,11 @@ module.exports.CreateAmtManager = function(parent) {
874
// Switch our communications to TLS (Restart our management of this node)
875
dev.switchToTls = 1;
876
delete dev.tlsfail;
880
- devTaskCompleted(dev);
877
+
878
+ // Wait 5 seconds before attempting to manage this device some more
879
+ var f = function doManage() { if (isAmtDeviceValid(dev)) { devTaskCompleted(doManage.dev); } }
880
+ f.dev = dev;
881
+ setTimeout(f, 5000);
882
});
883
}
884
}
@@ -1073,7 +1074,7 @@ module.exports.CreateAmtManager = function(parent) {
1074
1075
if (dev.policy.ciraPolicy == 2) {
1076
// Check that we have a random environment detection
1076
- if (domains.length == 0) { editEnvironmentDetectionTmp = [ Buffer.from(parent.crypto.randomBytes(6), 'binary').toString('hex') ]; changes = true; }
1077
+ if (domains.length == 0) { editEnvironmentDetectionTmp = [Buffer.from(parent.crypto.randomBytes(6), 'binary').toString('hex')]; changes = true; }
1078
} else if (dev.policy.ciraPolicy == 1) {
1079
// Check environment detection is clear
1080
if (domains.length != 0) { editEnvironmentDetectionTmp = []; changes = true; }
@@ -1230,7 +1231,7 @@ module.exports.CreateAmtManager = function(parent) {
1231
x.netmask = wired.SubnetMask;
1232
x.mac = wired.MACAddress.split('-').join(':').toUpperCase();
1233
x.gateway = wired.DefaultGateway;
1233
- net.netif2['Ethernet'] = [ x ];
1234
+ net.netif2['Ethernet'] = [x];
1235
}
1236
1237
if (wireless != null) {
@@ -1241,7 +1242,7 @@ module.exports.CreateAmtManager = function(parent) {
1242
x.netmask = wireless.SubnetMask;
1243
x.mac = wireless.MACAddress.split('-').join(':').toUpperCase();
1244
x.gateway = wireless.DefaultGateway;
1244
- net.netif2['Wireless'] = [ x ];
1245
+ net.netif2['Wireless'] = [x];
1246
}
1247
1248
net.updateTime = Date.now();
@@ -1375,7 +1376,15 @@ module.exports.CreateAmtManager = function(parent) {
1376
if (mesh.amt != null) { if (mesh.amt.type) { amtPolicy = mesh.amt.type; } }
1377
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; }
1378
if (amtPolicy == 0) { removeAmtDevice(dev); return; } // Do nothing, we should not have gotten this CIRA-LMS connection.
1378
- if (amtPolicy == 2) { activateIntelAmtCcm(dev, mesh.amt.password); }
1379
+ if (amtPolicy == 2) { activateIntelAmtCcm(dev, mesh.amt.password); } // Activate to CCM policy
1380
+ if (amtPolicy == 3) { // Activate to ACM policy
1381
+ var acminfo = checkAcmActivation(dev);
1382
+ if (acminfo == null) {
1383
+ activateIntelAmtCcm(dev, mesh.amt.password); // No ACM certificate found, fallback to CCM.
1384
+ } else {
1385
+ activateIntelAmtAcm(dev, mesh.amt.password, acminfo); // Found a certificate, activate to ACM.
1386
+ }
1387
+ }
1388
}
1389
1390
function activateIntelAmtCcm(dev, password) {
@@ -1419,6 +1428,7 @@ module.exports.CreateAmtManager = function(parent) {
1428
dev.aquired.lastContact = Date.now();
1429
dev.aquired.state = 2; // Activated
1430
delete dev.acctry;
1431
+ delete dev.temp;
1432
UpdateDevice(dev);
1433
1434
// Success, switch to managing this device
@@ -1431,6 +1441,128 @@ module.exports.CreateAmtManager = function(parent) {
1441
setTimeout(f, 10000);
1442
}
1443
1444
+ // Check if this device has any way to be activated in ACM using our server certificates.
1445
+ function checkAcmActivation(dev) {
1446
+ var domain = parent.config.domains[dev.domainid];
1447
+ if ((domain == null) || (domain.amtacmactivation == null) || (domain.amtacmactivation.certs == null) || (domain.amtacmactivation.certs.length == 0)) return null;
1448
+ const activationCerts = domain.amtacmactivation.certs;
1449
+ if ((dev.mpsConnection.tag.meiState == null) || (dev.mpsConnection.tag.meiState.Hashes == null) || (dev.mpsConnection.tag.meiState.Hashes.length == 0)) return null;
1450
+ const deviceHashes = dev.mpsConnection.tag.meiState.Hashes;
1451
+
1452
+ // Get the trusted FQDN of the device
1453
+ var trustedFqdn = null;
1454
+ if (dev.mpsConnection.tag.meiState.OsDnsSuffix != null) { trustedFqdn = dev.mpsConnection.tag.meiState.OsDnsSuffix; }
1455
+ if (dev.mpsConnection.tag.meiState.DnsSuffix != null) { trustedFqdn = dev.mpsConnection.tag.meiState.DnsSuffix; }
1456
+ if (trustedFqdn == null) return null;
1457
+
1458
+ // Find a matching certificate
1459
+ for (var i in activationCerts) {
1460
+ var cert = activationCerts[i];
1461
+ if ((cert.cn == '*') || (cert.cn == trustedFqdn)) {
1462
+ for (var j in deviceHashes) {
1463
+ var hashInfo = deviceHashes[j];
1464
+ if (hashInfo.isActive == 1) {
1465
+ if ((hashInfo.hashAlgorithmStr == 'SHA256') && (hashInfo.certificateHash.toLowerCase() == cert.sha256)) { return { cert: cert, fqdn: trustedFqdn, hash: cert.sha256 }; } // Found a match
1466
+ else if ((hashInfo.hashAlgorithmStr == 'SHA1') && (hashInfo.certificateHash.toLowerCase() == cert.sha1)) { return { cert: cert, fqdn: trustedFqdn, hash: cert.sha1 }; } // Found a match
1467
+ }
1468
+ }
1469
+ }
1470
+ }
1471
+ return null; // Did not find a match
1472
+ }
1473
+
1474
+ // Attempt Intel AMT ACM activation
1475
+ function activateIntelAmtAcm(dev, password, acminfo) {
1476
+ // Generate a random Intel AMT password if needed
1477
+ if ((password == null) || (password == '')) { password = getRandomAmtPassword(); }
1478
+ dev.temp = { pass: password, acminfo: acminfo };
1479
+
1480
+ // Setup the WSMAN stack, no TLS
1481
+ var comm = CreateWsmanComm(dev.nodeid, 16992, dev.mpsConnection.tag.meiState.OsAdmin.user, dev.mpsConnection.tag.meiState.OsAdmin.pass, 0, null, dev.mpsConnection); // No TLS
1482
+ var wsstack = WsmanStackCreateService(comm);
1483
+ dev.amtstack = AmtStackCreateService(wsstack);
1484
+ dev.amtstack.dev = dev;
1485
+ dev.amtstack.BatchEnum(null, ['*AMT_GeneralSettings', '*IPS_HostBasedSetupService'], activateIntelAmtAcmEx1);
1486
+ }
1487
+
1488
+ function activateIntelAmtAcmEx1(stack, name, responses, status) {
1489
+ const dev = stack.dev;
1490
+ if (isAmtDeviceValid(dev) == false) return; // Device no longer exists, ignore this request.
1491
+ if (status != 200) { dev.consoleMsg("Failed to get Intel AMT state."); removeAmtDevice(dev); return; }
1492
+
1493
+ // Sign the Intel AMT ACM activation request
1494
+ 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 };
1495
+ var acmdata = parent.certificateOperations.signAcmRequest(parent.config.domains[dev.domainid], info, 'admin', dev.temp.pass, obj.remoteaddrport, dev.nodeid, dev.meshid, dev.name, 0);
1496
+ if ((acmdata == null) || (acmdata.error != null)) { dev.consoleMsg("Failed to sign ACM nonce."); removeAmtDevice(dev); return; }
1497
+
1498
+ // Log this activation event
1499
+ 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 };
1500
+ 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.
1501
+ parent.DispatchEvent(parent.webserver.CreateMeshDispatchTargets(dev.meshid, [dev.nodeid]), obj, event);
1502
+
1503
+ // Start the activation process
1504
+ dev.temp.acmdata = acmdata;
1505
+ dev.temp.acmdata.index = 0;
1506
+ dev.consoleMsg("Performing ACM activation...");
1507
+ activateIntelAmtAcmEx2(dev);
1508
+ }
1509
+
1510
+ // Recursive function to inject the provisioning certificates into AMT in the proper order and completes ACM activation
1511
+ function activateIntelAmtAcmEx2(dev) {
1512
+ var acmdata = dev.temp.acmdata;
1513
+ var leaf = (acmdata.index == 0), root = (acmdata.index == (acmdata.certs.length - 1));
1514
+ if ((acmdata.index < acmdata.certs.length) && (acmdata.certs[acmdata.index] != null)) {
1515
+ dev.amtstack.IPS_HostBasedSetupService_AddNextCertInChain(acmdata.certs[acmdata.index], leaf, root,
1516
+ function (stack, name, responses, status) {
1517
+ const dev = stack.dev;
1518
+ if (isAmtDeviceValid(dev) == false) return; // Device no longer exists, ignore this request.
1519
+ if (status != 200) { dev.consoleMsg("Failed to set ACM certificate chain (" + status + ")."); removeAmtDevice(dev); return; }
1520
+ if (responses['Body']['ReturnValue'] != 0) { dev.consoleMsg("Failed to set ACM certificate chain (ERR/" + responses['Body']['ReturnValue'] + ")."); removeAmtDevice(dev); return; }
1521
+
1522
+ // Move to the next activation operation
1523
+ dev.temp.acmdata.index++;
1524
+ activateIntelAmtAcmEx2(dev);
1525
+ }
1526
+ );
1527
+ } else {
1528
+ dev.amtstack.IPS_HostBasedSetupService_AdminSetup(2, acmdata.password, acmdata.nonce, 2, acmdata.signature,
1529
+ function (stack, name, responses, status) {
1530
+ const dev = stack.dev;
1531
+ if (isAmtDeviceValid(dev) == false) return; // Device no longer exists, ignore this request.
1532
+ if (status != 200) { dev.consoleMsg("Failed to complete ACM activation (" + status + ")."); removeAmtDevice(dev); return; }
1533
+ if (responses['Body']['ReturnValue'] != 0) { dev.consoleMsg("Failed to complete ACM activation (ERR/" + responses['Body']['ReturnValue'] + ")."); removeAmtDevice(dev); return; }
1534
+
1535
+ // Success, switch to managing this device
1536
+ obj.parent.mpsserver.SendJsonControl(dev.mpsConnection, { action: 'mestate' }); // Request an MEI state refresh
1537
+ dev.consoleMsg("Succesfully activated in ACM mode, holding 10 seconds...");
1538
+
1539
+ // Update the device
1540
+ dev.aquired = {};
1541
+ dev.aquired.controlMode = 2; // 1 = CCM, 2 = ACM
1542
+ var verSplit = dev.amtstack.wsman.comm.amtVersion.split('.');
1543
+ 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]); }
1544
+ if ((typeof dev.mpsConnection.tag.meiState.OsHostname == 'string') && (typeof dev.mpsConnection.tag.meiState.OsDnsSuffix == 'string')) {
1545
+ dev.aquired.host = dev.mpsConnection.tag.meiState.OsHostname + '.' + dev.mpsConnection.tag.meiState.OsDnsSuffix;
1546
+ }
1547
+ dev.aquired.realm = dev.amtstack.wsman.comm.digestRealm;
1548
+ dev.intelamt.user = dev.aquired.user = 'admin';
1549
+ dev.intelamt.pass = dev.aquired.pass = dev.temp.pass;
1550
+ dev.intelamt.tls = dev.aquired.tls = 0;
1551
+ dev.aquired.lastContact = Date.now();
1552
+ dev.aquired.state = 2; // Activated
1553
+ delete dev.acctry;
1554
+ delete dev.temp;
1555
+ UpdateDevice(dev);
1556
+
1557
+ // Wait 10 seconds before attempting to manage this device in CCM
1558
+ var f = function doManage() { if (isAmtDeviceValid(dev)) { attemptInitialContact(doManage.dev); } }
1559
+ f.dev = dev;
1560
+ setTimeout(f, 10000);
1561
+ }
1562
+ );
1563
+ }
1564
+ }
1565
+
1566
1567
//
1568
// Intel AMT CCM deactivation