Secure Deployment of Managed COM Add-Ins in Office XP

This content is no longer actively maintained. It is provided as is, for anyone who may still be using these technologies, with no warranties or claims of accuracy with regard to the most recent product version or service release.

 

Siew-Moi Khor and Misha Shneerson
Microsoft Corporation

November 2002

Applies to:
   Microsoft® Office XP

Summary: Learn how to securely install and deploy managed COM add-ins in Microsoft Office XP. Also learn how to build unmanaged COM add-in proxies called shims in both Visual Basic 6.0 and Visual C++ and how they work. (22 printed pages)

Download Odc_ofshim.exe.

Contents

Introduction
Quick Overview of the Issues
The Solution: A Dedicated Visual Basic 6.0 or Unmanaged Visual C++ 7.0 Shim Component
Implementing Proxy Objects in Visual Basic 6.0
Implementing Proxy Objects in Unmanaged Visual C++
Running the Unsigned COM Add-In Samples
Security in Office XP
How Managed Code Is Different
Security Considerations
Additional Points to Consider
Conclusion

Introduction

The default security settings for Microsoft® Office XP is High, with Trust all installed add-ins and templates selected (enabled). This particular security settings will automatically trust all COM add-ins and templates (Microsoft Office XP Visual Basic® for Applications (VBA)) that are installed on a machine—regardless of whether these add-ins and templates are code signed or not. (Note that this does not apply to Microsoft Outlook 2002. See the Security Considerations section for more details.)

These days, emphasis on security is an integral part of most organizations' overall IT strategies. Many security conscious users and administrators set their Office XP security level to High, and disable the default Trust all installed add-ins and templates setting. If your current environment has disabled the Trust all installed add-ins and templates option, you will need a shim to securely run any managed COM add-ins.

You can implement unmanaged COM interfaces in .NET (managed code) and use those implementations transparently from COM-based clients by utilizing the COM interop layer and tools. However, the installation and deployment of managed COM add-ins need special security considerations and handling. To ensure a successful deployment of managed COM add-ins you will need to incorporate into your managed COM add-in project a small, unmanaged proxy called a shim.

In this article we will discuss why a shim is needed, how the shim solution works and how to build a shim in Microsoft Visual Basic 6.0 and unmanaged Microsoft Visual C++®. We will also discuss how to install and deploy managed COM add-ins in Office XP, and the security aspects to consider in using this approach.

The three interfaces that would be of special interest to you if you want to build custom managed Component Object Model (COM) add-ins in Microsoft Office XP are the IDTExtensibility2, ISmartTagRecognizer, and ISmartTagAction interfaces. The IDTExtensibility2 interface is implemented when building custom COM add-ins and the ISmartTagRecognizer and ISmartTagAction interfaces are implemented when building custom smart tag COM add-ins.

It is assumed that the reader is familiar with Microsoft .NET technology and knows how to build an unmanaged COM add-in in using Microsoft Visual Studio® 6.0 and managed COM add-in using Microsoft Visual Studio .NET. It's further assumed that the reader knows what code signing is and how to code sign. If not, there are many excellent articles written on these topics on MSDN—a list of reference is given at the end of the article.

Quick Overview of the Issues

Since managed code is not native (not machine code) and exposed to unmanaged clients through COM interop, the COM InprocServer32 registry entry for managed COM add-ins will point to the .NET run-time engine (mscoree.dll) and not your assembly. Because mscoree.dll is not digitally signed, when an Office XP application security is set to:

  • High with Trust all installed add-ins and templates disabled, the mscoree.dll won't be loaded
  • Medium with Trust all installed add-ins and templates disabled, users will be prompted to either enable or disable mscoree.dll as shown in Figure 1 even if your assembly is digitally signed

Aa163984.odc_ofshim01(en-us,office.10).gif

Figure 1. Security warning dialog box pointing to mscoree.dll

If there are several managed components registered as Office XP application COM add-ins, a user will be asked to enable mscoree.dll as many times as there are managed COM add-ins. However, a user will not get any indication as to which COM add-in he is prompted to enable or disable.

Note   If you are unfamiliar with the issues, there is a detailed discussion on them further down this article in the following sections: Security Settings in Office XP and How Managed Code Is Different.

To avoid this macro warning, it is recommended that you use a dedicated shim for your component. A dedicated shim is a simple signed unmanaged COM add-in by itself. The shim will load a managed component that is registered for COM interop and redirect all the calls to this managed implementation COM add-in. This dedicated shim can be created using either Visual Basic 6.0 or unmanaged Visual C++ and the following section discusses how this can be done.

The Solution: A Dedicated Visual Basic 6.0 or Unmanaged Visual C++ 7.0 Shim Component

