]> asedeno.scripts.mit.edu Git - PuTTY.git/blob - windows/winpgntc.c
More careful owner SID selection in the Pageant client code. This
[PuTTY.git] / windows / winpgntc.c
1 /*
2  * Pageant client code.
3  */
4
5 #include <stdio.h>
6 #include <stdlib.h>
7
8 #include "putty.h"
9
10 #ifndef NO_SECURITY
11 #include <aclapi.h>
12 #endif
13
14 #define AGENT_COPYDATA_ID 0x804e50ba   /* random goop */
15 #define AGENT_MAX_MSGLEN  8192
16
17 int agent_exists(void)
18 {
19     HWND hwnd;
20     hwnd = FindWindow("Pageant", "Pageant");
21     if (!hwnd)
22         return FALSE;
23     else
24         return TRUE;
25 }
26
27 /*
28  * Unfortunately, this asynchronous agent request mechanism doesn't
29  * appear to work terribly well. I'm going to comment it out for
30  * the moment, and see if I can come up with a better one :-/
31  */
32 #ifdef WINDOWS_ASYNC_AGENT
33
34 struct agent_query_data {
35     COPYDATASTRUCT cds;
36     unsigned char *mapping;
37     HANDLE handle;
38     char *mapname;
39     HWND hwnd;
40     void (*callback)(void *, void *, int);
41     void *callback_ctx;
42 };
43
44 DWORD WINAPI agent_query_thread(LPVOID param)
45 {
46     struct agent_query_data *data = (struct agent_query_data *)param;
47     unsigned char *ret;
48     int id, retlen;
49
50     id = SendMessage(data->hwnd, WM_COPYDATA, (WPARAM) NULL,
51                      (LPARAM) &data->cds);
52     ret = NULL;
53     if (id > 0) {
54         retlen = 4 + GET_32BIT(data->mapping);
55         ret = snewn(retlen, unsigned char);
56         if (ret) {
57             memcpy(ret, data->mapping, retlen);
58         }
59     }
60     if (!ret)
61         retlen = 0;
62     UnmapViewOfFile(data->mapping);
63     CloseHandle(data->handle);
64     sfree(data->mapname);
65
66     agent_schedule_callback(data->callback, data->callback_ctx, ret, retlen);
67
68     return 0;
69 }
70
71 #endif
72
73 /*
74  * Dynamically load advapi32.dll for SID manipulation. In its absence,
75  * we degrade gracefully.
76  */
77 #ifndef NO_SECURITY
78 int advapi_initialised = FALSE;
79 static HMODULE advapi;
80 DECL_WINDOWS_FUNCTION(static, BOOL, OpenProcessToken,
81                       (HANDLE, DWORD, PHANDLE));
82 DECL_WINDOWS_FUNCTION(static, BOOL, GetTokenInformation,
83                       (HANDLE, TOKEN_INFORMATION_CLASS,
84                        LPVOID, DWORD, PDWORD));
85 DECL_WINDOWS_FUNCTION(static, BOOL, InitializeSecurityDescriptor,
86                       (PSECURITY_DESCRIPTOR, DWORD));
87 DECL_WINDOWS_FUNCTION(static, BOOL, SetSecurityDescriptorOwner,
88                       (PSECURITY_DESCRIPTOR, PSID, BOOL));
89 static int init_advapi(void)
90 {
91     advapi = load_system32_dll("advapi32.dll");
92     return advapi &&
93         GET_WINDOWS_FUNCTION(advapi, OpenProcessToken) &&
94         GET_WINDOWS_FUNCTION(advapi, GetTokenInformation) &&
95         GET_WINDOWS_FUNCTION(advapi, InitializeSecurityDescriptor) &&
96         GET_WINDOWS_FUNCTION(advapi, SetSecurityDescriptorOwner);
97 }
98 #endif
99
100 int agent_query(void *in, int inlen, void **out, int *outlen,
101                 void (*callback)(void *, void *, int), void *callback_ctx)
102 {
103     HWND hwnd;
104     char *mapname;
105     HANDLE filemap;
106     unsigned char *p, *ret;
107     int id, retlen;
108     COPYDATASTRUCT cds;
109     SECURITY_ATTRIBUTES sa, *psa;
110     PSECURITY_DESCRIPTOR psd = NULL;
111     HANDLE proc, tok;
112     TOKEN_USER *user = NULL;
113
114     *out = NULL;
115     *outlen = 0;
116
117     hwnd = FindWindow("Pageant", "Pageant");
118     if (!hwnd)
119         return 1;                      /* *out == NULL, so failure */
120     mapname = dupprintf("PageantRequest%08x", (unsigned)GetCurrentThreadId());
121
122 #ifndef NO_SECURITY
123     if (advapi_initialised || init_advapi()) {
124         /*
125          * Make the file mapping we create for communication with
126          * Pageant owned by the user SID rather than the default. This
127          * should make communication between processes with slightly
128          * different contexts more reliable: in particular, command
129          * prompts launched as administrator should still be able to
130          * run PSFTPs which refer back to the owning user's
131          * unprivileged Pageant.
132          */
133
134         if ((proc = OpenProcess(MAXIMUM_ALLOWED, FALSE,
135                                 GetCurrentProcessId())) != NULL) {
136             if (p_OpenProcessToken(proc, TOKEN_QUERY, &tok)) {
137                 DWORD retlen;
138                 p_GetTokenInformation(tok, TokenUser, NULL, 0, &retlen);
139                 user = (TOKEN_USER *)LocalAlloc(LPTR, retlen);
140                 if (!p_GetTokenInformation(tok, TokenUser,
141                                            user, retlen, &retlen)) {
142                     LocalFree(user);
143                     user = NULL;
144                 }
145                 CloseHandle(tok);
146             }
147             CloseHandle(proc);
148         }
149
150         psa = NULL;
151         if (user) {
152             psd = (PSECURITY_DESCRIPTOR)
153                 LocalAlloc(LPTR, SECURITY_DESCRIPTOR_MIN_LENGTH);
154             if (psd) {
155                 if (p_InitializeSecurityDescriptor
156                     (psd, SECURITY_DESCRIPTOR_REVISION) &&
157                     p_SetSecurityDescriptorOwner(psd, user->User.Sid, FALSE)) {
158                     sa.nLength = sizeof(sa);
159                     sa.bInheritHandle = TRUE;
160                     sa.lpSecurityDescriptor = psd;
161                     psa = &sa;
162                 } else {
163                     LocalFree(psd);
164                     psd = NULL;
165                 }
166             }
167         }
168     }
169 #endif /* NO_SECURITY */
170
171     filemap = CreateFileMapping(INVALID_HANDLE_VALUE, psa, PAGE_READWRITE,
172                                 0, AGENT_MAX_MSGLEN, mapname);
173     if (filemap == NULL || filemap == INVALID_HANDLE_VALUE)
174         return 1;                      /* *out == NULL, so failure */
175     p = MapViewOfFile(filemap, FILE_MAP_WRITE, 0, 0, 0);
176     memcpy(p, in, inlen);
177     cds.dwData = AGENT_COPYDATA_ID;
178     cds.cbData = 1 + strlen(mapname);
179     cds.lpData = mapname;
180 #ifdef WINDOWS_ASYNC_AGENT
181     if (callback != NULL && !(flags & FLAG_SYNCAGENT)) {
182         /*
183          * We need an asynchronous Pageant request. Since I know of
184          * no way to stop SendMessage from blocking the thread it's
185          * called in, I see no option but to start a fresh thread.
186          * When we're done we'll PostMessage the result back to our
187          * main window, so that the callback is done in the primary
188          * thread to avoid concurrency.
189          */
190         struct agent_query_data *data = snew(struct agent_query_data);
191         DWORD threadid;
192         data->mapping = p;
193         data->handle = filemap;
194         data->mapname = mapname;
195         data->callback = callback;
196         data->callback_ctx = callback_ctx;
197         data->cds = cds;               /* structure copy */
198         data->hwnd = hwnd;
199         if (CreateThread(NULL, 0, agent_query_thread, data, 0, &threadid))
200             return 0;
201         sfree(data);
202     }
203 #endif
204
205     /*
206      * The user either passed a null callback (indicating that the
207      * query is required to be synchronous) or CreateThread failed.
208      * Either way, we need a synchronous request.
209      */
210     id = SendMessage(hwnd, WM_COPYDATA, (WPARAM) NULL, (LPARAM) &cds);
211     if (id > 0) {
212         retlen = 4 + GET_32BIT(p);
213         ret = snewn(retlen, unsigned char);
214         if (ret) {
215             memcpy(ret, p, retlen);
216             *out = ret;
217             *outlen = retlen;
218         }
219     }
220     UnmapViewOfFile(p);
221     CloseHandle(filemap);
222     if (psd)
223         LocalFree(psd);
224     if (user)
225         LocalFree(user);
226     return 1;
227 }