diff --git a/agents/MeshCentralRouter.exe b/agents/MeshCentralRouter.exe index 9e288fc4..42409f8d 100644 Binary files a/agents/MeshCentralRouter.exe and b/agents/MeshCentralRouter.exe differ diff --git a/meshuser.js b/meshuser.js index 279344cb..62c9d992 100644 --- a/meshuser.js +++ b/meshuser.js @@ -2848,6 +2848,7 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use // Event the node change. Only do this if the database will not do it. event.msg = 'Changed device ' + node.name + ' from group ' + mesh.name + ': ' + changes.join(', '); event.node = parent.CloneSafeNode(node); + if (command.rdpport == 3389) { event.node.rdpport = 3389; } if (db.changeStream) { event.noact = 1; } // If DB change stream is active, don't use this event to change the node. Another event will come. parent.parent.DispatchEvent(['*', node.meshid, user._id, node._id], obj, event); } diff --git a/webserver.js b/webserver.js index 25e8e2ac..2c76e53a 100644 --- a/webserver.js +++ b/webserver.js @@ -3777,12 +3777,26 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) { if ((err == null) && (user)) { // Check if a 2nd factor is needed 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) { } + // Figure out if email 2FA is allowed + var email2fa = (((typeof domain.passwordrequirements != 'object') || (domain.passwordrequirements.email2factor != false)) && (parent.mailserver != null) && (user.otpekey != null)); + if ((typeof req.query.token != 'string') || (req.query.token == '**email**')) { + if ((req.query.token == '**email**') && (email2fa == true)) { + // Cause a token to be sent to the user's registered email + user.otpekey = { k: obj.common.zeroPad(getRandomEightDigitInteger(), 8), d: Date.now() }; + obj.db.SetUser(user); + parent.debug('web', 'Sending 2FA email to: ' + user.email); + parent.mailserver.sendAccountLoginMail(domain, user.email, user.otpekey.k); + // Ask for a login token & confirm email was sent + try { ws.send(JSON.stringify({ action: 'close', cause: 'noauth', msg: 'tokenrequired', email2fa: email2fa, email2fasent: true })); ws.close(); } catch (e) { } + } else { + // Ask for a login token + try { ws.send(JSON.stringify({ action: 'close', cause: 'noauth', msg: 'tokenrequired', email2fa: email2fa })); ws.close(); } catch (e) { } + } } else { checkUserOneTimePassword(req, domain, user, req.query.token, null, function (result) { if (result == false) { - try { ws.send(JSON.stringify({ action: 'close', cause: 'noauth', msg: 'tokenrequired' })); ws.close(); } catch (e) { } + // Failed, ask for a login token again + try { ws.send(JSON.stringify({ action: 'close', cause: 'noauth', msg: 'tokenrequired', email2fa: email2fa })); ws.close(); } catch (e) { } } else { // We are authenticated with 2nd factor. func(ws, req, domain, user); @@ -3836,12 +3850,25 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) { if ((err == null) && (user)) { // Check if a 2nd factor is needed if (checkUserOneTimePasswordRequired(domain, user, req) == true) { + // Figure out if email 2FA is allowed + var email2fa = (((typeof domain.passwordrequirements != 'object') || (domain.passwordrequirements.email2factor != false)) && (parent.mailserver != null) && (user.otpekey != null)); if (s.length != 3) { - try { ws.send(JSON.stringify({ action: 'close', cause: 'noauth', msg: 'tokenrequired' })); ws.close(); } catch (e) { } + try { ws.send(JSON.stringify({ action: 'close', cause: 'noauth', msg: 'tokenrequired', email2fa: email2fa })); ws.close(); } catch (e) { } } else { checkUserOneTimePassword(req, domain, user, s[2], null, function (result) { if (result == false) { - try { ws.send(JSON.stringify({ action: 'close', cause: 'noauth', msg: 'tokenrequired' })); ws.close(); } catch (e) { } + if ((s[2] == '**email**') && (email2fa == true)) { + // Cause a token to be sent to the user's registered email + user.otpekey = { k: obj.common.zeroPad(getRandomEightDigitInteger(), 8), d: Date.now() }; + obj.db.SetUser(user); + parent.debug('web', 'Sending 2FA email to: ' + user.email); + parent.mailserver.sendAccountLoginMail(domain, user.email, user.otpekey.k); + // Ask for a login token & confirm email was sent + try { ws.send(JSON.stringify({ action: 'close', cause: 'noauth', msg: 'tokenrequired', email2fa: email2fa, email2fasent: true })); ws.close(); } catch (e) { } + } else { + // Ask for a login token + try { ws.send(JSON.stringify({ action: 'close', cause: 'noauth', msg: 'tokenrequired', email2fa: email2fa })); ws.close(); } catch (e) { } + } } else { // We are authenticated with 2nd factor. func(ws, req, domain, user);