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