Transparent Code Behavior in CLR 2.0 – #3

May 19th 2009 06:02 am

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();

}

Posted by David DeWinter under Security Tips | No Comments »

Trackback URI | Comments RSS

Leave a Reply