priv_sep

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

openbsd.rs (50818B)


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