More work on Intel AMT provisioning server.
Ylian Saint-Hilaire committed
Mar 11, 2021 at 18:23 UTC
83bb9a6993d82ec05349f29fe9f22f9f2560babf
2 files changed
+98
-10
amthelloserver.js
+97
-9
@@ -16,9 +16,15 @@
16
17
// Construct the Intel AMT hello server. This is used for Intel AMT bare-metal activation on the local LAN.
18
// This server can receive a notification from Intel AMT and attempt activation.
19
+// In Intel documentation, this is called the Setup and Configuration Application (SCA)
20
module.exports.CreateAmtHelloServer = function (parent, config) {
21
var obj = {};
22
23
+ // WSMAN stack
24
+ const CreateWsmanComm = require('./amt/amt-wsman-comm');
25
+ const WsmanStackCreateService = require('./amt/amt-wsman');
26
+ const AmtStackCreateService = require('./amt/amt');
27
+
28
// Start the Intel AMT hello server
29
var port = 9971;
30
if (typeof config.port == 'number') { port = config.port; }
@@ -45,7 +51,7 @@ module.exports.CreateAmtHelloServer = function (parent, config) {
51
console.log('MeshCentral Intel(R) AMT provisioning server running on port ' + port + '.');
52
53
// Example hello data for testing
48
- //processHelloData(Buffer.from('01000300000000004b529b93d413181de4871c697a6b7a2b170220c3846bf24b9e93ca64274c0ec67c1ecc5e024ffcacd2d74019350e81fe546ae4022045140b3247eb9cc8c5b4f0d7b53091f73292089e6e5a63e2749dd3aca9198eda0220d7a7a0fb5d7e2731d771e9484ebcdef71d5f0c3e0a2948782bc83ee0ea699ef402201465fa205397b876faa6f0a9958e5590e40fcc7faa4fb7c2c8677521fb5fb65802202ce1cb0bf9d2f9e102993fbe215152c3b2dd0cabde1c68e5319b839154dbb7f502209acfab7e43c8d880d06b262a94deeee4b4659989c3d0caf19baf6405e41ab7df022016af57a9f676b0ab126095aa5ebadef22ab31119d644ac95cd4b93dbf3f26aeb0220960adf0063e96356750c2965dd0a0867da0b9cbd6e77714aeafb2349ab393da3022068ad50909b04363c605ef13581a939ff2c96372e3f12325b0a6861e1d59f660302206dc47172e01cbcb0bf62580d895fe2b8ac9ad4f873801e0c10b9c837d21eb177022073c176434f1bc6d5adf45b0e76e727287c8de57616c1e6e6141a2b2cbc7d8e4c022043df5774b03e7fef5fe40d931a7bedf1bb2e6b42738c4e6d3841103d3aa7f33902202399561127a57125de8cefea610ddf2fa078b5c8067f4e828290bfb860e84b3c022070a73f7f376b60074248904534b11482d5bf0e698ecc498df52577ebf2e93b9a02204348a0e9444c78cb265e058d5e8944b4d84f9662bd26db257f8934a443c701610220cb3ccbb76031e5e0138f8dd39a23f9de47ffc35e43c1144cea27d46a5ab1cb5f022031ad6648f8104138c738f39ea4320133393e3a18cc02296ef97c2ac9ef6731d00220552f7bdcf1a7af9e6ce672017f4f12abf77240c78e761ac203d1d9d20ac89988022067540a47aa5b9f34570a99723cfefa96a96ee3f0d9b8bf4def9440b8065d665d02207224395222cd588c4f2683716922addb41e39b581ac34fa87b39efa896fbb39e0220cbb522d7b7f127ad6a0113865bdf1cd4102e7d0759af635a7cf4720dc963c53b0220179fbc148a3dd00fd24ea13458cc43bfa7f59c8182d783a513f6ebec100c892402202cabeafe37d06ca22aba7391c0033d25982952c453647349763a3ab5ad6ccf69', 'hex'), '192.168.2.148');
54
+ setTimeout(function () { processHelloData(Buffer.from('01000300000000004b529b93d413181de4871c697a6b7a2b170220c3846bf24b9e93ca64274c0ec67c1ecc5e024ffcacd2d74019350e81fe546ae4022045140b3247eb9cc8c5b4f0d7b53091f73292089e6e5a63e2749dd3aca9198eda0220d7a7a0fb5d7e2731d771e9484ebcdef71d5f0c3e0a2948782bc83ee0ea699ef402201465fa205397b876faa6f0a9958e5590e40fcc7faa4fb7c2c8677521fb5fb65802202ce1cb0bf9d2f9e102993fbe215152c3b2dd0cabde1c68e5319b839154dbb7f502209acfab7e43c8d880d06b262a94deeee4b4659989c3d0caf19baf6405e41ab7df022016af57a9f676b0ab126095aa5ebadef22ab31119d644ac95cd4b93dbf3f26aeb0220960adf0063e96356750c2965dd0a0867da0b9cbd6e77714aeafb2349ab393da3022068ad50909b04363c605ef13581a939ff2c96372e3f12325b0a6861e1d59f660302206dc47172e01cbcb0bf62580d895fe2b8ac9ad4f873801e0c10b9c837d21eb177022073c176434f1bc6d5adf45b0e76e727287c8de57616c1e6e6141a2b2cbc7d8e4c022043df5774b03e7fef5fe40d931a7bedf1bb2e6b42738c4e6d3841103d3aa7f33902202399561127a57125de8cefea610ddf2fa078b5c8067f4e828290bfb860e84b3c022070a73f7f376b60074248904534b11482d5bf0e698ecc498df52577ebf2e93b9a02204348a0e9444c78cb265e058d5e8944b4d84f9662bd26db257f8934a443c701610220cb3ccbb76031e5e0138f8dd39a23f9de47ffc35e43c1144cea27d46a5ab1cb5f022031ad6648f8104138c738f39ea4320133393e3a18cc02296ef97c2ac9ef6731d00220552f7bdcf1a7af9e6ce672017f4f12abf77240c78e761ac203d1d9d20ac89988022067540a47aa5b9f34570a99723cfefa96a96ee3f0d9b8bf4def9440b8065d665d02207224395222cd588c4f2683716922addb41e39b581ac34fa87b39efa896fbb39e0220cbb522d7b7f127ad6a0113865bdf1cd4102e7d0759af635a7cf4720dc963c53b0220179fbc148a3dd00fd24ea13458cc43bfa7f59c8182d783a513f6ebec100c892402202cabeafe37d06ca22aba7391c0033d25982952c453647349763a3ab5ad6ccf69', 'hex'), '192.168.2.148'); }, 500);
55
56
// Parse Intel AMT hello data
57
function parseHelloData(data, addr) {
@@ -91,14 +97,96 @@ module.exports.CreateAmtHelloServer = function (parent, config) {
97
// Process incoming Intel AMT hello data
98
function processHelloData(data, addr) {
99
// Check if we can parse the incoming data
94
- const amtHello = parseHelloData(data, addr);
95
- if (amtHello == null) return; // Invalid Intel AMT hello
96
-
97
- console.log(JSON.stringify(amtHello, null, 2));
98
- // TODO: Compute the nodeid for this device using the device GUID
99
- // TODO: Get device group and assumed trusted FQDN
100
- // TODO: Get an activation certificate chain
101
- // TODO: Setup a connection to the Intel AMT device
100
+ const dev = parseHelloData(data, addr);
101
+ if (dev == null) { parent.debug('amtsca', addr, 'Got invalid hello from: ' + addr); return; } // Invalid Intel AMT hello
102
+ parent.debug('amtsca', 'Got hello from ' + addr);
103
+
104
+ // Get assumed trusted FQDN and device group
105
+ dev.trustedFqdn = config.trustedfqdn;
106
+ var mesh = parent.webserver.meshes[config.devicegroup];
107
+ if ((mesh == null) || (mesh.mtype !== 1) || (typeof mesh.amt !== 'object') || (typeof mesh.amt.type !== 'number')) { parent.debug('amtsca', addr, 'Invalid device group for Intel AMT activation.'); return; }
108
+ if ((mesh.amt.type != 3) && (mesh.amt.type != 4)) { parent.debug('amtsca', addr, 'Device group does not have ACM activation policy.'); return; }
109
+ dev.mesh = mesh;
110
+ dev.domainid = mesh.domain;
111
+
112
+ // Compute the nodeid for this device using the device GUID
113
+ const g = dev.guid.split('-').join('');
114
+ const id = Buffer.from(g + g + g, 'hex').toString('base64');
115
+ dev.nodeid = 'node/' + mesh.domain + '/' + id;
116
+
117
+ // Attempts reverse DNS loopup on the device IP address
118
+ const func = function dnsReverseLoopup(err, hostnames) {
119
+ var hostname = dnsReverseLoopup.addr;
120
+ if ((err == null) && (hostnames != null) && (hostnames.length > 0)) { hostname = hostnames[0]; }
121
+ dnsReverseLoopup.dev.hostname = hostname;
122
+ processHelloDataEx1(dnsReverseLoopup.dev);
123
+ }
124
+ func.addr = addr;
125
+ func.dev = dev;
126
+ require('dns').reverse(addr, func);
127
+ }
128
+
129
+ // Check if this device has any way to be activated in ACM using our server certificates.
130
+ function checkAcmActivation(hello) {
131
+ var domain = parent.config.domains[hello.domainid];
132
+ if ((domain == null) || (domain.amtacmactivation == null) || (domain.amtacmactivation.certs == null) || (domain.amtacmactivation.certs.length == 0)) return null;
133
+ const activationCerts = domain.amtacmactivation.certs;
134
+
135
+ // Get the trusted FQDN of the device
136
+ var trustedFqdn = hello.trustedFqdn;
137
+
138
+ // Find a matching certificate
139
+ for (var i in activationCerts) {
140
+ var cert = activationCerts[i];
141
+ if ((cert.cn == '*') || (cert.cn == trustedFqdn)) {
142
+ for (var j in hello.hashes) {
143
+ var hash = hello.hashes[j];
144
+ if (hash == cert.sha256) { return { cert: cert, fqdn: trustedFqdn, hash: cert.sha256 }; } // Found a match
145
+ else if (hash == cert.sha1) { return { cert: cert, fqdn: trustedFqdn, hash: cert.sha1 }; } // Found a match
146
+ }
147
+ }
148
+ }
149
+ return null; // Did not find a match
150
+ }
151
+
152
+ function processHelloDataEx1(dev) {
153
+ // Get an activation certificate chain
154
+ const certinfo = checkAcmActivation(dev);
155
+ if (certinfo == null) { parent.debug('amtsca', dev.hostname, 'Unable to find a matching ACM activation certificate.'); destroyDevice(dev); return; }
156
+ dev.certinfo = certinfo;
157
+ dev.certinfo.cert.certs = dev.certinfo.cert.certs.reverse();
158
+
159
+ // Turn DER encoded certs into PEM
160
+ for (var i in dev.certinfo.cert.certs) {
161
+ if (dev.certinfo.cert.certs[i].indexOf('-----BEGIN CERTIFICATE-----') == -1) {
162
+ dev.certinfo.cert.certs[i] = '-----BEGIN CERTIFICATE-----\r\n' + dev.certinfo.cert.certs[i] + '\r\n-----END CERTIFICATE-----';
163
+ }
164
+ }
165
+
166
+ //console.log(JSON.stringify(dev.certinfo, null, 2));
167
+
168
+ // Setup a connection to the Intel AMT device
169
+ parent.debug('amtsca', dev.hostname, 'Launching TLS connection...');
170
+ var comm = CreateWsmanComm(dev.hostname, 16993, 'admin', 'P@ssw0rd', 1, { cert: dev.certinfo.cert.certs, key: dev.certinfo.cert.key }); // Perform TLS connection
171
+ comm.xtlsFingerprint = 0; // No Intel AMT certificate checking.
172
+ var wsstack = WsmanStackCreateService(comm);
173
+ dev.amtstack = AmtStackCreateService(wsstack);
174
+ dev.amtstack.dev = dev;
175
+ dev.amtstack.BatchEnum(null, ['*AMT_GeneralSettings', '*IPS_HostBasedSetupService'], processHelloDataEx2);
176
+ }
177
+
178
+ function processHelloDataEx2(stack, name, responses, status) {
179
+ const dev = stack.dev;
180
+ if (status != 200) { parent.debug('amtsca', dev.hostname, 'Failed TLS connection, status=' + status + '.'); destroyDevice(dev); return; }
181
+ parent.debug('amtsca', dev.hostname, 'Succesful TLS connection.');
182
+
183
+ //console.log('processHelloDataEx2', status, responses);
184
+ }
185
+
186
+ // Do aggressive cleanup on the device
187
+ function destroyDevice(dev) {
188
+ if (dev.amtstack != null) { delete dev.amtstack.dev; delete dev.amtstack; }
189
+ delete dev.certinfo;
190
}
191
192
return obj;
meshcentral.js
+1
-1
@@ -1650,7 +1650,7 @@ function CreateMeshCentralServer(config, args) {
1650
});
1651
1652
// Setup Intel AMT hello server
1653
- if ((typeof config.settings.amthelloserver == 'object') && (typeof config.settings.amthelloserver.devicegroup == 'string')) {
1653
+ if ((typeof config.settings.amthelloserver == 'object') && (typeof config.settings.amthelloserver.devicegroup == 'string') && (typeof config.settings.amthelloserver.newmebxpassword == 'string') && (typeof config.settings.amthelloserver.trustedfqdn == 'string')) {
1654
obj.amthelloserver = require('./amthelloserver').CreateAmtHelloServer(obj, config.settings.amthelloserver);
1655
}
1656