* attribute.cs (GetMarshal): Work even if "DefineCustom" is
[mono.git] / mcs / class / corlib / System.Security / SecurityManager.cs
1 //
2 // System.Security.SecurityManager.cs
3 //
4 // Authors:
5 //      Nick Drochak(ndrochak@gol.com)
6 //      Sebastien Pouliot  <sebastien@ximian.com>
7 //
8 // (C) Nick Drochak
9 // Portions (C) 2004 Motus Technologies Inc. (http://www.motus.com)
10 // Copyright (C) 2004-2005 Novell, Inc (http://www.novell.com)
11 //
12 // Permission is hereby granted, free of charge, to any person obtaining
13 // a copy of this software and associated documentation files (the
14 // "Software"), to deal in the Software without restriction, including
15 // without limitation the rights to use, copy, modify, merge, publish,
16 // distribute, sublicense, and/or sell copies of the Software, and to
17 // permit persons to whom the Software is furnished to do so, subject to
18 // the following conditions:
19 // 
20 // The above copyright notice and this permission notice shall be
21 // included in all copies or substantial portions of the Software.
22 // 
23 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
24 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
25 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
26 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
27 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
28 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
29 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
30 //
31
32 using System.Collections;
33 using System.Globalization;
34 using System.IO;
35 using System.Reflection;
36 using System.Runtime.CompilerServices;
37 using System.Runtime.InteropServices;
38 using System.Security.Permissions;
39 using System.Security.Policy;
40 using System.Text;
41
42 using Mono.Xml;
43
44 namespace System.Security {
45
46         // Must match MonoDeclSecurityActions in /mono/metadata/reflection.h
47         internal struct RuntimeDeclSecurityActions {
48                 public RuntimeDeclSecurityEntry cas;
49                 public RuntimeDeclSecurityEntry noncas;
50                 public RuntimeDeclSecurityEntry choice;
51         }
52
53 #if NET_2_0
54         [ComVisible (true)]
55         public static class SecurityManager {
56 #else
57         public sealed class SecurityManager {
58
59                 private SecurityManager ()
60                 {
61                 }
62 #endif
63                 private static object _lockObject;
64                 private static ArrayList _hierarchy;
65                 private static IPermission _unmanagedCode;
66                 private static Hashtable _declsecCache;
67                 private static PolicyLevel _level;
68
69                 static SecurityManager () 
70                 {
71                         // lock(this) is bad
72                         // http://msdn.microsoft.com/library/en-us/dnaskdr/html/askgui06032003.asp?frame=true
73                         _lockObject = new object ();
74                 }
75
76                 // properties
77
78                 extern public static bool CheckExecutionRights {
79                         [MethodImplAttribute (MethodImplOptions.InternalCall)]
80                         get;
81
82                         [MethodImplAttribute (MethodImplOptions.InternalCall)]
83                         [SecurityPermission (SecurityAction.Demand, ControlPolicy = true)]
84                         set;
85                 }
86
87                 extern public static bool SecurityEnabled {
88                         [MethodImplAttribute (MethodImplOptions.InternalCall)]
89                         get;
90
91                         [MethodImplAttribute (MethodImplOptions.InternalCall)]
92                         [SecurityPermission (SecurityAction.Demand, ControlPolicy = true)]
93                         set;
94                 }
95
96                 // methods
97
98 #if NET_1_1
99                 // NOTE: This method doesn't show in the class library status page because
100                 // it cannot be "found" with the StrongNameIdentityPermission for ECMA key.
101                 // But it's there!
102                 [MonoTODO ("works for fulltrust (empty), documentation doesn't really make sense, type wise")]
103                 [StrongNameIdentityPermission (SecurityAction.LinkDemand, PublicKey = "0x00000000000000000400000000000000")]
104                 public static void GetZoneAndOrigin (out ArrayList zone, out ArrayList origin) 
105                 {
106                         zone = new ArrayList ();
107                         origin = new ArrayList ();
108                 }
109 #endif
110
111                 public static bool IsGranted (IPermission perm)
112                 {
113                         if (perm == null)
114                                 return true;
115                         if (!SecurityEnabled)
116                                 return true;
117
118                         // - Policy driven
119                         // - Only check the caller (no stack walk required)
120                         // - Not affected by overrides (like Assert, Deny and PermitOnly)
121                         // - calls IsSubsetOf even for non CAS permissions
122                         //   (i.e. it does call Demand so any code there won't be executed)
123 #if NET_2_0
124                         // with 2.0 identity permission are unrestrictable
125                         return IsGranted (Assembly.GetCallingAssembly (), perm);
126 #else
127                         if (perm is IUnrestrictedPermission)
128                                 return IsGranted (Assembly.GetCallingAssembly (), perm);
129                         else
130                                 return IsGrantedRestricted (Assembly.GetCallingAssembly (), perm);
131 #endif
132                 }
133
134 #if !NET_2_0
135                 // only for permissions that do not implement IUnrestrictedPermission
136                 internal static bool IsGrantedRestricted (Assembly a, IPermission perm)
137                 {
138                         PermissionSet granted = a.GrantedPermissionSet;\r
139                         if (granted != null) {\r
140                                 CodeAccessPermission grant = (CodeAccessPermission) granted.GetPermission (perm.GetType ());
141                                 if (!perm.IsSubsetOf (grant)) {
142                                         return false;
143                                 }
144                         }
145
146                         PermissionSet denied = a.DeniedPermissionSet;
147                         if (denied != null) {\r
148                                 CodeAccessPermission refuse = (CodeAccessPermission) a.DeniedPermissionSet.GetPermission (perm.GetType ());
149                                 if ((refuse != null) && perm.IsSubsetOf (refuse))
150                                         return false;
151                         }
152                         return true;
153                 }
154 #endif
155                 // note: in 2.0 *all* permissions (including identity permissions) support unrestricted
156                 internal static bool IsGranted (Assembly a, IPermission perm)
157                 {
158                         PermissionSet granted = a.GrantedPermissionSet;\r
159                         if ((granted != null) && !granted.IsUnrestricted ()) {\r
160                                 CodeAccessPermission grant = (CodeAccessPermission) granted.GetPermission (perm.GetType ());\r
161                                 if (!perm.IsSubsetOf (grant)) {\r
162                                         return false;\r
163                                 }\r
164                         }\r
165 \r
166                         PermissionSet denied = a.DeniedPermissionSet;\r
167                         if ((denied != null) && !denied.IsEmpty ()) {\r
168                                 if (denied.IsUnrestricted ())\r
169                                         return false;\r
170                                 CodeAccessPermission refuse = (CodeAccessPermission) a.DeniedPermissionSet.GetPermission (perm.GetType ());\r
171                                 if ((refuse != null) && perm.IsSubsetOf (refuse))\r
172                                         return false;\r
173                         }\r
174                         return true;\r
175                 }
176
177                 internal static IPermission CheckPermissionSet (Assembly a, PermissionSet ps, bool noncas)
178                 {
179                         if (ps.IsEmpty ())
180                                 return null;
181
182                         foreach (IPermission p in ps) {
183                                 // note: this may contains non CAS permissions
184                                 if ((!noncas) && (p is CodeAccessPermission)) {
185 #if NET_2_0
186                                         if (!IsGranted (a, p))
187                                                 return p;
188 #else
189                                         if (p is IUnrestrictedPermission) {
190                                                 if (!IsGranted (a, p))
191                                                         return p;
192                                         } else {
193                                                 if (!IsGrantedRestricted (a, p))
194                                                         return p;
195                                         }
196 #endif
197                                 } else {
198                                         // but non-CAS will throw on failure...
199                                         try {
200                                                 p.Demand ();
201                                         }
202                                         catch (SecurityException) {
203                                                 // ... so we catch
204                                                 return p;
205                                         }
206                                 }
207                         }
208                         return null;
209                 }
210
211                 internal static IPermission CheckPermissionSet (AppDomain ad, PermissionSet ps)
212                 {
213                         if ((ps == null) || ps.IsEmpty ())
214                                 return null;
215
216                         PermissionSet granted = ad.GrantedPermissionSet;
217                         if (granted == null)
218                                 return null;
219                         if ((ad.GrantedPermissionSet.Count == 0) && ad.GrantedPermissionSet.IsUnrestricted ())
220                                 return null;
221
222                         foreach (IPermission p in ps) {
223                                 if (p is CodeAccessPermission) {
224                                         CodeAccessPermission grant = (CodeAccessPermission) granted.GetPermission (p.GetType ());
225                                         if (grant == null) {
226                                                 if (!granted.IsUnrestricted () || !(p is IUnrestrictedPermission)) {
227                                                         if (!p.IsSubsetOf (null))
228                                                                 return p;
229                                                 }
230                                         } else if (!p.IsSubsetOf (grant)) {
231                                                 return p;
232                                         }
233                                 } else {
234                                         // but non-CAS will throw on failure...
235                                         try {
236                                                 p.Demand ();
237                                         }
238                                         catch (SecurityException) {
239                                                 // ... so we catch
240                                                 return p;
241                                         }
242                                 }
243                         }
244                         return null;
245                 }
246
247                 [SecurityPermission (SecurityAction.Demand, ControlPolicy = true)]
248                 public static PolicyLevel LoadPolicyLevelFromFile (string path, PolicyLevelType type)
249                 {
250                         if (path == null)
251                                 throw new ArgumentNullException ("path");
252
253                         PolicyLevel pl = null;
254                         try {
255                                 pl = new PolicyLevel (type.ToString (), type);
256                                 pl.LoadFromFile (path);
257                         }
258                         catch (Exception e) {
259                                 throw new ArgumentException (Locale.GetText ("Invalid policy XML"), e);
260                         }
261                         return pl;
262                 }
263
264                 [SecurityPermission (SecurityAction.Demand, ControlPolicy = true)]
265                 public static PolicyLevel LoadPolicyLevelFromString (string str, PolicyLevelType type)
266                 {
267                         if (null == str)
268                                 throw new ArgumentNullException ("str");
269
270                         PolicyLevel pl = null;
271                         try {
272                                 pl = new PolicyLevel (type.ToString (), type);
273                                 pl.LoadFromString (str);
274                         }
275                         catch (Exception e) {
276                                 throw new ArgumentException (Locale.GetText ("Invalid policy XML"), e);
277                         }
278                         return pl;
279                 }
280
281                 [SecurityPermission (SecurityAction.Demand, ControlPolicy = true)]
282                 public static IEnumerator PolicyHierarchy ()
283                 {
284                         return Hierarchy;
285                 }
286
287                 public static PermissionSet ResolvePolicy (Evidence evidence)
288                 {
289                         // no evidence, no permission
290                         if (evidence == null)
291                                 return new PermissionSet (PermissionState.None);
292
293                         PermissionSet ps = null;
294                         // Note: can't call PolicyHierarchy since ControlPolicy isn't required to resolve policies
295                         IEnumerator ple = Hierarchy;
296                         while (ple.MoveNext ()) {
297                                 PolicyLevel pl = (PolicyLevel) ple.Current;
298                                 if (ResolvePolicyLevel (ref ps, pl, evidence)) {
299                                         break;  // i.e. PolicyStatementAttribute.LevelFinal
300                                 }
301                         }
302
303                         ResolveIdentityPermissions (ps, evidence);
304
305                         return ps;
306                 }
307
308 #if NET_2_0
309                 [MonoTODO ("(2.0) more tests are needed")]
310                 public static PermissionSet ResolvePolicy (Evidence[] evidences)
311                 {
312                         if ((evidences == null) || (evidences.Length == 0) ||
313                                 ((evidences.Length == 1) && (evidences [0].Count == 0))) {
314                                 return new PermissionSet (PermissionState.None);
315                         }
316
317                         // probably not optimal
318                         PermissionSet ps = ResolvePolicy (evidences [0]);
319                         for (int i=1; i < evidences.Length; i++) {
320                                 ps = ps.Intersect (ResolvePolicy (evidences [i]));
321                         }
322                         return ps;
323                 }
324
325                 public static PermissionSet ResolveSystemPolicy (Evidence evidence)
326                 {
327                         // no evidence, no permission
328                         if (evidence == null)
329                                 return new PermissionSet (PermissionState.None);
330
331                         // Note: can't call PolicyHierarchy since ControlPolicy isn't required to resolve policies
332                         PermissionSet ps = null;
333                         IEnumerator ple = Hierarchy;
334                         while (ple.MoveNext ()) {
335                                 PolicyLevel pl = (PolicyLevel) ple.Current;
336                                 if (pl.Type == PolicyLevelType.AppDomain)
337                                         break;
338                                 if (ResolvePolicyLevel (ref ps, pl, evidence))
339                                         break;  // i.e. PolicyStatementAttribute.LevelFinal
340                         }
341
342                         ResolveIdentityPermissions (ps, evidence);
343                         return ps;
344                 }
345 #endif
346
347                 static private SecurityPermission _execution = new SecurityPermission (SecurityPermissionFlag.Execution);
348
349                 public static PermissionSet ResolvePolicy (Evidence evidence, PermissionSet reqdPset, PermissionSet optPset, PermissionSet denyPset, out PermissionSet denied)
350                 {
351                         PermissionSet resolved = ResolvePolicy (evidence);
352                         // do we have the minimal permission requested by the assembly ?
353                         if ((reqdPset != null) && !reqdPset.IsSubsetOf (resolved)) {
354                                 throw new PolicyException (Locale.GetText (
355                                         "Policy doesn't grant the minimal permissions required to execute the assembly."));
356                         }
357
358                         // do we check for execution rights ?
359                         if (CheckExecutionRights) {
360                                 bool execute = false;
361                                 // an empty permissionset doesn't include Execution
362                                 if (resolved != null) {
363                                         // unless we have "Full Trust"...
364                                         if (resolved.IsUnrestricted ()) {
365                                                 execute = true;
366                                         } else {
367                                                 // ... we need to find a SecurityPermission
368                                                 IPermission security = resolved.GetPermission (typeof (SecurityPermission));
369                                                 execute = _execution.IsSubsetOf (security);
370                                         }
371                                 }
372
373                                 if (!execute) {
374                                         throw new PolicyException (Locale.GetText (
375                                                 "Policy doesn't grant the right to execute the assembly."));
376                                 }
377                         }
378
379                         denied = denyPset;
380                         return resolved;
381                 }
382
383                 public static IEnumerator ResolvePolicyGroups (Evidence evidence)
384                 {
385                         if (evidence == null)
386                                 throw new ArgumentNullException ("evidence");
387
388                         ArrayList al = new ArrayList ();
389                         // Note: can't call PolicyHierarchy since ControlPolicy isn't required to resolve policies
390                         IEnumerator ple = Hierarchy;
391                         while (ple.MoveNext ()) {
392                                 PolicyLevel pl = (PolicyLevel) ple.Current;
393                                 CodeGroup cg = pl.ResolveMatchingCodeGroups (evidence);
394                                 al.Add (cg);
395                         }
396                         return al.GetEnumerator ();
397                 }
398
399                 [SecurityPermission (SecurityAction.Demand, ControlPolicy = true)]
400                 public static void SavePolicy () 
401                 {
402                         IEnumerator e = Hierarchy;
403                         while (e.MoveNext ()) {
404                                 PolicyLevel level = (e.Current as PolicyLevel);
405                                 level.Save ();
406                         }
407                 }
408
409                 [SecurityPermission (SecurityAction.Demand, ControlPolicy = true)]
410                 public static void SavePolicyLevel (PolicyLevel level) 
411                 {
412                         // Yes this will throw a NullReferenceException, just like MS (see FDBK13121)
413                         level.Save ();
414                 }
415
416                 // private/internal stuff
417
418                 private static IEnumerator Hierarchy {
419                         get {
420                                 lock (_lockObject) {
421                                         if (_hierarchy == null)
422                                                 InitializePolicyHierarchy ();
423                                 }
424                                 return _hierarchy.GetEnumerator ();
425                         }
426                 }
427
428                 private static void InitializePolicyHierarchy ()
429                 {
430                         string machinePolicyPath = Path.GetDirectoryName (Environment.GetMachineConfigPath ());
431                         // note: use InternalGetFolderPath to avoid recursive policy initialization
432                         string userPolicyPath = Path.Combine (Environment.InternalGetFolderPath (Environment.SpecialFolder.ApplicationData), "mono");
433
434                         PolicyLevel enterprise = new PolicyLevel ("Enterprise", PolicyLevelType.Enterprise);
435                         _level = enterprise;
436                         enterprise.LoadFromFile (Path.Combine (machinePolicyPath, "enterprisesec.config"));
437
438                         PolicyLevel machine = new PolicyLevel ("Machine", PolicyLevelType.Machine);
439                         _level = machine;
440                         machine.LoadFromFile (Path.Combine (machinePolicyPath, "security.config"));
441
442                         PolicyLevel user = new PolicyLevel ("User", PolicyLevelType.User);
443                         _level = user;
444                         user.LoadFromFile (Path.Combine (userPolicyPath, "security.config"));
445
446                         ArrayList al = new ArrayList ();
447                         al.Add (enterprise);
448                         al.Add (machine);
449                         al.Add (user);
450
451                         _hierarchy = ArrayList.Synchronized (al);
452                         _level = null;
453                 }
454
455                 internal static bool ResolvePolicyLevel (ref PermissionSet ps, PolicyLevel pl, Evidence evidence)
456                 {
457                         PolicyStatement pst = pl.Resolve (evidence);
458                         if (pst != null) {
459                                 if (ps == null) {
460                                         // only for initial (first) policy level processed
461                                         ps = pst.PermissionSet;
462                                 } else {
463                                         ps = ps.Intersect (pst.PermissionSet);
464                                         if (ps == null) {
465                                                 // null is equals to None - exist that null can throw NullReferenceException ;-)
466                                                 ps = new PermissionSet (PermissionState.None);
467                                         }
468                                 }
469
470                                 if ((pst.Attributes & PolicyStatementAttribute.LevelFinal) == PolicyStatementAttribute.LevelFinal)
471                                         return true;
472                         }
473                         return false;
474                 }
475
476                 internal static void ResolveIdentityPermissions (PermissionSet ps, Evidence evidence)
477                 {
478 #if NET_2_0
479                         // in 2.0 identity permissions can now be unrestricted
480                         if (ps.IsUnrestricted ())
481                                 return;
482 #endif
483                         // Only host evidence are used for policy resolution
484                         IEnumerator ee = evidence.GetHostEnumerator ();
485                         while (ee.MoveNext ()) {
486                                 IIdentityPermissionFactory ipf = (ee.Current as IIdentityPermissionFactory);
487                                 if (ipf != null) {
488                                         IPermission p = ipf.CreateIdentityPermission (evidence);
489                                         ps.AddPermission (p);
490                                 }
491                         }
492                 }
493
494                 internal static PolicyLevel ResolvingPolicyLevel {
495                         get { return _level; }
496                         set { _level = value; }
497                 }
498
499                 internal static PermissionSet Decode (IntPtr permissions, int length)
500                 {
501                         // Permission sets from the runtime (declarative security) can be cached
502                         // for performance as they can never change (i.e. they are read-only).
503                         PermissionSet ps = null;
504
505                         lock (_lockObject) {
506                                 if (_declsecCache == null) {
507                                         _declsecCache = new Hashtable ();
508                                 }
509
510                                 object key = (object) (int) permissions;
511                                 ps = (PermissionSet) _declsecCache [key];
512                                 if (ps == null) {
513                                         // create permissionset and add it to the cache
514                                         byte[] data = new byte [length];
515                                         Marshal.Copy (permissions, data, 0, length);
516                                         ps = Decode (data);
517                                         ps.DeclarativeSecurity = true;
518                                         _declsecCache.Add (key, ps);
519                                 }
520                         }
521                         return ps;
522                 }
523
524                 internal static PermissionSet Decode (byte[] encodedPermissions)
525                 {
526                         if ((encodedPermissions == null) || (encodedPermissions.Length < 1))
527                                 throw new SecurityException ("Invalid metadata format.");
528
529                         switch (encodedPermissions [0]) {
530                         case 60:
531                                 // Fx 1.0/1.1 declarative security permissions metadata is in Unicode-encoded XML
532                                 string xml = Encoding.Unicode.GetString (encodedPermissions);
533                                 return new PermissionSet (xml);
534                         case 0x2E:
535                                 // Fx 2.0 are encoded "somewhat, but not enough, like" custom attributes
536                                 // note: we still support the older format!
537                                 return PermissionSet.CreateFromBinaryFormat (encodedPermissions);
538                         default:
539                                 throw new SecurityException (Locale.GetText ("Unknown metadata format."));
540                         }
541                 }
542 #if NET_2_0
543                 internal static PermissionSetCollection DecodeCollection (IntPtr permissions, int length)
544                 {
545                         // Permission sets from the runtime (declarative security) can be cached
546                         // for performance as they can never change (i.e. they are read-only).
547
548                         if (_declsecCache == null) {
549                                 lock (_lockObject) {
550                                         if (_declsecCache == null) {
551                                                 _declsecCache = new Hashtable ();
552                                         }
553                                 }
554                         }
555
556                         PermissionSetCollection psc = null;
557                         lock (_lockObject) {
558                                 object key = (object) (int) permissions;
559                                 psc = (PermissionSetCollection) _declsecCache [key];
560                                 if (psc == null) {
561                                         // create permissionset and add it to the cache
562                                         byte[] data = new byte [length];
563                                         Marshal.Copy (permissions, data, 0, length);
564                                         psc = DecodeCollection (data);
565                                         _declsecCache.Add (key, psc);
566                                 }
567                         }
568                         return psc;
569                 }
570
571                 internal static PermissionSetCollection DecodeCollection (byte[] encodedPermissions)
572                 {
573                         if ((encodedPermissions == null) || (encodedPermissions.Length < 1))
574                                 throw new SecurityException ("Invalid metadata format.");
575
576                         switch (encodedPermissions [0]) {
577                         case 60:
578                                 // Fx 1.0/1.1 declarative security permissions metadata is in Unicode-encoded XML
579                                 throw new SecurityException (Locale.GetText ("1.0 metadata format doesn't support collections."));
580                         case 0x2E:
581                                 // Fx 2.0 are encoded "somewhat, but not enough, like" custom attributes
582                                 // note: we still support the older format!
583                                 return PermissionSetCollection.CreateFromBinaryFormat (encodedPermissions);
584                         default:
585                                 throw new SecurityException (Locale.GetText ("Unknown metadata format."));
586                         }
587                 }
588 #endif
589
590                 private static IPermission UnmanagedCode {
591                         get {
592                                 lock (_lockObject) {
593                                         if (_unmanagedCode == null)
594                                                 _unmanagedCode = new SecurityPermission (SecurityPermissionFlag.UnmanagedCode);
595                                 }
596                                 return _unmanagedCode;
597                         }
598                 }
599
600                 //  security check when using reflection
601
602                 [MethodImplAttribute(MethodImplOptions.InternalCall)]
603                 private static unsafe extern bool GetLinkDemandSecurity (MethodBase method, RuntimeDeclSecurityActions *cdecl, RuntimeDeclSecurityActions *mdecl);
604
605                 // When using reflection LinkDemand are promoted to full Demand (i.e. stack walk)
606                 internal unsafe static void ReflectedLinkDemandInvoke (MethodBase mb)
607                 {
608                         RuntimeDeclSecurityActions klass;
609                         RuntimeDeclSecurityActions method;
610
611                         if (!GetLinkDemandSecurity (mb, &klass, &method))
612                                 return;
613
614                         PermissionSet ps = null;
615
616                         if (klass.cas.size > 0) {
617                                 ps = Decode (klass.cas.blob, klass.cas.size);
618                         }
619                         if (klass.noncas.size > 0) {
620                                 PermissionSet p = Decode (klass.noncas.blob, klass.noncas.size);
621                                 ps = (ps == null) ? p : ps.Union (p);
622                         }
623
624                         if (method.cas.size > 0) {
625                                 PermissionSet p = Decode (method.cas.blob, method.cas.size);
626                                 ps = (ps == null) ? p : ps.Union (p);
627                         }
628                         if (method.noncas.size > 0) {
629                                 PermissionSet p = Decode (method.noncas.blob, method.noncas.size);
630                                 ps = (ps == null) ? p : ps.Union (p);
631                         }
632
633                         // in this case we union-ed the permission sets because we want to do 
634                         // a single stack walk (not up to 4).
635                         if (ps != null)
636                                 ps.Demand ();
637 #if NET_2_0
638                         // Process LinkDemandChoice (2.0)
639                         if (klass.choice.size > 0) {
640                                 PermissionSetCollection psc = DecodeCollection (klass.choice.blob, klass.choice.size);
641                                 psc.DemandChoice ();
642                         }
643                         if (method.choice.size > 0) {
644                                 PermissionSetCollection psc = DecodeCollection (method.choice.blob, method.choice.size);
645                                 psc.DemandChoice ();
646                         }
647 #endif
648                 }
649
650                 internal unsafe static bool ReflectedLinkDemandQuery (MethodBase mb)
651                 {
652                         RuntimeDeclSecurityActions klass;
653                         RuntimeDeclSecurityActions method;
654
655                         if (!GetLinkDemandSecurity (mb, &klass, &method))
656                                 return true;
657
658                         return LinkDemand (mb.ReflectedType.Assembly, &klass, &method);
659                 }
660
661                 // internal - get called at JIT time
662
663                 private static void DemandUnmanaged ()
664                 {
665                         UnmanagedCode.Demand ();
666                 }
667
668                 private unsafe static bool LinkDemand (Assembly a, RuntimeDeclSecurityActions *klass, RuntimeDeclSecurityActions *method)
669                 {
670                         try {
671                                 PermissionSet ps = null;
672                                 bool result = true;
673                                 if (klass->cas.size > 0) {
674                                         ps = Decode (klass->cas.blob, klass->cas.size);
675                                         result = (SecurityManager.CheckPermissionSet (a, ps, false) == null);
676                                 }
677                                 if (result && (klass->noncas.size > 0)) {
678                                         ps = Decode (klass->noncas.blob, klass->noncas.size);
679                                         result = (SecurityManager.CheckPermissionSet (a, ps, true) == null);
680                                 }
681
682                                 if (result && (method->cas.size > 0)) {
683                                         ps = Decode (method->cas.blob, method->cas.size);
684                                         result = (SecurityManager.CheckPermissionSet (a, ps, false) == null);
685                                 }
686                                 if (result && (method->noncas.size > 0)) {
687                                         ps = Decode (method->noncas.blob, method->noncas.size);
688                                         result = (SecurityManager.CheckPermissionSet (a, ps, true) == null);
689                                 }
690 #if NET_2_0
691                                 // success if one of the permission is granted
692                                 if (result && (klass->choice.size > 0)) {
693                                         PermissionSetCollection psc = DecodeCollection (klass->choice.blob, klass->choice.size);
694                                         if (psc.Count > 0) {
695                                                 result = false;
696                                                 foreach (PermissionSet pset in psc) {
697                                                         if (SecurityManager.CheckPermissionSet (a, pset, false) == null) {
698                                                                 result = true;
699                                                                 break;
700                                                         }
701                                                 }
702                                         }
703                                 }
704                                 if (result && (method->choice.size > 0)) {
705                                         PermissionSetCollection psc = DecodeCollection (method->choice.blob, method->choice.size);
706                                         if (psc.Count > 0) {
707                                                 result = false;
708                                                 foreach (PermissionSet pset in psc) {
709                                                         if (SecurityManager.CheckPermissionSet (a, pset, false) == null) {
710                                                                 result = true;
711                                                                 break;
712                                                         }
713                                                 }
714                                         }
715                                 }
716 #endif
717                                 return result;
718                         }
719                         catch (SecurityException) {
720                                 return false;
721                         }
722                 }
723
724                 private static bool LinkDemandFullTrust (Assembly a)
725                 {
726                         // FullTrust is immutable (and means Unrestricted) 
727                         // so we can skip the subset operations and jump to IsUnrestricted.
728                         PermissionSet granted = a.GrantedPermissionSet;
729                         if ((granted != null) && !granted.IsUnrestricted ())
730                                 return false;
731
732                         PermissionSet denied = a.DeniedPermissionSet;
733                         if ((denied != null) && !denied.IsEmpty ())
734                                 return false;
735
736                         return true;
737                 }
738
739                 private static bool LinkDemandUnmanaged (Assembly a)
740                 {
741                         // note: we know that UnmanagedCode (SecurityPermission) implements IUnrestrictedPermission
742                         return IsGranted (a, UnmanagedCode);
743                 }
744
745                 // we try to provide as much details as possible to help debugging
746                 private static void LinkDemandSecurityException (int securityViolation, Assembly a, MethodInfo method)
747                 {
748                         string message = null;
749                         AssemblyName an = null;
750                         PermissionSet granted = null;
751                         PermissionSet refused = null;
752                         object demanded = null;
753                         IPermission failed = null;
754
755                         if (a != null) {
756                                 an = a.UnprotectedGetName ();
757                                 granted = a.GrantedPermissionSet;
758                                 refused = a.DeniedPermissionSet;
759                         }
760
761                         switch (securityViolation) {
762                         case 1: // MONO_JIT_LINKDEMAND_PERMISSION
763                                 message = Locale.GetText ("Permissions refused to call this method.");
764                                 break;
765                         case 2: // MONO_JIT_LINKDEMAND_APTC
766                                 message = Locale.GetText ("Partially trusted callers aren't allowed to call into this assembly.");
767                                 demanded = (object) DefaultPolicies.FullTrust; // immutable
768                                 break;
769                         case 4: // MONO_JIT_LINKDEMAND_ECMA
770                                 message = Locale.GetText ("Calling internal calls is restricted to ECMA signed assemblies.");
771                                 break;
772                         case 8: // MONO_JIT_LINKDEMAND_PINVOKE
773                                 message = Locale.GetText ("Calling unmanaged code isn't allowed from this assembly.");
774                                 demanded = (object) _unmanagedCode;
775                                 failed = _unmanagedCode;
776                                 break;
777                         default:
778                                 message = Locale.GetText ("JIT time LinkDemand failed.");
779                                 break;
780                         }
781
782                         throw new SecurityException (message, an, granted, refused, method, SecurityAction.LinkDemand, demanded, failed, null);
783                 }
784
785                 private static void InheritanceDemandSecurityException (int securityViolation, Assembly a, Type t, MethodInfo method)
786                 {
787                         string message = null;
788                         AssemblyName an = null;
789                         PermissionSet granted = null;
790                         PermissionSet refused = null;
791
792                         if (a != null) {
793                                 an = a.UnprotectedGetName ();
794                                 granted = a.GrantedPermissionSet;
795                                 refused = a.DeniedPermissionSet;
796                         }
797
798                         switch (securityViolation) {
799                         case 1: // MONO_METADATA_INHERITANCEDEMAND_CLASS
800                                 message = String.Format (Locale.GetText ("Class inheritance refused for {0}."), t);
801                                 break;
802                         case 2: // MONO_METADATA_INHERITANCEDEMAND_CLASS
803                                 message = Locale.GetText ("Method override refused.");
804                                 break;
805                         default:
806                                 message = Locale.GetText ("Load time InheritDemand failed.");
807                                 break;
808                         }
809
810                         throw new SecurityException (message, an, granted, refused, method, SecurityAction.LinkDemand, null, null, null);
811                 }
812
813                 // internal - get called by the class loader
814
815                 // Called when
816                 // - class inheritance
817                 // - method overrides
818                 private unsafe static bool InheritanceDemand (Assembly a, RuntimeDeclSecurityActions *actions)
819                 {
820                         try {
821                                 PermissionSet ps = null;
822                                 bool result = true;
823                                 if (actions->cas.size > 0) {
824                                         ps = Decode (actions->cas.blob, actions->cas.size);
825                                         result = (SecurityManager.CheckPermissionSet (a, ps, false) == null);
826                                 }
827                                 if (actions->noncas.size > 0) {
828                                         ps = Decode (actions->noncas.blob, actions->noncas.size);
829                                         result = (SecurityManager.CheckPermissionSet (a, ps, true) == null);
830                                 }
831 #if NET_2_0
832                                 // success if one of the permission is granted
833                                 if (result && (actions->choice.size > 0)) {
834                                         PermissionSetCollection psc = DecodeCollection (actions->choice.blob, actions->choice.size);
835                                         if (psc.Count > 0) {
836                                                 result = false;
837                                                 foreach (PermissionSet pset in psc) {
838                                                         if (SecurityManager.CheckPermissionSet (a, pset, false) == null) {
839                                                                 result = true;
840                                                                 break;
841                                                         }
842                                                 }
843                                         }
844                                 }
845 #endif
846                                 return result;
847                         }
848                         catch (SecurityException) {
849                                 return false;
850                         }
851                 }
852
853
854                 // internal - get called by JIT generated code
855
856                 private static void InternalDemand (IntPtr permissions, int length)
857                 {
858                         PermissionSet ps = Decode (permissions, length);
859                         ps.Demand ();
860                 }
861
862                 private static void InternalDemandChoice (IntPtr permissions, int length)
863                 {
864 #if NET_2_0
865                         PermissionSetCollection psc = DecodeCollection (permissions, length);
866                         psc.DemandChoice ();
867 #else
868                         throw new SecurityException ("SecurityAction.DemandChoice is only possible in 2.0");
869 #endif
870                 }
871         }
872 }