Transparent Code Behavior in CLR 2.0 – #3

I’ve talked about transparency and how to use it in CLR 2.0, but now I think it’s a good idea to consider what the differences are between transparent code and normal code (i.e. code that doesn’t opt into transparency). I’ve briefly mentioned some of the things which are different, but let’s go over everything here. This post will be fairly code-heavy.

  1. Transparent code cannot assert for permissions.
  2. Transparent code that calls a method with a link demand cannot satisfy that demand.
  3. Transparent code cannot call non-public critical code.
  4. Transparent, unsafe code faces a full demand for UnmanagedCode permissions.

Transparent Code Cannot Assert

Asserting for permissions in transparent code fails 100% of the time, every time. Consider the following code block:

[assembly: SecurityTransparent]

 

public class Program

{

    public static void Main(string[] args)

    {

        new SqlClientPermission(PermissionState.Unrestricted).Assert();

    }

}

The call to Assert will fail with an InvalidOperationException that states that you "Cannot perform CAS Asserts in Security Transparent assemblies." Nothing more to consider here.

Transparent Code Cannot Satisfy a Link Demand

This rule’s a little trickier, but imagine you have a method that causes a link demand for some permission, like the ExecuteCore method below. (A link demand ensures that only the caller, not the entire call stack, has the appropriate permissions.)

[ReflectionPermission(SecurityAction.LinkDemand, Flags = ReflectionPermissionFlag.MemberAccess)]

public static void ExecuteCore()

{

    // Security-sensitive code.

}

If transparent code calls this method, it is impossible for it to satisfy the demand; that is how transparency was designed. Instead, the LinkDemand turns into a full Demand, which ensures the entire call stack has the appropriate permissions. Consider having two assemblies, Driver.exe and Library.dll. Library.dll is a signed assembly that is installed in the GAC (which means it is full trust). It contains code that triggers a LinkDemand.

[assembly: SecurityCritical]

[assembly: AllowPartiallyTrustedCallers]

 

public class Foo

{

    public void Execute()

    {

        ExecuteCore();

    }

 

    [ReflectionPermission(SecurityAction.LinkDemand, Flags = ReflectionPermissionFlag.MemberAccess)]

    public static void ExecuteCore()

    {

        // Security-sensitive code.

    }

}

Driver.exe sets up a partial trust sandbox* that does not have the ReflectionPermission and tries to call the Foo.Execute method in Library.dll.

public partial class Program : MarshalByRefObject

{

    public static void Main(string[] args)

    {

        RunInPartialTrust();

    }

 

    public void PartialTrustMain()

    {

        Foo f = new Foo();

        f.Execute();

    }

 

    static void RunInPartialTrust()

    {

        // Setup sandbox and call PartialTrustMain

    }

}

Since Foo.Execute is transparent, the LinkDemand turns into a full Demand and fails when the CLR checks the permissions of the Program.PartialTrustMain method. If, however, you mark Foo.Execute as SecurityCritical, then the program will execute successfully. In this case, because the caller of ExecuteCore is no longer transparent, the LinkDemand no longer escalates to a full Demand.

Transparent Code Cannot Call Non-Public Critical Code

Transparent code cannot call non-public critical code unless that critical code is also marked with the SecurityTreatAsSafeAttribute.

[assembly: SecurityCritical]

 

public class Foo

{

    public void Execute()

    {

        ExecuteCore(); // Throws a MethodAccessException

    }

 

    [SecurityCritical]

    private static void ExecuteCore()

    {

        // Security-sensitive code.

    }

}

Here the call to ExecuteCore will throw a MethodAccessException because of the rule above.

Note: You might find it interesting that Execute can call ExecuteCore through reflection even without the SecurityTreatAsSafeAttribute, as long as it has enough permissions to call private members through reflection. This is most likely a bug…

Transparent, Unsafe Code Needs UnmanagedCode Permissions

When the JIT compiler tries to compile unsafe code, it normally just requires that the assembly has the SkipVerification permission. However, there are additional demands with transparent code. Consider the class below.

[assembly: SecurityCritical]

 

public class Foo

{

    public void Execute()

    {

        ExecuteCore();

    }

 

    private unsafe int ExecuteCore()

    {

        int foo = 5;

        int* bar = &foo;

        *bar = 7;

 

        return foo;

    }

}

Immediately Execute calls ExecuteCore, the CLR will inject a full demand for a SecurityPermission with the UnmanagedCode flag. The same thing occurs if Execute is marked critical. It is only when the unsafe code itself is marked critical that the CLR does not inject this demand.

* I’ll talk more about sandboxing in a future post. In case you’re interested, here’s the code for RunInPartialTrust:

static void RunInPartialTrust()

{

    AppDomainSetup setup = new AppDomainSetup

    {

        ApplicationBase = Environment.CurrentDirectory

    };

 

    PermissionSet permissions = new PermissionSet(null);

    permissions.AddPermission(new SecurityPermission(SecurityPermissionFlag.Execution));

    AppDomain appDomain = AppDomain.CreateDomain(

        "Partial Trust AppDomain",

        null,

        setup,

        permissions

    );

 

    Program p = (Program)appDomain.CreateInstanceAndUnwrap(

        typeof(Program).Assembly.FullName,

        typeof(Program).FullName

    );

 

    p.PartialTrustMain();

}

Using Transparency in CLR 2.0 – #2

Last time I talked about transparency, but now it’s time to look at how to use it in CLR 2.0 (…ominous foreshadowing).

Remember the layers that I mentioned last time? If not, remember that transparent code cannot elevate privilege or cause permission demands to succeed, while critical code can. To make this distinction in your code, there are three attributes in the System.Security namespace you need to know about:

  • SecurityTransparentAttribute
  • SecurityCriticalAttribute
  • SecurityTreatAsSafeAttribute

