[svn] GnuPG - r4958 - in trunk: . agent common scd

svn author wk cvs at cvs.gnupg.org
Thu Mar 19 08:09:31 CET 2009


Author: wk
Date: 2009-03-19 08:09:31 +0100 (Thu, 19 Mar 2009)
New Revision: 4958

Modified:
   trunk/ChangeLog
   trunk/NEWS
   trunk/agent/ChangeLog
   trunk/agent/gpg-agent.c
   trunk/common/ChangeLog
   trunk/common/Makefile.am
   trunk/common/exechelp.c
   trunk/common/exechelp.h
   trunk/configure.ac
   trunk/scd/ChangeLog
   trunk/scd/apdu.c
Log:
Make sure not to leak file descriptors if running gpg-agent with a
command.  Restore the signal mask to solve a problem in Mono.


Modified: trunk/ChangeLog
===================================================================
--- trunk/ChangeLog	2009-03-18 11:18:56 UTC (rev 4957)
+++ trunk/ChangeLog	2009-03-19 07:09:31 UTC (rev 4958)
@@ -1,3 +1,7 @@
+2009-03-18  Werner Koch  <wk at g10code.com>
+
+	* configure.ac: Test for getrlimit.
+
 2009-03-03  Werner Koch  <wk at g10code.com>
 
 	Release 2.0.11.

Modified: trunk/agent/ChangeLog
===================================================================
--- trunk/agent/ChangeLog	2009-03-18 11:18:56 UTC (rev 4957)
+++ trunk/agent/ChangeLog	2009-03-19 07:09:31 UTC (rev 4958)
@@ -1,3 +1,8 @@
+2009-03-19  Werner Koch  <wk at g10code.com>
+
+	* gpg-agent.c (main): Save signal mask and open fds.  Restore mask
+	and close all fds prior to the exec.  Fixes bug#1013.
+
 2009-03-17  Werner Koch  <wk at g10code.com>
 
 	* command.c (cmd_get_passphrase): Break repeat loop on error.

Modified: trunk/common/ChangeLog
===================================================================
--- trunk/common/ChangeLog	2009-03-18 11:18:56 UTC (rev 4957)
+++ trunk/common/ChangeLog	2009-03-19 07:09:31 UTC (rev 4958)
@@ -1,3 +1,13 @@
+2009-03-18  Werner Koch  <wk at g10code.com>
+
+	* exechelp.c: Include sys/resource.h and sys/stat.h.
+	(get_max_open_fds): New.
+	(do_exec): Use it.
+	(get_all_open_fds): New.
+	(close_all_fds): New.
+	(do_exec): Use close_all_fds.
+	* t-exechelp.c: New.
+
 2009-03-13  David Shaw  <dshaw at jabberwocky.com>
 
 	* http.c (do_parse_uri): Properly handle IPv6 literal addresses as

Modified: trunk/scd/ChangeLog
===================================================================
--- trunk/scd/ChangeLog	2009-03-18 11:18:56 UTC (rev 4957)
+++ trunk/scd/ChangeLog	2009-03-19 07:09:31 UTC (rev 4958)
@@ -1,5 +1,7 @@
 2009-03-18  Werner Koch  <wk at g10code.com>
 
+	* apdu.c (open_pcsc_reader_wrapped): Use close_all_fds.
+
 	* command.c (cmd_learn): Add option --keypairinfo.
 	* app.c (app_write_learn_status): Add arg FLAGS.
 	* app-common.h (struct app_ctx_s): Add arg FLAGS to LEARN_STATUS.

Modified: trunk/NEWS
===================================================================
--- trunk/NEWS	2009-03-18 11:18:56 UTC (rev 4957)
+++ trunk/NEWS	2009-03-19 07:09:31 UTC (rev 4958)
@@ -7,7 +7,10 @@
  * New command "KEYINFO" for GPG_AGENT.  GPGSM now also returns
    information about smartcards.
 
+ * Make sure not to leak file descriptors if running gpg-agent with a
+   command.  Restore the signal mask to solve a problem in Mono.
 
+
 Noteworthy changes in version 2.0.11 (2009-03-03)
 -------------------------------------------------
 

Modified: trunk/agent/gpg-agent.c
===================================================================
--- trunk/agent/gpg-agent.c	2009-03-18 11:18:56 UTC (rev 4957)
+++ trunk/agent/gpg-agent.c	2009-03-19 07:09:31 UTC (rev 4958)
@@ -47,6 +47,7 @@
 #include "sysutils.h"
 #include "setenv.h"
 #include "gc-opt-flags.h"
+#include "exechelp.h"
 
 
 enum cmd_and_opt_values 
@@ -197,6 +198,17 @@
 #define TIMERTICK_INTERVAL    (2)    /* Seconds.  */
 #endif
 
