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.
34 using System.Collections;
35 using System.Globalization;
37 using System.Reflection;
38 using System.Runtime.CompilerServices;
39 using System.Runtime.InteropServices;
40 using System.Security.Permissions;
41 using System.Security.Policy;
46 namespace System.Security {
48 // Must match MonoDeclSecurityActions in /mono/metadata/reflection.h
49 internal struct RuntimeDeclSecurityActions {
50 public RuntimeDeclSecurityEntry cas;
51 public RuntimeDeclSecurityEntry noncas;
52 public RuntimeDeclSecurityEntry choice;
57 public static class SecurityManager {
59 public sealed class SecurityManager {
61 private SecurityManager ()
65 private static object _lockObject;
66 private static ArrayList _hierarchy;
67 private static IPermission _unmanagedCode;
68 private static Hashtable _declsecCache;
69 private static PolicyLevel _level;
71 static SecurityManager ()
74 // http://msdn.microsoft.com/library/en-us/dnaskdr/html/askgui06032003.asp?frame=true
75 _lockObject = new object ();
80 extern public static bool CheckExecutionRights {
81 [MethodImplAttribute (MethodImplOptions.InternalCall)]
84 [MethodImplAttribute (MethodImplOptions.InternalCall)]
85 [SecurityPermission (SecurityAction.Demand, ControlPolicy = true)]
90 [Obsolete ("The security manager cannot be turned off on MS runtime")]
92 extern public static bool SecurityEnabled {
93 [MethodImplAttribute (MethodImplOptions.InternalCall)]
96 [MethodImplAttribute (MethodImplOptions.InternalCall)]
97 [SecurityPermission (SecurityAction.Demand, ControlPolicy = true)]
104 // NOTE: This method doesn't show in the class library status page because
105 // it cannot be "found" with the StrongNameIdentityPermission for ECMA key.
107 // FIXME works for fulltrust (empty), documentation doesn't really make sense, type wise
108 [MonoTODO ("CAS support is experimental (and unsupported). This method only works in FullTrust.")]
109 [StrongNameIdentityPermission (SecurityAction.LinkDemand, PublicKey = "0x00000000000000000400000000000000")]
110 public static void GetZoneAndOrigin (out ArrayList zone, out ArrayList origin)
112 zone = new ArrayList ();
113 origin = new ArrayList ();
117 public static bool IsGranted (IPermission perm)
121 if (!SecurityEnabled)
125 // - Only check the caller (no stack walk required)
126 // - Not affected by overrides (like Assert, Deny and PermitOnly)
127 // - calls IsSubsetOf even for non CAS permissions
128 // (i.e. it does call Demand so any code there won't be executed)
130 // with 2.0 identity permission are unrestrictable
131 return IsGranted (Assembly.GetCallingAssembly (), perm);
133 if (perm is IUnrestrictedPermission)
134 return IsGranted (Assembly.GetCallingAssembly (), perm);
136 return IsGrantedRestricted (Assembly.GetCallingAssembly (), perm);
141 // only for permissions that do not implement IUnrestrictedPermission
142 internal static bool IsGrantedRestricted (Assembly a, IPermission perm)
144 PermissionSet granted = a.GrantedPermissionSet;
\r
145 if (granted != null) {
\r
146 CodeAccessPermission grant = (CodeAccessPermission) granted.GetPermission (perm.GetType ());
147 if (!perm.IsSubsetOf (grant)) {
152 PermissionSet denied = a.DeniedPermissionSet;
153 if (denied != null) {
\r
154 CodeAccessPermission refuse = (CodeAccessPermission) a.DeniedPermissionSet.GetPermission (perm.GetType ());
155 if ((refuse != null) && perm.IsSubsetOf (refuse))
161 // note: in 2.0 *all* permissions (including identity permissions) support unrestricted
162 internal static bool IsGranted (Assembly a, IPermission perm)
164 PermissionSet granted = a.GrantedPermissionSet;
\r
165 if ((granted != null) && !granted.IsUnrestricted ()) {
\r
166 CodeAccessPermission grant = (CodeAccessPermission) granted.GetPermission (perm.GetType ());
\r
167 if (!perm.IsSubsetOf (grant)) {
\r
172 PermissionSet denied = a.DeniedPermissionSet;
\r
173 if ((denied != null) && !denied.IsEmpty ()) {
\r
174 if (denied.IsUnrestricted ())
\r
176 CodeAccessPermission refuse = (CodeAccessPermission) a.DeniedPermissionSet.GetPermission (perm.GetType ());
\r
177 if ((refuse != null) && perm.IsSubsetOf (refuse))
\r
183 internal static IPermission CheckPermissionSet (Assembly a, PermissionSet ps, bool noncas)
188 foreach (IPermission p in ps) {
189 // note: this may contains non CAS permissions
190 if ((!noncas) && (p is CodeAccessPermission)) {
192 if (!IsGranted (a, p))
195 if (p is IUnrestrictedPermission) {
196 if (!IsGranted (a, p))
199 if (!IsGrantedRestricted (a, p))
204 // but non-CAS will throw on failure...
208 catch (SecurityException) {
217 internal static IPermission CheckPermissionSet (AppDomain ad, PermissionSet ps)
219 if ((ps == null) || ps.IsEmpty ())
222 PermissionSet granted = ad.GrantedPermissionSet;
226 if (granted.IsUnrestricted ())
229 if ((granted.Count == 0) && granted.IsUnrestricted ())
232 if (ps.IsUnrestricted ())
233 return new SecurityPermission (SecurityPermissionFlag.NoFlags);
235 foreach (IPermission p in ps) {
236 if (p is CodeAccessPermission) {
237 CodeAccessPermission grant = (CodeAccessPermission) granted.GetPermission (p.GetType ());
239 if (!granted.IsUnrestricted () || !(p is IUnrestrictedPermission)) {
240 if (!p.IsSubsetOf (null))
243 } else if (!p.IsSubsetOf (grant)) {
247 // but non-CAS will throw on failure...
251 catch (SecurityException) {
260 [SecurityPermission (SecurityAction.Demand, ControlPolicy = true)]
261 public static PolicyLevel LoadPolicyLevelFromFile (string path, PolicyLevelType type)
264 throw new ArgumentNullException ("path");
266 PolicyLevel pl = null;
268 pl = new PolicyLevel (type.ToString (), type);
269 pl.LoadFromFile (path);
271 catch (Exception e) {
272 throw new ArgumentException (Locale.GetText ("Invalid policy XML"), e);
277 [SecurityPermission (SecurityAction.Demand, ControlPolicy = true)]
278 public static PolicyLevel LoadPolicyLevelFromString (string str, PolicyLevelType type)
281 throw new ArgumentNullException ("str");
283 PolicyLevel pl = null;
285 pl = new PolicyLevel (type.ToString (), type);
286 pl.LoadFromString (str);
288 catch (Exception e) {
289 throw new ArgumentException (Locale.GetText ("Invalid policy XML"), e);
294 [SecurityPermission (SecurityAction.Demand, ControlPolicy = true)]
295 public static IEnumerator PolicyHierarchy ()
300 public static PermissionSet ResolvePolicy (Evidence evidence)
302 // no evidence, no permission
303 if (evidence == null)
304 return new PermissionSet (PermissionState.None);
306 PermissionSet ps = null;
307 // Note: can't call PolicyHierarchy since ControlPolicy isn't required to resolve policies
308 IEnumerator ple = Hierarchy;
309 while (ple.MoveNext ()) {
310 PolicyLevel pl = (PolicyLevel) ple.Current;
311 if (ResolvePolicyLevel (ref ps, pl, evidence)) {
312 break; // i.e. PolicyStatementAttribute.LevelFinal
316 ResolveIdentityPermissions (ps, evidence);
322 [MonoTODO ("(2.0) more tests are needed")]
323 public static PermissionSet ResolvePolicy (Evidence[] evidences)
325 if ((evidences == null) || (evidences.Length == 0) ||
326 ((evidences.Length == 1) && (evidences [0].Count == 0))) {
327 return new PermissionSet (PermissionState.None);
330 // probably not optimal
331 PermissionSet ps = ResolvePolicy (evidences [0]);
332 for (int i=1; i < evidences.Length; i++) {
333 ps = ps.Intersect (ResolvePolicy (evidences [i]));
338 public static PermissionSet ResolveSystemPolicy (Evidence evidence)
340 // no evidence, no permission
341 if (evidence == null)
342 return new PermissionSet (PermissionState.None);
344 // Note: can't call PolicyHierarchy since ControlPolicy isn't required to resolve policies
345 PermissionSet ps = null;
346 IEnumerator ple = Hierarchy;
347 while (ple.MoveNext ()) {
348 PolicyLevel pl = (PolicyLevel) ple.Current;
349 if (pl.Type == PolicyLevelType.AppDomain)
351 if (ResolvePolicyLevel (ref ps, pl, evidence))
352 break; // i.e. PolicyStatementAttribute.LevelFinal
355 ResolveIdentityPermissions (ps, evidence);
360 static private SecurityPermission _execution = new SecurityPermission (SecurityPermissionFlag.Execution);
362 public static PermissionSet ResolvePolicy (Evidence evidence, PermissionSet reqdPset, PermissionSet optPset, PermissionSet denyPset, out PermissionSet denied)
364 PermissionSet resolved = ResolvePolicy (evidence);
365 // do we have the minimal permission requested by the assembly ?
366 if ((reqdPset != null) && !reqdPset.IsSubsetOf (resolved)) {
367 throw new PolicyException (Locale.GetText (
368 "Policy doesn't grant the minimal permissions required to execute the assembly."));
371 // do we check for execution rights ?
372 if (CheckExecutionRights) {
373 bool execute = false;
374 // an empty permissionset doesn't include Execution
375 if (resolved != null) {
376 // unless we have "Full Trust"...
377 if (resolved.IsUnrestricted ()) {
380 // ... we need to find a SecurityPermission
381 IPermission security = resolved.GetPermission (typeof (SecurityPermission));
382 execute = _execution.IsSubsetOf (security);
387 throw new PolicyException (Locale.GetText (
388 "Policy doesn't grant the right to execute the assembly."));
396 public static IEnumerator ResolvePolicyGroups (Evidence evidence)
398 if (evidence == null)
399 throw new ArgumentNullException ("evidence");
401 ArrayList al = new ArrayList ();
402 // Note: can't call PolicyHierarchy since ControlPolicy isn't required to resolve policies
403 IEnumerator ple = Hierarchy;
404 while (ple.MoveNext ()) {
405 PolicyLevel pl = (PolicyLevel) ple.Current;
406 CodeGroup cg = pl.ResolveMatchingCodeGroups (evidence);
409 return al.GetEnumerator ();
412 [SecurityPermission (SecurityAction.Demand, ControlPolicy = true)]
413 public static void SavePolicy ()
415 IEnumerator e = Hierarchy;
416 while (e.MoveNext ()) {
417 PolicyLevel level = (e.Current as PolicyLevel);
422 [SecurityPermission (SecurityAction.Demand, ControlPolicy = true)]
423 public static void SavePolicyLevel (PolicyLevel level)
425 // Yes this will throw a NullReferenceException, just like MS (see FDBK13121)
429 // private/internal stuff
431 private static IEnumerator Hierarchy {
434 if (_hierarchy == null)
435 InitializePolicyHierarchy ();
437 return _hierarchy.GetEnumerator ();
441 private static void InitializePolicyHierarchy ()
443 string machinePolicyPath = Path.GetDirectoryName (Environment.GetMachineConfigPath ());
444 // note: use InternalGetFolderPath to avoid recursive policy initialization
445 string userPolicyPath = Path.Combine (Environment.InternalGetFolderPath (Environment.SpecialFolder.ApplicationData), "mono");
447 PolicyLevel enterprise = new PolicyLevel ("Enterprise", PolicyLevelType.Enterprise);
449 enterprise.LoadFromFile (Path.Combine (machinePolicyPath, "enterprisesec.config"));
451 PolicyLevel machine = new PolicyLevel ("Machine", PolicyLevelType.Machine);
453 machine.LoadFromFile (Path.Combine (machinePolicyPath, "security.config"));
455 PolicyLevel user = new PolicyLevel ("User", PolicyLevelType.User);
457 user.LoadFromFile (Path.Combine (userPolicyPath, "security.config"));
459 ArrayList al = new ArrayList ();
464 _hierarchy = ArrayList.Synchronized (al);
468 internal static bool ResolvePolicyLevel (ref PermissionSet ps, PolicyLevel pl, Evidence evidence)
470 PolicyStatement pst = pl.Resolve (evidence);
473 // only for initial (first) policy level processed
474 ps = pst.PermissionSet;
476 ps = ps.Intersect (pst.PermissionSet);
478 // null is equals to None - exist that null can throw NullReferenceException ;-)
479 ps = new PermissionSet (PermissionState.None);
483 if ((pst.Attributes & PolicyStatementAttribute.LevelFinal) == PolicyStatementAttribute.LevelFinal)
489 internal static void ResolveIdentityPermissions (PermissionSet ps, Evidence evidence)
492 // in 2.0 identity permissions can now be unrestricted
493 if (ps.IsUnrestricted ())
496 // Only host evidence are used for policy resolution
497 IEnumerator ee = evidence.GetHostEnumerator ();
498 while (ee.MoveNext ()) {
499 IIdentityPermissionFactory ipf = (ee.Current as IIdentityPermissionFactory);
501 IPermission p = ipf.CreateIdentityPermission (evidence);
502 ps.AddPermission (p);
507 internal static PolicyLevel ResolvingPolicyLevel {
508 get { return _level; }
509 set { _level = value; }
512 internal static PermissionSet Decode (IntPtr permissions, int length)
514 // Permission sets from the runtime (declarative security) can be cached
515 // for performance as they can never change (i.e. they are read-only).
516 PermissionSet ps = null;
519 if (_declsecCache == null) {
520 _declsecCache = new Hashtable ();
523 object key = (object) (int) permissions;
524 ps = (PermissionSet) _declsecCache [key];
526 // create permissionset and add it to the cache
527 byte[] data = new byte [length];
528 Marshal.Copy (permissions, data, 0, length);
530 ps.DeclarativeSecurity = true;
531 _declsecCache.Add (key, ps);
537 internal static PermissionSet Decode (byte[] encodedPermissions)
539 if ((encodedPermissions == null) || (encodedPermissions.Length < 1))
540 throw new SecurityException ("Invalid metadata format.");
542 switch (encodedPermissions [0]) {
544 // Fx 1.0/1.1 declarative security permissions metadata is in Unicode-encoded XML
545 string xml = Encoding.Unicode.GetString (encodedPermissions);
546 return new PermissionSet (xml);
548 // Fx 2.0 are encoded "somewhat, but not enough, like" custom attributes
549 // note: we still support the older format!
550 return PermissionSet.CreateFromBinaryFormat (encodedPermissions);
552 throw new SecurityException (Locale.GetText ("Unknown metadata format."));
556 private static IPermission UnmanagedCode {
559 if (_unmanagedCode == null)
560 _unmanagedCode = new SecurityPermission (SecurityPermissionFlag.UnmanagedCode);
562 return _unmanagedCode;
566 // security check when using reflection
568 [MethodImplAttribute(MethodImplOptions.InternalCall)]
569 private static unsafe extern bool GetLinkDemandSecurity (MethodBase method, RuntimeDeclSecurityActions *cdecl, RuntimeDeclSecurityActions *mdecl);
571 // When using reflection LinkDemand are promoted to full Demand (i.e. stack walk)
572 internal unsafe static void ReflectedLinkDemandInvoke (MethodBase mb)
574 RuntimeDeclSecurityActions klass;
575 RuntimeDeclSecurityActions method;
577 if (!GetLinkDemandSecurity (mb, &klass, &method))
580 PermissionSet ps = null;
582 if (klass.cas.size > 0) {
583 ps = Decode (klass.cas.blob, klass.cas.size);
585 if (klass.noncas.size > 0) {
586 PermissionSet p = Decode (klass.noncas.blob, klass.noncas.size);
587 ps = (ps == null) ? p : ps.Union (p);
590 if (method.cas.size > 0) {
591 PermissionSet p = Decode (method.cas.blob, method.cas.size);
592 ps = (ps == null) ? p : ps.Union (p);
594 if (method.noncas.size > 0) {
595 PermissionSet p = Decode (method.noncas.blob, method.noncas.size);
596 ps = (ps == null) ? p : ps.Union (p);
599 // in this case we union-ed the permission sets because we want to do
600 // a single stack walk (not up to 4).
605 internal unsafe static bool ReflectedLinkDemandQuery (MethodBase mb)
607 RuntimeDeclSecurityActions klass;
608 RuntimeDeclSecurityActions method;
610 if (!GetLinkDemandSecurity (mb, &klass, &method))
613 return LinkDemand (mb.ReflectedType.Assembly, &klass, &method);
616 private unsafe static bool LinkDemand (Assembly a, RuntimeDeclSecurityActions *klass, RuntimeDeclSecurityActions *method)
619 PermissionSet ps = null;
621 if (klass->cas.size > 0) {
622 ps = Decode (klass->cas.blob, klass->cas.size);
623 result = (SecurityManager.CheckPermissionSet (a, ps, false) == null);
625 if (result && (klass->noncas.size > 0)) {
626 ps = Decode (klass->noncas.blob, klass->noncas.size);
627 result = (SecurityManager.CheckPermissionSet (a, ps, true) == null);
630 if (result && (method->cas.size > 0)) {
631 ps = Decode (method->cas.blob, method->cas.size);
632 result = (SecurityManager.CheckPermissionSet (a, ps, false) == null);
634 if (result && (method->noncas.size > 0)) {
635 ps = Decode (method->noncas.blob, method->noncas.size);
636 result = (SecurityManager.CheckPermissionSet (a, ps, true) == null);
640 catch (SecurityException) {
645 #pragma warning disable 169
646 private static bool LinkDemandFullTrust (Assembly a)
648 // FullTrust is immutable (and means Unrestricted)
649 // so we can skip the subset operations and jump to IsUnrestricted.
650 PermissionSet granted = a.GrantedPermissionSet;
651 if ((granted != null) && !granted.IsUnrestricted ())
654 PermissionSet denied = a.DeniedPermissionSet;
655 if ((denied != null) && !denied.IsEmpty ())
661 private static bool LinkDemandUnmanaged (Assembly a)
663 // note: we know that UnmanagedCode (SecurityPermission) implements IUnrestrictedPermission
664 return IsGranted (a, UnmanagedCode);
667 // we try to provide as much details as possible to help debugging
668 private static void LinkDemandSecurityException (int securityViolation, IntPtr methodHandle)
670 RuntimeMethodHandle runtimeHandle = new RuntimeMethodHandle (methodHandle);
671 MethodInfo method = (MethodInfo)(MethodBase.GetMethodFromHandle (runtimeHandle));
672 Assembly a = method.DeclaringType.Assembly;
674 string message = null;
675 AssemblyName an = null;
676 PermissionSet granted = null;
677 PermissionSet refused = null;
678 object demanded = null;
679 IPermission failed = null;
682 an = a.UnprotectedGetName ();
683 granted = a.GrantedPermissionSet;
684 refused = a.DeniedPermissionSet;
687 switch (securityViolation) {
688 case 1: // MONO_JIT_LINKDEMAND_PERMISSION
689 message = Locale.GetText ("Permissions refused to call this method.");
691 case 2: // MONO_JIT_LINKDEMAND_APTC
692 message = Locale.GetText ("Partially trusted callers aren't allowed to call into this assembly.");
693 demanded = (object) DefaultPolicies.FullTrust; // immutable
695 case 4: // MONO_JIT_LINKDEMAND_ECMA
696 message = Locale.GetText ("Calling internal calls is restricted to ECMA signed assemblies.");
698 case 8: // MONO_JIT_LINKDEMAND_PINVOKE
699 message = Locale.GetText ("Calling unmanaged code isn't allowed from this assembly.");
700 demanded = (object) _unmanagedCode;
701 failed = _unmanagedCode;
704 message = Locale.GetText ("JIT time LinkDemand failed.");
708 throw new SecurityException (message, an, granted, refused, method, SecurityAction.LinkDemand, demanded, failed, null);
711 private static void InheritanceDemandSecurityException (int securityViolation, Assembly a, Type t, MethodInfo method)
713 string message = null;
714 AssemblyName an = null;
715 PermissionSet granted = null;
716 PermissionSet refused = null;
719 an = a.UnprotectedGetName ();
720 granted = a.GrantedPermissionSet;
721 refused = a.DeniedPermissionSet;
724 switch (securityViolation) {
725 case 1: // MONO_METADATA_INHERITANCEDEMAND_CLASS
726 message = String.Format (Locale.GetText ("Class inheritance refused for {0}."), t);
728 case 2: // MONO_METADATA_INHERITANCEDEMAND_CLASS
729 message = Locale.GetText ("Method override refused.");
732 message = Locale.GetText ("Load time InheritDemand failed.");
736 throw new SecurityException (message, an, granted, refused, method, SecurityAction.InheritanceDemand, null, null, null);
739 // called by the runtime when CoreCLR is enabled
741 private static void ThrowException (Exception ex)
746 // internal - get called by the class loader
749 // - class inheritance
750 // - method overrides
751 private unsafe static bool InheritanceDemand (AppDomain ad, Assembly a, RuntimeDeclSecurityActions *actions)
754 PermissionSet ps = null;
756 if (actions->cas.size > 0) {
757 ps = Decode (actions->cas.blob, actions->cas.size);
758 result = (SecurityManager.CheckPermissionSet (a, ps, false) == null);
760 // also check appdomain
761 result = (SecurityManager.CheckPermissionSet (ad, ps) == null);
764 if (actions->noncas.size > 0) {
765 ps = Decode (actions->noncas.blob, actions->noncas.size);
766 result = (SecurityManager.CheckPermissionSet (a, ps, true) == null);
768 // also check appdomain
769 result = (SecurityManager.CheckPermissionSet (ad, ps) == null);
774 catch (SecurityException) {
779 // internal - get called at JIT time
781 private static void DemandUnmanaged ()
783 UnmanagedCode.Demand ();
786 // internal - get called by JIT generated code
788 private static void InternalDemand (IntPtr permissions, int length)
790 PermissionSet ps = Decode (permissions, length);
794 private static void InternalDemandChoice (IntPtr permissions, int length)
796 throw new SecurityException ("SecurityAction.DemandChoice was removed from 2.0");
798 #pragma warning restore 169