123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294 |
- /*
- * Pageant client code.
- */
- #include <stdio.h>
- #include <stdlib.h>
- #include <assert.h>
- #include "putty.h"
- #include "pageant.h" /* for AGENT_MAX_MSGLEN */
- #include "security-api.h"
- #include "cryptoapi.h"
- static bool wm_copydata_agent_exists(void)
- {
- HWND hwnd;
- hwnd = FindWindow("Pageant", "Pageant");
- if (!hwnd)
- return false;
- else
- return true;
- }
- static void wm_copydata_agent_query(strbuf *query, void **out, int *outlen)
- {
- 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; /* query too large */
- hwnd = FindWindow("Pageant", "Pageant");
- if (!hwnd)
- return; /* *out == NULL, so failure */
- mapname = dupprintf("PageantRequest%08x", (unsigned)GetCurrentThreadId());
- psa = NULL;
- 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;
- }
- }
- }
- }
- filemap = CreateFileMapping(INVALID_HANDLE_VALUE, psa, PAGE_READWRITE,
- 0, AGENT_MAX_MSGLEN, mapname);
- if (filemap == NULL || filemap == INVALID_HANDLE_VALUE) {
- sfree(mapname);
- return; /* *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);
- }
- Socket *agent_connect(Plug *plug)
- {
- char *pipename = agent_named_pipe_name();
- Socket *s = new_named_pipe_client(pipename, plug);
- sfree(pipename);
- return s;
- }
- static bool named_pipe_agent_exists(void)
- {
- char *pipename = agent_named_pipe_name();
- WIN32_FIND_DATA data;
- HANDLE ffh = FindFirstFile(pipename, &data);
- sfree(pipename);
- if (ffh == INVALID_HANDLE_VALUE)
- return false;
- FindClose(ffh);
- return true;
- }
- bool agent_exists(void)
- {
- return named_pipe_agent_exists() || wm_copydata_agent_exists();
- }
- struct agent_pending_query {
- struct handle *handle;
- HANDLE os_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);
- return 0;
- }
- 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;
- }
- static 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);
- pq->os_handle = pipehandle;
- pipehandle = INVALID_HANDLE_VALUE; /* 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);
- CloseHandle(pq->os_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;
- }
|