OLD | NEW |
1 // Copyright 2013 The Chromium Authors. All rights reserved. | 1 // Copyright 2013 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/it2me/it2me_native_messaging_host.h" | 5 #include "remoting/host/it2me/it2me_native_messaging_host.h" |
6 | 6 |
| 7 #include <memory> |
7 #include <string> | 8 #include <string> |
8 #include <utility> | 9 #include <utility> |
9 | 10 |
10 #include "base/bind.h" | 11 #include "base/bind.h" |
11 #include "base/callback.h" | 12 #include "base/callback.h" |
| 13 #include "base/callback_helpers.h" |
12 #include "base/json/json_reader.h" | 14 #include "base/json/json_reader.h" |
13 #include "base/json/json_writer.h" | 15 #include "base/json/json_writer.h" |
14 #include "base/strings/string_number_conversions.h" | 16 #include "base/strings/string_number_conversions.h" |
15 #include "base/strings/string_util.h" | 17 #include "base/strings/string_util.h" |
16 #include "base/strings/stringize_macros.h" | 18 #include "base/strings/stringize_macros.h" |
17 #include "base/threading/thread.h" | 19 #include "base/threading/thread.h" |
| 20 #include "base/time/time.h" |
18 #include "base/values.h" | 21 #include "base/values.h" |
19 #include "build/build_config.h" | 22 #include "build/build_config.h" |
| 23 #include "components/policy/policy_constants.h" |
20 #include "net/base/url_util.h" | 24 #include "net/base/url_util.h" |
21 #include "net/url_request/url_request_context_getter.h" | 25 #include "net/url_request/url_request_context_getter.h" |
22 #include "remoting/base/auto_thread_task_runner.h" | 26 #include "remoting/base/auto_thread_task_runner.h" |
23 #include "remoting/host/chromoting_host_context.h" | 27 #include "remoting/host/chromoting_host_context.h" |
24 #include "remoting/host/host_exit_codes.h" | 28 #include "remoting/host/host_exit_codes.h" |
| 29 #include "remoting/host/policy_watcher.h" |
25 #include "remoting/host/service_urls.h" | 30 #include "remoting/host/service_urls.h" |
26 #include "remoting/protocol/name_value_map.h" | 31 #include "remoting/protocol/name_value_map.h" |
27 | 32 |
| 33 #if defined(OS_WIN) |
| 34 #include "base/command_line.h" |
| 35 #include "base/files/file_path.h" |
| 36 |
| 37 #include "remoting/host/win/elevated_native_messaging_host.h" |
| 38 #endif // defined(OS_WIN) |
| 39 |
28 namespace remoting { | 40 namespace remoting { |
29 | 41 |
30 namespace { | 42 namespace { |
31 | 43 |
32 const remoting::protocol::NameMapElement<It2MeHostState> kIt2MeHostStates[] = { | 44 const remoting::protocol::NameMapElement<It2MeHostState> kIt2MeHostStates[] = { |
33 {kDisconnected, "DISCONNECTED"}, | 45 {kDisconnected, "DISCONNECTED"}, |
34 {kStarting, "STARTING"}, | 46 {kStarting, "STARTING"}, |
35 {kRequestedAccessCode, "REQUESTED_ACCESS_CODE"}, | 47 {kRequestedAccessCode, "REQUESTED_ACCESS_CODE"}, |
36 {kReceivedAccessCode, "RECEIVED_ACCESS_CODE"}, | 48 {kReceivedAccessCode, "RECEIVED_ACCESS_CODE"}, |
37 {kConnected, "CONNECTED"}, | 49 {kConnected, "CONNECTED"}, |
38 {kError, "ERROR"}, | 50 {kError, "ERROR"}, |
39 {kInvalidDomainError, "INVALID_DOMAIN_ERROR"}, | 51 {kInvalidDomainError, "INVALID_DOMAIN_ERROR"}, |
40 }; | 52 }; |
41 | 53 |
| 54 #if defined(OS_WIN) |
| 55 const base::FilePath::CharType kBaseHostBinaryName[] = |
| 56 FILE_PATH_LITERAL("remote_assistance_host.exe"); |
| 57 const base::FilePath::CharType kElevatedHostBinaryName[] = |
| 58 FILE_PATH_LITERAL("remote_assistance_host_uiaccess.exe"); |
| 59 #endif // defined(OS_WIN) |
| 60 |
| 61 // Helper function to run |callback| on the correct thread using |task_runner|. |
| 62 void PolicyUpdateCallback( |
| 63 scoped_refptr<base::SingleThreadTaskRunner> task_runner, |
| 64 remoting::PolicyWatcher::PolicyUpdatedCallback callback, |
| 65 std::unique_ptr<base::DictionaryValue> policies) { |
| 66 DCHECK(!callback.is_null()); |
| 67 |
| 68 // Always post the task so the execution is consistent (always asynchronous). |
| 69 task_runner->PostTask(FROM_HERE, |
| 70 base::Bind(callback, base::Passed(&policies))); |
| 71 } |
| 72 |
| 73 // Called when malformed policies are detected. |
| 74 void OnPolicyError() { |
| 75 // TODO(joedow): Report the policy error to the user. crbug.com/433009 |
| 76 NOTIMPLEMENTED(); |
| 77 } |
| 78 |
42 } // namespace | 79 } // namespace |
43 | 80 |
44 It2MeNativeMessagingHost::It2MeNativeMessagingHost( | 81 It2MeNativeMessagingHost::It2MeNativeMessagingHost( |
| 82 bool needs_elevation, |
| 83 policy::PolicyService* policy_service, |
45 std::unique_ptr<ChromotingHostContext> context, | 84 std::unique_ptr<ChromotingHostContext> context, |
46 std::unique_ptr<It2MeHostFactory> factory) | 85 std::unique_ptr<It2MeHostFactory> factory) |
47 : client_(nullptr), | 86 : needs_elevation_(needs_elevation), |
48 host_context_(std::move(context)), | 87 host_context_(std::move(context)), |
49 factory_(std::move(factory)), | 88 factory_(std::move(factory)), |
| 89 policy_service_(policy_service), |
| 90 policy_watcher_(PolicyWatcher::Create(policy_service_, |
| 91 host_context_->file_task_runner())), |
50 weak_factory_(this) { | 92 weak_factory_(this) { |
51 weak_ptr_ = weak_factory_.GetWeakPtr(); | 93 weak_ptr_ = weak_factory_.GetWeakPtr(); |
52 | 94 |
53 const ServiceUrls* service_urls = ServiceUrls::GetInstance(); | 95 const ServiceUrls* service_urls = ServiceUrls::GetInstance(); |
54 const bool xmpp_server_valid = | 96 const bool xmpp_server_valid = |
55 net::ParseHostAndPort(service_urls->xmpp_server_address(), | 97 net::ParseHostAndPort(service_urls->xmpp_server_address(), |
56 &xmpp_server_config_.host, | 98 &xmpp_server_config_.host, |
57 &xmpp_server_config_.port); | 99 &xmpp_server_config_.port); |
58 DCHECK(xmpp_server_valid); | 100 DCHECK(xmpp_server_valid); |
59 | 101 |
60 xmpp_server_config_.use_tls = service_urls->xmpp_server_use_tls(); | 102 xmpp_server_config_.use_tls = service_urls->xmpp_server_use_tls(); |
61 directory_bot_jid_ = service_urls->directory_bot_jid(); | 103 directory_bot_jid_ = service_urls->directory_bot_jid(); |
| 104 |
| 105 // The policy watcher runs on the |file_task_runner| but we want to run the |
| 106 // update code on |task_runner| so we use a shim to post the callback to the |
| 107 // preferred task runner. |
| 108 PolicyWatcher::PolicyUpdatedCallback update_callback = |
| 109 base::Bind(&It2MeNativeMessagingHost::OnPolicyUpdate, weak_ptr_); |
| 110 policy_watcher_->StartWatching( |
| 111 base::Bind(&PolicyUpdateCallback, task_runner(), update_callback), |
| 112 base::Bind(&OnPolicyError)); |
62 } | 113 } |
63 | 114 |
64 It2MeNativeMessagingHost::~It2MeNativeMessagingHost() { | 115 It2MeNativeMessagingHost::~It2MeNativeMessagingHost() { |
65 DCHECK(task_runner()->BelongsToCurrentThread()); | 116 DCHECK(task_runner()->BelongsToCurrentThread()); |
66 | 117 |
67 if (it2me_host_.get()) { | 118 if (it2me_host_.get()) { |
68 it2me_host_->Disconnect(); | 119 it2me_host_->Disconnect(); |
69 it2me_host_ = nullptr; | 120 it2me_host_ = nullptr; |
70 } | 121 } |
71 } | 122 } |
(...skipping 20 matching lines...) Expand all Loading... |
92 | 143 |
93 std::string type; | 144 std::string type; |
94 if (!message_dict->GetString("type", &type)) { | 145 if (!message_dict->GetString("type", &type)) { |
95 SendErrorAndExit(std::move(response), "'type' not found in request."); | 146 SendErrorAndExit(std::move(response), "'type' not found in request."); |
96 return; | 147 return; |
97 } | 148 } |
98 | 149 |
99 response->SetString("type", type + "Response"); | 150 response->SetString("type", type + "Response"); |
100 | 151 |
101 if (type == "hello") { | 152 if (type == "hello") { |
102 ProcessHello(*message_dict, std::move(response)); | 153 ProcessHello(std::move(message_dict), std::move(response)); |
103 } else if (type == "connect") { | 154 } else if (type == "connect") { |
104 ProcessConnect(*message_dict, std::move(response)); | 155 ProcessConnect(std::move(message_dict), std::move(response)); |
105 } else if (type == "disconnect") { | 156 } else if (type == "disconnect") { |
106 ProcessDisconnect(*message_dict, std::move(response)); | 157 ProcessDisconnect(std::move(message_dict), std::move(response)); |
107 } else { | 158 } else { |
108 SendErrorAndExit(std::move(response), "Unsupported request type: " + type); | 159 SendErrorAndExit(std::move(response), "Unsupported request type: " + type); |
109 } | 160 } |
110 } | 161 } |
111 | 162 |
112 void It2MeNativeMessagingHost::Start(Client* client) { | 163 void It2MeNativeMessagingHost::Start(Client* client) { |
113 DCHECK(task_runner()->BelongsToCurrentThread()); | 164 DCHECK(task_runner()->BelongsToCurrentThread()); |
114 client_ = client; | 165 client_ = client; |
115 #if !defined(OS_CHROMEOS) | 166 #if !defined(OS_CHROMEOS) |
116 log_message_handler_.reset( | 167 log_message_handler_.reset( |
117 new LogMessageHandler( | 168 new LogMessageHandler( |
118 base::Bind(&It2MeNativeMessagingHost::SendMessageToClient, | 169 base::Bind(&It2MeNativeMessagingHost::SendMessageToClient, |
119 base::Unretained(this)))); | 170 base::Unretained(this)))); |
120 #endif // !defined(OS_CHROMEOS) | 171 #endif // !defined(OS_CHROMEOS) |
121 } | 172 } |
122 | 173 |
123 void It2MeNativeMessagingHost::SendMessageToClient( | 174 void It2MeNativeMessagingHost::SendMessageToClient( |
124 std::unique_ptr<base::Value> message) const { | 175 std::unique_ptr<base::Value> message) const { |
125 DCHECK(task_runner()->BelongsToCurrentThread()); | 176 DCHECK(task_runner()->BelongsToCurrentThread()); |
126 std::string message_json; | 177 std::string message_json; |
127 base::JSONWriter::Write(*message, &message_json); | 178 base::JSONWriter::Write(*message, &message_json); |
128 client_->PostMessageFromNativeHost(message_json); | 179 client_->PostMessageFromNativeHost(message_json); |
129 } | 180 } |
130 | 181 |
131 void It2MeNativeMessagingHost::ProcessHello( | 182 void It2MeNativeMessagingHost::ProcessHello( |
132 const base::DictionaryValue& message, | 183 std::unique_ptr<base::DictionaryValue> message, |
133 std::unique_ptr<base::DictionaryValue> response) const { | 184 std::unique_ptr<base::DictionaryValue> response) const { |
134 DCHECK(task_runner()->BelongsToCurrentThread()); | 185 DCHECK(task_runner()->BelongsToCurrentThread()); |
135 | 186 |
136 response->SetString("version", STRINGIZE(VERSION)); | 187 response->SetString("version", STRINGIZE(VERSION)); |
137 | 188 |
138 // This list will be populated when new features are added. | 189 // This list will be populated when new features are added. |
139 std::unique_ptr<base::ListValue> supported_features_list( | 190 std::unique_ptr<base::ListValue> supported_features_list( |
140 new base::ListValue()); | 191 new base::ListValue()); |
141 response->Set("supportedFeatures", supported_features_list.release()); | 192 response->Set("supportedFeatures", supported_features_list.release()); |
142 | 193 |
143 SendMessageToClient(std::move(response)); | 194 SendMessageToClient(std::move(response)); |
144 } | 195 } |
145 | 196 |
146 void It2MeNativeMessagingHost::ProcessConnect( | 197 void It2MeNativeMessagingHost::ProcessConnect( |
147 const base::DictionaryValue& message, | 198 std::unique_ptr<base::DictionaryValue> message, |
148 std::unique_ptr<base::DictionaryValue> response) { | 199 std::unique_ptr<base::DictionaryValue> response) { |
149 DCHECK(task_runner()->BelongsToCurrentThread()); | 200 DCHECK(task_runner()->BelongsToCurrentThread()); |
150 | 201 |
| 202 if (!policy_received_) { |
| 203 DCHECK(pending_connect_.is_null()); |
| 204 pending_connect_ = |
| 205 base::Bind(&It2MeNativeMessagingHost::ProcessConnect, weak_ptr_, |
| 206 base::Passed(&message), base::Passed(&response)); |
| 207 return; |
| 208 } |
| 209 |
| 210 if (needs_elevation_) { |
| 211 // Attempt to pass the current message to the elevated process. This method |
| 212 // will spin up the elevated process if it is not already running. On |
| 213 // success, the elevated process will process the message and respond. |
| 214 // If the process cannot be started or message passing fails, then return an |
| 215 // error to the message sender. |
| 216 if (!DelegateToElevatedHost(std::move(message))) { |
| 217 SendErrorAndExit(std::move(response), |
| 218 "Failed to send message to elevated host."); |
| 219 } |
| 220 return; |
| 221 } |
| 222 |
151 if (it2me_host_.get()) { | 223 if (it2me_host_.get()) { |
152 SendErrorAndExit(std::move(response), | 224 SendErrorAndExit(std::move(response), |
153 "Connect can be called only when disconnected."); | 225 "Connect can be called only when disconnected."); |
154 return; | 226 return; |
155 } | 227 } |
156 | 228 |
157 XmppSignalStrategy::XmppServerConfig xmpp_config = xmpp_server_config_; | 229 XmppSignalStrategy::XmppServerConfig xmpp_config = xmpp_server_config_; |
158 | 230 |
159 if (!message.GetString("userName", &xmpp_config.username)) { | 231 if (!message->GetString("userName", &xmpp_config.username)) { |
160 SendErrorAndExit(std::move(response), "'userName' not found in request."); | 232 SendErrorAndExit(std::move(response), "'userName' not found in request."); |
161 return; | 233 return; |
162 } | 234 } |
163 | 235 |
164 std::string auth_service_with_token; | 236 std::string auth_service_with_token; |
165 if (!message.GetString("authServiceWithToken", &auth_service_with_token)) { | 237 if (!message->GetString("authServiceWithToken", &auth_service_with_token)) { |
166 SendErrorAndExit(std::move(response), | 238 SendErrorAndExit(std::move(response), |
167 "'authServiceWithToken' not found in request."); | 239 "'authServiceWithToken' not found in request."); |
168 return; | 240 return; |
169 } | 241 } |
170 | 242 |
171 // For backward compatibility the webapp still passes OAuth service as part of | 243 // For backward compatibility the webapp still passes OAuth service as part of |
172 // the authServiceWithToken field. But auth service part is always expected to | 244 // the authServiceWithToken field. But auth service part is always expected to |
173 // be set to oauth2. | 245 // be set to oauth2. |
174 const char kOAuth2ServicePrefix[] = "oauth2:"; | 246 const char kOAuth2ServicePrefix[] = "oauth2:"; |
175 if (!base::StartsWith(auth_service_with_token, kOAuth2ServicePrefix, | 247 if (!base::StartsWith(auth_service_with_token, kOAuth2ServicePrefix, |
176 base::CompareCase::SENSITIVE)) { | 248 base::CompareCase::SENSITIVE)) { |
177 SendErrorAndExit(std::move(response), "Invalid 'authServiceWithToken': " + | 249 SendErrorAndExit(std::move(response), "Invalid 'authServiceWithToken': " + |
178 auth_service_with_token); | 250 auth_service_with_token); |
179 return; | 251 return; |
180 } | 252 } |
181 | 253 |
182 xmpp_config.auth_token = | 254 xmpp_config.auth_token = |
183 auth_service_with_token.substr(strlen(kOAuth2ServicePrefix)); | 255 auth_service_with_token.substr(strlen(kOAuth2ServicePrefix)); |
184 | 256 |
185 #if !defined(NDEBUG) | 257 #if !defined(NDEBUG) |
186 std::string address; | 258 std::string address; |
187 if (!message.GetString("xmppServerAddress", &address)) { | 259 if (!message->GetString("xmppServerAddress", &address)) { |
188 SendErrorAndExit(std::move(response), | 260 SendErrorAndExit(std::move(response), |
189 "'xmppServerAddress' not found in request."); | 261 "'xmppServerAddress' not found in request."); |
190 return; | 262 return; |
191 } | 263 } |
192 | 264 |
193 if (!net::ParseHostAndPort(address, &xmpp_config.host, | 265 if (!net::ParseHostAndPort(address, &xmpp_config.host, |
194 &xmpp_config.port)) { | 266 &xmpp_config.port)) { |
195 SendErrorAndExit(std::move(response), | 267 SendErrorAndExit(std::move(response), |
196 "Invalid 'xmppServerAddress': " + address); | 268 "Invalid 'xmppServerAddress': " + address); |
197 return; | 269 return; |
198 } | 270 } |
199 | 271 |
200 if (!message.GetBoolean("xmppServerUseTls", &xmpp_config.use_tls)) { | 272 if (!message->GetBoolean("xmppServerUseTls", &xmpp_config.use_tls)) { |
201 SendErrorAndExit(std::move(response), | 273 SendErrorAndExit(std::move(response), |
202 "'xmppServerUseTls' not found in request."); | 274 "'xmppServerUseTls' not found in request."); |
203 return; | 275 return; |
204 } | 276 } |
205 | 277 |
206 if (!message.GetString("directoryBotJid", &directory_bot_jid_)) { | 278 if (!message->GetString("directoryBotJid", &directory_bot_jid_)) { |
207 SendErrorAndExit(std::move(response), | 279 SendErrorAndExit(std::move(response), |
208 "'directoryBotJid' not found in request."); | 280 "'directoryBotJid' not found in request."); |
209 return; | 281 return; |
210 } | 282 } |
211 #endif // !defined(NDEBUG) | 283 #endif // !defined(NDEBUG) |
212 | 284 |
213 // Create the It2Me host and start connecting. | 285 // Create the It2Me host and start connecting. |
214 it2me_host_ = factory_->CreateIt2MeHost(host_context_->Copy(), | 286 it2me_host_ = |
215 weak_ptr_, | 287 factory_->CreateIt2MeHost(host_context_->Copy(), policy_service_, |
216 xmpp_config, | 288 weak_ptr_, xmpp_config, directory_bot_jid_); |
217 directory_bot_jid_); | |
218 it2me_host_->Connect(); | 289 it2me_host_->Connect(); |
219 | 290 |
220 SendMessageToClient(std::move(response)); | 291 SendMessageToClient(std::move(response)); |
221 } | 292 } |
222 | 293 |
223 void It2MeNativeMessagingHost::ProcessDisconnect( | 294 void It2MeNativeMessagingHost::ProcessDisconnect( |
224 const base::DictionaryValue& message, | 295 std::unique_ptr<base::DictionaryValue> message, |
225 std::unique_ptr<base::DictionaryValue> response) { | 296 std::unique_ptr<base::DictionaryValue> response) { |
226 DCHECK(task_runner()->BelongsToCurrentThread()); | 297 DCHECK(task_runner()->BelongsToCurrentThread()); |
| 298 DCHECK(policy_received_); |
| 299 |
| 300 if (needs_elevation_) { |
| 301 // Attempt to pass the current message to the elevated process. This method |
| 302 // will spin up the elevated process if it is not already running. On |
| 303 // success, the elevated process will process the message and respond. |
| 304 // If the process cannot be started or message passing fails, then return an |
| 305 // error to the message sender. |
| 306 if (!DelegateToElevatedHost(std::move(message))) { |
| 307 SendErrorAndExit(std::move(response), |
| 308 "Failed to send message to elevated host."); |
| 309 } |
| 310 return; |
| 311 } |
227 | 312 |
228 if (it2me_host_.get()) { | 313 if (it2me_host_.get()) { |
229 it2me_host_->Disconnect(); | 314 it2me_host_->Disconnect(); |
230 it2me_host_ = nullptr; | 315 it2me_host_ = nullptr; |
231 } | 316 } |
232 SendMessageToClient(std::move(response)); | 317 SendMessageToClient(std::move(response)); |
233 } | 318 } |
234 | 319 |
235 void It2MeNativeMessagingHost::SendErrorAndExit( | 320 void It2MeNativeMessagingHost::SendErrorAndExit( |
236 std::unique_ptr<base::DictionaryValue> response, | 321 std::unique_ptr<base::DictionaryValue> response, |
(...skipping 84 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
321 It2MeNativeMessagingHost::task_runner() const { | 406 It2MeNativeMessagingHost::task_runner() const { |
322 return host_context_->ui_task_runner(); | 407 return host_context_->ui_task_runner(); |
323 } | 408 } |
324 | 409 |
325 /* static */ | 410 /* static */ |
326 std::string It2MeNativeMessagingHost::HostStateToString( | 411 std::string It2MeNativeMessagingHost::HostStateToString( |
327 It2MeHostState host_state) { | 412 It2MeHostState host_state) { |
328 return ValueToName(kIt2MeHostStates, host_state); | 413 return ValueToName(kIt2MeHostStates, host_state); |
329 } | 414 } |
330 | 415 |
| 416 void It2MeNativeMessagingHost::OnPolicyUpdate( |
| 417 std::unique_ptr<base::DictionaryValue> policies) { |
| 418 if (policy_received_) { |
| 419 // Don't dynamically change how the host operates since we don't have a good |
| 420 // way to communicate changes to the user. |
| 421 return; |
| 422 } |
| 423 |
| 424 bool allow_elevated_host = false; |
| 425 if (!policies->GetBoolean( |
| 426 policy::key::kRemoteAccessHostAllowUiAccessForRemoteAssistance, |
| 427 &allow_elevated_host)) { |
| 428 LOG(WARNING) << "Failed to retrieve elevated host policy value."; |
| 429 } |
| 430 #if defined(OS_WIN) |
| 431 LOG(INFO) << "Allow UiAccess for Remote Assistance: " << allow_elevated_host; |
| 432 #endif // defined(OS_WIN) |
| 433 |
| 434 policy_received_ = true; |
| 435 |
| 436 // If |allow_elevated_host| is false, then we will fall back to using a host |
| 437 // running in the current context regardless of the elevation request. This |
| 438 // may not be ideal, but is still functional. |
| 439 needs_elevation_ = needs_elevation_ && allow_elevated_host; |
| 440 if (!pending_connect_.is_null()) { |
| 441 base::ResetAndReturn(&pending_connect_).Run(); |
| 442 } |
| 443 } |
| 444 |
| 445 #if defined(OS_WIN) |
| 446 |
| 447 bool It2MeNativeMessagingHost::DelegateToElevatedHost( |
| 448 std::unique_ptr<base::DictionaryValue> message) { |
| 449 DCHECK(task_runner()->BelongsToCurrentThread()); |
| 450 DCHECK(needs_elevation_); |
| 451 |
| 452 if (!elevated_host_) { |
| 453 base::FilePath binary_path = |
| 454 base::CommandLine::ForCurrentProcess()->GetProgram(); |
| 455 CHECK(binary_path.BaseName() == base::FilePath(kBaseHostBinaryName)); |
| 456 |
| 457 // The new process runs at an elevated level due to being granted uiAccess. |
| 458 // |parent_window_handle| can be used to position dialog windows but is not |
| 459 // currently used. |
| 460 elevated_host_.reset(new ElevatedNativeMessagingHost( |
| 461 binary_path.DirName().Append(kElevatedHostBinaryName), |
| 462 /*parent_window_handle=*/0, |
| 463 /*elevate_process=*/false, |
| 464 /*host_timeout=*/base::TimeDelta(), client_)); |
| 465 } |
| 466 |
| 467 if (elevated_host_->EnsureElevatedHostCreated()) { |
| 468 elevated_host_->SendMessage(std::move(message)); |
| 469 return true; |
| 470 } |
| 471 |
| 472 return false; |
| 473 } |
| 474 |
| 475 #else // !defined(OS_WIN) |
| 476 |
| 477 bool It2MeNativeMessagingHost::DelegateToElevatedHost( |
| 478 std::unique_ptr<base::DictionaryValue> message) { |
| 479 NOTREACHED(); |
| 480 return false; |
| 481 } |
| 482 |
| 483 #endif // !defined(OS_WIN) |
| 484 |
331 } // namespace remoting | 485 } // namespace remoting |
OLD | NEW |