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);
|
const log = std.log.scoped(.session_lock);
|
||||||
|
|
||||||
|
wlr_manager: *wlr.SessionLockManagerV1,
|
||||||
|
|
||||||
state: enum {
|
state: enum {
|
||||||
/// No lock request has been made and the session is unlocked.
|
/// No lock request has been made and the session is unlocked.
|
||||||
unlocked,
|
unlocked,
|
||||||
@ -66,11 +68,11 @@ pub fn init(manager: *LockManager) !void {
|
|||||||
errdefer timer.remove();
|
errdefer timer.remove();
|
||||||
|
|
||||||
manager.* = .{
|
manager.* = .{
|
||||||
|
.wlr_manager = try wlr.SessionLockManagerV1.create(server.wl_server),
|
||||||
.lock_surfaces_timer = timer,
|
.lock_surfaces_timer = timer,
|
||||||
};
|
};
|
||||||
|
|
||||||
const wlr_manager = try wlr.SessionLockManagerV1.create(server.wl_server);
|
manager.wlr_manager.events.new_lock.add(&manager.new_lock);
|
||||||
wlr_manager.events.new_lock.add(&manager.new_lock);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn deinit(manager: *LockManager) void {
|
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,
|
output_layout: *wlr.OutputLayout,
|
||||||
layout_change: wl.Listener(*wlr.OutputLayout) = wl.Listener(*wlr.OutputLayout).init(handleLayoutChange),
|
layout_change: wl.Listener(*wlr.OutputLayout) = wl.Listener(*wlr.OutputLayout).init(handleLayoutChange),
|
||||||
|
|
||||||
|
presentation: *wlr.Presentation,
|
||||||
|
xdg_output_manager: *wlr.XdgOutputManagerV1,
|
||||||
|
|
||||||
output_manager: *wlr.OutputManagerV1,
|
output_manager: *wlr.OutputManagerV1,
|
||||||
manager_apply: wl.Listener(*wlr.OutputConfigurationV1) =
|
manager_apply: wl.Listener(*wlr.OutputConfigurationV1) =
|
||||||
wl.Listener(*wlr.OutputConfigurationV1).init(handleManagerApply),
|
wl.Listener(*wlr.OutputConfigurationV1).init(handleManagerApply),
|
||||||
@ -128,8 +131,6 @@ pub fn init(root: *Root) !void {
|
|||||||
const outputs = try interactive_content.createSceneTree();
|
const outputs = try interactive_content.createSceneTree();
|
||||||
const override_redirect = if (build_options.xwayland) 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);
|
const presentation = try wlr.Presentation.create(server.wl_server, server.backend);
|
||||||
scene.setPresentation(presentation);
|
scene.setPresentation(presentation);
|
||||||
|
|
||||||
@ -164,6 +165,9 @@ pub fn init(root: *Root) !void {
|
|||||||
.output_layout = output_layout,
|
.output_layout = output_layout,
|
||||||
.all_outputs = undefined,
|
.all_outputs = undefined,
|
||||||
.active_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),
|
.output_manager = try wlr.OutputManagerV1.create(server.wl_server),
|
||||||
.power_manager = try wlr.OutputPowerManagerV1.create(server.wl_server),
|
.power_manager = try wlr.OutputPowerManagerV1.create(server.wl_server),
|
||||||
.gamma_control_manager = try wlr.GammaControlManagerV1.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 build_options = @import("build_options");
|
||||||
const std = @import("std");
|
const std = @import("std");
|
||||||
|
const assert = std.debug.assert;
|
||||||
const wlr = @import("wlroots");
|
const wlr = @import("wlroots");
|
||||||
const wl = @import("wayland").server.wl;
|
const wl = @import("wayland").server.wl;
|
||||||
|
|
||||||
@ -54,34 +55,33 @@ session: ?*wlr.Session,
|
|||||||
renderer: *wlr.Renderer,
|
renderer: *wlr.Renderer,
|
||||||
allocator: *wlr.Allocator,
|
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,
|
compositor: *wlr.Compositor,
|
||||||
|
subcompositor: *wlr.Subcompositor,
|
||||||
|
cursor_shape_manager: *wlr.CursorShapeManagerV1,
|
||||||
|
|
||||||
xdg_shell: *wlr.XdgShell,
|
xdg_shell: *wlr.XdgShell,
|
||||||
new_xdg_surface: wl.Listener(*wlr.XdgSurface) =
|
|
||||||
wl.Listener(*wlr.XdgSurface).init(handleNewXdgSurface),
|
|
||||||
|
|
||||||
xdg_decoration_manager: *wlr.XdgDecorationManagerV1,
|
xdg_decoration_manager: *wlr.XdgDecorationManagerV1,
|
||||||
new_toplevel_decoration: wl.Listener(*wlr.XdgToplevelDecorationV1) =
|
|
||||||
wl.Listener(*wlr.XdgToplevelDecorationV1).init(handleNewToplevelDecoration),
|
|
||||||
|
|
||||||
layer_shell: *wlr.LayerShellV1,
|
layer_shell: *wlr.LayerShellV1,
|
||||||
new_layer_surface: wl.Listener(*wlr.LayerSurfaceV1) =
|
xdg_activation: *wlr.XdgActivationV1,
|
||||||
wl.Listener(*wlr.LayerSurfaceV1).init(handleNewLayerSurface),
|
|
||||||
|
|
||||||
xwayland: if (build_options.xwayland) ?*wlr.Xwayland else void = if (build_options.xwayland) null,
|
data_device_manager: *wlr.DataDeviceManager,
|
||||||
new_xwayland_surface: if (build_options.xwayland) wl.Listener(*wlr.XwaylandSurface) else void =
|
primary_selection_manager: *wlr.PrimarySelectionDeviceManagerV1,
|
||||||
if (build_options.xwayland) wl.Listener(*wlr.XwaylandSurface).init(handleNewXwaylandSurface),
|
data_control_manager: *wlr.DataControlManagerV1,
|
||||||
|
|
||||||
|
export_dmabuf_manager: *wlr.ExportDmabufManagerV1,
|
||||||
|
screencopy_manager: *wlr.ScreencopyManagerV1,
|
||||||
|
|
||||||
foreign_toplevel_manager: *wlr.ForeignToplevelManagerV1,
|
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,
|
input_manager: InputManager,
|
||||||
root: Root,
|
root: Root,
|
||||||
config: Config,
|
config: Config,
|
||||||
@ -91,6 +91,21 @@ layout_manager: LayoutManager,
|
|||||||
idle_inhibit_manager: IdleInhibitManager,
|
idle_inhibit_manager: IdleInhibitManager,
|
||||||
lock_manager: LockManager,
|
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 {
|
pub fn init(server: *Server, runtime_xwayland: bool) !void {
|
||||||
// We intentionally don't try to prevent memory leaks on error in this function
|
// 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.
|
// 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,
|
.renderer = renderer,
|
||||||
.allocator = try wlr.Allocator.autocreate(backend, 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,
|
.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_shell = try wlr.XdgShell.create(wl_server, 5),
|
||||||
.xdg_decoration_manager = try wlr.XdgDecorationManagerV1.create(wl_server),
|
.xdg_decoration_manager = try wlr.XdgDecorationManagerV1.create(wl_server),
|
||||||
.layer_shell = try wlr.LayerShellV1.create(wl_server, 4),
|
.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),
|
.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(),
|
.config = try Config.init(),
|
||||||
|
|
||||||
@ -134,27 +166,16 @@ pub fn init(server: *Server, runtime_xwayland: bool) !void {
|
|||||||
.lock_manager = undefined,
|
.lock_manager = undefined,
|
||||||
};
|
};
|
||||||
|
|
||||||
try renderer.initWlShm(wl_server);
|
|
||||||
if (renderer.getDmabufFormats() != null and renderer.getDrmFd() >= 0) {
|
if (renderer.getDmabufFormats() != null and renderer.getDrmFd() >= 0) {
|
||||||
// wl_drm is a legacy interface and all clients should switch to linux_dmabuf.
|
// 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
|
// However, enough widely used clients still rely on wl_drm that the pragmatic option
|
||||||
// is to keep it around for the near future.
|
// is to keep it around for the near future.
|
||||||
// TODO remove wl_drm support
|
// 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) {
|
if (build_options.xwayland and runtime_xwayland) {
|
||||||
server.xwayland = try wlr.Xwayland.create(wl_server, compositor, false);
|
server.xwayland = try wlr.Xwayland.create(wl_server, compositor, false);
|
||||||
server.xwayland.?.events.new_surface.add(&server.new_xwayland_surface);
|
server.xwayland.?.events.new_surface.add(&server.new_xwayland_surface);
|
||||||
@ -244,7 +265,85 @@ 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;
|
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
|
/// Handle SIGINT and SIGTERM by gracefully stopping the server
|
||||||
|
Loading…
Reference in New Issue
Block a user