aboutsummaryrefslogtreecommitdiffstats
path: root/agent/gpg-agent.c
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--agent/gpg-agent.c254
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;