Home > Software engineering >  Why does a thread need to impersonate itself?
Why does a thread need to impersonate itself?

Time:01-11

The Win32 function AccessCheck checks whether the holder of an access token can access a (real or fictitious) resource based on its security descriptor.

But you can't just pass it the current access token (or any other token). If you do this, you get error 1309 ERROR_NO_IMPERSONATION_TOKEN.

Instead you have to call ImpersonateSelf to "impersonate the security context of the calling process". Then you can call AccessCheck.

What does ImpersonateSelf actually do? Why does a thread need to impersonate itself? Is a thread not already itself, is it not already its process?

Note I am not asking how to perform an access check, but why it must be done this way - what is the logic behind it?

CodePudding user response:

As RbMm said in the comments, you don't need to call ImpersonateSelf. What you do need to do is to pass an impersonation token with the required permissions on the handle. The documentation is very simple and clear about that:

[in] ClientToken

A handle to an impersonation token that represents the client that is attempting to gain access. The handle must have TOKEN_QUERY access to the token; otherwise, the function fails with ERROR_ACCESS_DENIED.

As RbMm said, you obviously passed a primary token rather than impersonation token, so you got an error that said exactly that.

You are incorrect saying that "you can't just pass it the current access token (or any other token)". You actually can pass lots of other tokens. They just have to be impersonation tokens rather than primary tokens.

ImpersonateSelf is not the only way to get an impersonation token from the process primary token and it's not necessarily the recommended way. You can simply call DuplicateToken (or DuplicateTokenEx) to create an impersonation token out of your primary token and pass that to AccessCheck.


The practical reason why you'd might want to use ImpersonateSelf is to make sure another thread in your process doesn't change anything in the global process state.

For example, let's say you're doing something like:

auto my_primary_token =  get_current_process_token(); // nice wrapper around GetCurrentProcessToken
auto impersonation_token = duplicate_token(my_primary_token, SecurityImpersonation); // nice wrapper around DuplicateToken

auto sd = get_security_descriptor(some_resource);
auto access_rights = access_check(sd, impersonation_token, SOME_ACCESS_RIGHT); // nice wrapper around AccessCheck

// ***

if (access_rights) {
    auto h = CreateFile(some_resource.filename(), SOME_ACCESS_RIGHT, ...);
}

What happens if you got the desired access only because of a privilege you're holding (e.g. backup privilege), but at the moment marked with three asterisks another thread removes this privilege from the process token?

The call to CreateFile will fail and you'd be surprised.

To prevent that from happenning you should impersonate the same token you used for the access check and performs the operation with the same rights you performed the check on.

You can call ImpersonateSelf to first impersonate and then make the access check or you can call DuplicateToken, make the access check, and if it succeeds and you want to perform the operation call ImpersonateLoggedOnUser/SetThreadToken.


As for your question about what ImpersonateSelf does, it's something like:

ImpersonateSelf(...) {
    OpenProcessToken(...);
    DuplicateToken(...);
    SetThreadToken(...)
}

But with parameter validation and error checking.

You can see a more real version of the code in the base/ntos/rtl/sertl.c:RtlpImpersonateSelfEx function in the WRK.

CodePudding user response:

According to the Doc: ImpersonateSelf function

The ImpersonateSelf function obtains an access token that impersonates the security context of the calling process. The token is assigned to the calling thread.

ImpersonateSelf function duplicates the process token and assigns that to be the impersonation token of the calling thread. The thread no longer uses the old token.

According to the Doc: AccessCheck function

[in] ClientToken: A handle to an impersonation token that represents the client that is attempting to gain access.

Agree with RbMm, you need pass handle to an impersonation token to function AccessCheck.

  • Related