mirror of
https://github.com/clearml/dropbear
synced 2025-04-08 06:34:23 +00:00
* Per-IP connection unauthed connection limits
* m_close() exits fatally on failure * other cleanups --HG-- extra : convert_revision : bed6155e95a293c9fce7e889d283b5958f3035dc
This commit is contained in:
parent
bf045a0564
commit
422f4f2b41
13
dbutil.c
13
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));
|
||||
}
|
||||
}
|
||||
|
||||
|
2
dbutil.h
2
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);
|
||||
|
@ -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
|
||||
|
@ -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 */
|
||||
|
106
svr-main.c
106
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 */
|
||||
@ -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))
|
||||
|
Loading…
Reference in New Issue
Block a user