Skip to content

Commit e20a1f1

Browse files
committed
scaffolding for sudoedit (note: this does not compile and is not supposed to)
1 parent b57be94 commit e20a1f1

File tree

5 files changed

+94
-4
lines changed

5 files changed

+94
-4
lines changed

Cargo.toml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,10 @@ dev = []
5555
# compilation using "cargo --all-features", which should not be used on sudo-rs
5656
do-not-use-all-features = []
5757

58+
# whether to enable 'sudoedit' (this will become a default feature, or
59+
# perhaps we can just remove the feature flag altogether)
60+
sudoedit = []
61+
5862
[profile.release]
5963
strip = "symbols"
6064
lto = true

src/sudo/cli/mod.rs

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,7 @@ pub mod help;
99
#[cfg(test)]
1010
mod tests;
1111

12-
// remove dead_code when sudoedit has been implemented
13-
#[allow(dead_code)]
12+
#[cfg_attr(not(feature = "sudoedit"), allow(dead_code))]
1413
pub enum SudoAction {
1514
Edit(SudoEditOptions),
1615
Help(SudoHelpOptions),
@@ -218,7 +217,7 @@ impl TryFrom<SudoOptions> for SudoValidateOptions {
218217
}
219218

220219
// sudo -e [-ABkNnS] [-r role] [-t type] [-C num] [-D directory] [-g group] [-h host] [-p prompt] [-R directory] [-T timeout] [-u user] file ...
221-
#[allow(dead_code)]
220+
#[cfg_attr(not(feature = "sudoedit"), allow(dead_code))]
222221
pub struct SudoEditOptions {
223222
// -B
224223
pub bell: bool,

src/sudo/mod.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -108,6 +108,9 @@ fn sudo_process() -> Result<(), Error> {
108108
}
109109
}
110110
SudoAction::List(options) => pipeline::run_list(options),
111+
#[cfg(feature = "sudoedit")]
112+
SudoAction::Edit(options) => pipeline::run_edit(options),
113+
#[cfg(not(feature = "sudoedit"))]
111114
SudoAction::Edit(_) => {
112115
eprintln_ignore_io_error!("error: `--edit` flag has not yet been implemented");
113116
std::process::exit(1);

src/sudo/pipeline.rs

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,9 +19,13 @@ use crate::system::timestamp::{RecordScope, SessionRecordFile, TouchResult};
1919
use crate::system::{escape_os_str_lossy, Process};
2020

2121
mod list;
22-
2322
pub(super) use list::run_list;
2423

24+
#[cfg(feature = "sudoedit")]
25+
mod edit;
26+
#[cfg(feature = "sudoedit")]
27+
pub(super) use edit::run_edit;
28+
2529
fn read_sudoers() -> Result<Sudoers, Error> {
2630
let sudoers_path = &super::candidate_sudoers_file();
2731

src/sudo/pipeline/edit.rs

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
use std::process::exit;
2+
3+
use super::super::cli::SudoRunOptions;
4+
use crate::common::{Context, Error};
5+
use crate::exec::ExitReason;
6+
use crate::sudo::env::environment;
7+
use crate::sudo::pam::pre_exec;
8+
use crate::sudoers::Authorization;
9+
10+
pub fn run_edit(mut cmd_opts: SudoRunOptions) -> Result<(), Error> {
11+
let mut policy = super::read_sudoers()?;
12+
13+
let user_requested_env_vars = std::mem::take(&mut cmd_opts.env_var_list);
14+
15+
let mut context = Context::from_run_opts(cmd_opts, &mut policy)?;
16+
17+
let policy = super::judge(policy, &context)?;
18+
19+
let Authorization::Allowed(auth, controls) = policy.authorization() else {
20+
return Err(Error::Authorization(context.current_user.name.to_string()));
21+
};
22+
23+
super::apply_policy_to_context(&mut context, &controls)?;
24+
let mut pam_context = super::auth_and_update_record_file(&context, auth)?;
25+
26+
// build environment
27+
let additional_env = pre_exec(&mut pam_context, &context.target_user.name)?;
28+
29+
let current_env = environment::system_environment();
30+
let (checked_vars, trusted_vars) = if controls.trust_environment {
31+
(vec![], user_requested_env_vars)
32+
} else {
33+
(user_requested_env_vars, vec![])
34+
};
35+
36+
let mut target_env = environment::get_target_environment(
37+
current_env,
38+
additional_env,
39+
checked_vars,
40+
&context,
41+
&controls,
42+
)?;
43+
44+
environment::dangerous_extend(&mut target_env, trusted_vars);
45+
46+
let pid = context.process.pid;
47+
48+
// prepare switch of apparmor profile
49+
#[cfg(feature = "apparmor")]
50+
if let Some(profile) = controls.apparmor_profile {
51+
crate::apparmor::set_profile_for_next_exec(&profile)
52+
.map_err(|err| Error::AppArmor(profile, err))?;
53+
}
54+
55+
// run command and return corresponding exit code
56+
let command_exit_reason = if context.command.resolved {
57+
super::log_command_execution(&context);
58+
59+
crate::exec::run_command(
60+
context
61+
.try_as_run_options()
62+
.map_err(|io_error| Error::Io(Some(context.command.command.clone()), io_error))?,
63+
target_env,
64+
)
65+
.map_err(|io_error| Error::Io(Some(context.command.command), io_error))
66+
} else {
67+
Err(Error::CommandNotFound(context.command.command))
68+
};
69+
70+
pam_context.close_session();
71+
72+
match command_exit_reason? {
73+
ExitReason::Code(code) => exit(code),
74+
ExitReason::Signal(signal) => {
75+
crate::system::kill(pid, signal)?;
76+
}
77+
}
78+
79+
Ok(())
80+
}

0 commit comments

Comments
 (0)