[sgen] Never directly pin objects on the M&S major heap.
[mono.git] / mono / metadata / security-core-clr.c
index 5eb978306996bc20d37565405bafa7a33d412026..0e165611a5363fea05698cf692d831e6b2d7e2b9 100644 (file)
@@ -48,6 +48,13 @@ security_safe_critical_attribute (void)
        return class;
 }
 
+/* sometime we get a NULL (not found) caller (e.g. get_reflection_caller) */
+static char*
+get_method_full_name (MonoMethod * method)
+{
+       return method ? mono_method_full_name (method, TRUE) : g_strdup ("'no caller found'");
+}
+
 /*
  * set_type_load_exception_type
  *
@@ -82,8 +89,8 @@ set_type_load_exception_type (const char *format, MonoClass *class)
 static void
 set_type_load_exception_methods (const char *format, MonoMethod *override, MonoMethod *base)
 {
-       char *method_name = mono_method_full_name (override, TRUE);
-       char *base_name = mono_method_full_name (base, TRUE);
+       char *method_name = get_method_full_name (override);
+       char *base_name = get_method_full_name (base);
        char *message = g_strdup_printf (format, method_name, base_name);
 
        g_free (base_name);
@@ -103,6 +110,7 @@ get_default_ctor (MonoClass *klass)
 {
        int i;
 
+       mono_class_setup_methods (klass);
        if (!klass->methods)
                return NULL;
 
@@ -110,6 +118,9 @@ get_default_ctor (MonoClass *klass)
                MonoMethodSignature *sig;
                MonoMethod *method = klass->methods [i];
 
+               if (!method)
+                       continue;
+
                if ((method->flags & METHOD_ATTRIBUTE_SPECIAL_NAME) == 0)
                        continue;
                if ((method->name[0] != '.') || strcmp (".ctor", method->name))
@@ -137,10 +148,11 @@ get_default_ctor (MonoClass *klass)
  *     Reference: http://msdn.microsoft.com/en-us/magazine/cc765416.aspx#id0190030
  *
  *     Furthermore a class MUST have a default constructor if its base 
- *     class has a non-transparent default constructor. The same 
- *     inheritance rule applies to both default constructors.
+ *     class has a non-transparent, public or protected, default constructor. 
+ *     The same inheritance rule applies to both default constructors.
  *
  *     Reference: message from a SecurityException in SL4RC
+ *     Reference: fxcop CA2132 rule
  */
 void
 mono_security_core_clr_check_inheritance (MonoClass *class)
@@ -159,12 +171,15 @@ mono_security_core_clr_check_inheritance (MonoClass *class)
                        "Inheritance failure for type %s. Parent class %s is more restricted.",
                        class);
        } else {
-               class_level = mono_security_core_clr_method_level (get_default_ctor (class), FALSE);
-               parent_level = mono_security_core_clr_method_level (get_default_ctor (parent), FALSE);
-               if (class_level < parent_level) {
-                       set_type_load_exception_type (
-                               "Inheritance failure for type %s. Default constructor security mismatch with %s.",
-                               class);
+               MonoMethod *parent_ctor = get_default_ctor (parent);
+               if (parent_ctor && ((parent_ctor->flags & METHOD_ATTRIBUTE_PUBLIC) != 0)) {
+                       class_level = mono_security_core_clr_method_level (get_default_ctor (class), FALSE);
+                       parent_level = mono_security_core_clr_method_level (parent_ctor, FALSE);
+                       if (class_level < parent_level) {
+                               set_type_load_exception_type (
+                                       "Inheritance failure for type %s. Default constructor security mismatch with %s.",
+                                       class);
+                       }
                }
        }
 }
@@ -287,7 +302,7 @@ get_caller_no_reflection_related (MonoMethod *m, gint32 no, gint32 ilo, gboolean
  * 
  *     Walk to the first managed method outside:
  *     - System.Reflection* namespaces
- *     - System.[MulticastDelegate]Delegate or Activator type
+ *     - System.[Multicast]Delegate or Activator type
  *     - platform code
  *     and return a pointer to its MonoMethod.
  *
@@ -299,11 +314,140 @@ get_reflection_caller (void)
        MonoMethod *m = NULL;
        mono_stack_walk_no_il (get_caller_no_reflection_related, &m);
        if (G_UNLIKELY (!m)) {
-               mono_trace (G_LOG_LEVEL_ERROR, MONO_TRACE_SECURITY, "No caller outside reflection was found");
+               mono_trace (G_LOG_LEVEL_WARNING, MONO_TRACE_SECURITY, "No caller outside reflection was found");
        }
        return m;
 }
 
