-
Couldn't load subscription status.
- Fork 270
Token Helpers
WIL Windows Security Token methods make using thread and process tokens easier.
The token helpers can be used by including the correct header file:
#include <wil/token_helpers.h>A thread running with a different token than the token of its parent process is said to be impersonating. In most cases, security decisions should be made using the current access token - the thread token if set, the process token otherwise. This method returns a handle to that effective token, checking first the thread and then the process.
unique_handle open_current_acess_token(unsigned long access = TOKEN_QUERY, OpenThreadTokenAs openAs = OpenThreadTokenAs::Self);By default the token is opened with TOKEN_QUERY access
which allows inspection of the token but nothing else. This parameter is
passed along to OpenThreadToken and OpenProcessToken.
The OpenThreadTokenAs::Self option controls whether the call to
OpenThreadToken passes TRUE for OpenAsSelf. This is useful
when the current thread is a low-rights token and the desired access
is available to the process but not to the thread token.
The returned token handle is a regular token handle and can be used with any other token-accepting API.
Example:
auto check_token = wil::open_current_access_token(TOKEN_QUERY);
auto check_token = wil::open_current_access_token(TOKEN_IMPERSONATE | TOKEN_QUERY, OpenThreadTokenAs::Self);- The
nothrowversion clears the output token parameter and returnsGetLastErroras anHRESULT. - The
failfastversion fails fast if the token cannot be opened.
Windows pseudo tokens can be used with many token-accepting APIs. These pseudo-handles are context-sensitive but do not need lifecycle management. Examples include:
wil::GetCurrentThreadEffectiveTokenWithOverride takes a token parameter,
and if it's nullptr, returns GetCurrentThreadEffectiveToken instead.
Useful for methods that want to take nullptr to mean "the current token"
rather than pushing a call to GetCurrentThreadEffectiveToken upstream.
Examples:
THROW_IF_WIN32_BOOL_FALSE(::AccessCheck(
...,
wil::GetCurrentThreadEffectiveTokenWithOverride(m_myToken.get()),
...));GetTokenInformation
returns many different kinds of information about a token in a single
method call. Some information is variably-sized, requiring the typical
two-call pattern and management of an allocation. WIL's helper method
simplifies this operation.
The token methods follow these patterns:
// Variably-sized data emitted in a unique_ptr<>
template<typename T>
wistd::unique_ptr<T> get_token_information(_In_ HANDLE token = nullptr);
// Statically-sized data emitted in a unique_ptr<>
template<typename T>
T get_token_information(_In_ HANDLE token = nullptr);-
TA structure related to a token information class. The structure type is matched to aTOKEN_INFORMATION_CLASSwhen callingGetTokenInformation. When the class data is variably sized (such asTOKEN_USER) the result is emitted inside awistd::unique_ptr<T>.
A list of the supported structures and class levels:
TOKEN_INFORMATION_CLASS |
Output Type |
|---|---|
TokenAccessInformation |
wistd::unique_ptr<TOKEN_ACCESS_INFORMATION> |
TokenAppContainerSid |
wistd::unique_ptr<TOKEN_APPCONTAINER_INFORMATION> |
TokenDefaultDacl |
wistd::unique_ptr<TOKEN_DEFAULT_DACL> |
TokenGroupsAndPrivileges |
wistd::unique_ptr<TOKEN_GROUPS_AND_PRIVILEGES> |
TokenIntegrityLevel |
wistd::unique_ptr<TOKEN_MANDATORY_LABEL> |
TokenOwner |
wistd::unique_ptr<TOKEN_OWNER> |
TokenPrimaryGroup |
wistd::unique_ptr<TOKEN_PRIMARY_GROUP> |
TokenPrivileges |
wistd::unique_ptr<TOKEN_PRIVILEGES> |
TokenUser |
wistd::unique_ptr<TOKEN_USER> |
TokenElevationType |
TOKEN_ELEVATION_TYPE |
TokenMandatoryPolicy |
TOKEN_MANDATORY_POLICY |
TokenOrigin |
TOKEN_ORIGIN |
TokenSource |
TOKEN_SOURCE |
TokenStatistics |
TOKEN_STATISTICS |
TokenType |
TOKEN_TYPE |
TokenImpersonationLevel |
SECURITY_IMPERSONATION_LEVEL |
TokenElevation |
TOKEN_ELEVATION |
-
tokenWhen passed, this is the token whose information is to be queried. Both real and pseudo-tokens are valid parameters. Real tokens must have been opened withTOKEN_QUERYaccess. When defaulted (or explicitly passed asnullptr) the information is queried from the effective thread token.
// Reset the owner of the key to the effective user
THROW_IF_WIN32_ERROR(::SetNamedSecurityInfo(
keyPath,
SE_REGISTRY_KEY,
OWNER_SECURITY_INFORMATION,
wil::get_token_information<TOKEN_USER>()->User.Sid,
nullptr, nullptr, nullptr));// See if the passed-in token can be impersonated
if (wil::get_token_information<TOKEN_TYPE>() == TokenImpersonation)
{
// ...
}Nonthrowing variants emit their results through their first parameter. When
the information query fails GetLastError is returned inside an HRESULT.
template<typename T>
HRESULT get_token_information_nothrow(wistd::unique_ptr<T>& tokenInfo, _In_ HANDLE token = nullptr);
template<typename T>
HRESULT get_token_information_nothrow(T& tokenInfo, _In_ HANDLE token = nullptr);Failfast variants fail-fast when the query operation fails.
// Variably-sized data emitted in a unique_ptr<>
template<typename T>
wistd::unique_ptr<T> get_token_information_failfast(_In_ HANDLE token = nullptr);
// Statically-sized data emitted in a unique_ptr<>
template<typename T>
T get_token_information_failfast(_In_ HANDLE token = nullptr);Impersonation changes the current thread token to another token so the
thread can access resources available to the impersonated token. The key
method is SetThreadToken
which replaces the current thread token with a new token. Many implementations
treat the thread token like a 'stack', pushing and popping tokens onto
the thread and removing them after access operations are completed.
unique_token_reverter impersonate_token(_In_ HANDLE token);This method calls SetThreadToken with its parameter and returns a scope-
exit object that resets the token back to whatever it had been before.
The passed-in token must beena acquired with TOKEN_IMPERSONATE access.
If impersonation fails GetLastError is thrown inside a wil::ResultException.
as an HRESULT.
Impersonation is reverted when the returned object is destructed or
.reset().
// Open the caller's log file
auto runAs = wil::impersonate_token(m_callerToken.get());
wil::unique_hfile log { ::CreateFile( ..., FILE_READ, ... ) };
// Go back to running as us - the log file handle still has 'read' access
runAs.reset();
::ReadFile( log.get(), ... );A nonthrowing version takes the "undo" object as a parameter and
initializes it during the call. Errors during impersonation are
emitted as GetLastError wrapped in an HRESULT.
HRESULT impersonate_token_nothrow(HANDLE token, unique_token_reverter& reverter);A failfast variant returns the reverter object but fails-fast when impersonation fails.
unique_token_reverter impersonate_token_failfast(HANDLE token);A common operation in a higher-privileged server is to "run as itself" when accessing
resources unavailable to its caller. This operation is a wrapper around calling
wil::impersonate_token with nullptr - "no token." As the thread is no longer
impersonating anyone, access checks are performed under the process token instead.
unique_token_reverter run_as_self();
HRESULT run_as_self_nothrow(unique_token_reverter&);
unique_token_reverter run_as_self_failfast();Example:
void WriteAccessEntry()
{
// This serivce immediately impersonates its RPC client for the duration of an
// operation, elevating back to 'self' only as necessary. Here it captures the SID
// of the caller, opens its log file for write, saves the SID, and continues.
auto userId = wil::get_token_information<TOKEN_USER>();
// Switch to running as the service
auto runAsSelf = wil::run_as_self();
wil::unique_hfile log { ::CreateFile( ..., FILE_WRITE, ...) };
::WriteFile(log.get(), userId->User.Sid, GetLengthSid(userId->User.Sid), &wrote);
} // impersonation reverts here, back to the RPC caller's tokenThis method returns an initialized object structured like a
SID
but without requiring an allocation or call to AllocateAndInitializeSid
by using a stack/heap allocated structure. It's convenient when the SID is fixed
and known at compilation time.
template<typename... Ts>
constexpr auto make_static_sid(const SID_IDENTIFIER_AUTHORITY&, Ts&&... subAuthorities);The returned type is an instance of wil::details::sid_t<> physically laid out like
a _SID structure. It contains precisely sizeof...(Ts) subauthorities.
Example:
auto systemSid = wil::make_static_sid(
SECURITY_NT_AUTHORITY,
SECURITY_BUILTIN_DOMAIN_RID,
DOMAIN_ALIAS_RID_ADMINS);
RETURN_IF_WIN32_ERROR(::SetNamedSecurity(..., &systemSid, ...));APIs that accept PSID will accept &sid_t<N> as PSID is an alias of PVOID. For
explicit conversion, use the .get() method which returns PSID.
As the SECURITY_NT_AUTHORITY is common on Windows a convenience wrapper is provided
that only takes the subauthorities list:
auto sid = wil::make_static_nt_sid(SECURITY_BUILTIN_DOMAIN_RID, DOMAIN_ALIAS_RID_ADMINS);This method accepts a SID definition, constructs a sid_t around it, and calls the
platform API CheckTokenMembership.
When the SID has an enabled entry in the token this method returns
true.
template<typename... Ts>
bool test_token_membership(_In_ HANDLE token, const SID_IDENTIFIER_AUTHORITY& authority, Ts&&... subAuthorities);As a sample this method checks to see if the caller is a member of the "domain guests" group:
bool IsGuest()
{
return wil::test_token_membership(nullptr, SECURITY_NT_AUTHORITY, SECURITY_BUILTIN_DOMAIN_RID, DOMAIN_ALIAS_RID_GUESTS);
}-
tokenThe token to use when looking for membership. Passingnullptruses the effective thread token instead. -
authorityASID_IDENTIFIER_AUTHORITYused in relationship to the subauthorities. MSDN has a list of know authorities. -
subAuthoritiesZero or more subauthorities may be passed in.
A nonthrowing variant places the result of testing membership in an out-parameter
and returns any errors during the check as GetLastError wrapped in an HRESULT.
template<typename... Ts>
HRESULT test_token_membership_nothrow(_Out_ bool* result, _In_ HANDLE token,
const SID_IDENTIFIER_AUTHORITY& authority,
Ts&&... subAuthorities);A failfast variant returns the result of the test on success and fails-fast otherwise.
template<typename... Ts>
bool test_token_membership_failfast(_In_ HANDLE token, const SID_IDENTIFIER_AUTHORITY& authority, Ts&&... subAuthorities);This method is similar to wil::get_token_information purpose-built for querying
the TokenIsAppcontainer information level. See GetTokenInformation
for a special note on using this information level.
bool get_token_is_appcontainer(_In_ HANDLE token = nullptr);
HRESULT get_token_is_appcontainer_nothrow(_Out_ bool* isAppcontainer, _In_ HANDLE token = nullptr);
bool get_token_is_appcontainer_failfast(_In_ HANDLE token = nullptr);