From baeea830eae03062be7f664fa6cd42ca25ce37fe Mon Sep 17 00:00:00 2001 From: desbma Date: Sun, 10 Nov 2024 15:11:53 +0100 Subject: [PATCH] feat: support negative sets --- src/summarize.rs | 87 +++++++++++++++++++++++++++++++++++++---- src/systemd/options.rs | 9 ++++- src/systemd/resolver.rs | 9 ++++- 3 files changed, 94 insertions(+), 11 deletions(-) diff --git a/src/summarize.rs b/src/summarize.rs index f232f4b..4fccf9e 100644 --- a/src/summarize.rs +++ b/src/summarize.rs @@ -48,6 +48,7 @@ pub(crate) struct NetworkActivity { pub af: SetSpecifier, pub proto: SetSpecifier, pub kind: SetSpecifier, + pub local_port: CountableSetSpecifier, } /// Quantify something that is done or denied @@ -62,23 +63,71 @@ pub(crate) enum SetSpecifier { impl SetSpecifier { fn contains_one(&self, needle: &T) -> bool { match self { - SetSpecifier::None => false, - SetSpecifier::One(e) => e == needle, - SetSpecifier::Some(es) => es.contains(needle), - SetSpecifier::All => true, + Self::None => false, + Self::One(e) => e == needle, + Self::Some(es) => es.contains(needle), + Self::All => true, } } pub(crate) fn intersects(&self, other: &Self) -> bool { match self { - SetSpecifier::None => false, - SetSpecifier::One(e) => other.contains_one(e), - SetSpecifier::Some(es) => es.iter().any(|e| other.contains_one(e)), - SetSpecifier::All => !matches!(other, SetSpecifier::None), + Self::None => false, + Self::One(e) => other.contains_one(e), + Self::Some(es) => es.iter().any(|e| other.contains_one(e)), + Self::All => !matches!(other, Self::None), } } } +pub(crate) trait ValueCounted { + fn value_count() -> usize; +} + +impl ValueCounted for u16 { + fn value_count() -> usize { + Self::MAX as usize - Self::MIN as usize + 1 + } +} + +impl CountableSetSpecifier { + fn contains_one(&self, needle: &T) -> bool { + match self { + Self::None => false, + Self::One(e) => e == needle, + Self::Some(es) => es.contains(needle), + Self::AllExcept(es) => !es.contains(needle), + Self::All => true, + } + } + + pub(crate) fn intersects(&self, other: &Self) -> bool { + match self { + Self::None => false, + Self::One(e) => other.contains_one(e), + Self::Some(es) => es.iter().any(|e| other.contains_one(e)), + Self::AllExcept(excs) => match other { + Self::None => false, + Self::One(e) => !excs.contains(e), + Self::Some(es) => es.iter().any(|e| !excs.contains(e)), + Self::AllExcept(other_excs) => excs != other_excs, + Self::All => excs.len() < T::value_count(), + }, + Self::All => !matches!(other, Self::None), + } + } +} + +/// Quantify something that is done or denied +#[derive(Debug, Clone, Eq, PartialEq, serde::Serialize, serde::Deserialize)] +pub(crate) enum CountableSetSpecifier { + None, + One(T), + Some(Vec), + AllExcept(Vec), + All, +} + /// Socket activity #[derive(Debug, Clone, Eq, PartialEq, serde::Serialize, serde::Deserialize)] pub(crate) enum NetworkActivityKind { @@ -462,11 +511,32 @@ where let af = af .parse() .map_err(|()| anyhow::anyhow!("Unable to parse socket family {af:?}"))?; + let local_port = match addr + .iter() + .find_map(|(k, v)| k.ends_with("_port").then_some(v)) + { + Some(Expression::Macro { + name: macro_name, + args, + }) if macro_name == "htons" => match args.first() { + Some(Expression::Integer(IntegerExpression { + value: IntegerExpressionValue::Literal(port_val), + .. + })) => + { + #[expect(clippy::cast_possible_truncation, clippy::cast_sign_loss)] + CountableSetSpecifier::One(*port_val as u16) + } + _ => todo!(), + }, + _ => CountableSetSpecifier::None, + }; if let Some(proto) = known_sockets_proto.get(&(syscall.pid, *fd)) { actions.push(ProgramAction::NetworkActivity(NetworkActivity { af: SetSpecifier::One(af), proto: SetSpecifier::One(proto.to_owned()), kind: SetSpecifier::One(NetworkActivityKind::Bind), + local_port, })); } } @@ -516,6 +586,7 @@ where af: SetSpecifier::One(af), proto: SetSpecifier::One(proto), kind: SetSpecifier::One(NetworkActivityKind::SocketCreation), + local_port: CountableSetSpecifier::All, })); } Some(SyscallInfo::Mknod { mode_idx }) => { diff --git a/src/systemd/options.rs b/src/systemd/options.rs index e6b1a3c..b2a99e8 100644 --- a/src/systemd/options.rs +++ b/src/systemd/options.rs @@ -14,7 +14,9 @@ use strum::IntoEnumIterator; use crate::{ cl::HardeningMode, - summarize::{NetworkActivity, NetworkActivityKind, ProgramAction, SetSpecifier}, + summarize::{ + CountableSetSpecifier, NetworkActivity, NetworkActivityKind, ProgramAction, SetSpecifier, + }, systemd::{KernelVersion, SystemdVersion}, }; @@ -1164,6 +1166,7 @@ pub(crate) fn build_options( af: SetSpecifier::One(af.parse().unwrap()), proto: SetSpecifier::All, kind: SetSpecifier::All, + local_port: CountableSetSpecifier::All, }, )) }) @@ -1187,6 +1190,7 @@ pub(crate) fn build_options( af: SetSpecifier::All, proto: SetSpecifier::All, kind: SetSpecifier::All, + local_port: CountableSetSpecifier::All, }), )), }], @@ -1223,6 +1227,7 @@ pub(crate) fn build_options( af: SetSpecifier::One(af), proto: SetSpecifier::One(proto), kind: SetSpecifier::One(NetworkActivityKind::Bind), + local_port: CountableSetSpecifier::All, }, )) }) @@ -1321,6 +1326,7 @@ pub(crate) fn build_options( af: SetSpecifier::One(SocketFamily::Other("AF_PACKET".into())), proto: SetSpecifier::All, kind: SetSpecifier::All, + local_port: CountableSetSpecifier::All, }), )) .chain( @@ -1332,6 +1338,7 @@ pub(crate) fn build_options( af: SetSpecifier::One(af.parse().unwrap()), proto: SetSpecifier::One(SocketProtocol::Other("SOCK_RAW".into())), kind: SetSpecifier::All, + local_port: CountableSetSpecifier::All, }, )) }), diff --git a/src/systemd/resolver.rs b/src/systemd/resolver.rs index fa3cda0..5843fdb 100644 --- a/src/systemd/resolver.rs +++ b/src/systemd/resolver.rs @@ -12,12 +12,17 @@ impl OptionValueEffect { match self { OptionValueEffect::DenyAction(denied) => match denied { ProgramAction::NetworkActivity(denied) => { - if let ProgramAction::NetworkActivity(NetworkActivity { af, proto, kind }) = - action + if let ProgramAction::NetworkActivity(NetworkActivity { + af, + proto, + kind, + local_port, + }) = action { !denied.af.intersects(af) || !denied.proto.intersects(proto) || !denied.kind.intersects(kind) + || !denied.local_port.intersects(local_port) } else { true }