Skip to main content

sudo_rs/sudo/
mod.rs

1#![deny(unsafe_code)]
2
3use crate::common::Error;
4use crate::common::resolve::CurrentUser;
5use crate::log::dev_info;
6use crate::system::User;
7use crate::system::interface::UserId;
8use crate::system::timestamp::RecordScope;
9use crate::system::{Process, timestamp::SessionRecordFile};
10#[cfg(test)]
11pub(crate) use cli::SudoAction;
12#[cfg(not(test))]
13use cli::SudoAction;
14use std::{path::PathBuf, time::Duration};
15
16mod cli;
17pub(crate) use cli::{SudoEditOptions, SudoListOptions, SudoRunOptions, SudoValidateOptions};
18mod edit;
19
20pub(crate) mod diagnostic;
21mod env;
22pub(crate) use env::environment::PATH_DEFAULT;
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    #[cfg(feature = "gettext")]
70    crate::gettext::textdomain(c"sudo-rs");
71
72    self_check()?;
73
74    let usage_msg: &str;
75    let long_help: fn() -> String;
76    if cli::is_sudoedit(std::env::args_os().next()) {
77        usage_msg = cli::help_edit::usage_msg();
78        long_help = cli::help_edit::long_help_message;
79    } else {
80        usage_msg = cli::help::usage_msg();
81        long_help = cli::help::long_help_message;
82    }
83
84    // parse cli options
85    match SudoAction::from_env() {
86        Ok(action) => match action {
87            SudoAction::Help(_) => {
88                println_ignore_io_error!("{}", long_help());
89                std::process::exit(0);
90            }
91            SudoAction::Version(_) => {
92                println_ignore_io_error!("sudo-rs {VERSION}");
93                std::process::exit(0);
94            }
95            SudoAction::RemoveTimestamp(_) => {
96                let user = CurrentUser::resolve()?;
97                let mut record_file = SessionRecordFile::open_for_user(&user, Duration::default())?;
98                record_file.reset()?;
99                Ok(())
100            }
101            SudoAction::ResetTimestamp(_) => {
102                if let Some(scope) = RecordScope::for_process(&Process::new()) {
103                    let user = CurrentUser::resolve()?;
104                    let mut record_file =
105                        SessionRecordFile::open_for_user(&user, Duration::default())?;
106                    record_file.disable(scope)?;
107                }
108                Ok(())
109            }
110            SudoAction::Validate(options) => pipeline::run_validate(options),
111            SudoAction::Run(options) => {
112                #[cfg(feature = "dev")]
113                unstable_warning();
114
115                // SudoAction::from_env() should already ensure this
116                assert!(!options.positional_args.is_empty() || options.shell || options.login);
117
118                pipeline::run(options)
119            }
120            SudoAction::List(options) => pipeline::run_list(options),
121            SudoAction::Edit(options) => pipeline::run_edit(options),
122        },
123        Err(e) => {
124            eprintln_ignore_io_error!("{e}\n{}", usage_msg);
125            std::process::exit(1);
126        }
127    }
128}
129
130fn self_check() -> Result<(), Error> {
131    if User::effective_uid() != UserId::ROOT {
132        #[cfg(target_os = "linux")]
133        if crate::system::audit::no_new_privs_enabled()? {
134            return Err(Error::SelfCheckNoNewPrivs);
135        }
136
137        return Err(Error::SelfCheckSetuid);
138    }
139
140    Ok(())
141}
142
143pub fn main() {
144    match sudo_process() {
145        Ok(()) => (),
146        Err(error) => {
147            if !error.is_silent() {
148                diagnostic::diagnostic!("{error}");
149            }
150            std::process::exit(1);
151        }
152    }
153}