QueryInterface fails with E_ACCESSDENIED - c++

The following method is a DCOM server method. The COM client and server is running in different WinXP machines. The COM client calls RegisterClient method to register callback interface. The problem is QueryInterface method fails with error code E_ACCESSDENIED. What could be the reason for the issue?
STDMETHODIMP CGEMExtension::RegisterClient(IUnknown** ppGEMExtensionEvents, int* nClientId, int* nResult)
{
HRESULT hRes = (*ppGEMExtensionEvents)->QueryInterface(IID_IGEMExtension,(void**)&pUnknown);
return hRes;
}

When you get an E_ACCESSDENIED, it means you have a permissions problem (don't waist your time on firewalls or registrations - the former will raise errors telling you the service is not available, and the latter will tell you the class is not registered or so). COM relies on Windows permissions, so this is what you should focus on.
In your case, if I understand the case correctly, the server actually calls the client, in order to get the right interface. For that, the user running the server should have the right permissions on the client side. A few suggestions:
As daramarak suggests, have the server and the client use the same domain user, or the same local user with the same password.
On the client, set this setting to "classic".
Give the server's user, if known to the client, additional permissions using DCOMCNFG.

This might be because the correct permissions is wrong on the other computer. Simplest way to check this is to turn on logging with secpol (Local Policies, Audit policy, turn on logging of logon events and object access) then you can see if you are trying to access the other machine.
If you are just testing then I would suggest to use the setting "run as interactive user" on the com object in component services and make sure that you have the same user with the same password on both machines. Then you must be running as the common user on the client machine. Spesifically setting the user to the common user is also possible.
As a general advice to debugging DCOM connectivity: Turn off all firewalls and such to make sure that the connection is working, then turn on security measures one by one, making sure that you leave the correct ports open and that the correct users have the correct permissions.

I give you my experience even if it may not apply directly to your specific case.
On Windows 7 at 64bit I have an exe compiled with x64 and a dll compiled at 32 bit.
A COM object lives inside the dll.
The exe (launched by the "normal" user) creates the COM object (on the same computer) asking for IUnknown and the creation is successful. Then the exe asks for a different interface through QueryInterface and it fails with E_ACCESSDENIED.
If I launch the exe "as administrator" then the QueryInterface will return with S_OK.
I did not investigate further, I suspect there is some policy about the 32 bit - 64 bit interaction.

Related

Debugging Windows service failure before main() (error 1053 when run by LocalSystem user)

I have a Windows service that works perfectly when run via a user account, but fails with error 1053 when run by the Local System account. Unfortunately, whatever is going wrong happens before my main() function is called, which means I can't even insert logging calls to track down where things go wrong.
How can I debug Windows service startup failures when it's happening in pre-main() code?
Any ideas why it might fail specifically when it's run by the Local System user?
NOTE: Not sure if it's relevant, but I'm compiling with GCC, and the code depends on Cygwin.
This won't be complete answer because without actually having event log it would not be possible.
Since this happens pre-main this would be related to what your user account can do and what local system cannot.
In service property panel, for Local System account you have "Allow Service To interact with desktop". Many times this can be the reason.
The other of course is, your service is loading some libraries which are off limits to it - because of network\group security (local system cannot access network so far as I know).
You will need to eliminate one piece at time to see what happens. Or other way round - make service empty remove all dependencies and play 20 questions.

How to make existing DCOM (OPC) application run as a service?

Background:
I have used developed an OPC server based on LightOPC (https://github.com/Sayen/LightOPC). This works perfectly fine as a local executable. The only problem is that I want multiple clients to connect to the same instance of the exe so they can share data. Currently, even if the DCOM settings are such that it runs as a specific user, it seems that sometimes multiple instances of the exe start. The only solution has been to set it to run as the Interactive User. However this has an issue where it won't run if no user is logged in. I believe the right way is to make it run as a windows service.
Question:
How can I take my DCOM local executable and make it into a service?
Things I tried:
Based off of this question: Create Windows service from executable I used the NSSM( the non-Sucking Service Manager ) to make my exe into a service name MYOPCSERVICE.
Then based on some other googling and examining other OPC servers that run as services, I modified the following registry key:
HKEY_LOCAL_MACHINE\SOFTWARE\Classes\WOW6432Node\AppID{XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX} (where X's are my AppId) and added a "Local Service" key with the value of "MYOPCSERVICE".
After doing this, when I used DCOMCNFG, my DCOM Application shows up as Application Type = Local Service.
However, after adding this registry key, when I try to start the service or connect to the OPC server, the service fails to start with "CoRegisterClassObject() failed. Exiting..."
I found this document: https://learn.microsoft.com/en-us/windows/win32/api/combaseapi/nf-combaseapi-coregisterclassobject which has the following:
As of Windows Server 2003, if a COM object application is registered
as a service, COM verifies the registration. COM makes sure the
process ID of the service, in the service control manager (SCM),
matches the process ID of the registering process. If not, COM fails
the registration. If the COM object application runs in the system
account with no registry key, COM treats the objects application
identity as Launching User.
I don't know if this is the issue, and I also don't really understand what it means. What is the "process ID" being referred to? Is this the 1-4 digit integer that all Windows processes have? Or is this the name of the service and does it have to match the name of the executable or the class or the AppId?
Update:
I have been experimenting more, and I am starting to get the feeling that it isn't possible to use NSSM to make the COM executable into a service. It seems like the exe of the service needs to be the one that calls CoRegisterClassObject. I have made a simple service based off of Simple Windows Service in c++ https://www.codeproject.com/script/Articles/ViewDownloads.aspx?aid=499465 and when I do so, I can successfully call CoRegisterClassObject with the AppId of {XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX} if this AppId registry key contains the string LocalService = MYOPCSERVICE.
I think Windows is enforcing a rule that only the exe registered as a service (which is nssm.exe) is allowed to call CoRegisterClassObject. However nssm spawns another exe (the local OPC executable) and it doesn't pass along this ability. Does this sound accurate? Is there any work around besides having to write all my own service handlers?

DCOM interoperability between Windows XP and Windows 7

I am facing a rather strange and very specific DCOM related problem and I am hoping someone might have encountered it and solved it.
I am trying to instantiate a COM object in an EXE server on a Windows 7 machine (call it W7). The client resides on a Windows XP machine (call it WXP). On WXP the logged-in user is a domain user. On W7 the user is a local user. I have (afaik) correctly set all the DCOM rights, authentication and account privileges. There is no firewall involved.
All I get is that the COM EXE server process is started on W7, with the username I expect, but does not seem to even reach its WinMain function and remains hanging and never dies unless I kill it. I can attach a remote debugger (Visual Studio 2010) to it which will warn me that the process might be deadlocked, and when I break it, it stops in a message queue loop (GetMessage/Dispatch).
The client gets a (seemingly valid) pointer but any attempt to use it, results in E_ACCESSDENIED.
If anything from the scenario above is changed, the instantiation of the COM object succeeds and the object behaves correctly.
I know the chance is slight to find an answer but any tip is extremely welcome.
Thanks.
DCOM client and server either needs to be both the same local administrator on workgroup, or domain users on the same domain.
You can use this test app to check if your two machines are configured correctly:
http://support.microsoft.com/kb/259011
This way you make sure that your machine's permission and firewall are setup properly first without your own code.
Answering my own question...
It turns out that in the client CoInitializeSecurity didn't have all credentials it needed after all... It was called too early, before the credentials were known.
I discovered this after using CoSetProxyBlanket (as described here: How does impersonation in DCOM work?) on each component I was instantiating. Every component on which I called CoSetProxyBlanket was correctly working. This triggered me to go and double check the CoInitializeSecurity.
It remains strange that the reverse connection (from W7 to WXP) worked, but this is another research I need to do. The current question can be closed.

Is it possible to lower the privilege level when calling CoCreateInstance on Vista?

Okay, I have a plugin for IE that when installed needs to (with the user's permission) restart IE.
To do this I have a DLL that is invoked by the installer. And it works, but the problem is that when IE is restarted on Vista, it is restarted with the administrator privileges of the installer, which is a problem for a number of reasons.
I'm using CoCreateInstance to start IE, so that I get an instance of the IWebBrowser2 interface in order to perform some actions on it.
So my question is, is it possible to call CoCreateInstance from an application that is running with Administrator privileges, in such a way that the resulting COM object instance inherits the base user privileges rather than the administrator privileges of the calling application?
Okay, I found the solution from here:
http://social.msdn.microsoft.com/Forums/cs-CZ/ieextensiondevelopment/thread/78a2bc18-1920-4e58-af7e-48dbcebe7643
From my installer DLL I need to launch a new thread, and impersonate the current user on that thread, and then set a low integrity level, and create the COM instance with the CLSCTX_ENABLE_CLOAKING context.

Alternatives to LogonUser for network impersonation (C++)

Are there any alternatives to LogonUser and for impersonating given account in order to access network resources? I'm looking for the method of impersonation which would let me connect to machine in foreign domains (or, workgroup machines for the same matter).
For initial data I have: machine name, username (or domain\username), cleartext password.
I know there's a way to establish connection using WNetAddConnection to a \\machinename\ipc$, then most network functions will run in a context of that account, however win2008 added another twist and some functions still use the account, that thread is running under.
I'm also aware, that there's some way to get an impersonation token using SSPI. Have anyone experimented with those tokens, are they good for accessing shares, SCM, remote registry and stuff? Is is what WNetAddConnection is using?
EDIT: To clarify, the reason I cannot use LogonUser is because I need to impersonate user in a non-trusted domain or workgroup
EDIT2: Another clarification: the item I'm trying to implement is similar to psexec, e.g.:
program should not modify host or active directory configuration (e.g.: create temporary local users, etc). Moreover assumption cannot be made that it is running on DC or not
there can be no assumptions made about which software is pre-installed on the remote host, only condition given is that windows file sharing is enabled on target
Account/password is known to be working on target, but target machine may be in local domain, foreign domain, not in domain at all.
EDIT3: I would really love to hear more about SSPI InitializeSecurityContext / AcquireCredentialsHandle option. Is there anybody who has been working with this API extensively? Is it possible to use the tokens returned with impersonation, so that a thread can access network shares and copy files, etc? Can someone post a working code snippet?
EDIT4: Thanks to Marsh Ray, problem got resolved. If anyone is looking to see the proof-of-concept code, it is here
If you're wanting to "access network resources" outside of your forest, do that with WNetAddConnection2/3 as you mentioned, or use the standard RPC APIs with RPC_ C__ AUTHN__ GSS__ NEGOTIATE and and explicit credentials structure.
Normally, "impersonation" is something that happens on the server side. The server side will be able to impersonate the connection as the account you're connecting as.
But the key is this: impersonation only makes sense for impersonating an account the server can access in his local SAM/domain/forest directory. If the client and server are in different forests, they clearly can't agree on the SID of an account for an impersonation token (except for the case of well-known SIDs like Administrator which serve mainly to confuse this kind of thing), and that seems necessary to check against DACLs etc.
Perhaps what you want is to call LogonUserEx with the LOGON32__ LOGON__ NEW__ CREDENTIALS flag. This should succeed (even in a different forest - it doesn't actually authenticate the credentials you give it) giving you a token with the username/password you specified. You may have to use DuplicateToken to turn this into an impersonation token. Then you can use SetThreadToken to replace the token on your thread.
IMHO this isn't really "impersonation", you're just using the credentials outright, but it allows you to access network resources transparently as the arbitrary username/password you supply.
Edit: Oh yeah, be aware that there is no protection against man-in-the-middle on this type of connection. The client especially cannot strongly authenticate the server (short of heroics like IPSEC), so in theory you can't trust anything the server tells you.
The theory goes that you pass the credentials as a SEC_WINNT_AUTH_IDENTITY structure to the AcquireCredentialsHandle function that creates the handle used in InitializeSecurityContext. I never tried this on foreign domains though and I don't know if it works.
Doing this directly and reliably via the Windows API seems next to impossible, plus Windows does so much work behind the scenes to make network access "just work". Plus the impersonation side of things only works for the single thread that called the APIs.
But... you can run a whole program under a different user... such as when you run a service.
So you could edit the registry in your main program to run various services under different security tokens and use IPC/Sockets to communicate with those processes from your main application. ie. a whole bunch (or restarting and reconfiguring the same process) of helper processes running under the different user(s) which your main app abuses.
I realize this is a hack but it seems viable ;)
You could open a command line, map the drive using the plaintext username and password. Then disconnect the drive:
net use m: \\machinename\share password /user:username
... do stuff ...
net use m: /delete
http://technet.microsoft.com/en-us/library/cc756153(WS.10).aspx