Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add setfsuid and setfsgid implementation for filesystem checks #1163

Merged
merged 1 commit into from Feb 29, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
3 changes: 3 additions & 0 deletions CHANGELOG.md
Expand Up @@ -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
asomers marked this conversation as resolved.
Show resolved Hide resolved
identity for filesystem checks per-thread.
(#[1163](https://github.com/nix-rust/nix/pull/1163))
### Changed
### Fixed
### Removed
Expand Down
22 changes: 22 additions & 0 deletions src/unistd.rs
Expand Up @@ -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)
Expand Down
38 changes: 38 additions & 0 deletions test/test_unistd.rs
Expand Up @@ -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();
}