From 40dcf29841e9da56b787b7af0e5ba27d9ce18e0c Mon Sep 17 00:00:00 2001 From: Ilya Zlobintsev Date: Fri, 28 Aug 2026 22:59:50 +0300 Subject: [PATCH] fix: use pidfd for polkit auth if possible, patch zbus_polkit --- Cargo.lock | 3 +-- Cargo.toml | 3 +++ flatpak/generated-sources.json | 31 +++++++++++++++++++++---------- lact-daemon/src/server.rs | 27 +++++++++++++++++++-------- lact-daemon/src/server/handler.rs | 25 +++++++++++++++++++------ 5 files changed, 63 insertions(+), 26 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 747a3589..0a0f2fe2 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4076,8 +4076,7 @@ dependencies = [ [[package]] name = "zbus_polkit" version = "5.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ad23d5c4d198c7e2641b33e6e0d1f866f117408ba66fe80bbe52e289eeb77c52" +source = "git+https://github.com/ilya-zlobintsev/zbus_polkit?branch=fix-uid-type#cebdd40f9f91780816427922e6c8aa41b4d59e66" dependencies = [ "enumflags2", "serde", diff --git a/Cargo.toml b/Cargo.toml index 9f1284cf..c18e1cf5 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -62,3 +62,6 @@ strip = false debug = 1 lto = "thin" codegen-units = 256 + +[patch.crates-io] +zbus_polkit = { git = "https://github.com/ilya-zlobintsev/zbus_polkit", branch = "fix-uid-type" } diff --git a/flatpak/generated-sources.json b/flatpak/generated-sources.json index 374eda0b..e8d06b76 100644 --- a/flatpak/generated-sources.json +++ b/flatpak/generated-sources.json @@ -5,6 +5,12 @@ "commit": "eb47417eede43443f139053479618e96ad32893d", "dest": "flatpak-cargo/git/nvml-wrapper-eb47417" }, + { + "type": "git", + "url": "https://github.com/ilya-zlobintsev/zbus_polkit", + "commit": "cebdd40f9f91780816427922e6c8aa41b4d59e66", + "dest": "flatpak-cargo/git/zbus_polkit-cebdd40" + }, { "type": "archive", "archive-type": "tar-gzip", @@ -2678,7 +2684,7 @@ }, { "type": "inline", - "contents": "[package]\nname = \"nvml-wrapper\"\nversion = \"0.12.1\"\nauthors = [\"Cldfire\"]\ndescription = \"A safe and ergonomic Rust wrapper for the NVIDIA Management Library\"\nreadme = \"../README.md\"\ndocumentation = \"https://docs.rs/nvml-wrapper\"\nrepository = \"https://github.com/Cldfire/nvml-wrapper\"\nlicense = \"MIT OR Apache-2.0\"\nedition = \"2021\"\nrust-version = \"1.60.0\"\nkeywords = [\"nvidia\", \"gpu\", \"managment\", \"monitoring\", \"hardware\"]\ncategories = [\"api-bindings\", \"hardware-support\"]\n\n[features]\ndefault = []\nlegacy-functions = [\"nvml-wrapper-sys/legacy-functions\"]\nserde = [\"dep:serde\", \"dep:serde_derive\", \"bitflags/serde\"]\n\n[dependencies]\nthiserror = \"1.0\"\nbitflags = \"2.4.0\"\nwrapcenum-derive = \"0.4.1\"\nlibloading = \"0.8.1\"\nstatic_assertions = \"1.1\"\n\n[dependencies.serde]\nversion = \"1.0\"\noptional = true\n\n[dependencies.serde_derive]\nversion = \"1.0\"\noptional = true\n\n[dependencies.nvml-wrapper-sys]\nversion = \"0.9.1\"\npath = \"../nvml-wrapper-sys\"\n\n[dev-dependencies]\npretty-bytes = \"0.2\"\n", + "contents": "[package]\nname = \"nvml-wrapper\"\nversion = \"0.12.1\"\nauthors = [ \"Cldfire\",]\ndescription = \"A safe and ergonomic Rust wrapper for the NVIDIA Management Library\"\nreadme = \"../README.md\"\ndocumentation = \"https://docs.rs/nvml-wrapper\"\nrepository = \"https://github.com/Cldfire/nvml-wrapper\"\nlicense = \"MIT OR Apache-2.0\"\nedition = \"2021\"\nrust-version = \"1.60.0\"\nkeywords = [ \"nvidia\", \"gpu\", \"managment\", \"monitoring\", \"hardware\",]\ncategories = [ \"api-bindings\", \"hardware-support\",]\n\n[features]\ndefault = []\nlegacy-functions = [ \"nvml-wrapper-sys/legacy-functions\",]\nserde = [ \"dep:serde\", \"dep:serde_derive\", \"bitflags/serde\",]\n\n[dependencies]\nthiserror = \"1.0\"\nbitflags = \"2.4.0\"\nwrapcenum-derive = \"0.4.1\"\nlibloading = \"0.8.1\"\nstatic_assertions = \"1.1\"\n\n[dev-dependencies]\npretty-bytes = \"0.2\"\n\n[dependencies.serde]\nversion = \"1.0\"\noptional = true\n\n[dependencies.serde_derive]\nversion = \"1.0\"\noptional = true\n\n[dependencies.nvml-wrapper-sys]\nversion = \"0.9.1\"\npath = \"../nvml-wrapper-sys\"\n", "dest": "cargo/vendor/nvml-wrapper", "dest-filename": "Cargo.toml" }, @@ -2696,7 +2702,7 @@ }, { "type": "inline", - "contents": "[package]\nname = \"nvml-wrapper-sys\"\nversion = \"0.9.1\"\nauthors = [\"Cldfire\"]\ndescription = \"Generated bindings to the NVIDIA Management Library.\"\nreadme = \"README.md\"\ndocumentation = \"https://docs.rs/nvml-wrapper-sys\"\nrepository = \"https://github.com/Cldfire/nvml-wrapper\"\nlicense = \"MIT OR Apache-2.0\"\nedition = \"2021\"\nrust-version = \"1.60.0\"\nkeywords = [\"nvidia\", \"gpu\", \"managment\", \"monitoring\", \"hardware\"]\ncategories = [\"external-ffi-bindings\", \"hardware-support\"]\nexclude = [\"nvml.h\"]\n\n[dependencies]\nlibloading = \"0.8.1\"\n\n[features]\ndefault = []\nlegacy-functions = []\n", + "contents": "[package]\nname = \"nvml-wrapper-sys\"\nversion = \"0.9.1\"\nauthors = [ \"Cldfire\",]\ndescription = \"Generated bindings to the NVIDIA Management Library.\"\nreadme = \"README.md\"\ndocumentation = \"https://docs.rs/nvml-wrapper-sys\"\nrepository = \"https://github.com/Cldfire/nvml-wrapper\"\nlicense = \"MIT OR Apache-2.0\"\nedition = \"2021\"\nrust-version = \"1.60.0\"\nkeywords = [ \"nvidia\", \"gpu\", \"managment\", \"monitoring\", \"hardware\",]\ncategories = [ \"external-ffi-bindings\", \"hardware-support\",]\nexclude = [ \"nvml.h\",]\n\n[dependencies]\nlibloading = \"0.8.1\"\n\n[features]\ndefault = []\nlegacy-functions = []\n", "dest": "cargo/vendor/nvml-wrapper-sys", "dest-filename": "Cargo.toml" }, @@ -5229,16 +5235,21 @@ "dest-filename": ".cargo-checksum.json" }, { - "type": "archive", - "archive-type": "tar-gzip", - "url": "https://static.crates.io/crates/zbus_polkit/zbus_polkit-5.0.0.crate", - "sha256": "ad23d5c4d198c7e2641b33e6e0d1f866f117408ba66fe80bbe52e289eeb77c52", - "dest": "cargo/vendor/zbus_polkit-5.0.0" + "type": "shell", + "commands": [ + "cp -r --reflink=auto \"flatpak-cargo/git/zbus_polkit-cebdd40/.\" \"cargo/vendor/zbus_polkit\"" + ] }, { "type": "inline", - "contents": "{\"package\": \"ad23d5c4d198c7e2641b33e6e0d1f866f117408ba66fe80bbe52e289eeb77c52\", \"files\": {}}", - "dest": "cargo/vendor/zbus_polkit-5.0.0", + "contents": "[package]\nname = \"zbus_polkit\"\nversion = \"5.0.0\"\nauthors = [ \"Marc-Andr\u00e9 Lureau \", \"Zeeshan Ali Khan \",]\nedition = \"2021\"\nrust-version = \"1.64\"\ndescription = \"PolicyKit binding\"\nrepository = \"https://github.com/z-galaxy/zbus_polkit/\"\nkeywords = [ \"D-Bus\", \"DBus\", \"IPC\",]\nlicense = \"MIT\"\ncategories = [ \"os::unix-apis\",]\nreadme = \"README.md\"\n\n[features]\ndefault = [ \"zbus/async-io\", \"blocking-api\",]\ntokio = [ \"zbus/tokio\",]\nblocking-api = [ \"zbus/blocking-api\",]\n\n[dependencies]\nserde_repr = \"0.1.19\"\nstatic_assertions = \"1.1.0\"\n\n[dev-dependencies]\nbyteorder = \"1.5.0\"\n\n[dependencies.zbus]\nversion = \"5.0.0\"\ndefault-features = false\n\n[dependencies.serde]\nversion = \"1.0.200\"\nfeatures = [ \"derive\",]\n\n[dependencies.enumflags2]\nversion = \"0.7.9\"\nfeatures = [ \"serde\",]\n\n[dev-dependencies.tokio]\nversion = \"1\"\nfeatures = [ \"macros\", \"rt\",]\n", + "dest": "cargo/vendor/zbus_polkit", + "dest-filename": "Cargo.toml" + }, + { + "type": "inline", + "contents": "{\"package\": null, \"files\": {}}", + "dest": "cargo/vendor/zbus_polkit", "dest-filename": ".cargo-checksum.json" }, { @@ -5399,7 +5410,7 @@ }, { "type": "inline", - "contents": "[source.vendored-sources]\ndirectory = \"cargo/vendor\"\n\n[source.crates-io]\nreplace-with = \"vendored-sources\"\n\n[source.\"https://github.com/rust-nvml/nvml-wrapper\"]\ngit = \"https://github.com/rust-nvml/nvml-wrapper\"\nreplace-with = \"vendored-sources\"\nrev = \"eb47417eede43443f139053479618e96ad32893d\"\n", + "contents": "[source.vendored-sources]\ndirectory = \"cargo/vendor\"\n\n[source.crates-io]\nreplace-with = \"vendored-sources\"\n\n[source.\"https://github.com/rust-nvml/nvml-wrapper\"]\ngit = \"https://github.com/rust-nvml/nvml-wrapper\"\nreplace-with = \"vendored-sources\"\nrev = \"eb47417eede43443f139053479618e96ad32893d\"\n\n[source.\"https://github.com/ilya-zlobintsev/zbus_polkit\"]\ngit = \"https://github.com/ilya-zlobintsev/zbus_polkit\"\nreplace-with = \"vendored-sources\"\nbranch = \"fix-uid-type\"\n", "dest": "cargo", "dest-filename": "config" } diff --git a/lact-daemon/src/server.rs b/lact-daemon/src/server.rs index 29676cc8..f89f2227 100644 --- a/lact-daemon/src/server.rs +++ b/lact-daemon/src/server.rs @@ -12,9 +12,12 @@ use crate::{config::Config, socket, system}; use anyhow::Context; use futures::future::join_all; use lact_schema::{Pong, Request, Response}; -use nix::sys::socket::{getsockopt, sockopt::PeerCredentials}; +use nix::sys::socket::{ + getsockopt, + sockopt::{PeerCredentials, PeerPidfd}, +}; use serde::Serialize; -use std::fmt::Debug; +use std::{fmt::Debug, os::fd::OwnedFd}; use tokio::{ io::{AsyncBufReadExt, AsyncRead, AsyncWrite, AsyncWriteExt, BufReader}, net::{TcpListener, UnixListener}, @@ -79,9 +82,14 @@ impl Server { .inspect_err(|err| warn!("could not get client credentials: {err:#}")) .ok(); + let pid_fd = getsockopt(&stream, PeerPidfd) + .inspect_err(|err| warn!("could not get client pidfd: {err:#}")) + .ok(); + let ctx = ClientContext { pid: client_credentials.map(|creds| creds.pid().cast_unsigned()), uid: client_credentials.map(|creds| creds.uid()), + pid_fd, }; let handler = unix_handler.clone(); tokio::task::spawn_local(async move { @@ -125,10 +133,11 @@ impl Server { } } -#[derive(Clone, Copy, Debug, Default)] +#[derive(Debug, Default)] pub(crate) struct ClientContext { pub pid: Option, pub uid: Option, + pub pid_fd: Option, } #[instrument(level = "debug", skip(stream, handler))] @@ -147,10 +156,12 @@ pub async fn handle_stream( let maybe_request = serde_json::from_str(&buf); let response = match maybe_request { - Ok(request) => match handle_request(request, &handler, &disconnect_notify, ctx).await { - Ok(response) => response, - Err(error) => serde_json::to_vec(&Response::<()>::from(error))?, - }, + Ok(request) => { + match handle_request(request, &handler, &disconnect_notify, &ctx).await { + Ok(response) => response, + Err(error) => serde_json::to_vec(&Response::<()>::from(error))?, + } + } Err(error) => serde_json::to_vec(&Response::<()>::from( anyhow::Error::new(error).context("Failed to deserialize"), ))?, @@ -172,7 +183,7 @@ async fn handle_request<'a>( request: Request<'a>, handler: &'a Handler, disconnect_notify: &std::sync::Arc, - ctx: ClientContext, + ctx: &ClientContext, ) -> anyhow::Result> { match request { Request::Ping => ok_response(ping()), diff --git a/lact-daemon/src/server/handler.rs b/lact-daemon/src/server/handler.rs index fdeaa100..b89d47e8 100644 --- a/lact-daemon/src/server/handler.rs +++ b/lact-daemon/src/server/handler.rs @@ -41,7 +41,10 @@ use std::{ env, fs::{self, File, Permissions}, io::{BufWriter, Cursor, Write}, - os::unix::fs::{MetadataExt, PermissionsExt}, + os::{ + fd::AsFd as _, + unix::fs::{MetadataExt, PermissionsExt}, + }, path::{Path, PathBuf}, rc::Rc, sync::LazyLock, @@ -55,6 +58,7 @@ use tokio::{ time::sleep, }; use tracing::{debug, error, info, trace, warn}; +use zbus::zvariant::Value as ZbusValue; use zbus_polkit::policykit1::{self, AuthorityProxy}; const CONTROLLERS_LOAD_RETRY_ATTEMPTS: u8 = 5; @@ -948,7 +952,7 @@ impl<'a> Handler { &self, name: String, base: ProfileBase, - ctx: ClientContext, + ctx: &ClientContext, ) -> anyhow::Result<()> { { let mut config = self.config.write().await; @@ -1043,7 +1047,7 @@ impl<'a> Handler { name: &str, rule: Option, hooks: ProfileHooks, - ctx: ClientContext, + ctx: &ClientContext, ) -> anyhow::Result<()> { if !hooks.is_empty() { self.check_auth( @@ -1275,16 +1279,25 @@ impl<'a> Handler { &self, action: &str, error_msg: &str, - ctx: ClientContext, + ctx: &ClientContext, ) -> anyhow::Result<()> { let polkit_proxy = self .polkit_proxy .as_ref() .context("Polkit not available, cannot ask for authorization")?; - let pid = ctx.pid.context("No client PID available")?; let uid = ctx.uid.context("No client UID available")?; - let subject = policykit1::Subject::new_for_owner(pid, None, Some(uid))?; + let pid = ctx.pid.context("No client PID available")?; + + let mut subject = policykit1::Subject::new_for_owner(pid, None, Some(uid))?; + + if let Some(pid_fd) = &ctx.pid_fd { + subject.subject_details.insert( + "pidfd".to_owned(), + ZbusValue::Fd(pid_fd.as_fd().into()).try_into()?, + ); + } + let result = polkit_proxy .check_authorization( &subject,