.NET Security Workshop
Home About Workshops Articles Writing Talks Books Contact

6. Permission Enforcement

On earlier pages you have seen how to give permissions to assemblies. This raises another question: once an assembly has permissions, what does it use them for? Types in assemblies make demands for permissions. That is, a type knows that it uses some facility for which there is a .NET permission class and it demands that any code that calls the methods in the type has this specific permission. If the calling code does not have the permission the demand will throw an exception, which prevents the method from being called.

On this page you'll see how this works.

6.1 Types of Permissions

There are three types of permissions, CAS permissions, identity permissions and non-CAS permissions. Non-CAS permissions do not perform a stack walk when the permission is demanded, and so this type of permission is not based on the identity of the current executing assembly, it is based on some other evidence. In current versions of .NET the only non-CAS permission provided with the framework is PrincipalPermission, this permission is based on the identity of the current user (which clearly will be the same for all assemblies in the call stack).

The examples on this page will be mainly about CAS permissions, in which case a demand is based on evidence from the current executing assembly and hence a demand will trigger a stack walk to check all assemblies in the call stack.

Identity permissions are also CAS permissions, but have some differences. The framework defines several identity permission classes (eg PublisherIdentityPermission and the version 3.0/2.0 permission, GacIdentityPermission) but these are not granted through policy, instead they are granted to an assembly based on the assembly itself. Identity permissions have another property: you cannot create them in an unrestricted state (SecurityAttribute.Unrestricted). This means that the identity check will be performed, and the identity check can fail, if the assembly has full trust (for example, an assembly could have full trust, but if a demand was made against it for a certificate that it did not have - PublisherIdentityPermission - the demand would fail). This makes no sense, full trust should mean full trust.

.NET Version 3.0
In Version 3.0/2.0 of the framework this abnormality has been rectified, so demands for any identity permission will succeed if the assembly has full trust.

6.2 Demanding Permissions

One permission is FileIOPermission, this is used to specify which files and folders the assembly can access and what it can do with it. None of the default security policies define a file permission (other than Everything which grants access to all files and folders), this means that assemblies that have full trust (the framework assemblies and assemblies that are installed on the local computer) will have access to files subject only to NTFS permissions, .NET makes no attempt to intervene. All other assemblies (those that are downloaded from the internet or intranet) will not be allowed to access other files, because the LocalIntranet and Internet permission sets will only give access to files in isolated storage. In this example, you will develop a class that will need to access files in a specific folder on your hard disk. This is the source for lib.cs:

using System;
using System.IO;
using System.Reflection;

public class LibraryCode
{
   const string folder = @"C:\SecureFolder";

   public static string GetData(string str)
   {
      string strFile = folder + @"\" + str;
      StreamReader sr = new StreamReader(strFile);
      return sr.ReadToEnd();
   }
}

A process can call this static method to get access to the a file in the C:\SecureFolder folder. If the library and the process are installed on the local machine then GetData will have access to the files on the hard disk subject to NTFS access control lists. If the library is downloaded from the intranet then the assembly will not have access to any folders and so a security exception is thrown.

Now imagine that there is an assembly between the process and the library shown above:

The user.dll assembly can call LibraryCode.GetData in lib.dll to access the file. lib.dll knows that it should be able to access files in the C:\SecureFolder folder, but if it is called by another library, does that library also have the right permissions? If .NET blindly allowed lib.dll to access this folder then the permissions accorded to lib.dll would be delegated to any assembly calling it. Of course, .NET does not blindly give access to other assemblies.

Code access security involves checking the permissions that an assembly has, but it also involves checking the permissions of every assembly in the call stack. To investigate this, create a library called user.dll. The first thing to do is create a key pair for the user.dll file so that the public key for this assembly will be different to the public key for lib.dll. Next, create a C# file called user.cs that has following source code:

using System;
using System.Reflection;

public class UserCode
{
   public static string GetString(string str)
   {
      return LibraryCode.GetData(str);
   }
}

Finally, create a process (app.cs) that will call this code:

using System;

class App
{
   static void Main(string[] args)
   {
      if (args.Length > 0)
      {
         Console.WriteLine("{0}", UserCode.GetString(args[0]));
      }
   }
}

Compile all of this code: app.exe depends on user.dll and user.dll depends on lib.dll.

Now create the C:\SecureFolder folder and add a text file to that folder. Now run the process to access that file. You'll find that the contents of the file will be printed on the console.

Next, you'll address the issue of whether callers of lib.dll will have access. To do this add the following highlighted lines:

using System.Security.Permissions;

public class LibraryCode
{
   const string folder = @"C:\SecureFolder";

