[runtime] Use mono_string_new_checked instead of mono_string_new
[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 ves_icall_System_Security_Principal_WindowsIdentity_GetCurrentToken (void)
220 {
221         return GINT_TO_POINTER (geteuid ());
222 }
223
224 static gint32
225 internal_get_token_name (gpointer token, gunichar2 ** uniname)
226 {
227         gint32 size = 0;
228
229         gchar *uname = GetTokenName ((uid_t) GPOINTER_TO_INT (token));
230
231         if (uname) {
232                 size = strlen (uname);
233                 *uniname = g_utf8_to_utf16 (uname, size, NULL, NULL, NULL);
234                 g_free (uname);
235         }
236
237         return size;
238 }
239
240 MonoString*
241 ves_icall_System_Security_Principal_WindowsIdentity_GetTokenName (gpointer token)
242 {
243         MonoError error;
244         MonoString *result = NULL;
245         gunichar2 *uniname = NULL;
246         gint32 size = 0;
247
248         error_init (&error);
249
250         size = internal_get_token_name (token, &uniname);
251
252         if (size > 0) {
253                 result = mono_string_new_utf16_checked (mono_domain_get (), uniname, size, &error);
254         }
255         else
256                 result = mono_string_new_checked (mono_domain_get (), "", &error);
257
258         if (uniname)
259                 g_free (uniname);
260
261         mono_error_set_pending_exception (&error);
262         return result;
263 }
264 #endif  /* !HOST_WIN32 */
265
266 #ifndef HOST_WIN32
267 gpointer
268 ves_icall_System_Security_Principal_WindowsIdentity_GetUserToken (MonoString *username)
269 {
270         gpointer token = (gpointer)-2;
271
272 #ifdef HAVE_PWD_H
273
274 #ifdef HAVE_GETPWNAM_R
275         struct passwd pwd;
276         size_t fbufsize;
277         gchar *fbuf;
278         gint32 retval;
279 #endif
280         struct passwd *p;
281         gchar *utf8_name;
282         gboolean result;
283
284         utf8_name = mono_unicode_to_external (mono_string_chars (username));
285
286 #ifdef HAVE_GETPWNAM_R
287 #ifdef _SC_GETPW_R_SIZE_MAX
288         fbufsize = mono_sysconf (_SC_GETPW_R_SIZE_MAX);
289 #else
290         fbufsize = MONO_SYSCONF_DEFAULT_SIZE;
291 #endif
292
293         fbuf = (gchar *)g_malloc0 (fbufsize);
294         retval = getpwnam_r (utf8_name, &pwd, fbuf, fbufsize, &p);
295         result = ((retval == 0) && (p == &pwd));
296 #else
297         /* default to non thread-safe but posix compliant function */
298         p = getpwnam (utf8_name);
299         result = (p != NULL);
300 #endif
301
302         if (result) {
303                 token = GINT_TO_POINTER (p->pw_uid);
304         }
305
306 #ifdef HAVE_GETPWNAM_R
307         g_free (fbuf);
308 #endif
309         g_free (utf8_name);
310
311 #endif /* HAVE_PWD_H */
312
313         return token;
314 }
315 #endif /* HOST_WIN32 */
316
317 /* http://www.dotnet247.com/247reference/msgs/39/195403.aspx
318 // internal static string[] WindowsIdentity._GetRoles (IntPtr token)
319 */
320
321 #ifndef HOST_WIN32
322 MonoArray*
323 ves_icall_System_Security_Principal_WindowsIdentity_GetRoles (gpointer token)
324 {
325         MonoError error;
326         MonoArray *array = NULL;
327         MonoDomain *domain = mono_domain_get ();
328
329         /* POSIX-compliant systems should use IsMemberOfGroupId or IsMemberOfGroupName */
330         g_warning ("WindowsIdentity._GetRoles should never be called on POSIX");
331
332         if (!array) {
333                 /* return empty array of string, i.e. string [0] */
334                 array = mono_array_new_checked (domain, mono_get_string_class (), 0, &error);
335                 mono_error_set_pending_exception (&error);
336         }
337         return array;
338 }
339 #endif /* !HOST_WIN32 */
340
341 /* System.Security.Principal.WindowsImpersonationContext */
342
343 #ifndef HOST_WIN32
344 gboolean
345 ves_icall_System_Security_Principal_WindowsImpersonationContext_CloseToken (gpointer token)
346 {
347         return TRUE;
348 }
349 #endif /* !HOST_WIN32 */
350
351 #ifndef HOST_WIN32
352 gpointer
353 ves_icall_System_Security_Principal_WindowsImpersonationContext_DuplicateToken (gpointer token)
354 {
355         return token;
356 }
357 #endif /* !HOST_WIN32 */
358
359 #if G_HAVE_API_SUPPORT(HAVE_CLASSIC_WINAPI_SUPPORT)
360 gboolean
361 ves_icall_System_Security_Principal_WindowsImpersonationContext_SetCurrentToken (gpointer token)
362 {
363 #ifdef HOST_WIN32
364         return (ImpersonateLoggedOnUser (token) != 0);
365 #else
366         uid_t itoken = (uid_t) GPOINTER_TO_INT (token);
367 #ifdef HAVE_SETRESUID
368         if (setresuid (-1, itoken, getuid ()) < 0)
369                 return FALSE;
370 #endif
371         return geteuid () == itoken;
372 #endif
373 }
374
375 gboolean
376 ves_icall_System_Security_Principal_WindowsImpersonationContext_RevertToSelf (void)
377 {
378 #ifdef HOST_WIN32
379         return (RevertToSelf () != 0);
380 #else
381 #ifdef HAVE_GETRESUID
382         uid_t ruid, euid;
383 #endif
384         uid_t suid = -1;
385
386 #ifdef HAVE_GETRESUID
387         if (getresuid (&ruid, &euid, &suid) < 0)
388                 return FALSE;
389 #endif
390 #ifdef HAVE_SETRESUID
391         if (setresuid (-1, suid, -1) < 0)
392                 return FALSE;
393 #else
394         return TRUE;
395 #endif
396         return geteuid () == suid;
397 #endif
398 }
399 #endif /* G_HAVE_API_SUPPORT(HAVE_CLASSIC_WINAPI_SUPPORT) */
400
401 /* System.Security.Principal.WindowsPrincipal */
402
403 #ifndef HOST_WIN32
404 gboolean
405 ves_icall_System_Security_Principal_WindowsPrincipal_IsMemberOfGroupId (gpointer user, gpointer group)
406 {
407         gboolean result = FALSE;
408
409 #ifdef HAVE_GRP_H
410
411 #ifdef HAVE_GETGRGID_R
412         struct group grp;
413         size_t fbufsize;
414         gchar *fbuf;
415         gint32 retval;
416 #endif
417         struct group *g = NULL;
418
419 #ifdef HAVE_GETGRGID_R
420 #ifdef _SC_GETGR_R_SIZE_MAX
421         fbufsize = mono_sysconf (_SC_GETGR_R_SIZE_MAX);
422 #else
423         fbufsize = MONO_SYSCONF_DEFAULT_SIZE;
424 #endif
425         fbuf = (gchar *)g_malloc0 (fbufsize);
426         retval = getgrgid_r ((gid_t) GPOINTER_TO_INT (group), &grp, fbuf, fbufsize, &g);
427         result = ((retval == 0) && (g == &grp));
428 #else
429         /* default to non thread-safe but posix compliant function */
430         g = getgrgid ((gid_t) GPOINTER_TO_INT (group));
431         result = (g != NULL);
432 #endif
433
434         if (result) {
435                 result = IsMemberOf ((uid_t) GPOINTER_TO_INT (user), g);
436         }
437
438 #ifdef HAVE_GETGRGID_R
439         g_free (fbuf);
440 #endif
441
442 #endif /* HAVE_GRP_H */
443
444         return result;
445 }
446
447 gboolean
448 ves_icall_System_Security_Principal_WindowsPrincipal_IsMemberOfGroupName (gpointer user, MonoString *group)
449 {
450         gboolean result = FALSE;
451
452 #ifdef HAVE_GRP_H
453
454         gchar *utf8_groupname;
455
456         utf8_groupname = mono_unicode_to_external (mono_string_chars (group));
457         if (utf8_groupname) {
458                 struct group *g = NULL;
459 #ifdef HAVE_GETGRNAM_R
460                 struct group grp;
461                 gchar *fbuf;
462                 gint32 retval;
463 #ifdef _SC_GETGR_R_SIZE_MAX
464                 size_t fbufsize = mono_sysconf (_SC_GETGR_R_SIZE_MAX);
465 #else
466                 size_t fbufsize = MONO_SYSCONF_DEFAULT_SIZE;
467 #endif
468                 fbuf = (gchar *)g_malloc0 (fbufsize);
469                 retval = getgrnam_r (utf8_groupname, &grp, fbuf, fbufsize, &g);
470                 result = ((retval == 0) && (g == &grp));
471 #else
472                 /* default to non thread-safe but posix compliant function */
473                 g = getgrnam (utf8_groupname);
474                 result = (g != NULL);
475 #endif
476
477                 if (result) {
478                         result = IsMemberOf ((uid_t) GPOINTER_TO_INT (user), g);
479                 }
480
481 #ifdef HAVE_GETGRNAM_R
482                 g_free (fbuf);
483 #endif
484                 g_free (utf8_groupname);
485         }
486
487 #endif /* HAVE_GRP_H */
488
489         return result;
490 }
491 #endif /* !HOST_WIN32 */
492
493 /* Mono.Security.Cryptography IO related internal calls */
494
495 #ifndef HOST_WIN32
496 static gboolean
497 IsProtected (MonoString *path, gint32 protection) 
498 {
499         gboolean result = FALSE;
500         gchar *utf8_name = mono_unicode_to_external (mono_string_chars (path));
501         if (utf8_name) {
502                 struct stat st;
503                 if (stat (utf8_name, &st) == 0) {
504                         result = (((st.st_mode & 0777) & protection) == 0);
505                 }
506                 g_free (utf8_name);
507         }
508         return result;
509 }
510
511
512 static gboolean
513 Protect (MonoString *path, gint32 file_mode, gint32 add_dir_mode)
514 {
515         gboolean result = FALSE;
516         gchar *utf8_name = mono_unicode_to_external (mono_string_chars (path));
517         if (utf8_name) {
518                 struct stat st;
519                 if (stat (utf8_name, &st) == 0) {
520                         int mode = file_mode;
521                         if (st.st_mode & S_IFDIR)
522                                 mode |= add_dir_mode;
523                         result = (chmod (utf8_name, mode) == 0);
524                 }
525                 g_free (utf8_name);
526         }
527         return result;
528 }
529
530 MonoBoolean
531 ves_icall_Mono_Security_Cryptography_KeyPairPersistence_CanSecure (MonoString *root)
532 {
533         /* we assume some kind of security is applicable outside Windows */
534         return TRUE;
535 }
536
537 MonoBoolean
538 ves_icall_Mono_Security_Cryptography_KeyPairPersistence_IsMachineProtected (MonoString *path)
539 {
540         gboolean ret = FALSE;
541
542         /* no one, but the owner, should have write access to the directory */
543         ret = IsProtected (path, (S_IWGRP | S_IWOTH));
544         return (MonoBoolean)ret;
545 }
546
547 MonoBoolean
548 ves_icall_Mono_Security_Cryptography_KeyPairPersistence_IsUserProtected (MonoString *path)
549 {
550         gboolean ret = FALSE;
551
552         /* no one, but the user, should have access to the directory */
553         ret = IsProtected (path, (S_IRGRP | S_IWGRP | S_IXGRP | S_IROTH | S_IWOTH | S_IXOTH));
554         return (MonoBoolean)ret;
555 }
556
557 MonoBoolean
558 ves_icall_Mono_Security_Cryptography_KeyPairPersistence_ProtectMachine (MonoString *path)
559 {
560         gboolean ret = FALSE;
561
562         /* read/write to owner, read to everyone else */
563         ret = Protect (path, (S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH), (S_IXUSR | S_IXGRP | S_IXOTH));
564         return (MonoBoolean)ret;
565 }
566
567 MonoBoolean
568 ves_icall_Mono_Security_Cryptography_KeyPairPersistence_ProtectUser (MonoString *path)
569 {
570         gboolean ret = FALSE;
571         
572         /* read/write to user, no access to everyone else */
573         ret = Protect (path, (S_IRUSR | S_IWUSR), S_IXUSR);
574         return (MonoBoolean)ret;
575 }
576 #endif /* !HOST_WIN32 */
577
578 /*
579  * Returns TRUE if there is "something" where the Authenticode signature is 
580  * normally located. Returns FALSE is data directory is empty.
581  *
582  * Note: Neither the structure nor the signature is verified by this function.
583  */
584 MonoBoolean
585 ves_icall_System_Security_Policy_Evidence_IsAuthenticodePresent (MonoReflectionAssemblyHandle refass, MonoError *error)
586 {
587         error_init (error);
588         if (MONO_HANDLE_IS_NULL (refass))
589                 return FALSE;
590         MonoAssembly *assembly = MONO_HANDLE_GETVAL (refass, assembly);
591         if (assembly && assembly->image) {
592                 return (MonoBoolean)mono_image_has_authenticode_entry (assembly->image);
593         }
594         return FALSE;
595 }
596
597
598 /* System.Security.SecureString related internal calls */
599
600 static MonoImage *system_security_assembly = NULL;
601
602 void
603 ves_icall_System_Security_SecureString_DecryptInternal (MonoArray *data, MonoObject *scope)
604 {
605         MonoError error;
606         invoke_protected_memory_method (data, scope, FALSE, &error);
607         mono_error_set_pending_exception (&error);
608 }
609 void
610 ves_icall_System_Security_SecureString_EncryptInternal (MonoArray* data, MonoObject *scope)
611 {
612         MonoError error;
613         invoke_protected_memory_method (data, scope, TRUE, &error);
614         mono_error_set_pending_exception (&error);
615 }
616
617 void invoke_protected_memory_method (MonoArray *data, MonoObject *scope, gboolean encrypt, MonoError *error)
618 {
619         MonoClass *klass;
620         MonoMethod *method;
621         void *params [2];
622
623         error_init (error);
624         
625         if (system_security_assembly == NULL) {
626                 system_security_assembly = mono_image_loaded ("System.Security");
627                 if (!system_security_assembly) {
628                         MonoAssembly *sa = mono_assembly_open_predicate ("System.Security.dll", FALSE, FALSE, NULL, NULL, NULL);
629                         if (!sa)
630                                 g_assert_not_reached ();
631                         system_security_assembly = mono_assembly_get_image (sa);
632                 }
633         }
634
635         klass = mono_class_load_from_name (system_security_assembly,
636                                                                   "System.Security.Cryptography", "ProtectedMemory");
637         method = mono_class_get_method_from_name (klass, encrypt ? "Protect" : "Unprotect", 2);
638         params [0] = data;
639         params [1] = scope; /* MemoryProtectionScope.SameProcess */
640
641         mono_runtime_invoke_checked (method, NULL, params, error);
642 }