summaryrefslogtreecommitdiffhomepage
path: root/test/test-runner/src/util.rs
blob: 84cc048b49c67aa5ae9b71580052fd9ab85cc635 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
/// Drop guard that executes the provided callback function when dropped.
pub struct OnDrop<F = Box<dyn FnOnce() + Send>>
where
    F: FnOnce() + Send,
{
    callback: Option<F>,
}

impl<F: FnOnce() + Send> Drop for OnDrop<F> {
    fn drop(&mut self) {
        if let Some(callback) = self.callback.take() {
            callback();
        }
    }
}

impl<F: FnOnce() + Send> OnDrop<F> {
    pub fn new(callback: F) -> Self {
        Self {
            callback: Some(callback),
        }
    }
}

#[derive(thiserror::Error, Debug)]
#[error(transparent)]
pub struct Error {
    inner: InnerError,
}

#[cfg(unix)]
#[derive(thiserror::Error, Debug)]
enum InnerError {
    #[error("Failed to get the specified user")]
    GetUser(#[source] nix::Error),
    #[error("The specified user was not found")]
    MissingUser,
    #[error("Failed to set uid")]
    SetUid(#[source] nix::Error),
    #[error("Failed to set gid")]
    SetGid(#[source] nix::Error),
}

#[cfg(target_os = "windows")]
#[derive(thiserror::Error, Debug)]
enum InnerError {}

impl From<InnerError> for Error {
    fn from(inner: InnerError) -> Self {
        Self { inner }
    }
}

#[cfg(target_os = "windows")]
pub fn as_unprivileged_user<T>(unpriv_user: &str, func: impl FnOnce() -> T) -> Result<T, Error> {
    // NOTE: no-op
    let _ = unpriv_user;
    Ok(func())
}

#[cfg(unix)]
pub fn as_unprivileged_user<T>(unpriv_user: &str, func: impl FnOnce() -> T) -> Result<T, Error> {
    let original_uid = nix::unistd::getuid();
    let original_gid = nix::unistd::getgid();

    let user = nix::unistd::User::from_name(unpriv_user)
        .map_err(InnerError::GetUser)?
        .ok_or(InnerError::MissingUser)?;
    let uid = user.uid;
    let gid = user.gid;

    nix::unistd::setegid(gid).map_err(InnerError::SetGid)?;
    let restore_gid = OnDrop::new(|| {
        if let Err(error) = nix::unistd::setegid(original_gid) {
            log::error!("Failed to restore gid: {error}");
        }
    });

    nix::unistd::seteuid(uid).map_err(InnerError::SetUid)?;
    let restore_uid = OnDrop::new(|| {
        if let Err(error) = nix::unistd::seteuid(original_uid) {
            log::error!("Failed to restore uid: {error}");
        }
    });

    let result = Ok(func());

    drop(restore_uid);
    drop(restore_gid);

    result
}