priv_sep

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

commit 40554cc7d568214ecd497018ab9df9c6d9c69354
parent 94ff58f83ddc9d5c28983bcdf195cd484c23615a
Author: Zack Newman <zack@philomathiclife.com>
Date:   Tue, 25 Feb 2025 07:59:12 -0700

rust 2024

Diffstat:
MCargo.toml | 11++++-------
MREADME.md | 25+++++++++++++++++++++++--
Msrc/lib.rs | 38+++++++++++++++++++++-----------------
3 files changed, 48 insertions(+), 26 deletions(-)

diff --git a/Cargo.toml b/Cargo.toml @@ -3,24 +3,21 @@ authors = ["Zack Newman <zack@philomathiclife.com>"] categories = ["development-tools::ffi", "external-ffi-bindings", "os"] description = "FFI for pledge(2) and unveil(2) on OpenBSD." documentation = "https://docs.rs/priv_sep/latest/priv_sep/" -edition = "2021" +edition = "2024" keywords = ["ffi", "openbsd", "privsep", "security"] license = "MIT OR Apache-2.0" name = "priv_sep" readme = "README.md" repository = "https://git.philomathiclife.com/repos/priv_sep/" -rust-version = "1.81.0" -version = "2.1.0" - -[badges] -maintenance = { status = "actively-developed" } +rust-version = "1.85.0" +version = "2.2.0" [package.metadata.docs.rs] all-features = true rustdoc-args = ["--cfg", "docsrs"] [target.'cfg(target_os = "openbsd")'.dependencies] -libc = { version = "0.2.158", default-features = false, features = ["std"], optional = true } +libc = { version = "0.2.170", default-features = false, features = ["std"], optional = true } ### FEATURES ################################################################# diff --git a/README.md b/README.md @@ -1,5 +1,9 @@ # `priv_sep` +[<img alt="git" src="https://git.philomathiclife.com/badges/priv_sep.svg" height="20">](https://git.philomathiclife.com/priv_sep/log.html) +[<img alt="crates.io" src="https://img.shields.io/crates/v/priv_sep.svg?style=for-the-badge&color=fc8d62&logo=rust" height="20">](https://crates.io/crates/priv_sep) +[<img alt="docs.rs" src="https://img.shields.io/badge/docs.rs-priv_sep-66c2a5?style=for-the-badge&labelColor=555555&logo=docs.rs" height="20">](https://docs.rs/priv_sep/latest/priv_sep/) + `priv_sep` is a library for privilege separation. It is currently designed around [`pledge(2)`](https://man.openbsd.org/amd64/pledge.2) and [`unveil(2)`](https://man.openbsd.org/amd64/unveil.2) for OpenBSD, but @@ -20,12 +24,25 @@ Calls to `unveil(2)` are done via `Permissions::unveil` and `unveil_no_more`. Any error returned from the underlying system call is propagated via `Error`. +## Minimum Supported Rust Version (MSRV) + +This will frequently be updated to be the same as stable. Specifically, any time stable is updated and that +update has "useful" features or compilation no longer succeeds (e.g., due to new compiler lints), then MSRV +will be updated. + +MSRV changes will correspond to a SemVer minor version bump. + +## SemVer Policy + +* All on-by-default features of this library are covered by SemVer +* MSRV is considered exempt from SemVer as noted above + ## License Licensed under either of -* Apache License, Version 2.0 ([LICENSE-APACHE](LICENSE-APACHE) or http://www.apache.org/licenses/LICENSE-2.0). -* MIT license ([LICENSE-MIT](LICENSE-MIT) or http://opensource.org/licenses/MIT). +* Apache License, Version 2.0 ([LICENSE-APACHE](https://www.apache.org/licenses/LICENSE-2.0)) +* MIT license ([LICENSE-MIT](https://opensource.org/licenses/MIT)) at your option. @@ -34,6 +51,10 @@ at your option. Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in the work by you, as defined in the Apache-2.0 license, shall be dual licensed as above, without any additional terms or conditions. +Before any PR is sent, `cargo clippy` and `cargo t` should be run for both `--no-default-features` and +`--all-features`. Additionally `RUSTDOCFLAGS="--cfg docsrs" cargo +nightly doc --all-features` should be run to +ensure documentation can be built. + ### Status This package will be actively maintained to stay in-sync with the latest version of OpenBSD; as a result, diff --git a/src/lib.rs b/src/lib.rs @@ -1,4 +1,8 @@ -//! # `priv_sep` +//! [![git]](https://git.philomathiclife.com/priv_sep/log.html)&ensp;[![crates-io]](https://crates.io/crates/priv_sep)&ensp;[![docs-rs]](crate) +//! +//! [git]: https://git.philomathiclife.com/git_badge.svg +//! [crates-io]: https://img.shields.io/badge/crates.io-fc8d62?style=for-the-badge&labelColor=555555&logo=rust +//! [docs-rs]: https://img.shields.io/badge/docs.rs-66c2a5?style=for-the-badge&labelColor=555555&logo=docs.rs //! //! `priv_sep` is a library for privilege separation. //! It is currently designed around [`pledge(2)`](https://man.openbsd.org/amd64/pledge.2) and @@ -47,22 +51,26 @@ )] #![expect( clippy::blanket_clippy_restriction_lints, - clippy::doc_markdown, reason = "same reason as below" )] #![cfg_attr(docsrs, doc(cfg(feature = "openbsd")))] #![cfg(feature = "openbsd")] #![expect( + clippy::arbitrary_source_item_ordering, clippy::exhaustive_enums, clippy::implicit_return, clippy::min_ident_chars, clippy::missing_trait_methods, clippy::ref_patterns, - clippy::single_char_lifetime_names, clippy::unseparated_literal_suffix, reason = "noisy, opinionated, and likely doesn't prevent bugs or improve APIs" )] extern crate alloc; +use Promise::{ + Audio, Bpf, Chown, Cpath, Disklabel, Dns, Dpath, Drm, Error, Exec, Fattr, Flock, Getpw, Id, + Inet, Mcast, Pf, Proc, ProtExec, Ps, Recvfd, Route, Rpath, Sendfd, Settime, Stdio, Tape, + Tmppath, Tty, Unix, Unveil, Video, Vminfo, Vmm, Wpath, Wroute, +}; use alloc::ffi::{CString, NulError}; use core::{ convert::AsRef, @@ -72,12 +80,7 @@ use core::{ ops::{BitAnd, BitAndAssign, BitOr, BitOrAssign, BitXor, BitXorAssign, Not}, ptr, }; -use std::{io, os::unix::ffi::OsStrExt, path::Path}; -use Promise::{ - Audio, Bpf, Chown, Cpath, Disklabel, Dns, Dpath, Drm, Error, Exec, Fattr, Flock, Getpw, Id, - Inet, Mcast, Pf, Proc, ProtExec, Ps, Recvfd, Route, Rpath, Sendfd, Settime, Stdio, Tape, - Tmppath, Tty, Unix, Unveil, Video, Vminfo, Vmm, Wpath, Wroute, -}; +use std::{io, os::unix::ffi::OsStrExt as _, path::Path}; /// A `promise` to [`pledge(2)`](https://man.openbsd.org/amd64/pledge.2). #[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)] #[non_exhaustive] @@ -267,7 +270,7 @@ pub struct Promises(u64); /// `pledge_none`. #[expect(unsafe_code, reason = "FFI requires unsafe code")] fn pledge(promises: *const c_char) -> Result<(), io::Error> { - extern "C" { + unsafe extern "C" { fn pledge(promises: *const c_char, execpromises: *const c_char) -> c_int; } // SAFETY: @@ -497,7 +500,7 @@ impl Promises { /// assert!(proms.len() == 1 && proms.contains(Promise::Rpath)); /// ``` #[inline] - pub fn remove(&mut self, promise: Promise) { + pub const fn remove(&mut self, promise: Promise) { self.0 &= !promise.to_u64(); } /// Same as [`Self::remove`] then [`Self::pledge`]; however this is "atomic" in that if an error occurs from `pledge`, @@ -718,7 +721,7 @@ pub fn pledge_none() -> Result<(), io::Error> { /// `unveil_no_more`. #[expect(unsafe_code, reason = "FFI requires unsafe code")] fn unveil(path: *const c_char, permissions: *const c_char) -> Result<(), io::Error> { - extern "C" { + unsafe extern "C" { fn unveil(path: *const c_char, permissions: *const c_char) -> c_int; } // SAFETY: @@ -802,7 +805,7 @@ impl Permissions { /// assert_eq!(perms, Permissions::READ); /// ``` #[inline] - pub fn enable(&mut self, permission: Permission) { + pub const fn enable(&mut self, permission: Permission) { self.0 |= permission.to_u8(); } /// Disables `permission` in `self`. @@ -822,7 +825,7 @@ impl Permissions { /// assert!(perms.is_enabled(Permission::Create) && perms.is_enabled(Permission::Read) && perms.is_enabled(Permission::Write) && !perms.is_enabled(Permission::Execute)); /// ``` #[inline] - pub fn disable(&mut self, permission: Permission) { + pub const fn disable(&mut self, permission: Permission) { self.0 &= !permission.to_u8(); } /// Returns `true` iff `self` has `permission` enabled. @@ -959,7 +962,7 @@ impl BitAnd<&Permissions> for &Permissions { *self & *rhs } } -impl<'a> BitAnd<Permissions> for &'a Permissions { +impl BitAnd<Permissions> for &Permissions { type Output = Permissions; #[inline] fn bitand(self, rhs: Permissions) -> Self::Output { @@ -999,7 +1002,7 @@ impl BitOr<&Permissions> for &Permissions { *self | *rhs } } -impl<'a> BitOr<Permissions> for &'a Permissions { +impl BitOr<Permissions> for &Permissions { type Output = Permissions; #[inline] fn bitor(self, rhs: Permissions) -> Self::Output { @@ -1039,7 +1042,7 @@ impl BitXor<&Permissions> for &Permissions { *self ^ *rhs } } -impl<'a> BitXor<Permissions> for &'a Permissions { +impl BitXor<Permissions> for &Permissions { type Output = Permissions; #[inline] fn bitxor(self, rhs: Permissions) -> Self::Output { @@ -1147,6 +1150,7 @@ mod tests { // This tests that a NULL `promise` does nothing. assert!(crate::pledge_none().is_ok()); print!(""); + assert!(Promises::ALL.pledge().is_ok()); // This tests that duplicates are ignored as well as the implementation of PartialEq. let mut initial_promises = Promises::new([ Promise::Stdio,