Removing Permissions From FullTrust

Executing the following code:

PermissionSet ps = new PermissionSet(PermissionState.Unrestricted);
Console.WriteLine("Before Removing Permissions:");
Console.WriteLine(ps.ToXml().ToString());

ps.RemovePermission(typeof(RegistryPermission));
Console.WriteLine("After Removing Permissions:");
Console.WriteLine(ps.ToXml().ToString());

May lead to surprising results.  The output is:

Before Removing Permissions:
<PermissionSet class="System.Security.PermissionSet"
version="1"
Unrestricted="true"/>

After Removing Permissions:
<PermissionSet class="System.Security.PermissionSet"
version="1"
Unrestricted="true"/>

It appears that the RemovePermission call didn't actually remove the registry permission.  That can't be right can it?

Lets take a step back and look at what's actually going on here.  First we create an unrestricted permission set, and write that out to the console.  However, notice how FullTrust is represented as a permission set with the unrestricted flag set to true.  The CLR uses this representation rather than have the FullTrust set contain all of the permissions in their unrestricted state for several reasons

  1. Upradability -- for instance in Whidbey, we've introduced several permissions, such as DataProtectionPermission.  If the FullTrust set contained every permission in their unrestricted state, a FullTrust set on Everett would not be FullTrust on Whidbey.
  2. Extensibility -- since we allow custom permissions to be written, we run into a problem with custom permissions.  Since we don't know every custom permission available, it would be impossible to create a FullTrust set that contained them in their unrestricted state.

Because of those factors, we decided to create a special FullTrust flag that would mean FullTrust regardless of the version of the runtime in use, or which special permissions are installed on the machine.  Internally, this flag also lets us make certain optimizations inside the CLR when evaluating demands.

So now that we've established that the FullTrust set doesn't actually contain any permissions per say, it becomes more clear why the call to RemovePermission did not do anything.  The FullTrust set doesn't actually contain a ReflectionPermission, so when we call RemovePermission asking it to take away the ReflectionPermission there's nothing to do.  This can be easily verified by examining the return value from the RemovePermission call:

IPermission removed = ps.RemovePermission(typeof(RegistryPermission));
Console.WriteLine("After Removing Permissions:");
Console.WriteLine(ps.ToXml().ToString());
if(removed == null)
    Console.WriteLine("No permission removed");
else
    Console.WriteLine(removed.ToXml().ToString());

Running this shows:

After Removing Permissions:
<PermissionSet class="System.Security.PermissionSet"
version="1"
Unrestricted="true"/>

No permission removed

Which means that no permission was actually removed.  Hopefully this behavior, which at first seems pretty counter-intuitive, makes a little more sense when you look at it from the inside out.