priv_sep

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

lib.rs (42960B)


      1 //! [![git]](https://git.philomathiclife.com/priv_sep/log.html) [![crates-io]](https://crates.io/crates/priv_sep) [![docs-rs]](crate)
      2 //!
      3 //! [git]: https://git.philomathiclife.com/git_badge.svg
      4 //! [crates-io]: https://img.shields.io/badge/crates.io-fc8d62?style=for-the-badge&labelColor=555555&logo=rust
      5 //! [docs-rs]: https://img.shields.io/badge/docs.rs-66c2a5?style=for-the-badge&labelColor=555555&logo=docs.rs
      6 //!
      7 //! `priv_sep` is a library that uses the system's libc to perform privilege separation and privilege reduction.
      8 //!
      9 //! ## `priv_sep` in action for OpenBSD
     10 //!
     11 //! ```no_run
     12 //! use core::convert::Infallible;
     13 //! # #[cfg(target_os = "openbsd")]
     14 //! use priv_sep::{Permissions, PrivDropErr, Promise, Promises};
     15 //! use std::{
     16 //!     fs,
     17 //!     io::Error,
     18 //!     net::{Ipv6Addr, SocketAddrV6},
     19 //! };
     20 //! use tokio::net::TcpListener;
     21 //! # #[cfg(not(target_os = "openbsd"))]
     22 //! # fn main() {}
     23 //! # #[cfg(target_os = "openbsd")]
     24 //! #[tokio::main(flavor = "current_thread")]
     25 //! async fn main() -> Result<Infallible, PrivDropErr<Error>> {
     26 //!     /// Config file.
     27 //!     const CONFIG: &str = "config";
     28 //!     // Get the user ID and group ID for nobody from `passwd(5)`.
     29 //!     // `chroot(2)` to `/path/chroot/` and `chdir(2)` to `/`.
     30 //!     // `pledge(2)` `id`, `inet`, `rpath`, `stdio`, and `unveil`.
     31 //!     // Bind to TCP `[::1]:443` as root.
     32 //!     // `setresgid(2)` to the group ID associated with nobody.
     33 //!     // `setresuid(2)` to the user ID associated with nobody.
     34 //!     // Remove `id` from our `pledge(2)`d promises.
     35 //!     let (listener, mut promises) = Promises::new_chroot_then_priv_drop_async(
     36 //!         "nobody",
     37 //!         "/path/chroot/",
     38 //!         [Promise::Inet, Promise::Rpath, Promise::Unveil],
     39 //!         false,
     40 //!         async || TcpListener::bind(SocketAddrV6::new(Ipv6Addr::LOCALHOST, 443, 0, 0)).await,
     41 //!     ).await?;
     42 //!     // At this point, the process is running under nobody.
     43 //!     // Only allow file system access to `config` and only allow read access to it.
     44 //!     Permissions::READ.unveil(CONFIG)?;
     45 //!     // Read `config`.
     46 //!     // This will of course fail if the file does not exist or nobody does not
     47 //!     // have read permissions.
     48 //!     let config = fs::read(CONFIG)?;
     49 //!     // Remove file system access.
     50 //!     Permissions::NONE.unveil(CONFIG)?;
     51 //!     // Remove `rpath` and `unveil` from our `pledge(2)`d promises
     52 //!     // (i.e., only have `inet` and `stdio` abilities when we begin accepting TCP connections).
     53 //!     promises.remove_promises_then_pledge([Promise::Rpath, Promise::Unveil])?;
     54 //!     loop {
     55 //!         // Handle TCP connections.
     56 //!         if let Ok((_, ip)) = listener.accept().await {
     57 //!             assert!(ip.is_ipv6());
     58 //!         }
     59 //!     }
     60 //! }
     61 //! ```
     62 //!
     63 //! ## `priv_sep` in action for Unix-like OSes
     64 //!
     65 //! ```no_run
     66 //! use core::convert::Infallible;
     67 //! use priv_sep::{UserInfo, PrivDropErr};
     68 //! use std::{
     69 //!     io::Error,
     70 //!     net::{Ipv6Addr, SocketAddrV6},
     71 //! };
     72 //! use tokio::net::TcpListener;
     73 //! #[tokio::main(flavor = "current_thread")]
     74 //! async fn main() -> Result<Infallible, PrivDropErr<Error>> {
     75 //!     // Get the user ID and group ID for nobody from `passwd(5)`.
     76 //!     // `chroot(2)` to `/path/chroot/` and `chdir(2)` to `/`.
     77 //!     // Bind to TCP `[::1]:443` as root.
     78 //!     // `setresgid(2)` to the group ID associated with nobody.
     79 //!     // `setresuid(2)` to the user ID associated with nobody.
     80 //!     let listener = UserInfo::chroot_then_priv_drop_async("nobody", "/path/chroot/", false, async || {
     81 //!         TcpListener::bind(SocketAddrV6::new(Ipv6Addr::LOCALHOST, 443, 0, 0)).await
     82 //!     }).await?;
     83 //!     // At this point, the process is running under nobody.
     84 //!     loop {
     85 //!         // Handle TCP connections.
     86 //!         if let Ok((_, ip)) = listener.accept().await {
     87 //!             assert!(ip.is_ipv6());
     88 //!         }
     89 //!     }
     90 //! }
     91 //! ```
     92 #![cfg_attr(docsrs, feature(doc_cfg))]
     93 #![allow(clippy::pub_use, reason = "don't want openbsd types in a module")]
     94 extern crate alloc;
     95 /// C FFI.
     96 mod c;
     97 /// OpenBSD
     98 #[cfg(any(doc, target_os = "openbsd"))]
     99 mod openbsd;
    100 use alloc::ffi::{CString, NulError};
    101 use c::{IdT, SUCCESS};
    102 use core::{
    103     error::Error as CoreErr,
    104     ffi::{CStr, c_char, c_int},
    105     fmt::{self, Display, Formatter},
    106     mem::MaybeUninit,
    107     ptr,
    108 };
    109 #[cfg_attr(docsrs, doc(cfg(target_os = "openbsd")))]
    110 #[cfg(any(doc, target_os = "openbsd"))]
    111 pub use openbsd::{Permission, Permissions, Promise, Promises};
    112 use std::{io::Error, os::unix::ffi::OsStrExt as _, path::Path};
    113 /// [`uid_t`](https://pubs.opengroup.org/onlinepubs/9799919799.2024edition/basedefs/sys_types.h.html).
    114 #[derive(Clone, Copy, Debug, Eq, PartialEq)]
    115 pub struct Uid(pub IdT);
    116 impl Uid {
    117     /// The root user ID (i.e., 0).
    118     pub const ROOT: Self = Self(0);
    119     /// Returns `true` iff `self` is [`Self::ROOT`].
    120     ///
    121     /// # Examples
    122     ///
    123     /// ```no_run
    124     /// # use priv_sep::Uid;
    125     /// assert!(Uid::ROOT.is_root());
    126     /// ```
    127     #[inline]
    128     #[must_use]
    129     pub const fn is_root(self) -> bool {
    130         self.0 == Self::ROOT.0
    131     }
    132     /// [`getuid`](https://pubs.opengroup.org/onlinepubs/9799919799.2024edition/functions/getuid.html).
    133     ///
    134     /// # Examples
    135     ///
    136     /// ```no_run
    137     /// # use priv_sep::Uid;
    138     /// assert_eq!(Uid::getuid(), 1000);
    139     /// ```
    140     #[inline]
    141     #[must_use]
    142     pub fn getuid() -> Self {
    143         Self(c::getuid())
    144     }
    145     /// [`geteuid`](https://pubs.opengroup.org/onlinepubs/9799919799.2024edition/functions/geteuid.html).
    146     ///
    147     /// # Examples
    148     ///
    149     /// ```no_run
    150     /// # use priv_sep::Uid;
    151     /// assert_eq!(Uid::geteuid(), 1000);
    152     /// ```
    153     #[inline]
    154     #[must_use]
    155     pub fn geteuid() -> Self {
    156         Self(c::geteuid())
    157     }
    158     /// Calls [`setresuid`](https://pubs.opengroup.org/onlinepubs/9799919799.2024edition/functions/setresuid.html)
    159     /// passing `self` for the real, effective, and saved user IDs.
    160     ///
    161     /// # Errors
    162     ///
    163     /// Errors iff `setresuid` does.
    164     ///
    165     /// # Examples
    166     ///
    167     /// ```no_run
    168     /// # use priv_sep::Uid;
    169     /// assert!(Uid(1000).setresuid().is_ok());
    170     /// ```
    171     #[inline]
    172     pub fn setresuid(self) -> Result<(), Error> {
    173         if c::setresuid(self.0, self.0, self.0) == SUCCESS {
    174             Ok(())
    175         } else {
    176             Err(Error::last_os_error())
    177         }
    178     }
    179 }
    180 impl PartialEq<&Self> for Uid {
    181     #[inline]
    182     fn eq(&self, other: &&Self) -> bool {
    183         *self == **other
    184     }
    185 }
    186 impl PartialEq<Uid> for &Uid {
    187     #[inline]
    188     fn eq(&self, other: &Uid) -> bool {
    189         **self == *other
    190     }
    191 }
    192 impl PartialEq<IdT> for Uid {
    193     #[inline]
    194     fn eq(&self, other: &IdT) -> bool {
    195         self.0 == *other
    196     }
    197 }
    198 impl From<Uid> for IdT {
    199     #[inline]
    200     fn from(value: Uid) -> Self {
    201         value.0
    202     }
    203 }
    204 impl From<IdT> for Uid {
    205     #[inline]
    206     fn from(value: IdT) -> Self {
    207         Self(value)
    208     }
    209 }
    210 /// [`gid_t`](https://pubs.opengroup.org/onlinepubs/9799919799.2024edition/basedefs/sys_types.h.html).
    211 #[derive(Clone, Copy, Debug, Eq, PartialEq)]
    212 pub struct Gid(pub IdT);
    213 impl Gid {
    214     /// [`getgid`](https://pubs.opengroup.org/onlinepubs/9799919799.2024edition/functions/getgid.html).
    215     ///
    216     /// # Examples
    217     ///
    218     /// ```no_run
    219     /// # use priv_sep::Gid;
    220     /// assert_eq!(Gid::getgid(), 1000);
    221     /// ```
    222     #[inline]
    223     #[must_use]
    224     pub fn getgid() -> Self {
    225         Self(c::getgid())
    226     }
    227     /// [`getegid`](https://pubs.opengroup.org/onlinepubs/9799919799.2024edition/functions/getegid.html).
    228     ///
    229     /// # Examples
    230     ///
    231     /// ```no_run
    232     /// # use priv_sep::Gid;
    233     /// assert_eq!(Gid::getegid(), 1000);
    234     /// ```
    235     #[inline]
    236     #[must_use]
    237     pub fn getegid() -> Self {
    238         Self(c::getegid())
    239     }
    240     /// Calls [`setresgid`](https://pubs.opengroup.org/onlinepubs/9799919799.2024edition/functions/setresgid.html)
    241     /// passing `self` for the real, effective, and saved group IDs.
    242     ///
    243     /// # Errors
    244     ///
    245     /// Errors iff `setresgid` does.
    246     ///
    247     /// # Examples
    248     ///
    249     /// ```no_run
    250     /// # use priv_sep::Gid;
    251     /// assert!(Gid(1000).setresgid().is_ok());
    252     /// ```
    253     #[inline]
    254     pub fn setresgid(self) -> Result<(), Error> {
    255         if c::setresgid(self.0, self.0, self.0) == SUCCESS {
    256             Ok(())
    257         } else {
    258             Err(Error::last_os_error())
    259         }
    260     }
    261 }
    262 impl PartialEq<&Self> for Gid {
    263     #[inline]
    264     fn eq(&self, other: &&Self) -> bool {
    265         *self == **other
    266     }
    267 }
    268 impl PartialEq<Gid> for &Gid {
    269     #[inline]
    270     fn eq(&self, other: &Gid) -> bool {
    271         **self == *other
    272     }
    273 }
    274 impl PartialEq<IdT> for Gid {
    275     #[inline]
    276     fn eq(&self, other: &IdT) -> bool {
    277         self.0 == *other
    278     }
    279 }
    280 impl From<Gid> for IdT {
    281     #[inline]
    282     fn from(value: Gid) -> Self {
    283         value.0
    284     }
    285 }
    286 impl From<IdT> for Gid {
    287     #[inline]
    288     fn from(value: IdT) -> Self {
    289         Self(value)
    290     }
    291 }
    292 /// Error when [`CString::new`] errors or an I/O error occurs due to a libc call.
    293 #[derive(Debug)]
    294 pub enum NulOrIoErr {
    295     /// Error returned from [`CString::new`].
    296     Nul(NulError),
    297     /// Generic I/O error returned from a libc call.
    298     Io(Error),
    299 }
    300 impl Display for NulOrIoErr {
    301     #[inline]
    302     fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
    303         match *self {
    304             Self::Nul(ref err) => write!(f, "CString could not be created: {err}"),
    305             Self::Io(ref err) => write!(f, "libc I/O error: {err}"),
    306         }
    307     }
    308 }
    309 impl CoreErr for NulOrIoErr {}
    310 impl From<NulError> for NulOrIoErr {
    311     #[inline]
    312     fn from(value: NulError) -> Self {
    313         Self::Nul(value)
    314     }
    315 }
    316 impl From<Error> for NulOrIoErr {
    317     #[inline]
    318     fn from(value: Error) -> Self {
    319         Self::Io(value)
    320     }
    321 }
    322 /// [`chroot(2)`](https://manned.org/chroot.2).
    323 ///
    324 /// # Errors
    325 ///
    326 /// Returns [`NulError`] iff [`CString::new`] does.
    327 /// Returns [`Error`] iff `chroot(2)` errors.
    328 ///
    329 /// # Examples
    330 ///
    331 /// ```no_run
    332 /// assert!(priv_sep::chroot("./").is_ok());
    333 /// ```
    334 #[expect(unsafe_code, reason = "chroot(2) takes a pointer")]
    335 #[inline]
    336 pub fn chroot<P: AsRef<Path>>(path: P) -> Result<(), NulOrIoErr> {
    337     CString::new(path.as_ref().as_os_str().as_bytes())
    338         .map_err(NulOrIoErr::Nul)
    339         .and_then(|c_path| {
    340             let ptr = c_path.as_ptr();
    341             // SAFETY:
    342             // `ptr` is valid and not null.
    343             if unsafe { c::chroot(ptr) } == SUCCESS {
    344                 Ok(())
    345             } else {
    346                 Err(NulOrIoErr::Io(Error::last_os_error()))
    347             }
    348         })
    349 }
    350 /// [`chdir`](https://pubs.opengroup.org/onlinepubs/9799919799.2024edition/functions/chdir.html).
    351 ///
    352 /// This function MUST only be called by `chdir` and `chroot_then_chdir`.
    353 #[expect(unsafe_code, reason = "chdir(2) takes a pointer")]
    354 fn private_chdir(path: *const c_char) -> Result<(), Error> {
    355     // SAFETY:
    356     // `path` is valid and not null as can be seen in the only functions that call this function:
    357     // `chdir` and `chroot_then_chdir`.
    358     if unsafe { c::chdir(path) } == SUCCESS {
    359         Ok(())
    360     } else {
    361         Err(Error::last_os_error())
    362     }
    363 }
    364 /// [`chdir`](https://pubs.opengroup.org/onlinepubs/9799919799.2024edition/functions/chdir.html).
    365 ///
    366 /// # Errors
    367 ///
    368 /// Returns [`NulError`] iff [`CString::new`] does.
    369 /// Returns [`Error`] iff `chdir` errors.
    370 ///
    371 /// # Examples
    372 ///
    373 /// ```no_run
    374 /// assert!(priv_sep::chdir("/").is_ok());
    375 /// ```
    376 #[inline]
    377 pub fn chdir<P: AsRef<Path>>(path: P) -> Result<(), NulOrIoErr> {
    378     CString::new(path.as_ref().as_os_str().as_bytes())
    379         .map_err(NulOrIoErr::Nul)
    380         .and_then(|c_path| private_chdir(c_path.as_ptr()).map_err(NulOrIoErr::Io))
    381 }
    382 /// Calls [`chroot`] on `path` followed by a call to [`chdir`] on `"/"`.
    383 ///
    384 /// # Errors
    385 ///
    386 /// Errors iff `chroot` or `chdir` do.
    387 ///
    388 /// # Examples
    389 ///
    390 /// ```no_run
    391 /// assert!(priv_sep::chroot_then_chdir("./").is_ok());
    392 /// ```
    393 #[inline]
    394 pub fn chroot_then_chdir<P: AsRef<Path>>(path: P) -> Result<(), NulOrIoErr> {
    395     /// Root directory.
    396     const ROOT: *const c_char = c"/".as_ptr();
    397     chroot(path).and_then(|()| private_chdir(ROOT).map_err(NulOrIoErr::Io))
    398 }
    399 /// Error returned when dropping privileges.
    400 #[derive(Debug)]
    401 pub enum PrivDropErr<E> {
    402     /// Error when [`CString::new`] errors.
    403     Nul(NulError),
    404     /// Error when an I/O error occurs from a libc call.
    405     Io(Error),
    406     /// Error when there is no entry in the user database corresponding to the passed username.
    407     NoPasswdEntry,
    408     /// Error when [`UserInfo::is_root`].
    409     RootEntry,
    410     /// Error returned from the user-provided function that is invoked before calling [`UserInfo::setresid`].
    411     Other(E),
    412 }
    413 impl<E: Display> Display for PrivDropErr<E> {
    414     #[inline]
    415     fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
    416         match *self {
    417             Self::Nul(ref err) => write!(
    418                 f,
    419                 "CString could not be created from the username to drop privileges to: {err}"
    420             ),
    421             Self::Io(ref err) => write!(f, "libc I/O error when dropping privileges: {err}"),
    422             Self::NoPasswdEntry => f.write_str("no passwd(5) entry to drop privileges to"),
    423             Self::RootEntry => f.write_str(
    424                 "setresuid(2) is not allowed to be called on uid 0 when dropping privileges",
    425             ),
    426             Self::Other(ref err) => write!(
    427                 f,
    428                 "error calling function before dropping privileges: {err}"
    429             ),
    430         }
    431     }
    432 }
    433 impl<E: CoreErr> CoreErr for PrivDropErr<E> {}
    434 impl<E> From<NulError> for PrivDropErr<E> {
    435     #[inline]
    436     fn from(value: NulError) -> Self {
    437         Self::Nul(value)
    438     }
    439 }
    440 impl<E> From<Error> for PrivDropErr<E> {
    441     #[inline]
    442     fn from(value: Error) -> Self {
    443         Self::Io(value)
    444     }
    445 }
    446 impl<E> From<NulOrIoErr> for PrivDropErr<E> {
    447     #[inline]
    448     fn from(value: NulOrIoErr) -> Self {
    449         match value {
    450             NulOrIoErr::Nul(e) => Self::Nul(e),
    451             NulOrIoErr::Io(e) => Self::Io(e),
    452         }
    453     }
    454 }
    455 /// Error returned from [`UserInfo::setresid_if_valid`].
    456 #[derive(Debug)]
    457 pub enum SetresidErr {
    458     /// Error when an I/O error occurs from a libc call.
    459     Io(Error),
    460     /// Error when there is no entry in the user database corresponding to [`UserInfo::uid`].
    461     NoPasswdEntry,
    462     /// Error when the entry in the user database has a different gid than [`UserInfo::gid`].
    463     GidMismatch,
    464 }
    465 impl Display for SetresidErr {
    466     #[inline]
    467     fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
    468         match *self {
    469             Self::Io(ref err) => write!(f, "libc I/O error when dropping privileges: {err}"),
    470             Self::NoPasswdEntry => f.write_str("no passwd(5) entry to drop privileges to"),
    471             Self::GidMismatch => f.write_str("gid in passwd(5) does match the expected gid"),
    472         }
    473     }
    474 }
    475 impl CoreErr for SetresidErr {}
    476 impl From<Error> for SetresidErr {
    477     #[inline]
    478     fn from(value: Error) -> Self {
    479         Self::Io(value)
    480     }
    481 }
    482 /// Used by [`UserInfo::getpw_entry`].
    483 trait PwEntry {
    484     /// Calling code must uphold the following safety invariants:
    485     /// * `buf` must be a valid, initialized, non-null pointer
    486     /// * `size` must be the length of `buf`
    487     /// * `result` must be a valid, initialized non-null pointer referencing a valid and initialized pointer that
    488     ///   is allowed to be null.
    489     ///
    490     /// Implementors MUST only _write_ to `pwd` and never read from it (i.e., `pwd` is allowed to be unitialized).
    491     #[expect(
    492         unsafe_code,
    493         reason = "getpwnam_r(3) and getpwuid_r(3) take in pointers"
    494     )]
    495     unsafe fn getpw(
    496         self,
    497         pwd: *mut c::Passwd,
    498         buf: *mut c_char,
    499         size: usize,
    500         result: *mut *mut c::Passwd,
    501     ) -> c_int;
    502 }
    503 impl PwEntry for Uid {
    504     #[expect(unsafe_code, reason = "getpwuid_r(3) take in pointers")]
    505     unsafe fn getpw(
    506         self,
    507         pwd: *mut c::Passwd,
    508         buf: *mut c_char,
    509         size: usize,
    510         result: *mut *mut c::Passwd,
    511     ) -> c_int {
    512         // SAFETY:
    513         // Calling code must uphold safety invariants.
    514         // `pwd` is never read from.
    515         unsafe { c::getpwuid_r(self.0, pwd, buf, size, result) }
    516     }
    517 }
    518 /// `newtype` around `CStr`.
    519 #[derive(Clone, Copy)]
    520 struct CStrWrapper<'a>(&'a CStr);
    521 impl PwEntry for CStrWrapper<'_> {
    522     #[expect(unsafe_code, reason = "getpwnam_r(3) takes in pointers")]
    523     unsafe fn getpw(
    524         self,
    525         pwd: *mut c::Passwd,
    526         buf: *mut c_char,
    527         size: usize,
    528         result: *mut *mut c::Passwd,
    529     ) -> c_int {
    530         let ptr = self.0.as_ptr();
    531         // SAFETY:
    532         // Calling code must uphold safety invariants.
    533         // `ptr` is valid, initialized, and not null.
    534         // `pwd` is never read from.
    535         unsafe { c::getpwnam_r(ptr, pwd, buf, size, result) }
    536     }
    537 }
    538 /// User and group ID.
    539 #[derive(Clone, Copy, Debug, Eq, PartialEq)]
    540 pub struct UserInfo {
    541     /// The user ID.
    542     pub uid: Uid,
    543     /// The group ID.
    544     pub gid: Gid,
    545 }
    546 impl UserInfo {
    547     /// Returns `true` iff [`Uid::is_root`].
    548     ///
    549     /// # Examples
    550     ///
    551     /// ```no_run
    552     /// # use priv_sep::{Gid, Uid, UserInfo};
    553     /// assert!(UserInfo { uid: Uid::ROOT, gid: Gid(0), }.is_root());
    554     /// ```
    555     #[inline]
    556     #[must_use]
    557     pub const fn is_root(self) -> bool {
    558         self.uid.is_root()
    559     }
    560     /// [`getpwnam_r`](https://pubs.opengroup.org/onlinepubs/9799919799.2024edition/functions/getpwnam_r.html).
    561     ///
    562     /// Uses `buffer` to write the user database entry into returning `None` iff there is no entry; otherwise
    563     /// returns `Self`.
    564     ///
    565     /// Note it is the caller's responsibility to ensure `buffer` is large enough; otherwise an [`Error`] will
    566     /// be returned.
    567     ///
    568     /// # Errors
    569     ///
    570     /// Returns [`NulError`] iff [`CString::new`] does.
    571     /// Returns [`Error`] iff `getpwnam_r` errors.
    572     ///
    573     /// # Examples
    574     ///
    575     /// ```no_run
    576     /// # use priv_sep::{Uid, UserInfo};
    577     /// assert!(UserInfo::with_buffer("root", [0; 128].as_mut_slice())?.map_or(false, |info| info.is_root()));
    578     /// # Ok::<_, priv_sep::NulOrIoErr>(())
    579     /// ```
    580     #[expect(unsafe_code, reason = "getpwnam_r(3) takes in pointers")]
    581     #[inline]
    582     pub fn with_buffer<T: Into<Vec<u8>>>(
    583         name: T,
    584         buffer: &mut [c_char],
    585     ) -> Result<Option<Self>, NulOrIoErr> {
    586         CString::new(name).map_err(NulOrIoErr::Nul).and_then(|n| {
    587             let ptr = n.as_ptr();
    588             let mut pwd = MaybeUninit::<c::Passwd>::uninit();
    589             let pwd_ptr = pwd.as_mut_ptr();
    590             let buf_ptr = buffer.as_mut_ptr();
    591             let len = buffer.len();
    592             let mut result = ptr::null_mut();
    593             let res_ptr = &mut result;
    594             // SAFETY:
    595             // `pwd_ptr` is only written to; thus the fact `pwd` is unitialized is fine.
    596             // `buf_ptr` is valid, initialized, and not null.
    597             // `len` is the length of `buf_ptr`.
    598             // `res_ptr` is valid, initialized, and not null.
    599             // `result` is valid, initialized, and allowed to be null.
    600             let code = unsafe { c::getpwnam_r(ptr, pwd_ptr, buf_ptr, len, res_ptr) };
    601             if code == SUCCESS {
    602                 if result.is_null() {
    603                     Ok(None)
    604                 } else {
    605                     // SAFETY:
    606                     // `c::getpwnam_r` writes to `pwd` iff `result` is not null.
    607                     Ok(Some(unsafe { pwd.assume_init() }.into_user_info()))
    608                 }
    609             } else {
    610                 Err(NulOrIoErr::Io(Error::from_raw_os_error(code)))
    611             }
    612         })
    613     }
    614     /// Helper for [`Self::new`] and [`Self::setresid_if_exists`].
    615     #[expect(
    616         unsafe_code,
    617         reason = "getpwnam_r(3) and getpwuid_r(3) take in pointers"
    618     )]
    619     fn getpw_entry<P: Copy + PwEntry>(u: P) -> Result<Option<Self>, Error> {
    620         /// Initial buffer size.
    621         const INIT_CAP: usize = 128;
    622         // `2 * (MAX_CAP - 1) <= isize::MAX` MUST be true.
    623         /// Maximum buffer size.
    624         const MAX_CAP: usize = 0x4000;
    625         /// [`ERANGE`](https://man.openbsd.org/errno#Result).
    626         const ERANGE: c_int = 34;
    627         let mut buffer = Vec::with_capacity(INIT_CAP);
    628         let mut cap = buffer.capacity();
    629         let mut pwd = MaybeUninit::<c::Passwd>::uninit();
    630         let mut result = ptr::null_mut();
    631         let mut pwd_ptr;
    632         let mut res_ptr;
    633         let mut code;
    634         let mut buf_ptr;
    635         loop {
    636             pwd_ptr = pwd.as_mut_ptr();
    637             res_ptr = &mut result;
    638             buf_ptr = buffer.as_mut_ptr();
    639             // SAFETY:
    640             // `pwd_ptr` is only written to; thus the fact `pwd` is unitialized is fine.
    641             // `buf_ptr` is valid, initialized, and not null.
    642             // `cap` is the length of `buf_ptr`.
    643             // `res_ptr` is valid, initialized, and not null.
    644             // `result` is valid, initialized, and allowed to be null.
    645             code = unsafe { u.getpw(pwd_ptr, buf_ptr, cap, res_ptr) };
    646             if code == SUCCESS {
    647                 return Ok(if result.is_null() {
    648                     None
    649                 } else {
    650                     // SAFETY:
    651                     // `CStrWrapper::getpw` writes to `pwd` iff `result` is not null.
    652                     Some(unsafe { pwd.assume_init() }.into_user_info())
    653                 });
    654             } else if code == ERANGE {
    655                 if cap >= MAX_CAP {
    656                     return Err(Error::from_raw_os_error(code));
    657                 }
    658                 // `cap < MAX_CAP` and
    659                 // `2 * (MAX_CAP - 1) < isize::MAX`, so overflow is not possible.
    660                 buffer.reserve(cap << 1);
    661                 cap = buffer.capacity();
    662             } else {
    663                 return Err(Error::from_raw_os_error(code));
    664             }
    665         }
    666     }
    667     /// Same as [`Self::with_buffer`] except repeated attempts are made with progressively larger buffers up to
    668     /// 16 KiB.
    669     ///
    670     /// # Errors
    671     ///
    672     /// Errors iff [`Self::with_buffer`] does for a 16 KiB buffer.
    673     ///
    674     /// # Examples
    675     ///
    676     /// ```no_run
    677     /// # use priv_sep::UserInfo;
    678     /// assert!(UserInfo::new("root")?.map_or(false, |info| info.is_root()));
    679     /// # Ok::<_, priv_sep::NulOrIoErr>(())
    680     /// ```
    681     #[inline]
    682     pub fn new<T: Into<Vec<u8>>>(name: T) -> Result<Option<Self>, NulOrIoErr> {
    683         CString::new(name)
    684             .map_err(NulOrIoErr::Nul)
    685             .and_then(|n| Self::getpw_entry(CStrWrapper(n.as_c_str())).map_err(NulOrIoErr::Io))
    686     }
    687     /// Calls [`Gid::setresgid`] and [`Uid::setresuid`].
    688     ///
    689     /// # Errors
    690     ///
    691     /// Errors iff `Gid::setresgid` or `Uid::setresuid` error.
    692     ///
    693     /// # Examples
    694     ///
    695     /// ```no_run
    696     /// # use priv_sep::UserInfo;
    697     /// if let Some(user) = UserInfo::new("nobody")? {
    698     ///     user.setresid()?;
    699     /// }
    700     /// # Ok::<_, priv_sep::NulOrIoErr>(())
    701     /// ```
    702     #[inline]
    703     pub fn setresid(self) -> Result<(), Error> {
    704         self.gid.setresgid().and_then(|()| self.uid.setresuid())
    705     }
    706     /// Same as [`Self::setresid`] except
    707     /// [`getpwuid_r`](https://pubs.opengroup.org/onlinepubs/9799919799.2024edition/functions/getpwuid_r.html)
    708     /// is used to first confirm the existence of [`Self::uid`] and [`Self::gid`].
    709     ///
    710     /// Note this should rarely be used since most will rely on [`Self::new`], [`Self::with_buffer`],
    711     /// [`Self::priv_drop`], or [`Self::chroot_then_priv_drop`].
    712     ///
    713     /// Like [`Self::new`], this will fail if the buffer needed exceeds 16 KiB.
    714     ///
    715     /// # Errors
    716     ///
    717     /// Errors iff `getpwuid_r` errors for a 16 KiB buffer, [`Self::uid`] and [`Self::gid`] don't exist in the user
    718     /// database, [`Gid::setresgid`] errors, or [`Uid::setresuid`] errors.
    719     ///
    720     /// # Examples
    721     ///
    722     /// ```no_run
    723     /// # use priv_sep::{Gid, Uid, UserInfo};
    724     /// UserInfo { uid: Uid(1000), gid: Gid(1000), }.setresid_if_valid()?;
    725     /// # Ok::<_, priv_sep::SetresidErr>(())
    726     /// ```
    727     #[inline]
    728     pub fn setresid_if_valid(self) -> Result<(), SetresidErr> {
    729         Self::getpw_entry(self.uid)
    730             .map_err(SetresidErr::Io)
    731             .and_then(|opt| {
    732                 opt.ok_or(SetresidErr::NoPasswdEntry).and_then(|info| {
    733                     if info.gid == self.gid {
    734                         self.setresid().map_err(SetresidErr::Io)
    735                     } else {
    736                         Err(SetresidErr::GidMismatch)
    737                     }
    738                 })
    739             })
    740     }
    741     /// Calls [`Self::new`], invokes `f`, then calls [`Self::setresid`].
    742     ///
    743     /// Dropping privileges is necessary when needing to perform certain actions as root before no longer needing
    744     /// such abilities; at which point, one calls
    745     /// [`setresgid`](https://pubs.opengroup.org/onlinepubs/9799919799.2024edition/functions/setresgid.html) and
    746     /// [`setresuid`](https://pubs.opengroup.org/onlinepubs/9799919799.2024edition/functions/setresuid.html)
    747     /// using a lesser privileged gid and uid.
    748     ///
    749     /// # Errors
    750     ///
    751     /// Errors iff [`Self::new`], `f`, or [`Self::setresid`] do or there is no entry in the user database
    752     /// corresponding to `name` or the entry has uid 0.
    753     ///
    754     /// # Examples
    755     ///
    756     /// ```no_run
    757     /// # use core::net::{Ipv6Addr, SocketAddrV6};
    758     /// # use priv_sep::{PrivDropErr, UserInfo};
    759     /// # use std::{io::Error, net::TcpListener};
    760     /// let listener = UserInfo::priv_drop("nobody", || {
    761     ///     TcpListener::bind(SocketAddrV6::new(Ipv6Addr::LOCALHOST, 443, 0, 0))
    762     /// })?;
    763     /// # Ok::<_, PrivDropErr<Error>>(())
    764     /// ```
    765     #[inline]
    766     pub fn priv_drop<U: Into<Vec<u8>>, T, E, F: FnOnce() -> Result<T, E>>(
    767         name: U,
    768         f: F,
    769     ) -> Result<T, PrivDropErr<E>> {
    770         Self::new(name).map_err(PrivDropErr::from).and_then(|opt| {
    771             opt.ok_or_else(|| PrivDropErr::NoPasswdEntry)
    772                 .and_then(|info| {
    773                     if info.is_root() {
    774                         Err(PrivDropErr::RootEntry)
    775                     } else {
    776                         f().map_err(PrivDropErr::Other)
    777                             .and_then(|res| info.setresid().map_err(PrivDropErr::Io).map(|()| res))
    778                     }
    779                 })
    780         })
    781     }
    782     /// Same as [`Self::priv_drop`] except `f` is `async`.
    783     ///
    784     /// # Errors
    785     ///
    786     /// Read [`Self::priv_drop`].
    787     ///
    788     /// # Examples
    789     ///
    790     /// ```no_run
    791     /// # use core::net::{Ipv6Addr, SocketAddrV6};
    792     /// # use priv_sep::UserInfo;
    793     /// # use tokio::net::TcpListener;
    794     /// let listener_fut = UserInfo::priv_drop_async("nobody", async || {
    795     ///     TcpListener::bind(SocketAddrV6::new(Ipv6Addr::LOCALHOST, 443, 0, 0)).await
    796     /// });
    797     /// ```
    798     #[inline]
    799     pub async fn priv_drop_async<U: Into<Vec<u8>>, T, E, F: AsyncFnOnce() -> Result<T, E>>(
    800         name: U,
    801         f: F,
    802     ) -> Result<T, PrivDropErr<E>> {
    803         match Self::new(name) {
    804             Ok(opt) => match opt {
    805                 None => Err(PrivDropErr::NoPasswdEntry),
    806                 Some(info) => {
    807                     if info.is_root() {
    808                         Err(PrivDropErr::RootEntry)
    809                     } else {
    810                         f().await
    811                             .map_err(PrivDropErr::Other)
    812                             .and_then(|res| info.setresid().map_err(PrivDropErr::Io).map(|()| res))
    813                     }
    814                 }
    815             },
    816             Err(err) => Err(PrivDropErr::from(err)),
    817         }
    818     }
    819     /// Same as [`Self::priv_drop`] except [`chroot_then_chdir`] is called before or after invoking `f` based on
    820     /// `chroot_after_f`.
    821     ///
    822     /// # Errors
    823     ///
    824     /// Errors iff [`Self::priv_drop`] or [`chroot_then_chdir`] do.
    825     ///
    826     /// # Examples
    827     ///
    828     /// ```no_run
    829     /// # use core::net::{Ipv6Addr, SocketAddrV6};
    830     /// # use priv_sep::{PrivDropErr, UserInfo};
    831     /// # use std::{io::Error, net::TcpListener};
    832     /// let listener = UserInfo::chroot_then_priv_drop("nobody", "./", false, || {
    833     ///     TcpListener::bind(SocketAddrV6::new(Ipv6Addr::LOCALHOST, 443, 0, 0))
    834     /// })?;
    835     /// # Ok::<_, PrivDropErr<Error>>(())
    836     /// ```
    837     #[inline]
    838     pub fn chroot_then_priv_drop<
    839         U: Into<Vec<u8>>,
    840         P: AsRef<Path>,
    841         T,
    842         E,
    843         F: FnOnce() -> Result<T, E>,
    844     >(
    845         name: U,
    846         path: P,
    847         chroot_after_f: bool,
    848         f: F,
    849     ) -> Result<T, PrivDropErr<E>> {
    850         Self::new(name).map_err(PrivDropErr::from).and_then(|opt| {
    851             opt.ok_or_else(|| PrivDropErr::NoPasswdEntry)
    852                 .and_then(|info| {
    853                     if info.is_root() {
    854                         Err(PrivDropErr::RootEntry)
    855                     } else if chroot_after_f {
    856                         f().map_err(PrivDropErr::Other).and_then(|res| {
    857                             chroot_then_chdir(path)
    858                                 .map_err(PrivDropErr::from)
    859                                 .map(|()| res)
    860                         })
    861                     } else {
    862                         chroot_then_chdir(path)
    863                             .map_err(PrivDropErr::from)
    864                             .and_then(|()| f().map_err(PrivDropErr::Other))
    865                     }
    866                     .and_then(|res| info.setresid().map_err(PrivDropErr::Io).map(|()| res))
    867                 })
    868         })
    869     }
    870     /// Same as [`Self::chroot_then_priv_drop`] except `f` is `async`.
    871     ///
    872     /// # Errors
    873     ///
    874     /// Read [`Self::chroot_then_priv_drop`].
    875     ///
    876     /// # Examples
    877     ///
    878     /// ```no_run
    879     /// # use core::net::{Ipv6Addr, SocketAddrV6};
    880     /// # use priv_sep::UserInfo;
    881     /// # use tokio::net::TcpListener;
    882     /// let listener_fut = UserInfo::chroot_then_priv_drop_async("nobody", "./", false, async || {
    883     ///     TcpListener::bind(SocketAddrV6::new(Ipv6Addr::LOCALHOST, 443, 0, 0)).await
    884     /// });
    885     /// ```
    886     #[inline]
    887     pub async fn chroot_then_priv_drop_async<
    888         U: Into<Vec<u8>>,
    889         P: AsRef<Path>,
    890         T,
    891         E,
    892         F: AsyncFnOnce() -> Result<T, E>,
    893     >(
    894         name: U,
    895         path: P,
    896         chroot_after_f: bool,
    897         f: F,
    898     ) -> Result<T, PrivDropErr<E>> {
    899         match Self::new(name) {
    900             Ok(opt) => match opt {
    901                 None => Err(PrivDropErr::NoPasswdEntry),
    902                 Some(info) => if info.is_root() {
    903                     Err(PrivDropErr::RootEntry)
    904                 } else if chroot_after_f {
    905                     f().await.map_err(PrivDropErr::Other).and_then(|res| {
    906                         chroot_then_chdir(path)
    907                             .map_err(PrivDropErr::from)
    908                             .map(|()| res)
    909                     })
    910                 } else {
    911                     match chroot_then_chdir(path) {
    912                         Ok(()) => f().await.map_err(PrivDropErr::Other),
    913                         Err(err) => Err(PrivDropErr::from(err)),
    914                     }
    915                 }
    916                 .and_then(|res| info.setresid().map_err(PrivDropErr::Io).map(|()| res)),
    917             },
    918             Err(err) => Err(PrivDropErr::from(err)),
    919         }
    920     }
    921 }
    922 impl PartialEq<&Self> for UserInfo {
    923     #[inline]
    924     fn eq(&self, other: &&Self) -> bool {
    925         *self == **other
    926     }
    927 }
    928 impl PartialEq<UserInfo> for &UserInfo {
    929     #[inline]
    930     fn eq(&self, other: &UserInfo) -> bool {
    931         **self == *other
    932     }
    933 }
    934 #[cfg(test)]
    935 mod tests {
    936     use super::{Gid, NulOrIoErr, PrivDropErr, SetresidErr, Uid, UserInfo};
    937     #[cfg(target_os = "openbsd")]
    938     use super::{Permissions, Promise, Promises};
    939     use core::net::{Ipv6Addr, SocketAddrV6};
    940     use std::{fs, io::Error, net::TcpListener};
    941     use tokio as _;
    942     const README: &str = "README.md";
    943     #[test]
    944     fn test_getuid() {
    945         _ = Uid::getuid();
    946     }
    947     #[test]
    948     fn test_geteuid() {
    949         _ = Uid::geteuid();
    950     }
    951     #[test]
    952     fn test_getgid() {
    953         _ = Gid::getgid();
    954     }
    955     #[test]
    956     fn test_getegid() {
    957         _ = Gid::getegid();
    958     }
    959     #[test]
    960     fn test_setresuid() -> Result<(), Error> {
    961         Uid::geteuid().setresuid()
    962     }
    963     #[test]
    964     fn test_setresgid() -> Result<(), Error> {
    965         Gid::getegid().setresgid()
    966     }
    967     #[test]
    968     fn test_user_info_new() -> Result<(), NulOrIoErr> {
    969         if let Some(user) = UserInfo::new("root")? {
    970             assert!(user.is_root());
    971         }
    972         Ok(())
    973     }
    974     #[test]
    975     fn test_user_info_with_buffer() -> Result<(), NulOrIoErr> {
    976         if let Some(user) = UserInfo::with_buffer("root", [0; 512].as_mut_slice())? {
    977             assert!(user.is_root());
    978         }
    979         Ok(())
    980     }
    981     #[test]
    982     fn test_user_info_setresid() -> Result<(), Error> {
    983         UserInfo {
    984             uid: Uid::geteuid(),
    985             gid: Gid::getegid(),
    986         }
    987         .setresid()
    988     }
    989     #[test]
    990     fn test_user_info_setresid_if_exists() -> Result<(), SetresidErr> {
    991         UserInfo {
    992             uid: Uid::geteuid(),
    993             gid: Gid::getegid(),
    994         }
    995         .setresid_if_valid()
    996     }
    997     #[test]
    998     fn test_user_info_setresid_if_exists_failure() {
    999         assert!(
   1000             UserInfo {
   1001                 uid: Uid::geteuid(),
   1002                 gid: Gid(u32::MAX),
   1003             }
   1004             .setresid_if_valid()
   1005             .map_or_else(|e| matches!(e, SetresidErr::GidMismatch), |_| false)
   1006         );
   1007     }
   1008     #[test]
   1009     #[ignore]
   1010     fn test_priv_drop() -> Result<(), PrivDropErr<Error>> {
   1011         if Uid::geteuid().is_root() {
   1012             UserInfo::priv_drop("zack", || {
   1013                 TcpListener::bind(SocketAddrV6::new(Ipv6Addr::LOCALHOST, 443, 0, 0))
   1014             })
   1015             .map(|_| {
   1016                 assert!(
   1017                     TcpListener::bind(SocketAddrV6::new(Ipv6Addr::LOCALHOST, 80, 0, 0)).is_err()
   1018                 );
   1019             })
   1020         } else {
   1021             assert!(
   1022                 UserInfo::priv_drop("root", || Ok::<_, Error>(()))
   1023                     .map_or_else(|e| matches!(e, PrivDropErr::RootEntry), |_| false)
   1024             );
   1025             Ok(())
   1026         }
   1027     }
   1028     #[test]
   1029     #[ignore]
   1030     fn test_chroot_priv_drop() -> Result<(), PrivDropErr<Error>> {
   1031         if Uid::geteuid().is_root() {
   1032             UserInfo::chroot_then_priv_drop("zack", "./", false, || {
   1033                 TcpListener::bind(SocketAddrV6::new(Ipv6Addr::LOCALHOST, 443, 0, 0))
   1034             })
   1035             .and_then(|_| {
   1036                 fs::exists(README).map_err(PrivDropErr::Io).map(|exists| {
   1037                     assert!(exists);
   1038                     assert!(
   1039                         TcpListener::bind(SocketAddrV6::new(Ipv6Addr::LOCALHOST, 80, 0, 0))
   1040                             .is_err()
   1041                     );
   1042                 })
   1043             })
   1044         } else {
   1045             Ok(())
   1046         }
   1047     }
   1048     #[cfg(target_os = "openbsd")]
   1049     #[test]
   1050     #[ignore]
   1051     fn test_pledge_unveil() {
   1052         const FILE_EXISTS: &str = "/home/zack/foo.txt";
   1053         _ = fs::metadata(FILE_EXISTS)
   1054             .expect(format!("{FILE_EXISTS} does not exist, so unit testing cannot occur").as_str());
   1055         const FILE_NOT_EXISTS: &str = "/home/zack/aadkjfasj3s23";
   1056         drop(fs::metadata(FILE_NOT_EXISTS).expect_err(
   1057             format!("{FILE_NOT_EXISTS} exists, so unit testing cannot occur").as_str(),
   1058         ));
   1059         const DIR_NOT_EXISTS: &str = "/home/zack/aadkjfasj3s23/";
   1060         drop(
   1061             fs::metadata(DIR_NOT_EXISTS).expect_err(
   1062                 format!("{DIR_NOT_EXISTS} exists, so unit testing cannot occur").as_str(),
   1063             ),
   1064         );
   1065         // This tests that a NULL `promise` does nothing.
   1066         assert!(Promises::pledge_none().is_ok());
   1067         print!("");
   1068         assert!(Promises::ALL.pledge().is_ok());
   1069         // This tests that duplicates are ignored as well as the implementation of PartialEq.
   1070         let mut initial_promises = Promises::new([
   1071             Promise::Stdio,
   1072             Promise::Unveil,
   1073             Promise::Rpath,
   1074             Promise::Stdio,
   1075         ]);
   1076         assert!(initial_promises.len() == 3);
   1077         assert!(
   1078             initial_promises == Promises::new([Promise::Rpath, Promise::Stdio, Promise::Unveil])
   1079         );
   1080         // Test retain.
   1081         assert!({
   1082             let mut vals = Promises::new([
   1083                 Promise::Audio,
   1084                 Promise::Bpf,
   1085                 Promise::Chown,
   1086                 Promise::Cpath,
   1087                 Promise::Error,
   1088                 Promise::Exec,
   1089             ]);
   1090             vals.retain([Promise::Error, Promise::Chown]);
   1091             vals.len() == 2 && vals.contains(Promise::Chown) && vals.contains(Promise::Error)
   1092         });
   1093         assert!(initial_promises.pledge().is_ok());
   1094         // This tests unveil with no permissions.
   1095         assert!(Permissions::NONE.unveil(FILE_EXISTS).is_ok());
   1096         assert!(fs::metadata(FILE_EXISTS).is_err());
   1097         // This tests unveil with read permissions,
   1098         // and one can unveil more permissions (unlike pledge which can only remove promises).
   1099         assert!(Permissions::READ.unveil(FILE_EXISTS).is_ok());
   1100         assert!(fs::metadata(FILE_EXISTS).is_ok());
   1101         // This tests that calls to unveil on missing files don't error.
   1102         assert!(Permissions::NONE.unveil(FILE_NOT_EXISTS).is_ok());
   1103         // This tests that calls to unveil on missing directories error.
   1104         assert!(Permissions::NONE.unveil(DIR_NOT_EXISTS).is_err());
   1105         // This tests that unveil can no longer be called.
   1106         assert!(Permissions::unveil_no_more().is_ok());
   1107         assert!(Permissions::NONE.unveil(FILE_EXISTS).is_err());
   1108         assert!(fs::metadata(FILE_EXISTS).is_ok());
   1109         // The below tests that Promises can only be removed and not added.
   1110         initial_promises.remove_promises([Promise::Unveil]);
   1111         assert_eq!(initial_promises.len(), 2);
   1112         initial_promises.remove(Promise::Rpath);
   1113         assert_eq!(initial_promises.len(), 1);
   1114         initial_promises.remove(Promise::Rpath);
   1115         assert_eq!(initial_promises.len(), 1);
   1116         assert!(initial_promises.pledge().is_ok());
   1117         print!("");
   1118         assert!(Promises::new([Promise::Rpath]).pledge().is_err());
   1119         // If the below is uncommented, the program should crash since the above
   1120         // call to pledge no longer allows access to the file system.
   1121         // drop(fs::metadata(FILE_EXISTS));
   1122     }
   1123     #[cfg(target_os = "openbsd")]
   1124     #[test]
   1125     #[ignore]
   1126     fn test_pledge_priv_drop() -> Result<(), PrivDropErr<Error>> {
   1127         if Uid::geteuid().is_root() {
   1128             Promises::new_priv_drop(
   1129                 "zack",
   1130                 [Promise::Inet, Promise::Rpath, Promise::Unveil],
   1131                 false,
   1132                 || TcpListener::bind(SocketAddrV6::new(Ipv6Addr::LOCALHOST, 443, 0, 0)),
   1133             )
   1134             .and_then(|(_, mut promises)| {
   1135                 Permissions::READ
   1136                     .unveil(README)
   1137                     .map_err(PrivDropErr::from)
   1138                     .and_then(|()| {
   1139                         fs::exists(README)
   1140                             .map_err(PrivDropErr::Io)
   1141                             .and_then(|exists| {
   1142                                 Permissions::NONE
   1143                                     .unveil(README)
   1144                                     .map_err(PrivDropErr::from)
   1145                                     .and_then(|()| {
   1146                                         promises
   1147                                             .remove_promises_then_pledge([
   1148                                                 Promise::Rpath,
   1149                                                 Promise::Unveil,
   1150                                             ])
   1151                                             .map_err(PrivDropErr::Io)
   1152                                             .map(|()| {
   1153                                                 assert!(exists);
   1154                                                 assert!(
   1155                                                     TcpListener::bind(SocketAddrV6::new(
   1156                                                         Ipv6Addr::LOCALHOST,
   1157                                                         80,
   1158                                                         0,
   1159                                                         0
   1160                                                     ))
   1161                                                     .is_err()
   1162                                                 );
   1163                                             })
   1164                                     })
   1165                             })
   1166                     })
   1167             })
   1168         } else {
   1169             Ok(())
   1170         }
   1171     }
   1172     #[cfg(target_os = "openbsd")]
   1173     #[test]
   1174     #[ignore]
   1175     fn test_pledge_chroot_priv_drop() -> Result<(), PrivDropErr<Error>> {
   1176         if Uid::geteuid().is_root() {
   1177             Promises::new_chroot_then_priv_drop(
   1178                 "zack",
   1179                 "./",
   1180                 [Promise::Inet, Promise::Rpath, Promise::Unveil],
   1181                 false,
   1182                 || TcpListener::bind(SocketAddrV6::new(Ipv6Addr::LOCALHOST, 443, 0, 0)),
   1183             )
   1184             .and_then(|(_, mut promises)| {
   1185                 Permissions::READ
   1186                     .unveil(README)
   1187                     .map_err(PrivDropErr::from)
   1188                     .and_then(|()| {
   1189                         fs::exists(README)
   1190                             .map_err(PrivDropErr::Io)
   1191                             .and_then(|exists| {
   1192                                 Permissions::NONE
   1193                                     .unveil(README)
   1194                                     .map_err(PrivDropErr::from)
   1195                                     .and_then(|()| {
   1196                                         promises
   1197                                             .remove_promises_then_pledge([
   1198                                                 Promise::Rpath,
   1199                                                 Promise::Unveil,
   1200                                             ])
   1201                                             .map_err(PrivDropErr::Io)
   1202                                             .map(|()| {
   1203                                                 assert!(exists);
   1204                                                 assert!(
   1205                                                     TcpListener::bind(SocketAddrV6::new(
   1206                                                         Ipv6Addr::LOCALHOST,
   1207                                                         80,
   1208                                                         0,
   1209                                                         0
   1210                                                     ))
   1211                                                     .is_err()
   1212                                                 );
   1213                                             })
   1214                                     })
   1215                             })
   1216                     })
   1217             })
   1218         } else {
   1219             Ok(())
   1220         }
   1221     }
   1222 }