1
0
mirror of https://git.tartarus.org/simon/putty.git synced 2025-01-10 01:48:00 +00:00
putty-source/windows/winpgntc.c
Simon Tatham 4ff22863d8 Rewrite agent forwarding to serialise requests.
The previous agent-forwarding system worked by passing each complete
query received from the input to agent_query() as soon as it was
ready. So if the remote client were to pipeline multiple requests,
then Unix PuTTY (in which agent_query() works asynchronously) would
parallelise them into many _simultaneous_ connections to the real
agent - and would not track which query went out first, so that if the
real agent happened to send its replies (to what _it_ thought were
independent clients) in the wrong order, then PuTTY would serialise
the replies on to the forwarding channel in whatever order it got
them, which wouldn't be the order the remote client was expecting.

To solve this, I've done a considerable rewrite, which keeps the
request stream in a bufchain, and only removes data from the bufchain
when it has a complete request. Then, if agent_query decides to be
asynchronous, the forwarding system waits for _that_ agent response
before even trying to extract the next request's worth of data from
the bufchain.

As an added bonus (in principle), this gives agent-forwarding channels
some actual flow control for the first time ever! If a client spams us
with an endless stream of rapid requests, and never reads its
responses, then the output side of the channel will run out of window,
which causes us to stop processing requests until we have space to
send responses again, which in turn causes us to stop granting extra
window on the input side, which serves the client right.
2017-01-29 20:25:09 +00:00

123 lines
3.3 KiB
C

/*
* Pageant client code.
*/
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include "putty.h"
#ifndef NO_SECURITY
#include "winsecur.h"
#endif
#define AGENT_COPYDATA_ID 0x804e50ba /* random goop */
#define AGENT_MAX_MSGLEN 8192
int agent_exists(void)
{
HWND hwnd;
hwnd = FindWindow("Pageant", "Pageant");
if (!hwnd)
return FALSE;
else
return TRUE;
}
void agent_cancel_query(agent_pending_query *q)
{
assert(0 && "Windows agent queries are never asynchronous!");
}
agent_pending_query *agent_query(
void *in, int inlen, 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;
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);
memcpy(p, in, inlen);
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) {
retlen = 4 + GET_32BIT(p);
ret = snewn(retlen, unsigned char);
if (ret) {
memcpy(ret, p, retlen);
*out = ret;
*outlen = retlen;
}
}
UnmapViewOfFile(p);
CloseHandle(filemap);
sfree(mapname);
if (psd)
LocalFree(psd);
return NULL;
}