Skip to content
Open
1 change: 1 addition & 0 deletions policy/modules/apps/pipewire.fc
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
# PipeWire daemon executable.
/usr/bin/pipewire -- gen_context(system_u:object_r:pipewire_exec_t,s0)
/usr/bin/wpctl -- gen_context(system_u:object_r:pipewire_exec_t,s0)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This does not belong here as its part of wireplumber and even if then pipewire_client_exec_t would probably be much more appropriate than pipewire_exec_t.


# pw-cat is a standalone client utility (plays/captures audio via a running
# PipeWire daemon); it runs in pipewire_client_t, not pipewire_t.
Expand Down
9 changes: 9 additions & 0 deletions policy/modules/apps/wireplumber.fc
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
HOME_DIR/\.config/wireplumber(/.*)? gen_context(system_u:object_r:wireplumber_home_t,s0)
HOME_DIR/\.local/share/wireplumber(/.*)? gen_context(system_u:object_r:wireplumber_home_t,s0)

/run/wireplumber(/.*)? gen_context(system_u:object_r:wireplumber_runtime_t,s0)
/run/user/%{USERID}/wireplumber(/.*)? gen_context(system_u:object_r:wireplumber_runtime_t,s0)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why did you add support for wireplumber_runtime_t sockets? Who calls wireplumber_stream_connect() and why?


/usr/bin/wireplumber -- gen_context(system_u:object_r:wireplumber_exec_t,s0)

/var/lib/wireplumber(/.*)? gen_context(system_u:object_r:wireplumber_var_lib_t,s0)
45 changes: 45 additions & 0 deletions policy/modules/apps/wireplumber.if
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
## <summary>Policy for WirePlumber, the PipeWire session manager.</summary>

