github.com/stolowski/snapd@v0.0.0-20210407085831-115137ce5a22/interfaces/policy/basedeclaration_test.go (about)

     1  // -*- Mode: Go; indent-tabs-mode: t -*-
     2  
     3  /*
     4   * Copyright (C) 2016-2018 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 policy_test
    21  
    22  import (
    23  	"fmt"
    24  	"strings"
    25  
    26  	. "gopkg.in/check.v1"
    27  
    28  	"github.com/snapcore/snapd/asserts"
    29  	"github.com/snapcore/snapd/interfaces"
    30  	"github.com/snapcore/snapd/interfaces/builtin"
    31  	"github.com/snapcore/snapd/interfaces/policy"
    32  	"github.com/snapcore/snapd/release"
    33  	"github.com/snapcore/snapd/snap"
    34  	"github.com/snapcore/snapd/snap/snaptest"
    35  	"github.com/snapcore/snapd/strutil"
    36  	"github.com/snapcore/snapd/testutil"
    37  )
    38  
    39  type baseDeclSuite struct {
    40  	baseDecl        *asserts.BaseDeclaration
    41  	restoreSanitize func()
    42  }
    43  
    44  var _ = Suite(&baseDeclSuite{})
    45  
    46  func (s *baseDeclSuite) SetUpSuite(c *C) {
    47  	s.restoreSanitize = snap.MockSanitizePlugsSlots(func(snapInfo *snap.Info) {})
    48  	s.baseDecl = asserts.BuiltinBaseDeclaration()
    49  }
    50  
    51  func (s *baseDeclSuite) TearDownSuite(c *C) {
    52  	s.restoreSanitize()
    53  }
    54  
    55  func (s *baseDeclSuite) connectCand(c *C, iface, slotYaml, plugYaml string) *policy.ConnectCandidate {
    56  	if slotYaml == "" {
    57  		slotYaml = fmt.Sprintf(`name: slot-snap
    58  version: 0
    59  slots:
    60    %s:
    61  `, iface)
    62  	}
    63  	if plugYaml == "" {
    64  		plugYaml = fmt.Sprintf(`name: plug-snap
    65  version: 0
    66  plugs:
    67    %s:
    68  `, iface)
    69  	}
    70  	slotSnap := snaptest.MockInfo(c, slotYaml, nil)
    71  	plugSnap := snaptest.MockInfo(c, plugYaml, nil)
    72  	return &policy.ConnectCandidate{
    73  		Plug:            interfaces.NewConnectedPlug(plugSnap.Plugs[iface], nil, nil),
    74  		Slot:            interfaces.NewConnectedSlot(slotSnap.Slots[iface], nil, nil),
    75  		BaseDeclaration: s.baseDecl,
    76  	}
    77  }
    78  
    79  func (s *baseDeclSuite) installSlotCand(c *C, iface string, snapType snap.Type, yaml string) *policy.InstallCandidate {
    80  	if yaml == "" {
    81  		yaml = fmt.Sprintf(`name: install-slot-snap
    82  version: 0
    83  type: %s
    84  slots:
    85    %s:
    86  `, snapType, iface)
    87  	}
    88  	snap := snaptest.MockInfo(c, yaml, nil)
    89  	return &policy.InstallCandidate{
    90  		Snap:            snap,
    91  		BaseDeclaration: s.baseDecl,
    92  	}
    93  }
    94  
    95  func (s *baseDeclSuite) installPlugCand(c *C, iface string, snapType snap.Type, yaml string) *policy.InstallCandidate {
    96  	if yaml == "" {
    97  		yaml = fmt.Sprintf(`name: install-plug-snap
    98  version: 0
    99  type: %s
   100  plugs:
   101    %s:
   102  `, snapType, iface)
   103  	}
   104  	snap := snaptest.MockInfo(c, yaml, nil)
   105  	return &policy.InstallCandidate{
   106  		Snap:            snap,
   107  		BaseDeclaration: s.baseDecl,
   108  	}
   109  }
   110  
   111  const declTempl = `type: snap-declaration
   112  authority-id: canonical
   113  series: 16
   114  snap-name: @name@
   115  snap-id: @snapid@
   116  publisher-id: @publisher@
   117  @plugsSlots@
   118  timestamp: 2016-09-30T12:00:00Z
   119  sign-key-sha3-384: Jv8_JiHiIzJVcO9M55pPdqSDWUvuhfDIBJUS-3VW7F_idjix7Ffn5qMxB21ZQuij
   120  
   121  AXNpZw==`
   122  
   123  func (s *baseDeclSuite) mockSnapDecl(c *C, name, snapID, publisher string, plugsSlots string) *asserts.SnapDeclaration {
   124  	encoded := strings.Replace(declTempl, "@name@", name, 1)
   125  	encoded = strings.Replace(encoded, "@snapid@", snapID, 1)
   126  	encoded = strings.Replace(encoded, "@publisher@", publisher, 1)
   127  	if plugsSlots != "" {
   128  		encoded = strings.Replace(encoded, "@plugsSlots@", strings.TrimSpace(plugsSlots), 1)
   129  	} else {
   130  		encoded = strings.Replace(encoded, "@plugsSlots@\n", "", 1)
   131  	}
   132  	a, err := asserts.Decode([]byte(encoded))
   133  	c.Assert(err, IsNil)
   134  	return a.(*asserts.SnapDeclaration)
   135  }
   136  
   137  func (s *baseDeclSuite) TestAutoConnection(c *C) {
   138  	all := builtin.Interfaces()
   139  
   140  	// these have more complex or in flux policies and have their
   141  	// own separate tests
   142  	snowflakes := map[string]bool{
   143  		"content":            true,
   144  		"core-support":       true,
   145  		"home":               true,
   146  		"lxd-support":        true,
   147  		"multipass-support":  true,
   148  		"packagekit-control": true,
   149  		"snapd-control":      true,
   150  		"dummy":              true,
   151  	}
   152  
   153  	// these simply auto-connect, anything else doesn't
   154  	autoconnect := map[string]bool{
   155  		"audio-playback":          true,
   156  		"browser-support":         true,
   157  		"desktop":                 true,
   158  		"desktop-legacy":          true,
   159  		"gsettings":               true,
   160  		"media-hub":               true,
   161  		"mir":                     true,
   162  		"network":                 true,
   163  		"network-bind":            true,
   164  		"network-status":          true,
   165  		"online-accounts-service": true,
   166  		"opengl":                  true,
   167  		"optical-drive":           true,
   168  		"screen-inhibit-control":  true,
   169  		"ubuntu-download-manager": true,
   170  		"unity7":                  true,
   171  		"unity8":                  true,
   172  		"upower-observe":          true,
   173  		"wayland":                 true,
   174  		"x11":                     true,
   175  	}
   176  
   177  	for _, iface := range all {
   178  		if snowflakes[iface.Name()] {
   179  			continue
   180  		}
   181  		expected := autoconnect[iface.Name()]
   182  		comm := Commentf(iface.Name())
   183  
   184  		// check base declaration
   185  		cand := s.connectCand(c, iface.Name(), "", "")
   186  		arity, err := cand.CheckAutoConnect()
   187  		if expected {
   188  			c.Check(err, IsNil, comm)
   189  			c.Check(arity.SlotsPerPlugAny(), Equals, false)
   190  		} else {
   191  			c.Check(err, NotNil, comm)
   192  		}
   193  	}
   194  }
   195  
   196  func (s *baseDeclSuite) TestAutoConnectPlugSlot(c *C) {
   197  	all := builtin.Interfaces()
   198  
   199  	// these have more complex or in flux policies and have their
   200  	// own separate tests
   201  	snowflakes := map[string]bool{
   202  		"classic-support": true,
   203  		"content":         true,
   204  		"home":            true,
   205  		"lxd-support":     true,
   206  	}
   207  
   208  	for _, iface := range all {
   209  		if snowflakes[iface.Name()] {
   210  			continue
   211  		}
   212  		c.Check(iface.AutoConnect(nil, nil), Equals, true)
   213  	}
   214  }
   215  
   216  func (s *baseDeclSuite) TestInterimAutoConnectionHome(c *C) {
   217  	restore := release.MockOnClassic(true)
   218  	defer restore()
   219  	cand := s.connectCand(c, "home", "", "")
   220  	arity, err := cand.CheckAutoConnect()
   221  	c.Check(err, IsNil)
   222  	c.Check(arity.SlotsPerPlugAny(), Equals, false)
   223  
   224  	release.OnClassic = false
   225  	_, err = cand.CheckAutoConnect()
   226  	c.Check(err, ErrorMatches, `auto-connection denied by slot rule of interface \"home\"`)
   227  }
   228  
   229  func (s *baseDeclSuite) TestHomeReadAll(c *C) {
   230  	const plugYaml = `name: plug-snap
   231  version: 0
   232  plugs:
   233    home:
   234      read: all
   235  `
   236  	restore := release.MockOnClassic(true)
   237  	defer restore()
   238  	cand := s.connectCand(c, "home", "", plugYaml)
   239  	err := cand.Check()
   240  	c.Check(err, NotNil)
   241  
   242  	_, err = cand.CheckAutoConnect()
   243  	c.Check(err, NotNil)
   244  
   245  	release.OnClassic = false
   246  	err = cand.Check()
   247  	c.Check(err, NotNil)
   248  
   249  	_, err = cand.CheckAutoConnect()
   250  	c.Check(err, NotNil)
   251  }
   252  
   253  func (s *baseDeclSuite) TestHomeReadDefault(c *C) {
   254  	const plugYaml = `name: plug-snap
   255  version: 0
   256  plugs:
   257    home: null
   258  `
   259  	restore := release.MockOnClassic(true)
   260  	defer restore()
   261  	cand := s.connectCand(c, "home", "", plugYaml)
   262  	err := cand.Check()
   263  	c.Check(err, IsNil)
   264  
   265  	// Same as TestInterimAutoConnectionHome()
   266  	arity, err := cand.CheckAutoConnect()
   267  	c.Check(err, IsNil)
   268  	c.Check(arity.SlotsPerPlugAny(), Equals, false)
   269  
   270  	release.OnClassic = false
   271  	err = cand.Check()
   272  	c.Check(err, IsNil)
   273  
   274  	// Same as TestInterimAutoConnectionHome()
   275  	_, err = cand.CheckAutoConnect()
   276  	c.Check(err, NotNil)
   277  }
   278  
   279  func (s *baseDeclSuite) TestAutoConnectionSnapdControl(c *C) {
   280  	cand := s.connectCand(c, "snapd-control", "", "")
   281  	_, err := cand.CheckAutoConnect()
   282  	c.Check(err, NotNil)
   283  	c.Assert(err, ErrorMatches, "auto-connection denied by plug rule of interface \"snapd-control\"")
   284  
   285  	plugsSlots := `
   286  plugs:
   287    snapd-control:
   288      allow-auto-connection: true
   289  `
   290  
   291  	lxdDecl := s.mockSnapDecl(c, "some-snap", "J60k4JY0HppjwOjW8dZdYc8obXKxujRu", "canonical", plugsSlots)
   292  	cand.PlugSnapDeclaration = lxdDecl
   293  	arity, err := cand.CheckAutoConnect()
   294  	c.Check(err, IsNil)
   295  	c.Check(arity.SlotsPerPlugAny(), Equals, false)
   296  }
   297  
   298  func (s *baseDeclSuite) TestAutoConnectionContent(c *C) {
   299  	// random snaps cannot connect with content
   300  	// (Sanitize* will now also block this)
   301  	cand := s.connectCand(c, "content", "", "")
   302  	_, err := cand.CheckAutoConnect()
   303  	c.Check(err, NotNil)
   304  
   305  	slotDecl1 := s.mockSnapDecl(c, "slot-snap", "slot-snap-id", "pub1", "")
   306  	plugDecl1 := s.mockSnapDecl(c, "plug-snap", "plug-snap-id", "pub1", "")
   307  	plugDecl2 := s.mockSnapDecl(c, "plug-snap", "plug-snap-id", "pub2", "")
   308  
   309  	// same publisher, same content
   310  	cand = s.connectCand(c, "stuff", `
   311  name: slot-snap
   312  version: 0
   313  slots:
   314    stuff:
   315      interface: content
   316      content: mk1
   317  `, `
   318  name: plug-snap
   319  version: 0
   320  plugs:
   321    stuff:
   322      interface: content
   323      content: mk1
   324  `)
   325  	cand.SlotSnapDeclaration = slotDecl1
   326  	cand.PlugSnapDeclaration = plugDecl1
   327  	arity, err := cand.CheckAutoConnect()
   328  	c.Check(err, IsNil)
   329  	c.Check(arity.SlotsPerPlugAny(), Equals, false)
   330  
   331  	// different publisher, same content
   332  	cand.SlotSnapDeclaration = slotDecl1
   333  	cand.PlugSnapDeclaration = plugDecl2
   334  	_, err = cand.CheckAutoConnect()
   335  	c.Check(err, NotNil)
   336  
   337  	// same publisher, different content
   338  	cand = s.connectCand(c, "stuff", `name: slot-snap
   339  version: 0
   340  slots:
   341    stuff:
   342      interface: content
   343      content: mk1
   344  `, `
   345  name: plug-snap
   346  version: 0
   347  plugs:
   348    stuff:
   349      interface: content
   350      content: mk2
   351  `)
   352  	cand.SlotSnapDeclaration = slotDecl1
   353  	cand.PlugSnapDeclaration = plugDecl1
   354  	_, err = cand.CheckAutoConnect()
   355  	c.Check(err, NotNil)
   356  }
   357  
   358  func (s *baseDeclSuite) TestAutoConnectionLxdSupportOverride(c *C) {
   359  	// by default, don't auto-connect
   360  	cand := s.connectCand(c, "lxd-support", "", "")
   361  	_, err := cand.CheckAutoConnect()
   362  	c.Check(err, NotNil)
   363  
   364  	plugsSlots := `
   365  plugs:
   366    lxd-support:
   367      allow-auto-connection: true
   368  `
   369  
   370  	lxdDecl := s.mockSnapDecl(c, "lxd", "J60k4JY0HppjwOjW8dZdYc8obXKxujRu", "canonical", plugsSlots)
   371  	cand.PlugSnapDeclaration = lxdDecl
   372  	_, err = cand.CheckAutoConnect()
   373  	c.Check(err, IsNil)
   374  }
   375  
   376  func (s *baseDeclSuite) TestAutoConnectionLxdSupportOverrideRevoke(c *C) {
   377  	cand := s.connectCand(c, "lxd-support", "", "")
   378  	plugsSlots := `
   379  plugs:
   380    lxd-support:
   381      allow-auto-connection: false
   382  `
   383  
   384  	lxdDecl := s.mockSnapDecl(c, "notlxd", "J60k4JY0HppjwOjW8dZdYc8obXKxujRu", "canonical", plugsSlots)
   385  	cand.PlugSnapDeclaration = lxdDecl
   386  	_, err := cand.CheckAutoConnect()
   387  	c.Check(err, NotNil)
   388  	c.Assert(err, ErrorMatches, "auto-connection not allowed by plug rule of interface \"lxd-support\" for \"notlxd\" snap")
   389  }
   390  
   391  func (s *baseDeclSuite) TestAutoConnectionKernelModuleControlOverride(c *C) {
   392  	cand := s.connectCand(c, "kernel-module-control", "", "")
   393  	_, err := cand.CheckAutoConnect()
   394  	c.Check(err, NotNil)
   395  	c.Assert(err, ErrorMatches, "auto-connection denied by plug rule of interface \"kernel-module-control\"")
   396  
   397  	plugsSlots := `
   398  plugs:
   399    kernel-module-control:
   400      allow-auto-connection: true
   401  `
   402  
   403  	snapDecl := s.mockSnapDecl(c, "some-snap", "J60k4JY0HppjwOjW8dZdYc8obXKxujRu", "canonical", plugsSlots)
   404  	cand.PlugSnapDeclaration = snapDecl
   405  	_, err = cand.CheckAutoConnect()
   406  	c.Check(err, IsNil)
   407  }
   408  
   409  func (s *baseDeclSuite) TestAutoConnectionDockerSupportOverride(c *C) {
   410  	cand := s.connectCand(c, "docker-support", "", "")
   411  	_, err := cand.CheckAutoConnect()
   412  	c.Check(err, NotNil)
   413  	c.Assert(err, ErrorMatches, "auto-connection denied by plug rule of interface \"docker-support\"")
   414  
   415  	plugsSlots := `
   416  plugs:
   417    docker-support:
   418      allow-auto-connection: true
   419  `
   420  
   421  	snapDecl := s.mockSnapDecl(c, "some-snap", "J60k4JY0HppjwOjW8dZdYc8obXKxujRu", "canonical", plugsSlots)
   422  	cand.PlugSnapDeclaration = snapDecl
   423  	_, err = cand.CheckAutoConnect()
   424  	c.Check(err, IsNil)
   425  }
   426  
   427  func (s *baseDeclSuite) TestAutoConnectionClassicSupportOverride(c *C) {
   428  	cand := s.connectCand(c, "classic-support", "", "")
   429  	_, err := cand.CheckAutoConnect()
   430  	c.Check(err, NotNil)
   431  	c.Assert(err, ErrorMatches, "auto-connection denied by plug rule of interface \"classic-support\"")
   432  
   433  	plugsSlots := `
   434  plugs:
   435    classic-support:
   436      allow-auto-connection: true
   437  `
   438  
   439  	snapDecl := s.mockSnapDecl(c, "classic", "J60k4JY0HppjwOjW8dZdYc8obXKxujRu", "canonical", plugsSlots)
   440  	cand.PlugSnapDeclaration = snapDecl
   441  	_, err = cand.CheckAutoConnect()
   442  	c.Check(err, IsNil)
   443  }
   444  
   445  func (s *baseDeclSuite) TestAutoConnectionKubernetesSupportOverride(c *C) {
   446  	cand := s.connectCand(c, "kubernetes-support", "", "")
   447  	_, err := cand.CheckAutoConnect()
   448  	c.Check(err, NotNil)
   449  	c.Assert(err, ErrorMatches, "auto-connection denied by plug rule of interface \"kubernetes-support\"")
   450  
   451  	plugsSlots := `
   452  plugs:
   453    kubernetes-support:
   454      allow-auto-connection: true
   455  `
   456  
   457  	snapDecl := s.mockSnapDecl(c, "some-snap", "J60k4JY0HppjwOjW8dZdYc8obXKxujRu", "canonical", plugsSlots)
   458  	cand.PlugSnapDeclaration = snapDecl
   459  	_, err = cand.CheckAutoConnect()
   460  	c.Check(err, IsNil)
   461  }
   462  
   463  func (s *baseDeclSuite) TestAutoConnectionGreengrassSupportOverride(c *C) {
   464  	cand := s.connectCand(c, "greengrass-support", "", "")
   465  	_, err := cand.CheckAutoConnect()
   466  	c.Check(err, NotNil)
   467  	c.Assert(err, ErrorMatches, "auto-connection denied by plug rule of interface \"greengrass-support\"")
   468  
   469  	plugsSlots := `
   470  plugs:
   471    greengrass-support:
   472      allow-auto-connection: true
   473  `
   474  
   475  	snapDecl := s.mockSnapDecl(c, "some-snap", "J60k4JY0HppjwOjW8dZdYc8obXKxujRu", "canonical", plugsSlots)
   476  	cand.PlugSnapDeclaration = snapDecl
   477  	_, err = cand.CheckAutoConnect()
   478  	c.Check(err, IsNil)
   479  }
   480  
   481  func (s *baseDeclSuite) TestAutoConnectionMultipassSupportOverride(c *C) {
   482  	cand := s.connectCand(c, "multipass-support", "", "")
   483  	_, err := cand.CheckAutoConnect()
   484  	c.Check(err, NotNil)
   485  	c.Assert(err, ErrorMatches, "auto-connection denied by plug rule of interface \"multipass-support\"")
   486  
   487  	plugsSlots := `
   488  plugs:
   489    multipass-support:
   490      allow-auto-connection: true
   491  `
   492  
   493  	snapDecl := s.mockSnapDecl(c, "multipass-snap", "J60k4JY0HppjwOjW8dZdYc8obXKxujRu", "canonical", plugsSlots)
   494  	cand.PlugSnapDeclaration = snapDecl
   495  	_, err = cand.CheckAutoConnect()
   496  	c.Check(err, IsNil)
   497  }
   498  
   499  func (s *baseDeclSuite) TestAutoConnectionBlockDevicesOverride(c *C) {
   500  	cand := s.connectCand(c, "block-devices", "", "")
   501  	_, err := cand.CheckAutoConnect()
   502  	c.Check(err, NotNil)
   503  	c.Assert(err, ErrorMatches, "auto-connection denied by plug rule of interface \"block-devices\"")
   504  
   505  	plugsSlots := `
   506  plugs:
   507    block-devices:
   508      allow-auto-connection: true
   509  `
   510  
   511  	snapDecl := s.mockSnapDecl(c, "some-snap", "J60k4JY0HppjwOjW8dZdYc8obXKxujRu", "canonical", plugsSlots)
   512  	cand.PlugSnapDeclaration = snapDecl
   513  	_, err = cand.CheckAutoConnect()
   514  	c.Check(err, IsNil)
   515  }
   516  
   517  func (s *baseDeclSuite) TestAutoConnectionPackagekitControlOverride(c *C) {
   518  	cand := s.connectCand(c, "packagekit-control", "", "")
   519  	_, err := cand.CheckAutoConnect()
   520  	c.Check(err, NotNil)
   521  	c.Assert(err, ErrorMatches, "auto-connection denied by plug rule of interface \"packagekit-control\"")
   522  
   523  	plugsSlots := `
   524  plugs:
   525    packagekit-control:
   526      allow-auto-connection: true
   527  `
   528  
   529  	snapDecl := s.mockSnapDecl(c, "some-snap", "J60k4JY0HppjwOjW8dZdYc8obXKxujRu", "canonical", plugsSlots)
   530  	cand.PlugSnapDeclaration = snapDecl
   531  	_, err = cand.CheckAutoConnect()
   532  	c.Check(err, IsNil)
   533  }
   534  
   535  func (s *baseDeclSuite) TestAutoConnectionOverrideMultiple(c *C) {
   536  	plugsSlots := `
   537  plugs:
   538    network-bind:
   539      allow-auto-connection: true
   540    network-control:
   541      allow-auto-connection: true
   542    kernel-module-control:
   543      allow-auto-connection: true
   544    system-observe:
   545      allow-auto-connection: true
   546    hardware-observe:
   547      allow-auto-connection: true
   548  `
   549  
   550  	snapDecl := s.mockSnapDecl(c, "some-snap", "J60k4JY0HppjwOjW8dZdYc8obXKxujRu", "canonical", plugsSlots)
   551  
   552  	all := builtin.Interfaces()
   553  	// these are a mixture interfaces that the snap plugs
   554  	plugged := map[string]bool{
   555  		"network-bind":          true,
   556  		"network-control":       true,
   557  		"kernel-module-control": true,
   558  		"system-observe":        true,
   559  		"hardware-observe":      true,
   560  	}
   561  	for _, iface := range all {
   562  		if !plugged[iface.Name()] {
   563  			continue
   564  		}
   565  
   566  		cand := s.connectCand(c, iface.Name(), "", "")
   567  		cand.PlugSnapDeclaration = snapDecl
   568  		arity, err := cand.CheckAutoConnect()
   569  		c.Check(err, IsNil)
   570  		c.Check(arity.SlotsPerPlugAny(), Equals, false)
   571  	}
   572  }
   573  
   574  // describe installation rules for slots succinctly for cross-checking,
   575  // if an interface is not mentioned here a slot of its type can only
   576  // be installed by a core snap (and this was taken care by
   577  // BeforePrepareSlot),
   578  // otherwise the entry for the interface is the list of snap types it
   579  // can be installed by (using the declaration naming);
   580  // ATM a nil entry means even stricter rules that would need be tested
   581  // separately and whose implementation is in flux for now
   582  var (
   583  	unconstrained = []string{"core", "kernel", "gadget", "app"}
   584  
   585  	slotInstallation = map[string][]string{
   586  		// other
   587  		"adb-support":             {"core"},
   588  		"audio-playback":          {"app", "core"},
   589  		"audio-record":            {"app", "core"},
   590  		"autopilot-introspection": {"core"},
   591  		"avahi-control":           {"app", "core"},
   592  		"avahi-observe":           {"app", "core"},
   593  		"bluez":                   {"app", "core"},
   594  		"bool-file":               {"core", "gadget"},
   595  		"browser-support":         {"core"},
   596  		"content":                 {"app", "gadget"},
   597  		"core-support":            {"core"},
   598  		"cups":                    {"app"},
   599  		"cups-control":            {"app", "core"},
   600  		"dbus":                    {"app"},
   601  		"docker-support":          {"core"},
   602  		"dummy":                   {"app"},
   603  		"fwupd":                   {"app", "core"},
   604  		"gpio":                    {"core", "gadget"},
   605  		"gpio-control":            {"core"},
   606  		"greengrass-support":      {"core"},
   607  		"hidraw":                  {"core", "gadget"},
   608  		"i2c":                     {"core", "gadget"},
   609  		"iio":                     {"core", "gadget"},
   610  		"kubernetes-support":      {"core"},
   611  		"location-control":        {"app"},
   612  		"location-observe":        {"app"},
   613  		"lxd-support":             {"core"},
   614  		"maliit":                  {"app"},
   615  		"media-hub":               {"app", "core"},
   616  		"mir":                     {"app"},
   617  		"modem-manager":           {"app", "core"},
   618  		"mpris":                   {"app"},
   619  		"network-manager":         {"app", "core"},
   620  		"network-manager-observe": {"app", "core"},
   621  		"network-status":          {"core"},
   622  		"ofono":                   {"app", "core"},
   623  		"online-accounts-service": {"app"},
   624  		"power-control":           {"core"},
   625  		"ppp":                     {"core"},
   626  		"pulseaudio":              {"app", "core"},
   627  		"raw-volume":              {"core", "gadget"},
   628  		"serial-port":             {"core", "gadget"},
   629  		"spi":                     {"core", "gadget"},
   630  		"storage-framework-service": {"app"},
   631  		"thumbnailer-service":       {"app"},
   632  		"ubuntu-download-manager":   {"app"},
   633  		"udisks2":                   {"app", "core"},
   634  		"uhid":                      {"core"},
   635  		"uio":                       {"core", "gadget"},
   636  		"unity8":                    {"app"},
   637  		"unity8-calendar":           {"app"},
   638  		"unity8-contacts":           {"app"},
   639  		"upower-observe":            {"app", "core"},
   640  		"wayland":                   {"app", "core"},
   641  		"x11":                       {"app", "core"},
   642  		// snowflakes
   643  		"classic-support": nil,
   644  		"docker":          nil,
   645  		"lxd":             nil,
   646  	}
   647  
   648  	restrictedPlugInstallation = map[string][]string{
   649  		"core-support": {"core"},
   650  	}
   651  
   652  	snapTypeMap = map[string]snap.Type{
   653  		"core":   snap.TypeOS,
   654  		"app":    snap.TypeApp,
   655  		"kernel": snap.TypeKernel,
   656  		"gadget": snap.TypeGadget,
   657  	}
   658  )
   659  
   660  func (s *baseDeclSuite) TestSlotInstallation(c *C) {
   661  	all := builtin.Interfaces()
   662  
   663  	for _, iface := range all {
   664  		types, ok := slotInstallation[iface.Name()]
   665  		if !ok { // common ones, only core can install them,
   666  			types = []string{"core"}
   667  		}
   668  
   669  		if types == nil {
   670  			// snowflake needs to be tested specially
   671  			continue
   672  		}
   673  		for name, snapType := range snapTypeMap {
   674  			ok := strutil.ListContains(types, name)
   675  			ic := s.installSlotCand(c, iface.Name(), snapType, ``)
   676  			err := ic.Check()
   677  			comm := Commentf("%s by %s snap", iface.Name(), name)
   678  			if ok {
   679  				c.Check(err, IsNil, comm)
   680  			} else {
   681  				c.Check(err, NotNil, comm)
   682  			}
   683  		}
   684  	}
   685  
   686  	// test docker specially
   687  	ic := s.installSlotCand(c, "docker", snap.TypeApp, ``)
   688  	err := ic.Check()
   689  	c.Assert(err, Not(IsNil))
   690  	c.Assert(err, ErrorMatches, "installation not allowed by \"docker\" slot rule of interface \"docker\"")
   691  
   692  	// test lxd specially
   693  	ic = s.installSlotCand(c, "lxd", snap.TypeApp, ``)
   694  	err = ic.Check()
   695  	c.Assert(err, Not(IsNil))
   696  	c.Assert(err, ErrorMatches, "installation not allowed by \"lxd\" slot rule of interface \"lxd\"")
   697  }
   698  
   699  func (s *baseDeclSuite) TestPlugInstallation(c *C) {
   700  	all := builtin.Interfaces()
   701  
   702  	restricted := map[string]bool{
   703  		"block-devices":         true,
   704  		"classic-support":       true,
   705  		"docker-support":        true,
   706  		"greengrass-support":    true,
   707  		"gpio-control":          true,
   708  		"kernel-module-control": true,
   709  		"kubernetes-support":    true,
   710  		"lxd-support":           true,
   711  		"multipass-support":     true,
   712  		"packagekit-control":    true,
   713  		"personal-files":        true,
   714  		"snapd-control":         true,
   715  		"system-files":          true,
   716  		"tee":                   true,
   717  		"uinput":                true,
   718  		"unity8":                true,
   719  	}
   720  
   721  	for _, iface := range all {
   722  		types, ok := restrictedPlugInstallation[iface.Name()]
   723  		// If plug installation is restricted to specific snap types we
   724  		// need to make sure this is really the case here. If that is not
   725  		// the case we continue as normal.
   726  		if ok {
   727  			for name, snapType := range snapTypeMap {
   728  				ok := strutil.ListContains(types, name)
   729  				ic := s.installPlugCand(c, iface.Name(), snapType, ``)
   730  				err := ic.Check()
   731  				comm := Commentf("%s by %s snap", iface.Name(), name)
   732  				if ok {
   733  					c.Check(err, IsNil, comm)
   734  				} else {
   735  					c.Check(err, NotNil, comm)
   736  				}
   737  			}
   738  		} else {
   739  			ic := s.installPlugCand(c, iface.Name(), snap.TypeApp, ``)
   740  			err := ic.Check()
   741  			comm := Commentf("%s", iface.Name())
   742  			if restricted[iface.Name()] {
   743  				c.Check(err, NotNil, comm)
   744  			} else {
   745  				c.Check(err, IsNil, comm)
   746  			}
   747  		}
   748  	}
   749  }
   750  
   751  func (s *baseDeclSuite) TestConnection(c *C) {
   752  	all := builtin.Interfaces()
   753  
   754  	// connecting with these interfaces needs to be allowed on
   755  	// case-by-case basis
   756  	noconnect := map[string]bool{
   757  		"content":          true,
   758  		"cups":             true,
   759  		"docker":           true,
   760  		"fwupd":            true,
   761  		"location-control": true,
   762  		"location-observe": true,
   763  		"lxd":              true,
   764  		"maliit":           true,
   765  		"mir":              true,
   766  		"online-accounts-service":   true,
   767  		"raw-volume":                true,
   768  		"storage-framework-service": true,
   769  		"thumbnailer-service":       true,
   770  		"ubuntu-download-manager":   true,
   771  		"unity8-calendar":           true,
   772  		"unity8-contacts":           true,
   773  	}
   774  
   775  	for _, iface := range all {
   776  		expected := !noconnect[iface.Name()]
   777  		comm := Commentf(iface.Name())
   778  
   779  		// check base declaration
   780  		cand := s.connectCand(c, iface.Name(), "", "")
   781  		err := cand.Check()
   782  
   783  		if expected {
   784  			c.Check(err, IsNil, comm)
   785  		} else {
   786  			c.Check(err, NotNil, comm)
   787  		}
   788  	}
   789  }
   790  
   791  func (s *baseDeclSuite) TestConnectionOnClassic(c *C) {
   792  	restore := release.MockOnClassic(false)
   793  	defer restore()
   794  
   795  	all := builtin.Interfaces()
   796  
   797  	// connecting with these interfaces needs to be allowed on
   798  	// case-by-case basis when not on classic
   799  	noconnect := map[string]bool{
   800  		"audio-record":    true,
   801  		"modem-manager":   true,
   802  		"network-manager": true,
   803  		"ofono":           true,
   804  		"pulseaudio":      true,
   805  		"upower-observe":  true,
   806  	}
   807  
   808  	for _, onClassic := range []bool{true, false} {
   809  		release.OnClassic = onClassic
   810  		for _, iface := range all {
   811  			if !noconnect[iface.Name()] {
   812  				continue
   813  			}
   814  			expected := onClassic
   815  			comm := Commentf(iface.Name())
   816  
   817  			// check base declaration
   818  			cand := s.connectCand(c, iface.Name(), "", "")
   819  			err := cand.Check()
   820  
   821  			if expected {
   822  				c.Check(err, IsNil, comm)
   823  			} else {
   824  				c.Check(err, NotNil, comm)
   825  			}
   826  		}
   827  	}
   828  }
   829  
   830  func (s *baseDeclSuite) TestConnectionImplicitOnClassicOrAppSnap(c *C) {
   831  	restore := release.MockOnClassic(false)
   832  	defer restore()
   833  
   834  	all := builtin.Interfaces()
   835  
   836  	// These interfaces represent when the interface might be implicit on
   837  	// classic or when the interface is provided via an app snap. As such,
   838  	// they all share the following:
   839  	//
   840  	// - implicitOnCore: false
   841  	// - implicitOnClassis: true
   842  	// - base declaration uses:
   843  	//     allow-installation:
   844  	//       slot-snap-type:
   845  	//         - app
   846  	//         - core
   847  	//     deny-connection:
   848  	//       on-classic: false
   849  	//     deny-auto-connection: true|false|unspecified
   850  	//
   851  	// connecting with these interfaces needs to be allowed on
   852  	// case-by-case basis when not on classic
   853  	ifaces := map[string]bool{
   854  		"audio-playback":  true,
   855  		"audio-record":    true,
   856  		"cups-control":    true,
   857  		"modem-manager":   true,
   858  		"network-manager": true,
   859  		"ofono":           true,
   860  		"pulseaudio":      true,
   861  		"upower-observe":  true,
   862  	}
   863  
   864  	for _, iface := range all {
   865  		if !ifaces[iface.Name()] {
   866  			continue
   867  		}
   868  		comm := Commentf(iface.Name())
   869  
   870  		// verify the interface is setup as expected wrt
   871  		// implicitOnCore, implicitOnClassic, no plugs and has
   872  		// expected slots (ignoring AutoConnection)
   873  		si := interfaces.StaticInfoOf(iface)
   874  		c.Assert(si.ImplicitOnCore, Equals, false)
   875  		c.Assert(si.ImplicitOnClassic, Equals, true)
   876  
   877  		c.Assert(s.baseDecl.PlugRule(iface.Name()), IsNil)
   878  
   879  		sr := s.baseDecl.SlotRule(iface.Name())
   880  		c.Assert(sr, Not(IsNil))
   881  		c.Assert(sr.AllowInstallation, HasLen, 1)
   882  		c.Check(sr.AllowInstallation[0].SlotSnapTypes, DeepEquals, []string{"app", "core"}, comm)
   883  		c.Assert(sr.DenyConnection, HasLen, 1)
   884  		c.Check(sr.DenyConnection[0].OnClassic, DeepEquals, &asserts.OnClassicConstraint{Classic: false}, comm)
   885  
   886  		for _, onClassic := range []bool{true, false} {
   887  			release.OnClassic = onClassic
   888  
   889  			for _, implicit := range []bool{true, false} {
   890  				// When implicitOnCore is false, there is
   891  				// nothing to test on Core
   892  				if implicit && !onClassic {
   893  					continue
   894  				}
   895  
   896  				snapType := "app"
   897  				if implicit {
   898  					snapType = "os"
   899  				}
   900  				slotYaml := fmt.Sprintf(`name: slot-snap
   901  version: 0
   902  type: %s
   903  slots:
   904    %s:
   905  `, snapType, iface.Name())
   906  
   907  				// XXX: eventually 'onClassic && !implicit' but
   908  				// the current declaration allows connection on
   909  				// Core when 'type: app'. See:
   910  				// https://github.com/snapcore/snapd/pull/8920/files#r471678529
   911  				expected := onClassic
   912  
   913  				// check base declaration
   914  				cand := s.connectCand(c, iface.Name(), slotYaml, "")
   915  				err := cand.Check()
   916  
   917  				if expected {
   918  					c.Check(err, IsNil, comm)
   919  				} else {
   920  					c.Check(err, NotNil, comm)
   921  				}
   922  			}
   923  		}
   924  	}
   925  }
   926  
   927  func (s *baseDeclSuite) TestSanity(c *C) {
   928  	all := builtin.Interfaces()
   929  
   930  	// these interfaces have rules both for the slots and plugs side
   931  	// given how the rules work this can be delicate,
   932  	// listed here to make sure that was a conscious decision
   933  	bothSides := map[string]bool{
   934  		"block-devices":         true,
   935  		"audio-playback":        true,
   936  		"classic-support":       true,
   937  		"core-support":          true,
   938  		"docker-support":        true,
   939  		"greengrass-support":    true,
   940  		"gpio-control":          true,
   941  		"kernel-module-control": true,
   942  		"kubernetes-support":    true,
   943  		"lxd-support":           true,
   944  		"multipass-support":     true,
   945  		"packagekit-control":    true,
   946  		"personal-files":        true,
   947  		"snapd-control":         true,
   948  		"system-files":          true,
   949  		"tee":                   true,
   950  		"udisks2":               true,
   951  		"uinput":                true,
   952  		"unity8":                true,
   953  		"wayland":               true,
   954  	}
   955  
   956  	for _, iface := range all {
   957  		plugRule := s.baseDecl.PlugRule(iface.Name())
   958  		slotRule := s.baseDecl.SlotRule(iface.Name())
   959  		if plugRule == nil && slotRule == nil {
   960  			c.Logf("%s is not considered in the base declaration", iface.Name())
   961  			c.Fail()
   962  		}
   963  		if plugRule != nil && slotRule != nil {
   964  			if !bothSides[iface.Name()] {
   965  				c.Logf("%s have both a base declaration slot rule and plug rule, make sure that's intended and correct", iface.Name())
   966  				c.Fail()
   967  			}
   968  		}
   969  	}
   970  }
   971  
   972  func (s *baseDeclSuite) TestConnectionContent(c *C) {
   973  	// we let connect explicitly as long as content matches (or is absent on both sides)
   974  
   975  	// random (Sanitize* will now also block this)
   976  	cand := s.connectCand(c, "content", "", "")
   977  	err := cand.Check()
   978  	c.Check(err, NotNil)
   979  
   980  	slotDecl1 := s.mockSnapDecl(c, "slot-snap", "slot-snap-id", "pub1", "")
   981  	plugDecl1 := s.mockSnapDecl(c, "plug-snap", "plug-snap-id", "pub1", "")
   982  	plugDecl2 := s.mockSnapDecl(c, "plug-snap", "plug-snap-id", "pub2", "")
   983  
   984  	// same publisher, same content
   985  	cand = s.connectCand(c, "stuff", `name: slot-snap
   986  version: 0
   987  slots:
   988    stuff:
   989      interface: content
   990      content: mk1
   991  `, `
   992  name: plug-snap
   993  version: 0
   994  plugs:
   995    stuff:
   996      interface: content
   997      content: mk1
   998  `)
   999  	cand.SlotSnapDeclaration = slotDecl1
  1000  	cand.PlugSnapDeclaration = plugDecl1
  1001  	err = cand.Check()
  1002  	c.Check(err, IsNil)
  1003  
  1004  	// different publisher, same content
  1005  	cand.SlotSnapDeclaration = slotDecl1
  1006  	cand.PlugSnapDeclaration = plugDecl2
  1007  	err = cand.Check()
  1008  	c.Check(err, IsNil)
  1009  
  1010  	// same publisher, different content
  1011  	cand = s.connectCand(c, "stuff", `
  1012  name: slot-snap
  1013  version: 0
  1014  slots:
  1015    stuff:
  1016      interface: content
  1017      content: mk1
  1018  `, `
  1019  name: plug-snap
  1020  version: 0
  1021  plugs:
  1022    stuff:
  1023      interface: content
  1024      content: mk2
  1025  `)
  1026  	cand.SlotSnapDeclaration = slotDecl1
  1027  	cand.PlugSnapDeclaration = plugDecl1
  1028  	err = cand.Check()
  1029  	c.Check(err, NotNil)
  1030  }
  1031  
  1032  func (s *baseDeclSuite) TestComposeBaseDeclaration(c *C) {
  1033  	decl, err := policy.ComposeBaseDeclaration(nil)
  1034  	c.Assert(err, IsNil)
  1035  	c.Assert(string(decl), testutil.Contains, `
  1036  type: base-declaration
  1037  authority-id: canonical
  1038  series: 16
  1039  revision: 0
  1040  `)
  1041  }
  1042  
  1043  func (s *baseDeclSuite) TestDoesNotPanic(c *C) {
  1044  	// In case there are any issues in the actual interfaces we'd get a panic
  1045  	// on snapd startup. This test prevents this from happing unnoticed.
  1046  	_, err := policy.ComposeBaseDeclaration(builtin.Interfaces())
  1047  	c.Assert(err, IsNil)
  1048  }
  1049  
  1050  func (s *baseDeclSuite) TestBrowserSupportAllowSandbox(c *C) {
  1051  	const plugYaml = `name: plug-snap
  1052  version: 0
  1053  plugs:
  1054    browser-support:
  1055     allow-sandbox: true
  1056  `
  1057  	cand := s.connectCand(c, "browser-support", "", plugYaml)
  1058  	err := cand.Check()
  1059  	c.Check(err, NotNil)
  1060  
  1061  	_, err = cand.CheckAutoConnect()
  1062  	c.Check(err, NotNil)
  1063  }
  1064  
  1065  func (s *baseDeclSuite) TestOpticalDriveWrite(c *C) {
  1066  	type options struct {
  1067  		readonlyYamls []string
  1068  		writableYamls []string
  1069  	}
  1070  
  1071  	opts := &options{
  1072  		readonlyYamls: []string{
  1073  			// Non-specified "write" attribute
  1074  			`name: plug-snap
  1075  version: 0
  1076  plugs:
  1077    optical-drive: null
  1078  `,
  1079  			// Undefined "write" attribute
  1080  			`name: plug-snap
  1081  version: 0
  1082  plugs:
  1083    optical-drive: {}
  1084  `,
  1085  			// False "write" attribute
  1086  			`name: plug-snap
  1087  version: 0
  1088  plugs:
  1089    optical-drive:
  1090      write: false
  1091  `,
  1092  		},
  1093  		writableYamls: []string{
  1094  			// True "write" attribute
  1095  			`name: plug-snap
  1096  version: 0
  1097  plugs:
  1098    optical-drive:
  1099      write: true
  1100  `,
  1101  		},
  1102  	}
  1103  
  1104  	checkOpticalDriveAutoConnect := func(plugYaml string, checker Checker) {
  1105  		cand := s.connectCand(c, "optical-drive", "", plugYaml)
  1106  		err := cand.Check()
  1107  		c.Check(err, checker)
  1108  		_, err = cand.CheckAutoConnect()
  1109  		c.Check(err, checker)
  1110  	}
  1111  
  1112  	for _, plugYaml := range opts.readonlyYamls {
  1113  		checkOpticalDriveAutoConnect(plugYaml, IsNil)
  1114  	}
  1115  	for _, plugYaml := range opts.writableYamls {
  1116  		checkOpticalDriveAutoConnect(plugYaml, NotNil)
  1117  	}
  1118  }
  1119  
  1120  func (s *baseDeclSuite) TestRawVolumeOverride(c *C) {
  1121  	slotYaml := `name: slot-snap
  1122  type: gadget
  1123  version: 0
  1124  slots:
  1125    raw-volume:
  1126      path: /dev/mmcblk0p1
  1127  `
  1128  	slotSnap := snaptest.MockInfo(c, slotYaml, nil)
  1129  	// mock a well-formed slot snap decl with SnapID
  1130  	slotSnapDecl := s.mockSnapDecl(c, "slot-snap", "slotsnapidididididididididididid", "canonical", "")
  1131  
  1132  	plugYaml := `name: plug-snap
  1133  version: 0
  1134  plugs:
  1135    raw-volume:
  1136  `
  1137  	plugSnap := snaptest.MockInfo(c, plugYaml, nil)
  1138  
  1139  	// no plug-side declaration
  1140  	cand := &policy.ConnectCandidate{
  1141  		Plug:                interfaces.NewConnectedPlug(plugSnap.Plugs["raw-volume"], nil, nil),
  1142  		Slot:                interfaces.NewConnectedSlot(slotSnap.Slots["raw-volume"], nil, nil),
  1143  		SlotSnapDeclaration: slotSnapDecl,
  1144  		BaseDeclaration:     s.baseDecl,
  1145  	}
  1146  
  1147  	err := cand.Check()
  1148  	c.Check(err, NotNil)
  1149  	c.Assert(err, ErrorMatches, "connection denied by slot rule of interface \"raw-volume\"")
  1150  	_, err = cand.CheckAutoConnect()
  1151  	c.Check(err, NotNil)
  1152  	c.Assert(err, ErrorMatches, "auto-connection denied by slot rule of interface \"raw-volume\"")
  1153  
  1154  	// specific plug-side declaration for connection only
  1155  	plugsOverride := `
  1156  plugs:
  1157    raw-volume:
  1158      allow-connection:
  1159        slot-snap-id:
  1160          - slotsnapidididididididididididid
  1161      allow-auto-connection: false
  1162  `
  1163  	plugSnapDecl := s.mockSnapDecl(c, "plug-snap", "plugsnapidididididididididididid", "canonical", plugsOverride)
  1164  	cand.PlugSnapDeclaration = plugSnapDecl
  1165  	err = cand.Check()
  1166  	c.Check(err, IsNil)
  1167  	_, err = cand.CheckAutoConnect()
  1168  	c.Check(err, NotNil)
  1169  	c.Assert(err, ErrorMatches, "auto-connection not allowed by plug rule of interface \"raw-volume\" for \"plug-snap\" snap")
  1170  
  1171  	// specific plug-side declaration for connection and auto-connection
  1172  	plugsOverride = `
  1173  plugs:
  1174    raw-volume:
  1175      allow-connection:
  1176        slot-snap-id:
  1177          - slotsnapidididididididididididid
  1178      allow-auto-connection:
  1179        slot-snap-id:
  1180          - slotsnapidididididididididididid
  1181  `
  1182  	plugSnapDecl = s.mockSnapDecl(c, "plug-snap", "plugsnapidididididididididididid", "canonical", plugsOverride)
  1183  	cand.PlugSnapDeclaration = plugSnapDecl
  1184  	err = cand.Check()
  1185  	c.Check(err, IsNil)
  1186  	arity, err := cand.CheckAutoConnect()
  1187  	c.Check(err, IsNil)
  1188  	c.Check(arity.SlotsPerPlugAny(), Equals, false)
  1189  
  1190  	// blanket allow for connection and auto-connection to any slotting snap
  1191  	plugsOverride = `
  1192  plugs:
  1193    raw-volume:
  1194      allow-connection: true
  1195      allow-auto-connection: true
  1196  `
  1197  	plugSnapDecl = s.mockSnapDecl(c, "some-snap", "plugsnapidididididididididididid", "canonical", plugsOverride)
  1198  	cand.PlugSnapDeclaration = plugSnapDecl
  1199  	err = cand.Check()
  1200  	c.Check(err, IsNil)
  1201  	arity, err = cand.CheckAutoConnect()
  1202  	c.Check(err, IsNil)
  1203  	c.Check(arity.SlotsPerPlugAny(), Equals, false)
  1204  }