The simplest solution is to create a dedicated unmanaged COM add-in that redirects all calls to it to its managed COM add-in counterpart, that is, the managed COM add-in that you want loaded. The same way RegAsm.exe puts mscoree.dll in the middle, it is possible to put your own component in the middle so that the InprocServer32 value will point to your unmanaged dedicated DLL.

By ensuring that the hosting application can verify the validity of the dedicated unmanaged COM add-in that is loaded into its address space, you can satisfy the Office XP security requirement that the COM add-in is signed. This way the security of the user's computer will not be compromised. Your organization can maintain its Office XP security setting at its highest.

This particular component which we will call a shim, is an unmanaged component that acts as a proxy. Using a shim which acts as a proxy for a specific managed COM add-in means only that specific managed COM add-in is allowed to run, rather than all COM add-ins if you enable mscoree.dll.

Using a dedicated shim built using Visual Basic 6.0 that implement the IDTExtensibility2 interface as an example, here is how it works. (See the Implementing Proxy Objects in Visual Basic 6.0 section below for more implementation details.)

  1. First create the managed COM add-in and register it for COM interop.
  2. Next create a new Visual Basic 6.0 project.
  3. Create a proxy class that implements the IDTExtensibility2 interface.
  4. Add a reference to the managed COM add-in.
  5. Declare a class-level variable of the COM add-in type.
  6. Redirect all calls to the following methods to its managed COM add-in counterpart created in step 1:
    • OnConnection
    • OnAddInsUpdate
    • OnStartupComplete
    • OnBeginShutdown
    • OnDisconnection
  7. Build the Visual Basic 6.0 DLL.
  8. Test that it works correctly at Office XP Medium security level, with Trust all installed add-ins and templates disabled. If it's correctly built, you will be prompted to either enable or disable the dedicated Visual Basic 6.0 shim DLL when the application is launched.
  9. Sign the dedicated Visual Basic 6.0 shim DLL. (See the Security Considerations section for detailed discussions about signing components.)
  10. Deploy both the dedicated Visual Basic 6.0 shim DLL and its counterpart, the assembly. Do this by building a Visual Studio .NET setup deployment project. Compiling the deployment project will produce an .msi file that contains all the files you need to distribute to the public as a single unit.

So, when the COM classes are first instantiated inside an Office XP application, the following set of events happen:

  • Instances of the managed COM add-in classes are created and the pointers to those interfaces are cached.
  • All the calls made into the unmanaged classes are redirected to their managed counterparts using the cached pointers.

Since the shim DLL itself will be signed with Authenticode®, Office XP will allow the loading of the shim DLL into its address space.

  1. To create a simple unmanaged Visual C++ shim that implements the IDTExtensibility2 interface, the steps are quite similar:
  2. First create the managed COM add-in and register it for COM interop.
  3. Next create a new unmanaged Visual C++ project in the same Visual Studio .NET integrated development environment (IDE) as the managed COM add-in built in step 1.
  4. Create a proxy class that implements the IDTExtensibility2 interface.
  5. Add a reference to the managed COM add-in.
  6. Declare a class-level variable of the COM add-in type.
  7. Redirect all calls to the to the following methods to its managed COM add-in counterpart created in step 1:
    • OnConnection
    • OnAddInsUpdate
    • OnStartupComplete
    • OnBeginShutdown
    • OnDisconnection
  8. Build the unmanaged Visual C++ DLL.
  9. Test that it works correctly at Office XP Medium security level, with Trust all installed add-ins and templates disabled. If it's correctly built, you will be prompted to either enable or disable the dedicated unmanaged Visual C++ shim DLL when the application is launched.
  10. Sign the dedicated unmanaged Visual C++ shim DLL.
  11. Integrate the unmanaged Visual C++ shim DLL with the managed COM add-in using Visual Studio .NET.
  12. Deploy both the dedicated unmanaged Visual C++ shim DLL and assembly. Do this by building a Visual Studio .NET setup deployment project. Compiling the deployment project will produce an .msi file that contains all the files you need to distribute to the public as a single unit.

Implementing Proxy Objects in Visual Basic 6.0

What follows is a technical explanation of the Visual Basic 6.0 shim solution. Here is how a Visual Basic 6.0 shim is implemented to host a managed COM add-in.

To aid the explanation, a sample Visual Basic 6.0 COM add-in shim will be used. The COM add-in shim is a basic Visual Basic 6.0 project with a proxy class that implements the IDTExtensibility2 interface. To view the solution in Visual Studio 6.0, double click on the MyAddIn.vbp file located in the \article code\VB6SimpleShim\VB6 Project, Shim folder of the download accompanying this article.

