0

I wrote a service which will run as the local system. The service has a COM object. I'd like to create and access that object from any local user's account.

Of course, it fails during a call to CoGetClassObject() in order to get the class factory. It fails with (debugger watch):

hr  0x80080005 : Server execution failed    HRESULT

The ATL framework is registering the class factories on startup. I've stepped through the code and verified they are being registered with CLSCTX_LOCAL_SERVER and REGCLS_MULTIPLEUSE.

The service is running. I can't find any useful examples to solve this problem. I am thinking it is probably just a registry setting or possibly the way I call CoInitializeSecurity().

I call CoInitializeSecurity() like this:

class CCppServiceModule : public ATL::CAtlServiceModuleT< CCppServiceModule, IDS_SERVICENAME >
{
public :
    DECLARE_LIBID(LIBID_CppServiceLib)
    DECLARE_REGISTRY_APPID_RESOURCEID(IDR_CPPSERVICE, "{3a266de3-e432-4269-af44-5f76fdd0231f}")
    HRESULT InitializeSecurity() throw()
    {
        // TODO : Call CoInitializeSecurity and provide the appropriate security settings for your service
        // Suggested - PKT Level Authentication,
        // Impersonation Level of RPC_C_IMP_LEVEL_IDENTIFY
        // and an appropriate non-null Security Descriptor.

        HRESULT _hr = S_OK;
        _hr = CoInitializeSecurity((PSECURITY_DESCRIPTOR)&LIBID_CppServiceLib, -1, NULL, NULL, 0, RPC_C_IMP_LEVEL_IDENTIFY, NULL, EOAC_APPID, NULL);
        return _hr;
    }
    
    // more stuff ...
};

In the above call, LIBID_CppServiceLib and the AppID of the service are the same...the wizard created them that way. Originally, I had a different call to CoInitializeSecurity() when I was registering solely as a local server and not a service. I could get it to run when registering as a local server, but I need it to run as a server under the System account.

The .rgs files for the service and the COM object look like this:

CppService.rgs:

HKCR
{
    NoRemove AppID
    {
        ForceRemove {3a266de3-e432-4269-af44-5f76fdd0231f} = s 'CppService'
        {
            val LocalService = s 'CppService'
            val ROTFlags = d '1'
            val RunAs = s 'nt authority\system'
            val AuthenticationLevel = d '1'
        }
        ForceRemove CppService.exe
        {
            val AppID = s '{3a266de3-e432-4269-af44-5f76fdd0231f}'
        }
    }
}

TestObject.rgs:

HKCR
{
    CppService.TestObject.1 = s 'TestObject class'
    {
        CLSID = s '{7594592c-ddc5-47f3-a2df-9cc397988fd0}'
    }
    CppService.TestObject = s 'TestObject class'
    {       
        CurVer = s 'CppService.TestObject.1'
    }
    NoRemove CLSID
    {
        ForceRemove {7594592c-ddc5-47f3-a2df-9cc397988fd0} = s 'TestObject class'
        {
            AppID = s '{3a266de3-e432-4269-af44-5f76fdd0231f}'
            ProgID = s 'CppService.TestObject.1'
            VersionIndependentProgID = s 'CppService.TestObject'
            ForceRemove Programmable
            LocalServer32 = s '%MODULE%'
            {
                val ServerExecutable = s '%MODULE_RAW%'
            }
            TypeLib = s '{3a266de3-e432-4269-af44-5f76fdd0231f}'
            Version = s '1.0'
        }
    }
}

When I originally verified it worked when running as a local server, the CppService.rgs file was just a plain HKCR {} and the entry under CLSID for the object did not have the AppID key. As I said, originally it all worked fine when running as a local server, so the beginning COM plumbing worked fine, and I have many, many years working with COM. Maybe this isn't doable as COM. I could probably solve my problem with a WCF service in C#, but I wanted to do it in COM so I wouldn't have to listen on a port or named pipe.

I've seen comments in some places where people have registered the class factory in the ROT with the ROTREGFLAGS_ALLOWANYCLIENT flag, and then manually tried to connect to the class factory via the ROT. But, Microsoft's documentation says that my AppID value of ROTFlags being equal to 1 is supposed to have the same effect...but doesn't seem to.

I'm not calling CoInitializeSecurity() in the client application. Ideally, I'd like to be able to create with a simple vb script like:

set obj = CreateObject('CppService.TestObject')

Is this a solvable problem and I'm just missing something critical, or is it impossible?

Repo at: https://github.com/willcoxson/CppService

Joseph Willcoxson
  • 5,853
  • 1
  • 15
  • 29
  • Do you have any reproducing project available? – Simon Mourier Dec 30 '21 at 16:19
  • @SimonMourier I suppose I could try to put something on GitHub or whatever is the preferred way. Something like this you can't put in a small reproducible snippet on SO. – Joseph Willcoxson Dec 30 '21 at 16:21
  • @SimonMourier Take a look at https://github.com/willcoxson/CppService – Joseph Willcoxson Dec 30 '21 at 16:34
  • I don't think you can do this with "Local System" (why do you need this one? https://stackoverflow.com/a/510225/403671). I'm 100% sure it can work with "Local Service" because it works fine if you host any COM DLL in COM+ (dllhost surrogate w/o effort) like this: https://i.imgur.com/WVPQLNF.png (in fact I personally recommend everyone to stop writing .exe COM server/services but simply always write DLLs and host them within COM+ services which do the ground work for free). And BTW, in this screenshot, you will see that you can't use "Local System". – Simon Mourier Dec 30 '21 at 18:54
  • @SimonMourier Maybe you're right. I have found a method since my post to register the class factory with the ROT and then manually get the class factory from the ROT to create an object in the service. But, that is not straight forward. The client application has to do the same thing. Reason for Local System? I need to modify some info under a user's HKCU key that is protected. The user himself cannot edit it. I do not believe Local Service would be granted access either. It's for white hat security stuff (post logon 2FA). – Joseph Willcoxson Dec 30 '21 at 19:14

0 Answers0