########################################
## <summary>
## Connect to WirePlumber over a unix stream socket.
## </summary>
## <param name="domain">
## <summary>
## Domain allowed access.
## </summary>
## </param>
#
interface(`wireplumber_stream_connect',`
gen_require(`
type wireplumber_t, wireplumber_runtime_t;
')

files_search_runtime($1)
stream_connect_pattern($1, wireplumber_runtime_t, wireplumber_runtime_t, wireplumber_t)
')

########################################
## <summary>
## Role access for WirePlumber in user-service mode.
## </summary>
## <param name="role">
## <summary>
## Role allowed access.
## </summary>
## </param>
## <param name="domain">
## <summary>
## User domain for the role.
## </summary>
## </param>
#
interface(`wireplumber_role',`
gen_require(`
type wireplumber_t, wireplumber_exec_t;
attribute_role wireplumber_roles;
')

roleattribute $1 wireplumber_roles;
domtrans_pattern($2, wireplumber_exec_t, wireplumber_t)
')
138 changes: 138 additions & 0 deletions policy/modules/apps/wireplumber.te
Original file line number Diff line number Diff line change
@@ -0,0 +1,138 @@
policy_module(wireplumber)

########################################
#
# Declarations
#

attribute_role wireplumber_roles;

type wireplumber_t;
type wireplumber_exec_t;
ifdef(`wireplumber_system_service',`
init_daemon_domain(wireplumber_t, wireplumber_exec_t)
',`
userdom_user_application_domain(wireplumber_t, wireplumber_exec_t)
role wireplumber_roles types wireplumber_t;
')

type wireplumber_home_t alias wireplumber_var_lib_t;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

what is the point of that typealias? wireplumber_var_lib_t is not referenced anywhere?

userdom_user_home_content(wireplumber_home_t)

type wireplumber_runtime_t;
files_runtime_file(wireplumber_runtime_t)

type wireplumber_tmpfs_t;
userdom_user_tmpfs_file(wireplumber_tmpfs_t)

########################################
#
# Local policy
#

allow wireplumber_t self:process { getsched setsched signal signull };
allow wireplumber_t self:capability sys_nice;
allow wireplumber_t self:fifo_file rw_fifo_file_perms;
allow wireplumber_t self:unix_stream_socket { create_stream_socket_perms connectto };
allow wireplumber_t self:unix_dgram_socket create_socket_perms;

# Bluetooth socket (for BlueZ audio routing)
allow wireplumber_t self:bluetooth_socket { create bind setopt listen };

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

create_stream_socket_perms


# netlink kobject uevent socket (for udev device monitoring)
allow wireplumber_t self:netlink_kobject_uevent_socket { create bind setopt getattr getopt };

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

create_socket_perms


dev_read_sysfs(wireplumber_t)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Eventually would be nice if we could create some order in /sys and then it would be useful to know what exactly wireplumber_t is accessing below /sys. Then again it access a lot there so maybe best to not go there yet.


# /dev/snd inotify watch for device monitoring
dev_watch_dev_dirs(wireplumber_t)

# filesystem getattr
dev_getattr_fs(wireplumber_t)
fs_getattr_xattr_fs(wireplumber_t)

# v4l device getattr (video device enumeration)
dev_dontaudit_getattr_video_dev(wireplumber_t)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Probably best to just let it do its thing?

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

root@nimbus:~# sesearch -A -s wireplumber.subj -t v4l.nodedev -ds
allow wireplumber.subj v4l.nodedev:chr_file { append getattr ioctl lock open read watch write };


files_read_usr_files(wireplumber_t)
files_map_usr_files(wireplumber_t)
files_read_etc_files(wireplumber_t)

# /etc/machine-id
files_read_etc_runtime_files(wireplumber_t)

kernel_read_system_state(wireplumber_t)

domain_dontaudit_read_all_domains_state(wireplumber_t)

logging_send_syslog_msg(wireplumber_t)

miscfiles_read_localization(wireplumber_t)

# Connect to PipeWire runtime socket
optional_policy(`
pipewire_stream_connect(wireplumber_t)
')

# Use file descriptors passed from PipeWire (e.g. memfd)
optional_policy(`
pipewire_use_fds(wireplumber_t)
')

# Access PipeWire tmpfs shared memory (memfd zero-copy buffers)
optional_policy(`
pipewire_rw_tmpfs_files(wireplumber_t)

@kcinimod-defensec kcinimod-defensec Jun 29, 2026

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would argue that wireplumber_t should probably be associated with pipewire_client instead.

wireplumber_tmpfs_t would then ideally also be associated with pipewire_client_tmpfs_type

Also again. These accesses are inherited. Open permission should not be needed. I believe that if one just consistently omits open when its not needed that this is worth the trouble in the longer run.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So one could have a macro like:

pipewire_client(wireplumber_t, wireplumber_tmpfs_t)

Something like (untested):

interface pipewire_client(`,'
gen_require(` attribute pipewire_client_type, pipewire_client_tmpfs_type; ')
typeattribute $1 pipewire_client_type;
typeattribute $2 pipewire_client_tmpfs_type;
')

')

# Runtime directory management - differs between system and user service
ifdef(`wireplumber_system_service',`
files_var_lib_filetrans(wireplumber_t, wireplumber_home_t, { dir file })
files_runtime_filetrans(wireplumber_t, wireplumber_runtime_t, { dir file sock_file })
',`
userdom_user_runtime_filetrans(wireplumber_t, wireplumber_runtime_t, dir)
userdom_user_home_dir_filetrans(wireplumber_t, wireplumber_home_t, dir)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There are no wireplumber_home_t dirs directly under user_home_dir_t dirs. This rule does not correspond to the wireplumber_home_t fc specs above.

')

# tmpfs shared memory (memfd for zero-copy audio buffers)
mmap_manage_files_pattern(wireplumber_t, wireplumber_tmpfs_t, wireplumber_tmpfs_t)
fs_tmpfs_filetrans(wireplumber_t, wireplumber_tmpfs_t, file)

# udev runtime data (/run/udev)
udev_search_runtime(wireplumber_t)
udev_read_runtime_files(wireplumber_t)

# systemd sessions (/run/systemd/sessions)
systemd_read_logind_sessions_files(wireplumber_t)
systemd_watch_logind_sessions_dirs(wireplumber_t)

optional_policy(`
dbus_system_bus_client(wireplumber_t)
dbus_connect_system_bus(wireplumber_t)

ifdef(`wireplumber_system_service',`
dbus_system_domain(wireplumber_t, wireplumber_exec_t)
')
')

# Allow ModemManager D-Bus replies to wireplumber_t
optional_policy(`
modemmanager_dbus_chat(wireplumber_t)
')

# Allow BlueZ D-Bus signals to wireplumber_t
optional_policy(`
bluetooth_dbus_chat(wireplumber_t)
')

# Allow oFono D-Bus communication (telephony)
optional_policy(`
ofono_dbus_chat(wireplumber_t)
')

optional_policy(`
rtkit_scheduled(wireplumber_t)
')

optional_policy(`
systemd_read_journal_files(wireplumber_t)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

questionable. Have AVC denials?

')
26 changes: 26 additions & 0 deletions policy/modules/services/ofono.if
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,29 @@
##
## https://git.kernel.org/pub/scm/network/ofono/ofono.git
## </desc>

########################################
## <summary>
## Allow D-Bus communication with oFono.
## </summary>
## <desc>
## <p>
## Allow the specified domain to send and receive D-Bus
## messages to/from oFono for telephony audio routing.
## </p>
## </desc>
## <param name="domain">
## <summary>
## Domain allowed access.
## </summary>
## </param>
#
interface(`ofono_dbus_chat',`
gen_require(`
type ofono_t;
class dbus send_msg;
')

allow $1 ofono_t:dbus send_msg;
allow ofono_t $1:dbus send_msg;
')