mirror of
https://github.com/clearml/dropbear
synced 2025-04-19 21:55:41 +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
|
#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;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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_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);
|
||||||
|
@ -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
|
||||||
|
@ -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 */
|
||||||
|
104
svr-main.c
104
svr-main.c
@ -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))
|
||||||
|
Loading…
Reference in New Issue
Block a user