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

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
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.
54 const char kConfigFileSecurityDescriptor[] =
55 "O:BAG:BAD:(A;;GA;;;SY)(A;;GA;;;BA)";
56
57 const char kUnprivilegedConfigFileSecurityDescriptor[] =
58 "O:BAG:BAD:(A;;GA;;;SY)(A;;GA;;;BA)(A;;GR;;;AU)";
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 HRESULT ReadConfig(const base::FilePath& filename,
Sergey Ulanov 2015/01/31 01:06:58 return bool instead of HRESULT.
74 scoped_ptr<base::DictionaryValue>* config_out) {
75
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,
81 nullptr,
82 OPEN_EXISTING,
83 FILE_FLAG_SEQUENTIAL_SCAN,
84 nullptr));
85
86 if (!file.IsValid()) {
87 DWORD error = GetLastError();
88 PLOG(ERROR) << "Failed to open '" << filename.value() << "'";
89 return HRESULT_FROM_WIN32(error);
90 }
91
92 scoped_ptr<char[]> buffer(new char[kMaxConfigFileSize]);
93 DWORD size = kMaxConfigFileSize;
94 if (!::ReadFile(file.Get(), &buffer[0], size, &size, nullptr)) {
95 DWORD error = GetLastError();
96 PLOG(ERROR) << "Failed to read '" << filename.value() << "'";
97 return HRESULT_FROM_WIN32(error);
98 }
99
100 // Parse the JSON configuration, expecting it to contain a dictionary.
101 std::string file_content(buffer.get(), size);
102 scoped_ptr<base::Value> value(
103 base::JSONReader::Read(file_content, base::JSON_ALLOW_TRAILING_COMMAS));
104
105 base::DictionaryValue* dictionary;
106 if (value.get() == nullptr || !value->GetAsDictionary(&dictionary)) {
107 LOG(ERROR) << "Failed to read '" << filename.value() << "'.";
108 return E_FAIL;
109 }
110
111 value.release();
112 config_out->reset(dictionary);
113 return S_OK;
114 }
115
116 base::FilePath GetTempLocationFor(const base::FilePath& filename) {
117 return filename.ReplaceExtension(kTempFileExtension);
118 }
119
120 // Writes a config file to a temporary location.
121 HRESULT WriteConfigFileToTemp(const base::FilePath& filename,
122 const char* security_descriptor,
123 const char* content,
124 size_t length) {
125 // Create the security descriptor for the configuration file.
126 ScopedSd sd = ConvertSddlToSd(security_descriptor);
127 if (!sd) {
128 DWORD error = GetLastError();
129 PLOG(ERROR)
130 << "Failed to create a security descriptor for the configuration file";
131 return HRESULT_FROM_WIN32(error);
132 }
133
134 SECURITY_ATTRIBUTES security_attributes = {0};
135 security_attributes.nLength = sizeof(security_attributes);
136 security_attributes.lpSecurityDescriptor = sd.get();
137 security_attributes.bInheritHandle = FALSE;
138
139 // Create a temporary file and write configuration to it.
140 base::FilePath tempname = GetTempLocationFor(filename);
141 base::win::ScopedHandle file(
142 CreateFileW(tempname.value().c_str(),
143 GENERIC_WRITE,
144 0,
145 &security_attributes,
146 CREATE_ALWAYS,
147 FILE_FLAG_SEQUENTIAL_SCAN,
148 nullptr));
149
150 if (!file.IsValid()) {
151 DWORD error = GetLastError();
152 PLOG(ERROR) << "Failed to create '" << filename.value() << "'";
153 return HRESULT_FROM_WIN32(error);
154 }
155
156 DWORD written;
157 if (!WriteFile(file.Get(), content, static_cast<DWORD>(length), &written,
158 nullptr)) {
159 DWORD error = GetLastError();
160 PLOG(ERROR) << "Failed to write to '" << filename.value() << "'";
161 return HRESULT_FROM_WIN32(error);
162 }
163
164 return S_OK;
165 }
166
167 // Moves a config file from its temporary location to its permanent location.
168 HRESULT MoveConfigFileFromTemp(const base::FilePath& filename) {
169 // Now that the configuration is stored successfully replace the actual
170 // configuration file.
171 base::FilePath tempname = GetTempLocationFor(filename);
172 if (!MoveFileExW(tempname.value().c_str(),
173 filename.value().c_str(),
174 MOVEFILE_REPLACE_EXISTING)) {
175 DWORD error = GetLastError();
176 PLOG(ERROR) << "Failed to rename '" << tempname.value() << "' to '"
177 << filename.value() << "'";
178 return HRESULT_FROM_WIN32(error);
179 }
180
181 return S_OK;
182 }
183
184 // Writes the configuration file up to |kMaxConfigFileSize| in size.
185 HRESULT WriteConfig(const char* content, size_t length) {
186 if (length > kMaxConfigFileSize) {
187 return E_FAIL;
188 }
189
190 // Extract the configuration data that the user will verify.
191 scoped_ptr<base::Value> config_value(base::JSONReader::Read(content));
192 if (!config_value.get()) {
193 return E_FAIL;
194 }
195 base::DictionaryValue* config_dict = nullptr;
196 if (!config_value->GetAsDictionary(&config_dict)) {
197 return E_FAIL;
198 }
199 std::string email;
200 if (!config_dict->GetString(kHostOwnerEmailConfigPath, &email)) {
201 if (!config_dict->GetString(kHostOwnerConfigPath, &email)) {
202 if (!config_dict->GetString(kXmppLoginConfigPath, &email)) {
203 return E_FAIL;
204 }
205 }
206 }
207 std::string host_id, host_secret_hash;
208 if (!config_dict->GetString(kHostIdConfigPath, &host_id) ||
209 !config_dict->GetString(kHostSecretHashConfigPath, &host_secret_hash)) {
210 return E_FAIL;
211 }
212
213 // Extract the unprivileged fields from the configuration.
214 base::DictionaryValue unprivileged_config_dict;
215 for (int i = 0; i < arraysize(kUnprivilegedConfigKeys); ++i) {
216 const char* key = kUnprivilegedConfigKeys[i];
217 base::string16 value;
218 if (config_dict->GetString(key, &value)) {
219 unprivileged_config_dict.SetString(key, value);
220 }
221 }
222 std::string unprivileged_config_str;
223 base::JSONWriter::Write(&unprivileged_config_dict, &unprivileged_config_str);
224
225 // Write the full configuration file to a temporary location.
226 base::FilePath full_config_file_path =
227 remoting::GetConfigDir().Append(kConfigFileName);
228 HRESULT hr = WriteConfigFileToTemp(full_config_file_path,
229 kConfigFileSecurityDescriptor,
230 content,
231 length);
232 if (FAILED(hr)) {
233 return hr;
234 }
235
236 // Write the unprivileged configuration file to a temporary location.
237 base::FilePath unprivileged_config_file_path =
238 remoting::GetConfigDir().Append(kUnprivilegedConfigFileName);
239 hr = WriteConfigFileToTemp(unprivileged_config_file_path,
240 kUnprivilegedConfigFileSecurityDescriptor,
241 unprivileged_config_str.data(),
242 unprivileged_config_str.size());
243 if (FAILED(hr)) {
244 return hr;
245 }
246
247 // Move the full configuration file to its permanent location.
248 hr = MoveConfigFileFromTemp(full_config_file_path);
249 if (FAILED(hr)) {
250 return hr;
251 }
252
253 // Move the unprivileged configuration file to its permanent location.
254 hr = MoveConfigFileFromTemp(unprivileged_config_file_path);
255 if (FAILED(hr)) {
256 return hr;
257 }
258
259 return S_OK;
260 }
56 261
57 void ConfigToString(const base::DictionaryValue& config, ScopedBstr* out) { 262 void ConfigToString(const base::DictionaryValue& config, ScopedBstr* out) {
58 std::string config_str; 263 std::string config_str;
59 base::JSONWriter::Write(&config, &config_str); 264 base::JSONWriter::Write(&config, &config_str);
60 ScopedBstr config_scoped_bstr(base::UTF8ToUTF16(config_str).c_str()); 265 ScopedBstr config_scoped_bstr(base::UTF8ToUTF16(config_str).c_str());
61 out->Swap(config_scoped_bstr); 266 out->Swap(config_scoped_bstr);
62 } 267 }
63 268
64 DaemonController::State ConvertToDaemonState(DWORD service_state) { 269 DaemonController::State ConvertToDaemonState(DWORD service_state) {
65 switch (service_state) { 270 switch (service_state) {
(...skipping 14 matching lines...) Expand all
80 case SERVICE_STOPPED: 285 case SERVICE_STOPPED:
81 return DaemonController::STATE_STOPPED; 286 return DaemonController::STATE_STOPPED;
82 break; 287 break;
83 288
84 default: 289 default:
85 NOTREACHED(); 290 NOTREACHED();
86 return DaemonController::STATE_UNKNOWN; 291 return DaemonController::STATE_UNKNOWN;
87 } 292 }
88 } 293 }
89 294
90 DWORD OpenService(ScopedScHandle* service_out) { 295 DWORD OpenService(ScopedScHandle* service_out, DWORD access) {
91 // Open the service and query its current state. 296 // Open the service and query its current state.
92 ScopedScHandle scmanager( 297 ScopedScHandle scmanager(
93 ::OpenSCManagerW(nullptr, SERVICES_ACTIVE_DATABASE, 298 ::OpenSCManagerW(nullptr, SERVICES_ACTIVE_DATABASE,
94 SC_MANAGER_CONNECT | SC_MANAGER_ENUMERATE_SERVICE)); 299 SC_MANAGER_CONNECT | SC_MANAGER_ENUMERATE_SERVICE));
95 if (!scmanager.IsValid()) { 300 if (!scmanager.IsValid()) {
96 DWORD error = GetLastError(); 301 DWORD error = GetLastError();
97 PLOG(ERROR) << "Failed to connect to the service control manager"; 302 PLOG(ERROR) << "Failed to connect to the service control manager";
98 return error; 303 return error;
99 } 304 }
100 305
101 ScopedScHandle service(::OpenServiceW(scmanager.Get(), kWindowsServiceName, 306 ScopedScHandle service(::OpenServiceW(scmanager.Get(), kWindowsServiceName,
102 SERVICE_QUERY_STATUS)); 307 access));
103 if (!service.IsValid()) { 308 if (!service.IsValid()) {
104 DWORD error = GetLastError(); 309 DWORD error = GetLastError();
105 if (error != ERROR_SERVICE_DOES_NOT_EXIST) { 310 if (error != ERROR_SERVICE_DOES_NOT_EXIST) {
106 PLOG(ERROR) << "Failed to open to the '" << kWindowsServiceName 311 PLOG(ERROR) << "Failed to open to the '" << kWindowsServiceName
107 << "' service"; 312 << "' service";
108 } 313 }
109 return error; 314 return error;
110 } 315 }
111 316
112 service_out->Set(service.Take()); 317 service_out->Set(service.Take());
(...skipping 11 matching lines...) Expand all
124 // how to handle them. 329 // how to handle them.
125 return DaemonController::RESULT_FAILED; 330 return DaemonController::RESULT_FAILED;
126 } 331 }
127 } 332 }
128 333
129 void InvokeCompletionCallback( 334 void InvokeCompletionCallback(
130 const DaemonController::CompletionCallback& done, HRESULT hr) { 335 const DaemonController::CompletionCallback& done, HRESULT hr) {
131 done.Run(HResultToAsyncResult(hr)); 336 done.Run(HResultToAsyncResult(hr));
132 } 337 }
133 338
134 HWND GetTopLevelWindow(HWND window) {
135 if (window == nullptr) {
136 return nullptr;
137 }
138
139 for (;;) {
140 LONG style = GetWindowLong(window, GWL_STYLE);
141 if ((style & WS_OVERLAPPEDWINDOW) == WS_OVERLAPPEDWINDOW ||
142 (style & WS_POPUP) == WS_POPUP) {
143 return window;
144 }
145
146 HWND parent = GetAncestor(window, GA_PARENT);
147 if (parent == nullptr) {
148 return window;
149 }
150
151 window = parent;
152 }
153 }
154
155 } // namespace 339 } // namespace
156 340
157 DaemonControllerDelegateWin::DaemonControllerDelegateWin() 341 DaemonControllerDelegateWin::DaemonControllerDelegateWin() {
158 : control_is_elevated_(false),
159 window_handle_(nullptr) {
160 } 342 }
161 343
162 DaemonControllerDelegateWin::~DaemonControllerDelegateWin() { 344 DaemonControllerDelegateWin::~DaemonControllerDelegateWin() {
163 } 345 }
164 346
165 DaemonController::State DaemonControllerDelegateWin::GetState() { 347 DaemonController::State DaemonControllerDelegateWin::GetState() {
166 if (base::win::GetVersion() < base::win::VERSION_XP) { 348 if (base::win::GetVersion() < base::win::VERSION_XP) {
167 return DaemonController::STATE_NOT_IMPLEMENTED; 349 return DaemonController::STATE_NOT_IMPLEMENTED;
168 } 350 }
169 // TODO(alexeypa): Make the thread alertable, so we can switch to APC 351 // TODO(alexeypa): Make the thread alertable, so we can switch to APC
170 // notifications rather than polling. 352 // notifications rather than polling.
171 ScopedScHandle service; 353 ScopedScHandle service;
172 DWORD error = OpenService(&service); 354 DWORD error = OpenService(&service, SERVICE_QUERY_STATUS);
173 355
174 switch (error) { 356 switch (error) {
175 case ERROR_SUCCESS: { 357 case ERROR_SUCCESS: {
176 SERVICE_STATUS status; 358 SERVICE_STATUS status;
177 if (::QueryServiceStatus(service.Get(), &status)) { 359 if (::QueryServiceStatus(service.Get(), &status)) {
178 return ConvertToDaemonState(status.dwCurrentState); 360 return ConvertToDaemonState(status.dwCurrentState);
179 } else { 361 } else {
180 PLOG(ERROR) << "Failed to query the state of the '" 362 PLOG(ERROR) << "Failed to query the state of the '"
181 << kWindowsServiceName << "' service"; 363 << kWindowsServiceName << "' service";
182 return DaemonController::STATE_UNKNOWN; 364 return DaemonController::STATE_UNKNOWN;
183 } 365 }
184 break; 366 break;
185 } 367 }
186 case ERROR_SERVICE_DOES_NOT_EXIST: 368 case ERROR_SERVICE_DOES_NOT_EXIST:
187 return DaemonController::STATE_NOT_INSTALLED; 369 return DaemonController::STATE_NOT_INSTALLED;
188 default: 370 default:
189 return DaemonController::STATE_UNKNOWN; 371 return DaemonController::STATE_UNKNOWN;
190 } 372 }
191 } 373 }
192 374
193 scoped_ptr<base::DictionaryValue> DaemonControllerDelegateWin::GetConfig() { 375 scoped_ptr<base::DictionaryValue> DaemonControllerDelegateWin::GetConfig() {
194 // Configure and start the Daemon Controller if it is installed already. 376 // Get the host configuration.
195 HRESULT hr = ActivateController(); 377 ScopedBstr host_config;
378 HRESULT hr = DoGetConfig(host_config.Receive());
196 if (FAILED(hr)) 379 if (FAILED(hr))
197 return nullptr; 380 return nullptr;
198 381
199 // Get the host configuration.
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. 382 // Parse the string into a dictionary.
206 base::string16 file_content( 383 base::string16 file_content(
207 static_cast<BSTR>(host_config), host_config.Length()); 384 static_cast<BSTR>(host_config), host_config.Length());
208 scoped_ptr<base::Value> config( 385 scoped_ptr<base::Value> config(
209 base::JSONReader::Read(base::UTF16ToUTF8(file_content), 386 base::JSONReader::Read(base::UTF16ToUTF8(file_content),
210 base::JSON_ALLOW_TRAILING_COMMAS)); 387 base::JSON_ALLOW_TRAILING_COMMAS));
211 388
212 if (!config || !config->IsType(base::Value::TYPE_DICTIONARY)) 389 if (!config || !config->IsType(base::Value::TYPE_DICTIONARY))
213 return nullptr; 390 return nullptr;
214 391
215 return make_scoped_ptr(static_cast<base::DictionaryValue*>(config.release())); 392 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();
222 if (FAILED(hr)) {
223 InvokeCompletionCallback(done, hr);
224 return;
225 }
226
227 // Update the configuration. 398 // Update the configuration.
228 ScopedBstr config_str(nullptr); 399 ScopedBstr config_str(nullptr);
229 ConfigToString(*config, &config_str); 400 ConfigToString(*config, &config_str);
230 if (config_str == nullptr) { 401 if (config_str == nullptr) {
231 InvokeCompletionCallback(done, E_OUTOFMEMORY); 402 InvokeCompletionCallback(done, E_OUTOFMEMORY);
232 return; 403 return;
233 } 404 }
234 405
235 // Make sure that the PIN confirmation dialog is focused properly. 406 HRESULT hr = DoUpdateConfig(config_str);
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); 407 InvokeCompletionCallback(done, hr);
245 } 408 }
246 409
247 void DaemonControllerDelegateWin::Stop( 410 void DaemonControllerDelegateWin::Stop(
248 const DaemonController::CompletionCallback& done) { 411 const DaemonController::CompletionCallback& done) {
249 HRESULT hr = ActivateElevatedController(); 412 HRESULT hr = StopDaemon();
250 if (SUCCEEDED(hr))
251 hr = control_->StopDaemon();
252 413
253 InvokeCompletionCallback(done, hr); 414 InvokeCompletionCallback(done, hr);
254 } 415 }
255 416
256 void DaemonControllerDelegateWin::SetWindow(void* window_handle) { 417 void DaemonControllerDelegateWin::SetWindow(void* window_handle) {
257 window_handle_ = reinterpret_cast<HWND>(window_handle);
258 } 418 }
259 419
260 std::string DaemonControllerDelegateWin::GetVersion() { 420 std::string DaemonControllerDelegateWin::GetVersion() {
261 // Configure and start the Daemon Controller if it is installed already. 421 // Get the version string.
262 HRESULT hr = ActivateController(); 422 ScopedBstr version;
423 HRESULT hr = DoGetVersion(version.Receive());
263 if (FAILED(hr)) 424 if (FAILED(hr))
264 return std::string(); 425 return std::string();
265 426
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( 427 return base::UTF16ToUTF8(
273 base::string16(static_cast<BSTR>(version), version.Length())); 428 base::string16(static_cast<BSTR>(version), version.Length()));
274 } 429 }
275 430
276 DaemonController::UsageStatsConsent 431 DaemonController::UsageStatsConsent
277 DaemonControllerDelegateWin::GetUsageStatsConsent() { 432 DaemonControllerDelegateWin::GetUsageStatsConsent() {
278 DaemonController::UsageStatsConsent consent; 433 DaemonController::UsageStatsConsent consent;
279 consent.supported = true; 434 consent.supported = true;
280 consent.allowed = false; 435 consent.allowed = false;
281 consent.set_by_policy = false; 436 consent.set_by_policy = false;
282 437
283 // Activate the Daemon Controller and see if it supports |IDaemonControl2|.
284 HRESULT hr = ActivateController();
285 if (FAILED(hr)) {
286 // The host is not installed yet. Assume that the user didn't consent to
287 // collecting crash dumps.
288 return consent;
289 }
290
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. 438 // Get the recorded user's consent.
297 BOOL allowed; 439 BOOL allowed;
298 BOOL set_by_policy; 440 BOOL set_by_policy;
299 hr = control2_->GetUsageStatsConsent(&allowed, &set_by_policy); 441 HRESULT hr = DoGetUsageStatsConsent(&allowed, &set_by_policy);
300 if (FAILED(hr)) { 442 if (FAILED(hr)) {
301 // If the user's consent is not recorded yet, assume that the user didn't 443 // If the user's consent is not recorded yet, assume that the user didn't
302 // consent to collecting crash dumps. 444 // consent to collecting crash dumps.
303 return consent; 445 return consent;
304 } 446 }
305 447
306 consent.allowed = !!allowed; 448 consent.allowed = !!allowed;
307 consent.set_by_policy = !!set_by_policy; 449 consent.set_by_policy = !!set_by_policy;
308 return consent; 450 return consent;
309 } 451 }
310 452
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( 453 void DaemonControllerDelegateWin::SetConfigAndStart(
389 scoped_ptr<base::DictionaryValue> config, 454 scoped_ptr<base::DictionaryValue> config,
390 bool consent, 455 bool consent,
391 const DaemonController::CompletionCallback& done) { 456 const DaemonController::CompletionCallback& done) {
392 HRESULT hr = ActivateElevatedController(); 457 // Record the user's consent.
458 HRESULT hr = SetUsageStatsConsent(consent);
393 if (FAILED(hr)) { 459 if (FAILED(hr)) {
394 InvokeCompletionCallback(done, hr); 460 InvokeCompletionCallback(done, hr);
395 return; 461 return;
396 } 462 }
397 463
398 // Record the user's consent.
399 if (control2_.get()) {
400 hr = control2_->SetUsageStatsConsent(consent);
401 if (FAILED(hr)) {
402 InvokeCompletionCallback(done, hr);
403 return;
404 }
405 }
406
407 // Set the configuration. 464 // Set the configuration.
408 ScopedBstr config_str(nullptr); 465 ScopedBstr config_str(nullptr);
409 ConfigToString(*config, &config_str); 466 ConfigToString(*config, &config_str);
410 if (config_str == nullptr) { 467 if (config_str == nullptr) {
411 InvokeCompletionCallback(done, E_OUTOFMEMORY); 468 InvokeCompletionCallback(done, E_OUTOFMEMORY);
412 return; 469 return;
413 } 470 }
414 471
415 hr = control_->SetOwnerWindow( 472 hr = SetConfig(config_str);
416 reinterpret_cast<LONG_PTR>(GetTopLevelWindow(window_handle_)));
417 if (FAILED(hr)) { 473 if (FAILED(hr)) {
418 InvokeCompletionCallback(done, hr); 474 InvokeCompletionCallback(done, hr);
419 return; 475 return;
420 } 476 }
421 477
422 hr = control_->SetConfig(config_str);
423 if (FAILED(hr)) {
424 InvokeCompletionCallback(done, hr);
425 return;
426 }
427
428 // Start daemon. 478 // Start daemon.
429 hr = control_->StartDaemon(); 479 hr = StartDaemon();
430 InvokeCompletionCallback(done, hr); 480 InvokeCompletionCallback(done, hr);
431 } 481 }
432 482
483 HRESULT DaemonControllerDelegateWin::DoGetConfig(BSTR* config_out) {
Sergey Ulanov 2015/01/31 01:06:58 Return bool instead of HRESULT here? The caller on
Sergey Ulanov 2015/01/31 01:06:58 You don't really need this function. Just call Rea
484 base::FilePath config_dir = remoting::GetConfigDir();
485
486 // Read the unprivileged part of host configuration.
487 scoped_ptr<base::DictionaryValue> config;
488 HRESULT hr = ReadConfig(config_dir.Append(kUnprivilegedConfigFileName),
489 &config);
490 if (FAILED(hr)) {
491 return hr;
492 }
493
494 // Convert the config back to a string and return it to the caller.
495 std::string file_content;
496 base::JSONWriter::Write(config.get(), &file_content);
497
498 *config_out = ::SysAllocString(base::UTF8ToUTF16(file_content).c_str());
499 if (config_out == nullptr) {
500 return E_OUTOFMEMORY;
501 }
502
503 return S_OK;
504 }
505
506 HRESULT DaemonControllerDelegateWin::DoGetVersion(BSTR* version_out) {
507 // Report the product version number of the daemon controller binary as
508 // the host version.
509 HMODULE binary = base::GetModuleFromAddress(
510 reinterpret_cast<void*>(&ReadConfig));
511 scoped_ptr<FileVersionInfo> version_info(
512 FileVersionInfo::CreateFileVersionInfoForModule(binary));
513
514 base::string16 version;
515 if (version_info.get()) {
516 version = version_info->product_version();
517 }
518
519 *version_out = ::SysAllocString(version.c_str());
520 if (version_out == nullptr) {
521 return E_OUTOFMEMORY;
522 }
523
524 return S_OK;
525 }
526
527 HRESULT DaemonControllerDelegateWin::SetConfig(BSTR config) {
528 // Determine the config directory path and create it if necessary.
529 base::FilePath config_dir = remoting::GetConfigDir();
530 if (!base::CreateDirectory(config_dir)) {
531 return HRESULT_FROM_WIN32(ERROR_ACCESS_DENIED);
532 }
533
534 std::string file_content = base::UTF16ToUTF8(
535 base::string16(static_cast<base::char16*>(config), ::SysStringLen(config)));
536
537 return WriteConfig(file_content.c_str(), file_content.size());
538 }
539
540 HRESULT DaemonControllerDelegateWin::StartDaemon() {
541 ScopedScHandle service;
542 DWORD access = SERVICE_CHANGE_CONFIG | SERVICE_QUERY_STATUS |
543 SERVICE_START | SERVICE_STOP;
544 HRESULT hr = OpenService(&service, access);
545 if (FAILED(hr)) {
546 return hr;
547 }
548
549 // Change the service start type to 'auto'.
550 if (!::ChangeServiceConfigW(service.Get(),
551 SERVICE_NO_CHANGE,
552 SERVICE_AUTO_START,
553 SERVICE_NO_CHANGE,
554 nullptr,
555 nullptr,
556 nullptr,
557 nullptr,
558 nullptr,
559 nullptr,
560 nullptr)) {
561 DWORD error = GetLastError();
562 PLOG(ERROR) << "Failed to change the '" << kWindowsServiceName
563 << "'service start type to 'auto'";
564 return HRESULT_FROM_WIN32(error);
565 }
566
567 // Start the service.
568 if (!StartService(service.Get(), 0, nullptr)) {
569 DWORD error = GetLastError();
570 if (error != ERROR_SERVICE_ALREADY_RUNNING) {
571 PLOG(ERROR) << "Failed to start the '" << kWindowsServiceName
572 << "'service";
573
574 return HRESULT_FROM_WIN32(error);
575 }
576 }
577
578 return S_OK;
579 }
580
581 HRESULT DaemonControllerDelegateWin::StopDaemon() {
582 ScopedScHandle service;
583 DWORD access = SERVICE_CHANGE_CONFIG | SERVICE_QUERY_STATUS |
584 SERVICE_START | SERVICE_STOP;
585 HRESULT hr = OpenService(&service, access);
586 if (FAILED(hr)) {
587 return hr;
588 }
589
590 // Change the service start type to 'manual'.
591 if (!::ChangeServiceConfigW(service.Get(),
592 SERVICE_NO_CHANGE,
593 SERVICE_DEMAND_START,
594 SERVICE_NO_CHANGE,
595 nullptr,
596 nullptr,
597 nullptr,
598 nullptr,
599 nullptr,
600 nullptr,
601 nullptr)) {
602 DWORD error = GetLastError();
603 PLOG(ERROR) << "Failed to change the '" << kWindowsServiceName
604 << "'service start type to 'manual'";
605 return HRESULT_FROM_WIN32(error);
606 }
607
608 // Stop the service.
609 SERVICE_STATUS status;
610 if (!ControlService(service.Get(), SERVICE_CONTROL_STOP, &status)) {
611 DWORD error = GetLastError();
612 if (error != ERROR_SERVICE_NOT_ACTIVE) {
613 PLOG(ERROR) << "Failed to stop the '" << kWindowsServiceName
614 << "'service";
615 return HRESULT_FROM_WIN32(error);
616 }
617 }
618
619 return S_OK;
620 }
621
622 HRESULT DaemonControllerDelegateWin::DoUpdateConfig(BSTR config) {
623 // Parse the config.
624 std::string config_str = base::UTF16ToUTF8(
625 base::string16(static_cast<base::char16*>(config), ::SysStringLen(config)));
626 scoped_ptr<base::Value> config_value(base::JSONReader::Read(config_str));
627 if (!config_value.get()) {
628 return E_FAIL;
629 }
630 base::DictionaryValue* config_dict = nullptr;
631 if (!config_value->GetAsDictionary(&config_dict)) {
632 return E_FAIL;
633 }
634 // Check for bad keys.
635 for (int i = 0; i < arraysize(kReadonlyKeys); ++i) {
636 if (config_dict->HasKey(kReadonlyKeys[i])) {
637 return HRESULT_FROM_WIN32(ERROR_ACCESS_DENIED);
638 }
639 }
640 // Get the old config.
641 base::FilePath config_dir = remoting::GetConfigDir();
642 scoped_ptr<base::DictionaryValue> config_old;
643 HRESULT hr = ReadConfig(config_dir.Append(kConfigFileName), &config_old);
644 if (FAILED(hr)) {
645 return hr;
646 }
647 // Merge items from the given config into the old config.
648 config_old->MergeDictionary(config_dict);
649 // Write the updated config.
650 std::string config_updated_str;
651 base::JSONWriter::Write(config_old.get(), &config_updated_str);
652 return WriteConfig(config_updated_str.c_str(), config_updated_str.size());
653 }
654
655 HRESULT DaemonControllerDelegateWin::DoGetUsageStatsConsent(
656 BOOL* allowed, BOOL* set_by_policy) {
657 bool local_allowed;
658 bool local_set_by_policy;
659 if (remoting::GetUsageStatsConsent(&local_allowed, &local_set_by_policy)) {
660 *allowed = local_allowed;
661 *set_by_policy = local_set_by_policy;
662 return S_OK;
663 } else {
664 return E_FAIL;
665 }
666 }
667
668 HRESULT DaemonControllerDelegateWin::SetUsageStatsConsent(BOOL allowed) {
669 if (remoting::SetUsageStatsConsent(!!allowed)) {
670 return S_OK;
671 } else {
672 return E_FAIL;
673 }
674 }
675
433 scoped_refptr<DaemonController> DaemonController::Create() { 676 scoped_refptr<DaemonController> DaemonController::Create() {
434 scoped_ptr<DaemonController::Delegate> delegate( 677 scoped_ptr<DaemonController::Delegate> delegate(
435 new DaemonControllerDelegateWin()); 678 new DaemonControllerDelegateWin());
436 return new DaemonController(delegate.Pass()); 679 return new DaemonController(delegate.Pass());
437 } 680 }
438 681
439 } // namespace remoting 682 } // namespace remoting
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698