* Author:
* Sebastien Pouliot <sebastien@ximian.com>
*
- * Copyright (C) 2004 Novell, Inc (http://www.novell.com)
+ * Copyright (C) 2004-2005 Novell, Inc (http://www.novell.com)
*/
#include "declsec.h"
+#include "mini.h"
/*
* Does the methods (or it's class) as any declarative security attribute ?
MonoBoolean
mono_method_has_declsec (MonoMethod *method)
{
- if (method->wrapper_type != MONO_WRAPPER_NONE)
+ mono_jit_stats.cas_declsec_check++;
+
+ if (method->wrapper_type == MONO_WRAPPER_MANAGED_TO_NATIVE) {
+ method = mono_marshal_method_from_wrapper (method);
+ if (!method)
+ return FALSE;
+ } else if (method->wrapper_type != MONO_WRAPPER_NONE)
return FALSE;
-
+
if ((method->klass->flags & TYPE_ATTRIBUTE_HAS_SECURITY) || (method->flags & METHOD_ATTRIBUTE_HAS_SECURITY)) {
/* ignore static constructors */
if (strcmp (method->name, ".cctor"))
}
frame->method = mono_method_get_object (domain, jinfo->method, NULL);
+ frame->domain = domain->domain;
/* stack modifiers on methods have priority on (i.e. replaces) modifiers on class */
return frame;
}
+
+
+/*
+ * Execute any LinkDemand, NonCasLinkDemand, LinkDemandChoice declarative
+ * security attribute present on the called method or it's class.
+ *
+ * @domain The current application domain
+ * @caller The method calling
+ * @callee The called method.
+ * return value: TRUE if a security violation is detection, FALSE otherwise.
+ *
+ * Note: The execution is done in managed code in SecurityManager.LinkDemand
+ */
+static gboolean
+mono_declsec_linkdemand_standard (MonoDomain *domain, MonoMethod *caller, MonoMethod *callee)
+{
+ MonoDeclSecurityActions linkclass, linkmethod;
+
+ mono_jit_stats.cas_linkdemand++;
+
+ if (mono_declsec_get_linkdemands (callee, &linkclass, &linkmethod)) {
+ MonoAssembly *assembly = mono_image_get_assembly (caller->klass->image);
+ MonoReflectionAssembly *refass = (MonoReflectionAssembly*) mono_assembly_get_object (domain, assembly);
+ MonoSecurityManager *secman = mono_security_manager_get_methods ();
+ MonoObject *res;
+ gpointer args [3];
+
+ args [0] = refass;
+ args [1] = &linkclass;
+ args [2] = &linkmethod;
+
+ res = mono_runtime_invoke (secman->linkdemand, NULL, args, NULL);
+ return !(*(MonoBoolean *) mono_object_unbox(res));
+ }
+ return FALSE;
+}
+
+/*
+ * Ensure that the restrictions for partially trusted code are satisfied.
+ *
+ * @domain The current application domain
+ * @assembly The assembly to query
+ * return value: TRUE if the assembly is runnning at FullTrust, FALSE otherwise.
+ */
+static gboolean
+mono_declsec_is_assembly_fulltrust (MonoDomain *domain, MonoAssembly *assembly)
+{
+ if (!MONO_SECMAN_FLAG_INIT (assembly->fulltrust)) {
+ MonoReflectionAssembly *refass = (MonoReflectionAssembly*) mono_assembly_get_object (domain, assembly);
+ MonoSecurityManager *secman = mono_security_manager_get_methods ();
+
+ if (secman && refass) {
+ MonoObject *res;
+ gpointer args [1];
+ args [0] = refass;
+
+ res = mono_runtime_invoke (secman->linkdemandfulltrust, NULL, args, NULL);
+ if (*(MonoBoolean *) mono_object_unbox(res)) {
+ /* keep this value cached as it will be used very often */
+ MONO_SECMAN_FLAG_SET_VALUE (assembly->fulltrust, TRUE);
+ return TRUE;
+ }
+ }
+
+ MONO_SECMAN_FLAG_SET_VALUE (assembly->fulltrust, FALSE);
+ return FALSE;
+ }
+
+ return MONO_SECMAN_FLAG_GET_VALUE (assembly->fulltrust);
+}
+
+/*
+ * Ensure that the restrictions for partially trusted code are satisfied.
+ *
+ * @domain The current application domain
+ * @caller The method calling
+ * @callee The called method
+ * return value: TRUE if a security violation is detected, FALSE otherwise.
+ *
+ * If callee's assembly is strongnamed and doesn't have an
+ * [AllowPartiallyTrustedCallers] attribute then we must enforce a LinkDemand
+ * for FullTrust on all public/protected methods on public class.
+ *
+ * Note: APTC is only effective on stongnamed assemblies.
+ */
+static gboolean
+mono_declsec_linkdemand_aptc (MonoDomain *domain, MonoMethod *caller, MonoMethod *callee)
+{
+ MonoSecurityManager* secman = NULL;
+ MonoAssembly *assembly;
+ guint32 size = 0;
+
+ mono_jit_stats.cas_linkdemand_aptc++;
+
+ /* A - Applicable only if we're calling into *another* assembly */
+ if (caller->klass->image == callee->klass->image)
+ return FALSE;
+
+ /* B - Applicable if we're calling a public/protected method from a public class */
+ if (!(callee->klass->flags & TYPE_ATTRIBUTE_PUBLIC) || !(callee->flags & FIELD_ATTRIBUTE_PUBLIC))
+ return FALSE;
+
+ /* C - Applicable if the callee's assembly is strongnamed */
+ if ((mono_image_get_public_key (callee->klass->image, &size) == NULL) || (size < MONO_ECMA_KEY_LENGTH))
+ return FALSE;
+
+ /* D - the callee's assembly must have [AllowPartiallyTrustedCallers] */
+ assembly = mono_image_get_assembly (callee->klass->image);
+ if (!MONO_SECMAN_FLAG_INIT (assembly->aptc)) {
+ MonoCustomAttrInfo* cinfo = mono_custom_attrs_from_assembly (assembly);
+ gboolean result = FALSE;
+ secman = mono_security_manager_get_methods ();
+ if (secman && cinfo) {
+ /* look for AllowPartiallyTrustedCallersAttribute */
+ result = mono_custom_attrs_has_attr (cinfo, secman->allowpartiallytrustedcallers);
+ }
+ MONO_SECMAN_FLAG_SET_VALUE (assembly->aptc, result);
+ }
+
+ if (MONO_SECMAN_FLAG_GET_VALUE (assembly->aptc))
+ return FALSE;
+
+ /* E - the caller's assembly must have full trust permissions */
+ assembly = mono_image_get_assembly (caller->klass->image);
+ if (mono_declsec_is_assembly_fulltrust (domain, assembly))
+ return FALSE;
+
+ /* g_warning ("FAILURE *** JIT LinkDemand APTC check *** %s.%s calls into %s.%s",
+ caller->klass->name, caller->name, callee->klass->name, callee->name); */
+
+ return TRUE; /* i.e. throw new SecurityException(); */
+}
+
+/*
+ * Ensure that the restrictions for calling native code are satisfied.
+ *
+ * @domain The current application domain
+ * @caller The method calling
+ * @native The native method called
+ * return value: TRUE if a security violation is detected, FALSE otherwise.
+ *
+ * Executing Platform Invokes (P/Invoke) is a is a restricted operation.
+ * The security policy must allow (SecurityPermissionFlag.UnmanagedCode)
+ * an assembly to do this.
+ *
+ * This LinkDemand case is special because it only needs to call managed
+ * code once per assembly. Further calls on this assembly will use a cached
+ * flag for better performance. This is not done before the first call (e.g.
+ * when loading the assembly) because that would break the lazy policy
+ * evaluation that Mono use (another time saving optimization).
+ *
+ * Note: P/Invoke checks are ALWAYS (1) done at JIT time (as a LinkDemand).
+ * They are also checked at runtime, using a Demand (stack walk), unless the
+ * method or it's class has a [SuppressUnmanagedCodeSecurity] attribute.
+ *
+ * (1) well as long as the security manager is active (i.e. --security)
+ */
+static gboolean
+mono_declsec_linkdemand_pinvoke (MonoDomain *domain, MonoMethod *caller, MonoMethod *native)
+{
+ MonoAssembly *assembly = mono_image_get_assembly (caller->klass->image);
+
+ mono_jit_stats.cas_linkdemand_pinvoke++;
+
+ /* Check for P/Invoke flag for the assembly */
+ if (!MONO_SECMAN_FLAG_INIT (assembly->unmanaged)) {
+ /* Check if we know (and have) or FullTrust status */
+ if (MONO_SECMAN_FLAG_INIT (assembly->fulltrust) && MONO_SECMAN_FLAG_GET_VALUE (assembly->fulltrust)) {
+ /* FullTrust includes UnmanagedCode permission */
+ MONO_SECMAN_FLAG_SET_VALUE (assembly->unmanaged, TRUE);
+ return FALSE;
+ } else {
+ MonoReflectionAssembly *refass = (MonoReflectionAssembly*) mono_assembly_get_object (domain, assembly);
+ MonoSecurityManager* secman = mono_security_manager_get_methods ();
+ if (secman && refass) {
+ MonoObject *res;
+ gpointer args [1];
+ args [0] = refass;
+
+ res = mono_runtime_invoke (secman->linkdemandunmanaged, NULL, args, NULL);
+ if (*(MonoBoolean *) mono_object_unbox(res)) {
+ MONO_SECMAN_FLAG_SET_VALUE (assembly->unmanaged, TRUE);
+ return FALSE;
+ }
+ }
+ }
+
+ MONO_SECMAN_FLAG_SET_VALUE (assembly->unmanaged, FALSE);
+ }
+
+ if (MONO_SECMAN_FLAG_GET_VALUE (assembly->unmanaged))
+ return FALSE;
+
+ /* g_warning ("FAILURE *** JIT LinkDemand P/Invoke check *** %s.%s calls into %s.%s",
+ caller->klass->name, caller->name, native->klass->name, native->name); */
+
+ return TRUE; /* i.e. throw new SecurityException(); */
+}
+
+/*
+ * Ensure that the restrictions for calling internal calls are satisfied.
+ *
+ * @domain The current application domain
+ * @caller The method calling
+ * @icall The internal call method
+ * return value: TRUE if a security violation is detected, FALSE otherwise.
+ *
+ * We can't trust the icall flags/iflags as it comes from the assembly
+ * that we may want to restrict and we do not have the public/restricted
+ * information about icalls in the runtime. Actually it is not so bad
+ * as the CLR 2.0 doesn't enforce that restriction anymore.
+ *
+ * So we'll limit the icalls to originate from ECMA signed assemblies
+ * (as this is required for partial trust scenarios) - or - assemblies that
+ * have FullTrust.
+ */
+static gboolean
+mono_declsec_linkdemand_icall (MonoDomain *domain, MonoMethod *caller, MonoMethod *icall)
+{
+ MonoAssembly *assembly;
+
+ mono_jit_stats.cas_linkdemand_icall++;
+
+ /* check if the _icall_ is defined inside an ECMA signed assembly */
+ assembly = mono_image_get_assembly (icall->klass->image);
+ if (!MONO_SECMAN_FLAG_INIT (assembly->ecma)) {
+ guint32 size = 0;
+ const char *pk = mono_image_get_public_key (icall->klass->image, &size);
+ MONO_SECMAN_FLAG_SET_VALUE (assembly->ecma, mono_is_ecma_key (pk, size));
+ }
+
+ if (MONO_SECMAN_FLAG_GET_VALUE (assembly->ecma))
+ return FALSE;
+
+ /* else check if the _calling_ assembly is running at FullTrust */
+ assembly = mono_image_get_assembly (caller->klass->image);
+ return !mono_declsec_is_assembly_fulltrust (domain, assembly);
+}
+
+
+/*
+ * Before the JIT can link (call) into a method the following security checks
+ * must be done:
+ *
+ * We check that the code has the permission to link when:
+ * 1. the code try to call an internal call;
+ * 2. the code try to p/invoke to unmanaged code;
+ * 3. the code try to call trusted code without being trusted itself -
+ * or without the trusted code permission (APTC);
+ * 4. the code try to call managed code protected by a LinkDemand security
+ * attribute
+ *
+ * Failures result in a SecurityException being thrown (later in mini code).
+ *
+ * Note: Some checks are duplicated in managed code to deal when reflection is
+ * used to call the methods.
+ */
+guint32
+mono_declsec_linkdemand (MonoDomain *domain, MonoMethod *caller, MonoMethod *callee)
+{
+ guint32 violation = MONO_JIT_SECURITY_OK;
+
+ /* short-circuit corlib as it is fully trusted (within itself)
+ * and because this cause major recursion headaches */
+ if ((caller->klass->image == mono_defaults.corlib) && (callee->klass->image == mono_defaults.corlib))
+ return violation;
+
+ /* next, the special (implied) linkdemand */
+
+ if (callee->iflags & METHOD_IMPL_ATTRIBUTE_INTERNAL_CALL) {
+ /* restrict internal calls into the runtime */
+ if (mono_declsec_linkdemand_icall (domain, caller, callee))
+ violation = MONO_JIT_LINKDEMAND_ECMA;
+ } else if (callee->flags & METHOD_ATTRIBUTE_PINVOKE_IMPL) {
+ /* CAS can restrict p/invoke calls with the assembly granted permissions */
+ if (mono_declsec_linkdemand_pinvoke (domain, caller, callee))
+ violation = MONO_JIT_LINKDEMAND_PINVOKE;
+ }
+
+ if (!violation) {
+ /* check if we allow partially trusted callers in trusted (signed) assemblies */
+ if (mono_declsec_linkdemand_aptc (domain, caller, callee))
+ violation = MONO_JIT_LINKDEMAND_APTC;
+ }
+
+ /* then the "normal" LinkDemand (only when called method has declarative security) */
+ if (!violation && mono_method_has_declsec (callee)) {
+ /* LinkDemand are ignored for static constructors (ensured by calling mono_method_has_declsec) */
+ if (mono_declsec_linkdemand_standard (domain, caller, callee))
+ violation = MONO_JIT_LINKDEMAND_PERMISSION;
+ }
+
+ /* if (violation) g_warning ("mono_declsec_linkdemand violation reported %d", violation); */
+ return violation;
+}