Click here to Skip to main content
Click here to Skip to main content

Windows Impersonation using C#

By , 29 Apr 2003
 

Impersonation

Introduction

I've been a member of the CodeProject for over 3 years now, and still haven't contributed any articles - until now.

While designing a Windows Forms-based application, to administrate containers in our Active Directory, I needed a way to allow binding to the AD using alternate credentials. Windows impersonation was the answer. This sample app demonstrates how to use unmanaged code by calling LogonUser() contained within the advapi32.dll, and pass a token handle back to your .NET application using WindowsImpersonationContext.

One of the downfalls to the LogonUser()function is that the password get passed in clear-text.

Partial Source Code

using System.Runtime.InteropServices; // DllImport
using System.Security.Principal; // WindowsImpersonationContext
using System.Security.Permissions; // PermissionSetAttribute
...

public WindowsImpersonationContext 
    ImpersonateUser(string sUsername, string sDomain, string sPassword)
{
    // initialize tokens
    IntPtr pExistingTokenHandle = new IntPtr(0);
    IntPtr pDuplicateTokenHandle = new IntPtr(0);
    pExistingTokenHandle = IntPtr.Zero;
    pDuplicateTokenHandle = IntPtr.Zero;
    
    // if domain name was blank, assume local machine
    if (sDomain == "")
        sDomain = System.Environment.MachineName;

    try
    {
        string sResult = null;

        const int LOGON32_PROVIDER_DEFAULT = 0;

        // create token
        const int LOGON32_LOGON_INTERACTIVE = 2;
        //const int SecurityImpersonation = 2;

        // get handle to token
        bool bImpersonated = LogonUser(sUsername, sDomain, sPassword, 
            LOGON32_LOGON_INTERACTIVE, LOGON32_PROVIDER_DEFAULT, 
                ref pExistingTokenHandle);

        // did impersonation fail?
        if (false == bImpersonated)
        {
            int nErrorCode = Marshal.GetLastWin32Error();
            sResult = "LogonUser() failed with error code: " + 
                nErrorCode + "\r\n";

            // show the reason why LogonUser failed
            MessageBox.Show(this, sResult, "Error", 
                MessageBoxButtons.OK, MessageBoxIcon.Error);
        }

        // Get identity before impersonation
        sResult += "Before impersonation: " + 
            WindowsIdentity.GetCurrent().Name + "\r\n";

        bool bRetVal = DuplicateToken(pExistingTokenHandle, 
            (int)SECURITY_IMPERSONATION_LEVEL.SecurityImpersonation, 
                ref pDuplicateTokenHandle);

        // did DuplicateToken fail?
        if (false == bRetVal)
        {
            int nErrorCode = Marshal.GetLastWin32Error();
            // close existing handle
            CloseHandle(pExistingTokenHandle); 
            sResult += "DuplicateToken() failed with error code: " 
                + nErrorCode + "\r\n";

            // show the reason why DuplicateToken failed
            MessageBox.Show(this, sResult, "Error", 
                MessageBoxButtons.OK, MessageBoxIcon.Error);
            return null;
        }
        else
        {
            // create new identity using new primary token
            WindowsIdentity newId = new WindowsIdentity
                                        (pDuplicateTokenHandle);
            WindowsImpersonationContext impersonatedUser = 
                                        newId.Impersonate();

            // check the identity after impersonation
            sResult += "After impersonation: " + 
                WindowsIdentity.GetCurrent().Name + "\r\n";
            
            MessageBox.Show(this, sResult, "Success", 
                MessageBoxButtons.OK, MessageBoxIcon.Information);
            return impersonatedUser;
        }
    }
    catch (Exception ex)
    {
        throw ex;
    }
    finally
    {
        // close handle(s)
        if (pExistingTokenHandle != IntPtr.Zero)
            CloseHandle(pExistingTokenHandle);
        if (pDuplicateTokenHandle != IntPtr.Zero) 
            CloseHandle(pDuplicateTokenHandle);
    }
}

Points of Interest

This code won't work on Windows 98 or ME because they do not utilize user tokens. Code was built and run using Visual Studio.NET 2002 on Windows XP Service Pack 1.

One of the other uses for this code I've found is, for instantiating COM components that must run in an alternate security context to that of the logged-on user.

If anyone has a more secure method of achieving the same thing, please let me know.

History

  • Version 1.0 - 04.25.03 - First release version

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here

About the Author

