changeset 1617:1fbe598a14fb

Merge bugfix delay invalid users
author Matt Johnston <matt@ucc.asn.au>
date Thu, 23 Aug 2018 23:43:45 +0800
parents 0196f4f83fee (current diff) 5d2d1021ca00 (diff)
children a3bb8f8949de
files default_options.h svr-auth.c svr-authpubkey.c sysoptions.h
diffstat 6 files changed, 55 insertions(+), 36 deletions(-) [+]
line wrap: on
line diff
--- a/auth.h	Tue Jul 24 20:19:05 2018 +0800
+++ b/auth.h	Thu Aug 23 23:43:45 2018 +0800
@@ -37,9 +37,9 @@
 void send_msg_userauth_failure(int partial, int incrfail);
 void send_msg_userauth_success(void);
 void send_msg_userauth_banner(const buffer *msg);
-void svr_auth_password(void);
-void svr_auth_pubkey(void);
-void svr_auth_pam(void);
+void svr_auth_password(int valid_user);
+void svr_auth_pubkey(int valid_user);
+void svr_auth_pam(int valid_user);
 
 #if DROPBEAR_SVR_PUBKEY_OPTIONS_BUILT
 int svr_pubkey_allows_agentfwd(void);
--- a/svr-auth.c	Tue Jul 24 20:19:05 2018 +0800
+++ b/svr-auth.c	Thu Aug 23 23:43:45 2018 +0800
@@ -149,10 +149,8 @@
 		if (methodlen == AUTH_METHOD_PASSWORD_LEN &&
 				strncmp(methodname, AUTH_METHOD_PASSWORD,
 					AUTH_METHOD_PASSWORD_LEN) == 0) {
-			if (valid_user) {
-				svr_auth_password();
-				goto out;
-			}
+			svr_auth_password(valid_user);
+			goto out;
 		}
 	}
 #endif
@@ -164,10 +162,8 @@
 		if (methodlen == AUTH_METHOD_PASSWORD_LEN &&
 				strncmp(methodname, AUTH_METHOD_PASSWORD,
 					AUTH_METHOD_PASSWORD_LEN) == 0) {
-			if (valid_user) {
-				svr_auth_pam();
-				goto out;
-			}
+			svr_auth_pam(valid_user);
+			goto out;
 		}
 	}
 #endif
@@ -177,12 +173,7 @@
 	if (methodlen == AUTH_METHOD_PUBKEY_LEN &&
 			strncmp(methodname, AUTH_METHOD_PUBKEY,
 				AUTH_METHOD_PUBKEY_LEN) == 0) {
-		if (valid_user) {
-			svr_auth_pubkey();
-		} else {
-			/* pubkey has no failure delay */
-			send_msg_userauth_failure(0, 0);
-		}
+		svr_auth_pubkey(valid_user);
 		goto out;
 	}
 #endif
--- a/svr-authpam.c	Tue Jul 24 20:19:05 2018 +0800
+++ b/svr-authpam.c	Thu Aug 23 23:43:45 2018 +0800
@@ -178,13 +178,14 @@
  * Keyboard interactive would be a lot nicer, but since PAM is synchronous, it
  * gets very messy trying to send the interactive challenges, and read the
  * interactive responses, over the network. */
