From e45a919c6e30a69af9d3690affbb8e9581028655 Mon Sep 17 00:00:00 2001 From: Ylian Saint-Hilaire Date: Thu, 20 Jun 2019 14:27:57 -0700 Subject: [PATCH] More Intel AMT ACM activation work. --- agents/modules_meshcore/amt-manage.js | 6 +++--- certoperations.js | 15 ++++++++++++- meshagent.js | 31 ++++++++++++++++++++++++--- meshuser.js | 6 ------ 4 files changed, 45 insertions(+), 13 deletions(-) diff --git a/agents/modules_meshcore/amt-manage.js b/agents/modules_meshcore/amt-manage.js index 71de8b4d..5377d0a9 100644 --- a/agents/modules_meshcore/amt-manage.js +++ b/agents/modules_meshcore/amt-manage.js @@ -484,7 +484,7 @@ function AmtManager(agent, db, isdebug) { // Fetch Intel AMT realm and activation nonce and get ready to ACM activation... if (osamtstack != null) { //debug('Trying to get Intel AMT activation information (1)...'); - osamtstack.BatchEnum(null, ['*AMT_GeneralSettings', '*IPS_HostBasedSetupService'], activeToACM2, { fqdn: trustedFqdn, hash: hashMatch }); + osamtstack.BatchEnum(null, ['*AMT_GeneralSettings', '*IPS_HostBasedSetupService'], activeToACM2, { fqdn: trustedFqdn, hash: hashMatch, uuid: mestate.UUID }); } else { //debug('ACM Activation: Trying to get local account info...'); amtMei.getLocalSystemAccount(function (x) { @@ -496,7 +496,7 @@ function AmtManager(agent, db, isdebug) { oswsstack = new wsman(transport, '127.0.0.1', 16992, x.user, x.pass, false); osamtstack = new amt(oswsstack); //debug('Trying to get Intel AMT activation information (2)...'); - osamtstack.BatchEnum(null, ['*AMT_GeneralSettings', '*IPS_HostBasedSetupService'], activeToACM2, { fqdn: trustedFqdn, hash: hashMatch }); + osamtstack.BatchEnum(null, ['*AMT_GeneralSettings', '*IPS_HostBasedSetupService'], activeToACM2, { fqdn: trustedFqdn, hash: hashMatch, uuid: mestate.UUID }); } else { //debug('Unable to get $$OsAdmin password.'); } @@ -509,7 +509,7 @@ function AmtManager(agent, db, isdebug) { if (status != 200) return; var fwNonce = responses['IPS_HostBasedSetupService'].response['ConfigurationNonce']; var digestRealm = responses['AMT_GeneralSettings'].response['DigestRealm']; - agent.SendCommand({ "action": "acmactivate", "nonce": fwNonce, "realm": digestRealm, "fqdn": tag.fqdn, "hash": tag.hash }); + agent.SendCommand({ "action": "acmactivate", "nonce": fwNonce, "realm": digestRealm, "fqdn": tag.fqdn, "hash": tag.hash, "uuid": tag.uuid }); } // Called when the server responds with a ACM activation signature. diff --git a/certoperations.js b/certoperations.js index 25b3b021..754b868f 100644 --- a/certoperations.js +++ b/certoperations.js @@ -29,12 +29,13 @@ module.exports.CertificateOperations = function (parent) { const TopLevelDomainExtendedSupport = { 'net': 2, 'com': 2, 'arpa': 3, 'org': 2, 'gov': 2, 'edu': 2, 'de': 2, 'fr': 3, 'cn': 3, 'nl': 3, 'br': 3, 'mx': 3, 'uk': 3, 'pl': 3, 'tw': 3, 'ca': 3, 'fi': 3, 'be': 3, 'ru': 3, 'se': 3, 'ch': 2, 'dk': 2, 'ar': 3, 'es': 3, 'no': 3, 'at': 3, 'in': 3, 'tr': 3, 'cz': 2, 'ro': 3, 'hu': 3, 'nz': 3, 'pt': 3, 'il': 3, 'gr': 3, 'co': 3, 'ie': 3, 'za': 3, 'th': 3, 'sg': 3, 'hk': 3, 'cl': 2, 'lt': 3, 'id': 3, 'hr': 3, 'ee': 3, 'bg': 3, 'ua': 2 }; // Sign a Intel AMT ACM activation request - obj.signAcmRequest = function (domain, request, user, pass) { + obj.signAcmRequest = function (domain, request, user, pass, ipport, nodeid, meshid, computerName, agentId) { if ((domain == null) || (domain.amtacmactivation == null) || (domain.amtacmactivation.certs == null) || (request == null) || (request.nonce == null) || (request.realm == null) || (request.fqdn == null) || (request.hash == null)) return null; if (parent.common.validateString(request.nonce, 16, 256) == false) return null; if (parent.common.validateString(request.realm, 16, 256) == false) return null; if (parent.common.validateString(request.fqdn, 4, 256) == false) return null; if (parent.common.validateString(request.hash, 16, 256) == false) return null; + if (parent.common.validateString(request.uuid, 36, 36) == false) return null; // Look for the signing certificate var signkey = null, certChain = null, hashAlgo = null, certIndex = null; @@ -60,10 +61,22 @@ module.exports.CertificateOperations = function (parent) { signature = signer.sign(signkey, 'base64'); } catch (ex) { return null; } + // Log the activation request, logging is a required step for activation. + if (obj.logAmtActivation(domain, { time: new Date(), domain: domain.id, amtUuid: request.uuid, certHash: request.hash, hashType: hashAlgo, amtRealm: request.realm, amtFqdn: request.fqdn, user: user, password: pass, ipport: ipport, nodeid: nodeid, meshid: meshid, computerName: computerName, agentId: agentId }) == false) return null; + // Return the signature with the computed account password hash return { 'action': 'acmactivate', 'signature': signature, 'password': obj.crypto.createHash('md5').update(user + ':' + request.realm + ':' + pass).digest('hex'), 'nonce': mcNonce.toString('base64'), 'certs': certChain }; } + // Log the Intel AMT activation operation + obj.logAmtActivation = function (domain, x) { + if ((domain.amtacmactivation == null) || (domain.amtacmactivation.log == null) || (typeof domain.amtacmactivation.log != 'string') || (x == null)) return true; + var logpath = null; + 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); } + try { obj.fs.appendFileSync(logpath, JSON.stringify(x) + '\r\n'); } catch (ex) { console.log(ex); return false; } + return true; + } + // Load Intel AMT ACM activation certificates obj.loadIntelAmtAcmCerts = function (amtacmactivation) { if (amtacmactivation == null) return; diff --git a/meshagent.js b/meshagent.js index acf3ce83..612f2c61 100644 --- a/meshagent.js +++ b/meshagent.js @@ -1210,10 +1210,30 @@ module.exports.CreateMeshAgent = function (parent, db, ws, req, args, domain) { } case 'acmactivate': { + if (obj.agentInfo.capabilities & 0x20) return; // If this is a temporary device, don't do ACM activation. + + // Get the current Intel AMT policy + var mesh = parent.meshes[obj.dbMeshKey]; + if ((mesh == null) || (mesh.amt == null) || (mesh.amt.type != 3) || (domain.amtacmactivation == null) || (domain.amtacmactivation.acmmatch == null) || (mesh.amt.password == null)) break; // If this is not the right policy, ignore this. + + // Get the Intel AMT admin password, randomize if needed. + var amtpassword = ((mesh.amt.password == '') ? getRandomAmtPassword() : mesh.amt.password); + if (checkAmtPassword(amtpassword) == false) return; // Invalid Intel AMT password, this should never happen. + // Agent is asking the server to sign an Intel AMT ACM activation request - //console.log(command); - var signResponse = parent.parent.certificateOperations.signAcmRequest(domain, command, 'admin', 'P@ssw0rd'); // TODO: Place account credentials!!! - if (signResponse != null) { obj.send(JSON.stringify(signResponse)); } + var signResponse = parent.parent.certificateOperations.signAcmRequest(domain, command, 'admin', amtpassword, obj.remoteaddrport, obj.dbNodeKey, obj.dbMeshKey, obj.agentInfo.computerName, obj.agentInfo.agentId); // TODO: Place account credentials!!! + if (signResponse != null) { + // Log this activation event + var event = { etype: 'node', action: 'amtactivate', nodeid: obj.dbNodeKey, domain: domain.id, msg: 'Device requested Intel AMT ACM activation, FQDN: ' + command.fqdn, ip: obj.remoteaddrport }; + if (db.changeStream) { event.noact = 1; } // If DB change stream is active, don't use this event to change the node. Another event will come. + parent.parent.DispatchEvent(['*', obj.dbMeshKey], obj, event); + + // Update the device Intel AMT information + ChangeAgentCoreInfo({ "intelamt": { user: 'admin', pass: amtpassword, uuid: command.uuid, realm: command.realm } }); + + // Send the activation response + //obj.send(JSON.stringify(signResponse)); + } break; } case 'diagnostic': @@ -1307,6 +1327,7 @@ module.exports.CreateMeshAgent = function (parent, db, ws, req, args, domain) { if (device.intelamt.flags) { changes.push('AMT flags (' + device.intelamt.flags + ' --> ' + command.intelamt.flags + ')'); } else { changes.push('AMT flags (' + command.intelamt.flags + ')'); } device.intelamt.flags = command.intelamt.flags; change = 1; log = 1; } + if ((command.intelamt.realm != null) && (device.intelamt.realm != command.intelamt.realm)) { changes.push('AMT realm'); device.intelamt.realm = command.intelamt.realm; change = 1; log = 1; } if ((command.intelamt.host != null) && (device.intelamt.host != command.intelamt.host)) { changes.push('AMT host'); device.intelamt.host = command.intelamt.host; change = 1; log = 1; } if ((command.intelamt.uuid != null) && (device.intelamt.uuid != command.intelamt.uuid)) { changes.push('AMT uuid'); device.intelamt.uuid = command.intelamt.uuid; change = 1; log = 1; } if ((command.intelamt.user != null) && (device.intelamt.user != command.intelamt.user)) { changes.push('AMT user'); device.intelamt.user = command.intelamt.user; change = 1; log = 1; } @@ -1420,5 +1441,9 @@ module.exports.CreateMeshAgent = function (parent, db, ws, req, args, domain) { }); } + // Generate a random Intel AMT password + function checkAmtPassword(p) { return (p.length > 7) && (/\d/.test(p)) && (/[a-z]/.test(p)) && (/[A-Z]/.test(p)) && (/\W/.test(p)); } + function getRandomAmtPassword() { var p; do { p = Buffer.from(parent.crypto.randomBytes(9), 'binary').toString('base64').split('/').join('@'); } while (checkAmtPassword(p) == false); return p; } + return obj; }; diff --git a/meshuser.js b/meshuser.js index 421643fb..053525e6 100644 --- a/meshuser.js +++ b/meshuser.js @@ -734,12 +734,6 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use r = parent.db.getBackupConfig(); break; } - case 'acm': { - var acmrequest = { action: 'acmactivate', nonce: 'H9e099o2wIhnRkjd5vXJWBceqIY=', realm: 'Digest:4DCA0000000000000000000000000000', fqdn: 'vprodemo.com', hash: 'c3846bf24b9e93ca64274c0ec67c1ecc5e024ffcacd2d74019350e81fe546ae4' }; - var response = parent.parent.certificateOperations.signAcmRequest(domain, acmrequest, 'admin', 'P@ssw0rd'); - r = 'ACM Response: ' + JSON.stringify(response); - break; - } default: { // This is an unknown command, return an error message r = 'Unknown command \"' + cmd + '\", type \"help\" for list of avaialble commands.'; break;