I am trying to connect to a DCOM object on a remote machine which is logged in as a different user from a C# application. I have gotten my code working to connect to the same DCOM object while it is running on the same computer, like so:
MyDCOMType dcomObject;
Type remoteSessionContextType = Type.GetTypeFromProgID("ServerApp.MyDCOMType");
dcomObject = (MyDCOMType)Activator.CreateInstance(remoteSessionContextType);
string version = dcomObject.GetVersion();
MyDCOMType is a type implemented in the DCOM application (a VB6 app) that I added as a reference in my project. I have been able to create an instance of it in the C# code and call all of the methods with the expected results. Now I am trying to get it to connect to the same object on the remote machine. I am attempting to impersonate the user on the remote system like so (variable definitions and error handling omitted):
if (LogonUser(
userName,
domain,
password,
LOGON32_LOGON_NEW_CREDENTIALS,
LOGON32_PROVIDER_WINNT50,
out token) != false)
{
if (DuplicateToken(token, (int)_SECURITY_IMPERSONATION_LEVEL.SecurityDelegation, out tokenDuplicate) != false)
{
impersonationContext = WindowsIdentity.Impersonate(tokenDuplicate.DangerousGetHandle());
}
}
Which is then followed by similar code to create the object:
MyDCOMType dcomObject;
Type remoteSessionContextType = Type.GetTypeFromProgID("ServerApp.MyDCOMType", ipAddress);
dcomObject = (MyDCOMType)Activator.CreateInstance(remoteSessionContextType);
string version = dcomObject.GetVersion();
Except that the CreateInstance call throws an UnauthorizedAccessException error with error code 80070005 for unauthorized access.
The other thing is that I have C++ code that does the same thing that works perfectly. That code goes like so:
COAUTHINFO AuthInfo;
COAUTHIDENTITY AuthIdentity;
COSERVERINFO ServerInfo;
MULTI_QI Results;
HRESULT hr;
BSTR version;
_MyDCOMType *pSession = NULL;
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.dwReserved1 = 0;
ServerInfo.dwReserved2 = 0;
ServerInfo.pAuthInfo = &AuthInfo;
ServerInfo.pwszName = w_nodename;
hr = CoCreateInstanceEx(clsid, NULL, CLSCTX_ALL, &ServerInfo, (ULONG) 1, &Results);
hr = CoSetProxyBlanket(Results.pItf, RPC_C_AUTHN_WINNT, RPC_C_AUTHZ_NONE, NULL,
RPC_C_AUTHN_LEVEL_CALL, RPC_C_IMP_LEVEL_IMPERSONATE, &AuthIdentity, EOAC_NONE);
pSession = (_MyDCOMType *)Results.pItf;
hr = pSession->raw_GetVersion(&version);
It connects to the remote server, creates the DCOM object, and gets data from it just fine. I’d like to figure out how to get my C# code to do the same. Any ideas? Did I mess up one of the enums going to the functions? Am I missing some critical call or doing something all wrong? As background, my computer and the computer I am trying to connect to are both logged in under domain accounts, but neither has permissions on the other. The C++ code impersonates the remote user for the purpose of the DCOM connection. I am trying to do a .NET impersonation in such a way that it will be valid for the network connection given a manually entered username and password.
Alternatively, if it just isn’t possible, I suppose I could write a small C++/CLR DLL with the same C++ code to handle the DCOM connection part and reference it from the C# code, but I’m hoping to avoid the extra complexity.
I banged away on it for a while and made no progress, so I gave up and wrote a C++/CLR DLL, which works perfectly. Code is as follows:
With class definition:
The usual caveats apply – I chopped out the actual functionality and error checking/disposal details for simpler code, and I don’t really know C++/CLR or COM and DCOM that well, so there may be things that could be done easier. If you know of any improvements or a way to do it in C#, please comment/answer as appropriate.