5c58ad2d |
1 | /* |
2 | * Pageant client code. |
3 | */ |
4 | |
5c58ad2d |
5 | #include <stdio.h> |
6 | #include <stdlib.h> |
7 | |
c44bf5bd |
8 | #include "putty.h" |
dcbde236 |
9 | |
a197b711 |
10 | #ifndef NO_SECURITY |
11 | #include <aclapi.h> |
12 | #endif |
13 | |
d70f60ae |
14 | #define AGENT_COPYDATA_ID 0x804e50ba /* random goop */ |
15 | #define AGENT_MAX_MSGLEN 8192 |
16 | |
32874aea |
17 | int agent_exists(void) |
18 | { |
5c58ad2d |
19 | HWND hwnd; |
20 | hwnd = FindWindow("Pageant", "Pageant"); |
21 | if (!hwnd) |
32874aea |
22 | return FALSE; |
5c58ad2d |
23 | else |
32874aea |
24 | return TRUE; |
5c58ad2d |
25 | } |
26 | |
28f7ab3c |
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 | |
c44bf5bd |
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 | |
28f7ab3c |
71 | #endif |
72 | |
a197b711 |
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)); |
ac3337f6 |
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) |
a197b711 |
94 | { |
95 | advapi = load_system32_dll("advapi32.dll"); |
96 | return advapi && |
ac3337f6 |
97 | GET_WINDOWS_FUNCTION(advapi, GetSecurityInfo) && |
a197b711 |
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 | } |
ac3337f6 |
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 | |
a197b711 |
153 | #endif |
154 | |
839f10db |
155 | int agent_query(void *in, int inlen, void **out, int *outlen, |
156 | void (*callback)(void *, void *, int), void *callback_ctx) |
32874aea |
157 | { |
5c58ad2d |
158 | HWND hwnd; |
c44bf5bd |
159 | char *mapname; |
5c58ad2d |
160 | HANDLE filemap; |
d70f60ae |
161 | unsigned char *p, *ret; |
5c58ad2d |
162 | int id, retlen; |
163 | COPYDATASTRUCT cds; |
a197b711 |
164 | SECURITY_ATTRIBUTES sa, *psa; |
165 | PSECURITY_DESCRIPTOR psd = NULL; |
ac3337f6 |
166 | PSID usersid = NULL; |
5c58ad2d |
167 | |
168 | *out = NULL; |
169 | *outlen = 0; |
170 | |
171 | hwnd = FindWindow("Pageant", "Pageant"); |
5c58ad2d |
172 | if (!hwnd) |
839f10db |
173 | return 1; /* *out == NULL, so failure */ |
c44bf5bd |
174 | mapname = dupprintf("PageantRequest%08x", (unsigned)GetCurrentThreadId()); |
a197b711 |
175 | |
88ea8933 |
176 | psa = NULL; |
a197b711 |
177 | #ifndef NO_SECURITY |
178 | if (advapi_initialised || init_advapi()) { |
179 | /* |
180 | * Make the file mapping we create for communication with |
181 | * Pageant owned by the user SID rather than the default. This |
182 | * should make communication between processes with slightly |
183 | * different contexts more reliable: in particular, command |
184 | * prompts launched as administrator should still be able to |
185 | * run PSFTPs which refer back to the owning user's |
186 | * unprivileged Pageant. |
187 | */ |
ac3337f6 |
188 | usersid = get_user_sid(); |
a197b711 |
189 | |
ac3337f6 |
190 | if (usersid) { |
a197b711 |
191 | psd = (PSECURITY_DESCRIPTOR) |
192 | LocalAlloc(LPTR, SECURITY_DESCRIPTOR_MIN_LENGTH); |
193 | if (psd) { |
194 | if (p_InitializeSecurityDescriptor |
195 | (psd, SECURITY_DESCRIPTOR_REVISION) && |
ac3337f6 |
196 | p_SetSecurityDescriptorOwner(psd, usersid, FALSE)) { |
a197b711 |
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, |
32874aea |
211 | 0, AGENT_MAX_MSGLEN, mapname); |
0c33d3a6 |
212 | if (filemap == NULL || filemap == INVALID_HANDLE_VALUE) { |
213 | sfree(mapname); |
839f10db |
214 | return 1; /* *out == NULL, so failure */ |
0c33d3a6 |
215 | } |
d70f60ae |
216 | p = MapViewOfFile(filemap, FILE_MAP_WRITE, 0, 0, 0); |
217 | memcpy(p, in, inlen); |
218 | cds.dwData = AGENT_COPYDATA_ID; |
32874aea |
219 | cds.cbData = 1 + strlen(mapname); |
d70f60ae |
220 | cds.lpData = mapname; |
28f7ab3c |
221 | #ifdef WINDOWS_ASYNC_AGENT |
c44bf5bd |
222 | if (callback != NULL && !(flags & FLAG_SYNCAGENT)) { |
223 | /* |
224 | * We need an asynchronous Pageant request. Since I know of |
225 | * no way to stop SendMessage from blocking the thread it's |
226 | * called in, I see no option but to start a fresh thread. |
227 | * When we're done we'll PostMessage the result back to our |
228 | * main window, so that the callback is done in the primary |
229 | * thread to avoid concurrency. |
230 | */ |
231 | struct agent_query_data *data = snew(struct agent_query_data); |
232 | DWORD threadid; |
233 | data->mapping = p; |
234 | data->handle = filemap; |
235 | data->mapname = mapname; |
236 | data->callback = callback; |
237 | data->callback_ctx = callback_ctx; |
238 | data->cds = cds; /* structure copy */ |
239 | data->hwnd = hwnd; |
240 | if (CreateThread(NULL, 0, agent_query_thread, data, 0, &threadid)) |
241 | return 0; |
0c33d3a6 |
242 | sfree(mapname); |
c44bf5bd |
243 | sfree(data); |
244 | } |
28f7ab3c |
245 | #endif |
c44bf5bd |
246 | |
247 | /* |
248 | * The user either passed a null callback (indicating that the |
249 | * query is required to be synchronous) or CreateThread failed. |
250 | * Either way, we need a synchronous request. |
251 | */ |
e66c3912 |
252 | id = SendMessage(hwnd, WM_COPYDATA, (WPARAM) NULL, (LPARAM) &cds); |
5c58ad2d |
253 | if (id > 0) { |
32874aea |
254 | retlen = 4 + GET_32BIT(p); |
3d88e64d |
255 | ret = snewn(retlen, unsigned char); |
32874aea |
256 | if (ret) { |
257 | memcpy(ret, p, retlen); |
258 | *out = ret; |
259 | *outlen = retlen; |
260 | } |
5c58ad2d |
261 | } |
d70f60ae |
262 | UnmapViewOfFile(p); |
263 | CloseHandle(filemap); |
0c33d3a6 |
264 | sfree(mapname); |
a197b711 |
265 | if (psd) |
266 | LocalFree(psd); |
ac3337f6 |
267 | sfree(usersid); |
839f10db |
268 | return 1; |
5c58ad2d |
269 | } |