2008-07-11 Rodrigo Kumpera <rkumpera@novell.com>
[mono.git] / mono / metadata / security.c
1 /*
2  * security.c:  Security internal calls
3  *
4  * Author:
5  *      Sebastien Pouliot  <sebastien@ximian.com>
6  *
7  * (C) 2004 Novell (http://www.novell.com)
8  */
9
10 #ifdef HAVE_CONFIG_H
11 #include <config.h>
12 #endif
13
14 #include <mono/metadata/assembly.h>
15 #include <mono/metadata/appdomain.h>
16 #include <mono/metadata/image.h>
17 #include <mono/metadata/exception.h>
18 #include <mono/metadata/object-internals.h>
19 #include <mono/metadata/metadata-internals.h>
20 #include <mono/metadata/security.h>
21 #include <mono/io-layer/io-layer.h>
22 #include <mono/utils/strenc.h>
23
24 #ifdef PLATFORM_WIN32
25
26 #include <aclapi.h>
27 #include <accctrl.h>
28
29 #ifndef PROTECTED_DACL_SECURITY_INFORMATION
30 #define PROTECTED_DACL_SECURITY_INFORMATION     0x80000000L
31 #endif
32
33 #else
34
35 #include <config.h>
36 #include <grp.h>
37 #include <pwd.h>
38 #include <string.h>
39 #include <sys/stat.h>
40 #include <sys/types.h>
41 #include <unistd.h>
42
43 /* Disclaimers */
44
45 #if defined(__GNUC__)
46
47 #ifndef HAVE_GETGRGID_R
48         #warning Non-thread safe getgrgid being used!
49 #endif
50 #ifndef HAVE_GETGRNAM_R
51         #warning Non-thread safe getgrnam being used!
52 #endif
53 #ifndef HAVE_GETPWNAM_R
54         #warning Non-thread safe getpwnam being used!
55 #endif
56 #ifndef HAVE_GETPWUID_R
57         #warning Non-thread safe getpwuid being used!
58 #endif
59
60 #endif /* defined(__GNUC__) */
61
62 #endif /* not PLATFORM_WIN32 */
63
64
65 /* internal functions - reuse driven */
66
67 #ifdef PLATFORM_WIN32
68
69 /* ask a server to translate a SID into a textual representation */
70 static gunichar2*
71 GetSidName (gunichar2 *server, PSID sid, gint32 *size) 
72 {
73         gunichar2 *uniname = NULL;
74         DWORD cchName = 0;
75         DWORD cchDomain = 0;
76         SID_NAME_USE peUse; /* out */
77
78         LookupAccountSid (server, sid, NULL, &cchName, NULL, 
79                 &cchDomain, &peUse); 
80         
81         if ((cchName > 0) && (cchDomain > 0)) {
82                 gunichar2 *user = g_malloc0 ((cchName + 1) * 2);
83                 gunichar2 *domain = g_malloc0 ((cchDomain + 1) * 2);
84
85                 LookupAccountSid (server, sid, user, &cchName, domain,
86                         &cchDomain, &peUse);
87
88                 if (cchName > 0) {
89                         if (cchDomain > 0) {
90                                 /* domain/machine name included (+ sepearator) */
91                                 *size = cchName + cchDomain + 1;
92                                 uniname = g_malloc0 ((*size + 1) * 2);
93                                 memcpy (uniname, domain, cchDomain * 2);
94                                 *(uniname + cchDomain) = '\\';
95                                 memcpy (uniname + cchDomain + 1, user, cchName * 2);
96                                 g_free (user);
97                         }
98                         else {
99                                 /* no domain / machine */
100                                 *size = cchName;
101                                 uniname = user;
102                         }
103                 }
104                 else {
105                         /* nothing -> return NULL */
106                         g_free (user);
107                 }
108
109                 g_free (domain);
110         }
111
112         return uniname;
113 }
114
115
116 #else /* not PLATFORM_WIN32 */
117
118 #define MONO_SYSCONF_DEFAULT_SIZE       ((size_t) 1024)
119
120 /*
121  * Ensure we always get a valid (usable) value from sysconf.
122  * In case of error, we return the default value.
123  */
124 static size_t mono_sysconf (int name)
125 {
126         size_t size = (size_t) sysconf (name);
127         /* default value */
128         return (size == -1) ? MONO_SYSCONF_DEFAULT_SIZE : size;
129 }
130
131
132 static gchar*
133 GetTokenName (uid_t uid)
134 {
135         gchar *uname = NULL;
136
137 #ifdef HAVE_GETPWUID_R
138         struct passwd pwd;
139         size_t fbufsize;
140         gchar *fbuf;
141         gint32 retval;
142 #endif
143         struct passwd *p = NULL;
144         gboolean result;
145
146 #ifdef HAVE_GETPWUID_R
147 #ifdef _SC_GETPW_R_SIZE_MAX
148         fbufsize = mono_sysconf (_SC_GETPW_R_SIZE_MAX);
149 #else
150         fbufsize = MONO_SYSCONF_DEFAULT_SIZE;
151 #endif
152         fbuf = g_malloc0 (fbufsize);
153         retval = getpwuid_r (uid, &pwd, fbuf, fbufsize, &p);
154         result = ((retval == 0) && (p == &pwd));
155 #else
156         /* default to non thread-safe but posix compliant function */
157         p = getpwuid (uid);
158         result = (p != NULL);
159 #endif
160
161         if (result) {
162                 uname = g_strdup (p->pw_name);
163         }
164
165 #ifdef HAVE_GETPWUID_R
166         g_free (fbuf);
167 #endif
168
169         return uname;
170 }
171
172
173 static gboolean
174 IsMemberInList (uid_t user, struct group *g) 
175 {
176         gboolean result = FALSE;
177         gchar *utf8_username = GetTokenName (user);
178
179         if (!utf8_username)
180                 return FALSE;
181
182         if (g) {
183                 gchar **users = g->gr_mem;
184
185                 while (*users) {
186                         gchar *u = *(users);
187                         if (strcmp (utf8_username, u) == 0) {
188                                 result = TRUE;
189                                 break;
190                         }
191                         users++;
192                 }
193         }               
194
195         g_free (utf8_username);
196         return result;
197 }
198
199
200 static gboolean
201 IsDefaultGroup (uid_t user, gid_t group)
202 {
203 #ifdef HAVE_GETPWUID_R
204         struct passwd pwd;
205         size_t fbufsize;
206         gchar *fbuf;
207         gint32 retval;
208 #endif
209         struct passwd *p = NULL;
210         gboolean result;
211
212 #ifdef HAVE_GETPWUID_R
213 #ifdef _SC_GETPW_R_SIZE_MAX
214         fbufsize = mono_sysconf (_SC_GETPW_R_SIZE_MAX);
215 #else
216         fbufsize = MONO_SYSCONF_DEFAULT_SIZE;
217 #endif
218
219         fbuf = g_malloc0 (fbufsize);
220         retval = getpwuid_r (user, &pwd, fbuf, fbufsize, &p);
221         result = ((retval == 0) && (p == &pwd));
222 #else
223         /* default to non thread-safe but posix compliant function */
224         p = getpwuid (user);
225         result = (p != NULL);
226 #endif
227
228         if (result) {
229                 result = (p->pw_gid == group);
230         }
231
232 #ifdef HAVE_GETPWUID_R
233         g_free (fbuf);
234 #endif
235
236         return result;
237 }
238
239
240 static gboolean
241 IsMemberOf (gid_t user, struct group *g) 
242 {
243         if (!g)
244                 return FALSE;
245
246         /* is it the user default group */
247         if (IsDefaultGroup (user, g->gr_gid))
248                 return TRUE;
249
250         /* is the user in the group list */
251         return IsMemberInList (user, g);
252 }
253
254 #endif
255
256
257 /* ICALLS */
258
259
260 /* System.Environment */
261
262
263 MonoString*
264 ves_icall_System_Environment_get_UserName (void)
265 {
266         MONO_ARCH_SAVE_REGS;
267
268         /* using glib is more portable */
269         return mono_string_new (mono_domain_get (), g_get_user_name ());
270 }
271
272
273 /* System.Security.Principal.WindowsIdentity */
274
275
276 gpointer
277 ves_icall_System_Security_Principal_WindowsIdentity_GetCurrentToken (void)
278 {
279         gpointer token = NULL;
280
281         MONO_ARCH_SAVE_REGS;
282
283 #ifdef PLATFORM_WIN32
284         /* Note: This isn't a copy of the Token - we must not close it!!!
285          * http://www.develop.com/kbrown/book/html/whatis_windowsprincipal.html
286          */
287
288         /* thread may be impersonating somebody */
289         if (OpenThreadToken (GetCurrentThread (), TOKEN_QUERY, 1, &token) == 0) {
290                 /* if not take the process identity */
291                 OpenProcessToken (GetCurrentProcess (), TOKEN_QUERY, &token);
292         }
293 #else
294         token = GINT_TO_POINTER (geteuid ());
295 #endif
296         return token;
297 }
298
299
300 MonoString*
301 ves_icall_System_Security_Principal_WindowsIdentity_GetTokenName (gpointer token)
302 {
303         MonoString *result = NULL;
304         gunichar2 *uniname = NULL;
305         gint32 size = 0;
306
307 #ifdef PLATFORM_WIN32
308         MONO_ARCH_SAVE_REGS;
309
310         GetTokenInformation (token, TokenUser, NULL, size, (PDWORD)&size);
311         if (size > 0) {
312                 TOKEN_USER *tu = g_malloc0 (size);
313                 if (GetTokenInformation (token, TokenUser, tu, size, (PDWORD)&size)) {
314                         uniname = GetSidName (NULL, tu->User.Sid, &size);
315                 }
316                 g_free (tu);
317         }
318 #else 
319         gchar *uname = GetTokenName ((uid_t) GPOINTER_TO_INT (token));
320
321         MONO_ARCH_SAVE_REGS;
322
323         if (uname) {
324                 size = strlen (uname);
325                 uniname = g_utf8_to_utf16 (uname, size, NULL, NULL, NULL);
326                 g_free (uname);
327         }
328 #endif /* PLATFORM_WIN32 */
329
330         if (size > 0) {
331                 result = mono_string_new_utf16 (mono_domain_get (), uniname, size);
332         }
333         else
334                 result = mono_string_new (mono_domain_get (), "");
335
336         if (uniname)
337                 g_free (uniname);
338
339         return result;
340 }
341
342
343 gpointer
344 ves_icall_System_Security_Principal_WindowsIdentity_GetUserToken (MonoString *username)
345 {
346 #ifdef PLATFORM_WIN32
347         gpointer token = NULL;
348
349         MONO_ARCH_SAVE_REGS;
350
351         /* TODO: MS has something like this working in Windows 2003 (client and
352          * server) but works only for domain accounts (so it's quite limiting).
353          * http://www.develop.com/kbrown/book/html/howto_logonuser.html
354          */
355         g_warning ("Unsupported on Win32 (anyway requires W2K3 minimum)");
356
357 #else /* PLATFORM_WIN32*/
358
359 #ifdef HAVE_GETPWNAM_R
360         struct passwd pwd;
361         size_t fbufsize;
362         gchar *fbuf;
363         gint32 retval;
364 #endif
365         gpointer token = (gpointer) -2;
366         struct passwd *p;
367         gchar *utf8_name;
368         gboolean result;
369
370         MONO_ARCH_SAVE_REGS;
371
372         utf8_name = mono_unicode_to_external (mono_string_chars (username));
373
374 #ifdef HAVE_GETPWNAM_R
375 #ifdef _SC_GETPW_R_SIZE_MAX
376         fbufsize = mono_sysconf (_SC_GETPW_R_SIZE_MAX);
377 #else
378         fbufsize = MONO_SYSCONF_DEFAULT_SIZE;
379 #endif
380
381         fbuf = g_malloc0 (fbufsize);
382         retval = getpwnam_r (utf8_name, &pwd, fbuf, fbufsize, &p);
383         result = ((retval == 0) && (p == &pwd));
384 #else
385         /* default to non thread-safe but posix compliant function */
386         p = getpwnam (utf8_name);
387         result = (p != NULL);
388 #endif
389
390         if (result) {
391                 token = GINT_TO_POINTER (p->pw_uid);
392         }
393
394 #ifdef HAVE_GETPWNAM_R
395         g_free (fbuf);
396 #endif
397         g_free (utf8_name);
398 #endif
399         return token;
400 }
401
402
403 /* http://www.dotnet247.com/247reference/msgs/39/195403.aspx
404 // internal static string[] WindowsIdentity._GetRoles (IntPtr token)
405 */
406 MonoArray*
407 ves_icall_System_Security_Principal_WindowsIdentity_GetRoles (gpointer token) 
408 {
409         MonoArray *array = NULL;
410         MonoDomain *domain = mono_domain_get (); 
411 #ifdef PLATFORM_WIN32
412         gint32 size = 0;
413
414         MONO_ARCH_SAVE_REGS;
415
416         GetTokenInformation (token, TokenGroups, NULL, size, (PDWORD)&size);
417         if (size > 0) {
418                 TOKEN_GROUPS *tg = g_malloc0 (size);
419                 if (GetTokenInformation (token, TokenGroups, tg, size, (PDWORD)&size)) {
420                         int i=0;
421                         int num = tg->GroupCount;
422
423                         array = mono_array_new (domain, mono_get_string_class (), num);
424
425                         for (i=0; i < num; i++) {
426                                 gint32 size = 0;
427                                 gunichar2 *uniname = GetSidName (NULL, tg->Groups [i].Sid, &size);
428
429                                 if (uniname) {
430                                         MonoString *str = mono_string_new_utf16 (domain, uniname, size);
431                                         mono_array_setref (array, i, str);
432                                         g_free (uniname);
433                                 }
434                         }
435                 }
436                 g_free (tg);
437         }
438 #else
439         /* POSIX-compliant systems should use IsMemberOfGroupId or IsMemberOfGroupName */
440         g_warning ("WindowsIdentity._GetRoles should never be called on POSIX");
441 #endif
442         if (!array) {
443                 /* return empty array of string, i.e. string [0] */
444                 array = mono_array_new (domain, mono_get_string_class (), 0);
445         }
446         return array;
447 }
448
449
450 /* System.Security.Principal.WindowsImpersonationContext */
451
452
453 gboolean
454 ves_icall_System_Security_Principal_WindowsImpersonationContext_CloseToken (gpointer token)
455 {
456         gboolean result = TRUE;
457
458         MONO_ARCH_SAVE_REGS;
459
460 #ifdef PLATFORM_WIN32
461         result = (CloseHandle (token) != 0);
462 #endif
463         return result;
464 }
465
466
467 gpointer
468 ves_icall_System_Security_Principal_WindowsImpersonationContext_DuplicateToken (gpointer token)
469 {
470         gpointer dupe = NULL;
471
472         MONO_ARCH_SAVE_REGS;
473
474 #ifdef PLATFORM_WIN32
475         if (DuplicateToken (token, SecurityImpersonation, &dupe) == 0) {
476                 dupe = NULL;
477         }
478 #else
479         dupe = token;
480 #endif
481         return dupe;
482 }
483
484
485 gboolean
486 ves_icall_System_Security_Principal_WindowsImpersonationContext_SetCurrentToken (gpointer token)
487 {
488         MONO_ARCH_SAVE_REGS;
489
490         /* Posix version implemented in /mono/mono/io-layer/security.c */
491         return (ImpersonateLoggedOnUser (token) != 0);
492 }
493
494
495 gboolean
496 ves_icall_System_Security_Principal_WindowsImpersonationContext_RevertToSelf (void)
497 {
498         MONO_ARCH_SAVE_REGS;
499
500         /* Posix version implemented in /mono/mono/io-layer/security.c */
501         return (RevertToSelf () != 0);
502 }
503
504
505 /* System.Security.Principal.WindowsPrincipal */
506
507 gboolean
508 ves_icall_System_Security_Principal_WindowsPrincipal_IsMemberOfGroupId (gpointer user, gpointer group)
509 {
510         gboolean result = FALSE;
511
512 #ifdef PLATFORM_WIN32
513         MONO_ARCH_SAVE_REGS;
514
515         /* The convertion from an ID to a string is done in managed code for Windows */
516         g_warning ("IsMemberOfGroupId should never be called on Win32");
517
518 #else /* PLATFORM_WIN32 */
519
520 #ifdef HAVE_GETGRGID_R
521         struct group grp;
522         size_t fbufsize;
523         gchar *fbuf;
524         gint32 retval;
525 #endif
526         struct group *g = NULL;
527
528         MONO_ARCH_SAVE_REGS;
529
530 #ifdef HAVE_GETGRGID_R
531 #ifdef _SC_GETGR_R_SIZE_MAX
532         fbufsize = mono_sysconf (_SC_GETGR_R_SIZE_MAX);
533 #else
534         fbufsize = MONO_SYSCONF_DEFAULT_SIZE;
535 #endif
536         fbuf = g_malloc0 (fbufsize);
537         retval = getgrgid_r ((gid_t) GPOINTER_TO_INT (group), &grp, fbuf, fbufsize, &g);
538         result = ((retval == 0) && (g == &grp));
539 #else
540         /* default to non thread-safe but posix compliant function */
541         g = getgrgid ((gid_t) GPOINTER_TO_INT (group));
542         result = (g != NULL);
543 #endif
544
545         if (result) {
546                 result = IsMemberOf ((uid_t) GPOINTER_TO_INT (user), g);
547         }
548
549 #ifdef HAVE_GETGRGID_R
550         g_free (fbuf);
551 #endif
552
553 #endif /* PLATFORM_WIN32 */
554
555         return result;
556 }
557
558
559 gboolean
560 ves_icall_System_Security_Principal_WindowsPrincipal_IsMemberOfGroupName (gpointer user, MonoString *group)
561 {
562         gboolean result = FALSE;
563
564 #ifdef PLATFORM_WIN32
565
566         MONO_ARCH_SAVE_REGS;
567
568         /* Windows version use a cache built using WindowsIdentity._GetRoles */
569         g_warning ("IsMemberOfGroupName should never be called on Win32");
570
571 #else /* PLATFORM_WIN32 */
572         gchar *utf8_groupname;
573
574         MONO_ARCH_SAVE_REGS;
575
576         utf8_groupname = mono_unicode_to_external (mono_string_chars (group));
577         if (utf8_groupname) {
578                 struct group *g = NULL;
579 #ifdef HAVE_GETGRNAM_R
580                 struct group grp;
581                 gchar *fbuf;
582                 gint32 retval;
583 #ifdef _SC_GETGR_R_SIZE_MAX
584                 size_t fbufsize = mono_sysconf (_SC_GETGR_R_SIZE_MAX);
585 #else
586                 size_t fbufsize = MONO_SYSCONF_DEFAULT_SIZE;
587 #endif
588                 fbuf = g_malloc0 (fbufsize);
589                 retval = getgrnam_r (utf8_groupname, &grp, fbuf, fbufsize, &g);
590                 result = ((retval == 0) && (g == &grp));
591 #else
592                 /* default to non thread-safe but posix compliant function */
593                 g = getgrnam (utf8_groupname);
594                 result = (g != NULL);
595 #endif
596
597                 if (result) {
598                         result = IsMemberOf ((uid_t) GPOINTER_TO_INT (user), g);
599                 }
600
601 #ifdef HAVE_GETGRNAM_R
602                 g_free (fbuf);
603 #endif
604                 g_free (utf8_groupname);
605         }
606 #endif /* PLATFORM_WIN32 */
607
608         return result;
609 }
610
611
612 /* Mono.Security.Cryptography IO related internal calls */
613
614 #ifdef PLATFORM_WIN32
615
616 static PSID
617 GetAdministratorsSid (void) 
618 {
619         SID_IDENTIFIER_AUTHORITY admins = SECURITY_NT_AUTHORITY;
620         PSID pSid = NULL;
621         if (!AllocateAndInitializeSid (&admins, 2, SECURITY_BUILTIN_DOMAIN_RID, 
622                 DOMAIN_ALIAS_RID_ADMINS, 0, 0, 0, 0, 0, 0, &pSid)) 
623                 return NULL;
624         /* Note: this SID must be freed with FreeSid () */
625         return pSid;
626 }
627
628
629 static PSID
630 GetEveryoneSid (void)
631 {
632         SID_IDENTIFIER_AUTHORITY everyone = SECURITY_WORLD_SID_AUTHORITY;
633         PSID pSid = NULL;
634         if (!AllocateAndInitializeSid (&everyone, 1, SECURITY_WORLD_RID, 0, 0, 0, 0, 0, 0, 0, &pSid))
635                 return NULL;
636         /* Note: this SID must be freed with FreeSid () */
637         return pSid;
638 }
639
640
641 static PSID
642 GetCurrentUserSid (void) 
643 {
644         PSID sid = NULL;
645         guint32 size = 0;
646         gpointer token = ves_icall_System_Security_Principal_WindowsIdentity_GetCurrentToken ();
647
648         GetTokenInformation (token, TokenUser, NULL, size, (PDWORD)&size);
649         if (size > 0) {
650                 TOKEN_USER *tu = g_malloc0 (size);
651                 if (GetTokenInformation (token, TokenUser, tu, size, (PDWORD)&size)) {
652                         DWORD length = GetLengthSid (tu->User.Sid);
653                         sid = (PSID) g_malloc0 (length);
654                         if (!CopySid (length, sid, tu->User.Sid)) {
655                                 g_free (sid);
656                                 sid = NULL;
657                         }
658                 }
659                 g_free (tu);
660         }
661         /* Note: this SID must be freed with g_free () */
662         return sid;
663 }
664
665
666 static ACCESS_MASK
667 GetRightsFromSid (PSID sid, PACL acl) 
668 {
669         ACCESS_MASK rights = 0;
670         TRUSTEE trustee;
671
672         BuildTrusteeWithSidW (&trustee, sid);
673         if (GetEffectiveRightsFromAcl (acl, &trustee, &rights) != ERROR_SUCCESS)
674                 return 0;
675
676         return rights;
677 }
678
679
680 static gboolean 
681 IsMachineProtected (gunichar2 *path)
682 {
683         gboolean success = FALSE;
684         PACL pDACL = NULL;
685         PSID pEveryoneSid = NULL;
686
687         DWORD dwRes = GetNamedSecurityInfoW (path, SE_FILE_OBJECT, DACL_SECURITY_INFORMATION, NULL, NULL, &pDACL, NULL, NULL);
688         if (dwRes != ERROR_SUCCESS)
689                 return FALSE;
690
691         /* We check that Everyone is still limited to READ-ONLY -
692         but not if new entries have been added by an Administrator */
693
694         pEveryoneSid = GetEveryoneSid ();
695         if (pEveryoneSid) {
696                 ACCESS_MASK rights = GetRightsFromSid (pEveryoneSid, pDACL);
697                 /* http://msdn.microsoft.com/library/en-us/security/security/generic_access_rights.asp?frame=true */
698                 success = (rights == (READ_CONTROL | SYNCHRONIZE | FILE_READ_DATA | FILE_READ_EA | FILE_READ_ATTRIBUTES));
699                 FreeSid (pEveryoneSid);
700         }
701         /* Note: we don't need to check our own access - 
702         we'll know soon enough when reading the file */
703
704         if (pDACL)
705                 LocalFree (pDACL);
706
707         return success;
708 }
709
710
711 static gboolean 
712 IsUserProtected (gunichar2 *path)
713 {
714         gboolean success = FALSE;
715         PACL pDACL = NULL;
716         PSID pEveryoneSid = NULL;
717
718         DWORD dwRes = GetNamedSecurityInfoW (path, SE_FILE_OBJECT, 
719                 DACL_SECURITY_INFORMATION, NULL, NULL, &pDACL, NULL, NULL);
720         if (dwRes != ERROR_SUCCESS)
721                 return FALSE;
722
723         /* We check that our original entries in the ACL are in place -
724         but not if new entries have been added by the user */
725
726         /* Everyone should be denied */
727         pEveryoneSid = GetEveryoneSid ();
728         if (pEveryoneSid) {
729                 ACCESS_MASK rights = GetRightsFromSid (pEveryoneSid, pDACL);
730                 success = (rights == 0);
731                 FreeSid (pEveryoneSid);
732         }
733         /* Note: we don't need to check our own access - 
734         we'll know soon enough when reading the file */
735
736         if (pDACL)
737                 LocalFree (pDACL);
738
739         return success;
740 }
741
742
743 static gboolean 
744 ProtectMachine (gunichar2 *path)
745 {
746         PSID pEveryoneSid = GetEveryoneSid ();
747         PSID pAdminsSid = GetAdministratorsSid ();
748         DWORD retval = -1;
749
750         if (pEveryoneSid && pAdminsSid) {
751                 PACL pDACL = NULL;
752                 EXPLICIT_ACCESS ea [2];
753                 ZeroMemory (&ea, 2 * sizeof (EXPLICIT_ACCESS));
754
755                 /* grant all access to the BUILTIN\Administrators group */
756                 BuildTrusteeWithSidW (&ea [0].Trustee, pAdminsSid);
757                 ea [0].grfAccessPermissions = GENERIC_ALL;
758                 ea [0].grfAccessMode = SET_ACCESS;
759                 ea [0].grfInheritance = SUB_CONTAINERS_AND_OBJECTS_INHERIT;
760                 ea [0].Trustee.TrusteeForm = TRUSTEE_IS_SID;
761                 ea [0].Trustee.TrusteeType = TRUSTEE_IS_WELL_KNOWN_GROUP;
762
763                 /* read-only access everyone */
764                 BuildTrusteeWithSidW (&ea [1].Trustee, pEveryoneSid);
765                 ea [1].grfAccessPermissions = GENERIC_READ;
766                 ea [1].grfAccessMode = SET_ACCESS;
767                 ea [1].grfInheritance = SUB_CONTAINERS_AND_OBJECTS_INHERIT;
768                 ea [1].Trustee.TrusteeForm = TRUSTEE_IS_SID;
769                 ea [1].Trustee.TrusteeType = TRUSTEE_IS_WELL_KNOWN_GROUP;
770
771                 retval = SetEntriesInAcl (2, ea, NULL, &pDACL);
772                 if (retval == ERROR_SUCCESS) {
773                         /* with PROTECTED_DACL_SECURITY_INFORMATION we */
774                         /* remove any existing ACL (like inherited ones) */
775                         retval = SetNamedSecurityInfo (path, SE_FILE_OBJECT, 
776                                 DACL_SECURITY_INFORMATION | PROTECTED_DACL_SECURITY_INFORMATION,
777                                 NULL, NULL, pDACL, NULL);
778                 }
779                 if (pDACL)
780                         LocalFree (pDACL);
781         }
782
783         if (pEveryoneSid)
784                 FreeSid (pEveryoneSid);
785         if (pAdminsSid)
786                 FreeSid (pAdminsSid);
787         return (retval == ERROR_SUCCESS);
788 }
789
790
791 static gboolean 
792 ProtectUser (gunichar2 *path)
793 {
794         DWORD retval = -1;
795
796         PSID pCurrentSid = GetCurrentUserSid ();
797         if (pCurrentSid) {
798                 PACL pDACL = NULL;
799                 EXPLICIT_ACCESS ea;
800                 ZeroMemory (&ea, sizeof (EXPLICIT_ACCESS));
801
802                 /* grant exclusive access to the current user */
803                 BuildTrusteeWithSidW (&ea.Trustee, pCurrentSid);
804                 ea.grfAccessPermissions = GENERIC_ALL;
805                 ea.grfAccessMode = SET_ACCESS;
806                 ea.grfInheritance = SUB_CONTAINERS_AND_OBJECTS_INHERIT;
807                 ea.Trustee.TrusteeForm = TRUSTEE_IS_SID;
808                 ea.Trustee.TrusteeType = TRUSTEE_IS_USER;
809
810                 retval = SetEntriesInAcl (1, &ea, NULL, &pDACL);
811                 if (retval == ERROR_SUCCESS) {
812                         /* with PROTECTED_DACL_SECURITY_INFORMATION we
813                            remove any existing ACL (like inherited ones) */
814                         retval = SetNamedSecurityInfo (path, SE_FILE_OBJECT, 
815                                 DACL_SECURITY_INFORMATION | PROTECTED_DACL_SECURITY_INFORMATION,
816                                 NULL, NULL, pDACL, NULL);
817                 }
818
819                 if (pDACL)
820                         LocalFree (pDACL);
821                 g_free (pCurrentSid); /* g_malloc0 */
822         }
823
824         return (retval == ERROR_SUCCESS);
825 }
826
827 #else
828
829 static gboolean 
830 IsProtected (MonoString *path, gint32 protection) 
831 {
832         gboolean result = FALSE;
833         gchar *utf8_name = mono_unicode_to_external (mono_string_chars (path));
834         if (utf8_name) {
835                 struct stat st;
836                 if (stat (utf8_name, &st) == 0) {
837                         result = (((st.st_mode & 0777) & protection) == 0);
838                 }
839                 g_free (utf8_name);
840         }
841         return result;
842 }
843
844
845 static gboolean 
846 Protect (MonoString *path, gint32 file_mode, gint32 add_dir_mode)
847 {
848         gboolean result = FALSE;
849         gchar *utf8_name = mono_unicode_to_external (mono_string_chars (path));
850         if (utf8_name) {
851                 struct stat st;
852                 if (stat (utf8_name, &st) == 0) {
853                         int mode = file_mode;
854                         if (st.st_mode & S_IFDIR)
855                                 mode |= add_dir_mode;
856                         result = (chmod (utf8_name, mode) == 0);
857                 }
858                 g_free (utf8_name);
859         }
860         return result;
861 }
862
863 #endif /* not PLATFORM_WIN32 */
864
865
866 MonoBoolean
867 ves_icall_Mono_Security_Cryptography_KeyPairPersistence_CanSecure (MonoString *root)
868 {
869 #if PLATFORM_WIN32
870         gint32 flags;
871
872         MONO_ARCH_SAVE_REGS;
873
874         /* ACL are nice... unless you have FAT or other uncivilized filesystem */
875         if (!GetVolumeInformation (mono_string_chars (root), NULL, 0, NULL, NULL, (LPDWORD)&flags, NULL, 0))
876                 return FALSE;
877         return ((flags & FS_PERSISTENT_ACLS) == FS_PERSISTENT_ACLS);
878 #else
879         MONO_ARCH_SAVE_REGS;
880         /* we assume some kind of security is applicable outside Windows */
881         return TRUE;
882 #endif
883 }
884
885
886 MonoBoolean
887 ves_icall_Mono_Security_Cryptography_KeyPairPersistence_IsMachineProtected (MonoString *path)
888 {
889         gboolean ret = FALSE;
890
891         MONO_ARCH_SAVE_REGS;
892
893         /* no one, but the owner, should have write access to the directory */
894 #ifdef PLATFORM_WIN32
895         ret = IsMachineProtected (mono_string_chars (path));
896 #else
897         ret = IsProtected (path, (S_IWGRP | S_IWOTH));
898 #endif
899         return ret;
900 }
901
902
903 MonoBoolean
904 ves_icall_Mono_Security_Cryptography_KeyPairPersistence_IsUserProtected (MonoString *path)
905 {
906         gboolean ret = FALSE;
907
908         MONO_ARCH_SAVE_REGS;
909
910         /* no one, but the user, should have access to the directory */
911 #ifdef PLATFORM_WIN32
912         ret = IsUserProtected (mono_string_chars (path));
913 #else
914         ret = IsProtected (path, (S_IRGRP | S_IWGRP | S_IXGRP | S_IROTH | S_IWOTH | S_IXOTH));
915 #endif
916         return ret;
917 }
918
919
920 MonoBoolean
921 ves_icall_Mono_Security_Cryptography_KeyPairPersistence_ProtectMachine (MonoString *path)
922 {
923         gboolean ret = FALSE;
924
925         MONO_ARCH_SAVE_REGS;
926
927         /* read/write to owner, read to everyone else */
928 #ifdef PLATFORM_WIN32
929         ret = ProtectMachine (mono_string_chars (path));
930 #else
931         ret = Protect (path, (S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH), (S_IXUSR | S_IXGRP | S_IXOTH));
932 #endif
933         return ret;
934 }
935
936
937 MonoBoolean
938 ves_icall_Mono_Security_Cryptography_KeyPairPersistence_ProtectUser (MonoString *path)
939 {
940         gboolean ret = FALSE;
941         
942         MONO_ARCH_SAVE_REGS;
943
944         /* read/write to user, no access to everyone else */
945 #ifdef PLATFORM_WIN32
946         ret = ProtectUser (mono_string_chars (path));
947 #else
948         ret = Protect (path, (S_IRUSR | S_IWUSR), S_IXUSR);
949 #endif
950         return ret;
951 }
952
953
954 /*
955  * Returns TRUE if there is "something" where the Authenticode signature is 
956  * normally located. Returns FALSE is data directory is empty.
957  *
958  * Note: Neither the structure nor the signature is verified by this function.
959  */
960 MonoBoolean
961 ves_icall_System_Security_Policy_Evidence_IsAuthenticodePresent (MonoReflectionAssembly *refass)
962 {
963         if (refass && refass->assembly && refass->assembly->image) {
964                 return mono_image_has_authenticode_entry (refass->assembly->image);
965         }
966         return FALSE;
967 }
968
969
970 /* System.Security.SecureString related internal calls */
971
972 static MonoImage *system_security_assembly = NULL;
973
974 void
975 ves_icall_System_Security_SecureString_DecryptInternal (MonoArray *data, MonoObject *scope)
976 {
977         invoke_protected_memory_method (data, scope, FALSE);
978 }
979 void
980 ves_icall_System_Security_SecureString_EncryptInternal (MonoArray* data, MonoObject *scope)
981 {
982         invoke_protected_memory_method (data, scope, TRUE);
983 }
984
985 void invoke_protected_memory_method (MonoArray *data, MonoObject *scope, gboolean encrypt)
986 {
987         MonoClass *klass;
988         MonoMethod *method;
989         void *params [2];
990
991         MONO_ARCH_SAVE_REGS;
992
993         if (system_security_assembly == NULL) {
994                 system_security_assembly = mono_image_loaded ("System.Security");
995                 if (!system_security_assembly) {
996                         MonoAssembly *sa = mono_assembly_open ("System.Security.dll", NULL);
997                         if (!sa)
998                                 g_assert_not_reached ();
999                         system_security_assembly = mono_assembly_get_image (sa);
1000                 }
1001         }
1002
1003         klass = mono_class_from_name (system_security_assembly,
1004                                                                   "System.Security.Cryptography", "ProtectedMemory");
1005         method = mono_class_get_method_from_name (klass, encrypt ? "Protect" : "Unprotect", 2);
1006         params [0] = data;
1007         params [1] = scope; /* MemoryProtectionScope.SameProcess */
1008         mono_runtime_invoke (method, NULL, params, NULL);
1009 }