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