Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

interfaces/many: deny arbitrary desktop files and misc from /usr/share #8301

Merged
Show file tree
Hide file tree
Changes from 31 commits
Commits
Show all changes
33 commits
Select commit Hold shift + click to select a range
486a3eb
interfaces/desktop: limit access to /usr/share/applications
Mar 19, 2020
105b47f
interfaces/unity7: limit access to /usr/share
Mar 19, 2020
f1d87ff
interfaces/browser-support: limit reading of applications/*.desktop
Mar 19, 2020
6464c63
interfaces/{desktop-legacy,unity7}: add note about mimeinfo.cache
Mar 19, 2020
66bab80
interfaces/many: deny mimeinfo.cache and correct comments
Mar 20, 2020
d8879bd
browser-support: remove access to /var/lib/snapd/desktop/applications
Mar 20, 2020
6cfa4c8
Merge remote-tracking branch 'upstream/master' into deny-arbitrary-de…
Jun 1, 2020
3424667
interfaces/utils: add getDesktopFileRules() helper
Jun 1, 2020
bc49802
typo
Jun 1, 2020
1af64c3
interfaces/desktop-legacy: explicitly deny other snap's desktop files
Jun 1, 2020
94d5ff2
interfaces/unity7: explicitly deny other snap's desktop files
Jun 1, 2020
99744e4
remove duplicated rules in unity7 from recent commits
Jun 2, 2020
edee8f8
Merge remote-tracking branch 'upstream/master' into deny-arbitrary-de…
Jul 1, 2020
79095fc
Merge remote-tracking branch 'upstream/master' into deny-arbitrary-de…
Aug 14, 2020
59cc3fb
add snap.ValidateInstanceName() sanity check. Thanks to Ian
Aug 14, 2020
ff11e20
remove useless conditional. Thanks to jhenstridge
Aug 14, 2020
a320568
use strings.Join() instead of loop. Thanks jhenstridge
Aug 14, 2020
790ce3d
move wrappers/desktop.go:desktopPrefix() to snap/info.go:DesktopPrefix()
Aug 14, 2020
4448d3a
snap/info: DesktopFile() should use DesktopPrefix()
Aug 14, 2020
2ba55dc
add SNAP_INSTANCE_DESKTOP template var and use it
Aug 14, 2020
2c5d293
unity7: indicator-messages also converts '+' to '_'
Aug 14, 2020
55783d0
add ValidateDesktopPrefix()
Aug 14, 2020
99005f5
update to use DesktopPrefix()
Aug 14, 2020
58f8438
utils.go: fix comment
Aug 14, 2020
29ec3ac
use @{SNAP_INSTANCE_DESKTOP} in getDesktopFileRules() policy
Aug 15, 2020
272a48f
add more aareExclusivePatterns tests
Aug 15, 2020
7846842
snap/validate_test.go: add a few more tests
Aug 15, 2020
d008e8f
Use stand InstanceKey check in DesktopPrefix. Thanks jhenstridge
Aug 17, 2020
292f3f4
verify old-style parallel install desktop files aren't created
Aug 17, 2020
dfbdf7b
Merge remote-tracking branch 'upstream/master' into deny-arbitrary-de…
Aug 17, 2020
8d997c9
Merge remote-tracking branch 'upstream/master' into deny-arbitrary-de…
Aug 18, 2020
2a2212e
Merge remote-tracking branch 'refs/remotes/upstream/master' into deny…
Aug 18, 2020
349de3f
aareExclusivePatterns() should return nil on error. Thanks to mvo
Aug 18, 2020
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 15 additions & 11 deletions interfaces/builtin/browser_support.go
Original file line number Diff line number Diff line change
Expand Up @@ -111,19 +111,23 @@ deny capability mknod,
`

const browserSupportConnectedPlugAppArmorWithSandbox = `
# Leaks installed applications
# TODO: should this be somewhere else?
/etc/mailcap r,
# Snaps should be using xdg-open from the runtime instead of reading these
# files directly since the snap is unable to do anything with these files
# but these accesses have been allowed for a long time and remain so as not
# to break existing snaps. These could be changed to explicit deny rules,
# but that provides a worse debugging experience. Combined, the risks
# outweigh the benefits of closing this information leak for the small
# number of snaps allowed to use allow-sandbox: true. Reference:
# https://forum.snapcraft.io/t/cannot-open-pdf-attachment-with-thunderbird/11845
/usr/share/applications/{,*} r,
/var/lib/snapd/desktop/applications/{,*} r,

# 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. This is duplicated from desktop for compatibility with
# existing snaps.
/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

# Chromium content api unfortunately needs these for normal operation
owner @{PROC}/@{pid}/fd/[0-9]* w,

# Various files in /run/udev/data needed by Chrome Settings. Leaks device
Expand Down
13 changes: 12 additions & 1 deletion interfaces/builtin/desktop.go
Original file line number Diff line number Diff line change
Expand Up @@ -158,7 +158,18 @@ dbus (receive)

# Allow use of snapd's internal 'xdg-open'
/usr/bin/xdg-open ixr,
/usr/share/applications/{,*} r,
# 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=/
Expand Down
48 changes: 34 additions & 14 deletions interfaces/builtin/desktop_legacy.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,13 @@

package builtin

import (
"strings"

"github.com/snapcore/snapd/interfaces"
"github.com/snapcore/snapd/interfaces/apparmor"
)

const desktopLegacySummary = `allows privileged access to desktop legacy methods`

// While this gives privileged access to legacy methods we should auto-connect
Expand Down Expand Up @@ -234,13 +241,14 @@ dbus (send)
interface=org.gtk.vfs.MountTracker
member=LookupMount,

# This leaks the names of snaps with desktop files
/var/lib/snapd/desktop/applications/ r,
/var/lib/snapd/desktop/applications/mimeinfo.cache r,
# Support BAMF_DESKTOP_FILE_HINT by allowing reading our desktop files
# parallel-installs: this leaks read access to desktop files owned by keyed
# instances of @{SNAP_NAME} to @{SNAP_NAME} snap
/var/lib/snapd/desktop/applications/@{SNAP_INSTANCE_DESKTOP}_*.desktop r,
###SNAP_DESKTOP_FILE_RULES###
# Snaps are unable to use the data in mimeinfo.cache (since they can't execute
# the returned desktop file themselves). unity messaging menu doesn't require
# mimeinfo.cache and xdg-mime will fallback to reading the desktop files
# directly to look for MimeType. Since reading the snap's own desktop files is
# allowed, we can safely deny access to this file (and xdg-mime will either
# return one of the snap's mimetypes, or none).
deny /var/lib/snapd/desktop/applications/mimeinfo.cache r,

# glib-networking's GLib proxy (different than the portal's proxy service
# org.freedesktop.portal.ProxyResolver)
Expand All @@ -261,13 +269,25 @@ accept
accept4
`

type desktopLegacyInterface struct {
commonInterface
}

func (iface *desktopLegacyInterface) AppArmorConnectedPlug(spec *apparmor.Specification, plug *interfaces.ConnectedPlug, slot *interfaces.ConnectedSlot) error {
snippet := strings.Join(getDesktopFileRules(plug.Snap().DesktopPrefix()), "\n")
spec.AddSnippet(strings.Replace(desktopLegacyConnectedPlugAppArmor, "###SNAP_DESKTOP_FILE_RULES###", snippet+"\n", -1))

return nil
}

func init() {
registerIface(&commonInterface{
name: "desktop-legacy",
summary: desktopLegacySummary,
implicitOnClassic: true,
baseDeclarationSlots: desktopLegacyBaseDeclarationSlots,
connectedPlugAppArmor: desktopLegacyConnectedPlugAppArmor,
connectedPlugSecComp: desktopLegacyConnectedPlugSecComp,
registerIface(&desktopLegacyInterface{
commonInterface: commonInterface{
name: "desktop-legacy",
summary: desktopLegacySummary,
implicitOnClassic: true,
baseDeclarationSlots: desktopLegacyBaseDeclarationSlots,
connectedPlugSecComp: desktopLegacyConnectedPlugSecComp,
},
})
}
9 changes: 9 additions & 0 deletions interfaces/builtin/desktop_legacy_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,15 @@ func (s *DesktopLegacyInterfaceSuite) TestAppArmorSpec(c *C) {
c.Assert(spec.SnippetForTag("snap.consumer.app"), testutil.Contains, "# Description: Can access common desktop legacy methods")
c.Assert(spec.SnippetForTag("snap.consumer.app"), testutil.Contains, "#include <abstractions/dbus-accessibility-strict>")
c.Assert(spec.SnippetForTag("snap.consumer.app"), testutil.Contains, `peer=(addr="@/tmp/ibus/dbus-*"),`)
c.Assert(spec.SnippetForTag("snap.consumer.app"), testutil.Contains, `deny /var/lib/snapd/desktop/applications/mimeinfo.cache r,`)

// getDesktopFileRules() rules
c.Assert(spec.SnippetForTag("snap.consumer.app"), testutil.Contains, `# This leaks the names of snaps with desktop files`)
c.Assert(spec.SnippetForTag("snap.consumer.app"), testutil.Contains, `/var/lib/snapd/desktop/applications/ r,`)
c.Assert(spec.SnippetForTag("snap.consumer.app"), testutil.Contains, `/var/lib/snapd/desktop/applications/@{SNAP_INSTANCE_DESKTOP}_*.desktop r,`)
c.Assert(spec.SnippetForTag("snap.consumer.app"), testutil.Contains, `deny /var/lib/snapd/desktop/applications/@{SNAP_INSTANCE_DESKTOP}[^_.]*.desktop r,`)
c.Assert(spec.SnippetForTag("snap.consumer.app"), testutil.Contains, `deny /var/lib/snapd/desktop/applications/[^c]* r,`)
c.Assert(spec.SnippetForTag("snap.consumer.app"), testutil.Contains, `deny /var/lib/snapd/desktop/applications/consume[^r]* r,`)

// connected plug to core slot
spec = &apparmor.Specification{}
Expand Down
2 changes: 2 additions & 0 deletions interfaces/builtin/export_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,8 @@ var (
LabelExpr = labelExpr
PlugAppLabelExpr = plugAppLabelExpr
SlotAppLabelExpr = slotAppLabelExpr
AareExclusivePatterns = aareExclusivePatterns
GetDesktopFileRules = getDesktopFileRules
)

func MprisGetName(iface interfaces.Interface, attribs map[string]interface{}) (string, error) {
Expand Down
46 changes: 27 additions & 19 deletions interfaces/builtin/unity7.go
Original file line number Diff line number Diff line change
Expand Up @@ -62,8 +62,6 @@ owner @{HOME}/.local/share/fonts/{,**} r,

# subset of gnome abstraction
/etc/gnome/defaults.list r,
/usr/share/gnome/applications/ r,
/usr/share/applications/mimeinfo.cache r,

/etc/gtk-*/* r,
/usr/lib{,32,64}/gtk-*/** mr,
Expand All @@ -80,22 +78,29 @@ owner @{HOME}/.local/share/fonts/{,**} r,
/usr/share/icons/*/index.theme rk,
/usr/share/pixmaps/ r,
/usr/share/pixmaps/** r,
/usr/share/unity/icons/** r,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This access is removed but not added anywhere back AFAICT - is there a regression risk here?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No. This path does not exist in core images (notice is is in /usr, which comes from core* snaps). This is explained in commit 105b47f:

    interfaces/unity7: limit access to /usr/share
    
    Currently, the unity7 interface allows access to all of
    /usr/share/applications. The files in /usr/share/applications come from
    the core snaps, and with the exception of files related to xdg-open,
    snaps are unable to do anything useful with these files (since they are
    unable to execute the application). As such, limit the access to:
    
      /usr/share/applications/ r,
      /usr/share/applications/mimeapps.list r,
      /usr/share/applications/xdg-open.desktop r,
    
    and suppress noisy denials by adding explicit deny rules for the desktop
    files known to be shipped in the core, core18 and core20 snaps.
    
    In addition, to avoid confusion, remove legacy rules inherited from
    snappy 15.04 that do not exist in the core, core18 and core20 snaps:
    
      /usr/share/gnome/applications/
      /usr/share/applications/mimeinfo.cache
      /usr/share/unity/icons/**
      /usr/share/thumbnailer/icons/**
      /usr/share/themes/**
      /usr/share/gnome/glib*/schemas/{,*}
      /usr/share/ubuntu/glib*/schemas/{,*}
    
    References:
    https://bugs.launchpad.net/snapd/+bug/1868051 (related)

