Skip to content

Commit 9660d3d

Browse files
committed
dbus: Add NameOwners helper
Allows us to restrict a DBus protocol to only the owners of specific dbus names.
1 parent f2baec1 commit 9660d3d

File tree

2 files changed

+108
-0
lines changed

2 files changed

+108
-0
lines changed

src/dbus/mod.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ use zbus::blocking::{fdo::DBusProxy, Connection};
88

99
#[cfg(feature = "systemd")]
1010
pub mod logind;
11+
mod name_owners;
1112
mod power;
1213

1314
pub fn init(

src/dbus/name_owners.rs

Lines changed: 107 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,107 @@
1+
use futures_executor::ThreadPool;
2+
use futures_util::StreamExt;
3+
use std::{
4+
collections::HashMap,
5+
future::{poll_fn, Future},
6+
sync::{Arc, Mutex, Weak},
7+
task::{Context, Poll, Waker},
8+
};
9+
use zbus::{
10+
fdo,
11+
names::{BusName, UniqueName, WellKnownName},
12+
};
13+
14+
#[derive(Debug)]
15+
struct Inner {
16+
name_owners: HashMap<WellKnownName<'static>, UniqueName<'static>>,
17+
stream: fdo::NameOwnerChangedStream,
18+
// Waker from `update_task` is stored, so that task will still be woken after
19+
// polling elsewhere.
20+
waker: Waker,
21+
enforce: bool,
22+
}
23+
24+
impl Drop for Inner {
25+
fn drop(&mut self) {
26+
// XXX shouldn't wake until Arc has no refs? Race.
27+
self.waker.wake_by_ref();
28+
}
29+
}
30+
31+
impl Inner {
32+
/// Process all events so far on `stream`, and update `name_owners`.
33+
fn update_if_needed(&mut self) {
34+
let mut context = Context::from_waker(&self.waker);
35+
while let Poll::Ready(val) = self.stream.poll_next_unpin(&mut context) {
36+
let val = val.unwrap();
37+
let args = val.args().unwrap();
38+
if let BusName::WellKnown(name) = args.name {
39+
if let Some(owner) = &*args.new_owner {
40+
self.name_owners.insert(name.to_owned(), owner.to_owned());
41+
} else {
42+
self.name_owners.remove(&name.to_owned());
43+
}
44+
}
45+
}
46+
}
47+
}
48+
49+
/// This task polls the steam regularly, to make sure events on the stream aren't just
50+
/// buffered indefinitely if `check_owner` is never called.
51+
fn update_task(inner: Weak<Mutex<Inner>>) -> impl Future<Output = ()> {
52+
poll_fn(move |context| {
53+
if let Some(inner) = inner.upgrade() {
54+
let mut inner = inner.lock().unwrap();
55+
inner.waker = context.waker().clone();
56+
inner.update_if_needed();
57+
// Nothing to do now until waker is invoked
58+
Poll::Pending
59+
} else {
60+
// All strong references have been dropped, so task has nothing left to do.
61+
Poll::Ready(())
62+
}
63+
})
64+
}
65+
66+
/// Track which DBus unique names own which well-known names, so protocols can be restricted to
67+
/// only certain names.
68+
///
69+
/// Enforcement can be disabled by setting `COSMIC_ENFORCE_DBUS_OWNERS`.
70+
#[derive(Clone, Debug)]
71+
pub struct NameOwners(Arc<Mutex<Inner>>);
72+
73+
impl NameOwners {
74+
pub async fn new(connection: &zbus::Connection, executor: &ThreadPool) -> zbus::Result<Self> {
75+
let dbus = fdo::DBusProxy::new(connection).await?;
76+
let stream = dbus.receive_name_owner_changed().await?;
77+
78+
let enforce = crate::utils::env::bool_var("COSMIC_ENFORCE_DBUS_OWNERS").unwrap_or(true);
79+
80+
let inner = Arc::new(Mutex::new(Inner {
81+
name_owners: HashMap::new(),
82+
stream,
83+
waker: Waker::noop().clone(),
84+
enforce,
85+
}));
86+
87+
if enforce {
88+
executor.spawn_ok(update_task(Arc::downgrade(&inner)));
89+
}
90+
91+
Ok(NameOwners(inner))
92+
}
93+
94+
/// Check if the unique name `name` owns at least one of the well-known names in `allowed_names`.
95+
pub fn check_owner(&self, name: &UniqueName<'_>, allowed_names: &[WellKnownName<'_>]) -> bool {
96+
let mut inner = self.0.lock().unwrap();
97+
if !inner.enforce {
98+
return true;
99+
}
100+
// Make sure latest events from stream have been processed
101+
inner.update_if_needed();
102+
103+
allowed_names
104+
.iter()
105+
.any(|n| inner.name_owners.get(n) == Some(name))
106+
}
107+
}

0 commit comments

Comments
 (0)