* 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
/* 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));
}
}

View File

@ -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);

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
* 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

View File

@ -838,9 +838,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 */

View File

@ -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 */
@ -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 */
@ -362,11 +388,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))