1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175
// Copyright 2015 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
//! Sandbox profiles—lists of permitted operations.
use platform;
use std::path::PathBuf;
/// A sandbox profile, which specifies the set of operations that this process is allowed to
/// perform. Operations not in the list are implicitly prohibited.
///
/// If the process attempts to perform an operation in the list that this platform can prohibit
/// after the sandbox is entered via `activate()`, the operation will either fail or the process
/// will be immediately terminated. You can check whether an operation can be prohibited on this
/// platform with `Operation::prohibition_support()`.
///
/// All profiles implicitly prohibit *at least* the following operations. Future versions of `gaol`
/// may add operations to selectively allow these.
///
/// * Opening any file for writing.
///
/// * Creating new processes.
///
/// * Opening named pipes or System V IPC resources.
///
/// * Accessing System V semaphores.
///
/// * Sending signals to other processes.
///
/// * Tracing other processes.
///
/// * Accepting inbound network connections.
///
/// * Any operation that requires superuser privileges on the current operating system.
///
/// All profiles implicitly *allow* the following operations:
///
/// * All pure computation (user-mode CPU instructions that do not cause a context switch to
/// supervisor mode).
///
/// * Memory allocation (for example, via `brk` or anonymous `mmap` on Unix).
///
/// * Use of synchronization primitives (mutexes, condition variables).
///
/// * Changing memory protection and use policies: for example, marking pages non-writable or
/// informing the kernel that memory pages may be discarded. (It may be possible to restrict
/// this in future versions.)
///
/// * Spawning new threads.
///
/// * Responding to signals (e.g. `signal`, `sigaltstack`).
///
/// * Read, write, and memory map of already-opened file descriptors or handles.
///
/// * Determining how much has been sent on a file descriptor.
///
/// * Sending or receiving on already-opened sockets, including control messages on Unix.
///
/// * I/O multiplexing on already-opened sockets and/or file descriptors (`select`/`poll`).
///
/// * Opening and closing file descriptors and sockets (but not necessarily connecting them
/// to anything).
///
/// * Determining the user ID.
///
/// * Querying and altering thread scheduling options such as CPU affinity.
///
/// * Exiting the process.
///
/// Because of platform limitations, patterns within one profile are not permitted to overlap; the
/// behavior is undefined if they do. For example, you may not allow metadata reads of the subpath
/// rooted at `/dev` while allowing full reads of `/dev/null`; you must instead allow full reads of
/// `/dev` or make the profile more restrictive.
#[derive(Clone, Debug)]
pub struct Profile {
allowed_operations: Vec<Operation>,
}
/// An operation that this process is allowed to perform.
#[derive(Clone, Debug)]
pub enum Operation {
/// All file-related reading operations may be performed on this file.
FileReadAll(PathPattern),
/// Metadata (for example, `stat` or `readlink`) of this file may be read.
FileReadMetadata(PathPattern),
/// Outbound network connections to the given address may be initiated.
NetworkOutbound(AddressPattern),
/// System information may be read (via `sysctl` on Unix).
SystemInfoRead,
/// Platform-specific operations.
PlatformSpecific(platform::Operation),
}
/// Describes a path or paths on the filesystem.
#[derive(Clone, Debug)]
pub enum PathPattern {
/// One specific path.
Literal(PathBuf),
/// A directory and all of its contents, recursively.
Subpath(PathBuf),
}
/// Describes a network address.
#[derive(Clone, Debug)]
pub enum AddressPattern {
/// All network addresses.
All,
/// TCP connections on the given port.
Tcp(u16),
/// A local socket at the given path (for example, a Unix socket).
LocalSocket(PathBuf),
}
impl Profile {
/// Creates a new profile with the given set of allowed operations.
///
/// If the operations cannot be allowed precisely on this platform, this returns an error. You
/// can then inspect the operations via `OperationSupport::support()` to see which ones cannot
/// be allowed and modify the set of allowed operations as necessary. We are deliberately
/// strict here to reduce the probability of applications accidentally allowing operations due
/// to platform limitations.
pub fn new(allowed_operations: Vec<Operation>) -> Result<Profile,()> {
if allowed_operations.iter().all(|operation| {
match operation.support() {
OperationSupportLevel::NeverAllowed | OperationSupportLevel::CanBeAllowed => true,
OperationSupportLevel::CannotBeAllowedPrecisely |
OperationSupportLevel::AlwaysAllowed => false,
}
}) {
Ok(Profile {
allowed_operations: allowed_operations,
})
} else {
Err(())
}
}
/// Returns the list of allowed operations.
pub fn allowed_operations(&self) -> &[Operation] {
self.allowed_operations.as_slice()
}
}
/// How precisely an operation can be allowed on this platform.
#[derive(Copy, Clone, Debug, PartialEq)]
pub enum OperationSupportLevel {
/// This operation is never allowed on this platform.
NeverAllowed,
/// This operation can be precisely allowed on this platform.
CanBeAllowed,
/// This operation cannot be allowed precisely on this platform, but another set of operations
/// allows it to be allowed on a more coarse-grained level. For example, on Linux, it is not
/// possible to allow access to specific ports, but it is possible to allow network access
/// entirely.
CannotBeAllowedPrecisely,
/// This operation is always allowed on this platform.
AlwaysAllowed,
}
/// Allows operations to be queried to determine how precisely they can be allowed on this
/// platform.
pub trait OperationSupport {
/// Returns an `OperationSupportLevel` describing how well this operation can be allowed on
/// this platform.
fn support(&self) -> OperationSupportLevel;
}