/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,

# Snappy's 'xdg-open' talks to the snapd-xdg-open service which currently works
# only in environments supporting dbus-send (eg, X11). In the future once
# snappy's xdg-open supports all snaps images, this access may move to another
# interface.
# interface. This is duplicated from desktop for compatibility with existing
# snaps.
/usr/bin/xdg-open ixr,
/usr/share/applications/{,*} r,
# 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. This is duplicated from desktop for compatibility with
# existing snaps.
/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

# This allow access to the first version of the snapd-xdg-open
# version which was shipped outside of snapd
Expand Down Expand Up @@ -492,16 +497,14 @@ dbus (receive)
member=Get*
peer=(label=unconfined),

# unity messaging menu
# first, allow finding the desktop file
/usr/share/applications/ r,
# this leaks the names of snaps with desktop files
/var/lib/snapd/desktop/applications/ r,
/var/lib/snapd/desktop/applications/mimeinfo.cache r,
# Support BAMF_DESKTOP_FILE_HINT by allowing reading our desktop files
# parallel-installs: when @{SNAP_INSTANCE_DESKTOP} == @{SNAP_NAME},
# this leaks read access to desktop files of parallel installs of the snap
/var/lib/snapd/desktop/applications/@{SNAP_INSTANCE_DESKTOP}_*.desktop r,
###SNAP_DESKTOP_FILE_RULES###
# Snaps are unable to use the data in mimeinfo.cache (since they can't execute
# the returned desktop file themselves). unity messaging menu doesn't require
# mimeinfo.cache and xdg-mime will fallback to reading the desktop files
# directly to look for MimeType. Since reading the snap's own desktop files is
# allowed, we can safely deny access to this file (and xdg-mime will either
# return one of the snap's mimetypes, or none).
deny /var/lib/snapd/desktop/applications/mimeinfo.cache r,

