Merge pull request #5714 from alexischr/update_bockbuild
[mono.git] / mono / metadata / mono-security.c
1 /**
2  * \file
3  * Security internal calls
4  *
5  * Author:
6  *      Sebastien Pouliot  <sebastien@ximian.com>
7  *
8  * Copyright 2004-2009 Novell, Inc (http://www.novell.com)
9  * Licensed under the MIT license. See LICENSE file in the project root for full license information.
10  */
11
12 #ifdef HAVE_CONFIG_H
13 #include <config.h>
14 #endif
15
16 #include <mono/metadata/assembly-internals.h>
17 #include <mono/metadata/appdomain.h>
18 #include <mono/metadata/image.h>
19 #include <mono/metadata/exception.h>
20 #include <mono/metadata/object-internals.h>
21 #include <mono/metadata/metadata-internals.h>
22 #include <mono/metadata/security.h>
23 #include <mono/utils/strenc.h>
24
25 #ifndef HOST_WIN32
26 #include <config.h>
27 #ifdef HAVE_GRP_H
28 #include <grp.h>
29 #endif
30 #ifdef HAVE_PWD_H
31 #include <pwd.h>
32 #endif
33 #include <string.h>
34 #include <sys/stat.h>
35 #include <sys/types.h>
36 #include <unistd.h>
37
38 /* Disclaimers */
39
40 #if defined(__GNUC__)
41
42 #ifndef HAVE_GETGRGID_R
43         #warning Non-thread safe getgrgid being used!
44 #endif
45 #ifndef HAVE_GETGRNAM_R
46         #warning Non-thread safe getgrnam being used!
47 #endif
48 #ifndef HAVE_GETPWNAM_R
49         #warning Non-thread safe getpwnam being used!
50 #endif
51 #ifndef HAVE_GETPWUID_R
52         #warning Non-thread safe getpwuid being used!
53 #endif
54
55 #endif /* defined(__GNUC__) */
56 #endif /* !HOST_WIN32 */
57
58 /* internal functions - reuse driven */
59
60 /* ask a server to translate a SID into a textual representation */
61 #ifndef HOST_WIN32
62 #define MONO_SYSCONF_DEFAULT_SIZE       ((size_t) 1024)
63
64 /*
65  * Ensure we always get a valid (usable) value from sysconf.
66  * In case of error, we return the default value.
67  */
68 static size_t mono_sysconf (int name)
69 {
70         size_t size = (size_t) sysconf (name);
71         /* default value */
72         return (size == -1) ? MONO_SYSCONF_DEFAULT_SIZE : size;
73 }
74
75 static gchar*
76 GetTokenName (uid_t uid)
77 {
78         gchar *uname = NULL;
79
80 #ifdef HAVE_PWD_H
81
82 #ifdef HAVE_GETPWUID_R
83         struct passwd pwd;
84         size_t fbufsize;
85         gchar *fbuf;
86         gint32 retval;
87 #endif
88         struct passwd *p = NULL;
89         gboolean result;
90
91 #ifdef HAVE_GETPWUID_R
92 #ifdef _SC_GETPW_R_SIZE_MAX
93         fbufsize = mono_sysconf (_SC_GETPW_R_SIZE_MAX);
94 #else
95         fbufsize = MONO_SYSCONF_DEFAULT_SIZE;
96 #endif
97         fbuf = (gchar *)g_malloc0 (fbufsize);
98         retval = getpwuid_r (uid, &pwd, fbuf, fbufsize, &p);
99         result = ((retval == 0) && (p == &pwd));
100 #else
101         /* default to non thread-safe but posix compliant function */
102         p = getpwuid (uid);
103         result = (p != NULL);
104 #endif
105
106         if (result) {
107                 uname = g_strdup (p->pw_name);
108         }
109
110 #ifdef HAVE_GETPWUID_R
111         g_free (fbuf);
112 #endif
113
114 #endif /* HAVE_PWD_H */
115
116         return uname;
117 }
118
119 #ifdef HAVE_GRP_H
120
121 static gboolean
122 IsMemberInList (uid_t user, struct group *g) 
123 {
124         gboolean result = FALSE;
125         gchar *utf8_username = GetTokenName (user);
126
127         if (!utf8_username)
128                 return FALSE;
129
130         if (g) {
131                 gchar **users = g->gr_mem;
132
133                 while (*users) {
134                         gchar *u = *(users);
135                         if (strcmp (utf8_username, u) == 0) {
136                                 result = TRUE;
137                                 break;
138                         }
139                         users++;
140                 }
141         }               
142
143         g_free (utf8_username);
144         return result;
145 }
146
147 #endif /* HAVE_GRP_H */
148
149 static gboolean
150 IsDefaultGroup (uid_t user, gid_t group)
151 {
152         gboolean result = FALSE;
153
154 #ifdef HAVE_PWD_H
155
156 #ifdef HAVE_GETPWUID_R
157         struct passwd pwd;
158         size_t fbufsize;
159         gchar *fbuf;
160         gint32 retval;
161 #endif
162         struct passwd *p = NULL;
163
164 #ifdef HAVE_GETPWUID_R
165 #ifdef _SC_GETPW_R_SIZE_MAX
166         fbufsize = mono_sysconf (_SC_GETPW_R_SIZE_MAX);
167 #else
168         fbufsize = MONO_SYSCONF_DEFAULT_SIZE;
169 #endif
170
171         fbuf = (gchar *)g_malloc0 (fbufsize);
172         retval = getpwuid_r (user, &pwd, fbuf, fbufsize, &p);
173         result = ((retval == 0) && (p == &pwd));
174 #else
175         /* default to non thread-safe but posix compliant function */
176         p = getpwuid (user);
177         result = (p != NULL);
178 #endif
179
180         if (result) {
181                 result = (p->pw_gid == group);
182         }
183
184 #ifdef HAVE_GETPWUID_R
185         g_free (fbuf);
186 #endif
187
188 #endif /* HAVE_PWD_H */
189
190         return result;
191 }
192
193 #ifdef HAVE_GRP_H
194
195 static gboolean
196 IsMemberOf (gid_t user, struct group *g) 
197 {
198         if (!g)
199                 return FALSE;
200
201         /* is it the user default group */
202         if (IsDefaultGroup (user, g->gr_gid))
203                 return TRUE;
204
205         /* is the user in the group list */
206         return IsMemberInList (user, g);
207 }
208
209 #endif /* HAVE_GRP_H */
210
211 #endif /* !HOST_WIN32 */
212
213 /* ICALLS */
214
215 /* System.Security.Principal.WindowsIdentity */
216
217 #ifndef HOST_WIN32
218 gpointer
219 mono_security_principal_windows_identity_get_current_token ()
220 {
221         return GINT_TO_POINTER (geteuid ());
222 }
223
224 gpointer
225 ves_icall_System_Security_Principal_WindowsIdentity_GetCurrentToken (MonoError *error)
226 {
227         error_init (error);
228         return mono_security_principal_windows_identity_get_current_token ();
229 }
230
231 static gint32
232 internal_get_token_name (gpointer token, gunichar2 ** uniname)
233 {
234         gint32 size = 0;
235
236         gchar *uname = GetTokenName ((uid_t) GPOINTER_TO_INT (token));
237
238         if (uname) {
239                 size = strlen (uname);
240                 *uniname = g_utf8_to_utf16 (uname, size, NULL, NULL, NULL);
241                 g_free (uname);
242         }
243
244         return size;
245 }
246
247 MonoStringHandle
248 ves_icall_System_Security_Principal_WindowsIdentity_GetTokenName (gpointer token, MonoError *error)
249 {
250         MonoStringHandle result;
251         gunichar2 *uniname = NULL;
252         gint32 size = 0;
253
254         error_init (error);
255
256         size = internal_get_token_name (token, &uniname);
257
258         if (size > 0) {
259                 result = mono_string_new_utf16_handle (mono_domain_get (), uniname, size, error);
260         }
261         else
262                 result = mono_string_new_handle (mono_domain_get (), "", error);
263
264         if (uniname)
265                 g_free (uniname);
266
267         return result;
268 }
269 #endif  /* !HOST_WIN32 */
270
271 #ifndef HOST_WIN32
272 gpointer
273 ves_icall_System_Security_Principal_WindowsIdentity_GetUserToken (MonoStringHandle username, MonoError *error)
274 {
275         gpointer token = (gpointer)-2;
276
277         error_init (error);
278 #ifdef HAVE_PWD_H
279
280 #ifdef HAVE_GETPWNAM_R
281         struct passwd pwd;
282         size_t fbufsize;
283         gchar *fbuf;
284         gint32 retval;
285 #endif
286         struct passwd *p;
287         gchar *utf8_name;
288         gboolean result;
289
290         utf8_name = mono_string_handle_to_utf8 (username, error);
291         return_val_if_nok (error, NULL);
292
293 #ifdef HAVE_GETPWNAM_R
294 #ifdef _SC_GETPW_R_SIZE_MAX
295         fbufsize = mono_sysconf (_SC_GETPW_R_SIZE_MAX);
296 #else
297         fbufsize = MONO_SYSCONF_DEFAULT_SIZE;
298 #endif
299
300         fbuf = (gchar *)g_malloc0 (fbufsize);
301         retval = getpwnam_r (utf8_name, &pwd, fbuf, fbufsize, &p);
302         result = ((retval == 0) && (p == &pwd));
303 #else
304         /* default to non thread-safe but posix compliant function */
305         p = getpwnam (utf8_name);
306         result = (p != NULL);
307 #endif
308
309         if (result) {
310                 token = GINT_TO_POINTER (p->pw_uid);
311         }
312
313 #ifdef HAVE_GETPWNAM_R
314         g_free (fbuf);
315 #endif
316         g_free (utf8_name);
317
318 #endif /* HAVE_PWD_H */
319
320         return token;
321 }
322 #endif /* HOST_WIN32 */
323
324 /* http://www.dotnet247.com/247reference/msgs/39/195403.aspx
325 // internal static string[] WindowsIdentity._GetRoles (IntPtr token)
326 */
327
328 #ifndef HOST_WIN32
329 MonoArray*
330 ves_icall_System_Security_Principal_WindowsIdentity_GetRoles (gpointer token)
331 {
332         MonoError error;
333         MonoArray *array = NULL;
334         MonoDomain *domain = mono_domain_get ();
335
336         /* POSIX-compliant systems should use IsMemberOfGroupId or IsMemberOfGroupName */
337         g_warning ("WindowsIdentity._GetRoles should never be called on POSIX");
338
339         if (!array) {
340                 /* return empty array of string, i.e. string [0] */
341                 array = mono_array_new_checked (domain, mono_get_string_class (), 0, &error);
342                 mono_error_set_pending_exception (&error);
343         }
344         return array;
345 }
346 #endif /* !HOST_WIN32 */
347
348 /* System.Security.Principal.WindowsImpersonationContext */
349
350 #ifndef HOST_WIN32
351 gboolean
352 ves_icall_System_Security_Principal_WindowsImpersonationContext_CloseToken (gpointer token)
353 {
354         return TRUE;
355 }
356 #endif /* !HOST_WIN32 */
357
358 #ifndef HOST_WIN32
359 gpointer
360 ves_icall_System_Security_Principal_WindowsImpersonationContext_DuplicateToken (gpointer token)
361 {
362         return token;
363 }
364 #endif /* !HOST_WIN32 */
365
366 #if G_HAVE_API_SUPPORT(HAVE_CLASSIC_WINAPI_SUPPORT)
367 gboolean
368 ves_icall_System_Security_Principal_WindowsImpersonationContext_SetCurrentToken (gpointer token)
369 {
370 #ifdef HOST_WIN32
371         return (ImpersonateLoggedOnUser (token) != 0);
372 #else
373         uid_t itoken = (uid_t) GPOINTER_TO_INT (token);
374 #ifdef HAVE_SETRESUID
375         if (setresuid (-1, itoken, getuid ()) < 0)
376                 return FALSE;
377 #endif
378         return geteuid () == itoken;
379 #endif
380 }
381
382 gboolean
383 ves_icall_System_Security_Principal_WindowsImpersonationContext_RevertToSelf (void)
384 {
385 #ifdef HOST_WIN32
386         return (RevertToSelf () != 0);
387 #else
388 #ifdef HAVE_GETRESUID
389         uid_t ruid, euid;
390 #endif
391         uid_t suid = -1;
392
393 #ifdef HAVE_GETRESUID
394         if (getresuid (&ruid, &euid, &suid) < 0)
395                 return FALSE;
396 #endif
397 #ifdef HAVE_SETRESUID
398         if (setresuid (-1, suid, -1) < 0)
399                 return FALSE;
400 #else
401         return TRUE;
402 #endif
403         return geteuid () == suid;
404 #endif
405 }
406 #endif /* G_HAVE_API_SUPPORT(HAVE_CLASSIC_WINAPI_SUPPORT) */
407
408 /* System.Security.Principal.WindowsPrincipal */
409
410 #ifndef HOST_WIN32
411 gboolean
412 ves_icall_System_Security_Principal_WindowsPrincipal_IsMemberOfGroupId (gpointer user, gpointer group)
413 {
414         gboolean result = FALSE;
415
416 #ifdef HAVE_GRP_H
417
418 #ifdef HAVE_GETGRGID_R
419         struct group grp;
420         size_t fbufsize;
421         gchar *fbuf;
422         gint32 retval;
423 #endif
424         struct group *g = NULL;
425
426 #ifdef HAVE_GETGRGID_R
427 #ifdef _SC_GETGR_R_SIZE_MAX
428         fbufsize = mono_sysconf (_SC_GETGR_R_SIZE_MAX);
429 #else
430         fbufsize = MONO_SYSCONF_DEFAULT_SIZE;
431 #endif
432         fbuf = (gchar *)g_malloc0 (fbufsize);
433         retval = getgrgid_r ((gid_t) GPOINTER_TO_INT (group), &grp, fbuf, fbufsize, &g);
434         result = ((retval == 0) && (g == &grp));
435 #else
436         /* default to non thread-safe but posix compliant function */
437         g = getgrgid ((gid_t) GPOINTER_TO_INT (group));
438         result = (g != NULL);
439 #endif
440
441         if (result) {
442                 result = IsMemberOf ((uid_t) GPOINTER_TO_INT (user), g);
443         }
444
445 #ifdef HAVE_GETGRGID_R
446         g_free (fbuf);
447 #endif
448
449 #endif /* HAVE_GRP_H */
450
451         return result;
452 }
453
454 gboolean
455 ves_icall_System_Security_Principal_WindowsPrincipal_IsMemberOfGroupName (gpointer user, MonoString *group)
456 {
457         gboolean result = FALSE;
458
459 #ifdef HAVE_GRP_H
460
461         gchar *utf8_groupname;
462
463         utf8_groupname = mono_unicode_to_external (mono_string_chars (group));
464         if (utf8_groupname) {
465                 struct group *g = NULL;
466 #ifdef HAVE_GETGRNAM_R
467                 struct group grp;
468                 gchar *fbuf;
469                 gint32 retval;
470 #ifdef _SC_GETGR_R_SIZE_MAX
471                 size_t fbufsize = mono_sysconf (_SC_GETGR_R_SIZE_MAX);
472 #else
473                 size_t fbufsize = MONO_SYSCONF_DEFAULT_SIZE;
474 #endif
475                 fbuf = (gchar *)g_malloc0 (fbufsize);
476                 retval = getgrnam_r (utf8_groupname, &grp, fbuf, fbufsize, &g);
477                 result = ((retval == 0) && (g == &grp));
478 #else
479                 /* default to non thread-safe but posix compliant function */
480                 g = getgrnam (utf8_groupname);
481                 result = (g != NULL);
482 #endif
483
484                 if (result) {
485                         result = IsMemberOf ((uid_t) GPOINTER_TO_INT (user), g);
486                 }
487
488 #ifdef HAVE_GETGRNAM_R
489                 g_free (fbuf);
490 #endif
491                 g_free (utf8_groupname);
492         }
493
494 #endif /* HAVE_GRP_H */
495
496         return result;
497 }
498 #endif /* !HOST_WIN32 */
499
500 /* Mono.Security.Cryptography IO related internal calls */
501
502 #ifndef HOST_WIN32
503 static gboolean
504 IsProtected (MonoString *path, gint32 protection) 
505 {
506         gboolean result = FALSE;
507         gchar *utf8_name = mono_unicode_to_external (mono_string_chars (path));
508         if (utf8_name) {
509                 struct stat st;
510                 if (stat (utf8_name, &st) == 0) {
511                         result = (((st.st_mode & 0777) & protection) == 0);
512                 }
513                 g_free (utf8_name);
514         }
515         return result;
516 }
517
518
519 static gboolean
520 Protect (MonoString *path, gint32 file_mode, gint32 add_dir_mode)
521 {
522         gboolean result = FALSE;
523         gchar *utf8_name = mono_unicode_to_external (mono_string_chars (path));
524         if (utf8_name) {
525                 struct stat st;
526                 if (stat (utf8_name, &st) == 0) {
527                         int mode = file_mode;
528                         if (st.st_mode & S_IFDIR)
529                                 mode |= add_dir_mode;
530                         result = (chmod (utf8_name, mode) == 0);
531                 }
532                 g_free (utf8_name);
533         }
534         return result;
535 }
536
537 MonoBoolean
538 ves_icall_Mono_Security_Cryptography_KeyPairPersistence_CanSecure (MonoString *root)
539 {
540         /* we assume some kind of security is applicable outside Windows */
541         return TRUE;
542 }
543
544 MonoBoolean
545 ves_icall_Mono_Security_Cryptography_KeyPairPersistence_IsMachineProtected (MonoString *path)
546 {
547         gboolean ret = FALSE;
548
549         /* no one, but the owner, should have write access to the directory */
550         ret = IsProtected (path, (S_IWGRP | S_IWOTH));
551         return (MonoBoolean)ret;
552 }
553
554 MonoBoolean
555 ves_icall_Mono_Security_Cryptography_KeyPairPersistence_IsUserProtected (MonoString *path)
556 {
557         gboolean ret = FALSE;
558
559         /* no one, but the user, should have access to the directory */
560         ret = IsProtected (path, (S_IRGRP | S_IWGRP | S_IXGRP | S_IROTH | S_IWOTH | S_IXOTH));
561         return (MonoBoolean)ret;
562 }
563
564 MonoBoolean
565 ves_icall_Mono_Security_Cryptography_KeyPairPersistence_ProtectMachine (MonoString *path)
566 {
567         gboolean ret = FALSE;
568
569         /* read/write to owner, read to everyone else */
570         ret = Protect (path, (S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH), (S_IXUSR | S_IXGRP | S_IXOTH));
571         return (MonoBoolean)ret;
572 }
573
574 MonoBoolean
575 ves_icall_Mono_Security_Cryptography_KeyPairPersistence_ProtectUser (MonoString *path)
576 {
577         gboolean ret = FALSE;
578         
579         /* read/write to user, no access to everyone else */
580         ret = Protect (path, (S_IRUSR | S_IWUSR), S_IXUSR);
581         return (MonoBoolean)ret;
582 }
583 #endif /* !HOST_WIN32 */
584
585 /*
586  * Returns TRUE if there is "something" where the Authenticode signature is 
587  * normally located. Returns FALSE is data directory is empty.
588  *
589  * Note: Neither the structure nor the signature is verified by this function.
590  */
591 MonoBoolean
592 ves_icall_System_Security_Policy_Evidence_IsAuthenticodePresent (MonoReflectionAssemblyHandle refass, MonoError *error)
593 {
594         error_init (error);
595         if (MONO_HANDLE_IS_NULL (refass))
596                 return FALSE;
597         MonoAssembly *assembly = MONO_HANDLE_GETVAL (refass, assembly);
598         if (assembly && assembly->image) {
599                 return (MonoBoolean)mono_image_has_authenticode_entry (assembly->image);
600         }
601         return FALSE;
602 }
603
604
605 /* System.Security.SecureString related internal calls */
606
607 static MonoImage *system_security_assembly = NULL;
608
609 void
610 ves_icall_System_Security_SecureString_DecryptInternal (MonoArray *data, MonoObject *scope)
611 {
612         MonoError error;
613         invoke_protected_memory_method (data, scope, FALSE, &error);
614         mono_error_set_pending_exception (&error);
615 }
616 void
617 ves_icall_System_Security_SecureString_EncryptInternal (MonoArray* data, MonoObject *scope)
618 {
619         MonoError error;
620         invoke_protected_memory_method (data, scope, TRUE, &error);
621         mono_error_set_pending_exception (&error);
622 }
623
624 void invoke_protected_memory_method (MonoArray *data, MonoObject *scope, gboolean encrypt, MonoError *error)
625 {
626         MonoClass *klass;
627         MonoMethod *method;
628         void *params [2];
629
630         error_init (error);
631         
632         if (system_security_assembly == NULL) {
633                 system_security_assembly = mono_image_loaded ("System.Security");
634                 if (!system_security_assembly) {
635                         MonoAssembly *sa = mono_assembly_open_predicate ("System.Security.dll", FALSE, FALSE, NULL, NULL, NULL);
636                         if (!sa)
637                                 g_assert_not_reached ();
638                         system_security_assembly = mono_assembly_get_image (sa);
639                 }
640         }
641
642         klass = mono_class_load_from_name (system_security_assembly,
643                                                                   "System.Security.Cryptography", "ProtectedMemory");
644         method = mono_class_get_method_from_name (klass, encrypt ? "Protect" : "Unprotect", 2);
645         params [0] = data;
646         params [1] = scope; /* MemoryProtectionScope.SameProcess */
647
648         mono_runtime_invoke_checked (method, NULL, params, error);
649 }