Skip to content

Commit

Permalink
feat: changeable effects (WIP)
Browse files Browse the repository at this point in the history
  • Loading branch information
desbma-s1n committed Nov 15, 2024
1 parent baeea83 commit 91b8de1
Show file tree
Hide file tree
Showing 2 changed files with 157 additions and 32 deletions.
24 changes: 23 additions & 1 deletion src/summarize.rs
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,7 @@ impl ValueCounted for u16 {
}
}

impl<T: Eq + ValueCounted> CountableSetSpecifier<T> {
impl<T: Eq + Ord + Clone + ValueCounted> CountableSetSpecifier<T> {
fn contains_one(&self, needle: &T) -> bool {
match self {
Self::None => false,
Expand All @@ -116,6 +116,28 @@ impl<T: Eq + ValueCounted> CountableSetSpecifier<T> {
Self::All => !matches!(other, Self::None),
}
}

pub(crate) fn remove(&mut self, es: &Self) {
debug_assert!(self.intersects(es));
let Self::One(e) = es else { unreachable!() };
match self {
Self::None => unreachable!(),
Self::One(_) => {
*self = Self::None;
}
Self::Some(es) => {
let idx = es.iter().position(|e2| e == e2).unwrap();
es.remove(idx);
}
Self::AllExcept(excs) => {
let idx = excs.binary_search(&e).unwrap_err();
excs.insert(idx, e.to_owned());
}
Self::All => {
*self = Self::AllExcept(vec![e.to_owned()]);
}
}
}
}

/// Quantify something that is done or denied
Expand Down
165 changes: 134 additions & 31 deletions src/systemd/resolver.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,15 @@ use crate::{
},
};

type OptionValueUpdater = fn(&OptionValueEffect) -> OptionValue;

impl OptionValueEffect {
fn compatible(&self, action: &ProgramAction, prev_actions: &[ProgramAction]) -> bool {
fn compatible(
&self,
action: &ProgramAction,
prev_actions: &[ProgramAction],
value_updater: Option<OptionValueUpdater>,
) -> ActionOptionEffectCompatibility {
match self {
OptionValueEffect::DenyAction(denied) => match denied {
ProgramAction::NetworkActivity(denied) => {
Expand All @@ -19,66 +26,145 @@ impl OptionValueEffect {
local_port,
}) = action
{
!denied.af.intersects(af)
|| !denied.proto.intersects(proto)
|| !denied.kind.intersects(kind)
|| !denied.local_port.intersects(local_port)
let af_match = denied.af.intersects(af);
let proto_match = denied.proto.intersects(proto);
let kind_match = denied.kind.intersects(kind);
let local_port_match = denied.local_port.intersects(local_port);
if !af_match || !proto_match || !kind_match || !local_port_match {
ActionOptionEffectCompatibility::Compatible
} else if let Some(value_updater) = value_updater {
// This option supports altering the effects and generating the option value, to make it
// compatible
let mut new_eff_local_port = denied.local_port.to_owned();
new_eff_local_port.remove(local_port);
let new_eff = OptionValueEffect::DenyAction(
ProgramAction::NetworkActivity(NetworkActivity {
af: denied.af.to_owned(),
proto: denied.proto.to_owned(),
kind: denied.kind.to_owned(),
local_port: new_eff_local_port,
}),
);
ActionOptionEffectCompatibility::CompatibleIfChanged(
ChangedOptionValueDescription {
value: value_updater(&new_eff),
effect: new_eff,
},
)
} else {
ActionOptionEffectCompatibility::Incompatible
}
} else {
true
ActionOptionEffectCompatibility::Compatible
}
}
ProgramAction::WriteExecuteMemoryMapping
| ProgramAction::SetRealtimeScheduler
| ProgramAction::Wakeup
| ProgramAction::MknodSpecial
| ProgramAction::SetAlarm => action != denied,
| ProgramAction::SetAlarm => (action != denied).into(),
ProgramAction::Syscalls(_)
| ProgramAction::Read(_)
| ProgramAction::Write(_)
| ProgramAction::Create(_) => unreachable!(),
},
OptionValueEffect::DenyWrite(ro_paths) => match action {
ProgramAction::Write(path_action) | ProgramAction::Create(path_action) => {
!ro_paths.matches(path_action)
!ro_paths.matches(path_action).into()
}
_ => true,
_ => ActionOptionEffectCompatibility::Compatible,
},
OptionValueEffect::Hide(hidden_paths) => {
if let ProgramAction::Read(path_action) = action {
!hidden_paths.matches(path_action)
|| prev_actions.contains(&ProgramAction::Create(path_action.clone()))
(!hidden_paths.matches(path_action)
|| prev_actions.contains(&ProgramAction::Create(path_action.clone())))
.into()
} else {
true
ActionOptionEffectCompatibility::Compatible
}
}
OptionValueEffect::DenySyscalls(denied) => {
if let ProgramAction::Syscalls(syscalls) = action {
let denied_syscalls = denied.syscalls();
let syscalls = syscalls.iter().map(String::as_str).collect();
denied_syscalls.intersection(&syscalls).next().is_none()
denied_syscalls
.intersection(&syscalls)
.next()
.is_none()
.into()
} else {
true
ActionOptionEffectCompatibility::Compatible
}
}
OptionValueEffect::Multiple(effects) => {
effects.iter().all(|e| e.compatible(action, prev_actions))
}
OptionValueEffect::Multiple(effects) => effects
.iter()
.all(|e| match e.compatible(action, prev_actions) {
ActionOptionEffectCompatibility::Compatible => true,
ActionOptionEffectCompatibility::CompatibleIfChanged(_) => todo!(),
ActionOptionEffectCompatibility::Incompatible => false,
})
.into(),
}
}
}

/// A systemd option value and its effect, altered from original
struct ChangedOptionValueDescription {
pub value: OptionValue,
pub effect: OptionValueEffect,
}

/// How compatible is an action with an option effect?
enum ActionOptionEffectCompatibility {
Compatible,
CompatibleIfChanged(ChangedOptionValueDescription),
Incompatible,
}

impl From<bool> for ActionOptionEffectCompatibility {
fn from(value: bool) -> Self {
if value {
Self::Compatible
} else {
Self::Incompatible
}
}
}

pub(crate) fn actions_compatible(eff: &OptionValueEffect, actions: &[ProgramAction]) -> bool {
pub(crate) fn actions_compatible(
eff: &OptionValueEffect,
actions: &[ProgramAction],
) -> ActionOptionEffectCompatibility {
let mut changed_desc: Option<ChangedOptionValueDescription> = None;
for i in 0..actions.len() {
if !eff.compatible(&actions[i], &actions[..i]) {
log::debug!(
"Option effect {:?} is incompatible with {:?}",
eff,
actions[i]
);
return false;
let cur_eff = changed_desc.as_ref().map(|d| &d.effect).unwrap_or(eff);
match cur_eff.compatible(&actions[i], &actions[..i]) {
ActionOptionEffectCompatibility::Compatible => {}
ActionOptionEffectCompatibility::CompatibleIfChanged(new_desc) => {
log::debug!(
"Option effect {:?} is incompatible with {:?}, changing effect to {:?}",
cur_eff,
actions[i],
new_desc.effect
);
changed_desc = Some(new_desc);
}
ActionOptionEffectCompatibility::Incompatible => {
log::debug!(
"Option effect {:?} is incompatible with {:?}",
cur_eff,
actions[i]
);
return ActionOptionEffectCompatibility::Incompatible;
}
}
}
true

if let Some(new_desc) = changed_desc {
ActionOptionEffectCompatibility::CompatibleIfChanged(new_desc)
} else {
ActionOptionEffectCompatibility::Compatible
}
}

pub(crate) fn resolve(
Expand All @@ -98,15 +184,23 @@ pub(crate) fn resolve(
});
break;
}
OptionEffect::Simple(effect) => {
if actions_compatible(effect, actions) {
OptionEffect::Simple(effect) => match actions_compatible(effect, actions) {
ActionOptionEffectCompatibility::Compatible => {
candidates.push(OptionWithValue {
name: opt.name.to_owned(),
value: opt_value_desc.value.clone(),
});
break;
}
}
ActionOptionEffectCompatibility::CompatibleIfChanged(opt_new_desc) => {
candidates.push(OptionWithValue {
name: opt.name.to_owned(),
value: opt_new_desc.value.clone(),
});
break;
}
ActionOptionEffectCompatibility::Incompatible => {}
},
OptionEffect::Cumulative(effects) => {
match &opt_value_desc.value {
OptionValue::List {
Expand All @@ -121,8 +215,17 @@ pub(crate) fn resolve(
for (optv, opte) in values.iter().zip(effects) {
let compatible = actions_compatible(opte, actions);
let enable_opt = match mode {
ListMode::WhiteList => !compatible,
ListMode::BlackList => compatible,
ListMode::WhiteList => matches!(
compatible,
ActionOptionEffectCompatibility::Incompatible
),
ListMode::BlackList => match compatible {
ActionOptionEffectCompatibility::Compatible => true,
ActionOptionEffectCompatibility::CompatibleIfChanged(_) => {
unimplemented!()
}
ActionOptionEffectCompatibility::Incompatible => false,
},
};
if enable_opt {
compatible_opts.push(optv.to_string());
Expand Down

0 comments on commit 91b8de1

Please sign in to comment.