sudo_rs/sudo/
mod.rs

1#![deny(unsafe_code)]
2
3use crate::common::resolve::CurrentUser;
4use crate::common::Error;
5use crate::log::dev_info;
6use crate::system::interface::UserId;
7use crate::system::timestamp::RecordScope;
8use crate::system::User;
9use crate::system::{time::Duration, timestamp::SessionRecordFile, Process};
10#[cfg(test)]
11pub(crate) use cli::SudoAction;
12#[cfg(not(test))]
13use cli::SudoAction;
14use std::path::PathBuf;
15
16mod cli;
17pub(crate) use cli::{SudoEditOptions, SudoListOptions, SudoRunOptions, SudoValidateOptions};
18#[cfg(feature = "sudoedit")]
19mod edit;
20
21pub(crate) mod diagnostic;
22mod env;
23mod pam;
24mod pipeline;
25
26#[cfg_attr(not(feature = "dev"), allow(dead_code))]
27fn unstable_warning() {
28    let check_var = std::env::var("SUDO_RS_IS_UNSTABLE").unwrap_or_else(|_| "".to_string());
29
30    if check_var != "I accept that my system may break unexpectedly" {
31        eprintln_ignore_io_error!(
32            "WARNING!
33Sudo-rs is compiled with development logs on, which means it is less secure and could potentially
34break your system. We recommend that you do not run this on any production environment.
35To turn off this warning and use sudo-rs you need to set the environment variable
36SUDO_RS_IS_UNSTABLE to the value `I accept that my system may break unexpectedly`."
37        );
38
39        std::process::exit(1);
40    }
41}
42
43const VERSION: &str = if let Some(version_override) = std::option_env!("SUDO_RS_VERSION") {
44    version_override
45} else {
46    std::env!("CARGO_PKG_VERSION")
47};
48
49pub(crate) fn candidate_sudoers_file() -> PathBuf {
50    let mut path = if cfg!(target_os = "freebsd") {
51        option_env!("LOCALBASE").unwrap_or("/usr/local").into()
52    } else {
53        PathBuf::from("/")
54    };
55    path.push("etc/sudoers-rs");
56    if !path.exists() {
57        path.set_file_name("sudoers");
58    };
59
60    dev_info!("Running with {} file", path.display());
61    path
62}
63
64fn sudo_process() -> Result<(), Error> {
65    crate::log::SudoLogger::new("sudo: ").into_global_logger();
66
67    dev_info!("development logs are enabled");
68
69    self_check()?;
70
71    let usage_msg: &str;
72    let long_help: fn() -> String;
73    if cli::is_sudoedit(std::env::args().next()) {
74        usage_msg = cli::help_edit::USAGE_MSG;
75        long_help = cli::help_edit::long_help_message;
76    } else {
77        usage_msg = cli::help::USAGE_MSG;
78        long_help = cli::help::long_help_message;
79    }
80
81    // parse cli options
82    match SudoAction::from_env() {
83        Ok(action) => match action {
84            SudoAction::Help(_) => {
85                eprintln_ignore_io_error!("{}", long_help());
86                std::process::exit(0);
87            }
88            SudoAction::Version(_) => {
89                eprintln_ignore_io_error!("sudo-rs {VERSION}");
90                std::process::exit(0);
91            }
92            SudoAction::RemoveTimestamp(_) => {
93                let user = CurrentUser::resolve()?;
94                let mut record_file =
95                    SessionRecordFile::open_for_user(&user, Duration::seconds(0))?;
96                record_file.reset()?;
97                Ok(())
98            }
99            SudoAction::ResetTimestamp(_) => {
100                if let Some(scope) = RecordScope::for_process(&Process::new()) {
101                    let user = CurrentUser::resolve()?;
102                    let mut record_file =
103                        SessionRecordFile::open_for_user(&user, Duration::seconds(0))?;
104                    record_file.disable(scope, None)?;
105                }
106                Ok(())
107            }
108            SudoAction::Validate(options) => pipeline::run_validate(options),
109            SudoAction::Run(options) => {
110                // special case for when no command is given
111                if options.positional_args.is_empty() && !options.shell && !options.login {
112                    eprintln_ignore_io_error!("{}", usage_msg);
113                    std::process::exit(1);
114                } else {
115                    #[cfg(feature = "dev")]
116                    unstable_warning();
117
118                    pipeline::run(options)
119                }
120            }
121            SudoAction::List(options) => pipeline::run_list(options),
122            #[cfg(feature = "sudoedit")]
123            SudoAction::Edit(options) => pipeline::run_edit(options),
124            #[cfg(not(feature = "sudoedit"))]
125            SudoAction::Edit(_) => {
126                eprintln_ignore_io_error!("error: `--edit` flag has not yet been implemented");
127                std::process::exit(1);
128            }
129        },
130        Err(e) => {
131            eprintln_ignore_io_error!("{e}\n{}", usage_msg);
132            std::process::exit(1);
133        }
134    }
135}
136
137fn self_check() -> Result<(), Error> {
138    let euid = User::effective_uid();
139    if euid == UserId::ROOT {
140        Ok(())
141    } else {
142        Err(Error::SelfCheck)
143    }
144}
145
146pub fn main() {
147    match sudo_process() {
148        Ok(()) => (),
149        Err(error) => {
150            if !error.is_silent() {
151                diagnostic::diagnostic!("{error}");
152            }
153            std::process::exit(1);
154        }
155    }
156}