Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

COM delegation using the COM channel hook mechanism

0.00/5 (No votes)
27 Apr 2000 1  
Allows a low-privileged COM client to delegate calls to a COM server that is running under a higher-priveleged NT user account.
  • Download source files - 40 Kb
  • In one of my projects I needed a delegation feature for COM. The server was running under a high-privileged account and needed to issue COM calls on behalf of low-privileged clients to another COM server. After searching in MSDN I found that the current situation is pretty simple: "COM does not support delegation". Although named pipes in the Windows NT environment support it for single machine operations - COM does not. To overcome this situation I created my own user impersonation mechanism based on the usage of the COM channel hook interface.

    The idea behind the COM channel hook mechanism is described in the very good and comprehensive way in January'98 issue of MSJ. In short, an application could register a COM callback interface that will be called by the COM engine when it sends or receives a call. So The application can add whatever information it needs in a way that is transparent to the user.

    The callback interface IChannelHook consists of the following functions:

    void ClientGetSize(REFGUID uExtent, 
                       REFIID riid, 
                       ULONG *pDataSize);
    
    void ClientFillBuffer(REFGUID uExtent, 
                          REFIID riid, 
                          ULONG *pDataSize, 
                          void *pDataBuffer);
    
    void ClientNotify(REFGUID uExtent, 
                      REFIID riid, 
                      ULONG cbDataSize, 
                      void *pDataBuffer, 
                      DWORD lDataRep, 
                      HRESULT hrFault);
    
    void ServerNotify(REFGUID uExtent, 
                      REFIID riid, 
                      ULONG cbDataSize, 
                      void *pDataBuffer, 
                      DWORD lDataRep);
    
    void ServerGetSize(REFGUID uExtent, 
                       REFIID riid, 
                       HRESULT hrFault, 
                       ULONG *pDataSize); 
    
    void ServerFillBuffer(REFGUID uExtent, 
                          REFIID riid, 
                          ULONG *pDataSize, 
                          void *pDataBuffer, 
                          HRESULT hrFault);
    

    When a COM client issues call to server, first ClientGetSize is called on the client's side to determine size of additional data and then ClientFillBuffer to fill buffer with actual data. When COM call arrives to the server - server side gets notified about arrived data through ServerNotify function. Functions ServerGetSize and ServerFillBuffer are called on the way back on the server's side and, finally, ClientNotify when the call returns on the client's side.

    To make delegation possible I attach the following information to the COM hook buffer:

    • The process ID of the process that issued a call
    • The network name of the computer that issued a call
    • The user token of the thread that issued a call (if it exists) or the process-wide user token (if it does not)

    When the data arrives on the server's side the code first checks whether the client and server reside on the same machine using the transferred machine name. This is because the user token that we have transferred makes sense only on the same machine. If the machine is the same, the server tries to open a process that issued a call and duplicate the token for it to be valid for the server's process. Then it uses the duplicated token to call ImpersonateLoggedOnUser function to do actual user impersonation.

    All information about incoming calls is kept in the thread local storage (TLS) on a per-thread basis. To avoid losing information in case of recursive COM calls, the information about incoming calls is kept as a stack and every new call pushes its new context on the stack and removes it on the way back to the server.

    The COM channel hook will be installed automatically as long as DLL is loaded into memory of the process. This could be done either by an explicit call to the LoadLibrary function or implicitly by a CoCreateInstance call creating IClientInfo interface.

    For the proper functionality of the discussed user impersonation method the channel hook must be installed both in the client and server processes. For my project I managed to do it automatically for the client and for the server by initializing the interface to the IClientInfo from the Proxy-Stub DLL that is automatically generated by MSVC Wizard. To do so you need to create a new "c" file that looks like this :

    //CODE-----------------------  (file rcserverps_dllmain.c)--------------------------
    
    
    #include <windows.h>
    
    #include "..\COMchannelinfo\dcom_channel_info.h"
    
    #include "..\dcomchannelinfo\dcom_channel_info_i.c"
    
    
    //previous DLLMain function 
    
    BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved);
    
    BOOL WINAPI newDllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved)
    {
        HRESULT hr;
        static IClientInfo* client_info_iface;
    
        switch (fdwReason)
        {
            case DLL_PROCESS_ATTACH:
                client_info_iface = NULL;
                hr = CoCreateInstance(&CLSID_ClientInfo, NULL,
                                      CLSCTX_INPROC_SERVER, &IID_IClientInfo, 
                                      (void**)&client_info_iface);
                if (FAILED(hr))    
                {
                    return FALSE;
                }
                break;
        
            case DLL_PROCESS_DETACH:
    
                __try
                {
                    if (client_info_iface != NULL)
                    {
                        client_info_iface->lpVtbl->Release(client_info_iface);
                    }
                } __except (1)
                {
                    //we do not care if something has happened on shutdown
    
                };
                break;
        }
        
        return DllMain(hinstDLL, fdwReason, lpvReserved);
    }
    
    //CODE-----------------------------------------------------------------------------
    
    

    And then patch a generated make-file for your proxy-stub DLL (usually it has the name of the project + "ps.mk" at the end) and make the following changes to it:

    //CODE-----------------------------------------------------------------------------
    
    rcserverps.dll: dlldata.obj rcserver_p.obj rcserver_i.obj rcserverps_dllmain.obj (this is a new object file)
    link /dll /out:rcserverps.dll /def:rcserverps.def /entry:newDllMain \ (this is new DLL entry point) 
          dlldata.obj rcserver_p.obj rcserver_i.obj rcserverps_dllmain.obj \(this is a new object file)
          kernel32.lib rpcndr.lib rpcns4.lib rpcrt4.lib oleaut32.lib uuid.lib ole32.lib (additional standard library)
    //CODE-----------------------------------------------------------------------------
    
    

    The effect of this operation is the following: as soon as you make a first call to your COM interface the proxy-stub DLL will be loaded automatically by the COM engine. In its DLLMain it loads and installs our COM channel hook that transfers the required information about the called user automatically. You have an advanced impersonation routine for free! :)

    Using the interface in applications.

    You need to register the DLL with the call to regsvr32.exe dcom_channel_info.dll.

    Source, Demos, updates and legal stuff.

    The latest source and updates for the application can be found here.

    The Interface itself is used in the Remote Control application (in its server part) which can be used as an example on usage of this impersonation method. It also could be found here.

    You can drop an e-mail to author here.

    Big thanks to Don Box for pointing out an elegant solution to my problem.

    License

    This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

    A list of licenses authors might use can be found here