| 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 |