Răsfoiți Sursa

2004-12-20 Sebastien Pouliot <[email protected]>

	* CodeAccessPermission.cs: Refactored to play more nicely with 
	PermissionSet and the new functionalities in the runtime.
	* PermissionSet.cs: Modified to use the new functionalities in the
	runtime. This allows the declarative stack modifiers to work in a 
	stack walk.
	* SecurityFrame.cs: New. This is the managed representation of the
	security informations associated with a stack frame. This current
	version only support declarative stack modifiers.
	* SecurityManager.cs: Modified to use the new functionalities in the
	runtime.


svn path=/trunk/mcs/; revision=37973
Sebastien Pouliot 21 ani în urmă
părinte
comite
2ee233cb81

+ 13 - 0
mcs/class/corlib/System.Security/ChangeLog

@@ -1,3 +1,16 @@
+2004-12-20  Sebastien Pouliot  <[email protected]>
+
+	* CodeAccessPermission.cs: Refactored to play more nicely with 
+	PermissionSet and the new functionalities in the runtime.
+	* PermissionSet.cs: Modified to use the new functionalities in the
+	runtime. This allows the declarative stack modifiers to work in a 
+	stack walk.
+	* SecurityFrame.cs: New. This is the managed representation of the
+	security informations associated with a stack frame. This current
+	version only support declarative stack modifiers.
+	* SecurityManager.cs: Modified to use the new functionalities in the
+	runtime.
+
 2004-11-30  Sebastien Pouliot  <[email protected]>
 
 	* CodeAccessPermission.cs:

+ 124 - 174
mcs/class/corlib/System.Security/CodeAccessPermission.cs

@@ -37,34 +37,22 @@ using System.Reflection;
 using System.Runtime.CompilerServices;
 using System.Runtime.InteropServices;
 using System.Security.Permissions;
