1
0
mirror of https://git.tartarus.org/simon/putty.git synced 2025-01-09 09:27:59 +00:00

Windows: use the named pipe for normal agent queries.

As in the previous commit, this means that agent_query() is now able
to operate in an asynchronous mode, so that if Pageant takes time to
answer a request, the GUI of the PuTTY instance making the request
won't be blocked.

Also as in the previous commit, we still fall back to the WM_COPYDATA
protocol if the new named pipe protocol isn't available.
This commit is contained in:
Simon Tatham 2020-01-01 19:28:29 +00:00
parent cf29125fb4
commit c2b135c92a
2 changed files with 177 additions and 14 deletions

2
Recipe
View File

@ -341,7 +341,7 @@ pageant : [G] winpgnt pageant sshrsa sshpubk sshdes ARITH sshmd5 version
+ tree234 MISC sshaes sshsha winsecur winpgntc aqsync sshdss sshsh256
+ sshsh512 winutils sshecc winmisc winmiscs winhelp conf pageant.res
+ sshauxcrypt sshhmac wincapi winnps winnpc winhsock errsock winnet
+ winhandl callback be_misc winselgui LIBS
+ winhandl callback be_misc winselgui winhandl LIBS
puttygen : [G] winpgen sshrsag sshdssg sshprime sshdes ARITH sshmd5 version
+ sshrand winnoise sshsha winstore MISC winctrls sshrsa sshdss winmisc

View File

