Merge pull request #1304 from slluis/mac-proxy-autoconfig
[mono.git] / mono / metadata / security.c
1 /*
2  * security.c:  Security internal calls
3  *
4  * Author:
5  *      Sebastien Pouliot  <sebastien@ximian.com>
6  *
7  * Copyright 2004-2009 Novell, Inc (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 HOST_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 HOST_WIN32 */
63
64
65 /* internal functions - reuse driven */
66
67 #ifdef HOST_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 HOST_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.Security.Principal.WindowsIdentity */
261
262
263 gpointer
264 ves_icall_System_Security_Principal_WindowsIdentity_GetCurrentToken (void)
265 {
266         gpointer token = NULL;
267
268         MONO_ARCH_SAVE_REGS;
269
270 #ifdef HOST_WIN32
271         /* Note: This isn't a copy of the Token - we must not close it!!!
272          * http://www.develop.com/kbrown/book/html/whatis_windowsprincipal.html
273          */
274
275         /* thread may be impersonating somebody */
276         if (OpenThreadToken (GetCurrentThread (), TOKEN_QUERY, 1, &token) == 0) {
277                 /* if not take the process identity */
278                 OpenProcessToken (GetCurrentProcess (), TOKEN_QUERY, &token);
279         }
280 #else
281         token = GINT_TO_POINTER (geteuid ());
282 #endif
283         return token;
284 }
285
286
287 MonoString*
288 ves_icall_System_Security_Principal_WindowsIdentity_GetTokenName (gpointer token)
289 {
290         MonoString *result = NULL;
291         gunichar2 *uniname = NULL;
292         gint32 size = 0;
293
294 #ifdef HOST_WIN32
295         MONO_ARCH_SAVE_REGS;
296
297         GetTokenInformation (token, TokenUser, NULL, size, (PDWORD)&size);
298         if (size > 0) {
299                 TOKEN_USER *tu = g_malloc0 (size);
300                 if (GetTokenInformation (token, TokenUser, tu, size, (PDWORD)&size)) {
301                         uniname = GetSidName (NULL, tu->User.Sid, &size);
302                 }
303                 g_free (tu);
304         }
305 #else 
306         gchar *uname = GetTokenName ((uid_t) GPOINTER_TO_INT (token));
307
308         MONO_ARCH_SAVE_REGS;
309
310         if (uname) {
311                 size = strlen (uname);
312                 uniname = g_utf8_to_utf16 (uname, size, NULL, NULL, NULL);
313                 g_free (uname);
314         }
315 #endif /* HOST_WIN32 */
316
317         if (size > 0) {
318                 result = mono_string_new_utf16 (mono_domain_get (), uniname, size);
319         }
320         else
321                 result = mono_string_new (mono_domain_get (), "");
322
323         if (uniname)
324                 g_free (uniname);
325
326         return result;
327 }
328
329
330 gpointer
331 ves_icall_System_Security_Principal_WindowsIdentity_GetUserToken (MonoString *username)
332 {
333 #ifdef HOST_WIN32
334         gpointer token = NULL;
335
336         MONO_ARCH_SAVE_REGS;
337
338         /* TODO: MS has something like this working in Windows 2003 (client and
339          * server) but works only for domain accounts (so it's quite limiting).
340          * http://www.develop.com/kbrown/book/html/howto_logonuser.html
341          */
342         g_warning ("Unsupported on Win32 (anyway requires W2K3 minimum)");
343
344 #else /* HOST_WIN32*/
345
346 #ifdef HAVE_GETPWNAM_R
347         struct passwd pwd;
348         size_t fbufsize;
349         gchar *fbuf;
350         gint32 retval;
351 #endif
352         gpointer token = (gpointer) -2;
353         struct passwd *p;
354         gchar *utf8_name;
355         gboolean result;
356
357         MONO_ARCH_SAVE_REGS;
358
359         utf8_name = mono_unicode_to_external (mono_string_chars (username));
360
361 #ifdef HAVE_GETPWNAM_R
362 #ifdef _SC_GETPW_R_SIZE_MAX
363         fbufsize = mono_sysconf (_SC_GETPW_R_SIZE_MAX);
364 #else
365         fbufsize = MONO_SYSCONF_DEFAULT_SIZE;
366 #endif
367
368         fbuf = g_malloc0 (fbufsize);
369         retval = getpwnam_r (utf8_name, &pwd, fbuf, fbufsize, &p);
370         result = ((retval == 0) && (p == &pwd));
371 #else
372         /* default to non thread-safe but posix compliant function */
373         p = getpwnam (utf8_name);
374         result = (p != NULL);
375 #endif
376
377         if (result) {
378                 token = GINT_TO_POINTER (p->pw_uid);
379         }
380
381 #ifdef HAVE_GETPWNAM_R
382         g_free (fbuf);
383 #endif
384         g_free (utf8_name);
385 #endif
386         return token;
387 }
388
389
390 /* http://www.dotnet247.com/247reference/msgs/39/195403.aspx
391 // internal static string[] WindowsIdentity._GetRoles (IntPtr token)
392 */
393 MonoArray*
394 ves_icall_System_Security_Principal_WindowsIdentity_GetRoles (gpointer token) 
395 {
396         MonoArray *array = NULL;
397         MonoDomain *domain = mono_domain_get (); 
398 #ifdef HOST_WIN32
399         gint32 size = 0;
400
401         MONO_ARCH_SAVE_REGS;
402
403         GetTokenInformation (token, TokenGroups, NULL, size, (PDWORD)&size);
404         if (size > 0) {
405                 TOKEN_GROUPS *tg = g_malloc0 (size);
406                 if (GetTokenInformation (token, TokenGroups, tg, size, (PDWORD)&size)) {
407                         int i=0;
408                         int num = tg->GroupCount;
409
410                         array = mono_array_new (domain, mono_get_string_class (), num);
411
412                         for (i=0; i < num; i++) {
413                                 gint32 size = 0;
414                                 gunichar2 *uniname = GetSidName (NULL, tg->Groups [i].Sid, &size);
415
416                                 if (uniname) {
417                                         MonoString *str = mono_string_new_utf16 (domain, uniname, size);
418                                         mono_array_setref (array, i, str);
419                                         g_free (uniname);
420                                 }
421                         }
422                 }
423                 g_free (tg);
424         }
425 #else
426         /* POSIX-compliant systems should use IsMemberOfGroupId or IsMemberOfGroupName */
427         g_warning ("WindowsIdentity._GetRoles should never be called on POSIX");
428 #endif
429         if (!array) {
430                 /* return empty array of string, i.e. string [0] */
431                 array = mono_array_new (domain, mono_get_string_class (), 0);
432         }
433         return array;
434 }
435
436
437 /* System.Security.Principal.WindowsImpersonationContext */
438
439
440 gboolean
441 ves_icall_System_Security_Principal_WindowsImpersonationContext_CloseToken (gpointer token)
442 {
443         gboolean result = TRUE;
444
445         MONO_ARCH_SAVE_REGS;
446
447 #ifdef HOST_WIN32
448         result = (CloseHandle (token) != 0);
449 #endif
450         return result;
451 }
452
453
454 gpointer
455 ves_icall_System_Security_Principal_WindowsImpersonationContext_DuplicateToken (gpointer token)
456 {
457         gpointer dupe = NULL;
458
459         MONO_ARCH_SAVE_REGS;
460
461 #ifdef HOST_WIN32
462         if (DuplicateToken (token, SecurityImpersonation, &dupe) == 0) {
463                 dupe = NULL;
464         }
465 #else
466         dupe = token;
467 #endif
468         return dupe;
469 }
470
471
472 gboolean
473 ves_icall_System_Security_Principal_WindowsImpersonationContext_SetCurrentToken (gpointer token)
474 {
475         MONO_ARCH_SAVE_REGS;
476
477         /* Posix version implemented in /mono/mono/io-layer/security.c */
478         return (ImpersonateLoggedOnUser (token) != 0);
479 }
480
481
482 gboolean
483 ves_icall_System_Security_Principal_WindowsImpersonationContext_RevertToSelf (void)
484 {
485         MONO_ARCH_SAVE_REGS;
486
487         /* Posix version implemented in /mono/mono/io-layer/security.c */
488         return (RevertToSelf () != 0);
489 }
490
491
492 /* System.Security.Principal.WindowsPrincipal */
493
494 gboolean
495 ves_icall_System_Security_Principal_WindowsPrincipal_IsMemberOfGroupId (gpointer user, gpointer group)
496 {
497         gboolean result = FALSE;
498
499 #ifdef HOST_WIN32
500         MONO_ARCH_SAVE_REGS;
501
502         /* The convertion from an ID to a string is done in managed code for Windows */
503         g_warning ("IsMemberOfGroupId should never be called on Win32");
504
505 #else /* HOST_WIN32 */
506
507 #ifdef HAVE_GETGRGID_R
508         struct group grp;
509         size_t fbufsize;
510         gchar *fbuf;
511         gint32 retval;
512 #endif
513         struct group *g = NULL;
514
515         MONO_ARCH_SAVE_REGS;
516
517 #ifdef HAVE_GETGRGID_R
518 #ifdef _SC_GETGR_R_SIZE_MAX
519         fbufsize = mono_sysconf (_SC_GETGR_R_SIZE_MAX);
520 #else
521         fbufsize = MONO_SYSCONF_DEFAULT_SIZE;
522 #endif
523         fbuf = g_malloc0 (fbufsize);
524         retval = getgrgid_r ((gid_t) GPOINTER_TO_INT (group), &grp, fbuf, fbufsize, &g);
525         result = ((retval == 0) && (g == &grp));
526 #else
527         /* default to non thread-safe but posix compliant function */
528         g = getgrgid ((gid_t) GPOINTER_TO_INT (group));
529         result = (g != NULL);
530 #endif
531
532         if (result) {
533                 result = IsMemberOf ((uid_t) GPOINTER_TO_INT (user), g);
534         }
535
536 #ifdef HAVE_GETGRGID_R
537         g_free (fbuf);
538 #endif
539
540 #endif /* HOST_WIN32 */
541
542         return result;
543 }
544
545
546 gboolean
547 ves_icall_System_Security_Principal_WindowsPrincipal_IsMemberOfGroupName (gpointer user, MonoString *group)
548 {
549         gboolean result = FALSE;
550
551 #ifdef HOST_WIN32
552
553         MONO_ARCH_SAVE_REGS;
554
555         /* Windows version use a cache built using WindowsIdentity._GetRoles */
556         g_warning ("IsMemberOfGroupName should never be called on Win32");
557
558 #else /* HOST_WIN32 */
559         gchar *utf8_groupname;
560
561         MONO_ARCH_SAVE_REGS;
562
563         utf8_groupname = mono_unicode_to_external (mono_string_chars (group));
564         if (utf8_groupname) {
565                 struct group *g = NULL;
566 #ifdef HAVE_GETGRNAM_R
567                 struct group grp;
568                 gchar *fbuf;
569                 gint32 retval;
570 #ifdef _SC_GETGR_R_SIZE_MAX
571                 size_t fbufsize = mono_sysconf (_SC_GETGR_R_SIZE_MAX);
572 #else
573                 size_t fbufsize = MONO_SYSCONF_DEFAULT_SIZE;
574 #endif
575                 fbuf = g_malloc0 (fbufsize);
576                 retval = getgrnam_r (utf8_groupname, &grp, fbuf, fbufsize, &g);
577                 result = ((retval == 0) && (g == &grp));
578 #else
579                 /* default to non thread-safe but posix compliant function */
580                 g = getgrnam (utf8_groupname);
581                 result = (g != NULL);
582 #endif
583
584                 if (result) {
585                         result = IsMemberOf ((uid_t) GPOINTER_TO_INT (user), g);
586                 }
587
588 #ifdef HAVE_GETGRNAM_R
589                 g_free (fbuf);
590 #endif
591                 g_free (utf8_groupname);
592         }
593 #endif /* HOST_WIN32 */
594
595         return result;
596 }
597
598
599 /* Mono.Security.Cryptography IO related internal calls */
600
601 #ifdef HOST_WIN32
602
603 static PSID
604 GetAdministratorsSid (void) 
605 {
606         SID_IDENTIFIER_AUTHORITY admins = SECURITY_NT_AUTHORITY;
607         PSID pSid = NULL;
608         if (!AllocateAndInitializeSid (&admins, 2, SECURITY_BUILTIN_DOMAIN_RID, 
609                 DOMAIN_ALIAS_RID_ADMINS, 0, 0, 0, 0, 0, 0, &pSid)) 
610                 return NULL;
611         /* Note: this SID must be freed with FreeSid () */
612         return pSid;
613 }
614
615
616 static PSID
617 GetEveryoneSid (void)
618 {
619         SID_IDENTIFIER_AUTHORITY everyone = SECURITY_WORLD_SID_AUTHORITY;
620         PSID pSid = NULL;
621         if (!AllocateAndInitializeSid (&everyone, 1, SECURITY_WORLD_RID, 0, 0, 0, 0, 0, 0, 0, &pSid))
622                 return NULL;
623         /* Note: this SID must be freed with FreeSid () */
624         return pSid;
625 }
626
627
628 static PSID
629 GetCurrentUserSid (void) 
630 {
631         PSID sid = NULL;
632         guint32 size = 0;
633         gpointer token = ves_icall_System_Security_Principal_WindowsIdentity_GetCurrentToken ();
634
635         GetTokenInformation (token, TokenUser, NULL, size, (PDWORD)&size);
636         if (size > 0) {
637                 TOKEN_USER *tu = g_malloc0 (size);
638                 if (GetTokenInformation (token, TokenUser, tu, size, (PDWORD)&size)) {
639                         DWORD length = GetLengthSid (tu->User.Sid);
640                         sid = (PSID) g_malloc0 (length);
641                         if (!CopySid (length, sid, tu->User.Sid)) {
642                                 g_free (sid);
643                                 sid = NULL;
644                         }
645                 }
646                 g_free (tu);
647         }
648         /* Note: this SID must be freed with g_free () */
649         return sid;
650 }
651
652
653 static ACCESS_MASK
654 GetRightsFromSid (PSID sid, PACL acl) 
655 {
656         ACCESS_MASK rights = 0;
657         TRUSTEE trustee;
658
659         BuildTrusteeWithSidW (&trustee, sid);
660         if (GetEffectiveRightsFromAcl (acl, &trustee, &rights) != ERROR_SUCCESS)
661                 return 0;
662
663         return rights;
664 }
665
666
667 static gboolean 
668 IsMachineProtected (gunichar2 *path)
669 {
670         gboolean success = FALSE;
671         PACL pDACL = NULL;
672         PSID pEveryoneSid = NULL;
673
674         DWORD dwRes = GetNamedSecurityInfoW (path, SE_FILE_OBJECT, DACL_SECURITY_INFORMATION, NULL, NULL, &pDACL, NULL, NULL);
675         if (dwRes != ERROR_SUCCESS)
676                 return FALSE;
677
678         /* We check that Everyone is still limited to READ-ONLY -
679         but not if new entries have been added by an Administrator */
680
681         pEveryoneSid = GetEveryoneSid ();
682         if (pEveryoneSid) {
683                 ACCESS_MASK rights = GetRightsFromSid (pEveryoneSid, pDACL);
684                 /* http://msdn.microsoft.com/library/en-us/security/security/generic_access_rights.asp?frame=true */
685                 success = (rights == (READ_CONTROL | SYNCHRONIZE | FILE_READ_DATA | FILE_READ_EA | FILE_READ_ATTRIBUTES));
686                 FreeSid (pEveryoneSid);
687         }
688         /* Note: we don't need to check our own access - 
689         we'll know soon enough when reading the file */
690
691         if (pDACL)
692                 LocalFree (pDACL);
693
694         return success;
695 }
696
697
698 static gboolean 
699 IsUserProtected (gunichar2 *path)
700 {
701         gboolean success = FALSE;
702         PACL pDACL = NULL;
703         PSID pEveryoneSid = NULL;
704         PSECURITY_DESCRIPTOR pSecurityDescriptor = NULL;
705
706         DWORD dwRes = GetNamedSecurityInfoW (path, SE_FILE_OBJECT, 
707                 DACL_SECURITY_INFORMATION, NULL, NULL, &pDACL, NULL, &pSecurityDescriptor);
708         if (dwRes != ERROR_SUCCESS)
709                 return FALSE;
710
711         /* We check that our original entries in the ACL are in place -
712         but not if new entries have been added by the user */
713
714         /* Everyone should be denied */
715         pEveryoneSid = GetEveryoneSid ();
716         if (pEveryoneSid) {
717                 ACCESS_MASK rights = GetRightsFromSid (pEveryoneSid, pDACL);
718                 success = (rights == 0);
719                 FreeSid (pEveryoneSid);
720         }
721         /* Note: we don't need to check our own access - 
722         we'll know soon enough when reading the file */
723
724         if (pSecurityDescriptor)
725                 LocalFree (pSecurityDescriptor);
726
727         return success;
728 }
729
730
731 static gboolean 
732 ProtectMachine (gunichar2 *path)
733 {
734         PSID pEveryoneSid = GetEveryoneSid ();
735         PSID pAdminsSid = GetAdministratorsSid ();
736         DWORD retval = -1;
737
738         if (pEveryoneSid && pAdminsSid) {
739                 PACL pDACL = NULL;
740                 EXPLICIT_ACCESS ea [2];
741                 ZeroMemory (&ea, 2 * sizeof (EXPLICIT_ACCESS));
742
743                 /* grant all access to the BUILTIN\Administrators group */
744                 BuildTrusteeWithSidW (&ea [0].Trustee, pAdminsSid);
745                 ea [0].grfAccessPermissions = GENERIC_ALL;
746                 ea [0].grfAccessMode = SET_ACCESS;
747                 ea [0].grfInheritance = SUB_CONTAINERS_AND_OBJECTS_INHERIT;
748                 ea [0].Trustee.TrusteeForm = TRUSTEE_IS_SID;
749                 ea [0].Trustee.TrusteeType = TRUSTEE_IS_WELL_KNOWN_GROUP;
750
751                 /* read-only access everyone */
752                 BuildTrusteeWithSidW (&ea [1].Trustee, pEveryoneSid);
753                 ea [1].grfAccessPermissions = GENERIC_READ;
754                 ea [1].grfAccessMode = SET_ACCESS;
755                 ea [1].grfInheritance = SUB_CONTAINERS_AND_OBJECTS_INHERIT;
756                 ea [1].Trustee.TrusteeForm = TRUSTEE_IS_SID;
757                 ea [1].Trustee.TrusteeType = TRUSTEE_IS_WELL_KNOWN_GROUP;
758
759                 retval = SetEntriesInAcl (2, ea, NULL, &pDACL);
760                 if (retval == ERROR_SUCCESS) {
761                         /* with PROTECTED_DACL_SECURITY_INFORMATION we */
762                         /* remove any existing ACL (like inherited ones) */
763                         retval = SetNamedSecurityInfo (path, SE_FILE_OBJECT, 
764                                 DACL_SECURITY_INFORMATION | PROTECTED_DACL_SECURITY_INFORMATION,
765                                 NULL, NULL, pDACL, NULL);
766                 }
767                 if (pDACL)
768                         LocalFree (pDACL);
769         }
770
771         if (pEveryoneSid)
772                 FreeSid (pEveryoneSid);
773         if (pAdminsSid)
774                 FreeSid (pAdminsSid);
775         return (retval == ERROR_SUCCESS);
776 }
777
778
779 static gboolean 
780 ProtectUser (gunichar2 *path)
781 {
782         DWORD retval = -1;
783
784         PSID pCurrentSid = GetCurrentUserSid ();
785         if (pCurrentSid) {
786                 PACL pDACL = NULL;
787                 EXPLICIT_ACCESS ea;
788                 ZeroMemory (&ea, sizeof (EXPLICIT_ACCESS));
789
790                 /* grant exclusive access to the current user */
791                 BuildTrusteeWithSidW (&ea.Trustee, pCurrentSid);
792                 ea.grfAccessPermissions = GENERIC_ALL;
793                 ea.grfAccessMode = SET_ACCESS;
794                 ea.grfInheritance = SUB_CONTAINERS_AND_OBJECTS_INHERIT;
795                 ea.Trustee.TrusteeForm = TRUSTEE_IS_SID;
796                 ea.Trustee.TrusteeType = TRUSTEE_IS_USER;
797
798                 retval = SetEntriesInAcl (1, &ea, NULL, &pDACL);
799                 if (retval == ERROR_SUCCESS) {
800                         /* with PROTECTED_DACL_SECURITY_INFORMATION we
801                            remove any existing ACL (like inherited ones) */
802                         retval = SetNamedSecurityInfo (path, SE_FILE_OBJECT, 
803                                 DACL_SECURITY_INFORMATION | PROTECTED_DACL_SECURITY_INFORMATION,
804                                 NULL, NULL, pDACL, NULL);
805                 }
806
807                 if (pDACL)
808                         LocalFree (pDACL);
809                 g_free (pCurrentSid); /* g_malloc0 */
810         }
811
812         return (retval == ERROR_SUCCESS);
813 }
814
815 #else
816
817 static gboolean 
818 IsProtected (MonoString *path, gint32 protection) 
819 {
820         gboolean result = FALSE;
821         gchar *utf8_name = mono_unicode_to_external (mono_string_chars (path));
822         if (utf8_name) {
823                 struct stat st;
824                 if (stat (utf8_name, &st) == 0) {
825                         result = (((st.st_mode & 0777) & protection) == 0);
826                 }
827                 g_free (utf8_name);
828         }
829         return result;
830 }
831
832
833 static gboolean 
834 Protect (MonoString *path, gint32 file_mode, gint32 add_dir_mode)
835 {
836         gboolean result = FALSE;
837         gchar *utf8_name = mono_unicode_to_external (mono_string_chars (path));
838         if (utf8_name) {
839                 struct stat st;
840                 if (stat (utf8_name, &st) == 0) {
841                         int mode = file_mode;
842                         if (st.st_mode & S_IFDIR)
843                                 mode |= add_dir_mode;
844                         result = (chmod (utf8_name, mode) == 0);
845                 }
846                 g_free (utf8_name);
847         }
848         return result;
849 }
850
851 #endif /* not HOST_WIN32 */
852
853
854 MonoBoolean
855 ves_icall_Mono_Security_Cryptography_KeyPairPersistence_CanSecure (MonoString *root)
856 {
857 #if HOST_WIN32
858         gint32 flags;
859
860         MONO_ARCH_SAVE_REGS;
861
862         /* ACL are nice... unless you have FAT or other uncivilized filesystem */
863         if (!GetVolumeInformation (mono_string_chars (root), NULL, 0, NULL, NULL, (LPDWORD)&flags, NULL, 0))
864                 return FALSE;
865         return ((flags & FS_PERSISTENT_ACLS) == FS_PERSISTENT_ACLS);
866 #else
867         MONO_ARCH_SAVE_REGS;
868         /* we assume some kind of security is applicable outside Windows */
869         return TRUE;
870 #endif
871 }
872
873
874 MonoBoolean
875 ves_icall_Mono_Security_Cryptography_KeyPairPersistence_IsMachineProtected (MonoString *path)
876 {
877         gboolean ret = FALSE;
878
879         MONO_ARCH_SAVE_REGS;
880
881         /* no one, but the owner, should have write access to the directory */
882 #ifdef HOST_WIN32
883         ret = IsMachineProtected (mono_string_chars (path));
884 #else
885         ret = IsProtected (path, (S_IWGRP | S_IWOTH));
886 #endif
887         return ret;
888 }
889
890
891 MonoBoolean
892 ves_icall_Mono_Security_Cryptography_KeyPairPersistence_IsUserProtected (MonoString *path)
893 {
894         gboolean ret = FALSE;
895
896         MONO_ARCH_SAVE_REGS;
897
898         /* no one, but the user, should have access to the directory */
899 #ifdef HOST_WIN32
900         ret = IsUserProtected (mono_string_chars (path));
901 #else
902         ret = IsProtected (path, (S_IRGRP | S_IWGRP | S_IXGRP | S_IROTH | S_IWOTH | S_IXOTH));
903 #endif
904         return ret;
905 }
906
907
908 MonoBoolean
909 ves_icall_Mono_Security_Cryptography_KeyPairPersistence_ProtectMachine (MonoString *path)
910 {
911         gboolean ret = FALSE;
912
913         MONO_ARCH_SAVE_REGS;
914
915         /* read/write to owner, read to everyone else */
916 #ifdef HOST_WIN32
917         ret = ProtectMachine (mono_string_chars (path));
918 #else
919         ret = Protect (path, (S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH), (S_IXUSR | S_IXGRP | S_IXOTH));
920 #endif
921         return ret;
922 }
923
924
925 MonoBoolean
926 ves_icall_Mono_Security_Cryptography_KeyPairPersistence_ProtectUser (MonoString *path)
927 {
928         gboolean ret = FALSE;
929         
930         MONO_ARCH_SAVE_REGS;
931
932         /* read/write to user, no access to everyone else */
933 #ifdef HOST_WIN32
934         ret = ProtectUser (mono_string_chars (path));
935 #else
936         ret = Protect (path, (S_IRUSR | S_IWUSR), S_IXUSR);
937 #endif
938         return ret;
939 }
940
941
942 /*
943  * Returns TRUE if there is "something" where the Authenticode signature is 
944  * normally located. Returns FALSE is data directory is empty.
945  *
946  * Note: Neither the structure nor the signature is verified by this function.
947  */
948 MonoBoolean
949 ves_icall_System_Security_Policy_Evidence_IsAuthenticodePresent (MonoReflectionAssembly *refass)
950 {
951         if (refass && refass->assembly && refass->assembly->image) {
952                 return mono_image_has_authenticode_entry (refass->assembly->image);
953         }
954         return FALSE;
955 }
956
957
958 /* System.Security.SecureString related internal calls */
959
960 static MonoImage *system_security_assembly = NULL;
961
962 void
963 ves_icall_System_Security_SecureString_DecryptInternal (MonoArray *data, MonoObject *scope)
964 {
965         invoke_protected_memory_method (data, scope, FALSE);
966 }
967 void
968 ves_icall_System_Security_SecureString_EncryptInternal (MonoArray* data, MonoObject *scope)
969 {
970         invoke_protected_memory_method (data, scope, TRUE);
971 }
972
973 void invoke_protected_memory_method (MonoArray *data, MonoObject *scope, gboolean encrypt)
974 {
975         MonoClass *klass;
976         MonoMethod *method;
977         void *params [2];
978
979         MONO_ARCH_SAVE_REGS;
980
981         if (system_security_assembly == NULL) {
982                 system_security_assembly = mono_image_loaded ("System.Security");
983                 if (!system_security_assembly) {
984                         MonoAssembly *sa = mono_assembly_open ("System.Security.dll", NULL);
985                         if (!sa)
986                                 g_assert_not_reached ();
987                         system_security_assembly = mono_assembly_get_image (sa);
988                 }
989         }
990
991         klass = mono_class_from_name (system_security_assembly,
992                                                                   "System.Security.Cryptography", "ProtectedMemory");
993         method = mono_class_get_method_from_name (klass, encrypt ? "Protect" : "Unprotect", 2);
994         params [0] = data;
995         params [1] = scope; /* MemoryProtectionScope.SameProcess */
996         mono_runtime_invoke (method, NULL, params, NULL);
997 }