The AddinInstance_Initialize method of the COM add-ins class (see the Connect.Dsr file of the MyAddIn.vbp project) uses the unmanaged API to create an instance of a corresponding managed component.

Below is a code snippet showing how this is achieved:

Option Explicit
Dim managedAddIn As IDTExtensibility2
Private Sub AddinInstance_Initialize()
    Set managedAddIn = CreateObject("MyManagedAddIn.Connect")
End Sub

The code is very simple. The AddinInstance_Initialize method is used to create an instance of the managed COM class. In this sample the full name of the managed Connect class to be instantiated is MyManagedAddIn.Connect.

The implementation of the interface methods for Connect class is straightforward once there is an interface reference. It redirects the calls to the contained instance of the managed class. The code snippet from the Connect.Dsr file of the MyAddIn.vbp project is shown below:

Private Sub AddinInstance_OnAddInsUpdate(custom() As Variant)
    managedAddIn.OnAddInsUpdate custom
End Sub

Private Sub AddinInstance_OnBeginShutdown(custom() As Variant)
    managedAddIn.OnBeginShutdown custom
End Sub

Private Sub AddinInstance_OnConnection(ByVal Application As Object, _
        ByVal ConnectMode As AddInDesignerObjects.ext_ConnectMode, _
        ByVal AddInInst As Object, custom() As Variant)
    managedAddIn.OnConnection Application, ConnectMode, _
        AddInInst, custom
End Sub

Private Sub AddinInstance_OnDisconnection _
        (ByVal RemoveMode As AddInDesignerObjects.ext_DisconnectMode, _
        custom() As Variant)
    managedAddIn.OnDisconnection RemoveMode, custom
End Sub

Private Sub AddinInstance_OnStartupComplete(custom() As Variant)
    managedAddIn.OnStartupComplete custom
End Sub

Implementing Proxy Objects in Unmanaged Visual C++

Here is how an unmanaged Visual C++ simple shim is implemented to host a managed COM add-in. The shim is implemented in unmanaged Microsoft Visual C++ in the Visual Studio .NET integrated development environment (IDE).

To aid the explanation, a sample unmanaged Visual C++ simple shim will be used.

The unmanaged Visual C++ simple shim is a basic ATL project with a proxy class that implements the IDTExtensibility2 interface. To view the solution in Visual Studio .NET, double click the simpleShim.sln file located in the \article code\C++SimpleShim folder of the download accompanying this article.

Let us first look at the FinalConstruct method. (For the simpleShim project, to view the FinalConstruct method, open the Connect.cpp file.).

The FinalConstruct method is called each time the containing class is instantiated. The FinalConstruct method of the COM add-ins class uses the unmanaged API to create an instance of a corresponding managed component.

Below is a code snippet from the Connect.cpp file showing how this is achieved:

HRESULT CConnect::FinalConstruct()
{
    USES_CONVERSION;
    return m_pManagedComponent.CoCreateInstance
        (T2OLE("MyManagedAddIn.Connect"));
}

The CoCreateInstance method is used to create an instance of the managed COM class. In this sample the full name of the managed Connect class to be instantiated is MyManagedAddIn.Connect.

The implementation of the interface methods for CConnect (see Connect.cpp source file) is straightforward once there is an interface pointer. It redirects the calls to the contained instance of the managed class. The code snippet from the Connect.cpp file of the simpleShim project is shown below:

STDMETHODIMP CConnect::OnConnection(IDispatch *pApplication,
    AddInDesignerObjects::ext_ConnectMode ConnectMode, IDispatch
    *pAddInInst, SAFEARRAY ** custom)
{
    pApplication->QueryInterface(__uuidof(IDispatch),
        (LPVOID*)&m_pApplication);
    pAddInInst->QueryInterface(__uuidof(IDispatch),
        (LPVOID*)&m_pAddInInstance);

    m_pManagedComponent->OnConnection(pApplication, ConnectMode,
        pAddInInst, custom);
    return S_OK;
}

STDMETHODIMP Connect::OnDisconnection (AddInDesignerObjects::
    ext_DisconnectMode RemoveMode, SAFEARRAY ** custom )
{
    m_pApplication = NULL;
    m_pManagedComponent->OnDisconnection(RemoveMode, custom);
    return S_OK;
}

STDMETHODIMP CConnect::OnAddInsUpdate (SAFEARRAY ** custom)
{
    m_pManagedComponent->OnAddInsUpdate(custom);
    return S_OK;
}

STDMETHODIMP CConnect::OnStartupComplete (SAFEARRAY ** custom)
{
    m_pManagedComponent->OnStartupComplete(custom);
    return S_OK;
}

