Sunday, April 14, 2013

What's New in Code Access Security in .NET Framework 4.0 - Part 2

copyright: www.simple-talk.com


Having introduced us to the basics of the new Code Access Security Model available in .NET Framework 4.0, Matteo Slaviero explains how to use this powerful new system to implement fine-grained code security in ways where have never before been possible.

Introduction

This article is the second of a series of twowhich introduce how Code Access Security has changed in .NET Framework 4.0.  In the first article, we were introduced to the new .NET Framework 4.0 Level2 SecurityTransparence model and given some examples of its implementation. We’ve had a glimpse of the kind of changes which must be applied at assembly level in order to keep our code secure, and we have also seen that, with the new model, the host plays a principal role in defining what kind of resources can and cannot be accessed.
from what we saw previously, it seems that the new Level2 SecurityTransparence model is an all or nothing technology; If the assembly is fully trusted, all resources are available, and if it is only partially trusted, none of them are.
Thankfully, this is not the case, as we will see In this article. When protecting resources, in order to permit a more granular approach to security, an assembly can be marked with the Allow Partially Trusted Callers Assembly(APTCA) attribute. In this way, Security attributes become available at class or method level, bringing to more flexible configurations.  
Another important thing we will see is that, with the Level2 SecurityTransparence model, it is now possible to easily protect resources beyond the classical CAS resources defined in the .NET Framework, and we’ll call these new kinds of resources “custom resources”. Finally, we’ll finish this investigation into the new CAS implementation by describing how a new tool, called Security Annotator tool, can help us to discover the correct way to mix theSecurityCritical and the SecuritySafeCritical attributes to implement our desired security strategy. Without further ado, let’s get started.

The Allow Partially Trusted Callers Attribute (APTCA)

The Allow Partially Trusted Callers Attribute (APCTA) is an assembly-scoped attribute which changes how the assembly responds to the Level2 Security Transparence model.  When used, the following modifications take place:
  1. All the classes and methods inside the assembly became SecurityTransparent unless otherwise specified.
  2. To specify different behavior, the SecurityCritical or SecuritySafeCritical attributes can be added to desired class and/or method implementations.
The APCTA attribute is very similar to the SecurityTransparent attribute used in the previous article, which we used to force an assembly to run as SecurityTransparent. As a result, when the caller assembly tried to accessSecurityCritical code, an exception was thrown (remember the PermissionSet property?) As mentioned, the main differences among the two attributes lies in the fact that, when the APCTA attribute replaces theSecurityTransparent attribute, we are able to directly specify security settings for each class or method in an assembly through the use of SecurityCritical and/or SecuritySafeCritical attributes. If the assembly were marked asSecurityTransparent, these two attributes would have no effect, due to the fact that the SecurityTransparent attribute only works at the assembly level, and no lower.
So, with the APCTA attribute we are able to:
  1. Elevate the permissions of an individual class or method, transforming it into a SecuritySafeCritical class or method. By doing so, we grant the class or method all permissions to access protected resources (asSecurityCritical code) while it remains visible to SecurityTransparent code. Essentially, we create a sort of bridge between SecurityTransparent and SecurityCritical code.
  2. Keep some classes or methods protected from the partially trusted assembly by marking them asSecurityCritical.
As we will soon see, these two features remove the supposed “all or nothing” behavior of the SecurityTransparentattribute. To prove this, we’ll start by reusing the example provided in the previous article, with some modifications:
[assembly:AllowPartiallyTrustedCallers()]
      
namespace CassAssemblyInfo
  {
    
///
    /// Demo class
    ///

        
public class AssemblyInfo
    {
      
///
      /// Write to the console the security settings of the assembly
      ///

          
public  string GetCasSecurityAttributes()
      
{
        
//gets the reference to the current assembly
            
Assembly a Assembly.GetExecutingAssembly();
            
StringBuilder sb = new StringBuilder();
        
//show the transparence level
            
sb.AppendFormat("Security Rule Set: {0} \n\n"a.SecurityRuleSet);
        
//show if it is full trusted
            
sb.AppendFormat("Is Fully Trusted: {0} \n\n"a.IsFullyTrusted);
        
//get the type for the main class of the assembly
            
Type t a.GetType("CasAssemblyInfo.AssemblyInfo");
        
//show if the class is Critical, Transparent or SafeCritical
            
sb.AppendFormat("Class IsSecurityCritical: {0} \n"t.IsSecurityCritical);
            
sb.AppendFormat("Class IsSecuritySafeCritical: {0} \n",
            
t.IsSecuritySafeCritical);
            
sb.AppendFormat("Class IsSecurityTransparent: {0} \n",
            
t.IsSecurityTransparent);
        
//get the MethodInfo object of the current method             
            
MethodInfo m t.GetMethod("GetCasSecurityAttributes");
        
//show if the current method is Critical, Transparent or SafeCritical
            
sb.AppendFormat("Method IsSecurityCritical: {0} \n".IsSecurityCritical);
            
sb.AppendFormat("Method IsSecuritySafeCritical: {0} \n",
            
m.IsSecuritySafeCritical);
            
sb.AppendFormat("Method IsSecurityTransparent: {0} \n",
            
m.IsSecurityTransparent);
            
try
        
{
              sb.AppendFormat
("\nPermissions Count: {0} \n"a.PermissionSet.Count);
        
}
            
catch (Exception ex)
        
{
              sb.AppendFormat
("\nError while trying to get the Permission Count:
                              {0} \n"
ex.Message);
        
}
            
return sb.ToString();
      
}
    }
  }  
With respect to the previous version of this dll library, we have inserted the following code prior to the namespace declaration:
[assembly:AllowPartiallyTrustedCallers()]
… which states that our assembly is now an APCTA assembly. We have also added the following lines of code:
//get the MethodInfo object of the current method MethodInfo m t.GetMethod("GetCasSecurityAttributes");//show if the current method is Critical, Transparent or SafeCriticalsb.AppendFormat("Method IsSecurityCritical: {0} \n"m.IsSecurityCritical);sb.AppendFormat("Method IsSecuritySafeCritical: {0} \n", m.IsSecuritySafeCritical);sb.AppendFormat("Method IsSecurityTransparent: {0} \n"m.IsSecurityTransparent);
… which allow us to see if the GetCasSecurityAttributes method is SecurityCriticalSecuritySafeCritical orSecurityTransparent. By running the console application which we used to consume the previous assembly (and which you can download at the top of this article), we obtain the following output:
Figure 1. The console output of our modified demonstration program
Looking at figure 1, we can quickly see that:
  1. The assembly is running on the local computer,
  2. The assembly is fully trusted, but the AssemblyInfo class is transparent, and …
  3. Even the GetCasSecurityAttributesmethod is transparent;
  4. When trying to get the PermissionSet.Count value, we get an exception which reminds us that the assembly is marked with the APTCA attribute, so all of its classes and methods are SecurityTransparen, and cannot call SecurityCritical code.
