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