Execute user shell commands with /bin/sh

Execute user provided command invocations $CMD such as `editor_cmd` with
`/bin/sh` as `/bin/sh -c "$CMD"

Previously, user commands were split by whitespace which must trigger
erroneous behavior if quotes are involved.
async
Manos Pitsidianakis 2020-05-28 16:27:02 +03:00
parent bfff0e4feb
commit bd404e6937
Signed by: Manos Pitsidianakis
GPG Key ID: 73627C2F690DF710
10 changed files with 52 additions and 68 deletions

View File

@ -870,11 +870,9 @@ impl ImapType {
let server_password = if !s.extra.contains_key("server_password_command") { let server_password = if !s.extra.contains_key("server_password_command") {
get_conf_val!(s["server_password"])?.to_string() get_conf_val!(s["server_password"])?.to_string()
} else { } else {
let invocation = get_conf_val!(s["server_password_command"])? let invocation = get_conf_val!(s["server_password_command"])?;
.split_whitespace() let output = std::process::Command::new("sh")
.collect::<Vec<&str>>(); .args(&["-c", invocation])
let output = std::process::Command::new(invocation[0])
.args(&invocation[1..])
.stdin(std::process::Stdio::piped()) .stdin(std::process::Stdio::piped())
.stdout(std::process::Stdio::piped()) .stdout(std::process::Stdio::piped())
.stderr(std::process::Stdio::piped()) .stderr(std::process::Stdio::piped())

View File

@ -913,11 +913,13 @@ impl Component for Composer {
context.input_kill(); context.input_kill();
} }
let parts = split_command!(editor); let editor_cmd = format!("{} {}", editor, f.path().display());
let (cmd, args) = (parts[0], &parts[1..]); log(
match Command::new(cmd) format!("Executing: sh -c \"{}\"", editor_cmd.replace("\"", "\\\"")),
.args(args) DEBUG,
.arg(&f.path()) );
match Command::new("sh")
.args(&["-c", &editor_cmd])
.stdin(Stdio::inherit()) .stdin(Stdio::inherit())
.stdout(Stdio::inherit()) .stdout(Stdio::inherit())
.spawn() .spawn()
@ -963,8 +965,7 @@ impl Component for Composer {
UIEvent::Action(ref a) => { UIEvent::Action(ref a) => {
match a { match a {
Action::Compose(ComposeAction::AddAttachmentPipe(ref cmd)) => { Action::Compose(ComposeAction::AddAttachmentPipe(ref cmd)) => {
let parts = split_command!(cmd); if cmd.is_empty() {
if parts.is_empty() {
context.replies.push_back(UIEvent::Notification( context.replies.push_back(UIEvent::Notification(
None, None,
format!("pipe cmd value is invalid: {}", cmd), format!("pipe cmd value is invalid: {}", cmd),
@ -972,10 +973,9 @@ impl Component for Composer {
)); ));
return false; return false;
} }
let (cmd, args) = (parts[0], &parts[1..]);
let f = create_temp_file(&[], None, None, true); let f = create_temp_file(&[], None, None, true);
match std::process::Command::new(cmd) match std::process::Command::new("sh")
.args(args) .args(&["-c", cmd])
.stdin(std::process::Stdio::null()) .stdin(std::process::Stdio::null())
.stdout(std::process::Stdio::from(f.file())) .stdout(std::process::Stdio::from(f.file()))
.spawn() .spawn()
@ -1183,10 +1183,8 @@ pub fn send_draft(
use std::io::Write; use std::io::Write;
use std::process::{Command, Stdio}; use std::process::{Command, Stdio};
let format_flowed = *mailbox_acc_settings!(context[account_cursor].composing.format_flowed); let format_flowed = *mailbox_acc_settings!(context[account_cursor].composing.format_flowed);
let parts = split_command!(mailbox_acc_settings!( let cmd = mailbox_acc_settings!(context[account_cursor].composing.mailer_cmd);
context[account_cursor].composing.mailer_cmd if cmd.is_empty() {
));
if parts.is_empty() {
context.replies.push_back(UIEvent::Notification( context.replies.push_back(UIEvent::Notification(
None, None,
String::from("mailer_cmd configuration value is empty"), String::from("mailer_cmd configuration value is empty"),
@ -1195,9 +1193,8 @@ pub fn send_draft(
return false; return false;
} }
let bytes; let bytes;
let (cmd, args) = (parts[0], &parts[1..]); let mut msmtp = Command::new("sh")
let mut msmtp = Command::new(cmd) .args(&["-c", cmd])
.args(args)
.stdin(Stdio::piped()) .stdin(Stdio::piped())
.stdout(Stdio::piped()) .stdout(Stdio::piped())
.spawn() .spawn()

View File

@ -170,10 +170,8 @@ impl MailView {
) )
.as_ref() .as_ref()
{ {
let parts = split_command!(filter_invocation); let command_obj = Command::new("sh")
let (cmd, args) = (parts[0], &parts[1..]); .args(&["-c", filter_invocation])
let command_obj = Command::new(cmd)
.args(args)
.stdin(Stdio::piped()) .stdin(Stdio::piped())
.stdout(Stdio::piped()) .stdout(Stdio::piped())
.spawn(); .spawn();

View File

@ -98,10 +98,8 @@ impl EnvelopeView {
use std::io::Write; use std::io::Write;
let settings = &context.settings; let settings = &context.settings;
if let Some(filter_invocation) = settings.pager.html_filter.as_ref() { if let Some(filter_invocation) = settings.pager.html_filter.as_ref() {
let parts = split_command!(filter_invocation); let command_obj = Command::new("sh")
let (cmd, args) = (parts[0], &parts[1..]); .args(&["-c", filter_invocation])
let command_obj = Command::new(cmd)
.args(args)
.stdin(Stdio::piped()) .stdin(Stdio::piped())
.stdout(Stdio::piped()) .stdout(Stdio::piped())
.spawn(); .spawn();

View File

@ -38,10 +38,8 @@ impl HtmlView {
let settings = &context.settings; let settings = &context.settings;
let mut display_text = if let Some(filter_invocation) = settings.pager.html_filter.as_ref() let mut display_text = if let Some(filter_invocation) = settings.pager.html_filter.as_ref()
{ {
let parts = split_command!(filter_invocation); let command_obj = Command::new("sh")
let (cmd, args) = (parts[0], &parts[1..]); .args(&["-c", filter_invocation])
let command_obj = Command::new(cmd)
.args(args)
.stdin(Stdio::piped()) .stdin(Stdio::piped())
.stdout(Stdio::piped()) .stdout(Stdio::piped())
.spawn(); .spawn();

View File

@ -676,10 +676,8 @@ impl Account {
} }
pub fn refresh(&mut self, mailbox_hash: MailboxHash) -> Result<()> { pub fn refresh(&mut self, mailbox_hash: MailboxHash) -> Result<()> {
if let Some(ref refresh_command) = self.settings.conf().refresh_command { if let Some(ref refresh_command) = self.settings.conf().refresh_command {
let parts = crate::split_command!(refresh_command); let child = std::process::Command::new("sh")
let (cmd, args) = (parts[0], &parts[1..]); .args(&["-c", refresh_command])
let child = std::process::Command::new(cmd)
.args(args)
.stdin(std::process::Stdio::null()) .stdin(std::process::Stdio::null())
.stdout(std::process::Stdio::null()) .stdout(std::process::Stdio::null())
.stderr(std::process::Stdio::piped()) .stderr(std::process::Stdio::piped())

View File

@ -21,7 +21,6 @@
/*! Find mailcap entries to execute attachments. /*! Find mailcap entries to execute attachments.
*/ */
use crate::split_command;
use crate::state::Context; use crate::state::Context;
use crate::types::{create_temp_file, ForkType, UIEvent}; use crate::types::{create_temp_file, ForkType, UIEvent};
use melib::attachments::decode; use melib::attachments::decode;
@ -180,10 +179,15 @@ impl MailcapEntry {
{ {
context.input_kill(); context.input_kill();
} }
let cmd_string = format!("{} {}", cmd, args.join(" "));
melib::log(
format!("Executing: sh -c \"{}\"", cmd_string.replace("\"", "\\\"")),
melib::DEBUG,
);
if copiousoutput { if copiousoutput {
let out = if needs_stdin { let out = if needs_stdin {
let mut child = Command::new(cmd) let mut child = Command::new("sh")
.args(args) .args(&["-c", &cmd_string])
.stdin(Stdio::piped()) .stdin(Stdio::piped())
.stdout(Stdio::piped()) .stdout(Stdio::piped())
.spawn()?; .spawn()?;
@ -191,8 +195,8 @@ impl MailcapEntry {
child.stdin.as_mut().unwrap().write_all(&decode(a, None))?; child.stdin.as_mut().unwrap().write_all(&decode(a, None))?;
child.wait_with_output()?.stdout child.wait_with_output()?.stdout
} else { } else {
let child = Command::new(cmd) let child = Command::new("sh")
.args(args) .args(&["-c", &cmd_string])
.stdin(Stdio::piped()) .stdin(Stdio::piped())
.stdout(Stdio::piped()) .stdout(Stdio::piped())
.spawn()?; .spawn()?;
@ -213,8 +217,8 @@ impl MailcapEntry {
debug!(pager.wait_with_output()?.stdout); debug!(pager.wait_with_output()?.stdout);
} else { } else {
if needs_stdin { if needs_stdin {
let mut child = Command::new(cmd) let mut child = Command::new("sh")
.args(args) .args(&["-c", &cmd_string])
.stdin(Stdio::piped()) .stdin(Stdio::piped())
.stdout(Stdio::inherit()) .stdout(Stdio::inherit())
.spawn()?; .spawn()?;
@ -222,8 +226,8 @@ impl MailcapEntry {
child.stdin.as_mut().unwrap().write_all(&decode(a, None))?; child.stdin.as_mut().unwrap().write_all(&decode(a, None))?;
debug!(child.wait_with_output()?.stdout); debug!(child.wait_with_output()?.stdout);
} else { } else {
let child = Command::new(cmd) let child = Command::new("sh")
.args(args) .args(&["-c", &cmd_string])
.stdin(Stdio::inherit()) .stdin(Stdio::inherit())
.stdout(Stdio::inherit()) .stdout(Stdio::inherit())
.spawn()?; .spawn()?;

View File

@ -176,9 +176,9 @@ impl PluginManager {
match plugin.kind { match plugin.kind {
PluginKind::LongLived => { PluginKind::LongLived => {
/* spawn thread */ /* spawn thread */
let parts = split_command!(&plugin.executable); let inv = &plugin.executable;
let child = std::process::Command::new(&parts[0]) let child = std::process::Command::new("sh")
.args(&parts[1..]) .args(&["-c", inv])
.stdin(Stdio::piped()) .stdin(Stdio::piped())
.stdout(Stdio::piped()) .stdout(Stdio::piped())
.spawn()?; .spawn()?;
@ -241,9 +241,9 @@ impl PluginManager {
return reply; return reply;
} }
PluginKind::Filter => { PluginKind::Filter => {
let parts = split_command!(&plugin.executable); let inv = &plugin.executable;
let mut child = std::process::Command::new(&parts[0]) let mut child = std::process::Command::new("sh")
.args(&parts[1..]) .args(&["-c", inv])
.stdin(Stdio::piped()) .stdin(Stdio::piped())
.stdout(Stdio::piped()) .stdout(Stdio::piped())
.spawn()?; .spawn()?;

View File

@ -238,9 +238,9 @@ impl PluginBackend {
&plugin.name, &plugin.kind &plugin.name, &plugin.kind
))); )));
} }
let parts = split_command!(&plugin.executable); let inv = &plugin.executable;
let child = std::process::Command::new(&parts[0]) let child = std::process::Command::new("sh")
.args(&parts[1..]) .args(&["-c", inv])
.stdin(Stdio::piped()) .stdin(Stdio::piped())
.stdout(Stdio::piped()) .stdout(Stdio::piped())
.spawn()?; .spawn()?;

View File

@ -115,19 +115,12 @@ pub fn create_pty(
std::process::exit(-1); std::process::exit(-1);
} }
} }
let parts = split_command!(command);
let (cmd, _) = (parts[0], &parts[1..]);
let _parts = parts
.iter()
.map(|&a| CString::new(a).unwrap())
.collect::<Vec<CString>>();
if let Err(e) = nix::unistd::execv( if let Err(e) = nix::unistd::execv(
&CString::new(cmd).unwrap(), &CString::new("sh").unwrap(),
_parts &[
.iter() &CString::new("-c").unwrap(),
.map(|a| a.as_c_str()) &CString::new(command.as_bytes()).unwrap(),
.collect::<Vec<&_>>() ],
.as_slice(),
) { ) {
log(format!("Could not execute `{}`: {}", command, e,), ERROR); log(format!("Could not execute `{}`: {}", command, e,), ERROR);
std::process::exit(-1); std::process::exit(-1);