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