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