If you want to leverage transparency in your assembly, the first thing you must do is to tell the CLR that you want it. You can do this by marking your assembly with the SecurityTransparentAttribute or the SecurityCriticalAttribute. The difference? If you mark your assembly with the SecurityTransparentAttribute (1), all of the code inside that assembly is now transparent. If, on the other hand, you mark your assembly with the SecurityCriticalAttribute (2), all of the code inside that assembly is transparent unless you say otherwise. You can also mark your assembly with the SecurityCriticalAttribute and specify a SecurityCriticalScope of Everything (3). This means that everything in your assembly is critical code.

  1. [assembly: SecurityTransparent]

  2. [assembly: SecurityCritical]

  3. [assembly: SecurityCritical(SecurityCriticalScope.Everything)]

If you choose option 1, you’re done; you can’t do anything more with regards to transparency. You’re stating that none of your code performs any permission elevations and all demands for permissions will flow right through your assembly. To give you an example from the framework, System.Data.Linq.dll (LINQ to SQL) is marked as transparent. All of the work that it does does not require any permissions. The places that demand permissions are later down in the pipeline (think of System.Data.dll for SqlClientPermission, mscorlib.dll for ReflectionPermission, etc.) and there is no possible way that System.Data.Linq can satisfy these permission demands.

If you choose option 2, you need to locate all of the pieces of code which are not transparent and mark them with the SecurityCriticalAttribute. System.Data.Entity.dll (Entity Framework) is marked as SecurityCritical because it does a few things like access assembly names that it needs to do even if the caller does not have these permissions. Thus wherever System.Data.Entity asserts for permissions, it also marks that code as SecurityCritical.

But wait! You’re not done yet. The CLR 2.0′s enforcement of transparency means that SecurityTransparent code cannot call non-public SecurityCritical code. That is, the following code will throw a MethodAccessException when Main attempts to call GetAssemblyName.

[assembly: SecurityCritical]

 

public class Program

{

    public static void Main()

    {

        GetAssemblyName(typeof(Program).Assembly);

    }

 

    [SecurityCritical]

    [FileIOPermission(SecurityAction.Assert, AllLocalFiles = FileIOPermissionAccess.PathDiscovery)]

    private AssemblyName GetAssemblyName(Assembly assembly)

    {

        return assembly.GetName();

    }

}

If you are absolutely positively sure that you want your transparent code to call into critical code, you can apply the SecurityTreatAsSafeAttribute to the critical method in order to allow this to happen. Mike Downen, a program manager on the CLR Security team, summarizes the reasons quite nicely in his MSDN article What’s New With Code Access Security in .NET 2.0:

The reason for the SecurityTreatAsSafe attribute may not be obvious at first. Think of the security-transparent and security-critical code within your assembly as actually separated into two assemblies. The security-transparent code would not be able to see the private or internal members of the security-critical code. Additionally, the security-critical code is generally audited for access to its public interface. You would not expect private or internal state to be accessible outside of the assembly—you would want to keep the state isolated. So to allow the isolation of state between security-transparent and security-critical code while still providing the ability to override when necessary, the SecurityTreatAsSafe attribute was introduced. Security-transparent code cannot access private or internal members of security-critical code unless those members have been marked with SecurityTreatAsSafe. Before adding SecurityTreatAsSafe, the author of critical code should audit that member as though it were being exposed publicly.

Marking GetAssemblyName as SecurityTreatAsSafe indeed solves our problem:

[SecurityCritical]

[SecurityTreatAsSafe]

[FileIOPermission(SecurityAction.Assert, AllLocalFiles = FileIOPermissionAccess.PathDiscovery)]

internal AssemblyName GetAssemblyName(Assembly assembly)

{

    return assembly.GetName();

}

 

Transparency Can Reduce Your Security Footprint – #1

Transparency is a feature that shipped in the second version of the CLR, but it’s one that’s easy to overlook unless you are concerned with partial trust scenarios. The goal of transparency is to reduce the amount of security-related work for developers who write libraries or frameworks intended for consumption in partial trust.

If partial trust code can call your assembly, it’s important that you audit your assembly to ensure that no caller can elevate its privileges and execute code it shouldn’t have access to. Unfortunately, code bases can become very large, which makes this review very time-consuming. This is the exact situation where transparency is designed to help.

Imagine partitioning your assembly into various layers with regards to security and elevation of privilege. One layer can include all the code that doesn’t do anything interesting from that point-of-view. On the other hand, the code that does is placed in another layer. The first layer is called transparent code, and the second is called critical code.

The transparency feature concretizes these layers by enforcing that transparent code cannot leave you exposed to elevation of privilege attacks. The CLR supports this by saying that everything that transparent code attempts to do that requires permissions will require the same permission from the entire call stack. This means that LinkDemands are converted to full demands, and any transparent code that calls unverifiable/unsafe code faces an "injected" demand for unmanaged code. Transparent code also cannot assert for permissions.

The beauty of this model is that in most cases, the amount of transparent code far surpasses the amount of critical code. This significantly reduces costs for library developers for investments in security audits and security testing, because only security critical code need be considered.

.NET Security Tips

As a test engineer on the Entity Framework team, I’ve observed many different areas to test in order to deliver a quality product. One of these areas is the story of how assemblies in the Entity Framework work in partial trust. Building this story as a product team forces us to make decisions on how we operate in certain scenarios, but it also forces us to know a lot about the scenarios that our customers envision for using the Entity Framework.

Over the past few months I have learned a lot about CLR security in order to discover these scenarios, and I want to share what I’ve learned as a series of tips in the hopes that it can help application developers learn more about security in .NET and help other framework and library developers build better software. I have a set of about 25 tips now, but I am more than happy to add more based on your feedback! Enjoy!