mono-security-windows.c 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570
  1. /**
  2. * \file
  3. * Windows security support.
  4. *
  5. * Copyright 2016 Microsoft
  6. * Licensed under the MIT license. See LICENSE file in the project root for full license information.
  7. */
  8. #include <config.h>
  9. #include <glib.h>
  10. #if defined(HOST_WIN32)
  11. #include <winsock2.h>
  12. #include <windows.h>
  13. #include <mono/metadata/handle.h>
  14. #include <mono/utils/mono-error.h>
  15. #include <mono/utils/mono-error-internals.h>
  16. #include <mono/metadata/object-internals.h>
  17. #include <mono/utils/w32subset.h>
  18. #if HAVE_API_SUPPORT_WIN32_SECURITY
  19. #include <aclapi.h>
  20. #include <accctrl.h>
  21. #endif
  22. #ifndef PROTECTED_DACL_SECURITY_INFORMATION
  23. #define PROTECTED_DACL_SECURITY_INFORMATION 0x80000000L
  24. #endif
  25. #if HAVE_API_SUPPORT_WIN32_SECURITY
  26. static gunichar2*
  27. GetSidName (gunichar2 *server, PSID sid, gint32 *size)
  28. {
  29. gunichar2 *uniname = NULL;
  30. DWORD cchName = 0;
  31. DWORD cchDomain = 0;
  32. SID_NAME_USE peUse; /* out */
  33. LookupAccountSidW (server, sid, NULL, &cchName, NULL,
  34. &cchDomain, &peUse);
  35. if ((cchName > 0) && (cchDomain > 0)) {
  36. gunichar2 *user = g_malloc0 ((cchName + 1) * 2);
  37. gunichar2 *domain = g_malloc0 ((cchDomain + 1) * 2);
  38. LookupAccountSidW (server, sid, user, &cchName, domain,
  39. &cchDomain, &peUse);
  40. if (cchName > 0) {
  41. if (cchDomain > 0) {
  42. /* domain/machine name included (+ sepearator) */
  43. *size = cchName + cchDomain + 1;
  44. uniname = g_malloc0 ((*size + 1) * 2);
  45. memcpy (uniname, domain, cchDomain * 2);
  46. *(uniname + cchDomain) = '\\';
  47. memcpy (uniname + cchDomain + 1, user, cchName * 2);
  48. g_free (user);
  49. }
  50. else {
  51. /* no domain / machine */
  52. *size = cchName;
  53. uniname = user;
  54. }
  55. }
  56. else {
  57. /* nothing -> return NULL */
  58. g_free (user);
  59. }
  60. g_free (domain);
  61. }
  62. return uniname;
  63. }
  64. gpointer
  65. mono_security_principal_windows_identity_get_current_token (MonoError *error)
  66. {
  67. gpointer token = NULL;
  68. /* Note: This isn't a copy of the Token - we must not close it!!!
  69. * http://www.develop.com/kbrown/book/html/whatis_windowsprincipal.html
  70. */
  71. /* thread may be impersonating somebody */
  72. if (OpenThreadToken (GetCurrentThread (), MAXIMUM_ALLOWED, 1, &token) == 0) {
  73. /* if not take the process identity */
  74. OpenProcessToken (GetCurrentProcess (), MAXIMUM_ALLOWED, &token);
  75. }
  76. return token;
  77. }
  78. gpointer
  79. ves_icall_System_Security_Principal_WindowsIdentity_GetCurrentToken (MonoError *error)
  80. {
  81. return mono_security_principal_windows_identity_get_current_token (error);
  82. }
  83. gint32
  84. mono_security_win_get_token_name (gpointer token, gunichar2 ** uniname, MonoError *error)
  85. {
  86. gint32 size = 0;
  87. GetTokenInformation (token, TokenUser, NULL, size, (PDWORD)&size);
  88. if (size > 0) {
  89. TOKEN_USER *tu = g_malloc0 (size);
  90. if (GetTokenInformation (token, TokenUser, tu, size, (PDWORD)&size)) {
  91. *uniname = GetSidName (NULL, tu->User.Sid, &size);
  92. }
  93. g_free (tu);
  94. }
  95. return size;
  96. }
  97. #elif !HAVE_EXTERN_DEFINED_WIN32_SECURITY
  98. static void
  99. mono_security_win_not_supported (const char *functions, MonoError *error)
  100. {
  101. g_unsupported_api (functions);
  102. mono_error_set_not_supported (error, G_UNSUPPORTED_API, functions);
  103. SetLastError (ERROR_NOT_SUPPORTED);
  104. }
  105. gpointer
  106. mono_security_principal_windows_identity_get_current_token (MonoError *error)
  107. {
  108. mono_security_win_not_supported ("OpenThreadToken, OpenProcessToken", error);
  109. return NULL;
  110. }
  111. gpointer
  112. ves_icall_System_Security_Principal_WindowsIdentity_GetCurrentToken (MonoError *error)
  113. {
  114. return mono_security_principal_windows_identity_get_current_token (error);
  115. }
  116. gint32
  117. mono_security_win_get_token_name (gpointer token, gunichar2 **uniname, MonoError *error)
  118. {
  119. mono_security_win_not_supported ("GetTokenInformation", error);
  120. return 0;
  121. }
  122. #endif /* HAVE_API_SUPPORT_WIN32_SECURITY */
  123. MonoStringHandle
  124. ves_icall_System_Security_Principal_WindowsIdentity_GetTokenName (gpointer token, MonoError *error)
  125. {
  126. MonoStringHandle result;
  127. gunichar2 *uniname = NULL;
  128. gint32 size = 0;
  129. error_init (error);
  130. size = mono_security_win_get_token_name (token, &uniname, error);
  131. if (size == 0 && !is_ok (error))
  132. return NULL_HANDLE_STRING;
  133. if (size > 0) {
  134. result = mono_string_new_utf16_handle (mono_domain_get (), uniname, size, error);
  135. }
  136. else
  137. result = mono_string_new_handle (mono_domain_get (), "", error);
  138. if (uniname)
  139. g_free (uniname);
  140. return result;
  141. }
  142. gpointer
  143. ves_icall_System_Security_Principal_WindowsIdentity_GetUserToken (MonoStringHandle username, MonoError *error)
  144. {
  145. error_init (error);
  146. gpointer token = NULL;
  147. /* TODO: MS has something like this working in Windows 2003 (client and
  148. * server) but works only for domain accounts (so it's quite limiting).
  149. * http://www.develop.com/kbrown/book/html/howto_logonuser.html
  150. */
  151. g_warning ("Unsupported on Win32 (anyway requires W2K3 minimum)");
  152. return token;
  153. }
  154. #if HAVE_API_SUPPORT_WIN32_SECURITY
  155. MonoArrayHandle
  156. ves_icall_System_Security_Principal_WindowsIdentity_GetRoles (gpointer token, MonoError *error)
  157. {
  158. MonoArrayHandle array;
  159. MonoStringHandle str_h;
  160. MonoDomain *domain = mono_domain_get ();
  161. gint32 size = 0;
  162. gboolean created = FALSE;
  163. GetTokenInformation (token, TokenGroups, NULL, size, (PDWORD)&size);
  164. if (size > 0) {
  165. TOKEN_GROUPS *tg = g_malloc0 (size);
  166. if (GetTokenInformation (token, TokenGroups, tg, size, (PDWORD)&size)) {
  167. int i=0;
  168. int num = tg->GroupCount;
  169. array = mono_array_new_handle (domain, mono_get_string_class (), num, error);
  170. if (!is_ok (error)) {
  171. g_free (tg);
  172. return NULL_HANDLE_ARRAY;
  173. }
  174. created = TRUE;
  175. str_h = MONO_HANDLE_NEW (MonoString, NULL);
  176. for (i=0; i < num; i++) {
  177. gint32 size = 0;
  178. gunichar2 *uniname = GetSidName (NULL, tg->Groups [i].Sid, &size);
  179. if (uniname) {
  180. MonoString *str = mono_string_new_utf16_checked (domain, uniname, size, error);
  181. MONO_HANDLE_ASSIGN_RAW (str_h, str);
  182. if (!is_ok (error)) {
  183. g_free (uniname);
  184. g_free (tg);
  185. return NULL_HANDLE_ARRAY;
  186. }
  187. MONO_HANDLE_ARRAY_SETREF (array, i, str_h);
  188. g_free (uniname);
  189. }
  190. }
  191. }
  192. g_free (tg);
  193. }
  194. if (!created) {
  195. /* return empty array of string, i.e. string [0] */
  196. array = mono_array_new_handle (domain, mono_get_string_class (), 0, error);
  197. return_val_if_nok (error, NULL_HANDLE_ARRAY);
  198. }
  199. return array;
  200. }
  201. #elif !HAVE_EXTERN_DEFINED_WIN32_SECURITY
  202. MonoArray*
  203. ves_icall_System_Security_Principal_WindowsIdentity_GetRoles (gpointer token, MonoError *error)
  204. {
  205. mono_security_win_not_supported ("GetTokenInformation", error);
  206. mono_error_set_pending_exception (error);
  207. return NULL;
  208. }
  209. #endif /* HAVE_API_SUPPORT_WIN32_SECURITY */
  210. MonoBoolean
  211. ves_icall_System_Security_Principal_WindowsImpersonationContext_CloseToken (gpointer token, MonoError *error)
  212. {
  213. return !!CloseHandle (token);
  214. }
  215. #if HAVE_API_SUPPORT_WIN32_SECURITY
  216. gpointer
  217. ves_icall_System_Security_Principal_WindowsImpersonationContext_DuplicateToken (gpointer token, MonoError *error)
  218. {
  219. gpointer dupe = NULL;
  220. return DuplicateToken (token, SecurityImpersonation, &dupe) ? dupe : NULL;
  221. }
  222. #elif !HAVE_EXTERN_DEFINED_WIN32_SECURITY
  223. gpointer
  224. ves_icall_System_Security_Principal_WindowsImpersonationContext_DuplicateToken (gpointer token, MonoError *error)
  225. {
  226. mono_security_win_not_supported ("DuplicateToken", error);
  227. return NULL;
  228. }
  229. #endif /* HAVE_API_SUPPORT_WIN32_SECURITY */
  230. MonoBoolean
  231. ves_icall_System_Security_Principal_WindowsPrincipal_IsMemberOfGroupId (gpointer user, gpointer group, MonoError *error)
  232. {
  233. /* The convertion from an ID to a string is done in managed code for Windows */
  234. g_warning ("IsMemberOfGroupId should never be called on Win32");
  235. return FALSE;
  236. }
  237. MonoBoolean
  238. ves_icall_System_Security_Principal_WindowsPrincipal_IsMemberOfGroupName (gpointer user, const gchar *group, MonoError *error)
  239. {
  240. /* Windows version use a cache built using WindowsIdentity._GetRoles */
  241. g_warning ("IsMemberOfGroupName should never be called on Win32");
  242. return FALSE;
  243. }
  244. #if HAVE_API_SUPPORT_WIN32_SECURITY
  245. static PSID
  246. GetSidHelper (const SID_IDENTIFIER_AUTHORITY *authority, BYTE subAuthorityCount, DWORD subAuthority0, DWORD subAuthority1)
  247. {
  248. PSID pSid = NULL;
  249. // This SID must be freed with FreeSid ().
  250. return AllocateAndInitializeSid ((PSID_IDENTIFIER_AUTHORITY)authority, subAuthorityCount,
  251. subAuthority0, subAuthority1, 0/*2*/, 0/*3*/, 0/*4*/, 0/*5*/, 0/*6*/, 0/*7*/, &pSid) ? pSid : NULL;
  252. }
  253. static PSID
  254. GetAdministratorsSid (void)
  255. {
  256. const static SID_IDENTIFIER_AUTHORITY admins = { SECURITY_NT_AUTHORITY };
  257. // This SID must be freed with FreeSid ().
  258. return GetSidHelper (&admins, 2, SECURITY_BUILTIN_DOMAIN_RID, DOMAIN_ALIAS_RID_ADMINS);
  259. }
  260. static PSID
  261. GetEveryoneSid (void)
  262. {
  263. const static SID_IDENTIFIER_AUTHORITY everyone = { SECURITY_WORLD_SID_AUTHORITY };
  264. // This SID must be freed with FreeSid ().
  265. return GetSidHelper (&everyone, 1, SECURITY_WORLD_RID, 0);
  266. }
  267. static PSID
  268. GetCurrentUserSid (MonoError *error)
  269. {
  270. PSID sid = NULL;
  271. DWORD size = 0;
  272. gpointer token = mono_security_principal_windows_identity_get_current_token (error);
  273. GetTokenInformation (token, TokenUser, NULL, size, &size);
  274. if (size > 0) {
  275. TOKEN_USER *tu = g_malloc0 (size);
  276. if (GetTokenInformation (token, TokenUser, tu, size, &size)) {
  277. DWORD length = GetLengthSid (tu->User.Sid);
  278. sid = (PSID) g_malloc0 (length);
  279. if (!CopySid (length, sid, tu->User.Sid)) {
  280. g_free (sid);
  281. sid = NULL;
  282. }
  283. }
  284. g_free (tu);
  285. }
  286. /* Note: this SID must be freed with g_free () */
  287. return sid;
  288. }
  289. static ACCESS_MASK
  290. GetRightsFromSid (PSID sid, PACL acl)
  291. {
  292. ACCESS_MASK rights = 0;
  293. TRUSTEEW trustee;
  294. BuildTrusteeWithSidW (&trustee, sid);
  295. if (GetEffectiveRightsFromAclW (acl, &trustee, &rights) != ERROR_SUCCESS)
  296. return 0;
  297. return rights;
  298. }
  299. gboolean
  300. mono_security_win_is_machine_protected (const gunichar2 *path, MonoError *error)
  301. {
  302. gboolean success = FALSE;
  303. PACL pDACL = NULL;
  304. 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)
  307. return FALSE;
  308. /* We check that Everyone is still limited to READ-ONLY -
  309. but not if new entries have been added by an Administrator */
  310. PSID const pEveryoneSid = GetEveryoneSid ();
  311. if (pEveryoneSid) {
  312. ACCESS_MASK rights = GetRightsFromSid (pEveryoneSid, pDACL);
  313. /* http://msdn.microsoft.com/library/en-us/security/security/generic_access_rights.asp?frame=true */
  314. success = (rights == (READ_CONTROL | SYNCHRONIZE | FILE_READ_DATA | FILE_READ_EA | FILE_READ_ATTRIBUTES));
  315. FreeSid (pEveryoneSid);
  316. }
  317. /* Note: we don't need to check our own access -
  318. we'll know soon enough when reading the file */
  319. LocalFree (pSD);
  320. return success;
  321. }
  322. gboolean
  323. mono_security_win_is_user_protected (const gunichar2 *path, MonoError *error)
  324. {
  325. gboolean success = FALSE;
  326. PACL pDACL = NULL;
  327. PSECURITY_DESCRIPTOR pSecurityDescriptor = NULL;
  328. DWORD dwRes = GetNamedSecurityInfoW ((PWSTR)path, SE_FILE_OBJECT,
  329. DACL_SECURITY_INFORMATION, NULL, NULL, &pDACL, NULL, &pSecurityDescriptor);
  330. if (dwRes != ERROR_SUCCESS)
  331. return FALSE;
  332. /* We check that our original entries in the ACL are in place -
  333. but not if new entries have been added by the user */
  334. /* Everyone should be denied */
  335. PSID const pEveryoneSid = GetEveryoneSid ();
  336. if (pEveryoneSid) {
  337. ACCESS_MASK rights = GetRightsFromSid (pEveryoneSid, pDACL);
  338. success = (rights == 0);
  339. FreeSid (pEveryoneSid);
  340. }
  341. /* Note: we don't need to check our own access -
  342. we'll know soon enough when reading the file */
  343. LocalFree (pSecurityDescriptor);
  344. return success;
  345. }
  346. gboolean
  347. mono_security_win_protect_machine (const gunichar2 *path, MonoError *error)
  348. {
  349. PSID pEveryoneSid = GetEveryoneSid ();
  350. PSID pAdminsSid = GetAdministratorsSid ();
  351. DWORD retval = -1;
  352. if (pEveryoneSid && pAdminsSid) {
  353. PACL pDACL = NULL;
  354. EXPLICIT_ACCESSW ea [2];
  355. ZeroMemory (&ea, sizeof (ea));
  356. /* grant all access to the BUILTIN\Administrators group */
  357. BuildTrusteeWithSidW (&ea [0].Trustee, pAdminsSid);
  358. ea [0].grfAccessPermissions = GENERIC_ALL;
  359. ea [0].grfAccessMode = SET_ACCESS;
  360. ea [0].grfInheritance = SUB_CONTAINERS_AND_OBJECTS_INHERIT;
  361. ea [0].Trustee.TrusteeForm = TRUSTEE_IS_SID;
  362. ea [0].Trustee.TrusteeType = TRUSTEE_IS_WELL_KNOWN_GROUP;
  363. /* read-only access everyone */
  364. BuildTrusteeWithSidW (&ea [1].Trustee, pEveryoneSid);
  365. ea [1].grfAccessPermissions = GENERIC_READ;
  366. ea [1].grfAccessMode = SET_ACCESS;
  367. ea [1].grfInheritance = SUB_CONTAINERS_AND_OBJECTS_INHERIT;
  368. ea [1].Trustee.TrusteeForm = TRUSTEE_IS_SID;
  369. ea [1].Trustee.TrusteeType = TRUSTEE_IS_WELL_KNOWN_GROUP;
  370. retval = SetEntriesInAclW (2, ea, NULL, &pDACL);
  371. if (retval == ERROR_SUCCESS) {
  372. /* with PROTECTED_DACL_SECURITY_INFORMATION we */
  373. /* remove any existing ACL (like inherited ones) */
  374. retval = SetNamedSecurityInfoW ((PWSTR)path, SE_FILE_OBJECT,
  375. DACL_SECURITY_INFORMATION | PROTECTED_DACL_SECURITY_INFORMATION,
  376. NULL, NULL, pDACL, NULL);
  377. }
  378. LocalFree (pDACL);
  379. }
  380. if (pEveryoneSid)
  381. FreeSid (pEveryoneSid);
  382. if (pAdminsSid)
  383. FreeSid (pAdminsSid);
  384. return retval == ERROR_SUCCESS;
  385. }
  386. gboolean
  387. mono_security_win_protect_user (const gunichar2 *path, MonoError *error)
  388. {
  389. DWORD retval = -1;
  390. PSID const pCurrentSid = GetCurrentUserSid (error);
  391. if (pCurrentSid) {
  392. PACL pDACL = NULL;
  393. EXPLICIT_ACCESSW ea;
  394. ZeroMemory (&ea, sizeof (EXPLICIT_ACCESS));
  395. /* grant exclusive access to the current user */
  396. BuildTrusteeWithSidW (&ea.Trustee, pCurrentSid);
  397. ea.grfAccessPermissions = GENERIC_ALL;
  398. ea.grfAccessMode = SET_ACCESS;
  399. ea.grfInheritance = SUB_CONTAINERS_AND_OBJECTS_INHERIT;
  400. ea.Trustee.TrusteeForm = TRUSTEE_IS_SID;
  401. ea.Trustee.TrusteeType = TRUSTEE_IS_USER;
  402. retval = SetEntriesInAclW (1, &ea, NULL, &pDACL);
  403. if (retval == ERROR_SUCCESS) {
  404. /* with PROTECTED_DACL_SECURITY_INFORMATION we
  405. remove any existing ACL (like inherited ones) */
  406. retval = SetNamedSecurityInfoW ((PWSTR)path, SE_FILE_OBJECT,
  407. DACL_SECURITY_INFORMATION | PROTECTED_DACL_SECURITY_INFORMATION,
  408. NULL, NULL, pDACL, NULL);
  409. }
  410. LocalFree (pDACL);
  411. g_free (pCurrentSid); /* g_malloc0 */
  412. }
  413. return retval == ERROR_SUCCESS;
  414. }
  415. #elif !HAVE_EXTERN_DEFINED_WIN32_SECURITY
  416. gboolean
  417. mono_security_win_is_machine_protected (const gunichar2 *path, MonoError *error)
  418. {
  419. mono_security_win_not_supported ("GetNamedSecurityInfo, LocalFree", error);
  420. return FALSE;
  421. }
  422. gboolean
  423. mono_security_win_is_user_protected (const gunichar2 *path, MonoError *error)
  424. {
  425. mono_security_win_not_supported ("GetNamedSecurityInfo, LocalFree", error);
  426. return FALSE;
  427. }
  428. gboolean
  429. mono_security_win_protect_machine (const gunichar2 *path, MonoError *error)
  430. {
  431. mono_security_win_not_supported ("BuildTrusteeWithSid, SetEntriesInAcl, SetNamedSecurityInfo, LocalFree, FreeSid", error);
  432. return FALSE;
  433. }
  434. gboolean
  435. mono_security_win_protect_user (const gunichar2 *path, MonoError *error)
  436. {
  437. mono_security_win_not_supported ("BuildTrusteeWithSid, SetEntriesInAcl, SetNamedSecurityInfo, LocalFree", error);
  438. return FALSE;
  439. }
  440. #endif /* HAVE_API_SUPPORT_WIN32_SECURITY */
  441. MonoBoolean
  442. ves_icall_Mono_Security_Cryptography_KeyPairPersistence_CanSecure (const gunichar2 *root)
  443. {
  444. DWORD flags = 0;
  445. /* ACL are nice... unless you have FAT or other uncivilized filesystem */
  446. if (!GetVolumeInformationW (root, NULL, 0, NULL, NULL, &flags, NULL, 0))
  447. return FALSE;
  448. return (flags & FS_PERSISTENT_ACLS) == FS_PERSISTENT_ACLS;
  449. }
  450. MonoBoolean
  451. ves_icall_Mono_Security_Cryptography_KeyPairPersistence_IsMachineProtected (const gunichar2 *path)
  452. {
  453. /* no one, but the owner, should have write access to the directory */
  454. ERROR_DECL (error);
  455. MonoBoolean result = (MonoBoolean)mono_security_win_is_machine_protected (path, error);
  456. mono_error_set_pending_exception (error);
  457. return result;
  458. }
  459. MonoBoolean
  460. ves_icall_Mono_Security_Cryptography_KeyPairPersistence_IsUserProtected (const gunichar2 *path)
  461. {
  462. /* no one, but the user, should have access to the directory */
  463. ERROR_DECL (error);
  464. MonoBoolean result = (MonoBoolean)mono_security_win_is_user_protected (path, error);
  465. mono_error_set_pending_exception (error);
  466. return result;
  467. }
  468. MonoBoolean
  469. ves_icall_Mono_Security_Cryptography_KeyPairPersistence_ProtectMachine (const gunichar2 *path)
  470. {
  471. /* read/write to owner, read to everyone else */
  472. ERROR_DECL (error);
  473. MonoBoolean result = (MonoBoolean)mono_security_win_protect_machine (path, error);
  474. mono_error_set_pending_exception (error);
  475. return result;
  476. }
  477. MonoBoolean
  478. ves_icall_Mono_Security_Cryptography_KeyPairPersistence_ProtectUser (const gunichar2 *path)
  479. {
  480. /* read/write to user, no access to everyone else */
  481. ERROR_DECL (error);
  482. MonoBoolean result = (MonoBoolean)mono_security_win_protect_user (path, error);
  483. mono_error_set_pending_exception (error);
  484. return result;
  485. }
  486. #endif /* HOST_WIN32 */