Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 7 additions & 6 deletions src/common/context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ use crate::exec::RunOptions;
use crate::sudo::{SudoEditOptions, SudoListOptions, SudoRunOptions, SudoValidateOptions};
use crate::sudoers::Sudoers;
use crate::sudoers::{DirChange, Restrictions};
use crate::system::{audit::sudo_call, Group, Hostname, Process, User};
use crate::system::{audit::sudo_call, Group, Hostname, User};

use super::{
command::CommandAndArguments,
Expand All @@ -24,13 +24,13 @@ pub struct Context {
pub askpass: bool,
pub stdin: bool,
pub bell: bool,
pub background: bool,
pub prompt: Option<String>,
pub non_interactive: bool,
pub use_session_records: bool,
// system
pub hostname: Hostname,
pub current_user: CurrentUser,
pub process: Process,
// sudoedit
pub files_to_edit: Vec<Option<SudoPath>>,
}
Expand Down Expand Up @@ -96,9 +96,9 @@ impl Context {
askpass: sudo_options.askpass,
stdin: sudo_options.stdin,
bell: sudo_options.bell,
background: sudo_options.background,
prompt,
non_interactive: sudo_options.non_interactive,
process: Process::new(),
files_to_edit: vec![],
})
}
Expand Down Expand Up @@ -164,9 +164,9 @@ impl Context {
askpass: sudo_options.askpass,
stdin: sudo_options.stdin,
bell: sudo_options.bell,
background: false,
prompt: sudo_options.prompt,
non_interactive: sudo_options.non_interactive,
process: Process::new(),
files_to_edit,
})
}
Expand All @@ -188,9 +188,9 @@ impl Context {
askpass: sudo_options.askpass,
stdin: sudo_options.stdin,
bell: sudo_options.bell,
background: false,
prompt: sudo_options.prompt,
non_interactive: sudo_options.non_interactive,
process: Process::new(),
files_to_edit: vec![],
})
}
Expand Down Expand Up @@ -235,9 +235,9 @@ impl Context {
askpass: sudo_options.askpass,
stdin: sudo_options.stdin,
bell: sudo_options.bell,
background: false,
prompt: sudo_options.prompt,
non_interactive: sudo_options.non_interactive,
process: Process::new(),
files_to_edit: vec![],
})
}
Expand Down Expand Up @@ -280,6 +280,7 @@ impl Context {
group: &self.target_group,
umask: controls.umask,

background: self.background,
use_pty: controls.use_pty,
noexec: controls.noexec,
})
Expand Down
22 changes: 18 additions & 4 deletions src/exec/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ use std::{
os::unix::ffi::OsStrExt,
os::unix::process::CommandExt,
path::{Path, PathBuf},
process::Command,
process::{self, Command},
time::Duration,
};

Expand All @@ -24,13 +24,12 @@ use crate::{
exec::no_pty::exec_no_pty,
log::{dev_info, dev_warn, user_error},
system::{
_exit,
ForkResult, Group, User, _exit, fork,
interface::ProcessId,
kill, killpg, mark_fds_as_cloexec, set_target_user,
kill, killpg, mark_fds_as_cloexec, set_target_user, setpgid,
signal::{consts::*, signal_name, SignalNumber, SignalSet},
term::UserTerm,
wait::{Wait, WaitError, WaitOptions},
Group, User,
},
};

Expand Down Expand Up @@ -71,6 +70,7 @@ pub struct RunOptions<'a> {
pub group: &'a Group,
pub umask: Umask,

