mirror of
https://git.tartarus.org/simon/putty.git
synced 2025-01-10 18:07:59 +00:00
af78191a9c
code (as introduced in r9043), so that it uses the user SID rather
than the default SID.
This does change the access-control model, in that a Pageant running
with administrator privilege will now serve keys to an unprivileged
PuTTY running as the same user who started Pageant. Owen and I think
this isn't a problem (in particular, it will still not serve keys to a
_different_ user).
More importantly, making the Pageant client and server code work the
same way means that PuTTY and Pageant can still talk to each other
when UAC is turned off, which we've had several reports of r9043
having broken.
[originally from svn r9178]
[r9043 == 05f22632eb
]
266 lines
7.3 KiB
C
266 lines
7.3 KiB
C
/*
|
|
* Pageant client code.
|
|
*/
|
|
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
|
|
#include "putty.h"
|
|
|
|
#ifndef NO_SECURITY
|
|
#include <aclapi.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;
|
|
}
|
|
|
|
/*
|
|
* Unfortunately, this asynchronous agent request mechanism doesn't
|
|
* appear to work terribly well. I'm going to comment it out for
|
|
* the moment, and see if I can come up with a better one :-/
|
|
*/
|
|
#ifdef WINDOWS_ASYNC_AGENT
|
|
|
|
struct agent_query_data {
|
|
COPYDATASTRUCT cds;
|
|
unsigned char *mapping;
|
|
HANDLE handle;
|
|
char *mapname;
|
|
HWND hwnd;
|
|
void (*callback)(void *, void *, int);
|
|
void *callback_ctx;
|
|
};
|
|
|
|
DWORD WINAPI agent_query_thread(LPVOID param)
|
|
{
|
|
struct agent_query_data *data = (struct agent_query_data *)param;
|
|
unsigned char *ret;
|
|
int id, retlen;
|
|
|
|
id = SendMessage(data->hwnd, WM_COPYDATA, (WPARAM) NULL,
|
|
(LPARAM) &data->cds);
|
|
ret = NULL;
|
|
if (id > 0) {
|
|
retlen = 4 + GET_32BIT(data->mapping);
|
|
ret = snewn(retlen, unsigned char);
|
|
if (ret) {
|
|
memcpy(ret, data->mapping, retlen);
|
|
}
|
|
}
|
|
if (!ret)
|
|
retlen = 0;
|
|
UnmapViewOfFile(data->mapping);
|
|
CloseHandle(data->handle);
|
|
sfree(data->mapname);
|
|
|
|
agent_schedule_callback(data->callback, data->callback_ctx, ret, retlen);
|
|
|
|
return 0;
|
|
}
|
|
|
|
#endif
|
|
|
|
/*
|
|
* Dynamically load advapi32.dll for SID manipulation. In its absence,
|
|
* we degrade gracefully.
|
|
*/
|
|
#ifndef NO_SECURITY
|
|
int advapi_initialised = FALSE;
|
|
static HMODULE advapi;
|
|
DECL_WINDOWS_FUNCTION(static, BOOL, OpenProcessToken,
|
|
(HANDLE, DWORD, PHANDLE));
|
|
DECL_WINDOWS_FUNCTION(static, BOOL, GetTokenInformation,
|
|
(HANDLE, TOKEN_INFORMATION_CLASS,
|
|
LPVOID, DWORD, PDWORD));
|
|
DECL_WINDOWS_FUNCTION(static, BOOL, InitializeSecurityDescriptor,
|
|
(PSECURITY_DESCRIPTOR, DWORD));
|
|
DECL_WINDOWS_FUNCTION(static, BOOL, SetSecurityDescriptorOwner,
|
|
(PSECURITY_DESCRIPTOR, PSID, BOOL));
|
|
DECL_WINDOWS_FUNCTION(, DWORD, GetSecurityInfo,
|
|
(HANDLE, SE_OBJECT_TYPE, SECURITY_INFORMATION,
|
|
PSID *, PSID *, PACL *, PACL *,
|
|
PSECURITY_DESCRIPTOR *));
|
|
int init_advapi(void)
|
|
{
|
|
advapi = load_system32_dll("advapi32.dll");
|
|
return advapi &&
|
|
GET_WINDOWS_FUNCTION(advapi, GetSecurityInfo) &&
|
|
GET_WINDOWS_FUNCTION(advapi, OpenProcessToken) &&
|
|
GET_WINDOWS_FUNCTION(advapi, GetTokenInformation) &&
|
|
GET_WINDOWS_FUNCTION(advapi, InitializeSecurityDescriptor) &&
|
|
GET_WINDOWS_FUNCTION(advapi, SetSecurityDescriptorOwner);
|
|
}
|
|
|
|
PSID get_user_sid(void)
|
|
{
|
|
HANDLE proc = NULL, tok = NULL;
|
|
TOKEN_USER *user = NULL;
|
|
DWORD toklen, sidlen;
|
|
PSID sid = NULL, ret = NULL;
|
|
|
|
if ((proc = OpenProcess(MAXIMUM_ALLOWED, FALSE,
|
|
GetCurrentProcessId())) == NULL)
|
|
goto cleanup;
|
|
|
|
if (!p_OpenProcessToken(proc, TOKEN_QUERY, &tok))
|
|
goto cleanup;
|
|
|
|
if (!p_GetTokenInformation(tok, TokenUser, NULL, 0, &toklen) &&
|
|
GetLastError() != ERROR_INSUFFICIENT_BUFFER)
|
|
goto cleanup;
|
|
|
|
if ((user = (TOKEN_USER *)LocalAlloc(LPTR, toklen)) == NULL)
|
|
goto cleanup;
|
|
|
|
if (!p_GetTokenInformation(tok, TokenUser, user, toklen, &toklen))
|
|
goto cleanup;
|
|
|
|
sidlen = GetLengthSid(user->User.Sid);
|
|
|
|
sid = (PSID)smalloc(sidlen);
|
|
|
|
if (!CopySid(sidlen, sid, user->User.Sid))
|
|
goto cleanup;
|
|
|
|
/* Success. Move sid into the return value slot, and null it out
|
|
* to stop the cleanup code freeing it. */
|
|
ret = sid;
|
|
sid = NULL;
|
|
|
|
cleanup:
|
|
if (proc != NULL)
|
|
CloseHandle(proc);
|
|
if (tok != NULL)
|
|
CloseHandle(tok);
|
|
if (user != NULL)
|
|
LocalFree(user);
|
|
if (sid != NULL)
|
|
sfree(sid);
|
|
|
|
return ret;
|
|
}
|
|
|
|
#endif
|
|
|
|
int 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 1; /* *out == NULL, so failure */
|
|
mapname = dupprintf("PageantRequest%08x", (unsigned)GetCurrentThreadId());
|
|
|
|
#ifndef NO_SECURITY
|
|
if (advapi_initialised || init_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();
|
|
|
|
psa = NULL;
|
|
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)
|
|
return 1; /* *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;
|
|
#ifdef WINDOWS_ASYNC_AGENT
|
|
if (callback != NULL && !(flags & FLAG_SYNCAGENT)) {
|
|
/*
|
|
* We need an asynchronous Pageant request. Since I know of
|
|
* no way to stop SendMessage from blocking the thread it's
|
|
* called in, I see no option but to start a fresh thread.
|
|
* When we're done we'll PostMessage the result back to our
|
|
* main window, so that the callback is done in the primary
|
|
* thread to avoid concurrency.
|
|
*/
|
|
struct agent_query_data *data = snew(struct agent_query_data);
|
|
DWORD threadid;
|
|
data->mapping = p;
|
|
data->handle = filemap;
|
|
data->mapname = mapname;
|
|
data->callback = callback;
|
|
data->callback_ctx = callback_ctx;
|
|
data->cds = cds; /* structure copy */
|
|
data->hwnd = hwnd;
|
|
if (CreateThread(NULL, 0, agent_query_thread, data, 0, &threadid))
|
|
return 0;
|
|
sfree(data);
|
|
}
|
|
#endif
|
|
|
|
/*
|
|
* 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);
|
|
if (psd)
|
|
LocalFree(psd);
|
|
sfree(usersid);
|
|
return 1;
|
|
}
|