The unified diff between revisions [4c883eb4..] and [fff0894a..] is displayed below. It can also be downloaded as a raw diff.

#
#
# patch "dbutil.c"
#  from [bcd256f4bdc8183dfd6b813375c39af6513b183d]
#    to [4849b88af194476f39b4aa8fe3647e64e52b85a7]
#
# patch "dbutil.h"
#  from [76eb992fc016821fd1584982e59abe7ac66c5671]
#    to [80bc2ed10e5685a75b4443d9eceadf5159889df9]
#
# patch "options.h"
#  from [458cad58f22fe5b0e6ba44bbe9f1820d81f50314]
#    to [1d5e35e7d324996263755fb34439e4b91c5b0c00]
#
# patch "svr-chansession.c"
#  from [248542573188674e77293507ac3a23b081041f56]
#    to [d64917b0372fdb027e8704c9504cc4c2ed787398]
#
# patch "svr-main.c"
#  from [018c4a2cc2dd9cee3cca8b162ec50551fd7a30be]
#    to [948adffe37b58b0b102e25128b6b0e5a06b71448]
#
============================================================
--- dbutil.c	bcd256f4bdc8183dfd6b813375c39af6513b183d
+++ dbutil.c	4849b88af194476f39b4aa8fe3647e64e52b85a7
@@ -588,20 +588,17 @@ out:
 }
 #endif

