Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(121)

Side by Side Diff: remoting/host/setup/daemon_controller_delegate_win.cc

Issue 877343004: Move the |ElevatedDaemonController| implementation to |DaemonControllerDelegateWin| (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Created 5 years, 10 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « remoting/host/setup/daemon_controller_delegate_win.h ('k') | no next file » | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 // Copyright 2013 The Chromium Authors. All rights reserved. 1 // Copyright 2013 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include "remoting/host/setup/daemon_controller_delegate_win.h" 5 #include "remoting/host/setup/daemon_controller_delegate_win.h"
6 6
7 #include "base/basictypes.h" 7 #include "base/basictypes.h"
8 #include "base/bind.h" 8 #include "base/files/file_path.h"
9 #include "base/bind_helpers.h" 9 #include "base/files/file_util.h"
10 #include "base/compiler_specific.h"
11 #include "base/json/json_reader.h" 10 #include "base/json/json_reader.h"
12 #include "base/json/json_writer.h" 11 #include "base/json/json_writer.h"
13 #include "base/logging.h" 12 #include "base/logging.h"
14 #include "base/strings/string16.h"
15 #include "base/strings/utf_string_conversions.h"
16 #include "base/thread_task_runner_handle.h" 13 #include "base/thread_task_runner_handle.h"
17 #include "base/time/time.h"
18 #include "base/timer/timer.h"
19 #include "base/values.h" 14 #include "base/values.h"
20 #include "base/win/scoped_bstr.h" 15 #include "base/win/scoped_bstr.h"
21 #include "base/win/scoped_comptr.h"
22 #include "base/win/windows_version.h" 16 #include "base/win/windows_version.h"
23 #include "remoting/base/scoped_sc_handle_win.h" 17 #include "remoting/base/scoped_sc_handle_win.h"
24 #include "remoting/host/branding.h" 18 #include "remoting/host/branding.h"
25 // chromoting_lib.h contains MIDL-generated declarations. 19 #include "remoting/host/host_config.h"
26 #include "remoting/host/chromoting_lib.h"
27 #include "remoting/host/usage_stats_consent.h" 20 #include "remoting/host/usage_stats_consent.h"
28 21 #include "remoting/host/win/security_descriptor.h"
29 using base::win::ScopedBstr;
30 using base::win::ScopedComPtr;
31 22
32 namespace remoting { 23 namespace remoting {
33 24
34 namespace { 25 namespace {
35 26
36 // ProgID of the daemon controller. 27 // The maximum size of the configuration file. "1MB ought to be enough" for any
37 const wchar_t kDaemonController[] = 28 // reasonable configuration we will ever need. 1MB is low enough to make
38 L"ChromotingElevatedController.ElevatedController"; 29 // the probability of out of memory situation fairly low. OOM is still possible
39 30 // and we will crash if it occurs.
40 // The COM elevation moniker for the Elevated Controller. 31 const size_t kMaxConfigFileSize = 1024 * 1024;
41 const wchar_t kDaemonControllerElevationMoniker[] = 32
42 L"Elevation:Administrator!new:" 33 // The host configuration file name.
43 L"ChromotingElevatedController.ElevatedController"; 34 const base::FilePath::CharType kConfigFileName[] =
44 35 FILE_PATH_LITERAL("host.json");
45 // The maximum duration of keeping a reference to a privileged instance of 36
46 // the Daemon Controller. This effectively reduces number of UAC prompts a user 37 // The unprivileged configuration file name.
47 // sees. 38 const base::FilePath::CharType kUnprivilegedConfigFileName[] =
48 const int kPrivilegedTimeoutSec = 5 * 60; 39 FILE_PATH_LITERAL("host_unprivileged.json");
49 40
50 // The maximum duration of keeping a reference to an unprivileged instance of 41 // The extension for the temporary file.
51 // the Daemon Controller. This interval should not be too long. If upgrade 42 const base::FilePath::CharType kTempFileExtension[] =
52 // happens while there is a live reference to a Daemon Controller instance 43 FILE_PATH_LITERAL("json~");
53 // the old binary still can be used. So dropping the references often makes sure 44
54 // that the old binary will go away sooner. 45 // The host configuration file security descriptor that enables full access to
55 const int kUnprivilegedTimeoutSec = 60; 46 // Local System and built-in administrators only.
56 47 const char kConfigFileSecurityDescriptor[] =
57 void ConfigToString(const base::DictionaryValue& config, ScopedBstr* out) { 48 "O:BAG:BAD:(A;;GA;;;SY)(A;;GA;;;BA)";
58 std::string config_str; 49
59 base::JSONWriter::Write(&config, &config_str); 50 const char kUnprivilegedConfigFileSecurityDescriptor[] =
60 ScopedBstr config_scoped_bstr(base::UTF8ToUTF16(config_str).c_str()); 51 "O:BAG:BAD:(A;;GA;;;SY)(A;;GA;;;BA)(A;;GR;;;AU)";
61 out->Swap(config_scoped_bstr); 52
53 // Configuration keys.
54
55 // The configuration keys that cannot be specified in UpdateConfig().
56 const char* const kReadonlyKeys[] = {
57 kHostIdConfigPath, kHostOwnerConfigPath, kHostOwnerEmailConfigPath,
58 kXmppLoginConfigPath };
59
60 // The configuration keys whose values may be read by GetConfig().
61 const char* const kUnprivilegedConfigKeys[] = {
62 kHostIdConfigPath, kXmppLoginConfigPath };
63
64 // Reads and parses the configuration file up to |kMaxConfigFileSize| in
65 // size.
66 bool ReadConfig(const base::FilePath& filename,
67 scoped_ptr<base::DictionaryValue>* config_out) {
68 std::string file_content;
69 if (!base::ReadFileToString(filename, &file_content, kMaxConfigFileSize)) {
70 PLOG(ERROR) << "Failed to read '" << filename.value() << "'.";
71 return false;
72 }
73
74 // Parse the JSON configuration, expecting it to contain a dictionary.
75 scoped_ptr<base::Value> value(
76 base::JSONReader::Read(file_content, base::JSON_ALLOW_TRAILING_COMMAS));
77
78 base::DictionaryValue* dictionary;
79 if (!value || !value->GetAsDictionary(&dictionary)) {
80 LOG(ERROR) << "Failed to parse '" << filename.value() << "'.";
81 return false;
82 }
83
84 value.release();
85 config_out->reset(dictionary);
86 return true;
87 }
88
89 base::FilePath GetTempLocationFor(const base::FilePath& filename) {
90 return filename.ReplaceExtension(kTempFileExtension);
91 }
92
93 // Writes a config file to a temporary location.
94 bool WriteConfigFileToTemp(const base::FilePath& filename,
95 const char* security_descriptor,
96 const std::string& content) {
97 // Create the security descriptor for the configuration file.
98 ScopedSd sd = ConvertSddlToSd(security_descriptor);
99 if (!sd) {
100 PLOG(ERROR)
101 << "Failed to create a security descriptor for the configuration file";
102 return false;
103 }
104
105 SECURITY_ATTRIBUTES security_attributes = {0};
106 security_attributes.nLength = sizeof(security_attributes);
107 security_attributes.lpSecurityDescriptor = sd.get();
108 security_attributes.bInheritHandle = FALSE;
109
110 // Create a temporary file and write configuration to it.
111 base::FilePath tempname = GetTempLocationFor(filename);
112 base::win::ScopedHandle file(
113 CreateFileW(tempname.value().c_str(),
114 GENERIC_WRITE,
115 0,
116 &security_attributes,
117 CREATE_ALWAYS,
118 FILE_FLAG_SEQUENTIAL_SCAN,
119 nullptr));
120
121 if (!file.IsValid()) {
122 PLOG(ERROR) << "Failed to create '" << filename.value() << "'";
123 return false;
124 }
125
126 DWORD written;
127 if (!::WriteFile(file.Get(), content.c_str(), content.length(),
128 &written, nullptr)) {
129 PLOG(ERROR) << "Failed to write to '" << filename.value() << "'";
130 return false;
131 }
132
133 return true;
134 }
135
136 // Moves a config file from its temporary location to its permanent location.
137 bool MoveConfigFileFromTemp(const base::FilePath& filename) {
138 // Now that the configuration is stored successfully replace the actual
139 // configuration file.
140 base::FilePath tempname = GetTempLocationFor(filename);
141 if (!MoveFileExW(tempname.value().c_str(),
142 filename.value().c_str(),
143 MOVEFILE_REPLACE_EXISTING)) {
144 PLOG(ERROR) << "Failed to rename '" << tempname.value() << "' to '"
145 << filename.value() << "'";
146 return false;
147 }
148
149 return true;
150 }
151
152 // Writes the configuration file up to |kMaxConfigFileSize| in size.
153 bool WriteConfig(const std::string& content) {
154 if (content.length() > kMaxConfigFileSize) {
155 return false;
156 }
157
158 // Extract the configuration data that the user will verify.
159 scoped_ptr<base::Value> config_value(base::JSONReader::Read(content));
160 if (!config_value.get()) {
161 return false;
162 }
163 base::DictionaryValue* config_dict = nullptr;
164 if (!config_value->GetAsDictionary(&config_dict)) {
165 return false;
166 }
167 std::string email;
168 if (!config_dict->GetString(kHostOwnerEmailConfigPath, &email) &&
169 !config_dict->GetString(kHostOwnerConfigPath, &email) &&
170 !config_dict->GetString(kXmppLoginConfigPath, &email)) {
171 return false;
172 }
173 std::string host_id, host_secret_hash;
174 if (!config_dict->GetString(kHostIdConfigPath, &host_id) ||
175 !config_dict->GetString(kHostSecretHashConfigPath, &host_secret_hash)) {
176 return false;
177 }
178
179 // Extract the unprivileged fields from the configuration.
180 base::DictionaryValue unprivileged_config_dict;
181 for (int i = 0; i < arraysize(kUnprivilegedConfigKeys); ++i) {
182 const char* key = kUnprivilegedConfigKeys[i];
183 base::string16 value;
184 if (config_dict->GetString(key, &value)) {
185 unprivileged_config_dict.SetString(key, value);
186 }
187 }
188 std::string unprivileged_config_str;
189 base::JSONWriter::Write(&unprivileged_config_dict, &unprivileged_config_str);
190
191 // Write the full configuration file to a temporary location.
192 base::FilePath full_config_file_path =
193 remoting::GetConfigDir().Append(kConfigFileName);
194 if (!WriteConfigFileToTemp(full_config_file_path,
195 kConfigFileSecurityDescriptor,
196 content)) {
197 return false;
198 }
199
200 // Write the unprivileged configuration file to a temporary location.
201 base::FilePath unprivileged_config_file_path =
202 remoting::GetConfigDir().Append(kUnprivilegedConfigFileName);
203 if (!WriteConfigFileToTemp(unprivileged_config_file_path,
204 kUnprivilegedConfigFileSecurityDescriptor,
205 unprivileged_config_str)) {
206 return false;
207 }
208
209 // Move the full and unprivileged configuration files to their permanent
210 // locations.
211 return MoveConfigFileFromTemp(full_config_file_path) &&
212 MoveConfigFileFromTemp(unprivileged_config_file_path);
62 } 213 }
63 214
64 DaemonController::State ConvertToDaemonState(DWORD service_state) { 215 DaemonController::State ConvertToDaemonState(DWORD service_state) {
65 switch (service_state) { 216 switch (service_state) {
66 case SERVICE_RUNNING: 217 case SERVICE_RUNNING:
67 return DaemonController::STATE_STARTED; 218 return DaemonController::STATE_STARTED;
68 219
69 case SERVICE_CONTINUE_PENDING: 220 case SERVICE_CONTINUE_PENDING:
70 case SERVICE_START_PENDING: 221 case SERVICE_START_PENDING:
71 return DaemonController::STATE_STARTING; 222 return DaemonController::STATE_STARTING;
72 break; 223 break;
73 224
74 case SERVICE_PAUSE_PENDING: 225 case SERVICE_PAUSE_PENDING:
75 case SERVICE_STOP_PENDING: 226 case SERVICE_STOP_PENDING:
76 return DaemonController::STATE_STOPPING; 227 return DaemonController::STATE_STOPPING;
77 break; 228 break;
78 229
79 case SERVICE_PAUSED: 230 case SERVICE_PAUSED:
80 case SERVICE_STOPPED: 231 case SERVICE_STOPPED:
81 return DaemonController::STATE_STOPPED; 232 return DaemonController::STATE_STOPPED;
82 break; 233 break;
83 234
84 default: 235 default:
85 NOTREACHED(); 236 NOTREACHED();
86 return DaemonController::STATE_UNKNOWN; 237 return DaemonController::STATE_UNKNOWN;
87 } 238 }
88 } 239 }
89 240
90 DWORD OpenService(ScopedScHandle* service_out) { 241 ScopedScHandle OpenService(DWORD access) {
91 // Open the service and query its current state. 242 // Open the service and query its current state.
92 ScopedScHandle scmanager( 243 ScopedScHandle scmanager(
93 ::OpenSCManagerW(nullptr, SERVICES_ACTIVE_DATABASE, 244 ::OpenSCManagerW(nullptr, SERVICES_ACTIVE_DATABASE,
94 SC_MANAGER_CONNECT | SC_MANAGER_ENUMERATE_SERVICE)); 245 SC_MANAGER_CONNECT | SC_MANAGER_ENUMERATE_SERVICE));
95 if (!scmanager.IsValid()) { 246 if (!scmanager.IsValid()) {
96 DWORD error = GetLastError();
97 PLOG(ERROR) << "Failed to connect to the service control manager"; 247 PLOG(ERROR) << "Failed to connect to the service control manager";
98 return error; 248 return ScopedScHandle();
99 } 249 }
100 250
101 ScopedScHandle service(::OpenServiceW(scmanager.Get(), kWindowsServiceName, 251 ScopedScHandle service(::OpenServiceW(scmanager.Get(), kWindowsServiceName,
102 SERVICE_QUERY_STATUS)); 252 access));
103 if (!service.IsValid()) { 253 if (!service.IsValid()) {
104 DWORD error = GetLastError(); 254 PLOG(ERROR) << "Failed to open to the '" << kWindowsServiceName
105 if (error != ERROR_SERVICE_DOES_NOT_EXIST) { 255 << "' service";
106 PLOG(ERROR) << "Failed to open to the '" << kWindowsServiceName
107 << "' service";
108 }
109 return error;
110 } 256 }
111 257
112 service_out->Set(service.Take()); 258 return service.Pass();
113 return ERROR_SUCCESS;
114 }
115
116 DaemonController::AsyncResult HResultToAsyncResult(
117 HRESULT hr) {
118 if (SUCCEEDED(hr)) {
119 return DaemonController::RESULT_OK;
120 } else if (hr == HRESULT_FROM_WIN32(ERROR_CANCELLED)) {
121 return DaemonController::RESULT_CANCELLED;
122 } else {
123 // TODO(sergeyu): Report other errors to the webapp once it knows
124 // how to handle them.
125 return DaemonController::RESULT_FAILED;
126 }
127 } 259 }
128 260
129 void InvokeCompletionCallback( 261 void InvokeCompletionCallback(
130 const DaemonController::CompletionCallback& done, HRESULT hr) { 262 const DaemonController::CompletionCallback& done, bool success) {
131 done.Run(HResultToAsyncResult(hr)); 263 DaemonController::AsyncResult async_result =
264 success ? DaemonController::RESULT_OK : DaemonController::RESULT_FAILED;
265 done.Run(async_result);
132 } 266 }
133 267
134 HWND GetTopLevelWindow(HWND window) { 268 bool SetConfig(const std::string& config) {
135 if (window == nullptr) { 269 // Determine the config directory path and create it if necessary.
136 return nullptr; 270 base::FilePath config_dir = remoting::GetConfigDir();
271 if (!base::CreateDirectory(config_dir)) {
272 PLOG(ERROR) << "Failed to create the config directory.";
273 return false;
137 } 274 }
138 275
139 for (;;) { 276 return WriteConfig(config);
140 LONG style = GetWindowLong(window, GWL_STYLE); 277 }
141 if ((style & WS_OVERLAPPEDWINDOW) == WS_OVERLAPPEDWINDOW || 278
142 (style & WS_POPUP) == WS_POPUP) { 279 bool StartDaemon() {
143 return window; 280 DWORD access = SERVICE_CHANGE_CONFIG | SERVICE_QUERY_STATUS |
281 SERVICE_START | SERVICE_STOP;
282 ScopedScHandle service = OpenService(access);
283 if (!service.IsValid())
284 return false;
285
286 // Change the service start type to 'auto'.
287 if (!::ChangeServiceConfigW(service.Get(),
288 SERVICE_NO_CHANGE,
289 SERVICE_AUTO_START,
290 SERVICE_NO_CHANGE,
291 nullptr,
292 nullptr,
293 nullptr,
294 nullptr,
295 nullptr,
296 nullptr,
297 nullptr)) {
298 PLOG(ERROR) << "Failed to change the '" << kWindowsServiceName
299 << "'service start type to 'auto'";
300 return false;
301 }
302
303 // Start the service.
304 if (!StartService(service.Get(), 0, nullptr)) {
305 DWORD error = GetLastError();
306 if (error != ERROR_SERVICE_ALREADY_RUNNING) {
307 LOG(ERROR) << "Failed to start the '" << kWindowsServiceName
308 << "'service: " << error;
309
310 return false;
144 } 311 }
312 }
145 313
146 HWND parent = GetAncestor(window, GA_PARENT); 314 return true;
147 if (parent == nullptr) { 315 }
148 return window; 316
317 bool StopDaemon() {
318 DWORD access = SERVICE_CHANGE_CONFIG | SERVICE_QUERY_STATUS |
319 SERVICE_START | SERVICE_STOP;
320 ScopedScHandle service = OpenService(access);
321 if (!service.IsValid())
322 return false;
323
324 // Change the service start type to 'manual'.
325 if (!::ChangeServiceConfigW(service.Get(),
326 SERVICE_NO_CHANGE,
327 SERVICE_DEMAND_START,
328 SERVICE_NO_CHANGE,
329 nullptr,
330 nullptr,
331 nullptr,
332 nullptr,
333 nullptr,
334 nullptr,
335 nullptr)) {
336 PLOG(ERROR) << "Failed to change the '" << kWindowsServiceName
337 << "'service start type to 'manual'";
338 return false;
339 }
340
341 // Stop the service.
342 SERVICE_STATUS status;
343 if (!ControlService(service.Get(), SERVICE_CONTROL_STOP, &status)) {
344 DWORD error = GetLastError();
345 if (error != ERROR_SERVICE_NOT_ACTIVE) {
346 LOG(ERROR) << "Failed to stop the '" << kWindowsServiceName
347 << "'service: " << error;
348 return false;
149 } 349 }
350 }
150 351
151 window = parent; 352 return true;
152 }
153 } 353 }
154 354
155 } // namespace 355 } // namespace
156 356
157 DaemonControllerDelegateWin::DaemonControllerDelegateWin() 357 DaemonControllerDelegateWin::DaemonControllerDelegateWin() {
158 : control_is_elevated_(false),
159 window_handle_(nullptr) {
160 } 358 }
161 359
162 DaemonControllerDelegateWin::~DaemonControllerDelegateWin() { 360 DaemonControllerDelegateWin::~DaemonControllerDelegateWin() {
163 } 361 }
164 362
165 DaemonController::State DaemonControllerDelegateWin::GetState() { 363 DaemonController::State DaemonControllerDelegateWin::GetState() {
166 if (base::win::GetVersion() < base::win::VERSION_XP) { 364 if (base::win::GetVersion() < base::win::VERSION_XP) {
167 return DaemonController::STATE_NOT_IMPLEMENTED; 365 return DaemonController::STATE_NOT_IMPLEMENTED;
168 } 366 }
367
169 // TODO(alexeypa): Make the thread alertable, so we can switch to APC 368 // TODO(alexeypa): Make the thread alertable, so we can switch to APC
170 // notifications rather than polling. 369 // notifications rather than polling.
171 ScopedScHandle service; 370 ScopedScHandle service = OpenService(SERVICE_QUERY_STATUS);
172 DWORD error = OpenService(&service); 371 if (!service.IsValid())
372 return DaemonController::STATE_UNKNOWN;
173 373
174 switch (error) { 374 SERVICE_STATUS status;
175 case ERROR_SUCCESS: { 375 if (!::QueryServiceStatus(service.Get(), &status)) {
176 SERVICE_STATUS status; 376 PLOG(ERROR) << "Failed to query the state of the '"
177 if (::QueryServiceStatus(service.Get(), &status)) { 377 << kWindowsServiceName << "' service";
178 return ConvertToDaemonState(status.dwCurrentState); 378 return DaemonController::STATE_UNKNOWN;
179 } else {
180 PLOG(ERROR) << "Failed to query the state of the '"
181 << kWindowsServiceName << "' service";
182 return DaemonController::STATE_UNKNOWN;
183 }
184 break;
185 }
186 case ERROR_SERVICE_DOES_NOT_EXIST:
187 return DaemonController::STATE_NOT_INSTALLED;
188 default:
189 return DaemonController::STATE_UNKNOWN;
190 } 379 }
380
381 return ConvertToDaemonState(status.dwCurrentState);
191 } 382 }
192 383
193 scoped_ptr<base::DictionaryValue> DaemonControllerDelegateWin::GetConfig() { 384 scoped_ptr<base::DictionaryValue> DaemonControllerDelegateWin::GetConfig() {
194 // Configure and start the Daemon Controller if it is installed already. 385 base::FilePath config_dir = remoting::GetConfigDir();
195 HRESULT hr = ActivateController(); 386
196 if (FAILED(hr)) 387 // Read the unprivileged part of host configuration.
388 scoped_ptr<base::DictionaryValue> config;
389 if (!ReadConfig(config_dir.Append(kUnprivilegedConfigFileName), &config))
197 return nullptr; 390 return nullptr;
198 391
199 // Get the host configuration. 392 return config;
200 ScopedBstr host_config;
201 hr = control_->GetConfig(host_config.Receive());
202 if (FAILED(hr))
203 return nullptr;
204
205 // Parse the string into a dictionary.
206 base::string16 file_content(
207 static_cast<BSTR>(host_config), host_config.Length());
208 scoped_ptr<base::Value> config(
209 base::JSONReader::Read(base::UTF16ToUTF8(file_content),
210 base::JSON_ALLOW_TRAILING_COMMAS));
211
212 if (!config || !config->IsType(base::Value::TYPE_DICTIONARY))
213 return nullptr;
214
215 return make_scoped_ptr(static_cast<base::DictionaryValue*>(config.release()));
216 } 393 }
217 394
218 void DaemonControllerDelegateWin::UpdateConfig( 395 void DaemonControllerDelegateWin::UpdateConfig(
219 scoped_ptr<base::DictionaryValue> config, 396 scoped_ptr<base::DictionaryValue> config,
220 const DaemonController::CompletionCallback& done) { 397 const DaemonController::CompletionCallback& done) {
221 HRESULT hr = ActivateElevatedController(); 398 // Check for bad keys.
222 if (FAILED(hr)) { 399 for (int i = 0; i < arraysize(kReadonlyKeys); ++i) {
223 InvokeCompletionCallback(done, hr); 400 if (config->HasKey(kReadonlyKeys[i])) {
401 LOG(ERROR) << "Cannot update config: '" << kReadonlyKeys[i]
402 << "' is read only.";
403 InvokeCompletionCallback(done, false);
404 return;
405 }
406 }
407 // Get the old config.
408 base::FilePath config_dir = remoting::GetConfigDir();
409 scoped_ptr<base::DictionaryValue> config_old;
410 if (!ReadConfig(config_dir.Append(kConfigFileName), &config_old)) {
411 InvokeCompletionCallback(done, false);
224 return; 412 return;
225 } 413 }
226 414
227 // Update the configuration. 415 // Merge items from the given config into the old config.
228 ScopedBstr config_str(nullptr); 416 config_old->MergeDictionary(config.release());
229 ConfigToString(*config, &config_str);
230 if (config_str == nullptr) {
231 InvokeCompletionCallback(done, E_OUTOFMEMORY);
232 return;
233 }
234 417
235 // Make sure that the PIN confirmation dialog is focused properly. 418 // Write the updated config.
236 hr = control_->SetOwnerWindow( 419 std::string config_updated_str;
237 reinterpret_cast<LONG_PTR>(GetTopLevelWindow(window_handle_))); 420 base::JSONWriter::Write(config_old.get(), &config_updated_str);
238 if (FAILED(hr)) { 421 bool result = WriteConfig(config_updated_str);
239 InvokeCompletionCallback(done, hr);
240 return;
241 }
242 422
243 hr = control_->UpdateConfig(config_str); 423 InvokeCompletionCallback(done, result);
244 InvokeCompletionCallback(done, hr);
245 } 424 }
246 425
247 void DaemonControllerDelegateWin::Stop( 426 void DaemonControllerDelegateWin::Stop(
248 const DaemonController::CompletionCallback& done) { 427 const DaemonController::CompletionCallback& done) {
249 HRESULT hr = ActivateElevatedController(); 428 bool result = StopDaemon();
250 if (SUCCEEDED(hr))
251 hr = control_->StopDaemon();
252 429
253 InvokeCompletionCallback(done, hr); 430 InvokeCompletionCallback(done, result);
254 } 431 }
255 432
256 void DaemonControllerDelegateWin::SetWindow(void* window_handle) { 433 void DaemonControllerDelegateWin::SetWindow(void* window_handle) {
257 window_handle_ = reinterpret_cast<HWND>(window_handle);
258 } 434 }
259 435
260 std::string DaemonControllerDelegateWin::GetVersion() { 436 std::string DaemonControllerDelegateWin::GetVersion() {
261 // Configure and start the Daemon Controller if it is installed already. 437 // TODO (weitaosu): Remove this as GetVersion is not used anymore.
262 HRESULT hr = ActivateController(); 438 return std::string();
263 if (FAILED(hr))
264 return std::string();
265
266 // Get the version string.
267 ScopedBstr version;
268 hr = control_->GetVersion(version.Receive());
269 if (FAILED(hr))
270 return std::string();
271
272 return base::UTF16ToUTF8(
273 base::string16(static_cast<BSTR>(version), version.Length()));
274 } 439 }
275 440
276 DaemonController::UsageStatsConsent 441 DaemonController::UsageStatsConsent
277 DaemonControllerDelegateWin::GetUsageStatsConsent() { 442 DaemonControllerDelegateWin::GetUsageStatsConsent() {
278 DaemonController::UsageStatsConsent consent; 443 DaemonController::UsageStatsConsent consent;
279 consent.supported = true; 444 consent.supported = true;
280 consent.allowed = false; 445 consent.allowed = false;
281 consent.set_by_policy = false; 446 consent.set_by_policy = false;
282 447
283 // Activate the Daemon Controller and see if it supports |IDaemonControl2|. 448 // Get the recorded user's consent.
284 HRESULT hr = ActivateController(); 449 bool allowed;
285 if (FAILED(hr)) { 450 bool set_by_policy;
286 // The host is not installed yet. Assume that the user didn't consent to 451 // If the user's consent is not recorded yet, assume that the user didn't
287 // collecting crash dumps. 452 // consent to collecting crash dumps.
288 return consent; 453 if (remoting::GetUsageStatsConsent(&allowed, &set_by_policy)) {
454 consent.allowed = allowed;
455 consent.set_by_policy = set_by_policy;
289 } 456 }
290 457
291 if (control2_.get() == nullptr) {
292 // The host is installed and does not support crash dump reporting.
293 return consent;
294 }
295
296 // Get the recorded user's consent.
297 BOOL allowed;
298 BOOL set_by_policy;
299 hr = control2_->GetUsageStatsConsent(&allowed, &set_by_policy);
300 if (FAILED(hr)) {
301 // If the user's consent is not recorded yet, assume that the user didn't
302 // consent to collecting crash dumps.
303 return consent;
304 }
305
306 consent.allowed = !!allowed;
307 consent.set_by_policy = !!set_by_policy;
308 return consent; 458 return consent;
309 } 459 }
310 460
311 HRESULT DaemonControllerDelegateWin::ActivateController() {
312 if (!control_.get()) {
313 CLSID class_id;
314 HRESULT hr = CLSIDFromProgID(kDaemonController, &class_id);
315 if (FAILED(hr)) {
316 return hr;
317 }
318
319 hr = CoCreateInstance(class_id, nullptr, CLSCTX_LOCAL_SERVER,
320 IID_IDaemonControl, control_.ReceiveVoid());
321 if (FAILED(hr)) {
322 return hr;
323 }
324
325 // Ignore the error. IID_IDaemonControl2 is optional.
326 control_.QueryInterface(IID_IDaemonControl2, control2_.ReceiveVoid());
327
328 // Release |control_| upon expiration of the timeout.
329 release_timer_.reset(new base::OneShotTimer<DaemonControllerDelegateWin>());
330 release_timer_->Start(FROM_HERE,
331 base::TimeDelta::FromSeconds(kUnprivilegedTimeoutSec),
332 this,
333 &DaemonControllerDelegateWin::ReleaseController);
334 }
335
336 return S_OK;
337 }
338
339 HRESULT DaemonControllerDelegateWin::ActivateElevatedController() {
340 // The COM elevation is supported on Vista and above.
341 if (base::win::GetVersion() < base::win::VERSION_VISTA)
342 return ActivateController();
343
344 // Release an unprivileged instance of the daemon controller if any.
345 if (!control_is_elevated_)
346 ReleaseController();
347
348 if (!control_.get()) {
349 BIND_OPTS3 bind_options;
350 memset(&bind_options, 0, sizeof(bind_options));
351 bind_options.cbStruct = sizeof(bind_options);
352 bind_options.hwnd = GetTopLevelWindow(window_handle_);
353 bind_options.dwClassContext = CLSCTX_LOCAL_SERVER;
354
355 HRESULT hr = ::CoGetObject(
356 kDaemonControllerElevationMoniker,
357 &bind_options,
358 IID_IDaemonControl,
359 control_.ReceiveVoid());
360 if (FAILED(hr)) {
361 return hr;
362 }
363
364 // Ignore the error. IID_IDaemonControl2 is optional.
365 control_.QueryInterface(IID_IDaemonControl2, control2_.ReceiveVoid());
366
367 // Note that we hold a reference to an elevated instance now.
368 control_is_elevated_ = true;
369
370 // Release |control_| upon expiration of the timeout.
371 release_timer_.reset(new base::OneShotTimer<DaemonControllerDelegateWin>());
372 release_timer_->Start(FROM_HERE,
373 base::TimeDelta::FromSeconds(kPrivilegedTimeoutSec),
374 this,
375 &DaemonControllerDelegateWin::ReleaseController);
376 }
377
378 return S_OK;
379 }
380
381 void DaemonControllerDelegateWin::ReleaseController() {
382 control_.Release();
383 control2_.Release();
384 release_timer_.reset();
385 control_is_elevated_ = false;
386 }
387
388 void DaemonControllerDelegateWin::SetConfigAndStart( 461 void DaemonControllerDelegateWin::SetConfigAndStart(
389 scoped_ptr<base::DictionaryValue> config, 462 scoped_ptr<base::DictionaryValue> config,
390 bool consent, 463 bool consent,
391 const DaemonController::CompletionCallback& done) { 464 const DaemonController::CompletionCallback& done) {
392 HRESULT hr = ActivateElevatedController(); 465 // Record the user's consent.
393 if (FAILED(hr)) { 466 if (!remoting::SetUsageStatsConsent(consent)) {
394 InvokeCompletionCallback(done, hr); 467 InvokeCompletionCallback(done, false);
395 return; 468 return;
396 } 469 }
397 470
398 // Record the user's consent. 471 // Set the configuration.
399 if (control2_.get()) { 472 std::string config_str;
400 hr = control2_->SetUsageStatsConsent(consent); 473 base::JSONWriter::Write(config.release(), &config_str);
401 if (FAILED(hr)) {
402 InvokeCompletionCallback(done, hr);
403 return;
404 }
405 }
406 474
407 // Set the configuration. 475 // Determine the config directory path and create it if necessary.
408 ScopedBstr config_str(nullptr); 476 base::FilePath config_dir = remoting::GetConfigDir();
409 ConfigToString(*config, &config_str); 477 if (!base::CreateDirectory(config_dir)) {
410 if (config_str == nullptr) { 478 PLOG(ERROR) << "Failed to create the config directory.";
411 InvokeCompletionCallback(done, E_OUTOFMEMORY); 479 InvokeCompletionCallback(done, false);
412 return; 480 return;
413 } 481 }
414 482
415 hr = control_->SetOwnerWindow( 483 if (!WriteConfig(config_str)) {
416 reinterpret_cast<LONG_PTR>(GetTopLevelWindow(window_handle_))); 484 InvokeCompletionCallback(done, false);
417 if (FAILED(hr)) {
418 InvokeCompletionCallback(done, hr);
419 return;
420 }
421
422 hr = control_->SetConfig(config_str);
423 if (FAILED(hr)) {
424 InvokeCompletionCallback(done, hr);
425 return; 485 return;
426 } 486 }
427 487
428 // Start daemon. 488 // Start daemon.
429 hr = control_->StartDaemon(); 489 InvokeCompletionCallback(done, StartDaemon());
430 InvokeCompletionCallback(done, hr);
431 } 490 }
432 491
433 scoped_refptr<DaemonController> DaemonController::Create() { 492 scoped_refptr<DaemonController> DaemonController::Create() {
434 scoped_ptr<DaemonController::Delegate> delegate( 493 scoped_ptr<DaemonController::Delegate> delegate(
435 new DaemonControllerDelegateWin()); 494 new DaemonControllerDelegateWin());
436 return new DaemonController(delegate.Pass()); 495 return new DaemonController(delegate.Pass());
437 } 496 }
438 497
439 } // namespace remoting 498 } // namespace remoting
OLDNEW
« no previous file with comments | « remoting/host/setup/daemon_controller_delegate_win.h ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698