2005-09-02 Sebastien Pouliot <sebastien@ximian.com>
[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 NET_2_0
220                         if (granted.IsUnrestricted ())
221                                 return null;
222 #else
223                         if ((granted.Count == 0) && granted.IsUnrestricted ())
224                                 return null;
225 #endif
226                         if (ps.IsUnrestricted ())
227                                 return new SecurityPermission (SecurityPermissionFlag.NoFlags);
228
229                         foreach (IPermission p in ps) {
230                                 if (p is CodeAccessPermission) {
231                                         CodeAccessPermission grant = (CodeAccessPermission) granted.GetPermission (p.GetType ());
232                                         if (grant == null) {
233                                                 if (!granted.IsUnrestricted () || !(p is IUnrestrictedPermission)) {
234                                                         if (!p.IsSubsetOf (null))
235                                                                 return p;
236                                                 }
237                                         } else if (!p.IsSubsetOf (grant)) {
238                                                 return p;
239                                         }
240                                 } else {
241                                         // but non-CAS will throw on failure...
242                                         try {
243                                                 p.Demand ();
244                                         }
245                                         catch (SecurityException) {
246                                                 // ... so we catch
247                                                 return p;
248                                         }
249                                 }
250                         }
251                         return null;
252                 }
253
254                 [SecurityPermission (SecurityAction.Demand, ControlPolicy = true)]
255                 public static PolicyLevel LoadPolicyLevelFromFile (string path, PolicyLevelType type)
256                 {
257                         if (path == null)
258                                 throw new ArgumentNullException ("path");
259
260                         PolicyLevel pl = null;
261                         try {
262                                 pl = new PolicyLevel (type.ToString (), type);
263                                 pl.LoadFromFile (path);
264                         }
265                         catch (Exception e) {
266                                 throw new ArgumentException (Locale.GetText ("Invalid policy XML"), e);
267                         }
268                         return pl;
269                 }
270
271                 [SecurityPermission (SecurityAction.Demand, ControlPolicy = true)]
272                 public static PolicyLevel LoadPolicyLevelFromString (string str, PolicyLevelType type)
273                 {
274                         if (null == str)
275                                 throw new ArgumentNullException ("str");
276
277                         PolicyLevel pl = null;
278                         try {
279                                 pl = new PolicyLevel (type.ToString (), type);
280                                 pl.LoadFromString (str);
281                         }
282                         catch (Exception e) {
283                                 throw new ArgumentException (Locale.GetText ("Invalid policy XML"), e);
284                         }
285                         return pl;
286                 }
287
288                 [SecurityPermission (SecurityAction.Demand, ControlPolicy = true)]
289                 public static IEnumerator PolicyHierarchy ()
290                 {
291                         return Hierarchy;
292                 }
293
294                 public static PermissionSet ResolvePolicy (Evidence evidence)
295                 {
296                         // no evidence, no permission
297                         if (evidence == null)
298                                 return new PermissionSet (PermissionState.None);
299
300                         PermissionSet ps = null;
301                         // Note: can't call PolicyHierarchy since ControlPolicy isn't required to resolve policies
302                         IEnumerator ple = Hierarchy;
303                         while (ple.MoveNext ()) {
304                                 PolicyLevel pl = (PolicyLevel) ple.Current;
305                                 if (ResolvePolicyLevel (ref ps, pl, evidence)) {
306                                         break;  // i.e. PolicyStatementAttribute.LevelFinal
307                                 }
308                         }
309
310                         ResolveIdentityPermissions (ps, evidence);
311
312                         return ps;
313                 }
314
315 #if NET_2_0
316                 [MonoTODO ("(2.0) more tests are needed")]
317                 public static PermissionSet ResolvePolicy (Evidence[] evidences)
318                 {
319                         if ((evidences == null) || (evidences.Length == 0) ||
320                                 ((evidences.Length == 1) && (evidences [0].Count == 0))) {
321                                 return new PermissionSet (PermissionState.None);
322                         }
323
324                         // probably not optimal
325                         PermissionSet ps = ResolvePolicy (evidences [0]);
326                         for (int i=1; i < evidences.Length; i++) {
327                                 ps = ps.Intersect (ResolvePolicy (evidences [i]));
328                         }
329                         return ps;
330                 }
331
332                 public static PermissionSet ResolveSystemPolicy (Evidence evidence)
333                 {
334                         // no evidence, no permission
335                         if (evidence == null)
336                                 return new PermissionSet (PermissionState.None);
337
338                         // Note: can't call PolicyHierarchy since ControlPolicy isn't required to resolve policies
339                         PermissionSet ps = null;
340                         IEnumerator ple = Hierarchy;
341                         while (ple.MoveNext ()) {
342                                 PolicyLevel pl = (PolicyLevel) ple.Current;
343                                 if (pl.Type == PolicyLevelType.AppDomain)
344                                         break;
345                                 if (ResolvePolicyLevel (ref ps, pl, evidence))
346                                         break;  // i.e. PolicyStatementAttribute.LevelFinal
347                         }
348
349                         ResolveIdentityPermissions (ps, evidence);
350                         return ps;
351                 }
352 #endif
353
354                 static private SecurityPermission _execution = new SecurityPermission (SecurityPermissionFlag.Execution);
355
356                 public static PermissionSet ResolvePolicy (Evidence evidence, PermissionSet reqdPset, PermissionSet optPset, PermissionSet denyPset, out PermissionSet denied)
357                 {
358                         PermissionSet resolved = ResolvePolicy (evidence);
359                         // do we have the minimal permission requested by the assembly ?
360                         if ((reqdPset != null) && !reqdPset.IsSubsetOf (resolved)) {
361                                 throw new PolicyException (Locale.GetText (
362                                         "Policy doesn't grant the minimal permissions required to execute the assembly."));
363                         }
364
365                         // do we check for execution rights ?
366                         if (CheckExecutionRights) {
367                                 bool execute = false;
368                                 // an empty permissionset doesn't include Execution
369                                 if (resolved != null) {
370                                         // unless we have "Full Trust"...
371                                         if (resolved.IsUnrestricted ()) {
372                                                 execute = true;
373                                         } else {
374                                                 // ... we need to find a SecurityPermission
375                                                 IPermission security = resolved.GetPermission (typeof (SecurityPermission));
376                                                 execute = _execution.IsSubsetOf (security);
377                                         }
378                                 }
379
380                                 if (!execute) {
381                                         throw new PolicyException (Locale.GetText (
382                                                 "Policy doesn't grant the right to execute the assembly."));
383                                 }
384                         }
385
386                         denied = denyPset;
387                         return resolved;
388                 }
389
390                 public static IEnumerator ResolvePolicyGroups (Evidence evidence)
391                 {
392                         if (evidence == null)
393                                 throw new ArgumentNullException ("evidence");
394
395                         ArrayList al = new ArrayList ();
396                         // Note: can't call PolicyHierarchy since ControlPolicy isn't required to resolve policies
397                         IEnumerator ple = Hierarchy;
398                         while (ple.MoveNext ()) {
399                                 PolicyLevel pl = (PolicyLevel) ple.Current;
400                                 CodeGroup cg = pl.ResolveMatchingCodeGroups (evidence);
401                                 al.Add (cg);
402                         }
403                         return al.GetEnumerator ();
404                 }
405
406                 [SecurityPermission (SecurityAction.Demand, ControlPolicy = true)]
407                 public static void SavePolicy () 
408                 {
409                         IEnumerator e = Hierarchy;
410                         while (e.MoveNext ()) {
411                                 PolicyLevel level = (e.Current as PolicyLevel);
412                                 level.Save ();
413                         }
414                 }
415
416                 [SecurityPermission (SecurityAction.Demand, ControlPolicy = true)]
417                 public static void SavePolicyLevel (PolicyLevel level) 
418                 {
419                         // Yes this will throw a NullReferenceException, just like MS (see FDBK13121)
420                         level.Save ();
421                 }
422
423                 // private/internal stuff
424
425                 private static IEnumerator Hierarchy {
426                         get {
427                                 lock (_lockObject) {
428                                         if (_hierarchy == null)
429                                                 InitializePolicyHierarchy ();
430                                 }
431                                 return _hierarchy.GetEnumerator ();
432                         }
433                 }
434
435                 private static void InitializePolicyHierarchy ()
436                 {
437                         string machinePolicyPath = Path.GetDirectoryName (Environment.GetMachineConfigPath ());
438                         // note: use InternalGetFolderPath to avoid recursive policy initialization
439                         string userPolicyPath = Path.Combine (Environment.InternalGetFolderPath (Environment.SpecialFolder.ApplicationData), "mono");
440
441                         PolicyLevel enterprise = new PolicyLevel ("Enterprise", PolicyLevelType.Enterprise);
442                         _level = enterprise;
443                         enterprise.LoadFromFile (Path.Combine (machinePolicyPath, "enterprisesec.config"));
444
445                         PolicyLevel machine = new PolicyLevel ("Machine", PolicyLevelType.Machine);
446                         _level = machine;
447                         machine.LoadFromFile (Path.Combine (machinePolicyPath, "security.config"));
448
449                         PolicyLevel user = new PolicyLevel ("User", PolicyLevelType.User);
450                         _level = user;
451                         user.LoadFromFile (Path.Combine (userPolicyPath, "security.config"));
452
453                         ArrayList al = new ArrayList ();
454                         al.Add (enterprise);
455                         al.Add (machine);
456                         al.Add (user);
457
458                         _hierarchy = ArrayList.Synchronized (al);
459                         _level = null;
460                 }
461
462                 internal static bool ResolvePolicyLevel (ref PermissionSet ps, PolicyLevel pl, Evidence evidence)
463                 {
464                         PolicyStatement pst = pl.Resolve (evidence);
465                         if (pst != null) {
466                                 if (ps == null) {
467                                         // only for initial (first) policy level processed
468                                         ps = pst.PermissionSet;
469                                 } else {
470                                         ps = ps.Intersect (pst.PermissionSet);
471                                         if (ps == null) {
472                                                 // null is equals to None - exist that null can throw NullReferenceException ;-)
473                                                 ps = new PermissionSet (PermissionState.None);
474                                         }
475                                 }
476
477                                 if ((pst.Attributes & PolicyStatementAttribute.LevelFinal) == PolicyStatementAttribute.LevelFinal)
478                                         return true;
479                         }
480                         return false;
481                 }
482
483                 internal static void ResolveIdentityPermissions (PermissionSet ps, Evidence evidence)
484                 {
485 #if NET_2_0
486                         // in 2.0 identity permissions can now be unrestricted
487                         if (ps.IsUnrestricted ())
488                                 return;
489 #endif
490                         // Only host evidence are used for policy resolution
491                         IEnumerator ee = evidence.GetHostEnumerator ();
492                         while (ee.MoveNext ()) {
493                                 IIdentityPermissionFactory ipf = (ee.Current as IIdentityPermissionFactory);
494                                 if (ipf != null) {
495                                         IPermission p = ipf.CreateIdentityPermission (evidence);
496                                         ps.AddPermission (p);
497                                 }
498                         }
499                 }
500
501                 internal static PolicyLevel ResolvingPolicyLevel {
502                         get { return _level; }
503                         set { _level = value; }
504                 }
505
506                 internal static PermissionSet Decode (IntPtr permissions, int length)
507                 {
508                         // Permission sets from the runtime (declarative security) can be cached
509                         // for performance as they can never change (i.e. they are read-only).
510                         PermissionSet ps = null;
511
512                         lock (_lockObject) {
513                                 if (_declsecCache == null) {
514                                         _declsecCache = new Hashtable ();
515                                 }
516
517                                 object key = (object) (int) permissions;
518                                 ps = (PermissionSet) _declsecCache [key];
519                                 if (ps == null) {
520                                         // create permissionset and add it to the cache
521                                         byte[] data = new byte [length];
522                                         Marshal.Copy (permissions, data, 0, length);
523                                         ps = Decode (data);
524                                         ps.DeclarativeSecurity = true;
525                                         _declsecCache.Add (key, ps);
526                                 }
527                         }
528                         return ps;
529                 }
530
531                 internal static PermissionSet Decode (byte[] encodedPermissions)
532                 {
533                         if ((encodedPermissions == null) || (encodedPermissions.Length < 1))
534                                 throw new SecurityException ("Invalid metadata format.");
535
536                         switch (encodedPermissions [0]) {
537                         case 60:
538                                 // Fx 1.0/1.1 declarative security permissions metadata is in Unicode-encoded XML
539                                 string xml = Encoding.Unicode.GetString (encodedPermissions);
540                                 return new PermissionSet (xml);
541                         case 0x2E:
542                                 // Fx 2.0 are encoded "somewhat, but not enough, like" custom attributes
543                                 // note: we still support the older format!
544                                 return PermissionSet.CreateFromBinaryFormat (encodedPermissions);
545                         default:
546                                 throw new SecurityException (Locale.GetText ("Unknown metadata format."));
547                         }
548                 }
549 #if NET_2_0
550                 internal static PermissionSetCollection DecodeCollection (IntPtr permissions, int length)
551                 {
552                         // Permission sets from the runtime (declarative security) can be cached
553                         // for performance as they can never change (i.e. they are read-only).
554
555                         if (_declsecCache == null) {
556                                 lock (_lockObject) {
557                                         if (_declsecCache == null) {
558                                                 _declsecCache = new Hashtable ();
559                                         }
560                                 }
561                         }
562
563                         PermissionSetCollection psc = null;
564                         lock (_lockObject) {
565                                 object key = (object) (int) permissions;
566                                 psc = (PermissionSetCollection) _declsecCache [key];
567                                 if (psc == null) {
568                                         // create permissionset and add it to the cache
569                                         byte[] data = new byte [length];
570                                         Marshal.Copy (permissions, data, 0, length);
571                                         psc = DecodeCollection (data);
572                                         _declsecCache.Add (key, psc);
573                                 }
574                         }
575                         return psc;
576                 }
577
578                 internal static PermissionSetCollection DecodeCollection (byte[] encodedPermissions)
579                 {
580                         if ((encodedPermissions == null) || (encodedPermissions.Length < 1))
581                                 throw new SecurityException ("Invalid metadata format.");
582
583                         switch (encodedPermissions [0]) {
584                         case 60:
585                                 // Fx 1.0/1.1 declarative security permissions metadata is in Unicode-encoded XML
586                                 throw new SecurityException (Locale.GetText ("1.0 metadata format doesn't support collections."));
587                         case 0x2E:
588                                 // Fx 2.0 are encoded "somewhat, but not enough, like" custom attributes
589                                 // note: we still support the older format!
590                                 return PermissionSetCollection.CreateFromBinaryFormat (encodedPermissions);
591                         default:
592                                 throw new SecurityException (Locale.GetText ("Unknown metadata format."));
593                         }
594                 }
595 #endif
596
597                 private static IPermission UnmanagedCode {
598                         get {
599                                 lock (_lockObject) {
600                                         if (_unmanagedCode == null)
601                                                 _unmanagedCode = new SecurityPermission (SecurityPermissionFlag.UnmanagedCode);
602                                 }
603                                 return _unmanagedCode;
604                         }
605                 }
606
607                 //  security check when using reflection
608
609                 [MethodImplAttribute(MethodImplOptions.InternalCall)]
610                 private static unsafe extern bool GetLinkDemandSecurity (MethodBase method, RuntimeDeclSecurityActions *cdecl, RuntimeDeclSecurityActions *mdecl);
611
612                 // When using reflection LinkDemand are promoted to full Demand (i.e. stack walk)
613                 internal unsafe static void ReflectedLinkDemandInvoke (MethodBase mb)
614                 {
615                         RuntimeDeclSecurityActions klass;
616                         RuntimeDeclSecurityActions method;
617
618                         if (!GetLinkDemandSecurity (mb, &klass, &method))
619                                 return;
620
621                         PermissionSet ps = null;
622
623                         if (klass.cas.size > 0) {
624                                 ps = Decode (klass.cas.blob, klass.cas.size);
625                         }
626                         if (klass.noncas.size > 0) {
627                                 PermissionSet p = Decode (klass.noncas.blob, klass.noncas.size);
628                                 ps = (ps == null) ? p : ps.Union (p);
629                         }
630
631                         if (method.cas.size > 0) {
632                                 PermissionSet p = Decode (method.cas.blob, method.cas.size);
633                                 ps = (ps == null) ? p : ps.Union (p);
634                         }
635                         if (method.noncas.size > 0) {
636                                 PermissionSet p = Decode (method.noncas.blob, method.noncas.size);
637                                 ps = (ps == null) ? p : ps.Union (p);
638                         }
639
640                         // in this case we union-ed the permission sets because we want to do 
641                         // a single stack walk (not up to 4).
642                         if (ps != null)
643                                 ps.Demand ();
644 #if NET_2_0
645                         // Process LinkDemandChoice (2.0)
646                         if (klass.choice.size > 0) {
647                                 PermissionSetCollection psc = DecodeCollection (klass.choice.blob, klass.choice.size);
648                                 psc.DemandChoice ();
649                         }
650                         if (method.choice.size > 0) {
651                                 PermissionSetCollection psc = DecodeCollection (method.choice.blob, method.choice.size);
652                                 psc.DemandChoice ();
653                         }
654 #endif
655                 }
656
657                 internal unsafe static bool ReflectedLinkDemandQuery (MethodBase mb)
658                 {
659                         RuntimeDeclSecurityActions klass;
660                         RuntimeDeclSecurityActions method;
661
662                         if (!GetLinkDemandSecurity (mb, &klass, &method))
663                                 return true;
664
665                         return LinkDemand (mb.ReflectedType.Assembly, &klass, &method);
666                 }
667
668                 // internal - get called at JIT time
669
670                 private static void DemandUnmanaged ()
671                 {
672                         UnmanagedCode.Demand ();
673                 }
674
675                 private unsafe static bool LinkDemand (Assembly a, RuntimeDeclSecurityActions *klass, RuntimeDeclSecurityActions *method)
676                 {
677                         try {
678                                 PermissionSet ps = null;
679                                 bool result = true;
680                                 if (klass->cas.size > 0) {
681                                         ps = Decode (klass->cas.blob, klass->cas.size);
682                                         result = (SecurityManager.CheckPermissionSet (a, ps, false) == null);
683                                 }
684                                 if (result && (klass->noncas.size > 0)) {
685                                         ps = Decode (klass->noncas.blob, klass->noncas.size);
686                                         result = (SecurityManager.CheckPermissionSet (a, ps, true) == null);
687                                 }
688
689                                 if (result && (method->cas.size > 0)) {
690                                         ps = Decode (method->cas.blob, method->cas.size);
691                                         result = (SecurityManager.CheckPermissionSet (a, ps, false) == null);
692                                 }
693                                 if (result && (method->noncas.size > 0)) {
694                                         ps = Decode (method->noncas.blob, method->noncas.size);
695                                         result = (SecurityManager.CheckPermissionSet (a, ps, true) == null);
696                                 }
697 #if NET_2_0
698                                 // success if one of the permission is granted
699                                 if (result && (klass->choice.size > 0)) {
700                                         PermissionSetCollection psc = DecodeCollection (klass->choice.blob, klass->choice.size);
701                                         if (psc.Count > 0) {
702                                                 result = false;
703                                                 foreach (PermissionSet pset in psc) {
704                                                         if (SecurityManager.CheckPermissionSet (a, pset, false) == null) {
705                                                                 result = true;
706                                                                 break;
707                                                         }
708                                                 }
709                                         }
710                                 }
711                                 if (result && (method->choice.size > 0)) {
712                                         PermissionSetCollection psc = DecodeCollection (method->choice.blob, method->choice.size);
713                                         if (psc.Count > 0) {
714                                                 result = false;
715                                                 foreach (PermissionSet pset in psc) {
716                                                         if (SecurityManager.CheckPermissionSet (a, pset, false) == null) {
717                                                                 result = true;
718                                                                 break;
719                                                         }
720                                                 }
721                                         }
722                                 }
723 #endif
724                                 return result;
725                         }
726                         catch (SecurityException) {
727                                 return false;
728                         }
729                 }
730
731                 private static bool LinkDemandFullTrust (Assembly a)
732                 {
733                         // FullTrust is immutable (and means Unrestricted) 
734                         // so we can skip the subset operations and jump to IsUnrestricted.
735                         PermissionSet granted = a.GrantedPermissionSet;
736                         if ((granted != null) && !granted.IsUnrestricted ())
737                                 return false;
738
739                         PermissionSet denied = a.DeniedPermissionSet;
740                         if ((denied != null) && !denied.IsEmpty ())
741                                 return false;
742
743                         return true;
744                 }
745
746                 private static bool LinkDemandUnmanaged (Assembly a)
747                 {
748                         // note: we know that UnmanagedCode (SecurityPermission) implements IUnrestrictedPermission
749                         return IsGranted (a, UnmanagedCode);
750                 }
751
752                 // we try to provide as much details as possible to help debugging
753                 private static void LinkDemandSecurityException (int securityViolation, Assembly a, MethodInfo method)
754                 {
755                         string message = null;
756                         AssemblyName an = null;
757                         PermissionSet granted = null;
758                         PermissionSet refused = null;
759                         object demanded = null;
760                         IPermission failed = null;
761
762                         if (a != null) {
763                                 an = a.UnprotectedGetName ();
764                                 granted = a.GrantedPermissionSet;
765                                 refused = a.DeniedPermissionSet;
766                         }
767
768                         switch (securityViolation) {
769                         case 1: // MONO_JIT_LINKDEMAND_PERMISSION
770                                 message = Locale.GetText ("Permissions refused to call this method.");
771                                 break;
772                         case 2: // MONO_JIT_LINKDEMAND_APTC
773                                 message = Locale.GetText ("Partially trusted callers aren't allowed to call into this assembly.");
774                                 demanded = (object) DefaultPolicies.FullTrust; // immutable
775                                 break;
776                         case 4: // MONO_JIT_LINKDEMAND_ECMA
777                                 message = Locale.GetText ("Calling internal calls is restricted to ECMA signed assemblies.");
778                                 break;
779                         case 8: // MONO_JIT_LINKDEMAND_PINVOKE
780                                 message = Locale.GetText ("Calling unmanaged code isn't allowed from this assembly.");
781                                 demanded = (object) _unmanagedCode;
782                                 failed = _unmanagedCode;
783                                 break;
784                         default:
785                                 message = Locale.GetText ("JIT time LinkDemand failed.");
786                                 break;
787                         }
788
789                         throw new SecurityException (message, an, granted, refused, method, SecurityAction.LinkDemand, demanded, failed, null);
790                 }
791
792                 private static void InheritanceDemandSecurityException (int securityViolation, Assembly a, Type t, MethodInfo method)
793                 {
794                         string message = null;
795                         AssemblyName an = null;
796                         PermissionSet granted = null;
797                         PermissionSet refused = null;
798
799                         if (a != null) {
800                                 an = a.UnprotectedGetName ();
801                                 granted = a.GrantedPermissionSet;
802                                 refused = a.DeniedPermissionSet;
803                         }
804
805                         switch (securityViolation) {
806                         case 1: // MONO_METADATA_INHERITANCEDEMAND_CLASS
807                                 message = String.Format (Locale.GetText ("Class inheritance refused for {0}."), t);
808                                 break;
809                         case 2: // MONO_METADATA_INHERITANCEDEMAND_CLASS
810                                 message = Locale.GetText ("Method override refused.");
811                                 break;
812                         default:
813                                 message = Locale.GetText ("Load time InheritDemand failed.");
814                                 break;
815                         }
816
817                         throw new SecurityException (message, an, granted, refused, method, SecurityAction.InheritanceDemand, null, null, null);
818                 }
819
820                 // internal - get called by the class loader
821
822                 // Called when
823                 // - class inheritance
824                 // - method overrides
825                 private unsafe static bool InheritanceDemand (AppDomain ad, Assembly a, RuntimeDeclSecurityActions *actions)
826                 {
827                         try {
828                                 PermissionSet ps = null;
829                                 bool result = true;
830                                 if (actions->cas.size > 0) {
831                                         ps = Decode (actions->cas.blob, actions->cas.size);
832                                         result = (SecurityManager.CheckPermissionSet (a, ps, false) == null);
833                                         if (result) {
834                                                 // also check appdomain
835                                                 result = (SecurityManager.CheckPermissionSet (ad, ps) == null);
836                                         }
837                                 }
838                                 if (actions->noncas.size > 0) {
839                                         ps = Decode (actions->noncas.blob, actions->noncas.size);
840                                         result = (SecurityManager.CheckPermissionSet (a, ps, true) == null);
841                                         if (result) {
842                                                 // also check appdomain
843                                                 result = (SecurityManager.CheckPermissionSet (ad, ps) == null);
844                                         }
845                                 }
846 #if NET_2_0
847                                 // success if one of the permission is granted
848                                 if (result && (actions->choice.size > 0)) {
849                                         PermissionSetCollection psc = DecodeCollection (actions->choice.blob, actions->choice.size);
850                                         if (psc.Count > 0) {
851                                                 result = false;
852                                                 foreach (PermissionSet pset in psc) {
853                                                         if (SecurityManager.CheckPermissionSet (a, pset, false) == null) {
854                                                                 result = (SecurityManager.CheckPermissionSet (ad, pset) == null);
855                                                                 break;
856                                                         }
857                                                 }
858                                         }
859                                 }
860 #endif
861                                 return result;
862                         }
863                         catch (SecurityException) {
864                                 return false;
865                         }
866                 }
867
868
869                 // internal - get called by JIT generated code
870
871                 private static void InternalDemand (IntPtr permissions, int length)
872                 {
873                         PermissionSet ps = Decode (permissions, length);
874                         ps.Demand ();
875                 }
876
877                 private static void InternalDemandChoice (IntPtr permissions, int length)
878                 {
879 #if NET_2_0
880                         PermissionSetCollection psc = DecodeCollection (permissions, length);
881                         psc.DemandChoice ();
882 #else
883                         throw new SecurityException ("SecurityAction.DemandChoice is only possible in 2.0");
884 #endif
885                 }
886         }
887 }