Roll third_party/android_tools to the latest: updated the support library to 19.0.1
[chromium-blink-merge.git] / remoting / host / win / elevated_controller.cc
blobd826dc9e84a6bc8db91c481dbdcb95f0abc1327f
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "remoting/host/win/elevated_controller.h"
7 #include "base/file_util.h"
8 #include "base/file_version_info.h"
9 #include "base/json/json_reader.h"
10 #include "base/json/json_writer.h"
11 #include "base/logging.h"
12 #include "base/memory/scoped_ptr.h"
13 #include "base/path_service.h"
14 #include "base/process/memory.h"
15 #include "base/strings/utf_string_conversions.h"
16 #include "base/values.h"
17 #include "base/win/scoped_handle.h"
18 #include "remoting/host/branding.h"
19 #include "remoting/host/usage_stats_consent.h"
20 #include "remoting/host/verify_config_window_win.h"
21 #include "remoting/host/win/core_resource.h"
22 #include "remoting/host/win/security_descriptor.h"
24 namespace remoting {
26 namespace {
28 // The maximum size of the configuration file. "1MB ought to be enough" for any
29 // reasonable configuration we will ever need. 1MB is low enough to make
30 // the probability of out of memory situation fairly low. OOM is still possible
31 // and we will crash if it occurs.
32 const size_t kMaxConfigFileSize = 1024 * 1024;
34 // The host configuration file name.
35 const base::FilePath::CharType kConfigFileName[] = FILE_PATH_LITERAL("host.json");
37 // The unprivileged configuration file name.
38 const base::FilePath::CharType kUnprivilegedConfigFileName[] =
39 FILE_PATH_LITERAL("host_unprivileged.json");
41 // The extension for the temporary file.
42 const base::FilePath::CharType kTempFileExtension[] = FILE_PATH_LITERAL("json~");
44 // The host configuration file security descriptor that enables full access to
45 // Local System and built-in administrators only.
46 const char kConfigFileSecurityDescriptor[] =
47 "O:BAG:BAD:(A;;GA;;;SY)(A;;GA;;;BA)";
49 const char kUnprivilegedConfigFileSecurityDescriptor[] =
50 "O:BAG:BAD:(A;;GA;;;SY)(A;;GA;;;BA)(A;;GR;;;AU)";
52 // Configuration keys.
53 const char kHostId[] = "host_id";
54 const char kXmppLogin[] = "xmpp_login";
55 const char kHostOwner[] = "host_owner";
56 const char kHostSecretHash[] = "host_secret_hash";
58 // The configuration keys that cannot be specified in UpdateConfig().
59 const char* const kReadonlyKeys[] = { kHostId, kHostOwner, kXmppLogin };
61 // The configuration keys whose values may be read by GetConfig().
62 const char* const kUnprivilegedConfigKeys[] = { kHostId, kXmppLogin };
64 // Determines if the client runs in the security context that allows performing
65 // administrative tasks (i.e. the user belongs to the adminstrators group and
66 // the client runs elevated).
67 bool IsClientAdmin() {
68 HRESULT hr = CoImpersonateClient();
69 if (FAILED(hr)) {
70 return false;
73 SID_IDENTIFIER_AUTHORITY nt_authority = SECURITY_NT_AUTHORITY;
74 PSID administrators_group = NULL;
75 BOOL result = AllocateAndInitializeSid(&nt_authority,
77 SECURITY_BUILTIN_DOMAIN_RID,
78 DOMAIN_ALIAS_RID_ADMINS,
79 0, 0, 0, 0, 0, 0,
80 &administrators_group);
81 if (result) {
82 if (!CheckTokenMembership(NULL, administrators_group, &result)) {
83 result = false;
85 FreeSid(administrators_group);
88 hr = CoRevertToSelf();
89 CHECK(SUCCEEDED(hr));
91 return !!result;
94 // Reads and parses the configuration file up to |kMaxConfigFileSize| in
95 // size.
96 HRESULT ReadConfig(const base::FilePath& filename,
97 scoped_ptr<base::DictionaryValue>* config_out) {
99 // Read raw data from the configuration file.
100 base::win::ScopedHandle file(
101 CreateFileW(filename.value().c_str(),
102 GENERIC_READ,
103 FILE_SHARE_READ | FILE_SHARE_WRITE,
104 NULL,
105 OPEN_EXISTING,
106 FILE_FLAG_SEQUENTIAL_SCAN,
107 NULL));
109 if (!file.IsValid()) {
110 DWORD error = GetLastError();
111 LOG_GETLASTERROR(ERROR)
112 << "Failed to open '" << filename.value() << "'";
113 return HRESULT_FROM_WIN32(error);
116 scoped_ptr<char[]> buffer(new char[kMaxConfigFileSize]);
117 DWORD size = kMaxConfigFileSize;
118 if (!::ReadFile(file, &buffer[0], size, &size, NULL)) {
119 DWORD error = GetLastError();
120 LOG_GETLASTERROR(ERROR)
121 << "Failed to read '" << filename.value() << "'";
122 return HRESULT_FROM_WIN32(error);
125 // Parse the JSON configuration, expecting it to contain a dictionary.
126 std::string file_content(buffer.get(), size);
127 scoped_ptr<base::Value> value(
128 base::JSONReader::Read(file_content, base::JSON_ALLOW_TRAILING_COMMAS));
130 base::DictionaryValue* dictionary;
131 if (value.get() == NULL || !value->GetAsDictionary(&dictionary)) {
132 LOG(ERROR) << "Failed to read '" << filename.value() << "'.";
133 return E_FAIL;
136 value.release();
137 config_out->reset(dictionary);
138 return S_OK;
141 base::FilePath GetTempLocationFor(const base::FilePath& filename) {
142 return filename.ReplaceExtension(kTempFileExtension);
145 // Writes a config file to a temporary location.
146 HRESULT WriteConfigFileToTemp(const base::FilePath& filename,
147 const char* security_descriptor,
148 const char* content,
149 size_t length) {
150 // Create the security descriptor for the configuration file.
151 ScopedSd sd = ConvertSddlToSd(security_descriptor);
152 if (!sd) {
153 DWORD error = GetLastError();
154 LOG_GETLASTERROR(ERROR) <<
155 "Failed to create a security descriptor for the configuration file";
156 return HRESULT_FROM_WIN32(error);
159 SECURITY_ATTRIBUTES security_attributes = {0};
160 security_attributes.nLength = sizeof(security_attributes);
161 security_attributes.lpSecurityDescriptor = sd.get();
162 security_attributes.bInheritHandle = FALSE;
164 // Create a temporary file and write configuration to it.
165 base::FilePath tempname = GetTempLocationFor(filename);
166 base::win::ScopedHandle file(
167 CreateFileW(tempname.value().c_str(),
168 GENERIC_WRITE,
170 &security_attributes,
171 CREATE_ALWAYS,
172 FILE_FLAG_SEQUENTIAL_SCAN,
173 NULL));
175 if (!file.IsValid()) {
176 DWORD error = GetLastError();
177 LOG_GETLASTERROR(ERROR)
178 << "Failed to create '" << filename.value() << "'";
179 return HRESULT_FROM_WIN32(error);
182 DWORD written;
183 if (!WriteFile(file, content, static_cast<DWORD>(length), &written, NULL)) {
184 DWORD error = GetLastError();
185 LOG_GETLASTERROR(ERROR)
186 << "Failed to write to '" << filename.value() << "'";
187 return HRESULT_FROM_WIN32(error);
190 return S_OK;
193 // Moves a config file from its temporary location to its permanent location.
194 HRESULT MoveConfigFileFromTemp(const base::FilePath& filename) {
195 // Now that the configuration is stored successfully replace the actual
196 // configuration file.
197 base::FilePath tempname = GetTempLocationFor(filename);
198 if (!MoveFileExW(tempname.value().c_str(),
199 filename.value().c_str(),
200 MOVEFILE_REPLACE_EXISTING)) {
201 DWORD error = GetLastError();
202 LOG_GETLASTERROR(ERROR)
203 << "Failed to rename '" << tempname.value() << "' to '"
204 << filename.value() << "'";
205 return HRESULT_FROM_WIN32(error);
208 return S_OK;
211 // Writes the configuration file up to |kMaxConfigFileSize| in size.
212 HRESULT WriteConfig(const char* content, size_t length, HWND owner_window) {
213 if (length > kMaxConfigFileSize) {
214 return E_FAIL;
217 // Extract the configuration data that the user will verify.
218 scoped_ptr<base::Value> config_value(base::JSONReader::Read(content));
219 if (!config_value.get()) {
220 return E_FAIL;
222 base::DictionaryValue* config_dict = NULL;
223 if (!config_value->GetAsDictionary(&config_dict)) {
224 return E_FAIL;
226 std::string email;
227 if (!config_dict->GetString(kHostOwner, &email)) {
228 if (!config_dict->GetString(kXmppLogin, &email)) {
229 return E_FAIL;
232 std::string host_id, host_secret_hash;
233 if (!config_dict->GetString(kHostId, &host_id) ||
234 !config_dict->GetString(kHostSecretHash, &host_secret_hash)) {
235 return E_FAIL;
238 // Ask the user to verify the configuration (unless the client is admin
239 // already).
240 if (!IsClientAdmin()) {
241 remoting::VerifyConfigWindowWin verify_win(email, host_id,
242 host_secret_hash);
243 DWORD error = verify_win.DoModal(owner_window);
244 if (error != ERROR_SUCCESS) {
245 return HRESULT_FROM_WIN32(error);
249 // Extract the unprivileged fields from the configuration.
250 base::DictionaryValue unprivileged_config_dict;
251 for (int i = 0; i < arraysize(kUnprivilegedConfigKeys); ++i) {
252 const char* key = kUnprivilegedConfigKeys[i];
253 base::string16 value;
254 if (config_dict->GetString(key, &value)) {
255 unprivileged_config_dict.SetString(key, value);
258 std::string unprivileged_config_str;
259 base::JSONWriter::Write(&unprivileged_config_dict, &unprivileged_config_str);
261 // Write the full configuration file to a temporary location.
262 base::FilePath full_config_file_path =
263 remoting::GetConfigDir().Append(kConfigFileName);
264 HRESULT hr = WriteConfigFileToTemp(full_config_file_path,
265 kConfigFileSecurityDescriptor,
266 content,
267 length);
268 if (FAILED(hr)) {
269 return hr;
272 // Write the unprivileged configuration file to a temporary location.
273 base::FilePath unprivileged_config_file_path =
274 remoting::GetConfigDir().Append(kUnprivilegedConfigFileName);
275 hr = WriteConfigFileToTemp(unprivileged_config_file_path,
276 kUnprivilegedConfigFileSecurityDescriptor,
277 unprivileged_config_str.data(),
278 unprivileged_config_str.size());
279 if (FAILED(hr)) {
280 return hr;
283 // Move the full configuration file to its permanent location.
284 hr = MoveConfigFileFromTemp(full_config_file_path);
285 if (FAILED(hr)) {
286 return hr;
289 // Move the unprivileged configuration file to its permanent location.
290 hr = MoveConfigFileFromTemp(unprivileged_config_file_path);
291 if (FAILED(hr)) {
292 return hr;
295 return S_OK;
298 } // namespace
300 ElevatedController::ElevatedController() : owner_window_(NULL) {
303 HRESULT ElevatedController::FinalConstruct() {
304 return S_OK;
307 void ElevatedController::FinalRelease() {
310 STDMETHODIMP ElevatedController::GetConfig(BSTR* config_out) {
311 base::FilePath config_dir = remoting::GetConfigDir();
313 // Read the unprivileged part of host configuration.
314 scoped_ptr<base::DictionaryValue> config;
315 HRESULT hr = ReadConfig(config_dir.Append(kUnprivilegedConfigFileName),
316 &config);
317 if (FAILED(hr)) {
318 return hr;
321 // Convert the config back to a string and return it to the caller.
322 std::string file_content;
323 base::JSONWriter::Write(config.get(), &file_content);
325 *config_out = ::SysAllocString(base::UTF8ToUTF16(file_content).c_str());
326 if (config_out == NULL) {
327 return E_OUTOFMEMORY;
330 return S_OK;
333 STDMETHODIMP ElevatedController::GetVersion(BSTR* version_out) {
334 // Report the product version number of the daemon controller binary as
335 // the host version.
336 HMODULE binary = base::GetModuleFromAddress(
337 reinterpret_cast<void*>(&ReadConfig));
338 scoped_ptr<FileVersionInfo> version_info(
339 FileVersionInfo::CreateFileVersionInfoForModule(binary));
341 base::string16 version;
342 if (version_info.get()) {
343 version = version_info->product_version();
346 *version_out = ::SysAllocString(version.c_str());
347 if (version_out == NULL) {
348 return E_OUTOFMEMORY;
351 return S_OK;
354 STDMETHODIMP ElevatedController::SetConfig(BSTR config) {
355 // Determine the config directory path and create it if necessary.
356 base::FilePath config_dir = remoting::GetConfigDir();
357 if (!base::CreateDirectory(config_dir)) {
358 return HRESULT_FROM_WIN32(ERROR_ACCESS_DENIED);
361 std::string file_content = base::UTF16ToUTF8(
362 base::string16(static_cast<base::char16*>(config), ::SysStringLen(config)));
364 return WriteConfig(file_content.c_str(), file_content.size(), owner_window_);
367 STDMETHODIMP ElevatedController::SetOwnerWindow(LONG_PTR window_handle) {
368 owner_window_ = reinterpret_cast<HWND>(window_handle);
369 return S_OK;
372 STDMETHODIMP ElevatedController::StartDaemon() {
373 ScopedScHandle service;
374 HRESULT hr = OpenService(&service);
375 if (FAILED(hr)) {
376 return hr;
379 // Change the service start type to 'auto'.
380 if (!::ChangeServiceConfigW(service,
381 SERVICE_NO_CHANGE,
382 SERVICE_AUTO_START,
383 SERVICE_NO_CHANGE,
384 NULL,
385 NULL,
386 NULL,
387 NULL,
388 NULL,
389 NULL,
390 NULL)) {
391 DWORD error = GetLastError();
392 LOG_GETLASTERROR(ERROR)
393 << "Failed to change the '" << kWindowsServiceName
394 << "'service start type to 'auto'";
395 return HRESULT_FROM_WIN32(error);
398 // Start the service.
399 if (!StartService(service, 0, NULL)) {
400 DWORD error = GetLastError();
401 if (error != ERROR_SERVICE_ALREADY_RUNNING) {
402 LOG_GETLASTERROR(ERROR)
403 << "Failed to start the '" << kWindowsServiceName << "'service";
405 return HRESULT_FROM_WIN32(error);
409 return S_OK;
412 STDMETHODIMP ElevatedController::StopDaemon() {
413 ScopedScHandle service;
414 HRESULT hr = OpenService(&service);
415 if (FAILED(hr)) {
416 return hr;
419 // Change the service start type to 'manual'.
420 if (!::ChangeServiceConfigW(service,
421 SERVICE_NO_CHANGE,
422 SERVICE_DEMAND_START,
423 SERVICE_NO_CHANGE,
424 NULL,
425 NULL,
426 NULL,
427 NULL,
428 NULL,
429 NULL,
430 NULL)) {
431 DWORD error = GetLastError();
432 LOG_GETLASTERROR(ERROR)
433 << "Failed to change the '" << kWindowsServiceName
434 << "'service start type to 'manual'";
435 return HRESULT_FROM_WIN32(error);
438 // Stop the service.
439 SERVICE_STATUS status;
440 if (!ControlService(service, SERVICE_CONTROL_STOP, &status)) {
441 DWORD error = GetLastError();
442 if (error != ERROR_SERVICE_NOT_ACTIVE) {
443 LOG_GETLASTERROR(ERROR)
444 << "Failed to stop the '" << kWindowsServiceName << "'service";
445 return HRESULT_FROM_WIN32(error);
449 return S_OK;
452 STDMETHODIMP ElevatedController::UpdateConfig(BSTR config) {
453 // Parse the config.
454 std::string config_str = base::UTF16ToUTF8(
455 base::string16(static_cast<base::char16*>(config), ::SysStringLen(config)));
456 scoped_ptr<base::Value> config_value(base::JSONReader::Read(config_str));
457 if (!config_value.get()) {
458 return E_FAIL;
460 base::DictionaryValue* config_dict = NULL;
461 if (!config_value->GetAsDictionary(&config_dict)) {
462 return E_FAIL;
464 // Check for bad keys.
465 for (int i = 0; i < arraysize(kReadonlyKeys); ++i) {
466 if (config_dict->HasKey(kReadonlyKeys[i])) {
467 return HRESULT_FROM_WIN32(ERROR_ACCESS_DENIED);
470 // Get the old config.
471 base::FilePath config_dir = remoting::GetConfigDir();
472 scoped_ptr<base::DictionaryValue> config_old;
473 HRESULT hr = ReadConfig(config_dir.Append(kConfigFileName), &config_old);
474 if (FAILED(hr)) {
475 return hr;
477 // Merge items from the given config into the old config.
478 config_old->MergeDictionary(config_dict);
479 // Write the updated config.
480 std::string config_updated_str;
481 base::JSONWriter::Write(config_old.get(), &config_updated_str);
482 return WriteConfig(config_updated_str.c_str(), config_updated_str.size(),
483 owner_window_);
486 STDMETHODIMP ElevatedController::GetUsageStatsConsent(BOOL* allowed,
487 BOOL* set_by_policy) {
488 bool local_allowed;
489 bool local_set_by_policy;
490 if (remoting::GetUsageStatsConsent(&local_allowed, &local_set_by_policy)) {
491 *allowed = local_allowed;
492 *set_by_policy = local_set_by_policy;
493 return S_OK;
494 } else {
495 return E_FAIL;
499 STDMETHODIMP ElevatedController::SetUsageStatsConsent(BOOL allowed) {
500 if (remoting::SetUsageStatsConsent(!!allowed)) {
501 return S_OK;
502 } else {
503 return E_FAIL;
507 HRESULT ElevatedController::OpenService(ScopedScHandle* service_out) {
508 DWORD error;
510 ScopedScHandle scmanager(
511 ::OpenSCManagerW(NULL, SERVICES_ACTIVE_DATABASE,
512 SC_MANAGER_CONNECT | SC_MANAGER_ENUMERATE_SERVICE));
513 if (!scmanager.IsValid()) {
514 error = GetLastError();
515 LOG_GETLASTERROR(ERROR)
516 << "Failed to connect to the service control manager";
518 return HRESULT_FROM_WIN32(error);
521 DWORD desired_access = SERVICE_CHANGE_CONFIG | SERVICE_QUERY_STATUS |
522 SERVICE_START | SERVICE_STOP;
523 ScopedScHandle service(
524 ::OpenServiceW(scmanager, kWindowsServiceName, desired_access));
525 if (!service.IsValid()) {
526 error = GetLastError();
527 LOG_GETLASTERROR(ERROR)
528 << "Failed to open to the '" << kWindowsServiceName << "' service";
530 return HRESULT_FROM_WIN32(error);
533 service_out->Set(service.Take());
534 return S_OK;
537 } // namespace remoting