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 <stddef.h>
8 
9 #include <string>
10 #include <utility>
11 
12 #include "base/base_switches.h"
13 #include "base/bind.h"
14 #include "base/command_line.h"
15 #include "base/files/file_path.h"
16 #include "base/metrics/field_trial.h"
17 #include "base/stl_util.h"
18 #include "base/strings/string_number_conversions.h"
19 #include "base/strings/utf_string_conversions.h"
20 #include "build/build_config.h"
21 #include "content/browser/browser_child_process_host_impl.h"
22 #include "content/browser/plugin_service_impl.h"
23 #include "content/browser/renderer_host/render_message_filter.h"
24 #include "content/common/child_process_host_impl.h"
25 #include "content/common/content_switches_internal.h"
26 #include "content/public/browser/browser_task_traits.h"
27 #include "content/public/browser/browser_thread.h"
28 #include "content/public/browser/content_browser_client.h"
29 #include "content/public/browser/network_service_instance.h"
30 #include "content/public/common/content_client.h"
31 #include "content/public/common/content_constants.h"
32 #include "content/public/common/content_switches.h"
33 #include "content/public/common/pepper_plugin_info.h"
34 #include "content/public/common/process_type.h"
35 #include "content/public/common/sandboxed_process_launcher_delegate.h"
36 #include "content/public/common/zygote/zygote_buildflags.h"
37 #include "ppapi/proxy/ppapi_messages.h"
38 #include "ppapi/shared_impl/ppapi_permissions.h"
39 #include "sandbox/policy/sandbox_type.h"
40 #include "sandbox/policy/switches.h"
41 #include "services/network/public/cpp/network_connection_tracker.h"
42 #include "ui/base/ui_base_switches.h"
43 
44 #if defined(OS_WIN)
45 #include "base/win/windows_version.h"
46 #include "sandbox/policy/win/sandbox_win.h"
47 #include "sandbox/win/src/process_mitigations.h"
48 #include "sandbox/win/src/sandbox_policy.h"
49 #include "ui/display/win/dpi.h"
50 #include "ui/gfx/font_render_params.h"
51 #endif
52 
53 #if BUILDFLAG(USE_ZYGOTE_HANDLE)
54 #include "content/public/common/zygote/zygote_handle.h"  // nogncheck
55 #endif
56 
57 namespace content {
58 
59 // NOTE: changes to this class need to be reviewed by the security team.
60 class PpapiPluginSandboxedProcessLauncherDelegate
61     : public content::SandboxedProcessLauncherDelegate {
62  public:
PpapiPluginSandboxedProcessLauncherDelegate(const ppapi::PpapiPermissions & permissions)63   PpapiPluginSandboxedProcessLauncherDelegate(
64       const ppapi::PpapiPermissions& permissions)
65 #if defined(OS_WIN)
66       : permissions_(permissions)
67 #endif
68   {
69   }
70 
~PpapiPluginSandboxedProcessLauncherDelegate()71   ~PpapiPluginSandboxedProcessLauncherDelegate() override {}
72 
73 #if defined(OS_WIN)
PreSpawnTarget(sandbox::TargetPolicy * policy)74   bool PreSpawnTarget(sandbox::TargetPolicy* policy) override {
75     // The Pepper process is as locked-down as a renderer except that it can
76     // create the server side of Chrome pipes.
77     sandbox::ResultCode result;
78     result = policy->AddRule(sandbox::TargetPolicy::SUBSYS_NAMED_PIPES,
79                              sandbox::TargetPolicy::NAMEDPIPES_ALLOW_ANY,
80                              L"\\\\.\\pipe\\chrome.*");
81     if (result != sandbox::SBOX_ALL_OK)
82       return false;
83 
84     content::ContentBrowserClient* browser_client =
85         GetContentClient()->browser();
86 
87 #if !defined(NACL_WIN64)
88     // We don't support PPAPI win32k lockdown prior to Windows 10.
89     if (base::win::GetVersion() >= base::win::Version::WIN10) {
90       result =
91           sandbox::policy::SandboxWin::AddWin32kLockdownPolicy(policy, true);
92       if (result != sandbox::SBOX_ALL_OK)
93         return false;
94     }
95 #endif  // !defined(NACL_WIN64)
96     const base::string16& sid =
97         browser_client->GetAppContainerSidForSandboxType(GetSandboxType());
98     if (!sid.empty())
99       sandbox::policy::SandboxWin::AddAppContainerPolicy(policy, sid.c_str());
100 
101     // No plugins can generate executable code.
102     sandbox::MitigationFlags flags = policy->GetDelayedProcessMitigations();
103     flags |= sandbox::MITIGATION_DYNAMIC_CODE_DISABLE;
104     if (sandbox::SBOX_ALL_OK != policy->SetDelayedProcessMitigations(flags))
105       return false;
106 
107     return true;
108   }
109 #endif  // OS_WIN
110 
111 #if BUILDFLAG(USE_ZYGOTE_HANDLE)
GetZygote()112   ZygoteHandle GetZygote() override {
113     const base::CommandLine& browser_command_line =
114         *base::CommandLine::ForCurrentProcess();
115     base::CommandLine::StringType plugin_launcher = browser_command_line
116         .GetSwitchValueNative(switches::kPpapiPluginLauncher);
117     if (!plugin_launcher.empty())
118       return nullptr;
119     return GetGenericZygote();
120   }
121 #endif  // BUILDFLAG(USE_ZYGOTE_HANDLE)
122 
GetSandboxType()123   sandbox::policy::SandboxType GetSandboxType() override {
124     return sandbox::policy::SandboxType::kPpapi;
125   }
126 
127 #if defined(OS_MAC)
DisclaimResponsibility()128   bool DisclaimResponsibility() override { return true; }
129 #endif
130 
131  private:
132 #if defined(OS_WIN)
133   const ppapi::PpapiPermissions permissions_;
134 #endif
135 
136   DISALLOW_COPY_AND_ASSIGN(PpapiPluginSandboxedProcessLauncherDelegate);
137 };
138 
139 class PpapiPluginProcessHost::PluginNetworkObserver
140     : public network::NetworkConnectionTracker::NetworkConnectionObserver {
141  public:
PluginNetworkObserver(PpapiPluginProcessHost * process_host)142   explicit PluginNetworkObserver(PpapiPluginProcessHost* process_host)
143       : process_host_(process_host), network_connection_tracker_(nullptr) {
144     GetNetworkConnectionTrackerFromUIThread(
145         base::BindOnce(&PluginNetworkObserver::SetNetworkConnectionTracker,
146                        weak_factory_.GetWeakPtr()));
147   }
148 
SetNetworkConnectionTracker(network::NetworkConnectionTracker * network_connection_tracker)149   void SetNetworkConnectionTracker(
150       network::NetworkConnectionTracker* network_connection_tracker) {
151     DCHECK(network_connection_tracker);
152     network_connection_tracker_ = network_connection_tracker;
153     network_connection_tracker_->AddNetworkConnectionObserver(this);
154   }
155 
~PluginNetworkObserver()156   ~PluginNetworkObserver() override {
157     if (network_connection_tracker_)
158       network_connection_tracker_->RemoveNetworkConnectionObserver(this);
159   }
160 
OnConnectionChanged(network::mojom::ConnectionType type)161   void OnConnectionChanged(network::mojom::ConnectionType type) override {
162     process_host_->Send(new PpapiMsg_SetNetworkState(
163         type != network::mojom::ConnectionType::CONNECTION_NONE));
164   }
165 
166  private:
167   PpapiPluginProcessHost* const process_host_;
168   network::NetworkConnectionTracker* network_connection_tracker_;
169   base::WeakPtrFactory<PluginNetworkObserver> weak_factory_{this};
170 };
171 
~PpapiPluginProcessHost()172 PpapiPluginProcessHost::~PpapiPluginProcessHost() {
173   DVLOG(1) << "PpapiPluginProcessHost"
174            << "~PpapiPluginProcessHost()";
175   CancelRequests();
176 }
177 
178 // static
CreatePluginHost(const PepperPluginInfo & info,const base::FilePath & profile_data_directory,const base::Optional<url::Origin> & origin_lock)179 PpapiPluginProcessHost* PpapiPluginProcessHost::CreatePluginHost(
180     const PepperPluginInfo& info,
181     const base::FilePath& profile_data_directory,
182     const base::Optional<url::Origin>& origin_lock) {
183   PpapiPluginProcessHost* plugin_host =
184       new PpapiPluginProcessHost(info, profile_data_directory, origin_lock);
185   if (plugin_host->Init(info))
186     return plugin_host;
187 
188   NOTREACHED();  // Init is not expected to fail.
189   return nullptr;
190 }
191 
192 // static
DidCreateOutOfProcessInstance(int plugin_process_id,int32_t pp_instance,const PepperRendererInstanceData & instance_data)193 void PpapiPluginProcessHost::DidCreateOutOfProcessInstance(
194     int plugin_process_id,
195     int32_t pp_instance,
196     const PepperRendererInstanceData& instance_data) {
197   for (PpapiPluginProcessHostIterator iter; !iter.Done(); ++iter) {
198     if (iter->process_.get() &&
199         iter->process_->GetData().id == plugin_process_id) {
200       // Found the plugin.
201       iter->host_impl_->AddInstance(pp_instance, instance_data);
202       return;
203     }
204   }
205   // We'll see this passed with a 0 process ID for the browser tag stuff that
206   // is currently in the process of being removed.
207   //
208   // TODO(brettw) When old browser tag impl is removed
209   // (PepperPluginDelegateImpl::CreateBrowserPluginModule passes a 0 plugin
210   // process ID) this should be converted to a NOTREACHED().
211   DCHECK(plugin_process_id == 0)
212       << "Renderer sent a bad plugin process host ID";
213 }
214 
215 // static
DidDeleteOutOfProcessInstance(int plugin_process_id,int32_t pp_instance)216 void PpapiPluginProcessHost::DidDeleteOutOfProcessInstance(
217     int plugin_process_id,
218     int32_t pp_instance) {
219   for (PpapiPluginProcessHostIterator iter; !iter.Done(); ++iter) {
220     if (iter->process_.get() &&
221         iter->process_->GetData().id == plugin_process_id) {
222       // Found the plugin.
223       iter->host_impl_->DeleteInstance(pp_instance);
224       return;
225     }
226   }
227   // Note: It's possible that the plugin process has already been deleted by
228   // the time this message is received. For example, it could have crashed.
229   // That's OK, we can just ignore this message.
230 }
231 
232 // static
FindByName(const base::string16 & name,std::vector<PpapiPluginProcessHost * > * hosts)233 void PpapiPluginProcessHost::FindByName(
234     const base::string16& name,
235     std::vector<PpapiPluginProcessHost*>* hosts) {
236   for (PpapiPluginProcessHostIterator iter; !iter.Done(); ++iter) {
237     if (iter->process_.get() && iter->process_->GetData().name == name)
238       hosts->push_back(*iter);
239   }
240 }
241 
Send(IPC::Message * message)242 bool PpapiPluginProcessHost::Send(IPC::Message* message) {
243   return process_->Send(message);
244 }
245 
OpenChannelToPlugin(Client * client)246 void PpapiPluginProcessHost::OpenChannelToPlugin(Client* client) {
247   if (process_->GetHost()->IsChannelOpening()) {
248     // The channel is already in the process of being opened.  Put
249     // this "open channel" request into a queue of requests that will
250     // be run once the channel is open.
251     pending_requests_.push_back(client);
252     return;
253   }
254 
255   // We already have an open channel, send a request right away to plugin.
256   RequestPluginChannel(client);
257 }
258 
PpapiPluginProcessHost(const PepperPluginInfo & info,const base::FilePath & profile_data_directory,const base::Optional<url::Origin> & origin_lock)259 PpapiPluginProcessHost::PpapiPluginProcessHost(
260     const PepperPluginInfo& info,
261     const base::FilePath& profile_data_directory,
262     const base::Optional<url::Origin>& origin_lock)
263     : profile_data_directory_(profile_data_directory),
264       origin_lock_(origin_lock) {
265   uint32_t base_permissions = info.permissions;
266 
267   // We don't have to do any whitelisting for APIs in this process host, so
268   // don't bother passing a browser context or document url here.
269   if (GetContentClient()->browser()->IsPluginAllowedToUseDevChannelAPIs(nullptr,
270                                                                         GURL()))
271     base_permissions |= ppapi::PERMISSION_DEV_CHANNEL;
272   permissions_ = ppapi::PpapiPermissions::GetForCommandLine(base_permissions);
273 
274   process_ = std::make_unique<BrowserChildProcessHostImpl>(
275       PROCESS_TYPE_PPAPI_PLUGIN, this, ChildProcessHost::IpcMode::kNormal);
276 
277   host_impl_ = std::make_unique<BrowserPpapiHostImpl>(
278       this, permissions_, info.name, info.path, profile_data_directory,
279       false /* in_process */, false /* external_plugin */);
280 
281   filter_ = new PepperMessageFilter();
282   process_->AddFilter(filter_.get());
283   process_->GetHost()->AddFilter(host_impl_->message_filter().get());
284 
285   GetContentClient()->browser()->DidCreatePpapiPlugin(host_impl_.get());
286 
287   // Only request network status updates if the plugin has dev permissions.
288   if (permissions_.HasPermission(ppapi::PERMISSION_DEV))
289     network_observer_ = std::make_unique<PluginNetworkObserver>(this);
290 }
291 
Init(const PepperPluginInfo & info)292 bool PpapiPluginProcessHost::Init(const PepperPluginInfo& info) {
293   plugin_path_ = info.path;
294   if (info.name.empty()) {
295     process_->SetName(plugin_path_.BaseName().LossyDisplayName());
296   } else {
297     process_->SetName(base::UTF8ToUTF16(info.name));
298   }
299 
300   process_->GetHost()->CreateChannelMojo();
301 
302   const base::CommandLine& browser_command_line =
303       *base::CommandLine::ForCurrentProcess();
304   base::CommandLine::StringType plugin_launcher =
305       browser_command_line.GetSwitchValueNative(switches::kPpapiPluginLauncher);
306 
307 #if defined(OS_LINUX) || defined(OS_CHROMEOS) || defined(OS_BSD)
308   int flags = plugin_launcher.empty() ? ChildProcessHost::CHILD_ALLOW_SELF :
309                                         ChildProcessHost::CHILD_NORMAL;
310 #else
311   // Plugins can't generate executable code.
312   int flags = ChildProcessHost::CHILD_NORMAL;
313 #endif
314   base::FilePath exe_path = ChildProcessHost::GetChildPath(flags);
315   if (exe_path.empty()) {
316     VLOG(1) << "Pepper plugin exe path is empty.";
317     return false;
318   }
319 
320   std::unique_ptr<base::CommandLine> cmd_line =
321       std::make_unique<base::CommandLine>(exe_path);
322   cmd_line->AppendSwitchASCII(switches::kProcessType,
323                               switches::kPpapiPluginProcess);
324   BrowserChildProcessHostImpl::CopyFeatureAndFieldTrialFlags(cmd_line.get());
325   BrowserChildProcessHostImpl::CopyTraceStartupFlags(cmd_line.get());
326 
327 #if defined(OS_WIN)
328   cmd_line->AppendArg(switches::kPrefetchArgumentPpapi);
329 #endif  // defined(OS_WIN)
330 
331   // These switches are forwarded to plugin pocesses.
332   static const char* const kCommonForwardSwitches[] = {
333     switches::kVModule
334   };
335   cmd_line->CopySwitchesFrom(browser_command_line, kCommonForwardSwitches,
336                              base::size(kCommonForwardSwitches));
337 
338   static const char* const kPluginForwardSwitches[] = {
339     sandbox::policy::switches::kDisableSeccompFilterSandbox,
340     sandbox::policy::switches::kNoSandbox,
341 #if defined(OS_MAC)
342     sandbox::policy::switches::kEnableSandboxLogging,
343 #endif
344     switches::kPpapiStartupDialog,
345   };
346   cmd_line->CopySwitchesFrom(browser_command_line, kPluginForwardSwitches,
347                              base::size(kPluginForwardSwitches));
348 
349   std::string locale = GetContentClient()->browser()->GetApplicationLocale();
350   if (!locale.empty()) {
351     // Pass on the locale so the plugin will know what language we're using.
352     cmd_line->AppendSwitchASCII(switches::kLang, locale);
353   }
354 
355 #if defined(OS_WIN)
356   cmd_line->AppendSwitchASCII(
357       switches::kDeviceScaleFactor,
358       base::NumberToString(display::win::GetDPIScale()));
359   const gfx::FontRenderParams font_params =
360       gfx::GetFontRenderParams(gfx::FontRenderParamsQuery(), nullptr);
361   cmd_line->AppendSwitchASCII(switches::kPpapiAntialiasedTextEnabled,
362                               base::NumberToString(font_params.antialiasing));
363   cmd_line->AppendSwitchASCII(
364       switches::kPpapiSubpixelRenderingSetting,
365       base::NumberToString(font_params.subpixel_rendering));
366 #endif
367 
368   if (!plugin_launcher.empty())
369     cmd_line->PrependWrapper(plugin_launcher);
370 
371   // On posix, only use the zygote if we are not using a plugin launcher -
372   // having a plugin launcher means we need to use another process instead of
373   // just forking the zygote.
374   process_->Launch(
375       std::make_unique<PpapiPluginSandboxedProcessLauncherDelegate>(
376           permissions_),
377       std::move(cmd_line), true);
378   return true;
379 }
380 
RequestPluginChannel(Client * client)381 void PpapiPluginProcessHost::RequestPluginChannel(Client* client) {
382   base::ProcessHandle process_handle = base::kNullProcessHandle;
383   int renderer_child_id = base::kNullProcessId;
384   client->GetPpapiChannelInfo(&process_handle, &renderer_child_id);
385 
386   base::ProcessId process_id = base::kNullProcessId;
387   if (process_handle != base::kNullProcessHandle) {
388     // This channel is not used by the browser itself.
389     process_id = base::GetProcId(process_handle);
390     CHECK_NE(base::kNullProcessId, process_id);
391   }
392 
393   // We can't send any sync messages from the browser because it might lead to
394   // a hang. See the similar code in PluginProcessHost for more description.
395   PpapiMsg_CreateChannel* msg = new PpapiMsg_CreateChannel(
396       process_id, renderer_child_id, client->Incognito());
397   msg->set_unblock(true);
398   if (Send(msg)) {
399     sent_requests_.push(client);
400   } else {
401     client->OnPpapiChannelOpened(IPC::ChannelHandle(), base::kNullProcessId, 0);
402   }
403 }
404 
OnProcessLaunched()405 void PpapiPluginProcessHost::OnProcessLaunched() {
406   VLOG(2) << "ppapi plugin process launched.";
407   host_impl_->set_plugin_process(process_->GetProcess().Duplicate());
408 }
409 
OnProcessCrashed(int exit_code)410 void PpapiPluginProcessHost::OnProcessCrashed(int exit_code) {
411   VLOG(1) << "ppapi plugin process crashed.";
412   GetUIThreadTaskRunner({})->PostTask(
413       FROM_HERE,
414       base::BindOnce(&PluginServiceImpl::RegisterPluginCrash,
415                      base::Unretained(PluginServiceImpl::GetInstance()),
416                      plugin_path_));
417 }
418 
OnMessageReceived(const IPC::Message & msg)419 bool PpapiPluginProcessHost::OnMessageReceived(const IPC::Message& msg) {
420   bool handled = true;
421   IPC_BEGIN_MESSAGE_MAP(PpapiPluginProcessHost, msg)
422     IPC_MESSAGE_HANDLER(PpapiHostMsg_ChannelCreated,
423                         OnRendererPluginChannelCreated)
424     IPC_MESSAGE_UNHANDLED(handled = false)
425   IPC_END_MESSAGE_MAP()
426   DCHECK(handled);
427   return handled;
428 }
429 
430 // Called when the browser <--> plugin channel has been established.
OnChannelConnected(int32_t peer_pid)431 void PpapiPluginProcessHost::OnChannelConnected(int32_t peer_pid) {
432   // This will actually load the plugin. Errors will actually not be reported
433   // back at this point. Instead, the plugin will fail to establish the
434   // connections when we request them on behalf of the renderer(s).
435   Send(new PpapiMsg_LoadPlugin(plugin_path_, permissions_));
436 
437   // Process all pending channel requests from the renderers.
438   for (size_t i = 0; i < pending_requests_.size(); i++)
439     RequestPluginChannel(pending_requests_[i]);
440   pending_requests_.clear();
441 }
442 
443 // Called when the browser <--> plugin channel has an error. This normally
444 // means the plugin has crashed.
OnChannelError()445 void PpapiPluginProcessHost::OnChannelError() {
446   VLOG(1) << "PpapiPluginProcessHost"
447           << "::OnChannelError()";
448   // We don't need to notify the renderers that were communicating with the
449   // plugin since they have their own channels which will go into the error
450   // state at the same time. Instead, we just need to notify any renderers
451   // that have requested a connection but have not yet received one.
452   CancelRequests();
453 }
454 
CancelRequests()455 void PpapiPluginProcessHost::CancelRequests() {
456   DVLOG(1) << "PpapiPluginProcessHost"
457            << "CancelRequests()";
458   for (size_t i = 0; i < pending_requests_.size(); i++) {
459     pending_requests_[i]->OnPpapiChannelOpened(IPC::ChannelHandle(),
460                                                base::kNullProcessId, 0);
461   }
462   pending_requests_.clear();
463 
464   while (!sent_requests_.empty()) {
465     sent_requests_.front()->OnPpapiChannelOpened(IPC::ChannelHandle(),
466                                                  base::kNullProcessId, 0);
467     sent_requests_.pop();
468   }
469 }
470 
471 // Called when a new plugin <--> renderer channel has been created.
OnRendererPluginChannelCreated(const IPC::ChannelHandle & channel_handle)472 void PpapiPluginProcessHost::OnRendererPluginChannelCreated(
473     const IPC::ChannelHandle& channel_handle) {
474   if (sent_requests_.empty())
475     return;
476 
477   // All requests should be processed FIFO, so the next item in the
478   // sent_requests_ queue should be the one that the plugin just created.
479   Client* client = sent_requests_.front();
480   sent_requests_.pop();
481 
482   const ChildProcessData& data = process_->GetData();
483   client->OnPpapiChannelOpened(channel_handle, data.GetProcess().Pid(),
484                                data.id);
485 }
486 
487 }  // namespace content
488