[PATCH v2 2/2] login: improve login delays after invalid attempts

Hemmo Nieminen hemmo.nieminen at iki.fi
Mon May 17 12:22:42 UTC 2021


From: Hemmo Nieminen <hemmo.nieminen at kone.com>

Support increasing delays after invalid login attempts even when not
using PAM. Use libbb's tallying to count invalid login attempts and
increase the length of the pause after invalid login attempts
accordingly. Reset pause length upon successful login.

Signed-off-by: Hemmo Nieminen <hemmo.nieminen at kone.com>
---
 include/libbb.h      |  7 +++++-
 libbb/bb_do_delay.c  | 32 +++++++++++++++++---------
 loginutils/login.c   | 53 +++++++++++++++++++++++++++++++++++++++++++-
 loginutils/passwd.c  |  2 +-
 loginutils/su.c      |  2 +-
 loginutils/sulogin.c |  2 +-
 loginutils/vlock.c   |  2 +-
 7 files changed, 83 insertions(+), 17 deletions(-)

diff --git a/include/libbb.h b/include/libbb.h
index 50a302f17..e7286e406 100644
--- a/include/libbb.h
+++ b/include/libbb.h
@@ -1658,7 +1658,12 @@ char *bb_simplify_path(const char *path) FAST_FUNC;
 /* Returns ptr to NUL */
 char *bb_simplify_abs_path_inplace(char *path) FAST_FUNC;
 
-void pause_after_failed_login(void) FAST_FUNC;
+void pause_after_failed_login(char const *tty) FAST_FUNC;
+#if ENABLE_FEATURE_TALLY
+void pause_reset_after_successful_login(char const *tty) FAST_FUNC;
+#else
+#define pause_reset_after_successful_login(...)
+#endif
 void bb_do_delay(unsigned seconds) FAST_FUNC;
 void msleep(unsigned ms) FAST_FUNC;
 void sleep1(void) FAST_FUNC;
diff --git a/libbb/bb_do_delay.c b/libbb/bb_do_delay.c
index 9a84fa24b..f208fe192 100644
--- a/libbb/bb_do_delay.c
+++ b/libbb/bb_do_delay.c
@@ -13,20 +13,30 @@
 #ifndef LOGIN_FAIL_DELAY
 #define LOGIN_FAIL_DELAY 3
 #endif
-void FAST_FUNC pause_after_failed_login(void)
+
+#if ENABLE_FEATURE_TALLY
+void FAST_FUNC pause_reset_after_successful_login(char const *tally_file)
 {
-#if 0 /* over-engineered madness */
-	time_t end, diff;
+	if (tally_file)
+		bb_tally_reset(tally_file);
+}
+#endif
 
-	end = time(NULL) + LOGIN_FAIL_DELAY;
-	diff = LOGIN_FAIL_DELAY;
-	do {
-		sleep(diff);
-		diff = end - time(NULL);
-	} while (diff > 0);
-#else
-	sleep(LOGIN_FAIL_DELAY);
+void FAST_FUNC pause_after_failed_login(
+		char const *tally_file IF_NOT_FEATURE_TALLY(UNUSED_PARAM))
+{
+#if ENABLE_FEATURE_TALLY
+	if (tally_file) {
+		int mult = bb_tally_add(tally_file);
+
+		if (mult > 1) {
+			sleep(LOGIN_FAIL_DELAY * mult);
+			return;
+		}
+	}
 #endif
+
+	sleep(LOGIN_FAIL_DELAY);
 }
 
 void FAST_FUNC sleep1(void)
diff --git a/loginutils/login.c b/loginutils/login.c
index ce87e318a..af689edde 100644
--- a/loginutils/login.c
+++ b/loginutils/login.c
@@ -47,6 +47,16 @@
 //config:	The file /etc/securetty is used by (some versions of) login(1).
 //config:	The file contains the device names of tty lines (one per line,
 //config:	without leading /dev/) on which root is allowed to login.
+//config:
+//config:config FEATURE_TALLY_FAILURES
+//config:	bool "Tally failed login attemps to increse delays"
+//config:	default n
+//config:	depends on LOGIN && FEATURE_TALLY
+//config:	help
+//config:	Files under /run/login/ are used to store counters for failed login
+//config:	attempts in order to increase the duration of the pause between
+//config:	login attempts. Successfull login clears the counter. Ignored
+//config:	when also using PAM.
 
 //applet:/* Needs to be run by root or be suid root - needs to change uid and gid: */
 //applet:IF_LOGIN(APPLET(login, BB_DIR_BIN, BB_SUID_REQUIRE))
@@ -134,6 +144,13 @@ static const struct pam_conv conv = {
 };
 #endif
 
