blob: e042417a753ed86827120d2a177d3a59f1255f1a [file] [log] [blame]
// Copyright 2022 The Chromium OS 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 "mojo_service_manager/daemon/service_manager.h"
#include <set>
#include <string>
#include <utility>
#include <base/check.h>
#include "mojo_service_manager/daemon/mojo_error_util.h"
namespace chromeos {
namespace mojo_service_manager {
namespace {
template <typename T>
void ResetRemoteWithReason(mojo::PendingRemote<T> pending_remote,
mojom::ErrorCode error,
const std::string& message) {
// TODO(crbug/1310274): Currently |PendingRemote| doesn't support
// ResetWithReason.
mojo::Remote<T> remote(std::move(pending_remote));
remote.ResetWithReason(static_cast<uint32_t>(error), message);
}
} // namespace
ServiceManager::ServiceManager(Configuration configuration,
ServicePolicyMap policy_map)
: configuration_(std::move(configuration)) {
for (auto& item : policy_map) {
auto& [service_name, policy] = item;
service_map_[service_name].policy = std::move(policy);
}
}
ServiceManager::~ServiceManager() = default;
void ServiceManager::AddReceiver(
mojom::ProcessIdentityPtr process_identity,
mojo::PendingReceiver<mojom::ServiceManager> receiver) {
receiver_set_.Add(this, std::move(receiver), std::move(process_identity));
}
void ServiceManager::Register(
const std::string& service_name,
mojo::PendingRemote<mojom::ServiceProvider> service_provider) {
auto it = service_map_.find(service_name);
if (it == service_map_.end()) {
if (!configuration_.is_permissive) {
ResetRemoteWithReason(std::move(service_provider),
mojom::ErrorCode::kServiceNotFound,
"Cannot find service: " + service_name);
return;
}
// In permissive mode, users are allowed to register a service which is not
// in the policy. In this case, a new ServiceState needs to be created.
auto [it_new, success] = service_map_.try_emplace(service_name);
CHECK(success);
it = it_new;
}
ServiceState& service_state = it->second;
const mojom::ProcessIdentityPtr& identity = receiver_set_.current_context();
if (!configuration_.is_permissive &&
!service_state.policy.IsOwner(identity->security_context)) {
ResetRemoteWithReason(
std::move(service_provider), mojom::ErrorCode::kPermissionDenied,
"The security context: " + identity->security_context +
" is not allowed to own the service: " + service_name);
return;
}
if (service_state.service_provider.is_bound()) {
ResetRemoteWithReason(
std::move(service_provider),
mojom::ErrorCode::kServiceHasBeenRegistered,
"The service: " + service_name + " has already been registered.");
return;
}
service_state.service_provider.Bind(std::move(service_provider));
service_state.service_provider.set_disconnect_handler(
base::BindOnce(&ServiceManager::ServiceProviderDisconnectHandler,
weak_factory_.GetWeakPtr(), service_name));
service_state.owner = identity.Clone();
SendServiceEvent(
service_state.policy.requesters(),
mojom::ServiceEvent::New(mojom::ServiceEvent::Type::kRegistered,
service_name, identity.Clone()));
for (ServiceRequestQueue::ServiceRequest& request :
service_state.request_queue.TakeAllRequests()) {
// If a receiver become invalid before being posted, don't send it because
// the mojo will complain about sending invalid handles and reset the
// connection of service provider.
if (!request.receiver.is_valid())
continue;
service_state.service_provider->Request(std::move(request.identity),
std::move(request.receiver));
}
}
void ServiceManager::Request(const std::string& service_name,
std::optional<base::TimeDelta> timeout,
mojo::ScopedMessagePipeHandle receiver) {
auto it = service_map_.find(service_name);
if (it == service_map_.end()) {
if (!configuration_.is_permissive) {
ResetMojoReceiverPipeWithReason(std::move(receiver),
mojom::ErrorCode::kServiceNotFound,
"Cannot find service: " + service_name);
return;
}
// In permissive mode, users are allowed to request a service which is not
// in the policy. In this case, a new ServiceState needs to be created.
auto [it_new, success] = service_map_.try_emplace(service_name);
CHECK(success);
it = it_new;
}
ServiceState& service_state = it->second;
const mojom::ProcessIdentityPtr& identity = receiver_set_.current_context();
if (!configuration_.is_permissive &&
!service_state.policy.IsRequester(identity->security_context)) {
ResetMojoReceiverPipeWithReason(
std::move(receiver), mojom::ErrorCode::kPermissionDenied,
"The security context: " + identity->security_context +
" is not allowed to request the service: " + service_name);
return;
}
if (service_state.service_provider.is_bound()) {
service_state.service_provider->Request(identity.Clone(),
std::move(receiver));
return;
}
service_state.request_queue.Push(identity.Clone(), std::move(timeout),
std::move(receiver));
}
void ServiceManager::Query(const std::string& service_name,
QueryCallback callback) {
auto it = service_map_.find(service_name);
if (it == service_map_.end()) {
std::move(callback).Run(mojom::ErrorOrServiceState::NewError(
mojom::Error::New(mojom::ErrorCode::kServiceNotFound,
"Cannot find service: " + service_name)));
return;
}
const ServiceState& service_state = it->second;
const mojom::ProcessIdentityPtr& identity = receiver_set_.current_context();
if (!configuration_.is_permissive &&
!service_state.policy.IsRequester(identity->security_context)) {
std::move(callback).Run(
mojom::ErrorOrServiceState::NewError(mojom::Error::New(
mojom::ErrorCode::kPermissionDenied,
"The security context: " + identity->security_context +
" is not allowed to request the service: " + service_name)));
return;
}
std::move(callback).Run(
mojom::ErrorOrServiceState::NewState(mojom::ServiceState::New(
/*is_registered=*/!service_state.owner.is_null(),
/*owner=*/service_state.owner.Clone())));
}
void ServiceManager::AddServiceObserver(
mojo::PendingRemote<mojom::ServiceObserver> observer) {
const mojom::ProcessIdentityPtr& identity = receiver_set_.current_context();
service_observer_map_[identity->security_context].Add(std::move(observer));
}
void ServiceManager::ServiceProviderDisconnectHandler(
const std::string& service_name) {
auto it = service_map_.find(service_name);
CHECK(it != service_map_.end());
ServiceState& service_state = it->second;
service_state.service_provider.reset();
mojom::ProcessIdentityPtr dispatcher;
dispatcher.Swap(&service_state.owner);
SendServiceEvent(
service_state.policy.requesters(),
mojom::ServiceEvent::New(mojom::ServiceEvent::Type::kUnRegistered,
service_name, std::move(dispatcher)));
}
void ServiceManager::SendServiceEvent(const std::set<std::string>& requesters,
mojom::ServiceEventPtr event) {
if (configuration_.is_permissive) {
// In permissive mode, all the observer can receive the event.
for (const auto& item : service_observer_map_) {
for (const mojo::Remote<mojom::ServiceObserver>& remote : item.second) {
remote->OnServiceEvent(event.Clone());
}
}
return;
}
for (const std::string& security_context : requesters) {
auto it = service_observer_map_.find(security_context);
if (it == service_observer_map_.end())
continue;
for (const mojo::Remote<mojom::ServiceObserver>& remote : it->second) {
remote->OnServiceEvent(event.Clone());
}
}
}
} // namespace mojo_service_manager
} // namespace chromeos