Skip to content

Commit 1e145e5

Browse files
authored
cap-tempfile: Don't create anonymous files with 0o666 (#403)
* cap-tempfile: Don't create anonymous files with 0o666 When creating anonymous temporary files, use mode 0o000 instead of 0o666 to further discourage anything else from opening them. This fixes a bug pointed out in #390. * Fix anonymous files on Windows. * Fix umask tests on non-Linux Unix platforms. * Fix tempfile path. * Fix compilation on MSRV. * Fix compilation on Darwin.
1 parent 83f484a commit 1e145e5

File tree

3 files changed

+64
-28
lines changed

3 files changed

+64
-28
lines changed

cap-tempfile/Cargo.toml

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,12 +26,15 @@ rustix = { version = "1.0.0" }
2626
[target.'cfg(any(target_os = "android", target_os = "linux"))'.dependencies]
2727
rustix-linux-procfs = "0.1.1"
2828

29-
[target.'cfg(windows)'.dev-dependencies.windows-sys]
29+
[target.'cfg(windows)'.dependencies.windows-sys]
3030
version = ">=0.60, <0.62"
3131
features = [
3232
"Win32_Foundation",
3333
]
3434

35+
[dev-dependencies]
36+
tempfile = "3.23.0"
37+
3538
[features]
3639
default = []
3740
fs_utf8 = ["cap-std/fs_utf8", "camino"]

cap-tempfile/src/lib.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ use uuid::Uuid;
1919
pub mod utf8;
2020

2121
mod tempfile;
22-
pub use tempfile::*;
22+
pub use crate::tempfile::*;
2323

2424
/// Re-export because we use this in our public API.
2525
pub use cap_std;

cap-tempfile/src/tempfile.rs

