Updated Windows agent, security improvements.
This commit is contained in:
parent
027a9944f5
commit
524f910fd5
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
Copyright 2018-2019 Intel Corporation
|
||||
Copyright 2018 Intel Corporation
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
|
@ -193,11 +193,14 @@ function monitorinfo()
|
|||
this._X11 = this._gm.CreateNativeProxy(this.Location_X11LIB);
|
||||
this._X11.CreateMethod('XChangeProperty');
|
||||
this._X11.CreateMethod('XCloseDisplay');
|
||||
this._X11.CreateMethod('XConnectionNumber');
|
||||
this._X11.CreateMethod('XConvertSelection');
|
||||
this._X11.CreateMethod('XCreateGC');
|
||||
this._X11.CreateMethod('XCreateWindow');
|
||||
this._X11.CreateMethod('XCreateSimpleWindow');
|
||||
this._X11.CreateMethod('XDefaultColormap');
|
||||
this._X11.CreateMethod('XDefaultScreen');
|
||||
this._X11.CreateMethod('XDestroyWindow');
|
||||
this._X11.CreateMethod('XDrawLine');
|
||||
this._X11.CreateMethod('XDisplayHeight');
|
||||
this._X11.CreateMethod('XDisplayWidth');
|
||||
|
@ -209,8 +212,11 @@ function monitorinfo()
|
|||
this._X11.CreateMethod('XInternAtom');
|
||||
this._X11.CreateMethod('XMapWindow');
|
||||
this._X11.CreateMethod({ method: 'XNextEvent', threadDispatch: true });
|
||||
this._X11.CreateMethod({ method: 'XNextEvent', newName: 'XNextEventSync' });
|
||||
this._X11.CreateMethod('XOpenDisplay');
|
||||
this._X11.CreateMethod('XPending');
|
||||
this._X11.CreateMethod('XRootWindow');
|
||||
this._X11.CreateMethod('XSelectInput');
|
||||
this._X11.CreateMethod('XScreenCount');
|
||||
this._X11.CreateMethod('XScreenOfDisplay');
|
||||
this._X11.CreateMethod('XSelectInput');
|
||||
|
@ -220,7 +226,7 @@ function monitorinfo()
|
|||
this._X11.CreateMethod('XSetLineAttributes');
|
||||
this._X11.CreateMethod('XSetNormalHints');
|
||||
this._X11.CreateMethod('XSetSubwindowMode');
|
||||
|
||||
this._X11.CreateMethod('XSync');
|
||||
this._X11.CreateMethod('XBlackPixel');
|
||||
this._X11.CreateMethod('XWhitePixel');
|
||||
}
|
||||
|
|
File diff suppressed because one or more lines are too long
63
meshuser.js
63
meshuser.js
|
@ -128,13 +128,13 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use
|
|||
if (user == null) { try { obj.ws.close(); } catch (e) { } return; }
|
||||
|
||||
// Check if we have exceeded the user session limit
|
||||
if (typeof domain.limits.maxusersessions == 'number') {
|
||||
if ((typeof domain.limits.maxusersessions == 'number') || (typeof domain.limits.maxsingleusersessions == 'number')) {
|
||||
// Count the number of user sessions for this domain
|
||||
var domainUserSessionCount = 0;
|
||||
for (var i in obj.parent.wssessions2) { if (obj.parent.wssessions2[i].domainid == domain.id) { domainUserSessionCount++; } }
|
||||
var domainUserSessionCount = 0, selfUserSessionCount = 0;
|
||||
for (var i in obj.parent.wssessions2) { if (obj.parent.wssessions2[i].domainid == domain.id) { domainUserSessionCount++; if (obj.parent.wssessions2[i].userid == user._id) { selfUserSessionCount++; } } }
|
||||
|
||||
// Check if we have too many user sessions
|
||||
if (domainUserSessionCount >= domain.limits.maxusersessions) {
|
||||
if (((typeof domain.limits.maxusersessions == 'number') && (domainUserSessionCount >= domain.limits.maxusersessions)) || ((typeof domain.limits.maxsingleusersessions == 'number') && (selfUserSessionCount >= domain.limits.maxsingleusersessions))) {
|
||||
ws.send(JSON.stringify({ action: 'stopped', msg: 'Session count exceed' }));
|
||||
try { obj.ws.close(); } catch (e) { }
|
||||
return;
|
||||
|
@ -708,6 +708,45 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use
|
|||
}
|
||||
break;
|
||||
}
|
||||
case 'changepassword':
|
||||
{
|
||||
// Change our own password
|
||||
if (obj.common.validateString(command.oldpass, 1, 256) == false) break;
|
||||
if (obj.common.validateString(command.newpass, 1, 256) == false) break;
|
||||
if (obj.common.validateString(command.hint, 0, 256) == false) break;
|
||||
if (obj.common.checkPasswordRequirements(command.newpass, domain.passwordrequirements) == false) break; // Password does not meet requirements
|
||||
|
||||
// Start by checking the old password
|
||||
obj.parent.checkUserPassword(domain, user, command.oldpass, function (result) {
|
||||
if (result == true) {
|
||||
// Update the password
|
||||
require('./pass').hash(command.newpass, function (err, salt, hash) {
|
||||
if (err) {
|
||||
// Send user notification of error
|
||||
displayNotificationMessage('Error, password not changed.');
|
||||
} else {
|
||||
// Change the password
|
||||
var hint = command.hint;
|
||||
if (hint.length > 250) hint = hint.substring(0, 250);
|
||||
user.salt = salt;
|
||||
user.hash = hash;
|
||||
user.passhint = req.body.apasswordhint;
|
||||
user.passchange = Math.floor(Date.now() / 1000);
|
||||
delete user.passtype;
|
||||
obj.db.SetUser(user);
|
||||
obj.parent.parent.DispatchEvent(['*', 'server-users'], obj, { etype: 'user', username: user.name, action: 'passchange', msg: 'Account password changed: ' + user.name, domain: domain.id });
|
||||
|
||||
// Send user notification of password change
|
||||
displayNotificationMessage('Password changed.');
|
||||
}
|
||||
});
|
||||
} else {
|
||||
// Send user notification of error
|
||||
displayNotificationMessage('Current password not correct.');
|
||||
}
|
||||
});
|
||||
break;
|
||||
}
|
||||
case 'changeuserpass':
|
||||
{
|
||||
// Change a user's password
|
||||
|
@ -718,22 +757,27 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use
|
|||
if (typeof command.removeMultiFactor != 'boolean') break;
|
||||
if (obj.common.checkPasswordRequirements(command.pass, domain.passwordrequirements) == false) break; // Password does not meet requirements
|
||||
|
||||
var chguserid = 'user/' + domain.id + '/' + command.user.toLowerCase(), chguser = obj.parent.users[chguserid];
|
||||
if (chguser && chguser.salt) {
|
||||
var chguser = obj.parent.users['user/' + domain.id + '/' + command.user.toLowerCase()];
|
||||
if (chguser) {
|
||||
// Compute the password hash & save it
|
||||
require('./pass').hash(command.pass, chguser.salt, function (err, hash) {
|
||||
require('./pass').hash(command.pass, function (err, salt, hash) {
|
||||
if (!err) {
|
||||
var annonceChange = false;
|
||||
chguser.salt = salt;
|
||||
chguser.hash = hash;
|
||||
chguser.passhint = command.hint;
|
||||
chguser.passchange = Math.floor(Date.now() / 1000);
|
||||
delete chguser.passtype; // Remove the password type if one was present.
|
||||
if (command.removeMultiFactor == true) {
|
||||
if (chguser.otpsecret) { delete chguser.otpsecret; annonceChange = true; }
|
||||
if (chguser.otphkeys) { delete chguser.otphkeys; annonceChange = true; }
|
||||
if (chguser.otpkeys) { delete chguser.otpkeys; annonceChange = true; }
|
||||
}
|
||||
obj.db.SetUser(chguser);
|
||||
|
||||
if (annonceChange == true) { obj.parent.parent.DispatchEvent(['*', 'server-users', user._id, chguser._id], obj, { etype: 'user', username: user.name, account: obj.parent.CloneSafeUser(chguser), action: 'accountchange', msg: 'Removed 2nd factor auth.', domain: domain.id }); }
|
||||
} else {
|
||||
// Report that the password change failed
|
||||
// TODO
|
||||
}
|
||||
});
|
||||
}
|
||||
|
@ -1775,6 +1819,9 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use
|
|||
}
|
||||
}
|
||||
|
||||
// Display a notification message for this session only.
|
||||
function displayNotificationMessage(msg, tag) { ws.send(JSON.stringify({ "action": "msg", "type": "notify", "value": msg, "userid": user._id, "username": user.name, "tag": tag })); }
|
||||
|
||||
// Read the folder and all sub-folders and serialize that into json.
|
||||
function readFilesRec(path) {
|
||||
var r = {}, dir = obj.fs.readdirSync(path);
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "meshcentral",
|
||||
"version": "0.2.8-b",
|
||||
"version": "0.2.8-c",
|
||||
"keywords": [
|
||||
"Remote Management",
|
||||
"Intel AMT",
|
||||
|
|
File diff suppressed because one or more lines are too long
|
@ -5479,20 +5479,31 @@
|
|||
|
||||
function account_showChangePassword() {
|
||||
if (xxdialogMode) return;
|
||||
var x = "Change your account password by entering the new password twice in the boxes below.<br /><br />";
|
||||
x += "<form action='" + domainUrl + "changepassword' method=post><table style=margin-left:60px><tr>";
|
||||
x += "<td align=right>Password:</td><td><input id=apassword1 type=password name=apassword1 autocomplete=off onchange=account_validateNewPassword() onkeyup=account_validateNewPassword() onkeydown=account_validateNewPassword() /> <b><span id=dxPassWarn></span></b></td>";
|
||||
x += "</tr><tr><td align=right>Password:</td><td><input id=apassword2 type=password name=apassword2 autocomplete=off onchange=account_validateNewPassword() onkeyup=account_validateNewPassword() onkeydown=account_validateNewPassword() /></td>";
|
||||
x += "</tr><tr><td align=right>Password Hint:</td><td><input id=apasswordhint name=apasswordhint maxlength=250 type=text autocomplete=off /></td>";
|
||||
x += '</tr></table><br /><div style=padding:10px;margin-bottom:4px>';
|
||||
x += '<input id=account_dlgCancelButton type=button value=Cancel style=float:right;width:80px;margin-left:5px onclick=dialogclose(0)>';
|
||||
x += '<input id=account_dlgOkButton type=submit value=OK style="float:right;width:80px" onclick=dialogclose(1)>';
|
||||
x += '</div><br /></form>';
|
||||
setDialogMode(2, "Change Password", 0, null, x);
|
||||
Q('apassword1').focus();
|
||||
var x = "Change your account password by entering the old password and new password twice in the boxes below. Password hint can be used but is not recommanded.<br /><br />";
|
||||
//x += "<form action='" + domainUrl + "changepassword' method=post>";
|
||||
x += "<table style=margin-left:60px>";
|
||||
x += "<tr><td align=right>Old password:</td><td><input id=apassword0 type=password name=apassword0 autocomplete=off onchange=account_validateNewPassword() onkeyup=account_validateNewPassword() onkeydown=account_validateNewPassword() /> <b></b></td></tr>";
|
||||
x += "<tr><td align=right>New password:</td><td><input id=apassword1 type=password name=apassword1 autocomplete=off onchange=account_validateNewPassword() onkeyup=account_validateNewPassword() onkeydown=account_validateNewPassword() /> <b><span id=dxPassWarn></span></b></td></tr>";
|
||||
x += "<tr><td align=right>New password:</td><td><input id=apassword2 type=password name=apassword2 autocomplete=off onchange=account_validateNewPassword() onkeyup=account_validateNewPassword() onkeydown=account_validateNewPassword() /></td></tr>";
|
||||
x += "<tr><td align=right>Password hint:</td><td><input id=apasswordhint name=apasswordhint maxlength=250 type=text autocomplete=off onchange=account_validateNewPassword() onkeyup=account_validateNewPassword() onkeydown=account_validateNewPassword() /></td></tr>";
|
||||
x += '</table>'
|
||||
if (passRequirements) { var r = []; for (var i in passRequirements) { r.push(i + ':' + passRequirements[i]); } x += '<br /><span style=font-size:x-small>Requirements: ' + r.join(', ') + '.</span>'; }
|
||||
x += '<br />';
|
||||
//x += '<br /><div style=padding:10px;margin-bottom:4px>';
|
||||
//x += '<input id=account_dlgCancelButton type=button value=Cancel style=float:right;width:80px;margin-left:5px onclick=dialogclose(0)>';
|
||||
//x += '<input id=account_dlgOkButton type=submit value=OK style="float:right;width:80px" onclick=dialogclose(1)>';
|
||||
//x += '</div><br /></form>';
|
||||
setDialogMode(2, "Change Password", 3, account_showChangePasswordEx, x);
|
||||
Q('apassword0').focus();
|
||||
account_validateNewPassword();
|
||||
}
|
||||
|
||||
function account_showChangePasswordEx() {
|
||||
if (Q('apassword1').value == Q('apassword2').value) {
|
||||
meshserver.send({ action: 'changepassword', oldpass: Q('apassword0').value, newpass: Q('apassword1').value, hint: Q('apasswordhint').value });
|
||||
}
|
||||
}
|
||||
|
||||
function account_createMesh() {
|
||||
if (xxdialogMode) return;
|
||||
|
||||
|
@ -5522,7 +5533,7 @@
|
|||
}
|
||||
|
||||
function account_validateNewPassword() {
|
||||
var r = '', ok = (Q('apassword1').value.length > 0) && (Q('apassword1').value == Q('apassword2').value);
|
||||
var r = '', ok = (Q('apassword0').value.length > 0) && (Q('apassword1').value.length > 0) && (Q('apassword1').value == Q('apassword2').value) && (Q('apassword0').value != Q('apassword1').value) && (Q('apasswordhint').value != Q('apassword1').value);
|
||||
if (Q('apassword1').value != '') {
|
||||
if (passRequirements == null || passRequirements == '') {
|
||||
// No password requirements, display password strength
|
||||
|
@ -5535,7 +5546,8 @@
|
|||
}
|
||||
}
|
||||
QH('dxPassWarn', r);
|
||||
QE('account_dlgOkButton', ok);
|
||||
//QE('account_dlgOkButton', ok);
|
||||
QE('idx_dlgOkButton', ok);
|
||||
}
|
||||
|
||||
// Return a password strength score
|
||||
|
@ -6344,6 +6356,7 @@
|
|||
x += addHtmlValue('Email', '<input id=p4email style=width:230px maxlength=256 onchange=showCreateNewAccountDialogValidate() onkeyup=showCreateNewAccountDialogValidate() />');
|
||||
x += addHtmlValue('Password', '<input id=p4pass1 type=password style=width:230px maxlength=256 onchange=showCreateNewAccountDialogValidate() onkeyup=showCreateNewAccountDialogValidate() />');
|
||||
x += addHtmlValue('Password', '<input id=p4pass2 type=password style=width:230px maxlength=256 onchange=showCreateNewAccountDialogValidate() onkeyup=showCreateNewAccountDialogValidate() />');
|
||||
if (passRequirements) { var r = []; for (var i in passRequirements) { r.push(i + ':' + passRequirements[i]); } x += '<div style=font-size:x-small;padding:6px>Requirements: ' + r.join(', ') + '.</div>'; }
|
||||
setDialogMode(2, "Create Account", 3, showCreateNewAccountDialogEx, x);
|
||||
showCreateNewAccountDialogValidate();
|
||||
Q('p4name').focus();
|
||||
|
@ -6351,7 +6364,9 @@
|
|||
|
||||
function showCreateNewAccountDialogValidate(x) {
|
||||
if ((x == null) && (Q('p4email').value.length > 0) && (validateEmail(Q('p4email').value)) == false) { QE('idx_dlgOkButton', false); return; }
|
||||
QE('idx_dlgOkButton', (!Q('p4name') || ((Q('p4name').value.length > 0) && (Q('p4name').value.indexOf(' ') == -1))) && Q('p4pass1').value.length > 0 && Q('p4pass1').value == Q('p4pass2').value && checkPasswordRequirements(Q('p4pass1').value, passRequirements));
|
||||
var ok = (!Q('p4name') || ((Q('p4name').value.length > 0) && (Q('p4name').value.indexOf(' ') == -1))) && Q('p4pass1').value.length > 0 && Q('p4pass1').value == Q('p4pass2').value && checkPasswordRequirements(Q('p4pass1').value, passRequirements);
|
||||
if (ok && passRequirements) { if (checkPasswordRequirements(Q('p4pass1').value, passRequirements) == false) { ok = false; } }
|
||||
QE('idx_dlgOkButton', ok);
|
||||
}
|
||||
|
||||
function showCreateNewAccountDialogEx() {
|
||||
|
@ -6440,8 +6455,8 @@
|
|||
if (activeSessions == 0) { Q('MainUserImage').classList.add('gray'); }
|
||||
|
||||
// Server permissions
|
||||
var msg = '';
|
||||
if ((user.siteadmin != null) && ((user.siteadmin & 32) != 0) && (user.siteadmin != 0xFFFFFFFF)) { msg += "Locked account, "; }
|
||||
var msg = '', premsg = '';
|
||||
if ((user.siteadmin != null) && ((user.siteadmin & 32) != 0) && (user.siteadmin != 0xFFFFFFFF)) { premsg = '<img src="images/padlock12.png" height=12 width=8 title="Account is locked" style="margin-top:2px" /> '; msg += 'Locked account, '; }
|
||||
if ((user.siteadmin == null) || (user.siteadmin == 0) || (user.siteadmin == 32)) { msg += "No server rights"; } else if (user.siteadmin == 8) { msg += "Access to server files"; } else if (user.siteadmin == 0xFFFFFFFF) { msg += "Full administrator"; } else { msg += "Partial rights"; }
|
||||
|
||||
// Show user attributes
|
||||
|
@ -6449,7 +6464,7 @@
|
|||
var email = user.email?EscapeHtml(user.email):'<i>Not set</i>', everify = '';
|
||||
if (serverinfo.emailcheck) { everify = ((user.emailVerified == true)?'<b style=color:green;cursor:pointer title="Email is verified">🗸</b> ':'<b style=color:red;cursor:pointer title="Email not verified">🗴</b> '); }
|
||||
x += addDeviceAttribute('Email', everify + "<a style=cursor:pointer onclick=p30showUserEmailChangeDialog(event,\"" + userid + "\")>" + email + '</a> <a style=cursor:pointer onclick=doemail(event,\"' + user.email + '\")><img class=hoverButton width=10 height=10 src="images/link1.png" /></a>');
|
||||
x += addDeviceAttribute('Server Rights', "<a style=cursor:pointer onclick=showUserAdminDialog(event,\"" + userid + "\")>" + msg + "</a>");
|
||||
x += addDeviceAttribute('Server Rights', premsg + "<a style=cursor:pointer onclick=showUserAdminDialog(event,\"" + userid + "\")>" + msg + "</a>");
|
||||
if (user.quota) x += addDeviceAttribute('Server Quota', EscapeHtml(parseInt(user.quota) / 1024) + ' k');
|
||||
x += addDeviceAttribute('Creation', new Date(user.creation * 1000).toLocaleString());
|
||||
if (user.login) x += addDeviceAttribute('Last Login', new Date(user.login * 1000).toLocaleString());
|
||||
|
@ -6460,7 +6475,7 @@
|
|||
if (user.otpsecret > 0) { factors.push('Authentication App'); }
|
||||
if (user.otphkeys > 0) { factors.push('Security Key'); }
|
||||
if (user.otpkeys > 0) { factors.push('Backup Codes'); }
|
||||
x += addDeviceAttribute('Security', factors.join(', '));
|
||||
x += addDeviceAttribute('Security', '<img src="images/key12.png" height=12 width=11 title="2nd factor authentication enabled" style="margin-top:2px" /> ' + factors.join(', '));
|
||||
}
|
||||
|
||||
x += '</table></div><br />';
|
||||
|
@ -6534,7 +6549,8 @@
|
|||
x += addHtmlValue('Password', '<input id=p4pass1 type=password style=width:230px maxlength=256 onchange=showCreateNewAccountDialogValidate(1) onkeyup=showCreateNewAccountDialogValidate(1)></input>');
|
||||
x += addHtmlValue('Password', '<input id=p4pass2 type=password style=width:230px maxlength=256 onchange=showCreateNewAccountDialogValidate(1) onkeyup=showCreateNewAccountDialogValidate(1)></input>');
|
||||
x += addHtmlValue('Password hint', '<input id=p4hint type=text style=width:230px maxlength=256></input>');
|
||||
if (multiFactor == 1) { x += '<input id=p4twoFactorRemove type=checkbox />Remove all 2nd factor authentication.'; }
|
||||
if (passRequirements) { var r = []; for (var i in passRequirements) { r.push(i + ':' + passRequirements[i]); } x += '<div style=font-size:x-small;padding:6px>Requirements: ' + r.join(', ') + '.</div>'; }
|
||||
if (multiFactor == 1) { x += '<div><input id=p4twoFactorRemove type=checkbox />Remove all 2nd factor authentication.</div>'; }
|
||||
setDialogMode(2, "Change Password for " + EscapeHtml(currentUser.name), 3, p30showUserChangePassDialogEx, x, multiFactor);
|
||||
showCreateNewAccountDialogValidate(1);
|
||||
Q('p4pass1').focus();
|
||||
|
|
File diff suppressed because one or more lines are too long
|
@ -424,7 +424,7 @@
|
|||
ok = false;
|
||||
QS('nuPass1').color = '#7b241c';
|
||||
QS('nuPass2').color = '#7b241c';
|
||||
QH('passWarning', '<span style=color:red><b>Password Policy</b><span>'); // TODO: Display problem hint
|
||||
QH('passWarning', '<div style=color:red;cursor:pointer onclick=showPasswordPolicy()><b>Password Policy</b><div>'); // This is also a link to the password policy
|
||||
} else {
|
||||
QH('passWarning', '');
|
||||
}
|
||||
|
@ -442,6 +442,18 @@
|
|||
QE('createButton', ok);
|
||||
}
|
||||
|
||||
function showPasswordPolicy() {
|
||||
var policy = '<div style=text-align:left>';
|
||||
if (passRequirements.min) { policy += 'Minimum length: ' + passRequirements.min + '<br />'; }
|
||||
if (passRequirements.max) { policy += 'Maximum length: ' + passRequirements.max + '<br />'; }
|
||||
if (passRequirements.upper) { policy += 'Upper case: ' + passRequirements.upper + '<br />'; }
|
||||
if (passRequirements.lower) { policy += 'Lower case: ' + passRequirements.lower + '<br />'; }
|
||||
if (passRequirements.numeric) { policy += 'Numeric: ' + passRequirements.numeric + '<br />'; }
|
||||
if (passRequirements.nonalpha) { policy += 'Non-alphanumeric: ' + passRequirements.nonalpha + '<br />'; }
|
||||
policy += '</div>';
|
||||
messagebox("Password Policy", policy);
|
||||
}
|
||||
|
||||
function validateReset(e) {
|
||||
setDialogMode(0);
|
||||
var x = validateEmail(Q('remail').value);
|
||||
|
|
109
webserver.js
109
webserver.js
|
@ -421,12 +421,21 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) {
|
|||
if (checkUserOneTimePasswordRequired(domain, user)) {
|
||||
checkUserOneTimePassword(req, domain, user, req.body.token, req.body.hwtoken, function (result) {
|
||||
if (result == false) {
|
||||
var randomWaitTime = 0;
|
||||
|
||||
// 2-step auth is required, but the token is not present or not valid.
|
||||
if ((req.body.token != null) || (req.body.hwtoken != null)) { req.session.error = '<b style=color:#8C001A>Invalid token, try again.</b>'; }
|
||||
req.session.loginmode = '4';
|
||||
req.session.tokenusername = xusername;
|
||||
req.session.tokenpassword = xpassword;
|
||||
res.redirect(domain.url);
|
||||
if ((req.body.token != null) || (req.body.hwtoken != null)) {
|
||||
randomWaitTime = 2000 + (obj.crypto.randomBytes(2).readUInt16BE(0) % 4095); // This is a fail, wait a random time. 2 to 6 seconds.
|
||||
req.session.error = '<b style=color:#8C001A>Invalid token, try again.</b>';
|
||||
}
|
||||
|
||||
// Wait and redirect the user
|
||||
setTimeout(function () {
|
||||
req.session.loginmode = '4';
|
||||
req.session.tokenusername = xusername;
|
||||
req.session.tokenpassword = xpassword;
|
||||
res.redirect(domain.url);
|
||||
}, randomWaitTime);
|
||||
} else {
|
||||
// Login succesful
|
||||
completeLoginRequest(req, res, domain, user, userid);
|
||||
|
@ -438,15 +447,20 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) {
|
|||
// Login succesful
|
||||
completeLoginRequest(req, res, domain, user, userid);
|
||||
} else {
|
||||
//console.log('passhint', passhint);
|
||||
delete req.session.loginmode;
|
||||
if (err == 'locked') { req.session.error = '<b style=color:#8C001A>Account locked.</b>'; } else { req.session.error = '<b style=color:#8C001A>Login failed, check username and password.</b>'; }
|
||||
if ((passhint != null) && (passhint.length > 0)) {
|
||||
req.session.passhint = passhint;
|
||||
} else {
|
||||
delete req.session.passhint;
|
||||
}
|
||||
res.redirect(domain.url);
|
||||
// Login failed, wait a random delay
|
||||
setTimeout(function () {
|
||||
// If the account is locked, display that.
|
||||
if (err == 'locked') { req.session.error = '<b style=color:#8C001A>Account locked.</b>'; } else { req.session.error = '<b style=color:#8C001A>Login failed, check username and password.</b>'; }
|
||||
|
||||
// Clean up login mode and display password hint if present.
|
||||
delete req.session.loginmode;
|
||||
if ((passhint != null) && (passhint.length > 0)) {
|
||||
req.session.passhint = passhint;
|
||||
} else {
|
||||
delete req.session.passhint;
|
||||
}
|
||||
res.redirect(domain.url);
|
||||
}, 2000 + (obj.crypto.randomBytes(2).readUInt16BE(0) % 4095)); // Wait for 2 to ~6 seconds.
|
||||
}
|
||||
});
|
||||
}
|
||||
|
@ -547,6 +561,7 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) {
|
|||
if (err) throw err;
|
||||
user.salt = salt;
|
||||
user.hash = hash;
|
||||
delete user.passtype;
|
||||
obj.db.SetUser(user);
|
||||
|
||||
// Send the verification email
|
||||
|
@ -688,6 +703,7 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) {
|
|||
userinfo = obj.users[user._id];
|
||||
userinfo.salt = salt;
|
||||
userinfo.hash = hash;
|
||||
delete userinfo.passtype;
|
||||
userinfo.passchange = Math.floor(Date.now() / 1000);
|
||||
userinfo.passhint = null;
|
||||
//delete userinfo.otpsecret; // Currently a email password reset will turn off 2-step login.
|
||||
|
@ -758,28 +774,63 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) {
|
|||
});
|
||||
}
|
||||
|
||||
// Check a user's password
|
||||
obj.checkUserPassword = function(domain, user, password, func) {
|
||||
// Check the old password
|
||||
if (user.passtype != null) {
|
||||
// IIS default clear or weak password hashing (SHA-1)
|
||||
require('./pass').iishash(user.passtype, password, user.salt, function (err, hash) {
|
||||
if (err) return func(false);
|
||||
if (hash == user.hash) {
|
||||
if ((user.siteadmin) && (user.siteadmin != 0xFFFFFFFF) && (user.siteadmin & 32) != 0) { return func(false); } // Account is locked
|
||||
return func(true); // Allow password change
|
||||
}
|
||||
func(false);
|
||||
});
|
||||
} else {
|
||||
// Default strong password hashing (pbkdf2 SHA384)
|
||||
require('./pass').hash(password, user.salt, function (err, hash) {
|
||||
if (err) return func(false);
|
||||
if (hash == user.hash) {
|
||||
if ((user.siteadmin) && (user.siteadmin != 0xFFFFFFFF) && (user.siteadmin & 32) != 0) { return func(false); } // Account is locked
|
||||
return func(true); // Allow password change
|
||||
}
|
||||
func(false);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// Handle password changes
|
||||
function handlePasswordChangeRequest(req, res) {
|
||||
const domain = checkUserIpAddress(req, res);
|
||||
if ((domain == null) || (domain.auth == 'sspi')) return;
|
||||
|
||||
// Check if the user is logged and we have all required parameters
|
||||
if (!req.session || !req.session.userid || !req.body.apassword1 || (req.body.apassword1 != req.body.apassword2) || (req.session.domainid != domain.id)) { res.redirect(domain.url); return; }
|
||||
if (!req.session || !req.session.userid || !req.body.apassword0 || !req.body.apassword1 || (req.body.apassword1 != req.body.apassword2) || (req.session.domainid != domain.id)) { res.redirect(domain.url); return; }
|
||||
|
||||
// Update the password
|
||||
require('./pass').hash(req.body.apassword1, function (err, salt, hash) {
|
||||
if (err) throw err;
|
||||
var hint = req.body.apasswordhint;
|
||||
if (hint.length > 250) hint = hint.substring(0, 250);
|
||||
var user = obj.users[req.session.userid];
|
||||
user.salt = salt;
|
||||
user.hash = hash;
|
||||
user.passchange = Math.floor(Date.now() / 1000);
|
||||
user.passhint = req.body.apasswordhint;
|
||||
obj.db.SetUser(user);
|
||||
req.session.viewmode = 2;
|
||||
res.redirect(domain.url);
|
||||
obj.parent.DispatchEvent(['*', 'server-users'], obj, { etype: 'user', username: user.name, action: 'passchange', msg: 'Account password changed: ' + user.name, domain: domain.id });
|
||||
// Get the current user
|
||||
var user = obj.users[req.session.userid];
|
||||
if (!user) { res.redirect(domain.url); return; }
|
||||
|
||||
// Check old password
|
||||
obj.checkUserPassword(domain, user, req.body.apassword0, function (result) {
|
||||
if (result == true) {
|
||||
// Update the password
|
||||
require('./pass').hash(req.body.apassword1, function (err, salt, hash) {
|
||||
if (err) throw err;
|
||||
var hint = req.body.apasswordhint;
|
||||
if (hint.length > 250) hint = hint.substring(0, 250);
|
||||
user.salt = salt;
|
||||
user.hash = hash;
|
||||
user.passhint = req.body.apasswordhint;
|
||||
user.passchange = Math.floor(Date.now() / 1000);
|
||||
delete user.passtype;
|
||||
obj.db.SetUser(user);
|
||||
req.session.viewmode = 2;
|
||||
res.redirect(domain.url);
|
||||
obj.parent.DispatchEvent(['*', 'server-users'], obj, { etype: 'user', username: user.name, action: 'passchange', msg: 'Account password changed: ' + user.name, domain: domain.id });
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue