Complete replacement of the WebAuthn 2 factor support.

This commit is contained in:
Ylian Saint-Hilaire 2019-05-16 14:55:07 -07:00
parent c526c8b66b
commit 510a970bf6
9 changed files with 576 additions and 241 deletions

View File

@ -130,6 +130,7 @@
<Compile Include="public\scripts\filesaver.1.1.20151003.js" />
<Compile Include="public\scripts\meshcentral.js" />
<Compile Include="redirserver.js" />
<Compile Include="webauthn.js" />
<Compile Include="webserver.js" />
<Compile Include="winservice.js" />
<Content Include="agents\compressModules.bat" />

263
Webauthn.js Normal file
View File

@ -0,0 +1,263 @@
/**
* @description MeshCentral WebAuthn module
* @version v0.0.1
*/
//
'use strict'
const crypto = require('crypto')
const cbor = require('cbor')
//const iso_3166_1 = require('iso-3166-1')
//const Certificate = null; //require('@fidm/x509')
module.exports.CreateWebAuthnModule = function () {
var obj = {};
obj.generateRegistrationChallenge = function (rpName, user) {
return {
rp: { name: rpName },
user: user,
challenge: crypto.randomBytes(64).toString('base64'),
pubKeyCredParams: [{ type: 'public-key', alg: -7 }],
timeout: 60000,
attestation: 'none'
}
}
obj.verifyAuthenticatorAttestationResponse = function (webauthnResponse) {
const attestationBuffer = Buffer.from(webauthnResponse.attestationObject, 'base64');
const ctapMakeCredResp = cbor.decodeAllSync(attestationBuffer)[0];
const authrDataStruct = parseMakeCredAuthData(ctapMakeCredResp.authData);
//console.log('***CTAP_RESPONSE', ctapMakeCredResp)
//console.log('***AUTHR_DATA_STRUCT', authrDataStruct)
const response = { 'verified': false };
if (ctapMakeCredResp.fmt === 'none') {
if (!(authrDataStruct.flags & 0x01)) { throw new Error('User was NOT presented during authentication!'); } // U2F_USER_PRESENTED
const publicKey = COSEECDHAtoPKCS(authrDataStruct.COSEPublicKey)
response.verified = true;
if (response.verified) {
response.authrInfo = {
fmt: 'none',
publicKey: ASN1toPEM(publicKey),
counter: authrDataStruct.counter,
keyId: authrDataStruct.credID.toString('base64')
}
}
}
/*
else if (ctapMakeCredResp.fmt === 'fido-u2f') {
if (!(authrDataStruct.flags & 0x01)) // U2F_USER_PRESENTED
throw new Error('User was NOT presented during authentication!');
const clientDataHash = hash(webauthnResponse.clientDataJSON)
const reservedByte = Buffer.from([0x00]);
const publicKey = COSEECDHAtoPKCS(authrDataStruct.COSEPublicKey)
const signatureBase = Buffer.concat([reservedByte, authrDataStruct.rpIdHash, clientDataHash, authrDataStruct.credID, publicKey]);
const PEMCertificate = ASN1toPEM(ctapMakeCredResp.attStmt.x5c[0]);
const signature = ctapMakeCredResp.attStmt.sig;
response.verified = verifySignature(signature, signatureBase, PEMCertificate)
if (response.verified) {
response.authrInfo = {
fmt: 'fido-u2f',
publicKey: ASN1toPEM(publicKey),
counter: authrDataStruct.counter,
keyId: authrDataStruct.credID.toString('base64')
}
}
} else if (ctapMakeCredResp.fmt === 'packed' && ctapMakeCredResp.attStmt.hasOwnProperty('x5c')) {
if (!(authrDataStruct.flags & 0x01)) // U2F_USER_PRESENTED
throw new Error('User was NOT presented durring authentication!');
const clientDataHash = hash(webauthnResponse.clientDataJSON)
const publicKey = COSEECDHAtoPKCS(authrDataStruct.COSEPublicKey)
const signatureBase = Buffer.concat([ctapMakeCredResp.authData, clientDataHash]);
const PEMCertificate = ASN1toPEM(ctapMakeCredResp.attStmt.x5c[0]);
const signature = ctapMakeCredResp.attStmt.sig;
const pem = Certificate.fromPEM(PEMCertificate);
// Getting requirements from https://www.w3.org/TR/webauthn/#packed-attestation
const aaguid_ext = pem.getExtension('1.3.6.1.4.1.45724.1.1.4')
response.verified = // Verify that sig is a valid signature over the concatenation of authenticatorData
// and clientDataHash using the attestation public key in attestnCert with the algorithm specified in alg.
verifySignature(signature, signatureBase, PEMCertificate) &&
// version must be 3 (which is indicated by an ASN.1 INTEGER with value 2)
pem.version == 3 &&
// ISO 3166 valid country
typeof iso_3166_1.whereAlpha2(pem.subject.countryName) !== 'undefined' &&
// Legal name of the Authenticator vendor (UTF8String)
pem.subject.organizationName &&
// Literal string “Authenticator Attestation” (UTF8String)
pem.subject.organizationalUnitName === 'Authenticator Attestation' &&
// A UTF8String of the vendors choosing
pem.subject.commonName &&
// The Basic Constraints extension MUST have the CA component set to false
!pem.extensions.isCA &&
// If attestnCert contains an extension with OID 1.3.6.1.4.1.45724.1.1.4 (id-fido-gen-ce-aaguid)
// verify that the value of this extension matches the aaguid in authenticatorData.
// The extension MUST NOT be marked as critical.
(aaguid_ext != null ?
(authrDataStruct.hasOwnProperty('aaguid') ?
!aaguid_ext.critical && aaguid_ext.value.slice(2).equals(authrDataStruct.aaguid) : false)
: true);
if (response.verified) {
response.authrInfo = {
fmt: 'fido-u2f',
publicKey: publicKey,
counter: authrDataStruct.counter,
keyId: authrDataStruct.credID.toString('base64')
}
}
// Self signed
} else if (ctapMakeCredResp.fmt === 'packed') {
if (!(authrDataStruct.flags & 0x01)) // U2F_USER_PRESENTED
throw new Error('User was NOT presented durring authentication!');
const clientDataHash = hash(webauthnResponse.clientDataJSON)
const publicKey = COSEECDHAtoPKCS(authrDataStruct.COSEPublicKey)
const signatureBase = Buffer.concat([ctapMakeCredResp.authData, clientDataHash]);
const PEMCertificate = ASN1toPEM(publicKey);
const { attStmt: { sig: signature, alg } } = ctapMakeCredResp
response.verified = // Verify that sig is a valid signature over the concatenation of authenticatorData
// and clientDataHash using the attestation public key in attestnCert with the algorithm specified in alg.
verifySignature(signature, signatureBase, PEMCertificate) && alg === -7
if (response.verified) {
response.authrInfo = {
fmt: 'fido-u2f',
publicKey: ASN1toPEM(publicKey),
counter: authrDataStruct.counter,
keyId: authrDataStruct.credID.toString('base64')
}
}
} else if (ctapMakeCredResp.fmt === 'android-safetynet') {
console.log("Android safetynet request\n")
console.log(ctapMakeCredResp)
const authrDataStruct = parseMakeCredAuthData(ctapMakeCredResp.authData);
console.log('AUTH_DATA', authrDataStruct)
//console.log('CLIENT_DATA_JSON ', webauthnResponse.clientDataJSON)
const publicKey = COSEECDHAtoPKCS(authrDataStruct.COSEPublicKey)
let [header, payload, signature] = ctapMakeCredResp.attStmt.response.toString('utf8').split('.')
const signatureBase = Buffer.from([header, payload].join('.'))
header = JSON.parse(header)
payload = JSON.parse(payload)
console.log('JWS HEADER', header)
console.log('JWS PAYLOAD', payload)
console.log('JWS SIGNATURE', signature)
const PEMCertificate = ASN1toPEM(Buffer.from(header.x5c[0], 'base64'))
const pem = Certificate.fromPEM(PEMCertificate)
console.log('PEM', pem)
response.verified = // Verify that sig is a valid signature over the concatenation of authenticatorData
// and clientDataHash using the attestation public key in attestnCert with the algorithm specified in alg.
verifySignature(signature, signatureBase, PEMCertificate) &&
// version must be 3 (which is indicated by an ASN.1 INTEGER with value 2)
pem.version == 3 &&
pem.subject.commonName === 'attest.android.com'
if (response.verified) {
response.authrInfo = {
fmt: 'fido-u2f',
publicKey: ASN1toPEM(publicKey),
counter: authrDataStruct.counter,
keyId: authrDataStruct.credID.toString('base64')
}
}
console.log('RESPONSE', response)
} */
else {
throw new Error(`Unsupported attestation format: ${ctapMakeCredResp.fmt}`);
}
return response
}
obj.verifyAuthenticatorAssertionResponse = function (webauthnResponse, authr) {
const response = { 'verified': false }
if (['fido-u2f'].includes(authr.fmt)) {
const authrDataStruct = parseGetAssertAuthData(webauthnResponse.authenticatorData)
if (!(authrDataStruct.flags & 0x01)) { throw new Error('User was not presented durring authentication!') } // U2F_USER_PRESENTED
response.counter = authrDataStruct.counter;
response.verified = verifySignature(webauthnResponse.signature, Buffer.concat([authrDataStruct.rpIdHash, authrDataStruct.flagsBuf, authrDataStruct.counterBuf, hash(webauthnResponse.clientDataJSON)]), authr.publicKey);
}
return response;
}
function hash(data) { return crypto.createHash('sha256').update(data).digest() }
function verifySignature(signature, data, publicKey) { return crypto.createVerify('SHA256').update(data).verify(publicKey, signature); }
function parseGetAssertAuthData(buffer) {
const rpIdHash = buffer.slice(0, 32)
buffer = buffer.slice(32)
const flagsBuf = buffer.slice(0, 1)
buffer = buffer.slice(1)
const flags = flagsBuf[0]
const counterBuf = buffer.slice(0, 4)
buffer = buffer.slice(4)
const counter = counterBuf.readUInt32BE(0)
return { rpIdHash, flagsBuf, flags, counter, counterBuf }
}
function parseMakeCredAuthData(buffer) {
const rpIdHash = buffer.slice(0, 32)
buffer = buffer.slice(32)
const flagsBuf = buffer.slice(0, 1)
buffer = buffer.slice(1)
const flags = flagsBuf[0]
const counterBuf = buffer.slice(0, 4)
buffer = buffer.slice(4)
const counter = counterBuf.readUInt32BE(0)
const aaguid = buffer.slice(0, 16)
buffer = buffer.slice(16)
const credIDLenBuf = buffer.slice(0, 2)
buffer = buffer.slice(2)
const credIDLen = credIDLenBuf.readUInt16BE(0)
const credID = buffer.slice(0, credIDLen)
buffer = buffer.slice(credIDLen)
const COSEPublicKey = buffer
return { rpIdHash, flagsBuf, flags, counter, counterBuf, aaguid, credID, COSEPublicKey }
}
function COSEECDHAtoPKCS(COSEPublicKey) {
const coseStruct = cbor.decodeAllSync(COSEPublicKey)[0];
return Buffer.concat([Buffer.from([0x04]), coseStruct.get(-2), coseStruct.get(-3)])
}
function ASN1toPEM(pkBuffer) {
if (!Buffer.isBuffer(pkBuffer)) { throw new Error("ASN1toPEM: pkBuffer must be Buffer."); }
let type
if (pkBuffer.length == 65 && pkBuffer[0] == 0x04) { pkBuffer = Buffer.concat([ new Buffer.from("3059301306072a8648ce3d020106082a8648ce3d030107034200", "hex"), pkBuffer ]); type = 'PUBLIC KEY' } else { type = 'CERTIFICATE' }
const b64cert = pkBuffer.toString('base64')
let PEMKey = ''
for (let i = 0; i < Math.ceil(b64cert.length / 64); i++) { const start = 64 * i; PEMKey += b64cert.substr(start, 64) + '\n'; }
PEMKey = `-----BEGIN ${type}-----\n` + PEMKey + `-----END ${type}-----\n`
return PEMKey
}
return obj;
}