STDMETHODIMP CConnect::OnBeginShutdown (SAFEARRAY ** custom)
{
    m_pManagedComponent->OnBeginShutdown(custom);
    return S_OK;
}

Creating a Unmanaged COM Add-In using Microsoft Visual Studio .NET

To create an unmanaged Visual C++ shim using Microsoft Visual Studio .NET, first create a Visual Studio .NET Shared Add-In project as follows:

  1. Click Start, point to Programs, point to Microsoft Visual Studio .NET, and click Microsoft Visual Studio .NET.
  2. On the File menu, point to New, and click Project. The New Project dialog box appears.
  3. In the Project Types pane, double-click the Other Projects folder, and click Extensibility Projects.
  4. In the Templates pane, click Shared Add-In.
  5. In the Name box, type a name for the Shared Add-In project.
  6. In the Location box, type a folder path or click Browse and select a folder path, and then click OK. The Extensibility Wizard appears.
  7. Click Next. The Select a Programming Language page appears.
  8. Click Create an Add-in using Visual C++/ATL, then click Next. The Select An Application Host page appears.
  9. Check the box next to each application that you wish to host the unmanaged COM add-in.
  10. Click Next. The Enter a Name and Description page appears.
  11. In the What is the name of your Add-In box, type the name of your unmanaged COM add-in.
  12. In the What is the description of your Add-In box, type the description for your managed COM add-in.
  13. Click Next. The Choose Add-In Options page appears.
  14. Check the I would like my Add-in to load when the host application loads and/or My Add-in should be available to all users of the computer it was installed on, not just the person who installs it boxes as appropriate.
  15. Click Next. The Summary page appears.
  16. Click Finish, and wait while the Shared Add-In project is built.

We won't go into a detailed step-by-step tutorial on how to build the dedicated unmanaged Visual C++ simple shim. However, there are a few things that need to be called out regarding customizing the dedicated shim. To customize, do the following:

  1. Open the Connect.h header file to add members. For example, define the FinalConstructmethod and FinalRelease method as follows:

    HRESULT CConnect::FinalConstruct();
    void CConnect::FinalRelease();
    
  2. Also in the Connect.h header file, use the CComPtr class to manage the IDTExtensibility2 COM interface pointer, for example:

    CComPtr<AddInDesignerObjects::IDTExtensibility2>
      m_pManagedComponent;
    
  3. In the Connect.cpp file, first implement the FinalConstruct method of the COM add-ins class to use the unmanaged API to create an instance of a corresponding managed component using the CoCreateInstance method. For example:

    HRESULT CConnect::FinalConstruct()
    {
        USES_CONVERSION;
        return m_pManagedComponent.CoCreateInstance
            (T2OLE("MyManagedAddIn.Connect"));
    }
    

    Then for each of the Connect class functions, redirect all calls to the unmanaged component to its managed counterpart. For example, add

    m_pManagedComponent->OnConnection(pApplication, ConnectMode,
            pAddInInst, custom);
    

    to

    STDMETHODIMP CConnect::OnConnection(IDispatch *pApplication,
        AddInDesignerObjects::ext_ConnectMode ConnectMode,
        IDispatch *pAddInInst, SAFEARRAY ** custom)
    {
        pApplication->QueryInterface(__uuidof(IDispatch),
            (LPVOID*)&m_pApplication);
        pAddInInst->QueryInterface(__uuidof(IDispatch),
            (LPVOID*)&m_pAddInInstance);
        return S_OK;
    }
    

    It should now look as follows:

    STDMETHODIMP CConnect::OnConnection(IDispatch *pApplication,
        AddInDesignerObjects::ext_ConnectMode ConnectMode,
        IDispatch *pAddInInst, SAFEARRAY ** custom)
    {
        pApplication->QueryInterface(__uuidof(IDispatch), 
            (LPVOID*)&m_pApplication);
        pAddInInst->QueryInterface(__uuidof(IDispatch),
            (LPVOID*)&m_pAddInInstance);
        m_pManagedComponent->OnConnection(pApplication, ConnectMode,
            pAddInInst, custom);
        return S_OK;
    }
    
  4. In the Visual Studio .NET deployment setup, register the unmanaged shim on the target machine in the appropriate HKEY_CURRENT_USER host application key. In this example it would be HKEY_CURRENT_USER\Software\Microsoft\Office\Word\Addins\simpleShim.Connect.

    You can view this in Visual Studio .NET by right clicking simpleShimSetup, point to View, and click Registry. Expand the HKEY_CURRENT_USER hive as shown in Figure 2.

    Click here for larger image

    Figure 2. Registry path of the shim on the target machine (click picture to see larger image)

