Some Windows APIs return a primary token and some return an impersonation token. Some APIs require a primary token while others require an impersonation token.
For ex
A friend referred me to Programming Windows Security by Keith Brown which answers this question exactly.
Primary tokens can and should be called process tokens and impersonation tokens can and should be called thread tokens. Primary tokens can only be attached to process and impersonation tokens can only be attached to threads. That's all. They can indeed be freely converted using DuplicateTokenEx
(assuming you have the necessary access rights to the handle you wish to convert, obviously).
From page 115 in the book:
BOOL DuplicateTokenEx( HANDLE ExistingToken, // in DWORD DesiredAccess, // in LPSECURITY_ATTRIBUTES Attributes, // in, optional SECURITY_IMPERSONATION_LEVEL ImpLevel, // in TOKEN_TYPE Type, // in PHANDLE NewToken); // out
...
The
Type
parameter is a historical artifact. If you look at the definition of theTOKEN_TYPE
enumeration, you'll find that tokens have been taxonomized into two categories: impersonation versus primary tokens. Don't get hung up on this nomenclature; the meaning is actually much simpler than it sounds. Impersonation tokens can only be attached to threads, and primary tokens can only be attached to processes. That's all it means. The process token obtained earlier via OpenProcessToken was therefore a primary token.In very early versions of Windows NT (3.x), there were much more severe restrictions on what you could do with a token, depending on where you originally got it from, and hence the token type was introduced to track the intended usage of the token. Because this text assumes you're using Windows NT 4.0 or greater, just think of an impersonation token as a "thread token", and a primary token as a "process token", and use
DuplicateTokenEx
to convert between the two whenever necessary. Windows NT 4.0 tore down the boundaries between the two by introducingDuplicateTokenEx
; the Windows NT 3.x version of this function,DuplicateToken
, was hardcoded to only produce impersonation tokens. In fact, now you should be able to see the silly bug that causes the first call toSetThreadToken
to fail: the code is attempting to attach a primary token (the one obtained from the process) to a thread (which requires an impersonation token). That's a no-no. To fix both the logical problem and the silly historical problem, here's the corrected code:
Other stuff which aren't strictly an answer to the question but were mentioned in the question:
ImpersonateLoggedOnUser
goes the extra mile and converts primary tokens to impersonation tokens while SetThreadToken
doesn't bother. Why? who knows? Probably for the same reason some APIs enable privileges for the duration of the call while others require callers to enable the privileges on their own.LogonUser
(and LsaLogonUser
) probably return impersonation tokens for network logons because of the assumption of who usually perform network logons (p. 83 onwards, for example).ZwCreateToken
which requires pretty unusual privileges (specifically the unique SE_CREATE_TOKEN_NAME
). You probably shouldn't...