Last active
January 18, 2024 18:15
-
-
Save luben/2b93fc036df7750cfbc42c0cdc93deda to your computer and use it in GitHub Desktop.
Temporary privileged threads after `chown`
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
use caps::{self, CapSet, Capability, CapsHashSet}; | |
use daemonize::Daemonize; | |
use std::{ | |
fmt::Write, | |
sync::{Arc, Condvar, Mutex}, | |
thread, | |
time::Duration, | |
}; | |
struct Sensor { | |
name: String, | |
// etc | |
} | |
impl Sensor { | |
fn new(name: String) -> Self { | |
Sensor { name } | |
} | |
fn printcaps(&self) { | |
printcaps(&self.name); | |
} | |
fn setup(&self) { | |
// restore CAP_SYS_ADMIN | |
let sys_admin_caps: CapsHashSet = [ | |
Capability::CAP_SYS_ADMIN, // for entering namespaces | |
Capability::CAP_SETPCAP, // for dropping bounding CAPs | |
] | |
.into_iter() | |
.collect(); | |
// add the needed effective caps and remove unneeded permitted caps | |
for capset in [CapSet::Effective, CapSet::Permitted] { | |
caps::set(None, capset, &sys_admin_caps).unwrap(); | |
} | |
// clear the other capability sets | |
for capset in [CapSet::Ambient, CapSet::Bounding, CapSet::Inheritable] { | |
caps::clear(None, capset).unwrap(); | |
} | |
} | |
fn drop_caps(&self) { | |
// drop the rest of the capabilities | |
for capset in [CapSet::Effective, CapSet::Permitted] { | |
caps::clear(None, capset).unwrap(); | |
} | |
} | |
fn run(self, secure_bits_set: Arc<(Mutex<i32>, Condvar)>) { | |
caps::securebits::set_keepcaps(true).unwrap(); | |
let (lock, cvar) = &*secure_bits_set; | |
*(lock.lock().unwrap()) -= 1; | |
// We notify the condvar that the value has changed. | |
cvar.notify_one(); | |
let mut i = 0; | |
loop { | |
self.printcaps(); | |
thread::sleep(Duration::from_secs(2)); | |
if i <= 1 { | |
// setup the sensors | |
self.setup(); | |
} else if i == 2 { | |
// after the setup has finished | |
self.drop_caps(); | |
} | |
i += 1; | |
} | |
} | |
} | |
// For debugging: print current thread caps | |
fn printcaps(name: &str) { | |
let mut out = String::new(); | |
writeln!(out, "{name} ------------------------------").unwrap(); | |
for capset in [ | |
CapSet::Ambient, | |
CapSet::Bounding, | |
CapSet::Effective, | |
CapSet::Inheritable, | |
CapSet::Permitted, | |
] { | |
write!(out, "{name} {capset:?}:").unwrap(); | |
if let Ok(caps) = caps::read(None, capset) { | |
writeln!(out, " {caps:?}").unwrap(); | |
} else { | |
writeln!(out).unwrap(); | |
} | |
} | |
eprint!("{out}"); | |
} | |
fn start_sensors() { | |
let pair = Arc::new((Mutex::new(0), Condvar::new())); | |
for i in 0..3 { | |
*(pair.0.lock().unwrap()) += 1; | |
thread::spawn({ | |
let pair = pair.clone(); | |
let sensor = Sensor::new(format!("Sensor {i}")); | |
move || sensor.run(pair) | |
}); | |
} | |
// wait for all threads to set `securebits` | |
let (lock, cvar) = &*pair; | |
let _guard = cvar.wait_while(lock.lock().unwrap(), |count| *count > 0); | |
// drop main thread bounding set capabilites, so we cannot execute | |
// files that can gain elevated capabilities | |
caps::clear(None, CapSet::Bounding).unwrap(); | |
} | |
fn main() { | |
printcaps("root"); | |
Daemonize::new() | |
.pid_file("/tmp/threads.pid") | |
.user("nobody") | |
.group("nogroup") | |
.stdout(daemonize::Stdio::keep()) | |
.stderr(daemonize::Stdio::keep()) | |
.privileged_action(start_sensors) | |
.start() | |
.unwrap(); | |
// main thread | |
printcaps("daemonized"); | |
thread::park(); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
updated it to use Condvar