priv_sep

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

openbsd.rs (52193B)


      1 #[cfg(doc)]
      2 use super::chroot_then_chdir;
      3 use super::{NulOrIoErr, PrivDropErr, SUCCESS, UserInfo};
      4 use alloc::ffi::CString;
      5 #[cfg(doc)]
      6 use alloc::ffi::NulError;
      7 use core::{
      8     ffi::{c_char, c_int},
      9     fmt::{self, Display, Formatter},
     10     ops::{BitAnd, BitAndAssign, BitOr, BitOrAssign, BitXor, BitXorAssign, Not},
     11     ptr,
     12 };
     13 use std::{io::Error, os::unix::ffi::OsStrExt as _, path::Path};
     14 #[expect(unsafe_code, reason = "FFI requires unsafe")]
     15 unsafe extern "C" {
     16     /// [`pledge(2)`](https://man.openbsd.org/pledge.2).
     17     pub(crate) fn pledge(promises: *const c_char, execpromises: *const c_char) -> c_int;
     18     /// [`unveil(2)`](https://man.openbsd.org/unveil.2).
     19     pub(crate) fn unveil(path: *const c_char, permissions: *const c_char) -> c_int;
     20 }
     21 /// A `promise` to [`pledge(2)`](https://man.openbsd.org/pledge.2).
     22 #[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
     23 #[non_exhaustive]
     24 pub enum Promise {
     25     /// [`audio`](https://man.openbsd.org/pledge.2#audio).
     26     Audio,
     27     /// [`bpf`](https://man.openbsd.org/pledge.2#bpf).
     28     Bpf,
     29     /// [`chown`](https://man.openbsd.org/pledge.2#chown).
     30     Chown,
     31     /// [`cpath`](https://man.openbsd.org/pledge.2#cpath).
     32     Cpath,
     33     /// `disklabel`.
     34     Disklabel,
     35     /// [`dns`](https://man.openbsd.org/pledge.2#dns).
     36     Dns,
     37     /// [`dpath`](https://man.openbsd.org/pledge.2#dpath).
     38     Dpath,
     39     /// `drm`.
     40     Drm,
     41     /// [`error`](https://man.openbsd.org/pledge.2#error).
     42     Error,
     43     /// [`exec`](https://man.openbsd.org/pledge.2#exec).
     44     Exec,
     45     /// [`fattr`](https://man.openbsd.org/pledge.2#fattr).
     46     Fattr,
     47     /// [`flock`](https://man.openbsd.org/pledge.2#flock).
     48     Flock,
     49     /// [`getpw`](https://man.openbsd.org/pledge.2#getpw).
     50     Getpw,
     51     /// [`id`](https://man.openbsd.org/pledge.2#id).
     52     Id,
     53     /// [`inet`](https://man.openbsd.org/pledge.2#inet).
     54     Inet,
     55     /// [`mcast`](https://man.openbsd.org/pledge.2#mcast).
     56     Mcast,
     57     /// [`pf`](https://man.openbsd.org/pledge.2#pf).
     58     Pf,
     59     /// [`proc`](https://man.openbsd.org/pledge.2#proc).
     60     Proc,
     61     /// [`prot_exec`](https://man.openbsd.org/pledge.2#prot_exec).
     62     ProtExec,
     63     /// [`ps`](https://man.openbsd.org/pledge.2#ps).
     64     Ps,
     65     /// [`recvfd`](https://man.openbsd.org/pledge.2#recvfd).
     66     Recvfd,
     67     /// [`route`](https://man.openbsd.org/pledge.2#route).
     68     Route,
     69     /// [`rpath`](https://man.openbsd.org/pledge.2#rpath).
     70     Rpath,
     71     /// [`sendfd`](https://man.openbsd.org/pledge.2#sendfd).
     72     Sendfd,
     73     /// [`settime`](https://man.openbsd.org/pledge.2#settime).
     74     Settime,
     75     /// [`stdio`](https://man.openbsd.org/pledge.2#stdio).
     76     Stdio,
     77     /// [`tape`](https://man.openbsd.org/pledge.2#tape).
     78     Tape,
     79     /// [`tmppath`](https://man.openbsd.org/pledge.2#tmppath).
     80     Tmppath,
     81     /// [`tty`](https://man.openbsd.org/pledge.2#tty).
     82     Tty,
     83     /// [`unix`](https://man.openbsd.org/pledge.2#unix).
     84     Unix,
     85     /// [`unveil`](https://man.openbsd.org/pledge.2#unveil).
     86     Unveil,
     87     /// [`video`](https://man.openbsd.org/pledge.2#video).
     88     Video,
     89     /// [`vminfo`](https://man.openbsd.org/pledge.2#vminfo).
     90     Vminfo,
     91     /// `vmm`.
     92     Vmm,
     93     /// [`wpath`](https://man.openbsd.org/pledge.2#wpath).
     94     Wpath,
     95     /// [`wroute`](https://man.openbsd.org/pledge.2#wroute).
     96     Wroute,
     97 }
     98 impl Promise {
     99     /// Returns `self` as a `u64`.
    100     const fn to_u64(self) -> u64 {
    101         match self {
    102             Self::Audio => 0x1,
    103             Self::Bpf => 0x2,
    104             Self::Chown => 0x4,
    105             Self::Cpath => 0x8,
    106             Self::Disklabel => 0x10,
    107             Self::Dns => 0x20,
    108             Self::Dpath => 0x40,
    109             Self::Drm => 0x80,
    110             Self::Error => 0x100,
    111             Self::Exec => 0x200,
    112             Self::Fattr => 0x400,
    113             Self::Flock => 0x800,
    114             Self::Getpw => 0x1000,
    115             Self::Id => 0x2000,
    116             Self::Inet => 0x4000,
    117             Self::Mcast => 0x8000,
    118             Self::Pf => 0x10000,
    119             Self::Proc => 0x20000,
    120             Self::ProtExec => 0x40000,
    121             Self::Ps => 0x80000,
    122             Self::Recvfd => 0x0010_0000,
    123             Self::Route => 0x0020_0000,
    124             Self::Rpath => 0x0040_0000,
    125             Self::Sendfd => 0x0080_0000,
    126             Self::Settime => 0x0100_0000,
    127             Self::Stdio => 0x0200_0000,
    128             Self::Tape => 0x0400_0000,
    129             Self::Tmppath => 0x0800_0000,
    130             Self::Tty => 0x1000_0000,
    131             Self::Unix => 0x2000_0000,
    132             Self::Unveil => 0x4000_0000,
    133             Self::Video => 0x8000_0000,
    134             Self::Vminfo => 0x0001_0000_0000,
    135             Self::Vmm => 0x0002_0000_0000,
    136             Self::Wpath => 0x0004_0000_0000,
    137             Self::Wroute => 0x0008_0000_0000,
    138         }
    139     }
    140 }
    141 impl Display for Promise {
    142     #[inline]
    143     fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
    144         match *self {
    145             Self::Audio => f.write_str("pledge(2) 'audio' promise"),
    146             Self::Bpf => f.write_str("pledge(2) 'bpf' promise"),
    147             Self::Chown => f.write_str("pledge(2) 'chown' promise"),
    148             Self::Cpath => f.write_str("pledge(2) 'cpath' promise"),
    149             Self::Disklabel => f.write_str("pledge(2) 'disklabel' promise"),
    150             Self::Dns => f.write_str("pledge(2) 'dns' promise"),
    151             Self::Dpath => f.write_str("pledge(2) 'dpath' promise"),
    152             Self::Drm => f.write_str("pledge(2) 'drm' promise"),
    153             Self::Error => f.write_str("pledge(2) 'error' promise"),
    154             Self::Exec => f.write_str("pledge(2) 'exec' promise"),
    155             Self::Fattr => f.write_str("pledge(2) 'fattr' promise"),
    156             Self::Flock => f.write_str("pledge(2) 'flock' promise"),
    157             Self::Getpw => f.write_str("pledge(2) 'getpw' promise"),
    158             Self::Id => f.write_str("pledge(2) 'id' promise"),
    159             Self::Inet => f.write_str("pledge(2) 'inet' promise"),
    160             Self::Mcast => f.write_str("pledge(2) 'mcast' promise"),
    161             Self::Pf => f.write_str("pledge(2) 'pf' promise"),
    162             Self::Proc => f.write_str("pledge(2) 'proc' promise"),
    163             Self::ProtExec => f.write_str("pledge(2) 'prot_exec' promise"),
    164             Self::Ps => f.write_str("pledge(2) 'ps' promise"),
    165             Self::Recvfd => f.write_str("pledge(2) 'recvfd' promise"),
    166             Self::Route => f.write_str("pledge(2) 'route' promise"),
    167             Self::Rpath => f.write_str("pledge(2) 'rpath' promise"),
    168             Self::Sendfd => f.write_str("pledge(2) 'sendfd' promise"),
    169             Self::Settime => f.write_str("pledge(2) 'settime' promise"),
    170             Self::Stdio => f.write_str("pledge(2) 'stdio' promise"),
    171             Self::Tape => f.write_str("pledge(2) 'tape' promise"),
    172             Self::Tmppath => f.write_str("pledge(2) 'tmppath' promise"),
    173             Self::Tty => f.write_str("pledge(2) 'tty' promise"),
    174             Self::Unix => f.write_str("pledge(2) 'unix' promise"),
    175             Self::Unveil => f.write_str("pledge(2) 'unveil' promise"),
    176             Self::Video => f.write_str("pledge(2) 'video' promise"),
    177             Self::Vminfo => f.write_str("pledge(2) 'vminfo' promise"),
    178             Self::Vmm => f.write_str("pledge(2) 'vmm' promise"),
    179             Self::Wpath => f.write_str("pledge(2) 'wpath' promise"),
    180             Self::Wroute => f.write_str("pledge(2) 'wroute' promise"),
    181         }
    182     }
    183 }
    184 impl PartialEq<&Self> for Promise {
    185     #[inline]
    186     fn eq(&self, other: &&Self) -> bool {
    187         *self == **other
    188     }
    189 }
    190 impl PartialEq<Promise> for &Promise {
    191     #[inline]
    192     fn eq(&self, other: &Promise) -> bool {
    193         **self == *other
    194     }
    195 }
    196 /// A set of [`Promise`]s that can only have `Promise`s removed after creation.
    197 ///
    198 /// Once a set of `promises` has been [`pledge(2)`](https://man.openbsd.org/pledge.2)d,
    199 /// only a subset of those `promises` can be `pledge(2)`d again; as a result, this type can be used
    200 /// to ensure that `Promise`s are never added but only removed from an initial set.
    201 #[expect(
    202     missing_copy_implementations,
    203     reason = "a single instance should be created"
    204 )]
    205 #[derive(Debug, Eq, PartialEq)]
    206 pub struct Promises(u64);
    207 impl Promises {
    208     /// Empty `Promises`.
    209     pub const NONE: Self = Self(0);
    210     /// `Promises` containing all [`Promise`]s.
    211     pub const ALL: Self = Self::NONE
    212         .add(Promise::Audio)
    213         .add(Promise::Bpf)
    214         .add(Promise::Chown)
    215         .add(Promise::Cpath)
    216         .add(Promise::Disklabel)
    217         .add(Promise::Dns)
    218         .add(Promise::Dpath)
    219         .add(Promise::Drm)
    220         .add(Promise::Error)
    221         .add(Promise::Exec)
    222         .add(Promise::Fattr)
    223         .add(Promise::Flock)
    224         .add(Promise::Getpw)
    225         .add(Promise::Id)
    226         .add(Promise::Inet)
    227         .add(Promise::Mcast)
    228         .add(Promise::Pf)
    229         .add(Promise::Proc)
    230         .add(Promise::ProtExec)
    231         .add(Promise::Ps)
    232         .add(Promise::Recvfd)
    233         .add(Promise::Route)
    234         .add(Promise::Rpath)
    235         .add(Promise::Sendfd)
    236         .add(Promise::Settime)
    237         .add(Promise::Stdio)
    238         .add(Promise::Tape)
    239         .add(Promise::Tmppath)
    240         .add(Promise::Tty)
    241         .add(Promise::Unix)
    242         .add(Promise::Unveil)
    243         .add(Promise::Video)
    244         .add(Promise::Vminfo)
    245         .add(Promise::Vmm)
    246         .add(Promise::Wpath)
    247         .add(Promise::Wroute);
    248     /// Returns a `Promises` containing all `Promise`s in `self` and `promise`.
    249     const fn add(self, promise: Promise) -> Self {
    250         Self(self.0 | promise.to_u64())
    251     }
    252     /// Returns a `Promises` containing the unique `Promise`s in `initial`.
    253     ///
    254     /// # Examples
    255     ///
    256     /// ```
    257     /// # #[cfg(target_os = "openbsd")]
    258     /// # use priv_sep::{Promise, Promises};
    259     /// # #[cfg(target_os = "openbsd")]
    260     /// assert_eq!(Promises::new([Promise::Stdio]).len(), 1);
    261     /// ```
    262     #[inline]
    263     #[must_use]
    264     pub fn new<P: AsRef<[Promise]>>(initial: P) -> Self {
    265         initial
    266             .as_ref()
    267             .iter()
    268             .fold(Self::NONE, |val, promise| val.add(*promise))
    269     }
    270     /// Same as [`UserInfo::priv_drop`] except [`Self::pledge`] is called between [`UserInfo::new`] and
    271     /// the invocation of `f`.
    272     ///
    273     /// Note [`Promise::Id`] and [`Promise::Stdio`] are automatically added to `initial`. `retain_id_promise`
    274     /// dictates if [`Promise::Id`] should subsequently be retained after [`UserInfo::setresid`]. Callers must
    275     /// ensure `initial` contains any necessary [`Promise`]s needed for `f`.
    276     ///
    277     /// # Errors
    278     ///
    279     /// Errors iff [`UserInfo::priv_drop`] or [`Self::pledge`] error.
    280     ///
    281     /// # Examples
    282     ///
    283     /// ```no_run
    284     /// # #[cfg(target_os = "openbsd")]
    285     /// # use priv_sep::{Promise, Promises};
    286     /// # use core::net::{Ipv6Addr, SocketAddrV6};
    287     /// # use priv_sep::PrivDropErr;
    288     /// # use std::{io::Error, net::TcpListener};
    289     /// # #[cfg(target_os = "openbsd")]
    290     /// let (listener, promises) = Promises::new_priv_drop("nobody", [Promise::Inet], false, || {
    291     ///     TcpListener::bind(SocketAddrV6::new(Ipv6Addr::LOCALHOST, 443, 0, 0))
    292     /// })?;
    293     /// # Ok::<_, PrivDropErr<Error>>(())
    294     /// ```
    295     #[inline]
    296     pub fn new_priv_drop<
    297         U: Into<Vec<u8>>,
    298         Prom: AsRef<[Promise]>,
    299         T,
    300         E,
    301         F: FnOnce() -> Result<T, E>,
    302     >(
    303         name: U,
    304         initial: Prom,
    305         retain_id_promise: bool,
    306         f: F,
    307     ) -> Result<(T, Self), PrivDropErr<E>> {
    308         let mut promises = initial.as_ref().iter().fold(
    309             Self::NONE.add(Promise::Id).add(Promise::Stdio),
    310             |val, promise| val.add(*promise),
    311         );
    312         UserInfo::new(name)
    313             .map_err(PrivDropErr::from)
    314             .and_then(|opt| {
    315                 opt.ok_or_else(|| PrivDropErr::NoPasswdEntry)
    316                     .and_then(|info| {
    317                         if info.is_root() {
    318                             Err(PrivDropErr::RootEntry)
    319                         } else {
    320                             promises.pledge().map_err(PrivDropErr::Io).and_then(|()| {
    321                                 f().map_err(PrivDropErr::Other).and_then(|res| {
    322                                     info.setresid().map_err(PrivDropErr::Io).and_then(|()| {
    323                                         if retain_id_promise {
    324                                             Ok(())
    325                                         } else {
    326                                             promises
    327                                                 .remove_then_pledge(Promise::Id)
    328                                                 .map_err(PrivDropErr::Io)
    329                                         }
    330                                         .map(|()| (res, promises))
    331                                     })
    332                                 })
    333                             })
    334                         }
    335                     })
    336             })
    337     }
    338     /// Same as [`Self::new_priv_drop`] except `f` is `async`.
    339     ///
    340     /// # Errors
    341     ///
    342     /// Read [`Self::new_priv_drop`].
    343     ///
    344     /// # Examples
    345     ///
    346     /// ```no_run
    347     /// # #[cfg(target_os = "openbsd")]
    348     /// # use priv_sep::{Promise, Promises};
    349     /// # use core::net::{Ipv6Addr, SocketAddrV6};
    350     /// # use tokio::net::TcpListener;
    351     /// # #[cfg(target_os = "openbsd")]
    352     /// let listener_fut = Promises::new_priv_drop_async("nobody", [Promise::Inet], false, async || {
    353     ///     TcpListener::bind(SocketAddrV6::new(Ipv6Addr::LOCALHOST, 443, 0, 0)).await
    354     /// });
    355     /// ```
    356     #[inline]
    357     pub async fn new_priv_drop_async<
    358         U: Into<Vec<u8>>,
    359         Prom: AsRef<[Promise]>,
    360         T,
    361         E,
    362         F: AsyncFnOnce() -> Result<T, E>,
    363     >(
    364         name: U,
    365         initial: Prom,
    366         retain_id_promise: bool,
    367         f: F,
    368     ) -> Result<(T, Self), PrivDropErr<E>> {
    369         let mut promises = initial.as_ref().iter().fold(
    370             Self::NONE.add(Promise::Id).add(Promise::Stdio),
    371             |val, promise| val.add(*promise),
    372         );
    373         match UserInfo::new(name) {
    374             Ok(opt) => match opt {
    375                 None => Err(PrivDropErr::NoPasswdEntry),
    376                 Some(info) => {
    377                     if info.is_root() {
    378                         Err(PrivDropErr::RootEntry)
    379                     } else {
    380                         match promises.pledge() {
    381                             Ok(()) => f().await.map_err(PrivDropErr::Other).and_then(|res| {
    382                                 info.setresid().map_err(PrivDropErr::Io).and_then(|()| {
    383                                     if retain_id_promise {
    384                                         Ok(())
    385                                     } else {
    386                                         promises
    387                                             .remove_then_pledge(Promise::Id)
    388                                             .map_err(PrivDropErr::Io)
    389                                     }
    390                                     .map(|()| (res, promises))
    391                                 })
    392                             }),
    393                             Err(err) => Err(PrivDropErr::Io(err)),
    394                         }
    395                     }
    396                 }
    397             },
    398             Err(err) => Err(PrivDropErr::from(err)),
    399         }
    400     }
    401     /// Same as [`Self::new_priv_drop`] except [`chroot_then_chdir`] is called before [`Self::pledge`]ing `initial`.
    402     ///
    403     /// # Errors
    404     ///
    405     /// Errors iff [`Self::new_priv_drop`] or [`chroot_then_chdir`] do.
    406     ///
    407     /// # Examples
    408     ///
    409     /// ```no_run
    410     /// # #[cfg(target_os = "openbsd")]
    411     /// # use priv_sep::{Promise, Promises};
    412     /// # use core::net::{Ipv6Addr, SocketAddrV6};
    413     /// # use priv_sep::PrivDropErr;
    414     /// # use std::{io::Error, net::TcpListener};
    415     /// # #[cfg(target_os = "openbsd")]
    416     /// let (listener, promises) = Promises::new_chroot_then_priv_drop("nobody", "./", [Promise::Inet], false, || {
    417     ///     TcpListener::bind(SocketAddrV6::new(Ipv6Addr::LOCALHOST, 443, 0, 0))
    418     /// })?;
    419     /// # Ok::<_, PrivDropErr<Error>>(())
    420     /// ```
    421     #[inline]
    422     pub fn new_chroot_then_priv_drop<
    423         U: Into<Vec<u8>>,
    424         P: AsRef<Path>,
    425         Prom: AsRef<[Promise]>,
    426         T,
    427         E,
    428         F: FnOnce() -> Result<T, E>,
    429     >(
    430         name: U,
    431         path: P,
    432         initial: Prom,
    433         retain_id_promise: bool,
    434         f: F,
    435     ) -> Result<(T, Self), PrivDropErr<E>> {
    436         let mut promises = initial.as_ref().iter().fold(
    437             Self::NONE.add(Promise::Id).add(Promise::Stdio),
    438             |val, promise| val.add(*promise),
    439         );
    440         UserInfo::new(name)
    441             .map_err(PrivDropErr::from)
    442             .and_then(|opt| {
    443                 opt.ok_or_else(|| PrivDropErr::NoPasswdEntry)
    444                     .and_then(|info| {
    445                         if info.is_root() {
    446                             Err(PrivDropErr::RootEntry)
    447                         } else {
    448                             super::chroot_then_chdir(path)
    449                                 .map_err(PrivDropErr::from)
    450                                 .and_then(|()| {
    451                                     promises.pledge().map_err(PrivDropErr::Io).and_then(|()| {
    452                                         f().map_err(PrivDropErr::Other).and_then(|res| {
    453                                             info.setresid().map_err(PrivDropErr::Io).and_then(
    454                                                 |()| {
    455                                                     if retain_id_promise {
    456                                                         Ok(())
    457                                                     } else {
    458                                                         promises
    459                                                             .remove_then_pledge(Promise::Id)
    460                                                             .map_err(PrivDropErr::Io)
    461                                                     }
    462                                                     .map(|()| (res, promises))
    463                                                 },
    464                                             )
    465                                         })
    466                                     })
    467                                 })
    468                         }
    469                     })
    470             })
    471     }
    472     /// Same as [`Self::new_chroot_then_priv_drop`] except `f` is `async`.
    473     ///
    474     /// # Errors
    475     ///
    476     /// Read [`Self::new_chroot_then_priv_drop`].
    477     ///
    478     /// # Examples
    479     ///
    480     /// ```no_run
    481     /// # #[cfg(target_os = "openbsd")]
    482     /// # use priv_sep::{Promise, Promises};
    483     /// # use core::net::{Ipv6Addr, SocketAddrV6};
    484     /// # use tokio::net::TcpListener;
    485     /// # #[cfg(target_os = "openbsd")]
    486     /// let listener_fut = Promises::new_chroot_then_priv_drop_async("nobody", "./", [Promise::Inet], false, async || {
    487     ///     TcpListener::bind(SocketAddrV6::new(Ipv6Addr::LOCALHOST, 443, 0, 0)).await
    488     /// });
    489     /// ```
    490     #[inline]
    491     pub async fn new_chroot_then_priv_drop_async<
    492         U: Into<Vec<u8>>,
    493         P: AsRef<Path>,
    494         Prom: AsRef<[Promise]>,
    495         T,
    496         E,
    497         F: AsyncFnOnce() -> Result<T, E>,
    498     >(
    499         name: U,
    500         path: P,
    501         initial: Prom,
    502         retain_id_promise: bool,
    503         f: F,
    504     ) -> Result<(T, Self), PrivDropErr<E>> {
    505         let mut promises = initial.as_ref().iter().fold(
    506             Self::NONE.add(Promise::Id).add(Promise::Stdio),
    507             |val, promise| val.add(*promise),
    508         );
    509         match UserInfo::new(name) {
    510             Ok(opt) => match opt {
    511                 None => Err(PrivDropErr::NoPasswdEntry),
    512                 Some(info) => {
    513                     if info.is_root() {
    514                         Err(PrivDropErr::RootEntry)
    515                     } else {
    516                         match super::chroot_then_chdir(path) {
    517                             Ok(()) => match promises.pledge() {
    518                                 Ok(()) => f().await.map_err(PrivDropErr::Other).and_then(|res| {
    519                                     info.setresid().map_err(PrivDropErr::Io).and_then(|()| {
    520                                         if retain_id_promise {
    521                                             Ok(())
    522                                         } else {
    523                                             promises
    524                                                 .remove_then_pledge(Promise::Id)
    525                                                 .map_err(PrivDropErr::Io)
    526                                         }
    527                                         .map(|()| (res, promises))
    528                                     })
    529                                 }),
    530                                 Err(err) => Err(PrivDropErr::Io(err)),
    531                             },
    532                             Err(err) => Err(PrivDropErr::from(err)),
    533                         }
    534                     }
    535                 }
    536             },
    537             Err(err) => Err(PrivDropErr::from(err)),
    538         }
    539     }
    540     /// Returns the number of [`Promise`]s.
    541     ///
    542     /// # Examples
    543     ///
    544     /// ```
    545     /// # #[cfg(target_os = "openbsd")]
    546     /// # use priv_sep::{Promise, Promises};
    547     /// # #[cfg(target_os = "openbsd")]
    548     /// assert_eq!(Promises::new([Promise::Stdio]).len(), 1);
    549     /// ```
    550     #[inline]
    551     #[must_use]
    552     pub const fn len(&self) -> u32 {
    553         self.0.count_ones()
    554     }
    555     /// Returns `true` iff there are no [`Promise`]s.
    556     ///
    557     /// # Examples
    558     ///
    559     /// ```
    560     /// # #[cfg(target_os = "openbsd")]
    561     /// # use priv_sep::Promises;
    562     /// # #[cfg(target_os = "openbsd")]
    563     /// assert!(Promises::NONE.is_empty());
    564     /// ```
    565     #[inline]
    566     #[must_use]
    567     pub const fn is_empty(&self) -> bool {
    568         self.len() == 0
    569     }
    570     /// Returns `true` iff `self` contains `promise`.
    571     ///
    572     /// # Examples
    573     ///
    574     /// ```
    575     /// # #[cfg(target_os = "openbsd")]
    576     /// # use priv_sep::{Promise, Promises};
    577     /// # #[cfg(target_os = "openbsd")]
    578     /// assert!(Promises::new([Promise::Stdio]).contains(Promise::Stdio));
    579     /// # #[cfg(target_os = "openbsd")]
    580     /// assert!(!Promises::new([Promise::Stdio]).contains(Promise::Rpath));
    581     /// ```
    582     #[inline]
    583     #[must_use]
    584     pub const fn contains(&self, promise: Promise) -> bool {
    585         let val = promise.to_u64();
    586         self.0 & val == val
    587     }
    588     /// Removes all `Promise`s _not_ in `promises` from `self`.
    589     ///
    590     /// # Examples
    591     ///
    592     /// ```
    593     /// # #[cfg(target_os = "openbsd")]
    594     /// # use priv_sep::{Promise, Promises};
    595     /// # #[cfg(target_os = "openbsd")]
    596     /// let mut proms = Promises::new([Promise::Rpath, Promise::Stdio]);
    597     /// # #[cfg(target_os = "openbsd")]
    598     /// proms.retain([Promise::Stdio]);
    599     /// # #[cfg(target_os = "openbsd")]
    600     /// assert!(proms.len() == 1 && proms.contains(Promise::Stdio));
    601     /// ```
    602     #[inline]
    603     pub fn retain<P: AsRef<[Promise]>>(&mut self, promises: P) {
    604         self.0 = promises
    605             .as_ref()
    606             .iter()
    607             .fold(Self::NONE, |val, promise| {
    608                 if self.contains(*promise) {
    609                     val.add(*promise)
    610                 } else {
    611                     val
    612                 }
    613             })
    614             .0;
    615     }
    616     /// Same as [`Self::retain`] then [`Self::pledge`]; however this is "atomic" in that if an error occurs from `pledge`,
    617     /// `self` is left unchanged. This is useful when one wants to "recover" from an error since there is no
    618     /// way to add `Promise`s back forcing one to have to create a second `Promises`.
    619     ///
    620     /// Note that when `retain` doesn't change `self`, `pledge` is not called.
    621     ///
    622     /// # Errors
    623     ///
    624     /// Returns [`Error`] iff `pledge` does.
    625     ///
    626     /// # Examples
    627     ///
    628     /// ```no_run
    629     /// # #[cfg(target_os = "openbsd")]
    630     /// # use priv_sep::{Promise, Promises};
    631     /// # #[cfg(target_os = "openbsd")]
    632     /// assert!(Promises::new([Promise::Rpath, Promise::Stdio]).retain_then_pledge([Promise::Rpath]).is_ok());
    633     /// ```
    634     #[inline]
    635     pub fn retain_then_pledge<P: AsRef<[Promise]>>(&mut self, promises: P) -> Result<(), Error> {
    636         // We opt for the easy way by copying `self`. This should be the fastest for the
    637         // typical case since `self` likely has very few `Promise`s, and it makes for less code.
    638         let cur = Self(self.0);
    639         self.retain(promises);
    640         if *self == cur {
    641             Ok(())
    642         } else {
    643             self.pledge().inspect_err(|_| *self = cur)
    644         }
    645     }
    646     /// Removes all `Promise`s in `promises` from `self`.
    647     ///
    648     /// # Examples
    649     ///
    650     /// ```
    651     /// # #[cfg(target_os = "openbsd")]
    652     /// # use priv_sep::{Promise, Promises};
    653     /// # #[cfg(target_os = "openbsd")]
    654     /// let mut proms = Promises::new([Promise::Rpath, Promise::Stdio]);
    655     /// # #[cfg(target_os = "openbsd")]
    656     /// proms.remove_promises([Promise::Stdio]);
    657     /// # #[cfg(target_os = "openbsd")]
    658     /// assert!(proms.len() == 1 && proms.contains(Promise::Rpath));
    659     /// ```
    660     #[inline]
    661     pub fn remove_promises<P: AsRef<[Promise]>>(&mut self, promises: P) {
    662         promises
    663             .as_ref()
    664             .iter()
    665             .fold((), |(), promise| self.remove(*promise));
    666     }
    667     /// Same as [`Self::remove_promises`] then [`Self::pledge`]; however this is "atomic" in that if an error occurs from
    668     /// `pledge`, `self` is left unchanged. This is useful when one wants to "recover" from an error since there
    669     /// is no way to add `Promise`s back forcing one to have to create a second `Promises`.
    670     ///
    671     /// Note that when `remove_promises` doesn't remove any, `pledge` is not called.
    672     ///
    673     /// # Errors
    674     ///
    675     /// Returns [`Error`] iff `pledge` does.
    676     ///
    677     /// # Examples
    678     ///
    679     /// ```no_run
    680     /// # #[cfg(target_os = "openbsd")]
    681     /// # use priv_sep::{Promise, Promises};
    682     /// # #[cfg(target_os = "openbsd")]
    683     /// assert!(Promises::new([Promise::Rpath, Promise::Stdio]).remove_promises_then_pledge([Promise::Rpath]).is_ok());
    684     /// ```
    685     #[inline]
    686     pub fn remove_promises_then_pledge<P: AsRef<[Promise]>>(
    687         &mut self,
    688         promises: P,
    689     ) -> Result<(), Error> {
    690         // We opt for the easy way by copying `self`. This should be the fastest for the
    691         // typical case since `self` likely has very few `Promise`s, and it makes for less code.
    692         let cur = Self(self.0);
    693         self.remove_promises(promises);
    694         if *self == cur {
    695             Ok(())
    696         } else {
    697             self.pledge().inspect_err(|_| *self = cur)
    698         }
    699     }
    700     /// Removes `promise` from `self`.
    701     ///
    702     /// # Examples
    703     ///
    704     /// ```
    705     /// # #[cfg(target_os = "openbsd")]
    706     /// # use priv_sep::{Promise, Promises};
    707     /// # #[cfg(target_os = "openbsd")]
    708     /// let mut proms = Promises::new([Promise::Rpath, Promise::Stdio]);
    709     /// # #[cfg(target_os = "openbsd")]
    710     /// proms.remove(Promise::Stdio);
    711     /// # #[cfg(target_os = "openbsd")]
    712     /// assert!(proms.len() == 1 && proms.contains(Promise::Rpath));
    713     /// ```
    714     #[inline]
    715     pub const fn remove(&mut self, promise: Promise) {
    716         self.0 &= !promise.to_u64();
    717     }
    718     /// Same as [`Self::remove`] then [`Self::pledge`]; however this is "atomic" in that if an error occurs from `pledge`,
    719     /// `self` is left unchanged. This is useful when one wants to "recover" from an error since there is no
    720     /// way to add `Promise`s back forcing one to have to create a second `Promises`.
    721     ///
    722     /// # Errors
    723     ///
    724     /// Returns [`Error`] iff `pledge` does.
    725     ///
    726     /// # Examples
    727     ///
    728     /// ```no_run
    729     /// # #[cfg(target_os = "openbsd")]
    730     /// # use priv_sep::{Promise, Promises};
    731     /// # #[cfg(target_os = "openbsd")]
    732     /// assert!(Promises::new([Promise::Rpath, Promise::Stdio]).remove_then_pledge(Promise::Rpath).is_ok());
    733     /// ```
    734     #[inline]
    735     pub fn remove_then_pledge(&mut self, promise: Promise) -> Result<(), Error> {
    736         // We opt for the easy way by copying `self`. This should be the fastest for the
    737         // typical case since `self` likely has very few `Promise`s, and it makes for less code.
    738         let cur = Self(self.0);
    739         self.remove(promise);
    740         if *self == cur {
    741             Ok(())
    742         } else {
    743             self.pledge().inspect_err(|_| *self = cur)
    744         }
    745     }
    746     /// Invokes `pledge(2)` always passing `NULL` for `execpromises` and `promises` for `promises`.
    747     ///
    748     /// This function MUST only be called by [`Self::pledge`] and [`Self::pledge_none`].
    749     #[expect(unsafe_code, reason = "pledge(2) takes in pointers")]
    750     fn inner_pledge(promises: *const c_char) -> Result<(), Error> {
    751         // SAFETY:
    752         // `promises` is either null or valid as can be seen in the only functions that call this
    753         // function:
    754         // `Self::pledge` and `Self::pledge_none`.
    755         if unsafe { pledge(promises, ptr::null()) } == SUCCESS {
    756             Ok(())
    757         } else {
    758             Err(Error::last_os_error())
    759         }
    760     }
    761     /// Invokes [`pledge(2)`](https://man.openbsd.org/pledge.2) always passing in
    762     /// `NULL` for `execpromises` and its contained [`Promise`]s for `promises`.
    763     ///
    764     /// # Errors
    765     ///
    766     /// Returns [`Error`] iff `pledge(2)` errors.
    767     ///
    768     /// # Examples
    769     ///
    770     /// ```no_run
    771     /// # #[cfg(target_os = "openbsd")]
    772     /// # use priv_sep::{Promise, Promises};
    773     /// # #[cfg(target_os = "openbsd")]
    774     /// assert!(Promises::new([Promise::Stdio]).pledge().is_ok());
    775     /// ```
    776     #[expect(
    777         unsafe_code,
    778         reason = "we manually construct a valid CString and ensure its safety"
    779     )]
    780     #[expect(
    781         clippy::arithmetic_side_effects,
    782         clippy::indexing_slicing,
    783         reason = "we replace a trailing space with 0 to ensure CString is correctly constructed"
    784     )]
    785     #[expect(
    786         clippy::cognitive_complexity,
    787         clippy::too_many_lines,
    788         reason = "many if blocks to handle each Promise"
    789     )]
    790     #[inline]
    791     pub fn pledge(&self) -> Result<(), Error> {
    792         let mut p = Vec::new();
    793         if self.contains(Promise::Audio) {
    794             p.extend_from_slice(b"audio ");
    795         }
    796         if self.contains(Promise::Bpf) {
    797             p.extend_from_slice(b"bpf ");
    798         }
    799         if self.contains(Promise::Chown) {
    800             p.extend_from_slice(b"chown ");
    801         }
    802         if self.contains(Promise::Cpath) {
    803             p.extend_from_slice(b"cpath ");
    804         }
    805         if self.contains(Promise::Disklabel) {
    806             p.extend_from_slice(b"disklabel ");
    807         }
    808         if self.contains(Promise::Dns) {
    809             p.extend_from_slice(b"dns ");
    810         }
    811         if self.contains(Promise::Dpath) {
    812             p.extend_from_slice(b"dpath ");
    813         }
    814         if self.contains(Promise::Drm) {
    815             p.extend_from_slice(b"drm ");
    816         }
    817         if self.contains(Promise::Error) {
    818             p.extend_from_slice(b"error ");
    819         }
    820         if self.contains(Promise::Exec) {
    821             p.extend_from_slice(b"exec ");
    822         }
    823         if self.contains(Promise::Fattr) {
    824             p.extend_from_slice(b"fattr ");
    825         }
    826         if self.contains(Promise::Flock) {
    827             p.extend_from_slice(b"flock ");
    828         }
    829         if self.contains(Promise::Getpw) {
    830             p.extend_from_slice(b"getpw ");
    831         }
    832         if self.contains(Promise::Id) {
    833             p.extend_from_slice(b"id ");
    834         }
    835         if self.contains(Promise::Inet) {
    836             p.extend_from_slice(b"inet ");
    837         }
    838         if self.contains(Promise::Mcast) {
    839             p.extend_from_slice(b"mcast ");
    840         }
    841         if self.contains(Promise::Pf) {
    842             p.extend_from_slice(b"pf ");
    843         }
    844         if self.contains(Promise::Proc) {
    845             p.extend_from_slice(b"proc ");
    846         }
    847         if self.contains(Promise::ProtExec) {
    848             p.extend_from_slice(b"prot_exec ");
    849         }
    850         if self.contains(Promise::Ps) {
    851             p.extend_from_slice(b"ps ");
    852         }
    853         if self.contains(Promise::Recvfd) {
    854             p.extend_from_slice(b"recvfd ");
    855         }
    856         if self.contains(Promise::Route) {
    857             p.extend_from_slice(b"route ");
    858         }
    859         if self.contains(Promise::Rpath) {
    860             p.extend_from_slice(b"rpath ");
    861         }
    862         if self.contains(Promise::Sendfd) {
    863             p.extend_from_slice(b"sendfd ");
    864         }
    865         if self.contains(Promise::Settime) {
    866             p.extend_from_slice(b"settime ");
    867         }
    868         if self.contains(Promise::Stdio) {
    869             p.extend_from_slice(b"stdio ");
    870         }
    871         if self.contains(Promise::Tape) {
    872             p.extend_from_slice(b"tape ");
    873         }
    874         if self.contains(Promise::Tmppath) {
    875             p.extend_from_slice(b"tmppath ");
    876         }
    877         if self.contains(Promise::Tty) {
    878             p.extend_from_slice(b"tty ");
    879         }
    880         if self.contains(Promise::Unix) {
    881             p.extend_from_slice(b"unix ");
    882         }
    883         if self.contains(Promise::Unveil) {
    884             p.extend_from_slice(b"unveil ");
    885         }
    886         if self.contains(Promise::Video) {
    887             p.extend_from_slice(b"video ");
    888         }
    889         if self.contains(Promise::Vminfo) {
    890             p.extend_from_slice(b"vminfo ");
    891         }
    892         if self.contains(Promise::Vmm) {
    893             p.extend_from_slice(b"vmm ");
    894         }
    895         if self.contains(Promise::Wpath) {
    896             p.extend_from_slice(b"wpath ");
    897         }
    898         if self.contains(Promise::Wroute) {
    899             p.extend_from_slice(b"wroute ");
    900         }
    901         let idx = p.len();
    902         if idx == 0 {
    903             p.push(0);
    904         } else {
    905             // All promises have a space after them which means
    906             // we must replace the last promise's space with
    907             // 0/nul byte.
    908             // `idx` is at least 1 based on the above check.
    909             p[idx - 1] = 0;
    910         }
    911         // SAFETY:
    912         // `p` was populated above with correct ASCII-encoding of the literal
    913         // values all of which do not have 0 bytes.
    914         // `p` ends with a 0/nul byte.
    915         let arg = unsafe { CString::from_vec_with_nul_unchecked(p) };
    916         Self::inner_pledge(arg.as_ptr())
    917     }
    918     /// Invokes [`pledge(2)`](https://man.openbsd.org/pledge.2) with `NULL` for both `promises` and
    919     /// `execpromises`.
    920     ///
    921     /// # Errors
    922     ///
    923     /// Returns [`Error`] iff `pledge(2)` errors.
    924     ///
    925     /// # Examples
    926     ///
    927     /// ```no_run
    928     /// # #[cfg(target_os = "openbsd")]
    929     /// # use priv_sep::Promises;
    930     /// # #[cfg(target_os = "openbsd")]
    931     /// assert!(Promises::pledge_none().is_ok());
    932     /// ```
    933     #[inline]
    934     pub fn pledge_none() -> Result<(), Error> {
    935         // `NULL` is always valid for `promises`.
    936         Self::inner_pledge(ptr::null())
    937     }
    938 }
    939 impl PartialEq<&Self> for Promises {
    940     #[inline]
    941     fn eq(&self, other: &&Self) -> bool {
    942         *self == **other
    943     }
    944 }
    945 impl PartialEq<Promises> for &Promises {
    946     #[inline]
    947     fn eq(&self, other: &Promises) -> bool {
    948         **self == *other
    949     }
    950 }
    951 /// A permission in [`Permissions`].
    952 #[derive(Clone, Copy, Debug, Eq, PartialEq)]
    953 pub enum Permission {
    954     /// [c](https://man.openbsd.org/unveil.2#c).
    955     Create,
    956     /// [x](https://man.openbsd.org/unveil.2#x).
    957     Execute,
    958     /// [r](https://man.openbsd.org/unveil.2#r).
    959     Read,
    960     /// [w](https://man.openbsd.org/unveil.2#w).
    961     Write,
    962 }
    963 impl Permission {
    964     /// Transforms `self` into a `u8`.
    965     const fn to_u8(self) -> u8 {
    966         match self {
    967             Self::Create => 1,
    968             Self::Execute => 2,
    969             Self::Read => 4,
    970             Self::Write => 8,
    971         }
    972     }
    973 }
    974 impl PartialEq<&Self> for Permission {
    975     #[inline]
    976     fn eq(&self, other: &&Self) -> bool {
    977         *self == **other
    978     }
    979 }
    980 impl PartialEq<Permission> for &Permission {
    981     #[inline]
    982     fn eq(&self, other: &Permission) -> bool {
    983         **self == *other
    984     }
    985 }
    986 /// `permissions` to [`unveil(2)`](https://man.openbsd.org/unveil.2).
    987 #[derive(Clone, Copy, Debug, Eq, PartialEq)]
    988 pub struct Permissions(u8);
    989 impl Permissions {
    990     /// A `Permissions` with no [`Permission`]s enabled.
    991     pub const NONE: Self = Self(0);
    992     /// A `Permissions` with all [`Permission`]s enabled.
    993     pub const ALL: Self = Self::NONE
    994         .enable(Permission::Create)
    995         .enable(Permission::Execute)
    996         .enable(Permission::Read)
    997         .enable(Permission::Write);
    998     /// A `Permissions` with only [`Permission::Create`] enabled.
    999     pub const CREATE: Self = Self::NONE.enable(Permission::Create);
   1000     /// A `Permissions` with only [`Permission::Execute`] enabled.
   1001     pub const EXECUTE: Self = Self::NONE.enable(Permission::Execute);
   1002     /// A `Permissions` with only [`Permission::Read`] enabled.
   1003     pub const READ: Self = Self::NONE.enable(Permission::Read);
   1004     /// A `Permissions` with only [`Permission::Write`] enabled.
   1005     pub const WRITE: Self = Self::NONE.enable(Permission::Write);
   1006     /// `const` version of [`Self::bitor`].
   1007     #[inline]
   1008     #[must_use]
   1009     pub const fn enable(self, permission: Permission) -> Self {
   1010         Self(self.0 | permission.to_u8())
   1011     }
   1012     /// Returns `true` iff `self` has `permission` enabled.
   1013     ///
   1014     /// # Examples
   1015     ///
   1016     /// ```
   1017     /// # #[cfg(target_os = "openbsd")]
   1018     /// # use priv_sep::{Permission, Permissions};
   1019     /// # #[cfg(target_os = "openbsd")]
   1020     /// let perms = Permissions::CREATE;
   1021     /// # #[cfg(target_os = "openbsd")]
   1022     /// assert!(perms.is_enabled(Permission::Create));
   1023     /// # #[cfg(target_os = "openbsd")]
   1024     /// assert!(!perms.is_enabled(Permission::Write));
   1025     /// ```
   1026     #[inline]
   1027     #[must_use]
   1028     pub const fn is_enabled(self, permission: Permission) -> bool {
   1029         let val = permission.to_u8();
   1030         self.0 & val == val
   1031     }
   1032     /// Invokes `unveil(2)` passing `path` for `path` and `permissions` for `permissions`.
   1033     ///
   1034     /// This function MUST only be called by the functions [`Self::unveil`] and [`Self::unveil_no_more`].
   1035     #[expect(unsafe_code, reason = "unveil(2) takes in pointers")]
   1036     fn inner_unveil(path: *const c_char, permissions: *const c_char) -> Result<(), Error> {
   1037         // SAFETY:
   1038         // `path` and `permissions` are either null or valid as can be seen in the only functions that call this
   1039         // function:
   1040         // `Self::unveil` and `Self::unveil_no_more`.
   1041         if unsafe { unveil(path, permissions) } == SUCCESS {
   1042             Ok(())
   1043         } else {
   1044             Err(Error::last_os_error())
   1045         }
   1046     }
   1047     /// Invokes [`unveil(2)`](https://man.openbsd.org/unveil.2)
   1048     /// passing `path` for `path` and the contained permissions for `permissions`.
   1049     ///
   1050     /// # Errors
   1051     ///
   1052     /// Returns [`NulError`] iff [`CString::new`] does.
   1053     /// Returns [`Error`] iff `unveil(2)` errors.
   1054     ///
   1055     /// # Examples
   1056     ///
   1057     /// ```no_run
   1058     /// # #[cfg(target_os = "openbsd")]
   1059     /// # use std::io::ErrorKind;
   1060     /// # #[cfg(target_os = "openbsd")]
   1061     /// # use priv_sep::{Permissions, NulOrIoErr};
   1062     /// # #[cfg(target_os = "openbsd")]
   1063     /// assert!(Permissions::READ.unveil("/path/to/read").is_ok());
   1064     /// # #[cfg(target_os = "openbsd")]
   1065     /// assert!(Permissions::READ.unveil("/path/does/not/exist").map_or_else(
   1066     ///     |err| match err {
   1067     ///         NulOrIoErr::Io(e) => e.kind() == ErrorKind::NotFound,
   1068     ///         NulOrIoErr::Nul(_) => false,
   1069     ///     },
   1070     ///     |()| false
   1071     /// ));
   1072     /// ```
   1073     #[expect(
   1074         unsafe_code,
   1075         reason = "we manually construct a valid CString and ensure its safety"
   1076     )]
   1077     #[expect(
   1078         clippy::arithmetic_side_effects,
   1079         reason = "we pre-allocate a Vec with the exact capacity which is capped at 5"
   1080     )]
   1081     #[inline]
   1082     pub fn unveil<P: AsRef<Path>>(self, path: P) -> Result<(), NulOrIoErr> {
   1083         CString::new(path.as_ref().as_os_str().as_bytes()).map_or_else(
   1084             |e| Err(NulOrIoErr::Nul(e)),
   1085             |path_c| {
   1086                 // The max sum is 5, so overflow is not possible.
   1087                 let mut vec = Vec::with_capacity(
   1088                     usize::from(self.is_enabled(Permission::Create))
   1089                         + usize::from(self.is_enabled(Permission::Execute))
   1090                         + usize::from(self.is_enabled(Permission::Read))
   1091                         + usize::from(self.is_enabled(Permission::Write))
   1092                         + 1,
   1093                 );
   1094                 if self.is_enabled(Permission::Create) {
   1095                     vec.push(b'c');
   1096                 }
   1097                 if self.is_enabled(Permission::Execute) {
   1098                     vec.push(b'x');
   1099                 }
   1100                 if self.is_enabled(Permission::Read) {
   1101                     vec.push(b'r');
   1102                 }
   1103                 if self.is_enabled(Permission::Write) {
   1104                     vec.push(b'w');
   1105                 }
   1106                 vec.push(0);
   1107                 // SAFETY:
   1108                 // `vec` was populated above with correct ASCII-encoding of the literal
   1109                 // values all of which do not have 0 bytes.
   1110                 // `vec` ends with a 0/nul byte.
   1111                 let perm_c = unsafe { CString::from_vec_with_nul_unchecked(vec) };
   1112                 Self::inner_unveil(path_c.as_ptr(), perm_c.as_ptr()).map_err(NulOrIoErr::Io)
   1113             },
   1114         )
   1115     }
   1116     /// Invokes [`unveil(2)`](https://man.openbsd.org/unveil.2) by passing `NULL` for both `path` and
   1117     /// `permissions`.
   1118     ///
   1119     /// # Errors
   1120     ///
   1121     /// Returns [`Error`] when a problem occurs.
   1122     ///
   1123     /// # Examples
   1124     ///
   1125     /// ```no_run
   1126     /// # #[cfg(target_os = "openbsd")]
   1127     /// # use priv_sep::Permissions;
   1128     /// # #[cfg(target_os = "openbsd")]
   1129     /// assert!(Permissions::unveil_no_more().is_ok());
   1130     /// ```
   1131     #[inline]
   1132     pub fn unveil_no_more() -> Result<(), Error> {
   1133         // `NULL` is valid for both `path` and `permissions`.
   1134         Self::inner_unveil(ptr::null(), ptr::null())
   1135     }
   1136 }
   1137 impl Display for Permissions {
   1138     #[inline]
   1139     fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
   1140         write!(
   1141             f,
   1142             "unveil(2) '{}{}{}{}' permissions",
   1143             if self.is_enabled(Permission::Create) {
   1144                 "c"
   1145             } else {
   1146                 ""
   1147             },
   1148             if self.is_enabled(Permission::Execute) {
   1149                 "x"
   1150             } else {
   1151                 ""
   1152             },
   1153             if self.is_enabled(Permission::Read) {
   1154                 "r"
   1155             } else {
   1156                 ""
   1157             },
   1158             if self.is_enabled(Permission::Write) {
   1159                 "w"
   1160             } else {
   1161                 ""
   1162             },
   1163         )
   1164     }
   1165 }
   1166 impl BitAnd<Permission> for Permissions {
   1167     type Output = Self;
   1168     #[inline]
   1169     fn bitand(self, rhs: Permission) -> Self::Output {
   1170         Self(self.0 & rhs.to_u8())
   1171     }
   1172 }
   1173 impl BitAndAssign<Permission> for Permissions {
   1174     #[inline]
   1175     fn bitand_assign(&mut self, rhs: Permission) {
   1176         self.0 &= rhs.to_u8();
   1177     }
   1178 }
   1179 impl BitOr<Permission> for Permissions {
   1180     type Output = Self;
   1181     #[inline]
   1182     fn bitor(self, rhs: Permission) -> Self::Output {
   1183         Self(self.0 | rhs.to_u8())
   1184     }
   1185 }
   1186 impl BitOrAssign<Permission> for Permissions {
   1187     #[inline]
   1188     fn bitor_assign(&mut self, rhs: Permission) {
   1189         self.0 |= rhs.to_u8();
   1190     }
   1191 }
   1192 impl BitXor<Permission> for Permissions {
   1193     type Output = Self;
   1194     #[inline]
   1195     fn bitxor(self, rhs: Permission) -> Self::Output {
   1196         Self(self.0 ^ rhs.to_u8())
   1197     }
   1198 }
   1199 impl BitXorAssign<Permission> for Permissions {
   1200     #[inline]
   1201     fn bitxor_assign(&mut self, rhs: Permission) {
   1202         self.0 ^= rhs.to_u8();
   1203     }
   1204 }
   1205 impl Not for Permissions {
   1206     type Output = Self;
   1207     #[inline]
   1208     fn not(self) -> Self::Output {
   1209         Self(Self::ALL.0 & !self.0)
   1210     }
   1211 }
   1212 impl PartialEq<&Self> for Permissions {
   1213     #[inline]
   1214     fn eq(&self, other: &&Self) -> bool {
   1215         *self == **other
   1216     }
   1217 }
   1218 impl PartialEq<Permissions> for &Permissions {
   1219     #[inline]
   1220     fn eq(&self, other: &Permissions) -> bool {
   1221         **self == *other
   1222     }
   1223 }
   1224 #[cfg(test)]
   1225 mod tests {
   1226     use super::{Permissions, Promise, Promises};
   1227     use crate::{PrivDropErr, Uid};
   1228     use core::net::{Ipv6Addr, SocketAddrV6};
   1229     use std::{fs, io::Error, net::TcpListener};
   1230     const README: &str = "README.md";
   1231     #[test]
   1232     #[ignore]
   1233     fn test_pledge_unveil() {
   1234         const FILE_EXISTS: &str = "/home/zack/foo.txt";
   1235         _ = fs::metadata(FILE_EXISTS)
   1236             .expect(format!("{FILE_EXISTS} does not exist, so unit testing cannot occur").as_str());
   1237         const FILE_NOT_EXISTS: &str = "/home/zack/aadkjfasj3s23";
   1238         drop(fs::metadata(FILE_NOT_EXISTS).expect_err(
   1239             format!("{FILE_NOT_EXISTS} exists, so unit testing cannot occur").as_str(),
   1240         ));
   1241         const DIR_NOT_EXISTS: &str = "/home/zack/aadkjfasj3s23/";
   1242         drop(
   1243             fs::metadata(DIR_NOT_EXISTS).expect_err(
   1244                 format!("{DIR_NOT_EXISTS} exists, so unit testing cannot occur").as_str(),
   1245             ),
   1246         );
   1247         // This tests that a NULL `promise` does nothing.
   1248         assert!(Promises::pledge_none().is_ok());
   1249         print!("");
   1250         assert!(Promises::ALL.pledge().is_ok());
   1251         // This tests that duplicates are ignored as well as the implementation of PartialEq.
   1252         let mut initial_promises = Promises::new([
   1253             Promise::Stdio,
   1254             Promise::Unveil,
   1255             Promise::Rpath,
   1256             Promise::Stdio,
   1257         ]);
   1258         assert!(initial_promises.len() == 3);
   1259         assert!(
   1260             initial_promises == Promises::new([Promise::Rpath, Promise::Stdio, Promise::Unveil])
   1261         );
   1262         // Test retain.
   1263         assert!({
   1264             let mut vals = Promises::new([
   1265                 Promise::Audio,
   1266                 Promise::Bpf,
   1267                 Promise::Chown,
   1268                 Promise::Cpath,
   1269                 Promise::Error,
   1270                 Promise::Exec,
   1271             ]);
   1272             vals.retain([Promise::Error, Promise::Chown]);
   1273             vals.len() == 2 && vals.contains(Promise::Chown) && vals.contains(Promise::Error)
   1274         });
   1275         assert!(initial_promises.pledge().is_ok());
   1276         // This tests unveil with no permissions.
   1277         assert!(Permissions::NONE.unveil(FILE_EXISTS).is_ok());
   1278         assert!(fs::metadata(FILE_EXISTS).is_err());
   1279         // This tests unveil with read permissions,
   1280         // and one can unveil more permissions (unlike pledge which can only remove promises).
   1281         assert!(Permissions::READ.unveil(FILE_EXISTS).is_ok());
   1282         assert!(fs::metadata(FILE_EXISTS).is_ok());
   1283         // This tests that calls to unveil on missing files don't error.
   1284         assert!(Permissions::NONE.unveil(FILE_NOT_EXISTS).is_ok());
   1285         // This tests that calls to unveil on missing directories error.
   1286         assert!(Permissions::NONE.unveil(DIR_NOT_EXISTS).is_err());
   1287         // This tests that unveil can no longer be called.
   1288         assert!(Permissions::unveil_no_more().is_ok());
   1289         assert!(Permissions::NONE.unveil(FILE_EXISTS).is_err());
   1290         assert!(fs::metadata(FILE_EXISTS).is_ok());
   1291         // The below tests that Promises can only be removed and not added.
   1292         initial_promises.remove_promises([Promise::Unveil]);
   1293         assert_eq!(initial_promises.len(), 2);
   1294         initial_promises.remove(Promise::Rpath);
   1295         assert_eq!(initial_promises.len(), 1);
   1296         initial_promises.remove(Promise::Rpath);
   1297         assert_eq!(initial_promises.len(), 1);
   1298         assert!(initial_promises.pledge().is_ok());
   1299         print!("");
   1300         assert!(Promises::new([Promise::Rpath]).pledge().is_err());
   1301         // If the below is uncommented, the program should crash since the above
   1302         // call to pledge no longer allows access to the file system.
   1303         // drop(fs::metadata(FILE_EXISTS));
   1304     }
   1305     #[test]
   1306     #[ignore]
   1307     fn test_pledge_priv_drop() -> Result<(), PrivDropErr<Error>> {
   1308         if Uid::geteuid().is_root() {
   1309             Promises::new_priv_drop(
   1310                 "zack",
   1311                 [Promise::Inet, Promise::Rpath, Promise::Unveil],
   1312                 false,
   1313                 || TcpListener::bind(SocketAddrV6::new(Ipv6Addr::LOCALHOST, 443, 0, 0)),
   1314             )
   1315             .and_then(|(_, mut promises)| {
   1316                 Permissions::READ
   1317                     .unveil(README)
   1318                     .map_err(PrivDropErr::from)
   1319                     .and_then(|()| {
   1320                         fs::exists(README)
   1321                             .map_err(PrivDropErr::Io)
   1322                             .and_then(|exists| {
   1323                                 Permissions::NONE
   1324                                     .unveil(README)
   1325                                     .map_err(PrivDropErr::from)
   1326                                     .and_then(|()| {
   1327                                         promises
   1328                                             .remove_promises_then_pledge([
   1329                                                 Promise::Rpath,
   1330                                                 Promise::Unveil,
   1331                                             ])
   1332                                             .map_err(PrivDropErr::Io)
   1333                                             .map(|()| {
   1334                                                 assert!(exists);
   1335                                                 assert!(
   1336                                                     TcpListener::bind(SocketAddrV6::new(
   1337                                                         Ipv6Addr::LOCALHOST,
   1338                                                         80,
   1339                                                         0,
   1340                                                         0
   1341                                                     ))
   1342                                                     .is_err()
   1343                                                 );
   1344                                             })
   1345                                     })
   1346                             })
   1347                     })
   1348             })
   1349         } else {
   1350             Ok(())
   1351         }
   1352     }
   1353     #[test]
   1354     #[ignore]
   1355     fn test_pledge_chroot_priv_drop() -> Result<(), PrivDropErr<Error>> {
   1356         if Uid::geteuid().is_root() {
   1357             Promises::new_chroot_then_priv_drop(
   1358                 "zack",
   1359                 "./",
   1360                 [Promise::Inet, Promise::Rpath, Promise::Unveil],
   1361                 false,
   1362                 || TcpListener::bind(SocketAddrV6::new(Ipv6Addr::LOCALHOST, 443, 0, 0)),
   1363             )
   1364             .and_then(|(_, mut promises)| {
   1365                 Permissions::READ
   1366                     .unveil(README)
   1367                     .map_err(PrivDropErr::from)
   1368                     .and_then(|()| {
   1369                         fs::exists(README)
   1370                             .map_err(PrivDropErr::Io)
   1371                             .and_then(|exists| {
   1372                                 Permissions::NONE
   1373                                     .unveil(README)
   1374                                     .map_err(PrivDropErr::from)
   1375                                     .and_then(|()| {
   1376                                         promises
   1377                                             .remove_promises_then_pledge([
   1378                                                 Promise::Rpath,
   1379                                                 Promise::Unveil,
   1380                                             ])
   1381                                             .map_err(PrivDropErr::Io)
   1382                                             .map(|()| {
   1383                                                 assert!(exists);
   1384                                                 assert!(
   1385                                                     TcpListener::bind(SocketAddrV6::new(
   1386                                                         Ipv6Addr::LOCALHOST,
   1387                                                         80,
   1388                                                         0,
   1389                                                         0
   1390                                                     ))
   1391                                                     .is_err()
   1392                                                 );
   1393                                             })
   1394                                     })
   1395                             })
   1396                     })
   1397             })
   1398         } else {
   1399             Ok(())
   1400         }
   1401     }
   1402 }