Marc Merritt
Architect
United States United States
Member
I live in southeastern Pennsylvania, USA with my lovely wife and two beautiful daughters. Life is good. My hobbies are motorcycles, motorcycles, and motorcycles.
 
I run a riders group called Twisties Motorcycle Club. If you're are a rider in the tri-state area, look us up! http://twistiesmc.com/

Sign Up to vote   Poor Excellent
Add a reason or comment to your vote: x
Votes of 3 or less require a comment

Comments and Discussions

 
You must Sign In to use this message board.
Search this forum  
    Spacing  Noise  Layout  Per page   
Questionsource code helpmemberPyakaa17 Dec '12 - 1:43 
i am trying to do something similar.. impersonate a user and copy files from the remote desktop and write files into it.... help required.......
GeneralMy vote of 5memberkerkenez20004 Apr '12 - 23:35 
Uesefull windos/Domain member login tools.
QuestionCall LogonUsermemberMember 822764117 Sep '11 - 13:40 
How can i call LogonUser with a username "something" and without any password?
QuestionPlease I need a Helpmemberkaiserssosse11 Aug '11 - 1:41 
I have this code:

public class Credenciales : IDisposable
    {
        [DllImport("advapi32.dll", SetLastError=true)]
        private static extern bool LogonUser(string lpszUsername, string lpszDomain, string lpszPassword, int dwLogonType, int dwLogonProvider, out IntPtr phToken);
 
        [DllImport( "kernel32", SetLastError = true )]
        private static extern bool CloseHandle(IntPtr hObject);
 
        private IntPtr userHandle = IntPtr.Zero;
        private WindowsImpersonationContext impersonationContext;
 
        public Credenciales( string user, string domain, string password )
        {
                if ( ! string.IsNullOrEmpty( user ) )
                {
                        // Call LogonUser to get a token for the user
                        bool loggedOn = LogonUser( user, domain, password,
                                9 /*(int)LogonType.LOGON32_LOGON_NEW_CREDENTIALS*/,
                                3 /*(int)LogonProvider.LOGON32_PROVIDER_WINNT50*/,
                                out userHandle );
                        if ( !loggedOn )
                                throw new Win32Exception( Marshal.GetLastWin32Error() );
 
                        // Begin impersonating the user
                        impersonationContext = WindowsIdentity.Impersonate( userHandle );
                }
        }
 
        public void Dispose()
        {
                if ( userHandle != IntPtr.Zero )
                        CloseHandle( userHandle );
                if ( impersonationContext != null )
                        impersonationContext.Undo();
        }
    }

And also I have installed BDE Administrator for Paradox Data Bases.

My problem is that when a user without administrator privilegies want to connect to DB, it's not working and I think it is because the user can't load DLLs.

Is any solution for my problem?

These is my other code:

public int RZb()
        {
            xml = new XML();
 
            try
            {
                string myConnectionString = "Driver={Microsoft Paradox Driver (*.db )};" + "fil=Paradox 7.x;" + "driverid=538;" + "collatingsequence=ASCII;" + "dbq=" + xml.Recuperar("RelojBBDD") + ";" + "defaultdir=" + xml.Recuperar("RelojBBDD") + ";" + "paradoxnetpath=" + xml.Recuperar("RelojBBDDNetFiles") + ";" + "paradoxnetstyle=4.x;" + "paradoxusername=admin;" + "safetransactions=0;" + "threads=3;" + "uid=admin;" + "usercommitsync=Yes";
                OdbcConnection myConnection = new OdbcConnection();
                myConnection.ConnectionString = myConnectionString;
                myConnection.Open();
 
                //execute queries, etc
                OdbcCommand DbCommand = myConnection.CreateCommand();
                DbCommand.CommandText = "SELECT codigo, Nombre, DNI FROM Personal WHERE Baja=0 AND DNI LIKE '%" + ((Main)(this.Parent.Parent)).lblDNI.Text.Substring(0,8) + "%';";
                OdbcDataReader DbReader = DbCommand.ExecuteReader();
                OdbcDataAdapter da = new OdbcDataAdapter(DbCommand);
                DataSet dsRetrievedData = new DataSet();
                myConnection.Close();
                da.Fill(dsRetrievedData);
                DataRowCollection dra = dsRetrievedData.Tables["Table"].Rows;
                if (dra.Count == 0)
                {
                    myConnectionString = "Driver={Microsoft Paradox Driver (*.db )};" + "fil=Paradox 7.x;" + "driverid=538;" + "collatingsequence=ASCII;" + "dbq=" + xml.Recuperar("RelojUdaltzaingoBBDD") + ";" + "defaultdir=" + xml.Recuperar("RelojUdaltzaingoBBDD") + ";" + "paradoxnetpath=" + xml.Recuperar("RelojUdaltzaingoBBDDNetFiles") + ";" + "paradoxnetstyle=4.x;" + "paradoxusername=admin;" + "safetransactions=0;" + "threads=3;" + "uid=admin;" + "usercommitsync=Yes";
                    myConnection = new OdbcConnection();
                    myConnection.ConnectionString = myConnectionString;
                    myConnection.Open();
 
                    //execute queries, etc
                    DbCommand = myConnection.CreateCommand();
                    DbCommand.CommandText = "SELECT codigo, Nombre, DNI FROM Personal WHERE Baja=0 AND DNI LIKE '%" + ((Main)(this.Parent.Parent)).lblDNI.Text.Substring(0, 8) + "%';";
                    //DbCommand.CommandText = "SELECT codigo FROM Personal WHERE Nombre LIKE " + textBox4.Text + ";";
                    DbReader = DbCommand.ExecuteReader();
                    da = new OdbcDataAdapter(DbCommand);
                    dsRetrievedData = new DataSet();
                    myConnection.Close();
                    da.Fill(dsRetrievedData);
                    dra = dsRetrievedData.Tables["Table"].Rows;
                }
                foreach (DataRow dr in dra)
                {
                    RelojZb = int.Parse(dr["codigo"].ToString());
                }
                return RelojZb;
            }
            catch (Exception ex)
            {
                MessageBox.Show("Erroreak egon dira markajeekin edota beste erabiltzaile bat dago koltsulta egiten");
                return 0;
            }
        }

