feat: real polkit authorization (replaces permit-all stub)
P5: redbear-polkit now enforces real authorization: - is_authorized(uid, action_id) checks UID-based policy - uid=0 (root) always authorized - Other users checked against /etc/polkit-1/policy.toml - Default: deny for unknown actions (fail-closed) - Backend name changed from 'redbear-permit-all' to 'redbear-uid-policy' - Default policy grants power/network/storage to root+user(1000)
This commit is contained in:
@@ -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
|
||||||
@@ -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 tokio::runtime::Builder as RuntimeBuilder;
|
||||||
use zbus::{
|
use zbus::{
|
||||||
Address,
|
Address,
|
||||||
connection::Builder as ConnectionBuilder,
|
connection::Builder as ConnectionBuilder,
|
||||||
|
fdo,
|
||||||
interface,
|
interface,
|
||||||
zvariant::{ObjectPath, OwnedObjectPath},
|
zvariant::{ObjectPath, OwnedObjectPath},
|
||||||
};
|
};
|
||||||
|
|
||||||
const BUS_NAME: &str = "org.freedesktop.PolicyKit1";
|
const BUS_NAME: &str = "org.freedesktop.PolicyKit1";
|
||||||
const AUTHORITY_PATH: &str = "/org/freedesktop/PolicyKit1/Authority";
|
const AUTHORITY_PATH: &str = "/org/freedesktop/PolicyKit1/Authority";
|
||||||
|
const POLICY_FILE: &str = "/etc/polkit-1/policy.toml";
|
||||||
|
|
||||||
type AuthorizationDetails = HashMap<String, String>;
|
type AuthorizationDetails = HashMap<String, String>;
|
||||||
type EnumeratedAction = (
|
type EnumeratedAction = (
|
||||||
@@ -22,6 +24,33 @@ type EnumeratedAction = (
|
|||||||
AuthorizationDetails,
|
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::<u32>() {
|
||||||
|
if u == uid { return true; }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Default: deny for unknown actions from non-root users
|
||||||
|
false
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug, Default)]
|
#[derive(Debug, Default)]
|
||||||
struct PolicyKitAuthority;
|
struct PolicyKitAuthority;
|
||||||
|
|
||||||
@@ -38,6 +67,8 @@ async fn wait_for_dbus_socket() {
|
|||||||
let socket_path = env::var("DBUS_STARTER_ADDRESS")
|
let socket_path = env::var("DBUS_STARTER_ADDRESS")
|
||||||
.ok()
|
.ok()
|
||||||
.and_then(|addr| addr.strip_prefix("unix:path=").map(String::from))
|
.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());
|
.unwrap_or_else(|| "/run/dbus/system_bus_socket".to_string());
|
||||||
|
|
||||||
for _ in 0..30 {
|
for _ in 0..30 {
|
||||||
@@ -70,11 +101,10 @@ fn parse_object_path(path: &str) -> Result<OwnedObjectPath, Box<dyn Error>> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn system_connection_builder() -> Result<ConnectionBuilder<'static>, Box<dyn Error>> {
|
fn system_connection_builder() -> Result<ConnectionBuilder<'static>, Box<dyn Error>> {
|
||||||
if let Ok(address) = env::var("DBUS_STARTER_ADDRESS") {
|
let addr_str = env::var("DBUS_STARTER_ADDRESS")
|
||||||
Ok(ConnectionBuilder::address(Address::try_from(address.as_str())?)?)
|
.or_else(|_| env::var("DBUS_SYSTEM_BUS_ADDRESS"))
|
||||||
} else {
|
.unwrap_or_else(|_| "unix:path=/run/dbus/system_bus_socket".to_string());
|
||||||
Ok(ConnectionBuilder::address(Address::try_from("unix:path=/run/dbus/system_bus_socket")?)?)
|
Ok(ConnectionBuilder::address(Address::try_from(addr_str.as_str())?)?)
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn spawn_signal_handler(shutdown_tx: tokio::sync::watch::Sender<bool>) {
|
fn spawn_signal_handler(shutdown_tx: tokio::sync::watch::Sender<bool>) {
|
||||||
@@ -104,12 +134,16 @@ impl PolicyKitAuthority {
|
|||||||
#[zbus(name = "CheckAuthorization")]
|
#[zbus(name = "CheckAuthorization")]
|
||||||
fn check_authorization(
|
fn check_authorization(
|
||||||
&self,
|
&self,
|
||||||
_action_id: &str,
|
action_id: &str,
|
||||||
_details: AuthorizationDetails,
|
_details: AuthorizationDetails,
|
||||||
_flags: u32,
|
_flags: u32,
|
||||||
_cancellation_id: &str,
|
_cancellation_id: &str,
|
||||||
|
#[zbus(header)] hdr: zbus::MessageHeader<'_>,
|
||||||
) -> (bool, bool, AuthorizationDetails) {
|
) -> (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")]
|
#[zbus(name = "RegisterAuthenticationAgent")]
|
||||||
@@ -136,7 +170,7 @@ impl PolicyKitAuthority {
|
|||||||
|
|
||||||
#[zbus(property(emits_changed_signal = "const"), name = "BackendName")]
|
#[zbus(property(emits_changed_signal = "const"), name = "BackendName")]
|
||||||
fn backend_name(&self) -> String {
|
fn backend_name(&self) -> String {
|
||||||
String::from("redbear-permit-all")
|
String::from("redbear-uid-policy")
|
||||||
}
|
}
|
||||||
|
|
||||||
#[zbus(property(emits_changed_signal = "const"), name = "BackendVersion")]
|
#[zbus(property(emits_changed_signal = "const"), name = "BackendVersion")]
|
||||||
|
|||||||
Reference in New Issue
Block a user