Started work on Intel AMT ACM activation.

This commit is contained in:
Ylian Saint-Hilaire 2019-06-12 19:40:27 -07:00
parent f98d937923
commit 2117f253b3
13 changed files with 140 additions and 1979 deletions

Binary file not shown.

Binary file not shown.

File diff suppressed because one or more lines are too long

1944
agents/meshcmd.min.js vendored

File diff suppressed because one or more lines are too long

View File

@ -26,6 +26,23 @@ module.exports.CertificateOperations = function (parent) {
obj.dirExists = function (filePath) { try { return obj.fs.statSync(filePath).isDirectory(); } catch (err) { return false; } };
obj.getFilesizeInBytes = function (filename) { try { return obj.fs.statSync(filename).size; } catch (err) { return -1; } };
// Return the certificate of the remote HTTPS server
obj.loadPfxCertificate = function (filename, password) {
var r = { certs: [], keys: [] };
var pfxbuf = obj.fs.readFileSync(filename);
var pfxb64 = Buffer.from(pfxbuf).toString('base64');
var pfxder = obj.forge.util.decode64(pfxb64);
var asn = obj.forge.asn1.fromDer(pfxder);
var pfx = obj.forge.pkcs12.pkcs12FromAsn1(asn, true, password);
// Get the certs from certbags
var bags = pfx.getBags({ bagType: obj.forge.pki.oids.certBag });
for (var i = 0; i < bags[obj.forge.pki.oids.certBag].length; i++) { r.certs.push(bags[obj.forge.pki.oids.certBag][i].cert); }
// Get shrouded key from key bags
bags = pfx.getBags({ bagType: obj.forge.pki.oids.pkcs8ShroudedKeyBag });
for (var i = 0; i < bags[obj.forge.pki.oids.pkcs8ShroudedKeyBag].length; i++) { r.keys.push(bags[obj.forge.pki.oids.pkcs8ShroudedKeyBag][i].key); }
return r;
}
// Return the certificate of the remote HTTPS server
obj.loadCertificate = function (url, tag, func) {
const u = require('url').parse(url);

View File

@ -813,24 +813,28 @@ module.exports.CreateMeshAgent = function (parent, db, ws, req, args, domain) {
// Take a basic Intel AMT policy and add all server information to it, making it ready to send to this agent.
function completeIntelAmtPolicy(amtPolicy) {
var r = amtPolicy;
if (amtPolicy == null) return null;
if (amtPolicy.type == 2) {
// Add server root certificate
// CCM - Add server root certificate
if (parent.parent.certificates.rootex == null) { parent.parent.certificates.rootex = parent.parent.certificates.root.cert.split('-----BEGIN CERTIFICATE-----').join('').split('-----END CERTIFICATE-----').join('').split('\r').join('').split('\n').join(''); }
amtPolicy.rootcert = parent.parent.certificates.rootex;
r.rootcert = parent.parent.certificates.rootex;
} else if ((amtPolicy.type == 3) && (domain.amtacmactivation.dnsmatch)) {
// ACM - In this mode, don't send much to Intel AMT. Just indicate ACM policy and let the agent try activation when possible.
r = { type: 3, dnsmatch: domain.amtacmactivation.dnsmatch };
}
if ((amtPolicy.cirasetup == 2) && (parent.parent.mpsserver != null) && (parent.parent.certificates.AmtMpsName != null) && (args.lanonly != true) && (args.mpsport != 0)) {
if (((amtPolicy.cirasetup == 2) || (amtPolicy.cirasetup == 3)) && (parent.parent.mpsserver != null) && (parent.parent.certificates.AmtMpsName != null) && (args.lanonly != true) && (args.mpsport != 0)) {
// Add server CIRA settings
amtPolicy.ciraserver = {
r.ciraserver = {
name: parent.parent.certificates.AmtMpsName,
port: (typeof args.mpsaliasport == 'number' ? args.mpsaliasport : args.mpsport),
user: obj.meshid.replace(/\@/g, 'X').replace(/\$/g, 'X').substring(0, 16),
pass: args.mpspass ? args.mpspass : 'A@xew9rt', // If the MPS password is not set, just use anything. TODO: Use the password as an agent identifier?
home: ['sdlwerulis3wpj95dfj'] // Use a random FQDN to not have any home network.
};
if (Array.isArray(args.ciralocalfqdn)) { amtPolicy.ciraserver.home = args.ciralocalfqdn; }
if (Array.isArray(args.ciralocalfqdn)) { r.ciraserver.home = args.ciralocalfqdn; }
}
return amtPolicy;
return r;
}
// Send Intel AMT policy

View File

@ -714,6 +714,30 @@ function CreateMeshCentralServer(config, args) {
// Load any domain web certificates
for (i in obj.config.domains) {
// Load any Intel AMT ACM activation certificates
if (obj.config.domains[i].amtacmactivation && obj.config.domains[i].amtacmactivation.certs) {
var badAcmConfigs = [], dnsmatch = [], amtAcmCertCount = 0;
for (var j in obj.config.domains[i].amtacmactivation.certs) {
var acmconfig = obj.config.domains[i].amtacmactivation.certs[j];
if (acmconfig.dnsmatch == null) { acmconfig.dnsmatch = [ j ]; }
if (typeof acmconfig.dnsmatch == 'string') { acmconfig.dnsmatch = [ acmconfig.dnsmatch ]; }
if (typeof acmconfig.dnsmatch.length == 0) { badAcmConfigs.push(j); continue; }
if (typeof acmconfig.cert != 'string') { badAcmConfigs.push(j); continue; }
var r = null;
try { r = obj.certificateOperations.loadPfxCertificate(obj.path.join(obj.datapath, acmconfig.cert), acmconfig.certpass); } catch (ex) { console.log(ex); }
if ((r == null) || (r.certs == null) || (r.keys == null) || (r.certs.length < 2) || (r.keys.length == 0)) { badAcmConfigs.push(j); continue; }
delete acmconfig.cert;
delete acmconfig.certpass;
acmconfig.certs = r.certs;
acmconfig.keys = r.keys;
for (var k in acmconfig.dnsmatch) { if (dnsmatch.indexOf(acmconfig.dnsmatch[k]) == -1) { dnsmatch.push(acmconfig.dnsmatch[k]); } }
amtAcmCertCount++;
}
// Remove all bad configurations
for (var j in badAcmConfigs) { console.log('WARNING: Incorrect Intel AMT ACM configuration "' + i + (i == '' ? '' : '/') + badAcmConfigs[j] + '".'); delete obj.config.domains[i].amtacmactivationcerts[j]; }
if (amtAcmCertCount == 0) { delete obj.config.domains[i].amtacmactivation; } else { obj.config.domains[i].amtacmactivation.dnsmatch = dnsmatch; }
}
if (obj.config.domains[i].certurl != null) {
// Fix the URL and add 'https://' if needed
if (obj.config.domains[i].certurl.indexOf('://') < 0) { obj.config.domains[i].certurl = 'https://' + obj.config.domains[i].certurl; }

View File

@ -1579,12 +1579,16 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use
// Change a mesh Intel AMT policy
if (common.validateString(command.meshid, 1, 1024) == false) break; // Check the meshid
if (common.validateObject(command.amtpolicy) == false) break; // Check the amtpolicy
if (common.validateInt(command.amtpolicy.type, 0, 2) == false) break; // Check the amtpolicy.type
if (common.validateInt(command.amtpolicy.type, 0, 3) == false) break; // Check the amtpolicy.type
if (command.amtpolicy.type === 2) {
if (common.validateString(command.amtpolicy.password, 0, 32) == false) break; // Check the amtpolicy.password
if (common.validateInt(command.amtpolicy.badpass, 0, 1) == false) break; // Check the amtpolicy.badpass
if (common.validateInt(command.amtpolicy.cirasetup, 0, 2) == false) break; // Check the amtpolicy.cirasetup
} else if (command.amtpolicy.type === 3) {
if (common.validateString(command.amtpolicy.password, 0, 32) == false) break; // Check the amtpolicy.password
if (common.validateInt(command.amtpolicy.cirasetup, 0, 2) == false) break; // Check the amtpolicy.cirasetup
}
console.log('meshamtpolicy', command);
mesh = parent.meshes[command.meshid];
change = '';
if (mesh) {
@ -1598,6 +1602,7 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use
change = 'Intel AMT policy change';
var amtpolicy = { type: command.amtpolicy.type };
if (command.amtpolicy.type === 2) { amtpolicy = { type: command.amtpolicy.type, password: command.amtpolicy.password, badpass: command.amtpolicy.badpass, cirasetup: command.amtpolicy.cirasetup }; }
else if (command.amtpolicy.type === 3) { amtpolicy = { type: command.amtpolicy.type, password: command.amtpolicy.password, cirasetup: command.amtpolicy.cirasetup }; }
mesh.amt = amtpolicy;
db.Set(common.escapeLinksFieldName(mesh));
var event = { etype: 'mesh', username: user.name, meshid: mesh._id, amt: amtpolicy, action: 'meshchange', links: mesh.links, msg: change, domain: domain.id };

View File

@ -8758,7 +8758,10 @@ var QRCode;!function(){function a(a){this.mode=c.MODE_8BIT_BYTE,this.data=a,this
x += "<div id=emailInviteDiv style=display:none>Invite someone to install the mesh agent. An email with be sent with the link to the mesh agent installation for the \"" + EscapeHtml(mesh.name) + "\" device group.<br /><br />";
x += addHtmlValue('Name (optional)', '<input id=agentInviteName value="" style=width:230px maxlength=64 />');
x += addHtmlValue('Email', '<input id=agentInviteEmail style=width:230px placeholder="example@email.com" onkeyup=validateAgentInvite()></input>');
x += addHtmlValue('Operating System', '<select id=agentInviteNameOs style=width:236px><option value=0>Any supported</option><option value=1>Windows only</option><option value=3>Apple MacOS only</option><option value=2>Linux only</option></select>');
x += addHtmlValue('Operating System', '<select id=agentInviteNameOs onchange=d2ChangedInviteType() style=width:236px><option value=4>Send installation link</option><option value=0 selected>Any supported</option><option value=1>Windows only</option><option value=3>Apple MacOS only</option><option value=2>Linux only</option></select>');
x += '<div id=d2agentexpirediv>';
x += addHtmlValue('Link Expiration', '<select id=agentInviteExpire style=width:236px><option value=1>1 hour</option><option value=8>8 hours</option><option value=24>1 day</option><option value=168>1 week</option><option value=5040>1 month</option><option value=0>Unlimited</option></select>');
x += '</div>';
x += addHtmlValue('Installation Type', '<select id=agentInviteType style=width:236px><option value=0>Background and interactive</option><option value=2>Background only</option><option value=1>Interactive only</option></select>');
x += addHtmlValue('Message<br />(optional)', '<textarea id=agentInviteMessage value="" style=width:230px;height:100px;resize:none maxlength=1024 /></textarea>');
x += '</div>';
@ -8767,6 +8770,7 @@ var QRCode;!function(){function a(a){this.mode=c.MODE_8BIT_BYTE,this.data=a,this
x += addHtmlValue('Link Expiration', '<select id=d2inviteExpire style=width:236px onchange=d2RequestInvitationLink()><option value=1>1 hour</option><option value=8>8 hours</option><option value=24>1 day</option><option value=168>1 week</option><option value=5040>1 month</option><option value=0>Unlimited</option></select>');
x += '<div id=agentInvitationLinkDiv style="text-align:center;font-size:large;margin:16px;display:none"><a id=agentInvitationLink target="_blank" href="" style=cursor:pointer></a> <img src=images/link4.png height=10 width=10 title="Copy link to clipboard" style=cursor:pointer onclick=d2CopyInviteToClip()></div></div>';
setDialogMode(2, "Invite", 3, performAgentInvite, x, meshid);
d2ChangedInviteType();
validateAgentInvite();
d2RequestInvitationLink();
}
@ -8777,6 +8781,7 @@ var QRCode;!function(){function a(a){this.mode=c.MODE_8BIT_BYTE,this.data=a,this
function d2ChangedInviteType() {
QV('urlInviteDiv', Q('d2InviteType').value == 0);
QV('d2agentexpirediv', Q('agentInviteNameOs').value == 4);
if (features & 64) { QV('emailInviteDiv', Q('d2InviteType').value == 1); }
validateAgentInvite();
}
@ -8795,7 +8800,7 @@ var QRCode;!function(){function a(a){this.mode=c.MODE_8BIT_BYTE,this.data=a,this
function performAgentInvite(button, meshid) {
if ((features & 64) && (Q('d2InviteType').value == 1)) {
meshserver.send({ action: 'inviteAgent', meshid: meshid, email: Q('agentInviteEmail').value, name: Q('agentInviteName').value, os: Q('agentInviteNameOs').value, flags: Q('agentInviteType').value, msg: Q('agentInviteMessage').value });
meshserver.send({ action: 'inviteAgent', meshid: meshid, email: Q('agentInviteEmail').value, name: Q('agentInviteName').value, os: Q('agentInviteNameOs').value, flags: Q('agentInviteType').value, msg: Q('agentInviteMessage').value, expire: parseInt(Q('agentInviteExpire').value) });
}
}
@ -12299,6 +12304,9 @@ var QRCode;!function(){function a(a){this.mode=c.MODE_8BIT_BYTE,this.data=a,this
else if (currentMesh.amt.type == 2) {
intelAmtPolicy = 'Simple Client Control Mode (CCM)';
if (currentMesh.amt.cirasetup == 2) { intelAmtPolicy += ' + CIRA'; }
} else if (currentMesh.amt.type == 3) {
intelAmtPolicy = 'Simple Admin Control Mode (ACM)';
if (currentMesh.amt.cirasetup == 2) { intelAmtPolicy += ' + CIRA'; }
}
}
x += addHtmlValue('Intel&reg; AMT', addLinkConditional(intelAmtPolicy, 'p20editMeshAmt()', meshrights & 1));
@ -12368,17 +12376,18 @@ var QRCode;!function(){function a(a){this.mode=c.MODE_8BIT_BYTE,this.data=a,this
function p20editMeshAmt() {
if (xxdialogMode) return;
var x = '';
x += addHtmlValue('Type', '<select id=dp20amtpolicy style=width:230px onchange=p20editMeshAmtChange()><option value=0>No Policy</option><option value=1>Deactivate Client Control Mode (CCM)</option><option value=2>Simple Client Control Mode (CCM)</option></select>');
var x = '', acmoption = '';
if ((features & 0x100000) != 0) { acmoption = '<option value=3>Simple Admin Control Mode (ACM)</option>'; }
x += addHtmlValue('Type', '<select id=dp20amtpolicy style=width:230px onchange=p20editMeshAmtChange()><option value=0>No Policy</option><option value=1>Deactivate Client Control Mode (CCM)</option><option value=2>Simple Client Control Mode (CCM)</option>' + acmoption + '</select>');
x += '<div id=dp20amtpolicydiv></div>';
setDialogMode(2, "Intel&reg; AMT Policy", 3, p20editMeshAmtEx, x);
if (currentMesh.amt) { Q('dp20amtpolicy').value = currentMesh.amt.type; }
p20editMeshAmtChange();
// Set the current Intel AMT policy
if (currentMesh.amt && currentMesh.amt.type == 2) {
if (currentMesh.amt && (currentMesh.amt.type == 2) || (currentMesh.amt.type == 3)) {
Q('dp20amtpolicypass').value = currentMesh.amt.password;
Q('dp20amtbadpass').value = currentMesh.amt.badpass;
if (currentMesh.amt.type == 2) { Q('dp20amtbadpass').value = currentMesh.amt.badpass; }
if ((features & 0x400) == 0) { Q('dp20amtcira').value = currentMesh.amt.cirasetup; }
}
@ -12387,13 +12396,23 @@ var QRCode;!function(){function a(a){this.mode=c.MODE_8BIT_BYTE,this.data=a,this
function p20editMeshAmtChange() {
var ptype = Q('dp20amtpolicy').value, x = '';
if (ptype == 2) {
if (ptype >= 2) {
x = addHtmlValue('Password*', '<input id=dp20amtpolicypass style=width:230px maxlength=32 onchange=dp20amtValidatePolicy() onkeyup=dp20amtValidatePolicy() />')
x += addHtmlValue('Password mismatch', "<select id=dp20amtbadpass style=width:230px><option value=0>Do nothing</option><option value=1>Reactivate Intel&reg; AMT</option></select>");
if ((features & 0x400) == 0) { x += addHtmlValue('<span title="Client Initiated Remote Access">CIRA</span>', "<select id=dp20amtcira style=width:230px><option value=0>Don't configure</option><option value=1>Don't connect to server</option><option value=2>Connect to server</option></select>"); }
if (ptype == 2) { x += addHtmlValue('Password mismatch', "<select id=dp20amtbadpass style=width:230px><option value=0>Do nothing</option><option value=1>Reactivate Intel&reg; AMT</option></select>"); }
if ((features & 0x400) == 0) {
if (ptype == 2) {
x += addHtmlValue('<span title="Client Initiated Remote Access">CIRA</span>', "<select id=dp20amtcira style=width:230px><option value=0>Don't configure</option><option value=1>Don't connect to server</option><option value=2>Connect to server</option></select>");
} else {
x += addHtmlValue('<span title="Client Initiated Remote Access">CIRA</span>', "<select id=dp20amtcira style=width:230px><option value=0>Don't configure</option><option value=2>Connect to server</option></select>");
}
}
x += '<br/><span style="font-size:10px">* Recommanded, leave blank to assign a random password to each device.</span><br/>';
x += '<span style="font-size:10px">This policy will not impact devices with Intel&reg; AMT in ACM mode.</span><br/>';
x += '<span style="font-size:10px">This is not a secure policy as agents will be performing activation.</span>';
if (ptype == 2) {
x += '<span style="font-size:10px">This policy will not impact devices with Intel&reg; AMT in ACM mode.</span><br/>';
x += '<span style="font-size:10px">This is not a secure policy as agents will be performing activation.</span>';
} else {
x += '<span style="font-size:10px">During activation, the agent will have access to admin password infomation.</span>';
}
}
QH('dp20amtpolicydiv', x);
}
@ -12409,6 +12428,9 @@ var QRCode;!function(){function a(a){this.mode=c.MODE_8BIT_BYTE,this.data=a,this
if (ptype == 2) {
amtpolicy = { type: ptype, password: Q('dp20amtpolicypass').value, badpass: parseInt(Q('dp20amtbadpass').value) };
if ((features & 0x400) == 0) { amtpolicy.cirasetup = parseInt(Q('dp20amtcira').value); } else { amtpolicy.cirasetup = 1; }
} else if (ptype == 3) {
amtpolicy = { type: ptype, password: Q('dp20amtpolicypass').value };
if ((features & 0x400) == 0) { amtpolicy.cirasetup = parseInt(Q('dp20amtcira').value); } else { amtpolicy.cirasetup = 1; }
}
meshserver.send({ action: 'meshamtpolicy', meshid: currentMesh._id, amtpolicy: amtpolicy });
}

View File

@ -6256,6 +6256,9 @@
else if (currentMesh.amt.type == 2) {
intelAmtPolicy = 'Simple Client Control Mode (CCM)';
if (currentMesh.amt.cirasetup == 2) { intelAmtPolicy += ' + CIRA'; }
} else if (currentMesh.amt.type == 3) {
intelAmtPolicy = 'Simple Admin Control Mode (ACM)';
if (currentMesh.amt.cirasetup == 2) { intelAmtPolicy += ' + CIRA'; }
}
}
x += addHtmlValue('Intel&reg; AMT', addLinkConditional(intelAmtPolicy, 'p20editMeshAmt()', meshrights & 1));
@ -6325,17 +6328,18 @@
function p20editMeshAmt() {
if (xxdialogMode) return;
var x = '';
x += addHtmlValue('Type', '<select id=dp20amtpolicy style=width:230px onchange=p20editMeshAmtChange()><option value=0>No Policy</option><option value=1>Deactivate Client Control Mode (CCM)</option><option value=2>Simple Client Control Mode (CCM)</option></select>');
var x = '', acmoption = '';
if ((features & 0x100000) != 0) { acmoption = '<option value=3>Simple Admin Control Mode (ACM)</option>'; }
x += addHtmlValue('Type', '<select id=dp20amtpolicy style=width:230px onchange=p20editMeshAmtChange()><option value=0>No Policy</option><option value=1>Deactivate Client Control Mode (CCM)</option><option value=2>Simple Client Control Mode (CCM)</option>' + acmoption + '</select>');
x += '<div id=dp20amtpolicydiv></div>';
setDialogMode(2, "Intel&reg; AMT Policy", 3, p20editMeshAmtEx, x);
if (currentMesh.amt) { Q('dp20amtpolicy').value = currentMesh.amt.type; }
p20editMeshAmtChange();
// Set the current Intel AMT policy
if (currentMesh.amt && currentMesh.amt.type == 2) {
if (currentMesh.amt && (currentMesh.amt.type == 2) || (currentMesh.amt.type == 3)) {
Q('dp20amtpolicypass').value = currentMesh.amt.password;
Q('dp20amtbadpass').value = currentMesh.amt.badpass;
if (currentMesh.amt.type == 2) { Q('dp20amtbadpass').value = currentMesh.amt.badpass; }
if ((features & 0x400) == 0) { Q('dp20amtcira').value = currentMesh.amt.cirasetup; }
}
@ -6344,13 +6348,23 @@
function p20editMeshAmtChange() {
var ptype = Q('dp20amtpolicy').value, x = '';
if (ptype == 2) {
if (ptype >= 2) {
x = addHtmlValue('Password*', '<input id=dp20amtpolicypass style=width:230px maxlength=32 onchange=dp20amtValidatePolicy() onkeyup=dp20amtValidatePolicy() />')
x += addHtmlValue('Password mismatch', "<select id=dp20amtbadpass style=width:230px><option value=0>Do nothing</option><option value=1>Reactivate Intel&reg; AMT</option></select>");
if ((features & 0x400) == 0) { x += addHtmlValue('<span title="Client Initiated Remote Access">CIRA</span>', "<select id=dp20amtcira style=width:230px><option value=0>Don't configure</option><option value=1>Don't connect to server</option><option value=2>Connect to server</option></select>"); }
if (ptype == 2) { x += addHtmlValue('Password mismatch', "<select id=dp20amtbadpass style=width:230px><option value=0>Do nothing</option><option value=1>Reactivate Intel&reg; AMT</option></select>"); }
if ((features & 0x400) == 0) {
if (ptype == 2) {
x += addHtmlValue('<span title="Client Initiated Remote Access">CIRA</span>', "<select id=dp20amtcira style=width:230px><option value=0>Don't configure</option><option value=1>Don't connect to server</option><option value=2>Connect to server</option></select>");
} else {
x += addHtmlValue('<span title="Client Initiated Remote Access">CIRA</span>', "<select id=dp20amtcira style=width:230px><option value=0>Don't configure</option><option value=2>Connect to server</option></select>");
}
}
x += '<br/><span style="font-size:10px">* Recommanded, leave blank to assign a random password to each device.</span><br/>';
x += '<span style="font-size:10px">This policy will not impact devices with Intel&reg; AMT in ACM mode.</span><br/>';
x += '<span style="font-size:10px">This is not a secure policy as agents will be performing activation.</span>';
if (ptype == 2) {
x += '<span style="font-size:10px">This policy will not impact devices with Intel&reg; AMT in ACM mode.</span><br/>';
x += '<span style="font-size:10px">This is not a secure policy as agents will be performing activation.</span>';
} else {
x += '<span style="font-size:10px">During activation, the agent will have access to admin password infomation.</span>';
}
}
QH('dp20amtpolicydiv', x);
}
@ -6366,6 +6380,9 @@
if (ptype == 2) {
amtpolicy = { type: ptype, password: Q('dp20amtpolicypass').value, badpass: parseInt(Q('dp20amtbadpass').value) };
if ((features & 0x400) == 0) { amtpolicy.cirasetup = parseInt(Q('dp20amtcira').value); } else { amtpolicy.cirasetup = 1; }
} else if (ptype == 3) {
amtpolicy = { type: ptype, password: Q('dp20amtpolicypass').value };
if ((features & 0x400) == 0) { amtpolicy.cirasetup = parseInt(Q('dp20amtcira').value); } else { amtpolicy.cirasetup = 1; }
}
meshserver.send({ action: 'meshamtpolicy', meshid: currentMesh._id, amtpolicy: amtpolicy });
}

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -1353,6 +1353,7 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) {
if (parent.config.settings.no2factorauth !== true) { features += 0x00020000; } // Enable WebAuthn/FIDO2 support
if ((obj.args.nousers != true) && (domain.passwordrequirements != null) && (domain.passwordrequirements.force2factor === true)) { features += 0x00040000; } // Force 2-factor auth
if ((domain.auth == 'sspi') || (domain.auth == 'ldap')) { features += 0x00080000; } // LDAP or SSPI in use, warn that users must login first before adding a user to a group.
if (domain.amtacmactivation) { features += 0x00100000; } // Intel AMT ACM activation/upgrade is possible
// Create a authentication cookie
const authCookie = obj.parent.encodeCookie({ userid: user._id, domainid: domain.id }, obj.parent.loginCookieEncryptionKey);