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

Ylian Saint-Hilaire committed Apr 2, 2022 at 15:27 UTC 8e5c71cea25ac6358f346d6d439d868613c2c2f7
2 files changed +143 -39
amtmanager.js
+125 -38
@@ -22,6 +22,9 @@ module.exports.CreateAmtManager = function (parent) {
22 obj.rootCertBase64 = obj.parent.certificates.root.cert.split('-----BEGIN CERTIFICATE-----').join('').split('-----END CERTIFICATE-----').join('').split('\r').join('').split('\n').join('')
23 obj.rootCertCN = obj.parent.certificateOperations.forge.pki.certificateFromPem(obj.parent.certificates.root.cert).subject.getField('CN').value;
24
25 + // 802.1x authentication protocols
26 + 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'];
27 +
28 // WSMAN stack
29 const CreateWsmanComm = require('./amt/amt-wsman-comm');
30 const WsmanStackCreateService = require('./amt/amt-wsman');
@@ -102,7 +105,14 @@ module.exports.CreateAmtManager = function (parent) {
105 } else if ([5, 7, 32768, 32769].indexOf(wifiProfile.authentication) >= 0) {
106 // 802.1x authentication
107 if ((wifiProfile['802.1x'] == null) && (typeof wifiProfile['802.1x'] != 'object')) continue;
105 - 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'];
108 +
109 + if (wifiProfile['802.1x'].satellitecredentials != null) {
110 + if (typeof wifiProfile['802.1x'].satellitecredentials != 'string') continue;
111 + const userSplit = wifiProfile['802.1x'].satellitecredentials.split('/');
112 + if (userSplit.length > 3) continue;
113 + if (userSplit.length == 2) { wifiProfile['802.1x'].satellitecredentials = 'user/' + domain.id + '/' + userSplit[1]; }
114 + else if (userSplit.length == 1) { wifiProfile['802.1x'].satellitecredentials = 'user/' + domain.id + '/' + userSplit[0]; }
115 + }
116
117 if (typeof wifiProfile['802.1x'].servercertificatename != 'string') {
118 delete wifiProfile['802.1x'].servercertificatenamecomparison;
@@ -129,9 +139,16 @@ module.exports.CreateAmtManager = function (parent) {
139
140 // Check 802.1x wired profile if present
141 if ((domain.amtmanager['802.1x'] != null) && (typeof domain.amtmanager['802.1x'] == 'object')) {
132 - 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'];
142 + if (domain.amtmanager['802.1x'].satellitecredentials != null) {
143 + if (typeof domain.amtmanager['802.1x'].satellitecredentials != 'string') { delete domain.amtmanager['802.1x']; } else {
144 + const userSplit = domain.amtmanager['802.1x'].satellitecredentials.split('/');
145 + if (userSplit.length > 3) { delete domain.amtmanager['802.1x']; }
146 + else if (userSplit.length == 2) { domain.amtmanager['802.1x'].satellitecredentials = 'user/' + domain.id + '/' + userSplit[1]; }
147 + else if (userSplit.length == 1) { domain.amtmanager['802.1x'].satellitecredentials = 'user/' + domain.id + '/' + userSplit[0]; }
148 + }
149 + }
150
134 - if (typeof domain.amtmanager['802.1x'].servercertificatename != 'string') {
151 + if ((domain.amtmanager['802.1x'].satellitecredentials != null) && (typeof domain.amtmanager['802.1x'].servercertificatename != 'string')) {
152 delete domain.amtmanager['802.1x'].servercertificatenamecomparison;
153 const serverCertCompareStrings = ['', '', 'fullname', 'domainsuffix'];
154 if (typeof domain.amtmanager['802.1x'].servercertificatenamecomparison == 'string') {
@@ -140,7 +157,7 @@ module.exports.CreateAmtManager = function (parent) {
157 }
158 }
159
143 - if (typeof domain.amtmanager['802.1x'].authenticationprotocol == 'string') {
160 + if ((domain.amtmanager['802.1x'].satellitecredentials != null) && (typeof domain.amtmanager['802.1x'].authenticationprotocol == 'string')) {
161 domain.amtmanager['802.1x'].authenticationprotocol = netAuthStrings.indexOf(domain.amtmanager['802.1x'].authenticationprotocol.toLowerCase());
162 if (domain.amtmanager['802.1x'].authenticationprotocol == -1) { delete domain.amtmanager['802.1x']; }
163 }
@@ -395,6 +412,35 @@ module.exports.CreateAmtManager = function (parent) {
412 // TODO
413 break;
414 }
415 + case 'satelliteResponse': {
416 + if ((typeof event.nodeid != 'string') || (typeof event.reqid != 'string') || (event.satelliteFlags != 2)) return;
417 + var devices = obj.amtDevices[event.nodeid], devFound = null;
418 + if (devices != null) { for (var i in devices) { if (devices[i].netAuthSatReqId == event.reqid) { devFound = devices[i]; } } }
419 + if (devFound == null) return; // Unable to find a device for this 802.1x profile
420 + delete devFound.netAuthSatReqId;
421 + if (devFound.netAuthSatReqTimer != null) { clearTimeout(devFound.netAuthSatReqTimer); delete devFound.netAuthSatReqTimer; }
422 + if ((event.response == null) || (typeof event.response != 'object') || (typeof event.response.authProtocol != 'number')) {
423 + // Unable to create a 802.1x profile
424 + if (isAmtDeviceValid(devFound) == false) return; // Device no longer exists, ignore this request.
425 + delete devFound.netAuthSatReqDev;
426 + delete devFound.netAuthSatReqSrv;
427 + devFound.consoleMsg("MeshCentral Satellite could not create a 802.1x profile for this device.");
428 + devTaskCompleted(devFound);
429 + } else {
430 + // We got a new 802.1x profile
431 + if (devFound.netAuthCredentials == null) { devFound.netAuthCredentials = {}; }
432 + devFound.netAuthCredentials[event.response.authProtocol] = event.response;
433 + devFound.consoleMsg("Setting MeshCentral Satellite 802.1x profile...");
434 +
435 + // Set the 802.1x wired profile in the device
436 + var devNetAuthProfile = devFound.netAuthSatReqDev;
437 + var srvNetAuthProfile = devFound.netAuthSatReqSrv;
438 + delete devFound.netAuthSatReqDev;
439 + delete devFound.netAuthSatReqSrv;
440 + attempt8021xSyncEx(devFound, devNetAuthProfile, srvNetAuthProfile);
441 + }
442 + break;
443 + }
444 }
445 }
446
@@ -1314,49 +1360,46 @@ module.exports.CreateAmtManager = function (parent) {
1360 } else if ((typeof srvNetAuthProfile == 'object') && (devNetAuthProfile != null)) {
1361 // Check if the existing 802.1x profile look good
1362 if (devNetAuthProfile.AuthenticationProtocol != srvNetAuthProfile.authenticationprotocol) { match = false; }
1317 - if (devNetAuthProfile.RoamingIdentity != srvNetAuthProfile.roamingidentity) { match = false; }
1363 if (devNetAuthProfile.ServerCertificateName != srvNetAuthProfile.servercertificatename) { match = false; }
1364 if (devNetAuthProfile.ServerCertificateNameComparison != srvNetAuthProfile.servercertificatenamecomparison) { match = false; }
1320 - if (devNetAuthProfile.Username != srvNetAuthProfile.username) { match = false; }
1321 - if (devNetAuthProfile.Domain != srvNetAuthProfile.domain) { match = false; }
1365 if (devNetAuthProfile.ActiveInS0 != srvNetAuthProfile.availableins0) { match = false; }
1366 + if (typeof srvNetAuthProfile.satellitecredentials != 'string') {
1367 + // Credentials for this profile are in the config file
1368 + if (devNetAuthProfile.RoamingIdentity != srvNetAuthProfile.roamingidentity) { match = false; }
1369 + if (devNetAuthProfile.Username != srvNetAuthProfile.username) { match = false; }
1370 + if (devNetAuthProfile.Domain != srvNetAuthProfile.domain) { match = false; }
1371 + }
1372 }
1373
1374 // If there is a mismatch, set the new 802.1x profile
1375 if (match == false) {
1327 - var netAuthProfile = Clone(devNetAuthProfile);
1328 - netAuthProfile['Enabled'] = ((srvNetAuthProfile != null) && (typeof srvNetAuthProfile == 'object'));
1329 - if (netAuthProfile['Enabled']) {
1330 - netAuthProfile['ActiveInS0'] = (srvNetAuthProfile.availableInS0 !== false);
1331 - netAuthProfile['AuthenticationProtocol'] = srvNetAuthProfile.authenticationprotocol;
1332 - if (srvNetAuthProfile.roamingidentity && (srvNetAuthProfile.roamingidentity != '')) { netAuthProfile['RoamingIdentity'] = srvNetAuthProfile.roamingidentity; } else { delete netAuthProfile['RoamingIdentity']; }
1333 - if (srvNetAuthProfile.servercertificatename && (srvNetAuthProfile.servercertificatename != '')) {
1334 - netAuthProfile['ServerCertificateName'] = srvNetAuthProfile.servercertificatename;
1335 - netAuthProfile['ServerCertificateNameComparison'] = srvNetAuthProfile.servercertificatenamecomparison;
1336 - } else {
1337 - delete netAuthProfile['ServerCertificateName'];
1338 - delete netAuthProfile['ServerCertificateNameComparison'];
1339 - }
1340 - if (srvNetAuthProfile.username && (srvNetAuthProfile.username != '')) { netAuthProfile['Username'] = srvNetAuthProfile.username; } else { delete netAuthProfile['Username']; }
1341 - if (srvNetAuthProfile.password && (srvNetAuthProfile.password != '')) { netAuthProfile['Password'] = srvNetAuthProfile.password; } else { delete netAuthProfile['Password']; }
1342 - if (srvNetAuthProfile.domain && (srvNetAuthProfile.domain != '')) { netAuthProfile['Domain'] = srvNetAuthProfile.domain; } else { delete netAuthProfile['Domain']; }
1343 - if (srvNetAuthProfile.authenticationprotocol > 3) {
1344 - netAuthProfile['ProtectedAccessCredential'] = srvNetAuthProfile.protectedaccesscredentialhex;
1345 - netAuthProfile['PACPassword'] = srvNetAuthProfile.pacpassword;
1346 - } else {
1347 - delete netAuthProfile['ProtectedAccessCredential'];
1348 - delete netAuthProfile['PACPassword'];
1376 + if ((typeof srvNetAuthProfile.satellitecredentials == 'string') && ((dev.netAuthCredentials == null) || (dev.netAuthCredentials[srvNetAuthProfile.authenticationprotocol] == null))) {
1377 + // Credentials for this profile are provided using MeshCentral Satellite
1378 + // Send a message to Satellite requesting a 802.1x profile for this device
1379 + dev.consoleMsg("Requesting 802.1x credentials for " + netAuthStrings[srvNetAuthProfile.authenticationprotocol] + " from MeshCentral Satellite...");
1380 + dev.netAuthSatReqId = 'wired-' + Buffer.from(parent.crypto.randomBytes(16), 'binary').toString('base64'); // Generate a crypto-secure request id.
1381 + dev.netAuthSatReqDev = devNetAuthProfile;
1382 + dev.netAuthSatReqSrv = srvNetAuthProfile;
1383 + 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 });
1384 +
1385 + // Set a response timeout
1386 + const netAuthTimeoutFunc = function netAuthTimeout() {
1387 + if (isAmtDeviceValid(netAuthTimeout.dev) == false) return; // Device no longer exists, ignore this request.
1388 + if (dev.netAuthSatReqId != null) {
1389 + delete netAuthTimeout.dev.netAuthSatReqId;
1390 + delete netAuthTimeout.dev.netAuthSatReqDev;
1391 + delete netAuthTimeout.dev.netAuthSatReqSrv;
1392 + netAuthTimeout.dev.consoleMsg("MeshCentral Satellite did not respond in time, 802.1x profile will not be set.");
1393 + devTaskCompleted(netAuthTimeout.dev);
1394 + }
1395 }
1350 - //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']; }
1351 - //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']; }
1352 - netAuthProfile['PxeTimeout'] = (typeof srvNetAuthProfile.pxetimeoutinseconds == 'number') ? srvNetAuthProfile.pxetimeoutinseconds : 120;
1396 + netAuthTimeoutFunc.dev = dev;
1397 + dev.netAuthSatReqTimer = setTimeout(netAuthTimeoutFunc, 10000);
1398 + return;
1399 + } else {
1400 + // Set the 802.1x wired profile in the device
1401 + attempt8021xSyncEx(dev, devNetAuthProfile, srvNetAuthProfile);
1402 }
1354 - dev.amtstack.Put('AMT_8021XProfile', netAuthProfile, function (stack, name, responses, status) {
1355 - const dev = stack.dev;
1356 - if (isAmtDeviceValid(dev) == false) return; // Device no longer exists, ignore this request.
1357 - if (status == 200) { dev.consoleMsg("802.1x wired profile set."); }
1358 - devTaskCompleted(dev);
1359 - });
1403 } else {
1404 // Nothing to do
1405 devTaskCompleted(dev);
@@ -1364,6 +1407,50 @@ module.exports.CreateAmtManager = function (parent) {
1407 });
1408 }
1409
1410 + // Set the 802.1x wired profile
1411 + function attempt8021xSyncEx(dev, devNetAuthProfile, srvNetAuthProfile) {
1412 + var netAuthProfile = Clone(devNetAuthProfile);
1413 + netAuthProfile['Enabled'] = ((srvNetAuthProfile != null) && (typeof srvNetAuthProfile == 'object'));
1414 + if (netAuthProfile['Enabled']) {
1415 + netAuthProfile['ActiveInS0'] = (srvNetAuthProfile.availableInS0 !== false);
1416 + netAuthProfile['AuthenticationProtocol'] = srvNetAuthProfile.authenticationprotocol;
1417 + if (srvNetAuthProfile.roamingidentity && (srvNetAuthProfile.roamingidentity != '')) { netAuthProfile['RoamingIdentity'] = srvNetAuthProfile.roamingidentity; } else { delete netAuthProfile['RoamingIdentity']; }
1418 + if (srvNetAuthProfile.servercertificatename && (srvNetAuthProfile.servercertificatename != '')) {
1419 + netAuthProfile['ServerCertificateName'] = srvNetAuthProfile.servercertificatename;
1420 + netAuthProfile['ServerCertificateNameComparison'] = srvNetAuthProfile.servercertificatenamecomparison;
1421 + } else {
1422 + delete netAuthProfile['ServerCertificateName'];
1423 + delete netAuthProfile['ServerCertificateNameComparison'];
1424 + }
1425 + if (srvNetAuthProfile.username && (srvNetAuthProfile.username != '')) { netAuthProfile['Username'] = srvNetAuthProfile.username; } else { delete netAuthProfile['Username']; }
1426 + if (srvNetAuthProfile.password && (srvNetAuthProfile.password != '')) { netAuthProfile['Password'] = srvNetAuthProfile.password; } else { delete netAuthProfile['Password']; }
1427 + if (srvNetAuthProfile.domain && (srvNetAuthProfile.domain != '')) { netAuthProfile['Domain'] = srvNetAuthProfile.domain; } else { delete netAuthProfile['Domain']; }
1428 + if (srvNetAuthProfile.authenticationprotocol > 3) {
1429 + netAuthProfile['ProtectedAccessCredential'] = srvNetAuthProfile.protectedaccesscredentialhex;
1430 + netAuthProfile['PACPassword'] = srvNetAuthProfile.pacpassword;
1431 + } else {
1432 + delete netAuthProfile['ProtectedAccessCredential'];
1433 + delete netAuthProfile['PACPassword'];
1434 + }
1435 + //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']; }
1436 + //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']; }
1437 + netAuthProfile['PxeTimeout'] = (typeof srvNetAuthProfile.pxetimeoutinseconds == 'number') ? srvNetAuthProfile.pxetimeoutinseconds : 120;
1438 +
1439 + // If we have a MeshCentral Satellite profile, use that
1440 + if ((dev.netAuthCredentials != null) && (dev.netAuthCredentials[srvNetAuthProfile.authenticationprotocol] != null)) {
1441 + const srvNetAuthProfile2 = dev.netAuthCredentials[srvNetAuthProfile.authenticationprotocol];
1442 + if (srvNetAuthProfile2.username && (srvNetAuthProfile2.username != '')) { netAuthProfile['Username'] = srvNetAuthProfile2.username; }
1443 + if (srvNetAuthProfile2.password && (srvNetAuthProfile2.password != '')) { netAuthProfile['Password'] = srvNetAuthProfile2.password; }
1444 + if (srvNetAuthProfile2.domain && (srvNetAuthProfile2.domain != '')) { netAuthProfile['Domain'] = srvNetAuthProfile2.domain; }
1445 + }
1446 + }
1447 + dev.amtstack.Put('AMT_8021XProfile', netAuthProfile, function (stack, name, responses, status) {
1448 + const dev = stack.dev;
1449 + if (isAmtDeviceValid(dev) == false) return; // Device no longer exists, ignore this request.
1450 + if (status == 200) { dev.consoleMsg("802.1x wired profile set."); }
1451 + devTaskCompleted(dev);
1452 + });
1453 + }
1454
1455 //
1456 // Intel AMT WIFI
meshuser.js
+18 -1
@@ -82,6 +82,10 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use
82 const PROTOCOL_WEBSFTP = 203;
83 const PROTOCOL_WEBVNC = 204;
84
85 + // MeshCentral Satellite
86 + const SATELLITE_PRESENT = 1; // This session is a MeshCentral Salellite session
87 + const SATELLITE_802_1x = 2; // This session supports 802.1x profile checking and creation
88 +
89 // Events
90 /*
91 var eventsMessageId = {
@@ -424,6 +428,9 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use
428 for (var i in event.deviceShares) { if (event.deviceShares[i].userid != user._id) { delete event.deviceShares[i].url; } }
429 }
430
431 + // This is a MeshCentral Satellite message
432 + if (event.action == 'satellite') { if ((obj.satelliteFlags & event.satelliteFlags) != 0) { try { ws.send(JSON.stringify(event)); } catch (ex) { } return; } }
433 +
434 // Because of the device group "Show Self Events Only", we need to do more checks here.
435 if (id.startsWith('mesh/')) {
436 // 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
4813 const splitxuser = command.xuserid.split('/');
4814 var xusername = splitxuser[2];
4815 if (command.guestname != null) { xusername += '/' + command.guestname; }
4809 - 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 };
4816 + 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 };
4817 if (command.guestname != null) { event.guestname = command.guestname; }
4818 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.
4819 parent.parent.DispatchEvent(targets, obj, event);
@@ -4814,6 +4821,16 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use
4821
4822 break;
4823 }
4824 + case 'satellite': {
4825 + // Command indicates this is a MeshCentral Satellite session and what featues it supports
4826 + if ((command.setFlags != null) && (typeof command.setFlags == 'number')) { obj.satelliteFlags = command.setFlags; }
4827 + if ((command.reqid != null) && (command.response != null) && (typeof command.satelliteFlags == 'number')) {
4828 + const event = { action: 'satelliteResponse', reqid: command.reqid, response: command.response, satelliteFlags: command.satelliteFlags, nolog: 1 }
4829 + if (typeof command.nodeid == 'string') { event.nodeid = command.nodeid; }
4830 + parent.parent.DispatchEvent([ '*' ], obj, event);
4831 + }
4832 + break;
4833 + }
4834 default: {
4835 // Unknown user action
4836 console.log('Unknown action from user ' + user.name + ': ' + command.action + '.');