Skip to content

Commit c797146

Browse files
committed
expand alias table while printing in --list instead of unfolding in memory; fixes #979
1 parent 7cdd28d commit c797146

File tree

4 files changed

+59
-42
lines changed

4 files changed

+59
-42
lines changed

src/sudoers/ast.rs

-7
Original file line numberDiff line numberDiff line change
@@ -25,13 +25,6 @@ impl<T> Qualified<T> {
2525
}
2626
}
2727

28-
pub fn negate(&self) -> Qualified<&T> {
29-
match self {
30-
Qualified::Allow(item) => Qualified::Forbid(item),
31-
Qualified::Forbid(item) => Qualified::Allow(item),
32-
}
33-
}
34-
3528
#[cfg(test)]
3629
pub fn as_allow(&self) -> Option<&T> {
3730
if let Self::Allow(v) = self {

src/sudoers/entry.rs

+44-7
Original file line numberDiff line numberDiff line change
@@ -12,25 +12,32 @@ use crate::{
1212
use self::verbose::Verbose;
1313

1414
use super::{
15-
ast::{Authenticate, RunAs, Tag},
15+
ast::{Authenticate, Def, RunAs, Tag},
1616
tokens::Command,
17+
VecOrd,
1718
};
1819

1920
mod verbose;
2021

2122
pub struct Entry<'a> {
2223
run_as: Option<&'a RunAs>,
2324
cmd_specs: Vec<(Tag, Qualified<&'a Meta<Command>>)>,
25+
cmd_alias: &'a VecOrd<Def<Command>>,
2426
}
2527

2628
impl<'a> Entry<'a> {
2729
pub(super) fn new(
2830
run_as: Option<&'a RunAs>,
2931
cmd_specs: Vec<(Tag, Qualified<&'a Meta<Command>>)>,
32+
cmd_alias: &'a VecOrd<Def<Command>>,
3033
) -> Self {
3134
debug_assert!(!cmd_specs.is_empty());
3235

33-
Self { run_as, cmd_specs }
36+
Self {
37+
run_as,
38+
cmd_specs,
39+
cmd_alias,
40+
}
3441
}
3542

3643
pub fn verbose(self) -> impl fmt::Display + 'a {
@@ -56,7 +63,11 @@ fn root_runas() -> RunAs {
5663

5764
impl fmt::Display for Entry<'_> {
5865
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
59-
let Self { run_as, cmd_specs } = self;
66+
let Self {
67+
run_as,
68+
cmd_specs,
69+
cmd_alias,
70+
} = self;
6071

6172
let root_runas = root_runas();
6273
let run_as = run_as.unwrap_or(&root_runas);
@@ -79,7 +90,7 @@ impl fmt::Display for Entry<'_> {
7990

8091
write_tag(f, tag, last_tag)?;
8192
last_tag = Some(tag);
82-
write_spec(f, spec)?;
93+
write_spec(f, spec, cmd_alias, true)?;
8394
}
8495

8596
Ok(())
@@ -208,17 +219,28 @@ fn write_tag(f: &mut fmt::Formatter, tag: &Tag, last_tag: Option<&Tag>) -> fmt::
208219
Ok(())
209220
}
210221

211-
fn write_spec(f: &mut fmt::Formatter, spec: &Qualified<&Meta<Command>>) -> fmt::Result {
222+
fn write_spec(
223+
f: &mut fmt::Formatter,
224+
spec: &Qualified<&Meta<Command>>,
225+
alias_list: &VecOrd<Def<Command>>,
226+
mut sign: bool,
227+
) -> fmt::Result {
212228
let meta = match spec {
213229
Qualified::Allow(meta) => meta,
214230
Qualified::Forbid(meta) => {
215-
f.write_str("!")?;
231+
sign = !sign;
216232
meta
217233
}
218234
};
219235

236+
match meta {
237+
Meta::All | Meta::Only(_) if !sign => f.write_str("!")?,
238+
_ => {}
239+
}
240+
220241
match meta {
221242
Meta::All => f.write_str("ALL")?,
243+
222244
Meta::Only((cmd, args)) => {
223245
write!(f, "{cmd}")?;
224246
if let Some(args) = args {
@@ -227,7 +249,22 @@ fn write_spec(f: &mut fmt::Formatter, spec: &Qualified<&Meta<Command>>) -> fmt::
227249
}
228250
}
229251
}
230-
Meta::Alias(alias) => f.write_str(alias)?,
252+
Meta::Alias(alias) => {
253+
// this will terminate, since AliasTable has been checked by sanitize_alias_table
254+
if let Some(Def(_, spec_list)) = super::elems(alias_list).find(|Def(id, _)| id == alias)
255+
{
256+
let mut is_first_iteration = true;
257+
for spec in spec_list {
258+
if !is_first_iteration {
259+
f.write_str(", ")?;
260+
}
261+
write_spec(f, &spec.as_ref(), alias_list, sign)?;
262+
is_first_iteration = false;
263+
}
264+
} else {
265+
f.write_str("???")?
266+
}
267+
}
231268
}
232269

233270
Ok(())

src/sudoers/entry/verbose.rs

+6-2
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,11 @@ pub struct Verbose<'a>(pub Entry<'a>);
1111

1212
impl fmt::Display for Verbose<'_> {
1313
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
14-
let Self(Entry { run_as, cmd_specs }) = self;
14+
let Self(Entry {
15+
run_as,
16+
cmd_specs,
17+
cmd_alias,
18+
}) = self;
1519

1620
let root_runas = super::root_runas();
1721
let run_as = run_as.unwrap_or(&root_runas);
@@ -31,7 +35,7 @@ impl fmt::Display for Verbose<'_> {
3135
last_tag = Some(tag);
3236

3337
f.write_str("\n\t")?;
34-
super::write_spec(f, cmd_spec)?;
38+
super::write_spec(f, cmd_spec, cmd_alias, true)?;
3539
}
3640

3741
Ok(())

src/sudoers/mod.rs

+9-26
Original file line numberDiff line numberDiff line change
@@ -179,10 +179,9 @@ impl Sudoers {
179179
// ensure `sudo $command` and `sudo --list` use the same permission checking logic
180180
let user_specs = self.matching_user_specs(invoking_user, hostname);
181181

182-
let cmnd_aliases = unfold_alias_table(&self.aliases.cmnd);
183182
let mut entries = vec![];
184183
for cmd_specs in user_specs {
185-
group_cmd_specs_per_runas(cmd_specs, &mut entries, &cmnd_aliases);
184+
group_cmd_specs_per_runas(cmd_specs, &mut entries, &self.aliases.cmnd);
186185
}
187186

188187
entries
@@ -214,41 +213,29 @@ impl Sudoers {
214213
fn group_cmd_specs_per_runas<'a>(
215214
cmnd_specs: impl Iterator<Item = (Option<&'a RunAs>, (Tag, &'a Spec<Command>))>,
216215
entries: &mut Vec<Entry<'a>>,
217-
cmnd_aliases: &HashMap<&String, &'a Vec<Spec<Command>>>,
216+
cmnd_aliases: &'a VecOrd<Def<Command>>,
218217
) {
219218
let mut last_runas = None;
220219
let mut collected_specs = vec![];
221220

222221
for (runas, (tag, spec)) in cmnd_specs {
223222
if runas.map(|r| r as *const _) != last_runas.map(|r| r as *const _) {
224223
if !collected_specs.is_empty() {
225-
entries.push(Entry::new(last_runas, mem::take(&mut collected_specs)));
224+
entries.push(Entry::new(
225+
last_runas,
226+
mem::take(&mut collected_specs),
227+
cmnd_aliases,
228+
));
226229
}
227230

228231
last_runas = runas;
229232
}
230233

231-
let (negate, meta) = match spec {
232-
Qualified::Allow(meta) => (false, meta),
233-
Qualified::Forbid(meta) => (true, meta),
234-
};
235-
236-
if let Meta::Alias(alias_name) = meta {
237-
if let Some(specs) = cmnd_aliases.get(alias_name) {
238-
// expand Cmnd_Alias
239-
for spec in specs.iter() {
240-
let new_spec = if negate { spec.negate() } else { spec.as_ref() };
241-
242-
collected_specs.push((tag.clone(), new_spec))
243-
}
244-
}
245-
} else {
246-
collected_specs.push((tag, spec.as_ref()));
247-
}
234+
collected_specs.push((tag, spec.as_ref()));
248235
}
249236

250237
if !collected_specs.is_empty() {
251-
entries.push(Entry::new(last_runas, collected_specs));
238+
entries.push(Entry::new(last_runas, collected_specs, cmnd_aliases));
252239
}
253240
}
254241

@@ -481,10 +468,6 @@ fn match_command<'a>((cmd, args): (&'a Path, &'a [String])) -> (impl Fn(&Command
481468
}
482469
}
483470

484-
fn unfold_alias_table<T>(table: &VecOrd<Def<T>>) -> HashMap<&String, &Vec<Qualified<Meta<T>>>> {
485-
elems(table).map(|Def(id, list)| (id, list)).collect()
486-
}
487-
488471
/// Find all the aliases that a object is a member of; this requires [sanitize_alias_table] to have run first;
489472
/// I.e. this function should not be "pub".
490473
fn get_aliases<Predicate, T>(table: &VecOrd<Def<T>>, pred: &Predicate) -> FoundAliases

0 commit comments

Comments
 (0)