| // Copyright 2020 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. |
| |
| //! The broker daemon that supports Trichecus from within the Chrome OS guest machine. |
| |
| use std::cell::RefCell; |
| use std::env; |
| use std::fmt::{self, Debug, Display}; |
| use std::rc::Rc; |
| use std::thread::spawn; |
| use std::time::Duration; |
| |
| use dbus::arg::OwnedFd; |
| use dbus::blocking::LocalConnection; |
| use dbus::tree::{self, Interface, MTFn}; |
| use sys_util::{error, info, syslog}; |
| |
| use libchromeos::vsock::VMADDR_PORT_ANY; |
| use libsirenia::communication::{read_message, write_message}; |
| use libsirenia::transport::{ |
| self, ClientTransport, IPClientTransport, Transport, TransportRead, TransportType, |
| TransportWrite, VsockClientTransport, DEFAULT_CLIENT_PORT, |
| }; |
| use sirenia::build_info::BUILD_TIMESTAMP; |
| use sirenia::cli::initialize_common_arguments; |
| use sirenia::communication::{AppInfo, Request, Response}; |
| use sirenia::server::{org_chromium_mana_teeinterface_server, OrgChromiumManaTEEInterface}; |
| |
| #[derive(Debug)] |
| pub enum Error { |
| /// Failed to start D-Bus connection. |
| ConnectionRequest(dbus::Error), |
| /// Error registering D-Bus connection |
| DbusRegister(dbus::Error), |
| /// Error processing a D-Bus message. |
| ProcessMessage(dbus::Error), |
| /// Failed to start up the syslog. |
| SysLog(sys_util::syslog::Error), |
| /// Failed to connect to the socket |
| TransportConnection(transport::Error), |
| } |
| |
| impl Display for Error { |
| fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { |
| use self::Error::*; |
| |
| match self { |
| ConnectionRequest(e) => write!(f, "failed to open D-Bus connection: {}", e), |
| DbusRegister(e) => write!(f, "failed to register D-Bus handler: {}", e), |
| ProcessMessage(e) => write!(f, "failed to process the D-Bus message: {}", e), |
| SysLog(e) => write!(f, "failed to start up the syslog: {}", e), |
| TransportConnection(e) => write!(f, "failed to connect to socket: {}", e), |
| } |
| } |
| } |
| |
| /// The result of an operation in this crate. |
| pub type Result<T> = std::result::Result<T, Error>; |
| |
| #[derive(Copy, Clone, Default, Debug)] |
| struct TData; |
| impl tree::DataType for TData { |
| type Tree = (); |
| type ObjectPath = Rc<DugongDevice>; |
| type Property = (); |
| type Interface = (); |
| type Method = (); |
| type Signal = (); |
| } |
| |
| // TODO: May need to add more state at some point. |
| #[derive(Debug)] |
| struct DugongDevice { |
| w: RefCell<Box<dyn TransportWrite>>, |
| transport_type: TransportType, |
| } |
| |
| impl OrgChromiumManaTEEInterface for DugongDevice { |
| fn start_teeapplication( |
| &self, |
| app_id: &str, |
| ) -> std::result::Result<(i32, (OwnedFd, OwnedFd)), tree::MethodErr> { |
| info!("Got request to start up: {}", app_id); |
| let fds = request_start_tee_app(self, app_id); |
| match fds { |
| Ok(fds) => Ok((0, fds)), |
| Err(e) => Err(tree::MethodErr::failed(&e)), |
| } |
| } |
| } |
| |
| fn request_start_tee_app(device: &DugongDevice, app_id: &str) -> Result<(OwnedFd, OwnedFd)> { |
| // TODO: Need to bind to the new port to prevent other processes from using |
| // it, but need to add the option to bind to an ephemeral port in vsock |
| let app_info = AppInfo { |
| app_id: String::from(app_id), |
| port_number: 0, // TODO: Will use this later |
| }; |
| match write_message(&mut *device.w.borrow_mut(), Request::StartSession(app_info)) { |
| Ok(()) => (), |
| Err(e) => error!("Error writing: {}", e), |
| } |
| let mut transport = open_connection(&device.transport_type, None); |
| match transport.connect() { |
| Ok(Transport { r, w, id: _ }) => unsafe { |
| // This is safe because into_raw_fd transfers the ownership to OwnedFd. |
| Ok((OwnedFd::new(r.into_raw_fd()), OwnedFd::new(w.into_raw_fd()))) |
| }, |
| Err(err) => Err(Error::TransportConnection(err)), |
| } |
| } |
| |
| pub fn start_dbus_handler(w: Box<dyn TransportWrite>, transport_type: TransportType) -> Result<()> { |
| let c = LocalConnection::new_system().map_err(Error::ConnectionRequest)?; |
| c.request_name( |
| "org.chromium.ManaTEE", |
| false, /*allow_replacement*/ |
| false, /*replace_existing*/ |
| false, /*do_not_queue*/ |
| ) |
| .map_err(Error::ConnectionRequest)?; |
| let f = tree::Factory::new_fn(); |
| let interface: Interface<MTFn<TData>, TData> = |
| org_chromium_mana_teeinterface_server(&f, (), |m| { |
| let a: &Rc<DugongDevice> = m.path.get_data(); |
| let b: &DugongDevice = &a; |
| b |
| }); |
| |
| let tree = f.tree(()).add( |
| f.object_path( |
| "/org/chromium/ManaTEE1", |
| Rc::new(DugongDevice { |
| w: RefCell::new(w), |
| transport_type, |
| }), |
| ) |
| .introspectable() |
| .add(interface), |
| ); |
| |
| tree.start_receive(&c); |
| info!("Finished dbus setup, starting handler."); |
| loop { |
| c.process(Duration::from_millis(1000)) |
| .map_err(Error::ProcessMessage)?; |
| } |
| } |
| |
| fn main() -> Result<()> { |
| let args: Vec<String> = env::args().collect(); |
| let config = initialize_common_arguments(&args[1..]).unwrap(); |
| let transport_type = config.connection_type; |
| if let Err(e) = syslog::init() { |
| eprintln!("failed to initialize syslog: {}", e); |
| return Err(e).map_err(Error::SysLog); |
| } |
| |
| info!("Starting dugong: {}", BUILD_TIMESTAMP); |
| info!("Opening connection to trichechus"); |
| let mut transport = open_connection(&transport_type, Some(DEFAULT_CLIENT_PORT)); |
| |
| if let Ok(Transport { r, w, id: _ }) = transport.connect() { |
| info!("Starting rpc"); |
| start_rpc(transport_type, r, w); |
| } else { |
| error!("transport connect failed"); |
| } |
| |
| // TODO: If it gets here is something screwed up? |
| Ok(()) |
| } |
| |
| fn open_connection(connection_type: &TransportType, port: Option<u32>) -> Box<dyn ClientTransport> { |
| match connection_type { |
| TransportType::IpConnection(url) => { |
| Box::new(IPClientTransport::new(&url, port.unwrap_or(0) as u16).unwrap()) |
| } |
| TransportType::VsockConnection(url) => { |
| Box::new(VsockClientTransport::new(&url, port.unwrap_or(VMADDR_PORT_ANY)).unwrap()) |
| } |
| _ => panic!("unexpected connection type"), |
| } |
| } |
| |
| fn start_rpc(transport_type: TransportType, r: Box<dyn TransportRead>, w: Box<dyn TransportWrite>) { |
| info!("Opening connection to trichechus"); |
| // Right now just send the message to start up the shell and print and log |
| // responses from trichechus |
| let logger_child = spawn(move || { |
| info!("starting logger"); |
| start_logger(r); |
| }); |
| |
| let dbus_child = spawn(move || { |
| info!("starting dbus handler"); |
| start_dbus_handler(w, transport_type).unwrap(); |
| }); |
| logger_child.join().unwrap(); |
| dbus_child.join().unwrap(); |
| |
| // TODO: What happens if these joins finish? These should be running |
| // forever. How do we recover? |
| } |
| fn start_logger(mut r: Box<dyn TransportRead>) { |
| loop { |
| let message = read_message(&mut r).unwrap(); |
| match message { |
| Response::LogInfo(s) => { |
| info!("{}", s); |
| println!("{}", s); |
| } |
| Response::LogError(s) => { |
| error!("{}", s); |
| println!("{}", s); |
| } |
| } |
| } |
| } |