2 * declsec.c: Declarative Security support
5 * Sebastien Pouliot <sebastien@ximian.com>
7 * Copyright (C) 2004-2005 Novell, Inc (http://www.novell.com)
14 * Does the methods (or it's class) as any declarative security attribute ?
15 * Is so are they applicable ? (e.g. static class constructor)
18 mono_method_has_declsec (MonoMethod
*method
)
20 mono_jit_stats
.cas_declsec_check
++;
22 if (method
->wrapper_type
== MONO_WRAPPER_MANAGED_TO_NATIVE
|| method
->wrapper_type
== MONO_WRAPPER_MANAGED_TO_MANAGED
) {
23 method
= mono_marshal_method_from_wrapper (method
);
26 } else if (method
->wrapper_type
!= MONO_WRAPPER_NONE
)
29 if ((method
->klass
->flags
& TYPE_ATTRIBUTE_HAS_SECURITY
) || (method
->flags
& METHOD_ATTRIBUTE_HAS_SECURITY
)) {
30 /* ignore static constructors */
31 if (strcmp (method
->name
, ".cctor"))
39 * Fill actions for the specific index (which may either be an encoded class token or
40 * an encoded method token) from the metadata image.
41 * Returns TRUE if some actions requiring code generation are present, FALSE otherwise.
44 mono_declsec_cache_stack_modifiers (MonoJitInfo
*jinfo
)
46 /* first find the stack modifiers applied to the method */
47 guint32 flags
= mono_declsec_flags_from_method (jinfo
->method
);
48 jinfo
->cas_method_assert
= (flags
& MONO_DECLSEC_FLAG_ASSERT
) != 0;
49 jinfo
->cas_method_deny
= (flags
& MONO_DECLSEC_FLAG_DENY
) != 0;
50 jinfo
->cas_method_permitonly
= (flags
& MONO_DECLSEC_FLAG_PERMITONLY
) != 0;
52 /* then find the stack modifiers applied to the class */
53 flags
= mono_declsec_flags_from_class (jinfo
->method
->klass
);
54 jinfo
->cas_class_assert
= (flags
& MONO_DECLSEC_FLAG_ASSERT
) != 0;
55 jinfo
->cas_class_deny
= (flags
& MONO_DECLSEC_FLAG_DENY
) != 0;
56 jinfo
->cas_class_permitonly
= (flags
& MONO_DECLSEC_FLAG_PERMITONLY
) != 0;
61 mono_declsec_create_frame (MonoDomain
*domain
, MonoJitInfo
*jinfo
)
63 MonoSecurityFrame
*frame
= (MonoSecurityFrame
*) mono_object_new (domain
, mono_defaults
.runtimesecurityframe_class
);
65 if (!jinfo
->cas_inited
) {
66 if (mono_method_has_declsec (jinfo
->method
)) {
67 /* Cache the stack modifiers into the MonoJitInfo structure to speed up future stack walks */
68 mono_declsec_cache_stack_modifiers (jinfo
);
70 jinfo
->cas_inited
= TRUE
;
73 MONO_OBJECT_SETREF (frame
, method
, mono_method_get_object (domain
, jinfo
->method
, NULL
));
74 MONO_OBJECT_SETREF (frame
, domain
, domain
->domain
);
76 /* stack modifiers on methods have priority on (i.e. replaces) modifiers on class */
78 if (jinfo
->cas_method_assert
) {
79 mono_declsec_get_method_action (jinfo
->method
, SECURITY_ACTION_ASSERT
, &frame
->assert);
80 } else if (jinfo
->cas_class_assert
) {
81 mono_declsec_get_class_action (jinfo
->method
->klass
, SECURITY_ACTION_ASSERT
, &frame
->assert);
84 if (jinfo
->cas_method_deny
) {
85 mono_declsec_get_method_action (jinfo
->method
, SECURITY_ACTION_DENY
, &frame
->deny
);
86 } else if (jinfo
->cas_class_deny
) {
87 mono_declsec_get_class_action (jinfo
->method
->klass
, SECURITY_ACTION_DENY
, &frame
->deny
);
90 if (jinfo
->cas_method_permitonly
) {
91 mono_declsec_get_method_action (jinfo
->method
, SECURITY_ACTION_PERMITONLY
, &frame
->permitonly
);
92 } else if (jinfo
->cas_class_permitonly
) {
93 mono_declsec_get_class_action (jinfo
->method
->klass
, SECURITY_ACTION_PERMITONLY
, &frame
->permitonly
);
96 /* g_warning ("FRAME %s A(%p,%d) D(%p,%d) PO(%p,%d)",
97 jinfo->method->name, frame->assert.blob, frame->assert.size, frame->deny.blob, frame->deny.size, frame->permitonly.blob,frame->permitonly.size); */
104 * Execute any LinkDemand, NonCasLinkDemand, LinkDemandChoice declarative
105 * security attribute present on the called method or it's class.
107 * @domain The current application domain
108 * @caller The method calling
109 * @callee The called method.
110 * return value: TRUE if a security violation is detection, FALSE otherwise.
112 * Note: The execution is done in managed code in SecurityManager.LinkDemand
115 mono_declsec_linkdemand_standard (MonoDomain
*domain
, MonoMethod
*caller
, MonoMethod
*callee
)
117 MonoDeclSecurityActions linkclass
, linkmethod
;
119 mono_jit_stats
.cas_linkdemand
++;
121 if (mono_declsec_get_linkdemands (callee
, &linkclass
, &linkmethod
)) {
122 MonoAssembly
*assembly
= mono_image_get_assembly (caller
->klass
->image
);
123 MonoReflectionAssembly
*refass
= (MonoReflectionAssembly
*) mono_assembly_get_object (domain
, assembly
);
124 MonoSecurityManager
*secman
= mono_security_manager_get_methods ();
129 args
[1] = &linkclass
;
130 args
[2] = &linkmethod
;
132 res
= mono_runtime_invoke (secman
->linkdemand
, NULL
, args
, NULL
);
133 return !(*(MonoBoolean
*) mono_object_unbox(res
));
139 * Ensure that the restrictions for partially trusted code are satisfied.
141 * @domain The current application domain
142 * @assembly The assembly to query
143 * return value: TRUE if the assembly is runnning at FullTrust, FALSE otherwise.
146 mono_declsec_is_assembly_fulltrust (MonoDomain
*domain
, MonoAssembly
*assembly
)
148 if (!MONO_SECMAN_FLAG_INIT (assembly
->fulltrust
)) {
149 MonoReflectionAssembly
*refass
= (MonoReflectionAssembly
*) mono_assembly_get_object (domain
, assembly
);
150 MonoSecurityManager
*secman
= mono_security_manager_get_methods ();
152 if (secman
&& refass
) {
157 res
= mono_runtime_invoke (secman
->linkdemandfulltrust
, NULL
, args
, NULL
);
158 if (*(MonoBoolean
*) mono_object_unbox(res
)) {
159 /* keep this value cached as it will be used very often */
160 MONO_SECMAN_FLAG_SET_VALUE (assembly
->fulltrust
, TRUE
);
165 MONO_SECMAN_FLAG_SET_VALUE (assembly
->fulltrust
, FALSE
);
169 return MONO_SECMAN_FLAG_GET_VALUE (assembly
->fulltrust
);
173 * Ensure that the restrictions for partially trusted code are satisfied.
175 * @domain The current application domain
176 * @caller The method calling
177 * @callee The called method
178 * return value: TRUE if a security violation is detected, FALSE otherwise.
180 * If callee's assembly is strongnamed and doesn't have an
181 * [AllowPartiallyTrustedCallers] attribute then we must enforce a LinkDemand
182 * for FullTrust on all public/protected methods on public class.
184 * Note: APTC is only effective on stongnamed assemblies.
187 mono_declsec_linkdemand_aptc (MonoDomain
*domain
, MonoMethod
*caller
, MonoMethod
*callee
)
189 MonoSecurityManager
* secman
= NULL
;
190 MonoAssembly
*assembly
;
193 mono_jit_stats
.cas_linkdemand_aptc
++;
195 /* A - Applicable only if we're calling into *another* assembly */
196 if (caller
->klass
->image
== callee
->klass
->image
)
199 /* B - Applicable if we're calling a public/protected method from a public class */
200 if (!(callee
->klass
->flags
& TYPE_ATTRIBUTE_PUBLIC
) || !(callee
->flags
& FIELD_ATTRIBUTE_PUBLIC
))
203 /* C - Applicable if the callee's assembly is strongnamed */
204 if ((mono_image_get_public_key (callee
->klass
->image
, &size
) == NULL
) || (size
< MONO_ECMA_KEY_LENGTH
))
207 /* D - the callee's assembly must have [AllowPartiallyTrustedCallers] */
208 assembly
= mono_image_get_assembly (callee
->klass
->image
);
209 if (!MONO_SECMAN_FLAG_INIT (assembly
->aptc
)) {
210 MonoCustomAttrInfo
* cinfo
= mono_custom_attrs_from_assembly (assembly
);
211 gboolean result
= FALSE
;
212 secman
= mono_security_manager_get_methods ();
213 if (secman
&& cinfo
) {
214 /* look for AllowPartiallyTrustedCallersAttribute */
215 result
= mono_custom_attrs_has_attr (cinfo
, secman
->allowpartiallytrustedcallers
);
218 mono_custom_attrs_free (cinfo
);
219 MONO_SECMAN_FLAG_SET_VALUE (assembly
->aptc
, result
);
222 if (MONO_SECMAN_FLAG_GET_VALUE (assembly
->aptc
))
225 /* E - the caller's assembly must have full trust permissions */
226 assembly
= mono_image_get_assembly (caller
->klass
->image
);
227 if (mono_declsec_is_assembly_fulltrust (domain
, assembly
))
230 /* g_warning ("FAILURE *** JIT LinkDemand APTC check *** %s.%s calls into %s.%s",
231 caller->klass->name, caller->name, callee->klass->name, callee->name); */
233 return TRUE
; /* i.e. throw new SecurityException(); */
237 * Ensure that the restrictions for calling native code are satisfied.
239 * @domain The current application domain
240 * @caller The method calling
241 * @native The native method called
242 * return value: TRUE if a security violation is detected, FALSE otherwise.
244 * Executing Platform Invokes (P/Invoke) is a is a restricted operation.
245 * The security policy must allow (SecurityPermissionFlag.UnmanagedCode)
246 * an assembly to do this.
248 * This LinkDemand case is special because it only needs to call managed
249 * code once per assembly. Further calls on this assembly will use a cached
250 * flag for better performance. This is not done before the first call (e.g.
251 * when loading the assembly) because that would break the lazy policy
252 * evaluation that Mono use (another time saving optimization).
254 * Note: P/Invoke checks are ALWAYS (1) done at JIT time (as a LinkDemand).
255 * They are also checked at runtime, using a Demand (stack walk), unless the
256 * method or it's class has a [SuppressUnmanagedCodeSecurity] attribute.
258 * (1) well as long as the security manager is active (i.e. --security)
261 mono_declsec_linkdemand_pinvoke (MonoDomain
*domain
, MonoMethod
*caller
, MonoMethod
*native
)
263 MonoAssembly
*assembly
= mono_image_get_assembly (caller
->klass
->image
);
265 mono_jit_stats
.cas_linkdemand_pinvoke
++;
267 /* Check for P/Invoke flag for the assembly */
268 if (!MONO_SECMAN_FLAG_INIT (assembly
->unmanaged
)) {
269 /* Check if we know (and have) or FullTrust status */
270 if (MONO_SECMAN_FLAG_INIT (assembly
->fulltrust
) && MONO_SECMAN_FLAG_GET_VALUE (assembly
->fulltrust
)) {
271 /* FullTrust includes UnmanagedCode permission */
272 MONO_SECMAN_FLAG_SET_VALUE (assembly
->unmanaged
, TRUE
);
275 MonoReflectionAssembly
*refass
= (MonoReflectionAssembly
*) mono_assembly_get_object (domain
, assembly
);
276 MonoSecurityManager
* secman
= mono_security_manager_get_methods ();
277 if (secman
&& refass
) {
282 res
= mono_runtime_invoke (secman
->linkdemandunmanaged
, NULL
, args
, NULL
);
283 if (*(MonoBoolean
*) mono_object_unbox(res
)) {
284 MONO_SECMAN_FLAG_SET_VALUE (assembly
->unmanaged
, TRUE
);
290 MONO_SECMAN_FLAG_SET_VALUE (assembly
->unmanaged
, FALSE
);
293 if (MONO_SECMAN_FLAG_GET_VALUE (assembly
->unmanaged
))
296 /* g_warning ("FAILURE *** JIT LinkDemand P/Invoke check *** %s.%s calls into %s.%s",
297 caller->klass->name, caller->name, native->klass->name, native->name); */
299 return TRUE
; /* i.e. throw new SecurityException(); */
303 * Ensure that the restrictions for calling internal calls are satisfied.
305 * @domain The current application domain
306 * @caller The method calling
307 * @icall The internal call method
308 * return value: TRUE if a security violation is detected, FALSE otherwise.
310 * We can't trust the icall flags/iflags as it comes from the assembly
311 * that we may want to restrict and we do not have the public/restricted
312 * information about icalls in the runtime. Actually it is not so bad
313 * as the CLR 2.0 doesn't enforce that restriction anymore.
315 * So we'll limit the icalls to originate from ECMA signed assemblies
316 * (as this is required for partial trust scenarios) - or - assemblies that
320 mono_declsec_linkdemand_icall (MonoDomain
*domain
, MonoMethod
*caller
, MonoMethod
*icall
)
322 MonoAssembly
*assembly
;
324 mono_jit_stats
.cas_linkdemand_icall
++;
326 /* check if the _icall_ is defined inside an ECMA signed assembly */
327 assembly
= mono_image_get_assembly (icall
->klass
->image
);
328 if (!MONO_SECMAN_FLAG_INIT (assembly
->ecma
)) {
330 const char *pk
= mono_image_get_public_key (icall
->klass
->image
, &size
);
331 MONO_SECMAN_FLAG_SET_VALUE (assembly
->ecma
, mono_is_ecma_key (pk
, size
));
334 if (MONO_SECMAN_FLAG_GET_VALUE (assembly
->ecma
))
337 /* else check if the _calling_ assembly is running at FullTrust */
338 assembly
= mono_image_get_assembly (caller
->klass
->image
);
339 return !mono_declsec_is_assembly_fulltrust (domain
, assembly
);
344 * Before the JIT can link (call) into a method the following security checks
347 * We check that the code has the permission to link when:
348 * 1. the code try to call an internal call;
349 * 2. the code try to p/invoke to unmanaged code;
350 * 3. the code try to call trusted code without being trusted itself -
351 * or without the trusted code permission (APTC);
352 * 4. the code try to call managed code protected by a LinkDemand security
355 * Failures result in a SecurityException being thrown (later in mini code).
357 * Note: Some checks are duplicated in managed code to deal when reflection is
358 * used to call the methods.
361 mono_declsec_linkdemand (MonoDomain
*domain
, MonoMethod
*caller
, MonoMethod
*callee
)
363 guint32 violation
= MONO_JIT_SECURITY_OK
;
365 /* short-circuit corlib as it is fully trusted (within itself)
366 * and because this cause major recursion headaches */
367 if ((caller
->klass
->image
== mono_defaults
.corlib
) && (callee
->klass
->image
== mono_defaults
.corlib
))
370 /* next, the special (implied) linkdemand */
372 if (callee
->iflags
& METHOD_IMPL_ATTRIBUTE_INTERNAL_CALL
) {
373 /* restrict internal calls into the runtime */
374 if (mono_declsec_linkdemand_icall (domain
, caller
, callee
))
375 violation
= MONO_JIT_LINKDEMAND_ECMA
;
376 } else if (callee
->flags
& METHOD_ATTRIBUTE_PINVOKE_IMPL
) {
377 /* CAS can restrict p/invoke calls with the assembly granted permissions */
378 if (mono_declsec_linkdemand_pinvoke (domain
, caller
, callee
))
379 violation
= MONO_JIT_LINKDEMAND_PINVOKE
;
383 /* check if we allow partially trusted callers in trusted (signed) assemblies */
384 if (mono_declsec_linkdemand_aptc (domain
, caller
, callee
))
385 violation
= MONO_JIT_LINKDEMAND_APTC
;
388 /* then the "normal" LinkDemand (only when called method has declarative security) */
389 if (!violation
&& mono_method_has_declsec (callee
)) {
390 /* LinkDemand are ignored for static constructors (ensured by calling mono_method_has_declsec) */
391 if (mono_declsec_linkdemand_standard (domain
, caller
, callee
))
392 violation
= MONO_JIT_LINKDEMAND_PERMISSION
;
395 /* if (violation) g_warning ("mono_declsec_linkdemand violation reported %d", violation); */