summary refs log tree commit diff
path: root/servers/src/jailed.rs
diff options
context:
space:
mode:
Diffstat (limited to 'servers/src/jailed.rs')
-rw-r--r--servers/src/jailed.rs142
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);
+    }
+}