Update TortoiseGitPlink to PuTTY Plink 0.78
[TortoiseGit.git] / src / TortoisePlink / Windows / utils / security.c
blobb1f78a47598158fc1cc44115da25266fa3d72d90
1 /*
2 * windows/utils/security.c: implementation of security-api.h.
3 */
5 #include <stdio.h>
6 #include <stdlib.h>
8 #include "putty.h"
10 #include "security-api.h"
12 /* Initialised once, then kept around to reuse forever */
13 static PSID worldsid, networksid, usersid;
15 DEF_WINDOWS_FUNCTION(OpenProcessToken);
16 DEF_WINDOWS_FUNCTION(GetTokenInformation);
17 DEF_WINDOWS_FUNCTION(InitializeSecurityDescriptor);
18 DEF_WINDOWS_FUNCTION(SetSecurityDescriptorOwner);
19 DEF_WINDOWS_FUNCTION(GetSecurityInfo);
20 DEF_WINDOWS_FUNCTION(SetSecurityInfo);
21 DEF_WINDOWS_FUNCTION(SetEntriesInAclA);
23 bool should_have_security(void)
25 #ifdef LEGACY_WINDOWS
26 /* Legacy pre-NT platforms are not expected to have any of these APIs */
27 init_winver();
28 return (osPlatformId == VER_PLATFORM_WIN32_NT);
29 #else
30 /* In the up-to-date PuTTY builds which do not support those
31 * platforms, unconditionally return true, to minimise the risk of
32 * compiling out security checks. */
33 return true;
34 #endif
37 bool got_advapi(void)
39 static bool attempted = false;
40 static bool successful;
41 static HMODULE advapi;
43 if (!attempted) {
44 attempted = true;
45 advapi = load_system32_dll("advapi32.dll");
46 successful = advapi &&
47 GET_WINDOWS_FUNCTION(advapi, GetSecurityInfo) &&
48 GET_WINDOWS_FUNCTION(advapi, SetSecurityInfo) &&
49 GET_WINDOWS_FUNCTION(advapi, OpenProcessToken) &&
50 GET_WINDOWS_FUNCTION(advapi, GetTokenInformation) &&
51 GET_WINDOWS_FUNCTION(advapi, InitializeSecurityDescriptor) &&
52 GET_WINDOWS_FUNCTION(advapi, SetSecurityDescriptorOwner) &&
53 GET_WINDOWS_FUNCTION(advapi, SetEntriesInAclA);
55 return successful;
58 PSID get_user_sid(void)
60 HANDLE proc = NULL, tok = NULL;
61 TOKEN_USER *user = NULL;
62 DWORD toklen, sidlen;
63 PSID sid = NULL, ret = NULL;
65 if (usersid)
66 return usersid;
68 if (!got_advapi())
69 goto cleanup;
71 if ((proc = OpenProcess(MAXIMUM_ALLOWED, false,
72 GetCurrentProcessId())) == NULL)
73 goto cleanup;
75 if (!p_OpenProcessToken(proc, TOKEN_QUERY, &tok))
76 goto cleanup;
78 if (!p_GetTokenInformation(tok, TokenUser, NULL, 0, &toklen) &&
79 GetLastError() != ERROR_INSUFFICIENT_BUFFER)
80 goto cleanup;
82 if ((user = (TOKEN_USER *)LocalAlloc(LPTR, toklen)) == NULL)
83 goto cleanup;
85 if (!p_GetTokenInformation(tok, TokenUser, user, toklen, &toklen))
86 goto cleanup;
88 sidlen = GetLengthSid(user->User.Sid);
90 sid = (PSID)smalloc(sidlen);
92 if (!CopySid(sidlen, sid, user->User.Sid))
93 goto cleanup;
95 /* Success. Move sid into the return value slot, and null it out
96 * to stop the cleanup code freeing it. */
97 ret = usersid = sid;
98 sid = NULL;
100 cleanup:
101 if (proc != NULL)
102 CloseHandle(proc);
103 if (tok != NULL)
104 CloseHandle(tok);
105 if (user != NULL)
106 LocalFree(user);
107 if (sid != NULL)
108 sfree(sid);
110 return ret;
113 static bool getsids(char **error)
115 #ifdef __clang__
116 #pragma clang diagnostic push
117 #pragma clang diagnostic ignored "-Wmissing-braces"
118 #endif
119 SID_IDENTIFIER_AUTHORITY world_auth = SECURITY_WORLD_SID_AUTHORITY;
120 SID_IDENTIFIER_AUTHORITY nt_auth = SECURITY_NT_AUTHORITY;
121 #ifdef __clang__
122 #pragma clang diagnostic pop
123 #endif
125 bool ret = false;
127 *error = NULL;
129 if (!usersid) {
130 if ((usersid = get_user_sid()) == NULL) {
131 *error = dupprintf("unable to construct SID for current user: %s",
132 win_strerror(GetLastError()));
133 goto cleanup;
137 if (!worldsid) {
138 if (!AllocateAndInitializeSid(&world_auth, 1, SECURITY_WORLD_RID,
139 0, 0, 0, 0, 0, 0, 0, &worldsid)) {
140 *error = dupprintf("unable to construct SID for world: %s",
141 win_strerror(GetLastError()));
142 goto cleanup;
146 if (!networksid) {
147 if (!AllocateAndInitializeSid(&nt_auth, 1, SECURITY_NETWORK_RID,
148 0, 0, 0, 0, 0, 0, 0, &networksid)) {
149 *error = dupprintf("unable to construct SID for "
150 "local same-user access only: %s",
151 win_strerror(GetLastError()));
152 goto cleanup;
156 ret = true;
158 cleanup:
159 return ret;
163 bool make_private_security_descriptor(DWORD permissions,
164 PSECURITY_DESCRIPTOR *psd,
165 PACL *acl,
166 char **error)
168 EXPLICIT_ACCESS ea[3];
169 int acl_err;
170 bool ret = false;
173 *psd = NULL;
174 *acl = NULL;
175 *error = NULL;
177 if (!getsids(error))
178 goto cleanup;
180 memset(ea, 0, sizeof(ea));
181 ea[0].grfAccessPermissions = permissions;
182 ea[0].grfAccessMode = REVOKE_ACCESS;
183 ea[0].grfInheritance = NO_INHERITANCE;
184 ea[0].Trustee.TrusteeForm = TRUSTEE_IS_SID;
185 ea[0].Trustee.ptstrName = (LPTSTR)worldsid;
186 ea[1].grfAccessPermissions = permissions;
187 ea[1].grfAccessMode = GRANT_ACCESS;
188 ea[1].grfInheritance = NO_INHERITANCE;
189 ea[1].Trustee.TrusteeForm = TRUSTEE_IS_SID;
190 ea[1].Trustee.ptstrName = (LPTSTR)usersid;
191 ea[2].grfAccessPermissions = permissions;
192 ea[2].grfAccessMode = REVOKE_ACCESS;
193 ea[2].grfInheritance = NO_INHERITANCE;
194 ea[2].Trustee.TrusteeForm = TRUSTEE_IS_SID;
195 ea[2].Trustee.ptstrName = (LPTSTR)networksid;
197 acl_err = p_SetEntriesInAclA(3, ea, NULL, acl);
198 if (acl_err != ERROR_SUCCESS || *acl == NULL) {
199 *error = dupprintf("unable to construct ACL: %s",
200 win_strerror(acl_err));
201 goto cleanup;
204 *psd = (PSECURITY_DESCRIPTOR)
205 LocalAlloc(LPTR, SECURITY_DESCRIPTOR_MIN_LENGTH);
206 if (!*psd) {
207 *error = dupprintf("unable to allocate security descriptor: %s",
208 win_strerror(GetLastError()));
209 goto cleanup;
212 if (!InitializeSecurityDescriptor(*psd, SECURITY_DESCRIPTOR_REVISION)) {
213 *error = dupprintf("unable to initialise security descriptor: %s",
214 win_strerror(GetLastError()));
215 goto cleanup;
218 if (!SetSecurityDescriptorOwner(*psd, usersid, false)) {
219 *error = dupprintf("unable to set owner in security descriptor: %s",
220 win_strerror(GetLastError()));
221 goto cleanup;
224 if (!SetSecurityDescriptorDacl(*psd, true, *acl, false)) {
225 *error = dupprintf("unable to set DACL in security descriptor: %s",
226 win_strerror(GetLastError()));
227 goto cleanup;
230 ret = true;
232 cleanup:
233 if (!ret) {
234 if (*psd) {
235 LocalFree(*psd);
236 *psd = NULL;
238 if (*acl) {
239 LocalFree(*acl);
240 *acl = NULL;
242 } else {
243 sfree(*error);
244 *error = NULL;
246 return ret;
249 static bool acl_restricted = false;
250 bool restricted_acl(void) { return acl_restricted; }
252 static bool really_restrict_process_acl(char **error)
254 EXPLICIT_ACCESS ea[2];
255 int acl_err;
256 bool ret = false;
257 PACL acl = NULL;
259 static const DWORD nastyace=WRITE_DAC | WRITE_OWNER |
260 PROCESS_CREATE_PROCESS | PROCESS_CREATE_THREAD |
261 PROCESS_DUP_HANDLE |
262 PROCESS_SET_QUOTA | PROCESS_SET_INFORMATION |
263 PROCESS_VM_OPERATION | PROCESS_VM_READ | PROCESS_VM_WRITE |
264 PROCESS_SUSPEND_RESUME;
266 if (!getsids(error))
267 goto cleanup;
269 memset(ea, 0, sizeof(ea));
271 /* Everyone: deny */
272 ea[0].grfAccessPermissions = nastyace;
273 ea[0].grfAccessMode = DENY_ACCESS;
274 ea[0].grfInheritance = SUB_CONTAINERS_AND_OBJECTS_INHERIT;
275 ea[0].Trustee.TrusteeForm = TRUSTEE_IS_SID;
276 ea[0].Trustee.ptstrName = (LPTSTR)worldsid;
278 /* User: user ace */
279 ea[1].grfAccessPermissions = ~nastyace & 0x1fff;
280 ea[1].grfAccessMode = GRANT_ACCESS;
281 ea[1].grfInheritance = SUB_CONTAINERS_AND_OBJECTS_INHERIT;
282 ea[1].Trustee.TrusteeForm = TRUSTEE_IS_SID;
283 ea[1].Trustee.ptstrName = (LPTSTR)usersid;
285 acl_err = p_SetEntriesInAclA(2, ea, NULL, &acl);
287 if (acl_err != ERROR_SUCCESS || acl == NULL) {
288 *error = dupprintf("unable to construct ACL: %s",
289 win_strerror(acl_err));
290 goto cleanup;
293 if (ERROR_SUCCESS != p_SetSecurityInfo(
294 GetCurrentProcess(), SE_KERNEL_OBJECT,
295 OWNER_SECURITY_INFORMATION | DACL_SECURITY_INFORMATION,
296 usersid, NULL, acl, NULL)) {
297 *error = dupprintf("Unable to set process ACL: %s",
298 win_strerror(GetLastError()));
299 goto cleanup;
302 acl_restricted = true;
303 ret=true;
305 cleanup:
306 if (!ret) {
307 if (acl) {
308 LocalFree(acl);
309 acl = NULL;
312 return ret;
316 * Lock down our process's ACL, to present an obstacle to malware
317 * trying to write into its memory. This can't be a full defence,
318 * because well timed malware could attack us before this code runs -
319 * even if it was unconditionally run at the very start of main(),
320 * which we wouldn't want to do anyway because it turns out in practie
321 * that interfering with other processes in this way has significant
322 * non-infringing uses on Windows (e.g. screen reader software).
324 * If we've been requested to do this and are unsuccessful, bomb out
325 * via modalfatalbox rather than continue in a less protected mode.
327 * This function is intentionally outside the #ifndef NO_SECURITY that
328 * covers the rest of this file, because when PuTTY is compiled
329 * without the ability to restrict its ACL, we don't want it to
330 * silently pretend to honour the instruction to do so.
332 void restrict_process_acl(void)
334 char *error = NULL;
335 bool ret;
337 ret = really_restrict_process_acl(&error);
338 if (!ret)
339 modalfatalbox("Could not restrict process ACL: %s", error);