在桌面应用程序中通过VPN模拟用户

8

我在尝试在桌面应用程序中模拟活动目录用户时遇到了问题。每次使用LogOn API时,结果都为false。

该用户和域确实存在,因为我也可以在同一应用程序上通过DirectoryServices.AccountManagement对用户进行身份验证。

已阅读Microsoft网站上有关模拟的文档,甚至在此堆栈上发布了一些帖子。此外,还使用了SimpleImpersonation库,但结果相同。

public class Demo
{
    private WindowsImpersonationContext impersonationContext = null;

    [DllImport("advapi32.dll", SetLastError = true)]
    private static extern int LogonUser(string lpszUserName, string lpszDomain, string lpszPassword, int dwLogonType, int dwLogonProvider, ref IntPtr phToken);

    [DllImport("advapi32.dll", CharSet = CharSet.Auto, SetLastError = true)]
    private static extern int DuplicateToken(IntPtr hToken, int impersonationLevel, ref IntPtr hNewToken);
    private void Enter()
    {
        try
        {
            IntPtr token = IntPtr.Zero;
            IntPtr tokenDuplicate = IntPtr.Zero;
            string userName = "myValidUser";
            string domain = "my.domain.example";
            string password = "myValidPassword";

            if (LogonUser(userName, domain, password, (int)LogonType.LOGON32_LOGON_INTERACTIVE, (int)LogonProvider.LOGON32_PROVIDER_WINNT35, ref token) != 0)
            {
                WindowsIdentity WindowsIdentityPrincipal = new WindowsIdentity(token);
                if (DuplicateToken(token, 2, ref tokenDuplicate) != 0)
                {
                    WindowsIdentity tempWindowsIdentity = new WindowsIdentity(tokenDuplicate);
                    impersonationContext = tempWindowsIdentity.Impersonate();
                }
                else
                {
                    throw new Win32Exception(new Win32Exception(Marshal.GetLastWin32Error()).Message);
                }
            }
            else
            {
                //throws username or pass incorrect
                throw new Win32Exception(new Win32Exception(Marshal.GetLastWin32Error()).Message);
            }
        }
        catch (Exception exc)
        {
            throw exc;
        }
    }

    public enum LogonProvider
    {
        LOGON32_PROVIDER_DEFAULT = 0,
        LOGON32_PROVIDER_WINNT35 = 1,
        LOGON32_PROVIDER_WINNT40 = 2,
        LOGON32_PROVIDER_WINNT50 = 3
    }

    private enum LogonType
    {
        LOGON32_LOGON_INTERACTIVE = 2,
        LOGON32_LOGON_NETWORK = 3,
        LOGON32_LOGON_BATCH = 4,
        LOGON32_LOGON_SERVICE = 5,
        LOGON32_LOGON_UNLOCK = 7,
        LOGON32_LOGON_NETWORK_CLEARTEXT = 8,
        LOGON32_LOGON_NEW_CREDENTIALS = 9,
    }
}

我不确定它无法工作的原因是我的计算机正在外部网络上运行,并通过VPN连接/认证到公司网络。

编辑1: 生成的错误代码是1326(未知用户名或密码错误)

编辑2: 此方法尝试获取身份令牌以供后续在线程模拟中使用。


您可能希望添加实际返回的错误代码。问题不一定只是密码,也可能是您尝试进行的登录类型由于某种原因而不被允许。很遗憾,从我快速浏览的情况来看,您的问题更多是网络安全故障排除,而不是实际编码问题,但也许错误代码可以帮助解决问题。 - Maverik
@Maverik谢谢你的建议,我刚刚添加了错误代码。 - Salvador Ruiz Guevara
3
我认为问题在于您正在尝试作为交互式用户登录。由于登录发生的计算机不在同一域中,因此无法向域控制器提供登录请求。您有两个选择:使用logon_new_credentials作为登录类型。在这种情况下,您将作为工作站上的本地用户进行操作,但每个网络请求都将被冒充为域帐户。第二个选项(更可取)是将凭据传递给directoryentry/ldapconnection构造函数。这将导致网络登录并解决该问题。 - oldovets
2个回答

0

您可能需要查看LogonUser函数的文档。

如果您的用户名格式为user@domain.example,则需要传入以下内容:

  • lpszUserName = "user@domain.example"
  • lpszDomain = null

如果您的用户名格式为domain\user,则需要传入以下内容:

  • lpszUserName = "user"
  • lpszDomain = "domain"

错误处理完全限定的用户名将导致您看到的错误代码。


-1

您不能使用LogonUser来登录远程计算机。您需要使用WNetAddConnection2 API函数。请参考MSDN文档。

关于LogonUser: https://learn.microsoft.com/en-us/windows/desktop/api/winbase/nf-winbase-logonuserw

关于WNetAddConnection2: https://learn.microsoft.com/en-us/windows/desktop/api/winnetwk/nf-winnetwk-wnetaddconnection2w

这里是我编写的一个类:

public class RemoteNetworkConnector : IDisposable
{
    readonly string _networkName;

    public RemoteNetworkConnector(string networkName, NetworkCredential credentials)
    {
        _networkName = networkName;

        NetResource netResource = new NetResource
        {
            Scope = ResourceScope.GlobalNetwork,
            ResourceType = ResourceType.Disk,
            DisplayType = ResourceDisplaytype.Share,
            RemoteName = networkName
        };

        var userName = string.IsNullOrEmpty(credentials.Domain)
            ? credentials.UserName
            : string.Format(@"{0}\{1}", credentials.Domain, credentials.UserName);

        var connectionResult = WNetAddConnection2(
            netResource,
            credentials.Password,
            userName,
            0);

        if (connectionResult != 0)
        {
            throw new Win32Exception(connectionResult, "Error connecting to remote share");
        }
    }

    ~RemoteNetworkConnector()
    {
        Dispose(false);
    }

    public void Dispose()
    {
        Dispose(true);
        GC.SuppressFinalize(this);
    }

    protected virtual void Dispose(bool disposing)
    {
        WNetCancelConnection2(_networkName, 0, true);
    }

    [DllImport("mpr.dll")]
    private static extern int WNetAddConnection2(NetResource netResource,
        string password, string username, int flags);

    [DllImport("mpr.dll")]
    private static extern int WNetCancelConnection2(string name, int flags,
        bool force);

    [StructLayout(LayoutKind.Sequential)]
    public class NetResource
    {
        public ResourceScope Scope;
        public ResourceType ResourceType;
        public ResourceDisplaytype DisplayType;
        public int Usage;
        public string LocalName;
        public string RemoteName;
        public string Comment;
        public string Provider;
    }

    public enum ResourceScope : int
    {
        Connected = 1,
        GlobalNetwork,
        Remembered,
        Recent,
        Context
    };

    public enum ResourceType : int
    {
        Any = 0,
        Disk = 1,
        Print = 2,
        Reserved = 8,
    }

    public enum ResourceDisplaytype : int
    {
        Generic = 0x0,
        Domain = 0x01,
        Server = 0x02,
        Share = 0x03,
        File = 0x04,
        Group = 0x05,
        Network = 0x06,
        Root = 0x07,
        Shareadmin = 0x08,
        Directory = 0x09,
        Tree = 0x0a,
        Ndscontainer = 0x0b
    }
}

希望这能有所帮助。


我并不试图直接连接到资源。 该方法尝试获取令牌,以便稍后用于线程模拟。 - Salvador Ruiz Guevara

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接