Trusting the Document


So far, we have been talking only about trusting the customization assembly. That makes sense; it is, after all, the container of the code that is going to run. Something quite unusual about customized documents, however, makes them very different from traditional forms-based applications. Here is a silly but illustrative example. Suppose that you write a customization for a budget spreadsheet that has two named ranges with 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 companywide 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, 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, by their very nature, are edited all the time.

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.

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 that 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 see only that the document is in the LocalIntranet Zone code group and will not grant full trust.

We could fix this policy by making the URL code group above also grant the full-trust permission set and make it level-final. That fix represents a pretty serious weakening of the policy, however. Then the policy would say that all documents and code on that Web site, regardless of whether they were associated with a customization, whether they are 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 matches only Word and Excel documents. There is such a membership condition now: 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 in 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 


Now you can 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 DocumentMembership 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.


We noted above that msosec.dll might not be in the GAC. 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 in the GAC, the uninstaller must remove it. But consider what happens if the installer installs msosec.dll, and 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 enginesimply 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 in 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.

Deploying Policy to User Machines

After you have figured out which policies you need to deploy throughout your enterprise, how are you going to get them from your administration machine onto user machines? Fortunately, it is no different from deploying any other application: You can create an MSI installation file, create a batch file to set up security policy from the command line, or write a C# program.

Creating an MSI Installer

Create the Enterprise policy level you want to deploy on your machine, using the mscorcfg.msc management tool. When you have a satisfactory policy, right-click the Runtime Security Policy node in the tree view, and select Create Deployment Package. The dialog box shown in Figure 19.11 will appear.

Figure 19.11. Creating a code group based on the Office Document Membership Condition.


Then the Deployment Package Wizard will create an installation script that you can deploy the same way you deploy any other application throughout your enterprise, whether via System Management Server or Group Policy, to update user machines automatically or simply put the installation script up on a share so users can click it themselves.

Creating a Batch File

As discussed in Chapter 18, "Server Data Scenarios," you can use the caspol.exe utility to change security policy. Usually, caspol.exe is located in the Windows\Microsoft.NET\Framework\v2.0 directory. Generally, caspol.exe is extremely flexible and has many options, but for our purposes, we'll discuss only how to view and edit a policy level.

To view a policy level, the syntax is as follows:

caspol.exe -<level> -listgroups 


<level> can be enterprise, machine, or user. For example:

C:\> caspol -machine -listgroups Microsoft (R) .NET Framework CasPol 2.0 Copyright (C) Microsoft Corporation. All rights reserved. Security is ON Execution checking is ON Policy change prompt is ON Level = Machine Code Groups: 1. All code: Nothing  1.1. Zone - MyComputer: FullTrust   1.1.1. StrongName -: FullTrust   1.1.2. StrongName -: FullTrust  1.2. Zone - Intranet: LocalIntranet   1.2.1. All code: Same site Web   1.2.2. All code: Same directory FileIO - 'Read, PathDiscovery'   1.3. Zone - Internet: Internet    1.3.1. All code: Same site Web   1.4. Zone - Untrusted: Nothing   1.5. Zone - Trusted: Internet    1.5.1. All code: Same site Web 


As you can see, every group is numbered to indicate its position in the code group hierarchy. To add a child group, we need to give caspol the number of the parent group, the membership condition of the group, and the permission set granted by this group. The syntax is as follows:

caspol.exe-<level> -addgroup <parent> <condition> <permissions> 


<permissions> can be Nothing, Execution, Internet, Local-Intranet, or FullTrust. The <condition> is somewhat more complicated due to the number of possible membership conditions for a code group. Typically, you will want to pick one of the following:

  • -allcode (The group grants full trust to all code.)

  • -strong mycustomization.dll (The group grants full trust to the strong-named assembly; remember that strong-name groups should be child groups of a location group.)

  • -url http://mysite/* (URL groups can also refer to directories on the local machine.)

  • -zone <zone> (<zone> is MyComputer, Intranet, trusted, Internet or Untrusted.)

  • -custom customfile.xml (This creates a custom membership condition.)

To create a code group that includes all Office documents, use this customfile.xml:

<IMembershipCondition /> 


To create a level-final or exclusive code group, add levelfinal on or exclusive on to the end of the command line.

To create a policy in the Machine level that fully trusts all files on a particular intranet site, you could use caspol.exe like this:

caspol.exe-machine -addgroup 1.2 url http://MyCorp/* FullTrust 


Writing a Visual Basic Program That Modifies Security Policy

The System.Security namespace provides objects that enable you to manipulate all aspects of the security system programmatically. To set a security policy, we first obtain the three persisted policy levels (Enterprise, Machine, and User are saved to disk; the Application policy is created dynamically whenever an application domain is created). Then we create a new code group by associating a membership condition with a permission set. Finally, we search the Machine policy for the Intranet group and add a child group, as shown in the Console application in Listing 19.1.

Listing 19.1. Programmatically Modifying Security Policy

Imports System.Collections Imports System.Security Imports System.Security.Policy Module Module1   Sub Main()     Dim enterprisePolicyLevel As PolicyLevel     Dim machinePolicyLevel As PolicyLevel     Dim userPolicyLevel As PolicyLevel     Dim zone As ZoneMembershipCondition     Dim accountingServerGroup As CodeGroup     Dim accountingServerCondition As UrlMembershipCondition     Dim policyStatement As PolicyStatement     Dim fullTrust As PermissionSet     Dim children As IList     ' Obtain the three policy levels:     Dim policyEnumerator As IEnumerator = _      SecurityManager.PolicyHierarchy()     policyEnumerator.MoveNext()     enterprisePolicyLevel = CType(policyEnumerator.Current, PolicyLevel)     policyEnumerator.MoveNext()     machinePolicyLevel = CType(policyEnumerator.Current, _       PolicyLevel)     policyEnumerator.MoveNext()     userPolicyLevel = CType(policyEnumerator.Current, _       PolicyLevel)     ' Create a new group by combining a permission set with a     ' membership condition:     fullTrust = machinePolicyLevel.GetNamedPermissionSet( _       "FullTrust")     policyStatement = New PolicyStatement(fullTrust, _       PolicyStatementAttribute.Nothing)     accountingServerCondition = New UrlMembershipCondition( _       "http://accounting/*")     accountingServerGroup = New UnionCodeGroup( _       accountingServerCondition, policyStatement)     ' Search the Machine policy level for the parent group:     children = machinePolicyLevel.RootCodeGroup.Children     ' Note that this makes a _copy_ of the children, so we'll     ' have to copy it back when we're done editing it.     Dim codeGroup As CodeGroup     For Each codeGroup In children       Dim zone1 As ZoneMembershipCondition = _         codeGroup.MembershipCondition       If zone1 IsNot Nothing And _         zone1.SecurityZone =  SecurityZone.Intranet Then         codeGroup.AddChild(accountingServerGroup)         machinePolicyLevel.RootCodeGroup.Children = children         SecurityManager.SavePolicy()         Exit For       End If     Next   End Sub End Module 


This program just gives a good starting point for building a custom policy editor; a more sophisticated program would check to see whether the child group already existed, prompt the user before changing security policy, and so on.




Visual Studio Tools for Office(c) Using Visual Basic 2005 with Excel, Word, Outlook, and InfoPath
Visual Studio Tools for Office: Using Visual Basic 2005 with Excel, Word, Outlook, and InfoPath
ISBN: 0321411757
EAN: 2147483647
Year: N/A
Pages: 221

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net