+
+/* The list of open file descriptors at startup.  Note that this list
+   has been allocated using the standard malloc.  */
+static int *startup_fd_list;
+
+/* The signal mask at startup and a flag telling whether it is valid.  */
+#ifdef HAVE_SIGPROCMASK
+static sigset_t startup_signal_mask;
+static int startup_signal_mask_valid;
+#endif
+
 /* Flag to indicate that a shutdown was requested.  */
 static int shutdown_pending;
 
@@ -530,6 +542,16 @@
   const char *env_file_name = NULL;
 
 
+  /* Before we do anything else we save the list of currently open
+     file descriptors and the signal mask.  This info is required to
+     do the exec call properly. */
+  startup_fd_list = get_all_open_fds ();
+#ifdef HAVE_SIGPROCMASK
+  if (!sigprocmask (SIG_UNBLOCK, NULL, &startup_signal_mask))
+    startup_signal_mask_valid = 1;
+#endif /*HAVE_SIGPROCMASK*/
+
+  /* Set program name etc.  */
   set_strusage (my_strusage);
   gcry_control (GCRYCTL_SUSPEND_SECMEM_WARN);
   /* Please note that we may running SUID(ROOT), so be very CAREFUL
@@ -961,6 +983,27 @@
           
           close (fd);
           
+          /* Note that we used a standard fork so that Pth runs in
+             both the parent and the child.  The pth_fork would
+             terminate Pth in the child but that is not the way we
+             want it.  Thus we use a plain fork and terminate Pth here
+             in the parent.  The pth_kill may or may not work reliable
+             but it should not harm to call it.  Because Pth fiddles
+             with the signal mask the signal mask might not be correct
+             right now and thus we restore it.  That is not strictly
+             necessary but some programs falsely assume a cleared
+             signal mask.  */
+#ifdef HAVE_SIGPROCMASK
+          if (startup_signal_mask_valid)
+            {
+              if (sigprocmask (SIG_SETMASK, &startup_signal_mask, NULL))
+                log_error ("error restoring signal mask: %s\n",
+                           strerror (errno));
+            }
+          else
+            log_info ("no saved signal mask\n");
+#endif /*HAVE_SIGPROCMASK*/          
+
           /* Create the info string: <name>:<pid>:<protocol_version> */
           if (asprintf (&infostr, "GPG_AGENT_INFO=%s:%lu:1",
                         socket_name, (ulong)pid ) < 0)
@@ -1039,6 +1082,14 @@
                   kill (pid, SIGTERM );
                   exit (1);
                 }
+
+              /* Close all the file descriptors except the standard
+                 ones and those open at startup.  We explicitly don't
+                 close 0,1,2 in case something went wrong collecting
+                 them at startup.  */
+              close_all_fds (3, startup_fd_list);
+
+              /* Run the command.  */
               execvp (argv[0], argv);
               log_error ("failed to run the command: %s\n", strerror (errno));
               kill (pid, SIGTERM);

Modified: trunk/common/Makefile.am
===================================================================
--- trunk/common/Makefile.am	2009-03-18 11:18:56 UTC (rev 4957)
+++ trunk/common/Makefile.am	2009-03-19 07:09:31 UTC (rev 4958)
@@ -108,7 +108,7 @@
 #
 # Module tests
 #
-module_tests = t-convert t-percent t-gettime t-sysutils t-sexputil
+module_tests = t-convert t-percent t-gettime t-sysutils t-sexputil t-exechelp
 module_maint_tests = t-helpfile t-b64
 
 t_common_ldadd = libcommon.a ../jnlib/libjnlib.a ../gl/libgnu.a \
@@ -121,6 +121,7 @@
 t_helpfile_LDADD = $(t_common_ldadd)
 t_sexputil_LDADD = $(t_common_ldadd)
 t_b64_LDADD = $(t_common_ldadd)
+t_exechelp_LDADD = $(t_common_ldadd)
 
 
 

Modified: trunk/common/exechelp.c
===================================================================
--- trunk/common/exechelp.c	2009-03-18 11:18:56 UTC (rev 4957)
+++ trunk/common/exechelp.c	2009-03-19 07:09:31 UTC (rev 4958)
@@ -1,5 +1,5 @@
 /* exechelp.c - fork and exec helpers
- *	Copyright (C) 2004, 2007, 2008 Free Software Foundation, Inc.
+ * Copyright (C) 2004, 2007, 2008, 2009 Free Software Foundation, Inc.
  *
  * This file is part of GnuPG.
  *
@@ -40,6 +40,16 @@
 #include <sys/wait.h>
 #endif
 
+#ifdef HAVE_GETRLIMIT
+#include <sys/time.h>
+#include <sys/resource.h>
+#endif /*HAVE_GETRLIMIT*/
+
+#ifdef HAVE_STAT
+# include <sys/stat.h>
+#endif
+
+
 #include "util.h"
 #include "i18n.h"
 #include "exechelp.h"
