1
0
mirror of https://git.tartarus.org/simon/putty.git synced 2025-01-25 01:02:24 +00:00
putty-source/windows/winpgntc.c
Simon Tatham cf29125fb4 Windows: use named-pipe IPC for stream agent forwarding.
Now that Pageant runs a named-pipe server as well as a WM_COPYDATA
server, we prefer the former (if available) for agent forwarding, for
the same reasons as on Unix: it lets us establish a simple raw-data
streaming connection instead of agentf.c's complicated message
boundary detection and buffer management, and if agent connections
ever become stateful, this technique will cope.

On Windows, another advantage of this change is that forwarded agent
requests can now be asynchronous: if the agent takes time to respond
to a request for any reason, then the rest of PuTTY's GUI and SSH
connection are not blocked, and you can carry on working while the
agent is thinking about the request.

(I didn't list that as a benefit of doing the same thing for Unix in
commit ae1148267, because on Unix, agent_query() could _already_ run
asynchronously. It's only on Windows that that's new.)
2020-01-04 14:18:24 +00:00

193 lines
5.2 KiB
C

/*
* Pageant client code.
*/
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include "putty.h"
#include "pageant.h" /* for AGENT_MAX_MSGLEN */
#ifndef NO_SECURITY
#include "winsecur.h"
#include "wincapi.h"
#endif
#define AGENT_COPYDATA_ID 0x804e50ba /* random goop */
bool agent_exists(void)
{
HWND hwnd;
hwnd = FindWindow("Pageant", "Pageant");
if (!hwnd)
return false;
else
return true;
}
void agent_cancel_query(agent_pending_query *q)
{
unreachable("Windows agent queries are never asynchronous!");
}
agent_pending_query *agent_query(
strbuf *query, void **out, int *outlen,
void (*callback)(void *, void *, int), void *callback_ctx)
{
HWND hwnd;
char *mapname;
HANDLE filemap;
unsigned char *p, *ret;
int id, retlen;
COPYDATASTRUCT cds;
SECURITY_ATTRIBUTES sa, *psa;
PSECURITY_DESCRIPTOR psd = NULL;
PSID usersid = NULL;
*out = NULL;
*outlen = 0;
if (query->len > AGENT_MAX_MSGLEN)
return NULL; /* query too large */
hwnd = FindWindow("Pageant", "Pageant");
if (!hwnd)
return NULL; /* *out == NULL, so failure */
mapname = dupprintf("PageantRequest%08x", (unsigned)GetCurrentThreadId());
psa = NULL;
#ifndef NO_SECURITY
if (got_advapi()) {
/*
* Make the file mapping we create for communication with
* Pageant owned by the user SID rather than the default. This
* should make communication between processes with slightly
* different contexts more reliable: in particular, command
* prompts launched as administrator should still be able to
* run PSFTPs which refer back to the owning user's
* unprivileged Pageant.
*/
usersid = get_user_sid();
if (usersid) {
psd = (PSECURITY_DESCRIPTOR)
LocalAlloc(LPTR, SECURITY_DESCRIPTOR_MIN_LENGTH);
if (psd) {
if (p_InitializeSecurityDescriptor
(psd, SECURITY_DESCRIPTOR_REVISION) &&
p_SetSecurityDescriptorOwner(psd, usersid, false)) {
sa.nLength = sizeof(sa);
sa.bInheritHandle = true;
sa.lpSecurityDescriptor = psd;
psa = &sa;
} else {
LocalFree(psd);
psd = NULL;
}
}
}
}
#endif /* NO_SECURITY */
filemap = CreateFileMapping(INVALID_HANDLE_VALUE, psa, PAGE_READWRITE,
0, AGENT_MAX_MSGLEN, mapname);
if (filemap == NULL || filemap == INVALID_HANDLE_VALUE) {
sfree(mapname);
return NULL; /* *out == NULL, so failure */
}
p = MapViewOfFile(filemap, FILE_MAP_WRITE, 0, 0, 0);
strbuf_finalise_agent_query(query);
memcpy(p, query->s, query->len);
cds.dwData = AGENT_COPYDATA_ID;
cds.cbData = 1 + strlen(mapname);
cds.lpData = mapname;
/*
* The user either passed a null callback (indicating that the
* query is required to be synchronous) or CreateThread failed.
* Either way, we need a synchronous request.
*/
id = SendMessage(hwnd, WM_COPYDATA, (WPARAM) NULL, (LPARAM) &cds);
if (id > 0) {
uint32_t length_field = GET_32BIT_MSB_FIRST(p);
if (length_field > 0 && length_field <= AGENT_MAX_MSGLEN - 4) {
retlen = length_field + 4;
ret = snewn(retlen, unsigned char);
memcpy(ret, p, retlen);
*out = ret;
*outlen = retlen;
} else {
/*
* If we get here, we received an out-of-range length
* field, either without space for a message type code or
* overflowing the FileMapping.
*
* Treat this as if Pageant didn't answer at all - which
* actually means we do nothing, and just don't fill in
* out and outlen.
*/
}
}
UnmapViewOfFile(p);
CloseHandle(filemap);
sfree(mapname);
if (psd)
LocalFree(psd);
return NULL;
}
#ifndef NO_SECURITY
char *agent_named_pipe_name(void)
{
char *username, *suffix, *pipename;
username = get_username();
suffix = capi_obfuscate_string("Pageant");
pipename = dupprintf("\\\\.\\pipe\\pageant.%s.%s", username, suffix);
sfree(username);
sfree(suffix);
return pipename;
}
struct agent_connect_ctx {
char *pipename;
};
Socket *agent_connect(void *vctx, Plug *plug)
{
agent_connect_ctx *ctx = (agent_connect_ctx *)vctx;
return new_named_pipe_client(ctx->pipename, plug);
}
agent_connect_ctx *agent_get_connect_ctx(void)
{
agent_connect_ctx *ctx = snew(agent_connect_ctx);
ctx->pipename = agent_named_pipe_name();
return ctx;
}
void agent_free_connect_ctx(agent_connect_ctx *ctx)
{
sfree(ctx->pipename);
sfree(ctx);
}
#else /* NO_SECURITY */
Socket *agent_connect(void *vctx, Plug *plug)
{
unreachable("no agent_connect_ctx can be constructed on this platform");
}
agent_connect_ctx *agent_get_connect_ctx(void)
{
return NULL;
}
void agent_free_connect_ctx(agent_connect_ctx *ctx)
{
}
#endif /* NO_SECURITY */