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 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 if options.positional_args.is_empty() && !options.shell && !options.login {
114 eprintln_ignore_io_error!("{}", usage_msg);
115 std::process::exit(1);
116 } else {
117 #[cfg(feature = "dev")]
118 unstable_warning();
119
120 pipeline::run(options)
121 }
122 }
123 SudoAction::List(options) => pipeline::run_list(options),
124 SudoAction::Edit(options) => pipeline::run_edit(options),
125 },
126 Err(e) => {
127 eprintln_ignore_io_error!("{e}\n{}", usage_msg);
128 std::process::exit(1);
129 }
130 }
131}
132
133fn self_check() -> Result<(), Error> {
134 #[cfg(target_os = "linux")]
135 if crate::system::audit::no_new_privs_enabled()? {
136 return Err(Error::SelfCheckNoNewPrivs);
137 }
138
139 if User::effective_uid() != UserId::ROOT {
140 return Err(Error::SelfCheckSetuid);
141 }
142
143 Ok(())
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}