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