blob: 74e2be292ba1acfa7be3bbd18e14dc986df7cc34 [file] [log] [blame]
// Copyright 2024 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.cti
use std::sync::Arc;
use ::routing::RouteRequest;
use anyhow::Context;
use async_trait::async_trait;
use cm_types::Name;
use cm_util::TaskGroup;
use fidl::endpoints::ServerEnd;
use fidl_fuchsia_component as fcomponent;
use fuchsia_zircon as zx;
use futures::TryStreamExt;
use lazy_static::lazy_static;
use moniker::{ExtendedMoniker, Moniker, MonikerBase};
use routing::{capability_source::InternalCapability, error::RoutingError, policy::PolicyError};
use tracing::warn;
use vfs::directory::entry::OpenRequest;
use crate::{
capability::{CapabilityProvider, FrameworkCapability, InternalCapabilityProvider},
model::{
component::WeakComponentInstance,
error::CapabilityProviderError,
routing::report_routing_failure,
token::{InstanceRegistry, InstanceToken},
},
};
lazy_static! {
static ref INTROSPECTOR_SERVICE: Name = "fuchsia.component.Introspector".parse().unwrap();
static ref DEBUG_REQUEST: RouteRequest = RouteRequest::UseProtocol(cm_rust::UseProtocolDecl {
source: cm_rust::UseSource::Framework,
source_name: INTROSPECTOR_SERVICE.clone(),
source_dictionary: Default::default(),
target_path: cm_types::Path::new("/null").unwrap(),
dependency_type: cm_rust::DependencyType::Strong,
availability: Default::default(),
});
}
struct IntrospectorCapability {
scope_moniker: Moniker,
instance_registry: Arc<InstanceRegistry>,
}
impl IntrospectorCapability {
pub fn new(scope_moniker: Moniker, instance_registry: Arc<InstanceRegistry>) -> Self {
Self { scope_moniker, instance_registry }
}
pub async fn serve(
&self,
mut stream: fcomponent::IntrospectorRequestStream,
) -> Result<(), anyhow::Error> {
while let Some(request) = stream.try_next().await? {
let method_name = request.method_name();
self.handle_request(request)
.await
.with_context(|| format!("Error handling Introspector method {method_name}"))?;
}
Ok(())
}
async fn handle_request(
&self,
request: fcomponent::IntrospectorRequest,
) -> Result<(), fidl::Error> {
match request {
fcomponent::IntrospectorRequest::GetMoniker { component_instance, responder } => {
let token = InstanceToken::from(component_instance);
let Some(Ok(moniker)) = self
.instance_registry
.get(&token)
.map(|moniker| moniker.strip_prefix(&self.scope_moniker))
else {
return responder.send(Err(fcomponent::Error::InstanceNotFound));
};
return responder.send(Ok(&moniker.to_string()));
}
fcomponent::IntrospectorRequest::_UnknownMethod {
ordinal,
control_handle: _,
method_type,
..
} => {
warn!(%ordinal, "Unknown {method_type:?} Introspector method");
Ok(())
}
}
}
}
#[async_trait]
impl InternalCapabilityProvider for IntrospectorCapability {
async fn open_protocol(self: Box<Self>, server_end: zx::Channel) {
let server_end = ServerEnd::<fcomponent::IntrospectorMarker>::new(server_end);
let serve_result = self.serve(server_end.into_stream().unwrap()).await;
if let Err(error) = serve_result {
warn!(%error, "Error serving Introspector");
}
}
}
pub struct IntrospectorFrameworkCapability {
pub instance_registry: Arc<InstanceRegistry>,
}
impl FrameworkCapability for IntrospectorFrameworkCapability {
fn matches(&self, capability: &InternalCapability) -> bool {
capability.matches_protocol(&INTROSPECTOR_SERVICE)
}
fn new_provider(
&self,
scope: WeakComponentInstance,
target: WeakComponentInstance,
) -> Box<dyn CapabilityProvider> {
lazy_static! {
static ref MEMORY_MONITOR: Moniker =
Moniker::parse_str("/core/memory_monitor").unwrap();
/// Moniker for integration tests.
static ref RECEIVER: Moniker =
Moniker::parse_str("receiver").unwrap();
};
// TODO(https://fxbug.dev/318904493): Temporary workaround to prevent other components from
// using `Introspector` while improvements to framework capability allowlists are under way.
//
// In production, the capability is minted at `/`, then offered to `/core/memory_monitor`.
//
// In the `introspector-integration-test`, the capability is minted at some test specific
// realm, then exposed from `/`.
//
// All other cases are disallowed.
if target.moniker != *MEMORY_MONITOR
&& target.moniker != *RECEIVER
&& !target.moniker.is_root()
{
return Box::new(AccessDeniedCapabilityProvider {
target,
source_moniker: scope.moniker,
});
}
Box::new(IntrospectorCapability::new(scope.moniker.clone(), self.instance_registry.clone()))
}
}
// TODO(https://fxbug.dev/318904493): Remove this.
struct AccessDeniedCapabilityProvider {
target: WeakComponentInstance,
source_moniker: Moniker,
}
#[async_trait]
impl CapabilityProvider for AccessDeniedCapabilityProvider {
async fn open(
self: Box<Self>,
_task_group: TaskGroup,
_open_request: OpenRequest<'_>,
) -> Result<(), CapabilityProviderError> {
let err = RoutingError::from(PolicyError::CapabilityUseDisallowed {
cap: INTROSPECTOR_SERVICE.to_string(),
source_moniker: ExtendedMoniker::ComponentInstance(self.source_moniker),
target_moniker: self.target.moniker.clone(),
});
if let Ok(target) = self.target.upgrade() {
report_routing_failure(&DEBUG_REQUEST, &target, &err).await;
}
Err(err.into())
}
}