More work on AMT 802.1x support.
Ylian Saint-Hilaire committed
Apr 6, 2022 at 15:41 UTC
b910cde272d25b5e029bc685d669360700dc7876
1 file changed
+134
-39
amtmanager.js
+134
-39
@@ -438,34 +438,7 @@ module.exports.CreateAmtManager = function (parent) {
438
439
// We got a new 802.1x profile
440
devFound.netAuthCredentials = event.response;
441
- console.log('devFound.netAuthCredentials', devFound.netAuthCredentials);
442
- if (devFound.netAuthCredentials.certificate) {
443
- // The new 802.1x profile includes a new certificate, add it now before adding the 802.1x profiles
444
- // devFound.netAuthCredentials.certificate must be in DER encoded format
445
- devFound.consoleMsg("Setting up new 802.1x certificate...");
446
-
447
- const f = function AddCertificateResponse(stack, name, response, status) {
448
- if ((status != 200) || (response.Body['ReturnValue'] != 0)) {
449
- AddCertificateResponse.dev.consoleMsg("Unable to set 802.1x certificate.");
450
- } else {
451
- console.log('AddCertificate - TODO', response);
452
- // TODO: Keep the certificate reference since we need it to add 802.1x profiles
453
-
454
- // Set the 802.1x wired profile in the device
455
- AddCertificateResponse.dev.consoleMsg("Setting MeshCentral Satellite 802.1x profile...");
456
- const netAuthSatReqData = AddCertificateResponse.dev.netAuthSatReqData;
457
- attempt8021xSyncEx(AddCertificateResponse.dev, netAuthSatReqData);
458
- }
459
- }
460
- f.dev = devFound;
461
- devFound.amtstack.AMT_PublicKeyManagementService_AddCertificate(devFound.netAuthCredentials.certificate, f);
462
- } else {
463
- // No 802.1x certificate, set the 802.1x wired profile in the device
464
- devFound.consoleMsg("Setting MeshCentral Satellite 802.1x profile...");
465
- const netAuthSatReqData = devFound.netAuthSatReqData;
466
- delete devFound.netAuthSatReqData;
467
- attempt8021xSyncEx(devFound, netAuthSatReqData);
468
- }
441
+ perform8021xRootCertCheck(devFound);
442
break;
443
}
444
}
@@ -1363,6 +1336,23 @@ module.exports.CreateAmtManager = function (parent) {
1336
// Intel AMT WIFI
1337
//
1338
1339
+ // Check which key pair matches the public key in the certificate
1340
+ function amtcert_linkCertPrivateKey(certs, keys) {
1341
+ for (var i in certs) {
1342
+ var cert = certs[i];
1343
+ try {
1344
+ if (keys.length == 0) return;
1345
+ var publicKeyPEM = forge.pki.publicKeyToPem(forge.pki.certificateFromAsn1(forge.asn1.fromDer(cert.X509Certificate)).publicKey).substring(28 + 32).replace(/(\r\n|\n|\r)/gm, "");
1346
+ for (var j = 0; j < keys.length; j++) {
1347
+ if (publicKeyPEM === (keys[j]['DERKey'] + '-----END PUBLIC KEY-----')) {
1348
+ keys[j].XCert = cert; // Link the key pair to the certificate
1349
+ cert.XPrivateKey = keys[j]; // Link the certificate to the key pair
1350
+ }
1351
+ }
1352
+ } catch (e) { console.log(e); }
1353
+ }
1354
+ }
1355
+
1356
// This method will sync the WIFI profiles from the device and the server, but does not care about profile priority.
1357
// 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
1358
// We may want to work on an alternate version that does do priority if requested.
@@ -1377,17 +1367,41 @@ module.exports.CreateAmtManager = function (parent) {
1367
dev.taskCount = 1;
1368
dev.taskCompleted = func;
1369
1380
- const objQuery = ['CIM_WiFiEndpointSettings', '*CIM_WiFiPort', '*AMT_WiFiPortConfigurationService', 'CIM_IEEE8021xSettings'];
1370
+ const objQuery = ['CIM_WiFiEndpointSettings', '*CIM_WiFiPort', '*AMT_WiFiPortConfigurationService', 'CIM_IEEE8021xSettings', 'AMT_PublicKeyCertificate', 'AMT_PublicPrivateKeyPair'];
1371
if (parent.config.domains[dev.domainid].amtmanager['802.1x'] != null) { objQuery.push('*AMT_8021XProfile'); }
1372
dev.amtstack.BatchEnum(null, objQuery, function (stack, name, responses, status) {
1373
const dev = stack.dev;
1374
if (isAmtDeviceValid(dev) == false) return; // Device no longer exists, ignore this request.
1375
const domain = parent.config.domains[dev.domainid];
1376
+ if ((responses['AMT_PublicKeyCertificate'].status != 200) || (responses['AMT_PublicKeyCertificate'].status != 200)) { devTaskCompleted(dev); return; } // We can't get the certificate list, fail and carry on.
1377
1378
+ // See if we need to perform wired or wireless 802.1x configuration
1379
const wiredConfig = ((parent.config.domains[dev.domainid].amtmanager['802.1x'] != null) && (responses['AMT_8021XProfile'].status == 200));
1380
const wirelessConfig = ((responses['CIM_WiFiEndpointSettings'].status == 200) && (responses['AMT_WiFiPortConfigurationService'].status == 200) && (responses['CIM_WiFiPort'].status == 200) && (responses['CIM_IEEE8021xSettings'].status == 200));
1381
if (!wiredConfig && !wirelessConfig) { devTaskCompleted(dev); return; } // We can't get wired or wireless settings, ignore and carry on.
1382
1383
+ // Sort out the certificates
1384
+ var xxCertificates = responses['AMT_PublicKeyCertificate'].responses;
1385
+ var xxCertPrivateKeys = responses['AMT_PublicPrivateKeyPair'].responses;
1386
+ for (var i in xxCertificates) {
1387
+ xxCertificates[i].TrustedRootCertficate = (xxCertificates[i]['TrustedRootCertficate'] == true);
1388
+ xxCertificates[i].X509CertificateBin = Buffer.from(xxCertificates[i]['X509Certificate'], 'base64').toString('binary');
1389
+ xxCertificates[i].XIssuer = parseCertName(xxCertificates[i]['Issuer']);
1390
+ xxCertificates[i].XSubject = parseCertName(xxCertificates[i]['Subject']);
1391
+ }
1392
+ amtcert_linkCertPrivateKey(xxCertificates, xxCertPrivateKeys); // This links all certificates and private keys
1393
+
1394
+ // Remove any unlinked private keys
1395
+ for (var i in xxCertPrivateKeys) {
1396
+ if (!xxCertPrivateKeys[i].XCert) {
1397
+ console.log('PrivateKey-Removing');
1398
+ dev.amtstack.Delete('AMT_PublicPrivateKeyPair', { 'InstanceID': xxCertPrivateKeys[i]['InstanceID'] }, function (stack, name, response, status) {
1399
+ console.log('PrivateKey-Removed');
1400
+ //if (status == 200) { dev.consoleMsg("Removed unassigned private key pair."); }
1401
+ });
1402
+ }
1403
+ }
1404
+
1405
// Check if wired 802.1x needs updating
1406
var newNetAuthProfileRequested = false;
1407
var srvNetAuthProfile = domain.amtmanager['802.1x'];
@@ -1509,7 +1523,7 @@ module.exports.CreateAmtManager = function (parent) {
1523
// Send a message to Satellite requesting a 802.1x profile for this device
1524
dev.consoleMsg("Requesting 802.1x credentials for " + netAuthStrings[srvNetAuthProfile.authenticationprotocol] + " from MeshCentral Satellite...");
1525
dev.netAuthSatReqId = Buffer.from(parent.crypto.randomBytes(16), 'binary').toString('base64'); // Generate a crypto-secure request id.
1512
- dev.netAuthSatReqData = { domain: domain, wiredConfig: wiredConfig, wirelessConfig: wirelessConfig, devNetAuthProfile: devNetAuthProfile, srvNetAuthProfile: srvNetAuthProfile, profilesToAdd: profilesToAdd, prioritiesInUse: prioritiesInUse, responses: responses }
1526
+ dev.netAuthSatReqData = { domain: domain, wiredConfig: wiredConfig, wirelessConfig: wirelessConfig, devNetAuthProfile: devNetAuthProfile, srvNetAuthProfile: srvNetAuthProfile, profilesToAdd: profilesToAdd, prioritiesInUse: prioritiesInUse, responses: responses, xxCertificates: xxCertificates, xxCertPrivateKeys: xxCertPrivateKeys }
1527
parent.DispatchEvent([srvNetAuthProfile.satellitecredentials], obj, { 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 });
1528
1529
// Set a response timeout
@@ -1527,12 +1541,81 @@ module.exports.CreateAmtManager = function (parent) {
1541
return;
1542
} else {
1543
// No need to call MeshCentral Satellite for a 802.1x profile, so configure everything now.
1530
- attempt8021xSyncEx(dev, { domain: domain, wiredConfig: wiredConfig, wirelessConfig: wirelessConfig, devNetAuthProfile: devNetAuthProfile, srvNetAuthProfile: srvNetAuthProfile, profilesToAdd: profilesToAdd, prioritiesInUse: prioritiesInUse, responses: responses });
1544
+ attempt8021xSyncEx(dev, { domain: domain, wiredConfig: wiredConfig, wirelessConfig: wirelessConfig, devNetAuthProfile: devNetAuthProfile, srvNetAuthProfile: srvNetAuthProfile, profilesToAdd: profilesToAdd, prioritiesInUse: prioritiesInUse, responses: responses, xxCertificates: xxCertificates, xxCertPrivateKeys: xxCertPrivateKeys });
1545
}
1546
}
1547
});
1548
}
1549
1550
+
1551
+ // Check 802.1x root certificate
1552
+ function perform8021xRootCertCheck(dev) {
1553
+ // Check if there is a root certificate to add, if we already have it, get the instance id.
1554
+ if (dev.netAuthCredentials.rootcert) {
1555
+ var matchingRootCertId = null;
1556
+ for (var i in dev.netAuthSatReqData.xxCertificates) {
1557
+ if ((dev.netAuthSatReqData.xxCertificates[i].X509Certificate == dev.netAuthCredentials.rootcert) && (dev.netAuthSatReqData.xxCertificates[i].TrustedRootCertficate)) {
1558
+ matchingRootCertId = dev.netAuthSatReqData.xxCertificates[i].InstanceID;
1559
+ }
1560
+ }
1561
+ if (matchingRootCertId == null) {
1562
+ // Root certificate not found, add it
1563
+ dev.consoleMsg("Setting up new 802.1x root certificate...");
1564
+ const f = function perform8021xRootCertCheckResponse(stack, name, response, status) {
1565
+ if ((status != 200) || (response.Body['ReturnValue'] != 0)) {
1566
+ // Failed to add the root certificate
1567
+ dev.consoleMsg("Failed to sign the certificate request.");
1568
+ } else {
1569
+ // Root certificate added, move on to client certificate checking
1570
+ perform8021xRootCertCheckResponse.dev.netAuthSatReqData.rootCertInstanceId = response.Body.CreatedCertificate.ReferenceParameters.SelectorSet.Selector.Value;
1571
+ perform8021xClientCertCheck(perform8021xRootCertCheckResponse.dev);
1572
+ }
1573
+ }
1574
+ f.dev = dev;
1575
+ dev.amtstack.AMT_PublicKeyManagementService_AddTrustedRootCertificate(dev.netAuthCredentials.rootcert, f);
1576
+ } else {
1577
+ // Root certificate already present, move on to client certificate checking
1578
+ dev.netAuthSatReqData.rootCertInstanceId = matchingRootCertId;
1579
+ perform8021xClientCertCheck(dev);
1580
+ }
1581
+ } else {
1582
+ // No root certificate to check, move on to client certificate checking
1583
+ perform8021xClientCertCheck(dev);
1584
+ }
1585
+ }
1586
+
1587
+ // Check 802.1x client certificate
1588
+ function perform8021xClientCertCheck(dev) {
1589
+ if (dev.netAuthCredentials.certificate) {
1590
+ // The new 802.1x profile includes a new certificate, add it now before adding the 802.1x profiles
1591
+ // dev.netAuthCredentials.certificate must be in DER encoded format
1592
+ dev.consoleMsg("Setting up new 802.1x client certificate...");
1593
+ const f = function AddCertificateResponse(stack, name, response, status) {
1594
+ if ((status != 200) || (response.Body['ReturnValue'] != 0)) {
1595
+ AddCertificateResponse.dev.consoleMsg("Unable to set 802.1x certificate.");
1596
+ } else {
1597
+ // Keep the certificate reference since we need it to add 802.1x profiles
1598
+ const certInstanceId = response.Body.CreatedCertificate.ReferenceParameters.SelectorSet.Selector.Value;
1599
+
1600
+ // Set the 802.1x wired profile in the device
1601
+ AddCertificateResponse.dev.consoleMsg("Setting MeshCentral Satellite 802.1x profile...");
1602
+ const netAuthSatReqData = AddCertificateResponse.dev.netAuthSatReqData;
1603
+ delete dev.netAuthSatReqData;
1604
+ netAuthSatReqData.certInstanceId = certInstanceId;
1605
+ attempt8021xSyncEx(AddCertificateResponse.dev, netAuthSatReqData);
1606
+ }
1607
+ }
1608
+ f.dev = dev;
1609
+ dev.amtstack.AMT_PublicKeyManagementService_AddCertificate(dev.netAuthCredentials.certificate, f);
1610
+ } else {
1611
+ // No 802.1x certificate, set the 802.1x wired profile in the device
1612
+ dev.consoleMsg("Setting MeshCentral Satellite 802.1x profile...");
1613
+ const netAuthSatReqData = dev.netAuthSatReqData;
1614
+ delete dev.netAuthSatReqData;
1615
+ attempt8021xSyncEx(dev, netAuthSatReqData);
1616
+ }
1617
+ }
1618
+
1619
// Set the 802.1x wired profile
1620
function attempt8021xSyncEx(dev, devNetAuthData) {
1621
// Unpack
@@ -1568,8 +1651,21 @@ module.exports.CreateAmtManager = function (parent) {
1651
delete netAuthProfile['ProtectedAccessCredential'];
1652
delete netAuthProfile['PACPassword'];
1653
}
1571
- //if (parseInt(Q('idx_d27clientcert').value) >= 0) { netAuthProfile['ClientCertificate'] = '<a:Address>/wsman</a:Address><a:ReferenceParameters><w:ResourceURI>' + amtstack.CompleteName('AMT_PublicKeyCertificate') + '</w:ResourceURI><w:SelectorSet><w:Selector Name="InstanceID">' + xxCertificates[parseInt(Q('idx_d27clientcert').value)]['InstanceID'] + '</w:Selector></w:SelectorSet></a:ReferenceParameters>'; } else { delete sc['ClientCertificate']; }
1572
- //if (parseInt(Q('idx_d27servercert').value) >= 0) { netAuthProfile['ServerCertificateIssuer'] = '<a:Address>/wsman</a:Address><a:ReferenceParameters><w:ResourceURI>' + amtstack.CompleteName('AMT_PublicKeyCertificate') + '</w:ResourceURI><w:SelectorSet><w:Selector Name="InstanceID">' + xxCertificates[parseInt(Q('idx_d27servercert').value)]['InstanceID'] + '</w:Selector></w:SelectorSet></a:ReferenceParameters>'; } else { delete sc['ServerCertificateIssuer']; }
1654
+
1655
+ // Setup Client Certificate
1656
+ if (devNetAuthData.certInstanceId) {
1657
+ 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>';
1658
+ } else {
1659
+ delete netAuthProfile['ClientCertificate'];
1660
+ }
1661
+
1662
+ // Setup Server Certificate
1663
+ if (devNetAuthData.rootCertInstanceId) {
1664
+ 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>';
1665
+ } else {
1666
+ delete netAuthProfile['ServerCertificateIssuer'];
1667
+ }
1668
+
1669
netAuthProfile['PxeTimeout'] = (typeof srvNetAuthProfile.pxetimeoutinseconds == 'number') ? srvNetAuthProfile.pxetimeoutinseconds : 120;
1670
1671
// If we have a MeshCentral Satellite profile, use that
@@ -1580,10 +1676,14 @@ module.exports.CreateAmtManager = function (parent) {
1676
if (srvNetAuthProfile2.domain && (srvNetAuthProfile2.domain != '')) { netAuthProfile['Domain'] = srvNetAuthProfile2.domain; }
1677
}
1678
}
1679
+
1680
+ console.log('netAuthProfile', netAuthProfile);
1681
+
1682
dev.amtstack.Put('AMT_8021XProfile', netAuthProfile, function (stack, name, responses, status) {
1683
const dev = stack.dev;
1684
if (isAmtDeviceValid(dev) == false) return; // Device no longer exists, ignore this request.
1586
- if (status == 200) { dev.consoleMsg("802.1x wired profile set."); }
1685
+ console.log('AMT_8021XProfile-PUT', responses);
1686
+ if (status == 200) { dev.consoleMsg("802.1x wired profile set."); } else { dev.consoleMsg("Unable to set 802.1x wired profile."); }
1687
attemptWifiSyncEx(dev, devNetAuthData);
1688
});
1689
} else {
@@ -1706,11 +1806,6 @@ module.exports.CreateAmtManager = function (parent) {
1806
if (xresponse[i]['InstanceID'] == keyInstanceId) {
1807
// We found our matching DER key
1808
DERKey = xresponse[i]['DERKey'];
1709
- } else {
1710
- // This is not a matching key, since we are here, clean it up.
1711
- dev.amtstack.Delete('AMT_PublicPrivateKeyPair', { 'InstanceID': xresponse[i]['InstanceID'] }, function (stack, name, response, status) {
1712
- //if (status == 200) { dev.consoleMsg("Removed unassigned private key pair."); }
1713
- });
1809
}
1810
}
1811
if (DERKey == null) { dev.consoleMsg("Failed to match the generated RSA key pair."); return; }