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