From 590e31d0b80e1bc8d9bea686625afabcaf3eb153 Mon Sep 17 00:00:00 2001 From: Leonardo Held Date: Wed, 21 Aug 2024 15:25:47 -0300 Subject: [PATCH] Drop privileges after loading certificates Related-to: TOR-3521 Signed-off-by: Leonardo Held --- Cargo.lock | 1 + Cargo.toml | 1 + src/main.rs | 5 +++++ src/utils.rs | 34 ++++++++++++++++++++++++++++++++++ src/utils/tests.rs | 31 ++++++++++++++++++++++++++++++- 5 files changed, 71 insertions(+), 1 deletion(-) diff --git a/Cargo.lock b/Cargo.lock index 4f99ae4..cb0928e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1518,6 +1518,7 @@ dependencies = [ "eyre", "futures-util", "log", + "nix", "pretty_env_logger", "rumqttc", "rustls-pemfile", diff --git a/Cargo.toml b/Cargo.toml index f9d1d7b..aa06cf8 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -24,6 +24,7 @@ tokio = { version = "1.38.0", features = ["sync", "rt", "signal", "rt-multi-thre webpki-roots = "0.26.3" x509-parser = "0.16.0" zbus = "4.3.1" +nix = "0.29.0" [dev-dependencies] tempfile = "3.4" diff --git a/src/main.rs b/src/main.rs index d1c9b03..e16d315 100644 --- a/src/main.rs +++ b/src/main.rs @@ -52,6 +52,11 @@ async fn run() -> Result<()> { (device_id, Some(client_config)) }; + if let Err(e) = utils::drop_privileges() { + eprintln!("Failed to drop privileges: {}", e); + std::process::exit(1); + } + info!("connecting to {device_id}@{mqtt_hostname}:{mqtt_port}"); let mut mqttoptions = rumqttc::MqttOptions::new( diff --git a/src/utils.rs b/src/utils.rs index 47dc3e4..580f53b 100644 --- a/src/utils.rs +++ b/src/utils.rs @@ -4,6 +4,9 @@ use std::fs::File; use std::io::BufReader; use std::path::Path; +use nix::unistd::{self, Uid, Gid}; +use std::ffi::CString; +use log::info; use rustls_pemfile; use eyre::{Context, Result, OptionExt}; use x509_parser::prelude::*; @@ -83,3 +86,34 @@ pub fn parse_payload(payload: &[u8]) -> Result<(String, serde_json::Value)> { Ok((command, args_json)) } + +pub fn drop_privileges() -> Result<()> { + if !Uid::current().is_root() { + info!("No need to drop privileges, current user is not root"); + return Ok(()); + } + + let user = "1000"; + let group = "1000"; + + let ugroup = unistd::Group::from_gid(Gid::from_raw(1000))? + .ok_or(eyre::eyre!("Could not get group {group}"))?; + let uuser = unistd::User::from_uid(Uid::from_raw(1000))? + .ok_or(eyre::eyre!("Could not get user {user}"))?; + + let user_name_cstring = CString::new(user)?; + + unistd::initgroups(&user_name_cstring, ugroup.gid)?; + + unistd::setgid(ugroup.gid)?; + + unistd::setuid(uuser.uid)?; + + if unistd::setuid(Uid::from_raw(0)).is_ok() { + eyre::bail!("Could not drop privileges, can still change back to uid 0"); + } + + info!("Dropped privileges to {user}:{group}"); + + Ok(()) +} diff --git a/src/utils/tests.rs b/src/utils/tests.rs index 57e21d2..942cb3a 100644 --- a/src/utils/tests.rs +++ b/src/utils/tests.rs @@ -5,7 +5,8 @@ #[cfg(test)] mod tests { use std::path::Path; - use crate::utils::{load_cert, load_private_key, read_device_id, parse_payload}; + use nix::unistd::Uid; + use crate::utils::{load_cert, load_private_key, read_device_id, parse_payload, drop_privileges}; #[test] @@ -60,4 +61,32 @@ mod tests { let result = parse_payload(payload); assert!(result.is_err(), "Expected error for missing `command`, got: {:?}", result); } + + #[test] + fn test_drop_privileges_non_root() { + // This test should only be run as a non-root user + if nix::unistd::getuid().is_root() { + panic!("This test should not be run as root"); + } + + let result = drop_privileges(); + assert!(result.is_ok(), "Expected Ok(()), got {:?}", result); + } + + #[test] + fn test_drop_privileges_root_user_simulation() { + // Simulate root user environment by temporarily setting nix::unistd::setuid to return root + let original_uid = nix::unistd::getuid(); + let root_uid = Uid::from_raw(0); + let _ = nix::unistd::setuid(root_uid); + + let result = drop_privileges(); + + let _ = nix::unistd::setuid(original_uid); + + match original_uid.is_root() { + true => assert!(result.is_err(), "Expected an error, got {:?}", result), + false => assert!(result.is_ok(), "Expected Ok(()), got {:?}", result), + } + } }