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}