Skip to content

Instantly share code, notes, and snippets.

@luben
Last active January 18, 2024 18:15
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save luben/2b93fc036df7750cfbc42c0cdc93deda to your computer and use it in GitHub Desktop.
Save luben/2b93fc036df7750cfbc42c0cdc93deda to your computer and use it in GitHub Desktop.
Temporary privileged threads after `chown`
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();
}
@alexey-katranov
Copy link

Have you considered latches?

@luben
Copy link
Author

luben commented Jan 18, 2024

updated it to use Condvar

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment