From 09cc0ee7bebcdde9f5a40e827a9e29f9ae7fdf11 Mon Sep 17 00:00:00 2001 From: Werner Koch Date: Wed, 20 Jun 2007 11:16:42 +0000 Subject: [PATCH] [w32] gpg-agent is now started automagically by gpgsm. --- agent/gpg-agent.c | 2 +- common/ChangeLog | 11 ++++ common/exechelp.c | 135 +++++++++++++++++++++++++++++++++++++-------- common/sysutils.c | 55 ++++++++++++++++-- common/sysutils.h | 4 +- doc/ChangeLog | 4 ++ doc/glossary.texi | 5 ++ doc/gpg-agent.texi | 2 +- g10/ChangeLog | 8 +++ g10/misc.c | 73 ------------------------ g10/sign.c | 5 +- scd/scdaemon.c | 2 +- sm/ChangeLog | 4 ++ sm/call-agent.c | 83 +++++++++++++++++++--------- sm/gpgsm.c | 2 +- 15 files changed, 258 insertions(+), 137 deletions(-) diff --git a/agent/gpg-agent.c b/agent/gpg-agent.c index 8907e9234..a0646925c 100644 --- a/agent/gpg-agent.c +++ b/agent/gpg-agent.c @@ -691,7 +691,7 @@ main (int argc, char **argv ) { log_debug ("waiting for debugger - my pid is %u .....\n", (unsigned int)getpid()); - sleep (debug_wait); + gnupg_sleep (debug_wait); log_debug ("... okay\n"); } diff --git a/common/ChangeLog b/common/ChangeLog index f0381229e..3f8fa41c4 100644 --- a/common/ChangeLog +++ b/common/ChangeLog @@ -1,3 +1,14 @@ +2007-06-20 Werner Koch + + * sysutils.c (gnupg_sleep): New. + * sysutils.h [W32]: Remove _sleep wrapper. Changed all callers to + use gnupg_sleep. + + * exechelp.c (build_w32_commandline_copy): New. + (build_w32_commandline): Factored some code out to new function + and correctly process a PGMNAME with spaces. + (gnupg_spawn_process_detached) [W32]: Implement. + 2007-06-14 Werner Koch * simple-pwquery.h (MAP_SPWQ_ERROR_IMPL): New. diff --git a/common/exechelp.c b/common/exechelp.c index b4700c5cd..d0be84047 100644 --- a/common/exechelp.c +++ b/common/exechelp.c @@ -80,17 +80,51 @@ #ifdef HAVE_W32_SYSTEM +/* Helper function to build_w32_commandline. */ +static char * +build_w32_commandline_copy (char *buffer, const char *string) +{ + char *p = buffer; + const char *s; + + if (!*string) /* Empty string. */ + p = stpcpy (p, "\"\""); + else if (strpbrk (string, " \t\n\v\f\"")) + { + /* Need top do some kind of quoting. */ + p = stpcpy (p, "\""); + for (s=string; *s; s++) + { + *p++ = *s; + if (*s == '\"') + *p++ = *s; + } + *p++ = '\"'; + *p = 0; + } + else + p = stpcpy (p, string); + + return p; +} + /* Build a command line for use with W32's CreateProcess. On success CMDLINE gets the address of a newly allocated string. */ static gpg_error_t -build_w32_commandline (const char *pgmname, const char **argv, char **cmdline) +build_w32_commandline (const char *pgmname, const char * const *argv, + char **cmdline) { int i, n; const char *s; char *buf, *p; *cmdline = NULL; - n = strlen (pgmname); + n = 0; + s = pgmname; + n += strlen (s) + 1 + 2; /* (1 space, 2 quoting */ + for (; *s; s++) + if (*s == '\"') + n++; /* Need to double inner quotes. */ for (i=0; (s=argv[i]); i++) { n += strlen (s) + 1 + 2; /* (1 space, 2 quoting */ @@ -104,26 +138,11 @@ build_w32_commandline (const char *pgmname, const char **argv, char **cmdline) if (!buf) return gpg_error_from_syserror (); - /* fixme: PGMNAME may not contain spaces etc. */ - p = stpcpy (p, pgmname); + p = build_w32_commandline_copy (p, pgmname); for (i=0; argv[i]; i++) { - if (!*argv[i]) /* Empty string. */ - p = stpcpy (p, " \"\""); - else if (strpbrk (argv[i], " \t\n\v\f\"")) - { - p = stpcpy (p, " \""); - for (s=argv[i]; *s; s++) - { - *p++ = *s; - if (*s == '\"') - *p++ = *s; - } - *p++ = '\"'; - *p = 0; - } - else - p = stpcpy (stpcpy (p, " "), argv[i]); + *p++ = ' '; + p = build_w32_commandline_copy (p, argv[i]); } *cmdline= buf; @@ -330,7 +349,7 @@ gnupg_spawn_process (const char *pgmname, const char *argv[], pi.hProcess, pi.hThread, (int) pi.dwProcessId, (int) pi.dwThreadId); - /* Process ha been created suspended; resume it now. */ + /* Process has been created suspended; resume it now. */ ResumeThread (pi.hThread); CloseHandle (pi.hThread); @@ -525,7 +544,79 @@ gnupg_spawn_process_detached (const char *pgmname, const char *argv[], const char *envp[] ) { #ifdef HAVE_W32_SYSTEM - return gpg_error (GPG_ERR_NOT_IMPLEMENTED); + gpg_error_t err; + SECURITY_ATTRIBUTES sec_attr; + PROCESS_INFORMATION pi = + { + NULL, /* Returns process handle. */ + 0, /* Returns primary thread handle. */ + 0, /* Returns pid. */ + 0 /* Returns tid. */ + }; + STARTUPINFO si; + int cr_flags; + char *cmdline; + + + /* FIXME: We don't make use of ENVP yet. It is currently only used + to pass the GPG_AGENT_INFO variable to gpg-agent. As the default + on windows is to use a standard socket, this does not really + matter. */ + + + if (access (pgmname, X_OK)) + return gpg_error_from_syserror (); + + /* Prepare security attributes. */ + memset (&sec_attr, 0, sizeof sec_attr ); + sec_attr.nLength = sizeof sec_attr; + sec_attr.bInheritHandle = FALSE; + + /* Build the command line. */ + err = build_w32_commandline (pgmname, argv, &cmdline); + if (err) + return err; + + /* Start the process. */ + memset (&si, 0, sizeof si); + si.cb = sizeof (si); + si.dwFlags = STARTF_USESHOWWINDOW; + si.wShowWindow = DEBUG_W32_SPAWN? SW_SHOW : SW_MINIMIZE; + + cr_flags = (CREATE_DEFAULT_ERROR_MODE + | GetPriorityClass (GetCurrentProcess ()) + | CREATE_NEW_PROCESS_GROUP + | DETACHED_PROCESS); + log_debug ("CreateProcess(detached), path=`%s' cmdline=`%s'\n", + pgmname, cmdline); + if (!CreateProcess (pgmname, /* Program to start. */ + cmdline, /* Command line arguments. */ + &sec_attr, /* Process security attributes. */ + &sec_attr, /* Thread security attributes. */ + FALSE, /* Inherit handles. */ + cr_flags, /* Creation flags. */ + NULL, /* Environment. */ + NULL, /* Use current drive/directory. */ + &si, /* Startup information. */ + &pi /* Returns process information. */ + )) + { + log_error ("CreateProcess(detached) failed: %s\n", w32_strerror (-1)); + xfree (cmdline); + return gpg_error (GPG_ERR_GENERAL); + } + xfree (cmdline); + cmdline = NULL; + + log_debug ("CreateProcess(detached) ready: hProcess=%p hThread=%p" + " dwProcessID=%d dwThreadId=%d\n", + pi.hProcess, pi.hThread, + (int) pi.dwProcessId, (int) pi.dwThreadId); + + CloseHandle (pi.hThread); + + return 0; + #else pid_t pid; int i; diff --git a/common/sysutils.c b/common/sysutils.c index d044f222b..ff1fe1ba4 100644 --- a/common/sysutils.c +++ b/common/sysutils.c @@ -20,23 +20,36 @@ */ #include + +#ifdef WITHOUT_GNU_PTH /* Give the Makefile a chance to build without Pth. */ +# undef HAVE_PTH +# undef USE_GNU_PTH +#endif + #include #include #include #include #include #ifdef HAVE_STAT -#include +# include #endif #if defined(__linux__) && defined(__alpha__) && __GLIBC__ < 2 - #include - #include +# include +# include #endif #ifdef HAVE_SETRLIMIT - #include - #include - #include +# include +# include +# include #endif +#ifdef HAVE_W32_SYSTEM +# include +#endif +#ifdef HAVE_PTH +# include +#endif + #include "util.h" #include "i18n.h" @@ -229,3 +242,33 @@ check_permissions(const char *path,int extension,int checkonly) return 0; } #endif + + +/* Wrapper around the usual sleep fucntion. This one won't wake up + before the sleep time has really elapsed. When build with Pth it + merely calls pth_sleep and thus suspends only the current + thread. */ +void +gnupg_sleep (unsigned int seconds) +{ +#ifdef HAVE_PTH + /* With Pth we force a regular sleep for seconds == 0 so that also + the process will give up its timeslot. */ + if (!seconds) + { +# ifdef HAVE_W32_SYSTEM + Sleep (0); +# else + sleep (0); +# endif + } + pth_sleep (seconds); +#else + /* Fixme: make sure that a sleep won't wake up to early. */ +# ifdef HAVE_W32_SYSTEM + Sleep (seconds*1000); +# else + sleep (seconds); +# endif +#endif +} diff --git a/common/sysutils.h b/common/sysutils.h index 712991599..0e295f5d1 100644 --- a/common/sysutils.h +++ b/common/sysutils.h @@ -27,11 +27,9 @@ int disable_core_dumps (void); int enable_core_dumps (void); const unsigned char *get_session_marker (size_t *rlen); int check_permissions (const char *path,int extension,int checkonly); +void gnupg_sleep (unsigned int seconds); #ifdef HAVE_W32_SYSTEM -/* Windows declares sleep as obsolete, but provides a definition for - _sleep but non for the still existing sleep. */ -#define sleep(a) _sleep ((a)) #include "../jnlib/w32help.h" diff --git a/doc/ChangeLog b/doc/ChangeLog index df1b7d82f..a87680b2b 100644 --- a/doc/ChangeLog +++ b/doc/ChangeLog @@ -1,3 +1,7 @@ +2007-06-19 Werner Koch + + * glossary.texi (Glossary): Describe PSE. + 2007-06-18 Werner Koch * gpg-agent.texi (Agent GETINFO): New. diff --git a/doc/glossary.texi b/doc/glossary.texi index 5ea4a95f2..6eede19d2 100644 --- a/doc/glossary.texi +++ b/doc/glossary.texi @@ -26,5 +26,10 @@ entered as a 40 character hexadecimal formatted string. The @emph{Online Certificate Status Protocol} is used as an alternative to a @acronym{CRL}. It is described in @code{RFC 2560}. +@item PSE + The @emph{Personal Security Environment} describes a database to +store private keys. This is either a smartcard or a collection of files +on a disk; the latter is often called a Soft-PSE. + @end table diff --git a/doc/gpg-agent.texi b/doc/gpg-agent.texi index a079b3095..85ce1acd4 100644 --- a/doc/gpg-agent.texi +++ b/doc/gpg-agent.texi @@ -832,7 +832,7 @@ Here is an example session: @subsection Generating a Key This is used to create a new keypair and store the secret key inside the -active PSE -w which is in most cases a Soft-PSE. An not yet defined +active PSE --- which is in most cases a Soft-PSE. An not yet defined option allows to choose the storage location. To get the secret key out of the PSE, a special export tool has to be used. diff --git a/g10/ChangeLog b/g10/ChangeLog index 5e0a29062..b49d8868b 100644 --- a/g10/ChangeLog +++ b/g10/ChangeLog @@ -1,3 +1,11 @@ +2007-06-20 Werner Koch + + * misc.c (setsysinfo, trap_unaligned): Remove. It is also in + common/sysutils.c. + (disable_core_dumps, get_session_marker): + + * sign.c (sleep): Remove sleep wrapper. + 2007-06-18 Marcus Brinkmann * gpg.c (gpgconf_list): Percent escape output of --gpgconf-list. diff --git a/g10/misc.c b/g10/misc.c index c743da614..4cb7191e5 100644 --- a/g10/misc.c +++ b/g10/misc.c @@ -94,51 +94,6 @@ static struct secured_file_item *secured_files; -#if defined(__linux__) && defined(__alpha__) && __GLIBC__ < 2 -static int -setsysinfo(unsigned long op, void *buffer, unsigned long size, - int *start, void *arg, unsigned long flag) -{ - return syscall(__NR_osf_setsysinfo, op, buffer, size, start, arg, flag); -} - -void -trap_unaligned(void) -{ - unsigned int buf[2]; - - buf[0] = SSIN_UACPROC; - buf[1] = UAC_SIGBUS | UAC_NOPRINT; - setsysinfo(SSI_NVPAIRS, buf, 1, 0, 0, 0); -} -#else -void -trap_unaligned(void) -{ /* dummy */ -} -#endif - - -int -disable_core_dumps() -{ -#ifdef HAVE_DOSISH_SYSTEM - return 0; -#else -#ifdef HAVE_SETRLIMIT - struct rlimit limit; - - limit.rlim_cur = 0; - limit.rlim_max = 0; - if( !setrlimit( RLIMIT_CORE, &limit ) ) - return 0; - if( errno != EINVAL && errno != ENOSYS ) - log_fatal(_("can't disable core dumps: %s\n"), strerror(errno) ); -#endif - return 1; -#endif -} - /* For the sake of SELinux we want to restrict access through gpg to certain files we keep under our own control. This function @@ -371,34 +326,6 @@ print_digest_algo_note( int algo ) gcry_md_algo_name (algo)); } -/* Return a string which is used as a kind of process ID */ -const byte * -get_session_marker( size_t *rlen ) -{ - static byte marker[SIZEOF_UNSIGNED_LONG*2]; - static int initialized; - - if ( !initialized ) - { - volatile ulong aa, bb; /* We really want the uninitialized value. */ - ulong a, b; - - initialized = 1; - /* Although this marker is guessable it is not easy to use this - * for a faked control packet because an attacker does not have - * enough control about the time the verification takes place. - * Of course, we could add just more random but than we need the - * random generator even for verification tasks - which does not - * make sense. */ - a = aa ^ (ulong)getpid(); - b = bb ^ (ulong)time(NULL); - memcpy ( marker, &a, SIZEOF_UNSIGNED_LONG ); - memcpy ( marker+SIZEOF_UNSIGNED_LONG, &b, SIZEOF_UNSIGNED_LONG ); - } - *rlen = sizeof(marker); - return marker; -} - /**************** * Wrapper around the libgcrypt function with additonal checks on * the OpenPGP contraints for the algo ID. diff --git a/g10/sign.c b/g10/sign.c index 062fa9f48..1a1a80bc6 100644 --- a/g10/sign.c +++ b/g10/sign.c @@ -26,7 +26,6 @@ #include #include #include -#include /* need sleep() */ #include "gpg.h" #include "options.h" @@ -47,8 +46,6 @@ #ifdef HAVE_DOSISH_SYSTEM #define LF "\r\n" -void __stdcall Sleep(ulong); -#define sleep(a) Sleep((a)*1000) #else #define LF "\n" #endif @@ -1563,7 +1560,7 @@ update_keysig_packet( PKT_signature **ret_sig, one. */ while(sig->timestamp<=orig_sig->timestamp) { - sleep(1); + gnupg_sleep (1); sig->timestamp=make_timestamp(); } diff --git a/scd/scdaemon.c b/scd/scdaemon.c index f9a99922f..b2508943d 100644 --- a/scd/scdaemon.c +++ b/scd/scdaemon.c @@ -519,7 +519,7 @@ main (int argc, char **argv ) { log_debug ("waiting for debugger - my pid is %u .....\n", (unsigned int)getpid()); - sleep (debug_wait); + gnupg_sleep (debug_wait); log_debug ("... okay\n"); } diff --git a/sm/ChangeLog b/sm/ChangeLog index c5d342bd2..896b5c368 100644 --- a/sm/ChangeLog +++ b/sm/ChangeLog @@ -1,3 +1,7 @@ +2007-06-20 Werner Koch + + * call-agent.c (start_agent) [W32]: Start the agent on the fly. + 2007-06-18 Marcus Brinkmann * gpgsm.c (main): Percent escape output of --gpgconf-list. diff --git a/sm/call-agent.c b/sm/call-agent.c index 1ac5412be..b30fe60df 100644 --- a/sm/call-agent.c +++ b/sm/call-agent.c @@ -37,7 +37,8 @@ #include "i18n.h" #include "asshelp.h" #include "keydb.h" /* fixme: Move this to import.c */ -#include "../common/membuf.h" +#include "membuf.h" +#include "exechelp.h" static assuan_context_t agent_ctx = NULL; @@ -83,21 +84,12 @@ start_agent (ctrl_t ctrl) infostr = force_pipe_server? NULL : getenv ("GPG_AGENT_INFO"); if (!infostr || !*infostr) { - const char *pgmname; - const char *argv[3]; char *sockname; - int no_close_list[3]; - int i; /* First check whether we can connect at the standard socket. */ sockname = make_filename (opt.homedir, "S.gpg-agent", NULL); rc = assuan_socket_connect (&ctx, sockname, 0); - xfree (sockname); -#ifdef HAVE_W32_SYSTEM -# warning Print a warning if connecting is not possible - /* and offer to fire up the agent. */ -#endif if (rc) { @@ -112,30 +104,71 @@ start_agent (ctrl_t ctrl) gpg_error_t tmperr = gpg_error (gpg_err_code_from_errno (errno)); log_error ("error flushing pending output: %s\n", strerror (errno)); + xfree (sockname); return tmperr; } if (!opt.agent_program || !*opt.agent_program) opt.agent_program = gnupg_module_name (GNUPG_MODULE_NAME_AGENT); - if ( !(pgmname = strrchr (opt.agent_program, '/'))) - pgmname = opt.agent_program; - else - pgmname++; - argv[0] = pgmname; - argv[1] = "--server"; - argv[2] = NULL; +#ifdef HAVE_W32_SYSTEM + { + /* Under Windows we start the server in daemon mode. This + is because the default is to use the standard socket + and thus there is no need for the GPG_AGENT_INFO + envvar. This is possible as we don't have a real unix + domain socket but use a plain file and thus there is no + need to care about non-local file systems. */ + const char *argv[3]; - i=0; - if (log_get_fd () != -1) - no_close_list[i++] = log_get_fd (); - no_close_list[i++] = fileno (stderr); - no_close_list[i] = -1; + /* The --no-reuse-standard option makes sure that we don't + start a second instance of a agent in case another + process has started one in the meantime. */ + argv[0] = "--daemon"; + argv[1] = "--no-reuse-standard-socket"; + argv[2] = NULL; - /* Connect to the agent and perform initial handshaking. */ - rc = assuan_pipe_connect (&ctx, opt.agent_program, argv, - no_close_list); + rc = gnupg_spawn_process_detached (opt.agent_program, argv, NULL); + if (rc) + log_debug ("failed to start agent `%s': %s\n", + opt.agent_program, gpg_strerror (rc)); + else + { + /* Give the agent some time to prepare itself. */ + gnupg_sleep (3); + /* Now try again to connect the agent. */ + rc = assuan_socket_connect (&ctx, sockname, 0); + } + } +#else /*!HAVE_W32_SYSTEM*/ + { + const char *pgmname; + const char *argv[3]; + int no_close_list[3]; + int i; + + if ( !(pgmname = strrchr (opt.agent_program, '/'))) + pgmname = opt.agent_program; + else + pgmname++; + + argv[0] = pgmname; + argv[1] = "--server"; + argv[2] = NULL; + + i=0; + if (log_get_fd () != -1) + no_close_list[i++] = log_get_fd (); + no_close_list[i++] = fileno (stderr); + no_close_list[i] = -1; + + /* Connect to the agent and perform initial handshaking. */ + rc = assuan_pipe_connect (&ctx, opt.agent_program, argv, + no_close_list); + } +#endif /*!HAVE_W32_SYSTEM*/ } + xfree (sockname); } else { diff --git a/sm/gpgsm.c b/sm/gpgsm.c index f6d2b8444..b6a3e69c2 100644 --- a/sm/gpgsm.c +++ b/sm/gpgsm.c @@ -1438,7 +1438,7 @@ main ( int argc, char **argv) { log_debug ("waiting for debugger - my pid is %u .....\n", (unsigned int)getpid()); - sleep (debug_wait); + gnupg_sleep (debug_wait); log_debug ("... okay\n"); } gpgsm_server (recplist);