priv_sep

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

openbsd.rs (50990B)


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