• Sign In
 
  • MSDN Blogs
  • Microsoft Blog Images
  • More ...
Common Tasks
  • Blog Home
  • Email Blog Author
  • RSS for comments
  • RSS for posts
Search
  • Advanced search options...
Tags
  • .NET Framewor
  • .NET Framework
  • Ajax/Javascript
  • ASP.NET
  • CLR
  • Cool stuff
  • DataAccess
  • Debugging/Windbg
  • Hotfix/Service Pack
  • IDEVDataCollector
  • IIS
  • Internet Explorer
  • Italian techs
  • LogParser
  • OT
  • Personal
  • Productivity
  • Random
  • Scripting/ASP
  • Security
  • Technology
  • Tools
  • Troubleshooting
  • Vista/Longhorn
  • Visual Studio
Archives
Archives
  • November 2010 (1)
  • October 2010 (1)
  • July 2010 (2)
  • April 2010 (1)
  • March 2010 (2)
  • February 2010 (2)
  • January 2010 (1)
  • October 2009 (2)
  • September 2009 (2)
  • August 2009 (1)
  • July 2009 (5)
  • June 2009 (1)
  • May 2009 (1)
  • April 2009 (3)
  • March 2009 (3)
  • February 2009 (5)
  • January 2009 (3)
  • December 2008 (5)
  • November 2008 (3)
  • October 2008 (2)
  • September 2008 (3)
  • August 2008 (3)
  • July 2008 (3)
  • June 2008 (5)
  • May 2008 (4)
  • April 2008 (8)
  • March 2008 (4)
  • February 2008 (5)
  • January 2008 (2)
  • December 2007 (4)
  • November 2007 (6)
  • October 2007 (6)
  • September 2007 (8)
  • August 2007 (6)
  • July 2007 (7)
  • June 2007 (10)
  • May 2007 (9)
  • April 2007 (12)
  • March 2007 (8)
  • February 2007 (5)
  • January 2007 (3)
  • December 2006 (1)
  • November 2006 (4)
  • October 2006 (2)
  • September 2006 (9)
  • August 2006 (2)
  • July 2006 (1)

Remember to undo your impersonation

MSDN Blogs > Never doubt thy debugger > Remember to undo your impersonation

Remember to undo your impersonation

Carlo Cardella
29 Sep 2007 5:56 PM
  • Comments 5

A couple of weeks ago I got an interesting query from a customer, whom had a problem impersonating a service user account by code; the design was a bit more complicated, though:

  • Impersonation not set in web.config
  • By code they needed to impersonate the account logged on the client issuing the HTTP request (this worked fine)
  • By code they needed to impersonate a service account they used to access a backend database (here they were getting an access denied error)
  • Switch back to the previous user, the one logged on the client (again this was working fine)

This was quite clearly an impersonation problem, and after some debugging we found the "Access Denied" was being thrown when executing the line highlighted in red in the following snippet, way before even trying to access the network to read the backend database:

 1: If CType(LogonUser(username, domain, password, LOGON32_LOGON_INTERACTIVE, LOGON32_PROVIDER_DEFAULT, token), Boolean) Then
 2:    If DuplicateToken(token, 2, tokenDuplicate) Then
 3:    Dim identity As New WindowsIdentity(tokenDuplicate)
 4:       If System.Web.HttpContext.Current Is Nothing Then
 5:          Dim mImpersonatedContext As WindowsImpersonationContext = identity.Impersonate
 6:       Else
 7:          System.Web.HttpContext.Current.Items("ImpersonationContext") = identity.Impersonate
 8:       End If
 9:    End If
 10: [...]

In the screenshot below you can see the "Access Denied" message when trying to display the WindowsIdentity.Name property

Autos: access is denied

Since I was able to repro on my machine, I attached WinDbg to the worker process and set a breakpoint on advapi32!ImpersonateLoggedOnUser and having a look at the stack and managed exceptions the problem was quite clear. !gle shows the last error for the current thread:

   1: 0:017> !gle
   2: LastErrorValue: (Win32) 0x5 (5) - Access is denied.
   3: LastStatusValue: (NTSTATUS) 0xc0000022 - {Access Denied}  A process has requested access to an object, 
   4:     but has not been granted those access rights.

Also confirmed by the managed exceptions:

   1: Exception object: 021314ec
   2: Exception type: System.Web.HttpException
   3: Message: An error occurred while attempting to impersonate.  Execution of this request cannot continue.
   4: InnerException: <none>
   5: StackTrace (generated):
   6:     SP       IP       Function
   7:     01ECF4F0 044DCB73 System.Web.ImpersonationContext.GetCurrentToken()
   8:     01ECF534 041E3BE9 System.Web.ImpersonationContext.get_CurrentThreadTokenExists()
   9:     01ECF564 0417FB4E System.Web.HttpApplication.ExecuteStep(IExecutionStep, Boolean ByRef)
  10:     01ECF61C 041922CC System.Web.HttpApplication+ApplicationStepManager.ResumeSteps(System.Exception)
  11:     01ECF66C 0417EEA6 System.Web.HttpApplication.System.Web.IHttpAsyncHandler.BeginProcessRequest(System.Web.HttpContext, System.AsyncCallback, System.Object)
  12:     01ECF688 04183DB5 System.Web.HttpRuntime.ProcessRequestInternal(System.Web.HttpWorkerRequest)
  13:  
  14:  
  15: Exception object: 021312a8
  16: Exception type: System.Security.SecurityException
  17: Message: Access is denied.
  18:  
  19: InnerException: <none>
  20: StackTrace (generated):
  21:     SP       IP       Function
  22:     01ECF24C 79636928 System.Security.Principal.WindowsIdentity.GetCurrentInternal(System.Security.Principal.TokenAccessLevels, Boolean)
  23:     01ECF26C 79389652 System.Security.Principal.WindowsIdentity.GetCurrent()
  24:     01ECF278 06350A92 WebApplication1._Default.Page_Load(System.Object, System.EventArgs)
  25:     01ECF318 04301954 System.Web.UI.Control.OnLoad(System.EventArgs)
  26:     01ECF328 043019A0 System.Web.UI.Control.LoadRecursive()
  27:     01ECF33C 043147C4 System.Web.UI.Page.ProcessRequestMain(Boolean, Boolean)
  28:     01ECF50C 04312982 System.Web.UI.Page.ProcessRequest(Boolean, Boolean)
  29:     01ECF544 0431285F System.Web.UI.Page.ProcessRequest()
  30:     01ECF57C 0431277F System.Web.UI.Page.ProcessRequestWithNoAssert(System.Web.HttpContext)
  31:     01ECF584 04312712 System.Web.UI.Page.ProcessRequest(System.Web.HttpContext)
  32:     01ECF598 063502F6 ASP.default_aspx.ProcessRequest(System.Web.HttpContext)
  33:     01ECF5A4 041BA93F System.Web.HttpApplication+CallHandlerExecutionStep.System.Web.HttpApplication.IExecutionStep.Execute()
  34:     01ECF5DC 0417FAD1 System.Web.HttpApplication.ExecuteStep(IExecutionStep, Boolean ByRef)

As a test we added the Job Manager account to the local Administrators group and the problem went away, so this was clearly a lack of permission for that account; specifically, the Job Manager user was not allowed to "take ownership" of the current thread, which was already impersonating the client account whom issued the HTTP request. Since we where using service account we checked the permission required in the article Process and request identity in ASP.NET, in particular the ASPNET account specific permission configurable through the Group Policy snap-in (gpedit.msc); we added the required permission, but the problem was still there.

We then found a quite old (but still applicable) KB article which seemed to be applicable to this problem: LogonUser fails in ISAPI extensions. Here's the interesting part:

CAUSE

