1 // Copyright 2017 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/interface_provider_filtering.h"
6
7 #include <utility>
8
9 #include "base/bind.h"
10 #include "base/stl_util.h"
11 #include "base/strings/strcat.h"
12 #include "base/task/post_task.h"
13 #include "content/public/app/content_browser_manifest.h"
14 #include "content/public/browser/browser_task_traits.h"
15 #include "content/public/browser/browser_thread.h"
16 #include "content/public/browser/content_browser_client.h"
17 #include "content/public/common/content_client.h"
18 #include "content/public/common/service_names.mojom.h"
19 #include "mojo/public/cpp/bindings/message.h"
20 #include "mojo/public/cpp/bindings/pending_remote.h"
21 #include "mojo/public/cpp/bindings/receiver.h"
22 #include "mojo/public/cpp/bindings/remote.h"
23 #include "services/service_manager/public/cpp/manifest.h"
24
25 namespace content {
26 namespace {
27
28 bool g_bypass_interface_filtering_for_testing = false;
29
30 // The capability name used in the browser manifest for each renderer-exposed
31 // interface filter.
32 const char kRendererCapabilityName[] = "renderer";
33
GetInterfacesForSpec(const char * spec)34 service_manager::Manifest::InterfaceNameSet GetInterfacesForSpec(
35 const char* spec) {
36 service_manager::Manifest manifest = GetContentBrowserManifest();
37 base::Optional<service_manager::Manifest> overlay =
38 GetContentClient()->browser()->GetServiceManifestOverlay(
39 mojom::kBrowserServiceName);
40 if (overlay)
41 manifest.Amend(*overlay);
42 const auto& filters = manifest.exposed_interface_filter_capabilities;
43 auto filter_iter = filters.find(spec);
44 if (filter_iter == filters.end())
45 return {};
46 const auto& capabilities = filter_iter->second;
47 auto capability_iter = capabilities.find(kRendererCapabilityName);
48 if (capability_iter == capabilities.end())
49 return {};
50 return capability_iter->second;
51 }
52
53 // A simple InterfaceProvider implementation which forwards to another
54 // InterfaceProvider after applying a named interface filter for
55 // renderer-exposed interfaces in the browser.
56 class InterfaceFilterImpl : public service_manager::mojom::InterfaceProvider {
57 public:
58 InterfaceFilterImpl(const InterfaceFilterImpl&) = delete;
59 ~InterfaceFilterImpl() override = default;
60 InterfaceFilterImpl& operator=(const InterfaceFilterImpl&) = delete;
61
Create(service_manager::Manifest::InterfaceNameSet allowed_interfaces,mojo::PendingReceiver<service_manager::mojom::InterfaceProvider> receiver,mojo::PendingRemote<service_manager::mojom::InterfaceProvider> target)62 static void Create(
63 service_manager::Manifest::InterfaceNameSet allowed_interfaces,
64 mojo::PendingReceiver<service_manager::mojom::InterfaceProvider> receiver,
65 mojo::PendingRemote<service_manager::mojom::InterfaceProvider> target) {
66 // Owns itself. Destroyed when either InterfaceProvider endpoint is
67 // disconnected.
68 new InterfaceFilterImpl(std::move(allowed_interfaces), std::move(receiver),
69 std::move(target));
70 }
71
72 // service_manager::mojom::InterfaceFilter implementation:
GetInterface(const std::string & interface_name,mojo::ScopedMessagePipeHandle interface_pipe)73 void GetInterface(const std::string& interface_name,
74 mojo::ScopedMessagePipeHandle interface_pipe) override {
75 if (!base::Contains(allowed_interfaces_, interface_name)) {
76 mojo::ReportBadMessage(
77 base::StrCat({"Interface not allowed: ", interface_name}));
78 return;
79 }
80
81 target_->GetInterface(interface_name, std::move(interface_pipe));
82 }
83
84 private:
InterfaceFilterImpl(service_manager::Manifest::InterfaceNameSet allowed_interfaces,mojo::PendingReceiver<service_manager::mojom::InterfaceProvider> receiver,mojo::PendingRemote<service_manager::mojom::InterfaceProvider> target)85 InterfaceFilterImpl(
86 service_manager::Manifest::InterfaceNameSet allowed_interfaces,
87 mojo::PendingReceiver<service_manager::mojom::InterfaceProvider> receiver,
88 mojo::PendingRemote<service_manager::mojom::InterfaceProvider> target)
89 : allowed_interfaces_(std::move(allowed_interfaces)),
90 receiver_(this, std::move(receiver)),
91 target_(std::move(target)) {
92 receiver_.set_disconnect_handler(base::BindOnce(
93 &InterfaceFilterImpl::DeleteThis, base::Unretained(this)));
94 target_.set_disconnect_handler(base::BindOnce(
95 &InterfaceFilterImpl::DeleteThis, base::Unretained(this)));
96 }
97
DeleteThis()98 void DeleteThis() { delete this; }
99
100 const service_manager::Manifest::InterfaceNameSet allowed_interfaces_;
101 mojo::Receiver<service_manager::mojom::InterfaceProvider> receiver_;
102 mojo::Remote<service_manager::mojom::InterfaceProvider> target_;
103 };
104
105 } // namespace
106
107 mojo::PendingReceiver<service_manager::mojom::InterfaceProvider>
FilterRendererExposedInterfaces(const char * spec,int process_id,mojo::PendingReceiver<service_manager::mojom::InterfaceProvider> receiver)108 FilterRendererExposedInterfaces(
109 const char* spec,
110 int process_id,
111 mojo::PendingReceiver<service_manager::mojom::InterfaceProvider> receiver) {
112 if (g_bypass_interface_filtering_for_testing)
113 return receiver;
114
115 mojo::PendingRemote<service_manager::mojom::InterfaceProvider> provider;
116 auto filtered_receiver = provider.InitWithNewPipeAndPassReceiver();
117
118 service_manager::Manifest::InterfaceNameSet allowed_interfaces =
119 GetInterfacesForSpec(spec);
120 if (!BrowserThread::CurrentlyOn(BrowserThread::IO)) {
121 base::PostTask(FROM_HERE, {BrowserThread::IO},
122 base::BindOnce(&InterfaceFilterImpl::Create,
123 std::move(allowed_interfaces),
124 std::move(receiver), std::move(provider)));
125 } else {
126 InterfaceFilterImpl::Create(std::move(allowed_interfaces),
127 std::move(receiver), std::move(provider));
128 }
129 return filtered_receiver;
130 }
131
132 namespace test {
133
ScopedInterfaceFilterBypass()134 ScopedInterfaceFilterBypass::ScopedInterfaceFilterBypass() {
135 // Nesting not supported.
136 DCHECK(!g_bypass_interface_filtering_for_testing);
137 g_bypass_interface_filtering_for_testing = true;
138 }
139
~ScopedInterfaceFilterBypass()140 ScopedInterfaceFilterBypass::~ScopedInterfaceFilterBypass() {
141 g_bypass_interface_filtering_for_testing = false;
142 }
143
144 } // namespace test
145
146 } // namespace content
147