diff options
-rw-r--r-- | dbutil.c | 13 | ||||
-rw-r--r-- | dbutil.h | 2 | ||||
-rw-r--r-- | options.h | 7 | ||||
-rw-r--r-- | svr-chansession.c | 4 | ||||
-rw-r--r-- | svr-main.c | 106 |
5 files changed, 80 insertions, 52 deletions
@@ -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)); } } @@ -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); @@ -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 */ @@ -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)) |