github.com/hugh712/snapd@v0.0.0-20200910133618-1a99902bd583/interfaces/builtin/desktop.go (about)

     1  // -*- Mode: Go; indent-tabs-mode: t -*-
     2  
     3  /*
     4   * Copyright (C) 2017 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/dirs"
    24  	"github.com/snapcore/snapd/interfaces"
    25  	"github.com/snapcore/snapd/interfaces/apparmor"
    26  	"github.com/snapcore/snapd/interfaces/mount"
    27  	"github.com/snapcore/snapd/osutil"
    28  	"github.com/snapcore/snapd/release"
    29  	"github.com/snapcore/snapd/strutil"
    30  )
    31  
    32  const desktopSummary = `allows access to basic graphical desktop resources`
    33  
    34  const desktopBaseDeclarationSlots = `
    35    desktop:
    36      allow-installation:
    37        slot-snap-type:
    38          - core
    39  `
    40  
    41  const desktopConnectedPlugAppArmor = `
    42  # Description: Can access basic graphical desktop resources. To be used with
    43  # other interfaces (eg, wayland).
    44  
    45  #include <abstractions/dbus-strict>
    46  #include <abstractions/dbus-session-strict>
    47  
    48  # Allow finding the DBus session bus id (eg, via dbus_bus_get_id())
    49  dbus (send)
    50       bus=session
    51       path=/org/freedesktop/DBus
    52       interface=org.freedesktop.DBus
    53       member=GetId
    54       peer=(name=org.freedesktop.DBus, label=unconfined),
    55  
    56  #include <abstractions/fonts>
    57  owner @{HOME}/.local/share/fonts/{,**} r,
    58  /var/cache/fontconfig/   r,
    59  /var/cache/fontconfig/** mr,
    60  # some applications are known to mmap fonts
    61  /usr/{,local/}share/fonts/** m,
    62  
    63  # subset of gnome abstraction
    64  /etc/gtk-3.0/settings.ini r,
    65  owner @{HOME}/.config/gtk-3.0/settings.ini r,
    66  # Note: this leaks directory names that wouldn't otherwise be known to the snap
    67  owner @{HOME}/.config/gtk-3.0/bookmarks r,
    68  
    69  /usr/share/icons/                          r,
    70  /usr/share/icons/**                        r,
    71  /usr/share/icons/*/index.theme             rk,
    72  /usr/share/pixmaps/                        r,
    73  /usr/share/pixmaps/**                      r,
    74  /usr/share/unity/icons/**                  r,
    75  /usr/share/thumbnailer/icons/**            r,
    76  /usr/share/themes/**                       r,
    77  
    78  # The snapcraft desktop part may look for schema files in various locations, so
    79  # allow reading system installed schemas.
    80  /usr/share/glib*/schemas/{,*}              r,
    81  /usr/share/gnome/glib*/schemas/{,*}        r,
    82  /usr/share/ubuntu/glib*/schemas/{,*}       r,
    83  
    84  # subset of freedesktop.org
    85  owner @{HOME}/.local/share/mime/**   r,
    86  owner @{HOME}/.config/user-dirs.* r,
    87  
    88  /etc/xdg/user-dirs.conf r,
    89  /etc/xdg/user-dirs.defaults r,
    90  
    91  # gmenu
    92  dbus (send)
    93       bus=session
    94       interface=org.gtk.Actions
    95       member=Changed
    96       peer=(name=org.freedesktop.DBus, label=unconfined),
    97  
    98  # notifications
    99  dbus (send)
   100      bus=session
   101      path=/org/freedesktop/Notifications
   102      interface=org.freedesktop.Notifications
   103      member="{GetCapabilities,GetServerInformation,Notify,CloseNotification}"
   104      peer=(label=unconfined),
   105  
   106  dbus (receive)
   107      bus=session
   108      path=/org/freedesktop/Notifications
   109      interface=org.freedesktop.Notifications
   110      member={ActionInvoked,NotificationClosed,NotificationReplied}
   111      peer=(label=unconfined),
   112  
   113  # DesktopAppInfo Launched
   114  dbus (send)
   115      bus=session
   116      path=/org/gtk/gio/DesktopAppInfo
   117      interface=org.gtk.gio.DesktopAppInfo
   118      member=Launched
   119      peer=(label=unconfined),
   120  
   121  # Allow requesting interest in receiving media key events. This tells Gnome
   122  # settings that our application should be notified when key events we are
   123  # interested in are pressed, and allows us to receive those events.
   124  dbus (receive, send)
   125    bus=session
   126    interface=org.gnome.SettingsDaemon.MediaKeys
   127    path=/org/gnome/SettingsDaemon/MediaKeys
   128    peer=(label=unconfined),
   129  dbus (send)
   130    bus=session
   131    interface=org.freedesktop.DBus.Properties
   132    path=/org/gnome/SettingsDaemon/MediaKeys
   133    member="Get{,All}"
   134    peer=(label=unconfined),
   135  
   136  # Allow accessing the GNOME crypto services prompt APIs as used by
   137  # applications using libgcr (such as pinentry-gnome3) for secure pin
   138  # entry to unlock GPG keys etc. See:
   139  # https://developer.gnome.org/gcr/unstable/GcrPrompt.html
   140  # https://developer.gnome.org/gcr/unstable/GcrSecretExchange.html
   141  dbus (send)
   142      bus=session
   143      path=/org/gnome/keyring/Prompter
   144      interface=org.gnome.keyring.internal.Prompter
   145      member="{BeginPrompting,PerformPrompt,StopPrompting}"
   146      peer=(label=unconfined),
   147  
   148  # While the DBus path is not snap-specific, by the time an application
   149  # registers the prompt path via DBus, Gcr will check that it isn't
   150  # already in use and send the client an error if it is. See:
   151  # https://github.com/snapcore/snapd/pull/7673#issuecomment-592229711
   152  dbus (receive)
   153      bus=session
   154      path=/org/gnome/keyring/Prompt/p[0-9]*
   155      interface=org.gnome.keyring.internal.Prompter.Callback
   156      member="{PromptReady,PromptDone}"
   157      peer=(label=unconfined),
   158  
   159  # Allow use of snapd's internal 'xdg-open'
   160  /usr/bin/xdg-open ixr,
   161  # While /usr/share/applications comes from the base runtime of the snap, it
   162  # has some things that snaps actually need, so allow access to those and deny
   163  # access to the others
   164  /usr/share/applications/ r,
   165  /usr/share/applications/mimeapps.list r,
   166  /usr/share/applications/xdg-open.desktop r,
   167  # silence noisy denials from desktop files in core* snaps that aren't usable by
   168  # snaps
   169  deny /usr/share/applications/python*.desktop r,
   170  deny /usr/share/applications/vim.desktop r,
   171  deny /usr/share/applications/snap-handle-link.desktop r,  # core16
   172  
   173  dbus (send)
   174      bus=session
   175      path=/
   176      interface=com.canonical.SafeLauncher
   177      member=OpenURL
   178      peer=(label=unconfined),
   179  # ... and this allows access to the new xdg-open service which
   180  # is now part of snapd itself.
   181  dbus (send)
   182      bus=session
   183      path=/io/snapcraft/Launcher
   184      interface=io.snapcraft.Launcher
   185      member={OpenURL,OpenFile}
   186      peer=(label=unconfined),
   187  
   188  # Allow checking status, activating and locking the screensaver
   189  # gnome/kde/freedesktop.org
   190  dbus (send)
   191      bus=session
   192      path="/{,org/freedesktop/,org/gnome/}ScreenSaver"
   193      interface="org.{freedesktop,gnome}.ScreenSaver"
   194      member="{GetActive,GetActiveTime,Lock,SetActive}"
   195      peer=(label=unconfined),
   196  
   197  dbus (receive)
   198      bus=session
   199      path="/{,org/freedesktop/,org/gnome/}ScreenSaver"
   200      interface="org.{freedesktop,gnome}.ScreenSaver"
   201      member=ActiveChanged
   202      peer=(label=unconfined),
   203  
   204  # Allow unconfined to introspect us
   205  dbus (receive)
   206      bus=session
   207      interface=org.freedesktop.DBus.Introspectable
   208      member=Introspect
   209      peer=(label=unconfined),
   210  
   211  # Allow use of snapd's internal 'xdg-settings'
   212  /usr/bin/xdg-settings ixr,
   213  dbus (send)
   214      bus=session
   215      path=/io/snapcraft/Settings
   216      interface=io.snapcraft.Settings
   217      member={Check,CheckSub,Get,GetSub,Set,SetSub}
   218      peer=(label=unconfined),
   219  
   220  # Allow access to xdg-document-portal file system.  Access control is
   221  # handled by bind mounting a snap-specific sub-tree to this location
   222  # (ie, this is /run/user/<uid>/doc/by-app/snap.@{SNAP_INSTANCE_NAME}
   223  # on the host).
   224  owner /run/user/[0-9]*/doc/{,*/} r,
   225  # Allow rw access without owner match to the documents themselves since
   226  # the user guided the access and can specify anything DAC allows.
   227  /run/user/[0-9]*/doc/*/** rw,
   228  
   229  # Allow access to xdg-desktop-portal and xdg-document-portal
   230  dbus (receive, send)
   231      bus=session
   232      interface=org.freedesktop.portal.*
   233      path=/org/freedesktop/portal/{desktop,documents}{,/**}
   234      peer=(label=unconfined),
   235  
   236  dbus (receive, send)
   237      bus=session
   238      interface=org.freedesktop.DBus.Properties
   239      path=/org/freedesktop/portal/{desktop,documents}{,/**}
   240      peer=(label=unconfined),
   241  
   242  # The portals service is normally running and newer versions of
   243  # xdg-desktop-portal include AssumedAppArmor=unconfined. Since older
   244  # systems don't have this and because gtkfilechoosernativeportal.c relies on
   245  # service activation, allow sends to peer=(name=org.freedesktop.portal.Desktop)
   246  # for service activation.
   247  dbus (send)
   248      bus=session
   249      interface=org.freedesktop.portal.*
   250      path=/org/freedesktop/portal/{desktop,documents}{,/**}
   251      peer=(name=org.freedesktop.portal.Desktop),
   252  dbus (send)
   253      bus=session
   254      interface=org.freedesktop.DBus.Properties
   255      path=/org/freedesktop/portal/{desktop,documents}{,/**}
   256      peer=(name=org.freedesktop.portal.Desktop),
   257  
   258  # These accesses are noisy and applications can't do anything with the found
   259  # icon files, so explicitly deny to silence the denials
   260  deny /var/lib/snapd/desktop/icons/{,**/} r,
   261  
   262  # These accesses occur when flatpaks are on the system since it updates
   263  # XDG_DATA_DIRS to contain $HOME/.local/share/flatpak/exports/share. Until
   264  # we have better XDG_DATA_DIRS handling, silence these noisy denials.
   265  # https://github.com/snapcrafters/discord/issues/23#issuecomment-637607843
   266  deny @{HOME}/.local/share/flatpak/exports/share/** r,
   267  
   268  # Allow access to the IBus portal (IBUS_USE_PORTAL=1)
   269  dbus (send)
   270        bus=session
   271        path=/org/freedesktop/IBus
   272        interface=org.freedesktop.IBus.Portal
   273        member=CreateInputContext
   274        peer=(name=org.freedesktop.portal.IBus),
   275  
   276  dbus (send, receive)
   277        bus=session
   278        path=/org/freedesktop/IBus/InputContext_[0-9]*
   279        interface=org.freedesktop.IBus.InputContext
   280        peer=(label=unconfined),
   281  `
   282  
   283  type desktopInterface struct {
   284  	commonInterface
   285  }
   286  
   287  func (iface *desktopInterface) fontconfigDirs() []string {
   288  	fontDirs := []string{
   289  		dirs.SystemFontsDir,
   290  		dirs.SystemLocalFontsDir,
   291  	}
   292  	return append(fontDirs, dirs.SystemFontconfigCacheDirs...)
   293  }
   294  
   295  func (iface *desktopInterface) AppArmorConnectedPlug(spec *apparmor.Specification, plug *interfaces.ConnectedPlug, slot *interfaces.ConnectedSlot) error {
   296  	spec.AddSnippet(desktopConnectedPlugAppArmor)
   297  
   298  	// Allow mounting document portal
   299  	emit := spec.AddUpdateNSf
   300  	emit("  # Mount the document portal\n")
   301  	emit("  mount options=(bind) /run/user/[0-9]*/doc/by-app/snap.%s/ -> /run/user/[0-9]*/doc/,\n", plug.Snap().InstanceName())
   302  	emit("  umount /run/user/[0-9]*/doc/,\n\n")
   303  
   304  	if !release.OnClassic {
   305  		// We only need the font mount rules on classic systems
   306  		return nil
   307  	}
   308  
   309  	// Allow mounting fonts
   310  	for _, dir := range iface.fontconfigDirs() {
   311  		source := "/var/lib/snapd/hostfs" + dir
   312  		target := dirs.StripRootDir(dir)
   313  		emit("  # Read-only access to %s\n", target)
   314  		emit("  mount options=(bind) %s/ -> %s/,\n", source, target)
   315  		emit("  remount options=(bind, ro) %s/,\n", target)
   316  		emit("  umount %s/,\n\n", target)
   317  	}
   318  
   319  	return nil
   320  }
   321  
   322  func (iface *desktopInterface) MountConnectedPlug(spec *mount.Specification, plug *interfaces.ConnectedPlug, slot *interfaces.ConnectedSlot) error {
   323  	appId := "snap." + plug.Snap().InstanceName()
   324  	spec.AddUserMountEntry(osutil.MountEntry{
   325  		Name:    "$XDG_RUNTIME_DIR/doc/by-app/" + appId,
   326  		Dir:     "$XDG_RUNTIME_DIR/doc",
   327  		Options: []string{"bind", "rw", osutil.XSnapdIgnoreMissing()},
   328  	})
   329  
   330  	if !release.OnClassic {
   331  		// We only need the font mount rules on classic systems
   332  		return nil
   333  	}
   334  
   335  	for _, dir := range iface.fontconfigDirs() {
   336  		if !osutil.IsDirectory(dir) {
   337  			continue
   338  		}
   339  		if release.DistroLike("arch", "fedora") {
   340  			// XXX: on Arch and Fedora 32+ there is a known
   341  			// incompatibility between the binary fonts cache files
   342  			// and ones expected by desktop snaps; even though the
   343  			// cache format level is same for both, the host
   344  			// generated cache files cause instability, segfaults or
   345  			// incorrect rendering of fonts, for this reason do not
   346  			// mount the cache directories on those distributions,
   347  			// see https://bugs.launchpad.net/snapd/+bug/1877109
   348  			if strutil.ListContains(dirs.SystemFontconfigCacheDirs, dir) {
   349  				continue
   350  			}
   351  		}
   352  		// Since /etc/fonts/fonts.conf in the snap mount ns is the same
   353  		// as on the host, we need to preserve the original directory
   354  		// paths for the fontconfig runtime to poke the correct
   355  		// locations
   356  		spec.AddMountEntry(osutil.MountEntry{
   357  			Name:    "/var/lib/snapd/hostfs" + dir,
   358  			Dir:     dirs.StripRootDir(dir),
   359  			Options: []string{"bind", "ro"},
   360  		})
   361  	}
   362  
   363  	return nil
   364  }
   365  
   366  func init() {
   367  	registerIface(&desktopInterface{
   368  		commonInterface: commonInterface{
   369  			name:                 "desktop",
   370  			summary:              desktopSummary,
   371  			implicitOnClassic:    true,
   372  			baseDeclarationSlots: desktopBaseDeclarationSlots,
   373  		},
   374  	})
   375  }