github.com/rigado/snapd@v2.42.5-go-mod+incompatible/interfaces/builtin/bool_file_test.go (about)

     1  // -*- Mode: Go; indent-tabs-mode: t -*-
     2  
     3  /*
     4   * Copyright (C) 2016 Canonical Ltd
     5   *
     6   * This program is free software: you can redistribute it and/or modify
     7   * it under the terms of the GNU General Public License version 3 as
     8   * published by the Free Software Foundation.
     9   *
    10   * This program is distributed in the hope that it will be useful,
    11   * but WITHOUT ANY WARRANTY; without even the implied warranty of
    12   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    13   * GNU General Public License for more details.
    14   *
    15   * You should have received a copy of the GNU General Public License
    16   * along with this program.  If not, see <http://www.gnu.org/licenses/>.
    17   *
    18   */
    19  
    20  package builtin_test
    21  
    22  import (
    23  	"fmt"
    24  	"testing"
    25  
    26  	. "gopkg.in/check.v1"
    27  
    28  	"github.com/snapcore/snapd/interfaces"
    29  	"github.com/snapcore/snapd/interfaces/apparmor"
    30  	"github.com/snapcore/snapd/interfaces/builtin"
    31  	"github.com/snapcore/snapd/interfaces/dbus"
    32  	"github.com/snapcore/snapd/interfaces/seccomp"
    33  	"github.com/snapcore/snapd/interfaces/udev"
    34  	"github.com/snapcore/snapd/snap"
    35  	"github.com/snapcore/snapd/snap/snaptest"
    36  	"github.com/snapcore/snapd/testutil"
    37  )
    38  
    39  func Test(t *testing.T) {
    40  	TestingT(t)
    41  }
    42  
    43  type BoolFileInterfaceSuite struct {
    44  	testutil.BaseTest
    45  	iface                 interfaces.Interface
    46  	gpioSlotInfo          *snap.SlotInfo
    47  	gpioSlot              *interfaces.ConnectedSlot
    48  	ledSlotInfo           *snap.SlotInfo
    49  	ledSlot               *interfaces.ConnectedSlot
    50  	badPathSlotInfo       *snap.SlotInfo
    51  	badPathSlot           *interfaces.ConnectedSlot
    52  	parentDirPathSlotInfo *snap.SlotInfo
    53  	parentDirPathSlot     *interfaces.ConnectedSlot
    54  	missingPathSlotInfo   *snap.SlotInfo
    55  	missingPathSlot       *interfaces.ConnectedSlot
    56  	badInterfaceSlot      *interfaces.ConnectedSlot
    57  	plugInfo              *snap.PlugInfo
    58  	plug                  *interfaces.ConnectedPlug
    59  	badInterfaceSlotInfo  *snap.SlotInfo
    60  	badInterfacePlugInfo  *snap.PlugInfo
    61  	badInterfacePlug      *interfaces.ConnectedPlug
    62  }
    63  
    64  var _ = Suite(&BoolFileInterfaceSuite{
    65  	iface: builtin.MustInterface("bool-file"),
    66  })
    67  
    68  func (s *BoolFileInterfaceSuite) SetUpTest(c *C) {
    69  	plugSnapinfo := snaptest.MockInfo(c, `
    70  name: other
    71  version: 0
    72  plugs:
    73   plug: bool-file
    74  apps:
    75   app:
    76    command: foo
    77  `, nil)
    78  	info := snaptest.MockInfo(c, `
    79  name: ubuntu-core
    80  version: 0
    81  slots:
    82      gpio:
    83          interface: bool-file
    84          path: /sys/class/gpio/gpio13/value
    85      led:
    86          interface: bool-file
    87          path: "/sys/class/leds/input27::capslock/brightness"
    88      missing-path: bool-file
    89      bad-path:
    90          interface: bool-file
    91          path: path
    92      parent-dir-path:
    93          interface: bool-file
    94          path: "/sys/class/gpio/../value"
    95      bad-interface-slot: other-interface
    96  plugs:
    97      plug: bool-file
    98      bad-interface-plug: other-interface
    99  `, &snap.SideInfo{})
   100  	s.gpioSlotInfo = info.Slots["gpio"]
   101  	s.gpioSlot = interfaces.NewConnectedSlot(s.gpioSlotInfo, nil, nil)
   102  	s.ledSlotInfo = info.Slots["led"]
   103  	s.ledSlot = interfaces.NewConnectedSlot(s.ledSlotInfo, nil, nil)
   104  	s.missingPathSlotInfo = info.Slots["missing-path"]
   105  	s.missingPathSlot = interfaces.NewConnectedSlot(s.missingPathSlotInfo, nil, nil)
   106  	s.badPathSlotInfo = info.Slots["bad-path"]
   107  	s.badPathSlot = interfaces.NewConnectedSlot(s.badPathSlotInfo, nil, nil)
   108  	s.parentDirPathSlotInfo = info.Slots["parent-dir-path"]
   109  	s.parentDirPathSlot = interfaces.NewConnectedSlot(s.parentDirPathSlotInfo, nil, nil)
   110  	s.badInterfaceSlotInfo = info.Slots["bad-interface-slot"]
   111  	s.badInterfaceSlot = interfaces.NewConnectedSlot(s.badInterfaceSlotInfo, nil, nil)
   112  	s.plugInfo = plugSnapinfo.Plugs["plug"]
   113  	s.plug = interfaces.NewConnectedPlug(s.plugInfo, nil, nil)
   114  	s.badInterfacePlugInfo = info.Plugs["bad-interface-plug"]
   115  	s.badInterfacePlug = interfaces.NewConnectedPlug(s.badInterfacePlugInfo, nil, nil)
   116  }
   117  
   118  // TODO: add test for permanent slot when we have hook support.
   119  
   120  func (s *BoolFileInterfaceSuite) TestName(c *C) {
   121  	c.Assert(s.iface.Name(), Equals, "bool-file")
   122  }
   123  
   124  func (s *BoolFileInterfaceSuite) TestSanitizeSlot(c *C) {
   125  	// Both LED and GPIO slots are accepted
   126  	c.Assert(interfaces.BeforePrepareSlot(s.iface, s.ledSlotInfo), IsNil)
   127  	c.Assert(interfaces.BeforePrepareSlot(s.iface, s.gpioSlotInfo), IsNil)
   128  	// Slots without the "path" attribute are rejected.
   129  	c.Assert(interfaces.BeforePrepareSlot(s.iface, s.missingPathSlotInfo), ErrorMatches,
   130  		"bool-file must contain the path attribute")
   131  	// Slots without the "path" attribute are rejected.
   132  	c.Assert(interfaces.BeforePrepareSlot(s.iface, s.parentDirPathSlotInfo), ErrorMatches,
   133  		"bool-file can only point at LED brightness or GPIO value")
   134  	// Slots with incorrect value of the "path" attribute are rejected.
   135  	c.Assert(interfaces.BeforePrepareSlot(s.iface, s.badPathSlotInfo), ErrorMatches,
   136  		"bool-file can only point at LED brightness or GPIO value")
   137  }
   138  
   139  func (s *BoolFileInterfaceSuite) TestSanitizePlug(c *C) {
   140  	c.Assert(interfaces.BeforePreparePlug(s.iface, s.plugInfo), IsNil)
   141  }
   142  
   143  func (s *BoolFileInterfaceSuite) TestPlugSnippetHandlesSymlinkErrors(c *C) {
   144  	// Symbolic link traversal is handled correctly
   145  	builtin.MockEvalSymlinks(&s.BaseTest, func(path string) (string, error) {
   146  		return "", fmt.Errorf("broken symbolic link")
   147  	})
   148  
   149  	apparmorSpec := &apparmor.Specification{}
   150  	err := apparmorSpec.AddConnectedPlug(s.iface, s.plug, s.gpioSlot)
   151  	c.Assert(err, ErrorMatches, "cannot compute plug security snippet: broken symbolic link")
   152  	c.Assert(apparmorSpec.SecurityTags(), HasLen, 0)
   153  }
   154  
   155  func (s *BoolFileInterfaceSuite) TestPlugSnippetDereferencesSymlinks(c *C) {
   156  	// Use a fake (successful) dereferencing function for the remainder of the test.
   157  	builtin.MockEvalSymlinks(&s.BaseTest, func(path string) (string, error) {
   158  		return "(dereferenced)" + path, nil
   159  	})
   160  	// Extra apparmor permission to access GPIO value
   161  	// The path uses dereferenced symbolic links.
   162  	apparmorSpec := &apparmor.Specification{}
   163  	err := apparmorSpec.AddConnectedPlug(s.iface, s.plug, s.gpioSlot)
   164  	c.Assert(err, IsNil)
   165  	c.Assert(apparmorSpec.SecurityTags(), DeepEquals, []string{"snap.other.app"})
   166  	c.Assert(apparmorSpec.SnippetForTag("snap.other.app"), Equals, "(dereferenced)/sys/class/gpio/gpio13/value rwk,")
   167  	// Extra apparmor permission to access LED brightness.
   168  	// The path uses dereferenced symbolic links.
   169  	apparmorSpec = &apparmor.Specification{}
   170  	err = apparmorSpec.AddConnectedPlug(s.iface, s.plug, s.ledSlot)
   171  	c.Assert(err, IsNil)
   172  	c.Assert(apparmorSpec.SecurityTags(), DeepEquals, []string{"snap.other.app"})
   173  	c.Assert(apparmorSpec.SnippetForTag("snap.other.app"), Equals, "(dereferenced)/sys/class/leds/input27::capslock/brightness rwk,")
   174  }
   175  
   176  func (s *BoolFileInterfaceSuite) TestConnectedPlugSnippetPanicksOnUnsanitizedSlots(c *C) {
   177  	// Unsanitized slots should never be used and cause a panic.
   178  	c.Assert(func() {
   179  		apparmorSpec := &apparmor.Specification{}
   180  		apparmorSpec.AddConnectedPlug(s.iface, s.plug, s.missingPathSlot)
   181  	}, PanicMatches, "slot is not sanitized")
   182  }
   183  
   184  func (s *BoolFileInterfaceSuite) TestConnectedPlugSnippetUnusedSecuritySystems(c *C) {
   185  	for _, slot := range []*interfaces.ConnectedSlot{s.ledSlot, s.gpioSlot} {
   186  		// No extra seccomp permissions for plug
   187  		seccompSpec := &seccomp.Specification{}
   188  		err := seccompSpec.AddConnectedPlug(s.iface, s.plug, slot)
   189  		c.Assert(err, IsNil)
   190  		c.Assert(seccompSpec.Snippets(), HasLen, 0)
   191  		// No extra dbus permissions for plug
   192  		dbusSpec := &dbus.Specification{}
   193  		err = dbusSpec.AddConnectedPlug(s.iface, s.plug, slot)
   194  		c.Assert(err, IsNil)
   195  		c.Assert(dbusSpec.Snippets(), HasLen, 0)
   196  		// No extra udev permissions for plug
   197  		udevSpec := &udev.Specification{}
   198  		c.Assert(udevSpec.AddConnectedPlug(s.iface, s.plug, slot), IsNil)
   199  		c.Assert(udevSpec.Snippets(), HasLen, 0)
   200  	}
   201  }
   202  
   203  func (s *BoolFileInterfaceSuite) TestPermanentPlugSnippetUnusedSecuritySystems(c *C) {
   204  	// No extra seccomp permissions for plug
   205  	seccompSpec := &seccomp.Specification{}
   206  	err := seccompSpec.AddPermanentPlug(s.iface, s.plugInfo)
   207  	c.Assert(err, IsNil)
   208  	c.Assert(seccompSpec.Snippets(), HasLen, 0)
   209  	// No extra dbus permissions for plug
   210  	dbusSpec := &dbus.Specification{}
   211  	err = dbusSpec.AddPermanentPlug(s.iface, s.plugInfo)
   212  	c.Assert(err, IsNil)
   213  	c.Assert(dbusSpec.Snippets(), HasLen, 0)
   214  	// No extra udev permissions for plug
   215  	udevSpec := &udev.Specification{}
   216  	c.Assert(udevSpec.AddPermanentPlug(s.iface, s.plugInfo), IsNil)
   217  	c.Assert(udevSpec.Snippets(), HasLen, 0)
   218  }
   219  
   220  func (s *BoolFileInterfaceSuite) TestInterfaces(c *C) {
   221  	c.Check(builtin.Interfaces(), testutil.DeepContains, s.iface)
   222  }