Loading crates/fs-mistrust/src/imp.rs +5 −1 Original line number Diff line number Diff line Loading @@ -114,7 +114,7 @@ impl<'a> super::Verifier<'a> { if uid != 0 && Some(uid) != self.mistrust.trust_uid { errors.push(Error::BadOwner(path.into(), uid)); } let forbidden_bits = if !self.readable_okay && path_type == PathType::Final { let mut forbidden_bits = if !self.readable_okay && path_type == PathType::Final { // If this is the target object, and it must not be readable, // then we forbid it to be group-rwx and all-rwx. 0o077 Loading @@ -139,6 +139,10 @@ impl<'a> super::Verifier<'a> { 0o022 } }; // If we trust the GID, then we allow even more bits to be set. if self.mistrust.trust_gid == Some(meta.gid()) { forbidden_bits &= !0o070; } let bad_bits = meta.mode() & forbidden_bits; if bad_bits != 0 { errors.push(Error::BadPermission(path.into(), bad_bits)); Loading crates/fs-mistrust/src/lib.rs +53 −2 Original line number Diff line number Diff line Loading @@ -87,8 +87,6 @@ // TODO: Stuff to add before this crate is ready.... // - Ability to create directory if it doesn't exist. // - Get more flexible about group permissions. (diziet had an idea.) // - Stop-at-homedir support. // - Test the absolute heck out of it. // POSSIBLY TODO: Loading Loading @@ -168,6 +166,10 @@ pub struct Mistrust { /// What user ID do we trust by default (if any?) #[cfg(target_family = "unix")] trust_uid: Option<u32>, /// What group ID do we trust by default (if any?) #[cfg(target_family = "unix")] trust_gid: Option<u32>, } impl Default for Mistrust { Loading @@ -176,6 +178,8 @@ impl Default for Mistrust { ignore_prefix: None, #[cfg(target_family = "unix")] trust_uid: Some(unsafe { libc::getuid() }), #[cfg(target_family = "unix")] trust_gid: None, } } } Loading Loading @@ -249,6 +253,23 @@ impl Mistrust { self } #[cfg(target_family = "unix")] /// Configure a trusted group ID for this `Mistrust`. /// /// If a group ID is considered "trusted", then any file or directory we /// inspect is allowed to be readable and writable by that group. /// /// By default, no group ID is trusted, and any group-readable or /// group-writable objects are treated the same as world-readable and /// world-writable objects respectively. /// /// Anybody who is a member (or becomes a member) of the provided group will /// be allowed to read and modify the verified files. pub fn trust_group_id(&mut self, gid: u32) -> &mut Self { self.trust_gid = Some(gid); self } /// Create a new [`Verifier`] with this configuration, to perform a single check. pub fn verifier(&self) -> Verifier<'_> { Verifier { Loading Loading @@ -525,6 +546,36 @@ mod test { } } #[cfg(target_family = "unix")] #[test] fn trust_gid() { use std::os::unix::prelude::MetadataExt; let d = Dir::new(); d.dir("a/b"); d.chmod("a", 0o770); d.chmod("a/b", 0o770); let mut m = Mistrust::new(); m.ignore_prefix(d.canonical_root()).unwrap(); // By default, we shouldn't be accept this directory, since it is // group-writable. let e = m.check_directory(d.path("a/b")).unwrap_err(); assert!(matches!(e, Error::BadPermission(_, _))); // But we can make the group trusted, which will make it okay for the // directory to be group-writable. let gid = d.path("a/b").metadata().unwrap().gid(); m.trust_group_id(gid); m.check_directory(d.path("a/b")).unwrap(); // OTOH, if we made a _different_ group trusted, it'll fail. m.trust_group_id(gid ^ 1); let e = m.check_directory(d.path("a/b")).unwrap_err(); assert!(matches!(e, Error::BadPermission(_, _))); } // TODO: Write far more tests. // * Can there be a test for a failed readlink()? I can't see an easy way // to provoke that without trying to make a time-of-check/time-of-use race Loading Loading
crates/fs-mistrust/src/imp.rs +5 −1 Original line number Diff line number Diff line Loading @@ -114,7 +114,7 @@ impl<'a> super::Verifier<'a> { if uid != 0 && Some(uid) != self.mistrust.trust_uid { errors.push(Error::BadOwner(path.into(), uid)); } let forbidden_bits = if !self.readable_okay && path_type == PathType::Final { let mut forbidden_bits = if !self.readable_okay && path_type == PathType::Final { // If this is the target object, and it must not be readable, // then we forbid it to be group-rwx and all-rwx. 0o077 Loading @@ -139,6 +139,10 @@ impl<'a> super::Verifier<'a> { 0o022 } }; // If we trust the GID, then we allow even more bits to be set. if self.mistrust.trust_gid == Some(meta.gid()) { forbidden_bits &= !0o070; } let bad_bits = meta.mode() & forbidden_bits; if bad_bits != 0 { errors.push(Error::BadPermission(path.into(), bad_bits)); Loading
crates/fs-mistrust/src/lib.rs +53 −2 Original line number Diff line number Diff line Loading @@ -87,8 +87,6 @@ // TODO: Stuff to add before this crate is ready.... // - Ability to create directory if it doesn't exist. // - Get more flexible about group permissions. (diziet had an idea.) // - Stop-at-homedir support. // - Test the absolute heck out of it. // POSSIBLY TODO: Loading Loading @@ -168,6 +166,10 @@ pub struct Mistrust { /// What user ID do we trust by default (if any?) #[cfg(target_family = "unix")] trust_uid: Option<u32>, /// What group ID do we trust by default (if any?) #[cfg(target_family = "unix")] trust_gid: Option<u32>, } impl Default for Mistrust { Loading @@ -176,6 +178,8 @@ impl Default for Mistrust { ignore_prefix: None, #[cfg(target_family = "unix")] trust_uid: Some(unsafe { libc::getuid() }), #[cfg(target_family = "unix")] trust_gid: None, } } } Loading Loading @@ -249,6 +253,23 @@ impl Mistrust { self } #[cfg(target_family = "unix")] /// Configure a trusted group ID for this `Mistrust`. /// /// If a group ID is considered "trusted", then any file or directory we /// inspect is allowed to be readable and writable by that group. /// /// By default, no group ID is trusted, and any group-readable or /// group-writable objects are treated the same as world-readable and /// world-writable objects respectively. /// /// Anybody who is a member (or becomes a member) of the provided group will /// be allowed to read and modify the verified files. pub fn trust_group_id(&mut self, gid: u32) -> &mut Self { self.trust_gid = Some(gid); self } /// Create a new [`Verifier`] with this configuration, to perform a single check. pub fn verifier(&self) -> Verifier<'_> { Verifier { Loading Loading @@ -525,6 +546,36 @@ mod test { } } #[cfg(target_family = "unix")] #[test] fn trust_gid() { use std::os::unix::prelude::MetadataExt; let d = Dir::new(); d.dir("a/b"); d.chmod("a", 0o770); d.chmod("a/b", 0o770); let mut m = Mistrust::new(); m.ignore_prefix(d.canonical_root()).unwrap(); // By default, we shouldn't be accept this directory, since it is // group-writable. let e = m.check_directory(d.path("a/b")).unwrap_err(); assert!(matches!(e, Error::BadPermission(_, _))); // But we can make the group trusted, which will make it okay for the // directory to be group-writable. let gid = d.path("a/b").metadata().unwrap().gid(); m.trust_group_id(gid); m.check_directory(d.path("a/b")).unwrap(); // OTOH, if we made a _different_ group trusted, it'll fail. m.trust_group_id(gid ^ 1); let e = m.check_directory(d.path("a/b")).unwrap_err(); assert!(matches!(e, Error::BadPermission(_, _))); } // TODO: Write far more tests. // * Can there be a test for a failed readlink()? I can't see an easy way // to provoke that without trying to make a time-of-check/time-of-use race Loading