Jaa


An Enhanced Version of the Sandboxed AppDomain

Last week I showed how to create an AppDomain with a limited set of permissions.  I also presented an easy way to create a StrongNameMembershipCondition.  Now I'll put the two together to make an enhanced version of the sandboxed AppDomain.

Why create a new version?   The version I presented last time works wonderfully if you're going to create the AppDomain and then call AppDomain.ExecuteAssembly to run the untrusted code in it.  However, especially in plugin scenarios, a more common approach is to create a MarshalByRefObject in your application that you load into the new AppDomain, and then have this proxy object invoke the untrusted assembly.  This scenario won't work with the last version of CreateRestrictedDomain, since all code loaded into the AppDomain will be granted the same limited set of permissions.

This can be easily rectified by modifying CreateRestrictedDomain to take a second parameter, which is an extra code group to add to the policy:

/// <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>
/// <param name="extraCodeGroup">extra code groups to add</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, CodeGroup extraCodeGroup)
{
    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));
        
    // add the extra code groups to the tree
    if(extraCodeGroup != null)
        policyRoot.AddChild(extraCodeGroup);
        
    // 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;
}

Now, in order to enable the MarshalByRefObject scenario, we just need to make a code group that grants FullTrust to the assembly that's creating the AppDomain.  This is easily done with the CreateStrongMembershipCondition method:

// create a code group that gives this assembly full trust
PolicyStatement fullTrust = new PolicyStatement(new PermissionSet(PermissionState.Unrestricted));
CodeGroup trustSelf = new UnionCodeGroup(CreateStrongNameMembershipCondition(), fullTrust);        
        
AppDomain restrictedDomain = CreateRestrictedDomain("Internet", trustSelf);

Using the above snippet creates an AppDomain policy similar to:

  • AllCode: Nothing
    • All Code: Internet
    • AppStrongName: FullTrust

As you can see, this policy will grant Internet permissions to all code in the AppDomain except for the assembly that created the domain, which will remain fully trusted.

Comments

  • Anonymous
    November 02, 2004
    Thanks for the sample. I have applied a slightly different approach myself where, instead of specifying a CodeGroup I use a speparate method to add StrongNames (or StrongNameMembershipConditions) for fully trusted assemblies. I then call PolicyLevel.AddFullTrustAssembly for each of them before creating the AppDomain. I guess the advantage of this approach is that it allows greater flexibility per assembly, e.g. you might want another assembly to run under LocalIntranet instead of Internet or FullTrust.
  • Anonymous
    November 03, 2004
    Hi Kevin,

    I've actually been planning a post on what that full trust list means, its for a different purpose than what you've been using it for. To accomplish what you're trying to do I would write a method that got a strnong name membership condition fore each assembly and matched them to the correct permission set. Then make all of those code groups children of a big union code group that granted all code nothing.

    -Shawn
  • Anonymous
    November 04, 2004
    Thanks for the extra info Shawn. I look forward to your posts about evidence and full trust.
  • Anonymous
    November 05, 2004
    Bookmarked...
  • Anonymous
    November 07, 2004
    FullTrustAssemblies are those that implement CAS permissions. I think they are not evaluated by CAS at all. While it will work and I used it myself at first. It sounds wrong.