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.