1
0
mirror of https://git.tartarus.org/simon/putty.git synced 2025-01-10 18:07:59 +00:00
putty-source/windows/winpgntc.c

127 lines
3.4 KiB
C
Raw Normal View History

/*
* Pageant client code.
*/
#include <stdio.h>
#include <stdlib.h>
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 19:40:38 +00:00
#include <assert.h>
#include "putty.h"
#include "pageant.h" /* for AGENT_MAX_MSGLEN */
#ifndef NO_SECURITY
#include "winsecur.h"
#endif
#define AGENT_COPYDATA_ID 0x804e50ba /* random goop */
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(
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) {
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;
}