New Intel AMT ACM activation with MeshCMD
Ylian Saint-Hilaire committed
Jun 21, 2019 at 17:18 UTC
9fe71f88421d4174507220e4cf1f9668a0d36d93
8 files changed
+318
-318
agents/MeshCmd-signed.exe
Binary files a/agents/MeshCmd-signed.exe and b/agents/MeshCmd-signed.exe differ
agents/MeshCmd64-signed.exe
Binary files a/agents/MeshCmd64-signed.exe and b/agents/MeshCmd64-signed.exe differ
agents/meshcmd.js
+107
-150
@@ -498,9 +498,10 @@ function run(argv) {
498
activeToCCM();
499
} else if (settings.action == 'amtacm') {
500
// Start activation to ACM
501
- if ((settings.wss == null) || (typeof settings.wss != 'string') || (settings.wss == '')) { console.log('No or invalid \"server name\" specified, use --wss [servername:port].'); exit(1); return; }
502
- if ((settings.profile == null) || (typeof settings.profile != 'string') || (settings.profile == '')) { console.log('No or invalid \"profile name\" specified, use --profile [name].'); exit(1); return; }
503
- settings.protocol = 'http:';
501
+ if ((settings.wss == null) || (typeof settings.wss != 'string') || (settings.wss == '')) { console.log('No server URL specified, use --wss [url].'); exit(1); return; }
502
+ //if ((settings.profile == null) || (typeof settings.profile != 'string') || (settings.profile == '')) { console.log('No or invalid \"profile name\" specified, use --profile [name].'); exit(1); return; }
503
+ if ((typeof settings.profile != 'string') || (settings.profile == '')) { settings.profile = null; }
504
+ //settings.protocol = 'http:';
505
settings.localport = 16992;
506
debug(1, "Settings: " + JSON.stringify(settings));
507
activeToACM();
@@ -848,24 +849,63 @@ function deactivateACMEx() {
849
});
850
}
851
852
+//
853
+// Get Intel AMT activation hashes
854
+//
855
+
856
+var trustedHashes = null;
857
+function getTrustedHashes(amtMei, func, tag) {
858
+ console.log('getTrustedHashes');
859
+ if (trustedHashes != null) { func(tag); }
860
+ trustedHashes = [];
861
+ amtMei.getHashHandles(function (handles) {
862
+ var exitOnCount = handles.length;
863
+ for (var i = 0; i < handles.length; ++i) {
864
+ this.getCertHashEntry(handles[i], function (result) {
865
+ if (result.isActive == 1) { trustedHashes.push(result.certificateHash.toLowerCase()); }
866
+ if (--exitOnCount == 0) { func(tag); }
867
+ });
868
+ }
869
+ });
870
+}
871
872
//
873
// Activate Intel AMT to ACM
874
//
875
876
function activeToACM() {
857
- // See if MicroLMS needs to be started and setup the $$OsAdmin wsman stack
858
- console.log('Starting AMT Provisioning to Admin Control Mode.');
877
+ console.log('Starting Intel AMT provisioning to Admin Control Mode (ACM) attempt...');
878
settings.noconsole = true;
879
+
880
// Display Intel AMT version and activation state
881
mestate = {};
882
var amtMeiModule, amtMei;
883
try { amtMeiModule = require('amt-mei'); amtMei = new amtMeiModule(); } catch (ex) { console.log(ex); exit(1); return; }
884
amtMei.on('error', function (e) { console.log('ERROR: ' + e); exit(1); return; });
865
- amtMei.getProvisioningState(function (result) {
866
- if (result) {
867
- mestate.ProvisioningState = result;
868
- startLms(getFwNonce); // TODO: Fix this so that it works even if LMS already running.
885
+ amtMei.getProvisioningState(function (result) { if (result) { mestate.ProvisioningState = result; } });
886
+ amtMei.getLanInterfaceSettings(0, function (result) { if (result) { mestate.net0 = result; } });
887
+ amtMei.getUuid(function (result) { if ((result != null) && (result.uuid != null)) { mestate.uuid = result.uuid; } });
888
+ amtMei.getDnsSuffix(function (result) {
889
+ if (mestate.ProvisioningState.state !== 0) { console.log("Intel AMT is not in pre-provisioning state: " + mestate.ProvisioningState.stateStr); exit(100); return; }
890
+ if (mestate.uuid == null) { console.log("Unable to get Intel AMT UUID."); exit(100); return; }
891
+ var fqdn = null;
892
+ if ((mestate.net0 == null) && (meinfo.net0.enabled != 0)) { console.log("No Intel AMT wired interface, can't perform ACM activation."); exit(100); return; }
893
+ if (result) { fqdn = result; } // If Intel AMT has a trusted DNS suffix set, use that one.
894
+ else {
895
+ // Look for the DNS suffix for the Intel AMT Ethernet interface
896
+ var interfaces = require('os').networkInterfaces();
897
+ for (var i in interfaces) {
898
+ for (var j in interfaces[i]) {
899
+ if ((interfaces[i][j].mac == mestate.net0.mac) && (interfaces[i][j].fqdn != null) && (interfaces[i][j].fqdn != '')) { fqdn = interfaces[i][j].fqdn; }
900
+ }
901
+ }
902
+ }
903
+ if (fqdn != null) {
904
+ settings.fqdn = fqdn;
905
+ settings.uuid = mestate.uuid;
906
+ getTrustedHashes(amtMei, function () { startLms(getFwNonce, amtMei); });
907
+ } else {
908
+ console.log("Trusted DNS suffix not set, can't perform ACM activation."); exit(100); return;
909
}
910
});
911
}
@@ -873,166 +913,83 @@ function activeToACM() {
913
// Gets the FWNonce from AMT and saves it to a file.
914
function getFwNonce() {
915
osamtstack.BatchEnum(null, ['*AMT_GeneralSettings', '*IPS_HostBasedSetupService'], function (stack, name, responses, status) {
916
+ if (status != 200) { console.log("Unable to get firmware activation nonce, status=" + status); exit(100); return; }
917
var fwNonce = responses['IPS_HostBasedSetupService'].response['ConfigurationNonce'];
918
var digestRealm = responses['AMT_GeneralSettings'].response['DigestRealm'];
878
- var amtMeiModule, amtMei, str;
879
- try { amtMeiModule = require('amt-mei'); amtMei = new amtMeiModule(); } catch (ex) { console.log(ex); exit(1); return; }
880
- amtMei.getLanInterfaceSettings(0, function (result) { if (result) { mestate.net0 = result; } });
881
- amtMei.getDnsSuffix(function (result) {
882
- var fqdn = null;
883
- if ((mestate.net0 == null) && (meinfo.net0.enabled != 0)) { console.log("No Intel AMT wired interface, can't perform ACM activation."); exit(100); return; }
884
- if (result) { fqdn = result; } // If Intel AMT has a trusted DNS suffix set, use that one.
885
- else {
886
- // Look for the DNS suffix for the Intel AMT Ethernet interface
887
- var interfaces = require('os').networkInterfaces();
888
- for (var i in interfaces) {
889
- for (var j in interfaces[i]) {
890
- if ((interfaces[i][j].mac == mestate.net0.mac) && (interfaces[i][j].fqdn != null) && (interfaces[i][j].fqdn != '')) { fqdn = interfaces[i][j].fqdn; }
891
- }
892
- }
893
- }
894
- if (fqdn != null) {
895
- activeToACMEx(fwNonce, fqdn, digestRealm);
896
- } else {
897
- console.log("Trusted DNS suffix not set, can't perform ACM activation."); exit(100); return;
898
- }
899
- });
919
+ activeToACMEx(fwNonce, settings.fqdn, digestRealm, settings.uuid);
920
});
921
}
922
903
-// Sends a message to RCS server using RCS Message Protocol
904
-function sendRCSMessage(socket, status, event, message) {
905
- //console.log('Status: ' + status + '. Event: ' + event + '. Message: ' + message);
906
- if (socket !== null) { socket.write({ "status": status, "event": event, "data": message }); }
907
-}
923
+// Connect to the activation server and perform ACM activation
924
+function activeToACMEx(fwNonce, dnsSuffix, digestRealm, uuid) {
925
+ console.log('FQDN: ' + dnsSuffix);
926
+ console.log('UUID: ' + uuid);
927
+ console.log('Realm: ' + digestRealm);
928
+ console.log('Nonce: ' + fwNonce);
929
+ console.log('Connecting to ' + settings.wss);
930
909
-function activeToACMEx(fwNonce, dnsSuffix, digestRealm) {
910
- // open connection to RCS
911
- console.log('Initializing WebSocket...');
912
- // Establish WebSocket connection to RCS server
913
- var connection = http.request(settings.wss);
931
+ // Establish WebSocket connection to activation server
932
+ var options = http.parseUri(settings.wss);
933
+ options.checkServerIdentity = function (clientName, certs) { }; // TODO
934
+ options.rejectUnauthorized = false;
935
+ var connection = http.request(options);
936
connection.on('upgrade', function (response, socket) {
915
- // WebSocket is up. Handle data on the duplex socket
937
+ console.log('Connected, requesting activation...');
938
+ socket.on('end', function () { console.log('Connection closed'); exit(0); });
939
+ socket.on('error', function () { console.log('Connection error'); exit(100); });
940
socket.on('data', function (data) {
917
- // All messages from RCS are JSON.stringify format and need to be parsed
918
- var message = JSON.parse(data);
919
- // Check RCS Message Protocol version. Exit if version not supported
920
- if (message.version > RCSMessageProtocolVersion) { console.log('Unsupported RCS server.'); socket.end(); exit(0) }
921
- // Handle the AMT provisioning certificate blob (contains provisioning certificate, mcnonce, digital signature and password hash)
922
- if (message.data.provCertObj !== undefined) {
923
- activeToACMEx1(message.data, function (stack, name, responses, status, message) {
924
- if (status !== 200) {
925
- if (status == 2) {
926
- console.log('AMT already provisioned.Exiting ' + status);
927
- sendRCSMessage(socket, "error", "finish", "failed with status: " + status);
928
- } else {
929
- console.log('Failed to fetch activation status, status ' + status);
930
- sendRCSMessage(socket, "error", "finish", "failed with status: " + status);
931
- }
932
- socket.end();
933
- exit(status);
934
- } else if (responses['IPS_HostBasedSetupService'].response['AllowedControlModes'].length != 2) {
935
- console.log('Admin control mode activation not allowed');
936
- sendRCSMessage(socket, "error", "finish", "failed with message: Admin control mode activation not allowed");
937
- socket.end();
938
- exit(status);
941
+ // Parse and check the response
942
+ var cmd = null;
943
+ try { cmd = JSON.parse(data); } catch (ex) { console.log('Unable to parse server response: ' + data); exit(100); return; }
944
+ if (typeof cmd != 'object') { console.log('Invalid server response: ' + cmd); exit(100); return; }
945
+ if (typeof cmd.errorText == 'string') { console.log('Server error: ' + cmd.errorText); exit(100); return; }
946
+ if (cmd.action != 'acmactivate') { console.log('Invalid server response, command: ' + cmd.cmd); exit(100); return; }
947
+ if (typeof cmd.signature != 'string') { console.log('Invalid server signature'); exit(100); return; }
948
+ if (typeof cmd.password != 'string') { console.log('Invalid server password'); exit(100); return; }
949
+ if (typeof cmd.nonce != 'string') { console.log('Invalid server nonce'); exit(100); return; }
950
+ if (typeof cmd.certs != 'object') { console.log('Invalid server certificates'); exit(100); return; }
951
+
952
+ // We are ready to go, perform activation.
953
+ cmd.index = 0;
954
+ performAcmActivation(cmd, function (result) {
955
+ if (result == false) {
956
+ console.log('Intel AMT ACM activation failed.');
957
+ } else {
958
+ if ((cmd.profileScript !== null) && (cmd.rawpassword != null)) {
959
+ console.log("Intel AMT ACM activation success, applying profile...");
960
+ settings.scriptjson = cmd.profileScript;
961
+ settings.password = cmd.rawpassword; // TODO: This is only going to work if the server sends the raw password??
962
+ settings.username = 'admin';
963
+ startMeScriptEx(function () { console.log('Intel AMT profile applied.'); socket.end(); exit(0); }, stack);
964
} else {
940
- activeToACMEx2(message, function (stack, name, responses, status, message) {
941
- if (status != 200) {
942
- console.log('Failed to activate, status ' + status);
943
- sendRCSMessage(socket, "error", "finish", "failed to activate. Status: " + status);
944
- } else if (responses.Body.ReturnValue != 0) {
945
- console.log('Admin control mode activation failed: ' + responses.Body.ReturnValueStr);
946
- sendRCSMessage(socket, "error", "finish", "failed to activate: " + responses.Body.ReturnValueStr);
947
- } else {
948
- if (message.profileScript !== null) {
949
- console.log("Running MEScript...");
950
- settings.scriptjson = message.profileScript;
951
- settings.password = message.amtPassword
952
- settings.username = 'admin';
953
- startMeScriptEx(function () {
954
- console.log('AMT Profile applied');
955
- sendRCSMessage(socket, "ok", "finish", "success");
956
- socket.end();
957
- exit(0);
958
- }, stack);
959
- } else {
960
- sendRCSMessage(socket, "ok", "finish", "success");
961
- socket.end();
962
- exit(0);
963
- }
964
- console.log('AMT Provisioning Success');
965
- }
966
- //socket.end();
967
- //exit(status);
968
- });
965
+ console.log('Intel AMT ACM activation success.');
966
+ socket.end();
967
+ exit(0);
968
}
970
- });
971
- }
972
- if (message.event.toString() == "cmd" && message.data.toString() == "acmready") {
973
- sendRCSMessage(socket, "ok", "message", JSON.stringify(fwNonce));
974
- }
969
+ }
970
+ });
971
});
976
- socket.on('end', function () { console.log('WebSocket closed'); });
977
- sendRCSMessage(socket, "ok", "cmd", { "cmd": "acm", "dnssuffix": dnsSuffix, "profile": settings.profile, 'digestrealm': digestRealm, 'fwnonce': fwNonce });
972
+ socket.write({ client: 'meshcmd', version: 1, action: 'acmactivate', fqdn: dnsSuffix, realm: digestRealm, nonce: fwNonce, uuid: uuid, profile: settings.profile, hashes: trustedHashes });
973
});
974
+ connection.end();
975
}
976
981
-// Detects AMT provisioning state and injects the certificate chain into AMT firmware
982
-function activeToACMEx1(data, callback) {
983
- if (mestate.ProvisioningState.state == 0) {
984
- console.log('Performing ACM provisioning...');
985
- // Perform full provisioning -- AMT was fully unprovisioned
986
- injectCert(0, data, function (stack, name, responses, status, data) {
987
- if (status !== 200) { exit(status); return; }
988
- else if (responses['Body']['ReturnValue'] !== 0) { exit(responses['Body']['ReturnValueStr']); return; }
989
- else if (responses['Body']['ReturnValue'] == 0) {
990
- osamtstack.BatchEnum(null, ['*AMT_GeneralSettings', '*IPS_HostBasedSetupService'], function (stack, name, responses, status) {
991
- callback(stack, name, responses, status, data);
992
- });
993
- }
977
+// Recursive function to inject the provisioning certificates into AMT in the proper order and completes ACM activation
978
+function performAcmActivation(acmdata, func) {
979
+ var leaf = (acmdata.index == 0), root = (acmdata.index == (acmdata.certs.length - 1));
980
+ if ((acmdata.index < acmdata.certs.length) && (acmdata.certs[acmdata.index] != null)) {
981
+ osamtstack.IPS_HostBasedSetupService_AddNextCertInChain(acmdata.certs[acmdata.index], leaf, root, function (stack, name, responses, status) {
982
+ if (status !== 200) { debug('AddNextCertInChain status=' + status); return; }
983
+ else if (responses['Body']['ReturnValue'] !== 0) { debug('AddNextCertInChain error=' + responses['Body']['ReturnValue']); return; }
984
+ else { acmdata.index++; performAcmActivation(acmdata, func); }
985
});
995
- } else if (mestate.ProvisioningState.state == 1) {
996
- // Perform partial provisioning -- AMT was partial unprovisioned
997
- // Currently not functional due to limitations in the HW.
998
- console.log('Partial provisioning flow currently not available.');
999
- exit(0);
1000
- //osamtstack.BatchEnum(null, ['*AMT_GeneralSettings', '*IPS_HostBasedSetupService'], activeToACMEx2);
986
} else {
1002
- // AMT already provisioned
1003
- callback(null, null, null, 2, 'AMT already provisioned. Exiting')
1004
- exit(0);
987
+ osamtstack.IPS_HostBasedSetupService_AdminSetup(2, acmdata.password, acmdata.nonce, 2, acmdata.signature,
988
+ function (stack, name, responses, status) { func((status == 200) && (responses['Body']['ReturnValue'] == 0)); }
989
+ );
990
}
991
}
992
1008
-// Recursive function to inject the provisioning certificates into AMT in the proper order
1009
-function injectCert(index, cert, callback, stack, name, responses, status) {
1010
- var leaf = false;
1011
- var root = false;
1012
- if (index == 0) { leaf = true; }
1013
- if (index == cert.provCertObj.certChain.length - 1) { root = true; }
1014
- if (index < cert.provCertObj.certChain.length) {
1015
- if (cert.provCertObj.certChain[index] !== undefined) {
1016
- osamtstack.IPS_HostBasedSetupService_AddNextCertInChain(cert.provCertObj.certChain[index], leaf, root, function (stack, name, responses, status) {
1017
- if (status !== 200) { exit(status); return; }
1018
- else if (responses['Body']['ReturnValue'] !== 0) { exit(responses['Body']['ReturnValueStr']); return; }
1019
- else if (responses['Body']['ReturnValue'] == 0) {
1020
- index++;
1021
- injectCert(index, cert, callback, stack, name, responses, status);
1022
- }
1023
- });
1024
- }
1025
- } else { callback(stack, name, responses, status, cert); }
1026
-}
1027
-
1028
-// Sends the password hash, mcnonce, and digital signature to complete the admin control mode provisioning
1029
-function activeToACMEx2(data, callback) {
1030
- //var passwordhash = md5hex('admin:' + responses['AMT_GeneralSettings'].response['DigestRealm'] + ':' + data.passwordHash).substring(0, 32);
1031
- //var debugreturn = {"Body": {"ReturnValue": 0}};
1032
- //console.log("DEBUG: Everything up to activation works"); callback(null, null, debugreturn, 200, data);
1033
- osamtstack.IPS_HostBasedSetupService_AdminSetup(2, data.passwordHash, data.mcNonce, 2, data.digitalSignature, function (stack, name, responses, status) { callback(stack, name, responses, status, data); });
1034
-}
1035
-
993
//
994
// Get AMT UUID
995
//
agents/meshcmd.min.js
+107
-150
@@ -498,9 +498,10 @@ function run(argv) {
498
activeToCCM();
499
} else if (settings.action == 'amtacm') {
500
// Start activation to ACM
501
- if ((settings.wss == null) || (typeof settings.wss != 'string') || (settings.wss == '')) { console.log('No or invalid \"server name\" specified, use --wss [servername:port].'); exit(1); return; }
502
- if ((settings.profile == null) || (typeof settings.profile != 'string') || (settings.profile == '')) { console.log('No or invalid \"profile name\" specified, use --profile [name].'); exit(1); return; }
503
- settings.protocol = 'http:';
501
+ if ((settings.wss == null) || (typeof settings.wss != 'string') || (settings.wss == '')) { console.log('No server URL specified, use --wss [url].'); exit(1); return; }
502
+ //if ((settings.profile == null) || (typeof settings.profile != 'string') || (settings.profile == '')) { console.log('No or invalid \"profile name\" specified, use --profile [name].'); exit(1); return; }
503
+ if ((typeof settings.profile != 'string') || (settings.profile == '')) { settings.profile = null; }
504
+ //settings.protocol = 'http:';
505
settings.localport = 16992;
506
debug(1, "Settings: " + JSON.stringify(settings));
507
activeToACM();
@@ -848,24 +849,63 @@ function deactivateACMEx() {
849
});
850
}
851
852
+//
853
+// Get Intel AMT activation hashes
854
+//
855
+
856
+var trustedHashes = null;
857
+function getTrustedHashes(amtMei, func, tag) {
858
+ console.log('getTrustedHashes');
859
+ if (trustedHashes != null) { func(tag); }
860
+ trustedHashes = [];
861
+ amtMei.getHashHandles(function (handles) {
862
+ var exitOnCount = handles.length;
863
+ for (var i = 0; i < handles.length; ++i) {
864
+ this.getCertHashEntry(handles[i], function (result) {
865
+ if (result.isActive == 1) { trustedHashes.push(result.certificateHash.toLowerCase()); }
866
+ if (--exitOnCount == 0) { func(tag); }
867
+ });
868
+ }
869
+ });
870
+}
871
872
//
873
// Activate Intel AMT to ACM
874
//
875
876
function activeToACM() {
857
- // See if MicroLMS needs to be started and setup the $$OsAdmin wsman stack
858
- console.log('Starting AMT Provisioning to Admin Control Mode.');
877
+ console.log('Starting Intel AMT provisioning to Admin Control Mode (ACM) attempt...');
878
settings.noconsole = true;
879
+
880
// Display Intel AMT version and activation state
881
mestate = {};
882
var amtMeiModule, amtMei;
883
try { amtMeiModule = require('amt-mei'); amtMei = new amtMeiModule(); } catch (ex) { console.log(ex); exit(1); return; }
884
amtMei.on('error', function (e) { console.log('ERROR: ' + e); exit(1); return; });
865
- amtMei.getProvisioningState(function (result) {
866
- if (result) {
867
- mestate.ProvisioningState = result;
868
- startLms(getFwNonce); // TODO: Fix this so that it works even if LMS already running.
885
+ amtMei.getProvisioningState(function (result) { if (result) { mestate.ProvisioningState = result; } });
886
+ amtMei.getLanInterfaceSettings(0, function (result) { if (result) { mestate.net0 = result; } });
887
+ amtMei.getUuid(function (result) { if ((result != null) && (result.uuid != null)) { mestate.uuid = result.uuid; } });
888
+ amtMei.getDnsSuffix(function (result) {
889
+ if (mestate.ProvisioningState.state !== 0) { console.log("Intel AMT is not in pre-provisioning state: " + mestate.ProvisioningState.stateStr); exit(100); return; }
890
+ if (mestate.uuid == null) { console.log("Unable to get Intel AMT UUID."); exit(100); return; }
891
+ var fqdn = null;
892
+ if ((mestate.net0 == null) && (meinfo.net0.enabled != 0)) { console.log("No Intel AMT wired interface, can't perform ACM activation."); exit(100); return; }
893
+ if (result) { fqdn = result; } // If Intel AMT has a trusted DNS suffix set, use that one.
894
+ else {
895
+ // Look for the DNS suffix for the Intel AMT Ethernet interface
896
+ var interfaces = require('os').networkInterfaces();
897
+ for (var i in interfaces) {
898
+ for (var j in interfaces[i]) {
899
+ if ((interfaces[i][j].mac == mestate.net0.mac) && (interfaces[i][j].fqdn != null) && (interfaces[i][j].fqdn != '')) { fqdn = interfaces[i][j].fqdn; }
900
+ }
901
+ }
902
+ }
903
+ if (fqdn != null) {
904
+ settings.fqdn = fqdn;
905
+ settings.uuid = mestate.uuid;
906
+ getTrustedHashes(amtMei, function () { startLms(getFwNonce, amtMei); });
907
+ } else {
908
+ console.log("Trusted DNS suffix not set, can't perform ACM activation."); exit(100); return;
909
}
910
});
911
}
@@ -873,166 +913,83 @@ function activeToACM() {
913
// Gets the FWNonce from AMT and saves it to a file.
914
function getFwNonce() {
915
osamtstack.BatchEnum(null, ['*AMT_GeneralSettings', '*IPS_HostBasedSetupService'], function (stack, name, responses, status) {
916
+ if (status != 200) { console.log("Unable to get firmware activation nonce, status=" + status); exit(100); return; }
917
var fwNonce = responses['IPS_HostBasedSetupService'].response['ConfigurationNonce'];
918
var digestRealm = responses['AMT_GeneralSettings'].response['DigestRealm'];
878
- var amtMeiModule, amtMei, str;
879
- try { amtMeiModule = require('amt-mei'); amtMei = new amtMeiModule(); } catch (ex) { console.log(ex); exit(1); return; }
880
- amtMei.getLanInterfaceSettings(0, function (result) { if (result) { mestate.net0 = result; } });
881
- amtMei.getDnsSuffix(function (result) {
882
- var fqdn = null;
883
- if ((mestate.net0 == null) && (meinfo.net0.enabled != 0)) { console.log("No Intel AMT wired interface, can't perform ACM activation."); exit(100); return; }
884
- if (result) { fqdn = result; } // If Intel AMT has a trusted DNS suffix set, use that one.
885
- else {
886
- // Look for the DNS suffix for the Intel AMT Ethernet interface
887
- var interfaces = require('os').networkInterfaces();
888
- for (var i in interfaces) {
889
- for (var j in interfaces[i]) {
890
- if ((interfaces[i][j].mac == mestate.net0.mac) && (interfaces[i][j].fqdn != null) && (interfaces[i][j].fqdn != '')) { fqdn = interfaces[i][j].fqdn; }
891
- }
892
- }
893
- }
894
- if (fqdn != null) {
895
- activeToACMEx(fwNonce, fqdn, digestRealm);
896
- } else {
897
- console.log("Trusted DNS suffix not set, can't perform ACM activation."); exit(100); return;
898
- }
899
- });
919
+ activeToACMEx(fwNonce, settings.fqdn, digestRealm, settings.uuid);
920
});
921
}
922
903
-// Sends a message to RCS server using RCS Message Protocol
904
-function sendRCSMessage(socket, status, event, message) {
905
- //console.log('Status: ' + status + '. Event: ' + event + '. Message: ' + message);
906
- if (socket !== null) { socket.write({ "status": status, "event": event, "data": message }); }
907
-}
923
+// Connect to the activation server and perform ACM activation
924
+function activeToACMEx(fwNonce, dnsSuffix, digestRealm, uuid) {
925
+ console.log('FQDN: ' + dnsSuffix);
926
+ console.log('UUID: ' + uuid);
927
+ console.log('Realm: ' + digestRealm);
928
+ console.log('Nonce: ' + fwNonce);
929
+ console.log('Connecting to ' + settings.wss);
930
909
-function activeToACMEx(fwNonce, dnsSuffix, digestRealm) {
910
- // open connection to RCS
911
- console.log('Initializing WebSocket...');
912
- // Establish WebSocket connection to RCS server
913
- var connection = http.request(settings.wss);
931
+ // Establish WebSocket connection to activation server
932
+ var options = http.parseUri(settings.wss);
933
+ options.checkServerIdentity = function (clientName, certs) { }; // TODO
934
+ options.rejectUnauthorized = false;
935
+ var connection = http.request(options);
936
connection.on('upgrade', function (response, socket) {
915
- // WebSocket is up. Handle data on the duplex socket
937
+ console.log('Connected, requesting activation...');
938
+ socket.on('end', function () { console.log('Connection closed'); exit(0); });
939
+ socket.on('error', function () { console.log('Connection error'); exit(100); });
940
socket.on('data', function (data) {
917
- // All messages from RCS are JSON.stringify format and need to be parsed
918
- var message = JSON.parse(data);
919
- // Check RCS Message Protocol version. Exit if version not supported
920
- if (message.version > RCSMessageProtocolVersion) { console.log('Unsupported RCS server.'); socket.end(); exit(0) }
921
- // Handle the AMT provisioning certificate blob (contains provisioning certificate, mcnonce, digital signature and password hash)
922
- if (message.data.provCertObj !== undefined) {
923
- activeToACMEx1(message.data, function (stack, name, responses, status, message) {
924
- if (status !== 200) {
925
- if (status == 2) {
926
- console.log('AMT already provisioned.Exiting ' + status);
927
- sendRCSMessage(socket, "error", "finish", "failed with status: " + status);
928
- } else {
929
- console.log('Failed to fetch activation status, status ' + status);
930
- sendRCSMessage(socket, "error", "finish", "failed with status: " + status);
931
- }
932
- socket.end();
933
- exit(status);
934
- } else if (responses['IPS_HostBasedSetupService'].response['AllowedControlModes'].length != 2) {
935
- console.log('Admin control mode activation not allowed');
936
- sendRCSMessage(socket, "error", "finish", "failed with message: Admin control mode activation not allowed");
937
- socket.end();
938
- exit(status);
941
+ // Parse and check the response
942
+ var cmd = null;
943
+ try { cmd = JSON.parse(data); } catch (ex) { console.log('Unable to parse server response: ' + data); exit(100); return; }
944
+ if (typeof cmd != 'object') { console.log('Invalid server response: ' + cmd); exit(100); return; }
945
+ if (typeof cmd.errorText == 'string') { console.log('Server error: ' + cmd.errorText); exit(100); return; }
946
+ if (cmd.action != 'acmactivate') { console.log('Invalid server response, command: ' + cmd.cmd); exit(100); return; }
947
+ if (typeof cmd.signature != 'string') { console.log('Invalid server signature'); exit(100); return; }
948
+ if (typeof cmd.password != 'string') { console.log('Invalid server password'); exit(100); return; }
949
+ if (typeof cmd.nonce != 'string') { console.log('Invalid server nonce'); exit(100); return; }
950
+ if (typeof cmd.certs != 'object') { console.log('Invalid server certificates'); exit(100); return; }
951
+
952
+ // We are ready to go, perform activation.
953
+ cmd.index = 0;
954
+ performAcmActivation(cmd, function (result) {
955
+ if (result == false) {
956
+ console.log('Intel AMT ACM activation failed.');
957
+ } else {
958
+ if ((cmd.profileScript !== null) && (cmd.rawpassword != null)) {
959
+ console.log("Intel AMT ACM activation success, applying profile...");
960
+ settings.scriptjson = cmd.profileScript;
961
+ settings.password = cmd.rawpassword; // TODO: This is only going to work if the server sends the raw password??
962
+ settings.username = 'admin';
963
+ startMeScriptEx(function () { console.log('Intel AMT profile applied.'); socket.end(); exit(0); }, stack);
964
} else {
940
- activeToACMEx2(message, function (stack, name, responses, status, message) {
941
- if (status != 200) {
942
- console.log('Failed to activate, status ' + status);
943
- sendRCSMessage(socket, "error", "finish", "failed to activate. Status: " + status);
944
- } else if (responses.Body.ReturnValue != 0) {
945
- console.log('Admin control mode activation failed: ' + responses.Body.ReturnValueStr);
946
- sendRCSMessage(socket, "error", "finish", "failed to activate: " + responses.Body.ReturnValueStr);
947
- } else {
948
- if (message.profileScript !== null) {
949
- console.log("Running MEScript...");
950
- settings.scriptjson = message.profileScript;
951
- settings.password = message.amtPassword
952
- settings.username = 'admin';
953
- startMeScriptEx(function () {
954
- console.log('AMT Profile applied');
955
- sendRCSMessage(socket, "ok", "finish", "success");
956
- socket.end();
957
- exit(0);
958
- }, stack);
959
- } else {
960
- sendRCSMessage(socket, "ok", "finish", "success");
961
- socket.end();
962
- exit(0);
963
- }
964
- console.log('AMT Provisioning Success');
965
- }
966
- //socket.end();
967
- //exit(status);
968
- });
965
+ console.log('Intel AMT ACM activation success.');
966
+ socket.end();
967
+ exit(0);
968
}
970
- });
971
- }
972
- if (message.event.toString() == "cmd" && message.data.toString() == "acmready") {
973
- sendRCSMessage(socket, "ok", "message", JSON.stringify(fwNonce));
974
- }
969
+ }
970
+ });
971
});
976
- socket.on('end', function () { console.log('WebSocket closed'); });
977
- sendRCSMessage(socket, "ok", "cmd", { "cmd": "acm", "dnssuffix": dnsSuffix, "profile": settings.profile, 'digestrealm': digestRealm, 'fwnonce': fwNonce });
972
+ socket.write({ client: 'meshcmd', version: 1, action: 'acmactivate', fqdn: dnsSuffix, realm: digestRealm, nonce: fwNonce, uuid: uuid, profile: settings.profile, hashes: trustedHashes });
973
});
974
+ connection.end();
975
}
976
981
-// Detects AMT provisioning state and injects the certificate chain into AMT firmware
982
-function activeToACMEx1(data, callback) {
983
- if (mestate.ProvisioningState.state == 0) {
984
- console.log('Performing ACM provisioning...');
985
- // Perform full provisioning -- AMT was fully unprovisioned
986
- injectCert(0, data, function (stack, name, responses, status, data) {
987
- if (status !== 200) { exit(status); return; }
988
- else if (responses['Body']['ReturnValue'] !== 0) { exit(responses['Body']['ReturnValueStr']); return; }
989
- else if (responses['Body']['ReturnValue'] == 0) {
990
- osamtstack.BatchEnum(null, ['*AMT_GeneralSettings', '*IPS_HostBasedSetupService'], function (stack, name, responses, status) {
991
- callback(stack, name, responses, status, data);
992
- });
993
- }
977
+// Recursive function to inject the provisioning certificates into AMT in the proper order and completes ACM activation
978
+function performAcmActivation(acmdata, func) {
979
+ var leaf = (acmdata.index == 0), root = (acmdata.index == (acmdata.certs.length - 1));
980
+ if ((acmdata.index < acmdata.certs.length) && (acmdata.certs[acmdata.index] != null)) {
981
+ osamtstack.IPS_HostBasedSetupService_AddNextCertInChain(acmdata.certs[acmdata.index], leaf, root, function (stack, name, responses, status) {
982
+ if (status !== 200) { debug('AddNextCertInChain status=' + status); return; }
983
+ else if (responses['Body']['ReturnValue'] !== 0) { debug('AddNextCertInChain error=' + responses['Body']['ReturnValue']); return; }
984
+ else { acmdata.index++; performAcmActivation(acmdata, func); }
985
});
995
- } else if (mestate.ProvisioningState.state == 1) {
996
- // Perform partial provisioning -- AMT was partial unprovisioned
997
- // Currently not functional due to limitations in the HW.
998
- console.log('Partial provisioning flow currently not available.');
999
- exit(0);
1000
- //osamtstack.BatchEnum(null, ['*AMT_GeneralSettings', '*IPS_HostBasedSetupService'], activeToACMEx2);
986
} else {
1002
- // AMT already provisioned
1003
- callback(null, null, null, 2, 'AMT already provisioned. Exiting')
1004
- exit(0);
987
+ osamtstack.IPS_HostBasedSetupService_AdminSetup(2, acmdata.password, acmdata.nonce, 2, acmdata.signature,
988
+ function (stack, name, responses, status) { func((status == 200) && (responses['Body']['ReturnValue'] == 0)); }
989
+ );
990
}
991
}
992
1008
-// Recursive function to inject the provisioning certificates into AMT in the proper order
1009
-function injectCert(index, cert, callback, stack, name, responses, status) {
1010
- var leaf = false;
1011
- var root = false;
1012
- if (index == 0) { leaf = true; }
1013
- if (index == cert.provCertObj.certChain.length - 1) { root = true; }
1014
- if (index < cert.provCertObj.certChain.length) {
1015
- if (cert.provCertObj.certChain[index] !== undefined) {
1016
- osamtstack.IPS_HostBasedSetupService_AddNextCertInChain(cert.provCertObj.certChain[index], leaf, root, function (stack, name, responses, status) {
1017
- if (status !== 200) { exit(status); return; }
1018
- else if (responses['Body']['ReturnValue'] !== 0) { exit(responses['Body']['ReturnValueStr']); return; }
1019
- else if (responses['Body']['ReturnValue'] == 0) {
1020
- index++;
1021
- injectCert(index, cert, callback, stack, name, responses, status);
1022
- }
1023
- });
1024
- }
1025
- } else { callback(stack, name, responses, status, cert); }
1026
-}
1027
-
1028
-// Sends the password hash, mcnonce, and digital signature to complete the admin control mode provisioning
1029
-function activeToACMEx2(data, callback) {
1030
- //var passwordhash = md5hex('admin:' + responses['AMT_GeneralSettings'].response['DigestRealm'] + ':' + data.passwordHash).substring(0, 32);
1031
- //var debugreturn = {"Body": {"ReturnValue": 0}};
1032
- //console.log("DEBUG: Everything up to activation works"); callback(null, null, debugreturn, 200, data);
1033
- osamtstack.IPS_HostBasedSetupService_AdminSetup(2, data.passwordHash, data.mcNonce, 2, data.digitalSignature, function (stack, name, responses, status) { callback(stack, name, responses, status, data); });
1034
-}
1035
-
993
//
994
// Get AMT UUID
995
//
certoperations.js
+10
-10
@@ -30,12 +30,12 @@ module.exports.CertificateOperations = function (parent) {
30
31
// Sign a Intel AMT ACM activation request
32
obj.signAcmRequest = function (domain, request, user, pass, ipport, nodeid, meshid, computerName, agentId) {
33
- 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;
34
- if (parent.common.validateString(request.nonce, 16, 256) == false) return null;
35
- if (parent.common.validateString(request.realm, 16, 256) == false) return null;
36
- if (parent.common.validateString(request.fqdn, 4, 256) == false) return null;
37
- if (parent.common.validateString(request.hash, 16, 256) == false) return null;
38
- if (parent.common.validateString(request.uuid, 36, 36) == false) return null;
33
+ 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 { 'action': 'acmactivate', 'error': 1, 'errorText': 'Invalid arguments' };
34
+ if (parent.common.validateString(request.nonce, 16, 256) == false) return { 'action': 'acmactivate', 'error': 1, 'errorText': 'Invalid nonce argument' };
35
+ if (parent.common.validateString(request.realm, 16, 256) == false) return { 'action': 'acmactivate', 'error': 1, 'errorText': 'Invalid realm argument' };
36
+ if (parent.common.validateString(request.fqdn, 4, 256) == false) return { 'action': 'acmactivate', 'error': 1, 'errorText': 'Invalid FQDN argument' };
37
+ if (parent.common.validateString(request.hash, 16, 256) == false) return { 'action': 'acmactivate', 'error': 1, 'errorText': 'Invalid hash argument' };
38
+ if (parent.common.validateString(request.uuid, 36, 36) == false) return { 'action': 'acmactivate', 'error': 1, 'errorText': 'Invalid UUID argument' };
39
40
// Look for the signing certificate
41
var signkey = null, certChain = null, hashAlgo = null, certIndex = null;
@@ -44,10 +44,10 @@ module.exports.CertificateOperations = function (parent) {
44
if ((certEntry.sha256 == request.hash) && ((certEntry.cn == '*') || (certEntry.cn == request.fqdn))) { hashAlgo = 'sha256'; signkey = certEntry.key; certChain = certEntry.certs; certIndex = i; break; }
45
if ((certEntry.sha1 == request.hash) && ((certEntry.cn == '*') || (certEntry.cn == request.fqdn))) { hashAlgo = 'sha1'; signkey = certEntry.key; certChain = certEntry.certs; certIndex = i; break; }
46
}
47
- if (signkey == null) return null; // Did not find a match.
47
+ if (signkey == null) return { 'action': 'acmactivate', 'error': 2, 'errorText': 'No signing certificate found' }; // Did not find a match.
48
49
// If the matching certificate is a root cert, issue a leaf cert that matches the fqdn
50
- if (domain.amtacmactivation.certs[certIndex].cn == '*') return; // TODO: Add support for this mode
50
+ if (domain.amtacmactivation.certs[certIndex].cn == '*') return { 'action': 'acmactivate', 'error': 3, 'errorText': 'Unsupported activation' }; // TODO: Add support for this mode
51
52
// Setup both nonces, ready to be signed
53
const mcNonce = Buffer.from(obj.crypto.randomBytes(20), 'binary');
@@ -59,10 +59,10 @@ module.exports.CertificateOperations = function (parent) {
59
var signer = obj.crypto.createSign(hashAlgo);
60
signer.update(Buffer.concat([fwNonce, mcNonce]));
61
signature = signer.sign(signkey, 'base64');
62
- } catch (ex) { return null; }
62
+ } catch (ex) { return { 'action': 'acmactivate', 'error': 4, 'errorText': 'Unable to perform signature' }; }
63
64
// Log the activation request, logging is a required step for activation.
65
- 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;
65
+ 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 { 'action': 'acmactivate', 'error': 5, 'errorText': 'Unable to log operation' };
66
67
// Return the signature with the computed account password hash
68
return { 'action': 'acmactivate', 'signature': signature, 'password': obj.crypto.createHash('md5').update(user + ':' + request.realm + ':' + pass).digest('hex'), 'nonce': mcNonce.toString('base64'), 'certs': certChain };
meshagent.js
+2
-2
@@ -1222,7 +1222,7 @@ module.exports.CreateMeshAgent = function (parent, db, ws, req, args, domain) {
1222
1223
// Agent is asking the server to sign an Intel AMT ACM activation request
1224
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!!!
1225
- if (signResponse != null) {
1225
+ if ((signResponse != null) && (signResponse.error == null)) {
1226
// Log this activation event
1227
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 };
1228
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.
@@ -1232,7 +1232,7 @@ module.exports.CreateMeshAgent = function (parent, db, ws, req, args, domain) {
1232
ChangeAgentCoreInfo({ "intelamt": { user: 'admin', pass: amtpassword, uuid: command.uuid, realm: command.realm } });
1233
1234
// Send the activation response
1235
- obj.send(JSON.stringify(signResponse));
1235
+ //obj.send(JSON.stringify(signResponse)); // DEBUG****************************
1236
}
1237
break;
1238
}
views/default.handlebars
+6
-6
@@ -2766,8 +2766,8 @@
2766
// Windows agent install
2767
//x += "<div id=agins_windows>To add a new computer to device group \"" + EscapeHtml(mesh.name) + "\", download the mesh agent and configuration file and install the agent on the computer to manage.<br /><br />";
2768
x += "<div id=agins_windows>To add a new computer to device group \"" + EscapeHtml(mesh.name) + "\", download the mesh agent and install it the computer to manage. This agent has server and device group information embedded within it.<br /><br />";
2769
- x += addHtmlValue('Mesh Agent', '<a id=aginsw32lnk href="meshagents?id=3&meshid=' + meshid.split('/')[2] + '&installflags=0" download title="32bit version of the MeshAgent">Windows (.exe)</a>');
2770
- x += addHtmlValue('Mesh Agent', '<a id=aginsw64lnk href="meshagents?id=4&meshid=' + meshid.split('/')[2] + '&installflags=0" download title="64bit version of the MeshAgent">Windows x64 (.exe)</a>');
2769
+ x += addHtmlValue('Mesh Agent', '<a id=aginsw32lnk href="meshagents?id=3&meshid=' + meshid.split('/')[2] + '&installflags=0" download onclick="setDialogMode(0)" title="32bit version of the MeshAgent">Windows (.exe)</a>');
2770
+ x += addHtmlValue('Mesh Agent', '<a id=aginsw64lnk href="meshagents?id=4&meshid=' + meshid.split('/')[2] + '&installflags=0" download onclick="setDialogMode(0)" title="64bit version of the MeshAgent">Windows x64 (.exe)</a>');
2771
if (debugmode > 0) { x += addHtmlValue('Settings File', '<a id=aginswmshlnk href="meshsettings?id=' + meshid.split('/')[2] + '&installflags=0" rel="noreferrer noopener" target="_blank">' + EscapeHtml(mesh.name) + ' settings (.msh)</a>'); }
2772
x += "</div>";
2773
@@ -2783,8 +2783,8 @@
2783
2784
// Windows agent uninstall
2785
x += "<div id=agins_windows_un style=display:none>To remove a mesh agent, download the file below, run it and click \"uninstall\".<br /><br />";
2786
- x += addHtmlValue('Mesh Agent', '<a href="meshagents?id=3" download title="32bit version of the MeshAgent">Windows (.exe)</a>');
2787
- x += addHtmlValue('Mesh Agent', '<a href="meshagents?id=4" download title="64bit version of the MeshAgent">Windows x64 (.exe)</a>');
2786
+ x += addHtmlValue('Mesh Agent', '<a href="meshagents?id=3" download onclick="setDialogMode(0)" title="32bit version of the MeshAgent">Windows (.exe)</a>');
2787
+ x += addHtmlValue('Mesh Agent', '<a href="meshagents?id=4" download onclick="setDialogMode(0)" title="64bit version of the MeshAgent">Windows x64 (.exe)</a>');
2788
x += "</div>";
2789
2790
// Linux agent uninstall
@@ -4106,7 +4106,7 @@
4106
++count;
4107
date = new Date(date.getTime() - (1000 * 60 * 60 * 24)); // Substract one day
4108
}
4109
- QH('p10html2', '<table cellpadding=2 cellspacing=0><thead><tr style=><th scope=col style=text-align:center;width:150px>Day</th><th scope=col style=text-align:center><a download href="devicepowerevents.ashx?id=' + currentNode._id + '"><img title="Download power events" src="images/link4.png" /></a>7 Day Power State</th></tr></thead><tbody>' + x + '</tbody></table>');
4109
+ QH('p10html2', '<table cellpadding=2 cellspacing=0><thead><tr style=><th scope=col style=text-align:center;width:150px>Day</th><th scope=col style=text-align:center><a download href="devicepowerevents.ashx?id=' + currentNode._id + '" onclick="setDialogMode(0)"><img title="Download power events" src="images/link4.png" /></a>7 Day Power State</th></tr></thead><tbody>' + x + '</tbody></table>');
4110
}
4111
4112
// Return a color for the given power state
@@ -4261,7 +4261,7 @@
4261
function p10showMeshRouterDialog() {
4262
if (xxdialogMode) return;
4263
var x = "<div>MeshCentral Router is a Windows tool for TCP port mapping. You can, for example, RDP into a remote device thru this server.</div><br />";
4264
- x += addHtmlValue('Win32 Executable', '<a style=cursor:pointer download href="meshagents?meshaction=winrouter">MeshCentralRouter.exe</a>');
4264
+ x += addHtmlValue('Win32 Executable', '<a style=cursor:pointer download href="meshagents?meshaction=winrouter" onclick="setDialogMode(0)">MeshCentralRouter.exe</a>');
4265
setDialogMode(2, "MeshCentral Router", 1, null, x, "fileDownload");
4266
}
4267
webserver.js
+86
@@ -2153,6 +2153,83 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) {
2153
});
2154
}
2155
2156
+ // Handle a Intel AMT activation request
2157
+ function handleAmtActivateWebSocket(ws, req) {
2158
+ const domain = checkUserIpAddress(ws, req);
2159
+ if (domain == null) { ws.send(JSON.stringify({ errorText: 'Invalid domain' })); ws.close(); return; }
2160
+ if (req.query.id == null) { ws.send(JSON.stringify({ errorText: 'Missing group identifier' })); ws.close(); return; }
2161
+
2162
+ // Fetch the mesh object
2163
+ ws.meshid = 'mesh/' + domain.id + '/' + req.query.id;
2164
+ const mesh = obj.meshes[ws.meshid];
2165
+ if (mesh == null) { delete ws.meshid; ws.send(JSON.stringify({ errorText: 'Invalid device group' })); ws.close(); return; }
2166
+ if (mesh.mtype != 1) { ws.send(JSON.stringify({ errorText: 'Invalid device group type' })); ws.close(); return; }
2167
+
2168
+ // Fetch the remote IP:Port for logging
2169
+ const remoteaddr = (req.ip.startsWith('::ffff:')) ? (req.ip.substring(7)) : req.ip;
2170
+ ws.remoteaddrport = remoteaddr + ':' + ws._socket.remotePort;
2171
+
2172
+ // When data is received from the web socket, echo it back
2173
+ ws.on('message', function (data) {
2174
+ // Parse the incoming command
2175
+ var cmd = null;
2176
+ try { cmd = JSON.parse(data); } catch (ex) { };
2177
+ if (cmd == null) return;
2178
+
2179
+ // Process the command
2180
+ switch (cmd.action) {
2181
+ case 'acmactivate': {
2182
+ // Check the command
2183
+ if (cmd.version != 1) { ws.send(JSON.stringify({ errorText: 'Unsupported version' })); ws.close(); return; }
2184
+ if (typeof cmd.hashes != 'object') { ws.send(JSON.stringify({ errorText: 'Invalid hashes' })); ws.close(); return; }
2185
+ if (typeof cmd.fqdn != 'string') { ws.send(JSON.stringify({ errorText: 'Invalid FQDN' })); ws.close(); return; }
2186
+
2187
+ // Get the current Intel AMT policy
2188
+ var mesh = obj.meshes[ws.meshid];
2189
+ if ((mesh == null) || (mesh.amt == null) || (mesh.amt.type != 3) || (domain.amtacmactivation == null) || (domain.amtacmactivation.acmmatch == null) || (mesh.amt.password == null)) { ws.send(JSON.stringify({ errorText: 'Unable to activate' })); ws.close(); return; }
2190
+
2191
+ // Check if we have a FQDN/Hash match
2192
+ var matchingHash = null, matchingCN = null;
2193
+ for (var i in domain.amtacmactivation.acmmatch) {
2194
+ // Check for a matching FQDN
2195
+ if ((domain.amtacmactivation.acmmatch[i].cn == '*') || (domain.amtacmactivation.acmmatch[i].cn.toLowerCase() == cmd.fqdn)) {
2196
+ // Check for a matching certificate
2197
+ if (cmd.hashes.indexOf(domain.amtacmactivation.acmmatch[i].sha256) >= 0) {
2198
+ matchingCN = domain.amtacmactivation.acmmatch[i].cn;
2199
+ matchingHash = domain.amtacmactivation.acmmatch[i].sha256;
2200
+ continue;
2201
+ } else if (cmd.hashes.indexOf(domain.amtacmactivation.acmmatch[i].sha1) >= 0) {
2202
+ matchingCN = domain.amtacmactivation.acmmatch[i].cn;
2203
+ matchingHash = domain.amtacmactivation.acmmatch[i].sha1;
2204
+ continue;
2205
+ }
2206
+ }
2207
+ }
2208
+ if (matchingHash == null) { ws.send(JSON.stringify({ errorText: 'No matching activation certificates' })); ws.close(); return; }
2209
+ if (matchingCN == '*') { ws.send(JSON.stringify({ errorText: 'Wildcard certificate activation not yet supported' })); ws.close(); return; }
2210
+ cmd.hash = matchingHash;
2211
+
2212
+ // Get the Intel AMT admin password, randomize if needed.
2213
+ var amtpassword = ((mesh.amt.password == '') ? getRandomAmtPassword() : mesh.amt.password);
2214
+ if (checkAmtPassword(amtpassword) == false) { ws.send(JSON.stringify({ errorText: 'Invalid Intel AMT password' })); ws.close(); return; } // Invalid Intel AMT password, this should never happen.
2215
+
2216
+ // Agent is asking the server to sign an Intel AMT ACM activation request
2217
+ var signResponse = parent.certificateOperations.signAcmRequest(domain, cmd, 'admin', amtpassword, ws.remoteaddrport, null, ws.meshid, null, null);
2218
+ ws.send(JSON.stringify(signResponse));
2219
+ break;
2220
+ }
2221
+ default: {
2222
+ // This is not a known command
2223
+ ws.send(JSON.stringify({ errorText: 'Invalid command' })); ws.close(); return;
2224
+ }
2225
+ }
2226
+ });
2227
+
2228
+ // If close or error, do nothing.
2229
+ ws.on('error', function (err) { });
2230
+ ws.on('close', function (req) { });
2231
+ }
2232
+
2233
// Handle the web socket echo request, just echo back the data sent
2234
function handleEchoWebSocket(ws, req) {
2235
const domain = checkUserIpAddress(ws, req);
@@ -2790,6 +2867,11 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) {
2867
});
2868
}
2869
2870
+ // Intel AMT ACM activation
2871
+ if ((parent.config.domains[i].amtacmactivation != null) && (parent.config.domains[i].amtacmactivation.acmmatch != null)) {
2872
+ obj.app.ws(url + 'amtactivate', handleAmtActivateWebSocket);
2873
+ }
2874
+
2875
// Creates a login token using the user/pass that is passed in as URL arguments.
2876
// For example: https://localhost/createLoginToken.ashx?user=admin&pass=admin&a=3
2877
// It's not advised to use this to create login tokens since the URL is often logged and you got credentials in the URL.
@@ -3165,5 +3247,9 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) {
3247
// Return the query string portion of the URL, the ? and anything after.
3248
function getQueryPortion(req) { var s = req.url.indexOf('?'); if (s == -1) { if (req.body && req.body.urlargs) { return req.body.urlargs; } return ''; } return req.url.substring(s); }
3249
3250
+ // Generate a random Intel AMT password
3251
+ function checkAmtPassword(p) { return (p.length > 7) && (/\d/.test(p)) && (/[a-z]/.test(p)) && (/[A-Z]/.test(p)) && (/\W/.test(p)); }
3252
+ function getRandomAmtPassword() { var p; do { p = Buffer.from(obj.crypto.randomBytes(9), 'binary').toString('base64').split('/').join('@'); } while (checkAmtPassword(p) == false); return p; }
3253
+
3254
return obj;
3255
};
\ No newline at end of file