• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.
LowerProcessIntegrityLevel(DWORD max_level)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 
ChromotingModule(ATL::_ATL_OBJMAP_ENTRY * classes,ATL::_ATL_OBJMAP_ENTRY * classes_end)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 
~ChromotingModule()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
task_runner()116 scoped_refptr<AutoThreadTaskRunner> ChromotingModule::task_runner() {
117   return g_module_task_runner.Get();
118 }
119 
Run()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::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 
Unlock()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 
RegisterClassObjects(DWORD class_context,DWORD flags)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 
RevokeClassObjects()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.
ElevatedControllerMain()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(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.
RdpDesktopSessionMain()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