2 * windows/utils/security.c: implementation of security-api.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)
26 /* Legacy pre-NT platforms are not expected to have any of these APIs */
28 return (osPlatformId
== VER_PLATFORM_WIN32_NT
);
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. */
39 static bool attempted
= false;
40 static bool successful
;
41 static HMODULE advapi
;
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
);
58 PSID
get_user_sid(void)
60 HANDLE proc
= NULL
, tok
= NULL
;
61 TOKEN_USER
*user
= NULL
;
63 PSID sid
= NULL
, ret
= NULL
;
71 if ((proc
= OpenProcess(MAXIMUM_ALLOWED
, false,
72 GetCurrentProcessId())) == NULL
)
75 if (!p_OpenProcessToken(proc
, TOKEN_QUERY
, &tok
))
78 if (!p_GetTokenInformation(tok
, TokenUser
, NULL
, 0, &toklen
) &&
79 GetLastError() != ERROR_INSUFFICIENT_BUFFER
)
82 if ((user
= (TOKEN_USER
*)LocalAlloc(LPTR
, toklen
)) == NULL
)
85 if (!p_GetTokenInformation(tok
, TokenUser
, user
, toklen
, &toklen
))
88 sidlen
= GetLengthSid(user
->User
.Sid
);
90 sid
= (PSID
)smalloc(sidlen
);
92 if (!CopySid(sidlen
, sid
, user
->User
.Sid
))
95 /* Success. Move sid into the return value slot, and null it out
96 * to stop the cleanup code freeing it. */
113 static bool getsids(char **error
)
116 #pragma clang diagnostic push
117 #pragma clang diagnostic ignored "-Wmissing-braces"
119 SID_IDENTIFIER_AUTHORITY world_auth
= SECURITY_WORLD_SID_AUTHORITY
;
120 SID_IDENTIFIER_AUTHORITY nt_auth
= SECURITY_NT_AUTHORITY
;
122 #pragma clang diagnostic pop
130 if ((usersid
= get_user_sid()) == NULL
) {
131 *error
= dupprintf("unable to construct SID for current user: %s",
132 win_strerror(GetLastError()));
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()));
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()));
163 bool make_private_security_descriptor(DWORD permissions
,
164 PSECURITY_DESCRIPTOR
*psd
,
168 EXPLICIT_ACCESS ea
[3];
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
));
204 *psd
= (PSECURITY_DESCRIPTOR
)
205 LocalAlloc(LPTR
, SECURITY_DESCRIPTOR_MIN_LENGTH
);
207 *error
= dupprintf("unable to allocate security descriptor: %s",
208 win_strerror(GetLastError()));
212 if (!InitializeSecurityDescriptor(*psd
, SECURITY_DESCRIPTOR_REVISION
)) {
213 *error
= dupprintf("unable to initialise security descriptor: %s",
214 win_strerror(GetLastError()));
218 if (!SetSecurityDescriptorOwner(*psd
, usersid
, false)) {
219 *error
= dupprintf("unable to set owner in security descriptor: %s",
220 win_strerror(GetLastError()));
224 if (!SetSecurityDescriptorDacl(*psd
, true, *acl
, false)) {
225 *error
= dupprintf("unable to set DACL in security descriptor: %s",
226 win_strerror(GetLastError()));
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];
259 static const DWORD nastyace
=WRITE_DAC
| WRITE_OWNER
|
260 PROCESS_CREATE_PROCESS
| PROCESS_CREATE_THREAD
|
262 PROCESS_SET_QUOTA
| PROCESS_SET_INFORMATION
|
263 PROCESS_VM_OPERATION
| PROCESS_VM_READ
| PROCESS_VM_WRITE
|
264 PROCESS_SUSPEND_RESUME
;
269 memset(ea
, 0, sizeof(ea
));
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
;
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
));
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()));
302 acl_restricted
= true;
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)
337 ret
= really_restrict_process_acl(&error
);
339 modalfatalbox("Could not restrict process ACL: %s", error
);