We all know that the CLR provides many types of evidence to assemblies and AppDomains by default, but one feature of the runtime that's much less known is that assemblies can actually provide evidence of their own. This seems to be one of the best kept secrets in CLR security, the only mention of it that most of the people on the team today could find was in the .NET Framework Security book published for v1.0 of the framework. (In a somewhat interesting note, nearly everybody on the security team knew that this was possible, but the number of people who actually knew how to embed the evidence was not nearly as high. I'll let you guess at the number, but I'll give you a clue ... if it takes more than one bit to represent the number you're guessing then you've probably gone too high 🙂 ) Since that book is out of print, I've decided to write up the process here in case it interests anyone else.
Obviously evidence that's provided by an assembly is not as trustworthy as evidence that's provided by a host, however you can imagine situations in which assembly provided evidence could be used to make trust decisions for an assembly. One example might be an assembly that ships with a signed XML file indicating the results of a security audit done by a trusted company:
Since this hypothetical XML blob contains the strong name of the assembly to which it refers and the X.509 certificate of the company that did the audit and produced the signature, we could choose to trust that assembly if we trusted the company that provided the blob.
So, how would I go about embedding this custom evidence into my assembly? It's basically a three step process:
- Write the custom evidence using the BinaryFormatter into a file
- Compile your assembly into a netmodule
- Use the AL.exe tool to link the evidence into the assembly
Lets look at each step in detail.
1. Write the custom evidence using the BinaryFormatter into a file
The first step is to serialize the assembly evidence into a file that can be used by build tools to embed into the assembly. To do this, you'll need to write a short program that dumps the evidence out. The code looks something like this:
This code is fairly straightforward. First we load up the XML that our security auditors gave us into an XmlElement. We can't add the XmlElement directly into the evidence however because all evidence must be serializable and XmlElement is not. However, String is, so we can just get the OuterXml and add it to the Assembly evidence collection. It's important to AddAssembly rather than AddHost here, otherwise the CLR will ignore the evidence when it loads your assembly.
Once the Evidence collection is created, we use a FileStream and the BinaryFormatter to dump the evidence to a binary file on disk, in this case cleverly named TrustedAssembly.evidence, however any name will do.
2. Compile your assembly into a netmodule
The next step is to compile the code that would normally go into your assembly, however this time instead of building an .exe or .dll, you want to build a .netmodule. In C# this is done by saying /target:module on the command line, and VB supports a similar switch. I don't know of a way to modify the project properties in the Visual Studio UI to produce a netmodule, but you can manually edit your project file to achieve this:
In a standard Visual Studio generated project, you'll just have to change the OutputType property to be a Module rather than an Exe or a Library. This will cause the build to output <projectname>.netmodule as its result.
3. Use the AL.exe tool to link the evidence and netmodule into an assembly
The final step is to use the AL.exe tool to link the binary evidence file and the .netmodule containing the code for your assembly into the final assembly. AL takes the input evidence using it's /evidence command line switch, so an example of linking TrustedAssemblyEvidence.bin with TrustedAssembly.netmodule to produce TrustedAssembly.exe would be:
Here we tell al to link our evidence and our netmodule into an exe named TrustedAssembly.exe, setting the entry point to be Microosft.TrustedAssembly.Main and signing the assembly with TrustedAssembly.snk.
That's it, the resulting assembly now has evidence embedded within it. If you load this assembly in the CLR, you can look at it's evidence and in addition to the standard evidence types you would find if you looked at the output of GetHostEnumerator, you would find the string containing this XML blob in the assembly enumerator. A custom CLR host could use this evidence to decide that although this assembly was loaded from the Internet, it trusts the company that did the security audit, and therefore will grant the assembly extra permission.