priv_sep

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

openbsd.rs (51152B)


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