window.cc 6.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180
  1. // Copyright (c) 2011 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/window.h"
  5. #include <windows.h>
  6. #include <aclapi.h>
  7. #include <memory>
  8. #include "base/notreached.h"
  9. #include "base/win/sid.h"
  10. #include "base/win/win_util.h"
  11. #include "base/win/windows_version.h"
  12. #include "sandbox/win/src/acl.h"
  13. namespace {
  14. // Gets the security attributes of a window object referenced by |handle|. The
  15. // lpSecurityDescriptor member of the SECURITY_ATTRIBUTES parameter returned
  16. // must be freed using LocalFree by the caller.
  17. bool GetSecurityAttributes(HANDLE handle, SECURITY_ATTRIBUTES* attributes) {
  18. attributes->bInheritHandle = false;
  19. attributes->nLength = sizeof(SECURITY_ATTRIBUTES);
  20. PACL dacl = nullptr;
  21. DWORD result = ::GetSecurityInfo(
  22. handle, SE_WINDOW_OBJECT, DACL_SECURITY_INFORMATION, nullptr, nullptr,
  23. &dacl, nullptr, &attributes->lpSecurityDescriptor);
  24. if (ERROR_SUCCESS == result)
  25. return true;
  26. return false;
  27. }
  28. } // namespace
  29. namespace sandbox {
  30. ResultCode CreateAltWindowStation(HWINSTA* winsta) {
  31. // Get the security attributes from the current window station; we will
  32. // use this as the base security attributes for the new window station.
  33. HWINSTA current_winsta = ::GetProcessWindowStation();
  34. if (!current_winsta)
  35. return SBOX_ERROR_CANNOT_GET_WINSTATION;
  36. SECURITY_ATTRIBUTES attributes = {0};
  37. if (!GetSecurityAttributes(current_winsta, &attributes))
  38. return SBOX_ERROR_CANNOT_QUERY_WINSTATION_SECURITY;
  39. // Create the window station using nullptr for the name to ask the os to
  40. // generate it.
  41. *winsta = ::CreateWindowStationW(
  42. nullptr, 0, GENERIC_READ | WINSTA_CREATEDESKTOP, &attributes);
  43. if (!*winsta && ::GetLastError() == ERROR_ACCESS_DENIED) {
  44. *winsta = ::CreateWindowStationW(
  45. nullptr, 0, WINSTA_READATTRIBUTES | WINSTA_CREATEDESKTOP, &attributes);
  46. }
  47. LocalFree(attributes.lpSecurityDescriptor);
  48. if (*winsta)
  49. return SBOX_ALL_OK;
  50. return SBOX_ERROR_CANNOT_CREATE_WINSTATION;
  51. }
  52. ResultCode CreateAltDesktop(HWINSTA winsta, HDESK* desktop) {
  53. std::wstring desktop_name = L"sbox_alternate_desktop_";
  54. if (!winsta) {
  55. desktop_name += L"local_winstation_";
  56. }
  57. // Append the current PID to the desktop name.
  58. wchar_t buffer[16];
  59. _snwprintf_s(buffer, sizeof(buffer) / sizeof(wchar_t), L"0x%X",
  60. ::GetCurrentProcessId());
  61. desktop_name += buffer;
  62. HDESK current_desktop = GetThreadDesktop(GetCurrentThreadId());
  63. if (!current_desktop)
  64. return SBOX_ERROR_CANNOT_GET_DESKTOP;
  65. // Get the security attributes from the current desktop, we will use this as
  66. // the base security attributes for the new desktop.
  67. SECURITY_ATTRIBUTES attributes = {0};
  68. if (!GetSecurityAttributes(current_desktop, &attributes))
  69. return SBOX_ERROR_CANNOT_QUERY_DESKTOP_SECURITY;
  70. // Detect when the current desktop has a null DACL since it will require
  71. // special casing below.
  72. bool is_null_dacl = false;
  73. BOOL dacl_present = false;
  74. ACL* acl = nullptr;
  75. BOOL dacl_defaulted = false;
  76. if (::GetSecurityDescriptorDacl(attributes.lpSecurityDescriptor,
  77. &dacl_present, &acl, &dacl_defaulted)) {
  78. is_null_dacl = dacl_present && (acl == nullptr);
  79. }
  80. // Back up the current window station, in case we need to switch it.
  81. HWINSTA current_winsta = ::GetProcessWindowStation();
  82. if (winsta) {
  83. // We need to switch to the alternate window station before creating the
  84. // desktop.
  85. if (!::SetProcessWindowStation(winsta)) {
  86. ::LocalFree(attributes.lpSecurityDescriptor);
  87. return SBOX_ERROR_CANNOT_CREATE_DESKTOP;
  88. }
  89. }
  90. // Create the destkop.
  91. *desktop = ::CreateDesktop(desktop_name.c_str(), nullptr, nullptr, 0,
  92. DESKTOP_CREATEWINDOW | DESKTOP_READOBJECTS |
  93. READ_CONTROL | WRITE_DAC | WRITE_OWNER,
  94. &attributes);
  95. ::LocalFree(attributes.lpSecurityDescriptor);
  96. if (winsta) {
  97. // Revert to the right window station.
  98. if (!::SetProcessWindowStation(current_winsta)) {
  99. return SBOX_ERROR_FAILED_TO_SWITCH_BACK_WINSTATION;
  100. }
  101. }
  102. if (*desktop) {
  103. if (is_null_dacl) {
  104. // If the desktop had a NULL DACL, it allowed access to everything. When
  105. // we apply a new ACE with |kDesktopDenyMask| below, a NULL DACL would be
  106. // replaced with a new DACL with one ACE that denies access - which means
  107. // there is no ACE to allow anything access to the desktop. In this case,
  108. // replace the NULL DACL with one that has a single ACE that allows access
  109. // to everyone, so the desktop remains accessible when we further modify
  110. // the DACL. Also need WinBuiltinAnyPackageSid for AppContainer processes.
  111. if (base::win::GetVersion() >= base::win::Version::WIN8) {
  112. AddKnownSidToObject(*desktop, SecurityObjectType::kWindow,
  113. base::win::WellKnownSid::kAllApplicationPackages,
  114. SecurityAccessMode::kGrant, GENERIC_ALL);
  115. }
  116. AddKnownSidToObject(*desktop, SecurityObjectType::kWindow,
  117. base::win::WellKnownSid::kWorld,
  118. SecurityAccessMode::kGrant, GENERIC_ALL);
  119. }
  120. // Replace the DACL on the new Desktop with a reduced privilege version.
  121. // We can soft fail on this for now, as it's just an extra mitigation.
  122. static const ACCESS_MASK kDesktopDenyMask =
  123. WRITE_DAC | WRITE_OWNER | DELETE | DESKTOP_CREATEMENU |
  124. DESKTOP_CREATEWINDOW | DESKTOP_HOOKCONTROL | DESKTOP_JOURNALPLAYBACK |
  125. DESKTOP_JOURNALRECORD | DESKTOP_SWITCHDESKTOP;
  126. AddKnownSidToObject(*desktop, SecurityObjectType::kWindow,
  127. base::win::WellKnownSid::kRestricted,
  128. SecurityAccessMode::kDeny, kDesktopDenyMask);
  129. return SBOX_ALL_OK;
  130. }
  131. return SBOX_ERROR_CANNOT_CREATE_DESKTOP;
  132. }
  133. std::wstring GetFullDesktopName(HWINSTA winsta, HDESK desktop) {
  134. if (!desktop) {
  135. NOTREACHED();
  136. return std::wstring();
  137. }
  138. std::wstring name;
  139. if (winsta) {
  140. name = base::win::GetWindowObjectName(winsta);
  141. name += L'\\';
  142. }
  143. name += base::win::GetWindowObjectName(desktop);
  144. return name;
  145. }
  146. } // namespace sandbox