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 55 return SBOX_ERROR_CANNOT_CREATE_WINSTATION; 56 } 57 58 ResultCode CreateAltDesktop(HWINSTA winsta, HDESK* desktop) { 59 base::string16 desktop_name = L"sbox_alternate_desktop_"; 60 61 // Append the current PID to the desktop name. 62 wchar_t buffer[16]; 63 _snwprintf_s(buffer, sizeof(buffer) / sizeof(wchar_t), L"0x%X", 64 ::GetCurrentProcessId()); 65 desktop_name += buffer; 66 67 // Get the security attributes from the current desktop, we will use this as 68 // the base security attributes for the new desktop. 69 SECURITY_ATTRIBUTES attributes = {0}; 70 if (!GetSecurityAttributes(GetThreadDesktop(GetCurrentThreadId()), 71 &attributes)) { 72 return SBOX_ERROR_CANNOT_CREATE_DESKTOP; 73 } 74 75 // Back up the current window station, in case we need to switch it. 76 HWINSTA current_winsta = ::GetProcessWindowStation(); 77 78 if (winsta) { 79 // We need to switch to the alternate window station before creating the 80 // desktop. 81 if (!::SetProcessWindowStation(winsta)) { 82 ::LocalFree(attributes.lpSecurityDescriptor); 83 return SBOX_ERROR_CANNOT_CREATE_DESKTOP; 84 } 85 } 86 87 // Create the destkop. 88 // TODO(nsylvain): don't ask for GENERIC_ALL if we don't need to. 89 *desktop = ::CreateDesktop(desktop_name.c_str(), NULL, NULL, 0, GENERIC_ALL, 90 &attributes); 91 ::LocalFree(attributes.lpSecurityDescriptor); 92 93 if (winsta) { 94 // Revert to the right window station. 95 if (!::SetProcessWindowStation(current_winsta)) { 96 return SBOX_ERROR_FAILED_TO_SWITCH_BACK_WINSTATION; 97 } 98 } 99 100 if (*desktop) { 101 // Replace the DACL on the new Desktop with a reduced privilege version. 102 // We can soft fail on this for now, as it's just an extra mitigation. 103 static const ACCESS_MASK kDesktopDenyMask = WRITE_DAC | WRITE_OWNER | 104 DESKTOP_HOOKCONTROL | 105 DESKTOP_JOURNALPLAYBACK | 106 DESKTOP_JOURNALRECORD | 107 DESKTOP_SWITCHDESKTOP; 108 AddKnownSidToObject(*desktop, SE_WINDOW_OBJECT, Sid(WinRestrictedCodeSid), 109 DENY_ACCESS, kDesktopDenyMask); 110 return SBOX_ALL_OK; 111 } 112 113 return SBOX_ERROR_CANNOT_CREATE_DESKTOP; 114 } 115 116 base::string16 GetWindowObjectName(HANDLE handle) { 117 // Get the size of the name. 118 DWORD size = 0; 119 ::GetUserObjectInformation(handle, UOI_NAME, NULL, 0, &size); 120 121 if (!size) { 122 NOTREACHED(); 123 return base::string16(); 124 } 125 126 // Create the buffer that will hold the name. 127 scoped_ptr<wchar_t[]> name_buffer(new wchar_t[size]); 128 129 // Query the name of the object. 130 if (!::GetUserObjectInformation(handle, UOI_NAME, name_buffer.get(), size, 131 &size)) { 132 NOTREACHED(); 133 return base::string16(); 134 } 135 136 return base::string16(name_buffer.get()); 137 } 138 139 base::string16 GetFullDesktopName(HWINSTA winsta, HDESK desktop) { 140 if (!desktop) { 141 NOTREACHED(); 142 return base::string16(); 143 } 144 145 base::string16 name; 146 if (winsta) { 147 name = GetWindowObjectName(winsta); 148 name += L'\\'; 149 } 150 151 name += GetWindowObjectName(desktop); 152 return name; 153 } 154 155 } // namespace sandbox 156