Build libmonoruntime under none desktop Windows API family.
[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         /* Posix version implemented in /mono/mono/io-layer/security.c */
341         return (ImpersonateLoggedOnUser (token) != 0);
342 }
343
344 gboolean
345 ves_icall_System_Security_Principal_WindowsImpersonationContext_RevertToSelf (void)
346 {
347         /* Posix version implemented in /mono/mono/io-layer/security.c */
348         return (RevertToSelf () != 0);
349 }
350 #endif /* G_HAVE_API_SUPPORT(HAVE_CLASSIC_WINAPI_SUPPORT) */
351
352 /* System.Security.Principal.WindowsPrincipal */
353
354 #ifndef HOST_WIN32
355 gboolean
356 ves_icall_System_Security_Principal_WindowsPrincipal_IsMemberOfGroupId (gpointer user, gpointer group)
357 {
358         gboolean result = FALSE;
359
360 #ifdef HAVE_GETGRGID_R
361         struct group grp;
362         size_t fbufsize;
363         gchar *fbuf;
364         gint32 retval;
365 #endif
366         struct group *g = NULL;
367
368 #ifdef HAVE_GETGRGID_R
369 #ifdef _SC_GETGR_R_SIZE_MAX
370         fbufsize = mono_sysconf (_SC_GETGR_R_SIZE_MAX);
371 #else
372         fbufsize = MONO_SYSCONF_DEFAULT_SIZE;
373 #endif
374         fbuf = (gchar *)g_malloc0 (fbufsize);
375         retval = getgrgid_r ((gid_t) GPOINTER_TO_INT (group), &grp, fbuf, fbufsize, &g);
376         result = ((retval == 0) && (g == &grp));
377 #else
378         /* default to non thread-safe but posix compliant function */
379         g = getgrgid ((gid_t) GPOINTER_TO_INT (group));
380         result = (g != NULL);
381 #endif
382
383         if (result) {
384                 result = IsMemberOf ((uid_t) GPOINTER_TO_INT (user), g);
385         }
386
387 #ifdef HAVE_GETGRGID_R
388         g_free (fbuf);
389 #endif
390
391         return result;
392 }
393
394 gboolean
395 ves_icall_System_Security_Principal_WindowsPrincipal_IsMemberOfGroupName (gpointer user, MonoString *group)
396 {
397         gboolean result = FALSE;
398         gchar *utf8_groupname;
399
400         utf8_groupname = mono_unicode_to_external (mono_string_chars (group));
401         if (utf8_groupname) {
402                 struct group *g = NULL;
403 #ifdef HAVE_GETGRNAM_R
404                 struct group grp;
405                 gchar *fbuf;
406                 gint32 retval;
407 #ifdef _SC_GETGR_R_SIZE_MAX
408                 size_t fbufsize = mono_sysconf (_SC_GETGR_R_SIZE_MAX);
409 #else
410                 size_t fbufsize = MONO_SYSCONF_DEFAULT_SIZE;
411 #endif
412                 fbuf = (gchar *)g_malloc0 (fbufsize);
413                 retval = getgrnam_r (utf8_groupname, &grp, fbuf, fbufsize, &g);
414                 result = ((retval == 0) && (g == &grp));
415 #else
416                 /* default to non thread-safe but posix compliant function */
417                 g = getgrnam (utf8_groupname);
418                 result = (g != NULL);
419 #endif
420
421                 if (result) {
422                         result = IsMemberOf ((uid_t) GPOINTER_TO_INT (user), g);
423                 }
424
425 #ifdef HAVE_GETGRNAM_R
426                 g_free (fbuf);
427 #endif
428                 g_free (utf8_groupname);
429         }
430
431         return result;
432 }
433 #endif /* !HOST_WIN32 */
434
435 /* Mono.Security.Cryptography IO related internal calls */
436
437 #ifndef HOST_WIN32
438 static gboolean
439 IsProtected (MonoString *path, gint32 protection) 
440 {
441         gboolean result = FALSE;
442         gchar *utf8_name = mono_unicode_to_external (mono_string_chars (path));
443         if (utf8_name) {
444                 struct stat st;
445                 if (stat (utf8_name, &st) == 0) {
446                         result = (((st.st_mode & 0777) & protection) == 0);
447                 }
448                 g_free (utf8_name);
449         }
450         return result;
451 }
452
453
454 static gboolean
455 Protect (MonoString *path, gint32 file_mode, gint32 add_dir_mode)
456 {
457         gboolean result = FALSE;
458         gchar *utf8_name = mono_unicode_to_external (mono_string_chars (path));
459         if (utf8_name) {
460                 struct stat st;
461                 if (stat (utf8_name, &st) == 0) {
462                         int mode = file_mode;
463                         if (st.st_mode & S_IFDIR)
464                                 mode |= add_dir_mode;
465                         result = (chmod (utf8_name, mode) == 0);
466                 }
467                 g_free (utf8_name);
468         }
469         return result;
470 }
471
472 MonoBoolean
473 ves_icall_Mono_Security_Cryptography_KeyPairPersistence_CanSecure (MonoString *root)
474 {
475         /* we assume some kind of security is applicable outside Windows */
476         return TRUE;
477 }
478
479 MonoBoolean
480 ves_icall_Mono_Security_Cryptography_KeyPairPersistence_IsMachineProtected (MonoString *path)
481 {
482         gboolean ret = FALSE;
483
484         /* no one, but the owner, should have write access to the directory */
485         ret = IsProtected (path, (S_IWGRP | S_IWOTH));
486         return (MonoBoolean)ret;
487 }
488
489 MonoBoolean
490 ves_icall_Mono_Security_Cryptography_KeyPairPersistence_IsUserProtected (MonoString *path)
491 {
492         gboolean ret = FALSE;
493
494         /* no one, but the user, should have access to the directory */
495         ret = IsProtected (path, (S_IRGRP | S_IWGRP | S_IXGRP | S_IROTH | S_IWOTH | S_IXOTH));
496         return (MonoBoolean)ret;
497 }
498
499 MonoBoolean
500 ves_icall_Mono_Security_Cryptography_KeyPairPersistence_ProtectMachine (MonoString *path)
501 {
502         gboolean ret = FALSE;
503
504         /* read/write to owner, read to everyone else */
505         ret = Protect (path, (S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH), (S_IXUSR | S_IXGRP | S_IXOTH));
506         return (MonoBoolean)ret;
507 }
508
509 MonoBoolean
510 ves_icall_Mono_Security_Cryptography_KeyPairPersistence_ProtectUser (MonoString *path)
511 {
512         gboolean ret = FALSE;
513         
514         /* read/write to user, no access to everyone else */
515         ret = Protect (path, (S_IRUSR | S_IWUSR), S_IXUSR);
516         return (MonoBoolean)ret;
517 }
518 #endif /* !HOST_WIN32 */
519
520 /*
521  * Returns TRUE if there is "something" where the Authenticode signature is 
522  * normally located. Returns FALSE is data directory is empty.
523  *
524  * Note: Neither the structure nor the signature is verified by this function.
525  */
526 MonoBoolean
527 ves_icall_System_Security_Policy_Evidence_IsAuthenticodePresent (MonoReflectionAssembly *refass)
528 {
529         if (refass && refass->assembly && refass->assembly->image) {
530                 return (MonoBoolean)mono_image_has_authenticode_entry (refass->assembly->image);
531         }
532         return FALSE;
533 }
534
535
536 /* System.Security.SecureString related internal calls */
537
538 static MonoImage *system_security_assembly = NULL;
539
540 void
541 ves_icall_System_Security_SecureString_DecryptInternal (MonoArray *data, MonoObject *scope)
542 {
543         MonoError error;
544         invoke_protected_memory_method (data, scope, FALSE, &error);
545         mono_error_set_pending_exception (&error);
546 }
547 void
548 ves_icall_System_Security_SecureString_EncryptInternal (MonoArray* data, MonoObject *scope)
549 {
550         MonoError error;
551         invoke_protected_memory_method (data, scope, TRUE, &error);
552         mono_error_set_pending_exception (&error);
553 }
554
555 void invoke_protected_memory_method (MonoArray *data, MonoObject *scope, gboolean encrypt, MonoError *error)
556 {
557         MonoClass *klass;
558         MonoMethod *method;
559         void *params [2];
560
561         mono_error_init (error);
562         
563         if (system_security_assembly == NULL) {
564                 system_security_assembly = mono_image_loaded ("System.Security");
565                 if (!system_security_assembly) {
566                         MonoAssembly *sa = mono_assembly_open ("System.Security.dll", NULL);
567                         if (!sa)
568                                 g_assert_not_reached ();
569                         system_security_assembly = mono_assembly_get_image (sa);
570                 }
571         }
572
573         klass = mono_class_load_from_name (system_security_assembly,
574                                                                   "System.Security.Cryptography", "ProtectedMemory");
575         method = mono_class_get_method_from_name (klass, encrypt ? "Protect" : "Unprotect", 2);
576         params [0] = data;
577         params [1] = scope; /* MemoryProtectionScope.SameProcess */
578
579         mono_runtime_invoke_checked (method, NULL, params, error);
580 }