restricted_token_utils.cc 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447
  1. // Copyright (c) 2012 The Chromium Authors. All rights reserved.
  2. // Use of this source code is governed by a BSD-style license that can be
  3. // found in the LICENSE file.
  4. #include "sandbox/win/src/restricted_token_utils.h"
  5. #include <aclapi.h>
  6. #include <sddl.h>
  7. #include <memory>
  8. #include <vector>
  9. #include "base/check.h"
  10. #include "base/notreached.h"
  11. #include "base/strings/stringprintf.h"
  12. #include "base/win/scoped_handle.h"
  13. #include "base/win/windows_version.h"
  14. #include "sandbox/win/src/job.h"
  15. #include "sandbox/win/src/restricted_token.h"
  16. #include "sandbox/win/src/sandbox_nt_util.h"
  17. #include "sandbox/win/src/sandbox_utils.h"
  18. #include "sandbox/win/src/security_level.h"
  19. #include "sandbox/win/src/win_utils.h"
  20. namespace sandbox {
  21. namespace {
  22. DWORD GetObjectSecurityDescriptor(HANDLE handle,
  23. SECURITY_INFORMATION security_info,
  24. std::vector<char>* security_desc_buffer,
  25. PSECURITY_DESCRIPTOR* security_desc) {
  26. DWORD last_error = 0;
  27. DWORD length_needed = 0;
  28. ::GetKernelObjectSecurity(handle, security_info, nullptr, 0, &length_needed);
  29. last_error = ::GetLastError();
  30. if (last_error != ERROR_INSUFFICIENT_BUFFER)
  31. return last_error;
  32. security_desc_buffer->resize(length_needed);
  33. *security_desc =
  34. reinterpret_cast<PSECURITY_DESCRIPTOR>(security_desc_buffer->data());
  35. if (!::GetKernelObjectSecurity(handle, security_info, *security_desc,
  36. length_needed, &length_needed)) {
  37. return ::GetLastError();
  38. }
  39. return ERROR_SUCCESS;
  40. }
  41. void AddSidException(std::vector<base::win::Sid>& sids,
  42. base::win::WellKnownSid known_sid) {
  43. absl::optional<base::win::Sid> sid = base::win::Sid::FromKnownSid(known_sid);
  44. DCHECK(sid);
  45. sids.push_back(std::move(*sid));
  46. }
  47. typedef BOOL(WINAPI* CreateAppContainerTokenFunction)(
  48. HANDLE TokenHandle,
  49. PSECURITY_CAPABILITIES SecurityCapabilities,
  50. PHANDLE OutToken);
  51. } // namespace
  52. DWORD CreateRestrictedToken(
  53. HANDLE effective_token,
  54. TokenLevel security_level,
  55. IntegrityLevel integrity_level,
  56. TokenType token_type,
  57. bool lockdown_default_dacl,
  58. const absl::optional<base::win::Sid>& unique_restricted_sid,
  59. base::win::ScopedHandle* token) {
  60. RestrictedToken restricted_token;
  61. restricted_token.Init(effective_token);
  62. if (lockdown_default_dacl)
  63. restricted_token.SetLockdownDefaultDacl();
  64. if (unique_restricted_sid) {
  65. restricted_token.AddDefaultDaclSid(*unique_restricted_sid,
  66. SecurityAccessMode::kGrant, GENERIC_ALL);
  67. restricted_token.AddDefaultDaclSid(
  68. base::win::WellKnownSid::kCreatorOwnerRights,
  69. SecurityAccessMode::kGrant, READ_CONTROL);
  70. }
  71. std::vector<std::wstring> privilege_exceptions;
  72. std::vector<base::win::Sid> sid_exceptions;
  73. bool deny_sids = true;
  74. bool remove_privileges = true;
  75. bool remove_traverse_privilege = false;
  76. switch (security_level) {
  77. case USER_UNPROTECTED: {
  78. deny_sids = false;
  79. remove_privileges = false;
  80. break;
  81. }
  82. case USER_RESTRICTED_SAME_ACCESS: {
  83. deny_sids = false;
  84. remove_privileges = false;
  85. unsigned err_code = restricted_token.AddRestrictingSidAllSids();
  86. if (ERROR_SUCCESS != err_code)
  87. return err_code;
  88. break;
  89. }
  90. case USER_RESTRICTED_NON_ADMIN: {
  91. AddSidException(sid_exceptions, base::win::WellKnownSid::kBuiltinUsers);
  92. AddSidException(sid_exceptions, base::win::WellKnownSid::kWorld);
  93. AddSidException(sid_exceptions, base::win::WellKnownSid::kInteractive);
  94. AddSidException(sid_exceptions,
  95. base::win::WellKnownSid::kAuthenticatedUser);
  96. restricted_token.AddRestrictingSid(
  97. base::win::WellKnownSid::kBuiltinUsers);
  98. restricted_token.AddRestrictingSid(base::win::WellKnownSid::kWorld);
  99. restricted_token.AddRestrictingSid(base::win::WellKnownSid::kInteractive);
  100. restricted_token.AddRestrictingSid(
  101. base::win::WellKnownSid::kAuthenticatedUser);
  102. restricted_token.AddRestrictingSid(base::win::WellKnownSid::kRestricted);
  103. restricted_token.AddRestrictingSidCurrentUser();
  104. restricted_token.AddRestrictingSidLogonSession();
  105. if (unique_restricted_sid)
  106. restricted_token.AddRestrictingSid(*unique_restricted_sid);
  107. break;
  108. }
  109. case USER_INTERACTIVE: {
  110. AddSidException(sid_exceptions, base::win::WellKnownSid::kBuiltinUsers);
  111. AddSidException(sid_exceptions, base::win::WellKnownSid::kWorld);
  112. AddSidException(sid_exceptions, base::win::WellKnownSid::kInteractive);
  113. AddSidException(sid_exceptions,
  114. base::win::WellKnownSid::kAuthenticatedUser);
  115. restricted_token.AddRestrictingSid(
  116. base::win::WellKnownSid::kBuiltinUsers);
  117. restricted_token.AddRestrictingSid(base::win::WellKnownSid::kWorld);
  118. restricted_token.AddRestrictingSid(base::win::WellKnownSid::kRestricted);
  119. restricted_token.AddRestrictingSidCurrentUser();
  120. restricted_token.AddRestrictingSidLogonSession();
  121. if (unique_restricted_sid)
  122. restricted_token.AddRestrictingSid(*unique_restricted_sid);
  123. break;
  124. }
  125. case USER_LIMITED: {
  126. AddSidException(sid_exceptions, base::win::WellKnownSid::kBuiltinUsers);
  127. AddSidException(sid_exceptions, base::win::WellKnownSid::kWorld);
  128. AddSidException(sid_exceptions, base::win::WellKnownSid::kInteractive);
  129. restricted_token.AddRestrictingSid(
  130. base::win::WellKnownSid::kBuiltinUsers);
  131. restricted_token.AddRestrictingSid(base::win::WellKnownSid::kWorld);
  132. restricted_token.AddRestrictingSid(base::win::WellKnownSid::kRestricted);
  133. if (unique_restricted_sid)
  134. restricted_token.AddRestrictingSid(*unique_restricted_sid);
  135. // This token has to be able to create objects in BNO, it needs the
  136. // current logon sid in the token to achieve this. You should also set the
  137. // process to be low integrity level so it can't access object created by
  138. // other processes.
  139. restricted_token.AddRestrictingSidLogonSession();
  140. break;
  141. }
  142. case USER_RESTRICTED: {
  143. restricted_token.AddUserSidForDenyOnly();
  144. restricted_token.AddRestrictingSid(base::win::WellKnownSid::kRestricted);
  145. if (unique_restricted_sid)
  146. restricted_token.AddRestrictingSid(*unique_restricted_sid);
  147. break;
  148. }
  149. case USER_LOCKDOWN: {
  150. remove_traverse_privilege = true;
  151. restricted_token.AddUserSidForDenyOnly();
  152. restricted_token.AddRestrictingSid(base::win::WellKnownSid::kNull);
  153. if (unique_restricted_sid)
  154. restricted_token.AddRestrictingSid(*unique_restricted_sid);
  155. break;
  156. }
  157. case USER_LAST:
  158. return ERROR_BAD_ARGUMENTS;
  159. }
  160. DWORD err_code = ERROR_SUCCESS;
  161. if (deny_sids) {
  162. err_code = restricted_token.AddAllSidsForDenyOnly(sid_exceptions);
  163. if (ERROR_SUCCESS != err_code)
  164. return err_code;
  165. }
  166. if (remove_privileges) {
  167. err_code = restricted_token.DeleteAllPrivileges(remove_traverse_privilege);
  168. if (ERROR_SUCCESS != err_code)
  169. return err_code;
  170. }
  171. restricted_token.SetIntegrityLevel(integrity_level);
  172. switch (token_type) {
  173. case PRIMARY: {
  174. err_code = restricted_token.GetRestrictedToken(token);
  175. break;
  176. }
  177. case IMPERSONATION: {
  178. err_code = restricted_token.GetRestrictedTokenForImpersonation(token);
  179. break;
  180. }
  181. default: {
  182. err_code = ERROR_BAD_ARGUMENTS;
  183. break;
  184. }
  185. }
  186. return err_code;
  187. }
  188. DWORD SetTokenIntegrityLevel(HANDLE token, IntegrityLevel integrity_level) {
  189. absl::optional<base::win::Sid> sid = GetIntegrityLevelSid(integrity_level);
  190. if (!sid) {
  191. // No mandatory level specified, we don't change it.
  192. return ERROR_SUCCESS;
  193. }
  194. TOKEN_MANDATORY_LABEL label = {};
  195. label.Label.Attributes = SE_GROUP_INTEGRITY;
  196. label.Label.Sid = sid->GetPSID();
  197. if (!::SetTokenInformation(token, TokenIntegrityLevel, &label, sizeof(label)))
  198. return ::GetLastError();
  199. return ERROR_SUCCESS;
  200. }
  201. DWORD SetProcessIntegrityLevel(IntegrityLevel integrity_level) {
  202. // We don't check for an invalid level here because we'll just let it
  203. // fail on the SetTokenIntegrityLevel call later on.
  204. if (integrity_level == INTEGRITY_LEVEL_LAST) {
  205. // No mandatory level specified, we don't change it.
  206. return ERROR_SUCCESS;
  207. }
  208. HANDLE token_handle;
  209. if (!::OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_DEFAULT,
  210. &token_handle))
  211. return ::GetLastError();
  212. base::win::ScopedHandle token(token_handle);
  213. return SetTokenIntegrityLevel(token.Get(), integrity_level);
  214. }
  215. DWORD HardenTokenIntegrityLevelPolicy(HANDLE token) {
  216. std::vector<char> security_desc_buffer;
  217. PSECURITY_DESCRIPTOR security_desc = nullptr;
  218. DWORD last_error = GetObjectSecurityDescriptor(
  219. token, LABEL_SECURITY_INFORMATION, &security_desc_buffer, &security_desc);
  220. if (last_error != ERROR_SUCCESS)
  221. return last_error;
  222. PACL sacl = nullptr;
  223. BOOL sacl_present = false;
  224. BOOL sacl_defaulted = false;
  225. if (!::GetSecurityDescriptorSacl(security_desc, &sacl_present, &sacl,
  226. &sacl_defaulted)) {
  227. return ::GetLastError();
  228. }
  229. for (DWORD ace_index = 0; ace_index < sacl->AceCount; ++ace_index) {
  230. PSYSTEM_MANDATORY_LABEL_ACE ace;
  231. if (::GetAce(sacl, ace_index, reinterpret_cast<LPVOID*>(&ace)) &&
  232. ace->Header.AceType == SYSTEM_MANDATORY_LABEL_ACE_TYPE) {
  233. ace->Mask |= SYSTEM_MANDATORY_LABEL_NO_READ_UP |
  234. SYSTEM_MANDATORY_LABEL_NO_EXECUTE_UP;
  235. break;
  236. }
  237. }
  238. if (!::SetKernelObjectSecurity(token, LABEL_SECURITY_INFORMATION,
  239. security_desc))
  240. return ::GetLastError();
  241. return ERROR_SUCCESS;
  242. }
  243. DWORD HardenProcessIntegrityLevelPolicy() {
  244. HANDLE token_handle;
  245. if (!::OpenProcessToken(GetCurrentProcess(), READ_CONTROL | WRITE_OWNER,
  246. &token_handle))
  247. return ::GetLastError();
  248. base::win::ScopedHandle token(token_handle);
  249. return HardenTokenIntegrityLevelPolicy(token.Get());
  250. }
  251. DWORD CreateLowBoxToken(HANDLE base_token,
  252. TokenType token_type,
  253. SECURITY_CAPABILITIES* security_capabilities,
  254. base::win::ScopedHandle* token) {
  255. if (base::win::GetVersion() < base::win::Version::WIN8)
  256. return ERROR_CALL_NOT_IMPLEMENTED;
  257. if (token_type != PRIMARY && token_type != IMPERSONATION)
  258. return ERROR_INVALID_PARAMETER;
  259. if (!token)
  260. return ERROR_INVALID_PARAMETER;
  261. CreateAppContainerTokenFunction CreateAppContainerToken =
  262. reinterpret_cast<CreateAppContainerTokenFunction>(::GetProcAddress(
  263. ::GetModuleHandle(L"kernelbase.dll"), "CreateAppContainerToken"));
  264. if (!CreateAppContainerToken)
  265. return ::GetLastError();
  266. base::win::ScopedHandle base_token_handle;
  267. if (!base_token) {
  268. HANDLE process_token = nullptr;
  269. if (!::OpenProcessToken(::GetCurrentProcess(), TOKEN_ALL_ACCESS,
  270. &process_token)) {
  271. return ::GetLastError();
  272. }
  273. base_token_handle.Set(process_token);
  274. base_token = process_token;
  275. }
  276. HANDLE token_lowbox = nullptr;
  277. if (!CreateAppContainerToken(base_token, security_capabilities,
  278. &token_lowbox)) {
  279. return ::GetLastError();
  280. }
  281. base::win::ScopedHandle token_lowbox_handle(token_lowbox);
  282. DCHECK(token_lowbox_handle.IsValid());
  283. // Default from CreateAppContainerToken is a Primary token.
  284. if (token_type == PRIMARY) {
  285. *token = std::move(token_lowbox_handle);
  286. return ERROR_SUCCESS;
  287. }
  288. HANDLE dup_handle = nullptr;
  289. if (!::DuplicateTokenEx(token_lowbox_handle.Get(), TOKEN_ALL_ACCESS, nullptr,
  290. ::SecurityImpersonation, ::TokenImpersonation,
  291. &dup_handle)) {
  292. return ::GetLastError();
  293. }
  294. // Copy security descriptor from primary token as the new object will have
  295. // the DACL from the current token's default DACL.
  296. base::win::ScopedHandle token_for_sd(dup_handle);
  297. std::vector<char> security_desc_buffer;
  298. PSECURITY_DESCRIPTOR security_desc = nullptr;
  299. DWORD last_error = GetObjectSecurityDescriptor(
  300. token_lowbox_handle.Get(), DACL_SECURITY_INFORMATION,
  301. &security_desc_buffer, &security_desc);
  302. if (last_error != ERROR_SUCCESS)
  303. return last_error;
  304. if (!::SetKernelObjectSecurity(token_for_sd.Get(), DACL_SECURITY_INFORMATION,
  305. security_desc)) {
  306. return ::GetLastError();
  307. }
  308. *token = std::move(token_for_sd);
  309. return ERROR_SUCCESS;
  310. }
  311. bool CanLowIntegrityAccessDesktop() {
  312. // Access required for UI thread to initialize (when user32.dll loads without
  313. // win32k lockdown).
  314. DWORD desired_access = DESKTOP_WRITEOBJECTS | DESKTOP_READOBJECTS;
  315. // From MSDN
  316. // https://docs.microsoft.com/en-us/windows/win32/winstation/desktop-security-and-access-rights
  317. GENERIC_MAPPING generic_mapping{
  318. STANDARD_RIGHTS_READ | DESKTOP_READOBJECTS | DESKTOP_ENUMERATE,
  319. STANDARD_RIGHTS_WRITE | DESKTOP_CREATEWINDOW | DESKTOP_CREATEMENU |
  320. DESKTOP_HOOKCONTROL | DESKTOP_JOURNALRECORD |
  321. DESKTOP_JOURNALPLAYBACK | DESKTOP_WRITEOBJECTS,
  322. STANDARD_RIGHTS_EXECUTE | DESKTOP_SWITCHDESKTOP,
  323. STANDARD_RIGHTS_REQUIRED | DESKTOP_CREATEMENU | DESKTOP_CREATEWINDOW |
  324. DESKTOP_ENUMERATE | DESKTOP_HOOKCONTROL | DESKTOP_JOURNALPLAYBACK |
  325. DESKTOP_JOURNALRECORD | DESKTOP_READOBJECTS | DESKTOP_SWITCHDESKTOP |
  326. DESKTOP_WRITEOBJECTS};
  327. ::MapGenericMask(&desired_access, &generic_mapping);
  328. // Desktop is inherited by child process unless overridden, e.g. by sandbox.
  329. HDESK hdesk = ::GetThreadDesktop(GetCurrentThreadId());
  330. // Get the security descriptor of the desktop.
  331. DWORD size = 0;
  332. SECURITY_INFORMATION security_information =
  333. OWNER_SECURITY_INFORMATION | GROUP_SECURITY_INFORMATION |
  334. DACL_SECURITY_INFORMATION | LABEL_SECURITY_INFORMATION;
  335. ::GetUserObjectSecurity(hdesk, &security_information, nullptr, 0, &size);
  336. std::vector<char> sd_buffer(size);
  337. PSECURITY_DESCRIPTOR sd =
  338. reinterpret_cast<PSECURITY_DESCRIPTOR>(sd_buffer.data());
  339. if (!::GetUserObjectSecurity(hdesk, &security_information, sd, size, &size)) {
  340. return false;
  341. }
  342. // Get a low IL token starting with the current process token and lowering it.
  343. HANDLE temp_process_token = nullptr;
  344. if (!::OpenProcessToken(::GetCurrentProcess(), TOKEN_ALL_ACCESS,
  345. &temp_process_token)) {
  346. return false;
  347. }
  348. base::win::ScopedHandle process_token(temp_process_token);
  349. HANDLE temp_duplicate_token = nullptr;
  350. if (!::DuplicateTokenEx(process_token.Get(), MAXIMUM_ALLOWED, nullptr,
  351. SecurityImpersonation, TokenImpersonation,
  352. &temp_duplicate_token)) {
  353. return false;
  354. }
  355. base::win::ScopedHandle low_il_token(temp_duplicate_token);
  356. // The token should still succeed before lowered, even if the lowered token
  357. // fails.
  358. PRIVILEGE_SET priv_set = {};
  359. DWORD priv_set_length = sizeof(PRIVILEGE_SET);
  360. DWORD granted_access = 0;
  361. BOOL access_status = false;
  362. DCHECK(!!::AccessCheck(sd, low_il_token.Get(), desired_access,
  363. &generic_mapping, &priv_set, &priv_set_length,
  364. &granted_access, &access_status) &&
  365. access_status);
  366. if (sandbox::SetTokenIntegrityLevel(
  367. low_il_token.Get(), sandbox::INTEGRITY_LEVEL_LOW) != ERROR_SUCCESS) {
  368. return false;
  369. }
  370. // Access check the Low-IL token against the desktop - known to fail for third
  371. // party, winlogon, other non-default desktops, and required for user32.dll to
  372. // load.
  373. if (::AccessCheck(sd, low_il_token.Get(), desired_access, &generic_mapping,
  374. &priv_set, &priv_set_length, &granted_access,
  375. &access_status) &&
  376. access_status) {
  377. return true;
  378. }
  379. return false;
  380. }
  381. } // namespace sandbox