github.com/Lephar/snapd@v0.0.0-20210825215435-c7fba9cef4d2/overlord/snapstate/check_snap_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 snapstate_test
    21  
    22  import (
    23  	"errors"
    24  	"fmt"
    25  	"os/user"
    26  
    27  	. "gopkg.in/check.v1"
    28  
    29  	"github.com/snapcore/snapd/arch"
    30  	"github.com/snapcore/snapd/dirs"
    31  	"github.com/snapcore/snapd/osutil"
    32  	"github.com/snapcore/snapd/overlord/snapstate"
    33  	"github.com/snapcore/snapd/overlord/snapstate/snapstatetest"
    34  	"github.com/snapcore/snapd/overlord/state"
    35  	"github.com/snapcore/snapd/release"
    36  	seccomp_compiler "github.com/snapcore/snapd/sandbox/seccomp"
    37  	"github.com/snapcore/snapd/snap"
    38  	"github.com/snapcore/snapd/snap/snaptest"
    39  	"github.com/snapcore/snapd/snapdtool"
    40  	"github.com/snapcore/snapd/testutil"
    41  )
    42  
    43  type checkSnapSuite struct {
    44  	testutil.BaseTest
    45  	st        *state.State
    46  	deviceCtx snapstate.DeviceContext
    47  }
    48  
    49  var _ = Suite(&checkSnapSuite{})
    50  
    51  func (s *checkSnapSuite) SetUpTest(c *C) {
    52  	s.BaseTest.SetUpTest(c)
    53  	dirs.SetRootDir(c.MkDir())
    54  	s.st = state.New(nil)
    55  	s.BaseTest.AddCleanup(snap.MockSanitizePlugsSlots(func(snapInfo *snap.Info) {}))
    56  	s.deviceCtx = &snapstatetest.TrivialDeviceContext{DeviceModel: MakeModel(map[string]interface{}{
    57  		"kernel": "kernel",
    58  		"gadget": "gadget",
    59  	})}
    60  }
    61  
    62  func (s *checkSnapSuite) TearDownTest(c *C) {
    63  	s.BaseTest.TearDownTest(c)
    64  	dirs.SetRootDir("")
    65  }
    66  
    67  func emptyContainer(c *C) snap.Container {
    68  	return snaptest.MockContainer(c, nil)
    69  }
    70  
    71  func (s *checkSnapSuite) TestCheckSnapErrorOnUnsupportedArchitecture(c *C) {
    72  	const yaml = `name: hello
    73  version: 1.10
    74  architectures:
    75      - yadayada
    76      - blahblah
    77  `
    78  	info, err := snap.InfoFromSnapYaml([]byte(yaml))
    79  	c.Assert(err, IsNil)
    80  
    81  	var openSnapFile = func(path string, si *snap.SideInfo) (*snap.Info, snap.Container, error) {
    82  		c.Check(path, Equals, "snap-path")
    83  		c.Check(si, IsNil)
    84  		return info, emptyContainer(c), nil
    85  	}
    86  	restore := snapstate.MockOpenSnapFile(openSnapFile)
    87  	defer restore()
    88  
    89  	err = snapstate.CheckSnap(s.st, "snap-path", "hello", nil, nil, snapstate.Flags{}, nil)
    90  
    91  	errorMsg := fmt.Sprintf(`snap "hello" supported architectures (yadayada, blahblah) are incompatible with this system (%s)`, arch.DpkgArchitecture())
    92  	c.Assert(err.Error(), Equals, errorMsg)
    93  }
    94  
    95  var assumesTests = []struct {
    96  	version string
    97  	assumes string
    98  	classic bool
    99  	error   string
   100  }{{
   101  	assumes: "[common-data-dir]",
   102  }, {
   103  	assumes: "[f1, f2]",
   104  	error:   `snap "foo" assumes unsupported features: f1, f2 \(try to refresh the core or snapd snaps\)`,
   105  }, {
   106  	assumes: "[f1, f2]",
   107  	classic: true,
   108  	error:   `snap "foo" assumes unsupported features: f1, f2 \(try to update snapd and refresh the core snap\)`,
   109  }, {
   110  	assumes: "[snapd2.15]",
   111  	version: "unknown",
   112  }, {
   113  	assumes: "[snapdnono]",
   114  	version: "unknown",
   115  	error:   `.* unsupported features: snapdnono .*`,
   116  }, {
   117  	assumes: "[snapd2.15nono]",
   118  	version: "unknown",
   119  	error:   `.* unsupported features: snapd2.15nono .*`,
   120  }, {
   121  	assumes: "[snapd2.15~pre1]",
   122  	version: "unknown",
   123  	error:   `.* unsupported features: snapd2.15~pre1 .*`,
   124  }, {
   125  	assumes: "[snapd2.15]",
   126  	version: "2.15",
   127  }, {
   128  	assumes: "[snapd2.15]",
   129  	version: "2.15.1",
   130  }, {
   131  	assumes: "[snapd2.15]",
   132  	version: "2.15+git",
   133  }, {
   134  	assumes: "[snapd2.15]",
   135  	version: "2.16",
   136  }, {
   137  	assumes: "[snapd2.15.1]",
   138  	version: "2.16",
   139  }, {
   140  	assumes: "[snapd2.15.1]",
   141  	version: "2.15.1",
   142  }, {
   143  	assumes: "[snapd2.15.1.2]",
   144  	version: "2.15.1.2",
   145  }, {
   146  	assumes: "[snapd2.15.1.2]",
   147  	version: "2.15.1.3",
   148  }, {
   149  	// the horror the horror!
   150  	assumes: "[snapd2.15.1.2.4.5.6.7.8.8]",
   151  	version: "2.15.1.2.4.5.6.7.8.8",
   152  }, {
   153  	assumes: "[snapd2.15.1.2.4.5.6.7.8.8]",
   154  	version: "2.15.1.2.4.5.6.7.8.9",
   155  }, {
   156  	assumes: "[snapd2.15.1.2]",
   157  	version: "2.15.1.3",
   158  }, {
   159  	assumes: "[snapd2.15.2]",
   160  	version: "2.16.1",
   161  }, {
   162  	assumes: "[snapd2.1000]",
   163  	version: "3.1",
   164  }, {
   165  	assumes: "[snapd3]",
   166  	version: "3.1",
   167  }, {
   168  	assumes: "[snapd2]",
   169  	version: "3.1",
   170  }, {
   171  	assumes: "[snapd3]",
   172  	version: "2.48",
   173  	error:   `.* unsupported features: snapd3 .*`,
   174  }, {
   175  	assumes: "[snapd2.15.1.2]",
   176  	version: "2.15.1.1",
   177  	error:   `.* unsupported features: snapd2\.15\.1\.2 .*`,
   178  }, {
   179  	assumes: "[snapd2.15.1.2.4.5.6.7.8.8]",
   180  	version: "2.15.1.2.4.5.6.7.8.1",
   181  	error:   `.* unsupported features: snapd2\.15\.1\.2\.4\.5\.6\.7\.8\.8 .*`,
   182  }, {
   183  	assumes: "[snapd2.16]",
   184  	version: "2.15",
   185  	error:   `.* unsupported features: snapd2\.16 .*`,
   186  }, {
   187  	assumes: "[snapd2.15.1]",
   188  	version: "2.15",
   189  	error:   `.* unsupported features: snapd2\.15\.1 .*`,
   190  }, {
   191  	assumes: "[snapd2.15.1]",
   192  	version: "2.15.0",
   193  	error:   `.* unsupported features: snapd2\.15\.1 .*`,
   194  }, {
   195  	// Note that this is different from how strconv.VersionCompare
   196  	// (dpkg version numbering) would behave - it would error here
   197  	assumes: "[snapd2.15]",
   198  	version: "2.15~pre1",
   199  }, {
   200  	assumes: "[command-chain]",
   201  }, {
   202  	assumes: "[kernel-assets]",
   203  },
   204  }
   205  
   206  func (s *checkSnapSuite) TestCheckSnapAssumes(c *C) {
   207  	restore := snapdtool.MockVersion("2.15")
   208  	defer restore()
   209  
   210  	restore = release.MockOnClassic(false)
   211  	defer restore()
   212  
   213  	for _, test := range assumesTests {
   214  
   215  		snapdtool.Version = test.version
   216  		if snapdtool.Version == "" {
   217  			snapdtool.Version = "2.15"
   218  		}
   219  
   220  		comment := Commentf("snap assumes %q, but snapd version is %q", test.assumes, snapdtool.Version)
   221  		release.OnClassic = test.classic
   222  
   223  		yaml := fmt.Sprintf("name: foo\nversion: 1.0\nassumes: %s\n", test.assumes)
   224  
   225  		info, err := snap.InfoFromSnapYaml([]byte(yaml))
   226  		c.Assert(err, IsNil, comment)
   227  
   228  		var openSnapFile = func(path string, si *snap.SideInfo) (*snap.Info, snap.Container, error) {
   229  			return info, emptyContainer(c), nil
   230  		}
   231  		restore := snapstate.MockOpenSnapFile(openSnapFile)
   232  		defer restore()
   233  		err = snapstate.CheckSnap(s.st, "snap-path", "foo", nil, nil, snapstate.Flags{}, nil)
   234  		if test.error != "" {
   235  			c.Check(err, ErrorMatches, test.error, comment)
   236  		} else {
   237  			c.Assert(err, IsNil, comment)
   238  		}
   239  	}
   240  }
   241  
   242  func (s *checkSnapSuite) TestCheckSnapCheckCallbackOK(c *C) {
   243  	const yaml = `name: foo
   244  version: 1.0`
   245  
   246  	si := &snap.SideInfo{
   247  		SnapID: "snap-id",
   248  	}
   249  
   250  	var openSnapFile = func(path string, si *snap.SideInfo) (*snap.Info, snap.Container, error) {
   251  		info := snaptest.MockInfo(c, yaml, si)
   252  		cont := snaptest.MockContainer(c, [][]string{{"canary", "canary"}})
   253  		return info, cont, nil
   254  	}
   255  	r1 := snapstate.MockOpenSnapFile(openSnapFile)
   256  	defer r1()
   257  
   258  	checkCbCalled := false
   259  	checkCb := func(st *state.State, s, cur *snap.Info, sf snap.Container, flags snapstate.Flags, deviceCtx snapstate.DeviceContext) error {
   260  		c.Assert(sf, NotNil)
   261  		data, err := sf.ReadFile("canary")
   262  		c.Assert(err, IsNil)
   263  		c.Assert(data, DeepEquals, []byte("canary"))
   264  		c.Assert(s.InstanceName(), Equals, "foo")
   265  		c.Assert(s.SnapID, Equals, "snap-id")
   266  		checkCbCalled = true
   267  		return nil
   268  	}
   269  	r2 := snapstate.MockCheckSnapCallbacks([]snapstate.CheckSnapCallback{checkCb})
   270  	defer r2()
   271  
   272  	err := snapstate.CheckSnap(s.st, "snap-path", "foo", si, nil, snapstate.Flags{}, nil)
   273  	c.Check(err, IsNil)
   274  
   275  	c.Check(checkCbCalled, Equals, true)
   276  }
   277  
   278  func (s *checkSnapSuite) TestCheckSnapCheckCallbackFail(c *C) {
   279  	const yaml = `name: foo
   280  version: 1.0`
   281  
   282  	info, err := snap.InfoFromSnapYaml([]byte(yaml))
   283  	c.Assert(err, IsNil)
   284  
   285  	var openSnapFile = func(path string, si *snap.SideInfo) (*snap.Info, snap.Container, error) {
   286  		return info, emptyContainer(c), nil
   287  	}
   288  	restore := snapstate.MockOpenSnapFile(openSnapFile)
   289  	defer restore()
   290  
   291  	fail := errors.New("bad snap")
   292  	checkCb := func(st *state.State, s, cur *snap.Info, _ snap.Container, flags snapstate.Flags, deviceCtx snapstate.DeviceContext) error {
   293  		return fail
   294  	}
   295  	r2 := snapstate.MockCheckSnapCallbacks(nil)
   296  	defer r2()
   297  	snapstate.AddCheckSnapCallback(checkCb)
   298  
   299  	err = snapstate.CheckSnap(s.st, "snap-path", "foo", nil, nil, snapstate.Flags{}, nil)
   300  	c.Check(err, Equals, fail)
   301  }
   302  
   303  func (s *checkSnapSuite) TestCheckSnapGadgetUpdate(c *C) {
   304  	reset := release.MockOnClassic(false)
   305  	defer reset()
   306  
   307  	st := state.New(nil)
   308  	st.Lock()
   309  	defer st.Unlock()
   310  
   311  	si := &snap.SideInfo{RealName: "gadget", Revision: snap.R(2), SnapID: "gadget-id"}
   312  	snaptest.MockSnap(c, `
   313  name: gadget
   314  type: gadget
   315  version: 1
   316  `, si)
   317  	snapstate.Set(st, "gadget", &snapstate.SnapState{
   318  		SnapType: "gadget",
   319  		Active:   true,
   320  		Sequence: []*snap.SideInfo{si},
   321  		Current:  si.Revision,
   322  	})
   323  
   324  	const yaml = `name: gadget
   325  type: gadget
   326  version: 2
   327  `
   328  
   329  	info, err := snap.InfoFromSnapYaml([]byte(yaml))
   330  	info.SnapID = "gadget-id"
   331  	c.Assert(err, IsNil)
   332  
   333  	var openSnapFile = func(path string, si *snap.SideInfo) (*snap.Info, snap.Container, error) {
   334  		return info, emptyContainer(c), nil
   335  	}
   336  	restore := snapstate.MockOpenSnapFile(openSnapFile)
   337  	defer restore()
   338  
   339  	st.Unlock()
   340  	err = snapstate.CheckSnap(st, "snap-path", "gadget", nil, nil, snapstate.Flags{}, s.deviceCtx)
   341  	st.Lock()
   342  	c.Check(err, IsNil)
   343  }
   344  
   345  func (s *checkSnapSuite) TestCheckSnapGadgetUpdateLocal(c *C) {
   346  	reset := release.MockOnClassic(false)
   347  	defer reset()
   348  
   349  	st := state.New(nil)
   350  	st.Lock()
   351  	defer st.Unlock()
   352  
   353  	si := &snap.SideInfo{RealName: "gadget", Revision: snap.R(2)}
   354  	snaptest.MockSnap(c, `
   355  name: gadget
   356  type: gadget
   357  version: 1
   358  `, si)
   359  	snapstate.Set(st, "gadget", &snapstate.SnapState{
   360  		SnapType: "gadget",
   361  		Active:   true,
   362  		Sequence: []*snap.SideInfo{si},
   363  		Current:  si.Revision,
   364  	})
   365  
   366  	const yaml = `name: gadget
   367  type: gadget
   368  version: 2
   369  `
   370  
   371  	info, err := snap.InfoFromSnapYaml([]byte(yaml))
   372  	// no SnapID => local!
   373  	c.Assert(err, IsNil)
   374  
   375  	var openSnapFile = func(path string, si *snap.SideInfo) (*snap.Info, snap.Container, error) {
   376  		return info, emptyContainer(c), nil
   377  	}
   378  	restore := snapstate.MockOpenSnapFile(openSnapFile)
   379  	defer restore()
   380  
   381  	st.Unlock()
   382  	err = snapstate.CheckSnap(st, "snap-path", "gadget", nil, nil, snapstate.Flags{}, s.deviceCtx)
   383  	st.Lock()
   384  	c.Check(err, IsNil)
   385  }
   386  
   387  func (s *checkSnapSuite) TestCheckSnapGadgetUpdateToUnassertedProhibited(c *C) {
   388  	reset := release.MockOnClassic(false)
   389  	defer reset()
   390  
   391  	st := state.New(nil)
   392  	st.Lock()
   393  	defer st.Unlock()
   394  
   395  	si := &snap.SideInfo{RealName: "gadget", Revision: snap.R(2), SnapID: "gadget-id"}
   396  	snaptest.MockSnap(c, `
   397  name: gadget
   398  type: gadget
   399  version: 1
   400  `, si)
   401  	snapstate.Set(st, "gadget", &snapstate.SnapState{
   402  		SnapType: "gadget",
   403  		Active:   true,
   404  		Sequence: []*snap.SideInfo{si},
   405  		Current:  si.Revision,
   406  	})
   407  
   408  	const yaml = `name: gadget
   409  type: gadget
   410  version: 2
   411  `
   412  
   413  	info, err := snap.InfoFromSnapYaml([]byte(yaml))
   414  	c.Assert(err, IsNil)
   415  
   416  	var openSnapFile = func(path string, si *snap.SideInfo) (*snap.Info, snap.Container, error) {
   417  		return info, emptyContainer(c), nil
   418  	}
   419  	restore := snapstate.MockOpenSnapFile(openSnapFile)
   420  	defer restore()
   421  
   422  	st.Unlock()
   423  	err = snapstate.CheckSnap(st, "snap-path", "gadget", nil, nil, snapstate.Flags{}, s.deviceCtx)
   424  	st.Lock()
   425  	c.Check(err, ErrorMatches, `cannot replace signed gadget snap with an unasserted one`)
   426  }
   427  
   428  func (s *checkSnapSuite) TestCheckSnapGadgetAdditionProhibited(c *C) {
   429  	reset := release.MockOnClassic(false)
   430  	defer reset()
   431  
   432  	st := state.New(nil)
   433  	st.Lock()
   434  	defer st.Unlock()
   435  
   436  	si := &snap.SideInfo{RealName: "gadget", Revision: snap.R(2)}
   437  	snaptest.MockSnap(c, `
   438  name: gadget
   439  type: gadget
   440  version: 1
   441  `, si)
   442  	snapstate.Set(st, "gadget", &snapstate.SnapState{
   443  		SnapType: "gadget",
   444  		Active:   true,
   445  		Sequence: []*snap.SideInfo{si},
   446  		Current:  si.Revision,
   447  	})
   448  
   449  	const yaml = `name: zgadget
   450  type: gadget
   451  version: 2
   452  `
   453  
   454  	info, err := snap.InfoFromSnapYaml([]byte(yaml))
   455  	c.Assert(err, IsNil)
   456  
   457  	var openSnapFile = func(path string, si *snap.SideInfo) (*snap.Info, snap.Container, error) {
   458  		return info, emptyContainer(c), nil
   459  	}
   460  	restore := snapstate.MockOpenSnapFile(openSnapFile)
   461  	defer restore()
   462  
   463  	st.Unlock()
   464  	err = snapstate.CheckSnap(st, "snap-path", "gadget", nil, nil, snapstate.Flags{}, s.deviceCtx)
   465  	st.Lock()
   466  	c.Check(err, ErrorMatches, "cannot replace gadget snap with a different one")
   467  }
   468  
   469  func (s *checkSnapSuite) TestCheckSnapGadgetAdditionProhibitedBySnapID(c *C) {
   470  	reset := release.MockOnClassic(false)
   471  	defer reset()
   472  
   473  	st := state.New(nil)
   474  	st.Lock()
   475  	defer st.Unlock()
   476  
   477  	si := &snap.SideInfo{RealName: "gadget", Revision: snap.R(2), SnapID: "gadget-id"}
   478  	snaptest.MockSnap(c, `
   479  name: gadget
   480  type: gadget
   481  version: 1
   482  `, si)
   483  	snapstate.Set(st, "gadget", &snapstate.SnapState{
   484  		SnapType: "gadget",
   485  		Active:   true,
   486  		Sequence: []*snap.SideInfo{si},
   487  		Current:  si.Revision,
   488  	})
   489  
   490  	const yaml = `name: zgadget
   491  type: gadget
   492  version: 2
   493  `
   494  
   495  	info, err := snap.InfoFromSnapYaml([]byte(yaml))
   496  	info.SnapID = "zgadget-id"
   497  	c.Assert(err, IsNil)
   498  
   499  	var openSnapFile = func(path string, si *snap.SideInfo) (*snap.Info, snap.Container, error) {
   500  		return info, emptyContainer(c), nil
   501  	}
   502  	restore := snapstate.MockOpenSnapFile(openSnapFile)
   503  	defer restore()
   504  
   505  	st.Unlock()
   506  	err = snapstate.CheckSnap(st, "snap-path", "gadget", nil, nil, snapstate.Flags{}, s.deviceCtx)
   507  	st.Lock()
   508  	c.Check(err, ErrorMatches, "cannot replace gadget snap with a different one")
   509  }
   510  
   511  func (s *checkSnapSuite) TestCheckSnapGadgetNoPrior(c *C) {
   512  	reset := release.MockOnClassic(false)
   513  	defer reset()
   514  
   515  	st := state.New(nil)
   516  	st.Lock()
   517  	defer st.Unlock()
   518  	st.Set("seeded", true)
   519  
   520  	const yaml = `name: gadget
   521  type: gadget
   522  version: 1
   523  `
   524  	info, err := snap.InfoFromSnapYaml([]byte(yaml))
   525  	c.Assert(err, IsNil)
   526  
   527  	var openSnapFile = func(path string, si *snap.SideInfo) (*snap.Info, snap.Container, error) {
   528  		return info, emptyContainer(c), nil
   529  	}
   530  	restore := snapstate.MockOpenSnapFile(openSnapFile)
   531  	defer restore()
   532  
   533  	st.Unlock()
   534  	err = snapstate.CheckSnap(st, "snap-path", "gadget", nil, nil, snapstate.Flags{}, nil)
   535  	st.Lock()
   536  	c.Check(err, IsNil)
   537  }
   538  
   539  func (s *checkSnapSuite) TestCheckSnapErrorOnDevModeDisallowed(c *C) {
   540  	const yaml = `name: hello
   541  version: 1.10
   542  confinement: devmode
   543  `
   544  	info, err := snap.InfoFromSnapYaml([]byte(yaml))
   545  	c.Assert(err, IsNil)
   546  
   547  	var openSnapFile = func(path string, si *snap.SideInfo) (*snap.Info, snap.Container, error) {
   548  		c.Check(path, Equals, "snap-path")
   549  		c.Check(si, IsNil)
   550  		return info, emptyContainer(c), nil
   551  	}
   552  	restore := snapstate.MockOpenSnapFile(openSnapFile)
   553  	defer restore()
   554  
   555  	err = snapstate.CheckSnap(s.st, "snap-path", "hello", nil, nil, snapstate.Flags{}, nil)
   556  
   557  	c.Assert(err, ErrorMatches, ".* requires devmode or confinement override")
   558  }
   559  
   560  func (s *checkSnapSuite) TestCheckSnapErrorOnClassicDisallowed(c *C) {
   561  	const yaml = `name: hello
   562  version: 1.10
   563  confinement: classic
   564  `
   565  	info, err := snap.InfoFromSnapYaml([]byte(yaml))
   566  	c.Assert(err, IsNil)
   567  
   568  	var openSnapFile = func(path string, si *snap.SideInfo) (*snap.Info, snap.Container, error) {
   569  		c.Check(path, Equals, "snap-path")
   570  		c.Check(si, IsNil)
   571  		return info, emptyContainer(c), nil
   572  	}
   573  	restore := snapstate.MockOpenSnapFile(openSnapFile)
   574  	defer restore()
   575  
   576  	restore = release.MockOnClassic(true)
   577  	defer restore()
   578  
   579  	err = snapstate.CheckSnap(s.st, "snap-path", "hello", nil, nil, snapstate.Flags{}, nil)
   580  
   581  	c.Assert(err, ErrorMatches, ".* requires classic confinement")
   582  }
   583  
   584  func (s *checkSnapSuite) TestCheckSnapErrorClassicOnCoreDisallowed(c *C) {
   585  	const yaml = `name: hello
   586  version: 1.10
   587  confinement: classic
   588  `
   589  	info, err := snap.InfoFromSnapYaml([]byte(yaml))
   590  	c.Assert(err, IsNil)
   591  
   592  	var openSnapFile = func(path string, si *snap.SideInfo) (*snap.Info, snap.Container, error) {
   593  		c.Check(path, Equals, "snap-path")
   594  		c.Check(si, IsNil)
   595  		return info, emptyContainer(c), nil
   596  	}
   597  	restore := snapstate.MockOpenSnapFile(openSnapFile)
   598  	defer restore()
   599  
   600  	restore = release.MockOnClassic(false)
   601  	defer restore()
   602  
   603  	err = snapstate.CheckSnap(s.st, "snap-path", "hello", nil, nil, snapstate.Flags{Classic: true}, nil)
   604  
   605  	c.Assert(err, ErrorMatches, ".* requires classic confinement which is only available on classic systems")
   606  }
   607  
   608  func (s *checkSnapSuite) TestCheckSnapErrorClassicModeForStrictOrDevmode(c *C) {
   609  	const yaml = `name: hello
   610  version: 1.10
   611  confinement: strict
   612  `
   613  	info, err := snap.InfoFromSnapYaml([]byte(yaml))
   614  	c.Assert(err, IsNil)
   615  
   616  	var openSnapFile = func(path string, si *snap.SideInfo) (*snap.Info, snap.Container, error) {
   617  		c.Check(path, Equals, "snap-path")
   618  		c.Check(si, IsNil)
   619  		return info, emptyContainer(c), nil
   620  	}
   621  	restore := snapstate.MockOpenSnapFile(openSnapFile)
   622  	defer restore()
   623  
   624  	err = snapstate.CheckSnap(s.st, "snap-path", "hello", nil, nil, snapstate.Flags{Classic: true}, nil)
   625  
   626  	c.Assert(err, ErrorMatches, `snap "hello" is not a classic confined snap`)
   627  }
   628  
   629  func (s *checkSnapSuite) TestCheckSnapKernelUpdate(c *C) {
   630  	reset := release.MockOnClassic(false)
   631  	defer reset()
   632  
   633  	st := state.New(nil)
   634  	st.Lock()
   635  	defer st.Unlock()
   636  
   637  	si := &snap.SideInfo{RealName: "kernel", Revision: snap.R(2), SnapID: "kernel-id"}
   638  	snaptest.MockSnap(c, `
   639  name: kernel
   640  type: kernel
   641  version: 1
   642  `, si)
   643  	snapstate.Set(st, "kernel", &snapstate.SnapState{
   644  		SnapType: "kernel",
   645  		Active:   true,
   646  		Sequence: []*snap.SideInfo{si},
   647  		Current:  si.Revision,
   648  	})
   649  
   650  	const yaml = `name: kernel
   651  type: kernel
   652  version: 2
   653  `
   654  
   655  	info, err := snap.InfoFromSnapYaml([]byte(yaml))
   656  	info.SnapID = "kernel-id"
   657  	c.Assert(err, IsNil)
   658  
   659  	var openSnapFile = func(path string, si *snap.SideInfo) (*snap.Info, snap.Container, error) {
   660  		return info, emptyContainer(c), nil
   661  	}
   662  	restore := snapstate.MockOpenSnapFile(openSnapFile)
   663  	defer restore()
   664  
   665  	st.Unlock()
   666  	err = snapstate.CheckSnap(st, "snap-path", "kernel", nil, nil, snapstate.Flags{}, s.deviceCtx)
   667  	st.Lock()
   668  	c.Check(err, IsNil)
   669  }
   670  
   671  func (s *checkSnapSuite) TestCheckSnapKernelAdditionProhibitedBySnapID(c *C) {
   672  	reset := release.MockOnClassic(false)
   673  	defer reset()
   674  
   675  	st := state.New(nil)
   676  	st.Lock()
   677  	defer st.Unlock()
   678  
   679  	si := &snap.SideInfo{RealName: "kernel", Revision: snap.R(2), SnapID: "kernel-id"}
   680  	snaptest.MockSnap(c, `
   681  name: kernel
   682  type: kernel
   683  version: 1
   684  `, si)
   685  	snapstate.Set(st, "kernel", &snapstate.SnapState{
   686  		SnapType: "kernel",
   687  		Active:   true,
   688  		Sequence: []*snap.SideInfo{si},
   689  		Current:  si.Revision,
   690  	})
   691  
   692  	const yaml = `name: zkernel
   693  type: kernel
   694  version: 2
   695  `
   696  
   697  	info, err := snap.InfoFromSnapYaml([]byte(yaml))
   698  	info.SnapID = "zkernel-id"
   699  	c.Assert(err, IsNil)
   700  
   701  	var openSnapFile = func(path string, si *snap.SideInfo) (*snap.Info, snap.Container, error) {
   702  		return info, emptyContainer(c), nil
   703  	}
   704  	restore := snapstate.MockOpenSnapFile(openSnapFile)
   705  	defer restore()
   706  
   707  	st.Unlock()
   708  	err = snapstate.CheckSnap(st, "snap-path", "kernel", nil, nil, snapstate.Flags{}, s.deviceCtx)
   709  	st.Lock()
   710  	c.Check(err, ErrorMatches, "cannot replace kernel snap with a different one")
   711  }
   712  
   713  func (s *checkSnapSuite) TestCheckSnapNoStateInfoInternalError(c *C) {
   714  	reset := release.MockOnClassic(false)
   715  	defer reset()
   716  
   717  	st := state.New(nil)
   718  	st.Lock()
   719  	defer st.Unlock()
   720  
   721  	si := &snap.SideInfo{RealName: "other-kernel", Revision: snap.R(2), SnapID: "kernel-id"}
   722  	snaptest.MockSnap(c, `
   723  name: other-kernel
   724  type: kernel
   725  version: 1
   726  `, si)
   727  	// we have a state information for snap of type kernel, but it's a
   728  	// different snap
   729  	snapstate.Set(st, "other-kernel", &snapstate.SnapState{
   730  		SnapType: "kernel",
   731  		Active:   true,
   732  		Sequence: []*snap.SideInfo{si},
   733  		Current:  si.Revision,
   734  	})
   735  
   736  	const yaml = `name: kernel
   737  type: kernel
   738  version: 2
   739  `
   740  
   741  	info, err := snap.InfoFromSnapYaml([]byte(yaml))
   742  	info.SnapID = "kernel-id"
   743  	c.Assert(err, IsNil)
   744  
   745  	var openSnapFile = func(path string, si *snap.SideInfo) (*snap.Info, snap.Container, error) {
   746  		return info, emptyContainer(c), nil
   747  	}
   748  	restore := snapstate.MockOpenSnapFile(openSnapFile)
   749  	defer restore()
   750  
   751  	st.Unlock()
   752  	err = snapstate.CheckSnap(st, "snap-path", "kernel", nil, nil, snapstate.Flags{}, s.deviceCtx)
   753  	st.Lock()
   754  	c.Check(err, ErrorMatches, "internal error: no state for kernel snap \"kernel\"")
   755  }
   756  
   757  func (s *checkSnapSuite) TestCheckSnapBasesErrorsIfMissing(c *C) {
   758  	st := state.New(nil)
   759  	st.Lock()
   760  	defer st.Unlock()
   761  
   762  	const yaml = `name: requires-base
   763  version: 1
   764  base: some-base
   765  `
   766  
   767  	info, err := snap.InfoFromSnapYaml([]byte(yaml))
   768  	c.Assert(err, IsNil)
   769  
   770  	var openSnapFile = func(path string, si *snap.SideInfo) (*snap.Info, snap.Container, error) {
   771  		return info, emptyContainer(c), nil
   772  	}
   773  	restore := snapstate.MockOpenSnapFile(openSnapFile)
   774  	defer restore()
   775  
   776  	st.Unlock()
   777  	err = snapstate.CheckSnap(st, "snap-path", "requires-base", nil, nil, snapstate.Flags{}, nil)
   778  	st.Lock()
   779  	c.Check(err, ErrorMatches, "cannot find required base \"some-base\"")
   780  }
   781  
   782  func (s *checkSnapSuite) TestCheckSnapBasesNoneHappy(c *C) {
   783  	st := state.New(nil)
   784  	st.Lock()
   785  	defer st.Unlock()
   786  
   787  	const yaml = `name: use-base-none
   788  version: 1
   789  base: none
   790  `
   791  	info, err := snap.InfoFromSnapYaml([]byte(yaml))
   792  	c.Assert(err, IsNil)
   793  
   794  	var openSnapFile = func(path string, si *snap.SideInfo) (*snap.Info, snap.Container, error) {
   795  		return info, emptyContainer(c), nil
   796  	}
   797  	restore := snapstate.MockOpenSnapFile(openSnapFile)
   798  	defer restore()
   799  
   800  	st.Unlock()
   801  	err = snapstate.CheckSnap(st, "snap-path", "use-base-none", nil, nil, snapstate.Flags{}, nil)
   802  	st.Lock()
   803  	c.Check(err, IsNil)
   804  }
   805  
   806  func (s *checkSnapSuite) TestCheckSnapBasesHappy(c *C) {
   807  	st := state.New(nil)
   808  	st.Lock()
   809  	defer st.Unlock()
   810  
   811  	si := &snap.SideInfo{RealName: "some-base", Revision: snap.R(1), SnapID: "some-base-id"}
   812  	snaptest.MockSnap(c, `
   813  name: some-base
   814  type: base
   815  version: 1
   816  `, si)
   817  	snapstate.Set(st, "some-base", &snapstate.SnapState{
   818  		SnapType: "base",
   819  		Active:   true,
   820  		Sequence: []*snap.SideInfo{si},
   821  		Current:  si.Revision,
   822  	})
   823  
   824  	const yaml = `name: requires-base
   825  version: 1
   826  base: some-base
   827  `
   828  
   829  	info, err := snap.InfoFromSnapYaml([]byte(yaml))
   830  	c.Assert(err, IsNil)
   831  
   832  	var openSnapFile = func(path string, si *snap.SideInfo) (*snap.Info, snap.Container, error) {
   833  		return info, emptyContainer(c), nil
   834  	}
   835  	restore := snapstate.MockOpenSnapFile(openSnapFile)
   836  	defer restore()
   837  
   838  	st.Unlock()
   839  	err = snapstate.CheckSnap(st, "snap-path", "requires-base", nil, nil, snapstate.Flags{}, nil)
   840  	st.Lock()
   841  	c.Check(err, IsNil)
   842  }
   843  
   844  func (s *checkSnapSuite) TestCheckSnapInstanceName(c *C) {
   845  	st := state.New(nil)
   846  	st.Lock()
   847  	defer st.Unlock()
   848  
   849  	si := &snap.SideInfo{RealName: "foo", Revision: snap.R(1), SnapID: "some-base-id"}
   850  	info := snaptest.MockSnap(c, `
   851  name: foo
   852  version: 1
   853  `, si)
   854  	snapstate.Set(st, "foo", &snapstate.SnapState{
   855  		Active:   true,
   856  		Sequence: []*snap.SideInfo{si},
   857  		Current:  si.Revision,
   858  	})
   859  
   860  	var openSnapFile = func(path string, si *snap.SideInfo) (*snap.Info, snap.Container, error) {
   861  		return info, emptyContainer(c), nil
   862  	}
   863  	restore := snapstate.MockOpenSnapFile(openSnapFile)
   864  	defer restore()
   865  
   866  	st.Unlock()
   867  	err := snapstate.CheckSnap(st, "snap-path", "foo_instance", nil, nil, snapstate.Flags{}, nil)
   868  	st.Lock()
   869  	c.Check(err, IsNil)
   870  
   871  	st.Unlock()
   872  	err = snapstate.CheckSnap(st, "snap-path", "bar_instance", nil, nil, snapstate.Flags{}, nil)
   873  	st.Lock()
   874  	c.Check(err, ErrorMatches, `cannot install snap "foo" using instance name "bar_instance"`)
   875  
   876  	st.Unlock()
   877  	err = snapstate.CheckSnap(st, "snap-path", "other-name", nil, nil, snapstate.Flags{}, nil)
   878  	st.Lock()
   879  	c.Check(err, ErrorMatches, `cannot install snap "foo" using instance name "other-name"`)
   880  }
   881  
   882  func (s *checkSnapSuite) TestCheckSnapCheckCallInstanceKeySet(c *C) {
   883  	const yaml = `name: foo
   884  version: 1.0`
   885  
   886  	si := &snap.SideInfo{
   887  		SnapID: "snap-id",
   888  	}
   889  
   890  	var openSnapFile = func(path string, si *snap.SideInfo) (*snap.Info, snap.Container, error) {
   891  		info := snaptest.MockInfo(c, yaml, si)
   892  		return info, emptyContainer(c), nil
   893  	}
   894  	r1 := snapstate.MockOpenSnapFile(openSnapFile)
   895  	defer r1()
   896  
   897  	checkCbCalled := false
   898  	checkCb := func(st *state.State, s, cur *snap.Info, sf snap.Container, flags snapstate.Flags, deviceCtx snapstate.DeviceContext) error {
   899  		c.Assert(sf, NotNil)
   900  		c.Assert(s.InstanceName(), Equals, "foo_instance")
   901  		c.Assert(s.SnapName(), Equals, "foo")
   902  		c.Assert(s.SnapID, Equals, "snap-id")
   903  		checkCbCalled = true
   904  		return nil
   905  	}
   906  	r2 := snapstate.MockCheckSnapCallbacks([]snapstate.CheckSnapCallback{checkCb})
   907  	defer r2()
   908  
   909  	err := snapstate.CheckSnap(s.st, "snap-path", "foo_instance", si, nil, snapstate.Flags{}, nil)
   910  	c.Check(err, IsNil)
   911  
   912  	c.Check(checkCbCalled, Equals, true)
   913  }
   914  
   915  func (s *checkSnapSuite) TestCheckSnapCheckEpochLocal(c *C) {
   916  	si := &snap.SideInfo{
   917  		SnapID: "snap-id",
   918  	}
   919  
   920  	var openSnapFile = func(path string, si *snap.SideInfo) (*snap.Info, snap.Container, error) {
   921  		info := snaptest.MockInfo(c, "{name: foo, version: 1.0, epoch: 13}", si)
   922  		return info, emptyContainer(c), nil
   923  	}
   924  	r1 := snapstate.MockOpenSnapFile(openSnapFile)
   925  	defer r1()
   926  
   927  	err := snapstate.CheckSnap(s.st, "snap-path", "foo", si, &snap.Info{}, snapstate.Flags{}, nil)
   928  	c.Check(err, ErrorMatches, `cannot refresh "foo" to local snap with epoch 13, because it can't read the current epoch of 0`)
   929  }
   930  
   931  func (s *checkSnapSuite) TestCheckSnapCheckEpochNonLocal(c *C) {
   932  	si := &snap.SideInfo{
   933  		SnapID:   "snap-id",
   934  		Revision: snap.R(42),
   935  	}
   936  
   937  	var openSnapFile = func(path string, si *snap.SideInfo) (*snap.Info, snap.Container, error) {
   938  		info := snaptest.MockInfo(c, "{name: foo, version: 1.0, epoch: 13}", si)
   939  		return info, emptyContainer(c), nil
   940  	}
   941  	r1 := snapstate.MockOpenSnapFile(openSnapFile)
   942  	defer r1()
   943  
   944  	err := snapstate.CheckSnap(s.st, "snap-path", "foo", si, &snap.Info{}, snapstate.Flags{}, nil)
   945  	c.Check(err, ErrorMatches, `cannot refresh "foo" to new revision 42 with epoch 13, because it can't read the current epoch of 0`)
   946  }
   947  
   948  func (s *checkSnapSuite) TestCheckSnapBasesCoreCanBeUsedAsCore16(c *C) {
   949  	st := state.New(nil)
   950  	st.Lock()
   951  	defer st.Unlock()
   952  
   953  	si := &snap.SideInfo{RealName: "core", Revision: snap.R(1), SnapID: "core-id"}
   954  	snaptest.MockSnap(c, `
   955  name: core
   956  type: os
   957  version: 1
   958  `, si)
   959  	snapstate.Set(st, "core", &snapstate.SnapState{
   960  		SnapType: "os",
   961  		Active:   true,
   962  		Sequence: []*snap.SideInfo{si},
   963  		Current:  si.Revision,
   964  	})
   965  
   966  	const yaml = `name: requires-core16
   967  version: 1
   968  base: core16
   969  `
   970  
   971  	info, err := snap.InfoFromSnapYaml([]byte(yaml))
   972  	c.Assert(err, IsNil)
   973  
   974  	var openSnapFile = func(path string, si *snap.SideInfo) (*snap.Info, snap.Container, error) {
   975  		return info, emptyContainer(c), nil
   976  	}
   977  	restore := snapstate.MockOpenSnapFile(openSnapFile)
   978  	defer restore()
   979  
   980  	st.Unlock()
   981  	err = snapstate.CheckSnap(st, "snap-path", "requires-core16", nil, nil, snapstate.Flags{}, nil)
   982  	st.Lock()
   983  	c.Check(err, IsNil)
   984  }
   985  
   986  func (s *checkSnapSuite) TestCheckSnapdHappy(c *C) {
   987  	st := state.New(nil)
   988  	st.Lock()
   989  	defer st.Unlock()
   990  
   991  	for _, t := range []struct {
   992  		yaml   string
   993  		errStr string
   994  	}{
   995  		{"name: snapd\nversion: 1\ntype: snapd", ""},
   996  		{"name: some-snap\nversion: 1\ntype: snapd", `cannot install snap "some-snap" of type "snapd" with a name other than "snapd"`},
   997  		{"name: snapd_instance\nversion: 1\ntype: snapd", `cannot install snap "snapd_instance" of type "snapd" with a name other than "snapd"`},
   998  	} {
   999  		info, err := snap.InfoFromSnapYaml([]byte(t.yaml))
  1000  		c.Assert(err, IsNil)
  1001  
  1002  		var openSnapFile = func(path string, si *snap.SideInfo) (*snap.Info, snap.Container, error) {
  1003  			return info, emptyContainer(c), nil
  1004  		}
  1005  		restore := snapstate.MockOpenSnapFile(openSnapFile)
  1006  		defer restore()
  1007  
  1008  		st.Unlock()
  1009  		err = snapstate.CheckSnap(st, "snap-path", "snapd", nil, nil, snapstate.Flags{}, nil)
  1010  		st.Lock()
  1011  		if t.errStr == "" {
  1012  			c.Check(err, IsNil)
  1013  		} else {
  1014  			c.Check(err, ErrorMatches, t.errStr)
  1015  		}
  1016  	}
  1017  }
  1018  
  1019  // Note, invalid usernames checked in snap/info_snap_yaml.go
  1020  var systemUsernamesTests = []struct {
  1021  	snapID      string
  1022  	sysIDs      string
  1023  	classic     bool
  1024  	noRangeUser bool
  1025  	noUser      bool
  1026  	scVer       string
  1027  	error       string
  1028  }{{
  1029  	sysIDs: "snap_daemon: shared",
  1030  	scVer:  "dead 2.4.1 deadbeef bpf-actlog",
  1031  }, {
  1032  	sysIDs: "snap_daemon:\n    scope: shared",
  1033  	scVer:  "dead 2.4.1 deadbeef bpf-actlog",
  1034  }, {
  1035  	sysIDs: "snap_microk8s: shared",
  1036  	scVer:  "dead 2.4.1 deadbeef bpf-actlog",
  1037  	snapID: "some-uninteresting-snap-id",
  1038  	error:  `snap "foo" is not allowed to use the system user "snap_microk8s"`,
  1039  }, {
  1040  	snapID: "EaXqgt1lyCaxKaQCU349mlodBkDCXRcg", // microk8s
  1041  	sysIDs: "snap_microk8s: shared",
  1042  	scVer:  "dead 2.4.1 deadbeef bpf-actlog",
  1043  }, {
  1044  	// missing snap ID, therefore installation allowed
  1045  	sysIDs: "snap_microk8s:\n    scope: shared",
  1046  	scVer:  "dead 2.4.1 deadbeef bpf-actlog",
  1047  }, {
  1048  	sysIDs: "snap_daemon:\n    scope: private",
  1049  	scVer:  "dead 2.4.1 deadbeef bpf-actlog",
  1050  	error:  `snap "foo" requires unsupported user scope "private" for this version of snapd`,
  1051  }, {
  1052  	sysIDs: "snap_daemon:\n    scope: external",
  1053  	scVer:  "dead 2.4.1 deadbeef bpf-actlog",
  1054  	error:  `snap "foo" requires unsupported user scope "external" for this version of snapd`,
  1055  }, {
  1056  	sysIDs: "snap_daemon:\n    scope: other",
  1057  	scVer:  "dead 2.4.1 deadbeef bpf-actlog",
  1058  	error:  `snap "foo" requires unsupported user scope "other"`,
  1059  }, {
  1060  	sysIDs:  "snap_daemon: shared",
  1061  	scVer:   "dead 2.4.1 deadbeef bpf-actlog",
  1062  	classic: true,
  1063  }, {
  1064  	sysIDs:  "snap_daemon:\n    scope: shared",
  1065  	scVer:   "dead 2.4.1 deadbeef bpf-actlog",
  1066  	classic: true,
  1067  }, {
  1068  	sysIDs:  "snap_daemon:\n    scope: private",
  1069  	scVer:   "dead 2.4.1 deadbeef bpf-actlog",
  1070  	classic: true,
  1071  	error:   `snap "foo" requires unsupported user scope "private" for this version of snapd`,
  1072  }, {
  1073  	sysIDs:  "snap_daemon:\n    scope: external",
  1074  	scVer:   "dead 2.4.1 deadbeef bpf-actlog",
  1075  	classic: true,
  1076  	error:   `snap "foo" requires unsupported user scope "external" for this version of snapd`,
  1077  }, {
  1078  	sysIDs:  "snap_daemon:\n    scope: other",
  1079  	scVer:   "dead 2.4.1 deadbeef bpf-actlog",
  1080  	classic: true,
  1081  	error:   `snap "foo" requires unsupported user scope "other"`,
  1082  }, {
  1083  	sysIDs: "snap_daemon: shared\n  allowed-not: shared",
  1084  	scVer:  "dead 2.4.1 deadbeef bpf-actlog",
  1085  	error:  `snap "foo" requires unsupported system username "allowed-not"`,
  1086  }, {
  1087  	sysIDs:  "allowed-not: shared\n  snap_daemon: shared",
  1088  	scVer:   "dead 2.4.1 deadbeef bpf-actlog",
  1089  	classic: true,
  1090  	error:   `snap "foo" requires unsupported system username "allowed-not"`,
  1091  }, {
  1092  	sysIDs: "snap_daemon: shared",
  1093  	noUser: true,
  1094  	scVer:  "dead 2.4.1 deadbeef bpf-actlog",
  1095  	error:  `cannot ensure users for snap "foo" required system username "snap_daemon": cannot add user/group "snap_daemon", group exists and user does not`,
  1096  }, {
  1097  	sysIDs:  "snap_daemon: shared",
  1098  	classic: true,
  1099  	noUser:  true,
  1100  	scVer:   "dead 2.4.1 deadbeef bpf-actlog",
  1101  	error:   `cannot ensure users for snap "foo" required system username "snap_daemon": cannot add user/group "snap_daemon", group exists and user does not`,
  1102  }, {
  1103  	sysIDs: "snap_daemon: shared",
  1104  	scVer:  "dead 2.3.3 deadbeef bpf-actlog",
  1105  	error:  `snap "foo" system usernames require a snapd built against libseccomp >= 2.4`,
  1106  }, {
  1107  	sysIDs:  "snap_daemon: shared",
  1108  	classic: true,
  1109  	scVer:   "dead 2.3.3 deadbeef bpf-actlog",
  1110  	error:   `snap "foo" system usernames require a snapd built against libseccomp >= 2.4`,
  1111  }, {
  1112  	sysIDs: "snap_daemon: shared",
  1113  	scVer:  "dead 3.0.0 deadbeef bpf-actlog",
  1114  }, {
  1115  	sysIDs:  "snap_daemon: shared",
  1116  	classic: true,
  1117  	scVer:   "dead 3.0.0 deadbeef bpf-actlog",
  1118  }, {
  1119  	sysIDs: "snap_daemon: shared",
  1120  	scVer:  "dead 2.4.1 deadbeef -",
  1121  	error:  `snap "foo" system usernames require a snapd built against golang-seccomp >= 0.9.1`,
  1122  }, {
  1123  	sysIDs:  "snap_daemon: shared",
  1124  	classic: true,
  1125  	scVer:   "dead 2.4.1 deadbeef -",
  1126  	error:   `snap "foo" system usernames require a snapd built against golang-seccomp >= 0.9.1`,
  1127  }, {
  1128  	sysIDs:      "snap_daemon: shared",
  1129  	noRangeUser: true,
  1130  	scVer:       "dead 2.4.1 deadbeef bpf-actlog",
  1131  	error:       `cannot ensure users for snap "foo" required system username "snap_daemon": cannot add user/group "snapd-range-524288-root", group exists and user does not`,
  1132  }, {
  1133  	sysIDs:      "snap_daemon: shared",
  1134  	classic:     true,
  1135  	noRangeUser: true,
  1136  	scVer:       "dead 2.4.1 deadbeef bpf-actlog",
  1137  	error:       `cannot ensure users for snap "foo" required system username "snap_daemon": cannot add user/group "snapd-range-524288-root", group exists and user does not`,
  1138  }, {
  1139  	sysIDs:  "snap_daemon: shared\n  daemon: shared",
  1140  	classic: true,
  1141  	scVer:   "dead 2.4.1 deadbeef bpf-actlog",
  1142  	error:   `snap "foo" requires unsupported system username "daemon"`,
  1143  },
  1144  }
  1145  
  1146  func (s *checkSnapSuite) TestCheckSnapSystemUsernames(c *C) {
  1147  	for _, test := range systemUsernamesTests {
  1148  		restore := seccomp_compiler.MockCompilerVersionInfo(test.scVer)
  1149  		defer restore()
  1150  
  1151  		restore = release.MockOnClassic(test.classic)
  1152  		defer restore()
  1153  
  1154  		var osutilEnsureUserGroupCalls int
  1155  		if test.noRangeUser {
  1156  			restore = snapstate.MockOsutilEnsureUserGroup(func(name string, id uint32, extraUsers bool) error {
  1157  				return fmt.Errorf(`cannot add user/group "%s", group exists and user does not`, name)
  1158  			})
  1159  		} else if test.noUser {
  1160  			restore = snapstate.MockOsutilEnsureUserGroup(func(name string, id uint32, extraUsers bool) error {
  1161  				if name == "snapd-range-524288-root" {
  1162  					return nil
  1163  				}
  1164  				return fmt.Errorf(`cannot add user/group "%s", group exists and user does not`, name)
  1165  			})
  1166  		} else {
  1167  			restore = snapstate.MockOsutilEnsureUserGroup(func(name string, id uint32, extraUsers bool) error {
  1168  				osutilEnsureUserGroupCalls++
  1169  				return nil
  1170  			})
  1171  		}
  1172  		defer restore()
  1173  
  1174  		yaml := fmt.Sprintf("name: foo\nsystem-usernames:\n  %s\n", test.sysIDs)
  1175  
  1176  		info, err := snap.InfoFromSnapYaml([]byte(yaml))
  1177  		c.Assert(err, IsNil)
  1178  		info.SnapID = test.snapID
  1179  
  1180  		var openSnapFile = func(path string, si *snap.SideInfo) (*snap.Info, snap.Container, error) {
  1181  			return info, emptyContainer(c), nil
  1182  		}
  1183  		restore = snapstate.MockOpenSnapFile(openSnapFile)
  1184  		defer restore()
  1185  		err = snapstate.CheckSnap(s.st, "snap-path", "foo", nil, nil, snapstate.Flags{}, nil)
  1186  		if test.error != "" {
  1187  			c.Check(err, ErrorMatches, test.error)
  1188  			c.Check(osutilEnsureUserGroupCalls, Equals, 0)
  1189  		} else {
  1190  			c.Assert(err, IsNil)
  1191  			// one call for the range user, one for the system user
  1192  			c.Check(osutilEnsureUserGroupCalls, Equals, 2)
  1193  		}
  1194  	}
  1195  }
  1196  
  1197  func (s *checkSnapSuite) TestCheckSnapSystemUsernamesCallsSnapDaemon(c *C) {
  1198  	const yaml = `name: foo
  1199  version: 1.0
  1200  system-usernames:
  1201    snap_daemon: shared`
  1202  
  1203  	s.testCheckSnapSystemUsernamesCallsCommon(c, "snap_daemon", "584788", yaml)
  1204  }
  1205  
  1206  func (s *checkSnapSuite) TestCheckSnapSystemUsernamesCallsSnapMicrok8s(c *C) {
  1207  	const yaml = `name: microk8s
  1208  version: 1.0
  1209  system-usernames:
  1210    snap_microk8s: shared`
  1211  
  1212  	s.testCheckSnapSystemUsernamesCallsCommon(c, "snap_microk8s", "584789", yaml)
  1213  }
  1214  
  1215  func (s *checkSnapSuite) testCheckSnapSystemUsernamesCallsCommon(c *C, expectedUser, expectedID, yaml string) {
  1216  	r := osutil.MockFindGid(func(groupname string) (uint64, error) {
  1217  		if groupname == expectedUser || groupname == "snapd-range-524288-root" {
  1218  			return 0, user.UnknownGroupError(groupname)
  1219  		}
  1220  		return 0, fmt.Errorf("unexpected call to FindGid for %s", groupname)
  1221  	})
  1222  	defer r()
  1223  
  1224  	r = osutil.MockFindUid(func(username string) (uint64, error) {
  1225  		if username == expectedUser || username == "snapd-range-524288-root" {
  1226  			return 0, user.UnknownUserError(username)
  1227  		}
  1228  		return 0, fmt.Errorf("unexpected call to FindUid for %s", username)
  1229  	})
  1230  	defer r()
  1231  
  1232  	falsePath := osutil.LookPathDefault("false", "/bin/false")
  1233  	for _, classic := range []bool{false, true} {
  1234  		restore := release.MockOnClassic(classic)
  1235  		defer restore()
  1236  
  1237  		restore = seccomp_compiler.MockCompilerVersionInfo("dead 2.4.1 deadbeef bpf-actlog")
  1238  		defer restore()
  1239  
  1240  		info, err := snap.InfoFromSnapYaml([]byte(yaml))
  1241  		c.Assert(err, IsNil)
  1242  
  1243  		var openSnapFile = func(path string, si *snap.SideInfo) (*snap.Info, snap.Container, error) {
  1244  			return info, emptyContainer(c), nil
  1245  		}
  1246  		restore = snapstate.MockOpenSnapFile(openSnapFile)
  1247  		defer restore()
  1248  
  1249  		mockGroupAdd := testutil.MockCommand(c, "groupadd", "")
  1250  		defer mockGroupAdd.Restore()
  1251  
  1252  		mockUserAdd := testutil.MockCommand(c, "useradd", "")
  1253  		defer mockUserAdd.Restore()
  1254  
  1255  		err = snapstate.CheckSnap(s.st, "snap-path", info.SnapName(), nil, nil, snapstate.Flags{}, nil)
  1256  		c.Assert(err, IsNil)
  1257  		if classic {
  1258  			c.Check(mockGroupAdd.Calls(), DeepEquals, [][]string{
  1259  				{"groupadd", "--system", "--gid", "524288", "snapd-range-524288-root"},
  1260  				{"groupadd", "--system", "--gid", expectedID, expectedUser},
  1261  			})
  1262  			c.Check(mockUserAdd.Calls(), DeepEquals, [][]string{
  1263  				{"useradd", "--system", "--home-dir", "/nonexistent", "--no-create-home", "--shell", falsePath, "--gid", "524288", "--no-user-group", "--uid", "524288", "snapd-range-524288-root"},
  1264  				{"useradd", "--system", "--home-dir", "/nonexistent", "--no-create-home", "--shell", falsePath, "--gid", expectedID, "--no-user-group", "--uid", expectedID, expectedUser},
  1265  			})
  1266  		} else {
  1267  			c.Check(mockGroupAdd.Calls(), DeepEquals, [][]string{
  1268  				{"groupadd", "--system", "--gid", "524288", "--extrausers", "snapd-range-524288-root"},
  1269  				{"groupadd", "--system", "--gid", expectedID, "--extrausers", expectedUser},
  1270  			})
  1271  			c.Check(mockUserAdd.Calls(), DeepEquals, [][]string{
  1272  				{"useradd", "--system", "--home-dir", "/nonexistent", "--no-create-home", "--shell", falsePath, "--gid", "524288", "--no-user-group", "--uid", "524288", "--extrausers", "snapd-range-524288-root"},
  1273  				{"useradd", "--system", "--home-dir", "/nonexistent", "--no-create-home", "--shell", falsePath, "--gid", expectedID, "--no-user-group", "--uid", expectedID, "--extrausers", expectedUser},
  1274  			})
  1275  
  1276  		}
  1277  	}
  1278  }
  1279  
  1280  func (s *checkSnapSuite) TestCheckSnapRemodelKernel(c *C) {
  1281  	reset := release.MockOnClassic(false)
  1282  	defer reset()
  1283  
  1284  	st := state.New(nil)
  1285  	st.Lock()
  1286  	defer st.Unlock()
  1287  
  1288  	si := &snap.SideInfo{RealName: "kernel", Revision: snap.R(2), SnapID: "kernel-id"}
  1289  	snaptest.MockSnap(c, `
  1290  name: kernel
  1291  type: kernel
  1292  version: 1
  1293  `, si)
  1294  	snapstate.Set(st, "kernel", &snapstate.SnapState{
  1295  		SnapType: "kernel",
  1296  		Active:   true,
  1297  		Sequence: []*snap.SideInfo{si},
  1298  		Current:  si.Revision,
  1299  	})
  1300  
  1301  	const yaml = `name: new-kernel
  1302  type: kernel
  1303  version: 2
  1304  `
  1305  
  1306  	info, err := snap.InfoFromSnapYaml([]byte(yaml))
  1307  	info.SnapID = "new-kernel-id"
  1308  	c.Assert(err, IsNil)
  1309  
  1310  	var openSnapFile = func(path string, si *snap.SideInfo) (*snap.Info, snap.Container, error) {
  1311  		return info, emptyContainer(c), nil
  1312  	}
  1313  	restore := snapstate.MockOpenSnapFile(openSnapFile)
  1314  	defer restore()
  1315  
  1316  	// happy case, the new-kernel matches the model
  1317  	deviceCtx := &snapstatetest.TrivialDeviceContext{
  1318  		Remodeling: true,
  1319  		DeviceModel: MakeModel(map[string]interface{}{
  1320  			"kernel": "new-kernel",
  1321  			"gadget": "gadget",
  1322  		}),
  1323  	}
  1324  
  1325  	st.Unlock()
  1326  	err = snapstate.CheckSnap(st, "snap-path", "new-kernel", nil, nil, snapstate.Flags{}, deviceCtx)
  1327  	st.Lock()
  1328  	c.Check(err, IsNil)
  1329  }
  1330  
  1331  func (s *checkSnapSuite) TestCheckSnapRemodelGadget(c *C) {
  1332  	reset := release.MockOnClassic(false)
  1333  	defer reset()
  1334  
  1335  	st := state.New(nil)
  1336  	st.Lock()
  1337  	defer st.Unlock()
  1338  
  1339  	si := &snap.SideInfo{RealName: "gadget", Revision: snap.R(2), SnapID: "gadget-id"}
  1340  	snaptest.MockSnap(c, `
  1341  name: gadget
  1342  type: gadget
  1343  version: 1
  1344  `, si)
  1345  	snapstate.Set(st, "gadget", &snapstate.SnapState{
  1346  		SnapType: "gadget",
  1347  		Active:   true,
  1348  		Sequence: []*snap.SideInfo{si},
  1349  		Current:  si.Revision,
  1350  	})
  1351  
  1352  	const yaml = `name: new-gadget
  1353  type: gadget
  1354  version: 2
  1355  `
  1356  
  1357  	info, err := snap.InfoFromSnapYaml([]byte(yaml))
  1358  	info.SnapID = "new-gadget-id"
  1359  	c.Assert(err, IsNil)
  1360  
  1361  	var openSnapFile = func(path string, si *snap.SideInfo) (*snap.Info, snap.Container, error) {
  1362  		return info, emptyContainer(c), nil
  1363  	}
  1364  	restore := snapstate.MockOpenSnapFile(openSnapFile)
  1365  	defer restore()
  1366  
  1367  	// happy case, the new-gadget matches the model but we do not
  1368  	// support this yet
  1369  	deviceCtx := &snapstatetest.TrivialDeviceContext{
  1370  		Remodeling: true,
  1371  		DeviceModel: MakeModel(map[string]interface{}{
  1372  			"kernel": "kernel",
  1373  			"gadget": "new-gadget",
  1374  		}),
  1375  	}
  1376  
  1377  	st.Unlock()
  1378  	err = snapstate.CheckSnap(st, "snap-path", "new-gadget", nil, nil, snapstate.Flags{}, deviceCtx)
  1379  	st.Lock()
  1380  	c.Check(err, IsNil)
  1381  }