Dynamic Assemblies and Declarative Security

Speaking of dynamic IL generation ...

Before Whidbey, the framework supplied two ways of creating code on the fly, CodeDOM and Reflection.Emit.  The two vary greatly in their approaches.  With CodeDOM you basically emit C# (or VB, or any other language that you have a CodeDOM provider for).  To use the code, it gets written to disk, the appropriate compiler is called, and then you load the completed assembly.

For more lightweight solutions, Reflection.Emit may be appropriate.  The approach taken here is quite different.  Instead of emitting source code, you create a dynamic assembly using the AppDomain::DefineDynamicAssembly method.  This returns an AssemblyBuilder which lets you create dynamic modules.  The dynamic modules allow you to create types or global methods.  Finally, within the types you can emit dynamic methods using the ILGenerator.  Assuming you've been reading your ECMA spec before bed every night, you can then just write out the IL to do what you need.  Going this route, your assembly doesn't even have to ever touch the disk, let alone be written out as source code and compiled.

So what's all this got to do with declarative security?  Well, some of the DefineDynamicAssembly overloads take three PermissionSets, one each for the assembly's required, optional, and refused permission sets.  When using this API, you might think that they'll effect the grant set of the generated assembly in the same was as any other assembly level declarative security request would.  However this is not the case -- the permission set of the dynamic assembly will be inherited from the permission set of the assembly that is emitting it.  Any assembly level declarative security will not come into play when determining the grant set.  This means, for instance, that you cannot pass a permission set in the refusedPermissionsparameter and expect that the emitted assembly will not be granted those permissions.

If the dynamic assembly is unaffected by the assembly level declarative security at all, why even bother having the parameters?  Well, if you were to save the assembly to disk the declarative security would be written into it.  At that point, whenever the assembly was reloaded, the declarative security will have the effect you expect.

To work around this situation, you have a couple of options.  The most obvious is to emit with declarative security, unload the assembly (which means unloading the AppDomain that it was emitted into), and then reload the assembly.  However, if you're just looking to limit the assembly's grant set via a refused set, then you have another option.  If you use one of the overloads of DefineDynamicAssembly that take an Evidence parameter, you can use the evidence to limit the permission set of the generated assembly.  By creating an AppDomain policy level, you can use that evidence to ensure that the dynamic assembly is assigned the permission set of your choosing.  And, since passing Evidence will cause the CLR to evaluate the dynamic assembly to determine it's permission set, your assembly level declarative security will take effect.

A final option would be to have the emitting be done by an assembly that is granted the permission set you want the dynamic assembly to inherit.  However, I do not recommend doing this if the reason you are restricting the permissions of the dynamic assembly is that you don't trust it.  Since the emitting assembly will need some very high trust permissions, such as ControlEvidence, in order to do its job, and those permissions are too powerful to pass on to untrusted code, this technique should be avoided in most circumstances.  The one situation for which I think this solution is a good idea is when you're just trying to limit the dynamic assembly's permissions to the Everything permission set in order to ensure that you've emitted verifiable code.  Ensuring that you're emitting verifiable IL eliminates many subtle, hard to debug problems.  For many of these types of issues, getting a VerificationException is a lot nicer than digging through the emitted x86 code and various CLR internal data structures.

Updated 5/31/2005: Clarified the effect of passing Evidence to DefineDynamicAssembly