pub background: bool,
pub use_pty: bool,
pub noexec: bool,
}
Expand All @@ -83,6 +83,19 @@ pub fn run_command(
options: RunOptions<'_>,
env: impl IntoIterator<Item = (impl AsRef<OsStr>, impl AsRef<OsStr>)>,
) -> io::Result<ExitReason> {
if options.background {
// SAFETY: There should be no other threads at this point.
match unsafe { fork() }? {
ForkResult::Parent(_) => process::exit(0),
ForkResult::Child => {
// Child continues in an orphaned process group.
// Reads from the terminal fail with EIO.
// Writes succeed unless tostop is set on the terminal.
setpgid(ProcessId::new(0), ProcessId::new(0))?;
}
}
}

// FIXME: should we pipe the stdio streams?
let qualified_path = options.command;
let mut command = Command::new(qualified_path);
Expand Down Expand Up @@ -185,6 +198,7 @@ pub fn run_command(
command,
user_tty,
options.user,
options.background,
),
Err(err) => {
dev_info!("Could not open user's terminal, not allocating a pty: {err}");
Expand Down
29 changes: 28 additions & 1 deletion src/exec/use_pty/parent.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,12 @@
use std::collections::VecDeque;
use std::ffi::c_int;
use std::io;
use std::os::fd::{FromRawFd, OwnedFd};
use std::process::{Command, Stdio};

use libc::{close, O_CLOEXEC};

use crate::cutils::cerr;
use crate::exec::event::{EventHandle, EventRegistry, PollEvent, Process, StopReason};
use crate::exec::use_pty::monitor::exec_monitor;
use crate::exec::use_pty::SIGCONT_FG;
Expand Down Expand Up @@ -31,6 +35,7 @@ pub(in crate::exec) fn exec_pty(
mut command: Command,
user_tty: UserTerm,
pty_owner: &User,
background: bool,
) -> io::Result<ExitReason> {
// Allocate a pseudoterminal.
let pty = get_pty(pty_owner)?;
Expand All @@ -55,7 +60,7 @@ pub(in crate::exec) fn exec_pty(
// `policy_init_session`.
// FIXME (ogsudo): initializes ttyblock sigset here by calling `init_ttyblock`

// Fetch the parent process group so we can signals to it.
// Fetch the parent process group so we can send signals to it.
let parent_pgrp = getpgrp();

// Set all the IO streams for the command to the follower side of the pty.
Expand All @@ -81,6 +86,10 @@ pub(in crate::exec) fn exec_pty(
ParentEvent::Pty,
);

if background {
tty_pipe.disable_input(&mut registry);
}

let user_tty = tty_pipe.left_mut();

// Check if we are the foreground process
Expand Down Expand Up @@ -118,6 +127,24 @@ pub(in crate::exec) fn exec_pty(
// change the terminal mode.
exec_bg = true;
}
} else if background {
// Running in background (sudo -b), no access to terminal input.
// In non-pty mode, the command runs in an orphaned process
// group and reads from the controlling terminal fail with EIO.
// We cannot do the same while running in a pty but if we set
// stdin to a half-closed pipe, reads from it will get EOF.
exec_bg = true;

let mut pipes = [-1, -1];
// SAFETY: A valid pointer to a mutable array of 2 fds is passed in.
unsafe {
cerr(libc::pipe2(pipes.as_mut_ptr(), O_CLOEXEC))?;
}
// SAFETY: pipe2 created two owned pipe fds.
unsafe {
command.stdin(OwnedFd::from_raw_fd(pipes[0]));
close(pipes[1]);
}
}

if !io::stdout().is_terminal_for_pgrp(parent_pgrp) {
Expand Down
7 changes: 2 additions & 5 deletions src/su/context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ use std::{
use crate::common::{error::Error, resolve::CurrentUser};
use crate::exec::{RunOptions, Umask};
use crate::log::user_warn;
use crate::system::{Group, Process, User};
use crate::system::{Group, User};
use crate::{common::resolve::is_valid_executable, system::interface::UserId};

type Environment = HashMap<OsString, OsString>;
Expand All @@ -32,7 +32,6 @@ pub(crate) struct SuContext {
pub(crate) user: User,
pub(crate) requesting_user: CurrentUser,
group: Group,
pub(crate) process: Process,
}

/// check that a shell is not restricted / exists in /etc/shells
Expand All @@ -50,8 +49,6 @@ fn is_restricted(shell: &Path) -> bool {

impl SuContext {
pub(crate) fn from_env(options: SuRunOptions) -> Result<SuContext, Error> {
let process = crate::system::Process::new();

// resolve environment, reset if this is a login
let mut environment = if options.login {
Environment::default()
Expand Down Expand Up @@ -197,7 +194,6 @@ impl SuContext {
user,
requesting_user,
group,
process,
})
}
}
Expand All @@ -214,6 +210,7 @@ impl SuContext {
group: &self.group,
umask: Umask::Preserve,

background: false,
use_pty: true,
noexec: false,
}
Expand Down
5 changes: 2 additions & 3 deletions src/su/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ use crate::exec::ExitReason;
use crate::log::user_warn;
use crate::pam::{PamContext, PamError, PamErrorType};
use crate::system::term::current_tty_name;
use crate::system::Process;

use std::{env, process};

Expand Down Expand Up @@ -105,8 +106,6 @@ fn run(options: SuRunOptions) -> Result<(), Error> {
let mut environment = context.environment.clone();
environment.extend(pam.env()?);

let pid = context.process.pid;

// run command and return corresponding exit code
let command_exit_reason = crate::exec::run_command(context.as_run_options(), environment);

Expand All @@ -115,7 +114,7 @@ fn run(options: SuRunOptions) -> Result<(), Error> {
match command_exit_reason? {
ExitReason::Code(code) => process::exit(code),
ExitReason::Signal(signal) => {
crate::system::kill(pid, signal)?;
crate::system::kill(Process::process_id(), signal)?;
}
}

Expand Down
10 changes: 10 additions & 0 deletions src/sudo/cli/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -385,6 +385,8 @@ pub struct SudoRunOptions {
pub askpass: bool,
// -B
pub bell: bool,
// -b
pub background: bool,
// -E
/* ignored, part of env_var_list */
// -k
Expand Down Expand Up @@ -416,6 +418,7 @@ impl TryFrom<SudoOptions> for SudoRunOptions {
fn try_from(mut opts: SudoOptions) -> Result<Self, Self::Error> {
let askpass = mem::take(&mut opts.askpass);
let bell = mem::take(&mut opts.bell);
let background = mem::take(&mut opts.background);
let reset_timestamp = mem::take(&mut opts.reset_timestamp);
let non_interactive = mem::take(&mut opts.non_interactive);
let stdin = mem::take(&mut opts.stdin);
Expand Down Expand Up @@ -466,6 +469,7 @@ impl TryFrom<SudoOptions> for SudoRunOptions {
Ok(Self {
askpass,
bell,
background,
reset_timestamp,
non_interactive,
stdin,
Expand All @@ -487,6 +491,8 @@ struct SudoOptions {
askpass: bool,
// -B
bell: bool,
// -b
background: bool,
// -D
chdir: Option<SudoPath>,
// -g
Expand Down Expand Up @@ -698,6 +704,9 @@ impl SudoOptions {
"-B" | "--bell" => {
options.bell = true;
}
"-b" | "--background" => {
options.background = true;
}
"-E" | "--preserve-env" => {
user_warn!(
"preserving the entire environment is not supported, '{flag}' is ignored",
Expand Down Expand Up @@ -878,6 +887,7 @@ fn reject_all(context: &str, opts: SudoOptions) -> Result<(), String> {
check_options!(
askpass,
bell,
background,
chdir,
edit,
group,
Expand Down
2 changes: 1 addition & 1 deletion src/sudo/cli/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -330,7 +330,7 @@ fn help() {
let cmd = SudoAction::try_parse_from(["sudo", "-h"]).unwrap();
assert!(cmd.is_help());

let cmd = SudoAction::try_parse_from(["sudo", "-bh"]);
let cmd = SudoAction::try_parse_from(["sudo", "-zh"]);
assert!(cmd.is_err());

let cmd = SudoAction::try_parse_from(["sudo", "--help"]).unwrap();
Expand Down
4 changes: 2 additions & 2 deletions src/sudo/env/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ use crate::sudo::{
env::environment::{get_target_environment, Environment},
};
use crate::system::interface::{GroupId, UserId};
use crate::system::{Group, Hostname, Process, User};
use crate::system::{Group, Hostname, User};
use std::collections::{HashMap, HashSet};

const TESTS: &str = "
Expand Down Expand Up @@ -130,9 +130,9 @@ fn create_test_context(sudo_options: SudoRunOptions) -> Context {
stdin: sudo_options.stdin,
prompt: sudo_options.prompt,
non_interactive: sudo_options.non_interactive,
process: Process::new(),
use_session_records: false,
bell: false,
background: false,
files_to_edit: vec![],
}
}
Expand Down
4 changes: 1 addition & 3 deletions src/sudo/pipeline.rs
Original file line number Diff line number Diff line change
Expand Up @@ -105,8 +105,6 @@ pub fn run(mut cmd_opts: SudoRunOptions) -> Result<(), Error> {

environment::dangerous_extend(&mut target_env, trusted_vars);

let pid = context.process.pid;

// prepare switch of apparmor profile
#[cfg(feature = "apparmor")]
if let Some(profile) = &controls.apparmor_profile {
Expand All @@ -128,7 +126,7 @@ pub fn run(mut cmd_opts: SudoRunOptions) -> Result<(), Error> {
match command_exit_reason? {
ExitReason::Code(code) => exit(code),
ExitReason::Signal(signal) => {
crate::system::kill(pid, signal)?;
crate::system::kill(Process::process_id(), signal)?;
}
}

Expand Down
6 changes: 2 additions & 4 deletions src/sudo/pipeline/edit.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ use crate::common::{Context, Error};
use crate::exec::ExitReason;
use crate::log::{user_error, user_info};
use crate::sudoers::Authorization;
use crate::system::audit;
use crate::system::{audit, Process};

pub fn run_edit(edit_opts: SudoEditOptions) -> Result<(), Error> {
let policy = super::read_sudoers()?;
Expand All @@ -20,8 +20,6 @@ pub fn run_edit(edit_opts: SudoEditOptions) -> Result<(), Error> {

let mut pam_context = super::auth_and_update_record_file(&context, auth)?;

let pid = context.process.pid;

let mut opened_files = Vec::with_capacity(context.files_to_edit.len());
for (path, arg) in context.files_to_edit.iter().zip(&context.command.arguments) {
if let Some(path) = path {
Expand Down Expand Up @@ -67,7 +65,7 @@ pub fn run_edit(edit_opts: SudoEditOptions) -> Result<(), Error> {
match command_exit_reason? {
ExitReason::Code(code) => exit(code),
ExitReason::Signal(signal) => {
crate::system::kill(pid, signal)?;
crate::system::kill(Process::process_id(), signal)?;
}
}

Expand Down
Loading
Loading