diff --git a/overlord/devicestate/devicemgr.go b/overlord/devicestate/devicemgr.go index e8b6bc50dd9..a1133d596ef 100644 --- a/overlord/devicestate/devicemgr.go +++ b/overlord/devicestate/devicemgr.go @@ -2201,12 +2201,12 @@ func (m *DeviceManager) systems() ([]*System, error) { return systems, nil } -func snapdVersionByTypeFromSeed20(s seed.Seed, types []snap.Type) (snapdVersionByType map[snap.Type]string, err error) { +func snapdVersionByTypeFromSeed20(s seed.Seed, types []snap.Type) (systemSnapdVersions *install.SystemSnapdVersions, err error) { perf := &timings.Timings{} if err := s.LoadEssentialMeta(types, perf); err != nil { return nil, fmt.Errorf("cannot load essential snaps metadata: %v", err) } - snapdVersionByType = make(map[snap.Type]string) + systemSnapdVersions = &install.SystemSnapdVersions{} for _, snapSeed := range s.EssentialSnaps() { snapf, err := snapfile.Open(snapSeed.Path) if err != nil { @@ -2216,12 +2216,14 @@ func snapdVersionByTypeFromSeed20(s seed.Seed, types []snap.Type) (snapdVersionB if err != nil { return nil, err } - snapdVersionByType[snapSeed.EssentialType] = snapdVersion - } - if len(snapdVersionByType) != len(types) { - return nil, fmt.Errorf("internal error: retrieved snaps (%d) does not match number of types (%d)", len(snapdVersionByType), len(types)) + switch snapSeed.EssentialType { + case snap.TypeSnapd: + systemSnapdVersions.SnapdVersion = snapdVersion + case snap.TypeKernel: + systemSnapdVersions.SnapdInitramfsVersion = snapdVersion + } } - return snapdVersionByType, nil + return systemSnapdVersions, nil } // SystemAndGadgetAndEncryptionInfo return the system details @@ -2247,20 +2249,20 @@ func (m *DeviceManager) SystemAndGadgetAndEncryptionInfo(wantedSystemLabel strin return nil, nil, nil, fmt.Errorf("reading gadget information: %v", err) } - var snapdVersionByType map[snap.Type]string + var systemSnapdVersions *install.SystemSnapdVersions // Find snapd versions for snapd and kernel snaps in the seed for // the passphrase/PINs auth checks. // FDE is only supported in UC20+ (i.e. Model grade is set). if systemAndSnaps.Model.Grade() != asserts.ModelGradeUnset { // Snapd snap should exist in UC20+. - snapdVersionByType, err = snapdVersionByTypeFromSeed20(systemAndSnaps.Seed, []snap.Type{snap.TypeSnapd, snap.TypeKernel}) + systemSnapdVersions, err = snapdVersionByTypeFromSeed20(systemAndSnaps.Seed, []snap.Type{snap.TypeSnapd, snap.TypeKernel}) if err != nil { return nil, nil, nil, err } } // Encryption details - encInfo, err := m.encryptionSupportInfo(systemAndSnaps.Model, secboot.TPMProvisionFull, systemAndSnaps.InfosByType[snap.TypeKernel], gadgetInfo, snapdVersionByType) + encInfo, err := m.encryptionSupportInfo(systemAndSnaps.Model, secboot.TPMProvisionFull, systemAndSnaps.InfosByType[snap.TypeKernel], gadgetInfo, systemSnapdVersions) if err != nil { return nil, nil, nil, err } @@ -2903,6 +2905,6 @@ func (m *DeviceManager) checkEncryption(st *state.State, deviceCtx snapstate.Dev return install.CheckEncryptionSupport(model, tpmMode, kernelInfo, gadgetInfo, m.runFDESetupHook) } -func (m *DeviceManager) encryptionSupportInfo(model *asserts.Model, tpmMode secboot.TPMProvisionMode, kernelInfo *snap.Info, gadgetInfo *gadget.Info, snapdVersionByType map[snap.Type]string) (install.EncryptionSupportInfo, error) { - return install.GetEncryptionSupportInfo(model, tpmMode, kernelInfo, gadgetInfo, snapdVersionByType, m.runFDESetupHook) +func (m *DeviceManager) encryptionSupportInfo(model *asserts.Model, tpmMode secboot.TPMProvisionMode, kernelInfo *snap.Info, gadgetInfo *gadget.Info, systemSnapdVersions *install.SystemSnapdVersions) (install.EncryptionSupportInfo, error) { + return install.GetEncryptionSupportInfo(model, tpmMode, kernelInfo, gadgetInfo, systemSnapdVersions, m.runFDESetupHook) } diff --git a/overlord/devicestate/devicestate_install_api_test.go b/overlord/devicestate/devicestate_install_api_test.go index 9d6d84bb6c1..6db59f9dd08 100644 --- a/overlord/devicestate/devicestate_install_api_test.go +++ b/overlord/devicestate/devicestate_install_api_test.go @@ -723,6 +723,12 @@ func (s *deviceMgrInstallAPISuite) testInstallSetupStorageEncryption(c *C, hasTP snap.TypeSnapd: "2.68", snap.TypeKernel: "2.68", } + } else { + // mock other versions to cover more cases + snapdVersionByType = map[snap.Type]string{ + snap.TypeSnapd: "2.67", + snap.TypeKernel: "2.66", + } } seedOpts := mockSystemSeedWithLabelOpts{ isClassic: isClassic, diff --git a/overlord/devicestate/handlers_install.go b/overlord/devicestate/handlers_install.go index 8ce1a35f380..3524ba5fa28 100644 --- a/overlord/devicestate/handlers_install.go +++ b/overlord/devicestate/handlers_install.go @@ -1295,19 +1295,19 @@ func (m *DeviceManager) doInstallSetupStorageEncryption(t *state.Task, _ *tomb.T return fmt.Errorf("reading gadget information: %v", err) } - var snapdVersionByType map[snap.Type]string + var systemSnapdVersions *installLogic.SystemSnapdVersions // Find snapd versions for snapd and kernel snaps in the seed for // the passphrase/PINs auth checks. // FDE is only supported in UC20+ (i.e. Model grade is set). - if volumesAuthRequired && systemAndSeeds.Model.Grade() != asserts.ModelGradeUnset { + if systemAndSeeds.Model.Grade() != asserts.ModelGradeUnset { // Snapd and kernel snaps are expected to exist in UC20+. - snapdVersionByType, err = snapdVersionByTypeFromSeed20(systemAndSeeds.Seed, []snap.Type{snap.TypeSnapd, snap.TypeKernel}) + systemSnapdVersions, err = snapdVersionByTypeFromSeed20(systemAndSeeds.Seed, []snap.Type{snap.TypeSnapd, snap.TypeKernel}) if err != nil { return err } } - encryptInfo, err := m.encryptionSupportInfo(systemAndSeeds.Model, secboot.TPMProvisionFull, systemAndSeeds.InfosByType[snap.TypeKernel], gadgetInfo, snapdVersionByType) + encryptInfo, err := m.encryptionSupportInfo(systemAndSeeds.Model, secboot.TPMProvisionFull, systemAndSeeds.InfosByType[snap.TypeKernel], gadgetInfo, systemSnapdVersions) if err != nil { return err } diff --git a/overlord/install/install.go b/overlord/install/install.go index ba673e24a2d..4287ce90b7c 100644 --- a/overlord/install/install.go +++ b/overlord/install/install.go @@ -85,6 +85,15 @@ type EncryptionSupportInfo struct { PassphraseAuthAvailable bool } +// SystemSnapdVersions describes the snapd versions in a given systems. +type SystemSnapdVersions struct { + // SnapdVersion is the version of snapd in a given system + SnapdVersion string + // SnapdInitramfsVersion is the version of snapd related component, which participates + // in the boot process and performs unlocking. Typically snap-bootstrap in the kernel snap. + SnapdInitramfsVersion string +} + var ( timeNow = time.Now @@ -103,23 +112,32 @@ func MockSecbootCheckTPMKeySealingSupported(f func(tpmMode secboot.TPMProvisionM } } -func checkPassphraseSupportedByTargetSystem(snapdVersionByType map[snap.Type]string) (bool, error) { +func checkPassphraseSupportedByTargetSystem(sysVer *SystemSnapdVersions) (bool, error) { const minSnapdVersion = "2.68" - // snapd and snap-bootstrap inside the kernel must support passphrases. - for _, typ := range []snap.Type{snap.TypeSnapd, snap.TypeKernel} { - ver := snapdVersionByType[typ] - if ver == "" { - // absent snapd version info is expected for older kernels - return false, nil - } - cmp, err := strutil.VersionCompare(ver, minSnapdVersion) - if err != nil { - return false, fmt.Errorf("invalid snapd version in info file from %s snap: %v", typ, err) - } - if cmp < 0 { - return false, nil - } + if sysVer == nil { + return false, nil + } + if sysVer.SnapdVersion == "" || sysVer.SnapdInitramfsVersion == "" { + return false, nil + } + + // snapd snap must support passphrases. + cmp, err := strutil.VersionCompare(sysVer.SnapdVersion, minSnapdVersion) + if err != nil { + return false, fmt.Errorf("invalid snapd version in info file from snapd snap: %v", err) } + if cmp < 0 { + return false, nil + } + // snap-bootstrap inside the kernel must support passphrases. + cmp, err = strutil.VersionCompare(sysVer.SnapdInitramfsVersion, minSnapdVersion) + if err != nil { + return false, fmt.Errorf("invalid snapd version in info file from kernel snap: %v", err) + } + if cmp < 0 { + return false, nil + } + return true, nil } @@ -127,7 +145,7 @@ func checkPassphraseSupportedByTargetSystem(snapdVersionByType map[snap.Type]str // for the given model, TPM provision mode, kernel and gadget information and // system hardware. It uses runSetupHook to invoke the kernel fde-setup hook if // any is available, leaving the caller to decide how, based on the environment. -func GetEncryptionSupportInfo(model *asserts.Model, tpmMode secboot.TPMProvisionMode, kernelInfo *snap.Info, gadgetInfo *gadget.Info, snapdVersionByType map[snap.Type]string, runSetupHook fde.RunSetupHookFunc) (EncryptionSupportInfo, error) { +func GetEncryptionSupportInfo(model *asserts.Model, tpmMode secboot.TPMProvisionMode, kernelInfo *snap.Info, gadgetInfo *gadget.Info, systemSnapdVersions *SystemSnapdVersions, runSetupHook fde.RunSetupHookFunc) (EncryptionSupportInfo, error) { secured := model.Grade() == asserts.ModelSecured dangerous := model.Grade() == asserts.ModelDangerous encrypted := model.StorageSafety() == asserts.StorageSafetyEncrypted @@ -186,7 +204,7 @@ func GetEncryptionSupportInfo(model *asserts.Model, tpmMode secboot.TPMProvision // it is usually in the context of embedded systems where passphrase // authentication is not practical. if checkSecbootEncryption { - passphraseAuthAvailable, err := checkPassphraseSupportedByTargetSystem(snapdVersionByType) + passphraseAuthAvailable, err := checkPassphraseSupportedByTargetSystem(systemSnapdVersions) if err != nil { return res, fmt.Errorf("cannot check passphrase support: %v", err) } diff --git a/overlord/install/install_test.go b/overlord/install/install_test.go index 8ccfa675a47..c649ff5979b 100644 --- a/overlord/install/install_test.go +++ b/overlord/install/install_test.go @@ -370,12 +370,12 @@ func (s *installSuite) TestEncryptionSupportInfoWithTPM(c *C) { "grade": tc.grade, "storage-safety": tc.storageSafety, }) - mockSnapdVersionByType := map[snap.Type]string{ - snap.TypeSnapd: tc.snapdVersion, - snap.TypeKernel: tc.kernelSnapdVersion, + mockSystemSnapdVersions := &install.SystemSnapdVersions{ + SnapdVersion: tc.snapdVersion, + SnapdInitramfsVersion: tc.kernelSnapdVersion, } - res, err := install.GetEncryptionSupportInfo(mockModel, secboot.TPMProvisionFull, kernelInfo, gadgetInfo, mockSnapdVersionByType, nil) + res, err := install.GetEncryptionSupportInfo(mockModel, secboot.TPMProvisionFull, kernelInfo, gadgetInfo, mockSystemSnapdVersions, nil) c.Assert(err, IsNil) c.Check(res, DeepEquals, tc.expected, Commentf("%v", tc)) }