More work on 802.1x support with MeshCentral Satellite.

Ylian Saint-Hilaire committed Apr 5, 2022 at 15:09 UTC 631a3a6a7441308498a8ba7b4d8de33929e1096f
2 files changed +126 -23
amtmanager.js
+123 -20
@@ -212,7 +212,7 @@ module.exports.CreateAmtManager = function (parent) {
212 // If a 802.1x profile is active with MeshCentral Satellite, notify Satellite of the removal
213 if (domain.amtmanager['802.1x'] != null) {
214 var reqId = Buffer.from(parent.crypto.randomBytes(16), 'binary').toString('base64'); // Generate a crypto-secure request id.
215 - parent.DispatchEvent([domain.amtmanager['802.1x'].satellitecredentials], obj, { action: 'satellite', satelliteFlags: 2, nodeid: nodeid, icon: 0, domain: nodeid.split('/')[1], nolog: 1, reqid: reqId, authProtocol: -1 });
215 + 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 });
216 }
217
218 return true;
@@ -364,6 +364,7 @@ module.exports.CreateAmtManager = function (parent) {
364 var dev = devices[i];
365 dev.name = event.node.name;
366 dev.icon = event.node.icon;
367 + dev.rname = event.node.rname;
368
369 // If there are any changes, apply them.
370 if (event.node.intelamt != null) {
@@ -396,6 +397,9 @@ module.exports.CreateAmtManager = function (parent) {
397 }
398 case 'meshchange': {
399 // TODO
400 +
401 + // 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.
402 +
403 break;
404 }
405 case 'satelliteResponse': {
@@ -403,23 +407,63 @@ module.exports.CreateAmtManager = function (parent) {
407 var devices = obj.amtDevices[event.nodeid], devFound = null;
408 if (devices != null) { for (var i in devices) { if (devices[i].netAuthSatReqId == event.reqid) { devFound = devices[i]; } } }
409 if (devFound == null) return; // Unable to find a device for this 802.1x profile
406 - delete devFound.netAuthSatReqId;
407 - if (devFound.netAuthSatReqTimer != null) { clearTimeout(devFound.netAuthSatReqTimer); delete devFound.netAuthSatReqTimer; }
408 - if ((event.response == null) || (typeof event.response != 'object') || (typeof event.response.authProtocol != 'number')) {
409 - // Unable to create a 802.1x profile
410 - if (isAmtDeviceValid(devFound) == false) return; // Device no longer exists, ignore this request.
411 - delete devFound.netAuthSatReqData;
412 - devFound.consoleMsg("MeshCentral Satellite could not create a 802.1x profile for this device.");
413 - devTaskCompleted(devFound);
414 - } else {
415 - // We got a new 802.1x profile
416 - devFound.netAuthCredentials = event.response;
417 - devFound.consoleMsg("Setting MeshCentral Satellite 802.1x profile...");
418 -
419 - // Set the 802.1x wired profile in the device
420 - var netAuthSatReqData = devFound.netAuthSatReqData;
421 - delete devFound.netAuthSatReqData;
422 - attempt8021xSyncEx(devFound, netAuthSatReqData);
410 + switch (event.subaction) {
411 + case '802.1x-KeyPair-Request': {
412 + // 802.1x request for public/private key pair be generated
413 + attempt8021xKeyGeneration(devFound);
414 + break;
415 + }
416 + case '802.1x-CSR-Request': {
417 + // 802.1x request for a Certificate Signing Request
418 + attempt8021xCRSRequest(devFound, event);
419 + break;
420 + }
421 + case '802.1x-Profile-Completed': {
422 + // The 802.1x profile request is done, set it in Intel AMT.
423 + if (devFound.netAuthSatReqTimer != null) { clearTimeout(devFound.netAuthSatReqTimer); delete devFound.netAuthSatReqTimer; }
424 +
425 + if ((event.response == null) || (typeof event.response != 'object') || (typeof event.response.action != 'string')) {
426 + // Unable to create a 802.1x profile
427 + delete devFound.netAuthSatReqId;
428 + if (isAmtDeviceValid(devFound) == false) return; // Device no longer exists, ignore this request.
429 + delete devFound.netAuthSatReqData;
430 + devFound.consoleMsg("MeshCentral Satellite could not create a 802.1x profile for this device.");
431 + devTaskCompleted(devFound);
432 + }
433 +
434 + delete devFound.netAuthSatReqId;
435 + if (typeof event.response.authProtocol != 'number') break;
436 +
437 + // We got a new 802.1x profile
438 + devFound.netAuthCredentials = event.response;
439 + if (devFound.netAuthCredentials.certificate) {
440 + // The new 802.1x profile includes a new certificate, add it now before adding the 802.1x profiles
441 + // devFound.netAuthCredentials.certificate must be in DER encoded format
442 + devFound.consoleMsg("Setting up new 802.1x certificate...");
443 + devFound.amtstack.AMT_PublicKeyManagementService_AddCertificate(devFound.netAuthCredentials.certificate, function (stack, name, response, status) {
444 + if (status != 200) {
445 + devFound.consoleMsg("Unable to set 802.1x certificate.");
446 + } else {
447 +
448 + console.log('AddCertificate - TODO', response);
449 + // TODO: Keep the certificate reference since we need it to add 802.1x profiles
450 +
451 + // Set the 802.1x wired profile in the device
452 + devFound.consoleMsg("Setting MeshCentral Satellite 802.1x profile...");
453 + var netAuthSatReqData = devFound.netAuthSatReqData;
454 + delete devFound.netAuthSatReqData;
455 + attempt8021xSyncEx(devFound, netAuthSatReqData);
456 + }
457 + });
458 + } else {
459 + // No 802.1x certificate, set the 802.1x wired profile in the device
460 + devFound.consoleMsg("Setting MeshCentral Satellite 802.1x profile...");
461 + var netAuthSatReqData = devFound.netAuthSatReqData;
462 + delete devFound.netAuthSatReqData;
463 + attempt8021xSyncEx(devFound, netAuthSatReqData);
464 + }
465 + break;
466 + }
467 }
468 break;
469 }
@@ -464,6 +508,7 @@ module.exports.CreateAmtManager = function (parent) {
508
509 // Setup the monitored device
510 dev.name = node.name;
511 + dev.rname = node.rname;
512 dev.icon = node.icon;
513 dev.meshid = node.meshid;
514 dev.intelamt = node.intelamt;
@@ -1461,7 +1506,7 @@ module.exports.CreateAmtManager = function (parent) {
1506 dev.consoleMsg("Requesting 802.1x credentials for " + netAuthStrings[srvNetAuthProfile.authenticationprotocol] + " from MeshCentral Satellite...");
1507 dev.netAuthSatReqId = Buffer.from(parent.crypto.randomBytes(16), 'binary').toString('base64'); // Generate a crypto-secure request id.
1508 dev.netAuthSatReqData = { domain: domain, wiredConfig: wiredConfig, wirelessConfig: wirelessConfig, devNetAuthProfile: devNetAuthProfile, srvNetAuthProfile: srvNetAuthProfile, profilesToAdd: profilesToAdd, prioritiesInUse: prioritiesInUse, responses: responses }
1464 - parent.DispatchEvent([srvNetAuthProfile.satellitecredentials], obj, { action: 'satellite', satelliteFlags: 2, nodeid: dev.nodeid, icon: dev.icon, domain: dev.nodeid.split('/')[1], nolog: 1, reqid: dev.netAuthSatReqId, authProtocol: srvNetAuthProfile.authenticationprotocol, devname: dev.name });
1509 + 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 });
1510
1511 // Set a response timeout
1512 const netAuthTimeoutFunc = function netAuthTimeout() {
@@ -1474,7 +1519,7 @@ module.exports.CreateAmtManager = function (parent) {
1519 }
1520 }
1521 netAuthTimeoutFunc.dev = dev;
1477 - dev.netAuthSatReqTimer = setTimeout(netAuthTimeoutFunc, 10000);
1522 + dev.netAuthSatReqTimer = setTimeout(netAuthTimeoutFunc, 20000);
1523 return;
1524 } else {
1525 // No need to call MeshCentral Satellite for a 802.1x profile, so configure everything now.
@@ -1639,6 +1684,64 @@ module.exports.CreateAmtManager = function (parent) {
1684 devTaskCompleted(dev);
1685 }
1686
1687 + // Request for a RSA key pair generation. This will be used to generate the 802.1x certificate
1688 + function attempt8021xKeyGeneration(dev) {
1689 + dev.amtstack.AMT_PublicKeyManagementService_GenerateKeyPair(0, 2048, function (stack, name, response, status) {
1690 + if ((status != 200) || (response.Body['ReturnValue'] != 0)) {
1691 + // Failed to generate a key pair
1692 + dev.consoleMsg("Failed to generate the requested RSA key pair.");
1693 + } else {
1694 + dev.amtstack.Enum('AMT_PublicPrivateKeyPair', function (stack, name, xresponse, status, keyInstanceId) {
1695 + if (status != 200) {
1696 + // Failed to get the generated key pair
1697 + dev.consoleMsg("Failed to get the generated RSA key pair.");
1698 + } else {
1699 + // We got the key pair
1700 + var DERKey = null;
1701 + for (var i in xresponse) {
1702 + if (xresponse[i]['InstanceID'] == keyInstanceId) {
1703 + // We found our matching DER key
1704 + DERKey = xresponse[i]['DERKey'];
1705 + } else {
1706 + // This is not a matching key, since we are here, clean it up.
1707 + dev.amtstack.Delete('AMT_PublicPrivateKeyPair', { 'InstanceID': xresponse[i]['InstanceID'] }, function (stack, name, response, status) {
1708 + //if (status == 200) { dev.consoleMsg("Removed unassigned private key pair."); }
1709 + });
1710 + }
1711 + }
1712 + if (DERKey == null) { dev.consoleMsg("Failed to match the generated RSA key pair."); return; }
1713 + dev.consoleMsg("Generated a RSA key pair.");
1714 + var domain = parent.config.domains[dev.domainid];
1715 + 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 });
1716 + }
1717 + }, response.Body['KeyPair']['ReferenceParameters']['SelectorSet']['Selector']['Value']);
1718 + }
1719 + });
1720 + }
1721 +
1722 + // 802.1x request to process a Certificate Signing Request, we ask Intel AMT to sign the request
1723 + function attempt8021xCRSRequest(dev, event) {
1724 + console.log('attempt8021xCRSRequest', event);
1725 +
1726 + var keyPair = '<a:EndpointReference><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.keyInstanceId + '</w:Selector></w:SelectorSet></a:ReferenceParameters></a:EndpointReference>'; // keyPair EPR Reference
1727 + var signingAlgorithm = 1; // SHA-256 RSA, highest allowed value.
1728 + var nullSignedCertificateRequest = null; // DEREncodedRequest
1729 +
1730 + dev.amtstack.AMT_PublicKeyManagementService_GeneratePKCS10RequestEx(keyPair, signingAlgorithm, nullSignedCertificateRequest, function (stack, name, response, status) {
1731 + if (status != 200) {
1732 + // Failed to get the generated key pair
1733 + dev.consoleMsg("Failed to sign the certificate request.");
1734 + } else {
1735 + console.log('GeneratePKCS10RequestEx', status, response);
1736 +
1737 + // We got a signed certificate request, return that to the server
1738 + dev.consoleMsg("Generated a signed certificate request.");
1739 + var domain = parent.config.domains[dev.domainid];
1740 + 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 });
1741 + }
1742 + });
1743 + }
1744 +
1745
1746 //
1747 // Intel AMT Server Root Certificate
meshuser.js
+3 -3
@@ -4824,10 +4824,10 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use
4824 case 'satellite': {
4825 // Command indicates this is a MeshCentral Satellite session and what featues it supports
4826 if ((command.setFlags != null) && (typeof command.setFlags == 'number')) { obj.satelliteFlags = command.setFlags; }
4827 - if ((command.reqid != null) && (command.response != null) && (typeof command.satelliteFlags == 'number')) {
4828 - const event = { action: 'satelliteResponse', reqid: command.reqid, response: command.response, satelliteFlags: command.satelliteFlags, nolog: 1 }
4827 + if ((command.reqid != null) && (typeof command.satelliteFlags == 'number')) {
4828 + const event = { action: 'satelliteResponse', subaction: command.subaction, reqid: command.reqid, response: command.response, satelliteFlags: command.satelliteFlags, nolog: 1 }
4829 if (typeof command.nodeid == 'string') { event.nodeid = command.nodeid; }
4830 - parent.parent.DispatchEvent([ '*' ], obj, event);
4830 + parent.parent.DispatchEvent(['*'], obj, event);
4831 }
4832 break;
4833 }