View File

@ -1748,8 +1748,7 @@ function mainStart(args) {
if (config.settings.no2factorauth !== true) {
// Setup YubiKey OTP if configured
if (yubikey == true) { modules.push('yubikeyotp'); } // Add YubiKey OTP support
// if not all SSPI, WebAuthn/FIDO2 or U2F support depending on the NodeJS version. FIDO2 does not work below NodeJS 8.x
if (allsspi == false) { modules.push('otplib'); if (nodeVersion >= 8) { modules.push('@davedoesdev/fido2-lib'); } else { modules.push('authdog'); } }
if (allsspi == false) { modules.push('otplib'); } // Google Authenticator support
}
// Install any missing modules and launch the server

View File

@ -2253,64 +2253,6 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use
}
});
break;
}
case 'otp-hkey-setup-request':
{
if (parent.parent.config.settings.no2factorauth === true) return;
var authdoglib = null;
try { authdoglib = require('authdog'); } catch (ex) { }
// Check is 2-step login is supported
const twoStepLoginSupported = ((domain.auth != 'sspi') && (parent.parent.certificates.CommonName.indexOf('.') != -1) && (args.lanonly !== true) && (args.nousers !== true));
if ((authdoglib == null) || (twoStepLoginSupported == false)) break;
// Build list of known keys
var knownKeys = [];
if (user.otphkeys != null) { for (var i = 0; i < user.otphkeys.length; i++) { if (user.otphkeys[i].type == 1) { knownKeys.push(user.otphkeys[i]); } } }
// Build a key registration request and send it over
authdoglib.startRegistration('https://' + parent.parent.certificates.CommonName, knownKeys, { requestId: 556, timeoutSeconds: 100 }).then(function (registrationRequest) {
// Save registration request to session for later use
obj.hardwareKeyRegistrationRequest = registrationRequest;
// Send registration request to client
ws.send(JSON.stringify({ action: 'otp-hkey-setup-request', request: registrationRequest, name: command.name }));
}, function (error) {
// Handle registration request error
ws.send(JSON.stringify({ action: 'otp-hkey-setup-request', request: null, error: error, name: command.name }));
});
break;
}
case 'otp-hkey-setup-response':
{
if (parent.parent.config.settings.no2factorauth === true) return;
var authdoglib = null;
try { authdoglib = require('authdog'); } catch (ex) { }
// Check is 2-step login is supported
const twoStepLoginSupported = ((domain.auth != 'sspi') && (parent.parent.certificates.CommonName.indexOf('.') != -1) && (args.lanonly !== true) && (args.nousers !== true));
if ((authdoglib == null) || (twoStepLoginSupported == false) || (command.response == null) || (command.name == null) || (obj.hardwareKeyRegistrationRequest == null)) break;
// Check the key registration request
authdoglib.finishRegistration(obj.hardwareKeyRegistrationRequest, command.response).then(function (registrationStatus) {
var keyIndex = parent.crypto.randomBytes(4).readUInt32BE(0);
ws.send(JSON.stringify({ action: 'otp-hkey-setup-response', result: true, name: command.name, index: keyIndex }));
if (user.otphkeys == null) { user.otphkeys = []; }
user.otphkeys.push({ name: command.name, type: 1, publicKey: registrationStatus.publicKey, keyHandle: registrationStatus.keyHandle, certificate: registrationStatus.certificate, keyIndex: keyIndex });
parent.db.SetUser(user);
delete obj.hardwareKeyRegistrationRequest;
// Notify change
var targets = ['*', 'server-users', user._id];
if (user.groups) { for (var i in user.groups) { targets.push('server-users:' + i); } }
parent.parent.DispatchEvent(targets, obj, { etype: 'user', username: user.name, account: parent.CloneSafeUser(user), action: 'accountchange', msg: 'Added security key.', domain: domain.id });
}, function (error) {
ws.send(JSON.stringify({ action: 'otp-hkey-setup-response', result: false, error: error, name: command.name, index: keyIndex }));
delete obj.hardwareKeyRegistrationRequest;
});
break;
}
case 'webauthn-startregister':
@ -2319,47 +2261,28 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use
// Check is 2-step login is supported
const twoStepLoginSupported = ((domain.auth != 'sspi') && (parent.parent.certificates.CommonName.indexOf('.') != -1) && (args.lanonly !== true) && (args.nousers !== true));
if ((twoStepLoginSupported == false) || (command.name == null) || (parent.f2l == null)) break;
if ((twoStepLoginSupported == false) || (command.name == null)) break;
parent.f2l.attestationOptions().then(function (registrationOptions) {
// Convert the challenge to base64 and add user information
registrationOptions.challenge = Buffer(registrationOptions.challenge).toString('base64');
registrationOptions.user.id = Buffer(user._id, 'binary').toString('base64');
registrationOptions.user.name = user._id;
registrationOptions.user.displayName = user._id.split('/')[2];
// Send the registration request
obj.webAuthnReqistrationRequest = { action: 'webauthn-startregister', keyname: command.name, request: registrationOptions };
ws.send(JSON.stringify(obj.webAuthnReqistrationRequest));
//console.log(obj.webAuthnReqistrationRequest);
}, function (error) {
console.log('webauthn-startregister-error', error);
});
// Send the registration request
var registrationOptions = parent.webauthn.generateRegistrationChallenge("Anonymous Service", { id: Buffer(user._id, 'binary').toString('base64'), name: user._id, displayName: user._id.split('/')[2] });
obj.webAuthnReqistrationRequest = { action: 'webauthn-startregister', keyname: command.name, request: registrationOptions };
ws.send(JSON.stringify(obj.webAuthnReqistrationRequest));
break;
}
case 'webauthn-endregister':
{
if (parent.parent.config.settings.no2factorauth === true) return;
if ((obj.webAuthnReqistrationRequest == null) || (parent.f2l == null)) return;
if (obj.webAuthnReqistrationRequest == null) return;
// Figure out the origin
var httpport = ((args.aliasport != null) ? args.aliasport : args.port);
var origin = "https://" + (domain.dns ? domain.dns : parent.certificates.CommonName);
if (httpport != 443) { origin += ':' + httpport; }
var attestationExpectations = {
challenge: obj.webAuthnReqistrationRequest.request.challenge.split('+').join('-').split('/').join('_').split('=').join(''), // Convert to Base64URL
origin: origin,
factor: "either"
};
var clientAttestationResponse = command.response;
clientAttestationResponse.id = clientAttestationResponse.rawId;
clientAttestationResponse.rawId = new Uint8Array(Buffer.from(clientAttestationResponse.rawId, 'base64')).buffer;
clientAttestationResponse.response.attestationObject = new Uint8Array(Buffer.from(clientAttestationResponse.response.attestationObject, 'base64')).buffer;
clientAttestationResponse.response.clientDataJSON = new Uint8Array(Buffer.from(clientAttestationResponse.response.clientDataJSON, 'base64')).buffer;
parent.f2l.attestationResult(clientAttestationResponse, attestationExpectations).then(function (regResult) {
// Use internal WebAuthn module to check the response
var regResult = null;
try { regResult = parent.webauthn.verifyAuthenticatorAttestationResponse(command.response.response); } catch (ex) { regResult = { verified: false, error: ex }; }
if (regResult.verified === true) {
// Since we are registering a WebAuthn/FIDO2 key, remove all U2F keys (Type 1).
var otphkeys2 = [];
if (user.otphkeys && Array.isArray(user.otphkeys)) { for (var i = 0; i < user.otphkeys.length; i++) { if (user.otphkeys[i].type != 1) { otphkeys2.push(user.otphkeys[i]); } } }
@ -2368,7 +2291,7 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use
// Add the new WebAuthn/FIDO2 keys
var keyIndex = parent.crypto.randomBytes(4).readUInt32BE(0);
if (user.otphkeys == null) { user.otphkeys = []; }
user.otphkeys.push({ name: obj.webAuthnReqistrationRequest.keyname, type: 3, publicKey: regResult.authnrData.get('credentialPublicKeyPem'), counter: regResult.authnrData.get('counter'), keyIndex: keyIndex, keyId: clientAttestationResponse.id });
user.otphkeys.push({ name: obj.webAuthnReqistrationRequest.keyname, type: 3, publicKey: regResult.authrInfo.publicKey, counter: regResult.authrInfo.counter, keyIndex: keyIndex, keyId: regResult.authrInfo.keyId });
parent.db.SetUser(user);
ws.send(JSON.stringify({ action: 'otp-hkey-setup-response', result: true, name: command.name, index: keyIndex }));
@ -2376,10 +2299,10 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use
var targets = ['*', 'server-users', user._id];
if (user.groups) { for (var i in user.groups) { targets.push('server-users:' + i); } }
parent.parent.DispatchEvent(targets, obj, { etype: 'user', username: user.name, account: parent.CloneSafeUser(user), action: 'accountchange', msg: 'Added security key.', domain: domain.id });
}, function (error) {
console.log('webauthn-endregister-error', error);
ws.send(JSON.stringify({ action: 'otp-hkey-setup-response', result: false, error: error, name: command.name, index: keyIndex }));
});
} else {
//console.log('webauthn-endregister-error', regResult.error);
ws.send(JSON.stringify({ action: 'otp-hkey-setup-response', result: false, error: regResult.error, name: command.name, index: keyIndex }));
}
delete obj.hardwareKeyRegistrationRequest;
break;

264
package-lock.json generated
View File

@ -1,6 +1,6 @@
{
"name": "meshcentral",
"version": "0.3.1-h",
"version": "0.3.4-l",
"lockfileVersion": 1,
"requires": true,
"dependencies": {
@ -9,7 +9,7 @@
"resolved": "https://registry.npmjs.org/@davedoesdev/fido2-lib/-/fido2-lib-2.4.0.tgz",
"integrity": "sha512-qBNCio4amWMxDe7e8VG3wOo3iu/cFxzLpZ+vzRhLDYbQpZVBhJxFyYXCak33+1qPkDHJOSLZBU7x5jyruU1RMA==",
"requires": {
"@peculiar/webcrypto": "1.0.8",
"@peculiar/webcrypto": "1.0.10",
"asn1js": "2.0.22",
"cbor": "4.1.5",
"cose-to-jwk": "1.1.0",
@ -19,6 +19,27 @@
"psl": "1.1.31"
}
},
"@fidm/asn1": {
"version": "1.0.4",
"resolved": "https://registry.npmjs.org/@fidm/asn1/-/asn1-1.0.4.tgz",
"integrity": "sha512-esd1jyNvRb2HVaQGq2Gg8Z0kbQPXzV9Tq5Z14KNIov6KfFD6PTaRIO8UpcsYiTNzOqJpmyzWgVTrUwFV3UF4TQ=="
},
"@fidm/x509": {
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/@fidm/x509/-/x509-1.2.1.tgz",
"integrity": "sha512-nwc2iesjyc9hkuzcrMCBXQRn653XuAUKorfWM8PZyJawiy1QzLj4vahwzaI25+pfpwOLvMzbJ0uKpWLDNmo16w==",
"requires": {
"@fidm/asn1": "1.0.4",
"tweetnacl": "1.0.1"
},
"dependencies": {
"tweetnacl": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-1.0.1.tgz",
"integrity": "sha512-kcoMoKTPYnoeS50tzoqjPY3Uv9axeuuFAZY9M/9zFnhoVvRfxz9K29IMPD7jGmt2c8SW7i3gT9WqDl2+nV7p4A=="
}
}
},
"@peculiar/asn1-schema": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/@peculiar/asn1-schema/-/asn1-schema-1.0.3.tgz",
@ -37,22 +58,31 @@
}
},
"@peculiar/webcrypto": {
"version": "1.0.8",
"resolved": "https://registry.npmjs.org/@peculiar/webcrypto/-/webcrypto-1.0.8.tgz",
"integrity": "sha512-zOCwDimHbQeUgSdBCHnw/69wbA1ks+aUt1MPAttcWNOlXaZjbUppc8shtwX/mzDQYctzNGFlI8Xrayl7lBUe5A==",
"version": "1.0.10",
"resolved": "https://registry.npmjs.org/@peculiar/webcrypto/-/webcrypto-1.0.10.tgz",
"integrity": "sha512-Ohkfu3xgk/Y/6+40Uq8juAO1zPuuAuSJ/BLpP4xovjU3+u3J7Rc/dbcWB4/Z2sj+0L4hZXh6p4rZsvzDFk22QQ==",
"requires": {
"@peculiar/asn1-schema": "1.0.3",
"@peculiar/json-schema": "1.1.5",
"asn1js": "2.0.22",
"pvtsutils": "1.0.4",
"tslib": "1.9.3",
"webcrypto-core": "1.0.11"
"webcrypto-core": "1.0.12"
}
},
"@types/node": {
"version": "10.14.4",
"resolved": "https://registry.npmjs.org/@types/node/-/node-10.14.4.tgz",
"integrity": "sha512-DT25xX/YgyPKiHFOpNuANIQIVvYEwCWXgK2jYYwqgaMrYE6+tq+DtmMwlD3drl6DJbUwtlIDnn0d7tIn/EbXBg=="
"version": "10.14.6",
"resolved": "https://registry.npmjs.org/@types/node/-/node-10.14.6.tgz",
"integrity": "sha512-Fvm24+u85lGmV4hT5G++aht2C5I4Z4dYlWZIh62FAfFO/TfzXtPpoLI6I7AuBWkIFqZCnhFOoTT7RjjaIL5Fjg=="
},
"abstract-leveldown": {
"version": "6.0.3",
"resolved": "https://registry.npmjs.org/abstract-leveldown/-/abstract-leveldown-6.0.3.tgz",
"integrity": "sha512-jzewKKpZbaYUa6HTThnrl+GrJhzjEAeuc7hTVpZdzg7kupXZFoqQDFwyOwLNbmJKJlmzw8yiipMPkDiuKkT06Q==",
"requires": {
"level-concat-iterator": "2.0.1",
"xtend": "4.0.1"
}
},
"accepts": {
"version": "1.3.5",
@ -430,9 +460,9 @@
}
},
"combined-stream": {
"version": "1.0.7",
"resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.7.tgz",
"integrity": "sha512-brWl9y6vOB1xYPZcpZde3N9zDByXTosAeMDo4p1wzo6UMOX4vumB+TP1RZ76sfE6Md68Q0NJSrE/gbezd4Ul+w==",
"version": "1.0.8",
"resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz",
"integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==",
"requires": {
"delayed-stream": "1.0.0"
}
@ -652,6 +682,15 @@
"integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=",
"optional": true
},
"deferred-leveldown": {
"version": "5.0.1",
"resolved": "https://registry.npmjs.org/deferred-leveldown/-/deferred-leveldown-5.0.1.tgz",
"integrity": "sha512-BXohsvTedWOLkj2n/TY+yqVlrCWa2Zs8LSxh3uCAgFOru7/pjxKyZAexGa1j83BaKloER4PqUyQ9rGPJLt9bqA==",
"requires": {
"abstract-leveldown": "6.0.3",
"inherits": "2.0.3"
}
},
"define-properties": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.2.tgz",
@ -724,6 +763,17 @@
"resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz",
"integrity": "sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k="
},
"encoding-down": {
"version": "6.0.2",
"resolved": "https://registry.npmjs.org/encoding-down/-/encoding-down-6.0.2.tgz",
"integrity": "sha512-oAEANslmNb64AF4kvHXjTxB7KecwD7X0qf8MffMfhpjP6gjGcnCTOkRgps/1yUNeR4Bhe6ckN6aAzZz+RIYgTw==",
"requires": {
"abstract-leveldown": "6.0.3",
"inherits": "2.0.3",
"level-codec": "9.0.1",
"level-errors": "2.0.1"
}
},
"end-of-stream": {
"version": "1.4.1",
"resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.1.tgz",
@ -732,6 +782,14 @@
"once": "1.4.0"
}
},
"errno": {
"version": "0.1.7",
"resolved": "https://registry.npmjs.org/errno/-/errno-0.1.7.tgz",
"integrity": "sha512-MfrRBDWzIWifgq6tJj60gkAwtLNb6sQPlcFrSOflcP1aFmmruKQ2wRnze/8V6kgyz7H3FF8Npzv78mZ7XLLflg==",
"requires": {
"prr": "1.0.1"
}
},
"es6-promise": {
"version": "3.2.1",
"resolved": "https://registry.npmjs.org/es6-promise/-/es6-promise-3.2.1.tgz",
@ -872,6 +930,11 @@
"resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-2.0.1.tgz",
"integrity": "sha1-ewUhjd+WZ79/Nwv3/bLLFf3Qqkk="
},
"fast-future": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/fast-future/-/fast-future-1.0.2.tgz",
"integrity": "sha1-hDWpqqAteSSNF9cE52JZMB2ZKAo="
},
"fast-json-stable-stringify": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.0.0.tgz",
@ -915,7 +978,7 @@
"integrity": "sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ==",
"requires": {
"asynckit": "0.4.0",
"combined-stream": "1.0.7",
"combined-stream": "1.0.8",
"mime-types": "2.1.21"
}
},
@ -1104,6 +1167,11 @@
"resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz",
"integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE="
},
"iso-3166-1": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/iso-3166-1/-/iso-3166-1-1.1.0.tgz",
"integrity": "sha1-gGrfYoPV96pAXRY3qKUC+R0sVHw="
},
"isstream": {
"version": "0.1.2",
"resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz",
@ -1185,6 +1253,107 @@
"readable-stream": "2.3.6"
}
},
"level": {
"version": "5.0.1",
"resolved": "https://registry.npmjs.org/level/-/level-5.0.1.tgz",
"integrity": "sha512-wcak5OQeA4rURGacqS62R/xNHjCYnJSQDBOlm4KNUGJVE9bWv2B04TclqReYejN+oD65PzD4FsqeWoI5wNC5Lg==",
"requires": {
"level-js": "4.0.1",
"level-packager": "5.0.1",
"leveldown": "5.0.3",
"opencollective-postinstall": "2.0.2"
}
},
"level-codec": {
"version": "9.0.1",
"resolved": "https://registry.npmjs.org/level-codec/-/level-codec-9.0.1.tgz",
"integrity": "sha512-ajFP0kJ+nyq4i6kptSM+mAvJKLOg1X5FiFPtLG9M5gCEZyBmgDi3FkDrvlMkEzrUn1cWxtvVmrvoS4ASyO/q+Q=="
},
"level-concat-iterator": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/level-concat-iterator/-/level-concat-iterator-2.0.1.tgz",
"integrity": "sha512-OTKKOqeav2QWcERMJR7IS9CUo1sHnke2C0gkSmcR7QuEtFNLLzHQAvnMw8ykvEcv0Qtkg0p7FOwP1v9e5Smdcw=="
},
"level-errors": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/level-errors/-/level-errors-2.0.1.tgz",
"integrity": "sha512-UVprBJXite4gPS+3VznfgDSU8PTRuVX0NXwoWW50KLxd2yw4Y1t2JUR5In1itQnudZqRMT9DlAM3Q//9NCjCFw==",
"requires": {
"errno": "0.1.7"
}
},
"level-iterator-stream": {
"version": "4.0.1",
"resolved": "https://registry.npmjs.org/level-iterator-stream/-/level-iterator-stream-4.0.1.tgz",
"integrity": "sha512-pSZWqXK6/yHQkZKCHrR59nKpU5iqorKM22C/BOHTb/cwNQ2EOZG+bovmFFGcOgaBoF3KxqJEI27YwewhJQTzsw==",
"requires": {
"inherits": "2.0.3",
"readable-stream": "3.3.0",
"xtend": "4.0.1"
},
"dependencies": {
"readable-stream": {
"version": "3.3.0",
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.3.0.tgz",
"integrity": "sha512-EsI+s3k3XsW+fU8fQACLN59ky34AZ14LoeVZpYwmZvldCFo0r0gnelwF2TcMjLor/BTL5aDJVBMkss0dthToPw==",
"requires": {
"inherits": "2.0.3",
"string_decoder": "1.1.1",
"util-deprecate": "1.0.2"
}
}
}
},
"level-js": {
"version": "4.0.1",
"resolved": "https://registry.npmjs.org/level-js/-/level-js-4.0.1.tgz",
"integrity": "sha512-m5JRIyHZn5VnCCFeRegJkn5bQd3MJK5qZX12zg3Oivc8+BUIS2yFS6ANMMeHX2ieGxucNvEn6/ZnyjmZQLLUWw==",
"requires": {
"abstract-leveldown": "6.0.3",
"immediate": "3.2.3",
"inherits": "2.0.3",
"ltgt": "2.2.1",
"typedarray-to-buffer": "3.1.5"
},
"dependencies": {
"immediate": {
"version": "3.2.3",
"resolved": "https://registry.npmjs.org/immediate/-/immediate-3.2.3.tgz",
"integrity": "sha1-0UD6j2FGWb1lQSMwl92qwlzdmRw="
}
}
},
"level-packager": {
"version": "5.0.1",
"resolved": "https://registry.npmjs.org/level-packager/-/level-packager-5.0.1.tgz",
"integrity": "sha512-tigB8g7xnFE5es2d/OmGJvcJC9S+FQfJnnULSLbPr53/ABPIaCarCxofkwdBhnJxjLZCNvj/Je6luFj/XeuaUQ==",
"requires": {
"encoding-down": "6.0.2",
"levelup": "4.0.1"
}
},
"leveldown": {
"version": "5.0.3",
"resolved": "https://registry.npmjs.org/leveldown/-/leveldown-5.0.3.tgz",
"integrity": "sha512-isfWtOQIXbGbQRI8nmU9FqCZM0klmqTAOFi0vF6G/D0O1ZgxLrSh6Xd4Zj9iVQfGt6+8jpYwkRbN07VLrxRM8w==",
"requires": {
"abstract-leveldown": "6.0.3",
"fast-future": "1.0.2",
"napi-macros": "1.8.2",
"node-gyp-build": "3.8.0"
}
},
"levelup": {
"version": "4.0.1",
"resolved": "https://registry.npmjs.org/levelup/-/levelup-4.0.1.tgz",
"integrity": "sha512-l7KXOkINXHgNqmz0v9bxvRnMCUG4gmShFrzFSZXXhcqFnfvKAW8NerVsTICpZtVhGOMAmhY6JsVoVh/tUPBmdg==",
"requires": {
"deferred-leveldown": "5.0.1",
"level-errors": "2.0.1",
"level-iterator-stream": "4.0.1",
"xtend": "4.0.1"
}
},
"lie": {
"version": "3.1.1",
"resolved": "https://registry.npmjs.org/lie/-/lie-3.1.1.tgz",
@ -1251,6 +1420,11 @@
"resolved": "https://registry.npmjs.org/longest/-/longest-1.0.1.tgz",
"integrity": "sha1-MKCy2jj3N3DoKUoNIuZiXtd9AJc="
},
"ltgt": {
"version": "2.2.1",
"resolved": "https://registry.npmjs.org/ltgt/-/ltgt-2.2.1.tgz",
"integrity": "sha1-81ypHEk/e3PaDgdJUwTxezH4fuU="
},
"media-typer": {
"version": "0.3.0",
"resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz",
@ -1373,7 +1547,7 @@
"mongojs": {
"version": "2.6.0",
"resolved": "https://registry.npmjs.org/mongojs/-/mongojs-2.6.0.tgz",
"integrity": "sha512-r6tj71DjYcaRTi2jpa+CA6Iq72cTZclB2JKy+Zub+0JPTEq/l2plsAYfF2eHqSYBtZbKNcObvhGYk9E9UKZWJg==",
"integrity": "sha1-Mz9YBiGg3GSACGXjjjam5h/xaYk=",
"requires": {
"each-series": "1.0.0",
"mongodb": "2.2.36",
@ -1433,6 +1607,11 @@
}
}
},
"napi-macros": {
"version": "1.8.2",
"resolved": "https://registry.npmjs.org/napi-macros/-/napi-macros-1.8.2.tgz",
"integrity": "sha512-Tr0DNY4RzTaBG2W2m3l7ZtFuJChTH6VZhXVhkGGjF/4cZTt+i8GcM9ozD+30Lmr4mDoZ5Xx34t2o4GJqYWDGcg=="
},
"nedb": {
"version": "1.8.0",
"resolved": "https://registry.npmjs.org/nedb/-/nedb-1.8.0.tgz",
@ -1455,6 +1634,11 @@
"resolved": "https://registry.npmjs.org/node-forge/-/node-forge-0.7.6.tgz",
"integrity": "sha1-/fO0GK7h+U8O9kLNY0hsd8qXJKw="
},
"node-gyp-build": {
"version": "3.8.0",
"resolved": "https://registry.npmjs.org/node-gyp-build/-/node-gyp-build-3.8.0.tgz",
"integrity": "sha512-bYbpIHyRqZ7sVWXxGpz8QIRug5JZc/hzZH4GbdT9HTZi6WmKCZ8GLvP8OZ9TTiIBvwPFKgtGrlWQSXDAvYdsPw=="
},
"node-jose": {
"version": "1.1.3",
"resolved": "https://registry.npmjs.org/node-jose/-/node-jose-1.1.3.tgz",
@ -1464,7 +1648,7 @@
"es6-promise": "4.2.6",
"lodash": "4.17.11",
"long": "4.0.0",
"node-forge": "0.8.2",
"node-forge": "0.8.3",
"uuid": "3.3.2"
},
"dependencies": {
@ -1474,9 +1658,9 @@
"integrity": "sha512-aRVgGdnmW2OiySVPUC9e6m+plolMAJKjZnQlCwNSuK5yQ0JN61DZSO1X1Ufd1foqWRAlig0rhduTCHe7sVtK5Q=="
},
"node-forge": {
"version": "0.8.2",
"resolved": "https://registry.npmjs.org/node-forge/-/node-forge-0.8.2.tgz",
"integrity": "sha512-mXQ9GBq1N3uDCyV1pdSzgIguwgtVpM7f5/5J4ipz12PKWElmPpVWLDuWl8iXmhysr21+WmX/OJ5UKx82wjomgg=="
"version": "0.8.3",
"resolved": "https://registry.npmjs.org/node-forge/-/node-forge-0.8.3.tgz",
"integrity": "sha512-5lv9UKmvTBog+m4AWL8XpZnr3WbNKxYL2M77i903ylY/huJIooSTDHyUWQ/OppFuKQpAGMk6qNtDymSJNRIEIg=="
}
}
},
@ -1541,6 +1725,11 @@
"wrappy": "1.0.2"
}
},
"opencollective-postinstall": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/opencollective-postinstall/-/opencollective-postinstall-2.0.2.tgz",
"integrity": "sha512-pVOEP16TrAO2/fjej1IdOyupJY8KDUM1CvsaScRbw6oddvpQoOfGk4ywha0HKKVAD6RkW4x6Q+tNBwhf3Bgpuw=="
},
"optimist": {
"version": "0.6.1",
"resolved": "https://registry.npmjs.org/optimist/-/optimist-0.6.1.tgz",
@ -1637,6 +1826,11 @@
"ipaddr.js": "1.8.0"
}
},
"prr": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/prr/-/prr-1.0.1.tgz",
"integrity": "sha1-0/wRS6BplaRexok/SEzrHXj19HY="
},
"psl": {
"version": "1.1.31",
"resolved": "https://registry.npmjs.org/psl/-/psl-1.1.31.tgz",
@ -1652,7 +1846,7 @@
"resolved": "https://registry.npmjs.org/pvtsutils/-/pvtsutils-1.0.4.tgz",
"integrity": "sha512-lBDyLfPIWZjxHr6Nnl83/iaZgVLczDcpEqWdqRnghzBKXifRU/7D5T6JPYWUAm0sJdFeF9+sNTKto6dj/3P/Kg==",
"requires": {
"@types/node": "10.14.4",
"@types/node": "10.14.6",
"tslib": "1.9.3"
}
},
@ -1739,7 +1933,7 @@
"aws-sign2": "0.7.0",
"aws4": "1.8.0",
"caseless": "0.12.0",
"combined-stream": "1.0.7",
"combined-stream": "1.0.8",
"extend": "3.0.2",
"forever-agent": "0.6.1",
"form-data": "2.3.3",
@ -1958,6 +2152,14 @@
"mime-types": "2.1.21"
}
},
"typedarray-to-buffer": {
"version": "3.1.5",
"resolved": "https://registry.npmjs.org/typedarray-to-buffer/-/typedarray-to-buffer-3.1.5.tgz",
"integrity": "sha512-zdu8XMNEDepKKR+XYOXAVPtWui0ly0NtohUscw+UmaHiAWT8hrV1rr//H6V+0DvJ3OQ19S979M0laLfX8rm82Q==",
"requires": {
"is-typedarray": "1.0.0"
}
},
"uglify-js": {
"version": "2.8.29",
"resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-2.8.29.tgz",
@ -2022,7 +2224,7 @@
"uuid": {
"version": "3.3.2",
"resolved": "https://registry.npmjs.org/uuid/-/uuid-3.3.2.tgz",
"integrity": "sha1-G0r0lV6zB3xQHCOHL8ZROBFYcTE="
"integrity": "sha512-yXJmeNaw3DnnKAOKJE51sL/ZaYfWJRl1pK9dr19YFCu0ObS231AB1/LbqTKRAQ5kw8A90rA6fr4riOUpTZvQZA=="
},
"vary": {
"version": "1.1.2",
@ -2039,10 +2241,24 @@
"extsprintf": "1.3.0"
}
},
"webauthn": {
"version": "0.1.1",
"resolved": "https://registry.npmjs.org/webauthn/-/webauthn-0.1.1.tgz",
"integrity": "sha512-UEMV9M1MKK1EscfTKNU8/7Y+C31e+2bkgV3Pp66k4deHvBw8qDD4TNCAtaRKdPlPy8g0aZ/8sI1Q4obMPPwbEQ==",
"requires": {
"@fidm/x509": "1.2.1",
"base64url": "3.0.1",
"cbor": "4.1.5",
"express": "4.16.4",
"iso-3166-1": "1.1.0",
"level": "5.0.1",
"lodash": "4.17.11"
}
},
"webcrypto-core": {
"version": "1.0.11",
"resolved": "https://registry.npmjs.org/webcrypto-core/-/webcrypto-core-1.0.11.tgz",
"integrity": "sha512-cHxOMKNzRkPlCcQyGTuyaZmAVZlbby0lH9ASaFk5w8x/i3Q809sISA28AMVnkebW2ekcQ2+UtdaVBFUf58xjyA==",
"version": "1.0.12",
"resolved": "https://registry.npmjs.org/webcrypto-core/-/webcrypto-core-1.0.12.tgz",
"integrity": "sha512-U+qBSJY4u+Ev5KNiCOPZ5MwiCSDdoM9gxeYLdnr3q8LWnX6GOBJqcowhl/kOplbhHtaa4IVZK7R2zNC4fJ7/RQ==",
"requires": {
"pvtsutils": "1.0.4",
"tslib": "1.9.3"

View File

@ -1,6 +1,6 @@
{
"name": "meshcentral",
"version": "0.3.4-k",
"version": "0.3.4-l",
"keywords": [
"Remote Management",
"Intel AMT",
@ -29,6 +29,7 @@
"dependencies": {
"archiver": "^3.0.0",
"body-parser": "^1.18.2",
"cbor": "^4.1.5",
"compression": "^1.7.3",
"connect-redis": "^3.4.0",
"cookie-session": "^2.0.0-beta.3",
@ -38,10 +39,10 @@
"ipcheck": "^0.1.0",
"meshcentral": "*",
"minimist": "^1.2.0",
"mongojs": "^2.6.0",
"multiparty": "^4.2.1",
"nedb": "^1.8.0",
"node-forge": "^0.7.6",
"node-windows": "^0.1.14",
"ws": "^6.1.2",
"xmldom": "^0.1.27",
"yauzl": "^2.10.0"

File diff suppressed because one or more lines are too long

View File

@ -1561,7 +1561,6 @@
}
x += "</div>";
x += "<div><input type=button value='Close' onclick=setDialogMode(0) style=float:right></input>";
if ((features & 0x00020000) == 0) { x += "<input id=d2addkey1 type=button value='Add Key' onclick='account_addhkey(1);'></input>"; }
if ((features & 0x00020000) != 0) { x += "<input id=d2addkey3 type=button value='Add Key' onclick='account_addhkey(3);'></input>"; }
if ((features & 0x00004000) != 0) { x += "<input id=d2addkey2 type=button value='Add YubiKey&reg; OTP' onclick='account_addhkey(2);'></input>"; }
x += "</div><br />";
@ -1577,23 +1576,6 @@
}
break;
}
case 'otp-hkey-setup-request': {
if (xxdialogMode && (xxdialogTag != 'otpauth-hardware-manage')) return;
var x = "Press the key button now.<br /><br /><div style=width:100%;text-align:center><img width=120 height=117 src='images/hardware-keypress-120.png' /></div><input id=dp1keyname style=display:none value=" + message.name + " />";
setDialogMode(2, "Add Security Key", 2, null, x);
window.u2f.register(message.request.appId, message.request.registerRequests, message.request.registeredKeys, function (registrationResponse) {
if (registrationResponse.registrationData) {
meshserver.send({ action: 'otp-hkey-setup-response', response: registrationResponse, name: Q('dp1keyname').value });
setDialogMode(2, "Add Security Key", 0, null, '<br />Checking...<br /><br /><br />', 'otpauth-hardware-manage');
} else {
if (xxdialogMode && (xxdialogTag != 'otpauth-hardware-manage')) {
var errorCodes = ['', 'Unknown error', 'Bad request', 'Unsupported configuration', 'This key was already registered', 'Timeout'];
setDialogMode(2, "Add Security Key", 1, null, '<br />' + errorCodes[registrationResponse.errorCode] + '.<br /><br />');
}
}
}, message.request.timeoutSeconds);
break;
}
case 'otp-hkey-setup-response': {
if (xxdialogMode && (xxdialogTag != 'otpauth-hardware-manage')) return;
if (message.result == true) {
@ -5777,7 +5759,7 @@
}
function account_addhkey(type) {
if (type == 1 || type == 3) {
if (type == 3) {
var x = "Type in the name of the key to add.<br /><br />";
x += addHtmlValue('Key Name', '<input id=dp1keyname style=width:230px maxlength=20 autocomplete=off placeholder="MyKey" onkeyup=account_addhkeyValidate(event,2) />');
} else if (type == 2) {
@ -5796,9 +5778,7 @@
function account_addhkeyEx(button, type) {
var name = Q('dp1keyname').value;
if (name == '') { name = 'MyKey'; }
if (type == 1) {
meshserver.send({ action: 'otp-hkey-setup-request', name: name });
} else if (type == 2) {
if (type == 2) {
meshserver.send({ action: 'otp-hkey-yubikey-add', name: name, otp: Q('dp1key').value });
setDialogMode(2, "Add Security Key", 0, null, "<br />Checking...<br /><br /><br />", 'otpauth-hardware-manage');
} else if (type == 3) {

View File

@ -63,7 +63,7 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) {
const constants = (obj.crypto.constants ? obj.crypto.constants : require('constants')); // require('constants') is deprecated in Node 11.10, use require('crypto').constants instead.
// Setup WebAuthn / FIDO2
try { const { Fido2Lib } = require("@davedoesdev/fido2-lib"); obj.f2l = new Fido2Lib({ attestation: "none" }); } catch (ex) { }
obj.webauthn = require("./webauthn.js").CreateWebAuthnModule();
// Variables
obj.parent = parent;
@ -503,66 +503,50 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) {
if (user.otphkeys && (user.otphkeys.length > 0) && (typeof (hwtoken) == 'string') && (hwtoken.length > 0)) {
var authResponse = null;
try { authResponse = JSON.parse(hwtoken); } catch (ex) { }
if (authResponse != null) {
if ((obj.f2l != null) && (authResponse.clientDataJSON)) {
// Get all WebAuthn keys
var webAuthnKeys = [];
for (var i = 0; i < user.otphkeys.length; i++) { if (user.otphkeys[i].type == 3) { webAuthnKeys.push(user.otphkeys[i]); } }
if (webAuthnKeys.length > 0) {
// Decode authentication response
var clientAssertionResponse = { response: {} };
clientAssertionResponse.id = authResponse.id;
clientAssertionResponse.rawId = new Uint8Array(Buffer.from(authResponse.id, 'base64')).buffer;
clientAssertionResponse.response.authenticatorData = new Uint8Array(Buffer.from(authResponse.authenticatorData, 'base64')).buffer;
clientAssertionResponse.response.clientDataJSON = new Uint8Array(Buffer.from(authResponse.clientDataJSON, 'base64')).buffer;
clientAssertionResponse.response.signature = new Uint8Array(Buffer.from(authResponse.signature, 'base64')).buffer;
clientAssertionResponse.response.userHandle = new Uint8Array(Buffer.from(authResponse.userHandle, 'base64')).buffer;
if ((authResponse != null) && (authResponse.clientDataJSON)) {
// Get all WebAuthn keys
var webAuthnKeys = [];
for (var i = 0; i < user.otphkeys.length; i++) { if (user.otphkeys[i].type == 3) { webAuthnKeys.push(user.otphkeys[i]); } }
if (webAuthnKeys.length > 0) {
// Decode authentication response
var clientAssertionResponse = { response: {} };
clientAssertionResponse.id = authResponse.id;
clientAssertionResponse.rawId = Buffer.from(authResponse.id, 'base64');
clientAssertionResponse.response.authenticatorData = Buffer.from(authResponse.authenticatorData, 'base64');
clientAssertionResponse.response.clientDataJSON = Buffer.from(authResponse.clientDataJSON, 'base64');
clientAssertionResponse.response.signature = Buffer.from(authResponse.signature, 'base64');
clientAssertionResponse.response.userHandle = Buffer.from(authResponse.userHandle, 'base64');
// Look for the key with clientAssertionResponse.id
var webAuthnKey = null;
for (var i = 0; i < webAuthnKeys.length; i++) { if (webAuthnKeys[i].keyId == clientAssertionResponse.id) { webAuthnKey = webAuthnKeys[i]; } }
// Look for the key with clientAssertionResponse.id
var webAuthnKey = null;
for (var i = 0; i < webAuthnKeys.length; i++) { if (webAuthnKeys[i].keyId == clientAssertionResponse.id) { webAuthnKey = webAuthnKeys[i]; } }
// If we found a valid key to use, let's validate the response
if (webAuthnKey != null) {
// Figure out the origin
var httpport = ((args.aliasport != null) ? args.aliasport : args.port);
var origin = "https://" + (domain.dns ? domain.dns : parent.certificates.CommonName);
if (httpport != 443) { origin += ':' + httpport; }
// If we found a valid key to use, let's validate the response
if (webAuthnKey != null) {
// Figure out the origin
var httpport = ((args.aliasport != null) ? args.aliasport : args.port);
var origin = "https://" + (domain.dns ? domain.dns : parent.certificates.CommonName);
if (httpport != 443) { origin += ':' + httpport; }
var assertionExpectations = {
challenge: req.session.u2fchallenge,
origin: origin,
factor: "either",
publicKey: webAuthnKey.publicKey,
prevCounter: webAuthnKey.counter,
userHandle: Buffer(user._id, 'binary').toString('base64')
};
var assertionExpectations = {
challenge: req.session.u2fchallenge,
origin: origin,
factor: "either",
fmt: "fido-u2f",
publicKey: webAuthnKey.publicKey,
prevCounter: webAuthnKey.counter,
userHandle: Buffer(user._id, 'binary').toString('base64')
};
obj.f2l.assertionResult(clientAssertionResponse, assertionExpectations).then(
function (authnResult) {
// Update the hardware key counter and accept the 2nd factor
webAuthnKey.counter = authnResult.authnrData.get('counter');
obj.db.SetUser(user);
func(true);
},
function (error) {
//console.log('attestationResult-error', error);
func(false);
}
);
return;
}
}
} else {
// Get all U2F keys
var u2fKeys = [];
for (var i = 0; i < user.otphkeys.length; i++) { if (user.otphkeys[i].type == 1) { u2fKeys.push(user.otphkeys[i]); } }
if (u2fKeys.length > 0) {
// Check authentication response
var authdoglib = null;
try { authdoglib = require('authdog'); } catch (ex) { }
if (authdoglib == null) { func(false); } else {
authdoglib.finishAuthentication(req.session.u2fchallenge, authResponse, u2fKeys).then(function (authenticationStatus) { func(true); }, function (error) { console.log(error); func(false); });
var webauthnResponse = null;
try { webauthnResponse = obj.webauthn.verifyAuthenticatorAssertionResponse(clientAssertionResponse.response, assertionExpectations); } catch (ex) { console.log(ex); }
if ((webauthnResponse != null) && (webauthnResponse.verified === true)) {
// Update the hardware key counter and accept the 2nd factor
webAuthnKey.counter = webauthnResponse.counter;
obj.db.SetUser(user);
func(true);
} else {
func(false);
}
return;
}
@ -607,46 +591,14 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) {
if (req.session.u2fchallenge) { delete req.session.u2fchallenge; };
if (user.otphkeys && (user.otphkeys.length > 0)) {
// Get all WebAuthn keys
if (obj.f2l != null) {
var webAuthnKeys = [];
for (var i = 0; i < user.otphkeys.length; i++) { if (user.otphkeys[i].type == 3) { webAuthnKeys.push(user.otphkeys[i]); } }
if (webAuthnKeys.length > 0) {
obj.f2l.assertionOptions().then(function (authnOptions) {
authnOptions.type = 'webAuthn';
authnOptions.keyIds = [];
for (var i = 0; i < webAuthnKeys.length; i++) { authnOptions.keyIds.push(webAuthnKeys[i].keyId); }
req.session.u2fchallenge = authnOptions.challenge = Buffer(authnOptions.challenge).toString('base64');
func(JSON.stringify(authnOptions));
}, function (error) {
console.log('assertionOptions-Error', error);
func('');
});
return;
}
}
var authdoglib = null;
try { authdoglib = require('authdog'); } catch (ex) { }
if (authdoglib != null) {
// Get all U2F keys
var u2fKeys = [];
for (var i = 0; i < user.otphkeys.length; i++) { if (user.otphkeys[i].type == 1) { u2fKeys.push(user.otphkeys[i]); } }
// Generate a U2F challenge
if (u2fKeys.length > 0) {
authdoglib.startAuthentication('https://' + obj.parent.certificates.CommonName, u2fKeys, { requestId: 0, timeoutSeconds: 60 }).then(function (registrationRequest) {
// Save authentication request to session for later use
req.session.u2fchallenge = registrationRequest;
// Send authentication request to client
func(JSON.stringify(registrationRequest));
}, function (error) {
// Handle authentication request error
func('');
});
} else {
func('');
}
var webAuthnKeys = [];
for (var i = 0; i < user.otphkeys.length; i++) { if (user.otphkeys[i].type == 3) { webAuthnKeys.push(user.otphkeys[i]); } }
if (webAuthnKeys.length > 0) {
// Generate a Webauthn challenge, this is really easy, no need to call any modules to do this.
var authnOptions = { type: 'webAuthn', keyIds: [], timeout: 60000, challenge: obj.crypto.randomBytes(64).toString('base64') };
for (var i = 0; i < webAuthnKeys.length; i++) { authnOptions.keyIds.push(webAuthnKeys[i].keyId); }
req.session.u2fchallenge = authnOptions.challenge;
func(JSON.stringify(authnOptions));
}
} else {
func('');
@ -1338,7 +1290,7 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) {
if ((parent.config.settings.no2factorauth !== true) && domain.yubikey && domain.yubikey.id && domain.yubikey.secret) { features += 0x00004000; } // Indicates Yubikey support
if (domain.geolocation == true) { features += 0x00008000; } // Enable geo-location features
if ((domain.passwordrequirements != null) && (domain.passwordrequirements.hint === true)) { features += 0x00010000; } // Enable password hints
if ((parent.config.settings.no2factorauth !== true) && (obj.f2l != null)) { features += 0x00020000; } // Enable WebAuthn/FIDO2 support
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.