mirror of
git://git.gnupg.org/gnupg.git
synced 2025-07-02 22:46:30 +02:00
ssh: Add support for Putty.
* agent/gpg-agent.c [W32]: Include Several Windows header.
(opts): Change help text for enable-ssh-support.
(opts, main): Add option --enable-putty-support
(putty_support, PUTTY_IPC_MAGIC, PUTTY_IPC_MAXLEN): New for W32.
(agent_init_default_ctrl): Add and asssert call.
(putty_message_proc, putty_message_thread): New.
(handle_connections) [W32]: Start putty message thread.
* common/sysutils.c (w32_get_user_sid): New for W32 only
* tools/gpgconf-comp.c (gc_options_gpg_agent): Add
--enable-ssh-support and --enable-putty-support. Make the
configuration group visible at basic level.
* agent/command-ssh.c (serve_mmapped_ssh_request): New for W32 only.
--
This patch enables support for Putty. It has been tested with Putty
0.62 using an Unix created ssh key copied to the private-keys-v1.d
directory on Windows and with a manually crafted sshcontrol file. It
also works with a smartcard key.
May thanks to gniibe who implemented a proxy in Python to test the
putty/gpg-agent communication.
Signed-off-by: Werner Koch <wk@gnupg.org>
(cherry picked from commit 9f32499f99
)
Resolved conflicts:
NEWS
agent/agent.h
agent/gpg-agent.c: Convert from pth to npth.
common/sysutils.c
common/sysutils.h
This commit is contained in:
parent
179012ddd4
commit
5105c8d2d3
7 changed files with 473 additions and 7 deletions
|
@ -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;
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue