]> asedeno.scripts.mit.edu Git - PuTTY.git/blob - windows/winpgntc.c
Fix another error-reporting bug, in which sk_newlistener would fail to
[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 DECL_WINDOWS_FUNCTION(, DWORD, GetSecurityInfo,
90                       (HANDLE, SE_OBJECT_TYPE, SECURITY_INFORMATION,
91                        PSID *, PSID *, PACL *, PACL *,
92                        PSECURITY_DESCRIPTOR *));
93 int init_advapi(void)
94 {
95     advapi = load_system32_dll("advapi32.dll");
96     return advapi &&
97         GET_WINDOWS_FUNCTION(advapi, GetSecurityInfo) &&
98         GET_WINDOWS_FUNCTION(advapi, OpenProcessToken) &&
99         GET_WINDOWS_FUNCTION(advapi, GetTokenInformation) &&
100         GET_WINDOWS_FUNCTION(advapi, InitializeSecurityDescriptor) &&
101         GET_WINDOWS_FUNCTION(advapi, SetSecurityDescriptorOwner);
102 }
103
104 PSID get_user_sid(void)
105 {
106     HANDLE proc = NULL, tok = NULL;
107     TOKEN_USER *user = NULL;
108     DWORD toklen, sidlen;
109     PSID sid = NULL, ret = NULL;
110
111     if ((proc = OpenProcess(MAXIMUM_ALLOWED, FALSE,
112                             GetCurrentProcessId())) == NULL)
113         goto cleanup;
114
115     if (!p_OpenProcessToken(proc, TOKEN_QUERY, &tok))
116         goto cleanup;
117
118     if (!p_GetTokenInformation(tok, TokenUser, NULL, 0, &toklen) &&
119         GetLastError() != ERROR_INSUFFICIENT_BUFFER)
120         goto cleanup;
121
122     if ((user = (TOKEN_USER *)LocalAlloc(LPTR, toklen)) == NULL)
123         goto cleanup;
124
125     if (!p_GetTokenInformation(tok, TokenUser, user, toklen, &toklen))
126         goto cleanup;
127
128     sidlen = GetLengthSid(user->User.Sid);
129
130     sid = (PSID)smalloc(sidlen);
131
132     if (!CopySid(sidlen, sid, user->User.Sid))
133         goto cleanup;
134
135     /* Success. Move sid into the return value slot, and null it out
136      * to stop the cleanup code freeing it. */
137     ret = sid;
138     sid = NULL;
139
140   cleanup:
141     if (proc != NULL)
142         CloseHandle(proc);
143     if (tok != NULL)
144         CloseHandle(tok);
145     if (user != NULL)
146         LocalFree(user);
147     if (sid != NULL)
148         sfree(sid);
149
150     return ret;
151 }
152
153 #endif
154
155 int agent_query(void *in, int inlen, void **out, int *outlen,
156                 void (*callback)(void *, void *, int), void *callback_ctx)
157 {
158     HWND hwnd;
159     char *mapname;
160     HANDLE filemap;
161     unsigned char *p, *ret;
162     int id, retlen;
163     COPYDATASTRUCT cds;
164     SECURITY_ATTRIBUTES sa, *psa;
165     PSECURITY_DESCRIPTOR psd = NULL;
166     PSID usersid = NULL;
167
168     *out = NULL;
169     *outlen = 0;
170
171     hwnd = FindWindow("Pageant", "Pageant");
172     if (!hwnd)
173         return 1;                      /* *out == NULL, so failure */
174     mapname = dupprintf("PageantRequest%08x", (unsigned)GetCurrentThreadId());
175
176 #ifndef NO_SECURITY
177     if (advapi_initialised || init_advapi()) {
178         /*
179          * Make the file mapping we create for communication with
180          * Pageant owned by the user SID rather than the default. This
181          * should make communication between processes with slightly
182          * different contexts more reliable: in particular, command
183          * prompts launched as administrator should still be able to
184          * run PSFTPs which refer back to the owning user's
185          * unprivileged Pageant.
186          */
187         usersid = get_user_sid();
188
189         psa = NULL;
190         if (usersid) {
191             psd = (PSECURITY_DESCRIPTOR)
192                 LocalAlloc(LPTR, SECURITY_DESCRIPTOR_MIN_LENGTH);
193             if (psd) {
194                 if (p_InitializeSecurityDescriptor
195                     (psd, SECURITY_DESCRIPTOR_REVISION) &&
196                     p_SetSecurityDescriptorOwner(psd, usersid, FALSE)) {
197                     sa.nLength = sizeof(sa);
198                     sa.bInheritHandle = TRUE;
199                     sa.lpSecurityDescriptor = psd;
200                     psa = &sa;
201                 } else {
202                     LocalFree(psd);
203                     psd = NULL;
204                 }
205             }
206         }
207     }
208 #endif /* NO_SECURITY */
209
210     filemap = CreateFileMapping(INVALID_HANDLE_VALUE, psa, PAGE_READWRITE,
211                                 0, AGENT_MAX_MSGLEN, mapname);
212     if (filemap == NULL || filemap == INVALID_HANDLE_VALUE)
213         return 1;                      /* *out == NULL, so failure */
214     p = MapViewOfFile(filemap, FILE_MAP_WRITE, 0, 0, 0);
215     memcpy(p, in, inlen);
216     cds.dwData = AGENT_COPYDATA_ID;
217     cds.cbData = 1 + strlen(mapname);
218     cds.lpData = mapname;
219 #ifdef WINDOWS_ASYNC_AGENT
220     if (callback != NULL && !(flags & FLAG_SYNCAGENT)) {
221         /*
222          * We need an asynchronous Pageant request. Since I know of
223          * no way to stop SendMessage from blocking the thread it's
224          * called in, I see no option but to start a fresh thread.
225          * When we're done we'll PostMessage the result back to our
226          * main window, so that the callback is done in the primary
227          * thread to avoid concurrency.
228          */
229         struct agent_query_data *data = snew(struct agent_query_data);
230         DWORD threadid;
231         data->mapping = p;
232         data->handle = filemap;
233         data->mapname = mapname;
234         data->callback = callback;
235         data->callback_ctx = callback_ctx;
236         data->cds = cds;               /* structure copy */
237         data->hwnd = hwnd;
238         if (CreateThread(NULL, 0, agent_query_thread, data, 0, &threadid))
239             return 0;
240         sfree(data);
241     }
242 #endif
243
244     /*
245      * The user either passed a null callback (indicating that the
246      * query is required to be synchronous) or CreateThread failed.
247      * Either way, we need a synchronous request.
248      */
249     id = SendMessage(hwnd, WM_COPYDATA, (WPARAM) NULL, (LPARAM) &cds);
250     if (id > 0) {
251         retlen = 4 + GET_32BIT(p);
252         ret = snewn(retlen, unsigned char);
253         if (ret) {
254             memcpy(ret, p, retlen);
255             *out = ret;
256             *outlen = retlen;
257         }
258     }
259     UnmapViewOfFile(p);
260     CloseHandle(filemap);
261     if (psd)
262         LocalFree(psd);
263     sfree(usersid);
264     return 1;
265 }