Running the Unsigned COM Add-In Samples

The sample dedicated Visual Basic 6.0 (MyAddIn.dll) and unmanaged Visual C++ (simpleShim.dll) shims in the sample download are unsigned. To run an unsigned COM add-in, the security settings in the affected Office XP applications (Excel 2002 to run MyAddIn.dll, and Word 2002 to run simpleShim.dll) must be set to Medium, with the Trust all installed add-ins and templates check box cleared.

To change security settings, in the Tools menu, point to Macro, and click Security. It is strongly recommended that you do this only in a testing environment. After you are done testing, set the security level back to High.

Caution   By setting the security level to Medium, with the Trust all installed add-ins and templatescheck box cleared, users will have the choice to either enable or disable unsigned COM add-ins and VBA macros when they are prompted. If your security level is set to High,with the Trust all installed add-ins and templates check box cleared, all unsigned COM add-ins and VBA macros will be disabled automatically. Therefore, it is strongly recommended that all users keep their security levels set to High with the Trust all installed add-ins and templates check box cleared.

To run the Visual Basic 6.0 (MyAddIn.dll) shim sample, first open the VBSimpleShim.sln file located in the \article code\VB6SimpleShim\VB.NET Project, Setup folder in Visual Studio .NET. Rebuild the project by right-clicking on VBSimpleShim (2 projects) and then click Rebuild. Next build the setup project by right-clicking on VBSimpleShim and then click Rebuild.

To install the sample, right click on VBSimpleShim again and then click Install. After completing the installation, launch Excel 2002, the host application for this sample COM add-in.

When Excel 2002 opens, you will be prompted to either enable or disable MyAddIn.dll as opposed to mscoree.dll as shown in Figure 3 below.

Aa163984.odc_ofshim03(en-us,office.10).gif

Figure 3. A security prompt to enable or disable the dedicated Visual Basic 6.0 shim

Aa163984.odc_ofshim04(en-us,office.10).gif

Figure 4. A message box implemented in the managed COM add-in Connect class's OnConnection method

Click Enable Macros and a message box as shown in Figure 4 appears. It should be noted that this message box is implemented in the managed COM add-in Connect class OnConnection method (see the Connect.vb source file of the MyManagedAddIn project after opening VBSimpleShim.sln located in the \article code\VB6SimpleShim\VB.NET Project, Setup folder in Visual Studio .NET). You will get a series of message boxes as implemented in the managed COM add-in Connect class module.

Similarly for the unmanaged Visual C++ (simpleShim.dll) shim sample, first open the simpleShim.sln file located in the \article code\C++SimpleShim folder in Visual Studio .NET.

Note   If you installed the Visual Basic sample earlier, be sure to uninstall it first before trying the Visual C++ sample.

Rebuild the project by right clicking on simpleShim (3 projects) and then click Rebuild. Next build the setup project by right clicking on simpleShimSetup and then click Rebuild.

To install the sample, right click on simpleShimSetup again and then click Install. After completing the installation, launch Excel 2002, the host application for this sample COM add-in.

After completing the installation, launch Word 2002, the host application for this sample COM add-in. When Word 2002 opens, you will be prompted to either enable or disable simpleShim.dll as opposed to mscoree.dll as shown in Figure 5 below.

Aa163984.odc_ofshim05(en-us,office.10).gif

Figure 5. A security prompt to enable or disable the dedicated unmanaged Visual C++shim

Aa163984.odc_ofshim06(en-us,office.10).gif

Figure 6. A message box implemented in the managed COM add-in Connect class's OnConnection method

Click Enable Macros and a message box as shown in Figure 6 appears. This message box is implemented in the managed COM add-in Connect class OnConnection method (see the Connect.vb source file of the MyManagedAddIn project). You will get a series of messages boxes as implemented in the managed COM add-in Connect class module.

Security in Office XP

Table 1 below describes the behavior of Office XP applications when loading add-ins and macros. (This does not apply to Outlook 2002. See the Additional Points to Consider section for more details.) It demonstrates how Office mitigates the risks of loading malicious components by checking the identity of their publishers and by making explicit trust based decisions. (To display the Security dialog box, in the Tools menu, point to Macro, and click Security).

Table 1. Microsoft Office XP Security Settings matrix

Aa163984.odc_ofshimtablefig(en-us,office.10).gif

Note   The availability of, and options within, the Security dialog box varies depending on the specific Office application. Additionally, specific Office applications silently load signed add-ins and macros only from specific directories, along with registered COM add-ins and smart tags recognizers. For more information, see the Microsoft Office XP Macro Security White Paper.

