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 3 // found in the LICENSE file. 4 5 #include "content/browser/ppapi_plugin_process_host.h" 6 7 #include <string> 8 9 #include "base/base_switches.h" 10 #include "base/command_line.h" 11 #include "base/files/file_path.h" 12 #include "base/metrics/field_trial.h" 13 #include "base/strings/utf_string_conversions.h" 14 #include "content/browser/browser_child_process_host_impl.h" 15 #include "content/browser/plugin_service_impl.h" 16 #include "content/browser/renderer_host/render_message_filter.h" 17 #include "content/common/child_process_host_impl.h" 18 #include "content/common/child_process_messages.h" 19 #include "content/public/browser/content_browser_client.h" 20 #include "content/public/common/content_constants.h" 21 #include "content/public/common/content_switches.h" 22 #include "content/public/common/pepper_plugin_info.h" 23 #include "content/public/common/process_type.h" 24 #include "content/public/common/sandboxed_process_launcher_delegate.h" 25 #include "ipc/ipc_switches.h" 26 #include "net/base/network_change_notifier.h" 27 #include "ppapi/proxy/ppapi_messages.h" 28 #include "ui/base/ui_base_switches.h" 29 30 #if defined(OS_WIN) 31 #include "content/common/sandbox_win.h" 32 #include "sandbox/win/src/sandbox_policy.h" 33 #endif 34 35 namespace content { 36 37 // NOTE: changes to this class need to be reviewed by the security team. 38 class PpapiPluginSandboxedProcessLauncherDelegate 39 : public content::SandboxedProcessLauncherDelegate { 40 public: 41 PpapiPluginSandboxedProcessLauncherDelegate(bool is_broker, 42 const PepperPluginInfo& info, 43 ChildProcessHost* host) 44 : 45 #if defined(OS_POSIX) 46 info_(info), 47 ipc_fd_(host->TakeClientFileDescriptor()), 48 #endif // OS_POSIX 49 is_broker_(is_broker) {} 50 51 virtual ~PpapiPluginSandboxedProcessLauncherDelegate() {} 52 53 #if defined(OS_WIN) 54 virtual bool ShouldSandbox() OVERRIDE { 55 return !is_broker_; 56 } 57 58 virtual void PreSpawnTarget(sandbox::TargetPolicy* policy, 59 bool* success) { 60 if (is_broker_) 61 return; 62 // The Pepper process as locked-down as a renderer execpt that it can 63 // create the server side of chrome pipes. 64 sandbox::ResultCode result; 65 result = policy->AddRule(sandbox::TargetPolicy::SUBSYS_NAMED_PIPES, 66 sandbox::TargetPolicy::NAMEDPIPES_ALLOW_ANY, 67 L"\\\\.\\pipe\\chrome.*"); 68 *success = (result == sandbox::SBOX_ALL_OK); 69 } 70 71 #elif defined(OS_POSIX) 72 virtual bool ShouldUseZygote() OVERRIDE { 73 const CommandLine& browser_command_line = *CommandLine::ForCurrentProcess(); 74 CommandLine::StringType plugin_launcher = browser_command_line 75 .GetSwitchValueNative(switches::kPpapiPluginLauncher); 76 return !is_broker_ && plugin_launcher.empty() && info_.is_sandboxed; 77 } 78 virtual int GetIpcFd() OVERRIDE { 79 return ipc_fd_; 80 } 81 #endif // OS_WIN 82 83 private: 84 #if defined(OS_POSIX) 85 const PepperPluginInfo& info_; 86 int ipc_fd_; 87 #endif // OS_POSIX 88 bool is_broker_; 89 90 DISALLOW_COPY_AND_ASSIGN(PpapiPluginSandboxedProcessLauncherDelegate); 91 }; 92 93 class PpapiPluginProcessHost::PluginNetworkObserver 94 : public net::NetworkChangeNotifier::IPAddressObserver, 95 public net::NetworkChangeNotifier::ConnectionTypeObserver { 96 public: 97 explicit PluginNetworkObserver(PpapiPluginProcessHost* process_host) 98 : process_host_(process_host) { 99 net::NetworkChangeNotifier::AddIPAddressObserver(this); 100 net::NetworkChangeNotifier::AddConnectionTypeObserver(this); 101 } 102 103 virtual ~PluginNetworkObserver() { 104 net::NetworkChangeNotifier::RemoveConnectionTypeObserver(this); 105 net::NetworkChangeNotifier::RemoveIPAddressObserver(this); 106 } 107 108 // IPAddressObserver implementation. 109 virtual void OnIPAddressChanged() OVERRIDE { 110 // TODO(brettw) bug 90246: This doesn't seem correct. The online/offline 111 // notification seems like it should be sufficient, but I don't see that 112 // when I unplug and replug my network cable. Sending this notification when 113 // "something" changes seems to make Flash reasonably happy, but seems 114 // wrong. We should really be able to provide the real online state in 115 // OnConnectionTypeChanged(). 116 process_host_->Send(new PpapiMsg_SetNetworkState(true)); 117 } 118 119 // ConnectionTypeObserver implementation. 120 virtual void OnConnectionTypeChanged( 121 net::NetworkChangeNotifier::ConnectionType type) OVERRIDE { 122 process_host_->Send(new PpapiMsg_SetNetworkState( 123 type != net::NetworkChangeNotifier::CONNECTION_NONE)); 124 } 125 126 private: 127 PpapiPluginProcessHost* const process_host_; 128 }; 129 130 PpapiPluginProcessHost::~PpapiPluginProcessHost() { 131 DVLOG(1) << "PpapiPluginProcessHost" << (is_broker_ ? "[broker]" : "") 132 << "~PpapiPluginProcessHost()"; 133 CancelRequests(); 134 } 135 136 // static 137 PpapiPluginProcessHost* PpapiPluginProcessHost::CreatePluginHost( 138 const PepperPluginInfo& info, 139 const base::FilePath& profile_data_directory) { 140 PpapiPluginProcessHost* plugin_host = new PpapiPluginProcessHost( 141 info, profile_data_directory); 142 DCHECK(plugin_host); 143 if (plugin_host->Init(info)) 144 return plugin_host; 145 146 NOTREACHED(); // Init is not expected to fail. 147 return NULL; 148 } 149 150 // static 151 PpapiPluginProcessHost* PpapiPluginProcessHost::CreateBrokerHost( 152 const PepperPluginInfo& info) { 153 PpapiPluginProcessHost* plugin_host = 154 new PpapiPluginProcessHost(); 155 if (plugin_host->Init(info)) 156 return plugin_host; 157 158 NOTREACHED(); // Init is not expected to fail. 159 return NULL; 160 } 161 162 // static 163 void PpapiPluginProcessHost::DidCreateOutOfProcessInstance( 164 int plugin_process_id, 165 int32 pp_instance, 166 const PepperRendererInstanceData& instance_data) { 167 for (PpapiPluginProcessHostIterator iter; !iter.Done(); ++iter) { 168 if (iter->process_.get() && 169 iter->process_->GetData().id == plugin_process_id) { 170 // Found the plugin. 171 iter->host_impl_->AddInstance(pp_instance, instance_data); 172 return; 173 } 174 } 175 // We'll see this passed with a 0 process ID for the browser tag stuff that 176 // is currently in the process of being removed. 177 // 178 // TODO(brettw) When old browser tag impl is removed 179 // (PepperPluginDelegateImpl::CreateBrowserPluginModule passes a 0 plugin 180 // process ID) this should be converted to a NOTREACHED(). 181 DCHECK(plugin_process_id == 0) 182 << "Renderer sent a bad plugin process host ID"; 183 } 184 185 // static 186 void PpapiPluginProcessHost::DidDeleteOutOfProcessInstance( 187 int plugin_process_id, 188 int32 pp_instance) { 189 for (PpapiPluginProcessHostIterator iter; !iter.Done(); ++iter) { 190 if (iter->process_.get() && 191 iter->process_->GetData().id == plugin_process_id) { 192 // Found the plugin. 193 iter->host_impl_->DeleteInstance(pp_instance); 194 return; 195 } 196 } 197 // Note: It's possible that the plugin process has already been deleted by 198 // the time this message is received. For example, it could have crashed. 199 // That's OK, we can just ignore this message. 200 } 201 202 // static 203 void PpapiPluginProcessHost::FindByName( 204 const base::string16& name, 205 std::vector<PpapiPluginProcessHost*>* hosts) { 206 for (PpapiPluginProcessHostIterator iter; !iter.Done(); ++iter) { 207 if (iter->process_.get() && iter->process_->GetData().name == name) 208 hosts->push_back(*iter); 209 } 210 } 211 212 bool PpapiPluginProcessHost::Send(IPC::Message* message) { 213 return process_->Send(message); 214 } 215 216 void PpapiPluginProcessHost::OpenChannelToPlugin(Client* client) { 217 if (process_->GetHost()->IsChannelOpening()) { 218 // The channel is already in the process of being opened. Put 219 // this "open channel" request into a queue of requests that will 220 // be run once the channel is open. 221 pending_requests_.push_back(client); 222 return; 223 } 224 225 // We already have an open channel, send a request right away to plugin. 226 RequestPluginChannel(client); 227 } 228 229 PpapiPluginProcessHost::PpapiPluginProcessHost( 230 const PepperPluginInfo& info, 231 const base::FilePath& profile_data_directory) 232 : profile_data_directory_(profile_data_directory), 233 is_broker_(false) { 234 uint32 base_permissions = info.permissions; 235 236 // We don't have to do any whitelisting for APIs in this process host, so 237 // don't bother passing a browser context or document url here. 238 if (GetContentClient()->browser()->IsPluginAllowedToUseDevChannelAPIs( 239 NULL, GURL())) 240 base_permissions |= ppapi::PERMISSION_DEV_CHANNEL; 241 permissions_ = ppapi::PpapiPermissions::GetForCommandLine(base_permissions); 242 243 process_.reset(new BrowserChildProcessHostImpl( 244 PROCESS_TYPE_PPAPI_PLUGIN, this)); 245 246 host_impl_.reset(new BrowserPpapiHostImpl(this, permissions_, info.name, 247 info.path, profile_data_directory, 248 false /* in_process */, 249 false /* external_plugin */)); 250 251 filter_ = new PepperMessageFilter(); 252 process_->AddFilter(filter_.get()); 253 process_->GetHost()->AddFilter(host_impl_->message_filter().get()); 254 255 GetContentClient()->browser()->DidCreatePpapiPlugin(host_impl_.get()); 256 257 // Only request network status updates if the plugin has dev permissions. 258 if (permissions_.HasPermission(ppapi::PERMISSION_DEV)) 259 network_observer_.reset(new PluginNetworkObserver(this)); 260 } 261 262 PpapiPluginProcessHost::PpapiPluginProcessHost() 263 : is_broker_(true) { 264 process_.reset(new BrowserChildProcessHostImpl( 265 PROCESS_TYPE_PPAPI_BROKER, this)); 266 267 ppapi::PpapiPermissions permissions; // No permissions. 268 // The plugin name, path and profile data directory shouldn't be needed for 269 // the broker. 270 host_impl_.reset(new BrowserPpapiHostImpl(this, permissions, 271 std::string(), base::FilePath(), 272 base::FilePath(), 273 false /* in_process */, 274 false /* external_plugin */)); 275 } 276 277 bool PpapiPluginProcessHost::Init(const PepperPluginInfo& info) { 278 plugin_path_ = info.path; 279 if (info.name.empty()) { 280 process_->SetName(plugin_path_.BaseName().LossyDisplayName()); 281 } else { 282 process_->SetName(base::UTF8ToUTF16(info.name)); 283 } 284 285 std::string channel_id = process_->GetHost()->CreateChannel(); 286 if (channel_id.empty()) { 287 VLOG(1) << "Could not create pepper host channel."; 288 return false; 289 } 290 291 const CommandLine& browser_command_line = *CommandLine::ForCurrentProcess(); 292 CommandLine::StringType plugin_launcher = 293 browser_command_line.GetSwitchValueNative(switches::kPpapiPluginLauncher); 294 295 #if defined(OS_LINUX) 296 int flags = plugin_launcher.empty() ? ChildProcessHost::CHILD_ALLOW_SELF : 297 ChildProcessHost::CHILD_NORMAL; 298 #else 299 int flags = ChildProcessHost::CHILD_NORMAL; 300 #endif 301 base::FilePath exe_path = ChildProcessHost::GetChildPath(flags); 302 if (exe_path.empty()) { 303 VLOG(1) << "Pepper plugin exe path is empty."; 304 return false; 305 } 306 307 CommandLine* cmd_line = new CommandLine(exe_path); 308 cmd_line->AppendSwitchASCII(switches::kProcessType, 309 is_broker_ ? switches::kPpapiBrokerProcess 310 : switches::kPpapiPluginProcess); 311 cmd_line->AppendSwitchASCII(switches::kProcessChannelID, channel_id); 312 313 // These switches are forwarded to both plugin and broker pocesses. 314 static const char* kCommonForwardSwitches[] = { 315 switches::kVModule 316 }; 317 cmd_line->CopySwitchesFrom(browser_command_line, kCommonForwardSwitches, 318 arraysize(kCommonForwardSwitches)); 319 320 if (!is_broker_) { 321 static const char* kPluginForwardSwitches[] = { 322 switches::kDisableSeccompFilterSandbox, 323 #if defined(OS_MACOSX) 324 switches::kEnableSandboxLogging, 325 #endif 326 switches::kNoSandbox, 327 switches::kPpapiStartupDialog, 328 }; 329 cmd_line->CopySwitchesFrom(browser_command_line, kPluginForwardSwitches, 330 arraysize(kPluginForwardSwitches)); 331 332 // Copy any flash args over and introduce field trials if necessary. 333 // TODO(vtl): Stop passing flash args in the command line, or windows is 334 // going to explode. 335 std::string field_trial = 336 base::FieldTrialList::FindFullName(kFlashHwVideoDecodeFieldTrialName); 337 std::string existing_args = 338 browser_command_line.GetSwitchValueASCII(switches::kPpapiFlashArgs); 339 if (field_trial == kFlashHwVideoDecodeFieldTrialEnabledName) { 340 // Arguments passed to Flash are comma delimited. 341 if (!existing_args.empty()) 342 existing_args.append(","); 343 existing_args.append("enable_hw_video_decode=1"); 344 } 345 cmd_line->AppendSwitchASCII(switches::kPpapiFlashArgs, existing_args); 346 } 347 348 std::string locale = GetContentClient()->browser()->GetApplicationLocale(); 349 if (!locale.empty()) { 350 // Pass on the locale so the plugin will know what language we're using. 351 cmd_line->AppendSwitchASCII(switches::kLang, locale); 352 } 353 354 if (!plugin_launcher.empty()) 355 cmd_line->PrependWrapper(plugin_launcher); 356 357 // On posix, never use the zygote for the broker. Also, only use the zygote if 358 // the plugin is sandboxed, and we are not using a plugin launcher - having a 359 // plugin launcher means we need to use another process instead of just 360 // forking the zygote. 361 #if defined(OS_POSIX) 362 if (!info.is_sandboxed) 363 cmd_line->AppendSwitchASCII(switches::kNoSandbox, std::string()); 364 #endif // OS_POSIX 365 process_->Launch( 366 new PpapiPluginSandboxedProcessLauncherDelegate(is_broker_, 367 info, 368 process_->GetHost()), 369 cmd_line); 370 return true; 371 } 372 373 void PpapiPluginProcessHost::RequestPluginChannel(Client* client) { 374 base::ProcessHandle process_handle; 375 int renderer_child_id; 376 client->GetPpapiChannelInfo(&process_handle, &renderer_child_id); 377 378 base::ProcessId process_id = (process_handle == base::kNullProcessHandle) ? 379 0 : base::GetProcId(process_handle); 380 381 // We can't send any sync messages from the browser because it might lead to 382 // a hang. See the similar code in PluginProcessHost for more description. 383 PpapiMsg_CreateChannel* msg = new PpapiMsg_CreateChannel( 384 process_id, renderer_child_id, client->OffTheRecord()); 385 msg->set_unblock(true); 386 if (Send(msg)) { 387 sent_requests_.push(client); 388 } else { 389 client->OnPpapiChannelOpened(IPC::ChannelHandle(), base::kNullProcessId, 0); 390 } 391 } 392 393 void PpapiPluginProcessHost::OnProcessLaunched() { 394 VLOG(2) << "ppapi plugin process launched."; 395 host_impl_->set_plugin_process_handle(process_->GetHandle()); 396 } 397 398 void PpapiPluginProcessHost::OnProcessCrashed(int exit_code) { 399 VLOG(1) << "ppapi plugin process crashed."; 400 PluginServiceImpl::GetInstance()->RegisterPluginCrash(plugin_path_); 401 } 402 403 bool PpapiPluginProcessHost::OnMessageReceived(const IPC::Message& msg) { 404 bool handled = true; 405 IPC_BEGIN_MESSAGE_MAP(PpapiPluginProcessHost, msg) 406 IPC_MESSAGE_HANDLER(PpapiHostMsg_ChannelCreated, 407 OnRendererPluginChannelCreated) 408 IPC_MESSAGE_UNHANDLED(handled = false) 409 IPC_END_MESSAGE_MAP() 410 DCHECK(handled); 411 return handled; 412 } 413 414 // Called when the browser <--> plugin channel has been established. 415 void PpapiPluginProcessHost::OnChannelConnected(int32 peer_pid) { 416 // This will actually load the plugin. Errors will actually not be reported 417 // back at this point. Instead, the plugin will fail to establish the 418 // connections when we request them on behalf of the renderer(s). 419 Send(new PpapiMsg_LoadPlugin(plugin_path_, permissions_)); 420 421 // Process all pending channel requests from the renderers. 422 for (size_t i = 0; i < pending_requests_.size(); i++) 423 RequestPluginChannel(pending_requests_[i]); 424 pending_requests_.clear(); 425 } 426 427 // Called when the browser <--> plugin channel has an error. This normally 428 // means the plugin has crashed. 429 void PpapiPluginProcessHost::OnChannelError() { 430 VLOG(1) << "PpapiPluginProcessHost" << (is_broker_ ? "[broker]" : "") 431 << "::OnChannelError()"; 432 // We don't need to notify the renderers that were communicating with the 433 // plugin since they have their own channels which will go into the error 434 // state at the same time. Instead, we just need to notify any renderers 435 // that have requested a connection but have not yet received one. 436 CancelRequests(); 437 } 438 439 void PpapiPluginProcessHost::CancelRequests() { 440 DVLOG(1) << "PpapiPluginProcessHost" << (is_broker_ ? "[broker]" : "") 441 << "CancelRequests()"; 442 for (size_t i = 0; i < pending_requests_.size(); i++) { 443 pending_requests_[i]->OnPpapiChannelOpened(IPC::ChannelHandle(), 444 base::kNullProcessId, 0); 445 } 446 pending_requests_.clear(); 447 448 while (!sent_requests_.empty()) { 449 sent_requests_.front()->OnPpapiChannelOpened(IPC::ChannelHandle(), 450 base::kNullProcessId, 0); 451 sent_requests_.pop(); 452 } 453 } 454 455 // Called when a new plugin <--> renderer channel has been created. 456 void PpapiPluginProcessHost::OnRendererPluginChannelCreated( 457 const IPC::ChannelHandle& channel_handle) { 458 if (sent_requests_.empty()) 459 return; 460 461 // All requests should be processed FIFO, so the next item in the 462 // sent_requests_ queue should be the one that the plugin just created. 463 Client* client = sent_requests_.front(); 464 sent_requests_.pop(); 465 466 const ChildProcessData& data = process_->GetData(); 467 client->OnPpapiChannelOpened(channel_handle, base::GetProcId(data.handle), 468 data.id); 469 } 470 471 } // namespace content 472