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