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/handle.h>
15 #include <mono/utils/mono-error.h>
16 #include <mono/utils/mono-error-internals.h>
17 #include <mono/metadata/object-internals.h>
18 #include <mono/utils/w32subset.h>
20 #if HAVE_API_SUPPORT_WIN32_SECURITY
25 #ifndef PROTECTED_DACL_SECURITY_INFORMATION
26 #define PROTECTED_DACL_SECURITY_INFORMATION 0x80000000L
29 #if HAVE_API_SUPPORT_WIN32_SECURITY
32 GetSidName (gunichar2
*server
, PSID sid
, gint32
*size
)
34 gunichar2
*uniname
= NULL
;
37 SID_NAME_USE peUse
; /* out */
39 LookupAccountSidW (server
, sid
, NULL
, &cchName
, NULL
,
42 if ((cchName
> 0) && (cchDomain
> 0)) {
43 gunichar2
*user
= g_malloc0 ((cchName
+ 1) * 2);
44 gunichar2
*domain
= g_malloc0 ((cchDomain
+ 1) * 2);
46 LookupAccountSidW (server
, sid
, user
, &cchName
, domain
,
51 /* domain/machine name included (+ sepearator) */
52 *size
= cchName
+ cchDomain
+ 1;
53 uniname
= g_malloc0 ((*size
+ 1) * 2);
54 memcpy (uniname
, domain
, cchDomain
* 2);
55 *(uniname
+ cchDomain
) = '\\';
56 memcpy (uniname
+ cchDomain
+ 1, user
, cchName
* 2);
60 /* no domain / machine */
66 /* nothing -> return NULL */
77 mono_security_principal_windows_identity_get_current_token (MonoError
*error
)
79 gpointer token
= NULL
;
81 /* Note: This isn't a copy of the Token - we must not close it!!!
82 * http://www.develop.com/kbrown/book/html/whatis_windowsprincipal.html
85 /* thread may be impersonating somebody */
86 if (OpenThreadToken (GetCurrentThread (), MAXIMUM_ALLOWED
, 1, &token
) == 0) {
87 /* if not take the process identity */
88 OpenProcessToken (GetCurrentProcess (), MAXIMUM_ALLOWED
, &token
);
95 ves_icall_System_Security_Principal_WindowsIdentity_GetCurrentToken (MonoError
*error
)
97 return mono_security_principal_windows_identity_get_current_token (error
);
101 mono_security_win_get_token_name (gpointer token
, gunichar2
** uniname
, MonoError
*error
)
105 GetTokenInformation (token
, TokenUser
, NULL
, size
, (PDWORD
)&size
);
107 TOKEN_USER
*tu
= g_malloc0 (size
);
108 if (GetTokenInformation (token
, TokenUser
, tu
, size
, (PDWORD
)&size
)) {
109 *uniname
= GetSidName (NULL
, tu
->User
.Sid
, &size
);
116 #elif !HAVE_EXTERN_DEFINED_WIN32_SECURITY
118 mono_security_win_not_supported (const char *functions
, MonoError
*error
)
120 g_unsupported_api (functions
);
121 mono_error_set_not_supported (error
, G_UNSUPPORTED_API
, functions
);
122 SetLastError (ERROR_NOT_SUPPORTED
);
126 mono_security_principal_windows_identity_get_current_token (MonoError
*error
)
128 mono_security_win_not_supported ("OpenThreadToken, OpenProcessToken", error
);
133 ves_icall_System_Security_Principal_WindowsIdentity_GetCurrentToken (MonoError
*error
)
135 return mono_security_principal_windows_identity_get_current_token (error
);
139 mono_security_win_get_token_name (gpointer token
, gunichar2
**uniname
, MonoError
*error
)
141 mono_security_win_not_supported ("GetTokenInformation", error
);
144 #endif /* HAVE_API_SUPPORT_WIN32_SECURITY */
147 ves_icall_System_Security_Principal_WindowsIdentity_GetTokenName (gpointer token
, MonoError
*error
)
149 MonoStringHandle result
;
150 gunichar2
*uniname
= NULL
;
155 size
= mono_security_win_get_token_name (token
, &uniname
, error
);
156 if (size
== 0 && !is_ok (error
))
157 return NULL_HANDLE_STRING
;
160 result
= mono_string_new_utf16_handle (mono_domain_get (), uniname
, size
, error
);
163 result
= mono_string_new_handle (mono_domain_get (), "", error
);
172 ves_icall_System_Security_Principal_WindowsIdentity_GetUserToken (MonoStringHandle username
, MonoError
*error
)
175 gpointer token
= NULL
;
177 /* TODO: MS has something like this working in Windows 2003 (client and
178 * server) but works only for domain accounts (so it's quite limiting).
179 * http://www.develop.com/kbrown/book/html/howto_logonuser.html
181 g_warning ("Unsupported on Win32 (anyway requires W2K3 minimum)");
185 #if HAVE_API_SUPPORT_WIN32_SECURITY
187 ves_icall_System_Security_Principal_WindowsIdentity_GetRoles (gpointer token
, MonoError
*error
)
189 MonoArrayHandle array
;
190 MonoStringHandle str_h
;
191 MonoDomain
*domain
= mono_domain_get ();
193 gboolean created
= FALSE
;
195 GetTokenInformation (token
, TokenGroups
, NULL
, size
, (PDWORD
)&size
);
197 TOKEN_GROUPS
*tg
= g_malloc0 (size
);
198 if (GetTokenInformation (token
, TokenGroups
, tg
, size
, (PDWORD
)&size
)) {
200 int num
= tg
->GroupCount
;
202 array
= mono_array_new_handle (domain
, mono_get_string_class (), num
, error
);
203 if (!is_ok (error
)) {
205 return NULL_HANDLE_ARRAY
;
209 str_h
= MONO_HANDLE_NEW (MonoString
, NULL
);
210 for (i
=0; i
< num
; i
++) {
212 gunichar2
*uniname
= GetSidName (NULL
, tg
->Groups
[i
].Sid
, &size
);
215 MonoString
*str
= mono_string_new_utf16_checked (domain
, uniname
, size
, error
);
216 MONO_HANDLE_ASSIGN_RAW (str_h
, str
);
217 if (!is_ok (error
)) {
220 return NULL_HANDLE_ARRAY
;
222 MONO_HANDLE_ARRAY_SETREF (array
, i
, str_h
);
231 /* return empty array of string, i.e. string [0] */
232 array
= mono_array_new_handle (domain
, mono_get_string_class (), 0, error
);
233 return_val_if_nok (error
, NULL_HANDLE_ARRAY
);
237 #elif !HAVE_EXTERN_DEFINED_WIN32_SECURITY
239 ves_icall_System_Security_Principal_WindowsIdentity_GetRoles (gpointer token
, MonoError
*error
)
241 mono_security_win_not_supported ("GetTokenInformation", error
);
242 mono_error_set_pending_exception (error
);
245 #endif /* HAVE_API_SUPPORT_WIN32_SECURITY */
248 ves_icall_System_Security_Principal_WindowsImpersonationContext_CloseToken (gpointer token
, MonoError
*error
)
250 return !!CloseHandle (token
);
253 #if HAVE_API_SUPPORT_WIN32_SECURITY
255 ves_icall_System_Security_Principal_WindowsImpersonationContext_DuplicateToken (gpointer token
, MonoError
*error
)
257 gpointer dupe
= NULL
;
258 return DuplicateToken (token
, SecurityImpersonation
, &dupe
) ? dupe
: NULL
;
260 #elif !HAVE_EXTERN_DEFINED_WIN32_SECURITY
262 ves_icall_System_Security_Principal_WindowsImpersonationContext_DuplicateToken (gpointer token
, MonoError
*error
)
264 mono_security_win_not_supported ("DuplicateToken", error
);
267 #endif /* HAVE_API_SUPPORT_WIN32_SECURITY */
270 ves_icall_System_Security_Principal_WindowsPrincipal_IsMemberOfGroupId (gpointer user
, gpointer group
, MonoError
*error
)
272 /* The convertion from an ID to a string is done in managed code for Windows */
273 g_warning ("IsMemberOfGroupId should never be called on Win32");
278 ves_icall_System_Security_Principal_WindowsPrincipal_IsMemberOfGroupName (gpointer user
, const gchar
*group
, MonoError
*error
)
280 /* Windows version use a cache built using WindowsIdentity._GetRoles */
281 g_warning ("IsMemberOfGroupName should never be called on Win32");
285 #if HAVE_API_SUPPORT_WIN32_SECURITY
288 GetSidHelper (const SID_IDENTIFIER_AUTHORITY
*authority
, BYTE subAuthorityCount
, DWORD subAuthority0
, DWORD subAuthority1
)
291 // This SID must be freed with FreeSid ().
292 return AllocateAndInitializeSid ((PSID_IDENTIFIER_AUTHORITY
)authority
, subAuthorityCount
,
293 subAuthority0
, subAuthority1
, 0/*2*/, 0/*3*/, 0/*4*/, 0/*5*/, 0/*6*/, 0/*7*/, &pSid
) ? pSid
: NULL
;
297 GetAdministratorsSid (void)
299 const static SID_IDENTIFIER_AUTHORITY admins
= { SECURITY_NT_AUTHORITY
};
300 // This SID must be freed with FreeSid ().
301 return GetSidHelper (&admins
, 2, SECURITY_BUILTIN_DOMAIN_RID
, DOMAIN_ALIAS_RID_ADMINS
);
305 GetEveryoneSid (void)
307 const static SID_IDENTIFIER_AUTHORITY everyone
= { SECURITY_WORLD_SID_AUTHORITY
};
308 // This SID must be freed with FreeSid ().
309 return GetSidHelper (&everyone
, 1, SECURITY_WORLD_RID
, 0);
313 GetCurrentUserSid (MonoError
*error
)
317 gpointer token
= mono_security_principal_windows_identity_get_current_token (error
);
319 GetTokenInformation (token
, TokenUser
, NULL
, size
, &size
);
321 TOKEN_USER
*tu
= g_malloc0 (size
);
322 if (GetTokenInformation (token
, TokenUser
, tu
, size
, &size
)) {
323 DWORD length
= GetLengthSid (tu
->User
.Sid
);
324 sid
= (PSID
) g_malloc0 (length
);
325 if (!CopySid (length
, sid
, tu
->User
.Sid
)) {
332 /* Note: this SID must be freed with g_free () */
337 GetRightsFromSid (PSID sid
, PACL acl
)
339 ACCESS_MASK rights
= 0;
342 BuildTrusteeWithSidW (&trustee
, sid
);
343 if (GetEffectiveRightsFromAclW (acl
, &trustee
, &rights
) != ERROR_SUCCESS
)
350 mono_security_win_is_machine_protected (const gunichar2
*path
, MonoError
*error
)
352 gboolean success
= FALSE
;
354 PSECURITY_DESCRIPTOR pSD
= NULL
;
356 DWORD dwRes
= GetNamedSecurityInfoW ((PWSTR
)path
, SE_FILE_OBJECT
, DACL_SECURITY_INFORMATION
, NULL
, NULL
, &pDACL
, NULL
, &pSD
);
357 if (dwRes
!= ERROR_SUCCESS
)
360 /* We check that Everyone is still limited to READ-ONLY -
361 but not if new entries have been added by an Administrator */
363 PSID
const pEveryoneSid
= GetEveryoneSid ();
365 ACCESS_MASK rights
= GetRightsFromSid (pEveryoneSid
, pDACL
);
366 /* http://msdn.microsoft.com/library/en-us/security/security/generic_access_rights.asp?frame=true */
367 success
= (rights
== (READ_CONTROL
| SYNCHRONIZE
| FILE_READ_DATA
| FILE_READ_EA
| FILE_READ_ATTRIBUTES
));
368 FreeSid (pEveryoneSid
);
370 /* Note: we don't need to check our own access -
371 we'll know soon enough when reading the file */
379 mono_security_win_is_user_protected (const gunichar2
*path
, MonoError
*error
)
381 gboolean success
= FALSE
;
383 PSECURITY_DESCRIPTOR pSecurityDescriptor
= NULL
;
385 DWORD dwRes
= GetNamedSecurityInfoW ((PWSTR
)path
, SE_FILE_OBJECT
,
386 DACL_SECURITY_INFORMATION
, NULL
, NULL
, &pDACL
, NULL
, &pSecurityDescriptor
);
387 if (dwRes
!= ERROR_SUCCESS
)
390 /* We check that our original entries in the ACL are in place -
391 but not if new entries have been added by the user */
393 /* Everyone should be denied */
394 PSID
const pEveryoneSid
= GetEveryoneSid ();
396 ACCESS_MASK rights
= GetRightsFromSid (pEveryoneSid
, pDACL
);
397 success
= (rights
== 0);
398 FreeSid (pEveryoneSid
);
400 /* Note: we don't need to check our own access -
401 we'll know soon enough when reading the file */
403 LocalFree (pSecurityDescriptor
);
409 mono_security_win_protect_machine (const gunichar2
*path
, MonoError
*error
)
411 PSID pEveryoneSid
= GetEveryoneSid ();
412 PSID pAdminsSid
= GetAdministratorsSid ();
415 if (pEveryoneSid
&& pAdminsSid
) {
417 EXPLICIT_ACCESSW ea
[2];
418 ZeroMemory (&ea
, sizeof (ea
));
420 /* grant all access to the BUILTIN\Administrators group */
421 BuildTrusteeWithSidW (&ea
[0].Trustee
, pAdminsSid
);
422 ea
[0].grfAccessPermissions
= GENERIC_ALL
;
423 ea
[0].grfAccessMode
= SET_ACCESS
;
424 ea
[0].grfInheritance
= SUB_CONTAINERS_AND_OBJECTS_INHERIT
;
425 ea
[0].Trustee
.TrusteeForm
= TRUSTEE_IS_SID
;
426 ea
[0].Trustee
.TrusteeType
= TRUSTEE_IS_WELL_KNOWN_GROUP
;
428 /* read-only access everyone */
429 BuildTrusteeWithSidW (&ea
[1].Trustee
, pEveryoneSid
);
430 ea
[1].grfAccessPermissions
= GENERIC_READ
;
431 ea
[1].grfAccessMode
= SET_ACCESS
;
432 ea
[1].grfInheritance
= SUB_CONTAINERS_AND_OBJECTS_INHERIT
;
433 ea
[1].Trustee
.TrusteeForm
= TRUSTEE_IS_SID
;
434 ea
[1].Trustee
.TrusteeType
= TRUSTEE_IS_WELL_KNOWN_GROUP
;
436 retval
= SetEntriesInAclW (2, ea
, NULL
, &pDACL
);
437 if (retval
== ERROR_SUCCESS
) {
438 /* with PROTECTED_DACL_SECURITY_INFORMATION we */
439 /* remove any existing ACL (like inherited ones) */
440 retval
= SetNamedSecurityInfoW ((PWSTR
)path
, SE_FILE_OBJECT
,
441 DACL_SECURITY_INFORMATION
| PROTECTED_DACL_SECURITY_INFORMATION
,
442 NULL
, NULL
, pDACL
, NULL
);
448 FreeSid (pEveryoneSid
);
450 FreeSid (pAdminsSid
);
451 return retval
== ERROR_SUCCESS
;
455 mono_security_win_protect_user (const gunichar2
*path
, MonoError
*error
)
459 PSID
const pCurrentSid
= GetCurrentUserSid (error
);
463 ZeroMemory (&ea
, sizeof (EXPLICIT_ACCESS
));
465 /* grant exclusive access to the current user */
466 BuildTrusteeWithSidW (&ea
.Trustee
, pCurrentSid
);
467 ea
.grfAccessPermissions
= GENERIC_ALL
;
468 ea
.grfAccessMode
= SET_ACCESS
;
469 ea
.grfInheritance
= SUB_CONTAINERS_AND_OBJECTS_INHERIT
;
470 ea
.Trustee
.TrusteeForm
= TRUSTEE_IS_SID
;
471 ea
.Trustee
.TrusteeType
= TRUSTEE_IS_USER
;
473 retval
= SetEntriesInAclW (1, &ea
, NULL
, &pDACL
);
474 if (retval
== ERROR_SUCCESS
) {
475 /* with PROTECTED_DACL_SECURITY_INFORMATION we
476 remove any existing ACL (like inherited ones) */
477 retval
= SetNamedSecurityInfoW ((PWSTR
)path
, SE_FILE_OBJECT
,
478 DACL_SECURITY_INFORMATION
| PROTECTED_DACL_SECURITY_INFORMATION
,
479 NULL
, NULL
, pDACL
, NULL
);
483 g_free (pCurrentSid
); /* g_malloc0 */
486 return retval
== ERROR_SUCCESS
;
488 #elif !HAVE_EXTERN_DEFINED_WIN32_SECURITY
490 mono_security_win_is_machine_protected (const gunichar2
*path
, MonoError
*error
)
492 mono_security_win_not_supported ("GetNamedSecurityInfo, LocalFree", error
);
497 mono_security_win_is_user_protected (const gunichar2
*path
, MonoError
*error
)
499 mono_security_win_not_supported ("GetNamedSecurityInfo, LocalFree", error
);
504 mono_security_win_protect_machine (const gunichar2
*path
, MonoError
*error
)
506 mono_security_win_not_supported ("BuildTrusteeWithSid, SetEntriesInAcl, SetNamedSecurityInfo, LocalFree, FreeSid", error
);
511 mono_security_win_protect_user (const gunichar2
*path
, MonoError
*error
)
513 mono_security_win_not_supported ("BuildTrusteeWithSid, SetEntriesInAcl, SetNamedSecurityInfo, LocalFree", error
);
516 #endif /* HAVE_API_SUPPORT_WIN32_SECURITY */
519 ves_icall_Mono_Security_Cryptography_KeyPairPersistence_CanSecure (const gunichar2
*root
)
523 /* ACL are nice... unless you have FAT or other uncivilized filesystem */
524 if (!GetVolumeInformationW (root
, NULL
, 0, NULL
, NULL
, &flags
, NULL
, 0))
526 return (flags
& FS_PERSISTENT_ACLS
) == FS_PERSISTENT_ACLS
;
530 ves_icall_Mono_Security_Cryptography_KeyPairPersistence_IsMachineProtected (const gunichar2
*path
)
532 /* no one, but the owner, should have write access to the directory */
534 MonoBoolean result
= (MonoBoolean
)mono_security_win_is_machine_protected (path
, error
);
535 mono_error_set_pending_exception (error
);
540 ves_icall_Mono_Security_Cryptography_KeyPairPersistence_IsUserProtected (const gunichar2
*path
)
542 /* no one, but the user, should have access to the directory */
544 MonoBoolean result
= (MonoBoolean
)mono_security_win_is_user_protected (path
, error
);
545 mono_error_set_pending_exception (error
);
550 ves_icall_Mono_Security_Cryptography_KeyPairPersistence_ProtectMachine (const gunichar2
*path
)
552 /* read/write to owner, read to everyone else */
554 MonoBoolean result
= (MonoBoolean
)mono_security_win_protect_machine (path
, error
);
555 mono_error_set_pending_exception (error
);
560 ves_icall_Mono_Security_Cryptography_KeyPairPersistence_ProtectUser (const gunichar2
*path
)
562 /* read/write to user, no access to everyone else */
564 MonoBoolean result
= (MonoBoolean
)mono_security_win_protect_user (path
, error
);
565 mono_error_set_pending_exception (error
);
570 #endif /* HOST_WIN32 */