diff options
Diffstat (limited to '')
-rw-r--r-- | agent/gpg-agent.c | 254 |
1 files changed, 251 insertions, 3 deletions
diff --git a/agent/gpg-agent.c b/agent/gpg-agent.c index c53566b2b..25750f288 100644 --- a/agent/gpg-agent.c +++ b/agent/gpg-agent.c @@ -1,6 +1,7 @@ /* gpg-agent.c - The GnuPG Agent * Copyright (C) 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2009, * 2010 Free Software Foundation, Inc. + * Copyright (C) 2013 Werner Koch * * This file is part of GnuPG. * @@ -30,7 +31,16 @@ #include <time.h> #include <fcntl.h> #include <sys/stat.h> -#ifndef HAVE_W32_SYSTEM +#ifdef HAVE_W32_SYSTEM +# ifndef WINVER +# define WINVER 0x0500 /* Same as in common/sysutils.c */ +# endif +# ifdef HAVE_WINSOCK2_H +# include <winsock2.h> +# endif +# include <aclapi.h> +# include <sddl.h> +#else /*!HAVE_W32_SYSTEM*/ # include <sys/socket.h> # include <sys/un.h> #endif /*!HAVE_W32_SYSTEM*/ @@ -111,6 +121,7 @@ enum cmd_and_opt_values oKeepTTY, oKeepDISPLAY, oSSHSupport, + oPuttySupport, oDisableScdaemon, oDisableCheckOwnSocket, oWriteEnvFile @@ -186,7 +197,14 @@ static ARGPARSE_OPTS opts[] = { N_("allow presetting passphrase")}, { oAllowLoopbackPinentry, "allow-loopback-pinentry", 0, N_("allow presetting passphrase")}, - { oSSHSupport, "enable-ssh-support", 0, N_("enable ssh-agent emulation") }, + { oSSHSupport, "enable-ssh-support", 0, N_("enable ssh support") }, + { oPuttySupport, "enable-putty-support", 0, +#ifdef HAVE_W32_SYSTEM + N_("enable putty support") +#else + "@" +#endif + }, { oWriteEnvFile, "write-env-file", 2|8, N_("|FILE|write environment settings also to FILE")}, {0} @@ -218,6 +236,17 @@ static ARGPARSE_OPTS opts[] = { #endif +#ifdef HAVE_W32_SYSTEM +/* Flag indicating that support for Putty has been enabled. */ +static int putty_support; +/* A magic value used with WM_COPYDATA. */ +#define PUTTY_IPC_MAGIC 0x804e50ba +/* To avoid surprises we limit the size of the mapped IPC file to this + value. Putty currently (0.62) uses 8k, thus 16k should be enough + for the foreseeable future. */ +#define PUTTY_IPC_MAXLEN 16384 +#endif /*HAVE_W32_SYSTEM*/ + /* The list of open file descriptors at startup. Note that this list has been allocated using the standard malloc. */ static int *startup_fd_list; @@ -815,6 +844,13 @@ main (int argc, char **argv ) case oKeepDISPLAY: opt.keep_display = 1; break; case oSSHSupport: opt.ssh_support = 1; break; + case oPuttySupport: +# ifdef HAVE_W32_SYSTEM + putty_support = 1; + opt.ssh_support = 1; +# endif + break; + case oWriteEnvFile: if (pargs.r_type) env_file_name = pargs.r.ret_str; @@ -976,6 +1012,11 @@ main (int argc, char **argv ) GC_OPT_FLAG_NONE|GC_OPT_FLAG_RUNTIME); es_printf ("disable-scdaemon:%lu:\n", GC_OPT_FLAG_NONE|GC_OPT_FLAG_RUNTIME); +#ifdef HAVE_W32_SYSTEM + es_printf ("enable-putty-support:%lu:\n", GC_OPT_FLAG_NONE); +#else + es_printf ("enable-ssh-support:%lu:\n", GC_OPT_FLAG_NONE); +#endif agent_exit (0); } @@ -1311,6 +1352,8 @@ agent_exit (int rc) static void agent_init_default_ctrl (ctrl_t ctrl) { + assert (ctrl->session_env); + /* Note we ignore malloc errors because we can't do much about it and the request will fail anyway shortly after this initialization. */ @@ -1328,7 +1371,6 @@ agent_init_default_ctrl (ctrl_t ctrl) xfree (ctrl->lc_messages); ctrl->lc_messages = default_lc_messages? xtrystrdup (default_lc_messages) /**/ : NULL; - ctrl->cache_ttl_opt_preset = CACHE_TTL_OPT_PRESET; } @@ -1820,6 +1862,196 @@ check_nonce (ctrl_t ctrl, assuan_sock_nonce_t *nonce) } +#ifdef HAVE_W32_SYSTEM +/* The window message processing function for Putty. Warning: This + code runs as a native Windows thread. Use of our own functions + needs to be bracket with pth_leave/pth_enter. */ +static LRESULT CALLBACK +putty_message_proc (HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) +{ + int ret = 0; + int w32rc; + COPYDATASTRUCT *cds; + const char *mapfile; + HANDLE maphd; + PSID mysid = NULL; + PSID mapsid = NULL; + void *data = NULL; + PSECURITY_DESCRIPTOR psd = NULL; + ctrl_t ctrl = NULL; + + if (msg != WM_COPYDATA) + { + return DefWindowProc (hwnd, msg, wparam, lparam); + } + + cds = (COPYDATASTRUCT*)lparam; + if (cds->dwData != PUTTY_IPC_MAGIC) + return 0; /* Ignore data with the wrong magic. */ + mapfile = cds->lpData; + if (!cds->cbData || mapfile[cds->cbData - 1]) + return 0; /* Ignore empty and non-properly terminated strings. */ + + if (DBG_ASSUAN) + { + npth_protect (); + log_debug ("ssh map file '%s'", mapfile); + npth_unprotect (); + } + + maphd = OpenFileMapping (FILE_MAP_ALL_ACCESS, FALSE, mapfile); + if (DBG_ASSUAN) + { + npth_protect (); + log_debug ("ssh map handle %p\n", maphd); + npth_unprotect (); + } + + if (!maphd || maphd == INVALID_HANDLE_VALUE) + return 0; + + npth_protect (); + + mysid = w32_get_user_sid (); + if (!mysid) + { + log_error ("error getting my sid\n"); + goto leave; + } + + w32rc = GetSecurityInfo (maphd, SE_KERNEL_OBJECT, + OWNER_SECURITY_INFORMATION, + &mapsid, NULL, NULL, NULL, + &psd); + if (w32rc) + { + log_error ("error getting sid of ssh map file: rc=%d", w32rc); + goto leave; + } + + if (DBG_ASSUAN) + { + char *sidstr; + + if (!ConvertSidToStringSid (mysid, &sidstr)) + sidstr = NULL; + log_debug (" my sid: '%s'", sidstr? sidstr: "[error]"); + LocalFree (sidstr); + if (!ConvertSidToStringSid (mapsid, &sidstr)) + sidstr = NULL; + log_debug ("ssh map file sid: '%s'", sidstr? sidstr: "[error]"); + LocalFree (sidstr); + } + + if (!EqualSid (mysid, mapsid)) + { + log_error ("ssh map file has a non-matching sid\n"); + goto leave; + } + + data = MapViewOfFile (maphd, FILE_MAP_ALL_ACCESS, 0, 0, 0); + if (DBG_ASSUAN) + log_debug ("ssh IPC buffer at %p\n", data); + if (!data) + goto leave; + + /* log_printhex ("request:", data, 20); */ + + ctrl = xtrycalloc (1, sizeof *ctrl); + if (!ctrl) + { + log_error ("error allocating connection control data: %s\n", + strerror (errno) ); + goto leave; + } + ctrl->session_env = session_env_new (); + if (!ctrl->session_env) + { + log_error ("error allocating session environment block: %s\n", + strerror (errno) ); + goto leave; + } + + agent_init_default_ctrl (ctrl); + if (!serve_mmapped_ssh_request (ctrl, data, PUTTY_IPC_MAXLEN)) + ret = 1; /* Valid ssh message has been constructed. */ + agent_deinit_default_ctrl (ctrl); + /* log_printhex (" reply:", data, 20); */ + + leave: + xfree (ctrl); + if (data) + UnmapViewOfFile (data); + xfree (mapsid); + if (psd) + LocalFree (psd); + xfree (mysid); + CloseHandle (maphd); + + npth_unprotect (); + + return ret; +} +#endif /*HAVE_W32_SYSTEM*/ + + +#ifdef HAVE_W32_SYSTEM +/* The thread handling Putty's IPC requests. */ +static void * +putty_message_thread (void *arg) +{ + WNDCLASS wndwclass = {0, putty_message_proc, 0, 0, + NULL, NULL, NULL, NULL, NULL, "Pageant"}; + HWND hwnd; + MSG msg; + + (void)arg; + + if (opt.verbose) + log_info ("putty message loop thread started\n"); + + /* The message loop runs as thread independent from our nPth system. + This also means that we need to make sure that we switch back to + our system before calling any no-windows function. */ + npth_unprotect (); + + /* First create a window to make sure that a message queue exists + for this thread. */ + if (!RegisterClass (&wndwclass)) + { + npth_protect (); + log_error ("error registering Pageant window class"); + return NULL; + } + hwnd = CreateWindowEx (0, "Pageant", "Pageant", 0, + 0, 0, 0, 0, + HWND_MESSAGE, /* hWndParent */ + NULL, /* hWndMenu */ + NULL, /* hInstance */ + NULL); /* lpParm */ + if (!hwnd) + { + npth_protect (); + log_error ("error creating Pageant window"); + return NULL; + } + + while (GetMessage(&msg, NULL, 0, 0)) + { + TranslateMessage(&msg); + DispatchMessage(&msg); + } + + /* Back to nPth. */ + npth_protect (); + + if (opt.verbose) + log_info ("putty message loop thread stopped\n"); + return NULL; +} +#endif /*HAVE_W32_SYSTEM*/ + + /* This is the standard connection thread's main function. */ static void * start_connection_thread (void *arg) @@ -1920,6 +2152,22 @@ handle_connections (gnupg_fd_t listen_fd, gnupg_fd_t listen_fd_ssh) # endif #endif + /* On Windows we need to fire up a separate thread to listen for + requests from Putty (an SSH client), so we can replace Putty's + Pageant (its ssh-agent implementation). */ +#ifdef HAVE_W32_SYSTEM + if (putty_support) + { + npth_t thread; + + ret = npth_create (&thread, &tattr, putty_message_thread, NULL); + if (ret) + { + log_error ("error spawning putty message loop: %s\n", strerror (ret)); + } + } +#endif /*HAVE_W32_SYSTEM*/ + /* Set a flag to tell call-scd.c that it may enable event notifications. */ opt.sigusr2_enabled = 1; |