Issue:
I successfully call CoSetProxyBlanket on a proxy (if that's the right term for it) and then I call QueryInterface on that same proxy, but I receive a result of 0x80070005 ("Access Denied"). However, if I first call CoInitializeSecurity (which I am trying to avoid) with that same credentials then the call succeeds.
Question:
How can I successfully get the interface I need without having to call CoInitializeSecurity? From what I understand, a process can only call this method once so its not compatible with making a dll and can usually be substituted with calls to CoSetProxyBlanket.
Details:
I am experimenting with building my own OPC client that can communicate to computers running on different domains without matching user accounts.
First, I create an identity structure with a domain, username, and password that are valid on the server:
COAUTHINFO authInfo;
COAUTHIDENTITY authIdentity;
authIdentity.Domain = (unsigned short *) w_domain;
authIdentity.DomainLength = wcslen( w_domain);
authIdentity.Flags = SEC_WINNT_AUTH_IDENTITY_UNICODE;
authIdentity.Password = (unsigned short *) w_password;
authIdentity.PasswordLength = wcslen(w_password);
authIdentity.User = (unsigned short *) w_username;
authIdentity.UserLength = wcslen(w_username);
authInfo.dwAuthnLevel = RPC_C_AUTHN_LEVEL_CALL;
authInfo.dwAuthnSvc = RPC_C_AUTHN_WINNT;
authInfo.dwAuthzSvc = RPC_C_AUTHZ_NONE;
authInfo.dwCapabilities = EOAC_NONE;
authInfo.dwImpersonationLevel = RPC_C_IMP_LEVEL_IMPERSONATE;
authInfo.pAuthIdentityData = &authIdentity;
authInfo.pwszServerPrincName = NULL;
ServerInfo.pAuthInfo = &authInfo;
Then I am able to call CoCreateInstanceEx
with this server info an obtain a handle (m_IOPCServer
) to my OPC server (IID_IOPCServer
).
After I obtain the handle, I've found that it is necessary to once again set more permissions (see How does impersonation in DCOM work?) with this call:
hr = CoSetProxyBlanket(m_IOPCServer, RPC_C_AUTHN_WINNT, RPC_C_AUTHZ_NONE,
NULL, RPC_C_AUTHN_LEVEL_CALL, RPC_C_IMP_LEVEL_IMPERSONATE,
&authIdentity, EOAC_NONE);
After this I am able to successfully obtain a handle to a OPC Item Group:
hr = m_IOPCServer->AddGroup(L"", FALSE, reqUptRate, clientHandle,
NULL, NULL, lcid, &m_hServerGroup, &revisedUptRate,
IID_IOPCItemMgt,(LPUNKNOWN*)&m_IOPCItemMgt);
However, when I try to use this code:
hr = m_IOPCItemMgt->QueryInterface(IID_IOPCSyncIO, (void**)&m_IOPCSyncIO);
The result is 0x80070005 ("Access Denied"). This is the case even if I successfully call CoSetProxyBlanket on m_IOPCItemMgt. However if I first call CoInitializeSecurity, then the call succeeds.
I believe the issue related to How does impersonation in DCOM work? in that the QueryInterface function is a form of object creation so it doesn't use the same security as the other method calls like AddGroup. However in the Microsoft reference QueryInterface, under notes to implementer, it makes it sound like QueryInterface shouldn't be checking ACLs and under return values, Access Denied is not mentioned as a possibility. I don't think that this issue is implementation specific though because I have tried my code on some well known commercial OPC servers (e.g. Matrikon Simulation Server) as well as the opensource LightOPC which doesn't implement any extra security.
I am guessing that what I need to do is to find a way to replicate this command
hr = m_IOPCItemMgt->QueryInterface(IID_IOPCSyncIO, (void**)&m_IOPCSyncIO);
but do so while also supplying authIdentity
. Is this possible? Can it be done with CoCreateInstanceEx or CoGetClassObject or some other COM call?
Without going into too much detail: CoInitializeSecurity is always invoked at least once per process. This can be done implicitly or explicitly. If your code doesn't make an explicit call, the DCOM runtime does it for you with parameters populated from the registry. You can try to tweak the appropriate registry values to force DCOm using values similar to those used in your explicit call. The registry key that holds those values is "HKEY_LOCAL_MACHINE\SOFTWARE\Classes\AppID{AppID_GUID}" This key is described here:https://msdn.microsoft.com/en-us/library/windows/desktop/ms693736(v=vs.85).aspx
You have to call CoSetProxyBlanket
on every new COM object instance, so in your case you have to call it even for m_IOPCItemMgt
.
You need to call CoSetProxyBlanket
in the IUnknown
interface before using QueryInterface
CComPtr<IUnknown> pUnknown;
hr = m_IOPCItemMgt->QueryInterface(IID_IUnknown, (void**)&pUnknown);
hr = CoSetProxyBlanket(pUnknown, RPC_C_AUTHN_WINNT, RPC_C_AUTHZ_NONE,
NULL, RPC_C_AUTHN_LEVEL_CALL, RPC_C_IMP_LEVEL_IMPERSONATE,
&authIdentity, EOAC_NONE);
hr = pUnknown->QueryInterface(IID_IOPCSyncIO, (void**)&m_IOPCSyncIO);
You can check this for more info.
User contributions licensed under CC BY-SA 3.0