priv_sep

Privilege separation library.
git clone https://git.philomathiclife.com/repos/priv_sep
Log | Files | Refs | README

commit 197eb59641724d10bfdc4a3dc0227195b1e18764
parent 2501b48791b95bf517d1cdbfef25a53afb88be08
Author: Zack Newman <zack@philomathiclife.com>
Date:   Wed, 26 Jul 2023 09:31:45 -0600

permission impl copy like promise

Diffstat:
MCargo.toml | 2+-
MREADME.md | 9+++++----
Msrc/lib.rs | 74+++++++++++++++++++++++++++++++++++++++++---------------------------------
3 files changed, 47 insertions(+), 38 deletions(-)

diff --git a/Cargo.toml b/Cargo.toml @@ -9,7 +9,7 @@ license = "MIT OR Apache-2.0" name = "priv_sep" readme = "README.md" repository = "https://git.philomathiclife.com/repos/priv_sep/" -version = "0.3.0" +version = "0.4.0" [lib] name = "priv_sep" diff --git a/README.md b/README.md @@ -8,7 +8,8 @@ but in the future may contain functionality for Linux's ## Pledge -It is very rare to use the `execpromises` parameter, so [`pledge`](https://docs.rs/priv_sep/latest/priv_sep/fn.pledge.html) only relies on [`Promise`](https://docs.rs/priv_sep/latest/priv_sep/enum.Promise.html)s. +It is very rare to use the `execpromises` parameter, so [`pledge`](https://docs.rs/priv_sep/latest/priv_sep/fn.pledge.html) +only relies on [`Promise`](https://docs.rs/priv_sep/latest/priv_sep/enum.Promise.html)s. ## Unveil @@ -41,13 +42,13 @@ laptop$ cargo build --release Compiling semver v1.0.18 Compiling libc v0.2.147 Compiling rustc_version v0.4.0 - Compiling priv_sep v0.3.0 (/home/zack/priv_sep) + Compiling priv_sep v0.4.0 (/home/zack/priv_sep) Finished release [optimized] target(s) in 1.90s -laptop$ cargo t +laptop$ touch /home/zack/foo.txt && cargo t && rm /home/zack/foo.txt Compiling semver v1.0.18 Compiling libc v0.2.147 Compiling rustc_version v0.4.0 - Compiling priv_sep v0.3.0 (/home/zack/priv_sep) + Compiling priv_sep v0.4.0 (/home/zack/priv_sep) Finished test [unoptimized + debuginfo] target(s) in 1.43s Running unittests src/lib.rs (target/debug/deps/priv_sep-dcb151b099a76f20) diff --git a/src/lib.rs b/src/lib.rs @@ -219,6 +219,7 @@ pub fn pledge<const N: usize>(promises: Option<[Promise; N]>) -> Result<(), c_in } /// A `permission` to [`unveil(2)`](https://man.openbsd.org/amd64/unveil.2). #[allow(clippy::exhaustive_enums)] +#[derive(Clone, Copy)] pub enum Permission { /// Consult `unveil(2)`. Create, @@ -244,8 +245,14 @@ impl Display for UnveilErr { #[inline] fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { match *self { - Self::CInt(c) => c.fmt(f), - Self::NulError(ref e) => e.fmt(f), + Self::CInt(c) => write!( + f, + "The following error was returned when calling 'unveil(2)': {c}" + ), + Self::NulError(ref e) => write!( + f, + "The path passed to 'unveil(2)' was unable to be converted to a CString: {e}" + ), } } } @@ -257,43 +264,44 @@ impl std::error::Error for UnveilErr {} /// Will return [`c_int`](https://doc.rust-lang.org/stable/core/ffi/type.c_int.html) iff /// `unveil(2)` does. Returns [`NulError`](https://doc.rust-lang.org/alloc/ffi/struct.NulError.html) /// iff [`CString::new`](https://doc.rust-lang.org/alloc/ffi/struct.CString.html#method.new) does. +/// This is a private function and uses `Option` for the path to indicate calling `unveil(2)` with +/// two `NULL` arguments. #[inline] #[allow(unsafe_code, clippy::indexing_slicing, clippy::into_iter_on_ref)] fn unveil<P: AsRef<Path>, const N: usize>( path: Option<P>, - permissions: &[Permission; N], + permissions: [Permission; N], ) -> Result<(), UnveilErr> { extern "C" { fn unveil(path: *const c_char, permissions: *const c_char) -> c_int; } let path_c: CString; let perm_c: CString; - let (fst, snd) = match path { - Some(path_val) => { - let mut v = Vec::new(); - permissions.into_iter().fold((), |_, p| { - v.push(match *p { - Permission::Create => b'c', - Permission::Execute => b'x', - Permission::Read => b'r', - Permission::Write => b'w', - }); + let (fst, snd) = if let Some(p) = path { + let mut v = Vec::new(); + permissions.into_iter().fold((), |_, perm| { + v.push(match perm { + Permission::Create => b'c', + Permission::Execute => b'x', + Permission::Read => b'r', + Permission::Write => b'w', }); - v.push(0); - match CString::new(path_val.as_ref().as_os_str().as_bytes()) { - Ok(s) => { - path_c = s; - // SAFETY: - // v was populated above with correct ASCII-encoding of the literal - // values all of which do not have 0 bytes. - // v ends with a 0/nul byte. - perm_c = unsafe { CString::from_vec_with_nul_unchecked(v) }; - (path_c.as_ptr(), perm_c.as_ptr()) - } - Err(e) => return Err(UnveilErr::NulError(e)), + }); + v.push(0); + match CString::new(p.as_ref().as_os_str().as_bytes()) { + Ok(s) => { + path_c = s; + // SAFETY: + // v was populated above with correct ASCII-encoding of the literal + // values all of which do not have 0 bytes. + // v ends with a 0/nul byte. + perm_c = unsafe { CString::from_vec_with_nul_unchecked(v) }; + (path_c.as_ptr(), perm_c.as_ptr()) } + Err(e) => return Err(UnveilErr::NulError(e)), } - None => (ptr::null(), ptr::null()), + } else { + (ptr::null(), ptr::null()) }; // SAFETY: // unveil is an FFI binding; thus requires unsafe code. @@ -312,7 +320,7 @@ fn unveil<P: AsRef<Path>, const N: usize>( #[allow(clippy::unreachable)] #[inline] pub fn unveil_no_more() -> Result<(), c_int> { - unveil::<PathBuf, 0>(None, &[]).map_err(|e| match e { + unveil::<PathBuf, 0>(None, []).map_err(|e| match e { UnveilErr::CInt(c) => c, UnveilErr::NulError(_) => unreachable!("There is a bug in unveil."), }) @@ -324,7 +332,7 @@ pub fn unveil_no_more() -> Result<(), c_int> { /// Returns [`UnveilErr`] when a problem occurs. #[inline] pub fn unveil_no_perms<P: AsRef<Path>>(path: P) -> Result<(), UnveilErr> { - unveil(Some(path), &[]) + unveil(Some(path), []) } /// Invokes [`unveil(2)`](https://man.openbsd.org/amd64/unveil.2) by passing `path` for `path` and `perm` for `permissions`. /// @@ -333,7 +341,7 @@ pub fn unveil_no_perms<P: AsRef<Path>>(path: P) -> Result<(), UnveilErr> { /// Returns [`UnveilErr`] when a problem occurs. #[inline] pub fn unveil_one_perm<P: AsRef<Path>>(path: P, perm: Permission) -> Result<(), UnveilErr> { - unveil(Some(path), &[perm]) + unveil(Some(path), [perm]) } /// Invokes [`unveil(2)`](https://man.openbsd.org/amd64/unveil.2) by passing `path` for `path` and `perms` for `permissions`. /// @@ -341,7 +349,7 @@ pub fn unveil_one_perm<P: AsRef<Path>>(path: P, perm: Permission) -> Result<(), /// /// Returns [`UnveilErr`] when a problem occurs. #[inline] -pub fn unveil_two_perms<P: AsRef<Path>>(path: P, perms: &[Permission; 2]) -> Result<(), UnveilErr> { +pub fn unveil_two_perms<P: AsRef<Path>>(path: P, perms: [Permission; 2]) -> Result<(), UnveilErr> { unveil(Some(path), perms) } /// Invokes [`unveil(2)`](https://man.openbsd.org/amd64/unveil.2) by passing `path` for `path` and `perms` for `permissions`. @@ -352,7 +360,7 @@ pub fn unveil_two_perms<P: AsRef<Path>>(path: P, perms: &[Permission; 2]) -> Res #[inline] pub fn unveil_three_perms<P: AsRef<Path>>( path: P, - perms: &[Permission; 3], + perms: [Permission; 3], ) -> Result<(), UnveilErr> { unveil(Some(path), perms) } @@ -365,7 +373,7 @@ pub fn unveil_three_perms<P: AsRef<Path>>( pub fn unveil_all_perms<P: AsRef<Path>>(path: P) -> Result<(), UnveilErr> { unveil( Some(path), - &[ + [ Permission::Create, Permission::Execute, Permission::Read, @@ -401,7 +409,7 @@ mod tests { // and one can unveil more permissions (unlike pledge which can only remove promises). assert!(super::unveil_two_perms( "/home/zack/foo.txt", - &[Permission::Read, Permission::Read] + [Permission::Read, Permission::Read] ) .is_ok()); assert!(std::fs::metadata("/home/zack/foo.txt").is_ok());