**** Merged r40732-r40872 from MCS ****
[mono.git] / mono / mini / declsec.c
1 /*
2  * declsec.c:  Declarative Security support
3  *
4  * Author:
5  *      Sebastien Pouliot  <sebastien@ximian.com>
6  *
7  * Copyright (C) 2004-2005 Novell, Inc (http://www.novell.com)
8  */
9
10 #include "declsec.h"
11 #include "mini.h"
12
13 /*
14  * Does the methods (or it's class) as any declarative security attribute ?
15  * Is so are they applicable ? (e.g. static class constructor)
16  */
17 MonoBoolean
18 mono_method_has_declsec (MonoMethod *method)
19 {
20         mono_jit_stats.cas_declsec_check++;
21
22         if (method->wrapper_type == MONO_WRAPPER_MANAGED_TO_NATIVE) {
23                 method = mono_marshal_method_from_wrapper (method);
24                 if (!method)
25                         return FALSE;
26         } else if (method->wrapper_type != MONO_WRAPPER_NONE)
27                 return FALSE;
28
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"))
32                         return TRUE;
33         }
34         return FALSE;
35 }
36
37
38 /*
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.
42  */
43 void
44 mono_declsec_cache_stack_modifiers (MonoJitInfo *jinfo)
45 {
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;
51
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;
57 }
58
59
60 MonoSecurityFrame*
61 mono_declsec_create_frame (MonoDomain *domain, MonoJitInfo *jinfo)
62 {
63         MonoSecurityFrame *frame = (MonoSecurityFrame*) mono_object_new (domain, mono_defaults.runtimesecurityframe_class);
64
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);
69                 }
70                 jinfo->cas_inited = TRUE;
71         }
72
73         frame->method = mono_method_get_object (domain, jinfo->method, NULL);
74
75         /* stack modifiers on methods have priority on (i.e. replaces) modifiers on class */
76
77         if (jinfo->cas_method_assert) {
78                 mono_declsec_get_method_action (jinfo->method, SECURITY_ACTION_ASSERT, &frame->assert);
79         } else if (jinfo->cas_class_assert) {
80                 mono_declsec_get_class_action (jinfo->method->klass, SECURITY_ACTION_ASSERT, &frame->assert);
81         }
82
83         if (jinfo->cas_method_deny) {
84                 mono_declsec_get_method_action (jinfo->method, SECURITY_ACTION_DENY, &frame->deny);
85         } else if (jinfo->cas_class_deny) {
86                 mono_declsec_get_class_action (jinfo->method->klass, SECURITY_ACTION_DENY, &frame->deny);
87         }
88
89         if (jinfo->cas_method_permitonly) {
90                 mono_declsec_get_method_action (jinfo->method, SECURITY_ACTION_PERMITONLY, &frame->permitonly);
91         } else if (jinfo->cas_class_permitonly) {
92                 mono_declsec_get_class_action (jinfo->method->klass, SECURITY_ACTION_PERMITONLY, &frame->permitonly);
93         }
94
95         /* g_warning ("FRAME %s A(%p,%d) D(%p,%d) PO(%p,%d)", 
96         jinfo->method->name, frame->assert.blob, frame->assert.size, frame->deny.blob, frame->deny.size, frame->permitonly.blob,frame->permitonly.size); */
97
98         return frame;
99 }
100
101
102 /*
103  * Execute any LinkDemand, NonCasLinkDemand, LinkDemandChoice declarative
104  * security attribute present on the called method or it's class.
105  *
106  * @domain      The current application domain
107  * @caller      The method calling
108  * @callee      The called method.
109  * return value: TRUE if a security violation is detection, FALSE otherwise.
110  *
111  * Note: The execution is done in managed code in SecurityManager.LinkDemand
112  */
113 static gboolean
114 mono_declsec_linkdemand_standard (MonoDomain *domain, MonoMethod *caller, MonoMethod *callee)
115 {
116         MonoDeclSecurityActions linkclass, linkmethod;
117
118         mono_jit_stats.cas_linkdemand++;
119
120         if (mono_declsec_get_linkdemands (callee, &linkclass, &linkmethod)) {
121                 MonoAssembly *assembly = mono_image_get_assembly (caller->klass->image);
122                 MonoReflectionAssembly *refass = (MonoReflectionAssembly*) mono_assembly_get_object (domain, assembly);
123                 MonoSecurityManager *secman = mono_security_manager_get_methods ();
124                 MonoObject *res;
125                 gpointer args [3];
126
127                 args [0] = refass;
128                 args [1] = &linkclass;
129                 args [2] = &linkmethod;
130
131                 res = mono_runtime_invoke (secman->linkdemand, NULL, args, NULL);
132                 return !(*(MonoBoolean *) mono_object_unbox(res));
133         }
134         return FALSE;
135 }
136
137 /*
138  * Ensure that the restrictions for partially trusted code are satisfied.
139  *
140  * @domain      The current application domain
141  * @assembly    The assembly to query
142  * return value: TRUE if the assembly is runnning at FullTrust, FALSE otherwise.
143  */
144 static gboolean
145 mono_declsec_is_assembly_fulltrust (MonoDomain *domain, MonoAssembly *assembly)
146 {
147         if (!MONO_SECMAN_FLAG_INIT (assembly->fulltrust)) {
148                 MonoReflectionAssembly *refass = (MonoReflectionAssembly*) mono_assembly_get_object (domain, assembly);
149                 MonoSecurityManager *secman = mono_security_manager_get_methods ();
150
151                 if (secman && refass) {
152                         MonoObject *res;
153                         gpointer args [1];
154                         args [0] = refass;
155
156                         res = mono_runtime_invoke (secman->linkdemandfulltrust, NULL, args, NULL);
157                         if (*(MonoBoolean *) mono_object_unbox(res)) {
158                                 /* keep this value cached as it will be used very often */
159                                 MONO_SECMAN_FLAG_SET_VALUE (assembly->fulltrust, TRUE);
160                                 return TRUE;
161                         }
162                 }
163
164                 MONO_SECMAN_FLAG_SET_VALUE (assembly->fulltrust, FALSE);
165                 return FALSE;
166         }
167
168         return MONO_SECMAN_FLAG_GET_VALUE (assembly->fulltrust);
169 }
170
171 /*
172  * Ensure that the restrictions for partially trusted code are satisfied.
173  *
174  * @domain      The current application domain
175  * @caller      The method calling
176  * @callee      The called method
177  * return value: TRUE if a security violation is detection, FALSE otherwise.
178  *
179  * If callee's assembly is strongnamed and doesn't have an 
180  * [AllowPartiallyTrustedCallers] attribute then we must enforce a LinkDemand
181  * for FullTrust on all public/protected methods on public class.
182  *
183  * Note: APTC is only effective on stongnamed assemblies.
184  */
185 static gboolean
186 mono_declsec_linkdemand_aptc (MonoDomain *domain, MonoMethod *caller, MonoMethod *callee)
187 {
188         MonoSecurityManager* secman = NULL;
189         MonoAssembly *assembly;
190         guint32 size = 0;
191
192         mono_jit_stats.cas_linkdemand_aptc++;
193
194         /* A - Applicable only if we're calling into *another* assembly */
195         if (caller->klass->image == callee->klass->image)
196                 return FALSE;
197
198         /* B - Applicable if we're calling a public/protected method from a public class */
199         if (!(callee->klass->flags & TYPE_ATTRIBUTE_PUBLIC) || !(callee->flags & FIELD_ATTRIBUTE_PUBLIC))
200                 return FALSE;
201
202         /* C - Applicable if the callee's assembly is strongnamed */
203         if ((mono_image_get_public_key (callee->klass->image, &size) == NULL) || (size < MONO_ECMA_KEY_LENGTH))
204                 return FALSE;
205
206         /* D - the callee's assembly must have [AllowPartiallyTrustedCallers] */
207         assembly = mono_image_get_assembly (callee->klass->image);
208         if (!MONO_SECMAN_FLAG_INIT (assembly->aptc)) {
209                 MonoCustomAttrInfo* cinfo = mono_custom_attrs_from_assembly (assembly);
210                 gboolean result = FALSE;
211                 secman = mono_security_manager_get_methods ();
212                 if (secman && cinfo) {
213                         /* look for AllowPartiallyTrustedCallersAttribute */
214                         result = mono_custom_attrs_has_attr (cinfo, secman->allowpartiallytrustedcallers);
215                 }
216                 MONO_SECMAN_FLAG_SET_VALUE (assembly->aptc, result);
217         }
218
219         if (MONO_SECMAN_FLAG_GET_VALUE (assembly->aptc))
220                 return FALSE;
221
222         /* E - the caller's assembly must have full trust permissions */
223         if (mono_declsec_is_assembly_fulltrust (domain, assembly))
224                 return FALSE;
225
226         /* g_warning ("FAILURE *** JIT LinkDemand APTC check *** %s.%s calls into %s.%s",
227                 caller->klass->name, caller->name, callee->klass->name, callee->name); */
228
229         return TRUE;    /* i.e. throw new SecurityException(); */
230 }
231
232 /*
233  * Ensure that the restrictions for calling native code are satisfied.
234  *
235  * @domain      The current application domain
236  * @caller      The method calling
237  * @native      The native method called
238  * return value: TRUE if a security violation is detection, FALSE otherwise.
239  *
240  * Executing Platform Invokes (P/Invoke) is a is a restricted operation.
241  * The security policy must allow (SecurityPermissionFlag.UnmanagedCode)
242  * an assembly to do this.
243  *
244  * This LinkDemand case is special because it only needs to call managed
245  * code once per assembly. Further calls on this assembly will use a cached
246  * flag for better performance. This is not done before the first call (e.g.
247  * when loading the assembly) because that would break the lazy policy
248  * evaluation that Mono use (another time saving optimization).
249  *
250  * Note: P/Invoke checks are ALWAYS (1) done at JIT time (as a LinkDemand). 
251  * They are also checked at runtime, using a Demand (stack walk), unless the 
252  * method or it's class has a [SuppressUnmanagedCodeSecurity] attribute.
253  *
254  * (1) well as long as the security manager is active (i.e. --security)
255  */
256 static gboolean
257 mono_declsec_linkdemand_pinvoke (MonoDomain *domain, MonoMethod *caller, MonoMethod *native)
258 {
259         MonoAssembly *assembly = mono_image_get_assembly (caller->klass->image);
260
261         mono_jit_stats.cas_linkdemand_pinvoke++;
262
263         /* Check for P/Invoke flag for the assembly */
264         if (!MONO_SECMAN_FLAG_INIT (assembly->unmanaged)) {
265                 /* Check if we know (and have) or FullTrust status */
266                 if (MONO_SECMAN_FLAG_INIT (assembly->fulltrust) && MONO_SECMAN_FLAG_GET_VALUE (assembly->fulltrust)) {
267                         /* FullTrust includes UnmanagedCode permission */
268                         MONO_SECMAN_FLAG_SET_VALUE (assembly->unmanaged, TRUE);
269                 } else {
270                         MonoReflectionAssembly *refass = (MonoReflectionAssembly*) mono_assembly_get_object (domain, assembly);
271                         MonoSecurityManager* secman = mono_security_manager_get_methods ();
272                         if (secman && refass) {
273                                 MonoObject *res;
274                                 gpointer args [1];
275                                 args [0] = refass;
276
277                                 res = mono_runtime_invoke (secman->linkdemandunmanaged, NULL, args, NULL);
278                                 if (*(MonoBoolean *) mono_object_unbox(res)) {
279                                         MONO_SECMAN_FLAG_SET_VALUE (assembly->unmanaged, TRUE);
280                                         return FALSE;
281                                 }
282                         }
283                 }
284
285                 MONO_SECMAN_FLAG_SET_VALUE (assembly->unmanaged, FALSE);
286         }
287
288         if (MONO_SECMAN_FLAG_GET_VALUE (assembly->unmanaged))
289                 return FALSE;
290
291         /* g_warning ("FAILURE *** JIT LinkDemand P/Invoke check *** %s.%s calls into %s.%s",
292                 caller->klass->name, caller->name, native->klass->name, native->name); */
293
294         return TRUE;    /* i.e. throw new SecurityException(); */
295 }
296
297 /*
298  * Ensure that the restrictions for calling internal calls are satisfied.
299  *
300  * @domain      The current application domain
301  * @caller      The method calling
302  * @icall       The internal call method
303  * return value: TRUE if a security violation is detection, FALSE otherwise.
304  *
305  * We can't trust the icall flags/iflags as it comes from the assembly
306  * that we may want to restrict and we do not have the public/restricted
307  * information about icalls in the runtime. Actually it is not so bad 
308  * as the CLR 2.0 doesn't enforce that restriction anymore.
309  * 
310  * So we'll limit the icalls to originate from ECMA signed assemblies 
311  * (as this is required for partial trust scenarios) - or - assemblies that 
312  * have FullTrust.
313  */
314 static gboolean
315 mono_declsec_linkdemand_icall (MonoDomain *domain, MonoMethod *caller, MonoMethod *icall)
316 {
317         MonoAssembly *assembly;
318
319         mono_jit_stats.cas_linkdemand_icall++;
320
321         /* check if the _icall_ is defined inside an ECMA signed assembly */
322         assembly = mono_image_get_assembly (icall->klass->image);
323         if (!MONO_SECMAN_FLAG_INIT (assembly->ecma)) {
324                 guint32 size = 0;
325                 const char *pk = mono_image_get_public_key (icall->klass->image, &size);
326                 MONO_SECMAN_FLAG_SET_VALUE (assembly->ecma, mono_is_ecma_key (pk, size));
327         }
328
329         if (MONO_SECMAN_FLAG_GET_VALUE (assembly->ecma))
330                 return FALSE;
331
332         /* else check if the _calling_ assembly is running at FullTrust */
333         assembly = mono_image_get_assembly (caller->klass->image);
334         return !mono_declsec_is_assembly_fulltrust (domain, assembly);
335 }
336
337
338 /*
339  * Before the JIT can link (call) into a method the following security checks
340  * must be done:
341  *
342  * We check that the code has the permission to link when:
343  * 1. the code try to call an internal call;
344  * 2. the code try to p/invoke to unmanaged code;
345  * 3. the code try to call trusted code without being trusted itself -
346  *    or without the trusted code permission (APTC);
347  * 4. the code try to call managed code protected by a LinkDemand security 
348  *    attribute
349  *
350  * Failures result in a SecurityException being thrown (later in mini code).
351  *
352  * Note: Some checks are duplicated in managed code to deal when reflection is
353  * used to call the methods.
354  */
355 guint32
356 mono_declsec_linkdemand (MonoDomain *domain, MonoMethod *caller, MonoMethod *callee)
357 {
358         guint32 violation = MONO_JIT_SECURITY_OK;
359         /* first, the special (implied) linkdemand */
360
361         if (callee->iflags & METHOD_IMPL_ATTRIBUTE_INTERNAL_CALL) {
362                 /* restrict internal calls into the runtime */
363                 if (mono_declsec_linkdemand_icall (domain, caller, callee))
364                         violation = MONO_JIT_LINKDEMAND_ECMA;
365         } else if (callee->flags & METHOD_ATTRIBUTE_PINVOKE_IMPL) {
366                 /* CAS can restrict p/invoke calls with the assembly granted permissions */
367                 if (mono_declsec_linkdemand_pinvoke (domain, caller, callee))
368                         violation = MONO_JIT_LINKDEMAND_PINVOKE;
369         }
370
371         if (!violation) {
372                 /* check if we allow partially trusted callers in trusted (signed) assemblies */
373                 if (mono_declsec_linkdemand_aptc (domain, caller, callee))
374                         violation = MONO_JIT_LINKDEMAND_APTC;
375         }
376
377         /* then the "normal" LinkDemand (only when called method has declarative security) */
378         if (!violation && mono_method_has_declsec (callee)) {
379                 /* LinkDemand are ignored for static constructors (ensured by calling mono_method_has_declsec) */
380                 if (mono_declsec_linkdemand_standard (domain, caller, callee))
381                         violation = MONO_JIT_LINKDEMAND_PERMISSION;
382         }
383
384         /* if (violation) g_warning ("mono_declsec_linkdemand violation reported %d", violation); */
385         return violation;
386 }