- add sources.
[platform/framework/web/crosswalk.git] / src / remoting / host / win / chromoting_module.cc
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