]> asedeno.scripts.mit.edu Git - PuTTY.git/blob - windows/winsecur.c
GTK2: Return 2.20 compatibility back
[PuTTY.git] / windows / winsecur.c
1 /*
2  * winsecur.c: implementation of winsecur.h.
3  */
4
5 #include <stdio.h>
6 #include <stdlib.h>
7
8 #include "putty.h"
9
10 #if !defined NO_SECURITY
11
12 #define WINSECUR_GLOBAL
13 #include "winsecur.h"
14
15 /* Initialised once, then kept around to reuse forever */
16 static PSID worldsid, networksid, usersid;
17
18
19 int got_advapi(void)
20 {
21     static int attempted = FALSE;
22     static int successful;
23     static HMODULE advapi;
24
25     if (!attempted) {
26         attempted = TRUE;
27         advapi = load_system32_dll("advapi32.dll");
28         successful = advapi &&
29             GET_WINDOWS_FUNCTION(advapi, GetSecurityInfo) &&
30             GET_WINDOWS_FUNCTION(advapi, SetSecurityInfo) &&
31             GET_WINDOWS_FUNCTION(advapi, OpenProcessToken) &&
32             GET_WINDOWS_FUNCTION(advapi, GetTokenInformation) &&
33             GET_WINDOWS_FUNCTION(advapi, InitializeSecurityDescriptor) &&
34             GET_WINDOWS_FUNCTION(advapi, SetSecurityDescriptorOwner) &&
35             GET_WINDOWS_FUNCTION(advapi, SetEntriesInAclA);
36     }
37     return successful;
38 }
39
40 PSID get_user_sid(void)
41 {
42     HANDLE proc = NULL, tok = NULL;
43     TOKEN_USER *user = NULL;
44     DWORD toklen, sidlen;
45     PSID sid = NULL, ret = NULL;
46
47     if (usersid)
48         return usersid;
49
50     if (!got_advapi())
51         goto cleanup;
52
53     if ((proc = OpenProcess(MAXIMUM_ALLOWED, FALSE,
54                             GetCurrentProcessId())) == NULL)
55         goto cleanup;
56
57     if (!p_OpenProcessToken(proc, TOKEN_QUERY, &tok))
58         goto cleanup;
59
60     if (!p_GetTokenInformation(tok, TokenUser, NULL, 0, &toklen) &&
61         GetLastError() != ERROR_INSUFFICIENT_BUFFER)
62         goto cleanup;
63
64     if ((user = (TOKEN_USER *)LocalAlloc(LPTR, toklen)) == NULL)
65         goto cleanup;
66
67     if (!p_GetTokenInformation(tok, TokenUser, user, toklen, &toklen))
68         goto cleanup;
69
70     sidlen = GetLengthSid(user->User.Sid);
71
72     sid = (PSID)smalloc(sidlen);
73
74     if (!CopySid(sidlen, sid, user->User.Sid))
75         goto cleanup;
76
77     /* Success. Move sid into the return value slot, and null it out
78      * to stop the cleanup code freeing it. */
79     ret = usersid = sid;
80     sid = NULL;
81
82   cleanup:
83     if (proc != NULL)
84         CloseHandle(proc);
85     if (tok != NULL)
86         CloseHandle(tok);
87     if (user != NULL)
88         LocalFree(user);
89     if (sid != NULL)
90         sfree(sid);
91
92     return ret;
93 }
94
95 int getsids(char **error)
96 {
97 #ifdef __clang__
98 #pragma clang diagnostic push
99 #pragma clang diagnostic ignored "-Wmissing-braces"
100 #endif
101     SID_IDENTIFIER_AUTHORITY world_auth = SECURITY_WORLD_SID_AUTHORITY;
102     SID_IDENTIFIER_AUTHORITY nt_auth = SECURITY_NT_AUTHORITY;
103 #ifdef __clang__
104 #pragma clang diagnostic pop
105 #endif
106
107     int ret = FALSE;
108
109     *error = NULL;
110
111     if (!usersid) {
112         if ((usersid = get_user_sid()) == NULL) {
113             *error = dupprintf("unable to construct SID for current user: %s",
114                                win_strerror(GetLastError()));
115             goto cleanup;
116         }
117     }
118
119     if (!worldsid) {
120         if (!AllocateAndInitializeSid(&world_auth, 1, SECURITY_WORLD_RID,
121                                       0, 0, 0, 0, 0, 0, 0, &worldsid)) {
122             *error = dupprintf("unable to construct SID for world: %s",
123                                win_strerror(GetLastError()));
124             goto cleanup;
125         }
126     }
127
128     if (!networksid) {
129         if (!AllocateAndInitializeSid(&nt_auth, 1, SECURITY_NETWORK_RID,
130                                       0, 0, 0, 0, 0, 0, 0, &networksid)) {
131             *error = dupprintf("unable to construct SID for "
132                                "local same-user access only: %s",
133                                win_strerror(GetLastError()));
134             goto cleanup;
135         }
136     }
137
138     ret = TRUE;
139
140  cleanup:
141     return ret;
142 }
143   
144
145 int make_private_security_descriptor(DWORD permissions,
146                                      PSECURITY_DESCRIPTOR *psd,
147                                      PACL *acl,
148                                      char **error)
149 {
150     EXPLICIT_ACCESS ea[3];
151     int acl_err;
152     int ret = FALSE;
153
154
155     *psd = NULL;
156     *acl = NULL;
157     *error = NULL;
158
159     if (!getsids(error))
160       goto cleanup;
161
162     memset(ea, 0, sizeof(ea));
163     ea[0].grfAccessPermissions = permissions;
164     ea[0].grfAccessMode = REVOKE_ACCESS;
165     ea[0].grfInheritance = NO_INHERITANCE;
166     ea[0].Trustee.TrusteeForm = TRUSTEE_IS_SID;
167     ea[0].Trustee.ptstrName = (LPTSTR)worldsid;
168     ea[1].grfAccessPermissions = permissions;
169     ea[1].grfAccessMode = GRANT_ACCESS;
170     ea[1].grfInheritance = NO_INHERITANCE;
171     ea[1].Trustee.TrusteeForm = TRUSTEE_IS_SID;
172     ea[1].Trustee.ptstrName = (LPTSTR)usersid;
173     ea[2].grfAccessPermissions = permissions;
174     ea[2].grfAccessMode = REVOKE_ACCESS;
175     ea[2].grfInheritance = NO_INHERITANCE;
176     ea[2].Trustee.TrusteeForm = TRUSTEE_IS_SID;
177     ea[2].Trustee.ptstrName = (LPTSTR)networksid;
178
179     acl_err = p_SetEntriesInAclA(3, ea, NULL, acl);
180     if (acl_err != ERROR_SUCCESS || *acl == NULL) {
181         *error = dupprintf("unable to construct ACL: %s",
182                            win_strerror(acl_err));
183         goto cleanup;
184     }
185
186     *psd = (PSECURITY_DESCRIPTOR)
187         LocalAlloc(LPTR, SECURITY_DESCRIPTOR_MIN_LENGTH);
188     if (!*psd) {
189         *error = dupprintf("unable to allocate security descriptor: %s",
190                            win_strerror(GetLastError()));
191         goto cleanup;
192     }
193
194     if (!InitializeSecurityDescriptor(*psd, SECURITY_DESCRIPTOR_REVISION)) {
195         *error = dupprintf("unable to initialise security descriptor: %s",
196                            win_strerror(GetLastError()));
197         goto cleanup;
198     }
199
200     if (!SetSecurityDescriptorOwner(*psd, usersid, FALSE)) {
201         *error = dupprintf("unable to set owner in security descriptor: %s",
202                            win_strerror(GetLastError()));
203         goto cleanup;
204     }
205
206     if (!SetSecurityDescriptorDacl(*psd, TRUE, *acl, FALSE)) {
207         *error = dupprintf("unable to set DACL in security descriptor: %s",
208                            win_strerror(GetLastError()));
209         goto cleanup;
210     }
211
212     ret = TRUE;
213
214   cleanup:
215     if (!ret) {
216         if (*psd) {
217             LocalFree(*psd);
218             *psd = NULL;
219         }
220         if (*acl) {
221             LocalFree(*acl);
222             *acl = NULL;
223         }
224     } else {
225         sfree(*error);
226         *error = NULL;
227     }
228     return ret;
229 }
230
231 static int really_restrict_process_acl(char **error)
232 {
233     EXPLICIT_ACCESS ea[2];
234     int acl_err;
235     int ret=FALSE;
236     PACL acl = NULL;
237
238     static const DWORD nastyace=WRITE_DAC | WRITE_OWNER |
239         PROCESS_CREATE_PROCESS | PROCESS_CREATE_THREAD |
240         PROCESS_DUP_HANDLE |
241         PROCESS_SET_QUOTA | PROCESS_SET_INFORMATION |
242         PROCESS_VM_OPERATION | PROCESS_VM_READ | PROCESS_VM_WRITE |
243         PROCESS_SUSPEND_RESUME;
244
245     if (!getsids(error))
246         goto cleanup;
247
248     memset(ea, 0, sizeof(ea));
249
250     /* Everyone: deny */
251     ea[0].grfAccessPermissions = nastyace;
252     ea[0].grfAccessMode = DENY_ACCESS;
253     ea[0].grfInheritance = SUB_CONTAINERS_AND_OBJECTS_INHERIT;
254     ea[0].Trustee.TrusteeForm = TRUSTEE_IS_SID;
255     ea[0].Trustee.ptstrName = (LPTSTR)worldsid;
256
257     /* User: user ace */
258     ea[1].grfAccessPermissions = ~nastyace & 0x1fff;
259     ea[1].grfAccessMode = GRANT_ACCESS;
260     ea[1].grfInheritance = SUB_CONTAINERS_AND_OBJECTS_INHERIT;
261     ea[1].Trustee.TrusteeForm = TRUSTEE_IS_SID;
262     ea[1].Trustee.ptstrName = (LPTSTR)usersid;
263
264     acl_err = p_SetEntriesInAclA(2, ea, NULL, &acl);
265
266     if (acl_err != ERROR_SUCCESS || acl == NULL) {
267         *error = dupprintf("unable to construct ACL: %s",
268                            win_strerror(acl_err));
269         goto cleanup;
270     }
271
272     if (ERROR_SUCCESS != p_SetSecurityInfo
273         (GetCurrentProcess(), SE_KERNEL_OBJECT,
274          OWNER_SECURITY_INFORMATION | DACL_SECURITY_INFORMATION,
275          usersid, NULL, acl, NULL)) {
276         *error = dupprintf("Unable to set process ACL: %s",
277                            win_strerror(GetLastError()));
278         goto cleanup;
279     }
280                       
281
282     ret=TRUE;
283     
284   cleanup:
285     if (!ret) {
286         if (acl) {
287             LocalFree(acl);
288             acl = NULL;
289         }
290     }
291     return ret;
292 }
293 #endif /* !defined NO_SECURITY */
294
295 /*
296  * Lock down our process's ACL, to present an obstacle to malware
297  * trying to write into its memory. This can't be a full defence,
298  * because well timed malware could attack us before this code runs -
299  * even if it was unconditionally run at the very start of main(),
300  * which we wouldn't want to do anyway because it turns out in practie
301  * that interfering with other processes in this way has significant
302  * non-infringing uses on Windows (e.g. screen reader software).
303  *
304  * If we've been requested to do this and are unsuccessful, bomb out
305  * via modalfatalbox rather than continue in a less protected mode.
306  *
307  * This function is intentionally outside the #ifndef NO_SECURITY that
308  * covers the rest of this file, because when PuTTY is compiled
309  * without the ability to restrict its ACL, we don't want it to
310  * silently pretend to honour the instruction to do so.
311  */
312 void restrict_process_acl(void)
313 {
314     char *error = NULL;
315     int ret;
316
317 #if !defined NO_SECURITY
318     ret = really_restrict_process_acl(&error);
319 #else
320     ret = FALSE;
321     error = dupstr("ACL restrictions not compiled into this binary");
322 #endif
323     if (!ret)
324         modalfatalbox("Could not restrict process ACL: %s", error);
325 }