Sorry for not responding for so long, but I still think this security issue should be fixed in woody. I have made a diff between 3.22 and 3.26 and removed everything obviously not security related (unfortunately it is still ~600 lines). It should contain the changes corresponding to the following entries in the 3.26 HISTORY file:
Version 3.26, 2003.08.29 urgency: MEDIUM: * Fixed new child signal handler, introduced in 3.25, which was buggy in pthreads environments * Fixed problem where the accept() can block indefinately if the user or OS has discarded the connection. Version 3.25, 2003.07.25, urgency: HIGH: * Fixed buggy SIGCHLD handling using patch supplied by Nalin Dahyabhai of Red Hat. * Fixed buggy SIGCHLD handling patch (their new pipe descriptors were leaked), removed CRIT_LIBWRAP which needs to be inside CRIT_NTOA anyway. * REMOTE_HOST variable is always placed in the environment of procesess spawned with 'exec'. I have not included the following changes since RSA blinding has been backported to woody openssl: Version 3.24, 2002.04.23, urgency: HIGH: * Fixed bug whereby RSA blinding was called in client mode even when no cert was in use. Version 3.23, 2002.04.02, urgency: HIGH: * Enabled RSA blinding on all RSA keys to prevent RSA timing attack that was proven to be exploitable by David Brumley and Dan Boneh. See http://crypto.stanford.edu/~dabo/abstracts/ssl-timing.html for more details about the attack. If you have an OpenSSL library that has RSA blinding on by default (>=0.9.7b or >=0.9.6j) then you do not need to upgrade, but it is still suggested. The patched version compiles cleanly. However, I have NOT tested it since I have no working stunnel setup. I hope the patch helps nontheless. Cheers, Stefan
diff -rch stunnel-3.22/client.c stunnel-3.26/client.c
*** stunnel-3.22/client.c 2001-12-23 20:41:32.000000000 +0100
--- stunnel-3.26/client.c 2003-08-02 06:33:41.000000000 +0200
***************
*** 591,609 ****
struct request_info request;
int result;
! enter_critical_section(CRIT_LIBWRAP); /* libwrap is not mt-safe */
request_init(&request, RQ_DAEMON, options.servname, RQ_FILE, c->local_rfd, 0);
fromhost(&request);
result=hosts_access(&request);
- leave_critical_section(CRIT_LIBWRAP);
if (!result) {
- enter_critical_section(CRIT_NTOA); /* inet_ntoa is not mt-safe */
log(LOG_WARNING, "Connection from %s:%d REFUSED by libwrap",
inet_ntoa(c->addr.sin_addr), ntohs(c->addr.sin_port));
leave_critical_section(CRIT_NTOA);
log(LOG_DEBUG, "See hosts_access(5) for details");
return -1; /* FAILED */
}
#endif
return 0; /* OK */
}
--- 599,617 ----
struct request_info request;
int result;
! enter_critical_section(CRIT_NTOA); /* libwrap is not mt-safe, and uses
! inet_ntoa internally, so wrap thusly */
request_init(&request, RQ_DAEMON, options.servname, RQ_FILE, c->local_rfd, 0);
fromhost(&request);
result=hosts_access(&request);
if (!result) {
log(LOG_WARNING, "Connection from %s:%d REFUSED by libwrap",
inet_ntoa(c->addr.sin_addr), ntohs(c->addr.sin_port));
leave_critical_section(CRIT_NTOA);
log(LOG_DEBUG, "See hosts_access(5) for details");
return -1; /* FAILED */
}
+ leave_critical_section(CRIT_NTOA);
#endif
return 0; /* OK */
}
***************
*** 686,691 ****
--- 694,702 ----
char env[3][STRLEN], name[STRLEN];
int fd[2];
X509 *peer;
+ #ifdef HAVE_PTHREAD_SIGMASK
+ sigset_t newmask;
+ #endif
if (options.option & OPT_PTY) {
char tty[STRLEN];
***************
*** 711,726 ****
if(!options.foreground)
dup2(fd[1], 2);
closesocket(fd[1]);
if(c->ip) {
putenv("LD_PRELOAD=" libdir "/stunnel.so");
/* For Tru64 _RLD_LIST is used instead */
putenv("_RLD_LIST=" libdir "/stunnel.so:DEFAULT");
addr.s_addr = c->ip;
- safecopy(env[0], "REMOTE_HOST=");
- enter_critical_section(CRIT_NTOA); /* inet_ntoa is not mt-safe */
- safeconcat(env[0], inet_ntoa(addr));
- leave_critical_section(CRIT_NTOA);
- putenv(env[0]);
}
if(c->ssl) {
peer=SSL_get_peer_certificate(c->ssl);
--- 722,738 ----
if(!options.foreground)
dup2(fd[1], 2);
closesocket(fd[1]);
+ signal(SIGCHLD, SIG_DFL);
+ safecopy(env[0], "REMOTE_HOST=");
+ enter_critical_section(CRIT_NTOA); /* inet_ntoa is not mt-safe */
+ safeconcat(env[0], inet_ntoa(c->addr.sin_addr));
+ leave_critical_section(CRIT_NTOA);
+ putenv(env[0]);
if(c->ip) {
putenv("LD_PRELOAD=" libdir "/stunnel.so");
/* For Tru64 _RLD_LIST is used instead */
putenv("_RLD_LIST=" libdir "/stunnel.so:DEFAULT");
addr.s_addr = c->ip;
}
if(c->ssl) {
peer=SSL_get_peer_certificate(c->ssl);
***************
*** 738,743 ****
--- 750,759 ----
X509_free(peer);
}
}
+ #ifdef HAVE_PTHREAD_SIGMASK
+ sigemptyset(&newmask);
+ sigprocmask(SIG_SETMASK, &newmask, NULL);
+ #endif
execvp(options.execname, options.execargs);
ioerror(options.execname); /* execv failed */
_exit(1);
diff -rch stunnel-3.22/log.c stunnel-3.26/log.c
*** stunnel-3.22/log.c 2001-10-30 17:48:39.000000000 +0100
--- stunnel-3.26/log.c 2003-07-25 23:25:55.000000000 +0200
***************
*** 72,81 ****
#else /* USE_WIN32 */
void log_open() { /* Unix version */
! if(options.output_file)
! outfile=fopen(options.output_file, "a");
! if(outfile)
! return; /* It was possible o open a log file */
if(!options.foreground) {
#ifdef __ultrix__
openlog("stunnel", LOG_PID);
--- 72,89 ----
#else /* USE_WIN32 */
void log_open() { /* Unix version */
! if(options.output_file) {
! int fd;
! fd=open(options.output_file, O_CREAT|O_WRONLY|O_APPEND, 0640);
! if (fd>=0) { /* file opened or created */
! #ifdef FD_CLOEXEC
! fcntl(fd, F_SETFD, FD_CLOEXEC);
! #endif
! outfile=fdopen(fd, "a");
! if(outfile)
! return; /* It was possible to open a log file */
! }
! }
if(!options.foreground) {
#ifdef __ultrix__
openlog("stunnel", LOG_PID);
diff -rch stunnel-3.22/prototypes.h stunnel-3.26/prototypes.h
*** stunnel-3.22/prototypes.h 2001-11-11 20:16:01.000000000 +0100
--- stunnel-3.26/prototypes.h 2003-08-02 01:43:11.000000000 +0200
***************
*** 53,59 ****
/* Prototypes for sthreads.c */
typedef enum {
! CRIT_KEYGEN, CRIT_LIBWRAP, CRIT_NTOA, CRIT_CLIENTS, CRIT_SECTIONS
} section_code;
void enter_critical_section(section_code);
--- 55,61 ----
/* Prototypes for sthreads.c */
typedef enum {
! CRIT_KEYGEN, CRIT_NTOA, CRIT_CLIENTS, CRIT_SECTIONS
} section_code;
void enter_critical_section(section_code);
diff -rch stunnel-3.22/pty.c stunnel-3.26/pty.c
*** stunnel-3.22/pty.c 2001-10-14 17:01:48.000000000 +0200
--- stunnel-3.26/pty.c 2003-08-02 01:43:03.000000000 +0200
***************
*** 61,78 ****
* returned (the buffer must be able to hold at least 64 characters).
*/
int pty_allocate(int *ptyfd, int *ttyfd, char *namebuf, int namebuflen) {
#if defined(HAVE_OPENPTY) || defined(BSD4_4)
/* openpty(3) exists in OSF/1 and some other os'es */
! char buf[64];
int i;
! i = openpty(ptyfd, ttyfd, buf, NULL, NULL);
if (i < 0) {
sockerror("openpty");
return -1;
}
! safecopy(namebuf, buf); /* possible truncation */
return 0;
#else /* HAVE_OPENPTY */
#ifdef HAVE__GETPTY
--- 61,88 ----
* returned (the buffer must be able to hold at least 64 characters).
*/
+ /*
+ * This code is based on OpenSSH's 'sshpty.c' file, which was based on
+ * code by Tatu Ylonen <[EMAIL PROTECTED]> for the original SSH.
+ */
+
int pty_allocate(int *ptyfd, int *ttyfd, char *namebuf, int namebuflen) {
#if defined(HAVE_OPENPTY) || defined(BSD4_4)
/* openpty(3) exists in OSF/1 and some other os'es */
! char *name;
int i;
! i = openpty(ptyfd, ttyfd, NULL, NULL, NULL);
if (i < 0) {
sockerror("openpty");
return -1;
}
! name = ttyname(*ttyfd);
! if ( ! name ) {
! log(LOG_ERR, "openpty returns device for which ttyname fails");
! return -1;
! }
! safecopy(namebuf, name); /* possible truncation */
return 0;
#else /* HAVE_OPENPTY */
#ifdef HAVE__GETPTY
diff -rch stunnel-3.22/sthreads.c stunnel-3.26/sthreads.c
*** stunnel-3.22/sthreads.c 2001-11-11 16:06:22.000000000 +0100
--- stunnel-3.26/sthreads.c 2003-08-02 01:42:28.000000000 +0200
***************
*** 180,185 ****
--- 180,190 ----
return 0L;
}
+ extern int signal_pipe[2];
+ static void null_handler(int signum) {
+ signal(SIGCHLD, null_handler);
+ }
+
int create_client(int ls, int s, void *(*cli)(void *)) {
switch(fork()) {
case -1: /* error */
***************
*** 187,193 ****
return -1;
case 0: /* child */
closesocket(ls);
! signal(SIGCHLD, local_handler);
cli((void *)s);
exit(0);
default: /* parent */
--- 192,200 ----
return -1;
case 0: /* child */
closesocket(ls);
! signal(SIGCHLD, null_handler);
! close(signal_pipe[0]);
! close(signal_pipe[1]);
cli((void *)s);
exit(0);
default: /* parent */
diff -rch stunnel-3.22/stunnel.c stunnel-3.26/stunnel.c
*** stunnel-3.22/stunnel.c 2001-12-20 08:53:54.000000000 +0100
--- stunnel-3.26/stunnel.c 2003-08-30 06:34:57.000000000 +0200
***************
*** 48,74 ****
#endif
/* Prototypes */
! static void daemon_loop();
#ifndef USE_WIN32
! static void daemonize();
! static void create_pid();
! static void delete_pid();
#endif
/* Socket functions */
! static int listen_local();
/* Error/exceptions handling functions */
void ioerror(char *);
void sockerror(char *);
void log_error(int, int, char *);
static char *my_strerror(int);
#ifdef USE_FORK
! static void sigchld_handler(int);
#endif
#ifndef USE_WIN32
! void local_handler(int);
static void signal_handler(int);
#else
static BOOL CtrlHandler(DWORD);
#endif
--- 50,81 ----
#endif
/* Prototypes */
! static void daemon_loop(void);
#ifndef USE_WIN32
! static void daemonize(void);
! static void create_pid(void);
! static void delete_pid(void);
#endif
/* Socket functions */
! static int listen_local(void);
/* Error/exceptions handling functions */
void ioerror(char *);
void sockerror(char *);
void log_error(int, int, char *);
static char *my_strerror(int);
+ #ifdef USE_PTHREAD
+ static void exec_status(void);
+ #endif
#ifdef USE_FORK
! static void client_status(void);
#endif
#ifndef USE_WIN32
! static void sigchld_handler(int);
static void signal_handler(int);
+ static int signal_pipe[2];
+ static char signal_buffer[16];
#else
static BOOL CtrlHandler(DWORD);
#endif
***************
*** 139,144 ****
--- 146,159 ----
log(LOG_NOTICE, "%s", stunnel_info());
if(options.option & OPT_DAEMON) { /* daemon mode */
#ifndef USE_WIN32
+ if(pipe(signal_pipe)) {
+ ioerror("pipe");
+ exit(1);
+ }
+ #ifdef FD_CLOEXEC
+ fcntl(signal_pipe[0], F_SETFD, FD_CLOEXEC);
+ fcntl(signal_pipe[1], F_SETFD, FD_CLOEXEC);
+ #endif
if(!(options.option & OPT_FOREGROUND))
daemonize();
create_pid();
***************
*** 160,170 ****
return 0; /* success */
}
! static void daemon_loop() {
int ls, s;
struct sockaddr_in addr;
int addrlen;
int max_clients, fds_ulimit=-1;
#if defined HAVE_SYSCONF
fds_ulimit=sysconf(_SC_OPEN_MAX);
--- 175,188 ----
return 0; /* success */
}
! static void daemon_loop(void) {
int ls, s;
struct sockaddr_in addr;
int addrlen;
int max_clients, fds_ulimit=-1;
+ int ready;
+ int old_val;
+ fd_set read_fds;
#if defined HAVE_SYSCONF
fds_ulimit=sysconf(_SC_OPEN_MAX);
***************
*** 188,212 ****
log(LOG_ERR, "Memory allocation failed");
exit(1);
}
! max_clients=max_fds>=256 ? max_fds*125/256 : (max_fds-6)/2;
log(LOG_NOTICE, "FD_SETSIZE=%d, file ulimit=%d%s -> %d clients allowed",
FD_SETSIZE, fds_ulimit, fds_ulimit<0?" (unlimited)":"", max_clients);
ls=listen_local();
options.clients=0;
! #ifdef USE_FORK
/* Handle signals about dead children */
signal(SIGCHLD, sigchld_handler);
#endif /* defined USE_FORK */
- #ifdef USE_PTHREAD
- /* Handle signals about dead local processes */
- signal(SIGCHLD, local_handler);
- #endif /* defined USE_PTHREAD */
while(1) {
addrlen=sizeof(addr);
do {
! s=accept(ls, (struct sockaddr *)&addr, &addrlen);
! } while(s<0 && get_last_socket_error()==EINTR);
! if(s<0) {
sockerror("accept");
continue;
}
--- 206,262 ----
log(LOG_ERR, "Memory allocation failed");
exit(1);
}
! max_clients=max_fds>=256 ? max_fds*125/256 : (max_fds-8)/2;
log(LOG_NOTICE, "FD_SETSIZE=%d, file ulimit=%d%s -> %d clients allowed",
FD_SETSIZE, fds_ulimit, fds_ulimit<0?" (unlimited)":"", max_clients);
ls=listen_local();
options.clients=0;
! #ifndef USE_WIN32
/* Handle signals about dead children */
signal(SIGCHLD, sigchld_handler);
#endif /* defined USE_FORK */
while(1) {
addrlen=sizeof(addr);
do {
! FD_ZERO(&read_fds);
! FD_SET(ls, &read_fds);
! #ifndef USE_WIN32
! FD_SET(signal_pipe[0], &read_fds);
! ready=select((ls > signal_pipe[0]) ? ls + 1 : signal_pipe[0] + 1,
! &read_fds, NULL, NULL, NULL);
! #else
! ready=select(ls + 1, &read_fds, NULL, NULL, NULL);
! #endif
! } while(ready<=0 && get_last_error()==EINTR);
! #ifndef USE_WIN32
! if(FD_ISSET(signal_pipe[0], &read_fds)) {
! read(signal_pipe[0], signal_buffer, sizeof(signal_buffer));
! #ifdef USE_PTHREAD
! exec_status(); /* Report status of 'exec' process */
! #else
! client_status(); /* Report the status of the child process */
! #endif
! }
! #endif
! /* if we didn't also have a new connection, go back to waiting */
! if(!FD_ISSET(ls, &read_fds)) {
! continue;
! }
! /* if we also have a new connection, process it. First, put
! * the socket into nonblocking mode so that network errors
! * won't hang the next call "forever". */
! #ifndef USE_WIN32
! old_val = fcntl(ls, F_GETFL, 0);
! if (old_val >= 0)
! fcntl(ls, F_SETFL, old_val | O_NONBLOCK);
! #endif
! s=accept(ls, (struct sockaddr *)&addr, &addrlen);
! #ifndef USE_WIN32
! if (old_val >= 0)
! fcntl(ls, F_SETFL, old_val);
! #endif
!
! if(s<0) {
sockerror("accept");
continue;
}
***************
*** 370,376 ****
sockerror("listen");
exit(1);
}
!
#ifndef USE_WIN32
if(options.setgid_group) {
struct group *gr;
--- 420,429 ----
sockerror("listen");
exit(1);
}
! #ifdef FD_CLOEXEC
! fcntl(ls, F_SETFD, FD_CLOEXEC); /* close socket in child execvp */
! #endif
!
#ifndef USE_WIN32
if(options.setgid_group) {
struct group *gr;
***************
*** 575,632 ****
}
#ifdef USE_FORK
! static void sigchld_handler(int sig) { /* Dead children detected */
int pid, status;
#ifdef HAVE_WAIT_FOR_PID
while((pid=wait_for_pid(-1, &status, WNOHANG))>0) {
! options.clients--; /* One client less */
#else
! if((pid=wait(&status))>0) {
! options.clients--; /* One client less */
#endif
#ifdef WIFSIGNALED
if(WIFSIGNALED(status)) {
! log(LOG_DEBUG, "%s[%d] terminated on signal %d (%d left)",
! options.servname, pid, WTERMSIG(status), options.clients);
} else {
! log(LOG_DEBUG, "%s[%d] finished with code %d (%d left)",
! options.servname, pid, WEXITSTATUS(status), options.clients);
}
}
#else
! log(LOG_DEBUG, "%s[%d] finished with code %d (%d left)",
! options.servname, pid, status, options.clients);
}
#endif
- signal(SIGCHLD, sigchld_handler);
}
#endif
- #ifndef USE_WIN32
! void local_handler(int sig) { /* Dead of local (-l) process detected */
int pid, status;
#ifdef HAVE_WAIT_FOR_PID
while((pid=wait_for_pid(-1, &status, WNOHANG))>0) {
#else
! if((pid=wait(&status))>0) {
#endif
#ifdef WIFSIGNALED
! if(WIFSIGNALED(status)) {
! log(LOG_DEBUG, "Local process %s (PID=%lu) terminated on signal %d",
! options.servname, pid, WTERMSIG(status));
! } else {
! log(LOG_DEBUG, "Local process %s (PID=%lu) finished with code %d",
! options.servname, pid, WEXITSTATUS(status));
! }
#else
! log(LOG_DEBUG, "Local process %s (PID=%lu) finished with status %d",
! options.servname, pid, status);
#endif
! }
! signal(SIGCHLD, local_handler);
}
static void signal_handler(int sig) { /* Signal handler */
--- 628,694 ----
}
#ifdef USE_FORK
! static void client_status(void) { /* dead children detected */
int pid, status;
#ifdef HAVE_WAIT_FOR_PID
while((pid=wait_for_pid(-1, &status, WNOHANG))>0) {
! option.clients--; /* one client less */
#else
! if((pid=wait(&status))>0) {
! option.clients--; /* one client less */
#endif
#ifdef WIFSIGNALED
if(WIFSIGNALED(status)) {
! log(LOG_DEBUG, "Process %d terminated on signal %d (%d left)",
! pid, WTERMSIG(status), num_clients);
} else {
! log(LOG_DEBUG, "Process %d finished with code %d (%d left)",
! pid, WEXITSTATUS(status), num_clients);
}
}
#else
! log(LOG_DEBUG, "Process %d finished with code %d (%d left)",
! pid, status, num_clients);
}
#endif
}
#endif
! #ifdef USE_PTHREAD
! static void exec_status(void) { /* dead local ('exec') process detected */
int pid, status;
#ifdef HAVE_WAIT_FOR_PID
while((pid=wait_for_pid(-1, &status, WNOHANG))>0) {
#else
! if((pid=wait(&status))>0) {
#endif
#ifdef WIFSIGNALED
! if(WIFSIGNALED(status)) {
! log(LOG_INFO, "Local process %d terminated on signal %d",
! pid, WTERMSIG(status));
! } else {
! log(LOG_INFO, "Local process %d finished with code %d",
! pid, WEXITSTATUS(status));
! }
#else
! log(LOG_INFO, "Local process %d finished with status %d",
! pid, status);
#endif
! }
! }
! #endif /* !defined USE_WIN32 */
!
!
! #ifndef USE_WIN32
! static void sigchld_handler(int sig) { /* Death of child process detected */
! int save_errno=errno;
!
! write(signal_pipe[1], signal_buffer, 1);
! signal(SIGCHLD, sigchld_handler);
! errno=save_errno;
}
static void signal_handler(int sig) { /* Signal handler */
pgpMptGIoHO2r.pgp
Description: PGP signature

