Trusting the Document
So far we have been talking only about trusting the customization assembly. That makes senseit is, after all, the container of the code that is going to run. However, there is something quite unusual about customized documents that makes them very different from traditional forms-based applications. Here is a silly but illustrative example. Suppose you write a customization for a budget spreadsheet that has two named ranges that have event handlers that handle their double-click events as shown in Figure 19-9.
Figure 19-9. A budget spreadsheet that could be exploited by an attacker.
You build the customization, sign it with a strong name, ensure that company-wide security policy grants full trust to code with your strong name, and deploy the customization assembly and spreadsheet. But the text in the spreadsheet's named ranges is just text. What is to stop some unscrupulous person from changing the text in those ranges to whatever he wants? Anyone can swap the labels around, delete them entirely, change the size of the range, change the font to white letters on a white background, and so on. If the text in Figure 19-9's rows 11 and 12 is swapped, a double-click to raise taxes will actually invoke code that will lower taxes.
In most forms-based applications, the user interface is determined by the code. Not so with customized documents. The user interface is editable by end users and the customization is none the wiser. Therefore, it is not enough to trust only the customization; the document must be fully trusted as well. But how are we going to do that?
Unfortunately, all the techniques discussed thus far in this chapter for obtaining cryptographic evidence about the customization are not going to work well with the document. The whole point of cryptographic verification is to determine that not one bit of the assembly has been changed, but documents are edited all the time by their very nature.
For this reason, although the document must be fully trusted, the AppDomain policy level does not put the same policy restrictions on the document as it does on the assembly. A document can be fully trusted by virtue of its being in the My Computer Zone code group or in a fully trusted All Code code group.
Trusting Just Office Documents
Consider the following policy scenario: You want to deploy your customized document on an internal Web server. The customization is strong named, and you have an Enterprise policy that grants full trust to code with that strong name on that Web server. Suppose the policy looks like this:
Enterprise
All CodeFull trust
URL: http://MyServer/customizations/* No permissions
Foo Corporation Strong NameFull trust, level-final
This will fully trust the customization assembly because the level-final attribute on the strong-name code group will prevent the other three policy levels from further restricting the assembly's granted permission set.
But what about the document? The document needs to be trusted, too. In this example, the Enterprise policy level will fully trust the document by virtue of that root All Code code group. But the out-of-the-box Machine policy level will only see that the document is in the LocalIntranet Zone code group, and not grant full trust.
We could fix up this policy by making the URL code group above also grant the full trust permission set and make it level-final. However, that represents a pretty serious weakening of the policy. That would then say that all documents and code on that Web site, regardless of whether it was associated with a customization or not, whether strong named or not, are fully trusted. Really what we want to say is "all code signed with the strong name on the server, and all documents on the server are fully trusted."
We need a new membership condition that only matches Word and Excel documents. Fortunately, there now is such a membership condition, the aptly named Office Document Membership Condition. Membership conditions are represented by objects in the .NET security policy, and the assembly containing those objects has to be in the Global Assembly Cache (GAC). If it is not already, use gacutil.exe to install msosec.dll into the GAC:
> Gacutil -i MSOSec.DLL Microsoft (R) .NET Global Assembly Cache Utility. Version 2.0 Copyright (C) Microsoft Corporation. All rights reserved. Assembly successfully added to the cache
You can now create a Custom security policy that trusts all Word and Excel documents on a particular server. Custom membership conditions are represented by XML files. The Office Document Membership Condition has a simple representation in XML; it contains just the name of the membership condition type and the strong name of the assembly containing it, as shown in Figure 19-10.
Figure 19-10. Creating a code group based on the Office Document Membership Condition.
Why Is MSOSec Not in the GAC by Default?
The other VSTO assemblies are put in the GAC for you automatically, so why not this one? There is a good reason.
A basic tenet of writing install/uninstall software is that you must uninstall what you install. If the VSTO installer installs msosec.dll into the GAC, the uninstaller must remove it. But consider what happens if the installer installs msosec.dll, a user creates a security policy that uses the Office Document Membership Condition, and then uninstalls VSTO. What happens the next time that user tries to run managed code with msosec.dll deleted?
The managed code loader will examine the security policy and discover that policy references a membership object that no longer exists. The policy engine has no idea which assemblies would match that membership condition, so the policy engine really has no idea what permissions ought to be granted to a given assembly! When faced with this situation, the policy engine simply bails out and refuses to grant any code permission to run until the situation is fixed. All managed code on the machine would cease to run.
But if you do not install msosec.dll into the GAC in the first place, the uninstaller does not have to remove it. Users are responsible for putting this code in the GAC and ensuring that it is not removed until they have finished with it. Be very careful when removing security objects from the GAC.