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 23 namespace remoting { 24 25 namespace { 26 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 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. 39 base::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. 44 bool LowerProcessIntegrityLevel(DWORD max_level) { 45 base::win::ScopedHandle token; 46 if (!OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY | TOKEN_WRITE, 47 token.Receive())) { 48 PLOG(ERROR) << "OpenProcessToken() failed"; 49 return false; 50 } 51 52 TypedBuffer<TOKEN_MANDATORY_LABEL> mandatory_label; 53 DWORD length = 0; 54 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); 62 63 // Get the the mandatory label. 64 result = GetTokenInformation(token, TokenIntegrityLevel, 65 mandatory_label.get(), length, &length); 66 } 67 if (!result) { 68 PLOG(ERROR) << "Failed to get the mandatory label"; 69 return false; 70 } 71 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); 77 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(), 82 length)) { 83 PLOG(ERROR) << "Failed to set the mandatory label"; 84 return false; 85 } 86 } 87 88 return true; 89 } 90 91 } // namespace 92 93 ChromotingModule::ChromotingModule( 94 ATL::_ATL_OBJMAP_ENTRY* classes, 95 ATL::_ATL_OBJMAP_ENTRY* classes_end) 96 : classes_(classes), 97 classes_end_(classes_end) { 98 // Don't do anything if COM initialization failed. 99 if (!com_initializer_.succeeded()) 100 return; 101 102 ATL::_AtlComModule.ExecuteObjectMain(true); 103 } 104 105 ChromotingModule::~ChromotingModule() { 106 // Don't do anything if COM initialization failed. 107 if (!com_initializer_.succeeded()) 108 return; 109 110 Term(); 111 ATL::_AtlComModule.ExecuteObjectMain(false); 112 } 113 114 // static 115 scoped_refptr<AutoThreadTaskRunner> ChromotingModule::task_runner() { 116 return g_module_task_runner.Get(); 117 } 118 119 bool ChromotingModule::Run() { 120 // Don't do anything if COM initialization failed. 121 if (!com_initializer_.succeeded()) 122 return false; 123 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 << "."; 130 return false; 131 } 132 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()); 138 139 // Start accepting activations. 140 result = CoResumeClassObjects(); 141 if (FAILED(result)) { 142 LOG(ERROR) << "CoResumeClassObjects() failed, result=0x" 143 << std::hex << result << std::dec << "."; 144 return false; 145 } 146 147 // Run the loop until the module lock counter reaches zero. 148 run_loop.Run(); 149 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 << "."; 155 return false; 156 } 157 158 return true; 159 } 160 161 LONG ChromotingModule::Unlock() { 162 LONG count = ATL::CAtlModuleT<ChromotingModule>::Unlock(); 163 164 if (!count) { 165 // Stop accepting activations. 166 HRESULT hr = CoSuspendClassObjects(); 167 CHECK(SUCCEEDED(hr)); 168 169 // Release the message loop reference, causing the message loop to exit. 170 g_module_task_runner.Get() = NULL; 171 } 172 173 return count; 174 } 175 176 HRESULT ChromotingModule::RegisterClassObjects(DWORD class_context, 177 DWORD flags) { 178 for (ATL::_ATL_OBJMAP_ENTRY* i = classes_; i != classes_end_; ++i) { 179 HRESULT result = i->RegisterClassObject(class_context, flags); 180 if (FAILED(result)) 181 return result; 182 } 183 184 return S_OK; 185 } 186 187 HRESULT ChromotingModule::RevokeClassObjects() { 188 for (ATL::_ATL_OBJMAP_ENTRY* i = classes_; i != classes_end_; ++i) { 189 HRESULT result = i->RevokeClassObject(); 190 if (FAILED(result)) 191 return result; 192 } 193 194 return S_OK; 195 } 196 197 // Elevated controller entry point. 198 int ElevatedControllerMain() { 199 ATL::_ATL_OBJMAP_ENTRY elevated_controller_entry[] = { 200 OBJECT_ENTRY(__uuidof(ElevatedController), ElevatedController) 201 }; 202 203 ChromotingModule module(elevated_controller_entry, 204 elevated_controller_entry + 1); 205 206 if (!InitializeComSecurity(WideToUTF8(kElevatedControllerSd), "", true)) 207 return kInitializationFailed; 208 209 if (!module.Run()) 210 return kInitializationFailed; 211 212 return kSuccessExitCode; 213 } 214 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; 222 } 223 224 ATL::_ATL_OBJMAP_ENTRY rdp_client_entry[] = { 225 OBJECT_ENTRY(__uuidof(RdpDesktopSession), RdpDesktopSession) 226 }; 227 228 ChromotingModule module(rdp_client_entry, rdp_client_entry + 1); 229 return module.Run() ? kSuccessExitCode : kInitializationFailed; 230 } 231 232 } // namespace remoting 233