Trusted Helper Argv0 for Multi-call Firewall Binaries

Co-Authored-By: brekotis <93345790+brekotis@users.noreply.github.com>
This commit is contained in:
Alexey
2026-09-28 05:48:43 +03:00
parent 6f2245952d
commit 555d0be6d9
4 changed files with 46 additions and 17 deletions
+33
View File
@@ -1,5 +1,7 @@
use std::os::unix::fs::{MetadataExt, PermissionsExt};
use std::os::unix::process::CommandExt;
use std::path::{Path, PathBuf};
use std::process::Command;
const TRUSTED_HELPER_DIRS: [&str; 4] = ["/usr/sbin", "/usr/bin", "/sbin", "/bin"];
const TRUSTED_HELPERS: [&str; 12] = [
@@ -29,6 +31,19 @@ pub(crate) fn resolve_trusted_helper(binary: &str) -> Option<PathBuf> {
.find_map(|candidate| trusted_executable(&candidate))
}
/// Builds a trusted helper command with its allowlisted invocation name.
pub(crate) fn trusted_helper_command(binary: &str) -> Option<Command> {
let command_path = resolve_trusted_helper(binary)?;
Some(command_for_resolved_helper(binary, command_path))
}
fn command_for_resolved_helper(binary: &str, command_path: PathBuf) -> Command {
let mut command = Command::new(command_path);
// Multi-call helpers dispatch from argv[0], which canonical path resolution discards.
command.arg0(binary);
command
}
fn trusted_executable(candidate: &Path) -> Option<PathBuf> {
let canonical = std::fs::canonicalize(candidate).ok()?;
let metadata = std::fs::metadata(&canonical).ok()?;
@@ -78,6 +93,24 @@ mod tests {
assert!(!TRUSTED_HELPERS.contains(&"iptables-restore-wrapper"));
}
#[test]
fn trusted_multicall_command_preserves_logical_argv0() {
let command_path = std::fs::canonicalize("/bin/sh").unwrap();
for binary in [
"iptables",
"ip6tables",
"iptables-restore",
"ip6tables-restore",
] {
let mut command = command_for_resolved_helper(binary, command_path.clone());
assert_eq!(command.get_program(), command_path.as_os_str());
let output = command.args(["-c", "printf '%s' \"$0\""]).output().unwrap();
assert!(output.status.success());
assert_eq!(String::from_utf8(output.stdout).unwrap(), binary);
}
}
#[test]
fn writable_executable_is_not_trusted() {
let directory = tempfile::tempdir().unwrap();