PipeWire SecurityContext appears to only restrict access via pipewire-pulse #29

Closed
opened 2025-12-10 04:42:50 +09:00 by ophestra · 2 comments
Owner

The documentation seemed to imply that SecurityContext provided security similar to Wayland security-context-v1, with a similar interface and plenty of references to Wayland documentation. Testing shows that while access through pipewire-pulse is indeed restricted, protocol native is entirely unaffected and behaves as if no SecurityContext was attached at all. It was, in fact, even possible to destroy the SecurityContext object on the PipeWire server by targeting its global id in a Registry::Destroy message.

If this is confirmed, and nothing can be done, Hakurei will need to expose only the pipewire-pulse socket via the host filesystem, analyse its behaviour to consider whether it is vulnerable to a confused deputy attack (protocol native has full access), and potentially also apply hardening via Landlock or similar to reduce access to the filesystem.

The [documentation](https://docs.pipewire.org/group__pw__security__context.html) seemed to imply that `SecurityContext` provided security similar to Wayland `security-context-v1`, with a similar interface and plenty of references to Wayland documentation. Testing shows that while access through `pipewire-pulse` is indeed restricted, protocol native is entirely unaffected and behaves as if no `SecurityContext` was attached at all. It was, in fact, even possible to [destroy the `SecurityContext` object on the PipeWire server](https://gitlab.freedesktop.org/pipewire/pipewire/-/issues/5023) by targeting its global id in a `Registry::Destroy` message. If this is confirmed, and nothing can be done, Hakurei will need to expose only the `pipewire-pulse` socket via the host filesystem, analyse its behaviour to consider whether it is vulnerable to a confused deputy attack (protocol native has full access), and potentially also apply hardening via Landlock or similar to reduce access to the filesystem.
ophestra added the
Kind
Security
Priority
Critical
labels 2025-12-10 04:43:06 +09:00
Author
Owner

Permissions are set up by code in an entirely separate repository, where read and execute permissions are blindly set on everything for clients with PW_KEY_ACCESS set to "restricted":

		if (client->obj->info == NULL || client->obj->info->props == NULL ||
		    (str = spa_dict_lookup(client->obj->info->props, PW_KEY_ACCESS)) == NULL)
			return;

		if (spa_streq(str, "flatpak")) {
			if ((str = spa_dict_lookup(client->obj->info->props, PW_KEY_MEDIA_CATEGORY)) != NULL &&
			    (spa_streq(str, "Manager"))) {
				/* FIXME, use permission store to check if this app is allowed to
				 * be a manager app */
				perms = PW_PERM_ALL;
			} else {
				/* limited access for everything else */
				perms = PW_PERM_R | PW_PERM_X;
			}
		} else if (spa_streq(str, "restricted")) {
			perms = PW_PERM_R | PW_PERM_X;
		} else
			return;

		pw_log_info("%p: flatpak client %d granted 0x%08x permissions"
				, impl, client->id, perms);
		permissions[0] = PW_PERMISSION_INIT(PW_ID_ANY, perms);
		pw_client_update_permissions((struct pw_client*)client->obj->obj.proxy,
				1, permissions);
		client->active = true;

Registry::Destroy is implemented in src/pipewire/impl-core.c:

        permissions = pw_global_get_permissions(global, client);

        if (!PW_PERM_IS_R(permissions))
                goto error_no_id;

        if (id == PW_ID_CORE || !PW_PERM_IS_X(permissions))
                goto error_not_allowed;

        pw_log_debug("global %p: destroy global id %d", global, id);

        pw_global_destroy(global);

This means that, with the exception of PW_ID_CORE, the client is allowed to destroy any other object, even from behind SecurityContext.

Permissions are set up by code in an [entirely separate repository](https://gitlab.freedesktop.org/pipewire/media-session/-/blob/7376c167f1ad20b3968d7a5d6c395a8b698d31d8/src/access-flatpak.c#L82-121), where read and execute permissions are blindly set on everything for clients with `PW_KEY_ACCESS` set to `"restricted"`: ```c if (client->obj->info == NULL || client->obj->info->props == NULL || (str = spa_dict_lookup(client->obj->info->props, PW_KEY_ACCESS)) == NULL) return; if (spa_streq(str, "flatpak")) { if ((str = spa_dict_lookup(client->obj->info->props, PW_KEY_MEDIA_CATEGORY)) != NULL && (spa_streq(str, "Manager"))) { /* FIXME, use permission store to check if this app is allowed to * be a manager app */ perms = PW_PERM_ALL; } else { /* limited access for everything else */ perms = PW_PERM_R | PW_PERM_X; } } else if (spa_streq(str, "restricted")) { perms = PW_PERM_R | PW_PERM_X; } else return; pw_log_info("%p: flatpak client %d granted 0x%08x permissions" , impl, client->id, perms); permissions[0] = PW_PERMISSION_INIT(PW_ID_ANY, perms); pw_client_update_permissions((struct pw_client*)client->obj->obj.proxy, 1, permissions); client->active = true; ``` `Registry::Destroy` is implemented in [src/pipewire/impl-core.c](https://gitlab.freedesktop.org/pipewire/pipewire/-/blob/d329dac6ba7d32dacb13fd358b5360a59f7aac27/src/pipewire/impl-core.c#L82-131): ```c permissions = pw_global_get_permissions(global, client); if (!PW_PERM_IS_R(permissions)) goto error_no_id; if (id == PW_ID_CORE || !PW_PERM_IS_X(permissions)) goto error_not_allowed; pw_log_debug("global %p: destroy global id %d", global, id); pw_global_destroy(global); ``` This means that, with the exception of `PW_ID_CORE`, the client is allowed to destroy any other object, even from behind `SecurityContext`.
ophestra added the
Reviewed
Confirmed
label 2025-12-13 20:18:37 +09:00
cat referenced this issue from a commit 2025-12-15 12:29:37 +09:00
Author
Owner

The PipeWire socket is no longer directly exposed due to fundamental and unfixable flaws in the SecurityContext machinery. The current implementation now maintains a pipewire-pulse container for each instance via the shim. A security update will be tagged after all automated tests pass and manual testing is completed.

The PipeWire socket is no longer directly exposed due to fundamental and unfixable flaws in the `SecurityContext` machinery. The current implementation now maintains a `pipewire-pulse` container for each instance via the shim. A security update will be tagged after all automated tests pass and manual testing is completed.
cat closed this issue 2025-12-15 20:35:57 +09:00
Sign in to join this conversation.