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 + '.');