1
0
mirror of git://git.gnupg.org/gnupg.git synced 2024-12-22 10:19:57 +01:00

Make sure not to leak file descriptors if running gpg-agent with a

command.  Restore the signal mask to solve a problem in Mono.
This commit is contained in:
Werner Koch 2009-03-19 07:09:31 +00:00
parent a3b63ac1dc
commit 588a7c34bb
11 changed files with 252 additions and 28 deletions

View File

@ -1,3 +1,7 @@
2009-03-18 Werner Koch <wk@g10code.com>
* configure.ac: Test for getrlimit.
2009-03-03 Werner Koch <wk@g10code.com> 2009-03-03 Werner Koch <wk@g10code.com>
Release 2.0.11. Release 2.0.11.

3
NEWS
View File

@ -7,6 +7,9 @@ Noteworthy changes in version 2.0.12
* New command "KEYINFO" for GPG_AGENT. GPGSM now also returns * New command "KEYINFO" for GPG_AGENT. GPGSM now also returns
information about smartcards. 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) Noteworthy changes in version 2.0.11 (2009-03-03)
------------------------------------------------- -------------------------------------------------

View File

@ -1,3 +1,8 @@
2009-03-19 Werner Koch <wk@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@g10code.com> 2009-03-17 Werner Koch <wk@g10code.com>
* command.c (cmd_get_passphrase): Break repeat loop on error. * command.c (cmd_get_passphrase): Break repeat loop on error.

View File

@ -47,6 +47,7 @@
#include "sysutils.h" #include "sysutils.h"
#include "setenv.h" #include "setenv.h"
#include "gc-opt-flags.h" #include "gc-opt-flags.h"
#include "exechelp.h"
enum cmd_and_opt_values enum cmd_and_opt_values
@ -197,6 +198,17 @@ static ARGPARSE_OPTS opts[] = {
#define TIMERTICK_INTERVAL (2) /* Seconds. */ #define TIMERTICK_INTERVAL (2) /* Seconds. */
#endif #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. */ /* Flag to indicate that a shutdown was requested. */
static int shutdown_pending; static int shutdown_pending;
@ -530,6 +542,16 @@ main (int argc, char **argv )
const char *env_file_name = NULL; 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); set_strusage (my_strusage);
gcry_control (GCRYCTL_SUSPEND_SECMEM_WARN); gcry_control (GCRYCTL_SUSPEND_SECMEM_WARN);
/* Please note that we may running SUID(ROOT), so be very CAREFUL /* Please note that we may running SUID(ROOT), so be very CAREFUL
@ -961,6 +983,27 @@ main (int argc, char **argv )
close (fd); 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> */ /* Create the info string: <name>:<pid>:<protocol_version> */
if (asprintf (&infostr, "GPG_AGENT_INFO=%s:%lu:1", if (asprintf (&infostr, "GPG_AGENT_INFO=%s:%lu:1",
socket_name, (ulong)pid ) < 0) socket_name, (ulong)pid ) < 0)
@ -1039,6 +1082,14 @@ main (int argc, char **argv )
kill (pid, SIGTERM ); kill (pid, SIGTERM );
exit (1); 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); execvp (argv[0], argv);
log_error ("failed to run the command: %s\n", strerror (errno)); log_error ("failed to run the command: %s\n", strerror (errno));
kill (pid, SIGTERM); kill (pid, SIGTERM);

View File

@ -1,3 +1,13 @@
2009-03-18 Werner Koch <wk@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@jabberwocky.com> 2009-03-13 David Shaw <dshaw@jabberwocky.com>
* http.c (do_parse_uri): Properly handle IPv6 literal addresses as * http.c (do_parse_uri): Properly handle IPv6 literal addresses as

View File

@ -108,7 +108,7 @@ status-codes.h: Makefile mkstrtable.awk exstatus.awk status.h
# #
# Module tests # 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 module_maint_tests = t-helpfile t-b64
t_common_ldadd = libcommon.a ../jnlib/libjnlib.a ../gl/libgnu.a \ t_common_ldadd = libcommon.a ../jnlib/libjnlib.a ../gl/libgnu.a \
@ -121,6 +121,7 @@ t_sysutils_LDADD = $(t_common_ldadd)
t_helpfile_LDADD = $(t_common_ldadd) t_helpfile_LDADD = $(t_common_ldadd)
t_sexputil_LDADD = $(t_common_ldadd) t_sexputil_LDADD = $(t_common_ldadd)
t_b64_LDADD = $(t_common_ldadd) t_b64_LDADD = $(t_common_ldadd)
t_exechelp_LDADD = $(t_common_ldadd)

View File

@ -1,5 +1,5 @@
/* exechelp.c - fork and exec helpers /* 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. * This file is part of GnuPG.
* *
@ -40,6 +40,16 @@
#include <sys/wait.h> #include <sys/wait.h>
#endif #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 "util.h"
#include "i18n.h" #include "i18n.h"
#include "exechelp.h" #include "exechelp.h"
@ -48,12 +58,6 @@
#define DEBUG_W32_SPAWN 1 #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 /* 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 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 here. Using a weak symbol works but is not portable - we should
@ -85,6 +89,145 @@
#endif #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 #ifdef HAVE_W32_SYSTEM
/* Helper function to build_w32_commandline. */ /* Helper function to build_w32_commandline. */
static char * static char *
@ -216,7 +359,7 @@ do_exec (const char *pgmname, const char *argv[],
void (*preexec)(void) ) void (*preexec)(void) )
{ {
char **arg_list; char **arg_list;
int n, i, j; int i, j;
int fds[3]; int fds[3];
fds[0] = fd_in; fds[0] = fd_in;
@ -259,12 +402,7 @@ do_exec (const char *pgmname, const char *argv[],
} }
/* Close all other files. */ /* Close all other files. */
n = sysconf (_SC_OPEN_MAX); close_all_fds (3, NULL);
if (n < 0)
n = MAX_OPEN_FDS;
for (i=3; i < n; i++)
close(i);
errno = 0;
if (preexec) if (preexec)
preexec (); preexec ();

View File

@ -1,5 +1,5 @@
/* exechelp.h - Definitions for the fork and exec helpers /* 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. * This file is part of GnuPG.
* *
@ -20,6 +20,25 @@
#ifndef GNUPG_COMMON_EXECHELP_H #ifndef GNUPG_COMMON_EXECHELP_H
#define 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 /* Portable function to create a pipe. Under Windows the write end is
inheritable. */ inheritable. */

View File

@ -1059,7 +1059,7 @@ AC_FUNC_FORK
AC_CHECK_FUNCS([strerror strlwr tcgetattr mmap]) AC_CHECK_FUNCS([strerror strlwr tcgetattr mmap])
AC_CHECK_FUNCS([strcasecmp strncasecmp ctermid times gmtime_r]) AC_CHECK_FUNCS([strcasecmp strncasecmp ctermid times gmtime_r])
AC_CHECK_FUNCS([unsetenv getpwnam getpwuid fcntl ftruncate]) 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([atexit raise getpagesize strftime nl_langinfo setlocale])
AC_CHECK_FUNCS([waitpid wait4 sigaction sigprocmask pipe stat getaddrinfo]) AC_CHECK_FUNCS([waitpid wait4 sigaction sigprocmask pipe stat getaddrinfo])
AC_CHECK_FUNCS([ttyname rand ftello]) AC_CHECK_FUNCS([ttyname rand ftello])

View File

@ -1,5 +1,7 @@
2009-03-18 Werner Koch <wk@g10code.com> 2009-03-18 Werner Koch <wk@g10code.com>
* apdu.c (open_pcsc_reader_wrapped): Use close_all_fds.
* command.c (cmd_learn): Add option --keypairinfo. * command.c (cmd_learn): Add option --keypairinfo.
* app.c (app_write_learn_status): Add arg FLAGS. * app.c (app_write_learn_status): Add arg FLAGS.
* app-common.h (struct app_ctx_s): Add arg FLAGS to LEARN_STATUS. * app-common.h (struct app_ctx_s): Add arg FLAGS to LEARN_STATUS.

View File

@ -57,6 +57,7 @@
#include "cardglue.h" #include "cardglue.h"
#else /* GNUPG_MAJOR_VERSION != 1 */ #else /* GNUPG_MAJOR_VERSION != 1 */
#include "scdaemon.h" #include "scdaemon.h"
#include "exechelp.h"
#endif /* GNUPG_MAJOR_VERSION != 1 */ #endif /* GNUPG_MAJOR_VERSION != 1 */
#include "apdu.h" #include "apdu.h"
@ -81,11 +82,6 @@
#define DLSTDCALL #define DLSTDCALL
#endif #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. */ /* Helper to pass parameters related to keypad based operations. */
struct pininfo_s struct pininfo_s
@ -1653,12 +1649,7 @@ open_pcsc_reader_wrapped (const char *portstr)
log_fatal ("dup2 stderr failed: %s\n", strerror (errno)); log_fatal ("dup2 stderr failed: %s\n", strerror (errno));
/* Close all other files. */ /* Close all other files. */
n = sysconf (_SC_OPEN_MAX); close_all_fds (3, NULL);
if (n < 0)
n = MAX_OPEN_FDS;
for (i=3; i < n; i++)
close(i);
errno = 0;
execl (wrapperpgm, execl (wrapperpgm,
"pcsc-wrapper", "pcsc-wrapper",