diff options
Diffstat (limited to 'servers/src/jailed.rs')
-rw-r--r-- | servers/src/jailed.rs | 142 |
1 files changed, 142 insertions, 0 deletions
diff --git a/servers/src/jailed.rs b/servers/src/jailed.rs new file mode 100644 index 0000000..c633efb --- /dev/null +++ b/servers/src/jailed.rs @@ -0,0 +1,142 @@ +// Copyright 2020 Alyssa Ross. All rights reserved. +// Copyright 2017 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. + +use std::fmt::{self, Display, Formatter}; +use std::io; +use std::os::unix::prelude::*; +use std::time::Duration; + +use io_jail::Minijail; +use libc; +use msg_socket::{MsgError, MsgOnSocket, MsgReceiver, MsgSender, MsgSocket}; +use sys_util::net::UnixSeqpacket; +use sys_util::{error, warn}; + +use crate::Server; + +#[derive(Debug)] +pub enum Error { + ForkingJail(io_jail::Error), + Io(io::Error), +} + +impl Display for Error { + fn fmt(&self, f: &mut Formatter) -> fmt::Result { + use Error::*; + match self { + ForkingJail(e) => write!(f, "failed to fork jail process: {}", e), + Io(e) => write!(f, "IO error configuring proxy server: {}", e), + } + } +} + +impl std::error::Error for Error {} + +const SOCKET_TIMEOUT_MS: u64 = 2000; + +#[derive(Copy, Clone, Debug, Eq, MsgOnSocket, PartialEq)] +enum Event { + Activate, + Shutdown, +} + +#[derive(Debug)] +pub struct JailedServer<S> { + sock: MsgSocket<Event, ()>, + server: S, +} + +fn recv_ack(sock: &MsgSocket<(), Event>) -> Result<Event, MsgError> { + let event = sock.recv()?; + sock.send(&())?; + Ok(event) +} + +fn child_proc<S: Server>(sock: UnixSeqpacket, server: &mut S) -> Result<(), MsgError> { + let sock = MsgSocket::new(sock); + + if recv_ack(&sock)? == Event::Activate { + server.activate(); + + while recv_ack(&sock)? == Event::Activate { + warn!("server already activated"); + } + } + + eprintln!("Received shutdown"); + + // Event::Shutdown received. Time to stop. + Ok(()) +} + +impl<S: Server> JailedServer<S> { + pub fn jail(mut server: S, jail: &Minijail, mut keep_fds: Vec<RawFd>) -> Result<Self, Error> { + let (child_sock, parent_sock) = UnixSeqpacket::pair().map_err(Error::Io)?; + + keep_fds.push(child_sock.as_raw_fd()); + let pid = unsafe { jail.fork(Some(&keep_fds)) }.map_err(Error::ForkingJail)?; + + if pid == 0 { + server.on_sandboxed(); + if let Err(e) = child_proc(child_sock, &mut server) { + error!("child server process: {}", e); + } + + // We're explicitly not using std::process::exit here to avoid the cleanup of + // stdout/stderr globals. This can cause cascading panics and SIGILL if a worker + // thread attempts to log to stderr after at_exit handlers have been run. + // TODO(crbug.com/992494): Remove this once device shutdown ordering is clearly + // defined (or when corresponding code for devices has been removed). + // + // exit() is trivially safe. + // ! Never returns + unsafe { libc::exit(0) }; + } + + parent_sock + .set_write_timeout(Some(Duration::from_millis(SOCKET_TIMEOUT_MS))) + .map_err(Error::Io)?; + parent_sock + .set_read_timeout(Some(Duration::from_millis(SOCKET_TIMEOUT_MS))) + .map_err(Error::Io)?; + + Ok(Self { + server, + sock: MsgSocket::new(parent_sock), + }) + } +} + +impl<S> JailedServer<S> { + fn sync_send(&self, event: &Event) { + if let Err(e) = self.sock.send(event) { + error!("failed to send message to server: {}", e); + } + + if let Err(e) = self.sock.recv() { + error!("failed to receive ack from server: {}", e); + } + } +} + +impl<S> Drop for JailedServer<S> { + fn drop(&mut self) { + self.sync_send(&Event::Shutdown); + } +} + +impl<S> Server for JailedServer<S> { + fn keep_fds(&self) -> Vec<RawFd> { + panic!("Don't double jail servers!"); + } + + fn on_sandboxed(&mut self) { + panic!("Don't double jail servers!"); + } + + fn activate(&mut self) { + self.sync_send(&Event::Activate); + } +} |