From a6257910bb534762813bbb80fc0d660dfd7acdaf Mon Sep 17 00:00:00 2001 From: Julian Orth Date: Sat, 3 May 2025 13:09:13 +0200 Subject: [PATCH] config: add exe client criteria --- jay-config/src/_private.rs | 1 + jay-config/src/_private/client.rs | 2 ++ jay-config/src/client.rs | 4 ++++ src/config/handler.rs | 1 + src/criteria/clm.rs | 8 ++++++- src/criteria/clm/clm_matchers/clmm_string.rs | 12 +++++++++++ src/utils/pid_info.rs | 21 ++++++++++++++++++- toml-config/src/config.rs | 2 ++ .../src/config/parsers/client_match.rs | 6 ++++++ toml-config/src/rules.rs | 2 ++ toml-spec/spec/spec.generated.json | 8 +++++++ toml-spec/spec/spec.generated.md | 12 +++++++++++ toml-spec/spec/spec.yaml | 8 +++++++ 13 files changed, 85 insertions(+), 2 deletions(-) diff --git a/jay-config/src/_private.rs b/jay-config/src/_private.rs index 7229991c..6b75daa1 100644 --- a/jay-config/src/_private.rs +++ b/jay-config/src/_private.rs @@ -93,4 +93,5 @@ pub enum ClientCriterionStringField { SandboxAppId, SandboxInstanceId, Comm, + Exe, } diff --git a/jay-config/src/_private/client.rs b/jay-config/src/_private/client.rs index 559bec40..df02a491 100644 --- a/jay-config/src/_private/client.rs +++ b/jay-config/src/_private/client.rs @@ -1547,6 +1547,8 @@ impl ConfigClient { ClientCriterion::IsXwayland => ClientCriterionIpc::IsXwayland, ClientCriterion::Comm(t) => string!(t, Comm, false), ClientCriterion::CommRegex(t) => string!(t, Comm, true), + ClientCriterion::Exe(t) => string!(t, Exe, false), + ClientCriterion::ExeRegex(t) => string!(t, Exe, true), }; let res = self.send_with_response(&ClientMessage::CreateClientMatcher { criterion }); get_response!( diff --git a/jay-config/src/client.rs b/jay-config/src/client.rs index 6dc7487b..97b8ff39 100644 --- a/jay-config/src/client.rs +++ b/jay-config/src/client.rs @@ -87,6 +87,10 @@ pub enum ClientCriterion<'a> { Comm(&'a str), /// Matches the `/proc/pid/comm` of the client with a regular expression. CommRegex(&'a str), + /// Matches the `/proc/pid/exe` of the client verbatim. + Exe(&'a str), + /// Matches the `/proc/pid/exe` of the client with a regular expression. + ExeRegex(&'a str), } impl ClientCriterion<'_> { diff --git a/src/config/handler.rs b/src/config/handler.rs index 00a9fecd..6758eb01 100644 --- a/src/config/handler.rs +++ b/src/config/handler.rs @@ -1883,6 +1883,7 @@ impl ConfigProxyHandler { mgr.sandbox_instance_id(needle) } ClientCriterionStringField::Comm => mgr.comm(needle), + ClientCriterionStringField::Exe => mgr.exe(needle), } } ClientCriterionIpc::Sandboxed => mgr.sandboxed(), diff --git a/src/criteria/clm.rs b/src/criteria/clm.rs index 802d26f8..57a929cf 100644 --- a/src/criteria/clm.rs +++ b/src/criteria/clm.rs @@ -11,7 +11,7 @@ use { clmm_pid::ClmMatchPid, clmm_sandboxed::ClmMatchSandboxed, clmm_string::{ - ClmMatchComm, ClmMatchSandboxAppId, ClmMatchSandboxEngine, + ClmMatchComm, ClmMatchExe, ClmMatchSandboxAppId, ClmMatchSandboxEngine, ClmMatchSandboxInstanceId, }, clmm_uid::ClmMatchUid, @@ -60,6 +60,7 @@ pub struct RootMatchers { uid: ClmRootMatcherMap, pid: ClmRootMatcherMap, comm: ClmRootMatcherMap, + exe: ClmRootMatcherMap, } pub async fn handle_cl_changes(state: Rc) { @@ -163,6 +164,7 @@ impl ClMatcherManager { unconditional!(uid); unconditional!(pid); unconditional!(comm); + unconditional!(exe); fixed!(sandboxed); fixed!(is_xwayland); self.constant[true].handle(data); @@ -200,6 +202,10 @@ impl ClMatcherManager { pub fn comm(&self, string: CritLiteralOrRegex) -> Rc { self.root(ClmMatchComm::new(string)) } + + pub fn exe(&self, string: CritLiteralOrRegex) -> Rc { + self.root(ClmMatchExe::new(string)) + } } impl CritTarget for Rc { diff --git a/src/criteria/clm/clm_matchers/clmm_string.rs b/src/criteria/clm/clm_matchers/clmm_string.rs index c0369b32..626c5f3d 100644 --- a/src/criteria/clm/clm_matchers/clmm_string.rs +++ b/src/criteria/clm/clm_matchers/clmm_string.rs @@ -16,9 +16,11 @@ pub type ClmMatchSandboxEngine = ClmMatchString>; pub type ClmMatchSandboxInstanceId = ClmMatchString>; pub type ClmMatchComm = ClmMatchString; +pub type ClmMatchExe = ClmMatchString; pub struct AcceptorMetadataAccess(PhantomData); pub struct CommAccess; +pub struct ExeAccess; trait SandboxField: Sized + 'static { fn field(meta: &AcceptorMetadata) -> &Option; @@ -89,3 +91,13 @@ impl StringAccess> for CommAccess { &roots.comm } } + +impl StringAccess> for ExeAccess { + fn with_string(data: &Rc, f: impl FnOnce(&str) -> bool) -> bool { + f(&data.pid_info.exe) + } + + fn nodes(roots: &RootMatchers) -> &ClmRootMatcherMap> { + &roots.exe + } +} diff --git a/src/utils/pid_info.rs b/src/utils/pid_info.rs index d2c672c4..aec37028 100644 --- a/src/utils/pid_info.rs +++ b/src/utils/pid_info.rs @@ -1,6 +1,7 @@ use { crate::utils::{errorfmt::ErrorFmt, oserror::OsError}, bstr::ByteSlice, + std::os::unix::ffi::OsStrExt, uapi::{OwnedFd, c}, }; @@ -8,6 +9,7 @@ pub struct PidInfo { pub uid: c::uid_t, pub pid: c::pid_t, pub comm: String, + pub exe: String, } pub fn get_pid_info(uid: c::uid_t, pid: c::pid_t) -> PidInfo { @@ -18,7 +20,24 @@ pub fn get_pid_info(uid: c::uid_t, pid: c::pid_t) -> PidInfo { "Unknown".to_string() } }; - PidInfo { uid, pid, comm } + let exe = match std::fs::read_link(format!("/proc/{}/exe", pid)) { + Ok(name) => name + .as_os_str() + .as_bytes() + .trim_ascii_end() + .as_bstr() + .to_string(), + Err(e) => { + log::warn!("Could not read `exe` of pid {}: {}", pid, ErrorFmt(e)); + "Unknown".to_string() + } + }; + PidInfo { + uid, + pid, + comm, + exe, + } } pub fn get_socket_creds(socket: &OwnedFd) -> Option<(c::uid_t, c::pid_t)> { diff --git a/toml-config/src/config.rs b/toml-config/src/config.rs index 8f51e220..a19fb0a3 100644 --- a/toml-config/src/config.rs +++ b/toml-config/src/config.rs @@ -237,6 +237,8 @@ pub struct ClientMatch { pub is_xwayland: Option, pub comm: Option, pub comm_regex: Option, + pub exe: Option, + pub exe_regex: Option, } #[derive(Debug, Clone)] diff --git a/toml-config/src/config/parsers/client_match.rs b/toml-config/src/config/parsers/client_match.rs index 852cd9d2..013e7646 100644 --- a/toml-config/src/config/parsers/client_match.rs +++ b/toml-config/src/config/parsers/client_match.rs @@ -57,6 +57,8 @@ impl Parser for ClientMatchParser<'_> { is_xwayland, comm, comm_regex, + exe, + exe_regex, ), ) = ext.extract(( ( @@ -79,6 +81,8 @@ impl Parser for ClientMatchParser<'_> { opt(bol("is-xwayland")), opt(str("comm")), opt(str("comm-regex")), + opt(str("exe")), + opt(str("exe-regex")), ), ))?; let mut not = None; @@ -124,6 +128,8 @@ impl Parser for ClientMatchParser<'_> { is_xwayland: is_xwayland.despan(), comm: comm.despan_into(), comm_regex: comm_regex.despan_into(), + exe: exe.despan_into(), + exe_regex: exe_regex.despan_into(), }) } } diff --git a/toml-config/src/rules.rs b/toml-config/src/rules.rs index c54550ec..468fdb41 100644 --- a/toml-config/src/rules.rs +++ b/toml-config/src/rules.rs @@ -122,6 +122,8 @@ impl Rule for ClientRule { value_ref!(SandboxInstanceIdRegex, sandbox_instance_id_regex); value_ref!(Comm, comm); value_ref!(CommRegex, comm_regex); + value_ref!(Exe, exe); + value_ref!(ExeRegex, exe_regex); value!(Uid, uid); value!(Pid, pid); bool!(Sandboxed, sandboxed); diff --git a/toml-spec/spec/spec.generated.json b/toml-spec/spec/spec.generated.json index 92dcdaa8..cbd4ed2c 100644 --- a/toml-spec/spec/spec.generated.json +++ b/toml-spec/spec/spec.generated.json @@ -579,6 +579,14 @@ "comm-regex": { "type": "string", "description": "Matches the `/proc/pid/comm` of the client with a regular expression." + }, + "exe": { + "type": "string", + "description": "Matches the `/proc/pid/exe` of the client verbatim." + }, + "exe-regex": { + "type": "string", + "description": "Matches the `/proc/pid/exe` of the client with a regular expression." } }, "required": [] diff --git a/toml-spec/spec/spec.generated.md b/toml-spec/spec/spec.generated.md index c7286091..cdf4d275 100644 --- a/toml-spec/spec/spec.generated.md +++ b/toml-spec/spec/spec.generated.md @@ -905,6 +905,18 @@ The table has the following fields: The value of this field should be a string. +- `exe` (optional): + + Matches the `/proc/pid/exe` of the client verbatim. + + The value of this field should be a string. + +- `exe-regex` (optional): + + Matches the `/proc/pid/exe` of the client with a regular expression. + + The value of this field should be a string. + ### `ClientMatchExactly` diff --git a/toml-spec/spec/spec.yaml b/toml-spec/spec/spec.yaml index f124139b..2b3659e5 100644 --- a/toml-spec/spec/spec.yaml +++ b/toml-spec/spec/spec.yaml @@ -3259,6 +3259,14 @@ ClientMatch: kind: string required: false description: Matches the `/proc/pid/comm` of the client with a regular expression. + exe: + kind: string + required: false + description: Matches the `/proc/pid/exe` of the client verbatim. + exe-regex: + kind: string + required: false + description: Matches the `/proc/pid/exe` of the client with a regular expression. ClientMatchExactly: