Impersonate user over a VPN in a desktop application

Viewed 667

I'm having problems trying to impersonate an active directory user in a desktop application. Every time I use the LogOn API the result is false.

The user and domain do exist since I can also authenticate the user over the DirectoryServices.AccountManagement on the same application.

Have read the documentation about impersonation in the Microsoft site and even some post here on the stack. Also, have used the SimpleImpersonation library with the same results.

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,
    }
}

I don't know if the reason it isn't working is that my computer is running on an outside network and connecting/authenticating to the company network over a VPN.

Edit 1. The resulting error code is 1326 (unknown user name or bad password)

Edit 2. The method is trying to obtain the identity token for later use on thread impersonation.

2 Answers

You might want to check the documentation for the LogonUser function.

If your username is in the format user@domain.example then you need to pass in:

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

If your username is in the format domain\user then you need to pass in:

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

Treating the fully qualified username the wrong way will result in the error code you're seeing.

You cannot use LogonUser to log on to a remote computer. You need to use WNetAddConnection2 api function. Please refer to the msdn documentation.

for LogonUser: https://docs.microsoft.com/en-us/windows/desktop/api/winbase/nf-winbase-logonuserw

for WNetAddConnection2 : https://docs.microsoft.com/en-us/windows/desktop/api/winnetwk/nf-winnetwk-wnetaddconnection2w

here is a class that I have written :

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
    }
}

I hope this will help.

Related