diff svr-auth.c @ 1546:bb8eaa26bc93 fuzz

merge from main
author Matt Johnston <matt@ucc.asn.au>
date Mon, 26 Feb 2018 22:44:48 +0800
parents 5916af64acd4 51df3d53b050
children 61a793b6e471
line wrap: on
line diff
--- a/svr-auth.c	Wed Feb 21 21:59:52 2018 +0800
+++ b/svr-auth.c	Mon Feb 26 22:44:48 2018 +0800
@@ -25,6 +25,8 @@
 /* This file (auth.c) handles authentication requests, passing it to the
  * particular type (auth-passwd, auth-pubkey). */
 
+#include <limits.h>
+
 #include "includes.h"
 #include "dbutil.h"
 #include "session.h"
@@ -35,26 +37,10 @@
 #include "runopts.h"
 #include "dbrandom.h"
 
-static void authclear(void);
-static int checkusername(char *username, unsigned int userlen);
+static int checkusername(const char *username, unsigned int userlen);
 
 /* initialise the first time for a session, resetting all parameters */
 void svr_authinitialise() {
-
-	ses.authstate.failcount = 0;
-	ses.authstate.pw_name = NULL;
-	ses.authstate.pw_dir = NULL;
-	ses.authstate.pw_shell = NULL;
-	ses.authstate.pw_passwd = NULL;
-	authclear();
-	
-}
-
-/* Reset the auth state, but don't reset the failcount. This is for if the
- * user decides to try with a different username etc, and is also invoked
- * on initialisation */
-static void authclear() {
-	
 	memset(&ses.authstate, 0, sizeof(ses.authstate));
 #if DROPBEAR_SVR_PUBKEY_AUTH
 	ses.authstate.authtypes |= AUTH_TYPE_PUBKEY;
@@ -64,19 +50,6 @@
 		ses.authstate.authtypes |= AUTH_TYPE_PASSWORD;
 	}
 #endif
-	if (ses.authstate.pw_name) {
-		m_free(ses.authstate.pw_name);
-	}
-	if (ses.authstate.pw_shell) {
-		m_free(ses.authstate.pw_shell);
-	}
-	if (ses.authstate.pw_dir) {
-		m_free(ses.authstate.pw_dir);
-	}
-	if (ses.authstate.pw_passwd) {
-		m_free(ses.authstate.pw_passwd);
-	}
-	
 }
 
 /* Send a banner message if specified to the client. The client might
@@ -224,31 +197,76 @@
 	m_free(methodname);
 }
 
+/* returns DROPBEAR_SUCCESS or DROPBEAR_FAILURE */
+static int check_group_membership(gid_t check_gid, const char* username, gid_t user_gid) {
+	int ngroups, i, ret;
+	gid_t *grouplist = NULL;
+	int match = DROPBEAR_FAILURE;
+
+	for (ngroups = 32; ngroups <= DROPBEAR_NGROUP_MAX; ngroups *= 2) {
+		grouplist = m_malloc(sizeof(gid_t) * ngroups);
+
+		/* BSD returns ret==0 on success. Linux returns ret==ngroups on success */
+		ret = getgrouplist(username, user_gid, grouplist, &ngroups);
+		if (ret >= 0) {
+			break;
+		}
+		m_free(grouplist);
+		grouplist = NULL;
+	}
+
+	if (!grouplist) {
+		dropbear_log(LOG_ERR, "Too many groups for user '%s'", username);
+		return DROPBEAR_FAILURE;
+	}
+
+	for (i = 0; i < ngroups; i++) {
+		if (grouplist[i] == check_gid) {
+			match = DROPBEAR_SUCCESS;
+			break;
+		}
+	}
+	m_free(grouplist);
+
+	return match;
+}
+
 
 /* Check that the username exists and isn't disallowed (root), and has a valid shell.
  * returns DROPBEAR_SUCCESS on valid username, DROPBEAR_FAILURE on failure */
-static int checkusername(char *username, unsigned int userlen) {
+static int checkusername(const char *username, unsigned int userlen) {
 
 	char* listshell = NULL;
 	char* usershell = NULL;
 	uid_t uid;
+
 	TRACE(("enter checkusername"))
 	if (userlen > MAX_USERNAME_LEN) {
 		return DROPBEAR_FAILURE;
 	}
 
-	/* new user or username has changed */
-	if (ses.authstate.username == NULL ||
-		strcmp(username, ses.authstate.username) != 0) {
-			/* the username needs resetting */
-			if (ses.authstate.username != NULL) {
-				dropbear_log(LOG_WARNING, "Client trying multiple usernames from %s",
-							svr_ses.addrstring);
-				m_free(ses.authstate.username);
-			}
-			authclear();
-			fill_passwd(username);
-			ses.authstate.username = m_strdup(username);
+	if (strlen(username) != userlen) {
+		dropbear_exit("Attempted username with a null byte from %s",
+			svr_ses.addrstring);
+	}
+
+	if (ses.authstate.username == NULL) {
+		/* first request */
+		fill_passwd(username);
+		ses.authstate.username = m_strdup(username);
+	} else {
+		/* check username hasn't changed */
+		if (strcmp(username, ses.authstate.username) != 0) {
+			dropbear_exit("Client trying multiple usernames from %s",
+				svr_ses.addrstring);
+		}
+	}
+
+	/* avoids cluttering logs with repeated failure messages from
+	consecutive authentication requests in a sesssion */
+	if (ses.authstate.checkusername_failed) {
+		TRACE(("checkusername: returning cached failure"))
+		return DROPBEAR_FAILURE;
 	}
 
 	/* check that user exists */
@@ -257,6 +275,7 @@
 		dropbear_log(LOG_WARNING,
 				"Login attempt for nonexistent user from %s",
 				svr_ses.addrstring);
+		ses.authstate.checkusername_failed = 1;
 		return DROPBEAR_FAILURE;
 	}
 
@@ -268,6 +287,7 @@
 				"Login attempt with wrong user %s from %s",
 				ses.authstate.pw_name,
 				svr_ses.addrstring);
+		ses.authstate.checkusername_failed = 1;
 		return DROPBEAR_FAILURE;
 	}
 
@@ -275,9 +295,22 @@
 	if (svr_opts.norootlogin && ses.authstate.pw_uid == 0) {
 		TRACE(("leave checkusername: root login disabled"))
 		dropbear_log(LOG_WARNING, "root login rejected");
+		ses.authstate.checkusername_failed = 1;
 		return DROPBEAR_FAILURE;
 	}
 
+	/* check for login restricted to certain group if desired */
+	if (svr_opts.restrict_group) {
+		if (check_group_membership(svr_opts.restrict_group_gid,
+				ses.authstate.pw_name, ses.authstate.pw_gid) == DROPBEAR_FAILURE) {
+			dropbear_log(LOG_WARNING,
+				"Logins are restricted to the group %s but user '%s' is not a member",
+				svr_opts.restrict_group, ses.authstate.pw_name);
+			ses.authstate.checkusername_failed = 1;
+			return DROPBEAR_FAILURE;
+		}
+	}
+
 	TRACE(("shell is %s", ses.authstate.pw_shell))
 
 	/* check that the shell is set */
@@ -301,6 +334,7 @@
 	/* no matching shell */
 	endusershell();
 	TRACE(("no matching shell"))
+	ses.authstate.checkusername_failed = 1;
 	dropbear_log(LOG_WARNING, "User '%s' has invalid shell, rejected",
 				ses.authstate.pw_name);
 	return DROPBEAR_FAILURE;