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