When the Office XP security level is set at High, with the Trust all installed add-ins and templates option disabled, all add-ins and templates that are not code signed will automatically be disabled. And if the add-ins and templates are code signed with a certificate not listed in the Trusted Sources list, a user will be prompted to either enable or disable the add-ins or templates.

When a managed COM add-in is deployed using the RegAsm utility and the security setting in an Office XP application is set to Medium with Trust all installed add-ins and templates disabled, the user will be prompted to either enable or disable the COM add-in or templates when the application is launched. However, if the security level is set to High with Trust all installed add-ins and templates disabled, your managed COM add-in won't be loaded even when it is signed.

This isn't the behavior you would expect in correspondence to the security settings in Office XP. And you are right—if the COM add-in installed is an unmanaged COM add-in. However, with managed COM add-in, this is not so and the next section, Managed Code Is Different will explain why.

As can be seen from Table 1, Office XP approaches security from a trust perspective. Add-ins and templates will only be allowed to run if they are trusted, that is, published by trusted sources or if the user opts to Trust all installed add-ins and templates.

The notion of trust is subjective. It is up to the users to decide whether they want to trust the code published by a particular source. What is objective then are the software publisher's identity and the certificate used by the software publisher to sign the code file. Office XP components must use Authenticode to sign their code files so that users of the code files can verify the identity of the software publisher.

Office COM add-ins are COM components contained in native DLL files. When a COM component is registered, some data about it is written into the registry. Specifically, COM components (or COM classes) all have a globally unique identity called class ID (CLSID). All CLSIDs are grouped in the registry under the HKEY_CLASSES_ROOT\CLSID key and have the following form {XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXX}, where 'X' is a hexadecimal letter. Under the CLSID key there are several nested registry subkeys. One of the subkeys is InprocServer32 whose default value contains the full path to a DLL file as shown in the CLSID key registry layout in Figure 7.

This is the DLL that contains (or provides access) to the COM class associated with the CLSID. And in accordance with the Office security model, this is the class that has to be signed with Authenticode. Since classes cannot be signed with digital signatures, their immediate container is signed, that is, the DLL file.

Click here for larger image

Figure 7. A CLSID InprocServer key's entries for an unmanaged code COM class

How Managed Code Is Different

.NET technology introduces the concept of assemblies as the fundamental executable unit. Assemblies may be executables (.exe) or dynamic-link libraries (.dll), and may consist of multiple files. It's the logical unit where compiled code targeted at .NET is stored. An assembly contains all the information about code, types, and resources needed for a program to run. Compiling a Microsoft Visual Basic .NET or C# class library project produces an assembly containing the Microsoft intermediate language (MSIL) code.

The execution of the MSIL code is handled by the common language runtime. The common language runtime is different from other execution environments; to achieve optimal performance, the common language runtime compiles the MSIL into machine code. This machine code then runs without the major overhead of an interpreter.

In short, managed code is different because its execution is managed by the common language runtime. Therefore calls to COM servers will fail to execute if the DLL path as shown in Figure 1 points directly to the location of an assembly. The assembly, which is in MSIL code, has to be executed by the common language runtime. Hence, there is a need to launch the execution environment, which is the common language runtime.

This is achieved by placing a special DLL named mscoree.dll, as the default value of InprocServer32. To register an assembly for COM interop operation, the RegAsm (register assembly) utility is used. This utility registers .NET components type information into the system registry so that COM clients can access it, exposing the managed components to the unmanaged COM environment.

