From: Marcus Brinkmann Date: Wed, 25 Jun 2008 16:52:31 +0000 (+0000) Subject: assuan/ X-Git-Tag: gpgme-1.1.8~30 X-Git-Url: http://git.tremily.us/?a=commitdiff_plain;h=0560f3089b5d1f24fd9625612921ceffc995fff8;p=gpgme.git assuan/ 2008-06-25 Marcus Brinkmann * 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. gpgme/ 2008-06-25 Marcus Brinkmann * 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 , and . (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. --- diff --git a/assuan/ChangeLog b/assuan/ChangeLog index 22bc735..2e01943 100644 --- a/assuan/ChangeLog +++ b/assuan/ChangeLog @@ -1,3 +1,10 @@ +2008-06-25 Marcus Brinkmann + + * 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 * assuan-pipe-connect.c (_gpgme_io_spawn): Adjust prototype. diff --git a/assuan/assuan-pipe-connect.c b/assuan/assuan-pipe-connect.c index 32a6219..a0091c6 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 8c35959..2587f16 100644 --- a/gpgme/ChangeLog +++ b/gpgme/ChangeLog @@ -1,3 +1,49 @@ +2008-06-25 Marcus Brinkmann + + * 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 , and . + (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 * engine-gpgconf.c (gpgconf_read): Change ARGV initialization for diff --git a/gpgme/Makefile.am b/gpgme/Makefile.am index bd0191a..c3af79d 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 6a9cf2b..3d107d4 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 615648b..936ac2e 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 0000000..f132a8f --- /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 . + */ + + +#ifdef HAVE_CONFIG_H +# include +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + + +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 778932f..8740469 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 f2a616d..e6a3c67 100644 --- a/gpgme/posix-io.c +++ b/gpgme/posix-io.c @@ -34,6 +34,8 @@ #include #include #include +#include +#include #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 b0bc367..90a385d 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 f4ca2ad..51ca545 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) { @@ -226,12 +233,27 @@ add_arg_ext (engine_gpg_t gpg, const char *arg, int front) return 0; } +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 64e63c7..0d6a254 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 457945a..dd23ccf 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 8b28430..9381b2e 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 e7dad2c..e90f779 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 264d729..43f94fd 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(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 a4763de..fc8e4be 100644 --- a/gpgme/w32-util.c +++ b/gpgme/w32-util.c @@ -28,8 +28,11 @@ #include #include #include +#include #include #include +#include +#include #include #include #include @@ -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; +}