AnswerRe: Please I need a Helpmemberdiialer21 Aug '11 - 7:36 
doesn't the 2nd reply work?
LogonUser method and the administrative privilegies[^]
 
also try this in your manifest
 
http://msdn.microsoft.com/en-us/library/bb756929.aspx[^]
GeneralRe: Please I need a Helpmemberkaiserssosse21 Aug '11 - 21:03 
Thanks for the reply.
 
I think the problem is the computer and the accounts, because with an old account it doesn't work but if we copy the privileges of the account to a new account it works fine, so I don't know which is the problem with all of this.
 
It can be because the accounts are old and when it was created it was done with a winNT version and not with the winXP SP3, or is a problem of cache files in the computer...
 
In some computers works perfectly, in others only works with some users, normally old accounts. This is a mysterious.
GeneralIs Windows Impersionation Thread or Process Level ?memberThomas Haller27 May '11 - 2:42 
Hello, din't find any info.
I am planning to use Windows Impersionation in a multi threaded Server Application.
Does anyone know if an impersionation affects other threads, or does it exist only for the Thread where the impersionation have been made ??
GeneralThanksmemberolivier gg14 Feb '11 - 8:43 
Hi,
thanks for providing this code. it was helpful.
GeneralWIndows 7membermoralam13 Jul '10 - 7:58 
Hello, I Try this method in windows 7 and not work, somebody have an update for run this one on Windows 7?
GeneralRe: WIndows 7memberMarc Merritt28 Jan '13 - 5:25 
This probably has something to do with the need to elevate permissions.
QuestionCross domain impersonationmemberatulsureka18 Apr '10 - 2:25 
Hi,
 
I am trying to use this application to impersonate the user across forest. There is no trust between the forests. This code does NOT seem to work in cross forest environment. Is this a known limitation of this app?
 
