OSDN Git Service

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