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

Attachment: pgpMptGIoHO2r.pgp
Description: PGP signature

Reply via email to