security-context: implement protocol
Sensitive Wayland protocols such as wlr_screencopy and wlr_data_control (clipboard managment) are now blocked by default inside security contexts (e.g. flatpak 1.15.6 or later). User configuration of the allowlist/blocklist is TODO.
This commit is contained in:
parent
e143cdeca9
commit
1b63c463a7
2
deps/zig-wlroots
vendored
2
deps/zig-wlroots
vendored
@ -1 +1 @@
|
||||
Subproject commit 3789f0e0c946fe2c9ac09dc09593cada93fd3797
|
||||
Subproject commit c1e19f0b9fbdaf4f54a4a2129cf2cd7e68ec09ea
|
@ -32,6 +32,8 @@ const Output = @import("Output.zig");
|
||||
|
||||
const log = std.log.scoped(.session_lock);
|
||||
|
||||
wlr_manager: *wlr.SessionLockManagerV1,
|
||||
|
||||
state: enum {
|
||||
/// No lock request has been made and the session is unlocked.
|
||||
unlocked,
|
||||
@ -66,11 +68,11 @@ pub fn init(manager: *LockManager) !void {
|
||||
errdefer timer.remove();
|
||||
|
||||
manager.* = .{
|
||||
.wlr_manager = try wlr.SessionLockManagerV1.create(server.wl_server),
|
||||
.lock_surfaces_timer = timer,
|
||||
};
|
||||
|
||||
const wlr_manager = try wlr.SessionLockManagerV1.create(server.wl_server);
|
||||
wlr_manager.events.new_lock.add(&manager.new_lock);
|
||||
manager.wlr_manager.events.new_lock.add(&manager.new_lock);
|
||||
}
|
||||
|
||||
pub fn deinit(manager: *LockManager) void {
|
||||
|
@ -83,6 +83,9 @@ new_output: wl.Listener(*wlr.Output) = wl.Listener(*wlr.Output).init(handleNewOu
|
||||
output_layout: *wlr.OutputLayout,
|
||||
layout_change: wl.Listener(*wlr.OutputLayout) = wl.Listener(*wlr.OutputLayout).init(handleLayoutChange),
|
||||
|
||||
presentation: *wlr.Presentation,
|
||||
xdg_output_manager: *wlr.XdgOutputManagerV1,
|
||||
|
||||
output_manager: *wlr.OutputManagerV1,
|
||||
manager_apply: wl.Listener(*wlr.OutputConfigurationV1) =
|
||||
wl.Listener(*wlr.OutputConfigurationV1).init(handleManagerApply),
|
||||
@ -128,8 +131,6 @@ pub fn init(root: *Root) !void {
|
||||
const outputs = try interactive_content.createSceneTree();
|
||||
const override_redirect = if (build_options.xwayland) try interactive_content.createSceneTree();
|
||||
|
||||
_ = try wlr.XdgOutputManagerV1.create(server.wl_server, output_layout);
|
||||
|
||||
const presentation = try wlr.Presentation.create(server.wl_server, server.backend);
|
||||
scene.setPresentation(presentation);
|
||||
|
||||
@ -164,6 +165,9 @@ pub fn init(root: *Root) !void {
|
||||
.output_layout = output_layout,
|
||||
.all_outputs = undefined,
|
||||
.active_outputs = undefined,
|
||||
|
||||
.presentation = presentation,
|
||||
.xdg_output_manager = try wlr.XdgOutputManagerV1.create(server.wl_server, output_layout),
|
||||
.output_manager = try wlr.OutputManagerV1.create(server.wl_server),
|
||||
.power_manager = try wlr.OutputPowerManagerV1.create(server.wl_server),
|
||||
.gamma_control_manager = try wlr.GammaControlManagerV1.create(server.wl_server),
|
||||
|
167
river/Server.zig
167
river/Server.zig
@ -18,6 +18,7 @@ const Server = @This();
|
||||
|
||||
const build_options = @import("build_options");
|
||||
const std = @import("std");
|
||||
const assert = std.debug.assert;
|
||||
const wlr = @import("wlroots");
|
||||
const wl = @import("wayland").server.wl;
|
||||
|
||||
@ -54,34 +55,33 @@ session: ?*wlr.Session,
|
||||
renderer: *wlr.Renderer,
|
||||
allocator: *wlr.Allocator,
|
||||
|
||||
security_context_manager: *wlr.SecurityContextManagerV1,
|
||||
|
||||
shm: *wlr.Shm,
|
||||
drm: ?*wlr.Drm = null,
|
||||
linux_dmabuf: ?*wlr.LinuxDmabufV1 = null,
|
||||
single_pixel_buffer_manager: *wlr.SinglePixelBufferManagerV1,
|
||||
|
||||
viewporter: *wlr.Viewporter,
|
||||
fractional_scale_manager: *wlr.FractionalScaleManagerV1,
|
||||
compositor: *wlr.Compositor,
|
||||
subcompositor: *wlr.Subcompositor,
|
||||
cursor_shape_manager: *wlr.CursorShapeManagerV1,
|
||||
|
||||
xdg_shell: *wlr.XdgShell,
|
||||
new_xdg_surface: wl.Listener(*wlr.XdgSurface) =
|
||||
wl.Listener(*wlr.XdgSurface).init(handleNewXdgSurface),
|
||||
|
||||
xdg_decoration_manager: *wlr.XdgDecorationManagerV1,
|
||||
new_toplevel_decoration: wl.Listener(*wlr.XdgToplevelDecorationV1) =
|
||||
wl.Listener(*wlr.XdgToplevelDecorationV1).init(handleNewToplevelDecoration),
|
||||
|
||||
layer_shell: *wlr.LayerShellV1,
|
||||
new_layer_surface: wl.Listener(*wlr.LayerSurfaceV1) =
|
||||
wl.Listener(*wlr.LayerSurfaceV1).init(handleNewLayerSurface),
|
||||
xdg_activation: *wlr.XdgActivationV1,
|
||||
|
||||
xwayland: if (build_options.xwayland) ?*wlr.Xwayland else void = if (build_options.xwayland) null,
|
||||
new_xwayland_surface: if (build_options.xwayland) wl.Listener(*wlr.XwaylandSurface) else void =
|
||||
if (build_options.xwayland) wl.Listener(*wlr.XwaylandSurface).init(handleNewXwaylandSurface),
|
||||
data_device_manager: *wlr.DataDeviceManager,
|
||||
primary_selection_manager: *wlr.PrimarySelectionDeviceManagerV1,
|
||||
data_control_manager: *wlr.DataControlManagerV1,
|
||||
|
||||
export_dmabuf_manager: *wlr.ExportDmabufManagerV1,
|
||||
screencopy_manager: *wlr.ScreencopyManagerV1,
|
||||
|
||||
foreign_toplevel_manager: *wlr.ForeignToplevelManagerV1,
|
||||
|
||||
xdg_activation: *wlr.XdgActivationV1,
|
||||
request_activate: wl.Listener(*wlr.XdgActivationV1.event.RequestActivate) =
|
||||
wl.Listener(*wlr.XdgActivationV1.event.RequestActivate).init(handleRequestActivate),
|
||||
|
||||
cursor_shape_manager: *wlr.CursorShapeManagerV1,
|
||||
request_set_cursor_shape: wl.Listener(*wlr.CursorShapeManagerV1.event.RequestSetShape) =
|
||||
wl.Listener(*wlr.CursorShapeManagerV1.event.RequestSetShape).init(handleRequestSetCursorShape),
|
||||
|
||||
input_manager: InputManager,
|
||||
root: Root,
|
||||
config: Config,
|
||||
@ -91,6 +91,21 @@ layout_manager: LayoutManager,
|
||||
idle_inhibit_manager: IdleInhibitManager,
|
||||
lock_manager: LockManager,
|
||||
|
||||
xwayland: if (build_options.xwayland) ?*wlr.Xwayland else void = if (build_options.xwayland) null,
|
||||
new_xwayland_surface: if (build_options.xwayland) wl.Listener(*wlr.XwaylandSurface) else void =
|
||||
if (build_options.xwayland) wl.Listener(*wlr.XwaylandSurface).init(handleNewXwaylandSurface),
|
||||
|
||||
new_xdg_surface: wl.Listener(*wlr.XdgSurface) =
|
||||
wl.Listener(*wlr.XdgSurface).init(handleNewXdgSurface),
|
||||
new_toplevel_decoration: wl.Listener(*wlr.XdgToplevelDecorationV1) =
|
||||
wl.Listener(*wlr.XdgToplevelDecorationV1).init(handleNewToplevelDecoration),
|
||||
new_layer_surface: wl.Listener(*wlr.LayerSurfaceV1) =
|
||||
wl.Listener(*wlr.LayerSurfaceV1).init(handleNewLayerSurface),
|
||||
request_activate: wl.Listener(*wlr.XdgActivationV1.event.RequestActivate) =
|
||||
wl.Listener(*wlr.XdgActivationV1.event.RequestActivate).init(handleRequestActivate),
|
||||
request_set_cursor_shape: wl.Listener(*wlr.CursorShapeManagerV1.event.RequestSetShape) =
|
||||
wl.Listener(*wlr.CursorShapeManagerV1.event.RequestSetShape).init(handleRequestSetCursorShape),
|
||||
|
||||
pub fn init(server: *Server, runtime_xwayland: bool) !void {
|
||||
// We intentionally don't try to prevent memory leaks on error in this function
|
||||
// since river will exit during initialization anyway if there is an error.
|
||||
@ -115,13 +130,30 @@ pub fn init(server: *Server, runtime_xwayland: bool) !void {
|
||||
.renderer = renderer,
|
||||
.allocator = try wlr.Allocator.autocreate(backend, renderer),
|
||||
|
||||
.security_context_manager = try wlr.SecurityContextManagerV1.create(wl_server),
|
||||
|
||||
.shm = try wlr.Shm.createWithRenderer(wl_server, 1, renderer),
|
||||
.single_pixel_buffer_manager = try wlr.SinglePixelBufferManagerV1.create(wl_server),
|
||||
|
||||
.viewporter = try wlr.Viewporter.create(wl_server),
|
||||
.fractional_scale_manager = try wlr.FractionalScaleManagerV1.create(wl_server, 1),
|
||||
.compositor = compositor,
|
||||
.subcompositor = try wlr.Subcompositor.create(wl_server),
|
||||
.cursor_shape_manager = try wlr.CursorShapeManagerV1.create(server.wl_server, 1),
|
||||
|
||||
.xdg_shell = try wlr.XdgShell.create(wl_server, 5),
|
||||
.xdg_decoration_manager = try wlr.XdgDecorationManagerV1.create(wl_server),
|
||||
.layer_shell = try wlr.LayerShellV1.create(wl_server, 4),
|
||||
.foreign_toplevel_manager = try wlr.ForeignToplevelManagerV1.create(wl_server),
|
||||
.xdg_activation = try wlr.XdgActivationV1.create(wl_server),
|
||||
.cursor_shape_manager = try wlr.CursorShapeManagerV1.create(server.wl_server, 1),
|
||||
|
||||
.data_device_manager = try wlr.DataDeviceManager.create(wl_server),
|
||||
.primary_selection_manager = try wlr.PrimarySelectionDeviceManagerV1.create(wl_server),
|
||||
.data_control_manager = try wlr.DataControlManagerV1.create(wl_server),
|
||||
|
||||
.export_dmabuf_manager = try wlr.ExportDmabufManagerV1.create(wl_server),
|
||||
.screencopy_manager = try wlr.ScreencopyManagerV1.create(wl_server),
|
||||
|
||||
.foreign_toplevel_manager = try wlr.ForeignToplevelManagerV1.create(wl_server),
|
||||
|
||||
.config = try Config.init(),
|
||||
|
||||
@ -134,27 +166,16 @@ pub fn init(server: *Server, runtime_xwayland: bool) !void {
|
||||
.lock_manager = undefined,
|
||||
};
|
||||
|
||||
try renderer.initWlShm(wl_server);
|
||||
if (renderer.getDmabufFormats() != null and renderer.getDrmFd() >= 0) {
|
||||
// wl_drm is a legacy interface and all clients should switch to linux_dmabuf.
|
||||
// However, enough widely used clients still rely on wl_drm that the pragmatic option
|
||||
// is to keep it around for the near future.
|
||||
// TODO remove wl_drm support
|
||||
_ = try wlr.Drm.create(wl_server, renderer);
|
||||
server.drm = try wlr.Drm.create(wl_server, renderer);
|
||||
|
||||
_ = try wlr.LinuxDmabufV1.createWithRenderer(wl_server, 4, renderer);
|
||||
server.linux_dmabuf = try wlr.LinuxDmabufV1.createWithRenderer(wl_server, 4, renderer);
|
||||
}
|
||||
|
||||
_ = try wlr.Subcompositor.create(wl_server);
|
||||
_ = try wlr.PrimarySelectionDeviceManagerV1.create(wl_server);
|
||||
_ = try wlr.DataDeviceManager.create(wl_server);
|
||||
_ = try wlr.DataControlManagerV1.create(wl_server);
|
||||
_ = try wlr.ExportDmabufManagerV1.create(wl_server);
|
||||
_ = try wlr.ScreencopyManagerV1.create(wl_server);
|
||||
_ = try wlr.SinglePixelBufferManagerV1.create(wl_server);
|
||||
_ = try wlr.Viewporter.create(wl_server);
|
||||
_ = try wlr.FractionalScaleManagerV1.create(wl_server, 1);
|
||||
|
||||
if (build_options.xwayland and runtime_xwayland) {
|
||||
server.xwayland = try wlr.Xwayland.create(wl_server, compositor, false);
|
||||
server.xwayland.?.events.new_surface.add(&server.new_xwayland_surface);
|
||||
@ -244,8 +265,86 @@ fn globalFilter(client: *const wl.Client, global: *const wl.Global, server: *Ser
|
||||
}
|
||||
}
|
||||
|
||||
// User-configurable allow/block lists are TODO
|
||||
if (server.security_context_manager.lookupClient(client) != null) {
|
||||
const allowed = server.allowlist(global);
|
||||
const blocked = server.blocklist(global);
|
||||
assert(allowed != blocked);
|
||||
return allowed;
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
fn hackGlobal(ptr: *anyopaque) *wl.Global {
|
||||
// TODO(wlroots) MR that eliminates the need for this hack:
|
||||
// https://gitlab.freedesktop.org/wlroots/wlroots/-/merge_requests/4612
|
||||
if (wlr.version.major != 0 or wlr.version.minor != 17) @compileError("FIXME");
|
||||
|
||||
return @as(*extern struct { global: *wl.Global }, @alignCast(@ptrCast(ptr))).global;
|
||||
}
|
||||
|
||||
/// Returns true if the global is allowlisted for security contexts
|
||||
fn allowlist(server: *Server, global: *const wl.Global) bool {
|
||||
if (server.drm) |drm| if (global == drm.global) return true;
|
||||
if (server.linux_dmabuf) |linux_dmabuf| if (global == linux_dmabuf.global) return true;
|
||||
|
||||
{
|
||||
var it = server.root.all_outputs.iterator(.forward);
|
||||
while (it.next()) |output| {
|
||||
if (global == output.wlr_output.global) return true;
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
var it = server.input_manager.seats.first;
|
||||
while (it) |node| : (it = node.next) {
|
||||
if (global == node.data.wlr_seat.global) return true;
|
||||
}
|
||||
}
|
||||
|
||||
return global == hackGlobal(server.shm) or
|
||||
global == hackGlobal(server.single_pixel_buffer_manager) or
|
||||
global == server.viewporter.global or
|
||||
global == server.fractional_scale_manager.global or
|
||||
global == server.compositor.global or
|
||||
global == server.subcompositor.global or
|
||||
global == server.cursor_shape_manager.global or
|
||||
global == server.xdg_shell.global or
|
||||
global == server.xdg_decoration_manager.global or
|
||||
global == server.xdg_activation.global or
|
||||
global == server.data_device_manager.global or
|
||||
global == server.primary_selection_manager.global or
|
||||
global == server.root.presentation.global or
|
||||
global == server.root.xdg_output_manager.global or
|
||||
global == server.input_manager.relative_pointer_manager.global or
|
||||
global == server.input_manager.pointer_constraints.global or
|
||||
global == server.input_manager.text_input_manager.global or
|
||||
global == server.input_manager.tablet_manager.global or
|
||||
global == server.input_manager.pointer_gestures.global or
|
||||
global == server.idle_inhibit_manager.wlr_manager.global;
|
||||
}
|
||||
|
||||
/// Returns true if the global is blocked for security contexts
|
||||
fn blocklist(server: *Server, global: *const wl.Global) bool {
|
||||
return global == server.security_context_manager.global or
|
||||
global == server.layer_shell.global or
|
||||
global == server.foreign_toplevel_manager.global or
|
||||
global == server.screencopy_manager.global or
|
||||
global == server.export_dmabuf_manager.global or
|
||||
global == server.data_control_manager.global or
|
||||
global == server.layout_manager.global or
|
||||
global == server.control.global or
|
||||
global == server.status_manager.global or
|
||||
global == server.root.output_manager.global or
|
||||
global == server.root.power_manager.global or
|
||||
global == server.root.gamma_control_manager.global or
|
||||
global == hackGlobal(server.input_manager.idle_notifier) or
|
||||
global == server.input_manager.virtual_pointer_manager.global or
|
||||
global == server.input_manager.virtual_keyboard_manager.global or
|
||||
global == server.input_manager.input_method_manager.global or
|
||||
global == server.lock_manager.wlr_manager.global;
|
||||
}
|
||||
|
||||
/// Handle SIGINT and SIGTERM by gracefully stopping the server
|
||||
fn terminate(_: c_int, wl_server: *wl.Server) c_int {
|
||||
|
Loading…
Reference in New Issue
Block a user