mirror of
https://github.com/token2/snapd.git
synced 2026-03-13 11:15:47 -07:00
* Add snippets with priorities AppArmor rules that forbid access to a resource have more priority than rules that allow access to those same resources. This means that if an interface restricts access to an specific resource, it won't be possible to enable access to that same resource from another, more privileged, interface. An example of this is reading the .desktop files of all the installed snaps in the system: the superprivileged interface 'desktop-launch' enables access to these files, so any snap that has a connected plug for this interface should be able to read them. Unfortunately, the 'desktop-legacy' interface explicitly denies access to these files, and since it is connected automatically if a snap uses the 'desktop' or the 'unity7' interfaces, this mean that no graphical application will be able to read the .desktop files, even if the super- privileged interface 'desktop-launch' interface is connected. To allow this specific case, a temporary patch ( https://github.com/snapcore/snapd/pull/13933) was created and merged, but it is clearly an ugly and not-generic solution. For this reason, this new patch was created, following the specification https://docs.google.com/document/d/1K-1MYhp1RKSW_jzuuyX7TSVCg2rYplKZFdJbZAupP4Y/edit This patch allows to add "prioritized snippets". Each one has an UID and a priority. If no prioritized snippet with the same UID has been previously added, the new prioritized snippet will be added like any other normal snippet. But if there is already an added snippet with the same UID, then the priority of both the old and the new snippets are compared. If the new priority is lower than the old one, the new snippet is ignored; if the new priority is bigger than the old one, the new snippet fully replaces the old one. Finally, if both priorities are the same, the new snippet will be appended to the old snippet. This generic mechanism allows to give an interface priority over others if needed, like in the previous case. * Remove slices.Contains, since seems to be not supported * Update interfaces/apparmor/spec.go Co-authored-by: Zygmunt Bazyli Krynicki <me@zygoon.pl> * Use testutils.Contains * Replace "uid" with "key" for clarity and sanity * Add specific type for priority keys and force registering them * Remove unneeded return * Use SnippetKey as type * Don't use "slice" since MacOS seems to not support it * Update interfaces/apparmor/spec.go Co-authored-by: Zygmunt Bazyli Krynicki <me@zygoon.pl> * Update interfaces/apparmor/spec.go Co-authored-by: Zygmunt Bazyli Krynicki <me@zygoon.pl> * Use String instead of GetValue * Use SnippetKey as key instead of the inner string * Update interfaces/connection.go Co-authored-by: Zygmunt Bazyli Krynicki <me@zygoon.pl> * Several changes requested * Create the SnippetKeys inside Spec * Move key registration outside Spec This creates a centralized key registry inside apparmor module, so keys can be registered using top variables, and any duplicated key will produce a panic when snapd is launched, thus just panicking in any test too. * Added extra ways of working with SnippetKeys * Add extra check * Replace GetSnippetKey with GetSnippetKeys * Update the priority code use case A previous PR was merged with a Quick&Dirty(tm) solution to the priority problem between unity7 and desktop-legacy interfaces against desktop-launch interface. Now that it has been merged, that code must be updated to the new mechanism implemented in this PR. This is exactly what this commit does. * Add explanation and constants for prioritized snippets * Fix prioritized snippet key and add test in all_test * Several changes requested by Zygmunt Vazyli --------- Co-authored-by: Zygmunt Bazyli Krynicki <me@zygoon.pl>
149 lines
5.3 KiB
Go
149 lines
5.3 KiB
Go
// -*- Mode: Go; indent-tabs-mode: t -*-
|
|
|
|
/*
|
|
* Copyright (C) 2016 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 (
|
|
"errors"
|
|
"fmt"
|
|
"path/filepath"
|
|
"regexp"
|
|
|
|
"github.com/snapcore/snapd/dirs"
|
|
"github.com/snapcore/snapd/interfaces"
|
|
"github.com/snapcore/snapd/snap"
|
|
)
|
|
|
|
// The maximum number of Usb bInterfaceNumber.
|
|
const UsbMaxInterfaces = 32
|
|
|
|
// Determine if the permanent slot side is provided by the
|
|
// system. Some implicit slots can be provided by the system or by an
|
|
// application snap (eg avahi can be installed as deb or snap, upower
|
|
// can be part of the base snap or its own snap).
|
|
// - slot owned by the system (core/snapd snap) usually requires no action
|
|
// - slot owned by an application snap typically requires rules updates
|
|
func implicitSystemPermanentSlot(slot *snap.SlotInfo) bool {
|
|
if slot.Snap.Type() == snap.TypeOS || slot.Snap.Type() == snap.TypeSnapd {
|
|
return true
|
|
}
|
|
return false
|
|
}
|
|
|
|
// Determine if the connected slot side is provided by the system. As for
|
|
// isPermanentSlotSystemSlot(), the slot can be owned by the system or an
|
|
// application.
|
|
func implicitSystemConnectedSlot(slot *interfaces.ConnectedSlot) bool {
|
|
if slot.Snap().Type() == snap.TypeOS || slot.Snap().Type() == snap.TypeSnapd {
|
|
return true
|
|
}
|
|
return false
|
|
}
|
|
|
|
// determine if the given slot attribute path matches the regex.
|
|
// invalidErrFmt provides a fmt.Errorf format to create an error in
|
|
// the case the path does not matches, it should allow to include
|
|
// slotRef and be something like: "slot %q path attribute must be a
|
|
// valid <path kind>".
|
|
func verifySlotPathAttribute(slotRef *interfaces.SlotRef, attrs interfaces.Attrer, reg *regexp.Regexp, invalidErrFmt string) (string, error) {
|
|
var path string
|
|
if err := attrs.Attr("path", &path); err != nil || path == "" {
|
|
return "", fmt.Errorf("slot %q must have a path attribute", slotRef)
|
|
}
|
|
cleanPath := filepath.Clean(path)
|
|
if cleanPath != path {
|
|
return "", fmt.Errorf(`cannot use slot %q path %q: try %q"`, slotRef, path, cleanPath)
|
|
}
|
|
if !reg.MatchString(cleanPath) {
|
|
return "", fmt.Errorf(invalidErrFmt, slotRef)
|
|
}
|
|
return cleanPath, nil
|
|
}
|
|
|
|
// aareExclusivePatterns takes a string and generates deny alternations. Eg,
|
|
// aareExclusivePatterns("foo") returns:
|
|
//
|
|
// []string{
|
|
// "[^f]*",
|
|
// "f[^o]*",
|
|
// "fo[^o]*",
|
|
// }
|
|
//
|
|
// For a more generic version of this see GenerateAAREExclusionPatterns
|
|
// TODO: can this be rewritten to use/share code with that function instead?
|
|
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 nil
|
|
}
|
|
|
|
s := make([]string, len(orig))
|
|
|
|
prefix := ""
|
|
for i, letter := range orig {
|
|
prefix = orig[:i]
|
|
s[i] = fmt.Sprintf("%s[^%c]*", prefix, letter)
|
|
}
|
|
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) {
|
|
rules = append(rules, fmt.Sprintf("deny %s/%s r,", baseDir, t))
|
|
}
|
|
|
|
return rules
|
|
}
|
|
|
|
// stringListAttribute returns a list of strings for the given attribute key if the attribute exists.
|
|
func stringListAttribute(attrer interfaces.Attrer, key string) ([]string, error) {
|
|
var stringList []string
|
|
err := attrer.Attr(key, &stringList)
|
|
if err != nil && !errors.Is(err, snap.AttributeNotFoundError{}) {
|
|
value, _ := attrer.Lookup(key)
|
|
return nil, fmt.Errorf(`%q attribute must be a list of strings, not "%v"`, key, value)
|
|
}
|
|
|
|
return stringList, nil
|
|
}
|