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 PermissionSet _fullTrust; // for [AllowPartiallyTrustedCallers]
66 private static IPermission _unmanagedCode;
67 private static Hashtable _declsecCache;
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, Flags=SecurityPermissionFlag.ControlPolicy)]
87 extern public static bool SecurityEnabled {
88 [MethodImplAttribute (MethodImplOptions.InternalCall)]
91 [MethodImplAttribute (MethodImplOptions.InternalCall)]
92 [SecurityPermission (SecurityAction.Demand, Flags=SecurityPermissionFlag.ControlPolicy)]
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.
103 [StrongNameIdentityPermission (SecurityAction.LinkDemand, PublicKey = "0x00000000000000000400000000000000")]
104 public static void GetZoneAndOrigin (out ArrayList zone, out ArrayList origin)
106 zone = new ArrayList ();
107 origin = new ArrayList ();
111 public static bool IsGranted (IPermission perm)
115 if (!SecurityEnabled)
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 return IsGranted (Assembly.GetCallingAssembly (), perm);
126 internal static bool IsGranted (Assembly a, IPermission perm)
128 CodeAccessPermission grant = null;
130 if (a.GrantedPermissionSet != null) {
131 grant = (CodeAccessPermission) a.GrantedPermissionSet.GetPermission (perm.GetType ());
133 if (!a.GrantedPermissionSet.IsUnrestricted () || !(perm is IUnrestrictedPermission)) {
134 return perm.IsSubsetOf (null);
136 } else if (!perm.IsSubsetOf (grant)) {
141 if (a.DeniedPermissionSet != null) {
142 CodeAccessPermission refuse = (CodeAccessPermission) a.DeniedPermissionSet.GetPermission (perm.GetType ());
143 if ((refuse != null) && perm.IsSubsetOf (refuse))
149 internal static bool IsGranted (Assembly a, PermissionSet ps, bool noncas)
154 foreach (IPermission p in ps) {
155 // note: this may contains non CAS permissions
156 if ((!noncas) && (p is CodeAccessPermission)) {
157 if (!SecurityManager.IsGranted (a, p))
160 // but non-CAS will throw on failure...
164 catch (SecurityException) {
173 internal static bool IsGranted (AppDomain ad, PermissionSet ps)
175 if ((ps == null) || ps.IsEmpty ())
178 PermissionSet granted = ad.GrantedPermissionSet;
182 foreach (IPermission p in ps) {
183 if (p is CodeAccessPermission) {
184 CodeAccessPermission grant = (CodeAccessPermission) granted.GetPermission (p.GetType ());
186 if (!granted.IsUnrestricted () || !(p is IUnrestrictedPermission)) {
187 return p.IsSubsetOf (null);
189 } else if (!p.IsSubsetOf (grant)) {
193 // but non-CAS will throw on failure...
197 catch (SecurityException) {
206 [SecurityPermission (SecurityAction.Demand, Flags=SecurityPermissionFlag.ControlPolicy)]
207 public static PolicyLevel LoadPolicyLevelFromFile (string path, PolicyLevelType type)
210 throw new ArgumentNullException ("path");
212 PolicyLevel pl = null;
214 pl = new PolicyLevel (type.ToString (), type);
215 pl.LoadFromFile (path);
217 catch (Exception e) {
218 throw new ArgumentException (Locale.GetText ("Invalid policy XML"), e);
223 [SecurityPermission (SecurityAction.Demand, Flags=SecurityPermissionFlag.ControlPolicy)]
224 public static PolicyLevel LoadPolicyLevelFromString (string str, PolicyLevelType type)
227 throw new ArgumentNullException ("str");
229 PolicyLevel pl = null;
231 pl = new PolicyLevel (type.ToString (), type);
232 pl.LoadFromString (str);
234 catch (Exception e) {
235 throw new ArgumentException (Locale.GetText ("Invalid policy XML"), e);
240 [SecurityPermission (SecurityAction.Demand, Flags=SecurityPermissionFlag.ControlPolicy)]
241 public static IEnumerator PolicyHierarchy ()
246 public static PermissionSet ResolvePolicy (Evidence evidence)
248 // no evidence, no permission
249 if (evidence == null)
250 return new PermissionSet (PermissionState.None);
252 PermissionSet ps = null;
253 // Note: can't call PolicyHierarchy since ControlPolicy isn't required to resolve policies
254 IEnumerator ple = Hierarchy;
255 while (ple.MoveNext ()) {
256 PolicyLevel pl = (PolicyLevel) ple.Current;
257 if (ResolvePolicyLevel (ref ps, pl, evidence)) {
258 break; // i.e. PolicyStatementAttribute.LevelFinal
262 ResolveIdentityPermissions (ps, evidence);
267 [MonoTODO ("more tests are needed")]
268 public static PermissionSet ResolvePolicy (Evidence[] evidences)
270 if ((evidences == null) || (evidences.Length == 0) ||
271 ((evidences.Length == 1) && (evidences [0].Count == 0))) {
272 return new PermissionSet (PermissionState.None);
275 // probably not optimal
276 PermissionSet ps = ResolvePolicy (evidences [0]);
277 for (int i=1; i < evidences.Length; i++) {
278 ps = ps.Intersect (ResolvePolicy (evidences [i]));
283 public static PermissionSet ResolveSystemPolicy (Evidence evidence)
285 // no evidence, no permission
286 if (evidence == null)
287 return new PermissionSet (PermissionState.None);
289 // Note: can't call PolicyHierarchy since ControlPolicy isn't required to resolve policies
290 PermissionSet ps = null;
291 IEnumerator ple = Hierarchy;
292 while (ple.MoveNext ()) {
293 PolicyLevel pl = (PolicyLevel) ple.Current;
294 if (pl.Type == PolicyLevelType.AppDomain)
296 if (ResolvePolicyLevel (ref ps, pl, evidence))
297 break; // i.e. PolicyStatementAttribute.LevelFinal
300 ResolveIdentityPermissions (ps, evidence);
305 static private SecurityPermission _execution = new SecurityPermission (SecurityPermissionFlag.Execution);
308 public static PermissionSet ResolvePolicy (Evidence evidence, PermissionSet reqdPset, PermissionSet optPset, PermissionSet denyPset, out PermissionSet denied)
310 PermissionSet resolved = ResolvePolicy (evidence);
311 // do we have the minimal permission requested by the assembly ?
312 if ((reqdPset != null) && !reqdPset.IsSubsetOf (resolved)) {
313 throw new PolicyException (Locale.GetText (
314 "Policy doesn't grant the minimal permissions required to execute the assembly."));
316 // do we have the right to execute ?
317 if (CheckExecutionRights) {
318 // unless we have "Full Trust"...
319 if (!resolved.IsUnrestricted ()) {
320 // ... we need to find a SecurityPermission
321 IPermission security = resolved.GetPermission (typeof (SecurityPermission));
322 if (!_execution.IsSubsetOf (security)) {
323 throw new PolicyException (Locale.GetText (
324 "Policy doesn't grant the right to execute to the assembly."));
333 public static IEnumerator ResolvePolicyGroups (Evidence evidence)
335 if (evidence == null)
336 throw new ArgumentNullException ("evidence");
338 ArrayList al = new ArrayList ();
339 // Note: can't call PolicyHierarchy since ControlPolicy isn't required to resolve policies
340 IEnumerator ple = Hierarchy;
341 while (ple.MoveNext ()) {
342 PolicyLevel pl = (PolicyLevel) ple.Current;
343 CodeGroup cg = pl.ResolveMatchingCodeGroups (evidence);
346 return al.GetEnumerator ();
349 [SecurityPermission (SecurityAction.Demand, Flags=SecurityPermissionFlag.ControlPolicy)]
350 public static void SavePolicy ()
352 IEnumerator e = Hierarchy;
353 while (e.MoveNext ()) {
354 PolicyLevel level = (e.Current as PolicyLevel);
359 [SecurityPermission (SecurityAction.Demand, Flags=SecurityPermissionFlag.ControlPolicy)]
360 public static void SavePolicyLevel (PolicyLevel level)
362 // Yes this will throw a NullReferenceException, just like MS (see FDBK13121)
366 // private/internal stuff
368 private static IEnumerator Hierarchy {
370 // double-lock pattern
371 if (_hierarchy == null) {
373 if (_hierarchy == null)
374 InitializePolicyHierarchy ();
377 return _hierarchy.GetEnumerator ();
381 private static void InitializePolicyHierarchy ()
383 string machinePolicyPath = Path.GetDirectoryName (Environment.GetMachineConfigPath ());
384 // note: use InternalGetFolderPath to avoid recursive policy initialization
385 string userPolicyPath = Path.Combine (Environment.InternalGetFolderPath (Environment.SpecialFolder.ApplicationData), "mono");
387 ArrayList al = new ArrayList ();
388 al.Add (new PolicyLevel ("Enterprise", PolicyLevelType.Enterprise,
389 Path.Combine (machinePolicyPath, "enterprisesec.config")));
391 al.Add (new PolicyLevel ("Machine", PolicyLevelType.Machine,
392 Path.Combine (machinePolicyPath, "security.config")));
394 al.Add (new PolicyLevel ("User", PolicyLevelType.User,
395 Path.Combine (userPolicyPath, "security.config")));
397 _hierarchy = ArrayList.Synchronized (al);
400 internal static bool ResolvePolicyLevel (ref PermissionSet ps, PolicyLevel pl, Evidence evidence)
402 PolicyStatement pst = pl.Resolve (evidence);
405 // only for initial (first) policy level processed
406 ps = pst.PermissionSet;
408 ps = ps.Intersect (pst.PermissionSet);
410 // null is equals to None - exist that null can throw NullReferenceException ;-)
411 ps = new PermissionSet (PermissionState.None);
415 if ((pst.Attributes & PolicyStatementAttribute.LevelFinal) == PolicyStatementAttribute.LevelFinal)
421 // TODO: this changes in 2.0 as identity permissions can now be unrestricted
422 internal static void ResolveIdentityPermissions (PermissionSet ps, Evidence evidence)
424 // Only host evidence are used for policy resolution
425 IEnumerator ee = evidence.GetHostEnumerator ();
426 while (ee.MoveNext ()) {
427 IIdentityPermissionFactory ipf = (ee.Current as IIdentityPermissionFactory);
429 IPermission p = ipf.CreateIdentityPermission (evidence);
430 ps.AddPermission (p);
435 internal static PermissionSet Decode (IntPtr permissions, int length)
437 // Permission sets from the runtime (declarative security) can be cached
438 // for performance as they can never change (i.e. they are read-only).
440 if (_declsecCache == null) {
442 if (_declsecCache == null) {
443 _declsecCache = new Hashtable ();
448 PermissionSet ps = null;
450 object key = (object) (int) permissions;
451 ps = (PermissionSet) _declsecCache [key];
453 // create permissionset and add it to the cache
454 byte[] data = new byte [length];
455 Marshal.Copy (permissions, data, 0, length);
457 ps.DeclarativeSecurity = true;
458 _declsecCache.Add (key, ps);
464 internal static PermissionSet Decode (byte[] encodedPermissions)
466 if ((encodedPermissions == null) || (encodedPermissions.Length < 1))
467 throw new SecurityException ("Invalid metadata format.");
469 switch (encodedPermissions [0]) {
471 // Fx 1.0/1.1 declarative security permissions metadata is in Unicode-encoded XML
472 string xml = Encoding.Unicode.GetString (encodedPermissions);
473 return new PermissionSet (xml);
475 // Fx 2.0 are encoded "somewhat, but not enough, like" custom attributes
476 // note: we still support the older format!
477 return PermissionSet.CreateFromBinaryFormat (encodedPermissions);
479 throw new SecurityException (Locale.GetText ("Unknown metadata format."));
483 internal static PermissionSetCollection DecodeCollection (IntPtr permissions, int length)
485 // Permission sets from the runtime (declarative security) can be cached
486 // for performance as they can never change (i.e. they are read-only).
488 if (_declsecCache == null) {
490 if (_declsecCache == null) {
491 _declsecCache = new Hashtable ();
496 PermissionSetCollection psc = null;
498 object key = (object) (int) permissions;
499 psc = (PermissionSetCollection) _declsecCache [key];
501 // create permissionset and add it to the cache
502 byte[] data = new byte [length];
503 Marshal.Copy (permissions, data, 0, length);
504 psc = DecodeCollection (data);
505 _declsecCache.Add (key, psc);
511 internal static PermissionSetCollection DecodeCollection (byte[] encodedPermissions)
513 if ((encodedPermissions == null) || (encodedPermissions.Length < 1))
514 throw new SecurityException ("Invalid metadata format.");
516 switch (encodedPermissions [0]) {
518 // Fx 1.0/1.1 declarative security permissions metadata is in Unicode-encoded XML
519 throw new SecurityException (Locale.GetText ("1.0 metadata format doesn't support collections."));
521 // Fx 2.0 are encoded "somewhat, but not enough, like" custom attributes
522 // note: we still support the older format!
523 return PermissionSetCollection.CreateFromBinaryFormat (encodedPermissions);
525 throw new SecurityException (Locale.GetText ("Unknown metadata format."));
530 private static IPermission UnmanagedCode {
532 // double-lock pattern
533 if (_unmanagedCode == null) {
535 if (_unmanagedCode == null)
536 _unmanagedCode = new SecurityPermission (SecurityPermissionFlag.UnmanagedCode);
539 return _unmanagedCode;
543 // security check when using reflection
545 [MethodImplAttribute(MethodImplOptions.InternalCall)]
546 private static unsafe extern bool GetLinkDemandSecurity (MethodBase method, RuntimeDeclSecurityActions *cdecl, RuntimeDeclSecurityActions *mdecl);
548 // When using reflection LinkDemand are promoted to full Demand (i.e. stack walk)
549 internal unsafe static void ReflectedLinkDemandInvoke (MethodBase mb)
551 RuntimeDeclSecurityActions klass;
552 RuntimeDeclSecurityActions method;
554 if (!GetLinkDemandSecurity (mb, &klass, &method))
557 PermissionSet ps = null;
559 if (klass.cas.size > 0) {
560 ps = Decode (klass.cas.blob, klass.cas.size);
562 if (klass.noncas.size > 0) {
563 PermissionSet p = Decode (klass.noncas.blob, klass.noncas.size);
564 ps = (ps == null) ? p : ps.Union (p);
567 if (method.cas.size > 0) {
568 PermissionSet p = Decode (method.cas.blob, method.cas.size);
569 ps = (ps == null) ? p : ps.Union (p);
571 if (method.noncas.size > 0) {
572 PermissionSet p = Decode (method.noncas.blob, method.noncas.size);
573 ps = (ps == null) ? p : ps.Union (p);
576 // in this case we union-ed the permission sets because we want to do
577 // a single stack walk (not up to 4).
581 // Process LinkDemandChoice (2.0)
582 if (klass.choice.size > 0) {
583 PermissionSetCollection psc = DecodeCollection (klass.choice.blob, klass.choice.size);
586 if (method.choice.size > 0) {
587 PermissionSetCollection psc = DecodeCollection (method.choice.blob, method.choice.size);
593 internal unsafe static bool ReflectedLinkDemandQuery (MethodBase mb)
595 RuntimeDeclSecurityActions klass;
596 RuntimeDeclSecurityActions method;
598 if (!GetLinkDemandSecurity (mb, &klass, &method))
601 return LinkDemand (mb.ReflectedType.Assembly, &klass, &method);
604 // internal - get called at JIT time
606 private static void DemandUnmanaged ()
608 UnmanagedCode.Demand ();
611 private unsafe static bool LinkDemand (Assembly a, RuntimeDeclSecurityActions *klass, RuntimeDeclSecurityActions *method)
614 PermissionSet ps = null;
616 if (klass->cas.size > 0) {
617 ps = Decode (klass->cas.blob, klass->cas.size);
618 result = SecurityManager.IsGranted (a, ps, false);
620 if (result && (klass->noncas.size > 0)) {
621 ps = Decode (klass->noncas.blob, klass->noncas.size);
622 result = SecurityManager.IsGranted (a, ps, true);
625 if (result && (method->cas.size > 0)) {
626 ps = Decode (method->cas.blob, method->cas.size);
627 result = SecurityManager.IsGranted (a, ps, false);
629 if (result && (method->noncas.size > 0)) {
630 ps = Decode (method->noncas.blob, method->noncas.size);
631 result = SecurityManager.IsGranted (a, ps, true);
634 // success if one of the permission is granted
635 if (result && (klass->choice.size > 0)) {
636 PermissionSetCollection psc = DecodeCollection (klass->choice.blob, klass->choice.size);
639 foreach (PermissionSet pset in psc) {
640 if (SecurityManager.IsGranted (a, pset, false)) {
647 if (result && (method->choice.size > 0)) {
648 PermissionSetCollection psc = DecodeCollection (method->choice.blob, method->choice.size);
651 foreach (PermissionSet pset in psc) {
652 if (SecurityManager.IsGranted (a, pset, false)) {
662 catch (SecurityException) {
667 private static bool LinkDemandFullTrust (Assembly a)
669 // double-lock pattern
670 if (_fullTrust == null) {
672 if (_fullTrust == null)
673 _fullTrust = new NamedPermissionSet ("FullTrust");
678 return SecurityManager.IsGranted (a, _fullTrust, false);
680 catch (SecurityException) {
685 private static bool LinkDemandUnmanaged (Assembly a)
687 return IsGranted (a, UnmanagedCode);
690 // we try to provide as much details as possible to help debugging
691 private static void LinkDemandSecurityException (int securityViolation, Assembly a, MethodInfo method)
693 string message = null;
694 AssemblyName an = null;
695 PermissionSet granted = null;
696 PermissionSet refused = null;
697 object demanded = null;
698 IPermission failed = null;
701 an = a.UnprotectedGetName ();
702 granted = a.GrantedPermissionSet;
703 refused = a.DeniedPermissionSet;
706 switch (securityViolation) {
707 case 1: // MONO_JIT_LINKDEMAND_PERMISSION
708 message = Locale.GetText ("Permissions refused to call this method.");
710 case 2: // MONO_JIT_LINKDEMAND_APTC
711 message = Locale.GetText ("Partially trusted callers aren't allowed to call into this assembly.");
712 demanded = (object) _fullTrust;
714 case 4: // MONO_JIT_LINKDEMAND_ECMA
715 message = Locale.GetText ("Calling internal calls is restricted to ECMA signed assemblies.");
717 case 8: // MONO_JIT_LINKDEMAND_PINVOKE
718 message = Locale.GetText ("Calling unmanaged code isn't allowed from this assembly.");
719 demanded = (object) _unmanagedCode;
720 failed = _unmanagedCode;
723 message = Locale.GetText ("JIT time LinkDemand failed.");
727 throw new SecurityException (message, an, granted, refused, method, SecurityAction.LinkDemand, demanded, failed, null);
730 private static void InheritanceDemandSecurityException (int securityViolation, Assembly a, Type t, MethodInfo method)
732 string message = null;
733 AssemblyName an = null;
734 PermissionSet granted = null;
735 PermissionSet refused = null;
738 an = a.UnprotectedGetName ();
739 granted = a.GrantedPermissionSet;
740 refused = a.DeniedPermissionSet;
743 switch (securityViolation) {
744 case 1: // MONO_METADATA_INHERITANCEDEMAND_CLASS
745 message = String.Format (Locale.GetText ("Class inheritance refused for {0}."), t);
747 case 2: // MONO_METADATA_INHERITANCEDEMAND_CLASS
748 message = Locale.GetText ("Method override refused.");
751 message = Locale.GetText ("Load time InheritDemand failed.");
755 throw new SecurityException (message, an, granted, refused, method, SecurityAction.LinkDemand, null, null, null);
758 // internal - get called by the class loader
761 // - class inheritance
762 // - method overrides
763 private unsafe static bool InheritanceDemand (Assembly a, RuntimeDeclSecurityActions *actions)
766 PermissionSet ps = null;
768 if (actions->cas.size > 0) {
769 ps = Decode (actions->cas.blob, actions->cas.size);
770 result = SecurityManager.IsGranted (a, ps, false);
772 if (actions->noncas.size > 0) {
773 ps = Decode (actions->noncas.blob, actions->noncas.size);
774 result = SecurityManager.IsGranted (a, ps, true);
777 // success if one of the permission is granted
778 if (result && (actions->choice.size > 0)) {
779 PermissionSetCollection psc = DecodeCollection (actions->choice.blob, actions->choice.size);
782 foreach (PermissionSet pset in psc) {
783 if (SecurityManager.IsGranted (a, pset, false)) {
793 catch (SecurityException) {
799 // internal - get called by JIT generated code
801 private static void InternalDemand (IntPtr permissions, int length)
803 PermissionSet ps = Decode (permissions, length);
807 private static void InternalDemandChoice (IntPtr permissions, int length)
810 PermissionSetCollection psc = DecodeCollection (permissions, length);
813 throw new SecurityException ("SecurityAction.DemandChoice is only possible in 2.0");