github.com/ubuntu-core/snappy@v0.0.0-20210827154228-9e584df982bb/boot/boot_test.go (about)

     1  // -*- Mode: Go; indent-tabs-mode: t -*-
     2  
     3  /*
     4   * Copyright (C) 2014-2021 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 boot_test
    21  
    22  import (
    23  	"errors"
    24  	"fmt"
    25  	"io/ioutil"
    26  	"os"
    27  	"path/filepath"
    28  	"testing"
    29  
    30  	. "gopkg.in/check.v1"
    31  
    32  	"github.com/snapcore/snapd/asserts"
    33  	"github.com/snapcore/snapd/boot"
    34  	"github.com/snapcore/snapd/boot/boottest"
    35  	"github.com/snapcore/snapd/bootloader"
    36  	"github.com/snapcore/snapd/bootloader/bootloadertest"
    37  	"github.com/snapcore/snapd/dirs"
    38  	"github.com/snapcore/snapd/osutil"
    39  	"github.com/snapcore/snapd/secboot"
    40  	"github.com/snapcore/snapd/seed"
    41  	"github.com/snapcore/snapd/snap"
    42  	"github.com/snapcore/snapd/snap/snaptest"
    43  	"github.com/snapcore/snapd/testutil"
    44  	"github.com/snapcore/snapd/timings"
    45  )
    46  
    47  func TestBoot(t *testing.T) { TestingT(t) }
    48  
    49  type baseBootenvSuite struct {
    50  	testutil.BaseTest
    51  
    52  	rootdir     string
    53  	bootdir     string
    54  	cmdlineFile string
    55  }
    56  
    57  func (s *baseBootenvSuite) SetUpTest(c *C) {
    58  	s.BaseTest.SetUpTest(c)
    59  
    60  	s.rootdir = c.MkDir()
    61  	dirs.SetRootDir(s.rootdir)
    62  	s.AddCleanup(func() { dirs.SetRootDir("") })
    63  	restore := snap.MockSanitizePlugsSlots(func(snapInfo *snap.Info) {})
    64  	s.AddCleanup(restore)
    65  
    66  	s.bootdir = filepath.Join(s.rootdir, "boot")
    67  
    68  	s.cmdlineFile = filepath.Join(c.MkDir(), "cmdline")
    69  	restore = osutil.MockProcCmdline(s.cmdlineFile)
    70  	s.AddCleanup(restore)
    71  }
    72  
    73  func (s *baseBootenvSuite) forceBootloader(bloader bootloader.Bootloader) {
    74  	bootloader.Force(bloader)
    75  	s.AddCleanup(func() { bootloader.Force(nil) })
    76  }
    77  
    78  func (s *baseBootenvSuite) stampSealedKeys(c *C, rootdir string) {
    79  	stamp := filepath.Join(dirs.SnapFDEDirUnder(rootdir), "sealed-keys")
    80  	c.Assert(os.MkdirAll(filepath.Dir(stamp), 0755), IsNil)
    81  	err := ioutil.WriteFile(stamp, nil, 0644)
    82  	c.Assert(err, IsNil)
    83  }
    84  
    85  func (s *baseBootenvSuite) mockCmdline(c *C, cmdline string) {
    86  	c.Assert(ioutil.WriteFile(s.cmdlineFile, []byte(cmdline), 0644), IsNil)
    87  }
    88  
    89  // mockAssetsCache mocks the listed assets in the boot assets cache by creating
    90  // an empty file for each.
    91  func mockAssetsCache(c *C, rootdir, bootloaderName string, cachedAssets []string) {
    92  	p := filepath.Join(dirs.SnapBootAssetsDirUnder(rootdir), bootloaderName)
    93  	err := os.MkdirAll(p, 0755)
    94  	c.Assert(err, IsNil)
    95  	for _, cachedAsset := range cachedAssets {
    96  		err = ioutil.WriteFile(filepath.Join(p, cachedAsset), nil, 0644)
    97  		c.Assert(err, IsNil)
    98  	}
    99  }
   100  
   101  type bootenvSuite struct {
   102  	baseBootenvSuite
   103  
   104  	bootloader *bootloadertest.MockBootloader
   105  }
   106  
   107  var _ = Suite(&bootenvSuite{})
   108  
   109  func (s *bootenvSuite) SetUpTest(c *C) {
   110  	s.baseBootenvSuite.SetUpTest(c)
   111  
   112  	s.bootloader = bootloadertest.Mock("mock", c.MkDir())
   113  	s.forceBootloader(s.bootloader)
   114  }
   115  
   116  type baseBootenv20Suite struct {
   117  	baseBootenvSuite
   118  
   119  	kern1  snap.PlaceInfo
   120  	kern2  snap.PlaceInfo
   121  	ukern1 snap.PlaceInfo
   122  	ukern2 snap.PlaceInfo
   123  	base1  snap.PlaceInfo
   124  	base2  snap.PlaceInfo
   125  
   126  	normalDefaultState      *bootenv20Setup
   127  	normalTryingKernelState *bootenv20Setup
   128  }
   129  
   130  func (s *baseBootenv20Suite) SetUpTest(c *C) {
   131  	s.baseBootenvSuite.SetUpTest(c)
   132  
   133  	var err error
   134  	s.kern1, err = snap.ParsePlaceInfoFromSnapFileName("pc-kernel_1.snap")
   135  	c.Assert(err, IsNil)
   136  	s.kern2, err = snap.ParsePlaceInfoFromSnapFileName("pc-kernel_2.snap")
   137  	c.Assert(err, IsNil)
   138  
   139  	s.ukern1, err = snap.ParsePlaceInfoFromSnapFileName("pc-kernel_x1.snap")
   140  	c.Assert(err, IsNil)
   141  	s.ukern2, err = snap.ParsePlaceInfoFromSnapFileName("pc-kernel_x2.snap")
   142  	c.Assert(err, IsNil)
   143  
   144  	s.base1, err = snap.ParsePlaceInfoFromSnapFileName("core20_1.snap")
   145  	c.Assert(err, IsNil)
   146  	s.base2, err = snap.ParsePlaceInfoFromSnapFileName("core20_2.snap")
   147  	c.Assert(err, IsNil)
   148  
   149  	// default boot state for robustness tests, etc.
   150  	s.normalDefaultState = &bootenv20Setup{
   151  		modeenv: &boot.Modeenv{
   152  			// base is base1
   153  			Base: s.base1.Filename(),
   154  			// no try base
   155  			TryBase: "",
   156  			// base status is default
   157  			BaseStatus: boot.DefaultStatus,
   158  			// current kernels is just kern1
   159  			CurrentKernels: []string{s.kern1.Filename()},
   160  			// operating mode is run
   161  			Mode: "run",
   162  			// RecoverySystem is unset, as it should be during run mode
   163  			RecoverySystem: "",
   164  		},
   165  		// enabled kernel is kern1
   166  		kern: s.kern1,
   167  		// no try kernel enabled
   168  		tryKern: nil,
   169  		// kernel status is default
   170  		kernStatus: boot.DefaultStatus,
   171  	}
   172  
   173  	// state for after trying a new kernel for robustness tests, etc.
   174  	s.normalTryingKernelState = &bootenv20Setup{
   175  		modeenv: &boot.Modeenv{
   176  			// operating mode is run
   177  			Mode: "run",
   178  			// base is base1
   179  			Base: s.base1.Filename(),
   180  			// no try base
   181  			TryBase: "",
   182  			// base status is default
   183  			BaseStatus: boot.DefaultStatus,
   184  			// current kernels is kern1 + kern2
   185  			CurrentKernels: []string{s.kern1.Filename(), s.kern2.Filename()},
   186  		},
   187  		// enabled kernel is kern1
   188  		kern: s.kern1,
   189  		// try kernel is kern2
   190  		tryKern: s.kern2,
   191  		// kernel status is trying
   192  		kernStatus: boot.TryingStatus,
   193  	}
   194  
   195  	s.mockCmdline(c, "snapd_recovery_mode=run")
   196  }
   197  
   198  type bootenv20Suite struct {
   199  	baseBootenv20Suite
   200  
   201  	bootloader *bootloadertest.MockExtractedRunKernelImageBootloader
   202  }
   203  
   204  type bootenv20EnvRefKernelSuite struct {
   205  	baseBootenv20Suite
   206  
   207  	bootloader *bootloadertest.MockBootloader
   208  }
   209  
   210  var _ = Suite(&bootenv20Suite{})
   211  var _ = Suite(&bootenv20EnvRefKernelSuite{})
   212  
   213  func (s *bootenv20Suite) SetUpTest(c *C) {
   214  	s.baseBootenv20Suite.SetUpTest(c)
   215  
   216  	s.bootloader = bootloadertest.Mock("mock", c.MkDir()).WithExtractedRunKernelImage()
   217  	s.forceBootloader(s.bootloader)
   218  }
   219  
   220  func (s *bootenv20EnvRefKernelSuite) SetUpTest(c *C) {
   221  	s.baseBootenv20Suite.SetUpTest(c)
   222  
   223  	s.bootloader = bootloadertest.Mock("mock", c.MkDir())
   224  	s.forceBootloader(s.bootloader)
   225  }
   226  
   227  type bootenv20Setup struct {
   228  	modeenv    *boot.Modeenv
   229  	kern       snap.PlaceInfo
   230  	tryKern    snap.PlaceInfo
   231  	kernStatus string
   232  }
   233  
   234  func setupUC20Bootenv(c *C, bl bootloader.Bootloader, opts *bootenv20Setup) (restore func()) {
   235  	var cleanups []func()
   236  
   237  	// write the modeenv
   238  	if opts.modeenv != nil {
   239  		c.Assert(opts.modeenv.WriteTo(""), IsNil)
   240  		// this isn't strictly necessary since the modeenv will be written to
   241  		// the test's private dir anyways, but it's nice to have so we can write
   242  		// multiple modeenvs from a single test and just call the restore
   243  		// function in between the parts of the test that use different modeenvs
   244  		r := func() {
   245  			defaultModeenv := &boot.Modeenv{Mode: "run"}
   246  			c.Assert(defaultModeenv.WriteTo(""), IsNil)
   247  		}
   248  		cleanups = append(cleanups, r)
   249  	}
   250  
   251  	// set the status
   252  	origEnv, err := bl.GetBootVars("kernel_status")
   253  	c.Assert(err, IsNil)
   254  
   255  	err = bl.SetBootVars(map[string]string{"kernel_status": opts.kernStatus})
   256  	c.Assert(err, IsNil)
   257  	cleanups = append(cleanups, func() {
   258  		err := bl.SetBootVars(origEnv)
   259  		c.Assert(err, IsNil)
   260  	})
   261  
   262  	// check what kind of real mock bootloader we have to use different methods
   263  	// to set the kernel snaps are if they're non-nil
   264  	switch vbl := bl.(type) {
   265  	case *bootloadertest.MockExtractedRunKernelImageBootloader:
   266  		// then we can use the advanced methods on it
   267  		if opts.kern != nil {
   268  			r := vbl.SetEnabledKernel(opts.kern)
   269  			cleanups = append(cleanups, r)
   270  		}
   271  
   272  		if opts.tryKern != nil {
   273  			r := vbl.SetEnabledTryKernel(opts.tryKern)
   274  			cleanups = append(cleanups, r)
   275  		}
   276  
   277  		// don't count any calls to SetBootVars made thus far
   278  		vbl.SetBootVarsCalls = 0
   279  
   280  	case *bootloadertest.MockBootloader:
   281  		// then we need to use the bootenv to set the current kernels
   282  		origEnv, err := vbl.GetBootVars("snap_kernel", "snap_try_kernel")
   283  		c.Assert(err, IsNil)
   284  		m := make(map[string]string, 2)
   285  		if opts.kern != nil {
   286  			m["snap_kernel"] = opts.kern.Filename()
   287  		} else {
   288  			m["snap_kernel"] = ""
   289  		}
   290  
   291  		if opts.tryKern != nil {
   292  			m["snap_try_kernel"] = opts.tryKern.Filename()
   293  		} else {
   294  			m["snap_try_kernel"] = ""
   295  		}
   296  
   297  		err = vbl.SetBootVars(m)
   298  		c.Assert(err, IsNil)
   299  
   300  		// don't count any calls to SetBootVars made thus far
   301  		vbl.SetBootVarsCalls = 0
   302  
   303  		cleanups = append(cleanups, func() {
   304  			err := bl.SetBootVars(origEnv)
   305  			c.Assert(err, IsNil)
   306  		})
   307  	default:
   308  		c.Fatalf("unsupported bootloader %T", bl)
   309  	}
   310  
   311  	return func() {
   312  		for _, r := range cleanups {
   313  			r()
   314  		}
   315  	}
   316  }
   317  
   318  func (s *bootenvSuite) TestInUseClassic(c *C) {
   319  	classicDev := boottest.MockDevice("")
   320  
   321  	// make bootloader.Find fail but shouldn't matter
   322  	bootloader.ForceError(errors.New("broken bootloader"))
   323  
   324  	inUse, err := boot.InUse(snap.TypeBase, classicDev)
   325  	c.Assert(err, IsNil)
   326  	c.Check(inUse("core18", snap.R(41)), Equals, false)
   327  }
   328  
   329  func (s *bootenvSuite) TestInUseIrrelevantTypes(c *C) {
   330  	coreDev := boottest.MockDevice("some-snap")
   331  
   332  	// make bootloader.Find fail but shouldn't matter
   333  	bootloader.ForceError(errors.New("broken bootloader"))
   334  
   335  	inUse, err := boot.InUse(snap.TypeGadget, coreDev)
   336  	c.Assert(err, IsNil)
   337  	c.Check(inUse("gadget", snap.R(41)), Equals, false)
   338  }
   339  
   340  func (s *bootenvSuite) TestInUse(c *C) {
   341  	coreDev := boottest.MockDevice("some-snap")
   342  
   343  	for _, t := range []struct {
   344  		bootVarKey   string
   345  		bootVarValue string
   346  
   347  		snapName string
   348  		snapRev  snap.Revision
   349  
   350  		inUse bool
   351  	}{
   352  		// in use
   353  		{"snap_kernel", "kernel_41.snap", "kernel", snap.R(41), true},
   354  		{"snap_try_kernel", "kernel_82.snap", "kernel", snap.R(82), true},
   355  		{"snap_core", "core_21.snap", "core", snap.R(21), true},
   356  		{"snap_try_core", "core_42.snap", "core", snap.R(42), true},
   357  		// not in use
   358  		{"snap_core", "core_111.snap", "core", snap.R(21), false},
   359  		{"snap_try_core", "core_111.snap", "core", snap.R(21), false},
   360  		{"snap_kernel", "kernel_111.snap", "kernel", snap.R(1), false},
   361  		{"snap_try_kernel", "kernel_111.snap", "kernel", snap.R(1), false},
   362  	} {
   363  		typ := snap.TypeBase
   364  		if t.snapName == "kernel" {
   365  			typ = snap.TypeKernel
   366  		}
   367  		s.bootloader.BootVars[t.bootVarKey] = t.bootVarValue
   368  		inUse, err := boot.InUse(typ, coreDev)
   369  		c.Assert(err, IsNil)
   370  		c.Assert(inUse(t.snapName, t.snapRev), Equals, t.inUse, Commentf("unexpected result: %s %s %v", t.snapName, t.snapRev, t.inUse))
   371  	}
   372  }
   373  
   374  func (s *bootenvSuite) TestInUseEphemeral(c *C) {
   375  	coreDev := boottest.MockDevice("some-snap@install")
   376  
   377  	// make bootloader.Find fail but shouldn't matter
   378  	bootloader.ForceError(errors.New("broken bootloader"))
   379  
   380  	inUse, err := boot.InUse(snap.TypeBase, coreDev)
   381  	c.Assert(err, IsNil)
   382  	c.Check(inUse("whatever", snap.R(0)), Equals, true)
   383  }
   384  
   385  func (s *bootenvSuite) TestInUseUnhappy(c *C) {
   386  	coreDev := boottest.MockDevice("some-snap")
   387  
   388  	// make GetVars fail
   389  	s.bootloader.GetErr = errors.New("zap")
   390  	_, err := boot.InUse(snap.TypeKernel, coreDev)
   391  	c.Check(err, ErrorMatches, `cannot get boot variables: zap`)
   392  
   393  	// make bootloader.Find fail
   394  	bootloader.ForceError(errors.New("broken bootloader"))
   395  	_, err = boot.InUse(snap.TypeKernel, coreDev)
   396  	c.Check(err, ErrorMatches, `cannot get boot settings: broken bootloader`)
   397  }
   398  
   399  func (s *bootenvSuite) TestCurrentBootNameAndRevision(c *C) {
   400  	coreDev := boottest.MockDevice("some-snap")
   401  
   402  	s.bootloader.BootVars["snap_core"] = "core_2.snap"
   403  	s.bootloader.BootVars["snap_kernel"] = "canonical-pc-linux_2.snap"
   404  
   405  	current, err := boot.GetCurrentBoot(snap.TypeOS, coreDev)
   406  	c.Check(err, IsNil)
   407  	c.Check(current.SnapName(), Equals, "core")
   408  	c.Check(current.SnapRevision(), Equals, snap.R(2))
   409  
   410  	current, err = boot.GetCurrentBoot(snap.TypeKernel, coreDev)
   411  	c.Check(err, IsNil)
   412  	c.Check(current.SnapName(), Equals, "canonical-pc-linux")
   413  	c.Check(current.SnapRevision(), Equals, snap.R(2))
   414  
   415  	s.bootloader.BootVars["snap_mode"] = boot.TryingStatus
   416  	_, err = boot.GetCurrentBoot(snap.TypeKernel, coreDev)
   417  	c.Check(err, Equals, boot.ErrBootNameAndRevisionNotReady)
   418  }
   419  
   420  func (s *bootenv20Suite) TestCurrentBoot20NameAndRevision(c *C) {
   421  	coreDev := boottest.MockUC20Device("", nil)
   422  	c.Assert(coreDev.HasModeenv(), Equals, true)
   423  
   424  	r := setupUC20Bootenv(
   425  		c,
   426  		s.bootloader,
   427  		s.normalDefaultState,
   428  	)
   429  	defer r()
   430  
   431  	current, err := boot.GetCurrentBoot(snap.TypeBase, coreDev)
   432  	c.Check(err, IsNil)
   433  	c.Check(current.SnapName(), Equals, s.base1.SnapName())
   434  	c.Check(current.SnapRevision(), Equals, snap.R(1))
   435  
   436  	current, err = boot.GetCurrentBoot(snap.TypeKernel, coreDev)
   437  	c.Check(err, IsNil)
   438  	c.Check(current.SnapName(), Equals, s.kern1.SnapName())
   439  	c.Check(current.SnapRevision(), Equals, snap.R(1))
   440  
   441  	s.bootloader.BootVars["kernel_status"] = boot.TryingStatus
   442  	_, err = boot.GetCurrentBoot(snap.TypeKernel, coreDev)
   443  	c.Check(err, Equals, boot.ErrBootNameAndRevisionNotReady)
   444  }
   445  
   446  // only difference between this test and TestCurrentBoot20NameAndRevision is the
   447  // base bootloader which doesn't support ExtractedRunKernelImageBootloader.
   448  func (s *bootenv20EnvRefKernelSuite) TestCurrentBoot20NameAndRevision(c *C) {
   449  	coreDev := boottest.MockUC20Device("", nil)
   450  	c.Assert(coreDev.HasModeenv(), Equals, true)
   451  
   452  	r := setupUC20Bootenv(
   453  		c,
   454  		s.bootloader,
   455  		s.normalDefaultState,
   456  	)
   457  	defer r()
   458  
   459  	current, err := boot.GetCurrentBoot(snap.TypeKernel, coreDev)
   460  	c.Assert(err, IsNil)
   461  	c.Assert(current.SnapName(), Equals, s.kern1.SnapName())
   462  	c.Assert(current.SnapRevision(), Equals, snap.R(1))
   463  }
   464  
   465  func (s *bootenvSuite) TestCurrentBootNameAndRevisionUnhappy(c *C) {
   466  	coreDev := boottest.MockDevice("some-snap")
   467  
   468  	_, err := boot.GetCurrentBoot(snap.TypeKernel, coreDev)
   469  	c.Check(err, ErrorMatches, `cannot get name and revision of kernel \(snap_kernel\): boot variable unset`)
   470  
   471  	_, err = boot.GetCurrentBoot(snap.TypeOS, coreDev)
   472  	c.Check(err, ErrorMatches, `cannot get name and revision of boot base \(snap_core\): boot variable unset`)
   473  
   474  	_, err = boot.GetCurrentBoot(snap.TypeBase, coreDev)
   475  	c.Check(err, ErrorMatches, `cannot get name and revision of boot base \(snap_core\): boot variable unset`)
   476  
   477  	_, err = boot.GetCurrentBoot(snap.TypeApp, coreDev)
   478  	c.Check(err, ErrorMatches, `internal error: no boot state handling for snap type "app"`)
   479  
   480  	// sanity check
   481  	s.bootloader.BootVars["snap_kernel"] = "kernel_41.snap"
   482  	current, err := boot.GetCurrentBoot(snap.TypeKernel, coreDev)
   483  	c.Check(err, IsNil)
   484  	c.Check(current.SnapName(), Equals, "kernel")
   485  	c.Check(current.SnapRevision(), Equals, snap.R(41))
   486  
   487  	// make GetVars fail
   488  	s.bootloader.GetErr = errors.New("zap")
   489  	_, err = boot.GetCurrentBoot(snap.TypeKernel, coreDev)
   490  	c.Check(err, ErrorMatches, "cannot get boot variables: zap")
   491  	s.bootloader.GetErr = nil
   492  
   493  	// make bootloader.Find fail
   494  	bootloader.ForceError(errors.New("broken bootloader"))
   495  	_, err = boot.GetCurrentBoot(snap.TypeKernel, coreDev)
   496  	c.Check(err, ErrorMatches, "cannot get boot settings: broken bootloader")
   497  }
   498  
   499  func (s *bootenvSuite) TestParticipant(c *C) {
   500  	info := &snap.Info{}
   501  	info.RealName = "some-snap"
   502  
   503  	coreDev := boottest.MockDevice("some-snap")
   504  	classicDev := boottest.MockDevice("")
   505  
   506  	bp := boot.Participant(info, snap.TypeApp, coreDev)
   507  	c.Check(bp.IsTrivial(), Equals, true)
   508  
   509  	for _, typ := range []snap.Type{
   510  		snap.TypeKernel,
   511  		snap.TypeOS,
   512  		snap.TypeBase,
   513  	} {
   514  		bp = boot.Participant(info, typ, classicDev)
   515  		c.Check(bp.IsTrivial(), Equals, true)
   516  
   517  		bp = boot.Participant(info, typ, coreDev)
   518  		c.Check(bp.IsTrivial(), Equals, false)
   519  
   520  		c.Check(bp, DeepEquals, boot.NewCoreBootParticipant(info, typ, coreDev))
   521  	}
   522  }
   523  
   524  func (s *bootenvSuite) TestParticipantBaseWithModel(c *C) {
   525  	core := &snap.Info{SideInfo: snap.SideInfo{RealName: "core"}, SnapType: snap.TypeOS}
   526  	core18 := &snap.Info{SideInfo: snap.SideInfo{RealName: "core18"}, SnapType: snap.TypeBase}
   527  
   528  	type tableT struct {
   529  		with  *snap.Info
   530  		model string
   531  		nop   bool
   532  	}
   533  
   534  	table := []tableT{
   535  		{
   536  			with:  core,
   537  			model: "",
   538  			nop:   true,
   539  		}, {
   540  			with:  core,
   541  			model: "core",
   542  			nop:   false,
   543  		}, {
   544  			with:  core,
   545  			model: "core18",
   546  			nop:   true,
   547  		},
   548  		{
   549  			with:  core18,
   550  			model: "",
   551  			nop:   true,
   552  		},
   553  		{
   554  			with:  core18,
   555  			model: "core",
   556  			nop:   true,
   557  		},
   558  		{
   559  			with:  core18,
   560  			model: "core18",
   561  			nop:   false,
   562  		},
   563  		{
   564  			with:  core18,
   565  			model: "core18@install",
   566  			nop:   true,
   567  		},
   568  		{
   569  			with:  core,
   570  			model: "core@install",
   571  			nop:   true,
   572  		},
   573  	}
   574  
   575  	for i, t := range table {
   576  		dev := boottest.MockDevice(t.model)
   577  		bp := boot.Participant(t.with, t.with.Type(), dev)
   578  		c.Check(bp.IsTrivial(), Equals, t.nop, Commentf("%d", i))
   579  		if !t.nop {
   580  			c.Check(bp, DeepEquals, boot.NewCoreBootParticipant(t.with, t.with.Type(), dev))
   581  		}
   582  	}
   583  }
   584  
   585  func (s *bootenvSuite) TestKernelWithModel(c *C) {
   586  	info := &snap.Info{}
   587  	info.RealName = "kernel"
   588  
   589  	type tableT struct {
   590  		model string
   591  		nop   bool
   592  		krn   boot.BootKernel
   593  	}
   594  
   595  	table := []tableT{
   596  		{
   597  			model: "other-kernel",
   598  			nop:   true,
   599  			krn:   boot.Trivial{},
   600  		}, {
   601  			model: "kernel",
   602  			nop:   false,
   603  			krn:   boot.NewCoreKernel(info, boottest.MockDevice("kernel")),
   604  		}, {
   605  			model: "",
   606  			nop:   true,
   607  			krn:   boot.Trivial{},
   608  		}, {
   609  			model: "kernel@install",
   610  			nop:   true,
   611  			krn:   boot.Trivial{},
   612  		},
   613  	}
   614  
   615  	for _, t := range table {
   616  		dev := boottest.MockDevice(t.model)
   617  		krn := boot.Kernel(info, snap.TypeKernel, dev)
   618  		c.Check(krn.IsTrivial(), Equals, t.nop)
   619  		c.Check(krn, DeepEquals, t.krn)
   620  	}
   621  }
   622  
   623  func (s *bootenvSuite) TestMarkBootSuccessfulKernelStatusTryingNoTryKernelSnapCleansUp(c *C) {
   624  	coreDev := boottest.MockDevice("some-snap")
   625  
   626  	// set all the same vars as if we were doing trying, except don't set a try
   627  	// kernel
   628  
   629  	err := s.bootloader.SetBootVars(map[string]string{
   630  		"snap_kernel": "kernel_41.snap",
   631  		"snap_mode":   boot.TryingStatus,
   632  	})
   633  	c.Assert(err, IsNil)
   634  
   635  	// mark successful
   636  	err = boot.MarkBootSuccessful(coreDev)
   637  	c.Assert(err, IsNil)
   638  
   639  	// check that the bootloader variables were cleaned
   640  	expected := map[string]string{
   641  		"snap_mode":       boot.DefaultStatus,
   642  		"snap_kernel":     "kernel_41.snap",
   643  		"snap_try_kernel": "",
   644  	}
   645  	m, err := s.bootloader.GetBootVars("snap_mode", "snap_try_kernel", "snap_kernel")
   646  	c.Assert(err, IsNil)
   647  	c.Assert(m, DeepEquals, expected)
   648  
   649  	// do it again, verify it's still okay
   650  	err = boot.MarkBootSuccessful(coreDev)
   651  	c.Assert(err, IsNil)
   652  	m2, err := s.bootloader.GetBootVars("snap_mode", "snap_try_kernel", "snap_kernel")
   653  	c.Assert(err, IsNil)
   654  	c.Assert(m2, DeepEquals, expected)
   655  }
   656  
   657  func (s *bootenvSuite) TestMarkBootSuccessfulTryKernelKernelStatusDefaultCleansUp(c *C) {
   658  	coreDev := boottest.MockDevice("some-snap")
   659  
   660  	// set an errant snap_try_kernel
   661  	err := s.bootloader.SetBootVars(map[string]string{
   662  		"snap_kernel":     "kernel_41.snap",
   663  		"snap_try_kernel": "kernel_42.snap",
   664  		"snap_mode":       boot.DefaultStatus,
   665  	})
   666  	c.Assert(err, IsNil)
   667  
   668  	// mark successful
   669  	err = boot.MarkBootSuccessful(coreDev)
   670  	c.Assert(err, IsNil)
   671  
   672  	// check that the bootloader variables were cleaned
   673  	expected := map[string]string{
   674  		"snap_mode":       boot.DefaultStatus,
   675  		"snap_kernel":     "kernel_41.snap",
   676  		"snap_try_kernel": "",
   677  	}
   678  	m, err := s.bootloader.GetBootVars("snap_mode", "snap_try_kernel", "snap_kernel")
   679  	c.Assert(err, IsNil)
   680  	c.Assert(m, DeepEquals, expected)
   681  
   682  	// do it again, verify it's still okay
   683  	err = boot.MarkBootSuccessful(coreDev)
   684  	c.Assert(err, IsNil)
   685  	m2, err := s.bootloader.GetBootVars("snap_mode", "snap_try_kernel", "snap_kernel")
   686  	c.Assert(err, IsNil)
   687  	c.Assert(m2, DeepEquals, expected)
   688  }
   689  
   690  func (s *bootenv20Suite) TestCoreKernel20(c *C) {
   691  	coreDev := boottest.MockUC20Device("", nil)
   692  	c.Assert(coreDev.HasModeenv(), Equals, true)
   693  
   694  	r := setupUC20Bootenv(
   695  		c,
   696  		s.bootloader,
   697  		s.normalDefaultState,
   698  	)
   699  	defer r()
   700  
   701  	// get the boot kernel from our kernel snap
   702  	bootKern := boot.Kernel(s.kern1, snap.TypeKernel, coreDev)
   703  	// can't use FitsTypeOf with coreKernel here, cause that causes an import
   704  	// loop as boottest imports boot and coreKernel is unexported
   705  	c.Assert(bootKern.IsTrivial(), Equals, false)
   706  
   707  	// extract the kernel assets from the coreKernel
   708  	// the container here doesn't really matter since it's just being passed
   709  	// to the mock bootloader method anyways
   710  	kernelContainer := snaptest.MockContainer(c, nil)
   711  	err := bootKern.ExtractKernelAssets(kernelContainer)
   712  	c.Assert(err, IsNil)
   713  
   714  	// make sure that the bootloader was told to extract some assets
   715  	c.Assert(s.bootloader.ExtractKernelAssetsCalls, DeepEquals, []snap.PlaceInfo{s.kern1})
   716  
   717  	// now remove the kernel assets and ensure that we get those calls
   718  	err = bootKern.RemoveKernelAssets()
   719  	c.Assert(err, IsNil)
   720  
   721  	// make sure that the bootloader was told to remove assets
   722  	c.Assert(s.bootloader.RemoveKernelAssetsCalls, DeepEquals, []snap.PlaceInfo{s.kern1})
   723  }
   724  
   725  func (s *bootenv20Suite) TestCoreParticipant20SetNextSameKernelSnap(c *C) {
   726  	coreDev := boottest.MockUC20Device("", nil)
   727  	c.Assert(coreDev.HasModeenv(), Equals, true)
   728  
   729  	r := setupUC20Bootenv(
   730  		c,
   731  		s.bootloader,
   732  		s.normalDefaultState,
   733  	)
   734  	defer r()
   735  
   736  	// get the boot kernel participant from our kernel snap
   737  	bootKern := boot.Participant(s.kern1, snap.TypeKernel, coreDev)
   738  
   739  	// make sure it's not a trivial boot participant
   740  	c.Assert(bootKern.IsTrivial(), Equals, false)
   741  
   742  	// make the kernel used on next boot
   743  	rebootRequired, err := bootKern.SetNextBoot()
   744  	c.Assert(err, IsNil)
   745  	c.Assert(rebootRequired, Equals, false)
   746  
   747  	// make sure that the bootloader was asked for the current kernel
   748  	_, nKernelCalls := s.bootloader.GetRunKernelImageFunctionSnapCalls("Kernel")
   749  	c.Assert(nKernelCalls, Equals, 1)
   750  
   751  	// ensure that kernel_status is still empty
   752  	c.Assert(s.bootloader.BootVars["kernel_status"], Equals, boot.DefaultStatus)
   753  
   754  	// there was no attempt to enable a kernel
   755  	_, enableKernelCalls := s.bootloader.GetRunKernelImageFunctionSnapCalls("EnableTryKernel")
   756  	c.Assert(enableKernelCalls, Equals, 0)
   757  
   758  	// the modeenv is still the same as well
   759  	m2, err := boot.ReadModeenv("")
   760  	c.Assert(err, IsNil)
   761  	c.Assert(m2.CurrentKernels, DeepEquals, []string{s.kern1.Filename()})
   762  
   763  	// finally we didn't call SetBootVars on the bootloader because nothing
   764  	// changed
   765  	c.Assert(s.bootloader.SetBootVarsCalls, Equals, 0)
   766  }
   767  
   768  func (s *bootenv20EnvRefKernelSuite) TestCoreParticipant20SetNextSameKernelSnap(c *C) {
   769  	coreDev := boottest.MockUC20Device("", nil)
   770  	c.Assert(coreDev.HasModeenv(), Equals, true)
   771  
   772  	r := setupUC20Bootenv(
   773  		c,
   774  		s.bootloader,
   775  		s.normalDefaultState,
   776  	)
   777  	defer r()
   778  
   779  	// get the boot kernel participant from our kernel snap
   780  	bootKern := boot.Participant(s.kern1, snap.TypeKernel, coreDev)
   781  
   782  	// make sure it's not a trivial boot participant
   783  	c.Assert(bootKern.IsTrivial(), Equals, false)
   784  
   785  	// make the kernel used on next boot
   786  	rebootRequired, err := bootKern.SetNextBoot()
   787  	c.Assert(err, IsNil)
   788  	c.Assert(rebootRequired, Equals, false)
   789  
   790  	// ensure that bootenv is unchanged
   791  	m, err := s.bootloader.GetBootVars("kernel_status", "snap_kernel", "snap_try_kernel")
   792  	c.Assert(err, IsNil)
   793  	c.Assert(m, DeepEquals, map[string]string{
   794  		"kernel_status":   boot.DefaultStatus,
   795  		"snap_kernel":     s.kern1.Filename(),
   796  		"snap_try_kernel": "",
   797  	})
   798  
   799  	// the modeenv is still the same as well
   800  	m2, err := boot.ReadModeenv("")
   801  	c.Assert(err, IsNil)
   802  	c.Assert(m2.CurrentKernels, DeepEquals, []string{s.kern1.Filename()})
   803  
   804  	// finally we didn't call SetBootVars on the bootloader because nothing
   805  	// changed
   806  	c.Assert(s.bootloader.SetBootVarsCalls, Equals, 0)
   807  }
   808  
   809  func (s *bootenv20Suite) TestCoreParticipant20SetNextNewKernelSnap(c *C) {
   810  	coreDev := boottest.MockUC20Device("", nil)
   811  	c.Assert(coreDev.HasModeenv(), Equals, true)
   812  
   813  	r := setupUC20Bootenv(
   814  		c,
   815  		s.bootloader,
   816  		s.normalDefaultState,
   817  	)
   818  	defer r()
   819  
   820  	// get the boot kernel participant from our new kernel snap
   821  	bootKern := boot.Participant(s.kern2, snap.TypeKernel, coreDev)
   822  	// make sure it's not a trivial boot participant
   823  	c.Assert(bootKern.IsTrivial(), Equals, false)
   824  
   825  	// make the kernel used on next boot
   826  	rebootRequired, err := bootKern.SetNextBoot()
   827  	c.Assert(err, IsNil)
   828  	c.Assert(rebootRequired, Equals, true)
   829  
   830  	// make sure that the bootloader was asked for the current kernel
   831  	_, nKernelCalls := s.bootloader.GetRunKernelImageFunctionSnapCalls("Kernel")
   832  	c.Assert(nKernelCalls, Equals, 1)
   833  
   834  	// ensure that kernel_status is now try
   835  	c.Assert(s.bootloader.BootVars["kernel_status"], Equals, boot.TryStatus)
   836  
   837  	// and we were asked to enable kernel2 as the try kernel
   838  	actual, _ := s.bootloader.GetRunKernelImageFunctionSnapCalls("EnableTryKernel")
   839  	c.Assert(actual, DeepEquals, []snap.PlaceInfo{s.kern2})
   840  
   841  	// and that the modeenv now has this kernel listed
   842  	m2, err := boot.ReadModeenv("")
   843  	c.Assert(err, IsNil)
   844  	c.Assert(m2.CurrentKernels, DeepEquals, []string{s.kern1.Filename(), s.kern2.Filename()})
   845  }
   846  
   847  func (s *bootenv20Suite) TestCoreParticipant20SetNextNewKernelSnapWithReseal(c *C) {
   848  	// checked by resealKeyToModeenv
   849  	s.stampSealedKeys(c, dirs.GlobalRootDir)
   850  
   851  	tab := s.bootloaderWithTrustedAssets(c, []string{"asset"})
   852  
   853  	data := []byte("foobar")
   854  	// SHA3-384
   855  	dataHash := "0fa8abfbdaf924ad307b74dd2ed183b9a4a398891a2f6bac8fd2db7041b77f068580f9c6c66f699b496c2da1cbcc7ed8"
   856  
   857  	c.Assert(os.MkdirAll(filepath.Join(boot.InitramfsUbuntuBootDir), 0755), IsNil)
   858  	c.Assert(os.MkdirAll(filepath.Join(boot.InitramfsUbuntuSeedDir), 0755), IsNil)
   859  	c.Assert(ioutil.WriteFile(filepath.Join(boot.InitramfsUbuntuBootDir, "asset"), data, 0644), IsNil)
   860  	c.Assert(ioutil.WriteFile(filepath.Join(boot.InitramfsUbuntuSeedDir, "asset"), data, 0644), IsNil)
   861  
   862  	// mock the files in cache
   863  	mockAssetsCache(c, dirs.GlobalRootDir, "trusted", []string{
   864  		"asset-" + dataHash,
   865  	})
   866  
   867  	assetBf := bootloader.NewBootFile("", filepath.Join(dirs.SnapBootAssetsDir, "trusted", fmt.Sprintf("asset-%s", dataHash)), bootloader.RoleRunMode)
   868  	runKernelBf := bootloader.NewBootFile(filepath.Join(s.kern1.Filename()), "kernel.efi", bootloader.RoleRunMode)
   869  
   870  	tab.BootChainList = []bootloader.BootFile{
   871  		bootloader.NewBootFile("", "asset", bootloader.RoleRunMode),
   872  		// TODO:UC20: fix mocked trusted assets bootloader to actually
   873  		// geenerate kernel boot files
   874  		runKernelBf,
   875  	}
   876  
   877  	coreDev := boottest.MockUC20Device("", nil)
   878  	c.Assert(coreDev.HasModeenv(), Equals, true)
   879  	model := coreDev.Model()
   880  
   881  	m := &boot.Modeenv{
   882  		Mode:           "run",
   883  		Base:           s.base1.Filename(),
   884  		CurrentKernels: []string{s.kern1.Filename()},
   885  		CurrentTrustedBootAssets: boot.BootAssetsMap{
   886  			"asset": {dataHash},
   887  		},
   888  		Model:          model.Model(),
   889  		BrandID:        model.BrandID(),
   890  		Grade:          string(model.Grade()),
   891  		ModelSignKeyID: model.SignKeyID(),
   892  	}
   893  
   894  	r := setupUC20Bootenv(
   895  		c,
   896  		tab.MockBootloader,
   897  		&bootenv20Setup{
   898  			modeenv:    m,
   899  			kern:       s.kern1,
   900  			kernStatus: boot.DefaultStatus,
   901  		},
   902  	)
   903  	defer r()
   904  
   905  	resealCalls := 0
   906  	restore := boot.MockSecbootResealKeys(func(params *secboot.ResealKeysParams) error {
   907  		resealCalls++
   908  
   909  		c.Assert(params.ModelParams, HasLen, 1)
   910  		mp := params.ModelParams[0]
   911  		c.Check(mp.Model.Model(), Equals, model.Model())
   912  		for _, ch := range mp.EFILoadChains {
   913  			printChain(c, ch, "-")
   914  		}
   915  		c.Check(mp.EFILoadChains, DeepEquals, []*secboot.LoadChain{
   916  			secboot.NewLoadChain(assetBf,
   917  				secboot.NewLoadChain(runKernelBf)),
   918  			secboot.NewLoadChain(assetBf,
   919  				// TODO:UC20: once mock trusted assets
   920  				// bootloader can generated boot files for the
   921  				// kernel this will use candidate kernel
   922  				secboot.NewLoadChain(runKernelBf)),
   923  		})
   924  		// actual paths are seen only here
   925  		c.Check(tab.BootChainKernelPath, DeepEquals, []string{
   926  			s.kern1.MountFile(),
   927  			s.kern2.MountFile(),
   928  		})
   929  		return nil
   930  	})
   931  	defer restore()
   932  
   933  	// get the boot kernel participant from our new kernel snap
   934  	bootKern := boot.Participant(s.kern2, snap.TypeKernel, coreDev)
   935  	// make sure it's not a trivial boot participant
   936  	c.Assert(bootKern.IsTrivial(), Equals, false)
   937  
   938  	// make the kernel used on next boot
   939  	rebootRequired, err := bootKern.SetNextBoot()
   940  	c.Assert(err, IsNil)
   941  	c.Assert(rebootRequired, Equals, true)
   942  
   943  	// make sure the env was updated
   944  	bvars, err := tab.GetBootVars("kernel_status", "snap_kernel", "snap_try_kernel")
   945  	c.Assert(err, IsNil)
   946  	c.Assert(bvars, DeepEquals, map[string]string{
   947  		"kernel_status":   boot.TryStatus,
   948  		"snap_kernel":     s.kern1.Filename(),
   949  		"snap_try_kernel": s.kern2.Filename(),
   950  	})
   951  
   952  	// and that the modeenv now has this kernel listed
   953  	m2, err := boot.ReadModeenv("")
   954  	c.Assert(err, IsNil)
   955  	c.Assert(m2.CurrentKernels, DeepEquals, []string{s.kern1.Filename(), s.kern2.Filename()})
   956  
   957  	c.Check(resealCalls, Equals, 1)
   958  }
   959  
   960  func (s *bootenv20Suite) TestCoreParticipant20SetNextNewUnassertedKernelSnapWithReseal(c *C) {
   961  	// checked by resealKeyToModeenv
   962  	s.stampSealedKeys(c, dirs.GlobalRootDir)
   963  
   964  	tab := s.bootloaderWithTrustedAssets(c, []string{"asset"})
   965  
   966  	data := []byte("foobar")
   967  	// SHA3-384
   968  	dataHash := "0fa8abfbdaf924ad307b74dd2ed183b9a4a398891a2f6bac8fd2db7041b77f068580f9c6c66f699b496c2da1cbcc7ed8"
   969  
   970  	c.Assert(os.MkdirAll(filepath.Join(boot.InitramfsUbuntuBootDir), 0755), IsNil)
   971  	c.Assert(os.MkdirAll(filepath.Join(boot.InitramfsUbuntuSeedDir), 0755), IsNil)
   972  	c.Assert(ioutil.WriteFile(filepath.Join(boot.InitramfsUbuntuBootDir, "asset"), data, 0644), IsNil)
   973  	c.Assert(ioutil.WriteFile(filepath.Join(boot.InitramfsUbuntuSeedDir, "asset"), data, 0644), IsNil)
   974  
   975  	// mock the files in cache
   976  	mockAssetsCache(c, dirs.GlobalRootDir, "trusted", []string{
   977  		"asset-" + dataHash,
   978  	})
   979  
   980  	assetBf := bootloader.NewBootFile("", filepath.Join(dirs.SnapBootAssetsDir, "trusted", fmt.Sprintf("asset-%s", dataHash)), bootloader.RoleRunMode)
   981  	runKernelBf := bootloader.NewBootFile(filepath.Join(s.ukern1.Filename()), "kernel.efi", bootloader.RoleRunMode)
   982  
   983  	tab.BootChainList = []bootloader.BootFile{
   984  		bootloader.NewBootFile("", "asset", bootloader.RoleRunMode),
   985  		// TODO:UC20: fix mocked trusted assets bootloader to actually
   986  		// geenerate kernel boot files
   987  		runKernelBf,
   988  	}
   989  
   990  	uc20Model := boottest.MakeMockUC20Model()
   991  	coreDev := boottest.MockUC20Device("", uc20Model)
   992  	c.Assert(coreDev.HasModeenv(), Equals, true)
   993  
   994  	m := &boot.Modeenv{
   995  		Mode:           "run",
   996  		Base:           s.base1.Filename(),
   997  		CurrentKernels: []string{s.ukern1.Filename()},
   998  		CurrentTrustedBootAssets: boot.BootAssetsMap{
   999  			"asset": {dataHash},
  1000  		},
  1001  		Model:          uc20Model.Model(),
  1002  		BrandID:        uc20Model.BrandID(),
  1003  		Grade:          string(uc20Model.Grade()),
  1004  		ModelSignKeyID: uc20Model.SignKeyID(),
  1005  	}
  1006  
  1007  	r := setupUC20Bootenv(
  1008  		c,
  1009  		tab.MockBootloader,
  1010  		&bootenv20Setup{
  1011  			modeenv:    m,
  1012  			kern:       s.ukern1,
  1013  			kernStatus: boot.DefaultStatus,
  1014  		},
  1015  	)
  1016  	defer r()
  1017  
  1018  	resealCalls := 0
  1019  	restore := boot.MockSecbootResealKeys(func(params *secboot.ResealKeysParams) error {
  1020  		resealCalls++
  1021  
  1022  		c.Assert(params.ModelParams, HasLen, 1)
  1023  		mp := params.ModelParams[0]
  1024  		c.Check(mp.Model.Model(), Equals, uc20Model.Model())
  1025  		for _, ch := range mp.EFILoadChains {
  1026  			printChain(c, ch, "-")
  1027  		}
  1028  		c.Check(mp.EFILoadChains, DeepEquals, []*secboot.LoadChain{
  1029  			secboot.NewLoadChain(assetBf,
  1030  				secboot.NewLoadChain(runKernelBf)),
  1031  			secboot.NewLoadChain(assetBf,
  1032  				// TODO:UC20: once mock trusted assets
  1033  				// bootloader can generated boot files for the
  1034  				// kernel this will use candidate kernel
  1035  				secboot.NewLoadChain(runKernelBf)),
  1036  		})
  1037  		// actual paths are seen only here
  1038  		c.Check(tab.BootChainKernelPath, DeepEquals, []string{
  1039  			s.ukern1.MountFile(),
  1040  			s.ukern2.MountFile(),
  1041  		})
  1042  		return nil
  1043  	})
  1044  	defer restore()
  1045  
  1046  	// get the boot kernel participant from our new kernel snap
  1047  	bootKern := boot.Participant(s.ukern2, snap.TypeKernel, coreDev)
  1048  	// make sure it's not a trivial boot participant
  1049  	c.Assert(bootKern.IsTrivial(), Equals, false)
  1050  
  1051  	// make the kernel used on next boot
  1052  	rebootRequired, err := bootKern.SetNextBoot()
  1053  	c.Assert(err, IsNil)
  1054  	c.Assert(rebootRequired, Equals, true)
  1055  
  1056  	bvars, err := tab.GetBootVars("kernel_status", "snap_kernel", "snap_try_kernel")
  1057  	c.Assert(err, IsNil)
  1058  	c.Assert(bvars, DeepEquals, map[string]string{
  1059  		"kernel_status":   boot.TryStatus,
  1060  		"snap_kernel":     s.ukern1.Filename(),
  1061  		"snap_try_kernel": s.ukern2.Filename(),
  1062  	})
  1063  
  1064  	// and that the modeenv now has this kernel listed
  1065  	m2, err := boot.ReadModeenv("")
  1066  	c.Assert(err, IsNil)
  1067  	c.Assert(m2.CurrentKernels, DeepEquals, []string{s.ukern1.Filename(), s.ukern2.Filename()})
  1068  
  1069  	c.Check(resealCalls, Equals, 1)
  1070  }
  1071  
  1072  func (s *bootenv20Suite) TestCoreParticipant20SetNextSameKernelSnapNoReseal(c *C) {
  1073  	// checked by resealKeyToModeenv
  1074  	s.stampSealedKeys(c, dirs.GlobalRootDir)
  1075  
  1076  	tab := s.bootloaderWithTrustedAssets(c, []string{"asset"})
  1077  
  1078  	data := []byte("foobar")
  1079  	// SHA3-384
  1080  	dataHash := "0fa8abfbdaf924ad307b74dd2ed183b9a4a398891a2f6bac8fd2db7041b77f068580f9c6c66f699b496c2da1cbcc7ed8"
  1081  
  1082  	c.Assert(os.MkdirAll(filepath.Join(boot.InitramfsUbuntuBootDir), 0755), IsNil)
  1083  	c.Assert(os.MkdirAll(filepath.Join(boot.InitramfsUbuntuSeedDir), 0755), IsNil)
  1084  	c.Assert(ioutil.WriteFile(filepath.Join(boot.InitramfsUbuntuBootDir, "asset"), data, 0644), IsNil)
  1085  	c.Assert(ioutil.WriteFile(filepath.Join(boot.InitramfsUbuntuSeedDir, "asset"), data, 0644), IsNil)
  1086  
  1087  	// mock the files in cache
  1088  	mockAssetsCache(c, dirs.GlobalRootDir, "trusted", []string{
  1089  		"asset-" + dataHash,
  1090  	})
  1091  
  1092  	runKernelBf := bootloader.NewBootFile(filepath.Join(s.kern1.Filename()), "kernel.efi", bootloader.RoleRunMode)
  1093  
  1094  	tab.BootChainList = []bootloader.BootFile{
  1095  		bootloader.NewBootFile("", "asset", bootloader.RoleRunMode),
  1096  		runKernelBf,
  1097  	}
  1098  
  1099  	uc20Model := boottest.MakeMockUC20Model()
  1100  	coreDev := boottest.MockUC20Device("", uc20Model)
  1101  	c.Assert(coreDev.HasModeenv(), Equals, true)
  1102  
  1103  	m := &boot.Modeenv{
  1104  		Mode:           "run",
  1105  		Base:           s.base1.Filename(),
  1106  		CurrentKernels: []string{s.kern1.Filename()},
  1107  		CurrentTrustedBootAssets: boot.BootAssetsMap{
  1108  			"asset": {dataHash},
  1109  		},
  1110  		CurrentKernelCommandLines: boot.BootCommandLines{"snapd_recovery_mode=run"},
  1111  
  1112  		Model:          uc20Model.Model(),
  1113  		BrandID:        uc20Model.BrandID(),
  1114  		Grade:          string(uc20Model.Grade()),
  1115  		ModelSignKeyID: uc20Model.SignKeyID(),
  1116  	}
  1117  
  1118  	r := setupUC20Bootenv(
  1119  		c,
  1120  		tab.MockBootloader,
  1121  		&bootenv20Setup{
  1122  			modeenv:    m,
  1123  			kern:       s.kern1,
  1124  			kernStatus: boot.DefaultStatus,
  1125  		},
  1126  	)
  1127  	defer r()
  1128  
  1129  	resealCalls := 0
  1130  	restore := boot.MockSecbootResealKeys(func(params *secboot.ResealKeysParams) error {
  1131  		resealCalls++
  1132  		return fmt.Errorf("unexpected call")
  1133  	})
  1134  	defer restore()
  1135  
  1136  	// get the boot kernel participant from our kernel snap
  1137  	bootKern := boot.Participant(s.kern1, snap.TypeKernel, coreDev)
  1138  	// make sure it's not a trivial boot participant
  1139  	c.Assert(bootKern.IsTrivial(), Equals, false)
  1140  
  1141  	// write boot-chains for current state that will stay unchanged
  1142  	bootChains := []boot.BootChain{{
  1143  		BrandID:        "my-brand",
  1144  		Model:          "my-model-uc20",
  1145  		Grade:          "dangerous",
  1146  		ModelSignKeyID: "Jv8_JiHiIzJVcO9M55pPdqSDWUvuhfDIBJUS-3VW7F_idjix7Ffn5qMxB21ZQuij",
  1147  		AssetChain: []boot.BootAsset{
  1148  			{
  1149  				Role: bootloader.RoleRunMode,
  1150  				Name: "asset",
  1151  				Hashes: []string{
  1152  					"0fa8abfbdaf924ad307b74dd2ed183b9a4a398891a2f6bac8fd2db7041b77f068580f9c6c66f699b496c2da1cbcc7ed8",
  1153  				},
  1154  			},
  1155  		},
  1156  		Kernel:         "pc-kernel",
  1157  		KernelRevision: "1",
  1158  		KernelCmdlines: []string{"snapd_recovery_mode=run"},
  1159  	}}
  1160  	err := boot.WriteBootChains(bootChains, filepath.Join(dirs.SnapFDEDir, "boot-chains"), 0)
  1161  	c.Assert(err, IsNil)
  1162  
  1163  	// make the kernel used on next boot
  1164  	rebootRequired, err := bootKern.SetNextBoot()
  1165  	c.Assert(err, IsNil)
  1166  	c.Assert(rebootRequired, Equals, false)
  1167  
  1168  	// make sure the env is as expected
  1169  	bvars, err := tab.GetBootVars("kernel_status", "snap_kernel", "snap_try_kernel")
  1170  	c.Assert(err, IsNil)
  1171  	c.Assert(bvars, DeepEquals, map[string]string{
  1172  		"kernel_status":   boot.DefaultStatus,
  1173  		"snap_kernel":     s.kern1.Filename(),
  1174  		"snap_try_kernel": "",
  1175  	})
  1176  
  1177  	// and that the modeenv now has the one kernel listed
  1178  	m2, err := boot.ReadModeenv("")
  1179  	c.Assert(err, IsNil)
  1180  	c.Assert(m2.CurrentKernels, DeepEquals, []string{s.kern1.Filename()})
  1181  
  1182  	// boot chains were built
  1183  	c.Check(tab.BootChainKernelPath, DeepEquals, []string{
  1184  		s.kern1.MountFile(),
  1185  	})
  1186  	// no actual reseal
  1187  	c.Check(resealCalls, Equals, 0)
  1188  }
  1189  
  1190  func (s *bootenv20Suite) TestCoreParticipant20SetNextSameUnassertedKernelSnapNoReseal(c *C) {
  1191  	// checked by resealKeyToModeenv
  1192  	s.stampSealedKeys(c, dirs.GlobalRootDir)
  1193  
  1194  	tab := s.bootloaderWithTrustedAssets(c, []string{"asset"})
  1195  
  1196  	data := []byte("foobar")
  1197  	// SHA3-384
  1198  	dataHash := "0fa8abfbdaf924ad307b74dd2ed183b9a4a398891a2f6bac8fd2db7041b77f068580f9c6c66f699b496c2da1cbcc7ed8"
  1199  
  1200  	c.Assert(os.MkdirAll(filepath.Join(boot.InitramfsUbuntuBootDir), 0755), IsNil)
  1201  	c.Assert(os.MkdirAll(filepath.Join(boot.InitramfsUbuntuSeedDir), 0755), IsNil)
  1202  	c.Assert(ioutil.WriteFile(filepath.Join(boot.InitramfsUbuntuBootDir, "asset"), data, 0644), IsNil)
  1203  	c.Assert(ioutil.WriteFile(filepath.Join(boot.InitramfsUbuntuSeedDir, "asset"), data, 0644), IsNil)
  1204  
  1205  	// mock the files in cache
  1206  	mockAssetsCache(c, dirs.GlobalRootDir, "trusted", []string{
  1207  		"asset-" + dataHash,
  1208  	})
  1209  
  1210  	runKernelBf := bootloader.NewBootFile(filepath.Join(s.ukern1.Filename()), "kernel.efi", bootloader.RoleRunMode)
  1211  
  1212  	tab.BootChainList = []bootloader.BootFile{
  1213  		bootloader.NewBootFile("", "asset", bootloader.RoleRunMode),
  1214  		runKernelBf,
  1215  	}
  1216  
  1217  	uc20Model := boottest.MakeMockUC20Model()
  1218  	coreDev := boottest.MockUC20Device("", uc20Model)
  1219  	c.Assert(coreDev.HasModeenv(), Equals, true)
  1220  
  1221  	m := &boot.Modeenv{
  1222  		Mode:           "run",
  1223  		Base:           s.base1.Filename(),
  1224  		CurrentKernels: []string{s.ukern1.Filename()},
  1225  		CurrentTrustedBootAssets: boot.BootAssetsMap{
  1226  			"asset": {dataHash},
  1227  		},
  1228  		CurrentKernelCommandLines: boot.BootCommandLines{"snapd_recovery_mode=run"},
  1229  
  1230  		Model:          uc20Model.Model(),
  1231  		BrandID:        uc20Model.BrandID(),
  1232  		Grade:          string(uc20Model.Grade()),
  1233  		ModelSignKeyID: uc20Model.SignKeyID(),
  1234  	}
  1235  
  1236  	r := setupUC20Bootenv(
  1237  		c,
  1238  		tab.MockBootloader,
  1239  		&bootenv20Setup{
  1240  			modeenv:    m,
  1241  			kern:       s.ukern1,
  1242  			kernStatus: boot.DefaultStatus,
  1243  		},
  1244  	)
  1245  	defer r()
  1246  
  1247  	resealCalls := 0
  1248  	restore := boot.MockSecbootResealKeys(func(params *secboot.ResealKeysParams) error {
  1249  		resealCalls++
  1250  		return fmt.Errorf("unexpected call")
  1251  	})
  1252  	defer restore()
  1253  
  1254  	// get the boot kernel participant from our kernel snap
  1255  	bootKern := boot.Participant(s.ukern1, snap.TypeKernel, coreDev)
  1256  	// make sure it's not a trivial boot participant
  1257  	c.Assert(bootKern.IsTrivial(), Equals, false)
  1258  
  1259  	// write boot-chains for current state that will stay unchanged
  1260  	bootChains := []boot.BootChain{{
  1261  		BrandID:        "my-brand",
  1262  		Model:          "my-model-uc20",
  1263  		Grade:          "dangerous",
  1264  		ModelSignKeyID: "Jv8_JiHiIzJVcO9M55pPdqSDWUvuhfDIBJUS-3VW7F_idjix7Ffn5qMxB21ZQuij",
  1265  		AssetChain: []boot.BootAsset{
  1266  			{
  1267  				Role: bootloader.RoleRunMode,
  1268  				Name: "asset",
  1269  				Hashes: []string{
  1270  					"0fa8abfbdaf924ad307b74dd2ed183b9a4a398891a2f6bac8fd2db7041b77f068580f9c6c66f699b496c2da1cbcc7ed8",
  1271  				},
  1272  			},
  1273  		},
  1274  		Kernel:         "pc-kernel",
  1275  		KernelRevision: "",
  1276  		KernelCmdlines: []string{"snapd_recovery_mode=run"},
  1277  	}}
  1278  	err := boot.WriteBootChains(bootChains, filepath.Join(dirs.SnapFDEDir, "boot-chains"), 0)
  1279  	c.Assert(err, IsNil)
  1280  
  1281  	// make the kernel used on next boot
  1282  	rebootRequired, err := bootKern.SetNextBoot()
  1283  	c.Assert(err, IsNil)
  1284  	c.Assert(rebootRequired, Equals, false)
  1285  
  1286  	// make sure the env is as expected
  1287  	bvars, err := tab.GetBootVars("kernel_status", "snap_kernel", "snap_try_kernel")
  1288  	c.Assert(err, IsNil)
  1289  	c.Assert(bvars, DeepEquals, map[string]string{
  1290  		"kernel_status":   boot.DefaultStatus,
  1291  		"snap_kernel":     s.ukern1.Filename(),
  1292  		"snap_try_kernel": "",
  1293  	})
  1294  
  1295  	// and that the modeenv now has the one kernel listed
  1296  	m2, err := boot.ReadModeenv("")
  1297  	c.Assert(err, IsNil)
  1298  	c.Assert(m2.CurrentKernels, DeepEquals, []string{s.ukern1.Filename()})
  1299  
  1300  	// boot chains were built
  1301  	c.Check(tab.BootChainKernelPath, DeepEquals, []string{
  1302  		s.ukern1.MountFile(),
  1303  	})
  1304  	// no actual reseal
  1305  	c.Check(resealCalls, Equals, 0)
  1306  }
  1307  
  1308  func (s *bootenv20EnvRefKernelSuite) TestCoreParticipant20SetNextNewKernelSnap(c *C) {
  1309  	coreDev := boottest.MockUC20Device("", nil)
  1310  	c.Assert(coreDev.HasModeenv(), Equals, true)
  1311  
  1312  	r := setupUC20Bootenv(
  1313  		c,
  1314  		s.bootloader,
  1315  		s.normalDefaultState,
  1316  	)
  1317  	defer r()
  1318  
  1319  	// get the boot kernel participant from our new kernel snap
  1320  	bootKern := boot.Participant(s.kern2, snap.TypeKernel, coreDev)
  1321  	// make sure it's not a trivial boot participant
  1322  	c.Assert(bootKern.IsTrivial(), Equals, false)
  1323  
  1324  	// make the kernel used on next boot
  1325  	rebootRequired, err := bootKern.SetNextBoot()
  1326  	c.Assert(err, IsNil)
  1327  	c.Assert(rebootRequired, Equals, true)
  1328  
  1329  	// make sure the env was updated
  1330  	m := s.bootloader.BootVars
  1331  	c.Assert(m, DeepEquals, map[string]string{
  1332  		"kernel_status":   boot.TryStatus,
  1333  		"snap_kernel":     s.kern1.Filename(),
  1334  		"snap_try_kernel": s.kern2.Filename(),
  1335  	})
  1336  
  1337  	// and that the modeenv now has this kernel listed
  1338  	m2, err := boot.ReadModeenv("")
  1339  	c.Assert(err, IsNil)
  1340  	c.Assert(m2.CurrentKernels, DeepEquals, []string{s.kern1.Filename(), s.kern2.Filename()})
  1341  }
  1342  
  1343  func (s *bootenv20Suite) TestMarkBootSuccessful20KernelStatusTryingNoKernelSnapCleansUp(c *C) {
  1344  	coreDev := boottest.MockUC20Device("", nil)
  1345  	c.Assert(coreDev.HasModeenv(), Equals, true)
  1346  
  1347  	// set all the same vars as if we were doing trying, except don't set a try
  1348  	// kernel
  1349  	r := setupUC20Bootenv(
  1350  		c,
  1351  		s.bootloader,
  1352  		&bootenv20Setup{
  1353  			modeenv: &boot.Modeenv{
  1354  				Mode:           "run",
  1355  				Base:           s.base1.Filename(),
  1356  				CurrentKernels: []string{s.kern1.Filename(), s.kern2.Filename()},
  1357  			},
  1358  			kern: s.kern1,
  1359  			// no try-kernel
  1360  			kernStatus: boot.TryingStatus,
  1361  		},
  1362  	)
  1363  	defer r()
  1364  
  1365  	// mark successful
  1366  	err := boot.MarkBootSuccessful(coreDev)
  1367  	c.Assert(err, IsNil)
  1368  
  1369  	// check that the bootloader variable was cleaned
  1370  	expected := map[string]string{"kernel_status": boot.DefaultStatus}
  1371  	c.Assert(s.bootloader.BootVars, DeepEquals, expected)
  1372  
  1373  	// check that MarkBootSuccessful didn't enable a kernel (since there was no
  1374  	// try kernel)
  1375  	_, nEnableCalls := s.bootloader.GetRunKernelImageFunctionSnapCalls("EnableKernel")
  1376  	c.Assert(nEnableCalls, Equals, 0)
  1377  
  1378  	// we will always end up disabling a try-kernel though as cleanup
  1379  	_, nDisableTryCalls := s.bootloader.GetRunKernelImageFunctionSnapCalls("DisableTryKernel")
  1380  	c.Assert(nDisableTryCalls, Equals, 1)
  1381  
  1382  	// do it again, verify it's still okay
  1383  	err = boot.MarkBootSuccessful(coreDev)
  1384  	c.Assert(err, IsNil)
  1385  	c.Assert(s.bootloader.BootVars, DeepEquals, expected)
  1386  
  1387  	// no new enabled kernels
  1388  	_, nEnableCalls = s.bootloader.GetRunKernelImageFunctionSnapCalls("EnableKernel")
  1389  	c.Assert(nEnableCalls, Equals, 0)
  1390  
  1391  	// again we will try to cleanup any leftover try-kernels
  1392  	_, nDisableTryCalls = s.bootloader.GetRunKernelImageFunctionSnapCalls("DisableTryKernel")
  1393  	c.Assert(nDisableTryCalls, Equals, 2)
  1394  
  1395  	// check that the modeenv re-wrote the CurrentKernels
  1396  	m2, err := boot.ReadModeenv("")
  1397  	c.Assert(err, IsNil)
  1398  	c.Assert(m2.CurrentKernels, DeepEquals, []string{s.kern1.Filename()})
  1399  }
  1400  
  1401  func (s *bootenv20EnvRefKernelSuite) TestMarkBootSuccessful20KernelStatusTryingNoKernelSnapCleansUp(c *C) {
  1402  	coreDev := boottest.MockUC20Device("", nil)
  1403  	c.Assert(coreDev.HasModeenv(), Equals, true)
  1404  
  1405  	// set all the same vars as if we were doing trying, except don't set a try
  1406  	// kernel
  1407  	r := setupUC20Bootenv(
  1408  		c,
  1409  		s.bootloader,
  1410  		&bootenv20Setup{
  1411  			modeenv: &boot.Modeenv{
  1412  				Mode:           "run",
  1413  				Base:           s.base1.Filename(),
  1414  				CurrentKernels: []string{s.kern1.Filename(), s.kern2.Filename()},
  1415  			},
  1416  			kern: s.kern1,
  1417  			// no try-kernel
  1418  			kernStatus: boot.TryingStatus,
  1419  		},
  1420  	)
  1421  	defer r()
  1422  
  1423  	// mark successful
  1424  	err := boot.MarkBootSuccessful(coreDev)
  1425  	c.Assert(err, IsNil)
  1426  
  1427  	// make sure the env was updated
  1428  	expected := map[string]string{
  1429  		"kernel_status":   boot.DefaultStatus,
  1430  		"snap_kernel":     s.kern1.Filename(),
  1431  		"snap_try_kernel": "",
  1432  	}
  1433  	c.Assert(s.bootloader.BootVars, DeepEquals, expected)
  1434  
  1435  	// do it again, verify it's still okay
  1436  	err = boot.MarkBootSuccessful(coreDev)
  1437  	c.Assert(err, IsNil)
  1438  
  1439  	c.Assert(s.bootloader.BootVars, DeepEquals, expected)
  1440  
  1441  	// check that the modeenv re-wrote the CurrentKernels
  1442  	m2, err := boot.ReadModeenv("")
  1443  	c.Assert(err, IsNil)
  1444  	c.Assert(m2.CurrentKernels, DeepEquals, []string{s.kern1.Filename()})
  1445  }
  1446  
  1447  func (s *bootenv20Suite) TestMarkBootSuccessful20BaseStatusTryingNoTryBaseSnapCleansUp(c *C) {
  1448  	m := &boot.Modeenv{
  1449  		Mode: "run",
  1450  		Base: s.base1.Filename(),
  1451  		// no TryBase set
  1452  		BaseStatus: boot.TryingStatus,
  1453  	}
  1454  	r := setupUC20Bootenv(
  1455  		c,
  1456  		s.bootloader,
  1457  		&bootenv20Setup{
  1458  			modeenv: m,
  1459  			// no kernel setup necessary
  1460  		},
  1461  	)
  1462  	defer r()
  1463  
  1464  	coreDev := boottest.MockUC20Device("", nil)
  1465  	c.Assert(coreDev.HasModeenv(), Equals, true)
  1466  
  1467  	// mark successful
  1468  	err := boot.MarkBootSuccessful(coreDev)
  1469  	c.Assert(err, IsNil)
  1470  
  1471  	// check that the modeenv base_status was re-written to default
  1472  	m2, err := boot.ReadModeenv("")
  1473  	c.Assert(err, IsNil)
  1474  	c.Assert(m2.BaseStatus, Equals, boot.DefaultStatus)
  1475  	c.Assert(m2.Base, Equals, m.Base)
  1476  	c.Assert(m2.TryBase, Equals, m.TryBase)
  1477  
  1478  	// do it again, verify it's still okay
  1479  	err = boot.MarkBootSuccessful(coreDev)
  1480  	c.Assert(err, IsNil)
  1481  
  1482  	m3, err := boot.ReadModeenv("")
  1483  	c.Assert(err, IsNil)
  1484  	c.Assert(m3.BaseStatus, Equals, boot.DefaultStatus)
  1485  	c.Assert(m3.Base, Equals, m.Base)
  1486  	c.Assert(m3.TryBase, Equals, m.TryBase)
  1487  }
  1488  
  1489  func (s *bootenv20Suite) TestCoreParticipant20SetNextSameBaseSnap(c *C) {
  1490  	coreDev := boottest.MockUC20Device("", nil)
  1491  	c.Assert(coreDev.HasModeenv(), Equals, true)
  1492  
  1493  	m := &boot.Modeenv{
  1494  		Mode: "run",
  1495  		Base: s.base1.Filename(),
  1496  	}
  1497  	r := setupUC20Bootenv(
  1498  		c,
  1499  		s.bootloader,
  1500  		&bootenv20Setup{
  1501  			modeenv: m,
  1502  			// no kernel setup necessary
  1503  		},
  1504  	)
  1505  	defer r()
  1506  
  1507  	// get the boot base participant from our base snap
  1508  	bootBase := boot.Participant(s.base1, snap.TypeBase, coreDev)
  1509  	// make sure it's not a trivial boot participant
  1510  	c.Assert(bootBase.IsTrivial(), Equals, false)
  1511  
  1512  	// make the base used on next boot
  1513  	rebootRequired, err := bootBase.SetNextBoot()
  1514  	c.Assert(err, IsNil)
  1515  
  1516  	// we don't need to reboot because it's the same base snap
  1517  	c.Assert(rebootRequired, Equals, false)
  1518  
  1519  	// make sure the modeenv wasn't changed
  1520  	m2, err := boot.ReadModeenv("")
  1521  	c.Assert(err, IsNil)
  1522  	c.Assert(m2.Base, Equals, m.Base)
  1523  	c.Assert(m2.BaseStatus, Equals, m.BaseStatus)
  1524  	c.Assert(m2.TryBase, Equals, m.TryBase)
  1525  }
  1526  
  1527  func (s *bootenv20Suite) TestCoreParticipant20SetNextNewBaseSnap(c *C) {
  1528  	coreDev := boottest.MockUC20Device("", nil)
  1529  	c.Assert(coreDev.HasModeenv(), Equals, true)
  1530  
  1531  	// default state
  1532  	m := &boot.Modeenv{
  1533  		Mode: "run",
  1534  		Base: s.base1.Filename(),
  1535  	}
  1536  	r := setupUC20Bootenv(
  1537  		c,
  1538  		s.bootloader,
  1539  		&bootenv20Setup{
  1540  			modeenv: m,
  1541  			// no kernel setup necessary
  1542  		},
  1543  	)
  1544  	defer r()
  1545  
  1546  	// get the boot base participant from our new base snap
  1547  	bootBase := boot.Participant(s.base2, snap.TypeBase, coreDev)
  1548  	// make sure it's not a trivial boot participant
  1549  	c.Assert(bootBase.IsTrivial(), Equals, false)
  1550  
  1551  	// make the base used on next boot
  1552  	rebootRequired, err := bootBase.SetNextBoot()
  1553  	c.Assert(err, IsNil)
  1554  	c.Assert(rebootRequired, Equals, true)
  1555  
  1556  	// make sure the modeenv was updated
  1557  	m2, err := boot.ReadModeenv("")
  1558  	c.Assert(err, IsNil)
  1559  	c.Assert(m2.Base, Equals, m.Base)
  1560  	c.Assert(m2.BaseStatus, Equals, boot.TryStatus)
  1561  	c.Assert(m2.TryBase, Equals, s.base2.Filename())
  1562  }
  1563  
  1564  func (s *bootenv20Suite) TestCoreParticipant20SetNextNewBaseSnapNoReseal(c *C) {
  1565  	// checked by resealKeyToModeenv
  1566  	s.stampSealedKeys(c, dirs.GlobalRootDir)
  1567  
  1568  	// set up all the bits required for an encrypted system
  1569  	tab := s.bootloaderWithTrustedAssets(c, []string{"asset"})
  1570  	data := []byte("foobar")
  1571  	// SHA3-384
  1572  	dataHash := "0fa8abfbdaf924ad307b74dd2ed183b9a4a398891a2f6bac8fd2db7041b77f068580f9c6c66f699b496c2da1cbcc7ed8"
  1573  	c.Assert(os.MkdirAll(filepath.Join(boot.InitramfsUbuntuBootDir), 0755), IsNil)
  1574  	c.Assert(ioutil.WriteFile(filepath.Join(boot.InitramfsUbuntuBootDir, "asset"), data, 0644), IsNil)
  1575  	// mock the files in cache
  1576  	mockAssetsCache(c, dirs.GlobalRootDir, "trusted", []string{
  1577  		"asset-" + dataHash,
  1578  	})
  1579  	runKernelBf := bootloader.NewBootFile(filepath.Join(s.kern1.Filename()), "kernel.efi", bootloader.RoleRunMode)
  1580  	// write boot-chains for current state that will stay unchanged even
  1581  	// though base is changed
  1582  	bootChains := []boot.BootChain{{
  1583  		BrandID:        "my-brand",
  1584  		Model:          "my-model-uc20",
  1585  		Grade:          "dangerous",
  1586  		ModelSignKeyID: "Jv8_JiHiIzJVcO9M55pPdqSDWUvuhfDIBJUS-3VW7F_idjix7Ffn5qMxB21ZQuij",
  1587  		AssetChain: []boot.BootAsset{{
  1588  			Role: bootloader.RoleRunMode, Name: "asset", Hashes: []string{
  1589  				"0fa8abfbdaf924ad307b74dd2ed183b9a4a398891a2f6bac8fd2db7041b77f068580f9c6c66f699b496c2da1cbcc7ed8",
  1590  			},
  1591  		}},
  1592  		Kernel:         "pc-kernel",
  1593  		KernelRevision: "1",
  1594  		KernelCmdlines: []string{"snapd_recovery_mode=run"},
  1595  	}}
  1596  
  1597  	err := boot.WriteBootChains(boot.ToPredictableBootChains(bootChains), filepath.Join(dirs.SnapFDEDir, "boot-chains"), 0)
  1598  	c.Assert(err, IsNil)
  1599  
  1600  	coreDev := boottest.MockUC20Device("", nil)
  1601  	c.Assert(coreDev.HasModeenv(), Equals, true)
  1602  	model := coreDev.Model()
  1603  
  1604  	resealCalls := 0
  1605  	restore := boot.MockSecbootResealKeys(func(params *secboot.ResealKeysParams) error {
  1606  		resealCalls++
  1607  		return nil
  1608  	})
  1609  	defer restore()
  1610  
  1611  	tab.BootChainList = []bootloader.BootFile{
  1612  		bootloader.NewBootFile("", "asset", bootloader.RoleRunMode),
  1613  		runKernelBf,
  1614  	}
  1615  
  1616  	// default state
  1617  	m := &boot.Modeenv{
  1618  		Mode:           "run",
  1619  		Base:           s.base1.Filename(),
  1620  		CurrentKernels: []string{s.kern1.Filename()},
  1621  		CurrentTrustedBootAssets: boot.BootAssetsMap{
  1622  			"asset": {dataHash},
  1623  		},
  1624  
  1625  		Model:          model.Model(),
  1626  		BrandID:        model.BrandID(),
  1627  		Grade:          string(model.Grade()),
  1628  		ModelSignKeyID: model.SignKeyID(),
  1629  	}
  1630  	r := setupUC20Bootenv(
  1631  		c,
  1632  		tab.MockBootloader,
  1633  		&bootenv20Setup{
  1634  			modeenv: m,
  1635  			// no kernel setup necessary
  1636  		},
  1637  	)
  1638  	defer r()
  1639  
  1640  	// get the boot base participant from our new base snap
  1641  	bootBase := boot.Participant(s.base2, snap.TypeBase, coreDev)
  1642  	// make sure it's not a trivial boot participant
  1643  	c.Assert(bootBase.IsTrivial(), Equals, false)
  1644  
  1645  	// make the base used on next boot
  1646  	rebootRequired, err := bootBase.SetNextBoot()
  1647  	c.Assert(err, IsNil)
  1648  	c.Assert(rebootRequired, Equals, true)
  1649  
  1650  	// make sure the modeenv was updated
  1651  	m2, err := boot.ReadModeenv("")
  1652  	c.Assert(err, IsNil)
  1653  	c.Assert(m2.Base, Equals, m.Base)
  1654  	c.Assert(m2.BaseStatus, Equals, boot.TryStatus)
  1655  	c.Assert(m2.TryBase, Equals, s.base2.Filename())
  1656  
  1657  	// no reseal
  1658  	c.Check(resealCalls, Equals, 0)
  1659  }
  1660  
  1661  func (s *bootenvSuite) TestMarkBootSuccessfulAllSnap(c *C) {
  1662  	coreDev := boottest.MockDevice("some-snap")
  1663  
  1664  	s.bootloader.BootVars["snap_mode"] = boot.TryingStatus
  1665  	s.bootloader.BootVars["snap_try_core"] = "os1"
  1666  	s.bootloader.BootVars["snap_try_kernel"] = "k1"
  1667  	err := boot.MarkBootSuccessful(coreDev)
  1668  	c.Assert(err, IsNil)
  1669  
  1670  	expected := map[string]string{
  1671  		// cleared
  1672  		"snap_mode":       boot.DefaultStatus,
  1673  		"snap_try_kernel": "",
  1674  		"snap_try_core":   "",
  1675  		// updated
  1676  		"snap_kernel": "k1",
  1677  		"snap_core":   "os1",
  1678  	}
  1679  	c.Assert(s.bootloader.BootVars, DeepEquals, expected)
  1680  
  1681  	// do it again, verify its still valid
  1682  	err = boot.MarkBootSuccessful(coreDev)
  1683  	c.Assert(err, IsNil)
  1684  	c.Assert(s.bootloader.BootVars, DeepEquals, expected)
  1685  }
  1686  
  1687  func (s *bootenv20Suite) TestMarkBootSuccessful20AllSnap(c *C) {
  1688  	coreDev := boottest.MockUC20Device("", nil)
  1689  	c.Assert(coreDev.HasModeenv(), Equals, true)
  1690  
  1691  	// bonus points: we were trying both a base snap and a kernel snap
  1692  	m := &boot.Modeenv{
  1693  		Mode:           "run",
  1694  		Base:           s.base1.Filename(),
  1695  		TryBase:        s.base2.Filename(),
  1696  		BaseStatus:     boot.TryingStatus,
  1697  		CurrentKernels: []string{s.kern1.Filename(), s.kern2.Filename()},
  1698  	}
  1699  	r := setupUC20Bootenv(
  1700  		c,
  1701  		s.bootloader,
  1702  		&bootenv20Setup{
  1703  			modeenv:    m,
  1704  			kern:       s.kern1,
  1705  			tryKern:    s.kern2,
  1706  			kernStatus: boot.TryingStatus,
  1707  		},
  1708  	)
  1709  	defer r()
  1710  
  1711  	err := boot.MarkBootSuccessful(coreDev)
  1712  	c.Assert(err, IsNil)
  1713  
  1714  	// check the bootloader variables
  1715  	expected := map[string]string{
  1716  		// cleared
  1717  		"kernel_status": boot.DefaultStatus,
  1718  	}
  1719  	c.Assert(s.bootloader.BootVars, DeepEquals, expected)
  1720  
  1721  	// check that we called EnableKernel() on the try-kernel
  1722  	actual, _ := s.bootloader.GetRunKernelImageFunctionSnapCalls("EnableKernel")
  1723  	c.Assert(actual, DeepEquals, []snap.PlaceInfo{s.kern2})
  1724  
  1725  	// and that we disabled a try kernel
  1726  	_, nDisableTryCalls := s.bootloader.GetRunKernelImageFunctionSnapCalls("DisableTryKernel")
  1727  	c.Assert(nDisableTryCalls, Equals, 1)
  1728  
  1729  	// also check that the modeenv was updated
  1730  	m2, err := boot.ReadModeenv("")
  1731  	c.Assert(err, IsNil)
  1732  	c.Assert(m2.Base, Equals, s.base2.Filename())
  1733  	c.Assert(m2.TryBase, Equals, "")
  1734  	c.Assert(m2.BaseStatus, Equals, boot.DefaultStatus)
  1735  	c.Assert(m2.CurrentKernels, DeepEquals, []string{s.kern2.Filename()})
  1736  
  1737  	// do it again, verify its still valid
  1738  	err = boot.MarkBootSuccessful(coreDev)
  1739  	c.Assert(err, IsNil)
  1740  	c.Assert(s.bootloader.BootVars, DeepEquals, expected)
  1741  
  1742  	// no new enabled kernels
  1743  	actual, _ = s.bootloader.GetRunKernelImageFunctionSnapCalls("EnableKernel")
  1744  	c.Assert(actual, DeepEquals, []snap.PlaceInfo{s.kern2})
  1745  	// we always disable the try kernel as a cleanup operation, so there's one
  1746  	// more call here
  1747  	_, nDisableTryCalls = s.bootloader.GetRunKernelImageFunctionSnapCalls("DisableTryKernel")
  1748  	c.Assert(nDisableTryCalls, Equals, 2)
  1749  }
  1750  
  1751  func (s *bootenv20EnvRefKernelSuite) TestMarkBootSuccessful20AllSnap(c *C) {
  1752  	coreDev := boottest.MockUC20Device("", nil)
  1753  	c.Assert(coreDev.HasModeenv(), Equals, true)
  1754  
  1755  	// bonus points: we were trying both a base snap and a kernel snap
  1756  	m := &boot.Modeenv{
  1757  		Mode:           "run",
  1758  		Base:           s.base1.Filename(),
  1759  		TryBase:        s.base2.Filename(),
  1760  		BaseStatus:     boot.TryingStatus,
  1761  		CurrentKernels: []string{s.kern1.Filename(), s.kern2.Filename()},
  1762  	}
  1763  	r := setupUC20Bootenv(
  1764  		c,
  1765  		s.bootloader,
  1766  		&bootenv20Setup{
  1767  			modeenv:    m,
  1768  			kern:       s.kern1,
  1769  			tryKern:    s.kern2,
  1770  			kernStatus: boot.TryingStatus,
  1771  		},
  1772  	)
  1773  	defer r()
  1774  
  1775  	err := boot.MarkBootSuccessful(coreDev)
  1776  	c.Assert(err, IsNil)
  1777  
  1778  	// check the bootloader variables
  1779  	expected := map[string]string{
  1780  		// cleared
  1781  		"kernel_status":   boot.DefaultStatus,
  1782  		"snap_try_kernel": "",
  1783  		// enabled new kernel
  1784  		"snap_kernel": s.kern2.Filename(),
  1785  	}
  1786  	c.Assert(s.bootloader.BootVars, DeepEquals, expected)
  1787  
  1788  	// also check that the modeenv was updated
  1789  	m2, err := boot.ReadModeenv("")
  1790  	c.Assert(err, IsNil)
  1791  	c.Assert(m2.Base, Equals, s.base2.Filename())
  1792  	c.Assert(m2.TryBase, Equals, "")
  1793  	c.Assert(m2.BaseStatus, Equals, boot.DefaultStatus)
  1794  	c.Assert(m2.CurrentKernels, DeepEquals, []string{s.kern2.Filename()})
  1795  
  1796  	// do it again, verify its still valid
  1797  	err = boot.MarkBootSuccessful(coreDev)
  1798  	c.Assert(err, IsNil)
  1799  	c.Assert(s.bootloader.BootVars, DeepEquals, expected)
  1800  }
  1801  
  1802  func (s *bootenvSuite) TestMarkBootSuccessfulKernelUpdate(c *C) {
  1803  	coreDev := boottest.MockDevice("some-snap")
  1804  
  1805  	s.bootloader.BootVars["snap_mode"] = boot.TryingStatus
  1806  	s.bootloader.BootVars["snap_core"] = "os1"
  1807  	s.bootloader.BootVars["snap_kernel"] = "k1"
  1808  	s.bootloader.BootVars["snap_try_core"] = ""
  1809  	s.bootloader.BootVars["snap_try_kernel"] = "k2"
  1810  	err := boot.MarkBootSuccessful(coreDev)
  1811  	c.Assert(err, IsNil)
  1812  	c.Assert(s.bootloader.BootVars, DeepEquals, map[string]string{
  1813  		// cleared
  1814  		"snap_mode":       boot.DefaultStatus,
  1815  		"snap_try_kernel": "",
  1816  		"snap_try_core":   "",
  1817  		// unchanged
  1818  		"snap_core": "os1",
  1819  		// updated
  1820  		"snap_kernel": "k2",
  1821  	})
  1822  }
  1823  
  1824  func (s *bootenvSuite) TestMarkBootSuccessfulBaseUpdate(c *C) {
  1825  	coreDev := boottest.MockDevice("some-snap")
  1826  
  1827  	s.bootloader.BootVars["snap_mode"] = boot.TryingStatus
  1828  	s.bootloader.BootVars["snap_core"] = "os1"
  1829  	s.bootloader.BootVars["snap_kernel"] = "k1"
  1830  	s.bootloader.BootVars["snap_try_core"] = "os2"
  1831  	s.bootloader.BootVars["snap_try_kernel"] = ""
  1832  	err := boot.MarkBootSuccessful(coreDev)
  1833  	c.Assert(err, IsNil)
  1834  	c.Assert(s.bootloader.BootVars, DeepEquals, map[string]string{
  1835  		// cleared
  1836  		"snap_mode":     boot.DefaultStatus,
  1837  		"snap_try_core": "",
  1838  		// unchanged
  1839  		"snap_kernel":     "k1",
  1840  		"snap_try_kernel": "",
  1841  		// updated
  1842  		"snap_core": "os2",
  1843  	})
  1844  }
  1845  
  1846  func (s *bootenv20Suite) TestMarkBootSuccessful20KernelUpdate(c *C) {
  1847  	// trying a kernel snap
  1848  	m := &boot.Modeenv{
  1849  		Mode:           "run",
  1850  		Base:           s.base1.Filename(),
  1851  		CurrentKernels: []string{s.kern1.Filename(), s.kern2.Filename()},
  1852  	}
  1853  	r := setupUC20Bootenv(
  1854  		c,
  1855  		s.bootloader,
  1856  		&bootenv20Setup{
  1857  			modeenv:    m,
  1858  			kern:       s.kern1,
  1859  			tryKern:    s.kern2,
  1860  			kernStatus: boot.TryingStatus,
  1861  		},
  1862  	)
  1863  	defer r()
  1864  
  1865  	coreDev := boottest.MockUC20Device("", nil)
  1866  	c.Assert(coreDev.HasModeenv(), Equals, true)
  1867  
  1868  	// mark successful
  1869  	err := boot.MarkBootSuccessful(coreDev)
  1870  	c.Assert(err, IsNil)
  1871  
  1872  	// check the bootloader variables
  1873  	expected := map[string]string{"kernel_status": boot.DefaultStatus}
  1874  	c.Assert(s.bootloader.BootVars, DeepEquals, expected)
  1875  
  1876  	// check that MarkBootSuccessful enabled the try kernel
  1877  	actual, _ := s.bootloader.GetRunKernelImageFunctionSnapCalls("EnableKernel")
  1878  	c.Assert(actual, DeepEquals, []snap.PlaceInfo{s.kern2})
  1879  
  1880  	// and that we disabled a try kernel
  1881  	_, nDisableTryCalls := s.bootloader.GetRunKernelImageFunctionSnapCalls("DisableTryKernel")
  1882  	c.Assert(nDisableTryCalls, Equals, 1)
  1883  
  1884  	// check that the new kernel is the only one in modeenv
  1885  	m2, err := boot.ReadModeenv("")
  1886  	c.Assert(err, IsNil)
  1887  	c.Assert(m2.CurrentKernels, DeepEquals, []string{s.kern2.Filename()})
  1888  
  1889  	// do it again, verify its still valid
  1890  	err = boot.MarkBootSuccessful(coreDev)
  1891  	c.Assert(err, IsNil)
  1892  	c.Assert(s.bootloader.BootVars, DeepEquals, expected)
  1893  
  1894  	// no new bootloader calls
  1895  	actual, _ = s.bootloader.GetRunKernelImageFunctionSnapCalls("EnableKernel")
  1896  	c.Assert(actual, DeepEquals, []snap.PlaceInfo{s.kern2})
  1897  
  1898  	// we did disable the kernel again because we always do this to cleanup in
  1899  	// case there were leftovers
  1900  	_, nDisableTryCalls = s.bootloader.GetRunKernelImageFunctionSnapCalls("DisableTryKernel")
  1901  	c.Assert(nDisableTryCalls, Equals, 2)
  1902  }
  1903  
  1904  func (s *bootenv20Suite) TestMarkBootSuccessful20KernelUpdateWithReseal(c *C) {
  1905  	// checked by resealKeyToModeenv
  1906  	s.stampSealedKeys(c, dirs.GlobalRootDir)
  1907  
  1908  	tab := s.bootloaderWithTrustedAssets(c, []string{"asset"})
  1909  
  1910  	data := []byte("foobar")
  1911  	// SHA3-384
  1912  	dataHash := "0fa8abfbdaf924ad307b74dd2ed183b9a4a398891a2f6bac8fd2db7041b77f068580f9c6c66f699b496c2da1cbcc7ed8"
  1913  
  1914  	c.Assert(os.MkdirAll(filepath.Join(boot.InitramfsUbuntuBootDir), 0755), IsNil)
  1915  	c.Assert(ioutil.WriteFile(filepath.Join(boot.InitramfsUbuntuBootDir, "asset"), data, 0644), IsNil)
  1916  
  1917  	// mock the files in cache
  1918  	mockAssetsCache(c, dirs.GlobalRootDir, "trusted", []string{
  1919  		"asset-" + dataHash,
  1920  	})
  1921  
  1922  	assetBf := bootloader.NewBootFile("", filepath.Join(dirs.SnapBootAssetsDir, "trusted", fmt.Sprintf("asset-%s", dataHash)), bootloader.RoleRunMode)
  1923  	newRunKernelBf := bootloader.NewBootFile(filepath.Join(s.kern2.Filename()), "kernel.efi", bootloader.RoleRunMode)
  1924  
  1925  	tab.BootChainList = []bootloader.BootFile{
  1926  		bootloader.NewBootFile("", "asset", bootloader.RoleRunMode),
  1927  		newRunKernelBf,
  1928  	}
  1929  
  1930  	coreDev := boottest.MockUC20Device("", nil)
  1931  	c.Assert(coreDev.HasModeenv(), Equals, true)
  1932  	model := coreDev.Model()
  1933  
  1934  	// trying a kernel snap
  1935  	m := &boot.Modeenv{
  1936  		Mode:           "run",
  1937  		Base:           s.base1.Filename(),
  1938  		CurrentKernels: []string{s.kern1.Filename(), s.kern2.Filename()},
  1939  		CurrentTrustedBootAssets: boot.BootAssetsMap{
  1940  			"asset": {dataHash},
  1941  		},
  1942  		Model:          model.Model(),
  1943  		BrandID:        model.BrandID(),
  1944  		Grade:          string(model.Grade()),
  1945  		ModelSignKeyID: model.SignKeyID(),
  1946  	}
  1947  	r := setupUC20Bootenv(
  1948  		c,
  1949  		tab.MockBootloader,
  1950  		&bootenv20Setup{
  1951  			modeenv:    m,
  1952  			kern:       s.kern1,
  1953  			tryKern:    s.kern2,
  1954  			kernStatus: boot.TryingStatus,
  1955  		},
  1956  	)
  1957  	defer r()
  1958  
  1959  	// write boot-chains that describe a state in which we have a new kernel
  1960  	// candidate (pc-kernel_2)
  1961  	bootChains := []boot.BootChain{{
  1962  		BrandID:        "my-brand",
  1963  		Model:          "my-model-uc20",
  1964  		Grade:          "dangerous",
  1965  		ModelSignKeyID: "Jv8_JiHiIzJVcO9M55pPdqSDWUvuhfDIBJUS-3VW7F_idjix7Ffn5qMxB21ZQuij",
  1966  		AssetChain: []boot.BootAsset{{
  1967  			Role: bootloader.RoleRunMode, Name: "asset", Hashes: []string{
  1968  				"0fa8abfbdaf924ad307b74dd2ed183b9a4a398891a2f6bac8fd2db7041b77f068580f9c6c66f699b496c2da1cbcc7ed8",
  1969  			},
  1970  		}},
  1971  		Kernel:         "pc-kernel",
  1972  		KernelRevision: "1",
  1973  		KernelCmdlines: []string{"snapd_recovery_mode=run"},
  1974  	}, {
  1975  		BrandID:        "my-brand",
  1976  		Model:          "my-model-uc20",
  1977  		Grade:          "dangerous",
  1978  		ModelSignKeyID: "Jv8_JiHiIzJVcO9M55pPdqSDWUvuhfDIBJUS-3VW7F_idjix7Ffn5qMxB21ZQuij",
  1979  		AssetChain: []boot.BootAsset{{
  1980  			Role: bootloader.RoleRunMode, Name: "asset", Hashes: []string{
  1981  				"0fa8abfbdaf924ad307b74dd2ed183b9a4a398891a2f6bac8fd2db7041b77f068580f9c6c66f699b496c2da1cbcc7ed8",
  1982  			},
  1983  		}},
  1984  		Kernel:         "pc-kernel",
  1985  		KernelRevision: "2",
  1986  		KernelCmdlines: []string{"snapd_recovery_mode=run"},
  1987  	}}
  1988  
  1989  	err := boot.WriteBootChains(boot.ToPredictableBootChains(bootChains), filepath.Join(dirs.SnapFDEDir, "boot-chains"), 0)
  1990  	c.Assert(err, IsNil)
  1991  
  1992  	resealCalls := 0
  1993  	restore := boot.MockSecbootResealKeys(func(params *secboot.ResealKeysParams) error {
  1994  		resealCalls++
  1995  
  1996  		c.Assert(params.ModelParams, HasLen, 1)
  1997  		mp := params.ModelParams[0]
  1998  		c.Check(mp.Model.Model(), Equals, model.Model())
  1999  		for _, ch := range mp.EFILoadChains {
  2000  			printChain(c, ch, "-")
  2001  		}
  2002  		c.Check(mp.EFILoadChains, DeepEquals, []*secboot.LoadChain{
  2003  			secboot.NewLoadChain(assetBf,
  2004  				secboot.NewLoadChain(newRunKernelBf)),
  2005  		})
  2006  		return nil
  2007  	})
  2008  	defer restore()
  2009  
  2010  	// mark successful
  2011  	err = boot.MarkBootSuccessful(coreDev)
  2012  	c.Assert(err, IsNil)
  2013  
  2014  	c.Check(resealCalls, Equals, 1)
  2015  	// check the bootloader variables
  2016  	expected := map[string]string{
  2017  		"kernel_status":   boot.DefaultStatus,
  2018  		"snap_kernel":     s.kern2.Filename(),
  2019  		"snap_try_kernel": boot.DefaultStatus,
  2020  	}
  2021  	c.Assert(tab.BootVars, DeepEquals, expected)
  2022  	c.Check(tab.BootChainKernelPath, DeepEquals, []string{s.kern2.MountFile()})
  2023  
  2024  	// check that the new kernel is the only one in modeenv
  2025  	m2, err := boot.ReadModeenv("")
  2026  	c.Assert(err, IsNil)
  2027  	c.Assert(m2.CurrentKernels, DeepEquals, []string{s.kern2.Filename()})
  2028  }
  2029  
  2030  func (s *bootenv20EnvRefKernelSuite) TestMarkBootSuccessful20KernelUpdate(c *C) {
  2031  	// trying a kernel snap
  2032  	m := &boot.Modeenv{
  2033  		Mode:           "run",
  2034  		Base:           s.base1.Filename(),
  2035  		CurrentKernels: []string{s.kern1.Filename(), s.kern2.Filename()},
  2036  	}
  2037  	r := setupUC20Bootenv(
  2038  		c,
  2039  		s.bootloader,
  2040  		&bootenv20Setup{
  2041  			modeenv:    m,
  2042  			kern:       s.kern1,
  2043  			tryKern:    s.kern2,
  2044  			kernStatus: boot.TryingStatus,
  2045  		},
  2046  	)
  2047  	defer r()
  2048  
  2049  	coreDev := boottest.MockUC20Device("", nil)
  2050  	c.Assert(coreDev.HasModeenv(), Equals, true)
  2051  
  2052  	// mark successful
  2053  	err := boot.MarkBootSuccessful(coreDev)
  2054  	c.Assert(err, IsNil)
  2055  
  2056  	// check the bootloader variables
  2057  	expected := map[string]string{
  2058  		"kernel_status":   boot.DefaultStatus,
  2059  		"snap_kernel":     s.kern2.Filename(),
  2060  		"snap_try_kernel": "",
  2061  	}
  2062  	c.Assert(s.bootloader.BootVars, DeepEquals, expected)
  2063  
  2064  	// check that the new kernel is the only one in modeenv
  2065  	m2, err := boot.ReadModeenv("")
  2066  	c.Assert(err, IsNil)
  2067  	c.Assert(m2.CurrentKernels, DeepEquals, []string{s.kern2.Filename()})
  2068  
  2069  	// do it again, verify its still valid
  2070  	err = boot.MarkBootSuccessful(coreDev)
  2071  	c.Assert(err, IsNil)
  2072  	c.Assert(s.bootloader.BootVars, DeepEquals, expected)
  2073  	c.Assert(s.bootloader.BootVars, DeepEquals, expected)
  2074  }
  2075  
  2076  func (s *bootenv20Suite) TestMarkBootSuccessful20BaseUpdate(c *C) {
  2077  	// we were trying a base snap
  2078  	m := &boot.Modeenv{
  2079  		Mode:           "run",
  2080  		Base:           s.base1.Filename(),
  2081  		TryBase:        s.base2.Filename(),
  2082  		BaseStatus:     boot.TryingStatus,
  2083  		CurrentKernels: []string{s.kern1.Filename()},
  2084  	}
  2085  	r := setupUC20Bootenv(
  2086  		c,
  2087  		s.bootloader,
  2088  		&bootenv20Setup{
  2089  			modeenv:    m,
  2090  			kern:       s.kern1,
  2091  			kernStatus: boot.DefaultStatus,
  2092  		},
  2093  	)
  2094  	defer r()
  2095  
  2096  	coreDev := boottest.MockUC20Device("", nil)
  2097  	c.Assert(coreDev.HasModeenv(), Equals, true)
  2098  
  2099  	// mark successful
  2100  	err := boot.MarkBootSuccessful(coreDev)
  2101  	c.Assert(err, IsNil)
  2102  
  2103  	// check the modeenv
  2104  	m2, err := boot.ReadModeenv("")
  2105  	c.Assert(err, IsNil)
  2106  	c.Assert(m2.Base, Equals, s.base2.Filename())
  2107  	c.Assert(m2.TryBase, Equals, "")
  2108  	c.Assert(m2.BaseStatus, Equals, "")
  2109  
  2110  	// do it again, verify its still valid
  2111  	err = boot.MarkBootSuccessful(coreDev)
  2112  	c.Assert(err, IsNil)
  2113  
  2114  	// check the modeenv again
  2115  	m3, err := boot.ReadModeenv("")
  2116  	c.Assert(err, IsNil)
  2117  	c.Assert(m3.Base, Equals, s.base2.Filename())
  2118  	c.Assert(m3.TryBase, Equals, "")
  2119  	c.Assert(m3.BaseStatus, Equals, "")
  2120  }
  2121  
  2122  func (s *bootenv20Suite) bootloaderWithTrustedAssets(c *C, trustedAssets []string) *bootloadertest.MockTrustedAssetsBootloader {
  2123  	// TODO:UC20: this should be an ExtractedRecoveryKernelImageBootloader
  2124  	// because that would reflect our main currently supported
  2125  	// trusted assets bootloader (grub)
  2126  	tab := bootloadertest.Mock("trusted", "").WithTrustedAssets()
  2127  	bootloader.Force(tab)
  2128  	tab.TrustedAssetsList = trustedAssets
  2129  	s.AddCleanup(func() { bootloader.Force(nil) })
  2130  	return tab
  2131  }
  2132  
  2133  func (s *bootenv20Suite) TestMarkBootSuccessful20BootAssetsUpdateHappy(c *C) {
  2134  	// checked by resealKeyToModeenv
  2135  	s.stampSealedKeys(c, dirs.GlobalRootDir)
  2136  
  2137  	tab := s.bootloaderWithTrustedAssets(c, []string{"asset", "shim"})
  2138  
  2139  	data := []byte("foobar")
  2140  	// SHA3-384
  2141  	dataHash := "0fa8abfbdaf924ad307b74dd2ed183b9a4a398891a2f6bac8fd2db7041b77f068580f9c6c66f699b496c2da1cbcc7ed8"
  2142  	shim := []byte("shim")
  2143  	shimHash := "dac0063e831d4b2e7a330426720512fc50fa315042f0bb30f9d1db73e4898dcb89119cac41fdfa62137c8931a50f9d7b"
  2144  
  2145  	c.Assert(os.MkdirAll(boot.InitramfsUbuntuBootDir, 0755), IsNil)
  2146  	c.Assert(os.MkdirAll(boot.InitramfsUbuntuSeedDir, 0755), IsNil)
  2147  	// only asset for ubuntu
  2148  	c.Assert(ioutil.WriteFile(filepath.Join(boot.InitramfsUbuntuBootDir, "asset"), data, 0644), IsNil)
  2149  	// shim and asset for seed
  2150  	c.Assert(ioutil.WriteFile(filepath.Join(boot.InitramfsUbuntuSeedDir, "asset"), data, 0644), IsNil)
  2151  	c.Assert(ioutil.WriteFile(filepath.Join(boot.InitramfsUbuntuSeedDir, "shim"), shim, 0644), IsNil)
  2152  
  2153  	// mock the files in cache
  2154  	mockAssetsCache(c, dirs.GlobalRootDir, "trusted", []string{
  2155  		"shim-recoveryshimhash",
  2156  		"shim-" + shimHash,
  2157  		"asset-assethash",
  2158  		"asset-recoveryassethash",
  2159  		"asset-" + dataHash,
  2160  	})
  2161  
  2162  	shimBf := bootloader.NewBootFile("", filepath.Join(dirs.SnapBootAssetsDir, "trusted", fmt.Sprintf("shim-%s", shimHash)), bootloader.RoleRecovery)
  2163  	assetBf := bootloader.NewBootFile("", filepath.Join(dirs.SnapBootAssetsDir, "trusted", fmt.Sprintf("asset-%s", dataHash)), bootloader.RoleRecovery)
  2164  	runKernelBf := bootloader.NewBootFile(filepath.Join(s.kern1.Filename()), "kernel.efi", bootloader.RoleRunMode)
  2165  	recoveryKernelBf := bootloader.NewBootFile("pc-kernel_1.snap", "kernel.efi", bootloader.RoleRecovery)
  2166  
  2167  	tab.BootChainList = []bootloader.BootFile{
  2168  		bootloader.NewBootFile("", "shim", bootloader.RoleRecovery),
  2169  		bootloader.NewBootFile("", "asset", bootloader.RoleRecovery),
  2170  		runKernelBf,
  2171  	}
  2172  	tab.RecoveryBootChainList = []bootloader.BootFile{
  2173  		bootloader.NewBootFile("", "shim", bootloader.RoleRecovery),
  2174  		bootloader.NewBootFile("", "asset", bootloader.RoleRecovery),
  2175  		recoveryKernelBf,
  2176  	}
  2177  
  2178  	uc20Model := boottest.MakeMockUC20Model()
  2179  
  2180  	restore := boot.MockSeedReadSystemEssential(func(seedDir, label string, essentialTypes []snap.Type, tm timings.Measurer) (*asserts.Model, []*seed.Snap, error) {
  2181  		return uc20Model, []*seed.Snap{mockKernelSeedSnap(c, snap.R(1)), mockGadgetSeedSnap(c, nil)}, nil
  2182  	})
  2183  	defer restore()
  2184  
  2185  	// we were trying an update of boot assets
  2186  	m := &boot.Modeenv{
  2187  		Mode:           "run",
  2188  		Base:           s.base1.Filename(),
  2189  		CurrentKernels: []string{s.kern1.Filename()},
  2190  		CurrentTrustedBootAssets: boot.BootAssetsMap{
  2191  			"asset": {"assethash", dataHash},
  2192  		},
  2193  		CurrentTrustedRecoveryBootAssets: boot.BootAssetsMap{
  2194  			"asset": {"recoveryassethash", dataHash},
  2195  			"shim":  {"recoveryshimhash", shimHash},
  2196  		},
  2197  		CurrentRecoverySystems: []string{"system"},
  2198  
  2199  		Model:          uc20Model.Model(),
  2200  		BrandID:        uc20Model.BrandID(),
  2201  		Grade:          string(uc20Model.Grade()),
  2202  		ModelSignKeyID: uc20Model.SignKeyID(),
  2203  	}
  2204  	r := setupUC20Bootenv(
  2205  		c,
  2206  		tab.MockBootloader,
  2207  		&bootenv20Setup{
  2208  			modeenv:    m,
  2209  			kern:       s.kern1,
  2210  			kernStatus: boot.DefaultStatus,
  2211  		},
  2212  	)
  2213  	defer r()
  2214  
  2215  	coreDev := boottest.MockUC20Device("", uc20Model)
  2216  	c.Assert(coreDev.HasModeenv(), Equals, true)
  2217  
  2218  	resealCalls := 0
  2219  	restore = boot.MockSecbootResealKeys(func(params *secboot.ResealKeysParams) error {
  2220  		resealCalls++
  2221  
  2222  		c.Assert(params.ModelParams, HasLen, 1)
  2223  		mp := params.ModelParams[0]
  2224  		c.Check(mp.Model.Model(), Equals, uc20Model.Model())
  2225  		for _, ch := range mp.EFILoadChains {
  2226  			printChain(c, ch, "-")
  2227  		}
  2228  		switch resealCalls {
  2229  		case 1:
  2230  			c.Check(mp.EFILoadChains, DeepEquals, []*secboot.LoadChain{
  2231  				secboot.NewLoadChain(shimBf,
  2232  					secboot.NewLoadChain(assetBf,
  2233  						secboot.NewLoadChain(recoveryKernelBf))),
  2234  				secboot.NewLoadChain(shimBf,
  2235  					secboot.NewLoadChain(assetBf,
  2236  						secboot.NewLoadChain(runKernelBf))),
  2237  			})
  2238  		case 2:
  2239  			c.Check(mp.EFILoadChains, DeepEquals, []*secboot.LoadChain{
  2240  				secboot.NewLoadChain(shimBf,
  2241  					secboot.NewLoadChain(assetBf,
  2242  						secboot.NewLoadChain(recoveryKernelBf))),
  2243  			})
  2244  		default:
  2245  			c.Errorf("unexpected additional call to secboot.ResealKey (call # %d)", resealCalls)
  2246  		}
  2247  		return nil
  2248  	})
  2249  	defer restore()
  2250  
  2251  	// mark successful
  2252  	err := boot.MarkBootSuccessful(coreDev)
  2253  	c.Assert(err, IsNil)
  2254  
  2255  	// check the modeenv
  2256  	m2, err := boot.ReadModeenv("")
  2257  	c.Assert(err, IsNil)
  2258  	// update assets are in the list
  2259  	c.Check(m2.CurrentTrustedBootAssets, DeepEquals, boot.BootAssetsMap{
  2260  		"asset": {dataHash},
  2261  	})
  2262  	c.Check(m2.CurrentTrustedRecoveryBootAssets, DeepEquals, boot.BootAssetsMap{
  2263  		"asset": {dataHash},
  2264  		"shim":  {shimHash},
  2265  	})
  2266  	// unused files were dropped from cache
  2267  	checkContentGlob(c, filepath.Join(dirs.SnapBootAssetsDir, "trusted", "*"), []string{
  2268  		filepath.Join(dirs.SnapBootAssetsDir, "trusted", "asset-"+dataHash),
  2269  		filepath.Join(dirs.SnapBootAssetsDir, "trusted", "shim-"+shimHash),
  2270  	})
  2271  	c.Check(resealCalls, Equals, 2)
  2272  }
  2273  
  2274  func (s *bootenv20Suite) TestMarkBootSuccessful20BootAssetsStableStateHappy(c *C) {
  2275  	// checked by resealKeyToModeenv
  2276  	s.stampSealedKeys(c, dirs.GlobalRootDir)
  2277  
  2278  	tab := s.bootloaderWithTrustedAssets(c, []string{"nested/asset", "shim"})
  2279  
  2280  	data := []byte("foobar")
  2281  	// SHA3-384
  2282  	dataHash := "0fa8abfbdaf924ad307b74dd2ed183b9a4a398891a2f6bac8fd2db7041b77f068580f9c6c66f699b496c2da1cbcc7ed8"
  2283  	shim := []byte("shim")
  2284  	shimHash := "dac0063e831d4b2e7a330426720512fc50fa315042f0bb30f9d1db73e4898dcb89119cac41fdfa62137c8931a50f9d7b"
  2285  
  2286  	c.Assert(os.MkdirAll(filepath.Join(boot.InitramfsUbuntuBootDir, "nested"), 0755), IsNil)
  2287  	c.Assert(os.MkdirAll(filepath.Join(boot.InitramfsUbuntuSeedDir, "nested"), 0755), IsNil)
  2288  	// only asset for ubuntu-boot
  2289  	c.Assert(ioutil.WriteFile(filepath.Join(boot.InitramfsUbuntuBootDir, "nested/asset"), data, 0644), IsNil)
  2290  	// shim and asset for ubuntu-seed
  2291  	c.Assert(ioutil.WriteFile(filepath.Join(boot.InitramfsUbuntuSeedDir, "nested/asset"), data, 0644), IsNil)
  2292  	c.Assert(ioutil.WriteFile(filepath.Join(boot.InitramfsUbuntuSeedDir, "shim"), shim, 0644), IsNil)
  2293  
  2294  	// mock the files in cache
  2295  	mockAssetsCache(c, dirs.GlobalRootDir, "trusted", []string{
  2296  		"shim-" + shimHash,
  2297  		"asset-" + dataHash,
  2298  	})
  2299  
  2300  	runKernelBf := bootloader.NewBootFile(filepath.Join(s.kern1.Filename()), "kernel.efi", bootloader.RoleRunMode)
  2301  	recoveryKernelBf := bootloader.NewBootFile("/var/lib/snapd/seed/snaps/pc-kernel_1.snap", "kernel.efi", bootloader.RoleRecovery)
  2302  
  2303  	tab.BootChainList = []bootloader.BootFile{
  2304  		bootloader.NewBootFile("", "shim", bootloader.RoleRecovery),
  2305  		bootloader.NewBootFile("", "asset", bootloader.RoleRecovery),
  2306  		runKernelBf,
  2307  	}
  2308  	tab.RecoveryBootChainList = []bootloader.BootFile{
  2309  		bootloader.NewBootFile("", "shim", bootloader.RoleRecovery),
  2310  		bootloader.NewBootFile("", "asset", bootloader.RoleRecovery),
  2311  		recoveryKernelBf,
  2312  	}
  2313  
  2314  	uc20Model := boottest.MakeMockUC20Model()
  2315  
  2316  	restore := boot.MockSeedReadSystemEssential(func(seedDir, label string, essentialTypes []snap.Type, tm timings.Measurer) (*asserts.Model, []*seed.Snap, error) {
  2317  		return uc20Model, []*seed.Snap{mockNamedKernelSeedSnap(c, snap.R(1), "pc-kernel-recovery"), mockGadgetSeedSnap(c, nil)}, nil
  2318  	})
  2319  	defer restore()
  2320  
  2321  	// we were trying an update of boot assets
  2322  	m := &boot.Modeenv{
  2323  		Mode:           "run",
  2324  		Base:           s.base1.Filename(),
  2325  		CurrentKernels: []string{s.kern1.Filename()},
  2326  		CurrentTrustedBootAssets: boot.BootAssetsMap{
  2327  			"asset": {dataHash},
  2328  		},
  2329  		CurrentTrustedRecoveryBootAssets: boot.BootAssetsMap{
  2330  			"asset": {dataHash},
  2331  			"shim":  {shimHash},
  2332  		},
  2333  		CurrentRecoverySystems:    []string{"system"},
  2334  		CurrentKernelCommandLines: boot.BootCommandLines{"snapd_recovery_mode=run"},
  2335  
  2336  		Model:          uc20Model.Model(),
  2337  		BrandID:        uc20Model.BrandID(),
  2338  		Grade:          string(uc20Model.Grade()),
  2339  		ModelSignKeyID: uc20Model.SignKeyID(),
  2340  	}
  2341  	r := setupUC20Bootenv(
  2342  		c,
  2343  		tab.MockBootloader,
  2344  		&bootenv20Setup{
  2345  			modeenv:    m,
  2346  			kern:       s.kern1,
  2347  			kernStatus: boot.DefaultStatus,
  2348  		},
  2349  	)
  2350  	defer r()
  2351  
  2352  	coreDev := boottest.MockUC20Device("", uc20Model)
  2353  	c.Assert(coreDev.HasModeenv(), Equals, true)
  2354  
  2355  	resealCalls := 0
  2356  	restore = boot.MockSecbootResealKeys(func(params *secboot.ResealKeysParams) error {
  2357  		resealCalls++
  2358  		return nil
  2359  	})
  2360  	defer restore()
  2361  
  2362  	// write boot-chains for current state that will stay unchanged
  2363  	bootChains := []boot.BootChain{{
  2364  		BrandID:        "my-brand",
  2365  		Model:          "my-model-uc20",
  2366  		Grade:          "dangerous",
  2367  		ModelSignKeyID: "Jv8_JiHiIzJVcO9M55pPdqSDWUvuhfDIBJUS-3VW7F_idjix7Ffn5qMxB21ZQuij",
  2368  		AssetChain: []boot.BootAsset{{
  2369  			Role: bootloader.RoleRecovery, Name: "shim",
  2370  			Hashes: []string{
  2371  				"dac0063e831d4b2e7a330426720512fc50fa315042f0bb30f9d1db73e4898dcb89119cac41fdfa62137c8931a50f9d7b",
  2372  			},
  2373  		}, {
  2374  			Role: bootloader.RoleRecovery, Name: "asset", Hashes: []string{
  2375  				"0fa8abfbdaf924ad307b74dd2ed183b9a4a398891a2f6bac8fd2db7041b77f068580f9c6c66f699b496c2da1cbcc7ed8",
  2376  			},
  2377  		}},
  2378  		Kernel:         "pc-kernel",
  2379  		KernelRevision: "1",
  2380  		KernelCmdlines: []string{"snapd_recovery_mode=run"},
  2381  	}, {
  2382  		BrandID:        "my-brand",
  2383  		Model:          "my-model-uc20",
  2384  		Grade:          "dangerous",
  2385  		ModelSignKeyID: "Jv8_JiHiIzJVcO9M55pPdqSDWUvuhfDIBJUS-3VW7F_idjix7Ffn5qMxB21ZQuij",
  2386  		AssetChain: []boot.BootAsset{{
  2387  			Role: bootloader.RoleRecovery, Name: "shim",
  2388  			Hashes: []string{
  2389  				"dac0063e831d4b2e7a330426720512fc50fa315042f0bb30f9d1db73e4898dcb89119cac41fdfa62137c8931a50f9d7b",
  2390  			},
  2391  		}, {
  2392  			Role: bootloader.RoleRecovery, Name: "asset", Hashes: []string{
  2393  				"0fa8abfbdaf924ad307b74dd2ed183b9a4a398891a2f6bac8fd2db7041b77f068580f9c6c66f699b496c2da1cbcc7ed8",
  2394  			},
  2395  		}},
  2396  		Kernel:         "pc-kernel-recovery",
  2397  		KernelRevision: "1",
  2398  		KernelCmdlines: []string{"snapd_recovery_mode=recover snapd_recovery_system=system"},
  2399  	}}
  2400  
  2401  	recoveryBootChains := []boot.BootChain{bootChains[1]}
  2402  
  2403  	err := boot.WriteBootChains(boot.ToPredictableBootChains(bootChains), filepath.Join(dirs.SnapFDEDir, "boot-chains"), 0)
  2404  	c.Assert(err, IsNil)
  2405  
  2406  	err = boot.WriteBootChains(boot.ToPredictableBootChains(recoveryBootChains), filepath.Join(dirs.SnapFDEDir, "recovery-boot-chains"), 0)
  2407  	c.Assert(err, IsNil)
  2408  
  2409  	// mark successful
  2410  	err = boot.MarkBootSuccessful(coreDev)
  2411  	c.Assert(err, IsNil)
  2412  
  2413  	// modeenv is unchanged
  2414  	m2, err := boot.ReadModeenv("")
  2415  	c.Assert(err, IsNil)
  2416  	c.Check(m2.CurrentTrustedBootAssets, DeepEquals, m.CurrentTrustedBootAssets)
  2417  	c.Check(m2.CurrentTrustedRecoveryBootAssets, DeepEquals, m.CurrentTrustedRecoveryBootAssets)
  2418  	// files are still in cache
  2419  	checkContentGlob(c, filepath.Join(dirs.SnapBootAssetsDir, "trusted", "*"), []string{
  2420  		filepath.Join(dirs.SnapBootAssetsDir, "trusted", "asset-"+dataHash),
  2421  		filepath.Join(dirs.SnapBootAssetsDir, "trusted", "shim-"+shimHash),
  2422  	})
  2423  
  2424  	// boot chains were built
  2425  	c.Check(tab.BootChainKernelPath, DeepEquals, []string{
  2426  		s.kern1.MountFile(),
  2427  	})
  2428  	// no actual reseal
  2429  	c.Check(resealCalls, Equals, 0)
  2430  }
  2431  
  2432  func (s *bootenv20Suite) TestMarkBootSuccessful20BootUnassertedKernelAssetsStableStateHappy(c *C) {
  2433  	// checked by resealKeyToModeenv
  2434  	s.stampSealedKeys(c, dirs.GlobalRootDir)
  2435  
  2436  	tab := s.bootloaderWithTrustedAssets(c, []string{"nested/asset", "shim"})
  2437  
  2438  	data := []byte("foobar")
  2439  	// SHA3-384
  2440  	dataHash := "0fa8abfbdaf924ad307b74dd2ed183b9a4a398891a2f6bac8fd2db7041b77f068580f9c6c66f699b496c2da1cbcc7ed8"
  2441  	shim := []byte("shim")
  2442  	shimHash := "dac0063e831d4b2e7a330426720512fc50fa315042f0bb30f9d1db73e4898dcb89119cac41fdfa62137c8931a50f9d7b"
  2443  
  2444  	c.Assert(os.MkdirAll(filepath.Join(boot.InitramfsUbuntuBootDir, "nested"), 0755), IsNil)
  2445  	c.Assert(os.MkdirAll(filepath.Join(boot.InitramfsUbuntuSeedDir, "nested"), 0755), IsNil)
  2446  	// only asset for ubuntu-boot
  2447  	c.Assert(ioutil.WriteFile(filepath.Join(boot.InitramfsUbuntuBootDir, "nested/asset"), data, 0644), IsNil)
  2448  	// shim and asset for ubuntu-seed
  2449  	c.Assert(ioutil.WriteFile(filepath.Join(boot.InitramfsUbuntuSeedDir, "nested/asset"), data, 0644), IsNil)
  2450  	c.Assert(ioutil.WriteFile(filepath.Join(boot.InitramfsUbuntuSeedDir, "shim"), shim, 0644), IsNil)
  2451  
  2452  	// mock the files in cache
  2453  	mockAssetsCache(c, dirs.GlobalRootDir, "trusted", []string{
  2454  		"shim-" + shimHash,
  2455  		"asset-" + dataHash,
  2456  	})
  2457  
  2458  	runKernelBf := bootloader.NewBootFile(filepath.Join(s.ukern1.Filename()), "kernel.efi", bootloader.RoleRunMode)
  2459  	recoveryKernelBf := bootloader.NewBootFile("/var/lib/snapd/seed/snaps/pc-kernel_1.snap", "kernel.efi", bootloader.RoleRecovery)
  2460  
  2461  	tab.BootChainList = []bootloader.BootFile{
  2462  		bootloader.NewBootFile("", "shim", bootloader.RoleRecovery),
  2463  		bootloader.NewBootFile("", "asset", bootloader.RoleRecovery),
  2464  		runKernelBf,
  2465  	}
  2466  	tab.RecoveryBootChainList = []bootloader.BootFile{
  2467  		bootloader.NewBootFile("", "shim", bootloader.RoleRecovery),
  2468  		bootloader.NewBootFile("", "asset", bootloader.RoleRecovery),
  2469  		recoveryKernelBf,
  2470  	}
  2471  
  2472  	uc20Model := boottest.MakeMockUC20Model()
  2473  
  2474  	restore := boot.MockSeedReadSystemEssential(func(seedDir, label string, essentialTypes []snap.Type, tm timings.Measurer) (*asserts.Model, []*seed.Snap, error) {
  2475  		return uc20Model, []*seed.Snap{mockNamedKernelSeedSnap(c, snap.R(1), "pc-kernel-recovery"), mockGadgetSeedSnap(c, nil)}, nil
  2476  	})
  2477  	defer restore()
  2478  
  2479  	// we were trying an update of boot assets
  2480  	m := &boot.Modeenv{
  2481  		Mode:           "run",
  2482  		Base:           s.base1.Filename(),
  2483  		CurrentKernels: []string{s.ukern1.Filename()},
  2484  		CurrentTrustedBootAssets: boot.BootAssetsMap{
  2485  			"asset": {dataHash},
  2486  		},
  2487  		CurrentTrustedRecoveryBootAssets: boot.BootAssetsMap{
  2488  			"asset": {dataHash},
  2489  			"shim":  {shimHash},
  2490  		},
  2491  		CurrentRecoverySystems:    []string{"system"},
  2492  		GoodRecoverySystems:       []string{"system"},
  2493  		CurrentKernelCommandLines: boot.BootCommandLines{"snapd_recovery_mode=run"},
  2494  		// leave this comment to keep old gofmt happy
  2495  		Model:          "my-model-uc20",
  2496  		BrandID:        "my-brand",
  2497  		Grade:          "dangerous",
  2498  		ModelSignKeyID: "Jv8_JiHiIzJVcO9M55pPdqSDWUvuhfDIBJUS-3VW7F_idjix7Ffn5qMxB21ZQuij",
  2499  	}
  2500  	r := setupUC20Bootenv(
  2501  		c,
  2502  		tab.MockBootloader,
  2503  		&bootenv20Setup{
  2504  			modeenv:    m,
  2505  			kern:       s.ukern1,
  2506  			kernStatus: boot.DefaultStatus,
  2507  		},
  2508  	)
  2509  	defer r()
  2510  
  2511  	coreDev := boottest.MockUC20Device("", uc20Model)
  2512  	c.Assert(coreDev.HasModeenv(), Equals, true)
  2513  
  2514  	resealCalls := 0
  2515  	restore = boot.MockSecbootResealKeys(func(params *secboot.ResealKeysParams) error {
  2516  		resealCalls++
  2517  		return nil
  2518  	})
  2519  	defer restore()
  2520  
  2521  	// write boot-chains for current state that will stay unchanged
  2522  	bootChains := []boot.BootChain{{
  2523  		BrandID:        "my-brand",
  2524  		Model:          "my-model-uc20",
  2525  		Grade:          "dangerous",
  2526  		ModelSignKeyID: "Jv8_JiHiIzJVcO9M55pPdqSDWUvuhfDIBJUS-3VW7F_idjix7Ffn5qMxB21ZQuij",
  2527  		AssetChain: []boot.BootAsset{{
  2528  			Role: bootloader.RoleRecovery, Name: "shim",
  2529  			Hashes: []string{
  2530  				"dac0063e831d4b2e7a330426720512fc50fa315042f0bb30f9d1db73e4898dcb89119cac41fdfa62137c8931a50f9d7b",
  2531  			},
  2532  		}, {
  2533  			Role: bootloader.RoleRecovery, Name: "asset", Hashes: []string{
  2534  				"0fa8abfbdaf924ad307b74dd2ed183b9a4a398891a2f6bac8fd2db7041b77f068580f9c6c66f699b496c2da1cbcc7ed8",
  2535  			},
  2536  		}},
  2537  		Kernel: "pc-kernel",
  2538  		// unasserted kernel snap
  2539  		KernelRevision: "",
  2540  		KernelCmdlines: []string{"snapd_recovery_mode=run"},
  2541  	}, {
  2542  		BrandID:        "my-brand",
  2543  		Model:          "my-model-uc20",
  2544  		Grade:          "dangerous",
  2545  		ModelSignKeyID: "Jv8_JiHiIzJVcO9M55pPdqSDWUvuhfDIBJUS-3VW7F_idjix7Ffn5qMxB21ZQuij",
  2546  		AssetChain: []boot.BootAsset{{
  2547  			Role: bootloader.RoleRecovery, Name: "shim",
  2548  			Hashes: []string{
  2549  				"dac0063e831d4b2e7a330426720512fc50fa315042f0bb30f9d1db73e4898dcb89119cac41fdfa62137c8931a50f9d7b",
  2550  			},
  2551  		}, {
  2552  			Role: bootloader.RoleRecovery, Name: "asset", Hashes: []string{
  2553  				"0fa8abfbdaf924ad307b74dd2ed183b9a4a398891a2f6bac8fd2db7041b77f068580f9c6c66f699b496c2da1cbcc7ed8",
  2554  			},
  2555  		}},
  2556  		Kernel:         "pc-kernel-recovery",
  2557  		KernelRevision: "1",
  2558  		KernelCmdlines: []string{"snapd_recovery_mode=recover snapd_recovery_system=system"},
  2559  	}}
  2560  
  2561  	recoveryBootChains := []boot.BootChain{bootChains[1]}
  2562  
  2563  	err := boot.WriteBootChains(boot.ToPredictableBootChains(bootChains), filepath.Join(dirs.SnapFDEDir, "boot-chains"), 0)
  2564  	c.Assert(err, IsNil)
  2565  
  2566  	err = boot.WriteBootChains(boot.ToPredictableBootChains(recoveryBootChains), filepath.Join(dirs.SnapFDEDir, "recovery-boot-chains"), 0)
  2567  	c.Assert(err, IsNil)
  2568  
  2569  	// mark successful
  2570  	err = boot.MarkBootSuccessful(coreDev)
  2571  	c.Assert(err, IsNil)
  2572  
  2573  	// modeenv is unchanged
  2574  	m2, err := boot.ReadModeenv("")
  2575  	c.Assert(err, IsNil)
  2576  	c.Check(m2.CurrentTrustedBootAssets, DeepEquals, m.CurrentTrustedBootAssets)
  2577  	c.Check(m2.CurrentTrustedRecoveryBootAssets, DeepEquals, m.CurrentTrustedRecoveryBootAssets)
  2578  	// files are still in cache
  2579  	checkContentGlob(c, filepath.Join(dirs.SnapBootAssetsDir, "trusted", "*"), []string{
  2580  		filepath.Join(dirs.SnapBootAssetsDir, "trusted", "asset-"+dataHash),
  2581  		filepath.Join(dirs.SnapBootAssetsDir, "trusted", "shim-"+shimHash),
  2582  	})
  2583  
  2584  	// boot chains were built
  2585  	c.Check(tab.BootChainKernelPath, DeepEquals, []string{
  2586  		s.ukern1.MountFile(),
  2587  	})
  2588  	// no actual reseal
  2589  	c.Check(resealCalls, Equals, 0)
  2590  }
  2591  
  2592  func (s *bootenv20Suite) TestMarkBootSuccessful20BootAssetsUpdateUnexpectedAsset(c *C) {
  2593  	tab := s.bootloaderWithTrustedAssets(c, []string{"EFI/asset"})
  2594  
  2595  	data := []byte("foobar")
  2596  	// SHA3-384
  2597  	dataHash := "0fa8abfbdaf924ad307b74dd2ed183b9a4a398891a2f6bac8fd2db7041b77f068580f9c6c66f699b496c2da1cbcc7ed8"
  2598  
  2599  	c.Assert(os.MkdirAll(filepath.Join(boot.InitramfsUbuntuBootDir, "EFI"), 0755), IsNil)
  2600  	c.Assert(os.MkdirAll(filepath.Join(boot.InitramfsUbuntuSeedDir, "EFI"), 0755), IsNil)
  2601  	c.Assert(ioutil.WriteFile(filepath.Join(boot.InitramfsUbuntuBootDir, "EFI/asset"), data, 0644), IsNil)
  2602  	c.Assert(ioutil.WriteFile(filepath.Join(boot.InitramfsUbuntuSeedDir, "EFI/asset"), data, 0644), IsNil)
  2603  	// mock some state in the cache
  2604  	mockAssetsCache(c, dirs.GlobalRootDir, "trusted", []string{
  2605  		"asset-one",
  2606  		"asset-two",
  2607  	})
  2608  
  2609  	coreDev := boottest.MockUC20Device("", nil)
  2610  	c.Assert(coreDev.HasModeenv(), Equals, true)
  2611  	model := coreDev.Model()
  2612  
  2613  	// we were trying an update of boot assets
  2614  	m := &boot.Modeenv{
  2615  		Mode:           "run",
  2616  		Base:           s.base1.Filename(),
  2617  		CurrentKernels: []string{s.kern1.Filename()},
  2618  		CurrentTrustedBootAssets: boot.BootAssetsMap{
  2619  			// hash will not match
  2620  			"asset": {"one", "two"},
  2621  		},
  2622  		CurrentTrustedRecoveryBootAssets: boot.BootAssetsMap{
  2623  			"asset": {"one", "two"},
  2624  		},
  2625  		Model:          model.Model(),
  2626  		BrandID:        model.BrandID(),
  2627  		Grade:          string(model.Grade()),
  2628  		ModelSignKeyID: model.SignKeyID(),
  2629  	}
  2630  	r := setupUC20Bootenv(
  2631  		c,
  2632  		tab.MockBootloader,
  2633  		&bootenv20Setup{
  2634  			modeenv:    m,
  2635  			kern:       s.kern1,
  2636  			kernStatus: boot.DefaultStatus,
  2637  		},
  2638  	)
  2639  	defer r()
  2640  
  2641  	// mark successful
  2642  	err := boot.MarkBootSuccessful(coreDev)
  2643  	c.Assert(err, ErrorMatches, fmt.Sprintf(`cannot mark boot successful: cannot mark successful boot assets: system booted with unexpected run mode bootloader asset "EFI/asset" hash %s`, dataHash))
  2644  
  2645  	// check the modeenv
  2646  	m2, err := boot.ReadModeenv("")
  2647  	c.Assert(err, IsNil)
  2648  	// modeenv is unchaged
  2649  	c.Check(m2.CurrentTrustedBootAssets, DeepEquals, m.CurrentTrustedBootAssets)
  2650  	c.Check(m2.CurrentTrustedRecoveryBootAssets, DeepEquals, m.CurrentTrustedRecoveryBootAssets)
  2651  	// nothing was removed from cache
  2652  	checkContentGlob(c, filepath.Join(dirs.SnapBootAssetsDir, "trusted", "*"), []string{
  2653  		filepath.Join(dirs.SnapBootAssetsDir, "trusted", "asset-one"),
  2654  		filepath.Join(dirs.SnapBootAssetsDir, "trusted", "asset-two"),
  2655  	})
  2656  }
  2657  
  2658  func (s *bootenv20Suite) setupMarkBootSuccessful20CommandLine(c *C, model *asserts.Model, mode string, cmdlines boot.BootCommandLines) *boot.Modeenv {
  2659  	// mock some state in the cache
  2660  	mockAssetsCache(c, dirs.GlobalRootDir, "trusted", []string{
  2661  		"asset-one",
  2662  	})
  2663  	// a pending kernel command line change
  2664  	m := &boot.Modeenv{
  2665  		Mode:           mode,
  2666  		Base:           s.base1.Filename(),
  2667  		CurrentKernels: []string{s.kern1.Filename()},
  2668  		CurrentTrustedBootAssets: boot.BootAssetsMap{
  2669  			"asset": {"one"},
  2670  		},
  2671  		CurrentTrustedRecoveryBootAssets: boot.BootAssetsMap{
  2672  			"asset": {"one"},
  2673  		},
  2674  		CurrentKernelCommandLines: cmdlines,
  2675  
  2676  		Model:          model.Model(),
  2677  		BrandID:        model.BrandID(),
  2678  		Grade:          string(model.Grade()),
  2679  		ModelSignKeyID: model.SignKeyID(),
  2680  	}
  2681  	return m
  2682  }
  2683  
  2684  func (s *bootenv20Suite) TestMarkBootSuccessful20CommandLineUpdatedHappy(c *C) {
  2685  	s.mockCmdline(c, "snapd_recovery_mode=run candidate panic=-1")
  2686  	tab := s.bootloaderWithTrustedAssets(c, []string{"asset"})
  2687  	coreDev := boottest.MockUC20Device("", nil)
  2688  	c.Assert(coreDev.HasModeenv(), Equals, true)
  2689  	m := s.setupMarkBootSuccessful20CommandLine(c, coreDev.Model(), "run", boot.BootCommandLines{
  2690  		"snapd_recovery_mode=run panic=-1",
  2691  		"snapd_recovery_mode=run candidate panic=-1",
  2692  	})
  2693  
  2694  	r := setupUC20Bootenv(
  2695  		c,
  2696  		tab.MockBootloader,
  2697  		&bootenv20Setup{
  2698  			modeenv:    m,
  2699  			kern:       s.kern1,
  2700  			kernStatus: boot.DefaultStatus,
  2701  		},
  2702  	)
  2703  	defer r()
  2704  	// mark successful
  2705  	err := boot.MarkBootSuccessful(coreDev)
  2706  	c.Assert(err, IsNil)
  2707  
  2708  	// check the modeenv
  2709  	m2, err := boot.ReadModeenv("")
  2710  	c.Assert(err, IsNil)
  2711  	// modeenv is unchaged
  2712  	c.Check(m2.CurrentKernelCommandLines, DeepEquals, boot.BootCommandLines{
  2713  		"snapd_recovery_mode=run candidate panic=-1",
  2714  	})
  2715  }
  2716  
  2717  func (s *bootenv20Suite) TestMarkBootSuccessful20CommandLineUpdatedOld(c *C) {
  2718  	s.mockCmdline(c, "snapd_recovery_mode=run panic=-1")
  2719  	tab := s.bootloaderWithTrustedAssets(c, []string{"asset"})
  2720  	coreDev := boottest.MockUC20Device("", nil)
  2721  	c.Assert(coreDev.HasModeenv(), Equals, true)
  2722  	m := s.setupMarkBootSuccessful20CommandLine(c, coreDev.Model(), "run", boot.BootCommandLines{
  2723  		"snapd_recovery_mode=run panic=-1",
  2724  		"snapd_recovery_mode=run candidate panic=-1",
  2725  	})
  2726  	r := setupUC20Bootenv(
  2727  		c,
  2728  		tab.MockBootloader,
  2729  		&bootenv20Setup{
  2730  			modeenv:    m,
  2731  			kern:       s.kern1,
  2732  			kernStatus: boot.DefaultStatus,
  2733  		},
  2734  	)
  2735  	defer r()
  2736  
  2737  	// mark successful
  2738  	err := boot.MarkBootSuccessful(coreDev)
  2739  	c.Assert(err, IsNil)
  2740  
  2741  	// check the modeenv
  2742  	m2, err := boot.ReadModeenv("")
  2743  	c.Assert(err, IsNil)
  2744  	// modeenv is unchaged
  2745  	c.Check(m2.CurrentKernelCommandLines, DeepEquals, boot.BootCommandLines{
  2746  		"snapd_recovery_mode=run panic=-1",
  2747  	})
  2748  }
  2749  
  2750  func (s *bootenv20Suite) TestMarkBootSuccessful20CommandLineUpdatedMismatch(c *C) {
  2751  	s.mockCmdline(c, "snapd_recovery_mode=run different")
  2752  	tab := s.bootloaderWithTrustedAssets(c, []string{"asset"})
  2753  	coreDev := boottest.MockUC20Device("", nil)
  2754  	c.Assert(coreDev.HasModeenv(), Equals, true)
  2755  	m := s.setupMarkBootSuccessful20CommandLine(c, coreDev.Model(), "run", boot.BootCommandLines{
  2756  		"snapd_recovery_mode=run",
  2757  		"snapd_recovery_mode=run candidate",
  2758  	})
  2759  	r := setupUC20Bootenv(
  2760  		c,
  2761  		tab.MockBootloader,
  2762  		&bootenv20Setup{
  2763  			modeenv:    m,
  2764  			kern:       s.kern1,
  2765  			kernStatus: boot.DefaultStatus,
  2766  		},
  2767  	)
  2768  	defer r()
  2769  
  2770  	// mark successful
  2771  	err := boot.MarkBootSuccessful(coreDev)
  2772  	c.Assert(err, ErrorMatches, `cannot mark boot successful: cannot mark successful boot command line: current command line content "snapd_recovery_mode=run different" not matching any expected entry`)
  2773  }
  2774  
  2775  func (s *bootenv20Suite) TestMarkBootSuccessful20CommandLineUpdatedFallbackOnBootSuccessful(c *C) {
  2776  	s.mockCmdline(c, "snapd_recovery_mode=run panic=-1")
  2777  	tab := s.bootloaderWithTrustedAssets(c, []string{"asset"})
  2778  	tab.StaticCommandLine = "panic=-1"
  2779  	coreDev := boottest.MockUC20Device("", nil)
  2780  	c.Assert(coreDev.HasModeenv(), Equals, true)
  2781  	m := s.setupMarkBootSuccessful20CommandLine(c, coreDev.Model(), "run", nil)
  2782  	r := setupUC20Bootenv(
  2783  		c,
  2784  		tab.MockBootloader,
  2785  		&bootenv20Setup{
  2786  			modeenv:    m,
  2787  			kern:       s.kern1,
  2788  			kernStatus: boot.DefaultStatus,
  2789  		},
  2790  	)
  2791  	defer r()
  2792  
  2793  	// mark successful
  2794  	err := boot.MarkBootSuccessful(coreDev)
  2795  	c.Assert(err, IsNil)
  2796  
  2797  	// check the modeenv
  2798  	m2, err := boot.ReadModeenv("")
  2799  	c.Assert(err, IsNil)
  2800  	// modeenv is unchaged
  2801  	c.Check(m2.CurrentKernelCommandLines, DeepEquals, boot.BootCommandLines{
  2802  		"snapd_recovery_mode=run panic=-1",
  2803  	})
  2804  }
  2805  
  2806  func (s *bootenv20Suite) TestMarkBootSuccessful20CommandLineUpdatedFallbackOnBootMismatch(c *C) {
  2807  	s.mockCmdline(c, "snapd_recovery_mode=run panic=-1 unexpected")
  2808  	tab := s.bootloaderWithTrustedAssets(c, []string{"asset"})
  2809  	tab.StaticCommandLine = "panic=-1"
  2810  	coreDev := boottest.MockUC20Device("", nil)
  2811  	c.Assert(coreDev.HasModeenv(), Equals, true)
  2812  	m := s.setupMarkBootSuccessful20CommandLine(c, coreDev.Model(), "run", nil)
  2813  	r := setupUC20Bootenv(
  2814  		c,
  2815  		tab.MockBootloader,
  2816  		&bootenv20Setup{
  2817  			modeenv:    m,
  2818  			kern:       s.kern1,
  2819  			kernStatus: boot.DefaultStatus,
  2820  		},
  2821  	)
  2822  	defer r()
  2823  
  2824  	// mark successful
  2825  	err := boot.MarkBootSuccessful(coreDev)
  2826  	c.Assert(err, ErrorMatches, `cannot mark boot successful: cannot mark successful boot command line: unexpected current command line: "snapd_recovery_mode=run panic=-1 unexpected"`)
  2827  }
  2828  
  2829  func (s *bootenv20Suite) TestMarkBootSuccessful20CommandLineNonRunMode(c *C) {
  2830  	// recover mode
  2831  	s.mockCmdline(c, "snapd_recovery_mode=recover snapd_recovery_system=1234 panic=-1")
  2832  	tab := s.bootloaderWithTrustedAssets(c, []string{"asset"})
  2833  	tab.StaticCommandLine = "panic=-1"
  2834  	coreDev := boottest.MockUC20Device("", nil)
  2835  	c.Assert(coreDev.HasModeenv(), Equals, true)
  2836  	// current command line does not match any of the run mode command lines
  2837  	m := s.setupMarkBootSuccessful20CommandLine(c, coreDev.Model(), "recover", boot.BootCommandLines{
  2838  		"snapd_recovery_mode=run panic=-1",
  2839  		"snapd_recovery_mode=run candidate panic=-1",
  2840  	})
  2841  	r := setupUC20Bootenv(
  2842  		c,
  2843  		tab.MockBootloader,
  2844  		&bootenv20Setup{
  2845  			modeenv:    m,
  2846  			kern:       s.kern1,
  2847  			kernStatus: boot.DefaultStatus,
  2848  		},
  2849  	)
  2850  	defer r()
  2851  
  2852  	// mark successful
  2853  	err := boot.MarkBootSuccessful(coreDev)
  2854  	c.Assert(err, IsNil)
  2855  
  2856  	// check the modeenv
  2857  	m2, err := boot.ReadModeenv("")
  2858  	c.Assert(err, IsNil)
  2859  	// modeenv is unchaged
  2860  	c.Check(m2.CurrentKernelCommandLines, DeepEquals, boot.BootCommandLines{
  2861  		"snapd_recovery_mode=run panic=-1",
  2862  		"snapd_recovery_mode=run candidate panic=-1",
  2863  	})
  2864  }
  2865  
  2866  func (s *bootenv20Suite) TestMarkBootSuccessful20CommandLineUpdatedNoFDEManagedBootloader(c *C) {
  2867  	s.mockCmdline(c, "snapd_recovery_mode=run candidate panic=-1")
  2868  	tab := s.bootloaderWithTrustedAssets(c, nil)
  2869  	coreDev := boottest.MockUC20Device("", nil)
  2870  	c.Assert(coreDev.HasModeenv(), Equals, true)
  2871  	m := s.setupMarkBootSuccessful20CommandLine(c, coreDev.Model(), "run", boot.BootCommandLines{
  2872  		"snapd_recovery_mode=run panic=-1",
  2873  		"snapd_recovery_mode=run candidate panic=-1",
  2874  	})
  2875  	// without encryption, the trusted assets are not tracked in the modeenv,
  2876  	// but we still may want to track command lines so that the gadget can
  2877  	// contribute to the system command line
  2878  	m.CurrentTrustedBootAssets = nil
  2879  	m.CurrentTrustedRecoveryBootAssets = nil
  2880  
  2881  	r := setupUC20Bootenv(
  2882  		c,
  2883  		tab.MockBootloader,
  2884  		&bootenv20Setup{
  2885  			modeenv:    m,
  2886  			kern:       s.kern1,
  2887  			kernStatus: boot.DefaultStatus,
  2888  		},
  2889  	)
  2890  	defer r()
  2891  
  2892  	// mark successful
  2893  	err := boot.MarkBootSuccessful(coreDev)
  2894  	c.Assert(err, IsNil)
  2895  
  2896  	// check the modeenv
  2897  	m2, err := boot.ReadModeenv("")
  2898  	c.Assert(err, IsNil)
  2899  	// modeenv is unchaged
  2900  	c.Check(m2.CurrentKernelCommandLines, DeepEquals, boot.BootCommandLines{
  2901  		"snapd_recovery_mode=run candidate panic=-1",
  2902  	})
  2903  }
  2904  
  2905  func (s *bootenv20Suite) TestMarkBootSuccessful20CommandLineCompatNonTrustedBootloader(c *C) {
  2906  	s.mockCmdline(c, "snapd_recovery_mode=run candidate panic=-1")
  2907  	// bootloader has no trusted assets
  2908  	bl := bootloadertest.Mock("not-trusted", "")
  2909  	bootloader.Force(bl)
  2910  	s.AddCleanup(func() { bootloader.Force(nil) })
  2911  	coreDev := boottest.MockUC20Device("", nil)
  2912  	c.Assert(coreDev.HasModeenv(), Equals, true)
  2913  	m := s.setupMarkBootSuccessful20CommandLine(c, coreDev.Model(), "run", nil)
  2914  	// no trusted assets
  2915  	m.CurrentTrustedBootAssets = nil
  2916  	m.CurrentTrustedRecoveryBootAssets = nil
  2917  	// no kernel command lines tracked
  2918  	m.CurrentKernelCommandLines = nil
  2919  
  2920  	r := setupUC20Bootenv(
  2921  		c,
  2922  		bl,
  2923  		&bootenv20Setup{
  2924  			modeenv:    m,
  2925  			kern:       s.kern1,
  2926  			kernStatus: boot.DefaultStatus,
  2927  		},
  2928  	)
  2929  	defer r()
  2930  
  2931  	// mark successful
  2932  	err := boot.MarkBootSuccessful(coreDev)
  2933  	c.Assert(err, IsNil)
  2934  
  2935  	// check the modeenv
  2936  	m2, err := boot.ReadModeenv("")
  2937  	c.Assert(err, IsNil)
  2938  	// modeenv isn't changed
  2939  	c.Check(m2.CurrentKernelCommandLines, HasLen, 0)
  2940  }
  2941  
  2942  func (s *bootenv20Suite) TestMarkBootSuccessful20SystemsCompat(c *C) {
  2943  	b := bootloadertest.Mock("mock", s.bootdir)
  2944  	s.forceBootloader(b)
  2945  
  2946  	m := &boot.Modeenv{
  2947  		Mode:                   "run",
  2948  		Base:                   s.base1.Filename(),
  2949  		CurrentKernels:         []string{s.kern1.Filename()},
  2950  		CurrentRecoverySystems: []string{"1234"},
  2951  	}
  2952  
  2953  	r := setupUC20Bootenv(
  2954  		c,
  2955  		b,
  2956  		&bootenv20Setup{
  2957  			modeenv:    m,
  2958  			kern:       s.kern1,
  2959  			kernStatus: boot.DefaultStatus,
  2960  		},
  2961  	)
  2962  	defer r()
  2963  
  2964  	coreDev := boottest.MockUC20Device("", nil)
  2965  	c.Assert(coreDev.HasModeenv(), Equals, true)
  2966  	// mark successful
  2967  	err := boot.MarkBootSuccessful(coreDev)
  2968  	c.Assert(err, IsNil)
  2969  
  2970  	// check the modeenv
  2971  	m2, err := boot.ReadModeenv("")
  2972  	c.Assert(err, IsNil)
  2973  	// the list of good recovery systems has not been modified
  2974  	c.Check(m2.GoodRecoverySystems, DeepEquals, []string{"1234"})
  2975  	c.Check(m2.CurrentRecoverySystems, DeepEquals, []string{"1234"})
  2976  }
  2977  
  2978  func (s *bootenv20Suite) TestMarkBootSuccessful20SystemsPopulated(c *C) {
  2979  	b := bootloadertest.Mock("mock", s.bootdir)
  2980  	s.forceBootloader(b)
  2981  
  2982  	m := &boot.Modeenv{
  2983  		Mode:                   "run",
  2984  		Base:                   s.base1.Filename(),
  2985  		CurrentKernels:         []string{s.kern1.Filename()},
  2986  		CurrentRecoverySystems: []string{"1234", "9999"},
  2987  		GoodRecoverySystems:    []string{"1234"},
  2988  	}
  2989  
  2990  	r := setupUC20Bootenv(
  2991  		c,
  2992  		b,
  2993  		&bootenv20Setup{
  2994  			modeenv:    m,
  2995  			kern:       s.kern1,
  2996  			kernStatus: boot.DefaultStatus,
  2997  		},
  2998  	)
  2999  	defer r()
  3000  
  3001  	coreDev := boottest.MockUC20Device("", nil)
  3002  	c.Assert(coreDev.HasModeenv(), Equals, true)
  3003  	// mark successful
  3004  	err := boot.MarkBootSuccessful(coreDev)
  3005  	c.Assert(err, IsNil)
  3006  
  3007  	// check the modeenv
  3008  	m2, err := boot.ReadModeenv("")
  3009  	c.Assert(err, IsNil)
  3010  	// good recovery systems has been populated
  3011  	c.Check(m2.GoodRecoverySystems, DeepEquals, []string{"1234"})
  3012  	c.Check(m2.CurrentRecoverySystems, DeepEquals, []string{"1234", "9999"})
  3013  }
  3014  
  3015  func (s *bootenv20Suite) TestMarkBootSuccessful20ModelSignKeyIDPopulated(c *C) {
  3016  	b := bootloadertest.Mock("mock", s.bootdir)
  3017  	s.forceBootloader(b)
  3018  
  3019  	coreDev := boottest.MockUC20Device("", nil)
  3020  	c.Assert(coreDev.HasModeenv(), Equals, true)
  3021  
  3022  	m := &boot.Modeenv{
  3023  		Mode:           "run",
  3024  		Base:           s.base1.Filename(),
  3025  		CurrentKernels: []string{s.kern1.Filename()},
  3026  		Model:          "my-model-uc20",
  3027  		BrandID:        "my-brand",
  3028  		Grade:          "dangerous",
  3029  		// sign key ID is unset
  3030  	}
  3031  
  3032  	r := setupUC20Bootenv(
  3033  		c,
  3034  		b,
  3035  		&bootenv20Setup{
  3036  			modeenv:    m,
  3037  			kern:       s.kern1,
  3038  			kernStatus: boot.DefaultStatus,
  3039  		},
  3040  	)
  3041  	defer r()
  3042  
  3043  	// mark successful
  3044  	err := boot.MarkBootSuccessful(coreDev)
  3045  	c.Assert(err, IsNil)
  3046  
  3047  	// check the modeenv
  3048  	m2, err := boot.ReadModeenv("")
  3049  	c.Assert(err, IsNil)
  3050  	// model's sign key ID has been set
  3051  	c.Check(m2.ModelSignKeyID, Equals, "Jv8_JiHiIzJVcO9M55pPdqSDWUvuhfDIBJUS-3VW7F_idjix7Ffn5qMxB21ZQuij")
  3052  	c.Check(m2.Model, Equals, "my-model-uc20")
  3053  	c.Check(m2.BrandID, Equals, "my-brand")
  3054  	c.Check(m2.Grade, Equals, "dangerous")
  3055  }
  3056  
  3057  type recoveryBootenv20Suite struct {
  3058  	baseBootenvSuite
  3059  
  3060  	bootloader *bootloadertest.MockBootloader
  3061  
  3062  	dev boot.Device
  3063  }
  3064  
  3065  var _ = Suite(&recoveryBootenv20Suite{})
  3066  
  3067  func (s *recoveryBootenv20Suite) SetUpTest(c *C) {
  3068  	s.baseBootenvSuite.SetUpTest(c)
  3069  
  3070  	s.bootloader = bootloadertest.Mock("mock", c.MkDir())
  3071  	s.forceBootloader(s.bootloader)
  3072  
  3073  	s.dev = boottest.MockUC20Device("", nil)
  3074  }
  3075  
  3076  func (s *recoveryBootenv20Suite) TestSetRecoveryBootSystemAndModeHappy(c *C) {
  3077  	err := boot.SetRecoveryBootSystemAndMode(s.dev, "1234", "install")
  3078  	c.Assert(err, IsNil)
  3079  	c.Check(s.bootloader.BootVars, DeepEquals, map[string]string{
  3080  		"snapd_recovery_system": "1234",
  3081  		"snapd_recovery_mode":   "install",
  3082  	})
  3083  }
  3084  
  3085  func (s *recoveryBootenv20Suite) TestSetRecoveryBootSystemAndModeSetErr(c *C) {
  3086  	s.bootloader.SetErr = errors.New("no can do")
  3087  	err := boot.SetRecoveryBootSystemAndMode(s.dev, "1234", "install")
  3088  	c.Assert(err, ErrorMatches, `no can do`)
  3089  }
  3090  
  3091  func (s *recoveryBootenv20Suite) TestSetRecoveryBootSystemAndModeNonUC20(c *C) {
  3092  	non20Dev := boottest.MockDevice("some-snap")
  3093  	err := boot.SetRecoveryBootSystemAndMode(non20Dev, "1234", "install")
  3094  	c.Assert(err, Equals, boot.ErrUnsupportedSystemMode)
  3095  }
  3096  
  3097  func (s *recoveryBootenv20Suite) TestSetRecoveryBootSystemAndModeErrClumsy(c *C) {
  3098  	err := boot.SetRecoveryBootSystemAndMode(s.dev, "", "install")
  3099  	c.Assert(err, ErrorMatches, "internal error: system label is unset")
  3100  	err = boot.SetRecoveryBootSystemAndMode(s.dev, "1234", "")
  3101  	c.Assert(err, ErrorMatches, "internal error: system mode is unset")
  3102  }
  3103  
  3104  func (s *recoveryBootenv20Suite) TestSetRecoveryBootSystemAndModeRealHappy(c *C) {
  3105  	bootloader.Force(nil)
  3106  
  3107  	mockSeedGrubDir := filepath.Join(boot.InitramfsUbuntuSeedDir, "EFI", "ubuntu")
  3108  	err := os.MkdirAll(mockSeedGrubDir, 0755)
  3109  	c.Assert(err, IsNil)
  3110  	err = ioutil.WriteFile(filepath.Join(mockSeedGrubDir, "grub.cfg"), nil, 0644)
  3111  	c.Assert(err, IsNil)
  3112  
  3113  	err = boot.SetRecoveryBootSystemAndMode(s.dev, "1234", "install")
  3114  	c.Assert(err, IsNil)
  3115  
  3116  	bl, err := bootloader.Find(boot.InitramfsUbuntuSeedDir, &bootloader.Options{Role: bootloader.RoleRecovery})
  3117  	c.Assert(err, IsNil)
  3118  
  3119  	blvars, err := bl.GetBootVars("snapd_recovery_mode", "snapd_recovery_system")
  3120  	c.Assert(err, IsNil)
  3121  	c.Check(blvars, DeepEquals, map[string]string{
  3122  		"snapd_recovery_system": "1234",
  3123  		"snapd_recovery_mode":   "install",
  3124  	})
  3125  }
  3126  
  3127  type bootConfigSuite struct {
  3128  	baseBootenvSuite
  3129  
  3130  	bootloader *bootloadertest.MockTrustedAssetsBootloader
  3131  	gadgetSnap string
  3132  }
  3133  
  3134  var _ = Suite(&bootConfigSuite{})
  3135  
  3136  func (s *bootConfigSuite) SetUpTest(c *C) {
  3137  	s.baseBootenvSuite.SetUpTest(c)
  3138  
  3139  	s.bootloader = bootloadertest.Mock("trusted", c.MkDir()).WithTrustedAssets()
  3140  	s.bootloader.StaticCommandLine = "this is mocked panic=-1"
  3141  	s.bootloader.CandidateStaticCommandLine = "mocked candidate panic=-1"
  3142  	s.forceBootloader(s.bootloader)
  3143  
  3144  	s.mockCmdline(c, "snapd_recovery_mode=run this is mocked panic=-1")
  3145  	s.gadgetSnap = snaptest.MakeTestSnapWithFiles(c, gadgetSnapYaml, nil)
  3146  }
  3147  
  3148  func (s *bootConfigSuite) mockCmdline(c *C, cmdline string) {
  3149  	c.Assert(ioutil.WriteFile(s.cmdlineFile, []byte(cmdline), 0644), IsNil)
  3150  }
  3151  
  3152  func (s *bootConfigSuite) TestBootConfigUpdateHappyNoKeysNoReseal(c *C) {
  3153  	coreDev := boottest.MockUC20Device("", nil)
  3154  	c.Assert(coreDev.HasModeenv(), Equals, true)
  3155  
  3156  	m := &boot.Modeenv{
  3157  		Mode: "run",
  3158  		CurrentKernelCommandLines: boot.BootCommandLines{
  3159  			"snapd_recovery_mode=run this is mocked panic=-1",
  3160  		},
  3161  	}
  3162  	c.Assert(m.WriteTo(""), IsNil)
  3163  
  3164  	resealCalls := 0
  3165  	restore := boot.MockSecbootResealKeys(func(params *secboot.ResealKeysParams) error {
  3166  		resealCalls++
  3167  		return nil
  3168  	})
  3169  	defer restore()
  3170  
  3171  	updated, err := boot.UpdateManagedBootConfigs(coreDev, s.gadgetSnap)
  3172  	c.Assert(err, IsNil)
  3173  	c.Check(updated, Equals, false)
  3174  	c.Check(s.bootloader.UpdateCalls, Equals, 1)
  3175  	c.Check(resealCalls, Equals, 0)
  3176  }
  3177  
  3178  func (s *bootConfigSuite) TestBootConfigUpdateHappyWithReseal(c *C) {
  3179  	s.stampSealedKeys(c, dirs.GlobalRootDir)
  3180  
  3181  	coreDev := boottest.MockUC20Device("", nil)
  3182  	c.Assert(coreDev.HasModeenv(), Equals, true)
  3183  
  3184  	runKernelBf := bootloader.NewBootFile("/var/lib/snapd/snap/pc-kernel_600.snap", "kernel.efi", bootloader.RoleRunMode)
  3185  	recoveryKernelBf := bootloader.NewBootFile("/var/lib/snapd/seed/snaps/pc-kernel_1.snap", "kernel.efi", bootloader.RoleRecovery)
  3186  	mockAssetsCache(c, dirs.GlobalRootDir, "trusted", []string{
  3187  		"asset-hash-1",
  3188  	})
  3189  
  3190  	s.bootloader.TrustedAssetsList = []string{"asset"}
  3191  	s.bootloader.BootChainList = []bootloader.BootFile{
  3192  		bootloader.NewBootFile("", "asset", bootloader.RoleRunMode),
  3193  		runKernelBf,
  3194  	}
  3195  	s.bootloader.RecoveryBootChainList = []bootloader.BootFile{
  3196  		bootloader.NewBootFile("", "asset", bootloader.RoleRecovery),
  3197  		recoveryKernelBf,
  3198  	}
  3199  	m := &boot.Modeenv{
  3200  		Mode:           "run",
  3201  		CurrentKernels: []string{"pc-kernel_500.snap"},
  3202  		CurrentKernelCommandLines: boot.BootCommandLines{
  3203  			"snapd_recovery_mode=run this is mocked panic=-1",
  3204  		},
  3205  		CurrentTrustedRecoveryBootAssets: boot.BootAssetsMap{
  3206  			"asset": []string{"hash-1"},
  3207  		},
  3208  		CurrentTrustedBootAssets: boot.BootAssetsMap{
  3209  			"asset": []string{"hash-1"},
  3210  		},
  3211  	}
  3212  	c.Assert(m.WriteTo(""), IsNil)
  3213  
  3214  	resealCalls := 0
  3215  	restore := boot.MockSecbootResealKeys(func(params *secboot.ResealKeysParams) error {
  3216  		resealCalls++
  3217  		c.Assert(params, NotNil)
  3218  		c.Assert(params.ModelParams, HasLen, 1)
  3219  		c.Check(params.ModelParams[0].KernelCmdlines, DeepEquals, []string{
  3220  			"snapd_recovery_mode=run mocked candidate panic=-1",
  3221  			"snapd_recovery_mode=run this is mocked panic=-1",
  3222  		})
  3223  		return nil
  3224  	})
  3225  	defer restore()
  3226  
  3227  	updated, err := boot.UpdateManagedBootConfigs(coreDev, s.gadgetSnap)
  3228  	c.Assert(err, IsNil)
  3229  	c.Check(updated, Equals, false)
  3230  	c.Check(s.bootloader.UpdateCalls, Equals, 1)
  3231  	c.Check(resealCalls, Equals, 1)
  3232  
  3233  	m2, err := boot.ReadModeenv("")
  3234  	c.Assert(err, IsNil)
  3235  	c.Assert(m2.CurrentKernelCommandLines, DeepEquals, boot.BootCommandLines{
  3236  		"snapd_recovery_mode=run this is mocked panic=-1",
  3237  		"snapd_recovery_mode=run mocked candidate panic=-1",
  3238  	})
  3239  }
  3240  
  3241  func (s *bootConfigSuite) TestBootConfigUpdateHappyNoChange(c *C) {
  3242  	s.stampSealedKeys(c, dirs.GlobalRootDir)
  3243  
  3244  	coreDev := boottest.MockUC20Device("", nil)
  3245  	c.Assert(coreDev.HasModeenv(), Equals, true)
  3246  
  3247  	s.bootloader.StaticCommandLine = "mocked unchanged panic=-1"
  3248  	s.bootloader.CandidateStaticCommandLine = "mocked unchanged panic=-1"
  3249  	s.mockCmdline(c, "snapd_recovery_mode=run mocked unchanged panic=-1")
  3250  
  3251  	m := &boot.Modeenv{
  3252  		Mode: "run",
  3253  		CurrentKernelCommandLines: boot.BootCommandLines{
  3254  			"snapd_recovery_mode=run mocked unchanged panic=-1",
  3255  		},
  3256  	}
  3257  	c.Assert(m.WriteTo(""), IsNil)
  3258  
  3259  	resealCalls := 0
  3260  	restore := boot.MockSecbootResealKeys(func(params *secboot.ResealKeysParams) error {
  3261  		resealCalls++
  3262  		return nil
  3263  	})
  3264  	defer restore()
  3265  
  3266  	updated, err := boot.UpdateManagedBootConfigs(coreDev, s.gadgetSnap)
  3267  	c.Assert(err, IsNil)
  3268  	c.Check(updated, Equals, false)
  3269  	c.Check(s.bootloader.UpdateCalls, Equals, 1)
  3270  	c.Check(resealCalls, Equals, 0)
  3271  
  3272  	m2, err := boot.ReadModeenv("")
  3273  	c.Assert(err, IsNil)
  3274  	c.Assert(m2.CurrentKernelCommandLines, HasLen, 1)
  3275  }
  3276  
  3277  func (s *bootConfigSuite) TestBootConfigUpdateNonUC20DoesNothing(c *C) {
  3278  	nonUC20coreDev := boottest.MockDevice("pc-kernel")
  3279  	c.Assert(nonUC20coreDev.HasModeenv(), Equals, false)
  3280  	updated, err := boot.UpdateManagedBootConfigs(nonUC20coreDev, s.gadgetSnap)
  3281  	c.Assert(err, IsNil)
  3282  	c.Check(updated, Equals, false)
  3283  	c.Check(s.bootloader.UpdateCalls, Equals, 0)
  3284  }
  3285  
  3286  func (s *bootConfigSuite) TestBootConfigUpdateBadModeErr(c *C) {
  3287  	uc20Dev := boottest.MockUC20Device("recover", nil)
  3288  	c.Assert(uc20Dev.HasModeenv(), Equals, true)
  3289  	updated, err := boot.UpdateManagedBootConfigs(uc20Dev, s.gadgetSnap)
  3290  	c.Assert(err, ErrorMatches, "internal error: boot config can only be updated in run mode")
  3291  	c.Check(updated, Equals, false)
  3292  	c.Check(s.bootloader.UpdateCalls, Equals, 0)
  3293  }
  3294  
  3295  func (s *bootConfigSuite) TestBootConfigUpdateFailErr(c *C) {
  3296  	coreDev := boottest.MockUC20Device("", nil)
  3297  	c.Assert(coreDev.HasModeenv(), Equals, true)
  3298  
  3299  	m := &boot.Modeenv{
  3300  		Mode: "run",
  3301  		CurrentKernelCommandLines: boot.BootCommandLines{
  3302  			"snapd_recovery_mode=run this is mocked panic=-1",
  3303  		},
  3304  	}
  3305  	c.Assert(m.WriteTo(""), IsNil)
  3306  
  3307  	s.bootloader.UpdateErr = errors.New("update fail")
  3308  
  3309  	updated, err := boot.UpdateManagedBootConfigs(coreDev, s.gadgetSnap)
  3310  	c.Assert(err, ErrorMatches, "update fail")
  3311  	c.Check(updated, Equals, false)
  3312  	c.Check(s.bootloader.UpdateCalls, Equals, 1)
  3313  }
  3314  
  3315  func (s *bootConfigSuite) TestBootConfigUpdateCmdlineMismatchErr(c *C) {
  3316  	coreDev := boottest.MockUC20Device("", nil)
  3317  	c.Assert(coreDev.HasModeenv(), Equals, true)
  3318  
  3319  	m := &boot.Modeenv{
  3320  		Mode: "run",
  3321  	}
  3322  	c.Assert(m.WriteTo(""), IsNil)
  3323  
  3324  	s.mockCmdline(c, "snapd_recovery_mode=run unexpected cmdline")
  3325  
  3326  	updated, err := boot.UpdateManagedBootConfigs(coreDev, s.gadgetSnap)
  3327  	c.Assert(err, ErrorMatches, `internal error: current kernel command lines is unset`)
  3328  	c.Check(updated, Equals, false)
  3329  	c.Check(s.bootloader.UpdateCalls, Equals, 0)
  3330  }
  3331  
  3332  func (s *bootConfigSuite) TestBootConfigUpdateNotManagedErr(c *C) {
  3333  	coreDev := boottest.MockUC20Device("", nil)
  3334  	c.Assert(coreDev.HasModeenv(), Equals, true)
  3335  
  3336  	bl := bootloadertest.Mock("not-managed", c.MkDir())
  3337  	bootloader.Force(bl)
  3338  	defer bootloader.Force(nil)
  3339  
  3340  	m := &boot.Modeenv{
  3341  		Mode: "run",
  3342  	}
  3343  	c.Assert(m.WriteTo(""), IsNil)
  3344  
  3345  	updated, err := boot.UpdateManagedBootConfigs(coreDev, s.gadgetSnap)
  3346  	c.Assert(err, IsNil)
  3347  	c.Check(updated, Equals, false)
  3348  	c.Check(s.bootloader.UpdateCalls, Equals, 0)
  3349  }
  3350  
  3351  func (s *bootConfigSuite) TestBootConfigUpdateBootloaderFindErr(c *C) {
  3352  	coreDev := boottest.MockUC20Device("", nil)
  3353  	c.Assert(coreDev.HasModeenv(), Equals, true)
  3354  
  3355  	bootloader.ForceError(errors.New("mocked find error"))
  3356  	defer bootloader.ForceError(nil)
  3357  
  3358  	m := &boot.Modeenv{
  3359  		Mode: "run",
  3360  	}
  3361  	c.Assert(m.WriteTo(""), IsNil)
  3362  
  3363  	updated, err := boot.UpdateManagedBootConfigs(coreDev, s.gadgetSnap)
  3364  	c.Assert(err, ErrorMatches, "internal error: cannot find trusted assets bootloader under .*: mocked find error")
  3365  	c.Check(updated, Equals, false)
  3366  	c.Check(s.bootloader.UpdateCalls, Equals, 0)
  3367  }
  3368  
  3369  func (s *bootConfigSuite) TestBootConfigUpdateWithGadgetAndReseal(c *C) {
  3370  	s.stampSealedKeys(c, dirs.GlobalRootDir)
  3371  
  3372  	gadgetSnap := snaptest.MakeTestSnapWithFiles(c, gadgetSnapYaml, [][]string{
  3373  		{"cmdline.extra", "foo bar baz"},
  3374  	})
  3375  	coreDev := boottest.MockUC20Device("", nil)
  3376  	c.Assert(coreDev.HasModeenv(), Equals, true)
  3377  
  3378  	runKernelBf := bootloader.NewBootFile("/var/lib/snapd/snap/pc-kernel_600.snap", "kernel.efi", bootloader.RoleRunMode)
  3379  	recoveryKernelBf := bootloader.NewBootFile("/var/lib/snapd/seed/snaps/pc-kernel_1.snap", "kernel.efi", bootloader.RoleRecovery)
  3380  	mockAssetsCache(c, dirs.GlobalRootDir, "trusted", []string{
  3381  		"asset-hash-1",
  3382  	})
  3383  
  3384  	s.bootloader.TrustedAssetsList = []string{"asset"}
  3385  	s.bootloader.BootChainList = []bootloader.BootFile{
  3386  		bootloader.NewBootFile("", "asset", bootloader.RoleRunMode),
  3387  		runKernelBf,
  3388  	}
  3389  	s.bootloader.RecoveryBootChainList = []bootloader.BootFile{
  3390  		bootloader.NewBootFile("", "asset", bootloader.RoleRecovery),
  3391  		recoveryKernelBf,
  3392  	}
  3393  	m := &boot.Modeenv{
  3394  		Mode:           "run",
  3395  		CurrentKernels: []string{"pc-kernel_500.snap"},
  3396  		CurrentKernelCommandLines: boot.BootCommandLines{
  3397  			// the extra arguments would be included in the current
  3398  			// command line already
  3399  			"snapd_recovery_mode=run this is mocked panic=-1 foo bar baz",
  3400  		},
  3401  		CurrentTrustedRecoveryBootAssets: boot.BootAssetsMap{
  3402  			"asset": []string{"hash-1"},
  3403  		},
  3404  		CurrentTrustedBootAssets: boot.BootAssetsMap{
  3405  			"asset": []string{"hash-1"},
  3406  		},
  3407  	}
  3408  	c.Assert(m.WriteTo(""), IsNil)
  3409  
  3410  	resealCalls := 0
  3411  	restore := boot.MockSecbootResealKeys(func(params *secboot.ResealKeysParams) error {
  3412  		resealCalls++
  3413  		c.Assert(params, NotNil)
  3414  		c.Assert(params.ModelParams, HasLen, 1)
  3415  		c.Check(params.ModelParams[0].KernelCmdlines, DeepEquals, []string{
  3416  			"snapd_recovery_mode=run mocked candidate panic=-1 foo bar baz",
  3417  			"snapd_recovery_mode=run this is mocked panic=-1 foo bar baz",
  3418  		})
  3419  		return nil
  3420  	})
  3421  	defer restore()
  3422  
  3423  	updated, err := boot.UpdateManagedBootConfigs(coreDev, gadgetSnap)
  3424  	c.Assert(err, IsNil)
  3425  	c.Check(updated, Equals, false)
  3426  	c.Check(s.bootloader.UpdateCalls, Equals, 1)
  3427  	c.Check(resealCalls, Equals, 1)
  3428  
  3429  	m2, err := boot.ReadModeenv("")
  3430  	c.Assert(err, IsNil)
  3431  	c.Assert(m2.CurrentKernelCommandLines, DeepEquals, boot.BootCommandLines{
  3432  		"snapd_recovery_mode=run this is mocked panic=-1 foo bar baz",
  3433  		"snapd_recovery_mode=run mocked candidate panic=-1 foo bar baz",
  3434  	})
  3435  }
  3436  
  3437  func (s *bootConfigSuite) TestBootConfigUpdateWithGadgetFullAndReseal(c *C) {
  3438  	s.stampSealedKeys(c, dirs.GlobalRootDir)
  3439  
  3440  	gadgetSnap := snaptest.MakeTestSnapWithFiles(c, gadgetSnapYaml, [][]string{
  3441  		{"cmdline.full", "foo bar baz"},
  3442  	})
  3443  	coreDev := boottest.MockUC20Device("", nil)
  3444  	c.Assert(coreDev.HasModeenv(), Equals, true)
  3445  
  3446  	// a minimal bootloader and modeenv setup that works because reseal is
  3447  	// not executed
  3448  	s.bootloader.TrustedAssetsList = []string{"asset"}
  3449  	m := &boot.Modeenv{
  3450  		Mode: "run",
  3451  		CurrentKernelCommandLines: boot.BootCommandLines{
  3452  			// the full arguments would be included in the current
  3453  			// command line already
  3454  			"snapd_recovery_mode=run foo bar baz",
  3455  		},
  3456  	}
  3457  	c.Assert(m.WriteTo(""), IsNil)
  3458  
  3459  	s.bootloader.Updated = true
  3460  
  3461  	resealCalls := 0
  3462  	// reseal does not happen, because the gadget overrides the static
  3463  	// command line which is part of boot config, thus there's no resulting
  3464  	// change in the command lines tracked in modeenv and no need to reseal
  3465  	restore := boot.MockSecbootResealKeys(func(params *secboot.ResealKeysParams) error {
  3466  		resealCalls++
  3467  		return fmt.Errorf("unexpected call")
  3468  	})
  3469  	defer restore()
  3470  
  3471  	updated, err := boot.UpdateManagedBootConfigs(coreDev, gadgetSnap)
  3472  	c.Assert(err, IsNil)
  3473  	c.Check(updated, Equals, true)
  3474  	c.Check(s.bootloader.UpdateCalls, Equals, 1)
  3475  	c.Check(resealCalls, Equals, 0)
  3476  
  3477  	m2, err := boot.ReadModeenv("")
  3478  	c.Assert(err, IsNil)
  3479  	c.Assert(m2.CurrentKernelCommandLines, DeepEquals, boot.BootCommandLines{
  3480  		"snapd_recovery_mode=run foo bar baz",
  3481  	})
  3482  }
  3483  
  3484  type bootKernelCommandLineSuite struct {
  3485  	baseBootenvSuite
  3486  
  3487  	bootloader            *bootloadertest.MockTrustedAssetsBootloader
  3488  	gadgetSnap            string
  3489  	uc20dev               boot.Device
  3490  	recoveryKernelBf      bootloader.BootFile
  3491  	runKernelBf           bootloader.BootFile
  3492  	modeenvWithEncryption *boot.Modeenv
  3493  	resealCalls           int
  3494  	resealCommandLines    [][]string
  3495  }
  3496  
  3497  var _ = Suite(&bootKernelCommandLineSuite{})
  3498  
  3499  func (s *bootKernelCommandLineSuite) SetUpTest(c *C) {
  3500  	s.baseBootenvSuite.SetUpTest(c)
  3501  
  3502  	data := []byte("foobar")
  3503  	// SHA3-384
  3504  	dataHash := "0fa8abfbdaf924ad307b74dd2ed183b9a4a398891a2f6bac8fd2db7041b77f068580f9c6c66f699b496c2da1cbcc7ed8"
  3505  	c.Assert(os.MkdirAll(filepath.Join(boot.InitramfsUbuntuBootDir), 0755), IsNil)
  3506  	c.Assert(os.MkdirAll(filepath.Join(boot.InitramfsUbuntuSeedDir), 0755), IsNil)
  3507  	c.Assert(ioutil.WriteFile(filepath.Join(boot.InitramfsUbuntuBootDir, "asset"), data, 0644), IsNil)
  3508  	c.Assert(ioutil.WriteFile(filepath.Join(boot.InitramfsUbuntuSeedDir, "asset"), data, 0644), IsNil)
  3509  
  3510  	s.bootloader = bootloadertest.Mock("trusted", c.MkDir()).WithTrustedAssets()
  3511  	s.bootloader.TrustedAssetsList = []string{"asset"}
  3512  	s.bootloader.StaticCommandLine = "static mocked panic=-1"
  3513  	s.bootloader.CandidateStaticCommandLine = "mocked candidate panic=-1"
  3514  	s.forceBootloader(s.bootloader)
  3515  
  3516  	s.mockCmdline(c, "snapd_recovery_mode=run this is mocked panic=-1")
  3517  	s.gadgetSnap = snaptest.MakeTestSnapWithFiles(c, gadgetSnapYaml, nil)
  3518  	s.uc20dev = boottest.MockUC20Device("", boottest.MakeMockUC20Model(nil))
  3519  	s.runKernelBf = bootloader.NewBootFile("/var/lib/snapd/snap/pc-kernel_600.snap", "kernel.efi", bootloader.RoleRunMode)
  3520  	s.recoveryKernelBf = bootloader.NewBootFile("/var/lib/snapd/seed/snaps/pc-kernel_1.snap", "kernel.efi", bootloader.RoleRecovery)
  3521  	mockAssetsCache(c, dirs.GlobalRootDir, "trusted", []string{
  3522  		"asset-" + dataHash,
  3523  	})
  3524  
  3525  	s.bootloader.BootChainList = []bootloader.BootFile{
  3526  		bootloader.NewBootFile("", "asset", bootloader.RoleRunMode),
  3527  		s.runKernelBf,
  3528  	}
  3529  	s.bootloader.RecoveryBootChainList = []bootloader.BootFile{
  3530  		bootloader.NewBootFile("", "asset", bootloader.RoleRecovery),
  3531  		s.recoveryKernelBf,
  3532  	}
  3533  	s.modeenvWithEncryption = &boot.Modeenv{
  3534  		Mode:           "run",
  3535  		CurrentKernels: []string{"pc-kernel_500.snap"},
  3536  		Base:           "core20_1.snap",
  3537  		BaseStatus:     boot.DefaultStatus,
  3538  		CurrentKernelCommandLines: boot.BootCommandLines{
  3539  			// the extra arguments would be included in the current
  3540  			// command line already
  3541  			"snapd_recovery_mode=run static mocked panic=-1",
  3542  		},
  3543  		CurrentTrustedRecoveryBootAssets: boot.BootAssetsMap{
  3544  			"asset": []string{dataHash},
  3545  		},
  3546  		CurrentTrustedBootAssets: boot.BootAssetsMap{
  3547  			"asset": []string{dataHash},
  3548  		},
  3549  	}
  3550  	s.bootloader.SetBootVars(map[string]string{
  3551  		"snap_kernel": "pc-kernel_500.snap",
  3552  	})
  3553  	s.bootloader.SetBootVarsCalls = 0
  3554  
  3555  	s.resealCommandLines = nil
  3556  	s.resealCalls = 0
  3557  	restore := boot.MockSecbootResealKeys(func(params *secboot.ResealKeysParams) error {
  3558  		s.resealCalls++
  3559  		c.Assert(params, NotNil)
  3560  		c.Assert(params.ModelParams, HasLen, 1)
  3561  		s.resealCommandLines = append(s.resealCommandLines, params.ModelParams[0].KernelCmdlines)
  3562  		return nil
  3563  	})
  3564  	s.AddCleanup(restore)
  3565  }
  3566  
  3567  func (s *bootKernelCommandLineSuite) TestCommandLineUpdateNonUC20(c *C) {
  3568  	nonUC20dev := boottest.MockDevice("")
  3569  
  3570  	// gadget which would otherwise trigger an update
  3571  	sf := snaptest.MakeTestSnapWithFiles(c, gadgetSnapYaml, [][]string{
  3572  		{"cmdline.extra", "foo"},
  3573  	})
  3574  
  3575  	reboot, err := boot.UpdateCommandLineForGadgetComponent(nonUC20dev, sf)
  3576  	c.Assert(err, ErrorMatches, "internal error: command line component cannot be updated on non UC20 devices")
  3577  	c.Assert(reboot, Equals, false)
  3578  }
  3579  
  3580  func (s *bootKernelCommandLineSuite) TestCommandLineUpdateUC20NotManagedBootloader(c *C) {
  3581  	// gadget which would otherwise trigger an update
  3582  	sf := snaptest.MakeTestSnapWithFiles(c, gadgetSnapYaml, [][]string{
  3583  		{"cmdline.extra", "foo"},
  3584  	})
  3585  
  3586  	// but the bootloader is not managed by snapd
  3587  	bl := bootloadertest.Mock("not-managed", c.MkDir())
  3588  	bl.SetErr = fmt.Errorf("unexpected call")
  3589  	s.forceBootloader(bl)
  3590  
  3591  	reboot, err := boot.UpdateCommandLineForGadgetComponent(s.uc20dev, sf)
  3592  	c.Assert(err, IsNil)
  3593  	c.Assert(reboot, Equals, false)
  3594  	c.Check(bl.SetBootVarsCalls, Equals, 0)
  3595  }
  3596  
  3597  func (s *bootKernelCommandLineSuite) TestCommandLineUpdateUC20ArgsAdded(c *C) {
  3598  	s.stampSealedKeys(c, dirs.GlobalRootDir)
  3599  
  3600  	sf := snaptest.MakeTestSnapWithFiles(c, gadgetSnapYaml, [][]string{
  3601  		{"cmdline.extra", "args from gadget"},
  3602  	})
  3603  
  3604  	s.modeenvWithEncryption.CurrentKernelCommandLines = []string{"snapd_recovery_mode=run static mocked panic=-1"}
  3605  	c.Assert(s.modeenvWithEncryption.WriteTo(""), IsNil)
  3606  
  3607  	reboot, err := boot.UpdateCommandLineForGadgetComponent(s.uc20dev, sf)
  3608  	c.Assert(err, IsNil)
  3609  	c.Assert(reboot, Equals, true)
  3610  
  3611  	// reseal happened
  3612  	c.Check(s.resealCalls, Equals, 1)
  3613  	c.Check(s.resealCommandLines, DeepEquals, [][]string{{
  3614  		"snapd_recovery_mode=run static mocked panic=-1",
  3615  		"snapd_recovery_mode=run static mocked panic=-1 args from gadget",
  3616  	}})
  3617  
  3618  	// modeenv has been updated
  3619  	newM, err := boot.ReadModeenv("")
  3620  	c.Assert(err, IsNil)
  3621  	c.Check(newM.CurrentKernelCommandLines, DeepEquals, boot.BootCommandLines{
  3622  		"snapd_recovery_mode=run static mocked panic=-1",
  3623  		"snapd_recovery_mode=run static mocked panic=-1 args from gadget",
  3624  	})
  3625  
  3626  	// bootloader variables too
  3627  	c.Check(s.bootloader.SetBootVarsCalls, Equals, 1)
  3628  	args, err := s.bootloader.GetBootVars("snapd_extra_cmdline_args", "snapd_full_cmdline_args")
  3629  	c.Assert(err, IsNil)
  3630  	c.Check(args, DeepEquals, map[string]string{
  3631  		"snapd_extra_cmdline_args": "args from gadget",
  3632  		"snapd_full_cmdline_args":  "",
  3633  	})
  3634  }
  3635  
  3636  func (s *bootKernelCommandLineSuite) TestCommandLineUpdateUC20ArgsSwitch(c *C) {
  3637  	s.stampSealedKeys(c, dirs.GlobalRootDir)
  3638  
  3639  	sf := snaptest.MakeTestSnapWithFiles(c, gadgetSnapYaml, [][]string{
  3640  		{"cmdline.extra", "no change"},
  3641  	})
  3642  
  3643  	s.modeenvWithEncryption.CurrentKernelCommandLines = []string{"snapd_recovery_mode=run static mocked panic=-1 no change"}
  3644  	c.Assert(s.modeenvWithEncryption.WriteTo(""), IsNil)
  3645  	err := s.bootloader.SetBootVars(map[string]string{
  3646  		"snapd_extra_cmdline_args": "no change",
  3647  		// this is intentionally filled and will be cleared
  3648  		"snapd_full_cmdline_args": "canary",
  3649  	})
  3650  	c.Assert(err, IsNil)
  3651  	s.bootloader.SetBootVarsCalls = 0
  3652  
  3653  	reboot, err := boot.UpdateCommandLineForGadgetComponent(s.uc20dev, sf)
  3654  	c.Assert(err, IsNil)
  3655  	c.Assert(reboot, Equals, false)
  3656  
  3657  	// no reseal needed
  3658  	c.Check(s.resealCalls, Equals, 0)
  3659  
  3660  	newM, err := boot.ReadModeenv("")
  3661  	c.Assert(err, IsNil)
  3662  	c.Check(newM.CurrentKernelCommandLines, DeepEquals, boot.BootCommandLines{
  3663  		"snapd_recovery_mode=run static mocked panic=-1 no change",
  3664  	})
  3665  	c.Check(s.bootloader.SetBootVarsCalls, Equals, 0)
  3666  	args, err := s.bootloader.GetBootVars("snapd_extra_cmdline_args", "snapd_full_cmdline_args")
  3667  	c.Assert(err, IsNil)
  3668  	c.Check(args, DeepEquals, map[string]string{
  3669  		"snapd_extra_cmdline_args": "no change",
  3670  		// canary is still present, as nothing was modified
  3671  		"snapd_full_cmdline_args": "canary",
  3672  	})
  3673  
  3674  	// let's change them now
  3675  	sfChanged := snaptest.MakeTestSnapWithFiles(c, gadgetSnapYaml, [][]string{
  3676  		{"cmdline.extra", "changed"},
  3677  	})
  3678  
  3679  	reboot, err = boot.UpdateCommandLineForGadgetComponent(s.uc20dev, sfChanged)
  3680  	c.Assert(err, IsNil)
  3681  	c.Assert(reboot, Equals, true)
  3682  
  3683  	// reseal was applied
  3684  	c.Check(s.resealCalls, Equals, 1)
  3685  	c.Check(s.resealCommandLines, DeepEquals, [][]string{{
  3686  		// those come from boot chains which use predictable sorting
  3687  		"snapd_recovery_mode=run static mocked panic=-1 changed",
  3688  		"snapd_recovery_mode=run static mocked panic=-1 no change",
  3689  	}})
  3690  
  3691  	// modeenv has been updated
  3692  	newM, err = boot.ReadModeenv("")
  3693  	c.Assert(err, IsNil)
  3694  	c.Check(newM.CurrentKernelCommandLines, DeepEquals, boot.BootCommandLines{
  3695  		"snapd_recovery_mode=run static mocked panic=-1 no change",
  3696  		// new ones are appended
  3697  		"snapd_recovery_mode=run static mocked panic=-1 changed",
  3698  	})
  3699  	// and bootloader env too
  3700  	args, err = s.bootloader.GetBootVars("snapd_extra_cmdline_args", "snapd_full_cmdline_args")
  3701  	c.Assert(err, IsNil)
  3702  	c.Check(args, DeepEquals, map[string]string{
  3703  		"snapd_extra_cmdline_args": "changed",
  3704  		// canary has been cleared as bootenv was modified
  3705  		"snapd_full_cmdline_args": "",
  3706  	})
  3707  }
  3708  
  3709  func (s *bootKernelCommandLineSuite) TestCommandLineUpdateUC20UnencryptedArgsRemoved(c *C) {
  3710  	s.stampSealedKeys(c, dirs.GlobalRootDir)
  3711  
  3712  	// pretend we used to have additional arguments from the gadget, but
  3713  	// those will be gone with new update
  3714  	sf := snaptest.MakeTestSnapWithFiles(c, gadgetSnapYaml, nil)
  3715  
  3716  	s.modeenvWithEncryption.CurrentKernelCommandLines = []string{"snapd_recovery_mode=run static mocked panic=-1 from-gadget"}
  3717  	c.Assert(s.modeenvWithEncryption.WriteTo(""), IsNil)
  3718  	err := s.bootloader.SetBootVars(map[string]string{
  3719  		"snapd_extra_cmdline_args": "from-gadget",
  3720  		// this is intentionally filled and will be cleared
  3721  		"snapd_full_cmdline_args": "canary",
  3722  	})
  3723  	c.Assert(err, IsNil)
  3724  	s.bootloader.SetBootVarsCalls = 0
  3725  
  3726  	reboot, err := boot.UpdateCommandLineForGadgetComponent(s.uc20dev, sf)
  3727  	c.Assert(err, IsNil)
  3728  	c.Assert(reboot, Equals, true)
  3729  
  3730  	c.Check(s.resealCalls, Equals, 1)
  3731  	c.Check(s.resealCommandLines, DeepEquals, [][]string{{
  3732  		"snapd_recovery_mode=run static mocked panic=-1",
  3733  		"snapd_recovery_mode=run static mocked panic=-1 from-gadget",
  3734  	}})
  3735  
  3736  	newM, err := boot.ReadModeenv("")
  3737  	c.Assert(err, IsNil)
  3738  	c.Check(newM.CurrentKernelCommandLines, DeepEquals, boot.BootCommandLines{
  3739  		"snapd_recovery_mode=run static mocked panic=-1 from-gadget",
  3740  		"snapd_recovery_mode=run static mocked panic=-1",
  3741  	})
  3742  	// bootloader variables were explicitly cleared
  3743  	c.Check(s.bootloader.SetBootVarsCalls, Equals, 1)
  3744  	args, err := s.bootloader.GetBootVars("snapd_extra_cmdline_args", "snapd_full_cmdline_args")
  3745  	c.Assert(err, IsNil)
  3746  	c.Check(args, DeepEquals, map[string]string{
  3747  		"snapd_extra_cmdline_args": "",
  3748  		"snapd_full_cmdline_args":  "",
  3749  	})
  3750  }
  3751  
  3752  func (s *bootKernelCommandLineSuite) TestCommandLineUpdateUC20SetError(c *C) {
  3753  	s.stampSealedKeys(c, dirs.GlobalRootDir)
  3754  
  3755  	// pretend we used to have additional arguments from the gadget, but
  3756  	// those will be gone with new update
  3757  	sf := snaptest.MakeTestSnapWithFiles(c, gadgetSnapYaml, [][]string{
  3758  		{"cmdline.extra", "this-is-not-applied"},
  3759  	})
  3760  
  3761  	s.modeenvWithEncryption.CurrentKernelCommandLines = []string{"snapd_recovery_mode=run static mocked panic=-1"}
  3762  	c.Assert(s.modeenvWithEncryption.WriteTo(""), IsNil)
  3763  
  3764  	s.bootloader.SetErr = fmt.Errorf("set fails")
  3765  
  3766  	reboot, err := boot.UpdateCommandLineForGadgetComponent(s.uc20dev, sf)
  3767  	c.Assert(err, ErrorMatches, "cannot set run system kernel command line arguments: set fails")
  3768  	c.Assert(reboot, Equals, false)
  3769  	// set boot vars was called and failed
  3770  	c.Check(s.bootloader.SetBootVarsCalls, Equals, 1)
  3771  
  3772  	// reseal with new parameters happened though
  3773  	c.Check(s.resealCalls, Equals, 1)
  3774  	c.Check(s.resealCommandLines, DeepEquals, [][]string{{
  3775  		"snapd_recovery_mode=run static mocked panic=-1",
  3776  		"snapd_recovery_mode=run static mocked panic=-1 this-is-not-applied",
  3777  	}})
  3778  
  3779  	newM, err := boot.ReadModeenv("")
  3780  	c.Assert(err, IsNil)
  3781  	c.Check(newM.CurrentKernelCommandLines, DeepEquals, boot.BootCommandLines{
  3782  		"snapd_recovery_mode=run static mocked panic=-1",
  3783  		// this will be cleared on next reboot or will get overwritten
  3784  		// by an update
  3785  		"snapd_recovery_mode=run static mocked panic=-1 this-is-not-applied",
  3786  	})
  3787  }
  3788  
  3789  func (s *bootKernelCommandLineSuite) TestCommandLineUpdateWithResealError(c *C) {
  3790  	gadgetSnap := snaptest.MakeTestSnapWithFiles(c, gadgetSnapYaml, [][]string{
  3791  		{"cmdline.extra", "args from gadget"},
  3792  	})
  3793  
  3794  	s.stampSealedKeys(c, dirs.GlobalRootDir)
  3795  	c.Assert(s.modeenvWithEncryption.WriteTo(""), IsNil)
  3796  
  3797  	resealCalls := 0
  3798  	restore := boot.MockSecbootResealKeys(func(params *secboot.ResealKeysParams) error {
  3799  		resealCalls++
  3800  		return fmt.Errorf("reseal fails")
  3801  	})
  3802  	defer restore()
  3803  
  3804  	reboot, err := boot.UpdateCommandLineForGadgetComponent(s.uc20dev, gadgetSnap)
  3805  	c.Assert(err, ErrorMatches, "cannot reseal the encryption key: reseal fails")
  3806  	c.Check(reboot, Equals, false)
  3807  	c.Check(s.bootloader.SetBootVarsCalls, Equals, 0)
  3808  	c.Check(resealCalls, Equals, 1)
  3809  
  3810  	m2, err := boot.ReadModeenv("")
  3811  	c.Assert(err, IsNil)
  3812  	c.Assert(m2.CurrentKernelCommandLines, DeepEquals, boot.BootCommandLines{
  3813  		"snapd_recovery_mode=run static mocked panic=-1",
  3814  		"snapd_recovery_mode=run static mocked panic=-1 args from gadget",
  3815  	})
  3816  }
  3817  
  3818  func (s *bootKernelCommandLineSuite) TestCommandLineUpdateUC20TransitionFullExtraAndBack(c *C) {
  3819  	s.stampSealedKeys(c, dirs.GlobalRootDir)
  3820  
  3821  	// no command line arguments from gadget
  3822  	s.modeenvWithEncryption.CurrentKernelCommandLines = []string{"snapd_recovery_mode=run static mocked panic=-1"}
  3823  	c.Assert(s.modeenvWithEncryption.WriteTo(""), IsNil)
  3824  	err := s.bootloader.SetBootVars(map[string]string{
  3825  		// those are intentionally filled by the test
  3826  		"snapd_extra_cmdline_args": "canary",
  3827  		"snapd_full_cmdline_args":  "canary",
  3828  	})
  3829  	c.Assert(err, IsNil)
  3830  	s.bootloader.SetBootVarsCalls = 0
  3831  
  3832  	// transition to gadget with cmdline.extra
  3833  	sf := snaptest.MakeTestSnapWithFiles(c, gadgetSnapYaml, [][]string{
  3834  		{"cmdline.extra", "extra args"},
  3835  	})
  3836  	reboot, err := boot.UpdateCommandLineForGadgetComponent(s.uc20dev, sf)
  3837  	c.Assert(err, IsNil)
  3838  	c.Assert(reboot, Equals, true)
  3839  	c.Check(s.resealCalls, Equals, 1)
  3840  	c.Check(s.resealCommandLines, DeepEquals, [][]string{{
  3841  		// those come from boot chains which use predictable sorting
  3842  		"snapd_recovery_mode=run static mocked panic=-1",
  3843  		"snapd_recovery_mode=run static mocked panic=-1 extra args",
  3844  	}})
  3845  	s.resealCommandLines = nil
  3846  
  3847  	newM, err := boot.ReadModeenv("")
  3848  	c.Assert(err, IsNil)
  3849  	c.Check(newM.CurrentKernelCommandLines, DeepEquals, boot.BootCommandLines{
  3850  		"snapd_recovery_mode=run static mocked panic=-1",
  3851  		"snapd_recovery_mode=run static mocked panic=-1 extra args",
  3852  	})
  3853  	c.Check(s.bootloader.SetBootVarsCalls, Equals, 1)
  3854  	args, err := s.bootloader.GetBootVars("snapd_extra_cmdline_args", "snapd_full_cmdline_args")
  3855  	c.Assert(err, IsNil)
  3856  	c.Check(args, DeepEquals, map[string]string{
  3857  		"snapd_extra_cmdline_args": "extra args",
  3858  		// canary has been cleared
  3859  		"snapd_full_cmdline_args": "",
  3860  	})
  3861  	// this normally happens after booting
  3862  	s.modeenvWithEncryption.CurrentKernelCommandLines = []string{"snapd_recovery_mode=run static mocked panic=-1 extra args"}
  3863  	c.Assert(s.modeenvWithEncryption.WriteTo(""), IsNil)
  3864  
  3865  	// transition to full override from gadget
  3866  	sfFull := snaptest.MakeTestSnapWithFiles(c, gadgetSnapYaml, [][]string{
  3867  		{"cmdline.full", "full args"},
  3868  	})
  3869  	reboot, err = boot.UpdateCommandLineForGadgetComponent(s.uc20dev, sfFull)
  3870  	c.Assert(err, IsNil)
  3871  	c.Assert(reboot, Equals, true)
  3872  	c.Check(s.resealCalls, Equals, 2)
  3873  	c.Check(s.resealCommandLines, DeepEquals, [][]string{{
  3874  		// those come from boot chains which use predictable sorting
  3875  		"snapd_recovery_mode=run full args",
  3876  		"snapd_recovery_mode=run static mocked panic=-1 extra args",
  3877  	}})
  3878  	s.resealCommandLines = nil
  3879  	// modeenv has been updated
  3880  	newM, err = boot.ReadModeenv("")
  3881  	c.Assert(err, IsNil)
  3882  	c.Check(newM.CurrentKernelCommandLines, DeepEquals, boot.BootCommandLines{
  3883  		"snapd_recovery_mode=run static mocked panic=-1 extra args",
  3884  		// new ones are appended
  3885  		"snapd_recovery_mode=run full args",
  3886  	})
  3887  	// and bootloader env too
  3888  	args, err = s.bootloader.GetBootVars("snapd_extra_cmdline_args", "snapd_full_cmdline_args")
  3889  	c.Assert(err, IsNil)
  3890  	c.Check(args, DeepEquals, map[string]string{
  3891  		// cleared
  3892  		"snapd_extra_cmdline_args": "",
  3893  		// and full arguments were set
  3894  		"snapd_full_cmdline_args": "full args",
  3895  	})
  3896  	// this normally happens after booting
  3897  	s.modeenvWithEncryption.CurrentKernelCommandLines = []string{"snapd_recovery_mode=run full args"}
  3898  	c.Assert(s.modeenvWithEncryption.WriteTo(""), IsNil)
  3899  
  3900  	// transition back to no arguments from the gadget
  3901  	sfNone := snaptest.MakeTestSnapWithFiles(c, gadgetSnapYaml, nil)
  3902  	reboot, err = boot.UpdateCommandLineForGadgetComponent(s.uc20dev, sfNone)
  3903  	c.Assert(err, IsNil)
  3904  	c.Assert(reboot, Equals, true)
  3905  	c.Check(s.resealCalls, Equals, 3)
  3906  	c.Check(s.resealCommandLines, DeepEquals, [][]string{{
  3907  		// those come from boot chains which use predictable sorting
  3908  		"snapd_recovery_mode=run full args",
  3909  		"snapd_recovery_mode=run static mocked panic=-1",
  3910  	}})
  3911  	// modeenv has been updated again
  3912  	newM, err = boot.ReadModeenv("")
  3913  	c.Assert(err, IsNil)
  3914  	c.Check(newM.CurrentKernelCommandLines, DeepEquals, boot.BootCommandLines{
  3915  		"snapd_recovery_mode=run full args",
  3916  		// new ones are appended
  3917  		"snapd_recovery_mode=run static mocked panic=-1",
  3918  	})
  3919  	// and bootloader env too
  3920  	args, err = s.bootloader.GetBootVars("snapd_extra_cmdline_args", "snapd_full_cmdline_args")
  3921  	c.Assert(err, IsNil)
  3922  	c.Check(args, DeepEquals, map[string]string{
  3923  		// both env variables have been cleared
  3924  		"snapd_extra_cmdline_args": "",
  3925  		"snapd_full_cmdline_args":  "",
  3926  	})
  3927  }
  3928  
  3929  func (s *bootKernelCommandLineSuite) TestCommandLineUpdateUC20OverSpuriousRebootsBeforeBootVarsSet(c *C) {
  3930  	// simulate spurious reboots
  3931  	s.stampSealedKeys(c, dirs.GlobalRootDir)
  3932  
  3933  	resealPanic := false
  3934  	restore := boot.MockSecbootResealKeys(func(params *secboot.ResealKeysParams) error {
  3935  		s.resealCalls++
  3936  		c.Logf("reseal call %v", s.resealCalls)
  3937  		c.Assert(params, NotNil)
  3938  		c.Assert(params.ModelParams, HasLen, 1)
  3939  		s.resealCommandLines = append(s.resealCommandLines, params.ModelParams[0].KernelCmdlines)
  3940  		if resealPanic {
  3941  			panic("reseal panic")
  3942  		}
  3943  		return nil
  3944  	})
  3945  	defer restore()
  3946  
  3947  	// no command line arguments from gadget
  3948  	s.modeenvWithEncryption.CurrentKernelCommandLines = []string{"snapd_recovery_mode=run static mocked panic=-1"}
  3949  	c.Assert(s.modeenvWithEncryption.WriteTo(""), IsNil)
  3950  
  3951  	cmdlineFile := filepath.Join(c.MkDir(), "cmdline")
  3952  	err := ioutil.WriteFile(cmdlineFile, []byte("snapd_recovery_mode=run static mocked panic=-1"), 0644)
  3953  	c.Assert(err, IsNil)
  3954  	restore = osutil.MockProcCmdline(cmdlineFile)
  3955  	s.AddCleanup(restore)
  3956  
  3957  	err = s.bootloader.SetBootVars(map[string]string{
  3958  		// those are intentionally filled by the test
  3959  		"snapd_extra_cmdline_args": "canary",
  3960  		"snapd_full_cmdline_args":  "canary",
  3961  	})
  3962  	c.Assert(err, IsNil)
  3963  	s.bootloader.SetBootVarsCalls = 0
  3964  
  3965  	restoreBootloaderNoPanic := s.bootloader.SetMockToPanic("SetBootVars")
  3966  	defer restoreBootloaderNoPanic()
  3967  
  3968  	// transition to gadget with cmdline.extra
  3969  	sf := snaptest.MakeTestSnapWithFiles(c, gadgetSnapYaml, [][]string{
  3970  		{"cmdline.extra", "extra args"},
  3971  	})
  3972  
  3973  	// let's panic on reseal first
  3974  	resealPanic = true
  3975  	c.Assert(func() {
  3976  		boot.UpdateCommandLineForGadgetComponent(s.uc20dev, sf)
  3977  	}, PanicMatches, "reseal panic")
  3978  	c.Check(s.resealCalls, Equals, 1)
  3979  	c.Check(s.resealCommandLines, DeepEquals, [][]string{{
  3980  		// those come from boot chains which use predictable sorting
  3981  		"snapd_recovery_mode=run static mocked panic=-1",
  3982  		"snapd_recovery_mode=run static mocked panic=-1 extra args",
  3983  	}})
  3984  	// bootenv hasn't been updated yet
  3985  	c.Check(s.bootloader.SetBootVarsCalls, Equals, 0)
  3986  	// but modeenv has already been updated
  3987  	m, err := boot.ReadModeenv("")
  3988  	c.Assert(err, IsNil)
  3989  	c.Check(m.CurrentKernelCommandLines, DeepEquals, boot.BootCommandLines{
  3990  		"snapd_recovery_mode=run static mocked panic=-1",
  3991  		"snapd_recovery_mode=run static mocked panic=-1 extra args",
  3992  	})
  3993  
  3994  	// REBOOT
  3995  	resealPanic = false
  3996  	err = boot.MarkBootSuccessful(s.uc20dev)
  3997  	c.Assert(err, IsNil)
  3998  	// we resealed after reboot, since modeenv was updated and carries the
  3999  	// current command line only
  4000  	c.Check(s.resealCalls, Equals, 2)
  4001  	m, err = boot.ReadModeenv("")
  4002  	c.Assert(err, IsNil)
  4003  	c.Check(m.CurrentKernelCommandLines, DeepEquals, boot.BootCommandLines{
  4004  		"snapd_recovery_mode=run static mocked panic=-1",
  4005  	})
  4006  
  4007  	// try the update again, but no panic in reseal this time
  4008  	s.resealCalls = 0
  4009  	s.resealCommandLines = nil
  4010  	resealPanic = false
  4011  	// but panic in set
  4012  	c.Assert(func() {
  4013  		boot.UpdateCommandLineForGadgetComponent(s.uc20dev, sf)
  4014  	}, PanicMatches, "mocked reboot panic in SetBootVars")
  4015  	c.Check(s.resealCalls, Equals, 1)
  4016  	c.Check(s.resealCommandLines, DeepEquals, [][]string{{
  4017  		// those come from boot chains which use predictable sorting
  4018  		"snapd_recovery_mode=run static mocked panic=-1",
  4019  		"snapd_recovery_mode=run static mocked panic=-1 extra args",
  4020  	}})
  4021  	// the call to bootloader wasn't counted, because it called panic
  4022  	c.Check(s.bootloader.SetBootVarsCalls, Equals, 0)
  4023  	m, err = boot.ReadModeenv("")
  4024  	c.Assert(err, IsNil)
  4025  	c.Check(m.CurrentKernelCommandLines, DeepEquals, boot.BootCommandLines{
  4026  		"snapd_recovery_mode=run static mocked panic=-1",
  4027  		"snapd_recovery_mode=run static mocked panic=-1 extra args",
  4028  	})
  4029  
  4030  	// REBOOT
  4031  	err = boot.MarkBootSuccessful(s.uc20dev)
  4032  	c.Assert(err, IsNil)
  4033  	// we resealed after reboot again
  4034  	c.Check(s.resealCalls, Equals, 2)
  4035  	m, err = boot.ReadModeenv("")
  4036  	c.Assert(err, IsNil)
  4037  	c.Check(m.CurrentKernelCommandLines, DeepEquals, boot.BootCommandLines{
  4038  		"snapd_recovery_mode=run static mocked panic=-1",
  4039  	})
  4040  
  4041  	// try again, for the last time, things should go smoothly
  4042  	s.resealCalls = 0
  4043  	s.resealCommandLines = nil
  4044  	restoreBootloaderNoPanic()
  4045  	reboot, err := boot.UpdateCommandLineForGadgetComponent(s.uc20dev, sf)
  4046  	c.Assert(err, IsNil)
  4047  	c.Check(reboot, Equals, true)
  4048  	c.Check(s.resealCalls, Equals, 1)
  4049  	c.Check(s.bootloader.SetBootVarsCalls, Equals, 1)
  4050  	// all done, modeenv
  4051  	m, err = boot.ReadModeenv("")
  4052  	c.Assert(err, IsNil)
  4053  	c.Check(m.CurrentKernelCommandLines, DeepEquals, boot.BootCommandLines{
  4054  		"snapd_recovery_mode=run static mocked panic=-1",
  4055  		"snapd_recovery_mode=run static mocked panic=-1 extra args",
  4056  	})
  4057  	args, err := s.bootloader.GetBootVars("snapd_extra_cmdline_args", "snapd_full_cmdline_args")
  4058  	c.Assert(err, IsNil)
  4059  	c.Check(args, DeepEquals, map[string]string{
  4060  		"snapd_extra_cmdline_args": "extra args",
  4061  		// canary has been cleared
  4062  		"snapd_full_cmdline_args": "",
  4063  	})
  4064  }
  4065  
  4066  func (s *bootKernelCommandLineSuite) TestCommandLineUpdateUC20OverSpuriousRebootsAfterBootVars(c *C) {
  4067  	// simulate spurious reboots
  4068  	s.stampSealedKeys(c, dirs.GlobalRootDir)
  4069  
  4070  	// no command line arguments from gadget
  4071  	s.modeenvWithEncryption.CurrentKernelCommandLines = []string{"snapd_recovery_mode=run static mocked panic=-1"}
  4072  	c.Assert(s.modeenvWithEncryption.WriteTo(""), IsNil)
  4073  
  4074  	cmdlineFile := filepath.Join(c.MkDir(), "cmdline")
  4075  	restore := osutil.MockProcCmdline(cmdlineFile)
  4076  	s.AddCleanup(restore)
  4077  
  4078  	err := s.bootloader.SetBootVars(map[string]string{
  4079  		// those are intentionally filled by the test
  4080  		"snapd_extra_cmdline_args": "canary",
  4081  		"snapd_full_cmdline_args":  "canary",
  4082  	})
  4083  	c.Assert(err, IsNil)
  4084  	s.bootloader.SetBootVarsCalls = 0
  4085  
  4086  	// transition to gadget with cmdline.extra
  4087  	sf := snaptest.MakeTestSnapWithFiles(c, gadgetSnapYaml, [][]string{
  4088  		{"cmdline.extra", "extra args"},
  4089  	})
  4090  
  4091  	// let's panic after setting bootenv, but before returning, such that if
  4092  	// executed by a task handler, the task's status would not get updated
  4093  	s.bootloader.SetErrFunc = func() error {
  4094  		panic("mocked reboot panic after SetBootVars")
  4095  	}
  4096  	c.Assert(func() {
  4097  		boot.UpdateCommandLineForGadgetComponent(s.uc20dev, sf)
  4098  	}, PanicMatches, "mocked reboot panic after SetBootVars")
  4099  	c.Check(s.resealCalls, Equals, 1)
  4100  	c.Check(s.resealCommandLines, DeepEquals, [][]string{{
  4101  		// those come from boot chains which use predictable sorting
  4102  		"snapd_recovery_mode=run static mocked panic=-1",
  4103  		"snapd_recovery_mode=run static mocked panic=-1 extra args",
  4104  	}})
  4105  	// the call to bootloader was executed
  4106  	c.Check(s.bootloader.SetBootVarsCalls, Equals, 1)
  4107  	m, err := boot.ReadModeenv("")
  4108  	c.Assert(err, IsNil)
  4109  	c.Check(m.CurrentKernelCommandLines, DeepEquals, boot.BootCommandLines{
  4110  		"snapd_recovery_mode=run static mocked panic=-1",
  4111  		"snapd_recovery_mode=run static mocked panic=-1 extra args",
  4112  	})
  4113  	args, err := s.bootloader.GetBootVars("snapd_extra_cmdline_args", "snapd_full_cmdline_args")
  4114  	c.Assert(err, IsNil)
  4115  	c.Check(args, DeepEquals, map[string]string{
  4116  		"snapd_extra_cmdline_args": "extra args",
  4117  		// canary has been cleared
  4118  		"snapd_full_cmdline_args": "",
  4119  	})
  4120  
  4121  	// REBOOT; since we rebooted after updating the bootenv, the kernel
  4122  	// command line will include arguments that came from gadget snap
  4123  	s.bootloader.SetBootVarsCalls = 0
  4124  	s.resealCalls = 0
  4125  	err = ioutil.WriteFile(cmdlineFile, []byte("snapd_recovery_mode=run static mocked panic=-1 extra args"), 0644)
  4126  	c.Assert(err, IsNil)
  4127  	err = boot.MarkBootSuccessful(s.uc20dev)
  4128  	c.Assert(err, IsNil)
  4129  	// we resealed after reboot again
  4130  	c.Check(s.resealCalls, Equals, 1)
  4131  	// bootenv wasn't touched
  4132  	c.Check(s.bootloader.SetBootVarsCalls, Equals, 0)
  4133  	m, err = boot.ReadModeenv("")
  4134  	c.Assert(err, IsNil)
  4135  	c.Check(m.CurrentKernelCommandLines, DeepEquals, boot.BootCommandLines{
  4136  		"snapd_recovery_mode=run static mocked panic=-1 extra args",
  4137  	})
  4138  
  4139  	// try again, as if the task handler gets to run again
  4140  	s.resealCalls = 0
  4141  	reboot, err := boot.UpdateCommandLineForGadgetComponent(s.uc20dev, sf)
  4142  	c.Assert(err, IsNil)
  4143  	// nothing changed now, we already booted with the new command line
  4144  	c.Check(reboot, Equals, false)
  4145  	// not reseal since nothing changed
  4146  	c.Check(s.resealCalls, Equals, 0)
  4147  	// no changes to the bootenv either
  4148  	c.Check(s.bootloader.SetBootVarsCalls, Equals, 0)
  4149  	// all done, modeenv
  4150  	m, err = boot.ReadModeenv("")
  4151  	c.Assert(err, IsNil)
  4152  	c.Check(m.CurrentKernelCommandLines, DeepEquals, boot.BootCommandLines{
  4153  		"snapd_recovery_mode=run static mocked panic=-1 extra args",
  4154  	})
  4155  	args, err = s.bootloader.GetBootVars("snapd_extra_cmdline_args", "snapd_full_cmdline_args")
  4156  	c.Assert(err, IsNil)
  4157  	c.Check(args, DeepEquals, map[string]string{
  4158  		"snapd_extra_cmdline_args": "extra args",
  4159  		"snapd_full_cmdline_args":  "",
  4160  	})
  4161  }