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