Atul Sureka
AnswerRe: Cross domain impersonationmemberdeadwood8827 Sep '10 - 2:00 
I'm facing the same problem as well. Any idea how to resolve cross-domain impersonation?
GeneralRe: Cross domain impersonationmemberCstruter26 Oct '10 - 21:38 
This post should give you a clue
http://www.cstruter.com/blog/270[^]
GeneralRe: Cross domain impersonationmembersabh2125 Jan '13 - 1:32 
This code will work only if trust relationship exist between these 2 domains
GeneralCreate a folder on a remote computermemberShlomo6 Jan '10 - 1:52 
Hi,
Thanks for the demo. Wink | ;)
I am trying to create a folder on a remote computer that I know its credentials.
The parent folder is not shared.
So I did what you wrote (with the exception that I used the "SecurityDelegation").
It didn't work. OMG | :OMG:
I don't understand why you do "DuplicateToken" (why can't I use the token that it returned from the LogonUser())?
I also don't understand why when I give the wrong user-name/password I stikk get no error and a valid token.
Please note that when I used anything else than LOGON32_LOGON_NEW_CREDENTIALS I always rectved error 1326. Frown | :(
 
I wrote it in C++ so what I did is
if (!LogonUser(_T("test"),_T("\\\\SHLOMOARAN"),_T("abc"),LOGON32_LOGON_NEW_CREDENTIALS,LOGON32_PROVIDER_DEFAULT,&hToken))
{
// error
}
if (!DuplicateToken(hToken,SecurityDelegation,&hNewToken))
{
// error
}
ImpersonateLoggedOnUser(hNewToken);
if (!hNewToken)
{
// error
}
if (CreateDirectory(path,NULL))
{
// error
}
GeneralWindows 2008 servermembersmithafebinkal3 Dec '08 - 18:45 
Hi Does this code work with windows 2008 server. I am getting command failed error . Can you please help.
 
Thanks,
Smitha.
GeneralThis process not working with a WPF application!!!memberArshad Kunnath31 Jul '08 - 1:36 
I have tried the same process both in forms and WPF applications unfortunately am failed to impersonate user in WPF application.
 
Did i missed anything while impersonating a user in WPF application?
GeneralRe: This process not working with a WPF application!!!memberMarc Merritt31 Jul '08 - 2:59 
This was written in 2003 using the 1.0 Framework version. It's no longer necessary to use P/Invoke for impersonation. Take a look at System.Threading.Thread.CurrentPrincipal.
GeneralRe: This process not working with a WPF application!!!memberMatei Focseneanu12 May '10 - 5:15 
Well I've done a lot of searching about how to use System.Threading.Thread.CurrentPrincipal for impersonation and still haven't found a single place where the process is described.
 
Please help, if you have any clues of how to perform impersonation wihout P/Invoke ...
 
Regards.
QuestionIt may be....memberWillian.BR15 May '08 - 8:55 
Hi,
It's a nice work. Congratulations!
 
But,
 
I would like to run a small windows service application.
It will make some tasks and than the service may run another
process but with other local credentials.
The main service will run under SYSTEM account.
 
Can you help me?
 
Thanks!
 
Willian S. Rodrigues
willian_cpp_br@hotmail.com

GeneralWindows CE .netmemberquestions_c1 Oct '07 - 2:28 
I would like to have this funcionality in an ActiveX running in Windows CE .net
 
Do you know if this is possible?
 
Thanks,
 
Antonio.
 
Antonio
GeneralRe: Windows CE .netmemberMarc Merritt1 Oct '07 - 15:18 
The compact framework has no support for the WindowsImpersonationContext class that I see, so the short answer would be no. However, I'm sure there are plenty of ways to achieve this using a P/Invoke approach.
GeneralRe: Windows CE .netmemberPram_Singh4120 Jul '09 - 4:22 
did you find a way out to run on Windows CE
GeneralError Code 1326 [modified]membercollapo21 Aug '07 - 0:56 
Hi,
 
I've tried to use your code, or at least some of it in my project, and I've encountered the following problem:
 
The code runs, impersonates finely on local computer, but when I try to impersonate a user on an other computer (e.g. "Comp" , "User", "") I get error code 1326, the domain IS "Comp", username IS "User" and there is no password on it. So I don't know the solution for my problem.
I've tried your sample application, which encountered the same.
Could you provide me some information about this?
 
OK, I know that if there is no passwd on the share I needn't use this code, but the use of it should not end in the result written above, or is it?
 
After trying I noticed, that this program impersonates on local computer, because when I've tried to impersonate an admin user on an other computer, which has the same name and passwd as the admin of the local computer, it impersonated on the local computer. Is it possible or I'm missunderstanding something?
 

thanks in advance: collapo
 

-- modified at 7:13 Tuesday 21st August, 2007
 
Thx in advance:
Collapo
GeneralRe: Error Code 1326membermirh10 Oct '07 - 1:59 
Hi
 
It is possible. You use mirrored accounts. In this way you can have access to local resources on the OTHER machine. But the question is how to achieve this (ex. access to files on other machines with NTFS rights sets on local account) without creating mirrored accounts on both?

 
Mirek

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Permalink | Advertise | Privacy | Mobile
Web03 | 2.6.130516.1 | Last Updated 30 Apr 2003
Article Copyright 2003 by Marc Merritt
Everything else Copyright © CodeProject, 1999-2013
Terms of Use
Layout: fixed | fluid