.NET Code Access Security (CAS)

Security is an essential part of an application and it should be taken into consideration from the grass root level from an application’s design. Security is all about protecting your assets from unauthorized actions.

Introduction

Security is an essential part of an application and it should be taken into consideration from the grass-roots level of an application's design. Security is all about protecting your assets from unauthorized actions.

But Code Access Security (CAS) is a feature of .NET CLR that enables you to control the permissions that an individual .NET application has on your system during its execution. As a developer, you must understand how to create applications that work, even when some permission is restricted or to restrict your application from performing certain tasks, for example, a user can't delete/create a file in the "C:\Windows" directory.

Security Model

When you perform some action on the user interface, such as click a button, your application connects to the internet, downloads the modules into the Download Assembly Cache, and begins executing. Behind the scenes, what evidence do you actually have that you can trust the code your computer is downloading?

.NET enforces security polices around assemblies. It uses the evidence that an assembly has, such as the origination of the file. The runtime places all code from the local intranet into a specific group. It then uses the security policies to decide what permissions the code should be granted at a granular level.

MSIL Verification

When .NET code is compiled, the output it produces is an intermediate language (IL) code that requires a runtime to execute the IL. So during assembly load at runtime, it first validates the metadata then examines the IL code against this metadata to see that the code is type safe. When MSIL code is executed, it is compiled Just-in-Time and converted into a platform-specific code that's called native code.
Thus any code that can be converted to native code is valid code. Code that can't be converted to native code due to unrecognized instructions is called Invalid code. During JIT compilation the code is verified to see if it is type-safe or not.

To determine whether your code is verifiably type safe, you can use the "peverify" utility at the .NET command prompt as in the following;

peverify ConsoleApplication1.exe

After executing the aforementioned command, you will get the following output:

All classes and Methods in ConsoleApplication1.exe Verified

Code Access Security

Code Access Security enables users to restrict, on a very granular level, what managed code can do according to a level of trust. If the CLR trusts the code enough to allow it to run then it will begin executing the code depending on the permissions given to the assembly. If the code is not trusted and attempts to perform some action it does not have relevant rights for then a security exception is thrown.

It is important to understand that code access security is about protecting resources, such as directories, local drives, event logs, user interface and the network from malicious code. It is not primarily a tool for protecting software from users.

Code Access Security can be applied only to managed applications. Unmanaged applications run without any CAS restrictions and are limited only by the operating system's role-based security. If CAS is used to restrict the permissions of an assembly then the assembly is considered partially trusted. Partially trusted assemblies must undergo CAS permission checks each time they access a protected resource. Fully trusted assemblies, like unmanaged code, can access any system resource that the user has permissions to access

Elements of Code Access Security

Every security system needs some sort of mechanism (such as user name, password and Access Control List (ACL)) to identify the users and determine what a user can or can't do. However CAS identifies and assigns permissions to application rather than to application users.

CAS identifies assemblies using evidence, there are a few elements by which an assembly can be identified, such as location, hash code and signature of the assembly. Evidence is the information that the runtime gathers about an assembly to determine which code group the assembly belongs to. Code groups in turn grant an assembly a permission set.

Components Of Code Access Security

Code Group

The evidence provided by an assembly is used as the condition for granting and revoking permissions to it. It is done by putting the code in an appropriate code group. Every code group stipulates a membership condition and has specific conditions attached to it. Any assemblies that meet the condition become a member of the group. Code groups are arranged in a hierarchy and assemblies are nearly always matched to several code groups. The code group at the root of the hierarchy is called All Code and contains all other code groups.

Evidence

In order for the CLR to determine which code group to place assembly information into, the first step is to read supplied evidence. There are two main sources of information, they are internet and intranet. The group internet defines code that is sources from the internet and the group intranet defines code sources from a LAN. The examination of the assembly evidence makes the authentication part of the security process. The following table depicts the major type of evidence an assembly can present to the CLR.

Evidence Description
Zone The region such as intranet, local, trusted from which the code originated.
URL The specific URL from which the code originated.
Strong Name Unique verifiable name for the code.
Site The website from which the code originated.
Harsh Value The hash value of the assembly contents.
Publisher The assembly digital signature that uniquely identified the developer.
Application Directory The directory in which assembly resides.

Permissions

Permissions are the actions you allow each code group to perform. The system administrator usually manages the permissions at the enterprise, machine and user levels. The CLR Virtual Execution System (VES) loads and runs programs. It provides the functionality required to execute managed code and uses assembly metadata to connect modules together at runtime. When VES loads an assembly, it matches the assembly to one or more code groups. Each code group is assigned to one or more permissions that specify what actions assemblies can do in that code group.

Note: mscorcfg.msc is no more a part of .NET 4.0; you can use caspol.exe instead to do the same work.

Permissions Sets are unique combinations of security configurations that determine what each user with access to a machine can do on that machine. There are several permission sets shipped with the .NET Framework as in the following table:

Permission Description
FullTrust Allow full access to all resources.
Everything Allow full access to all resources (group isn't added to assembly list)
Internet Grant Default rights.
SkipVerification Bypass all security verification
Nothing Denies all access including Execution
Execution Allows execution-only access.

We will now create simple applications that will hard-code the name of an assembly with full path to load it into memory. At this point, we will enumerate over each supplied form of assembly evidence and print the data to the console window.

To begin, the Program type provides a Main() method that allows users to enter the full path to the assembly they wish to evaluate. We will pass the Assembly reference to another helper method named DisplayEvidence(). The following is the story so far:

using System;
using System.Collections;
using System.Security.Policy;
using System.Reflection;

namespace SecTest
{
    class Program
    {
        static void Main(string[] args)
        {
            // Load another assembly
            Assembly asb = Assembly.LoadFrom(@"F:\Temp\test\test\bin\Debug\test.exe");
 
            Console.WriteLine("***** Evidence Viewer *****\n");
 
            if (asb != null)
            {
                DisplayEvidence(asb);
            }
            Console.ReadLine();
        }

        private static void DisplayEvidence(Assembly asm)
        {
            // Get evidence collection using enumerator.
            Evidence e = asm.Evidence;
            IEnumerator obj = e.GetHostEnumerator();
            // Now print out the evidence.
            while (obj.MoveNext())
            {
                Console.WriteLine(" **** Press Enter to continue ****");
                Console.ReadLine();
                Console.WriteLine(obj.Current);
            }
        }
    }
}


To test our application, my suggestion is to create a folder directly of your C drive named "Temp". Into this folder, copy the strongly named "test.exe" assembly that is just a simple program and run your program. Your application should now print out each flavor of evidence to the console, as shown in the the figure below (note that the way our application was created, you will need to hit the Enter key to display each form of evidence).

Security.jpg

Here you can notice that test.exe has been placed into the MyComputer zone, from the URL of "F:/Temp", and has a specific strong name value. If you were to load assemblies from an entirely different location (such as a remote website), you would obviously see unique output. At this point simply understand that when an assembly is loaded into memory, the CLR will investigate the supplied evidence.

Using Visual Studio to Figure Minimum Permissions

One of the most common problems programmers encountered during testing of application security and permissions in the past, is that they were always forced to develop applications under Full Trust. Which implies having access to the system resources in a very open manner. The Visual Studio IDE ships with the security configuration features if you notice in the properties of your solution.

The new security tab allows you to run your applications under various types of zones as shown in the following screenshot:

Security1.jpg

After checking the "Enable ClickOnce security" setting check box, you will able to select whether the application will be running on the client machine under full trust or partial trust.

Managing Code Access Permissions

This section describes the most common types of permissions for programmatic access and how they are used. We first set up the permission that we want to be effective for a .NET code execution. Then grant the code up to the appropriate access level.

This example illustrates the use of the FileIOPermission class and the file "c:/temp/xyz.txt" has been secured at the operating system level so that no one can access it. Hence, let's create a Windows Form and place a button control that will read from or write to a local text file. The following program requires the following specified namespacs.

using System.Security;
using System.Security.Principal;
using System.Security.Permissions;
using System.IO;
FileIOPermission ff = new FileIOPermission(FileIOPermissionAccess.Write,"c:/temp/xyz.txt");
ff.Assert();

    try
    {
      StreamWriter objW= new   StreamWriter(File.Open("c:/temp/xyz.txt",FileMode.Open));
      objW.WriteLine("testing");
      objW.Flush();
      objW.Close();
      objW = null
    }
    catch(Exception err)
    {
       MessageBox.Show(err.Message); 
    }

In the code above we use the Assert() method that declares that the resources should be accessible even if the caller has not been granted the permission to access the resource. So in this case, since the file is secured at the operating system level, we get the following error:

Security2.jpg

Now, let's look at another scenario where the operating system imposes full rights to the resource, but the code permissions is set to Deny as in the following;

CodeAccessPermission  perm = new FileIOPermission(FileIOPermissionAccess.AllAccess,"c:/temp/xyz.txt");
perm.Deny();
   
try
    {
        StreamReader objW= File.OpenText(@"c:/temp/xyz.txt");
        CodeAccessPermission.RevertDeny(); 
        objW = null
    }
    catch(Exception err)
    {
    MessageBox.Show(err.Message); 
    }


In the code above the Deny() method denies all the caller's access to the object, regardless of whether the operating system granted them permission; that is usually the case. We catch the following error in our exception handler as in the following.

Security3.jpg

Code Access Security Tools (caspol.exe)

.NET includes a command line utility "caspol.exe" to configure or view the security policy of a particular assembly. Using caspol.exe you can specify what level of trust you have for each code access group as well as managing code groups and permissions in more granular fashion. This utility helps us to configure and view security policy at both the Machine level and User level.


Note: In the .NET Framework version 3.5 and earlier versions, CAS policy is always in effect. In the .NET Framework 4, CAS policy must be enabled. It suggested making the following entry in the app.config or Machine.config file to suppress the warning message.

<configuration>

<runtime>

      <LegacyCasPolicy enabled="true"/>

   </runtime>

</configuration>

To use caspol.exe just go to "Start"- > "Programs" -> "Microsoft Visual Studio 2010" -> "Visual Studio Tools" -> "Command Prompt". Let's take a glimpse at this utility:

Security4.jpg

Policy

Security policy is the predefined set of rules that the CLR follows when determining the permissions to grant to code; the .NET Framework ships with four policy levels, User, Enterprise and Machine. The three policy levels as discussed are as follows:

  • User Policy

    This policy is administered by and for the current logged in user and evaluated at the time of grant.
     

  • Enterprise

    This policy is applied for a particular application being used on all the systems in the enterprise. This policy affects every user and system in the enterprise.
     

  • Machine

    This policy is administered by the system administrator or domain controller. The settings on this level affect only the applications that run on the local system.

When you run caspol.exe as an administrator account, it defaults to machine level, but if you log out and log back in as a user who is not in an administrator user group then caspol.exe will default to the User level instead. To work with code groups and permissions on the user or enterprise level using caspol.exe, add either the "-enterprise" or "-user" argument to change the command mode as in the following:

Security5.jpg

Here you notice a warning message occurring due to the disabled CAS policy. You need to enable it in the project app.config file as discussed earlier. Now run the same command, but this time with code groups at the machine level as in the following:

Security6.jpg