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
14namespace {
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.
19bool 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
35namespace sandbox {
36
37ResultCode 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
57ResultCode 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
118base::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
141base::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