Early work on support for MeshCentral Satellite for AMT 802.1x.

This commit is contained in:
Ylian Saint-Hilaire 2022-04-02 15:27:15 -07:00
parent 5a81c84d67
commit 8e5c71cea2
2 changed files with 143 additions and 39 deletions

View File

@ -22,6 +22,9 @@ module.exports.CreateAmtManager = function (parent) {
obj.rootCertBase64 = obj.parent.certificates.root.cert.split('-----BEGIN CERTIFICATE-----').join('').split('-----END CERTIFICATE-----').join('').split('\r').join('').split('\n').join('') obj.rootCertBase64 = obj.parent.certificates.root.cert.split('-----BEGIN CERTIFICATE-----').join('').split('-----END CERTIFICATE-----').join('').split('\r').join('').split('\n').join('')
obj.rootCertCN = obj.parent.certificateOperations.forge.pki.certificateFromPem(obj.parent.certificates.root.cert).subject.getField('CN').value; obj.rootCertCN = obj.parent.certificateOperations.forge.pki.certificateFromPem(obj.parent.certificates.root.cert).subject.getField('CN').value;
// 802.1x authentication protocols
const netAuthStrings = ['eap-tls', 'eap-ttls/mschapv2', 'peapv0/eap-mschapv2', 'peapv1/eap-gtc', 'eap-fast/mschapv2', 'eap-fast/gtc', 'eap-md5', 'eap-psk', 'eap-sim', 'eap-aka', 'eap-fast/tls'];
// WSMAN stack // WSMAN stack
const CreateWsmanComm = require('./amt/amt-wsman-comm'); const CreateWsmanComm = require('./amt/amt-wsman-comm');
const WsmanStackCreateService = require('./amt/amt-wsman'); const WsmanStackCreateService = require('./amt/amt-wsman');
@ -102,7 +105,14 @@ module.exports.CreateAmtManager = function (parent) {
} else if ([5, 7, 32768, 32769].indexOf(wifiProfile.authentication) >= 0) { } else if ([5, 7, 32768, 32769].indexOf(wifiProfile.authentication) >= 0) {
// 802.1x authentication // 802.1x authentication
if ((wifiProfile['802.1x'] == null) && (typeof wifiProfile['802.1x'] != 'object')) continue; if ((wifiProfile['802.1x'] == null) && (typeof wifiProfile['802.1x'] != 'object')) continue;
const netAuthStrings = ['eap-tls', 'eap-ttls/mschapv2', 'peapv0/eap-mschapv2', 'peapv1/eap-gtc', 'eap-fast/mschapv2', 'eap-fast/gtc', 'eap-md5', 'eap-psk', 'eap-sim', 'eap-aka', 'eap-fast/tls'];
if (wifiProfile['802.1x'].satellitecredentials != null) {
if (typeof wifiProfile['802.1x'].satellitecredentials != 'string') continue;
const userSplit = wifiProfile['802.1x'].satellitecredentials.split('/');
if (userSplit.length > 3) continue;
if (userSplit.length == 2) { wifiProfile['802.1x'].satellitecredentials = 'user/' + domain.id + '/' + userSplit[1]; }
else if (userSplit.length == 1) { wifiProfile['802.1x'].satellitecredentials = 'user/' + domain.id + '/' + userSplit[0]; }
}
if (typeof wifiProfile['802.1x'].servercertificatename != 'string') { if (typeof wifiProfile['802.1x'].servercertificatename != 'string') {
delete wifiProfile['802.1x'].servercertificatenamecomparison; delete wifiProfile['802.1x'].servercertificatenamecomparison;
@ -129,9 +139,16 @@ module.exports.CreateAmtManager = function (parent) {
// Check 802.1x wired profile if present // Check 802.1x wired profile if present
if ((domain.amtmanager['802.1x'] != null) && (typeof domain.amtmanager['802.1x'] == 'object')) { if ((domain.amtmanager['802.1x'] != null) && (typeof domain.amtmanager['802.1x'] == 'object')) {
const netAuthStrings = ['eap-tls', 'eap-ttls/mschapv2', 'peapv0/eap-mschapv2', 'peapv1/eap-gtc', 'eap-fast/mschapv2', 'eap-fast/gtc', 'eap-md5', 'eap-psk', 'eap-sim', 'eap-aka', 'eap-fast/tls']; if (domain.amtmanager['802.1x'].satellitecredentials != null) {
if (typeof domain.amtmanager['802.1x'].satellitecredentials != 'string') { delete domain.amtmanager['802.1x']; } else {
const userSplit = domain.amtmanager['802.1x'].satellitecredentials.split('/');
if (userSplit.length > 3) { delete domain.amtmanager['802.1x']; }
else if (userSplit.length == 2) { domain.amtmanager['802.1x'].satellitecredentials = 'user/' + domain.id + '/' + userSplit[1]; }
else if (userSplit.length == 1) { domain.amtmanager['802.1x'].satellitecredentials = 'user/' + domain.id + '/' + userSplit[0]; }
}
}
if (typeof domain.amtmanager['802.1x'].servercertificatename != 'string') { if ((domain.amtmanager['802.1x'].satellitecredentials != null) && (typeof domain.amtmanager['802.1x'].servercertificatename != 'string')) {
delete domain.amtmanager['802.1x'].servercertificatenamecomparison; delete domain.amtmanager['802.1x'].servercertificatenamecomparison;
const serverCertCompareStrings = ['', '', 'fullname', 'domainsuffix']; const serverCertCompareStrings = ['', '', 'fullname', 'domainsuffix'];
if (typeof domain.amtmanager['802.1x'].servercertificatenamecomparison == 'string') { if (typeof domain.amtmanager['802.1x'].servercertificatenamecomparison == 'string') {
@ -140,7 +157,7 @@ module.exports.CreateAmtManager = function (parent) {
} }
} }
if (typeof domain.amtmanager['802.1x'].authenticationprotocol == 'string') { if ((domain.amtmanager['802.1x'].satellitecredentials != null) && (typeof domain.amtmanager['802.1x'].authenticationprotocol == 'string')) {
domain.amtmanager['802.1x'].authenticationprotocol = netAuthStrings.indexOf(domain.amtmanager['802.1x'].authenticationprotocol.toLowerCase()); domain.amtmanager['802.1x'].authenticationprotocol = netAuthStrings.indexOf(domain.amtmanager['802.1x'].authenticationprotocol.toLowerCase());
if (domain.amtmanager['802.1x'].authenticationprotocol == -1) { delete domain.amtmanager['802.1x']; } if (domain.amtmanager['802.1x'].authenticationprotocol == -1) { delete domain.amtmanager['802.1x']; }
} }
@ -395,6 +412,35 @@ module.exports.CreateAmtManager = function (parent) {
// TODO // TODO
break; break;
} }
case 'satelliteResponse': {
if ((typeof event.nodeid != 'string') || (typeof event.reqid != 'string') || (event.satelliteFlags != 2)) return;
var devices = obj.amtDevices[event.nodeid], devFound = null;
if (devices != null) { for (var i in devices) { if (devices[i].netAuthSatReqId == event.reqid) { devFound = devices[i]; } } }
if (devFound == null) return; // Unable to find a device for this 802.1x profile
delete devFound.netAuthSatReqId;
if (devFound.netAuthSatReqTimer != null) { clearTimeout(devFound.netAuthSatReqTimer); delete devFound.netAuthSatReqTimer; }
if ((event.response == null) || (typeof event.response != 'object') || (typeof event.response.authProtocol != 'number')) {
// Unable to create a 802.1x profile
if (isAmtDeviceValid(devFound) == false) return; // Device no longer exists, ignore this request.
delete devFound.netAuthSatReqDev;
delete devFound.netAuthSatReqSrv;
devFound.consoleMsg("MeshCentral Satellite could not create a 802.1x profile for this device.");
devTaskCompleted(devFound);
} else {
// We got a new 802.1x profile
if (devFound.netAuthCredentials == null) { devFound.netAuthCredentials = {}; }
devFound.netAuthCredentials[event.response.authProtocol] = event.response;
devFound.consoleMsg("Setting MeshCentral Satellite 802.1x profile...");
// Set the 802.1x wired profile in the device
var devNetAuthProfile = devFound.netAuthSatReqDev;
var srvNetAuthProfile = devFound.netAuthSatReqSrv;
delete devFound.netAuthSatReqDev;
delete devFound.netAuthSatReqSrv;
attempt8021xSyncEx(devFound, devNetAuthProfile, srvNetAuthProfile);
}
break;
}
} }
} }
@ -1314,16 +1360,55 @@ module.exports.CreateAmtManager = function (parent) {
} else if ((typeof srvNetAuthProfile == 'object') && (devNetAuthProfile != null)) { } else if ((typeof srvNetAuthProfile == 'object') && (devNetAuthProfile != null)) {
// Check if the existing 802.1x profile look good // Check if the existing 802.1x profile look good
if (devNetAuthProfile.AuthenticationProtocol != srvNetAuthProfile.authenticationprotocol) { match = false; } if (devNetAuthProfile.AuthenticationProtocol != srvNetAuthProfile.authenticationprotocol) { match = false; }
if (devNetAuthProfile.RoamingIdentity != srvNetAuthProfile.roamingidentity) { match = false; }
if (devNetAuthProfile.ServerCertificateName != srvNetAuthProfile.servercertificatename) { match = false; } if (devNetAuthProfile.ServerCertificateName != srvNetAuthProfile.servercertificatename) { match = false; }
if (devNetAuthProfile.ServerCertificateNameComparison != srvNetAuthProfile.servercertificatenamecomparison) { match = false; } if (devNetAuthProfile.ServerCertificateNameComparison != srvNetAuthProfile.servercertificatenamecomparison) { match = false; }
if (devNetAuthProfile.ActiveInS0 != srvNetAuthProfile.availableins0) { match = false; }
if (typeof srvNetAuthProfile.satellitecredentials != 'string') {
// Credentials for this profile are in the config file
if (devNetAuthProfile.RoamingIdentity != srvNetAuthProfile.roamingidentity) { match = false; }
if (devNetAuthProfile.Username != srvNetAuthProfile.username) { match = false; } if (devNetAuthProfile.Username != srvNetAuthProfile.username) { match = false; }
if (devNetAuthProfile.Domain != srvNetAuthProfile.domain) { match = false; } if (devNetAuthProfile.Domain != srvNetAuthProfile.domain) { match = false; }
if (devNetAuthProfile.ActiveInS0 != srvNetAuthProfile.availableins0) { match = false; } }
} }
// If there is a mismatch, set the new 802.1x profile // If there is a mismatch, set the new 802.1x profile
if (match == false) { if (match == false) {
if ((typeof srvNetAuthProfile.satellitecredentials == 'string') && ((dev.netAuthCredentials == null) || (dev.netAuthCredentials[srvNetAuthProfile.authenticationprotocol] == null))) {
// Credentials for this profile are provided using MeshCentral Satellite
// 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 = 'wired-' + Buffer.from(parent.crypto.randomBytes(16), 'binary').toString('base64'); // Generate a crypto-secure request id.
dev.netAuthSatReqDev = devNetAuthProfile;
dev.netAuthSatReqSrv = srvNetAuthProfile;
parent.DispatchEvent([srvNetAuthProfile.satellitecredentials], obj, { action: 'satellite', satelliteFlags: 2, nodeid: dev.nodeid, domain: dev.nodeid.split('/')[1], nolog: 1, reqid: dev.netAuthSatReqId, authProtocol: srvNetAuthProfile.authenticationprotocol, devname: dev.name });
// Set a response timeout
const netAuthTimeoutFunc = function netAuthTimeout() {
if (isAmtDeviceValid(netAuthTimeout.dev) == false) return; // Device no longer exists, ignore this request.
if (dev.netAuthSatReqId != null) {
delete netAuthTimeout.dev.netAuthSatReqId;
delete netAuthTimeout.dev.netAuthSatReqDev;
delete netAuthTimeout.dev.netAuthSatReqSrv;
netAuthTimeout.dev.consoleMsg("MeshCentral Satellite did not respond in time, 802.1x profile will not be set.");
devTaskCompleted(netAuthTimeout.dev);
}
}
netAuthTimeoutFunc.dev = dev;
dev.netAuthSatReqTimer = setTimeout(netAuthTimeoutFunc, 10000);
return;
} else {
// Set the 802.1x wired profile in the device
attempt8021xSyncEx(dev, devNetAuthProfile, srvNetAuthProfile);
}
} else {
// Nothing to do
devTaskCompleted(dev);
}
});
}
// Set the 802.1x wired profile
function attempt8021xSyncEx(dev, devNetAuthProfile, srvNetAuthProfile) {
var netAuthProfile = Clone(devNetAuthProfile); var netAuthProfile = Clone(devNetAuthProfile);
netAuthProfile['Enabled'] = ((srvNetAuthProfile != null) && (typeof srvNetAuthProfile == 'object')); netAuthProfile['Enabled'] = ((srvNetAuthProfile != null) && (typeof srvNetAuthProfile == 'object'));
if (netAuthProfile['Enabled']) { if (netAuthProfile['Enabled']) {
@ -1350,6 +1435,14 @@ module.exports.CreateAmtManager = function (parent) {
//if (parseInt(Q('idx_d27clientcert').value) >= 0) { netAuthProfile['ClientCertificate'] = '<a:Address>/wsman</a:Address><a:ReferenceParameters><w:ResourceURI>' + amtstack.CompleteName('AMT_PublicKeyCertificate') + '</w:ResourceURI><w:SelectorSet><w:Selector Name="InstanceID">' + xxCertificates[parseInt(Q('idx_d27clientcert').value)]['InstanceID'] + '</w:Selector></w:SelectorSet></a:ReferenceParameters>'; } else { delete sc['ClientCertificate']; } //if (parseInt(Q('idx_d27clientcert').value) >= 0) { netAuthProfile['ClientCertificate'] = '<a:Address>/wsman</a:Address><a:ReferenceParameters><w:ResourceURI>' + amtstack.CompleteName('AMT_PublicKeyCertificate') + '</w:ResourceURI><w:SelectorSet><w:Selector Name="InstanceID">' + xxCertificates[parseInt(Q('idx_d27clientcert').value)]['InstanceID'] + '</w:Selector></w:SelectorSet></a:ReferenceParameters>'; } else { delete sc['ClientCertificate']; }
//if (parseInt(Q('idx_d27servercert').value) >= 0) { netAuthProfile['ServerCertificateIssuer'] = '<a:Address>/wsman</a:Address><a:ReferenceParameters><w:ResourceURI>' + amtstack.CompleteName('AMT_PublicKeyCertificate') + '</w:ResourceURI><w:SelectorSet><w:Selector Name="InstanceID">' + xxCertificates[parseInt(Q('idx_d27servercert').value)]['InstanceID'] + '</w:Selector></w:SelectorSet></a:ReferenceParameters>'; } else { delete sc['ServerCertificateIssuer']; } //if (parseInt(Q('idx_d27servercert').value) >= 0) { netAuthProfile['ServerCertificateIssuer'] = '<a:Address>/wsman</a:Address><a:ReferenceParameters><w:ResourceURI>' + amtstack.CompleteName('AMT_PublicKeyCertificate') + '</w:ResourceURI><w:SelectorSet><w:Selector Name="InstanceID">' + xxCertificates[parseInt(Q('idx_d27servercert').value)]['InstanceID'] + '</w:Selector></w:SelectorSet></a:ReferenceParameters>'; } else { delete sc['ServerCertificateIssuer']; }
netAuthProfile['PxeTimeout'] = (typeof srvNetAuthProfile.pxetimeoutinseconds == 'number') ? srvNetAuthProfile.pxetimeoutinseconds : 120; netAuthProfile['PxeTimeout'] = (typeof srvNetAuthProfile.pxetimeoutinseconds == 'number') ? srvNetAuthProfile.pxetimeoutinseconds : 120;
// If we have a MeshCentral Satellite profile, use that
if ((dev.netAuthCredentials != null) && (dev.netAuthCredentials[srvNetAuthProfile.authenticationprotocol] != null)) {
const srvNetAuthProfile2 = dev.netAuthCredentials[srvNetAuthProfile.authenticationprotocol];
if (srvNetAuthProfile2.username && (srvNetAuthProfile2.username != '')) { netAuthProfile['Username'] = srvNetAuthProfile2.username; }
if (srvNetAuthProfile2.password && (srvNetAuthProfile2.password != '')) { netAuthProfile['Password'] = srvNetAuthProfile2.password; }
if (srvNetAuthProfile2.domain && (srvNetAuthProfile2.domain != '')) { netAuthProfile['Domain'] = srvNetAuthProfile2.domain; }
}
} }
dev.amtstack.Put('AMT_8021XProfile', netAuthProfile, function (stack, name, responses, status) { dev.amtstack.Put('AMT_8021XProfile', netAuthProfile, function (stack, name, responses, status) {
const dev = stack.dev; const dev = stack.dev;
@ -1357,13 +1450,7 @@ module.exports.CreateAmtManager = function (parent) {
if (status == 200) { dev.consoleMsg("802.1x wired profile set."); } if (status == 200) { dev.consoleMsg("802.1x wired profile set."); }
devTaskCompleted(dev); devTaskCompleted(dev);
}); });
} else {
// Nothing to do
devTaskCompleted(dev);
} }
});
}
// //
// Intel AMT WIFI // Intel AMT WIFI

