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;
53 public sealed class SecurityManager {
55 private static object _lockObject;
56 private static ArrayList _hierarchy;
57 private static PermissionSet _fullTrust; // for [AllowPartiallyTrustedCallers]
58 private static IPermission _unmanagedCode;
59 private static Hashtable _declsecCache;
61 static SecurityManager ()
64 // http://msdn.microsoft.com/library/en-us/dnaskdr/html/askgui06032003.asp?frame=true
65 _lockObject = new object ();
68 private SecurityManager ()
74 extern public static bool CheckExecutionRights {
75 [MethodImplAttribute (MethodImplOptions.InternalCall)]
78 [MethodImplAttribute (MethodImplOptions.InternalCall)]
79 [SecurityPermission (SecurityAction.Demand, Flags=SecurityPermissionFlag.ControlPolicy)]
83 extern public static bool SecurityEnabled {
84 [MethodImplAttribute (MethodImplOptions.InternalCall)]
87 [MethodImplAttribute (MethodImplOptions.InternalCall)]
88 [SecurityPermission (SecurityAction.Demand, Flags=SecurityPermissionFlag.ControlPolicy)]
96 [StrongNameIdentityPermission (SecurityAction.LinkDemand, PublicKey = "0x00000000000000000400000000000000")]
97 public static void GetZoneAndOrigin (out ArrayList zone, out ArrayList origin)
99 zone = new ArrayList ();
100 origin = new ArrayList ();
104 public static bool IsGranted (IPermission perm)
108 if (!SecurityEnabled)
112 // - Only check the caller (no stack walk required)
113 // - Not affected by overrides (like Assert, Deny and PermitOnly)
114 // - calls IsSubsetOf even for non CAS permissions
115 // (i.e. it does call Demand so any code there won't be executed)
116 return IsGranted (Assembly.GetCallingAssembly (), perm);
119 internal static bool IsGranted (Assembly a, IPermission perm)
121 CodeAccessPermission grant = null;
123 if (a.GrantedPermissionSet != null) {
124 grant = (CodeAccessPermission) a.GrantedPermissionSet.GetPermission (perm.GetType ());
126 if (!a.GrantedPermissionSet.IsUnrestricted () || !(perm is IUnrestrictedPermission)) {
129 } else if (!perm.IsSubsetOf (grant)) {
134 if (a.DeniedPermissionSet != null) {
135 CodeAccessPermission refuse = (CodeAccessPermission) a.DeniedPermissionSet.GetPermission (perm.GetType ());
136 if ((refuse != null) && perm.IsSubsetOf (refuse))
142 internal static bool IsGranted (Assembly a, PermissionSet ps, bool noncas)
147 foreach (IPermission p in ps) {
148 // note: this may contains non CAS permissions
149 if ((!noncas) && (p is CodeAccessPermission)) {
150 if (!SecurityManager.IsGranted (a, p))
153 // but non-CAS will throw on failure...
157 catch (SecurityException) {
166 [SecurityPermission (SecurityAction.Demand, Flags=SecurityPermissionFlag.ControlPolicy)]
167 public static PolicyLevel LoadPolicyLevelFromFile (string path, PolicyLevelType type)
170 throw new ArgumentNullException ("path");
172 PolicyLevel pl = null;
174 pl = new PolicyLevel (type.ToString (), type);
175 pl.LoadFromFile (path);
177 catch (Exception e) {
178 throw new ArgumentException (Locale.GetText ("Invalid policy XML"), e);
183 [SecurityPermission (SecurityAction.Demand, Flags=SecurityPermissionFlag.ControlPolicy)]
184 public static PolicyLevel LoadPolicyLevelFromString (string str, PolicyLevelType type)
187 throw new ArgumentNullException ("str");
189 PolicyLevel pl = null;
191 pl = new PolicyLevel (type.ToString (), type);
192 pl.LoadFromString (str);
194 catch (Exception e) {
195 throw new ArgumentException (Locale.GetText ("Invalid policy XML"), e);
200 [SecurityPermission (SecurityAction.Demand, Flags=SecurityPermissionFlag.ControlPolicy)]
201 public static IEnumerator PolicyHierarchy ()
206 public static PermissionSet ResolvePolicy (Evidence evidence)
208 // no evidence, no permission
209 if (evidence == null)
210 return new PermissionSet (PermissionState.None);
212 PermissionSet ps = null;
213 // Note: can't call PolicyHierarchy since ControlPolicy isn't required to resolve policies
214 IEnumerator ple = Hierarchy;
215 while (ple.MoveNext ()) {
216 PolicyLevel pl = (PolicyLevel) ple.Current;
217 if (ResolvePolicyLevel (ref ps, pl, evidence)) {
218 break; // i.e. PolicyStatementAttribute.LevelFinal
222 ResolveIdentityPermissions (ps, evidence);
227 [MonoTODO ("more tests are needed")]
228 public static PermissionSet ResolvePolicy (Evidence[] evidences)
230 if ((evidences == null) || (evidences.Length == 0) ||
231 ((evidences.Length == 1) && (evidences [0].Count == 0))) {
232 return new PermissionSet (PermissionState.None);
235 // probably not optimal
236 PermissionSet ps = ResolvePolicy (evidences [0]);
237 for (int i=1; i < evidences.Length; i++) {
238 ps = ps.Intersect (ResolvePolicy (evidences [i]));
243 public static PermissionSet ResolveSystemPolicy (Evidence evidence)
245 // no evidence, no permission
246 if (evidence == null)
247 return new PermissionSet (PermissionState.None);
249 // Note: can't call PolicyHierarchy since ControlPolicy isn't required to resolve policies
250 PermissionSet ps = null;
251 IEnumerator ple = Hierarchy;
252 while (ple.MoveNext ()) {
253 PolicyLevel pl = (PolicyLevel) ple.Current;
254 if (pl.Type == PolicyLevelType.AppDomain)
256 if (ResolvePolicyLevel (ref ps, pl, evidence))
257 break; // i.e. PolicyStatementAttribute.LevelFinal
260 ResolveIdentityPermissions (ps, evidence);
265 static private SecurityPermission _execution = new SecurityPermission (SecurityPermissionFlag.Execution);
268 public static PermissionSet ResolvePolicy (Evidence evidence, PermissionSet reqdPset, PermissionSet optPset, PermissionSet denyPset, out PermissionSet denied)
270 PermissionSet resolved = ResolvePolicy (evidence);
271 // do we have the minimal permission requested by the assembly ?
272 if ((reqdPset != null) && !reqdPset.IsSubsetOf (resolved)) {
273 throw new PolicyException (Locale.GetText (
274 "Policy doesn't grant the minimal permissions required to execute the assembly."));
276 // do we have the right to execute ?
277 if (CheckExecutionRights) {
278 // unless we have "Full Trust"...
279 if (!resolved.IsUnrestricted ()) {
280 // ... we need to find a SecurityPermission
281 IPermission security = resolved.GetPermission (typeof (SecurityPermission));
282 if (!_execution.IsSubsetOf (security)) {
283 throw new PolicyException (Locale.GetText (
284 "Policy doesn't grant the right to execute to the assembly."));
293 public static IEnumerator ResolvePolicyGroups (Evidence evidence)
295 if (evidence == null)
296 throw new ArgumentNullException ("evidence");
298 ArrayList al = new ArrayList ();
299 // Note: can't call PolicyHierarchy since ControlPolicy isn't required to resolve policies
300 IEnumerator ple = Hierarchy;
301 while (ple.MoveNext ()) {
302 PolicyLevel pl = (PolicyLevel) ple.Current;
303 CodeGroup cg = pl.ResolveMatchingCodeGroups (evidence);
306 return al.GetEnumerator ();
309 [SecurityPermission (SecurityAction.Demand, Flags=SecurityPermissionFlag.ControlPolicy)]
310 public static void SavePolicy ()
312 IEnumerator e = Hierarchy;
313 while (e.MoveNext ()) {
314 PolicyLevel level = (e.Current as PolicyLevel);
319 [SecurityPermission (SecurityAction.Demand, Flags=SecurityPermissionFlag.ControlPolicy)]
320 public static void SavePolicyLevel (PolicyLevel level)
322 // Yes this will throw a NullReferenceException, just like MS (see FDBK13121)
326 // private/internal stuff
328 private static IEnumerator Hierarchy {
330 // double-lock pattern
331 if (_hierarchy == null) {
333 if (_hierarchy == null)
334 InitializePolicyHierarchy ();
337 return _hierarchy.GetEnumerator ();
341 private static void InitializePolicyHierarchy ()
343 string machinePolicyPath = Path.GetDirectoryName (Environment.GetMachineConfigPath ());
344 // note: use InternalGetFolderPath to avoid recursive policy initialization
345 string userPolicyPath = Path.Combine (Environment.InternalGetFolderPath (Environment.SpecialFolder.ApplicationData), "mono");
347 ArrayList al = new ArrayList ();
348 al.Add (new PolicyLevel ("Enterprise", PolicyLevelType.Enterprise,
349 Path.Combine (machinePolicyPath, "enterprisesec.config")));
351 al.Add (new PolicyLevel ("Machine", PolicyLevelType.Machine,
352 Path.Combine (machinePolicyPath, "security.config")));
354 al.Add (new PolicyLevel ("User", PolicyLevelType.User,
355 Path.Combine (userPolicyPath, "security.config")));
357 _hierarchy = ArrayList.Synchronized (al);
360 internal static bool ResolvePolicyLevel (ref PermissionSet ps, PolicyLevel pl, Evidence evidence)
362 PolicyStatement pst = pl.Resolve (evidence);
365 // only for initial (first) policy level processed
366 ps = pst.PermissionSet;
368 ps = ps.Intersect (pst.PermissionSet);
370 // null is equals to None - exist that null can throw NullReferenceException ;-)
371 ps = new PermissionSet (PermissionState.None);
375 if ((pst.Attributes & PolicyStatementAttribute.LevelFinal) == PolicyStatementAttribute.LevelFinal)
381 // TODO: this changes in 2.0 as identity permissions can now be unrestricted
382 internal static void ResolveIdentityPermissions (PermissionSet ps, Evidence evidence)
384 // Only host evidence are used for policy resolution
385 IEnumerator ee = evidence.GetHostEnumerator ();
386 while (ee.MoveNext ()) {
387 IIdentityPermissionFactory ipf = (ee.Current as IIdentityPermissionFactory);
389 IPermission p = ipf.CreateIdentityPermission (evidence);
390 ps.AddPermission (p);
395 internal static PermissionSet Decode (IntPtr permissions, int length)
397 // Permission sets from the runtime (declarative security) can be cached
398 // for performance as they can never change (i.e. they are read-only).
400 if (_declsecCache == null) {
402 if (_declsecCache == null) {
403 _declsecCache = new Hashtable ();
408 PermissionSet ps = null;
410 object key = (object) (int) permissions;
411 ps = (PermissionSet) _declsecCache [key];
413 // create permissionset and add it to the cache
414 byte[] data = new byte [length];
415 Marshal.Copy (permissions, data, 0, length);
417 ps.DeclarativeSecurity = true;
418 _declsecCache.Add (key, ps);
424 internal static PermissionSet Decode (byte[] encodedPermissions)
426 if ((encodedPermissions == null) || (encodedPermissions.Length < 1))
427 throw new SecurityException ("Invalid metadata format.");
429 switch (encodedPermissions [0]) {
431 // Fx 1.0/1.1 declarative security permissions metadata is in Unicode-encoded XML
432 string xml = Encoding.Unicode.GetString (encodedPermissions);
433 return new PermissionSet (xml);
435 // Fx 2.0 are encoded "somewhat, but not enough, like" custom attributes
436 // note: we still support the older format!
437 return PermissionSet.CreateFromBinaryFormat (encodedPermissions);
439 throw new SecurityException (Locale.GetText ("Unknown metadata format."));
443 internal static PermissionSetCollection DecodeCollection (IntPtr permissions, int length)
445 // Permission sets from the runtime (declarative security) can be cached
446 // for performance as they can never change (i.e. they are read-only).
448 if (_declsecCache == null) {
450 if (_declsecCache == null) {
451 _declsecCache = new Hashtable ();
456 PermissionSetCollection psc = null;
458 object key = (object) (int) permissions;
459 psc = (PermissionSetCollection) _declsecCache [key];
461 // create permissionset and add it to the cache
462 byte[] data = new byte [length];
463 Marshal.Copy (permissions, data, 0, length);
464 psc = DecodeCollection (data);
465 _declsecCache.Add (key, psc);
471 internal static PermissionSetCollection DecodeCollection (byte[] encodedPermissions)
473 if ((encodedPermissions == null) || (encodedPermissions.Length < 1))
474 throw new SecurityException ("Invalid metadata format.");
476 switch (encodedPermissions [0]) {
478 // Fx 1.0/1.1 declarative security permissions metadata is in Unicode-encoded XML
479 throw new SecurityException (Locale.GetText ("1.0 metadata format doesn't support collections."));
481 // Fx 2.0 are encoded "somewhat, but not enough, like" custom attributes
482 // note: we still support the older format!
483 return PermissionSetCollection.CreateFromBinaryFormat (encodedPermissions);
485 throw new SecurityException (Locale.GetText ("Unknown metadata format."));
490 private static IPermission UnmanagedCode {
492 // double-lock pattern
493 if (_unmanagedCode == null) {
495 if (_unmanagedCode == null)
496 _unmanagedCode = new SecurityPermission (SecurityPermissionFlag.UnmanagedCode);
499 return _unmanagedCode;
503 // security check when using reflection
505 [MethodImplAttribute(MethodImplOptions.InternalCall)]
506 private static unsafe extern bool GetLinkDemandSecurity (MethodBase method, RuntimeDeclSecurityActions *cdecl, RuntimeDeclSecurityActions *mdecl);
508 // When using reflection LinkDemand are promoted to full Demand (i.e. stack walk)
509 internal unsafe static void ReflectedLinkDemandInvoke (MethodBase mb)
511 RuntimeDeclSecurityActions klass;
512 RuntimeDeclSecurityActions method;
514 if (!GetLinkDemandSecurity (mb, &klass, &method))
517 PermissionSet ps = null;
519 if (klass.cas.size > 0) {
520 ps = Decode (klass.cas.blob, klass.cas.size);
522 if (klass.noncas.size > 0) {
523 PermissionSet p = Decode (klass.noncas.blob, klass.noncas.size);
524 ps = (ps == null) ? p : ps.Union (p);
527 if (method.cas.size > 0) {
528 PermissionSet p = Decode (method.cas.blob, method.cas.size);
529 ps = (ps == null) ? p : ps.Union (p);
531 if (method.noncas.size > 0) {
532 PermissionSet p = Decode (method.noncas.blob, method.noncas.size);
533 ps = (ps == null) ? p : ps.Union (p);
536 // in this case we union-ed the permission sets because we want to do
537 // a single stack walk (not up to 4).
541 // Process LinkDemandChoice (2.0)
542 if (klass.choice.size > 0) {
543 PermissionSetCollection psc = DecodeCollection (klass.choice.blob, klass.choice.size);
546 if (method.choice.size > 0) {
547 PermissionSetCollection psc = DecodeCollection (method.choice.blob, method.choice.size);
553 internal unsafe static bool ReflectedLinkDemandQuery (MethodBase mb)
555 RuntimeDeclSecurityActions klass;
556 RuntimeDeclSecurityActions method;
558 if (!GetLinkDemandSecurity (mb, &klass, &method))
561 return LinkDemand (mb.ReflectedType.Assembly, &klass, &method);
564 // internal - get called at JIT time
566 private static void DemandUnmanaged ()
568 UnmanagedCode.Demand ();
571 private unsafe static bool LinkDemand (Assembly a, RuntimeDeclSecurityActions *klass, RuntimeDeclSecurityActions *method)
574 PermissionSet ps = null;
576 if (klass->cas.size > 0) {
577 ps = Decode (klass->cas.blob, klass->cas.size);
578 result = SecurityManager.IsGranted (a, ps, false);
580 if (result && (klass->noncas.size > 0)) {
581 ps = Decode (klass->noncas.blob, klass->noncas.size);
582 result = SecurityManager.IsGranted (a, ps, true);
585 if (result && (method->cas.size > 0)) {
586 ps = Decode (method->cas.blob, method->cas.size);
587 result = SecurityManager.IsGranted (a, ps, false);
589 if (result && (method->noncas.size > 0)) {
590 ps = Decode (method->noncas.blob, method->noncas.size);
591 result = SecurityManager.IsGranted (a, ps, true);
594 // success if one of the permission is granted
595 if (result && (klass->choice.size > 0)) {
596 PermissionSetCollection psc = DecodeCollection (klass->choice.blob, klass->choice.size);
599 foreach (PermissionSet pset in psc) {
600 if (SecurityManager.IsGranted (a, pset, false)) {
607 if (result && (method->choice.size > 0)) {
608 PermissionSetCollection psc = DecodeCollection (method->choice.blob, method->choice.size);
611 foreach (PermissionSet pset in psc) {
612 if (SecurityManager.IsGranted (a, pset, false)) {
622 catch (SecurityException) {
627 private static bool LinkDemandFullTrust (Assembly a)
629 // double-lock pattern
630 if (_fullTrust == null) {
632 if (_fullTrust == null)
633 _fullTrust = new NamedPermissionSet ("FullTrust");
638 return SecurityManager.IsGranted (a, _fullTrust, false);
640 catch (SecurityException) {
645 private static bool LinkDemandUnmanaged (Assembly a)
647 return IsGranted (a, UnmanagedCode);
650 // we try to provide as much details as possible to help debugging
651 private static void LinkDemandSecurityException (int securityViolation, Assembly a, MethodInfo method)
653 string message = null;
654 AssemblyName an = null;
655 PermissionSet granted = null;
656 PermissionSet refused = null;
657 object demanded = null;
658 IPermission failed = null;
662 granted = a.GrantedPermissionSet;
663 refused = a.DeniedPermissionSet;
666 switch (securityViolation) {
667 case 1: // MONO_JIT_LINKDEMAND_PERMISSION
668 message = Locale.GetText ("Permissions refused to call this method.");
670 case 2: // MONO_JIT_LINKDEMAND_APTC
671 message = Locale.GetText ("Partially trusted callers aren't allowed to call into this assembly.");
672 demanded = (object) _fullTrust;
674 case 4: // MONO_JIT_LINKDEMAND_ECMA
675 message = Locale.GetText ("Calling internal calls is restricted to ECMA signed assemblies.");
677 case 8: // MONO_JIT_LINKDEMAND_PINVOKE
678 message = Locale.GetText ("Calling unmanaged code isn't allowed from this assembly.");
679 demanded = (object) _unmanagedCode;
680 failed = _unmanagedCode;
683 message = Locale.GetText ("JIT time LinkDemand failed.");
687 throw new SecurityException (message, an, granted, refused, method, SecurityAction.LinkDemand, demanded, failed, null);
690 private static void InheritanceDemandSecurityException (int securityViolation, Assembly a, Type t, MethodInfo method)
692 string message = null;
693 AssemblyName an = null;
694 PermissionSet granted = null;
695 PermissionSet refused = null;
699 granted = a.GrantedPermissionSet;
700 refused = a.DeniedPermissionSet;
703 switch (securityViolation) {
704 case 1: // MONO_METADATA_INHERITANCEDEMAND_CLASS
705 message = String.Format (Locale.GetText ("Class inheritance refused for {0}."), t);
707 case 2: // MONO_METADATA_INHERITANCEDEMAND_CLASS
708 message = Locale.GetText ("Method override refused.");
711 message = Locale.GetText ("Load time InheritDemand failed.");
715 throw new SecurityException (message, an, granted, refused, method, SecurityAction.LinkDemand, null, null, null);
718 // internal - get called by the class loader
721 // - class inheritance
722 // - method overrides
723 private unsafe static bool InheritanceDemand (Assembly a, RuntimeDeclSecurityActions *actions)
726 PermissionSet ps = null;
728 if (actions->cas.size > 0) {
729 ps = Decode (actions->cas.blob, actions->cas.size);
730 result = SecurityManager.IsGranted (a, ps, false);
732 if (actions->noncas.size > 0) {
733 ps = Decode (actions->noncas.blob, actions->noncas.size);
734 result = SecurityManager.IsGranted (a, ps, true);
737 // success if one of the permission is granted
738 if (result && (actions->choice.size > 0)) {
739 PermissionSetCollection psc = DecodeCollection (actions->choice.blob, actions->choice.size);
742 foreach (PermissionSet pset in psc) {
743 if (SecurityManager.IsGranted (a, pset, false)) {
753 catch (SecurityException) {
759 // internal - get called by JIT generated code
761 private static void InternalDemand (IntPtr permissions, int length)
763 PermissionSet ps = Decode (permissions, length);
767 private static void InternalDemandChoice (IntPtr permissions, int length)
770 PermissionSetCollection psc = DecodeCollection (permissions, length);
773 throw new SecurityException ("SecurityAction.DemandChoice is only possible in 2.0");