OLD | NEW |
---|---|
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 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 | 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/elevated_controller_win.h" | 5 #include "remoting/host/elevated_controller_win.h" |
6 | 6 |
7 #include <sddl.h> | 7 #include <sddl.h> |
8 | 8 |
9 #include "base/file_util.h" | 9 #include "base/file_util.h" |
10 #include "base/logging.h" | 10 #include "base/logging.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/memory/scoped_ptr.h" | 13 #include "base/memory/scoped_ptr.h" |
14 #include "base/path_service.h" | 14 #include "base/path_service.h" |
15 #include "base/stringize_macros.h" | 15 #include "base/stringize_macros.h" |
16 #include "base/utf_string_conversions.h" | 16 #include "base/utf_string_conversions.h" |
17 #include "base/values.h" | 17 #include "base/values.h" |
18 #include "base/win/scoped_handle.h" | 18 #include "base/win/scoped_handle.h" |
19 #include "remoting/host/branding.h" | 19 #include "remoting/host/branding.h" |
20 #include "remoting/host/daemon_controller_common_win.h" | |
20 #include "remoting/host/elevated_controller_resource.h" | 21 #include "remoting/host/elevated_controller_resource.h" |
21 #include "remoting/host/verify_config_window_win.h" | 22 #include "remoting/host/verify_config_window_win.h" |
22 | 23 |
23 namespace { | 24 namespace { |
24 | 25 |
25 // The host configuration file name. | 26 // The host configuration file name. |
26 const FilePath::CharType kConfigFileName[] = FILE_PATH_LITERAL("host.json"); | 27 const FilePath::CharType kConfigFileName[] = FILE_PATH_LITERAL("host.json"); |
27 | 28 |
28 // The extension for the temporary file. | 29 // The extension for the temporary file. |
29 const FilePath::CharType kTempFileExtension[] = FILE_PATH_LITERAL("json~"); | 30 const FilePath::CharType kTempFileExtension[] = FILE_PATH_LITERAL("json~"); |
30 | 31 |
31 // The host configuration file security descriptor that enables full access to | 32 // The host configuration file security descriptor that enables full access to |
32 // Local System and built-in administrators only. | 33 // Local System and built-in administrators only. |
33 const char16 kConfigFileSecurityDescriptor[] = | 34 const char16 kConfigFileSecurityDescriptor[] = |
34 TO_L_STRING("O:BAG:BAD:(A;;GA;;;SY)(A;;GA;;;BA)"); | 35 TO_L_STRING("O:BAG:BAD:(A;;GA;;;SY)(A;;GA;;;BA)"); |
35 | 36 |
36 // The maximum size of the configuration file. "1MB ought to be enough" for any | 37 const char16 kUnprivilegedConfigFileSecurityDescriptor[] = |
37 // reasonable configuration we will ever need. 1MB is low enough to make | 38 TO_L_STRING("O:BAG:BAD:(A;;GA;;;SY)(A;;GA;;;BA)(A;;GR;;;AU)"); |
38 // the probability of out of memory situation fairly low. OOM is still possible | |
39 // and we will crash if it occurs. | |
40 const size_t kMaxConfigFileSize = 1024 * 1024; | |
41 | 39 |
42 // ReadConfig() filters the configuration file stripping all variables except of | 40 // Configuration keys. |
43 // the following two. | |
44 const char kHostId[] = "host_id"; | 41 const char kHostId[] = "host_id"; |
45 const char kXmppLogin[] = "xmpp_login"; | 42 const char kXmppLogin[] = "xmpp_login"; |
43 const char kHostSecretHash[] = "host_secret_hash"; | |
46 | 44 |
47 // The configuration keys that cannot be specified in UpdateConfig(). | 45 // The configuration keys that cannot be specified in UpdateConfig(). |
48 const char* const kReadonlyKeys[] = { kHostId, kXmppLogin }; | 46 const char* const kReadonlyKeys[] = { kHostId, kXmppLogin }; |
49 | 47 |
48 // The configuration keys whose values may be read by GetConfig(). | |
49 const char* const kUnprivilegedConfigKeys[] = { kHostId, kXmppLogin }; | |
50 | |
51 // TODO(simonmorris): Remove this routine: the plugin implements GetConfig(), | |
52 // so it's no longer used. | |
50 // Reads and parses the configuration file up to |kMaxConfigFileSize| in | 53 // Reads and parses the configuration file up to |kMaxConfigFileSize| in |
51 // size. | 54 // size. |
52 HRESULT ReadConfig(const FilePath& filename, | 55 HRESULT ReadConfig(const FilePath& filename, |
53 scoped_ptr<base::DictionaryValue>* config_out) { | 56 scoped_ptr<base::DictionaryValue>* config_out) { |
54 | 57 |
55 // Read raw data from the configuration file. | 58 // Read raw data from the configuration file. |
56 base::win::ScopedHandle file( | 59 base::win::ScopedHandle file( |
57 CreateFileW(filename.value().c_str(), | 60 CreateFileW(filename.value().c_str(), |
58 GENERIC_READ, | 61 GENERIC_READ, |
59 FILE_SHARE_READ | FILE_SHARE_WRITE, | 62 FILE_SHARE_READ | FILE_SHARE_WRITE, |
60 NULL, | 63 NULL, |
61 OPEN_EXISTING, | 64 OPEN_EXISTING, |
62 FILE_FLAG_SEQUENTIAL_SCAN, | 65 FILE_FLAG_SEQUENTIAL_SCAN, |
63 NULL)); | 66 NULL)); |
64 | 67 |
65 if (!file.IsValid()) { | 68 if (!file.IsValid()) { |
66 DWORD error = GetLastError(); | 69 DWORD error = GetLastError(); |
67 LOG_GETLASTERROR(ERROR) | 70 LOG_GETLASTERROR(ERROR) |
68 << "Failed to open '" << filename.value() << "'"; | 71 << "Failed to open '" << filename.value() << "'"; |
69 return HRESULT_FROM_WIN32(error); | 72 return HRESULT_FROM_WIN32(error); |
70 } | 73 } |
71 | 74 |
72 scoped_array<char> buffer(new char[kMaxConfigFileSize]); | 75 scoped_array<char> buffer(new char[remoting::kMaxConfigFileSize]); |
73 DWORD size = kMaxConfigFileSize; | 76 DWORD size = remoting::kMaxConfigFileSize; |
74 if (!::ReadFile(file, &buffer[0], size, &size, NULL)) { | 77 if (!::ReadFile(file, &buffer[0], size, &size, NULL)) { |
75 DWORD error = GetLastError(); | 78 DWORD error = GetLastError(); |
76 LOG_GETLASTERROR(ERROR) | 79 LOG_GETLASTERROR(ERROR) |
77 << "Failed to read '" << filename.value() << "'"; | 80 << "Failed to read '" << filename.value() << "'"; |
78 return HRESULT_FROM_WIN32(error); | 81 return HRESULT_FROM_WIN32(error); |
79 } | 82 } |
80 | 83 |
81 // Parse the JSON configuration, expecting it to contain a dictionary. | 84 // Parse the JSON configuration, expecting it to contain a dictionary. |
82 std::string file_content(buffer.get(), size); | 85 std::string file_content(buffer.get(), size); |
83 scoped_ptr<base::Value> value( | 86 scoped_ptr<base::Value> value( |
84 base::JSONReader::Read(file_content, base::JSON_ALLOW_TRAILING_COMMAS)); | 87 base::JSONReader::Read(file_content, base::JSON_ALLOW_TRAILING_COMMAS)); |
85 | 88 |
86 base::DictionaryValue* dictionary; | 89 base::DictionaryValue* dictionary; |
87 if (value.get() == NULL || !value->GetAsDictionary(&dictionary)) { | 90 if (value.get() == NULL || !value->GetAsDictionary(&dictionary)) { |
88 LOG(ERROR) << "Failed to read '" << filename.value() << "'."; | 91 LOG(ERROR) << "Failed to read '" << filename.value() << "'."; |
89 return E_FAIL; | 92 return E_FAIL; |
90 } | 93 } |
91 | 94 |
92 value.release(); | 95 value.release(); |
93 config_out->reset(dictionary); | 96 config_out->reset(dictionary); |
94 return S_OK; | 97 return S_OK; |
95 } | 98 } |
96 | 99 |
97 // Writes the configuration file up to |kMaxConfigFileSize| in size. | 100 FilePath GetTempLocationFor(const FilePath& filename) { |
98 HRESULT WriteConfig(const FilePath& filename, | 101 return filename.ReplaceExtension(kTempFileExtension); |
99 const char* content, | 102 } |
100 size_t length) { | |
101 if (length > kMaxConfigFileSize) { | |
102 return E_FAIL; | |
103 } | |
104 | 103 |
105 // Extract the configuration data that the user will verify. | 104 // Writes a config file to a temporary location. |
106 scoped_ptr<base::Value> config_value(base::JSONReader::Read(content)); | 105 HRESULT WriteConfigFileToTemp(const FilePath& filename, |
107 if (!config_value.get()) { | 106 const char16* security_descriptor, |
108 return E_FAIL; | 107 const char* content, |
109 } | 108 size_t length) { |
110 base::DictionaryValue* config_dict = NULL; | |
111 if (!config_value->GetAsDictionary(&config_dict)) { | |
112 return E_FAIL; | |
113 } | |
114 std::string email, host_id, host_secret_hash; | |
115 if (!config_dict->GetString("xmpp_login", &email) || | |
116 !config_dict->GetString("host_id", &host_id) || | |
117 !config_dict->GetString("host_secret_hash", &host_secret_hash)) { | |
118 return E_FAIL; | |
119 } | |
120 | |
121 // Ask the user to verify the configuration. | |
122 remoting::VerifyConfigWindowWin verify_win(email, host_id, host_secret_hash); | |
123 if (!verify_win.Run()) { | |
124 return E_FAIL; | |
125 } | |
126 | |
127 // Create a security descriptor for the configuration file. | 109 // Create a security descriptor for the configuration file. |
128 SECURITY_ATTRIBUTES security_attributes; | 110 SECURITY_ATTRIBUTES security_attributes; |
129 security_attributes.nLength = sizeof(security_attributes); | 111 security_attributes.nLength = sizeof(security_attributes); |
130 security_attributes.bInheritHandle = FALSE; | 112 security_attributes.bInheritHandle = FALSE; |
131 | 113 |
132 ULONG security_descriptor_length = 0; | 114 ULONG security_descriptor_length = 0; |
133 if (!ConvertStringSecurityDescriptorToSecurityDescriptorW( | 115 if (!ConvertStringSecurityDescriptorToSecurityDescriptorW( |
134 kConfigFileSecurityDescriptor, | 116 security_descriptor, |
135 SDDL_REVISION_1, | 117 SDDL_REVISION_1, |
136 reinterpret_cast<PSECURITY_DESCRIPTOR*>( | 118 reinterpret_cast<PSECURITY_DESCRIPTOR*>( |
137 &security_attributes.lpSecurityDescriptor), | 119 &security_attributes.lpSecurityDescriptor), |
138 &security_descriptor_length)) { | 120 &security_descriptor_length)) { |
139 DWORD error = GetLastError(); | 121 DWORD error = GetLastError(); |
140 LOG_GETLASTERROR(ERROR) << | 122 LOG_GETLASTERROR(ERROR) << |
141 "Failed to create a security descriptor for the configuration file"; | 123 "Failed to create a security descriptor for the configuration file"; |
142 return HRESULT_FROM_WIN32(error); | 124 return HRESULT_FROM_WIN32(error); |
143 } | 125 } |
144 | 126 |
145 // Create a temporary file and write configuration to it. | 127 // Create a temporary file and write configuration to it. |
146 FilePath tempname = filename.ReplaceExtension(kTempFileExtension); | 128 FilePath tempname = GetTempLocationFor(filename); |
147 { | 129 { |
alexeypa (please no reviews)
2012/04/25 16:36:56
nit: This scope is not needed any more. It was nee
simonmorris
2012/04/25 17:08:44
Done.
| |
148 base::win::ScopedHandle file( | 130 base::win::ScopedHandle file( |
149 CreateFileW(tempname.value().c_str(), | 131 CreateFileW(tempname.value().c_str(), |
150 GENERIC_WRITE, | 132 GENERIC_WRITE, |
151 0, | 133 0, |
152 &security_attributes, | 134 &security_attributes, |
153 CREATE_ALWAYS, | 135 CREATE_ALWAYS, |
154 FILE_FLAG_SEQUENTIAL_SCAN, | 136 FILE_FLAG_SEQUENTIAL_SCAN, |
155 NULL)); | 137 NULL)); |
156 | 138 |
157 if (!file.IsValid()) { | 139 if (!file.IsValid()) { |
158 DWORD error = GetLastError(); | 140 DWORD error = GetLastError(); |
159 LOG_GETLASTERROR(ERROR) | 141 LOG_GETLASTERROR(ERROR) |
160 << "Failed to create '" << filename.value() << "'"; | 142 << "Failed to create '" << filename.value() << "'"; |
161 return HRESULT_FROM_WIN32(error); | 143 return HRESULT_FROM_WIN32(error); |
162 } | 144 } |
163 | 145 |
164 DWORD written; | 146 DWORD written; |
165 if (!WriteFile(file, content, static_cast<DWORD>(length), &written, NULL)) { | 147 if (!WriteFile(file, content, static_cast<DWORD>(length), &written, NULL)) { |
166 DWORD error = GetLastError(); | 148 DWORD error = GetLastError(); |
167 LOG_GETLASTERROR(ERROR) | 149 LOG_GETLASTERROR(ERROR) |
168 << "Failed to write to '" << filename.value() << "'"; | 150 << "Failed to write to '" << filename.value() << "'"; |
169 return HRESULT_FROM_WIN32(error); | 151 return HRESULT_FROM_WIN32(error); |
170 } | 152 } |
171 } | 153 } |
172 | 154 |
155 return S_OK; | |
156 } | |
157 | |
158 // Moves a config file from its temporary location to its permanent location. | |
159 HRESULT MoveConfigFileFromTemp(const FilePath& filename) { | |
173 // Now that the configuration is stored successfully replace the actual | 160 // Now that the configuration is stored successfully replace the actual |
174 // configuration file. | 161 // configuration file. |
162 FilePath tempname = GetTempLocationFor(filename); | |
175 if (!MoveFileExW(tempname.value().c_str(), | 163 if (!MoveFileExW(tempname.value().c_str(), |
176 filename.value().c_str(), | 164 filename.value().c_str(), |
177 MOVEFILE_REPLACE_EXISTING)) { | 165 MOVEFILE_REPLACE_EXISTING)) { |
178 DWORD error = GetLastError(); | 166 DWORD error = GetLastError(); |
179 LOG_GETLASTERROR(ERROR) | 167 LOG_GETLASTERROR(ERROR) |
180 << "Failed to rename '" << tempname.value() << "' to '" | 168 << "Failed to rename '" << tempname.value() << "' to '" |
181 << filename.value() << "'"; | 169 << filename.value() << "'"; |
182 return HRESULT_FROM_WIN32(error); | 170 return HRESULT_FROM_WIN32(error); |
183 } | 171 } |
184 | 172 |
185 return S_OK; | 173 return S_OK; |
186 } | 174 } |
187 | 175 |
176 // Writes the configuration file up to |kMaxConfigFileSize| in size. | |
177 HRESULT WriteConfig(const char* content, size_t length) { | |
178 if (length > remoting::kMaxConfigFileSize) { | |
179 return E_FAIL; | |
180 } | |
181 | |
182 // Extract the configuration data that the user will verify. | |
183 scoped_ptr<base::Value> config_value(base::JSONReader::Read(content)); | |
184 if (!config_value.get()) { | |
185 return E_FAIL; | |
186 } | |
187 base::DictionaryValue* config_dict = NULL; | |
188 if (!config_value->GetAsDictionary(&config_dict)) { | |
189 return E_FAIL; | |
190 } | |
191 std::string email, host_id, host_secret_hash; | |
192 if (!config_dict->GetString(kXmppLogin, &email) || | |
193 !config_dict->GetString(kHostId, &host_id) || | |
194 !config_dict->GetString(kHostSecretHash, &host_secret_hash)) { | |
195 return E_FAIL; | |
196 } | |
197 | |
198 // Ask the user to verify the configuration. | |
199 remoting::VerifyConfigWindowWin verify_win(email, host_id, host_secret_hash); | |
200 if (!verify_win.Run()) { | |
201 return E_FAIL; | |
202 } | |
203 | |
204 // Extract the unprivileged fields from the configuration. | |
205 base::DictionaryValue unprivileged_config_dict; | |
206 for (int i = 0; i < arraysize(kUnprivilegedConfigKeys); ++i) { | |
207 const char* key = kUnprivilegedConfigKeys[i]; | |
208 string16 value; | |
209 if (config_dict->GetString(key, &value)) { | |
210 unprivileged_config_dict.SetString(key, value); | |
211 } | |
212 } | |
213 std::string unprivileged_config_str; | |
214 base::JSONWriter::Write(&unprivileged_config_dict, &unprivileged_config_str); | |
215 | |
216 // Write the full configuration file to a temporary location. | |
217 FilePath full_config_file_path = | |
218 remoting::GetConfigDir().Append(kConfigFileName); | |
219 HRESULT hr = WriteConfigFileToTemp(full_config_file_path, | |
220 kConfigFileSecurityDescriptor, | |
221 content, | |
222 length); | |
223 if (FAILED(hr)) { | |
224 return hr; | |
225 } | |
226 | |
227 // Write the unprivileged configuration file to a temporary location. | |
228 FilePath unprivileged_config_file_path = | |
229 remoting::GetConfigDir().Append(remoting::kUnprivilegedConfigFileName); | |
230 hr = WriteConfigFileToTemp(unprivileged_config_file_path, | |
231 kUnprivilegedConfigFileSecurityDescriptor, | |
232 unprivileged_config_str.data(), | |
233 unprivileged_config_str.size()); | |
234 if (FAILED(hr)) { | |
235 return hr; | |
236 } | |
237 | |
238 // Move the full configuration file to its permanent location. | |
239 hr = MoveConfigFileFromTemp(full_config_file_path); | |
240 if (FAILED(hr)) { | |
241 return hr; | |
242 } | |
243 | |
244 // Move the unprivileged configuration file to its permanent location. | |
245 hr = MoveConfigFileFromTemp(unprivileged_config_file_path); | |
246 if (FAILED(hr)) { | |
247 return hr; | |
248 } | |
249 | |
250 return S_OK; | |
251 } | |
188 | 252 |
189 } // namespace | 253 } // namespace |
190 | 254 |
191 namespace remoting { | 255 namespace remoting { |
192 | 256 |
193 ElevatedControllerWin::ElevatedControllerWin() { | 257 ElevatedControllerWin::ElevatedControllerWin() { |
194 } | 258 } |
195 | 259 |
196 HRESULT ElevatedControllerWin::FinalConstruct() { | 260 HRESULT ElevatedControllerWin::FinalConstruct() { |
197 return S_OK; | 261 return S_OK; |
(...skipping 39 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
237 STDMETHODIMP ElevatedControllerWin::SetConfig(BSTR config) { | 301 STDMETHODIMP ElevatedControllerWin::SetConfig(BSTR config) { |
238 // Determine the config directory path and create it if necessary. | 302 // Determine the config directory path and create it if necessary. |
239 FilePath config_dir = remoting::GetConfigDir(); | 303 FilePath config_dir = remoting::GetConfigDir(); |
240 if (!file_util::CreateDirectory(config_dir)) { | 304 if (!file_util::CreateDirectory(config_dir)) { |
241 return HRESULT_FROM_WIN32(ERROR_ACCESS_DENIED); | 305 return HRESULT_FROM_WIN32(ERROR_ACCESS_DENIED); |
242 } | 306 } |
243 | 307 |
244 std::string file_content = UTF16ToUTF8( | 308 std::string file_content = UTF16ToUTF8( |
245 string16(static_cast<char16*>(config), ::SysStringLen(config))); | 309 string16(static_cast<char16*>(config), ::SysStringLen(config))); |
246 | 310 |
247 return WriteConfig(config_dir.Append(kConfigFileName), | 311 return WriteConfig(file_content.c_str(), file_content.size()); |
248 file_content.c_str(), | |
249 file_content.size()); | |
250 } | 312 } |
251 | 313 |
252 STDMETHODIMP ElevatedControllerWin::StartDaemon() { | 314 STDMETHODIMP ElevatedControllerWin::StartDaemon() { |
253 ScopedScHandle service; | 315 ScopedScHandle service; |
254 HRESULT hr = OpenService(&service); | 316 HRESULT hr = OpenService(&service); |
255 if (FAILED(hr)) { | 317 if (FAILED(hr)) { |
256 return hr; | 318 return hr; |
257 } | 319 } |
258 | 320 |
259 // Change the service start type to 'auto'. | 321 // Change the service start type to 'auto'. |
(...skipping 92 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
352 scoped_ptr<base::DictionaryValue> config_old; | 414 scoped_ptr<base::DictionaryValue> config_old; |
353 HRESULT hr = ReadConfig(config_dir.Append(kConfigFileName), &config_old); | 415 HRESULT hr = ReadConfig(config_dir.Append(kConfigFileName), &config_old); |
354 if (FAILED(hr)) { | 416 if (FAILED(hr)) { |
355 return hr; | 417 return hr; |
356 } | 418 } |
357 // Merge items from the given config into the old config. | 419 // Merge items from the given config into the old config. |
358 config_old->MergeDictionary(config_dict); | 420 config_old->MergeDictionary(config_dict); |
359 // Write the updated config. | 421 // Write the updated config. |
360 std::string config_updated_str; | 422 std::string config_updated_str; |
361 base::JSONWriter::Write(config_old.get(), &config_updated_str); | 423 base::JSONWriter::Write(config_old.get(), &config_updated_str); |
362 return WriteConfig(config_dir.Append(kConfigFileName), | 424 return WriteConfig(config_updated_str.c_str(), config_updated_str.size()); |
363 config_updated_str.c_str(), | |
364 config_updated_str.size()); | |
365 } | 425 } |
366 | 426 |
367 HRESULT ElevatedControllerWin::OpenService(ScopedScHandle* service_out) { | 427 HRESULT ElevatedControllerWin::OpenService(ScopedScHandle* service_out) { |
368 DWORD error; | 428 DWORD error; |
369 | 429 |
370 ScopedScHandle scmanager( | 430 ScopedScHandle scmanager( |
371 ::OpenSCManagerW(NULL, SERVICES_ACTIVE_DATABASE, | 431 ::OpenSCManagerW(NULL, SERVICES_ACTIVE_DATABASE, |
372 SC_MANAGER_CONNECT | SC_MANAGER_ENUMERATE_SERVICE)); | 432 SC_MANAGER_CONNECT | SC_MANAGER_ENUMERATE_SERVICE)); |
373 if (!scmanager.IsValid()) { | 433 if (!scmanager.IsValid()) { |
374 error = GetLastError(); | 434 error = GetLastError(); |
(...skipping 13 matching lines...) Expand all Loading... | |
388 << "Failed to open to the '" << kWindowsServiceName << "' service"; | 448 << "Failed to open to the '" << kWindowsServiceName << "' service"; |
389 | 449 |
390 return HRESULT_FROM_WIN32(error); | 450 return HRESULT_FROM_WIN32(error); |
391 } | 451 } |
392 | 452 |
393 service_out->Set(service.Take()); | 453 service_out->Set(service.Take()); |
394 return S_OK; | 454 return S_OK; |
395 } | 455 } |
396 | 456 |
397 } // namespace remoting | 457 } // namespace remoting |
OLD | NEW |