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 "base/file_util.h" |
| 8 #include "base/logging.h" |
| 9 #include "base/json/json_reader.h" |
| 10 #include "base/json/json_writer.h" |
| 11 #include "base/memory/scoped_ptr.h" |
| 12 #include "base/path_service.h" |
| 13 #include "base/utf_string_conversions.h" |
| 14 #include "base/values.h" |
| 15 #include "base/win/scoped_handle.h" |
| 16 #include "remoting/host/branding.h" |
| 17 |
7 // MIDL-generated definitions. | 18 // MIDL-generated definitions. |
8 #include <elevated_controller_i.c> | 19 #include "elevated_controller_i.c" |
9 | 20 |
10 using ATL::CComQIPtr; | 21 namespace { |
11 using ATL::CComPtr; | 22 |
| 23 // ReadConfig() filters the configuration file stripping all variables except of |
| 24 // the following two. |
| 25 const char kHostId[] = "host_id"; |
| 26 const char kXmppLogin[] = "xmpp_login"; |
| 27 |
| 28 // Names of the configuration files. |
| 29 const FilePath::CharType kAuthConfigFilename[] = FILE_PATH_LITERAL("auth.json"); |
| 30 const FilePath::CharType kHostConfigFilename[] = FILE_PATH_LITERAL("host.json"); |
| 31 |
| 32 // TODO(alexeypa): Remove the hardcoded undocumented paths and store |
| 33 // the configuration in the registry. |
| 34 #ifdef OFFICIAL_BUILD |
| 35 const FilePath::CharType kConfigDir[] = FILE_PATH_LITERAL( |
| 36 "config\\systemprofile\\AppData\\Local\\Google\\Chrome Remote Desktop"); |
| 37 #else |
| 38 const FilePath::CharType kConfigDir[] = |
| 39 FILE_PATH_LITERAL("config\\systemprofile\\AppData\\Local\\Chromoting"); |
| 40 #endif |
| 41 |
| 42 // Reads and parses a JSON configuration file. |
| 43 HRESULT ReadConfig(const FilePath& filename, |
| 44 scoped_ptr<base::DictionaryValue>* config_out) { |
| 45 // TODO(alexeypa): Remove 64KB limitation. |
| 46 const size_t kMaxConfigFileSize = 64 * 1024; |
| 47 |
| 48 // Read raw data from the configuration file. |
| 49 base::win::ScopedHandle file( |
| 50 CreateFileW(filename.value().c_str(), |
| 51 GENERIC_READ, |
| 52 FILE_SHARE_READ | FILE_SHARE_WRITE, |
| 53 NULL, |
| 54 OPEN_EXISTING, |
| 55 FILE_FLAG_SEQUENTIAL_SCAN, |
| 56 NULL)); |
| 57 |
| 58 if (!file.IsValid()) { |
| 59 DWORD error = GetLastError(); |
| 60 LOG_GETLASTERROR(ERROR) |
| 61 << "Failed to read '" << filename.value() << "'"; |
| 62 return HRESULT_FROM_WIN32(error); |
| 63 } |
| 64 |
| 65 std::vector<char> buffer(kMaxConfigFileSize); |
| 66 DWORD size = static_cast<DWORD>(buffer.size()); |
| 67 if (!::ReadFile(file, &buffer[0], size, &size, NULL)) { |
| 68 DWORD error = GetLastError(); |
| 69 LOG_GETLASTERROR(ERROR) |
| 70 << "Failed to read '" << filename.value() << "'"; |
| 71 return HRESULT_FROM_WIN32(error); |
| 72 } |
| 73 |
| 74 // Parse the JSON configuration, expecting it to contain a dictionary. |
| 75 std::string file_content(&buffer[0], size); |
| 76 scoped_ptr<base::Value> value(base::JSONReader::Read(file_content, true)); |
| 77 |
| 78 base::DictionaryValue* dictionary; |
| 79 if (value.get() == NULL || !value->GetAsDictionary(&dictionary)) { |
| 80 LOG(ERROR) << "Failed to read '" << filename.value() << "'."; |
| 81 return E_FAIL; |
| 82 } |
| 83 |
| 84 value.release(); |
| 85 config_out->reset(dictionary); |
| 86 return S_OK; |
| 87 } |
| 88 |
| 89 } // namespace |
12 | 90 |
13 namespace remoting { | 91 namespace remoting { |
14 | 92 |
15 ElevatedControllerWin::ElevatedControllerWin() { | 93 ElevatedControllerWin::ElevatedControllerWin() { |
16 } | 94 } |
17 | 95 |
18 HRESULT ElevatedControllerWin::FinalConstruct() { | 96 HRESULT ElevatedControllerWin::FinalConstruct() { |
19 return S_OK; | 97 return S_OK; |
20 } | 98 } |
21 | 99 |
22 void ElevatedControllerWin::FinalRelease() { | 100 void ElevatedControllerWin::FinalRelease() { |
23 } | 101 } |
24 | 102 |
25 STDMETHODIMP ElevatedControllerWin::get_State(DaemonState* state_out) { | 103 STDMETHODIMP ElevatedControllerWin::GetConfig(BSTR* config_out) { |
26 return E_NOTIMPL; | 104 FilePath system_profile; |
| 105 PathService::Get(base::DIR_SYSTEM, &system_profile); |
| 106 |
| 107 // Read the host configuration. |
| 108 scoped_ptr<base::DictionaryValue> config; |
| 109 HRESULT hr = ReadConfig( |
| 110 system_profile.Append(kConfigDir).Append(kHostConfigFilename), |
| 111 &config); |
| 112 if (FAILED(hr)) { |
| 113 return hr; |
| 114 } |
| 115 |
| 116 // Build the filtered config. |
| 117 scoped_ptr<base::DictionaryValue> filtered_config( |
| 118 new base::DictionaryValue()); |
| 119 |
| 120 std::string value; |
| 121 if (config->GetString(kHostId, &value)) { |
| 122 filtered_config->SetString(kHostId, value); |
| 123 } |
| 124 if (config->GetString(kXmppLogin, &value)) { |
| 125 filtered_config->SetString(kXmppLogin, value); |
| 126 } |
| 127 |
| 128 // Convert the filtered config back to a string and return it to the caller. |
| 129 std::string file_content; |
| 130 base::JSONWriter::Write(filtered_config.get(), &file_content); |
| 131 |
| 132 *config_out = ::SysAllocString(UTF8ToUTF16(file_content).c_str()); |
| 133 if (config_out == NULL) { |
| 134 return E_OUTOFMEMORY; |
| 135 } |
| 136 |
| 137 return S_OK; |
27 } | 138 } |
28 | 139 |
29 STDMETHODIMP ElevatedControllerWin::ReadConfig(BSTR* config_out) { | 140 STDMETHODIMP ElevatedControllerWin::SetConfig(BSTR config) { |
30 return E_NOTIMPL; | 141 // Determine the config directory path and create it if necessary. |
31 } | 142 // N.B. The configuration files are stored in LocalSystems's profile which is |
| 143 // not readable by non administrators. |
| 144 FilePath system_profile; |
| 145 PathService::Get(base::DIR_SYSTEM, &system_profile); |
| 146 FilePath config_dir = system_profile.Append(kConfigDir); |
| 147 if (!file_util::CreateDirectory(config_dir)) { |
| 148 return HRESULT_FROM_WIN32(ERROR_ACCESS_DENIED); |
| 149 } |
32 | 150 |
33 STDMETHODIMP ElevatedControllerWin::WriteConfig(BSTR config) { | 151 std::string file_content = UTF16ToUTF8( |
34 return E_NOTIMPL; | 152 string16(static_cast<char16*>(config), ::SysStringLen(config))); |
| 153 |
| 154 int written = file_util::WriteFile( |
| 155 config_dir.Append(kAuthConfigFilename), |
| 156 file_content.c_str(), |
| 157 file_content.size()); |
| 158 if (written != static_cast<int>(file_content.size())) { |
| 159 return HRESULT_FROM_WIN32(ERROR_ACCESS_DENIED); |
| 160 } |
| 161 |
| 162 // TODO(alexeypa): Store the authentication and host configurations in a |
| 163 // single file. |
| 164 written = file_util::WriteFile( |
| 165 config_dir.Append(kHostConfigFilename), |
| 166 file_content.c_str(), |
| 167 file_content.size()); |
| 168 if (written != static_cast<int>(file_content.size())) { |
| 169 return E_FAIL; |
| 170 } |
| 171 |
| 172 return S_OK; |
35 } | 173 } |
36 | 174 |
37 STDMETHODIMP ElevatedControllerWin::StartDaemon() { | 175 STDMETHODIMP ElevatedControllerWin::StartDaemon() { |
38 return E_NOTIMPL; | 176 ScopedScHandle service; |
39 } | 177 HRESULT hr = OpenService(&service); |
40 | 178 if (FAILED(hr)) { |
41 STDMETHODIMP ElevatedControllerWin::StopDaemon() { | 179 return hr; |
42 return E_NOTIMPL; | |
43 } | |
44 | |
45 HRESULT ElevatedControllerWin::FireOnStateChange(DaemonState state) { | |
46 CComPtr<IConnectionPoint> connection_point; | |
47 FindConnectionPoint(__uuidof(IDaemonEvents), &connection_point); | |
48 if (!connection_point) { | |
49 return S_OK; | |
50 } | 180 } |
51 | 181 |
52 CComPtr<IEnumConnections> connections; | 182 if (!StartService(service, 0, NULL)) { |
53 if (FAILED(connection_point->EnumConnections(&connections))) { | 183 DWORD error = GetLastError(); |
54 return S_OK; | 184 if (error != ERROR_SERVICE_ALREADY_RUNNING) { |
55 } | 185 LOG_GETLASTERROR(ERROR) |
| 186 << "Failed to start the '" << kWindowsServiceName << "'service"; |
56 | 187 |
57 CONNECTDATA connect_data; | 188 return HRESULT_FROM_WIN32(error); |
58 while (connections->Next(1, &connect_data, NULL) == S_OK) { | |
59 if (connect_data.pUnk != NULL) { | |
60 CComQIPtr<IDaemonEvents, &__uuidof(IDaemonEvents)> sink( | |
61 connect_data.pUnk); | |
62 | |
63 if (sink != NULL) { | |
64 sink->OnStateChange(state); | |
65 } | |
66 | |
67 connect_data.pUnk->Release(); | |
68 } | 189 } |
69 } | 190 } |
70 | 191 |
71 return S_OK; | 192 return S_OK; |
72 } | 193 } |
73 | 194 |
| 195 STDMETHODIMP ElevatedControllerWin::StopDaemon() { |
| 196 ScopedScHandle service; |
| 197 HRESULT hr = OpenService(&service); |
| 198 if (FAILED(hr)) { |
| 199 return hr; |
| 200 } |
| 201 |
| 202 SERVICE_STATUS status; |
| 203 if (!ControlService(service, SERVICE_CONTROL_STOP, &status)) { |
| 204 DWORD error = GetLastError(); |
| 205 if (error != ERROR_SERVICE_NOT_ACTIVE) { |
| 206 LOG_GETLASTERROR(ERROR) |
| 207 << "Failed to stop the '" << kWindowsServiceName << "'service"; |
| 208 return HRESULT_FROM_WIN32(error); |
| 209 } |
| 210 } |
| 211 |
| 212 return S_OK; |
| 213 } |
| 214 |
| 215 HRESULT ElevatedControllerWin::OpenService(ScopedScHandle* service_out) { |
| 216 DWORD error; |
| 217 |
| 218 ScopedScHandle scmanager( |
| 219 ::OpenSCManagerW(NULL, SERVICES_ACTIVE_DATABASE, |
| 220 SC_MANAGER_CONNECT | SC_MANAGER_ENUMERATE_SERVICE)); |
| 221 if (!scmanager.IsValid()) { |
| 222 error = GetLastError(); |
| 223 LOG_GETLASTERROR(ERROR) |
| 224 << "Failed to connect to the service control manager"; |
| 225 |
| 226 return HRESULT_FROM_WIN32(error); |
| 227 } |
| 228 |
| 229 ScopedScHandle service( |
| 230 ::OpenServiceW(scmanager, UTF8ToUTF16(kWindowsServiceName).c_str(), |
| 231 SERVICE_QUERY_STATUS | SERVICE_START | SERVICE_STOP)); |
| 232 if (!service.IsValid()) { |
| 233 error = GetLastError(); |
| 234 LOG_GETLASTERROR(ERROR) |
| 235 << "Failed to open to the '" << kWindowsServiceName << "' service"; |
| 236 |
| 237 return HRESULT_FROM_WIN32(error); |
| 238 } |
| 239 |
| 240 service_out->Set(service.Take()); |
| 241 return S_OK; |
| 242 } |
| 243 |
74 } // namespace remoting | 244 } // namespace remoting |
OLD | NEW |