Skip to content

Commit 12eae18

Browse files
bjorn3squell
authored andcommitted
Implement support for the umask and umask_override defaults
1 parent 77fa3b6 commit 12eae18

File tree

6 files changed

+75
-3
lines changed

6 files changed

+75
-3
lines changed

src/common/context.rs

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

33
use crate::common::{HARDENED_ENUM_VALUE_0, HARDENED_ENUM_VALUE_1, HARDENED_ENUM_VALUE_2};
4-
use crate::exec::RunOptions;
4+
use crate::exec::{RunOptions, Umask};
55
use crate::sudo::{SudoListOptions, SudoRunOptions, SudoValidateOptions};
66
use crate::sudoers::Sudoers;
77
use crate::system::{Group, Hostname, Process, User};
@@ -32,6 +32,7 @@ pub struct Context {
3232
// policy
3333
pub use_pty: bool,
3434
pub noexec: bool,
35+
pub umask: Umask,
3536
}
3637

3738
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
@@ -95,6 +96,7 @@ impl Context {
9596
process: Process::new(),
9697
use_pty: true,
9798
noexec: false,
99+
umask: policy.umask(),
98100
})
99101
}
100102

@@ -120,6 +122,7 @@ impl Context {
120122
process: Process::new(),
121123
use_pty: true,
122124
noexec: false,
125+
umask: Umask::Preserve,
123126
})
124127
}
125128

@@ -165,6 +168,7 @@ impl Context {
165168
process: Process::new(),
166169
use_pty: true,
167170
noexec: false,
171+
umask: Umask::Preserve,
168172
})
169173
}
170174

@@ -181,6 +185,7 @@ impl Context {
181185
is_login: self.launch == LaunchType::Login,
182186
user: &self.target_user,
183187
group: &self.target_group,
188+
umask: self.umask,
184189

185190
use_pty: self.use_pty,
186191
noexec: self.noexec,

src/defaults/mod.rs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,8 @@ defaults! {
4343

4444
setenv = false
4545
apparmor_profile = None (!= None)
46+
umask = 0o022 (!= 0o777) {octal_mode}
47+
umask_override = false
4648

4749
passwd_tries = 3 [0..=1000]
4850

@@ -66,6 +68,12 @@ defaults! {
6668
"PYTHONINSPECT", "PYTHONUSERBASE", "RUBYLIB", "RUBYOPT", "*=()*"] #ignored
6769
}
6870

71+
fn octal_mode(input: &str) -> Option<i64> {
72+
<libc::mode_t>::from_str_radix(input.strip_prefix('0')?, 8)
73+
.ok()
74+
.map(Into::into)
75+
}
76+
6977
/// A custom parser to parse seconds as fractional "minutes", the format used by
7078
/// passwd_timeout and timestamp_timeout.
7179
fn fractional_minutes(input: &str) -> Option<i64> {

src/exec/mod.rs

Lines changed: 38 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,9 @@ use std::{
1818
};
1919

2020
use crate::{
21-
common::bin_serde::BinPipe,
21+
common::{
22+
bin_serde::BinPipe, HARDENED_ENUM_VALUE_0, HARDENED_ENUM_VALUE_1, HARDENED_ENUM_VALUE_2,
23+
},
2224
exec::no_pty::exec_no_pty,
2325
log::{dev_info, dev_warn, user_error},
2426
system::{
@@ -47,6 +49,17 @@ impl SpawnNoexecHandler {
4749
fn spawn(self) {}
4850
}
4951

52+
#[derive(Debug, Copy, Clone)]
53+
#[repr(u32)]
54+
pub enum Umask {
55+
/// Keep the umask of the parent process.
56+
Preserve = HARDENED_ENUM_VALUE_0,
57+
/// Mask out more of the permission bits in the new umask.
58+
Extend(libc::mode_t) = HARDENED_ENUM_VALUE_1,
59+
/// Override the umask of the parent process entirely with the given umask.
60+
Override(libc::mode_t) = HARDENED_ENUM_VALUE_2,
61+
}
62+
5063
pub struct RunOptions<'a> {
5164
pub command: &'a Path,
5265
pub arguments: &'a [String],
@@ -55,6 +68,7 @@ pub struct RunOptions<'a> {
5568
pub is_login: bool,
5669
pub user: &'a User,
5770
pub group: &'a Group,
71+
pub umask: Umask,
5872

5973
pub use_pty: bool,
6074
pub noexec: bool,
@@ -131,6 +145,29 @@ pub fn run_command(
131145
}
132146
}
133147

148+
// SAFETY: Umask is async-signal-safe.
149+
unsafe {
150+
let umask = options.umask;
151+
152+
command.pre_exec(move || {
153+
match umask {
154+
Umask::Preserve => {}
155+
Umask::Extend(umask) => {
156+
// The only options to get the existing umask are overwriting it or
157+
// parsing a /proc file. Given that this is a single-threaded context,
158+
// overwrite it with a safe value is fine and the simpler option.
159+
let existing_umask = libc::umask(0o777);
160+
libc::umask(existing_umask | umask);
161+
}
162+
Umask::Override(umask) => {
163+
libc::umask(umask);
164+
}
165+
}
166+
167+
Ok(())
168+
});
169+
}
170+
134171
let sudo_pid = ProcessId::new(std::process::id() as i32);
135172

136173
if options.use_pty {

src/su/context.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ use std::{
77
};
88

99
use crate::common::{error::Error, resolve::CurrentUser};
10-
use crate::exec::RunOptions;
10+
use crate::exec::{RunOptions, Umask};
1111
use crate::log::user_warn;
1212
use crate::system::{Group, Process, User};
1313
use crate::{common::resolve::is_valid_executable, system::interface::UserId};
@@ -216,6 +216,7 @@ impl SuContext {
216216
is_login: self.options.login,
217217
user: &self.user,
218218
group: &self.group,
219+
umask: Umask::Preserve,
219220

220221
use_pty: true,
221222
noexec: false,

src/sudo/env/tests.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
use crate::common::resolve::CurrentUser;
22
use crate::common::{CommandAndArguments, Context};
3+
use crate::exec::Umask;
34
use crate::sudo::{
45
cli::{SudoAction, SudoRunOptions},
56
env::environment::{get_target_environment, Environment},
@@ -134,6 +135,7 @@ fn create_test_context(sudo_options: SudoRunOptions) -> Context {
134135
use_pty: true,
135136
noexec: false,
136137
bell: false,
138+
umask: Umask::Preserve,
137139
}
138140
}
139141

src/sudoers/mod.rs

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ use std::path::{Path, PathBuf};
1515

1616
use crate::common::resolve::resolve_path;
1717
use crate::defaults;
18+
use crate::exec::Umask;
1819
use crate::log::auth_warn;
1920
use crate::system::interface::{GroupId, UnixGroup, UnixUser, UserId};
2021
use crate::system::{self, can_execute};
@@ -317,6 +318,24 @@ impl Sudoers {
317318

318319
None
319320
}
321+
322+
pub(crate) fn umask(&self) -> Umask {
323+
if self.settings.umask_override() {
324+
Umask::Override(
325+
self.settings
326+
.umask()
327+
.try_into()
328+
.expect("the umask parser should have prevented overflow"),
329+
)
330+
} else {
331+
Umask::Extend(
332+
self.settings
333+
.umask()
334+
.try_into()
335+
.expect("the umask parser should have prevented overflow"),
336+
)
337+
}
338+
}
320339
}
321340

322341
// a `take_while` variant that does not consume the first non-matching item

0 commit comments

Comments
 (0)