Creating an AppDomain with limited permissions

Oftentimes in an application, it's necessary to run untrusted code.  The CLR lets you do this safely by placing the code in its own AppDomain and sandboxing the AppDomain to have a limited set of permissions.  Usually setting up the AppDomain with the Internet permission set allows you to feel confident in executing arbitrary managed code.  Doing this is relatively easy, here's a code snippet that takes advantage of last week's GetNamedPermissionSet method in order to create a sandboxed AppDomain:

/// <summary>
/// Create an AppDomain that contains policy restricting code to execute
/// with only the permissions granted by a named permission set
/// </summary>
/// <param name="permissionSetName">name of the permission set to restrict to</param>
/// <exception cref="ArgumentNullException">
/// if <paramref name="permissionSetName"/> is null
/// </exception>
/// <exception cref="ArgumentOutOfRangeException">
/// if <paramref name="permissionSetName"/> is empty
/// </exception>
/// <returns>AppDomain with a restricted security policy</returns>
public static AppDomain CreateRestrictedDomain(string permissionSetName)
{
    if(permissionSetName == null)
        throw new ArgumentNullException("permissionSetName");
    if(permissionSetName.Length == 0)
        throw new ArgumentOutOfRangeException("permissionSetName", permissionSetName, "Cannot have an empty permission set name");
        
    // Default to all code getting nothing
    PolicyStatement emptyPolicy = new PolicyStatement(new PermissionSet(PermissionState.None));
    UnionCodeGroup policyRoot = new UnionCodeGroup(new AllMembershipCondition(), emptyPolicy);

    // Grant all code the named permission set passed in
    PolicyStatement permissions = new PolicyStatement(GetNamedPermissionSet(permissionSetName));
    policyRoot.AddChild(new UnionCodeGroup(new AllMembershipCondition(), permissions));
        
    // create an AppDomain policy level for the policy tree
    PolicyLevel appDomainLevel = PolicyLevel.CreateAppDomainLevel();
    appDomainLevel.RootCodeGroup = policyRoot;

    // create an AppDomain where this policy will be in effect
    string domainName = String.Format("Restricted Domain: {0}", permissionSetName); 
    AppDomain restrictedDomain = AppDomain.CreateDomain(domainName);
    restrictedDomain.SetAppDomainPolicy(appDomainLevel);

    return restrictedDomain;
}

The code is pretty straight forward.  First I create a code group that grants AllCode no permission (similar to how the other policy levels create their root code group.)  The reason for this is that I'd like the flexibility of having multiple code groups on this AppDomain if necessary, and I can only have one root code group.  By setting the root code group to AllCode -> Nothing, I can then chain as many child code groups as I want onto that root.

In this case, the only child code group I create assigns AllCode the permission set named in the parameter of the method call.  Once this policy is set up, I create an AppDomain PolicyLevel, and assign the code groups to that level.

At that point, the only thing left to do is create the AppDomain and set its policy level.  This is easily done with the CreateDomain and SetAppDomainPolicy methods of the AppDomain class.

One important point to note is that setting the AppDomain policy must be done before any code is loaded into the AppDomain, and may only be done once.  As soon as code gets loaded into the AppDomain any changes to the security policy of that domain will no longer have any effect.

Another point is that this sandbox could be made even more secure by assigning some evidence to the AppDomain itself.  For instance, if I was planning on creating an AppDomain with the Internet named permission set, I might assign the AppDomain some evidence including Internet zone and a URL.