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