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

Side by Side Diff: remoting/host/win/elevated_controller.cc

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

Powered by Google App Engine
This is Rietveld 408576698