Skip to content

Commit a817820

Browse files
committed
boot/makebootable.go: make reprovision only seal
1 parent 5519cdc commit a817820

3 files changed

Lines changed: 247 additions & 52 deletions

File tree

boot/makebootable.go

Lines changed: 51 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -474,6 +474,46 @@ func isSealModeenvLocked() bool {
474474
return atomic.LoadInt32(&sealModeenvLocked) == 1
475475
}
476476

477+
func makeRunnableSystemSeal(modeenv *Modeenv, model *asserts.Model, protector secboot.KeyProtectorFactory, encryption *EncryptionSetup, makeOpts makeRunnableOptions) error {
478+
tokens := UseTokens(model)
479+
if tokens {
480+
logger.Debugf("key data will be stored in tokens")
481+
} else {
482+
logger.Debugf("key data will be stored in files")
483+
}
484+
485+
flags := sealKeyToModeenvFlags{
486+
HookKeyProtectorFactory: protector,
487+
LegacyFactoryResetKeyPath: makeOpts.LegacyFactoryResetKeyPath,
488+
Reprovision: makeOpts.Reprovision,
489+
SeedDir: makeOpts.SeedDir,
490+
StateUnlocker: makeOpts.StateUnlocker,
491+
UseTokens: tokens,
492+
}
493+
494+
if makeOpts.Standalone {
495+
flags.SnapsDir = dirs.SnapBlobDirUnder(InstallHostWritableDir(model))
496+
}
497+
498+
// seal the encryption key to the parameters specified in
499+
// modeenv as well as optimum PCR configuration specified in the
500+
// check result (when available)
501+
if err := sealKeyToModeenv(
502+
encryption.dataBootstrappedContainer,
503+
encryption.saveBootstrappedContainer,
504+
encryption.primaryKey,
505+
encryption.volumesAuth,
506+
encryption.checkResult,
507+
model,
508+
modeenv,
509+
flags,
510+
); err != nil {
511+
return err
512+
}
513+
514+
return nil
515+
}
516+
477517
func makeRunnableSystem(model *asserts.Model, bootWith *BootableSet, bootAssets BootAssets, encryption *EncryptionSetup, makeOpts makeRunnableOptions) error {
478518
if model.Grade() == asserts.ModelGradeUnset {
479519
return fmt.Errorf("internal error: cannot make pre-UC20 system runnable")
@@ -668,37 +708,7 @@ func makeRunnableSystem(model *asserts.Model, bootWith *BootableSet, bootAssets
668708
return fmt.Errorf("cannot check for fde-setup hook key protector: %v", err)
669709
}
670710

671-
tokens := UseTokens(model)
672-
if tokens {
673-
logger.Debugf("key data will be stored in tokens")
674-
} else {
675-
logger.Debugf("key data will be stored in files")
676-
}
677-
678-
flags := sealKeyToModeenvFlags{
679-
HookKeyProtectorFactory: protector,
680-
LegacyFactoryResetKeyPath: makeOpts.LegacyFactoryResetKeyPath,
681-
Reprovision: makeOpts.Reprovision,
682-
SeedDir: makeOpts.SeedDir,
683-
StateUnlocker: makeOpts.StateUnlocker,
684-
UseTokens: tokens,
685-
}
686-
if makeOpts.Standalone {
687-
flags.SnapsDir = snapBlobDir
688-
}
689-
// seal the encryption key to the parameters specified in
690-
// modeenv as well as optimum PCR configuration specified in the
691-
// check result (when available)
692-
if err := sealKeyToModeenv(
693-
encryption.dataBootstrappedContainer,
694-
encryption.saveBootstrappedContainer,
695-
encryption.primaryKey,
696-
encryption.volumesAuth,
697-
encryption.checkResult,
698-
model,
699-
modeenv,
700-
flags,
701-
); err != nil {
711+
if err := makeRunnableSystemSeal(modeenv, model, protector, encryption, makeOpts); err != nil {
702712
return err
703713
}
704714
}
@@ -813,8 +823,16 @@ func MakeRunnableSystemAfterDataReset(model *asserts.Model, bootWith *BootableSe
813823
// MakeRunnableSystemReprovision make the systems currently running bootable again.
814824
// This is intended to repair the boot of a system that was booted for example
815825
// with a recovery key.
816-
func MakeRunnableSystemReprovision(model *asserts.Model, bootWith *BootableSet, bootAssets BootAssets, encryption *EncryptionSetup) error {
817-
return makeRunnableSystem(model, bootWith, bootAssets, encryption, makeRunnableOptions{
826+
func MakeRunnableSystemReprovision(model *asserts.Model, protector secboot.KeyProtectorFactory, encryption *EncryptionSetup) error {
827+
sealModeenvLock()
828+
defer sealModeenvUnlock()
829+
830+
modeenv, err := ReadModeenv("")
831+
if err != nil {
832+
return err
833+
}
834+
835+
return makeRunnableSystemSeal(modeenv, model, protector, encryption, makeRunnableOptions{
818836
Reprovision: true,
819837
SeedDir: dirs.SnapSeedDir,
820838
})

boot/makebootable_test.go

Lines changed: 181 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -580,7 +580,6 @@ func (s *makeBootable20Suite) TestMakeSystemRunnableSealWithHookKeyProtector(c *
580580
type testMakeSystemRunnable20Opts struct {
581581
standalone bool
582582
factoryReset bool
583-
reprovision bool
584583
classic bool
585584
fromInitrd bool
586585
withKComps bool
@@ -822,7 +821,7 @@ version: 5.0
822821
c.Assert(recoveryGrub.Hashes, HasLen, 1)
823822
c.Check(recoveryGrub.Hashes[0], Equals, "aa3c1a83e74bf6dd40dd64e5c5bd1971d75cdf55515b23b9eb379f66bf43d4661d22c4b8cf7d7a982d2013ab65c1c4c5")
824823

825-
c.Check(params.Reprovision, Equals, opts.reprovision || opts.factoryReset)
824+
c.Check(params.Reprovision, Equals, opts.factoryReset)
826825
c.Check(params.LegacyFactoryResetKeyPath, Equals, opts.factoryReset)
827826
if opts.classic {
828827
c.Check(params.InstallHostWritableDir, Equals, filepath.Join(boot.InitramfsRunMntDir, "ubuntu-data"))
@@ -870,8 +869,6 @@ version: 5.0
870869
c.Check(u.unlocked, Equals, 1)
871870
case opts.factoryReset && !opts.fromInitrd:
872871
err = boot.MakeRunnableSystemAfterDataReset(model, bootWith, obs.BootAssets(), obs.EncryptionSetup())
873-
case opts.reprovision && !opts.fromInitrd:
874-
err = boot.MakeRunnableSystemReprovision(model, bootWith, obs.BootAssets(), obs.EncryptionSetup())
875872
default:
876873
err = boot.MakeRunnableSystem(model, bootWith, obs.BootAssets(), obs.EncryptionSetup())
877874
}
@@ -1050,16 +1047,6 @@ func (s *makeBootable20Suite) TestMakeSystemRunnable20FactoryResetOnClassic(c *C
10501047
})
10511048
}
10521049

1053-
func (s *makeBootable20Suite) TestMakeSystemRunnable20Reprovision(c *C) {
1054-
s.testMakeSystemRunnable20(c, testMakeSystemRunnable20Opts{
1055-
standalone: false,
1056-
reprovision: true,
1057-
classic: false,
1058-
fromInitrd: false,
1059-
withKComps: true,
1060-
})
1061-
}
1062-
10631050
func (s *makeBootable20Suite) TestMakeSystemRunnable20InstallFromInitrd(c *C) {
10641051
s.testMakeSystemRunnable20(c, testMakeSystemRunnable20Opts{
10651052
standalone: true,
@@ -2608,3 +2595,183 @@ func (s *makeBootable20Suite) TestMakeBootableImageOptionalKernelArgsSignedAndDa
26082595
// The option is ignored if non-dangerous model
26092596
s.testMakeBootableImageOptionalKernelArgs(c, model, options, "", "")
26102597
}
2598+
2599+
func (s *makeBootable20Suite) TestMakeSystemRunnableReprovision(c *C) {
2600+
/* baseName := "core26" */
2601+
fakeProc := c.MkDir()
2602+
fakeCmdline := filepath.Join(fakeProc, "cmdline")
2603+
defer kcmdline.MockProcCmdline(fakeCmdline)()
2604+
err := os.WriteFile(fakeCmdline, []byte(fmt.Sprintf("some ubuntu-core.force-experimental-tokens=1 args")), 0644)
2605+
c.Assert(err, IsNil)
2606+
2607+
restore := release.MockOnClassic(true)
2608+
defer restore()
2609+
dirs.SetRootDir(dirs.GlobalRootDir)
2610+
2611+
bootloader.Force(nil)
2612+
2613+
var model *asserts.Model
2614+
model = boottest.MakeMockUC20Model(map[string]any{
2615+
"classic": "true",
2616+
"distribution": "ubuntu",
2617+
})
2618+
/*seedSnapsDirs := filepath.Join(s.rootdir, "/snaps")
2619+
err = os.MkdirAll(seedSnapsDirs, 0755)
2620+
c.Assert(err, IsNil)*/
2621+
2622+
mockSeedGrubDir := filepath.Join(boot.InitramfsUbuntuSeedDir, "EFI", "ubuntu")
2623+
mockSeedGrubCfg := filepath.Join(mockSeedGrubDir, "grub.cfg")
2624+
err = os.MkdirAll(filepath.Dir(mockSeedGrubCfg), 0755)
2625+
c.Assert(err, IsNil)
2626+
err = os.WriteFile(mockSeedGrubCfg, []byte("# Snapd-Boot-Config-Edition: 1\n"), 0644)
2627+
c.Assert(err, IsNil)
2628+
genv := grubenv.NewEnv(filepath.Join(mockSeedGrubDir, "grubenv"))
2629+
c.Assert(genv.Save(), IsNil)
2630+
2631+
mockBootGrubDir := filepath.Join(boot.InitramfsUbuntuBootDir, "EFI", "ubuntu")
2632+
mockBootGrubCfg := filepath.Join(mockBootGrubDir, "grub.cfg")
2633+
err = os.MkdirAll(filepath.Dir(mockBootGrubCfg), 0755)
2634+
c.Assert(err, IsNil)
2635+
err = os.WriteFile(mockBootGrubCfg, nil, 0644)
2636+
c.Assert(err, IsNil)
2637+
2638+
myKey := secboot.CreateMockBootstrappedContainer()
2639+
myKey2 := secboot.CreateMockBootstrappedContainer()
2640+
chosenPrimaryKey := []byte("primarykey!")
2641+
myVolumesAuth := &device.VolumesAuthOptions{Mode: device.AuthModePassphrase, Passphrase: "test"}
2642+
myCheckResult := &secboot.PreinstallCheckResult{}
2643+
2644+
encryptionSetup := boot.NewEncryptionSetup(
2645+
myKey, myKey2,
2646+
chosenPrimaryKey,
2647+
myVolumesAuth,
2648+
myCheckResult,
2649+
)
2650+
2651+
var readSystemEssentialCalls []string
2652+
restore = boot.MockSeedReadSystemEssential(func(seedDir, label string, essentialTypes []snap.Type, tm timings.Measurer) (*asserts.Model, []*seed.Snap, error) {
2653+
readSystemEssentialCalls = append(readSystemEssentialCalls, label)
2654+
c.Check(seedDir, Equals, dirs.SnapSeedDir)
2655+
if label == "test" {
2656+
return model, []*seed.Snap{mockKernelSeedSnap(snap.R(1)), mockGadgetSeedSnap(c, nil)}, nil
2657+
} else {
2658+
return model, []*seed.Snap{mockKernelSeedSnap(snap.R(2)), mockGadgetSeedSnap(c, nil)}, nil
2659+
}
2660+
})
2661+
defer restore()
2662+
2663+
kernel2, err := snap.ParsePlaceInfoFromSnapFileName("pc-kernel_2.snap")
2664+
c.Assert(err, IsNil)
2665+
2666+
kernel3, err := snap.ParsePlaceInfoFromSnapFileName("pc-kernel_3.snap")
2667+
c.Assert(err, IsNil)
2668+
2669+
sealKeyForBootChainsCalled := 0
2670+
restore = boot.MockSealKeyForBootChains(func(method device.SealingMethod, key, saveKey secboot.BootstrappedContainer, primaryKey []byte, volumesAuth *device.VolumesAuthOptions, checkResult *secboot.PreinstallCheckResult, params *boot.SealKeyForBootChainsParams) error {
2671+
sealKeyForBootChainsCalled++
2672+
c.Check(method, Equals, device.SealingMethodTPM)
2673+
c.Check(key, Equals, myKey)
2674+
c.Check(saveKey, Equals, myKey2)
2675+
c.Check(primaryKey, DeepEquals, chosenPrimaryKey)
2676+
c.Check(volumesAuth, Equals, myVolumesAuth)
2677+
c.Check(checkResult, Equals, myCheckResult)
2678+
2679+
recoveryBootLoader, hasRecovery := params.RoleToBlName[bootloader.RoleRecovery]
2680+
c.Assert(hasRecovery, Equals, true)
2681+
c.Check(recoveryBootLoader, Equals, "grub")
2682+
runBootLoader, hasRun := params.RoleToBlName[bootloader.RoleRunMode]
2683+
c.Assert(hasRun, Equals, true)
2684+
c.Check(runBootLoader, Equals, "grub")
2685+
2686+
c.Assert(params.RunModeBootChains, HasLen, 2)
2687+
for n, runBootChain := range params.RunModeBootChains {
2688+
c.Check(runBootChain.Model, Equals, model.Model())
2689+
c.Check(runBootChain.KernelCmdlines, DeepEquals, []string{"foo", "bar"})
2690+
c.Check(runBootChain.KernelBootFile.Path, Equals, "kernel.efi")
2691+
switch n {
2692+
case 0:
2693+
c.Check(runBootChain.KernelBootFile.Snap, Equals, filepath.Join(dirs.SnapBlobDir, "pc-kernel_2.snap"))
2694+
case 1:
2695+
c.Check(runBootChain.KernelBootFile.Snap, Equals, filepath.Join(dirs.SnapBlobDir, "pc-kernel_3.snap"))
2696+
}
2697+
c.Check(runBootChain.KernelBootFile.Role, Equals, bootloader.RoleRunMode)
2698+
c.Assert(runBootChain.AssetChain, HasLen, 3)
2699+
runShim := runBootChain.AssetChain[0]
2700+
runGrub := runBootChain.AssetChain[1]
2701+
runGrubRun := runBootChain.AssetChain[2]
2702+
c.Check(runShim.Name, Equals, "bootx64.efi")
2703+
c.Check(runShim.Hashes, DeepEquals, []string{"shimhash1", "shimhash2"})
2704+
c.Check(runGrub.Name, Equals, "grubx64.efi")
2705+
c.Check(runGrub.Hashes, DeepEquals, []string{"recovery-hash1"})
2706+
c.Check(runGrubRun.Name, Equals, "grubx64.efi")
2707+
c.Check(runGrubRun.Hashes, DeepEquals, []string{"hash1", "hash2"})
2708+
}
2709+
2710+
c.Check(params.RecoveryBootChainsForRunKey, HasLen, 0)
2711+
c.Assert(params.RecoveryBootChains, HasLen, 2)
2712+
for n, recoveryBootChain := range params.RecoveryBootChains {
2713+
c.Check(recoveryBootChain.KernelBootFile.Path, Equals, "kernel.efi")
2714+
switch n {
2715+
case 0:
2716+
c.Check(recoveryBootChain.KernelBootFile.Snap, Equals, "/var/lib/snapd/seed/snaps/pc-kernel_1.snap")
2717+
case 1:
2718+
c.Check(recoveryBootChain.KernelBootFile.Snap, Equals, "/var/lib/snapd/seed/snaps/pc-kernel_2.snap")
2719+
}
2720+
c.Check(recoveryBootChain.KernelBootFile.Role, Equals, bootloader.RoleRecovery)
2721+
c.Check(recoveryBootChain.Model, Equals, model.Model())
2722+
c.Assert(recoveryBootChain.AssetChain, HasLen, 2)
2723+
recoveryShim := recoveryBootChain.AssetChain[0]
2724+
recoveryGrub := recoveryBootChain.AssetChain[1]
2725+
c.Check(recoveryShim.Name, Equals, "bootx64.efi")
2726+
c.Check(recoveryShim.Hashes, DeepEquals, []string{"shimhash1", "shimhash2"})
2727+
c.Check(recoveryGrub.Name, Equals, "grubx64.efi")
2728+
c.Check(recoveryGrub.Hashes, DeepEquals, []string{"recovery-hash1"})
2729+
}
2730+
2731+
c.Check(params.Reprovision, Equals, true)
2732+
c.Check(params.LegacyFactoryResetKeyPath, Equals, false)
2733+
c.Check(params.InstallHostWritableDir, Equals, filepath.Join(boot.InitramfsRunMntDir, "ubuntu-data"))
2734+
2735+
c.Check(params.UseTokens, Equals, true)
2736+
2737+
return nil
2738+
})
2739+
defer restore()
2740+
2741+
restore = boot.MockCryptsetupSupportsTokenReplace(true)
2742+
defer restore()
2743+
2744+
modeenv := &boot.Modeenv{
2745+
Mode: "run",
2746+
RecoverySystem: "test",
2747+
CurrentRecoverySystems: []string{"test", "other"},
2748+
GoodRecoverySystems: []string{"test", "other"},
2749+
2750+
CurrentTrustedBootAssets: boot.BootAssetsMap{
2751+
"grubx64.efi": []string{"hash1", "hash2"},
2752+
},
2753+
CurrentTrustedRecoveryBootAssets: boot.BootAssetsMap{
2754+
"bootx64.efi": []string{"shimhash1", "shimhash2"},
2755+
"grubx64.efi": []string{"recovery-hash1"},
2756+
},
2757+
CurrentKernelCommandLines: boot.BootCommandLines{
2758+
"foo", "bar",
2759+
},
2760+
2761+
CurrentKernels: []string{kernel2.Filename(), kernel3.Filename()},
2762+
2763+
Model: "my-model-uc20",
2764+
BrandID: "my-brand",
2765+
ModelSignKeyID: "Jv8_JiHiIzJVcO9M55pPdqSDWUvuhfDIBJUS-3VW7F_idjix7Ffn5qMxB21ZQuij",
2766+
Grade: "dangerous",
2767+
}
2768+
c.Assert(modeenv.WriteTo(""), IsNil)
2769+
2770+
var protector secboot.KeyProtectorFactory
2771+
2772+
err = boot.MakeRunnableSystemReprovision(model, protector, encryptionSetup)
2773+
c.Assert(err, IsNil)
2774+
2775+
c.Check(sealKeyForBootChainsCalled, Equals, 1)
2776+
c.Check(readSystemEssentialCalls, DeepEquals, []string{"test", "other"})
2777+
}

boot/seal.go

Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -242,13 +242,23 @@ func sealKeyToModeenvForMethod(
242242
}
243243
}
244244

245+
// When installing or reprovsion, we expect there is no try system
245246
includeTryModel := false
246-
systems := []string{modeenv.RecoverySystem}
247-
modes := map[string][]string{
248-
// the system we are installing from is considered current and
249-
// tested, hence allow both recover and factory reset modes
250-
modeenv.RecoverySystem: {ModeRecover, ModeFactoryReset},
247+
systems := modeenv.GoodRecoverySystems
248+
if len(systems) == 0 {
249+
systems = []string{modeenv.RecoverySystem}
250+
}
251+
modes := map[string][]string{}
252+
for _, system := range systems {
253+
logger.Debugf("sealing for system %q", system)
254+
modes[system] = []string{ModeRecover, ModeFactoryReset}
255+
}
256+
for _, system := range modeenv.CurrentRecoverySystems {
257+
if _, has := modes[system]; !has {
258+
return fmt.Errorf("trying to install or reprovision with a try system %q", system)
259+
}
251260
}
261+
252262
var err error
253263
params.RecoveryBootChains, err = recoveryBootChainsForSystems(systems, modes, tbl, modeenv, includeTryModel, flags.SeedDir)
254264
if err != nil {

0 commit comments

Comments
 (0)