-/* loop until the socket is closed (in case of EINTR) or
- * we get and error.
- * Returns DROPBEAR_SUCCESS or DROPBEAR_FAILURE */
-int m_close(int fd) {
+/* make sure that the socket closes */
+void m_close(int fd) {

 	int val;
 	do {
 		val = close(fd);
 	} while (val < 0 && errno == EINTR);

-	if (val == 0 || errno == EBADF) {
-		return DROPBEAR_SUCCESS;
-	} else {
-		return DROPBEAR_FAILURE;
+	if (val < 0 && errno != EBADF) {
+		/* Linux says EIO can happen */
+		dropbear_exit("Error closing fd %d, %s", fd, strerror(errno));
 	}
 }

============================================================
--- dbutil.h	76eb992fc016821fd1584982e59abe7ac66c5671
+++ dbutil.h	80bc2ed10e5685a75b4443d9eceadf5159889df9
@@ -55,7 +55,7 @@ int buf_getline(buffer * line, FILE * au
 int buf_readfile(buffer* buf, const char* filename);
 int buf_getline(buffer * line, FILE * authfile);

-int m_close(int fd);
+void m_close(int fd);
 void * m_malloc(size_t size);
 void * m_strdup(const char * str);
 void * m_realloc(void* ptr, size_t size);
============================================================
--- options.h	458cad58f22fe5b0e6ba44bbe9f1820d81f50314
+++ options.h	1d5e35e7d324996263755fb34439e4b91c5b0c00
@@ -161,6 +161,13 @@ etc) slower (perhaps by 50%). Recommende

 /* Specify the number of clients we will allow to be connected but
  * not yet authenticated. After this limit, connections are rejected */
+/* The first setting is per-IP, to avoid denial of service */
+#ifndef MAX_UNAUTH_PER_IP
+#define MAX_UNAUTH_PER_IP 5
+#endif
+
+/* And then a global limit to avoid chewing memory if connections
+ * come from many IPs */
 #ifndef MAX_UNAUTH_CLIENTS
 #define MAX_UNAUTH_CLIENTS 30
 #endif
============================================================
--- svr-chansession.c	248542573188674e77293507ac3a23b081041f56
+++ svr-chansession.c	d64917b0372fdb027e8704c9504cc4c2ed787398
@@ -851,9 +851,7 @@ static void execchild(struct ChanSess *c
 	/* close file descriptors except stdin/stdout/stderr
 	 * Need to be sure FDs are closed here to avoid reading files as root */
 	for (i = 3; i <= (unsigned int)ses.maxfd; i++) {
-		if (m_close(i) == DROPBEAR_FAILURE) {
-			dropbear_exit("Error closing file desc");
-		}
+		m_close(i);
 	}

 	/* clear environment */
============================================================
--- svr-main.c	018c4a2cc2dd9cee3cca8b162ec50551fd7a30be
+++ svr-main.c	948adffe37b58b0b102e25128b6b0e5a06b71448
@@ -29,7 +29,7 @@
 #include "signkey.h"
 #include "runopts.h"

-static int listensockets(int *sock, int sockcount, int *maxfd);
+static size_t listensockets(int *sock, size_t sockcount, int *maxfd);
 static void sigchld_handler(int dummy);
 static void sigsegv_handler(int);
 static void sigintterm_handler(int fish);
@@ -41,8 +41,6 @@ static void commonsetup();
 #endif
 static void commonsetup();

-static int childpipes[MAX_UNAUTH_CLIENTS];
-
 #if defined(DBMULTI_dropbear) || !defined(DROPBEAR_MULTI)
 #if defined(DBMULTI_dropbear) && defined(DROPBEAR_MULTI)
 int dropbear_main(int argc, char ** argv)
@@ -80,7 +78,7 @@ static void main_inetd() {
 static void main_inetd() {

 	struct sockaddr_storage remoteaddr;
-	int remoteaddrlen;
+	socklen_t remoteaddrlen;
 	char * addrstring = NULL;

 	/* Set up handlers, syslog, seed random */
@@ -116,14 +114,14 @@ void main_noinetd() {
 	unsigned int i, j;
 	int val;
 	int maxsock = -1;
-	struct sockaddr_storage remoteaddr;
-	int remoteaddrlen;
 	int listensocks[MAX_LISTEN_ADDR];
-	int listensockcount = 0;
+	size_t listensockcount = 0;
 	FILE *pidfile = NULL;

+	int childpipes[MAX_UNAUTH_CLIENTS];
+	char * preauth_addrs[MAX_UNAUTH_CLIENTS];
+
 	int childsock;
-	pid_t childpid;
 	int childpipe[2];

 	/* fork */
@@ -160,11 +158,13 @@ void main_noinetd() {
 	for (i = 0; i < MAX_UNAUTH_CLIENTS; i++) {
 		childpipes[i] = -1;
 	}
+	bzero(preauth_addrs, sizeof(preauth_addrs));

 	/* Set up the listening sockets */
 	/* XXX XXX ports */
 	listensockcount = listensockets(listensocks, MAX_LISTEN_ADDR, &maxsock);
-	if (listensockcount < 0) {
+	if (listensockcount == 0)
+	{
 		dropbear_exit("No listening ports available.");
 	}

@@ -177,7 +177,7 @@ void main_noinetd() {
 		seltimeout.tv_usec = 0;

 		/* listening sockets */
-		for (i = 0; i < (unsigned int)listensockcount; i++) {
+		for (i = 0; i < listensockcount; i++) {
 			FD_SET(listensocks[i], &fds);
 		}

@@ -208,17 +208,27 @@ void main_noinetd() {
 			dropbear_exit("Listening socket error");
 		}

-		/* close fds which have been authed or closed - auth.c handles
+		/* close fds which have been authed or closed - svr-auth.c handles
 		 * closing the auth sockets on success */
 		for (i = 0; i < MAX_UNAUTH_CLIENTS; i++) {
 			if (childpipes[i] >= 0 && FD_ISSET(childpipes[i], &fds)) {
-				close(childpipes[i]);
+				m_close(childpipes[i]);
 				childpipes[i] = -1;
+				m_free(preauth_addrs[i]);
 			}
 		}

 		/* handle each socket which has something to say */
-		for (i = 0; i < (unsigned int)listensockcount; i++) {
+		for (i = 0; i < listensockcount; i++) {
+
+			struct sockaddr_storage remoteaddr;
+			socklen_t remoteaddrlen = 0;
+			size_t num_unauthed_for_addr = 0;
+			size_t num_unauthed_total = 0;
+			char * remote_addr_str = NULL;
+			pid_t fork_ret = 0;
+			size_t conn_idx = 0;
+
 			if (!FD_ISSET(listensocks[i], &fds))
 				continue;

@@ -231,29 +241,48 @@ void main_noinetd() {
 				continue;
 			}

-			/* check for max number of connections not authorised */
+			/* Limit the number of unauthenticated connections per IP */
+			remote_addr_str = getaddrstring(&remoteaddr, 0);
+
+			num_unauthed_for_addr = 0;
+			num_unauthed_total = 0;
 			for (j = 0; j < MAX_UNAUTH_CLIENTS; j++) {
-				if (childpipes[j] < 0) {
-					break;
+				if (childpipes[j] >= 0) {
+					num_unauthed_total++;
+					if (strcmp(remote_addr_str, preauth_addrs[j]) == 0) {
+						num_unauthed_for_addr++;
+					}
+				} else {
+					/* a free slot */
+					conn_idx = j;
 				}
 			}

-			if (j == MAX_UNAUTH_CLIENTS) {
-				/* no free connections */
-				/* TODO - possibly log, though this would be an easy way
-				 * to fill logs/disk */
-				close(childsock);
-				continue;
+			if (num_unauthed_total >= MAX_UNAUTH_CLIENTS
+					|| num_unauthed_for_addr >= MAX_UNAUTH_PER_IP) {
+				goto out;
 			}

 			if (pipe(childpipe) < 0) {
 				TRACE(("error creating child pipe"))
-				close(childsock);
-				continue;
+				goto out;
 			}

-			if ((childpid = fork()) == 0) {
+			fork_ret = fork();
+			if (fork_ret < 0) {
+				dropbear_log(LOG_WARNING, "error forking: %s", strerror(errno));
+				goto out;

+			} else if (fork_ret > 0) {
+
+				/* parent */
+				childpipes[conn_idx] = childpipe[0];
+				m_close(childpipe[1]);
+				preauth_addrs[conn_idx] = remote_addr_str;
+				remote_addr_str = NULL;
+
+			} else {
+
 				/* child */
 				char * addrstring = NULL;
 #ifdef DEBUG_FORKGPROF
@@ -261,6 +290,7 @@ void main_noinetd() {
 				monstartup((u_long)&_start, (u_long)&etext);
 #endif /* DEBUG_FORKGPROF */

+				m_free(remote_addr_str);
 				addrstring = getaddrstring(&remoteaddr, 1);
 				dropbear_log(LOG_INFO, "Child connection from %s", addrstring);

@@ -269,15 +299,11 @@ void main_noinetd() {
 				}

 				/* make sure we close sockets */
-				for (i = 0; i < (unsigned int)listensockcount; i++) {
-					if (m_close(listensocks[i]) == DROPBEAR_FAILURE) {
-						dropbear_exit("Couldn't close socket");
-					}
+				for (i = 0; i < listensockcount; i++) {
+					m_close(listensocks[i]);
 				}

-				if (m_close(childpipe[0]) == DROPBEAR_FAILURE) {
-					dropbear_exit("Couldn't close socket");
-				}
+				m_close(childpipe[0]);

 				/* start the session */
 				svr_session(childsock, childpipe[1],
@@ -286,12 +312,12 @@ void main_noinetd() {
 				/* don't return */
 				dropbear_assert(0);
 			}
-
-			/* parent */
-			childpipes[j] = childpipe[0];
-			if (m_close(childpipe[1]) == DROPBEAR_FAILURE
-					|| m_close(childsock) == DROPBEAR_FAILURE) {
-				dropbear_exit("Couldn't close socket");
+
+out:
+			/* This section is important for the parent too */
+			m_close(childsock);
+			if (remote_addr_str) {
+				m_free(remote_addr_str);
 			}
 		}
 	} /* for(;;) loop */
@@ -364,11 +390,11 @@ static void commonsetup() {
 }

 /* Set up listening sockets for all the requested ports */
-static int listensockets(int *sock, int sockcount, int *maxfd) {
+static size_t listensockets(int *sock, size_t sockcount, int *maxfd) {

 	unsigned int i;
 	char* errstring = NULL;
-	unsigned int sockpos = 0;
+	size_t sockpos = 0;
 	int nsock;

 	TRACE(("listensockets: %d to try\n", svr_opts.portcount))