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