diff --git a/cmd/snap-confine/snap-confine.apparmor.in b/cmd/snap-confine/snap-confine.apparmor.in index d943703a6b8..a2bc1c0d2dd 100644 --- a/cmd/snap-confine/snap-confine.apparmor.in +++ b/cmd/snap-confine/snap-confine.apparmor.in @@ -242,6 +242,10 @@ audit deny mount /** -> /snap/bin/**, # Allow the content interface to bind fonts from the host filesystem mount options=(ro bind) /var/lib/snapd/hostfs/usr/share/fonts/ -> /snap/*/*/**, + # Allow the desktop interface to bind fonts from the host filesystem + mount options=(ro bind) /var/lib/snapd/hostfs/usr/share/fonts -> /usr/share/fonts, + mount options=(ro bind) /var/lib/snapd/hostfs/usr/local/share/fonts -> /usr/local/share/fonts, + mount options=(ro bind) /var/lib/snapd/hostfs/var/cache/fontconfig -> /var/cache/fontconfig, # nvidia handling, glob needs /usr/** and the launcher must be # able to bind mount the nvidia dir diff --git a/dirs/dirs.go b/dirs/dirs.go index dc6755c0d15..db6f5767b10 100644 --- a/dirs/dirs.go +++ b/dirs/dirs.go @@ -87,6 +87,10 @@ var ( CompletionHelper string CompletersDir string CompleteSh string + + SystemFontsDir string + SystemLocalFontsDir string + SystemFontconfigCacheDir string ) const ( @@ -224,4 +228,8 @@ func SetRootDir(rootdir string) { CompletionHelper = filepath.Join(CoreLibExecDir, "etelpmoc.sh") CompletersDir = filepath.Join(rootdir, "/usr/share/bash-completion/completions/") CompleteSh = filepath.Join(SnapMountDir, "core/current/usr/lib/snapd/complete.sh") + + SystemFontsDir = filepath.Join(rootdir, "/usr/share/fonts") + SystemLocalFontsDir = filepath.Join(rootdir, "/usr/local/share/fonts") + SystemFontconfigCacheDir = filepath.Join(rootdir, "/var/cache/fontconfig") } diff --git a/interfaces/builtin/desktop.go b/interfaces/builtin/desktop.go index 4459bb2fd8b..6345d565bb6 100644 --- a/interfaces/builtin/desktop.go +++ b/interfaces/builtin/desktop.go @@ -19,6 +19,15 @@ package builtin +import ( + "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" +) + const desktopSummary = `allows access to basic graphical desktop resources` const desktopBaseDeclarationSlots = ` @@ -117,13 +126,59 @@ dbus (send) deny /{dev,run,var/run}/shm/lttng-ust-* rw, ` +type desktopInterface struct{} + +func (iface *desktopInterface) Name() string { + return "desktop" +} + +func (iface *desktopInterface) StaticInfo() interfaces.StaticInfo { + return interfaces.StaticInfo{ + Summary: desktopSummary, + ImplicitOnClassic: true, + BaseDeclarationSlots: desktopBaseDeclarationSlots, + } +} + +func (iface *desktopInterface) SanitizeSlot(slot *interfaces.Slot) error { + return sanitizeSlotReservedForOS(iface, slot) +} + +func (iface *desktopInterface) AutoConnect(*interfaces.Plug, *interfaces.Slot) bool { + // allow what declarations allowed + return true +} + +func (iface *desktopInterface) AppArmorConnectedPlug(spec *apparmor.Specification, plug *interfaces.Plug, plugAttrs map[string]interface{}, slot *interfaces.Slot, slotAttrs map[string]interface{}) error { + spec.AddSnippet(desktopConnectedPlugAppArmor) + return nil +} + +func (iface *desktopInterface) MountConnectedPlug(spec *mount.Specification, plug *interfaces.Plug, plugAttrs map[string]interface{}, slot *interfaces.Slot, slotAttrs map[string]interface{}) error { + if !release.OnClassic { + // There is nothing to expose on an all-snaps system + return nil + } + + fontconfigDirs := []string{ + dirs.SystemFontsDir, + dirs.SystemLocalFontsDir, + dirs.SystemFontconfigCacheDir, + } + + for _, dir := range fontconfigDirs { + if !osutil.IsDirectory(dir) { + continue + } + spec.AddMountEntry(mount.Entry{ + Name: "/var/lib/snapd/hostfs" + dir, + Dir: dirs.StripRootDir(dir), + Options: []string{"bind", "ro"}, + }) + } + return nil +} + func init() { - registerIface(&commonInterface{ - name: "desktop", - summary: desktopSummary, - implicitOnClassic: true, - baseDeclarationSlots: desktopBaseDeclarationSlots, - connectedPlugAppArmor: desktopConnectedPlugAppArmor, - reservedForOS: true, - }) + registerIface(&desktopInterface{}) } diff --git a/interfaces/builtin/desktop_test.go b/interfaces/builtin/desktop_test.go index 6ec4f7ff5c1..2d0554ee9c9 100644 --- a/interfaces/builtin/desktop_test.go +++ b/interfaces/builtin/desktop_test.go @@ -20,11 +20,17 @@ package builtin_test import ( + "os" + "path/filepath" + . "gopkg.in/check.v1" + "github.com/snapcore/snapd/dirs" "github.com/snapcore/snapd/interfaces" "github.com/snapcore/snapd/interfaces/apparmor" "github.com/snapcore/snapd/interfaces/builtin" + "github.com/snapcore/snapd/interfaces/mount" + "github.com/snapcore/snapd/release" "github.com/snapcore/snapd/snap" "github.com/snapcore/snapd/testutil" ) @@ -56,6 +62,10 @@ func (s *DesktopInterfaceSuite) SetUpTest(c *C) { s.coreSlot = MockSlot(c, desktopCoreYaml, nil, "desktop") } +func (s *DesktopInterfaceSuite) TearDownTest(c *C) { + dirs.SetRootDir("/") +} + func (s *DesktopInterfaceSuite) TestName(c *C) { c.Assert(s.iface.Name(), Equals, "desktop") } @@ -91,6 +101,45 @@ func (s *DesktopInterfaceSuite) TestAppArmorSpec(c *C) { c.Assert(spec.SecurityTags(), HasLen, 0) } +func (s *DesktopInterfaceSuite) TestMountSpec(c *C) { + tmpdir := c.MkDir() + dirs.SetRootDir(tmpdir) + c.Assert(os.MkdirAll(filepath.Join(tmpdir, "/usr/share/fonts"), 0777), IsNil) + c.Assert(os.MkdirAll(filepath.Join(tmpdir, "/usr/local/share/fonts"), 0777), IsNil) + c.Assert(os.MkdirAll(filepath.Join(tmpdir, "/var/cache/fontconfig"), 0777), IsNil) + + restore := release.MockOnClassic(false) + defer restore() + + // On all-snaps systems, no mount entries are added + spec := &mount.Specification{} + c.Assert(spec.AddConnectedPlug(s.iface, s.plug, nil, s.coreSlot, nil), IsNil) + c.Check(spec.MountEntries(), HasLen, 0) + + // On classic systems, a number of font related directories + // are bind mounted from the host system if they exist. + restore = release.MockOnClassic(true) + defer restore() + spec = &mount.Specification{} + c.Assert(spec.AddConnectedPlug(s.iface, s.plug, nil, s.coreSlot, nil), IsNil) + + entries := spec.MountEntries() + c.Assert(entries, HasLen, 3) + + const hostfs = "/var/lib/snapd/hostfs" + c.Check(entries[0].Name, Equals, hostfs+dirs.SystemFontsDir) + c.Check(entries[0].Dir, Equals, "/usr/share/fonts") + c.Check(entries[0].Options, DeepEquals, []string{"bind", "ro"}) + + c.Check(entries[1].Name, Equals, hostfs+dirs.SystemLocalFontsDir) + c.Check(entries[1].Dir, Equals, "/usr/local/share/fonts") + c.Check(entries[1].Options, DeepEquals, []string{"bind", "ro"}) + + c.Check(entries[2].Name, Equals, hostfs+dirs.SystemFontconfigCacheDir) + c.Check(entries[2].Dir, Equals, "/var/cache/fontconfig") + c.Check(entries[2].Options, DeepEquals, []string{"bind", "ro"}) +} + func (s *DesktopInterfaceSuite) TestStaticInfo(c *C) { si := interfaces.StaticInfoOf(s.iface) c.Assert(si.ImplicitOnCore, Equals, false)