Improved server tracing.

This commit is contained in:
Ylian Saint-Hilaire 2019-08-23 11:51:48 -07:00
parent 489666d9b8
commit 3aebee8f5e
3 changed files with 91 additions and 28 deletions

View File

@ -45,7 +45,7 @@ function CreateMeshCentralServer(config, args) {
obj.connectivityByNode = {}; // This object keeps a list of all connected CIRA and agents, by nodeid->value (value: 1 = Agent, 2 = CIRA, 4 = AmtDirect) obj.connectivityByNode = {}; // This object keeps a list of all connected CIRA and agents, by nodeid->value (value: 1 = Agent, 2 = CIRA, 4 = AmtDirect)
obj.peerConnectivityByNode = {}; // This object keeps a list of all connected CIRA and agents of peers, by serverid->nodeid->value (value: 1 = Agent, 2 = CIRA, 4 = AmtDirect) obj.peerConnectivityByNode = {}; // This object keeps a list of all connected CIRA and agents of peers, by serverid->nodeid->value (value: 1 = Agent, 2 = CIRA, 4 = AmtDirect)
obj.debugSources = []; obj.debugSources = [];
obj.debugRemoteSources = []; obj.debugRemoteSources = null;
obj.config = config; // Configuration file obj.config = config; // Configuration file
obj.dbconfig = {}; // Persistance values, loaded from database obj.dbconfig = {}; // Persistance values, loaded from database
obj.certificateOperations = null; obj.certificateOperations = null;
@ -109,7 +109,7 @@ function CreateMeshCentralServer(config, args) {
try { require('./pass').hash('test', function () { }, 0); } catch (e) { console.log('Old version of node, must upgrade.'); return; } // TODO: Not sure if this test works or not. try { require('./pass').hash('test', function () { }, 0); } catch (e) { console.log('Old version of node, must upgrade.'); return; } // TODO: Not sure if this test works or not.
// Check for invalid arguments // Check for invalid arguments
var validArguments = ['_', 'notls', 'user', 'port', 'aliasport', 'mpsport', 'mpsaliasport', 'redirport', 'cert', 'mpscert', 'deletedomain', 'deletedefaultdomain', 'showall', 'showusers', 'shownodes', 'showmeshes', 'showevents', 'showpower', 'clearpower', 'showiplocations', 'help', 'exactports', 'install', 'uninstall', 'start', 'stop', 'restart', 'debug', 'remotedebug', 'filespath', 'datapath', 'noagentupdate', 'launch', 'noserverbackup', 'mongodb', 'mongodbcol', 'wanonly', 'lanonly', 'nousers', 'mpspass', 'ciralocalfqdn', 'dbexport', 'dbexportmin', 'dbimport', 'dbmerge', 'dbencryptkey', 'selfupdate', 'tlsoffload', 'userallowedip', 'userblockedip', 'swarmallowedip', 'agentallowedip', 'agentblockedip', 'fastcert', 'swarmport', 'logintoken', 'logintokenkey', 'logintokengen', 'logintokengen', 'mailtokengen', 'admin', 'unadmin', 'sessionkey', 'sessiontime', 'minify', 'minifycore', 'dblistconfigfiles', 'dbshowconfigfile', 'dbpushconfigfiles', 'dbpullconfigfiles', 'dbdeleteconfigfiles', 'configkey', 'loadconfigfromdb', 'npmpath', 'memorytracking', 'serverid']; var validArguments = ['_', 'notls', 'user', 'port', 'aliasport', 'mpsport', 'mpsaliasport', 'redirport', 'cert', 'mpscert', 'deletedomain', 'deletedefaultdomain', 'showall', 'showusers', 'shownodes', 'showmeshes', 'showevents', 'showpower', 'clearpower', 'showiplocations', 'help', 'exactports', 'install', 'uninstall', 'start', 'stop', 'restart', 'debug', 'filespath', 'datapath', 'noagentupdate', 'launch', 'noserverbackup', 'mongodb', 'mongodbcol', 'wanonly', 'lanonly', 'nousers', 'mpspass', 'ciralocalfqdn', 'dbexport', 'dbexportmin', 'dbimport', 'dbmerge', 'dbencryptkey', 'selfupdate', 'tlsoffload', 'userallowedip', 'userblockedip', 'swarmallowedip', 'agentallowedip', 'agentblockedip', 'fastcert', 'swarmport', 'logintoken', 'logintokenkey', 'logintokengen', 'logintokengen', 'mailtokengen', 'admin', 'unadmin', 'sessionkey', 'sessiontime', 'minify', 'minifycore', 'dblistconfigfiles', 'dbshowconfigfile', 'dbpushconfigfiles', 'dbpullconfigfiles', 'dbdeleteconfigfiles', 'configkey', 'loadconfigfromdb', 'npmpath', 'memorytracking', 'serverid'];
for (var arg in obj.args) { obj.args[arg.toLocaleLowerCase()] = obj.args[arg]; if (validArguments.indexOf(arg.toLocaleLowerCase()) == -1) { console.log('Invalid argument "' + arg + '", use --help.'); return; } } for (var arg in obj.args) { obj.args[arg.toLocaleLowerCase()] = obj.args[arg]; if (validArguments.indexOf(arg.toLocaleLowerCase()) == -1) { console.log('Invalid argument "' + arg + '", use --help.'); return; } }
if (obj.args.mongodb == true) { console.log('Must specify: --mongodb [connectionstring] \r\nSee https://docs.mongodb.com/manual/reference/connection-string/ for MongoDB connection string.'); return; } if (obj.args.mongodb == true) { console.log('Must specify: --mongodb [connectionstring] \r\nSee https://docs.mongodb.com/manual/reference/connection-string/ for MongoDB connection string.'); return; }
for (i in obj.config.settings) { obj.args[i] = obj.config.settings[i]; } // Place all settings into arguments, arguments have already been placed into settings so arguments take precedence. for (i in obj.config.settings) { obj.args[i] = obj.config.settings[i]; } // Place all settings into arguments, arguments have already been placed into settings so arguments take precedence.
@ -264,11 +264,6 @@ function CreateMeshCentralServer(config, args) {
else if (typeof obj.args.debug == 'object') { obj.debugSources = obj.args.debug; } else if (typeof obj.args.debug == 'object') { obj.debugSources = obj.args.debug; }
else if (obj.args.debug === true) { obj.debugSources = '*'; } else if (obj.args.debug === true) { obj.debugSources = '*'; }
// Remote web application tracing
if (typeof obj.args.remotedebug == 'string') { obj.debugRemoteSources = obj.args.remotedebug.toLowerCase().split(','); }
else if (typeof obj.args.remotedebug == 'object') { obj.debugRemoteSources = obj.args.remotedebug; }
else if (obj.args.remotedebug === true) { obj.debugRemoteSources = '*'; }
require('./db.js').CreateDB(obj, require('./db.js').CreateDB(obj,
function (db) { function (db) {
obj.db = db; obj.db = db;
@ -1574,8 +1569,10 @@ function CreateMeshCentralServer(config, args) {
o.time = Math.floor(Date.now() / 1000); // Add the cookie creation time o.time = Math.floor(Date.now() / 1000); // Add the cookie creation time
const iv = Buffer.from(obj.crypto.randomBytes(12), 'binary'), cipher = obj.crypto.createCipheriv('aes-256-gcm', key.slice(0, 32), iv); const iv = Buffer.from(obj.crypto.randomBytes(12), 'binary'), cipher = obj.crypto.createCipheriv('aes-256-gcm', key.slice(0, 32), iv);
const crypted = Buffer.concat([cipher.update(JSON.stringify(o), 'utf8'), cipher.final()]); const crypted = Buffer.concat([cipher.update(JSON.stringify(o), 'utf8'), cipher.final()]);
return Buffer.concat([iv, cipher.getAuthTag(), crypted]).toString('base64').replace(/\+/g, '@').replace(/\//g, '$'); var r = Buffer.concat([iv, cipher.getAuthTag(), crypted]).toString('base64').replace(/\+/g, '@').replace(/\//g, '$');
} catch (e) { return null; } obj.debug('cookie', 'Encoded AESGCM cookie: ' + JSON.stringify(o));
return r;
} catch (ex) { obj.debug('cookie', 'ERR: Failed to encode AESGCM cookie due to exception: ' + ex); return null; }
}; };
// Decode a cookie back into an object using a key using AES256-GCM or AES128-CBC/HMAC-SHA386. Return null if it's not a valid cookie. (key must be 32 bytes or more) // Decode a cookie back into an object using a key using AES256-GCM or AES128-CBC/HMAC-SHA386. Return null if it's not a valid cookie. (key must be 32 bytes or more)
@ -1604,6 +1601,7 @@ function CreateMeshCentralServer(config, args) {
// An expire time is included in the cookie (in minutes), use this. // An expire time is included in the cookie (in minutes), use this.
if ((o.expire !== 0) && ((o.dtime > (o.expire * 60000)) || (o.dtime < -30000))) { obj.debug('cookie', 'ERR: Bad cookie due to timeout'); return null; } // The cookie is only valid 120 seconds, or 30 seconds back in time (in case other server's clock is not quite right) if ((o.expire !== 0) && ((o.dtime > (o.expire * 60000)) || (o.dtime < -30000))) { obj.debug('cookie', 'ERR: Bad cookie due to timeout'); return null; } // The cookie is only valid 120 seconds, or 30 seconds back in time (in case other server's clock is not quite right)
} }
obj.debug('cookie', 'Decoded AESGCM cookie: ' + JSON.stringify(o));
return o; return o;
} catch (ex) { obj.debug('cookie', 'ERR: Bad AESGCM cookie due to exception: ' + ex); return null; } } catch (ex) { obj.debug('cookie', 'ERR: Bad AESGCM cookie due to exception: ' + ex); return null; }
}; };
@ -1632,6 +1630,7 @@ function CreateMeshCentralServer(config, args) {
// An expire time is included in the cookie (in minutes), use this. // An expire time is included in the cookie (in minutes), use this.
if ((o.expire !== 0) && ((o.dtime > (o.expire * 60000)) || (o.dtime < -30000))) { obj.debug('cookie', 'ERR: Bad cookie due to timeout'); return null; } // The cookie is only valid 120 seconds, or 30 seconds back in time (in case other server's clock is not quite right) if ((o.expire !== 0) && ((o.dtime > (o.expire * 60000)) || (o.dtime < -30000))) { obj.debug('cookie', 'ERR: Bad cookie due to timeout'); return null; } // The cookie is only valid 120 seconds, or 30 seconds back in time (in case other server's clock is not quite right)
} }
obj.debug('cookie', 'Decoded AESSHA cookie: ' + JSON.stringify(o));
return o; return o;
} catch (ex) { obj.debug('cookie', 'ERR: Bad AESSHA cookie due to exception: ' + ex); return null; } } catch (ex) { obj.debug('cookie', 'ERR: Bad AESSHA cookie due to exception: ' + ex); return null; }
}; };
@ -1643,15 +1642,17 @@ function CreateMeshCentralServer(config, args) {
// Send the event to logged in administrators // Send the event to logged in administrators
if ((obj.debugRemoteSources != null) && ((obj.debugRemoteSources == '*') || (obj.debugRemoteSources.indexOf(source) >= 0))) { if ((obj.debugRemoteSources != null) && ((obj.debugRemoteSources == '*') || (obj.debugRemoteSources.indexOf(source) >= 0))) {
var sendcount = 0;
for (var sessionid in obj.webserver.wssessions2) { for (var sessionid in obj.webserver.wssessions2) {
var ws = obj.webserver.wssessions2[sessionid]; var ws = obj.webserver.wssessions2[sessionid];
if ((ws != null) && (ws.userid != null)) { if ((ws != null) && (ws.userid != null)) {
var user = obj.webserver.users[ws.userid]; var user = obj.webserver.users[ws.userid];
if ((user != null) && (user.siteadmin == 4294967295)) { if ((user != null) && (user.siteadmin == 4294967295)) {
try { ws.send(JSON.stringify({ action: 'trace', source: source, args: args, time: Date.now() })); } catch (ex) { } try { ws.send(JSON.stringify({ action: 'trace', source: source, args: args, time: Date.now() })); sendcount++; } catch (ex) { }
} }
} }
} }
if (sendcount == 0) { obj.debugRemoteSources = null; } // If there are no listeners, remove debug sources.
} }
}; };

