summaryrefslogtreecommitdiff
path: root/agent/gpg-agent.c
diff options
context:
space:
mode:
Diffstat (limited to 'agent/gpg-agent.c')
-rw-r--r--agent/gpg-agent.c280
1 files changed, 270 insertions, 10 deletions
diff --git a/agent/gpg-agent.c b/agent/gpg-agent.c
index b00d899..bf2a26d 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*/
@@ -102,10 +112,12 @@ enum cmd_and_opt_values
oIgnoreCacheForSigning,
oAllowMarkTrusted,
+ oNoAllowMarkTrusted,
oAllowPresetPassphrase,
oKeepTTY,
oKeepDISPLAY,
oSSHSupport,
+ oPuttySupport,
oDisableScdaemon,
oWriteEnvFile
};
@@ -120,8 +132,8 @@ static ARGPARSE_OPTS opts[] = {
{ 301, NULL, 0, N_("@Options:\n ") },
- { oServer, "server", 0, N_("run in server mode (foreground)") },
{ oDaemon, "daemon", 0, N_("run in daemon mode (background)") },
+ { oServer, "server", 0, N_("run in server mode (foreground)") },
{ oVerbose, "verbose", 0, N_("verbose") },
{ oQuiet, "quiet", 0, N_("be somewhat more quiet") },
{ oSh, "sh", 0, N_("sh-style command output") },
@@ -173,11 +185,19 @@ static ARGPARSE_OPTS opts[] = {
{ oIgnoreCacheForSigning, "ignore-cache-for-signing", 0,
N_("do not use the PIN cache when signing")},
- { oAllowMarkTrusted, "allow-mark-trusted", 0,
- N_("allow clients to mark keys as \"trusted\"")},
+ { oNoAllowMarkTrusted, "no-allow-mark-trusted", 0,
+ N_("disallow clients to mark keys as \"trusted\"")},
+ { oAllowMarkTrusted, "allow-mark-trusted", 0, "@"},
{ oAllowPresetPassphrase, "allow-preset-passphrase", 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}
@@ -202,6 +222,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;
@@ -277,12 +308,15 @@ static int check_for_running_agent (int silent, int mode);
/* Pth wrapper function definitions. */
ASSUAN_SYSTEM_PTH_IMPL;
+#if GCRYPT_VERSION_NUMBER < 0x010600
GCRY_THREAD_OPTION_PTH_IMPL;
+#if GCRY_THREAD_OPTION_VERSION < 1
static int fixed_gcry_pth_init (void)
{
return pth_self ()? 0 : (pth_init () == FALSE) ? errno : 0;
}
-
+#endif
+#endif /*GCRYPT_VERSION_NUMBER < 0x10600*/
#ifndef PTH_HAVE_PTH_THREAD_ID
static unsigned long pth_thread_id (void)
@@ -473,7 +507,7 @@ parse_rereadable_options (ARGPARSE_ARGS *pargs, int reread)
opt.max_passphrase_days = MAX_PASSPHRASE_DAYS;
opt.enable_passhrase_history = 0;
opt.ignore_cache_for_signing = 0;
- opt.allow_mark_trusted = 0;
+ opt.allow_mark_trusted = 1;
opt.disable_scdaemon = 0;
return 1;
}
@@ -533,6 +567,7 @@ parse_rereadable_options (ARGPARSE_ARGS *pargs, int reread)
case oIgnoreCacheForSigning: opt.ignore_cache_for_signing = 1; break;
case oAllowMarkTrusted: opt.allow_mark_trusted = 1; break;
+ case oNoAllowMarkTrusted: opt.allow_mark_trusted = 0; break;
case oAllowPresetPassphrase: opt.allow_preset_passphrase = 1; break;
@@ -592,15 +627,19 @@ main (int argc, char **argv )
init_common_subsystems ();
- /* Libgcrypt requires us to register the threading model first.
+#if GCRYPT_VERSION_NUMBER < 0x010600
+ /* Libgcrypt < 1.6 requires us to register the threading model first.
Note that this will also do the pth_init. */
+#if GCRY_THREAD_OPTION_VERSION < 1
gcry_threads_pth.init = fixed_gcry_pth_init;
+#endif
err = gcry_control (GCRYCTL_SET_THREAD_CBS, &gcry_threads_pth);
if (err)
{
log_fatal ("can't register GNU Pth with Libgcrypt: %s\n",
gpg_strerror (err));
}
+#endif /*GCRYPT_VERSION_NUMBER < 0x010600*/
/* Check that the libraries are suitable. Do it here because
@@ -802,6 +841,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;
@@ -921,10 +967,15 @@ main (int argc, char **argv )
GC_OPT_FLAG_NONE|GC_OPT_FLAG_RUNTIME);
printf ("ignore-cache-for-signing:%lu:\n",
GC_OPT_FLAG_NONE|GC_OPT_FLAG_RUNTIME);
- printf ("allow-mark-trusted:%lu:\n",
+ printf ("no-allow-mark-trusted:%lu:\n",
GC_OPT_FLAG_NONE|GC_OPT_FLAG_RUNTIME);
printf ("disable-scdaemon:%lu:\n",
GC_OPT_FLAG_NONE|GC_OPT_FLAG_RUNTIME);
+#ifdef HAVE_W32_SYSTEM
+ printf ("enable-putty-support:%lu:\n", GC_OPT_FLAG_NONE);
+#else
+ printf ("enable-ssh-support:%lu:\n", GC_OPT_FLAG_NONE);
+#endif
agent_exit (0);
}
@@ -1289,6 +1340,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. */
@@ -1306,7 +1359,6 @@ agent_init_default_ctrl (ctrl_t ctrl)
xfree (ctrl->lc_messages);
ctrl->lc_messages = default_lc_messages? xtrystrdup (default_lc_messages)
/**/ : NULL;
-
}
@@ -1785,6 +1837,199 @@ 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)
+ {
+ /* pth_leave (); */
+ /* log_debug ("putty loop: received WM_%u\n", msg ); */
+ /* pth_enter (); */
+ 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)
+ {
+ pth_leave ();
+ log_debug ("ssh map file '%s'", mapfile);
+ pth_enter ();
+ }
+
+ maphd = OpenFileMapping (FILE_MAP_ALL_ACCESS, FALSE, mapfile);
+ if (DBG_ASSUAN)
+ {
+ pth_leave ();
+ log_debug ("ssh map handle %p\n", maphd);
+ pth_enter ();
+ }
+
+ if (!maphd || maphd == INVALID_HANDLE_VALUE)
+ return 0;
+
+ pth_leave ();
+
+ 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);
+
+ pth_enter ();
+
+ 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 0x%lx started\n", pth_thread_id ());
+
+ /* The message loop runs as thread independet from out Pth system.
+ This also meand that we need to make sure that we switch back to
+ our system before calling any no-windows function. */
+ pth_enter ();
+
+ /* First create a window to make sure that a message queue exists
+ for this thread. */
+ if (!RegisterClass (&wndwclass))
+ {
+ pth_leave ();
+ 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)
+ {
+ pth_leave ();
+ log_error ("error creating Pageant window");
+ return NULL;
+ }
+
+ while (GetMessage(&msg, NULL, 0, 0))
+ {
+ TranslateMessage(&msg);
+ DispatchMessage(&msg);
+ }
+
+ /* Back to Pth. */
+ pth_leave ();
+
+ if (opt.verbose)
+ log_info ("putty message loop thread 0x%lx stopped\n", pth_thread_id ());
+ return NULL;
+}
+#endif /*HAVE_W32_SYSTEM*/
+
+
/* This is the standard connection thread's main function. */
static void *
start_connection_thread (void *arg)
@@ -1894,6 +2139,21 @@ handle_connections (gnupg_fd_t listen_fd, gnupg_fd_t listen_fd_ssh)
#endif
time_ev = NULL;
+ /* 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)
+ {
+ pth_attr_set (tattr, PTH_ATTR_NAME, "putty message loop");
+ if (!pth_spawn (tattr, putty_message_thread, NULL))
+ {
+ log_error ("error spawning putty message loop: %s\n",
+ strerror (errno) );
+ }
+ }
+#endif /*HAVE_W32_SYSTEM*/
+
/* Set a flag to tell call-scd.c that it may enable event
notifications. */
opt.sigusr2_enabled = 1;