Chromium Code Reviews| 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; | |
|
Wez
2012/03/30 22:11:01
nit: Specifying the max file size in hex makes it
alexeypa (please no reviews)
2012/03/30 23:47:09
Done.
| |
| 25 | |
| 26 const char kHostId[] = "host_id"; | |
| 27 const char kXmppLogin[] = "xmpp_login"; | |
|
Wez
2012/03/30 22:11:01
nit: Add a comment to explain what these constants
alexeypa (please no reviews)
2012/03/30 23:47:09
Done.
| |
| 28 | |
| 29 // Names of the configuration files. | |
| 30 const FilePath::CharType kAuthConfig[] = FILE_PATH_LITERAL("auth.json"); | |
|
Wez
2012/03/30 22:11:01
nit: These should really be kAuthConfig -> kAuthCo
alexeypa (please no reviews)
2012/03/30 23:47:09
Done.
| |
| 31 const FilePath::CharType kHostConfig[] = FILE_PATH_LITERAL("host.json"); | |
| 32 | |
| 33 // TODO(alexeypa): remove the hardcoded undocimented paths and store | |
|
Wez
2012/03/30 22:11:01
typos: remove -> Remove, undocimented.
alexeypa (please no reviews)
2012/03/30 23:47:09
Done.
| |
| 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). | |
|
Wez
2012/03/30 22:11:01
nit: The "up to 64KB in size" comment goes with th
alexeypa (please no reviews)
2012/03/30 23:47:09
Done.
| |
| 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. | |
|
Wez
2012/03/30 22:11:01
nit: "Parse the JSON configuration, expecting it t
alexeypa (please no reviews)
2012/03/30 23:47:09
Done.
| |
| 73 std::string file_content(&buffer[0], size); | |
| 74 scoped_ptr<base::Value> value(base::JSONReader::Read(file_content, true)); | |
| 75 | |
| 76 base::DictionaryValue* dictionary; | |
| 77 if (value.get() == NULL || !value->GetAsDictionary(&dictionary)) { | |
| 78 LOG(ERROR) << "Failed to read '" << filename.value() << "'."; | |
| 79 return E_FAIL; | |
| 80 } | |
| 81 | |
| 82 value.release(); | |
| 83 config_out->reset(dictionary); | |
|
Wez
2012/03/30 22:11:01
Gah. Gross. Can't see a better way to do it, off
| |
| 84 return S_OK; | |
| 85 } | |
| 86 | |
| 87 } // namespace | |
| 12 | 88 |
| 13 namespace remoting { | 89 namespace remoting { |
| 14 | 90 |
| 15 ElevatedControllerWin::ElevatedControllerWin() { | 91 ElevatedControllerWin::ElevatedControllerWin() { |
| 16 } | 92 } |
| 17 | 93 |
| 18 HRESULT ElevatedControllerWin::FinalConstruct() { | 94 HRESULT ElevatedControllerWin::FinalConstruct() { |
| 19 return S_OK; | 95 return S_OK; |
| 20 } | 96 } |
| 21 | 97 |
| 22 void ElevatedControllerWin::FinalRelease() { | 98 void ElevatedControllerWin::FinalRelease() { |
| 23 } | 99 } |
| 24 | 100 |
| 25 STDMETHODIMP ElevatedControllerWin::get_State(DaemonState* state_out) { | 101 STDMETHODIMP ElevatedControllerWin::GetConfig(BSTR* config_out) { |
| 26 return E_NOTIMPL; | 102 FilePath system_profile; |
| 103 PathService::Get(base::DIR_SYSTEM, &system_profile); | |
| 104 | |
| 105 // Build the host configuration. | |
|
Wez
2012/03/30 22:11:01
nit: Isn't this reading it, rather than "building"
alexeypa (please no reviews)
2012/03/30 23:47:09
Done.
| |
| 106 scoped_ptr<base::DictionaryValue> config; | |
| 107 HRESULT hr = ReadConfig(system_profile.Append(kConfigDir).Append(kHostConfig), | |
| 108 &config); | |
| 109 if (FAILED(hr)) { | |
| 110 return hr; | |
| 111 } | |
| 112 | |
| 113 // Build the filtered config. | |
|
Wez
2012/03/30 22:11:01
Why? What is the purpose of the filtering?
alexeypa (please no reviews)
2012/03/30 23:47:09
This matched Mac implementation.
Lambros
2012/03/31 00:48:34
GetConfig() is documented to return only certain k
Wez
2012/04/01 01:25:27
Right; my point is that the comment should clarify
alexeypa (please no reviews)
2012/04/02 16:26:39
Oh, well. I'll try to address this in one of the f
| |
| 114 scoped_ptr<base::DictionaryValue> filtered_config( | |
| 115 new base::DictionaryValue()); | |
| 116 | |
| 117 std::string value; | |
| 118 if (config->GetString(kHostId, &value)) { | |
| 119 filtered_config->SetString(kHostId, value); | |
| 120 } | |
| 121 | |
|
Wez
2012/03/30 22:11:01
nit: No need for this blank line.
alexeypa (please no reviews)
2012/03/30 23:47:09
Done.
| |
| 122 if (config->GetString(kXmppLogin, &value)) { | |
| 123 filtered_config->SetString(kXmppLogin, value); | |
| 124 } | |
| 125 | |
| 126 // Convert the filtered config back to string and return it to the caller. | |
|
Wez
2012/03/30 22:11:01
typo: ... back to a string ...
alexeypa (please no reviews)
2012/03/30 23:47:09
Done.
| |
| 127 std::string file_content; | |
| 128 base::JSONWriter::Write(filtered_config.get(), &file_content); | |
| 129 | |
| 130 *config_out = ::SysAllocString(UTF8ToUTF16(file_content).c_str()); | |
| 131 if (config_out == NULL) { | |
| 132 return E_OUTOFMEMORY; | |
| 133 } | |
| 134 | |
| 135 return S_OK; | |
| 27 } | 136 } |
| 28 | 137 |
| 29 STDMETHODIMP ElevatedControllerWin::ReadConfig(BSTR* config_out) { | 138 STDMETHODIMP ElevatedControllerWin::SetConfig(BSTR config) { |
| 30 return E_NOTIMPL; | 139 FilePath system_profile; |
|
Wez
2012/03/30 22:11:01
nit: Comment before these lines e.g. "Determine th
alexeypa (please no reviews)
2012/03/30 23:47:09
Done.
| |
| 31 } | 140 PathService::Get(base::DIR_SYSTEM, &system_profile); |
| 32 | 141 |
|
Wez
2012/03/30 22:11:01
nit: No need for this blank line?
alexeypa (please no reviews)
2012/03/30 23:47:09
Removed. My eyes hurt now! :-)
| |
| 33 STDMETHODIMP ElevatedControllerWin::WriteConfig(BSTR config) { | 142 FilePath config_dir = system_profile.Append(kConfigDir); |
| 34 return E_NOTIMPL; | 143 if (!file_util::CreateDirectory(config_dir)) { |
| 144 return HRESULT_FROM_WIN32(ERROR_ACCESS_DENIED); | |
| 145 } | |
| 146 | |
| 147 std::string file_content = UTF16ToUTF8( | |
| 148 string16(static_cast<char16*>(config), ::SysStringLen(config))); | |
| 149 | |
| 150 int written = file_util::WriteFile( | |
|
Wez
2012/03/30 22:11:01
Should we be doing the write-temp-file-then-delete
alexeypa (please no reviews)
2012/03/30 23:47:09
We could. However the whole configuration flow is
| |
| 151 config_dir.Append(kAuthConfig), | |
| 152 file_content.c_str(), | |
| 153 file_content.size()); | |
| 154 | |
| 155 if (written != static_cast<int>(file_content.size())) { | |
| 156 return HRESULT_FROM_WIN32(ERROR_ACCESS_DENIED); | |
| 157 } | |
| 158 | |
| 159 // TODO(alexeypa): make it a single file. | |
|
Wez
2012/03/30 22:11:01
nit: I think you mean "Store the authentication an
alexeypa (please no reviews)
2012/03/30 23:47:09
Done.
| |
| 160 written = file_util::WriteFile( | |
| 161 config_dir.Append(kHostConfig), | |
| 162 file_content.c_str(), | |
| 163 file_content.size()); | |
| 164 | |
| 165 if (written != static_cast<int>(file_content.size())) { | |
| 166 return HRESULT_FROM_WIN32(ERROR_ACCESS_DENIED); | |
|
Wez
2012/03/30 22:11:01
nit: That's not strictly the right error, and in f
alexeypa (please no reviews)
2012/03/30 23:47:09
Nope. I bet something else bad happened. I replace
| |
| 167 } | |
| 168 | |
| 169 return S_OK; | |
| 35 } | 170 } |
| 36 | 171 |
| 37 STDMETHODIMP ElevatedControllerWin::StartDaemon() { | 172 STDMETHODIMP ElevatedControllerWin::StartDaemon() { |
| 38 return E_NOTIMPL; | 173 ScopedScHandle service; |
| 39 } | 174 HRESULT hr = OpenService(&service); |
| 40 | 175 if (FAILED(hr)) { |
| 41 STDMETHODIMP ElevatedControllerWin::StopDaemon() { | 176 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 } | 177 } |
| 51 | 178 |
| 52 CComPtr<IEnumConnections> connections; | 179 if (!StartService(service, 0, NULL)) { |
| 53 if (FAILED(connection_point->EnumConnections(&connections))) { | 180 DWORD error = GetLastError(); |
| 54 return S_OK; | 181 if (error != ERROR_SERVICE_ALREADY_RUNNING) { |
| 55 } | 182 LOG_GETLASTERROR(ERROR) |
| 183 << "Failed to start the '" << kWindowsServiceName << "'service"; | |
| 56 | 184 |
| 57 CONNECTDATA connect_data; | 185 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 } | 186 } |
| 69 } | 187 } |
| 70 | 188 |
| 71 return S_OK; | 189 return S_OK; |
| 72 } | 190 } |
| 73 | 191 |
| 192 STDMETHODIMP ElevatedControllerWin::StopDaemon() { | |
| 193 ScopedScHandle service; | |
| 194 HRESULT hr = OpenService(&service); | |
| 195 if (FAILED(hr)) { | |
| 196 return hr; | |
|
Wez
2012/03/30 22:11:01
Do you actually want to return potentially arbitra
alexeypa (please no reviews)
2012/03/30 23:47:09
I don't think so. Something better defined is "ope
Wez
2012/04/01 01:25:27
The word "here" was missing from my sentence; I th
alexeypa (please no reviews)
2012/04/02 16:26:39
That is not the only reason. I don't think that us
| |
| 197 } | |
| 198 | |
| 199 SERVICE_STATUS status; | |
| 200 if (!ControlService(service, SERVICE_CONTROL_STOP, &status)) { | |
| 201 DWORD error = GetLastError(); | |
| 202 if (error != ERROR_SERVICE_NOT_ACTIVE) { | |
| 203 LOG_GETLASTERROR(ERROR) | |
| 204 << "Failed to stop the '" << kWindowsServiceName << "'service"; | |
| 205 return HRESULT_FROM_WIN32(error); | |
| 206 } | |
| 207 } | |
| 208 | |
| 209 return S_OK; | |
| 210 } | |
| 211 | |
| 212 HRESULT ElevatedControllerWin::OpenService(ScopedScHandle* service_out) { | |
| 213 DWORD error; | |
| 214 | |
| 215 ScopedScHandle scmanager( | |
| 216 ::OpenSCManagerW(NULL, SERVICES_ACTIVE_DATABASE, | |
| 217 SC_MANAGER_CONNECT | SC_MANAGER_ENUMERATE_SERVICE)); | |
| 218 if (!scmanager.IsValid()) { | |
| 219 error = GetLastError(); | |
| 220 LOG_GETLASTERROR(ERROR) | |
| 221 << "Failed to connect to the service control manager"; | |
| 222 | |
| 223 return HRESULT_FROM_WIN32(error); | |
| 224 } | |
| 225 | |
| 226 ScopedScHandle service( | |
| 227 ::OpenServiceA(scmanager, kWindowsServiceName, | |
|
Wez
2012/03/30 22:11:01
nit: Why are we using OpenServiceA but OpenSCManag
alexeypa (please no reviews)
2012/03/30 23:47:09
Done.
| |
| 228 SERVICE_QUERY_STATUS | SERVICE_START | SERVICE_STOP)); | |
| 229 if (!service.IsValid()) { | |
| 230 error = GetLastError(); | |
| 231 LOG_GETLASTERROR(ERROR) | |
| 232 << "Failed to open to the '" << kWindowsServiceName << "' service"; | |
| 233 | |
| 234 return HRESULT_FROM_WIN32(error); | |
| 235 } | |
| 236 | |
| 237 service_out->Set(service.Take()); | |
| 238 return S_OK; | |
| 239 } | |
| 240 | |
| 74 } // namespace remoting | 241 } // namespace remoting |
| OLD | NEW |