(Alternatively, you can set Register for COM Interop property to True on the Build page of the project's properties before compiling the assembly. See Registering Assemblies with COM for more information about RegAsm.)

The most interesting part is the default value for the InprocServer32 key (see Figure 8). Instead of the path to a managed code DLL file, it points to the mscoree.dll—the main entry point of the common language runtime engine. (The InProcServer32 key is created by the RegAsm utility earlier. The default value is set to mscoree.dll.)

Click here for larger image

Figure 8. A CLSID InprocServer key's entries for an managed code COM class exposed through COM interop

Here is why signing an assembly using Authenticode does not help. Since the InprocServer32 key points to mscoree.dll, Office XP will examine mscoree.dll for the signature and not the assembly itself. mscoree.dll is a system component and is not signed. At such, when the Office XP security is set to:

  • High with Trust all installed add-ins and templates disabled, the mscoree.dll won't be loaded
  • Medium with Trust all installed add-ins and templates disabled, users will be prompted to either enable or disable mscoree.dll

It should be noted that if there are several managed components registered as Office XP applications COM add-ins, a user will be asked to enable mscoree.dll as many times as there are managed COM add-ins. However, a user will not get any indication as to which COM add-in he is prompted to enable or disable.

Security Considerations

Let's say a user's Office XP security settings is at its highest, that is, the security levels set to High with the Trust all installed add-ins and templates check box cleared. In this scenario, as discussed earlier, an unsigned COM add-in won't be loaded at all (see Table 1). A signed COM add-in will be loaded silently if the digital certificate used to sign it is in the Trusted Sources list. If not, then the user is prompted to either enable or disable the COM add-in.

User will have to opt in and explicitly trust the COM add-in to be able to run it. Once the COM add-in is loaded and therefore allowed to run, it basically can do whatever the COM add-in code and whatever component the COM add-in calls into says to do. Office XP will not do any security checks on the calls made from within the COM add-in once the COM add-in is loaded and allowed to run.

But not all COM add-ins are truly secure. In the COM add-in code (the dedicated shim DLL) presented above we load, and execute a class from another module (a module in a managed component in this case) without verifying the integrity of that assembly, that is, there are no verification done to check whether the assembly has been tampered with or not. Indeed, there is no simple way to do such verifications without explicitly hosting the common language runtime as presented in the following dedicated shim article: Deployment of Managed COM Add-Ins in Office XP.

To help you better understand some of the security concerns with using the implementation technique presented in this article, we will use an analogy.

In this analogy we'll compare the Office security model to a private party. There is a security guard at the entrance to this particular party. The security guard checks the identities of the guests and only those who are on the guests list (that is, trusted and approved by the host) are allowed in. The security guard will not check the items that the guest brings with him because he trusts the person after he has presented significant evidence of his identity.

Let's suppose that one of the guests brought some cool toys that he intends to show at the party just for fun without any malicious intent. At some instance while this particular guest is partying, he takes out these cool toys and started showing them to everyone. It is all part of the fun, what he thinks a party is supposed to be.

Now imagine if on the way to the party someone maliciously substituted his toys with some very harmful toys. Since no one checks whether it is the original toys or not, it can turn out very dangerous.

Very similar things can happen to Office COM add-ins. Whether Office XP will load a COM add-in (signed or unsigned) or not depends on a user's security level settings as shown in Table 1 above. But once they are allowed to run, no one watches what they are doing and it is completely the responsibility of the add-ins to check that they use only approved components. The COM add-in is like the "trusted guest" in the analogy, with the managed code being the "original toys" and the "substituted toys" being the maliciously substituted managed code.

Having said that, the probability of such an attack happening is quite small. This is because after an assembly has been installed and registered on a machine, to launch such an attack, an attacker will need to be able to access the physical disk on the said machine to replace the assembly with that of his own. And as such, if a malicious attacker manages to compromise a machine and get access to the local disk, he can essentially perform much more malicious acts such as doing an actual replacement of the Excel.exe with his own malicious version of the Excel application.

All of the security options available in Office XP and the Visual Basic 6.0 and simple unmanaged Visual C++ shim solutions rely on the fact that the registry and the file system are secure. If an attacker can modify (or replace) registry entries or DLLs on your machine, they can circumvent the security mechanisms (for example, modify the registry key so that Office security is set to low). It is important to not run as an administrator on a computer unless absolutely necessary, and to never download and run code from untrustworthy sources.

If you look at The Ten Immutable Laws of Security, you will see that if you download and run someone else's program on your computer, it is already too late. And if you don't run as an administrator (which is highly recommended), you can't even install an add-in by mistake.

The Visual Basic 6.0 and simple unmanaged Visual C++ shim solutions are as secure as your registry and file systems are. The choice whether to use the presented technique is yours. You have to make an informed assessment and decide whether your organization can take advantage of the simplicity of this technique. If your organization prefers all add-ins verified before they are allowed to run, then the complex dedicated shim described in the following articles is the way to go. See the following articles for more information:

It is also important to note that the security considerations discussed in this section are not unique to just when you use the Visual Basic 6.0 and simple unmanaged Visual C++ shims technique to invoke managed code. It is similar to a VBA macro or unmanaged COM add-in (signed and trusted) today calling other unmanaged COM add-ins (signed or unsigned) or ActiveX® controls. Office XP will only verify the signature on the VBA macro or unmanaged COM add-in to be loaded. Office XP does not do any signature verification on the unmanaged COM add-ins or ActiveX controls being called.

Also, the complex dedicated shim is another example of a "defense in depth" tactic. If a malicious attacker manages to compromise a machine that has permission to modify the registry settings, even if the complex dedicated shim is used, it is still essentially, "game over". The malicious attacker can do much worse damage than just substituting an assembly.

Whichever solution you choose, the importance of securing the registry and file systems cannot be emphasized enough.

Additional Points to Consider

A few important points to remember and note:

  1. Outlook 2002 It should be noted that the security model for Microsoft Outlook® 2002 differs from other Office XP applications. In Outlook 2002, whether an Outlook COM add-in will be loaded or not is not determined by a user's security settings described in Table 1. In addition to a user's security settings, COM add-ins (regardless of whether they are signed or not) are subjected to the restrictions on certain Outlook 2002 object model property and method calls imposed by the Outlook E-mail Security Update that is an integral component of Outlook 2002.

    For an Office COM add-in to have unrestricted access to the Outlook 2002 object model, the COM add-in must also be added to the Trusted Code page of the Administrative Form. To add an Outlook COM add-in developed with Visual Studio .NET to the Trusted Code page of the Administrative Form, you must develop your Outlook managed COM add-in with the COM add-in shim discussed in this article. You then add the shim DLL to the Trusted Code list. Do not add mscoree.dll to the Trusted Code page in the Administrative Form.
    For more information on the Outlook E-mail Security Update and the Administrative Form in the Outlook Security Settings Public Folder, see the following whitepapers and Knowledge Base (KB) articles:

    Randy Byrne's Using the COM Add-in Shim to Trust Outlook Add-ins Built with Visual Studio .NET article is a must read on how to use a shim in Outlook 2002.

  2. Parallel development Even after adding the COM add-in shim to your assembly project, you can still continue developing your COM add-in assembly. The content of your assembly doesn't have to be frozen. Development can continue in parallel.

  3. Code signing The shim DLL must be signed with Authenticode.

  4. Test certificates In most organizations, the private key used for signing an unmanaged component is accessible only to authorized employees. In most cases, software developers do not belong to this category. However they would still need to test the software with it signed. This can be achieved using testing certificates. For details on how to create and sign using test certificates, the Digital Code Signing Step-by-Step Guide article has detailed step-by-step instructions and explanation on how to do this using Authenticode test certificates.

  5. Smart tags The steps for developing a smart tag shim in Visual Basic 6.0 or unmanaged Visual C++ is very similar to the way an Office COM add-in is implemented. Instead of implementing the IDTExtensibility2 interface in step 3 of The Solution: A Dedicated Unmanaged Visual Basic 6.0 or Visual C++ Shim Component section, you implement the ISmartTagRecognizer and ISmartTagAction interfaces respectively instead.

  6. To or not to Enable "Trust all installed add-ins and templates"? The Trust all installed add-ins and templates check box (see Table 1) is commonly misunderstood. By default it is enabled, but Microsoft recommends that customers with very high security requirements disable it, and this is a good "defense in depth" approach.

    If you have no need to run personal macros or unsigned COM add-ins, you should turn off this option. As mentioned earlier when Office XP security level is at High, with Trust all installed add-ins and templates option disabled, all add-ins and templates that are not code signed will automatically be disabled. And if the add-ins and templates are code signed with a certificate not listed in the Trusted Sources list, a user will be prompted to either enable or disable the add-ins or templates.

    Enabling Trust all installed add-ins and templates option will allow all COM add-ins that have been installed in the registry (which requires administrative privileges) or VBA macros that are stored in your personal or workgroup locations to run, regardless of whether they are code signed or not.
    It should also be noted that end-users do not need administrative privileges to install VBA macros to certain template and startup folders. Examples of these locations in Word 2002 are:

    • \Documents and Settings\<user name>\Application Data\Microsoft\Word\STARTUP
    • \Documents and Settings\<user name>\Application Data\Microsoft\Templates

    The way you can be attacked with the Trust all installed add-ins and templates option enabled is if you download, register, and run a malicious COM add-in or run a malicious template from someone else.

    If you decide to run personal VBA macros or to run locally installed COM add-ins, and you don't want or can't afford a digital certificate, and don't want to use the Trust all installed add-ins and templates option, here is how you can do it. Set your Office XP security level to Medium with Trust all installed add-ins and templates disabled. With this particular security setting, you will be prompted to either enable or disable a COM add-in or VBA macro when the application is launched.

Conclusion

The Visual Basic 6.0 and simple unmanaged Visual C++ shim solutions implementation is quite straightforward. The decision whether to use this technique to install and deploy managed components for Office XP depends on how your organization computer network security administration is set up. The Visual Basic 6.0 and simple unmanaged Visual C++ shim solutions are as secure as your registry and file systems are. Your organization has to make an informed assessment and decision as to whether it can take advantage of the simplicity of this technique.

For more information about Office XP security, .NET and COM Interop, the .NET Framework, .NET security, PIAs, and so forth, here is a list of references: