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/home: add 'read' attribute to allow non-owner read to @{HOME} #5016
Changes from all commits
d361726
f828ad9
ca0c514
6f6022a
bf65b10
5237bea
a958603
bf86a34
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,7 +1,7 @@ | ||
// -*- Mode: Go; indent-tabs-mode: t -*- | ||
|
||
/* | ||
* Copyright (C) 2016 Canonical Ltd | ||
* Copyright (C) 2016-2018 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 | ||
|
@@ -19,18 +19,31 @@ | |
|
||
package builtin | ||
|
||
import ( | ||
"fmt" | ||
"github.com/snapcore/snapd/interfaces" | ||
"github.com/snapcore/snapd/interfaces/apparmor" | ||
"github.com/snapcore/snapd/snap" | ||
) | ||
|
||
const homeSummary = `allows access to non-hidden files in the home directory` | ||
|
||
const homeBaseDeclarationSlots = ` | ||
home: | ||
allow-installation: | ||
slot-snap-type: | ||
- core | ||
deny-connection: | ||
plug-attributes: | ||
read: all | ||
deny-auto-connection: | ||
on-classic: false | ||
- | ||
on-classic: false | ||
- | ||
plug-attributes: | ||
read: all | ||
` | ||
|
||
// http://bazaar.launchpad.net/~ubuntu-security/ubuntu-core-security/trunk/view/head:/data/apparmor/policygroups/ubuntu-core/16.04/home | ||
const homeConnectedPlugAppArmor = ` | ||
# Description: Can access non-hidden files in user's $HOME. This is restricted | ||
# because it gives file access to all of the user's $HOME. | ||
|
@@ -63,14 +76,53 @@ owner /run/user/[0-9]*/gvfs/{,**} r, | |
owner /run/user/[0-9]*/gvfs/*/** w, | ||
` | ||
|
||
const homeConnectedPlugAppArmorWithAllRead = ` | ||
# Allow non-owner read to non-hidden and non-snap files and directories | ||
@{HOME}/ r, | ||
@{HOME}/[^s.]** r, | ||
@{HOME}/s[^n]** r, | ||
@{HOME}/sn[^a]** r, | ||
@{HOME}/sna[^p]** r, | ||
@{HOME}/snap[^/]** r, | ||
@{HOME}/{s,sn,sna}{,/} r, | ||
` | ||
|
||
type homeInterface struct { | ||
commonInterface | ||
} | ||
|
||
func (iface *homeInterface) BeforePreparePlug(plug *snap.PlugInfo) error { | ||
// It's fine if 'read' isn't specified, but if it is, it needs to be | ||
// 'all' | ||
if r, ok := plug.Attrs["read"]; ok && r != "all" { | ||
return fmt.Errorf(`home plug requires "read" be 'all'`) | ||
} | ||
|
||
return nil | ||
} | ||
|
||
func (iface *homeInterface) AppArmorConnectedPlug(spec *apparmor.Specification, plug *interfaces.ConnectedPlug, slot *interfaces.ConnectedSlot) error { | ||
var read string | ||
_ = plug.Attr("read", &read) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Could we use plug.Attr in the BeforePreparePlug above? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm doing it this way in BeforePreparePlug because I want to see if it exists at all (since it is ok if it doesn't) and if it does make sure it is a string. I could just ignore if it isn't a string, but this isn't what we do elsewhere (eg browser-support, content and docker-support). The reason I can use plug.Attr() in AppArmorConnectedPlug is because by the time it is run, I'm sure things are ok and if read isn't an attribute, then the 'read' variable is simply empty and won't match |
||
// 'owner' is the standard policy | ||
spec.AddSnippet(homeConnectedPlugAppArmor) | ||
|
||
// 'all' grants standard policy plus read access to home without owner | ||
// match | ||
if read == "all" { | ||
spec.AddSnippet(homeConnectedPlugAppArmorWithAllRead) | ||
} | ||
return nil | ||
} | ||
|
||
func init() { | ||
registerIface(&commonInterface{ | ||
registerIface(&homeInterface{commonInterface{ | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I must say I'm really happy we're doing this. It makes to-and-from common vs custom interface transitions so much easier than before. |
||
name: "home", | ||
summary: homeSummary, | ||
implicitOnCore: true, | ||
implicitOnClassic: true, | ||
baseDeclarationSlots: homeBaseDeclarationSlots, | ||
connectedPlugAppArmor: homeConnectedPlugAppArmor, | ||
reservedForOS: true, | ||
}) | ||
}}) | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is this documented anywhere public? The home interface is probably the most used interface we have.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not yet-- this is new :) I will update the documentation for it, yes.