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 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, 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, 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, TokenIntegrityLevel, mandatory_label.get(), 83 length)) { 84 PLOG(ERROR) << "Failed to set the mandatory label"; 85 return false; 86 } 87 } 88 89 return true; 90 } 91 92 } // namespace 93 94 ChromotingModule::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 106 ChromotingModule::~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 116 scoped_refptr<AutoThreadTaskRunner> ChromotingModule::task_runner() { 117 return g_module_task_runner.Get(); 118 } 119 120 bool 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::MessageLoop message_loop(base::MessageLoop::TYPE_UI); 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 162 LONG 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 177 HRESULT 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 188 HRESULT 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. 199 int 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(WideToUTF8(kElevatedControllerSd), "", true)) 208 return kInitializationFailed; 209 210 if (!module.Run()) 211 return kInitializationFailed; 212 213 return kSuccessExitCode; 214 } 215 216 // RdpClient entry point. 217 int 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