1 //********************************************************* 2 // 3 // Copyright (c) Microsoft. All rights reserved. 4 // This code is licensed under the MIT License. 5 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF 6 // ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED 7 // TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A 8 // PARTICULAR PURPOSE AND NONINFRINGEMENT. 9 // 10 //********************************************************* 11 #ifndef __WIL_TOKEN_HELPERS_INCLUDED 12 #define __WIL_TOKEN_HELPERS_INCLUDED 13 14 #ifdef _KERNEL_MODE 15 #error This header is not supported in kernel-mode. 16 #endif 17 18 #include "resource.h" 19 #include <new> 20 #include <lmcons.h> // for UNLEN and DNLEN 21 #include <processthreadsapi.h> 22 23 // for GetUserNameEx() 24 #define SECURITY_WIN32 25 #include <Security.h> 26 27 namespace wil 28 { 29 /// @cond 30 namespace details 31 { 32 // Template specialization for TOKEN_INFORMATION_CLASS, add more mappings here as needed 33 // TODO: The mapping should be reversed to be MapTokenInfoClassToStruct since there may 34 // be an info class value that uses the same structure. That is the case for the file 35 // system information. 36 template<typename T> struct MapTokenStructToInfoClass; 37 template<> struct MapTokenStructToInfoClass<TOKEN_ACCESS_INFORMATION> { static const TOKEN_INFORMATION_CLASS infoClass = TokenAccessInformation; static const bool FixedSize = false; }; 38 template<> struct MapTokenStructToInfoClass<TOKEN_APPCONTAINER_INFORMATION> { static const TOKEN_INFORMATION_CLASS infoClass = TokenAppContainerSid; static const bool FixedSize = false; }; 39 template<> struct MapTokenStructToInfoClass<TOKEN_DEFAULT_DACL> { static const TOKEN_INFORMATION_CLASS infoClass = TokenDefaultDacl; static const bool FixedSize = false; }; 40 template<> struct MapTokenStructToInfoClass<TOKEN_GROUPS_AND_PRIVILEGES> { static const TOKEN_INFORMATION_CLASS infoClass = TokenGroupsAndPrivileges; static const bool FixedSize = false; }; 41 template<> struct MapTokenStructToInfoClass<TOKEN_MANDATORY_LABEL> { static const TOKEN_INFORMATION_CLASS infoClass = TokenIntegrityLevel; static const bool FixedSize = false; }; 42 template<> struct MapTokenStructToInfoClass<TOKEN_OWNER> { static const TOKEN_INFORMATION_CLASS infoClass = TokenOwner; static const bool FixedSize = false; }; 43 template<> struct MapTokenStructToInfoClass<TOKEN_PRIMARY_GROUP> { static const TOKEN_INFORMATION_CLASS infoClass = TokenPrimaryGroup; static const bool FixedSize = false; }; 44 template<> struct MapTokenStructToInfoClass<TOKEN_PRIVILEGES> { static const TOKEN_INFORMATION_CLASS infoClass = TokenPrivileges; static const bool FixedSize = false; }; 45 template<> struct MapTokenStructToInfoClass<TOKEN_USER> { static const TOKEN_INFORMATION_CLASS infoClass = TokenUser; static const bool FixedSize = false; }; 46 47 // fixed size cases 48 template<> struct MapTokenStructToInfoClass<TOKEN_ELEVATION_TYPE> { static const TOKEN_INFORMATION_CLASS infoClass = TokenElevationType; static const bool FixedSize = true; }; 49 template<> struct MapTokenStructToInfoClass<TOKEN_MANDATORY_POLICY> { static const TOKEN_INFORMATION_CLASS infoClass = TokenMandatoryPolicy; static const bool FixedSize = true; }; 50 template<> struct MapTokenStructToInfoClass<TOKEN_ORIGIN> { static const TOKEN_INFORMATION_CLASS infoClass = TokenOrigin; static const bool FixedSize = true; }; 51 template<> struct MapTokenStructToInfoClass<TOKEN_SOURCE> { static const TOKEN_INFORMATION_CLASS infoClass = TokenSource; static const bool FixedSize = true; }; 52 template<> struct MapTokenStructToInfoClass<TOKEN_STATISTICS> { static const TOKEN_INFORMATION_CLASS infoClass = TokenStatistics; static const bool FixedSize = true; }; 53 template<> struct MapTokenStructToInfoClass<TOKEN_TYPE> { static const TOKEN_INFORMATION_CLASS infoClass = TokenType; static const bool FixedSize = true; }; 54 template<> struct MapTokenStructToInfoClass<SECURITY_IMPERSONATION_LEVEL> { static const TOKEN_INFORMATION_CLASS infoClass = TokenImpersonationLevel; static const bool FixedSize = true; }; 55 template<> struct MapTokenStructToInfoClass<TOKEN_ELEVATION> { static const TOKEN_INFORMATION_CLASS infoClass = TokenElevation; static const bool FixedSize = true; }; 56 } 57 /// @endcond 58 59 enum class OpenThreadTokenAs 60 { 61 Current, 62 Self 63 }; 64 65 /** Open the active token. 66 Opens either the current thread token (if impersonating) or the current process token. Returns a token the caller 67 can use with methods like get_token_information<> below. By default, the token is opened for TOKEN_QUERY and as the 68 effective user. 69 70 Consider using GetCurrentThreadEffectiveToken() instead of this method when eventually calling get_token_information. 71 This method returns a real handle to the effective token, but GetCurrentThreadEffectiveToken() is a Pseudo-handle 72 and much easier to manage. 73 ~~~~ 74 wil::unique_handle theToken; 75 RETURN_IF_FAILED(wil::open_current_access_token_nothrow(&theToken)); 76 ~~~~ 77 Callers who want more access to the token (such as to duplicate or modify the token) can pass 78 any mask of the token rights. 79 ~~~~ 80 wil::unique_handle theToken; 81 RETURN_IF_FAILED(wil::open_current_access_token_nothrow(&theToken, TOKEN_QUERY | TOKEN_ADJUST_PRIVILEGES)); 82 ~~~~ 83 Services impersonating their clients may need to request that the active token is opened on the 84 behalf of the service process to perform certain operations. Opening a token for impersonation access 85 or privilege-adjustment are examples of uses. 86 ~~~~ 87 wil::unique_handle callerToken; 88 RETURN_IF_FAILED(wil::open_current_access_token_nothrow(&theToken, TOKEN_QUERY | TOKEN_IMPERSONATE, true)); 89 ~~~~ 90 @param tokenHandle Receives the token opened during the operation. Must be CloseHandle'd by the caller, or 91 (preferably) stored in a wil::unique_handle 92 @param access Bits from the TOKEN_* access mask which are passed to OpenThreadToken/OpenProcessToken 93 @param asSelf When true, and if the thread is impersonating, the thread token is opened using the 94 process token's rights. 95 */ 96 inline HRESULT open_current_access_token_nothrow(_Out_ HANDLE* tokenHandle, unsigned long access = TOKEN_QUERY, OpenThreadTokenAs openAs = OpenThreadTokenAs::Current) 97 { 98 HRESULT hr = (OpenThreadToken(GetCurrentThread(), access, (openAs == OpenThreadTokenAs::Self), tokenHandle) ? S_OK : HRESULT_FROM_WIN32(::GetLastError())); 99 if (hr == HRESULT_FROM_WIN32(ERROR_NO_TOKEN)) 100 { 101 hr = (OpenProcessToken(GetCurrentProcess(), access, tokenHandle) ? S_OK : HRESULT_FROM_WIN32(::GetLastError())); 102 } 103 return hr; 104 } 105 106 //! Current thread or process token, consider using GetCurrentThreadEffectiveToken() instead. 107 inline wil::unique_handle open_current_access_token_failfast(unsigned long access = TOKEN_QUERY, OpenThreadTokenAs openAs = OpenThreadTokenAs::Current) 108 { 109 HANDLE rawTokenHandle; 110 FAIL_FAST_IF_FAILED(open_current_access_token_nothrow(&rawTokenHandle, access, openAs)); 111 return wil::unique_handle(rawTokenHandle); 112 } 113 114 // Exception based function to open current thread/process access token and acquire pointer to it 115 #ifdef WIL_ENABLE_EXCEPTIONS 116 //! Current thread or process token, consider using GetCurrentThreadEffectiveToken() instead. 117 inline wil::unique_handle open_current_access_token(unsigned long access = TOKEN_QUERY, OpenThreadTokenAs openAs = OpenThreadTokenAs::Current) 118 { 119 HANDLE rawTokenHandle; 120 THROW_IF_FAILED(open_current_access_token_nothrow(&rawTokenHandle, access, openAs)); 121 return wil::unique_handle(rawTokenHandle); 122 } 123 #endif // WIL_ENABLE_EXCEPTIONS 124 125 // Returns tokenHandle or the effective thread token if tokenHandle is null. 126 // Note, this returns an token handle who's lifetime is managed independently 127 // and it may be a pseudo token, don't free it! 128 inline HANDLE GetCurrentThreadEffectiveTokenWithOverride(HANDLE tokenHandle) 129 { 130 return tokenHandle ? tokenHandle : GetCurrentThreadEffectiveToken(); 131 } 132 133 /** Fetches information about a token. 134 See GetTokenInformation on MSDN for what this method can return. For variable sized structs the information 135 is returned to the caller as a wistd::unique_ptr<T> (like TOKEN_ORIGIN, TOKEN_USER, TOKEN_ELEVATION, etc.). For 136 fixed sized, the struct is returned directly. 137 The caller must have access to read the information from the provided token. This method works with both real 138 (e.g. OpenCurrentAccessToken) and pseudo (e.g. GetCurrentThreadToken) token handles. 139 ~~~~ 140 // Retrieve the TOKEN_USER structure for the current process 141 wistd::unique_ptr<TOKEN_USER> user; 142 RETURN_IF_FAILED(wil::get_token_information_nothrow(user, GetCurrentProcessToken())); 143 RETURN_IF_FAILED(ConsumeSid(user->User.Sid)); 144 ~~~~ 145 Not specifying the token handle is the same as specifying 'nullptr' and retrieves information about the effective token. 146 ~~~~ 147 wistd::unique_ptr<TOKEN_PRIVILEGES> privileges; 148 RETURN_IF_FAILED(wil::get_token_information_nothrow(privileges)); 149 for (auto const& privilege : wil::GetRange(privileges->Privileges, privileges->PrivilegeCount)) 150 { 151 RETURN_IF_FAILED(ConsumePrivilege(privilege)); 152 } 153 ~~~~ 154 @param tokenInfo Receives a pointer to a structure containing the results of GetTokenInformation for the requested 155 type. The type of <T> selects which TOKEN_INFORMATION_CLASS will be used. 156 @param tokenHandle Specifies which token will be queried. When nullptr, the thread's effective current token is used. 157 @return S_OK on success, a FAILED hresult containing the win32 error from querying the token otherwise. 158 */ 159 160 template <typename T, wistd::enable_if_t<!details::MapTokenStructToInfoClass<T>::FixedSize>* = nullptr> 161 inline HRESULT get_token_information_nothrow(wistd::unique_ptr<T>& tokenInfo, HANDLE tokenHandle = nullptr) 162 { 163 tokenInfo.reset(); 164 tokenHandle = GetCurrentThreadEffectiveTokenWithOverride(tokenHandle); 165 166 DWORD tokenInfoSize = 0; 167 const auto infoClass = details::MapTokenStructToInfoClass<T>::infoClass; 168 RETURN_LAST_ERROR_IF(!((!GetTokenInformation(tokenHandle, infoClass, nullptr, 0, &tokenInfoSize)) && 169 (::GetLastError() == ERROR_INSUFFICIENT_BUFFER))); 170 wistd::unique_ptr<char> tokenInfoClose( 171 static_cast<char*>(operator new(tokenInfoSize, std::nothrow))); 172 RETURN_IF_NULL_ALLOC(tokenInfoClose.get()); 173 RETURN_IF_WIN32_BOOL_FALSE(GetTokenInformation(tokenHandle, infoClass, tokenInfoClose.get(), tokenInfoSize, &tokenInfoSize)); 174 tokenInfo.reset(reinterpret_cast<T *>(tokenInfoClose.release())); 175 176 return S_OK; 177 } 178 179 template <typename T, wistd::enable_if_t<details::MapTokenStructToInfoClass<T>::FixedSize>* = nullptr> 180 inline HRESULT get_token_information_nothrow(_Out_ T* tokenInfo, HANDLE tokenHandle = nullptr) 181 { 182 *tokenInfo = {}; 183 tokenHandle = GetCurrentThreadEffectiveTokenWithOverride(tokenHandle); 184 185 DWORD tokenInfoSize = sizeof(T); 186 const auto infoClass = details::MapTokenStructToInfoClass<T>::infoClass; 187 RETURN_IF_WIN32_BOOL_FALSE(GetTokenInformation(tokenHandle, infoClass, tokenInfo, tokenInfoSize, &tokenInfoSize)); 188 189 return S_OK; 190 } 191 192 namespace details 193 { 194 template<typename T, typename policy, wistd::enable_if_t<!details::MapTokenStructToInfoClass<T>::FixedSize>* = nullptr> 195 wistd::unique_ptr<T> GetTokenInfoWrap(HANDLE token = nullptr) 196 { 197 wistd::unique_ptr<T> temp; 198 policy::HResult(get_token_information_nothrow(temp, token)); 199 return temp; 200 } 201 202 template<typename T, typename policy, wistd::enable_if_t<details::MapTokenStructToInfoClass<T>::FixedSize>* = nullptr> 203 T GetTokenInfoWrap(HANDLE token = nullptr) 204 { 205 T temp{}; 206 policy::HResult(get_token_information_nothrow(&temp, token)); 207 return temp; 208 } 209 } 210 211 //! A variant of get_token_information<T> that fails-fast on errors retrieving the token 212 template <typename T> 213 inline auto get_token_information_failfast(HANDLE token = nullptr) 214 { 215 return details::GetTokenInfoWrap<T, err_failfast_policy>(token); 216 } 217 218 //! Overload of GetTokenInformationNoThrow that retrieves a token linked from the provided token 219 inline HRESULT get_token_information_nothrow(unique_token_linked_token& tokenInfo, HANDLE tokenHandle = nullptr) 220 { 221 static_assert(sizeof(tokenInfo) == sizeof(TOKEN_LINKED_TOKEN), "confusing size mismatch"); 222 tokenHandle = GetCurrentThreadEffectiveTokenWithOverride(tokenHandle); 223 224 DWORD tokenInfoSize = 0; 225 RETURN_IF_WIN32_BOOL_FALSE(::GetTokenInformation(tokenHandle, TokenLinkedToken, 226 tokenInfo.reset_and_addressof(), sizeof(tokenInfo), &tokenInfoSize)); 227 return S_OK; 228 } 229 230 /** Retrieves the linked-token information for a token. 231 Fails-fast if the link information cannot be retrieved. 232 ~~~~ 233 auto link = get_linked_token_information_failfast(GetCurrentThreadToken()); 234 auto tokenUser = get_token_information<TOKEN_USER>(link.LinkedToken); 235 ~~~~ 236 @param token Specifies the token to query. Pass nullptr to use the current effective thread token 237 @return unique_token_linked_token containing a handle to the linked token 238 */ 239 inline unique_token_linked_token get_linked_token_information_failfast(HANDLE token = nullptr) 240 { 241 unique_token_linked_token tokenInfo; 242 FAIL_FAST_IF_FAILED(get_token_information_nothrow(tokenInfo, token)); 243 return tokenInfo; 244 } 245 246 #ifdef WIL_ENABLE_EXCEPTIONS 247 /** Fetches information about a token. 248 See get_token_information_nothrow for full details. 249 ~~~~ 250 auto user = wil::get_token_information<TOKEN_USER>(GetCurrentProcessToken()); 251 ConsumeSid(user->User.Sid); 252 ~~~~ 253 Pass 'nullptr' (or omit the parameter) as tokenHandle to retrieve information about the effective token. 254 ~~~~ 255 auto privs = wil::get_token_information<TOKEN_PRIVILEGES>(privileges); 256 for (auto& priv : wil::make_range(privs->Privileges, privs->Privilieges + privs->PrivilegeCount)) 257 { 258 if (priv.Attributes & SE_PRIVILEGE_ENABLED) 259 { 260 // ... 261 } 262 } 263 ~~~~ 264 @return A pointer to a structure containing the results of GetTokenInformation for the requested type. The type of 265 <T> selects which TOKEN_INFORMATION_CLASS will be used. 266 @param token Specifies which token will be queried. When nullptr or not set, the thread's effective current token is used. 267 */ 268 template <typename T> 269 inline auto get_token_information(HANDLE token = nullptr) 270 { 271 return details::GetTokenInfoWrap<T, err_exception_policy>(token); 272 } 273 274 /** Retrieves the linked-token information for a token. 275 Throws an exception if the link information cannot be retrieved. 276 ~~~~ 277 auto link = get_linked_token_information(GetCurrentThreadToken()); 278 auto tokenUser = get_token_information<TOKEN_USER>(link.LinkedToken); 279 ~~~~ 280 @param token Specifies the token to query. Pass nullptr to use the current effective thread token 281 @return unique_token_linked_token containing a handle to the linked token 282 */ 283 inline unique_token_linked_token get_linked_token_information(HANDLE token = nullptr) 284 { 285 unique_token_linked_token tokenInfo; 286 THROW_IF_FAILED(get_token_information_nothrow(tokenInfo, token)); 287 return tokenInfo; 288 } 289 #endif 290 291 /// @cond 292 namespace details 293 { 294 inline void RevertImpersonateToken(_Pre_opt_valid_ _Frees_ptr_opt_ HANDLE oldToken) 295 { 296 FAIL_FAST_IMMEDIATE_IF(!::SetThreadToken(nullptr, oldToken)); 297 298 if (oldToken) 299 { 300 ::CloseHandle(oldToken); 301 } 302 } 303 } 304 /// @endcond 305 306 using unique_token_reverter = wil::unique_any< 307 HANDLE, 308 decltype(&details::RevertImpersonateToken), 309 details::RevertImpersonateToken, 310 details::pointer_access_none, 311 HANDLE, 312 INT_PTR, 313 -1, 314 HANDLE>; 315 316 /** Temporarily impersonates a token on this thread. 317 This method sets a new token on a thread, restoring the current token when the returned object 318 is destroyed. Useful for impersonating other tokens or running as 'self,' especially in services. 319 ~~~~ 320 HRESULT OpenFileAsSessionuser(PCWSTR filePath, DWORD session, _Out_ HANDLE* opened) 321 { 322 wil::unique_handle userToken; 323 RETURN_IF_WIN32_BOOL_FALSE(QueryUserToken(session, &userToken)); 324 325 wil::unique_token_reverter reverter; 326 RETURN_IF_FAILED(wil::impersonate_token_nothrow(userToken.get(), reverter)); 327 328 wil::unique_hfile userFile(::CreateFile(filePath, ...)); 329 RETURN_LAST_ERROR_IF(!userFile && (::GetLastError() != ERROR_FILE_NOT_FOUND)); 330 331 *opened = userFile.release(); 332 return S_OK; 333 } 334 ~~~~ 335 @param token A token to impersonate, or 'nullptr' to run as the process identity. 336 */ 337 inline HRESULT impersonate_token_nothrow(HANDLE token, unique_token_reverter& reverter) 338 { 339 wil::unique_handle currentToken; 340 341 // Get the token for the current thread. If there wasn't one, the reset will clear it as well 342 if (!OpenThreadToken(GetCurrentThread(), TOKEN_ALL_ACCESS, TRUE, ¤tToken)) 343 { 344 RETURN_LAST_ERROR_IF(::GetLastError() != ERROR_NO_TOKEN); 345 } 346 347 // Update the current token 348 RETURN_IF_WIN32_BOOL_FALSE(::SetThreadToken(nullptr, token)); 349 350 reverter.reset(currentToken.release()); // Ownership passed 351 return S_OK; 352 } 353 354 /** Temporarily clears any impersonation on this thread. 355 This method resets the current thread's token to nullptr, indicating that it is not impersonating 356 any user. Useful for elevating to whatever identity a service or higher-privilege process might 357 be capable of running under. 358 ~~~~ 359 HRESULT DeleteFileRetryAsSelf(PCWSTR filePath) 360 { 361 if (!::DeleteFile(filePath)) 362 { 363 RETURN_LAST_ERROR_IF(::GetLastError() != ERROR_ACCESS_DENIED); 364 wil::unique_token_reverter reverter; 365 RETURN_IF_FAILED(wil::run_as_self_nothrow(reverter)); 366 RETURN_IF_FAILED(TakeOwnershipOfFile(filePath)); 367 RETURN_IF_FAILED(GrantDeleteAccess(filePath)); 368 RETURN_IF_WIN32_BOOL_FALSE(::DeleteFile(filePath)); 369 } 370 return S_OK; 371 } 372 ~~~~ 373 */ 374 inline HRESULT run_as_self_nothrow(unique_token_reverter& reverter) 375 { 376 return impersonate_token_nothrow(nullptr, reverter); 377 } 378 379 inline unique_token_reverter impersonate_token_failfast(HANDLE token) 380 { 381 unique_token_reverter oldToken; 382 FAIL_FAST_IF_FAILED(impersonate_token_nothrow(token, oldToken)); 383 return oldToken; 384 } 385 386 inline unique_token_reverter run_as_self_failfast() 387 { 388 return impersonate_token_failfast(nullptr); 389 } 390 391 #ifdef WIL_ENABLE_EXCEPTIONS 392 /** Temporarily impersonates a token on this thread. 393 This method sets a new token on a thread, restoring the current token when the returned object 394 is destroyed. Useful for impersonating other tokens or running as 'self,' especially in services. 395 ~~~~ 396 wil::unique_hfile OpenFileAsSessionuser(_In_z_ const wchar_t* filePath, DWORD session) 397 { 398 wil::unique_handle userToken; 399 THROW_IF_WIN32_BOOL_FALSE(QueryUserToken(session, &userToken)); 400 401 auto priorToken = wil::impersonate_token(userToken.get()); 402 403 wil::unique_hfile userFile(::CreateFile(filePath, ...)); 404 THROW_LAST_ERROR_IF(::GetLastError() != ERROR_FILE_NOT_FOUND); 405 406 return userFile; 407 } 408 ~~~~ 409 @param token A token to impersonate, or 'nullptr' to run as the process identity. 410 */ 411 inline unique_token_reverter impersonate_token(HANDLE token = nullptr) 412 { 413 unique_token_reverter oldToken; 414 THROW_IF_FAILED(impersonate_token_nothrow(token, oldToken)); 415 return oldToken; 416 } 417 418 /** Temporarily clears any impersonation on this thread. 419 This method resets the current thread's token to nullptr, indicating that it is not impersonating 420 any user. Useful for elevating to whatever identity a service or higher-privilege process might 421 be capable of running under. 422 ~~~~ 423 void DeleteFileRetryAsSelf(_In_z_ const wchar_t* filePath) 424 { 425 if (!::DeleteFile(filePath) && (::GetLastError() == ERROR_ACCESS_DENIED)) 426 { 427 auto priorToken = wil::run_as_self(); 428 TakeOwnershipOfFile(filePath); 429 GrantDeleteAccess(filePath); 430 ::DeleteFile(filePath); 431 } 432 } 433 ~~~~ 434 */ 435 inline unique_token_reverter run_as_self() 436 { 437 return impersonate_token(nullptr); 438 } 439 #endif // WIL_ENABLE_EXCEPTIONS 440 441 namespace details 442 { 443 template<size_t AuthorityCount> struct static_sid_t 444 { 445 BYTE Revision; 446 BYTE SubAuthorityCount; 447 SID_IDENTIFIER_AUTHORITY IdentifierAuthority; 448 DWORD SubAuthority[AuthorityCount]; 449 450 PSID get() 451 { 452 return reinterpret_cast<PSID>(this); 453 } 454 455 template<size_t other> static_sid_t& operator=(const static_sid_t<other>& source) 456 { 457 static_assert(other <= AuthorityCount, "Cannot assign from a larger static sid to a smaller one"); 458 459 if (&this->Revision != &source.Revision) 460 { 461 memcpy(this, &source, sizeof(source)); 462 } 463 464 return *this; 465 } 466 }; 467 } 468 469 /** Returns a structure containing a Revision 1 SID initialized with the authorities provided 470 Replaces AllocateAndInitializeSid by constructing a structure laid out like a PSID, but 471 returned like a value. The resulting object is suitable for use with any method taking PSID, 472 passed by "&the_sid" or via "the_sid.get()" 473 ~~~~ 474 // Change the owner of the key to administrators 475 auto systemSid = wil::make_static_sid(SECURITY_NT_AUTHORITY, SECURITY_BUILTIN_DOMAIN_RID, DOMAIN_ALIAS_RID_ADMINS); 476 RETURN_IF_WIN32_ERROR(SetNamedSecurityInfo(keyPath, SE_REGISTRY_KEY, OWNER_SECURITY_INFORMATION, &systemSid, nullptr, nullptr, nullptr)); 477 ~~~~ 478 */ 479 template<typename... Ts> constexpr auto make_static_sid(const SID_IDENTIFIER_AUTHORITY& authority, Ts&&... subAuthorities) 480 { 481 using sid_t = details::static_sid_t<sizeof...(subAuthorities)>; 482 483 static_assert(sizeof...(subAuthorities) <= SID_MAX_SUB_AUTHORITIES, "too many sub authorities"); 484 static_assert(offsetof(sid_t, Revision) == offsetof(_SID, Revision), "layout mismatch"); 485 static_assert(offsetof(sid_t, SubAuthorityCount) == offsetof(_SID, SubAuthorityCount), "layout mismatch"); 486 static_assert(offsetof(sid_t, IdentifierAuthority) == offsetof(_SID, IdentifierAuthority), "layout mismatch"); 487 static_assert(offsetof(sid_t, SubAuthority) == offsetof(_SID, SubAuthority), "layout mismatch"); 488 489 return sid_t { SID_REVISION, sizeof...(subAuthorities), authority, { static_cast<DWORD>(subAuthorities)... } }; 490 } 491 492 //! Variant of static_sid that defaults to the NT authority 493 template<typename... Ts> constexpr auto make_static_nt_sid(Ts&& ... subAuthorities) 494 { 495 return make_static_sid(SECURITY_NT_AUTHORITY, wistd::forward<Ts>(subAuthorities)...); 496 } 497 498 /** Determines whether a specified security identifier (SID) is enabled in an access token. 499 This function determines whether a security identifier, described by a given set of subauthorities, is enabled 500 in the given access token. Note that only up to eight subauthorities can be passed to this function. 501 ~~~~ 502 bool IsGuest() 503 { 504 return wil::test_token_membership(nullptr, SECURITY_NT_AUTHORITY, SECURITY_BUILTIN_DOMAIN_RID, DOMAIN_ALIAS_RID_GUESTS)); 505 } 506 ~~~~ 507 @param result This will be set to true if and only if a security identifier described by the given set of subauthorities is enabled in the given access token. 508 @param token A handle to an access token. The handle must have TOKEN_QUERY access to the token, and must be an impersonation token. If token is nullptr, test_token_membership 509 uses the impersonation token of the calling thread. If the thread is not impersonating, the function duplicates the thread's primary token to create an impersonation token. 510 @param sidAuthority A reference to a SID_IDENTIFIER_AUTHORITY structure. This structure provides the top-level identifier authority value to set in the SID. 511 @param subAuthorities Up to 15 subauthority values to place in the SID (this is a systemwide limit) 512 @return S_OK on success, a FAILED hresult containing the win32 error from creating the SID or querying the token otherwise. 513 */ 514 template<typename... Ts> HRESULT test_token_membership_nothrow(_Out_ bool* result, _In_opt_ HANDLE token, 515 const SID_IDENTIFIER_AUTHORITY& sidAuthority, Ts&&... subAuthorities) 516 { 517 *result = false; 518 auto tempSid = make_static_sid(sidAuthority, wistd::forward<Ts>(subAuthorities)...); 519 BOOL isMember; 520 RETURN_IF_WIN32_BOOL_FALSE(CheckTokenMembership(token, &tempSid, &isMember)); 521 522 *result = (isMember != FALSE); 523 524 return S_OK; 525 } 526 527 /** Determine whether a token represents an app container 528 This method uses the passed in token and emits a boolean indicating that 529 whether TokenIsAppContainer is true. 530 ~~~~ 531 HRESULT OnlyIfAppContainer() 532 { 533 bool isAppContainer; 534 RETURN_IF_FAILED(wil::get_token_is_app_container_nothrow(nullptr, isAppContainer)); 535 RETURN_HR_IF(E_ACCESSDENIED, !isAppContainer); 536 RETURN_HR(...); 537 } 538 ~~~~ 539 @param token A token to get info about, or 'nullptr' to run as the current thread. 540 */ 541 inline HRESULT get_token_is_app_container_nothrow(_In_opt_ HANDLE token, bool& value) 542 { 543 DWORD isAppContainer = 0; 544 DWORD returnLength = 0; 545 RETURN_IF_WIN32_BOOL_FALSE(::GetTokenInformation( 546 token ? token : GetCurrentThreadEffectiveToken(), 547 TokenIsAppContainer, 548 &isAppContainer, 549 sizeof(isAppContainer), 550 &returnLength)); 551 552 value = (isAppContainer != 0); 553 554 return S_OK; 555 } 556 557 //! A variant of get_token_is_app_container_nothrow that fails-fast on errors retrieving the token information 558 inline bool get_token_is_app_container_failfast(HANDLE token = nullptr) 559 { 560 bool value = false; 561 FAIL_FAST_IF_FAILED(get_token_is_app_container_nothrow(token, value)); 562 563 return value; 564 } 565 566 #ifdef WIL_ENABLE_EXCEPTIONS 567 //! A variant of get_token_is_app_container_nothrow that throws on errors retrieving the token information 568 inline bool get_token_is_app_container(HANDLE token = nullptr) 569 { 570 bool value = false; 571 THROW_IF_FAILED(get_token_is_app_container_nothrow(token, value)); 572 573 return value; 574 } 575 #endif // WIL_ENABLE_EXCEPTIONS 576 577 template<typename... Ts> bool test_token_membership_failfast(_In_opt_ HANDLE token, 578 const SID_IDENTIFIER_AUTHORITY& sidAuthority, Ts&&... subAuthorities) 579 { 580 bool result; 581 FAIL_FAST_IF_FAILED(test_token_membership_nothrow(&result, token, sidAuthority, wistd::forward<Ts>(subAuthorities)...)); 582 return result; 583 } 584 585 #ifdef WIL_ENABLE_EXCEPTIONS 586 template<typename... Ts> bool test_token_membership(_In_opt_ HANDLE token, const SID_IDENTIFIER_AUTHORITY& sidAuthority, 587 Ts&&... subAuthorities) 588 { 589 bool result; 590 THROW_IF_FAILED(test_token_membership_nothrow(&result, token, sidAuthority, wistd::forward<Ts>(subAuthorities)...)); 591 return result; 592 } 593 #endif 594 595 } //namespace wil 596 597 #endif // __WIL_TOKEN_HELPERS_INCLUDED 598