From ef53d72ff873ea7d900aedd00a57c2509573e8ad Mon Sep 17 00:00:00 2001 From: Julian Orth Date: Tue, 23 Apr 2024 22:06:29 +0200 Subject: [PATCH 1/3] clients: use fine-grained capabilities for privileged protocols --- src/acceptor.rs | 21 +++++++----- src/client.rs | 32 +++++++++++++------ src/globals.rs | 26 ++++++++------- src/ifs/ext_foreign_toplevel_list_v1.rs | 6 ++-- src/ifs/ext_idle_notifier_v1.rs | 6 ++-- src/ifs/ext_session_lock_manager_v1.rs | 6 ++-- src/ifs/ipc/zwlr_data_control_manager_v1.rs | 6 ++-- src/ifs/jay_compositor.rs | 6 ++-- src/ifs/wl_registry.rs | 2 +- .../zwp_virtual_keyboard_manager_v1.rs | 6 ++-- src/ifs/zwlr_layer_shell_v1.rs | 6 ++-- src/ifs/zwlr_screencopy_manager_v1.rs | 6 ++-- src/xwayland.rs | 4 +-- 13 files changed, 78 insertions(+), 55 deletions(-) diff --git a/src/acceptor.rs b/src/acceptor.rs index f4ae3b21..e85871e6 100644 --- a/src/acceptor.rs +++ b/src/acceptor.rs @@ -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, state: Rc, secure: bool) { +async fn accept(fd: Rc, state: Rc, 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, state: Rc, 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; } diff --git a/src/client.rs b/src/client.rs index e0f2da25..26aafa66 100644 --- a/src/client.rs +++ b/src/client.rs @@ -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, socket: Rc, - 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, uid: c::uid_t, pid: c::pid_t, - secure: bool, + caps: ClientCaps, is_xwayland: bool, ) -> Result, 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(&self, secure: bool, xwayland_only: bool, mut f: B) + pub fn broadcast(&self, required_caps: ClientCaps, xwayland_only: bool, mut f: B) where B: FnMut(&Rc), { 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, pub is_xwayland: bool, - pub secure: bool, + pub caps: ClientCaps, pub last_enter_serial: Cell, pub pid_info: PidInfo, pub serials: RefCell>, diff --git a/src/globals.rs b/src/globals.rs index 6e3aa850..7c344ab8 100644 --- a/src/globals.rs +++ b/src/globals.rs @@ -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) { 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, 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(&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) { - 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)>( &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); diff --git a/src/ifs/ext_foreign_toplevel_list_v1.rs b/src/ifs/ext_foreign_toplevel_list_v1.rs index 8e21c486..352ebb9d 100644 --- a/src/ifs/ext_foreign_toplevel_list_v1.rs +++ b/src/ifs/ext_foreign_toplevel_list_v1.rs @@ -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 } } diff --git a/src/ifs/ext_idle_notifier_v1.rs b/src/ifs/ext_idle_notifier_v1.rs index 6cbade69..ed29440e 100644 --- a/src/ifs/ext_idle_notifier_v1.rs +++ b/src/ifs/ext_idle_notifier_v1.rs @@ -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 } } diff --git a/src/ifs/ext_session_lock_manager_v1.rs b/src/ifs/ext_session_lock_manager_v1.rs index 85b14a8c..b2981ddf 100644 --- a/src/ifs/ext_session_lock_manager_v1.rs +++ b/src/ifs/ext_session_lock_manager_v1.rs @@ -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 } } diff --git a/src/ifs/ipc/zwlr_data_control_manager_v1.rs b/src/ifs/ipc/zwlr_data_control_manager_v1.rs index 87aa1f5f..ad5ce34f 100644 --- a/src/ifs/ipc/zwlr_data_control_manager_v1.rs +++ b/src/ifs/ipc/zwlr_data_control_manager_v1.rs @@ -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 } } diff --git a/src/ifs/jay_compositor.rs b/src/ifs/jay_compositor.rs index a28d0b05..d98580de 100644 --- a/src/ifs/jay_compositor.rs +++ b/src/ifs/jay_compositor.rs @@ -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 } } diff --git a/src/ifs/wl_registry.rs b/src/ifs/wl_registry.rs index 7c91713c..b1457c41 100644 --- a/src/ifs/wl_registry.rs +++ b/src/ifs/wl_registry.rs @@ -48,7 +48,7 @@ impl WlRegistryRequestHandler for WlRegistry { fn bind(&self, bind: Bind, _slf: &Rc) -> 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(), diff --git a/src/ifs/wl_seat/zwp_virtual_keyboard_manager_v1.rs b/src/ifs/wl_seat/zwp_virtual_keyboard_manager_v1.rs index 717666ca..54027dbf 100644 --- a/src/ifs/wl_seat/zwp_virtual_keyboard_manager_v1.rs +++ b/src/ifs/wl_seat/zwp_virtual_keyboard_manager_v1.rs @@ -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 } } diff --git a/src/ifs/zwlr_layer_shell_v1.rs b/src/ifs/zwlr_layer_shell_v1.rs index 7e8ca7c2..7391cea0 100644 --- a/src/ifs/zwlr_layer_shell_v1.rs +++ b/src/ifs/zwlr_layer_shell_v1.rs @@ -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 } } diff --git a/src/ifs/zwlr_screencopy_manager_v1.rs b/src/ifs/zwlr_screencopy_manager_v1.rs index 866c3691..5df2a52f 100644 --- a/src/ifs/zwlr_screencopy_manager_v1.rs +++ b/src/ifs/zwlr_screencopy_manager_v1.rs @@ -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 } } diff --git a/src/xwayland.rs b/src/xwayland.rs index 79e5a398..c734a380 100644 --- a/src/xwayland.rs +++ b/src/xwayland.rs @@ -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 { From 886339ff967d36869f0537bf9693fa02a06f9453 Mon Sep 17 00:00:00 2001 From: Julian Orth Date: Tue, 23 Apr 2024 22:10:51 +0200 Subject: [PATCH 2/3] clients: grant layer-shell to unprivileged clients --- docs/config.md | 6 +-- docs/features.md | 77 +++++++++++++++-------------- src/acceptor.rs | 4 +- toml-config/src/default-config.toml | 4 +- toml-spec/spec/spec.generated.json | 2 +- toml-spec/spec/spec.generated.md | 4 +- toml-spec/spec/spec.yaml | 4 +- 7 files changed, 51 insertions(+), 50 deletions(-) diff --git a/docs/config.md b/docs/config.md index c2f21096..e318da5e 100644 --- a/docs/config.md +++ b/docs/config.md @@ -32,7 +32,7 @@ keymap = """ """ # An action that will be executed when the GPU has been initialized. -on-graphics-initialized = { type = "exec", exec = { prog = "mako", privileged = true } } +on-graphics-initialized = { type = "exec", exec = "mako" } # Shortcuts that are processed by the compositor. # The left hand side should be a key, possibly prefixed with modifiers. @@ -79,7 +79,7 @@ alt-shift-f = "toggle-floating" # For example, the exec action spawns an application and has the exec field # that describes how to spawn the application. Super_L = { type = "exec", exec = "alacritty" } -alt-p = { type = "exec", exec = { prog = "bemenu-run", privileged = true } } +alt-p = { type = "exec", exec = "bemenu-run" } # The quit action terminates the compositor. alt-q = "quit" @@ -266,7 +266,7 @@ If you want to run an action at startup, you can use the top-level `on-graphics- field: ```toml -on-graphics-initialized = { type = "exec", exec = { prog = "mako", privileged = true } } +on-graphics-initialized = { type = "exec", exec = "mako" } ``` ### Setting Environment Variables diff --git a/docs/features.md b/docs/features.md index 797b6832..c280eafb 100644 --- a/docs/features.md +++ b/docs/features.md @@ -122,48 +122,49 @@ Jay's shortcut system allows you to execute an action when a key is pressed and Jay supports the following wayland protocols: -| Global | Version | Privileged | -|-----------------------------------------|:-----------------|------------| -| ext_foreign_toplevel_list_v1 | 1 | Yes | -| ext_idle_notifier_v1 | 1 | Yes | -| ext_session_lock_manager_v1 | 1 | Yes | -| org_kde_kwin_server_decoration_manager | 1 | | -| wl_compositor | 6[^no_touch] | | -| wl_data_device_manager | 3 | | -| wl_drm | 2 | | -| wl_output | 4 | | -| wl_seat | 9 | | -| wl_shm | 2 | | -| wl_subcompositor | 1 | | -| wp_alpha_modifier_v1 | 1 | | -| wp_content_type_manager_v1 | 1 | | -| wp_cursor_shape_manager_v1 | 1 | | -| wp_fractional_scale_manager_v1 | 1 | | -| wp_linux_drm_syncobj_manager_v1 | 1 | | -| wp_presentation | 1 | | -| wp_single_pixel_buffer_manager_v1 | 1 | | -| wp_tearing_control_manager_v1 | 1[^no_tearing] | | -| wp_viewporter | 1 | | -| xdg_activation_v1 | 1 | | -| xdg_toplevel_drag_manager_v1 | 1 | | -| xdg_wm_base | 6 | | -| zwlr_data_control_manager_v1 | 2 | Yes | -| zwlr_layer_shell_v1 | 4[^no_exclusive] | Yes | -| zwlr_screencopy_manager_v1 | 3 | Yes | -| zwp_idle_inhibit_manager_v1 | 1 | | -| zwp_input_method_manager_v2 | 1 | Yes | -| zwp_linux_dmabuf_v1 | 5 | | -| zwp_pointer_constraints_v1 | 1 | | -| zwp_primary_selection_device_manager_v1 | 1 | | -| zwp_relative_pointer_manager_v1 | 1 | | -| zwp_text_input_manager_v3 | 1 | | -| zwp_virtual_keyboard_manager_v1 | 1 | Yes | -| zxdg_decoration_manager_v1 | 1 | | -| zxdg_output_manager_v1 | 3 | | +| Global | Version | Privileged | +|-----------------------------------------|:-----------------|---------------| +| ext_foreign_toplevel_list_v1 | 1 | Yes | +| ext_idle_notifier_v1 | 1 | Yes | +| ext_session_lock_manager_v1 | 1 | Yes | +| org_kde_kwin_server_decoration_manager | 1 | | +| wl_compositor | 6[^no_touch] | | +| wl_data_device_manager | 3 | | +| wl_drm | 2 | | +| wl_output | 4 | | +| wl_seat | 9 | | +| wl_shm | 2 | | +| wl_subcompositor | 1 | | +| wp_alpha_modifier_v1 | 1 | | +| wp_content_type_manager_v1 | 1 | | +| wp_cursor_shape_manager_v1 | 1 | | +| wp_fractional_scale_manager_v1 | 1 | | +| wp_linux_drm_syncobj_manager_v1 | 1 | | +| wp_presentation | 1 | | +| wp_single_pixel_buffer_manager_v1 | 1 | | +| wp_tearing_control_manager_v1 | 1[^no_tearing] | | +| wp_viewporter | 1 | | +| xdg_activation_v1 | 1 | | +| xdg_toplevel_drag_manager_v1 | 1 | | +| xdg_wm_base | 6 | | +| zwlr_data_control_manager_v1 | 2 | Yes | +| zwlr_layer_shell_v1 | 4[^no_exclusive] | No[^lsaccess] | +| zwlr_screencopy_manager_v1 | 3 | Yes | +| zwp_idle_inhibit_manager_v1 | 1 | | +| zwp_input_method_manager_v2 | 1 | Yes | +| zwp_linux_dmabuf_v1 | 5 | | +| zwp_pointer_constraints_v1 | 1 | | +| zwp_primary_selection_device_manager_v1 | 1 | | +| zwp_relative_pointer_manager_v1 | 1 | | +| zwp_text_input_manager_v3 | 1 | | +| zwp_virtual_keyboard_manager_v1 | 1 | Yes | +| zxdg_decoration_manager_v1 | 1 | | +| zxdg_output_manager_v1 | 3 | | [^no_touch]: Touch input is not supported. [^no_tearing]: Tearing screen updates are not supported. [^no_exclusive]: Exclusive zones are not supported. +[^lsaccess]: Sandboxes can restrict access to this protocol. ## Missing Features diff --git a/src/acceptor.rs b/src/acceptor.rs index e85871e6..b359d773 100644 --- a/src/acceptor.rs +++ b/src/acceptor.rs @@ -1,7 +1,7 @@ use { crate::{ async_engine::SpawnedFuture, - client::ClientCaps, + client::{ClientCaps, CAP_LAYER_SHELL}, state::State, utils::{errorfmt::ErrorFmt, oserror::OsError, xrd::xrd}, }, @@ -154,7 +154,7 @@ impl Acceptor { state.eng.spawn(accept( acc.socket.insecure.clone(), state.clone(), - ClientCaps::none(), + CAP_LAYER_SHELL, )), ]; state.acceptor.set(Some(acc.clone())); diff --git a/toml-config/src/default-config.toml b/toml-config/src/default-config.toml index 496264d5..1b65db26 100644 --- a/toml-config/src/default-config.toml +++ b/toml-config/src/default-config.toml @@ -7,7 +7,7 @@ keymap = """ }; """ -on-graphics-initialized = { type = "exec", exec = { prog = "mako", privileged = true } } +on-graphics-initialized = { type = "exec", exec = "mako" } [shortcuts] alt-h = "focus-left" @@ -31,7 +31,7 @@ alt-f = "focus-parent" alt-shift-c = "close" alt-shift-f = "toggle-floating" Super_L = { type = "exec", exec = "alacritty" } -alt-p = { type = "exec", exec = { prog = "bemenu-run", privileged = true } } +alt-p = { type = "exec", exec = "bemenu-run" } alt-q = "quit" alt-shift-r = "reload-config-toml" diff --git a/toml-spec/spec/spec.generated.json b/toml-spec/spec/spec.generated.json index 2f194626..ebf1ba64 100644 --- a/toml-spec/spec/spec.generated.json +++ b/toml-spec/spec/spec.generated.json @@ -447,7 +447,7 @@ "required": [] }, "Config": { - "description": "This is the top-level table.\n\n- Example:\n\n ```toml\n keymap = \"\"\"\n xkb_keymap {\n xkb_keycodes { include \"evdev+aliases(qwerty)\" };\n xkb_types { include \"complete\" };\n xkb_compat { include \"complete\" };\n xkb_symbols { include \"pc+us+inet(evdev)\" };\n };\n \"\"\"\n\n on-graphics-initialized = { type = \"exec\", exec = { prog = \"mako\", privileged = true } }\n\n [shortcuts]\n alt-h = \"focus-left\"\n alt-j = \"focus-down\"\n alt-k = \"focus-up\"\n alt-l = \"focus-right\"\n\n alt-shift-h = \"move-left\"\n alt-shift-j = \"move-down\"\n alt-shift-k = \"move-up\"\n alt-shift-l = \"move-right\"\n\n alt-d = \"split-horizontal\"\n alt-v = \"split-vertical\"\n\n alt-t = \"toggle-split\"\n alt-m = \"toggle-mono\"\n alt-u = \"toggle-fullscreen\"\n\n alt-f = \"focus-parent\"\n alt-shift-c = \"close\"\n alt-shift-f = \"toggle-floating\"\n Super_L = { type = \"exec\", exec = \"alacritty\" }\n alt-p = { type = \"exec\", exec = { prog = \"bemenu-run\", privileged = true } }\n alt-q = \"quit\"\n alt-shift-r = \"reload-config-toml\"\n\n ctrl-alt-F1 = { type = \"switch-to-vt\", num = 1 }\n ctrl-alt-F2 = { type = \"switch-to-vt\", num = 2 }\n # ...\n\n alt-F1 = { type = \"show-workspace\", name = \"1\" }\n alt-F2 = { type = \"show-workspace\", name = \"2\" }\n # ...\n\n alt-shift-F1 = { type = \"move-to-workspace\", name = \"1\" }\n alt-shift-F2 = { type = \"move-to-workspace\", name = \"2\" }\n # ...\n ```\n", + "description": "This is the top-level table.\n\n- Example:\n\n ```toml\n keymap = \"\"\"\n xkb_keymap {\n xkb_keycodes { include \"evdev+aliases(qwerty)\" };\n xkb_types { include \"complete\" };\n xkb_compat { include \"complete\" };\n xkb_symbols { include \"pc+us+inet(evdev)\" };\n };\n \"\"\"\n\n on-graphics-initialized = { type = \"exec\", exec = \"mako\" }\n\n [shortcuts]\n alt-h = \"focus-left\"\n alt-j = \"focus-down\"\n alt-k = \"focus-up\"\n alt-l = \"focus-right\"\n\n alt-shift-h = \"move-left\"\n alt-shift-j = \"move-down\"\n alt-shift-k = \"move-up\"\n alt-shift-l = \"move-right\"\n\n alt-d = \"split-horizontal\"\n alt-v = \"split-vertical\"\n\n alt-t = \"toggle-split\"\n alt-m = \"toggle-mono\"\n alt-u = \"toggle-fullscreen\"\n\n alt-f = \"focus-parent\"\n alt-shift-c = \"close\"\n alt-shift-f = \"toggle-floating\"\n Super_L = { type = \"exec\", exec = \"alacritty\" }\n alt-p = { type = \"exec\", exec = \"bemenu-run\" }\n alt-q = \"quit\"\n alt-shift-r = \"reload-config-toml\"\n\n ctrl-alt-F1 = { type = \"switch-to-vt\", num = 1 }\n ctrl-alt-F2 = { type = \"switch-to-vt\", num = 2 }\n # ...\n\n alt-F1 = { type = \"show-workspace\", name = \"1\" }\n alt-F2 = { type = \"show-workspace\", name = \"2\" }\n # ...\n\n alt-shift-F1 = { type = \"move-to-workspace\", name = \"1\" }\n alt-shift-F2 = { type = \"move-to-workspace\", name = \"2\" }\n # ...\n ```\n", "type": "object", "properties": { "keymap": { diff --git a/toml-spec/spec/spec.generated.md b/toml-spec/spec/spec.generated.md index 0184926d..c2e7e11e 100644 --- a/toml-spec/spec/spec.generated.md +++ b/toml-spec/spec/spec.generated.md @@ -679,7 +679,7 @@ This is the top-level table. }; """ - on-graphics-initialized = { type = "exec", exec = { prog = "mako", privileged = true } } + on-graphics-initialized = { type = "exec", exec = "mako" } [shortcuts] alt-h = "focus-left" @@ -703,7 +703,7 @@ This is the top-level table. alt-shift-c = "close" alt-shift-f = "toggle-floating" Super_L = { type = "exec", exec = "alacritty" } - alt-p = { type = "exec", exec = { prog = "bemenu-run", privileged = true } } + alt-p = { type = "exec", exec = "bemenu-run" } alt-q = "quit" alt-shift-r = "reload-config-toml" diff --git a/toml-spec/spec/spec.yaml b/toml-spec/spec/spec.yaml index 6efbfb9b..4454b080 100644 --- a/toml-spec/spec/spec.yaml +++ b/toml-spec/spec/spec.yaml @@ -1642,7 +1642,7 @@ Config: }; """ - on-graphics-initialized = { type = "exec", exec = { prog = "mako", privileged = true } } + on-graphics-initialized = { type = "exec", exec = "mako" } [shortcuts] alt-h = "focus-left" @@ -1666,7 +1666,7 @@ Config: alt-shift-c = "close" alt-shift-f = "toggle-floating" Super_L = { type = "exec", exec = "alacritty" } - alt-p = { type = "exec", exec = { prog = "bemenu-run", privileged = true } } + alt-p = { type = "exec", exec = "bemenu-run" } alt-q = "quit" alt-shift-r = "reload-config-toml" From 1fceffe235c2380cda3a25b30eb8263e51ac8b59 Mon Sep 17 00:00:00 2001 From: Julian Orth Date: Wed, 24 Apr 2024 13:00:17 +0200 Subject: [PATCH 3/3] wayland: implement wp-security-manager-v1 --- docs/features.md | 1 + release-notes.md | 2 + src/acceptor.rs | 7 +- src/client.rs | 27 +++-- src/compositor.rs | 1 + src/globals.rs | 4 +- src/ifs.rs | 2 + src/ifs/wl_registry.rs | 2 +- src/ifs/wp_security_context_manager_v1.rs | 107 +++++++++++++++++++ src/ifs/wp_security_context_v1.rs | 119 +++++++++++++++++++++ src/main.rs | 1 + src/security_context_acceptor.rs | 123 ++++++++++++++++++++++ src/state.rs | 3 + src/xwayland.rs | 1 + wire/wp_security_context_manager_v1.txt | 9 ++ wire/wp_security_context_v1.txt | 19 ++++ 16 files changed, 417 insertions(+), 11 deletions(-) create mode 100644 src/ifs/wp_security_context_manager_v1.rs create mode 100644 src/ifs/wp_security_context_v1.rs create mode 100644 src/security_context_acceptor.rs create mode 100644 wire/wp_security_context_manager_v1.txt create mode 100644 wire/wp_security_context_v1.txt diff --git a/docs/features.md b/docs/features.md index c280eafb..964cb4d0 100644 --- a/docs/features.md +++ b/docs/features.md @@ -141,6 +141,7 @@ Jay supports the following wayland protocols: | wp_fractional_scale_manager_v1 | 1 | | | wp_linux_drm_syncobj_manager_v1 | 1 | | | wp_presentation | 1 | | +| wp_security_context_manager_v1 | 1 | | | wp_single_pixel_buffer_manager_v1 | 1 | | | wp_tearing_control_manager_v1 | 1[^no_tearing] | | | wp_viewporter | 1 | | diff --git a/release-notes.md b/release-notes.md index f4008d54..33a3405f 100644 --- a/release-notes.md +++ b/release-notes.md @@ -1,5 +1,7 @@ # Unreleased +- Add support for wp-security-manager-v1. + # 1.1.0 (2024-04-22) - Screencasts now support window capture. diff --git a/src/acceptor.rs b/src/acceptor.rs index b359d773..a73fca68 100644 --- a/src/acceptor.rs +++ b/src/acceptor.rs @@ -171,7 +171,7 @@ impl Acceptor { } } -async fn accept(fd: Rc, state: Rc, caps: ClientCaps) { +async fn accept(fd: Rc, state: Rc, effective_caps: ClientCaps) { loop { let fd = match state.ring.accept(&fd, c::SOCK_CLOEXEC).await { Ok(fd) => fd, @@ -181,7 +181,10 @@ async fn accept(fd: Rc, state: Rc, caps: ClientCaps) { } }; let id = state.clients.id(); - if let Err(e) = state.clients.spawn(id, &state, fd, caps) { + if let Err(e) = state + .clients + .spawn(id, &state, fd, effective_caps, ClientCaps::all()) + { log::error!("Could not spawn a client: {}", ErrorFmt(e)); break; } diff --git a/src/client.rs b/src/client.rs index 26aafa66..2c95c68d 100644 --- a/src/client.rs +++ b/src/client.rs @@ -113,7 +113,8 @@ impl Clients { id: ClientId, global: &Rc, socket: Rc, - caps: ClientCaps, + effective_caps: ClientCaps, + bounding_caps: ClientCaps, ) -> Result<(), ClientError> { let (uid, pid) = { let mut cred = c::ucred { @@ -132,7 +133,16 @@ impl Clients { } } }; - self.spawn2(id, global, socket, uid, pid, caps, false)?; + self.spawn2( + id, + global, + socket, + uid, + pid, + effective_caps, + bounding_caps, + false, + )?; Ok(()) } @@ -143,7 +153,8 @@ impl Clients { socket: Rc, uid: c::uid_t, pid: c::pid_t, - caps: ClientCaps, + effective_caps: ClientCaps, + bounding_caps: ClientCaps, is_xwayland: bool, ) -> Result, ClientError> { let data = Rc::new(Client { @@ -157,7 +168,8 @@ impl Clients { shutdown: Default::default(), tracker: Default::default(), is_xwayland, - caps, + effective_caps, + bounding_caps, last_enter_serial: Cell::new(0), pid_info: get_pid_info(uid, pid), serials: Default::default(), @@ -183,7 +195,7 @@ impl Clients { uid, client.data.socket.raw(), data.pid_info.comm, - caps, + effective_caps, ); self.clients.borrow_mut().insert(client.data.id, client); Ok(data) @@ -211,7 +223,7 @@ impl Clients { { let clients = self.clients.borrow(); for client in clients.values() { - if client.data.caps.contains(required_caps) + if client.data.effective_caps.contains(required_caps) && (!xwayland_only || client.data.is_xwayland) { f(&client.data); @@ -272,7 +284,8 @@ pub struct Client { shutdown: AsyncEvent, pub tracker: Tracker, pub is_xwayland: bool, - pub caps: ClientCaps, + pub effective_caps: ClientCaps, + pub bounding_caps: ClientCaps, pub last_enter_serial: Cell, pub pid_info: PidInfo, pub serials: RefCell>, diff --git a/src/compositor.rs b/src/compositor.rs index eb361305..eeebec41 100644 --- a/src/compositor.rs +++ b/src/compositor.rs @@ -235,6 +235,7 @@ fn start_compositor2( wait_for_sync_obj: Rc::new(WaitForSyncObj::new(&ring, &engine)), explicit_sync_enabled: Cell::new(true), keyboard_state_ids: Default::default(), + security_context_acceptors: Default::default(), }); state.tracker.register(ClientId::from_raw(0)); create_dummy_output(&state); diff --git a/src/globals.rs b/src/globals.rs index 7c344ab8..df2eb32e 100644 --- a/src/globals.rs +++ b/src/globals.rs @@ -34,6 +34,7 @@ use { wp_cursor_shape_manager_v1::WpCursorShapeManagerV1Global, wp_fractional_scale_manager_v1::WpFractionalScaleManagerV1Global, wp_presentation::WpPresentationGlobal, + wp_security_context_manager_v1::WpSecurityContextManagerV1Global, wp_single_pixel_buffer_manager_v1::WpSinglePixelBufferManagerV1Global, wp_tearing_control_manager_v1::WpTearingControlManagerV1Global, wp_viewporter::WpViewporterGlobal, @@ -184,6 +185,7 @@ impl Globals { add_singleton!(ZwpVirtualKeyboardManagerV1Global); add_singleton!(ZwpInputMethodManagerV2Global); add_singleton!(ZwpTextInputManagerV3Global); + add_singleton!(WpSecurityContextManagerV1Global); } pub fn add_backend_singletons(&self, backend: &Rc) { @@ -249,7 +251,7 @@ impl Globals { } pub fn notify_all(&self, registry: &Rc) { - let caps = registry.client.caps; + let caps = registry.client.effective_caps; let xwayland = registry.client.is_xwayland; let globals = self.registry.lock(); macro_rules! emit { diff --git a/src/ifs.rs b/src/ifs.rs index 2fab7574..ccd578da 100644 --- a/src/ifs.rs +++ b/src/ifs.rs @@ -46,6 +46,8 @@ pub mod wp_linux_drm_syncobj_manager_v1; pub mod wp_linux_drm_syncobj_timeline_v1; pub mod wp_presentation; pub mod wp_presentation_feedback; +pub mod wp_security_context_manager_v1; +pub mod wp_security_context_v1; pub mod wp_single_pixel_buffer_manager_v1; pub mod wp_tearing_control_manager_v1; pub mod wp_viewporter; diff --git a/src/ifs/wl_registry.rs b/src/ifs/wl_registry.rs index b1457c41..9392043e 100644 --- a/src/ifs/wl_registry.rs +++ b/src/ifs/wl_registry.rs @@ -48,7 +48,7 @@ impl WlRegistryRequestHandler for WlRegistry { fn bind(&self, bind: Bind, _slf: &Rc) -> Result<(), Self::Error> { let name = GlobalName::from_raw(bind.name); let globals = &self.client.state.globals; - let global = globals.get(name, self.client.caps, self.client.is_xwayland)?; + let global = globals.get(name, self.client.effective_caps, self.client.is_xwayland)?; if global.interface().name() != bind.interface { return Err(WlRegistryError::InvalidInterface(InterfaceError { name: global.name(), diff --git a/src/ifs/wp_security_context_manager_v1.rs b/src/ifs/wp_security_context_manager_v1.rs new file mode 100644 index 00000000..7ea10332 --- /dev/null +++ b/src/ifs/wp_security_context_manager_v1.rs @@ -0,0 +1,107 @@ +use { + crate::{ + client::{Client, ClientError}, + globals::{Global, GlobalName}, + ifs::wp_security_context_v1::WpSecurityContextV1, + leaks::Tracker, + object::{Object, Version}, + wire::{wp_security_context_manager_v1::*, WpSecurityContextManagerV1Id}, + }, + std::rc::Rc, + thiserror::Error, +}; + +pub struct WpSecurityContextManagerV1Global { + pub name: GlobalName, +} + +impl WpSecurityContextManagerV1Global { + pub fn new(name: GlobalName) -> Self { + Self { name } + } + + fn bind_( + self: Rc, + id: WpSecurityContextManagerV1Id, + client: &Rc, + version: Version, + ) -> Result<(), WpSecurityContextManagerV1Error> { + let obj = Rc::new(WpSecurityContextManagerV1 { + id, + client: client.clone(), + tracker: Default::default(), + version, + }); + track!(client, obj); + client.add_client_obj(&obj)?; + Ok(()) + } +} + +global_base!( + WpSecurityContextManagerV1Global, + WpSecurityContextManagerV1, + WpSecurityContextManagerV1Error +); + +impl Global for WpSecurityContextManagerV1Global { + fn singleton(&self) -> bool { + true + } + + fn version(&self) -> u32 { + 1 + } +} + +simple_add_global!(WpSecurityContextManagerV1Global); + +pub struct WpSecurityContextManagerV1 { + pub id: WpSecurityContextManagerV1Id, + pub client: Rc, + pub tracker: Tracker, + pub version: Version, +} + +impl WpSecurityContextManagerV1RequestHandler for WpSecurityContextManagerV1 { + type Error = WpSecurityContextManagerV1Error; + + fn destroy(&self, _req: Destroy, _slf: &Rc) -> Result<(), Self::Error> { + self.client.remove_obj(self)?; + Ok(()) + } + + fn create_listener(&self, req: CreateListener, _slf: &Rc) -> Result<(), Self::Error> { + let obj = Rc::new(WpSecurityContextV1 { + id: req.id, + client: self.client.clone(), + tracker: Default::default(), + version: self.version, + listen_fd: req.listen_fd, + close_fd: req.close_fd, + sandbox_engine: Default::default(), + app_id: Default::default(), + instance_id: Default::default(), + committed: Default::default(), + }); + track!(self.client, obj); + self.client.add_client_obj(&obj)?; + Ok(()) + } +} + +object_base! { + self = WpSecurityContextManagerV1; + version = self.version; +} + +impl Object for WpSecurityContextManagerV1 {} + +simple_add_obj!(WpSecurityContextManagerV1); + +#[derive(Debug, Error)] +pub enum WpSecurityContextManagerV1Error { + #[error(transparent)] + ClientError(Box), +} +efrom!(WpSecurityContextManagerV1Error, ClientError); diff --git a/src/ifs/wp_security_context_v1.rs b/src/ifs/wp_security_context_v1.rs new file mode 100644 index 00000000..670a70d3 --- /dev/null +++ b/src/ifs/wp_security_context_v1.rs @@ -0,0 +1,119 @@ +use { + crate::{ + client::{Client, ClientCaps, ClientError}, + leaks::Tracker, + object::{Object, Version}, + wire::{wp_security_context_v1::*, WpSecurityContextV1Id}, + }, + std::{ + cell::{Cell, RefCell}, + rc::Rc, + }, + thiserror::Error, + uapi::OwnedFd, +}; + +pub struct WpSecurityContextV1 { + pub id: WpSecurityContextV1Id, + pub client: Rc, + pub tracker: Tracker, + pub version: Version, + pub listen_fd: Rc, + pub close_fd: Rc, + pub sandbox_engine: RefCell>, + pub app_id: RefCell>, + pub instance_id: RefCell>, + pub committed: Cell, +} + +impl WpSecurityContextV1 { + fn check_committed(&self) -> Result<(), WpSecurityContextV1Error> { + if self.committed.get() { + return Err(WpSecurityContextV1Error::Committed); + } + Ok(()) + } +} + +impl WpSecurityContextV1RequestHandler for WpSecurityContextV1 { + type Error = WpSecurityContextV1Error; + + fn destroy(&self, _req: Destroy, _slf: &Rc) -> Result<(), Self::Error> { + self.client.remove_obj(self)?; + Ok(()) + } + + fn set_sandbox_engine( + &self, + req: SetSandboxEngine<'_>, + _slf: &Rc, + ) -> Result<(), Self::Error> { + self.check_committed()?; + let val = &mut *self.sandbox_engine.borrow_mut(); + if val.is_some() { + return Err(WpSecurityContextV1Error::EnginSet); + } + *val = Some(req.name.to_string()); + Ok(()) + } + + fn set_app_id(&self, req: SetAppId<'_>, _slf: &Rc) -> Result<(), Self::Error> { + self.check_committed()?; + let val = &mut *self.app_id.borrow_mut(); + if val.is_some() { + return Err(WpSecurityContextV1Error::AppSet); + } + *val = Some(req.app_id.to_string()); + Ok(()) + } + + fn set_instance_id(&self, req: SetInstanceId<'_>, _slf: &Rc) -> Result<(), Self::Error> { + self.check_committed()?; + let val = &mut *self.instance_id.borrow_mut(); + if val.is_some() { + return Err(WpSecurityContextV1Error::InstanceSet); + } + *val = Some(req.instance_id.to_string()); + Ok(()) + } + + fn commit(&self, _req: Commit, _slf: &Rc) -> Result<(), Self::Error> { + self.check_committed()?; + self.committed.set(true); + let caps = ClientCaps::none() & self.client.bounding_caps; + self.client.state.security_context_acceptors.spawn( + &self.client.state, + self.sandbox_engine.take(), + self.app_id.take(), + self.instance_id.take(), + &self.listen_fd, + &self.close_fd, + caps, + ); + Ok(()) + } +} + +object_base! { + self = WpSecurityContextV1; + version = self.version; +} + +impl Object for WpSecurityContextV1 {} + +simple_add_obj!(WpSecurityContextV1); + +#[derive(Debug, Error)] +pub enum WpSecurityContextV1Error { + #[error(transparent)] + ClientError(Box), + #[error("The sandbox engine has already been set")] + EnginSet, + #[error("The app id has already been set")] + AppSet, + #[error("The instance id has already been set")] + InstanceSet, + #[error("The context has already been committed")] + Committed, +} +efrom!(WpSecurityContextV1Error, ClientError); diff --git a/src/main.rs b/src/main.rs index bdd39264..9047758a 100644 --- a/src/main.rs +++ b/src/main.rs @@ -76,6 +76,7 @@ mod rect; mod renderer; mod scale; mod screenshoter; +mod security_context_acceptor; mod sighand; mod state; mod tasks; diff --git a/src/security_context_acceptor.rs b/src/security_context_acceptor.rs new file mode 100644 index 00000000..69f102af --- /dev/null +++ b/src/security_context_acceptor.rs @@ -0,0 +1,123 @@ +use { + crate::{ + async_engine::SpawnedFuture, + client::ClientCaps, + state::State, + utils::{copyhashmap::CopyHashMap, errorfmt::ErrorFmt}, + }, + std::{ + cell::Cell, + fmt::{Display, Formatter}, + rc::Rc, + }, + uapi::{c, OwnedFd}, +}; + +#[derive(Default)] +pub struct SecurityContextAcceptors { + ids: AcceptorIds, + acceptors: CopyHashMap>, +} + +linear_ids!(AcceptorIds, AcceptorId, u64); + +struct Acceptor { + id: AcceptorId, + state: Rc, + sandbox_engine: Option, + app_id: Option, + instance_id: Option, + listen_fd: Rc, + close_fd: Rc, + caps: ClientCaps, + listen_future: Cell>>, + close_future: Cell>>, +} + +impl SecurityContextAcceptors { + pub fn clear(&self) { + for (_, acceptor) in self.acceptors.lock().drain() { + acceptor.kill(); + } + } + + pub fn spawn( + &self, + state: &Rc, + sandbox_engine: Option, + app_id: Option, + instance_id: Option, + listen_fd: &Rc, + close_fd: &Rc, + caps: ClientCaps, + ) { + let acceptor = Rc::new(Acceptor { + id: self.ids.next(), + state: state.clone(), + sandbox_engine, + app_id, + instance_id, + listen_fd: listen_fd.clone(), + close_fd: close_fd.clone(), + caps, + listen_future: Cell::new(None), + close_future: Cell::new(None), + }); + log::info!("Creating security acceptor {acceptor}"); + acceptor + .listen_future + .set(Some(state.eng.spawn(acceptor.clone().accept()))); + acceptor + .close_future + .set(Some(state.eng.spawn(acceptor.clone().close()))); + self.acceptors.set(acceptor.id, acceptor); + } +} + +impl Acceptor { + fn kill(&self) { + log::info!("Destroying security acceptor {self}"); + self.listen_future.take(); + self.close_future.take(); + self.state + .security_context_acceptors + .acceptors + .remove(&self.id); + } + + async fn accept(self: Rc) { + let s = &self.state; + loop { + let fd = match s.ring.accept(&self.listen_fd, c::SOCK_CLOEXEC).await { + Ok(fd) => fd, + Err(e) => { + log::error!("Could not accept a client: {}", ErrorFmt(e)); + break; + } + }; + let id = s.clients.id(); + if let Err(e) = s.clients.spawn(id, s, fd, self.caps, self.caps) { + log::error!("Could not spawn a client: {}", ErrorFmt(e)); + break; + } + } + self.kill(); + } + + async fn close(self: Rc) { + let _ = self.state.ring.poll(&self.close_fd, 0).await; + self.kill(); + } +} + +impl Display for Acceptor { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + write!( + f, + "{}/{}/{}", + self.sandbox_engine.as_deref().unwrap_or(""), + self.app_id.as_deref().unwrap_or(""), + self.instance_id.as_deref().unwrap_or(""), + ) + } +} diff --git a/src/state.rs b/src/state.rs index 03a078fa..fe49b79e 100644 --- a/src/state.rs +++ b/src/state.rs @@ -51,6 +51,7 @@ use { rect::Rect, renderer::{RenderResult, Renderer}, scale::Scale, + security_context_acceptor::SecurityContextAcceptors, theme::{Color, Theme}, tree::{ ContainerNode, ContainerSplit, Direction, DisplayNode, FloatNode, Node, NodeIds, @@ -181,6 +182,7 @@ pub struct State { pub wait_for_sync_obj: Rc, pub explicit_sync_enabled: Cell, pub keyboard_state_ids: KeyboardStateIds, + pub security_context_acceptors: SecurityContextAcceptors, } // impl Drop for State { @@ -744,6 +746,7 @@ impl State { self.render_ctx_watchers.clear(); self.workspace_watchers.clear(); self.toplevel_lists.clear(); + self.security_context_acceptors.clear(); self.slow_clients.clear(); for (_, h) in self.input_device_handlers.borrow_mut().drain() { h.async_event.clear(); diff --git a/src/xwayland.rs b/src/xwayland.rs index c734a380..7b11d972 100644 --- a/src/xwayland.rs +++ b/src/xwayland.rs @@ -172,6 +172,7 @@ async fn run( uapi::getuid(), pid, ClientCaps::all(), + ClientCaps::all(), true, ); let client = match client { diff --git a/wire/wp_security_context_manager_v1.txt b/wire/wp_security_context_manager_v1.txt new file mode 100644 index 00000000..15aabdae --- /dev/null +++ b/wire/wp_security_context_manager_v1.txt @@ -0,0 +1,9 @@ +request destroy { + +} + +request create_listener { + id: id(wp_security_context_v1), + listen_fd: fd, + close_fd: fd, +} diff --git a/wire/wp_security_context_v1.txt b/wire/wp_security_context_v1.txt new file mode 100644 index 00000000..1c538df5 --- /dev/null +++ b/wire/wp_security_context_v1.txt @@ -0,0 +1,19 @@ +request destroy { + +} + +request set_sandbox_engine { + name: str, +} + +request set_app_id { + app_id: str, +} + +request set_instance_id { + instance_id: str, +} + +request commit { + +}