   [FileIOPermission(SecurityAction.Demand, Read=folder)]
   public static string GetData(string str)

(Note that StreamReader will perform its own demand for permissions, as explained later, but we will apply this demand here so that you can test permission demands. In most cases you will not need to apply such a demand on your code.)

The GetData method has been decorated with the permission attribute with the Demand parameter. This attribute indicates that the caller of the method needs to have read access to the folder, and furthermore, all other assemblies in the stack must also have the permission (the method that makes the demand is not checked for the permission).

Before LibraryCode.GetData is called the runtime inspects every method in the stack starting at the method that called LibraryCode.GetData for the permissions given to its assembly. If the runtime finds that an assembly does not have the permission that is demanded then a security exception is thrown. You can add more than one security attribute to a method in this way, and if you do this you indicate that callers must have all of the permissions demanded. If the caller lacks one of the permissions then a security exception will be thrown.

Since user.dll is installed on the local machine it will have full trust. Let's change that, by adding the following lines to user.cs.

using System.Reflection;
using System.Security.Permissions;

public class UserCode
{
   [FileIOPermission(SecurityAction.Deny, Unrestricted=true)]
   public static string GetString(string str)

This code tells the runtime explicitly to deny the assembly the permission to access any file. When you compile all the code and run it, you'll find that a security exception is thrown.

Unhandled Exception: System.Security.SecurityException: Request failed.
   at System.Security.SecurityRuntime.FrameDescSetHelper(FrameSecurityDescriptor secDesc, PermissionSet demandSet, PermissionSet& alteredDemandSet)
   at LibraryCode.GetData(String str)
   at UserCode.GetString(String str)
   at UserCode.GetString(String str)
   at App.Main(String[] args)

.NET Version 3.0
When you run this code on Version 3.0/2.0 of the framework you will get a more detailed exception stack trace:

Unhandled Exception: System.Security.SecurityException: Request failed.
   at LibraryCode.GetData(String str)
   at UserCode.GetString(String str)
   at App.Main(String[] args)
The action that failed was:
   Demand
The type of the first permission that failed was:
   System.Security.Permissions.FileIOPermission
The first permission that failed was:
<IPermission class="System.Security.Permissions.FileIOPermission, mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"
   version="1"
   Read="C:\SecureFolder"/>

The trace indicates that the failed call was due to the denied FileIOPermission.

The reason this exception is thrown is that the GetData method in lib.dll has demanded that it needs the FileIOPermission, so a stack walk is initiated and this starts with the immediate caller, GetString in user.dll, since this method is denied the permission an exception is thrown.

Note that since the demand stack walk starts at the immediate caller it means that there is no point in making a demain in the Main method of a process because this method has no caller.

An assembly may not have a permission because of its evidence, but why would a library deny itself a permission? Well, the code in a library will know what code it wants to call and the actions it will take. UserCode.GetString may make the decision that LibraryCode.GetData should be allowed to return data if it was read from a database or read from a socket, but it may take the policy decision that LibraryCode.GetData should not access the file system. Note that you cannot deny the SecurityPermissionFlag.SkipVerification because verification is performed at JIT time but the denying of a permission is performed at runtime.

I should add a bit more explanation about the demands that are made when you use FileStream. In the code used so far, there are two lines of code that access the file (the StreamReader constructor, and the call to ReadToEnd) this code is provided by the framework library, which performs a demand for FileIOPermission for the file that is being accessed, but only when the file is opened. To test this out, comment out the declarative demand on the GetData method, compile and run. Since GetString in user.dll is denied access a security exception is thrown. The interesting thing is the call stack:

System.Security.CodeAccessPermission.Demand
System.IO.FileStream..ctor
System.IO.FileStream..ctor
System.IO.StreamReader..ctor
System.IO.StreamReader..ctor
LibraryCode.GetData
UserCode.GetString
UserCode.GetString
App.Main

This shows that the call to the StreamReader constructor in GetData involves a call to a FileStream constructor that makes a demand for the FileIOPermission for the requested file, a stack walk is performed (starting at the caller of this method) until it reaches GetString which is denied access to files in C:\SecureFolder.

.NET Version 3.0
The call stack is slightly different if the same code is compiled for version 3.0/2.0 of the framework. Firstly, there is only one call to UserCode.GetString, and secondly there is only one FileStream constructor but this calls a method called Init that makes the demand. The exception dump also indicates that the method that caused the failure was GetString, and although it does not give the class name we know that it is a method on UserCode.

The demand for the permission is made in the FileStream constructor and not in the methods that actually read from the file. The reason for this is performance. The argument is that a stack walk is expensive and if the constructor has already performed a stack walk there is no need to perform another one.

There is a problem here. A class, say, FileData, could open the StreamReader object in its constructor and access the file in another method. In this case you could have two other assemblies, one that creates the FileData object which must have the appropriate FileIOPermission permission and this class could pass the initialized FileData object to another class in another assembly that does not have the appropriate permissions. This is no problem because the second class could call any method on FileData that accesses the StreamReader object and no demands will be made.

The solution to this problem is to  make sure that any method on FileData that accesses the StreamReader object must have the demand for the appropriate FileIOPermission permission.

Clean up the project by removing the [FileIOPermission] attribute from UserCode.GetString.

6.3 Imperative Demands

Demanding that permissions are available through attributes has the advantage that you can use reflection to analyse an assembly and get a list of the permissions demanded by the code in the assembly. The framework provides a tool called permview that will do this for you and print the permissions on the console:

C:\TestFolder>permview /decl lib.dll

Microsoft (R) .NET Framework Permission Request Viewer.Version 1.1.4322.573
Copyright (C) Microsoft Corporation 1998-2002. All rights reserved.

Method LibraryCode::GetData() Demand permission set:
<PermissionSet class="System.Security.PermissionSet" version="1">
  <IPermission class="System.Security.Permissions.FileIOPermission,
      mscorlib, Version=1.0.5000.0, Culture=neutral,
      PublicKeyToken=b77a5c561934e089"
    version="1"
    Read="C:\SecureFolder"/>
</PermissionSet>

This is a list of the declarative requests that your code makes, in other words, this output shows that the LibraryCode.GetData method requires that calling code must have the FileIOPermission permission to accesses the specified folder. This does not necessarily tell you the permissions required by code in the assembly, because the assembly you examine could call another assembly that makes demands. However, it does give you a starting point to determine the minimum permissions that the target assembly requires to run.

.NET Version 3.0
This tool is not present in version 3.0/2.0 of the .NET SDK. It has been replaced with a tool call permcalc. This new tool takes a deeper look at the assembly and it examines all application code paths including the assemblies called by the methods in the candidate assembly. As it does this, the tool maintains a simulated call stack and when it comes across a demands it applies this to the assemblies in the simulated stack. This means that a more accurate estimate of the minimum permission set required by the assembly can be calculated.

The advantage with declarative demands is that the details of the demand is determined at compile time. This is also its disadvantage because it means that at compile time these details must be known and the assembly will be constrained to use these values. For example, in our code the path of the folder specified as the secure folder must be known at compile time and then it will be used forever more. Another disadvantage is that the granularity of the permission demand is the scope of the method.

If you want to have a finer granularity you can use a permission object instead. Remove the FileIOPermission attribute on LibraryCode.GetData and change the code to this:

public static string GetData(string str)
{
   // Code here is not subject to the permission demand
   // Determine the folder at runtime
   string strFolder = folder;
   FileIOPermission fio = new FileIOPermission(
      FileIOPermissionAccess.Read, strFolder);
   fio.Demand();
   string strFile = folder + @"\" + str;
   StreamReader sr = new StreamReader(strFile);
   return sr.ReadToEnd();
}

The code determines the folder at runtime (it could read this from configuration information, for example). The code then specifically makes the demand. It is only at this specific point in the code that the stack walk will occur (starting at the immediate caller). Change the process entry point to look like this:

static void Main(string[] args)
{
   if (args.Length > 0)
   {
      if (args.Length > 1)
      {
         FileIOPermission fio = new FileIOPermission(
            FileIOPermissionAccess.Read, @"C:\SecureFolder");
         fio.Deny();
      }
      Console.WriteLine("{0}",
      UserCode.GetString(args[0]));
      if (args.Length > 1)
      {
         FileIOPermission.RevertDeny();
      }

   }
}

(Also add a using statement at the top of the file for System.Security.Permissions.) Compile lib.cs, then compile user.cs (which is dependent upon lib.dll) and finally, compile app.cs (which is dependent upon user.dll).

The idea is that if you provide more than one command line argument the read permission will be denied (regardless of what the command line argument is!). This denial only lasts until RevertDeny is called. Now you should be able to test the code by passing just the name of a file (to get access) or the name of the file and another parameter (to be denied access).

Note that if a method needs to demand more than one permission it can do this by creating a PermissionSet object and each permission can be added through a call to AddPermission and the Demand can be performed on the permission set.

6.4 Assert Demands

Denying permissions through a stack walk is powerful. It protects the machine from possibly dangerous actions requested from code from an unknown source. However, a stack walk is not without cost, and many calls to code that trigger a stack walk can impeded performance. For this reason CAS gives you the ability to assert a demand.

Code that performs some action that requires a CAS permission can call the Assert method on that permission object which will perform a check on the assembly that called Assert, but will not perform a stack walk, so other assemblies in the call stack will not be checked. This looks like a hole in CAS but it isn't for two reasons: firstly the assembly making the assert will be checked for the asserted permission and so this is not a mechanism where a rogue assembly can prevent CAS checks for permissions it does not have; secondly, the assembly must have the permission to call assert (the default policy gives this permission to intranet, but not internet code).

One side effect is that calling code does not have the permission that is asserted, so if you call assert you must be careful about the actions being taken. When you assert a permission you are effectively saying that any code could have access and so do not assert a permission that could allow sensitive information to leak.

For example, imagine our SecureFolder actually has two types of files, secret and public. The public files have names that start with 'public'. Now imagine that CAS policy denies an assembly ccess to the file system (in our example code app.exe does this through a call to Deny), this will mean that calls from the assembly will be denied access regardless of the type of file it is accessing. The LibraryCode in lib.dll can apply a more intelligent solution by examining the request and determine whether the call is to a restricted file or not. If the call is to a public file then LibraryCode.GetData can assert the file permission. This means that if lib.dll has the permission to make the assert, and it has the file permission, then a stack walk for the file permission will stop at lib.dll and the call to read the file will succeed even if an assembly higher in the stack does not have the permission. 

Add the following code to LibraryCode.GetData.

FileIOPermission fio = new FileIOPermission(
   FileIOPermissionAccess.Read, strFolder);
if (str.IndexOf("public" == 0)
   fio.Assert();
else
   fio.Demand();
string strFile = folder + "\\" + str;

Notice that this makes a proactive decision over the assertion: it will only assert the permission if it can positively determine that the assertion will not cause a security issue.

Create two files in SecureFolder called public_data.txt and secret_data.txt. Bearing in mind that the test application called with just one parameter will type the file indicated, but the application called with two parameters will deny the FileIOPermission, try these tests:

app public_data.txt
app public_data.txt deny
app.secret_data.txt
app secret_data.txt deny

You will find that only the last test will throw a security exception: all of the others will access the file. Calling the test application with two parameters simulates the occasion when a calling assembly does not have the FileIOPermission, and calling it with a single parameter simulates the calling assembly having the permission. Thus the results from the last two tests are understandable: in the first of the two the calling assembly has the permission, but in the second of the two the calling assembly does not have the permission and hence the security exception is thrown. The first test is also easily understandable: just one parameter is passed and so the calling assembly has the FileIOPermission. The second test is the interesting one, because in this test app.exe is denied the permission, but since it requests a 'safe' file, LibraryCode.GetData asserts this permission, preventing a stack walk from examining the permissions for app.exe.

Note that lib.dll does not get any special permissions. This assembly is granted FileIOPermission by policy because it is in the MyComputer zone and so has FullTrust. lib.dll is asserting that it wants to use those permissions regardless of the permissions granted to assemblies higher in the stack. IStackWalk.Assert does not give the assembly generating the assert any more permissions, but it effectively elevates the permissions of the calling assemblies because a stack walk demand that would have failed is not made.

A common example of asserting permissions is the SecurityPermissionFlags.UnmanagedCode permission that is required to call native code. Many of the framework classes need to call Win32 native code but it would be impractical to require all assemblies to have this permission.

Code that calls Assert must be granted the SecurityPermissionFlags.Assertion permission and this is verified at link time through a link demand.

6.5 Link Demands

Review the code that we have seen so far: with imperative demands the exception is thrown when the Demand is made. With declarative demands the demand is made when the method is called or (if the attribute is added to the class) when the class is accessed. In all of these cases the assembly has already been loaded and JIT compiled. .NET security allows us to go a couple of steps further. In this section we'll see that the runtime can make checks at JIT compile time, and in the next section we'll see that the assembly loader (Fusion) can also perform checks.

The JIT compiler can perform code access security checks while it is compiling IL. It checks to see if the code being compiled calls code that has code access security attributes that request a SecurityAction.LinkDemand permission check. This check is similar to Demand in that a permission is demanded, however, the demand is only made on the immediate caller and not on the entire stack. Also, unlike Demand, where the check is made at runtime and every time the method is called, LinkDemand is only made at link time, that is, during JIT compilation.

Note the order: the JIT compiler checks to see if the methods called by the current have a LinkDemand and if so it performs the check on the current assembly to see if it has the required permission.

This makes LinkDemand much cheaper to use than Demand, however, because the security check is shallow it is not as safe as Demand. The LinkDemand action is ideal for use with identity checks like [PublisherIdentityPermission], because it checks that the direct caller of the current assembly has the appropriate identity (in this case, a certificate). ([StrongNameIdentityPermission] is also an identity check, however as I explained earlier you should not put any trust in strong names in v1.0 and v1.1 of the framework because there is a bug in how strong names are validated. Furthermore, a cracker can provide a spoofed strong name through the evidence collection of code that calls a library and hence the identity check will succeed even though the caller does not have the specified strong name. For this reason the identity permission has no effect in version 3.0/2.0.)

To test this out, edit LibraryCode.GetData so that it no longer uses imperative security, and instead it uses LinkDemand:

[FileIOPermission(SecurityAction.LinkDemand, Read=folder)]
public static string GetData(string str)
{
   Console.WriteLine("UserCode.GetData");
   string strFile = folder + @"\" + str;
   StreamReader sr = new StreamReader(strFile);
   return sr.ReadToEnd();
}

You have added a call to WriteLine so that you can see when the exception is thrown. The Main method of app.exe will allow you to determine if the FileIOPermission is denied, but let's do this explicitly in the user.dll assembly. Edit user.cs and add the highlighted lines:

[assembly: FileIOPermission(SecurityAction.RequestRefuse, Unrestricted=true)]

public class UserCode
{
  public static string GetString(string str)
   {
      Console.WriteLine("UserCode.GetString");

Again there is a WriteLine to indicate that this code has been called. The attribute is applied at assembly level and uses SecurityAction.RequestRefuse which means that when policy determines the permissions that it will grant to the user.dll assembly it will not get file permissions (and hence user.dll becomes a partially trusted assembly). Setting the Unrestricted property to true means that no file can be accessed. It is worthwhile adding a message in the Main function:

static void Main(string[] args)
{
   Console.WriteLine("App.Main");

Compile all the code and run it requesting a file. You'll find that a SecurityException will be thrown regardless of how many parameters you pass to the process. You will get the following:

App.Main

Unhandled Exception: System.Security.SecurityException: Request for the permission of type System.Security.Permissions.FileIOPermission, mscorlib, Version=1.0.5000.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 failed.
   at UserCode.GetString(String str)
   at App.Main(String[] args)

The state of the failed permission was:
<IPermission class="System.Security.Permissions.FileIOPermission, mscorlib, Version=1.0.5000.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"
   version="1"
   Read="C:\SecureFolder"/>

Here, you'll see that the message App.Main appears on the console, but the message UserCode.GetString does not appear. This confirms what I mentioned above: when UserCode.GetString is JIT compiled the JIT compiler checks the methods that UserCode.GetString will call, if those called methods have a LinkDemand a permission check is performed on user.dll. If this check fails then JIT compilation of UserCode.GetString will fail.

This is important: the caller code is not JIT compiled if the called code has a LinkDemand and the caller assembly does not have the permission.

.NET Version 3.0
The exception trace for this code on version 3.0/2.0 is different and more elucidating (this is edited):

App.Main

Unhandled Exception: System.Security.SecurityException: Request for the permission of type 'System.Security.Permissions.FileIOPermission, mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089' failed.
[snip]
   at UserCode.GetString(String str)
   at App.Main(String[] args)
The action that failed was:
   LinkDemand

Again, this indicates that the code in App.Main was called, but the code in UserCode.GetString wasn't called and the reason is explicit: a LinkDemand failed.

MSDN is a bit confusing on this issue. On the one hand the Developer's Guide says this:

Linking occurs when your code is bound to a type reference, including function pointer references and method calls. If the caller does not have sufficient permission to link to your code, the link is not allowed and a runtime exception is thrown when the code is loaded and run.

This text says that the link is not allowed if the caller does not have sufficient permissions, that is, the caller will throw the exception and will not be executed. This part of MSDN is explicit and clear, however, many other places are not so clear. For example the MSDN documentation for SecurityAction says this for LinkDemand:

The immediate caller is required to have been granted the specified permission.

It is easy to interpret this as meaning when a method that has an attribute with LinkDemand is JIT compiled its immediate caller is checked. This is not the case, it is the other way round.

If you have already worked through my Fusion tutorial you'll remember that an assembly can be pre-jitted by the ngen utility. Normally JIT compilation will be applied on a method by method basis performed just before the first time that a method is called (the result is cached in memory and is used during later calls to the method). When you pre-jit the assembly (to create a native image) the JIT compiler will be run over every method in the assembly. If there are any link demands then these are performed against the policy defined at link time, which will be when ngen is called. This is one reason why you cannot pre-JIT an assembly on your machine and then install the native image on another machine: you cannot guarantee that the policies will be the same for both machines. Furthermore, even if you do generate the native image on the target machine the link demands will be affected by any changes in the security policy. If the new policy is a superset of the policy when the native image was generated then the runtime will ignore the native image and it will use the IL image and JIT compile it as normal. Thus if you change policy on a machine then you may need to ngen all of your native images.

.NET Version 3.0
Finally, it is important to realise that in version 3.0/2.0 of the runtime link demands are optimized away when the assembly has full trust because such demands should always succeed (full trust, should mean that). In version 1.1 of the framework a link demand will be performed, and this would mean that, for example, a link demand for strong name identity could fail if a full trust calling assembly did not have the strong name, which is a contradiction to what full trust means.

Note that when you make a call to a public (or protected) method on a public class in a strong named assembly then there will be an implicit link demand for FullTrust, more details are given in a later section.

To clean up this example, remove the FileIOPermission attribute from user.cs and the LinkDemand from lib.cs.

6.6 Requested Permissions

We established earlier on that by the time that an assembly is loaded the runtime would be assured that the assembly has not been tampered or corrupted (through validation), and that it does not attempt to anything that is unsafe (through verification). In addition, when a class is loaded, or a method is JIT compiled, or a method is run, a security check is performed against the permissions granted by policy to the assembly. If any of these checks fail a security exception is thrown. However, the assembly will be loaded and in some cases code in the assembly will have been executed. The code that has executed will have the correct permissions, or perform some low privileged action that does not require elevated permissions. In this case the code is most likely initializing code ready to perform the privileged operation at a later time. You could argue that if the eventual operation will fail there is no point performing the initialization.

.NET allows you to mark an assembly with the minimum permissions that must be granted for all code in the assembly to run. The implication is that if the assembly is not granted those permissions then some code in the assembly will not run, so the assembly indicates to the runtime that no code should run. Thus, the runtime will not load the assembly.

.NET Version 3.0
You can use the permcalc tool, described earlier, to determine the minimum permissions that your assembly needs to run.

There are three SecurityAction members that you can use to do this, and we have already seen one in use. RequestMinimum is used for permissions that your assembly must have to run, RequestOptional are the permissions that your assembly would like to have, but would work without, and RequestRefuse are the permissions that the assembly does not want. However, note that RequestOptional cannot be used without RequestMinimum requests because RequestOptional refuses all other permissions, which I will return to in a moment. When the assembly is loaded policy is applied to determine the permissions that the assembly is granted; the runtime then checks that the permission set contains the permissions that the assembly specifies are RequestMinimum. If this is the case, then the runtime calculates the union of the RequestMinimum and RequestOptional permissions and intersects this with the granted permissions (since it is an intersection it means that no extra permissions are granted in this way). Finally, the permissions that are RequestRefuse are removed from the permission set. This then gives the final permissions granted to the assembly.

For example, lib.dll could have this attribute:

[assembly:FileIOPermission(SecurityAction.RequestMinimum, Read=@"C:\SecureFolder")]

It is good practice to give an assembly a minimum permission set.

RequestOptional is interesting and curious. To test how such a request works create a new file called requested.cs:

using System;
using System.IO;
using System.Security;
using System.Security.Permissions;

[assembly: FileIOPermission(SecurityAction.RequestOptional, Read = @"c:\SecureFolder\public_data.txt")]
[assembly: FileIOPermission(SecurityAction.RequestOptional, Read = @"c:\SecureFolder\secret_data.txt")]

class App
{
   const string folder = @"C:\SecureFolder";

   static void Main()
   {
      try
      {
         string str = GetString("public_data.txt");
         Console.WriteLine(str);
         str = GetString("secret_data.txt");
         Console.WriteLine(str);
      }
      catch(SecurityException e)
      {
         Console.WriteLine(e.Message);
      }
   }

   static string GetString(string str)
   {
      string strFile = folder + @"\" + str;
      StreamReader sr = new StreamReader(strFile);
      return sr.ReadToEnd();
   }
}

This code accesses two files from the SecureFolder directory. Make sure that you have this folder and you have these two files. All the code does is open the files and print each file's contents to the console. Although this assembly appears to require access to these two files I have used RequestOptional, this is not a contradiction because the code handles the exception thrown if the permission is not granted, so you can argue that the application will runt to completion whether or not the permission is granted. If the exception handling code was not present then permission to access these files is required because the application will not complete if the application cannot access one of these files.

Compile this code and run it; you'll find that it will run as expected and the files will be accessed. Comment out both attributes, compile and run the code. Again you'll find that the code will run. This is understandable because the assembly is in the MyComputer zone and so it will have FullTrust.

Now change the code so that only the second of the request attributes (the one for secret_data.txt) is commented. Recompile and run the code, here are the results I get (v1.1):

this is public

Request for the permission of type System.Security.Permissions.FileIOPermission, mscorlib, Version=1.0.5000.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 failed.

The first line indicates that the file public_data.txt was read, which is understandable because you made an optional request that the read file permission is in the granted permission set; but the following lines are surprising. The name of the request would suggest that the request is optional: 'load this assembly whether or not this permission is granted'. However, this is not the case. The request means that the assembly would like the permission to be granted by policy (and you can only get the permission through policy), but it also indicates that all other permissions should be removed. Thus if you have this request, you indicate that this is the only permission you want.

Since RequestOptional will refuse all other permissions except the permission mentioned it really should have been called something like RequestExclusive.

If your code requires any more permissions then you should add a RequestOptional for that permission or a RequestMinimum.

In summary: if you do not use RequestOptional then the final permissions are the intersection of the RequestMinimum and the policy granted permissions, reduced by RequestRefuse. If you use RequestOptional then the final permissions are the union of the RequestMinimum and the RequestOptional permissions, reduced by RequestRefuse. However, note that if the assembly does not have the permission specified by RequestMinimum then the assembly will not be loaded; but the RequestOptional and RequestRefuse will not prevent the assembly from being loaded, instead, they affect the permission set used when the assembly executes.

6.7 Inheritance

In .NET the assembly is the security boundary, that is, permissions are given to an assembly and different assemblies will have different permissions. .NET allows a class in one assembly to derive from a class in another assembly. This means that the base class could perform some privileged operation and the assembly will have the evidence to be granted those permissions. An attacker could decide to exploit this by creating a class that derives from the privileged class, the reasoning goes like this: the base class assembly has the evidence that gives it the elevated permissions, but the attacker's assembly has evidence that gives it restricted permissions but the derived class can override methods of the base class that will have security demands and replace those methods with code that does not have the demands.

There are a couple of ways to prevent this. The first is the simplest: make the privileged class sealed so that it cannot be used as a base class. The second is to mark the class with an inheritance demand. This indicates that the current class requires the specified permissions and that any class that derives from it should also have the required permissions. Thus, if a derived class is declared in another assembly it means that the other assembly must have the required permission.

To do this, you use a permission attribute on the class and use a SecurityAction of InheritanceDemand. To illustrate this you will have to have three assemblies: a process assembly that uses a class in a library, and this class derives from another class in another library. Create a file called lib.cs and add this code:

using System;
using System.Security.Permissions;

public class PrintData
{
   protected string data = "secret value";
   [UIPermission(SecurityAction.Demand, Window=UIPermissionWindow.AllWindows)]
   public virtual void PrintString()
   {
      Console.WriteLine(data);
   }
}

This code initializes a protected field and returns this value in the PrintString method. A caller of this method must have an appropriate UI permission. (I have chosen [UIPermission] attribute because the method is accessing the UI through the console.) Now let's take this a step further and derive from the class. Create a new file called data.cs and add this code:

using System;
using System.Security.Permissions;

[assembly: UIPermission(SecurityAction.RequestRefuse, Window=UIPermissionWindow.AllWindows)]

public class PrintDataDerived : PrintData
{
   public override void printString()
   {
      Console.WriteLine(data);
   }
}

This code overrides the PrintString so that it does not have the permission demand. I have added the RequestRefuse to ensure that this assembly does not have the appropriate permission. The application code (app.cs) looks like this:

using System;

class App
{
   static void Main()
   {
      Console.WriteLine("App.Main");
      PrintDataDerived pd = new PrintDataDerived();
      pd.PrintString();
   }
}

This code creates our PrintDataDerived class and hence gets access to the PrintString method regardless of the permissions that data.dll has. Compile this code: note that the application must have references to both of the libraries because the constructor call in app.exe needs to have access to both the base class and the derived class.

csc /t:library lib.cs
csc /t:library data.cs /r:lib.dll
csc app.cs /r:data.dll /r:lib.dll

If you run the application you'll find that the data is printed on the command line. The secret is out!

Now change the attribute on PrintData.PrintString on the base class.

[UIPermission(SecurityAction.InheritanceDemand, Window=UIPermissionWindow.AllWindows)]
public virtual void PrintString()

This indicates that if the method is overridden in a derived class in another assembly then that assembly must have the required permission. Once you have compiled this, run the application, and you'll find that a security exception is thrown:

Unhandled Exception: System.Security.SecurityException: Request for the permission of type System.Security.Permissions.UIPermission, mscorlib, Version=1.0.5000.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 failed.
   at App.Main()

The state of the failed permission was:
<IPermission class="System.Security.Permissions.UIPermission, mscorlib, Version=1.0.5000.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"
   version="1"
   Window="AllWindows"/>

This shows that the exception is thrown before App.Main is called because the message in Main is not printed on the command line. The reason is that when Main is JIT compiled the JIT compiler must have access to both PrintDataDerived and its base class PrintData. The JIT compiler sees that PrintData has the InheritanceDemand and so it checks the permissions granted to data.dll which results in the exception.

.NET Version 3.0
The exception is more explicit in version 3.0/2.0 of the runtime (edited trace):

Unhandled Exception: System.Security.SecurityException: Request for the permission of type 'System.Security.Permissions.UIPermission, mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089' failed.
[snip]
The action that failed was:
   InheritanceDemand
The type of the first permission that failed was:
   System.Security.Permissions.UIPermission
The first permission that failed was:
<IPermission class="System.Security.Permissions.UIPermission, mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"
   version="1"
   Window="AllWindows"/>

A security attribute with InheritanceDemand can be applied to a class or a virtual method (it is meaningless on a non-virtual method). This attribute is transitive, that is, if it is applied to a base class (or a base class method) then it also applies to all classes in the inheritance chain, not just the immediately derived class.

6.8 Partially Trusted Code

Fully trusted code gets all permissions and code that is installed on your machine will get the FullTrust permission set. In addition, the framework assemblies get full trust, not only because it is installed on the local machine, but also because assemblies that have the Microsoft strong name (and assemblies with the ECMA strong name) also get full trust.

.NET Version 3.0
In version 3.0/2.0 of the framework, any assembly that is installed in the GAC will get full trust. There are several reasons for this. Firstly, the physical NTFS folders that are used by the GAC have Access Control Lists that restrict the operations of adding assemblies to the GAC to administrators. An administrator is a highly trusted person and so anything they install into the GAC should also be trusted. This is reflected in the special treatment of GAC assemblies, for example, once an assembly has been installed in the GAC strong name is never performed on it when it is loaded by another assembly.

Code that does not get full trust will have partial trust. Partial trust code will have to call framework code, and hence an assembly with full trust will be called by an assembly with partial trust.

In the first version of the .NET framework there was an implicit link demand for FullTrust on every public or private method on public types in a library with a strong name. This meant that an assembly with a strong name could only be called by assemblies that had full trust. If an assembly was downloaded from another machine then the assembly will have partial trust which is the default policy, however, since the assembly was downloaded from another machine it means that the assembly must have a strong name. In this case a partially trusted assembly will implicitly link demand that the assemblies that call it have full trust. The reason for this is to prevent a luring attack, that is, an assembly with partial trust calls a fully trusted assembly that performs some privileged action (perhaps code that should, but doesn't, make a demand, or maybe code that asserts a permission). This implicit link demand will prevent such an attack.

This implicit link demand was relaxed in version 1.1 with the assembly-level custom attribute (ie, not a security attribute) called [AllowPartiallyTrustedCallers]. Assemblies with this attribute can be called by partially trusted assemblies and the implicit link demand is not called. You should only apply this attribute after you have carefully checked that the code does not do something that is privileged and is not protected by a demand, and you should check that there are no asserts that can be exploited by the caller. This attribute does not relax code access security because any demands that you apply (link demands or stack-walk demands), or are applied by the code you call will still occur.

For example, use the code developed during the early parts of this page. The process file (app.cs) calls an intermediate library (user.cs), which then calls the final library (lib.cs). The process takes the name of a file to use, but if more than one parameters are passed on the command line then the process will deny the FileIOPermission effectively preventing the file access. We won't use that facility in this test. Instead, give the final assembly (lib.dll) a strong name:

[assembly: AssemblyKeyFile("key.snk")]
public class LibraryCode

(If you use C# 8.0 then use the command line switch /keyfile:key.snk.) Next, make the calling assembly have partial trust by adding the following:

[assembly: FileDialogPermission(SecurityAction.RequestRefuse, Unrestricted=true)]
public class UserCode

Compile all the code and run the process requesting a file. You should get a result like this (edited):

App.Main

Unhandled Exception: System.Security.SecurityException: Security error.
   at UserCode.GetString(String str)
   at App.Main(String[] args)

This is misleading, the code should work because no demand was made for the permission that was refused.

.NET Version 3.0
In version 3.0/2.0 of the framework, any assembly that is installed in the GAC will get full trust.

App.Main

Unhandled Exception: System.Security.SecurityException: That assembly does not allow partially trusted callers.
   at System.Security.CodeAccessSecurityEngine.ThrowSecurityException(Assembly asm, PermissionSet granted, PermissionSet refused, RuntimeMethodHandle rmh, SecurityAction action, Object demand, IPermission permThatFailed)
   at UserCode.GetString(String str)
   at App.Main(String[] args)
The action that failed was:
   LinkDemand

This specifically shows that a LinkDemand occurred and the exception indicates that partially trusted assemblies are not allowed to call the method with the link demand.

Remove the strong name on lib.dll (in C# 7.0 comment out the attribute). Now you will find that the code works. Clearly the problem is that the RequestRefuse makes user.dll a partially trusted assembly and this called a strong named assembly.

Replace the strong name and add the [AllowPartiallyTrustedCallers] assembly attribute:

using System.Security.Permissions;
using System.Security;

[assembly: AssemblyKeyFile("key.snk")]
[assembly: AllowPartiallyTrustedCallers]

Again, compile all of the code and then run the process requesting a file. This time you will find that the process will work as expected.

6.9 Transparencyy

Code Access Security gets a huge amount of power from the stack walk. When a demand is made, CAS walks the stack to ensure that all assemblies in the stack has the demanded permission. If one assembly does not have the permission then a security exception is thrown and the code that demanded the permission is not run. A link demand curtails this mechanism because this demand will only solicit a check on the caller to see if it has the demanded permission. The stack walk can be prevented entirely with an assert: the code that asserts the permission must have the permission, but code that calls it does not have to have the permission. In effect, link demands and asserts elevate the permissions of calling code, because code further up in the call stack will not have to have a required permission. In many cases this is by design, but it does mean that you have to check very carefully that you really do want partially trusted code to perform the operation.

Transparency is a feature of version 3.0/2.0 of the framework that attempts to resolve the problems outlined above. Transparent code voluntarily relinquishes permissions if its callers have less permissions. Transparent code follows these rules:

  • cannot assert for permissions and stop a stack walk
  • cannot satisfy a link demand, and so link demands are converted into normal stack-walk demands
  • cannot automatically use unverifiable code even if the code has SkipVerification, instead a stack walk demand is made for UnmanagedCode
  • cannot automatically call platform invoke code with [SuppressUnmanagedCode], instead a stack walk demand is made for UnmanagedCode

Code will never get more permissions through transparency, it will get the permissions granted by policy or the permissions of its caller, whichever is the less. An assembly is marked as transparent with the assembly level attribute, [SecurityTransparent].

Note that a transparent assembly can have [AllowPartiallyTrustedCallers] in which case lower-trust assemblies can call its types under that lower trust, or it may not. Similarly, a strong named assembly with [AllowPartiallyTrustedCallers] may be transparent (in which case the types will be called using the permissions of the caller) or it may not, in which case it can call Assert and its code will be called with the higher permissions of the strong named assembly. You have a range of choices.

For example, examine the following diagram:

Here, the process loads a partially trusted library assembly called caller. caller calls trans which then calls lower, both of which are fully trusted. lower.cs looks like this:

using System;
using System.Security;
using System.Security.Permissions;

[assembly: AllowPartiallyTrustedCallers]
public class Lower
{
   [SecurityPermission(SecurityAction.LinkDemand, Unrestricted=true)]
   public void CallLower()
   {
   }
}

You should not write code like this, there is a general rule that you should not expose a method that has a link demand because this demand will not cause a full stack walk, all it will do is cause a JIT check on the caller. In this case that means that trans will be checked for full trust, which will succeed. This assembly has a strong name because trans will have a strong name, and a strong named assembly can only call strong named assemblies. You'll see the reason for [AllowPartialTrustedCallers] later. trans.cs looks like this:

using System;
using System.Security;

[assembly: AllowPartiallyTrustedCallers]

public class Trans
{
   public void CallTrans()
   {
      Lower lower = new Lower();
      lower.CallLower();
   }
}

The only point to make about this is that it has [AllowPartialTrustedCallers] because its caller will be partially trusted. caller.cs is similarly simple:

using System;

public class Caller
{
   public void CallCaller()
   {
      Trans trans = new Trans();
      trans.CallTrans();
   }
}

Finally the process, app.cs:

using System;
using System.Security;

class App
{
   static void Main()
   {
      try
      {
         Caller caller = new Caller();
         caller.CallCaller();
      }
      catch (SecurityException se)
      {
         Console.WriteLine(se.ToString());
      }
   }
}

Since this is demonstrating a feature that is only available on .NET 3.0/2.0 we will apply the strong name through the command line. The three libraries require a strong name, so create a key file with sn -k key.snk and then use the following command lines:

csc /t:library lower.cs /keyfile:key.snk
csc /t:library trans.cs /keyfile:key.snk /r:lower.dll
csc /t:library caller.cs /keyfile:key.snk /r:trans.dll
csc app.cs /r:caller.dll

Run the process and you'll find that it will run with no exceptions. The reason for this is because all the assemblies will have full trust and so the link demand in lower will succeed because trans will have full trust. We want to simulate caller getting a partial trust by being downloaded from the intranet. To do this you'll use the technique shown in the Fusion workshop by installing the library within IIS on the local machine and then using a configuration file to specify a code base. The assembly that is downloaded must have a strong name, which is why caller has to be signed. First create a folder under the root web folder, then move the library to the new folder:

md \Inetpub\wwwroot\bin
move caller \Inetpub\wwwroot\bin

The easiest way to create the configuration file is to use the configuration tool. Under My Computer is Assemblies. Right click on this and select Add, then click on Other and browse for the application process. Click Open then OK. Now click on Configured Assemblies, right click and select Add. Select Choose and assembly from the list of assemblies this application uses, click on Choose Assembly and select caller and click on Select and click on Finished. The property dialog for the assembly will show, so select the Codebase tab. In the Requested Version type 0.0.0.0 then next to it in the URI column type http://localhost/bin/caller.dll; click on OK. In effect, this will create the following configuration file:

<?xml version="1.0"?>
<configuration>
   <runtime>
      <assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
         <dependentAssembly>
            <assemblyIdentity name="caller" publicKeyToken="caec91f465edcaba" />
            <publisherPolicy apply="yes" />
            <codeBase version="0.0.0.0" href="http://localhost/bin/caller.dll" />
         </dependentAssembly>
      </assemblyBinding>
   </runtime>
</configuration>

Now when you run the example the caller assembly will be downloaded from the website and put in the download cache. Run the code. You'll find that no exception has been thrown.

The problem here is that lower performs a link demand for full trust which is satisfied by trans, but caller only has partial trust and yet can call an assembly that can perform a privileged task. The trust of caller has been elevated by the link demand. Now change trans.cs to make the security transparent:

[assembly: AllowPartiallyTrustedCallers]
[assembly: SecurityTransparent]

Since the version of trans does not change, and neither does its public interface, you can simply compile trans.cs and you do not need to build caller.dll, nor app.exe. Now run the application, you'll find that the following exception will be thrown:

System.Security.SecurityException: Request for the permission of type 'System.Security.Permissions.SecurityPermission, mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089' failed.
   at Trans.CallTrans()
   at Caller.CallCaller()
   at App.Main()
The action that failed was:
   Demand

Notice that the problem is that a demand failed, not a link demand. The reason is that in response to the [SecurityTransparent] attribute the runtime converts all link demands to full demands.

Code that isn't transparent is called security critical and is the default for current versions of the framework. In some cases you may want some code in the assembly to be transparent and other code to be critical. The way to do this is a little odd. You start by marking the entire assembly with the [SecurityCritical] attribute that indicates that by default code is transparent (yes, that is not a typo) but that some of the code can be security critical. Contrast this with [SecurityTransparent] which indicates that the entire assembly has transparent code and contains no critical code. Edit trans.cs and change [SecurityTransparent] to [SecurityCritical], recompile and re-run the application to confirm that the link demand is converted to a demand.

Once you have marked an assembly with [[SecurityCritical] you use this same attribute on types or methods that you want to indicate as being security critical. If you want to mark an entire type as being critical, you do so with the [SecurityCritical] attribute, passing SecurityCriticalScope.Everything. Now edit the trans.cs to include the following method, and call this method in CallTrans (remember to add a using statement for System.Security.Permissions):

public void CallTrans()
{
   Critical();
   Lower lower = new Lower();
   lower.CallLower();
}

public void Critical()
{
   SecurityPermission sp = new SecurityPermission(PermissionState.Unrestricted);
   sp.Assert();
}

Run this code. You will find that you will get the following exception:

Unhandled Exception: System.InvalidOperationException: Cannot perform CAS Asserts in Security Transparent assemblies

This confirms that the runtime is enforcing the rules of transparent code. To make this code work add [SecurityCritical] to the Critical method. Compile the method and run the application and this time you'll find that the Critical method will be called with no exception, but you will still get an exception from CallLower (if you chose to, you can comment out the call to CallLower so that the exception will not be thrown.).

Transparent code has modified .NET visibility rules. Normally, critical code (that is, the default in current versions of .NET) can call members on other classes in the assembly that have the assembly metadata attribute (that is, marked with internal in C#) or protected and private methods on the same class. However, transparent code can only call critical code that are public. The framework does provide a solution to this issue, the [SecurityTreatAsSafe] attribute. When you apply this to a critical member with assembly accessibility you can call this member from a method marked as transparent code.

To test this out, make Critical a private method, compile trans.cs and run the application; you'll get the following method:

Unhandled Exception: System.MethodAccessException: Trans.Critical()
   at Trans.CallTrans()
   at Caller.CallCaller()
   at App.Main()

This exception shows that CallTrans does not have the access permission to call the Critical method: but they are methods within the same class! To resolve this issue add the [SecurityTreatAsSafe] to Critical; compile and run to confirm that the method can be called.

I hope that you enjoy this tutorial and value the knowledge that you will gain from it. I am always pleased to hear from people who use this tutorial (contact me). If you find this tutorial useful then please also email your comments to mvpga@microsoft.com.

Errata

If you see an error on this page, please contact me and I will fix the problem.

Page Seven

This page is (c) 2007 Richard Grimes, all rights reserved