Added support for email 2FA in MeshCentral Router.
Ylian Saint-Hilaire committed
Mar 21, 2020 at 22:52 UTC
d089062167203f7db605cbb9c4386551e3ae517a
3 files changed
+33
-5
agents/MeshCentralRouter.exe
Binary files a/agents/MeshCentralRouter.exe and b/agents/MeshCentralRouter.exe differ
meshuser.js
+1
@@ -2848,6 +2848,7 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use
2848
// Event the node change. Only do this if the database will not do it.
2849
event.msg = 'Changed device ' + node.name + ' from group ' + mesh.name + ': ' + changes.join(', ');
2850
event.node = parent.CloneSafeNode(node);
2851
+ if (command.rdpport == 3389) { event.node.rdpport = 3389; }
2852
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.
2853
parent.parent.DispatchEvent(['*', node.meshid, user._id, node._id], obj, event);
2854
}
webserver.js
+32
-5
@@ -3777,12 +3777,26 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) {
3777
if ((err == null) && (user)) {
3778
// Check if a 2nd factor is needed
3779
if (checkUserOneTimePasswordRequired(domain, user, req) == true) {
3780
- if (typeof req.query.token != 'string') {
3781
- try { ws.send(JSON.stringify({ action: 'close', cause: 'noauth', msg: 'tokenrequired' })); ws.close(); } catch (e) { }
3780
+ // Figure out if email 2FA is allowed
3781
+ var email2fa = (((typeof domain.passwordrequirements != 'object') || (domain.passwordrequirements.email2factor != false)) && (parent.mailserver != null) && (user.otpekey != null));
3782
+ if ((typeof req.query.token != 'string') || (req.query.token == '**email**')) {
3783
+ if ((req.query.token == '**email**') && (email2fa == true)) {
3784
+ // Cause a token to be sent to the user's registered email
3785
+ user.otpekey = { k: obj.common.zeroPad(getRandomEightDigitInteger(), 8), d: Date.now() };
3786
+ obj.db.SetUser(user);
3787
+ parent.debug('web', 'Sending 2FA email to: ' + user.email);
3788
+ parent.mailserver.sendAccountLoginMail(domain, user.email, user.otpekey.k);
3789
+ // Ask for a login token & confirm email was sent
3790
+ try { ws.send(JSON.stringify({ action: 'close', cause: 'noauth', msg: 'tokenrequired', email2fa: email2fa, email2fasent: true })); ws.close(); } catch (e) { }
3791
+ } else {
3792
+ // Ask for a login token
3793
+ try { ws.send(JSON.stringify({ action: 'close', cause: 'noauth', msg: 'tokenrequired', email2fa: email2fa })); ws.close(); } catch (e) { }
3794
+ }
3795
} else {
3796
checkUserOneTimePassword(req, domain, user, req.query.token, null, function (result) {
3797
if (result == false) {
3785
- try { ws.send(JSON.stringify({ action: 'close', cause: 'noauth', msg: 'tokenrequired' })); ws.close(); } catch (e) { }
3798
+ // Failed, ask for a login token again
3799
+ try { ws.send(JSON.stringify({ action: 'close', cause: 'noauth', msg: 'tokenrequired', email2fa: email2fa })); ws.close(); } catch (e) { }
3800
} else {
3801
// We are authenticated with 2nd factor.
3802
func(ws, req, domain, user);
@@ -3836,12 +3850,25 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) {
3850
if ((err == null) && (user)) {
3851
// Check if a 2nd factor is needed
3852
if (checkUserOneTimePasswordRequired(domain, user, req) == true) {
3853
+ // Figure out if email 2FA is allowed
3854
+ var email2fa = (((typeof domain.passwordrequirements != 'object') || (domain.passwordrequirements.email2factor != false)) && (parent.mailserver != null) && (user.otpekey != null));
3855
if (s.length != 3) {
3840
- try { ws.send(JSON.stringify({ action: 'close', cause: 'noauth', msg: 'tokenrequired' })); ws.close(); } catch (e) { }
3856
+ try { ws.send(JSON.stringify({ action: 'close', cause: 'noauth', msg: 'tokenrequired', email2fa: email2fa })); ws.close(); } catch (e) { }
3857
} else {
3858
checkUserOneTimePassword(req, domain, user, s[2], null, function (result) {
3859
if (result == false) {
3844
- try { ws.send(JSON.stringify({ action: 'close', cause: 'noauth', msg: 'tokenrequired' })); ws.close(); } catch (e) { }
3860
+ if ((s[2] == '**email**') && (email2fa == true)) {
3861
+ // Cause a token to be sent to the user's registered email
3862
+ user.otpekey = { k: obj.common.zeroPad(getRandomEightDigitInteger(), 8), d: Date.now() };
3863
+ obj.db.SetUser(user);
3864
+ parent.debug('web', 'Sending 2FA email to: ' + user.email);
3865
+ parent.mailserver.sendAccountLoginMail(domain, user.email, user.otpekey.k);
3866
+ // Ask for a login token & confirm email was sent
3867
+ try { ws.send(JSON.stringify({ action: 'close', cause: 'noauth', msg: 'tokenrequired', email2fa: email2fa, email2fasent: true })); ws.close(); } catch (e) { }
3868
+ } else {
3869
+ // Ask for a login token
3870
+ try { ws.send(JSON.stringify({ action: 'close', cause: 'noauth', msg: 'tokenrequired', email2fa: email2fa })); ws.close(); } catch (e) { }
3871
+ }
3872
} else {
3873
// We are authenticated with 2nd factor.
3874
func(ws, req, domain, user);