The Simple Sandboxing API

A while back I gave some sample code to show how to setup a sandboxed AppDomain.  This technique has worked since v1.0, and will continue to work with Whidbey.  However, Whidbey also introduces a simple sandboxing API which eliminates the need for this boilerplate code, and makes setting up a sandboxed AppDomain trivial for your application.

This API is exposed as a new overload of AppDomain.CreateDomain:

AppDomain.CreateDomain( string friendlyName,
Evidence securityInfo,
AppDomainSetup info,
PermissionSet grantSet,
params StrongName[] fullTrustAssemblies);

The first three parameters should be familiar, since they're also used in other CreateDomain overloads. Where the simple sandboxing API gets interesting is with the last two parameters. These provide the permission set to grant to all assemblies in the domain, and a set of assemblies that should be treated as fully trusted.

These two parameters define the way that the sandboxed domain will trust assemblies.  A sandboxed domain created with the simple sandboxing API has assemblies loaded with at most two grant sets.  Either the assembly is fully trusted, or it is granted the permission set passed in via the grantSet parameter.

An assembly will be fully trusted if it meets one of two conditions, if neither of these conditions are met it receives grantSet:

  1. The assembly is in the GAC
  2. It is strongly named and its strong name should match one of the names passed in through the fullTrustAssemblies parameter.

We can easily construct a StrongName object for an assembly in the same way we created StrongNameMembershipCondition objects:

    /// <summary>
    /// Create a StrongName that matches a specific assembly
    /// </summary>
    /// <exception cref="ArgumentNullException">
    /// if <paramref name="assembly"/> is null
    /// </exception>
    /// <exception cref="InvalidOperationException">
    /// if <paramref name="assembly"/> does not represent a strongly named assembly
    /// </exception>
    /// <param name="assembly">Assembly to create a StrongName for</param>
    /// <returns>A StrongName that matches the given assembly</returns>
    public static StrongName CreateStrongName(Assembly assembly)
    {
        if(assembly == null)
            throw new ArgumentNullException("assembly");

        AssemblyName assemblyName = assembly.GetName();
        Debug.Assert(assemblyName != null, "Could not get assembly name");
        
        // get the public key blob
        byte[] publicKey = assemblyName.GetPublicKey();
        if(publicKey == null || publicKey.Length == 0)
            throw new InvalidOperationException("Assembly is not strongly named");

        StrongNamePublicKeyBlob keyBlob = new StrongNamePublicKeyBlob(publicKey);

        // and create the StrongName
        return new StrongName(keyBlob, assemblyName.Name, assemblyName.Version);
    }

 

With that bit of code in place, it's now pretty trivial to create an AppDomain that grants, say only Execution permission to any assembly but the currently running one.

// create the permission set to grant other assemblies
PermissionSet pset = new PermissionSet(PermissionState.None);
pset.AddPermission(new SecurityPermission(SecurityPermissionFlag.Execution));

// create the sandboxed domain
AppDomain sandbox = AppDomain.CreateDomain(
    "Sandboxed Domain",
    AppDomain.CurrentDomain.Evidence,
    new AppDomainSetup(),
    pset,
    CreateStrongName(Assembly.GetExecutingAssembly()));

// transfer control to the new appdomain
// ...

Clearly this API makes setting up a sandboxed domain much easier than the old method, which required you to setup an entire policy level, deal with various code groups, etc.

Next time we'll take a deeper look at the created domain, and discover what may be some surprising properties that it has.