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

Windows Impersonation using C#

, 29 Apr 2003 CPOL
Rate this:
Please Sign up or sign in to vote.
An article demonstrating how to use Windows impersonation in your C# code

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, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

Share

About the Author

Marc Merritt
Architect
United States United States
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/
Follow on   Twitter

Comments and Discussions

 
GeneralMy vote of 5 Pinmembersrilekhamenon1-Sep-14 3:25 
QuestionThanks a lot, you saved 2 hrs. PinmemberMember 103845676-Nov-13 9:23 
AnswerRe: Thanks a lot, you saved 2 hrs. PinmemberMarc Merritt12-Nov-13 9:34 
GeneralRe: Thanks a lot, you saved 2 hrs. PinmemberOded Arbel20-Mar-14 4:04 
GeneralRe: Thanks a lot, you saved 2 hrs. PinmemberMarc Merritt20-Mar-14 4:10 
GeneralRe: Thanks a lot, you saved 2 hrs. PinmemberGuss7720-Mar-14 10:47 
Questionsource code help PinmemberPyakaa17-Dec-12 2:43 
GeneralMy vote of 5 Pinmemberkerkenez20005-Apr-12 0:35 
QuestionCall LogonUser PinmemberMember 822764117-Sep-11 14:40 
QuestionPlease I need a Help Pinmemberkaiserssosse11-Aug-11 2: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;
            }
        }

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

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

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