You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

598 lines
28KB

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