blob: 527629bad0aaf00f575254bb4d9993e3704a908d [file] [log] [blame]
// Copyright 2017 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "content/browser/interface_provider_filtering.h"
#include <utility>
#include "base/bind.h"
#include "base/stl_util.h"
#include "base/strings/strcat.h"
#include "base/task/post_task.h"
#include "content/public/app/content_browser_manifest.h"
#include "content/public/browser/browser_task_traits.h"
#include "content/public/browser/browser_thread.h"
#include "content/public/browser/content_browser_client.h"
#include "content/public/common/content_client.h"
#include "content/public/common/service_names.mojom.h"
#include "mojo/public/cpp/bindings/message.h"
#include "mojo/public/cpp/bindings/pending_remote.h"
#include "mojo/public/cpp/bindings/receiver.h"
#include "mojo/public/cpp/bindings/remote.h"
#include "services/service_manager/public/cpp/manifest.h"
namespace content {
namespace {
bool g_bypass_interface_filtering_for_testing = false;
// The capability name used in the browser manifest for each renderer-exposed
// interface filter.
const char kRendererCapabilityName[] = "renderer";
service_manager::Manifest::InterfaceNameSet GetInterfacesForSpec(
const char* spec) {
service_manager::Manifest manifest = GetContentBrowserManifest();
base::Optional<service_manager::Manifest> overlay =
GetContentClient()->browser()->GetServiceManifestOverlay(
mojom::kBrowserServiceName);
if (overlay)
manifest.Amend(*overlay);
const auto& filters = manifest.exposed_interface_filter_capabilities;
auto filter_iter = filters.find(spec);
if (filter_iter == filters.end())
return {};
const auto& capabilities = filter_iter->second;
auto capability_iter = capabilities.find(kRendererCapabilityName);
if (capability_iter == capabilities.end())
return {};
return capability_iter->second;
}
// A simple InterfaceProvider implementation which forwards to another
// InterfaceProvider after applying a named interface filter for
// renderer-exposed interfaces in the browser.
class InterfaceFilterImpl : public service_manager::mojom::InterfaceProvider {
public:
InterfaceFilterImpl(const InterfaceFilterImpl&) = delete;
~InterfaceFilterImpl() override = default;
InterfaceFilterImpl& operator=(const InterfaceFilterImpl&) = delete;
static void Create(
service_manager::Manifest::InterfaceNameSet allowed_interfaces,
mojo::PendingReceiver<service_manager::mojom::InterfaceProvider> receiver,
mojo::PendingRemote<service_manager::mojom::InterfaceProvider> target) {
// Owns itself. Destroyed when either InterfaceProvider endpoint is
// disconnected.
new InterfaceFilterImpl(std::move(allowed_interfaces), std::move(receiver),
std::move(target));
}
// service_manager::mojom::InterfaceFilter implementation:
void GetInterface(const std::string& interface_name,
mojo::ScopedMessagePipeHandle interface_pipe) override {
if (!base::Contains(allowed_interfaces_, interface_name)) {
mojo::ReportBadMessage(
base::StrCat({"Interface not allowed: ", interface_name}));
return;
}
target_->GetInterface(interface_name, std::move(interface_pipe));
}
private:
InterfaceFilterImpl(
service_manager::Manifest::InterfaceNameSet allowed_interfaces,
mojo::PendingReceiver<service_manager::mojom::InterfaceProvider> receiver,
mojo::PendingRemote<service_manager::mojom::InterfaceProvider> target)
: allowed_interfaces_(std::move(allowed_interfaces)),
receiver_(this, std::move(receiver)),
target_(std::move(target)) {
receiver_.set_disconnect_handler(base::BindOnce(
&InterfaceFilterImpl::DeleteThis, base::Unretained(this)));
target_.set_disconnect_handler(base::BindOnce(
&InterfaceFilterImpl::DeleteThis, base::Unretained(this)));
}
void DeleteThis() { delete this; }
const service_manager::Manifest::InterfaceNameSet allowed_interfaces_;
mojo::Receiver<service_manager::mojom::InterfaceProvider> receiver_;
mojo::Remote<service_manager::mojom::InterfaceProvider> target_;
};
} // namespace
mojo::PendingReceiver<service_manager::mojom::InterfaceProvider>
FilterRendererExposedInterfaces(
const char* spec,
int process_id,
mojo::PendingReceiver<service_manager::mojom::InterfaceProvider> receiver) {
if (g_bypass_interface_filtering_for_testing)
return receiver;
mojo::PendingRemote<service_manager::mojom::InterfaceProvider> provider;
auto filtered_receiver = provider.InitWithNewPipeAndPassReceiver();
service_manager::Manifest::InterfaceNameSet allowed_interfaces =
GetInterfacesForSpec(spec);
if (!BrowserThread::CurrentlyOn(BrowserThread::IO)) {
base::PostTask(FROM_HERE, {BrowserThread::IO},
base::BindOnce(&InterfaceFilterImpl::Create,
std::move(allowed_interfaces),
std::move(receiver), std::move(provider)));
} else {
InterfaceFilterImpl::Create(std::move(allowed_interfaces),
std::move(receiver), std::move(provider));
}
return filtered_receiver;
}
namespace test {
ScopedInterfaceFilterBypass::ScopedInterfaceFilterBypass() {
// Nesting not supported.
DCHECK(!g_bypass_interface_filtering_for_testing);
g_bypass_interface_filtering_for_testing = true;
}
ScopedInterfaceFilterBypass::~ScopedInterfaceFilterBypass() {
g_bypass_interface_filtering_for_testing = false;
}
} // namespace test
} // namespace content