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