-void svr_auth_pam() {
+void svr_auth_pam(int valid_user) {
 
 	struct UserDataS userData = {NULL, NULL};
 	struct pam_conv pamConv = {
 		pamConvFunc,
 		&userData /* submitted to pamvConvFunc as appdata_ptr */ 
 	};
+	const char* printable_user = NULL;
 
 	pam_handle_t* pamHandlep = NULL;
 
@@ -204,12 +205,23 @@
 
 	password = buf_getstring(ses.payload, &passwordlen);
 
+	/* We run the PAM conversation regardless of whether the username is valid
+	in case the conversation function has an inherent delay.
+	Use ses.authstate.username rather than ses.authstate.pw_name.
+	After PAM succeeds we then check the valid_user flag too */
+
 	/* used to pass data to the PAM conversation function - don't bother with
 	 * strdup() etc since these are touched only by our own conversation
 	 * function (above) which takes care of it */
-	userData.user = ses.authstate.pw_name;
+	userData.user = ses.authstate.username;
 	userData.passwd = password;
 
+	if (ses.authstate.pw_name) {
+		printable_user = ses.authstate.pw_name;
+	} else {
+		printable_user = "<invalid username>";
+	}
+
 	/* Init pam */
 	if ((rc = pam_start("sshd", NULL, &pamConv, &pamHandlep)) != PAM_SUCCESS) {
 		dropbear_log(LOG_WARNING, "pam_start() failed, rc=%d, %s", 
@@ -242,7 +254,7 @@
 				rc, pam_strerror(pamHandlep, rc));
 		dropbear_log(LOG_WARNING,
 				"Bad PAM password attempt for '%s' from %s",
-				ses.authstate.pw_name,
+				printable_user,
 				svr_ses.addrstring);
 		send_msg_userauth_failure(0, 1);
 		goto cleanup;
@@ -253,12 +265,18 @@
 				rc, pam_strerror(pamHandlep, rc));
 		dropbear_log(LOG_WARNING,
 				"Bad PAM password attempt for '%s' from %s",
-				ses.authstate.pw_name,
+				printable_user,
 				svr_ses.addrstring);
 		send_msg_userauth_failure(0, 1);
 		goto cleanup;
 	}
 
+	if (!valid_user) {
+		/* PAM auth succeeded but the username isn't allowed in for another reason
+		(checkusername() failed) */
+		send_msg_userauth_failure(0, 1);
+	}
+
 	/* successful authentication */
 	dropbear_log(LOG_NOTICE, "PAM password auth succeeded for '%s' from %s",
 			ses.authstate.pw_name,
--- a/svr-authpasswd.c	Tue Jul 24 20:19:05 2018 +0800
+++ b/svr-authpasswd.c	Thu Aug 23 23:43:45 2018 +0800
@@ -48,22 +48,14 @@
 
 /* Process a password auth request, sending success or failure messages as
  * appropriate */
-void svr_auth_password() {
+void svr_auth_password(int valid_user) {
 	
 	char * passwdcrypt = NULL; /* the crypt from /etc/passwd or /etc/shadow */
 	char * testcrypt = NULL; /* crypt generated from the user's password sent */
-	char * password;
+	char * password = NULL;
 	unsigned int passwordlen;
-
 	unsigned int changepw;
 
-	passwdcrypt = ses.authstate.pw_passwd;
-
-#ifdef DEBUG_HACKCRYPT
-	/* debugging crypt for non-root testing with shadows */
-	passwdcrypt = DEBUG_HACKCRYPT;
-#endif
-
 	/* check if client wants to change password */
 	changepw = buf_getbool(ses.payload);
 	if (changepw) {
@@ -73,12 +65,21 @@
 	}
 
 	password = buf_getstring(ses.payload, &passwordlen);
-
-	/* the first bytes of passwdcrypt are the salt */
-	testcrypt = crypt(password, passwdcrypt);
+	if (valid_user) {
+		/* the first bytes of passwdcrypt are the salt */
+		passwdcrypt = ses.authstate.pw_passwd;
+		testcrypt = crypt(password, passwdcrypt);
+	}
 	m_burn(password, passwordlen);
 	m_free(password);
 
+	/* After we have got the payload contents we can exit if the username
+	is invalid. Invalid users have already been logged. */
+	if (!valid_user) {
+		send_msg_userauth_failure(0, 1);
+		return;
+	}
+
 	if (testcrypt == NULL) {
 		/* crypt() with an invalid salt like "!!" */
 		dropbear_log(LOG_WARNING, "User account '%s' is locked",
--- a/svr-authpubkey.c	Tue Jul 24 20:19:05 2018 +0800
+++ b/svr-authpubkey.c	Thu Aug 23 23:43:45 2018 +0800
@@ -79,7 +79,7 @@
 
 /* process a pubkey auth request, sending success or failure message as
  * appropriate */
-void svr_auth_pubkey() {
+void svr_auth_pubkey(int valid_user) {
 
 	unsigned char testkey; /* whether we're just checking if a key is usable */
 	char* algo = NULL; /* pubkey algo */
@@ -102,6 +102,15 @@
 	keybloblen = buf_getint(ses.payload);
 	keyblob = buf_getptr(ses.payload, keybloblen);
 
+	if (!valid_user) {
+		/* Return failure once we have read the contents of the packet
+		required to validate a public key. 
+		Avoids blind user enumeration though it isn't possible to prevent
+		testing for user existence if the public key is known */
+		send_msg_userauth_failure(0, 0);
+		goto out;
+	}
+
 	/* check if the key is valid */
 	if (checkpubkey(algo, algolen, keyblob, keybloblen) == DROPBEAR_FAILURE) {
 		send_msg_userauth_failure(0, 0);
--- a/sysoptions.h	Tue Jul 24 20:19:05 2018 +0800
+++ b/sysoptions.h	Thu Aug 23 23:43:45 2018 +0800
@@ -225,7 +225,7 @@
 #define DROPBEAR_ZLIB_MEM_LEVEL 8
 
 #if (DROPBEAR_SVR_PASSWORD_AUTH) && (DROPBEAR_SVR_PAM_AUTH)
-#error "You can't turn on PASSWORD and PAM auth both at once. Fix it in options.h"
+#error "You can't turn on PASSWORD and PAM auth both at once. Fix it in localoptions.h"
 #endif
 
 /* PAM requires ./configure --enable-pam */