diff options
| -rw-r--r-- | assuan/ChangeLog | 7 | ||||
| -rw-r--r-- | assuan/assuan-pipe-connect.c | 68 | ||||
| -rw-r--r-- | gpgme/ChangeLog | 46 | ||||
| -rw-r--r-- | gpgme/Makefile.am | 6 | ||||
| -rw-r--r-- | gpgme/engine-gpgconf.c | 11 | ||||
| -rw-r--r-- | gpgme/engine-gpgsm.c | 39 | ||||
| -rw-r--r-- | gpgme/gpgme-w32spawn.c | 434 | ||||
| -rw-r--r-- | gpgme/gpgme.h | 2 | ||||
| -rw-r--r-- | gpgme/posix-io.c | 175 | ||||
| -rw-r--r-- | gpgme/priv-io.h | 21 | ||||
| -rw-r--r-- | gpgme/rungpg.c | 130 | ||||
| -rw-r--r-- | gpgme/util.h | 6 | ||||
| -rw-r--r-- | gpgme/version.c | 7 | ||||
| -rw-r--r-- | gpgme/w32-glib-io.c | 221 | ||||
| -rw-r--r-- | gpgme/w32-io.c | 224 | ||||
| -rw-r--r-- | gpgme/w32-qt-io.cpp | 220 | ||||
| -rw-r--r-- | gpgme/w32-util.c | 154 | 
17 files changed, 1354 insertions, 417 deletions
| diff --git a/assuan/ChangeLog b/assuan/ChangeLog index 22bc7353..2e01943b 100644 --- a/assuan/ChangeLog +++ b/assuan/ChangeLog @@ -1,3 +1,10 @@ +2008-06-25  Marcus Brinkmann  <[email protected]> + +	* assuan-pipe-connect.c (struct spawn_fd_item_s): Add new members. +	(HANDLE_TRANSLATION): New macro. +	(pipe_connect_gpgme): Adjust caller of _gpgme_io_spawn. +	[HANDLE_TRANSLATION]: Return translated handles. +  2008-02-14  Werner Koch  <[email protected]>  	* assuan-pipe-connect.c (_gpgme_io_spawn): Adjust prototype. diff --git a/assuan/assuan-pipe-connect.c b/assuan/assuan-pipe-connect.c index 32a62191..a0091c6a 100644 --- a/assuan/assuan-pipe-connect.c +++ b/assuan/assuan-pipe-connect.c @@ -41,10 +41,19 @@  #ifdef _ASSUAN_IN_GPGME_BUILD_ASSUAN +/* From GPGME priv-io.h  */ +struct spawn_fd_item_s +{ +  int fd; +  int dup_to; +  int peer_name; +  int arg_loc; +}; + +  int _gpgme_io_pipe (int filedes[2], int inherit_idx);  int _gpgme_io_spawn (const char *path, char **argv, -		     struct spawn_fd_item_s *fd_child_list, -		     struct spawn_fd_item_s *fd_parent_list, pid_t *r_pid); +		     struct spawn_fd_item_s *fd_list, pid_t *r_pid);  #endif  /* Hacks for Slowaris.  */ @@ -566,13 +575,6 @@ socketpair_connect (assuan_context_t *ctx,  #define pipe_connect pipe_connect_gpgme -/* From GPGME priv-io.h  */ -struct spawn_fd_item_s -{ -  int fd; -  int dup_to; -}; -  /* W32 version of the pipe connection code. */  static assuan_error_t  pipe_connect_gpgme (assuan_context_t *ctx, @@ -583,14 +585,25 @@ pipe_connect_gpgme (assuan_context_t *ctx,  {    assuan_error_t err;    int res; +  int idx; +  int nr;    int rp[2];    int wp[2];    char mypidstr[50]; -  struct spawn_fd_item_s child_fds[3]; /* stdin, stdout, terminating -1 */ +  struct spawn_fd_item_s *child_fds;    if (!ctx || !name || !argv || !argv[0])      return _assuan_error (ASSUAN_Invalid_Value); +  /* stdin, stdout, terminating -1 */ +  nr = 3; +  for (idx = 0; fd_child_list[idx] != -1; idx++) +    nr++; + +  child_fds = calloc (nr, sizeof *child_fds); +  if (! child_fds) +    return _assuan_error (ASSUAN_Out_Of_Core); +    /* Actually, GPGME does this for us.  But we plan to reuse this code       in the generic assuan.  */    fix_signals (); @@ -631,19 +644,28 @@ pipe_connect_gpgme (assuan_context_t *ctx,       the old value, changeit, create proces and restore it, is not       thread safe.  */ -  /* Parent list is same as client list.  Note that GPGME will dup nul -     to stderr even if the caller wants to inherit the handle for -     it.  */ +  nr = 0;    /* Server stdout is its write end of our read pipe.  */ -  child_fds[0].fd = rp[1]; -  child_fds[0].dup_to = 1; +  child_fds[nr].fd = rp[1]; +  child_fds[nr].dup_to = 1; +  nr++;    /* Server stdin is its read end of our write pipe.  */ -  child_fds[1].fd = wp[0]; -  child_fds[1].dup_to = 0; -  child_fds[2].fd = -1; +  child_fds[nr].fd = wp[0]; +  child_fds[nr].dup_to = 0; +  nr++; + +  for (idx = 0; fd_child_list[idx] != -1; idx++) +    { +      child_fds[nr].fd = fd_child_list[idx]; +      child_fds[nr].dup_to = -1; +      nr++; +    } + +  child_fds[nr].fd = -1; +  child_fds[nr].dup_to = -1;    /* Start the process.  */ -  res = _gpgme_io_spawn (name, argv, child_fds, child_fds, NULL); +  res = _gpgme_io_spawn (name, argv, child_fds, NULL);    if (res == -1)      {        _assuan_log_printf ("CreateProcess failed: %s\n", strerror (errno)); @@ -653,6 +675,14 @@ pipe_connect_gpgme (assuan_context_t *ctx,        _gpgme_io_close (wp[1]);        return _assuan_error (ASSUAN_General_Error);      } +  else +    { +      /* For W32, the user needs to know the server-local names of the +	 inherited handles.  Return them here.  */ +      for (idx = 0; fd_child_list[idx] != -1; idx++) +	/* We add 2 to skip over the stdin/stdout pair.  */ +	fd_child_list[idx] = child_fds[idx + 2].peer_name; +    }    (*ctx)->pid = 0;  /* We don't use the PID. */ diff --git a/gpgme/ChangeLog b/gpgme/ChangeLog index 8c359598..2587f16c 100644 --- a/gpgme/ChangeLog +++ b/gpgme/ChangeLog @@ -1,3 +1,49 @@ +2008-06-25  Marcus Brinkmann  <[email protected]> + +	* gpgme-w32spawn.c: New file. +	* Makefile.am (libexec_PROGRAMS) [HAVE_W32_SYSTEM]: New variable +	with gpgme-w32spawn. +	* engine-gpgsm.c (gpgsm_new): Use server translated handles. +	(gpgsm_set_locale): Return early if locale value is NULL. +	* util.h (_gpgme_mkstemp) +	(_gpgme_get_w32spawn_path) [HAVE_W32_SYSTEM]: New function +	prototypes. +	* w32-util.c: Include <stdint.h>, <sys/stat.h> and <unistd.h>. +	(letters, mkstemp, _gpgme_mkstemp, _gpgme_get_w32spawn_path): New +	functions. +	* rungpg.c (gpg_decrypt, gpg_encrypt, gpg_encrypt_sign) +	(gpg_genkey, gpg_import, gpg_verify, gpg_sign): Pass data over +	special filename FD rather than stdin. +	(struct arg_and_data_s): Add member ARG_LOCP. +	(struct fd_data_map_s): Add member ARG_LOC. +	(struct engine_gpg): Add member ARG_LOC to status and colon. +	(_add_arg, add_arg_with_locp): New function. +	(add_arg_ext): Reimplement in terms of _add_arg. +	(gpg_new): Remember argument location for status FD. +	(build_argv): Set argument location if requested.  Also set +	argument location of fd_data_map for data items. +	(start): Adjust caller of _gpgme_io_spawn. +	* priv-io.h (struct spawn_fd_item_s): Add members peer_name and +	arg_loc. +	(_gpgme_io_spawn): Remove parent fd list argument. +	* posix-io.c (get_max_fds): New function. +	(_gpgme_io_dup): Add tracing. +	(_gpgme_io_spawn): Remove parent fd list.  Change meaning of child +	fd list to contain all child fds that should be inherited.  Close +	all other file descriptors after fork. +	* w32-io.c, w32-glib-io.c, w32-qt-io.c(_gpgme_io_spawn): Remove +	parent fd list.  Change meaning of child fd list to contain all +	child fds that should be inherited.  Do not inherit any file +	descriptors, but DuplicateHandle them.  Spawn process through +	wrapper process.  Provide wrapper process with a temporary file +	containing handle translation data.  Return translated handle +	names. +	* w32-io.c (reader): Add more tracing output. +	(_gpgme_io_read): Likewise. +	* engine-gpgconf.c (gpgconf_read): Adjust caller of +	_gpgme_io_spawn. +	* version.c (_gpgme_get_program_version): Likewise. +  2008-06-20  Werner Koch  <[email protected]>  	* engine-gpgconf.c (gpgconf_read): Change ARGV initialization for diff --git a/gpgme/Makefile.am b/gpgme/Makefile.am index bd0191ae..c3af79db 100644 --- a/gpgme/Makefile.am +++ b/gpgme/Makefile.am @@ -142,6 +142,12 @@ AM_CFLAGS = @PTH_CFLAGS@ @GLIB_CFLAGS@ @QT4_CORE_CFLAGS@  if HAVE_W32_SYSTEM +# Windows provides us with an endless stream of Tough Love.  To spawn +# processes with a controlled set of inherited handles, we need a +# wrapper process. +libexec_PROGRAMS = gpgme-w32spawn + +  LTRCCOMPILE = $(LIBTOOL) --mode=compile $(RC) \       `echo $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) | \       sed -e 's/-I/--include-dir /g;s/-D/--define /g'` diff --git a/gpgme/engine-gpgconf.c b/gpgme/engine-gpgconf.c index 6a9cf2be..3d107d45 100644 --- a/gpgme/engine-gpgconf.c +++ b/gpgme/engine-gpgconf.c @@ -201,8 +201,8 @@ gpgconf_read (void *engine, char *arg1, char *arg2,    int linelen = 0;    char *argv[4] = { NULL /* file_name */, NULL, NULL, NULL };    int rp[2]; -  struct spawn_fd_item_s pfd[] = { {0, -1}, {-1, -1} }; -  struct spawn_fd_item_s cfd[] = { {-1, 1 /* STDOUT_FILENO */}, {-1, -1} }; +  struct spawn_fd_item_s cfd[] = { {-1, 1 /* STDOUT_FILENO */, -1, 0}, +				   {-1, -1} };    int status;    int nread;    char *mark = NULL; @@ -219,10 +219,9 @@ gpgconf_read (void *engine, char *arg1, char *arg2,    if (_gpgme_io_pipe (rp, 1) < 0)      return gpg_error_from_syserror (); -  pfd[0].fd = rp[1];    cfd[0].fd = rp[1]; -  status = _gpgme_io_spawn (gpgconf->file_name, argv, cfd, pfd, NULL); +  status = _gpgme_io_spawn (gpgconf->file_name, argv, cfd, NULL);    if (status < 0)      {        _gpgme_io_close (rp[0]); @@ -645,7 +644,6 @@ gpgconf_write (void *engine, char *arg1, char *arg2, gpgme_data_t conf)    int buflen = 0;    char *argv[] = { NULL /* file_name */, arg1, arg2, 0 };    int rp[2]; -  struct spawn_fd_item_s pfd[] = { {1, -1}, {-1, -1} };    struct spawn_fd_item_s cfd[] = { {-1, 0 /* STDIN_FILENO */}, {-1, -1} };    int status;    int nwrite; @@ -659,10 +657,9 @@ gpgconf_write (void *engine, char *arg1, char *arg2, gpgme_data_t conf)    if (_gpgme_io_pipe (rp, 0) < 0)      return gpg_error_from_syserror (); -  pfd[0].fd = rp[0];    cfd[0].fd = rp[0]; -  status = _gpgme_io_spawn (gpgconf->file_name, argv, cfd, pfd, NULL); +  status = _gpgme_io_spawn (gpgconf->file_name, argv, cfd, NULL);    if (status < 0)      {        _gpgme_io_close (rp[0]); diff --git a/gpgme/engine-gpgsm.c b/gpgme/engine-gpgsm.c index 615648b0..936ac2e2 100644 --- a/gpgme/engine-gpgsm.c +++ b/gpgme/engine-gpgsm.c @@ -405,9 +405,6 @@ gpgsm_new (void **engine, const char *file_name, const char *home_dir)      }    gpgsm->input_cb.fd = fds[1];    gpgsm->input_cb.server_fd = fds[0]; -  _gpgme_io_fd2str (gpgsm->input_cb.server_fd_str,  -                    sizeof gpgsm->input_cb.server_fd_str, -                    gpgsm->input_cb.server_fd);    if (_gpgme_io_pipe (fds, 1) < 0)      { @@ -416,9 +413,6 @@ gpgsm_new (void **engine, const char *file_name, const char *home_dir)      }    gpgsm->output_cb.fd = fds[0];    gpgsm->output_cb.server_fd = fds[1]; -  _gpgme_io_fd2str (gpgsm->output_cb.server_fd_str,  -                    sizeof gpgsm->output_cb.server_fd_str, -                    gpgsm->output_cb.server_fd);    if (_gpgme_io_pipe (fds, 0) < 0)      { @@ -427,9 +421,6 @@ gpgsm_new (void **engine, const char *file_name, const char *home_dir)      }    gpgsm->message_cb.fd = fds[1];    gpgsm->message_cb.server_fd = fds[0]; -  _gpgme_io_fd2str (gpgsm->message_cb.server_fd_str,  -                    sizeof gpgsm->message_cb.server_fd_str, -                    gpgsm->message_cb.server_fd);    child_fds[0] = gpgsm->input_cb.server_fd;    child_fds[1] = gpgsm->output_cb.server_fd; @@ -455,10 +446,35 @@ gpgsm_new (void **engine, const char *file_name, const char *home_dir)    err = assuan_pipe_connect      (&gpgsm->assuan_ctx, file_name ? file_name : _gpgme_get_gpgsm_path (),       argv, child_fds); + +  /* On Windows, handles are inserted in the spawned process with +     DuplicateHandle, and child_fds contains the server-local names +     for the inserted handles when assuan_pipe_connect returns.  */ +  if (!err) +    { +      /* Note: We don't use _gpgme_io_fd2str here.  On W32 the +	 returned handles are real W32 system handles, not whatever +	 GPGME uses internally (which may be a system handle, a C +	 library handle or a GLib/Qt channel.  Confusing, yes, but +	 remember these are server-local names, so they are not part +	 of GPGME at all.  */ +      snprintf (gpgsm->input_cb.server_fd_str, +		sizeof gpgsm->input_cb.server_fd_str, "%d", child_fds[0]); +      snprintf (gpgsm->output_cb.server_fd_str, +		sizeof gpgsm->output_cb.server_fd_str, "%d", child_fds[1]); +      snprintf (gpgsm->message_cb.server_fd_str, +		sizeof gpgsm->message_cb.server_fd_str, "%d", child_fds[2]); +    }  #endif    if (err)      goto leave; +  /* assuan_pipe_connect in this case uses _gpgme_io_spawn which +     closes the child fds for us.  */ +  gpgsm->input_cb.server_fd = -1; +  gpgsm->output_cb.server_fd = -1; +  gpgsm->message_cb.server_fd = -1; +    err = _gpgme_getenv ("DISPLAY", &dft_display);    if (err)      goto leave; @@ -623,6 +639,10 @@ gpgsm_set_locale (void *engine, int category, const char *value)    else      return gpg_error (GPG_ERR_INV_VALUE); +  /* FIXME: Reset value to default.  */ +  if (!value)  +    return 0; +    if (asprintf (&optstr, "OPTION %s=%s", catstr, value) < 0)      err = gpg_error_from_errno (errno);    else @@ -633,6 +653,7 @@ gpgsm_set_locale (void *engine, int category, const char *value)        if (err)  	err = map_assuan_error (err);      } +    return err;  } diff --git a/gpgme/gpgme-w32spawn.c b/gpgme/gpgme-w32spawn.c new file mode 100644 index 00000000..f132a8fc --- /dev/null +++ b/gpgme/gpgme-w32spawn.c @@ -0,0 +1,434 @@ +/* gpgme-w32spawn.c - Wrapper to spawn a process under Windows. +   Copyright (C) 2008 g10 Code GmbH + +   This file is part of GPGME. +  +   GPGME is free software; you can redistribute it and/or modify it +   under the terms of the GNU Lesser General Public License as +   published by the Free Software Foundation; either version 2.1 of +   the License, or (at your option) any later version. +    +   GPGME is distributed in the hope that it will be useful, but +   WITHOUT ANY WARRANTY; without even the implied warranty of +   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU +   Lesser General Public License for more details. +    +   You should have received a copy of the GNU Lesser General Public +   License along with this program; if not, see <http://www.gnu.org/licenses/>. + */ + + +#ifdef HAVE_CONFIG_H +# include <config.h> +#endif + +#include <errno.h> +#include <stdio.h> +#include <stdlib.h> +#include <fcntl.h> +#include <ctype.h> +#include <string.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <stdint.h> +#include <process.h> +#include <windows.h> + + + +struct spawn_fd_item_s +{ +  int handle; +  int dup_to; +  int peer_name; +  int arg_loc; +}; + + +static char * +build_commandline (char **argv) +{ +  int i; +  int n = 0; +  char *buf; +  char *p; +   +  /* We have to quote some things because under Windows the program +     parses the commandline and does some unquoting.  We enclose the +     whole argument in double-quotes, and escape literal double-quotes +     as well as backslashes with a backslash.  We end up with a +     trailing space at the end of the line, but that is harmless.  */ +  for (i = 0; argv[i]; i++) +    { +      p = argv[i]; +      /* The leading double-quote.  */ +      n++; +      while (*p) +	{ +	  /* An extra one for each literal that must be escaped.  */ +	  if (*p == '\\' || *p == '"') +	    n++; +	  n++; +	  p++; +	} +      /* The trailing double-quote and the delimiter.  */ +      n += 2; +    } +  /* And a trailing zero.  */ +  n++; + +  buf = p = malloc (n); +  if (!buf) +    return NULL; +  for (i = 0; argv[i]; i++) +    { +      char *argvp = argv[i]; + +      *(p++) = '"'; +      while (*argvp) +	{ +	  if (*argvp == '\\' || *argvp == '"') +	    *(p++) = '\\'; +	  *(p++) = *(argvp++); +	} +      *(p++) = '"'; +      *(p++) = ' '; +    } +  *(p++) = 0; + +  return buf; +} + + +int +my_spawn (char **argv, struct spawn_fd_item_s *fd_list) +{ +  SECURITY_ATTRIBUTES sec_attr; +  PROCESS_INFORMATION pi = +    { +      NULL,      /* returns process handle */ +      0,         /* returns primary thread handle */ +      0,         /* returns pid */ +      0          /* returns tid */ +    }; +  STARTUPINFO si; +  char *envblock = NULL; +  int cr_flags = CREATE_DEFAULT_ERROR_MODE +    | GetPriorityClass (GetCurrentProcess ()); +  int i; +  char *arg_string; +  int duped_stdin = 0; +  int duped_stdout = 0; +  int duped_stderr = 0; +  HANDLE hnul = INVALID_HANDLE_VALUE; +  /* FIXME.  */ +  int debug_me = 0; + +  i = 0; +  while (argv[i]) +    { +      fprintf (stderr, "argv[%2i] = %s\n", i, argv[i]); +      i++; +    } + +  memset (&sec_attr, 0, sizeof sec_attr); +  sec_attr.nLength = sizeof sec_attr; +  sec_attr.bInheritHandle = FALSE; +   +  arg_string = build_commandline (argv); +  if (!arg_string) +    return -1; + +  memset (&si, 0, sizeof si); +  si.cb = sizeof (si); +  si.dwFlags = STARTF_USESTDHANDLES | STARTF_USESHOWWINDOW; +  si.wShowWindow = debug_me ? SW_SHOW : SW_HIDE; +  si.hStdInput = GetStdHandle (STD_INPUT_HANDLE); +  si.hStdOutput = GetStdHandle (STD_OUTPUT_HANDLE); +  si.hStdError = GetStdHandle (STD_ERROR_HANDLE); + +  fprintf (stderr, "spawning: %s\n", arg_string); + +  for (i = 0; fd_list[i].handle != -1; i++) +    { +      /* The handle already is inheritable.  */ +      if (fd_list[i].dup_to == 0) +	{ +	  si.hStdInput = (HANDLE) fd_list[i].peer_name; +	  duped_stdin = 1; +	  fprintf (stderr, "dup 0x%x to stdin\n", fd_list[i].peer_name); +        } +      else if (fd_list[i].dup_to == 1) +	{ +	  si.hStdOutput = (HANDLE) fd_list[i].peer_name; +	  duped_stdout = 1; +	  fprintf (stderr, "dup 0x%x to stdout\n", fd_list[i].peer_name); +        } +      else if (fd_list[i].dup_to == 2) +	{ +	  si.hStdError = (HANDLE) fd_list[i].peer_name; +	  duped_stderr = 1; +	  fprintf (stderr, "dup 0x%x to stderr\n", fd_list[i].peer_name); +        } +    } +   +  if (!duped_stdin || !duped_stdout || !duped_stderr) +    { +      SECURITY_ATTRIBUTES sa; +       +      memset (&sa, 0, sizeof sa); +      sa.nLength = sizeof sa; +      sa.bInheritHandle = TRUE; +      hnul = CreateFile ("nul", +			 GENERIC_READ|GENERIC_WRITE, +			 FILE_SHARE_READ|FILE_SHARE_WRITE, +			 &sa, +			 OPEN_EXISTING, +			 FILE_ATTRIBUTE_NORMAL, +			 NULL); +      if (hnul == INVALID_HANDLE_VALUE) +	{ +	  free (arg_string); +	  /* FIXME: Should translate the error code.  */ +	  errno = EIO; +	  return -1; +        } +      /* Make sure that the process has a connected stdin.  */ +      if (!duped_stdin) +	si.hStdInput = hnul; +      /* Make sure that the process has a connected stdout.  */ +      if (!duped_stdout) +	si.hStdOutput = hnul; +      /* We normally don't want all the normal output.  */ +      if (!duped_stderr) +	si.hStdError = hnul; +    } +   +  cr_flags |= CREATE_SUSPENDED;  +  cr_flags |= DETACHED_PROCESS; +  if (!CreateProcessA (argv[0], +		       arg_string, +		       &sec_attr,     /* process security attributes */ +		       &sec_attr,     /* thread security attributes */ +		       TRUE,          /* inherit handles */ +		       cr_flags,      /* creation flags */ +		       envblock,      /* environment */ +		       NULL,          /* use current drive/directory */ +		       &si,           /* startup information */ +		       &pi))          /* returns process information */ +    { +      free (arg_string); +      /* FIXME: Should translate the error code.  */ +      errno = EIO; +      return -1; +    } + +  free (arg_string); + +  /* Close the /dev/nul handle if used.  */ +  if (hnul != INVALID_HANDLE_VALUE) +    CloseHandle (hnul); +   +  for (i = 0; fd_list[i].handle != -1; i++) +    CloseHandle ((HANDLE) fd_list[i].handle); +   +  ResumeThread (pi.hThread); +  CloseHandle (pi.hThread); +  CloseHandle (pi.hProcess); + +  return 0; +} + + +#define MAX_TRANS 10 + +int +translate_get_from_file (const char *trans_file,  +			 struct spawn_fd_item_s *fd_list) +{ +  /* Hold roughly MAX_TRANS triplets of 64 bit numbers in hex +     notation: "0xFEDCBA9876543210".  10*19*4 - 1 = 759.  This plans +     ahead for a time when a HANDLE is 64 bit.  */ +#define BUFFER_MAX 800 + +  char line[BUFFER_MAX + 1]; +  char *linep; +  int idx; +  int res; +  int fd; + +  fd = open (trans_file, O_RDONLY); +  if (fd < 0) +    return -1; + +  /* We always read one line from stdin.  */ +  res = read (fd, line, BUFFER_MAX); +  close (fd); +  if (res < 0) +    return -1; + +  line[BUFFER_MAX] = '\0'; +  linep = strchr (line, '\n'); +  if (linep > line && linep[-1] == '\r') +    linep--; +  *linep = '\0'; + +  linep = line; + +  /* Now start to read mapping pairs.  */ +  for (idx = 0; idx < MAX_TRANS; idx++) +    { +      unsigned long from; +      long dup_to; +      unsigned long to; +      unsigned long loc; +      char *tail; + +      /* FIXME: Maybe could use scanf.  */ +      while (isspace (*((unsigned char *)linep))) +	linep++; +      if (*linep == '\0') +	break; +      from = strtoul (linep, &tail, 0); +      if (tail == NULL || ! (*tail == '\0' || isspace (*tail))) +	break; +      linep = tail; + +      while (isspace (*linep)) +	linep++; +      if (*linep == '\0') +	break; +      dup_to = strtol (linep, &tail, 0); +      if (tail == NULL || ! (*tail == '\0' || isspace (*tail))) +	break; +      linep = tail; + +      while (isspace (*linep)) +	linep++; +      if (*linep == '\0') +	break; +      to = strtoul (linep, &tail, 0); +      if (tail == NULL || ! (*tail == '\0' || isspace (*tail))) +	break; +      linep = tail; + +      while (isspace (*linep)) +	linep++; +      if (*linep == '\0') +	break; +      loc = strtoul (linep, &tail, 0); +      if (tail == NULL || ! (*tail == '\0' || isspace (*tail))) +	break; +      linep = tail; + +      fd_list[idx].handle = from; +      fd_list[idx].dup_to = dup_to; +      fd_list[idx].peer_name = to; +      fd_list[idx].arg_loc = loc; +    } +  fd_list[idx].handle = -1; +  fd_list[idx].dup_to = -1; +  fd_list[idx].peer_name = -1; +  fd_list[idx].arg_loc = 0; +  return 0; +} + + +/* Read the translated handles from TRANS_FILE and do a substitution +   in ARGV.  Returns the new argv and the list of substitutions in +   FD_LIST (which must be MAX_TRANS+1 large).  */ +char ** +translate_handles (const char *trans_file, const char * const *argv, +		   struct spawn_fd_item_s *fd_list) +{ +  int res; +  int idx; +  char **args; + +  res = translate_get_from_file (trans_file, fd_list); +  if (res < 0) +    return NULL; + +  for (idx = 0; argv[idx]; idx++) +    ; +  args = malloc (sizeof (*args) * (idx + 1)); +  for (idx = 0; argv[idx]; idx++) +    { +      args[idx] = strdup (argv[idx]); +      if (!args[idx]) +	return NULL; +    } +  args[idx] = NULL; + +  for (idx = 0; fd_list[idx].handle != -1; idx++) +    { +      char buf[25]; +      int aidx; + +      aidx = fd_list[idx].arg_loc; +      if (aidx == 0) +	continue; + +      args[aidx] = malloc (sizeof (buf)); +      /* We currently disable translation for stdin/stdout/stderr.  We +	 assume that the spawned program handles 0/1/2 specially +	 already.  FIXME: Check if this is true.  */ +      if (!args[idx] || fd_list[idx].dup_to != -1) +	return NULL; + +      /* NOTE: Here is the part where application specific knowledge +	 comes in.  GPGME/GnuPG uses two forms of descriptor +	 specification, a plain number and a "-&" form.  */ +      if (argv[aidx][0] == '-' && argv[aidx][1] == '&') +	snprintf (args[aidx], sizeof (buf), "-&%d", fd_list[idx].peer_name); +      else +	snprintf (args[aidx], sizeof (buf), "%d", fd_list[idx].peer_name); +    } +  return args; +} + + +int +main (int argc, const char * const *argv) +{ +  int rc = 0; +  char **argv_spawn; +  struct spawn_fd_item_s fd_list[MAX_TRANS + 1]; + +  if (argc < 3) +    { +      rc = 2; +      goto leave; +    } + +  argv_spawn = translate_handles (argv[1], &argv[2], fd_list); +  if (!argv_spawn) +    { +      rc = 2; +      goto leave; +    } + +  /* Using execv does not replace the existing program image, but +     spawns a new one and daemonizes it, confusing the command line +     interpreter.  So we have to use spawnv.  */ +  rc = my_spawn (argv_spawn, fd_list); +  if (rc < 0) +    { +      fprintf (stderr, "gpgwrap: executing `%s' failed: %s\n", +	       argv[0], strerror (errno)); +      rc = 2; +      goto leave; +    } + + leave: +  if (rc) +    fprintf (stderr, "gpg-w32spawn: internal error\n"); +  /* Always try to delete the temporary file.  */ +  if (argc >= 2) +    { +      if (DeleteFile (argv[1]) == 0) +	fprintf (stderr, "Failed to delete %s: ec=%ld\n", +		 argv[1], GetLastError ()); +    } +  return rc; +} diff --git a/gpgme/gpgme.h b/gpgme/gpgme.h index 778932f4..8740469f 100644 --- a/gpgme/gpgme.h +++ b/gpgme/gpgme.h @@ -72,7 +72,7 @@ extern "C" {     AM_PATH_GPGME macro) check that this header matches the installed     library.  Warning: Do not edit the next line.  configure will do     that for you!  */ -#define GPGME_VERSION "1.1.7-svn1313" +#define GPGME_VERSION "1.1.7-svn1315" diff --git a/gpgme/posix-io.c b/gpgme/posix-io.c index f2a616d2..e6a3c676 100644 --- a/gpgme/posix-io.c +++ b/gpgme/posix-io.c @@ -34,6 +34,8 @@  #include <sys/types.h>  #include <sys/wait.h>  #include <ctype.h> +#include <sys/resource.h> +#include <unistd.h>  #include "util.h"  #include "priv-io.h" @@ -207,6 +209,72 @@ _gpgme_io_set_nonblocking (int fd)  } +static long int +get_max_fds (void) +{ +  char *source = NULL; +  long int fds = -1; +  int rc; + +#ifdef RLIMIT_NOFILE +  { +    struct rlimit rl; +    rc = getrlimit (RLIMIT_NOFILE, &rl); +    if (rc == 0) +      { +	source = "RLIMIT_NOFILE"; +	fds = rl.rlim_max; +      } +  } +#endif +#ifdef RLIMIT_OFILE +  if (fds == -1) +    { +      struct rlimit rl; +      rc = getrlimit (RLIMIT_OFILE, &rl); +      if (rc == 0) +	{ +	  source = "RLIMIT_OFILE"; +	  fds = rl.rlim_max; +	} +    } +#endif +#ifdef _SC_OPEN_MAX +  if (fds == -1) +    { +      long int scres; +      scres = sysconf (_SC_OPEN_MAX); +      if (scres >= 0) +	{ +	  source = "_SC_OPEN_MAX"; +	  return scres; +	} +    } +#endif +#ifdef OPEN_MAX +  if (fds == -1) +    { +      source = "OPEN_MAX"; +      fds = OPEN_MAX; +    } +#endif + +#if !defined(RLIMIT_NOFILE) && !defined(RLIMIT_OFILE) \ +  && !defined(_SC_OPEN_MAX) && !defined(OPEN_MAX) +#warning "No known way to get the maximum number of file descriptors." +#endif +  if (fds == -1) +    { +      source = "arbitrary"; +      /* Arbitrary limit.  */ +      fds = 1024; +    } + +  TRACE2 (DEBUG_SYSIO, "gpgme:max_fds", NULL, "max fds=%i (%s)", fds, source); +  return fds; +} + +  static int  _gpgme_io_waitpid (int pid, int hang, int *r_status, int *r_signal)  { @@ -234,8 +302,7 @@ _gpgme_io_waitpid (int pid, int hang, int *r_status, int *r_signal)  /* Returns 0 on success, -1 on error.  */  int  _gpgme_io_spawn (const char *path, char **argv, -		 struct spawn_fd_item_s *fd_child_list, -		 struct spawn_fd_item_s *fd_parent_list, pid_t *r_pid) +		 struct spawn_fd_item_s *fd_list, pid_t *r_pid)  {    pid_t pid;    int i; @@ -249,52 +316,75 @@ _gpgme_io_spawn (const char *path, char **argv,        TRACE_LOG2 ("argv[%2i] = %s", i, argv[i]);        i++;      } -   +  for (i = 0; fd_list[i].fd != -1; i++) +    if (fd_list[i].dup_to == -1) +      TRACE_LOG2 ("fd[%i] = 0x%x", i, fd_list[i].fd); +    else +      TRACE_LOG3 ("fd[%i] = 0x%x -> 0x%x", i, fd_list[i].fd, fd_list[i].dup_to); +    pid = fork ();    if (pid == -1)       return TRACE_SYSRES (-1); -   +    if (!pid)      {        /* Intermediate child to prevent zombie processes.  */        if ((pid = fork ()) == 0)  	{ +	  int max_fds = get_max_fds (); +	  int fd; +  	  /* Child.  */ -	  int duped_stdin = 0; -	  int duped_stderr = 0; +	  int seen_stdin = 0; +	  int seen_stderr = 0; -	  /* First close all fds which will not be duped.  */ -	  for (i=0; fd_child_list[i].fd != -1; i++) -	    if (fd_child_list[i].dup_to == -1) -	      close (fd_child_list[i].fd); +	  /* First close all fds which will not be inherited.  */ +	  for (fd = 0; fd < max_fds; fd++) +	    { +	      for (i = 0; fd_list[i].fd != -1; i++) +		if (fd_list[i].fd == fd) +		  break; +	      if (fd_list[i].fd == -1) +		close (fd); +	    } -	  /* And now dup and close the rest.  */ -	  for (i=0; fd_child_list[i].fd != -1; i++) +	  /* And now dup and close those to be duplicated.  */ +	  for (i = 0; fd_list[i].fd != -1; i++)  	    { -	      if (fd_child_list[i].dup_to != -1) +	      int child_fd; +	      int res; + +	      if (fd_list[i].dup_to != -1) +		child_fd = fd_list[i].dup_to; +	      else +		child_fd = fd_list[i].fd; + +	      if (child_fd == 0) +		seen_stdin = 1; +	      else if (child_fd == 2) +		seen_stderr = 1; + +	      if (fd_list[i].dup_to == -1) +		continue; + +	      res = dup2 (fd_list[i].fd, fd_list[i].dup_to); +	      if (res < 0)  		{ -		  if (dup2 (fd_child_list[i].fd, -			    fd_child_list[i].dup_to) == -1) -		    {  #if 0 -		      /* FIXME: The debug file descriptor is not -			 dup'ed anyway, so we can't see this.  */ -		      TRACE_LOG1 ("dup2 failed in child: %s\n", -				  strerror (errno)); +		  /* FIXME: The debug file descriptor is not +		     dup'ed anyway, so we can't see this.  */ +		  TRACE_LOG1 ("dup2 failed in child: %s\n", +			      strerror (errno));  #endif -		      _exit (8); -		    } -		  if (fd_child_list[i].dup_to == 0) -		    duped_stdin=1; -		  if (fd_child_list[i].dup_to == 2) -		    duped_stderr=1; -		  close (fd_child_list[i].fd); +		  _exit (8);  		} + +	      close (fd_list[i].fd);  	    } -	  if (!duped_stdin || !duped_stderr) +	  if (! seen_stdin || ! seen_stderr)  	    { -	      int fd = open ("/dev/null", O_RDWR); +	      fd = open ("/dev/null", O_RDWR);  	      if (fd == -1)  		{  #if 0 @@ -306,7 +396,7 @@ _gpgme_io_spawn (const char *path, char **argv,  		  _exit (8);  		}  	      /* Make sure that the process has a connected stdin.  */ -	      if (!duped_stdin) +	      if (! seen_stdin && fd != 0)  		{  		  if (dup2 (fd, 0) == -1)  		    { @@ -319,7 +409,7 @@ _gpgme_io_spawn (const char *path, char **argv,  		      _exit (8);  		    }  		} -	      if (!duped_stderr) +	      if (! seen_stderr && fd != 2)  		if (dup2 (fd, 2) == -1)  		  {  #if 0 @@ -330,10 +420,11 @@ _gpgme_io_spawn (const char *path, char **argv,  #endif  		    _exit (8);  		  } -	      close (fd); +	      if (fd != 0 && fd != 2) +		close (fd);  	    } -	  execv ( path, argv ); +	  execv (path, argv);  	  /* Hmm: in that case we could write a special status code to the  	     status-pipe.  */  #if 0 @@ -342,7 +433,8 @@ _gpgme_io_spawn (const char *path, char **argv,  	  TRACE_LOG1 ("exec of `%s' failed\n", path);  #endif  	  _exit (8); -	} /* End child.  */ +	  /* End child.  */ +	}        if (pid == -1)  	_exit (1);        else @@ -354,9 +446,12 @@ _gpgme_io_spawn (const char *path, char **argv,    if (status)      return TRACE_SYSRES (-1); -  /* .dup_to is not used in the parent list.  */ -  for (i = 0; fd_parent_list[i].fd != -1; i++) -    _gpgme_io_close (fd_parent_list[i].fd); +  for (i = 0; fd_list[i].fd != -1; i++) +    { +      _gpgme_io_close (fd_list[i].fd); +      /* No handle translation.  */ +      fd_list[i].peer_name = fd_list[i].fd; +    }    if (r_pid)      *r_pid = pid; @@ -549,5 +644,9 @@ _gpgme_io_sendmsg (int fd, const struct msghdr *msg, int flags)  int  _gpgme_io_dup (int fd)  { -  return dup (fd); +  int new_fd = dup (fd); + +  TRACE1 (DEBUG_SYSIO, "_gpgme_io_dup", fd, "new fd==%i", new_fd); + +  return new_fd;  } diff --git a/gpgme/priv-io.h b/gpgme/priv-io.h index b0bc367d..90a385da 100644 --- a/gpgme/priv-io.h +++ b/gpgme/priv-io.h @@ -24,11 +24,19 @@  /* A single file descriptor passed to spawn.  For child fds, dup_to -   specifies the fd it should become in the child.  */ +   specifies the fd it should become in the child, but only 0, 1 and 2 +   are valid values (due to a limitation in the W32 code).  As return +   value, the PEER_NAME fields specify the name of the file +   descriptor in the spawned process, or -1 if no change.  If ARG_LOC +   is not 0, it specifies the index in the argument vector of the +   program which contains a numerical representation of the file +   descriptor for translation purposes.  */  struct spawn_fd_item_s  {    int fd;    int dup_to; +  int peer_name; +  int arg_loc;  };  struct io_select_fd_s @@ -51,12 +59,13 @@ int _gpgme_io_set_close_notify (int fd, _gpgme_close_notify_handler_t handler,  				void *value);  int _gpgme_io_set_nonblocking (int fd); -/* Spawn the executable PATH with ARGV as arguments, after forking -   close all fds in FD_PARENT_LIST in the parent and close or dup all -   fds in FD_CHILD_LIST in the child.  */ +/* Spawn the executable PATH with ARGV as arguments.  After forking +   close all fds except for those in FD_LIST in the child, then +   optionally dup() the child fds.  Finally, all fds in the list are +   closed in the parent.  */  int _gpgme_io_spawn (const char *path, char **argv, -		     struct spawn_fd_item_s *fd_child_list, -		     struct spawn_fd_item_s *fd_parent_list, pid_t *r_pid); +		     struct spawn_fd_item_s *fd_list, pid_t *r_pid); +  int _gpgme_io_select (struct io_select_fd_s *fds, size_t nfds, int nonblock);  /* Write the printable version of FD to the buffer BUF of length diff --git a/gpgme/rungpg.c b/gpgme/rungpg.c index f4ca2ad5..51ca545b 100644 --- a/gpgme/rungpg.c +++ b/gpgme/rungpg.c @@ -52,6 +52,8 @@ struct arg_and_data_s    int inbound;     /* True if this is used for reading from gpg.  */    int dup_to;    int print_fd;    /* Print the fd number and not the special form of it.  */ +  int *arg_locp;   /* Write back the argv idx of this argument when +		      building command line to this location.  */    char arg[1];     /* Used if data above is not used.  */  }; @@ -62,7 +64,8 @@ struct fd_data_map_s    int inbound;  /* true if this is used for reading from gpg */    int dup_to;    int fd;       /* the fd to use */ -  int peer_fd;  /* the outher side of the pipe */ +  int peer_fd;  /* the other side of the pipe */ +  int arg_loc;  /* The index into the argv for translation purposes.  */    void *tag;  }; @@ -82,6 +85,7 @@ struct engine_gpg    struct    {      int fd[2];   +    int arg_loc;      size_t bufsize;      char *buffer;      size_t readpos; @@ -95,6 +99,7 @@ struct engine_gpg    struct    {      int fd[2];   +    int arg_loc;      size_t bufsize;      char *buffer;      size_t readpos; @@ -191,7 +196,7 @@ close_notify_handler (int fd, void *opaque)  /* If FRONT is true, push at the front of the list.  Use this for     options added late in the process.  */  static gpgme_error_t -add_arg_ext (engine_gpg_t gpg, const char *arg, int front) +_add_arg (engine_gpg_t gpg, const char *arg, int front, int *arg_locp)  {    struct arg_and_data_s *a; @@ -204,6 +209,8 @@ add_arg_ext (engine_gpg_t gpg, const char *arg, int front)    a->data = NULL;    a->dup_to = -1; +  a->arg_locp = arg_locp; +    strcpy (a->arg, arg);    if (front)      { @@ -227,11 +234,26 @@ add_arg_ext (engine_gpg_t gpg, const char *arg, int front)  }  static gpgme_error_t +add_arg_ext (engine_gpg_t gpg, const char *arg, int front) +{ +  return _add_arg (gpg, arg, front, NULL); +} + + +static gpgme_error_t +add_arg_with_locp (engine_gpg_t gpg, const char *arg, int *locp) +{ +  return _add_arg (gpg, arg, 0, locp); +} + + +static gpgme_error_t  add_arg (engine_gpg_t gpg, const char *arg)  {    return add_arg_ext (gpg, arg, 0);  } +  static gpgme_error_t  add_data (engine_gpg_t gpg, gpgme_data_t data, int dup_to, int inbound)  { @@ -246,6 +268,8 @@ add_data (engine_gpg_t gpg, gpgme_data_t data, int dup_to, int inbound)    a->next = NULL;    a->data = data;    a->inbound = inbound; +  a->arg_locp = NULL; +    if (dup_to == -2)      {        a->print_fd = 1; @@ -450,7 +474,7 @@ gpg_new (void **engine, const char *file_name, const char *home_dir)    {      char buf[25];      _gpgme_io_fd2str (buf, sizeof (buf), gpg->status.fd[1]); -    rc = add_arg (gpg, buf); +    rc = add_arg_with_locp (gpg, buf, &gpg->status.arg_loc);      if (rc)        goto leave;    } @@ -798,6 +822,9 @@ build_argv (engine_gpg_t gpg)    argc++;    for (a = gpg->arglist; a; a = a->next)      { +      if (a->arg_locp) +	*(a->arg_locp) = argc; +        if (a->data)  	{  	  /* Create a pipe to pass it down to gpg.  */ @@ -853,6 +880,7 @@ build_argv (engine_gpg_t gpg)  	  fd_data_map[datac].data = a->data;  	  fd_data_map[datac].dup_to = a->dup_to; +  	  if (a->dup_to == -1)  	    {  	      char *ptr; @@ -874,8 +902,9 @@ build_argv (engine_gpg_t gpg)  		  *(ptr++) = '&';  		  buflen -= 2;  		} -	       +  	      _gpgme_io_fd2str (ptr, buflen, fd_data_map[datac].peer_fd); +	      fd_data_map[datac].arg_loc = argc;  	      argc++;              }  	  datac++; @@ -1225,7 +1254,7 @@ start (engine_gpg_t gpg)    int saved_errno;    int i, n;    int status; -  struct spawn_fd_item_s *fd_child_list, *fd_parent_list; +  struct spawn_fd_item_s *fd_list;    pid_t pid;    if (!gpg) @@ -1256,63 +1285,41 @@ start (engine_gpg_t gpg)    if (rc)      return rc; -  n = 3; /* status_fd, colon_fd and end of list */ +  /* status_fd, colon_fd and end of list.  */ +  n = 3;    for (i = 0; gpg->fd_data_map[i].data; i++)       n++; -  fd_child_list = calloc (n + n, sizeof *fd_child_list); -  if (!fd_child_list) +  fd_list = calloc (n, sizeof *fd_list); +  if (! fd_list)      return gpg_error_from_errno (errno); -  fd_parent_list = fd_child_list + n; -  /* build the fd list for the child */ +  /* Build the fd list for the child.  */    n = 0; -  /* The status fd is never dup'ed, so do not include it in the list.  */ +  fd_list[n].fd = gpg->status.fd[1]; +  fd_list[n].dup_to = -1; +  fd_list[n].arg_loc = gpg->status.arg_loc; +  n++;    if (gpg->colon.fnc)      { -      fd_child_list[n].fd = gpg->colon.fd[1];  -      fd_child_list[n].dup_to = 1; /* dup to stdout */ +      fd_list[n].fd = gpg->colon.fd[1];  +      fd_list[n].dup_to = 1;        n++;      }    for (i = 0; gpg->fd_data_map[i].data; i++)      { -      if (gpg->fd_data_map[i].dup_to != -1) -	{ -	  fd_child_list[n].fd = gpg->fd_data_map[i].peer_fd; -	  fd_child_list[n].dup_to = gpg->fd_data_map[i].dup_to; -	  n++; -        } -    } -  fd_child_list[n].fd = -1; -  fd_child_list[n].dup_to = -1; - -  /* Build the fd list for the parent.  */ -  n = 0; -  if (gpg->status.fd[1] != -1) -    { -      fd_parent_list[n].fd = gpg->status.fd[1]; -      fd_parent_list[n].dup_to = -1; -      n++; -    } -  if (gpg->colon.fd[1] != -1) -    { -      fd_parent_list[n].fd = gpg->colon.fd[1]; -      fd_parent_list[n].dup_to = -1; +      fd_list[n].fd = gpg->fd_data_map[i].peer_fd; +      fd_list[n].dup_to = gpg->fd_data_map[i].dup_to; +      fd_list[n].arg_loc = gpg->fd_data_map[i].arg_loc;        n++;      } -  for (i = 0; gpg->fd_data_map[i].data; i++) -    { -      fd_parent_list[n].fd = gpg->fd_data_map[i].peer_fd; -      fd_parent_list[n].dup_to = -1; -      n++; -    }         -  fd_parent_list[n].fd = -1; -  fd_parent_list[n].dup_to = -1; +  fd_list[n].fd = -1; +  fd_list[n].dup_to = -1;    status = _gpgme_io_spawn (gpg->file_name ? gpg->file_name : -			    _gpgme_get_gpg_path (), -			    gpg->argv, fd_child_list, fd_parent_list, &pid); +			    _gpgme_get_gpg_path (), gpg->argv, fd_list, &pid);    saved_errno = errno; -  free (fd_child_list); + +  free (fd_list);    if (status == -1)      return gpg_error_from_errno (saved_errno); @@ -1382,7 +1389,9 @@ gpg_decrypt (void *engine, gpgme_data_t ciph, gpgme_data_t plain)    if (!err)      err = add_data (gpg, plain, 1, 1);    if (!err) -    err = add_data (gpg, ciph, 0, 0); +    err = add_arg (gpg, "--"); +  if (!err) +    err = add_data (gpg, ciph, -1, 0);    if (!err)      start (gpg); @@ -1616,7 +1625,7 @@ gpg_encrypt (void *engine, gpgme_key_t recp[], gpgme_encrypt_flags_t flags,    if (!err)      err = add_arg (gpg, "--");    if (!err) -    err = add_data (gpg, plain, 0, 0); +    err = add_data (gpg, plain, -1, 0);    if (!err)      err = start (gpg); @@ -1670,7 +1679,7 @@ gpg_encrypt_sign (void *engine, gpgme_key_t recp[],    if (!err)      err = add_arg (gpg, "--");    if (!err) -    err = add_data (gpg, plain, 0, 0); +    err = add_data (gpg, plain, -1, 0);    if (!err)      err = start (gpg); @@ -1759,7 +1768,9 @@ gpg_genkey (void *engine, gpgme_data_t help_data, int use_armor,    if (!err && use_armor)      err = add_arg (gpg, "--armor");    if (!err) -    err = add_data (gpg, help_data, 0, 0); +    err = add_arg (gpg, "--"); +  if (!err) +    err = add_data (gpg, help_data, -1, 0);    if (!err)      err = start (gpg); @@ -1776,7 +1787,9 @@ gpg_import (void *engine, gpgme_data_t keydata)    err = add_arg (gpg, "--import");    if (!err) -    err = add_data (gpg, keydata, 0, 0); +    err = add_arg (gpg, "--"); +  if (!err) +    err = add_data (gpg, keydata, -1, 0);    if (!err)      err = start (gpg); @@ -2011,7 +2024,9 @@ gpg_sign (void *engine, gpgme_data_t in, gpgme_data_t out,    /* Tell the gpg object about the data.  */    if (!err) -    err = add_data (gpg, in, 0, 0); +    err = add_arg (gpg, "--"); +  if (!err) +    err = add_data (gpg, in, -1, 0);    if (!err)      err = add_data (gpg, out, 1, 1); @@ -2061,7 +2076,7 @@ gpg_verify (void *engine, gpgme_data_t sig, gpgme_data_t signed_text,        if (!err)  	err = add_arg (gpg, "--");        if (!err) -	err = add_data (gpg, sig, 0, 0); +	err = add_data (gpg, sig, -1, 0);        if (!err)  	err = add_data (gpg, plaintext, 1, 1);      } @@ -2072,13 +2087,8 @@ gpg_verify (void *engine, gpgme_data_t sig, gpgme_data_t signed_text,  	err = add_arg (gpg, "--");        if (!err)  	err = add_data (gpg, sig, -1, 0); -      if (signed_text) -	{ -	  if (!err) -	    err = add_arg (gpg, "-"); -	  if (!err) -	    err = add_data (gpg, signed_text, 0, 0); -	} +      if (!err && signed_text) +	err = add_data (gpg, signed_text, -1, 0);      }    if (!err) diff --git a/gpgme/util.h b/gpgme/util.h index 64e63c70..0d6a2543 100644 --- a/gpgme/util.h +++ b/gpgme/util.h @@ -105,4 +105,10 @@ gpgme_error_t _gpgme_map_gnupg_error (char *err);     set, return NULL in *VALUE.  */  gpgme_error_t _gpgme_getenv (const char *name, char **value); + +#ifdef HAVE_W32_SYSTEM +int _gpgme_mkstemp (int *fd, char **name); +const char *_gpgme_get_w32spawn_path (void); +#endif +  #endif /* UTIL_H */ diff --git a/gpgme/version.c b/gpgme/version.c index 457945a6..dd23ccfc 100644 --- a/gpgme/version.c +++ b/gpgme/version.c @@ -247,8 +247,8 @@ _gpgme_get_program_version (const char *const file_name)    int rp[2];    int nread;    char *argv[] = {NULL /* file_name */, "--version", 0}; -  struct spawn_fd_item_s pfd[] = { {0, -1}, {-1, -1} }; -  struct spawn_fd_item_s cfd[] = { {-1, 1 /* STDOUT_FILENO */}, {-1, -1} }; +  struct spawn_fd_item_s cfd[] = { {-1, 1 /* STDOUT_FILENO */, -1, 0}, +				   {-1, -1} };    int status;    if (!file_name) @@ -258,10 +258,9 @@ _gpgme_get_program_version (const char *const file_name)    if (_gpgme_io_pipe (rp, 1) < 0)      return NULL; -  pfd[0].fd = rp[1];    cfd[0].fd = rp[1]; -  status = _gpgme_io_spawn (file_name, argv, cfd, pfd, NULL); +  status = _gpgme_io_spawn (file_name, argv, cfd, NULL);    if (status < 0)      {        _gpgme_io_close (rp[0]); diff --git a/gpgme/w32-glib-io.c b/gpgme/w32-glib-io.c index 8b284304..9381b2e6 100644 --- a/gpgme/w32-glib-io.c +++ b/gpgme/w32-glib-io.c @@ -449,8 +449,7 @@ build_commandline (char **argv)  int  _gpgme_io_spawn (const char *path, char **argv, -		 struct spawn_fd_item_s *fd_child_list, -		 struct spawn_fd_item_s *fd_parent_list, pid_t *r_pid) +		 struct spawn_fd_item_s *fd_list, pid_t *r_pid)  {    SECURITY_ATTRIBUTES sec_attr;    PROCESS_INFORMATION pi = @@ -461,16 +460,16 @@ _gpgme_io_spawn (const char *path, char **argv,        0          /* returns tid */      };    STARTUPINFO si; -  char *envblock = NULL;    int cr_flags = CREATE_DEFAULT_ERROR_MODE      | GetPriorityClass (GetCurrentProcess ());    int i; +  char **args;    char *arg_string; -  int duped_stdin = 0; -  int duped_stderr = 0; -  HANDLE hnul = INVALID_HANDLE_VALUE;    /* FIXME.  */    int debug_me = 0; +  int tmp_fd; +  char *tmp_name; +    TRACE_BEG1 (DEBUG_SYSIO, "_gpgme_io_spawn", path,  	      "path=%s", path);    i = 0; @@ -480,119 +479,145 @@ _gpgme_io_spawn (const char *path, char **argv,        i++;      } +  /* We do not inherit any handles by default, and just insert those +     handles we want the child to have afterwards.  But some handle +     values occur on the command line, and we need to move +     stdin/out/err to the right location.  So we use a wrapper program +     which gets the information from a temporary file.  */ +  if (_gpgme_mkstemp (&tmp_fd, &tmp_name) < 0) +    { +      TRACE_LOG1 ("_gpgme_mkstemp failed: %s", strerror (errno)); +      return TRACE_SYSRES (-1); +    } +  TRACE_LOG1 ("tmp_name = %s", tmp_name); + +  args = calloc (2 + i + 1, sizeof (*args)); +  args[0] = (char *) _gpgme_get_w32spawn_path (); +  args[1] = tmp_name; +  args[2] = path; +  memcpy (&args[3], &argv[1], i * sizeof (*args)); +    memset (&sec_attr, 0, sizeof sec_attr);    sec_attr.nLength = sizeof sec_attr;    sec_attr.bInheritHandle = FALSE; -  arg_string = build_commandline (argv); +  arg_string = build_commandline (args); +  free (args);    if (!arg_string) -    return TRACE_SYSRES (-1); -   +    { +      close (tmp_fd); +      DeleteFile (tmp_name); +      return TRACE_SYSRES (-1); +    } +    memset (&si, 0, sizeof si);    si.cb = sizeof (si);    si.dwFlags = STARTF_USESTDHANDLES | STARTF_USESHOWWINDOW; -  si.wShowWindow = debug_me? SW_SHOW : SW_HIDE; -  si.hStdInput = GetStdHandle (STD_INPUT_HANDLE); -  si.hStdOutput = GetStdHandle (STD_OUTPUT_HANDLE); -  si.hStdError = GetStdHandle (STD_ERROR_HANDLE); -   -  for (i = 0; fd_child_list[i].fd != -1; i++) -    { -      if (fd_child_list[i].dup_to == 0) -	{ -	  si.hStdInput = (HANDLE) _get_osfhandle (fd_child_list[i].fd); -	  TRACE_LOG2 ("using 0x%x/%p for stdin", fd_child_list[i].fd, -		      _get_osfhandle (fd_child_list[i].fd)); -	  duped_stdin = 1; -        } -      else if (fd_child_list[i].dup_to == 1) -	{ -	  si.hStdOutput = (HANDLE) _get_osfhandle (fd_child_list[i].fd); -	  TRACE_LOG2 ("using 0x%x/%p for stdout", fd_child_list[i].fd, -		      _get_osfhandle (fd_child_list[i].fd)); -	} -      else if (fd_child_list[i].dup_to == 2) -	{ -	  si.hStdError = (HANDLE) _get_osfhandle (fd_child_list[i].fd); -	  TRACE_LOG2 ("using 0x%x/%p for stderr", fd_child_list[i].fd, -		      _get_osfhandle (fd_child_list[i].fd)); -	  duped_stderr = 1; -        } -    } -   -  if (!duped_stdin || !duped_stderr) -    { -      SECURITY_ATTRIBUTES sa; -       -      memset (&sa, 0, sizeof sa); -      sa.nLength = sizeof sa; -      sa.bInheritHandle = TRUE; -      hnul = CreateFile ("nul", -			 GENERIC_READ|GENERIC_WRITE, -			 FILE_SHARE_READ|FILE_SHARE_WRITE, -			 &sa, -			 OPEN_EXISTING, -			 FILE_ATTRIBUTE_NORMAL, -			 NULL); -      if (hnul == INVALID_HANDLE_VALUE) -	{ -	  TRACE_LOG1 ("CreateFile (\"nul\") failed: ec=%d", -		      (int) GetLastError ()); -	  free (arg_string); -	  /* FIXME: Should translate the error code.  */ -	  errno = EIO; -	  return TRACE_SYSRES (-1); -        } -      /* Make sure that the process has a connected stdin.  */ -      if (!duped_stdin) -	{ -	  si.hStdInput = hnul; -	  TRACE_LOG1 ("using 0x%x for dummy stdin", (int) hnul); -	} -      /* We normally don't want all the normal output.  */ -      if (!duped_stderr) -	{ -	  si.hStdError = hnul; -	  TRACE_LOG1 ("using %d for dummy stderr", (int)hnul); -	} -    } -   +  si.wShowWindow = debug_me ? SW_SHOW : SW_HIDE; +  si.hStdInput = INVALID_HANDLE_VALUE; +  si.hStdOutput = INVALID_HANDLE_VALUE; +  si.hStdError = INVALID_HANDLE_VALUE; +    cr_flags |= CREATE_SUSPENDED;    cr_flags |= DETACHED_PROCESS; -  if (!CreateProcessA (path, +  if (!CreateProcessA (_gpgme_get_w32spawn_path (),  		       arg_string,  		       &sec_attr,     /* process security attributes */  		       &sec_attr,     /* thread security attributes */ -		       TRUE,          /* inherit handles */ +		       FALSE,         /* inherit handles */  		       cr_flags,      /* creation flags */ -		       envblock,      /* environment */ +		       NULL,          /* environment */  		       NULL,          /* use current drive/directory */  		       &si,           /* startup information */  		       &pi))          /* returns process information */      {        TRACE_LOG1 ("CreateProcess failed: ec=%d", (int) GetLastError ());        free (arg_string); +      close (tmp_fd); +      DeleteFile (tmp_name); +        /* FIXME: Should translate the error code.  */        errno = EIO;        return TRACE_SYSRES (-1);      } + +  free (arg_string); -  /* Close the /dev/nul handle if used.  */ -  if (hnul != INVALID_HANDLE_VALUE) +  /* Insert the inherited handles.  */ +  for (i = 0; fd_list[i].fd != -1; i++)      { -      if (!CloseHandle (hnul)) -	TRACE_LOG1 ("CloseHandle (hnul) failed: ec=%d (ignored)", -		    (int) GetLastError ()); +      HANDLE hd; + +      /* Make it inheritable for the wrapper process.  */ +      if (!DuplicateHandle (GetCurrentProcess(), _get_osfhandle (fd_list[i].fd), +			    pi.hProcess, &hd, 0, TRUE, DUPLICATE_SAME_ACCESS)) +	{ +	  TRACE_LOG1 ("DuplicateHandle failed: ec=%d", (int) GetLastError ()); +	  TerminateProcess (pi.hProcess, 0); +	  /* Just in case TerminateProcess didn't work, let the +	     process fail on its own.  */ +	  ResumeThread (pi.hThread); +	  CloseHandle (pi.hThread); +	  CloseHandle (pi.hProcess); + +	  close (tmp_fd); +	  DeleteFile (tmp_name); + +	  /* FIXME: Should translate the error code.  */ +	  errno = EIO; +	  return TRACE_SYSRES (-1); +        } +      /* Return the child name of this handle.  */ +      fd_list[i].peer_name = (int) hd;      } -   -  /* Close the other ends of the pipes.  */ -  for (i = 0; fd_parent_list[i].fd != -1; i++) -    _gpgme_io_close (fd_parent_list[i].fd); -   + +  /* Write the handle translation information to the temporary +     file.  */ +  { +    /* Hold roughly MAX_TRANS quadruplets of 64 bit numbers in hex +       notation: "0xFEDCBA9876543210" with an extra white space after +       every quadruplet.  10*(19*4 + 1) - 1 = 769.  This plans ahead +       for a time when a HANDLE is 64 bit.  */ +#define BUFFER_MAX 800 +    char line[BUFFER_MAX + 1]; +    int res; +    int written; +    size_t len; + +    line[0] = '\n'; +    line[1] = '\0'; +    for (i = 0; fd_list[i].fd != -1; i++) +      { +	/* Strip the newline.  */ +	len = strlen (line) - 1; +	 +	/* Format is: Local name, stdin/stdout/stderr, peer name, argv idx.  */ +	snprintf (&line[len], BUFFER_MAX - len, "0x%x %d 0x%x %d  \n", +		  fd_list[i].fd, fd_list[i].dup_to, +		  fd_list[i].peer_name, fd_list[i].arg_loc); +	/* Rather safe than sorry.  */ +	line[BUFFER_MAX - 1] = '\n'; +	line[BUFFER_MAX] = '\0'; +      } +    len = strlen (line); +    written = 0; +    do +      { +	res = write (tmp_fd, &line[written], len - written); +	if (res > 0) +	  written += res; +      } +    while (res > 0 || (res < 0 && errno == EAGAIN)); +  } +  close (tmp_fd); +  /* The temporary file is deleted by the gpgme-w32spawn process +     (hopefully).  */ +        TRACE_LOG4 ("CreateProcess ready: hProcess=%p, hThread=%p, "  	      "dwProcessID=%d, dwThreadId=%d",  	      pi.hProcess, pi.hThread,   	      (int) pi.dwProcessId, (int) pi.dwThreadId); +      if (r_pid)      *r_pid = (pid_t)pi.dwProcessId; @@ -603,10 +628,24 @@ _gpgme_io_spawn (const char *path, char **argv,      TRACE_LOG1 ("CloseHandle of thread failed: ec=%d",  		(int) GetLastError ()); -  TRACE_SUC1 ("process=%p", pi.hProcess); +  TRACE_LOG1 ("process=%p", pi.hProcess); + +  /* We don't need to wait for the process.  */ +  if (!CloseHandle (pi.hProcess)) +    TRACE_LOG1 ("CloseHandle of process failed: ec=%d", +		(int) GetLastError ()); -  /* We don't need to wait for the process. */ -  CloseHandle (pi.hProcess); +  for (i = 0; fd_list[i].fd != -1; i++) +    _gpgme_io_close (fd_list[i].fd); + +  for (i = 0; fd_list[i].fd != -1; i++) +    if (fd_list[i].dup_to == -1) +      TRACE_LOG3 ("fd[%i] = 0x%x -> 0x%x", i, fd_list[i].fd, +		  fd_list[i].peer_name); +    else +      TRACE_LOG4 ("fd[%i] = 0x%x -> 0x%x (std%s)", i, fd_list[i].fd, +		  fd_list[i].peer_name, (fd_list[i].dup_to == 0) ? "in" : +		  ((fd_list[i].dup_to == 1) ? "out" : "err"));    return TRACE_SYSRES (0);  } diff --git a/gpgme/w32-io.c b/gpgme/w32-io.c index e7dad2c3..e90f7796 100644 --- a/gpgme/w32-io.c +++ b/gpgme/w32-io.c @@ -39,6 +39,7 @@  #include "priv-io.h"  #include "debug.h" +  /* We assume that a HANDLE can be represented by an int which should     be true for all i386 systems (HANDLE is defined as void *) and     these are the only systems for which Windows is available.  Further @@ -242,12 +243,14 @@ reader (void *arg)          }        ctx->writepos = (ctx->writepos + nread) % READBUF_SIZE;        if (!SetEvent (ctx->have_data_ev)) -	TRACE_LOG1 ("SetEvent failed: ec=%d", (int) GetLastError ()); +	TRACE_LOG2 ("SetEvent (0x%x) failed: ec=%d", ctx->have_data_ev, +		    (int) GetLastError ());        UNLOCK (ctx->mutex);      }    /* Indicate that we have an error or EOF.  */    if (!SetEvent (ctx->have_data_ev)) -    TRACE_LOG1 ("SetEvent failed: ec=%d", (int) GetLastError ()); +	TRACE_LOG2 ("SetEvent (0x%x) failed: ec=%d", ctx->have_data_ev, +		    (int) GetLastError ());    SetEvent (ctx->stopped);    return TRACE_SUC (); @@ -479,7 +482,8 @@ _gpgme_io_read (int fd, void *buffer, size_t count)      }    if (!SetEvent (ctx->have_space_ev))      { -      TRACE_LOG1 ("SetEvent failed: ec=%d", (int) GetLastError ()); +      TRACE_LOG2 ("SetEvent (0x%x) failed: ec=%d", +		  ctx->have_space_ev, (int) GetLastError ());        UNLOCK (ctx->mutex);        /* FIXME: Should translate the error code.  */        errno = EIO; @@ -1006,8 +1010,7 @@ build_commandline (char **argv)  int  _gpgme_io_spawn (const char *path, char **argv, -		 struct spawn_fd_item_s *fd_child_list, -		 struct spawn_fd_item_s *fd_parent_list, pid_t *r_pid) +		 struct spawn_fd_item_s *fd_list, pid_t *r_pid)  {    SECURITY_ATTRIBUTES sec_attr;    PROCESS_INFORMATION pi = @@ -1018,16 +1021,16 @@ _gpgme_io_spawn (const char *path, char **argv,        0          /* returns tid */      };    STARTUPINFO si; -  char *envblock = NULL;    int cr_flags = CREATE_DEFAULT_ERROR_MODE      | GetPriorityClass (GetCurrentProcess ());    int i; +  char **args;    char *arg_string; -  int duped_stdin = 0; -  int duped_stderr = 0; -  HANDLE hnul = INVALID_HANDLE_VALUE;    /* FIXME.  */    int debug_me = 0; +  int tmp_fd; +  char *tmp_name; +    TRACE_BEG1 (DEBUG_SYSIO, "_gpgme_io_spawn", path,  	      "path=%s", path);    i = 0; @@ -1037,116 +1040,145 @@ _gpgme_io_spawn (const char *path, char **argv,        i++;      } +  /* We do not inherit any handles by default, and just insert those +     handles we want the child to have afterwards.  But some handle +     values occur on the command line, and we need to move +     stdin/out/err to the right location.  So we use a wrapper program +     which gets the information from a temporary file.  */ +  if (_gpgme_mkstemp (&tmp_fd, &tmp_name) < 0) +    { +      TRACE_LOG1 ("_gpgme_mkstemp failed: %s", strerror (errno)); +      return TRACE_SYSRES (-1); +    } +  TRACE_LOG1 ("tmp_name = %s", tmp_name); + +  args = calloc (2 + i + 1, sizeof (*args)); +  args[0] = (char *) _gpgme_get_w32spawn_path (); +  args[1] = tmp_name; +  args[2] = path; +  memcpy (&args[3], &argv[1], i * sizeof (*args)); +    memset (&sec_attr, 0, sizeof sec_attr);    sec_attr.nLength = sizeof sec_attr;    sec_attr.bInheritHandle = FALSE; -   -  arg_string = build_commandline (argv); +  +  arg_string = build_commandline (args); +  free (args);    if (!arg_string) -    return TRACE_SYSRES (-1); -   +    { +      close (tmp_fd); +      DeleteFile (tmp_name); +      return TRACE_SYSRES (-1); +    } +    memset (&si, 0, sizeof si);    si.cb = sizeof (si);    si.dwFlags = STARTF_USESTDHANDLES | STARTF_USESHOWWINDOW;    si.wShowWindow = debug_me ? SW_SHOW : SW_HIDE; -  si.hStdInput = GetStdHandle (STD_INPUT_HANDLE); -  si.hStdOutput = GetStdHandle (STD_OUTPUT_HANDLE); -  si.hStdError = GetStdHandle (STD_ERROR_HANDLE); +  si.hStdInput = INVALID_HANDLE_VALUE; +  si.hStdOutput = INVALID_HANDLE_VALUE; +  si.hStdError = INVALID_HANDLE_VALUE; -  for (i = 0; fd_child_list[i].fd != -1; i++) -    { -      if (fd_child_list[i].dup_to == 0) -	{ -	  si.hStdInput = fd_to_handle (fd_child_list[i].fd); -	  TRACE_LOG1 ("using 0x%x for stdin", fd_child_list[i].fd); -	  duped_stdin = 1; -        } -      else if (fd_child_list[i].dup_to == 1) -	{ -	  si.hStdOutput = fd_to_handle (fd_child_list[i].fd); -	  TRACE_LOG1 ("using 0x%x for stdout", fd_child_list[i].fd); -        } -      else if (fd_child_list[i].dup_to == 2) -	{ -	  si.hStdError = fd_to_handle (fd_child_list[i].fd); -	  TRACE_LOG1 ("using 0x%x for stderr", fd_child_list[i].fd); -	  duped_stderr = 1; -        } -    } -   -  if (!duped_stdin || !duped_stderr) -    { -      SECURITY_ATTRIBUTES sa; -       -      memset (&sa, 0, sizeof sa); -      sa.nLength = sizeof sa; -      sa.bInheritHandle = TRUE; -      hnul = CreateFile ("nul", -			 GENERIC_READ|GENERIC_WRITE, -			 FILE_SHARE_READ|FILE_SHARE_WRITE, -			 &sa, -			 OPEN_EXISTING, -			 FILE_ATTRIBUTE_NORMAL, -			 NULL); -      if (hnul == INVALID_HANDLE_VALUE) -	{ -	  TRACE_LOG1 ("CreateFile (\"nul\") failed: ec=%d", -		      (int) GetLastError ()); -	  free (arg_string); -	  /* FIXME: Should translate the error code.  */ -	  errno = EIO; -	  return TRACE_SYSRES (-1); -        } -      /* Make sure that the process has a connected stdin.  */ -      if (!duped_stdin) -	{ -	  si.hStdInput = hnul; -	  TRACE_LOG1 ("using 0x%x for dummy stdin", (int) hnul); -        } -      /* We normally don't want all the normal output.  */ -      if (!duped_stderr) -	{ -	  si.hStdError = hnul; -	  TRACE_LOG1 ("using 0x%x for dummy stderr", (int) hnul); -        } -    } -      cr_flags |= CREATE_SUSPENDED;     cr_flags |= DETACHED_PROCESS; -  if (!CreateProcessA (path, +  if (!CreateProcessA (_gpgme_get_w32spawn_path (),  		       arg_string,  		       &sec_attr,     /* process security attributes */  		       &sec_attr,     /* thread security attributes */ -		       TRUE,          /* inherit handles */ +		       FALSE,         /* inherit handles */  		       cr_flags,      /* creation flags */ -		       envblock,      /* environment */ +		       NULL,          /* environment */  		       NULL,          /* use current drive/directory */  		       &si,           /* startup information */  		       &pi))          /* returns process information */      {        TRACE_LOG1 ("CreateProcess failed: ec=%d", (int) GetLastError ());        free (arg_string); +      close (tmp_fd); +      DeleteFile (tmp_name); +        /* FIXME: Should translate the error code.  */        errno = EIO;        return TRACE_SYSRES (-1);      } -  /* Close the /dev/nul handle if used.  */ -  if (hnul != INVALID_HANDLE_VALUE) +  free (arg_string); + +  /* Insert the inherited handles.  */ +  for (i = 0; fd_list[i].fd != -1; i++)      { -      if (!CloseHandle (hnul)) -	TRACE_LOG1 ("CloseHandle (hnul) failed: ec=%d (ignored)", -		    (int) GetLastError ()); +      HANDLE hd; + +      /* Make it inheritable for the wrapper process.  */ +      if (!DuplicateHandle (GetCurrentProcess(), fd_to_handle (fd_list[i].fd), +			    pi.hProcess, &hd, 0, TRUE, DUPLICATE_SAME_ACCESS)) +	{ +	  TRACE_LOG1 ("DuplicateHandle failed: ec=%d", (int) GetLastError ()); +	  TerminateProcess (pi.hProcess, 0); +	  /* Just in case TerminateProcess didn't work, let the +	     process fail on its own.  */ +	  ResumeThread (pi.hThread); +	  CloseHandle (pi.hThread); +	  CloseHandle (pi.hProcess); + +	  close (tmp_fd); +	  DeleteFile (tmp_name); + +	  /* FIXME: Should translate the error code.  */ +	  errno = EIO; +	  return TRACE_SYSRES (-1); +        } +      /* Return the child name of this handle.  */ +      fd_list[i].peer_name = handle_to_fd (hd);      } -  /* Close the other ends of the pipes.  */ -  for (i = 0; fd_parent_list[i].fd != -1; i++) -    _gpgme_io_close (fd_parent_list[i].fd); -   +  /* Write the handle translation information to the temporary +     file.  */ +  { +    /* Hold roughly MAX_TRANS quadruplets of 64 bit numbers in hex +       notation: "0xFEDCBA9876543210" with an extra white space after +       every quadruplet.  10*(19*4 + 1) - 1 = 769.  This plans ahead +       for a time when a HANDLE is 64 bit.  */ +#define BUFFER_MAX 800 +    char line[BUFFER_MAX + 1]; +    int res; +    int written; +    size_t len; + +    line[0] = '\n'; +    line[1] = '\0'; +    for (i = 0; fd_list[i].fd != -1; i++) +      { +	/* Strip the newline.  */ +	len = strlen (line) - 1; +	 +	/* Format is: Local name, stdin/stdout/stderr, peer name, argv idx.  */ +	snprintf (&line[len], BUFFER_MAX - len, "0x%x %d 0x%x %d  \n", +		  fd_list[i].fd, fd_list[i].dup_to, +		  fd_list[i].peer_name, fd_list[i].arg_loc); +	/* Rather safe than sorry.  */ +	line[BUFFER_MAX - 1] = '\n'; +	line[BUFFER_MAX] = '\0'; +      } +    len = strlen (line); +    written = 0; +    do +      { +	res = write (tmp_fd, &line[written], len - written); +	if (res > 0) +	  written += res; +      } +    while (res > 0 || (res < 0 && errno == EAGAIN)); +  } +  close (tmp_fd); +  /* The temporary file is deleted by the gpgme-w32spawn process +     (hopefully).  */ +    TRACE_LOG4 ("CreateProcess ready: hProcess=%p, hThread=%p, "  	      "dwProcessID=%d, dwThreadId=%d",  	      pi.hProcess, pi.hThread,   	      (int) pi.dwProcessId, (int) pi.dwThreadId); +      if (r_pid)      *r_pid = (pid_t)pi.dwProcessId; @@ -1157,10 +1189,24 @@ _gpgme_io_spawn (const char *path, char **argv,      TRACE_LOG1 ("CloseHandle of thread failed: ec=%d",  		(int) GetLastError ()); -  TRACE_SUC1 ("process=%p", pi.hProcess); +  TRACE_LOG1 ("process=%p", pi.hProcess); + +  /* We don't need to wait for the process.  */ +  if (!CloseHandle (pi.hProcess)) +    TRACE_LOG1 ("CloseHandle of process failed: ec=%d", +		(int) GetLastError ()); -  /* We don't need to wait for the process. */ -  CloseHandle (pi.hProcess); +  for (i = 0; fd_list[i].fd != -1; i++) +    _gpgme_io_close (fd_list[i].fd); + +  for (i = 0; fd_list[i].fd != -1; i++) +    if (fd_list[i].dup_to == -1) +      TRACE_LOG3 ("fd[%i] = 0x%x -> 0x%x", i, fd_list[i].fd, +		  fd_list[i].peer_name); +    else +      TRACE_LOG4 ("fd[%i] = 0x%x -> 0x%x (std%s)", i, fd_list[i].fd, +		  fd_list[i].peer_name, (fd_list[i].dup_to == 0) ? "in" : +		  ((fd_list[i].dup_to == 1) ? "out" : "err"));    return TRACE_SYSRES (0);  } diff --git a/gpgme/w32-qt-io.cpp b/gpgme/w32-qt-io.cpp index 264d729b..43f94fd1 100644 --- a/gpgme/w32-qt-io.cpp +++ b/gpgme/w32-qt-io.cpp @@ -398,8 +398,7 @@ build_commandline (char **argv)  int  _gpgme_io_spawn (const char *path, char **argv, -		 struct spawn_fd_item_s *fd_child_list, -		 struct spawn_fd_item_s *fd_parent_list, pid_t *r_pid) +		 struct spawn_fd_item_s *fd_list, pid_t *r_pid)  {    SECURITY_ATTRIBUTES sec_attr;    PROCESS_INFORMATION pi = @@ -410,16 +409,16 @@ _gpgme_io_spawn (const char *path, char **argv,        0         /* returns tid */      };    STARTUPINFO si; -  char *envblock = NULL;    int cr_flags = CREATE_DEFAULT_ERROR_MODE      | GetPriorityClass (GetCurrentProcess ());    int i; +  char **args;    char *arg_string; -  int duped_stdin = 0; -  int duped_stderr = 0; -  HANDLE hnul = INVALID_HANDLE_VALUE;    /* FIXME.  */    int debug_me = 0; +  int tmp_fd; +  char *tmp_name; +    TRACE_BEG1 (DEBUG_SYSIO, "_gpgme_io_spawn", path,  	      "path=%s", path);    i = 0; @@ -429,120 +428,145 @@ _gpgme_io_spawn (const char *path, char **argv,        i++;      } +  /* We do not inherit any handles by default, and just insert those +     handles we want the child to have afterwards.  But some handle +     values occur on the command line, and we need to move +     stdin/out/err to the right location.  So we use a wrapper program +     which gets the information from a temporary file.  */ +  if (_gpgme_mkstemp (&tmp_fd, &tmp_name) < 0) +    { +      TRACE_LOG1 ("_gpgme_mkstemp failed: %s", strerror (errno)); +      return TRACE_SYSRES (-1); +    } +  TRACE_LOG1 ("tmp_name = %s", tmp_name); + +  args = (char **) calloc (2 + i + 1, sizeof (*args)); +  args[0] = (char *) _gpgme_get_w32spawn_path (); +  args[1] = tmp_name; +  args[2] = const_cast<char *>(path); +  memcpy (&args[3], &argv[1], i * sizeof (*args)); +    memset (&sec_attr, 0, sizeof sec_attr);    sec_attr.nLength = sizeof sec_attr;    sec_attr.bInheritHandle = FALSE; -  arg_string = build_commandline (argv); +  arg_string = build_commandline (args); +  free (args);    if (!arg_string) -    return TRACE_SYSRES (-1); +    { +      close (tmp_fd); +      DeleteFile (tmp_name); +      return TRACE_SYSRES (-1); +    }    memset (&si, 0, sizeof si);    si.cb = sizeof (si);    si.dwFlags = STARTF_USESTDHANDLES | STARTF_USESHOWWINDOW; -  si.wShowWindow = debug_me? SW_SHOW : SW_HIDE; -  si.hStdInput = GetStdHandle (STD_INPUT_HANDLE); -  si.hStdOutput = GetStdHandle (STD_OUTPUT_HANDLE); -  si.hStdError = GetStdHandle (STD_ERROR_HANDLE); -   -  for (i = 0; fd_child_list[i].fd != -1; i++) -    { -      if (fd_child_list[i].dup_to == 0) -	{ -	  si.hStdInput = (HANDLE) _get_osfhandle (fd_child_list[i].fd); -	  TRACE_LOG2 ("using 0x%x/%p for stdin", fd_child_list[i].fd, -		      _get_osfhandle (fd_child_list[i].fd)); -	  duped_stdin = 1; -        } -      else if (fd_child_list[i].dup_to == 1) -	{ -	  si.hStdOutput = (HANDLE) _get_osfhandle (fd_child_list[i].fd); -	  TRACE_LOG2 ("using 0x%x/%p for stdout", fd_child_list[i].fd, -		      _get_osfhandle (fd_child_list[i].fd)); -	} -      else if (fd_child_list[i].dup_to == 2) -	{ -	  si.hStdError = (HANDLE) _get_osfhandle (fd_child_list[i].fd); -	  TRACE_LOG2 ("using 0x%x/%p for stderr", fd_child_list[i].fd, -		      _get_osfhandle (fd_child_list[i].fd)); -	  duped_stderr = 1; -        } -    } -   -  if (!duped_stdin || !duped_stderr) -    { -      SECURITY_ATTRIBUTES sa; -       -      memset (&sa, 0, sizeof sa); -      sa.nLength = sizeof sa; -      sa.bInheritHandle = TRUE; -      hnul = CreateFile ("nul", -			 GENERIC_READ|GENERIC_WRITE, -			 FILE_SHARE_READ|FILE_SHARE_WRITE, -			 &sa, -			 OPEN_EXISTING, -			 FILE_ATTRIBUTE_NORMAL, -			 NULL); -      if (hnul == INVALID_HANDLE_VALUE) -	{ -	  TRACE_LOG1 ("CreateFile (\"nul\") failed: ec=%d", -		      (int) GetLastError ()); -	  free (arg_string); -	  /* FIXME: Should translate the error code.  */ -	  errno = EIO; -	  return TRACE_SYSRES (-1); -        } -      /* Make sure that the process has a connected stdin.  */ -      if (!duped_stdin) -	{ -	  si.hStdInput = hnul; -	  TRACE_LOG1 ("using 0x%x for dummy stdin", (int) hnul); -	} -      /* We normally don't want all the normal output.  */ -      if (!duped_stderr) -	{ -	  si.hStdError = hnul; -	  TRACE_LOG1 ("using %d for dummy stderr", (int)hnul); -	} -    } -   +  si.wShowWindow = debug_me ? SW_SHOW : SW_HIDE; +  si.hStdInput = INVALID_HANDLE_VALUE; +  si.hStdOutput = INVALID_HANDLE_VALUE; +  si.hStdError = INVALID_HANDLE_VALUE; +    cr_flags |= CREATE_SUSPENDED;    cr_flags |= DETACHED_PROCESS; -  if (!CreateProcessA (path, +  if (!CreateProcessA (_gpgme_get_w32spawn_path (),  		       arg_string,  		       &sec_attr,     /* process security attributes */  		       &sec_attr,     /* thread security attributes */ -		       TRUE,          /* inherit handles */ +		       FALSE,         /* inherit handles */  		       cr_flags,      /* creation flags */ -		       envblock,      /* environment */ +		       NULL,          /* environment */  		       NULL,          /* use current drive/directory */  		       &si,           /* startup information */  		       &pi))          /* returns process information */      {        TRACE_LOG1 ("CreateProcess failed: ec=%d", (int) GetLastError ());        free (arg_string); +      close (tmp_fd); +      DeleteFile (tmp_name); +        /* FIXME: Should translate the error code.  */        errno = EIO;        return TRACE_SYSRES (-1);      } -   -  /* Close the /dev/nul handle if used.  */ -  if (hnul != INVALID_HANDLE_VALUE) + +  free (arg_string); + +  /* Insert the inherited handles.  */ +  for (i = 0; fd_list[i].fd != -1; i++)      { -      if (!CloseHandle (hnul)) -	TRACE_LOG1 ("CloseHandle (hnul) failed: ec=%d (ignored)", -		    (int) GetLastError ()); -    } -   -  /* Close the other ends of the pipes.  */ -  for (i = 0; fd_parent_list[i].fd != -1; i++) -    _gpgme_io_close (fd_parent_list[i].fd); +      HANDLE hd; + +      if (!DuplicateHandle (GetCurrentProcess(), +			    (HANDLE) _get_osfhandle (fd_list[i].fd), +			    pi.hProcess, &hd, 0, TRUE, DUPLICATE_SAME_ACCESS)) +	{ +	  TRACE_LOG1 ("DuplicateHandle failed: ec=%d", (int) GetLastError ()); +	  TerminateProcess (pi.hProcess, 0); +	  /* Just in case TerminateProcess didn't work, let the +	     process fail on its own.  */ +	  ResumeThread (pi.hThread); +	  CloseHandle (pi.hThread); +	  CloseHandle (pi.hProcess); + +	  close (tmp_fd); +	  DeleteFile (tmp_name); +	  /* FIXME: Should translate the error code.  */ +	  errno = EIO; +	  return TRACE_SYSRES (-1); +        } +      /* Return the child name of this handle.  */ +      fd_list[i].peer_name = (int) hd; +    } +     +  /* Write the handle translation information to the temporary +     file.  */ +  { +    /* Hold roughly MAX_TRANS quadruplets of 64 bit numbers in hex +       notation: "0xFEDCBA9876543210" with an extra white space after +       every quadruplet.  10*(19*4 + 1) - 1 = 769.  This plans ahead +       for a time when a HANDLE is 64 bit.  */ +#define BUFFER_MAX 800 +    char line[BUFFER_MAX + 1]; +    int res; +    int written; +    size_t len; + +    line[0] = '\n'; +    line[1] = '\0'; +    for (i = 0; fd_list[i].fd != -1; i++) +      { +	/* Strip the newline.  */ +	len = strlen (line) - 1; +	 +	/* Format is: Local name, stdin/stdout/stderr, peer name, argv idx.  */ +	snprintf (&line[len], BUFFER_MAX - len, "0x%x %d 0x%x %d  \n", +		  fd_list[i].fd, fd_list[i].dup_to, +		  fd_list[i].peer_name, fd_list[i].arg_loc); +	/* Rather safe than sorry.  */ +	line[BUFFER_MAX - 1] = '\n'; +	line[BUFFER_MAX] = '\0'; +      } +    len = strlen (line); +    written = 0; +    do +      { +	res = write (tmp_fd, &line[written], len - written); +	if (res > 0) +	  written += res; +      } +    while (res > 0 || (res < 0 && errno == EAGAIN)); +  } +  close (tmp_fd); +  /* The temporary file is deleted by the gpgme-w32spawn process +     (hopefully).  */    TRACE_LOG4 ("CreateProcess ready: hProcess=%p, hThread=%p, "  	      "dwProcessID=%d, dwThreadId=%d",  	      pi.hProcess, pi.hThread,   	      (int) pi.dwProcessId, (int) pi.dwThreadId); +    if (r_pid)      *r_pid = (pid_t)pi.dwProcessId; @@ -553,10 +577,24 @@ _gpgme_io_spawn (const char *path, char **argv,      TRACE_LOG1 ("CloseHandle of thread failed: ec=%d",  		(int) GetLastError ()); -  TRACE_SUC1 ("process=%p", pi.hProcess); +  TRACE_LOG1 ("process=%p", pi.hProcess); + +  /* We don't need to wait for the process.  */ +  if (!CloseHandle (pi.hProcess)) +    TRACE_LOG1 ("CloseHandle of process failed: ec=%d", +		(int) GetLastError ()); -  /* We don't need to wait for the process. */ -  CloseHandle (pi.hProcess); +  for (i = 0; fd_list[i].fd != -1; i++) +    _gpgme_io_close (fd_list[i].fd); + +  for (i = 0; fd_list[i].fd != -1; i++) +    if (fd_list[i].dup_to == -1) +      TRACE_LOG3 ("fd[%i] = 0x%x -> 0x%x", i, fd_list[i].fd, +		  fd_list[i].peer_name); +    else +      TRACE_LOG4 ("fd[%i] = 0x%x -> 0x%x (std%s)", i, fd_list[i].fd, +		  fd_list[i].peer_name, (fd_list[i].dup_to == 0) ? "in" : +		  ((fd_list[i].dup_to == 1) ? "out" : "err"));    return TRACE_SYSRES (0);  } diff --git a/gpgme/w32-util.c b/gpgme/w32-util.c index a4763dec..fc8e4be1 100644 --- a/gpgme/w32-util.c +++ b/gpgme/w32-util.c @@ -28,8 +28,11 @@  #include <string.h>  #include <assert.h>  #include <errno.h> +#include <stdint.h>  #include <sys/time.h>  #include <sys/types.h> +#include <sys/stat.h> +#include <unistd.h>  #include <signal.h>  #include <fcntl.h>  #include <windows.h> @@ -251,8 +254,8 @@ find_program_in_inst_dir (const char *name)    char *tmp;    tmp = read_w32_registry_string ("HKEY_LOCAL_MACHINE", -					"Software\\GNU\\GnuPG", -					"Install Directory"); +				  "Software\\GNU\\GnuPG", +				  "Install Directory");    if (!tmp)      return NULL; @@ -350,6 +353,22 @@ _gpgme_get_gpgconf_path (void)  } +const char * +_gpgme_get_w32spawn_path (void) +{ +  static char *w32spawn_program; + +  LOCK (get_path_lock); +  if (!w32spawn_program) +    w32spawn_program = find_program_in_inst_dir ("gpgme-w32spawn.exe"); +  if (!w32spawn_program) +    w32spawn_program +      = find_program_at_standard_place ("GNU\\GnuPG\\gpgme-w32spawn.exe"); +  UNLOCK (get_path_lock); +  return w32spawn_program; +} + +  /* Return an integer value from gpgme specific configuration     entries. VALUE receives that value; function returns true if a value     has been configured and false if not. */ @@ -395,3 +414,134 @@ _gpgme_allow_set_foregound_window (pid_t pid)  } + + +/* mkstemp extracted from libc/sysdeps/posix/tempname.c.  Copyright +   (C) 1991-1999, 2000, 2001, 2006 Free Software Foundation, Inc. + +   The GNU C Library is free software; you can redistribute it and/or +   modify it under the terms of the GNU Lesser General Public +   License as published by the Free Software Foundation; either +   version 2.1 of the License, or (at your option) any later version.  */ + +static const char letters[] = +"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"; + +/* Generate a temporary file name based on TMPL.  TMPL must match the +   rules for mk[s]temp (i.e. end in "XXXXXX").  The name constructed +   does not exist at the time of the call to mkstemp.  TMPL is +   overwritten with the result.  */ +static int +mkstemp (char *tmpl) +{ +  int len; +  char *XXXXXX; +  static uint64_t value; +  uint64_t random_time_bits; +  unsigned int count; +  int fd = -1; +  int save_errno = errno; + +  /* A lower bound on the number of temporary files to attempt to +     generate.  The maximum total number of temporary file names that +     can exist for a given template is 62**6.  It should never be +     necessary to try all these combinations.  Instead if a reasonable +     number of names is tried (we define reasonable as 62**3) fail to +     give the system administrator the chance to remove the problems.  */ +#define ATTEMPTS_MIN (62 * 62 * 62) + +  /* The number of times to attempt to generate a temporary file.  To +     conform to POSIX, this must be no smaller than TMP_MAX.  */ +#if ATTEMPTS_MIN < TMP_MAX +  unsigned int attempts = TMP_MAX; +#else +  unsigned int attempts = ATTEMPTS_MIN; +#endif + +  len = strlen (tmpl); +  if (len < 6 || strcmp (&tmpl[len - 6], "XXXXXX")) +    { +      errno = EINVAL; +      return -1; +    } + +  /* This is where the Xs start.  */ +  XXXXXX = &tmpl[len - 6]; + +  /* Get some more or less random data.  */ +  { +    struct timeval tv; +    gettimeofday (&tv, NULL); +    random_time_bits = ((uint64_t) tv.tv_usec << 16) ^ tv.tv_sec; +  } +  value += random_time_bits ^ getpid (); + +  for (count = 0; count < attempts; value += 7777, ++count) +    { +      uint64_t v = value; + +      /* Fill in the random bits.  */ +      XXXXXX[0] = letters[v % 62]; +      v /= 62; +      XXXXXX[1] = letters[v % 62]; +      v /= 62; +      XXXXXX[2] = letters[v % 62]; +      v /= 62; +      XXXXXX[3] = letters[v % 62]; +      v /= 62; +      XXXXXX[4] = letters[v % 62]; +      v /= 62; +      XXXXXX[5] = letters[v % 62]; + +      fd = open (tmpl, O_RDWR | O_CREAT | O_EXCL, S_IRUSR | S_IWUSR); +      if (fd >= 0) +	{ +	  errno = save_errno; +	  return fd; +	} +      else if (errno != EEXIST) +	return -1; +    } + +  /* We got out of the loop because we ran out of combinations to try.  */ +  errno = EEXIST; +  return -1; +} + + +int +_gpgme_mkstemp (int *fd, char **name) +{ +  char tmp[MAX_PATH + 2]; +  char *tmpname; +  int err; + +  *fd = -1; +  *name = NULL; + +  err = GetTempPath (MAX_PATH + 1, tmp); +  if (err == 0 || err > MAX_PATH + 1) +    strcpy (tmp,"c:\\windows\\temp"); +  else +    { +      int len = strlen(tmp); +       +      /* GetTempPath may return with \ on the end */ +      while(len > 0 && tmp[len - 1] == '\\') +	{ +	  tmp[len-1] = '\0'; +	  len--; +	} +    } + +  tmpname = malloc (strlen (tmp) + 13 + 1); +  if (!tmpname) +    return -1; +  sprintf (tmpname, "%s\\gpgme-XXXXXX", tmp); +  *fd = mkstemp (tmpname); +  if (fd < 0) +    return -1; + +  *name = tmpname; +  return 0; +} | 
