Skip to main content

signstar_config/
utils.rs

1//! Utilities for signstar-config.
2use std::path::PathBuf;
3
4use nix::unistd::{User, geteuid};
5use which::which;
6
7use crate::SystemUserId;
8
9/// An error that may occur when using signstar-config utils.
10#[derive(Debug, thiserror::Error)]
11pub enum Error {
12    /// An executable that is supposed to be called, is not found.
13    #[error("Unable to to find executable \"{command}\"")]
14    ExecutableNotFound {
15        /// The executable that could not be found.
16        command: String,
17        /// The source error.
18        source: which::Error,
19    },
20
21    /// There is no data about a system user.
22    #[error("Data for system user {user} is missing")]
23    SystemUserData {
24        /// The user identifier for which data is missing.
25        user: NameOrUid,
26    },
27
28    /// Unable to lookup system user data (by name or from process EUID).
29    #[error(
30        "Unable to lookup data for system user {}:\n{source}",
31        match user {
32            NameOrUid::Name(name) => format!("user {name}"),
33            NameOrUid::Uid(uid) => format!("uid {uid}"),
34        }
35    )]
36    SystemUserLookup {
37        /// The user identifier for which data could not be looked up.
38        user: NameOrUid,
39        /// The source error.
40        source: nix::errno::Errno,
41    },
42
43    /// The calling user does not match the targeted system user.
44    #[error(
45        "The targeted system user {target_user} is not the currently calling system user {current_user}."
46    )]
47    SystemUserMismatch {
48        /// The system user that is the target of the operation.
49        target_user: String,
50        /// The currently calling system user.
51        current_user: String,
52    },
53
54    /// The current user is an unprivileged user, but should be root.
55    #[error("The command requires running as root, but running as \"{user}\"")]
56    SystemUserNotRoot {
57        /// The system user that is used instead of `root`.
58        user: String,
59    },
60}
61
62/// A name or uid of a system user on a host
63#[derive(Debug, strum::Display)]
64pub enum NameOrUid {
65    /// The name of the system user.
66    Name(SystemUserId),
67    /// The ID of the system user.
68    Uid(nix::unistd::Uid),
69}
70
71/// Returns the path to a `command`.
72///
73/// Searches for an executable in `$PATH` of the current environment and returns the first one
74/// found.
75///
76/// # Errors
77///
78/// Returns an error if no executable matches the provided `command`.
79pub(crate) fn get_command(command: &str) -> Result<PathBuf, Error> {
80    which(command).map_err(|source| Error::ExecutableNotFound {
81        command: command.to_string(),
82        source,
83    })
84}
85
86/// Fails if not running as root.
87///
88/// Evaluates the effective user ID.
89///
90/// # Errors
91///
92/// Returns an error if the effective user ID is not that of root.
93pub(crate) fn fail_if_not_root(user: &User) -> Result<(), Error> {
94    if !user.uid.is_root() {
95        return Err(Error::SystemUserNotRoot {
96            user: user.name.clone(),
97        });
98    }
99    Ok(())
100}
101
102/// Returns the [`User`] associated with the current process.
103///
104/// Retrieves user data of the system based on the effective user ID of the current process.
105///
106/// # Errors
107///
108/// Returns an error if
109/// - no user data can be derived from the current process
110/// - no user data can be found on the system, associated with the ID of the user of the current
111///   process.
112pub(crate) fn get_current_system_user() -> Result<User, Error> {
113    let euid = geteuid();
114    let Some(user) = User::from_uid(euid).map_err(|source| Error::SystemUserLookup {
115        user: NameOrUid::Uid(euid),
116        source,
117    })?
118    else {
119        return Err(Error::SystemUserData {
120            user: NameOrUid::Uid(euid),
121        });
122    };
123    Ok(user)
124}