1// Copyright (c) 2013 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 "remoting/host/win/chromoting_module.h"
6
7#include <sddl.h>
8
9#include "base/lazy_instance.h"
10#include "base/logging.h"
11#include "base/message_loop/message_loop.h"
12#include "base/run_loop.h"
13#include "base/strings/utf_string_conversions.h"
14#include "base/win/scoped_handle.h"
15#include "base/win/windows_version.h"
16#include "remoting/base/auto_thread_task_runner.h"
17#include "remoting/base/typed_buffer.h"
18#include "remoting/host/host_exit_codes.h"
19#include "remoting/host/win/com_security.h"
20#include "remoting/host/win/elevated_controller.h"
21#include "remoting/host/win/rdp_desktop_session.h"
22
23namespace remoting {
24
25namespace {
26
27// A security descriptor allowing local processes running under SYSTEM, built-in
28// administrators and interactive users to call COM methods.
29const wchar_t kElevatedControllerSd[] =
30    SDDL_OWNER L":" SDDL_BUILTIN_ADMINISTRATORS
31    SDDL_GROUP L":" SDDL_BUILTIN_ADMINISTRATORS
32    SDDL_DACL L":"
33    SDDL_ACE(SDDL_ACCESS_ALLOWED, SDDL_COM_EXECUTE_LOCAL, SDDL_LOCAL_SYSTEM)
34    SDDL_ACE(SDDL_ACCESS_ALLOWED, SDDL_COM_EXECUTE_LOCAL,
35             SDDL_BUILTIN_ADMINISTRATORS)
36    SDDL_ACE(SDDL_ACCESS_ALLOWED, SDDL_COM_EXECUTE_LOCAL, SDDL_INTERACTIVE);
37
38// Holds a reference to the task runner used by the module.
39base::LazyInstance<scoped_refptr<AutoThreadTaskRunner> > g_module_task_runner =
40    LAZY_INSTANCE_INITIALIZER;
41
42// Lowers the process integrity level such that it does not exceed |max_level|.
43// |max_level| is expected to be one of SECURITY_MANDATORY_XXX constants.
44bool LowerProcessIntegrityLevel(DWORD max_level) {
45  HANDLE temp_handle;
46  if (!OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY | TOKEN_WRITE,
47                        &temp_handle)) {
48    PLOG(ERROR) << "OpenProcessToken() failed";
49    return false;
50  }
51  base::win::ScopedHandle token(temp_handle);
52
53  TypedBuffer<TOKEN_MANDATORY_LABEL> mandatory_label;
54  DWORD length = 0;
55
56  // Get the size of the buffer needed to hold the mandatory label.
57  BOOL result = GetTokenInformation(token.Get(), TokenIntegrityLevel,
58                                    mandatory_label.get(), length, &length);
59  if (!result && GetLastError() == ERROR_INSUFFICIENT_BUFFER) {
60    // Allocate a buffer that is large enough.
61    TypedBuffer<TOKEN_MANDATORY_LABEL> buffer(length);
62    mandatory_label.Swap(buffer);
63
64    // Get the the mandatory label.
65    result = GetTokenInformation(token.Get(), TokenIntegrityLevel,
66                                 mandatory_label.get(), length, &length);
67  }
68  if (!result) {
69    PLOG(ERROR) << "Failed to get the mandatory label";
70    return false;
71  }
72
73  // Read the current integrity level.
74  DWORD sub_authority_count =
75      *GetSidSubAuthorityCount(mandatory_label->Label.Sid);
76  DWORD* current_level = GetSidSubAuthority(mandatory_label->Label.Sid,
77                                            sub_authority_count - 1);
78
79  // Set the integrity level to |max_level| if needed.
80  if (*current_level > max_level) {
81    *current_level = max_level;
82    if (!SetTokenInformation(token.Get(), TokenIntegrityLevel,
83                             mandatory_label.get(), length)) {
84      PLOG(ERROR) << "Failed to set the mandatory label";
85      return false;
86    }
87  }
88
89  return true;
90}
91
92}  // namespace
93
94ChromotingModule::ChromotingModule(
95    ATL::_ATL_OBJMAP_ENTRY* classes,
96    ATL::_ATL_OBJMAP_ENTRY* classes_end)
97    : classes_(classes),
98      classes_end_(classes_end) {
99  // Don't do anything if COM initialization failed.
100  if (!com_initializer_.succeeded())
101    return;
102
103  ATL::_AtlComModule.ExecuteObjectMain(true);
104}
105
106ChromotingModule::~ChromotingModule() {
107  // Don't do anything if COM initialization failed.
108  if (!com_initializer_.succeeded())
109    return;
110
111  Term();
112  ATL::_AtlComModule.ExecuteObjectMain(false);
113}
114
115// static
116scoped_refptr<AutoThreadTaskRunner> ChromotingModule::task_runner() {
117  return g_module_task_runner.Get();
118}
119
120bool ChromotingModule::Run() {
121  // Don't do anything if COM initialization failed.
122  if (!com_initializer_.succeeded())
123    return false;
124
125  // Register class objects.
126  HRESULT result = RegisterClassObjects(CLSCTX_LOCAL_SERVER,
127                                        REGCLS_MULTIPLEUSE | REGCLS_SUSPENDED);
128  if (FAILED(result)) {
129    LOG(ERROR) << "Failed to register class objects, result=0x"
130               << std::hex << result << std::dec << ".";
131    return false;
132  }
133
134  // Arrange to run |message_loop| until no components depend on it.
135  base::MessageLoopForUI message_loop;
136  base::RunLoop run_loop;
137  g_module_task_runner.Get() = new AutoThreadTaskRunner(
138      message_loop.message_loop_proxy(), run_loop.QuitClosure());
139
140  // Start accepting activations.
141  result = CoResumeClassObjects();
142  if (FAILED(result)) {
143    LOG(ERROR) << "CoResumeClassObjects() failed, result=0x"
144               << std::hex << result << std::dec << ".";
145    return false;
146  }
147
148  // Run the loop until the module lock counter reaches zero.
149  run_loop.Run();
150
151  // Unregister class objects.
152  result = RevokeClassObjects();
153  if (FAILED(result)) {
154    LOG(ERROR) << "Failed to unregister class objects, result=0x"
155               << std::hex << result << std::dec << ".";
156    return false;
157  }
158
159  return true;
160}
161
162LONG ChromotingModule::Unlock() {
163  LONG count = ATL::CAtlModuleT<ChromotingModule>::Unlock();
164
165  if (!count) {
166    // Stop accepting activations.
167    HRESULT hr = CoSuspendClassObjects();
168    CHECK(SUCCEEDED(hr));
169
170    // Release the message loop reference, causing the message loop to exit.
171    g_module_task_runner.Get() = NULL;
172  }
173
174  return count;
175}
176
177HRESULT ChromotingModule::RegisterClassObjects(DWORD class_context,
178                                               DWORD flags) {
179  for (ATL::_ATL_OBJMAP_ENTRY* i = classes_; i != classes_end_; ++i) {
180    HRESULT result = i->RegisterClassObject(class_context, flags);
181    if (FAILED(result))
182      return result;
183  }
184
185  return S_OK;
186}
187
188HRESULT ChromotingModule::RevokeClassObjects() {
189  for (ATL::_ATL_OBJMAP_ENTRY* i = classes_; i != classes_end_; ++i) {
190    HRESULT result = i->RevokeClassObject();
191    if (FAILED(result))
192      return result;
193  }
194
195  return S_OK;
196}
197
198// Elevated controller entry point.
199int ElevatedControllerMain() {
200  ATL::_ATL_OBJMAP_ENTRY elevated_controller_entry[] = {
201    OBJECT_ENTRY(__uuidof(ElevatedController), ElevatedController)
202  };
203
204  ChromotingModule module(elevated_controller_entry,
205                          elevated_controller_entry + 1);
206
207  if (!InitializeComSecurity(base::WideToUTF8(kElevatedControllerSd), "", true))
208    return kInitializationFailed;
209
210  if (!module.Run())
211    return kInitializationFailed;
212
213  return kSuccessExitCode;
214}
215
216// RdpClient entry point.
217int RdpDesktopSessionMain() {
218  // Lower the integrity level to medium, which is the lowest level at which
219  // the RDP ActiveX control can run.
220  if (base::win::GetVersion() >= base::win::VERSION_VISTA) {
221    if (!LowerProcessIntegrityLevel(SECURITY_MANDATORY_MEDIUM_RID))
222      return kInitializationFailed;
223  }
224
225  ATL::_ATL_OBJMAP_ENTRY rdp_client_entry[] = {
226    OBJECT_ENTRY(__uuidof(RdpDesktopSession), RdpDesktopSession)
227  };
228
229  ChromotingModule module(rdp_client_entry, rdp_client_entry + 1);
230  return module.Run() ? kSuccessExitCode : kInitializationFailed;
231}
232
233} // namespace remoting
234