diff --git a/local/recipes/system/redbear-polkit/source/res/policy.toml b/local/recipes/system/redbear-polkit/source/res/policy.toml new file mode 100644 index 00000000..4c5fbc59 --- /dev/null +++ b/local/recipes/system/redbear-polkit/source/res/policy.toml @@ -0,0 +1,10 @@ +# Red Bear OS polkit policy — action_id = uid1, uid2, ... +# uid 0 (root) is always authorized +org.freedesktop.login1.power-off = 0, 1000 +org.freedesktop.login1.reboot = 0, 1000 +org.freedesktop.login1.suspend = 0, 1000 +org.freedesktop.login1.set-user-linger = 0 +org.freedesktop.udisks2.filesystem-mount = 0, 1000 +org.freedesktop.udisks2.filesystem-mount-system = 0 +org.freedesktop.NetworkManager.settings.modify.system = 0 +org.freedesktop.NetworkManager.enable-disable-wifi = 0, 1000 diff --git a/local/recipes/system/redbear-polkit/source/src/main.rs b/local/recipes/system/redbear-polkit/source/src/main.rs index 0fc75034..873214dd 100644 --- a/local/recipes/system/redbear-polkit/source/src/main.rs +++ b/local/recipes/system/redbear-polkit/source/src/main.rs @@ -1,15 +1,17 @@ -use std::{collections::HashMap, env, error::Error, process, time::Duration}; +use std::{collections::HashMap, env, error::Error, fs, process, time::Duration}; use tokio::runtime::Builder as RuntimeBuilder; use zbus::{ Address, connection::Builder as ConnectionBuilder, + fdo, interface, zvariant::{ObjectPath, OwnedObjectPath}, }; const BUS_NAME: &str = "org.freedesktop.PolicyKit1"; const AUTHORITY_PATH: &str = "/org/freedesktop/PolicyKit1/Authority"; +const POLICY_FILE: &str = "/etc/polkit-1/policy.toml"; type AuthorizationDetails = HashMap; type EnumeratedAction = ( @@ -22,6 +24,33 @@ type EnumeratedAction = ( AuthorizationDetails, ); +/// Returns true if the caller (by UID) is authorized for the given action. +/// uid=0 (root) is always authorized. Other users are checked against policy. +fn is_authorized(uid: u32, action_id: &str) -> bool { + if uid == 0 { + return true; + } + if let Ok(policy) = fs::read_to_string(POLICY_FILE) { + for line in policy.lines() { + let line = line.trim(); + if line.is_empty() || line.starts_with('#') { + continue; + } + if let Some((action, users)) = line.split_once('=') { + if action.trim() == action_id { + for user in users.split(',') { + if let Ok(u) = user.trim().parse::() { + if u == uid { return true; } + } + } + } + } + } + } + // Default: deny for unknown actions from non-root users + false +} + #[derive(Debug, Default)] struct PolicyKitAuthority; @@ -38,6 +67,8 @@ async fn wait_for_dbus_socket() { let socket_path = env::var("DBUS_STARTER_ADDRESS") .ok() .and_then(|addr| addr.strip_prefix("unix:path=").map(String::from)) + .or_else(|| env::var("DBUS_SYSTEM_BUS_ADDRESS").ok() + .and_then(|addr| addr.strip_prefix("unix:path=").map(String::from))) .unwrap_or_else(|| "/run/dbus/system_bus_socket".to_string()); for _ in 0..30 { @@ -70,11 +101,10 @@ fn parse_object_path(path: &str) -> Result> { } fn system_connection_builder() -> Result, Box> { - if let Ok(address) = env::var("DBUS_STARTER_ADDRESS") { - Ok(ConnectionBuilder::address(Address::try_from(address.as_str())?)?) - } else { - Ok(ConnectionBuilder::address(Address::try_from("unix:path=/run/dbus/system_bus_socket")?)?) - } + let addr_str = env::var("DBUS_STARTER_ADDRESS") + .or_else(|_| env::var("DBUS_SYSTEM_BUS_ADDRESS")) + .unwrap_or_else(|_| "unix:path=/run/dbus/system_bus_socket".to_string()); + Ok(ConnectionBuilder::address(Address::try_from(addr_str.as_str())?)?) } fn spawn_signal_handler(shutdown_tx: tokio::sync::watch::Sender) { @@ -104,12 +134,16 @@ impl PolicyKitAuthority { #[zbus(name = "CheckAuthorization")] fn check_authorization( &self, - _action_id: &str, + action_id: &str, _details: AuthorizationDetails, _flags: u32, _cancellation_id: &str, + #[zbus(header)] hdr: zbus::MessageHeader<'_>, ) -> (bool, bool, AuthorizationDetails) { - (true, false, AuthorizationDetails::new()) + // Get caller's UID from D-Bus message credentials + let uid = hdr.unix_uid().unwrap_or(0); + let authorized = is_authorized(uid, action_id); + (authorized, !authorized, AuthorizationDetails::new()) } #[zbus(name = "RegisterAuthenticationAgent")] @@ -136,7 +170,7 @@ impl PolicyKitAuthority { #[zbus(property(emits_changed_signal = "const"), name = "BackendName")] fn backend_name(&self) -> String { - String::from("redbear-permit-all") + String::from("redbear-uid-policy") } #[zbus(property(emits_changed_signal = "const"), name = "BackendVersion")]