github.com/rigado/snapd@v2.42.5-go-mod+incompatible/interfaces/dbus/backend_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 dbus_test
    21  
    22  import (
    23  	"fmt"
    24  	"io/ioutil"
    25  	"os"
    26  	"path/filepath"
    27  
    28  	. "gopkg.in/check.v1"
    29  
    30  	"github.com/snapcore/snapd/dirs"
    31  	"github.com/snapcore/snapd/interfaces"
    32  	"github.com/snapcore/snapd/interfaces/dbus"
    33  	"github.com/snapcore/snapd/interfaces/ifacetest"
    34  	"github.com/snapcore/snapd/snap"
    35  	"github.com/snapcore/snapd/snap/snaptest"
    36  	"github.com/snapcore/snapd/testutil"
    37  )
    38  
    39  type backendSuite struct {
    40  	ifacetest.BackendSuite
    41  }
    42  
    43  var _ = Suite(&backendSuite{})
    44  
    45  var testedConfinementOpts = []interfaces.ConfinementOptions{
    46  	{},
    47  	{DevMode: true},
    48  	{JailMode: true},
    49  	{Classic: true},
    50  }
    51  
    52  func (s *backendSuite) SetUpTest(c *C) {
    53  	s.Backend = &dbus.Backend{}
    54  	s.BackendSuite.SetUpTest(c)
    55  	c.Assert(s.Repo.AddBackend(s.Backend), IsNil)
    56  
    57  	// Prepare a directory for DBus configuration files.
    58  	// NOTE: Normally this is a part of the OS snap.
    59  	err := os.MkdirAll(dirs.SnapBusPolicyDir, 0700)
    60  	c.Assert(err, IsNil)
    61  }
    62  
    63  func (s *backendSuite) TearDownTest(c *C) {
    64  	s.BackendSuite.TearDownTest(c)
    65  }
    66  
    67  // Tests for Setup() and Remove()
    68  func (s *backendSuite) TestName(c *C) {
    69  	c.Check(s.Backend.Name(), Equals, interfaces.SecurityDBus)
    70  }
    71  
    72  func (s *backendSuite) TestInstallingSnapWritesConfigFiles(c *C) {
    73  	// NOTE: Hand out a permanent snippet so that .conf file is generated.
    74  	s.Iface.DBusPermanentSlotCallback = func(spec *dbus.Specification, slot *snap.SlotInfo) error {
    75  		spec.AddSnippet("<policy/>")
    76  		return nil
    77  	}
    78  	for _, opts := range testedConfinementOpts {
    79  		snapInfo := s.InstallSnap(c, opts, "", ifacetest.SambaYamlV1, 0)
    80  		profile := filepath.Join(dirs.SnapBusPolicyDir, "snap.samba.smbd.conf")
    81  		// file called "snap.sambda.smbd.conf" was created
    82  		_, err := os.Stat(profile)
    83  		c.Check(err, IsNil)
    84  		s.RemoveSnap(c, snapInfo)
    85  	}
    86  }
    87  
    88  func (s *backendSuite) TestInstallingSnapWithHookWritesConfigFiles(c *C) {
    89  	// NOTE: Hand out a permanent snippet so that .conf file is generated.
    90  	s.Iface.DBusPermanentSlotCallback = func(spec *dbus.Specification, slot *snap.SlotInfo) error {
    91  		spec.AddSnippet("<policy/>")
    92  		return nil
    93  	}
    94  	s.Iface.DBusPermanentPlugCallback = func(spec *dbus.Specification, plug *snap.PlugInfo) error {
    95  		spec.AddSnippet("<policy/>")
    96  		return nil
    97  	}
    98  	for _, opts := range testedConfinementOpts {
    99  		snapInfo := s.InstallSnap(c, opts, "", ifacetest.HookYaml, 0)
   100  		profile := filepath.Join(dirs.SnapBusPolicyDir, "snap.foo.hook.configure.conf")
   101  
   102  		// Verify that "snap.foo.hook.configure.conf" was created
   103  		_, err := os.Stat(profile)
   104  		c.Check(err, IsNil)
   105  		s.RemoveSnap(c, snapInfo)
   106  	}
   107  }
   108  
   109  func (s *backendSuite) TestRemovingSnapRemovesConfigFiles(c *C) {
   110  	// NOTE: Hand out a permanent snippet so that .conf file is generated.
   111  	s.Iface.DBusPermanentSlotCallback = func(spec *dbus.Specification, slot *snap.SlotInfo) error {
   112  		spec.AddSnippet("<policy/>")
   113  		return nil
   114  	}
   115  	for _, opts := range testedConfinementOpts {
   116  		snapInfo := s.InstallSnap(c, opts, "", ifacetest.SambaYamlV1, 0)
   117  		s.RemoveSnap(c, snapInfo)
   118  		profile := filepath.Join(dirs.SnapBusPolicyDir, "snap.samba.smbd.conf")
   119  		// file called "snap.sambda.smbd.conf" was removed
   120  		_, err := os.Stat(profile)
   121  		c.Check(os.IsNotExist(err), Equals, true)
   122  	}
   123  }
   124  
   125  func (s *backendSuite) TestRemovingSnapWithHookRemovesConfigFiles(c *C) {
   126  	// NOTE: Hand out a permanent snippet so that .conf file is generated.
   127  	s.Iface.DBusPermanentSlotCallback = func(spec *dbus.Specification, slot *snap.SlotInfo) error {
   128  		spec.AddSnippet("<policy/>")
   129  		return nil
   130  	}
   131  	s.Iface.DBusPermanentPlugCallback = func(spec *dbus.Specification, plug *snap.PlugInfo) error {
   132  		spec.AddSnippet("<policy/>")
   133  		return nil
   134  	}
   135  	for _, opts := range testedConfinementOpts {
   136  		snapInfo := s.InstallSnap(c, opts, "", ifacetest.HookYaml, 0)
   137  		s.RemoveSnap(c, snapInfo)
   138  		profile := filepath.Join(dirs.SnapBusPolicyDir, "snap.foo.hook.configure.conf")
   139  
   140  		// Verify that "snap.foo.hook.configure.conf" was removed
   141  		_, err := os.Stat(profile)
   142  		c.Check(os.IsNotExist(err), Equals, true)
   143  	}
   144  }
   145  
   146  func (s *backendSuite) TestUpdatingSnapToOneWithMoreApps(c *C) {
   147  	// NOTE: Hand out a permanent snippet so that .conf file is generated.
   148  	s.Iface.DBusPermanentSlotCallback = func(spec *dbus.Specification, slot *snap.SlotInfo) error {
   149  		spec.AddSnippet("<policy/>")
   150  		return nil
   151  	}
   152  	for _, opts := range testedConfinementOpts {
   153  		snapInfo := s.InstallSnap(c, opts, "", ifacetest.SambaYamlV1, 0)
   154  		snapInfo = s.UpdateSnap(c, snapInfo, opts, ifacetest.SambaYamlV1WithNmbd, 0)
   155  		profile := filepath.Join(dirs.SnapBusPolicyDir, "snap.samba.nmbd.conf")
   156  		// file called "snap.sambda.nmbd.conf" was created
   157  		_, err := os.Stat(profile)
   158  		c.Check(err, IsNil)
   159  		s.RemoveSnap(c, snapInfo)
   160  	}
   161  }
   162  
   163  func (s *backendSuite) TestUpdatingSnapToOneWithMoreHooks(c *C) {
   164  	// NOTE: Hand out a permanent snippet so that .conf file is generated.
   165  	s.Iface.DBusPermanentSlotCallback = func(spec *dbus.Specification, slot *snap.SlotInfo) error {
   166  		spec.AddSnippet("<policy/>")
   167  		return nil
   168  	}
   169  	s.Iface.DBusPermanentPlugCallback = func(spec *dbus.Specification, plug *snap.PlugInfo) error {
   170  		spec.AddSnippet("<policy/>")
   171  		return nil
   172  	}
   173  	for _, opts := range testedConfinementOpts {
   174  		snapInfo := s.InstallSnap(c, opts, "", ifacetest.SambaYamlV1, 0)
   175  		snapInfo = s.UpdateSnap(c, snapInfo, opts, ifacetest.SambaYamlWithHook, 0)
   176  		profile := filepath.Join(dirs.SnapBusPolicyDir, "snap.samba.hook.configure.conf")
   177  
   178  		// Verify that "snap.samba.hook.configure.conf" was created
   179  		_, err := os.Stat(profile)
   180  		c.Check(err, IsNil)
   181  		s.RemoveSnap(c, snapInfo)
   182  	}
   183  }
   184  
   185  func (s *backendSuite) TestUpdatingSnapToOneWithFewerApps(c *C) {
   186  	// NOTE: Hand out a permanent snippet so that .conf file is generated.
   187  	s.Iface.DBusPermanentSlotCallback = func(spec *dbus.Specification, slot *snap.SlotInfo) error {
   188  		spec.AddSnippet("<policy/>")
   189  		return nil
   190  	}
   191  	for _, opts := range testedConfinementOpts {
   192  		snapInfo := s.InstallSnap(c, opts, "", ifacetest.SambaYamlV1WithNmbd, 0)
   193  		snapInfo = s.UpdateSnap(c, snapInfo, opts, ifacetest.SambaYamlV1, 0)
   194  		profile := filepath.Join(dirs.SnapBusPolicyDir, "snap.samba.nmbd.conf")
   195  		// file called "snap.sambda.nmbd.conf" was removed
   196  		_, err := os.Stat(profile)
   197  		c.Check(os.IsNotExist(err), Equals, true)
   198  		s.RemoveSnap(c, snapInfo)
   199  	}
   200  }
   201  
   202  func (s *backendSuite) TestUpdatingSnapToOneWithFewerHooks(c *C) {
   203  	// NOTE: Hand out a permanent snippet so that .conf file is generated.
   204  	s.Iface.DBusPermanentSlotCallback = func(spec *dbus.Specification, slot *snap.SlotInfo) error {
   205  		spec.AddSnippet("<policy/>")
   206  		return nil
   207  	}
   208  	s.Iface.DBusPermanentPlugCallback = func(spec *dbus.Specification, plug *snap.PlugInfo) error {
   209  		spec.AddSnippet("<policy/>")
   210  		return nil
   211  	}
   212  	for _, opts := range testedConfinementOpts {
   213  		snapInfo := s.InstallSnap(c, opts, "", ifacetest.SambaYamlWithHook, 0)
   214  		snapInfo = s.UpdateSnap(c, snapInfo, opts, ifacetest.SambaYamlV1, 0)
   215  		profile := filepath.Join(dirs.SnapBusPolicyDir, "snap.samba.hook.configure.conf")
   216  
   217  		// Verify that "snap.samba.hook.configure.conf" was removed
   218  		_, err := os.Stat(profile)
   219  		c.Check(os.IsNotExist(err), Equals, true)
   220  		s.RemoveSnap(c, snapInfo)
   221  	}
   222  }
   223  
   224  func (s *backendSuite) TestCombineSnippetsWithActualSnippets(c *C) {
   225  	// NOTE: replace the real template with a shorter variant
   226  	restore := dbus.MockXMLEnvelope([]byte("<?xml>\n"), []byte("</xml>"))
   227  	defer restore()
   228  	s.Iface.DBusPermanentSlotCallback = func(spec *dbus.Specification, slot *snap.SlotInfo) error {
   229  		spec.AddSnippet("<policy>...</policy>")
   230  		return nil
   231  	}
   232  	for _, opts := range testedConfinementOpts {
   233  		snapInfo := s.InstallSnap(c, opts, "", ifacetest.SambaYamlV1, 0)
   234  		profile := filepath.Join(dirs.SnapBusPolicyDir, "snap.samba.smbd.conf")
   235  		c.Check(profile, testutil.FileEquals, "<?xml>\n<policy>...</policy>\n</xml>")
   236  		stat, err := os.Stat(profile)
   237  		c.Assert(err, IsNil)
   238  		c.Check(stat.Mode(), Equals, os.FileMode(0644))
   239  		s.RemoveSnap(c, snapInfo)
   240  	}
   241  }
   242  
   243  func (s *backendSuite) TestCombineSnippetsWithoutAnySnippets(c *C) {
   244  	for _, opts := range testedConfinementOpts {
   245  		snapInfo := s.InstallSnap(c, opts, "", ifacetest.SambaYamlV1, 0)
   246  		profile := filepath.Join(dirs.SnapBusPolicyDir, "snap.samba.smbd.conf")
   247  		_, err := os.Stat(profile)
   248  		// Without any snippets, there the .conf file is not created.
   249  		c.Check(os.IsNotExist(err), Equals, true)
   250  		s.RemoveSnap(c, snapInfo)
   251  	}
   252  }
   253  
   254  const sambaYamlWithIfaceBoundToNmbd = `
   255  name: samba
   256  version: 1
   257  developer: acme
   258  apps:
   259      smbd:
   260      nmbd:
   261          slots: [iface]
   262  `
   263  
   264  func (s *backendSuite) TestAppBoundIfaces(c *C) {
   265  	// NOTE: Hand out a permanent snippet so that .conf file is generated.
   266  	s.Iface.DBusPermanentSlotCallback = func(spec *dbus.Specification, slot *snap.SlotInfo) error {
   267  		spec.AddSnippet("<policy/>")
   268  		return nil
   269  	}
   270  	// Install a snap with two apps, only one of which needs a .conf file
   271  	// because the interface is app-bound.
   272  	snapInfo := s.InstallSnap(c, interfaces.ConfinementOptions{}, "", sambaYamlWithIfaceBoundToNmbd, 0)
   273  	defer s.RemoveSnap(c, snapInfo)
   274  	// Check that only one of the .conf files is actually created
   275  	_, err := os.Stat(filepath.Join(dirs.SnapBusPolicyDir, "snap.samba.smbd.conf"))
   276  	c.Check(os.IsNotExist(err), Equals, true)
   277  	_, err = os.Stat(filepath.Join(dirs.SnapBusPolicyDir, "snap.samba.nmbd.conf"))
   278  	c.Check(err, IsNil)
   279  }
   280  
   281  func (s *backendSuite) TestSandboxFeatures(c *C) {
   282  	c.Assert(s.Backend.SandboxFeatures(), DeepEquals, []string{"mediated-bus-access"})
   283  }
   284  
   285  func makeFakeDbusUserdServiceFiles(c *C, coreOrSnapdSnap *snap.Info) {
   286  	err := os.MkdirAll(filepath.Join(dirs.GlobalRootDir, "/usr/share/dbus-1/services"), 0755)
   287  	c.Assert(err, IsNil)
   288  
   289  	servicesPath := filepath.Join(coreOrSnapdSnap.MountDir(), "/usr/share/dbus-1/services")
   290  	err = os.MkdirAll(servicesPath, 0755)
   291  	c.Assert(err, IsNil)
   292  
   293  	for _, fn := range []string{
   294  		"io.snapcraft.Launcher.service",
   295  		"io.snapcraft.Settings.service",
   296  	} {
   297  		content := fmt.Sprintf("content of %s for snap %s", fn, coreOrSnapdSnap.InstanceName())
   298  		err = ioutil.WriteFile(filepath.Join(servicesPath, fn), []byte(content), 0644)
   299  		c.Assert(err, IsNil)
   300  	}
   301  }
   302  
   303  func (s *backendSuite) testSetupWritesUsedFilesForCoreOrSnapd(c *C, coreOrSnapdYaml string) {
   304  	coreOrSnapdInfo := snaptest.MockInfo(c, coreOrSnapdYaml, &snap.SideInfo{Revision: snap.R(2)})
   305  	makeFakeDbusUserdServiceFiles(c, coreOrSnapdInfo)
   306  
   307  	err := s.Backend.Setup(coreOrSnapdInfo, interfaces.ConfinementOptions{}, s.Repo, nil)
   308  	c.Assert(err, IsNil)
   309  
   310  	for _, fn := range []string{
   311  		"io.snapcraft.Launcher.service",
   312  		"io.snapcraft.Settings.service",
   313  	} {
   314  		c.Assert(filepath.Join(dirs.GlobalRootDir, "/usr/share/dbus-1/services/"+fn), testutil.FilePresent)
   315  	}
   316  }
   317  
   318  var (
   319  	coreYaml  string = "name: core\nversion: 1\ntype: os"
   320  	snapdYaml string = "name: snapd\nversion: 1\ntype: snapd"
   321  )
   322  
   323  func (s *backendSuite) TestSetupWritesUsedFilesForCore(c *C) {
   324  	s.testSetupWritesUsedFilesForCoreOrSnapd(c, coreYaml)
   325  }
   326  
   327  func (s *backendSuite) TestSetupWritesUsedFilesForSnapd(c *C) {
   328  	s.testSetupWritesUsedFilesForCoreOrSnapd(c, snapdYaml)
   329  }
   330  
   331  func (s *backendSuite) TestSetupWritesUsedFilesBothSnapdAndCoreInstalled(c *C) {
   332  	err := os.MkdirAll(filepath.Join(dirs.SnapMountDir, "snapd/current"), 0755)
   333  	c.Assert(err, IsNil)
   334  
   335  	coreInfo := snaptest.MockInfo(c, coreYaml, &snap.SideInfo{Revision: snap.R(2)})
   336  	makeFakeDbusUserdServiceFiles(c, coreInfo)
   337  	snapdInfo := snaptest.MockInfo(c, snapdYaml, &snap.SideInfo{Revision: snap.R(3)})
   338  	makeFakeDbusUserdServiceFiles(c, snapdInfo)
   339  
   340  	// first setup snapd which writes the files
   341  	err = s.Backend.Setup(snapdInfo, interfaces.ConfinementOptions{}, s.Repo, nil)
   342  	c.Assert(err, IsNil)
   343  
   344  	// then setup core - if both are installed snapd should win
   345  	err = s.Backend.Setup(coreInfo, interfaces.ConfinementOptions{}, s.Repo, nil)
   346  	c.Assert(err, IsNil)
   347  
   348  	for _, fn := range []string{
   349  		"io.snapcraft.Launcher.service",
   350  		"io.snapcraft.Settings.service",
   351  	} {
   352  		c.Assert(filepath.Join(dirs.GlobalRootDir, "/usr/share/dbus-1/services/"+fn), testutil.FileEquals, fmt.Sprintf("content of %s for snap snapd", fn))
   353  	}
   354  }