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
;
56 public static class SecurityManager
{
57 private static object _lockObject
;
58 private static ArrayList _hierarchy
;
59 private static IPermission _unmanagedCode
;
60 private static Hashtable _declsecCache
;
61 private static PolicyLevel _level
;
63 static SecurityManager ()
66 // http://msdn.microsoft.com/library/en-us/dnaskdr/html/askgui06032003.asp?frame=true
67 _lockObject
= new object ();
72 extern public static bool CheckExecutionRights
{
73 [MethodImplAttribute (MethodImplOptions
.InternalCall
)]
76 [MethodImplAttribute (MethodImplOptions
.InternalCall
)]
77 [SecurityPermission (SecurityAction
.Demand
, ControlPolicy
= true)]
81 [Obsolete ("The security manager cannot be turned off on MS runtime")]
82 extern public static bool SecurityEnabled
{
83 [MethodImplAttribute (MethodImplOptions
.InternalCall
)]
86 [MethodImplAttribute (MethodImplOptions
.InternalCall
)]
87 [SecurityPermission (SecurityAction
.Demand
, ControlPolicy
= true)]
93 // NOTE: This method doesn't show in the class library status page because
94 // it cannot be "found" with the StrongNameIdentityPermission for ECMA key.
96 // FIXME works for fulltrust (empty), documentation doesn't really make sense, type wise
97 [MonoTODO ("CAS support is experimental (and unsupported). This method only works in FullTrust.")]
98 [StrongNameIdentityPermission (SecurityAction
.LinkDemand
, PublicKey
= "0x00000000000000000400000000000000")]
99 public static void GetZoneAndOrigin (out ArrayList zone
, out ArrayList origin
)
101 zone
= new ArrayList ();
102 origin
= new ArrayList ();
105 public static bool IsGranted (IPermission perm
)
109 if (!SecurityEnabled
)
113 // - Only check the caller (no stack walk required)
114 // - Not affected by overrides (like Assert, Deny and PermitOnly)
115 // - calls IsSubsetOf even for non CAS permissions
116 // (i.e. it does call Demand so any code there won't be executed)
117 // with 2.0 identity permission are unrestrictable
118 return IsGranted (Assembly
.GetCallingAssembly (), perm
);
121 // note: in 2.0 *all* permissions (including identity permissions) support unrestricted
122 internal static bool IsGranted (Assembly a
, IPermission perm
)
124 PermissionSet granted
= a
.GrantedPermissionSet
;
125 if ((granted
!= null) && !granted
.IsUnrestricted ()) {
126 CodeAccessPermission grant
= (CodeAccessPermission
) granted
.GetPermission (perm
.GetType ());
127 if (!perm
.IsSubsetOf (grant
)) {
132 PermissionSet denied
= a
.DeniedPermissionSet
;
133 if ((denied
!= null) && !denied
.IsEmpty ()) {
134 if (denied
.IsUnrestricted ())
136 CodeAccessPermission refuse
= (CodeAccessPermission
) a
.DeniedPermissionSet
.GetPermission (perm
.GetType ());
137 if ((refuse
!= null) && perm
.IsSubsetOf (refuse
))
143 internal static IPermission
CheckPermissionSet (Assembly a
, PermissionSet ps
, bool noncas
)
148 foreach (IPermission p
in ps
) {
149 // note: this may contains non CAS permissions
150 if ((!noncas
) && (p
is CodeAccessPermission
)) {
151 if (!IsGranted (a
, p
))
154 // but non-CAS will throw on failure...
158 catch (SecurityException
) {
167 internal static IPermission
CheckPermissionSet (AppDomain ad
, PermissionSet ps
)
169 if ((ps
== null) || ps
.IsEmpty ())
172 PermissionSet granted
= ad
.GrantedPermissionSet
;
175 if (granted
.IsUnrestricted ())
177 if (ps
.IsUnrestricted ())
178 return new SecurityPermission (SecurityPermissionFlag
.NoFlags
);
180 foreach (IPermission p
in ps
) {
181 if (p
is CodeAccessPermission
) {
182 CodeAccessPermission grant
= (CodeAccessPermission
) granted
.GetPermission (p
.GetType ());
184 if (!granted
.IsUnrestricted () || !(p
is IUnrestrictedPermission
)) {
185 if (!p
.IsSubsetOf (null))
188 } else if (!p
.IsSubsetOf (grant
)) {
192 // but non-CAS will throw on failure...
196 catch (SecurityException
) {
205 [SecurityPermission (SecurityAction
.Demand
, ControlPolicy
= true)]
206 public static PolicyLevel
LoadPolicyLevelFromFile (string path
, PolicyLevelType type
)
209 throw new ArgumentNullException ("path");
211 PolicyLevel pl
= null;
213 pl
= new PolicyLevel (type
.ToString (), type
);
214 pl
.LoadFromFile (path
);
216 catch (Exception e
) {
217 throw new ArgumentException (Locale
.GetText ("Invalid policy XML"), e
);
222 [SecurityPermission (SecurityAction
.Demand
, ControlPolicy
= true)]
223 public static PolicyLevel
LoadPolicyLevelFromString (string str
, PolicyLevelType type
)
226 throw new ArgumentNullException ("str");
228 PolicyLevel pl
= null;
230 pl
= new PolicyLevel (type
.ToString (), type
);
231 pl
.LoadFromString (str
);
233 catch (Exception e
) {
234 throw new ArgumentException (Locale
.GetText ("Invalid policy XML"), e
);
239 [SecurityPermission (SecurityAction
.Demand
, ControlPolicy
= true)]
240 public static IEnumerator
PolicyHierarchy ()
245 public static PermissionSet
ResolvePolicy (Evidence evidence
)
247 // no evidence, no permission
248 if (evidence
== null)
249 return new PermissionSet (PermissionState
.None
);
251 PermissionSet ps
= null;
252 // Note: can't call PolicyHierarchy since ControlPolicy isn't required to resolve policies
253 IEnumerator ple
= Hierarchy
;
254 while (ple
.MoveNext ()) {
255 PolicyLevel pl
= (PolicyLevel
) ple
.Current
;
256 if (ResolvePolicyLevel (ref ps
, pl
, evidence
)) {
257 break; // i.e. PolicyStatementAttribute.LevelFinal
261 ResolveIdentityPermissions (ps
, evidence
);
266 [MonoTODO ("(2.0) more tests are needed")]
267 public static PermissionSet
ResolvePolicy (Evidence
[] evidences
)
269 if ((evidences
== null) || (evidences
.Length
== 0) ||
270 ((evidences
.Length
== 1) && (evidences
[0].Count
== 0))) {
271 return new PermissionSet (PermissionState
.None
);
274 // probably not optimal
275 PermissionSet ps
= ResolvePolicy (evidences
[0]);
276 for (int i
=1; i
< evidences
.Length
; i
++) {
277 ps
= ps
.Intersect (ResolvePolicy (evidences
[i
]));
282 public static PermissionSet
ResolveSystemPolicy (Evidence evidence
)
284 // no evidence, no permission
285 if (evidence
== null)
286 return new PermissionSet (PermissionState
.None
);
288 // Note: can't call PolicyHierarchy since ControlPolicy isn't required to resolve policies
289 PermissionSet ps
= null;
290 IEnumerator ple
= Hierarchy
;
291 while (ple
.MoveNext ()) {
292 PolicyLevel pl
= (PolicyLevel
) ple
.Current
;
293 if (pl
.Type
== PolicyLevelType
.AppDomain
)
295 if (ResolvePolicyLevel (ref ps
, pl
, evidence
))
296 break; // i.e. PolicyStatementAttribute.LevelFinal
299 ResolveIdentityPermissions (ps
, evidence
);
303 static private SecurityPermission _execution
= new SecurityPermission (SecurityPermissionFlag
.Execution
);
305 public static PermissionSet
ResolvePolicy (Evidence evidence
, PermissionSet reqdPset
, PermissionSet optPset
, PermissionSet denyPset
, out PermissionSet denied
)
307 PermissionSet resolved
= ResolvePolicy (evidence
);
308 // do we have the minimal permission requested by the assembly ?
309 if ((reqdPset
!= null) && !reqdPset
.IsSubsetOf (resolved
)) {
310 throw new PolicyException (Locale
.GetText (
311 "Policy doesn't grant the minimal permissions required to execute the assembly."));
314 // do we check for execution rights ?
315 if (CheckExecutionRights
) {
316 bool execute
= false;
317 // an empty permissionset doesn't include Execution
318 if (resolved
!= null) {
319 // unless we have "Full Trust"...
320 if (resolved
.IsUnrestricted ()) {
323 // ... we need to find a SecurityPermission
324 IPermission security
= resolved
.GetPermission (typeof (SecurityPermission
));
325 execute
= _execution
.IsSubsetOf (security
);
330 throw new PolicyException (Locale
.GetText (
331 "Policy doesn't grant the right to execute the assembly."));
339 public static IEnumerator
ResolvePolicyGroups (Evidence evidence
)
341 if (evidence
== null)
342 throw new ArgumentNullException ("evidence");
344 ArrayList al
= new ArrayList ();
345 // Note: can't call PolicyHierarchy since ControlPolicy isn't required to resolve policies
346 IEnumerator ple
= Hierarchy
;
347 while (ple
.MoveNext ()) {
348 PolicyLevel pl
= (PolicyLevel
) ple
.Current
;
349 CodeGroup cg
= pl
.ResolveMatchingCodeGroups (evidence
);
352 return al
.GetEnumerator ();
355 [SecurityPermission (SecurityAction
.Demand
, ControlPolicy
= true)]
356 public static void SavePolicy ()
358 IEnumerator e
= Hierarchy
;
359 while (e
.MoveNext ()) {
360 PolicyLevel level
= (e
.Current
as PolicyLevel
);
365 [SecurityPermission (SecurityAction
.Demand
, ControlPolicy
= true)]
366 public static void SavePolicyLevel (PolicyLevel level
)
368 // Yes this will throw a NullReferenceException, just like MS (see FDBK13121)
372 // private/internal stuff
374 private static IEnumerator Hierarchy
{
377 if (_hierarchy
== null)
378 InitializePolicyHierarchy ();
380 return _hierarchy
.GetEnumerator ();
384 private static void InitializePolicyHierarchy ()
386 string machinePolicyPath
= Path
.GetDirectoryName (Environment
.GetMachineConfigPath ());
387 // note: use InternalGetFolderPath to avoid recursive policy initialization
388 string userPolicyPath
= Path
.Combine (Environment
.InternalGetFolderPath (Environment
.SpecialFolder
.ApplicationData
), "mono");
390 PolicyLevel enterprise
= new PolicyLevel ("Enterprise", PolicyLevelType
.Enterprise
);
392 enterprise
.LoadFromFile (Path
.Combine (machinePolicyPath
, "enterprisesec.config"));
394 PolicyLevel machine
= new PolicyLevel ("Machine", PolicyLevelType
.Machine
);
396 machine
.LoadFromFile (Path
.Combine (machinePolicyPath
, "security.config"));
398 PolicyLevel user
= new PolicyLevel ("User", PolicyLevelType
.User
);
400 user
.LoadFromFile (Path
.Combine (userPolicyPath
, "security.config"));
402 ArrayList al
= new ArrayList ();
407 _hierarchy
= ArrayList
.Synchronized (al
);
411 internal static bool ResolvePolicyLevel (ref PermissionSet ps
, PolicyLevel pl
, Evidence evidence
)
413 PolicyStatement pst
= pl
.Resolve (evidence
);
416 // only for initial (first) policy level processed
417 ps
= pst
.PermissionSet
;
419 ps
= ps
.Intersect (pst
.PermissionSet
);
421 // null is equals to None - exist that null can throw NullReferenceException ;-)
422 ps
= new PermissionSet (PermissionState
.None
);
426 if ((pst
.Attributes
& PolicyStatementAttribute
.LevelFinal
) == PolicyStatementAttribute
.LevelFinal
)
432 internal static void ResolveIdentityPermissions (PermissionSet ps
, Evidence evidence
)
434 // in 2.0 identity permissions can now be unrestricted
435 if (ps
.IsUnrestricted ())
438 // Only host evidence are used for policy resolution
439 IEnumerator ee
= evidence
.GetHostEnumerator ();
440 while (ee
.MoveNext ()) {
441 IIdentityPermissionFactory ipf
= (ee
.Current
as IIdentityPermissionFactory
);
443 IPermission p
= ipf
.CreateIdentityPermission (evidence
);
444 ps
.AddPermission (p
);
449 internal static PolicyLevel ResolvingPolicyLevel
{
450 get { return _level; }
451 set { _level = value; }
454 internal static PermissionSet
Decode (IntPtr permissions
, int length
)
456 // Permission sets from the runtime (declarative security) can be cached
457 // for performance as they can never change (i.e. they are read-only).
458 PermissionSet ps
= null;
461 if (_declsecCache
== null) {
462 _declsecCache
= new Hashtable ();
465 object key
= (object) (int) permissions
;
466 ps
= (PermissionSet
) _declsecCache
[key
];
468 // create permissionset and add it to the cache
469 byte[] data
= new byte [length
];
470 Marshal
.Copy (permissions
, data
, 0, length
);
472 ps
.DeclarativeSecurity
= true;
473 _declsecCache
.Add (key
, ps
);
479 internal static PermissionSet
Decode (byte[] encodedPermissions
)
481 if ((encodedPermissions
== null) || (encodedPermissions
.Length
< 1))
482 throw new SecurityException ("Invalid metadata format.");
484 switch (encodedPermissions
[0]) {
486 // Fx 1.0/1.1 declarative security permissions metadata is in Unicode-encoded XML
487 string xml
= Encoding
.Unicode
.GetString (encodedPermissions
);
488 return new PermissionSet (xml
);
490 // Fx 2.0 are encoded "somewhat, but not enough, like" custom attributes
491 // note: we still support the older format!
492 return PermissionSet
.CreateFromBinaryFormat (encodedPermissions
);
494 throw new SecurityException (Locale
.GetText ("Unknown metadata format."));
498 private static IPermission UnmanagedCode
{
501 if (_unmanagedCode
== null)
502 _unmanagedCode
= new SecurityPermission (SecurityPermissionFlag
.UnmanagedCode
);
504 return _unmanagedCode
;
508 // security check when using reflection
510 [MethodImplAttribute(MethodImplOptions
.InternalCall
)]
511 private static unsafe extern bool GetLinkDemandSecurity (MethodBase method
, RuntimeDeclSecurityActions
*cdecl
, RuntimeDeclSecurityActions
*mdecl
);
513 // When using reflection LinkDemand are promoted to full Demand (i.e. stack walk)
514 internal unsafe static void ReflectedLinkDemandInvoke (MethodBase mb
)
516 RuntimeDeclSecurityActions klass
;
517 RuntimeDeclSecurityActions method
;
519 if (!GetLinkDemandSecurity (mb
, &klass
, &method
))
522 PermissionSet ps
= null;
524 if (klass
.cas
.size
> 0) {
525 ps
= Decode (klass
.cas
.blob
, klass
.cas
.size
);
527 if (klass
.noncas
.size
> 0) {
528 PermissionSet p
= Decode (klass
.noncas
.blob
, klass
.noncas
.size
);
529 ps
= (ps
== null) ? p
: ps
.Union (p
);
532 if (method
.cas
.size
> 0) {
533 PermissionSet p
= Decode (method
.cas
.blob
, method
.cas
.size
);
534 ps
= (ps
== null) ? p
: ps
.Union (p
);
536 if (method
.noncas
.size
> 0) {
537 PermissionSet p
= Decode (method
.noncas
.blob
, method
.noncas
.size
);
538 ps
= (ps
== null) ? p
: ps
.Union (p
);
541 // in this case we union-ed the permission sets because we want to do
542 // a single stack walk (not up to 4).
547 internal unsafe static bool ReflectedLinkDemandQuery (MethodBase mb
)
549 RuntimeDeclSecurityActions klass
;
550 RuntimeDeclSecurityActions method
;
552 if (!GetLinkDemandSecurity (mb
, &klass
, &method
))
555 return LinkDemand (mb
.ReflectedType
.Assembly
, &klass
, &method
);
558 private unsafe static bool LinkDemand (Assembly a
, RuntimeDeclSecurityActions
*klass
, RuntimeDeclSecurityActions
*method
)
561 PermissionSet ps
= null;
563 if (klass
->cas
.size
> 0) {
564 ps
= Decode (klass
->cas
.blob
, klass
->cas
.size
);
565 result
= (SecurityManager
.CheckPermissionSet (a
, ps
, false) == null);
567 if (result
&& (klass
->noncas
.size
> 0)) {
568 ps
= Decode (klass
->noncas
.blob
, klass
->noncas
.size
);
569 result
= (SecurityManager
.CheckPermissionSet (a
, ps
, true) == null);
572 if (result
&& (method
->cas
.size
> 0)) {
573 ps
= Decode (method
->cas
.blob
, method
->cas
.size
);
574 result
= (SecurityManager
.CheckPermissionSet (a
, ps
, false) == null);
576 if (result
&& (method
->noncas
.size
> 0)) {
577 ps
= Decode (method
->noncas
.blob
, method
->noncas
.size
);
578 result
= (SecurityManager
.CheckPermissionSet (a
, ps
, true) == null);
582 catch (SecurityException
) {
587 #pragma warning disable 169
588 private static bool LinkDemandFullTrust (Assembly a
)
590 // FullTrust is immutable (and means Unrestricted)
591 // so we can skip the subset operations and jump to IsUnrestricted.
592 PermissionSet granted
= a
.GrantedPermissionSet
;
593 if ((granted
!= null) && !granted
.IsUnrestricted ())
596 PermissionSet denied
= a
.DeniedPermissionSet
;
597 if ((denied
!= null) && !denied
.IsEmpty ())
603 private static bool LinkDemandUnmanaged (Assembly a
)
605 // note: we know that UnmanagedCode (SecurityPermission) implements IUnrestrictedPermission
606 return IsGranted (a
, UnmanagedCode
);
609 // we try to provide as much details as possible to help debugging
610 private static void LinkDemandSecurityException (int securityViolation
, IntPtr methodHandle
)
612 RuntimeMethodHandle runtimeHandle
= new RuntimeMethodHandle (methodHandle
);
613 MethodInfo method
= (MethodInfo
)(MethodBase
.GetMethodFromHandle (runtimeHandle
));
614 Assembly a
= method
.DeclaringType
.Assembly
;
616 string message
= null;
617 AssemblyName an
= null;
618 PermissionSet granted
= null;
619 PermissionSet refused
= null;
620 object demanded
= null;
621 IPermission failed
= null;
624 an
= a
.UnprotectedGetName ();
625 granted
= a
.GrantedPermissionSet
;
626 refused
= a
.DeniedPermissionSet
;
629 switch (securityViolation
) {
630 case 1: // MONO_JIT_LINKDEMAND_PERMISSION
631 message
= Locale
.GetText ("Permissions refused to call this method.");
633 case 2: // MONO_JIT_LINKDEMAND_APTC
634 message
= Locale
.GetText ("Partially trusted callers aren't allowed to call into this assembly.");
635 demanded
= (object) DefaultPolicies
.FullTrust
; // immutable
637 case 4: // MONO_JIT_LINKDEMAND_ECMA
638 message
= Locale
.GetText ("Calling internal calls is restricted to ECMA signed assemblies.");
640 case 8: // MONO_JIT_LINKDEMAND_PINVOKE
641 message
= Locale
.GetText ("Calling unmanaged code isn't allowed from this assembly.");
642 demanded
= (object) _unmanagedCode
;
643 failed
= _unmanagedCode
;
646 message
= Locale
.GetText ("JIT time LinkDemand failed.");
650 throw new SecurityException (message
, an
, granted
, refused
, method
, SecurityAction
.LinkDemand
, demanded
, failed
, null);
653 private static void InheritanceDemandSecurityException (int securityViolation
, Assembly a
, Type t
, MethodInfo method
)
655 string message
= null;
656 AssemblyName an
= null;
657 PermissionSet granted
= null;
658 PermissionSet refused
= null;
661 an
= a
.UnprotectedGetName ();
662 granted
= a
.GrantedPermissionSet
;
663 refused
= a
.DeniedPermissionSet
;
666 switch (securityViolation
) {
667 case 1: // MONO_METADATA_INHERITANCEDEMAND_CLASS
668 message
= String
.Format (Locale
.GetText ("Class inheritance refused for {0}."), t
);
670 case 2: // MONO_METADATA_INHERITANCEDEMAND_CLASS
671 message
= Locale
.GetText ("Method override refused.");
674 message
= Locale
.GetText ("Load time InheritDemand failed.");
678 throw new SecurityException (message
, an
, granted
, refused
, method
, SecurityAction
.InheritanceDemand
, null, null, null);
681 // called by the runtime when CoreCLR is enabled
683 private static void ThrowException (Exception ex
)
688 // internal - get called by the class loader
691 // - class inheritance
692 // - method overrides
693 private unsafe static bool InheritanceDemand (AppDomain ad
, Assembly a
, RuntimeDeclSecurityActions
*actions
)
696 PermissionSet ps
= null;
698 if (actions
->cas
.size
> 0) {
699 ps
= Decode (actions
->cas
.blob
, actions
->cas
.size
);
700 result
= (SecurityManager
.CheckPermissionSet (a
, ps
, false) == null);
702 // also check appdomain
703 result
= (SecurityManager
.CheckPermissionSet (ad
, ps
) == null);
706 if (actions
->noncas
.size
> 0) {
707 ps
= Decode (actions
->noncas
.blob
, actions
->noncas
.size
);
708 result
= (SecurityManager
.CheckPermissionSet (a
, ps
, true) == null);
710 // also check appdomain
711 result
= (SecurityManager
.CheckPermissionSet (ad
, ps
) == null);
716 catch (SecurityException
) {
721 // internal - get called at JIT time
723 private static void DemandUnmanaged ()
725 UnmanagedCode
.Demand ();
728 // internal - get called by JIT generated code
730 private static void InternalDemand (IntPtr permissions
, int length
)
732 PermissionSet ps
= Decode (permissions
, length
);
736 private static void InternalDemandChoice (IntPtr permissions
, int length
)
738 throw new SecurityException ("SecurityAction.DemandChoice was removed from 2.0");
740 #pragma warning restore 169