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