priv_sep.rs (7996B)
1 #[cfg(all(feature = "priv_sep", target_os = "openbsd"))] 2 pub use priv_sep::UnveilErr; 3 #[cfg(all(feature = "priv_sep", target_os = "openbsd"))] 4 use priv_sep::{self, Permission, Permissions, Promise, Promises}; 5 #[cfg(all(feature = "priv_sep", target_os = "openbsd"))] 6 use std::env; 7 #[cfg(not(all(feature = "priv_sep", target_os = "openbsd")))] 8 use std::fs; 9 use std::{ 10 io::{Error, ErrorKind}, 11 path::Path, 12 }; 13 /// Used instead of `()` for the parameter 14 /// in the `pledge` functions. This allows 15 /// one to avoid having to disable certain lints. 16 #[cfg(not(all(feature = "priv_sep", target_os = "openbsd")))] 17 #[derive(Clone, Copy)] 18 pub struct Zst; 19 /// Calls `pledge` with only the sys calls necessary for a minimal application 20 /// to run. Specifically, the `Promise`s `Cpath`, `Dns`, `Inet`, `Rpath`, `Stdio`, `Unveil`, and `Wpath` 21 /// are passed. 22 #[cfg(all(feature = "priv_sep", target_os = "openbsd"))] 23 pub fn pledge_init() -> Result<Promises, Error> { 24 let promises = Promises::new([ 25 Promise::Cpath, 26 Promise::Dns, 27 Promise::Inet, 28 Promise::Rpath, 29 Promise::Stdio, 30 Promise::Unveil, 31 Promise::Wpath, 32 ]); 33 match promises.pledge() { 34 Ok(()) => Ok(promises), 35 Err(e) => Err(e), 36 } 37 } 38 /// No-op that always returns `Ok`. 39 #[expect(clippy::unnecessary_wraps, reason = "need to align with OpenBSD code")] 40 #[cfg(not(all(feature = "priv_sep", target_os = "openbsd")))] 41 pub const fn pledge_init() -> Result<Zst, !> { 42 Ok(Zst) 43 } 44 /// Removes `Cpath` and `Wpath` `Promise`s. 45 /// 46 /// This should only be called when `stdout` is written to 47 /// instead of an RPZ file. 48 #[cfg(all(feature = "priv_sep", target_os = "openbsd"))] 49 pub fn pledge_away_create_write(promises: &mut Promises) -> Result<(), Error> { 50 promises.remove_promises_then_pledge([Promise::Cpath, Promise::Wpath]) 51 } 52 /// No-op that always returns `Ok`. 53 #[expect(clippy::unnecessary_wraps, reason = "need to align with OpenBSD code")] 54 #[cfg(not(all(feature = "priv_sep", target_os = "openbsd")))] 55 pub fn pledge_away_create_write(_: &mut Zst) -> Result<(), !> { 56 Ok(()) 57 } 58 /// Removes `Promise::Unveil`. 59 #[cfg(all(feature = "priv_sep", target_os = "openbsd"))] 60 pub fn pledge_away_unveil(promises: &mut Promises) -> Result<(), Error> { 61 promises.remove_then_pledge(Promise::Unveil) 62 } 63 /// No-op that always returns `Ok`. 64 #[expect(clippy::unnecessary_wraps, reason = "need to align with OpenBSD code")] 65 #[cfg(not(all(feature = "priv_sep", target_os = "openbsd")))] 66 pub fn pledge_away_unveil(_: &mut Zst) -> Result<(), !> { 67 Ok(()) 68 } 69 /// Removes `Promise::Dns` and `Promise::Inet`. 70 #[cfg(all(feature = "priv_sep", target_os = "openbsd"))] 71 pub fn pledge_away_net(promises: &mut Promises) -> Result<(), Error> { 72 promises.remove_promises_then_pledge([Promise::Dns, Promise::Inet]) 73 } 74 /// No-op that always returns `Ok`. 75 #[expect(clippy::unnecessary_wraps, reason = "need to align with OpenBSD code")] 76 #[cfg(not(all(feature = "priv_sep", target_os = "openbsd")))] 77 pub fn pledge_away_net(_: &mut Zst) -> Result<(), !> { 78 Ok(()) 79 } 80 /// Removes all `Promise`s except `Stdio`. 81 #[cfg(all(feature = "priv_sep", target_os = "openbsd"))] 82 pub fn pledge_away_all_but_stdio(promises: &mut Promises) -> Result<(), Error> { 83 promises.retain_then_pledge([Promise::Stdio]) 84 } 85 /// No-op that always returns `Ok`. 86 #[expect(clippy::unnecessary_wraps, reason = "need to align with OpenBSD code")] 87 #[cfg(not(all(feature = "priv_sep", target_os = "openbsd")))] 88 pub fn pledge_away_all_but_stdio(_: &mut Zst) -> Result<(), !> { 89 Ok(()) 90 } 91 /// Calls `unveil`_on `path` with no `Permissions`. 92 #[cfg(all(feature = "priv_sep", target_os = "openbsd"))] 93 pub fn unveil_none<P: AsRef<Path>>(path: P) -> Result<(), UnveilErr> { 94 Permissions::NONE.unveil(path) 95 } 96 /// No-op that always returns `Ok`. 97 #[cfg(not(all(feature = "priv_sep", target_os = "openbsd")))] 98 #[expect(clippy::unnecessary_wraps, reason = "need to align with OpenBSD code")] 99 pub fn unveil_none<P: AsRef<Path>>(_: P) -> Result<(), !> { 100 Ok(()) 101 } 102 /// Calls `unveil_none` on `/`. 103 #[cfg(all(feature = "priv_sep", target_os = "openbsd"))] 104 pub fn veil_all() -> Result<(), UnveilErr> { 105 unveil_none("/") 106 } 107 /// No-op that always returns `Ok`. 108 #[cfg(not(all(feature = "priv_sep", target_os = "openbsd")))] 109 #[expect(clippy::unnecessary_wraps, reason = "need to align with OpenBSD code")] 110 pub const fn veil_all() -> Result<(), !> { 111 Ok(()) 112 } 113 /// Calls `unveil`_on `path` with `Permissions::CREATE`. 114 #[cfg(all(feature = "priv_sep", target_os = "openbsd"))] 115 pub fn unveil_create<P: AsRef<Path>>(path: P) -> Result<(), UnveilErr> { 116 Permissions::CREATE.unveil(path) 117 } 118 /// No-op that always returns `Ok`. 119 #[cfg(not(all(feature = "priv_sep", target_os = "openbsd")))] 120 #[expect(clippy::unnecessary_wraps, reason = "need to align with OpenBSD code")] 121 pub fn unveil_create<P: AsRef<Path>>(_: P) -> Result<(), !> { 122 Ok(()) 123 } 124 /// Calls `unveil`_on `path` with `Permissions::READ` and returns 125 /// `true` iff `path` exists. 126 #[expect(clippy::wildcard_enum_match_arm)] 127 #[cfg(all(feature = "priv_sep", target_os = "openbsd"))] 128 pub fn unveil_read_dir<P: AsRef<Path>>(path: P) -> Result<bool, UnveilErr> { 129 Permissions::READ.unveil(path).map_or_else( 130 |err| match err { 131 UnveilErr::Io(ref err2) => match err2.kind() { 132 ErrorKind::NotFound => Ok(false), 133 _ => Err(err), 134 }, 135 UnveilErr::Nul(_) => Err(err), 136 }, 137 |()| Ok(true), 138 ) 139 } 140 /// Returns `true` iff `path` exists 141 #[expect( 142 clippy::wildcard_enum_match_arm, 143 reason = "too many branches to write out manually" 144 )] 145 #[cfg(not(all(feature = "priv_sep", target_os = "openbsd")))] 146 pub fn unveil_read_dir<P: AsRef<Path>>(path: P) -> Result<bool, Error> { 147 fs::metadata(path).map_or_else( 148 |err| match err.kind() { 149 ErrorKind::NotFound => Ok(false), 150 _ => Err(err), 151 }, 152 |dir| Ok(dir.is_dir()), 153 ) 154 } 155 /// Calls `unveil`_on `path` with `Permissions::READ`. 156 #[cfg(all(feature = "priv_sep", target_os = "openbsd"))] 157 pub fn unveil_read_file<P: AsRef<Path>>(path: P) -> Result<(), UnveilErr> { 158 Permissions::READ.unveil(path) 159 } 160 /// No-op that always returns `Ok`. 161 #[cfg(not(all(feature = "priv_sep", target_os = "openbsd")))] 162 #[expect(clippy::unnecessary_wraps, reason = "need to align with OpenBSD code")] 163 pub fn unveil_read_file<P: AsRef<Path>>(_: P) -> Result<(), !> { 164 Ok(()) 165 } 166 /// Calls `unveil` on all files necessary for HTTP(S) with `Permissions::READ` 167 /// in addition to setting the `SSL_CERT_FILE` variable to `/etc/ssl/cert.pem`. 168 /// We rely on `SSL_CERT_FILE` as that allows one to `unveil(2)` only `/etc/ssl/cert.pem` 169 /// instead of `/etc/ssl/`. 170 #[expect(unsafe_code)] 171 #[cfg(all(feature = "priv_sep", target_os = "openbsd"))] 172 pub fn unveil_https() -> Result<(), UnveilErr> { 173 /// The path to the root certificate store. 174 const CERTS: &str = "/etc/ssl/cert.pem"; 175 unveil_read_file(CERTS).map(|()| { 176 // SAFETY: 177 // `unveil_https` is only called in `super::main` 178 // in a single-threaded context; thus this is OK. 179 unsafe { env::set_var("SSL_CERT_FILE", CERTS) } 180 }) 181 } 182 /// No-op that always returns `Ok`. 183 #[cfg(not(all(feature = "priv_sep", target_os = "openbsd")))] 184 #[expect(clippy::unnecessary_wraps, reason = "need to align with OpenBSD code")] 185 pub const fn unveil_https() -> Result<(), !> { 186 Ok(()) 187 } 188 /// Calls `unveil`_on `path` with create, read, and write `Permissions`. 189 #[cfg(all(feature = "priv_sep", target_os = "openbsd"))] 190 pub fn unveil_create_read_write<P: AsRef<Path>>(path: P) -> Result<(), UnveilErr> { 191 let mut perms = Permissions::ALL; 192 perms.disable(Permission::Execute); 193 perms.unveil(path) 194 } 195 /// No-op that always returns `Ok`. 196 #[cfg(not(all(feature = "priv_sep", target_os = "openbsd")))] 197 #[expect(clippy::unnecessary_wraps, reason = "need to align with OpenBSD code")] 198 pub fn unveil_create_read_write<P: AsRef<Path>>(_: P) -> Result<(), !> { 199 Ok(()) 200 }