+typedef struct {
+       int depth;
+       MonoMethod *caller;
+} ElevatedTrustCookie;
+
+/*
+ * get_caller_of_elevated_trust_code
+ *
+ *     Stack walk to find who is calling code requiring Elevated Trust.
+ *     If a critical method is found then the caller is platform code
+ *     and has elevated trust, otherwise (transparent) a check needs to
+ *     be done (on the managed side) to determine if the application is
+ *     running with elevated permissions.
+ */
+static gboolean
+get_caller_of_elevated_trust_code (MonoMethod *m, gint32 no, gint32 ilo, gboolean managed, gpointer data)
+{
+       ElevatedTrustCookie *cookie = data;
+
+       /* skip unmanaged frames and wrappers */
+       if (!managed || (m->wrapper_type != MONO_WRAPPER_NONE))
+               return FALSE;
+
+       /* end stack walk if we find ourselves outside platform code (we won't find critical code anymore) */
+       if (!mono_security_core_clr_is_platform_image (m->klass->image)) {
+               cookie->caller = m;
+               return TRUE;
+       }
+
+       switch (cookie->depth) {
+       /* while depth == 0 look for SecurityManager::[Check|Ensure]ElevatedPermissions */
+       case 0:
+               if (strcmp (m->klass->name_space, "System.Security"))
+                       return FALSE;
+               if (strcmp (m->klass->name, "SecurityManager"))
+                       return FALSE;
+               if ((strcmp (m->name, "EnsureElevatedPermissions")) && strcmp (m->name, "CheckElevatedPermissions"))
+                       return FALSE;
+               cookie->depth = 1;
+               break;
+       /* while depth == 1 look for the caller to SecurityManager::[Check|Ensure]ElevatedPermissions */
+       case 1:
+               /* this frame is [SecuritySafeCritical] because it calls [SecurityCritical] [Check|Ensure]ElevatedPermissions */
+               /* the next frame will contain the caller(s) we want to check */
+               cookie->depth = 2;
+               break;
+       /* while depth >= 2 look for [safe]critical caller, end stack walk if we find it  */
+       default:
+               cookie->depth++;
+               /* if the caller is transparent then we continue the stack walk */
+               if (mono_security_core_clr_method_level (m, TRUE) == MONO_SECURITY_CORE_CLR_TRANSPARENT)
+                       break;
+
+               /* Security[Safe]Critical code is always allowed to call elevated-trust code */
+               cookie->caller = m;
+               return TRUE;
+       }
+
+       return FALSE;
+}
+
+/*
+ * mono_security_core_clr_require_elevated_permissions:
+ *
+ *     Return TRUE if the caller of the current method (the code who 
+ *     called SecurityManager.get_RequiresElevatedPermissions) needs
+ *     elevated trust to perform an action.
+ *
+ *     A stack walk is done to find the callers. If one of the callers
+ *     is either [SecurityCritical] or [SecuritySafeCritical] then the
+ *     action is needed for platform code (i.e. no restriction). 
+ *     Otherwise (transparent) the requested action needs elevated trust
+ */
+gboolean
+mono_security_core_clr_require_elevated_permissions (void)
+{
+       ElevatedTrustCookie cookie;
+       cookie.depth = 0;
+       cookie.caller = NULL;
+       mono_stack_walk_no_il (get_caller_of_elevated_trust_code, &cookie);
+
+       /* return TRUE if the stack walk did not reach far enough or did not find callers */
+       if (!cookie.caller || cookie.depth < 3)
+               return TRUE;
+
+       /* return TRUE if the caller is transparent, i.e. if elevated trust is required to continue executing the method */
+       return (mono_security_core_clr_method_level (cookie.caller, TRUE) == MONO_SECURITY_CORE_CLR_TRANSPARENT);
+}
+
+
+static MonoSecurityCoreCLROptions security_core_clr_options = MONO_SECURITY_CORE_CLR_OPTIONS_DEFAULT;
+
+/**
+ * mono_security_core_clr_set_options:
+ * @options: the new options for the coreclr system to use
+ *
+ * By default, the CoreCLRs security model forbids execution trough reflection of methods not visible from the calling code.
+ * Even if the method being called is not in a platform assembly. For non moonlight CoreCLR users this restriction does not
+ * make a lot of sense, since the author could have just changed the non platform assembly to allow the method to be called.
+ * This function allows specific relaxations from the default behaviour to be set.
+ *
+ * Use MONO_SECURITY_CORE_CLR_OPTIONS_DEFAULT for the default coreclr coreclr behaviour as used in Moonlight.
+ *
+ * Use MONO_SECURITY_CORE_CLR_OPTIONS_RELAX_REFLECTION to allow transparent code to execute methods and access 
+ * fields that are not in platformcode, even if those methods and fields are private or otherwise not visible to the calling code.
+ *
+ * Use MONO_SECURITY_CORE_CLR_OPTIONS_RELAX_DELEGATE to allow delegates to be created that point at methods that are not in
+ * platformcode even if those methods and fields are private or otherwise not visible to the calling code.
+ *
+ */
+
+void 
+mono_security_core_clr_set_options (MonoSecurityCoreCLROptions options) {
+       security_core_clr_options = options;
+}
+
+/**
+ * mono_security_core_clr_get_options:
+ *
+ * Retrieves the current options used by the coreclr system.
+ */
+
+MonoSecurityCoreCLROptions
+mono_security_core_clr_get_options ()
+{
+       return security_core_clr_options;
+}
+
+
 /*
  * check_field_access:
  *
@@ -314,7 +458,17 @@ check_field_access (MonoMethod *caller, MonoClassField *field)
 {
        /* if get_reflection_caller returns NULL then we assume the caller has NO privilege */
        if (caller) {
-               MonoClass *klass = (mono_field_get_flags (field) & FIELD_ATTRIBUTE_STATIC) ? NULL : mono_field_get_parent (field);
+               MonoError error;
+               MonoClass *klass;
+
+               /* this check can occur before the field's type is resolved (and that can fail) */
+               mono_field_get_type_checked (field, &error);
+               if (!mono_error_ok (&error)) {
+                       mono_error_cleanup (&error);
+                       return FALSE;
+               }
+
+               klass = (mono_field_get_flags (field) & FIELD_ATTRIBUTE_STATIC) ? NULL : mono_field_get_parent (field);
                return mono_method_can_access_field_full (caller, field, klass);
        }
        return FALSE;
