diff --git a/amtmanager.js b/amtmanager.js index 9a2e5baa..181303d3 100644 --- a/amtmanager.js +++ b/amtmanager.js @@ -438,34 +438,7 @@ module.exports.CreateAmtManager = function (parent) { // We got a new 802.1x profile devFound.netAuthCredentials = event.response; - console.log('devFound.netAuthCredentials', devFound.netAuthCredentials); - if (devFound.netAuthCredentials.certificate) { - // The new 802.1x profile includes a new certificate, add it now before adding the 802.1x profiles - // devFound.netAuthCredentials.certificate must be in DER encoded format - devFound.consoleMsg("Setting up new 802.1x certificate..."); - - const f = function AddCertificateResponse(stack, name, response, status) { - if ((status != 200) || (response.Body['ReturnValue'] != 0)) { - AddCertificateResponse.dev.consoleMsg("Unable to set 802.1x certificate."); - } else { - console.log('AddCertificate - TODO', response); - // TODO: Keep the certificate reference since we need it to add 802.1x profiles - - // Set the 802.1x wired profile in the device - AddCertificateResponse.dev.consoleMsg("Setting MeshCentral Satellite 802.1x profile..."); - const netAuthSatReqData = AddCertificateResponse.dev.netAuthSatReqData; - attempt8021xSyncEx(AddCertificateResponse.dev, netAuthSatReqData); - } - } - f.dev = devFound; - devFound.amtstack.AMT_PublicKeyManagementService_AddCertificate(devFound.netAuthCredentials.certificate, f); - } else { - // No 802.1x certificate, set the 802.1x wired profile in the device - devFound.consoleMsg("Setting MeshCentral Satellite 802.1x profile..."); - const netAuthSatReqData = devFound.netAuthSatReqData; - delete devFound.netAuthSatReqData; - attempt8021xSyncEx(devFound, netAuthSatReqData); - } + perform8021xRootCertCheck(devFound); break; } } @@ -1363,6 +1336,23 @@ module.exports.CreateAmtManager = function (parent) { // Intel AMT WIFI // + // Check which key pair matches the public key in the certificate + function amtcert_linkCertPrivateKey(certs, keys) { + for (var i in certs) { + var cert = certs[i]; + try { + if (keys.length == 0) return; + var publicKeyPEM = forge.pki.publicKeyToPem(forge.pki.certificateFromAsn1(forge.asn1.fromDer(cert.X509Certificate)).publicKey).substring(28 + 32).replace(/(\r\n|\n|\r)/gm, ""); + for (var j = 0; j < keys.length; j++) { + if (publicKeyPEM === (keys[j]['DERKey'] + '-----END PUBLIC KEY-----')) { + keys[j].XCert = cert; // Link the key pair to the certificate + cert.XPrivateKey = keys[j]; // Link the certificate to the key pair + } + } + } catch (e) { console.log(e); } + } + } + // This method will sync the WIFI profiles from the device and the server, but does not care about profile priority. // 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 // We may want to work on an alternate version that does do priority if requested. @@ -1377,17 +1367,41 @@ module.exports.CreateAmtManager = function (parent) { dev.taskCount = 1; dev.taskCompleted = func; - const objQuery = ['CIM_WiFiEndpointSettings', '*CIM_WiFiPort', '*AMT_WiFiPortConfigurationService', 'CIM_IEEE8021xSettings']; + const objQuery = ['CIM_WiFiEndpointSettings', '*CIM_WiFiPort', '*AMT_WiFiPortConfigurationService', 'CIM_IEEE8021xSettings', 'AMT_PublicKeyCertificate', 'AMT_PublicPrivateKeyPair']; if (parent.config.domains[dev.domainid].amtmanager['802.1x'] != null) { objQuery.push('*AMT_8021XProfile'); } dev.amtstack.BatchEnum(null, objQuery, function (stack, name, responses, status) { const dev = stack.dev; if (isAmtDeviceValid(dev) == false) return; // Device no longer exists, ignore this request. const domain = parent.config.domains[dev.domainid]; + 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. + // See if we need to perform wired or wireless 802.1x configuration const wiredConfig = ((parent.config.domains[dev.domainid].amtmanager['802.1x'] != null) && (responses['AMT_8021XProfile'].status == 200)); const wirelessConfig = ((responses['CIM_WiFiEndpointSettings'].status == 200) && (responses['AMT_WiFiPortConfigurationService'].status == 200) && (responses['CIM_WiFiPort'].status == 200) && (responses['CIM_IEEE8021xSettings'].status == 200)); if (!wiredConfig && !wirelessConfig) { devTaskCompleted(dev); return; } // We can't get wired or wireless settings, ignore and carry on. + // Sort out the certificates + var xxCertificates = responses['AMT_PublicKeyCertificate'].responses; + var xxCertPrivateKeys = responses['AMT_PublicPrivateKeyPair'].responses; + for (var i in xxCertificates) { + xxCertificates[i].TrustedRootCertficate = (xxCertificates[i]['TrustedRootCertficate'] == true); + xxCertificates[i].X509CertificateBin = Buffer.from(xxCertificates[i]['X509Certificate'], 'base64').toString('binary'); + xxCertificates[i].XIssuer = parseCertName(xxCertificates[i]['Issuer']); + xxCertificates[i].XSubject = parseCertName(xxCertificates[i]['Subject']); + } + amtcert_linkCertPrivateKey(xxCertificates, xxCertPrivateKeys); // This links all certificates and private keys + + // Remove any unlinked private keys + for (var i in xxCertPrivateKeys) { + if (!xxCertPrivateKeys[i].XCert) { + console.log('PrivateKey-Removing'); + dev.amtstack.Delete('AMT_PublicPrivateKeyPair', { 'InstanceID': xxCertPrivateKeys[i]['InstanceID'] }, function (stack, name, response, status) { + console.log('PrivateKey-Removed'); + //if (status == 200) { dev.consoleMsg("Removed unassigned private key pair."); } + }); + } + } + // Check if wired 802.1x needs updating var newNetAuthProfileRequested = false; var srvNetAuthProfile = domain.amtmanager['802.1x']; @@ -1509,7 +1523,7 @@ module.exports.CreateAmtManager = function (parent) { // Send a message to Satellite requesting a 802.1x profile for this device dev.consoleMsg("Requesting 802.1x credentials for " + netAuthStrings[srvNetAuthProfile.authenticationprotocol] + " from MeshCentral Satellite..."); dev.netAuthSatReqId = Buffer.from(parent.crypto.randomBytes(16), 'binary').toString('base64'); // Generate a crypto-secure request id. - dev.netAuthSatReqData = { domain: domain, wiredConfig: wiredConfig, wirelessConfig: wirelessConfig, devNetAuthProfile: devNetAuthProfile, srvNetAuthProfile: srvNetAuthProfile, profilesToAdd: profilesToAdd, prioritiesInUse: prioritiesInUse, responses: responses } + dev.netAuthSatReqData = { domain: domain, wiredConfig: wiredConfig, wirelessConfig: wirelessConfig, devNetAuthProfile: devNetAuthProfile, srvNetAuthProfile: srvNetAuthProfile, profilesToAdd: profilesToAdd, prioritiesInUse: prioritiesInUse, responses: responses, xxCertificates: xxCertificates, xxCertPrivateKeys: xxCertPrivateKeys } 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 }); // Set a response timeout @@ -1527,12 +1541,81 @@ module.exports.CreateAmtManager = function (parent) { return; } else { // No need to call MeshCentral Satellite for a 802.1x profile, so configure everything now. - attempt8021xSyncEx(dev, { domain: domain, wiredConfig: wiredConfig, wirelessConfig: wirelessConfig, devNetAuthProfile: devNetAuthProfile, srvNetAuthProfile: srvNetAuthProfile, profilesToAdd: profilesToAdd, prioritiesInUse: prioritiesInUse, responses: responses }); + attempt8021xSyncEx(dev, { domain: domain, wiredConfig: wiredConfig, wirelessConfig: wirelessConfig, devNetAuthProfile: devNetAuthProfile, srvNetAuthProfile: srvNetAuthProfile, profilesToAdd: profilesToAdd, prioritiesInUse: prioritiesInUse, responses: responses, xxCertificates: xxCertificates, xxCertPrivateKeys: xxCertPrivateKeys }); } } }); } + + // Check 802.1x root certificate + function perform8021xRootCertCheck(dev) { + // Check if there is a root certificate to add, if we already have it, get the instance id. + if (dev.netAuthCredentials.rootcert) { + var matchingRootCertId = null; + for (var i in dev.netAuthSatReqData.xxCertificates) { + if ((dev.netAuthSatReqData.xxCertificates[i].X509Certificate == dev.netAuthCredentials.rootcert) && (dev.netAuthSatReqData.xxCertificates[i].TrustedRootCertficate)) { + matchingRootCertId = dev.netAuthSatReqData.xxCertificates[i].InstanceID; + } + } + if (matchingRootCertId == null) { + // Root certificate not found, add it + dev.consoleMsg("Setting up new 802.1x root certificate..."); + const f = function perform8021xRootCertCheckResponse(stack, name, response, status) { + if ((status != 200) || (response.Body['ReturnValue'] != 0)) { + // Failed to add the root certificate + dev.consoleMsg("Failed to sign the certificate request."); + } else { + // Root certificate added, move on to client certificate checking + perform8021xRootCertCheckResponse.dev.netAuthSatReqData.rootCertInstanceId = response.Body.CreatedCertificate.ReferenceParameters.SelectorSet.Selector.Value; + perform8021xClientCertCheck(perform8021xRootCertCheckResponse.dev); + } + } + f.dev = dev; + dev.amtstack.AMT_PublicKeyManagementService_AddTrustedRootCertificate(dev.netAuthCredentials.rootcert, f); + } else { + // Root certificate already present, move on to client certificate checking + dev.netAuthSatReqData.rootCertInstanceId = matchingRootCertId; + perform8021xClientCertCheck(dev); + } + } else { + // No root certificate to check, move on to client certificate checking + perform8021xClientCertCheck(dev); + } + } + + // Check 802.1x client certificate + function perform8021xClientCertCheck(dev) { + if (dev.netAuthCredentials.certificate) { + // The new 802.1x profile includes a new certificate, add it now before adding the 802.1x profiles + // dev.netAuthCredentials.certificate must be in DER encoded format + dev.consoleMsg("Setting up new 802.1x client certificate..."); + const f = function AddCertificateResponse(stack, name, response, status) { + if ((status != 200) || (response.Body['ReturnValue'] != 0)) { + AddCertificateResponse.dev.consoleMsg("Unable to set 802.1x certificate."); + } else { + // Keep the certificate reference since we need it to add 802.1x profiles + const certInstanceId = response.Body.CreatedCertificate.ReferenceParameters.SelectorSet.Selector.Value; + + // Set the 802.1x wired profile in the device + AddCertificateResponse.dev.consoleMsg("Setting MeshCentral Satellite 802.1x profile..."); + const netAuthSatReqData = AddCertificateResponse.dev.netAuthSatReqData; + delete dev.netAuthSatReqData; + netAuthSatReqData.certInstanceId = certInstanceId; + attempt8021xSyncEx(AddCertificateResponse.dev, netAuthSatReqData); + } + } + f.dev = dev; + dev.amtstack.AMT_PublicKeyManagementService_AddCertificate(dev.netAuthCredentials.certificate, f); + } else { + // No 802.1x certificate, set the 802.1x wired profile in the device + dev.consoleMsg("Setting MeshCentral Satellite 802.1x profile..."); + const netAuthSatReqData = dev.netAuthSatReqData; + delete dev.netAuthSatReqData; + attempt8021xSyncEx(dev, netAuthSatReqData); + } + } + // Set the 802.1x wired profile function attempt8021xSyncEx(dev, devNetAuthData) { // Unpack @@ -1568,8 +1651,21 @@ module.exports.CreateAmtManager = function (parent) { delete netAuthProfile['ProtectedAccessCredential']; delete netAuthProfile['PACPassword']; } - //if (parseInt(Q('idx_d27clientcert').value) >= 0) { netAuthProfile['ClientCertificate'] = '/wsman' + amtstack.CompleteName('AMT_PublicKeyCertificate') + '' + xxCertificates[parseInt(Q('idx_d27clientcert').value)]['InstanceID'] + ''; } else { delete sc['ClientCertificate']; } - //if (parseInt(Q('idx_d27servercert').value) >= 0) { netAuthProfile['ServerCertificateIssuer'] = '/wsman' + amtstack.CompleteName('AMT_PublicKeyCertificate') + '' + xxCertificates[parseInt(Q('idx_d27servercert').value)]['InstanceID'] + ''; } else { delete sc['ServerCertificateIssuer']; } + + // Setup Client Certificate + if (devNetAuthData.certInstanceId) { + netAuthProfile['ClientCertificate'] = '/wsman' + dev.amtstack.CompleteName('AMT_PublicKeyCertificate') + '' + devNetAuthData.certInstanceId + ''; + } else { + delete netAuthProfile['ClientCertificate']; + } + + // Setup Server Certificate + if (devNetAuthData.rootCertInstanceId) { + netAuthProfile['ServerCertificateIssuer'] = '/wsman' + dev.amtstack.CompleteName('AMT_PublicKeyCertificate') + '' + devNetAuthData.rootCertInstanceId + ''; + } else { + delete netAuthProfile['ServerCertificateIssuer']; + } + netAuthProfile['PxeTimeout'] = (typeof srvNetAuthProfile.pxetimeoutinseconds == 'number') ? srvNetAuthProfile.pxetimeoutinseconds : 120; // If we have a MeshCentral Satellite profile, use that @@ -1580,10 +1676,14 @@ module.exports.CreateAmtManager = function (parent) { if (srvNetAuthProfile2.domain && (srvNetAuthProfile2.domain != '')) { netAuthProfile['Domain'] = srvNetAuthProfile2.domain; } } } + + console.log('netAuthProfile', netAuthProfile); + dev.amtstack.Put('AMT_8021XProfile', netAuthProfile, function (stack, name, responses, status) { const dev = stack.dev; if (isAmtDeviceValid(dev) == false) return; // Device no longer exists, ignore this request. - if (status == 200) { dev.consoleMsg("802.1x wired profile set."); } + console.log('AMT_8021XProfile-PUT', responses); + if (status == 200) { dev.consoleMsg("802.1x wired profile set."); } else { dev.consoleMsg("Unable to set 802.1x wired profile."); } attemptWifiSyncEx(dev, devNetAuthData); }); } else { @@ -1706,11 +1806,6 @@ module.exports.CreateAmtManager = function (parent) { if (xresponse[i]['InstanceID'] == keyInstanceId) { // We found our matching DER key DERKey = xresponse[i]['DERKey']; - } else { - // This is not a matching key, since we are here, clean it up. - dev.amtstack.Delete('AMT_PublicPrivateKeyPair', { 'InstanceID': xresponse[i]['InstanceID'] }, function (stack, name, response, status) { - //if (status == 200) { dev.consoleMsg("Removed unassigned private key pair."); } - }); } } if (DERKey == null) { dev.consoleMsg("Failed to match the generated RSA key pair."); return; }