1#![doc = include_str!("../README.md")]
2
3use common::singleton::Singleton;
4use nix::{
5 errno::Errno,
6 unistd::{ResGid, ResUid, getresgid, getresuid, setresgid, setresuid},
7};
8use std::{error, fmt, sync::LazyLock};
9
10pub static USER: LazyLock<ResUid> = LazyLock::new(|| getresuid().expect("Failed to get UID!"));
12
13pub static GROUP: LazyLock<ResGid> = LazyLock::new(|| getresgid().expect("Failed to get GID!"));
15
16pub static SETUID: LazyLock<bool> = LazyLock::new(|| USER.effective != USER.real);
19
20#[derive(Debug)]
22pub struct Error {
23 uid: ResUid,
25
26 errno: Errno,
28}
29impl fmt::Display for Error {
30 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
31 let current = getresgid();
32 if let Ok(uid) = current {
33 write!(
34 f,
35 "Failed to change UID from: ({}, {}, {}) to ({}, {}, {})",
36 uid.real,
37 uid.effective,
38 uid.saved,
39 self.uid.real,
40 self.uid.effective,
41 self.uid.saved
42 )
43 } else {
44 write!(
45 f,
46 "Failed to change UID to ({}, {}, {})",
47 self.uid.real, self.uid.effective, self.uid.saved
48 )
49 }
50 }
51}
52impl error::Error for Error {
53 fn source(&self) -> Option<&(dyn error::Error + 'static)> {
54 Some(&self.errno as &dyn error::Error)
55 }
56}
57
58#[derive(Debug, Copy, Clone)]
60pub enum Mode {
61 Real,
64
65 Effective,
68
69 Existing,
72
73 Original,
77}
78
79pub fn set(mode: Mode) -> Result<(ResUid, ResGid), Errno> {
103 if !*SETUID {
104 return Ok((*USER, *GROUP));
105 }
106
107 let uid = getresuid()?;
108 let gid = getresgid()?;
109
110 match mode {
111 Mode::Real => {
112 setresuid(USER.real, USER.real, USER.effective)?;
113 setresgid(GROUP.real, GROUP.real, GROUP.effective)?;
114 }
115 Mode::Effective => {
116 setresuid(USER.effective, USER.effective, USER.real)?;
117 setresgid(GROUP.effective, GROUP.effective, GROUP.real)?;
118 }
119 Mode::Original => revert()?,
120 Mode::Existing => {}
121 }
122
123 Ok((uid, gid))
124}
125
126pub fn current() -> Result<Mode, Errno> {
132 let uid = getresuid()?.real;
133 if uid == USER.real {
134 Ok(Mode::Real)
135 } else if uid == USER.effective {
136 Ok(Mode::Effective)
137 } else {
138 Err(Errno::EINVAL)
139 }
140}
141
142pub fn revert() -> Result<(), Errno> {
146 setresuid(USER.real, USER.effective, USER.saved)?;
147 setresgid(GROUP.real, GROUP.effective, GROUP.saved)
148}
149
150pub fn drop(mode: Mode) -> Result<(), Errno> {
162 match mode {
163 Mode::Real => {
164 setresuid(USER.real, USER.real, USER.real)?;
165 setresgid(GROUP.real, GROUP.real, GROUP.real)
166 }
167 Mode::Effective => {
168 setresuid(USER.effective, USER.effective, USER.effective)?;
169 setresgid(GROUP.effective, GROUP.effective, GROUP.effective)
170 }
171 Mode::Original => revert(),
172 Mode::Existing => {
173 let (user, group) = (getresuid()?, getresgid()?);
174 setresuid(user.real, user.real, user.real)?;
175 setresgid(group.real, group.real, group.real)
176 }
177 }
178}
179
180pub fn restore((uid, gid): (ResUid, ResGid)) -> Result<(), Errno> {
189 if !*SETUID {
190 return Ok(());
191 }
192
193 setresuid(uid.real, uid.effective, uid.saved)?;
194 setresgid(gid.real, gid.effective, gid.saved)
195}
196
197pub fn obtain_lock() -> Option<Singleton> {
198 if *crate::SETUID {
199 Singleton::new()
200 } else {
201 None
202 }
203}
204
205#[macro_export]
210macro_rules! run_as {
211 ($mode:path, $ret:ty, $body:block) => {{
212 {
213 let lock = user::obtain_lock();
214 match user::set($mode) {
215 Ok(__saved) => {
216 let __result = (|| -> $ret { $body })();
217 user::restore(__saved).map(|e| __result)
218 }
219 Err(e) => Err(e),
220 }
221 }
222 }};
223
224 ($mode:path, $body:block) => {{
225 {
226 let lock = user::obtain_lock();
227 match user::set($mode) {
228 Ok(__saved) => {
229 let __result = (|| $body)();
230 user::restore(__saved).map(|e| __result)
231 }
232 Err(e) => Err(e),
233 }
234 }
235 }};
236
237 ($mode:path, $expr:expr) => {{
238 {
239 let lock = user::obtain_lock();
240 match user::set($mode) {
241 Ok(__saved) => {
242 let __result = $expr;
243 user::restore(__saved).map(|e| __result)
244 }
245 Err(e) => Err(e),
246 }
247 }
248 }};
249}
250
251#[macro_export]
253macro_rules! as_real {
254 ($ret:ty, $body:block) => {{ user::run_as!(user::Mode::Real, $ret, $body) }};
255 ($body:block) => {{ user::run_as!(user::Mode::Real, $body) }};
256 ($expr:expr) => {{ user::run_as!(user::Mode::Real, { $expr }) }};
257}
258
259#[macro_export]
261macro_rules! as_effective {
262 ($ret:ty, $body:block) => {{ user::run_as!(user::Mode::Effective, $ret, $body) }};
263 ($body:block) => {{ user::run_as!(user::Mode::Effective, $body) }};
264 ($expr:expr) => {{ user::run_as!(user::Mode::Effective, { $expr }) }};
265}