]>
Commit | Line | Data |
---|---|---|
1c1af145 | 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 | } |