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