diff --git a/CHANGELOG.md b/CHANGELOG.md index 51536f5d52..f28b6f8c55 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,9 @@ This project adheres to [Semantic Versioning](http://semver.org/). (#[1185](https://github.com/nix-rust/nix/pull/1185)) - `FsType` inner value made public. (#[1187](https://github.com/nix-rust/nix/pull/1187)) +- Added `unistd::setfsuid` and `unistd::setfsgid` to set the user or group + identity for filesystem checks per-thread. + (#[1163](https://github.com/nix-rust/nix/pull/1163)) ### Changed ### Fixed ### Removed diff --git a/src/unistd.rs b/src/unistd.rs index 2dd5064b76..904c1c81ed 100644 --- a/src/unistd.rs +++ b/src/unistd.rs @@ -1398,6 +1398,28 @@ pub fn setgid(gid: Gid) -> Result<()> { Errno::result(res).map(drop) } +/// Set the user identity used for filesystem checks per-thread. +/// On both success and failure, this call returns the previous filesystem user +/// ID of the caller. +/// +/// See also [setfsuid(2)](http://man7.org/linux/man-pages/man2/setfsuid.2.html) +#[cfg(any(target_os = "linux", target_os = "android"))] +pub fn setfsuid(uid: Uid) -> Uid { + let prev_fsuid = unsafe { libc::setfsuid(uid.into()) }; + Uid::from_raw(prev_fsuid as uid_t) +} + +/// Set the group identity used for filesystem checks per-thread. +/// On both success and failure, this call returns the previous filesystem group +/// ID of the caller. +/// +/// See also [setfsgid(2)](http://man7.org/linux/man-pages/man2/setfsgid.2.html) +#[cfg(any(target_os = "linux", target_os = "android"))] +pub fn setfsgid(gid: Gid) -> Gid { + let prev_fsgid = unsafe { libc::setfsgid(gid.into()) }; + Gid::from_raw(prev_fsgid as gid_t) +} + /// Get the list of supplementary group IDs of the calling process. /// /// [Further reading](http://pubs.opengroup.org/onlinepubs/009695399/functions/getgroups.html) diff --git a/test/test_unistd.rs b/test/test_unistd.rs index 24e0c86bec..7e3be1ccb2 100644 --- a/test/test_unistd.rs +++ b/test/test_unistd.rs @@ -866,3 +866,41 @@ fn test_access_file_exists() { let _file = File::create(path.clone()).unwrap(); assert!(access(&path, AccessFlags::R_OK | AccessFlags::W_OK).is_ok()); } + +/// Tests setting the filesystem UID with `setfsuid`. +#[cfg(any(target_os = "linux", target_os = "android"))] +#[test] +fn test_setfsuid() { + use std::os::unix::fs::PermissionsExt; + use std::{fs, thread}; + require_capability!(CAP_SETUID); + + // get the UID of the "nobody" user + let nobody = User::from_name("nobody").unwrap().unwrap(); + + // create a temporary file with permissions '-rw-r-----' + let file = tempfile::NamedTempFile::new().unwrap(); + let temp_path = file.into_temp_path(); + let temp_path_2 = (&temp_path).to_path_buf(); + let mut permissions = fs::metadata(&temp_path).unwrap().permissions(); + permissions.set_mode(640); + + // spawn a new thread where to test setfsuid + thread::spawn(move || { + // set filesystem UID + let fuid = setfsuid(nobody.uid); + // trying to open the temporary file should fail with EACCES + let res = fs::File::open(&temp_path); + assert!(res.is_err()); + assert_eq!(res.err().unwrap().kind(), io::ErrorKind::PermissionDenied); + + // assert fuid actually changes + let prev_fuid = setfsuid(Uid::from_raw(-1i32 as u32)); + assert_ne!(prev_fuid, fuid); + }) + .join() + .unwrap(); + + // open the temporary file with the current thread filesystem UID + fs::File::open(temp_path_2).unwrap(); +}