From 664eb63c636a32ddccf9efcc693eb787fccf059f Mon Sep 17 00:00:00 2001 From: Ylian Saint-Hilaire Date: Thu, 28 Nov 2019 20:57:34 -0800 Subject: [PATCH] Implemented optional 2nd factor skip for some IP addresses --- meshcentral.js | 7 +++++++ sample-config.json | 2 +- webserver.js | 15 ++++++++++----- 3 files changed, 18 insertions(+), 6 deletions(-) diff --git a/meshcentral.js b/meshcentral.js index 67d76122..e93a9399 100644 --- a/meshcentral.js +++ b/meshcentral.js @@ -728,6 +728,13 @@ function CreateMeshCentralServer(config, args) { if (typeof obj.config.domains[i].userblockedip == 'string') { if (obj.config.domains[i].userblockedip == '') { obj.config.domains[i].userblockedip = null; } else { obj.config.domains[i].userblockedip = obj.config.domains[i].userallowedip.split(','); } } if (typeof obj.config.domains[i].agentallowedip == 'string') { if (obj.config.domains[i].agentallowedip == '') { obj.config.domains[i].agentallowedip = null; } else { obj.config.domains[i].agentallowedip = obj.config.domains[i].agentallowedip.split(','); } } if (typeof obj.config.domains[i].agentblockedip == 'string') { if (obj.config.domains[i].agentblockedip == '') { obj.config.domains[i].agentblockedip = null; } else { obj.config.domains[i].agentblockedip = obj.config.domains[i].agentblockedip.split(','); } } + if ((obj.config.domains[i].passwordrequirements != null) && (typeof obj.config.domains[i].passwordrequirements == 'object')) { + if (typeof obj.config.domains[i].passwordrequirements.skip2factor == 'string') { + obj.config.domains[i].passwordrequirements.skip2factor = obj.config.domains[i].passwordrequirements.skip2factor.split(','); + } else { + delete obj.config.domains[i].passwordrequirements.skip2factor; + } + } if ((obj.config.domains[i].auth == 'ldap') && (typeof obj.config.domains[i].ldapoptions != 'object')) { if (i == '') { console.log("ERROR: Default domain is LDAP, but is missing LDAPOptions."); } else { console.log("ERROR: Domain '" + i + "' is LDAP, but is missing LDAPOptions."); } process.exit(); diff --git a/sample-config.json b/sample-config.json index 86188643..aaeb005f 100644 --- a/sample-config.json +++ b/sample-config.json @@ -76,7 +76,7 @@ "_NewAccountsRights": [ "nonewgroups", "notools" ], "Footer": "Twitter", "_CertUrl": "https://192.168.2.106:443/", - "_PasswordRequirements": { "min": 8, "max": 128, "upper": 1, "lower": 1, "numeric": 1, "nonalpha": 1, "reset": 90, "force2factor": true }, + "_PasswordRequirements": { "min": 8, "max": 128, "upper": 1, "lower": 1, "numeric": 1, "nonalpha": 1, "reset": 90, "force2factor": true, "skip2factor": "127.0.0.1,192.168.2.0/24" }, "_AgentNoProxy": true, "_GeoLocation": true, "_UserAllowedIP": "127.0.0.1,192.168.1.0/24", diff --git a/webserver.js b/webserver.js index 3f666630..be8a3679 100644 --- a/webserver.js +++ b/webserver.js @@ -511,7 +511,12 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) { } // Return true if this user has 2-step auth active - function checkUserOneTimePasswordRequired(domain, user) { + function checkUserOneTimePasswordRequired(domain, user, req) { + // Check if we can skip 2nd factor auth because of the source IP address + if ((req != null) && (req.ip != null) && (domain.passwordrequirements != null) && (domain.passwordrequirements.skip2factor != null)) { + for (var i in domain.passwordrequirements.skip2factor) { if (require('ipcheck').match(req.ip, domain.passwordrequirements.skip2factor[i]) === true) return false; } + } + // Check if a 2nd factor is present return ((parent.config.settings.no2factorauth !== true) && ((user.otpsecret != null) || ((user.otphkeys != null) && (user.otphkeys.length > 0)))); } @@ -657,7 +662,7 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) { var user = obj.users[userid]; // Check if this user has 2-step login active - if ((req.session.loginmode != '6') && checkUserOneTimePasswordRequired(domain, user)) { + if ((req.session.loginmode != '6') && checkUserOneTimePasswordRequired(domain, user, req)) { checkUserOneTimePassword(req, domain, user, req.body.token, req.body.hwtoken, function (result) { if (result == false) { var randomWaitTime = 0; @@ -1011,7 +1016,7 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) { var responseSent = false; for (var i in docs) { var user = docs[i]; - if (checkUserOneTimePasswordRequired(domain, user) == true) { + if (checkUserOneTimePasswordRequired(domain, user, req) == true) { // Second factor setup, request it now. checkUserOneTimePassword(req, domain, user, req.body.token, req.body.hwtoken, function (result) { if (result == false) { @@ -3503,7 +3508,7 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) { var user = obj.users[userid]; if ((err == null) && (user)) { // Check if a 2nd factor is needed - if (checkUserOneTimePasswordRequired(domain, user) == true) { + if (checkUserOneTimePasswordRequired(domain, user, req) == true) { if (typeof req.query.token != 'string') { try { ws.send(JSON.stringify({ action: 'close', cause: 'noauth', msg: 'tokenrequired' })); ws.close(); } catch (e) { } } else { @@ -3558,7 +3563,7 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) { var user = obj.users[userid]; if ((err == null) && (user)) { // Check if a 2nd factor is needed - if (checkUserOneTimePasswordRequired(domain, user) == true) { + if (checkUserOneTimePasswordRequired(domain, user, req) == true) { if (s.length != 3) { try { ws.send(JSON.stringify({ action: 'close', cause: 'noauth', msg: 'tokenrequired' })); ws.close(); } catch (e) { } } else {