1
0
Fork 0
forked from wry/wry

clients: use fine-grained capabilities for privileged protocols

This commit is contained in:
Julian Orth 2024-04-23 22:06:29 +02:00
parent e543646944
commit ef53d72ff8
13 changed files with 78 additions and 55 deletions

View file

@ -1,6 +1,7 @@
use {
crate::{
async_engine::SpawnedFuture,
client::ClientCaps,
state::State,
utils::{errorfmt::ErrorFmt, oserror::OsError, xrd::xrd},
},
@ -145,12 +146,16 @@ impl Acceptor {
}
let acc = Rc::new(Acceptor { socket });
let futures = vec![
state
.eng
.spawn(accept(acc.socket.secure.clone(), state.clone(), true)),
state
.eng
.spawn(accept(acc.socket.insecure.clone(), state.clone(), false)),
state.eng.spawn(accept(
acc.socket.secure.clone(),
state.clone(),
ClientCaps::all(),
)),
state.eng.spawn(accept(
acc.socket.insecure.clone(),
state.clone(),
ClientCaps::none(),
)),
];
state.acceptor.set(Some(acc.clone()));
Ok((acc, futures))
@ -166,7 +171,7 @@ impl Acceptor {
}
}
async fn accept(fd: Rc<OwnedFd>, state: Rc<State>, secure: bool) {
async fn accept(fd: Rc<OwnedFd>, state: Rc<State>, caps: ClientCaps) {
loop {
let fd = match state.ring.accept(&fd, c::SOCK_CLOEXEC).await {
Ok(fd) => fd,
@ -176,7 +181,7 @@ async fn accept(fd: Rc<OwnedFd>, state: Rc<State>, secure: bool) {
}
};
let id = state.clients.id();
if let Err(e) = state.clients.spawn(id, &state, fd, secure) {
if let Err(e) = state.clients.spawn(id, &state, fd, caps) {
log::error!("Could not spawn a client: {}", ErrorFmt(e));
break;
}

View file

@ -43,6 +43,18 @@ mod error;
mod objects;
mod tasks;
bitflags! {
ClientCaps: u32;
CAP_DATA_CONTROL_MANAGER = 1 << 0,
CAP_VIRTUAL_KEYBOARD_MANAGER = 1 << 1,
CAP_FOREIGN_TOPLEVEL_LIST = 1 << 2,
CAP_IDLE_NOTIFIER = 1 << 3,
CAP_SESSION_LOCK_MANAGER = 1 << 4,
CAP_JAY_COMPOSITOR = 1 << 5,
CAP_LAYER_SHELL = 1 << 6,
CAP_SCREENCOPY_MANAGER = 1 << 7,
}
#[derive(Debug, Copy, Clone, Hash, Ord, PartialOrd, Eq, PartialEq)]
pub struct ClientId(u64);
@ -101,7 +113,7 @@ impl Clients {
id: ClientId,
global: &Rc<State>,
socket: Rc<OwnedFd>,
secure: bool,
caps: ClientCaps,
) -> Result<(), ClientError> {
let (uid, pid) = {
let mut cred = c::ucred {
@ -120,7 +132,7 @@ impl Clients {
}
}
};
self.spawn2(id, global, socket, uid, pid, secure, false)?;
self.spawn2(id, global, socket, uid, pid, caps, false)?;
Ok(())
}
@ -131,7 +143,7 @@ impl Clients {
socket: Rc<OwnedFd>,
uid: c::uid_t,
pid: c::pid_t,
secure: bool,
caps: ClientCaps,
is_xwayland: bool,
) -> Result<Rc<Client>, ClientError> {
let data = Rc::new(Client {
@ -145,7 +157,7 @@ impl Clients {
shutdown: Default::default(),
tracker: Default::default(),
is_xwayland,
secure,
caps,
last_enter_serial: Cell::new(0),
pid_info: get_pid_info(uid, pid),
serials: Default::default(),
@ -165,13 +177,13 @@ impl Clients {
data: data.clone(),
};
log::info!(
"Client {} connected, pid: {}, uid: {}, fd: {}, secure: {}, comm: {:?}",
"Client {} connected, pid: {}, uid: {}, fd: {}, comm: {:?}, caps: {:?}",
id,
pid,
uid,
client.data.socket.raw(),
secure,
data.pid_info.comm,
caps,
);
self.clients.borrow_mut().insert(client.data.id, client);
Ok(data)
@ -193,13 +205,15 @@ impl Clients {
}
}
pub fn broadcast<B>(&self, secure: bool, xwayland_only: bool, mut f: B)
pub fn broadcast<B>(&self, required_caps: ClientCaps, xwayland_only: bool, mut f: B)
where
B: FnMut(&Rc<Client>),
{
let clients = self.clients.borrow();
for client in clients.values() {
if (!secure || client.data.secure) && (!xwayland_only || client.data.is_xwayland) {
if client.data.caps.contains(required_caps)
&& (!xwayland_only || client.data.is_xwayland)
{
f(&client.data);
}
}
@ -258,7 +272,7 @@ pub struct Client {
shutdown: AsyncEvent,
pub tracker: Tracker<Client>,
pub is_xwayland: bool,
pub secure: bool,
pub caps: ClientCaps,
pub last_enter_serial: Cell<u32>,
pub pid_info: PidInfo,
pub serials: RefCell<VecDeque<SerialRange>>,

View file

@ -1,7 +1,7 @@
use {
crate::{
backend::Backend,
client::Client,
client::{Client, ClientCaps},
ifs::{
ext_foreign_toplevel_list_v1::ExtForeignToplevelListV1Global,
ext_idle_notifier_v1::ExtIdleNotifierV1Global,
@ -113,8 +113,8 @@ pub trait Global: GlobalBase {
fn singleton(&self) -> bool;
fn version(&self) -> u32;
fn break_loops(&self) {}
fn secure(&self) -> bool {
false
fn required_caps(&self) -> ClientCaps {
ClientCaps::none()
}
fn xwayland_only(&self) -> bool {
false
@ -215,7 +215,7 @@ impl Globals {
fn insert(&self, state: &State, global: Rc<dyn Global>) {
self.insert_no_broadcast_(&global);
self.broadcast(state, global.secure(), global.xwayland_only(), |r| {
self.broadcast(state, global.required_caps(), global.xwayland_only(), |r| {
r.send_global(&global)
});
}
@ -223,11 +223,13 @@ impl Globals {
pub fn get(
&self,
name: GlobalName,
allow_secure: bool,
client_caps: ClientCaps,
allow_xwayland_only: bool,
) -> Result<Rc<dyn Global>, GlobalsError> {
let global = self.take(name, false)?;
if (global.secure() && !allow_secure) || (global.xwayland_only() && !allow_xwayland_only) {
if client_caps.not_contains(global.required_caps())
|| (global.xwayland_only() && !allow_xwayland_only)
{
return Err(GlobalsError::GlobalDoesNotExist(name));
}
Ok(global)
@ -236,7 +238,7 @@ impl Globals {
pub fn remove<T: WaylandGlobal>(&self, state: &State, global: &T) -> Result<(), GlobalsError> {
let _global = self.take(global.name(), true)?;
global.remove(self);
self.broadcast(state, global.secure(), global.xwayland_only(), |r| {
self.broadcast(state, global.required_caps(), global.xwayland_only(), |r| {
r.send_global_remove(global.name())
});
Ok(())
@ -247,14 +249,16 @@ impl Globals {
}
pub fn notify_all(&self, registry: &Rc<WlRegistry>) {
let secure = registry.client.secure;
let caps = registry.client.caps;
let xwayland = registry.client.is_xwayland;
let globals = self.registry.lock();
macro_rules! emit {
($singleton:expr) => {
for global in globals.values() {
if global.singleton() == $singleton {
if (secure || !global.secure()) && (xwayland || !global.xwayland_only()) {
if caps.contains(global.required_caps())
&& (xwayland || !global.xwayland_only())
{
registry.send_global(global);
}
}
@ -268,11 +272,11 @@ impl Globals {
fn broadcast<F: Fn(&Rc<WlRegistry>)>(
&self,
state: &State,
secure: bool,
required_caps: ClientCaps,
xwayland_only: bool,
f: F,
) {
state.clients.broadcast(secure, xwayland_only, |c| {
state.clients.broadcast(required_caps, xwayland_only, |c| {
let registries = c.lock_registries();
for registry in registries.values() {
f(registry);

View file

@ -1,6 +1,6 @@
use {
crate::{
client::{Client, ClientError},
client::{Client, ClientCaps, ClientError, CAP_FOREIGN_TOPLEVEL_LIST},
globals::{Global, GlobalName},
ifs::{
ext_foreign_toplevel_handle_v1::ExtForeignToplevelHandleV1,
@ -145,8 +145,8 @@ impl Global for ExtForeignToplevelListV1Global {
1
}
fn secure(&self) -> bool {
true
fn required_caps(&self) -> ClientCaps {
CAP_FOREIGN_TOPLEVEL_LIST
}
}

View file

@ -1,6 +1,6 @@
use {
crate::{
client::{Client, ClientError},
client::{Client, ClientCaps, ClientError, CAP_IDLE_NOTIFIER},
globals::{Global, GlobalName},
ifs::ext_idle_notification_v1::ExtIdleNotificationV1,
leaks::Tracker,
@ -117,8 +117,8 @@ impl Global for ExtIdleNotifierV1Global {
1
}
fn secure(&self) -> bool {
true
fn required_caps(&self) -> ClientCaps {
CAP_IDLE_NOTIFIER
}
}

View file

@ -1,6 +1,6 @@
use {
crate::{
client::{Client, ClientError},
client::{Client, ClientCaps, ClientError, CAP_SESSION_LOCK_MANAGER},
globals::{Global, GlobalName},
ifs::ext_session_lock_v1::ExtSessionLockV1,
leaks::Tracker,
@ -98,8 +98,8 @@ impl Global for ExtSessionLockManagerV1Global {
1
}
fn secure(&self) -> bool {
true
fn required_caps(&self) -> ClientCaps {
CAP_SESSION_LOCK_MANAGER
}
}

View file

@ -1,6 +1,6 @@
use {
crate::{
client::{Client, ClientError},
client::{Client, ClientCaps, ClientError, CAP_DATA_CONTROL_MANAGER},
globals::{Global, GlobalName},
ifs::ipc::{
zwlr_data_control_device_v1::{ZwlrDataControlDeviceV1, PRIMARY_SELECTION_SINCE},
@ -111,8 +111,8 @@ impl Global for ZwlrDataControlManagerV1Global {
2
}
fn secure(&self) -> bool {
true
fn required_caps(&self) -> ClientCaps {
CAP_DATA_CONTROL_MANAGER
}
}

View file

@ -1,7 +1,7 @@
use {
crate::{
cli::CliLogLevel,
client::{Client, ClientError},
client::{Client, ClientCaps, ClientError, CAP_JAY_COMPOSITOR},
globals::{Global, GlobalName},
ifs::{
jay_idle::JayIdle,
@ -68,8 +68,8 @@ impl Global for JayCompositorGlobal {
1
}
fn secure(&self) -> bool {
true
fn required_caps(&self) -> ClientCaps {
CAP_JAY_COMPOSITOR
}
}

View file

@ -48,7 +48,7 @@ impl WlRegistryRequestHandler for WlRegistry {
fn bind(&self, bind: Bind, _slf: &Rc<Self>) -> Result<(), Self::Error> {
let name = GlobalName::from_raw(bind.name);
let globals = &self.client.state.globals;
let global = globals.get(name, self.client.secure, self.client.is_xwayland)?;
let global = globals.get(name, self.client.caps, self.client.is_xwayland)?;
if global.interface().name() != bind.interface {
return Err(WlRegistryError::InvalidInterface(InterfaceError {
name: global.name(),

View file

@ -1,6 +1,6 @@
use {
crate::{
client::{Client, ClientError},
client::{Client, ClientCaps, ClientError, CAP_VIRTUAL_KEYBOARD_MANAGER},
globals::{Global, GlobalName},
ifs::wl_seat::zwp_virtual_keyboard_v1::ZwpVirtualKeyboardV1,
leaks::Tracker,
@ -61,8 +61,8 @@ impl Global for ZwpVirtualKeyboardManagerV1Global {
1
}
fn secure(&self) -> bool {
true
fn required_caps(&self) -> ClientCaps {
CAP_VIRTUAL_KEYBOARD_MANAGER
}
}

View file

@ -1,6 +1,6 @@
use {
crate::{
client::{Client, ClientError},
client::{Client, ClientCaps, ClientError, CAP_LAYER_SHELL},
globals::{Global, GlobalName},
ifs::wl_surface::zwlr_layer_surface_v1::{ZwlrLayerSurfaceV1, ZwlrLayerSurfaceV1Error},
leaks::Tracker,
@ -110,8 +110,8 @@ impl Global for ZwlrLayerShellV1Global {
4
}
fn secure(&self) -> bool {
true
fn required_caps(&self) -> ClientCaps {
CAP_LAYER_SHELL
}
}

View file

@ -1,6 +1,6 @@
use {
crate::{
client::{Client, ClientError},
client::{Client, ClientCaps, ClientError, CAP_SCREENCOPY_MANAGER},
globals::{Global, GlobalName},
ifs::zwlr_screencopy_frame_v1::ZwlrScreencopyFrameV1,
leaks::Tracker,
@ -59,8 +59,8 @@ impl Global for ZwlrScreencopyManagerV1Global {
3
}
fn secure(&self) -> bool {
true
fn required_caps(&self) -> ClientCaps {
CAP_SCREENCOPY_MANAGER
}
}

View file

@ -3,7 +3,7 @@ mod xwm;
use {
crate::{
client::ClientError,
client::{ClientCaps, ClientError},
compositor::DISPLAY,
forker::{ForkerError, ForkerProxy},
ifs::{
@ -171,7 +171,7 @@ async fn run(
Rc::new(client1),
uapi::getuid(),
pid,
true,
ClientCaps::all(),
true,
);
let client = match client {