| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300 | /* * 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 */        { // WINSCP        int overall_length = length_field + 4;        if (sb->len >= overall_length)            return overall_length;        } // WINSCP    }    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;    }    { // WINSCP    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;    } // WINSCP}static agent_pending_query *named_pipe_agent_query(    strbuf *query, void **out, int *outlen,    void (*callback)(void *, void *, int), void *callback_ctx, struct callback_set * callback_set) // WINSCP{    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);    { // WINSCP    DWORD done; // WINSCP    for (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(callback_set, pipehandle, named_pipe_agent_gotdata, pq, 0); // WINSCP    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;    } // WINSCP}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, struct callback_set * callback_set) // WINSCP{    agent_pending_query *pq = named_pipe_agent_query(        query, out, outlen, callback, callback_ctx, callback_set); // WINSCP    if (pq || *out)        return pq;    wm_copydata_agent_query(query, out, outlen);    return NULL;}
 |