Skip to content

Commit

Permalink
Module dependency cleanup.
Browse files Browse the repository at this point in the history
  • Loading branch information
Ylianst committed Mar 25, 2019
1 parent d63639f commit ac6c39d
Show file tree
Hide file tree
Showing 11 changed files with 16,631 additions and 49 deletions.
40 changes: 18 additions & 22 deletions meshcentral.js
Original file line number Diff line number Diff line change
Expand Up @@ -1606,23 +1606,16 @@ function InstallModules(modules, func) {
// Check if a module is present and install it if missing
var InstallModuleChildProcess = null;
function InstallModule(modulename, func, tag1, tag2) {
try {
var module = require(modulename);
} catch (e) {
console.log('Installing ' + modulename + '...');
var child_process = require('child_process');

// Looks like we need to keep a global reference to the child process object for this to work correctly.
InstallModuleChildProcess = child_process.exec('npm install ' + modulename + ' --no-optional --save', { maxBuffer: 512000, timeout: 10000 }, function (error, stdout, stderr) {
InstallModuleChildProcess = null;
if (error != null) { console.log('ERROR: Unable to install missing package \'' + modulename + '\', make sure npm is installed: ' + error); process.exit(); return; }
func(tag1, tag2);
return;
});

console.log('Installing ' + modulename + '...');
var child_process = require('child_process');

// Looks like we need to keep a global reference to the child process object for this to work correctly.
InstallModuleChildProcess = child_process.exec('npm install ' + modulename + ' --no-optional --save', { maxBuffer: 512000, timeout: 10000 }, function (error, stdout, stderr) {
InstallModuleChildProcess = null;
if (error != null) { console.log('ERROR: Unable to install missing package \'' + modulename + '\', make sure npm is installed: ' + error); process.exit(); return; }
func(tag1, tag2);
return;
}
func(tag1, tag2);
});
}

// Detect CTRL-C on Linux and stop nicely
Expand All @@ -1640,27 +1633,30 @@ function mainStart(args) {
var config = getConfig(false);
if (config == null) { process.exit(); }

// Check is Windows SSPI will be used
// Check is Windows SSPI and YubiKey OTP will be used
var sspi = false;
var allsspi = true;
if (require('os').platform() == 'win32') { for (var i in config.domains) { if (config.domains[i].auth == 'sspi') { sspi = true; } else { allsspi = false; } } }
var yubikey = false;
if (require('os').platform() == 'win32') { for (var i in config.domains) { if (config.domains[i].auth == 'sspi') { sspi = true; } else { allsspi = false; } } } else { allsspi = false; }
for (var i in config.domains) { if (config.domains[i].yubikey != null) { yubikey = true; } }

// Build the list of required modules
var modules = ['ws', 'nedb', 'https', 'yauzl', 'xmldom', 'express', 'archiver', 'multiparty', 'node-forge', 'express-ws', 'compression', 'body-parser', 'connect-redis', 'express-handlebars'];
if (require('os').platform() == 'win32') { modules.push('node-windows'); if (sspi == true) { modules.push('node-sspi'); } } // Add Windows modules
if (config.letsencrypt != null) { modules.push('greenlock'); modules.push('le-store-certbot'); modules.push('le-challenge-fs'); modules.push('le-acme-core'); } // Add Greenlock Modules
if (config.settings.mongodb != null) { modules.push('mongojs'); } // Add MongoDB
if (config.smtp != null) { modules.push('nodemailer'); } // Add SMTP support
if (yubikey == true) { modules.push('yubikeyotp'); } // Add YubiKey OTP support

// Get the current node version
var nodeVersion = Number(process.version.match(/^v(\d+\.\d+)/)[1]);

// If running NodeJS < 8, install "util.promisify"
if (nodeVersion < 8) { modules.push('util.promisify'); }

// if running NodeJS 8 or higher, we can install WebAuthn/FIDO2 support
if ((nodeVersion >= 8) && (allsspi == false)) { modules.push('@davedoesdev/fido2-lib'); }

// 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'); } }
// Install any missing modules and launch the server
InstallModules(modules, function () { meshserver = CreateMeshCentralServer(config, args); meshserver.Start(); });
});
Expand All @@ -1670,4 +1666,4 @@ if (require.main === module) {
mainStart(require('minimist')(process.argv.slice(2))); // Called directly, launch normally.
} else {
module.exports.mainStart = mainStart; // Required as a module, useful for winservice.js
}
}
29 changes: 19 additions & 10 deletions meshuser.js
Original file line number Diff line number Diff line change
Expand Up @@ -1773,7 +1773,9 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use
const twoStepLoginSupported = ((domain.auth != 'sspi') && (parent.parent.certificates.CommonName.indexOf('.') != -1) && (args.lanonly !== true) && (args.nousers !== true));
if (twoStepLoginSupported) {
// Request a one time password to be setup
const otplib = require('otplib');
var otplib = null;
try { otplib = require('otplib'); } catch (ex) { }
if (otplib == null) { break; }
const secret = otplib.authenticator.generateSecret(); // TODO: Check the random source of this value.
ws.send(JSON.stringify({ action: 'otpauth-request', secret: secret, url: otplib.authenticator.keyuri(user.name, parent.certificates.CommonName, secret) }));
}
Expand All @@ -1785,7 +1787,9 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use
const twoStepLoginSupported = ((domain.auth != 'sspi') && (parent.parent.certificates.CommonName.indexOf('.') != -1) && (args.lanonly !== true) && (args.nousers !== true));
if (twoStepLoginSupported) {
// Perform the one time password setup
const otplib = require('otplib');
var otplib = null;
try { otplib = require('otplib'); } catch (ex) { }
if (otplib == null) { break; }
otplib.authenticator.options = { window: 2 }; // Set +/- 1 minute window
if (otplib.authenticator.check(command.token, command.secret) === true) {
// Token is valid, activate 2-step login on this account.
Expand Down Expand Up @@ -1853,8 +1857,6 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use
}
case 'otp-hkey-get':
{


// 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) break;
Expand Down Expand Up @@ -1887,10 +1889,12 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use
case 'otp-hkey-yubikey-add':
{
// Yubico API id and signature key can be requested from https://upgrade.yubico.com/getapikey/
var yubikeyotp = null;
try { yubikeyotp = require('yubikeyotp'); } 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 ((twoStepLoginSupported == false) || (typeof command.otp != 'string')) {
if ((yubikeyotp == null) || (twoStepLoginSupported == false) || (typeof command.otp != 'string')) {
ws.send(JSON.stringify({ action: 'otp-hkey-yubikey-add', result: false, name: command.name }));
break;
}
Expand All @@ -1904,7 +1908,6 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use
// TODO: Check if command.otp is modhex encoded, reject if not.

// Query the YubiKey server to validate the OTP
var yubikeyotp = require('yubikeyotp');
var request = { otp: command.otp, id: domain.yubikey.id, key: domain.yubikey.secret, timestamp: true }
if (domain.yubikey.proxy) { request.requestParams = { proxy: domain.yubikey.proxy }; }
yubikeyotp.verifyOTP(request, function (err, results) {
Expand Down Expand Up @@ -1934,16 +1937,19 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use
}
case 'otp-hkey-setup-request':
{
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 (twoStepLoginSupported == false) break;
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
require('authdog').startRegistration('https://' + parent.parent.certificates.CommonName, knownKeys, { requestId: 556, timeoutSeconds: 100 }).then(function (registrationRequest) {
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;

Expand All @@ -1957,12 +1963,15 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use
}
case 'otp-hkey-setup-response':
{
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 ((twoStepLoginSupported == false) || (command.response == null) || (command.name == null) || (obj.hardwareKeyRegistrationRequest == null)) break;
if ((authdoglib == null) || (twoStepLoginSupported == false) || (command.response == null) || (command.name == null) || (obj.hardwareKeyRegistrationRequest == null)) break;

// Check the key registration request
require('authdog').finishRegistration(obj.hardwareKeyRegistrationRequest, command.response).then(function (registrationStatus) {
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 = []; }
Expand Down
57 changes: 57 additions & 0 deletions package - Copy.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
{
"name": "meshcentral",
"version": "0.3.0-x",
"keywords": [
"Remote Management",
"Intel AMT",
"Active Management",
"Remote Desktop"
],
"homepage": "http://meshcommander.com",
"description": "Web based remote computer management and file server",
"author": "Ylian Saint-Hilaire <[email protected]>",
"main": "meshcentral.js",
"bin": {
"meshcentral": "./bin/meshcentral"
},
"license": "Apache-2.0",
"files": [
"*.js",
"sample-config.json",
"license.txt",
"readme.txt",
"agents",
"public",
"views",
"bin"
],
"dependencies": {
"archiver": "^3.0.0",
"authdog": "^0.1.1",
"body-parser": "^1.18.2",
"compression": "^1.7.3",
"connect-redis": "^3.4.0",
"cookie-session": "^2.0.0-beta.3",
"express": "^4.16.4",
"express-handlebars": "^3.0.0",
"express-ws": "^4.0.0",
"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",
"otplib": "^10.0.1",
"ws": "^6.1.2",
"xmldom": "^0.1.27",
"yauzl": "^2.10.0",
"yubikeyotp": "^0.2.0"
},
"devDependencies": {},
"repository": {
"type": "git",
"url": "https://github.com/Ylianst/MeshCentral.git"
},
"readme": "readme.txt"
}
7 changes: 2 additions & 5 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "meshcentral",
"version": "0.3.0-u",
"version": "0.3.0-y",
"keywords": [
"Remote Management",
"Intel AMT",
Expand All @@ -27,7 +27,6 @@
],
"dependencies": {
"archiver": "^3.0.0",
"authdog": "^0.1.1",
"body-parser": "^1.18.2",
"compression": "^1.7.3",
"connect-redis": "^3.4.0",
Expand All @@ -41,11 +40,9 @@
"multiparty": "^4.2.1",
"nedb": "^1.8.0",
"node-forge": "^0.7.6",
"otplib": "^10.0.1",
"ws": "^6.1.2",
"xmldom": "^0.1.27",
"yauzl": "^2.10.0",
"yubikeyotp": "^0.2.0"
"yauzl": "^2.10.0"
},
"devDependencies": {},
"repository": {
Expand Down
1 change: 1 addition & 0 deletions reinstall-modules.bat
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
npm install archiver authdog body-parser compression connect-redis cookie-session express express-handlebars express-ws ipcheck minimist mongojs multiparty nedb node-forge otplib ws xmldom yauzl yubikeyotp
13,941 changes: 13,940 additions & 1 deletion views/default-min.handlebars

Large diffs are not rendered by default.

1,288 changes: 1,287 additions & 1 deletion views/login-min.handlebars

Large diffs are not rendered by default.

1,218 changes: 1,217 additions & 1 deletion views/login-mobile-min.handlebars

Large diffs are not rendered by default.

58 changes: 56 additions & 2 deletions views/login-mobile.handlebars
Original file line number Diff line number Diff line change
Expand Up @@ -287,7 +287,34 @@
if ('{{loginmode}}' == '4') {
try { if (hardwareKeyChallenge.length > 0) { hardwareKeyChallenge = JSON.parse(hardwareKeyChallenge); } else { hardwareKeyChallenge = null; } } catch (ex) { hardwareKeyChallenge = null }
if ((hardwareKeyChallenge != null) && u2fSupported()) {
if ((hardwareKeyChallenge != null) && (hardwareKeyChallenge.type == 'webAuthn')) {
hardwareKeyChallenge.challenge = Uint8Array.from(atob(hardwareKeyChallenge.challenge), c => c.charCodeAt(0)).buffer;
const publicKeyCredentialRequestOptions = { challenge: hardwareKeyChallenge.challenge, allowCredentials: [], timeout: hardwareKeyChallenge.timeout }
for (var i = 0; i < hardwareKeyChallenge.keyIds.length; i++) {
publicKeyCredentialRequestOptions.allowCredentials.push(
{ id: Uint8Array.from(atob(hardwareKeyChallenge.keyIds[i]), c => c.charCodeAt(0)), type: 'public-key', transports: ['usb', 'ble', 'nfc'], }
);
}
// New WebAuthn hardware keys
navigator.credentials.get({ publicKey: publicKeyCredentialRequestOptions }).then(
function (rawAssertion) {
var assertion = {
id: btoa(String.fromCharCode.apply(null, new Uint8Array(rawAssertion.rawId))),
clientDataJSON: btoa(String.fromCharCode.apply(null, new Uint8Array(rawAssertion.response.clientDataJSON))),
userHandle: btoa(String.fromCharCode.apply(null, new Uint8Array(rawAssertion.response.userHandle))),
signature: btoa(String.fromCharCode.apply(null, new Uint8Array(rawAssertion.response.signature))),
authenticatorData: btoa(String.fromCharCode.apply(null, new Uint8Array(rawAssertion.response.authenticatorData))),
};
Q('hwtokenInput').value = JSON.stringify(assertion);
QE('tokenOkButton', true);
Q('tokenOkButton').click();
},
function (error) { console.log('credentials-get error', error); }
);
} else if ((hardwareKeyChallenge != null) && u2fSupported()) {
// Old U2F hardware keys
window.u2f.sign(hardwareKeyChallenge.appId, hardwareKeyChallenge.challenge, hardwareKeyChallenge.registeredKeys, function (authResponse) {
if ((currentpanel == 4) && authResponse.signatureData) {
Q('hwtokenInput').value = JSON.stringify(authResponse);
Expand All @@ -300,7 +327,34 @@
if ('{{loginmode}}' == '5') {
try { if (hardwareKeyChallenge.length > 0) { hardwareKeyChallenge = JSON.parse(hardwareKeyChallenge); } else { hardwareKeyChallenge = null; } } catch (ex) { hardwareKeyChallenge = null }
if ((hardwareKeyChallenge != null) && u2fSupported()) {
if ((hardwareKeyChallenge != null) && (hardwareKeyChallenge.type == 'webAuthn')) {
hardwareKeyChallenge.challenge = Uint8Array.from(atob(hardwareKeyChallenge.challenge), c => c.charCodeAt(0)).buffer;
const publicKeyCredentialRequestOptions = { challenge: hardwareKeyChallenge.challenge, allowCredentials: [], timeout: hardwareKeyChallenge.timeout }
for (var i = 0; i < hardwareKeyChallenge.keyIds.length; i++) {
publicKeyCredentialRequestOptions.allowCredentials.push(
{ id: Uint8Array.from(atob(hardwareKeyChallenge.keyIds[i]), c => c.charCodeAt(0)), type: 'public-key', transports: ['usb', 'ble', 'nfc'], }
);
}
// New WebAuthn hardware keys
navigator.credentials.get({ publicKey: publicKeyCredentialRequestOptions }).then(
function (rawAssertion) {
var assertion = {
id: btoa(String.fromCharCode.apply(null, new Uint8Array(rawAssertion.rawId))),
clientDataJSON: btoa(String.fromCharCode.apply(null, new Uint8Array(rawAssertion.response.clientDataJSON))),
userHandle: btoa(String.fromCharCode.apply(null, new Uint8Array(rawAssertion.response.userHandle))),
signature: btoa(String.fromCharCode.apply(null, new Uint8Array(rawAssertion.response.signature))),
authenticatorData: btoa(String.fromCharCode.apply(null, new Uint8Array(rawAssertion.response.authenticatorData))),
};
Q('resetHwtokenInput').value = JSON.stringify(assertion);
QE('resetTokenOkButton', true);
Q('resetTokenOkButton').click();
},
function (error) { console.log('credentials-get error', error); }
);
} else if ((hardwareKeyChallenge != null) && u2fSupported()) {
// Old U2F hardware keys
window.u2f.sign(hardwareKeyChallenge.appId, hardwareKeyChallenge.challenge, hardwareKeyChallenge.registeredKeys, function (authResponse) {
if ((currentpanel == 5) && authResponse.signatureData) {
Q('resetHwtokenInput').value = JSON.stringify(authResponse);
Expand Down
Loading

0 comments on commit ac6c39d

Please sign in to comment.