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 |