1
1
mirror of https://github.com/wez/wezterm.git synced 2024-10-26 23:58:28 +03:00

Take care to restore the original umask

wezterm sets a more restrictive umask (`0o077`) by default so that any files
that it creates (eg: unix domain socket, log files) are more secure
by default.

However, some environments rely on the more general default of (`0o022`)
without checking that it is set.

This matters because programs spawned by wezterm inherit its more
restricted umask.

I hadn't noticed this because I've had `umask 022` in my shell RC files
since sometime in the 1990's.

This commit adds some plumbing to the pty layer to specify an optional
umask for the child process, and some more to our umask saver helper
so that any thread can determine the saved umask without needing a
reference to the saver itself, which may be in a different crate.

The logic in the config crate has been adjusted to connect the saved
value to the default command builder arguments.

The net result of this is that running `wezterm -n start bash -- --norc`
and typing `umask` in the resultant window now prints `0022`.

refs: #416
This commit is contained in:
Wez Furlong 2021-01-07 09:14:51 -08:00
parent 704dd9aa44
commit db0d54cf44
8 changed files with 55 additions and 4 deletions

2
Cargo.lock generated
View File

@ -693,6 +693,7 @@ dependencies = [
"smol",
"termwiz",
"toml",
"umask",
"vergen",
"wezterm-input-types",
"wezterm-term",
@ -3836,6 +3837,7 @@ dependencies = [
name = "umask"
version = "0.1.0"
dependencies = [
"lazy_static",
"libc",
]

View File

@ -32,6 +32,7 @@ serde = {version="1.0", features = ["rc", "derive"]}
smol = "1.2"
termwiz = { path = "../termwiz" }
toml = "0.5"
umask = { path = "../umask" }
wezterm-input-types = { path = "../wezterm-input-types" }
wezterm-term = { path = "../term", features=["use_serde"] }

View File

@ -1241,6 +1241,9 @@ impl Config {
for (k, v) in &self.set_environment_variables {
cmd.env(k, v);
}
#[cfg(unix)]
cmd.umask(umask::UmaskSaver::saved_umask());
cmd.env("TERM", &self.term);
// TERM_PROGRAM and TERM_PROGRAM_VERSION are an emerging
// de-facto standard for identifying the terminal.

View File

@ -14,6 +14,8 @@ pub struct CommandBuilder {
args: Vec<OsString>,
envs: Vec<(OsString, OsString)>,
cwd: Option<OsString>,
#[cfg(unix)]
pub(crate) umask: Option<libc::mode_t>,
}
impl CommandBuilder {
@ -24,6 +26,7 @@ impl CommandBuilder {
args: vec![program.as_ref().to_owned()],
envs: vec![],
cwd: None,
umask: None,
}
}
@ -33,6 +36,7 @@ impl CommandBuilder {
args,
envs: vec![],
cwd: None,
umask: None,
}
}
@ -43,6 +47,7 @@ impl CommandBuilder {
args: vec![],
envs: vec![],
cwd: None,
umask: None,
}
}
@ -88,6 +93,11 @@ impl CommandBuilder {
self.cwd = Some(dir.as_ref().to_owned());
}
#[cfg(unix)]
pub fn umask(&mut self, mask: Option<libc::mode_t>) {
self.umask = mask;
}
#[cfg(feature = "ssh")]
pub(crate) fn iter_env_as_str(&self) -> impl Iterator<Item = (&str, &str)> {
self.envs.iter().filter_map(|(key, val)| {

View File

@ -192,6 +192,8 @@ impl PtyFd {
}
fn spawn_command(&self, builder: CommandBuilder) -> anyhow::Result<std::process::Child> {
let configured_umask = builder.umask;
let mut cmd = builder.as_command()?;
unsafe {
@ -235,6 +237,10 @@ impl PtyFd {
close_random_fds();
if let Some(mask) = configured_umask {
libc::umask(mask);
}
Ok(())
})
};

View File

@ -7,4 +7,5 @@ edition = "2018"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
lazy_static = "1.4"
libc = "0.2"

View File

@ -1,5 +1,12 @@
#[cfg(unix)]
use libc::{mode_t, umask};
#[cfg(unix)]
use std::sync::Mutex;
#[cfg(unix)]
lazy_static::lazy_static! {
static ref SAVED_UMASK: Mutex<Option<libc::mode_t>> = Mutex::new(None);
}
/// Unfortunately, novice unix users can sometimes be running
/// with an overly permissive umask so we take care to install
@ -14,10 +21,26 @@ pub struct UmaskSaver {
impl UmaskSaver {
pub fn new() -> Self {
Self {
let me = Self {
#[cfg(unix)]
mask: unsafe { umask(0o077) },
};
#[cfg(unix)]
{
SAVED_UMASK.lock().unwrap().replace(me.mask);
}
me
}
/// Retrieves the mask saved by a UmaskSaver, without
/// having a reference to the UmaskSaver.
/// This is only meaningful if a single UmaskSaver is
/// used in a program.
#[cfg(unix)]
pub fn saved_umask() -> Option<mode_t> {
SAVED_UMASK.lock().unwrap().clone()
}
}
@ -26,6 +49,7 @@ impl Drop for UmaskSaver {
#[cfg(unix)]
unsafe {
umask(self.mask);
SAVED_UMASK.lock().unwrap().take();
}
}
}

View File

@ -10,6 +10,7 @@ use std::io::{Read, Write};
use std::rc::Rc;
use structopt::StructOpt;
use tabout::{tabulate_output, Alignment, Column};
use umask::UmaskSaver;
use wezterm_client::client::{unix_connect_with_retry, Client};
use wezterm_gui_subcommands::*;
@ -223,7 +224,7 @@ fn main() {
fn run() -> anyhow::Result<()> {
env_bootstrap::bootstrap();
let _saver = umask::UmaskSaver::new();
let saver = UmaskSaver::new();
let opts = Opt::from_args();
if !opts.skip_config {
@ -240,16 +241,19 @@ fn run() -> anyhow::Result<()> {
SubCommand::Start(_)
| SubCommand::Ssh(_)
| SubCommand::Serial(_)
| SubCommand::Connect(_) => delegate_to_gui(),
| SubCommand::Connect(_) => delegate_to_gui(saver),
SubCommand::ImageCat(cmd) => cmd.run(),
SubCommand::SetCwd(cmd) => cmd.run(),
SubCommand::Cli(cli) => run_cli(config, cli),
}
}
fn delegate_to_gui() -> anyhow::Result<()> {
fn delegate_to_gui(saver: UmaskSaver) -> anyhow::Result<()> {
use std::process::Command;
// Restore the original umask
drop(saver);
let exe_name = if cfg!(windows) {
"wezterm-gui.exe"
} else {