Lines changed: 59 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -68,9 +68,17 @@ fn new_tempfile_linux(d: &Dir, anonymous: bool) -> io::Result<Option<File>> {
6868
if anonymous {
6969
oflags |= OFlags::EXCL;
7070
}
71-
// We default to 0o666, same as main rust when creating new files; this will be
72-
// modified by umask: <https://github.com/rust-lang/rust/blob/44628f7273052d0bb8e8218518dacab210e1fe0d/library/std/src/sys/unix/fs.rs#L762>
73-
let mode = Mode::from_raw_mode(0o666);
71+
// For anonymous files, open with no permissions to discourage other
72+
// processes from opening them.
73+
//
74+
// For named files, default to 0o666, same as main rust when creating new
75+
// files; this will be modified by umask:
76+
// <https://github.com/rust-lang/rust/blob/44628f7273052d0bb8e8218518dacab210e1fe0d/library/std/src/sys/unix/fs.rs#L762>
77+
let mode = if anonymous {
78+
Mode::from_raw_mode(0o000)
79+
} else {
80+
Mode::from_raw_mode(0o666)
81+
};
7482
// Happy path - Linux with O_TMPFILE
7583
match rustix::fs::openat(d, ".", oflags, mode) {
7684
Ok(r) => Ok(Some(File::from(r))),
@@ -111,11 +119,29 @@ fn new_tempfile(d: &Dir, anonymous: bool) -> io::Result<(File, Option<String>)>
111119
opts.read(true);
112120
opts.write(true);
113121
opts.create_new(true);
122+
#[cfg(unix)]
123+
if anonymous {
124+
use cap_std::fs::OpenOptionsExt;
125+
opts.mode(0);
126+
}
127+
#[cfg(windows)]
128+
if anonymous {
129+
use cap_std::fs::OpenOptionsExt;
130+
use windows_sys::Win32::Storage::FileSystem::{
131+
FILE_ATTRIBUTE_TEMPORARY, FILE_FLAG_DELETE_ON_CLOSE,
132+
};
133+
opts.share_mode(0);
134+
opts.custom_flags(FILE_ATTRIBUTE_TEMPORARY | FILE_FLAG_DELETE_ON_CLOSE);
135+
}
114136
let (f, name) = super::retry_with_name_ignoring(io::ErrorKind::AlreadyExists, |name| {
115137
d.open_with(name, &opts)
116138
})?;
117139
if anonymous {
118-
d.remove_file(name)?;
140+
// On Windows we use `FILE_FLAG_DELETE_ON_CLOSE` instead.
141+
#[cfg(not(windows))]
142+
{
143+
d.remove_file(name)?;
144+
}
119145
Ok((f, None))
120146
} else {
121147
Ok((f, Some(name)))
@@ -217,26 +243,22 @@ mod test {
217243
use super::*;
218244

219245
/// On Unix, calling `umask()` actually *mutates* the process global state.
220-
/// This uses Linux `/proc` to read the current value.
221-
#[cfg(any(target_os = "android", target_os = "linux"))]
246+
/// This uses a temporary file instead.
247+
#[cfg(unix)]
222248
fn get_process_umask() -> io::Result<u32> {
223-
use io::BufRead;
224-
let status = std::fs::File::open("/proc/self/status")?;
225-
let bufr = io::BufReader::new(status);
226-
for line in bufr.lines() {
227-
let line = line?;
228-
let l = if let Some(v) = line.split_once(':') {
229-
v
230-
} else {
231-
continue;
232-
};
233-
let (k, v) = l;
234-
if k != "Umask" {
235-
continue;
236-
}
237-
return Ok(u32::from_str_radix(v.trim(), 8).unwrap());
238-
}
239-
panic!("Could not determine process umask")
249+
use std::os::unix::fs::{MetadataExt, OpenOptionsExt};
250+
251+
let d = ::tempfile::tempdir().unwrap();
252+
let p = d.path().join("file");
253+
254+
let mut opts = std::fs::OpenOptions::new();
255+
opts.read(true);
256+
opts.write(true);
257+
opts.create_new(true);
258+
opts.mode(0o777);
259+
let f = opts.open(p).unwrap();
260+
let m = f.metadata().unwrap();
261+
Ok(!m.mode() & 0o777)
240262
}
241263

242264
/// Older Windows versions don't support removing open files
@@ -262,15 +284,15 @@ mod test {
262284

263285
let mut tf = TempFile::new(&td)?;
264286
// Test that we created with the right permissions
265-
#[cfg(any(target_os = "android", target_os = "linux"))]
287+
#[cfg(unix)]
266288
{
267289
use cap_std::fs_utf8::MetadataExt;
268290
use rustix::fs::Mode;
269291
let umask = get_process_umask()?;
270292
let metadata = tf.as_file().metadata().unwrap();
271293
let mode = metadata.mode();
272-
let mode = Mode::from_bits_truncate(mode);
273-
assert_eq!(0o666 & !umask, mode.bits() & 0o777);
294+
let mode = Mode::from_bits_truncate(mode as _);
295+
assert_eq!(0o666 & !umask, (mode.bits() & 0o777) as _);
274296
}
275297
// And that we can write
276298
tf.write_all(b"hello world")?;
@@ -291,6 +313,17 @@ mod test {
291313
let mut buf = String::new();
292314
tf.read_to_string(&mut buf).unwrap();
293315
assert_eq!(&buf, "hello world, I'm anonymous");
316+
317+
// Test that we created with the right permissions
318+
#[cfg(unix)]
319+
{
320+
use cap_std::fs_utf8::MetadataExt;
321+
use rustix::fs::Mode;
322+
let metadata = tf.metadata().unwrap();
323+
let mode = metadata.mode();
324+
let mode = Mode::from_bits_truncate(mode as _);
325+
assert_eq!(0o000, mode.bits() & 0o777);
326+
}
294327
} else if cfg!(windows) {
295328
eprintln!("notice: Detected older Windows");
296329
}

0 commit comments

Comments
 (0)