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 + + * 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 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 + + * 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 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 . + */ + + +#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 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 #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); - - /* And now dup and close the rest. */ - for (i=0; fd_child_list[i].fd != -1; i++) + /* First close all fds which will not be inherited. */ + for (fd = 0; fd < max_fds; fd++) { - if (fd_child_list[i].dup_to != -1) + 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 those to be duplicated. */ + for (i = 0; fd_list[i].fd != -1; i++) + { + 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) { @@ -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; + 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++; } - if (gpg->colon.fd[1] != -1) - { - fd_parent_list[n].fd = gpg->colon.fd[1]; - fd_parent_list[n].dup_to = -1; - 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. */ - CloseHandle (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 ()); + + 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. */ - CloseHandle (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 ()); + + 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(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) - { - 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); + free (arg_string); + + /* Insert the inherited handles. */ + for (i = 0; fd_list[i].fd != -1; i++) + { + 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. */ - CloseHandle (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 ()); + + 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 #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; +}