View File

@ -82,6 +82,10 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use
const PROTOCOL_WEBSFTP = 203; const PROTOCOL_WEBSFTP = 203;
const PROTOCOL_WEBVNC = 204; const PROTOCOL_WEBVNC = 204;
// MeshCentral Satellite
const SATELLITE_PRESENT = 1; // This session is a MeshCentral Salellite session
const SATELLITE_802_1x = 2; // This session supports 802.1x profile checking and creation
// Events // Events
/* /*
var eventsMessageId = { var eventsMessageId = {
@ -424,6 +428,9 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use
for (var i in event.deviceShares) { if (event.deviceShares[i].userid != user._id) { delete event.deviceShares[i].url; } } for (var i in event.deviceShares) { if (event.deviceShares[i].userid != user._id) { delete event.deviceShares[i].url; } }
} }
// This is a MeshCentral Satellite message
if (event.action == 'satellite') { if ((obj.satelliteFlags & event.satelliteFlags) != 0) { try { ws.send(JSON.stringify(event)); } catch (ex) { } return; } }
// Because of the device group "Show Self Events Only", we need to do more checks here. // Because of the device group "Show Self Events Only", we need to do more checks here.
if (id.startsWith('mesh/')) { if (id.startsWith('mesh/')) {
// Check if we have rights to get this message. If we have limited events on this mesh, don't send the event to the user. // Check if we have rights to get this message. If we have limited events on this mesh, don't send the event to the user.
@ -4806,7 +4813,7 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use
const splitxuser = command.xuserid.split('/'); const splitxuser = command.xuserid.split('/');
var xusername = splitxuser[2]; var xusername = splitxuser[2];
if (command.guestname != null) { xusername += '/' + command.guestname; } if (command.guestname != null) { xusername += '/' + command.guestname; }
var event = { etype: 'user', userid: user._id, username: user.name, nodeid: command.nodeid, xuserid: command.xuserid, action: 'endsession', msgid: 134, msgArgs: [xusername], msg: 'Forcibly disconnected desktop session of user ' + xusername, domain: domain.id }; const event = { etype: 'user', userid: user._id, username: user.name, nodeid: command.nodeid, xuserid: command.xuserid, action: 'endsession', msgid: 134, msgArgs: [xusername], msg: 'Forcibly disconnected desktop session of user ' + xusername, domain: domain.id };
if (command.guestname != null) { event.guestname = command.guestname; } if (command.guestname != null) { event.guestname = command.guestname; }
if (db.changeStream) { event.noact = 1; } // If DB change stream is active, don't use this event to change the user. Another event will come. if (db.changeStream) { event.noact = 1; } // If DB change stream is active, don't use this event to change the user. Another event will come.
parent.parent.DispatchEvent(targets, obj, event); parent.parent.DispatchEvent(targets, obj, event);
@ -4814,6 +4821,16 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use
break; break;
} }
case 'satellite': {
// Command indicates this is a MeshCentral Satellite session and what featues it supports
if ((command.setFlags != null) && (typeof command.setFlags == 'number')) { obj.satelliteFlags = command.setFlags; }
if ((command.reqid != null) && (command.response != null) && (typeof command.satelliteFlags == 'number')) {
const event = { action: 'satelliteResponse', reqid: command.reqid, response: command.response, satelliteFlags: command.satelliteFlags, nolog: 1 }
if (typeof command.nodeid == 'string') { event.nodeid = command.nodeid; }
parent.parent.DispatchEvent([ '*' ], obj, event);
}
break;
}
default: { default: {
// Unknown user action // Unknown user action
console.log('Unknown action from user ' + user.name + ': ' + command.action + '.'); console.log('Unknown action from user ' + user.name + ': ' + command.action + '.');