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

Side by Side Diff: remoting/host/elevated_controller_win.cc

Issue 10832068: Moving Windows-only files: remoting/host -> remoting/host/win. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src/
Patch Set: Created 8 years, 4 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 | Annotate | Revision Log
« no previous file with comments | « remoting/host/elevated_controller_win.h ('k') | remoting/host/host_service.rc » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(Empty)
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.
4
5 #include "remoting/host/elevated_controller_win.h"
6
7 #include <sddl.h>
8
9 #include "base/file_util.h"
10 #include "base/file_version_info.h"
11 #include "base/logging.h"
12 #include "base/json/json_reader.h"
13 #include "base/json/json_writer.h"
14 #include "base/memory/scoped_ptr.h"
15 #include "base/path_service.h"
16 #include "base/process_util.h"
17 #include "base/utf_string_conversions.h"
18 #include "base/values.h"
19 #include "base/win/scoped_handle.h"
20 #include "remoting/host/branding.h"
21 #include "remoting/host/elevated_controller_resource.h"
22 #include "remoting/host/usage_stats_consent.h"
23 #include "remoting/host/verify_config_window_win.h"
24
25 namespace {
26
27 // The maximum size of the configuration file. "1MB ought to be enough" for any
28 // reasonable configuration we will ever need. 1MB is low enough to make
29 // the probability of out of memory situation fairly low. OOM is still possible
30 // and we will crash if it occurs.
31 const size_t kMaxConfigFileSize = 1024 * 1024;
32
33 // The host configuration file name.
34 const FilePath::CharType kConfigFileName[] = FILE_PATH_LITERAL("host.json");
35
36 // The unprivileged configuration file name.
37 const FilePath::CharType kUnprivilegedConfigFileName[] =
38 FILE_PATH_LITERAL("host_unprivileged.json");
39
40 // The extension for the temporary file.
41 const FilePath::CharType kTempFileExtension[] = FILE_PATH_LITERAL("json~");
42
43 // The host configuration file security descriptor that enables full access to
44 // Local System and built-in administrators only.
45 const wchar_t kConfigFileSecurityDescriptor[] =
46 L"O:BAG:BAD:(A;;GA;;;SY)(A;;GA;;;BA)";
47
48 const wchar_t kUnprivilegedConfigFileSecurityDescriptor[] =
49 L"O:BAG:BAD:(A;;GA;;;SY)(A;;GA;;;BA)(A;;GR;;;AU)";
50
51 // Configuration keys.
52 const char kHostId[] = "host_id";
53 const char kXmppLogin[] = "xmpp_login";
54 const char kHostSecretHash[] = "host_secret_hash";
55
56 // The configuration keys that cannot be specified in UpdateConfig().
57 const char* const kReadonlyKeys[] = { kHostId, kXmppLogin };
58
59 // The configuration keys whose values may be read by GetConfig().
60 const char* const kUnprivilegedConfigKeys[] = { kHostId, kXmppLogin };
61
62 // Determines if the client runs in the security context that allows performing
63 // administrative tasks (i.e. the user belongs to the adminstrators group and
64 // the client runs elevated).
65 bool IsClientAdmin() {
66 HRESULT hr = CoImpersonateClient();
67 if (FAILED(hr)) {
68 return false;
69 }
70
71 SID_IDENTIFIER_AUTHORITY nt_authority = SECURITY_NT_AUTHORITY;
72 PSID administrators_group = NULL;
73 BOOL result = AllocateAndInitializeSid(&nt_authority,
74 2,
75 SECURITY_BUILTIN_DOMAIN_RID,
76 DOMAIN_ALIAS_RID_ADMINS,
77 0, 0, 0, 0, 0, 0,
78 &administrators_group);
79 if (result) {
80 if (!CheckTokenMembership(NULL, administrators_group, &result)) {
81 result = false;
82 }
83 FreeSid(administrators_group);
84 }
85
86 hr = CoRevertToSelf();
87 CHECK(SUCCEEDED(hr));
88
89 return !!result;
90 }
91
92 // Reads and parses the configuration file up to |kMaxConfigFileSize| in
93 // size.
94 HRESULT ReadConfig(const FilePath& filename,
95 scoped_ptr<base::DictionaryValue>* config_out) {
96
97 // Read raw data from the configuration file.
98 base::win::ScopedHandle file(
99 CreateFileW(filename.value().c_str(),
100 GENERIC_READ,
101 FILE_SHARE_READ | FILE_SHARE_WRITE,
102 NULL,
103 OPEN_EXISTING,
104 FILE_FLAG_SEQUENTIAL_SCAN,
105 NULL));
106
107 if (!file.IsValid()) {
108 DWORD error = GetLastError();
109 LOG_GETLASTERROR(ERROR)
110 << "Failed to open '" << filename.value() << "'";
111 return HRESULT_FROM_WIN32(error);
112 }
113
114 scoped_array<char> buffer(new char[kMaxConfigFileSize]);
115 DWORD size = kMaxConfigFileSize;
116 if (!::ReadFile(file, &buffer[0], size, &size, NULL)) {
117 DWORD error = GetLastError();
118 LOG_GETLASTERROR(ERROR)
119 << "Failed to read '" << filename.value() << "'";
120 return HRESULT_FROM_WIN32(error);
121 }
122
123 // Parse the JSON configuration, expecting it to contain a dictionary.
124 std::string file_content(buffer.get(), size);
125 scoped_ptr<base::Value> value(
126 base::JSONReader::Read(file_content, base::JSON_ALLOW_TRAILING_COMMAS));
127
128 base::DictionaryValue* dictionary;
129 if (value.get() == NULL || !value->GetAsDictionary(&dictionary)) {
130 LOG(ERROR) << "Failed to read '" << filename.value() << "'.";
131 return E_FAIL;
132 }
133
134 value.release();
135 config_out->reset(dictionary);
136 return S_OK;
137 }
138
139 FilePath GetTempLocationFor(const FilePath& filename) {
140 return filename.ReplaceExtension(kTempFileExtension);
141 }
142
143 // Writes a config file to a temporary location.
144 HRESULT WriteConfigFileToTemp(const FilePath& filename,
145 const wchar_t* security_descriptor,
146 const char* content,
147 size_t length) {
148 // Create a security descriptor for the configuration file.
149 SECURITY_ATTRIBUTES security_attributes;
150 security_attributes.nLength = sizeof(security_attributes);
151 security_attributes.bInheritHandle = FALSE;
152
153 ULONG security_descriptor_length = 0;
154 if (!ConvertStringSecurityDescriptorToSecurityDescriptorW(
155 security_descriptor,
156 SDDL_REVISION_1,
157 reinterpret_cast<PSECURITY_DESCRIPTOR*>(
158 &security_attributes.lpSecurityDescriptor),
159 &security_descriptor_length)) {
160 DWORD error = GetLastError();
161 LOG_GETLASTERROR(ERROR) <<
162 "Failed to create a security descriptor for the configuration file";
163 return HRESULT_FROM_WIN32(error);
164 }
165
166 // Create a temporary file and write configuration to it.
167 FilePath tempname = GetTempLocationFor(filename);
168 base::win::ScopedHandle file(
169 CreateFileW(tempname.value().c_str(),
170 GENERIC_WRITE,
171 0,
172 &security_attributes,
173 CREATE_ALWAYS,
174 FILE_FLAG_SEQUENTIAL_SCAN,
175 NULL));
176
177 if (!file.IsValid()) {
178 DWORD error = GetLastError();
179 LOG_GETLASTERROR(ERROR)
180 << "Failed to create '" << filename.value() << "'";
181 return HRESULT_FROM_WIN32(error);
182 }
183
184 DWORD written;
185 if (!WriteFile(file, content, static_cast<DWORD>(length), &written, NULL)) {
186 DWORD error = GetLastError();
187 LOG_GETLASTERROR(ERROR)
188 << "Failed to write to '" << filename.value() << "'";
189 return HRESULT_FROM_WIN32(error);
190 }
191
192 return S_OK;
193 }
194
195 // Moves a config file from its temporary location to its permanent location.
196 HRESULT MoveConfigFileFromTemp(const FilePath& filename) {
197 // Now that the configuration is stored successfully replace the actual
198 // configuration file.
199 FilePath tempname = GetTempLocationFor(filename);
200 if (!MoveFileExW(tempname.value().c_str(),
201 filename.value().c_str(),
202 MOVEFILE_REPLACE_EXISTING)) {
203 DWORD error = GetLastError();
204 LOG_GETLASTERROR(ERROR)
205 << "Failed to rename '" << tempname.value() << "' to '"
206 << filename.value() << "'";
207 return HRESULT_FROM_WIN32(error);
208 }
209
210 return S_OK;
211 }
212
213 // Writes the configuration file up to |kMaxConfigFileSize| in size.
214 HRESULT WriteConfig(const char* content, size_t length, HWND owner_window) {
215 if (length > kMaxConfigFileSize) {
216 return E_FAIL;
217 }
218
219 // Extract the configuration data that the user will verify.
220 scoped_ptr<base::Value> config_value(base::JSONReader::Read(content));
221 if (!config_value.get()) {
222 return E_FAIL;
223 }
224 base::DictionaryValue* config_dict = NULL;
225 if (!config_value->GetAsDictionary(&config_dict)) {
226 return E_FAIL;
227 }
228 std::string email, host_id, host_secret_hash;
229 if (!config_dict->GetString(kXmppLogin, &email) ||
230 !config_dict->GetString(kHostId, &host_id) ||
231 !config_dict->GetString(kHostSecretHash, &host_secret_hash)) {
232 return E_FAIL;
233 }
234
235 // Ask the user to verify the configuration (unless the client is admin
236 // already).
237 if (!IsClientAdmin()) {
238 remoting::VerifyConfigWindowWin verify_win(email, host_id,
239 host_secret_hash);
240 if (verify_win.DoModal(owner_window) != IDOK) {
241 return E_FAIL;
242 }
243 }
244
245 // Extract the unprivileged fields from the configuration.
246 base::DictionaryValue unprivileged_config_dict;
247 for (int i = 0; i < arraysize(kUnprivilegedConfigKeys); ++i) {
248 const char* key = kUnprivilegedConfigKeys[i];
249 string16 value;
250 if (config_dict->GetString(key, &value)) {
251 unprivileged_config_dict.SetString(key, value);
252 }
253 }
254 std::string unprivileged_config_str;
255 base::JSONWriter::Write(&unprivileged_config_dict, &unprivileged_config_str);
256
257 // Write the full configuration file to a temporary location.
258 FilePath full_config_file_path =
259 remoting::GetConfigDir().Append(kConfigFileName);
260 HRESULT hr = WriteConfigFileToTemp(full_config_file_path,
261 kConfigFileSecurityDescriptor,
262 content,
263 length);
264 if (FAILED(hr)) {
265 return hr;
266 }
267
268 // Write the unprivileged configuration file to a temporary location.
269 FilePath unprivileged_config_file_path =
270 remoting::GetConfigDir().Append(kUnprivilegedConfigFileName);
271 hr = WriteConfigFileToTemp(unprivileged_config_file_path,
272 kUnprivilegedConfigFileSecurityDescriptor,
273 unprivileged_config_str.data(),
274 unprivileged_config_str.size());
275 if (FAILED(hr)) {
276 return hr;
277 }
278
279 // Move the full configuration file to its permanent location.
280 hr = MoveConfigFileFromTemp(full_config_file_path);
281 if (FAILED(hr)) {
282 return hr;
283 }
284
285 // Move the unprivileged configuration file to its permanent location.
286 hr = MoveConfigFileFromTemp(unprivileged_config_file_path);
287 if (FAILED(hr)) {
288 return hr;
289 }
290
291 return S_OK;
292 }
293
294 } // namespace
295
296 namespace remoting {
297
298 ElevatedControllerWin::ElevatedControllerWin() : owner_window_(NULL) {
299 }
300
301 HRESULT ElevatedControllerWin::FinalConstruct() {
302 return S_OK;
303 }
304
305 void ElevatedControllerWin::FinalRelease() {
306 }
307
308 STDMETHODIMP ElevatedControllerWin::GetConfig(BSTR* config_out) {
309 FilePath config_dir = remoting::GetConfigDir();
310
311 // Read the unprivileged part of host configuration.
312 scoped_ptr<base::DictionaryValue> config;
313 HRESULT hr = ReadConfig(config_dir.Append(kUnprivilegedConfigFileName),
314 &config);
315 if (FAILED(hr)) {
316 return hr;
317 }
318
319 // Convert the config back to a string and return it to the caller.
320 std::string file_content;
321 base::JSONWriter::Write(config.get(), &file_content);
322
323 *config_out = ::SysAllocString(UTF8ToUTF16(file_content).c_str());
324 if (config_out == NULL) {
325 return E_OUTOFMEMORY;
326 }
327
328 return S_OK;
329 }
330
331 STDMETHODIMP ElevatedControllerWin::GetVersion(BSTR* version_out) {
332 // Report the product version number of the daemon controller binary as
333 // the host version.
334 HMODULE binary = base::GetModuleFromAddress(
335 reinterpret_cast<void*>(&ReadConfig));
336 scoped_ptr<FileVersionInfo> version_info(
337 FileVersionInfo::CreateFileVersionInfoForModule(binary));
338
339 string16 version;
340 if (version_info.get()) {
341 version = version_info->product_version();
342 }
343
344 *version_out = ::SysAllocString(version.c_str());
345 if (version_out == NULL) {
346 return E_OUTOFMEMORY;
347 }
348
349 return S_OK;
350 }
351
352 STDMETHODIMP ElevatedControllerWin::SetConfig(BSTR config) {
353 // Determine the config directory path and create it if necessary.
354 FilePath config_dir = remoting::GetConfigDir();
355 if (!file_util::CreateDirectory(config_dir)) {
356 return HRESULT_FROM_WIN32(ERROR_ACCESS_DENIED);
357 }
358
359 std::string file_content = UTF16ToUTF8(
360 string16(static_cast<char16*>(config), ::SysStringLen(config)));
361
362 return WriteConfig(file_content.c_str(), file_content.size(), owner_window_);
363 }
364
365 STDMETHODIMP ElevatedControllerWin::SetOwnerWindow(LONG_PTR window_handle) {
366 owner_window_ = reinterpret_cast<HWND>(window_handle);
367 return S_OK;
368 }
369
370 STDMETHODIMP ElevatedControllerWin::StartDaemon() {
371 ScopedScHandle service;
372 HRESULT hr = OpenService(&service);
373 if (FAILED(hr)) {
374 return hr;
375 }
376
377 // Change the service start type to 'auto'.
378 if (!::ChangeServiceConfigW(service,
379 SERVICE_NO_CHANGE,
380 SERVICE_AUTO_START,
381 SERVICE_NO_CHANGE,
382 NULL,
383 NULL,
384 NULL,
385 NULL,
386 NULL,
387 NULL,
388 NULL)) {
389 DWORD error = GetLastError();
390 LOG_GETLASTERROR(ERROR)
391 << "Failed to change the '" << kWindowsServiceName
392 << "'service start type to 'auto'";
393 return HRESULT_FROM_WIN32(error);
394 }
395
396 // Start the service.
397 if (!StartService(service, 0, NULL)) {
398 DWORD error = GetLastError();
399 if (error != ERROR_SERVICE_ALREADY_RUNNING) {
400 LOG_GETLASTERROR(ERROR)
401 << "Failed to start the '" << kWindowsServiceName << "'service";
402
403 return HRESULT_FROM_WIN32(error);
404 }
405 }
406
407 return S_OK;
408 }
409
410 STDMETHODIMP ElevatedControllerWin::StopDaemon() {
411 ScopedScHandle service;
412 HRESULT hr = OpenService(&service);
413 if (FAILED(hr)) {
414 return hr;
415 }
416
417 // Change the service start type to 'manual'.
418 if (!::ChangeServiceConfigW(service,
419 SERVICE_NO_CHANGE,
420 SERVICE_DEMAND_START,
421 SERVICE_NO_CHANGE,
422 NULL,
423 NULL,
424 NULL,
425 NULL,
426 NULL,
427 NULL,
428 NULL)) {
429 DWORD error = GetLastError();
430 LOG_GETLASTERROR(ERROR)
431 << "Failed to change the '" << kWindowsServiceName
432 << "'service start type to 'manual'";
433 return HRESULT_FROM_WIN32(error);
434 }
435
436 // Stop the service.
437 SERVICE_STATUS status;
438 if (!ControlService(service, SERVICE_CONTROL_STOP, &status)) {
439 DWORD error = GetLastError();
440 if (error != ERROR_SERVICE_NOT_ACTIVE) {
441 LOG_GETLASTERROR(ERROR)
442 << "Failed to stop the '" << kWindowsServiceName << "'service";
443 return HRESULT_FROM_WIN32(error);
444 }
445 }
446
447 return S_OK;
448 }
449
450 STDMETHODIMP ElevatedControllerWin::UpdateConfig(BSTR config) {
451 // Parse the config.
452 std::string config_str = UTF16ToUTF8(
453 string16(static_cast<char16*>(config), ::SysStringLen(config)));
454 scoped_ptr<base::Value> config_value(base::JSONReader::Read(config_str));
455 if (!config_value.get()) {
456 return E_FAIL;
457 }
458 base::DictionaryValue* config_dict = NULL;
459 if (!config_value->GetAsDictionary(&config_dict)) {
460 return E_FAIL;
461 }
462 // Check for bad keys.
463 for (int i = 0; i < arraysize(kReadonlyKeys); ++i) {
464 if (config_dict->HasKey(kReadonlyKeys[i])) {
465 return HRESULT_FROM_WIN32(ERROR_ACCESS_DENIED);
466 }
467 }
468 // Get the old config.
469 FilePath config_dir = remoting::GetConfigDir();
470 scoped_ptr<base::DictionaryValue> config_old;
471 HRESULT hr = ReadConfig(config_dir.Append(kConfigFileName), &config_old);
472 if (FAILED(hr)) {
473 return hr;
474 }
475 // Merge items from the given config into the old config.
476 config_old->MergeDictionary(config_dict);
477 // Write the updated config.
478 std::string config_updated_str;
479 base::JSONWriter::Write(config_old.get(), &config_updated_str);
480 return WriteConfig(config_updated_str.c_str(), config_updated_str.size(),
481 owner_window_);
482 }
483
484 STDMETHODIMP ElevatedControllerWin::GetUsageStatsConsent(BOOL* allowed,
485 BOOL* set_by_policy) {
486 bool local_allowed;
487 bool local_set_by_policy;
488 if (remoting::GetUsageStatsConsent(&local_allowed, &local_set_by_policy)) {
489 *allowed = local_allowed;
490 *set_by_policy = local_set_by_policy;
491 return S_OK;
492 } else {
493 return E_FAIL;
494 }
495 }
496
497 STDMETHODIMP ElevatedControllerWin::SetUsageStatsConsent(BOOL allowed) {
498 if (remoting::SetUsageStatsConsent(!!allowed)) {
499 return S_OK;
500 } else {
501 return E_FAIL;
502 }
503 }
504
505 HRESULT ElevatedControllerWin::OpenService(ScopedScHandle* service_out) {
506 DWORD error;
507
508 ScopedScHandle scmanager(
509 ::OpenSCManagerW(NULL, SERVICES_ACTIVE_DATABASE,
510 SC_MANAGER_CONNECT | SC_MANAGER_ENUMERATE_SERVICE));
511 if (!scmanager.IsValid()) {
512 error = GetLastError();
513 LOG_GETLASTERROR(ERROR)
514 << "Failed to connect to the service control manager";
515
516 return HRESULT_FROM_WIN32(error);
517 }
518
519 DWORD desired_access = SERVICE_CHANGE_CONFIG | SERVICE_QUERY_STATUS |
520 SERVICE_START | SERVICE_STOP;
521 ScopedScHandle service(
522 ::OpenServiceW(scmanager, kWindowsServiceName, desired_access));
523 if (!service.IsValid()) {
524 error = GetLastError();
525 LOG_GETLASTERROR(ERROR)
526 << "Failed to open to the '" << kWindowsServiceName << "' service";
527
528 return HRESULT_FROM_WIN32(error);
529 }
530
531 service_out->Set(service.Take());
532 return S_OK;
533 }
534
535 } // namespace remoting
OLDNEW
« no previous file with comments | « remoting/host/elevated_controller_win.h ('k') | remoting/host/host_service.rc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698