Skip to content

Commit

Permalink
Use pledge(2) on OpenBSD to restrict system calls (#1264)
Browse files Browse the repository at this point in the history
Limit the attack surface of spotifyd, an internet facing network daemon
with read/write filesystem access and options to execute arbitrary
commands.

Most importantly, prevent fork(2)/execve(2) unless `onevent` is used.

While the set of runtime promises retains full read/write filesystem as
well as network access (for now), it does exclude a number of groups of
unused system calls -- the manual[0] for details.

OpenBSD's official package has been shipping this patch for a month by
now without any regressions or reports of breakage.

0: https://man.openbsd.org/pledge.2
  • Loading branch information
klemensn committed Feb 18, 2024
1 parent 663d067 commit ff2f7a0
Show file tree
Hide file tree
Showing 3 changed files with 54 additions and 0 deletions.
10 changes: 10 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 3 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,9 @@ syslog = "6"
[target."cfg(target_os = \"macos\")".dependencies]
whoami = "1"

[target."cfg(target_os = \"openbsd\")".dependencies]
pledge = "0.4.2"

[dev-dependencies]
env_logger = "0.10"

Expand Down
41 changes: 41 additions & 0 deletions src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ use daemonize::Daemonize;
#[cfg(unix)]
use log::error;
use log::{info, trace, LevelFilter};
#[cfg(target_os = "openbsd")]
use pledge::pledge;
#[cfg(windows)]
use std::fs;
use structopt::StructOpt;
Expand Down Expand Up @@ -85,6 +87,15 @@ fn setup_logger(log_target: LogTarget, verbose: bool) -> eyre::Result<()> {
}

fn main() -> eyre::Result<()> {
// Start with superset of all potentially required promises.
// Drop later after CLI arguments and configuration files were parsed.
#[cfg(target_os = "openbsd")]
pledge(
"stdio rpath wpath cpath inet mcast flock chown unix dns proc exec audio",
None,
)
.unwrap();

color_eyre::install().wrap_err("Couldn't initialize error reporting")?;

let mut cli_config: CliConfig = CliConfig::from_args();
Expand Down Expand Up @@ -166,6 +177,36 @@ fn main() -> eyre::Result<()> {
}
}

#[cfg(target_os = "openbsd")]
{
// At this point:
// * --username-cmd, --password-cmd were handled
// > no "proc exec"
// * --pid, daemon(3) were handled
// > no "cpath flock chown" for PID file
// > no "proc" for double-fork(2)
//
// Required runtime promises:
// stdout/err, syslog(3) "stdio"
// ${TMPDIR}/.tmp*, cache "[rwc]path"
// Spotify API/Connect "inet dns"
// D-Bus, MPRIS "unix"
// Zeroconf Discovery "mcast"
// PortAudio, sio_open(3) ("[rwc]path unix inet audio")
// > after sndio(7) cookie "audio"

// --on-song-change-hook aka. "onevent", run via --shell aka. "shell"
if internal_config.onevent.is_some() {
pledge(
"stdio rpath wpath cpath inet mcast unix dns proc exec audio",
None,
)
.unwrap();
} else {
pledge("stdio rpath wpath cpath inet mcast unix dns audio", None).unwrap();
}
}

let runtime = Runtime::new().unwrap();
runtime.block_on(async {
let mut initial_state = setup::initial_state(internal_config);
Expand Down

0 comments on commit ff2f7a0

Please sign in to comment.