Skip to content

Commit 2fc1822

Browse files
committed
mkfifo: drop path-based chmod to close TOCTOU race; switch to rustix (#10020)
1 parent d928f05 commit 2fc1822

3 files changed

Lines changed: 40 additions & 39 deletions

File tree

Cargo.lock

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/uu/mkfifo/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ uucore = { workspace = true, features = ["fs", "mode"] }
2525
fluent = { workspace = true }
2626

2727
[target.'cfg(unix)'.dependencies]
28-
nix = { workspace = true, features = ["fs"] }
28+
rustix = { workspace = true, features = ["fs", "process"] }
2929

3030
[features]
3131
selinux = ["uucore/selinux"]

src/uu/mkfifo/src/mkfifo.rs

Lines changed: 38 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,10 @@
44
// file that was distributed with this source code.
55

66
use clap::{Arg, ArgAction, Command, value_parser};
7-
use nix::sys::stat::Mode;
8-
use nix::unistd::mkfifo;
7+
use rustix::fs::{CWD, Mode, mkfifoat};
8+
use rustix::process::umask;
9+
#[cfg(any(feature = "selinux", feature = "smack"))]
910
use std::fs;
10-
use std::os::unix::fs::PermissionsExt;
1111
use uucore::display::Quotable;
1212
use uucore::error::{UResult, USimpleError};
1313
use uucore::translate;
@@ -48,47 +48,48 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> {
4848
};
4949

5050
for f in fifos {
51-
if mkfifo(f.as_str(), Mode::from_bits_truncate(0o666)).is_err() {
51+
// Clear umask around mkfifo so the kernel applies the exact
52+
// requested mode atomically. Skipping the path-based chmod
53+
// that used to follow this call closes the TOCTOU window an
54+
// attacker could use to swap the FIFO for a symlink between
55+
// mkfifo and chmod (issue #10020).
56+
let prev_umask = umask(Mode::empty());
57+
let mkfifo_result = mkfifoat(CWD, f.as_str(), Mode::from_bits_truncate(mode));
58+
umask(prev_umask);
59+
60+
if mkfifo_result.is_err() {
5261
show!(USimpleError::new(
5362
1,
5463
translate!("mkfifo-error-cannot-create-fifo", "path" => f.quote()),
5564
));
56-
continue;
57-
}
58-
59-
// Explicitly set the permissions to ignore umask
60-
if let Err(e) = fs::set_permissions(&f, fs::Permissions::from_mode(mode)) {
61-
return Err(USimpleError::new(
62-
1,
63-
translate!("mkfifo-error-cannot-set-permissions", "path" => f.quote(), "error" => e),
64-
));
65-
}
66-
67-
// Apply SELinux context if requested
68-
#[cfg(all(feature = "selinux", any(target_os = "linux", target_os = "android")))]
69-
{
70-
// Extract the SELinux related flags and options
71-
let set_security_context = matches.get_flag(options::SECURITY_CONTEXT);
72-
let context = matches.get_one::<String>(options::CONTEXT);
73-
74-
if set_security_context || context.is_some() {
75-
use std::path::Path;
76-
if let Err(e) =
77-
uucore::selinux::set_selinux_security_context(Path::new(&f), context)
78-
{
79-
let _ = fs::remove_file(f);
80-
return Err(USimpleError::new(1, e.to_string()));
65+
} else {
66+
// Apply SELinux context if requested
67+
#[cfg(all(feature = "selinux", any(target_os = "linux", target_os = "android")))]
68+
{
69+
let set_security_context = matches.get_flag(options::SECURITY_CONTEXT);
70+
let context = matches.get_one::<String>(options::CONTEXT);
71+
72+
if set_security_context || context.is_some() {
73+
use std::path::Path;
74+
if let Err(e) =
75+
uucore::selinux::set_selinux_security_context(Path::new(&f), context)
76+
{
77+
let _ = fs::remove_file(f);
78+
return Err(USimpleError::new(1, e.to_string()));
79+
}
8180
}
8281
}
83-
}
8482

85-
// Apply SMACK context if requested
86-
#[cfg(all(feature = "smack", target_os = "linux"))]
87-
{
88-
let set_security_context = matches.get_flag(options::SECURITY_CONTEXT);
89-
let context = matches.get_one::<String>(options::CONTEXT);
90-
if set_security_context || context.is_some() {
91-
uucore::smack::set_smack_label_and_cleanup(&f, context, |p| fs::remove_file(p))?;
83+
// Apply SMACK context if requested
84+
#[cfg(all(feature = "smack", target_os = "linux"))]
85+
{
86+
let set_security_context = matches.get_flag(options::SECURITY_CONTEXT);
87+
let context = matches.get_one::<String>(options::CONTEXT);
88+
if set_security_context || context.is_some() {
89+
uucore::smack::set_smack_label_and_cleanup(&f, context, |p| {
90+
fs::remove_file(p)
91+
})?;
92+
}
9293
}
9394
}
9495
}

0 commit comments

Comments
 (0)