mirror of
				https://github.com/Ylianst/MeshCentral.git
				synced 2025-10-29 23:35:02 -04:00 
			
		
		
		
	Fixed Duo 2FA security.
This commit is contained in:
		
							parent
							
								
									5da849063b
								
							
						
					
					
						commit
						f80ba62cfc
					
				| @ -3645,14 +3645,13 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use | ||||
|                     if ((user.siteadmin != 0xFFFFFFFF) && ((user.siteadmin & 1024) != 0)) return; // If this account is settings locked, return here.
 | ||||
| 
 | ||||
|                     // Check input
 | ||||
|                     if (typeof command.enabled != 'boolean') return; | ||||
|                     if ((typeof command.enabled != 'boolean') || (command.enabled != false)) return; | ||||
| 
 | ||||
|                     // See if we really need to change the state
 | ||||
|                     if ((command.enabled === true) && (user.otpduo != null)) return; | ||||
|                     if ((command.enabled === false) && (user.otpduo == null)) return; | ||||
| 
 | ||||
|                     // Change the duo 2FA of this user
 | ||||
|                     if (command.enabled === true) { user.otpduo = {}; } else { delete user.otpduo; } | ||||
|                     delete user.otpduo; | ||||
|                     parent.db.SetUser(user); | ||||
|                     ws.send(JSON.stringify({ action: 'otpduo', success: true, enabled: command.enabled })); // Report success
 | ||||
| 
 | ||||
|  | ||||
| @ -12887,9 +12887,15 @@ | ||||
|         function account_manageAuthDuo() { | ||||
|             if (xxdialogMode || ((features2 & 0x20000000) == 0)) return; | ||||
|             var duoU2Fenabled = ((userinfo.otpduo == 1)); | ||||
|             setDialogMode(2, "Duo Authentication", 1, function () { | ||||
|                 if (duoU2Fenabled != Q('duo2facheck').checked) { meshserver.send({ action: 'otpduo', enabled: Q('duo2facheck').checked }); } | ||||
|             }, "When enabled, on each login, you will be given the option to use Duo for added security." + '<br /><br /><label><input id=duo2facheck type=checkbox ' + (duoU2Fenabled?'checked':'') + '/>' + "Enable Duo two-factor authentication." + '</label>'); | ||||
|             if (duoU2Fenabled == false) { | ||||
|                 setDialogMode(2, "Duo Authentication", 3, function () { | ||||
|                     window.location.href = '/add-duo?rurl=' + encodeURIComponentEx(window.location.href) + ((urlargs.key)?('&key=' + urlargs.key):''); | ||||
|                 }, "Confirm enabling of Duo 2FA security. Once enabled you will be given the option to use Due security at login. Click ok to go thru the steps to enable Duo."); | ||||
|             } else { | ||||
|                 setDialogMode(2, "Duo Authentication", 3, function () { | ||||
|                     meshserver.send({ action: 'otpduo', enabled: false }); | ||||
|                 }, "Confirm disabling Duo security."); | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         function account_manageAuthApp() { | ||||
|  | ||||
							
								
								
									
										120
									
								
								webserver.js
									
									
									
									
									
								
							
							
						
						
									
										120
									
								
								webserver.js
									
									
									
									
									
								
							| @ -1226,7 +1226,7 @@ module.exports.CreateWebServer = function (parent, db, args, certificates, doneF | ||||
|                         sec.duostate = client.generateState(); | ||||
|                         req.session.e = parent.encryptSessionData(sec); | ||||
|                         parent.debug('web', 'Redirecting user ' + user._id + ' to Duo'); | ||||
|                         res.redirect(client.createAuthUrl(user._id, sec.duostate)); | ||||
|                         res.redirect(client.createAuthUrl(user._id.split('/')[2], sec.duostate)); | ||||
|                         return; | ||||
|                     } | ||||
| 
 | ||||
| @ -6951,13 +6951,14 @@ module.exports.CreateWebServer = function (parent, db, args, certificates, doneF | ||||
|                     } | ||||
|                 } | ||||
| 
 | ||||
|                 // Setup Duo callback if needed
 | ||||
|                 // Setup Duo HTTP handlers if supported
 | ||||
|                 if ((typeof domain.duo2factor == 'object') && (typeof domain.duo2factor.integrationkey == 'string') && (typeof domain.duo2factor.secretkey == 'string') && (typeof domain.duo2factor.apihostname == 'string')) { | ||||
|                     // Duo authentication handler
 | ||||
|                     obj.app.get(url + 'auth-duo', function (req, res){ | ||||
|                         var domain = getDomain(req); | ||||
|                         const sec = parent.decryptSessionData(req.session.e); | ||||
|                         if (req.query.state !== sec.duostate) { | ||||
|                             // The state returned from Duo IS NOT the same as what was in the session, so must fail
 | ||||
|                         if ((req.query.state !== sec.duostate) || (req.query.duo_code == null)) { | ||||
|                             // The state returned from Duo is not the same as what was in the session, so must fail
 | ||||
|                             parent.debug('web', 'handleRootRequest: Duo 2FA state failed.'); | ||||
|                             req.session.loginmode = 1; | ||||
|                             req.session.messageid = 117; // Invalid security check
 | ||||
| @ -6975,21 +6976,76 @@ module.exports.CreateWebServer = function (parent, db, args, certificates, doneF | ||||
|                                         apiHost: domain.duo2factor.apihostname, | ||||
|                                         redirectUrl: obj.generateBaseURL(domain, req) + 'auth-duo' + (domain.loginkey != null ? ('?key=' + domain.loginkey) : '') | ||||
|                                     }); | ||||
|                                     client.exchangeAuthorizationCodeFor2FAResult(req.query.duo_code, userid).then(function (data) { | ||||
|                                         parent.debug('web', 'handleRootRequest: Duo 2FA auth ok.'); | ||||
|                                         req.session.userid = userid; | ||||
|                                         delete req.session.currentNode; | ||||
|                                         req.session.ip = req.clientIp; // Bind this session to the IP address of the request
 | ||||
|                                         setSessionRandom(req); | ||||
|                                         obj.parent.authLog('https', 'Accepted Duo authentication for ' + userid + ' from ' + req.clientIp + ' port ' + req.connection.remotePort, { useragent: req.headers['user-agent'], sessionid: req.session.x }); | ||||
|                                         res.redirect(domain.url + getQueryPortion(req)); | ||||
|                                     client.exchangeAuthorizationCodeFor2FAResult(req.query.duo_code, userid.split('/')[2]).then(function (data) { | ||||
|                                         const sec = parent.decryptSessionData(req.session.e); | ||||
|                                         if ((sec != null) && (sec.duoconfig == 1)) { | ||||
|                                             // Duo 2FA exchange success
 | ||||
|                                             parent.debug('web', 'handleRootRequest: Duo 2FA configuration success.'); | ||||
| 
 | ||||
|                                             // Enable Duo for this user
 | ||||
|                                             var user = obj.users[userid]; | ||||
|                                             if (user.otpduo == null) { | ||||
|                                                 user.otpduo = {}; | ||||
|                                                 db.SetUser(user); | ||||
| 
 | ||||
|                                                 // Notify change
 | ||||
|                                                 var targets = ['*', 'server-users', user._id]; | ||||
|                                                 if (user.groups) { for (var i in user.groups) { targets.push('server-users:' + i); } } | ||||
|                                                 var event = { etype: 'user', userid: user._id, username: user.name, account: obj.CloneSafeUser(user), action: 'accountchange', msgid: 160, msg: "Enabled duo two-factor authentication.", domain: domain.id }; | ||||
|                                                 if (db.changeStream) { event.noact = 1; } // If DB change stream is active, don't use this event to change the user. Another event will come.
 | ||||
|                                                 parent.DispatchEvent(targets, obj, event); | ||||
|                                             } | ||||
| 
 | ||||
|                                             // Clear the Duo state
 | ||||
|                                             delete sec.duostate; | ||||
|                                             delete sec.duoconfig; | ||||
|                                             req.session.e = parent.encryptSessionData(sec); | ||||
| 
 | ||||
|                                             var url = req.session.duorurl; | ||||
|                                             delete req.session.duorurl; | ||||
|                                             res.redirect(url ? url : domain.url); // Redirect back to the user's original page
 | ||||
|                                         } else { | ||||
|                                             // Duo 2FA exchange success
 | ||||
|                                             parent.debug('web', 'handleRootRequest: Duo 2FA authorization success.'); | ||||
|                                             req.session.userid = userid; | ||||
|                                             delete req.session.currentNode; | ||||
|                                             req.session.ip = req.clientIp; // Bind this session to the IP address of the request
 | ||||
|                                             setSessionRandom(req); | ||||
| 
 | ||||
|                                             // Clear the Duo state
 | ||||
|                                             delete sec.duostate; | ||||
|                                             req.session.e = parent.encryptSessionData(sec); | ||||
| 
 | ||||
|                                             obj.parent.authLog('https', 'Accepted Duo authentication for ' + userid + ' from ' + req.clientIp + ':' + req.connection.remotePort, { useragent: req.headers['user-agent'], sessionid: req.session.x }); | ||||
|                                             res.redirect(domain.url + getQueryPortion(req)); | ||||
|                                         } | ||||
|                                     }).catch(function (err) { | ||||
|                                         // Duo 2FA exchange failed
 | ||||
|                                         console.log('err',err); | ||||
|                                         parent.debug('web', 'handleRootRequest: Duo 2FA exchange authorization code failed.'); | ||||
|                                         req.session.loginmode = 1; | ||||
|                                         req.session.messageid = 117; // Invalid security check
 | ||||
|                                         res.redirect(domain.url + getQueryPortion(req)); | ||||
|                                         console.log('err', err); | ||||
|                                         const sec = parent.decryptSessionData(req.session.e); | ||||
|                                         if ((sec != null) && (sec.duoconfig == 1)) { | ||||
|                                             // Duo 2FA exchange success
 | ||||
|                                             parent.debug('web', 'handleRootRequest: Duo 2FA configuration failed.'); | ||||
| 
 | ||||
|                                             // Clear the Duo state
 | ||||
|                                             delete sec.duostate; | ||||
|                                             delete sec.duoconfig; | ||||
|                                             req.session.e = parent.encryptSessionData(sec); | ||||
| 
 | ||||
|                                             var url = req.session.duorurl; | ||||
|                                             delete req.session.duorurl; | ||||
|                                             res.redirect(url ? url : domain.url); // Redirect back to the user's original page
 | ||||
|                                         } else { | ||||
|                                             // Duo 2FA exchange failed
 | ||||
|                                             parent.debug('web', 'handleRootRequest: Duo 2FA authorization failed.'); | ||||
| 
 | ||||
|                                             // Clear the Duo state
 | ||||
|                                             delete sec.duostate; | ||||
|                                             req.session.e = parent.encryptSessionData(sec); | ||||
| 
 | ||||
|                                             req.session.loginmode = 1; | ||||
|                                             req.session.messageid = 117; // Invalid security check
 | ||||
|                                             res.redirect(domain.url + getQueryPortion(req)); | ||||
|                                         } | ||||
|                                     }); | ||||
|                                 } else { | ||||
|                                     // Login failed
 | ||||
| @ -6998,6 +7054,34 @@ module.exports.CreateWebServer = function (parent, db, args, certificates, doneF | ||||
|                             }); | ||||
|                         } | ||||
|                     }); | ||||
| 
 | ||||
|                     // Configure Duo handler
 | ||||
|                     obj.app.get(url + 'add-duo', function (req, res) { | ||||
|                         var domain = getDomain(req); | ||||
|                         const sec = parent.decryptSessionData(req.session.e); | ||||
| 
 | ||||
|                         if (req.session.userid == null) { | ||||
|                             res.sendStatus(404); | ||||
|                         } else { | ||||
|                             // Redirect to Duo here
 | ||||
|                             const duo = require('@duosecurity/duo_universal'); | ||||
|                             const client = new duo.Client({ | ||||
|                                 clientId: domain.duo2factor.integrationkey, | ||||
|                                 clientSecret: domain.duo2factor.secretkey, | ||||
|                                 apiHost: domain.duo2factor.apihostname, | ||||
|                                 redirectUrl: obj.generateBaseURL(domain, req) + 'auth-duo' + (domain.loginkey != null ? ('&key=' + domain.loginkey) : '') | ||||
|                             }); | ||||
| 
 | ||||
|                             // Setup the Duo configuration
 | ||||
|                             if (req.query.rurl) { req.session.duorurl = req.query.rurl; } // Set Duo return URL
 | ||||
|                             const sec = parent.decryptSessionData(req.session.e); | ||||
|                             sec.duostate = client.generateState(); | ||||
|                             sec.duoconfig = 1; | ||||
|                             req.session.e = parent.encryptSessionData(sec); | ||||
|                             parent.debug('web', 'Redirecting user ' + req.session.userid + ' to Duo'); | ||||
|                             res.redirect(client.createAuthUrl(req.session.userid.split('/')[2], sec.duostate)); | ||||
|                         } | ||||
|                     }); | ||||
|                 } | ||||
| 
 | ||||
|                 // Server redirects
 | ||||
|  | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user