Impersonation and Exception Filters in v2.0

A while back, I wrote about a potential security hole when malicious code can set up an exception filter before calling your code which does impersonation.

In the final release of v2.0, we've added a feature to help mitigate this problem.  The CLR records that you've begun impersonation on the stack frame where you make the call to Impersonate().  If an exception is thrown, when the CLR walks the call stack looking for handlers, it will see this note and revert the impersonation when it moves past the frame.

This means that the following sample from my previous post would just work under v2.0:

public void SomeApi()
{
    // Call LogonUser to get a token for the user
    IntPtr userHandle = IntPtr.Zero();
    bool loggedOn = LogonUser(
        user,
        domain,
        password,
        LogonType.Interactive,
        LogonProvider.Default,
        out userHandle);
    if(!loggedOn)
        throw new Win32Exception(Marshal.GetLastWin32Error());

    // Begin impersonating the user
    WindowsImpersonationContext impersonationContext = null;
    try
    {
        WindowsIdentity.Impersonate(userHandle.Token);
        DoSomeWorkWhileImpersonating();
    }
    finally
    {
        // Clean up
        CloseHandle(userHandle);
        if(impersonationContext != null)
            impersonationContext.Undo();
    }
}

When the call to Impersonate() is made, the CLR notes that on SomeApi()'s stack frame and if DoSomeWorkWhileImpersonating() happens to throw, the impersonation is reverted before any callers of the SomeApi() have their exception filters run.

Note that since this state is tied to the stack frame, you won't get this benefit if you impersonate in one method and revert in another:

public void SomeOtherApi()
{

    // Begin impersonating the user
    WindowsImpersonationContext impersonationContext = null;
    try
    {
        impersonationContext = BeginImpersonating();
        DoSomeWorkWhileImpersonating();
    }
    finally
    {
        // Clean up
        if(impersonationContext != null)
            impersonationContext.Undo();
    }
}

Here, the impersonation is done in the BeginImpersonating() method, but is reverted in SomeOtherApi().  In this case, the stack frame for BeginImpersonating() is gone if DoSomeWorkWhileImpersonating() throws an exception.  Since the BeginImpersonating() stack frame is the one which contained the annotation that impersonation needed to be undone, you lose the automatic revert behavior.

Obviously getting the undo for free is a much better option than having to go through all the work of protecting your code manually, so as you begin upgrading your code to run with v2.0 of the framework, you might want to look for places where you don't both impersonate and undo the impersonation in the same method.