From 5e20a2d5704586f89f48d7575e63f9dac621b89f Mon Sep 17 00:00:00 2001 From: Sergio Costas Rodriguez Date: Wed, 22 Nov 2023 11:48:14 +0100 Subject: [PATCH] Add missing files Accidentally, I forgot to add snap-policy.* files. --- .../module-protocol-pulse/snap-policy.c | 193 ++++++++++++++++++ .../module-protocol-pulse/snap-policy.h | 22 ++ 2 files changed, 215 insertions(+) create mode 100644 src/modules/module-protocol-pulse/snap-policy.c create mode 100644 src/modules/module-protocol-pulse/snap-policy.h diff --git a/src/modules/module-protocol-pulse/snap-policy.c b/src/modules/module-protocol-pulse/snap-policy.c new file mode 100644 index 000000000..70bf98499 --- /dev/null +++ b/src/modules/module-protocol-pulse/snap-policy.c @@ -0,0 +1,193 @@ +/* PipeWire */ +/* SPDX-FileCopyrightText: Copyright © 2022 Canonical Ltd. */ +/* SPDX-License-Identifier: MIT */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include +#include +#include +#include "client.h" +#include +#include +#include "snap-policy.h" +#include + +#define SNAP_LABEL_PREFIX "snap." + +static gboolean check_is_same_snap(gchar *snap1, gchar *snap2) { + // Checks if two apparmor labels belong to the same snap + g_auto(GStrv) strings1 = NULL; + g_auto(GStrv) strings2 = NULL; + + if (!g_str_has_prefix(snap1, SNAP_LABEL_PREFIX)) { + return FALSE; + } + if (!g_str_has_prefix(snap2, SNAP_LABEL_PREFIX)) { + return FALSE; + } + strings1 = g_strsplit(snap1, ".", 3); + strings2 = g_strsplit(snap2, ".", 3); + + if (g_str_equal(strings1[1], strings2[1]) && (strings1[1] != NULL)) { + return TRUE; + } + return FALSE; +} + +pw_sandbox_access_t pw_snap_get_audio_permissions(struct client *client, int fd, char **app_id) +{ + g_autofree gchar* aa_label = NULL; + gchar* snap_id = NULL; + gchar* snap_confinement = NULL; + gchar *separator = NULL; + g_autofree gchar *aacon = NULL; + gchar *aamode = NULL; + const char *context = NULL; + g_autoptr(SnapdClient) snapdclient = NULL; + g_autoptr(GPtrArray) plugs = NULL; + gboolean retv; + pw_sandbox_access_t permissions = PW_SANDBOX_ACCESS_NONE; + pw_sandbox_access_t add_permission = PW_SANDBOX_ACCESS_NONE; + SnapdPlug **plug = NULL; + GPtrArray *slots = NULL; + SnapdSlotRef **slot = NULL; + GError *error = NULL; + int exit_code; + + *app_id = g_strdup("unknown"); + if (client == NULL) { + pw_log_warn("Called snap_get_audio_permissions with NULL parameter."); + return PW_SANDBOX_ACCESS_NONE; + } + + if (aa_getpeercon(fd, &aa_label, &snap_confinement) == -1) { + if (errno == EINVAL) { + // if apparmor isn't enabled, we can safely assume that there are no SNAPs in the system + return PW_SANDBOX_ACCESS_NOT_A_SANDBOX; + } + pw_log_warn("snap_get_audio_permissions: failed to get the AppArmor info."); + return PW_SANDBOX_ACCESS_NONE; + } + if (!g_str_has_prefix(aa_label, SNAP_LABEL_PREFIX)) { + // not a SNAP. + pw_log_info("snap_get_audio_permissions: not an snap."); + return PW_SANDBOX_ACCESS_NOT_A_SANDBOX; + } + + snap_id = g_strdup(aa_label + strlen(SNAP_LABEL_PREFIX)); + separator = strchr(snap_id, '.'); + if (separator == NULL) { + pw_log_info("snap_get_audio_permissions: aa_label has only one dot; not a valid ID."); + return PW_SANDBOX_ACCESS_NONE; + } + *separator = 0; + g_free(*app_id); + *app_id = snap_id; + + // it's a "classic" or a "devmode" confinement snap, so we give it full access + if (g_str_equal (snap_confinement, "complain")) { + return PW_SANDBOX_ACCESS_ALL; + } + + snapdclient = snapd_client_new(); + if (snapdclient == NULL) { + pw_log_warn("snap_get_audio_permissions: error creating SnapdClient object."); + return PW_SANDBOX_ACCESS_NONE; + } + + if (aa_getcon(&aacon, &aamode) == -1) { + pw_log_warn("snap_get_audio_permissions: error checking if pipewire-pulse is inside a snap."); + return PW_SANDBOX_ACCESS_NONE; // failed to get access to apparmor + } + + // If pipewire-pulse is inside a snap, use snapctl API + if (g_str_has_prefix(aacon, SNAP_LABEL_PREFIX)) { + // If the snap wanting to get access is the same that contains pipewire, + // give to it full access. + if (check_is_same_snap(aacon, aa_label)) + return PW_SANDBOX_ACCESS_ALL; + snapd_client_set_socket_path (snapdclient, "/run/snapd-snap.socket"); + + /* Take context from the environment if available */ + context = g_getenv ("SNAP_COOKIE"); + if (!context) + context = ""; + + char *snapctl_command[] = { "is-connected", "--apparmor-label", aa_label, "pulseaudio", NULL }; + if (!snapd_client_run_snapctl2_sync (snapdclient, context, (char **) snapctl_command, NULL, NULL, &exit_code, NULL, &error)) { + pw_log_warn("snap_get_audio_permissions: error summoning snapctl2 for pulseaudio interface: %s", error->message); + return PW_SANDBOX_ACCESS_NONE; + } + if (exit_code != 1) { + // 0 = Connected + // 10 = Classic environment + // 11 = Not a snap + return PW_SANDBOX_ACCESS_ALL; + } + char *snapctl_command2[] = { "is-connected", "--apparmor-label", aa_label, "audio-record", NULL }; + if (!snapd_client_run_snapctl2_sync (snapdclient, context, (char **) snapctl_command2, NULL, NULL, &exit_code, NULL, &error)) { + pw_log_warn("snap_get_audio_permissions: error summoning snapctl2 for audio-record interface: %s", error->message); + return PW_SANDBOX_ACCESS_NONE; + } + if (exit_code == 1) { + return PW_SANDBOX_ACCESS_PLAYBACK; + } + return PW_SANDBOX_ACCESS_ALL; + } + + retv = snapd_client_get_connections2_sync(snapdclient, + SNAPD_GET_CONNECTIONS_FLAGS_NONE, + snap_id, + NULL, + NULL, + NULL, + &plugs, + NULL, + NULL, + NULL); + if (retv == FALSE) { + pw_log_warn("Failed to get Snap connections for snap %s\n", snap_id); + return PW_SANDBOX_ACCESS_NONE; + } + if (plugs == NULL) { + pw_log_warn("Failed to get Snap connections for snap %s\n", snap_id); + return PW_SANDBOX_ACCESS_NONE; + } + if (plugs->pdata == NULL) { + pw_log_warn("Failed to get Snap connections for snap %s\n", snap_id); + return PW_SANDBOX_ACCESS_NONE; + } + + plug = (SnapdPlug **)plugs->pdata; + for (guint p = 0; p < plugs->len; p++, plug++) { + const gchar *plug_name = snapd_plug_get_name(*plug); + if (g_str_equal("audio-record", plug_name)) { + add_permission = PW_SANDBOX_ACCESS_RECORD; + } else if (g_str_equal("audio-playback", plug_name)) { + add_permission = PW_SANDBOX_ACCESS_PLAYBACK; + } else if (g_str_equal("pulseaudio", plug_name)) { + add_permission = PW_SANDBOX_ACCESS_ALL; + } else { + continue; + } + slots = snapd_plug_get_connected_slots(*plug); + if (slots == NULL) + continue; + slot = (SnapdSlotRef**) slots->pdata; + + for (guint q = 0; q < slots->len; q++, slot++) { + const gchar *slot_name = snapd_slot_ref_get_slot (*slot); + const gchar *snap_name = snapd_slot_ref_get_snap (*slot); + if (g_str_equal (snap_name, "snapd") && + g_str_equal (slot_name, plug_name)) + permissions |= add_permission; + } + } + + return permissions; +} diff --git a/src/modules/module-protocol-pulse/snap-policy.h b/src/modules/module-protocol-pulse/snap-policy.h new file mode 100644 index 000000000..0c152d315 --- /dev/null +++ b/src/modules/module-protocol-pulse/snap-policy.h @@ -0,0 +1,22 @@ +/* PipeWire */ +/* SPDX-FileCopyrightText: Copyright © 2022 Canonical Ltd. */ +/* SPDX-License-Identifier: MIT */ + +#ifndef _SNAP_POLICY_H_ +#define _SNAP_POLICY_H_ + +typedef enum _pw_sandbox_access { + PW_SANDBOX_ACCESS_NONE = 0, + PW_SANDBOX_ACCESS_NOT_A_SANDBOX = 1 << 0, + PW_SANDBOX_ACCESS_RECORD = 1 << 1, + PW_SANDBOX_ACCESS_PLAYBACK = 1 << 2, + PW_SANDBOX_ACCESS_ALL = (PW_SANDBOX_ACCESS_PLAYBACK | PW_SANDBOX_ACCESS_RECORD), +} pw_sandbox_access_t; + +#define PW_KEY_SNAP_ID "pipewire.snap.id" +#define PW_KEY_SNAP_PLAYBACK_ALLOWED "pipewire.snap.audio.playback" +#define PW_KEY_SNAP_RECORD_ALLOWED "pipewire.snap.audio.record" + +pw_sandbox_access_t pw_snap_get_audio_permissions(struct client *client, int fd, char **app_id); + +#endif // _SNAP_POLICY_H_