Click here to Skip to main content
Licence 
First Posted 29 Apr 2003
Views 365,645
Downloads 5,269
Bookmarked 111 times

Windows Impersonation using C#

By | 29 Apr 2003 | Article
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 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

Technical Lead
Motorcycle Road Racing Forums
United States United States

Member

Follow on Twitter Follow on Twitter
I live in southeastern Pennsylvania, USA with my lovely wife and two beautiful daughters. Life is good. My hobbies are motorcycles, motorcycles, and motorcycles.

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. (secure sign-in)
 
Search this forum  
 FAQ
    Noise  Layout  Per page   
  Refresh
GeneralRe: First call takes too long PinmemberMarc Merritt2:24 12 Jun '06  
GeneralA similar article [modified] PinsitebuilderUwe Keim23:59 23 Apr '05  
GeneralRe: A similar article Pinmembercraigg755:57 3 Nov '06  
GeneralRe: A similar article PinsitebuilderUwe Keim6:14 3 Nov '06  
GeneralImpersonation Pinmemberchriskoiak5:13 16 Mar '04  
QuestionRe: Impersonation Pinmembertee_jay22:08 10 Apr '06  
AnswerRe: Impersonation Pinmemberhk1119:01 12 Jul '07  
Here you go...Smile | :)
 
public class NetworkHelper {
// Used to Map UNC from a Windows Service
 
#region Constants
//NetResource Scope
private const int RESOURCE_CONNECTED = 0x00000001;
private const int RESOURCE_GLOBALNET = 0x00000002;
private const int RESOURCE_REMEMBERED = 0x00000003;
 
//NetResource Type
private const int RESOURCETYPE_ANY = 0x00000000;
private const int RESOURCETYPE_DISK = 0x00000001;
private const int RESOURCETYPE_PRINT = 0x00000002;
 
//NetResource Usage
private const int RESOURCEUSAGE_CONNECTABLE = 0x00000001;
private const int RESOURCEUSAGE_CONTAINER = 0x00000002;
 

//NetResource Display Type
private const int RESOURCEDISPLAYTYPE_GENERIC = 0x00000000;
private const int RESOURCEDISPLAYTYPE_DOMAIN = 0x00000001;
private const int RESOURCEDISPLAYTYPE_SERVER = 0x00000002;
private const int RESOURCEDISPLAYTYPE_SHARE = 0x00000003;
private const int RESOURCEDISPLAYTYPE_FILE = 0x00000004;
private const int RESOURCEDISPLAYTYPE_GROUP = 0x00000005;
 
//Flags
private const int CONNECT_UPDATE_PROFILE = 0x00000001;
private const int CONNECT_UPDATE_RECENT = 0x00000002;
private const int CONNECT_TEMPORARY = 0x00000004;
private const int CONNECT_INTERACTIVE = 0x00000008;
private const int CONNECT_PROMPT = 0x00000010;
private const int CONNECT_NEED_DRIVE = 0x00000020;
 
#endregion
 
#region NetResource Structure
[StructLayout(LayoutKind.Sequential)]
private struct NetResource {
public int Scope;
public int Type;
public int DisplayType;
public int Usage;
public string LocalName;
public string RemoteName;
public string Comment;
public string Provider;
}
#endregion
 
#region Win32 Functions
[DllImport("mpr.dll", EntryPoint = "WNetAddConnection2A", CharSet = CharSet.Ansi, SetLastError = true)]
private static extern int WNetAddConnection2A(ref NetResource netresource, string password, string username, int flags);
 
[DllImport("mpr.dll", EntryPoint = "WNetCancelConnection2", CharSet = CharSet.Ansi, SetLastError = true)]
private static extern int WNetCancelConnection2(string drivename, int flag, bool force);
 
#endregion
 
myLib lib = new myLib();
 
public bool WNetAddConnection(string LocalDrive, string NetworkFolderPath, string User, string Password, bool Force) {
bool success = false;
 
try {
NetResource netresource = new NetResource();
netresource.Scope = RESOURCE_GLOBALNET;
netresource.Type = RESOURCETYPE_DISK;
netresource.Usage = RESOURCEUSAGE_CONNECTABLE;
netresource.DisplayType = RESOURCEDISPLAYTYPE_SHARE;
netresource.LocalName = LocalDrive;
netresource.RemoteName = NetworkFolderPath;
netresource.Comment = "";
netresource.Provider = "";
 
int Flag = CONNECT_UPDATE_PROFILE;
 
if (Force) {
success = WNetCancelConnection(LocalDrive, true);
}
 
int result = WNetAddConnection2A(ref netresource, Password, User, Flag);
 
if (result > 0) {
throw new System.ComponentModel.Win32Exception(result);
}
success = true;
 
}
catch (Exception e) {
lib.Echo("Error: " + e.Message, myLib.MsgType.FAIL);
}
 
return success;
}
 
public bool WNetCancelConnection(string LocalDrive, bool Force) {
bool success = false;
try {
int result = WNetCancelConnection2(LocalDrive, CONNECT_UPDATE_PROFILE, Force);
if (result > 0) {
throw new System.ComponentModel.Win32Exception(result);
}
success = true;
 
}
catch (Exception e) {
lib.Echo("Error:" + e.Message, myLib.MsgType.FAIL);
}
return success;
 

}
 
}

GeneralRe: Impersonation PinmemberPhutphornxai1:43 19 Jul '07  
GeneralCoding Style Issues PinmemberAndy Neilson2:56 7 May '03  
GeneralRe: Coding Style Issues PinmemberMaximilian Hänel12:54 27 May '03  
GeneralRe: Coding Style Issues PinmemberAndy Neilson2:53 28 May '03  
GeneralRe: Coding Style Issues PinmemberMaximilian Hänel4:56 28 May '03  
GeneralJust to complete this PinmemberJahava4:53 25 Oct '03  
GeneralRe: Just to complete this PinmemberMarc Merritt8:42 25 Oct '03  
GeneralWhat an attitude... PinmemberJahava5:08 27 Oct '03  
GeneralRe: What an attitude... PinmemberMarc Merritt5:46 27 Oct '03  
GeneralIt's cool PinmemberJahava13:45 27 Oct '03  
GeneralRe: Coding Style Issues PinmemberJonathan C Dickinson1:53 5 May '08  
GeneralError messages PinmemberMaximilian Hänel1:24 6 May '03  
GeneralRe: Error messages PinmemberHaidong Chen12:15 27 May '03  
GeneralRe: Error messages PinmemberHaidong Chen12:17 27 May '03  
GeneralRe: Error messages PinmemberMaximilian Hänel12:47 27 May '03  
GeneralSeImpersonatePrivilege Pinmembergeo_m22:56 3 May '03  
GeneralThis method won't work on Windows 2000 or Windows NT PinmemberSoftomatix13:54 3 May '03  
GeneralRe: This method won't work on Windows 2000 or Windows NT PinmemberMarc Merritt15:53 3 May '03  

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.

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