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 }