@@ -48,12 +58,6 @@
 #define DEBUG_W32_SPAWN 1
 
 
-#ifdef _POSIX_OPEN_MAX
-#define MAX_OPEN_FDS _POSIX_OPEN_MAX
-#else
-#define MAX_OPEN_FDS 20
-#endif
-
 /* We have the usual problem here: Some modules are linked against pth
    and some are not.  However we want to use pth_fork and pth_waitpid
    here. Using a weak symbol works but is not portable - we should
@@ -85,6 +89,145 @@
 #endif
 
 
+/* Return the maximum number of currently allowed open file
+   descriptors.  Only useful on POSIX systems but returns a value on
+   other systems too.  */
+int
+get_max_fds (void)
+{
+  int max_fds = -1;
+#ifdef HAVE_GETRLIMIT
+  struct rlimit rl;
+
+# ifdef RLIMIT_NOFILE
+  if (!getrlimit (RLIMIT_NOFILE, &rl))
+    max_fds = rl.rlim_max;
+# endif
+
+# ifdef RLIMIT_OFILE
+  if (max_fds == -1 && !getrlimit (RLIMIT_OFILE, &rl))
+    max_fds = rl.rlim_max;
+
+# endif
+#endif /*HAVE_GETRLIMIT*/
+
+#ifdef _SC_OPEN_MAX
+  if (max_fds == -1)
+    {
+      long int scres = sysconf (_SC_OPEN_MAX);
+      if (scres >= 0)
+        max_fds = scres;
+    }
+#endif
+
+#ifdef _POSIX_OPEN_MAX
+  if (max_fds == -1)
+    max_fds = _POSIX_OPEN_MAX;
+#endif
+
+#ifdef OPEN_MAX
+  if (max_fds == -1)
+    max_fds = OPEN_MAX;
+#endif
+
+  if (max_fds == -1)
+    max_fds = 256;  /* Arbitrary limit.  */
+
+  return max_fds;
+}
+
+
+/* Close all file descriptors starting with descriptor FIRST.  If
+   EXCEPT is not NULL, it is expected to be a list of file descriptors
+   which shall not be closed.  This list shall be sorted in ascending
+   order with the end marked by -1.  */
+void
+close_all_fds (int first, int *except)
+{
+  int max_fd = get_max_fds ();
+  int fd, i, except_start;
+
+  if (except)
+    {
+      except_start = 0;
+      for (fd=first; fd < max_fd; fd++)
+        {
+          for (i=except_start; except[i] != -1; i++)
+            {
+              if (except[i] == fd)
+                {
+                  /* If we found the descriptor in the exception list
+                     we can start the next compare run at the next
+                     index because the exception list is ordered.  */
+                except_start = i + 1;
+                break;
+                }
+            }
+          if (except[i] == -1)
+            close (fd);
+        }
+    }
+  else
+    {
+      for (fd=first; fd < max_fd; fd++)
+        close (fd);
+    }
+
+  errno = 0;
+}
+
+
+/* Returns an array with all currently open file descriptors.  The end
+   of the array is marked by -1.  The caller needs to release this
+   array using the *standard free* and not with xfree.  This allow the
+   use of this fucntion right at startup even before libgcrypt has
+   been initialized.  Returns NULL on error and sets ERRNO
+   accordingly.  */
+int *
+get_all_open_fds (void)
+{
+  int *array;
+  size_t narray;
+  int fd, max_fd, idx;
+#ifndef HAVE_STAT
+  array = calloc (1, sizeof *array);
+  if (array)
+    array[0] = -1;
+#else /*HAVE_STAT*/
+  struct stat statbuf;
+
+  max_fd = get_max_fds ();
+  narray = 32;  /* If you change this change also t-exechelp.c.  */
+  array = calloc (narray, sizeof *array);
+  if (!array)
+    return NULL;
+  
+  /* Note:  The list we return is ordered.  */
+  for (idx=0, fd=0; fd < max_fd; fd++)
+    if (!(fstat (fd, &statbuf) == -1 && errno == EBADF))
+      {
+        if (idx+1 >= narray)
+          {
+            int *tmp;
+
+            narray += (narray < 256)? 32:256;
+            tmp = realloc (array, narray * sizeof *array);
+            if (!tmp)
+              {
+                free (array);
+                return NULL;
+              }
+            array = tmp;
+          }
+        array[idx++] = fd;
+      }
+  array[idx] = -1;
+#endif /*HAVE_STAT*/
+  return array;
+}
+
+
+
 #ifdef HAVE_W32_SYSTEM
 /* Helper function to build_w32_commandline. */
 static char *
