* Disconnect clients that attempt to login with banned usernames for Telnet as well

* Slow disconnects to thwart brute force attacks - these names won't exist anyway,
  but we want the attacking client to not DoS us
This commit is contained in:
Bryan Ashby 2018-12-25 00:18:04 -07:00
parent 06a1925288
commit ee93035bb8
4 changed files with 38 additions and 21 deletions

View File

@ -25,6 +25,7 @@ This document attempts to track **major** changes and additions in ENiGMA½. For
* NNTP support! See [NNTP docs](/docs/servers/nntp.md) for more information. * NNTP support! See [NNTP docs](/docs/servers/nntp.md) for more information.
* `oputil.js user rm` and `oputil.js user info` are in! See [oputil CLI](/docs/admin/oputil.md). * `oputil.js user rm` and `oputil.js user info` are in! See [oputil CLI](/docs/admin/oputil.md).
* Performing a file scan/import using `oputil.js fb scan` now recognizes various `FILES.BBS` formats. * Performing a file scan/import using `oputil.js fb scan` now recognizes various `FILES.BBS` formats.
* Usernames found in the `config.users.badUserNames` are now not only disallowed from applying, but disconnected at any login attempt.
## 0.0.8-alpha ## 0.0.8-alpha

View File

@ -57,39 +57,46 @@ function SSHClient(clientConn) {
} }
}; };
function terminateConnection() { const terminateConnection = () => {
safeContextReject(); safeContextReject();
return clientConn.end(); return clientConn.end();
} };
function promptAndTerm(msg) { // slow version to thwart brute force attacks
const slowTerminateConnection = () => {
setTimeout( () => {
return terminateConnection();
}, 2000);
};
const promptAndTerm = (msg, method = 'standard') => {
if('keyboard-interactive' === ctx.method) { if('keyboard-interactive' === ctx.method) {
ctx.prompt(msg); ctx.prompt(msg);
} }
return terminateConnection(); return 'slow' === method ? slowTerminateConnection() : terminateConnection();
} };
function accountAlreadyLoggedIn(username) { const accountAlreadyLoggedIn = (username) => {
return promptAndTerm(`${username} is already connected to the system. Terminating connection.\n(Press any key to continue)`); return promptAndTerm(`${username} is already connected to the system. Terminating connection.\n(Press any key to continue)`);
} };
function accountDisabled(username) { const accountDisabled = (username) => {
return promptAndTerm(`${username} is disabled.\n(Press any key to continue)`); return promptAndTerm(`${username} is disabled.\n(Press any key to continue)`);
} };
function accountInactive(username) { const accountInactive = (username) => {
return promptAndTerm(`${username} is waiting for +op activation.\n(Press any key to continue)`); return promptAndTerm(`${username} is waiting for +op activation.\n(Press any key to continue)`);
} };
function accountLocked(username) { const accountLocked = (username) => {
return promptAndTerm(`${username} is locked.\n(Press any key to continue)`); return promptAndTerm(`${username} is locked.\n(Press any key to continue)`, 'slow');
} };
function isSpecialHandleError(err) { const isSpecialHandleError = (err) => {
return [ ErrorReasons.AlreadyLoggedIn, ErrorReasons.Disabled, ErrorReasons.Inactive, ErrorReasons.Locked ].includes(err.reasonCode); return [ ErrorReasons.AlreadyLoggedIn, ErrorReasons.Disabled, ErrorReasons.Inactive, ErrorReasons.Locked ].includes(err.reasonCode);
} };
function handleSpecialError(err, username) { const handleSpecialError = (err, username) => {
switch(err.reasonCode) { switch(err.reasonCode) {
case ErrorReasons.AlreadyLoggedIn : return accountAlreadyLoggedIn(username); case ErrorReasons.AlreadyLoggedIn : return accountAlreadyLoggedIn(username);
case ErrorReasons.Inactive : return accountInactive(username); case ErrorReasons.Inactive : return accountInactive(username);
@ -97,7 +104,7 @@ function SSHClient(clientConn) {
case ErrorReasons.Locked : return accountLocked(username); case ErrorReasons.Locked : return accountLocked(username);
default : return terminateConnection(); default : return terminateConnection();
} }
} };
// //
// If the system is open and |isNewUser| is true, the login // If the system is open and |isNewUser| is true, the login
@ -115,7 +122,7 @@ function SSHClient(clientConn) {
} }
if(Errors.BadLogin().code === err.code) { if(Errors.BadLogin().code === err.code) {
return terminateConnection(); return slowTerminateConnection();
} }
return safeContextReject(SSHClient.ValidAuthMethods); return safeContextReject(SSHClient.ValidAuthMethods);
@ -143,7 +150,7 @@ function SSHClient(clientConn) {
} }
if(Errors.BadLogin().code === err.code) { if(Errors.BadLogin().code === err.code) {
return terminateConnection(); return slowTerminateConnection();
} }
const artOpts = { const artOpts = {

View File

@ -34,6 +34,11 @@ function login(callingMenu, formData, extraArgs, cb) {
return callingMenu.gotoMenu(callingMenu.menuConfig.config.tooNodeMenu, cb); return callingMenu.gotoMenu(callingMenu.menuConfig.config.tooNodeMenu, cb);
} }
// banned username results in disconnect
if(ErrorReasons.NotAllowed === err.reasonCode) {
return logoff(callingMenu, {}, {}, cb);
}
const ReasonsMenus = [ const ReasonsMenus = [
ErrorReasons.TooMany, ErrorReasons.Disabled, ErrorReasons.Inactive, ErrorReasons.Locked ErrorReasons.TooMany, ErrorReasons.Disabled, ErrorReasons.Inactive, ErrorReasons.Locked
]; ];

View File

@ -27,7 +27,11 @@ function userLogin(client, username, password, cb) {
if(config.users.badUserNames.includes(username.toLowerCase())) { if(config.users.badUserNames.includes(username.toLowerCase())) {
client.log.info( { username : username }, 'Attempt to login with banned username'); client.log.info( { username : username }, 'Attempt to login with banned username');
return cb(Errors.BadLogin(ErrorReasons.NotAllowed));
// slow down a bit to thwart brute force attacks
return setTimeout( () => {
return cb(Errors.BadLogin('Disallowed username', ErrorReasons.NotAllowed));
}, 2000);
} }
client.user.authenticate(username, password, err => { client.user.authenticate(username, password, err => {