2 // System.Security.SecurityManager.cs
5 // Nick Drochak(ndrochak@gol.com)
6 // Sebastien Pouliot <sebastien@ximian.com>
9 // Portions (C) 2004 Motus Technologies Inc. (http://www.motus.com)
10 // Copyright (C) 2004-2005 Novell, Inc (http://www.novell.com)
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:
20 // The above copyright notice and this permission notice shall be
21 // included in all copies or substantial portions of the Software.
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.
32 using System.Collections;
33 using System.Globalization;
35 using System.Reflection;
36 using System.Runtime.CompilerServices;
37 using System.Runtime.InteropServices;
38 using System.Security.Permissions;
39 using System.Security.Policy;
44 namespace System.Security {
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;
55 public static class SecurityManager {
57 public sealed class SecurityManager {
59 private SecurityManager ()
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;
69 static SecurityManager ()
72 // http://msdn.microsoft.com/library/en-us/dnaskdr/html/askgui06032003.asp?frame=true
73 _lockObject = new object ();
78 extern public static bool CheckExecutionRights {
79 [MethodImplAttribute (MethodImplOptions.InternalCall)]
82 [MethodImplAttribute (MethodImplOptions.InternalCall)]
83 [SecurityPermission (SecurityAction.Demand, ControlPolicy = true)]
88 [Obsolete ("The security manager cannot be turned off on MS runtime")]
90 extern public static bool SecurityEnabled {
91 [MethodImplAttribute (MethodImplOptions.InternalCall)]
94 [MethodImplAttribute (MethodImplOptions.InternalCall)]
95 [SecurityPermission (SecurityAction.Demand, ControlPolicy = true)]
102 // NOTE: This method doesn't show in the class library status page because
103 // it cannot be "found" with the StrongNameIdentityPermission for ECMA key.
105 [MonoTODO ("works for fulltrust (empty), documentation doesn't really make sense, type wise")]
106 [StrongNameIdentityPermission (SecurityAction.LinkDemand, PublicKey = "0x00000000000000000400000000000000")]
107 public static void GetZoneAndOrigin (out ArrayList zone, out ArrayList origin)
109 zone = new ArrayList ();
110 origin = new ArrayList ();
114 public static bool IsGranted (IPermission perm)
118 if (!SecurityEnabled)
122 // - Only check the caller (no stack walk required)
123 // - Not affected by overrides (like Assert, Deny and PermitOnly)
124 // - calls IsSubsetOf even for non CAS permissions
125 // (i.e. it does call Demand so any code there won't be executed)
127 // with 2.0 identity permission are unrestrictable
128 return IsGranted (Assembly.GetCallingAssembly (), perm);
130 if (perm is IUnrestrictedPermission)
131 return IsGranted (Assembly.GetCallingAssembly (), perm);
133 return IsGrantedRestricted (Assembly.GetCallingAssembly (), perm);
138 // only for permissions that do not implement IUnrestrictedPermission
139 internal static bool IsGrantedRestricted (Assembly a, IPermission perm)
141 PermissionSet granted = a.GrantedPermissionSet;
\r
142 if (granted != null) {
\r
143 CodeAccessPermission grant = (CodeAccessPermission) granted.GetPermission (perm.GetType ());
144 if (!perm.IsSubsetOf (grant)) {
149 PermissionSet denied = a.DeniedPermissionSet;
150 if (denied != null) {
\r
151 CodeAccessPermission refuse = (CodeAccessPermission) a.DeniedPermissionSet.GetPermission (perm.GetType ());
152 if ((refuse != null) && perm.IsSubsetOf (refuse))
158 // note: in 2.0 *all* permissions (including identity permissions) support unrestricted
159 internal static bool IsGranted (Assembly a, IPermission perm)
161 PermissionSet granted = a.GrantedPermissionSet;
\r
162 if ((granted != null) && !granted.IsUnrestricted ()) {
\r
163 CodeAccessPermission grant = (CodeAccessPermission) granted.GetPermission (perm.GetType ());
\r
164 if (!perm.IsSubsetOf (grant)) {
\r
169 PermissionSet denied = a.DeniedPermissionSet;
\r
170 if ((denied != null) && !denied.IsEmpty ()) {
\r
171 if (denied.IsUnrestricted ())
\r
173 CodeAccessPermission refuse = (CodeAccessPermission) a.DeniedPermissionSet.GetPermission (perm.GetType ());
\r
174 if ((refuse != null) && perm.IsSubsetOf (refuse))
\r
180 internal static IPermission CheckPermissionSet (Assembly a, PermissionSet ps, bool noncas)
185 foreach (IPermission p in ps) {
186 // note: this may contains non CAS permissions
187 if ((!noncas) && (p is CodeAccessPermission)) {
189 if (!IsGranted (a, p))
192 if (p is IUnrestrictedPermission) {
193 if (!IsGranted (a, p))
196 if (!IsGrantedRestricted (a, p))
201 // but non-CAS will throw on failure...
205 catch (SecurityException) {
214 internal static IPermission CheckPermissionSet (AppDomain ad, PermissionSet ps)
216 if ((ps == null) || ps.IsEmpty ())
219 PermissionSet granted = ad.GrantedPermissionSet;
223 if (granted.IsUnrestricted ())
226 if ((granted.Count == 0) && granted.IsUnrestricted ())
229 if (ps.IsUnrestricted ())
230 return new SecurityPermission (SecurityPermissionFlag.NoFlags);
232 foreach (IPermission p in ps) {
233 if (p is CodeAccessPermission) {
234 CodeAccessPermission grant = (CodeAccessPermission) granted.GetPermission (p.GetType ());
236 if (!granted.IsUnrestricted () || !(p is IUnrestrictedPermission)) {
237 if (!p.IsSubsetOf (null))
240 } else if (!p.IsSubsetOf (grant)) {
244 // but non-CAS will throw on failure...
248 catch (SecurityException) {
257 [SecurityPermission (SecurityAction.Demand, ControlPolicy = true)]
258 public static PolicyLevel LoadPolicyLevelFromFile (string path, PolicyLevelType type)
261 throw new ArgumentNullException ("path");
263 PolicyLevel pl = null;
265 pl = new PolicyLevel (type.ToString (), type);
266 pl.LoadFromFile (path);
268 catch (Exception e) {
269 throw new ArgumentException (Locale.GetText ("Invalid policy XML"), e);
274 [SecurityPermission (SecurityAction.Demand, ControlPolicy = true)]
275 public static PolicyLevel LoadPolicyLevelFromString (string str, PolicyLevelType type)
278 throw new ArgumentNullException ("str");
280 PolicyLevel pl = null;
282 pl = new PolicyLevel (type.ToString (), type);
283 pl.LoadFromString (str);
285 catch (Exception e) {
286 throw new ArgumentException (Locale.GetText ("Invalid policy XML"), e);
291 [SecurityPermission (SecurityAction.Demand, ControlPolicy = true)]
292 public static IEnumerator PolicyHierarchy ()
297 public static PermissionSet ResolvePolicy (Evidence evidence)
299 // no evidence, no permission
300 if (evidence == null)
301 return new PermissionSet (PermissionState.None);
303 PermissionSet ps = null;
304 // Note: can't call PolicyHierarchy since ControlPolicy isn't required to resolve policies
305 IEnumerator ple = Hierarchy;
306 while (ple.MoveNext ()) {
307 PolicyLevel pl = (PolicyLevel) ple.Current;
308 if (ResolvePolicyLevel (ref ps, pl, evidence)) {
309 break; // i.e. PolicyStatementAttribute.LevelFinal
313 ResolveIdentityPermissions (ps, evidence);
319 [MonoTODO ("(2.0) more tests are needed")]
320 public static PermissionSet ResolvePolicy (Evidence[] evidences)
322 if ((evidences == null) || (evidences.Length == 0) ||
323 ((evidences.Length == 1) && (evidences [0].Count == 0))) {
324 return new PermissionSet (PermissionState.None);
327 // probably not optimal
328 PermissionSet ps = ResolvePolicy (evidences [0]);
329 for (int i=1; i < evidences.Length; i++) {
330 ps = ps.Intersect (ResolvePolicy (evidences [i]));
335 public static PermissionSet ResolveSystemPolicy (Evidence evidence)
337 // no evidence, no permission
338 if (evidence == null)
339 return new PermissionSet (PermissionState.None);
341 // Note: can't call PolicyHierarchy since ControlPolicy isn't required to resolve policies
342 PermissionSet ps = null;
343 IEnumerator ple = Hierarchy;
344 while (ple.MoveNext ()) {
345 PolicyLevel pl = (PolicyLevel) ple.Current;
346 if (pl.Type == PolicyLevelType.AppDomain)
348 if (ResolvePolicyLevel (ref ps, pl, evidence))
349 break; // i.e. PolicyStatementAttribute.LevelFinal
352 ResolveIdentityPermissions (ps, evidence);
357 static private SecurityPermission _execution = new SecurityPermission (SecurityPermissionFlag.Execution);
359 public static PermissionSet ResolvePolicy (Evidence evidence, PermissionSet reqdPset, PermissionSet optPset, PermissionSet denyPset, out PermissionSet denied)
361 PermissionSet resolved = ResolvePolicy (evidence);
362 // do we have the minimal permission requested by the assembly ?
363 if ((reqdPset != null) && !reqdPset.IsSubsetOf (resolved)) {
364 throw new PolicyException (Locale.GetText (
365 "Policy doesn't grant the minimal permissions required to execute the assembly."));
368 // do we check for execution rights ?
369 if (CheckExecutionRights) {
370 bool execute = false;
371 // an empty permissionset doesn't include Execution
372 if (resolved != null) {
373 // unless we have "Full Trust"...
374 if (resolved.IsUnrestricted ()) {
377 // ... we need to find a SecurityPermission
378 IPermission security = resolved.GetPermission (typeof (SecurityPermission));
379 execute = _execution.IsSubsetOf (security);
384 throw new PolicyException (Locale.GetText (
385 "Policy doesn't grant the right to execute the assembly."));
393 public static IEnumerator ResolvePolicyGroups (Evidence evidence)
395 if (evidence == null)
396 throw new ArgumentNullException ("evidence");
398 ArrayList al = new ArrayList ();
399 // Note: can't call PolicyHierarchy since ControlPolicy isn't required to resolve policies
400 IEnumerator ple = Hierarchy;
401 while (ple.MoveNext ()) {
402 PolicyLevel pl = (PolicyLevel) ple.Current;
403 CodeGroup cg = pl.ResolveMatchingCodeGroups (evidence);
406 return al.GetEnumerator ();
409 [SecurityPermission (SecurityAction.Demand, ControlPolicy = true)]
410 public static void SavePolicy ()
412 IEnumerator e = Hierarchy;
413 while (e.MoveNext ()) {
414 PolicyLevel level = (e.Current as PolicyLevel);
419 [SecurityPermission (SecurityAction.Demand, ControlPolicy = true)]
420 public static void SavePolicyLevel (PolicyLevel level)
422 // Yes this will throw a NullReferenceException, just like MS (see FDBK13121)
426 // private/internal stuff
428 private static IEnumerator Hierarchy {
431 if (_hierarchy == null)
432 InitializePolicyHierarchy ();
434 return _hierarchy.GetEnumerator ();
438 private static void InitializePolicyHierarchy ()
440 string machinePolicyPath = Path.GetDirectoryName (Environment.GetMachineConfigPath ());
441 // note: use InternalGetFolderPath to avoid recursive policy initialization
442 string userPolicyPath = Path.Combine (Environment.InternalGetFolderPath (Environment.SpecialFolder.ApplicationData), "mono");
444 PolicyLevel enterprise = new PolicyLevel ("Enterprise", PolicyLevelType.Enterprise);
446 enterprise.LoadFromFile (Path.Combine (machinePolicyPath, "enterprisesec.config"));
448 PolicyLevel machine = new PolicyLevel ("Machine", PolicyLevelType.Machine);
450 machine.LoadFromFile (Path.Combine (machinePolicyPath, "security.config"));
452 PolicyLevel user = new PolicyLevel ("User", PolicyLevelType.User);
454 user.LoadFromFile (Path.Combine (userPolicyPath, "security.config"));
456 ArrayList al = new ArrayList ();
461 _hierarchy = ArrayList.Synchronized (al);
465 internal static bool ResolvePolicyLevel (ref PermissionSet ps, PolicyLevel pl, Evidence evidence)
467 PolicyStatement pst = pl.Resolve (evidence);
470 // only for initial (first) policy level processed
471 ps = pst.PermissionSet;
473 ps = ps.Intersect (pst.PermissionSet);
475 // null is equals to None - exist that null can throw NullReferenceException ;-)
476 ps = new PermissionSet (PermissionState.None);
480 if ((pst.Attributes & PolicyStatementAttribute.LevelFinal) == PolicyStatementAttribute.LevelFinal)
486 internal static void ResolveIdentityPermissions (PermissionSet ps, Evidence evidence)
489 // in 2.0 identity permissions can now be unrestricted
490 if (ps.IsUnrestricted ())
493 // Only host evidence are used for policy resolution
494 IEnumerator ee = evidence.GetHostEnumerator ();
495 while (ee.MoveNext ()) {
496 IIdentityPermissionFactory ipf = (ee.Current as IIdentityPermissionFactory);
498 IPermission p = ipf.CreateIdentityPermission (evidence);
499 ps.AddPermission (p);
504 internal static PolicyLevel ResolvingPolicyLevel {
505 get { return _level; }
506 set { _level = value; }
509 internal static PermissionSet Decode (IntPtr permissions, int length)
511 // Permission sets from the runtime (declarative security) can be cached
512 // for performance as they can never change (i.e. they are read-only).
513 PermissionSet ps = null;
516 if (_declsecCache == null) {
517 _declsecCache = new Hashtable ();
520 object key = (object) (int) permissions;
521 ps = (PermissionSet) _declsecCache [key];
523 // create permissionset and add it to the cache
524 byte[] data = new byte [length];
525 Marshal.Copy (permissions, data, 0, length);
527 ps.DeclarativeSecurity = true;
528 _declsecCache.Add (key, ps);
534 internal static PermissionSet Decode (byte[] encodedPermissions)
536 if ((encodedPermissions == null) || (encodedPermissions.Length < 1))
537 throw new SecurityException ("Invalid metadata format.");
539 switch (encodedPermissions [0]) {
541 // Fx 1.0/1.1 declarative security permissions metadata is in Unicode-encoded XML
542 string xml = Encoding.Unicode.GetString (encodedPermissions);
543 return new PermissionSet (xml);
545 // Fx 2.0 are encoded "somewhat, but not enough, like" custom attributes
546 // note: we still support the older format!
547 return PermissionSet.CreateFromBinaryFormat (encodedPermissions);
549 throw new SecurityException (Locale.GetText ("Unknown metadata format."));
553 private static IPermission UnmanagedCode {
556 if (_unmanagedCode == null)
557 _unmanagedCode = new SecurityPermission (SecurityPermissionFlag.UnmanagedCode);
559 return _unmanagedCode;
563 // security check when using reflection
565 [MethodImplAttribute(MethodImplOptions.InternalCall)]
566 private static unsafe extern bool GetLinkDemandSecurity (MethodBase method, RuntimeDeclSecurityActions *cdecl, RuntimeDeclSecurityActions *mdecl);
568 // When using reflection LinkDemand are promoted to full Demand (i.e. stack walk)
569 internal unsafe static void ReflectedLinkDemandInvoke (MethodBase mb)
571 RuntimeDeclSecurityActions klass;
572 RuntimeDeclSecurityActions method;
574 if (!GetLinkDemandSecurity (mb, &klass, &method))
577 PermissionSet ps = null;
579 if (klass.cas.size > 0) {
580 ps = Decode (klass.cas.blob, klass.cas.size);
582 if (klass.noncas.size > 0) {
583 PermissionSet p = Decode (klass.noncas.blob, klass.noncas.size);
584 ps = (ps == null) ? p : ps.Union (p);
587 if (method.cas.size > 0) {
588 PermissionSet p = Decode (method.cas.blob, method.cas.size);
589 ps = (ps == null) ? p : ps.Union (p);
591 if (method.noncas.size > 0) {
592 PermissionSet p = Decode (method.noncas.blob, method.noncas.size);
593 ps = (ps == null) ? p : ps.Union (p);
596 // in this case we union-ed the permission sets because we want to do
597 // a single stack walk (not up to 4).
602 internal unsafe static bool ReflectedLinkDemandQuery (MethodBase mb)
604 RuntimeDeclSecurityActions klass;
605 RuntimeDeclSecurityActions method;
607 if (!GetLinkDemandSecurity (mb, &klass, &method))
610 return LinkDemand (mb.ReflectedType.Assembly, &klass, &method);
613 // internal - get called at JIT time
615 private static void DemandUnmanaged ()
617 UnmanagedCode.Demand ();
620 private unsafe static bool LinkDemand (Assembly a, RuntimeDeclSecurityActions *klass, RuntimeDeclSecurityActions *method)
623 PermissionSet ps = null;
625 if (klass->cas.size > 0) {
626 ps = Decode (klass->cas.blob, klass->cas.size);
627 result = (SecurityManager.CheckPermissionSet (a, ps, false) == null);
629 if (result && (klass->noncas.size > 0)) {
630 ps = Decode (klass->noncas.blob, klass->noncas.size);
631 result = (SecurityManager.CheckPermissionSet (a, ps, true) == null);
634 if (result && (method->cas.size > 0)) {
635 ps = Decode (method->cas.blob, method->cas.size);
636 result = (SecurityManager.CheckPermissionSet (a, ps, false) == null);
638 if (result && (method->noncas.size > 0)) {
639 ps = Decode (method->noncas.blob, method->noncas.size);
640 result = (SecurityManager.CheckPermissionSet (a, ps, true) == null);
644 catch (SecurityException) {
649 private static bool LinkDemandFullTrust (Assembly a)
651 // FullTrust is immutable (and means Unrestricted)
652 // so we can skip the subset operations and jump to IsUnrestricted.
653 PermissionSet granted = a.GrantedPermissionSet;
654 if ((granted != null) && !granted.IsUnrestricted ())
657 PermissionSet denied = a.DeniedPermissionSet;
658 if ((denied != null) && !denied.IsEmpty ())
664 private static bool LinkDemandUnmanaged (Assembly a)
666 // note: we know that UnmanagedCode (SecurityPermission) implements IUnrestrictedPermission
667 return IsGranted (a, UnmanagedCode);
670 // we try to provide as much details as possible to help debugging
671 private static void LinkDemandSecurityException (int securityViolation, Assembly a, MethodInfo method)
673 string message = null;
674 AssemblyName an = null;
675 PermissionSet granted = null;
676 PermissionSet refused = null;
677 object demanded = null;
678 IPermission failed = null;
681 an = a.UnprotectedGetName ();
682 granted = a.GrantedPermissionSet;
683 refused = a.DeniedPermissionSet;
686 switch (securityViolation) {
687 case 1: // MONO_JIT_LINKDEMAND_PERMISSION
688 message = Locale.GetText ("Permissions refused to call this method.");
690 case 2: // MONO_JIT_LINKDEMAND_APTC
691 message = Locale.GetText ("Partially trusted callers aren't allowed to call into this assembly.");
692 demanded = (object) DefaultPolicies.FullTrust; // immutable
694 case 4: // MONO_JIT_LINKDEMAND_ECMA
695 message = Locale.GetText ("Calling internal calls is restricted to ECMA signed assemblies.");
697 case 8: // MONO_JIT_LINKDEMAND_PINVOKE
698 message = Locale.GetText ("Calling unmanaged code isn't allowed from this assembly.");
699 demanded = (object) _unmanagedCode;
700 failed = _unmanagedCode;
703 message = Locale.GetText ("JIT time LinkDemand failed.");
707 throw new SecurityException (message, an, granted, refused, method, SecurityAction.LinkDemand, demanded, failed, null);
710 private static void InheritanceDemandSecurityException (int securityViolation, Assembly a, Type t, MethodInfo method)
712 string message = null;
713 AssemblyName an = null;
714 PermissionSet granted = null;
715 PermissionSet refused = null;
718 an = a.UnprotectedGetName ();
719 granted = a.GrantedPermissionSet;
720 refused = a.DeniedPermissionSet;
723 switch (securityViolation) {
724 case 1: // MONO_METADATA_INHERITANCEDEMAND_CLASS
725 message = String.Format (Locale.GetText ("Class inheritance refused for {0}."), t);
727 case 2: // MONO_METADATA_INHERITANCEDEMAND_CLASS
728 message = Locale.GetText ("Method override refused.");
731 message = Locale.GetText ("Load time InheritDemand failed.");
735 throw new SecurityException (message, an, granted, refused, method, SecurityAction.InheritanceDemand, null, null, null);
738 // internal - get called by the class loader
741 // - class inheritance
742 // - method overrides
743 private unsafe static bool InheritanceDemand (AppDomain ad, Assembly a, RuntimeDeclSecurityActions *actions)
746 PermissionSet ps = null;
748 if (actions->cas.size > 0) {
749 ps = Decode (actions->cas.blob, actions->cas.size);
750 result = (SecurityManager.CheckPermissionSet (a, ps, false) == null);
752 // also check appdomain
753 result = (SecurityManager.CheckPermissionSet (ad, ps) == null);
756 if (actions->noncas.size > 0) {
757 ps = Decode (actions->noncas.blob, actions->noncas.size);
758 result = (SecurityManager.CheckPermissionSet (a, ps, true) == null);
760 // also check appdomain
761 result = (SecurityManager.CheckPermissionSet (ad, ps) == null);
766 catch (SecurityException) {
772 // internal - get called by JIT generated code
774 private static void InternalDemand (IntPtr permissions, int length)
776 PermissionSet ps = Decode (permissions, length);
780 private static void InternalDemandChoice (IntPtr permissions, int length)
782 throw new SecurityException ("SecurityAction.DemandChoice was removed from 2.0");