View File

@ -1391,10 +1391,11 @@
} }
case 'traceinfo': { case 'traceinfo': {
if (typeof message.traceSources == 'object') { if (typeof message.traceSources == 'object') {
serverTraceSources = message.traceSources; if ((message.traceSources != null) && (message.traceSources.length > 0)) {
if (message.traceSources.length > 0) { serverTraceSources = message.traceSources;
QH('p41traceStatus', EscapeHtml(message.traceSources.join(', '))); QH('p41traceStatus', EscapeHtml(message.traceSources.join(', ')));
} else { } else {
serverTraceSources = [];
QH('p41traceStatus', 'None'); QH('p41traceStatus', 'None');
} }
} }
@ -2119,10 +2120,11 @@
} }
case 'traceinfo': { case 'traceinfo': {
if (typeof message.event.traceSources == 'object') { if (typeof message.event.traceSources == 'object') {
serverTraceSources = message.event.traceSources; if ((message.event.traceSources != null) && (message.event.traceSources.length > 0)) {
if (message.event.traceSources.length > 0) { serverTraceSources = message.event.traceSources;
QH('p41traceStatus', EscapeHtml(message.event.traceSources.join(', '))); QH('p41traceStatus', EscapeHtml(message.event.traceSources.join(', ')));
} else { } else {
serverTraceSources = [];
QH('p41traceStatus', 'None'); QH('p41traceStatus', 'None');
} }
} }
@ -4878,7 +4880,7 @@
case 2: case 2:
break; break;
case 3: case 3:
if (desktop.serverIsRecording == true) { QV('deskRecordIcon', true); } if (desktop && (desktop.serverIsRecording == true)) { QV('deskRecordIcon', true); }
break; break;
default: default:
//console.log('Unknown onDesktopStateChange state', state); //console.log('Unknown onDesktopStateChange state', state);
@ -5444,7 +5446,7 @@
break; break;
case 3: case 3:
QE('termSizeList', false); QE('termSizeList', false);
if (xterminal.serverIsRecording == true) { QV('termRecordIcon', true); } if (xterminal && (xterminal.serverIsRecording == true)) { QV('termRecordIcon', true); }
break; break;
default: default:
QE('termSizeList', false); QE('termSizeList', false);
@ -5596,8 +5598,10 @@
break; break;
case 3: case 3:
p13targetpath = ''; p13targetpath = '';
files.sendText({ action: 'ls', reqid: 1, path: '' }); if (files) {
if (files.serverIsRecording == true) { QV('filesRecordIcon', true); } files.sendText({ action: 'ls', reqid: 1, path: '' });
if (files.serverIsRecording == true) { QV('filesRecordIcon', true); }
}
break; break;
default: default:
//console.log('Unknown onFilesStateChange state', state); //console.log('Unknown onFilesStateChange state', state);

View File

@ -489,7 +489,11 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) {
function handleLogoutRequest(req, res) { function handleLogoutRequest(req, res) {
const domain = checkUserIpAddress(req, res); const domain = checkUserIpAddress(req, res);
if ((domain == null) || (domain.auth == 'sspi')) { res.sendStatus(404); return; } if ((domain == null) || (domain.auth == 'sspi')) {
parent.debug('web', 'handleLogoutRequest: failed checks.');
res.sendStatus(404);
return;
}
res.set({ 'Cache-Control': 'no-cache, no-store, must-revalidate', 'Pragma': 'no-cache', 'Expires': '0' }); res.set({ 'Cache-Control': 'no-cache, no-store, must-revalidate', 'Pragma': 'no-cache', 'Expires': '0' });
// Destroy the user's session to log them out will be re-created next request // Destroy the user's session to log them out will be re-created next request
@ -499,6 +503,7 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) {
} }
req.session = null; req.session = null;
res.redirect(domain.url); res.redirect(domain.url);
parent.debug('web', 'handleLogoutRequest: success.');
} }
// Return true if this user has 2-step auth active // Return true if this user has 2-step auth active
@ -619,7 +624,7 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) {
function handleLoginRequest(req, res) { function handleLoginRequest(req, res) {
const domain = checkUserIpAddress(req, res); const domain = checkUserIpAddress(req, res);
if (domain == null) { res.sendStatus(404); return; } if (domain == null) { parent.debug('web', 'handleLoginRequest: invalid domain'); res.sendStatus(404); return; }
// Normally, use the body username/password. If this is a token, use the username/password in the session. // Normally, use the body username/password. If this is a token, use the username/password in the session.
var xusername = req.body.username, xpassword = req.body.password; var xusername = req.body.username, xpassword = req.body.password;
@ -640,6 +645,9 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) {
if ((req.body.token != null) || (req.body.hwtoken != null)) { 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. 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>'; req.session.error = '<b style=color:#8C001A>Invalid token, try again.</b>';
parent.debug('web', 'handleLoginRequest: invalid 2FA token');
} else {
parent.debug('web', 'handleLoginRequest: 2FA token required');
} }
// Wait and redirect the user // Wait and redirect the user
@ -651,6 +659,7 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) {
}, randomWaitTime); }, randomWaitTime);
} else { } else {
// Login succesful // Login succesful
parent.debug('web', 'handleLoginRequest: succesful 2FA login');
completeLoginRequest(req, res, domain, user, userid, xusername, xpassword); completeLoginRequest(req, res, domain, user, userid, xusername, xpassword);
} }
}); });
@ -658,12 +667,19 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) {
} }
// Login succesful // Login succesful
parent.debug('web', 'handleLoginRequest: succesful login');
completeLoginRequest(req, res, domain, user, userid, xusername, xpassword); completeLoginRequest(req, res, domain, user, userid, xusername, xpassword);
} else { } else {
// Login failed, wait a random delay // Login failed, wait a random delay
setTimeout(function () { setTimeout(function () {
// If the account is locked, display that. // 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>'; } if (err == 'locked') {
parent.debug('web', 'handleLoginRequest: login failed, locked account');
req.session.error = '<b style=color:#8C001A>Account locked.</b>';
} else {
parent.debug('web', 'handleLoginRequest: login failed, bad username and password');
req.session.error = '<b style=color:#8C001A>Login failed, check username and password.</b>';
}
// Clean up login mode and display password hint if present. // Clean up login mode and display password hint if present.
delete req.session.loginmode; delete req.session.loginmode;
@ -682,6 +698,7 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) {
// Check if we need to change the password // Check if we need to change the password
if ((typeof user.passchange == 'number') && ((user.passchange == -1) || ((typeof domain.passwordrequirements == 'object') && (typeof domain.passwordrequirements.reset == 'number') && (user.passchange + (domain.passwordrequirements.reset * 86400) < Math.floor(Date.now() / 1000))))) { if ((typeof user.passchange == 'number') && ((user.passchange == -1) || ((typeof domain.passwordrequirements == 'object') && (typeof domain.passwordrequirements.reset == 'number') && (user.passchange + (domain.passwordrequirements.reset * 86400) < Math.floor(Date.now() / 1000))))) {
// Request a password change // Request a password change
parent.debug('web', 'handleLoginRequest: login ok, password change requested');
req.session.loginmode = '6'; req.session.loginmode = '6';
req.session.error = '<b style=color:#8C001A>Password change requested.</b>'; req.session.error = '<b style=color:#8C001A>Password change requested.</b>';
req.session.resettokenusername = xusername; req.session.resettokenusername = xusername;
@ -729,8 +746,10 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) {
res.redirect(domain.url + getQueryPortion(req)); res.redirect(domain.url + getQueryPortion(req));
}); });
*/ */
parent.debug('web', 'handleLoginRequest: login ok (1)');
res.redirect(domain.url + getQueryPortion(req)); // Temporary res.redirect(domain.url + getQueryPortion(req)); // Temporary
} else { } else {
parent.debug('web', 'handleLoginRequest: login ok (2)');
res.redirect(domain.url + getQueryPortion(req)); res.redirect(domain.url + getQueryPortion(req));
} }
//}); //});
@ -759,6 +778,7 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) {
var i = -1; var i = -1;
if (typeof req.body.email == 'string') { i = req.body.email.indexOf('@'); } if (typeof req.body.email == 'string') { i = req.body.email.indexOf('@'); }
if (i == -1) { if (i == -1) {
parent.debug('web', 'handleCreateAccountRequest: unable to create account (1)');
req.session.loginmode = '2'; req.session.loginmode = '2';
req.session.error = '<b style=color:#8C001A>Unable to create account.</b>'; req.session.error = '<b style=color:#8C001A>Unable to create account.</b>';
res.redirect(domain.url + getQueryPortion(req)); res.redirect(domain.url + getQueryPortion(req));
@ -767,6 +787,7 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) {
var emailok = false, emaildomain = req.body.email.substring(i + 1).toLowerCase(); var emailok = false, emaildomain = req.body.email.substring(i + 1).toLowerCase();
for (var i in domain.newaccountemaildomains) { if (emaildomain == domain.newaccountemaildomains[i].toLowerCase()) { emailok = true; } } for (var i in domain.newaccountemaildomains) { if (emaildomain == domain.newaccountemaildomains[i].toLowerCase()) { emailok = true; } }
if (emailok == false) { if (emailok == false) {
parent.debug('web', 'handleCreateAccountRequest: unable to create account (2)');
req.session.loginmode = '2'; req.session.loginmode = '2';
req.session.error = '<b style=color:#8C001A>Unable to create account.</b>'; req.session.error = '<b style=color:#8C001A>Unable to create account.</b>';
res.redirect(domain.url + getQueryPortion(req)); res.redirect(domain.url + getQueryPortion(req));
@ -777,12 +798,14 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) {
// Check if we exceed the maximum number of user accounts // Check if we exceed the maximum number of user accounts
obj.db.isMaxType(domain.limits.maxuseraccounts, 'user', domain.id, function (maxExceed) { obj.db.isMaxType(domain.limits.maxuseraccounts, 'user', domain.id, function (maxExceed) {
if (maxExceed) { if (maxExceed) {
parent.debug('web', 'handleCreateAccountRequest: account limit reached');
req.session.loginmode = '2'; req.session.loginmode = '2';
req.session.error = '<b style=color:#8C001A>Account limit reached.</b>'; req.session.error = '<b style=color:#8C001A>Account limit reached.</b>';
console.log('max', req.session); console.log('max', req.session);
res.redirect(domain.url + getQueryPortion(req)); res.redirect(domain.url + getQueryPortion(req));
} else { } else {
if (!obj.common.validateUsername(req.body.username, 1, 64) || !obj.common.validateEmail(req.body.email, 1, 256) || !obj.common.validateString(req.body.password1, 1, 256) || !obj.common.validateString(req.body.password2, 1, 256) || (req.body.password1 != req.body.password2) || req.body.username == '~' || !obj.common.checkPasswordRequirements(req.body.password1, domain.passwordrequirements)) { if (!obj.common.validateUsername(req.body.username, 1, 64) || !obj.common.validateEmail(req.body.email, 1, 256) || !obj.common.validateString(req.body.password1, 1, 256) || !obj.common.validateString(req.body.password2, 1, 256) || (req.body.password1 != req.body.password2) || req.body.username == '~' || !obj.common.checkPasswordRequirements(req.body.password1, domain.passwordrequirements)) {
parent.debug('web', 'handleCreateAccountRequest: unable to create account (3)');
req.session.loginmode = '2'; req.session.loginmode = '2';
req.session.error = '<b style=color:#8C001A>Unable to create account.</b>'; req.session.error = '<b style=color:#8C001A>Unable to create account.</b>';
res.redirect(domain.url + getQueryPortion(req)); res.redirect(domain.url + getQueryPortion(req));
@ -790,12 +813,14 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) {
// Check if this email was already verified // Check if this email was already verified
obj.db.GetUserWithVerifiedEmail(domain.id, req.body.email, function (err, docs) { obj.db.GetUserWithVerifiedEmail(domain.id, req.body.email, function (err, docs) {
if (docs.length > 0) { if (docs.length > 0) {
parent.debug('web', 'handleCreateAccountRequest: Existing account with this email address');
req.session.loginmode = '2'; req.session.loginmode = '2';
req.session.error = '<b style=color:#8C001A>Existing account with this email address.</b>'; req.session.error = '<b style=color:#8C001A>Existing account with this email address.</b>';
res.redirect(domain.url + getQueryPortion(req)); res.redirect(domain.url + getQueryPortion(req));
} else { } else {
// Check if there is domain.newAccountToken, check if supplied token is valid // Check if there is domain.newAccountToken, check if supplied token is valid
if ((domain.newaccountspass != null) && (domain.newaccountspass != '') && (req.body.anewaccountpass != domain.newaccountspass)) { if ((domain.newaccountspass != null) && (domain.newaccountspass != '') && (req.body.anewaccountpass != domain.newaccountspass)) {
parent.debug('web', 'handleCreateAccountRequest: Invalid account creation token');
req.session.loginmode = '2'; req.session.loginmode = '2';
req.session.error = '<b style=color:#8C001A>Invalid account creation token.</b>'; req.session.error = '<b style=color:#8C001A>Invalid account creation token.</b>';
res.redirect(domain.url + getQueryPortion(req)); res.redirect(domain.url + getQueryPortion(req));
@ -803,6 +828,7 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) {
} }
// Check if user exists // Check if user exists
if (obj.users['user/' + domain.id + '/' + req.body.username.toLowerCase()]) { if (obj.users['user/' + domain.id + '/' + req.body.username.toLowerCase()]) {
parent.debug('web', 'handleCreateAccountRequest: Username already exists');
req.session.loginmode = '2'; req.session.loginmode = '2';
req.session.error = '<b style=color:#8C001A>Username already exists.</b>'; req.session.error = '<b style=color:#8C001A>Username already exists.</b>';
} else { } else {
@ -842,6 +868,7 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) {
// Check everything is ok // Check everything is ok
if ((domain == null) || (domain.auth == 'sspi') || (domain.auth == 'ldap') || (typeof req.body.rpassword1 != 'string') || (typeof req.body.rpassword2 != 'string') || (req.body.rpassword1 != req.body.rpassword2) || (typeof req.body.rpasswordhint != 'string') || (req.session == null) || (typeof req.session.resettokenusername != 'string') || (typeof req.session.resettokenpassword != 'string')) { if ((domain == null) || (domain.auth == 'sspi') || (domain.auth == 'ldap') || (typeof req.body.rpassword1 != 'string') || (typeof req.body.rpassword2 != 'string') || (req.body.rpassword1 != req.body.rpassword2) || (typeof req.body.rpasswordhint != 'string') || (req.session == null) || (typeof req.session.resettokenusername != 'string') || (typeof req.session.resettokenpassword != 'string')) {
parent.debug('web', 'handleResetPasswordRequest: checks failed');
delete req.session.loginmode; delete req.session.loginmode;
delete req.session.tokenusername; delete req.session.tokenusername;
delete req.session.tokenpassword; delete req.session.tokenpassword;
@ -863,6 +890,7 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) {
// If we have password requirements, check this here. // If we have password requirements, check this here.
if (!obj.common.checkPasswordRequirements(req.body.rpassword1, domain.passwordrequirements)) { if (!obj.common.checkPasswordRequirements(req.body.rpassword1, domain.passwordrequirements)) {
parent.debug('web', 'handleResetPasswordRequest: password rejected, use a different one (1)');
req.session.loginmode = '6'; req.session.loginmode = '6';
req.session.error = '<b style=color:#8C001A>Password rejected, use a different one.</b>'; req.session.error = '<b style=color:#8C001A>Password rejected, use a different one.</b>';
res.redirect(domain.url + getQueryPortion(req)); res.redirect(domain.url + getQueryPortion(req));
@ -873,6 +901,7 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) {
require('./pass').hash(req.body.rpassword1, user.salt, function (err, hash, tag) { require('./pass').hash(req.body.rpassword1, user.salt, function (err, hash, tag) {
if (user.hash == hash) { if (user.hash == hash) {
// This is the same password, request a password change again // This is the same password, request a password change again
parent.debug('web', 'handleResetPasswordRequest: password rejected, use a different one (2)');
req.session.loginmode = '6'; req.session.loginmode = '6';
req.session.error = '<b style=color:#8C001A>Password rejected, use a different one.</b>'; req.session.error = '<b style=color:#8C001A>Password rejected, use a different one.</b>';
res.redirect(domain.url + getQueryPortion(req)); res.redirect(domain.url + getQueryPortion(req));
@ -891,6 +920,7 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) {
obj.parent.DispatchEvent(['*', 'server-users', user._id], obj, event); obj.parent.DispatchEvent(['*', 'server-users', user._id], obj, event);
// Login succesful // Login succesful
parent.debug('web', 'handleResetPasswordRequest: success');
req.session.userid = userid; req.session.userid = userid;
req.session.domainid = domain.id; req.session.domainid = domain.id;
completeLoginRequest(req, res, domain, obj.users[userid], userid, req.session.tokenusername, req.session.tokenpassword); completeLoginRequest(req, res, domain, obj.users[userid], userid, req.session.tokenusername, req.session.tokenpassword);
@ -899,6 +929,7 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) {
}, 0); }, 0);
} else { } else {
// Failed, error out. // Failed, error out.
parent.debug('web', 'handleResetPasswordRequest: failed authenticate()');
delete req.session.loginmode; delete req.session.loginmode;
delete req.session.tokenusername; delete req.session.tokenusername;
delete req.session.tokenpassword; delete req.session.tokenpassword;
@ -917,7 +948,11 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) {
// Called to process an account reset request // Called to process an account reset request
function handleResetAccountRequest(req, res) { function handleResetAccountRequest(req, res) {
const domain = checkUserIpAddress(req, res); const domain = checkUserIpAddress(req, res);
if ((domain == null) || (domain.auth == 'sspi') || (domain.auth == 'ldap') || (obj.args.lanonly == true) || (obj.parent.certificates.CommonName == null) || (obj.parent.certificates.CommonName.indexOf('.') == -1)) { res.sendStatus(404); return; } if ((domain == null) || (domain.auth == 'sspi') || (domain.auth == 'ldap') || (obj.args.lanonly == true) || (obj.parent.certificates.CommonName == null) || (obj.parent.certificates.CommonName.indexOf('.') == -1)) {
parent.debug('web', 'handleResetAccountRequest: check failed');
res.sendStatus(404);
return;
}
// Always lowercase the email address // Always lowercase the email address
if (req.body.email) { req.body.email = req.body.email.toLowerCase(); } if (req.body.email) { req.body.email = req.body.email.toLowerCase(); }
@ -928,12 +963,14 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) {
// Check the email string format // Check the email string format
if (!email || checkEmail(email) == false) { if (!email || checkEmail(email) == false) {
parent.debug('web', 'handleResetAccountRequest: Invalid email');
req.session.loginmode = '3'; req.session.loginmode = '3';
req.session.error = '<b style=color:#8C001A>Invalid email.</b>'; req.session.error = '<b style=color:#8C001A>Invalid email.</b>';
res.redirect(domain.url + getQueryPortion(req)); res.redirect(domain.url + getQueryPortion(req));
} else { } else {
obj.db.GetUserWithVerifiedEmail(domain.id, email, function (err, docs) { obj.db.GetUserWithVerifiedEmail(domain.id, email, function (err, docs) {
if ((err != null) || (docs.length == 0)) { if ((err != null) || (docs.length == 0)) {
parent.debug('web', 'handleResetAccountRequest: Account not found');
req.session.loginmode = '3'; req.session.loginmode = '3';
req.session.error = '<b style=color:#8C001A>Account not found.</b>'; req.session.error = '<b style=color:#8C001A>Account not found.</b>';
res.redirect(domain.url + getQueryPortion(req)); res.redirect(domain.url + getQueryPortion(req));
@ -948,6 +985,7 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) {
if (result == false) { if (result == false) {
if (i == 0) { if (i == 0) {
// 2-step auth is required, but the token is not present or not valid. // 2-step auth is required, but the token is not present or not valid.
parent.debug('web', 'handleResetAccountRequest: Invalid 2FA token, try again');
if ((req.body.token != null) || (req.body.hwtoken != null)) { req.session.error = '<b style=color:#8C001A>Invalid token, try again.</b>'; } if ((req.body.token != null) || (req.body.hwtoken != null)) { req.session.error = '<b style=color:#8C001A>Invalid token, try again.</b>'; }
req.session.loginmode = '5'; req.session.loginmode = '5';
req.session.tokenemail = email; req.session.tokenemail = email;
@ -959,12 +997,14 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) {
if (obj.parent.mailserver != null) { if (obj.parent.mailserver != null) {
obj.parent.mailserver.sendAccountResetMail(domain, user.name, user.email); obj.parent.mailserver.sendAccountResetMail(domain, user.name, user.email);
if (i == 0) { if (i == 0) {
parent.debug('web', 'handleResetAccountRequest: Hold on, reset mail sent.');
req.session.loginmode = '1'; req.session.loginmode = '1';
req.session.error = '<b style=color:darkgreen>Hold on, reset mail sent.</b>'; req.session.error = '<b style=color:darkgreen>Hold on, reset mail sent.</b>';
res.redirect(domain.url + getQueryPortion(req)); res.redirect(domain.url + getQueryPortion(req));
} }
} else { } else {
if (i == 0) { if (i == 0) {
parent.debug('web', 'handleResetAccountRequest: Unable to sent email.');
req.session.loginmode = '3'; req.session.loginmode = '3';
req.session.error = '<b style=color:#8C001A>Unable to sent email.</b>'; req.session.error = '<b style=color:#8C001A>Unable to sent email.</b>';
res.redirect(domain.url + getQueryPortion(req)); res.redirect(domain.url + getQueryPortion(req));
@ -977,12 +1017,14 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) {
if (obj.parent.mailserver != null) { if (obj.parent.mailserver != null) {
obj.parent.mailserver.sendAccountResetMail(domain, user.name, user.email); obj.parent.mailserver.sendAccountResetMail(domain, user.name, user.email);
if (i == 0) { if (i == 0) {
parent.debug('web', 'handleResetAccountRequest: Hold on, reset mail sent.');
req.session.loginmode = '1'; req.session.loginmode = '1';
req.session.error = '<b style=color:darkgreen>Hold on, reset mail sent.</b>'; req.session.error = '<b style=color:darkgreen>Hold on, reset mail sent.</b>';
res.redirect(domain.url + getQueryPortion(req)); res.redirect(domain.url + getQueryPortion(req));
} }
} else { } else {
if (i == 0) { if (i == 0) {
parent.debug('web', 'handleResetAccountRequest: Unable to sent email.');
req.session.loginmode = '3'; req.session.loginmode = '3';
req.session.error = '<b style=color:#8C001A>Unable to sent email.</b>'; req.session.error = '<b style=color:#8C001A>Unable to sent email.</b>';
res.redirect(domain.url + getQueryPortion(req)); res.redirect(domain.url + getQueryPortion(req));
@ -998,7 +1040,11 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) {
// Called to process a web based email verification request // Called to process a web based email verification request
function handleCheckMailRequest(req, res) { function handleCheckMailRequest(req, res) {
const domain = checkUserIpAddress(req, res); const domain = checkUserIpAddress(req, res);
if ((domain == null) || (domain.auth == 'sspi') || (domain.auth == 'ldap')) { res.sendStatus(404); return; } if ((domain == null) || (domain.auth == 'sspi') || (domain.auth == 'ldap')) {
parent.debug('web', 'handleCheckMailRequest: failed checks.');
res.sendStatus(404);
return;
}
if (req.query.c != null) { if (req.query.c != null) {
var cookie = obj.parent.decodeCookie(req.query.c, obj.parent.mailserver.mailCookieEncryptionKey, 30); var cookie = obj.parent.decodeCookie(req.query.c, obj.parent.mailserver.mailCookieEncryptionKey, 30);
@ -1090,7 +1136,11 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) {
// Called to process an agent invite request // Called to process an agent invite request
function handleAgentInviteRequest(req, res) { function handleAgentInviteRequest(req, res) {
const domain = checkUserIpAddress(req, res); const domain = checkUserIpAddress(req, res);
if ((domain == null) || ((req.query.m == null) && (req.query.c == null))) { res.sendStatus(404); return; } if ((domain == null) || ((req.query.m == null) && (req.query.c == null))) {
parent.debug('web', 'handleAgentInviteRequest: failed checks.');
res.sendStatus(404);
return;
}
if (req.query.c != null) { if (req.query.c != null) {
// A cookie is specified in the query string, use that // A cookie is specified in the query string, use that
var cookie = obj.parent.decodeCookie(req.query.c, obj.parent.invitationLinkEncryptionKey); var cookie = obj.parent.decodeCookie(req.query.c, obj.parent.invitationLinkEncryptionKey);
@ -1113,7 +1163,11 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) {
function handleDeleteAccountRequest(req, res) { function handleDeleteAccountRequest(req, res) {
const domain = checkUserIpAddress(req, res); const domain = checkUserIpAddress(req, res);
if ((domain == null) || (domain.auth == 'sspi') || (domain.auth == 'ldap')) { res.sendStatus(404); return; } if ((domain == null) || (domain.auth == 'sspi') || (domain.auth == 'ldap')) {
parent.debug('web', 'handleDeleteAccountRequest: failed checks.');
res.sendStatus(404);
return;
}
// Check if the user is logged and we have all required parameters // 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 + getQueryPortion(req)); return; } if (!req.session || !req.session.userid || !req.body.apassword1 || (req.body.apassword1 != req.body.apassword2) || (req.session.domainid != domain.id)) { res.redirect(domain.url + getQueryPortion(req)); return; }
@ -1183,7 +1237,11 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) {
// Handle password changes // Handle password changes
function handlePasswordChangeRequest(req, res) { function handlePasswordChangeRequest(req, res) {
const domain = checkUserIpAddress(req, res); const domain = checkUserIpAddress(req, res);
if ((domain == null) || (domain.auth == 'sspi') || (domain.auth == 'ldap')) { res.sendStatus(404); return; } if ((domain == null) || (domain.auth == 'sspi') || (domain.auth == 'ldap')) {
parent.debug('web', 'handlePasswordChangeRequest: failed checks.');
res.sendStatus(404);
return;
}
// Check if the user is logged and we have all required parameters // Check if the user is logged and we have all required parameters
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 + getQueryPortion(req)); 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 + getQueryPortion(req)); return; }
@ -1215,8 +1273,8 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) {
// Indicates that any request to "/" should render "default" or "login" depending on login state // Indicates that any request to "/" should render "default" or "login" depending on login state
function handleRootRequest(req, res) { function handleRootRequest(req, res) {
const domain = checkUserIpAddress(req, res); const domain = checkUserIpAddress(req, res);
if (domain == null) { res.sendStatus(404); return; } if (domain == null) { parent.debug('web', 'handleRootRequest: invalid domain.'); res.sendStatus(404); return; }
if (!obj.args) { res.sendStatus(500); return; } if (!obj.args) { parent.debug('web', 'handleRootRequest: no obj.args.'); res.sendStatus(500); return; }
if ((domain.sspi != null) && ((req.query.login == null) || (obj.parent.loginCookieEncryptionKey == null))) { if ((domain.sspi != null) && ((req.query.login == null) || (obj.parent.loginCookieEncryptionKey == null))) {
// Login using SSPI // Login using SSPI