Click here to Skip to main content
Email Password   helpLost your password?

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

You must Sign In to use this message board.
 
 
Per page   
 FirstPrevNext
GeneralCreate a folder on a remote computer
Shlomo
2:52 6 Jan '10  
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
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 server
smithafebinkal
19:45 3 Dec '08  
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!!!
Arshad Kunnath
2:36 31 Jul '08  
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!!!
Marc Merritt
3:59 31 Jul '08  
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.
QuestionIt may be....
Willian.BR
9:55 15 May '08  
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 .net
questions_c
3:28 1 Oct '07  
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 .net
Marc Merritt
16:18 1 Oct '07  
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 .net
Pram_Singh41
5:22 20 Jul '09  
did you find a way out to run on Windows CE
GeneralError Code 1326 [modified]
collapo
1:56 21 Aug '07  
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 1326
mirh
2:59 10 Oct '07  
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
GeneralError 1314
m.aldegheri
7:28 3 Aug '06  
Hi,

I'm using Win 2000 Server and the sample application return:

LogonUser() failed with error code: 1314

and then:

DuplicateToken() failed with error code: 6

This error encurred only in a machine, in other machine works fine. I search the code on web and I think that I have to set some permission. But I don't know what.

Please help me, thanks Matteo
GeneralRe: Error 1314
m.aldegheri
23:23 3 Aug '06  
I'm sorry, setting the permission "Act as part of the operating system" works fine. Sorry again
GeneralRe: Error 1314
MunkieFish
8:49 11 Apr '07  
How do you do that? Can you show me an example.
AnswerRe: Error 1314
uppals
14:06 12 Apr '07  
* Log in as Administrator
* Execute gpedit.msc
* Go to "Local Computer Policy\Computer Configuration\Windows Settings\Security Settings\Local Policies\User Rights Assignment" and add the account being impersonated to “Act as part of the operating system”.

GeneralHelp needed Windows 2000 SP4 still getting 1314! -- Re: Error 1314
devvvy
21:41 21 Feb '08  
I'm using Windows 2000 SP4 still getting 1314 error (C# .net 2.0)

I've configured both impersonated and impersonating account as "Act as part of the operating system" but still getting 1314 error!

http://www.microsoft.com/technet/security/prodtech/windows2000/w2kccadm/win2kpol/w2kadm03.mspx[^]

Perhaps because both are DOMAIN account?

Thanks

devy

GeneralRe: Help needed Windows 2000 SP4 still getting 1314! -- Re: Error 1314
Marc Merritt
4:02 26 Feb '08  
If you have a global policy that overrides this setting then it explains why you cannot get it to work.
Generalhow to use this in case of a local account?
yossof elnaggar
21:29 27 Jun '06  
i used this solution to impersonate a domain user, but can't do with a local account.

the problem is:
- i have a workgroup, has two machine (mac1 & mac2)
- i create a local account on (mac1) [username=ysa,password=123]
- i create a shared folder on (mac1) called "Images", and assign the local user [ysa] to it.
- i want to access this shared folder from (mac2)



Yossef Elnaggar
GeneralRe: how to use this in case of a local account?
hk11
20:04 12 Jul '07  
http://www.codeproject.com/csharp/cpimpersonation1.asp?forumid=15321&select=2128115&df=100#xx2128115xx
GeneralFirst call takes too long
schweeneh
7:04 4 Apr '06  
The first call to ImpersonateUser() takes about 10 or more seconds, I assume this is because of the Import statements (I read an article that explained exactly why it takes so long but honestly didn't fully understand it). On the second call though, it's instantaneous. Is there a way to speed this process up or maybe Import the dlls when I start my application so that when I make the first call it doesn't get bogged down.

Thanks,

Chris

-- modified at 12:23 Tuesday 4th April, 2006
GeneralRe: First call takes too long
Marc Merritt
3:24 12 Jun '06  
10 seconds seems a bit excessive..? Honestly, I was still learning .NET when I wrote this article over 3 years ago, so I'd recommend using Uwe Keim's code instead of mine. His code is much more up-to-date and easier to use. I'd be curious to know if you are still incurring the latency issue, so let me know. Thanks!
GeneralA similar article
Uwe Keim
0:59 24 Apr '05  
Please see also my article [^] that is similar to this one.

--
Affordable Windows-based CMS for only 99 €: try www.zeta-producer.com for free!


GeneralRe: A similar article
craigg75
6:57 3 Nov '06  
Actually it's -- http://www.codeproject.com/csharp/ZetaImpersonator.asp
GeneralRe: A similar article
Uwe Keim
7:14 3 Nov '06  
Oh, they moved it to the official section Smile. thanks!

http://www.codeproject.com/csharp/ZetaImpersonator.asp[^]

--
Try our Windows-based CMS: www.zeta-producer.com
Try our ticket helpdesk system: www.zeta-helpdesk.com
See me working: www.magerquark.com

GeneralImpersonation
chriskoiak
6:13 16 Mar '04  
Hi,

Is it possible to contain impersonation to a given AppDomain rather than
impersonation consuming the full process?

I have a service running under the local system account, but need to
impersonate the current user for some functionality (e.g. accessing the
current users network share). However I don't want the full service to
change it's access rights. I can spawn a seperate process (which
impersonates a specifed user) but this therefore consumes more memory.

My service needs to run as the Local System account (not a specified account
particular to the domain).

Any ideas?

Thanks
Chris


QuestionRe: Impersonation
tee_jay
23:08 10 Apr '06  
Hi,
Even i have the same query..

I also want to impersonate a user (i'hav his credentials) from inside a service running under local system account so that i can access "Mapped Drives" that are mapped in the context of the user whom i want to impersonate.

I gave several experiments to this.. like trying and get the UNC name from the mapped name using WNet APIs but no results..

FYI: I can easily access network shares (using UNC names) to which i had access to. But couldnot access mapped drives.
Reason i'm aware of, that drives are mapped on a per user basis i.e. i cannot see a drive mapped in some another user's account.

But there must be some workaround.. like impersonating.. (but that too did not help..Frown)

If someone has a diffenent vision.. pls let us know..

Love,
TJ


Last Updated 30 Apr 2003 | Advertise | Privacy | Terms of Use | Copyright © CodeProject, 1999-2010