The AllowPartiallyTrustedCallersAttribute (APTCA) – #6

I decided for today’s tip to review a concept that may be familiar to a lot of you already, because it provides some backstory before I jump into more .NET 4 security topics.

The System.Security.AllowPartiallyTrustedCallersAttribute (APTCA, for short) is used to expose strong-named assemblies to partially trusted callers. If you don’t need to expose your library to partially trusted callers (examples include a web application running in partial trust or a WPF application running in a ClickOnce sandbox), then you shouldn’t worry about this attribute. Applying it to your assembly means you should audit your assembly very carefully to ensure that partially trusted callers can’t elevate their privileges.

The only time partially trusted code can call a strong-named assembly is when the strong-named assembly has APTCA applied. (Partially trusted code can always call assemblies without strong names.) An assembly is considered strong-named when it’s signed with a public/private key pair. You can do this through the Signing tab on the project properties in Visual Studio or by using the compiler directly. The picture below shows the option in a C# project in VS2008. (VB projects also feature the Signing tab with similar contents.)

Signing Tab in VS

You can also delay sign the assembly, as I’ve done above. Test-signed assemblies are also considered strong-named.

Once an assembly is strong-named, all of its public APIs will be protected with a LinkDemand for FullTrust, implying that direct callers of the public API must be fully trusted unless they want to face a hard failure. Let’s look at a concrete example of code that needs APTCA to run successfully.

Driver.exe

public class Program

{

    public static void Main(string[] args)

    {

        RunInPartialTrust();

    }

 

    public void PartialTrustMain()

    {

        Bar b = new Bar();

        b.Execute();

    }

}

StrongNamedLibrary.dll – A strong-named assembly

public class Bar

{

    public void Execute()

    {

        new Baz().ExecuteCore();

    }

 

    private class Baz

    {

        public void ExecuteCore()

        {

            Console.WriteLine("Hello world!");

        }

    }

}

When Driver.exe executes, the Main method sets up a partial trust sandbox (which I’ll cover in tomorrow’s tip) that calls PartialTrustMain. Having a sandbox essentially means that starting from PartialTrustMain, the code is partially trusted, which basically means it is running at a trust level other than full trust. If you’re not familiar with this, don’t get hung up on the details; in this application, PartialTrustMain is partially trusted code.

Immediately PartialTrustMain tries to instantiate the Bar class in StrongNamedLibrary.dll, we get a SecurityException:

APTCA SecurityException

Applying APTCA to StrongNamedLibrary will allow Driver.exe to call the library and output text to the console.

[assembly: System.Security.AllowPartiallyTrustedCallers]

 

Successful Output

So this is the simple case. Let’s look at some others…

What if Driver.exe is strong-named?

It doesn’t matter. Strong-named does not equal full trust, so you would see the same SecurityException. Otherwise, you could create a new strong-named assembly that calls StrongNamedLibrary.dll and call that assembly from Driver.exe and effectively circumvent the LinkDemand for FullTrust.

In fact, using the sandbox API (which, again, I’ll talk about in a later tip), strong-naming Driver.exe would cause the failure to occur because the partial trust code tries to create a new instance of the Program class so it can call the PartialTrustMain method. This will fail unless Driver.exe is itself marked with APTCA as well.

What if I use reflection?

Good question. There are two cases here. First, let’s try creating an instance of Bar through reflection.

public void PartialTrustMain()

{

    typeof(Bar).GetConstructor(Type.EmptyTypes).Invoke(null);

}

Same SecurityException, different stack trace.

APTCA SecurityException with Reflection

Second, let’s try creating a new instance of Baz and calling its ExecuteCore method.

public void PartialTrustMain()

{

    Type bazType = typeof(Bar).GetNestedType("Baz", BindingFlags.NonPublic);

    object baz = bazType.GetConstructor(Type.EmptyTypes).Invoke(null);

    bazType.GetMethod("ExecuteCore").Invoke(baz, null);

}

Does the code succeed or fail?

It succeeds, and "Hello world!" is written to the console. Remember the LinkDemand for FullTrust is placed only on public APIs! The Baz type is private, and that implies its ExecuteCore method is also private (at least, it is not exposed externally).

The mitigating factor here is that the partially trusted code must have enough reflection permissions to make this call. (We’ll talk more about these reflection permissions in a future tip.)

Do you have any other questions about APTCA? Leave a comment!

Leave a Reply

Your email address will not be published. Required fields are marked *

*

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>