From 0ea4b47ffa04d086ecde4759402f8346dccf6302 Mon Sep 17 00:00:00 2001 From: Yaroslav Bolyukin Date: Thu, 18 Jun 2026 16:28:24 +0000 Subject: [PATCH] refactor: drop lach.PolkitHelper Polkit127 has introduced /run/polkit/agent-helper.socket instead, which implements the same thing I want. --- --- a/Cargo.lock +++ b/Cargo.lock @@ -560,31 +560,11 @@ [[package]] name = "bindgen" -version = "0.69.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "271383c67ccabffb7381723dea0672a673f292304fcb45c01cc648c7a8d58088" -dependencies = [ - "bitflags 2.13.0", - "cexpr", - "clang-sys", - "itertools 0.12.1", - "lazy_static", - "lazycell", - "proc-macro2", - "quote", - "regex", - "rustc-hash 1.1.0", - "shlex 1.3.0", - "syn 2.0.118", -] - -[[package]] -name = "bindgen" version = "0.72.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "993776b509cfb49c750f11b8f07a46fa23e0a1386ffc01fb1e7d343efc387895" dependencies = [ - "bitflags 2.13.0", + "bitflags", "cexpr", "clang-sys", "itertools 0.13.0", @@ -597,12 +577,6 @@ "shlex 1.3.0", "syn 2.0.118", ] - -[[package]] -name = "bitflags" -version = "1.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" [[package]] name = "bitflags" @@ -1415,7 +1389,7 @@ checksum = "ccc2776f0c61eca1ca32528f85548abd1a4be8fb53d1b21c013e4f18da1e7090" dependencies = [ "data-encoding", - "syn 1.0.109", + "syn 2.0.118", ] [[package]] @@ -1566,7 +1540,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1e0e367e4e7da84520dedcac1901e4da967309406d1e51017ae1abfb97adbd38" dependencies = [ - "bitflags 2.13.0", + "bitflags", "block2", "libc", "objc2", @@ -1707,17 +1681,6 @@ ] [[package]] -name = "enum-repr" -version = "0.2.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bad30c9c0fa1aaf1ae5010dab11f1117b15d35faf62cda4bbbc53b9987950f18" -dependencies = [ - "proc-macro2", - "quote", - "syn 1.0.109", -] - -[[package]] name = "enum_dispatch" version = "0.3.13" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -3125,15 +3088,6 @@ [[package]] name = "itertools" -version = "0.12.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ba291022dbbd398a455acf126c1e341954079855bc60dfdda641363bd6922569" -dependencies = [ - "either", -] - -[[package]] -name = "itertools" version = "0.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "413ee7dfc52ee1a4949ceeb7dbc8a33f2d6c088194d9f922fb8318faf1f01186" @@ -3288,12 +3242,6 @@ dependencies = [ "spin 0.9.8", ] - -[[package]] -name = "lazycell" -version = "1.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55" [[package]] name = "libc" @@ -3323,7 +3271,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f02ab6bace2054fb888a3c16f990117b579d14a3088e472d63c6011fa185c9d3" dependencies = [ - "bitflags 2.13.0", + "bitflags", "libc", "plain", "redox_syscall 0.8.1", @@ -3611,7 +3559,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "df9854ea6ad14e3f4698a7f03b65bce0833dd2d81d594a0e4a984170537146b6" dependencies = [ - "bitflags 2.13.0", + "bitflags", "libc", "log", "netlink-packet-core", @@ -3623,7 +3571,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e2288fcb784eb3defd5fb16f4c4160d5f477de192eac730f43e1d11c24d9a007" dependencies = [ - "bitflags 2.13.0", + "bitflags", "libc", "log", "netlink-packet-core", @@ -3699,7 +3647,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cf20d2fde8ff38632c426f1165ed7436270b44f199fc55284c38276f9db47c3d" dependencies = [ - "bitflags 2.13.0", + "bitflags", "cfg-if", "cfg_aliases", "libc", @@ -3710,7 +3658,7 @@ version = "0.1.9" dependencies = [ "anyhow", - "bindgen 0.72.1", + "bindgen", "camino", "cxx", "cxx-build", @@ -3943,7 +3891,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2a180dd8642fa45cdb7dd721cd4c11b1cadd4929ce112ebd8b9f5803cc79d536" dependencies = [ - "bitflags 2.13.0", + "bitflags", "block2", "dispatch2", "libc", @@ -3956,7 +3904,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c71e34919aba0d701380d911702455038a8a3587467fe0141d6a71501e7ffe48" dependencies = [ - "bitflags 2.13.0", + "bitflags", "objc2", "objc2-core-foundation", "objc2-foundation", @@ -3976,7 +3924,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e3e0adef53c21f888deb4fa59fc59f7eb17404926ee8a6f59f5df0fd7f9f3272" dependencies = [ - "bitflags 2.13.0", + "bitflags", "block2", "libc", "objc2", @@ -3989,7 +3937,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "709fe137109bd1e8b5a99390f77a7d8b2961dafc1a1c5db8f2e60329ad6d895a" dependencies = [ - "bitflags 2.13.0", + "bitflags", "objc2", "objc2-core-foundation", ] @@ -4010,7 +3958,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7216bd11cbda54ccabcab84d523dc93b858ec75ecfb3a7d89513fa22464da396" dependencies = [ - "bitflags 2.13.0", + "bitflags", "dispatch2", "libc", "objc2", @@ -4215,30 +4163,6 @@ "tokio", "windows", "windows-strings", -] - -[[package]] -name = "pam-client" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "51bd776116a7ada5ebbe31f54cdc5b1030ed7265686cf7c8a21c057a2f8dab9a" -dependencies = [ - "bitflags 1.3.2", - "enum-repr", - "libc", - "pam-sys", - "rpassword", - "rustversion", -] - -[[package]] -name = "pam-sys" -version = "1.0.0-alpha5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ce9484729b3e52c0bacdc5191cb6a6a5f31ef4c09c5e4ab1209d3340ad9e997b" -dependencies = [ - "bindgen 0.69.5", - "libc", ] [[package]] @@ -4507,23 +4431,6 @@ "quick-xml", "serde", "time", -] - -[[package]] -name = "polkit-backend" -version = "0.1.9" -dependencies = [ - "anyhow", - "clap", - "nix", - "pam-client", - "remowt-polkit-shared", - "remowt-ui-prompt", - "tokio", - "tracing", - "tracing-subscriber", - "zbus", - "zbus_polkit", ] [[package]] @@ -4761,7 +4668,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4b45fcc2344c680f5025fe57779faef368840d0bd1f42f216291f0dc4ace4744" dependencies = [ - "bitflags 2.13.0", + "bitflags", "num-traits", "rand 0.9.4", "rand_chacha 0.9.0", @@ -4988,7 +4895,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ed2bf2547551a7053d6fdfafda3f938979645c44812fbfcda098faae3f1a362d" dependencies = [ - "bitflags 2.13.0", + "bitflags", ] [[package]] @@ -4997,7 +4904,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5b44b894f2a6e36457d665d1e08c3866add6ed5e70050c1b4ba8a8ddedb02ce7" dependencies = [ - "bitflags 2.13.0", + "bitflags", ] [[package]] @@ -5260,18 +5167,6 @@ "libc", "untrusted", "windows-sys 0.52.0", -] - -[[package]] -name = "rpassword" -version = "6.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2bf099a1888612545b683d2661a1940089f6c2e5a8e38979b2159da876bfd956" -dependencies = [ - "libc", - "serde", - "serde_json", - "winapi", ] [[package]] @@ -5320,7 +5215,7 @@ checksum = "bbf893f64684e58da8a68d56a5e84d1cf0440226274c515770fe267707a7d0b0" dependencies = [ "aes 0.9.1", - "bitflags 2.13.0", + "bitflags", "block-padding 0.4.2", "byteorder", "bytes", @@ -5484,7 +5379,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fdb5bc1ae2baa591800df16c9ca78619bf65c0488b41b96ccec5d11220d8c154" dependencies = [ - "bitflags 2.13.0", + "bitflags", "errno", "libc", "linux-raw-sys 0.4.15", @@ -5497,7 +5392,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b6fe4565b9518b83ef4f91bb47ce29620ca828bd32cb7e408f0062e9930ba190" dependencies = [ - "bitflags 2.13.0", + "bitflags", "errno", "libc", "linux-raw-sys 0.12.1", @@ -5700,7 +5595,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b7f4bc775c73d9a02cde8bf7b2ec4c9d12743edf609006c7facc23998404cd1d" dependencies = [ - "bitflags 2.13.0", + "bitflags", "core-foundation 0.10.1", "core-foundation-sys", "libc", @@ -5956,7 +5851,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7a75cbde1bf934313596a004973e462f9a82caa814dcf1a5f507bdf51597eeb4" dependencies = [ - "bitflags 2.13.0", + "bitflags", ] [[package]] @@ -6184,7 +6079,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a13f3d0daba03132c0aa9767f98351b3488edc2c100cda2d2ec2b04f3d8d3c8b" dependencies = [ - "bitflags 2.13.0", + "bitflags", "core-foundation 0.9.4", "system-configuration-sys", ] @@ -6610,7 +6505,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4cfcf7e2740e6fc6d4d688b4ef00650406bb94adf4731e43c096c3a19fe40840" dependencies = [ - "bitflags 2.13.0", + "bitflags", "bytes", "futures-util", "http", @@ -6953,7 +6848,7 @@ checksum = "a5924018406ce0063cd67f8e008104968b74b563ee1b85dde3ed1f7cb87d3dbd" dependencies = [ "arrayvec", - "bitflags 2.13.0", + "bitflags", "cursor-icon", "log", "memchr", --- a/Cargo.toml +++ b/Cargo.toml @@ -89,7 +89,6 @@ zbus = "5.16.0" zbus_polkit = "5.0.0" tokio-util = "0.7.11" -pam-client = "0.5.0" [profile.dev] panic = "abort" --- a/remowt/cmds/polkit-dbus-helper/Cargo.toml +++ /dev/null @@ -1,19 +0,0 @@ -[package] -name = "polkit-backend" -description = "polkit/PAM D-Bus authentication backend for remowt" -version.workspace = true -edition = "2021" -license.workspace = true - -[dependencies] -anyhow.workspace = true -clap = { workspace = true, features = ["derive"] } -nix.workspace = true -pam-client.workspace = true -remowt-polkit-shared.workspace = true -tokio = { workspace = true, features = ["macros", "rt", "rt-multi-thread"] } -tracing.workspace = true -tracing-subscriber.workspace = true -remowt-ui-prompt.workspace = true -zbus = { workspace = true, features = ["tokio"] } -zbus_polkit = { workspace = true, features = ["tokio"] } --- a/remowt/cmds/polkit-dbus-helper/README.adoc +++ /dev/null @@ -1,8 +0,0 @@ -== What is it? - -Usually, there is a `polkit-agent-helper-1` suid binary installed on the system with polkit package. - -This, however, an alternative to that approach, a system daemon listening for dbus requests, which works -without using suid binaries. - -In future it will provide some additional features. --- a/remowt/cmds/polkit-dbus-helper/etc/systemd/system/remowt-polkit-helper.service +++ /dev/null @@ -1,12 +0,0 @@ -[Unit] -Description=Remowt polkit helper service - -[Service] -Type=dbus -BusName=lach.polkit.helper1 -ExecStart=@libexecdir@/polkit-backend -# TODO: Hardening - -[Install] -WantedBy=multi-user.target -Alias=dbus-lach.polkit.helper1.service --- a/remowt/cmds/polkit-dbus-helper/share/dbus-1/system-services/lach.polkit.helper1.conf +++ /dev/null @@ -1,5 +0,0 @@ -[D-BUS Service] -Name=lach.polkit.helper1 -Exec=/bin/false -User=root -SystemdService=dbus-lach.polkit.helper1.service --- a/remowt/cmds/polkit-dbus-helper/share/dbus-1/system.d/lach.polkit.helper1.conf +++ /dev/null @@ -1,12 +0,0 @@ - - - - - - - - - - - - --- a/remowt/cmds/polkit-dbus-helper/src/main.rs +++ /dev/null @@ -1,236 +0,0 @@ -use std::collections::{HashMap, HashSet}; -use std::ffi::{CStr, CString}; -use std::future::pending; -use std::sync::LazyLock; - -use anyhow::Context as _; -use clap::Parser; -use nix::unistd::{setuid, Uid, User}; -use pam_client::{Context, ConversationHandler, ErrorCode, Flag}; -use remowt_polkit_shared::BackendRequest; -use remowt_ui_prompt::dbus::DbusPrompterProxyBlocking; -use remowt_ui_prompt::BlockingPrompter; -use tokio::task::{block_in_place, spawn_blocking}; -use tracing::trace; -use zbus::fdo; -use zbus::message::Header; -use zbus::zvariant::OwnedValue; -use zbus::{blocking, interface, proxy, Connection}; - -struct Helper { - connection: Connection, - blocking_connection: blocking::Connection, -} - -static ALLOWED_ENVIRONMENT: LazyLock> = LazyLock::new(|| { - [ - // pam ssh agent auth - "SSH_AUTH_SOCK", - // ssh itself provides this when running PAM - "SSH_AUTH_INFO_0", - // contains user which ran sudo - "SUDO_USER", - ] - .into_iter() - .collect() -}); - -struct Conversation

(P); -impl Conversation

{ - fn prompt_inner(&self, echo: bool, prompt: &CStr) -> Result { - trace!("do prompt"); - let out = self - .0 - .prompt_text(echo, &prompt.to_string_lossy(), "PAM prompt request", &[]) - .map_err(|e| { - trace!("prompt error: {e}"); - ErrorCode::CONV_ERR - })?; - CString::new(out).map_err(|_| ErrorCode::CONV_AGAIN) - } - fn text_inner(&self, error: bool, msg: &CStr) { - trace!("do text"); - let msg = msg.to_string_lossy(); - let _ = self.0.display_text(error, &msg, &[]); - } -} -impl ConversationHandler for Conversation

{ - fn prompt_echo_on(&mut self, prompt: &CStr) -> Result { - self.prompt_inner(true, prompt) - } - - fn prompt_echo_off(&mut self, prompt: &CStr) -> Result { - self.prompt_inner(false, prompt) - } - - fn text_info(&mut self, msg: &CStr) { - self.text_inner(false, msg) - } - - fn error_msg(&mut self, msg: &CStr) { - self.text_inner(true, msg) - } - - fn radio_prompt(&mut self, prompt: &CStr) -> Result { - let prompt = prompt.to_string_lossy(); - let result = self - .0 - .prompt_radio(&prompt, "PAM prompt request", &[]) - .map_err(|_| ErrorCode::CONV_ERR)?; - Ok(result) - } -} - -#[proxy( - default_service = "org.freedesktop.DBus", - default_path = "/org/freedesktop/DBus" -)] -trait DBus { - fn get_connection_credentials(&self, body: &str) -> zbus::Result>; -} - -#[interface(name = "lach.PolkitHelper")] -impl Helper { - async fn init_conversation( - &self, - request: BackendRequest, - #[zbus(header)] hdr: Header<'_>, - ) -> fdo::Result<()> { - let Some(sender) = hdr.sender().map(|v| v.to_owned()) else { - trace!("missing sender"); - return Err(fdo::Error::AuthFailed("missing sender".to_owned())); - }; - - let dbus = DBusProxy::new(&self.connection).await?; - - // TOCTOU: sender might be already disconnected, and there might be another - // user with different user id here, but does it matters? - let reply = dbus.get_connection_credentials(&sender).await?; - let connection_uid: u32 = (&reply["UnixUserID"]).try_into().unwrap(); - - let identity = request.identity.clone(); - let blocking_connection = self.blocking_connection.clone(); - let thread_result: fdo::Result<()> = block_in_place(move || { - trace!("find user"); - let Some(identity_uid) = identity.uid() else { - return Err(fdo::Error::AuthFailed("can't process identity".to_owned())); - }; - let user = User::from_uid(identity_uid) - .map_err(|_| fdo::Error::AuthFailed("error querying user".to_owned()))? - .ok_or_else(|| fdo::Error::AuthFailed("uid not found".to_owned()))?; - - let responder = DbusPrompterProxyBlocking::new( - &blocking_connection, - sender, - request.prompter_path, - )?; - let conversation = Conversation(responder); - trace!("run context for {}", &user.name); - let mut ctx = Context::new( - // TODO: Should another scope be used? - "login", - Some(&user.name), - conversation, - ) - .map_err(|_| fdo::Error::Failed("pam context init failed".to_owned()))?; - - trace!("fill env"); - for (k, v) in request.environment { - if k.contains('=') || !ALLOWED_ENVIRONMENT.contains(k.as_str()) { - continue; - } - let _ = ctx.putenv(format!("{k}={v}")); - } - - trace!("authenticate"); - ctx.authenticate(Flag::NONE) - .map_err(|_| fdo::Error::AuthFailed("pam authentication failed".to_owned()))?; - - trace!("acct mgmt"); - ctx.acct_mgmt(Flag::NONE) - .map_err(|_| fdo::Error::AuthFailed("pam acct mgmt failed".to_owned()))?; - - Ok(()) - }); - - thread_result?; - - trace!("respond"); - let proxy = zbus_polkit::policykit1::AuthorityProxy::new(&self.connection).await?; - - let identity_details = request - .identity - .details - .iter() - .map(|(k, v)| (k.as_str(), (**v).try_clone().expect("success"))) - .collect::>(); - proxy - .authentication_agent_response2( - connection_uid, - &request.cookie, - &zbus_polkit::policykit1::Identity { - identity_kind: &request.identity.kind, - identity_details: &identity_details, - }, - ) - .await?; - Ok(()) - } -} - -const OBJ_PATH: &str = "/lach/PolkitHelper"; - -#[derive(Parser)] -struct Opts { - /// Not recommended: start as a session connection, then use escalation - /// to respond to polkit requests. - #[arg(long)] - session: bool, -} - -#[tokio::main] -async fn main() -> anyhow::Result<()> { - tracing_subscriber::fmt::init(); - let opts = Opts::parse(); - let connection = if opts.session { - Connection::session().await - } else { - Connection::system().await - } - .context("failed to open connection")?; - - let session = opts.session; - let blocking_connection: anyhow::Result = spawn_blocking(move || { - Ok(if session { - blocking::Connection::session()? - } else { - blocking::Connection::system()? - }) - }) - .await?; - let blocking_connection = blocking_connection.context("failed to open blocking connection")?; - - if opts.session { - setuid(Uid::from_raw(0)) - .context("polkit-backend needs to be suid if run in session mode")?; - } - - connection - .object_server() - .at( - OBJ_PATH, - Helper { - connection: connection.clone(), - blocking_connection, - }, - ) - .await - .context("failed listen path")?; - - connection - .request_name("lach.polkit.helper1") - .await - .context("failed to request name")?; - - pending().await -} --- a/remowt/cmds/remowt-agent/src/helper/dbus.rs +++ /dev/null @@ -1,81 +0,0 @@ -use std::collections::HashMap; -use std::marker::PhantomData; - -use remowt_polkit_shared::{BackendRequest, Identity}; -use remowt_ui_prompt::dbus::DbusPrompterInterface; -use remowt_ui_prompt::Prompter; -use zbus::Connection; - -use crate::PolkitHelperProxy; - -use super::Helper; - -struct TemporaryPrompterInterface { - connection: Connection, - path: String, - _marker: PhantomData

, -} -impl TemporaryPrompterInterface

{ - async fn new(connection: Connection, prompter: P) -> Self { - let path = format!( - "/remowt/prompters/{}", - uuid::Uuid::new_v4().to_string().replace("-", "_") - ); - let _ = connection - .object_server() - .at(path.clone(), DbusPrompterInterface(prompter)) - .await; - Self { - connection, - path, - _marker: PhantomData, - } - } -} -impl Drop for TemporaryPrompterInterface

{ - fn drop(&mut self) { - // Removal is async because of async RwLock used inside... - // We should not care about its reuse - let connection = self.connection.clone(); - let path = std::mem::take(&mut self.path); - tokio::spawn(async move { - let _ = connection - .object_server() - .remove::, String>(path) - .await; - }); - } -} - -#[derive(Clone)] -pub struct DbusHelper { - connection: Connection, - helper: PolkitHelperProxy<'static>, -} -impl DbusHelper { - pub async fn new(connection: Connection) -> zbus::Result { - let helper = PolkitHelperProxy::new(&connection).await?; - Ok(Self { connection, helper }) - } -} -impl Helper for DbusHelper { - async fn help_me( - &self, - cookie: &str, - prompter: P, - identity: Identity, - ) -> anyhow::Result<()> { - let prompter = TemporaryPrompterInterface::new(self.connection.clone(), prompter).await; - self.helper - .init_conversation( - BackendRequest { - cookie: cookie.to_owned(), - environment: HashMap::new(), - prompter_path: prompter.path.clone(), - identity, - }, // cookie.to_owned(), HashMap::new(), prompter.path.clone() - ) - .await?; - Ok(()) - } -} --- a/remowt/cmds/remowt-agent/src/helper/mod.rs +++ b/remowt/cmds/remowt-agent/src/helper/mod.rs @@ -2,12 +2,10 @@ use remowt_polkit_shared::Identity; use remowt_ui_prompt::Prompter; -mod dbus; mod protocol; mod socket; mod suid; -pub use dbus::DbusHelper; pub use socket::SocketHelper; pub use suid::SuidHelper; --- a/remowt/cmds/remowt-agent/src/main.rs +++ b/remowt/cmds/remowt-agent/src/main.rs @@ -18,7 +18,7 @@ }; use remowt_link_shared::iroh_tunnel::TunnelDialer; use remowt_link_shared::{editor::EditorEndpointsClient, Address, BifConfig}; -use remowt_polkit_shared::{emphasize, BackendRequest, Identity, PidDisplay}; +use remowt_polkit_shared::{emphasize, Identity, PidDisplay}; use remowt_ui_prompt::bifrost::PromptEndpointsClient; use remowt_ui_prompt::rofi::RofiPrompter; use remowt_ui_prompt::{PrependSourcePrompter, Prompter, Source}; @@ -29,7 +29,7 @@ use tracing::{debug, trace}; use zbus::fdo; use zbus::zvariant::{OwnedValue, Str}; -use zbus::{interface, proxy, Connection}; +use zbus::{interface, Connection}; use zbus_polkit::policykit1::Subject; use self::helper::{Helper, SocketHelper, SuidHelper}; @@ -199,15 +199,6 @@ } const OBJ_PATH: &str = "/org/freedesktop/PolicyKit1/AuthenticationAgent"; - -#[proxy( - interface = "lach.PolkitHelper", - default_service = "lach.polkit.helper1", - default_path = "/lach/PolkitHelper" -)] -trait PolkitHelper { - fn init_conversation(&self, request: BackendRequest) -> zbus::Result<()>; -} #[derive(Parser)] enum Opts { --- a/remowt/crates/polkit-shared/src/lib.rs +++ b/remowt/crates/polkit-shared/src/lib.rs @@ -103,11 +103,3 @@ } } } - -#[derive(Serialize, Deserialize, Type, PartialEq, Debug)] -pub struct BackendRequest { - pub cookie: String, - pub environment: HashMap, - pub prompter_path: String, - pub identity: Identity, -} --- a/remowt/crates/remowt-link-shared/Cargo.toml +++ b/remowt/crates/remowt-link-shared/Cargo.toml @@ -9,7 +9,7 @@ anyhow.workspace = true bifrostlink.workspace = true bytes.workspace = true -camino = { workspace = true } +camino = { workspace = true, features = ["serde1"] } serde = { workspace = true, features = ["derive"] } serde_json.workspace = true thiserror.workspace = true --- /dev/null +++ b/remowt/crates/remowt-link-shared/src/gateway.rs @@ -0,0 +1,100 @@ +use std::path::{Path, PathBuf}; + +use anyhow::{anyhow, Context as _}; +use bifrostlink::{Rpc, Rtt}; +use tokio::io::{AsyncReadExt as _, AsyncWriteExt as _}; +use tokio::net::{UnixListener, UnixStream}; +use tracing::{debug, warn}; +use uuid::Uuid; + +use crate::port::child_port; +use crate::{Address, BifConfig}; + +pub const SOCKET_ENV: &str = "REMOWT_AGENT_SOCKET"; + +pub const SOCKET_NAME: &str = "agent.sock"; + +pub fn local_socket() -> anyhow::Result { + let dir = std::env::var_os("XDG_RUNTIME_DIR").context("XDG_RUNTIME_DIR not set")?; + Ok(PathBuf::from(dir).join("remowt-local")) +} + +pub fn socket_path() -> anyhow::Result { + match std::env::var_os(SOCKET_ENV) { + Some(p) => Ok(PathBuf::from(p)), + None => local_socket(), + } +} + +fn peer_tag(addr: &Address) -> u8 { + match addr { + Address::User => 0, + Address::Agent => 1, + Address::AgentPrivileged => 2, + _ => unreachable!(), + } +} +fn untag_peer(tag: u8) -> anyhow::Result

{ + Ok(match tag { + 0 => Address::User, + 1 => Address::Agent, + 2 => Address::AgentPrivileged, + _ => unreachable!(), + }) +} + +pub async fn serve(rpc: Rpc, path: &Path) -> anyhow::Result<()> { + let _ = tokio::fs::remove_file(path).await; + let listener = UnixListener::bind(path) + .with_context(|| format!("binding agent gateway at {}", path.display()))?; + let tag = peer_tag(&rpc.me()); + tokio::spawn(async move { + loop { + let mut stream = match listener.accept().await { + Ok((stream, _)) => stream, + Err(e) => { + warn!("gateway accept failed: {e}"); + continue; + } + }; + let id = Uuid::new_v4().as_u128(); + let mut hello = [0u8; 17]; + hello[0] = tag; + hello[1..].copy_from_slice(&id.to_be_bytes()); + if let Err(e) = stream.write_all(&hello).await { + warn!("gateway handshake failed: {e}"); + continue; + } + debug!("gateway client {id:032x}"); + let (rx, tx) = stream.into_split(); + rpc.add_direct( + Address::Ephemeral(Uuid::from_u128(id)), + child_port(rx, tx), + Rtt(0), + ); + } + }); + Ok(()) +} + +pub async fn connect(path: &Path) -> anyhow::Result> { + let mut stream = UnixStream::connect(path) + .await + .with_context(|| format!("connecting to agent gateway at {}", path.display()))?; + + let mut hello = [0u8; 17]; + stream + .read_exact(&mut hello) + .await + .context("reading gateway handshake")?; + let peer = untag_peer(hello[0])?; + let id = u128::from_be_bytes(hello[1..].try_into().expect("16 bytes")); + + let (rx, tx) = stream.into_split(); + let rpc = Rpc::::new(Address::Ephemeral(Uuid::from_u128(id))); + rpc.add_direct(peer, child_port(rx, tx), Rtt(0)); + rpc.wait_for_connection_to(Address::User) + .await + .map_err(|_| anyhow!("no route to the User through the agent"))?; + Ok(rpc) +} --- a/remowt/crates/remowt-ui-prompt/Cargo.toml +++ b/remowt/crates/remowt-ui-prompt/Cargo.toml @@ -2,19 +2,15 @@ name = "remowt-ui-prompt" description = "Interactive UI prompt endpoint for remowt (D-Bus)" version.workspace = true -edition = "2021" +edition.workspace = true license.workspace = true [dependencies] anyhow.workspace = true bifrostlink.workspace = true bifrostlink-macros.workspace = true +remowt-link-shared.workspace = true serde.workspace = true thiserror.workspace = true tokio = { workspace = true, features = ["io-util", "macros", "process", "rt"] } tracing.workspace = true -zbus = { workspace = true, optional = true } - -[features] -default = ["dbus"] -dbus = ["dep:zbus"] --- a/remowt/crates/remowt-ui-prompt/src/auto.rs +++ b/remowt/crates/remowt-ui-prompt/src/auto.rs @@ -1,45 +1,41 @@ -use anyhow::bail; +use std::path::Path; + +use bifrostlink::declarative::RemoteEndpoints as _; +use remowt_link_shared::{Address, BifConfig, gateway}; use tracing::debug; -use zbus::fdo::DBusProxy; -use zbus::names::BusName; -use crate::dbus::{DbusPrompterProxy, BUS_NAME, PROMPTER_PATH}; +use crate::bifrost::PromptEndpointsClient; use crate::rofi::RofiPrompter; use crate::{Prompter, Result, Source}; pub struct AutoPrompter { - dbus: Option>, + remote: Option>, fallback: RofiPrompter, } impl AutoPrompter { pub async fn new() -> Self { - let dbus = match Self::try_dbus().await { - Ok(p) => Some(p), + let remote = match gateway::local_socket() { + Ok(path) => Self::try_connect(&path).await, Err(e) => { - debug!("dbus prompter unavailable, falling back to rofi: {e}"); + debug!("no local gateway socket, falling back to rofi: {e}"); None } }; Self { - dbus, + remote, fallback: RofiPrompter, } } - async fn try_dbus() -> anyhow::Result> { - let conn = zbus::Connection::session().await?; - let dbus = DBusProxy::new(&conn).await?; - let name = BusName::try_from(BUS_NAME)?; - if !dbus.name_has_owner(name).await? { - bail!("{BUS_NAME} not registered on session bus"); + async fn try_connect(path: &Path) -> Option> { + match gateway::connect(path).await { + Ok(rpc) => Some(PromptEndpointsClient::wrap(rpc.remote(Address::User))), + Err(e) => { + debug!("local prompt agent unavailable, falling back to rofi: {e}"); + None + } } - let proxy = DbusPrompterProxy::builder(&conn) - .destination(BUS_NAME)? - .path(PROMPTER_PATH)? - .build() - .await?; - Ok(proxy) } } @@ -51,8 +47,8 @@ variants: &[&str], source: &[Source], ) -> Result { - if let Some(dbus) = &self.dbus { - return Prompter::prompt_enum(dbus, prompt, description, variants, source).await; + if let Some(remote) = &self.remote { + return Prompter::prompt_enum(remote, prompt, description, variants, source).await; } self.fallback .prompt_enum(prompt, description, variants, source) @@ -66,8 +62,8 @@ description: &str, source: &[Source], ) -> Result { - if let Some(dbus) = &self.dbus { - return Prompter::prompt_text(dbus, echo, prompt, description, source).await; + if let Some(remote) = &self.remote { + return Prompter::prompt_text(remote, echo, prompt, description, source).await; } self.fallback .prompt_text(echo, prompt, description, source) @@ -75,8 +71,8 @@ } async fn display_text(&self, error: bool, description: &str, source: &[Source]) -> Result<()> { - if let Some(dbus) = &self.dbus { - return Prompter::display_text(dbus, error, description, source).await; + if let Some(remote) = &self.remote { + return Prompter::display_text(remote, error, description, source).await; } self.fallback.display_text(error, description, source).await } --- a/remowt/crates/remowt-ui-prompt/src/dbus.rs +++ /dev/null @@ -1,143 +0,0 @@ -use zbus::interface; -use zbus::{fdo, proxy}; - -use crate::Source; -use crate::{BlockingPrompter, Result}; -use crate::{Error, Prompter}; - -pub const BUS_NAME: &str = "lach.RemowtAskpass"; -pub const PROMPTER_PATH: &str = "/lach/Askpass"; - -pub struct DbusPrompterInterface

(pub P); - -#[interface(name = "lach.PolkitInputHandler")] -impl DbusPrompterInterface

{ - async fn prompt_enum( - &self, - prompt: &str, - description: &str, - variants: Vec, - source: Vec, - ) -> fdo::Result { - let variants: Vec<&str> = variants.iter().map(|v| v.as_str()).collect(); - Ok(self - .0 - .prompt_enum(prompt, description, &variants, &source) - .await?) - } - async fn prompt_text( - &self, - echo: bool, - prompt: &str, - description: &str, - source: Vec, - ) -> fdo::Result { - Ok(self - .0 - .prompt_text(echo, prompt, description, &source) - .await?) - } - async fn display_text( - &self, - error: bool, - description: &str, - source: Vec, - ) -> fdo::Result<()> { - Ok(self.0.display_text(error, description, &source).await?) - } -} - -#[proxy(interface = "lach.PolkitInputHandler")] -pub trait DbusPrompter { - async fn prompt_enum( - &self, - prompt: &str, - description: &str, - variants: &[&str], - source: &[Source], - ) -> fdo::Result; - async fn prompt_text( - &self, - echo: bool, - prompt: &str, - description: &str, - source: &[Source], - ) -> fdo::Result; - async fn display_text( - &self, - error: bool, - description: &str, - source: &[Source], - ) -> fdo::Result<()>; -} - -impl Prompter for DbusPrompterProxy<'_> { - async fn prompt_enum( - &self, - prompt: &str, - description: &str, - variants: &[&str], - source: &[Source], - ) -> Result { - Ok(self - .prompt_enum(prompt, description, variants, source) - .await?) - } - - async fn prompt_text( - &self, - echo: bool, - prompt: &str, - description: &str, - source: &[Source], - ) -> Result { - Ok(self.prompt_text(echo, prompt, description, source).await?) - } - - async fn display_text(&self, error: bool, description: &str, source: &[Source]) -> Result<()> { - Ok(self.display_text(error, description, source).await?) - } -} -impl BlockingPrompter for DbusPrompterProxyBlocking<'_> { - fn prompt_enum( - &self, - prompt: &str, - description: &str, - variants: &[&str], - source: &[Source], - ) -> Result { - Ok(self.prompt_enum(prompt, description, variants, source)?) - } - - fn prompt_text( - &self, - echo: bool, - prompt: &str, - description: &str, - source: &[Source], - ) -> Result { - Ok(self.prompt_text(echo, prompt, description, source)?) - } - - fn display_text(&self, error: bool, description: &str, source: &[Source]) -> Result<()> { - Ok(self.display_text(error, description, source)?) - } -} - -impl From for Error { - fn from(value: fdo::Error) -> Self { - if matches!(value, fdo::Error::NoReply(_)) { - return Self::Cancel; - } - Self::InputError(format!("{value}")) - } -} -impl From for fdo::Error { - fn from(value: Error) -> Self { - match value { - Error::Cancel => fdo::Error::NoReply("input was cancelled".to_owned()), - Error::Remote(e) => fdo::Error::NoReply(format!("remote error occured: {e}")), - Error::InputError(e) => fdo::Error::Failed(e), - } - } -} --- a/remowt/crates/remowt-ui-prompt/src/lib.rs +++ b/remowt/crates/remowt-ui-prompt/src/lib.rs @@ -5,7 +5,6 @@ pub mod auto; pub mod bifrost; -pub mod dbus; pub mod rofi; #[derive(thiserror::Error, Debug, serde::Serialize, serde::Deserialize)] @@ -20,7 +19,6 @@ pub type Result = result::Result; -#[cfg_attr(feature = "dbus", derive(zbus::zvariant::Type))] #[derive(serde::Serialize, serde::Deserialize, Clone)] pub struct Source(pub Cow<'static, str>); impl fmt::Display for Source { -- gitstuff