The code inside LogonUser tries to open the process token. It fails since the authenticated user may not have access to the process token (SYSTEM if it's an inproc ISAPI.)

RESOLUTION

As a temporary workaround, you can call RevertToSelf to return the thread to the security context of the process token before calling LogonUser.

STATUS

This behavior is by design.

So, going back in the stack where the failing call started, here is what we find:

   1: Dim test As New customer
   2: Dim col As List(Of CustomerDetails)
   3: Dim p As WindowsPrincipal = HttpContext.Current.User
   4: Dim id As WindowsIdentity = p.Identity
   5:  
   6: 'impersonate the Windows Authenticated User
   7: Dim wic As WindowsImpersonationContext = id.Impersonate()
   8: col = test.getcustomers() ' Works okay to access database
   9:  
  10: 'Error Switching below, step into code
  11: CBPUser.SwitchToIOUser()   ' Access Denied, step through code to see issue arise.
  12: col = test.getcustomers()  ' Fails to access database
  13:  
  14: CBPUser.SwitchFromIOUser() ' Switch back
  15: col = test.getcustomers()  ' Works okay to access database again

I then had a look at the MSDN docs about impersonation, especially to find some sample code, like for example How To: Using impersonation and delegation in ASP.NET 2.0 and How to implement impersonation in an ASP.NET application: interesting enough all the samples in those articles always revert the impersonation calling the WindowsImpersonationContext.Undo() method (which under the covers ultimately calls RevertToSelf(), as you can guess)...

Since testing the code in practice is easier an quicker, I added the Undo() call and run it again:

   1: Dim test As New customer
   2: Dim col As List(Of CustomerDetails)
   3: Dim p As WindowsPrincipal = HttpContext.Current.User
   4: Dim id As WindowsIdentity = p.Identity
   5:  
   6: ' impersonate the Windows Authenticated User
   7: Dim wic As WindowsImpersonationContext = id.Impersonate()
   8: col = test.getcustomers() ' Works okay to access database
   9: wic.Undo()
  10:  
  11: ' Error Switching below, step into code
  12: CBPUser.SwitchToIOUser()   'Works fine now, no more access denied!
  13: col = test.getcustomers()  'Works ok to access database
  14:  
  15: CBPUser.SwitchFromIOUser() ' Switch back
  16: col = test.getcustomers()  ' Works okay to access database again

Much better! smile_regular Just to be sure, Sql Profiler shown a connection with the service account which was our final goal. So the final message is: remember to always use Undo() when you're done with your code impersonation.

Case closed Sherlock! smile_nerd

 

Carlo


Quote of the Day:
Resentment is like taking poison and hoping the other person dies.
--St. Augustine
  • 5 Comments
ASP.NET, Security
Leave a Comment
  • Please add 6 and 5 and type the answer here:
  • Post
Comments
  • Techy News Blog » Remember to undo your impersonation
    29 Sep 2007 6:08 PM

    PingBack from http://www.artofbam.com/wordpress/?p=3928

  • Alik Levin
    30 Sep 2007 3:43 AM

    To identify the problem one can also use procmon from sysinternals or set auditing in the accessed objects, if the "object" is SQL Server then SQL profiler would show the actual account that accesses it. You may find these helpful next time t-shooting identity flow challenges :).

    http://blogs.msdn.com/alikl/archive/2007/04/11/authentication-hub.aspx

    procmon and auditing is demonstrated here

    http://blogs.msdn.com/alikl/archive/2007/04/03/who-access-my-file.aspx

    http://blogs.msdn.com/alikl/archive/2007/04/01/file-access-auditing-i-am-not-afraid-of-gpo.aspx

  • Carlo Cardella
    30 Sep 2007 6:24 AM

    Yep, we actually used procmon at the beginning, when one of the assumptions we were making was the job manager account was missing special permission needed to access core ASP.NET files and folders, but we didn't get any "Access denied" there...

    In fact the problem was not accessing a file or folder but rather we were failing changing the account for the specific thread, not for the entire process, and procmon just shows me NETWORK SERVICE which is the account running the application pool.

  • Noticias externas
    8 Mar 2008 2:48 PM

    There are circumstances where taking a dump is not possible or simply not convenient; imagine a situation

  • Never doubt thy debugger
    27 Apr 2008 1:05 PM

    There are circumstances where taking a dump is not possible or simply not convenient; imagine a situation

Page 1 of 1 (5 items)
  • © 2012 Microsoft Corporation.
  • Terms of Use
  • Trademarks
  • Privacy Statement
  • Report Abuse
  • 5.6.402.223