+#if !ENABLE_PAM && ENABLE_FEATURE_TALLY_FAILURES
+#define LOGIN_TALLY_DIR "/run/login"
+#define LOGIN_TALLY_PREFIX LOGIN_TALLY_DIR "/tally_"
+#define LOGIN_TALLY_PREFIX_LENGTH 17
+#define TALLYPATH_BUFFER_SIZE 256
+#endif
+
 enum {
 	EMPTY_USERNAME_COUNT = 10,
 	/* Some users found 32 chars limit to be too low: */
@@ -179,6 +196,29 @@ static void die_if_nologin(void)
 # define die_if_nologin() ((void)0)
 #endif
 
+#if !ENABLE_PAM && ENABLE_FEATURE_TALLY_FAILURES
+static char * prepare_tallypath(char tallypath_buf[TALLYPATH_BUFFER_SIZE],
+		char const * short_tty)
+{
+	int i, j;
+
+	if (mkdir(LOGIN_TALLY_DIR, S_IRWXU) == 0 || errno == EEXIST) {
+		for (i = LOGIN_TALLY_PREFIX_LENGTH, j = 0 ;
+				short_tty[j] != '\0' && i < TALLYPATH_BUFFER_SIZE ;
+				i++, j++) {
+			if (short_tty[j] == '/')
+				tallypath_buf[i] = '_';
+			else
+				tallypath_buf[i] = short_tty[j];
+		}
+		if (i < TALLYPATH_BUFFER_SIZE)
+			return tallypath_buf;
+	}
+
+	return NULL;
+}
+#endif
+
 #if ENABLE_SELINUX
 static void initselinux(char *username, char *full_tty,
 						security_context_t *user_sid)
@@ -342,6 +382,10 @@ int login_main(int argc UNUSED_PARAM, char **argv)
 	struct passwd pwdstruct;
 	char pwdbuf[256];
 	char **pamenv;
+#endif
+	char const * tallypath = NULL;
+#if !ENABLE_PAM && ENABLE_FEATURE_TALLY_FAILURES
+	char tallypath_buf[TALLYPATH_BUFFER_SIZE] = LOGIN_TALLY_PREFIX;
 #endif
 #if ENABLE_LOGIN_SESSION_AS_CHILD
 	pid_t child_pid;
@@ -391,6 +435,10 @@ int login_main(int argc UNUSED_PARAM, char **argv)
 		full_tty = xstrdup("UNKNOWN");
 	short_tty = skip_dev_pfx(full_tty);
 
+#if !ENABLE_PAM && ENABLE_FEATURE_TALLY_FAILURES
+	tallypath = prepare_tallypath(tallypath_buf, short_tty);
+#endif
+
 	if (opt_host) {
 		fromhost = xasprintf(" on '%s' from '%s'", short_tty, opt_host);
 	} else {
@@ -511,11 +559,14 @@ int login_main(int argc UNUSED_PARAM, char **argv)
 		 * If we get interrupted by SIGALRM, we need to restore attrs.
 		 */
 		if (ask_and_check_password(pw) > 0)
+		{
+			pause_reset_after_successful_login(tallypath);
 			break;
+		}
 #endif /* ENABLE_PAM */
  auth_failed:
 		opt &= ~LOGIN_OPT_f;
-		pause_after_failed_login();
+		pause_after_failed_login(tallypath);
 		/* TODO: doesn't sound like correct English phrase to me */
 		puts("Login incorrect");
 		syslog(LOG_WARNING, "invalid password for '%s'%s",
diff --git a/loginutils/passwd.c b/loginutils/passwd.c
index acc942275..f15e1355f 100644
--- a/loginutils/passwd.c
+++ b/loginutils/passwd.c
@@ -57,7 +57,7 @@ static char* new_password(const struct passwd *pw, uid_t myuid, const char *algo
 		encrypted = pw_encrypt(orig, pw->pw_passwd, 1); /* returns malloced str */
 		if (strcmp(encrypted, pw->pw_passwd) != 0) {
 			syslog(LOG_WARNING, "incorrect password for %s", pw->pw_name);
-			pause_after_failed_login();
+			pause_after_failed_login(NULL);
 			puts("Incorrect password");
 			goto err_ret;
 		}
diff --git a/loginutils/su.c b/loginutils/su.c
index 784a53552..7d83757dc 100644
--- a/loginutils/su.c
+++ b/loginutils/su.c
@@ -146,7 +146,7 @@ int su_main(int argc UNUSED_PARAM, char **argv)
 		if (ENABLE_FEATURE_SU_SYSLOG)
 			syslog(LOG_NOTICE, "%c %s %s:%s",
 				'-', tty, old_user, opt_username);
-		pause_after_failed_login();
+		pause_after_failed_login(NULL);
 		bb_simple_error_msg_and_die("incorrect password");
 	}
 
diff --git a/loginutils/sulogin.c b/loginutils/sulogin.c
index 69d8b5ec7..bc5ed2a1d 100644
--- a/loginutils/sulogin.c
+++ b/loginutils/sulogin.c
@@ -74,7 +74,7 @@ int sulogin_main(int argc UNUSED_PARAM, char **argv)
 		if (r > 0) {
 			break;
 		}
-		pause_after_failed_login();
+		pause_after_failed_login(NULL);
 		bb_simple_info_msg("Login incorrect");
 	}
 
diff --git a/loginutils/vlock.c b/loginutils/vlock.c
index 334b7d2ad..af9064f51 100644
--- a/loginutils/vlock.c
+++ b/loginutils/vlock.c
@@ -120,7 +120,7 @@ int vlock_main(int argc UNUSED_PARAM, char **argv)
 		if (ask_and_check_password(pw) > 0) {
 			break;
 		}
-		pause_after_failed_login();
+		pause_after_failed_login(NULL);
 		puts("Incorrect password");
 	}
 
-- 
2.31.1



More information about the busybox mailing list