summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorDavid Lönnhager <david.l@mullvad.net>2020-03-18 11:17:59 +0100
committerDavid Lönnhager <david.l@mullvad.net>2020-06-02 10:05:02 +0200
commit0961e1096cabc07916124c7c56987b959756c0d3 (patch)
tree7932a52c8ae553ec958c6ee3dd931176bfaf75b7
parent36c1dd005e79e3d2beb9bfb04235150e7e82b34c (diff)
downloadmullvadvpn-0961e1096cabc07916124c7c56987b959756c0d3.tar.xz
mullvadvpn-0961e1096cabc07916124c7c56987b959756c0d3.zip
Add launcher binary for excluded apps (mullvad-exclude)
-rw-r--r--Cargo.lock9
-rw-r--r--Cargo.toml1
-rwxr-xr-xbuild.sh1
-rw-r--r--dist-assets/linux/after-install.sh2
-rw-r--r--gui/tasks/distribution.js2
-rw-r--r--mullvad-exclude/Cargo.toml12
-rw-r--r--mullvad-exclude/src/main.rs90
7 files changed, 117 insertions, 0 deletions
diff --git a/Cargo.lock b/Cargo.lock
index 92246ebd57..08cff3260c 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -1410,6 +1410,15 @@ dependencies = [
]
[[package]]
+name = "mullvad-exclude"
+version = "0.1.0"
+dependencies = [
+ "err-derive 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)",
+ "nix 0.17.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "void 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
name = "mullvad-ipc-client"
version = "0.1.0"
dependencies = [
diff --git a/Cargo.toml b/Cargo.toml
index bbaaee503a..2debcc071c 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -10,6 +10,7 @@ members = [
"mullvad-types",
"mullvad-rpc",
"mullvad-tests",
+ "mullvad-exclude",
"talpid-openvpn-plugin",
"talpid-core",
"talpid-ipc",
diff --git a/build.sh b/build.sh
index de58aff5ed..168f01502d 100755
--- a/build.sh
+++ b/build.sh
@@ -167,6 +167,7 @@ elif [[ ("$(uname -s)" == "Linux") ]]; then
mullvad-problem-report
libtalpid_openvpn_plugin.so
mullvad-setup
+ mullvad-exclude
)
elif [[ ("$(uname -s)" == "MINGW"*) ]]; then
binaries=(
diff --git a/dist-assets/linux/after-install.sh b/dist-assets/linux/after-install.sh
index 0abe49cbe5..b75b72dc31 100644
--- a/dist-assets/linux/after-install.sh
+++ b/dist-assets/linux/after-install.sh
@@ -1,6 +1,8 @@
#!/usr/bin/env bash
set -eu
+chmod u+s "/usr/bin/mullvad-exclude"
+
if which systemctl &> /dev/null; then
systemctl enable "/opt/Mullvad VPN/resources/mullvad-daemon.service"
systemctl start mullvad-daemon.service
diff --git a/gui/tasks/distribution.js b/gui/tasks/distribution.js
index 30c0fe1f03..4e8061a7fa 100644
--- a/gui/tasks/distribution.js
+++ b/gui/tasks/distribution.js
@@ -126,6 +126,7 @@ const config = {
'--config-files',
'/opt/Mullvad VPN/resources/mullvad-daemon.conf',
distAssets('mullvad') + '=/usr/bin/',
+ distAssets('mullvad-exclude') + '=/usr/bin/',
distAssets('linux/problem-report-link') + '=/usr/bin/mullvad-problem-report',
distAssets('shell-completions/mullvad.bash') +
'=/usr/share/bash-completion/completions/mullvad',
@@ -149,6 +150,7 @@ const config = {
'--config-files',
'/opt/Mullvad VPN/resources/mullvad-daemon.conf',
distAssets('mullvad') + '=/usr/bin/',
+ distAssets('mullvad-exclude') + '=/usr/bin/',
distAssets('linux/problem-report-link') + '=/usr/bin/mullvad-problem-report',
distAssets('shell-completions/mullvad.bash') +
'=/usr/share/bash-completion/completions/mullvad',
diff --git a/mullvad-exclude/Cargo.toml b/mullvad-exclude/Cargo.toml
new file mode 100644
index 0000000000..06e0c8ab56
--- /dev/null
+++ b/mullvad-exclude/Cargo.toml
@@ -0,0 +1,12 @@
+[package]
+name = "mullvad-exclude"
+version = "0.1.0"
+authors = ["Mullvad VPN"]
+license = "GPL-3.0"
+edition = "2018"
+publish = false
+
+[target.'cfg(target_os = "linux")'.dependencies]
+nix = "0.17"
+err-derive = "0.2.1"
+void = "1.0.2"
diff --git a/mullvad-exclude/src/main.rs b/mullvad-exclude/src/main.rs
new file mode 100644
index 0000000000..133424c027
--- /dev/null
+++ b/mullvad-exclude/src/main.rs
@@ -0,0 +1,90 @@
+#[cfg(target_os = "linux")]
+use nix::unistd::{execvp, getgid, getpid, getuid, setgid, setuid};
+#[cfg(target_os = "linux")]
+use std::{
+ env,
+ error::Error as StdError,
+ ffi::{CStr, CString, NulError},
+ fs, io,
+ os::unix::ffi::OsStrExt,
+};
+
+#[cfg(target_os = "linux")]
+const CGROUP_PROCS_PATH: &str = "/sys/fs/cgroup/net_cls/mullvad-exclusions/cgroup.procs";
+
+#[cfg(target_os = "linux")]
+const PROGRAM_NAME: &str = "mullvad-exclude";
+
+#[cfg(target_os = "linux")]
+#[derive(err_derive::Error, Debug)]
+#[error(no_from)]
+enum Error {
+ #[error(display = "Invalid arguments")]
+ InvalidArguments,
+
+ #[error(display = "Cannot set the cgroup")]
+ AddProcToCGroup(#[error(source)] io::Error),
+
+ #[error(display = "Failed to drop root user privileges for the process")]
+ DropRootUid(#[error(source)] nix::Error),
+
+ #[error(display = "Failed to drop root group privileges for the process")]
+ DropRootGid(#[error(source)] nix::Error),
+
+ #[error(display = "Failed to launch the process")]
+ Exec(#[error(source)] nix::Error),
+
+ #[error(display = "An argument contains interior nul bytes")]
+ ArgumentNulError(#[error(source)] NulError),
+}
+
+fn main() {
+ #[cfg(target_os = "linux")]
+ match run() {
+ Err(Error::InvalidArguments) => {
+ let mut args = env::args();
+ let program = args.next().unwrap_or(PROGRAM_NAME.to_string());
+ eprintln!("Usage {} COMMAND [ARGS]", program);
+ std::process::exit(1);
+ }
+ Err(e) => {
+ let mut s = format!("{}", e);
+ let mut source = e.source();
+ while let Some(error) = source {
+ s.push_str(&format!("\nCaused by: {}", error));
+ source = error.source();
+ }
+ eprintln!("{}", s);
+
+ std::process::exit(1);
+ }
+ _ => unreachable!("execv returned unexpectedly"),
+ }
+}
+
+#[cfg(target_os = "linux")]
+fn run() -> Result<void::Void, Error> {
+ let mut args_iter = env::args_os().skip(1);
+ let program = args_iter.next().ok_or(Error::InvalidArguments)?;
+ let program = CString::new(program.as_bytes()).map_err(Error::ArgumentNulError)?;
+
+ let args: Vec<CString> = env::args_os()
+ .skip(1)
+ .map(|arg| CString::new(arg.as_bytes()))
+ .collect::<Result<Vec<CString>, NulError>>()
+ .map_err(Error::ArgumentNulError)?;
+ let args: Vec<&CStr> = args.iter().map(|arg| &**arg).collect();
+
+ // Set the cgroup of this process
+ fs::write(CGROUP_PROCS_PATH, getpid().to_string().as_bytes())
+ .map_err(Error::AddProcToCGroup)?;
+
+ // Drop root privileges
+ let real_uid = getuid();
+ setuid(real_uid).map_err(Error::DropRootUid)?;
+ let real_gid = getgid();
+ setgid(real_gid).map_err(Error::DropRootGid)?;
+
+ // Launch the process
+ execvp(&program, &args).map_err(Error::Exec)
+}