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.
5 #include "remoting/host/win/chromoting_module.h"
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"
27 // A security descriptor allowing local processes running under SYSTEM, built-in
28 // administrators and interactive users to call COM methods.
29 const wchar_t kElevatedControllerSd[] =
30 SDDL_OWNER L":" SDDL_BUILTIN_ADMINISTRATORS
31 SDDL_GROUP L":" SDDL_BUILTIN_ADMINISTRATORS
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);
38 // Holds a reference to the task runner used by the module.
39 base::LazyInstance<scoped_refptr<AutoThreadTaskRunner> > g_module_task_runner =
40 LAZY_INSTANCE_INITIALIZER;
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.
44 bool LowerProcessIntegrityLevel(DWORD max_level) {
45 base::win::ScopedHandle token;
46 if (!OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY | TOKEN_WRITE,
48 PLOG(ERROR) << "OpenProcessToken() failed";
52 TypedBuffer<TOKEN_MANDATORY_LABEL> mandatory_label;
55 // Get the size of the buffer needed to hold the mandatory label.
56 BOOL result = GetTokenInformation(token, TokenIntegrityLevel,
57 mandatory_label.get(), length, &length);
58 if (!result && GetLastError() == ERROR_INSUFFICIENT_BUFFER) {
59 // Allocate a buffer that is large enough.
60 TypedBuffer<TOKEN_MANDATORY_LABEL> buffer(length);
61 mandatory_label.Swap(buffer);
63 // Get the the mandatory label.
64 result = GetTokenInformation(token, TokenIntegrityLevel,
65 mandatory_label.get(), length, &length);
68 PLOG(ERROR) << "Failed to get the mandatory label";
72 // Read the current integrity level.
73 DWORD sub_authority_count =
74 *GetSidSubAuthorityCount(mandatory_label->Label.Sid);
75 DWORD* current_level = GetSidSubAuthority(mandatory_label->Label.Sid,
76 sub_authority_count - 1);
78 // Set the integrity level to |max_level| if needed.
79 if (*current_level > max_level) {
80 *current_level = max_level;
81 if (!SetTokenInformation(token, TokenIntegrityLevel, mandatory_label.get(),
83 PLOG(ERROR) << "Failed to set the mandatory label";
93 ChromotingModule::ChromotingModule(
94 ATL::_ATL_OBJMAP_ENTRY* classes,
95 ATL::_ATL_OBJMAP_ENTRY* classes_end)
97 classes_end_(classes_end) {
98 // Don't do anything if COM initialization failed.
99 if (!com_initializer_.succeeded())
102 ATL::_AtlComModule.ExecuteObjectMain(true);
105 ChromotingModule::~ChromotingModule() {
106 // Don't do anything if COM initialization failed.
107 if (!com_initializer_.succeeded())
111 ATL::_AtlComModule.ExecuteObjectMain(false);
115 scoped_refptr<AutoThreadTaskRunner> ChromotingModule::task_runner() {
116 return g_module_task_runner.Get();
119 bool ChromotingModule::Run() {
120 // Don't do anything if COM initialization failed.
121 if (!com_initializer_.succeeded())
124 // Register class objects.
125 HRESULT result = RegisterClassObjects(CLSCTX_LOCAL_SERVER,
126 REGCLS_MULTIPLEUSE | REGCLS_SUSPENDED);
127 if (FAILED(result)) {
128 LOG(ERROR) << "Failed to register class objects, result=0x"
129 << std::hex << result << std::dec << ".";
133 // Arrange to run |message_loop| until no components depend on it.
134 base::MessageLoop message_loop(base::MessageLoop::TYPE_UI);
135 base::RunLoop run_loop;
136 g_module_task_runner.Get() = new AutoThreadTaskRunner(
137 message_loop.message_loop_proxy(), run_loop.QuitClosure());
139 // Start accepting activations.
140 result = CoResumeClassObjects();
141 if (FAILED(result)) {
142 LOG(ERROR) << "CoResumeClassObjects() failed, result=0x"
143 << std::hex << result << std::dec << ".";
147 // Run the loop until the module lock counter reaches zero.
150 // Unregister class objects.
151 result = RevokeClassObjects();
152 if (FAILED(result)) {
153 LOG(ERROR) << "Failed to unregister class objects, result=0x"
154 << std::hex << result << std::dec << ".";
161 LONG ChromotingModule::Unlock() {
162 LONG count = ATL::CAtlModuleT<ChromotingModule>::Unlock();
165 // Stop accepting activations.
166 HRESULT hr = CoSuspendClassObjects();
167 CHECK(SUCCEEDED(hr));
169 // Release the message loop reference, causing the message loop to exit.
170 g_module_task_runner.Get() = NULL;
176 HRESULT ChromotingModule::RegisterClassObjects(DWORD class_context,
178 for (ATL::_ATL_OBJMAP_ENTRY* i = classes_; i != classes_end_; ++i) {
179 HRESULT result = i->RegisterClassObject(class_context, flags);
187 HRESULT ChromotingModule::RevokeClassObjects() {
188 for (ATL::_ATL_OBJMAP_ENTRY* i = classes_; i != classes_end_; ++i) {
189 HRESULT result = i->RevokeClassObject();
197 // Elevated controller entry point.
198 int ElevatedControllerMain() {
199 ATL::_ATL_OBJMAP_ENTRY elevated_controller_entry[] = {
200 OBJECT_ENTRY(__uuidof(ElevatedController), ElevatedController)
203 ChromotingModule module(elevated_controller_entry,
204 elevated_controller_entry + 1);
206 if (!InitializeComSecurity(WideToUTF8(kElevatedControllerSd), "", true))
207 return kInitializationFailed;
210 return kInitializationFailed;
212 return kSuccessExitCode;
215 // RdpClient entry point.
216 int RdpDesktopSessionMain() {
217 // Lower the integrity level to medium, which is the lowest level at which
218 // the RDP ActiveX control can run.
219 if (base::win::GetVersion() >= base::win::VERSION_VISTA) {
220 if (!LowerProcessIntegrityLevel(SECURITY_MANDATORY_MEDIUM_RID))
221 return kInitializationFailed;
224 ATL::_ATL_OBJMAP_ENTRY rdp_client_entry[] = {
225 OBJECT_ENTRY(__uuidof(RdpDesktopSession), RdpDesktopSession)
228 ChromotingModule module(rdp_client_entry, rdp_client_entry + 1);
229 return module.Run() ? kSuccessExitCode : kInitializationFailed;
232 } // namespace remoting