+using System.Threading;
 
 namespace System.Security {
 
 	[Serializable]
 	public abstract class CodeAccessPermission : IPermission, ISecurityEncodable, IStackWalk {
 
-		internal enum StackModifier {
-			Assert = 1,
-			Deny = 2,
-			PermitOnly = 3
-		}
-
 		protected CodeAccessPermission ()
 		{
 		}
 
 		// LAMESPEC: Documented as virtual
-		[MonoTODO ("unmanaged side is incomplete")]
+		[MonoTODO ("Imperative mode isn't supported")]
 		public void Assert ()
 		{
-			// Not everyone can assert freely so we must check for
-			// System.Security.Permissions.SecurityPermissionFlag.Assertion
-			new SecurityPermission (SecurityPermissionFlag.Assertion).Demand ();
-
-			// we must have the permission to assert it to others
-			if (SecurityManager.IsGranted (this)) {
-				SetCurrentFrame (StackModifier.Assert, this.Copy ());
-			}
+			new PermissionSet (this).Assert ();
 		}
 
 #if NET_2_0
@@ -129,121 +117,19 @@ namespace System.Security {
 		[MonoTODO ("Assert, Deny and PermitOnly aren't yet supported")]
 		public void Demand ()
 		{
+			// note: here we're sure it's a CAS demand
 			if (!SecurityManager.SecurityEnabled)
 				return;
 
-			// Order is:
-			// 1. CheckDemand (current frame)
-			//	note: for declarative attributes directly calls IsSubsetOf
-
-			Assembly a = null;
-			StackTrace st = new StackTrace (1); // skip ourself
-			StackFrame[] frames = st.GetFrames ();
-			foreach (StackFrame sf in frames) {
-				MethodBase mb = sf.GetMethod ();
-				// however the "final" grant set is resolved by assembly, so
-				// there's no need to check it every time (just when we're 
-				// changing assemblies between frames).
-				Assembly af = mb.ReflectedType.Assembly;
-				CodeAccessPermission cap = null;
-				if (a != af) {
-					a = af;
-					if (a.GrantedPermissionSet != null)
-						cap = (CodeAccessPermission) a.GrantedPermissionSet.GetPermission (this.GetType ());
-					else
-						cap = null;
-
-					// CheckDemand will always return false in case cap is null
-					if ((cap == null) || !CheckDemand (cap)) {
-						if (a.DeniedPermissionSet != null) {
-							cap = (CodeAccessPermission) a.DeniedPermissionSet.GetPermission (this.GetType ());
-						}
-						else
-							cap = null;
-
-						// IsSubsetOf "should" always return false if cap is null
-						if ((cap != null) && IsSubsetOf (cap)) {
-							Type t = this.GetType ();
-							// TODO add more details
-							throw new SecurityException ("ReqRefuse", t);
-						}
-					}
-					else {
-						throw new SecurityException ("Demand failed", a.GetName (),
-							a.GrantedPermissionSet, a.DeniedPermissionSet, (MethodInfo) mb, 
-							SecurityAction.Demand, this, cap, a.Evidence);
-					}
-				}
-				object[] perms = GetFramePermissions ();
-				if (perms == null)
-					continue;
-
-				// 2. CheckPermitOnly
-				object o = perms [(int)StackModifier.PermitOnly];
-				if (o != null) {
-					cap = (o as CodeAccessPermission);
-					if (cap != null) {
-						if (!CheckPermitOnly (cap))
-							throw new SecurityException ("PermitOnly");
-					}
-					else {
-						PermissionSet ps = (o as PermissionSet);
-						foreach (IPermission p in ps) {
-							if (p is CodeAccessPermission) {
-								if (!CheckPermitOnly (p as CodeAccessPermission))
-									throw new SecurityException ("PermitOnly");
-							}
-						}
-					}
-				}
-
-				// 3. CheckDeny
-				o = perms [(int)StackModifier.Deny];
-				if (o != null) {
-					cap = (o as CodeAccessPermission) ;
-					if (cap != null) {
-						if (!CheckDeny (cap))
-							throw new SecurityException ("Deny");
-					}
-					else {
-						PermissionSet ps = (o as PermissionSet);
-						foreach (IPermission p in ps) {
-							if (p is CodeAccessPermission) {
-								if (!CheckPermitOnly (p as CodeAccessPermission))
-									throw new SecurityException ("Deny");
-							}
-						}
-					}
-				}
-
-				// 4. CheckAssert
-				o = perms [(int)StackModifier.Assert];
-				if (o != null) {
-					cap = (o as CodeAccessPermission);
-					if (cap != null) {
-						if (CheckAssert (cap)) {
-							return; // stop the stack walk
-						}
-					}
-					else {
-						PermissionSet ps = (o as PermissionSet);
-						foreach (IPermission p in ps) {
-							if (p is CodeAccessPermission) {
-								if (!CheckPermitOnly (p as CodeAccessPermission)) {
-									return; // stop the stack walk
-								}
-							}
-						}
-					}
-				}
-			}
+			// skip frames until we get the caller (of our caller)
+			new PermissionSet (this).CasOnlyDemand (2);
 		}
 
 		// LAMESPEC: Documented as virtual
-		[MonoTODO ("unmanaged side is incomplete")]
+		[MonoTODO ("Imperative mode isn't supported")]
 		public void Deny ()
 		{
-			SetCurrentFrame (StackModifier.Deny, this.Copy ());
+			new PermissionSet (this).Deny ();
 		}
 
 #if NET_2_0
@@ -291,69 +177,79 @@ namespace System.Security {
 		}
 
 		// LAMESPEC: Documented as virtual
-		[MonoTODO ("unmanaged side is incomplete")]
+		[MonoTODO ("Imperative mode isn't supported")]
 		public void PermitOnly ()
 		{
-			SetCurrentFrame (StackModifier.PermitOnly, this.Copy ());
+			new PermissionSet (this).PermitOnly ();
 		}
 
-		[MonoTODO ("unmanaged side is incomplete")]
+		[MonoTODO ("Imperative mode isn't supported")]
 		public static void RevertAll ()
 		{
-			if (!ClearFramePermissions ()) {
-				string msg = Locale.GetText ("No security frame present to be reverted.");
+			SecurityFrame sf = new SecurityFrame (1);
+			bool revert = false;
+			if ((sf.Assert != null) && !sf.Assert.DeclarativeSecurity) {
+				revert = true;
+				// TODO
+				throw new NotSupportedException ("Currently only declarative Assert are supported.");
+			}
+			if ((sf.Deny != null) && !sf.Deny.DeclarativeSecurity) {
+				revert = true;
+				// TODO
+				throw new NotSupportedException ("Currently only declarative Deny are supported.");
+			}
+			if ((sf.PermitOnly != null) && sf.PermitOnly.DeclarativeSecurity) {
+				revert = true;
+				// TODO
+				throw new NotSupportedException ("Currently only declarative PermitOnly are supported.");
+			}
+
+			if (!revert) {
+				string msg = Locale.GetText ("No stack modifiers are present on the current stack frame.");
+				// FIXME: we don't (yet) support imperative stack modifiers
+				msg += Environment.NewLine + "Currently only declarative stack modifiers are supported.";
 				throw new ExecutionEngineException (msg);
 			}
 		}
 
-		[MonoTODO ("unmanaged side is incomplete")]
+		[MonoTODO ("Imperative mode isn't supported")]
 		public static void RevertAssert ()
 		{
-			RevertCurrentFrame (StackModifier.Assert);
+			SecurityFrame sf = new SecurityFrame (1);
+			if ((sf.Assert != null) && !sf.Assert.DeclarativeSecurity) {
+				// TODO
+				throw new NotSupportedException ("Currently only declarative Assert are supported.");
+			} else {
+				// we can't revert declarative security (or an empty frame) imperatively
+				ThrowExecutionEngineException (SecurityAction.Assert);
+			}
 		}
 
-		[MonoTODO ("unmanaged side is incomplete")]
+		[MonoTODO ("Imperative mode isn't supported")]
 		public static void RevertDeny ()
 		{
-			RevertCurrentFrame (StackModifier.Deny);
+			SecurityFrame sf = new SecurityFrame (1);
+			if ((sf.Deny != null) && !sf.Deny.DeclarativeSecurity) {
+				// TODO
+				throw new NotSupportedException ("Currently only declarative Deny are supported.");
+			} else {
+				// we can't revert declarative security (or an empty frame) imperatively
+				ThrowExecutionEngineException (SecurityAction.Deny);
+			}
 		}
 
-		[MonoTODO ("unmanaged side is incomplete")]
+		[MonoTODO ("Imperative mode isn't supported")]
 		public static void RevertPermitOnly ()
 		{
-			RevertCurrentFrame (StackModifier.PermitOnly);
-		}
-
-		// Internal calls
-#if false
-		// see mono/mono/metadata/cas.c for implementation
-
-		[MethodImplAttribute (MethodImplOptions.InternalCall)]
-		static extern bool ClearFramePermissions ();
-
-		[MethodImplAttribute (MethodImplOptions.InternalCall)]
-		static extern object[] GetFramePermissions ();
-
-		[MethodImplAttribute (MethodImplOptions.InternalCall)]
-		static extern bool SetFramePermissions (int index, object permissions);
-#else
-		// icalls are not yet commited so...
-
-		static bool ClearFramePermissions () 
-		{
-			return true;
-		}
-
-		static object[] GetFramePermissions () 
-		{
-			return null;
-		}
-
-		static bool SetFramePermissions (int index, object permissions)
-		{
-			return true;
+			SecurityFrame sf = new SecurityFrame (1);
+			if ((sf.PermitOnly != null) && sf.PermitOnly.DeclarativeSecurity) {
+				// TODO
+				throw new NotSupportedException ("Currently only declarative PermitOnly are supported.");
+			} else {
+				// we can't revert declarative security (or an empty frame) imperatively
+				ThrowExecutionEngineException (SecurityAction.PermitOnly);
+			}
 		}
-#endif
 
 		// Internal helpers methods
 
@@ -433,6 +329,60 @@ namespace System.Security {
 			return (String.Compare (value, Boolean.TrueString, true, CultureInfo.InvariantCulture) == 0);
 		}
 
+		internal bool ProcessFrame (SecurityFrame frame, ref Assembly current)
+		{ 
+			// however the "final" grant set is resolved by assembly, so
+			// there's no need to check it every time (just when we're 
+			// changing assemblies between frames).
+			if (frame.Assembly != current) {
+				current = frame.Assembly;
+				// 1. CheckDemand
+				if (!SecurityManager.IsGranted (current, this)) {
+					ThrowSecurityException ("Demand failed assembly permissions checks.",
+						current, frame.Method, SecurityAction.Demand, this);
+				}
+			}
+
+			// skip next steps if not Assert, Deny or PermitOnly are present
+			if (!frame.HasStackModifiers)
+				return false;	// continue the stack walk
+
+			// 2. CheckPermitOnly
+			if (frame.PermitOnly != null) {
+				foreach (IPermission p in frame.PermitOnly) {
+					if (p is CodeAccessPermission) {
+						if (!CheckPermitOnly (p as CodeAccessPermission))
+							ThrowSecurityException ("PermitOnly", current, frame.Method, SecurityAction.Demand, p);
+					}
+				}
+			}
+
+			// 3. CheckDeny
+			if (frame.Deny != null) {
+				foreach (IPermission p in frame.Deny) {
+					if (p is CodeAccessPermission) {
+						if (!CheckDeny (p as CodeAccessPermission))
+							ThrowSecurityException ("Deny", current, frame.Method, SecurityAction.Demand, p);
+					}
+				}
+			}
+
+			// 4. CheckAssert
+			if (frame.Assert != null) {
+				foreach (IPermission p in frame.Assert) {
+					if (p is CodeAccessPermission) {
+						if (!CheckAssert (p as CodeAccessPermission)) {
+							// FIXME: partial asserts
+							return true; // stop the stack walk
+						}
+					}
+				}
+			}
+
+			// continue the stack walk
+			return false; 
+		}
+
 		internal static void ThrowInvalidPermission (IPermission target, Type expected) 
 		{
 			string msg = Locale.GetText ("Invalid permission type '{0}', expected type '{1}'.");
@@ -440,20 +390,20 @@ namespace System.Security {
 			throw new ArgumentException (msg, "target");
 		}
 
-		internal static void SetCurrentFrame (StackModifier stackmod, object permissions)
+		internal static void ThrowExecutionEngineException (SecurityAction stackmod)
 		{
-			if (!SetFramePermissions ((int)stackmod, permissions)) {
-				string msg = Locale.GetText ("An {0} modifier is already present on the current stack frame.");
-				throw new SecurityException (String.Format (msg, stackmod));
-			}
+			string msg = Locale.GetText ("No {0} modifier is present on the current stack frame.");
+			// FIXME: we don't (yet) support imperative stack modifiers
+			msg += Environment.NewLine + "Currently only declarative stack modifiers are supported.";
+			throw new ExecutionEngineException (String.Format (msg, stackmod));
 		}
 
-		internal static void RevertCurrentFrame (StackModifier stackmod)
+		internal void ThrowSecurityException (string message, Assembly a, MethodInfo mi, SecurityAction action, IPermission failed)
 		{
-			if (!SetFramePermissions ((int)stackmod, null)) {
-				string msg = Locale.GetText ("No {0} modifier is present on the current stack frame.");
-				throw new ExecutionEngineException (String.Format (msg, stackmod));
-			}
+			throw new SecurityException (Locale.GetText (message), 
+				a.GetName (), a.GrantedPermissionSet, 
+				a.DeniedPermissionSet, mi, action, this, 
+				failed, a.Evidence);
 		}
 	}
 }

+ 76 - 27
mcs/class/corlib/System.Security/PermissionSet.cs

@@ -39,6 +39,7 @@ using System.Runtime.Serialization.Formatters.Binary;
 using System.Security.Permissions;
 using System.Security.Policy;
 using System.Text;
+using System.Threading;
 
 namespace System.Security {
 
@@ -53,6 +54,7 @@ namespace System.Security {
 		private ArrayList list;
 		private int _hashcode;
 		private PolicyLevel _policyLevel;
+		private bool _declsec;
 
 		// constructors
 
@@ -93,6 +95,17 @@ namespace System.Security {
 			}
 		}
 
+		// Light version for creating a (non unrestricted) PermissionSet with
+		// a single permission. This allows to relax most validations.
+		internal PermissionSet (IPermission perm)
+			: this ()
+		{
+			if (perm != null) {
+				// note: we do not copy IPermission like AddPermission
+				list.Add (perm);
+			}
+		}
+
 		// methods
 
 		public virtual IPermission AddPermission (IPermission perm)
@@ -119,11 +132,12 @@ namespace System.Security {
 				perm = perm.Union (existing);
 			}
 
+			// note: Add doesn't copy
 			list.Add (perm);
 			return perm;
 		}
 
-		[MonoTODO ("unmanaged side is incomplete")]
+		[MonoTODO ("Imperative mode isn't supported")]
 		public virtual void Assert ()
 		{
 			new SecurityPermission (SecurityPermissionFlag.Assertion).Demand ();
@@ -139,7 +153,7 @@ namespace System.Security {
 				}
 			}
 
-			CodeAccessPermission.SetCurrentFrame (CodeAccessPermission.StackModifier.Assert, this.Copy ());
+			throw new NotSupportedException ("Currently only declarative Assert are supported.");
 		}
 
 		internal void Clear () 
@@ -170,9 +184,11 @@ namespace System.Security {
 			}
 		}
 
-		[MonoTODO ("Assert, Deny and PermitOnly aren't yet supported")]
+		[MonoTODO ("Imperative Assert, Deny and PermitOnly aren't yet supported")]
 		public virtual void Demand ()
 		{
+			// Note: SecurityEnabled only applies to CAS permissions
+			// so we're not checking for it (yet)
 			if (IsEmpty ())
 				return;
 
@@ -189,26 +205,43 @@ namespace System.Security {
 						cas.RemovePermission (t);
 					}
 				}
-
-				// don't start the walk if the permission set only contains non CAS permissions
-				if (cas.Count == 0)
-					return;
 			}
 
