diff --git a/WHATSNEW.md b/WHATSNEW.md index 5b08e2b8..01fc8edc 100644 --- a/WHATSNEW.md +++ b/WHATSNEW.md @@ -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. * `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. +* Usernames found in the `config.users.badUserNames` are now not only disallowed from applying, but disconnected at any login attempt. ## 0.0.8-alpha diff --git a/core/servers/login/ssh.js b/core/servers/login/ssh.js index 6c270378..27b5aed9 100644 --- a/core/servers/login/ssh.js +++ b/core/servers/login/ssh.js @@ -57,39 +57,46 @@ function SSHClient(clientConn) { } }; - function terminateConnection() { + const terminateConnection = () => { safeContextReject(); 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) { 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)`); - } + }; - function accountDisabled(username) { + const accountDisabled = (username) => { 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)`); - } + }; - function accountLocked(username) { - return promptAndTerm(`${username} is locked.\n(Press any key to continue)`); - } + const accountLocked = (username) => { + 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); - } + }; - function handleSpecialError(err, username) { + const handleSpecialError = (err, username) => { switch(err.reasonCode) { case ErrorReasons.AlreadyLoggedIn : return accountAlreadyLoggedIn(username); case ErrorReasons.Inactive : return accountInactive(username); @@ -97,7 +104,7 @@ function SSHClient(clientConn) { case ErrorReasons.Locked : return accountLocked(username); default : return terminateConnection(); } - } + }; // // If the system is open and |isNewUser| is true, the login @@ -115,7 +122,7 @@ function SSHClient(clientConn) { } if(Errors.BadLogin().code === err.code) { - return terminateConnection(); + return slowTerminateConnection(); } return safeContextReject(SSHClient.ValidAuthMethods); @@ -143,7 +150,7 @@ function SSHClient(clientConn) { } if(Errors.BadLogin().code === err.code) { - return terminateConnection(); + return slowTerminateConnection(); } const artOpts = { diff --git a/core/system_menu_method.js b/core/system_menu_method.js index 5e7b651b..ea2cbc09 100644 --- a/core/system_menu_method.js +++ b/core/system_menu_method.js @@ -34,6 +34,11 @@ function login(callingMenu, formData, extraArgs, 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 = [ ErrorReasons.TooMany, ErrorReasons.Disabled, ErrorReasons.Inactive, ErrorReasons.Locked ]; diff --git a/core/user_login.js b/core/user_login.js index af764208..395c446d 100644 --- a/core/user_login.js +++ b/core/user_login.js @@ -27,7 +27,11 @@ function userLogin(client, username, password, cb) { if(config.users.badUserNames.includes(username.toLowerCase())) { 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 => {