Security is always a complex issue. CRL's security is not an exception to this rule.
Code access security is the CLR's security subsystem regarding code and permissions may be granted to code in accessing protected resources and performing operations. Code access security in CLR is based on evidence.
see also:
A CLR host collects security information about the code it executes which is known as Evidence. This evidence information may be the strong name of an assembly, the application's installation directory, the zone of origin (internet/intranet), the URL, the publisher etc.
When a CLR host has permission to provide this evidence information to the CLR security system, then it is considered a trusted host.
The AppDomain class and the Assembly class provide an Evidence property of type System.Security.Policy.Evident
Control Panel | Administrative Tools | Microsoft .NET Framework 2.0 Configuration is the tool for administering security permissions. (that same tool is used with .Net 3 and 3.5). Expand the tree provided by this tool to Console Root | .NET Framework 2.0 Configuration | My Computer | Runtime Security Policy.
That .NET Framework Configuration tool is the recommended way to configure security policy. The Code Access Security Policy command line tool (Caspol.exe), also provides security administration functionality.
The Permissions command line tool (permview.exe) is a tool used to inspect the minimum, optional and refused permission sets an assembly requests by the security system.
Here are some terms used with CAS.
Security Police Level is a configurable set of security rules organized in levels.
Security police levels form a hierarchy where Enterprise level is the top level. Most configuration is done at the Machine level though.
Each Security police level contains two things:
A Permission represents one of two things:
A File IO Permission may or may not permit write access to the C:\Program Files and a User Interface Permission may grant full access to any UI element.
Permissions are predefined by the CLR which provides a class for each possible permission. The ultimate base class is the System.Security.CodeAccessPermission. There are derived classes such as
It is not possible to use a single permission when granting secutity rights. Instead permissions are organized into collections called Permission Sets and is those Permission Sets that are granted to security targets.
A Permission Set is a collection of permissions under a name and it may consists of one or more permissions. A single Permission may be a member to multiple Permission Sets. Administrators can associate Permission Sets with a target code. CLR comes with some predefined Permission Sets listed in the following table.
Permission Sets have no coresponding classes. Permission Sets are just named collections. Administrators may create new Permission Sets and include permissions to them.
A Code Group is a named security policy element. It has two things
Any code that meets the membership condition of a certain Code Group is automatically included in that Code Group. A code may meet the membership condition of many Code Groups. Membership condition match determination is done for a code, based on the Evidence provided to the security system for that code.
Code Groups may have child (nested) Code Groups. A code can not be a member of a child Code Group if it is not a member of the parent Code Group.
Membership conditions are predefined by the CLR which provides a class for each possible membership condition. There can be custom membersip conditions though. Here is a list of the default membership conditions
A assembly may fall into more than one Security Policy Levels and into more than one Code Groups. The CLR's security system uses a special logic in determining the effective Permission Set for an assembly. For each Security Policy Level an individual union set of permissions is constructed using the Permission Sets defined in any Code Group, in that Level, the assembly meets its membership condition. Then a final intersection set of permissions is constructed using the previously constructed sets. That final set is the effective set of permissions granted to the assembly.
A Code Group may have attributes which are used by the security system when calculating the effective Permission Set for an assembly. Specifically a Code Group may have none or one or both of two attributes:
Exclusive: If the membership condition is met then the whole Security Police Level, this Code Group belongs to, contributes to the calculating logic just the Permission Set of the Code Group where this attribute is defined. No other Code Group contributes to the permission set being constructed. Within a certain Security Police Level, code can be a member of just a single Code Group which has the Exclusive attribute defined.
LevelFinal: If the membership condition is met then policy levels in the policy level hierarchy below the Security Police Level, this Code Group belongs to, are not considered by the calculating the effective Permission Set logic.
By default the Enterprise and the User Security Leves each contain oly the All_Code group. At those two levels the All_Code grants full access to any code. Instead at the Machine Security Level the All_Code group grants no access to anyone. It is just the root of the other predefined Code Groups each of which allows specific permissions to resources. It is here in the Machine Security Level where an administrator configures the security policy.
Managed code interacts with CLR's security sub-system by using
Requests used in informing the security subsystem about certain permissions they require to have or they do not wish to have. Demands used in protecting resources by unauthorized callers. Overrides used in override default security settings.
The System.Security.Permissions.SecurityAction enum type defines the possible requests, demands and overrides as members. It is defined as
public enum SecurityAction
{
Demand = 2,
Assert = 3,
Deny = 4,
PermitOnly = 5,
LinkDemand = 6,
InheritanceDemand = 7,
RequestMinimum = 8,
RequestOptional = 9,
RequestRefuse = 10,
}
Here is the grouping
The SecurityAction enum type is used by the System.Security.Permissions.SecurityAttribute which is the base of all security attribute classes.
public abstract class SecurityAttribute : Attribute
{
protected SecurityAttribute(SecurityAction action);
public SecurityAction Action { get; set; }
public bool Unrestricted { get; set; }
public abstract IPermission CreatePermission();
}
The SecurityAction enum type is used by SecurityAttribute descendants to declaratively, that is using attributes, interact with the security system.
The System.Security.CodeAccessPermission class provides methods coressponding to some of the SecurityAction flags.
public abstract class CodeAccessPermission : IPermission, ISecurityEncodable, IStackWalk
{
...
public void Demand();
public void Assert();
public void Deny();
public void PermitOnly();
public static void RevertAll();
public static void RevertAssert();
public static void RevertDeny();
public static void RevertPermitOnly();
...
}
CodeAccessPermission descendants are used to imperatively, that is by calling methods, interact with the security system.
RequestMinimum is used to specify the permission an assembly must have in order to be executed. That is the CLR will run that assembly if and only if all the required permissions are granted to that assembly by the security policy.
RequestOptional is used to specify the permission an assembly would use, but it is not required in order to run. An assembly like that has to be prepared to react politely in case any optional permission is not granted and handle any exceptional situation. CAUTION: If RequestOptional is defined together with RequestMinimum, no other permissions will ever be granted to the assembly even if the security policy grants additional permissions.
[assembly: FileIOPermission(SecurityAction.RequestMinimum, Read = "C:\\")]
[assembly: FileIOPermission(SecurityAction.RequestOptional, Write ="C:\\")]
RequestRefuse is used to specify the permission an assembly refuses to have even if the security policy grants that permission to it.
The Demand is used to specify the permission a caller required to have in order to call the demanding code element. Demands are checked by the CLR performing a so called stack walk.
At execution time, not only the currently executed code but the whole call stack is required to be verified regarding a security permission being demanded. The CLR walks up the call stack and compares the granted permissions of each caller to the currently demanded permission. If any caller fails the check, a security exception is thrown and no access is given. This is called security stack walk.
The LinkDemand is used to specify the permission that just the immediate caller is required to have in order to call the demanding code element. That is no stack walk is perform. Just the immediate caller is checked. CLR performs LinkDemand security checks during JIT compilation time.
InheritanceDemand is applied to classes and methods only and it is used to specify the permission a derived class or an overriden method in a derived class must have.
Overrides are used to alter the outcome of a stack walk initiated by a Demand, thus overriding the security behavior.
The Assert forces a stack walk, initiated by a Demand lower in the stack, to succeed, even if callers higher in the stack than the asserting code, are not granted the specified permission. The code that uses Assert must already have the specified permission.
The Deny forces a stack walk, initiated by a Demand lower in the stack, to fail, even if callers higher in the stack than the denying code, are granted the specified permission. The code that uses Deny must already have the specified permission.
The PermitOnly forces a stack walk, initiated by a Demand lower in the stack, to fail on any demanded permission other than the one the PermitOnly specifies.
Security code uses two syntax forms: declarative and imperative syntax.
Declarative syntax uses Attribute class descendants to mark types with security information. Declarative syntax is used to perform requests, demands and overrides.
Imperative syntax uses method and property calls of CodeAccessPermission derived class instances. Imperative syntax is used to perform demands and overrides, but not requests.
Declarative calls are evaluated by CLR when the marked code element is JIT-compiled. Imperative calls are evaluted by the CRL at run-time.
Declarative security syntax uses a descendant of the System.Security.Permissions.CodeAccessSecurityAttribute class to mark a code element. CodeAccessSecurityAttribute descends from System.Security.Permissions.SecurityAttribute.
public abstract class SecurityAttribute : Attribute
{
protected SecurityAttribute(SecurityAction action);
public SecurityAction Action { get; set; }
public bool Unrestricted { get; set; }
public abstract IPermission CreatePermission();
}
public abstract class CodeAccessSecurityAttribute : SecurityAttribute
{
protected CodeAccessSecurityAttribute(SecurityAction action);
}
Security attribute classes form a hierarchy which is parallel to the hierarchy that stems from the CodeAccessPermission class. That is security attribute classes parallel, to a degree, code access permission classes.
Action and Unrestricted properties are common to all SecurityAttribute classes.
By using the System.Security.Permissions.PermissionSetAttribute class it is possible to have custom defined permission sets that used declaratively.
The SecurityAction.RequestMinimum flag can only be used with assembly scope.
[assembly: RegistryPermissionAttribute(SecurityAction.RequestMinimum, Read = @"HKEY_LOCAL_MACHINE\.....")]
Imperative security syntax uses a descendant of the System.Security.CodeAccessPermission class instance to issue security related calls.
public abstract class CodeAccessPermission : IPermission, ISecurityEncodable, IStackWalk
{
protected CodeAccessPermission();
public override bool Equals(object obj);
public override int GetHashCode();
public override string ToString();
public void Demand();
public void Assert();
public void Deny();
public void PermitOnly();
public static void RevertAssert();
public static void RevertDeny();
public static void RevertPermitOnly();
public static void RevertAll();
public virtual IPermission Union(IPermission other);
public abstract IPermission Intersect(IPermission target);
public abstract bool IsSubsetOf(IPermission target);
public abstract IPermission Copy();
public abstract void FromXml(SecurityElement elem);
public abstract SecurityElement ToXml();
}
NOTE: imperative syntax can perform demands and overrides only, NO requests.
Basically imperative syntax is used to ensure that callers of a method involving imperative syntax are granted the permissions the CodeAccessPermission requires.
FileIOPermission FP = new FileIOPermission(FileIOPermissionAccess.Write, fileName);
FP.Demand();
The above demands that all callers of the enclosing method have write access to fileName.