@@ -216,7 +359,7 @@
          void (*preexec)(void) )
 {
   char **arg_list;
-  int n, i, j;
+  int i, j;
   int fds[3];
 
   fds[0] = fd_in;
@@ -259,12 +402,7 @@
     }
 
   /* Close all other files. */
-  n = sysconf (_SC_OPEN_MAX);
-  if (n < 0)
-    n = MAX_OPEN_FDS;
-  for (i=3; i < n; i++)
-    close(i);
-  errno = 0;
+  close_all_fds (3, NULL);
   
   if (preexec)
     preexec ();

Modified: trunk/common/exechelp.h
===================================================================
--- trunk/common/exechelp.h	2009-03-18 11:18:56 UTC (rev 4957)
+++ trunk/common/exechelp.h	2009-03-19 07:09:31 UTC (rev 4958)
@@ -1,5 +1,5 @@
 /* exechelp.h - Definitions for the fork and exec helpers
- *	Copyright (C) 2004 Free Software Foundation, Inc.
+ *	Copyright (C) 2004, 2009 Free Software Foundation, Inc.
  *
  * This file is part of GnuPG.
  *
@@ -20,7 +20,26 @@
 #ifndef GNUPG_COMMON_EXECHELP_H
 #define GNUPG_COMMON_EXECHELP_H
 
+/* Return the maximum number of currently allowed file descriptors.
+   Only useful on POSIX systems.  */
+int get_max_fds (void);
 
+
+/* Close all file descriptors starting with descriptor FIRST.  If
+   EXCEPT is not NULL, it is expected to be a list of file descriptors
+   which are not to close.  This list shall be sorted in ascending
+   order with its end marked by -1.  */
+void close_all_fds (int first, int *except);
+
+
+/* Returns an array with all currently open file descriptors.  The end
+   of the array is marked by -1.  The caller needs to release this
+   array using the *standard free* and not with xfree.  This allow the
+   use of this fucntion right at startup even before libgcrypt has
+   been initialized.  Returns NULL on error and sets ERRNO accordingly.  */
+int *get_all_open_fds (void);
+
+
 /* Portable function to create a pipe.  Under Windows the write end is
    inheritable.  */
 gpg_error_t gnupg_create_inbound_pipe (int filedes[2]);

Modified: trunk/configure.ac
===================================================================
--- trunk/configure.ac	2009-03-18 11:18:56 UTC (rev 4957)
+++ trunk/configure.ac	2009-03-19 07:09:31 UTC (rev 4958)
@@ -1059,7 +1059,7 @@
 AC_CHECK_FUNCS([strerror strlwr tcgetattr mmap])
 AC_CHECK_FUNCS([strcasecmp strncasecmp ctermid times gmtime_r])
 AC_CHECK_FUNCS([unsetenv getpwnam getpwuid fcntl ftruncate])
-AC_CHECK_FUNCS([gettimeofday getrusage setrlimit clock_gettime])
+AC_CHECK_FUNCS([gettimeofday getrusage getrlimit setrlimit clock_gettime])
 AC_CHECK_FUNCS([atexit raise getpagesize strftime nl_langinfo setlocale])
 AC_CHECK_FUNCS([waitpid wait4 sigaction sigprocmask pipe stat getaddrinfo])
 AC_CHECK_FUNCS([ttyname rand ftello])

Modified: trunk/scd/apdu.c
===================================================================
--- trunk/scd/apdu.c	2009-03-18 11:18:56 UTC (rev 4957)
+++ trunk/scd/apdu.c	2009-03-19 07:09:31 UTC (rev 4958)
@@ -57,6 +57,7 @@
 #include "cardglue.h"
 #else /* GNUPG_MAJOR_VERSION != 1 */
 #include "scdaemon.h"
+#include "exechelp.h"
 #endif /* GNUPG_MAJOR_VERSION != 1 */
 
 #include "apdu.h"
@@ -81,11 +82,6 @@
 #define DLSTDCALL
 #endif
 
-#ifdef _POSIX_OPEN_MAX
-#define MAX_OPEN_FDS _POSIX_OPEN_MAX
-#else
-#define MAX_OPEN_FDS 20
-#endif
 
 /* Helper to pass parameters related to keypad based operations. */
 struct pininfo_s
@@ -1653,12 +1649,7 @@
         log_fatal ("dup2 stderr failed: %s\n", strerror (errno));
 
       /* Close all other files. */
-      n = sysconf (_SC_OPEN_MAX);
-      if (n < 0)
-        n = MAX_OPEN_FDS;
-      for (i=3; i < n; i++)
-        close(i);
-      errno = 0;
+      close_all_fds (3, NULL);
 
       execl (wrapperpgm,
              "pcsc-wrapper",




More information about the Gnupg-commits mailing list