@@ -348,8 +502,8 @@ static MonoException*
 get_argument_exception (const char *format, MonoMethod *caller, MonoMethod *callee)
 {
        MonoException *ex;
-       char *caller_name = mono_method_full_name (caller, TRUE);
-       char *callee_name = mono_method_full_name (callee, TRUE);
+       char *caller_name = get_method_full_name (caller);
+       char *callee_name = get_method_full_name (callee);
        char *message = g_strdup_printf (format, caller_name, callee_name);
        g_free (callee_name);
        g_free (caller_name);
@@ -373,7 +527,7 @@ static MonoException*
 get_field_access_exception (const char *format, MonoMethod *caller, MonoClassField *field)
 {
        MonoException *ex;
-       char *caller_name = mono_method_full_name (caller, TRUE);
+       char *caller_name = get_method_full_name (caller);
        char *field_name = mono_field_full_name (field);
        char *message = g_strdup_printf (format, caller_name, field_name);
        g_free (field_name);
@@ -398,8 +552,8 @@ static MonoException*
 get_method_access_exception (const char *format, MonoMethod *caller, MonoMethod *callee)
 {
        MonoException *ex;
-       char *caller_name = mono_method_full_name (caller, TRUE);
-       char *callee_name = mono_method_full_name (callee, TRUE);
+       char *caller_name = get_method_full_name (caller);
+       char *callee_name = get_method_full_name (callee);
        char *message = g_strdup_printf (format, caller_name, callee_name);
        g_free (callee_name);
        g_free (caller_name);
@@ -428,6 +582,11 @@ mono_security_core_clr_ensure_reflection_access_field (MonoClassField *field)
        if (mono_security_core_clr_method_level (caller, TRUE) != MONO_SECURITY_CORE_CLR_TRANSPARENT)
                return;
 
+       if (mono_security_core_clr_get_options () & MONO_SECURITY_CORE_CLR_OPTIONS_RELAX_REFLECTION) {
+               if (!mono_security_core_clr_is_platform_image (mono_field_get_parent(field)->image))
+                       return;
+       }
+
        /* Transparent code cannot [get|set]value on Critical fields */
        if (mono_security_core_clr_class_level (mono_field_get_parent (field)) == MONO_SECURITY_CORE_CLR_CRITICAL) {
                mono_raise_exception (get_field_access_exception (
@@ -460,6 +619,11 @@ mono_security_core_clr_ensure_reflection_access_method (MonoMethod *method)
        if (mono_security_core_clr_method_level (caller, TRUE) != MONO_SECURITY_CORE_CLR_TRANSPARENT)
                return;
 
+       if (mono_security_core_clr_get_options () & MONO_SECURITY_CORE_CLR_OPTIONS_RELAX_REFLECTION) {
+               if (!mono_security_core_clr_is_platform_image (method->klass->image))
+                       return;
+       }
+
        /* Transparent code cannot invoke, even using reflection, Critical code */
        if (mono_security_core_clr_method_level (method, TRUE) == MONO_SECURITY_CORE_CLR_CRITICAL) {
                mono_raise_exception (get_method_access_exception (
@@ -542,7 +706,12 @@ mono_security_core_clr_ensure_delegate_creation (MonoMethod *method, gboolean th
                        "Transparent method %s cannot create a delegate on Critical method %s.", 
                        caller, method));
        }
-       
+
+       if (mono_security_core_clr_get_options () & MONO_SECURITY_CORE_CLR_OPTIONS_RELAX_DELEGATE) {
+               if (!mono_security_core_clr_is_platform_image (method->klass->image))
+                       return TRUE;
+       }
+
        /* also it cannot create the delegate on a method that is not visible from it's (caller) point of view */
        if (!check_method_access (caller, method)) {
                mono_raise_exception (get_method_access_exception (