summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
-rw-r--r--dbutil.c13
-rw-r--r--dbutil.h2
-rw-r--r--options.h7
-rw-r--r--svr-chansession.c4
-rw-r--r--svr-main.c106
5 files changed, 80 insertions, 52 deletions
diff --git a/dbutil.c b/dbutil.c
index 696bac3..15f51ba 100644
--- a/dbutil.c
+++ b/dbutil.c
@@ -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));
}
}
diff --git a/dbutil.h b/dbutil.h
index eef75e3..d74e17e 100644
--- a/dbutil.h
+++ b/dbutil.h
@@ -55,7 +55,7 @@ char* getaddrhostname(struct sockaddr_storage * addr);
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);
diff --git a/options.h b/options.h
index 1fa7d3f..1feae40 100644
--- a/options.h
+++ b/options.h
@@ -161,6 +161,13 @@ etc) slower (perhaps by 50%). Recommended for most small systems. */
/* 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
diff --git a/svr-chansession.c b/svr-chansession.c
index 8ddbe40..0916e7e 100644
--- a/svr-chansession.c
+++ b/svr-chansession.c
@@ -851,9 +851,7 @@ static void execchild(struct ChanSess *chansess) {
/* 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 */
diff --git a/svr-main.c b/svr-main.c
index aef00f6..e59d895 100644
--- a/svr-main.c
+++ b/svr-main.c
@@ -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 main_noinetd();
#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 @@ int main(int argc, char ** argv)
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,28 +241,47 @@ 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;
@@ -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))