Skip to content

Commit 5259536

Browse files
authored
route sudo -e to pipeline (#1176)
2 parents 2f78c34 + 2d6a966 commit 5259536

File tree

6 files changed

+100
-6
lines changed

6 files changed

+100
-6
lines changed

Cargo.toml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,11 @@ 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+
# NOTE: this is currently work in progress and *NOT* implemented
61+
sudoedit = []
62+
5863
[profile.release]
5964
strip = "symbols"
6065
lto = true

src/common/context.rs

Lines changed: 38 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,8 @@ use std::io;
22

33
use crate::common::{HARDENED_ENUM_VALUE_0, HARDENED_ENUM_VALUE_1, HARDENED_ENUM_VALUE_2};
44
use crate::exec::{RunOptions, Umask};
5-
use crate::sudo::{SudoListOptions, SudoRunOptions, SudoValidateOptions};
5+
#[cfg_attr(not(feature = "sudoedit"), allow(unused_imports))]
6+
use crate::sudo::{SudoEditOptions, SudoListOptions, SudoRunOptions, SudoValidateOptions};
67
use crate::sudoers::Sudoers;
78
use crate::system::{Group, Hostname, Process, User};
89

@@ -100,6 +101,42 @@ impl Context {
100101
})
101102
}
102103

104+
#[cfg(feature = "sudoedit")]
105+
pub fn from_edit_opts(sudo_options: SudoEditOptions) -> Result<Context, Error> {
106+
let hostname = Hostname::resolve();
107+
let current_user = CurrentUser::resolve()?;
108+
109+
let (target_user, target_group) =
110+
resolve_target_user_and_group(&sudo_options.user, &sudo_options.group, &current_user)?;
111+
112+
// TODO: the more Rust way of doing things would be to create an alternative for sudoedit instead;
113+
// but a stringly typed interface feels the most decent thing to do (if we can pull it off)
114+
// since "sudoedit" really is like a builtin command to sudo.
115+
let command = CommandAndArguments {
116+
command: std::path::PathBuf::from("sudoedit"),
117+
arguments: sudo_options.positional_args,
118+
..Default::default()
119+
};
120+
121+
Ok(Context {
122+
hostname,
123+
command,
124+
current_user,
125+
target_user,
126+
target_group,
127+
use_session_records: !sudo_options.reset_timestamp,
128+
launch: Default::default(),
129+
chdir: sudo_options.chdir,
130+
stdin: sudo_options.stdin,
131+
bell: sudo_options.bell,
132+
prompt: sudo_options.prompt,
133+
non_interactive: sudo_options.non_interactive,
134+
process: Process::new(),
135+
use_pty: true,
136+
noexec: false,
137+
umask: Umask::Preserve,
138+
})
139+
}
103140
pub fn from_validate_opts(sudo_options: SudoValidateOptions) -> Result<Context, Error> {
104141
let hostname = Hostname::resolve();
105142
let current_user = CurrentUser::resolve()?;

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: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ use cli::SudoAction;
1515
use std::path::PathBuf;
1616

1717
mod cli;
18-
pub(crate) use cli::{SudoListOptions, SudoRunOptions, SudoValidateOptions};
18+
pub(crate) use cli::{SudoEditOptions, SudoListOptions, SudoRunOptions, SudoValidateOptions};
1919

2020
pub(crate) mod diagnostic;
2121
mod env;
@@ -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: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
use std::process::exit;
2+
3+
use super::super::cli::SudoEditOptions;
4+
use crate::common::{Context, Error};
5+
use crate::exec::ExitReason;
6+
use crate::sudoers::Authorization;
7+
8+
pub fn run_edit(edit_opts: SudoEditOptions) -> Result<(), Error> {
9+
let policy = super::read_sudoers()?;
10+
11+
let mut context = Context::from_edit_opts(edit_opts)?;
12+
13+
let policy = super::judge(policy, &context)?;
14+
15+
let Authorization::Allowed(auth, controls) = policy.authorization() else {
16+
return Err(Error::Authorization(context.current_user.name.to_string()));
17+
};
18+
19+
super::apply_policy_to_context(&mut context, &controls)?;
20+
let mut pam_context = super::auth_and_update_record_file(&context, auth)?;
21+
22+
let pid = context.process.pid;
23+
24+
// run command and return corresponding exit code
25+
let command_exit_reason = {
26+
super::log_command_execution(&context);
27+
28+
eprintln_ignore_io_error!(
29+
"this would launch sudoedit as requested, to edit the files: {:?}",
30+
context.command.arguments.as_slice()
31+
);
32+
33+
Ok::<_, std::io::Error>(ExitReason::Code(42))
34+
};
35+
36+
pam_context.close_session();
37+
38+
match command_exit_reason? {
39+
ExitReason::Code(code) => exit(code),
40+
ExitReason::Signal(signal) => {
41+
crate::system::kill(pid, signal)?;
42+
}
43+
}
44+
45+
Ok(())
46+
}

0 commit comments

Comments
 (0)