# then allow talking to Unity DBus service
dbus (send)
Expand Down Expand Up @@ -670,6 +673,11 @@ func (iface *unity7Interface) AppArmorConnectedPlug(spec *apparmor.Specification
new = strings.Replace(new, "+", "_", -1)
old := "###UNITY_SNAP_NAME###"
snippet := strings.Replace(unity7ConnectedPlugAppArmor, old, new, -1)

old = "###SNAP_DESKTOP_FILE_RULES###"
new = strings.Join(getDesktopFileRules(plug.Snap().DesktopPrefix()), "\n")
snippet = strings.Replace(snippet, old, new+"\n", -1)

spec.AddSnippet(snippet)
return nil
}
Expand Down
17 changes: 17 additions & 0 deletions interfaces/builtin/unity7_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,23 @@ func (s *Unity7InterfaceSuite) TestUsedSecuritySystems(c *C) {
c.Assert(apparmorSpec.SecurityTags(), DeepEquals, []string{"snap.other-snap.app2"})
c.Assert(apparmorSpec.SnippetForTag("snap.other-snap.app2"), testutil.Contains, `/usr/share/pixmaps`)
c.Assert(apparmorSpec.SnippetForTag("snap.other-snap.app2"), testutil.Contains, `path=/com/canonical/indicator/messages/other_snap_*_desktop`)
c.Assert(apparmorSpec.SnippetForTag("snap.other-snap.app2"), testutil.Contains, `deny /var/lib/snapd/desktop/applications/mimeinfo.cache r,`)

// getDesktopFileRules() rules
c.Assert(apparmorSpec.SnippetForTag("snap.other-snap.app2"), testutil.Contains, `# This leaks the names of snaps with desktop files`)
c.Assert(apparmorSpec.SnippetForTag("snap.other-snap.app2"), testutil.Contains, `/var/lib/snapd/desktop/applications/ r,`)
c.Assert(apparmorSpec.SnippetForTag("snap.other-snap.app2"), testutil.Contains, `/var/lib/snapd/desktop/applications/@{SNAP_INSTANCE_DESKTOP}_*.desktop r,`)
c.Assert(apparmorSpec.SnippetForTag("snap.other-snap.app2"), testutil.Contains, `deny /var/lib/snapd/desktop/applications/@{SNAP_INSTANCE_DESKTOP}[^_.]*.desktop r,`)
c.Assert(apparmorSpec.SnippetForTag("snap.other-snap.app2"), testutil.Contains, `deny /var/lib/snapd/desktop/applications/[^o]* r,`)
c.Assert(apparmorSpec.SnippetForTag("snap.other-snap.app2"), testutil.Contains, `deny /var/lib/snapd/desktop/applications/other-sna[^p]* r,`)

// connected plugs for instance name have a non-nil security snippet for apparmor
apparmorSpec = &apparmor.Specification{}
err = apparmorSpec.AddConnectedPlug(s.iface, s.plugInst, s.slot)
c.Assert(err, IsNil)
c.Assert(apparmorSpec.SecurityTags(), DeepEquals, []string{"snap.other-snap_instance.app2"})
c.Assert(apparmorSpec.SnippetForTag("snap.other-snap_instance.app2"), testutil.Contains, `/usr/share/pixmaps`)
c.Assert(apparmorSpec.SnippetForTag("snap.other-snap_instance.app2"), testutil.Contains, `path=/com/canonical/indicator/messages/other_snap_instance_*_desktop`)

// connected plugs for instance name have a non-nil security snippet for apparmor
apparmorSpec = &apparmor.Specification{}
Expand Down
56 changes: 56 additions & 0 deletions interfaces/builtin/utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ import (
"regexp"
"sort"

"github.com/snapcore/snapd/dirs"
"github.com/snapcore/snapd/interfaces"
"github.com/snapcore/snapd/release"
"github.com/snapcore/snapd/snap"
Expand Down Expand Up @@ -125,3 +126,58 @@ func verifySlotPathAttribute(slotRef *interfaces.SlotRef, attrs interfaces.Attre
}
return cleanPath, nil
}

// aareExclusivePatterns takes a string and generates deny alternations. Eg,
// aareExclusivePatterns("foo") returns:
// []string{
// "[^f]*",
// "f[^o]*",
// "fo[^o]*",
// }
func aareExclusivePatterns(orig string) []string {
// This function currently is only intended to be used with desktop
// prefixes as calculated by info.DesktopPrefix (the snap name and
// instance name, if present). To avoid having to worry about aare
// special characters, etc, perform ValidateDesktopPrefix() and return
// an empty list if invalid. If this function is modified for other
// input, aare/quoting/etc will have to be considered.
if !snap.ValidateDesktopPrefix(orig) {
return make([]string, 0)
jdstrand marked this conversation as resolved.
Show resolved Hide resolved
}

s := make([]string, len(orig))

prefix := ""
for i, letter := range orig {
prefix = orig[:i]
s[i] = fmt.Sprintf("%s[^%c]*", prefix, letter)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

since apparmor globbing is not quite regex, are there any characters here we might need to escape such as "^" itself or is this all handled in the expected way by apparmor's regex/globs ?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@jdstrand is the authority here, but I suspect the problematic characters would be \, ], ", and maybe newline. This could probably be handled by checking whether orig matches a regexp searching for the problem characters.

Given that the input to this function is validated to not contain the problem characters, I don't think it is worth trying to implement escaping.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Given that the input to this function is validated to not contain the problem characters, I don't think it is worth trying to implement escaping.

That is precisely why I did not do this; we are using this with snapInstanceName only. I'll add a snap.ValidateInstanceName() and add some additional tests.

Copy link
Author

@jdstrand jdstrand Aug 14, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done (at first I did ValidateInstanceName() but based on other feedback, I'm not using a new ValidateDesktopPrefix()).

}
return s
}

// getDesktopFileRules(<snap instance name>) generates snippet rules for
// allowing access to the specified snap's desktop files in
// dirs.SnapDesktopFilesDir, but explicitly denies access to all other snaps'
// desktop files since xdg libraries may try to read all the desktop files
// in the dir, causing excessive noise. (LP: #1868051)
func getDesktopFileRules(snapInstanceName string) []string {
baseDir := dirs.SnapDesktopFilesDir

rules := []string{
"# Support applications which use the unity messaging menu, xdg-mime, etc",
"# This leaks the names of snaps with desktop files",
fmt.Sprintf("%s/ r,", baseDir),
"# Allowing reading only our desktop files (required by (at least) the unity",
"# messaging menu).",
"# parallel-installs: this leaks read access to desktop files owned by keyed",
"# instances of @{SNAP_NAME} to @{SNAP_NAME} snap",
fmt.Sprintf("%s/@{SNAP_INSTANCE_DESKTOP}_*.desktop r,", baseDir),
"# Explicitly deny access to other snap's desktop files",
fmt.Sprintf("deny %s/@{SNAP_INSTANCE_DESKTOP}[^_.]*.desktop r,", baseDir),
}
for _, t := range aareExclusivePatterns(snapInstanceName) {
mvo5 marked this conversation as resolved.
Show resolved Hide resolved
rules = append(rules, fmt.Sprintf("deny %s/%s r,", baseDir, t))
}

return rules
}
Loading