3 * Windows security support.
5 * Copyright 2016 Microsoft
6 * Licensed under the MIT license. See LICENSE file in the project root for full license information.
11 #if defined(HOST_WIN32)
14 #include "mono/metadata/mono-security-windows-internals.h"
16 #if G_HAVE_API_SUPPORT(HAVE_CLASSIC_WINAPI_SUPPORT)
21 #ifndef PROTECTED_DACL_SECURITY_INFORMATION
22 #define PROTECTED_DACL_SECURITY_INFORMATION 0x80000000L
25 #if G_HAVE_API_SUPPORT(HAVE_CLASSIC_WINAPI_SUPPORT)
28 GetSidName (gunichar2
*server
, PSID sid
, gint32
*size
)
30 gunichar2
*uniname
= NULL
;
33 SID_NAME_USE peUse
; /* out */
35 LookupAccountSid (server
, sid
, NULL
, &cchName
, NULL
,
38 if ((cchName
> 0) && (cchDomain
> 0)) {
39 gunichar2
*user
= g_malloc0 ((cchName
+ 1) * 2);
40 gunichar2
*domain
= g_malloc0 ((cchDomain
+ 1) * 2);
42 LookupAccountSid (server
, sid
, user
, &cchName
, domain
,
47 /* domain/machine name included (+ sepearator) */
48 *size
= cchName
+ cchDomain
+ 1;
49 uniname
= g_malloc0 ((*size
+ 1) * 2);
50 memcpy (uniname
, domain
, cchDomain
* 2);
51 *(uniname
+ cchDomain
) = '\\';
52 memcpy (uniname
+ cchDomain
+ 1, user
, cchName
* 2);
56 /* no domain / machine */
62 /* nothing -> return NULL */
73 mono_security_principal_windows_identity_get_current_token (MonoError
*error
)
75 gpointer token
= NULL
;
77 /* Note: This isn't a copy of the Token - we must not close it!!!
78 * http://www.develop.com/kbrown/book/html/whatis_windowsprincipal.html
81 /* thread may be impersonating somebody */
82 if (OpenThreadToken (GetCurrentThread (), MAXIMUM_ALLOWED
, 1, &token
) == 0) {
83 /* if not take the process identity */
84 OpenProcessToken (GetCurrentProcess (), MAXIMUM_ALLOWED
, &token
);
91 ves_icall_System_Security_Principal_WindowsIdentity_GetCurrentToken (MonoError
*error
)
93 return mono_security_principal_windows_identity_get_current_token (error
);
97 mono_security_win_get_token_name (gpointer token
, gunichar2
** uniname
, MonoError
*error
)
101 GetTokenInformation (token
, TokenUser
, NULL
, size
, (PDWORD
)&size
);
103 TOKEN_USER
*tu
= g_malloc0 (size
);
104 if (GetTokenInformation (token
, TokenUser
, tu
, size
, (PDWORD
)&size
)) {
105 *uniname
= GetSidName (NULL
, tu
->User
.Sid
, &size
);
112 #endif /* G_HAVE_API_SUPPORT(HAVE_CLASSIC_WINAPI_SUPPORT) */
115 ves_icall_System_Security_Principal_WindowsIdentity_GetTokenName (gpointer token
, MonoError
*error
)
117 MonoStringHandle result
;
118 gunichar2
*uniname
= NULL
;
123 size
= mono_security_win_get_token_name (token
, &uniname
, error
);
126 result
= mono_string_new_utf16_handle (mono_domain_get (), uniname
, size
, error
);
129 result
= mono_string_new_handle (mono_domain_get (), "", error
);
138 ves_icall_System_Security_Principal_WindowsIdentity_GetUserToken (MonoStringHandle username
, MonoError
*error
)
141 gpointer token
= NULL
;
143 /* TODO: MS has something like this working in Windows 2003 (client and
144 * server) but works only for domain accounts (so it's quite limiting).
145 * http://www.develop.com/kbrown/book/html/howto_logonuser.html
147 g_warning ("Unsupported on Win32 (anyway requires W2K3 minimum)");
151 #if G_HAVE_API_SUPPORT(HAVE_CLASSIC_WINAPI_SUPPORT)
153 ves_icall_System_Security_Principal_WindowsIdentity_GetRoles (gpointer token
)
156 MonoArray
*array
= NULL
;
157 MonoDomain
*domain
= mono_domain_get ();
161 GetTokenInformation (token
, TokenGroups
, NULL
, size
, (PDWORD
)&size
);
163 TOKEN_GROUPS
*tg
= g_malloc0 (size
);
164 if (GetTokenInformation (token
, TokenGroups
, tg
, size
, (PDWORD
)&size
)) {
166 int num
= tg
->GroupCount
;
168 array
= mono_array_new_checked (domain
, mono_get_string_class (), num
, error
);
169 if (mono_error_set_pending_exception (error
)) {
174 for (i
=0; i
< num
; i
++) {
176 gunichar2
*uniname
= GetSidName (NULL
, tg
->Groups
[i
].Sid
, &size
);
179 MonoString
*str
= mono_string_new_utf16_checked (domain
, uniname
, size
, error
);
180 if (!is_ok (error
)) {
183 mono_error_set_pending_exception (error
);
186 mono_array_setref_internal (array
, i
, str
);
195 /* return empty array of string, i.e. string [0] */
196 array
= mono_array_new_checked (domain
, mono_get_string_class (), 0, error
);
197 mono_error_set_pending_exception (error
);
201 #endif /* G_HAVE_API_SUPPORT(HAVE_CLASSIC_WINAPI_SUPPORT) */
204 ves_icall_System_Security_Principal_WindowsImpersonationContext_CloseToken (gpointer token
, MonoError
*error
)
206 return !!CloseHandle (token
);
209 #if G_HAVE_API_SUPPORT(HAVE_CLASSIC_WINAPI_SUPPORT)
211 ves_icall_System_Security_Principal_WindowsImpersonationContext_DuplicateToken (gpointer token
, MonoError
*error
)
213 gpointer dupe
= NULL
;
214 return DuplicateToken (token
, SecurityImpersonation
, &dupe
) ? dupe
: NULL
;
216 #endif /* G_HAVE_API_SUPPORT(HAVE_CLASSIC_WINAPI_SUPPORT) */
219 ves_icall_System_Security_Principal_WindowsPrincipal_IsMemberOfGroupId (gpointer user
, gpointer group
, MonoError
*error
)
221 /* The convertion from an ID to a string is done in managed code for Windows */
222 g_warning ("IsMemberOfGroupId should never be called on Win32");
227 ves_icall_System_Security_Principal_WindowsPrincipal_IsMemberOfGroupName (gpointer user
, const gchar
*group
, MonoError
*error
)
229 /* Windows version use a cache built using WindowsIdentity._GetRoles */
230 g_warning ("IsMemberOfGroupName should never be called on Win32");
234 #if G_HAVE_API_SUPPORT(HAVE_CLASSIC_WINAPI_SUPPORT)
237 GetSidHelper (const SID_IDENTIFIER_AUTHORITY
*authority
, BYTE subAuthorityCount
, DWORD subAuthority0
, DWORD subAuthority1
)
240 // This SID must be freed with FreeSid ().
241 return AllocateAndInitializeSid ((PSID_IDENTIFIER_AUTHORITY
)authority
, subAuthorityCount
,
242 subAuthority0
, subAuthority1
, 0/*2*/, 0/*3*/, 0/*4*/, 0/*5*/, 0/*6*/, 0/*7*/, &pSid
) ? pSid
: NULL
;
246 GetAdministratorsSid (void)
248 const static SID_IDENTIFIER_AUTHORITY admins
= { SECURITY_NT_AUTHORITY
};
249 // This SID must be freed with FreeSid ().
250 return GetSidHelper (&admins
, 2, SECURITY_BUILTIN_DOMAIN_RID
, DOMAIN_ALIAS_RID_ADMINS
);
254 GetEveryoneSid (void)
256 const static SID_IDENTIFIER_AUTHORITY everyone
= { SECURITY_WORLD_SID_AUTHORITY
};
257 // This SID must be freed with FreeSid ().
258 return GetSidHelper (&everyone
, 1, SECURITY_WORLD_RID
, 0);
262 GetCurrentUserSid (MonoError
*error
)
266 gpointer token
= mono_security_principal_windows_identity_get_current_token (error
);
268 GetTokenInformation (token
, TokenUser
, NULL
, size
, &size
);
270 TOKEN_USER
*tu
= g_malloc0 (size
);
271 if (GetTokenInformation (token
, TokenUser
, tu
, size
, &size
)) {
272 DWORD length
= GetLengthSid (tu
->User
.Sid
);
273 sid
= (PSID
) g_malloc0 (length
);
274 if (!CopySid (length
, sid
, tu
->User
.Sid
)) {
281 /* Note: this SID must be freed with g_free () */
286 GetRightsFromSid (PSID sid
, PACL acl
)
288 ACCESS_MASK rights
= 0;
291 BuildTrusteeWithSidW (&trustee
, sid
);
292 if (GetEffectiveRightsFromAcl (acl
, &trustee
, &rights
) != ERROR_SUCCESS
)
299 mono_security_win_is_machine_protected (const gunichar2
*path
, MonoError
*error
)
301 gboolean success
= FALSE
;
303 PSECURITY_DESCRIPTOR pSD
= NULL
;
305 DWORD dwRes
= GetNamedSecurityInfoW ((PWSTR
)path
, SE_FILE_OBJECT
, DACL_SECURITY_INFORMATION
, NULL
, NULL
, &pDACL
, NULL
, &pSD
);
306 if (dwRes
!= ERROR_SUCCESS
)
309 /* We check that Everyone is still limited to READ-ONLY -
310 but not if new entries have been added by an Administrator */
312 PSID
const pEveryoneSid
= GetEveryoneSid ();
314 ACCESS_MASK rights
= GetRightsFromSid (pEveryoneSid
, pDACL
);
315 /* http://msdn.microsoft.com/library/en-us/security/security/generic_access_rights.asp?frame=true */
316 success
= (rights
== (READ_CONTROL
| SYNCHRONIZE
| FILE_READ_DATA
| FILE_READ_EA
| FILE_READ_ATTRIBUTES
));
317 FreeSid (pEveryoneSid
);
319 /* Note: we don't need to check our own access -
320 we'll know soon enough when reading the file */
328 mono_security_win_is_user_protected (const gunichar2
*path
, MonoError
*error
)
330 gboolean success
= FALSE
;
332 PSECURITY_DESCRIPTOR pSecurityDescriptor
= NULL
;
334 DWORD dwRes
= GetNamedSecurityInfoW ((PWSTR
)path
, SE_FILE_OBJECT
,
335 DACL_SECURITY_INFORMATION
, NULL
, NULL
, &pDACL
, NULL
, &pSecurityDescriptor
);
336 if (dwRes
!= ERROR_SUCCESS
)
339 /* We check that our original entries in the ACL are in place -
340 but not if new entries have been added by the user */
342 /* Everyone should be denied */
343 PSID
const pEveryoneSid
= GetEveryoneSid ();
345 ACCESS_MASK rights
= GetRightsFromSid (pEveryoneSid
, pDACL
);
346 success
= (rights
== 0);
347 FreeSid (pEveryoneSid
);
349 /* Note: we don't need to check our own access -
350 we'll know soon enough when reading the file */
352 LocalFree (pSecurityDescriptor
);
358 mono_security_win_protect_machine (const gunichar2
*path
, MonoError
*error
)
360 PSID pEveryoneSid
= GetEveryoneSid ();
361 PSID pAdminsSid
= GetAdministratorsSid ();
364 if (pEveryoneSid
&& pAdminsSid
) {
366 EXPLICIT_ACCESS ea
[2];
367 ZeroMemory (&ea
, sizeof (ea
));
369 /* grant all access to the BUILTIN\Administrators group */
370 BuildTrusteeWithSidW (&ea
[0].Trustee
, pAdminsSid
);
371 ea
[0].grfAccessPermissions
= GENERIC_ALL
;
372 ea
[0].grfAccessMode
= SET_ACCESS
;
373 ea
[0].grfInheritance
= SUB_CONTAINERS_AND_OBJECTS_INHERIT
;
374 ea
[0].Trustee
.TrusteeForm
= TRUSTEE_IS_SID
;
375 ea
[0].Trustee
.TrusteeType
= TRUSTEE_IS_WELL_KNOWN_GROUP
;
377 /* read-only access everyone */
378 BuildTrusteeWithSidW (&ea
[1].Trustee
, pEveryoneSid
);
379 ea
[1].grfAccessPermissions
= GENERIC_READ
;
380 ea
[1].grfAccessMode
= SET_ACCESS
;
381 ea
[1].grfInheritance
= SUB_CONTAINERS_AND_OBJECTS_INHERIT
;
382 ea
[1].Trustee
.TrusteeForm
= TRUSTEE_IS_SID
;
383 ea
[1].Trustee
.TrusteeType
= TRUSTEE_IS_WELL_KNOWN_GROUP
;
385 retval
= SetEntriesInAcl (2, ea
, NULL
, &pDACL
);
386 if (retval
== ERROR_SUCCESS
) {
387 /* with PROTECTED_DACL_SECURITY_INFORMATION we */
388 /* remove any existing ACL (like inherited ones) */
389 retval
= SetNamedSecurityInfoW ((PWSTR
)path
, SE_FILE_OBJECT
,
390 DACL_SECURITY_INFORMATION
| PROTECTED_DACL_SECURITY_INFORMATION
,
391 NULL
, NULL
, pDACL
, NULL
);
397 FreeSid (pEveryoneSid
);
399 FreeSid (pAdminsSid
);
400 return retval
== ERROR_SUCCESS
;
404 mono_security_win_protect_user (const gunichar2
*path
, MonoError
*error
)
408 PSID
const pCurrentSid
= GetCurrentUserSid (error
);
412 ZeroMemory (&ea
, sizeof (EXPLICIT_ACCESS
));
414 /* grant exclusive access to the current user */
415 BuildTrusteeWithSidW (&ea
.Trustee
, pCurrentSid
);
416 ea
.grfAccessPermissions
= GENERIC_ALL
;
417 ea
.grfAccessMode
= SET_ACCESS
;
418 ea
.grfInheritance
= SUB_CONTAINERS_AND_OBJECTS_INHERIT
;
419 ea
.Trustee
.TrusteeForm
= TRUSTEE_IS_SID
;
420 ea
.Trustee
.TrusteeType
= TRUSTEE_IS_USER
;
422 retval
= SetEntriesInAcl (1, &ea
, NULL
, &pDACL
);
423 if (retval
== ERROR_SUCCESS
) {
424 /* with PROTECTED_DACL_SECURITY_INFORMATION we
425 remove any existing ACL (like inherited ones) */
426 retval
= SetNamedSecurityInfoW ((PWSTR
)path
, SE_FILE_OBJECT
,
427 DACL_SECURITY_INFORMATION
| PROTECTED_DACL_SECURITY_INFORMATION
,
428 NULL
, NULL
, pDACL
, NULL
);
432 g_free (pCurrentSid
); /* g_malloc0 */
435 return retval
== ERROR_SUCCESS
;
438 #endif /* G_HAVE_API_SUPPORT(HAVE_CLASSIC_WINAPI_SUPPORT) */
441 ves_icall_Mono_Security_Cryptography_KeyPairPersistence_CanSecure (const gunichar2
*root
, MonoError
*error
)
445 /* ACL are nice... unless you have FAT or other uncivilized filesystem */
446 if (!GetVolumeInformationW (root
, NULL
, 0, NULL
, NULL
, &flags
, NULL
, 0))
448 return (flags
& FS_PERSISTENT_ACLS
) == FS_PERSISTENT_ACLS
;
452 ves_icall_Mono_Security_Cryptography_KeyPairPersistence_IsMachineProtected (const gunichar2
*path
, MonoError
*error
)
454 /* no one, but the owner, should have write access to the directory */
455 return (MonoBoolean
)mono_security_win_is_machine_protected (path
, error
);
459 ves_icall_Mono_Security_Cryptography_KeyPairPersistence_IsUserProtected (const gunichar2
*path
, MonoError
*error
)
461 /* no one, but the user, should have access to the directory */
462 return (MonoBoolean
)mono_security_win_is_user_protected (path
, error
);
466 ves_icall_Mono_Security_Cryptography_KeyPairPersistence_ProtectMachine (const gunichar2
*path
, MonoError
*error
)
468 /* read/write to owner, read to everyone else */
469 return (MonoBoolean
)mono_security_win_protect_machine (path
, error
);
473 ves_icall_Mono_Security_Cryptography_KeyPairPersistence_ProtectUser (const gunichar2
*path
, MonoError
*error
)
475 /* read/write to user, no access to everyone else */
476 return (MonoBoolean
)mono_security_win_protect_user (path
, error
);
479 #endif /* HOST_WIN32 */