windowswinapiwindows-security

What's the difference between a Primary Token and an Impersonation Token


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 example, LogonUser usually returns a primary token, except when using LOGON32_LOGON_NETWORK as the logon type (dwLogonType):

In most cases, the returned handle is a primary token that you can use in calls to the CreateProcessAsUser function. However, if you specify the LOGON32_LOGON_NETWORK flag, LogonUser returns an impersonation token that you cannot use in CreateProcessAsUser unless you call DuplicateTokenEx to convert it to a primary token.

SetThreadToken requires an impersonation token while ImpersonateLoggedOnUser which seems to do pretty much the same thing takes either one.

CreateProcessAsUser and CreateProcessWithTokenW both require a primary token and both note a primary token can be acquired from an impersonation token by calling DuplicateTokenEx, but what do the token types mean?

The glossary says the following:

access token

An access token contains the security information for a logon session. The system creates an access token when a user logs on, and every process executed on behalf of the user has a copy of the token. The token identifies the user, the user's groups, and the user's privileges. The system uses the token to control access to securable objects and to control the ability of the user to perform various system-related operations on the local computer. There are two kinds of access token, primary and impersonation.

primary token

An access token that is typically created only by the Windows kernel. It may be assigned to a process to represent the default security information for that process.

impersonation token

An access token that has been created to capture the security information of a client process, allowing a server to "impersonate" the client process in security operations.

But that's not entirely useful. It seems like somebody wanted to use big boy words like "kernel" but this only serves to raise more questions such as what else (besides being assigned to a process) can a primary token be used for and who else besides the kernel can create access tokens?

(Do they mean it the the Microsoft sense where the Kernel is only part of what runs in kernel-mode and there's also the Executive etc. or do they mean that user-mode code can also create tokens? Regardless, even if a user-mode code can create tokens it will have to do it through a system-call, as with any Object Manager object, so the token will actually be created in kernel mode anyway.)

Anyway, this doesn't answer the fundamental question: What the difference between the token types? Not what they may be used for or how they are usually created.


Solution

  • 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 processes, 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 the TOKEN_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 introducing DuplicateTokenEx; 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 to SetThreadToken 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: