]> asedeno.scripts.mit.edu Git - PuTTY.git/blob - windows/winsecur.c
A bunch of further warning fixes in the Windows code.
[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     SID_IDENTIFIER_AUTHORITY world_auth = { SECURITY_WORLD_SID_AUTHORITY };
98     SID_IDENTIFIER_AUTHORITY nt_auth = { SECURITY_NT_AUTHORITY };
99     int ret = FALSE;
100
101     *error = NULL;
102
103     if (!usersid) {
104         if ((usersid = get_user_sid()) == NULL) {
105             *error = dupprintf("unable to construct SID for current user: %s",
106                                win_strerror(GetLastError()));
107             goto cleanup;
108         }
109     }
110
111     if (!worldsid) {
112         if (!AllocateAndInitializeSid(&world_auth, 1, SECURITY_WORLD_RID,
113                                       0, 0, 0, 0, 0, 0, 0, &worldsid)) {
114             *error = dupprintf("unable to construct SID for world: %s",
115                                win_strerror(GetLastError()));
116             goto cleanup;
117         }
118     }
119
120     if (!networksid) {
121         if (!AllocateAndInitializeSid(&nt_auth, 1, SECURITY_NETWORK_RID,
122                                       0, 0, 0, 0, 0, 0, 0, &networksid)) {
123             *error = dupprintf("unable to construct SID for "
124                                "local same-user access only: %s",
125                                win_strerror(GetLastError()));
126             goto cleanup;
127         }
128     }
129
130     ret = TRUE;
131
132  cleanup:
133     return ret;
134 }
135   
136
137 int make_private_security_descriptor(DWORD permissions,
138                                      PSECURITY_DESCRIPTOR *psd,
139                                      PACL *acl,
140                                      char **error)
141 {
142     EXPLICIT_ACCESS ea[3];
143     int acl_err;
144     int ret = FALSE;
145
146
147     *psd = NULL;
148     *acl = NULL;
149     *error = NULL;
150
151     if (!getsids(error))
152       goto cleanup;
153
154     memset(ea, 0, sizeof(ea));
155     ea[0].grfAccessPermissions = permissions;
156     ea[0].grfAccessMode = REVOKE_ACCESS;
157     ea[0].grfInheritance = NO_INHERITANCE;
158     ea[0].Trustee.TrusteeForm = TRUSTEE_IS_SID;
159     ea[0].Trustee.ptstrName = (LPTSTR)worldsid;
160     ea[1].grfAccessPermissions = permissions;
161     ea[1].grfAccessMode = GRANT_ACCESS;
162     ea[1].grfInheritance = NO_INHERITANCE;
163     ea[1].Trustee.TrusteeForm = TRUSTEE_IS_SID;
164     ea[1].Trustee.ptstrName = (LPTSTR)usersid;
165     ea[2].grfAccessPermissions = permissions;
166     ea[2].grfAccessMode = REVOKE_ACCESS;
167     ea[2].grfInheritance = NO_INHERITANCE;
168     ea[2].Trustee.TrusteeForm = TRUSTEE_IS_SID;
169     ea[2].Trustee.ptstrName = (LPTSTR)networksid;
170
171     acl_err = p_SetEntriesInAclA(3, ea, NULL, acl);
172     if (acl_err != ERROR_SUCCESS || *acl == NULL) {
173         *error = dupprintf("unable to construct ACL: %s",
174                            win_strerror(acl_err));
175         goto cleanup;
176     }
177
178     *psd = (PSECURITY_DESCRIPTOR)
179         LocalAlloc(LPTR, SECURITY_DESCRIPTOR_MIN_LENGTH);
180     if (!*psd) {
181         *error = dupprintf("unable to allocate security descriptor: %s",
182                            win_strerror(GetLastError()));
183         goto cleanup;
184     }
185
186     if (!InitializeSecurityDescriptor(*psd, SECURITY_DESCRIPTOR_REVISION)) {
187         *error = dupprintf("unable to initialise security descriptor: %s",
188                            win_strerror(GetLastError()));
189         goto cleanup;
190     }
191
192     if (!SetSecurityDescriptorOwner(*psd, usersid, FALSE)) {
193         *error = dupprintf("unable to set owner in security descriptor: %s",
194                            win_strerror(GetLastError()));
195         goto cleanup;
196     }
197
198     if (!SetSecurityDescriptorDacl(*psd, TRUE, *acl, FALSE)) {
199         *error = dupprintf("unable to set DACL in security descriptor: %s",
200                            win_strerror(GetLastError()));
201         goto cleanup;
202     }
203
204     ret = TRUE;
205
206   cleanup:
207     if (!ret) {
208         if (*psd) {
209             LocalFree(*psd);
210             *psd = NULL;
211         }
212         if (*acl) {
213             LocalFree(*acl);
214             *acl = NULL;
215         }
216     } else {
217         sfree(*error);
218         *error = NULL;
219     }
220     return ret;
221 }
222
223 static int really_restrict_process_acl(char **error)
224 {
225     EXPLICIT_ACCESS ea[2];
226     int acl_err;
227     int ret=FALSE;
228     PACL acl = NULL;
229
230     static const DWORD nastyace=WRITE_DAC | WRITE_OWNER |
231         PROCESS_CREATE_PROCESS | PROCESS_CREATE_THREAD |
232         PROCESS_DUP_HANDLE |
233         PROCESS_SET_QUOTA | PROCESS_SET_INFORMATION |
234         PROCESS_VM_OPERATION | PROCESS_VM_READ | PROCESS_VM_WRITE |
235         PROCESS_SUSPEND_RESUME;
236
237     if (!getsids(error))
238         goto cleanup;
239
240     memset(ea, 0, sizeof(ea));
241
242     /* Everyone: deny */
243     ea[0].grfAccessPermissions = nastyace;
244     ea[0].grfAccessMode = DENY_ACCESS;
245     ea[0].grfInheritance = SUB_CONTAINERS_AND_OBJECTS_INHERIT;
246     ea[0].Trustee.TrusteeForm = TRUSTEE_IS_SID;
247     ea[0].Trustee.ptstrName = (LPTSTR)worldsid;
248
249     /* User: user ace */
250     ea[1].grfAccessPermissions = ~nastyace & 0x1fff;
251     ea[1].grfAccessMode = GRANT_ACCESS;
252     ea[1].grfInheritance = SUB_CONTAINERS_AND_OBJECTS_INHERIT;
253     ea[1].Trustee.TrusteeForm = TRUSTEE_IS_SID;
254     ea[1].Trustee.ptstrName = (LPTSTR)usersid;
255
256     acl_err = p_SetEntriesInAclA(2, ea, NULL, &acl);
257
258     if (acl_err != ERROR_SUCCESS || acl == NULL) {
259         *error = dupprintf("unable to construct ACL: %s",
260                            win_strerror(acl_err));
261         goto cleanup;
262     }
263
264     if (ERROR_SUCCESS != p_SetSecurityInfo
265         (GetCurrentProcess(), SE_KERNEL_OBJECT,
266          OWNER_SECURITY_INFORMATION | DACL_SECURITY_INFORMATION,
267          usersid, NULL, acl, NULL)) {
268         *error = dupprintf("Unable to set process ACL: %s",
269                            win_strerror(GetLastError()));
270         goto cleanup;
271     }
272                       
273
274     ret=TRUE;
275     
276   cleanup:
277     if (!ret) {
278         if (acl) {
279             LocalFree(acl);
280             acl = NULL;
281         }
282     }
283     return ret;
284 }
285 #endif /* !defined NO_SECURITY */
286
287 /*
288  * Lock down our process's ACL, to present an obstacle to malware
289  * trying to write into its memory. This can't be a full defence,
290  * because well timed malware could attack us before this code runs -
291  * even if it was unconditionally run at the very start of main(),
292  * which we wouldn't want to do anyway because it turns out in practie
293  * that interfering with other processes in this way has significant
294  * non-infringing uses on Windows (e.g. screen reader software).
295  *
296  * If we've been requested to do this and are unsuccessful, bomb out
297  * via modalfatalbox rather than continue in a less protected mode.
298  *
299  * This function is intentionally outside the #ifndef NO_SECURITY that
300  * covers the rest of this file, because when PuTTY is compiled
301  * without the ability to restrict its ACL, we don't want it to
302  * silently pretend to honour the instruction to do so.
303  */
304 void restrict_process_acl(void)
305 {
306     char *error = NULL;
307     int ret;
308
309 #if !defined NO_SECURITY
310     ret = really_restrict_process_acl(&error);
311 #else
312     ret = FALSE;
313     error = dupstr("ACL restrictions not compiled into this binary");
314 #endif
315     if (!ret)
316         modalfatalbox("Could not restrict process ACL: %s", error);
317 }