From 2ec202dfa1df48189c3cdc70b4ac302ae7c0e06f Mon Sep 17 00:00:00 2001 From: Sergio Costas Date: Sat, 13 Jan 2024 16:18:13 +0000 Subject: [PATCH] client access: add support for snap permissions This patch adds to wireplumber code to manage the Snap audio permissions. SNAP containers have two main "audio" rules: * audio-playback: the applications inside the container can send audio samples into a sink * audio-record: the applications inside the container can get audio samples from a source Also, old SNAP containers had the "pulseaudio" rule, which just exposed the pulseaudio socket directly, without limits. This is similar to the current Flatpak audio permissions. In the pulseaudio days, an specific pulseaudio module was used that checked the permissions given to the application and allowed or forbide access to the pulseaudio operations. With the change to pipewire, this functionality must be implemented in pipewire-pulse and wireplumber to guarantee the sandbox security. The current code checks for the presence of the pipewire.snap.id property in a client, in which case it will read the pipewire.snap.audio.playback and pipewire.snap.audio.record properties, and allow or deny access to that client to the nodes with Audio/Sink or Audio/Source media.class property. See !567 and pipewire!1779 --- src/config/wireplumber.conf | 7 ++- src/scripts/client/access-snap.lua | 87 ++++++++++++++++++++++++++++++ 2 files changed, 93 insertions(+), 1 deletion(-) create mode 100644 src/scripts/client/access-snap.lua diff --git a/src/config/wireplumber.conf b/src/config/wireplumber.conf index c408f1dc..0904569e 100644 --- a/src/config/wireplumber.conf +++ b/src/config/wireplumber.conf @@ -406,10 +406,15 @@ wireplumber.components = [ provides = script.client.access-portal requires = [ support.portal-permissionstore ] } + { + name = client/access-snap.lua, type = script/lua + provides = script.client.access-snap + } { type = virtual, provides = policy.client.access wants = [ script.client.access-default, - script.client.access-portal ] + script.client.access-portal, + script.client.access-snap ] } ## Device profile selection hooks diff --git a/src/scripts/client/access-snap.lua b/src/scripts/client/access-snap.lua new file mode 100644 index 00000000..ae0016c3 --- /dev/null +++ b/src/scripts/client/access-snap.lua @@ -0,0 +1,87 @@ +-- Manage snap audio permissions +-- +-- Copyright © 2023 Canonical Ltd. +-- @author Sergio Costas Rodriguez +-- +-- SPDX-License-Identifier: MIT + +function removeClientPermissionsForOtherClients (client) + -- Remove access to any other clients, but allow all the process of the + -- same snap to access their elements + local client_id = client.properties["pipewire.snap.id"] + for snap_client in clients_snap:iterate() do + local snap_client_id = snap_client.properties["pipewire.snap.id"] + if snap_client_id ~= client_id then + client:update_permissions { [snap_client["bound-id"]] = "-" } + end + end + for no_snap_client in clients_no_snap:iterate() do + client:update_permissions { [no_snap_client["bound-id"]] = "-" } + end +end + +function updateClientPermissions (client) + -- Remove access to Audio/Sources and Audio/Sinks based on snap permissions + for node in nodes_om:iterate() do + local node_id = node["bound-id"] + local property = "pipewire.snap.audio.playback" + + if node.properties["media.class"] == "Audio/Source" then + property = "pipewire.snap.audio.record" + end + + if client.properties[property] ~= "true" then + client:update_permissions { [node_id] = "-" } + end + end +end + +clients_snap = ObjectManager { + Interest { + type = "client", + Constraint { "pipewire.snap.id", "+", type = "pw"}, + } +} + +clients_no_snap = ObjectManager { + Interest { + type = "client", + Constraint { "pipewire.snap.id", "-", type = "pw"}, + } +} + +nodes_om = ObjectManager { + Interest { + type = "node", + Constraint { "media.class", "matches", "Audio/*"} + } +} + +clients_snap:connect("object-added", function (om, client) + -- If a new snap client is added, adjust its permissions + updateClientPermissions (client) + removeClientPermissionsForOtherClients (client) +end) + +clients_no_snap:connect("object-added", function (om, client) + -- If a new, non-snap client is added, + -- remove access to it from other snaps + client_id = client["bound-id"] + for snap_client in clients_snap:iterate() do + if client.properties["pipewire.snap.id"] ~= nil then + snap_client:update_permissions { [client_id] = "-" } + end + end +end) + +nodes_om:connect("object-added", function (om, node) + -- If a new Audio/Sink or Audio/Source node is added, + -- adjust the permissions in the snap clients + for client in clients_snap:iterate() do + updateClientPermissions (client) + end +end) + +clients_snap:activate() +clients_no_snap:activate() +nodes_om:activate() \ No newline at end of file