github.com/kubiko/snapd@v0.0.0-20201013125620-d4f3094d9ddf/interfaces/builtin/audio_playback.go (about)

     1  // -*- Mode: Go; indent-tabs-mode: t -*-
     2  
     3  /*
     4   * Copyright (C) 2018 Canonical Ltd
     5   *
     6   * This program is free software: you can redistribute it and/or modify
     7   * it under the terms of the GNU General Public License version 3 as
     8   * published by the Free Software Foundation.
     9   *
    10   * This program is distributed in the hope that it will be useful,
    11   * but WITHOUT ANY WARRANTY; without even the implied warranty of
    12   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    13   * GNU General Public License for more details.
    14   *
    15   * You should have received a copy of the GNU General Public License
    16   * along with this program.  If not, see <http://www.gnu.org/licenses/>.
    17   *
    18   */
    19  
    20  package builtin
    21  
    22  import (
    23  	"github.com/snapcore/snapd/interfaces"
    24  	"github.com/snapcore/snapd/interfaces/apparmor"
    25  	"github.com/snapcore/snapd/interfaces/seccomp"
    26  	"github.com/snapcore/snapd/interfaces/udev"
    27  	"github.com/snapcore/snapd/release"
    28  	"github.com/snapcore/snapd/snap"
    29  )
    30  
    31  // The audio-playback interface is the companion interface to the audio-record
    32  // interface. The design of this interface is based on the idea that the slot
    33  // implementation (eg pulseaudio) is expected to query snapd on if the
    34  // audio-record slot is connected or not and the audio service will mediate
    35  // recording (ie, the rules below allow connecting to the audio service, but do
    36  // not implement enforcement rules; it is up to the audio service to provide
    37  // enforcement). If other audio recording servers require different security
    38  // policy for record and playback (eg, a different socket path), then those
    39  // accesses will be added to this interface.
    40  
    41  const audioPlaybackSummary = `allows audio playback via supporting services`
    42  
    43  const audioPlaybackBaseDeclarationSlots = `
    44    audio-playback:
    45      allow-installation:
    46        slot-snap-type:
    47          - app
    48          - core
    49      deny-connection:
    50        on-classic: false
    51  `
    52  
    53  const audioPlaybackConnectedPlugAppArmor = `
    54  # Allow communicating with pulseaudio service
    55  /{run,dev}/shm/pulse-shm-* mrwk,
    56  
    57  owner /{,var/}run/pulse/ r,
    58  owner /{,var/}run/pulse/native rwk,
    59  owner /{,var/}run/pulse/pid r,
    60  owner /{,var/}run/user/[0-9]*/ r,
    61  owner /{,var/}run/user/[0-9]*/pulse/ rw,
    62  
    63  /run/udev/data/c116:[0-9]* r,
    64  /run/udev/data/+sound:card[0-9]* r,
    65  `
    66  
    67  const audioPlaybackConnectedPlugAppArmorDesktop = `
    68  # Allow communicating with pulseaudio service on the desktop in classic distro.
    69  # Only on desktop do we need access to /etc/pulse for any PulseAudio client
    70  # to read available client side configuration settings. On an Ubuntu Core
    71  # device those things will be stored inside the snap directory.
    72  /etc/pulse/ r,
    73  /etc/pulse/** r,
    74  owner @{HOME}/.pulse-cookie rk,
    75  owner @{HOME}/.config/pulse/cookie rk,
    76  owner /{,var/}run/user/*/pulse/ rwk,
    77  owner /{,var/}run/user/*/pulse/native rwk,
    78  owner /{,var/}run/user/*/pulse/pid r,
    79  `
    80  
    81  const audioPlaybackConnectedPlugSecComp = `
    82  shmctl
    83  `
    84  
    85  const audioPlaybackPermanentSlotAppArmor = `
    86  # When running PulseAudio in system mode it will switch to the at
    87  # build time configured user/group on startup.
    88  capability setuid,
    89  capability setgid,
    90  
    91  capability sys_nice,
    92  capability sys_resource,
    93  
    94  owner @{PROC}/@{pid}/exe r,
    95  /etc/machine-id r,
    96  
    97  # For udev
    98  network netlink raw,
    99  /sys/devices/virtual/dmi/id/sys_vendor r,
   100  /sys/devices/virtual/dmi/id/bios_vendor r,
   101  /sys/**/sound/** r,
   102  
   103  owner /{,var/}run/pulse/ rw,
   104  owner /{,var/}run/pulse/** rwk,
   105  
   106  # Shared memory based communication with clients
   107  /{run,dev}/shm/pulse-shm-* mrwk,
   108  
   109  owner /run/pulse/native/ rwk,
   110  owner /run/user/[0-9]*/ r,
   111  owner /run/user/[0-9]*/pulse/ rw,
   112  `
   113  
   114  const audioPlaybackPermanentSlotSecComp = `
   115  # The following are needed for UNIX sockets
   116  personality
   117  setpriority
   118  bind
   119  listen
   120  accept
   121  accept4
   122  shmctl
   123  # Needed to set root as group for different state dirs
   124  # pulseaudio creates on startup.
   125  setgroups
   126  setgroups32
   127  # libudev
   128  socket AF_NETLINK - NETLINK_KOBJECT_UEVENT
   129  `
   130  
   131  type audioPlaybackInterface struct{}
   132  
   133  func (iface *audioPlaybackInterface) Name() string {
   134  	return "audio-playback"
   135  }
   136  
   137  func (iface *audioPlaybackInterface) StaticInfo() interfaces.StaticInfo {
   138  	return interfaces.StaticInfo{
   139  		Summary:              audioPlaybackSummary,
   140  		ImplicitOnClassic:    true,
   141  		BaseDeclarationSlots: audioPlaybackBaseDeclarationSlots,
   142  	}
   143  }
   144  
   145  func (iface *audioPlaybackInterface) AppArmorConnectedPlug(spec *apparmor.Specification, plug *interfaces.ConnectedPlug, slot *interfaces.ConnectedSlot) error {
   146  	spec.AddSnippet(audioPlaybackConnectedPlugAppArmor)
   147  	if release.OnClassic {
   148  		spec.AddSnippet(audioPlaybackConnectedPlugAppArmorDesktop)
   149  	}
   150  	return nil
   151  }
   152  
   153  func (iface *audioPlaybackInterface) UDevPermanentSlot(spec *udev.Specification, slot *snap.SlotInfo) error {
   154  	spec.TagDevice(`KERNEL=="controlC[0-9]*"`)
   155  	spec.TagDevice(`KERNEL=="pcmC[0-9]*D[0-9]*[cp]"`)
   156  	spec.TagDevice(`KERNEL=="timer"`)
   157  	return nil
   158  }
   159  
   160  func (iface *audioPlaybackInterface) AppArmorPermanentSlot(spec *apparmor.Specification, slot *snap.SlotInfo) error {
   161  	spec.AddSnippet(audioPlaybackPermanentSlotAppArmor)
   162  	return nil
   163  }
   164  
   165  func (iface *audioPlaybackInterface) SecCompConnectedPlug(spec *seccomp.Specification, plug *interfaces.ConnectedPlug, slot *interfaces.ConnectedSlot) error {
   166  	spec.AddSnippet(audioPlaybackConnectedPlugSecComp)
   167  	return nil
   168  }
   169  
   170  func (iface *audioPlaybackInterface) SecCompPermanentSlot(spec *seccomp.Specification, slot *snap.SlotInfo) error {
   171  	spec.AddSnippet(audioPlaybackPermanentSlotSecComp)
   172  	return nil
   173  }
   174  
   175  func (iface *audioPlaybackInterface) AutoConnect(*snap.PlugInfo, *snap.SlotInfo) bool {
   176  	return true
   177  }
   178  
   179  func init() {
   180  	registerIface(&audioPlaybackInterface{})
   181  }