-			// Note: SecurityEnabled only applies to CAS permissions
-			if (!SecurityManager.SecurityEnabled)
-				return;
+			// don't start the stack walk if
+			// - the permission set only contains non CAS permissions; or
+			// - security isn't enabled (applis only to CAS!)
+			if ((cas.Count > 0) && SecurityManager.SecurityEnabled)
+				CasOnlyDemand (_declsec ? 3 : 2);
+		}
 
-			// FIXME: optimize
-			foreach (IPermission p in cas.list) {
-				p.Demand ();
+		// The number of frames to skip depends on who's calling
+		// - CodeAccessPermission.Demand (imperative)
+		// - PermissionSet.Demand (imperative)
+		// - SecurityManager.InternalDemand (declarative)
+		internal void CasOnlyDemand (int skip)
+		{
+			Assembly current = null;
+
+			// skip ourself, Demand and other security runtime methods
+			foreach (SecurityFrame sf in SecurityFrame.GetStack (skip)) {
+				if (ProcessFrame (sf, ref current))
+					return; // reached Assert
+			}
+
+			// Is there a CompressedStack to handle ?
+			CompressedStack stack = Thread.CurrentThread.GetCompressedStack ();
+			if ((stack != null) && !stack.IsEmpty ()) {
+				foreach (SecurityFrame frame in stack.List) {
+					if (ProcessFrame (frame, ref current))
+						return; // reached Assert
+				}
 			}
 		}
 
-		[MonoTODO ("unmanaged side is incomplete")]
+		[MonoTODO ("Imperative mode isn't supported")]
 		public virtual void Deny ()
 		{
-			CodeAccessPermission.SetCurrentFrame (CodeAccessPermission.StackModifier.Deny, this.Copy ());
+			throw new NotSupportedException ("Currently only declarative Deny are supported.");
 		}
 
 		[MonoTODO ("adjust class version with current runtime - unification")]
@@ -289,10 +322,10 @@ namespace System.Security {
 			return true;
 		}
 
-		[MonoTODO ("unmanaged side is incomplete")]
+		[MonoTODO ("Imperative mode isn't supported")]
 		public virtual void PermitOnly ()
 		{
-			CodeAccessPermission.SetCurrentFrame (CodeAccessPermission.StackModifier.PermitOnly, this.Copy ());
+			throw new NotSupportedException ("Currently only declarative Deny are supported.");
 		}
 
 		public bool ContainsNonCodeAccessPermissions () 
@@ -540,6 +573,11 @@ namespace System.Security {
 			get { return this; }
 		}
 
+		internal bool DeclarativeSecurity {
+			get { return _declsec; }
+			set { _declsec = value; }
+		}
+
 		[MonoTODO()]
 		void IDeserializationCallback.OnDeserialization (object sender) 
 		{
@@ -589,6 +627,8 @@ namespace System.Security {
 		[MonoTODO ("what's it doing here?")]
 		static public void RevertAssert ()
 		{
+			// FIXME: There's probably a reason this was added here ?
+			CodeAccessPermission.RevertAssert ();
 		}
 #endif
 
@@ -602,19 +642,19 @@ namespace System.Security {
 
 		internal void ImmediateCallerDemand ()
 		{
-			if (!SecurityManager.SecurityEnabled)
-				return;
 			if (IsEmpty ())
 				return;
 
-			StackTrace st = new StackTrace (1); // skip ourself
-			StackFrame sf = st.GetFrame (0);
-			MethodBase mb = sf.GetMethod ();
-			Assembly af = mb.ReflectedType.Assembly;
-			if (!af.Demand (this)) {
-				Type t = this.GetType ();
-				// TODO add more details
-				throw new SecurityException ("LinkDemand failed", t);
+			// skip ourself
+			SecurityFrame sf = new SecurityFrame (1);	// FIXME skip
+			foreach (IPermission p in list) {
+				// note: this may contains non CAS permissions
+				if (p is CodeAccessPermission) {
+					if (SecurityManager.SecurityEnabled)
+						SecurityManager.IsGranted (sf.Assembly, p);
+				} else {
+					p.Demand ();
+				}
 			}
 		}
 
@@ -629,5 +669,14 @@ namespace System.Security {
 				p.Demand ();
 			}
 		}
+
+		internal bool ProcessFrame (SecurityFrame frame, ref Assembly current)
+		{
+			foreach (CodeAccessPermission cap in list) {
+				if (cap.ProcessFrame (frame, ref current))
+					return true; // Assert reached - abort stack walk!
+			}
+			return false;
+		}
 	}
 }

+ 153 - 0
mcs/class/corlib/System.Security/SecurityFrame.cs

@@ -0,0 +1,153 @@
+//
+// System.Security.SecurityFrame.cs
+//
+// Authors:
+//	Sebastien Pouliot  <[email protected]>
+//
+// Copyright (C) 2004 Novell, Inc (http://www.novell.com)
+//
+// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to
+// permit persons to whom the Software is furnished to do so, subject to
+// the following conditions:
+// 
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+// 
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+//
+
+using System.Collections;
+using System.Globalization;
+using System.Reflection;
+using System.Runtime.CompilerServices;
+using System.Security.Permissions;
+
+namespace System.Security {
+
+	// Must match MonoDeclSecurityEntry in /mono/metadata/reflection.h
+	internal struct RuntimeDeclSecurityEntry {
+		public IntPtr blob;
+		public int size;
+	}
+
+	// Must match MonoSecurityFrame in /mono/mini/declsec.h
+	internal class RuntimeSecurityFrame {
+		public MethodInfo method;
+		public RuntimeDeclSecurityEntry assert;
+		public RuntimeDeclSecurityEntry deny;
+		public RuntimeDeclSecurityEntry permitonly;
+	}
+
+	internal struct SecurityFrame {
+
+		private MethodInfo _method;
+		private PermissionSet _assert;
+		private PermissionSet _deny;
+		private PermissionSet _permitonly;
+
+		[MethodImplAttribute (MethodImplOptions.InternalCall)]
+		extern static RuntimeSecurityFrame _GetSecurityFrame (int skip);
+
+		[MethodImplAttribute (MethodImplOptions.InternalCall)]
+		extern static Array _GetSecurityStack (int skip);
+
+		internal SecurityFrame (RuntimeSecurityFrame frame)
+		{
+			_method = null;
+			_assert = null;
+			_deny = null;
+			_permitonly = null;
+			InitFromRuntimeFrame (frame);
+		}
+
+		internal SecurityFrame (int skip)
+		{
+			_method = null;
+			_assert = null;
+			_deny = null;
+			_permitonly = null;
+
+			InitFromRuntimeFrame (_GetSecurityFrame (skip + 2));
+
+			// TODO - add the imperative informations into the frame
+		}
+
+		// Note: SecurityManager.Decode implements a cache - so not every call
+		// ends up making an icall
+		internal void InitFromRuntimeFrame (RuntimeSecurityFrame frame)
+		{
+			_method = frame.method;
+
+			if (frame.assert.size > 0) {
+				_assert = SecurityManager.Decode (frame.assert.blob, frame.assert.size);
+			}
+			if (frame.deny.size > 0) {
+				_deny = SecurityManager.Decode (frame.deny.blob, frame.deny.size);
+			}
+			if (frame.permitonly.size > 0) {
+				_permitonly = SecurityManager.Decode (frame.permitonly.blob, frame.permitonly.size);
+			}
+		}
+
+		public Assembly Assembly {
+			get { return _method.ReflectedType.Assembly; }
+		}
+
+		public MethodInfo Method {
+			get { return _method; }
+		}
+
+		public PermissionSet Assert {
+			get { return _assert; }
+		}
+
+		public PermissionSet Deny {
+			get { return _deny; }
+		}
+
+		public PermissionSet PermitOnly {
+			get { return _permitonly; }
+		}
+
+		public bool HasStackModifiers {
+			get { return ((_assert != null) || (_deny != null) || (_permitonly != null)); }
+		}
+
+		public bool Equals (SecurityFrame sf)
+		{
+			if (Assembly.ToString () != sf.Assembly.ToString ())
+				return false;
+			if (Method.ToString () != sf.Method.ToString ())
+				return false;
+
+			if ((_assert != null) && !_assert.Equals (sf.Assert))
+				return false;
+			if ((_deny != null) && !_deny.Equals (sf.Deny))
+				return false;
+			if ((_permitonly != null) && !_permitonly.Equals (sf.PermitOnly))
+				return false;
+
+			return true;
+		}
+
+		static public ArrayList GetStack (int skipFrames)
+		{
+			Array stack = _GetSecurityStack (skipFrames+2);
+			ArrayList al = new ArrayList (stack.Length);
+			foreach (RuntimeSecurityFrame frame in stack) {
+				al.Add (new SecurityFrame (frame));
+			}
+			return al;
+		}
+	}
+}

+ 83 - 52
mcs/class/corlib/System.Security/SecurityManager.cs

@@ -33,6 +33,8 @@ using System.Collections;
 using System.Globalization;
 using System.IO;
 using System.Reflection;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
 using System.Security.Permissions;
 using System.Security.Policy;
 using System.Text;
@@ -41,9 +43,6 @@ using Mono.Xml;
 
 namespace System.Security {
 
-	// Note: Using [SecurityPermissionAttribute] would be cool but triggers an error
-	// as you can't reference a custom security attribute from it's own assembly (CS0647)
-
 	public sealed class SecurityManager {
 
 		private static bool checkExecutionRights;
@@ -51,6 +50,7 @@ namespace System.Security {
 		private static object _lockObject;
 		private static ArrayList _hierarchy;
 		private static PermissionSet _fullTrust; // for [AllowPartiallyTrustedCallers]
+		private static Hashtable _declsecCache;
 
 		static SecurityManager () 
 		{
@@ -109,7 +109,27 @@ namespace System.Security {
 			// - Policy driven
 			// - Only check the caller (no stack walk required)
 			// - Not affected by overrides (like Assert, Deny and PermitOnly)
-			return Assembly.GetCallingAssembly ().Demand (perm);
+			// - calls IsSubsetOf even for non CAS permissions
+			//   (i.e. it does call Demand so any code there won't be executed)
+			return IsGranted (Assembly.GetCallingAssembly (), perm);
+		}
+
+		internal static bool IsGranted (Assembly a, IPermission perm)
+		{
+			CodeAccessPermission grant = null;
+
+			if (a.GrantedPermissionSet != null)
+				grant = (CodeAccessPermission) a.GrantedPermissionSet.GetPermission (perm.GetType ());
+
+			if ((grant == null) || !perm.IsSubsetOf (grant)) {
+				if (a.DeniedPermissionSet != null) {
+					CodeAccessPermission refuse = (CodeAccessPermission) a.DeniedPermissionSet.GetPermission (perm.GetType ());
+					if ((refuse != null) && perm.IsSubsetOf (refuse))
+						return false;
+				}
+				return true;
+			}
+			return false;
 		}
 
 		[SecurityPermission (SecurityAction.Demand, Flags=SecurityPermissionFlag.ControlPolicy)]
@@ -309,7 +329,36 @@ namespace System.Security {
 			_hierarchy = ArrayList.Synchronized (al);
 		}
 
-		private static PermissionSet Decode (byte[] encodedPermissions)
+		internal static PermissionSet Decode (IntPtr permissions, int length)
+		{
+			// Permission sets from the runtime (declarative security) can be cached
+			// for performance as they can never change (i.e. they are read-only).
+
+			if (_declsecCache == null) {
+				lock (_lockObject) {
+					if (_declsecCache == null) {
+						_declsecCache = new Hashtable ();
+					}
+				}
+			}
+
+			PermissionSet ps = null;
+			lock (_lockObject) {
+				object key = (object) (int) permissions;
+				ps = (PermissionSet) _declsecCache [key];
+				if (ps == null) {
+					// create permissionset and add it to the cache
+					byte[] data = new byte [length];
+					Marshal.Copy (permissions, data, 0, length);
+					ps = Decode (data);
+					ps.DeclarativeSecurity = true;
+					_declsecCache.Add (key, ps);
+				}
+			}
+			return ps;
+		}
+
+		internal static PermissionSet Decode (byte[] encodedPermissions)
 		{
 			switch (encodedPermissions [0]) {
 			case 60:
@@ -337,38 +386,36 @@ namespace System.Security {
 			return Decode (methodPermissions);
 		}
 
-		// internal - get called by JIT generated code
+		// internal - get called at JIT time
 
-		private static void LinkDemand (
-			byte[] classPermissions, byte[] classNonCasPermissions,
-			byte[] methodPermissions, byte[] methodNonCasPermissions,
-			bool allowPartiallyTrustedCallers)
+		unsafe private static void LinkDemand (
+			IntPtr casClassPermission, int casClassLength,
+			IntPtr nonCasClassPermission, int nonCasClassLength,
+			IntPtr casMethodPermission, int casMethodLength,
+			IntPtr nonCasMethodPermission, int nonCasMethodLength,
+			bool requiresFullTrust)
 		{
 			PermissionSet ps = null;
 
-			if (classPermissions != null) {
-				ps = Decode (classPermissions);
-				if (ps != null)
-					ps.ImmediateCallerDemand ();
+			if (casClassLength > 0) {
+				ps = Decode (casClassPermission, casClassLength);
+				ps.ImmediateCallerDemand ();
 			}
-			if (classNonCasPermissions != null) {
-				ps = Decode (classNonCasPermissions);
-				if (ps != null)
-					ps.ImmediateCallerNonCasDemand ();
+			if (nonCasClassLength > 0) {
+				ps = Decode (nonCasClassPermission, nonCasClassLength);
+				ps.ImmediateCallerNonCasDemand ();
 			}
 
-			if (methodPermissions != null) {
-				ps = Decode (methodPermissions);
-				if (ps != null)
-					ps.ImmediateCallerDemand ();
+			if (casMethodLength > 0) {
+				ps = Decode (casMethodPermission, casMethodLength);
+				ps.ImmediateCallerDemand ();
 			}
-			if (methodNonCasPermissions != null) {
-				ps = Decode (methodNonCasPermissions);
-				if (ps != null)
-					ps.ImmediateCallerNonCasDemand ();
+			if (nonCasMethodLength > 0) {
+				ps = Decode (nonCasMethodPermission, nonCasMethodLength);
+				ps.ImmediateCallerNonCasDemand ();
 			}
 
-			if (allowPartiallyTrustedCallers) {
+			if (requiresFullTrust) {
 				// double-lock pattern
 				if (_fullTrust == null) {
 					lock (_lockObject) {
@@ -376,10 +423,13 @@ namespace System.Security {
 							_fullTrust = new NamedPermissionSet ("FullTrust");
 					}
 				}
+				// FIXME: to be optimized with a flag
 				_fullTrust.ImmediateCallerDemand ();
 			}
 		}
 
+		// internal - get called by the class loader
+
 		// Called when
 		// - class inheritance
 		// - method overrides
@@ -397,41 +447,22 @@ namespace System.Security {
 			}
 		}
 
-		private static void InternalDemand (byte[] permissions)
+		// internal - get called by JIT generated code
+
+		unsafe private static void InternalDemand (IntPtr permissions, int length)
 		{
-			PermissionSet ps = Decode (permissions);
+			PermissionSet ps = Decode (permissions, length);
 			ps.Demand ();
 		}
 
-		private static void InternalDemandChoice (byte[] permissions)
+		unsafe private static void InternalDemandChoice (IntPtr permissions, int length)
 		{
 #if NET_2_0
-			PermissionSet ps = Decode (permissions);
+			PermissionSet ps = Decode (permissions, length);
 			// TODO
 #else
 			throw new SecurityException ("SecurityAction.DemandChoice is only possible in 2.0");
 #endif
 		}
-
-		private static void InternalAssert (byte[] permissions)
-		{
-			PermissionSet ps = Decode (permissions);
-			// note: Calling PermissionSet.Assert would have produced an unrequired copy of the PermissionSet
-			CodeAccessPermission.SetCurrentFrame (CodeAccessPermission.StackModifier.Assert, ps);
-		}
-
-		private static void InternalDeny (byte[] permissions)
-		{
-			PermissionSet ps = Decode (permissions);
-			// note: Calling PermissionSet.Deny would have produced an unrequired copy of the PermissionSet
-			CodeAccessPermission.SetCurrentFrame (CodeAccessPermission.StackModifier.Deny, ps);
-		}
-
-		private static void InternalPermitOnly (byte[] permissions)
-		{
-			PermissionSet ps = Decode (permissions);
-			// note: Calling PermissionSet.PermitOnly would have produced an unrequired copy of the PermissionSet
-			CodeAccessPermission.SetCurrentFrame (CodeAccessPermission.StackModifier.PermitOnly, ps);
-		}
 	}
 }