@ -16,7 +16,7 @@
#define AGENT_COPYDATA_ID 0x804e50ba /* random goop */
bool agent_exists(void)
static bool wm_copydata_agent_exists(void)
{
HWND hwnd;
hwnd = FindWindow("Pageant", "Pageant");
@ -26,14 +26,7 @@ bool agent_exists(void)
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)
static void wm_copydata_agent_query(strbuf *query, void **out, int *outlen)
{
HWND hwnd;
char *mapname;
@ -49,11 +42,11 @@ agent_pending_query *agent_query(
*outlen = 0;
if (query->len > AGENT_MAX_MSGLEN)
return NULL; /* query too large */
return; /* query too large */
hwnd = FindWindow("Pageant", "Pageant");
if (!hwnd)
return NULL; /* *out == NULL, so failure */
return; /* *out == NULL, so failure */
mapname = dupprintf("PageantRequest%08x", (unsigned)GetCurrentThreadId());
psa = NULL;
@ -94,7 +87,7 @@ agent_pending_query *agent_query(
0, AGENT_MAX_MSGLEN, mapname);
if (filemap == NULL || filemap == INVALID_HANDLE_VALUE) {
sfree(mapname);
return NULL; /* *out == NULL, so failure */
return; /* *out == NULL, so failure */
}
p = MapViewOfFile(filemap, FILE_MAP_WRITE, 0, 0, 0);
strbuf_finalise_agent_query(query);
@ -134,7 +127,6 @@ agent_pending_query *agent_query(
sfree(mapname);
if (psd)
LocalFree(psd);
return NULL;
}
#ifndef NO_SECURITY
@ -173,6 +165,159 @@ void agent_free_connect_ctx(agent_connect_ctx *ctx)
sfree(ctx);
}
static bool named_pipe_agent_exists(void)
{
char *pipename = agent_named_pipe_name();
DWORD type = GetFileType(pipename);
sfree(pipename);
return type == FILE_TYPE_PIPE;
}
bool agent_exists(void)
{
return named_pipe_agent_exists() || wm_copydata_agent_exists();
}
struct agent_pending_query {
struct handle *handle;
strbuf *response;
void (*callback)(void *, void *, int);
void *callback_ctx;
};
static int named_pipe_agent_accumulate_response(
strbuf *sb, const void *data, size_t len)
{
put_data(sb, data, len);
if (sb->len >= 4) {
uint32_t length_field = GET_32BIT_MSB_FIRST(sb->u);
if (length_field > AGENT_MAX_MSGLEN)
return -1; /* badly formatted message */
int overall_length = length_field + 4;
if (sb->len >= overall_length)
return overall_length;
}
return 0; /* not done yet */
}
static size_t named_pipe_agent_gotdata(
struct handle *h, const void *data, size_t len, int err)
{
agent_pending_query *pq = handle_get_privdata(h);
if (err || len == 0) {
pq->callback(pq->callback_ctx, NULL, 0);
agent_cancel_query(pq);
}
int status = named_pipe_agent_accumulate_response(pq->response, data, len);
if (status == -1) {
pq->callback(pq->callback_ctx, NULL, 0);
agent_cancel_query(pq);
} else if (status > 0) {
void *response_buf = strbuf_to_str(pq->response);
pq->response = NULL;
pq->callback(pq->callback_ctx, response_buf, status);
agent_cancel_query(pq);
}
return 0;
}
agent_pending_query *named_pipe_agent_query(
strbuf *query, void **out, int *outlen,
void (*callback)(void *, void *, int), void *callback_ctx)
{
agent_pending_query *pq = NULL;
char *err = NULL, *pipename = NULL;
strbuf *sb = NULL;
HANDLE pipehandle;
pipename = agent_named_pipe_name();
pipehandle = connect_to_named_pipe(pipename, &err);
if (pipehandle == INVALID_HANDLE_VALUE)
goto failure;
strbuf_finalise_agent_query(query);
for (DWORD done = 0; done < query->len ;) {
DWORD nwritten;
bool ret = WriteFile(pipehandle, query->s + done, query->len - done,
&nwritten, NULL);
if (!ret)
goto failure;
done += nwritten;
}
if (!callback) {
int status;
sb = strbuf_new_nm();
do {
char buf[1024];
DWORD nread;
bool ret = ReadFile(pipehandle, buf, sizeof(buf), &nread, NULL);
if (!ret)
goto failure;
status = named_pipe_agent_accumulate_response(sb, buf, nread);
} while (status == 0);
if (status == -1)
goto failure;
*out = strbuf_to_str(sb);
*outlen = status;
sb = NULL;
pq = NULL;
goto out;
}
pq = snew(agent_pending_query);
pq->handle = handle_input_new(pipehandle, named_pipe_agent_gotdata, pq, 0);
pipehandle = NULL; /* prevent it being closed below */
pq->response = strbuf_new_nm();
pq->callback = callback;
pq->callback_ctx = callback_ctx;
goto out;
failure:
*out = NULL;
*outlen = 0;
pq = NULL;
out:
sfree(err);
sfree(pipename);
if (pipehandle != INVALID_HANDLE_VALUE)
CloseHandle(pipehandle);
if (sb)
strbuf_free(sb);
return pq;
}
void agent_cancel_query(agent_pending_query *pq)
{
handle_free(pq->handle);
if (pq->response)
strbuf_free(pq->response);
sfree(pq);
}
agent_pending_query *agent_query(
strbuf *query, void **out, int *outlen,
void (*callback)(void *, void *, int), void *callback_ctx)
{
agent_pending_query *pq = named_pipe_agent_query(
query, out, outlen, callback, callback_ctx);
if (pq || *out)
return pq;
wm_copydata_agent_query(query, out, outlen);
return NULL;
}
#else /* NO_SECURITY */
Socket *agent_connect(void *vctx, Plug *plug)
@ -189,4 +334,22 @@ void agent_free_connect_ctx(agent_connect_ctx *ctx)
{
}
bool agent_exists(void)
{
return wm_copydata_agent_exists();
}
agent_pending_query *agent_query(
strbuf *query, void **out, int *outlen,
void (*callback)(void *, void *, int), void *callback_ctx)
{
wm_copydata_agent_query(query, out, outlen);
return NULL;
}
void agent_cancel_query(agent_pending_query *q)
{
unreachable("Windows agent queries are never asynchronous!");
}
#endif /* NO_SECURITY */