github.com/chipaca/snappy@v0.0.0-20210104084008-1f06296fe8ad/overlord/devicestate/devicestate_install_mode_test.go (about)

     1  // -*- Mode: Go; indent-tabs-mode: t -*-
     2  
     3  /*
     4   * Copyright (C) 2019 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 devicestate_test
    21  
    22  import (
    23  	"bytes"
    24  	"fmt"
    25  	"io/ioutil"
    26  	"os"
    27  	"path/filepath"
    28  
    29  	. "gopkg.in/check.v1"
    30  	"gopkg.in/tomb.v2"
    31  
    32  	"github.com/snapcore/snapd/asserts"
    33  	"github.com/snapcore/snapd/boot"
    34  	"github.com/snapcore/snapd/bootloader"
    35  	"github.com/snapcore/snapd/bootloader/bootloadertest"
    36  	"github.com/snapcore/snapd/dirs"
    37  	"github.com/snapcore/snapd/gadget"
    38  	"github.com/snapcore/snapd/gadget/install"
    39  	"github.com/snapcore/snapd/logger"
    40  	"github.com/snapcore/snapd/overlord/auth"
    41  	"github.com/snapcore/snapd/overlord/devicestate"
    42  	"github.com/snapcore/snapd/overlord/devicestate/devicestatetest"
    43  	"github.com/snapcore/snapd/overlord/hookstate"
    44  	"github.com/snapcore/snapd/overlord/snapstate"
    45  	"github.com/snapcore/snapd/overlord/snapstate/snapstatetest"
    46  	"github.com/snapcore/snapd/overlord/state"
    47  	"github.com/snapcore/snapd/release"
    48  	"github.com/snapcore/snapd/secboot"
    49  	"github.com/snapcore/snapd/snap"
    50  	"github.com/snapcore/snapd/snap/snaptest"
    51  	"github.com/snapcore/snapd/sysconfig"
    52  	"github.com/snapcore/snapd/testutil"
    53  )
    54  
    55  type deviceMgrInstallModeSuite struct {
    56  	deviceMgrBaseSuite
    57  
    58  	ConfigureTargetSystemOptsPassed []*sysconfig.Options
    59  	ConfigureTargetSystemErr        error
    60  }
    61  
    62  var _ = Suite(&deviceMgrInstallModeSuite{})
    63  
    64  func (s *deviceMgrInstallModeSuite) findInstallSystem() *state.Change {
    65  	for _, chg := range s.state.Changes() {
    66  		if chg.Kind() == "install-system" {
    67  			return chg
    68  		}
    69  	}
    70  	return nil
    71  }
    72  
    73  func (s *deviceMgrInstallModeSuite) SetUpTest(c *C) {
    74  	s.deviceMgrBaseSuite.SetUpTest(c)
    75  
    76  	s.ConfigureTargetSystemOptsPassed = nil
    77  	s.ConfigureTargetSystemErr = nil
    78  	restore := devicestate.MockSysconfigConfigureTargetSystem(func(opts *sysconfig.Options) error {
    79  		s.ConfigureTargetSystemOptsPassed = append(s.ConfigureTargetSystemOptsPassed, opts)
    80  		return s.ConfigureTargetSystemErr
    81  	})
    82  	s.AddCleanup(restore)
    83  
    84  	restore = devicestate.MockSecbootCheckKeySealingSupported(func() error {
    85  		return fmt.Errorf("TPM not available")
    86  	})
    87  	s.AddCleanup(restore)
    88  
    89  	s.state.Lock()
    90  	defer s.state.Unlock()
    91  	s.state.Set("seeded", true)
    92  }
    93  
    94  const (
    95  	pcSnapID       = "pcididididididididididididididid"
    96  	pcKernelSnapID = "pckernelidididididididididididid"
    97  	core20SnapID   = "core20ididididididididididididid"
    98  )
    99  
   100  func (s *deviceMgrInstallModeSuite) makeMockInstalledPcGadget(c *C, grade, gadgetDefaultsYaml string) *asserts.Model {
   101  	si := &snap.SideInfo{
   102  		RealName: "pc-kernel",
   103  		Revision: snap.R(1),
   104  		SnapID:   pcKernelSnapID,
   105  	}
   106  	snapstate.Set(s.state, "pc-kernel", &snapstate.SnapState{
   107  		SnapType: "kernel",
   108  		Sequence: []*snap.SideInfo{si},
   109  		Current:  si.Revision,
   110  		Active:   true,
   111  	})
   112  	kernelInfo := snaptest.MockSnapWithFiles(c, "name: pc-kernel\ntype: kernel", si, nil)
   113  	kernelFn := snaptest.MakeTestSnapWithFiles(c, "name: pc-kernel\ntype: kernel\nversion: 1.0", nil)
   114  	err := os.Rename(kernelFn, kernelInfo.MountFile())
   115  	c.Assert(err, IsNil)
   116  
   117  	si = &snap.SideInfo{
   118  		RealName: "pc",
   119  		Revision: snap.R(1),
   120  		SnapID:   pcSnapID,
   121  	}
   122  	snapstate.Set(s.state, "pc", &snapstate.SnapState{
   123  		SnapType: "gadget",
   124  		Sequence: []*snap.SideInfo{si},
   125  		Current:  si.Revision,
   126  		Active:   true,
   127  	})
   128  	snaptest.MockSnapWithFiles(c, "name: pc\ntype: gadget", si, [][]string{
   129  		{"meta/gadget.yaml", uc20gadgetYamlWithSave + gadgetDefaultsYaml},
   130  	})
   131  
   132  	si = &snap.SideInfo{
   133  		RealName: "core20",
   134  		Revision: snap.R(2),
   135  		SnapID:   core20SnapID,
   136  	}
   137  	snapstate.Set(s.state, "core20", &snapstate.SnapState{
   138  		SnapType: "base",
   139  		Sequence: []*snap.SideInfo{si},
   140  		Current:  si.Revision,
   141  		Active:   true,
   142  	})
   143  	snaptest.MockSnapWithFiles(c, "name: core20\ntype: base", si, nil)
   144  
   145  	mockModel := s.makeModelAssertionInState(c, "my-brand", "my-model", map[string]interface{}{
   146  		"display-name": "my model",
   147  		"architecture": "amd64",
   148  		"base":         "core20",
   149  		"grade":        grade,
   150  		"snaps": []interface{}{
   151  			map[string]interface{}{
   152  				"name":            "pc-kernel",
   153  				"id":              pcKernelSnapID,
   154  				"type":            "kernel",
   155  				"default-channel": "20",
   156  			},
   157  			map[string]interface{}{
   158  				"name":            "pc",
   159  				"id":              pcSnapID,
   160  				"type":            "gadget",
   161  				"default-channel": "20",
   162  			}},
   163  	})
   164  	devicestatetest.SetDevice(s.state, &auth.DeviceState{
   165  		Brand: "my-brand",
   166  		Model: "my-model",
   167  		// no serial in install mode
   168  	})
   169  
   170  	return mockModel
   171  }
   172  
   173  type encTestCase struct {
   174  	tpm               bool
   175  	bypass            bool
   176  	encrypt           bool
   177  	trustedBootloader bool
   178  }
   179  
   180  var (
   181  	dataEncryptionKey = secboot.EncryptionKey{'d', 'a', 't', 'a', 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16}
   182  	dataRecoveryKey   = secboot.RecoveryKey{'r', 'e', 'c', 'o', 'v', 'e', 'r', 'y', 10, 11, 12, 13, 14, 15, 16, 17}
   183  
   184  	saveKey      = secboot.EncryptionKey{'s', 'a', 'v', 'e', 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16}
   185  	reinstallKey = secboot.RecoveryKey{'r', 'e', 'i', 'n', 's', 't', 'a', 'l', 'l', 11, 12, 13, 14, 15, 16, 17}
   186  )
   187  
   188  func (s *deviceMgrInstallModeSuite) doRunChangeTestWithEncryption(c *C, grade string, tc encTestCase) error {
   189  	restore := release.MockOnClassic(false)
   190  	defer restore()
   191  	bootloaderRootdir := c.MkDir()
   192  
   193  	var brGadgetRoot, brDevice string
   194  	var brOpts install.Options
   195  	var installRunCalled int
   196  	var installSealingObserver gadget.ContentObserver
   197  	restore = devicestate.MockInstallRun(func(mod gadget.Model, gadgetRoot, device string, options install.Options, obs gadget.ContentObserver) (*install.InstalledSystemSideData, error) {
   198  		// ensure we can grab the lock here, i.e. that it's not taken
   199  		s.state.Lock()
   200  		s.state.Unlock()
   201  
   202  		c.Check(mod.Grade(), Equals, asserts.ModelGrade(grade))
   203  
   204  		brGadgetRoot = gadgetRoot
   205  		brDevice = device
   206  		brOpts = options
   207  		installSealingObserver = obs
   208  		installRunCalled++
   209  		var keysForRoles map[string]*install.EncryptionKeySet
   210  		if tc.encrypt {
   211  			keysForRoles = map[string]*install.EncryptionKeySet{
   212  				gadget.SystemData: {
   213  					Key:         dataEncryptionKey,
   214  					RecoveryKey: dataRecoveryKey,
   215  				},
   216  				gadget.SystemSave: {
   217  					Key:         saveKey,
   218  					RecoveryKey: reinstallKey,
   219  				},
   220  			}
   221  		}
   222  		return &install.InstalledSystemSideData{
   223  			KeysForRoles: keysForRoles,
   224  		}, nil
   225  	})
   226  	defer restore()
   227  
   228  	restore = devicestate.MockSecbootCheckKeySealingSupported(func() error {
   229  		if tc.tpm {
   230  			return nil
   231  		} else {
   232  			return fmt.Errorf("TPM not available")
   233  		}
   234  	})
   235  	defer restore()
   236  
   237  	if tc.trustedBootloader {
   238  		tab := bootloadertest.Mock("trusted", bootloaderRootdir).WithTrustedAssets()
   239  		tab.TrustedAssetsList = []string{"trusted-asset"}
   240  		bootloader.Force(tab)
   241  		s.AddCleanup(func() { bootloader.Force(nil) })
   242  
   243  		err := os.MkdirAll(boot.InitramfsUbuntuSeedDir, 0755)
   244  		c.Assert(err, IsNil)
   245  		err = ioutil.WriteFile(filepath.Join(boot.InitramfsUbuntuSeedDir, "trusted-asset"), nil, 0644)
   246  		c.Assert(err, IsNil)
   247  	}
   248  
   249  	s.state.Lock()
   250  	mockModel := s.makeMockInstalledPcGadget(c, grade, "")
   251  	s.state.Unlock()
   252  
   253  	bypassEncryptionPath := filepath.Join(boot.InitramfsUbuntuSeedDir, ".force-unencrypted")
   254  	if tc.bypass {
   255  		err := os.MkdirAll(filepath.Dir(bypassEncryptionPath), 0755)
   256  		c.Assert(err, IsNil)
   257  		f, err := os.Create(bypassEncryptionPath)
   258  		c.Assert(err, IsNil)
   259  		f.Close()
   260  	} else {
   261  		os.RemoveAll(bypassEncryptionPath)
   262  	}
   263  
   264  	bootMakeBootableCalled := 0
   265  	restore = devicestate.MockBootMakeBootable(func(model *asserts.Model, rootdir string, bootWith *boot.BootableSet, seal *boot.TrustedAssetsInstallObserver) error {
   266  		c.Check(model, DeepEquals, mockModel)
   267  		c.Check(rootdir, Equals, dirs.GlobalRootDir)
   268  		c.Check(bootWith.KernelPath, Matches, ".*/var/lib/snapd/snaps/pc-kernel_1.snap")
   269  		c.Check(bootWith.BasePath, Matches, ".*/var/lib/snapd/snaps/core20_2.snap")
   270  		c.Check(bootWith.RecoverySystemDir, Matches, "/systems/20191218")
   271  		c.Check(bootWith.UnpackedGadgetDir, Equals, filepath.Join(dirs.SnapMountDir, "pc/1"))
   272  		if tc.encrypt {
   273  			c.Check(seal, NotNil)
   274  		} else {
   275  			c.Check(seal, IsNil)
   276  		}
   277  		bootMakeBootableCalled++
   278  		return nil
   279  	})
   280  	defer restore()
   281  
   282  	modeenv := boot.Modeenv{
   283  		Mode:           "install",
   284  		RecoverySystem: "20191218",
   285  	}
   286  	c.Assert(modeenv.WriteTo(""), IsNil)
   287  	devicestate.SetSystemMode(s.mgr, "install")
   288  
   289  	// normally done by snap-bootstrap
   290  	err := os.MkdirAll(boot.InitramfsUbuntuBootDir, 0755)
   291  	c.Assert(err, IsNil)
   292  
   293  	s.settle(c)
   294  
   295  	// the install-system change is created
   296  	s.state.Lock()
   297  	defer s.state.Unlock()
   298  	installSystem := s.findInstallSystem()
   299  	c.Assert(installSystem, NotNil)
   300  
   301  	// and was run successfully
   302  	if err := installSystem.Err(); err != nil {
   303  		// we failed, no further checks needed
   304  		return err
   305  	}
   306  
   307  	c.Assert(installSystem.Status(), Equals, state.DoneStatus)
   308  
   309  	// in the right way
   310  	c.Assert(brGadgetRoot, Equals, filepath.Join(dirs.SnapMountDir, "/pc/1"))
   311  	c.Assert(brDevice, Equals, "")
   312  	if tc.encrypt {
   313  		c.Assert(brOpts, DeepEquals, install.Options{
   314  			Mount:   true,
   315  			Encrypt: true,
   316  		})
   317  	} else {
   318  		c.Assert(brOpts, DeepEquals, install.Options{
   319  			Mount: true,
   320  		})
   321  	}
   322  	if tc.encrypt {
   323  		// inteface is not nil
   324  		c.Assert(installSealingObserver, NotNil)
   325  		// we expect a very specific type
   326  		trustedInstallObserver, ok := installSealingObserver.(*boot.TrustedAssetsInstallObserver)
   327  		c.Assert(ok, Equals, true, Commentf("unexpected type: %T", installSealingObserver))
   328  		c.Assert(trustedInstallObserver, NotNil)
   329  	} else {
   330  		c.Assert(installSealingObserver, IsNil)
   331  	}
   332  
   333  	c.Assert(installRunCalled, Equals, 1)
   334  	c.Assert(bootMakeBootableCalled, Equals, 1)
   335  	c.Assert(s.restartRequests, DeepEquals, []state.RestartType{state.RestartSystemNow})
   336  
   337  	return nil
   338  }
   339  
   340  func (s *deviceMgrInstallModeSuite) TestInstallTaskErrors(c *C) {
   341  	restore := release.MockOnClassic(false)
   342  	defer restore()
   343  
   344  	restore = devicestate.MockInstallRun(func(mod gadget.Model, gadgetRoot, device string, options install.Options, _ gadget.ContentObserver) (*install.InstalledSystemSideData, error) {
   345  		return nil, fmt.Errorf("The horror, The horror")
   346  	})
   347  	defer restore()
   348  
   349  	err := ioutil.WriteFile(filepath.Join(dirs.GlobalRootDir, "/var/lib/snapd/modeenv"),
   350  		[]byte("mode=install\n"), 0644)
   351  	c.Assert(err, IsNil)
   352  
   353  	s.state.Lock()
   354  	s.makeMockInstalledPcGadget(c, "dangerous", "")
   355  	devicestate.SetSystemMode(s.mgr, "install")
   356  	s.state.Unlock()
   357  
   358  	s.settle(c)
   359  
   360  	s.state.Lock()
   361  	defer s.state.Unlock()
   362  
   363  	installSystem := s.findInstallSystem()
   364  	c.Check(installSystem.Err(), ErrorMatches, `(?ms)cannot perform the following tasks:
   365  - Setup system for run mode \(cannot install system: The horror, The horror\)`)
   366  	// no restart request on failure
   367  	c.Check(s.restartRequests, HasLen, 0)
   368  }
   369  
   370  func (s *deviceMgrInstallModeSuite) TestInstallModeNotInstallmodeNoChg(c *C) {
   371  	restore := release.MockOnClassic(false)
   372  	defer restore()
   373  
   374  	s.state.Lock()
   375  	devicestate.SetSystemMode(s.mgr, "")
   376  	s.state.Unlock()
   377  
   378  	s.settle(c)
   379  
   380  	s.state.Lock()
   381  	defer s.state.Unlock()
   382  
   383  	// the install-system change is *not* created (not in install mode)
   384  	installSystem := s.findInstallSystem()
   385  	c.Assert(installSystem, IsNil)
   386  }
   387  
   388  func (s *deviceMgrInstallModeSuite) TestInstallModeNotClassic(c *C) {
   389  	restore := release.MockOnClassic(true)
   390  	defer restore()
   391  
   392  	s.state.Lock()
   393  	devicestate.SetSystemMode(s.mgr, "install")
   394  	s.state.Unlock()
   395  
   396  	s.settle(c)
   397  
   398  	s.state.Lock()
   399  	defer s.state.Unlock()
   400  
   401  	// the install-system change is *not* created (we're on classic)
   402  	installSystem := s.findInstallSystem()
   403  	c.Assert(installSystem, IsNil)
   404  }
   405  
   406  func (s *deviceMgrInstallModeSuite) TestInstallDangerous(c *C) {
   407  	err := s.doRunChangeTestWithEncryption(c, "dangerous", encTestCase{tpm: false, bypass: false, encrypt: false})
   408  	c.Assert(err, IsNil)
   409  }
   410  
   411  func (s *deviceMgrInstallModeSuite) TestInstallDangerousWithTPM(c *C) {
   412  	err := s.doRunChangeTestWithEncryption(c, "dangerous", encTestCase{
   413  		tpm: true, bypass: false, encrypt: true, trustedBootloader: true,
   414  	})
   415  	c.Assert(err, IsNil)
   416  	c.Check(filepath.Join(boot.InstallHostFDEDataDir, "recovery.key"), testutil.FileEquals, dataRecoveryKey[:])
   417  }
   418  
   419  func (s *deviceMgrInstallModeSuite) TestInstallDangerousBypassEncryption(c *C) {
   420  	err := s.doRunChangeTestWithEncryption(c, "dangerous", encTestCase{tpm: false, bypass: true, encrypt: false})
   421  	c.Assert(err, IsNil)
   422  }
   423  
   424  func (s *deviceMgrInstallModeSuite) TestInstallDangerousWithTPMBypassEncryption(c *C) {
   425  	err := s.doRunChangeTestWithEncryption(c, "dangerous", encTestCase{tpm: true, bypass: true, encrypt: false})
   426  	c.Assert(err, IsNil)
   427  }
   428  
   429  func (s *deviceMgrInstallModeSuite) TestInstallSigned(c *C) {
   430  	err := s.doRunChangeTestWithEncryption(c, "signed", encTestCase{tpm: false, bypass: false, encrypt: false})
   431  	c.Assert(err, IsNil)
   432  }
   433  
   434  func (s *deviceMgrInstallModeSuite) TestInstallSignedWithTPM(c *C) {
   435  	err := s.doRunChangeTestWithEncryption(c, "signed", encTestCase{
   436  		tpm: true, bypass: false, encrypt: true, trustedBootloader: true,
   437  	})
   438  	c.Assert(err, IsNil)
   439  	c.Check(filepath.Join(boot.InstallHostFDEDataDir, "recovery.key"), testutil.FileEquals, dataRecoveryKey[:])
   440  }
   441  
   442  func (s *deviceMgrInstallModeSuite) TestInstallSignedBypassEncryption(c *C) {
   443  	err := s.doRunChangeTestWithEncryption(c, "signed", encTestCase{tpm: false, bypass: true, encrypt: false})
   444  	c.Assert(err, IsNil)
   445  }
   446  
   447  func (s *deviceMgrInstallModeSuite) TestInstallSecured(c *C) {
   448  	err := s.doRunChangeTestWithEncryption(c, "secured", encTestCase{tpm: false, bypass: false, encrypt: false})
   449  	c.Assert(err, ErrorMatches, "(?s).*cannot encrypt device storage as mandated by model grade secured:.*TPM not available.*")
   450  }
   451  
   452  func (s *deviceMgrInstallModeSuite) TestInstallSecuredWithTPM(c *C) {
   453  	err := s.doRunChangeTestWithEncryption(c, "secured", encTestCase{
   454  		tpm: true, bypass: false, encrypt: true, trustedBootloader: true,
   455  	})
   456  	c.Assert(err, IsNil)
   457  	c.Check(filepath.Join(boot.InstallHostFDEDataDir, "recovery.key"), testutil.FileEquals, dataRecoveryKey[:])
   458  }
   459  
   460  func (s *deviceMgrInstallModeSuite) TestInstallDangerousEncryptionWithTPMNoTrustedAssets(c *C) {
   461  	err := s.doRunChangeTestWithEncryption(c, "dangerous", encTestCase{
   462  		tpm: true, bypass: false, encrypt: true, trustedBootloader: false,
   463  	})
   464  	c.Assert(err, IsNil)
   465  	c.Check(filepath.Join(boot.InstallHostFDEDataDir, "recovery.key"), testutil.FileEquals, dataRecoveryKey[:])
   466  }
   467  
   468  func (s *deviceMgrInstallModeSuite) TestInstallDangerousNoEncryptionWithTrustedAssets(c *C) {
   469  	err := s.doRunChangeTestWithEncryption(c, "dangerous", encTestCase{
   470  		tpm: false, bypass: false, encrypt: false, trustedBootloader: true,
   471  	})
   472  	c.Assert(err, IsNil)
   473  }
   474  
   475  func (s *deviceMgrInstallModeSuite) TestInstallSecuredWithTPMAndSave(c *C) {
   476  	err := s.doRunChangeTestWithEncryption(c, "secured", encTestCase{
   477  		tpm: true, bypass: false, encrypt: true, trustedBootloader: true,
   478  	})
   479  	c.Assert(err, IsNil)
   480  	c.Check(filepath.Join(boot.InstallHostFDEDataDir, "recovery.key"), testutil.FileEquals, dataRecoveryKey[:])
   481  	c.Check(filepath.Join(boot.InstallHostFDEDataDir, "ubuntu-save.key"), testutil.FileEquals, saveKey[:])
   482  	c.Check(filepath.Join(boot.InstallHostFDEDataDir, "reinstall.key"), testutil.FileEquals, reinstallKey[:])
   483  	marker, err := ioutil.ReadFile(filepath.Join(boot.InstallHostFDEDataDir, "marker"))
   484  	c.Assert(err, IsNil)
   485  	c.Check(marker, HasLen, 32)
   486  	c.Check(filepath.Join(boot.InstallHostFDESaveDir, "marker"), testutil.FileEquals, marker)
   487  }
   488  
   489  func (s *deviceMgrInstallModeSuite) TestInstallSecuredBypassEncryption(c *C) {
   490  	err := s.doRunChangeTestWithEncryption(c, "secured", encTestCase{tpm: false, bypass: true, encrypt: false})
   491  	c.Assert(err, ErrorMatches, "(?s).*cannot encrypt device storage as mandated by model grade secured:.*TPM not available.*")
   492  }
   493  
   494  func (s *deviceMgrInstallModeSuite) testInstallEncryptionSanityChecks(c *C, errMatch string) {
   495  	restore := release.MockOnClassic(false)
   496  	defer restore()
   497  
   498  	restore = devicestate.MockSecbootCheckKeySealingSupported(func() error { return nil })
   499  	defer restore()
   500  
   501  	err := ioutil.WriteFile(filepath.Join(dirs.GlobalRootDir, "/var/lib/snapd/modeenv"),
   502  		[]byte("mode=install\n"), 0644)
   503  	c.Assert(err, IsNil)
   504  
   505  	s.state.Lock()
   506  	s.makeMockInstalledPcGadget(c, "dangerous", "")
   507  	devicestate.SetSystemMode(s.mgr, "install")
   508  	s.state.Unlock()
   509  
   510  	s.settle(c)
   511  
   512  	s.state.Lock()
   513  	defer s.state.Unlock()
   514  
   515  	installSystem := s.findInstallSystem()
   516  	c.Check(installSystem.Err(), ErrorMatches, errMatch)
   517  	// no restart request on failure
   518  	c.Check(s.restartRequests, HasLen, 0)
   519  }
   520  
   521  func (s *deviceMgrInstallModeSuite) TestInstallEncryptionSanityChecksNoKeys(c *C) {
   522  	restore := devicestate.MockInstallRun(func(mod gadget.Model, gadgetRoot, device string, options install.Options, _ gadget.ContentObserver) (*install.InstalledSystemSideData, error) {
   523  		c.Check(options.Encrypt, Equals, true)
   524  		// no keys set
   525  		return &install.InstalledSystemSideData{}, nil
   526  	})
   527  	defer restore()
   528  	s.testInstallEncryptionSanityChecks(c, `(?ms)cannot perform the following tasks:
   529  - Setup system for run mode \(internal error: system encryption keys are unset\)`)
   530  }
   531  
   532  func (s *deviceMgrInstallModeSuite) TestInstallEncryptionSanityChecksNoSystemDataKey(c *C) {
   533  	restore := devicestate.MockInstallRun(func(mod gadget.Model, gadgetRoot, device string, options install.Options, _ gadget.ContentObserver) (*install.InstalledSystemSideData, error) {
   534  		c.Check(options.Encrypt, Equals, true)
   535  		// no keys set
   536  		return &install.InstalledSystemSideData{
   537  			// empty map
   538  			KeysForRoles: map[string]*install.EncryptionKeySet{},
   539  		}, nil
   540  	})
   541  	defer restore()
   542  	s.testInstallEncryptionSanityChecks(c, `(?ms)cannot perform the following tasks:
   543  - Setup system for run mode \(internal error: system encryption keys are unset\)`)
   544  }
   545  
   546  func (s *deviceMgrInstallModeSuite) mockInstallModeChange(c *C, modelGrade, gadgetDefaultsYaml string) *asserts.Model {
   547  	restore := release.MockOnClassic(false)
   548  	defer restore()
   549  
   550  	restore = devicestate.MockInstallRun(func(mod gadget.Model, gadgetRoot, device string, options install.Options, _ gadget.ContentObserver) (*install.InstalledSystemSideData, error) {
   551  		return nil, nil
   552  	})
   553  	defer restore()
   554  
   555  	s.state.Lock()
   556  	mockModel := s.makeMockInstalledPcGadget(c, modelGrade, gadgetDefaultsYaml)
   557  	s.state.Unlock()
   558  	c.Check(mockModel.Grade(), Equals, asserts.ModelGrade(modelGrade))
   559  
   560  	restore = devicestate.MockBootMakeBootable(func(model *asserts.Model, rootdir string, bootWith *boot.BootableSet, seal *boot.TrustedAssetsInstallObserver) error {
   561  		return nil
   562  	})
   563  	defer restore()
   564  
   565  	modeenv := boot.Modeenv{
   566  		Mode:           "install",
   567  		RecoverySystem: "20191218",
   568  	}
   569  	c.Assert(modeenv.WriteTo(""), IsNil)
   570  	devicestate.SetSystemMode(s.mgr, "install")
   571  
   572  	// normally done by snap-bootstrap
   573  	err := os.MkdirAll(boot.InitramfsUbuntuBootDir, 0755)
   574  	c.Assert(err, IsNil)
   575  
   576  	s.settle(c)
   577  
   578  	return mockModel
   579  }
   580  
   581  func (s *deviceMgrInstallModeSuite) TestInstallModeRunSysconfig(c *C) {
   582  	s.mockInstallModeChange(c, "dangerous", "")
   583  
   584  	s.state.Lock()
   585  	defer s.state.Unlock()
   586  
   587  	// the install-system change is created
   588  	installSystem := s.findInstallSystem()
   589  	c.Assert(installSystem, NotNil)
   590  
   591  	// and was run successfully
   592  	c.Check(installSystem.Err(), IsNil)
   593  	c.Check(installSystem.Status(), Equals, state.DoneStatus)
   594  
   595  	// and sysconfig.ConfigureTargetSystem was run exactly once
   596  	c.Assert(s.ConfigureTargetSystemOptsPassed, DeepEquals, []*sysconfig.Options{
   597  		{
   598  			AllowCloudInit: true,
   599  			TargetRootDir:  boot.InstallHostWritableDir,
   600  			GadgetDir:      filepath.Join(dirs.SnapMountDir, "pc/1/"),
   601  		},
   602  	})
   603  }
   604  
   605  func (s *deviceMgrInstallModeSuite) TestInstallModeRunSysconfigErr(c *C) {
   606  	s.ConfigureTargetSystemErr = fmt.Errorf("error from sysconfig.ConfigureTargetSystem")
   607  	s.mockInstallModeChange(c, "dangerous", "")
   608  
   609  	s.state.Lock()
   610  	defer s.state.Unlock()
   611  
   612  	// the install-system was run but errorred as specified in the above mock
   613  	installSystem := s.findInstallSystem()
   614  	c.Check(installSystem.Err(), ErrorMatches, `(?ms)cannot perform the following tasks:
   615  - Setup system for run mode \(error from sysconfig.ConfigureTargetSystem\)`)
   616  	// and sysconfig.ConfigureTargetSystem was run exactly once
   617  	c.Assert(s.ConfigureTargetSystemOptsPassed, DeepEquals, []*sysconfig.Options{
   618  		{
   619  			AllowCloudInit: true,
   620  			TargetRootDir:  boot.InstallHostWritableDir,
   621  			GadgetDir:      filepath.Join(dirs.SnapMountDir, "pc/1/"),
   622  		},
   623  	})
   624  }
   625  
   626  func (s *deviceMgrInstallModeSuite) TestInstallModeSupportsCloudInitInDangerous(c *C) {
   627  	// pretend we have a cloud-init config on the seed partition
   628  	cloudCfg := filepath.Join(boot.InitramfsUbuntuSeedDir, "data/etc/cloud/cloud.cfg.d")
   629  	err := os.MkdirAll(cloudCfg, 0755)
   630  	c.Assert(err, IsNil)
   631  	for _, mockCfg := range []string{"foo.cfg", "bar.cfg"} {
   632  		err = ioutil.WriteFile(filepath.Join(cloudCfg, mockCfg), []byte(fmt.Sprintf("%s config", mockCfg)), 0644)
   633  		c.Assert(err, IsNil)
   634  	}
   635  
   636  	s.mockInstallModeChange(c, "dangerous", "")
   637  
   638  	// and did tell sysconfig about the cloud-init files
   639  	c.Assert(s.ConfigureTargetSystemOptsPassed, DeepEquals, []*sysconfig.Options{
   640  		{
   641  			AllowCloudInit:  true,
   642  			CloudInitSrcDir: filepath.Join(boot.InitramfsUbuntuSeedDir, "data/etc/cloud/cloud.cfg.d"),
   643  			TargetRootDir:   boot.InstallHostWritableDir,
   644  			GadgetDir:       filepath.Join(dirs.SnapMountDir, "pc/1/"),
   645  		},
   646  	})
   647  }
   648  
   649  func (s *deviceMgrInstallModeSuite) TestInstallModeSignedNoUbuntuSeedCloudInit(c *C) {
   650  	// pretend we have a cloud-init config on the seed partition
   651  	cloudCfg := filepath.Join(boot.InitramfsUbuntuSeedDir, "data/etc/cloud/cloud.cfg.d")
   652  	err := os.MkdirAll(cloudCfg, 0755)
   653  	c.Assert(err, IsNil)
   654  	for _, mockCfg := range []string{"foo.cfg", "bar.cfg"} {
   655  		err = ioutil.WriteFile(filepath.Join(cloudCfg, mockCfg), []byte(fmt.Sprintf("%s config", mockCfg)), 0644)
   656  		c.Assert(err, IsNil)
   657  	}
   658  
   659  	s.mockInstallModeChange(c, "signed", "")
   660  
   661  	// and did NOT tell sysconfig about the cloud-init file, but also did not
   662  	// explicitly disable cloud init
   663  	c.Assert(s.ConfigureTargetSystemOptsPassed, DeepEquals, []*sysconfig.Options{
   664  		{
   665  			AllowCloudInit: true,
   666  			TargetRootDir:  boot.InstallHostWritableDir,
   667  			GadgetDir:      filepath.Join(dirs.SnapMountDir, "pc/1/"),
   668  		},
   669  	})
   670  }
   671  
   672  func (s *deviceMgrInstallModeSuite) TestInstallModeSecuredGadgetCloudConfCloudInit(c *C) {
   673  	// pretend we have a cloud.conf from the gadget
   674  	gadgetDir := filepath.Join(dirs.SnapMountDir, "pc/1/")
   675  	err := os.MkdirAll(gadgetDir, 0755)
   676  	c.Assert(err, IsNil)
   677  	err = ioutil.WriteFile(filepath.Join(gadgetDir, "cloud.conf"), nil, 0644)
   678  	c.Assert(err, IsNil)
   679  
   680  	err = s.doRunChangeTestWithEncryption(c, "secured", encTestCase{
   681  		tpm: true, bypass: false, encrypt: true, trustedBootloader: true,
   682  	})
   683  	c.Assert(err, IsNil)
   684  
   685  	c.Assert(s.ConfigureTargetSystemOptsPassed, DeepEquals, []*sysconfig.Options{
   686  		{
   687  			AllowCloudInit: true,
   688  			TargetRootDir:  boot.InstallHostWritableDir,
   689  			GadgetDir:      filepath.Join(dirs.SnapMountDir, "pc/1/"),
   690  		},
   691  	})
   692  }
   693  
   694  func (s *deviceMgrInstallModeSuite) TestInstallModeSecuredNoUbuntuSeedCloudInit(c *C) {
   695  	// pretend we have a cloud-init config on the seed partition
   696  	cloudCfg := filepath.Join(boot.InitramfsUbuntuSeedDir, "data/etc/cloud/cloud.cfg.d")
   697  	err := os.MkdirAll(cloudCfg, 0755)
   698  	c.Assert(err, IsNil)
   699  	for _, mockCfg := range []string{"foo.cfg", "bar.cfg"} {
   700  		err = ioutil.WriteFile(filepath.Join(cloudCfg, mockCfg), []byte(fmt.Sprintf("%s config", mockCfg)), 0644)
   701  		c.Assert(err, IsNil)
   702  	}
   703  
   704  	err = s.doRunChangeTestWithEncryption(c, "secured", encTestCase{
   705  		tpm: true, bypass: false, encrypt: true, trustedBootloader: true,
   706  	})
   707  	c.Assert(err, IsNil)
   708  
   709  	// and did NOT tell sysconfig about the cloud-init files, instead it was
   710  	// disabled because only gadget cloud-init is allowed
   711  	c.Assert(s.ConfigureTargetSystemOptsPassed, DeepEquals, []*sysconfig.Options{
   712  		{
   713  			AllowCloudInit: false,
   714  			TargetRootDir:  boot.InstallHostWritableDir,
   715  			GadgetDir:      filepath.Join(dirs.SnapMountDir, "pc/1/"),
   716  		},
   717  	})
   718  }
   719  
   720  func (s *deviceMgrInstallModeSuite) TestInstallModeWritesModel(c *C) {
   721  	// pretend we have a cloud-init config on the seed partition
   722  	model := s.mockInstallModeChange(c, "dangerous", "")
   723  
   724  	var buf bytes.Buffer
   725  	err := asserts.NewEncoder(&buf).Encode(model)
   726  	c.Assert(err, IsNil)
   727  
   728  	s.state.Lock()
   729  	defer s.state.Unlock()
   730  
   731  	installSystem := s.findInstallSystem()
   732  	c.Assert(installSystem, NotNil)
   733  
   734  	// and was run successfully
   735  	c.Check(installSystem.Err(), IsNil)
   736  	c.Check(installSystem.Status(), Equals, state.DoneStatus)
   737  
   738  	c.Check(filepath.Join(boot.InitramfsUbuntuBootDir, "device/model"), testutil.FileEquals, buf.String())
   739  }
   740  
   741  func (s *deviceMgrInstallModeSuite) testInstallGadgetNoSave(c *C) {
   742  	err := ioutil.WriteFile(filepath.Join(dirs.GlobalRootDir, "/var/lib/snapd/modeenv"),
   743  		[]byte("mode=install\n"), 0644)
   744  	c.Assert(err, IsNil)
   745  
   746  	s.state.Lock()
   747  	s.makeMockInstalledPcGadget(c, "dangerous", "")
   748  	info, err := snapstate.CurrentInfo(s.state, "pc")
   749  	c.Assert(err, IsNil)
   750  	// replace gadget yaml with one that has no ubuntu-save
   751  	c.Assert(uc20gadgetYaml, Not(testutil.Contains), "ubuntu-save")
   752  	err = ioutil.WriteFile(filepath.Join(info.MountDir(), "meta/gadget.yaml"), []byte(uc20gadgetYaml), 0644)
   753  	c.Assert(err, IsNil)
   754  	devicestate.SetSystemMode(s.mgr, "install")
   755  	s.state.Unlock()
   756  
   757  	s.settle(c)
   758  }
   759  
   760  func (s *deviceMgrInstallModeSuite) TestInstallWithEncryptionValidatesGadgetErr(c *C) {
   761  	restore := release.MockOnClassic(false)
   762  	defer restore()
   763  
   764  	restore = devicestate.MockInstallRun(func(mod gadget.Model, gadgetRoot, device string, options install.Options, _ gadget.ContentObserver) (*install.InstalledSystemSideData, error) {
   765  		return nil, fmt.Errorf("unexpected call")
   766  	})
   767  	defer restore()
   768  
   769  	// pretend we have a TPM
   770  	restore = devicestate.MockSecbootCheckKeySealingSupported(func() error { return nil })
   771  	defer restore()
   772  
   773  	s.testInstallGadgetNoSave(c)
   774  
   775  	s.state.Lock()
   776  	defer s.state.Unlock()
   777  
   778  	installSystem := s.findInstallSystem()
   779  	c.Check(installSystem.Err(), ErrorMatches, `(?ms)cannot perform the following tasks:
   780  - Setup system for run mode \(cannot use gadget: gadget does not support encrypted data: volume "pc" has no structure with system-save role\)`)
   781  	// no restart request on failure
   782  	c.Check(s.restartRequests, HasLen, 0)
   783  }
   784  
   785  func (s *deviceMgrInstallModeSuite) TestInstallWithoutEncryptionValidatesGadgetWithoutSaveHappy(c *C) {
   786  	restore := release.MockOnClassic(false)
   787  	defer restore()
   788  
   789  	restore = devicestate.MockInstallRun(func(mod gadget.Model, gadgetRoot, device string, options install.Options, _ gadget.ContentObserver) (*install.InstalledSystemSideData, error) {
   790  		return nil, nil
   791  	})
   792  	defer restore()
   793  
   794  	// pretend we have a TPM
   795  	restore = devicestate.MockSecbootCheckKeySealingSupported(func() error { return fmt.Errorf("TPM2 not available") })
   796  	defer restore()
   797  
   798  	s.testInstallGadgetNoSave(c)
   799  
   800  	s.state.Lock()
   801  	defer s.state.Unlock()
   802  
   803  	installSystem := s.findInstallSystem()
   804  	c.Check(installSystem.Err(), IsNil)
   805  	c.Check(s.restartRequests, HasLen, 1)
   806  }
   807  
   808  func (s *deviceMgrInstallModeSuite) TestInstallCheckEncrypted(c *C) {
   809  	st := s.state
   810  	st.Lock()
   811  	defer st.Unlock()
   812  
   813  	mockModel := s.makeModelAssertionInState(c, "canonical", "pc", map[string]interface{}{
   814  		"architecture": "amd64",
   815  		"kernel":       "pc-kernel",
   816  		"gadget":       "pc",
   817  	})
   818  	devicestatetest.SetDevice(s.state, &auth.DeviceState{
   819  		Brand: "canonical",
   820  		Model: "pc",
   821  	})
   822  	deviceCtx := &snapstatetest.TrivialDeviceContext{DeviceModel: mockModel}
   823  
   824  	for _, tc := range []struct {
   825  		hasFDESetupHook bool
   826  		hasTPM          bool
   827  		encrypt         bool
   828  	}{
   829  		// unhappy: no tpm, no hook
   830  		{false, false, false},
   831  		// happy: either tpm or hook or both
   832  		{false, true, true},
   833  		{true, false, true},
   834  		{true, true, true},
   835  	} {
   836  		hookInvoke := func(ctx *hookstate.Context, tomb *tomb.Tomb) ([]byte, error) {
   837  			ctx.Lock()
   838  			defer ctx.Unlock()
   839  			ctx.Set("fde-setup-result", []byte(`{"features":[]}`))
   840  			return nil, nil
   841  		}
   842  		rhk := hookstate.MockRunHook(hookInvoke)
   843  		defer rhk()
   844  
   845  		if tc.hasFDESetupHook {
   846  			makeInstalledMockKernelSnap(c, st, kernelYamlWithFdeSetup)
   847  		} else {
   848  			makeInstalledMockKernelSnap(c, st, kernelYamlNoFdeSetup)
   849  		}
   850  		restore := devicestate.MockSecbootCheckKeySealingSupported(func() error {
   851  			if tc.hasTPM {
   852  				return nil
   853  			}
   854  			return fmt.Errorf("tpm says no")
   855  		})
   856  		defer restore()
   857  
   858  		encrypt, err := devicestate.DeviceManagerCheckEncryption(s.mgr, st, deviceCtx)
   859  		c.Assert(err, IsNil)
   860  		c.Check(encrypt, Equals, tc.encrypt, Commentf("%v", tc))
   861  	}
   862  }
   863  
   864  func (s *deviceMgrInstallModeSuite) TestInstallCheckEncryptedStorageSafety(c *C) {
   865  	s.state.Lock()
   866  	defer s.state.Unlock()
   867  
   868  	restore := devicestate.MockSecbootCheckKeySealingSupported(func() error { return nil })
   869  	defer restore()
   870  
   871  	var testCases = []struct {
   872  		grade, storageSafety string
   873  
   874  		expectedEncryption bool
   875  	}{
   876  		// we don't test unset here because the assertion assembly
   877  		// will ensure it has a default
   878  		{"dangerous", "prefer-unencrypted", false},
   879  		{"dangerous", "prefer-encrypted", true},
   880  		{"dangerous", "encrypted", true},
   881  		{"signed", "prefer-unencrypted", false},
   882  		{"signed", "prefer-encrypted", true},
   883  		{"signed", "encrypted", true},
   884  		// secured+prefer-{,un}encrypted is an error at the
   885  		// assertion level already so cannot be tested here
   886  		{"secured", "encrypted", true},
   887  	}
   888  	for _, tc := range testCases {
   889  		mockModel := s.makeModelAssertionInState(c, "my-brand", "my-model", map[string]interface{}{
   890  			"display-name":   "my model",
   891  			"architecture":   "amd64",
   892  			"base":           "core20",
   893  			"grade":          tc.grade,
   894  			"storage-safety": tc.storageSafety,
   895  			"snaps": []interface{}{
   896  				map[string]interface{}{
   897  					"name":            "pc-kernel",
   898  					"id":              pcKernelSnapID,
   899  					"type":            "kernel",
   900  					"default-channel": "20",
   901  				},
   902  				map[string]interface{}{
   903  					"name":            "pc",
   904  					"id":              pcSnapID,
   905  					"type":            "gadget",
   906  					"default-channel": "20",
   907  				}},
   908  		})
   909  		deviceCtx := &snapstatetest.TrivialDeviceContext{DeviceModel: mockModel}
   910  
   911  		encrypt, err := devicestate.DeviceManagerCheckEncryption(s.mgr, s.state, deviceCtx)
   912  		c.Assert(err, IsNil)
   913  		c.Check(encrypt, Equals, tc.expectedEncryption)
   914  	}
   915  }
   916  
   917  func (s *deviceMgrInstallModeSuite) TestInstallCheckEncryptedErrors(c *C) {
   918  	s.state.Lock()
   919  	defer s.state.Unlock()
   920  
   921  	restore := devicestate.MockSecbootCheckKeySealingSupported(func() error { return fmt.Errorf("tpm says no") })
   922  	defer restore()
   923  
   924  	var testCases = []struct {
   925  		grade, storageSafety string
   926  
   927  		expectedErr string
   928  	}{
   929  		// we don't test unset here because the assertion assembly
   930  		// will ensure it has a default
   931  		{
   932  			"dangerous", "encrypted",
   933  			"cannot encrypt device storage as mandated by encrypted storage-safety model option: tpm says no",
   934  		}, {
   935  			"signed", "encrypted",
   936  			"cannot encrypt device storage as mandated by encrypted storage-safety model option: tpm says no",
   937  		}, {
   938  			"secured", "",
   939  			"cannot encrypt device storage as mandated by model grade secured: tpm says no",
   940  		}, {
   941  			"secured", "encrypted",
   942  			"cannot encrypt device storage as mandated by model grade secured: tpm says no",
   943  		},
   944  	}
   945  	for _, tc := range testCases {
   946  		mockModel := s.makeModelAssertionInState(c, "my-brand", "my-model", map[string]interface{}{
   947  			"display-name":   "my model",
   948  			"architecture":   "amd64",
   949  			"base":           "core20",
   950  			"grade":          tc.grade,
   951  			"storage-safety": tc.storageSafety,
   952  			"snaps": []interface{}{
   953  				map[string]interface{}{
   954  					"name":            "pc-kernel",
   955  					"id":              pcKernelSnapID,
   956  					"type":            "kernel",
   957  					"default-channel": "20",
   958  				},
   959  				map[string]interface{}{
   960  					"name":            "pc",
   961  					"id":              pcSnapID,
   962  					"type":            "gadget",
   963  					"default-channel": "20",
   964  				}},
   965  		})
   966  		deviceCtx := &snapstatetest.TrivialDeviceContext{DeviceModel: mockModel}
   967  		_, err := devicestate.DeviceManagerCheckEncryption(s.mgr, s.state, deviceCtx)
   968  		c.Check(err, ErrorMatches, tc.expectedErr, Commentf("%s %s", tc.grade, tc.storageSafety))
   969  	}
   970  }
   971  
   972  func (s *deviceMgrInstallModeSuite) TestInstallCheckEncryptedFDEHook(c *C) {
   973  	st := s.state
   974  	st.Lock()
   975  	defer st.Unlock()
   976  
   977  	s.makeModelAssertionInState(c, "canonical", "pc", map[string]interface{}{
   978  		"architecture": "amd64",
   979  		"kernel":       "pc-kernel",
   980  		"gadget":       "pc",
   981  	})
   982  	devicestatetest.SetDevice(s.state, &auth.DeviceState{
   983  		Brand: "canonical",
   984  		Model: "pc",
   985  	})
   986  	makeInstalledMockKernelSnap(c, st, kernelYamlWithFdeSetup)
   987  
   988  	for _, tc := range []struct {
   989  		hookOutput  string
   990  		expectedErr string
   991  	}{
   992  		// invalid json
   993  		{"xxx", `cannot parse hook output "xxx": invalid character 'x' looking for beginning of value`},
   994  		// no output is invalid
   995  		{"", `cannot parse hook output "": unexpected end of JSON input`},
   996  		// specific error
   997  		{`{"error":"failed"}`, `cannot use hook: it returned error: failed`},
   998  		{`{}`, `cannot use hook: neither "features" nor "error" returned`},
   999  		// valid
  1000  		{`{"features":[]}`, ""},
  1001  		{`{"features":["a"]}`, ""},
  1002  		{`{"features":["a","b"]}`, ""},
  1003  		// features must be list of strings
  1004  		{`{"features":[1]}`, `cannot parse hook output ".*": json: cannot unmarshal number into Go struct.*`},
  1005  		{`{"features":1}`, `cannot parse hook output ".*": json: cannot unmarshal number into Go struct.*`},
  1006  		{`{"features":"1"}`, `cannot parse hook output ".*": json: cannot unmarshal string into Go struct.*`},
  1007  	} {
  1008  		hookInvoke := func(ctx *hookstate.Context, tomb *tomb.Tomb) ([]byte, error) {
  1009  			ctx.Lock()
  1010  			defer ctx.Unlock()
  1011  			ctx.Set("fde-setup-result", []byte(tc.hookOutput))
  1012  			return nil, nil
  1013  		}
  1014  		rhk := hookstate.MockRunHook(hookInvoke)
  1015  		defer rhk()
  1016  
  1017  		err := devicestate.DeviceManagerCheckFDEFeatures(s.mgr, st)
  1018  		if tc.expectedErr != "" {
  1019  			c.Check(err, ErrorMatches, tc.expectedErr, Commentf("%v", tc))
  1020  		} else {
  1021  			c.Check(err, IsNil, Commentf("%v", tc))
  1022  		}
  1023  	}
  1024  }
  1025  
  1026  var checkEncryptionModelHeaders = map[string]interface{}{
  1027  	"display-name": "my model",
  1028  	"architecture": "amd64",
  1029  	"base":         "core20",
  1030  	"grade":        "dangerous",
  1031  	"snaps": []interface{}{
  1032  		map[string]interface{}{
  1033  			"name":            "pc-kernel",
  1034  			"id":              pcKernelSnapID,
  1035  			"type":            "kernel",
  1036  			"default-channel": "20",
  1037  		},
  1038  		map[string]interface{}{
  1039  			"name":            "pc",
  1040  			"id":              pcSnapID,
  1041  			"type":            "gadget",
  1042  			"default-channel": "20",
  1043  		}},
  1044  }
  1045  
  1046  func (s *deviceMgrInstallModeSuite) TestInstallCheckEncryptedErrorsLogsTPM(c *C) {
  1047  	s.state.Lock()
  1048  	defer s.state.Unlock()
  1049  
  1050  	restore := devicestate.MockSecbootCheckKeySealingSupported(func() error {
  1051  		return fmt.Errorf("tpm says no")
  1052  	})
  1053  	defer restore()
  1054  
  1055  	logbuf, restore := logger.MockLogger()
  1056  	defer restore()
  1057  
  1058  	mockModel := s.makeModelAssertionInState(c, "my-brand", "my-model", checkEncryptionModelHeaders)
  1059  	deviceCtx := &snapstatetest.TrivialDeviceContext{DeviceModel: mockModel}
  1060  	_, err := devicestate.DeviceManagerCheckEncryption(s.mgr, s.state, deviceCtx)
  1061  	c.Check(err, IsNil)
  1062  	c.Check(logbuf.String(), Matches, "(?s).*: not encrypting device storage as checking TPM gave: tpm says no\n")
  1063  }
  1064  
  1065  func (s *deviceMgrInstallModeSuite) TestInstallCheckEncryptedErrorsLogsHook(c *C) {
  1066  	s.state.Lock()
  1067  	defer s.state.Unlock()
  1068  
  1069  	logbuf, restore := logger.MockLogger()
  1070  	defer restore()
  1071  
  1072  	mockModel := s.makeModelAssertionInState(c, "my-brand", "my-model", checkEncryptionModelHeaders)
  1073  	// mock kernel installed but no hook or handle so checkEncryption
  1074  	// will fail
  1075  	makeInstalledMockKernelSnap(c, s.state, kernelYamlWithFdeSetup)
  1076  
  1077  	deviceCtx := &snapstatetest.TrivialDeviceContext{DeviceModel: mockModel}
  1078  	_, err := devicestate.DeviceManagerCheckEncryption(s.mgr, s.state, deviceCtx)
  1079  	c.Check(err, IsNil)
  1080  	c.Check(logbuf.String(), Matches, "(?s).*: not encrypting device storage as querying kernel fde-setup hook did not succeed:.*\n")
  1081  }