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