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