At this point, it seems that we’re observing the same behavior we would have obtained by using theSecurityTransparent assembly attribute, so where is the difference? The difference lies in the fact that the APTCA attribute allow us to define the Security level of the code in a more granular way. With it, we can directly modify the Security level of the GetCasSecurityAttributes method, making it SecurityCritical or SecuritySafeCritical. At this point, we’ll choose to set it as SecurityCritical:
///
/// Write to the console the security settings of the assembly
///

    
[SecurityCrtitical()]
    
public string GetCasSecurityAttributes()    {
… and by running the .exe a second time, we obtain the following result:
Figure 2. Running the demonstration program with fine-grained control of method security level in place.
As you can see, the exception message has disappeared because, even if the class is SecurityTransparent, the underlying method is now SecurityCritical and can execute the PermissionSet property’s accessor. Just to demonstrate the difference between the APTCA and SecurityTransparent attributes, if we replace the following line:
[assembly:AllowPartiallyTrustedCallers()]
… with:
[assembly:SecurityTransparent()]
which we used in Part I of this short series (as I mentioned at the start of this section), we get  a familiar output:
Figure 3. Running the demonstration program with assembly-level SecurityTransparency in place, and no fine-grained control.
As expected, the SecurityCritical attribute on the GetCasSecurityAttributes now has no effect, and the method remains SecurityTransparent.

Custom Resources

Despite the simplicity of the previous example, SecurityCritical and SecuritySafeCritical attributes can be mixed together in APCTA assemblies in very different ways to set up custom protection strategies. Rather than always invoking the same classical protected resources of a system, let’s look at an example that shows how the Level2 Security Transparence model can be used to protect any type of resource we want, thus going beyond the legacy CAS Policy model. Consider the following CasWriter class, defined inside a demo assembly namedCasWriter.dll:
[assembly: AllowPartiallyTrustedCallers()]
      
namespace CasWriterDemo
  {
    
///
    /// Write sentences
    ///

        
public class CasWriter
    {
      
///
      /// Write a sentence to console
      ///

      ///
          
public void WriteCustomSentence(string text)
      
{
            Console.WriteLine
(text "\n");
      
}
      
///
      /// Write a sentence to console
      ///

      ///
          
public void WriteDefaultSentence(int index)
      
{
            
switch (index)
        
{
              
case 0:
              
WriteCustomSentence("homo homini lupus");
              
break;
              
case 1:
              
WriteCustomSentence("melius abundare quam deficere");
              
break;
              
case 2:
              
WriteCustomSentence("audaces fortuna iuvat");
              
break;
        
}
      }
      
///
      /// Get the Security status of each method developed
      ///

          
public string GetMethodsSecurityStatus()
      
{
        
//get the MethodInfo of each method
            
MethodInfo[] infos GetType().GetMethods();
            
StringBuilder sb = new StringBuilder();
            
foreach (MethodInfo m in infos)
        
{
              
if (m.ReturnType != typeof(void)) continue;
              
sb.Append("\n");
              
sb.Append(m.Name ": ");
              
if (m.IsSecurityCritical)
          
{
                sb.Append
("SecurityCritical\n");
          
}
              
else if (m.IsSecuritySafeCritical)
          
{
                sb.Append
("SecuritySafeCritical\n");
          
}
              
else if (m.IsSecurityTransparent)
          
{
                sb.Append
("SecurityTransparent\n");
          
}
        }
            
return sb.Append("\n\n").ToString();
      
}
    }
  }
The class has the following three static methods:
  • WriteCustomSentence(string text): this method writes a sentence, passed to it as input, to the console.
  • WriteDefaultSentence(int index): This method writes a fixed sentence to the console, selecting from among three possible values. The input parameter states which sentence to write.
  • string GetMethodsSecurityStatus(): This method returns, as a string, the Security status of the two previous methods.
Now we write a console application (CasWriterDemo.exe) that consumes the previous methods:
[assembly:SecurityTransparent()]
      
namespace CasWriterDemo
  {
        
class Program
    {
          
static void Main(string[] args)
      
{
            CasWriter writer 
= new CasWriter();
            
Console.WriteLine(writer.GetMethodsSecurityStatus());
            
try
        
{
              Console.Write
("Custom Sentence: ");
              
writer.WriteCustomSentence("Barba non facit philosophum");
        
}
            
catch (Exception ex)
        
{
              Console.WriteLine
("\n\n" ex.Message "\n\n" );
        
}
            
try
        
{
              Console.Write
("Default Sentence: ");
              
writer.WriteDefaultSentence(new Random().Next(02));
        
}
            
catch (Exception ex)
        
{
              Console.WriteLine
("\n\n" ex.Message);
        
}
            Console.ReadKey
();
      
}
    }
  }
We have marked the CasWriterDemo.exe assembly as SecurityTransparent because we want to test what happens when the CasWriter.dll assembly is called by partially trusted code.
Given that the CasWriter.dll is marked with the APTCA attribute, all the code inside it is SecurityTransparent, and so we should expect that the application will run correctly. We are in a situation where SecurityTransparent code calls other SecurityTransparent code, and the Level2 SecurityTransparent model certainly allows this. Running the application, we obtain the following result:
Figure 4. Testing the new demonstration CASWriterDemo program.
We see from figure 4 that, as expected, the two methods are both SecurityTransparent and the sentences are correctly written to the console. Now suppose that we want to prevent partially trusted code from being able to write a custom sentence, and only leave it with the ability to write a default sentence selected from an index. In this situation, the WriteCustomSentence therefore becomes our protected resource. To achieve this, we need to:
  1. Mark the WriteCustomSentence method as SecurityCritical, so that SecurityTransparent code cannot access it.
  2. Mark the WriteDefaultSentence method as SecuritySafeCritical.
This second modification should sound a little strange; after all, the WriteDefaultSentence method is alreadySecurityTransparent, and so it can be accessed by other SecurityTransparent code. Our executable is alsoSecurityTransparent, so it can also access the SecurityTransparent WriteDefaultSentence method. However, you should note that the WriteDefaultSentence method uses the WriteCustomSentence method after a sentence has been selected.
The overall effect is that the SecurityTransparent WriteDefaultSentence method now calls a SecurityCriticalmethod: WriteCustomSentence. So, if we try to call WriteDefaultSentence from SecurityTransparent code, we’ll get an exception; let’s try to run our .exe without the second modification:
Figure 5. Running the demonstration .exe without marking the WriteDefaultSentence method asSecuritySafeCritical.
As we can see, the WriteCustomSentence method is now SecurityCritical, and cannot be accessed bySecurityTransparent code. You can find the exception associated with this behavior after the “Custom Sentence:” line in figure 5. To quickly recap, the WriteDefaultSentence method is SecurityTransparent, so the main method of the .exe can access it, but when WriteDefaultSentence tries to use the WriteCustomSentence method to write the output to the console, an exception occurs, as you can see after the “Default Sentence:” line in figure 5.
So, analyzing the each step involved in this demonstration, we have:
  1. The Main method calls WriteCustomSentence, which leads to an exception. A SecurityTransparent method cannot call a SecurityCritical method.
  2. (a) The Main method calls WriteDefaultSentence, which is successful. A SecurityTransparent method can call a SecurityTransparent method.
  1. (b) The WriteDefaultSentence method calls WriteCustomSentence, which leads to an exception. ASecurityTransparent method cannot call a SecurityCritical method.
If, as suggested in the second modification above, we mark the WriteDefaultSentence method asSecuritySafeCritical, we solve this potential problem. SecuritySafeCritical code is designed to act as a permission bridge, in that it can be called by SecurityTransparent code and it can, in turn, call SecurityCritical code. So, with this modification, we will create a bridge between the SecurityTransparent code (the Main method) and theSecurityCritical code (the WriteCustomSentence method). If we now run our .exe, we see this result:
Figure 6. Using SecuritySafeCritical code to bridge the permission gap between the Main method and the WriteCustomSentence method.
… Which is exactly the result we want to achieve. We have protected the WriteCustomSentence method (our custom resource) from the partially trusted assembly (which is SecurityTransparent code) while allowing the same assembly to access the WriteDefaultSentence method!

Inheritance and Override Rules

We’ve seen how resource protection works when one method calls another, but the security checks performed in these situations are not enough to achieve a complete set of security instruments. For example, we know that object oriented languages, such as those provided with .NET, allow inheritance and the overriding of classes, methods and types. So we need to protect those same objects with a derived version of the same inheritance structure. The new .NET Framework 4.0 Code Access Security system manages this need by using the following two rules:
  1. Derived types must be at least as restrictive as base types.
  2. Derived methods cannot modify the accessibility of their base methods.
Derived methods are SecurityTransparent by default and so, if the base method is not SecurityTransparent, the derived must be marked appropriately to prevent violating the first inheritance rule.
To demonstrate the two rules, we’ll write a CasWriter2 class that inherits from the CasWriter class, and will have a WriteCustomSentence method that inherits from the base WriteCustomSentence method (which we mark as virtual). The code for this will be:
namespace CasWriterDemo
  {
    
///
    /// Write sentences
    ///

        
public class CasWriter2 CasWriter
    {
      
///
      ///
      ///

      ///
          
public override void WriteCustomSentence(string text)
      
{
            base.WriteCustomSentence
(text);
      
}
    }
  }
 To demonstrate the first inheritance and override rule, we’ll set the CasWriter class as SecurityCritical:
///
  /// Write sentences
  ///

      
[SecurityCritical()]
      
public class CasWriter
… and, in the main method of the CasWriterDemo.exe assembly, we’ll substitute the CasWriter object with theCasWriter2 object:
static void Main(string[] args)
  
{
        CasWriter writer 
= new CasWriter2();
        
Console.WriteLine(writer.GetMethodsSecurityStatus());
So, we’ve tried to derived the SecurityTransparent CasWriter2 class from a SecurityCritical CasWriter class, but, with the first rule in place, this is not possible because we have tried to create a SecurityTransparent (low protected) type from a SecurityCritical (high protected) type. As a result, if we run our .exe we obtain:
Figure 7. An exception thrown from trying to derive a SecurityTransparent type from a SecurityCritical one.
As expected, a type load exception is thrown, stating that an inheritance security rule has been violated. Notice that the exe stop working as well; because the exception is detected when the assembly tries to load the CasWriter2 type, it’s not possible to handle the exception through code.
To make this as clear as possible, the following table sums up the inheritance rules for classes:
Base Class
Derived Class  
Transparent
Transparent
Transparent
SafeCritical
Transparent
Critical
SafeCritical
SafeCritical
SafeCritical
Critical
Critical
Critical
To demonstrate the second rule, we’ll remove the SecurityCritical attribute from the CasWriter class. In this case, the first rules is no longer violated as both classes are SecurityTransparent. However, there is a second issue to consider; we are trying to override SecurityCritical code (the base WriteCustomSentence) with what is nowSecurityTransparent code (the derived WriteCustomSentence), which is not allowed by the second rule. Remember that the derived method is SecurityTransparent by default, and we haven’t specified any other security attribute for it. Running the .exe, we therefore get:
Figure 8. Our new demonstration program violating the second Inheritance and Override CAS rule, and throwing an exception.
As expected, an exception is thrown saying that there is a violation on a security rule when overriding theWriteCustomSentence. I’ll leave it to you to mark the WriteCustomSentence method of the CasWriter2 class asSecurityCritical and verify that, in this last situation, all goes well. To try it, you can download the supporting zip file at the top of the page, which contains the entire example provided in this article. Before we finish looking at methods, let’s just confirm their inheritance rules:
Base Method
Derived Method 
Transparent
Transparent
Transparent
SafeCritical
SafeCritical
Transaprent
SafeCritical
SafeCritical
Critical
Critical
I’ll end this section by pointing that the same rules apply when we develop a class that implements an interface. The implemented method must respect the inheritance rules (the same as those in the table above) in relation to the attributes set for the interface members.  

The .NET Security Annotator Tool

In the previous example we saw how to mix the SecurityCritical and SecuritySafeCritical attributes to protect theWriteCustomSentence method from partially trusted code. Admittedly, that example was very easy and to set the correct attributes was a trivial task. Things are not so easy with more complex assemblies, and there is a risk of creating confusion as you try and unravel the security dependencies. This is precisely why Microsoft’s .NET Framework 4.0 provides a very useful tool, named .NET Security Annotator (SecAnnotate.exe), which can help developers to identify the correct attributes to use in theirs code. You can find it in the Microsoft Windows SDK version 7.0A, under the \bin\NETFX 4.0 Tools folder.
The SecAnnotate.exe tool browses an assembly to identify what modifications have to be made to avoid security exceptions when the assembly runs, and checks are made in several passes. In the first pass, the tool discovers what modifications must be performed on the assembly as it initially exists. If it detects that some code must be marked as SecurityCritical or SecuritySafeCritical, it performs a second pass, applying, at run time, the modifications discovered to be necessary in the first pass. The tool then makes a third pass, and if it detects thatnew modifications are needed as a result of the previous changes, it then makes these modifications in a fourth pass . The process repeats itself (scan – modify – scan – modify…), and ends when the tool doesn’t find anything left to change. At the end of the execution, SecAnnotation.exe generates an output report that contains the result of the analysis performed in each step.
There are two things you should bear in mind:
  1. If SecAnnotate.exe discovers that a method should be marked as either SecurityCritical orSecuritySafeCritical, it prefers the first attribute, it being a more secure option. Sometimes developers need to manually select the SecuritySafeCritical attribute instead of SecurityCritical, and this could generate problems during the following passes. We will see an example of what I mean in a moment. To avoid this, the SecAnnotate.exe tool comes with the /p:  command-line switch, which can be used to set the maximum number of passes that can be performed prior to stopping the execution and generating the output. In terms of a more tightly-controlled process which allows you to take direct and fine-grained control of your code security, It would be better to:

    1. run the tool with the /p:1 command-line switch so that, at each pass, a new output is generated;
    2. manually perform the desired modifications to the assembly on the basis of that output,
    3. recompile your assembly and
    4. re-run SecAnnotate.exe with the /p:1 command-line switch to obtain a new output, and repeat. The procedure ends when no other modifications are needed, as when you allowSecAnnotate.exe to run without human intervention.
  2. To perform the check, the SecAnnotate.exe tool has to verify how the assembly’s methods behave in relation to the methods that they call. Usually, assemblies use the .NET Framework base classes, and so checks regarding the attributes needed to call their methods can be performed.  If an assembly uses other (third party or your own) assemblies, different from those present in .NET Framework base classes (and, in general, from those contained in the Global Assembly Cache), the path to them must be specified Using  the /d:  command-line switch.
With all that in mind, if we return to our CasWriter.dll assembly, remove the security attributes which we set in the previous section and launch the following command from the console:
SecAnnotate.exe CasWriter.dll
…we will obtain the following output:
Figure 9. Running SecAnnotate.exe against our demonstration program.
The tool doesn’t find anything to annotate, because the assembly is made up of SecurityTransparent code that calls other SecurityTransparent code (specifically, those code of the .NET Framework base classes which we used).
But, if we want to protect the WriteCustomSentence method by marking it as SecurityCritical (as we did earlier), and we launch the previous command on the newly compiled assembly, we get a different result:
Figure 10. Getting SecAnnotate.exe to do some work on our demo .exe.
We can see that the tool found three necessary annotation and the jobs were completed in two passes.  Moreover, it generated a detailed report titles TransparencyAnnotations.xml (we can override the name with the /o:command-line switch), the contents of which looks like this:
Figure 11. The contents of TransparencyAnnotations.xml
We can quickly see that the SecAnnotation.exe tool made an annotation in the WriteDefaultSentence method, for three identical reasons. The rule violated is given by TransparentMethodsMustNotReferenceCriticalCode, as we expected. The three reasons are all identical because the SecurityTransparent WriteDefaultSentence method contains three calls to the SecurityCritical WriteCustomSentence method (inside the switch block of code).
Another important aspect of this report to take note of is that the tool suggests four different ways to avoid the annotation:
  1. WriteDefaultSentence must become SecurityCritical
  2. WriteDefaultSentence must become SecuritySafeCritical
  3. WriteCustomSentence must become SecuritySafeCritical
  4. WriteCustomSentence must become SecurityTransparent
If they could all, separately, entirely resolve the problem, we know that, for the goals we have in mind, the only available solution is to make WriteDefaultSentence SecuritySafeCritical, in order to grant access to it fromSecurityTransparent code, while leaving the WriteCustomSentence method protected.  We also know that the tool, after the first pass, applies the rule that it consider to be preferable as it performs its second pass, and that it prefers changes that bring about the best possible security situation.
In our example it might have chosen option number 1 and, as a result, the assembly would become fullySecurityCritical, and thus completely protected from SecurityTransparent code. This represents the more secure situation. However, we know that, for our goals, the solution that we need is number 2. Indeed, applying option number 1 instead of number 2 could bring about another round of checks that could have a totally different output, sending SecAnnotate.exe further and further away from our desired outcome. So, as I mentioned, we should probably use the tools with the /p:1 command-line switch, and make the changes manually.
We’ll end this section by running the SecAnnotatio.exe tool against the console application, just to see what happens. To do so, we need to specify the location of the CasWriter.dll assembly from which CasWriterDemo.exedepends. As seen earlier, to do so, we must use the /d command-line switch; assuming that the CasWriter.dll is contained in root of the D:\ drive, we need to run the following command:
SecAnnotate.exe /d:D:\ CasWriterDemo.exe
The output that we get is seen below:
Figure 12. Running SecAnnotate.exe against our console application.
We can quickly see that the tool has found only one annotation, which we can see in the accompanying report:
Figure 13. The SecAnnotate.exe report for our console application.
The annotation is related to the Main method, which is SecurityTransparent and is trying to accessSecurityCritical code. Note that this is not an exception, but rather the behavior that we wanted to implement in ourCasWriter.dll to protect WriteCustomSentence from SecurityTransparent code (such as the Main method). So, when using this tool, analyze the output generated with great care and attention.
Beyond all this, there is one last important point to consider. We have written two assemblies,CasWriterDemo.exe and its related CasWriter.dll assemblies, and if we want to use the SecAnnotation.exe tool to check the CAS rules for the entire solution, we simply cannot do it in a single step. In the last example, we analyzed the CasWriterDemo.exe assembly by specifying its referenced CasWriter.dll assembly. However, from the output that we obtained, it's clear that the checks were only made for the CasWriterDemo.exe assembly and how it behaves in relation to its dependent CasWriter.dll assembly - No check was made for the CasWriter.dllassembly (If you didn’t notice it at the time, the three annotations related to the CasWriter.dll assembly are not present in the later report).
The point I’m trying to make is that, if you want to check your entire solution, you need to perform the check for each assembly, one at a time.  Unless you have specific security goals in mind, the best way seem to be to check the dependent assembly first, and then its immediate callers.

Conclusion

We’ve covered a huge amount of ground in this article (much of which was set up and based on material in my previous CAS article, which you should read if you haven’t done so already). To start with, we’ve seen how to useAPTCASecurityCritical and SecuritySafeCritical attributes to set up a protection strategy when an assembly must be callable by partially trusted code. We have also seen that the work that has to be done to implement security strategies within this new model is not as easy as we might like, but, fortunately, the new SecAnnotation.exe tool can give us a great head start.
Let’s end this article with some reflections about how to set up a successful protection strategy when working with the new Level2 SecurityTransparence model. We can define two different situations  which we are likely to find ourselves in:
  1. Our assembly must protect the underlying dependent assemblies (for example, the .NET Framework base classes). In this case, we need to maximize the amount of SecurityCritical code. In this way, we are able to protect all the dependent assemblies from partially trusted (SecurityTransparent) assemblies with an impenetrable “wall”.
  2. Our assembly will be protected by its potential callers. If our assembly needs to be accessed by partially trusted assembly, we need to maximize the amount of SecurityTransparent code. If the assembly doesn’t make use of protected resources, we only need to mark it as SecurityTransparent, otherwise, we must use the APTCA attributes and try, method by method, to maximize SecurityTransparent code and minimizeSecuritySafeCritical code.
Of course, it isn’t so easy to guess the entire spectrum of possible scenarios and verify if the two rules above are applicable to all of them, so those two must be considered as general guidelines instead. In any case, we shouldn’t enter too deeply into this particular subject at this stage; partly because it is too complex to analyze succinctly, and partly because the Level2 SecurityTransparence model is, at the time of writing, a very new, and not yet sufficiently documented technology. I’d suggest that you follow the .NET Security Blog, which will surely, over time, bring you up-to-date about the new Level2 SecurityTransparence model and its implementations.

What's New in Code Access Security in .NET Framework 4.0 - Part I

copyright: www.simple-talk.com


The Code Access Security model has been completely redesigned in the .NET Framework 4.0, to the point where CAS policies have been completely removed, and everything now works through Level2 Security Transparency. Confused? Not for long. Matteo Slaviero, a .NET security expert,  brings us up to speed.
As many of you probably already know, Code Access Security (or CAS, for short) is a security technology developed to provide the ability to protect system resources when a .NET assembly is executed. Such system resources could be: local files, files on a remote file system, registry keys, databases, printers and so on. Unfettered access to these types of resources can lead to potential security risks, as malicious code could perform damaging operations on them, such as removing critical files, modifying registry keys, or deleting data stored in databases to suggest just a few.
Thankfully, CAS can mitigate those security risks by giving us fine-grained control over which resources can be accessed, and who can access them. Unfortunately, using CAS features in .NET Framework is not always so easy, and a lot of work is typically needed to implement them in a correct manner. For this reason, Microsoft has changed CAS a lot in .NET Framework 4.0, with the goal of making it easier to implement and manage.
This article is the first of two, in which we will introduce the new Code Access Security model of .NET Framework 4.0 and how it changes the way in which software has to be developed when security is a fundamental priority. We will see the new Security Transparence model of .NET Framework 4.0, and how it was designed to eliminate the need for the CAS Policy model, which was used until .NET Framework 3.5. We will see how important the host in which protected code runs has become important, and how the host and assemblies (whether they’re your own or 3rd party) interact with each other to control protected resources. In the next article we will go deeper into the assembly’s structure, focusing our attention on protected assemblies’ methods and how to implement a more sophisticate, yet granular, protection strategy for them.
We’ll start this article by describing CAS before the .NET 4.0 Framework, with the intent of  making the changes applied in the new model as clear as possible. Next, we will cover the fundamentals of the new CAS system, providing, at the very least, what you need to get started with it.  Finally, we will analyze how hosts change the way in which CAS has to be applied, and how to sandbox untrusted assemblies to prevent possible malicious code from running. To help describe the new security model and how it is likely to affect our assemblies, we’ll run a few demonstrations as examples, and you can find them in the supporting documents at the top of this article.

Code Access Security Before .NET Framework 4.0

Here’s a very brief description of how, prior to .NET Framework 4.0, Code Access Security allowed developers and system administrator to protect resources by defining:
  • A set of Permissions that an assembly or a method must have in order to access critical (from a security perspective) resources. A full list of permissions defined on the .NET Framework can be found over at MSDN.
  • PermissionSets, which are collections of two or more Permissions.
  • An assembly property called Evidence, which acts as a sort of combination of the identity of the assembly in relation to the zone from which it came from (current machine, intranet etc.), the identity of the assembly’s publisher (obtained using a digital certificate to sign the assembly), and identity of the assembly itself (its strong name or its hash) or simply its location.
  • A set of Code Groups, which contain all the assemblies that possess specific Evidence.  Every Code Grouphas a specific PermissionSet assigned.
While Evidence is assigned by the run-time every time an assembly executes, Code Groups and the related PermissionSet are stored inside the machine, and they can be modified or newly created by system administrators. Developers are able to interact with the permission assigned to their assembly in one of two ways:
  1. Declaratively: by using a set of attributes that can be assigned to an assembly or to its classes and/or methods (properties accessors included).
  2. Imperatively: by using a set of classes inside an assembly’s methods.
For example, to control the access to the file system, FileIOPermissionAttribute or an instance of theFileIOPermission class can be used. The first is used declaratively, the second imperatively.
To check permissions assigned to their code, developers can invoke methods such as Demand(), LinkDemand() or InheritanceDemand() (the last two only declaratively), or override them by using methods such as  Assert() ,Deny() or PermitOnly().
When an assembly loads, the .NET Framework run-time checks its Evidence and assigns to it only the specific permissions allowed for its Code Group. If the assembly has all the permissions granted, it is said to be fully trusted, otherwise it is said to be partially trusted. That’s all  you need to know about it for now, because this system is fiendishly difficult to use effectively, and the new CAS security model in the .NET Framework 4.0 completely replaces it.

What’s new in the .NET Framework 4.0

The changes in .NET 4.0 are largely reactive, in that they address existing problems rather than implementing new features. Over the years, the CAS model, as implemented in the pre-4.0 version of the .NET Framework, has revealed some problems which are not so trivial to solve. In particular:
All the work that must be done to setup a successful CAS Policy, that is, all the work needed to define the right PermissionSet and Code Groups for each specific machine. This discouraged a lot of administrators from implementing the technology on their systems.
  • When a specific application needs to be moved onto a different system, the different security policy applied to this new system could cause malfunctions in the application itself. For example, if an executable file was able to run properly on the developer’s machine, sometimes, when it needed to be moved on to a production server or a remote share, the same executable could suddenly stop working.
  • When developing code, it was not so easy to set up the CAS features for the assembly. This was because administrators needed to set up very different CAS policies on their machines, and the developer had to keep in mind all the possible locations their assemblies might be run, as well as what the possibly PermissionSets were. Developers had no way of knowing the administrators decisions in advance.
  • CAS policies were very useful when administrators needed to control what software could and could not do, but CAS policies had no effect at all on unmanaged code.
So, the Microsoft .NET Security Team decided to rebuild Code Access Security from the ground up. The main differences can be boiled down to:
  1. All of the CAS policy system has been completely removed. Decisions about what permissions can be granted to an assembly are now taken by the host in which the assembly runs. This eliminates all problems related to CAS Policies setup.
  2. The enforcement mechanism, that is, the mechanism used by the run-time to force an assembly to execute only code that has permission to execute, has been replaced by the Security Transparent model. This simplifies a lot of the work needed to set the access conditions for the resources that the assembly has to use.
The Security Transparent model was introduced in the .NET Framework 2.0 but, until the 4.0 version, it could only be used at the assembly level, and it was mainly used to prevent security transparent code from elevating privileges. In fact, Security transparent code could not even use the Assert method. In pre-4.0 versions of the .NET Framework, transparency could not be used for enforcement, as enforcement was handled by the CAS Policy system; this behavior is now called Level1 Security Transparency. With .NET Framework 4.0, these limitations have been removed and the Security Transparent model became the standard way to protect resources. The new model has been called Level2 Security Transparency, and now we’ll take a look at how it works.

The Level2 Security Transparent Model

Level2 security transparent model divides all code in three categories:  SecurityCritical code,SecurityTransparent code and SecuritySafeCritical code. Let’s see in detail what they can and cannot do:
  • SecurityCritical:
    SecurityCritical code is full trusted. Such code can be called by other SecurityCritical code or by SecuritySafeCritical code, but cannot be called by SecurityTransparent code.
  • SecurityTransparent:
    SecurityTransparent code has limited privileges on the system resources, and has no access to SecurityCritical code. Moreover, it cannot call native code nor elevate permissions.
  • SecuritySafeCritical:
    SecuritySafeCritical code provides a sort of bridge between SecurityTransparent code and SecurityCritical code. In fact, SecurityTransparent code can call SecuritySafeCritical code, which in turn can call SecurityCritical code. SecuritySafeCritical code is considered fully trusted, and has the same permission of the SecurityCritical code.
Note that due to the fact that SecurityTransparent code cannot call SecurityCritical code, Level2 Security Transparence became an enforcement mechanism.
Figure 1. The relationships between the three types of security categories
Let’s start with some examples to demonstrate the model. Say we start writing a simple console application that helps us to explore the security settings of an assembly which we write. To do this, we use the following new properties of .NET Framework 4.0:
  • Assembly.SecurityRuleSet:
    This states which security rule is used on our assembly: Level1 Security Transparence or Level2 Security Transparence.
  • Assembly.IsFullTrusted:
    If true, the assembly is executing as a fully trusted assembly, and all of its methods are SecurityCritical. If false, the assembly is partially trusted, and all its methods are SecurityTransparent.
  • Type.IsSecurityCritical:
    If true, the object is running as SecurityCritical code.
  • Type.IsSecuritySafeCritical:
    If true, the object is running as SecuritySafeCritical code.
  • Type.IsSecurityTransparent:
    If true, the object is running as SecurityTransparent code.
Initially, we write a simple dll library that contains the following class:
    /// 
    /// Demo class
    /// 
    public class AssemblyInfo   
    {
        /// 
        /// Write to the console the security settings of the assembly
        /// 
        public  string GetCasSecurityAttributes()
        {
            //gets the reference to the current assembly
            Assembly a = Assembly.GetExecutingAssembly();

            StringBuilder sb = new StringBuilder();

            //show the transparence level
            sb.AppendFormat("Security Rule Set: {0} \n\n", a.SecurityRuleSet);

            //show if it is full trusted
            sb.AppendFormat("Is Fully Trusted: {0} \n\n", a.IsFullyTrusted);

            //get the type for the main class of the assembly
            Type t = a.GetType("CasAssemblyInfo.AssemblyInfo");

            //show if the class is Critical,Transparent or SafeCritical
            sb.AppendFormat("Class IsSecurityCritical: {0} \n", t.IsSecurityCritical);
            sb.AppendFormat("Class IsSecuritySafeCritical: {0} \n", t.IsSecuritySafeCritical);
            sb.AppendFormat("Class IsSecurityTransparent: {0} \n", t.IsSecurityTransparent);

            try
            {
                sb.AppendFormat("\nPermissions Count: {0} \n", a.PermissionSet.Count);
            }
            catch (Exception ex)
            {
                sb.AppendFormat("\nError while trying to get the Permission Count: {0} \n", ex.Message);
            }

            return sb.ToString();

        }
    }
The GetCasSecurityAttributes() method inside the class returns a string that contains:
  • The rule set for the assembly (Level1 Security Transparence or Level2 Security Transparence)
  • Whether the assembly is Full Trusted
  • Whether the AssemblyInfo class is SecurityCritical, SecurityTransaprent or SecuritySafeCritical
  • The number of permission in the assembly’s PermissionSet.
We then create a console application that consumes the AssemblyInfo class exposed by the previous library:
   class Program
    {
        /// 
        /// Entry point
        /// 
        /// 
        static void Main(string[] args)
        {
            //get the assembly zone evidence
            Zone z = Assembly.GetExecutingAssembly().Evidence.GetHostEvidence();
            Console.WriteLine("Zone Evidence: " + z.SecurityZone.ToString() + "\n");
            Console.WriteLine(new AssemblyInfo().GetCasSecurityAttributes());
        }
The main method writes the value of the assembly’s Zone Evidence to the console, and then it calls the  GetCasSecurityAttributes() method of the AssemblyInfo class contained in the dll. When we run our program, the output that we get is:
Figure 2. The output of the simple demonstration program running on the local machine.
As we can see from Figure 2:
  1. The Evidence for the assembly states that it is running on the local machine.
  2. The security transparency is given as Level2 Security Transparence. We haven’t set it in our code, so we can see that Level2 is the default Security Transparence mode in .NET Framework 4.0. If we want to use the previous Level1 model, we can use the assembly’s attribute:

    [assembly: SecurityRules(SecurityRuleSet.Level1)]


    Pay attention to the fact that, by doing that, the assembly becomes transparent, but our program will still execute. This is because Level1 Security Transparency is not able to perform enforcement on our code because, as I mentioned previously, enforcement with the pre-4.0 versions of .NET Framework was handled by the CAS Policy. We will soon see that this lack of enforcement doesn’t happen with Level2 Security Transparence, and if you want to maintain compatibility with the pre-4.0 versions of CAS, you must activate the CAS Policy by adding the following lines to the assembly configuration file:

    <configuration>
      <runtime>
        <NetFx40_LegacySecurityPolicy enabled="true" />
      </runtime>
    </configuration>

  3. The assembly is fully trusted. This means that all the classes inside it are SecurityCritical, and that no permissions are set to the assembly. This occurs because assemblies that run on a computer or on a shared folder are considered unhosted applications. To explain what that actually means, remember that we saw earlier that, with Level2 Security Transparence, permissions to an assembly are now decided by the assembly’s host, not by the CAS Security Policies. Examples of a host include an ASP.NET run-time, SQL CRL run-time and so on. Applications that run outside such host are called unhosted applications, and they are always fully trusted by default.

    This seem to be a natural decision that the .NET Framework Security Team has taken. If the permissions are all defined on a host, this mean that unhosted applications, for which permission cannot be set, can be either fully trusted or absolutely untrusted (that is, without any permission to access protected resources). In the first case, resources can still be protected by using other technique or tools, and we will see some of them in the next paragraph. In the second case, to allow the .NET Framework to continue working using protected resources would require techniques or tools which would be able to elevate permissions. From a security point of view, this second option would clearly not be a good choice.

    Another advantage of unhosted applications being fully trusted is that, in this way, they can run asSecurityCritical code and cannot be accessed by SecurityTransparent code. So, code that belongs to, for example, the internet zone (which is SecurityTransparent) cannot make use of unhosted applications to damage our systems. As we already know, SecurityTransparent code cannot access SecurityCriticalcode, and so our unhosted application is therefore protected from code that came from internet or is otherwise suspicious.

    Now, suppose that we would like to run our .exe from a network shared folder. The output which we would then obtain would be:

    Figure 3. The output of the simple demonstration program running on a network shared folder.
    We can see from Figure 3 that the assembly zone evidence has changed to “Internet”, but our assembly is once again fully trusted, and the classes inside it are SecurityCritical; no permissions are applied to the assembly.

    With the pre-4.0 versions of the .NET Framework, the different zone evidence would imply that a different Code Group would be applied to the assembly and, in some situation, the same code would inexplicably stop working. If, for example, the exe should need to access some file, it could do that on the local machine (MyComputer zone), but when moved to a shared folder (Internet Zone) it would throw a security exception, stopping the execution. It may initial seem that there is minimal security in place with this new system, but let’s take a look at how we can keep our code under tighter control.

Reducing permissions in the Level2 Security Transparent Model

From the previous example, the first thing that might come in mind is that, with the new Level2 Security Transparence, the overall security of systems seem to be diminished. By ignoring the principle of least privilege, the new model will surely result in more code than before with the full trust to execute? This is really not the case; the overall security model has only changed, becoming easier to implement, and so reducing the chance of potentially dangerous errors. In a moment, we’ll see how we can reduce the permissions of code running in the Level2 model. However, If administrators want to control which type of code can run on a particular system, they can use tools such as Software Restriction Policies or the new AppLocker available from Windows 7 and Windows 2008 Server R2. Using these newer tools, they can control not only managed code (as the legacy CAS Policy enabled), but even unmanaged code.
From a developer point of view, when it comes to reducing permissions, it is now possible to run application asSecurityTransparent code. if the application doesn’t need to access protected resources, then this a good way to satisfy the guidelines of the principle of least privilege. To force the assembly to run as SecurityTransparent, we just need to insert the following attribute for the assembly:
 [assemblySecurityTransparent()]
This states that all code, even if the assembly is fully trusted, will be of the type SecurityTransparent.  If we add this previous line to our demo assembly, we get the following output:
Figure 4. The output from our demonstration program with the code set to SecurityTransparent.
As we can see, our assembly is now SecurityTransparent, even though it is fully trusted due to the fact that it is also unhosted. As a result, it can only call SecurityTransparent code and so it cannot be used to access protected (SecurityCritical) resources. In fact, as we see, when it tries to get the Permission Count an exception occurs, because the code contained in the get accessor of the PermissionSet property is SecurityCritical code. The same also happens if the .exe is executed from a network shared folder.
The examples provided in this section seem to state that Level2 Security Transparence is, de facto, an all or nothing model. If the assembly is fully trusted it can do anything, and if we set it to be SecurityTransparent it cannot use protected resources at all. However, a more granular approach is possible when we need to protect specific resources, and it is based on the Allow Partially Trusted Caller Attribute (APTCA) which we can set for an assembly. With it, we can set code as SecuritySafeCritical, thereby creating a bridge betweenSecurityTransparent and SecurityCritical code. We will discuss this in detail in the next article.

Sandboxing

This is all fine if we’re only working with our own code, but what if we have to use a third party assembly that we doesn’t fully trust? We know that, if we run it on our machines and a SecurityTransparent attribute was not specified inside the assembly, it can do anything with our resources.
The solution is to sandbox the assembly, which restricts which resources the assembly can use, enabling us to protect our systems. Sandboxing consists of the creation of a partially trusted host and forcing the assembly to run inside it. As I mentioned at the beginning of this article, the Level2 Security Transparence replaced the CAS Policy, leaving the host with the ability to set permissions, so this is quite an elegant method of creating a sandbox, as we’ll see in a moment. The partially trusted host is created with the AppDomain.CreateDomain() method defined in the .NET Framework 4.0; This method is not new in the 4.0 version, it has just been modified to permit the sandboxing.
In the pre-4.0 application domain, permissions to access resources were determined by the CAS Policy, which, for each assembly loaded into the domain, applied restrictions to it based on its Code Group, which in turn was determined by its Evidence, and the PermissionSet imposed on the Code Group itself. This lead to a heterogeneous domain, in which PermissionSets could mix each other’s configurations, bringing about very complex situations. With Level2 Security Transparence, permissions are imposed directly to the domain, and all the assemblies inside it are forced to follow them (exceptions are made for those that the developer decides can be fully trusted). This has been called a Homogeneous Domain.
Let’s resume working on our demonstration dll library, and try to run it in a sandboxed (i.e. partially trusted) domain. To do so, we need to modify the AssemblyInfo class by allow it to derive from MarshalByRefObject, and need to use the AppDomain.CreateDomain() method. Before we can use this method, we need to create a PermissionSet which we would like to be granted to the newly-created domain, and so, with the Homogeneous Domain behavior of the Level2 Security Transparent model, to the assemblies loaded on it.
Rather that specify all the permission one by one which we would like to insert into the PermissionSet, we can use the new 4.0 Framework SecurityManager.GetStandardSandbox() method, which allows you to return the associated PermissionSet with the evidence passed as input. The following code shows how to do this:
       /// 
        /// create a permission set
        /// 
        public static PermissionSet GetPermissionSet()
        {
            //create an evidence of type zone
            Evidence ev = new Evidence();
            ev.AddHostEvidence(new Zone(SecurityZone.MyComputer));

            //return the PermissionSets specific to the type of zone
            return SecurityManager.GetStandardSandbox(ev);
        }
The line of code above returns a PermissionSet object that contains all the permissions associated with the MyComputer Zone Evidence. We then create a method that browses the security features of the domain that we will create:
        /// 
        /// Get the Domain security info
        /// 
        public static string GetDomainInfo(AppDomain domain)
        {
            StringBuilder sb = new StringBuilder();
            //check the domain trust
            sb.AppendFormat("Domain Is Full Trusted: {0} \n", domain.IsFullyTrusted);
            //show the number of the permission granted to the assembly
             sb.AppendFormat("\nPermissions Count: {0} \n\n\n", domain.PermissionSet.Count);
            return sb.ToString();
        }
Now we can implement our Main method:
        /// 
        /// Entry point
        /// 
        static void Main(string[] args)
        {
             //create  the AppDomainSetup
            AppDomainSetup info = new AppDomainSetup();
            //set the path to the assembly to load.
            info.ApplicationBase =Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);

            //create the domain
            AppDomain domain = AppDomain.CreateDomain("CasHostDemo"null, info, GetPermissionSet());
            //create an instance of the AssemblyInfo class
            Type t = typeof(AssemblyInfo);
            ObjectHandle handle =
Activator.CreateInstanceFrom(domain, t.Assembly.ManifestModule.FullyQualifiedName, t.FullName);
            AssemblyInfo ai = (AssemblyInfo)handle.Unwrap();

        Console.WriteLine("DOMAIN INFO:\n");
            //get the domain info
            Console.WriteLine(GetDomainInfo(domain));

            Console.WriteLine("ASSEMBLY INFO:\n");
            //get the assembly info form the sandboxed assembly
            Console.WriteLine(ai.GetCasSecurityAttributes());
                          Console.ReadKey();
        }
Just to explain what’s happening, the main method:
  1. Creates an AssemblyDomainSetup object and set its ApplicationBase value to the directory that contains our demo assembly.
  2. Creates the domain...
      naming it “CasHostDemo”,
    • without passing an Evidence object
    • using the AssemblyDomainSetup object created in the previous step, and
    • setting the PermissionSet obtained with the GetPermissionSet() method which we developed.
  3. Uses the Activator class to create an ObjectHandler that keep the reference to an object of typeAssemblyInfo (defined on our demo dll), then unwrapping it into an AssemblyInfo object.
  4. Calls our GetDomainInfo() method by passing it the domain we’ve created.
  5. Calls the GetCasSecurityAttributes() method of the AssemblyInfo object instantiated.
Note that we don’t pass any Evidence objects to the AppDomain.CreateDomain method, because it does not need them anymore. Given that Evidence is no longer used to assign the correct Code Group to the Domain using CAS Policies, the Evidence  is simply no longer needed.
When we run our program now, we get the following output:
Figure 5. The demonstration program running in a Sandbox on our local machine.
As we can see, using the MyComputer Zone as Evidence doesn’t affect our code. In fact, this Zone creates a Full Trust domain without permissions from the PermissionSet. However, if we change the MyComputer Zone to, for example, the Internet Zone, we get:
Figure 6. The demonstration program running in a Sandbox in the Internet Zone.
The domain now runs as partially trusted domain, and there are 7 permissions granted (the same 7 permission related to the Internet Zone), meaning that our assembly runs now as partially trusted assembly. All classes areSecurityTransparent and the accessor for the PermissionSet property of the assembly throws the same exception seen previously in Figure 4. We have created a sandbox for the assembly.>
I’ll finish this article by pointing out that the sandboxed domain allows for the possibility of running assemblies as fully trusted, even if the domain is only partially trusted: assemblies contained on the Global Assembly Cache (GAC) run in fully trusted mode by default. If we want to add a non-GAC assembly to the list of fully trusted assemblies, we just need to inform the Assembly.CreateDomain() method about it by listing this non-GAC assembly using its StrongName. Obviously, the assemblies must therefore be signed with a strong name key file. To do this, we modify the previous method as follows:
        /// 
        /// Entry point
        /// 
        static void Main(string[] args)
        {
            //create the AppDomainSetup
            AppDomainSetup info = new AppDomainSetup();
            //set the path to the assembly to load.
            info.ApplicationBase =Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
            Assembly a =Assembly.LoadFile(Path.Combine(info.ApplicationBase,"CasAssemblyInfo.dll"));
            StrongName sName = a.Evidence.GetHostEvidence<StrongName>();
            //create the domain
            AppDomain domain = AppDomain.CreateDomain("CasHostDemo"null, info, GetPermissionSet() ,new StrongName[] {sName});

            //create an instance of the AseemblyInfo class
            Type t = typeof(AssemblyInfo);
            ObjectHandle handle =
Activator.CreateInstanceFrom(domain, t.Assembly.ManifestModule.FullyQualifiedName, t.FullName);
            AssemblyInfo ai = (AssemblyInfo)handle.Unwrap();

            Console.WriteLine("DOMAIN INFO:\n");
            //get the domain info
            Console.WriteLine(GetDomainInfo(domain));

            Console.WriteLine("ASSEMBLY INFO:\n");
            //get the assembly info form the sandboxed assembly
            Console.WriteLine(ai.GetCasSecurityAttributes());
               
           Console.ReadKey();
        }
In this new main method, we load the demo assembly from file System and get its StrongName:
            Assembly a =Assembly.LoadFile(Path.Combine(info.ApplicationBase,"CasAssemblyInfo.dll"));
            StrongName sName = a.Evidence.GetHostEvidence<StrongName>();
Then, we use a different overload of the AppDomain.CreateDomain() method, which allows us to set which assemblies must be considered full trust, and we pass it the StrongName of the demo assembly.
            AppDomain domain = AppDomain.CreateDomain("CasHostDemo"null, info, GetPermissionSet() ,new StrongName[] {sName});
By running our .exe, we get:
Figure 7. Our demonstration program, running in a sandbox with full trust.
We can see that, while the domain remains partially trusted, the assembly runs in full trust mode and all the classes inside it are SecurityCritical.

Conclusion

In this article we looked at how the new Code Access Security model works in the .NET Framework 4.0, and we saw that things have changed a lot, compared to the pre-4.0 versions. This is largely due to the fact that the previous model had some serious limitations (complexity, above all) which were not so easy to change without an overhaul.  From the developers’ perspective, migration of our code to the new Level2 Security Transparences model will be a tough task to accomplish, and in some cases, re-engineering parts of their application could well be necessary.
Thus far, we've been introduced to the general concept of the new CAS model, but the analysis stops at the assembly boundary. We looked at transparence, how to use it at the assembly and class level, and how assemblies interact with hosts. We’ve also looked at a few examples of how the new CAS model behaves by default, and from these examples we noticed that the new Level2 Security Transparence model seems to be an all or nothing model. If the assembly is fully trusted, all the system resources can be accessed, and if it is partially trusted, none of them can be used. Despite appearances, this is not actually the case, and in the next article we will see how the Level2 Security Transparence model can be applied in a more granular way by using the Allow Partially Trusted Callers attribute (APTCA) and SecuritySafeCritical code to apply CAS at the method level.