* Per-IP connection unauthed connection limits

* m_close() exits fatally on failure
* other cleanups

--HG--
extra : convert_revision : bed6155e95a293c9fce7e889d283b5958f3035dc
This commit is contained in:
Matt Johnston 2006-03-08 12:41:27 +00:00
parent bf045a0564
commit 422f4f2b41
5 changed files with 80 additions and 52 deletions

View File

@ -588,20 +588,17 @@ out:
} }
#endif #endif
/* loop until the socket is closed (in case of EINTR) or /* make sure that the socket closes */
* we get and error. void m_close(int fd) {
* Returns DROPBEAR_SUCCESS or DROPBEAR_FAILURE */
int m_close(int fd) {
int val; int val;
do { do {
val = close(fd); val = close(fd);
} while (val < 0 && errno == EINTR); } while (val < 0 && errno == EINTR);
if (val == 0 || errno == EBADF) { if (val < 0 && errno != EBADF) {
return DROPBEAR_SUCCESS; /* Linux says EIO can happen */
} else { dropbear_exit("Error closing fd %d, %s", fd, strerror(errno));
return DROPBEAR_FAILURE;
} }
} }

View File

@ -55,7 +55,7 @@ char* getaddrhostname(struct sockaddr_storage * addr);
int buf_readfile(buffer* buf, const char* filename); int buf_readfile(buffer* buf, const char* filename);
int buf_getline(buffer * line, FILE * authfile); 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_malloc(size_t size);
void * m_strdup(const char * str); void * m_strdup(const char * str);
void * m_realloc(void* ptr, size_t size); void * m_realloc(void* ptr, size_t size);

View File

@ -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 /* Specify the number of clients we will allow to be connected but
* not yet authenticated. After this limit, connections are rejected */ * 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 #ifndef MAX_UNAUTH_CLIENTS
#define MAX_UNAUTH_CLIENTS 30 #define MAX_UNAUTH_CLIENTS 30
#endif #endif

View File

@ -838,9 +838,7 @@ static void execchild(struct ChanSess *chansess) {
/* close file descriptors except stdin/stdout/stderr /* close file descriptors except stdin/stdout/stderr
* Need to be sure FDs are closed here to avoid reading files as root */ * Need to be sure FDs are closed here to avoid reading files as root */
for (i = 3; i <= (unsigned int)ses.maxfd; i++) { for (i = 3; i <= (unsigned int)ses.maxfd; i++) {
if (m_close(i) == DROPBEAR_FAILURE) { m_close(i);
dropbear_exit("Error closing file desc");
}
} }
/* clear environment */ /* clear environment */

View File

@ -29,7 +29,7 @@
#include "signkey.h" #include "signkey.h"
#include "runopts.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 sigchld_handler(int dummy);
static void sigsegv_handler(int); static void sigsegv_handler(int);
static void sigintterm_handler(int fish); static void sigintterm_handler(int fish);
@ -41,8 +41,6 @@ static void main_noinetd();
#endif #endif
static void commonsetup(); static void commonsetup();
static int childpipes[MAX_UNAUTH_CLIENTS];
#if defined(DBMULTI_dropbear) || !defined(DROPBEAR_MULTI) #if defined(DBMULTI_dropbear) || !defined(DROPBEAR_MULTI)
#if defined(DBMULTI_dropbear) && defined(DROPBEAR_MULTI) #if defined(DBMULTI_dropbear) && defined(DROPBEAR_MULTI)
int dropbear_main(int argc, char ** argv) int dropbear_main(int argc, char ** argv)
@ -80,7 +78,7 @@ int main(int argc, char ** argv)
static void main_inetd() { static void main_inetd() {
struct sockaddr_storage remoteaddr; struct sockaddr_storage remoteaddr;
int remoteaddrlen; socklen_t remoteaddrlen;
char * addrstring = NULL; char * addrstring = NULL;
/* Set up handlers, syslog */ /* Set up handlers, syslog */
@ -116,14 +114,14 @@ void main_noinetd() {
unsigned int i, j; unsigned int i, j;
int val; int val;
int maxsock = -1; int maxsock = -1;
struct sockaddr_storage remoteaddr;
int remoteaddrlen;
int listensocks[MAX_LISTEN_ADDR]; int listensocks[MAX_LISTEN_ADDR];
int listensockcount = 0; size_t listensockcount = 0;
FILE *pidfile = NULL; FILE *pidfile = NULL;
int childpipes[MAX_UNAUTH_CLIENTS];
char * preauth_addrs[MAX_UNAUTH_CLIENTS];
int childsock; int childsock;
pid_t childpid;
int childpipe[2]; int childpipe[2];
/* fork */ /* fork */
@ -160,11 +158,13 @@ void main_noinetd() {
for (i = 0; i < MAX_UNAUTH_CLIENTS; i++) { for (i = 0; i < MAX_UNAUTH_CLIENTS; i++) {
childpipes[i] = -1; childpipes[i] = -1;
} }
bzero(preauth_addrs, sizeof(preauth_addrs));
/* Set up the listening sockets */ /* Set up the listening sockets */
/* XXX XXX ports */ /* XXX XXX ports */
listensockcount = listensockets(listensocks, MAX_LISTEN_ADDR, &maxsock); listensockcount = listensockets(listensocks, MAX_LISTEN_ADDR, &maxsock);
if (listensockcount < 0) { if (listensockcount == 0)
{
dropbear_exit("No listening ports available."); dropbear_exit("No listening ports available.");
} }
@ -177,7 +177,7 @@ void main_noinetd() {
seltimeout.tv_usec = 0; seltimeout.tv_usec = 0;
/* listening sockets */ /* listening sockets */
for (i = 0; i < (unsigned int)listensockcount; i++) { for (i = 0; i < listensockcount; i++) {
FD_SET(listensocks[i], &fds); FD_SET(listensocks[i], &fds);
} }
@ -208,17 +208,27 @@ void main_noinetd() {
dropbear_exit("Listening socket error"); 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 */ * closing the auth sockets on success */
for (i = 0; i < MAX_UNAUTH_CLIENTS; i++) { for (i = 0; i < MAX_UNAUTH_CLIENTS; i++) {
if (childpipes[i] >= 0 && FD_ISSET(childpipes[i], &fds)) { if (childpipes[i] >= 0 && FD_ISSET(childpipes[i], &fds)) {
close(childpipes[i]); m_close(childpipes[i]);
childpipes[i] = -1; childpipes[i] = -1;
m_free(preauth_addrs[i]);
} }
} }
/* handle each socket which has something to say */ /* 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)) if (!FD_ISSET(listensocks[i], &fds))
continue; continue;
@ -231,28 +241,47 @@ void main_noinetd() {
continue; 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++) { for (j = 0; j < MAX_UNAUTH_CLIENTS; j++) {
if (childpipes[j] < 0) { if (childpipes[j] >= 0) {
break; 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) { if (num_unauthed_total >= MAX_UNAUTH_CLIENTS
/* no free connections */ || num_unauthed_for_addr >= MAX_UNAUTH_PER_IP) {
/* TODO - possibly log, though this would be an easy way goto out;
* to fill logs/disk */
close(childsock);
continue;
} }
if (pipe(childpipe) < 0) { if (pipe(childpipe) < 0) {
TRACE(("error creating child pipe")) TRACE(("error creating child pipe"))
close(childsock); goto out;
continue;
} }
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 */ /* child */
char * addrstring = NULL; char * addrstring = NULL;
@ -261,6 +290,7 @@ void main_noinetd() {
monstartup((u_long)&_start, (u_long)&etext); monstartup((u_long)&_start, (u_long)&etext);
#endif /* DEBUG_FORKGPROF */ #endif /* DEBUG_FORKGPROF */
m_free(remote_addr_str);
addrstring = getaddrstring(&remoteaddr, 1); addrstring = getaddrstring(&remoteaddr, 1);
dropbear_log(LOG_INFO, "Child connection from %s", addrstring); dropbear_log(LOG_INFO, "Child connection from %s", addrstring);
@ -269,15 +299,11 @@ void main_noinetd() {
} }
/* make sure we close sockets */ /* make sure we close sockets */
for (i = 0; i < (unsigned int)listensockcount; i++) { for (i = 0; i < listensockcount; i++) {
if (m_close(listensocks[i]) == DROPBEAR_FAILURE) { m_close(listensocks[i]);
dropbear_exit("Couldn't close socket");
}
} }
if (m_close(childpipe[0]) == DROPBEAR_FAILURE) { m_close(childpipe[0]);
dropbear_exit("Couldn't close socket");
}
/* start the session */ /* start the session */
svr_session(childsock, childpipe[1], svr_session(childsock, childpipe[1],
@ -287,11 +313,11 @@ void main_noinetd() {
dropbear_assert(0); dropbear_assert(0);
} }
/* parent */ out:
childpipes[j] = childpipe[0]; /* This section is important for the parent too */
if (m_close(childpipe[1]) == DROPBEAR_FAILURE m_close(childsock);
|| m_close(childsock) == DROPBEAR_FAILURE) { if (remote_addr_str) {
dropbear_exit("Couldn't close socket"); m_free(remote_addr_str);
} }
} }
} /* for(;;) loop */ } /* for(;;) loop */
@ -362,11 +388,11 @@ static void commonsetup() {
} }
/* Set up listening sockets for all the requested ports */ /* 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; unsigned int i;
char* errstring = NULL; char* errstring = NULL;
unsigned int sockpos = 0; size_t sockpos = 0;
int nsock; int nsock;
TRACE(("listensockets: %d to try\n", svr_opts.portcount)) TRACE(("listensockets: %d to try\n", svr_opts.portcount))