OLD | NEW |
| (Empty) |
1 /* | |
2 * Copyright 2008 The Native Client Authors. All rights reserved. | |
3 * Use of this source code is governed by a BSD-style license that can | |
4 * be found in the LICENSE file. | |
5 */ | |
6 | |
7 #include "native_client/src/trusted/plugin/srpc/service_runtime.h" | |
8 | |
9 #include <map> | |
10 #include <vector> | |
11 | |
12 #include "native_client/src/include/nacl_string.h" | |
13 #include "native_client/src/include/nacl_macros.h" | |
14 #include "native_client/src/shared/imc/nacl_imc.h" | |
15 #include "native_client/src/shared/platform/nacl_log.h" | |
16 #include "native_client/src/trusted/desc/nacl_desc_imc.h" | |
17 #include "native_client/src/trusted/desc/nrd_xfer.h" | |
18 #include "native_client/src/trusted/desc/nrd_xfer_effector.h" | |
19 #include "native_client/src/trusted/handle_pass/browser_handle.h" | |
20 #include "native_client/src/trusted/nonnacl_util/sel_ldr_launcher.h" | |
21 | |
22 #include "native_client/src/trusted/plugin/npapi/multimedia_socket.h" | |
23 #include "native_client/src/trusted/plugin/srpc/browser_interface.h" | |
24 #include "native_client/src/trusted/plugin/srpc/connected_socket.h" | |
25 #include "native_client/src/trusted/plugin/srpc/plugin.h" | |
26 #include "native_client/src/trusted/plugin/srpc/shared_memory.h" | |
27 #include "native_client/src/trusted/plugin/srpc/socket_address.h" | |
28 #include "native_client/src/trusted/plugin/srpc/srt_socket.h" | |
29 #include "native_client/src/trusted/plugin/srpc/scriptable_handle.h" | |
30 #include "native_client/src/trusted/plugin/srpc/utility.h" | |
31 | |
32 #include "native_client/src/trusted/service_runtime/nacl_error_code.h" | |
33 #include "native_client/src/trusted/service_runtime/include/sys/nacl_imc_api.h" | |
34 | |
35 using std::vector; | |
36 | |
37 namespace plugin { | |
38 | |
39 ServiceRuntime::ServiceRuntime( | |
40 BrowserInterface* browser_interface, | |
41 Plugin* plugin) : | |
42 async_receive_desc(NULL), | |
43 async_send_desc(NULL), | |
44 browser_interface_(browser_interface), | |
45 default_socket_address_(NULL), | |
46 default_socket_(NULL), | |
47 plugin_(plugin), | |
48 runtime_channel_(NULL), | |
49 subprocess_(NULL) { | |
50 } | |
51 | |
52 // shm is consumed (Delete invoked). | |
53 bool ServiceRuntime::InitCommunication(nacl::DescWrapper* shm) { | |
54 // TODO(sehr): this should use the new | |
55 // SelLdrLauncher::OpenSrpcChannels interface, which should be free | |
56 // of resource leaks. | |
57 // Channel5 was opened to communicate with the sel_ldr instance. | |
58 // Get the first IMC message and create a socket address from it. | |
59 nacl::Handle channel5 = subprocess_->channel(); | |
60 PLUGIN_PRINTF(("ServiceRuntime(%p): opened %p 0x%p\n", | |
61 static_cast<void*>(this), static_cast<void*>(subprocess_), | |
62 reinterpret_cast<void*>(channel5))); | |
63 // GetSocketAddress implicitly invokes Close(channel5). | |
64 default_socket_address_ = GetSocketAddress(plugin_, channel5); | |
65 PLUGIN_PRINTF(("ServiceRuntime::Start: " | |
66 "Got service channel descriptor %p\n", | |
67 static_cast<void*>(default_socket_address_))); | |
68 | |
69 if (NULL == default_socket_address_) { | |
70 PLUGIN_PRINTF(("ServiceRuntime::InitCommunication " | |
71 "no valid socket address\n")); | |
72 browser_interface_->Alert(plugin()->instance_id(), | |
73 "service runtime: no valid socket address"); | |
74 return false; | |
75 } | |
76 ScriptableHandle* raw_channel; | |
77 // The first connect on the socket address returns the service | |
78 // runtime command channel. This channel is created before the NaCl | |
79 // module runs, and is private to the service runtime. This channel | |
80 // is used for a secure plugin<->service runtime communication path | |
81 | |
82 | |
83 // that can be used to forcibly shut down the sel_ldr. | |
84 PLUGIN_PRINTF((" connecting for SrtSocket\n")); | |
85 PortableHandle* portable_socket_address = default_socket_address_->handle(); | |
86 raw_channel = portable_socket_address->Connect(); | |
87 if (NULL == raw_channel) { | |
88 PLUGIN_PRINTF(("ServiceRuntime::Start: " | |
89 "service runtime Connect failed.\n")); | |
90 browser_interface_->Alert(plugin()->instance_id(), | |
91 "service runtime Connect failed"); | |
92 return false; | |
93 } | |
94 PLUGIN_PRINTF((" constructing SrtSocket\n")); | |
95 runtime_channel_ = new(std::nothrow) SrtSocket(raw_channel, | |
96 browser_interface_); | |
97 if (NULL == runtime_channel_) { | |
98 // TODO(sehr): leaking raw_channel. | |
99 return false; | |
100 } | |
101 | |
102 // Set module's origin at the service runtime. NB: we may end up | |
103 // enforcing restrictions for network access to the origin of the | |
104 // module only in the plugin. Here's why: | |
105 // | |
106 // When a plugin using NPAPI invokes NPP_GetURLNotify (on the behalf | |
107 // of a NaCl module), it may provide a relative URL; even if it is | |
108 // not a relative URL, 3xx redirects may change the source domain of | |
109 // the actual web content as a side effect -- the 3xx redirects | |
110 // cause the browser to make new HTTP connections, and the browser | |
111 // does not notify the plugin as this is occurring. However, when | |
112 // the browser invokes NPN_NewStream and then either | |
113 // NPN_StreamAsFile or NPP_WriteReady/NPP_Write to deliver the | |
114 // content, the NPStream object contains the fully-qualified URL of | |
115 // the web content, after redirections (if any). This means that we | |
116 // should parse the FQ-URL at NPN_NewStream or NPN_StreamAsFile, | |
117 // rather than try to canonicalize the URL before passing it to | |
118 // NPP_GetURLNotify, since we won't know the final FQ-URL at that | |
119 // point. | |
120 // | |
121 // This strategy also implies that we might allow any URL, e.g., a | |
122 // tinyurl.com redirecting URL, to be used with NPP_GetURLNotify, | |
123 // and allow the access if the final FQ-URL is the same domain. | |
124 // This has the hazard that we still are doing fetches to other | |
125 // domains (including sending cookies specific to those domains), | |
126 // just not making the results available to the NaCl module; the | |
127 // same thing occurs if IMG SRC tags were scripted. | |
128 // | |
129 // A perhaps more important downside is that this is an easy way for | |
130 // NaCl modules to leak information: the target web server is always | |
131 // contacted, since we don't know if | |
132 // http://evil.org/leak?s3kr1t_inf0 might result in a 3xx redirect | |
133 // to an acceptable origin. If we want to prevent this, we could | |
134 // require that all URLs given to NPP_GetURLNotify are either | |
135 // relative or are absolute with the same origin as the module; this | |
136 // would prevent the scenario where an evil module is hosted (e.g., | |
137 // in temporary upload directory that's visible, or as a gmail | |
138 // attachment URL) at an innocent server to extract confidential | |
139 // info and send it to third party servers. An explict network ACL | |
140 // a la /robots.txt might be useful to serve as an ingress filter.z | |
141 | |
142 PLUGIN_PRINTF(("invoking set_origin\n")); | |
143 if (!runtime_channel_->SetOrigin(plugin_->nacl_module_origin())) { | |
144 PLUGIN_PRINTF(("ServiceRuntime::Start: " | |
145 "set_origin RPC failed.\n")); | |
146 browser_interface_->Alert(plugin()->instance_id(), "Could not set origin"); | |
147 // TODO(sehr): leaking raw_channel and runtime_channel_. | |
148 return false; | |
149 } | |
150 | |
151 if (shm != NULL) { | |
152 // This code is executed only if NaCl is running as a built-in plugin | |
153 // in Chrome. | |
154 // We now have an open communication channel to the sel_ldr process, | |
155 // so we can send the nexe bits over | |
156 if (!runtime_channel_->LoadModule(shm->desc())) { | |
157 PLUGIN_PRINTF(("ServiceRuntime::Start failed to send nexe\n")); | |
158 browser_interface_->Alert(plugin()->instance_id(), "failed to send nexe"); | |
159 shm->Delete(); | |
160 // TODO(gregoryd): close communication channels | |
161 delete subprocess_; | |
162 subprocess_ = NULL; | |
163 return false; | |
164 } | |
165 /* LoadModule succeeded, proceed normally */ | |
166 shm->Delete(); | |
167 #if NACL_WINDOWS && !defined(NACL_STANDALONE) | |
168 // Establish the communication for handle passing protocol | |
169 struct NaClDesc* desc = NaClHandlePassBrowserGetSocketAddress(); | |
170 if (!runtime_channel_->InitHandlePassing(desc, subprocess_->child())) { | |
171 delete subprocess_; | |
172 subprocess_ = NULL; | |
173 return false; | |
174 } | |
175 #endif | |
176 } | |
177 | |
178 // start the module. otherwise we cannot connect for multimedia | |
179 // subsystem since that is handled by user-level code (not secure!) | |
180 // in libsrpc. | |
181 int load_status; | |
182 | |
183 PLUGIN_PRINTF((" invoking start_module RPC\n")); | |
184 if (!runtime_channel_->StartModule(&load_status)) { | |
185 PLUGIN_PRINTF(("ServiceRuntime::Start: " | |
186 "module_start RPC failed.\n")); | |
187 browser_interface_->Alert(plugin()->instance_id(), | |
188 "Could not start nacl module"); | |
189 | |
190 // TODO(sehr): leaking raw_channel and runtime_channel_. | |
191 return false; | |
192 } | |
193 PLUGIN_PRINTF((" start_module returned %d\n", load_status)); | |
194 if (LOAD_OK != load_status) { | |
195 PLUGIN_PRINTF(("ServiceRuntime::Start: " | |
196 "module load status %d", | |
197 load_status)); | |
198 nacl::stringstream ss; | |
199 ss << "Loading of module failed with status " << load_status; | |
200 browser_interface_->Alert(plugin()->instance_id(), ss.str()); | |
201 // TODO(sehr): leaking raw_channel and runtime_channel_. | |
202 return false; | |
203 } | |
204 | |
205 // The second connect on the socket address is to the untrusted side | |
206 // of sel_ldr. | |
207 default_socket_ = portable_socket_address->Connect(); | |
208 if (NULL == default_socket_) { | |
209 PLUGIN_PRINTF(("ServiceRuntime::Start: " | |
210 "SRPC channel Connect failed.\n")); | |
211 // TODO(sehr): leaking raw_channel and runtime_channel_. | |
212 return false; | |
213 } | |
214 default_socket_->handle()->StartJSObjectProxy(plugin_); | |
215 plugin_->EnableVideo(); | |
216 // Create the listener thread and initialize the nacl module. | |
217 if (!plugin_->InitializeModuleMultimedia(default_socket_, this)) { | |
218 // TODO(sehr): leaking raw_channel, runtime_channel_, and default_socket_. | |
219 return false; | |
220 } | |
221 return true; | |
222 } | |
223 | |
224 bool ServiceRuntime::Start(const char* nacl_file) { | |
225 // The arguments we want to pass to the service runtime are | |
226 // "-P 5" sets the default SRPC channel to be over descriptor 5. The 5 needs | |
227 // to match the 5 in the Launcher invocation below. | |
228 // "-X 5" causes the service runtime to create a bound socket and socket | |
229 // address at descriptors 3 and 4. The socket address is transferred as | |
230 // the first IMC message on descriptor 5. This is used when connecting | |
231 // to socket addresses. | |
232 // "-d" (not default) invokes the service runtime in debug mode. | |
233 // const char* kSelLdrArgs[] = { "-P", "5", "-X", "5" }; | |
234 const char* kSelLdrArgs[] = { "-P", "5", "-X", "5" }; | |
235 // TODO(sehr): remove -P support and default channels. | |
236 const int kSelLdrArgLength = NACL_ARRAY_SIZE(kSelLdrArgs); | |
237 vector<nacl::string> kArgv(kSelLdrArgs, kSelLdrArgs + kSelLdrArgLength); | |
238 vector<nacl::string> kEmpty; | |
239 | |
240 PLUGIN_PRINTF(("ServiceRuntime::ServiceRuntime" | |
241 "(%p, %p, %s)\n", | |
242 static_cast<void*>(this), | |
243 static_cast<void*>(plugin()), | |
244 nacl_file)); | |
245 | |
246 subprocess_ = new(std::nothrow) nacl::SelLdrLauncher(); | |
247 if (NULL == subprocess_) { | |
248 PLUGIN_PRINTF(("ServiceRuntime: Could not create SelLdrLauncher")); | |
249 browser_interface_->Alert(plugin()->instance_id(), | |
250 "Could not create SelLdrLauncher"); | |
251 return false; | |
252 } | |
253 subprocess_->Init(nacl_file, 5, kArgv, kEmpty); | |
254 | |
255 nacl::Handle receive_handle = subprocess_->ExportImcFD(6); | |
256 if (receive_handle == nacl::kInvalidHandle) { | |
257 browser_interface_->Alert(plugin()->instance_id(), | |
258 "Failed to create async receive handle"); | |
259 return false; | |
260 } | |
261 async_receive_desc = plugin()->wrapper_factory()->MakeImcSock(receive_handle); | |
262 | |
263 nacl::Handle send_handle = subprocess_->ExportImcFD(7); | |
264 if (send_handle == nacl::kInvalidHandle) { | |
265 browser_interface_->Alert(plugin()->instance_id(), | |
266 "Failed to create async send handle"); | |
267 return false; | |
268 } | |
269 async_send_desc = plugin()->wrapper_factory()->MakeImcSock(send_handle); | |
270 | |
271 if (!subprocess_->Launch()) { | |
272 PLUGIN_PRINTF(("ServiceRuntime: Could not start SelLdrLauncher")); | |
273 browser_interface_->Alert(plugin()->instance_id(), | |
274 "Could not start SelLdrLauncher"); | |
275 delete subprocess_; | |
276 subprocess_ = NULL; | |
277 return false; | |
278 } | |
279 | |
280 // TODO(gregoryd) - this should deal with buffer and size correctly | |
281 // - do we need to send the load command from here? | |
282 if (!InitCommunication(NULL)) { | |
283 return false; | |
284 } | |
285 | |
286 PLUGIN_PRINTF(("ServiceRuntime::Start was successful\n")); | |
287 return true; | |
288 } | |
289 | |
290 // Chromium version. | |
291 bool ServiceRuntime::Start(const char* url, nacl::DescWrapper* shm) { | |
292 subprocess_ = new(std::nothrow) nacl::SelLdrLauncher(); | |
293 if (NULL == subprocess_) { | |
294 return false; | |
295 } | |
296 if (!subprocess_->Start(url, 5)) { | |
297 delete subprocess_; | |
298 subprocess_ = NULL; | |
299 return false; | |
300 } | |
301 | |
302 if (!InitCommunication(shm)) { | |
303 return false; | |
304 } | |
305 | |
306 PLUGIN_PRINTF(("ServiceRuntime::Start was successful\n")); | |
307 return true; | |
308 } | |
309 | |
310 bool ServiceRuntime::Kill() { | |
311 return subprocess_->KillChild(); | |
312 } | |
313 | |
314 bool ServiceRuntime::Log(int severity, nacl::string msg) { | |
315 return runtime_channel_->Log(severity, msg); | |
316 } | |
317 | |
318 void ServiceRuntime::Shutdown() { | |
319 if (subprocess_ != NULL) { | |
320 Kill(); | |
321 } | |
322 // This waits for the upcall thread to exit so it must come after we | |
323 // terminate the subprocess. | |
324 plugin_->ShutdownMultimedia(); | |
325 | |
326 // Note that this does waitpid() to get rid of any zombie subprocess. | |
327 delete subprocess_; | |
328 subprocess_ = NULL; | |
329 | |
330 delete runtime_channel_; | |
331 runtime_channel_ = NULL; | |
332 } | |
333 | |
334 ServiceRuntime::~ServiceRuntime() { | |
335 PLUGIN_PRINTF(("ServiceRuntime::~ServiceRuntime(%p)\n", | |
336 static_cast<void*>(this))); | |
337 | |
338 // We do this just in case Terminate() was not called. | |
339 delete subprocess_; | |
340 delete runtime_channel_; | |
341 | |
342 if (async_receive_desc != NULL) { | |
343 async_receive_desc->Delete(); | |
344 } | |
345 if (async_send_desc != NULL) { | |
346 async_send_desc->Delete(); | |
347 } | |
348 } | |
349 | |
350 ScriptableHandle* ServiceRuntime::default_socket_address() const { | |
351 PLUGIN_PRINTF(("ServiceRuntime::default_socket_address(%p) = %p\n", | |
352 static_cast<void*>(const_cast<ServiceRuntime*>(this)), | |
353 static_cast<void*>(const_cast<ScriptableHandle*>( | |
354 default_socket_address_)))); | |
355 | |
356 return default_socket_address_; | |
357 } | |
358 | |
359 ScriptableHandle* ServiceRuntime::default_socket() const { | |
360 PLUGIN_PRINTF(("ServiceRuntime::default_socket(%p) = %p\n", | |
361 static_cast<void*>(const_cast<ServiceRuntime*>(this)), | |
362 static_cast<void*>(const_cast<ScriptableHandle*>( | |
363 default_socket_)))); | |
364 | |
365 return default_socket_; | |
366 } | |
367 | |
368 ScriptableHandle* ServiceRuntime::GetSocketAddress( | |
369 Plugin* plugin, | |
370 nacl::Handle channel) { | |
371 nacl::DescWrapper::MsgHeader header; | |
372 nacl::DescWrapper::MsgIoVec iovec[1]; | |
373 unsigned char bytes[NACL_ABI_IMC_USER_BYTES_MAX]; | |
374 nacl::DescWrapper* descs[NACL_ABI_IMC_USER_DESC_MAX]; | |
375 | |
376 PLUGIN_PRINTF(("ServiceRuntime::GetSocketAddress(%p, %p)\n", | |
377 static_cast<void*>(plugin), | |
378 reinterpret_cast<void*>(channel))); | |
379 | |
380 ScriptableHandle* retval = NULL; | |
381 nacl::DescWrapper* imc_desc = plugin->wrapper_factory()->MakeImcSock(channel); | |
382 // Set up to receive a message. | |
383 iovec[0].base = bytes; | |
384 iovec[0].length = NACL_ABI_IMC_USER_BYTES_MAX; | |
385 header.iov = iovec; | |
386 header.iov_length = NACL_ARRAY_SIZE(iovec); | |
387 header.ndescv = descs; | |
388 header.ndescv_length = NACL_ARRAY_SIZE(descs); | |
389 header.flags = 0; | |
390 // Receive the message. | |
391 ssize_t ret = imc_desc->RecvMsg(&header, 0); | |
392 // Check that there was exactly one descriptor passed. | |
393 if (0 > ret || 1 != header.ndescv_length) { | |
394 PLUGIN_PRINTF(("ServiceRuntime::GetSocketAddress: " | |
395 "message receive failed %" NACL_PRIdS " %" NACL_PRIdNACL_SIZE | |
396 " %" NACL_PRIdNACL_SIZE "\n", ret, | |
397 header.ndescv_length, | |
398 header.iov_length)); | |
399 goto cleanup; | |
400 } | |
401 PLUGIN_PRINTF(("ServiceRuntime::GetSocketAddress: " | |
402 "-X result descriptor (DescWrapper*) = %p\n", | |
403 static_cast<void*>(descs[0]))); | |
404 retval = browser_interface_->NewScriptableHandle( | |
405 SocketAddress::New(plugin, descs[0])); | |
406 cleanup: | |
407 imc_desc->Delete(); | |
408 PLUGIN_PRINTF((" returning %p\n", static_cast<void*>(retval))); | |
409 return retval; | |
410 } | |
411 | |
412 } // namespace plugin | |
OLD | NEW |