use std::{collections::HashMap, io::BufRead};
use enumflags2::{bitflags, BitFlags};
use serde::{Deserialize, Serialize};
use serde_repr::{Deserialize_repr, Serialize_repr};
use static_assertions::assert_impl_all;
use zbus::{
fdo,
names::OwnedUniqueName,
zvariant::{OwnedValue, Type, Value},
};
use crate::Error;
#[bitflags]
#[repr(u32)]
#[derive(Type, Debug, PartialEq, Eq, Copy, Clone, Serialize, Deserialize)]
pub enum CheckAuthorizationFlags {
AllowUserInteraction = 0x01,
}
assert_impl_all!(CheckAuthorizationFlags: Send, Sync, Unpin);
#[repr(u32)]
#[derive(Deserialize_repr, Serialize_repr, Type, Debug, PartialEq, Eq)]
pub enum ImplicitAuthorization {
NotAuthorized = 0,
AuthenticationRequired = 1,
AdministratorAuthenticationRequired = 2,
AuthenticationRequiredRetained = 3,
AdministratorAuthenticationRequiredRetained = 4,
Authorized = 5,
}
assert_impl_all!(ImplicitAuthorization: Send, Sync, Unpin);
#[bitflags]
#[repr(u32)]
#[derive(Type, Debug, PartialEq, Eq, Copy, Clone, Serialize, Deserialize)]
pub enum AuthorityFeatures {
TemporaryAuthorization = 0x01,
}
assert_impl_all!(AuthorityFeatures: Send, Sync, Unpin);
impl TryFrom<OwnedValue> for AuthorityFeatures {
type Error = <u32 as TryFrom<OwnedValue>>::Error;
fn try_from(v: OwnedValue) -> Result<Self, Self::Error> {
Ok(unsafe { std::mem::transmute::<u32, AuthorityFeatures>(v.try_into()?) })
}
}
#[derive(Debug, Type, Deserialize)]
pub struct TemporaryAuthorization {
pub id: String,
pub action_id: String,
pub subject: Subject,
pub time_obtained: u64,
pub time_expires: u64,
}
assert_impl_all!(TemporaryAuthorization: Send, Sync, Unpin);
#[derive(Debug, Type, Serialize)]
pub struct Identity<'a> {
pub identity_kind: &'a str,
pub identity_details: &'a HashMap<&'a str, Value<'a>>,
}
assert_impl_all!(Identity<'_>: Send, Sync, Unpin);
fn pid_start_time(pid: u32) -> Result<u64, Error> {
let fname = format!("/proc/{pid}/stat");
let content = std::fs::read_to_string(fname)?;
if let Some(i) = content.rfind(')') {
if let Some(start_time) = content[i..].split(' ').nth(20) {
return Ok(start_time.parse()?);
}
}
Err(std::io::Error::from(std::io::ErrorKind::NotFound).into())
}
fn pid_uid_racy(pid: u32) -> Result<u32, Error> {
let fname = format!("/proc/{pid}/status");
let file = std::fs::File::open(fname)?;
let lines = std::io::BufReader::new(file).lines();
for line in lines.map_while(Result::ok) {
if line.starts_with("Uid:") {
if let Some(uid) = line.split('\t').nth(1) {
return Ok(uid.parse()?);
}
}
}
Err(std::io::Error::from(std::io::ErrorKind::NotFound).into())
}
#[derive(Debug, Type, Serialize, Deserialize)]
pub struct Subject {
pub subject_kind: String,
pub subject_details: HashMap<String, OwnedValue>,
}
assert_impl_all!(Subject: Send, Sync, Unpin);
impl Subject {
pub fn new_for_owner(
pid: u32,
start_time: Option<u64>,
uid: Option<u32>,
) -> Result<Self, Error> {
let start_time = match start_time {
Some(s) => s,
None => pid_start_time(pid)?,
};
let uid = match uid {
Some(u) => u,
None => pid_uid_racy(pid)?,
};
let mut hashmap = HashMap::new();
hashmap.insert("pid".to_string(), pid.into());
hashmap.insert("start-time".to_string(), start_time.into());
hashmap.insert("uid".to_string(), uid.into());
Ok(Self {
subject_kind: "unix-process".into(),
subject_details: hashmap,
})
}
pub fn new_for_message_header(
message_header: &zbus::message::Header<'_>,
) -> Result<Self, Error> {
let mut subject_details = HashMap::new();
match message_header.sender() {
Some(sender) => {
subject_details.insert(
"name".to_string(),
OwnedUniqueName::from(sender.clone()).try_into().unwrap(),
);
}
None => {
return Err(Error::MissingSender);
}
}
Ok(Self {
subject_kind: "system-bus-name".to_string(),
subject_details,
})
}
}
#[derive(Debug, Type, Serialize, Deserialize)]
pub struct ActionDescription {
pub action_id: String,
pub description: String,
pub message: String,
pub vendor_name: String,
pub vendor_url: String,
pub icon_name: String,
pub implicit_any: ImplicitAuthorization,
pub implicit_inactive: ImplicitAuthorization,
pub implicit_active: ImplicitAuthorization,
pub annotations: HashMap<String, String>,
}
assert_impl_all!(ActionDescription: Send, Sync, Unpin);
#[derive(Debug, Type, Serialize, Deserialize)]
pub struct AuthorizationResult {
pub is_authorized: bool,
pub is_challenge: bool,
pub details: std::collections::HashMap<String, String>,
}
assert_impl_all!(AuthorizationResult: Send, Sync, Unpin);
#[zbus::proxy(
interface = "org.freedesktop.PolicyKit1.Authority",
default_service = "org.freedesktop.PolicyKit1",
default_path = "/org/freedesktop/PolicyKit1/Authority"
)]
pub trait Authority {
fn authentication_agent_response(
&self,
cookie: &str,
identity: &Identity<'_>,
) -> zbus::Result<()>;
fn authentication_agent_response2(
&self,
uid: u32,
cookie: &str,
identity: &Identity<'_>,
) -> zbus::Result<()>;
fn cancel_check_authorization(&self, cancellation_id: &str) -> zbus::Result<()>;
fn check_authorization(
&self,
subject: &Subject,
action_id: &str,
details: &std::collections::HashMap<&str, &str>,
flags: BitFlags<CheckAuthorizationFlags>,
cancellation_id: &str,
) -> zbus::Result<AuthorizationResult>;
fn enumerate_actions(&self, locale: &str) -> zbus::Result<Vec<ActionDescription>>;
fn enumerate_temporary_authorizations(
&self,
subject: &Subject,
) -> zbus::Result<Vec<TemporaryAuthorization>>;
fn register_authentication_agent(
&self,
subject: &Subject,
locale: &str,
object_path: &str,
) -> zbus::Result<()>;
fn register_authentication_agent_with_options(
&self,
subject: &Subject,
locale: &str,
object_path: &str,
options: &std::collections::HashMap<&str, Value<'_>>,
) -> zbus::Result<()>;
fn revoke_temporary_authorization_by_id(&self, id: &str) -> zbus::Result<()>;
fn revoke_temporary_authorizations(&self, subject: &Subject) -> zbus::Result<()>;
fn unregister_authentication_agent(
&self,
subject: &Subject,
object_path: &str,
) -> zbus::Result<()>;
#[zbus(signal)]
fn changed(&self) -> fdo::Result<()>;
#[zbus(property)]
fn backend_features(&self) -> fdo::Result<AuthorityFeatures>;
#[zbus(property)]
fn backend_name(&self) -> fdo::Result<String>;
#[zbus(property)]
fn backend_version(&self) -> fdo::Result<String>;
}
assert_impl_all!(AuthorityProxy<'_>: Send, Sync, Unpin);
#[cfg(feature = "blocking-api")]
assert_impl_all!(AuthorityProxyBlocking<'_>: Send, Sync, Unpin);