Why doesn’t RevertToSelf undo the most recent SetThreadToken?


A customer was experiencing unexpected behavior in their Windows service process with respect to impersonation. The customer's question had two parts. Let's take them one at a time.

Our service receives a request from a client and impersonates the client in order to satisfy the request.

As part of satisfying the request, the service needs to impersonate a specific unrelated identity in order to get some information. That nested impersonation is done with Set­Thread­Token.

When the nested impersonation is complete, we call Revert­To­Self. But this does not restore the impersonation to the original client; instead, the thread loses all impersonation and becomes "Network Service", which is the token of the service process.

Is this how the Revert­To­Self function is supposed to work? MSDN doesn't explicitly mention this.

Here's what MSDN says about Revert­To­Self:

Revert­To­Self function

The Revert­To­Self function terminates the impersonation of a client application.

It states right there that Revert­To­Self ends impersonation. When it returns, impersonation has terminated. It is an ex-impersonation.

I guess that's why the function is called Revert­To­Self and not Revert­To­Previous­Token­Prior­To­Most­Recent­Call­To­Set­Thread­Token.

The thread token is a single value. It's not a stack of values; Set­Thread­Token does not push a new value onto the top of the stack, and Revert­To­Self does not pop the top value off the stack and reveal the previous value. For one thing, that model would make it hard to manage impersonation if you wanted to change impersonation in a non-stack-like manner. Second, maintaining a stack of tokens would create problems if somebody destroyed a token while it was still in the token stack.

Nope, a thread token is just one token. When you call Set­Thread­Token, it replaces the token. When you call Revert­To­Self, the token is cleared and the thread no longer has a token. Maybe Revert­To­Self should have been named Clear­Thread­Token, since that would emphasize that the function erases any existing thread token, leaving the thread to inherit the identity of its host process.

If you want to change impersonation to some other identity, then call Set­Thread­Token with the token whose identity you want to impersonate.

Okay, that's part one. The customer's original question anticipated this answer and had a follow-up question.

Presumably, if this is the expected behavior of the Revert­To­Self function, then what the code needs to do in order to perform the nested impersonation is

  1. Call Get­Thread­Token to get the current impersonation token.

  2. Call Set­Thread­Token to set the nested impersonation token.

  3. Do the necessary work.
  4. To end nested impersonation, call Set­Thread­Token with the token obtained in step 1 to restore the thread token to the original impersonation token.

Is that correct?

Close.

It's possible that step 1 will fail with ERROR_NO_TOKEN. That happens if the thread is not impersonating at all, which means that your code is operating from a flawed assumption. In that case, you have no nested impersonation; you just have impersonation. Step 4 needs to be adjusted as follows:

  1. If step 1 failed with ERROR_NO_TOKEN, then call Revert­To­Self to end impersonation. If step 1 succeeded, then the thread was previously impersonating, in which case call Set­Thread­Token with the token obtained in step 1 to restore the thread token to the original impersonation token.

  2. Close the thread token obtained in step 1, if any.

The customer replied, "Thanks. It appears that we misunderstood the statement in MSDN."

Comments (21)
  1. Joshua says:

    Wise customer.

  2. Medinoc says:

    At first I wondered why close the thread token in step 1, but apparently the thing is that GetCurrentThreadToken() returns a pseudo-handle, therefore DuplicateHandle() is necessary (and thus, step 1 does create a new token handle that must be closed).

  3. It’s a bit disappointing that this special case is actually necessary, this is a case where a model similar to device contexts (where there’s always a valid “something” selected, even if a “magic” null one, and there’s a SelectObject-like to perform switches) would have proven nicer to work with.

  4. Thomas Anderson says:

    The SetThreadToken docs say “If Token is NULL, the function causes the thread to stop using an impersonation token.”. So you should be able to pass NULL in step 4 in the case where GetThreadToken failed. (I think?).

    1. Yup, that’s certainly simpler.

    2. Neil says:

      “causes the thread to stop using an impersonation token” sounds like the phrase that should have been used to describe the effect of RevertToSelf.

  5. andy_k says:

    > When it returns, impersonation has terminated. It is an ex-impersonation.

    The impersonation is not dead, it’s resting.

    1. The_Assimilator says:

      This impersonation is deceased! It is DEMISED!

  6. Ray Koopa says:

    I totally agree with you that “ClearThreadToken” might have been a better name.

  7. Mr Chen, it appears your blog’s font problem (mentioned in previous feedbacks) is still not resolved. The blog looks awful on any system other than Windows Vista or later. (That means iPhone, iPad, Android tablets and phones, etc.) If I am remembering right, you said you’ve filed a ticket with your … webmaster or webmistress?

    Your neighboring Windows PowerShell Blog seems to have solved this problem already.

    1. Boris says:

      I just want the old color scheme back, since white on black is more about uniformity and loss of identity among so many other MSDN blogs. The spirit of oldnewthing requires adaptation to new requirements and technology while also maintaining continuity with the past.

      1. Ron O says:

        A current/recommended solution is to use the Stylish plugin/extension (I know it’s available for both Firefox and Chrome) and then add The Old New Thing Classic Style (https://userstyles.org/styles/121616/the-old-new-thing-classic-style).

        1. Ian Yates says:

          Thanks for the stylish link. The familiar blog is back! :)

    2. I don’t have an iPad so I don’t know how bad it looks there. Tell me what to fix.

      1. Then don’t trust ipadpreview.com ever again. It is using the local font.

        I have an actual iPad. It is not okay.

        1. Scarlet Manuka says:

          In what way is that telling him what to fix? Anyone reading this blog ought to be able to leave a better bug report than “it is not okay”.

      2. Nick says:

        The theme sets font-family to “Segoe UI.” Add more fonts in the fallback (font-family: “Segoe UI”, sans-serif; would mostly solve it).

  8. cheong00 says:

    That said the instructions in the followup question may work for them “for this case”.

    Since in the original qusetion, the customer mentioned reverting to “Network Service” is bad for them, I’ll think perheps their application code is always running under some impersonation context, so maybe the GetThreadToken() call would always succeed.

    1. Scarlet Manuka says:

      That’s certainly possible, but it’s also possible that they may or may not be impersonating originally, but they’re only asking about the case where they are because that’s the case where their code isn’t working. It’s prudent to make your answers a bit more general than the exact scope of the stated problem.

  9. Hofi says:

    Any chance to get similar details in cases like this in the msdn documentation?

Comments are closed.

Skip to main content