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

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