Files
snapd/interfaces/seccomp/spec.go
Andrew Phelps d8a2e847e5 many: introduce SnapAppSet for use in security backends (#13574)
* interfaces: introduce SnapAppSet and forward one to the security backends

* snap: implement methods on snap.Info for getting apps and hooks for slots and plugs

This will enable us to remove the Hooks fields from the SlotInfo and
PlugInfo structs.

* interfaces: implement methods on SnapAppSet in terms of methods on snap.Info

* snap, interfaces: replace usage of {Plug,Slot}Info.SecurityTags with methods on SnapAppSet

* i/builtin: replace slotAppLabelExpr and plugAppLabelExpr with corresponding methods on SnapAppSet

* snap, o/snapstate, interfaces: remove Hooks field on snap.PlugInfo and snap.SlotInfo

* builtin, interfaces: fix tests that use Specification that now have a SnapAppSet

* snap: add tests for new methods on Info

* interfaces, i/builtin: port over some tests for SnapAppSet methods {Plug,Slot}LabelExpression

* interfaces: test PlugSecurityTags and SlotSecurityTags methods

* interfaces: add doc comments to SnapAppSet and methods

* i/builtin: remove ported over tests

* interfaces, many: require that SnapAppSet methods for getting security tags are called with plug/slot that comes from correct snap

Many tests did not properly adhere to this requirment, so they had to be
modifed to modify this rule.

Additionally, a hack was inroduced in the methods for getting label
expressions on the SnapAppSet. If a plug/slot did not originate from the
same snap that the SnapAppSet was created from, then we will use the
snap.Info that the plug/slot carries in the method instead. This will
fail to work once component hooks are introduced, so this will need to
be resolved by then.

* interfaces: test fallback for using LabelExpr methods with mismatch plug/slot

* snap: correct placement of TODOs to preserve doc comments

* snap: add doc comments for Plug/Slot.Unscoped

* interfaces: test for using SecurityTagsForPlug and SecurityTagsForSlot with wrong snap

* interfaces: tweak error messages in SnapAppSet SecurityTags methods

* i/builtin: fix missed conflict

* i/apparmor: add doc comment to Specification.appSet

* snap: fix doc coment on PlugInfo.Unscoped
2024-02-19 11:57:42 +01:00

167 lines
4.9 KiB
Go

// -*- Mode: Go; indent-tabs-mode: t -*-
/*
* Copyright (C) 2016-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 seccomp
import (
"bytes"
"sort"
"github.com/snapcore/snapd/interfaces"
"github.com/snapcore/snapd/snap"
)
// Specification keeps all the seccomp snippets.
type Specification struct {
appSet *interfaces.SnapAppSet
// Snippets are indexed by security tag.
snippets map[string][]string
securityTags []string
}
func NewSpecification(appSet *interfaces.SnapAppSet) *Specification {
return &Specification{
appSet: appSet,
}
}
func (spec *Specification) SnapAppSet() *interfaces.SnapAppSet {
return spec.appSet
}
// AddSnippet adds a new seccomp snippet.
func (spec *Specification) AddSnippet(snippet string) {
if len(spec.securityTags) == 0 {
return
}
if spec.snippets == nil {
spec.snippets = make(map[string][]string)
}
for _, tag := range spec.securityTags {
spec.snippets[tag] = append(spec.snippets[tag], snippet)
}
}
// Snippets returns a deep copy of all the added snippets.
func (spec *Specification) Snippets() map[string][]string {
result := make(map[string][]string, len(spec.snippets))
for k, v := range spec.snippets {
vCopy := make([]string, 0, len(v))
vCopy = append(vCopy, v...)
result[k] = vCopy
}
return result
}
// SnippetForTag returns a combined snippet for given security tag with individual snippets
// joined with newline character. Empty string is returned for non-existing security tag.
func (spec *Specification) SnippetForTag(tag string) string {
var buffer bytes.Buffer
sort.Strings(spec.snippets[tag])
for _, snippet := range spec.snippets[tag] {
buffer.WriteString(snippet)
buffer.WriteRune('\n')
}
return buffer.String()
}
// SecurityTags returns a list of security tags which have a snippet.
func (spec *Specification) SecurityTags() []string {
var tags []string
for t := range spec.snippets {
tags = append(tags, t)
}
sort.Strings(tags)
return tags
}
// Implementation of methods required by interfaces.Specification
// AddConnectedPlug records seccomp-specific side-effects of having a connected plug.
func (spec *Specification) AddConnectedPlug(iface interfaces.Interface, plug *interfaces.ConnectedPlug, slot *interfaces.ConnectedSlot) error {
type definer interface {
SecCompConnectedPlug(spec *Specification, plug *interfaces.ConnectedPlug, slot *interfaces.ConnectedSlot) error
}
if iface, ok := iface.(definer); ok {
tags, err := spec.appSet.SecurityTagsForConnectedPlug(plug)
if err != nil {
return err
}
spec.securityTags = tags
defer func() { spec.securityTags = nil }()
return iface.SecCompConnectedPlug(spec, plug, slot)
}
return nil
}
// AddConnectedSlot records seccomp-specific side-effects of having a connected slot.
func (spec *Specification) AddConnectedSlot(iface interfaces.Interface, plug *interfaces.ConnectedPlug, slot *interfaces.ConnectedSlot) error {
type definer interface {
SecCompConnectedSlot(spec *Specification, plug *interfaces.ConnectedPlug, slot *interfaces.ConnectedSlot) error
}
if iface, ok := iface.(definer); ok {
tags, err := spec.appSet.SecurityTagsForConnectedSlot(slot)
if err != nil {
return err
}
spec.securityTags = tags
defer func() { spec.securityTags = nil }()
return iface.SecCompConnectedSlot(spec, plug, slot)
}
return nil
}
// AddPermanentPlug records seccomp-specific side-effects of having a plug.
func (spec *Specification) AddPermanentPlug(iface interfaces.Interface, plug *snap.PlugInfo) error {
type definer interface {
SecCompPermanentPlug(spec *Specification, plug *snap.PlugInfo) error
}
if iface, ok := iface.(definer); ok {
tags, err := spec.appSet.SecurityTagsForPlug(plug)
if err != nil {
return err
}
spec.securityTags = tags
defer func() { spec.securityTags = nil }()
return iface.SecCompPermanentPlug(spec, plug)
}
return nil
}
// AddPermanentSlot records seccomp-specific side-effects of having a slot.
func (spec *Specification) AddPermanentSlot(iface interfaces.Interface, slot *snap.SlotInfo) error {
type definer interface {
SecCompPermanentSlot(spec *Specification, slot *snap.SlotInfo) error
}
if iface, ok := iface.(definer); ok {
tags, err := spec.appSet.SecurityTagsForSlot(slot)
if err != nil {
return err
}
spec.securityTags = tags
defer func() { spec.securityTags = nil }()
return iface.SecCompPermanentSlot(spec, slot)
}
return nil
}