c# - 如何在 C# 中模拟另一个用户进行 DCOM 连接?

标签 c# .net impersonation dcom

我正在尝试连接到远程计算机上的 DCOM 对象,该计算机以不同于 C# 应用程序的用户身份登录。当我的代码在同一台计算机上运行时,我的代码可以连接到同一个 DCOM 对象,如下所示:

MyDCOMType dcomObject;
Type remoteSessionContextType = Type.GetTypeFromProgID("ServerApp.MyDCOMType");
dcomObject = (MyDCOMType)Activator.CreateInstance(remoteSessionContextType);
string version = dcomObject.GetVersion();

MyDCOMType 是在 DCOM 应用程序(一个 VB6 应用程序)中实现的类型,我将其作为引用添加到我的项目中。我已经能够在 C# 代码中创建它的一个实例,并调用所有具有预期结果的方法。现在我试图让它连接到远程机器上的同一个对象。我正试图像这样在远程系统上模拟用户(省略了变量定义和错误处理):

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());
      }
 }

然后是类似的代码来创建对象:

MyDCOMType dcomObject;
Type remoteSessionContextType = Type.GetTypeFromProgID("ServerApp.MyDCOMType", ipAddress);
dcomObject = (MyDCOMType)Activator.CreateInstance(remoteSessionContextType);
string version = dcomObject.GetVersion();

除了 CreateInstance 调用会针对未经授权的访问抛出错误代码为 80070005 的 UnauthorizedAccessException 错误。

另一件事是我有 C++ 代码可以完美地完成同样的事情。该代码如下所示:

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);

它连接到远程服务器,创建 DCOM 对象,并从中获取数据。我想弄清楚如何让我的 C# 代码执行相同的操作。有任何想法吗?我是否弄乱了函数的枚举之一?我错过了一些关键电话还是做错了什么?作为背景,我的计算机和我尝试连接的计算机都在域帐户下登录,但彼此都没有权限。 C++ 代码模拟远程用户以实现 DCOM 连接。我正在尝试以这种方式进行 .NET 模拟,以便在给定手动输入的用户名和密码的情况下对网络连接有效。

或者,如果这不可能,我想我可以用相同的 C++ 代码编写一个小的 C++/CLR DLL 来处理 DCOM 连接部分并从 C# 代码中引用它,但我希望避免额外的复杂性。

最佳答案

我折腾了一段时间,没有任何进展,所以我放弃了,写了一个 C++/CLR DLL,效果很好。代码如下:

MyDCOMTalker::MyDCOMTalker(String ^ipAddress, String ^username, String ^password, String ^domain)
{
    COAUTHINFO      AuthInfo;
    COSERVERINFO    ServerInfo;
    MULTI_QI        Results;
    CLSID           clsid;
    AuthIdentity = new COAUTHIDENTITY;

    long        DSStatus;
    BSTR        umVersion;

    AuthIdentity->Domain            = (unsigned short *)Marshal::StringToHGlobalAuto(domain).ToPointer();
    AuthIdentity->DomainLength      = domain->Length;
    AuthIdentity->Flags             = SEC_WINNT_AUTH_IDENTITY_UNICODE;
    AuthIdentity->Password          = (unsigned short *)Marshal::StringToHGlobalAuto(password).ToPointer();
    AuthIdentity->PasswordLength    = password->Length;
    AuthIdentity->User              = (unsigned short *)Marshal::StringToHGlobalAuto(username).ToPointer();
    AuthIdentity->UserLength        = username->Length;

    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     = (LPWSTR)Marshal::StringToHGlobalAuto(ipAddress).ToPointer();

    Results.pIID = &_uuidof(_MyDCOMType);
    Results.pItf = NULL;
    Results.hr   = 0;

    CoInitialize(NULL);
    Marshal::ThrowExceptionForHR(CLSIDFromProgID(L"MyDcomDLL.MyDCOMType", &clsid));
    Marshal::ThrowExceptionForHR(CoCreateInstanceEx(clsid, NULL, CLSCTX_ALL, &ServerInfo, (ULONG) 1, &Results));
    Marshal::ThrowExceptionForHR(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;
    Marshal::ThrowExceptionForHR(pSession->raw_GetVersion(&DSStatus, &umVersion));
    if (DSStatus == 0 && umVersion != NULL)
        version = Marshal::PtrToStringBSTR((System::IntPtr)umVersion);
    else
        version = String::Empty;
}

类定义:

public ref class MyDCOMTalker
{
private:
    _MyDCOMType *pSession;
    String ^version;
    COAUTHIDENTITY  *AuthIdentity;
public: 
    MyDCOMTalker(String ^ipAddress, String ^username, String ^password, String ^domain);
};

通常的注意事项适用——我删除了实际的功能和错误检查/处理细节以获得更简单的代码,而且我不太了解 C++/CLR 或 COM 和 DCOM,所以可能有些事情可以做更轻松。如果您知道在 C# 中有任何改进或方法,请酌情发表评论/回答。

关于c# - 如何在 C# 中模拟另一个用户进行 DCOM 连接?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/13278831/

相关文章:

c# - 搜索属性名称包含连字符/破折号的 XElement

c# - 如何使用 MS.DI 和 .NET Core 从静态方法重构为依赖注入(inject)?

c# - 为什么拆分不在 UI 线程上运行的任务以使 WPF 应用程序更具响应性?

.net - WPF中的键盘焦点与逻辑焦点

c# - 避免客户端> Web服务> SQL Server双跳的解决方案

c# - 延迟文本更改事件一秒钟 wpf

c# - 在 .NET 2.0 中将位图转换为一个多页 TIFF 图像

c# - 什么是 DLR 层职责?

c++ - 是否可以从远程计算机获取有效 token ?

C# Winform 模拟无法工作